From bc8d6d51bfa48b8c468944e852897bd4482ff329 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Thu, 1 Aug 2024 21:48:40 +0800 Subject: [PATCH 01/31] Fix .gitattributes Signed-off-by: nyanmisaka --- .gitattributes | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index bb32dc23869..f4fea03f7ad 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,6 @@ * text eol=lf +*.ico -diff -text *.pnm -diff -text -tests/ref/fate/sub-scc eol=crlf +debian/patches/* -text +tests/ref/fate/* -text +tests/ref/fate/sub-scc text eol=crlf From 8b6e13f596ca087b65a499418fa4c8b355d50b59 Mon Sep 17 00:00:00 2001 From: nyanmisaka Date: Sat, 3 Aug 2024 18:31:39 +0800 Subject: [PATCH 02/31] New upstream version 7.0.2 Signed-off-by: nyanmisaka --- Changelog | 590 ++- MAINTAINERS | 88 +- Makefile | 20 +- RELEASE | 2 +- RELEASE_NOTES | 10 +- VERSION | 2 +- compat/atomics/win32/stdatomic.h | 1 - compat/w32pthreads.h | 10 +- configure | 623 ++- doc/APIchanges | 303 +- doc/Doxyfile | 2 +- doc/Makefile | 1 + doc/bitstream_filters.texi | 35 +- doc/codecs.texi | 20 +- doc/community.texi | 177 + doc/decoders.texi | 187 + doc/demuxers.texi | 204 +- doc/dev_community/community.md | 82 - doc/dev_community/resolution_process.md | 91 - doc/developer.texi | 363 +- doc/encoders.texi | 253 +- doc/examples/demux_decode.c | 10 +- doc/examples/mux.c | 27 +- doc/examples/qsv_decode.c | 2 - doc/examples/qsv_transcode.c | 19 +- doc/examples/transcode.c | 50 +- doc/examples/transcode_aac.c | 20 +- doc/examples/vaapi_encode.c | 4 + doc/examples/vaapi_transcode.c | 4 +- doc/faq.texi | 4 +- doc/fate.texi | 16 + doc/fate_config.sh.template | 22 + doc/ffmpeg.texi | 672 ++- doc/ffplay.texi | 12 + doc/ffprobe.texi | 22 +- doc/ffprobe.xsd | 908 ++-- doc/fftools-common-opts.texi | 26 +- doc/filters.texi | 2809 ++++++++-- doc/formats.texi | 3 +- doc/general_contents.texi | 31 +- doc/git-howto.texi | 2 +- doc/indevs.texi | 16 +- doc/infra.txt | 94 + doc/mailing-list-faq.texi | 2 +- doc/metadata.texi | 1 + doc/mips.txt | 8 - doc/multithreading.txt | 3 +- doc/muxers.texi | 2020 ++++--- doc/outdevs.texi | 45 +- doc/platform.texi | 3 - doc/protocols.texi | 13 + doc/utils.texi | 28 +- ffbuild/arch.mak | 1 + ffbuild/common.mak | 4 +- fftools/Makefile | 5 + fftools/cmdutils.c | 439 +- fftools/cmdutils.h | 198 +- fftools/ffmpeg.c | 3847 ++------------ fftools/ffmpeg.h | 850 ++- fftools/ffmpeg_dec.c | 1361 +++++ fftools/ffmpeg_demux.c | 1903 ++++--- fftools/ffmpeg_enc.c | 966 ++++ fftools/ffmpeg_filter.c | 3368 ++++++++---- fftools/ffmpeg_hw.c | 282 +- fftools/ffmpeg_mux.c | 817 +-- fftools/ffmpeg_mux.h | 79 +- fftools/ffmpeg_mux_init.c | 2480 +++++---- fftools/ffmpeg_opt.c | 1210 +++-- fftools/ffmpeg_sched.c | 2540 +++++++++ fftools/ffmpeg_sched.h | 489 ++ fftools/ffmpeg_utils.h | 56 + fftools/ffplay.c | 505 +- fftools/ffplay_renderer.c | 835 +++ .../x86/fft.h => fftools/ffplay_renderer.h | 29 +- fftools/ffprobe.c | 1300 +++-- fftools/opt_common.c | 93 +- fftools/opt_common.h | 62 +- fftools/sync_queue.c | 335 +- fftools/sync_queue.h | 23 +- fftools/thread_queue.c | 19 +- libavcodec/012v.c | 2 +- libavcodec/4xm.c | 27 +- libavcodec/8bps.c | 87 +- libavcodec/Makefile | 152 +- libavcodec/aac.h | 268 - libavcodec/aac_ac3_parser.c | 6 - libavcodec/aac_defines.h | 13 +- libavcodec/aaccoder.c | 26 +- libavcodec/aaccoder_trellis.h | 2 +- libavcodec/aaccoder_twoloop.h | 2 +- libavcodec/aacdec.c | 18 +- libavcodec/aacdec.h | 279 + libavcodec/aacdec_common.c | 307 ++ libavcodec/aacdec_fixed.c | 54 +- libavcodec/aacdec_template.c | 226 +- libavcodec/aacdectab.h | 105 +- libavcodec/aacenc.c | 52 +- libavcodec/aacenc.h | 97 +- libavcodec/aacenc_is.c | 6 +- libavcodec/aacenc_ltp.c | 12 +- libavcodec/aacenc_pred.c | 8 +- libavcodec/aacenc_tns.c | 5 +- libavcodec/aacenc_utils.h | 26 +- libavcodec/aacencdsp.h | 72 + libavcodec/aacenctab.h | 9 +- libavcodec/aacps.c | 9 +- libavcodec/aacps.h | 12 +- libavcodec/aacps_common.c | 98 +- libavcodec/aacps_tablegen_template.c | 2 + libavcodec/aacpsdata.c | 185 +- libavcodec/aacpsdsp_template.c | 32 +- libavcodec/aacpsy.c | 2 +- libavcodec/aacsbr.c | 42 +- libavcodec/aacsbr.h | 22 +- libavcodec/aacsbr_fixed.c | 3 +- libavcodec/aacsbr_template.c | 123 +- libavcodec/aacsbrdata.h | 492 -- libavcodec/aactab.c | 158 +- libavcodec/aactab.h | 38 +- libavcodec/aarch64/Makefile | 7 +- libavcodec/aarch64/aacpsdsp_neon.S | 218 +- libavcodec/aarch64/fft_init_aarch64.c | 52 - libavcodec/aarch64/fft_neon.S | 447 -- libavcodec/aarch64/h264cmc_neon.S | 410 +- libavcodec/aarch64/h264dsp_neon.S | 602 +-- libavcodec/aarch64/h264idct_neon.S | 392 +- libavcodec/aarch64/h264qpel_init_aarch64.c | 91 +- libavcodec/aarch64/h264qpel_neon.S | 1100 +++- libavcodec/aarch64/hevcdsp_deblock_neon.S | 600 +++ libavcodec/aarch64/hevcdsp_epel_neon.S | 4668 +++++++++++++++++ libavcodec/aarch64/hevcdsp_idct_neon.S | 546 +- libavcodec/aarch64/hevcdsp_init_aarch64.c | 335 +- libavcodec/aarch64/hevcdsp_qpel_neon.S | 4533 +++++++++++++++- libavcodec/aarch64/hpeldsp_neon.S | 362 +- libavcodec/aarch64/mdct_neon.S | 326 -- libavcodec/aarch64/me_cmp_neon.S | 2 +- libavcodec/aarch64/mpegaudiodsp_neon.S | 8 +- libavcodec/aarch64/neon.S | 257 +- libavcodec/aarch64/opusdsp_neon.S | 102 +- libavcodec/aarch64/sbrdsp_neon.S | 294 +- libavcodec/aarch64/simple_idct_neon.S | 386 +- libavcodec/aarch64/synth_filter_init.c | 7 - libavcodec/aarch64/synth_filter_neon.S | 2 - libavcodec/aarch64/vp8dsp_neon.S | 310 +- libavcodec/ac3_parser.c | 4 +- libavcodec/ac3dec.c | 9 +- libavcodec/ac3dec.h | 3 +- libavcodec/ac3dec_float.c | 4 +- libavcodec/ac3dsp.c | 4 +- libavcodec/ac3dsp.h | 8 +- libavcodec/ac3enc.c | 83 +- libavcodec/ac3enc.h | 3 - libavcodec/ac3enc_fixed.c | 1 - libavcodec/ac3enc_float.c | 1 - libavcodec/ac3enc_template.c | 6 +- libavcodec/ac3tab.h | 16 - libavcodec/adts_parser.c | 7 +- libavcodec/adx.h | 4 +- libavcodec/adx_parser.c | 10 + libavcodec/adxdec.c | 2 +- libavcodec/agm.c | 18 +- libavcodec/aic.c | 2 +- libavcodec/alac.c | 54 +- libavcodec/alacenc.c | 16 - libavcodec/aliaspixdec.c | 2 +- libavcodec/allcodecs.c | 31 +- libavcodec/alpha/idctdsp_alpha.c | 1 + libavcodec/alsdec.c | 10 +- libavcodec/amfenc.c | 107 +- libavcodec/amfenc.h | 27 +- libavcodec/amfenc_av1.c | 257 +- libavcodec/amfenc_h264.c | 285 +- libavcodec/amfenc_hevc.c | 234 +- libavcodec/amrwbdec.c | 3 + libavcodec/ansi.c | 10 +- libavcodec/aom_film_grain.c | 548 ++ libavcodec/aom_film_grain.h | 51 + libavcodec/aom_film_grain_template.c | 577 ++ libavcodec/apac.c | 6 +- libavcodec/apedec.c | 144 +- libavcodec/aptxdec.c | 2 - libavcodec/aptxenc.c | 2 - libavcodec/arbc.c | 8 +- libavcodec/argo.c | 5 +- libavcodec/arm/Makefile | 7 - libavcodec/arm/ac3dsp_init_arm.c | 2 +- libavcodec/arm/fft_init_arm.c | 63 - libavcodec/arm/fft_neon.S | 375 -- libavcodec/arm/fft_vfp.S | 530 -- libavcodec/arm/hevcdsp_idct_neon.S | 28 +- libavcodec/arm/hevcdsp_init_neon.c | 4 +- libavcodec/arm/mdct_neon.S | 301 -- libavcodec/arm/mdct_vfp.S | 347 -- libavcodec/arm/rdft_neon.S | 155 - libavcodec/arm/synth_filter_init_arm.c | 1 - libavcodec/assenc.c | 29 +- libavcodec/asvdec.c | 52 +- libavcodec/asvenc.c | 3 +- libavcodec/atrac3.c | 16 +- libavcodec/atrac3plus.c | 4 +- libavcodec/atrac9dec.c | 66 +- libavcodec/audiodsp.c | 40 +- libavcodec/audiotoolboxdec.c | 2 + libavcodec/audiotoolboxenc.c | 86 +- libavcodec/av1.h | 8 + libavcodec/av1_levels.c | 92 + libavcodec/av1_levels.h | 58 + libavcodec/av1_parse.c | 24 +- libavcodec/av1_parse.h | 22 +- libavcodec/av1_parser.c | 11 +- libavcodec/av1dec.c | 654 ++- libavcodec/av1dec.h | 38 +- libavcodec/avcodec.c | 100 +- libavcodec/avcodec.h | 993 ++-- libavcodec/avcodec_internal.h | 61 + libavcodec/avdct.c | 46 +- libavcodec/avfft.c | 215 +- libavcodec/avfft.h | 31 + libavcodec/avpacket.c | 105 + libavcodec/avrndec.c | 2 +- libavcodec/avs.c | 4 +- libavcodec/avs2_parser.c | 4 +- libavcodec/avs3_parser.c | 3 +- libavcodec/avuidec.c | 2 +- libavcodec/bethsoftvideo.c | 4 + libavcodec/bfi.c | 12 +- libavcodec/bgmc.c | 4 +- libavcodec/bgmc.h | 3 +- libavcodec/bink.c | 12 +- libavcodec/binkaudio.c | 6 +- libavcodec/binkdsp.c | 3 +- libavcodec/binkdsp.h | 4 +- libavcodec/bintext.c | 4 + libavcodec/bitpacked_dec.c | 19 +- libavcodec/bitstream.h | 2 + libavcodec/bitstream_filters.c | 9 +- libavcodec/bitstream_template.h | 48 +- libavcodec/blockdsp.c | 2 + libavcodec/blockdsp.h | 1 + libavcodec/bmp.c | 2 +- libavcodec/bmvvideo.c | 4 + libavcodec/bonk.c | 6 +- libavcodec/brenderpix.c | 10 +- libavcodec/bsf.c | 11 +- libavcodec/bsf/Makefile | 49 + .../aac_adtstoasc.c} | 0 .../av1_frame_merge.c} | 0 .../av1_frame_split.c} | 0 .../av1_metadata.c} | 4 +- libavcodec/{chomp_bsf.c => bsf/chomp.c} | 0 libavcodec/{dca_core_bsf.c => bsf/dca_core.c} | 0 libavcodec/{dts2pts_bsf.c => bsf/dts2pts.c} | 0 .../dump_extradata.c} | 2 +- .../dv_error_marker.c} | 38 +- .../{eac3_core_bsf.c => bsf/eac3_core.c} | 0 libavcodec/bsf/evc_frame_merge.c | 289 + .../extract_extradata.c} | 66 +- .../filter_units.c} | 62 +- .../h264_metadata.c} | 7 +- .../h264_mp4toannexb.c} | 186 +- .../h264_redundant_pps.c} | 0 .../h265_metadata.c} | 2 +- libavcodec/bsf/h266_metadata.c | 150 + .../hapqa_extract.c} | 8 +- .../hevc_mp4toannexb.c} | 9 +- .../imx_dump_header.c} | 0 .../media100_to_mjpegb.c} | 0 .../{mjpeg2jpeg_bsf.c => bsf/mjpeg2jpeg.c} | 0 .../mjpega_dump_header.c} | 0 libavcodec/{movsub_bsf.c => bsf/movsub.c} | 0 .../mpeg2_metadata.c} | 0 .../mpeg4_unpack_bframes.c} | 0 libavcodec/{noise_bsf.c => bsf/noise.c} | 24 +- libavcodec/{null_bsf.c => bsf/null.c} | 0 .../opus_metadata.c} | 0 .../{pcm_rechunk_bsf.c => bsf/pcm_rechunk.c} | 44 +- .../pgs_frame_merge.c} | 0 .../prores_metadata.c} | 46 +- .../remove_extradata.c} | 2 +- libavcodec/{setts_bsf.c => bsf/setts.c} | 0 libavcodec/bsf/showinfo.c | 63 + .../trace_headers.c} | 2 + .../{truehd_core_bsf.c => bsf/truehd_core.c} | 0 .../vp9_metadata.c} | 4 +- .../vp9_raw_reorder.c} | 0 .../vp9_superframe.c} | 0 .../vp9_superframe_split.c} | 0 libavcodec/bsf/vvc_mp4toannexb.c | 328 ++ libavcodec/bytestream.h | 10 +- libavcodec/c93.c | 8 +- libavcodec/cavs_parser.c | 11 +- libavcodec/cavsdec.c | 4 + libavcodec/cbs.c | 289 +- libavcodec/cbs.h | 117 +- libavcodec/cbs_av1.c | 326 +- libavcodec/cbs_av1.h | 21 +- libavcodec/cbs_av1_syntax_template.c | 101 +- libavcodec/cbs_bsf.c | 5 + libavcodec/cbs_bsf.h | 6 +- libavcodec/cbs_h264.h | 6 +- libavcodec/cbs_h2645.c | 885 +++- libavcodec/cbs_h264_syntax_template.c | 35 +- libavcodec/cbs_h265.h | 9 +- libavcodec/cbs_h265_syntax_template.c | 58 +- libavcodec/cbs_h266.h | 879 ++++ libavcodec/cbs_h266_syntax_template.c | 3489 ++++++++++++ libavcodec/cbs_internal.h | 111 +- libavcodec/cbs_jpeg.c | 4 +- libavcodec/cbs_mpeg2.c | 15 +- libavcodec/cbs_sei.c | 90 +- libavcodec/cbs_sei.h | 27 +- libavcodec/cbs_sei_syntax_template.c | 52 +- libavcodec/cbs_vp8.c | 386 ++ libavcodec/cbs_vp8.h | 133 + libavcodec/cbs_vp8_syntax_template.c | 248 + libavcodec/cbs_vp9.c | 122 +- libavcodec/ccaption_dec.c | 241 +- libavcodec/cdgraphics.c | 41 +- libavcodec/cdtoons.c | 4 + libavcodec/cdxl.c | 7 +- libavcodec/cfhd.c | 6 +- libavcodec/cfhddata.c | 4 +- libavcodec/cfhdenc.c | 35 +- libavcodec/cinepak.c | 13 +- libavcodec/clearvideo.c | 78 +- libavcodec/cljrdec.c | 2 +- libavcodec/cllc.c | 16 +- libavcodec/cngenc.c | 1 + libavcodec/codec.h | 12 +- libavcodec/codec_desc.c | 91 +- libavcodec/codec_desc.h | 8 +- libavcodec/codec_id.h | 12 +- libavcodec/codec_internal.h | 19 - libavcodec/codec_par.c | 104 +- libavcodec/codec_par.h | 57 +- libavcodec/cook.c | 8 +- libavcodec/cos_tablegen.c | 80 - libavcodec/cpia.c | 4 +- libavcodec/cri.c | 11 +- libavcodec/crystalhd.c | 822 --- libavcodec/cscd.c | 4 +- libavcodec/cuviddec.c | 72 +- libavcodec/d3d11va.h | 3 - libavcodec/d3d12va_av1.c | 206 + libavcodec/d3d12va_decode.c | 536 ++ libavcodec/d3d12va_decode.h | 179 + libavcodec/d3d12va_h264.c | 203 + libavcodec/d3d12va_hevc.c | 208 + libavcodec/d3d12va_mpeg2.c | 179 + libavcodec/d3d12va_vc1.c | 214 + libavcodec/d3d12va_vp9.c | 167 + libavcodec/dca_core.c | 24 +- libavcodec/dca_lbr.c | 2 +- libavcodec/dca_parser.c | 16 +- libavcodec/dca_syncwords.h | 3 + libavcodec/dca_xll.c | 26 +- libavcodec/dca_xll.h | 3 + libavcodec/dcadec.c | 40 +- libavcodec/dcaenc.c | 3 - libavcodec/dcahuff.c | 8 +- libavcodec/dct.c | 228 - libavcodec/dct.h | 34 +- libavcodec/dds.c | 14 +- libavcodec/decode.c | 849 +-- libavcodec/decode.h | 70 +- libavcodec/defs.h | 143 + libavcodec/dfa.c | 4 + libavcodec/dirac.c | 3 + libavcodec/dirac.h | 6 +- libavcodec/dirac_arith.h | 4 + libavcodec/dirac_dwt.c | 1 + libavcodec/diracdec.c | 20 +- libavcodec/dnxhddata.c | 14 +- libavcodec/dnxhddec.c | 61 +- libavcodec/dnxhdenc.c | 66 +- libavcodec/dnxhdenc.h | 5 +- libavcodec/dolby_e.c | 13 +- libavcodec/dovi_rpu.c | 105 +- libavcodec/dovi_rpu.h | 4 +- libavcodec/dpx.c | 63 +- libavcodec/dsicinvideo.c | 4 + libavcodec/dv.h | 6 + libavcodec/dvdec.c | 24 +- libavcodec/dvdsubenc.c | 3 +- libavcodec/dvenc.c | 5 +- libavcodec/dxa.c | 21 +- libavcodec/dxtory.c | 2 +- libavcodec/dxv.c | 363 +- libavcodec/dxv.h | 34 + libavcodec/dxva2.c | 73 +- libavcodec/dxva2.h | 3 - libavcodec/dxva2_av1.c | 41 +- libavcodec/dxva2_h264.c | 73 +- libavcodec/dxva2_hevc.c | 49 +- libavcodec/dxva2_internal.h | 94 +- libavcodec/dxva2_mpeg2.c | 57 +- libavcodec/dxva2_vc1.c | 82 +- libavcodec/dxva2_vp9.c | 46 +- libavcodec/dxvenc.c | 357 ++ libavcodec/dynamic_hdr10_plus.c | 198 - libavcodec/dynamic_hdr_vivid.c | 61 +- libavcodec/eac3dec.c | 11 +- libavcodec/eac3enc.c | 1 - libavcodec/eacmv.c | 9 +- libavcodec/eamad.c | 3 +- libavcodec/eatgq.c | 2 +- libavcodec/eatgv.c | 7 +- libavcodec/elbg.c | 62 +- libavcodec/encode.c | 247 +- libavcodec/encode.h | 16 +- libavcodec/error_resilience.c | 15 +- libavcodec/error_resilience.h | 15 +- libavcodec/escape124.c | 18 +- libavcodec/evc.h | 146 + libavcodec/evc_parse.c | 214 + libavcodec/evc_parse.h | 105 + libavcodec/evc_parser.c | 377 ++ libavcodec/evc_ps.c | 442 ++ libavcodec/evc_ps.h | 224 + libavcodec/evrcdec.c | 10 +- libavcodec/exr.c | 62 +- libavcodec/exrdsp.c | 20 +- libavcodec/exrdsp.h | 1 + libavcodec/exrenc.c | 16 +- libavcodec/faandct.c | 1 + libavcodec/faanidct.c | 1 + libavcodec/faxcompr.c | 32 +- libavcodec/fdctdsp.c | 1 - libavcodec/fdctdsp.h | 19 +- libavcodec/ffjni.c | 15 +- libavcodec/ffjni.h | 3 +- libavcodec/fflcms2.c | 51 + libavcodec/fflcms2.h | 7 + libavcodec/fft-internal.h | 62 - libavcodec/fft.h | 160 - libavcodec/fft_fixed_32.c | 51 - libavcodec/fft_init_table.c | 344 -- libavcodec/fft_table.h | 66 - libavcodec/fft_template.c | 628 --- libavcodec/ffv1dec.c | 85 +- libavcodec/ffv1enc.c | 27 +- libavcodec/fic.c | 4 +- libavcodec/fitsdec.c | 3 +- libavcodec/fitsenc.c | 1 + libavcodec/flac.c | 18 +- libavcodec/flac_parse.h | 4 +- libavcodec/flac_parser.c | 2 + libavcodec/flacdsp.c | 2 + libavcodec/flacdsp.h | 1 + libavcodec/flacenc.c | 36 +- libavcodec/flashsv.c | 2 - libavcodec/flashsv2enc.c | 2 +- libavcodec/flicvideo.c | 284 +- libavcodec/flvdec.c | 2 - libavcodec/fmvc.c | 5 +- libavcodec/frame_thread_encoder.c | 5 +- libavcodec/fraps.c | 11 +- libavcodec/frwu.c | 2 +- libavcodec/ftr.c | 6 +- libavcodec/g2meet.c | 9 +- libavcodec/g722dsp.c | 2 + libavcodec/g722dsp.h | 1 + libavcodec/g722enc.c | 1 - libavcodec/g723_1dec.c | 6 +- libavcodec/g729dec.c | 12 +- libavcodec/g729postfilter.c | 8 +- libavcodec/g729postfilter.h | 14 +- libavcodec/gemdec.c | 6 +- libavcodec/get_bits.h | 51 +- libavcodec/get_buffer.c | 66 +- libavcodec/gif.c | 11 +- libavcodec/gif_parser.c | 45 +- libavcodec/gifdec.c | 125 +- libavcodec/golomb.h | 5 + libavcodec/h261dec.c | 61 +- libavcodec/h263dec.c | 66 +- libavcodec/h263dec.h | 10 +- libavcodec/h2645_parse.c | 76 +- libavcodec/h2645_sei.c | 214 +- libavcodec/h2645_sei.h | 19 + libavcodec/h2645_vui.c | 16 +- libavcodec/h2645_vui.h | 2 + libavcodec/h264_cavlc.c | 210 +- libavcodec/h264_direct.c | 6 +- libavcodec/h264_mb.c | 5 +- libavcodec/h264_mvpred.h | 132 + libavcodec/h264_parse.c | 14 +- libavcodec/h264_parser.c | 13 +- libavcodec/h264_picture.c | 112 +- libavcodec/h264_ps.c | 113 +- libavcodec/h264_ps.h | 24 +- libavcodec/h264_refs.c | 28 +- libavcodec/h264_sei.c | 2 +- libavcodec/h264_slice.c | 365 +- libavcodec/h264chroma.c | 2 + libavcodec/h264chroma.h | 1 + libavcodec/h264dec.c | 148 +- libavcodec/h264dec.h | 191 +- libavcodec/h264idct_template.c | 2 +- libavcodec/h264qpel_template.c | 93 +- libavcodec/h26x/h2656_deblock_template.c | 99 + libavcodec/h26x/h2656_inter_template.c | 577 ++ libavcodec/h26x/h2656_sao_template.c | 217 + libavcodec/h274.c | 32 +- libavcodec/h274.h | 15 + libavcodec/hap.h | 6 +- libavcodec/hapdec.c | 21 +- libavcodec/hapenc.c | 27 +- libavcodec/hcadec.c | 235 +- libavcodec/hdrdec.c | 2 +- libavcodec/hdrenc.c | 2 +- libavcodec/hevc.h | 3 + libavcodec/hevc_cabac.c | 248 +- libavcodec/hevc_mvs.c | 2 +- libavcodec/hevc_parse.c | 12 +- libavcodec/hevc_parser.c | 12 +- libavcodec/hevc_ps.c | 661 ++- libavcodec/hevc_ps.h | 138 +- libavcodec/hevc_refs.c | 107 +- libavcodec/hevc_sei.c | 55 +- libavcodec/hevc_sei.h | 16 - libavcodec/hevcdec.c | 443 +- libavcodec/hevcdec.h | 83 +- libavcodec/hevcdsp.c | 6 +- libavcodec/hevcdsp.h | 5 +- libavcodec/hevcdsp_template.c | 918 +--- libavcodec/hevcpred_template.c | 11 +- libavcodec/hnm4video.c | 4 +- libavcodec/hq_hqa.c | 56 +- libavcodec/hq_hqa.h | 60 - libavcodec/{hq_hqadata.c => hq_hqadata.h} | 37 +- libavcodec/hqx.c | 6 +- libavcodec/hqxvlc.c | 4 +- libavcodec/huffman.c | 2 +- libavcodec/huffyuvdec.c | 22 +- libavcodec/huffyuvdsp.c | 4 +- libavcodec/huffyuvdsp.h | 2 + libavcodec/huffyuvenc.c | 11 +- libavcodec/hwaccel_internal.h | 180 + libavcodec/hwaccels.h | 132 +- libavcodec/hwconfig.h | 11 +- libavcodec/idcinvideo.c | 9 +- libavcodec/idctdsp.c | 14 +- libavcodec/idctdsp.h | 33 +- libavcodec/iff.c | 8 +- libavcodec/iirfilter.c | 4 + libavcodec/ilbcdec.c | 6 - libavcodec/imc.c | 30 +- libavcodec/imm4.c | 62 +- libavcodec/imx.c | 18 +- libavcodec/indeo2.c | 10 +- libavcodec/indeo3.c | 3 + libavcodec/intelh263dec.c | 4 - libavcodec/internal.h | 76 +- libavcodec/interplayacm.c | 8 +- libavcodec/interplayvideo.c | 12 +- libavcodec/intrax8.c | 48 +- libavcodec/ituh263dec.c | 71 +- libavcodec/ituh263enc.c | 5 + .../{aarch64/asm-offsets.h => itut35.h} | 15 +- libavcodec/ivi.c | 12 +- libavcodec/ivi.h | 15 +- libavcodec/j2kenc.c | 96 +- libavcodec/jfdctfst.c | 2 +- libavcodec/jfdctint_template.c | 8 +- libavcodec/jni.c | 47 +- libavcodec/jni.h | 21 + libavcodec/jpeg2000.c | 18 +- libavcodec/jpeg2000.h | 5 +- libavcodec/jpeg2000dec.c | 201 +- libavcodec/jpeg2000dec.h | 119 + libavcodec/jpeg2000dsp.c | 7 +- libavcodec/jpeg2000dsp.h | 3 + libavcodec/jpeg2000htdec.c | 1476 ++++++ libavcodec/jpeg2000htdec.h | 34 + libavcodec/jpegls.c | 1 + libavcodec/jpeglsdec.c | 13 + libavcodec/jpeglsenc.c | 10 +- libavcodec/jpegxl.h | 94 + libavcodec/jpegxl_parse.c | 520 ++ libavcodec/jpegxl_parse.h | 72 + libavcodec/jpegxl_parser.c | 1538 ++++++ libavcodec/jvdec.c | 10 +- libavcodec/kbdwin.c | 49 +- libavcodec/kmvc.c | 21 +- libavcodec/lagarith.c | 72 +- libavcodec/lagarithrac.h | 3 +- libavcodec/lcldec.c | 8 +- libavcodec/leaddata.h | 62 + libavcodec/leaddec.c | 316 ++ libavcodec/leb.h | 70 + libavcodec/libaomdec.c | 25 +- libavcodec/libaomenc.c | 59 +- libavcodec/libaribb24.c | 56 +- libavcodec/libaribcaption.c | 1181 +++++ libavcodec/libcodec2.c | 2 - libavcodec/libdav1d.c | 253 +- libavcodec/libdavs2.c | 2 - libavcodec/libfdk-aacdec.c | 28 +- libavcodec/libfdk-aacenc.c | 201 +- libavcodec/libgsmenc.c | 2 - libavcodec/libjxl.h | 13 +- libavcodec/libjxldec.c | 173 +- libavcodec/libjxlenc.c | 31 +- libavcodec/libkvazaar.c | 26 +- libavcodec/libmp3lame.c | 22 +- libavcodec/libopencore-amr.c | 4 +- libavcodec/libopenh264.c | 15 - libavcodec/libopenh264.h | 2 - libavcodec/libopenh264dec.c | 7 +- libavcodec/libopenh264enc.c | 95 +- libavcodec/libopenjpegdec.c | 516 -- libavcodec/libopenjpegenc.c | 36 +- libavcodec/libopusenc.c | 26 +- libavcodec/librav1e.c | 30 +- libavcodec/librsvgdec.c | 83 +- libavcodec/libshine.c | 1 - libavcodec/libspeexdec.c | 6 +- libavcodec/libspeexenc.c | 1 - libavcodec/libsvtav1.c | 185 +- libavcodec/libtwolame.c | 13 +- libavcodec/libuavs3d.c | 18 +- libavcodec/libvpx.c | 81 - libavcodec/libvpx.h | 10 - libavcodec/libvpxdec.c | 27 +- libavcodec/libvpxenc.c | 317 +- libavcodec/libwebpenc_animencoder.c | 13 - libavcodec/libwebpenc_common.c | 16 +- libavcodec/libx264.c | 554 +- libavcodec/libx265.c | 102 +- libavcodec/libxavs.c | 24 +- libavcodec/libxevd.c | 484 ++ libavcodec/libxeve.c | 616 +++ libavcodec/libxvid.c | 10 +- libavcodec/libzvbi-teletextdec.c | 8 +- libavcodec/ljpegenc.c | 10 +- libavcodec/loco.c | 11 +- libavcodec/loongarch/Makefile | 15 +- libavcodec/loongarch/h264_deblock_lasx.c | 2 +- .../loongarch/h264_intrapred_init_loongarch.c | 18 +- libavcodec/loongarch/h264_intrapred_lasx.c | 121 - ...pred_lasx.h => h264_intrapred_loongarch.h} | 12 +- libavcodec/loongarch/h264chroma.S | 966 ++++ .../loongarch/h264chroma_init_loongarch.c | 10 +- libavcodec/loongarch/h264chroma_lasx.c | 1280 ----- libavcodec/loongarch/h264chroma_lasx.h | 36 - libavcodec/loongarch/h264chroma_loongarch.h | 41 + libavcodec/loongarch/h264dsp.S | 1977 +++++++ libavcodec/loongarch/h264dsp_init_loongarch.c | 72 +- libavcodec/loongarch/h264dsp_lasx.c | 1356 +---- libavcodec/loongarch/h264dsp_lasx.h | 97 - libavcodec/loongarch/h264dsp_loongarch.h | 132 + libavcodec/loongarch/h264idct.S | 658 +++ libavcodec/loongarch/h264idct_lasx.c | 498 -- libavcodec/loongarch/h264idct_loongarch.c | 184 + libavcodec/loongarch/h264intrapred.S | 299 ++ libavcodec/loongarch/h264qpel.S | 1686 ++++++ .../loongarch/h264qpel_init_loongarch.c | 74 +- libavcodec/loongarch/h264qpel_lasx.c | 401 +- libavcodec/loongarch/h264qpel_lasx.h | 158 - libavcodec/loongarch/h264qpel_loongarch.h | 312 ++ libavcodec/loongarch/h264qpel_lsx.c | 487 ++ libavcodec/loongarch/hevc_add_res.S | 162 + libavcodec/loongarch/hevc_idct.S | 857 +++ libavcodec/loongarch/hevc_idct_lsx.c | 10 +- libavcodec/loongarch/hevc_mc.S | 4445 ++++++++++++++++ libavcodec/loongarch/hevc_mc_bi_lsx.c | 6 +- libavcodec/loongarch/hevc_mc_uni_lsx.c | 6 +- libavcodec/loongarch/hevc_mc_uniw_lsx.c | 4 +- libavcodec/loongarch/hevcdsp_init_loongarch.c | 177 + libavcodec/loongarch/hevcdsp_lasx.h | 136 + libavcodec/loongarch/hevcdsp_lsx.c | 6 +- libavcodec/loongarch/hevcdsp_lsx.h | 98 +- libavcodec/loongarch/idctdsp_lasx.c | 6 +- libavcodec/loongarch/idctdsp_loongarch.h | 6 +- libavcodec/loongarch/loongson_asm.S | 945 ++++ libavcodec/lossless_audiodsp.c | 2 + libavcodec/lossless_audiodsp.h | 1 + libavcodec/lossless_videodsp.c | 2 + libavcodec/lossless_videodsp.h | 1 + libavcodec/lossless_videoencdsp.c | 29 +- libavcodec/lossless_videoencdsp.h | 5 +- libavcodec/lpc.c | 41 +- libavcodec/lpc.h | 98 +- libavcodec/lpc_functions.h | 100 + libavcodec/lscrdec.c | 13 +- libavcodec/m101.c | 14 +- libavcodec/magicyuv.c | 94 +- libavcodec/magicyuvenc.c | 332 +- libavcodec/mdct_fixed_32.c | 51 - libavcodec/mdct_template.c | 209 - libavcodec/mdec.c | 4 +- libavcodec/me_cmp.c | 2 + libavcodec/me_cmp.h | 1 + libavcodec/mediacodec_wrapper.c | 557 +- libavcodec/mediacodec_wrapper.h | 73 + libavcodec/mediacodecdec.c | 13 +- libavcodec/mediacodecdec_common.c | 89 +- libavcodec/mediacodecenc.c | 416 +- libavcodec/mfenc.c | 46 +- libavcodec/midivid.c | 5 +- libavcodec/mimic.c | 17 +- libavcodec/mips/Makefile | 2 - libavcodec/mips/aaccoder_mips.c | 2502 --------- libavcodec/mips/aacdec_mips.c | 10 +- libavcodec/mips/aacdec_mips.h | 2 +- libavcodec/mips/aacsbr_mips.c | 4 +- libavcodec/mips/aacsbr_mips.h | 2 +- libavcodec/mips/ac3dsp_mips.c | 4 +- libavcodec/mips/cabac.h | 2 + libavcodec/mips/compute_antialias_fixed.h | 2 + libavcodec/mips/fft_mips.c | 516 -- libavcodec/mips/hevc_mc_bi_msa.c | 6 +- libavcodec/mips/hevc_mc_biw_msa.c | 6 +- libavcodec/mips/hevc_mc_uni_msa.c | 6 +- libavcodec/mips/hevc_mc_uniw_msa.c | 6 +- libavcodec/mips/hevcdsp_mmi.c | 20 +- libavcodec/mips/hevcdsp_msa.c | 6 +- libavcodec/mips/idctdsp_mips.h | 12 +- libavcodec/mips/idctdsp_mmi.c | 6 +- libavcodec/mips/idctdsp_msa.c | 6 +- libavcodec/mips/me_cmp_mips.h | 2 +- libavcodec/mips/pixblockdsp_mips.h | 6 +- libavcodec/mips/pixblockdsp_mmi.c | 4 +- libavcodec/mips/pixblockdsp_msa.c | 6 +- libavcodec/misc4.c | 5 +- libavcodec/mjpegdec.c | 192 +- libavcodec/mjpegdec_common.c | 2 +- libavcodec/mjpegenc.c | 6 +- libavcodec/mlp.c | 9 - libavcodec/mlp.h | 12 +- libavcodec/mlp_parse.h | 12 + libavcodec/mlpdec.c | 98 +- libavcodec/mlpdsp.c | 1 - libavcodec/mlpdsp.h | 8 - libavcodec/mlpenc.c | 1568 +++--- libavcodec/mmaldec.c | 22 +- libavcodec/mobiclip.c | 45 +- libavcodec/motion_est.c | 2 +- libavcodec/motionpixels.c | 4 +- libavcodec/movtextenc.c | 33 +- libavcodec/mp3_header_decompress_bsf.c | 130 - libavcodec/mpc.c | 2 +- libavcodec/mpc7.c | 52 +- libavcodec/mpc8.c | 4 +- libavcodec/mpeg12.c | 60 +- libavcodec/mpeg12dec.c | 505 +- libavcodec/mpeg12dec.h | 4 +- libavcodec/mpeg12enc.c | 46 +- libavcodec/mpeg12vlc.h | 14 +- libavcodec/mpeg4video_parser.c | 1 - libavcodec/mpeg4videodec.c | 138 +- libavcodec/mpeg4videodec.h | 3 +- libavcodec/mpeg4videoenc.c | 6 +- libavcodec/mpegaudiodata.h | 5 +- libavcodec/mpegaudiodec_common.c | 20 +- libavcodec/mpegaudiodec_template.c | 7 +- libavcodec/mpegaudiodsp.c | 6 +- libavcodec/mpegaudiodsp.h | 3 + libavcodec/mpegaudioenc_fixed.c | 1 - libavcodec/mpegaudioenc_float.c | 1 - libavcodec/mpegpicture.c | 127 +- libavcodec/mpegpicture.h | 16 +- libavcodec/mpegutils.c | 42 +- libavcodec/mpegvideo.c | 13 +- libavcodec/mpegvideo.h | 1 - libavcodec/mpegvideo_dec.c | 170 +- libavcodec/mpegvideo_enc.c | 224 +- libavcodec/mpegvideo_parser.c | 72 +- libavcodec/mpegvideoenc.h | 54 +- libavcodec/mscc.c | 12 +- libavcodec/msmpeg4_vc1_data.c | 61 +- libavcodec/msmpeg4_vc1_data.h | 10 +- libavcodec/msmpeg4data.h | 2 +- libavcodec/msmpeg4dec.c | 140 +- libavcodec/msmpeg4dec.h | 4 +- libavcodec/msmpeg4enc.c | 30 +- libavcodec/msp2dec.c | 2 +- libavcodec/msrle.c | 9 +- libavcodec/msrleenc.c | 300 ++ libavcodec/mss1.c | 11 +- libavcodec/mss2.c | 23 +- libavcodec/mss3.c | 5 +- libavcodec/mss4.c | 18 +- libavcodec/msvideo1.c | 9 +- libavcodec/msvideo1enc.c | 4 +- libavcodec/mv30.c | 18 +- libavcodec/mvcdec.c | 2 +- libavcodec/mvha.c | 8 +- libavcodec/mwsc.c | 23 +- libavcodec/mxpegdec.c | 4 +- libavcodec/neon/Makefile | 3 + libavcodec/notchlc.c | 11 +- libavcodec/nuv.c | 9 +- libavcodec/nvdec.c | 103 +- libavcodec/nvdec.h | 12 +- libavcodec/nvdec_av1.c | 15 +- libavcodec/nvdec_h264.c | 19 +- libavcodec/nvdec_hevc.c | 27 +- libavcodec/nvdec_mjpeg.c | 11 +- libavcodec/nvdec_mpeg12.c | 21 +- libavcodec/nvdec_mpeg4.c | 11 +- libavcodec/nvdec_vc1.c | 21 +- libavcodec/nvdec_vp8.c | 11 +- libavcodec/nvdec_vp9.c | 11 +- libavcodec/nvenc.c | 169 +- libavcodec/nvenc.h | 17 +- libavcodec/nvenc_av1.c | 141 +- libavcodec/nvenc_h264.c | 209 +- libavcodec/nvenc_hevc.c | 183 +- libavcodec/omx.c | 19 +- libavcodec/on2avc.c | 8 +- libavcodec/options.c | 12 +- libavcodec/options_table.h | 546 +- libavcodec/opus_pvq.c | 2 +- libavcodec/opus_silk.c | 10 +- libavcodec/opus_silk.h | 3 +- libavcodec/opusenc.c | 5 +- libavcodec/osq.c | 494 ++ libavcodec/packet.h | 161 +- libavcodec/packet_internal.h | 2 + libavcodec/pafvideo.c | 8 +- libavcodec/parser.c | 5 + libavcodec/parsers.c | 6 +- libavcodec/pcm-bluray.c | 5 +- libavcodec/pcm-blurayenc.c | 11 - libavcodec/pcm-dvd.c | 2 +- libavcodec/pcm-dvdenc.c | 4 +- libavcodec/pcm.c | 1 - libavcodec/pcx.c | 3 +- libavcodec/pdvdec.c | 140 + libavcodec/pgxdec.c | 2 +- libavcodec/photocd.c | 8 +- libavcodec/pictordec.c | 4 + libavcodec/pixblockdsp.c | 6 +- libavcodec/pixblockdsp.h | 10 +- libavcodec/pixlet.c | 6 +- libavcodec/pngdec.c | 299 +- libavcodec/pngenc.c | 62 +- libavcodec/pnm.c | 6 +- libavcodec/pnmdec.c | 6 +- libavcodec/ppc/Makefile | 3 - libavcodec/ppc/fdctdsp.c | 1 + libavcodec/ppc/fft_altivec.S | 458 -- libavcodec/ppc/fft_init.c | 168 - libavcodec/ppc/fft_vsx.c | 226 - libavcodec/ppc/fft_vsx.h | 829 --- libavcodec/ppc/h264dsp.c | 39 +- libavcodec/ppc/idctdsp.c | 1 + libavcodec/ppc/mpegaudiodsp_altivec.c | 2 + libavcodec/ppc/vp8dsp_altivec.c | 3 +- libavcodec/profiles.c | 239 +- libavcodec/profiles.h | 51 +- libavcodec/proresdata.c | 8 + libavcodec/proresdata.h | 3 + libavcodec/proresdec.h | 5 + libavcodec/proresdec2.c | 52 +- libavcodec/proresdsp.c | 8 +- libavcodec/proresdsp.h | 5 +- libavcodec/proresenc_anatoliy.c | 252 +- libavcodec/proresenc_kostya.c | 172 +- libavcodec/prosumer.c | 2 +- libavcodec/psd.c | 4 + libavcodec/pthread_frame.c | 231 +- libavcodec/qdm2_tablegen.h | 4 +- libavcodec/qdmc.c | 4 +- libavcodec/qdrw.c | 6 +- libavcodec/qoadec.c | 170 + libavcodec/qoidec.c | 2 +- libavcodec/qpeg.c | 17 +- libavcodec/qsv.c | 31 +- libavcodec/qsv_internal.h | 5 +- libavcodec/qsvdec.c | 113 +- libavcodec/qsvenc.c | 161 +- libavcodec/qsvenc.h | 54 +- libavcodec/qsvenc_av1.c | 8 +- libavcodec/qsvenc_h264.c | 42 +- libavcodec/qsvenc_hevc.c | 46 +- libavcodec/qsvenc_mpeg2.c | 12 +- libavcodec/qsvenc_vp9.c | 14 +- libavcodec/qtrle.c | 9 +- libavcodec/qtrleenc.c | 3 +- libavcodec/r210dec.c | 2 +- libavcodec/r210enc.c | 2 +- libavcodec/ra144enc.c | 1 - libavcodec/ra288.c | 2 +- libavcodec/ralf.c | 14 +- libavcodec/rasc.c | 15 +- libavcodec/ratecontrol.c | 12 +- libavcodec/raw.c | 2 + libavcodec/rawdec.c | 19 +- libavcodec/rdft.c | 120 - libavcodec/rdft.h | 52 - libavcodec/refstruct.c | 386 ++ libavcodec/refstruct.h | 297 ++ libavcodec/riscv/Makefile | 57 +- libavcodec/riscv/aacencdsp_init.c | 41 + libavcodec/riscv/aacencdsp_rvv.S | 38 + libavcodec/riscv/aacpsdsp_init.c | 16 +- libavcodec/riscv/aacpsdsp_rvv.S | 196 +- libavcodec/riscv/ac3dsp_init.c | 44 + libavcodec/riscv/ac3dsp_rvb.S | 38 + libavcodec/riscv/ac3dsp_rvv.S | 39 + libavcodec/riscv/alacdsp_init.c | 2 +- libavcodec/riscv/alacdsp_rvv.S | 6 +- libavcodec/riscv/audiodsp_init.c | 14 +- libavcodec/riscv/audiodsp_rvv.S | 19 +- libavcodec/riscv/blockdsp_init.c | 41 + libavcodec/riscv/blockdsp_rvv.S | 42 + libavcodec/riscv/bswapdsp_init.c | 15 +- libavcodec/riscv/bswapdsp_rvb.S | 43 +- libavcodec/riscv/bswapdsp_rvv.S | 44 +- .../timer.h => libavcodec/riscv/exrdsp_init.c | 31 +- libavcodec/riscv/exrdsp_rvv.S | 38 + libavcodec/riscv/flacdsp_init.c | 124 + libavcodec/riscv/flacdsp_rvv.S | 531 ++ libavcodec/riscv/fmtconvert_init.c | 2 +- libavcodec/riscv/fmtconvert_rvv.S | 2 +- .../{x86/dct_init.c => riscv/g722dsp_init.c} | 24 +- libavcodec/riscv/g722dsp_rvv.S | 68 + libavcodec/riscv/h264_chroma_init_riscv.c | 50 + libavcodec/riscv/h264_mc_chroma.S | 374 ++ libavcodec/riscv/huffyuvdsp_init.c | 41 + .../riscv/huffyuvdsp_rvv.S | 51 +- libavcodec/riscv/idctdsp_init.c | 2 +- libavcodec/riscv/idctdsp_rvv.S | 80 +- libavcodec/riscv/jpeg2000dsp_init.c | 40 + libavcodec/riscv/jpeg2000dsp_rvv.S | 72 + libavcodec/riscv/llauddsp_init.c | 44 + libavcodec/riscv/llauddsp_rvv.S | 71 + .../rdft_init_arm.c => riscv/llviddsp_init.c} | 21 +- libavcodec/riscv/llviddsp_rvv.S | 36 + libavcodec/riscv/llvidencdsp_init.c | 39 + .../riscv/llvidencdsp_rvv.S | 27 +- libavcodec/riscv/lpc_init.c | 43 + libavcodec/riscv/lpc_rvv.S | 118 + libavcodec/riscv/me_cmp_init.c | 112 + libavcodec/riscv/me_cmp_rvv.S | 527 ++ libavcodec/riscv/opusdsp_init.c | 23 +- libavcodec/riscv/opusdsp_rvv.S | 83 +- libavcodec/riscv/pixblockdsp_init.c | 27 +- libavcodec/riscv/pixblockdsp_rvv.S | 52 +- .../{x86/fft_init.c => riscv/rv34dsp_init.c} | 32 +- libavcodec/riscv/rv34dsp_rvv.S | 53 + libavcodec/riscv/sbrdsp_init.c | 72 + libavcodec/riscv/sbrdsp_rvv.S | 312 ++ libavcodec/riscv/svqenc_init.c | 41 + .../svqenc_rvv.S} | 38 +- libavcodec/riscv/takdsp_init.c | 45 + libavcodec/riscv/takdsp_rvv.S | 88 + libavcodec/riscv/utvideodsp_init.c | 45 + libavcodec/riscv/utvideodsp_rvv.S | 88 + libavcodec/riscv/vc1dsp_init.c | 47 + libavcodec/riscv/vc1dsp_rvv.S | 113 + libavcodec/riscv/vorbisdsp_init.c | 2 +- libavcodec/riscv/vorbisdsp_rvv.S | 2 +- .../vp8dsp_init.c} | 36 +- libavcodec/riscv/vp8dsp_rvv.S | 73 + libavcodec/rka.c | 54 +- libavcodec/rkmppdec.c | 8 +- libavcodec/rl.c | 4 +- libavcodec/rl.h | 2 +- libavcodec/roqvideo.c | 4 +- libavcodec/roqvideo.h | 4 +- libavcodec/roqvideodec.c | 8 +- libavcodec/roqvideoenc.c | 8 +- libavcodec/rpzaenc.c | 33 +- libavcodec/rscc.c | 11 +- libavcodec/rtjpeg.c | 2 +- libavcodec/rtjpeg.h | 4 +- libavcodec/rtv1.c | 152 + libavcodec/rv10.c | 69 +- libavcodec/rv30.c | 4 - libavcodec/rv34.c | 108 +- libavcodec/rv34.h | 14 +- libavcodec/rv34_parser.c | 19 +- libavcodec/rv34dsp.c | 2 + libavcodec/rv34dsp.h | 1 + libavcodec/rv40.c | 73 +- libavcodec/s302m.c | 10 +- libavcodec/sanm.c | 4 +- libavcodec/sbcdec.c | 1 - libavcodec/sbcenc.c | 6 +- libavcodec/sbr.h | 4 +- libavcodec/sbrdsp.h | 1 + libavcodec/sbrdsp_template.c | 277 + libavcodec/scpr.c | 12 +- libavcodec/scpr3.c | 10 +- libavcodec/screenpresso.c | 2 +- libavcodec/sga.c | 15 +- libavcodec/sgidec.c | 2 +- libavcodec/sgirledec.c | 2 +- libavcodec/sheervideo.c | 10 +- libavcodec/shorten.c | 8 +- libavcodec/sipr.c | 1 - libavcodec/sipr.h | 3 - libavcodec/smacker.c | 46 +- libavcodec/smc.c | 9 +- libavcodec/smcenc.c | 5 +- libavcodec/snow.c | 95 +- libavcodec/snow.h | 287 +- libavcodec/snow_dwt.h | 40 + libavcodec/snowdec.c | 168 +- libavcodec/snowenc.c | 602 ++- libavcodec/speedhqdec.c | 56 +- libavcodec/speexdec.c | 9 +- libavcodec/srtenc.c | 15 +- libavcodec/svq1dec.c | 91 +- libavcodec/svq1enc.c | 36 +- libavcodec/svq1encdsp.h | 26 + libavcodec/svq3.c | 7 +- libavcodec/tak.c | 24 +- libavcodec/tak.h | 7 +- libavcodec/takdsp.c | 10 +- libavcodec/takdsp.h | 7 +- libavcodec/targa.c | 4 + libavcodec/targa_y216dec.c | 2 +- libavcodec/targaenc.c | 5 +- libavcodec/tdsc.c | 3 +- libavcodec/tests/av1_levels.c | 126 + libavcodec/tests/avcodec.c | 8 +- libavcodec/tests/bitstream_template.c | 4 +- libavcodec/tests/codec_desc.c | 2 +- libavcodec/tests/dct.c | 6 +- libavcodec/tests/fft.c | 677 --- libavcodec/tests/h264_levels.c | 6 +- libavcodec/tests/jpeg2000dwt.c | 4 +- libavcodec/tests/motion.c | 1 + libavcodec/tests/snowenc.c | 2 +- libavcodec/texturedsp.c | 11 +- libavcodec/texturedsp.h | 18 +- libavcodec/texturedsp_template.c | 17 +- libavcodec/texturedspenc.c | 21 +- libavcodec/thread.h | 17 +- libavcodec/threadframe.h | 8 +- libavcodec/tiertexseqv.c | 4 + libavcodec/tiff.c | 161 +- libavcodec/tiff.h | 8 - libavcodec/tiff_data.h | 133 +- libavcodec/tiffenc.c | 15 +- libavcodec/tmv.c | 6 +- libavcodec/truemotion1.c | 5 + libavcodec/truemotion2.c | 13 +- libavcodec/truemotion2rt.c | 2 +- libavcodec/tscc.c | 4 + libavcodec/tscc2.c | 4 +- libavcodec/tta.c | 9 +- libavcodec/ttadata.c | 3 +- libavcodec/ttadsp.c | 3 +- libavcodec/ttmlenc.c | 43 +- libavcodec/utils.c | 152 +- libavcodec/utvideo.h | 34 +- libavcodec/utvideodec.c | 132 +- libavcodec/utvideodsp.c | 4 +- libavcodec/utvideodsp.h | 1 + libavcodec/utvideoenc.c | 30 +- libavcodec/v210dec.c | 15 +- libavcodec/v210x.c | 2 +- libavcodec/v308dec.c | 2 +- libavcodec/v408dec.c | 37 +- libavcodec/v408enc.c | 37 +- libavcodec/v410dec.c | 2 +- libavcodec/v4l2_buffers.c | 3 +- libavcodec/v4l2_context.c | 8 +- libavcodec/v4l2_m2m.c | 1 - libavcodec/v4l2_m2m_dec.c | 2 +- libavcodec/v4l2_m2m_enc.c | 36 +- libavcodec/vaapi_av1.c | 36 +- libavcodec/vaapi_decode.c | 23 +- libavcodec/vaapi_encode.c | 469 +- libavcodec/vaapi_encode.h | 66 +- libavcodec/vaapi_encode_av1.c | 951 ++++ libavcodec/vaapi_encode_h264.c | 209 +- libavcodec/vaapi_encode_h265.c | 134 +- libavcodec/vaapi_encode_mjpeg.c | 10 +- libavcodec/vaapi_encode_mpeg2.c | 28 +- libavcodec/vaapi_encode_vp8.c | 8 +- libavcodec/vaapi_encode_vp9.c | 36 +- libavcodec/vaapi_h264.c | 12 +- libavcodec/vaapi_hevc.c | 104 +- libavcodec/vaapi_hevc.h | 2 +- libavcodec/vaapi_mjpeg.c | 12 +- libavcodec/vaapi_mpeg2.c | 12 +- libavcodec/vaapi_mpeg4.c | 22 +- libavcodec/vaapi_vc1.c | 23 +- libavcodec/vaapi_vp8.c | 12 +- libavcodec/vaapi_vp9.c | 12 +- libavcodec/vb.c | 4 + libavcodec/vble.c | 5 +- libavcodec/vbndec.c | 6 +- libavcodec/vbnenc.c | 14 +- libavcodec/vc1.c | 71 +- libavcodec/vc1.h | 10 +- libavcodec/vc1_block.c | 105 +- libavcodec/vc1_loopfilter.c | 5 +- libavcodec/vc1_mc.c | 18 +- libavcodec/vc1_parser.c | 3 +- libavcodec/vc1data.c | 36 +- libavcodec/vc1data.h | 36 +- libavcodec/vc1dec.c | 337 +- libavcodec/vc1dsp.c | 2 + libavcodec/vc1dsp.h | 1 + libavcodec/vc2enc.c | 33 +- libavcodec/vcr1.c | 2 +- libavcodec/vdpau.c | 14 +- libavcodec/vdpau.h | 24 +- libavcodec/vdpau_av1.c | 18 +- libavcodec/vdpau_h264.c | 34 +- libavcodec/vdpau_hevc.c | 20 +- libavcodec/vdpau_mpeg12.c | 26 +- libavcodec/vdpau_mpeg4.c | 18 +- libavcodec/vdpau_vc1.c | 40 +- libavcodec/vdpau_vp9.c | 21 +- libavcodec/version_major.h | 21 +- libavcodec/videotoolbox.c | 128 +- libavcodec/videotoolbox.h | 65 - libavcodec/videotoolbox_vp9.c | 11 +- libavcodec/videotoolboxenc.c | 606 ++- libavcodec/vlc.c | 279 +- libavcodec/vlc.h | 249 +- libavcodec/vmdvideo.c | 3 +- libavcodec/vmixdec.c | 318 ++ libavcodec/vmnc.c | 4 +- libavcodec/vorbis.c | 8 +- libavcodec/vorbis.h | 4 +- libavcodec/vorbis_data.c | 14 - libavcodec/vorbis_data.h | 6 +- libavcodec/vorbis_parser.c | 3 +- libavcodec/vorbisdec.c | 10 +- libavcodec/vp3.c | 624 +-- libavcodec/vp5.c | 11 +- libavcodec/vp56.c | 17 +- libavcodec/vp6.c | 19 +- libavcodec/vp8.c | 108 +- libavcodec/vp8.h | 8 +- libavcodec/vp8data.c | 212 + libavcodec/vp8data.h | 191 +- libavcodec/vp8dsp.c | 2 + libavcodec/vp8dsp.h | 1 + libavcodec/vp9.c | 135 +- libavcodec/vp9dec.h | 3 +- libavcodec/vp9shared.h | 5 +- libavcodec/vqavideo.c | 4 + libavcodec/vqcdec.c | 18 +- libavcodec/{fft_float.c => vulkan.c} | 3 +- libavcodec/{mdct_float.c => vulkan.h} | 8 +- libavcodec/vulkan_av1.c | 649 +++ libavcodec/vulkan_decode.c | 1297 +++++ libavcodec/vulkan_decode.h | 191 + libavcodec/vulkan_h264.c | 570 ++ libavcodec/vulkan_hevc.c | 944 ++++ libavcodec/vulkan_video.c | 377 ++ libavcodec/vulkan_video.h | 91 + libavcodec/vvc.h | 156 + libavcodec/vvc/Makefile | 17 + libavcodec/vvc/vvc_cabac.c | 2478 +++++++++ libavcodec/vvc/vvc_cabac.h | 126 + libavcodec/vvc/vvc_ctu.c | 2551 +++++++++ libavcodec/vvc/vvc_ctu.h | 484 ++ libavcodec/vvc/vvc_data.c | 3486 ++++++++++++ libavcodec/vvc/vvc_data.h | 80 + libavcodec/vvc/vvc_filter.c | 1279 +++++ libavcodec/vvc/vvc_filter.h | 71 + libavcodec/vvc/vvc_filter_template.c | 872 +++ libavcodec/vvc/vvc_inter.c | 926 ++++ libavcodec/vvc/vvc_inter.h | 42 + libavcodec/vvc/vvc_inter_template.c | 466 ++ libavcodec/vvc/vvc_intra.c | 695 +++ libavcodec/vvc/vvc_intra.h | 49 + libavcodec/vvc/vvc_intra_template.c | 1015 ++++ libavcodec/vvc/vvc_intra_utils.c | 218 + libavcodec/vvc/vvc_itx_1d.c | 708 +++ libavcodec/vvc/vvc_itx_1d.h | 52 + libavcodec/vvc/vvc_mvs.c | 1917 +++++++ libavcodec/vvc/vvc_mvs.h | 48 + libavcodec/vvc/vvc_ps.c | 1178 +++++ libavcodec/vvc/vvc_ps.h | 262 + libavcodec/vvc/vvc_refs.c | 567 ++ libavcodec/vvc/vvc_refs.h | 58 + libavcodec/vvc/vvc_thread.c | 828 +++ libavcodec/vvc/vvc_thread.h | 36 + libavcodec/vvc/vvcdec.c | 1062 ++++ libavcodec/vvc/vvcdec.h | 229 + libavcodec/vvc/vvcdsp.c | 127 + libavcodec/vvc/vvcdsp.h | 172 + libavcodec/vvc/vvcdsp_template.c | 120 + libavcodec/vvc_parser.c | 517 ++ libavcodec/wavarc.c | 393 +- libavcodec/wavpack.c | 194 +- libavcodec/wavpackenc.c | 17 +- libavcodec/wbmpdec.c | 2 +- libavcodec/wcmv.c | 5 +- libavcodec/webp.c | 23 +- libavcodec/webvttenc.c | 15 +- libavcodec/wma.c | 12 +- libavcodec/wma.h | 2 +- libavcodec/wmadec.c | 6 +- libavcodec/wmalosslessdec.c | 6 +- libavcodec/wmaprodec.c | 91 +- libavcodec/wmavoice.c | 114 +- libavcodec/wmv2dec.c | 15 +- libavcodec/wnv1.c | 14 +- libavcodec/x86/Makefile | 11 +- libavcodec/x86/aacencdsp.asm | 2 +- libavcodec/x86/aacencdsp_init.c | 5 +- libavcodec/x86/ac3dsp.asm | 70 +- libavcodec/x86/ac3dsp_init.c | 6 +- libavcodec/x86/cabac.h | 2 +- libavcodec/x86/celt_pvq_search.asm | 4 +- libavcodec/x86/fft.asm | 838 --- libavcodec/x86/flacdsp.asm | 1 + libavcodec/x86/fpel.asm | 25 - libavcodec/x86/fpel.h | 8 - libavcodec/x86/h264_chromamc.asm | 10 +- libavcodec/x86/h264_idct.asm | 18 +- libavcodec/x86/h264_intrapred.asm | 31 +- libavcodec/x86/h264_intrapred_init.c | 14 +- libavcodec/x86/h264_qpel.c | 32 +- libavcodec/x86/h26x/h2656_inter.asm | 1143 ++++ libavcodec/x86/h26x/h2656dsp.c | 101 + libavcodec/x86/h26x/h2656dsp.h | 103 + libavcodec/x86/hevc_deblock.asm | 40 +- libavcodec/x86/hevc_mc.asm | 478 +- libavcodec/x86/hevcdsp_init.c | 108 +- libavcodec/x86/hpeldsp.asm | 77 + libavcodec/x86/hpeldsp.h | 4 - libavcodec/x86/hpeldsp_init.c | 29 +- libavcodec/x86/hpeldsp_vp3.asm | 99 - libavcodec/x86/idctdsp_init.c | 2 + libavcodec/x86/lossless_audiodsp.asm | 6 +- libavcodec/x86/mpegaudiodsp.c | 6 + libavcodec/x86/mpegvideoenc_template.c | 3 +- libavcodec/x86/proresdsp_init.c | 4 +- libavcodec/x86/rv34dsp.asm | 18 +- libavcodec/x86/rv40dsp.asm | 22 +- libavcodec/x86/sbrdsp.asm | 6 +- libavcodec/x86/simple_idct.asm | 37 +- libavcodec/x86/snowdsp.c | 1 - libavcodec/x86/takdsp.asm | 45 +- libavcodec/x86/takdsp_init.c | 17 +- libavcodec/x86/vp3dsp_init.c | 2 +- libavcodec/x86/vp6dsp.asm | 55 - libavcodec/x86/vp8dsp.asm | 30 +- libavcodec/x86/vp9itxfm.asm | 2 + libavcodec/x86/vp9itxfm_16bpp.asm | 12 +- libavcodec/x86/vvc/Makefile | 7 + libavcodec/x86/vvc/vvc_mc.asm | 302 ++ libavcodec/x86/vvc/vvcdsp_init.c | 256 + libavcodec/xan.c | 3 +- libavcodec/xbmdec.c | 29 +- libavcodec/xbmenc.c | 21 +- libavcodec/xfacedec.c | 1 - libavcodec/xl.c | 2 +- libavcodec/xpmdec.c | 34 +- libavcodec/xsubdec.c | 11 +- libavcodec/xvmc.h | 171 - libavcodec/xwddec.c | 2 +- libavcodec/y41pdec.c | 2 +- libavcodec/ylc.c | 8 +- libavcodec/yop.c | 4 + libavcodec/yuv4dec.c | 2 +- libavcodec/yuv4enc.c | 21 +- libavcodec/zerocodec.c | 7 +- libavcodec/zmbv.c | 4 +- libavdevice/Makefile | 1 + libavdevice/alldevices.c | 67 +- libavdevice/alsa_dec.c | 11 +- libavdevice/alsa_enc.c | 8 +- libavdevice/android_camera.c | 22 +- libavdevice/audiotoolbox.m | 4 + libavdevice/avdevice.c | 5 +- libavdevice/avfoundation.m | 79 +- libavdevice/bktr.c | 28 +- libavdevice/caca.c | 34 +- libavdevice/ccfifo.c | 24 + libavdevice/decklink_common.cpp | 110 + libavdevice/decklink_common.h | 21 +- libavdevice/decklink_common_c.h | 1 + libavdevice/decklink_dec.cpp | 132 +- libavdevice/decklink_dec_c.c | 115 +- libavdevice/decklink_enc.cpp | 362 +- libavdevice/decklink_enc_c.c | 51 +- libavdevice/dshow.c | 31 +- libavdevice/dshow_capture.h | 10 +- libavdevice/dshow_filter.c | 2 +- libavdevice/fbdev_common.c | 2 +- libavdevice/fbdev_dec.c | 11 +- libavdevice/gdigrab.c | 26 +- libavdevice/iec61883.c | 19 +- libavdevice/jack.c | 11 +- libavdevice/kmsgrab.c | 11 +- libavdevice/lavfi.c | 35 +- libavdevice/libcdio.c | 20 +- libavdevice/libdc1394.c | 11 +- libavdevice/openal-dec.c | 17 +- libavdevice/opengl_enc.c | 22 +- libavdevice/oss_dec.c | 11 +- libavdevice/pulse_audio_common.h | 2 +- libavdevice/pulse_audio_dec.c | 11 +- libavdevice/pulse_audio_enc.c | 24 +- libavdevice/sdl2.c | 10 + libavdevice/sndio_dec.c | 11 +- libavdevice/utils.c | 5 +- libavdevice/v4l2.c | 38 +- libavdevice/version_major.h | 9 +- libavdevice/vfwcap.c | 11 +- libavdevice/xcbgrab.c | 20 +- libavdevice/xv.c | 5 +- libavfilter/Makefile | 54 +- libavfilter/aap_template.c | 227 + libavfilter/aarch64/Makefile | 2 + libavfilter/aarch64/vf_bwdif_init_aarch64.c | 125 + libavfilter/aarch64/vf_bwdif_neon.S | 788 +++ libavfilter/aarch64/vf_nlmeans_neon.S | 78 +- libavfilter/adynamicequalizer_template.c | 438 ++ libavfilter/aeval.c | 1 + libavfilter/af_aap.c | 332 ++ libavfilter/af_acontrast.c | 10 +- libavfilter/af_acopy.c | 9 +- libavfilter/af_acrossover.c | 30 +- libavfilter/af_acrusher.c | 15 +- libavfilter/af_adeclick.c | 115 +- libavfilter/af_adecorrelate.c | 10 +- libavfilter/af_adelay.c | 67 +- libavfilter/af_adenorm.c | 10 +- libavfilter/af_aderivative.c | 12 +- libavfilter/af_adrc.c | 9 +- libavfilter/af_adynamicequalizer.c | 344 +- libavfilter/af_adynamicsmooth.c | 10 +- libavfilter/af_aecho.c | 9 +- libavfilter/af_aemphasis.c | 35 +- libavfilter/af_aexciter.c | 9 +- libavfilter/af_afade.c | 173 +- libavfilter/af_afftdn.c | 77 +- libavfilter/af_afftfilt.c | 9 +- libavfilter/af_afir.c | 398 +- libavfilter/af_afir.h | 18 +- libavfilter/af_afirdsp.h | 5 +- libavfilter/af_aformat.c | 31 +- libavfilter/af_afreqshift.c | 26 +- libavfilter/af_afwtdn.c | 61 +- libavfilter/af_agate.c | 27 +- libavfilter/af_aiir.c | 44 +- libavfilter/af_alimiter.c | 8 +- libavfilter/af_amerge.c | 29 +- libavfilter/af_amix.c | 25 +- libavfilter/af_amultiply.c | 6 +- libavfilter/af_anequalizer.c | 8 +- libavfilter/af_anlmdn.c | 21 +- libavfilter/af_anlms.c | 143 +- libavfilter/af_anull.c | 18 +- libavfilter/af_apad.c | 114 +- libavfilter/af_aphaser.c | 10 +- libavfilter/af_apsyclip.c | 9 +- libavfilter/af_apulsator.c | 30 +- libavfilter/af_aresample.c | 75 +- libavfilter/af_arls.c | 298 ++ libavfilter/af_arnndn.c | 9 +- libavfilter/af_asdr.c | 233 +- libavfilter/af_asetnsamples.c | 30 +- libavfilter/af_asetrate.c | 13 +- libavfilter/af_ashowinfo.c | 22 +- libavfilter/af_asoftclip.c | 30 +- libavfilter/af_aspectralstats.c | 41 +- libavfilter/af_asr.c | 10 +- libavfilter/af_astats.c | 285 +- libavfilter/af_asubboost.c | 10 +- libavfilter/af_asupercut.c | 16 +- libavfilter/af_atempo.c | 42 +- libavfilter/af_atilt.c | 10 +- libavfilter/af_axcorrelate.c | 148 +- libavfilter/af_biquads.c | 773 +-- libavfilter/af_bs2b.c | 8 +- libavfilter/af_channelmap.c | 176 +- libavfilter/af_channelsplit.c | 34 +- libavfilter/af_compensationdelay.c | 9 +- libavfilter/af_crossfeed.c | 9 +- libavfilter/af_crystalizer.c | 10 +- libavfilter/af_dcshift.c | 9 +- libavfilter/af_deesser.c | 18 +- libavfilter/af_dialoguenhance.c | 251 +- libavfilter/af_drmeter.c | 54 +- libavfilter/af_dynaudnorm.c | 9 +- libavfilter/af_earwax.c | 9 +- libavfilter/af_extrastereo.c | 9 +- libavfilter/af_firequalizer.c | 48 +- libavfilter/af_flanger.c | 25 +- libavfilter/af_haas.c | 19 +- libavfilter/af_hdcd.c | 33 +- libavfilter/af_headphone.c | 45 +- libavfilter/af_join.c | 49 +- libavfilter/af_ladspa.c | 6 +- libavfilter/af_loudnorm.c | 38 +- libavfilter/af_lv2.c | 6 +- libavfilter/af_mcompand.c | 4 +- libavfilter/af_pan.c | 32 +- libavfilter/af_replaygain.c | 38 +- libavfilter/af_rubberband.c | 86 +- libavfilter/af_sidechaincompress.c | 18 +- libavfilter/af_silencedetect.c | 10 +- libavfilter/af_silenceremove.c | 1067 ++-- libavfilter/af_sofalizer.c | 17 +- libavfilter/af_speechnorm.c | 9 +- libavfilter/af_stereotools.c | 45 +- libavfilter/af_stereowiden.c | 13 +- libavfilter/af_surround.c | 140 +- libavfilter/af_tremolo.c | 9 +- libavfilter/af_vibrato.c | 58 +- libavfilter/af_virtualbass.c | 10 +- libavfilter/af_volume.c | 38 +- libavfilter/af_volume.h | 2 + libavfilter/af_volumedetect.c | 9 +- libavfilter/afir_template.c | 359 +- libavfilter/allfilters.c | 22 +- libavfilter/anlms_template.c | 141 + libavfilter/arls_template.c | 164 + libavfilter/asrc_afdelaysrc.c | 27 +- libavfilter/asrc_afirsrc.c | 296 +- libavfilter/asrc_anoisesrc.c | 44 +- libavfilter/asrc_anullsrc.c | 35 +- libavfilter/asrc_flite.c | 100 +- libavfilter/asrc_hilbert.c | 1 + libavfilter/asrc_sinc.c | 37 +- libavfilter/asrc_sine.c | 1 + libavfilter/audio.c | 41 +- libavfilter/audio.h | 6 + libavfilter/avf_abitscope.c | 40 +- libavfilter/avf_ahistogram.c | 88 +- libavfilter/avf_aphasemeter.c | 85 +- libavfilter/avf_avectorscope.c | 38 +- libavfilter/avf_concat.c | 2 + libavfilter/avf_showcqt.c | 112 +- libavfilter/avf_showcwt.c | 812 ++- libavfilter/avf_showfreqs.c | 50 +- libavfilter/avf_showspatial.c | 10 +- libavfilter/avf_showspectrum.c | 185 +- libavfilter/avf_showvolume.c | 53 +- libavfilter/avf_showwaves.c | 267 +- libavfilter/avfilter.c | 358 +- libavfilter/avfilter.h | 157 +- libavfilter/avfilter_internal.h | 156 + libavfilter/avfiltergraph.c | 296 +- libavfilter/blend_modes.c | 5 +- libavfilter/buffersink.c | 138 +- libavfilter/buffersink.h | 8 +- libavfilter/buffersrc.c | 231 +- libavfilter/buffersrc.h | 11 +- libavfilter/bwdif.h | 42 - libavfilter/bwdifdsp.c | 226 + libavfilter/bwdifdsp.h | 85 + libavfilter/ccfifo.c | 195 + libavfilter/ccfifo.h | 129 + libavfilter/convolution.h | 3 +- libavfilter/cuda/load_helper.h | 2 +- libavfilter/deshake.h | 83 - libavfilter/dialoguenhance_template.c | 274 + libavfilter/dnn/Makefile | 11 +- libavfilter/dnn/dnn_backend_common.c | 7 - libavfilter/dnn/dnn_backend_native.c | 561 -- libavfilter/dnn/dnn_backend_native.h | 149 - .../dnn/dnn_backend_native_layer_avgpool.c | 147 - .../dnn/dnn_backend_native_layer_avgpool.h | 69 - .../dnn/dnn_backend_native_layer_conv2d.c | 265 - .../dnn/dnn_backend_native_layer_conv2d.h | 68 - .../dnn/dnn_backend_native_layer_dense.c | 151 - .../dnn/dnn_backend_native_layer_dense.h | 65 - .../dnn_backend_native_layer_depth2space.c | 102 - .../dnn_backend_native_layer_depth2space.h | 72 - .../dnn/dnn_backend_native_layer_mathbinary.c | 193 - .../dnn/dnn_backend_native_layer_mathbinary.h | 54 - .../dnn/dnn_backend_native_layer_mathunary.c | 156 - .../dnn/dnn_backend_native_layer_mathunary.h | 92 - .../dnn/dnn_backend_native_layer_maximum.c | 83 - .../dnn/dnn_backend_native_layer_pad.c | 268 - .../dnn/dnn_backend_native_layer_pad.h | 43 - libavfilter/dnn/dnn_backend_native_layers.c | 42 - libavfilter/dnn/dnn_backend_openvino.c | 892 +++- libavfilter/dnn/dnn_backend_tf.c | 524 +- libavfilter/dnn/dnn_backend_tf.h | 40 - libavfilter/dnn/dnn_backend_torch.cpp | 597 +++ libavfilter/dnn/dnn_interface.c | 58 +- libavfilter/dnn/dnn_io_proc.c | 259 +- libavfilter/dnn_filter_common.c | 41 +- libavfilter/dnn_filter_common.h | 2 +- libavfilter/dnn_interface.h | 30 +- libavfilter/drawutils.c | 1 + libavfilter/edge_template.c | 30 +- libavfilter/f_bench.c | 27 +- libavfilter/f_cue.c | 38 +- libavfilter/f_drawgraph.c | 45 +- libavfilter/f_ebur128.c | 91 +- libavfilter/f_graphmonitor.c | 414 +- libavfilter/f_interleave.c | 9 +- libavfilter/f_latency.c | 40 +- libavfilter/f_loop.c | 145 +- libavfilter/f_metadata.c | 47 +- libavfilter/f_perms.c | 30 +- libavfilter/f_realtime.c | 20 +- libavfilter/f_reverse.c | 6 +- libavfilter/f_select.c | 22 +- libavfilter/f_sendcmd.c | 26 +- libavfilter/f_sidedata.c | 71 +- libavfilter/f_streamselect.c | 7 +- libavfilter/f_zmq.c | 18 +- libavfilter/fifo.c | 165 - libavfilter/formats.c | 189 +- libavfilter/formats.h | 58 + libavfilter/framepool.c | 5 - libavfilter/framequeue.c | 5 +- libavfilter/framesync.c | 17 +- libavfilter/graphdump.c | 4 +- libavfilter/graphparser.c | 41 +- libavfilter/internal.h | 82 +- libavfilter/lavfutils.c | 3 +- libavfilter/median_template.c | 1 - libavfilter/opencl.c | 1 - libavfilter/opencl/deshake.cl | 2 +- libavfilter/opencl_source.h | 28 +- libavfilter/phase_template.c | 5 +- libavfilter/pthread.c | 26 +- libavfilter/qrencode.c | 822 +++ libavfilter/qsvvpp.c | 163 +- libavfilter/qsvvpp.h | 20 +- libavfilter/riscv/Makefile | 2 + .../riscv/af_afir_init.c | 37 +- libavfilter/riscv/af_afir_rvv.S | 55 + libavfilter/scale_eval.c | 9 +- libavfilter/scale_eval.h | 2 +- libavfilter/setpts.c | 199 +- libavfilter/settb.c | 18 +- libavfilter/signature_lookup.c | 64 +- libavfilter/silenceremove_template.c | 446 ++ libavfilter/split.c | 30 +- libavfilter/src_avsynctest.c | 26 +- libavfilter/src_movie.c | 318 +- libavfilter/stack_internal.c | 4 +- libavfilter/tests/dnn-layer-avgpool.c | 197 - libavfilter/tests/dnn-layer-conv2d.c | 248 - libavfilter/tests/dnn-layer-dense.c | 131 - libavfilter/tests/dnn-layer-depth2space.c | 102 - libavfilter/tests/dnn-layer-mathbinary.c | 214 - libavfilter/tests/dnn-layer-mathunary.c | 148 - libavfilter/tests/dnn-layer-maximum.c | 71 - libavfilter/tests/dnn-layer-pad.c | 239 - libavfilter/tests/filtfmts.c | 9 +- libavfilter/tests/formats.c | 3 - libavfilter/textutils.c | 382 ++ libavfilter/textutils.h | 229 + libavfilter/tinterlace.h | 2 + libavfilter/trim.c | 217 +- libavfilter/v360.h | 14 +- libavfilter/vaapi_vpp.c | 33 +- libavfilter/vaapi_vpp.h | 7 + libavfilter/vaf_spectrumsynth.c | 22 +- libavfilter/version.h | 2 +- libavfilter/version_major.h | 4 +- libavfilter/vf_addroi.c | 10 +- libavfilter/vf_alphamerge.c | 6 + libavfilter/vf_amplify.c | 2 - libavfilter/vf_aspect.c | 38 +- libavfilter/vf_atadenoise.c | 7 +- libavfilter/vf_avgblur.c | 12 +- libavfilter/vf_avgblur_opencl.c | 3 +- libavfilter/vf_avgblur_vulkan.c | 367 +- libavfilter/vf_backgroundkey.c | 3 +- libavfilter/vf_bilateral.c | 10 +- libavfilter/vf_bilateral_cuda.c | 4 - libavfilter/vf_bitplanenoise.c | 12 +- libavfilter/vf_blackdetect.c | 10 +- libavfilter/vf_blackframe.c | 12 +- libavfilter/vf_blend.c | 124 +- libavfilter/vf_blend_init.h | 4 +- libavfilter/vf_blend_vulkan.c | 335 +- libavfilter/vf_blockdetect.c | 10 +- libavfilter/vf_blurdetect.c | 10 +- libavfilter/vf_bm3d.c | 9 +- libavfilter/vf_boxblur.c | 9 +- libavfilter/vf_bwdif.c | 245 +- libavfilter/vf_bwdif_cuda.c | 368 ++ libavfilter/vf_bwdif_cuda.cu | 309 ++ libavfilter/vf_bwdif_vulkan.c | 413 ++ libavfilter/vf_cas.c | 10 +- libavfilter/vf_ccrepack.c | 97 + libavfilter/vf_chromaber_vulkan.c | 291 +- libavfilter/vf_chromakey.c | 34 +- libavfilter/vf_chromakey_cuda.c | 4 - libavfilter/vf_chromanr.c | 21 +- libavfilter/vf_chromashift.c | 27 +- libavfilter/vf_ciescope.c | 68 +- libavfilter/vf_codecview.c | 37 +- libavfilter/vf_colorbalance.c | 1 - libavfilter/vf_colorchannelmixer.c | 17 +- libavfilter/vf_colorconstancy.c | 17 +- libavfilter/vf_colorcontrast.c | 28 +- libavfilter/vf_colorcorrect.c | 60 +- libavfilter/vf_colorize.c | 24 +- libavfilter/vf_colorkey.c | 4 +- libavfilter/vf_colorkey_opencl.c | 7 +- libavfilter/vf_colorlevels.c | 27 +- libavfilter/vf_colormap.c | 12 +- libavfilter/vf_colormatrix.c | 11 +- libavfilter/vf_colorspace.c | 43 +- libavfilter/vf_colorspace_cuda.c | 14 +- libavfilter/vf_colortemperature.c | 72 +- libavfilter/vf_convolution.c | 44 +- libavfilter/vf_convolution_opencl.c | 9 +- libavfilter/vf_convolve.c | 29 +- libavfilter/vf_copy.c | 10 +- libavfilter/vf_coreimage.m | 6 + libavfilter/vf_corr.c | 160 +- libavfilter/vf_cover_rect.c | 16 +- libavfilter/vf_crop.c | 14 +- libavfilter/vf_cropdetect.c | 16 +- libavfilter/vf_curves.c | 39 +- libavfilter/vf_datascope.c | 34 +- libavfilter/vf_dblur.c | 10 +- libavfilter/vf_dctdnoiz.c | 10 +- libavfilter/vf_deband.c | 10 +- libavfilter/vf_deblock.c | 7 +- libavfilter/vf_dedot.c | 21 +- libavfilter/vf_deflicker.c | 19 +- libavfilter/vf_deinterlace_vaapi.c | 28 +- libavfilter/vf_delogo.c | 10 +- libavfilter/vf_derain.c | 23 +- libavfilter/vf_deshake.c | 81 +- libavfilter/vf_deshake_opencl.c | 17 +- libavfilter/vf_despill.c | 10 +- libavfilter/vf_detelecine.c | 10 +- libavfilter/vf_displace.c | 124 +- libavfilter/vf_dnn_classify.c | 29 +- libavfilter/vf_dnn_detect.c | 468 +- libavfilter/vf_dnn_processing.c | 34 +- libavfilter/vf_drawbox.c | 19 +- libavfilter/vf_drawtext.c | 1647 +++--- libavfilter/vf_edgedetect.c | 31 +- libavfilter/vf_elbg.c | 12 +- libavfilter/vf_entropy.c | 17 +- libavfilter/vf_epx.c | 1 + libavfilter/vf_eq.c | 24 +- libavfilter/vf_eq.h | 4 + libavfilter/vf_estdif.c | 72 +- libavfilter/vf_exposure.c | 11 +- libavfilter/vf_extractplanes.c | 22 +- libavfilter/vf_fade.c | 13 +- libavfilter/vf_feedback.c | 20 +- libavfilter/vf_fftdnoiz.c | 7 +- libavfilter/vf_fftfilt.c | 14 +- libavfilter/vf_field.c | 11 +- libavfilter/vf_fieldhint.c | 19 +- libavfilter/vf_fieldmatch.c | 71 +- libavfilter/vf_fieldorder.c | 25 +- libavfilter/vf_fillborders.c | 54 +- libavfilter/vf_find_rect.c | 10 +- libavfilter/vf_flip_vulkan.c | 232 +- libavfilter/vf_floodfill.c | 12 +- libavfilter/vf_format.c | 172 +- libavfilter/vf_fps.c | 40 +- libavfilter/vf_framepack.c | 43 +- libavfilter/vf_framerate.c | 9 +- libavfilter/vf_freezedetect.c | 11 +- libavfilter/vf_frei0r.c | 9 +- libavfilter/vf_fspp.c | 11 +- libavfilter/vf_fsync.c | 301 ++ libavfilter/vf_gblur.c | 11 +- libavfilter/vf_gblur_vulkan.c | 332 +- libavfilter/vf_geq.c | 23 +- libavfilter/vf_gradfun.c | 13 +- libavfilter/vf_grayworld.c | 11 +- libavfilter/vf_guided.c | 13 +- libavfilter/vf_hflip.c | 9 +- libavfilter/vf_histeq.c | 14 +- libavfilter/vf_histogram.c | 54 +- libavfilter/vf_hqdn3d.c | 11 +- libavfilter/vf_hqx.c | 1 + libavfilter/vf_hsvkey.c | 34 +- libavfilter/vf_hue.c | 10 +- libavfilter/vf_huesaturation.c | 30 +- libavfilter/vf_hwmap.c | 11 +- libavfilter/vf_hwupload.c | 1 + libavfilter/vf_hysteresis.c | 1 - libavfilter/vf_iccdetect.c | 14 +- libavfilter/vf_iccgen.c | 68 +- libavfilter/vf_identity.c | 2 - libavfilter/vf_idet.c | 41 +- libavfilter/vf_il.c | 53 +- libavfilter/vf_kerndeint.c | 16 +- libavfilter/vf_lagfun.c | 2 - libavfilter/vf_lenscorrection.c | 6 +- libavfilter/vf_lensfun.c | 54 +- libavfilter/vf_libplacebo.c | 1336 +++-- libavfilter/vf_libvmaf.c | 353 +- libavfilter/vf_limitdiff.c | 1 - libavfilter/vf_limiter.c | 10 +- libavfilter/vf_lumakey.c | 12 +- libavfilter/vf_lut.c | 7 +- libavfilter/vf_lut3d.c | 71 +- libavfilter/vf_maskedclamp.c | 1 - libavfilter/vf_maskedmerge.c | 1 - libavfilter/vf_maskedminmax.c | 1 - libavfilter/vf_maskedthreshold.c | 7 +- libavfilter/vf_maskfun.c | 10 +- libavfilter/vf_mcdeint.c | 46 +- libavfilter/vf_median.c | 10 +- libavfilter/vf_mergeplanes.c | 23 +- libavfilter/vf_mestimate.c | 16 +- libavfilter/vf_midequalizer.c | 1 - libavfilter/vf_minterpolate.c | 24 +- libavfilter/vf_misc_vaapi.c | 11 +- libavfilter/vf_mix.c | 96 +- libavfilter/vf_monochrome.c | 32 +- libavfilter/vf_morpho.c | 277 +- libavfilter/vf_mpdecimate.c | 34 +- libavfilter/vf_multiply.c | 1 - libavfilter/vf_negate.c | 31 +- libavfilter/vf_neighbor.c | 10 +- libavfilter/vf_neighbor_opencl.c | 3 +- libavfilter/vf_nlmeans.c | 10 +- libavfilter/vf_nlmeans_opencl.c | 3 +- libavfilter/vf_nlmeans_vulkan.c | 1113 ++++ libavfilter/vf_nnedi.c | 87 +- libavfilter/vf_noise.c | 22 +- libavfilter/vf_normalize.c | 11 +- libavfilter/vf_null.c | 18 +- libavfilter/vf_ocr.c | 10 +- libavfilter/vf_overlay.c | 296 +- libavfilter/vf_overlay.h | 3 + libavfilter/vf_overlay_cuda.c | 34 +- libavfilter/vf_overlay_opencl.c | 3 +- libavfilter/vf_overlay_qsv.c | 9 +- libavfilter/vf_overlay_vaapi.c | 13 +- libavfilter/vf_overlay_vulkan.c | 400 +- libavfilter/vf_owdenoise.c | 10 +- libavfilter/vf_pad.c | 4 +- libavfilter/vf_pad_opencl.c | 8 +- libavfilter/vf_palettegen.c | 10 +- libavfilter/vf_paletteuse.c | 29 +- libavfilter/vf_perspective.c | 24 +- libavfilter/vf_phase.c | 14 +- libavfilter/vf_photosensitivity.c | 12 +- libavfilter/vf_pixdesctest.c | 9 +- libavfilter/vf_pixelize.c | 10 +- libavfilter/vf_pp.c | 10 +- libavfilter/vf_pp7.c | 19 +- libavfilter/vf_premultiply.c | 6 +- libavfilter/vf_procamp_vaapi.c | 12 +- libavfilter/vf_program_opencl.c | 4 +- libavfilter/vf_pseudocolor.c | 203 +- libavfilter/vf_psnr.c | 7 + libavfilter/vf_pullup.c | 34 +- libavfilter/vf_qp.c | 12 +- libavfilter/vf_quirc.c | 183 + libavfilter/vf_random.c | 2 - libavfilter/vf_readeia608.c | 11 +- libavfilter/vf_readvitc.c | 12 +- libavfilter/vf_remap.c | 2 +- libavfilter/vf_remap_opencl.c | 11 +- libavfilter/vf_removegrain.c | 10 +- libavfilter/vf_removelogo.c | 10 +- libavfilter/vf_repeatfields.c | 17 +- libavfilter/vf_rotate.c | 4 +- libavfilter/vf_sab.c | 11 +- libavfilter/vf_scale.c | 308 +- libavfilter/vf_scale_cuda.c | 23 +- libavfilter/vf_scale_cuda.cu | 151 +- libavfilter/vf_scale_npp.c | 83 +- libavfilter/vf_scale_vaapi.c | 46 +- libavfilter/vf_scale_vt.c | 270 + libavfilter/vf_scale_vulkan.c | 390 +- libavfilter/vf_scdet.c | 11 +- libavfilter/vf_scroll.c | 11 +- libavfilter/vf_selectivecolor.c | 16 +- libavfilter/vf_separatefields.c | 21 +- libavfilter/vf_setparams.c | 221 +- libavfilter/vf_sharpen_npp.c | 4 +- libavfilter/vf_shear.c | 8 +- libavfilter/vf_showinfo.c | 112 +- libavfilter/vf_shuffleframes.c | 9 +- libavfilter/vf_shufflepixels.c | 18 +- libavfilter/vf_shuffleplanes.c | 10 +- libavfilter/vf_signalstats.c | 16 +- libavfilter/vf_signature.c | 26 +- libavfilter/vf_siti.c | 10 +- libavfilter/vf_smartblur.c | 13 +- libavfilter/vf_spp.c | 17 +- libavfilter/vf_sr.c | 8 +- libavfilter/vf_ssim.c | 7 + libavfilter/vf_ssim360.c | 34 +- libavfilter/vf_stack.c | 3 +- libavfilter/vf_stack_qsv.c | 6 +- libavfilter/vf_stack_vaapi.c | 6 +- libavfilter/vf_stereo3d.c | 114 +- libavfilter/vf_subtitles.c | 41 +- libavfilter/vf_super2xsai.c | 1 - libavfilter/vf_swaprect.c | 42 +- libavfilter/vf_swapuv.c | 9 +- libavfilter/vf_telecine.c | 40 +- libavfilter/vf_thumbnail.c | 8 +- libavfilter/vf_thumbnail_cuda.c | 2 +- libavfilter/vf_tile.c | 2 +- libavfilter/vf_tiltandshift.c | 370 ++ libavfilter/vf_tinterlace.c | 94 +- libavfilter/vf_tmidequalizer.c | 1 - libavfilter/vf_tonemap.c | 27 +- libavfilter/vf_tonemap_opencl.c | 59 +- libavfilter/vf_tonemap_vaapi.c | 27 +- libavfilter/vf_tpad.c | 53 +- libavfilter/vf_transpose.c | 10 +- libavfilter/vf_transpose_npp.c | 20 +- libavfilter/vf_transpose_opencl.c | 13 +- libavfilter/vf_transpose_vaapi.c | 12 +- libavfilter/vf_transpose_vt.c | 285 + libavfilter/vf_transpose_vulkan.c | 238 +- libavfilter/vf_unsharp.c | 40 +- libavfilter/vf_unsharp_opencl.c | 3 +- libavfilter/vf_untile.c | 10 +- libavfilter/vf_uspp.c | 222 +- libavfilter/vf_v360.c | 224 +- libavfilter/vf_vaguedenoiser.c | 25 +- libavfilter/vf_varblur.c | 50 +- libavfilter/vf_vectorscope.c | 62 +- libavfilter/vf_vflip.c | 9 +- libavfilter/vf_vfrdet.c | 10 +- libavfilter/vf_vibrance.c | 52 +- libavfilter/vf_vidstabdetect.c | 27 +- libavfilter/vf_vidstabtransform.c | 36 +- libavfilter/vf_vif.c | 8 +- libavfilter/vf_vignette.c | 22 +- libavfilter/vf_vmafmotion.c | 10 +- libavfilter/vf_vpp_qsv.c | 294 +- libavfilter/vf_w3fdif.c | 22 +- libavfilter/vf_waveform.c | 98 +- libavfilter/vf_weave.c | 30 +- libavfilter/vf_xbr.c | 1 + libavfilter/vf_xfade.c | 677 ++- libavfilter/vf_xfade_opencl.c | 25 +- libavfilter/vf_xfade_vulkan.c | 720 +++ libavfilter/vf_xmedian.c | 28 +- libavfilter/vf_yadif.c | 30 +- libavfilter/vf_yadif_cuda.c | 31 +- libavfilter/vf_yadif_videotoolbox.m | 25 +- libavfilter/vf_yaepblur.c | 10 +- libavfilter/vf_zoompan.c | 10 +- libavfilter/vf_zscale.c | 294 +- libavfilter/video.c | 31 +- libavfilter/video.h | 7 + libavfilter/vsrc_cellauto.c | 2 - libavfilter/vsrc_ddagrab.c | 219 +- libavfilter/vsrc_gradients.c | 91 +- libavfilter/vsrc_mandelbrot.c | 24 +- libavfilter/vsrc_mptestsrc.c | 29 +- libavfilter/vsrc_sierpinski.c | 8 +- libavfilter/vsrc_testsrc.c | 369 +- libavfilter/vsrc_testsrc_vulkan.c | 376 ++ libavfilter/vulkan_filter.c | 485 +- libavfilter/vulkan_filter.h | 39 +- {libavutil => libavfilter}/vulkan_glslang.c | 19 +- {libavutil => libavfilter}/vulkan_shaderc.c | 8 +- libavfilter/vulkan_spirv.h | 45 + libavfilter/window_func.h | 65 +- libavfilter/x86/af_afir.asm | 27 + libavfilter/x86/af_afir_init.c | 5 + libavfilter/x86/vf_bwdif.asm | 29 +- libavfilter/x86/vf_bwdif_init.c | 26 +- libavfilter/x86/vf_overlay.asm | 18 +- libavfilter/x86/vf_ssim.asm | 25 +- libavfilter/yadif.h | 8 + libavfilter/yadif_common.c | 101 +- libavformat/3dostr.c | 11 +- libavformat/4xm.c | 9 +- libavformat/Makefile | 44 +- libavformat/a64.c | 3 + libavformat/aacdec.c | 17 +- libavformat/aadec.c | 14 +- libavformat/aaxdec.c | 12 +- libavformat/ac3dec.c | 25 +- libavformat/ac4dec.c | 107 + libavformat/ac4enc.c | 91 + libavformat/acedec.c | 9 +- libavformat/acm.c | 12 +- libavformat/act.c | 7 +- libavformat/adp.c | 9 +- libavformat/ads.c | 9 +- libavformat/adtsenc.c | 13 +- libavformat/adxdec.c | 17 +- libavformat/{aea.c => aeadec.c} | 58 +- libavformat/aeaenc.c | 108 + libavformat/afc.c | 11 +- libavformat/aiffdec.c | 14 +- libavformat/aiffenc.c | 22 +- libavformat/aixdec.c | 10 +- libavformat/allformats.c | 730 +-- libavformat/alp.c | 21 +- libavformat/amr.c | 65 +- libavformat/amvenc.c | 9 +- libavformat/anm.c | 8 +- libavformat/apac.c | 12 +- libavformat/apc.c | 6 +- libavformat/ape.c | 13 +- libavformat/apm.c | 25 +- libavformat/apngdec.c | 13 +- libavformat/apngenc.c | 11 +- libavformat/aptxdec.c | 25 +- libavformat/aqtitledec.c | 13 +- libavformat/argo_asf.c | 27 +- libavformat/argo_brp.c | 7 +- libavformat/argo_cvg.c | 63 +- libavformat/asfdec_f.c | 18 +- libavformat/asfdec_o.c | 13 +- libavformat/asfenc.c | 6 +- libavformat/assdec.c | 13 +- libavformat/assenc.c | 33 +- libavformat/astdec.c | 13 +- libavformat/astenc.c | 27 +- libavformat/au.c | 29 +- libavformat/av1.c | 12 +- libavformat/av1dec.c | 77 +- libavformat/avformat.c | 263 +- libavformat/avformat.h | 709 ++- libavformat/avidec.c | 50 +- libavformat/avio.c | 237 +- libavformat/avio.h | 14 +- libavformat/avio_internal.h | 51 +- libavformat/aviobuf.c | 256 +- libavformat/avisynth.c | 33 +- libavformat/avlanguage.c | 10 +- libavformat/avr.c | 23 +- libavformat/avs.c | 7 +- libavformat/bethsoftvid.c | 7 +- libavformat/bfi.c | 6 +- libavformat/bink.c | 8 +- libavformat/binka.c | 11 +- libavformat/bintext.c | 63 +- libavformat/bit.c | 18 +- libavformat/bitstream.c | 1 + libavformat/bluray.c | 1 - libavformat/bmv.c | 9 +- libavformat/boadec.c | 9 +- libavformat/bonk.c | 12 +- libavformat/brstm.c | 20 +- libavformat/c93.c | 7 +- libavformat/cafdec.c | 25 +- libavformat/cafenc.c | 7 +- libavformat/cdg.c | 13 +- libavformat/cdxl.c | 13 +- libavformat/chromaprint.c | 21 +- libavformat/cinedec.c | 7 +- libavformat/codec2.c | 36 +- libavformat/concat.c | 4 +- libavformat/concatdec.c | 26 +- libavformat/crypto.c | 4 +- libavformat/dashdec.c | 25 +- libavformat/dashenc.c | 122 +- libavformat/dauddec.c | 15 +- libavformat/daudenc.c | 28 +- libavformat/dcstr.c | 11 +- libavformat/demux.c | 374 +- libavformat/demux.h | 117 +- libavformat/demux_utils.c | 28 +- libavformat/derf.c | 9 +- libavformat/dfa.c | 9 +- libavformat/dfpwmdec.c | 32 +- libavformat/dhav.c | 13 +- libavformat/dovi_isom.c | 20 +- libavformat/dovi_isom.h | 7 +- libavformat/dsfdec.c | 9 +- libavformat/dsicin.c | 7 +- libavformat/dss.c | 9 +- libavformat/dtsdec.c | 13 +- libavformat/dtshddec.c | 11 +- libavformat/dump.c | 518 +- libavformat/dv.c | 79 +- libavformat/dv.h | 2 +- libavformat/dvdclut.c | 75 + .../dvdclut.h | 29 +- libavformat/dvdvideodec.c | 1714 ++++++ libavformat/dvenc.c | 116 +- libavformat/dxa.c | 9 +- libavformat/eacdata.c | 9 +- libavformat/electronicarts.c | 9 +- libavformat/epafdec.c | 11 +- libavformat/evc.c | 388 ++ libavformat/evc.h | 69 + libavformat/evcdec.c | 218 + .../fft-fixed32.c => libavformat/ffjni.c | 8 +- libavformat/ffmetadec.c | 6 +- libavformat/fifo.c | 47 +- libavformat/fifo_test.c | 152 - libavformat/file.c | 168 +- libavformat/filmstripdec.c | 11 +- libavformat/filmstripenc.c | 7 +- libavformat/fitsdec.c | 98 +- libavformat/fitsenc.c | 92 +- libavformat/flacdec.c | 53 +- libavformat/flic.c | 7 +- libavformat/flv.h | 17 +- libavformat/flvdec.c | 286 +- libavformat/flvenc.c | 275 +- libavformat/format.c | 31 +- libavformat/frmdec.c | 7 +- libavformat/fsb.c | 12 +- libavformat/ftp.c | 1 + libavformat/fwse.c | 11 +- libavformat/g722.c | 19 +- libavformat/g723_1.c | 11 +- libavformat/g726.c | 19 +- libavformat/g729dec.c | 13 +- libavformat/gdv.c | 7 +- libavformat/genh.c | 9 +- libavformat/gif.c | 17 +- libavformat/gifdec.c | 256 +- libavformat/gopher.c | 3 + libavformat/gsmdec.c | 15 +- libavformat/gxf.c | 6 +- libavformat/gxfenc.c | 32 +- libavformat/hca.c | 62 +- libavformat/hcom.c | 7 +- libavformat/hdsenc.c | 2 +- libavformat/hls.c | 74 +- libavformat/hls_sample_encryption.c | 7 +- libavformat/hlsenc.c | 188 +- libavformat/hlsplaylist.c | 6 +- libavformat/hlsplaylist.h | 2 +- libavformat/hlsproto.c | 1 - libavformat/hnm.c | 13 +- libavformat/http.c | 13 +- libavformat/httpauth.c | 1 - libavformat/iamf.c | 125 + libavformat/iamf.h | 202 + libavformat/iamf_parse.c | 1121 ++++ libavformat/iamf_parse.h | 37 + libavformat/iamf_reader.c | 338 ++ libavformat/iamf_reader.h | 49 + libavformat/iamf_writer.c | 1151 ++++ libavformat/iamf_writer.h | 42 + libavformat/iamfdec.c | 195 + libavformat/iamfenc.c | 201 + libavformat/icodec.c | 11 +- libavformat/id3v2.c | 15 +- libavformat/idcin.c | 8 +- libavformat/idroqdec.c | 9 +- libavformat/idroqenc.c | 3 + libavformat/iff.c | 13 +- libavformat/ifv.c | 9 +- libavformat/ilbc.c | 26 +- libavformat/imf.h | 6 +- libavformat/imf_cpl.c | 179 +- libavformat/imfdec.c | 24 +- libavformat/img2_alias_pix.c | 9 +- libavformat/img2_brender_pix.c | 9 +- libavformat/img2dec.c | 90 +- libavformat/img2enc.c | 2 + libavformat/imx.c | 11 +- libavformat/ingenientdec.c | 13 +- libavformat/internal.h | 75 +- libavformat/ipmovie.c | 6 +- libavformat/ipudec.c | 13 +- libavformat/ircamdec.c | 11 +- libavformat/ircamenc.c | 7 +- libavformat/isom.c | 2 + libavformat/isom.h | 47 +- libavformat/isom_tags.c | 12 +- libavformat/iss.c | 7 +- libavformat/iv8.c | 9 +- libavformat/ivfdec.c | 24 +- libavformat/ivfenc.c | 25 +- libavformat/jacosubdec.c | 29 +- libavformat/jacosubenc.c | 4 + libavformat/jpegxl_anim_dec.c | 206 + .../avfft.c => libavformat/jpegxl_parse.c | 11 +- libavformat/jpegxl_probe.c | 411 -- libavformat/jvdec.c | 9 +- libavformat/kvag.c | 30 +- libavformat/lafdec.c | 17 +- libavformat/latmenc.c | 2 + libavformat/libamqp.c | 6 +- libavformat/libgme.c | 11 +- libavformat/libmodplug.c | 11 +- libavformat/libopenmpt.c | 27 +- libavformat/librist.c | 8 +- libavformat/librtmp.c | 14 +- libavformat/libsmbclient.c | 2 - libavformat/libsrt.c | 52 +- libavformat/libssh.c | 11 +- libavformat/libzmq.c | 5 +- libavformat/lmlm4.c | 7 +- libavformat/loasdec.c | 13 +- libavformat/lrcdec.c | 21 +- libavformat/lrcenc.c | 9 +- libavformat/luodatdec.c | 11 +- libavformat/lvfdec.c | 11 +- libavformat/lxfdec.c | 8 +- libavformat/matroska.c | 66 +- libavformat/matroska.h | 52 +- libavformat/matroskadec.c | 1397 +++-- libavformat/matroskaenc.c | 538 +- libavformat/mca.c | 8 +- libavformat/mccdec.c | 11 +- libavformat/md5proto.c | 1 - libavformat/mgsts.c | 9 +- libavformat/microdvddec.c | 11 +- libavformat/microdvdenc.c | 9 +- libavformat/mlpdec.c | 25 +- libavformat/mlvdec.c | 10 +- libavformat/mm.c | 9 +- libavformat/mmf.c | 12 +- libavformat/mmsh.c | 3 +- libavformat/mmst.c | 4 +- libavformat/mods.c | 11 +- libavformat/moflex.c | 13 +- libavformat/mov.c | 1852 +++++-- libavformat/mov_chan.c | 837 ++- libavformat/mov_chan.h | 30 + libavformat/movenc.c | 1000 +++- libavformat/movenc.h | 6 + libavformat/mp3dec.c | 37 +- libavformat/mp3enc.c | 16 +- libavformat/mpc.c | 8 +- libavformat/mpc8.c | 6 +- libavformat/mpeg.c | 31 +- libavformat/mpeg.h | 1 + libavformat/mpegenc.c | 26 +- libavformat/mpegts.c | 106 +- libavformat/mpegts.h | 1 + libavformat/mpegtsenc.c | 228 +- libavformat/mpjpeg.c | 2 + libavformat/mpjpegdec.c | 31 +- libavformat/mpl2dec.c | 11 +- libavformat/mpsubdec.c | 16 +- libavformat/msf.c | 9 +- libavformat/msnwc_tcp.c | 7 +- libavformat/mspdec.c | 9 +- libavformat/mtaf.c | 9 +- libavformat/mtv.c | 7 +- libavformat/musx.c | 9 +- libavformat/mux.c | 119 +- libavformat/mux.h | 37 +- libavformat/mux_utils.c | 38 +- libavformat/mvdec.c | 7 +- libavformat/mvi.c | 9 +- libavformat/mxf.c | 2 + libavformat/mxf.h | 5 +- libavformat/mxfdec.c | 408 +- libavformat/mxfenc.c | 467 +- libavformat/mxg.c | 9 +- libavformat/ncdec.c | 9 +- libavformat/network.c | 14 +- libavformat/network.h | 2 +- libavformat/nistspheredec.c | 11 +- libavformat/nspdec.c | 11 +- libavformat/nsvdec.c | 11 +- libavformat/nut.c | 2 + libavformat/nutdec.c | 18 +- libavformat/nutenc.c | 53 +- libavformat/nuv.c | 8 +- libavformat/oggdec.c | 12 +- libavformat/oggenc.c | 27 +- libavformat/oggparseflac.c | 24 +- libavformat/oggparsevorbis.c | 7 +- libavformat/omadec.c | 15 +- libavformat/omaenc.c | 3 + libavformat/options.c | 256 +- libavformat/options_table.h | 82 +- libavformat/os_support.c | 6 +- libavformat/osq.c | 118 + libavformat/paf.c | 9 +- libavformat/pcm.c | 41 +- libavformat/pcm.h | 1 + libavformat/pcmdec.c | 49 +- libavformat/pcmenc.c | 3 + libavformat/pdvdec.c | 174 + libavformat/pjsdec.c | 11 +- libavformat/pmpdec.c | 7 +- libavformat/pp_bnk.c | 9 +- libavformat/protocols.c | 3 + libavformat/psxstr.c | 20 +- libavformat/pva.c | 7 +- libavformat/pvfdec.c | 11 +- libavformat/qcp.c | 7 +- libavformat/qoadec.c | 111 + libavformat/r3d.c | 7 +- libavformat/rangecoder_dec.c | 1 + libavformat/rawdec.c | 23 +- libavformat/rawdec.h | 25 +- libavformat/rawenc.c | 201 +- libavformat/rawvideodec.c | 63 +- libavformat/rcwtenc.c | 173 + libavformat/rdt.c | 10 +- libavformat/realtextdec.c | 17 +- libavformat/redspark.c | 9 +- libavformat/replaygain.c | 10 +- libavformat/riff.c | 5 + libavformat/riff.h | 3 +- libavformat/riffdec.c | 26 +- libavformat/riffenc.c | 8 +- libavformat/rka.c | 8 +- libavformat/rl2.c | 6 +- libavformat/rm.h | 2 +- libavformat/rmdec.c | 34 +- libavformat/rpl.c | 11 +- libavformat/rsd.c | 12 +- libavformat/rsodec.c | 11 +- libavformat/rsoenc.c | 2 + libavformat/rtmpdh.c | 21 +- libavformat/rtmppkt.c | 12 +- libavformat/rtmppkt.h | 8 + libavformat/rtmpproto.c | 56 +- libavformat/rtpdec_asf.c | 2 +- libavformat/rtpdec_qt.c | 2 +- libavformat/rtpenc.h | 16 +- libavformat/rtpenc_chain.c | 1 + libavformat/rtpenc_h261.c | 4 +- libavformat/rtpenc_h263.c | 4 +- libavformat/rtpenc_jpeg.c | 41 +- libavformat/rtpenc_rfc4175.c | 4 +- libavformat/rtpenc_vc2hq.c | 15 +- libavformat/rtpproto.c | 1 - libavformat/rtsp.c | 69 +- libavformat/rtsp.h | 1 + libavformat/rtspdec.c | 15 +- libavformat/s337m.c | 9 +- libavformat/samidec.c | 17 +- libavformat/sapdec.c | 15 +- libavformat/sauce.c | 7 +- libavformat/sbcdec.c | 13 +- libavformat/sbgdec.c | 76 +- libavformat/sccdec.c | 12 +- libavformat/sccenc.c | 16 +- libavformat/scd.c | 11 +- libavformat/sdns.c | 10 +- libavformat/sdp.c | 3 + libavformat/sdr2.c | 11 +- libavformat/sdsdec.c | 11 +- libavformat/sdxdec.c | 11 +- libavformat/seek.c | 39 +- libavformat/segafilm.c | 9 +- libavformat/segafilmenc.c | 10 +- libavformat/segment.c | 25 +- libavformat/serdec.c | 15 +- libavformat/sga.c | 11 +- libavformat/shortendec.c | 13 +- libavformat/sierravmd.c | 9 +- libavformat/siff.c | 14 +- libavformat/smacker.c | 11 +- libavformat/smjpegdec.c | 11 +- libavformat/smjpegenc.c | 6 +- libavformat/smoothstreamingenc.c | 4 +- libavformat/smush.c | 7 +- libavformat/sol.c | 10 +- libavformat/soxdec.c | 17 +- libavformat/soxenc.c | 4 +- libavformat/spdifdec.c | 9 +- libavformat/spdifenc.c | 6 +- libavformat/srtdec.c | 13 +- libavformat/srtenc.c | 9 +- libavformat/srtpproto.c | 2 - libavformat/stldec.c | 11 +- libavformat/subfile.c | 8 +- libavformat/subtitles.c | 23 +- libavformat/subtitles.h | 18 +- libavformat/subviewer1dec.c | 11 +- libavformat/subviewerdec.c | 11 +- libavformat/supdec.c | 13 +- libavformat/supenc.c | 13 +- libavformat/svag.c | 9 +- libavformat/svs.c | 9 +- libavformat/swfdec.c | 6 +- libavformat/swfenc.c | 12 +- libavformat/takdec.c | 13 +- libavformat/tcp.c | 53 +- libavformat/tedcaptionsdec.c | 13 +- libavformat/tee.c | 10 +- libavformat/teeproto.c | 25 +- libavformat/tests/fifo_muxer.c | 127 +- libavformat/tests/imf.c | 74 +- libavformat/tests/movenc.c | 58 +- libavformat/thp.c | 7 +- libavformat/tiertexseq.c | 9 +- libavformat/tls.c | 26 +- libavformat/tls_schannel.c | 2 +- libavformat/tmv.c | 9 +- libavformat/tta.c | 9 +- libavformat/ttaenc.c | 11 +- libavformat/ttmlenc.c | 63 +- libavformat/tty.c | 26 +- libavformat/txd.c | 7 +- libavformat/ty.c | 13 +- libavformat/udp.c | 6 +- libavformat/uncodedframecrcenc.c | 1 + libavformat/unix.c | 8 +- libavformat/url.c | 6 +- libavformat/url.h | 26 +- libavformat/usmdec.c | 427 ++ libavformat/utils.c | 1 + libavformat/vag.c | 9 +- libavformat/vapoursynth.c | 18 +- libavformat/vc1test.c | 12 +- libavformat/vc1testenc.c | 7 +- libavformat/version.h | 2 +- libavformat/version_major.h | 11 +- libavformat/vividas.c | 19 +- libavformat/vivo.c | 9 +- libavformat/vocdec.c | 9 +- libavformat/vpcc.c | 16 +- libavformat/vpk.c | 8 +- libavformat/vplayerdec.c | 11 +- libavformat/vqf.c | 11 +- libavformat/vvc.c | 980 ++++ libavformat/vvc.h | 99 + libavformat/vvcdec.c | 77 + libavformat/wady.c | 16 +- libavformat/wavarc.c | 15 +- libavformat/wavdec.c | 59 +- libavformat/wavenc.c | 39 +- libavformat/wc3movie.c | 9 +- libavformat/webmdashenc.c | 1 + libavformat/webpenc.c | 146 +- libavformat/webvttdec.c | 29 +- libavformat/webvttenc.c | 10 +- libavformat/westwood_aud.c | 7 +- libavformat/westwood_audenc.c | 17 +- libavformat/westwood_vqa.c | 8 +- libavformat/wsddec.c | 15 +- libavformat/wtvdec.c | 60 +- libavformat/wvdec.c | 9 +- libavformat/wvedec.c | 9 +- libavformat/wvenc.c | 15 +- libavformat/xa.c | 7 +- libavformat/xmd.c | 10 +- libavformat/xmv.c | 13 +- libavformat/xvag.c | 9 +- libavformat/xwma.c | 7 +- libavformat/yop.c | 10 +- libavformat/yuv4mpegdec.c | 9 +- libavformat/yuv4mpegenc.c | 17 +- libavutil/Makefile | 13 +- libavutil/aarch64/asm.S | 23 + libavutil/aarch64/cpu.c | 119 +- libavutil/aarch64/cpu.h | 2 + libavutil/aarch64/float_dsp_neon.S | 200 +- libavutil/aarch64/tx_float_neon.S | 132 +- libavutil/arm/float_dsp_init_vfp.c | 2 +- libavutil/audio_fifo.c | 9 +- libavutil/audio_fifo.h | 9 +- libavutil/avassert.h | 3 + libavutil/avstring.c | 3 +- libavutil/avstring.h | 3 +- libavutil/avutil.h | 19 +- libavutil/base64.c | 6 +- libavutil/bprint.c | 7 +- libavutil/bprint.h | 15 +- libavutil/buffer.c | 10 +- libavutil/channel_layout.c | 562 +- libavutil/channel_layout.h | 266 +- libavutil/common.h | 21 +- libavutil/cpu.c | 13 +- libavutil/cpu.h | 3 + libavutil/dict.c | 14 +- libavutil/{x86 => }/emms.h | 14 +- libavutil/encryption_info.c | 2 + libavutil/eval.c | 46 +- libavutil/eval.h | 2 +- libavutil/executor.c | 201 + libavutil/executor.h | 67 + libavutil/fifo.c | 222 +- libavutil/fifo.h | 206 +- libavutil/file.c | 7 +- libavutil/file.h | 17 - libavutil/file_open.c | 7 - libavutil/film_grain_params.c | 67 +- libavutil/film_grain_params.h | 68 +- libavutil/fixed_dsp.c | 2 +- libavutil/fixed_dsp.h | 3 +- libavutil/float_dsp.c | 2 +- libavutil/float_dsp.h | 4 +- libavutil/frame.c | 514 +- libavutil/frame.h | 255 +- libavutil/hash.c | 74 +- libavutil/hdr_dynamic_metadata.c | 350 ++ libavutil/hdr_dynamic_metadata.h | 33 + libavutil/hdr_dynamic_vivid_metadata.h | 63 +- libavutil/hmac.c | 1 + libavutil/hwcontext.c | 300 +- libavutil/hwcontext.h | 26 +- libavutil/hwcontext_cuda.c | 86 +- libavutil/hwcontext_cuda.h | 5 + libavutil/hwcontext_d3d11va.c | 142 +- libavutil/hwcontext_d3d12va.c | 697 +++ libavutil/hwcontext_d3d12va.h | 134 + libavutil/hwcontext_d3d12va_internal.h | 59 + libavutil/hwcontext_dxva2.c | 44 +- libavutil/hwcontext_internal.h | 31 +- libavutil/hwcontext_opencl.c | 190 +- libavutil/hwcontext_qsv.c | 200 +- libavutil/hwcontext_vaapi.c | 167 +- libavutil/hwcontext_vdpau.c | 47 +- libavutil/hwcontext_videotoolbox.c | 46 +- libavutil/hwcontext_videotoolbox.h | 7 +- libavutil/hwcontext_vulkan.c | 2524 ++++----- libavutil/hwcontext_vulkan.h | 128 +- libavutil/iamf.c | 563 ++ libavutil/iamf.h | 680 +++ libavutil/imgutils.c | 129 +- libavutil/imgutils.h | 56 +- libavutil/internal.h | 9 - libavutil/intreadwrite.h | 8 +- libavutil/log.c | 11 +- libavutil/loongarch/cpu.c | 24 +- .../loongarch/timer.h | 34 +- libavutil/macos_kperf.c | 2 +- libavutil/mathematics.c | 104 + libavutil/mathematics.h | 55 + libavutil/md5.c | 2 + libavutil/mem.c | 2 +- libavutil/mem.h | 4 +- libavutil/mem_internal.h | 35 +- libavutil/mips/float_dsp_mips.c | 2 +- libavutil/mips/generic_macros_msa.h | 6 +- libavutil/murmur3.c | 1 + libavutil/opt.c | 1050 ++-- libavutil/opt.h | 601 ++- libavutil/pixdesc.c | 106 +- libavutil/pixdesc.h | 5 + libavutil/pixfmt.h | 32 +- libavutil/ppc/cpu.c | 6 +- libavutil/random_seed.c | 69 +- libavutil/random_seed.h | 14 + libavutil/rational.c | 2 +- libavutil/rational.h | 4 + libavutil/riscv/asm.S | 5 + libavutil/riscv/bswap.h | 52 +- libavutil/riscv/bswap_rvb.S | 65 + libavutil/riscv/cpu.c | 7 +- libavutil/riscv/fixed_dsp_init.c | 28 +- libavutil/riscv/fixed_dsp_rvv.S | 179 +- libavutil/riscv/float_dsp_init.c | 30 +- libavutil/riscv/float_dsp_rvv.S | 94 +- libavutil/riscv/intmath.h | 30 +- libavutil/riscv/timer.h | 13 +- libavutil/samplefmt.c | 4 +- libavutil/samplefmt.h | 4 +- libavutil/sfc64.h | 84 + libavutil/slicethread.c | 30 +- libavutil/spherical.c | 1 + libavutil/tests/base64.c | 10 + libavutil/tests/channel_layout.c | 498 +- libavutil/tests/cpu.c | 20 +- libavutil/tests/dict.c | 9 +- libavutil/tests/hwdevice.c | 4 +- libavutil/tests/imgutils.c | 121 +- libavutil/tests/lzo.c | 2 + libavutil/tests/murmur3.c | 3 + libavutil/tests/opt.c | 62 +- libavutil/tests/pixdesc.c | 1 + libavutil/tests/pixelutils.c | 8 - libavutil/tests/side_data_array.c | 103 + libavutil/thread.h | 39 +- libavutil/threadmessage.c | 3 + libavutil/timecode.c | 5 +- libavutil/timecode.h | 14 +- libavutil/timer.h | 6 +- libavutil/timestamp.c | 36 + libavutil/timestamp.h | 17 +- libavutil/tomi/intreadwrite.h | 150 - libavutil/tx.c | 49 +- libavutil/tx.h | 34 + libavutil/tx_priv.h | 1 + libavutil/tx_template.c | 284 +- libavutil/version.c | 17 - libavutil/version.h | 19 +- libavutil/video_enc_params.h | 4 +- libavutil/video_hint.c | 81 + libavutil/video_hint.h | 107 + libavutil/vulkan.c | 2086 +++++--- libavutil/vulkan.h | 508 +- libavutil/vulkan_functions.h | 51 + libavutil/vulkan_loader.h | 50 +- libavutil/wchar_filename.h | 5 +- libavutil/x86/fixed_dsp_init.c | 4 +- libavutil/x86/float_dsp_init.c | 5 +- libavutil/x86/intreadwrite.h | 36 - libavutil/x86/pixelutils.asm | 1 + libavutil/x86/tx_float.asm | 8 +- libavutil/x86/tx_float_init.c | 18 +- libavutil/x86/x86inc.asm | 682 ++- libavutil/x86/x86util.asm | 4 - libpostproc/version_major.h | 2 +- libswresample/aarch64/resample.S | 80 +- libswresample/options.c | 85 +- libswresample/rematrix.c | 48 +- libswresample/resample.c | 110 +- libswresample/swresample.c | 144 +- libswresample/swresample.h | 67 +- libswresample/swresample_frame.c | 47 +- libswresample/swresample_internal.h | 7 - libswresample/version.h | 2 +- libswresample/version_major.h | 2 +- libswscale/aarch64/hscale.S | 2250 ++++---- libswscale/aarch64/output.S | 330 +- libswscale/aarch64/yuv2rgb_neon.S | 220 +- libswscale/input.c | 15 + libswscale/loongarch/Makefile | 6 + libswscale/loongarch/input.S | 285 + libswscale/loongarch/output.S | 138 + libswscale/loongarch/output_lasx.c | 4 +- libswscale/loongarch/output_lsx.c | 1828 +++++++ libswscale/loongarch/swscale.S | 1868 +++++++ libswscale/loongarch/swscale_init_loongarch.c | 114 +- libswscale/loongarch/swscale_loongarch.h | 61 +- libswscale/loongarch/swscale_lsx.c | 57 + libswscale/loongarch/yuv2rgb_lasx.c | 4 +- libswscale/loongarch/yuv2rgb_lsx.c | 361 ++ libswscale/options.c | 60 +- libswscale/output.c | 106 +- libswscale/ppc/swscale_vsx.c | 60 - libswscale/riscv/Makefile | 1 + libswscale/riscv/rgb2rgb.c | 21 +- .../riscv/rgb2rgb_rvb.S | 20 +- libswscale/riscv/rgb2rgb_rvv.S | 173 +- libswscale/swscale.c | 8 +- libswscale/swscale_unscaled.c | 3 + libswscale/tests/swscale.c | 107 +- libswscale/utils.c | 26 +- libswscale/version_major.h | 2 +- libswscale/x86/rgb2rgb_template.c | 5 +- libswscale/x86/scale.asm | 5 - libswscale/x86/swscale.c | 14 +- libswscale/yuv2rgb.c | 4 +- tests/Makefile | 31 +- tests/checkasm/Makefile | 17 +- tests/checkasm/aacencdsp.c | 70 + tests/checkasm/ac3dsp.c | 70 + tests/checkasm/af_afir.c | 141 +- tests/checkasm/checkasm.c | 181 +- tests/checkasm/checkasm.h | 101 +- tests/checkasm/exrdsp.c | 2 +- tests/checkasm/fixed_dsp.c | 2 +- tests/checkasm/flacdsp.c | 34 +- tests/checkasm/float_dsp.c | 4 +- tests/checkasm/h264chroma.c | 85 + tests/checkasm/h264dsp.c | 2 - tests/checkasm/h264pred.c | 2 +- tests/checkasm/hevc_add_res.c | 6 +- tests/checkasm/hevc_deblock.c | 286 + tests/checkasm/hevc_idct.c | 41 +- tests/checkasm/hevc_pel.c | 245 +- tests/checkasm/hevc_sao.c | 24 +- tests/checkasm/huffyuvdsp.c | 38 +- tests/checkasm/idctdsp.c | 1 + tests/checkasm/llauddsp.c | 115 + tests/checkasm/llviddsp.c | 32 +- tests/checkasm/llviddspenc.c | 4 +- tests/checkasm/motion.c | 10 +- tests/checkasm/pixblockdsp.c | 2 +- tests/checkasm/rv34dsp.c | 89 + tests/checkasm/sbrdsp.c | 9 +- tests/checkasm/svq1enc.c | 68 + tests/checkasm/sw_scale.c | 12 +- tests/checkasm/takdsp.c | 154 + tests/checkasm/utvideodsp.c | 1 - tests/checkasm/vf_bwdif.c | 255 + tests/checkasm/vorbisdsp.c | 4 +- tests/checkasm/vp8dsp.c | 1 - tests/checkasm/vp9dsp.c | 1 + tests/checkasm/vvc_mc.c | 332 ++ tests/fate-run.sh | 105 +- tests/fate.sh | 15 +- tests/fate/aac.mak | 10 +- tests/fate/ac3.mak | 10 +- tests/fate/acodec.mak | 8 +- tests/fate/adpcm.mak | 44 +- tests/fate/alac.mak | 6 +- tests/fate/amrnb.mak | 4 +- tests/fate/amrwb.mak | 2 +- tests/fate/atrac.mak | 18 +- tests/fate/audio.mak | 28 +- tests/fate/cbs.mak | 68 +- tests/fate/checkasm.mak | 12 +- tests/fate/dca.mak | 12 +- tests/fate/demux.mak | 10 +- tests/fate/dnn.mak | 45 - tests/fate/dvvideo.mak | 2 +- tests/fate/enc_external.mak | 16 + tests/fate/ffmpeg.mak | 129 +- tests/fate/ffprobe.mak | 25 +- tests/fate/fft.mak | 83 - tests/fate/filter-audio.mak | 87 +- tests/fate/filter-video.mak | 79 +- tests/fate/fits.mak | 6 +- tests/fate/flvenc.mak | 15 +- tests/fate/gapless.mak | 78 +- tests/fate/gif.mak | 2 +- tests/fate/h264.mak | 25 +- tests/fate/hevc.mak | 4 +- tests/fate/hlsenc.mak | 18 +- tests/fate/iamf.mak | 44 + tests/fate/image.mak | 25 +- tests/fate/imf.mak | 4 +- tests/fate/jpeg2000.mak | 66 + tests/fate/jxl.mak | 27 + tests/fate/lavf-audio.mak | 1 + tests/fate/lavf-container.mak | 42 +- tests/fate/lavf-video.mak | 2 +- tests/fate/libavcodec.mak | 5 + tests/fate/libavutil.mak | 4 + tests/fate/libswresample.mak | 229 +- tests/fate/lossless-audio.mak | 16 +- tests/fate/lossless-video.mak | 6 +- tests/fate/matroska.mak | 55 +- tests/fate/monkeysaudio.mak | 5 +- tests/fate/mov.mak | 137 +- tests/fate/mp3.mak | 2 +- tests/fate/mpc.mak | 2 +- tests/fate/mpeg4.mak | 2 +- tests/fate/mpegps.mak | 2 +- tests/fate/mxf.mak | 2 +- tests/fate/pcm.mak | 10 +- tests/fate/qoa.mak | 12 + tests/fate/real.mak | 6 +- tests/fate/screen.mak | 3 +- tests/fate/source-check.sh | 1 + tests/fate/subtitles.mak | 8 +- tests/fate/truehd.mak | 2 +- tests/fate/vcodec.mak | 27 +- tests/fate/video.mak | 17 +- tests/fate/voice.mak | 4 +- tests/fate/vorbis.mak | 8 +- tests/fate/vpx.mak | 6 +- tests/fate/vqf.mak | 2 +- tests/fate/vvc.mak | 50 + tests/fate/wma.mak | 25 +- tests/filtergraphs/iamf_5_1_4 | 6 + tests/filtergraphs/iamf_7_1_4 | 7 + tests/filtergraphs/iamf_ambisonic_1 | 4 + tests/filtergraphs/mov-mp4-pcm | 5 + tests/filtergraphs/overlay_yuv444p10 | 5 + tests/maps/fsync-down | 7 + tests/maps/fsync-up | 57 + tests/ref/acodec/adpcm-ima_wav | 2 +- tests/ref/acodec/adpcm-ima_wav-trellis | 2 +- tests/ref/acodec/adpcm-ms | 2 +- tests/ref/acodec/adpcm-ms-trellis | 2 +- tests/ref/acodec/adpcm-swf | 2 +- tests/ref/acodec/adpcm-swf-trellis | 2 +- tests/ref/acodec/adpcm-swf-wav | 2 +- tests/ref/acodec/adpcm-yamaha | 2 +- tests/ref/acodec/adpcm-yamaha-trellis | 2 +- tests/ref/acodec/pcm-f32le | 4 +- tests/ref/acodec/pcm-f64le | 4 +- tests/ref/acodec/pcm-s16be | 2 +- tests/ref/acodec/pcm-s16be_planar | 4 +- tests/ref/acodec/pcm-s16le_planar | 4 +- tests/ref/acodec/pcm-s24be | 2 +- tests/ref/acodec/pcm-s24le_planar | 4 +- tests/ref/acodec/pcm-s32be | 2 +- tests/ref/acodec/pcm-s32le_planar | 4 +- tests/ref/acodec/pcm-s8_planar | 4 +- tests/ref/acodec/pcm-u16be | 4 +- tests/ref/acodec/pcm-u16le | 4 +- tests/ref/acodec/pcm-u24be | 4 +- tests/ref/acodec/pcm-u24le | 4 +- tests/ref/acodec/pcm-u32be | 4 +- tests/ref/acodec/pcm-u32le | 4 +- tests/ref/acodec/s302m | 8 +- tests/ref/fate/aac-autobsf-adtstoasc | 4 +- tests/ref/fate/adpcm-ima-smjpeg | 658 +-- tests/ref/fate/apng-osample | 12 +- tests/ref/fate/atrac-aea-remux | 94 + tests/ref/fate/atrac-matroska-remux | 94 + tests/ref/fate/autorotate | 8 +- tests/ref/fate/av1-annexb-demux | 2 +- tests/ref/fate/cavs-demux | 62 + tests/ref/fate/cbs-h264-discard-bidir | 1 + tests/ref/fate/cbs-h264-discard-nonintra | 1 + tests/ref/fate/cbs-h264-discard-nonkey | 1 + tests/ref/fate/cbs-h264-discard-nonref | 1 + tests/ref/fate/cbs-hevc-discard-bidir | 1 + tests/ref/fate/cbs-hevc-discard-nonintra | 1 + tests/ref/fate/cbs-hevc-discard-nonkey | 1 + tests/ref/fate/cbs-hevc-discard-nonref | 1 + tests/ref/fate/cbs-vp9-vp90-2-03-deltaq | 2 +- tests/ref/fate/cbs-vp9-vp90-2-06-bilinear | 2 +- tests/ref/fate/cbs-vp9-vp90-2-09-lf_deltas | 2 +- .../cbs-vp9-vp90-2-10-show-existing-frame | 2 +- .../cbs-vp9-vp90-2-10-show-existing-frame2 | 2 +- .../fate/cbs-vp9-vp90-2-segmentation-aq-akiyo | 2 +- .../fate/cbs-vp9-vp90-2-segmentation-sf-akiyo | 2 +- .../ref/fate/cbs-vp9-vp90-2-tiling-pedestrian | 2 +- tests/ref/fate/cbs-vp9-vp91-2-04-yuv440 | 2 +- tests/ref/fate/cbs-vp9-vp91-2-04-yuv444 | 2 +- tests/ref/fate/cbs-vp9-vp92-2-20-10bit-yuv420 | 2 +- tests/ref/fate/cbs-vp9-vp93-2-20-10bit-yuv422 | 2 +- tests/ref/fate/cbs-vp9-vp93-2-20-12bit-yuv444 | 2 +- tests/ref/fate/cbs-vvc-APSALF_A_2 | 1 + tests/ref/fate/cbs-vvc-APSLMCS_D_1 | 1 + tests/ref/fate/cbs-vvc-APSMULT_A_4 | 1 + tests/ref/fate/cbs-vvc-AUD_A_3 | 1 + tests/ref/fate/cbs-vvc-BOUNDARY_A_3 | 1 + tests/ref/fate/cbs-vvc-BUMP_A_2 | 1 + tests/ref/fate/cbs-vvc-CROP_B_4 | 1 + tests/ref/fate/cbs-vvc-CodingToolsSets_A_2 | 1 + tests/ref/fate/cbs-vvc-DCI_A_3 | 1 + tests/ref/fate/cbs-vvc-HRD_A_3 | 1 + tests/ref/fate/cbs-vvc-OPI_B_3 | 1 + tests/ref/fate/cbs-vvc-PHSH_B_1 | 1 + tests/ref/fate/cbs-vvc-POC_A_1 | 1 + tests/ref/fate/cbs-vvc-PPS_B_1 | 1 + tests/ref/fate/cbs-vvc-RAP_A_1 | 1 + tests/ref/fate/cbs-vvc-SAO_A_3 | 1 + tests/ref/fate/cbs-vvc-SCALING_A_1 | 1 + tests/ref/fate/cbs-vvc-SLICES_A_3 | 1 + tests/ref/fate/cbs-vvc-SPS_B_1 | 1 + tests/ref/fate/cbs-vvc-STILL_B_1 | 1 + tests/ref/fate/cbs-vvc-SUBPIC_A_3 | 1 + tests/ref/fate/cbs-vvc-TILE_A_2 | 1 + tests/ref/fate/cbs-vvc-VPS_A_3 | 1 + tests/ref/fate/cbs-vvc-WPP_A_3 | 1 + tests/ref/fate/cbs-vvc-WP_A_3 | 1 + tests/ref/fate/cbs-vvc-WRAP_A_4 | 1 + tests/ref/fate/channel_layout | 239 +- .../ref/fate/concat-demuxer-extended-lavf-mxf | 2 +- .../fate/concat-demuxer-extended-lavf-mxf_d10 | 2 +- .../ref/fate/concat-demuxer-simple1-lavf-mxf | 4 +- .../fate/concat-demuxer-simple1-lavf-mxf_d10 | 4 +- tests/ref/fate/concat-demuxer-simple2-lavf-ts | 68 +- tests/ref/fate/copy-trac3074 | 2 +- tests/ref/fate/cover-art-aiff-id3v2-remux | 34 +- tests/ref/fate/cover-art-mp3-id3v2-remux | 22 +- tests/ref/fate/d-cinema-demux | 10 +- tests/ref/fate/dcinema-encode | 102 +- tests/ref/fate/dds-dxt2 | 2 +- tests/ref/fate/dds-dxt4 | 2 +- tests/ref/fate/dirac | 28 + tests/ref/fate/dirac-low-delay | 28 + tests/ref/fate/dxv3-ycg6 | 6 + tests/ref/fate/dxv3-yg10 | 6 + tests/ref/fate/dxv3enc-dxt1 | 6 + tests/ref/fate/enhanced-flv-av1 | 70 + tests/ref/fate/enhanced-flv-hevc | 258 + tests/ref/fate/enhanced-flv-vp9 | 18 + tests/ref/fate/eval | 2 +- tests/ref/fate/exif-image-embedded | 50 +- tests/ref/fate/exif-image-jpg | 8 +- tests/ref/fate/exif-image-tiff | 8 +- tests/ref/fate/exif-image-webp | 8 +- tests/ref/fate/ffmpeg-bsf-input | 18 + tests/ref/fate/ffmpeg-error-rate-fail | 0 tests/ref/fate/ffmpeg-error-rate-pass | 0 tests/ref/fate/ffmpeg-filter-in-eof | 55 + tests/ref/fate/ffmpeg-filter_complex_audio | 2 +- .../fate/ffmpeg-fix_sub_duration_heartbeat | 35 +- tests/ref/fate/ffmpeg-input-r | 12 + tests/ref/fate/ffmpeg-loopback-decoding | 57 + ...mpeg-streamloop => ffmpeg-streamloop-copy} | 0 tests/ref/fate/ffmpeg-streamloop-transcode-av | 151 + tests/ref/fate/ffprobe_compact | 36 +- tests/ref/fate/ffprobe_csv | 36 +- tests/ref/fate/ffprobe_default | 80 +- tests/ref/fate/ffprobe_flat | 80 +- tests/ref/fate/ffprobe_ini | 80 +- tests/ref/fate/ffprobe_json | 80 +- tests/ref/fate/ffprobe_xml | 62 +- tests/ref/fate/ffprobe_xsd | 62 +- tests/ref/fate/film-cvid | 218 +- tests/ref/fate/filter-acrossfade | 623 +-- tests/ref/fate/filter-adelay | 324 +- tests/ref/fate/filter-aecho | 324 +- tests/ref/fate/filter-aemphasis-50fm | 324 +- tests/ref/fate/filter-aemphasis-75kf | 324 +- tests/ref/fate/filter-afade-esin | 324 +- tests/ref/fate/filter-afade-exp | 324 +- tests/ref/fate/filter-afade-hsin | 324 +- tests/ref/fate/filter-afade-iqsin | 324 +- tests/ref/fate/filter-afade-log | 324 +- tests/ref/fate/filter-afade-qsin | 324 +- tests/ref/fate/filter-agate | 324 +- tests/ref/fate/filter-alimiter | 324 +- tests/ref/fate/filter-amerge | 195 +- tests/ref/fate/filter-anequalizer | 324 +- tests/ref/fate/filter-apad | 324 +- .../ref/fate/filter-asegment-samples-absolute | 80 + .../ref/fate/filter-asegment-samples-relative | 80 + .../fate/filter-asegment-timestamps-absolute | 81 + .../fate/filter-asegment-timestamps-relative | 81 + tests/ref/fate/filter-asetrate | 40 +- tests/ref/fate/filter-atrim-mixed | 4 +- tests/ref/fate/filter-atrim-time | 5 +- tests/ref/fate/filter-bwdif-mode0 | 35 + tests/ref/fate/filter-bwdif-mode1 | 64 + tests/ref/fate/filter-bwdif10 | 35 + tests/ref/fate/filter-channelsplit | 2 + tests/ref/fate/filter-compand | 40 +- tests/ref/fate/filter-concat-vfr | 66 +- tests/ref/fate/filter-crystalizer | 324 +- tests/ref/fate/filter-dcshift | 40 +- tests/ref/fate/filter-earwax | 40 +- tests/ref/fate/filter-extrastereo | 40 +- tests/ref/fate/filter-formats | 3 - tests/ref/fate/filter-fsync-down | 12 + tests/ref/fate/filter-fsync-up | 62 + tests/ref/fate/filter-median | 1 + tests/ref/fate/filter-metadata-cropdetect | 1 - tests/ref/fate/filter-metadata-cropdetect1 | 2 - tests/ref/fate/filter-metadata-cropdetect2 | 3 - tests/ref/fate/filter-metadata-scdet | 12 +- tests/ref/fate/filter-metadata-silencedetect | 2 +- tests/ref/fate/filter-overlay-dvdsub-2397 | 4 +- tests/ref/fate/filter-overlay_yuv444p10 | 8 + tests/ref/fate/filter-pan-downmix1 | 40 +- tests/ref/fate/filter-pan-downmix2 | 40 +- tests/ref/fate/filter-pan-mono1 | 40 +- tests/ref/fate/filter-pan-mono2 | 40 +- tests/ref/fate/filter-pan-stereo1 | 40 +- tests/ref/fate/filter-pan-stereo2 | 40 +- tests/ref/fate/filter-pan-stereo3 | 40 +- tests/ref/fate/filter-pan-stereo4 | 40 +- tests/ref/fate/filter-pan-upmix1 | 40 +- tests/ref/fate/filter-pan-upmix2 | 40 +- tests/ref/fate/filter-pixdesc-gbrap14be | 1 + tests/ref/fate/filter-pixdesc-gbrap14le | 1 + tests/ref/fate/filter-pixdesc-p212be | 1 + tests/ref/fate/filter-pixdesc-p212le | 1 + tests/ref/fate/filter-pixdesc-p412be | 1 + tests/ref/fate/filter-pixdesc-p412le | 1 + tests/ref/fate/filter-pixelize-avg | 1 + tests/ref/fate/filter-pixelize-max | 1 + tests/ref/fate/filter-pixelize-min | 1 + tests/ref/fate/filter-pixfmts-copy | 6 + tests/ref/fate/filter-pixfmts-crop | 6 + tests/ref/fate/filter-pixfmts-field | 6 + tests/ref/fate/filter-pixfmts-fieldorder | 6 + tests/ref/fate/filter-pixfmts-hflip | 6 + tests/ref/fate/filter-pixfmts-il | 6 + tests/ref/fate/filter-pixfmts-null | 6 + tests/ref/fate/filter-pixfmts-pad | 20 +- tests/ref/fate/filter-pixfmts-scale | 6 + tests/ref/fate/filter-pixfmts-transpose | 4 + tests/ref/fate/filter-pixfmts-vflip | 6 + tests/ref/fate/filter-setpts | 89 +- tests/ref/fate/filter-stereotools | 40 +- tests/ref/fate/filter-tiltandshift | 55 + tests/ref/fate/filter-tpad-add-duration | 11 + tests/ref/fate/filter-untile-yuv422p | 13 + tests/ref/fate/filter-yadif10 | 60 +- tests/ref/fate/filter-yadif16 | 60 +- .../{fitsdec-gbrap16le => fitsdec-gbrap16be} | 4 +- .../fate/{fitsdec-gbrp16 => fitsdec-gbrp16be} | 4 +- tests/ref/fate/flcl1905 | 454 +- tests/ref/fate/flv-demux | 12 +- tests/ref/fate/force_key_frames | 8 +- tests/ref/fate/force_key_frames-source | 397 ++ tests/ref/fate/force_key_frames-source-drop | 22 + tests/ref/fate/force_key_frames-source-dup | 617 +++ tests/ref/fate/g722-encode | 106 +- tests/ref/fate/g726-encode-2bit | 97 +- tests/ref/fate/g726-encode-3bit | 99 +- tests/ref/fate/g726-encode-4bit | 100 +- tests/ref/fate/g726-encode-5bit | 102 +- tests/ref/fate/gapless-mp3-side-data | 13 +- tests/ref/fate/gaplessenc-itunes-to-ipod-aac | 36 +- tests/ref/fate/gaplessenc-pcm-to-mov-aac | 36 +- tests/ref/fate/gaplessinfo-itunes1 | 36 +- tests/ref/fate/gaplessinfo-itunes2 | 36 +- tests/ref/fate/gif-demux | 2 +- tests/ref/fate/gif-disposal-restore | 2 +- tests/ref/fate/gif-gray | 72 +- tests/ref/fate/h264-3386 | 94 +- tests/ref/fate/h264-afd | 288 + .../fate/h264-bsf-mp4toannexb-new-extradata | 9 + .../fate/h264-conformance-cabac_mot_fld0_full | 60 +- .../h264-conformance-cabac_mot_picaff0_full | 60 +- .../ref/fate/h264-conformance-cabref3_sand_d | 100 +- tests/ref/fate/h264-conformance-cafi1_sva_c | 66 +- .../ref/fate/h264-conformance-capa1_toshiba_b | 180 +- .../ref/fate/h264-conformance-capama3_sand_f | 100 +- .../h264-conformance-cavlc_mot_fld0_full_b | 60 +- .../h264-conformance-cavlc_mot_picaff0_full_b | 60 +- tests/ref/fate/h264-conformance-cvfi1_sony_d | 34 +- tests/ref/fate/h264-conformance-cvfi1_sva_c | 14 +- tests/ref/fate/h264-conformance-cvfi2_sony_h | 34 +- tests/ref/fate/h264-conformance-cvfi2_sva_c | 26 +- .../fate/h264-conformance-cvmapaqp3_sony_e | 14 +- .../fate/h264-conformance-cvmp_mot_fld_l30_b | 60 +- .../fate/h264-conformance-cvmp_mot_frm_l31_b | 60 +- .../ref/fate/h264-conformance-cvnlfi1_sony_c | 34 +- .../ref/fate/h264-conformance-cvnlfi2_sony_h | 34 +- .../ref/fate/h264-conformance-cvpa1_toshiba_b | 180 +- tests/ref/fate/h264-conformance-fi1_sony_e | 34 +- .../fate/h264-conformance-frext-bcrm_freh10 | 200 +- .../fate/h264-conformance-frext-brcm_freh11 | 200 +- .../fate/h264-conformance-frext-brcm_freh4 | 200 +- tests/ref/fate/h264-conformance-frext-freh6 | 200 +- tests/ref/fate/h264-conformance-frext-freh7_b | 200 +- .../fate/h264-conformance-frext-hcaff1_hhi_b | 20 +- .../fate/h264-conformance-frext-hpcafl_bcrm_c | 600 +-- .../h264-conformance-frext-hpcaflnl_bcrm_c | 600 +-- .../h264-conformance-frext-hpcamapalq_bcrm_b | 316 +- .../fate/h264-conformance-frext-hpcvfl_bcrm_a | 600 +-- .../h264-conformance-frext-hpcvflnl_bcrm_a | 600 +-- .../ref/fate/h264-conformance-mr3_tandberg_b | 582 +- tests/ref/fate/h264-conformance-mr6_bt_b | 120 +- tests/ref/fate/h264-conformance-mr7_bt_b | 100 +- tests/ref/fate/h264-conformance-mr8_bt_b | 116 +- tests/ref/fate/h264-conformance-mr9_bt_b | 96 +- .../fate/h264-conformance-sharp_mp_field_1_b | 30 +- .../fate/h264-conformance-sharp_mp_field_2_b | 30 +- .../fate/h264-conformance-sharp_mp_field_3_b | 30 +- .../fate/h264-conformance-sharp_mp_paff_1r2 | 26 +- .../fate/h264-conformance-sharp_mp_paff_2r | 26 +- tests/ref/fate/h264-dts_5frames | 40 +- tests/ref/fate/h264-intra-refresh-recovery | 20 +- tests/ref/fate/h264_mp4toannexb_ticket5927 | 8 +- tests/ref/fate/h264_mp4toannexb_ticket5927_2 | 8 +- tests/ref/fate/h264_redundant_pps-mov | 2 +- ...hapqa-extract-nosnappy-to-hapalphaonly-mov | 1 + .../fate/hapqa-extract-nosnappy-to-hapq-mov | 1 + .../hevc-conformance-BUMPING_A_ericsson_1 | 90 +- .../fate/hevc-conformance-CIP_A_Panasonic_3 | 4 +- .../fate/hevc-conformance-CIP_C_Panasonic_2 | 4 +- .../fate/hevc-conformance-DELTAQP_A_BRCM_4 | 2 +- .../fate/hevc-conformance-NUT_A_ericsson_5 | 24 +- .../hevc-conformance-NoOutPrior_A_Qualcomm_1 | 36 +- .../hevc-conformance-NoOutPrior_B_Qualcomm_1 | 50 +- .../fate/hevc-conformance-OPFLAG_B_Qualcomm_1 | 118 +- .../fate/hevc-conformance-OPFLAG_C_Qualcomm_1 | 162 +- .../ref/fate/hevc-conformance-RAP_A_docomo_4 | 172 +- .../ref/fate/hevc-conformance-RAP_B_Bossen_1 | 26 +- .../ref/fate/hevc-conformance-SDH_A_Orange_3 | 4 +- tests/ref/fate/hevc-dv-rpu | 98 +- tests/ref/fate/hevc-hdr-vivid-metadata | 6 +- tests/ref/fate/hevc-small422chroma | 2 +- tests/ref/fate/iamf-5_1_4 | 481 ++ tests/ref/fate/iamf-7_1_4 | 541 ++ tests/ref/fate/iamf-ambisonic_1 | 308 ++ tests/ref/fate/iamf-stereo | 168 + tests/ref/fate/idroq-video-encode | 2 +- tests/ref/fate/imf | 2 + tests/ref/fate/imgutils | 223 + tests/ref/fate/jpeg2000dec-ds0_ht_01_b11 | 6 + tests/ref/fate/jpeg2000dec-p0_01 | 6 + tests/ref/fate/jpeg2000dec-p0_02 | 6 + tests/ref/fate/jpeg2000dec-p0_03 | 6 + tests/ref/fate/jpeg2000dec-p0_04 | 6 + tests/ref/fate/jpeg2000dec-p0_05 | 6 + tests/ref/fate/jpeg2000dec-p0_07 | 6 + tests/ref/fate/jpeg2000dec-p0_08 | 6 + tests/ref/fate/jpeg2000dec-p0_09 | 6 + tests/ref/fate/jpeg2000dec-p0_11 | 6 + tests/ref/fate/jpeg2000dec-p0_12 | 6 + tests/ref/fate/jpeg2000dec-p0_14 | 6 + tests/ref/fate/jpeg2000dec-p0_15 | 6 + tests/ref/fate/jpeg2000dec-p0_16 | 6 + tests/ref/fate/jpg-icc | 8 +- tests/ref/fate/jpg-rgb-1 | 6 + tests/ref/fate/jpg-rgb-2 | 6 + tests/ref/fate/jpg-rgb-221 | 6 + tests/ref/fate/jpg-rgb-3 | 6 + tests/ref/fate/jpg-rgb-4 | 6 + tests/ref/fate/jpg-rgb-5 | 6 + tests/ref/fate/jpg-rgb-baseline | 6 + tests/ref/fate/jpg-rgb-progressive | 6 + tests/ref/fate/jxl-anim-demux-belgium | 6 + tests/ref/fate/jxl-anim-demux-icos4d | 6 + tests/ref/fate/jxl-anim-demux-lenna256 | 6 + tests/ref/fate/jxl-anim-demux-newton | 6 + tests/ref/fate/jxl-multiframe-permuted-toc | 11 + tests/ref/fate/jxl-small-ext-box | 9 + tests/ref/fate/libsvtav1-hdr10 | 14 + tests/ref/fate/libx264-hdr10 | 15 + tests/ref/fate/libx264-simple | 85 + tests/ref/fate/lossless-monkeysaudio-legacy | 1 + tests/ref/fate/lossless-osq | 1 + tests/ref/fate/lossless-rka | 1 + tests/ref/fate/m4v | 84 +- tests/ref/fate/m4v-cfr | 1 + tests/ref/fate/matroska-alac-remux | 174 + tests/ref/fate/matroska-avoid-negative-ts | 6 +- tests/ref/fate/matroska-dovi-write-config7 | 6 +- tests/ref/fate/matroska-dovi-write-config8 | 15 +- tests/ref/fate/matroska-dvbsub-remux | 4 +- tests/ref/fate/matroska-encoding-delay | 16 +- tests/ref/fate/matroska-flac-extradata-update | 4 +- tests/ref/fate/matroska-h264-remux | 4 +- tests/ref/fate/matroska-hdr10-plus-remux | 63 + .../fate/matroska-mastering-display-metadata | 8 +- tests/ref/fate/matroska-move-cues-to-front | 66 +- tests/ref/fate/matroska-mpegts-remux | 6 +- tests/ref/fate/matroska-ms-mode | 4 +- .../fate/matroska-non-rotation-displaymatrix | 15 + tests/ref/fate/matroska-ogg-opus-remux | 10 +- tests/ref/fate/matroska-opus-remux | 10 +- tests/ref/fate/matroska-pgs-remux | 4 +- tests/ref/fate/matroska-pgs-remux-durations | 4 +- tests/ref/fate/matroska-qt-mode | 4 +- tests/ref/fate/matroska-side-data-pref-codec | 348 ++ tests/ref/fate/matroska-side-data-pref-packet | 348 ++ tests/ref/fate/matroska-spherical-mono-remux | 8 +- tests/ref/fate/matroska-stereo_mode | 195 + tests/ref/fate/matroska-vp8-alpha-remux | 7 +- tests/ref/fate/matroska-zero-length-block | 4 +- tests/ref/fate/mov-aac-2048-priming | 3 +- .../fate/mov-avif-demux-still-image-1-item | 10 +- .../mov-avif-demux-still-image-multiple-items | 10 +- tests/ref/fate/mov-channel-description | 4 +- .../fate/mov-heic-demux-still-image-1-item | 7 + .../ref/fate/mov-heic-demux-still-image-grid | 170 + .../ref/fate/mov-heic-demux-still-image-iovl | 102 + .../fate/mov-heic-demux-still-image-iovl-2 | 73 + .../mov-heic-demux-still-image-multiple-items | 14 + .../ref/fate/mov-mp4-disposition-mpegts-remux | 9 +- tests/ref/fate/mov-mp4-iamf-5_1_4 | 523 ++ tests/ref/fate/mov-mp4-iamf-7_1_4 | 583 ++ tests/ref/fate/mov-mp4-iamf-ambisonic_1 | 350 ++ tests/ref/fate/mov-mp4-iamf-stereo | 210 + tests/ref/fate/mov-mp4-pcm | 27 + tests/ref/fate/mov-mp4-pcm-float | 7 + tests/ref/fate/mov-mp4-ttml-dfxp | 12 +- tests/ref/fate/mov-mp4-ttml-stpp | 13 +- tests/ref/fate/mov-read-amve | 8 + tests/ref/fate/mov-write-amve | 33 + tests/ref/fate/mov-zombie | 197 +- tests/ref/fate/mpeg2-ticket6677 | 14 +- tests/ref/fate/mxf-d10-user-comments | 2 +- tests/ref/fate/mxf-probe-applehdr10 | 3 + tests/ref/fate/mxf-probe-d10 | 2 + tests/ref/fate/mxf-probe-dnxhd | 4 + tests/ref/fate/mxf-probe-dv25 | 5 +- tests/ref/fate/mxf-probe-j2k | 3 +- tests/ref/fate/mxf-remux-applehdr10 | 2 +- tests/ref/fate/oggopus-demux | 10 +- tests/ref/fate/opt | 35 +- tests/ref/fate/pcm_dvd-16-5.1-96000 | 8 +- tests/ref/fate/png-icc | 16 +- tests/ref/fate/png-icc-parse | 21 +- tests/ref/fate/png-side-data | 10 +- tests/ref/fate/qoa-152 | 13 + tests/ref/fate/qoa-278 | 135 + tests/ref/fate/qoa-303 | 35 + tests/ref/fate/quickdraw | 2 +- tests/ref/fate/rgb24-mkv | 4 +- tests/ref/fate/shortest | 101 +- tests/ref/fate/shortest-sub | 4 +- tests/ref/fate/side_data_array | 14 + tests/ref/fate/smacker-video | 2 +- tests/ref/fate/source | 2 + tests/ref/fate/sub-aqtitle | 94 +- tests/ref/fate/sub-ass-to-ass-transcode | 124 +- tests/ref/fate/sub-cc | 32 +- tests/ref/fate/sub-cc-realtime | 44 +- tests/ref/fate/sub-cc-scte20 | 34 +- tests/ref/fate/sub-charenc | 128 +- tests/ref/fate/sub-jacosub | 50 +- tests/ref/fate/sub-microdvd | 48 +- tests/ref/fate/sub-movtext | 34 +- tests/ref/fate/sub-mpl2 | 36 +- tests/ref/fate/sub-mpsub | 70 +- tests/ref/fate/sub-mpsub-frames | 32 +- tests/ref/fate/sub-pjs | 34 +- tests/ref/fate/sub-realtext | 38 +- tests/ref/fate/sub-sami | 46 +- tests/ref/fate/sub-sami2 | 186 +- tests/ref/fate/sub-srt | 102 +- tests/ref/fate/sub-srt-badsyntax | 48 +- tests/ref/fate/sub-ssa-to-ass-remux | 168 +- tests/ref/fate/sub-stl | 62 +- tests/ref/fate/sub-subviewer | 34 +- tests/ref/fate/sub-subviewer1 | 48 +- tests/ref/fate/sub-vplayer | 34 +- tests/ref/fate/sub-webvtt | 58 +- tests/ref/fate/sub-webvtt2 | 52 +- tests/ref/fate/sub2video | 8 +- tests/ref/fate/sub2video_basic | 182 +- tests/ref/fate/sub2video_time_limited | 8 +- tests/ref/fate/swr-async-firstpts | 24 + tests/ref/fate/sws-pixdesc-query | 39 + tests/ref/fate/ts-demux | 62 +- tests/ref/fate/ts-opus-demux | 1540 ++---- tests/ref/fate/ts-small-demux | 223 +- tests/ref/fate/ts-timed-id3-demux | 4 + tests/ref/fate/tscc2-mov | 2 +- tests/ref/fate/vc1_ilaced_twomv | 24 +- tests/ref/fate/vc1_sa00040 | 12 +- tests/ref/fate/vc1_sa00050 | 28 +- tests/ref/fate/vc1_sa10091 | 26 +- tests/ref/fate/vc1_sa10143 | 58 +- tests/ref/fate/vc1_sa20021 | 62 +- tests/ref/fate/vp9-superframe-bsf | 30 + tests/ref/fate/vvc-conformance-APSALF_A_2 | 13 + tests/ref/fate/vvc-conformance-APSLMCS_D_1 | 37 + tests/ref/fate/vvc-conformance-APSMULT_A_4 | 53 + tests/ref/fate/vvc-conformance-AUD_A_3 | 35 + tests/ref/fate/vvc-conformance-BUMP_A_2 | 45 + tests/ref/fate/vvc-conformance-CROP_B_4 | 105 + .../fate/vvc-conformance-CodingToolsSets_A_2 | 7 + tests/ref/fate/vvc-conformance-DCI_A_3 | 7 + tests/ref/fate/vvc-conformance-HRD_A_3 | 65 + tests/ref/fate/vvc-conformance-PHSH_B_1 | 11 + tests/ref/fate/vvc-conformance-POC_A_1 | 25 + tests/ref/fate/vvc-conformance-PPS_B_1 | 69 + tests/ref/fate/vvc-conformance-RAP_A_1 | 6 + tests/ref/fate/vvc-conformance-SAO_A_3 | 65 + tests/ref/fate/vvc-conformance-SCALING_A_1 | 69 + tests/ref/fate/vvc-conformance-SLICES_A_3 | 30 + tests/ref/fate/vvc-conformance-SPS_B_1 | 133 + tests/ref/fate/vvc-conformance-STILL_B_1 | 10 + tests/ref/fate/vvc-conformance-SUBPIC_A_3 | 9 + tests/ref/fate/vvc-conformance-TILE_A_2 | 14 + tests/ref/fate/vvc-conformance-WPP_A_3 | 54 + tests/ref/fate/vvc-conformance-WP_A_3 | 22 + tests/ref/fate/vvc-conformance-WRAP_A_4 | 14 + tests/ref/fate/webm-av1-extradata-update | 4 +- tests/ref/fate/webm-dash-chapters | 4 +- tests/ref/fate/webm-hdr10-plus-remux | 63 + tests/ref/fate/webm-webvtt-remux | 8 +- tests/ref/fate/xvid-custom-matrix | 2 +- tests/ref/fate/xvid-idct | 2 +- tests/ref/fate/zmbv-8bit | 1 - tests/ref/lavf-fate/av1.mkv | 4 +- tests/ref/lavf-fate/av1.mp4 | 4 +- tests/ref/lavf-fate/evc.mp4 | 3 + tests/ref/lavf-fate/hevc.flv | 3 + tests/ref/lavf/aiff | 4 +- tests/ref/lavf/asf | 2 +- tests/ref/lavf/ast | 4 +- tests/ref/lavf/dv_ntsc | 4 +- tests/ref/lavf/gif | 2 +- tests/ref/lavf/gxf_ntsc | 6 +- tests/ref/lavf/ismv | 6 +- tests/ref/lavf/mka | 4 +- tests/ref/lavf/mkv | 4 +- tests/ref/lavf/mkv_attachment | 4 +- tests/ref/lavf/mov | 14 +- tests/ref/lavf/mov_rtphint | 4 +- tests/ref/lavf/mp4 | 6 +- tests/ref/lavf/mpg | 4 +- tests/ref/lavf/mxf | 10 +- tests/ref/lavf/mxf_dvcpro100 | 3 + tests/ref/lavf/mxf_ffv1 | 3 + tests/ref/lavf/mxf_opatom | 2 +- tests/ref/lavf/s16.voc | 4 +- tests/ref/lavf/smjpeg | 6 +- tests/ref/lavf/voc | 4 +- tests/ref/lavf/y4m | 4 +- tests/ref/seek/acodec-pcm-alaw | 54 +- tests/ref/seek/acodec-pcm-f32be | 54 +- tests/ref/seek/acodec-pcm-f32le | 54 +- tests/ref/seek/acodec-pcm-f64be | 54 +- tests/ref/seek/acodec-pcm-f64le | 54 +- tests/ref/seek/acodec-pcm-mulaw | 54 +- tests/ref/seek/acodec-pcm-s16le | 54 +- tests/ref/seek/acodec-pcm-s24le | 54 +- tests/ref/seek/acodec-pcm-s32le | 54 +- tests/ref/seek/acodec-pcm-u8 | 54 +- tests/ref/seek/lavf-aiff | 30 +- tests/ref/seek/lavf-al | 46 +- tests/ref/seek/lavf-au | 30 +- tests/ref/seek/lavf-dv | 32 +- tests/ref/seek/lavf-mkv | 44 +- tests/ref/seek/lavf-mov | 44 +- tests/ref/seek/lavf-ul | 46 +- tests/ref/seek/lavf-voc | 22 +- tests/ref/seek/lavf-wav | 30 +- tests/ref/seek/lavf-y4m | 22 +- tests/ref/seek/vsynth_lena-dv | 24 +- tests/ref/seek/vsynth_lena-dv-411 | 24 +- tests/ref/seek/vsynth_lena-dv-50 | 24 +- tests/ref/vsynth/vsynth1-jpeg2000-gbrp12 | 4 + tests/ref/vsynth/vsynth1-jpeg2000-yuva444p16 | 4 + tests/ref/vsynth/vsynth1-msrle | 4 + tests/ref/vsynth/vsynth1-prores | 2 +- tests/ref/vsynth/vsynth1-prores_444 | 2 +- tests/ref/vsynth/vsynth1-prores_444_int | 2 +- tests/ref/vsynth/vsynth1-prores_int | 2 +- tests/ref/vsynth/vsynth1-prores_ks | 2 +- tests/ref/vsynth/vsynth1-rpza | 4 + tests/ref/vsynth/vsynth1-smc | 4 + tests/ref/vsynth/vsynth2-jpeg2000-gbrp12 | 4 + tests/ref/vsynth/vsynth2-jpeg2000-yuva444p16 | 4 + tests/ref/vsynth/vsynth2-msrle | 4 + tests/ref/vsynth/vsynth2-prores | 2 +- tests/ref/vsynth/vsynth2-prores_444 | 2 +- tests/ref/vsynth/vsynth2-prores_444_int | 2 +- tests/ref/vsynth/vsynth2-prores_int | 2 +- tests/ref/vsynth/vsynth2-prores_ks | 2 +- tests/ref/vsynth/vsynth2-rpza | 4 + tests/ref/vsynth/vsynth2-smc | 4 + tests/ref/vsynth/vsynth3-jpeg2000-gbrp12 | 4 + tests/ref/vsynth/vsynth3-jpeg2000-yuva444p16 | 4 + tests/ref/vsynth/vsynth3-msrle | 4 + tests/ref/vsynth/vsynth3-prores | 2 +- tests/ref/vsynth/vsynth3-prores_444 | 2 +- tests/ref/vsynth/vsynth3-prores_444_int | 2 +- tests/ref/vsynth/vsynth3-prores_int | 2 +- tests/ref/vsynth/vsynth3-prores_ks | 2 +- tests/ref/vsynth/vsynth3-rpza | 4 + tests/ref/vsynth/vsynth3-smc | 4 + tests/ref/vsynth/vsynth_lena-jpeg2000-gbrp12 | 4 + .../vsynth/vsynth_lena-jpeg2000-yuva444p16 | 4 + tests/ref/vsynth/vsynth_lena-msrle | 4 + tests/ref/vsynth/vsynth_lena-prores | 2 +- tests/ref/vsynth/vsynth_lena-prores_444 | 2 +- tests/ref/vsynth/vsynth_lena-prores_444_int | 2 +- tests/ref/vsynth/vsynth_lena-prores_int | 2 +- tests/ref/vsynth/vsynth_lena-prores_ks | 2 +- tests/ref/vsynth/vsynth_lena-rpza | 4 + tests/ref/vsynth/vsynth_lena-smc | 4 + tests/streamgroups/audio_element-5_1_4 | 7 + tests/streamgroups/audio_element-7_1_4 | 6 + tests/streamgroups/audio_element-ambisonic_1 | 2 + tests/streamgroups/audio_element-stereo | 3 + tests/streamgroups/mix_presentation-5_1_4 | 2 + tests/streamgroups/mix_presentation-7_1_4 | 2 + .../streamgroups/mix_presentation-ambisonic_1 | 2 + tests/streamgroups/mix_presentation-stereo | 3 + tools/Makefile | 6 +- tools/coverity.c | 18 + tools/decode_simple.c | 17 +- tools/enc_recon_frame_test.c | 396 ++ tools/ffeval.c | 4 + tools/general_assembly.pl | 121 +- tools/normalize.py | 106 +- tools/patcheck | 3 +- tools/probetest.c | 5 +- tools/python/convert.py | 56 - tools/python/convert_from_tensorflow.py | 607 --- tools/python/convert_header.py | 26 - tools/{cl2c => source2c} | 10 +- tools/target_bsf_fuzzer.c | 3 +- tools/target_dec_fuzzer.c | 18 +- tools/target_dem_fuzzer.c | 8 +- tools/target_sws_fuzzer.c | 202 + tools/zmqsend.c | 3 + tools/zmqshell.py | 63 +- 3232 files changed, 222679 insertions(+), 99659 deletions(-) create mode 100644 doc/community.texi delete mode 100644 doc/dev_community/community.md delete mode 100644 doc/dev_community/resolution_process.md create mode 100644 doc/infra.txt create mode 100644 fftools/ffmpeg_dec.c create mode 100644 fftools/ffmpeg_enc.c create mode 100644 fftools/ffmpeg_sched.c create mode 100644 fftools/ffmpeg_sched.h create mode 100644 fftools/ffmpeg_utils.h create mode 100644 fftools/ffplay_renderer.c rename libavcodec/x86/fft.h => fftools/ffplay_renderer.h (56%) create mode 100644 libavcodec/aacdec.h create mode 100644 libavcodec/aacdec_common.c create mode 100644 libavcodec/aacencdsp.h delete mode 100644 libavcodec/aarch64/fft_init_aarch64.c delete mode 100644 libavcodec/aarch64/fft_neon.S create mode 100644 libavcodec/aarch64/hevcdsp_deblock_neon.S create mode 100644 libavcodec/aarch64/hevcdsp_epel_neon.S delete mode 100644 libavcodec/aarch64/mdct_neon.S create mode 100644 libavcodec/aom_film_grain.c create mode 100644 libavcodec/aom_film_grain.h create mode 100644 libavcodec/aom_film_grain_template.c delete mode 100644 libavcodec/arm/fft_init_arm.c delete mode 100644 libavcodec/arm/fft_neon.S delete mode 100644 libavcodec/arm/fft_vfp.S delete mode 100644 libavcodec/arm/mdct_neon.S delete mode 100644 libavcodec/arm/mdct_vfp.S delete mode 100644 libavcodec/arm/rdft_neon.S create mode 100644 libavcodec/av1_levels.c create mode 100644 libavcodec/av1_levels.h create mode 100644 libavcodec/avcodec_internal.h create mode 100644 libavcodec/bsf/Makefile rename libavcodec/{aac_adtstoasc_bsf.c => bsf/aac_adtstoasc.c} (100%) rename libavcodec/{av1_frame_merge_bsf.c => bsf/av1_frame_merge.c} (100%) rename libavcodec/{av1_frame_split_bsf.c => bsf/av1_frame_split.c} (100%) rename libavcodec/{av1_metadata_bsf.c => bsf/av1_metadata.c} (98%) rename libavcodec/{chomp_bsf.c => bsf/chomp.c} (100%) rename libavcodec/{dca_core_bsf.c => bsf/dca_core.c} (100%) rename libavcodec/{dts2pts_bsf.c => bsf/dts2pts.c} (100%) rename libavcodec/{dump_extradata_bsf.c => bsf/dump_extradata.c} (99%) rename libavcodec/{dv_error_marker_bsf.c => bsf/dv_error_marker.c} (77%) rename libavcodec/{eac3_core_bsf.c => bsf/eac3_core.c} (100%) create mode 100644 libavcodec/bsf/evc_frame_merge.c rename libavcodec/{extract_extradata_bsf.c => bsf/extract_extradata.c} (88%) rename libavcodec/{filter_units_bsf.c => bsf/filter_units.c} (71%) rename libavcodec/{h264_metadata_bsf.c => bsf/h264_metadata.c} (99%) rename libavcodec/{h264_mp4toannexb_bsf.c => bsf/h264_mp4toannexb.c} (65%) rename libavcodec/{h264_redundant_pps_bsf.c => bsf/h264_redundant_pps.c} (100%) rename libavcodec/{h265_metadata_bsf.c => bsf/h265_metadata.c} (99%) create mode 100644 libavcodec/bsf/h266_metadata.c rename libavcodec/{hapqa_extract_bsf.c => bsf/hapqa_extract.c} (95%) rename libavcodec/{hevc_mp4toannexb_bsf.c => bsf/hevc_mp4toannexb.c} (94%) rename libavcodec/{imx_dump_header_bsf.c => bsf/imx_dump_header.c} (100%) rename libavcodec/{media100_to_mjpegb_bsf.c => bsf/media100_to_mjpegb.c} (100%) rename libavcodec/{mjpeg2jpeg_bsf.c => bsf/mjpeg2jpeg.c} (100%) rename libavcodec/{mjpega_dump_header_bsf.c => bsf/mjpega_dump_header.c} (100%) rename libavcodec/{movsub_bsf.c => bsf/movsub.c} (100%) rename libavcodec/{mpeg2_metadata_bsf.c => bsf/mpeg2_metadata.c} (100%) rename libavcodec/{mpeg4_unpack_bframes_bsf.c => bsf/mpeg4_unpack_bframes.c} (100%) rename libavcodec/{noise_bsf.c => bsf/noise.c} (88%) rename libavcodec/{null_bsf.c => bsf/null.c} (100%) rename libavcodec/{opus_metadata_bsf.c => bsf/opus_metadata.c} (100%) rename libavcodec/{pcm_rechunk_bsf.c => bsf/pcm_rechunk.c} (91%) rename libavcodec/{pgs_frame_merge_bsf.c => bsf/pgs_frame_merge.c} (100%) rename libavcodec/{prores_metadata_bsf.c => bsf/prores_metadata.c} (81%) rename libavcodec/{remove_extradata_bsf.c => bsf/remove_extradata.c} (99%) rename libavcodec/{setts_bsf.c => bsf/setts.c} (100%) create mode 100644 libavcodec/bsf/showinfo.c rename libavcodec/{trace_headers_bsf.c => bsf/trace_headers.c} (97%) rename libavcodec/{truehd_core_bsf.c => bsf/truehd_core.c} (100%) rename libavcodec/{vp9_metadata_bsf.c => bsf/vp9_metadata.c} (97%) rename libavcodec/{vp9_raw_reorder_bsf.c => bsf/vp9_raw_reorder.c} (100%) rename libavcodec/{vp9_superframe_bsf.c => bsf/vp9_superframe.c} (100%) rename libavcodec/{vp9_superframe_split_bsf.c => bsf/vp9_superframe_split.c} (100%) create mode 100644 libavcodec/bsf/vvc_mp4toannexb.c create mode 100644 libavcodec/cbs_h266.h create mode 100644 libavcodec/cbs_h266_syntax_template.c create mode 100644 libavcodec/cbs_vp8.c create mode 100644 libavcodec/cbs_vp8.h create mode 100644 libavcodec/cbs_vp8_syntax_template.c delete mode 100644 libavcodec/cos_tablegen.c delete mode 100644 libavcodec/crystalhd.c create mode 100644 libavcodec/d3d12va_av1.c create mode 100644 libavcodec/d3d12va_decode.c create mode 100644 libavcodec/d3d12va_decode.h create mode 100644 libavcodec/d3d12va_h264.c create mode 100644 libavcodec/d3d12va_hevc.c create mode 100644 libavcodec/d3d12va_mpeg2.c create mode 100644 libavcodec/d3d12va_vc1.c create mode 100644 libavcodec/d3d12va_vp9.c delete mode 100644 libavcodec/dct.c create mode 100644 libavcodec/dxv.h create mode 100644 libavcodec/dxvenc.c delete mode 100644 libavcodec/dynamic_hdr10_plus.c create mode 100644 libavcodec/evc.h create mode 100644 libavcodec/evc_parse.c create mode 100644 libavcodec/evc_parse.h create mode 100644 libavcodec/evc_parser.c create mode 100644 libavcodec/evc_ps.c create mode 100644 libavcodec/evc_ps.h delete mode 100644 libavcodec/fft-internal.h delete mode 100644 libavcodec/fft.h delete mode 100644 libavcodec/fft_fixed_32.c delete mode 100644 libavcodec/fft_init_table.c delete mode 100644 libavcodec/fft_table.h delete mode 100644 libavcodec/fft_template.c create mode 100644 libavcodec/h26x/h2656_deblock_template.c create mode 100644 libavcodec/h26x/h2656_inter_template.c create mode 100644 libavcodec/h26x/h2656_sao_template.c delete mode 100644 libavcodec/hq_hqa.h rename libavcodec/{hq_hqadata.c => hq_hqadata.h} (99%) create mode 100644 libavcodec/hwaccel_internal.h rename libavcodec/{aarch64/asm-offsets.h => itut35.h} (69%) create mode 100644 libavcodec/jpeg2000dec.h create mode 100644 libavcodec/jpeg2000htdec.c create mode 100644 libavcodec/jpeg2000htdec.h create mode 100644 libavcodec/jpegxl.h create mode 100644 libavcodec/jpegxl_parse.c create mode 100644 libavcodec/jpegxl_parse.h create mode 100644 libavcodec/jpegxl_parser.c create mode 100644 libavcodec/leaddata.h create mode 100644 libavcodec/leaddec.c create mode 100644 libavcodec/leb.h create mode 100644 libavcodec/libaribcaption.c delete mode 100644 libavcodec/libopenjpegdec.c delete mode 100644 libavcodec/libvpx.c create mode 100644 libavcodec/libxevd.c create mode 100644 libavcodec/libxeve.c delete mode 100644 libavcodec/loongarch/h264_intrapred_lasx.c rename libavcodec/loongarch/{h264_intrapred_lasx.h => h264_intrapred_loongarch.h} (70%) create mode 100644 libavcodec/loongarch/h264chroma.S delete mode 100644 libavcodec/loongarch/h264chroma_lasx.c delete mode 100644 libavcodec/loongarch/h264chroma_lasx.h create mode 100644 libavcodec/loongarch/h264chroma_loongarch.h create mode 100644 libavcodec/loongarch/h264dsp.S delete mode 100644 libavcodec/loongarch/h264dsp_lasx.h create mode 100644 libavcodec/loongarch/h264dsp_loongarch.h create mode 100644 libavcodec/loongarch/h264idct.S delete mode 100644 libavcodec/loongarch/h264idct_lasx.c create mode 100644 libavcodec/loongarch/h264idct_loongarch.c create mode 100644 libavcodec/loongarch/h264intrapred.S create mode 100644 libavcodec/loongarch/h264qpel.S delete mode 100644 libavcodec/loongarch/h264qpel_lasx.h create mode 100644 libavcodec/loongarch/h264qpel_loongarch.h create mode 100644 libavcodec/loongarch/h264qpel_lsx.c create mode 100644 libavcodec/loongarch/hevc_add_res.S create mode 100644 libavcodec/loongarch/hevc_idct.S create mode 100644 libavcodec/loongarch/hevc_mc.S create mode 100644 libavcodec/loongarch/hevcdsp_lasx.h create mode 100644 libavcodec/loongarch/loongson_asm.S create mode 100644 libavcodec/lpc_functions.h delete mode 100644 libavcodec/mdct_fixed_32.c delete mode 100644 libavcodec/mdct_template.c delete mode 100644 libavcodec/mips/aaccoder_mips.c delete mode 100644 libavcodec/mips/fft_mips.c delete mode 100644 libavcodec/mp3_header_decompress_bsf.c create mode 100644 libavcodec/msrleenc.c create mode 100644 libavcodec/osq.c create mode 100644 libavcodec/pdvdec.c delete mode 100644 libavcodec/ppc/fft_altivec.S delete mode 100644 libavcodec/ppc/fft_init.c delete mode 100644 libavcodec/ppc/fft_vsx.c delete mode 100644 libavcodec/ppc/fft_vsx.h create mode 100644 libavcodec/qoadec.c delete mode 100644 libavcodec/rdft.c delete mode 100644 libavcodec/rdft.h create mode 100644 libavcodec/refstruct.c create mode 100644 libavcodec/refstruct.h create mode 100644 libavcodec/riscv/aacencdsp_init.c create mode 100644 libavcodec/riscv/aacencdsp_rvv.S create mode 100644 libavcodec/riscv/ac3dsp_init.c create mode 100644 libavcodec/riscv/ac3dsp_rvb.S create mode 100644 libavcodec/riscv/ac3dsp_rvv.S create mode 100644 libavcodec/riscv/blockdsp_init.c create mode 100644 libavcodec/riscv/blockdsp_rvv.S rename libavutil/bfin/timer.h => libavcodec/riscv/exrdsp_init.c (63%) create mode 100644 libavcodec/riscv/exrdsp_rvv.S create mode 100644 libavcodec/riscv/flacdsp_init.c create mode 100644 libavcodec/riscv/flacdsp_rvv.S rename libavcodec/{x86/dct_init.c => riscv/g722dsp_init.c} (66%) create mode 100644 libavcodec/riscv/g722dsp_rvv.S create mode 100644 libavcodec/riscv/h264_chroma_init_riscv.c create mode 100644 libavcodec/riscv/h264_mc_chroma.S create mode 100644 libavcodec/riscv/huffyuvdsp_init.c rename libavfilter/dnn/dnn_backend_native_layer_maximum.h => libavcodec/riscv/huffyuvdsp_rvv.S (50%) create mode 100644 libavcodec/riscv/jpeg2000dsp_init.c create mode 100644 libavcodec/riscv/jpeg2000dsp_rvv.S create mode 100644 libavcodec/riscv/llauddsp_init.c create mode 100644 libavcodec/riscv/llauddsp_rvv.S rename libavcodec/{arm/rdft_init_arm.c => riscv/llviddsp_init.c} (68%) create mode 100644 libavcodec/riscv/llviddsp_rvv.S create mode 100644 libavcodec/riscv/llvidencdsp_init.c rename libavformat/jpegxl_probe.h => libavcodec/riscv/llvidencdsp_rvv.S (65%) create mode 100644 libavcodec/riscv/lpc_init.c create mode 100644 libavcodec/riscv/lpc_rvv.S create mode 100644 libavcodec/riscv/me_cmp_init.c create mode 100644 libavcodec/riscv/me_cmp_rvv.S rename libavcodec/{x86/fft_init.c => riscv/rv34dsp_init.c} (57%) create mode 100644 libavcodec/riscv/rv34dsp_rvv.S create mode 100644 libavcodec/riscv/sbrdsp_init.c create mode 100644 libavcodec/riscv/sbrdsp_rvv.S create mode 100644 libavcodec/riscv/svqenc_init.c rename libavcodec/{dynamic_hdr10_plus.h => riscv/svqenc_rvv.S} (51%) create mode 100644 libavcodec/riscv/takdsp_init.c create mode 100644 libavcodec/riscv/takdsp_rvv.S create mode 100644 libavcodec/riscv/utvideodsp_init.c create mode 100644 libavcodec/riscv/utvideodsp_rvv.S create mode 100644 libavcodec/riscv/vc1dsp_init.c create mode 100644 libavcodec/riscv/vc1dsp_rvv.S rename libavcodec/{x86/hpeldsp_vp3_init.c => riscv/vp8dsp_init.c} (50%) create mode 100644 libavcodec/riscv/vp8dsp_rvv.S create mode 100644 libavcodec/rtv1.c create mode 100644 libavcodec/tests/av1_levels.c delete mode 100644 libavcodec/tests/fft.c create mode 100644 libavcodec/vaapi_encode_av1.c create mode 100644 libavcodec/vmixdec.c create mode 100644 libavcodec/vp8data.c rename libavcodec/{fft_float.c => vulkan.c} (94%) rename libavcodec/{mdct_float.c => vulkan.h} (87%) create mode 100644 libavcodec/vulkan_av1.c create mode 100644 libavcodec/vulkan_decode.c create mode 100644 libavcodec/vulkan_decode.h create mode 100644 libavcodec/vulkan_h264.c create mode 100644 libavcodec/vulkan_hevc.c create mode 100644 libavcodec/vulkan_video.c create mode 100644 libavcodec/vulkan_video.h create mode 100644 libavcodec/vvc.h create mode 100644 libavcodec/vvc/Makefile create mode 100644 libavcodec/vvc/vvc_cabac.c create mode 100644 libavcodec/vvc/vvc_cabac.h create mode 100644 libavcodec/vvc/vvc_ctu.c create mode 100644 libavcodec/vvc/vvc_ctu.h create mode 100644 libavcodec/vvc/vvc_data.c create mode 100644 libavcodec/vvc/vvc_data.h create mode 100644 libavcodec/vvc/vvc_filter.c create mode 100644 libavcodec/vvc/vvc_filter.h create mode 100644 libavcodec/vvc/vvc_filter_template.c create mode 100644 libavcodec/vvc/vvc_inter.c create mode 100644 libavcodec/vvc/vvc_inter.h create mode 100644 libavcodec/vvc/vvc_inter_template.c create mode 100644 libavcodec/vvc/vvc_intra.c create mode 100644 libavcodec/vvc/vvc_intra.h create mode 100644 libavcodec/vvc/vvc_intra_template.c create mode 100644 libavcodec/vvc/vvc_intra_utils.c create mode 100644 libavcodec/vvc/vvc_itx_1d.c create mode 100644 libavcodec/vvc/vvc_itx_1d.h create mode 100644 libavcodec/vvc/vvc_mvs.c create mode 100644 libavcodec/vvc/vvc_mvs.h create mode 100644 libavcodec/vvc/vvc_ps.c create mode 100644 libavcodec/vvc/vvc_ps.h create mode 100644 libavcodec/vvc/vvc_refs.c create mode 100644 libavcodec/vvc/vvc_refs.h create mode 100644 libavcodec/vvc/vvc_thread.c create mode 100644 libavcodec/vvc/vvc_thread.h create mode 100644 libavcodec/vvc/vvcdec.c create mode 100644 libavcodec/vvc/vvcdec.h create mode 100644 libavcodec/vvc/vvcdsp.c create mode 100644 libavcodec/vvc/vvcdsp.h create mode 100644 libavcodec/vvc/vvcdsp_template.c create mode 100644 libavcodec/vvc_parser.c delete mode 100644 libavcodec/x86/fft.asm create mode 100644 libavcodec/x86/h26x/h2656_inter.asm create mode 100644 libavcodec/x86/h26x/h2656dsp.c create mode 100644 libavcodec/x86/h26x/h2656dsp.h delete mode 100644 libavcodec/x86/hpeldsp_vp3.asm create mode 100644 libavcodec/x86/vvc/Makefile create mode 100644 libavcodec/x86/vvc/vvc_mc.asm create mode 100644 libavcodec/x86/vvc/vvcdsp_init.c delete mode 100644 libavcodec/xvmc.h create mode 100644 libavdevice/ccfifo.c create mode 100644 libavfilter/aap_template.c create mode 100644 libavfilter/aarch64/vf_bwdif_init_aarch64.c create mode 100644 libavfilter/aarch64/vf_bwdif_neon.S create mode 100644 libavfilter/adynamicequalizer_template.c create mode 100644 libavfilter/af_aap.c create mode 100644 libavfilter/af_arls.c create mode 100644 libavfilter/anlms_template.c create mode 100644 libavfilter/arls_template.c create mode 100644 libavfilter/avfilter_internal.h delete mode 100644 libavfilter/bwdif.h create mode 100644 libavfilter/bwdifdsp.c create mode 100644 libavfilter/bwdifdsp.h create mode 100644 libavfilter/ccfifo.c create mode 100644 libavfilter/ccfifo.h delete mode 100644 libavfilter/deshake.h create mode 100644 libavfilter/dialoguenhance_template.c delete mode 100644 libavfilter/dnn/dnn_backend_native.c delete mode 100644 libavfilter/dnn/dnn_backend_native.h delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_avgpool.c delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_avgpool.h delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_conv2d.c delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_conv2d.h delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_dense.c delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_dense.h delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_depth2space.c delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_depth2space.h delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_mathbinary.c delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_mathbinary.h delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_mathunary.c delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_mathunary.h delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_maximum.c delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_pad.c delete mode 100644 libavfilter/dnn/dnn_backend_native_layer_pad.h delete mode 100644 libavfilter/dnn/dnn_backend_native_layers.c delete mode 100644 libavfilter/dnn/dnn_backend_tf.h create mode 100644 libavfilter/dnn/dnn_backend_torch.cpp delete mode 100644 libavfilter/fifo.c create mode 100644 libavfilter/qrencode.c create mode 100644 libavfilter/riscv/Makefile rename libavutil/bfin/bswap.h => libavfilter/riscv/af_afir_init.c (62%) create mode 100644 libavfilter/riscv/af_afir_rvv.S create mode 100644 libavfilter/silenceremove_template.c delete mode 100644 libavfilter/tests/dnn-layer-avgpool.c delete mode 100644 libavfilter/tests/dnn-layer-conv2d.c delete mode 100644 libavfilter/tests/dnn-layer-dense.c delete mode 100644 libavfilter/tests/dnn-layer-depth2space.c delete mode 100644 libavfilter/tests/dnn-layer-mathbinary.c delete mode 100644 libavfilter/tests/dnn-layer-mathunary.c delete mode 100644 libavfilter/tests/dnn-layer-maximum.c delete mode 100644 libavfilter/tests/dnn-layer-pad.c create mode 100644 libavfilter/textutils.c create mode 100644 libavfilter/textutils.h create mode 100644 libavfilter/vf_bwdif_cuda.c create mode 100644 libavfilter/vf_bwdif_cuda.cu create mode 100644 libavfilter/vf_bwdif_vulkan.c create mode 100644 libavfilter/vf_ccrepack.c create mode 100644 libavfilter/vf_fsync.c create mode 100644 libavfilter/vf_nlmeans_vulkan.c create mode 100644 libavfilter/vf_quirc.c create mode 100644 libavfilter/vf_scale_vt.c create mode 100644 libavfilter/vf_tiltandshift.c create mode 100644 libavfilter/vf_transpose_vt.c create mode 100644 libavfilter/vf_xfade_vulkan.c create mode 100644 libavfilter/vsrc_testsrc_vulkan.c rename {libavutil => libavfilter}/vulkan_glslang.c (95%) rename {libavutil => libavfilter}/vulkan_shaderc.c (96%) create mode 100644 libavfilter/vulkan_spirv.h create mode 100644 libavformat/ac4dec.c create mode 100644 libavformat/ac4enc.c rename libavformat/{aea.c => aeadec.c} (65%) create mode 100644 libavformat/aeaenc.c create mode 100644 libavformat/bitstream.c create mode 100644 libavformat/dvdclut.c rename libavfilter/dnn/dnn_backend_openvino.h => libavformat/dvdclut.h (53%) create mode 100644 libavformat/dvdvideodec.c create mode 100644 libavformat/evc.c create mode 100644 libavformat/evc.h create mode 100644 libavformat/evcdec.c rename libavcodec/tests/fft-fixed32.c => libavformat/ffjni.c (85%) delete mode 100644 libavformat/fifo_test.c create mode 100644 libavformat/iamf.c create mode 100644 libavformat/iamf.h create mode 100644 libavformat/iamf_parse.c create mode 100644 libavformat/iamf_parse.h create mode 100644 libavformat/iamf_reader.c create mode 100644 libavformat/iamf_reader.h create mode 100644 libavformat/iamf_writer.c create mode 100644 libavformat/iamf_writer.h create mode 100644 libavformat/iamfdec.c create mode 100644 libavformat/iamfenc.c create mode 100644 libavformat/jpegxl_anim_dec.c rename libavcodec/tests/avfft.c => libavformat/jpegxl_parse.c (86%) delete mode 100644 libavformat/jpegxl_probe.c create mode 100644 libavformat/osq.c create mode 100644 libavformat/pdvdec.c create mode 100644 libavformat/qoadec.c create mode 100644 libavformat/rangecoder_dec.c create mode 100644 libavformat/rcwtenc.c create mode 100644 libavformat/usmdec.c create mode 100644 libavformat/vvc.c create mode 100644 libavformat/vvc.h create mode 100644 libavformat/vvcdec.c rename libavutil/{x86 => }/emms.h (91%) create mode 100644 libavutil/executor.c create mode 100644 libavutil/executor.h create mode 100644 libavutil/hwcontext_d3d12va.c create mode 100644 libavutil/hwcontext_d3d12va.h create mode 100644 libavutil/hwcontext_d3d12va_internal.h create mode 100644 libavutil/iamf.c create mode 100644 libavutil/iamf.h rename libavfilter/dnn/dnn_backend_native_layers.h => libavutil/loongarch/timer.h (56%) create mode 100644 libavutil/riscv/bswap_rvb.S create mode 100644 libavutil/sfc64.h create mode 100644 libavutil/tests/side_data_array.c create mode 100644 libavutil/timestamp.c delete mode 100644 libavutil/tomi/intreadwrite.h create mode 100644 libavutil/video_hint.c create mode 100644 libavutil/video_hint.h create mode 100644 libswscale/loongarch/input.S create mode 100644 libswscale/loongarch/output.S create mode 100644 libswscale/loongarch/output_lsx.c create mode 100644 libswscale/loongarch/swscale.S create mode 100644 libswscale/loongarch/swscale_lsx.c create mode 100644 libswscale/loongarch/yuv2rgb_lsx.c rename libavfilter/thread.h => libswscale/riscv/rgb2rgb_rvb.S (73%) create mode 100644 tests/checkasm/aacencdsp.c create mode 100644 tests/checkasm/ac3dsp.c create mode 100644 tests/checkasm/h264chroma.c create mode 100644 tests/checkasm/hevc_deblock.c create mode 100644 tests/checkasm/llauddsp.c create mode 100644 tests/checkasm/rv34dsp.c create mode 100644 tests/checkasm/svq1enc.c create mode 100644 tests/checkasm/takdsp.c create mode 100644 tests/checkasm/vf_bwdif.c create mode 100644 tests/checkasm/vvc_mc.c delete mode 100644 tests/fate/dnn.mak create mode 100644 tests/fate/enc_external.mak delete mode 100644 tests/fate/fft.mak create mode 100644 tests/fate/iamf.mak create mode 100644 tests/fate/jpeg2000.mak create mode 100644 tests/fate/jxl.mak create mode 100644 tests/fate/qoa.mak create mode 100644 tests/fate/vvc.mak create mode 100644 tests/filtergraphs/iamf_5_1_4 create mode 100644 tests/filtergraphs/iamf_7_1_4 create mode 100644 tests/filtergraphs/iamf_ambisonic_1 create mode 100644 tests/filtergraphs/mov-mp4-pcm create mode 100644 tests/filtergraphs/overlay_yuv444p10 create mode 100644 tests/maps/fsync-down create mode 100644 tests/maps/fsync-up create mode 100644 tests/ref/fate/atrac-aea-remux create mode 100644 tests/ref/fate/atrac-matroska-remux create mode 100644 tests/ref/fate/cavs-demux create mode 100644 tests/ref/fate/cbs-h264-discard-bidir create mode 100644 tests/ref/fate/cbs-h264-discard-nonintra create mode 100644 tests/ref/fate/cbs-h264-discard-nonkey create mode 100644 tests/ref/fate/cbs-h264-discard-nonref create mode 100644 tests/ref/fate/cbs-hevc-discard-bidir create mode 100644 tests/ref/fate/cbs-hevc-discard-nonintra create mode 100644 tests/ref/fate/cbs-hevc-discard-nonkey create mode 100644 tests/ref/fate/cbs-hevc-discard-nonref create mode 100644 tests/ref/fate/cbs-vvc-APSALF_A_2 create mode 100644 tests/ref/fate/cbs-vvc-APSLMCS_D_1 create mode 100644 tests/ref/fate/cbs-vvc-APSMULT_A_4 create mode 100644 tests/ref/fate/cbs-vvc-AUD_A_3 create mode 100644 tests/ref/fate/cbs-vvc-BOUNDARY_A_3 create mode 100644 tests/ref/fate/cbs-vvc-BUMP_A_2 create mode 100644 tests/ref/fate/cbs-vvc-CROP_B_4 create mode 100644 tests/ref/fate/cbs-vvc-CodingToolsSets_A_2 create mode 100644 tests/ref/fate/cbs-vvc-DCI_A_3 create mode 100644 tests/ref/fate/cbs-vvc-HRD_A_3 create mode 100644 tests/ref/fate/cbs-vvc-OPI_B_3 create mode 100644 tests/ref/fate/cbs-vvc-PHSH_B_1 create mode 100644 tests/ref/fate/cbs-vvc-POC_A_1 create mode 100644 tests/ref/fate/cbs-vvc-PPS_B_1 create mode 100644 tests/ref/fate/cbs-vvc-RAP_A_1 create mode 100644 tests/ref/fate/cbs-vvc-SAO_A_3 create mode 100644 tests/ref/fate/cbs-vvc-SCALING_A_1 create mode 100644 tests/ref/fate/cbs-vvc-SLICES_A_3 create mode 100644 tests/ref/fate/cbs-vvc-SPS_B_1 create mode 100644 tests/ref/fate/cbs-vvc-STILL_B_1 create mode 100644 tests/ref/fate/cbs-vvc-SUBPIC_A_3 create mode 100644 tests/ref/fate/cbs-vvc-TILE_A_2 create mode 100644 tests/ref/fate/cbs-vvc-VPS_A_3 create mode 100644 tests/ref/fate/cbs-vvc-WPP_A_3 create mode 100644 tests/ref/fate/cbs-vvc-WP_A_3 create mode 100644 tests/ref/fate/cbs-vvc-WRAP_A_4 create mode 100644 tests/ref/fate/dxv3-ycg6 create mode 100644 tests/ref/fate/dxv3-yg10 create mode 100644 tests/ref/fate/dxv3enc-dxt1 create mode 100644 tests/ref/fate/enhanced-flv-av1 create mode 100644 tests/ref/fate/enhanced-flv-hevc create mode 100644 tests/ref/fate/enhanced-flv-vp9 create mode 100644 tests/ref/fate/ffmpeg-bsf-input create mode 100644 tests/ref/fate/ffmpeg-error-rate-fail create mode 100644 tests/ref/fate/ffmpeg-error-rate-pass create mode 100644 tests/ref/fate/ffmpeg-filter-in-eof create mode 100644 tests/ref/fate/ffmpeg-input-r create mode 100644 tests/ref/fate/ffmpeg-loopback-decoding rename tests/ref/fate/{ffmpeg-streamloop => ffmpeg-streamloop-copy} (100%) create mode 100644 tests/ref/fate/ffmpeg-streamloop-transcode-av create mode 100644 tests/ref/fate/filter-asegment-samples-absolute create mode 100644 tests/ref/fate/filter-asegment-samples-relative create mode 100644 tests/ref/fate/filter-asegment-timestamps-absolute create mode 100644 tests/ref/fate/filter-asegment-timestamps-relative create mode 100644 tests/ref/fate/filter-bwdif-mode0 create mode 100644 tests/ref/fate/filter-bwdif-mode1 create mode 100644 tests/ref/fate/filter-bwdif10 create mode 100644 tests/ref/fate/filter-channelsplit create mode 100644 tests/ref/fate/filter-fsync-down create mode 100644 tests/ref/fate/filter-fsync-up create mode 100644 tests/ref/fate/filter-median create mode 100644 tests/ref/fate/filter-overlay_yuv444p10 create mode 100644 tests/ref/fate/filter-pixdesc-gbrap14be create mode 100644 tests/ref/fate/filter-pixdesc-gbrap14le create mode 100644 tests/ref/fate/filter-pixdesc-p212be create mode 100644 tests/ref/fate/filter-pixdesc-p212le create mode 100644 tests/ref/fate/filter-pixdesc-p412be create mode 100644 tests/ref/fate/filter-pixdesc-p412le create mode 100644 tests/ref/fate/filter-pixelize-avg create mode 100644 tests/ref/fate/filter-pixelize-max create mode 100644 tests/ref/fate/filter-pixelize-min create mode 100644 tests/ref/fate/filter-tiltandshift create mode 100644 tests/ref/fate/filter-tpad-add-duration create mode 100644 tests/ref/fate/filter-untile-yuv422p rename tests/ref/fate/{fitsdec-gbrap16le => fitsdec-gbrap16be} (79%) rename tests/ref/fate/{fitsdec-gbrp16 => fitsdec-gbrp16be} (79%) create mode 100644 tests/ref/fate/force_key_frames-source create mode 100644 tests/ref/fate/force_key_frames-source-drop create mode 100644 tests/ref/fate/force_key_frames-source-dup create mode 100644 tests/ref/fate/h264-bsf-mp4toannexb-new-extradata create mode 100644 tests/ref/fate/iamf-5_1_4 create mode 100644 tests/ref/fate/iamf-7_1_4 create mode 100644 tests/ref/fate/iamf-ambisonic_1 create mode 100644 tests/ref/fate/iamf-stereo create mode 100644 tests/ref/fate/jpeg2000dec-ds0_ht_01_b11 create mode 100644 tests/ref/fate/jpeg2000dec-p0_01 create mode 100644 tests/ref/fate/jpeg2000dec-p0_02 create mode 100644 tests/ref/fate/jpeg2000dec-p0_03 create mode 100644 tests/ref/fate/jpeg2000dec-p0_04 create mode 100644 tests/ref/fate/jpeg2000dec-p0_05 create mode 100644 tests/ref/fate/jpeg2000dec-p0_07 create mode 100644 tests/ref/fate/jpeg2000dec-p0_08 create mode 100644 tests/ref/fate/jpeg2000dec-p0_09 create mode 100644 tests/ref/fate/jpeg2000dec-p0_11 create mode 100644 tests/ref/fate/jpeg2000dec-p0_12 create mode 100644 tests/ref/fate/jpeg2000dec-p0_14 create mode 100644 tests/ref/fate/jpeg2000dec-p0_15 create mode 100644 tests/ref/fate/jpeg2000dec-p0_16 create mode 100644 tests/ref/fate/jpg-rgb-1 create mode 100644 tests/ref/fate/jpg-rgb-2 create mode 100644 tests/ref/fate/jpg-rgb-221 create mode 100644 tests/ref/fate/jpg-rgb-3 create mode 100644 tests/ref/fate/jpg-rgb-4 create mode 100644 tests/ref/fate/jpg-rgb-5 create mode 100644 tests/ref/fate/jpg-rgb-baseline create mode 100644 tests/ref/fate/jpg-rgb-progressive create mode 100644 tests/ref/fate/jxl-anim-demux-belgium create mode 100644 tests/ref/fate/jxl-anim-demux-icos4d create mode 100644 tests/ref/fate/jxl-anim-demux-lenna256 create mode 100644 tests/ref/fate/jxl-anim-demux-newton create mode 100644 tests/ref/fate/jxl-multiframe-permuted-toc create mode 100644 tests/ref/fate/jxl-small-ext-box create mode 100644 tests/ref/fate/libsvtav1-hdr10 create mode 100644 tests/ref/fate/libx264-hdr10 create mode 100644 tests/ref/fate/libx264-simple create mode 100644 tests/ref/fate/lossless-monkeysaudio-legacy create mode 100644 tests/ref/fate/lossless-osq create mode 100644 tests/ref/fate/lossless-rka create mode 100644 tests/ref/fate/matroska-alac-remux create mode 100644 tests/ref/fate/matroska-hdr10-plus-remux create mode 100644 tests/ref/fate/matroska-non-rotation-displaymatrix create mode 100644 tests/ref/fate/matroska-side-data-pref-codec create mode 100644 tests/ref/fate/matroska-side-data-pref-packet create mode 100644 tests/ref/fate/matroska-stereo_mode create mode 100644 tests/ref/fate/mov-heic-demux-still-image-1-item create mode 100644 tests/ref/fate/mov-heic-demux-still-image-grid create mode 100644 tests/ref/fate/mov-heic-demux-still-image-iovl create mode 100644 tests/ref/fate/mov-heic-demux-still-image-iovl-2 create mode 100644 tests/ref/fate/mov-heic-demux-still-image-multiple-items create mode 100644 tests/ref/fate/mov-mp4-iamf-5_1_4 create mode 100644 tests/ref/fate/mov-mp4-iamf-7_1_4 create mode 100644 tests/ref/fate/mov-mp4-iamf-ambisonic_1 create mode 100644 tests/ref/fate/mov-mp4-iamf-stereo create mode 100644 tests/ref/fate/mov-mp4-pcm create mode 100644 tests/ref/fate/mov-mp4-pcm-float create mode 100644 tests/ref/fate/mov-read-amve create mode 100644 tests/ref/fate/mov-write-amve create mode 100644 tests/ref/fate/qoa-152 create mode 100644 tests/ref/fate/qoa-278 create mode 100644 tests/ref/fate/qoa-303 create mode 100644 tests/ref/fate/side_data_array create mode 100644 tests/ref/fate/swr-async-firstpts create mode 100644 tests/ref/fate/ts-timed-id3-demux create mode 100644 tests/ref/fate/vp9-superframe-bsf create mode 100644 tests/ref/fate/vvc-conformance-APSALF_A_2 create mode 100644 tests/ref/fate/vvc-conformance-APSLMCS_D_1 create mode 100644 tests/ref/fate/vvc-conformance-APSMULT_A_4 create mode 100644 tests/ref/fate/vvc-conformance-AUD_A_3 create mode 100644 tests/ref/fate/vvc-conformance-BUMP_A_2 create mode 100644 tests/ref/fate/vvc-conformance-CROP_B_4 create mode 100644 tests/ref/fate/vvc-conformance-CodingToolsSets_A_2 create mode 100644 tests/ref/fate/vvc-conformance-DCI_A_3 create mode 100644 tests/ref/fate/vvc-conformance-HRD_A_3 create mode 100644 tests/ref/fate/vvc-conformance-PHSH_B_1 create mode 100644 tests/ref/fate/vvc-conformance-POC_A_1 create mode 100644 tests/ref/fate/vvc-conformance-PPS_B_1 create mode 100644 tests/ref/fate/vvc-conformance-RAP_A_1 create mode 100644 tests/ref/fate/vvc-conformance-SAO_A_3 create mode 100644 tests/ref/fate/vvc-conformance-SCALING_A_1 create mode 100644 tests/ref/fate/vvc-conformance-SLICES_A_3 create mode 100644 tests/ref/fate/vvc-conformance-SPS_B_1 create mode 100644 tests/ref/fate/vvc-conformance-STILL_B_1 create mode 100644 tests/ref/fate/vvc-conformance-SUBPIC_A_3 create mode 100644 tests/ref/fate/vvc-conformance-TILE_A_2 create mode 100644 tests/ref/fate/vvc-conformance-WPP_A_3 create mode 100644 tests/ref/fate/vvc-conformance-WP_A_3 create mode 100644 tests/ref/fate/vvc-conformance-WRAP_A_4 create mode 100644 tests/ref/fate/webm-hdr10-plus-remux create mode 100644 tests/ref/lavf-fate/evc.mp4 create mode 100644 tests/ref/lavf-fate/hevc.flv create mode 100644 tests/ref/lavf/mxf_dvcpro100 create mode 100644 tests/ref/lavf/mxf_ffv1 create mode 100644 tests/ref/vsynth/vsynth1-jpeg2000-gbrp12 create mode 100644 tests/ref/vsynth/vsynth1-jpeg2000-yuva444p16 create mode 100644 tests/ref/vsynth/vsynth1-msrle create mode 100644 tests/ref/vsynth/vsynth1-rpza create mode 100644 tests/ref/vsynth/vsynth1-smc create mode 100644 tests/ref/vsynth/vsynth2-jpeg2000-gbrp12 create mode 100644 tests/ref/vsynth/vsynth2-jpeg2000-yuva444p16 create mode 100644 tests/ref/vsynth/vsynth2-msrle create mode 100644 tests/ref/vsynth/vsynth2-rpza create mode 100644 tests/ref/vsynth/vsynth2-smc create mode 100644 tests/ref/vsynth/vsynth3-jpeg2000-gbrp12 create mode 100644 tests/ref/vsynth/vsynth3-jpeg2000-yuva444p16 create mode 100644 tests/ref/vsynth/vsynth3-msrle create mode 100644 tests/ref/vsynth/vsynth3-rpza create mode 100644 tests/ref/vsynth/vsynth3-smc create mode 100644 tests/ref/vsynth/vsynth_lena-jpeg2000-gbrp12 create mode 100644 tests/ref/vsynth/vsynth_lena-jpeg2000-yuva444p16 create mode 100644 tests/ref/vsynth/vsynth_lena-msrle create mode 100644 tests/ref/vsynth/vsynth_lena-rpza create mode 100644 tests/ref/vsynth/vsynth_lena-smc create mode 100644 tests/streamgroups/audio_element-5_1_4 create mode 100644 tests/streamgroups/audio_element-7_1_4 create mode 100644 tests/streamgroups/audio_element-ambisonic_1 create mode 100644 tests/streamgroups/audio_element-stereo create mode 100644 tests/streamgroups/mix_presentation-5_1_4 create mode 100644 tests/streamgroups/mix_presentation-7_1_4 create mode 100644 tests/streamgroups/mix_presentation-ambisonic_1 create mode 100644 tests/streamgroups/mix_presentation-stereo create mode 100644 tools/enc_recon_frame_test.c mode change 100644 => 100755 tools/general_assembly.pl delete mode 100644 tools/python/convert.py delete mode 100644 tools/python/convert_from_tensorflow.py delete mode 100644 tools/python/convert_header.py rename tools/{cl2c => source2c} (78%) create mode 100644 tools/target_sws_fuzzer.c diff --git a/Changelog b/Changelog index 24b5be3b232..2faa8f3805b 100644 --- a/Changelog +++ b/Changelog @@ -1,185 +1,417 @@ Entries are sorted chronologically from oldest to youngest within each release, releases are sorted from youngest to oldest. -version 6.0.1: - avcodec/4xm: Check for cfrm exhaustion - avformat/mov: Disallow FTYP after streams - doc/html: fix styling issue with Texinfo 7.0 - doc/html: support texinfo 7.0 - Changelog: update - avformat/lafdec: Check for 0 parameters - avformat/lafdec: Check for 0 parameters - avfilter/buffersink: fix order of operation with = and <0 - avfilter/framesync: fix order of operation with = and <0 - tools/target_dec_fuzzer: Adjust threshold for CSCD - avcodec/dovi_rpu: Use 64 bit in get_us/se_coeff() - avformat/mov: Check that is_still_picture_avif has no trak based streams - avformat/matroskadec: Fix declaration-after-statement warnings - Update for FFmpeg 6.0.1 - fftools/ffmpeg_mux_init: Restrict disabling automatic copying of metadata - avformat/rtsp: Use rtsp_st->stream_index - avformat/rtsp: Use rtsp_st->stream_index - avutil/tx_template: fix integer ovberflwo in fft3() - avcodec/jpeg2000dec: Check image offset - avformat/mxfdec: Check klv offset - libavutil/ppc/cpu.c: check that AT_HWCAP2 is defined - avcodec/h2645_parse: Avoid EAGAIN - avcodec/xvididct: Make c* unsigned to avoid undefined overflows - avcodec/bonk: Fix undefined overflow in predictor_calc_error() - avformat/tmv: Check video chunk size - avcodec/h264_parser: saturate dts a bit - avformat/asfdec_f: Saturate presentation time in marker - avformat/xwma: sanity check bits_per_coded_sample - avformat/matroskadec: Check prebuffered_ns for overflow - avformat/wavdec: Check left avio_tell for overflow - avformat/tta: Better totalframes check - avformat/rpl: Check for number_of_chunks overflow - avformat/mov: compute absolute dts difference without overflow in mov_find_next_sample() - avformat/jacosubdec: Check timeres - avformat/jacosubdec: avoid signed integer overflows in get_shift() - avformat/jacosubdec: Factorize code in get_shift() a bit - avformat/sbgdec: Check for negative duration or un-representable end pts - avcodec/escape124: Do not return random numbers - avcodec/apedec: Fix an integer overflow in predictor_update_filter() - tools/target_dec_fuzzer: Adjust wmapro threshold - avcodec/wavarc: Allocate AV_INPUT_BUFFER_PADDING_SIZE - avcodec/wavarc: Fix integer overflwo in do_stereo() - avutil/tx_template: Fix some signed integer overflows in DECL_FFT5() - avcodec/aacdec_template: Better avoidance of signed integer overflow in imdct_and_windowing_eld() - tools/target_dec_fuzzer: Adjust threshold for MVHA - avformat/avs: Check if return code is representable - avcodec/flacdec: Fix integer overflow in "33bit" DECODER_SUBFRAME_FIXED_WIDE() - avcodec/flacdec: Fix overflow in "33bit" decorrelate - avcodec/lcldec: Make PNG filter addressing match the code afterwards - avformat/westwood_vqa: Check chunk size - avformat/sbgdec: Check for period overflow - avformat/concatdec: Check in/outpoint for overflow - avformat/mov: Check avif_info - avformat/mxfdec: Remove this_partition - avcodec/xvididct: Fix integer overflow in idct_row() - avcodec/celp_math: avoid overflow in shift - tools/target_dec_fuzzer: Adjust threshold for rtv1 - avformat/hls: reduce default max reload to 3 - avformat/format: Stop reading data at EOF during probing - avcodec/bonk: Fix integer overflow in predictor_calc_error() - avcodec/jpeg2000dec: jpeg2000 has its own lowres option - avcodec/huffyuvdec: avoid undefined behavior with get_vlc2() failure - avcodec/cscd: Fix "CamStudio Lossless Codec 1.0" gzip files - avcodec/cscd: Check for CamStudio Lossless Codec 1.0 behavior in end check of LZO files - avcodec/mpeg4videodec: consider lowres in dest_pcm[] - avcodec/hevcdec: Fix undefined memcpy() - avcodec/mpeg4videodec: more unsigned in amv computation - avcodec/tta: fix signed overflow in decorrelate - avcodec/apedec: remove unused variable - avcodec/apedec: Fix 48khz 24bit below insane level - avcodec/apedec: Fix CRC for 24bps and bigendian - avcodec/wavarc: Check that nb_samples is not negative - avcodec/wavarc: Check shift - avcodec/xvididct: Fix integer overflow in idct_row() - avformat/avr: Check sample rate - avformat/imf_cpl: Replace NULL content_title_utf8 by "" - avformat/imf_cpl: xmlNodeListGetString() can return NULL - avcodec/aacdec_template: Fix undefined signed interger operations - avcodec/wavarc: Fix k limit - avcodec/rka: Fix integer overflow in decode_filter() - avformat/rka: bps < 8 is invalid - avcodec/pcm: allow Changing parameters - avutil/tx_template: extend to 2M - avcodec/jpeg2000dec: Check for reduction factor and image offset - avutil/softfloat: Basic documentation for av_sincos_sf() - avutil/softfloat: fix av_sincos_sf() - tools/target_dec_fuzzer: Adjust threshold for speex - avcodec/utils: fix 2 integer overflows in get_audio_frame_duration() - avcodec/hevcdec: Avoid null pointer dereferences in MC - avcodec/takdsp: Fix integer overflows - avcodec/mpegvideo_dec: consider interlaced lowres 4:2:0 chroma in edge emulation check better - avcodec/rka: use unsigned for buf0 additions - avcodec/rka: Avoid undefined left shift - avcodec: Ignoring errors is only possible before the input end - avformat/jpegxl_probe: Forward error codes - avformat/jpegxl_probe: check length instead of blindly reading - avformat/jpegxl_probe: Remove intermediate macro obfuscation around get_bits*() - avcodec/noise_bsf: Check for wrapped frames - avformat/oggparsetheora: clip duration within 64bit - avcodec/rka: avoid undefined multiply in cmode==0 - avcodec/rka: use 64bit for srate_pad computation - avcodec/bonk: Avoid undefined integer overflow in predictor_calc_error() - avformat/wavdec: Check that smv block fits in available space - avcodec/adpcm: Fix integer overflow in intermediate in ADPCM_XMD - avcodec/dpcm: fix undefined interger overflow in wady - avcodec/tiff: add a zero DNG_LINEARIZATION_TABLE check - avcodec/tak: Check remaining bits in ff_tak_decode_frame_header() - avcodec/sonic: Fix two undefined integer overflows - avcodec/utils: the IFF_ILBM implementation assumes that there are a multiple of 16 allocated - avcodec/flacdec: Fix signed integre overflow - avcodec/exr: Cleanup befor return - avcodec/pngdec: Do not pass AVFrame into global header decode - avcodec/pngdec: remove AVFrame argument from decode_iccp_chunk() - avcodec/wavarc: Check order before using it to write the list - avcodec/bonk: decode multiple passes in intlist_read() at once - avcodec/vorbisdec: Check codebook float values to be finite - avcodec/g2meet: Replace fake allocation avoidance for framebuf - avutil/tx_priv: Use unsigned in BF() to avoid signed overflows - avcodec/lcldec: More space for rgb24 - avcodec/lcldec: Support 4:1:1 and 4:2:2 with odd width - libavcodec/lcldec: width and height should not be unsigned - avformat/imf: fix invalid resource handling - avcodec/escape124: Check that blocks are allocated before use - avcodec/rka: Fix signed integer overflow in decode_filter() - avcodec/huffyuvdec: Fix undefined behavior with shift - avcodec/j2kenc: Replace RGB24 special case by generic test - avcodec/j2kenc: Replace BGR48 / GRAY16 test by test for number of bits - avcodec/j2kenc: simplify pixel format setup - avcodec/j2kenc: Fix funky bpno errors on decoding - avcodec/j2kenc: remove misleading pred value - avcodec/j2kenc: fix 5/3 DWT identifer - avcodec/vp3: Check width to avoid assertion failure - avcodec/g729postfilter: Limit shift in long term filter - avcodec/wavarc: Fix several integer overflows - avcodec/tests/snowenc: Fix 2nd test - avcodec/tests/snowenc: return a failure if DWT/IDWT mismatches - avcodec/snowenc: Fix visual weight calculation - avcodec/tests/snowenc: unbreak DWT tests - avcodec/mpeg12dec: Check input size - avcodec/escape124: Fix some return codes - avcodec/escape124: fix signdness of end of input check - Use https for repository links - avcodec/nvdec_hevc: fail to initialize on unsupported profiles - fftools/ffmpeg_enc: apply -top to individual encoded frames - avcodec/on2avc: use correct fft sizes - avcodec/on2avc: use the matching AVTX context for the 512 sized iMDCT - examples: fix build of mux and resample_audio - avcodec/nvenc: stop using deprecated rc modes with SDK 12.1 - configure: use non-deprecated nvenc GUID for conftest - avcodec/x86/mathops: clip constants used with shift instructions within inline assembly - avfilter/vsrc_ddagrab: calculate pointer position on rotated screens - avfilter/vsrc_ddagrab: account for mouse-only frames during probing - avcodec/aac_ac3_parser: add preprocessor checks for codec specific code - avcodec/nvenc: handle frame durations and AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE - Revert "lavc/nvenc: handle frame durations and AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE" - Revert "avcodec/nvenc: fix b-frame DTS behavior with fractional framerates" - avcodec/vdpau_mpeg4: fix order of quant matrix coefficients - avcodec/vdpau_mpeg12: fix order of quant matrix coefficients - avcodec/nvdec_mpeg4: fix order of quant matrix coefficients - avcodec/nvdec_mpeg2: fix order of quant matrix coefficients - fftools/ffmpeg_filter: fix leak of AVIOContext in read_binary() - fftools/ffmpeg: avoid possible invalid reads with short -tag values - avcodec/mp_cmp: reject invalid comparison function values - avcodec/aacpsy: clip global_quality within the psy_vbr_map array boundaries - avutil/wchar_filename: propagate MultiByteToWideChar() and WideCharToMultiByte() failures - avformat/concatf: check if any nodes were allocated - avcodec/nvenc: fix b-frame DTS behavior with fractional framerates - avcodec/vorbisdec: export skip_samples instead of dropping frames - fftools/ffmpeg_mux_init: avoid invalid reads in forced keyframe parsing - lavfi/vf_vpp_qsv: set the right timestamp for AVERROR_EOF - avfilter/vf_untile: swap the chroma shift values used for plane offsets - lavc/decode: stop mangling last_pkt_props->opaque - avcodec/nvenc: avoid failing b_ref_mode check when unset - lavu/vulkan: fix handle type for 32-bit targets - vulkan: Fix win/i386 calling convention - avfilter/graphparser: fix filter instance name when an id is provided - avcodec/aacps_tablegen: fix build error after avutil bump - avcodec/nvenc: fix potential NULL pointer dereference +version 7.0.2: + avcodec/snow: Fix off by 1 error in run_buffer + avcodec/utils: apply the same alignment to YUV410 as we do to YUV420 for snow + avformat/iamf_parse: Check for 0 samples + swscale: [loongarch] Fix checkasm-sw_yuv2rgb failure. + avcodec/aacps_tablegen_template: don't redefine CONFIG_HARDCODED_TABLES + avutil/hwcontext_vaapi: use the correct type for VASurfaceAttribExternalBuffers.buffers + avcodec/pcm-bluray/dvd: Use correct pointer types on BE + avcodec/pngenc: fix sBIT writing for indexed-color PNGs + avcodec/pngdec: use 8-bit sBIT cap for indexed PNGs per spec + avformat/mov: check that child boxes of trak are only present inside it + avformat/mov: check that sample and chunk count is 1 for HEIF + avcodec/videotoolboxenc: Fix bitrate doesn't work as expected + avdevice/dshow: Don't skip audio devices if no video device is present + avcodec/hdrenc: Allocate more space + avcodec/cfhdenc: Height of 16 is not supported + avcodec/cfhdenc: Allocate more space + avcodec/osq: fix integer overflow when applying factor + avcodec/osq: avoid using too large numbers for shifts and integers in update_residue_parameter() + avcodec/vaapi_encode: Check hwctx + avcodec/proresdec: Consider negative bits left + avcodec/alsdec: Clear shift_value + avcodec/hevc/hevcdec: Do not allow slices to depend on failed slices + avformat/mov: add an EOF check in IPRP + avfilter/vf_xfade: Check ff_inlink_consume_frame() for failure + avutil/slicethread: Check pthread_*_init() for failure + avutil/frame: Check log2_crop_align + avutil/buffer: Check ff_mutex_init() for failure + avformat/xmv: Check this_packet_size + avformat/webpenc: Check filesize in trailer + avformat/ty: rec_size seems to only need 32bit + avformat/tty: Check avio_size() + avformat/siff: Basic pkt_size check + avformat/sauce: Check avio_size() for failure + avformat/sapdec: Check ffurl_get_file_handle() for error + avformat/nsvdec: Check asize for PCM + avformat/mp3dec: Check header_filesize + avformat/mp3dec; Check for avio_size() failure + avformat/mov: Use 64bit for str_size + avformat/mm: Check length + avformat/hnm: Check *chunk_size + avformat/hlsenc: Check ret + avformat/bintext: Check avio_size() return + avformat/asfdec_o: Check size of index object + avfilter/vf_scale: Check ff_scale_adjust_dimensions() for failure + avfilter/scale_eval: Use 64bit, check values in ff_scale_adjust_dimensions() + avfilter/vf_lut3d: Check av_scanf() + avfilter/vf_elbg: Use unsigned for shifting into the top bit + avfilter/vf_premultiply: Use AV_PIX_MAX_PLANES + avfilter/vf_deshake_opencl: Ensure that the first iteration initializes the best variables + avformat/iamf_parse: Check for negative sample sizes + swscale/output: Fix integer overflows in yuv2rgba64_X_c_template + avformat/mxfdec: Reorder elements of expression in bisect loop + avutil/timecode: Use a 64bit framenum internally + avcodec/pnmdec: Use 64bit for input size check + avformat/mov: Check extradata in mov_read_iacb() + avcodec/mpeg12enc: Use av_rescale() in vbv_buffer_size computation + avcodec/utvideoenc: Use unsigned shift to build flags + avcodec/j2kenc: Merge dwt_norm into lambda + avcodec/vc2enc: Fix overflows with storing large values + avcodec/mpegvideo_enc: Do not duplicate pictures on shifting + avdevice/dshow_capture: Fix error handling in ff_dshow_##prefix##_Create() + avcodec/tiff: Check value on positive signed targets + avfilter/vf_convolution_opencl: Assert that the filter name is one of the filters + avfilter/vf_bm3d: Dont round MSE2SSE to an integer + avdevice/dshow: Remove NULL check on pin + avdevice/dshow: check ff_dshow_pin_ConnectionMediaType() for failure + avdevice/dshow: Check device_filter_unique_name before use + avdevice/dshow: Cleanup also on av_log case + avdevice/dshow_filter: Use wcscpy_s() + avcodec/flac_parser: Assert that we do not overrun the link_penalty array + avcodec/osq: avoid signed overflow in downsample path + avcodec/pixlet: Simplify pfx computation + avcodec/motion_est: Fix score squaring overflow + avcodec/mlpenc: Use 64 for ml, mr + avcodec/loco: Check loco_get_rice() for failure + avcodec/loco: check get_ur_golomb_jpegls() for failure + avcodec/leaddec: Check init_get_bits8() for failure + avcodec/imm4: check cbphi for error + avcodec/iff: Use signed count + avcodec/golomb: Assert that k is in the supported range for get_ur/sr_golomb() + avcodec/golomb: Document return for get_ur_golomb_jpegls() and get_sr_golomb_flac() + avcodec/dxv: Fix type in get_opcodes() + avcodec/cri: Check length + avcodec/xsubdec: Check parse_timecode() + avutil/imgutils: av_image_check_size2() ensure width and height fit in 32bit + avfilter/vf_tiltandshift: Free dst on error + doc/examples/mux: remove nop + avcodec/proresenc_kostya: use unsigned alpha for rotation + avformat/rtpenc_rfc4175: Use 64bit in computation if copy_offset + avformat/rtmpproto: Use AV_DICT_MATCH_CASE instead of litteral number + avformat/rtmppkt: Simplify and deobfuscate amf_tag_skip() slightly + avformat/rmdec: use 64bit for audio_framesize checks + avutil/wchar_filename: Correct sizeof + avutil/hwcontext_d3d11va: correct sizeof IDirect3DSurface9 + avutil/hwcontext_d3d11va: Free AVD3D11FrameDescriptor on error + avutil/hwcontext_d3d11va: correct sizeof AVD3D11FrameDescriptor + avcodec/vvc/refs: Use unsigned mask + doc/examples/vaapi_encode: Try to check fwrite() for failure + avformat/usmdec: Initialize value + avformat/tls_schannel: Initialize ret + avformat/subfile: Assert that whence is a known case + avformat/subfile: Merge if into switch() + avformat/rtsp: Check that lower transport is handled in one of the if() + avformat/rtsp: initialize reply1 + avformat/rtsp: use < 0 for error check + avformat/rtpenc_vc2hq: Check sizes + avfilter/af_aderivative: Free out on error + swscale/swscale: Use ptrdiff_t for linesize computations + avfilter/af_amerge: Cleanup on av_channel_layout_copy() failure + avfilter/af_afir: Assert format + avfilter/af_afftdn: Assert format + avfilter/af_pan: check nb_output_channels before use + cbs_av1: Reject thirty-two zero bits in uvlc code + avfilter/af_mcompand: compute half frequency in double + avfilter/af_channelsplit: Assert that av_channel_layout_channel_from_index() succeeds + avfilter/af_aresample: Cleanup on av_channel_layout_copy() failure + tools/coverity: Phase 1 study of anti-halicogenic for coverity av_rescale() + avfilter/vf_avgblur: Check plane instead of AVFrame + avfilter/drawutils: Fix depthb computation + avfilter/avf_showcwt: Check av_parse_video_rate() for failure + avformat/rdt: Check pkt_len + avformat/mpeg: Check len in mpegps_probe() + avformat/mxfenc: resurrects the error print + avdevice/dshow: Check ICaptureGraphBuilder2_SetFiltergraph() for failure + avcodec/mfenc: check IMFSample_ConvertToContiguousBuffer() for failure + avcodec/vc1_loopfilter: Factor duplicate code in vc1_b_h_intfi_loop_filter() + avcodec/vvc/ctu: Remove dead ret check + avcodec/vvc/dec: Remove constant eos_at_start + avformat/img2dec: assert no pipe on ts_from_file + avcodec/cbs_jpeg: Try to move the read entity to one side in a test + fftools/ffplay: Check vulkan_params + fftools/ffmpeg_enc: Initialize Decoder + fftools/ffmpeg_enc: Initialize fd + fftools/ffmpeg_enc: simplify opaque_ref check + avformat/mov: Check edit list for overflow + fftools/ffmpeg: Check read() for failure + avcodec/vvc/dec: Check ff_init_cabac_decoder() for failure + MAINTAINERS: Add Timo Rothenpieler to server admins + swscale/output: Avoid undefined overflow in yuv2rgb_write_full() + swscale/output: alpha can become negative after scaling, use multiply + avcodec/targaenc: Allocate space for the palette + avcodec/r210enc: Use av_rescale for bitrate + avcodec/jfdctint_template: Fewer integer anomalies + avcodec/snowenc: MV limits due to mv_penalty table size + tools/target_dec_fuzzer: Adjust threshold for MV30 + tools/target_dec_fuzzer: Adjust threshold for jpeg2000 + avformat/mxfdec: Check container_ul->desc before use + avcodec/libvpxenc: Cleanup on error + MAINTAINERS: Update the entries for the release maintainer for FFmpeg + doc/developer: Provide information about git send-email and gmail + avfilter/vf_rotate: Check ff_draw_init2() return value + avformat/mov: Use int64_t in intermediate for corrected_dts + avformat/mov: Use 64bit in intermediate for current_dts + avformat/matroskadec: Assert that num_levels is non negative + avformat/libzmq: Check av_strstart() + avformat/img2dec: Little JFIF / Exif cleanup + avformat/img2dec: Move DQT after unrelated if() + avformat/imfdec: Simplify get_next_track_with_minimum_timestamp() + avdevice/xcbgrab: Check sscanf() return + fftools/cmdutils: Add protective () to FLAGS + avformat/sdp: Check before appending "," + avcodec/libx264: Check init_get_bits8() return code + avcodec/ilbcdec: Remove dead code + avcodec/vp8: Check cond init + avcodec/vp8: Check mutex init + avcodec/proresenc_anatoliy: Assert that AV_PROFILE_UNKNOWN is replaced + avcodec/pcm-dvdenc: 64bit pkt-size + avcodec/notchlc: Check init_get_bits8() for failure + avcodec/tests/dct: Use 64bit in intermediate for error computation + avcodec/scpr3: Check add_dec() for failure + avcodec/rv34: assert that size is not 0 in rv34_gen_vlc_ext() + avcodec/wavpackenc: Use unsigned for potential 31bit shift + avcodec/vvc/mvs: Initialize mvf + avcodec/tests/jpeg2000dwt: Use 64bit in comparission + avcodec/tests/jpeg2000dwt: Use 64bit in err2 computation + avformat/fwse: Remove always false expression + avcodec/sga: Make it clear that the return is intentionally not checked + avformat/asfdec_f: Use 64bit for preroll computation + avformat/argo_asf: Use 64bit in offset intermediate + avformat/ape: Use 64bit for final frame size + avformat/ac4dec: Check remaining space in ac4_probe() + avdevice/pulse_audio_enc: Use av_rescale() to avoid integer overflow + avcodec/vlc: Cleanup on multi table alloc failure in ff_vlc_init_multi_from_lengths() + avcodec/tiff: Assert init_get_bits8() success in unpack_gray() + avcodec/tiff: Assert init_get_bits8() success in horizontal_fill() + tools/decode_simple: Check avcodec_send_packet() for errors on flushing + swscale/yuv2rgb: Use 64bit for brightness computation + swscale/x86/swscale: use a clearer name for INPUT_PLANER_RGB_A_FUNC_CASE + avutil/tests/opt: Check av_set_options_string() for failure + avutil/tests/dict: Check av_dict_set() before get for failure + avdevice/dshow: fix badly indented line + avformat/demux: resurrect dead stores + avcodec/tests/bitstream_template: Assert bits_init8() return + tools/enc_recon_frame_test: Assert that av_image_get_linesize() succeeds + avformat/iamf_writer: disallow Opus extradata with mapping family other than 0 + avformat/iamf_parse: sanitize audio_roll_distance values + avformat/iamf: byteswap values in OpusHeader + avformat/iamf: rename Codec Config seek_preroll to audio_roll_distance + avformat/iamf_writer: fix coded audio_roll_distance values + avformat/iamf_writer: fix PCM endian-ness flag + avformat/movenc: fix channel count and samplerate fields for IAMF tracks + avformat/iamf_parse: keep substream count consistent + avformat/iamf_parse: add missing padding to AAC extradata + avformat/iamf_parse: 0 layers are not allowed + avformat/iamf_parse: consider nb_substreams when accessing substreams array + avformat/iamf_parse: Remove dead case + avcodec/png: more informative error message for invalid sBIT size + avcodec/pngdec: avoid erroring with sBIT on indexed-color images + avfilter/vf_tiltandshift: fix buffer offset for yuv422p input + avutil/timestamp: avoid possible FPE when 0 is passed to av_ts_make_time_string2() + avformat/mov: add more checks for infe atom size + avformat/mov: check for EOF inside the infe list parsing loop + avformat/mov: check extent_offset calculation for overflow + avformat/mov: check that iloc offset values fit on an int64_t + avcodec/pngenc: fix mDCv typo + avcodec/pngdec: fix mDCv typo + avcodec/nvenc: fix segfault in intra-only mode + avdevice/avfoundation: add external video devices + aarch64: Add OpenBSD runtime detection of dotprod and i8mm using sysctl + fftools/ffplay_renderer: use correct NULL value for Vulkan type + qsv: Initialize impl_value + avutil/hwcontext_qsv: fix GCC 14.1 warnings + avcodec/mediacodecenc: workaround the alignment requirement for H.265 + avcodec/mediacodecenc: workaround the alignment requirement only for H.264 + lavc/lpc: fix off-by-one in R-V V compute_autocorr + lavc/vp9: reset segmentation fields when segmentation isn't enabled + configure: enable ffnvcodec, nvenc, nvdec for FreeBSD + lavc/sbrdsp: fix potential overflow in noise table + +version 7.0.1: + lavc/flacdsp: do not assume maximum R-V VL + avformat/flacdec: Reorder allocations to avoid leak on error + avcodec/adts_parser: Don't presume buffer to be padded + avformat/movenc: Check av_malloc() + avcodec/vp8: Return error on error + avformat/mov: store sample_sizes as unsigned ints + avformat/vvc: fix parsing sps_subpic_id + avformat/vvc: initialize some ptl flags + avcodec/mscc & mwsc: Check loop counts before use + avcodec/mpegvideo_enc: Fix potential overflow in RD + avcodec/mpeg4videodec: assert impossible wrap points + avcodec/mpeg12dec: Use 64bit in bit computation + avcodec/vqcdec: Check init_get_bits8() for failure + avcodec/vvc/dec: Check init_get_bits8() for failure + avcodec/vble: Check av_image_get_buffer_size() for failure + avcodec/vp3: Replace check by assert + avcodec/vp8: Forward return of ff_vpx_init_range_decoder() + avcodec/jpeg2000dec: remove ST=3 case + avcodec/qsvdec: Check av_image_get_buffer_size() for failure + avcodec/exr: Fix preview overflow + avcodec/decode: decode_simple_internal() only implements audio and video + avcodec/fmvc: remove dead assignment + avcodec/h2645_sei: Remove dead checks + avcodec/h264_slice: Remove dead sps check + avcodec/lpc: copy levenson coeffs only when they have been computed + avutil/tests/base64: Check with too short output array + libavutil/base64: Try not to write over the array end + avcodec/cbs_av1: Avoid shift overflow + fftools/ffplay: Check return of swr_alloc_set_opts2() + tools/opt_common: Check for malloc failure + doc/examples/demux_decode: Simplify loop + avformat/concatdec: Check file + avcodec/mpegvideo_enc: Fix 1 line and one column images + avcodec/amrwbdec: assert mode to be valid in decode_fixed_vector() + avcodec/wavarc: fix integer overflow in decode_5elp() block type 2 + swscale/output: Fix integer overflow in yuv2rgba64_full_1_c_template() + swscale/output: Fix integer overflow in yuv2rgba64_1_c_template + avcodec/av1dec: Change bit_depth to int + avcodec/av1dec: bit_depth cannot be another values than 8,10,12 + avcodec/avs3_parser: assert the return value of init_get_bits() + avcodec/avs2_parser: Assert init_get_bits8() success with const size 15 + avfilter/avfiltergraph: return value of ff_request_frame() is unused + avformat/mxfdec: Check body_offset + avformat/kvag: Check sample_rate + avcodec/atrac9dec: Check init_get_bits8() for failure + avcodec/ac3_parser: Check init_get_bits8() for failure + avcodec/pngdec: Check last AVFrame before deref + avcodec/hevcdec: Check ref frame + doc/examples/qsv_transcode: Initialize pointer before free + doc/examples/qsv_transcode: Simplify str_to_dict() loop + doc/examples/vaapi_transcode: Simplify loop + doc/examples/qsv_transcode: Simplify loop + avcodec/cbs_h2645: Check NAL space + avfilter/vf_thumbnail_cuda: Set ret before checking it + avfilter/signature_lookup: Dont copy uninitialized stuff around + avfilter/signature_lookup: Fix 2 differences to the refernce SW + avcodec/x86/vp3dsp_init: Set correct function pointer, fix crash + avformat/mp3dec: change bogus error message if read_header encounters EOF + avformat/mp3dec: simplify inner frame size check in mp3_read_header + avformat/mp3dec: only call ffio_ensure_seekback once + avcodec/cbs_h266: read vps_ptl_max_tid before using it + avcodec/cbs_h266: fix sh_collocated_from_l0_flag and sh_collocated_ref_idx infer + avformat/vvc: fix parsing some early VPS bitstream values + avformat/vvc: fix writing general_constraint_info bytes + avutil/ppc/cpu: Also use the machdep.altivec sysctl on NetBSD + lavd/v4l2: Use proper field type for second parameter of ioctl() with BSD's + vulkan_av1: Fix force_integer_mv value + vaapi_av1: Fix force_integer_mv value + av1dec: Add force_integer_mv derived field for decoder use + avutil/iamf: fix offsets for mix_gain options + avformat/iamfdec: check nb_streams in header read + avformat/mov: free the infe allocated item data on failure + avformat/iamf_writer: reject duplicated stream ids in a stream group + avformat/mov: don't read key_size bytes twice in the keys atom + avformat/mov: take into account the first eight bytes in the keys atom + avformat/mov: fix the check for the heif item parsing loop + avutil/iamf: fix mix_gain_class name + av1dec: Fix RefFrameSignBias calculation + avcodec/codec_par: always clear extradata_size in avcodec_parameters_to_context() + avcodec/mediacodecenc: Fix return empty packet when bsf is used + avcodec/hevcdec: Fix precedence, bogus film grain warning + avcodec/hevcdec: fix segfault on invalid film grain metadata + lavc/vvc: Skip enhancement layer NAL units + avformat/mov: ignore old infe box versions + vulkan_av1: add workaround for NVIDIA drivers tested on broken CTS + lavc/vulkan_av1: Use av1dec reference order hint information + lavc/av1: Record reference ordering information for each frame + doc/encoders: add missing libxvid option + doc/encoders: remove non-existent flag + fate/ffmpeg: Avoid dependency on samples + avcodec/wavpack: Remove always-false check + avcodec/wavpack: Fix leak and segfault on reallocation error + avcodec/lossless_videoencdsp: Don't presume alignment in diff_bytes + avcodec/ppc/h264dsp: Fix left shifts of negative numbers + +version 7.0: +- DXV DXT1 encoder +- LEAD MCMP decoder +- EVC decoding using external library libxevd +- EVC encoding using external library libxeve +- QOA decoder and demuxer +- aap filter +- demuxing, decoding, filtering, encoding, and muxing in the + ffmpeg CLI now all run in parallel +- enable gdigrab device to grab a window using the hwnd=HANDLER syntax +- IAMF raw demuxer and muxer +- D3D12VA hardware accelerated H264, HEVC, VP9, AV1, MPEG-2 and VC1 decoding +- tiltandshift filter +- qrencode filter and qrencodesrc source +- quirc filter +- lavu/eval: introduce randomi() function in expressions +- VVC decoder (experimental) +- fsync filter +- Raw Captions with Time (RCWT) closed caption muxer +- ffmpeg CLI -bsf option may now be used for input as well as output +- ffmpeg CLI options may now be used as -/opt , which is equivalent + to -opt > +- showinfo bitstream filter +- a C11-compliant compiler is now required; note that this requirement + will be bumped to C17 in the near future, so consider updating your + build environment if it lacks C17 support +- Change the default bitrate control method from VBR to CQP for QSV encoders. +- removed deprecated ffmpeg CLI options -psnr and -map_channel +- DVD-Video demuxer, powered by libdvdnav and libdvdread +- ffprobe -show_stream_groups option +- ffprobe (with -export_side_data film_grain) now prints film grain metadata +- AEA muxer +- ffmpeg CLI loopback decoders +- Support PacketTypeMetadata of PacketType in enhanced flv format +- ffplay with hwaccel decoding support (depends on vulkan renderer via libplacebo) +- dnn filter libtorch backend +- Android content URIs protocol +- AOMedia Film Grain Synthesis 1 (AFGS1) +- RISC-V optimizations for AAC, FLAC, JPEG-2000, LPC, RV4.0, SVQ, VC1, VP8, and more +- Loongarch optimizations for HEVC decoding +- Important AArch64 optimizations for HEVC +- IAMF support inside MP4/ISOBMFF +- Support for HEIF/AVIF still images and tiled still images +- Dolby Vision profile 10 support in AV1 +- Support for Ambient Viewing Environment metadata in MP4/ISOBMFF +- HDR10 metadata passthrough when encoding with libx264, libx265, and libsvtav1 + + +version 6.1: +- libaribcaption decoder +- Playdate video decoder and demuxer +- Extend VAAPI support for libva-win32 on Windows +- afireqsrc audio source filter +- arls filter +- ffmpeg CLI new option: -readrate_initial_burst +- zoneplate video source filter +- command support in the setpts and asetpts filters +- Vulkan decode hwaccel, supporting H264, HEVC and AV1 +- color_vulkan filter +- bwdif_vulkan filter +- nlmeans_vulkan filter +- RivaTuner video decoder +- xfade_vulkan filter +- vMix video decoder +- Essential Video Coding parser, muxer and demuxer +- Essential Video Coding frame merge bsf +- bwdif_cuda filter +- Microsoft RLE video encoder +- Raw AC-4 muxer and demuxer +- Raw VVC bitstream parser, muxer and demuxer +- Bitstream filter for editing metadata in VVC streams +- Bitstream filter for converting VVC from MP4 to Annex B +- scale_vt filter for videotoolbox +- transpose_vt filter for videotoolbox +- support for the P_SKIP hinting to speed up libx264 encoding +- Support HEVC,VP9,AV1 codec in enhanced flv format +- apsnr and asisdr audio filters +- OSQ demuxer and decoder +- Support HEVC,VP9,AV1 codec fourcclist in enhanced rtmp protocol +- CRI USM demuxer +- ffmpeg CLI '-top' option deprecated in favor of the setfield filter +- VAAPI AV1 encoder +- ffprobe XML output schema changed to account for multiple + variable-fields elements within the same parent element +- ffprobe -output_format option added as an alias of -of version 6.0: diff --git a/MAINTAINERS b/MAINTAINERS index 854ccc3fa4c..a1599c7b0c8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -34,8 +34,8 @@ Miscellaneous Areas =================== documentation Stefano Sabatini, Mike Melanson, Timothy Gu, Gyan Doshi -project server day to day operations Árpád Gereöffy, Michael Niedermayer, Reimar Doeffinger, Alexander Strasser, Nikolay Aleksandrov -project server emergencies Árpád Gereöffy, Reimar Doeffinger, Alexander Strasser, Nikolay Aleksandrov +project server day to day operations Árpád Gereöffy, Michael Niedermayer, Reimar Doeffinger, Alexander Strasser, Nikolay Aleksandrov, Timo Rothenpieler +project server emergencies Árpád Gereöffy, Reimar Doeffinger, Alexander Strasser, Nikolay Aleksandrov, Timo Rothenpieler presets Robert Swain metadata subsystem Aurelien Jacobs release management Michael Niedermayer @@ -144,7 +144,6 @@ Codecs: bgmc.c, bgmc.h Thilo Borgmann binkaudio.c Peter Ross cavs* Stefan Gehrer - cdxl.c Paul B Mahol celp_filters.* Vitor Sessak cinepak.c Roberto Togni cinepakenc.c Rl / Aetey G.T. AB @@ -163,7 +162,6 @@ Codecs: dv.c Roman Shaposhnik dvbsubdec.c Anshul Maheshwari eacmv*, eaidct*, eat* Peter Ross - evrc* Paul B Mahol exif.c, exif.h Thilo Borgmann ffv1* Michael Niedermayer ffwavesynth.c Nicolas George @@ -181,6 +179,7 @@ Codecs: interplayvideo.c Mike Melanson jni*, ffjni* Matthieu Bouron jpeg2000* Nicolas Bertrand + jpegxl* Leo Izen jvdec.c Peter Ross lcl*.c Roberto Togni, Reimar Doeffinger libcelt_dec.c Nicolas George @@ -210,12 +209,12 @@ Codecs: mqc* Nicolas Bertrand msmpeg4.c, msmpeg4data.h Michael Niedermayer msrle.c Mike Melanson + msrleenc.c Tomas Härdin msvideo1.c Mike Melanson nuv.c Reimar Doeffinger nvdec*, nvenc* Timo Rothenpieler omx.c Martin Storsjo, Aman Gupta opus* Rostislav Pehlivanov - paf.* Paul B Mahol pcx.c Ivo van Poorten pgssubdec.c Reimar Doeffinger ptx.c Ivo van Poorten @@ -229,7 +228,6 @@ Codecs: rpza.c Roberto Togni rtjpeg.c, rtjpeg.h Reimar Doeffinger rv10.c Michael Niedermayer - s3tc* Ivo van Poorten smc.c Mike Melanson snow* Michael Niedermayer, Loren Merritt sonic.c Alex Beregszaszi @@ -237,16 +235,13 @@ Codecs: srt* Aurelien Jacobs sunrast.c Ivo van Poorten svq3.c Michael Niedermayer - tak* Paul B Mahol truemotion1* Mike Melanson tta.c Alex Beregszaszi, Jaikrishnan Menon - ttaenc.c Paul B Mahol txd.c Ivo van Poorten v4l2_* Jorge Ramirez-Ortiz vc2* Rostislav Pehlivanov vcr1.c Michael Niedermayer videotoolboxenc.c Rick Kern, Aman Gupta - vima.c Paul B Mahol vorbisdec.c Denes Balatoni, David Conrad vorbisenc.c Oded Shimon vp3* Mike Melanson @@ -255,22 +250,21 @@ Codecs: vp8 David Conrad, Ronald Bultje vp9 Ronald Bultje vqavideo.c Mike Melanson + vvc Nuo Mi wmaprodec.c Sascha Sommer wmavoice.c Ronald S. Bultje wmv2.c Michael Niedermayer xan.c Mike Melanson - xbm* Paul B Mahol xface Stefano Sabatini - xwd* Paul B Mahol Hardware acceleration: dxva2* Hendrik Leppkes, Laurent Aimar, Steve Lhomme d3d11va* Steve Lhomme - mediacodec* Matthieu Bouron, Aman Gupta + mediacodec* Matthieu Bouron, Aman Gupta, Zhao Zhili vaapi* Haihao Xiang vaapi_encode* Mark Thompson, Haihao Xiang vdpau* Philip Langdale, Carl Eugen Hoyos - videotoolbox* Rick Kern, Aman Gupta + videotoolbox* Rick Kern, Aman Gupta, Zhao Zhili libavdevice @@ -305,64 +299,34 @@ Generic parts: motion_estimation.c Davinder Singh Filters: - f_drawgraph.c Paul B Mahol - af_adelay.c Paul B Mahol - af_aecho.c Paul B Mahol - af_afade.c Paul B Mahol af_amerge.c Nicolas George - af_aphaser.c Paul B Mahol af_aresample.c Michael Niedermayer - af_astats.c Paul B Mahol af_atempo.c Pavel Koshevoy - af_biquads.c Paul B Mahol - af_chorus.c Paul B Mahol - af_compand.c Paul B Mahol af_firequalizer.c Muhammad Faiz af_hdcd.c Burt P. - af_ladspa.c Paul B Mahol af_loudnorm.c Kyle Swanson af_pan.c Nicolas George - af_sidechaincompress.c Paul B Mahol - af_silenceremove.c Paul B Mahol - avf_aphasemeter.c Paul B Mahol - avf_avectorscope.c Paul B Mahol avf_showcqt.c Muhammad Faiz - vf_blend.c Paul B Mahol vf_bwdif Thomas Mundt (CC ) vf_chromakey.c Timo Rothenpieler - vf_colorchannelmixer.c Paul B Mahol vf_colorconstancy.c Mina Sami (CC ) - vf_colorbalance.c Paul B Mahol vf_colorkey.c Timo Rothenpieler - vf_colorlevels.c Paul B Mahol vf_coreimage.m Thilo Borgmann - vf_deband.c Paul B Mahol vf_dejudder.c Nicholas Robbins vf_delogo.c Jean Delvare (CC ) vf_drawbox.c/drawgrid Andrey Utkin - vf_extractplanes.c Paul B Mahol - vf_histogram.c Paul B Mahol + vf_fsync.c Thilo Borgmann vf_hqx.c Clément Bœsch vf_idet.c Pascal Massimino - vf_il.c Paul B Mahol vf_(t)interlace Thomas Mundt (CC ) vf_lenscorrection.c Daniel Oberhoff vf_libplacebo.c Niklas Haas - vf_mergeplanes.c Paul B Mahol vf_mestimate.c Davinder Singh vf_minterpolate.c Davinder Singh - vf_neighbor.c Paul B Mahol - vf_psnr.c Paul B Mahol - vf_random.c Paul B Mahol vf_readvitc.c Tobias Rapp (CC t.rapp at noa-archive dot com) vf_scale.c Michael Niedermayer - vf_separatefields.c Paul B Mahol - vf_ssim.c Paul B Mahol - vf_stereo3d.c Paul B Mahol - vf_telecine.c Paul B Mahol vf_tonemap_opencl.c Ruiling Song vf_yadif.c Michael Niedermayer - vf_zoompan.c Paul B Mahol Sources: vsrc_mandelbrot.c Michael Niedermayer @@ -384,7 +348,6 @@ Muxers/Demuxers: 4xm.c Mike Melanson aadec.c Vesselin Bontchev (vesselin.bontchev at yandex dot com) adtsenc.c Robert Swain - afc.c Paul B Mahol aiffdec.c Baptiste Coudurier, Matthieu Bouron aiffenc.c Baptiste Coudurier, Matthieu Bouron alp.c Zane van Iperen @@ -395,16 +358,12 @@ Muxers/Demuxers: argo_brp.c Zane van Iperen argo_cvg.c Zane van Iperen ass* Aurelien Jacobs - astdec.c Paul B Mahol astenc.c James Almer avi* Michael Niedermayer avisynth.c Stephen Hutchinson - avr.c Paul B Mahol bink.c Peter Ross boadec.c Michael Niedermayer - brstm.c Paul B Mahol caf* Peter Ross - cdxl.c Paul B Mahol codec2.c Tomas Härdin crc.c Michael Niedermayer dashdec.c Steven Liu @@ -413,10 +372,10 @@ Muxers/Demuxers: dfpwmdec.c Jack Bruienne dss.c Oleksij Rempel dtsdec.c foo86 - dtshddec.c Paul B Mahol dv.c Roman Shaposhnik + dvdvideodec.c Marth64 electronicarts.c Peter Ross - epafdec.c Paul B Mahol + evc* Samsung (Dawid Kozinski) ffm* Baptiste Coudurier flic.c Mike Melanson flvdec.c Michael Niedermayer @@ -430,22 +389,19 @@ Muxers/Demuxers: imf* Pierre-Anthony Lemieux img2*.c Michael Niedermayer ipmovie.c Mike Melanson - ircam* Paul B Mahol iss.c Stefan Gehrer - jpegxl_probe.* Leo Izen + jpegxl* Leo Izen jvdec.c Peter Ross kvag.c Zane van Iperen libmodplug.c Clément Bœsch libopenmpt.c Josh de Kock lmlm4.c Ivo van Poorten - lvfdec.c Paul B Mahol lxfdec.c Tomas Härdin matroska.c Aurelien Jacobs, Andreas Rheinhardt matroskadec.c Aurelien Jacobs, Andreas Rheinhardt matroskaenc.c David Conrad, Andreas Rheinhardt matroska subtitles (matroskaenc.c) John Peebles metadata* Aurelien Jacobs - mgsts.c Paul B Mahol microdvd* Aurelien Jacobs mm.c Peter Ross mov.c Baptiste Coudurier @@ -458,7 +414,6 @@ Muxers/Demuxers: msnwc_tcp.c Ramiro Polla mtv.c Reynaldo H. Verdejo Pinochet mxf* Baptiste Coudurier, Tomas Härdin - nistspheredec.c Paul B Mahol nsvdec.c Francois Revol nut* Michael Niedermayer nuv.c Reimar Doeffinger @@ -466,13 +421,12 @@ Muxers/Demuxers: oggenc.c Baptiste Coudurier oggparse*.c David Conrad oma.c Maxim Poliakovski - paf.c Paul B Mahol pp_bnk.c Zane van Iperen psxstr.c Mike Melanson pva.c Ivo van Poorten - pvfdec.c Paul B Mahol r3d.c Baptiste Coudurier raw.c Michael Niedermayer + rcwtenc.c Marth64 rdt.c Ronald S. Bultje rl2.c Sascha Sommer rmdec.c, rmenc.c Ronald S. Bultje @@ -491,11 +445,9 @@ Muxers/Demuxers: sdp.c Martin Storsjo segafilm.c Mike Melanson segment.c Stefano Sabatini - smjpeg* Paul B Mahol spdif* Anssi Hannula srtdec.c Aurelien Jacobs swf.c Baptiste Coudurier - takdec.c Paul B Mahol tta.c Alex Beregszaszi txd.c Ivo van Poorten voc.c Aurelien Jacobs @@ -505,7 +457,6 @@ Muxers/Demuxers: webvtt* Matthew J Heaney westwood.c Mike Melanson wtv.c Peter Ross - wvenc.c Paul B Mahol Protocols: async.c Zhang Rui @@ -543,10 +494,10 @@ LoongArch Shiyou Yin Mac OS X / PowerPC Romain Dolbeau, Guillaume Poirier Amiga / PowerPC Colin Ward Linux / PowerPC Lauri Kasanen +RISC-V Rémi Denis-Courmont Windows MinGW Alex Beregszaszi, Ramiro Polla Windows Cygwin Victor Paesa -Windows MSVC Matthew Oliver, Hendrik Leppkes -Windows ICL Matthew Oliver +Windows MSVC Hendrik Leppkes ADI/Blackfin DSP Marc Hoffman Sparc Roman Shaposhnik OS/2 KO Myung-Hun @@ -584,10 +535,12 @@ wm4 Releases ======== +7.0 Michael Niedermayer +6.1 Michael Niedermayer +5.1 Michael Niedermayer +4.4 Michael Niedermayer +3.4 Michael Niedermayer 2.8 Michael Niedermayer -2.7 Michael Niedermayer -2.6 Michael Niedermayer -2.5 Michael Niedermayer If you want to maintain an older release, please contact us @@ -614,7 +567,8 @@ Haihao Xiang (haihao) 1F0C 31E8 B4FE F7A4 4DC1 DC99 E0F5 76D4 76FC 437F Jaikrishnan Menon 61A1 F09F 01C9 2D45 78E1 C862 25DC 8831 AF70 D368 James Almer 7751 2E8C FD94 A169 57E6 9A7A 1463 01AD 7376 59E0 Jean Delvare 7CA6 9F44 60F1 BDC4 1FD2 C858 A552 6B9B B3CD 4E6A -Leo Izen (thebombzen) B6FD 3CFC 7ACF 83FC 9137 6945 5A71 C331 FD2F A19A +Leo Izen (Traneptora) B6FD 3CFC 7ACF 83FC 9137 6945 5A71 C331 FD2F A19A +Leo Izen (Traneptora) 1D83 0A0B CE46 709E 203B 26FC 764E 48EA 4822 1833 Loren Merritt ABD9 08F4 C920 3F65 D8BE 35D7 1540 DAA7 060F 56DE Lynne FE50 139C 6805 72CA FD52 1F8D A2FE A5F0 3F03 4464 Michael Niedermayer 9FF2 128B 147E F673 0BAD F133 611E C787 040B 0FAB diff --git a/Makefile b/Makefile index 1fb742f3909..b309dbc4db9 100644 --- a/Makefile +++ b/Makefile @@ -47,7 +47,7 @@ FF_DEP_LIBS := $(DEP_LIBS) FF_STATIC_DEP_LIBS := $(STATIC_DEP_LIBS) $(TOOLS): %$(EXESUF): %.o - $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(EXTRALIBS-$(*F)) $(EXTRALIBS) $(ELIBS) + $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(filter-out $(FF_DEP_LIBS), $^) $(EXTRALIBS-$(*F)) $(EXTRALIBS) $(ELIBS) target_dec_%_fuzzer$(EXESUF): target_dec_%_fuzzer.o $(FF_DEP_LIBS) $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH) @@ -64,9 +64,14 @@ tools/target_dem_fuzzer$(EXESUF): tools/target_dem_fuzzer.o $(FF_DEP_LIBS) tools/target_io_dem_fuzzer$(EXESUF): tools/target_io_dem_fuzzer.o $(FF_DEP_LIBS) $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH) +tools/target_sws_fuzzer$(EXESUF): tools/target_sws_fuzzer.o $(FF_DEP_LIBS) + $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $^ $(ELIBS) $(FF_EXTRALIBS) $(LIBFUZZER_PATH) + tools/enum_options$(EXESUF): ELIBS = $(FF_EXTRALIBS) tools/enum_options$(EXESUF): $(FF_DEP_LIBS) +tools/enc_recon_frame_test$(EXESUF): $(FF_DEP_LIBS) +tools/enc_recon_frame_test$(EXESUF): ELIBS = $(FF_EXTRALIBS) tools/scale_slice_test$(EXESUF): $(FF_DEP_LIBS) tools/scale_slice_test$(EXESUF): ELIBS = $(FF_EXTRALIBS) tools/sofa2wavs$(EXESUF): ELIBS = $(FF_EXTRALIBS) @@ -91,10 +96,10 @@ ffbuild/.config: $(CONFIGURABLE_COMPONENTS) SUBDIR_VARS := CLEANFILES FFLIBS HOSTPROGS TESTPROGS TOOLS \ HEADERS ARCH_HEADERS BUILT_HEADERS SKIPHEADERS \ ARMV5TE-OBJS ARMV6-OBJS ARMV8-OBJS VFP-OBJS NEON-OBJS \ - ALTIVEC-OBJS VSX-OBJS RVV-OBJS MMX-OBJS X86ASM-OBJS \ + ALTIVEC-OBJS VSX-OBJS MMX-OBJS X86ASM-OBJS \ MIPSFPU-OBJS MIPSDSPR2-OBJS MIPSDSP-OBJS MSA-OBJS \ - MMI-OBJS LSX-OBJS LASX-OBJS OBJS SLIBOBJS SHLIBOBJS \ - STLIBOBJS HOSTOBJS TESTOBJS + MMI-OBJS LSX-OBJS LASX-OBJS RV-OBJS RVV-OBJS \ + OBJS SLIBOBJS SHLIBOBJS STLIBOBJS HOSTOBJS TESTOBJS define RESET $(1) := @@ -131,13 +136,18 @@ endif $(LD) $(LDFLAGS) $(LDEXEFLAGS) $(LD_O) $(OBJS-$*) $(FF_EXTRALIBS) VERSION_SH = $(SRC_PATH)/ffbuild/version.sh +ifeq ($(VERSION_TRACKING),yes) GIT_LOG = $(SRC_PATH)/.git/logs/HEAD +endif .version: $(wildcard $(GIT_LOG)) $(VERSION_SH) ffbuild/config.mak .version: M=@ +ifneq ($(VERSION_TRACKING),yes) +libavutil/ffversion.h .version: REVISION=unknown +endif libavutil/ffversion.h .version: - $(M)$(VERSION_SH) $(SRC_PATH) libavutil/ffversion.h $(EXTRA_VERSION) + $(M)revision=$(REVISION) $(VERSION_SH) $(SRC_PATH) libavutil/ffversion.h $(EXTRA_VERSION) $(Q)touch .version # force version.sh to run whenever version might have changed diff --git a/RELEASE b/RELEASE index 5fe60723048..a8907c025d5 100644 --- a/RELEASE +++ b/RELEASE @@ -1 +1 @@ -6.0.1 +7.0.2 diff --git a/RELEASE_NOTES b/RELEASE_NOTES index ad1c6d7750e..93bcf7d5bdf 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,10 +1,10 @@ - ┌────────────────────────────────────────────┐ - │ RELEASE NOTES for FFmpeg 6.0 "Von Neumann" │ - └────────────────────────────────────────────┘ + ┌─────────────────────────────────────────┐ + │ RELEASE NOTES for FFmpeg 7.0 "Dijkstra" │ + └─────────────────────────────────────────┘ - The FFmpeg Project proudly presents FFmpeg 6.0 "Von Neumann", about 6 - months after the release of FFmpeg 5.1. + The FFmpeg Project proudly presents FFmpeg 7.0 "Dijkstra", about 6 + months after the release of FFmpeg 6.1. A complete Changelog is available at the root of the project, and the complete Git history on https://git.ffmpeg.org/gitweb/ffmpeg.git diff --git a/VERSION b/VERSION index 5fe60723048..a8907c025d5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.0.1 +7.0.2 diff --git a/compat/atomics/win32/stdatomic.h b/compat/atomics/win32/stdatomic.h index 28a627bfd3d..4f8ac2bb600 100644 --- a/compat/atomics/win32/stdatomic.h +++ b/compat/atomics/win32/stdatomic.h @@ -19,7 +19,6 @@ #ifndef COMPAT_ATOMICS_WIN32_STDATOMIC_H #define COMPAT_ATOMICS_WIN32_STDATOMIC_H -#define WIN32_LEAN_AND_MEAN #include #include #include diff --git a/compat/w32pthreads.h b/compat/w32pthreads.h index 6405e72b64f..2ff97352277 100644 --- a/compat/w32pthreads.h +++ b/compat/w32pthreads.h @@ -35,7 +35,6 @@ * As most functions here are used without checking return values, * only implement return values as necessary. */ -#define WIN32_LEAN_AND_MEAN #include #include #include @@ -66,7 +65,14 @@ typedef CONDITION_VARIABLE pthread_cond_t; #define PTHREAD_CANCEL_ENABLE 1 #define PTHREAD_CANCEL_DISABLE 0 -static av_unused unsigned __stdcall attribute_align_arg win32thread_worker(void *arg) +#if HAVE_WINRT +#define THREADFUNC_RETTYPE DWORD +#else +#define THREADFUNC_RETTYPE unsigned +#endif + +static av_unused THREADFUNC_RETTYPE +__stdcall attribute_align_arg win32thread_worker(void *arg) { pthread_t *h = (pthread_t*)arg; h->ret = h->func(h->arg); diff --git a/configure b/configure index 3cd3bdfb449..86425130bd5 100755 --- a/configure +++ b/configure @@ -136,13 +136,9 @@ Component options: --disable-w32threads disable Win32 threads [autodetect] --disable-os2threads disable OS/2 threads [autodetect] --disable-network disable network support [no] - --disable-dct disable DCT code --disable-dwt disable DWT code --disable-error-resilience disable error resilience code --disable-lsp disable LSP code - --disable-mdct disable MDCT code - --disable-rdft disable RDFT code - --disable-fft disable FFT code --disable-faan disable floating point AAN (I)DCT code --disable-pixelutils disable pixel utils in libavutil @@ -218,6 +214,7 @@ External library support: --enable-lcms2 enable ICC profile support via LittleCMS 2 [no] --enable-libaom enable AV1 video encoding/decoding via libaom [no] --enable-libaribb24 enable ARIB text and caption decoding via libaribb24 [no] + --enable-libaribcaption enable ARIB text and caption decoding via libaribcaption [no] --enable-libass enable libass subtitles rendering, needed for subtitles and ass filter [no] --enable-libbluray enable BluRay reading using libbluray [no] @@ -230,11 +227,14 @@ External library support: --enable-libdavs2 enable AVS2 decoding via libdavs2 [no] --enable-libdc1394 enable IIDC-1394 grabbing using libdc1394 and libraw1394 [no] + --enable-libdvdnav enable libdvdnav, needed for DVD demuxing [no] + --enable-libdvdread enable libdvdread, needed for DVD demuxing [no] --enable-libfdk-aac enable AAC de/encoding via libfdk-aac [no] --enable-libflite enable flite (voice synthesis) support via libflite [no] --enable-libfontconfig enable libfontconfig, useful for drawtext filter [no] --enable-libfreetype enable libfreetype, needed for drawtext filter [no] --enable-libfribidi enable libfribidi, improves drawtext filter [no] + --enable-libharfbuzz enable libharfbuzz, needed for drawtext filter [no] --enable-libglslang enable GLSL->SPIRV compilation via libglslang [no] --enable-libgme enable Game Music Emu via libgme [no] --enable-libgsm enable GSM de/encoding via libgsm [no] @@ -258,6 +258,8 @@ External library support: --enable-libopus enable Opus de/encoding via libopus [no] --enable-libplacebo enable libplacebo library [no] --enable-libpulse enable Pulseaudio input via libpulse [no] + --enable-libqrencode enable QR encode generation via libqrencode [no] + --enable-libquirc enable QR decoding via libquirc [no] --enable-librabbitmq enable RabbitMQ library [no] --enable-librav1e enable AV1 encoding via rav1e [no] --enable-librist enable RIST via librist [no] @@ -279,6 +281,7 @@ External library support: --enable-libtheora enable Theora encoding via libtheora [no] --enable-libtls enable LibreSSL (via libtls), needed for https support if openssl, gnutls or mbedtls is not used [no] + --enable-libtorch enable Torch as one DNN backend [no] --enable-libtwolame enable MP2 encoding via libtwolame [no] --enable-libuavs3d enable AVS3 decoding via libuavs3d [no] --enable-libv4l2 enable libv4l2/v4l-utils [no] @@ -291,6 +294,8 @@ External library support: --enable-libwebp enable WebP encoding via libwebp [no] --enable-libx264 enable H.264 encoding via x264 [no] --enable-libx265 enable HEVC encoding via x265 [no] + --enable-libxeve enable EVC encoding via libxeve [no] + --enable-libxevd enable EVC decoding via libxevd [no] --enable-libxavs enable AVS encoding via xavs [no] --enable-libxavs2 enable AVS2 encoding via xavs2 [no] --enable-libxcb enable X11 grabbing using XCB [autodetect] @@ -326,7 +331,6 @@ External library support: --disable-securetransport disable Secure Transport, needed for TLS support on OSX if openssl and gnutls are not used [autodetect] --enable-vapoursynth enable VapourSynth demuxer [no] - --disable-vulkan disable Vulkan code [autodetect] --disable-xlib disable xlib [autodetect] --disable-zlib disable zlib [autodetect] @@ -337,9 +341,10 @@ External library support: --disable-cuda-llvm disable CUDA compilation using clang [autodetect] --disable-cuvid disable Nvidia CUVID support [autodetect] --disable-d3d11va disable Microsoft Direct3D 11 video acceleration code [autodetect] + --disable-d3d12va disable Microsoft Direct3D 12 video acceleration code [autodetect] --disable-dxva2 disable Microsoft DirectX 9 video acceleration code [autodetect] --disable-ffnvcodec disable dynamically linked Nvidia code [autodetect] - --enable-libdrm enable DRM code (Linux) [no] + --disable-libdrm disable DRM code (Linux) [autodetect] --enable-libmfx enable Intel MediaSDK (AKA Quick Sync Video) code via libmfx [no] --enable-libvpl enable Intel oneVPL code via libvpl if libmfx is not used [no] --enable-libnpp enable Nvidia Performance Primitives-based code [no] @@ -353,6 +358,7 @@ External library support: --disable-vaapi disable Video Acceleration API (mainly Unix/Intel) code [autodetect] --disable-vdpau disable Nvidia Video Decode and Presentation API for Unix code [autodetect] --disable-videotoolbox disable VideoToolbox code [autodetect] + --disable-vulkan disable Vulkan code [autodetect] Toolchain options: --arch=ARCH select architecture [$arch] @@ -381,7 +387,9 @@ Toolchain options: --windres=WINDRES use windows resource compiler WINDRES [$windres_default] --x86asmexe=EXE use nasm-compatible assembler EXE [$x86asmexe_default] --cc=CC use C compiler CC [$cc_default] + --stdc=STDC use C standard STDC [$stdc_default] --cxx=CXX use C compiler CXX [$cxx_default] + --stdcxx=STDCXX use C standard STDCXX [$stdcxx_default] --objcc=OCC use ObjC compiler OCC [$cc_default] --dep-cc=DEPCC use dependency generator DEPCC [$cc_default] --nvcc=NVCC use Nvidia CUDA compiler NVCC or clang [$nvcc_default] @@ -412,7 +420,7 @@ Toolchain options: --build-suffix=SUFFIX library name suffix [] --enable-pic build position-independent code --enable-thumb compile for Thumb instruction set - --enable-lto use link-time optimization + --enable-lto[=arg] use link-time optimization --env="ENV=override" override the environment variables Advanced options (experts only): @@ -453,6 +461,8 @@ Optimization options (experts only): --disable-armv6t2 disable armv6t2 optimizations --disable-vfp disable VFP optimizations --disable-neon disable NEON optimizations + --disable-dotprod disable DOTPROD optimizations + --disable-i8mm disable I8MM optimizations --disable-inline-asm disable use of inline assembly --disable-x86asm disable use of standalone x86 assembly --disable-mipsdsp disable MIPS DSP ASE R1 optimizations @@ -502,6 +512,7 @@ Developer options (useful when working on FFmpeg itself): --enable-macos-kperf enable macOS kperf (private) API --disable-large-tests disable tests that use a large amount of memory --disable-ptx-compression don't compress CUDA PTX code even when possible + --disable-version-tracking don't include the git/release version in the build NOTE: Object files are built at the place where configure is launched. EOF @@ -1153,6 +1164,43 @@ check_insn(){ check_as ${1}_external "$2" } +check_arch_level(){ + log check_arch_level "$@" + level="$1" + check_as tested_arch_level ".arch $level" + enabled tested_arch_level && as_arch_level="$level" +} + +check_archext_insn(){ + log check_archext_insn "$@" + feature="$1" + instr="$2" + # Check if the assembly is accepted in inline assembly. + check_inline_asm ${feature}_inline "\"$instr\"" + # We don't check if the instruction is supported out of the box by the + # external assembler (we don't try to set ${feature}_external) as we don't + # need to use these instructions in non-runtime detected codepaths. + + disable $feature + + enabled as_arch_directive && arch_directive=".arch $as_arch_level" || arch_directive="" + + # Test if the assembler supports the .arch_extension $feature directive. + arch_extension_directive=".arch_extension $feature" + test_as < +#if !($condition) +#error "unsatisfied condition: $condition" +#endif +EOF +} + +check_cxxflags_cc(){ + log check_cxxflags_cc "$@" + flags=$1 + test_cxxflags_cc "$@" && add_cxxflags $flags +} + check_lib(){ log check_lib "$@" name="$1" @@ -1649,6 +1724,27 @@ int x; EOF } +test_host_cflags_cc(){ + log test_host_cflags_cc "$@" + flags=$1 + header=$2 + condition=$3 + shift 3 + set -- $($host_cflags_filter "$flags") + test_host_cc "$@" < +#if !($condition) +#error "unsatisfied condition: $condition" +#endif +EOF +} + +check_host_cflags_cc(){ + log check_host_cflags_cc "$@" + flags=$1 + test_host_cflags_cc "$@" && add_host_cflags $flags +} + test_host_cpp_condition(){ log test_host_cpp_condition "$@" header=$1 @@ -1763,6 +1859,8 @@ EXTERNAL_LIBRARY_GPL_LIST=" frei0r libcdio libdavs2 + libdvdnav + libdvdread librubberband libvidstab libx264 @@ -1805,6 +1903,7 @@ EXTERNAL_LIBRARY_LIST=" ladspa lcms2 libaom + libaribcaption libass libbluray libbs2b @@ -1813,11 +1912,11 @@ EXTERNAL_LIBRARY_LIST=" libcodec2 libdav1d libdc1394 - libdrm libflite libfontconfig libfreetype libfribidi + libharfbuzz libglslang libgme libgsm @@ -1838,6 +1937,8 @@ EXTERNAL_LIBRARY_LIST=" libopus libplacebo libpulse + libqrencode + libquirc librabbitmq librav1e librist @@ -1855,6 +1956,7 @@ EXTERNAL_LIBRARY_LIST=" libtensorflow libtesseract libtheora + libtorch libtwolame libuavs3d libv4l2 @@ -1862,6 +1964,8 @@ EXTERNAL_LIBRARY_LIST=" libvorbis libvpx libwebp + libxevd + libxeve libxml2 libzimg libzmq @@ -1878,13 +1982,14 @@ EXTERNAL_LIBRARY_LIST=" HWACCEL_AUTODETECT_LIBRARY_LIST=" amf audiotoolbox - crystalhd cuda cuda_llvm cuvid d3d11va + d3d12va dxva2 ffnvcodec + libdrm nvdec nvenc vaapi @@ -1961,17 +2066,13 @@ PROGRAM_LIST=" " SUBSYSTEM_LIST=" - dct dwt error_resilience faan fast_unaligned - fft lsp - mdct pixelutils network - rdft " # COMPONENT_LIST needs to come last to ensure correct dependency checking @@ -2046,7 +2147,6 @@ ARCH_LIST=" sparc64 tilegx tilepro - tomi x86 x86_32 x86_64 @@ -2057,6 +2157,8 @@ ARCH_EXT_LIST_ARM=" armv6 armv6t2 armv8 + dotprod + i8mm neon vfp vfpv3 @@ -2114,12 +2216,12 @@ ARCH_EXT_LIST_PPC=" " ARCH_EXT_LIST_RISCV=" + rv rvv " ARCH_EXT_LIST_X86=" $ARCH_EXT_LIST_X86_SIMD - cpunop i686 " @@ -2191,6 +2293,7 @@ HEADERS_LIST=" opencv2_core_core_c_h OpenGL_gl3_h poll_h + pthread_np_h sys_param_h sys_resource_h sys_select_h @@ -2255,7 +2358,7 @@ SYSTEM_FEATURES=" SYSTEM_FUNCS=" access aligned_malloc - arc4random + arc4random_buf clock_gettime closesocket CommandLineToArgvW @@ -2293,6 +2396,8 @@ SYSTEM_FUNCS=" posix_memalign prctl pthread_cancel + pthread_set_name_np + pthread_setname_np sched_getaffinity SecItemImport SetConsoleTextAttribute @@ -2304,6 +2409,7 @@ SYSTEM_FUNCS=" strerror_r sysconf sysctl + sysctlbyname usleep UTGetOSTypeFromString VirtualAlloc @@ -2314,11 +2420,14 @@ SYSTEM_LIBRARIES=" bcrypt vaapi_drm vaapi_x11 + vaapi_win32 vdpau_x11 " TOOLCHAIN_FEATURES=" as_arch_directive + as_archext_dotprod_directive + as_archext_i8mm_directive as_dn_directive as_fpu_directive as_func @@ -2405,11 +2514,13 @@ HAVE_LIST=" opencl_dxva2 opencl_vaapi_beignet opencl_vaapi_intel_media + opencl_videotoolbox perl pod2man texi2html xmllint zlib_gzip + openvino2 " # options emitted with CONFIG_ prefix but not available on the command line @@ -2427,14 +2538,17 @@ CONFIG_EXTRA=" cbs_av1 cbs_h264 cbs_h265 + cbs_h266 cbs_jpeg cbs_mpeg2 + cbs_vp8 cbs_vp9 deflate_wrapper dirac_parse dnn dovi_rpu dvprofile + evcparse exif faandct faanidct @@ -2457,6 +2571,8 @@ CONFIG_EXTRA=" huffman huffyuvdsp huffyuvencdsp + iamfdec + iamfenc idctdsp iirfilter inflate_wrapper @@ -2466,6 +2582,7 @@ CONFIG_EXTRA=" jpegtables lgplv3 libx262 + libx264_hdr10 llauddsp llviddsp llvidencdsp @@ -2524,10 +2641,10 @@ CMDLINE_SELECT=" debug extra_warnings logging - lto optimizations rpath stripping + version_tracking " PATHS_LIST=" @@ -2585,6 +2702,8 @@ CMDLINE_SET=" random_seed ranlib samples + stdc + stdcxx strip sws_max_filter_size sysinclude @@ -2617,9 +2736,11 @@ armv6t2_deps="arm" armv8_deps="aarch64" neon_deps_any="aarch64 arm" intrinsics_neon_deps="neon" -vfp_deps_any="aarch64 arm" +vfp_deps="arm" vfpv3_deps="vfp" setend_deps="arm" +dotprod_deps="aarch64 neon" +i8mm_deps="aarch64 neon" map 'eval ${v}_inline_deps=inline_asm' $ARCH_EXT_LIST_ARM @@ -2630,7 +2751,8 @@ ppc4xx_deps="ppc" vsx_deps="altivec" power8_deps="vsx" -rvv_deps="riscv" +rv_deps="riscv" +rvv_deps="rv" loongson2_deps="mips" loongson3_deps="mips" @@ -2648,7 +2770,6 @@ mipsdsp_deps="mips" mipsdspr2_deps="mips" msa_deps="mipsfpu" -cpunop_deps="i686" x86_64_select="i686" x86_64_suggest="fast_cmov" @@ -2709,16 +2830,18 @@ threads_if_any="$THREADS_LIST" cbs_av1_select="cbs" cbs_h264_select="cbs" cbs_h265_select="cbs" +cbs_h266_select="cbs" cbs_jpeg_select="cbs" cbs_mpeg2_select="cbs" +cbs_vp8_select="cbs" cbs_vp9_select="cbs" -dct_select="rdft" deflate_wrapper_deps="zlib" dirac_parse_select="golomb" dovi_rpu_select="golomb" -dnn_suggest="libtensorflow libopenvino" +dnn_suggest="libtensorflow libopenvino libtorch" dnn_deps="avformat swscale" error_resilience_select="me_cmp" +evcparse_select="golomb" faandct_deps="faan" faandct_select="fdctdsp" faanidct_deps="faan" @@ -2728,21 +2851,19 @@ h264_sei_select="atsc_a53 golomb" hevcparse_select="golomb" hevc_sei_select="atsc_a53 golomb" frame_thread_encoder_deps="encoders threads" +iamfdec_select="iso_media mpeg4audio" inflate_wrapper_deps="zlib" intrax8_select="blockdsp wmv2dsp" iso_media_select="mpeg4audio" -mdct_select="fft" me_cmp_select="idctdsp" mpeg_er_select="error_resilience" mpegaudio_select="mpegaudiodsp mpegaudioheader" -mpegaudiodsp_select="dct" mpegvideo_select="blockdsp hpeldsp idctdsp videodsp" mpegvideodec_select="h264chroma mpegvideo mpeg_er" mpegvideoenc_select="aandcttables fdctdsp me_cmp mpegvideo pixblockdsp" msmpeg4dec_select="h263_decoder" msmpeg4enc_select="h263_encoder" vc1dsp_select="h264chroma qpeldsp startcode" -rdft_select="fft" # decoders / encoders aac_decoder_select="adts_header mpeg4audio sinewin" @@ -2774,7 +2895,7 @@ asv1_encoder_select="aandcttables bswapdsp fdctdsp pixblockdsp" asv2_decoder_select="blockdsp bswapdsp idctdsp" asv2_encoder_select="aandcttables bswapdsp fdctdsp pixblockdsp" atrac1_decoder_select="sinewin" -av1_decoder_select="av1_frame_split_bsf cbs_av1" +av1_decoder_select="atsc_a53 cbs_av1 dovi_rpu" bink_decoder_select="blockdsp hpeldsp" binkaudio_dct_decoder_select="wma_freqs" binkaudio_rdft_decoder_select="wma_freqs" @@ -2793,6 +2914,7 @@ dvvideo_decoder_select="dvprofile idctdsp" dvvideo_encoder_select="dvprofile fdctdsp me_cmp pixblockdsp" dxa_decoder_deps="zlib" dxv_decoder_select="lzf texturedsp" +dxv_encoder_select="texturedspenc" eac3_decoder_select="ac3_decoder" eac3_encoder_select="ac3_encoder" eamad_decoder_select="aandcttables blockdsp bswapdsp" @@ -2845,6 +2967,7 @@ ipu_decoder_select="mpegvideodec" jpegls_decoder_select="mjpeg_decoder" jv_decoder_select="blockdsp" lagarith_decoder_select="llviddsp" +lead_decoder_select="idctdsp jpegtables" ljpeg_encoder_select="jpegtables" lscr_decoder_select="inflate_wrapper" magicyuv_decoder_select="llviddsp" @@ -2898,6 +3021,7 @@ notchlc_decoder_select="lzf" nuv_decoder_select="idctdsp" opus_decoder_deps="swresample" opus_encoder_select="audio_frame_queue" +pdv_decoder_deps="zlib" png_decoder_select="inflate_wrapper" png_encoder_select="deflate_wrapper llvidencdsp" prores_decoder_select="blockdsp idctdsp" @@ -2912,6 +3036,7 @@ ralf_decoder_select="golomb" rasc_decoder_select="inflate_wrapper" rawvideo_decoder_select="bswapdsp" rscc_decoder_deps="zlib" +rtv1_decoder_select="texturedsp" rv10_decoder_select="h263_decoder" rv10_encoder_select="h263_encoder" rv20_decoder_select="h263_decoder" @@ -2922,7 +3047,7 @@ screenpresso_decoder_deps="zlib" shorten_decoder_select="bswapdsp" sipr_decoder_select="lsp" smvjpeg_decoder_select="mjpeg_decoder" -snow_decoder_select="dwt h264qpel hpeldsp rangecoder videodsp" +snow_decoder_select="dwt h264qpel rangecoder videodsp" snow_encoder_select="dwt h264qpel hpeldsp me_cmp mpegvideoenc rangecoder videodsp" sonic_decoder_select="golomb rangecoder" sonic_encoder_select="golomb rangecoder" @@ -2955,6 +3080,7 @@ utvideo_encoder_select="bswapdsp huffman llvidencdsp" vble_decoder_select="llviddsp" vbn_decoder_select="texturedsp" vbn_encoder_select="texturedspenc" +vmix_decoder_select="idctdsp" vc1_decoder_select="blockdsp h264qpel intrax8 mpegvideodec qpeldsp vc1dsp" vc1image_decoder_select="vc1_decoder" vorbis_encoder_select="audio_frame_queue" @@ -2967,6 +3093,7 @@ vp6f_decoder_select="vp6_decoder" vp7_decoder_select="h264pred videodsp vp8dsp" vp8_decoder_select="h264pred videodsp vp8dsp" vp9_decoder_select="videodsp vp9_parser vp9_superframe_split_bsf" +vvc_decoder_select="cabac cbs_h266 golomb videodsp" wcmv_decoder_select="inflate_wrapper" webp_decoder_select="vp8_decoder exif" wmalossless_decoder_select="llauddsp" @@ -2975,7 +3102,7 @@ wmav1_decoder_select="sinewin wma_freqs" wmav1_encoder_select="sinewin wma_freqs" wmav2_decoder_select="sinewin wma_freqs" wmav2_encoder_select="sinewin wma_freqs" -wmavoice_decoder_select="lsp rdft dct sinewin" +wmavoice_decoder_select="lsp sinewin" wmv1_decoder_select="msmpeg4dec" wmv1_encoder_select="msmpeg4enc" wmv2_decoder_select="blockdsp error_resilience idctdsp intrax8 msmpeg4dec videodsp wmv2dsp" @@ -2992,22 +3119,26 @@ zmbv_decoder_select="inflate_wrapper" zmbv_encoder_select="deflate_wrapper" # hardware accelerators -crystalhd_deps="libcrystalhd_libcrystalhd_if_h" cuda_deps="ffnvcodec" cuvid_deps="ffnvcodec" d3d11va_deps="dxva_h ID3D11VideoDecoder ID3D11VideoContext" +d3d12va_deps="dxva_h ID3D12Device ID3D12VideoDecoder" dxva2_deps="dxva2api_h DXVA2_ConfigPictureDecode ole32 user32" ffnvcodec_deps_any="libdl LoadLibrary" +mediacodec_deps="android" nvdec_deps="ffnvcodec" vaapi_x11_deps="xlib_x11" videotoolbox_hwaccel_deps="videotoolbox pthreads" videotoolbox_hwaccel_extralibs="-framework QuartzCore" +vulkan_deps="threads" vulkan_deps_any="libdl LoadLibrary" av1_d3d11va_hwaccel_deps="d3d11va DXVA_PicParams_AV1" av1_d3d11va_hwaccel_select="av1_decoder" av1_d3d11va2_hwaccel_deps="d3d11va DXVA_PicParams_AV1" av1_d3d11va2_hwaccel_select="av1_decoder" +av1_d3d12va_hwaccel_deps="d3d12va DXVA_PicParams_AV1" +av1_d3d12va_hwaccel_select="av1_decoder" av1_dxva2_hwaccel_deps="dxva2 DXVA_PicParams_AV1" av1_dxva2_hwaccel_select="av1_decoder" av1_nvdec_hwaccel_deps="nvdec CUVIDAV1PICPARAMS" @@ -3016,6 +3147,8 @@ av1_vaapi_hwaccel_deps="vaapi VADecPictureParameterBufferAV1_bit_depth_idx" av1_vaapi_hwaccel_select="av1_decoder" av1_vdpau_hwaccel_deps="vdpau VdpPictureInfoAV1" av1_vdpau_hwaccel_select="av1_decoder" +av1_vulkan_hwaccel_deps="vulkan" +av1_vulkan_hwaccel_select="av1_decoder" h263_vaapi_hwaccel_deps="vaapi" h263_vaapi_hwaccel_select="h263_decoder" h263_videotoolbox_hwaccel_deps="videotoolbox" @@ -3024,6 +3157,8 @@ h264_d3d11va_hwaccel_deps="d3d11va" h264_d3d11va_hwaccel_select="h264_decoder" h264_d3d11va2_hwaccel_deps="d3d11va" h264_d3d11va2_hwaccel_select="h264_decoder" +h264_d3d12va_hwaccel_deps="d3d12va" +h264_d3d12va_hwaccel_select="h264_decoder" h264_dxva2_hwaccel_deps="dxva2" h264_dxva2_hwaccel_select="h264_decoder" h264_nvdec_hwaccel_deps="nvdec" @@ -3034,10 +3169,14 @@ h264_vdpau_hwaccel_deps="vdpau" h264_vdpau_hwaccel_select="h264_decoder" h264_videotoolbox_hwaccel_deps="videotoolbox" h264_videotoolbox_hwaccel_select="h264_decoder" +h264_vulkan_hwaccel_deps="vulkan" +h264_vulkan_hwaccel_select="h264_decoder" hevc_d3d11va_hwaccel_deps="d3d11va DXVA_PicParams_HEVC" hevc_d3d11va_hwaccel_select="hevc_decoder" hevc_d3d11va2_hwaccel_deps="d3d11va DXVA_PicParams_HEVC" hevc_d3d11va2_hwaccel_select="hevc_decoder" +hevc_d3d12va_hwaccel_deps="d3d12va DXVA_PicParams_HEVC" +hevc_d3d12va_hwaccel_select="hevc_decoder" hevc_dxva2_hwaccel_deps="dxva2 DXVA_PicParams_HEVC" hevc_dxva2_hwaccel_select="hevc_decoder" hevc_nvdec_hwaccel_deps="nvdec" @@ -3048,6 +3187,8 @@ hevc_vdpau_hwaccel_deps="vdpau VdpPictureInfoHEVC" hevc_vdpau_hwaccel_select="hevc_decoder" hevc_videotoolbox_hwaccel_deps="videotoolbox" hevc_videotoolbox_hwaccel_select="hevc_decoder" +hevc_vulkan_hwaccel_deps="vulkan" +hevc_vulkan_hwaccel_select="hevc_decoder" mjpeg_nvdec_hwaccel_deps="nvdec" mjpeg_nvdec_hwaccel_select="mjpeg_decoder" mjpeg_vaapi_hwaccel_deps="vaapi" @@ -3062,6 +3203,8 @@ mpeg2_d3d11va_hwaccel_deps="d3d11va" mpeg2_d3d11va_hwaccel_select="mpeg2video_decoder" mpeg2_d3d11va2_hwaccel_deps="d3d11va" mpeg2_d3d11va2_hwaccel_select="mpeg2video_decoder" +mpeg2_d3d12va_hwaccel_deps="d3d12va" +mpeg2_d3d12va_hwaccel_select="mpeg2video_decoder" mpeg2_dxva2_hwaccel_deps="dxva2" mpeg2_dxva2_hwaccel_select="mpeg2video_decoder" mpeg2_nvdec_hwaccel_deps="nvdec" @@ -3086,6 +3229,8 @@ vc1_d3d11va_hwaccel_deps="d3d11va" vc1_d3d11va_hwaccel_select="vc1_decoder" vc1_d3d11va2_hwaccel_deps="d3d11va" vc1_d3d11va2_hwaccel_select="vc1_decoder" +vc1_d3d12va_hwaccel_deps="d3d12va" +vc1_d3d12va_hwaccel_select="vc1_decoder" vc1_dxva2_hwaccel_deps="dxva2" vc1_dxva2_hwaccel_select="vc1_decoder" vc1_nvdec_hwaccel_deps="nvdec" @@ -3102,6 +3247,8 @@ vp9_d3d11va_hwaccel_deps="d3d11va DXVA_PicParams_VP9" vp9_d3d11va_hwaccel_select="vp9_decoder" vp9_d3d11va2_hwaccel_deps="d3d11va DXVA_PicParams_VP9" vp9_d3d11va2_hwaccel_select="vp9_decoder" +vp9_d3d12va_hwaccel_deps="d3d12va DXVA_PicParams_VP9" +vp9_d3d12va_hwaccel_select="vp9_decoder" vp9_dxva2_hwaccel_deps="dxva2 DXVA_PicParams_VP9" vp9_dxva2_hwaccel_select="vp9_decoder" vp9_nvdec_hwaccel_deps="nvdec" @@ -3114,6 +3261,7 @@ vp9_videotoolbox_hwaccel_deps="videotoolbox" vp9_videotoolbox_hwaccel_select="vp9_decoder" wmv3_d3d11va_hwaccel_select="vc1_d3d11va_hwaccel" wmv3_d3d11va2_hwaccel_select="vc1_d3d11va2_hwaccel" +wmv3_d3d12va_hwaccel_select="vc1_d3d12va_hwaccel" wmv3_dxva2_hwaccel_select="vc1_dxva2_hwaccel" wmv3_nvdec_hwaccel_select="vc1_nvdec_hwaccel" wmv3_vaapi_hwaccel_select="vc1_vaapi_hwaccel" @@ -3158,20 +3306,17 @@ aac_mf_encoder_deps="mediafoundation" ac3_mf_encoder_deps="mediafoundation" av1_cuvid_decoder_deps="cuvid CUVIDAV1PICPARAMS" av1_mediacodec_decoder_deps="mediacodec" -av1_mediacodec_decoder_extralibs="-landroid" +av1_mediacodec_encoder_deps="mediacodec" av1_nvenc_encoder_deps="nvenc NV_ENC_PIC_PARAMS_AV1" av1_nvenc_encoder_select="atsc_a53" h263_v4l2m2m_decoder_deps="v4l2_m2m h263_v4l2_m2m" h263_v4l2m2m_encoder_deps="v4l2_m2m h263_v4l2_m2m" h264_amf_encoder_deps="amf" -h264_crystalhd_decoder_select="crystalhd h264_mp4toannexb_bsf h264_parser" h264_cuvid_decoder_deps="cuvid" h264_cuvid_decoder_select="h264_mp4toannexb_bsf" h264_mediacodec_decoder_deps="mediacodec" -h264_mediacodec_decoder_extralibs="-landroid" h264_mediacodec_decoder_select="h264_mp4toannexb_bsf h264_parser" h264_mediacodec_encoder_deps="mediacodec" -h264_mediacodec_encoder_extralibs="-landroid" h264_mediacodec_encoder_select="h264_metadata" h264_mf_encoder_deps="mediafoundation" h264_mmal_decoder_deps="mmal" @@ -3190,10 +3335,8 @@ hevc_amf_encoder_deps="amf" hevc_cuvid_decoder_deps="cuvid" hevc_cuvid_decoder_select="hevc_mp4toannexb_bsf" hevc_mediacodec_decoder_deps="mediacodec" -hevc_mediacodec_decoder_extralibs="-landroid" hevc_mediacodec_decoder_select="hevc_mp4toannexb_bsf hevc_parser" hevc_mediacodec_encoder_deps="mediacodec" -hevc_mediacodec_encoder_extralibs="-landroid" hevc_mediacodec_encoder_select="hevc_metadata" hevc_mf_encoder_deps="mediafoundation" hevc_nvenc_encoder_deps="nvenc" @@ -3216,7 +3359,6 @@ mjpeg_vaapi_encoder_select="cbs_jpeg jpegtables vaapi_encode" mp3_mf_encoder_deps="mediafoundation" mpeg1_cuvid_decoder_deps="cuvid" mpeg1_v4l2m2m_decoder_deps="v4l2_m2m mpeg1_v4l2_m2m" -mpeg2_crystalhd_decoder_select="crystalhd" mpeg2_cuvid_decoder_deps="cuvid" mpeg2_mmal_decoder_deps="mmal" mpeg2_mediacodec_decoder_deps="mediacodec" @@ -3224,21 +3366,20 @@ mpeg2_qsv_decoder_select="qsvdec" mpeg2_qsv_encoder_select="qsvenc" mpeg2_vaapi_encoder_select="cbs_mpeg2 vaapi_encode" mpeg2_v4l2m2m_decoder_deps="v4l2_m2m mpeg2_v4l2_m2m" -mpeg4_crystalhd_decoder_select="crystalhd" mpeg4_cuvid_decoder_deps="cuvid" mpeg4_mediacodec_decoder_deps="mediacodec" +mpeg4_mediacodec_encoder_deps="mediacodec" mpeg4_mmal_decoder_deps="mmal" mpeg4_omx_encoder_deps="omx" mpeg4_v4l2m2m_decoder_deps="v4l2_m2m mpeg4_v4l2_m2m" mpeg4_v4l2m2m_encoder_deps="v4l2_m2m mpeg4_v4l2_m2m" -msmpeg4_crystalhd_decoder_select="crystalhd" -vc1_crystalhd_decoder_select="crystalhd" vc1_cuvid_decoder_deps="cuvid" vc1_mmal_decoder_deps="mmal" vc1_qsv_decoder_select="qsvdec" vc1_v4l2m2m_decoder_deps="v4l2_m2m vc1_v4l2_m2m" vp8_cuvid_decoder_deps="cuvid" vp8_mediacodec_decoder_deps="mediacodec" +vp8_mediacodec_encoder_deps="mediacodec" vp8_qsv_decoder_select="qsvdec" vp8_rkmpp_decoder_deps="rkmpp" vp8_vaapi_encoder_deps="VAEncPictureParameterBufferVP8" @@ -3247,6 +3388,7 @@ vp8_v4l2m2m_decoder_deps="v4l2_m2m vp8_v4l2_m2m" vp8_v4l2m2m_encoder_deps="v4l2_m2m vp8_v4l2_m2m" vp9_cuvid_decoder_deps="cuvid" vp9_mediacodec_decoder_deps="mediacodec" +vp9_mediacodec_encoder_deps="mediacodec" vp9_qsv_decoder_select="qsvdec" vp9_rkmpp_decoder_deps="rkmpp" vp9_vaapi_encoder_deps="VAEncPictureParameterBufferVP9" @@ -3254,20 +3396,23 @@ vp9_vaapi_encoder_select="vaapi_encode" vp9_qsv_encoder_deps="libmfx MFX_CODEC_VP9" vp9_qsv_encoder_select="qsvenc" vp9_v4l2m2m_decoder_deps="v4l2_m2m vp9_v4l2_m2m" -wmv3_crystalhd_decoder_select="crystalhd" av1_qsv_decoder_select="qsvdec" av1_qsv_encoder_select="qsvenc" av1_qsv_encoder_deps="libvpl" av1_amf_encoder_deps="amf" +av1_vaapi_encoder_deps="VAEncPictureParameterBufferAV1" +av1_vaapi_encoder_select="cbs_av1 vaapi_encode" # parsers aac_parser_select="adts_header mpeg4audio" av1_parser_select="cbs_av1" +evc_parser_select="evcparse" h264_parser_select="golomb h264dsp h264parse h264_sei" hevc_parser_select="hevcparse hevc_sei" mpegaudio_parser_select="mpegaudioheader" mpeg4video_parser_select="h263dsp mpegvideodec qpeldsp" vc1_parser_select="vc1dsp" +vvc_parser_select="cbs_h266" # bitstream_filters aac_adtstoasc_bsf_select="adts_header mpeg4audio" @@ -3276,6 +3421,7 @@ av1_frame_split_bsf_select="cbs_av1" av1_metadata_bsf_select="cbs_av1" dts2pts_bsf_select="cbs_h264 h264parse" eac3_core_bsf_select="ac3_parser" +evc_frame_merge_bsf_select="evcparse" filter_units_bsf_select="cbs" h264_metadata_bsf_deps="const_nan" h264_metadata_bsf_select="cbs_h264" @@ -3283,8 +3429,9 @@ h264_redundant_pps_bsf_select="cbs_h264" hevc_metadata_bsf_select="cbs_h265" mjpeg2jpeg_bsf_select="jpegtables" mpeg2_metadata_bsf_select="cbs_mpeg2" -trace_headers_bsf_select="cbs" +trace_headers_bsf_select="cbs cbs_vp8" vp9_metadata_bsf_select="cbs_vp9" +vvc_metadata_bsf_select="cbs_h266" # external libraries aac_at_decoder_deps="audiotoolbox" @@ -3332,11 +3479,12 @@ libaom_av1_decoder_deps="libaom" libaom_av1_encoder_deps="libaom" libaom_av1_encoder_select="extract_extradata_bsf" libaribb24_decoder_deps="libaribb24" +libaribcaption_decoder_deps="libaribcaption" libcelt_decoder_deps="libcelt" libcodec2_decoder_deps="libcodec2" libcodec2_encoder_deps="libcodec2" libdav1d_decoder_deps="libdav1d" -libdav1d_decoder_select="atsc_a53" +libdav1d_decoder_select="atsc_a53 dovi_rpu" libdavs2_decoder_deps="libdavs2" libdavs2_decoder_select="avs2_parser" libfdk_aac_decoder_deps="libfdk_aac" @@ -3362,7 +3510,6 @@ libopencore_amrwb_decoder_deps="libopencore_amrwb" libopenh264_decoder_deps="libopenh264" libopenh264_decoder_select="h264_mp4toannexb_bsf" libopenh264_encoder_deps="libopenh264" -libopenjpeg_decoder_deps="libopenjpeg" libopenjpeg_encoder_deps="libopenjpeg" libopenmpt_demuxer_deps="libopenmpt" libopus_decoder_deps="libopus" @@ -3391,13 +3538,15 @@ libwebp_encoder_deps="libwebp" libwebp_anim_encoder_deps="libwebp" libx262_encoder_deps="libx262" libx264_encoder_deps="libx264" -libx264_encoder_select="atsc_a53" +libx264_encoder_select="atsc_a53 golomb" libx264rgb_encoder_deps="libx264" libx264rgb_encoder_select="libx264_encoder" libx265_encoder_deps="libx265" libx265_encoder_select="atsc_a53" libxavs_encoder_deps="libxavs" libxavs2_encoder_deps="libxavs2" +libxevd_decoder_deps="libxevd" +libxeve_encoder_deps="libxeve" libxvid_encoder_deps="libxvid" libzvbi_teletext_decoder_deps="libzvbi" vapoursynth_demuxer_deps="vapoursynth" @@ -3423,22 +3572,27 @@ caf_demuxer_select="iso_media" caf_muxer_select="iso_media" dash_muxer_select="mp4_muxer" dash_demuxer_deps="libxml2" +daud_muxer_select="pcm_rechunk_bsf" dirac_demuxer_select="dirac_parser" dts_demuxer_select="dca_parser" dtshd_demuxer_select="dca_parser" dv_demuxer_select="dvprofile" dv_muxer_select="dvprofile" +dvdvideo_demuxer_select="mpegps_demuxer" +dvdvideo_demuxer_deps="libdvdnav libdvdread" dxa_demuxer_select="riffdec" eac3_demuxer_select="ac3_parser" +evc_demuxer_select="evc_frame_merge_bsf evc_parser" f4v_muxer_select="mov_muxer" fifo_muxer_deps="threads" flac_demuxer_select="flac_parser" flv_muxer_select="aac_adtstoasc_bsf" gxf_muxer_select="pcm_rechunk_bsf" hds_muxer_select="flv_muxer" -hls_demuxer_select="adts_header ac3_parser mov_demuxer mpegts_demuxer" +hls_demuxer_select="aac_demuxer ac3_demuxer adts_header ac3_parser eac3_demuxer mov_demuxer mpegts_demuxer" hls_muxer_select="mov_muxer mpegts_muxer" -hls_muxer_suggest="gcrypt openssl" +iamf_demuxer_select="iamfdec" +iamf_muxer_select="iamfenc" image2_alias_pix_demuxer_select="image2_demuxer" image2_brender_pix_demuxer_select="image2_demuxer" imf_demuxer_deps="libxml2" @@ -3453,16 +3607,16 @@ matroska_demuxer_suggest="bzlib zlib" matroska_muxer_select="mpeg4audio riffenc aac_adtstoasc_bsf pgs_frame_merge_bsf vp9_superframe_bsf" mlp_demuxer_select="mlp_parser" mmf_muxer_select="riffenc" -mov_demuxer_select="iso_media riffdec" +mov_demuxer_select="iso_media riffdec iamfdec" mov_demuxer_suggest="zlib" -mov_muxer_select="iso_media riffenc rtpenc_chain vp9_superframe_bsf aac_adtstoasc_bsf ac3_parser" +mov_muxer_select="iso_media riffenc rtpenc_chain vp9_superframe_bsf aac_adtstoasc_bsf ac3_parser iamfenc" mp3_demuxer_select="mpegaudio_parser" mp3_muxer_select="mpegaudioheader" mp4_muxer_select="mov_muxer" mpegts_demuxer_select="iso_media" -mpegts_muxer_select="ac3_parser adts_muxer latm_muxer h264_mp4toannexb_bsf hevc_mp4toannexb_bsf" +mpegts_muxer_select="ac3_parser adts_muxer latm_muxer h264_mp4toannexb_bsf hevc_mp4toannexb_bsf vvc_mp4toannexb_bsf" mpegtsraw_demuxer_select="mpegts_demuxer" -mxf_muxer_select="pcm_rechunk_bsf" +mxf_muxer_select="pcm_rechunk_bsf rangecoder" mxf_d10_muxer_select="mxf_muxer" mxf_opatom_muxer_select="mxf_muxer" nut_muxer_select="riffenc" @@ -3505,7 +3659,6 @@ xwma_demuxer_select="riffdec" # indevs / outdevs android_camera_indev_deps="android camera2ndk mediandk pthreads" -android_camera_indev_extralibs="-landroid -lcamera2ndk -lmediandk" alsa_indev_deps="alsa" alsa_outdev_deps="alsa" avfoundation_indev_deps="avfoundation corevideo coremedia pthreads" @@ -3557,6 +3710,8 @@ xcbgrab_indev_suggest="libxcb_shm libxcb_shape libxcb_xfixes" xv_outdev_deps="xlib_xv xlib_x11 xlib_xext" # protocols +android_content_protocol_deps="jni" +android_content_protocol_select="file_protocol" async_protocol_deps="threads" bluray_protocol_deps="libbluray" ffrtmpcrypt_protocol_conflict="librtmp_protocol" @@ -3638,7 +3793,11 @@ blend_vulkan_filter_deps="vulkan spirv_compiler" boxblur_filter_deps="gpl" boxblur_opencl_filter_deps="opencl gpl" bs2b_filter_deps="libbs2b" +bwdif_cuda_filter_deps="ffnvcodec" +bwdif_cuda_filter_deps_any="cuda_nvcc cuda_llvm" +bwdif_vulkan_filter_deps="vulkan spirv_compiler" chromaber_vulkan_filter_deps="vulkan spirv_compiler" +color_vulkan_filter_deps="vulkan spirv_compiler" colorkey_opencl_filter_deps="opencl" colormatrix_filter_deps="gpl" convolution_opencl_filter_deps="opencl" @@ -3660,7 +3819,7 @@ dilation_opencl_filter_deps="opencl" dnn_classify_filter_select="dnn" dnn_detect_filter_select="dnn" dnn_processing_filter_select="dnn" -drawtext_filter_deps="libfreetype" +drawtext_filter_deps="libfreetype libharfbuzz" drawtext_filter_suggest="libfontconfig libfribidi" elbg_filter_deps="avcodec" eq_filter_deps="gpl" @@ -3674,6 +3833,7 @@ frei0r_deps_any="libdl LoadLibrary" frei0r_filter_deps="frei0r" frei0r_src_filter_deps="frei0r" fspp_filter_deps="gpl" +fsync_filter_deps="avformat" gblur_vulkan_filter_deps="vulkan spirv_compiler" hflip_vulkan_filter_deps="vulkan spirv_compiler" histeq_filter_deps="gpl" @@ -3695,10 +3855,14 @@ minterpolate_filter_select="scene_sad" mptestsrc_filter_deps="gpl" negate_filter_deps="lut_filter" nlmeans_opencl_filter_deps="opencl" +nlmeans_vulkan_filter_deps="vulkan spirv_compiler" nnedi_filter_deps="gpl" ocr_filter_deps="libtesseract" ocv_filter_deps="libopencv" openclsrc_filter_deps="opencl" +qrencode_filter_deps="libqrencode" +qrencodesrc_filter_deps="libqrencode" +quirc_filter_deps="libquirc" overlay_opencl_filter_deps="opencl" overlay_qsv_filter_deps="libmfx" overlay_qsv_filter_select="qsvvpp" @@ -3735,7 +3899,7 @@ smartblur_filter_deps="gpl swscale" sobel_opencl_filter_deps="opencl" sofalizer_filter_deps="libmysofa" spp_filter_deps="gpl avcodec" -spp_filter_select="fft idctdsp fdctdsp me_cmp pixblockdsp" +spp_filter_select="idctdsp fdctdsp me_cmp pixblockdsp" sr_filter_deps="avformat swscale" sr_filter_select="dnn" stereo3d_filter_deps="gpl" @@ -3750,6 +3914,7 @@ tonemap_vaapi_filter_deps="vaapi VAProcFilterParameterBufferHDRToneMapping" tonemap_opencl_filter_deps="opencl const_nan" transpose_opencl_filter_deps="opencl" transpose_vaapi_filter_deps="vaapi VAProcPipelineCaps_rotation_flags" +transpose_vt_filter_deps="videotoolbox VTPixelRotationSessionCreate" transpose_vulkan_filter_deps="vulkan spirv_compiler" unsharp_opencl_filter_deps="opencl" uspp_filter_deps="gpl avcodec" @@ -3758,14 +3923,17 @@ vflip_vulkan_filter_deps="vulkan spirv_compiler" vidstabdetect_filter_deps="libvidstab" vidstabtransform_filter_deps="libvidstab" libvmaf_filter_deps="libvmaf" +libvmaf_cuda_filter_deps="libvmaf libvmaf_cuda ffnvcodec" zmq_filter_deps="libzmq" zoompan_filter_deps="swscale" zscale_filter_deps="libzimg const_nan" scale_vaapi_filter_deps="vaapi" +scale_vt_filter_deps="videotoolbox VTPixelTransferSessionCreate" scale_vulkan_filter_deps="vulkan spirv_compiler" vpp_qsv_filter_deps="libmfx" vpp_qsv_filter_select="qsvvpp" xfade_opencl_filter_deps="opencl" +xfade_vulkan_filter_deps="vulkan spirv_compiler" yadif_cuda_filter_deps="ffnvcodec" yadif_cuda_filter_deps_any="cuda_nvcc cuda_llvm" yadif_videotoolbox_filter_deps="metal corevideo videotoolbox" @@ -3818,7 +3986,7 @@ avfilter_deps="avutil" avfilter_suggest="libm stdatomic" avformat_deps="avcodec avutil" avformat_suggest="libm network zlib stdatomic" -avutil_suggest="clock_gettime ffnvcodec libm libdrm libmfx opencl user32 vaapi vulkan videotoolbox corefoundation corevideo coremedia bcrypt stdatomic" +avutil_suggest="clock_gettime ffnvcodec gcrypt libm libdrm libmfx opencl openssl user32 vaapi vulkan videotoolbox corefoundation corevideo coremedia bcrypt stdatomic" postproc_deps="avutil gpl" postproc_suggest="libm stdatomic" swresample_deps="avutil" @@ -3828,7 +3996,7 @@ swscale_suggest="libm stdatomic" avcodec_extralibs="pthreads_extralibs iconv_extralibs dxva2_extralibs lcms2_extralibs" avfilter_extralibs="pthreads_extralibs" -avutil_extralibs="d3d11va_extralibs nanosleep_extralibs pthreads_extralibs vaapi_drm_extralibs vaapi_x11_extralibs vdpau_x11_extralibs" +avutil_extralibs="d3d11va_extralibs d3d12va_extralibs mediacodec_extralibs nanosleep_extralibs pthreads_extralibs vaapi_drm_extralibs vaapi_x11_extralibs vaapi_win32_extralibs vdpau_x11_extralibs" # programs ffmpeg_deps="avcodec avfilter avformat threads" @@ -3836,9 +4004,9 @@ ffmpeg_select="aformat_filter anull_filter atrim_filter format_filter hflip_filter null_filter transpose_filter trim_filter vflip_filter" ffmpeg_suggest="ole32 psapi shell32" -ffplay_deps="avcodec avformat swscale swresample sdl2" -ffplay_select="rdft crop_filter transpose_filter hflip_filter vflip_filter rotate_filter" -ffplay_suggest="shell32" +ffplay_deps="avcodec avformat avfilter swscale swresample sdl2" +ffplay_select="crop_filter transpose_filter hflip_filter vflip_filter rotate_filter" +ffplay_suggest="shell32 libplacebo vulkan" ffprobe_deps="avcodec avformat" ffprobe_suggest="shell32" @@ -3866,6 +4034,8 @@ mandir_default='${prefix}/share/man' # toolchain ar_default="ar" cc_default="gcc" +stdc_default="c17" +stdcxx_default="c++11" cxx_default="g++" host_cc_default="gcc" doxygen_default="doxygen" @@ -3904,6 +4074,7 @@ enable $DOCUMENT_LIST enable $EXAMPLE_LIST enable $LIBRARY_LIST enable stripping +enable version_tracking enable asm enable debug @@ -4017,9 +4188,9 @@ find_filters_extern(){ FILTER_LIST=$(find_filters_extern libavfilter/allfilters.c) OUTDEV_LIST=$(find_things_extern muxer FFOutputFormat libavdevice/alldevices.c outdev) -INDEV_LIST=$(find_things_extern demuxer AVInputFormat libavdevice/alldevices.c indev) +INDEV_LIST=$(find_things_extern demuxer FFInputFormat libavdevice/alldevices.c indev) MUXER_LIST=$(find_things_extern muxer FFOutputFormat libavformat/allformats.c) -DEMUXER_LIST=$(find_things_extern demuxer AVInputFormat libavformat/allformats.c) +DEMUXER_LIST=$(find_things_extern demuxer FFInputFormat libavformat/allformats.c) ENCODER_LIST=$(find_things_extern encoder FFCodec libavcodec/allcodecs.c) DECODER_LIST=$(find_things_extern decoder FFCodec libavcodec/allcodecs.c) CODEC_LIST=" @@ -4028,7 +4199,7 @@ CODEC_LIST=" " PARSER_LIST=$(find_things_extern parser AVCodecParser libavcodec/parsers.c) BSF_LIST=$(find_things_extern bsf FFBitStreamFilter libavcodec/bitstream_filters.c) -HWACCEL_LIST=$(find_things_extern hwaccel AVHWAccel libavcodec/hwaccels.h) +HWACCEL_LIST=$(find_things_extern hwaccel FFHWAccel libavcodec/hwaccels.h) PROTOCOL_LIST=$(find_things_extern protocol URLProtocol libavformat/protocols.c) AVCODEC_COMPONENTS_LIST=" @@ -4170,6 +4341,9 @@ for opt do --enable-sdl) enable sdl2 ;; + --enable-lto*) + lto=-f${opt#--enable-} + ;; --enable-*=*|--disable-*=*) eval $(echo "${opt%%=*}" | sed 's/--/action=/;s/-/ thing=/') is_in "${thing}s" $COMPONENT_LIST || die_unknown "$opt" @@ -4469,7 +4643,7 @@ if enabled cuda_nvcc; then fi set_default arch cc cxx doxygen pkg_config ranlib strip sysinclude \ - target_exec x86asmexe metalcc metallib + target_exec x86asmexe metalcc metallib stdc stdcxx enabled cross_compile || host_cc_default=$cc set_default host_cc @@ -4594,7 +4768,7 @@ msvc_common_flags(){ # generic catch all at the bottom will print the original flag. -Wall) ;; -Wextra) ;; - -std=c*) ;; + -std=c*) echo /std:${flag#-std=};; # Common flags -fomit-frame-pointer) ;; -g) echo -Z7 ;; @@ -4604,6 +4778,7 @@ msvc_common_flags(){ -fPIC) ;; -mthumb) ;; -march=*) ;; + -mfp16-format=*) ;; -lz) echo zlib.lib ;; -lx264) echo libx264.lib ;; -lstdc++) ;; @@ -4638,8 +4813,8 @@ icl_flags(){ # Despite what Intel's documentation says -Wall, which is supported # on Windows, does enable remarks so disable them here. -Wall) echo $flag -Qdiag-disable:remark ;; - -std=c99) echo -Qstd=c99 ;; - -flto) echo -ipo ;; + -std=$stdc) echo -Qstd=$stdc ;; + -flto*) echo -ipo ;; esac done } @@ -4647,7 +4822,7 @@ icl_flags(){ icc_flags(){ for flag; do case $flag in - -flto) echo -ipo ;; + -flto*) echo -ipo ;; *) echo $flag ;; esac done @@ -4686,7 +4861,7 @@ suncc_flags(){ athlon*) echo -xarch=pentium_proa ;; esac ;; - -std=c99) echo -xc99 ;; + -std=$stdc) echo -x$stdc ;; -fomit-frame-pointer) echo -xregs=frameptr ;; -fPIC) echo -KPIC -xcode=pic32 ;; -W*,*) echo $flag ;; @@ -4775,8 +4950,8 @@ probe_cc(){ _type=suncc _ident=$($_cc -V 2>&1 | head -n1 | cut -d' ' -f 2-) _DEPCMD='$(DEP$(1)) $(DEP$(1)FLAGS) $($(1)DEP_FLAGS) $< | sed -e "1s,^.*: ,$@: ," -e "\$$!s,\$$, \\\," -e "1!s,^.*: , ," > $(@:.o=.d)' - _DEPFLAGS='-xM1 -xc99' - _ldflags='-std=c99' + _DEPFLAGS='-xM1 -x$stdc' + _ldflags='-std=$stdc' _cflags_speed='-O5' _cflags_size='-O5 -xspace' _flags_filter=suncc_flags @@ -5342,6 +5517,9 @@ elif enabled riscv; then if test_cpp_condition stddef.h "__riscv_zbb"; then enable fast_clz fi + if test_cpp_condition stddef.h "__riscv_zfhmin"; then + enable fast_float16 + fi elif enabled sparc; then @@ -5367,20 +5545,18 @@ elif enabled x86; then cpuflags="-march=$cpu" disable i686 ;; - # targets that do support nopl and conditional mov (cmov) - i686|pentiumpro|pentium[23]|pentium-m|athlon|athlon-tbird|athlon-4|athlon-[mx]p|athlon64*|k8*|opteron*|athlon-fx\ - |core*|atom|bonnell|nehalem|westmere|silvermont|sandybridge|ivybridge|haswell|broadwell|skylake*|knl\ - |amdfam10|barcelona|b[dt]ver*|znver*) - cpuflags="-march=$cpu" - enable i686 - enable fast_cmov - ;; # targets that do support conditional mov but on which it's slow pentium4|pentium4m|prescott|nocona) cpuflags="-march=$cpu" enable i686 disable fast_cmov ;; + # everything else should support nopl and conditional mov (cmov) + *) + cpuflags="-march=$cpu" + enable i686 + enable fast_cmov + ;; esac fi @@ -5404,21 +5580,36 @@ if test "$?" != 0; then die "C compiler test failed." fi -add_cppflags -D_ISOC99_SOURCE +add_cppflags -D_ISOC11_SOURCE add_cxxflags -D__STDC_CONSTANT_MACROS -check_cxxflags -std=c++11 || check_cxxflags -std=c++0x +check_cxxflags_cc -std=$stdcxx ctype.h "__cplusplus >= 201103L" || + { check_cxxflags -std=c++11 && stdcxx="c++11" || { check_cxxflags -std=c++0x && stdcxx="c++0x"; }; } # some compilers silently accept -std=c11, so we also need to check that the # version macro is defined properly -test_cflags_cc -std=c11 ctype.h "__STDC_VERSION__ >= 201112L" && - add_cflags -std=c11 || - check_cflags -std=c99 +check_cflags_cc -std=$stdc ctype.h "__STDC_VERSION__ >= 201112L" || + { check_cflags_cc -std=c11 ctype.h "__STDC_VERSION__ >= 201112L" && stdc="c11" || die "Compiler lacks C11 support"; } + +test_cc < +#include +struct Foo { + int a; + void *ptr; +} obj; +static_assert(offsetof(struct Foo, a) == 0, + "First element of struct does not have offset 0"); +_Static_assert(offsetof(struct Foo, ptr) >= offsetof(struct Foo, a) + sizeof(obj.a), + "elements not properly ordered in struct"); +EOF check_cppflags -D_FILE_OFFSET_BITS=64 check_cppflags -D_LARGEFILE_SOURCE -add_host_cppflags -D_ISOC99_SOURCE -check_host_cflags -std=c99 +add_host_cppflags -D_ISOC11_SOURCE +check_host_cflags_cc -std=$stdc ctype.h "__STDC_VERSION__ >= 201112L" || + check_host_cflags_cc -std=c11 ctype.h "__STDC_VERSION__ >= 201112L" || die "Host compiler lacks C11 support" + check_host_cflags -Wall check_host_cflags $host_cflags_speed @@ -5519,11 +5710,12 @@ case $target_os in ;; netbsd) disable symver + enable section_data_rel_ro oss_indev_extralibs="-lossaudio" oss_outdev_extralibs="-lossaudio" enabled gcc || check_ldflags -Wl,-zmuldefs ;; - openbsd|bitrig) + openbsd) disable symver enable section_data_rel_ro striptype="" @@ -5537,6 +5729,7 @@ case $target_os in disable symver ;; freebsd) + enable section_data_rel_ro ;; bsd/os) add_extralibs -lpoll -lgnugetopt @@ -5589,6 +5782,7 @@ case $target_os in fi ! enabled small && test_cmd $windres --version && enable gnu_windres enabled x86_32 && check_ldflags -Wl,--large-address-aware + add_cppflags -DWIN32_LEAN_AND_MEAN shlibdir_default="$bindir_default" SLIBPREF="" SLIBSUF=".dll" @@ -5639,6 +5833,7 @@ case $target_os in fi ! enabled small && test_cmd $windres --version && enable gnu_windres enabled x86_32 && check_ldflags -LARGEADDRESSAWARE + add_cppflags -DWIN32_LEAN_AND_MEAN shlibdir_default="$bindir_default" SLIBPREF="" SLIBSUF=".dll" @@ -5668,6 +5863,7 @@ case $target_os in enabled x86_64 && objformat="win64" || objformat="win32" enable dos_paths ! enabled small && test_cmd $windres --version && enable gnu_windres + add_cppflags -DWIN32_LEAN_AND_MEAN add_cppflags -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 ;; *-dos|freedos|opendos) @@ -5911,11 +6107,7 @@ extern_prefix=${sym%%ff_extern*} ! disabled inline_asm && check_inline_asm inline_asm '"" ::' -for restrict_keyword in restrict __restrict__ __restrict ""; do - test_code cc "" "char * $restrict_keyword p" && break -done - -check_cc pragma_deprecated "" '_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")' +check_cc pragma_deprecated "" '_Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")' # The global variable ensures the bits appear unchanged in the object file. test_cc <" to not have any effect unless it + # had an extra "+" included - but it was activated on the next + # ".arch_extension" directive. Check if we can include "+crc" as dummy + # feature to make the .arch directive behave as expected and take + # effect right away. + check_arch_level "${as_arch_level}+crc" + fi + enabled armv8 && check_insn armv8 'prfm pldl1strm, [x0]' # internal assembler in clang 3.3 does not support this instruction enabled neon && check_insn neon 'ext v0.8B, v0.8B, v1.8B, #1' - enabled vfp && check_insn vfp 'fmadd d0, d0, d1, d2' - map 'enabled_any ${v}_external ${v}_inline || disable $v' $ARCH_EXT_LIST_ARM + archext_list="dotprod i8mm" + enabled dotprod && check_archext_insn dotprod 'udot v0.4s, v0.16b, v0.16b' + enabled i8mm && check_archext_insn i8mm 'usdot v0.4s, v0.16b, v0.16b' + + # Disable the main feature (e.g. HAVE_NEON) if neither inline nor external + # assembly support the feature out of the box. Skip this for the features + # checked with check_archext_insn above, as that function takes care of + # updating all the variables as necessary. + for v in $ARCH_EXT_LIST_ARM; do + is_in $v $archext_list && continue + enabled_any ${v}_external ${v}_inline || disable $v + done elif enabled alpha; then @@ -6008,6 +6233,12 @@ EOF warn "Compiler does not indicate floating-point ABI, guessing $fpabi." fi + # Test for various instruction sets, testing support both in inline and + # external assembly. This sets the ${v}_inline or ${v}_external flags + # if the instruction can be used unconditionally in either inline or + # external assembly. This means that if the ${v}_external feature is set, + # that feature can be used unconditionally in various support macros + # anywhere in external assembly, in any function. enabled armv5te && check_insn armv5te 'qadd r0, r0, r0' enabled armv6 && check_insn armv6 'sadd16 r0, r0, r0' enabled armv6t2 && check_insn armv6t2 'movt r0, #0' @@ -6016,6 +6247,14 @@ EOF enabled vfpv3 && check_insn vfpv3 'vmov.f32 s0, #1.0' enabled setend && check_insn setend 'setend be' + # If neither inline nor external assembly can use the feature by default, + # disable the main unsuffixed feature (e.g. HAVE_NEON). + # + # For targets that support runtime CPU feature detection, don't disable + # the main feature flag - there we assume that all supported toolchains + # can assemble code for all instruction set features (e.g. NEON) with + # suitable assembly flags (such as ".fpu neon"); we don't check + # specifically that they really do. [ $target_os = linux ] || [ $target_os = android ] || map 'enabled_any ${v}_external ${v}_inline || disable $v' \ $ARCH_EXT_LIST_ARM @@ -6115,6 +6354,7 @@ elif enabled ppc; then elif enabled riscv; then + enabled rv && check_inline_asm rv '".option arch, +zbb\nrev8 t0, t1"' enabled rvv && check_inline_asm rvv '".option arch, +v\nvsetivli zero, 0, e8, m1, ta, ma"' elif enabled x86; then @@ -6173,7 +6413,6 @@ EOF done disabled x86asm && die "nasm/yasm not found or too old. Use --disable-x86asm for a crippled build." X86ASMFLAGS="-f $objformat" - enabled pic && append X86ASMFLAGS "-DPIC" test -n "$extern_prefix" && append X86ASMFLAGS "-DPREFIX" case "$objformat" in elf*) enabled debug && append X86ASMFLAGS $x86asm_debug ;; @@ -6184,7 +6423,6 @@ EOF enabled avx2 && check_x86asm avx2_external "vextracti128 xmm0, ymm0, 0" enabled xop && check_x86asm xop_external "vpmacsdd xmm0, xmm1, xmm2, xmm3" enabled fma4 && check_x86asm fma4_external "vfmaddps ymm0, ymm1, ymm2, ymm3" - check_x86asm cpunop "CPU amdnop" fi case "$cpu" in @@ -6260,22 +6498,32 @@ if enabled float16; then fi case "$custom_allocator" in + "") + ;; jemalloc) - # jemalloc by default does not use a prefix - require libjemalloc jemalloc/jemalloc.h malloc -ljemalloc + test -n "$malloc_prefix" || + malloc_prefix=$($pkg_config --variable=jemalloc_prefix $pkg_config_flags jemalloc 2>/dev/null) + require_pkg_config custom_allocator jemalloc jemalloc/jemalloc.h ${malloc_prefix}malloc ;; tcmalloc) - require_pkg_config libtcmalloc libtcmalloc gperftools/tcmalloc.h tc_malloc + require_pkg_config custom_allocator libtcmalloc gperftools/tcmalloc.h tc_malloc malloc_prefix=tc_ ;; + *) + require_pkg_config custom_allocator "$custom_allocator" stdlib.h malloc + ;; esac +if test -n "$custom_allocator"; then + add_extralibs "$custom_allocator_extralibs" +fi + check_func_headers malloc.h _aligned_malloc && enable aligned_malloc check_func ${malloc_prefix}memalign && enable memalign check_func ${malloc_prefix}posix_memalign && enable posix_memalign check_func access -check_func_headers stdlib.h arc4random +check_func_headers stdlib.h arc4random_buf check_lib clock_gettime time.h clock_gettime || check_lib clock_gettime time.h clock_gettime -lrt check_func fcntl check_func fork @@ -6305,6 +6553,7 @@ check_func_headers mach/mach_time.h mach_absolute_time check_func_headers stdlib.h getenv check_func_headers sys/stat.h lstat check_func_headers sys/auxv.h getauxval +check_func_headers sys/sysctl.h sysctlbyname check_func_headers windows.h GetModuleHandle check_func_headers windows.h GetProcessAffinityMask @@ -6345,11 +6594,11 @@ enabled libdrm && check_headers linux/dma-buf.h check_headers linux/perf_event.h -check_headers libcrystalhd/libcrystalhd_if.h check_headers malloc.h check_headers mftransform.h check_headers net/udplite.h check_headers poll.h +check_headers pthread_np.h check_headers sys/param.h check_headers sys/resource.h check_headers sys/select.h @@ -6359,6 +6608,8 @@ check_headers termios.h check_headers unistd.h check_headers valgrind/valgrind.h check_func_headers VideoToolbox/VTCompressionSession.h VTCompressionSessionPrepareToEncodeFrames -framework VideoToolbox +check_func_headers VideoToolbox/VideoToolbox.h VTPixelTransferSessionCreate -framework VideoToolbox +check_func_headers VideoToolbox/VideoToolbox.h VTPixelRotationSessionCreate -framework VideoToolbox check_headers windows.h check_headers asm/types.h @@ -6377,7 +6628,7 @@ done check_lib advapi32 "windows.h" RegCloseKey -ladvapi32 check_lib bcrypt "windows.h bcrypt.h" BCryptGenRandom -lbcrypt && check_cpp_condition bcrypt bcrypt.h "defined BCRYPT_RNG_ALGORITHM" -check_lib ole32 "windows.h" CoTaskMemFree -lole32 +check_lib ole32 "windows.h objbase.h" CoTaskMemFree -lole32 check_lib shell32 "windows.h shellapi.h" CommandLineToArgvW -lshell32 check_lib psapi "windows.h psapi.h" GetProcessMemoryInfo -lpsapi @@ -6435,6 +6686,8 @@ check_type "windows.h dxgi1_2.h" "IDXGIOutput1" check_type "windows.h dxgi1_5.h" "IDXGIOutput5" check_type "windows.h d3d11.h" "ID3D11VideoDecoder" check_type "windows.h d3d11.h" "ID3D11VideoContext" +check_type "windows.h d3d12.h" "ID3D12Device" +check_type "windows.h d3d12video.h" "ID3D12VideoDecoder" check_type "windows.h" "DPI_AWARENESS_CONTEXT" -D_WIN32_WINNT=0x0A00 check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602 check_func_headers mfapi.h MFCreateAlignedMemoryBuffer -lmfplat @@ -6464,10 +6717,11 @@ fi if ! disabled ffnvcodec; then ffnv_hdr_list="ffnvcodec/nvEncodeAPI.h ffnvcodec/dynlink_cuda.h ffnvcodec/dynlink_cuviddec.h ffnvcodec/dynlink_nvcuvid.h" - check_pkg_config ffnvcodec "ffnvcodec >= 12.0.16.0" "$ffnv_hdr_list" "" || \ - check_pkg_config ffnvcodec "ffnvcodec >= 11.1.5.2 ffnvcodec < 12.0" "$ffnv_hdr_list" "" || \ - check_pkg_config ffnvcodec "ffnvcodec >= 11.0.10.2 ffnvcodec < 11.1" "$ffnv_hdr_list" "" || \ - check_pkg_config ffnvcodec "ffnvcodec >= 8.1.24.14 ffnvcodec < 8.2" "$ffnv_hdr_list" "" + check_pkg_config ffnvcodec "ffnvcodec >= 12.1.14.0" "$ffnv_hdr_list" "" || \ + check_pkg_config ffnvcodec "ffnvcodec >= 12.0.16.1 ffnvcodec < 12.1" "$ffnv_hdr_list" "" || \ + check_pkg_config ffnvcodec "ffnvcodec >= 11.1.5.3 ffnvcodec < 12.0" "$ffnv_hdr_list" "" || \ + check_pkg_config ffnvcodec "ffnvcodec >= 11.0.10.3 ffnvcodec < 11.1" "$ffnv_hdr_list" "" || \ + check_pkg_config ffnvcodec "ffnvcodec >= 8.1.24.15 ffnvcodec < 8.2" "$ffnv_hdr_list" "" fi if enabled_all libglslang libshaderc; then @@ -6513,6 +6767,12 @@ if ! disabled pthreads && ! enabled w32threads && ! enabled os2threads; then if enabled pthreads; then check_builtin sem_timedwait semaphore.h "sem_t *s; sem_init(s,0,0); sem_timedwait(s,0); sem_destroy(s)" $pthreads_extralibs check_func pthread_cancel $pthreads_extralibs + hdrs=pthread.h + if enabled pthread_np_h; then + hdrs="$hdrs pthread_np.h" + fi + check_lib pthread_set_name_np "$hdrs" pthread_set_name_np -lpthread + check_lib pthread_setname_np "$hdrs" pthread_setname_np -lpthread fi fi @@ -6550,8 +6810,8 @@ done # these are off by default, so fail if requested and not available enabled avisynth && { require_headers "avisynth/avisynth_c.h avisynth/avs/version.h" && - { test_cpp_condition avisynth/avs/version.h "AVS_MAJOR_VER >= 3 && AVS_MINOR_VER >= 7 && AVS_BUGFIX_VER >= 1 || AVS_MAJOR_VER >= 3 && AVS_MINOR_VER > 7 || AVS_MAJOR_VER > 3" || - die "ERROR: AviSynth+ header version must be >= 3.7.1"; } } + { test_cpp_condition avisynth/avs/version.h "AVS_MAJOR_VER >= 3 && AVS_MINOR_VER >= 7 && AVS_BUGFIX_VER >= 3 || AVS_MAJOR_VER >= 3 && AVS_MINOR_VER > 7 || AVS_MAJOR_VER > 3" || + die "ERROR: AviSynth+ header version must be >= 3.7.3"; } } enabled cuda_nvcc && { check_nvcc cuda_nvcc || die "ERROR: failed checking for nvcc."; } enabled chromaprint && { check_pkg_config chromaprint libchromaprint "chromaprint.h" chromaprint_get_version || require chromaprint chromaprint.h chromaprint_get_version -lchromaprint; } @@ -6567,6 +6827,7 @@ enabled libaom && require_pkg_config libaom "aom >= 1.0.0" aom/aom_co enabled libaribb24 && { check_pkg_config libaribb24 "aribb24 > 1.0.3" "aribb24/aribb24.h" arib_instance_new || { enabled gpl && require_pkg_config libaribb24 aribb24 "aribb24/aribb24.h" arib_instance_new; } || die "ERROR: libaribb24 requires version higher than 1.0.3 or --enable-gpl."; } +enabled libaribcaption && require_pkg_config libaribcaption "libaribcaption >= 1.1.1" "aribcaption/aribcaption.h" aribcc_context_alloc enabled lv2 && require_pkg_config lv2 lilv-0 "lilv/lilv.h" lilv_world_new enabled libiec61883 && require libiec61883 libiec61883/iec61883.h iec61883_cmp_connect -lraw1394 -lavc1394 -lrom1394 -liec61883 enabled libass && require_pkg_config libass "libass >= 0.11.0" ass/ass.h ass_library_init @@ -6580,7 +6841,9 @@ enabled libcodec2 && require libcodec2 codec2/codec2.h codec2_create -lc enabled libdav1d && require_pkg_config libdav1d "dav1d >= 0.5.0" "dav1d/dav1d.h" dav1d_version enabled libdavs2 && require_pkg_config libdavs2 "davs2 >= 1.6.0" davs2.h davs2_decoder_open enabled libdc1394 && require_pkg_config libdc1394 libdc1394-2 dc1394/dc1394.h dc1394_new -enabled libdrm && require_pkg_config libdrm libdrm xf86drm.h drmGetVersion +enabled libdrm && check_pkg_config libdrm libdrm xf86drm.h drmGetVersion +enabled libdvdnav && require_pkg_config libdvdnav "dvdnav >= 6.1.1" dvdnav/dvdnav.h dvdnav_open2 +enabled libdvdread && require_pkg_config libdvdread "dvdread >= 6.1.2" dvdread/dvd_reader.h DVDOpen2 enabled libfdk_aac && { check_pkg_config libfdk_aac fdk-aac "fdk-aac/aacenc_lib.h" aacEncOpen || { require libfdk_aac fdk-aac/aacenc_lib.h aacEncOpen -lfdk-aac && warn "using libfdk without pkg-config"; } } @@ -6590,12 +6853,13 @@ enabled fontconfig && enable libfontconfig enabled libfontconfig && require_pkg_config libfontconfig fontconfig "fontconfig/fontconfig.h" FcInit enabled libfreetype && require_pkg_config libfreetype freetype2 "ft2build.h FT_FREETYPE_H" FT_Init_FreeType enabled libfribidi && require_pkg_config libfribidi fribidi fribidi.h fribidi_version_info +enabled libharfbuzz && require_pkg_config libharfbuzz harfbuzz hb.h hb_buffer_create enabled libglslang && { check_lib spirv_compiler glslang/Include/glslang_c_interface.h glslang_initialize_process \ - -lglslang -lMachineIndependent -lOSDependent -lHLSL -lOGLCompiler -lGenericCodeGen \ + -lglslang -lMachineIndependent -lGenericCodeGen \ -lSPVRemapper -lSPIRV -lSPIRV-Tools-opt -lSPIRV-Tools -lpthread -lstdc++ -lm || require spirv_compiler glslang/Include/glslang_c_interface.h glslang_initialize_process \ - -lglslang -lOSDependent -lHLSL -lOGLCompiler \ - -lSPVRemapper -lSPIRV -lSPIRV-Tools-opt -lSPIRV-Tools -lpthread -lstdc++ -lm; } + -lglslang -lMachineIndependent -lOSDependent -lHLSL -lOGLCompiler -lGenericCodeGen \ + -lSPVRemapper -lSPIRV -lSPIRV-Tools-opt -lSPIRV-Tools -lpthread -lstdc++ -lm ; } enabled libgme && { check_pkg_config libgme libgme gme/gme.h gme_new_emu || require libgme gme/gme.h gme_new_emu -lgme -lstdc++; } enabled libgsm && { for gsm_hdr in "gsm.h" "gsm/gsm.h"; do @@ -6605,8 +6869,8 @@ enabled libilbc && require libilbc ilbc.h WebRtcIlbcfix_InitDecode -li enabled libjxl && require_pkg_config libjxl "libjxl >= 0.7.0" jxl/decode.h JxlDecoderVersion && require_pkg_config libjxl_threads "libjxl_threads >= 0.7.0" jxl/thread_parallel_runner.h JxlThreadParallelRunner enabled libklvanc && require libklvanc libklvanc/vanc.h klvanc_context_create -lklvanc -enabled libkvazaar && require_pkg_config libkvazaar "kvazaar >= 0.8.1" kvazaar.h kvz_api_get -enabled liblensfun && require_pkg_config liblensfun lensfun lensfun.h lf_db_new +enabled libkvazaar && require_pkg_config libkvazaar "kvazaar >= 2.0.0" kvazaar.h kvz_api_get +enabled liblensfun && require_pkg_config liblensfun lensfun lensfun.h lf_db_create if enabled libmfx && enabled libvpl; then die "ERROR: can not use libmfx and libvpl together" @@ -6626,14 +6890,14 @@ elif enabled libmfx; then { require libmfx "mfxvideo.h mfxdefs.h" MFXInit "-llibmfx $advapi32_extralibs" && { test_cpp_condition mfxdefs.h "MFX_VERSION >= 1028 && MFX_VERSION < 2000" || die "ERROR: libmfx version must be >= 1.28 and < 2.0"; } && warn "using libmfx without pkg-config"; } } && - warn "build FFmpeg against libmfx 1.x, obsolete features of libmfx such as OPAQUE memory,\n"\ - "multi-frame encode, user plugins and LA_EXT rate control mode are enabled" + warn "libmfx is deprecated. Please run configure with --enable-libvpl to use libvpl instead." elif enabled libvpl; then # Consider pkg-config only. The name of libmfx is still passed to check_pkg_config function for --enable-libvpl option # because QSV has dependency on libmfx, we can use the same dependency if using libmfx in this check. The package name # is extracted from "vpl >= 2.6" check_pkg_config libmfx "vpl >= 2.6" "mfxvideo.h mfxdispatcher.h" MFXLoad || \ die "ERROR: libvpl >= 2.6 not found" + add_cflags -DMFX_DEPRECATED_OFF fi if enabled libmfx; then @@ -6653,11 +6917,13 @@ enabled libopencv && { check_headers opencv2/core/core_c.h && { check_pkg_config libopencv opencv opencv2/core/core_c.h cvCreateImageHeader || require libopencv opencv2/core/core_c.h cvCreateImageHeader -lopencv_core -lopencv_imgproc; } || require_pkg_config libopencv opencv opencv/cxcore.h cvCreateImageHeader; } -enabled libopenh264 && require_pkg_config libopenh264 openh264 wels/codec_api.h WelsGetCodecVersion +enabled libopenh264 && require_pkg_config libopenh264 "openh264 >= 1.3.0" wels/codec_api.h WelsGetCodecVersion enabled libopenjpeg && { check_pkg_config libopenjpeg "libopenjp2 >= 2.1.0" openjpeg.h opj_version || { require_pkg_config libopenjpeg "libopenjp2 >= 2.1.0" openjpeg.h opj_version -DOPJ_STATIC && add_cppflags -DOPJ_STATIC; } } enabled libopenmpt && require_pkg_config libopenmpt "libopenmpt >= 0.2.6557" libopenmpt/libopenmpt.h openmpt_module_create -lstdc++ && append libopenmpt_extralibs "-lstdc++" -enabled libopenvino && require libopenvino c_api/ie_c_api.h ie_c_api_version -linference_engine_c_api +enabled libopenvino && { { check_pkg_config libopenvino openvino openvino/c/openvino.h ov_core_create && enable openvino2; } || + { check_pkg_config libopenvino openvino c_api/ie_c_api.h ie_c_api_version || + require libopenvino c_api/ie_c_api.h ie_c_api_version -linference_engine_c_api; } } enabled libopus && { enabled libopus_decoder && { require_pkg_config libopus opus opus_multistream.h opus_multistream_decoder_create @@ -6668,10 +6934,12 @@ enabled libopus && { } enabled libplacebo && require_pkg_config libplacebo "libplacebo >= 4.192.0" libplacebo/vulkan.h pl_vulkan_create enabled libpulse && require_pkg_config libpulse libpulse pulse/pulseaudio.h pa_context_new +enabled libqrencode && require_pkg_config libqrencode libqrencode qrencode.h QRcode_encodeString +enabled libquirc && require libquirc quirc.h quirc_decode -lquirc enabled librabbitmq && require_pkg_config librabbitmq "librabbitmq >= 0.7.1" amqp.h amqp_new_connection enabled librav1e && require_pkg_config librav1e "rav1e >= 0.5.0" rav1e.h rav1e_context_new enabled librist && require_pkg_config librist "librist >= 0.2.7" librist/librist.h rist_receiver_create -enabled librsvg && require_pkg_config librsvg librsvg-2.0 librsvg-2.0/librsvg/rsvg.h rsvg_handle_render_cairo +enabled librsvg && require_pkg_config librsvg librsvg-2.0 librsvg-2.0/librsvg/rsvg.h rsvg_handle_new_from_data enabled librtmp && require_pkg_config librtmp librtmp librtmp/rtmp.h RTMP_Socket enabled librubberband && require_pkg_config librubberband "rubberband >= 1.8.1" rubberband/rubberband-c.h rubberband_new -lstdc++ && append librubberband_extralibs "-lstdc++" enabled libshaderc && require_pkg_config spirv_compiler "shaderc >= 2019.1" shaderc/shaderc.h shaderc_compiler_initialize @@ -6680,7 +6948,7 @@ enabled libsmbclient && { check_pkg_config libsmbclient smbclient libsmbcli require libsmbclient libsmbclient.h smbc_init -lsmbclient; } enabled libsnappy && require libsnappy snappy-c.h snappy_compress -lsnappy -lstdc++ enabled libsoxr && require libsoxr soxr.h soxr_create -lsoxr -enabled libssh && require_pkg_config libssh libssh libssh/sftp.h sftp_init +enabled libssh && require_pkg_config libssh "libssh >= 0.6.0" libssh/sftp.h sftp_init enabled libspeex && require_pkg_config libspeex speex speex/speex.h speex_decoder_init enabled libsrt && require_pkg_config libsrt "srt >= 1.3.0" srt/srt.h srt_socket enabled libsvtav1 && require_pkg_config libsvtav1 "SvtAv1Enc >= 0.9.0" EbSvtAv1Enc.h svt_av1_enc_init_handle @@ -6688,6 +6956,7 @@ enabled libtensorflow && require libtensorflow tensorflow/c/c_api.h TF_Versi enabled libtesseract && require_pkg_config libtesseract tesseract tesseract/capi.h TessBaseAPICreate enabled libtheora && require libtheora theora/theoraenc.h th_info_init -ltheoraenc -ltheoradec -logg enabled libtls && require_pkg_config libtls libtls tls.h tls_configure +enabled libtorch && check_cxxflags -std=c++17 && require_cpp libtorch torch/torch.h "torch::Tensor" -ltorch -lc10 -ltorch_cpu -lstdc++ -lpthread enabled libtwolame && require libtwolame twolame.h twolame_init -ltwolame && { check_lib libtwolame twolame.h twolame_encode_buffer_float32_interleaved -ltwolame || die "ERROR: libtwolame must be installed and version must be >= 0.3.10"; } @@ -6695,6 +6964,7 @@ enabled libuavs3d && require_pkg_config libuavs3d "uavs3d >= 1.1.41" uav enabled libv4l2 && require_pkg_config libv4l2 libv4l2 libv4l2.h v4l2_ioctl enabled libvidstab && require_pkg_config libvidstab "vidstab >= 0.98" vid.stab/libvidstab.h vsMotionDetectInit enabled libvmaf && require_pkg_config libvmaf "libvmaf >= 2.0.0" libvmaf.h vmaf_init +enabled libvmaf && check_pkg_config libvmaf_cuda "libvmaf >= 2.0.0" libvmaf_cuda.h vmaf_cuda_state_init enabled libvo_amrwbenc && require libvo_amrwbenc vo-amrwbenc/enc_if.h E_IF_init -lvo-amrwbenc enabled libvorbis && require_pkg_config libvorbis vorbis vorbis/codec.h vorbis_info_init && require_pkg_config libvorbisenc vorbisenc vorbis/vorbisenc.h vorbis_encode_init @@ -6728,11 +6998,14 @@ enabled libx264 && require_pkg_config libx264 x264 "stdint.h x264.h" x require_cpp_condition libx264 x264.h "X264_BUILD >= 122" && { [ "$toolchain" != "msvc" ] || require_cpp_condition libx264 x264.h "X264_BUILD >= 158"; } && + check_cpp_condition libx264_hdr10 x264.h "X264_BUILD >= 163" && check_cpp_condition libx262 x264.h "X264_MPEG2" enabled libx265 && require_pkg_config libx265 x265 x265.h x265_api_get && require_cpp_condition libx265 x265.h "X265_BUILD >= 89" enabled libxavs && require libxavs "stdint.h xavs.h" xavs_encoder_encode "-lxavs $pthreads_extralibs $libm_extralibs" enabled libxavs2 && require_pkg_config libxavs2 "xavs2 >= 1.3.0" "stdint.h xavs2.h" xavs2_api_get +enabled libxevd && require_pkg_config libxevd "xevd >= 0.4.1" "xevd.h" xevd_decode +enabled libxeve && require_pkg_config libxeve "xeve >= 0.4.3" "xeve.h" xeve_encode enabled libxvid && require libxvid xvid.h xvid_global -lxvidcore enabled libzimg && require_pkg_config libzimg "zimg >= 2.7.0" zimg.h zimg_get_api_version enabled libzmq && require_pkg_config libzmq "libzmq >= 4.2.1" zmq.h zmq_ctx_new @@ -6908,8 +7181,7 @@ enabled alsa && { check_pkg_config alsa alsa "alsa/asoundlib.h" snd_pcm_htimesta enabled libjack && require_pkg_config libjack jack jack/jack.h jack_port_get_latency_range -enabled sndio && { check_pkg_config sndio sndio "sndio.h" sio_open || - check_lib sndio sndio.h sio_open -lsndio; } +enabled sndio && check_pkg_config sndio sndio sndio.h sio_open if enabled libcdio; then check_pkg_config libcdio libcdio_paranoia "cdio/cdda.h cdio/paranoia.h" cdio_cddap_open || @@ -6930,9 +7202,8 @@ fi check_func_headers "windows.h" CreateDIBSection "$gdigrab_indev_extralibs" -# d3d11va requires linking directly to dxgi and d3d11 if not building for -# the desktop api partition -test_cpp < #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) @@ -6945,8 +7216,17 @@ test_cpp <= 0.35.0" "va/va.h" vaInitialize if enabled vaapi; then - check_pkg_config vaapi_drm "libva-drm" "va/va_drm.h" vaGetDisplayDRM + case $target_os in + mingw32*|mingw64*|win32|win64) + check_pkg_config vaapi_win32 "libva-win32" "va/va_win32.h" vaGetDisplayWin32 + ;; + *) + check_pkg_config vaapi_drm "libva-drm" "va/va_drm.h" vaGetDisplayDRM + ;; + esac if enabled xlib_x11; then check_pkg_config vaapi_x11 "libva-x11" "va/va_x11.h" vaGetDisplay @@ -6973,6 +7260,7 @@ if enabled vaapi; then check_type "va/va.h va/va_enc_jpeg.h" "VAEncPictureParameterBufferJPEG" check_type "va/va.h va/va_enc_vp8.h" "VAEncPictureParameterBufferVP8" check_type "va/va.h va/va_enc_vp9.h" "VAEncPictureParameterBufferVP9" + check_type "va/va.h va/va_enc_av1.h" "VAEncPictureParameterBufferAV1" fi if enabled_all opencl libdrm ; then @@ -7001,24 +7289,29 @@ if enabled_all opencl d3d11va ; then enable opencl_d3d11 fi +if enabled_all opencl videotoolbox ; then + check_func_headers OpenCL/cl_gl_ext.h clCreateImageFromIOSurfaceWithPropertiesAPPLE -framework VideoToolbox -framework OpenCL && + enable opencl_videotoolbox +fi + enabled vdpau && check_cpp_condition vdpau vdpau/vdpau.h "defined VDP_DECODER_PROFILE_MPEG4_PART2_ASP" enabled vdpau && check_lib vdpau_x11 "vdpau/vdpau.h vdpau/vdpau_x11.h" vdp_device_create_x11 -lvdpau -lX11 -enabled crystalhd && check_lib crystalhd "stdint.h libcrystalhd/libcrystalhd_if.h" DtsCrystalHDVersion -lcrystalhd && \ - warn "CrystalHD support is deprecated and will be removed, please contact the developers if you are interested" \ - "in maintaining it." - if enabled vulkan; then - check_pkg_config_header_only vulkan "vulkan >= 1.2.189" "vulkan/vulkan.h" "defined VK_VERSION_1_2" || - check_cpp_condition vulkan "vulkan/vulkan.h" "defined(VK_VERSION_1_3) || (defined(VK_VERSION_1_2) && VK_HEADER_VERSION >= 189)" + check_pkg_config_header_only vulkan "vulkan >= 1.3.277" "vulkan/vulkan.h" "defined VK_VERSION_1_3" || + check_cpp_condition vulkan "vulkan/vulkan.h" "defined(VK_VERSION_1_4) || (defined(VK_VERSION_1_3) && VK_HEADER_VERSION >= 277)" +fi + +if disabled vulkan; then + disable libglslang libshaderc spirv_compiler fi if enabled x86; then case $target_os in - mingw32*|mingw64*|win32|win64|linux|cygwin*) + freebsd|mingw32*|mingw64*|win32|win64|linux|cygwin*) ;; *) disable ffnvcodec cuvid nvdec nvenc @@ -7056,7 +7349,7 @@ fi enabled amf && check_cpp_condition amf "AMF/core/Version.h" \ - "(AMF_VERSION_MAJOR << 48 | AMF_VERSION_MINOR << 32 | AMF_VERSION_RELEASE << 16 | AMF_VERSION_BUILD_NUM) >= 0x00010004001c0000" + "(AMF_VERSION_MAJOR << 48 | AMF_VERSION_MINOR << 32 | AMF_VERSION_RELEASE << 16 | AMF_VERSION_BUILD_NUM) >= 0x00010004001d0000" # Funny iconv installations are not unusual, so check it after all flags have been set if enabled libc_iconv; then @@ -7117,7 +7410,6 @@ void (^block)(void); EOF # add some linker flags -check_ldflags -Wl,--warn-common check_ldflags -Wl,-rpath-link=:libpostproc:libswresample:libswscale:libavfilter:libavdevice:libavformat:libavcodec:libavutil enabled rpath && add_ldexeflags -Wl,-rpath,$libdir && add_ldsoflags -Wl,-rpath,$libdir test_ldflags -Wl,-Bsymbolic && append SHFLAGS -Wl,-Bsymbolic @@ -7182,18 +7474,28 @@ fi check_optflags(){ check_cflags "$@" - enabled lto && check_ldflags "$@" + [ -n "$lto" ] && check_ldflags "$@" } check_optflags $optflags check_optflags -fno-math-errno check_optflags -fno-signed-zeros -if enabled lto; then +if [ -n "$lto" ]; then test "$cc_type" != "$ld_type" && die "LTO requires same compiler and linker" - check_cflags -flto - check_ldflags -flto $cpuflags + check_cflags $lto + check_ldflags $lto $cpuflags disable inline_asm_direct_symbol_refs + if test "$cc_type" = "clang"; then + # Clang's LTO fails on Windows, when there are references outside + # of inline assembly to nonlocal labels defined within inline assembly, + # see https://github.com/llvm/llvm-project/issues/76046. + case $target_os in + mingw32|win32) + disable inline_asm_nonlocal_labels + ;; + esac + fi fi enabled ftrapv && check_cflags -ftrapv @@ -7223,7 +7525,7 @@ if enabled icc; then # icc 11.0 and 11.1 work with ebp_available, but don't pass the test enable ebp_available # The test above does not test linking - enabled lto && disable symver_asm_label + [ -n "$lto" ] && disable symver_asm_label if enabled x86_32; then icc_version=$($cc -dumpversion) test ${icc_version%%.*} -ge 11 && @@ -7311,17 +7613,6 @@ elif enabled_any msvc icl; then fi # msvcrt10 x64 incorrectly enables log2, only msvcrt12 (MSVC 2013) onwards actually has log2. check_cpp_condition log2 crtversion.h "_VC_CRT_MAJOR_VERSION >= 12" - # The CRT headers contain __declspec(restrict) in a few places, but if redefining - # restrict, this might break. MSVC 2010 and 2012 fail with __declspec(__restrict) - # (as it ends up if the restrict redefine is done before including stdlib.h), while - # MSVC 2013 and newer can handle it fine. - # If this declspec fails, force including stdlib.h before the restrict redefinition - # happens in config.h. - if [ $restrict_keyword != restrict ]; then - test_cc <= 190024218" || @@ -7355,12 +7646,6 @@ esac enable frame_thread_encoder -# these filters depend on removed avcodec APIs -# they are kept disabled for now, but will be removed if -# nobody updates and re-enables them -disable mcdeint_filter -disable uspp_filter - enabled asm || { arch=c; disable $ARCH_LIST $ARCH_EXT_LIST; } check_deps $CONFIG_LIST \ @@ -7370,6 +7655,12 @@ check_deps $CONFIG_LIST \ enabled threads && ! enabled pthreads && ! enabled atomics_native && die "non pthread threading without atomics not supported, try adding --enable-pthreads or --cpu=i486 or higher if you are on x86" +enabled threads || warn \ + "Threading support was disabled or is not available on the target platform." \ + "This means that not only is this build not multi-threaded, but also" \ + "that the libraries from this build MUST NOT be used in a multi-threaded"\ + "environment." + case $target_os in haiku) disable memalign @@ -7470,10 +7761,13 @@ enabled cover_rect_filter && prepend avfilter_deps "avformat avcodec" enabled ebur128_filter && enabled swresample && prepend avfilter_deps "swresample" enabled elbg_filter && prepend avfilter_deps "avcodec" enabled find_rect_filter && prepend avfilter_deps "avformat avcodec" +enabled fsync_filter && prepend avfilter_deps "avformat" enabled mcdeint_filter && prepend avfilter_deps "avcodec" enabled movie_filter && prepend avfilter_deps "avformat avcodec" enabled pan_filter && prepend avfilter_deps "swresample" enabled pp_filter && prepend avfilter_deps "postproc" +enabled qrencode_filter && prepend_avfilter_deps "swscale" +enabled qrencodesrc_filter && prepend_avfilter_deps "swscale" enabled removelogo_filter && prepend avfilter_deps "avformat avcodec swscale" enabled sab_filter && prepend avfilter_deps "swscale" enabled scale_filter && prepend avfilter_deps "swscale" @@ -7578,7 +7872,8 @@ if enabled x86; then fi if enabled aarch64; then echo "NEON enabled ${neon-no}" - echo "VFP enabled ${vfp-no}" + echo "DOTPROD enabled ${dotprod-no}" + echo "I8MM enabled ${i8mm-no}" fi if enabled arm; then echo "ARMv5TE enabled ${armv5te-no}" @@ -7835,6 +8130,7 @@ SAMPLES:=${samples:-\$(FATE_SAMPLES)} NOREDZONE_FLAGS=$noredzone_flags LIBFUZZER_PATH=$libfuzzer_path IGNORE_TESTS=$ignore_tests +VERSION_TRACKING=$version_tracking EOF map 'eval echo "${v}_FFLIBS=\$${v}_deps" >> ffbuild/config.mak' $LIBRARY_LIST @@ -7849,17 +8145,15 @@ cat > $TMPH <>$TMPH +enabled aarch64 && + echo "#define AS_ARCH_LEVEL $as_arch_level" >>$TMPH + if enabled x86asm; then append config_files $TMPASM cat > $TMPASM <. + +@anchor{Resolution Process} +@section Resolution Process + +The Technical Committee (TC) is here to arbitrate and make decisions when technical conflicts occur in the project. + +The TC main role is to resolve technical conflicts. It is therefore not a technical steering committee, but it is understood that some decisions might impact the future of the project. + +@subsection Seizing + +The TC can take possession of any technical matter that it sees fit. + +To involve the TC in a matter, email tc@ or CC them on an ongoing discussion. + +As members of TC are developers, they also can email tc@ to raise an issue. +@subsection Announcement + +The TC, once seized, must announce itself on the main mailing list, with a [TC] tag. + +The TC has 2 modes of operation: a RFC one and an internal one. + +If the TC thinks it needs the input from the larger community, the TC can call for a RFC. Else, it can decide by itself. + +If the disagreement involves a member of the TC, that member should recuse themselves from the decision. + +The decision to use a RFC process or an internal discussion is a discretionary decision of the TC. + +The TC can also reject a seizure for a few reasons such as: the matter was not discussed enough previously; it lacks expertise to reach a beneficial decision on the matter; or the matter is too trivial. +@subsection RFC call + +In the RFC mode, one person from the TC posts on the mailing list the technical question and will request input from the community. + +The mail will have the following specification: + + a precise title + a specific tag [TC RFC] + a top-level email + contain a precise question that does not exceed 100 words and that is answerable by developers + may have an extra description, or a link to a previous discussion, if deemed necessary, + contain a precise end date for the answers. + +The answers from the community must be on the main mailing list and must have the following specification: + + keep the tag and the title unchanged + limited to 400 words + a first-level, answering directly to the main email + answering to the question. + +Further replies to answers are permitted, as long as they conform to the community standards of politeness, they are limited to 100 words, and are not nested more than once. (max-depth=2) + +After the end-date, mails on the thread will be ignored. + +Violations of those rules will be escalated through the Community Committee. + +After all the emails are in, the TC has 96 hours to give its final decision. Exceptionally, the TC can request an extra delay, that will be notified on the mailing list. +@subsection Within TC + +In the internal case, the TC has 96 hours to give its final decision. Exceptionally, the TC can request an extra delay. +@subsection Decisions + +The decisions from the TC will be sent on the mailing list, with the [TC] tag. + +Internally, the TC should take decisions with a majority, or using ranked-choice voting. + +The decision from the TC should be published with a summary of the reasons that lead to this decision. + +The decisions from the TC are final, until the matters are reopened after no less than one year. + +@anchor{Community Committee} +@chapter Community Committee + +The Community Committee (CC) is here to arbitrage and make decisions when inter-personal conflicts occur in the project. It will decide quickly and take actions, for the sake of the project. + +The CC can remove privileges of offending members, including removal of commit access and temporary ban from the community. + +Decisions made by the CC can be re-opened after 1 year or by a majority vote of the General Assembly. Indefinite bans from the community must be confirmed by the General Assembly, in a majority vote. + +The CC is elected by the General Assembly for a duration of 1 year, and is composed of 5 members. Members can be re-elected if they wish. A majority vote in the General Assembly can trigger a new election of the CC. + +The members of the CC can be elected from outside of the GA. Candidates for election can either be suggested or self-nominated. + +The CC is governed by and responsible for enforcing the Code of Conduct. + +The CC can be contacted at . + +@anchor{Code of Conduct} +@chapter Code of Conduct + +Be friendly and respectful towards others and third parties. +Treat others the way you yourself want to be treated. + +Be considerate. Not everyone shares the same viewpoint and priorities as you do. +Different opinions and interpretations help the project. +Looking at issues from a different perspective assists development. + +Do not assume malice for things that can be attributed to incompetence. Even if +it is malice, it's rarely good to start with that as initial assumption. + +Stay friendly even if someone acts contrarily. Everyone has a bad day +once in a while. +If you yourself have a bad day or are angry then try to take a break and reply +once you are calm and without anger if you have to. + +Try to help other team members and cooperate if you can. + +The goal of software development is to create technical excellence, not for any +individual to be better and "win" against the others. Large software projects +are only possible and successful through teamwork. + +If someone struggles do not put them down. Give them a helping hand +instead and point them in the right direction. + +Finally, keep in mind the immortal words of Bill and Ted, +"Be excellent to each other." + +@bye diff --git a/doc/decoders.texi b/doc/decoders.texi index 5ba85cf9b14..293c82c2ba0 100644 --- a/doc/decoders.texi +++ b/doc/decoders.texi @@ -130,6 +130,30 @@ Set amount of frame threads to use during decoding. The default value is 0 (auto @end table +@section libxevd + +eXtra-fast Essential Video Decoder (XEVD) MPEG-5 EVC decoder wrapper. + +This decoder requires the presence of the libxevd headers and library +during configuration. You need to explicitly configure the build with +@option{--enable-libxevd}. + +The xevd project website is at @url{https://github.com/mpeg5/xevd}. + +@subsection Options + +The following options are supported by the libxevd wrapper. +The xevd-equivalent options or values are listed in parentheses for easy migration. + +To get a more accurate and extensive documentation of the libxevd options, +invoke the command @code{xevd_app --help} or consult the libxevd documentation. + +@table @option +@item threads (@emph{threads}) +Force to use a specific number of threads + +@end table + @section QSV Decoders The family of Intel QuickSync Video decoders (VC1, MPEG-2, H.264, HEVC, @@ -353,6 +377,169 @@ Enabled by default. @end table +@section libaribcaption + +Yet another ARIB STD-B24 caption decoder using external @dfn{libaribcaption} +library. + +Implements profiles A and C of the Japanse ARIB STD-B24 standard, +Brazilian ABNT NBR 15606-1, and Philippines version of ISDB-T. + +Requires the presence of the libaribcaption headers and library +(@url{https://github.com/xqq/libaribcaption}) during configuration. +You need to explicitly configure the build with @code{--enable-libaribcaption}. +If both @dfn{libaribb24} and @dfn{libaribcaption} are enabled, @dfn{libaribcaption} +decoder precedes. + +@subsection libaribcaption Decoder Options + +@table @option + +@item -sub_type @var{subtitle_type} +Specifies the format of the decoded subtitles. + +@table @samp +@item bitmap +Graphical image. +@item ass +ASS formatted text. +@item text +Simple text based output without formatting. +@end table + +The default is @dfn{ass} as same as @dfn{libaribb24} decoder. +Some present players (e.g., @dfn{mpv}) expect ASS format for ARIB caption. + +@item -caption_encoding @var{encoding_scheme} +Specifies the encoding scheme of input subtitle text. + +@table @samp +@item auto +Automatically detect text encoding (default). +@item jis +8bit-char JIS encoding defined in ARIB STD B24. +This encoding used in Japan for ISDB captions. +@item utf8 +UTF-8 encoding defined in ARIB STD B24. +This encoding is used in Philippines for ISDB-T captions. +@item latin +Latin character encoding defined in ABNT NBR 15606-1. +This encoding is used in South America for SBTVD / ISDB-Tb captions. +@end table + +@item -font @var{font_name[,font_name2,...]} +Specify comma-separated list of font family names to be used for @dfn{bitmap} +or @dfn{ass} type subtitle rendering. +Only first font name is used for @dfn{ass} type subtitle. + +If not specified, use internaly defined default font family. + +@item -ass_single_rect @var{boolean} +ARIB STD-B24 specifies that some captions may be displayed at different +positions at a time (multi-rectangle subtitle). +Since some players (e.g., old @dfn{mpv}) can't handle multiple ASS rectangles +in a single AVSubtitle, or multiple ASS rectangles of indeterminate duration +with the same start timestamp, this option can change the behavior so that +all the texts are displayed in a single ASS rectangle. + +The default is @var{false}. + +If your player cannot handle AVSubtitles with multiple ASS rectangles properly, +set this option to @var{true} or define @env{ASS_SINGLE_RECT=1} to change +default behavior at compilation. + +@item -force_outline_text @var{boolean} +Specify whether always render outline text for all characters regardless of +the indication by charactor style. + +The default is @var{false}. + +@item -outline_width @var{number} (0.0 - 3.0) +Specify width for outline text, in dots (relative). + +The default is @var{1.5}. + +@item -ignore_background @var{boolean} +Specify whether to ignore background color rendering. + +The default is @var{false}. + +@item -ignore_ruby @var{boolean} +Specify whether to ignore rendering for ruby-like (furigana) characters. + +The default is @var{false}. + +@item -replace_drcs @var{boolean} +Specify whether to render replaced DRCS characters as Unicode characters. + +The default is @var{true}. + +@item -replace_msz_ascii @var{boolean} +Specify whether to replace MSZ (Middle Size; half width) fullwidth +alphanumerics with halfwidth alphanumerics. + +The default is @var{true}. + +@item -replace_msz_japanese @var{boolean} +Specify whether to replace some MSZ (Middle Size; half width) fullwidth +japanese special characters with halfwidth ones. + +The default is @var{true}. + +@item -replace_msz_glyph @var{boolean} +Specify whether to replace MSZ (Middle Size; half width) characters +with halfwidth glyphs if the fonts supports it. +This option works under FreeType or DirectWrite renderer +with Adobe-Japan1 compliant fonts. +e.g., IBM Plex Sans JP, Morisawa BIZ UDGothic, Morisawa BIZ UDMincho, +Yu Gothic, Yu Mincho, and Meiryo. + +The default is @var{true}. + +@item -canvas_size @var{image_size} +Specify the resolution of the canvas to render subtitles to; usually, this +should be frame size of input video. +This only applies when @code{-subtitle_type} is set to @var{bitmap}. + +The libaribcaption decoder assumes input frame size for bitmap rendering as below: +@enumerate +@item +PROFILE_A : 1440 x 1080 with SAR (PAR) 4:3 +@item +PROFILE_C : 320 x 180 with SAR (PAR) 1:1 +@end enumerate + +If actual frame size of input video does not match above assumption, +the rendered captions may be distorted. +To make the captions undistorted, add @code{-canvas_size} option to specify +actual input video size. + +Note that the @code{-canvas_size} option is not required for video with +different size but same aspect ratio. +In such cases, the caption will be stretched or shrunk to actual video size +if @code{-canvas_size} option is not specified. +If @code{-canvas_size} option is specified with different size, +the caption will be stretched or shrunk as specified size with calculated SAR. + +@end table + +@subsection libaribcaption decoder usage examples + +Display MPEG-TS file with ARIB subtitle by @code{ffplay} tool: +@example +ffplay -sub_type bitmap MPEG.TS +@end example + +Display MPEG-TS file with input frame size 1920x1080 by @code{ffplay} tool: +@example +ffplay -sub_type bitmap -canvas_size 1920x1080 MPEG.TS +@end example + +Embed ARIB subtitle in transcoded video: +@example +ffmpeg -sub_type bitmap -i src.m2t -filter_complex "[0:v][0:s]overlay" -vcodec h264 dest.mp4 +@end example + @section dvbsub @subsection Options diff --git a/doc/demuxers.texi b/doc/demuxers.texi index 3c81024f038..b70f3a38d71 100644 --- a/doc/demuxers.texi +++ b/doc/demuxers.texi @@ -285,6 +285,168 @@ This demuxer accepts the following option: @end table +@section dvdvideo + +DVD-Video demuxer, powered by libdvdnav and libdvdread. + +Can directly ingest DVD titles, specifically sequential PGCs, into +a conversion pipeline. Menu assets, such as background video or audio, +can also be demuxed given the menu's coordinates (at best effort). +Seeking is not supported at this time. + +Block devices (DVD drives), ISO files, and directory structures are accepted. +Activate with @code{-f dvdvideo} in front of one of these inputs. + +This demuxer does NOT have decryption code of any kind. You are on your own +working with encrypted DVDs, and should not expect support on the matter. + +Underlying playback is handled by libdvdnav, and structure parsing by libdvdread. +FFmpeg must be built with GPL library support available as well as the +configure switches @code{--enable-libdvdnav} and @code{--enable-libdvdread}. + +You will need to provide either the desired "title number" or exact PGC/PG coordinates. +Many open-source DVD players and tools can aid in providing this information. +If not specified, the demuxer will default to title 1 which works for many discs. +However, due to the flexibility of the format, it is recommended to check manually. +There are many discs that are authored strangely or with invalid headers. + +If the input is a real DVD drive, please note that there are some drives which may +silently fail on reading bad sectors from the disc, returning random bits instead +which is effectively corrupt data. This is especially prominent on aging or rotting discs. +A second pass and integrity checks would be needed to detect the corruption. +This is not an FFmpeg issue. + +@subsection Background + +DVD-Video is not a directly accessible, linear container format in the +traditional sense. Instead, it allows for complex and programmatic playback of +carefully muxed MPEG-PS streams that are stored in headerless VOB files. +To the end-user, these streams are known simply as "titles", but the actual +logical playback sequence is defined by one or more "PGCs", or Program Group Chains, +within the title. The PGC is in turn comprised of multiple "PGs", or Programs", +which are the actual video segments (and for a typical video feature, sequentially +ordered). The PGC structure, along with stream layout and metadata, are stored in +IFO files that need to be parsed. PGCs can be thought of as playlists in easier terms. + +An actual DVD player relies on user GUI interaction via menus and an internal VM +to drive the direction of demuxing. Generally, the user would either navigate (via menus) +or automatically be redirected to the PGC of their choice. During this process and +the subsequent playback, the DVD player's internal VM also maintains a state and +executes instructions that can create jumps to different sectors during playback. +This is why libdvdnav is involved, as a linear read of the MPEG-PS blobs on the +disc (VOBs) is not enough to produce the right sequence in many cases. + +There are many other DVD structures (a long subject) that will not be discussed here. +NAV packets, in particular, are handled by this demuxer to build accurate timing +but not emitted as a stream. For a good high-level understanding, refer to: +@url{https://code.videolan.org/videolan/libdvdnav/-/blob/master/doc/dvd_structures} + +@subsection Options + +This demuxer accepts the following options: + +@table @option + +@item title @var{int} +The title number to play. Must be set if @option{pgc} and @option{pg} are not set. +Not applicable to menus. +Default is 0 (auto), which currently only selects the first available title (title 1) +and notifies the user about the implications. + +@item chapter_start @var{int} +The chapter, or PTT (part-of-title), number to start at. Not applicable to menus. +Default is 1. + +@item chapter_end @var{int} +The chapter, or PTT (part-of-title), number to end at. Not applicable to menus. +Default is 0, which is a special value to signal end at the last possible chapter. + +@item angle @var{int} +The video angle number, referring to what is essentially an additional +video stream that is composed from alternate frames interleaved in the VOBs. +Not applicable to menus. +Default is 1. + +@item region @var{int} +The region code to use for playback. Some discs may use this to default playback +at a particular angle in different regions. This option will not affect the region code +of a real DVD drive, if used as an input. Not applicable to menus. +Default is 0, "world". + +@item menu @var{bool} +Demux menu assets instead of navigating a title. Requires exact coordinates +of the menu (@option{menu_lu}, @option{menu_vts}, @option{pgc}, @option{pg}). +Default is false. + +@item menu_lu @var{int} +The menu language to demux. In DVD, menus are grouped by language. +Default is 0, the first language unit. + +@item menu_vts @var{int} +The VTS where the menu lives, or 0 if it is a VMG menu (root-level). +Default is 0, VMG menu. + +@item pgc @var{int} +The entry PGC to start playback, in conjunction with @option{pg}. +Alternative to setting @option{title}. +Chapter markers are not supported at this time. +Must be explicitly set for menus. +Default is 0, automatically resolve from value of @option{title}. + +@item pg @var{int} +The entry PG to start playback, in conjunction with @option{pgc}. +Alternative to setting @option{title}. +Chapter markers are not supported at this time. +Default is 0, automatically resolve from value of @option{title}, or +start from the beginning (PG 1) of the menu. + +@item preindex @var{bool} +Enable this to have accurate chapter (PTT) markers and duration measurement, +which requires a slow second pass read in order to index the chapter marker +timestamps from NAV packets. This is non-ideal extra work for real optical drives. +It is recommended and faster to use this option with a backup of the DVD structure +stored on a hard drive. Not compatible with @option{pgc} and @option{pg}. +Not applicable to menus. +Default is 0, false. + +@item trim @var{bool} +Skip padding cells (i.e. cells shorter than 1 second) from the beginning. +There exist many discs with filler segments at the beginning of the PGC, +often with junk data intended for controlling a real DVD player's +buffering speed and with no other material data value. +Not applicable to menus. +Default is 1, true. + +@end table + +@subsection Examples + +@itemize +@item +Open title 3 from a given DVD structure: +@example +ffmpeg -f dvdvideo -title 3 -i ... +@end example + +@item +Open chapters 3-6 from title 1 from a given DVD structure: +@example +ffmpeg -f dvdvideo -chapter_start 3 -chapter_end 6 -title 1 -i ... +@end example + +@item +Open only chapter 5 from title 1 from a given DVD structure: +@example +ffmpeg -f dvdvideo -chapter_start 5 -chapter_end 5 -title 1 -i ... +@end example + +@item +Demux menu with language 1 from VTS 1, PGC 1, starting at PG 1: +@example +ffmpeg -f dvdvideo -menu 1 -menu_lu 1 -menu_vts 1 -pgc 1 -pg 1 -i ... +@end example +@end itemize + @section ea Electronic Arts Multimedia format demuxer. @@ -307,7 +469,15 @@ stream which contains the alpha channel in addition to the ordinary video. Interoperable Master Format demuxer. -This demuxer presents audio and video streams found in an IMF Composition. +This demuxer presents audio and video streams found in an IMF Composition, as +specified in @url{https://doi.org/10.5594/SMPTE.ST2067-2.2020, SMPTE ST 2067-2}. + +@example +ffmpeg [-assetmaps ,,...] -i ... +@end example + +If @code{-assetmaps} is not specified, the demuxer looks for a file called +@file{ASSETMAP.xml} in the same directory as the CPL. @section flv, live_flv, kux @@ -769,8 +939,15 @@ error or used to store a negative value for dts correction when treated as signe the user set an upper limit, beyond which the delta is clamped to 1. Values greater than the limit if negative when cast to int32 are used to adjust onward dts. -Unit is the track time scale. Range is 0 to UINT_MAX. Default is @code{UINT_MAX - 48000*10} which allows upto +Unit is the track time scale. Range is 0 to UINT_MAX. Default is @code{UINT_MAX - 48000*10} which allows up to a 10 second dts correction for 48 kHz audio streams while accommodating 99.9% of @code{uint32} range. + +@item interleaved_read +Interleave packets from multiple tracks at demuxer level. For badly interleaved files, this prevents playback issues +caused by large gaps between packets in different tracks, as MOV/MP4 do not have packet placement requirements. +However, this can cause excessive seeking on very badly interleaved files, due to seeking between tracks, so disabling +it may prevent I/O issues, at the expense of playback. + @end table @subsection Audible AAX @@ -928,4 +1105,27 @@ which in turn, acts as a ceiling for the size of scripts that can be read. Default is 1 MiB. @end table +@section w64 + +Sony Wave64 Audio demuxer. + +This demuxer accepts the following options: +@table @option +@item max_size +See the same option for the @ref{wav} demuxer. +@end table + +@anchor{wav} +@section wav + +RIFF Wave Audio demuxer. + +This demuxer accepts the following options: +@table @option +@item max_size +Specify the maximum packet size in bytes for the demuxed packets. By default +this is set to 0, which means that a sensible value is chosen based on the +input format. +@end table + @c man end DEMUXERS diff --git a/doc/dev_community/community.md b/doc/dev_community/community.md deleted file mode 100644 index 516ca5c05e7..00000000000 --- a/doc/dev_community/community.md +++ /dev/null @@ -1,82 +0,0 @@ -# FFmpeg project - -## Organisation - -The FFmpeg project is organized through a community working on global consensus. - -Decisions are taken by the ensemble of active members, through voting and -are aided by two committees. - -## General Assembly - -The ensemble of active members is called the General Assembly (GA). - -The General Assembly is sovereign and legitimate for all its decisions -regarding the FFmpeg project. - -The General Assembly is made up of active contributors. - -Contributors are considered "active contributors" if they have pushed more -than 20 patches in the last 36 months in the main FFmpeg repository, or -if they have been voted in by the GA. - -Additional members are added to the General Assembly through a vote after -proposal by a member of the General Assembly. -They are part of the GA for two years, after which they need a confirmation by -the GA. - -A script to generate the current members of the general assembly (minus members -voted in) can be found in `tools/general_assembly.pl`. - -## Voting - -Voting is done using a ranked voting system, currently running on https://vote.ffmpeg.org/ . - -Majority vote means more than 50% of the expressed ballots. - -## Technical Committee - -The Technical Committee (TC) is here to arbitrate and make decisions when -technical conflicts occur in the project. -They will consider the merits of all the positions, judge them and make a -decision. - -The TC resolves technical conflicts but is not a technical steering committee. - -Decisions by the TC are binding for all the contributors. - -Decisions made by the TC can be re-opened after 1 year or by a majority vote -of the General Assembly, requested by one of the member of the GA. - -The TC is elected by the General Assembly for a duration of 1 year, and -is composed of 5 members. -Members can be re-elected if they wish. A majority vote in the General Assembly -can trigger a new election of the TC. - -The members of the TC can be elected from outside of the GA. -Candidates for election can either be suggested or self-nominated. - -The conflict resolution process is detailed in the [resolution process](resolution_process.md) document. - -## Community committee - -The Community Committee (CC) is here to arbitrage and make decisions when -inter-personal conflicts occur in the project. It will decide quickly and -take actions, for the sake of the project. - -The CC can remove privileges of offending members, including removal of -commit access and temporary ban from the community. - -Decisions made by the CC can be re-opened after 1 year or by a majority vote -of the General Assembly. Indefinite bans from the community must be confirmed -by the General Assembly, in a majority vote. - -The CC is elected by the General Assembly for a duration of 1 year, and is -composed of 5 members. -Members can be re-elected if they wish. A majority vote in the General Assembly -can trigger a new election of the CC. - -The members of the CC can be elected from outside of the GA. -Candidates for election can either be suggested or self-nominated. - -The CC is governed by and responsible for enforcing the Code of Conduct. diff --git a/doc/dev_community/resolution_process.md b/doc/dev_community/resolution_process.md deleted file mode 100644 index 4ed0b63c433..00000000000 --- a/doc/dev_community/resolution_process.md +++ /dev/null @@ -1,91 +0,0 @@ -# Technical Committee - -_This document only makes sense with the rules from [the community document](community)_. - -The Technical Committee (**TC**) is here to arbitrate and make decisions when -technical conflicts occur in the project. - -The TC main role is to resolve technical conflicts. -It is therefore not a technical steering committee, but it is understood that -some decisions might impact the future of the project. - -# Process - -## Seizing - -The TC can take possession of any technical matter that it sees fit. - -To involve the TC in a matter, email tc@ or CC them on an ongoing discussion. - -As members of TC are developers, they also can email tc@ to raise an issue. - -## Announcement - -The TC, once seized, must announce itself on the main mailing list, with a _[TC]_ tag. - -The TC has 2 modes of operation: a RFC one and an internal one. - -If the TC thinks it needs the input from the larger community, the TC can call -for a RFC. Else, it can decide by itself. - -If the disagreement involves a member of the TC, that member should recuse -themselves from the decision. - -The decision to use a RFC process or an internal discussion is a discretionary -decision of the TC. - -The TC can also reject a seizure for a few reasons such as: -the matter was not discussed enough previously; it lacks expertise to reach a -beneficial decision on the matter; or the matter is too trivial. - -### RFC call - -In the RFC mode, one person from the TC posts on the mailing list the -technical question and will request input from the community. - -The mail will have the following specification: -* a precise title -* a specific tag [TC RFC] -* a top-level email -* contain a precise question that does not exceed 100 words and that is answerable by developers -* may have an extra description, or a link to a previous discussion, if deemed necessary, -* contain a precise end date for the answers. - -The answers from the community must be on the main mailing list and must have -the following specification: -* keep the tag and the title unchanged -* limited to 400 words -* a first-level, answering directly to the main email -* answering to the question. - -Further replies to answers are permitted, as long as they conform to the -community standards of politeness, they are limited to 100 words, and are not -nested more than once. (max-depth=2) - -After the end-date, mails on the thread will be ignored. - -Violations of those rules will be escalated through the Community Committee. - -After all the emails are in, the TC has 96 hours to give its final decision. -Exceptionally, the TC can request an extra delay, that will be notified on the -mailing list. - -### Within TC - -In the internal case, the TC has 96 hours to give its final decision. -Exceptionally, the TC can request an extra delay. - - -## Decisions - -The decisions from the TC will be sent on the mailing list, with the _[TC]_ tag. - -Internally, the TC should take decisions with a majority, or using -ranked-choice voting. - -The decision from the TC should be published with a summary of the reasons that -lead to this decision. - -The decisions from the TC are final, until the matters are reopened after -no less than one year. - diff --git a/doc/developer.texi b/doc/developer.texi index 31b485b0f62..ed998adecb6 100644 --- a/doc/developer.texi +++ b/doc/developer.texi @@ -24,6 +24,10 @@ generated from the headers the examples under @file{doc/examples} @end itemize +For more detailed legal information about the use of FFmpeg in +external programs read the @file{LICENSE} file in the source tree and +consult @url{https://ffmpeg.org/legal.html}. + If you modify FFmpeg code for your own use case, you are highly encouraged to @emph{submit your changes back to us}, using this document as a guide. There are both pragmatic and ideological reasons to do so: @@ -40,12 +44,6 @@ By supporting the project you find useful you ensure it continues to be maintained and developed. @end itemize -For more detailed legal information about the use of FFmpeg in -external programs read the @file{LICENSE} file in the source tree and -consult @url{https://ffmpeg.org/legal.html}. - -@section Contributing code - All proposed code changes should be submitted for review to @url{mailto:ffmpeg-devel@@ffmpeg.org, the development mailing list}, as described in more detail in the @ref{Submitting patches} chapter. The code @@ -56,16 +54,10 @@ and should try to fix issues their commit causes. @anchor{Coding Rules} @chapter Coding Rules -@section C language features +@section Language -FFmpeg is programmed in the ISO C99 language, extended with: -@itemize @bullet -@item -Atomic operations from C11 @file{stdatomic.h}. They are emulated on -architectures/compilers that do not support them, so all FFmpeg-internal code -may use atomics without any extra checks. However, @file{stdatomic.h} must not -be included in public headers, so they stay C99-compatible. -@end itemize +FFmpeg is mainly programmed in the ISO C11 language, except for the public +headers which must stay C99 compatible. Compiler-specific extensions may be used with good reason, but must not be depended on, i.e. the code must still compile and work with compilers lacking @@ -83,6 +75,44 @@ complex numbers; mixed statements and declarations. @end itemize +@subsection SIMD/DSP +@anchor{SIMD/DSP} + +As modern compilers are unable to generate efficient SIMD or other +performance-critical DSP code from plain C, handwritten assembly is used. +Usually such code is isolated in a separate function. Then the standard approach +is writing multiple versions of this function – a plain C one that works +everywhere and may also be useful for debugging, and potentially multiple +architecture-specific optimized implementations. Initialization code then +chooses the best available version at runtime and loads it into a function +pointer; the function in question is then always called through this pointer. + +The specific syntax used for writing assembly is: +@itemize @bullet +@item +NASM on x86; + +@item +GAS on ARM and RISC-V. +@end itemize + +A unit testing framework for assembly called @code{checkasm} lives under +@file{tests/checkasm}. All new assembly should come with @code{checkasm} tests; +adding tests for existing assembly that lacks them is also strongly encouraged. + +@subsection Other languages + +Other languages than C may be used in special cases: +@itemize @bullet +@item +Compiler intrinsics or inline assembly when the code in question cannot be +written in the standard way described in the @ref{SIMD/DSP} section. This +typically applies to code that needs to be inlined. + +@item +Objective-C where required for interacting with macOS-specific interfaces. +@end itemize + @section Code formatting conventions There are the following guidelines regarding the indentation in files: @@ -179,6 +209,7 @@ int myfunc(int my_parameter) ... @end example +@anchor{Naming conventions} @section Naming conventions Names of functions, variables, and struct members must be lowercase, using @@ -237,10 +268,6 @@ symbols. If in doubt, just avoid names starting with @code{_} altogether. @section Miscellaneous conventions @itemize @bullet -@item -fprintf and printf are forbidden in libavformat and libavcodec, -please use av_log() instead. - @item Casts should be used only when necessary. Unneeded parentheses should also be avoided if they don't make the code easier to understand. @@ -249,6 +276,42 @@ should also be avoided if they don't make the code easier to understand. @anchor{Development Policy} @chapter Development Policy +@section Code behaviour + +@subheading Correctness +The code must be valid. It must not crash, abort, access invalid pointers, leak +memory, cause data races or signed integer overflow, or otherwise cause +undefined behaviour. Error codes should be checked and, when applicable, +forwarded to the caller. + +@subheading Thread- and library-safety +Our libraries may be called by multiple independent callers in the same process. +These calls may happen from any number of threads and the different call sites +may not be aware of each other - e.g. a user program may be calling our +libraries directly, and use one or more libraries that also call our libraries. +The code must behave correctly under such conditions. + +@subheading Robustness +The code must treat as untrusted any bytestream received from a caller or read +from a file, network, etc. It must not misbehave when arbitrary data is sent to +it - typically it should print an error message and return +@code{AVERROR_INVALIDDATA} on encountering invalid input data. + +@subheading Memory allocation +The code must use the @code{av_malloc()} family of functions from +@file{libavutil/mem.h} to perform all memory allocation, except in special cases +(e.g. when interacting with an external library that requires a specific +allocator to be used). + +All allocations should be checked and @code{AVERROR(ENOMEM)} returned on +failure. A common mistake is that error paths leak memory - make sure that does +not happen. + +@subheading stdio +Our libraries must not access the stdio streams stdin/stdout/stderr directly +(e.g. via @code{printf()} family of functions), as that is not library-safe. For +logging, use @code{av_log()}. + @section Patches/Committing @subheading Licenses for patches must be compatible with FFmpeg. Contributions should be licensed under the @@ -271,13 +334,24 @@ missing samples or an implementation with a small subset of features. Always check the mailing list for any reviewers with issues and test FATE before you push. -@subheading Keep the main commit message short with an extended description below. -The commit message should have a short first line in the form of -a @samp{topic: short description} as a header, separated by a newline -from the body consisting of an explanation of why the change is necessary. -If the commit fixes a known bug on the bug tracker, the commit message -should include its bug ID. Referring to the issue on the bug tracker does -not exempt you from writing an excerpt of the bug in the commit message. +@subheading Commit messages +Commit messages are highly important tools for informing other developers on +what a given change does and why. Every commit must always have a properly +filled out commit message with the following format: +@example +area changed: short 1 line description + +details describing what and why and giving references. +@end example + +If the commit addresses a known bug on our bug tracker or other external issue +(e.g. CVE), the commit message should include the relevant bug ID(s) or other +external identifiers. Note that this should be done in addition to a proper +explanation and not instead of it. Comments such as "fixed!" or "Changed it." +are not acceptable. + +When applying patches that have been discussed at length on the mailing list, +reference the thread in the commit message. @subheading Testing must be adequate but not excessive. If it works for you, others, and passes FATE then it should be OK to commit @@ -296,15 +370,6 @@ later on. Also if you have doubts about splitting or not splitting, do not hesitate to ask/discuss it on the developer mailing list. -@subheading Ask before you change the build system (configure, etc). -Do not commit changes to the build system (Makefiles, configure script) -which change behavior, defaults etc, without asking first. The same -applies to compiler warning fixes, trivial looking fixes and to code -maintained by other developers. We usually have a reason for doing things -the way we do. Send your changes as patches to the ffmpeg-devel mailing -list, and if the code maintainers say OK, you may commit. This does not -apply to files you wrote and/or maintain. - @subheading Cosmetic changes should be kept in separate patches. We refuse source indentation and other cosmetic changes if they are mixed with functional changes, such commits will be rejected and removed. Every @@ -319,27 +384,15 @@ NOTE: If you had to put if()@{ .. @} over a large (> 5 lines) chunk of code, then either do NOT change the indentation of the inner part within (do not move it to the right)! or do so in a separate commit -@subheading Commit messages should always be filled out properly. -Always fill out the commit log message. Describe in a few lines what you -changed and why. You can refer to mailing list postings if you fix a -particular bug. Comments such as "fixed!" or "Changed it." are unacceptable. -Recommended format: - -@example -area changed: Short 1 line description - -details describing what and why and giving references. -@end example - @subheading Credit the author of the patch. Make sure the author of the commit is set correctly. (see git commit --author) If you apply a patch, send an answer to ffmpeg-devel (or wherever you got the patch from) saying that you applied the patch. -@subheading Complex patches should refer to discussion surrounding them. -When applying patches that have been discussed (at length) on the mailing -list, reference the thread in the log message. +@subheading Credit any researchers +If a commit/patch fixes an issues found by some researcher, always credit the +researcher in the commit message for finding/reporting the issue. @subheading Always wait long enough before pushing changes Do NOT commit to code actively maintained by others without permission. @@ -349,22 +402,6 @@ time-frame (12h for build failures and security fixes, 3 days small changes, Also note, the maintainer can simply ask for more time to review! @section Code -@subheading API/ABI changes should be discussed before they are made. -Do not change behavior of the programs (renaming options etc) or public -API or ABI without first discussing it on the ffmpeg-devel mailing list. -Do not remove widely used functionality or features (redundant code can be removed). - -@subheading Remember to check if you need to bump versions for libav*. -Depending on the change, you may need to change the version integer. -Incrementing the first component means no backward compatibility to -previous versions (e.g. removal of a function from the public API). -Incrementing the second component means backward compatible change -(e.g. addition of a function to the public API or extension of an -existing data structure). -Incrementing the third component means a noteworthy binary compatible -change (e.g. encoder bug fix that matters for the decoder). The third -component always starts at 100 to distinguish FFmpeg from Libav. - @subheading Warnings for correct code may be disabled if there is no other option. Compiler warnings indicate potential bugs or code with bad style. If a type of warning always points to correct and clean code, that warning should @@ -374,10 +411,150 @@ If it is a bug, the bug has to be fixed. If it is not, the code should be changed to not generate a warning unless that causes a slowdown or obfuscates the code. -@subheading Check untrusted input properly. -Never write to unallocated memory, never write over the end of arrays, -always check values read from some untrusted source before using them -as array index or other risky things. +@section Library public interfaces +Every library in FFmpeg provides a set of public APIs in its installed headers, +which are those listed in the variable @code{HEADERS} in that library's +@file{Makefile}. All identifiers defined in those headers (except for those +explicitly documented otherwise), and corresponding symbols exported from +compiled shared or static libraries are considered public interfaces and must +comply with the API and ABI compatibility rules described in this section. + +Public APIs must be backward compatible within a given major version. I.e. any +valid user code that compiles and works with a given library version must still +compile and work with any later version, as long as the major version number is +unchanged. "Valid user code" here means code that is calling our APIs in a +documented and/or intended manner and is not relying on any undefined behavior. +Incrementing the major version may break backward compatibility, but only to the +extent described in @ref{Major version bumps}. + +We also guarantee backward ABI compatibility for shared and static libraries. +I.e. it should be possible to replace a shared or static build of our library +with a build of any later version (re-linking the user binary in the static +case) without breaking any valid user binaries, as long as the major version +number remains unchanged. + +@subsection Adding new interfaces +Any new public identifiers in installed headers are considered new API - this +includes new functions, structs, macros, enum values, typedefs, new fields in +existing structs, new installed headers, etc. Consider the following +guidelines when adding new APIs. + +@subsubheading Motivation +While new APIs can be added relatively easily, changing or removing them is much +harder due to abovementioned compatibility requirements. You should then +consider carefully whether the functionality you are adding really needs to be +exposed to our callers as new public API. + +Your new API should have at least one well-established use case outside of the +library that cannot be easily achieved with existing APIs. Every library in +FFmpeg also has a defined scope - your new API must fit within it. + +@subsubheading Replacing existing APIs +If your new API is replacing an existing one, it should be strictly superior to +it, so that the advantages of using the new API outweight the cost to the +callers of changing their code. After adding the new API you should then +deprecate the old one and schedule it for removal, as described in +@ref{Removing interfaces}. + +If you deem an existing API deficient and want to fix it, the preferred approach +in most cases is to add a differently-named replacement and deprecate the +existing API rather than modify it. It is important to make the changes visible +to our callers (e.g. through compile- or run-time deprecation warnings) and make +it clear how to transition to the new API (e.g. in the Doxygen documentation or +on the wiki). + +@subsubheading API design +The FFmpeg libraries are used by a variety of callers to perform a wide range of +multimedia-related processing tasks. You should therefore - within reason - try +to design your new API for the broadest feasible set of use cases and avoid +unnecessarily limiting it to a specific type of callers (e.g. just media +playback or just transcoding). + +@subsubheading Consistency +Check whether similar APIs already exist in FFmpeg. If they do, try to model +your new addition on them to achieve better overall consistency. + +The naming of your new identifiers should follow the @ref{Naming conventions} +and be aligned with other similar APIs, if applicable. + +@subsubheading Extensibility +You should also consider how your API might be extended in the future in a +backward-compatible way. If you are adding a new struct @code{AVFoo}, the +standard approach is requiring the caller to always allocate it through a +constructor function, typically named @code{av_foo_alloc()}. This way new fields +may be added to the end of the struct without breaking ABI compatibility. +Typically you will also want a destructor - @code{av_foo_free(AVFoo**)} that +frees the indirectly supplied object (and its contents, if applicable) and +writes @code{NULL} to the supplied pointer, thus eliminating the potential +dangling pointer in the caller's memory. + +If you are adding new functions, consider whether it might be desirable to tweak +their behavior in the future - you may want to add a flags argument, even though +it would be unused initially. + +@subsubheading Documentation +All new APIs must be documented as Doxygen-formatted comments above the +identifiers you add to the public headers. You should also briefly mention the +change in @file{doc/APIchanges}. + +@subsubheading Bump the version +Backward-incompatible API or ABI changes require incrementing (bumping) the +major version number, as described in @ref{Major version bumps}. Major +bumps are significant events that happen on a schedule - so if your change +strictly requires one you should add it under @code{#if} preprocesor guards that +disable it until the next major bump happens. + +New APIs that can be added without breaking API or ABI compatibility require +bumping the minor version number. + +Incrementing the third (micro) version component means a noteworthy binary +compatible change (e.g. encoder bug fix that matters for the decoder). The third +component always starts at 100 to distinguish FFmpeg from Libav. + +@anchor{Removing interfaces} +@subsection Removing interfaces +Due to abovementioned compatibility guarantees, removing APIs is an involved +process that should only be undertaken with good reason. Typically a deficient, +restrictive, or otherwise inadequate API is replaced by a superior one, though +it does at times happen that we remove an API without any replacement (e.g. when +the feature it provides is deemed not worth the maintenance effort, out of scope +of the project, fundamentally flawed, etc.). + +The removal has two steps - first the API is deprecated and scheduled for +removal, but remains present and functional. The second step is actually +removing the API - this is described in @ref{Major version bumps}. + +To deprecate an API you should signal to our users that they should stop using +it. E.g. if you intend to remove struct members or functions, you should mark +them with @code{attribute_deprecated}. When this cannot be done, it may be +possible to detect the use of the deprecated API at runtime and print a warning +(though take care not to print it too often). You should also document the +deprecation (and the replacement, if applicable) in the relevant Doxygen +documentation block. + +Finally, you should define a deprecation guard along the lines of +@code{#define FF_API_ (LIBAVBAR_VERSION_MAJOR < XX)} (where XX is the major +version in which the API will be removed) in @file{libavbar/version_major.h} +(@file{version.h} in case of @code{libavutil}). Then wrap all uses of the +deprecated API in @code{#if FF_API_ .... #endif}, so that the code will +automatically get disabled once the major version reaches XX. You can also use +@code{FF_DISABLE_DEPRECATION_WARNINGS} and @code{FF_ENABLE_DEPRECATION_WARNINGS} +to suppress compiler deprecation warnings inside these guards. You should test +that the code compiles and works with the guard macro evaluating to both true +and false. + +@anchor{Major version bumps} +@subsection Major version bumps +A major version bump signifies an API and/or ABI compatibility break. To reduce +the negative effects on our callers, who are required to adapt their code, +backward-incompatible changes during a major bump should be limited to: +@itemize @bullet +@item +Removing previously deprecated APIs. + +@item +Performing ABI- but not API-breaking changes, like reordering struct contents. +@end itemize @section Documentation/Other @subheading Subscribe to the ffmpeg-devel mailing list. @@ -421,35 +598,6 @@ finding a new maintainer and also don't forget to update the @file{MAINTAINERS} We think our rules are not too hard. If you have comments, contact us. -@chapter Code of conduct - -Be friendly and respectful towards others and third parties. -Treat others the way you yourself want to be treated. - -Be considerate. Not everyone shares the same viewpoint and priorities as you do. -Different opinions and interpretations help the project. -Looking at issues from a different perspective assists development. - -Do not assume malice for things that can be attributed to incompetence. Even if -it is malice, it's rarely good to start with that as initial assumption. - -Stay friendly even if someone acts contrarily. Everyone has a bad day -once in a while. -If you yourself have a bad day or are angry then try to take a break and reply -once you are calm and without anger if you have to. - -Try to help other team members and cooperate if you can. - -The goal of software development is to create technical excellence, not for any -individual to be better and "win" against the others. Large software projects -are only possible and successful through teamwork. - -If someone struggles do not put them down. Give them a helping hand -instead and point them in the right direction. - -Finally, keep in mind the immortal words of Bill and Ted, -"Be excellent to each other." - @anchor{Submitting patches} @chapter Submitting patches @@ -490,6 +638,11 @@ patch is inline or attached per mail. You can check @url{https://patchwork.ffmpeg.org}, if your patch does not show up, its mime type likely was wrong. +@subheading How to setup git send-email? + +Please see @url{https://git-send-email.io/}. +For gmail additionally see @url{https://shallowsky.com/blog/tech/email/gmail-app-passwds.html}. + @subheading Sending patches from email clients Using @code{git send-email} might not be desirable for everyone. The following trick allows to send patches via email clients in a safe @@ -666,16 +819,14 @@ Lines with similar content should be aligned vertically when doing so improves readability. @item -Consider adding a regression test for your code. +Consider adding a regression test for your code. All new modules +should be covered by tests. That includes demuxers, muxers, decoders, encoders +filters, bitstream filters, parsers. If its not possible to do that, add +an explanation why to your patchset, its ok to not test if theres a reason. @item If you added YASM code please check that things still work with --disable-yasm. -@item -Make sure you check the return values of function and return appropriate -error codes. Especially memory allocation functions like @code{av_malloc()} -are notoriously left unchecked, which is a serious problem. - @item Test your code with valgrind and or Address Sanitizer to ensure it's free of leaks, out of array accesses, etc. @@ -725,6 +876,8 @@ accordingly]. @section Adding files to the fate-suite dataset +If you need a sample uploaded send a mail to samples-request. + When there is no muxer or encoder available to generate test media for a specific test then the media has to be included in the fate-suite. First please make sure that the sample file is as small as possible to test the diff --git a/doc/encoders.texi b/doc/encoders.texi index b02737b9dfb..840382a25ac 100644 --- a/doc/encoders.texi +++ b/doc/encoders.texi @@ -615,7 +615,7 @@ and slightly improves compression. Opus encoder. -This is a native FFmpeg encoder for the Opus format. Currently its in development and +This is a native FFmpeg encoder for the Opus format. Currently, it's in development and only implements the CELT part of the codec. Its quality is usually worse and at best is equal to the libopus encoder. @@ -789,6 +789,11 @@ about 80-96 kbps/channel @end table Default value is 0. + +@item frame_length +Set the audio frame length in samples. Default value is the internal +default of the library. Refer to the library's documentation for information +about supported values. @end table @subsection Examples @@ -859,6 +864,13 @@ Enable the encoder to use ABR when set to 1. The @command{lame} @option{--abr} sets the target bitrate, while this options only tells FFmpeg to use ABR still relies on @option{b} to set bitrate. +@item copyright (@emph{-c}) +Set MPEG audio copyright flag when set to 1. The default value is 0 +(disabled). + +@item original (@emph{-o}) +Set MPEG audio original flag when set to 1. The default value is 1 +(enabled). @end table @section libopencore-amrnb @@ -965,14 +977,17 @@ Favor improved speech intelligibility. @item audio Favor faithfulness to the input (the default). @item lowdelay -Restrict to only the lowest delay modes. +Restrict to only the lowest delay modes by disabling voice-optimized +modes. @end table @item cutoff (N.A.) Set cutoff bandwidth in Hz. The argument must be exactly one of the following: 4000, 6000, 8000, 12000, or 20000, corresponding to narrowband, mediumband, wideband, super wideband, and fullband -respectively. The default is 0 (cutoff disabled). +respectively. The default is 0 (cutoff disabled). Note that libopus +forces a wideband cutoff for bitrates < 15 kbps, unless CELT-only +(@option{application} set to @samp{lowdelay}) mode is used. @item mapping_family (@emph{mapping_family}) Set channel mapping family to be used by the encoder. The default value of -1 @@ -2222,6 +2237,12 @@ the two temporal layer 2 frames within the temporal period. @end table @end table +@item VP8-specific options +@table @option +@item screen-content-mode +Screen content mode, one of: 0 (off), 1 (screen), 2 (screen with more aggressive rate control). +@end table + @item VP9-specific options @table @option @item lossless @@ -2406,6 +2427,10 @@ To get a more accurate and extensive documentation of the libx264 options, invoke the command @command{x264 --fullhelp} or consult the libx264 documentation. +In the list below, note that the @command{x264} option name is shown +in parentheses after the libavcodec corresponding name, in case there +is a direct mapping. + @table @option @item b (@emph{bitrate}) Set bitrate in bits/s. Note that FFmpeg's @option{b} option is @@ -2413,17 +2438,19 @@ expressed in bits/s, while @command{x264}'s @option{bitrate} is in kilobits/s. @item bf (@emph{bframes}) +Number of B-frames between I and P-frames @item g (@emph{keyint}) +Maximum GOP size @item qmin (@emph{qpmin}) -Minimum quantizer scale. +Minimum quantizer scale @item qmax (@emph{qpmax}) -Maximum quantizer scale. +Maximum quantizer scale @item qdiff (@emph{qpstep}) -Maximum difference between quantizer scales. +Maximum difference between quantizer scales @item qblur (@emph{qblur}) Quantizer curve blur @@ -2432,7 +2459,21 @@ Quantizer curve blur Quantizer curve compression factor @item refs (@emph{ref}) -Number of reference frames each P-frame can use. The range is from @var{0-16}. +Number of reference frames each P-frame can use. The range is @var{0-16}. + +@item level (@emph{level}) +Set the @code{x264_param_t.i_level_idc} value in case the value is +positive, it is ignored otherwise. + +This value can be set using the @code{AVCodecContext} API (e.g. by +setting the @code{AVCodecContext} value directly), and is specified as +an integer mapped on a corresponding level (e.g. the value 31 maps +to H.264 level IDC "3.1", as defined in the @code{x264_levels} +table). It is ignored when set to a non positive value. + +Alternatively it can be set as a private option, overriding the value +set in @code{AVCodecContext}, and in this case must be specified as +the level IDC identifier (e.g. "3.1"), as defined by H.264 Annex A. @item sc_threshold (@emph{scenecut}) Sets the threshold for the scene change detection. @@ -2440,7 +2481,8 @@ Sets the threshold for the scene change detection. @item trellis (@emph{trellis}) Performs Trellis quantization to increase efficiency. Enabled by default. -@item nr (@emph{nr}) +@item nr (@emph{nr}) +Noise reduction @item me_range (@emph{merange}) Maximum range of the motion search in pixels. @@ -2521,6 +2563,7 @@ open GOP by setting it to @code{-cgop}. The result is similar to the behavior of @command{x264}'s @option{--open-gop} option. @item rc_init_occupancy (@emph{vbv-init}) +Initial VBV buffer occupancy @item preset (@emph{preset}) Set the encoding preset. @@ -2566,7 +2609,7 @@ Set AQ strength, reduce blocking and blurring in flat and textured areas. Use psychovisual optimizations when set to 1. When set to 0, it has the same effect as @command{x264}'s @option{--no-psy} option. -@item psy-rd (@emph{psy-rd}) +@item psy-rd (@emph{psy-rd}) Set strength of psychovisual optimization, in @var{psy-rd}:@var{psy-trellis} format. @@ -2598,7 +2641,7 @@ to 1. @item avcintra-class (@emph{class}) Configure the encoder to generate AVC-Intra. -Valid values are 50,100 and 200 +Valid values are 50, 100 and 200 @item bluray-compat (@emph{bluray-compat}) Configure the encoder to be compatible with the bluray standard. @@ -2649,8 +2692,8 @@ Set loop filter parameters, in @var{alpha}:@var{beta} form. Set fluctuations reduction in QP (before curve compression). @item partitions (@emph{partitions}) -Set partitions to consider as a comma-separated list of. Possible -values in the list: +Set partitions to consider as a comma-separated list of values. +Possible values in the list: @table @samp @item p8x8 @@ -2706,19 +2749,32 @@ Variable bit rate. Constant bit rate (not allowed in MP4 container). @end table -@item x264opts (N.A.) -Set any x264 option, see @command{x264 --fullhelp} for a list. +@item x264opts @var{opts} +@item x264-params @var{opts} +Override the x264 configuration using a :-separated list of key=value +options. + +The argument for both options is a list of @var{key}=@var{value} +couples separated by ":". With @option{x264opts} the value can be +omitted, and the value @code{1} is assumed in that case. + +For @var{filter} and @var{psy-rd} options values that use ":" as a +separator themselves, use "," instead. They accept it as well since +long ago but this is kept undocumented for some reason. -Argument is a list of @var{key}=@var{value} couples separated by -":". In @var{filter} and @var{psy-rd} options that use ":" as a separator -themselves, use "," instead. They accept it as well since long ago but this -is kept undocumented for some reason. +For example, the options might be provided as: +@example +level=30:bframes=0:weightp=0:cabac=0:ref=1:vbv-maxrate=768:vbv-bufsize=2000:analyse=all:me=umh:no-fast-pskip=1:subq=6:8x8dct=0:trellis=0 +@end example For example to specify libx264 encoding options with @command{ffmpeg}: @example ffmpeg -i foo.mpg -c:v libx264 -x264opts keyint=123:min-keyint=20 -an out.mkv @end example +To get the complete list of the libx264 options, invoke the command +@command{x264 --fullhelp} or consult the libx264 documentation. + @item a53cc @var{boolean} Import closed captions (which must be ATSC compatible format) into output. Only the mpeg2 and h264 decoders provide these. Default is 1 (on). @@ -2726,19 +2782,9 @@ Only the mpeg2 and h264 decoders provide these. Default is 1 (on). @item udu_sei @var{boolean} Import user data unregistered SEI if available into output. Default is 0 (off). -@item x264-params (N.A.) -Override the x264 configuration using a :-separated list of key=value -parameters. - -This option is functionally the same as the @option{x264opts}, but is -duplicated for compatibility with the Libav fork. - -For example to specify libx264 encoding options with @command{ffmpeg}: -@example -ffmpeg -i INPUT -c:v libx264 -x264-params level=30:bframes=0:weightp=0:\ -cabac=0:ref=1:vbv-maxrate=768:vbv-bufsize=2000:analyse=all:me=umh:\ -no-fast-pskip=1:subq=6:8x8dct=0:trellis=0 OUTPUT -@end example +@item mb_info @var{boolean} +Set mb_info data through AVFrameSideData, only useful when used from the +API. Default is 0 (off). @end table Encoding ffpresets for common usages are provided so they can be used with the @@ -2878,6 +2924,75 @@ ffmpeg -i input -c:v libxavs2 -xavs2-params RdoqLevel=0 output.avs2 @end example @end table +@section libxeve + +eXtra-fast Essential Video Encoder (XEVE) MPEG-5 EVC encoder wrapper. +The xeve-equivalent options or values are listed in parentheses for easy migration. + +This encoder requires the presence of the libxeve headers and library +during configuration. You need to explicitly configure the build with +@option{--enable-libxeve}. + +@float NOTE +Many libxeve encoder options are mapped to FFmpeg global codec options, +while unique encoder options are provided through private options. +Additionally the xeve-params private options allows one to pass a list +of key=value tuples as accepted by the libxeve @code{parse_xeve_params} function. +@end float + +The xeve project website is at @url{https://github.com/mpeg5/xeve}. + +@subsection Options + +The following options are supported by the libxeve wrapper. +The xeve-equivalent options or values are listed in parentheses for easy migration. + +@float NOTE +To reduce the duplication of documentation, only the private options +and some others requiring special attention are documented here. For +the documentation of the undocumented generic options, see +@ref{codec-options,,the Codec Options chapter}. +@end float + +@float NOTE +To get a more accurate and extensive documentation of the libxeve options, +invoke the command @code{xeve_app --help} or consult the libxeve documentation. +@end float + +@table @option +@item b (@emph{bitrate}) +Set target video bitrate in bits/s. +Note that FFmpeg's b option is expressed in bits/s, while xeve's bitrate is in kilobits/s. + +@item bf (@emph{bframes}) +Set the maximum number of B frames (1,3,7,15). + +@item g (@emph{keyint}) +Set the GOP size (I-picture period). + +@item preset (@emph{preset}) +Set the xeve preset. +Set the encoder preset value to determine encoding speed [fast, medium, slow, placebo] + +@item tune (@emph{tune}) +Set the encoder tune parameter [psnr, zerolatency] + +@item profile (@emph{profile}) +Set the encoder profile [0: baseline; 1: main] + +@item crf (@emph{crf}) +Set the quality for constant quality mode. +Constant rate factor <10..49> [default: 32] + +@item qp (@emph{qp}) +Set constant quantization rate control method parameter. +Quantization parameter qp <0..51> [default: 32] + +@item threads (@emph{threads}) +Force to use a specific number of threads + +@end table + @section libxvid Xvid MPEG-4 Part 2 encoder wrapper. @@ -2930,9 +3045,6 @@ Enable high quality AC prediction. @item gray Only encode grayscale. -@item gmc -Enable the use of global motion compensation (GMC). - @item qpel Enable quarter-pixel motion compensation. @@ -2944,29 +3056,28 @@ Place global headers in extradata instead of every keyframe. @end table -@item trellis +@item gmc +Enable the use of global motion compensation (GMC). Default is 0 +(disabled). -@item me_method -Set motion estimation method. Possible values in decreasing order of +@item me_quality +Set motion estimation quality level. Possible values in decreasing order of speed and increasing order of quality: @table @samp -@item zero +@item 0 Use no motion estimation (default). -@item phods -@item x1 -@item log +@item 1, 2 Enable advanced diamond zonal search for 16x16 blocks and half-pixel -refinement for 16x16 blocks. @samp{x1} and @samp{log} are aliases for -@samp{phods}. +refinement for 16x16 blocks. -@item epzs +@item 3, 4 Enable all of the things described above, plus advanced diamond zonal -search for 8x8 blocks, half-pixel refinement for 8x8 blocks, and motion -estimation on chroma planes. +search for 8x8 blocks and half-pixel refinement for 8x8 blocks, also +enable motion estimation on chroma planes for P and B-frames. -@item full +@item 5, 6 Enable all of the things described above, plus extended 16x16 and 8x8 blocks search. @end table @@ -3002,6 +3113,9 @@ be better than any of the two specified individually. In other words, the resulting quality will be the worse one of the two effects. +@item trellis +Set rate-distortion optimal quantization. + @item ssim Set structural similarity (SSIM) displaying method. Possible values: @@ -3049,6 +3163,20 @@ Video encoders can take input in either of nv12 or yuv420p form (some encoders support both, some support only either - in practice, nv12 is the safer choice, especially among HW encoders). +@section Microsoft RLE + +Microsoft RLE aka MSRLE encoder. +Only 8-bit palette mode supported. +Compatible with Windows 3.1 and Windows 95. + +@subsection Options + +@table @option +@item g @var{integer} +Keyframe interval. +A keyframe is inserted at least every @code{-g} frames, sometimes sooner. +@end table + @section mpeg2 MPEG-2 video encoder. @@ -3188,8 +3316,8 @@ recommended value) and do not set a size constraint. @section QSV Encoders -The family of Intel QuickSync Video encoders (MPEG-2, H.264, HEVC, JPEG/MJPEG -and VP9) +The family of Intel QuickSync Video encoders (MPEG-2, H.264, HEVC, JPEG/MJPEG, +VP9, AV1) @subsection Ratecontrol Method The ratecontrol method is selected as follows: @@ -3212,8 +3340,8 @@ quality range is 1 to 51, with 1 being the best quality. @end itemize @item -Otherwise, a bitrate-based mode is used. For all of those, you should specify at -least the desired average bitrate with the @option{b} option. +Otherwise when the desired average bitrate is specified with the @option{b} +option, a bitrate-based mode is used. @itemize @minus @item @var{LA} - VBR with lookahead, when the @option{look_ahead} option is specified. @@ -3234,6 +3362,9 @@ than the average bitrate. @option{avbr_accuracy} and @option{avbr_convergence} are set to non-zero. This mode is available for H264 and HEVC on Windows. @end itemize + +@item +Otherwise the default ratecontrol method @var{CQP} is used. @end itemize Note that depending on your system, a different mode than the one you specified @@ -3344,7 +3475,7 @@ Supported in h264_qsv. Change these value to reset qsv codec's max/min qp configuration. @item @var{low_delay_brc} -Supported in h264_qsv and hevc_qsv. +Supported in h264_qsv, hevc_qsv and av1_qsv. Change this value to reset qsv codec's low_delay_brc configuration. @item @var{framerate} @@ -3960,11 +4091,29 @@ Quality-defined variable-bitrate. Average variable bitrate. @end table +@item blbrc +Enable block level rate control, which assigns different bitrate block by block. +Invalid for CQP mode. + @end table Each encoder also has its own specific options: @table @option +@item av1_vaapi +@option{profile} sets the value of @emph{seq_profile}. +@option{tier} sets the value of @emph{seq_tier}. +@option{level} sets the value of @emph{seq_level_idx}. + +@table @option +@item tiles +Set the number of tiles to encode the input video with, as columns x rows. +(default is auto, which means use minimal tile column/row number). +@item tile_groups +Set tile groups number. All the tiles will be distributed as evenly as possible to +each tile group. (default is 1). +@end table + @item h264_vaapi @option{profile} sets the value of @emph{profile_idc} and the @emph{constraint_set*_flag}s. @option{level} sets the value of @emph{level_idc}. diff --git a/doc/examples/demux_decode.c b/doc/examples/demux_decode.c index 298a369f436..64f5547bc43 100644 --- a/doc/examples/demux_decode.c +++ b/doc/examples/demux_decode.c @@ -78,9 +78,9 @@ static int output_video_frame(AVFrame *frame) /* copy decoded frame to destination buffer: * this is required since rawvideo expects non aligned data */ - av_image_copy(video_dst_data, video_dst_linesize, - (const uint8_t **)(frame->data), frame->linesize, - pix_fmt, width, height); + av_image_copy2(video_dst_data, video_dst_linesize, + frame->data, frame->linesize, + pix_fmt, width, height); /* write to rawvideo file */ fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file); @@ -138,11 +138,9 @@ static int decode_packet(AVCodecContext *dec, const AVPacket *pkt) ret = output_audio_frame(frame); av_frame_unref(frame); - if (ret < 0) - return ret; } - return 0; + return ret; } static int open_codec_context(int *stream_idx, diff --git a/doc/examples/mux.c b/doc/examples/mux.c index d1f682e1968..0f3a2bb125f 100644 --- a/doc/examples/mux.c +++ b/doc/examples/mux.c @@ -347,8 +347,7 @@ static int write_audio_frame(AVFormatContext *oc, OutputStream *ost) if (frame) { /* convert samples from native format to destination codec format, using the resampler */ /* compute destination number of samples */ - dst_nb_samples = av_rescale_rnd(swr_get_delay(ost->swr_ctx, c->sample_rate) + frame->nb_samples, - c->sample_rate, c->sample_rate, AV_ROUND_UP); + dst_nb_samples = swr_get_delay(ost->swr_ctx, c->sample_rate) + frame->nb_samples; av_assert0(dst_nb_samples == frame->nb_samples); /* when we pass a frame to the encoder, it may keep a reference to it @@ -379,27 +378,27 @@ static int write_audio_frame(AVFormatContext *oc, OutputStream *ost) /**************************************************************/ /* video output */ -static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height) +static AVFrame *alloc_frame(enum AVPixelFormat pix_fmt, int width, int height) { - AVFrame *picture; + AVFrame *frame; int ret; - picture = av_frame_alloc(); - if (!picture) + frame = av_frame_alloc(); + if (!frame) return NULL; - picture->format = pix_fmt; - picture->width = width; - picture->height = height; + frame->format = pix_fmt; + frame->width = width; + frame->height = height; /* allocate the buffers for the frame data */ - ret = av_frame_get_buffer(picture, 0); + ret = av_frame_get_buffer(frame, 0); if (ret < 0) { fprintf(stderr, "Could not allocate frame data.\n"); exit(1); } - return picture; + return frame; } static void open_video(AVFormatContext *oc, const AVCodec *codec, @@ -420,7 +419,7 @@ static void open_video(AVFormatContext *oc, const AVCodec *codec, } /* allocate and init a re-usable frame */ - ost->frame = alloc_picture(c->pix_fmt, c->width, c->height); + ost->frame = alloc_frame(c->pix_fmt, c->width, c->height); if (!ost->frame) { fprintf(stderr, "Could not allocate video frame\n"); exit(1); @@ -431,9 +430,9 @@ static void open_video(AVFormatContext *oc, const AVCodec *codec, * output format. */ ost->tmp_frame = NULL; if (c->pix_fmt != AV_PIX_FMT_YUV420P) { - ost->tmp_frame = alloc_picture(AV_PIX_FMT_YUV420P, c->width, c->height); + ost->tmp_frame = alloc_frame(AV_PIX_FMT_YUV420P, c->width, c->height); if (!ost->tmp_frame) { - fprintf(stderr, "Could not allocate temporary picture\n"); + fprintf(stderr, "Could not allocate temporary video frame\n"); exit(1); } } diff --git a/doc/examples/qsv_decode.c b/doc/examples/qsv_decode.c index cc2662d5bdd..901eac3b278 100644 --- a/doc/examples/qsv_decode.c +++ b/doc/examples/qsv_decode.c @@ -28,8 +28,6 @@ * GPU video surfaces, write the decoded frames to an output file. */ -#include "config.h" - #include #include "libavformat/avformat.h" diff --git a/doc/examples/qsv_transcode.c b/doc/examples/qsv_transcode.c index 7ea3ef5674b..ff115f36691 100644 --- a/doc/examples/qsv_transcode.c +++ b/doc/examples/qsv_transcode.c @@ -62,10 +62,10 @@ static int str_to_dict(char* optstr, AVDictionary **opt) return 0; key = strtok(optstr, " "); if (key == NULL) - return AVERROR(ENAVAIL); + return AVERROR(EINVAL); value = strtok(NULL, " "); if (value == NULL) - return AVERROR(ENAVAIL); + return AVERROR(EINVAL); av_dict_set(opt, key, value, 0); do { key = strtok(NULL, " "); @@ -73,10 +73,9 @@ static int str_to_dict(char* optstr, AVDictionary **opt) return 0; value = strtok(NULL, " "); if (value == NULL) - return AVERROR(ENAVAIL); + return AVERROR(EINVAL); av_dict_set(opt, key, value, 0); - } while(key != NULL); - return 0; + } while(1); } static int dynamic_set_parameter(AVCodecContext *avctx) @@ -88,7 +87,7 @@ static int dynamic_set_parameter(AVCodecContext *avctx) if (current_setting_number < setting_number && frame_number == dynamic_setting[current_setting_number].frame_number) { AVDictionaryEntry *e = NULL; - ret = str_to_dict(dynamic_setting[current_setting_number].optstr, &opts); + ret = str_to_dict(dynamic_setting[current_setting_number++].optstr, &opts); if (ret < 0) { fprintf(stderr, "The dynamic parameter is wrong\n"); goto fail; @@ -181,7 +180,7 @@ static int open_input_file(char *filename) break; default: fprintf(stderr, "Codec is not supportted by qsv\n"); - return AVERROR(ENAVAIL); + return AVERROR(EINVAL); } if (!(decoder_ctx = avcodec_alloc_context3(decoder))) @@ -334,17 +333,15 @@ static int dec_enc(AVPacket *pkt, const AVCodec *enc_codec, char *optstr) fail: av_frame_free(&frame); - if (ret < 0) - return ret; } - return 0; + return ret; } int main(int argc, char **argv) { const AVCodec *enc_codec; int ret = 0; - AVPacket *dec_pkt; + AVPacket *dec_pkt = NULL; if (argc < 5 || (argc - 5) % 2) { av_log(NULL, AV_LOG_ERROR, "Usage: %s " diff --git a/doc/examples/transcode.c b/doc/examples/transcode.c index 805a028ed73..a544ec03409 100644 --- a/doc/examples/transcode.c +++ b/doc/examples/transcode.c @@ -97,6 +97,11 @@ static int open_input_file(const char *filename) "for stream #%u\n", i); return ret; } + + /* Inform the decoder about the timebase for the packet timestamps. + * This is highly recommended, but not mandatory. */ + codec_ctx->pkt_timebase = stream->time_base; + /* Reencode video & audio and remux subtitles etc. */ if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO || codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { @@ -191,7 +196,7 @@ static int open_output_file(const char *filename) /* Third parameter can be used to pass settings to encoder */ ret = avcodec_open2(enc_ctx, encoder, NULL); if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Cannot open video encoder for stream #%u\n", i); + av_log(NULL, AV_LOG_ERROR, "Cannot open %s encoder for stream #%u\n", encoder->name, i); return ret; } ret = avcodec_parameters_from_context(out_stream->codecpar, enc_ctx); @@ -266,7 +271,7 @@ static int init_filter(FilteringContext* fctx, AVCodecContext *dec_ctx, snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, - dec_ctx->time_base.num, dec_ctx->time_base.den, + dec_ctx->pkt_timebase.num, dec_ctx->pkt_timebase.den, dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den); @@ -306,7 +311,7 @@ static int init_filter(FilteringContext* fctx, AVCodecContext *dec_ctx, av_channel_layout_describe(&dec_ctx->ch_layout, buf, sizeof(buf)); snprintf(args, sizeof(args), "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%s", - dec_ctx->time_base.num, dec_ctx->time_base.den, dec_ctx->sample_rate, + dec_ctx->pkt_timebase.num, dec_ctx->pkt_timebase.den, dec_ctx->sample_rate, av_get_sample_fmt_name(dec_ctx->sample_fmt), buf); ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", @@ -436,6 +441,10 @@ static int encode_write_frame(unsigned int stream_index, int flush) /* encode filtered frame */ av_packet_unref(enc_pkt); + if (filt_frame && filt_frame->pts != AV_NOPTS_VALUE) + filt_frame->pts = av_rescale_q(filt_frame->pts, filt_frame->time_base, + stream->enc_ctx->time_base); + ret = avcodec_send_frame(stream->enc_ctx, filt_frame); if (ret < 0) @@ -490,6 +499,7 @@ static int filter_encode_write_frame(AVFrame *frame, unsigned int stream_index) break; } + filter->filtered_frame->time_base = av_buffersink_get_time_base(filter->buffersink_ctx);; filter->filtered_frame->pict_type = AV_PICTURE_TYPE_NONE; ret = encode_write_frame(stream_index, 0); av_frame_unref(filter->filtered_frame); @@ -544,9 +554,6 @@ int main(int argc, char **argv) av_log(NULL, AV_LOG_DEBUG, "Going to reencode&filter the frame\n"); - av_packet_rescale_ts(packet, - ifmt_ctx->streams[stream_index]->time_base, - stream->dec_ctx->time_base); ret = avcodec_send_packet(stream->dec_ctx, packet); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Decoding failed\n"); @@ -578,11 +585,38 @@ int main(int argc, char **argv) av_packet_unref(packet); } - /* flush filters and encoders */ + /* flush decoders, filters and encoders */ for (i = 0; i < ifmt_ctx->nb_streams; i++) { - /* flush filter */ + StreamContext *stream; + if (!filter_ctx[i].filter_graph) continue; + + stream = &stream_ctx[i]; + + av_log(NULL, AV_LOG_INFO, "Flushing stream %u decoder\n", i); + + /* flush decoder */ + ret = avcodec_send_packet(stream->dec_ctx, NULL); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Flushing decoding failed\n"); + goto end; + } + + while (ret >= 0) { + ret = avcodec_receive_frame(stream->dec_ctx, stream->dec_frame); + if (ret == AVERROR_EOF) + break; + else if (ret < 0) + goto end; + + stream->dec_frame->pts = stream->dec_frame->best_effort_timestamp; + ret = filter_encode_write_frame(stream->dec_frame, i); + if (ret < 0) + goto end; + } + + /* flush filter */ ret = filter_encode_write_frame(NULL, i); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Flushing filter failed\n"); diff --git a/doc/examples/transcode_aac.c b/doc/examples/transcode_aac.c index bb5681a7c0a..cf6edc98907 100644 --- a/doc/examples/transcode_aac.c +++ b/doc/examples/transcode_aac.c @@ -447,26 +447,17 @@ static int init_converted_samples(uint8_t ***converted_input_samples, int error; /* Allocate as many pointers as there are audio channels. - * Each pointer will later point to the audio samples of the corresponding + * Each pointer will point to the audio samples of the corresponding * channels (although it may be NULL for interleaved formats). - */ - if (!(*converted_input_samples = calloc(output_codec_context->ch_layout.nb_channels, - sizeof(**converted_input_samples)))) { - fprintf(stderr, "Could not allocate converted input sample pointers\n"); - return AVERROR(ENOMEM); - } - - /* Allocate memory for the samples of all channels in one consecutive + * Allocate memory for the samples of all channels in one consecutive * block for convenience. */ - if ((error = av_samples_alloc(*converted_input_samples, NULL, + if ((error = av_samples_alloc_array_and_samples(converted_input_samples, NULL, output_codec_context->ch_layout.nb_channels, frame_size, output_codec_context->sample_fmt, 0)) < 0) { fprintf(stderr, "Could not allocate converted input samples (error '%s')\n", av_err2str(error)); - av_freep(&(*converted_input_samples)[0]); - free(*converted_input_samples); return error; } return 0; @@ -598,10 +589,9 @@ static int read_decode_convert_and_store(AVAudioFifo *fifo, ret = 0; cleanup: - if (converted_input_samples) { + if (converted_input_samples) av_freep(&converted_input_samples[0]); - free(converted_input_samples); - } + av_freep(&converted_input_samples); av_frame_free(&input_frame); return ret; diff --git a/doc/examples/vaapi_encode.c b/doc/examples/vaapi_encode.c index d5f472f6dd8..ff3ebb1e2b8 100644 --- a/doc/examples/vaapi_encode.c +++ b/doc/examples/vaapi_encode.c @@ -88,6 +88,10 @@ static int encode_write(AVCodecContext *avctx, AVFrame *frame, FILE *fout) enc_pkt->stream_index = 0; ret = fwrite(enc_pkt->data, enc_pkt->size, 1, fout); av_packet_unref(enc_pkt); + if (ret != enc_pkt->size) { + ret = AVERROR(errno); + break; + } } end: diff --git a/doc/examples/vaapi_transcode.c b/doc/examples/vaapi_transcode.c index 8367cb30404..e1b7a438836 100644 --- a/doc/examples/vaapi_transcode.c +++ b/doc/examples/vaapi_transcode.c @@ -215,10 +215,8 @@ static int dec_enc(AVPacket *pkt, const AVCodec *enc_codec) fail: av_frame_free(&frame); - if (ret < 0) - return ret; } - return 0; + return ret; } int main(int argc, char **argv) diff --git a/doc/faq.texi b/doc/faq.texi index 8b165eb436d..477cc60533a 100644 --- a/doc/faq.texi +++ b/doc/faq.texi @@ -450,7 +450,7 @@ work with streams that were detected during the initial scan; streams that are detected later are ignored. The size of the initial scan is controlled by two options: @code{probesize} -(default ~5 Mo) and @code{analyzeduration} (default 5,000,000 µs = 5 s). For +(default ~5@tie{}Mo) and @code{analyzeduration} (default 5,000,000@tie{}µs = 5@tie{}s). For the subtitle stream to be detected, both values must be large enough. @section Why was the @command{ffmpeg} @option{-sameq} option removed? What to use instead? @@ -467,7 +467,7 @@ point acceptable for your tastes. The most common options to do that are @option{-qscale} and @option{-qmax}, but you should peruse the documentation of the encoder you chose. -@section I have a stretched video, why does scaling does not fix it? +@section I have a stretched video, why does scaling not fix it? A lot of video codecs and formats can store the @emph{aspect ratio} of the video: this is the ratio between the width and the height of either the full diff --git a/doc/fate.texi b/doc/fate.texi index 84508560157..17644ce65ae 100644 --- a/doc/fate.texi +++ b/doc/fate.texi @@ -79,6 +79,14 @@ Do not put a '~' character in the samples path to indicate a home directory. Because of shell nuances, this will cause FATE to fail. @end float +Beware that some assertions are disabled by default, so mind setting +@option{--assert-level=} at configuration time, e.g. when seeking +the highest possible test coverage: +@example +./configure --assert-level=2 +@end example +Note that raising the assert level could have a performance impact. + To get the complete list of tests, run the command: @example make fate-list @@ -223,6 +231,14 @@ meaning only while running the regression tests. Specify how many threads to use while running regression tests, it is quite useful to detect thread-related regressions. +This variable may be set to the string "random", optionally followed by a +number, like "random99", This will cause each test to use a random number of +threads. If a number is specified, it is used as a maximum number of threads, +otherwise 16 is the maximum. + +In case a test fails, the thread count used for it will be written into the +errfile. + @item THREAD_TYPE Specify which threading strategy test, either @samp{slice} or @samp{frame}, by default @samp{slice+frame} diff --git a/doc/fate_config.sh.template b/doc/fate_config.sh.template index ab1bda45e4c..8681805d65e 100644 --- a/doc/fate_config.sh.template +++ b/doc/fate_config.sh.template @@ -31,3 +31,25 @@ makeopts= # extra options passed to 'make' # defaulting to makeopts above if this is not set #tar= # command to create a tar archive from its arguments on stdout, # defaults to 'tar c' +#fate_targets= # targets to make when running fate; defaults to "fate", + # can be set to run a subset of tests, e.g. "fate-checkasm". + +#fate_environments= # a list of names of configurations to run tests for; + # each round is run with variables from ${${name}_env} set. + +# One example of using fate_environments: + +# target_exec="qemu-aarch64-static" +# fate_targets="fate-checkasm fate-cpu" +# fate_environments="sve128 sve256" +# sve128_env="QEMU_CPU=max,sve128=on" +# sve256_env="QEMU_CPU=max,sve256=on" + +# The variables set by fate_environments can also be used explicitly +# by target_exec, e.g. like this: + +# target_exec="qemu-aarch64-static -cpu \$(MY_CPU)" +# fate_targets="fate-checkasm fate-cpu" +# fate_environments="sve128 sve256" +# sve128_env="MY_CPU=max,sve128=on" +# sve256_env="MY_CPU=max,sve256=on" diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index d9d4b75567c..a38ef834e16 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -17,9 +17,9 @@ ffmpeg [@var{global_options}] @{[@var{input_file_options}] -i @file{input_url}@} @chapter Description @c man begin DESCRIPTION -@command{ffmpeg} is a very fast video and audio converter that can also grab from -a live audio/video source. It can also convert between arbitrary sample -rates and resize video on the fly with a high quality polyphase filter. +@command{ffmpeg} is a universal media converter. It can read a wide variety of +inputs - including live grabbing/recording devices - filter, and transcode them +into a plethora of output formats. @command{ffmpeg} reads from an arbitrary number of input "files" (which can be regular files, pipes, network streams, grabbing devices, etc.), specified by the @@ -49,24 +49,32 @@ Do not mix input and output files -- first specify all input files, then all output files. Also do not mix options which belong to different files. All options apply ONLY to the next input or output file and are reset between files. +Some simple examples follow. + @itemize @item -To set the video bitrate of the output file to 64 kbit/s: +Convert an input media file to a different format, by re-encoding media streams: @example -ffmpeg -i input.avi -b:v 64k -bufsize 64k output.avi +ffmpeg -i input.avi output.mp4 @end example @item -To force the frame rate of the output file to 24 fps: +Set the video bitrate of the output file to 64 kbit/s: @example -ffmpeg -i input.avi -r 24 output.avi +ffmpeg -i input.avi -b:v 64k -bufsize 64k output.mp4 @end example @item -To force the frame rate of the input file (valid for raw formats only) -to 1 fps and the frame rate of the output file to 24 fps: +Force the frame rate of the output file to 24 fps: @example -ffmpeg -r 1 -i input.m2v -r 24 output.avi +ffmpeg -i input.avi -r 24 output.mp4 +@end example + +@item +Force the frame rate of the input file (valid for raw formats only) to 1 fps and +the frame rate of the output file to 24 fps: +@example +ffmpeg -r 1 -i input.m2v -r 24 output.mp4 @end example @end itemize @@ -110,7 +118,7 @@ Encoded packets are then passed to the decoder (unless streamcopy is selected for the stream, see further for a description). The decoder produces uncompressed frames (raw video/PCM audio/...) which can be processed further by filtering (see next section). After filtering, the frames are passed to the -encoder, which encodes them and outputs encoded packets. Finally those are +encoder, which encodes them and outputs encoded packets. Finally, those are passed to the muxer, which writes the encoded packets to the output file. @section Filtering @@ -211,6 +219,40 @@ Since there is no decoding or encoding, it is very fast and there is no quality loss. However, it might not work in some cases because of many factors. Applying filters is obviously also impossible, since filters work on uncompressed data. +@section Loopback decoders +While decoders are normally associated with demuxer streams, it is also possible +to create "loopback" decoders that decode the output from some encoder and allow +it to be fed back to complex filtergraphs. This is done with the @code{-dec} +directive, which takes as a parameter the index of the output stream that should +be decoded. Every such directive creates a new loopback decoder, indexed with +successive integers starting at zero. These indices should then be used to refer +to loopback decoders in complex filtergraph link labels, as described in the +documentation for @option{-filter_complex}. + +E.g. the following example: + +@example +ffmpeg -i INPUT \ + -map 0:v:0 -c:v libx264 -crf 45 -f null - \ + -dec 0:0 -filter_complex '[0:v][dec:0]hstack[stack]' \ + -map '[stack]' -c:v ffv1 OUTPUT +@end example + +reads an input video and +@itemize +@item +(line 2) encodes it with @code{libx264} at low quality; + +@item +(line 3) decodes this encoded stream and places it side by side with the +original input video; + +@item +(line 4) combined video is then losslessly encoded and written into +@file{OUTPUT}. + +@end itemize + @c man end DETAILED DESCRIPTION @chapter Stream selection @@ -615,6 +657,206 @@ Not all muxers support embedded thumbnails, and those who do, only support a few Creates a program with the specified @var{title}, @var{program_num} and adds the specified @var{stream}(s) to it. +@item -stream_group type=@var{type}:st=@var{stream}[:st=@var{stream}][:stg=@var{stream_group}][:id=@var{stream_group_id}...] (@emph{output}) + +Creates a stream group of the specified @var{type}, @var{stream_group_id} and adds the specified +@var{stream}(s) and/or previously defined @var{stream_group}(s) to it. + +@var{type} can be one of the following: +@table @option + +@item iamf_audio_element +Groups @var{stream}s that belong to the same IAMF Audio Element + +For this group @var{type}, the following options are available +@table @option +@item audio_element_type +The Audio Element type. The following values are supported: + +@table @option +@item channel +Scalable channel audio representation +@item scene +Ambisonics representation +@end table + +@item demixing +Demixing information used to reconstruct a scalable channel audio representation. +This option must be separated from the rest with a ',', and takes the following +key=value options + +@table @option +@item parameter_id +An identifier parameters blocks in frames may refer to +@item dmixp_mode +A pre-defined combination of demixing parameters +@end table + +@item recon_gain +Recon gain information used to reconstruct a scalable channel audio representation. +This option must be separated from the rest with a ',', and takes the following +key=value options + +@table @option +@item parameter_id +An identifier parameters blocks in frames may refer to +@end table + +@item layer +A layer defining a Channel Layout in the Audio Element. +This option must be separated from the rest with a ','. Several ',' separated entries +can be defined, and at least one must be set. + +It takes the following ":"-separated key=value options + +@table @option +@item ch_layout +The layer's channel layout +@item flags +The following flags are available: + +@table @option +@item recon_gain +Wether to signal if recon_gain is present as metadata in parameter blocks within frames +@end table + +@item output_gain +@item output_gain_flags +Which channels output_gain applies to. The following flags are available: + +@table @option +@item FL +@item FR +@item BL +@item BR +@item TFL +@item TFR +@end table + +@item ambisonics_mode +The ambisonics mode. This has no effect if audio_element_type is set to channel. + +The following values are supported: + +@table @option +@item mono +Each ambisonics channel is coded as an individual mono stream in the group +@end table + +@end table + +@item default_w +Default weight value + +@end table + +@item iamf_mix_presentation +Groups @var{stream}s that belong to all IAMF Audio Element the same +IAMF Mix Presentation references + +For this group @var{type}, the following options are available + +@table @option +@item submix +A sub-mix within the Mix Presentation. +This option must be separated from the rest with a ','. Several ',' separated entries +can be defined, and at least one must be set. + +It takes the following ":"-separated key=value options + +@table @option +@item parameter_id +An identifier parameters blocks in frames may refer to, for post-processing the mixed +audio signal to generate the audio signal for playback +@item parameter_rate +The sample rate duration fields in parameters blocks in frames that refer to this +@var{parameter_id} are expressed as +@item default_mix_gain +Default mix gain value to apply when there are no parameter blocks sharing the same +@var{parameter_id} for a given frame + +@item element +References an Audio Element used in this Mix Presentation to generate the final output +audio signal for playback. +This option must be separated from the rest with a '|'. Several '|' separated entries +can be defined, and at least one must be set. + +It takes the following ":"-separated key=value options: + +@table @option +@item stg +The @var{stream_group_id} for an Audio Element which this sub-mix refers to +@item parameter_id +An identifier parameters blocks in frames may refer to, for applying any processing to +the referenced and rendered Audio Element before being summed with other processed Audio +Elements +@item parameter_rate +The sample rate duration fields in parameters blocks in frames that refer to this +@var{parameter_id} are expressed as +@item default_mix_gain +Default mix gain value to apply when there are no parameter blocks sharing the same +@var{parameter_id} for a given frame +@item annotations +A key=value string describing the sub-mix element where "key" is a string conforming to +BCP-47 that specifies the language for the "value" string. "key" must be the same as the +one in the mix's @var{annotations} +@item headphones_rendering_mode +Indicates whether the input channel-based Audio Element is rendered to stereo loudspeakers +or spatialized with a binaural renderer when played back on headphones. +This has no effect if the referenced Audio Element's @var{audio_element_type} is set to +channel. + +The following values are supported: + +@table @option +@item stereo +@item binaural +@end table + +@end table + +@item layout +Specifies the layouts for this sub-mix on which the loudness information was measured. +This option must be separated from the rest with a '|'. Several '|' separated entries +can be defined, and at least one must be set. + +It takes the following ":"-separated key=value options: + +@table @option +@item layout_type + +@table @option +@item loudspeakers +The layout follows the loudspeaker sound system convention of ITU-2051-3. +@item binaural +The layout is binaural. +@end table + +@item sound_system +Channel layout matching one of Sound Systems A to J of ITU-2051-3, plus 7.1.2 and 3.1.2 +This has no effect if @var{layout_type} is set to binaural. +@item integrated_loudness +The program integrated loudness information, as defined in ITU-1770-4. +@item digital_peak +The digital (sampled) peak value of the audio signal, as defined in ITU-1770-4. +@item true_peak +The true peak of the audio signal, as defined in ITU-1770-4. +@item dialog_anchored_loudness +The Dialogue loudness information, as defined in ITU-1770-4. +@item album_anchored_loudness +The Album loudness information, as defined in ITU-1770-4. +@end table + +@end table + +@item annotations +A key=value string string describing the mix where "key" is a string conforming to BCP-47 +that specifies the language for the "value" string. "key" must be the same as the ones in +all sub-mix element's @var{annotations}s +@end table + +@end table + @item -target @var{type} (@emph{output}) Specify target file type (@code{vcd}, @code{svcd}, @code{dvd}, @code{dv}, @code{dv50}). @var{type} may be prefixed with @code{pal-}, @code{ntsc-} or @@ -769,11 +1011,6 @@ syntax. See the @ref{filter_complex_option,,-filter_complex option} if you want to create filtergraphs with multiple inputs and/or outputs. -@item -filter_script[:@var{stream_specifier}] @var{filename} (@emph{output,per-stream}) -This option is similar to @option{-filter}, the only difference is that its -argument is the name of the file from which a filtergraph description is to be -read. - @item -reinit_filter[:@var{stream_specifier}] @var{integer} (@emph{input,per-stream}) This boolean option determines if the filtergraph(s) to which this stream is fed gets reinitialized when input frame parameters change mid-stream. This option is enabled by @@ -821,7 +1058,7 @@ be achieved with @code{ffmpeg ... < /dev/null} but it requires a shell. @item -debug_ts (@emph{global}) -Print timestamp information. It is off by default. This option is +Print timestamp/latency information. It is off by default. This option is mostly useful for testing and debugging purposes, and the output format may change from one version to another, so it should not be employed by portable scripts. @@ -1014,7 +1251,12 @@ If @var{pix_fmt} is a single @code{+}, ffmpeg selects the same pixel format as the input (or graph output) and automatic conversions are disabled. @item -sws_flags @var{flags} (@emph{input/output}) -Set SwScaler flags. +Set default flags for the libswscale library. These flags are used by +automatically inserted @code{scale} filters and those within simple +filtergraphs, if not overridden within the filtergraph definition. + +See the @ref{scaler_options,,ffmpeg-scaler manual,ffmpeg-scaler} for a list +of scaler options. @item -rc_override[:@var{stream_specifier}] @var{override} (@emph{output,per-stream}) Rate control override for specific intervals, formatted as "int,int,int" @@ -1022,36 +1264,24 @@ list separated with slashes. Two first values are the beginning and end frame numbers, last one is quantizer to use if positive, or quality factor if negative. -@item -psnr -Calculate PSNR of compressed frames. This option is deprecated, pass the -PSNR flag to the encoder instead, using @code{-flags +psnr}. @item -vstats -Dump video coding statistics to @file{vstats_HHMMSS.log}. -@item -vstats_file @var{file} -Dump video coding statistics to @var{file}. -@item -vstats_version @var{file} -Specifies which version of the vstats format to use. Default is 2. - -version = 1 : +Dump video coding statistics to @file{vstats_HHMMSS.log}. See the +@ref{vstats_file_format,,vstats file format} section for the format description. -@code{frame= %5d q= %2.1f PSNR= %6.2f f_size= %6d s_size= %8.0fkB time= %0.3f br= %7.1fkbits/s avg_br= %7.1fkbits/s} +@item -vstats_file @var{file} +Dump video coding statistics to @var{file}. See the +@ref{vstats_file_format,,vstats file format} section for the format description. -version > 1: +@item -vstats_version @var{file} +Specify which version of the vstats format to use. Default is @code{2}. See the +@ref{vstats_file_format,,vstats file format} section for the format description. -@code{out= %2d st= %2d frame= %5d q= %2.1f PSNR= %6.2f f_size= %6d s_size= %8.0fkB time= %0.3f br= %7.1fkbits/s avg_br= %7.1fkbits/s} -@item -top[:@var{stream_specifier}] @var{n} (@emph{output,per-stream}) -top=1/bottom=0/auto=-1 field first @item -vtag @var{fourcc/tag} (@emph{output}) Force video tag/fourcc. This is an alias for @code{-tag:v}. -@item -qphist (@emph{global}) -Show QP histogram -@item -vbsf @var{bitstream_filter} -Deprecated see -bsf @item -force_key_frames[:@var{stream_specifier}] @var{time}[,@var{time}...] (@emph{output,per-stream}) @item -force_key_frames[:@var{stream_specifier}] expr:@var{expr} (@emph{output,per-stream}) @item -force_key_frames[:@var{stream_specifier}] source (@emph{output,per-stream}) -@item -force_key_frames[:@var{stream_specifier}] source_no_drop (@emph{output,per-stream}) @var{force_key_frames} can take arguments of the following form: @@ -1112,10 +1342,6 @@ starting from second 13: @item source If the argument is @code{source}, ffmpeg will force a key frame if the current frame being encoded is marked as a key frame in its source. - -@item source_no_drop -If the argument is @code{source_no_drop}, ffmpeg will force a key frame if -the current frame being encoded is marked as a key frame in its source. In cases where this particular source frame has to be dropped, enforce the next available frame to become a key frame instead. @@ -1161,11 +1387,50 @@ Choose the first device and use the primary device context. @item d3d11va @var{device} is the number of the Direct3D 11 display adapter. +If not specified, it will attempt to use the default Direct3D 11 display adapter +or the first Direct3D 11 display adapter whose hardware VendorId is specified +by @samp{vendor_id}. + +Examples: +@table @emph +@item -init_hw_device d3d11va +Create a d3d11va device on the default Direct3D 11 display adapter. + +@item -init_hw_device d3d11va:1 +Create a d3d11va device on the Direct3D 11 display adapter specified by index 1. + +@item -init_hw_device d3d11va:,vendor_id=0x8086 +Create a d3d11va device on the first Direct3D 11 display adapter whose hardware VendorId is 0x8086. +@end table @item vaapi -@var{device} is either an X11 display name or a DRM render node. +@var{device} is either an X11 display name, a DRM render node or a DirectX adapter index. If not specified, it will attempt to open the default X11 display (@emph{$DISPLAY}) -and then the first DRM render node (@emph{/dev/dri/renderD128}). +and then the first DRM render node (@emph{/dev/dri/renderD128}), or the default +DirectX adapter on Windows. + +The following options are recognized: +@table @option +@item kernel_driver +When @var{device} is not specified, use this option to specify the name of the kernel +driver associated with the desired device. This option is available only when +the hardware acceleration method @emph{drm} and @emph{vaapi} are enabled. +@end table + +Examples: +@table @emph +@item -init_hw_device vaapi +Create a vaapi device on the default device. + +@item -init_hw_device vaapi:/dev/dri/renderD129 +Create a vaapi device on DRM render node @file{/dev/dri/renderD129}. + +@item -init_hw_device vaapi:1 +Create a vaapi device on DirectX adapter 1. + +@item -init_hw_device vaapi:,kernel_driver=i915 +Create a vaapi device on a device associated with kernel driver @samp{i915}. +@end table @item vdpau @var{device} is an X11 display name. @@ -1188,16 +1453,38 @@ If not specified, @samp{auto_any} is used. platform-appropriate subdevice (@samp{dxva2} or @samp{d3d11va} or @samp{vaapi}) and then deriving a QSV device from that.) -Alternatively, @samp{child_device_type} helps to choose platform-appropriate subdevice type. -On Windows @samp{d3d11va} is used as default subdevice type. +The following options are recognized: +@table @option +@item child_device +Specify a DRM render node on Linux or DirectX adapter on Windows. +@item child_device_type +Choose platform-appropriate subdevice type. On Windows @samp{d3d11va} is used +as default subdevice type when @code{--enable-libvpl} is specified at configuration time, +@samp{dxva2} is used as default subdevice type when @code{--enable-libmfx} is specified at +configuration time. On Linux user can use @samp{vaapi} only as subdevice type. +@end table Examples: @table @emph +@item -init_hw_device qsv:hw,child_device=/dev/dri/renderD129 +Create a QSV device with @samp{MFX_IMPL_HARDWARE} on DRM render node @file{/dev/dri/renderD129}. + +@item -init_hw_device qsv:hw,child_device=1 +Create a QSV device with @samp{MFX_IMPL_HARDWARE} on DirectX adapter 1. + @item -init_hw_device qsv:hw,child_device_type=d3d11va Choose the GPU subdevice with type @samp{d3d11va} and create QSV device with @samp{MFX_IMPL_HARDWARE}. @item -init_hw_device qsv:hw,child_device_type=dxva2 Choose the GPU subdevice with type @samp{dxva2} and create QSV device with @samp{MFX_IMPL_HARDWARE}. + +@item -init_hw_device qsv:hw,child_device=1,child_device_type=d3d11va +Create a QSV device with @samp{MFX_IMPL_HARDWARE} on DirectX adapter 1 with subdevice type @samp{d3d11va}. + +@item -init_hw_device vaapi=va:/dev/dri/renderD129 -init_hw_device qsv=hw1@@@var{va} +Create a VAAPI device called @samp{va} on @file{/dev/dri/renderD129}, then derive a QSV device called @samp{hw1} +from device @samp{va}. + @end table @item opencl @@ -1404,8 +1691,6 @@ This is an alias for @code{-filter:a}, see the @ref{filter_option,,-filter optio @table @option @item -atag @var{fourcc/tag} (@emph{output}) Force audio tag/fourcc. This is an alias for @code{-tag:a}. -@item -absf @var{bitstream_filter} -Deprecated, see -bsf @item -guess_layout_max @var{channels} (@emph{input,per-stream}) If some input channel layout is not known, try to guess only if it corresponds to at most the specified number of channels. For example, 2 @@ -1427,8 +1712,6 @@ option to disable streams individually. As an output option, disables subtitle recording i.e. automatic selection or mapping of any subtitle stream. For full manual control see the @code{-map} option. -@item -sbsf @var{bitstream_filter} -Deprecated, see -bsf @end table @section Advanced Subtitle options @@ -1461,8 +1744,7 @@ Set the size of the canvas used to render subtitles. Create one or more streams in the output file. This option has two forms for specifying the data source(s): the first selects one or more streams from some input file (specified with @code{-i}), the second takes an output from some -complex filtergraph (specified with @code{-filter_complex} or -@code{-filter_complex_script}). +complex filtergraph (specified with @code{-filter_complex}). In the first form, an output stream is created for every stream from the input file with the index @var{input_file_id}. If @var{stream_specifier} is given, @@ -1554,77 +1836,6 @@ such streams is attempted. Allow input streams with unknown type to be copied instead of failing if copying such streams is attempted. -@item -map_channel [@var{input_file_id}.@var{stream_specifier}.@var{channel_id}|-1][?][:@var{output_file_id}.@var{stream_specifier}] -This option is deprecated and will be removed. It can be replaced by the -@var{pan} filter. In some cases it may be easier to use some combination of the -@var{channelsplit}, @var{channelmap}, or @var{amerge} filters. - -Map an audio channel from a given input to an output. If -@var{output_file_id}.@var{stream_specifier} is not set, the audio channel will -be mapped on all the audio streams. - -Using "-1" instead of -@var{input_file_id}.@var{stream_specifier}.@var{channel_id} will map a muted -channel. - -A trailing @code{?} will allow the map_channel to be -optional: if the map_channel matches no channel the map_channel will be ignored instead -of failing. - -For example, assuming @var{INPUT} is a stereo audio file, you can switch the -two audio channels with the following command: -@example -ffmpeg -i INPUT -map_channel 0.0.1 -map_channel 0.0.0 OUTPUT -@end example - -If you want to mute the first channel and keep the second: -@example -ffmpeg -i INPUT -map_channel -1 -map_channel 0.0.1 OUTPUT -@end example - -The order of the "-map_channel" option specifies the order of the channels in -the output stream. The output channel layout is guessed from the number of -channels mapped (mono if one "-map_channel", stereo if two, etc.). Using "-ac" -in combination of "-map_channel" makes the channel gain levels to be updated if -input and output channel layouts don't match (for instance two "-map_channel" -options and "-ac 6"). - -You can also extract each channel of an input to specific outputs; the following -command extracts two channels of the @var{INPUT} audio stream (file 0, stream 0) -to the respective @var{OUTPUT_CH0} and @var{OUTPUT_CH1} outputs: -@example -ffmpeg -i INPUT -map_channel 0.0.0 OUTPUT_CH0 -map_channel 0.0.1 OUTPUT_CH1 -@end example - -The following example splits the channels of a stereo input into two separate -streams, which are put into the same output file: -@example -ffmpeg -i stereo.wav -map 0:0 -map 0:0 -map_channel 0.0.0:0.0 -map_channel 0.0.1:0.1 -y out.ogg -@end example - -Note that currently each output stream can only contain channels from a single -input stream; you can't for example use "-map_channel" to pick multiple input -audio channels contained in different streams (from the same or different files) -and merge them into a single output stream. It is therefore not currently -possible, for example, to turn two separate mono streams into a single stereo -stream. However splitting a stereo stream into two single channel mono streams -is possible. - -If you need this feature, a possible workaround is to use the @emph{amerge} -filter. For example, if you need to merge a media (here @file{input.mkv}) with 2 -mono audio streams into one single stereo channel audio stream (and keep the -video stream), you can use the following command: -@example -ffmpeg -i input.mkv -filter_complex "[0:1] [0:2] amerge" -c:a pcm_s16le -c:v copy output.mkv -@end example - -To map the first two audio channels from the first input, and using the -trailing @code{?}, ignore the audio channel mapping if the first input is -mono instead of stereo: -@example -ffmpeg -i INPUT -map_channel 0.0.0 -map_channel 0.0.1? OUTPUT -@end example - @item -map_metadata[:@var{metadata_spec_out}] @var{infile}[:@var{metadata_spec_in}] (@emph{output,per-metadata}) Set metadata information of the next output file from @var{infile}. Note that those are file indices (zero-based), not filenames. @@ -1701,6 +1912,9 @@ it may cause packet loss. It is useful for when flow speed of output packets is important, such as live streaming. @item -re (@emph{input}) Read input at native frame rate. This is equivalent to setting @code{-readrate 1}. +@item -readrate_initial_burst @var{seconds} +Set an initial read burst time, in seconds, after which @option{-re/-readrate} +will be enforced. @item -vsync @var{parameter} (@emph{global}) @itemx -fps_mode[:@var{stream_specifier}] @var{parameter} (@emph{output,per-stream}) Set video sync method / framerate mode. vsync is applied to all output video streams @@ -1719,9 +1933,6 @@ constant frame rate. @item vfr (2) Frames are passed through with their timestamp or dropped so as to prevent 2 frames from having the same timestamp. -@item drop -As passthrough but destroys all timestamps, making the muxer generate -fresh timestamps based on frame-rate. @item auto (-1) Chooses between cfr and vfr depending on muxer capabilities. This is the default method. @@ -1742,12 +1953,6 @@ The default is -1.1. One possible usecase is to avoid framedrops in case of noisy timestamps or to increase frame drop precision in case of exact timestamps. -@item -adrift_threshold @var{time} -Set the minimum difference between timestamps and audio data (in seconds) to trigger -adding/dropping samples to make it match the timestamps. This option effectively is -a threshold to select between hard (add/drop) and soft (squeeze/stretch) compensation. -@code{-async} must be set to a positive value. - @item -apad @var{parameters} (@emph{output,per-stream}) Pad the output audio stream(s). This is the same as applying @code{-af apad}. Argument is a string of filter parameters composed the same as with the @code{apad} filter. @@ -1794,8 +1999,7 @@ Try to make the choice automatically, in order to generate a sane output. Default value is -1. @item -enc_time_base[:@var{stream_specifier}] @var{timebase} (@emph{output,per-stream}) -Set the encoder timebase. @var{timebase} is a floating point number, -and can assume one of the following values: +Set the encoder timebase. @var{timebase} can assume one of the following values: @table @option @item 0 @@ -1803,16 +2007,17 @@ Assign a default value according to the media type. For video - use 1/framerate, for audio - use 1/samplerate. -@item -1 -Use the input stream timebase when possible. +@item demux +Use the timebase from the demuxer. -If an input stream is not available, the default timebase will be used. +@item filter +Use the timebase from the filtergraph. -@item >0 +@item a positive number Use the provided number as the timebase. This field can be provided as a ratio of two integers (e.g. 1:24, 1:48000) -or as a floating point number (e.g. 0.04166, 2.0833e-5) +or as a decimal number (e.g. 0.04166, 2.0833e-5) @end table Default value is 0. @@ -1837,12 +2042,38 @@ results, but increase memory use and latency. The default value is 10 seconds. -@item -dts_delta_threshold -Timestamp discontinuity delta threshold. -@item -dts_error_threshold @var{seconds} -Timestamp error delta threshold. This threshold use to discard crazy/damaged -timestamps and the default is 30 hours which is arbitrarily picked and quite -conservative. +@item -dts_delta_threshold @var{threshold} +Timestamp discontinuity delta threshold, expressed as a decimal number +of seconds. + +The timestamp discontinuity correction enabled by this option is only +applied to input formats accepting timestamp discontinuity (for which +the @code{AVFMT_TS_DISCONT} flag is enabled), e.g. MPEG-TS and HLS, and +is automatically disabled when employing the @code{-copyts} option +(unless wrapping is detected). + +If a timestamp discontinuity is detected whose absolute value is +greater than @var{threshold}, ffmpeg will remove the discontinuity by +decreasing/increasing the current DTS and PTS by the corresponding +delta value. + +The default value is 10. + +@item -dts_error_threshold @var{threshold} +Timestamp error delta threshold, expressed as a decimal number of +seconds. + +The timestamp correction enabled by this option is only applied to +input formats not accepting timestamp discontinuity (for which the +@code{AVFMT_TS_DISCONT} flag is not enabled). + +If a timestamp discontinuity is detected whose absolute value is +greater than @var{threshold}, ffmpeg will drop the PTS/DTS timestamp +value. + +The default value is @code{3600*30} (30 hours), which is arbitrarily +picked and quite conservative. + @item -muxdelay @var{seconds} (@emph{output}) Set the maximum demux-decode delay. @item -muxpreload @var{seconds} (@emph{output}) @@ -1859,16 +2090,36 @@ an output mpegts file: ffmpeg -i inurl -streamid 0:33 -streamid 1:36 out.ts @end example -@item -bsf[:@var{stream_specifier}] @var{bitstream_filters} (@emph{output,per-stream}) -Set bitstream filters for matching streams. @var{bitstream_filters} is -a comma-separated list of bitstream filters. Use the @code{-bsfs} option -to get the list of bitstream filters. +@item -bsf[:@var{stream_specifier}] @var{bitstream_filters} (@emph{input/output,per-stream}) +Apply bitstream filters to matching streams. The filters are applied to each +packet as it is received from the demuxer (when used as an input option) or +before it is sent to the muxer (when used as an output option). + +@var{bitstream_filters} is a comma-separated list of bitstream filter +specifications, each of the form @example -ffmpeg -i h264.mp4 -c:v copy -bsf:v h264_mp4toannexb -an out.h264 +@var{filter}[=@var{optname0}=@var{optval0}:@var{optname1}=@var{optval1}:...] @end example +Any of the ',=:' characters that are to be a part of an option value need to be +escaped with a backslash. + +Use the @code{-bsfs} option to get the list of bitstream filters. + +E.g. +@example +ffmpeg -bsf:v h264_mp4toannexb -i h264.mp4 -c:v copy -an out.h264 +@end example +applies the @code{h264_mp4toannexb} bitstream filter (which converts +MP4-encapsulated H.264 stream to Annex B) to the @emph{input} video stream. + +On the other hand, @example ffmpeg -i file.mov -an -vn -bsf:s mov2textsub -c:s copy -f rawvideo sub.txt @end example +applies the @code{mov2textsub} bitstream filter (which extracts text from MOV +subtitles) to the @emph{output} subtitle stream. Note, however, that since both +examples use @code{-c copy}, it matters little whether the filters are applied +on input or output - that would change if transcoding was happening. @item -tag[:@var{stream_specifier}] @var{codec_tag} (@emph{input/output,per-stream}) Force a tag/fourcc for matching streams. @@ -1888,11 +2139,16 @@ type -- see the @option{-filter} options. @var{filtergraph} is a description of the filtergraph, as described in the ``Filtergraph syntax'' section of the ffmpeg-filters manual. -Input link labels must refer to input streams using the -@code{[file_index:stream_specifier]} syntax (i.e. the same as @option{-map} -uses). If @var{stream_specifier} matches multiple streams, the first one will be -used. An unlabeled input will be connected to the first unused input stream of -the matching type. +Input link labels must refer to either input streams or loopback decoders. For +input streams, use the @code{[file_index:stream_specifier]} syntax (i.e. the +same as @option{-map} uses). If @var{stream_specifier} matches multiple streams, +the first one will be used. + +For decoders, the link label must be [dec:@var{dec_idx}], where @var{dec_idx} is +the index of the loopback decoder to be connected to given input. + +An unlabeled input will be connected to the first unused input stream of the +matching type. Output link labels are referred to with @option{-map}. Unlabeled outputs are added to the first output file. @@ -1953,11 +2209,6 @@ The default is the number of available CPUs. Define a complex filtergraph, i.e. one with arbitrary number of inputs and/or outputs. Equivalent to @option{-filter_complex}. -@item -filter_complex_script @var{filename} (@emph{global}) -This option is similar to @option{-filter_complex}, the only difference is that -its argument is the name of the file from which a complex filtergraph -description is to be read. - @item -accurate_seek (@emph{input}) This option enables or disables accurate seeking in input files with the @option{-ss} option. It is enabled by default, so seeking is accurate when @@ -2061,6 +2312,7 @@ encoder/muxer, it does not change the stream to conform to this value. Setting values that do not match the stream properties may result in encoding failures or invalid output files. +@anchor{stats_enc_options} @item -stats_enc_pre[:@var{stream_specifier}] @var{path} (@emph{output,per-stream}) @item -stats_enc_post[:@var{stream_specifier}] @var{path} (@emph{output,per-stream}) @item -stats_mux_pre[:@var{stream_specifier}] @var{path} (@emph{output,per-stream}) @@ -2108,8 +2360,8 @@ Input frame number. Index of the input frame (i.e. output by a decoder) that corresponds to this output frame or packet. -1 if unavailable. @item tb -Encoder timebase, as a rational number @var{num/den}. Note that this may be -different from the timebase used by the muxer. +Timebase in which this frame/packet's timestamps are expressed, as a rational +number @var{num/den}. Note that encoder and muxer may use different timebases. @item tbi Timebase for @var{ptsi}, as a rational number @var{num/den}. Available when @@ -2132,31 +2384,42 @@ Presentation time of the frame or packet, as a decimal number. Equal to Presentation time of the input frame (see @var{ni}), as a decimal number. Equal to @var{ptsi} multiplied by @var{tbi}. Printed as inf when not available. -@item dts +@item dts (@emph{packet}) Decoding timestamp of the packet, as an integer. Should be multiplied by the -timebase to compute presentation time. Post-encoding only. +timebase to compute presentation time. -@item dt +@item dt (@emph{packet}) Decoding time of the frame or packet, as a decimal number. Equal to @var{dts} multiplied by @var{tb}. -@item sn -Number of audio samples sent to the encoder so far. Audio and pre-encoding only. +@item sn (@emph{frame,audio}) +Number of audio samples sent to the encoder so far. -@item samp -Number of audio samples in the frame. Audio and pre-encoding only. +@item samp (@emph{frame,audio}) +Number of audio samples in the frame. -@item size -Size of the encoded packet in bytes. Post-encoding only. +@item size (@emph{packet}) +Size of the encoded packet in bytes. -@item br -Current bitrate in bits per second. Post-encoding only. +@item br (@emph{packet}) +Current bitrate in bits per second. -@item abr +@item abr (@emph{packet}) Average bitrate for the whole stream so far, in bits per second, -1 if it cannot -be determined at this point. Post-encoding only. +be determined at this point. + +@item key (@emph{packet}) +Character 'K' if the packet contains a keyframe, character 'N' otherwise. @end table +Directives tagged with @emph{packet} may only be used with +@option{-stats_enc_post_fmt} and @option{-stats_mux_pre_fmt}. + +Directives tagged with @emph{frame} may only be used with +@option{-stats_enc_pre_fmt}. + +Directives tagged with @emph{audio} may only be used with audio streams. + The default format strings are: @table @option @item pre-encoding @@ -2228,6 +2491,63 @@ search for the file @file{libvpx-1080p.avpreset}. If no such file is found, then ffmpeg will search for a file named @var{arg}.avpreset in the same directories. +@anchor{vstats_file_format} +@section vstats file format +The @code{-vstats} and @code{-vstats_file} options enable generation of a file +containing statistics about the generated video outputs. + +The @code{-vstats_version} option controls the format version of the generated +file. + +With version @code{1} the format is: +@example +frame= @var{FRAME} q= @var{FRAME_QUALITY} PSNR= @var{PSNR} f_size= @var{FRAME_SIZE} s_size= @var{STREAM_SIZE}kB time= @var{TIMESTAMP} br= @var{BITRATE}kbits/s avg_br= @var{AVERAGE_BITRATE}kbits/s +@end example + +With version @code{2} the format is: +@example +out= @var{OUT_FILE_INDEX} st= @var{OUT_FILE_STREAM_INDEX} frame= @var{FRAME_NUMBER} q= @var{FRAME_QUALITY}f PSNR= @var{PSNR} f_size= @var{FRAME_SIZE} s_size= @var{STREAM_SIZE}kB time= @var{TIMESTAMP} br= @var{BITRATE}kbits/s avg_br= @var{AVERAGE_BITRATE}kbits/s +@end example + +The value corresponding to each key is described below: +@table @option +@item avg_br +average bitrate expressed in Kbits/s + +@item br +bitrate expressed in Kbits/s + +@item frame +number of encoded frame + +@item out +out file index + +@item PSNR +Peak Signal to Noise Ratio + +@item q +quality of the frame + +@item f_size +encoded packet size expressed as number of bytes + +@item s_size +stream size expressed in KiB + +@item st +out file stream index + +@item time +time of the packet + +@item type +picture type +@end table + +See also the @ref{stats_enc_options,,-stats_enc options} for an alternative way +to show encoding statistics. + @c man end OPTIONS @chapter Examples @@ -2491,7 +2811,7 @@ ffmpeg-devices(1), ffmpeg-protocols(1), ffmpeg-filters(1) @ignore @setfilename ffmpeg -@settitle ffmpeg video converter +@settitle ffmpeg media converter @end ignore diff --git a/doc/ffplay.texi b/doc/ffplay.texi index 5dd860b846f..93f77eeecea 100644 --- a/doc/ffplay.texi +++ b/doc/ffplay.texi @@ -196,6 +196,18 @@ will produce a thread pool with this many threads available for parallel processing. The default is 0 which means that the thread count will be determined by the number of available CPUs. +@item -enable_vulkan +Use vulkan renderer rather than SDL builtin renderer. Depends on libplacebo. + +@item -vulkan_params + +Vulkan configuration using a list of @var{key}=@var{value} pairs separated by +":". + +@item -hwaccel +Use HW accelerated decoding. Enable this option will enable vulkan renderer +automatically. + @end table @section While playing diff --git a/doc/ffprobe.texi b/doc/ffprobe.texi index 4dc9f577bb8..6333249a6e8 100644 --- a/doc/ffprobe.texi +++ b/doc/ffprobe.texi @@ -41,15 +41,15 @@ ffprobe will show it. ffprobe output is designed to be easily parsable by a textual filter, and consists of one or more sections of a form defined by the selected -writer, which is specified by the @option{print_format} option. +writer, which is specified by the @option{output_format} option. Sections may contain other nested sections, and are identified by a name (which may be shared by other sections), and an unique name. See the output of @option{sections}. Metadata tags stored in the container or in the streams are recognized -and printed in the corresponding "FORMAT", "STREAM" or "PROGRAM_STREAM" -section. +and printed in the corresponding "FORMAT", "STREAM", "STREAM_GROUP_STREAM" +or "PROGRAM_STREAM" section. @c man end @@ -83,7 +83,7 @@ Use sexagesimal format HH:MM:SS.MICROSECONDS for time values. Prettify the format of the displayed values, it corresponds to the options "-unit -prefix -byte_binary_prefix -sexagesimal". -@item -of, -print_format @var{writer_name}[=@var{writer_options}] +@item -output_format, -of, -print_format @var{writer_name}[=@var{writer_options}] Set the output printing format. @var{writer_name} specifies the name of the writer, and @@ -91,7 +91,7 @@ Set the output printing format. For example for printing the output in JSON format, specify: @example --print_format json +-output_format json @end example For more details on the available output printing formats, see the @@ -232,6 +232,13 @@ multimedia stream. Each media stream information is printed within a dedicated section with name "PROGRAM_STREAM". +@item -show_stream_groups +Show information about stream groups and their streams contained in the +input multimedia stream. + +Each media stream information is printed within a dedicated section +with name "STREAM_GROUP_STREAM". + @item -show_chapters Show information about chapters stored in the format. @@ -415,8 +422,9 @@ keyN=valN [/SECTION] @end example -Metadata tags are printed as a line in the corresponding FORMAT, STREAM or -PROGRAM_STREAM section, and are prefixed by the string "TAG:". +Metadata tags are printed as a line in the corresponding FORMAT, STREAM, +STREAM_GROUP_STREAM or PROGRAM_STREAM section, and are prefixed by the +string "TAG:". A description of the accepted options follows. diff --git a/doc/ffprobe.xsd b/doc/ffprobe.xsd index 0920380108f..6d5d094d97a 100644 --- a/doc/ffprobe.xsd +++ b/doc/ffprobe.xsd @@ -1,389 +1,527 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + targetNamespace="http://www.ffmpeg.org/schema/ffprobe" + xmlns:ffprobe="http://www.ffmpeg.org/schema/ffprobe"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/fftools-common-opts.texi b/doc/fftools-common-opts.texi index d9145704d6e..1974d79a4c1 100644 --- a/doc/fftools-common-opts.texi +++ b/doc/fftools-common-opts.texi @@ -13,6 +13,15 @@ corresponding value to true. They can be set to false by prefixing the option name with "no". For example using "-nofoo" will set the boolean option with name "foo" to false. +Options that take arguments support a special syntax where the argument given on +the command line is interpreted as a path to the file from which the actual +argument value is loaded. To use this feature, add a forward slash '/' +immediately before the option name (after the leading dash). E.g. +@example +ffmpeg -i INPUT -/filter:v filter.script OUTPUT +@end example +will load a filtergraph description from the file named @file{filter.script}. + @anchor{Stream specifiers} @section Stream specifiers Some options are applied per-stream, e.g. bitrate or codec. Stream specifiers @@ -37,9 +46,9 @@ Matches the stream with this index. E.g. @code{-threads:1 4} would set the thread count for the second stream to 4. If @var{stream_index} is used as an additional stream specifier (see below), then it selects stream number @var{stream_index} from the matching streams. Stream numbering is based on the -order of the streams as detected by libavformat except when a program ID is -also specified. In this case it is based on the ordering of the streams in the -program. +order of the streams as detected by libavformat except when a stream group +specifier or program ID is also specified. In this case it is based on the +ordering of the streams in the group or program. @item @var{stream_type}[:@var{additional_stream_specifier}] @var{stream_type} is one of following: 'v' or 'V' for video, 'a' for audio, 's' for subtitle, 'd' for data, and 't' for attachments. 'v' matches all video @@ -48,6 +57,17 @@ thumbnails or cover arts. If @var{additional_stream_specifier} is used, then it matches streams which both have this type and match the @var{additional_stream_specifier}. Otherwise, it matches all streams of the specified type. +@item g:@var{group_specifier}[:@var{additional_stream_specifier}] +Matches streams which are in the group with the specifier @var{group_specifier}. +if @var{additional_stream_specifier} is used, then it matches streams which both +are part of the group and match the @var{additional_stream_specifier}. +@var{group_specifier} may be one of the following: +@table @option +@item @var{group_index} +Match the stream with this group index. +@item #@var{group_id} or i:@var{group_id} +Match the stream with this group id. +@end table @item p:@var{program_id}[:@var{additional_stream_specifier}] Matches streams which are in the program with the id @var{program_id}. If @var{additional_stream_specifier} is used, then it matches streams which both diff --git a/doc/filters.texi b/doc/filters.texi index 47e92b92698..bfa8ccec8b2 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -214,6 +214,23 @@ In a complete filterchain all the unlabelled filter input and output pads must be connected. A filtergraph is considered valid if all the filter input and output pads of all the filterchains are connected. +Leading and trailing whitespaces (space, tabs, or line feeds) separating tokens +in the filtergraph specification are ignored. This means that the filtergraph +can be expressed using empty lines and spaces to improve redability. + +For example, the filtergraph: +@example +testsrc,split[L1],hflip[L2];[L1][L2] hstack +@end example + +can be represented as: +@example +testsrc, +split [L1], hflip [L2]; + +[L1][L2] hstack +@end example + Libavfilter will automatically insert @ref{scale} filters where format conversion is required. It is possible to specify swscale flags for those automatically inserted scalers by prepending @@ -282,6 +299,14 @@ previous string will finally result in: -vf "drawtext=text=this is a \\\\\\'string\\\\\\'\\\\: may contain one\\, or more\\, special characters" @end example +In order to avoid cumbersome escaping when using a commandline tool accepting a +filter specification as input, it is advisable to avoid direct inclusion of the +filter or options specification in the shell. + +For example, in case of the @ref{drawtext,,drawtext filter}, you might prefer to +use the @option{textfile} option in place of @option{text} to specify the text +to render. + @chapter Timeline editing Some filters support a generic @option{enable} option. For the filters @@ -299,7 +324,8 @@ timestamp expressed in seconds, NAN if the input timestamp is unknown sequential number of the input frame, starting from 0 @item pos -the position in the file of the input frame, NAN if unknown +the position in the file of the input frame, NAN if unknown; deprecated, do +not use @item w @item h @@ -388,6 +414,63 @@ build. Below is a description of the currently available audio filters. +@section aap +Apply Affine Projection algorithm to the first audio stream using the second audio stream. + +This adaptive filter is used to estimate unknown audio based on multiple input audio samples. +Affine projection algorithm can make trade-offs between computation complexity with convergence speed. + +A description of the accepted options follows. + +@table @option +@item order +Set the filter order. + +@item projection +Set the projection order. + +@item mu +Set the filter mu. + +@item delta +Set the coefficient to initialize internal covariance matrix. + +@item out_mode +Set the filter output samples. It accepts the following values: +@table @option +@item i +Pass the 1st input. + +@item d +Pass the 2nd input. + +@item o +Pass difference between desired, 2nd input and error signal estimate. + +@item n +Pass difference between input, 1st input and error signal estimate. + +@item e +Pass error signal estimated samples. + +Default value is @var{o}. +@end table + +@item precision +Set which precision to use when processing samples. + +@table @option +@item auto +Auto pick internal sample format depending on other filters. + +@item float +Always use single-floating point precision sample format. + +@item double +Always use double-floating point precision sample format. +@end table +@end table + @section acompressor A compressor is mainly used to reduce the dynamic range of a signal. @@ -977,15 +1060,15 @@ A description of the accepted options follows. @table @option @item threshold Set the detection threshold used to trigger equalization. -Threshold detection is using bandpass filter. +Threshold detection is using detection filter. Default value is 0. Allowed range is from 0 to 100. @item dfrequency -Set the detection frequency in Hz used for bandpass filter used to trigger equalization. +Set the detection frequency in Hz used for detection filter used to trigger equalization. Default value is 1000 Hz. Allowed range is between 2 and 1000000 Hz. @item dqfactor -Set the detection resonance factor for bandpass filter used to trigger equalization. +Set the detection resonance factor for detection filter used to trigger equalization. Default value is 1. Allowed range is from 0.001 to 1000. @item tfrequency @@ -1023,13 +1106,28 @@ Set the mode of filter operation, can be one of the following: @table @samp @item listen -Output only isolated bandpass signal. -@item cut +Output only isolated detection signal. +@item cutbelow +Cut frequencies below detection threshold. +@item cutabove Cut frequencies above detection threshold. -@item boost -Boost frequencies bellow detection threshold. +@item boostbelow +Boost frequencies below detection threshold. +@item boostabove +Boost frequencies above detection threshold. +@end table +Default mode is @samp{cutbelow}. + +@item dftype +Set the type of detection filter, can be one of the following: + +@table @samp +@item bandpass +@item lowpass +@item highpass +@item peak @end table -Default mode is @samp{cut}. +Default type is @samp{bandpass}. @item tftype Set the type of target filter, can be one of the following: @@ -1041,16 +1139,6 @@ Set the type of target filter, can be one of the following: @end table Default type is @samp{bell}. -@item direction -Set processing direction relative to threshold. -@table @samp -@item downward -Boost/Cut if threshold is higher/lower than detected volume. -@item upward -Boost/Cut if threshold is lower/higher than detected volume. -@end table -Default direction is @samp{downward}. - @item auto Automatically gather threshold from detection filter. By default is @samp{disabled}. @@ -1065,6 +1153,22 @@ Disable using automatically gathered threshold value. Stop picking threshold value. @item on Start picking threshold value. +@item adaptive +Adaptively pick threshold value, by calculating sliding window entropy. +@end table + +@item precision +Set which precision to use when processing samples. + +@table @option +@item auto +Auto pick internal sample format depending on other filters. + +@item float +Always use single-floating point precision sample format. + +@item double +Always use double-floating point precision sample format. @end table @end table @@ -1404,6 +1508,14 @@ select logistic sigmoid select sine cardinal function @item isinc select inverted sine cardinal function +@item quat +select quartic +@item quatr +select quartic root +@item qsin2 +select squared quarter of sine wave +@item hsin2 +select squared half of sine wave @item nofade no fade applied @end table @@ -1735,33 +1847,24 @@ Set wet gain. This sets final output gain. Set Impulse Response filter length. Default is 1, which means whole IR is processed. @item gtype -Enable applying gain measured from power of IR. +This option is deprecated, and does nothing. -Set which approach to use for auto gain measurement. +@item irnorm +Set norm to be applied to IR coefficients before filtering. +Allowed range is from @var{-1} to @var{2}. +IR coefficients are normalized with calculated vector norm set by this option. +For negative values, no norm is calculated, and IR coefficients are not modified at all. +Default is @var{1}. -@table @option -@item none -Do not apply any gain. - -@item peak -select peak gain, very conservative approach. This is default value. - -@item dc -select DC gain, limited application. - -@item gn -select gain to noise approach, this is most popular one. - -@item ac -select AC gain. - -@item rms -select RMS gain. -@end table +@item irlink +For multichannel IR if this option is set to @var{true}, all IR channels will be +normalized with maximal measured gain of all IR channels coefficients as set by @code{irnorm} option. +When disabled, all IR coefficients in each IR channel will be normalized independently. +Default is @var{true}. @item irgain Set gain to be applied to IR coefficients before filtering. -Allowed range is 0 to 1. This gain is applied after any gain applied with @var{gtype} option. +Allowed range is 0 to 1. This gain is applied after any gain applied with @var{irnorm} option. @item irfmt Set format of IR stream. Can be @code{mono} or @code{input}. @@ -1772,18 +1875,16 @@ Set max allowed Impulse Response filter duration in seconds. Default is 30 secon Allowed range is 0.1 to 60 seconds. @item response -Show IR frequency response, magnitude(magenta), phase(green) and group delay(yellow) in additional video stream. -By default it is disabled. +This option is deprecated, and does nothing. @item channel -Set for which IR channel to display frequency response. By default is first channel -displayed. This option is used only when @var{response} is enabled. +This option is deprecated, and does nothing. @item size -Set video stream size. This option is used only when @var{response} is enabled. +This option is deprecated, and does nothing. @item rate -Set video stream frame rate. This option is used only when @var{response} is enabled. +This option is deprecated, and does nothing. @item minp Set minimal partition size used for convolution. Default is @var{8192}. @@ -1819,6 +1920,12 @@ Always use double-floating point precision sample format. @end table Default value is auto. + +@item irload +Set when to load IR stream. Can be @code{init} or @code{access}. +First one load and prepares all IRs on initialization, second one +once on first access of specific IR. +Default is @code{init}. @end table @subsection Examples @@ -1832,9 +1939,15 @@ ffmpeg -i input.wav -i middle_tunnel_1way_mono.wav -lavfi afir output.wav @item Apply true stereo processing given input stereo stream, and two stereo impulse responses for left and right channel, -the impulse response files are files with names l_ir.wav and r_ir.wav: +the impulse response files are files with names l_ir.wav and r_ir.wav, and setting irnorm option value: +@example +"pan=4C|c0=FL|c1=FL|c2=FR|c3=FR[a];amovie=l_ir.wav[LIR];amovie=r_ir.wav[RIR];[LIR][RIR]amerge[ir];[a][ir]afir=irfmt=input:irnorm=1.2,pan=stereo|FL stronger filtering @item al/autolevels[:f/fullyrange], automatic brightness / contrast correction @table @option @item f/fullyrange -Stretch luminance to @code{0-255}. +Stretch luma to @code{0-255}. @end table @item lb/linblenddeint @@ -19081,7 +19779,7 @@ pp=default/tmpnoise|1|2|3 @end example @item -Apply deblocking on luminance only, and switch vertical deblocking on or off +Apply deblocking on luma only, and switch vertical deblocking on or off automatically depending on available CPU time: @example pp=hb|y/vb|a @@ -19194,6 +19892,12 @@ Available LUTs: @item preferred @item total @item spectral +@item cool +@item heat +@item fiery +@item blues +@item green +@item helix @end table @item opacity @@ -19443,6 +20147,330 @@ qp=2+2*sin(PI*qp) @end example @end itemize +@section qrencode +Generate a QR code using the libqrencode library (see +@url{https://fukuchi.org/works/qrencode/}), and overlay it on top of the current +frame. + +To enable the compilation of this filter, you need to configure FFmpeg with +@code{--enable-libqrencode}. + +The QR code is generated from the provided text or text pattern. The +corresponding QR code is scaled and overlayed into the video output according to +the specified options. + +In case no text is specified, no QR code is overlaied. + +This filter accepts the following options: + +@table @option + +@item qrcode_width, q +@item padded_qrcode_width, Q +Specify an expression for the width of the rendered QR code, with and without +padding. The @var{qrcode_width} expression can reference the value set by the +@var{padded_qrcode_width} expression, and vice versa. +By default @var{padded_qrcode_width} is set to @var{qrcode_width}, meaning that +there is no padding. + +These expressions are evaluated for each new frame. + +See the @ref{qrencode_expressions,,qrencode Expressions} section for details. + +@item x +@item y +Specify an expression for positioning the padded QR code top-left corner. The +@var{x} expression can reference the value set by the @var{y} expression, and +vice. + +By default @var{x} and @var{y} are set set to @var{0}, meaning that the QR code +is placed in the top left corner of the input. + +These expressions are evaluated for each new frame. + +See the @ref{qrencode_expressions,,qrencode Expressions} section for details. + +@item case_sensitive, cs +Instruct libqrencode to use case sensitive encoding. This is enabled by +default. This can be disabled to reduce the QR encoding size. + +@item level, l +Specify the QR encoding error correction level. With an higher correction level, +the encoding size will increase but the code will be more robust to corruption. +Lower level is @var{L}. + +It accepts the following values: +@table @samp +@item L +@item M +@item Q +@item H +@end table + +@item expansion +Select how the input text is expanded. Can be either @code{none}, or +@code{normal} (default). See the @ref{qrencode_text_expansion,,qrencode Text expansion} +section below for details. + +@item text +@item textfile +Define the text to be rendered. In case neither is specified, no QR is encoded +(just an empty colored frame). + +In case expansion is enabled, the text is treated as a text template, using the +qrencode expansion mechanism. See the @ref{qrencode_text_expansion,,qrencode +Text expansion} section below for details. + +@item background_color, bc +@item foreground_color, fc +Set the QR code and background color. The default value of +@var{foreground_color} is "black", the default value of @var{background_color} +is "white". + +For the syntax of the color options, check the @ref{color syntax,,"Color" +section in the ffmpeg-utils manual,ffmpeg-utils}. +@end table + +@anchor{qrencode_expressions} +@subsection qrencode Expressions + +The expressions set by the options contain the following constants and functions. + +@table @option +@item dar +input display aspect ratio, it is the same as (@var{w} / @var{h}) * @var{sar} + +@item duration +the current frame's duration, in seconds + +@item hsub +@item vsub +horizontal and vertical chroma subsample values. For example for the +pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1. + +@item main_h, H +the input height + +@item main_w, W +the input width + +@item n +the number of input frame, starting from 0 + +@item pict_type +a number representing the picture type + +@item qr_w, w +the width of the encoded QR code + +@item rendered_qr_w, q +@item rendered_padded_qr_w, Q +the width of the rendered QR code, without and without padding. + +These parameters allow the @var{q} and @var{Q} expressions to refer to each +other, so you can for example specify @code{q=3/4*Q}. + +@item rand(min, max) +return a random number included between @var{min} and @var{max} + +@item sar +the input sample aspect ratio + +@item t +timestamp expressed in seconds, NAN if the input timestamp is unknown + +@item x +@item y +the x and y offset coordinates where the text is drawn. + +These parameters allow the @var{x} and @var{y} expressions to refer to each +other, so you can for example specify @code{y=x/dar}. +@end table + +@anchor{qrencode_text_expansion} +@subsection qrencode Text expansion + +If @option{expansion} is set to @code{none}, the text is printed verbatim. + +If @option{expansion} is set to @code{normal} (which is the default), +the following expansion mechanism is used. + +The backslash character @samp{\}, followed by any character, always expands to +the second character. + +Sequences of the form @code{%@{...@}} are expanded. The text between the +braces is a function name, possibly followed by arguments separated by ':'. +If the arguments contain special characters or delimiters (':' or '@}'), +they should be escaped. + +Note that they probably must also be escaped as the value for the @option{text} +option in the filter argument string and as the filter argument in the +filtergraph description, and possibly also for the shell, that makes up to four +levels of escaping; using a text file with the @option{textfile} option avoids +these problems. + +The following functions are available: + +@table @command +@item n, frame_num +return the frame number + +@item pts +Return the presentation timestamp of the current frame. + +It can take up to two arguments. + +The first argument is the format of the timestamp; it defaults to @code{flt} for +seconds as a decimal number with microsecond accuracy; @code{hms} stands for a +formatted @var{[-]HH:MM:SS.mmm} timestamp with millisecond accuracy. +@code{gmtime} stands for the timestamp of the frame formatted as UTC time; +@code{localtime} stands for the timestamp of the frame formatted as local time +zone time. If the format is set to @code{hms24hh}, the time is formatted in 24h +format (00-23). + +The second argument is an offset added to the timestamp. + +If the format is set to @code{localtime} or @code{gmtime}, a third argument may +be supplied: a @code{strftime} C function format string. By default, +@var{YYYY-MM-DD HH:MM:SS} format will be used. + +@item expr, e +Evaluate the expression's value and output as a double. + +It must take one argument specifying the expression to be evaluated, accepting +the constants and functions defined in @ref{qrencode_expressions}. + +@item expr_formatted, ef +Evaluate the expression's value and output as a formatted string. + +The first argument is the expression to be evaluated, just as for the @var{expr} function. +The second argument specifies the output format. Allowed values are @samp{x}, +@samp{X}, @samp{d} and @samp{u}. They are treated exactly as in the +@code{printf} function. +The third parameter is optional and sets the number of positions taken by the output. +It can be used to add padding with zeros from the left. + +@item gmtime +The time at which the filter is running, expressed in UTC. +It can accept an argument: a @code{strftime} C function format string. +The format string is extended to support the variable @var{%[1-6]N} +which prints fractions of the second with optionally specified number of digits. + +@item localtime +The time at which the filter is running, expressed in the local time zone. +It can accept an argument: a @code{strftime} C function format string. +The format string is extended to support the variable @var{%[1-6]N} +which prints fractions of the second with optionally specified number of digits. + +@item metadata +Frame metadata. Takes one or two arguments. + +The first argument is mandatory and specifies the metadata key. + +The second argument is optional and specifies a default value, used when the +metadata key is not found or empty. + +Available metadata can be identified by inspecting entries starting with TAG +included within each frame section printed by running @code{ffprobe +-show_frames}. + +String metadata generated in filters leading to the qrencode filter are also +available. + +@item rand(min, max) +return a random number included between @var{min} and @var{max} +@end table + +@subsection Examples + +@itemize +@item +Generate a QR code encoding the specified text with the default size, overalaid +in the top left corner of the input video, with the default size: +@example +qrencode=text=www.ffmpeg.org +@end example + +@item +Same as below, but select blue on pink colors: +@example +qrencode=text=www.ffmpeg.org:bc=pink@@0.5:fc=blue +@end example + +@item +Place the QR code in the bottom right corner of the input video: +@example +qrencode=text=www.ffmpeg.org:x=W-Q:y=H-Q +@end example + +@item +Generate a QR code with width of 200 pixels and padding, making the padded width +4/3 of the QR code width: +@example +qrencode=text=www.ffmpeg.org:q=200:Q=4/3*q +@end example + +@item +Generate a QR code with padded width of 200 pixels and padding, making the QR +code width 3/4 of the padded width: +@example +qrencode=text=www.ffmpeg.org:Q=200:q=3/4*Q +@end example + +@item +Make the QR code a fraction of the input video width: +@example +qrencode=text=www.ffmpeg.org:q=W/5 +@end example + +@item +Generate a QR code encoding the frame number: +@example +qrencode=text=%@{n@} +@end example + +@item +Generate a QR code encoding the GMT timestamp: +@example +qrencode=text=%@{gmtime@} +@end example + +@item +Generate a QR code encoding the timestamp expressed as a float: +@example +qrencode=text=%@{pts@} +@end example + +@end itemize + +@section quirc + +Identify and decode a QR code using the libquirc library (see +@url{https://github.com/dlbeer/quirc/}), and print the identified QR codes +positions and payload as metadata. + +To enable the compilation of this filter, you need to configure FFmpeg with +@code{--enable-libquirc}. + +For each found QR code in the input video, some metadata entries are added with +the prefix @var{lavfi.quirc.N}, where @var{N} is the index, starting from 0, +associated to the QR code. + +A description of each metadata value follows: + +@table @option +@item lavfi.quirc.count +the number of found QR codes, it is not set in case none was found + +@item lavfi.quirc.N.corner.M.x +@item lavfi.quirc.N.coreer.M.y +the x/y positions of the four corners of the square containing the QR code, +where @var{M} is the index of the corner starting from 0 + +@item lavfi.quirc.N.payload +the payload of the QR code +@end table + @section random Flush video frames from internal cache of frames into a random order. @@ -20199,6 +21227,7 @@ seconds. Only available with @code{eval=frame}. The position (byte offset) of the frame in the input stream, or NaN if this information is unavailable and/or meaningless (for example in case of synthetic video). Only available with @code{eval=frame}. +Deprecated, do not use. @end table @subsection Examples @@ -20315,6 +21344,7 @@ If the specified expression is not valid, it is kept at its current value. @end table +@anchor{scale_cuda} @section scale_cuda Scale (resize) and convert (pixel format) the input video, using accelerated CUDA kernels. @@ -20357,7 +21387,7 @@ pixel format is used. The filter does not support converting between YUV and RGB pixel formats. @item passthrough -If set to 0, every frame is processed, even if no conversion is neccesary. +If set to 0, every frame is processed, even if no conversion is necessary. This mode can be useful to use the filter as a buffer for a downstream frame-consumer that exhausts the limited decoder frame pool. @@ -20531,6 +21561,7 @@ seconds. Only available with @code{eval=frame}. The position (byte offset) of the frame in the input stream, or NaN if this information is unavailable and/or meaningless (for example in case of synthetic video). Only available with @code{eval=frame}. +Deprecated, do not use. @end table @section scale2ref @@ -20661,6 +21692,27 @@ Scale a logo to 1/10th the height of a video, while preserving its display aspec @end example @end itemize +@section scale_vt + +Scale and convert the color parameters using VTPixelTransferSession. + +The filter accepts the following options: +@table @option +@item w +@item h +Set the output video dimension expression. Default value is the input dimension. + +@item color_matrix +Set the output colorspace matrix. + +@item color_primaries +Set the output color primaries. + +@item color_transfer +Set the output transfer characteristics. + +@end table + @section scharr Apply scharr operator to input video stream. @@ -20860,12 +21912,9 @@ It accepts the following parameters: @item r, ratio, dar (@code{setdar} only), sar (@code{setsar} only) Set the aspect ratio used by the filter. -The parameter can be a floating point number string, an expression, or -a string of the form @var{num}:@var{den}, where @var{num} and -@var{den} are the numerator and denominator of the aspect ratio. If -the parameter is not specified, it is assumed the value "0". -In case the form "@var{num}:@var{den}" is used, the @code{:} character -should be escaped. +The parameter can be a floating point number string, or an expression. If the +parameter is not specified, the value "0" is assumed, meaning that the same +input value is used. @item max Set the maximum integer value to use for expressing numerator and @@ -20874,19 +21923,14 @@ Default value is @code{100}. @end table -The parameter @var{sar} is an expression containing -the following constants: +The parameter @var{sar} is an expression containing the following constants: @table @option -@item E, PI, PHI -These are approximated values for the mathematical constants e -(Euler's number), pi (Greek pi), and phi (the golden ratio). - @item w, h The input width and height. @item a -These are the same as @var{w} / @var{h}. +Same as @var{w} / @var{h}. @item sar The input sample aspect ratio. @@ -21135,6 +22179,10 @@ This filter supports the following options: @table @option @item checksum Calculate checksums of each plane. By default enabled. + +@item udu_sei_as_ascii +Try to print user data unregistered SEI as ascii character when possible, +in hex format otherwise. @end table The shown line contains a sequence of key/value pairs of the form @@ -21154,10 +22202,6 @@ time base units. The time base unit depends on the filter input pad. The Presentation TimeStamp of the input frame, expressed as a number of seconds. -@item pos -The position of the frame in the input stream, or -1 if this information is -unavailable and/or meaningless (for example in case of synthetic video). - @item fmt The pixel format name. @@ -21593,9 +22637,11 @@ ffmpeg -i input1.mkv -i input2.mkv -filter_complex "[0:v][1:v] signature=nb_inpu @anchor{siti} @section siti -Calculate Spatial Info (SI) and Temporal Info (TI) scores for a video, as defined -in ITU-T P.910: Subjective video quality assessment methods for multimedia -applications. Available PDF at @url{https://www.itu.int/rec/T-REC-P.910-199909-S/en }. +Calculate Spatial Information (SI) and Temporal Information (TI) scores for a video, +as defined in ITU-T Rec. P.910 (11/21): Subjective video quality assessment methods +for multimedia applications. Available PDF at @url{https://www.itu.int/rec/T-REC-P.910-202111-S/en}. +Note that this is a legacy implementation that corresponds to a superseded recommendation. +Refer to ITU-T Rec. P.910 (07/22) for the latest version: @url{https://www.itu.int/rec/T-REC-P.910-202207-I/en} It accepts the following option: @@ -21748,9 +22794,6 @@ Training scripts as well as scripts for model file (.pb) saving can be found at @url{https://github.com/XueweiMeng/sr/tree/sr_dnn_native}. Original repository is at @url{https://github.com/HighVoltageRocknRoll/sr.git}. -Native model files (.model) can be generated from TensorFlow model -files (.pb) by using tools/python/convert.py - The filter accepts the following options: @table @option @@ -21759,9 +22802,6 @@ Specify which DNN backend to use for model loading and execution. This option ac the following values: @table @samp -@item native -Native implementation of DNN loading and execution. - @item tensorflow TensorFlow backend. To enable this backend you need to install the TensorFlow for C library (see @@ -21769,13 +22809,10 @@ need to install the TensorFlow for C library (see @code{--enable-libtensorflow} @end table -Default value is @samp{native}. - @item model Set path to model file specifying network architecture and its parameters. -Note that different backends use different file formats. TensorFlow backend -can load files for both formats, while native backend can load files for only -its format. +Note that different backends use different file formats. TensorFlow, OpenVINO backend +can load files for only its format. @item scale_factor Set scale factor for SRCNN model. Allowed values are @code{2}, @code{3} and @code{4}. @@ -22146,6 +23183,13 @@ Set subtitles stream index. @code{subtitles} filter only. @item force_style Override default style or script info parameters of the subtitles. It accepts a string containing ASS style format @code{KEY=VALUE} couples separated by ",". + +@item wrap_unicode +Break lines according to the Unicode Line Breaking Algorithm. Availability requires +at least libass release 0.17.0 (or LIBASS_VERSION 0x01600010), @emph{and} libass must +have been built with libunibreak. + +The option is enabled by default except for native ASS. @end table If the first key is not specified, it is assumed that the first value @@ -22236,7 +23280,8 @@ The number of the input frame, starting from 0. The timestamp expressed in seconds. It's NAN if the input timestamp is unknown. @item pos -the position in the file of the input frame, NAN if unknown +the position in the file of the input frame, NAN if unknown; deprecated, +do not use @end table @subsection Commands @@ -22476,7 +23521,7 @@ The filter accepts the following options: @table @option @item layout -Set the grid size in the form @code{COLUMNSxROWS}. Range is upto UINT_MAX cells. +Set the grid size in the form @code{COLUMNSxROWS}. Range is up to UINT_MAX cells. Default is @code{6x5}. @item nb_frames @@ -22528,11 +23573,74 @@ tile=3x2:nb_frames=5:padding=7:margin=2 @end example @end itemize -@section tinterlace +@section tiltandshift -Perform various types of temporal field interlacing. +What happens when you invert time and space? -Frames are counted starting from 1, so the first input frame is +Normally a video is composed of several frames that represent a different +instant of time and shows a scence that evolves in the space captured by the +frame. This filter is the antipode of that concept, taking inspiration by +tilt and shift photography. + +A filtered frame contains the whole timeline of events composing the sequence, +and this is obtained by placing a slice of pixels from each frame into a single +one. However, since there are no infinite-width frames, this is done up the +width of the input frame, and a video is recomposed by shifting away one +column for each subsequent frame. In order to map space to time, the filter +tilts each input frame as well, so that motion is preseved. This is accomplished +by progressively selecting a different column from each input frame. + +The end result is a sort of inverted parralax, so that far away objects move +much faster that the ones in the front. The ideal conditions for this video +effect are when there is either very little motion and the backgroud is static, +or when there is a lot of motion and a very wide depth of field (eg. wide +panorama, while moving on a train). + +The filter accepts the following parameters: + +@table @option + +@item tilt +Tilt video while shifting (default). When unset, video will be sliding a +static image, composed of the first column of each frame. + +@item start +What to do at the start of filtering (see below). + +@item end +What to do at the end of filtering (see below). + +@item hold +How many columns should pass through before start of filtering. + +@item pad +How many columns should be inserted before end of filtering. + +@end table + +Normally the filter shifts and tils from the very first frame, and stops when +the last one is received. However, before filtering starts, normal video may +be preseved, so that the effect is slowly shifted in its place. Similarly, +the last video frame may be reconstructed at the end. Alternatively it is +possible to just start and end with black. + +@table @samp +@item none +Filtering is starts immediately and ends when the last frame is received. + +@item frame +The first frames or the very last frame are kept intact during processing. + +@item black +Black is padded at the beginning or at the end of filtering. + +@end table + +@section tinterlace + +Perform various types of temporal field interlacing. + +Frames are counted starting from 1, so the first input frame is considered odd. The filter accepts the following options: @@ -23365,6 +24473,9 @@ that value the speed drops by a factor of approximately 2. Default value is @item qp Force a constant quantization parameter. If not set, the filter will use the QP from the video stream (if available). + +@item codec +Use specified codec instead of snow. @end table @section v360 @@ -24043,6 +25154,19 @@ If set to 0, it is disabled. The frames are counted starting from 1. Show fields and transforms in the resulting frames. It accepts an integer in the range 0-2. Default value is 0, which disables any visualization. + +@item fileformat +Format for the transforms data file to be written. +Acceptable values are + +@table @samp +@item ascii +Human-readable plain text + +@item binary +Binary format, roughly 40% smaller than @code{ascii}. (@emph{default}) +@end table + @end table @subsection Examples @@ -24533,7 +25657,7 @@ This filter supports same @ref{commands} as options. @section waveform Video waveform monitor. -The waveform monitor plots color component intensity. By default luminance +The waveform monitor plots color component intensity. By default luma only. Each column of the waveform corresponds to a column of pixels in the source video. @@ -24588,7 +25712,7 @@ correction is easy to perform by making level adjustments the three waveforms. Default is @code{stack}. @item components, c -Set which color components to display. Default is 1, which means only luminance +Set which color components to display. Default is 1, which means only luma or red color component if input is in RGB colorspace. If is set for example to 7 it will display all 3 (if) available color components. @@ -24698,6 +25822,12 @@ Set sample aspect ration to 1/1. Set sample aspect ratio to match input size of video @end table Default is @samp{none}. + +@item input +Set input formats for filter to pick from. +Can be @samp{all}, for selecting from all available formats, +or @samp{first}, for selecting first available format. +Default is @samp{first}. @end table @section weave, doubleweave @@ -24828,6 +25958,18 @@ Set one of available transition effects: @item zoomin @item fadefast @item fadeslow +@item hlwind +@item hrwind +@item vuwind +@item vdwind +@item coverleft +@item coverright +@item coverup +@item coverdown +@item revealleft +@item revealright +@item revealup +@item revealdown @end table Default transition effect is fade. @@ -25986,6 +27128,7 @@ Apply dilation filter with threshold0 set to 30, threshold1 set 40, threshold2 s @end example @end itemize +@anchor{nlmeans_opencl} @section nlmeans_opencl Non-local Means denoise filter through OpenCL, this filter accepts same options as @ref{nlmeans}. @@ -26739,7 +27882,7 @@ 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. +keeping the original aspect. It accepts the following options: @@ -26760,7 +27903,7 @@ 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. +keeping the original aspect. It accepts the following options: @@ -26814,177 +27957,579 @@ See @ref{xstack}. @c man end VAAPI VIDEO FILTERS -@chapter QSV Video Filters -@c man begin QSV VIDEO FILTERS +@chapter Vulkan Video Filters +@c man begin VULKAN VIDEO FILTERS -Below is a description of the currently available QSV video filters. +Below is a description of the currently available Vulkan video filters. To enable compilation of these filters you need to configure FFmpeg with -@code{--enable-libmfx} or @code{--enable-libvpl}. +@code{--enable-vulkan} and either @code{--enable-libglslang} or @code{--enable-libshaderc}. -To use QSV filters, you need to setup the QSV device correctly. For more information, please read @url{https://trac.ffmpeg.org/wiki/Hardware/QuickSync} +Running Vulkan filters requires you to initialize a hardware device and to pass that device to all filters in any filter graph. +@table @option -@section hstack_qsv -Stack input videos horizontally. +@item -init_hw_device vulkan[=@var{name}][:@var{device}[,@var{key=value}...]] +Initialise a new hardware device of type @var{vulkan} called @var{name}, using the +given device parameters and options in @var{key=value}. The following options +are supported: -This is the QSV 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. +@table @option +@item debug +Switches validation layers on if set to 1. -It accepts the following options: +@item linear_images +Allocates linear images. Does not apply to decoding. + +@item disable_multiplane +Disables multiplane images. Does not apply to decoding. +@end table + +@item -filter_hw_device @var{name} +Pass the hardware device called @var{name} to all filters in any filter graph. + +@end table + +For more detailed information see @url{https://www.ffmpeg.org/ffmpeg.html#Advanced-Video-options} + +@itemize +@item +Example of choosing the first device and running nlmeans_vulkan filter with default parameters on it. +@example +-init_hw_device vulkan=vk:0 -filter_hw_device vk -i INPUT -vf "hwupload,nlmeans_vulkan,hwdownload" OUTPUT +@end example +@end itemize + +As Vulkan filters are not able to access frame data in normal memory, all frame data needs to be uploaded (@ref{hwupload}) to hardware surfaces connected to the appropriate device before being used and then downloaded (@ref{hwdownload}) back to normal memory. Note that @ref{hwupload} will upload to a frame with the same layout as the software frame, so it may be necessary to add a @ref{format} filter immediately before to get the input into the right format and @ref{hwdownload} does not support all formats on the output - it is usually necessary to insert an additional @ref{format} filter immediately following in the graph to get the output in a supported format. + +@section avgblur_vulkan + +Apply an average blur filter, implemented on the GPU using Vulkan. + +The filter accepts the following options: @table @option -@item inputs -See @ref{hstack}. +@item sizeX +Set horizontal radius size. +Range is @code{[1, 32]} and default value is @code{3}. -@item shortest -See @ref{hstack}. +@item sizeY +Set vertical radius size. Range is @code{[1, 32]} and default value is @code{3}. -@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. +@item planes +Set which planes to filter. Default value is @code{0xf}, by which all planes are processed. @end table -@section vstack_qsv -Stack input videos vertically. +@section blend_vulkan -This is the QSV 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. +Blend two Vulkan frames into each other. -It accepts the following options: +The @code{blend} filter takes two input streams and outputs one +stream, the first input is the "top" layer and second input is +"bottom" layer. By default, the output terminates when the longest input terminates. + +A description of the accepted options follows. @table @option -@item inputs -See @ref{vstack}. +@item c0_mode +@item c1_mode +@item c2_mode +@item c3_mode +@item all_mode +Set blend mode for specific pixel component or all pixel components in case +of @var{all_mode}. Default value is @code{normal}. -@item shortest -See @ref{vstack}. +Available values for component modes are: +@table @samp +@item normal +@item multiply +@end table -@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_qsv -Stack video inputs into custom layout. +@section bwdif_vulkan -This is the QSV variant of the @ref{xstack} filter. +Deinterlacer using @ref{bwdif}, the "Bob Weaver Deinterlacing Filter" algorithm, implemented +on the GPU using Vulkan. -It accepts the following options: +It accepts the following parameters: @table @option -@item inputs -See @ref{xstack}. +@item mode +The interlacing mode to adopt. It accepts one of the following values: -@item shortest -See @ref{xstack}. +@table @option +@item 0, send_frame +Output one frame for each frame. +@item 1, send_field +Output one frame for each field. +@end table -@item layout -See @ref{xstack}. -Moreover, this permits the user to supply output size for each input stream. -@example -xstack_qsv=inputs=4:layout=0_0_1920x1080|0_h0_1920x1080|w0_0_1920x1080|w0_h0_1920x1080 -@end example +The default value is @code{send_field}. -@item grid -See @ref{xstack}. +@item parity +The picture field parity assumed for the input interlaced video. It accepts one +of the following values: -@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}. +@table @option +@item 0, tff +Assume the top field is first. +@item 1, bff +Assume the bottom field is first. +@item -1, auto +Enable automatic detection of field parity. +@end table -@item fill -See @ref{xstack}. +The default value is @code{auto}. +If the interlacing is unknown or the decoder does not export this information, +top field first will be assumed. + +@item deint +Specify which frames to deinterlace. Accepts one of the following +values: + +@table @option +@item 0, all +Deinterlace all frames. +@item 1, interlaced +Only deinterlace frames marked as interlaced. @end table -@c man end QSV VIDEO FILTERS +The default value is @code{all}. +@end table -@chapter Video Sources -@c man begin VIDEO SOURCES +@section chromaber_vulkan -Below is a description of the currently available video sources. +Apply an effect that emulates chromatic aberration. Works best with RGB inputs, +but provides a similar effect with YCbCr inputs too. -@section buffer +@table @option +@item dist_x +Horizontal displacement multiplier. Each chroma pixel's position will be multiplied +by this amount, starting from the center of the image. Default is @code{0}. -Buffer video frames, and make them available to the filter chain. +@item dist_y +Similarly, this sets the vertical displacement multiplier. Default is @code{0}. -This source is mainly intended for a programmatic use, in particular -through the interface defined in @file{libavfilter/buffersrc.h}. +@end table + +@section color_vulkan + +Video source that creates a Vulkan frame of a solid color. +Useful for benchmarking, or overlaying. It accepts the following parameters: @table @option +@item color +The color to use. Either a name, or a hexadecimal value. +The default value is @code{black}. -@item video_size -Specify the size (width and height) of the buffered video frames. For the -syntax of this option, check the -@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}. +@item size +The size of the output frame. Default value is @code{1920x1080}. -@item width -The input video width. +@item rate +The framerate to output at. Default value is @code{60} frames per second. -@item height -The input video height. +@item duration +The video duration. Default value is @code{-0.000001}. -@item pix_fmt -A string representing the pixel format of the buffered video frames. -It may be a number corresponding to a pixel format, or a pixel format -name. +@item sar +The video signal aspect ratio. Default value is @code{1/1}. -@item time_base -Specify the timebase assumed by the timestamps of the buffered frames. +@item format +The pixel format of the output Vulkan frames. Default value is @code{yuv444p}. -@item frame_rate -Specify the frame rate expected for the video stream. +@item out_range +Set the output YCbCr sample range. -@item pixel_aspect, sar -The sample (pixel) aspect ratio of the input video. +This allows the autodetected value to be overridden as well as allows forcing +a specific value used for the output and encoder. If not specified, the +range depends on the pixel format. Possible values: -@item hw_frames_ctx -When using a hardware pixel format, this should be a reference to an -AVHWFramesContext describing input frames. +@table @samp +@item auto/unknown +Choose automatically. + +@item jpeg/full/pc +Set full range (0-255 in case of 8-bit luma). + +@item mpeg/limited/tv +Set "MPEG" range (16-235 in case of 8-bit luma). @end table -For example: -@example -buffer=width=320:height=240:pix_fmt=yuv410p:time_base=1/24:sar=1 -@end example +@end table -will instruct the source to accept video frames with size 320x240 and -with format "yuv410p", assuming 1/24 as the timestamps timebase and -square pixels (1:1 sample aspect ratio). -Since the pixel format with name "yuv410p" corresponds to the number 6 -(check the enum AVPixelFormat definition in @file{libavutil/pixfmt.h}), -this example corresponds to: -@example -buffer=size=320x240:pixfmt=6:time_base=1/24:pixel_aspect=1/1 -@end example +@section vflip_vulkan -Alternatively, the options can be specified as a flat string, but this -syntax is deprecated: +Flips an image vertically. -@var{width}:@var{height}:@var{pix_fmt}:@var{time_base.num}:@var{time_base.den}:@var{pixel_aspect.num}:@var{pixel_aspect.den} +@section hflip_vulkan -@section cellauto +Flips an image horizontally. -Create a pattern generated by an elementary cellular automaton. +@section flip_vulkan -The initial state of the cellular automaton can be defined through the -@option{filename} and @option{pattern} options. If such options are -not specified an initial state is created randomly. +Flips an image along both the vertical and horizontal axis. -At each new frame a new row in the video is filled with the result of -the cellular automaton next generation. The behavior when the whole -frame is filled is defined by the @option{scroll} option. +@section gblur_vulkan -This source accepts the following options: +Apply Gaussian blur filter on Vulkan frames. + +The filter accepts the following options: @table @option -@item filename, f -Read the initial cellular automaton state, i.e. the starting row, from +@item sigma +Set horizontal sigma, standard deviation of Gaussian blur. Default is @code{0.5}. + +@item sigmaV +Set vertical sigma, if negative it will be same as @code{sigma}. +Default is @code{-1}. + +@item planes +Set which planes to filter. By default all planes are filtered. + +@item size +Set the kernel size along the horizontal axis. Default is @code{19}. + +@item sizeV +Set the kernel size along the vertical axis. Default is @code{0}, +which sets to use the same value as @var{size}. + +@end table + +@section nlmeans_vulkan + +Denoise frames using Non-Local Means algorithm, implemented on the GPU using +Vulkan. +Supports more pixel formats than @ref{nlmeans} or @ref{nlmeans_opencl}, including +alpha channel support. + +The filter accepts the following options. + +@table @option +@item s +Set denoising strength for all components. Default is 1.0. Must be in range [1.0, 100.0]. + +@item p +Set patch size for all planes. Default is 7. Must be odd number in range [0, 99]. + +@item r +Set research size. Default is 15. Must be odd number in range [0, 99]. + +@item t +Set parallelism. Default is 36. Must be a number in the range [1, 168]. +Larger values may speed up processing, at the cost of more VRAM. +Lower values will slow it down, reducing VRAM usage. +Only supported on GPUs with atomic float operations (RDNA3+, Ampere+). + +@item s0 +@item s1 +@item s2 +@item s3 +Set denoising strength for a specific component. Default is @var{1}, equal to @option{s}. +Must be odd number in range [1, 100]. + +@item p0 +@item p1 +@item p2 +@item p3 +Set patch size for a specific component. Default is @var{7}, equal to @option{p}. +Must be odd number in range [0, 99]. + +@end table + +@section overlay_vulkan + +Overlay one video on top of another. + +It takes two inputs and has one output. The first input is the "main" video on which the second input is overlaid. +This filter requires all inputs to use the same pixel format. So, format conversion may be needed. + +The filter accepts the following options: + +@table @option +@item x +Set the x coordinate of the overlaid video on the main video. +Default value is @code{0}. + +@item y +Set the y coordinate of the overlaid video on the main video. +Default value is @code{0}. + +@end table + +@section transpose_vt + +Transpose rows with columns in the input video and optionally flip it. +For more in depth examples see the @ref{transpose} video filter, which shares mostly the same options. + +It accepts the following parameters: + +@table @option + +@item dir +Specify the transposition direction. + +Can assume the following values: +@table @samp +@item cclock_flip +Rotate by 90 degrees counterclockwise and vertically flip. (default) + +@item clock +Rotate by 90 degrees clockwise. + +@item cclock +Rotate by 90 degrees counterclockwise. + +@item clock_flip +Rotate by 90 degrees clockwise and vertically flip. + +@item hflip +Flip the input video horizontally. + +@item vflip +Flip the input video vertically. + +@end table + +@item passthrough +Do not apply the transposition if the input geometry matches the one +specified by the specified value. It accepts the following values: +@table @samp +@item none +Always apply transposition. (default) +@item portrait +Preserve portrait geometry (when @var{height} >= @var{width}). +@item landscape +Preserve landscape geometry (when @var{width} >= @var{height}). +@end table + +@end table + +@section transpose_vulkan + +Transpose rows with columns in the input video and optionally flip it. +For more in depth examples see the @ref{transpose} video filter, which shares mostly the same options. + +It accepts the following parameters: + +@table @option + +@item dir +Specify the transposition direction. + +Can assume the following values: +@table @samp +@item cclock_flip +Rotate by 90 degrees counterclockwise and vertically flip. (default) + +@item clock +Rotate by 90 degrees clockwise. + +@item cclock +Rotate by 90 degrees counterclockwise. + +@item clock_flip +Rotate by 90 degrees clockwise and vertically flip. +@end table + +@item passthrough +Do not apply the transposition if the input geometry matches the one +specified by the specified value. It accepts the following values: +@table @samp +@item none +Always apply transposition. (default) +@item portrait +Preserve portrait geometry (when @var{height} >= @var{width}). +@item landscape +Preserve landscape geometry (when @var{width} >= @var{height}). +@end table + +@end table + +@c man end VULKAN VIDEO FILTERS + +@chapter QSV Video Filters +@c man begin QSV VIDEO FILTERS + +Below is a description of the currently available QSV video filters. + +To enable compilation of these filters you need to configure FFmpeg with +@code{--enable-libmfx} or @code{--enable-libvpl}. + +To use QSV filters, you need to setup the QSV device correctly. For more information, please read @url{https://trac.ffmpeg.org/wiki/Hardware/QuickSync} + +@section hstack_qsv +Stack input videos horizontally. + +This is the QSV 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 original 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_qsv +Stack input videos vertically. + +This is the QSV 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 original 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_qsv +Stack video inputs into custom layout. + +This is the QSV variant of the @ref{xstack} filter. + +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_qsv=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 QSV VIDEO FILTERS + +@chapter Video Sources +@c man begin VIDEO SOURCES + +Below is a description of the currently available video sources. + +@section buffer + +Buffer video frames, and make them available to the filter chain. + +This source is mainly intended for a programmatic use, in particular +through the interface defined in @file{libavfilter/buffersrc.h}. + +It accepts the following parameters: + +@table @option + +@item video_size +Specify the size (width and height) of the buffered video frames. For the +syntax of this option, check the +@ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}. + +@item width +The input video width. + +@item height +The input video height. + +@item pix_fmt +A string representing the pixel format of the buffered video frames. +It may be a number corresponding to a pixel format, or a pixel format +name. + +@item time_base +Specify the timebase assumed by the timestamps of the buffered frames. + +@item frame_rate +Specify the frame rate expected for the video stream. + +@item colorspace +A string representing the color space of the buffered video frames. +It may be a number corresponding to a color space, or a color space +name. + +@item range +A string representing the color range of the buffered video frames. +It may be a number corresponding to a color range, or a color range +name. + +@item pixel_aspect, sar +The sample (pixel) aspect ratio of the input video. + +@item hw_frames_ctx +When using a hardware pixel format, this should be a reference to an +AVHWFramesContext describing input frames. +@end table + +For example: +@example +buffer=width=320:height=240:pix_fmt=yuv410p:time_base=1/24:sar=1 +@end example + +will instruct the source to accept video frames with size 320x240 and +with format "yuv410p", assuming 1/24 as the timestamps timebase and +square pixels (1:1 sample aspect ratio). +Since the pixel format with name "yuv410p" corresponds to the number 6 +(check the enum AVPixelFormat definition in @file{libavutil/pixfmt.h}), +this example corresponds to: +@example +buffer=size=320x240:pixfmt=6:time_base=1/24:pixel_aspect=1/1 +@end example + +Alternatively, the options can be specified as a flat string, but this +syntax is deprecated: + +@var{width}:@var{height}:@var{pix_fmt}:@var{time_base.num}:@var{time_base.den}:@var{pixel_aspect.num}:@var{pixel_aspect.den} + +@section cellauto + +Create a pattern generated by an elementary cellular automaton. + +The initial state of the cellular automaton can be defined through the +@option{filename} and @option{pattern} options. If such options are +not specified an initial state is created randomly. + +At each new frame a new row in the video is filled with the result of +the cellular automaton next generation. The behavior when the whole +frame is filled is defined by the @option{scroll} option. + +This source accepts the following options: + +@table @option +@item filename, f +Read the initial cellular automaton state, i.e. the starting row, from the specified file. In the file, each non-whitespace character is considered an alive cell, a newline will terminate the row, and further characters in the @@ -27171,7 +28716,12 @@ Only affects hardware cursors. If a game or application renders its own cursor, it'll always be captured. @item framerate -Framerate at which the desktop will be captured. +Maximum framerate at which the desktop will be captured - the interval between +successive frames will not be smaller than the inverse of the framerate. When +@var{dup_frames} is true (the default) and the desktop is not being updated +often enough, the filter will duplicate a previous frame. Note that there is no +background buffering going on, so when the filter is not polled often enough +then the actual inter-frame interval may be significantly larger. Defaults to 30 FPS. @@ -27198,12 +28748,19 @@ It accepts the following values: Passes all supported output formats to DDA and returns what DDA decides to use. @item 8bit @item bgra -8 Bit formats always work, and DDA will convert to them if neccesary. +8 Bit formats always work, and DDA will convert to them if necessary. @item 10bit @item x2bgr10 Filter initialization will fail if 10 bit format is requested but unavailable. @end table +@item dup_frames +When this option is set to true (the default), the filter will duplicate frames +when the desktop has not been updated in order to maintain approximately +constant target framerate. When this option is set to false, the filter will +wait for the desktop to be updated (inter-frame intervals may vary significantly +in this case). + @end table @subsection Examples @@ -27264,9 +28821,22 @@ supposed to be generated forever. Set speed of gradients rotation. @item type, t -Set type of gradients, can be @code{linear} or @code{radial} or @code{circular} or @code{spiral}. +Set type of gradients. +Available values are: +@table @samp +@item linear +@item radial +@item circular +@item spiral +@item square @end table +Default type is @var{linear}. +@end table + +@subsection Commands + +This source supports the some above options as @ref{commands}. @section mandelbrot @@ -27561,6 +29131,137 @@ ffplay -f lavfi life=s=300x200:mold=10:r=60:ratio=0.1:death_color=#C83232:life_c @end example @end itemize +@section qrencodesrc + +Generate a QR code using the libqrencode library (see +@url{https://fukuchi.org/works/qrencode/}). + +To enable the compilation of this source, you need to configure FFmpeg with +@code{--enable-libqrencode}. + +The QR code is generated from the provided text or text pattern. The +corresponding QR code is scaled and put in the video output according to the +specified output size options. + +In case no text is specified, the QR code is not generated, but an empty colored +output is returned instead. + +This source accepts the following options: + +@table @option + +@item qrcode_width, q +@item padded_qrcode_width, Q +Specify an expression for the width of the rendered QR code, with and without +padding. The @var{qrcode_width} expression can reference the value set by the +@var{padded_qrcode_width} expression, and vice versa. +By default @var{padded_qrcode_width} is set to @var{qrcode_width}, meaning that +there is no padding. + +These expressions are evaluated only once, when initializing the source. +See the @ref{qrencode_expressions,,qrencode Expressions} section for details. + +Note that some of the constants are missing for the source (for example the +@var{x} or @var{t} or ¸@var{n}), since they only makes sense when evaluating the +expression for each frame rather than at initialization time. + +@item rate, r +Specify the frame rate of the sourced video, as the number of frames +generated per second. It has to be a string in the format +@var{frame_rate_num}/@var{frame_rate_den}, an integer number, a floating point +number or a valid video frame rate abbreviation. The default value is +"25". + +@item case_sensitive, cs +Instruct libqrencode to use case sensitive encoding. This is enabled by +default. This can be disabled to reduce the QR encoding size. + +@item level, l +Specify the QR encoding error correction level. With an higher correction level, +the encoding size will increase but the code will be more robust to corruption. +Lower level is @var{L}. + +It accepts the following values: +@table @samp +@item L +@item M +@item Q +@item H +@end table + +@item expansion +Select how the input text is expanded. Can be either @code{none}, or +@code{normal} (default). See the @ref{qrencode_text_expansion,,qrencode Text expansion} +section for details. + +@item text +@item textfile +Define the text to be rendered. In case neither is specified, no QR is encoded +(just an empty colored frame). + +In case expansion is enabled, the text is treated as a text template, using the +qrencode expansion mechanism. See the @ref{qrencode_text_expansion,,qrencode +Text expansion} section for details. + +@item background_color, bc +@item foreground_color, fc +Set the QR code and background color. The default value of +@var{foreground_color} is "black", the default value of @var{background_color} +is "white". + +For the syntax of the color options, check the @ref{color syntax,,"Color" +section in the ffmpeg-utils manual,ffmpeg-utils}. +@end table + +@subsection Examples + +@itemize +@item +Generate a QR code encoding the specified text with the default size: +@example +qrencodesrc=text=www.ffmpeg.org +@end example + +@item +Same as below, but select blue on pink colors: +@example +qrencodesrc=text=www.ffmpeg.org:bc=pink:fc=blue +@end example + +@item +Generate a QR code with width of 200 pixels and padding, making the padded width +4/3 of the QR code width: +@example +qrencodesrc=text=www.ffmpeg.org:q=200:Q=4/3*q +@end example + +@item +Generate a QR code with padded width of 200 pixels and padding, making the QR +code width 3/4 of the padded width: +@example +qrencodesrc=text=www.ffmpeg.org:Q=200:q=3/4*Q +@end example + +@item +Generate a QR code encoding the frame number: +@example +qrencodesrc=text=%@{n@} +@end example + +@item +Generate a QR code encoding the GMT timestamp: +@example +qrencodesrc=text=%@{gmtime@} +@end example + +@item +Generate a QR code encoding the timestamp expressed as a float: +@example +qrencodesrc=text=%@{pts@} +@end example + +@end itemize + @anchor{allrgb} @anchor{allyuv} @anchor{color} @@ -27727,7 +29428,7 @@ color=c=red@@0.2:s=qcif:r=10 @item If the input content is to be ignored, @code{nullsrc} can be used. The -following command generates noise in the luminance plane by employing +following command generates noise in the luma plane by employing the @code{geq} filter: @example nullsrc=s=256x256, geq=random(1)*255:128:128 @@ -27841,6 +29542,104 @@ Set max jump for single pan destination. Allowed range is from 1 to 10000. Set fractal type, can be default @code{carpet} or @code{triangle}. @end table +@section zoneplate +Generate a zoneplate test video pattern. + +This source accepts the following options: + +@table @option +@item size, s +Set frame size. For the syntax of this option, check the @ref{video size syntax,,"Video +size" section in the ffmpeg-utils manual,ffmpeg-utils}. Default value is "320x240". + +@item rate, r +Set frame rate, expressed as number of frames per second. Default +value is "25". + +@item duration, d +Set the duration of the sourced video. See +@ref{time duration syntax,,the Time duration section in the ffmpeg-utils(1) manual,ffmpeg-utils} +for the accepted syntax. + +If not specified, or the expressed duration is negative, the video is +supposed to be generated forever. + +@item sar +Set the sample aspect ratio of the sourced video. + +@item precision +Set precision in bits for look-up table for sine calculations. Default value is 10. +Allowed range is from 4 to 16. + +@item xo +Set horizontal axis offset for output signal. Default value is 0. + +@item yo +Set vertical axis offset for output signal. Default value is 0. + +@item to +Set time axis offset for output signal. Default value is 0. + +@item k0 +Set 0-order, constant added to signal phase. Default value is 0. + +@item kx +Set 1-order, phase factor multiplier for horizontal axis. Default value is 0. + +@item ky +Set 1-order, phase factor multiplier for vertical axis. Default value is 0. + +@item kt +Set 1-order, phase factor multiplier for time axis. Default value is 0. + +@item kxt, kyt, kxy +Set phase factor multipliers for combination of spatial and temporal axis. +Default value is 0. + +@item kx2 +Set 2-order, phase factor multiplier for horizontal axis. Default value is 0. + +@item ky2 +Set 2-order, phase factor multiplier for vertical axis. Default value is 0. + +@item kt2 +Set 2-order, phase factor multiplier for time axis. Default value is 0. + +@item ku +Set the constant added to final phase to produce chroma-blue component of signal. +Default value is 0. + +@item kv +Set the constant added to final phase to produce chroma-red component of signal. +Default value is 0. +@end table + +@subsection Commands + +This source supports the some above options as @ref{commands}. + +@subsection Examples + +@itemize +@item +Generate horizontal color sine sweep: +@example +zoneplate=ku=512:kv=0:kt2=0:kx2=256:s=wvga:xo=-426:kt=11 +@end example + +@item +Generate vertical color sine sweep: +@example +zoneplate=ku=512:kv=0:kt2=0:ky2=156:s=wvga:yo=-240:kt=11 +@end example + +@item +Generate circular zone-plate: +@example +zoneplate=ku=512:kv=100:kt2=0:ky2=256:kx2=556:s=wvga:yo=0:kt=11 +@end example +@end itemize + @c man end VIDEO SOURCES @chapter Video Sinks @@ -28494,6 +30293,24 @@ live mixing). Sets the display scale for the loudness. Valid parameters are @code{absolute} (in LUFS) or @code{relative} (LU) relative to the target. This only affects the video output, not the summary or continuous log output. + +@item integrated +Read-only exported value for measured integrated loudness, in LUFS. + +@item range +Read-only exported value for measured loudness range, in LU. + +@item lra_low +Read-only exported value for measured LRA low, in LUFS. + +@item lra_high +Read-only exported value for measured LRA high, in LUFS. + +@item sample_peak +Read-only exported value for measured sample peak, in dBFS. + +@item true_peak +Read-only exported value for measured true peak, in dBFS. @end table @subsection Examples @@ -28908,7 +30725,7 @@ This is 1 if the filtered frame is a key-frame, 0 otherwise. @item pos the position in the file of the filtered frame, -1 if the information -is not available (e.g. for synthetic video) +is not available (e.g. for synthetic video); deprecated, do not use @item scene @emph{(video only)} value between 0 and 1 to indicate a new scene; a low value reflects a low @@ -29098,7 +30915,7 @@ constants: @table @option @item POS Original position in the file of the frame, or undefined if undefined -for the current frame. +for the current frame. Deprecated, do not use. @item PTS The presentation timestamp in input. @@ -29246,7 +31063,7 @@ the time in seconds of the current frame @item POS original position in the file of the frame, or undefined if undefined -for the current frame +for the current frame; deprecated, do not use @item PREV_INPTS The previous input PTS. @@ -29270,6 +31087,9 @@ The wallclock (RTC) time at the start of the movie in microseconds. @item TB The timebase of the input timestamps. +@item T_CHANGE +Time of the first frame after command was applied or time of the first frame if no commands. + @end table @subsection Examples @@ -29299,6 +31119,12 @@ Set fixed rate of 25 frames per second: setpts=N/(25*TB) @end example +@item +Apply a random jitter effect of +/-100 TB units: +@example +setpts=PTS+randomi(0, -100\,100) +@end example + @item Set fixed rate 25 fps with some jitter: @example @@ -29325,6 +31151,10 @@ asetpts=N/SR/TB @end itemize +@subsection Commands + +Both filters support all above options as @ref{commands}. + @section setrange Force color range for the output video frame. @@ -29713,13 +31543,29 @@ Set the frequency scale used. Allowed values are: @table @option @item linear -@item log2 +@item log @item bark @item mel @item erbs +@item sqrt +@item cbrt +@item qdrt +@item fm @end table Default value is @code{linear}. +@item iscale +Set the intensity scale used. Allowed values are: + +@table @option +@item linear +@item log +@item sqrt +@item cbrt +@item qdrt +@end table +Default value is @code{log}. + @item min Set the minimum frequency that will be used in output. Default is @code{20} Hz. @@ -29730,6 +31576,12 @@ Default is @code{20000} Hz. The real frequency upper limit depends on input audio's sample rate and such will be enforced on this value when it is set to value greater than Nyquist frequency. +@item imin +Set the minimum intensity that will be used in output. + +@item imax +Set the maximum intensity that will be used in output. + @item logb Set the logarithmic basis for brightness strength when mapping calculated magnitude values to pixel values. @@ -29789,6 +31641,13 @@ Direction from up to down. @item du Direction from down to up. @end table + +@item bar +Set the ratio of bargraph display to display size. Default is 0. + +@item rotation +Set color rotation, must be in [-1.0, 1.0] range. +Default value is @code{0}. @end table @section showfreqs @@ -30895,6 +32754,10 @@ Set stream output duration. By default duration is unlimited. Set foreground/background/additional color. @end table +@subsection Commands + +This source supports the some above options as @ref{commands}. + @anchor{movie} @section movie diff --git a/doc/formats.texi b/doc/formats.texi index 640b23b7906..69fc1457a43 100644 --- a/doc/formats.texi +++ b/doc/formats.texi @@ -46,7 +46,8 @@ Enable fast, but inaccurate seeks for some formats. @item genpts Generate missing PTS if DTS is present. @item igndts -Ignore DTS if PTS is set. Inert when nofillin is set. +Ignore DTS if PTS is also set. In case the PTS is set, the DTS value +is set to NOPTS. This is ignored when the @code{nofillin} flag is set. @item ignidx Ignore index. @item nobuffer diff --git a/doc/general_contents.texi b/doc/general_contents.texi index 2eeebd847da..f269cbd1a95 100644 --- a/doc/general_contents.texi +++ b/doc/general_contents.texi @@ -343,6 +343,22 @@ libxavs2 is under the GNU Public License Version 2 or later details), you must upgrade FFmpeg's license to GPL in order to use it. @end float +@section eXtra-fast Essential Video Encoder (XEVE) + +FFmpeg can make use of the XEVE library for EVC video encoding. + +Go to @url{https://github.com/mpeg5/xeve} and follow the instructions for +installing the XEVE library. Then pass @code{--enable-libxeve} to configure to +enable it. + +@section eXtra-fast Essential Video Decoder (XEVD) + +FFmpeg can make use of the XEVD library for EVC video decoding. + +Go to @url{https://github.com/mpeg5/xevd} and follow the instructions for +installing the XEVD library. Then pass @code{--enable-libxevd} to configure to +enable it. + @section ZVBI ZVBI is a VBI decoding library which can be used by FFmpeg to decode DVB @@ -598,6 +614,7 @@ library: @item raw DTS @tab X @tab X @item raw DTS-HD @tab @tab X @item raw E-AC-3 @tab X @tab X +@item raw EVC @tab X @tab X @item raw FLAC @tab X @tab X @item raw GSM @tab @tab X @item raw H.261 @tab X @tab X @@ -615,6 +632,7 @@ library: @item raw video @tab X @tab X @item raw id RoQ @tab X @tab @item raw OBU @tab X @tab X +@item raw OSQ @tab @tab X @item raw SBC @tab X @tab X @item raw Shorten @tab @tab X @item raw TAK @tab @tab X @@ -652,7 +670,8 @@ library: @item Redirector @tab @tab X @item RedSpark @tab @tab X @item Renderware TeXture Dictionary @tab @tab X -@item Resolume DXV @tab @tab X +@item Resolume DXV @tab X @tab X + @tab Encoding is only supported for the DXT1 (Normal Quality, No Alpha) texture format. @item RF64 @tab @tab X @item RL2 @tab @tab X @tab Audio and video format used in some games by Entertainment Software Partners. @@ -943,6 +962,8 @@ following image formats are supported: @item Electronic Arts TQI video @tab @tab X @item Escape 124 @tab @tab X @item Escape 130 @tab @tab X +@item EVC / MPEG-5 Part 1 @tab E @tab E + @tab encoding and decoding supported through external libraries libxeve and libxevd @item FFmpeg video codec #1 @tab X @tab X @tab lossless codec (fourcc: FFV1) @item Flash Screen Video v1 @tab X @tab X @@ -996,6 +1017,7 @@ following image formats are supported: @item Lagarith @tab @tab X @item LCL (LossLess Codec Library) MSZH @tab @tab X @item LCL (LossLess Codec Library) ZLIB @tab E @tab E +@item LEAD MCMP @tab @tab X @item LOCO @tab @tab X @item LucasArts SANM/Smush @tab @tab X @tab Used in LucasArts games / SMUSH animations. @@ -1006,7 +1028,7 @@ following image formats are supported: @tab Also known as Microsoft Screen 3. @item Microsoft Expression Encoder Screen @tab @tab X @tab Also known as Microsoft Titanium Screen 2. -@item Microsoft RLE @tab @tab X +@item Microsoft RLE @tab X @tab X @item Microsoft Screen 1 @tab @tab X @tab Also known as Windows Media Video V7 Screen. @item Microsoft Screen 2 @tab @tab X @@ -1066,6 +1088,8 @@ following image formats are supported: @item RealVideo 4.0 @tab @tab X @item Renderware TXD (TeXture Dictionary) @tab @tab X @tab Texture dictionaries used by the Renderware Engine. +@item RivaTuner Video @tab @tab X + @tab fourcc: 'RTV1' @item RL2 video @tab @tab X @tab used in some games by Entertainment Software Partners @item ScreenPressor @tab @tab X @@ -1102,6 +1126,8 @@ following image formats are supported: @item v408 QuickTime uncompressed 4:4:4:4 @tab X @tab X @item v410 QuickTime uncompressed 4:4:4 10-bit @tab X @tab X @item VBLE Lossless Codec @tab @tab X +@item vMix Video @tab @tab X + @tab fourcc: 'VMX1' @item VMware Screen Codec / VMware Video @tab @tab X @tab Codec used in videos captured by VMware. @item Westwood Studios VQA (Vector Quantized Animation) video @tab @tab X @@ -1294,6 +1320,7 @@ following image formats are supported: @item On2 AVC (Audio for Video Codec) @tab @tab X @item Opus @tab E @tab X @tab encoding supported through external library libopus +@item OSQ (Original Sound Quality) @tab @tab X @item PCM A-law @tab X @tab X @item PCM mu-law @tab X @tab X @item PCM Archimedes VIDC @tab X @tab X diff --git a/doc/git-howto.texi b/doc/git-howto.texi index f4e2f2ec232..075b188abe7 100644 --- a/doc/git-howto.texi +++ b/doc/git-howto.texi @@ -66,7 +66,7 @@ This will put the FFmpeg sources into the directory @var{} and let you push back your changes to the remote repository. @example -git clone gil@@ffmpeg.org:ffmpeg-web +git clone git@@ffmpeg.org:ffmpeg-web @end example This will put the source of the FFmpeg website into the directory diff --git a/doc/indevs.texi b/doc/indevs.texi index 8a198c4b441..d1b2bacf8b8 100644 --- a/doc/indevs.texi +++ b/doc/indevs.texi @@ -222,7 +222,8 @@ $ ffmpeg -f avfoundation -capture_raw_data true -i "zr100:none" out.dv @section bktr -BSD video input device. +BSD video input device. Deprecated and will be removed - please contact +the developers if you are interested in maintaining it. @subsection Options @@ -722,7 +723,7 @@ Win32 GDI-based screen capture device. This device allows you to capture a region of the display on Windows. -There are two options for the input filename: +Amongst options for the imput filenames are such elements as: @example desktop @end example @@ -730,9 +731,13 @@ or @example title=@var{window_title} @end example +or +@example +hwnd=@var{window_hwnd} +@end example The first option will capture the entire desktop, or a fixed region of the -desktop. The second option will instead capture the contents of a single +desktop. The second and third options will instead capture the contents of a single window, regardless of its position on the screen. For example, to grab the entire desktop using @command{ffmpeg}: @@ -991,9 +996,8 @@ This input device reads data from the open output pads of a libavfilter filtergraph. For each filtergraph open output, the input device will create a -corresponding stream which is mapped to the generated output. Currently -only video data is supported. The filtergraph is specified through the -option @option{graph}. +corresponding stream which is mapped to the generated output. +The filtergraph is specified through the option @option{graph}. @subsection Options diff --git a/doc/infra.txt b/doc/infra.txt new file mode 100644 index 00000000000..30a85dd5ce9 --- /dev/null +++ b/doc/infra.txt @@ -0,0 +1,94 @@ +FFmpeg Infrastructure: +====================== + + + + +Servers: +~~~~~~~~ + + +Main Server: +------------ +Our Main server is hosted at telepoint.bg +for more details see: https://www.ffmpeg.org/#thanks_sponsor_0001 +Nothing runs on our main server directly, instead several VMs run on it. + + +ffmpeg.org VM: +-------------- +Web, mail, and public facing git, also website git + + +fftrac VM: +---------- +trac.ffmpeg.org Issue tracking + + +ffaux VM: +--------- +patchwork.ffmpeg.org Patch tracking +vote.ffmpeg.org Condorcet voting + + +fate: +----- +fate.ffmpeg.org FFmpeg automated testing environment + + +coverage: +--------- +coverage.ffmpeg.org Fate code coverage + + +The main and fate server as well as VMs currently run ubuntu + + + +Cronjobs: +~~~~~~~~~ +Part of the docs is in the main ffmpeg repository as texi files, this part is build by a cronjob. So is the +doxygen stuff as well as the FFmpeg git snapshot. +These 3 scripts are under the ffcron user + + + +Git: +~~~~ +Public facing git is provided by our infra, (https://git.ffmpeg.org/gitweb) +main developer ffmpeg git repository for historic reasons is provided by (git@source.ffmpeg.org:ffmpeg) +Other developer git repositories are provided via git@git.ffmpeg.org: +git mirrors are available on https://github.com/FFmpeg +(there are some exceptions where primary repositories are on github or elsewhere instead of the mirrors) + +Github mirrors are redundantly synced by multiple people + +You need a new git repository related to FFmpeg ? contact root at ffmpeg.org + + +Fate: +~~~~~ +fatesamples are provided via rsync. Every FFmpeg developer who has a shell account in ffmpeg.org +should be in the samples group and be able to upload samples. +See https://www.ffmpeg.org/fate.html#Uploading-new-samples-to-the-fate-suite + + + +Accounts: +~~~~~~~~~ +You need an account for some FFmpeg work? Send mail to root at ffmpeg.org + + + +VMs: +~~~~ +You need a VM, docker container for FFmpeg? contact root at ffmpeg.org +(for docker, CC Andriy) + + + +IRC: +~~~~ +irc channels are at https://libera.chat/ +irc channel archives are at https://libera.irclog.whitequark.org + diff --git a/doc/mailing-list-faq.texi b/doc/mailing-list-faq.texi index 534ef3f8020..b2028eeee13 100644 --- a/doc/mailing-list-faq.texi +++ b/doc/mailing-list-faq.texi @@ -344,7 +344,7 @@ recommended. Avoid sending the same message to multiple mailing lists. @item -Please follow our @url{https://ffmpeg.org/developer.html#Code-of-conduct, Code of Conduct}. +Please follow our @url{https://ffmpeg.org/community.html#Code-of-conduct, Code of Conduct}. @end itemize @chapter Help diff --git a/doc/metadata.texi b/doc/metadata.texi index be91059a98c..e081da7735f 100644 --- a/doc/metadata.texi +++ b/doc/metadata.texi @@ -1,3 +1,4 @@ +@anchor{metadata} @chapter Metadata @c man begin METADATA diff --git a/doc/mips.txt b/doc/mips.txt index a84e89ae798..d66ce3b4472 100644 --- a/doc/mips.txt +++ b/doc/mips.txt @@ -48,11 +48,6 @@ Files that have MIPS copyright notice in them: float_dsp_mips.c libm_mips.h softfloat_tables.h -* libavcodec/ - fft_fixed_32.c - fft_init_table.c - fft_table.h - mdct_fixed_32.c * libavcodec/mips/ aacdec_fixed.c aacsbr_fixed.c @@ -70,9 +65,6 @@ Files that have MIPS copyright notice in them: compute_antialias_float.h lsp_mips.h dsputil_mips.c - fft_mips.c - fft_table.h - fft_init_table.c fmtconvert_mips.c iirfilter_mips.c mpegaudiodsp_mips_fixed.c diff --git a/doc/multithreading.txt b/doc/multithreading.txt index 470194ff857..6c65ca96516 100644 --- a/doc/multithreading.txt +++ b/doc/multithreading.txt @@ -55,8 +55,7 @@ speed gain at this point but it should work. If there are inter-frame dependencies, so the codec calls ff_thread_report/await_progress(), set FF_CODEC_CAP_ALLOCATE_PROGRESS in -AVCodec.caps_internal and use ff_thread_get_buffer() to allocate frames. The -frames must then be freed with ff_thread_release_buffer(). +FFCodec.caps_internal and use ff_thread_get_buffer() to allocate frames. Otherwise decode directly into the user-supplied frames. Call ff_thread_report_progress() after some part of the current picture has decoded. diff --git a/doc/muxers.texi b/doc/muxers.texi index 79dd8641825..a10a8e216fb 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -19,69 +19,710 @@ enabled demuxers and muxers. A description of some of the currently available muxers follows. +@anchor{raw muxers} +@section Raw muxers + +This section covers raw muxers. They accept a single stream matching +the designated codec. They do not store timestamps or metadata. The +recognized extension is the same as the muxer name unless indicated +otherwise. + +It comprises the following muxers. The media type and the eventual +extensions used to automatically selects the muxer from the output +extensions are also shown. + +@table @samp +@item ac3 @emph{audio} +Dolby Digital, also known as AC-3. + +@item adx @emph{audio} +CRI Middleware ADX audio. + +This muxer will write out the total sample count near the start of the +first packet when the output is seekable and the count can be stored +in 32 bits. + +@item aptx @emph{audio} +aptX (Audio Processing Technology for Bluetooth) + +@item aptx_hd @emph{audio} (aptxdh) +aptX HD (Audio Processing Technology for Bluetooth) audio + +@item avs2 @emph{video} (avs, avs2) +AVS2-P2 (Audio Video Standard - Second generation - Part 2) / +IEEE 1857.4 video + +@item avs3 @emph{video} (avs3) +AVS3-P2 (Audio Video Standard - Third generation - Part 2) / +IEEE 1857.10 video + +@item cavsvideo @emph{video} (cavs) +Chinese AVS (Audio Video Standard - First generation) + +@item codec2raw @emph{audio} +Codec 2 audio. + +No extension is registered so format name has to be supplied e.g. with +the ffmpeg CLI tool @code{-f codec2raw}. + +@item data @emph{any} +Generic data muxer. + +This muxer accepts a single stream with any codec of any type. The +input stream has to be selected using the @code{-map} option with the +@command{ffmpeg} CLI tool. + +No extension is registered so format name has to be supplied e.g. with +the @command{ffmpeg} CLI tool @code{-f data}. + +@item dfpwm @emph{audio} (dfpwm) +Raw DFPWM1a (Dynamic Filter Pulse With Modulation) audio muxer. + +@item dirac @emph{video} (drc, vc2) +BBC Dirac video. + +The Dirac Pro codec is a subset and is standardized as SMPTE VC-2. + +@item dnxhd @emph{video} (dnxhd, dnxhr) +Avid DNxHD video. + +It is standardized as SMPTE VC-3. Accepts DNxHR streams. + +@item dts @emph{audio} +DTS Coherent Acoustics (DCA) audio + +@item eac3 @emph{audio} +Dolby Digital Plus, also known as Enhanced AC-3 + +@item evc @emph{video} (evc) +MPEG-5 Essential Video Coding (EVC) / EVC / MPEG-5 Part 1 EVC video + +@item g722 @emph{audio} +ITU-T G.722 audio + +@item g723_1 @emph{audio} (tco, rco) +ITU-T G.723.1 audio + +@item g726 @emph{audio} +ITU-T G.726 big-endian ("left-justified") audio. + +No extension is registered so format name has to be supplied e.g. with +the @command{ffmpeg} CLI tool @code{-f g726}. + +@item g726le @emph{audio} +ITU-T G.726 little-endian ("right-justified") audio. + +No extension is registered so format name has to be supplied e.g. with +the @command{ffmpeg} CLI tool @code{-f g726le}. + +@item gsm @emph{audio} +Global System for Mobile Communications audio + +@item h261 @emph{video} +ITU-T H.261 video + +@item h263 @emph{video} +ITU-T H.263 / H.263-1996, H.263+ / H.263-1998 / H.263 version 2 video + +@item h264 @emph{video} (h264, 264) +ITU-T H.264 / MPEG-4 Part 10 AVC video. Bitstream shall be converted +to Annex B syntax if it's in length-prefixed mode. + +@item hevc @emph{video} (hevc, h265, 265) +ITU-T H.265 / MPEG-H Part 2 HEVC video. Bitstream shall be converted +to Annex B syntax if it's in length-prefixed mode. + +@item m4v @emph{video} +MPEG-4 Part 2 video + +@item mjpeg @emph{video} (mjpg, mjpeg) +Motion JPEG video + +@item mlp @emph{audio} +Meridian Lossless Packing, also known as Packed PCM + +@item mp2 @emph{audio} (mp2, m2a, mpa) +MPEG-1 Audio Layer II audio + +@item mpeg1video @emph{video} (mpg, mpeg, m1v) +MPEG-1 Part 2 video. + +@item mpeg2video @emph{video} (m2v) +ITU-T H.262 / MPEG-2 Part 2 video + +@item obu @emph{video} +AV1 low overhead Open Bitstream Units muxer. + +Temporal delimiter OBUs will be inserted in all temporal units of the +stream. + +@item rawvideo @emph{video} (yuv, rgb) +Raw uncompressed video. + +@item sbc @emph{audio} (sbc, msbc) +Bluetooth SIG low-complexity subband codec audio + +@item truehd @emph{audio} (thd) +Dolby TrueHD audio + +@item vc1 @emph{video} +SMPTE 421M / VC-1 video +@end table + +@subsection Examples + +@itemize +@item +Store raw video frames with the @samp{rawvideo} muxer using @command{ffmpeg}: +@example +ffmpeg -f lavfi -i testsrc -t 10 -s hd1080p testsrc.yuv +@end example + +Since the rawvideo muxer do not store the information related to size +and format, this information must be provided when demuxing the file: +@example +ffplay -video_size 1920x1080 -pixel_format rgb24 -f rawvideo testsrc.rgb +@end example +@end itemize + +@section Raw PCM muxers +This section covers raw PCM (Pulse-Code Modulation) audio muxers. + +They accept a single stream matching the designated codec. They do not +store timestamps or metadata. The recognized extension is the same as +the muxer name. + +It comprises the following muxers. The optional additional extension +used to automatically select the muxer from the output extension is +also shown in parentheses. + +@table @samp +@item alaw (al) +PCM A-law + +@item f32be +PCM 32-bit floating-point big-endian + +@item f32le +PCM 32-bit floating-point little-endian + +@item f64be +PCM 64-bit floating-point big-endian + +@item f64le +PCM 64-bit floating-point little-endian + +@item mulaw (ul) +PCM mu-law + +@item s16be +PCM signed 16-bit big-endian + +@item s16le +PCM signed 16-bit little-endian + +@item s24be +PCM signed 24-bit big-endian + +@item s24le +PCM signed 24-bit little-endian + +@item s32be +PCM signed 32-bit big-endian + +@item s32le +PCM signed 32-bit little-endian + +@item s8 (sb) +PCM signed 8-bit + +@item u16be +PCM unsigned 16-bit big-endian + +@item u16le +PCM unsigned 16-bit little-endian + +@item u24be +PCM unsigned 24-bit big-endian + +@item u24le +PCM unsigned 24-bit little-endian + +@item u32be +PCM unsigned 32-bit big-endian + +@item u32le +PCM unsigned 32-bit little-endian + +@item u8 (ub) +PCM unsigned 8-bit + +@item vidc +PCM Archimedes VIDC +@end table + +@section MPEG-1/MPEG-2 program stream muxers + +This section covers formats belonging to the MPEG-1 and MPEG-2 Systems +family. + +The MPEG-1 Systems format (also known as ISO/IEEC 11172-1 or MPEG-1 +program stream) has been adopted for the format of media track stored +in VCD (Video Compact Disc). + +The MPEG-2 Systems standard (also known as ISO/IEEC 13818-1) covers +two containers formats, one known as transport stream and one known as +program stream; only the latter is covered here. + +The MPEG-2 program stream format (also known as VOB due to the +corresponding file extension) is an extension of MPEG-1 program +stream: in addition to support different codecs for the audio and +video streams, it also stores subtitles and navigation metadata. +MPEG-2 program stream has been adopted for storing media streams in +SVCD and DVD storage devices. + +This section comprises the following muxers. + +@table @samp +@item mpeg (mpg,mpeg) +MPEG-1 Systems / MPEG-1 program stream muxer. + +@item vcd +MPEG-1 Systems / MPEG-1 program stream (VCD) muxer. + +This muxer can be used to generate tracks in the format accepted by +the VCD (Video Compact Disc) storage devices. + +It is the same as the @samp{mpeg} muxer with a few differences. + +@item vob +MPEG-2 program stream (VOB) muxer. + +@item dvd +MPEG-2 program stream (DVD VOB) muxer. + +This muxer can be used to generate tracks in the format accepted by +the DVD (Digital Versatile Disc) storage devices. + +This is the same as the @samp{vob} muxer with a few differences. + +@item svcd (vob) +MPEG-2 program stream (SVCD VOB) muxer. + +This muxer can be used to generate tracks in the format accepted by +the SVCD (Super Video Compact Disc) storage devices. + +This is the same as the @samp{vob} muxer with a few differences. +@end table + +@subsection Options +@table @option +@item muxrate @var{rate} +Set user-defined mux rate expressed as a number of bits/s. If not +specied the automatically computed mux rate is employed. Default value +is @code{0}. + +@item preload @var{delay} +Set initial demux-decode delay in microseconds. Default value is +@code{500000}. +@end table + +@section MOV/MPEG-4/ISOMBFF muxers + +This section covers formats belonging to the QuickTime / MOV family, +including the MPEG-4 Part 14 format and ISO base media file format +(ISOBMFF). These formats share a common structure based on the ISO +base media file format (ISOBMFF). + +The MOV format was originally developed for use with Apple QuickTime. +It was later used as the basis for the MPEG-4 Part 1 (later Part 14) +format, also known as ISO/IEC 14496-1. That format was then +generalized into ISOBMFF, also named MPEG-4 Part 12 format, ISO/IEC +14496-12, or ISO/IEC 15444-12. + +It comprises the following muxers. + +@table @samp +@item 3gp +Third Generation Partnership Project (3GPP) format for 3G UMTS +multimedia services + +@item 3g2 +Third Generation Partnership Project 2 (3GP2 or 3GPP2) format for 3G +CDMA2000 multimedia services, similar to @samp{3gp} with extensions +and limitations + +@item f4v +Adobe Flash Video format + +@item ipod +MPEG-4 audio file format, as MOV/MP4 but limited to contain only audio +streams, typically played with the Apple ipod device + +@item ismv +Microsoft IIS (Internet Information Services) Smooth Streaming +Audio/Video (ISMV or ISMA) format. This is based on MPEG-4 Part 14 +format with a few incompatible variants, used to stream media files +for the Microsoft IIS server. + +@item mov +QuickTime player format identified by the @code{.mov} extension + +@item mp4 +MP4 or MPEG-4 Part 14 format + +@item psp +PlayStation Portable MP4/MPEG-4 Part 14 format variant. This is based +on MPEG-4 Part 14 format with a few incompatible variants, used to +play files on PlayStation devices. +@end table + +@subsection Fragmentation + +The @samp{mov}, @samp{mp4}, and @samp{ismv} muxers support +fragmentation. Normally, a MOV/MP4 file has all the metadata about all +packets stored in one location. + +This data is usually written at the end of the file, but it can be +moved to the start for better playback by adding @code{+faststart} to +the @code{-movflags}, or using the @command{qt-faststart} tool). + +A fragmented file consists of a number of fragments, where packets and +metadata about these packets are stored together. Writing a fragmented +file has the advantage that the file is decodable even if the writing +is interrupted (while a normal MOV/MP4 is undecodable if it is not +properly finished), and it requires less memory when writing very long +files (since writing normal MOV/MP4 files stores info about every +single packet in memory until the file is closed). The downside is +that it is less compatible with other applications. + +Fragmentation is enabled by setting one of the options that define +how to cut the file into fragments: +@table @option +@item frag_duration +@item frag_size +@item min_frag_duration +@item movflags +frag_keyframe +@item movflags +frag_custom +@end table + +If more than one condition is specified, fragments are cut when one of +the specified conditions is fulfilled. The exception to this is the +option @option{min_frag_duration}, which has to be fulfilled for any +of the other conditions to apply. + +@subsection Options + +@table @option + +@item brand @var{brand_string} +Override major brand. + +@item empty_hdlr_name @var{bool} +Enable to skip writing the name inside a @code{hdlr} box. +Default is @code{false}. + +@item encryption_key @var{key} +set the media encryption key in hexadecimal format + +@item encryption_kid @var{kid} +set the media encryption key identifier in hexadecimal format + +@item encryption_scheme @var{scheme} +configure the encryption scheme, allowed values are @samp{none}, and +@samp{cenc-aes-ctr} + +@item frag_duration @var{duration} +Create fragments that are @var{duration} microseconds long. + +@item frag_interleave @var{number} +Interleave samples within fragments (max number of consecutive +samples, lower is tighter interleaving, but with more overhead. It is +set to @code{0} by default. + +@item frag_size @var{size} +create fragments that contain up to @var{size} bytes of payload data + +@item iods_audio_profile @var{profile} +specify iods number for the audio profile atom (from -1 to 255), +default is @code{-1} + +@item iods_video_profile @var{profile} +specify iods number for the video profile atom (from -1 to 255), +default is @code{-1} + +@item ism_lookahead @var{num_entries} +specify number of lookahead entries for ISM files (from 0 to 255), +default is @code{0} + +@item min_frag_duration @var{duration} +do not create fragments that are shorter than @var{duration} microseconds long + +@item moov_size @var{bytes} +Reserves space for the moov atom at the beginning of the file instead of placing the +moov atom at the end. If the space reserved is insufficient, muxing will fail. + +@item mov_gamma @var{gamma} +specify gamma value for gama atom (as a decimal number from 0 to 10), +default is @code{0.0}, must be set together with @code{+ movflags} + +@item movflags @var{flags} +Set various muxing switches. The following flags can be used: +@table @samp +@item cmaf +write CMAF (Common Media Application Format) compatible fragmented +MP4 output + +@item dash +write DASH (Dynamic Adaptive Streaming over HTTP) compatible fragmented +MP4 output + +@item default_base_moof +Similarly to the @samp{omit_tfhd_offset} flag, this flag avoids +writing the absolute base_data_offset field in tfhd atoms, but does so +by using the new default-base-is-moof flag instead. This flag is new +from 14496-12:2012. This may make the fragments easier to parse in +certain circumstances (avoiding basing track fragment location +calculations on the implicit end of the previous track fragment). + +@item delay_moov +delay writing the initial moov until the first fragment is cut, or +until the first fragment flush + +@item disable_chpl +Disable Nero chapter markers (chpl atom). Normally, both Nero chapters +and a QuickTime chapter track are written to the file. With this +option set, only the QuickTime chapter track will be written. Nero +chapters can cause failures when the file is reprocessed with certain +tagging programs, like mp3Tag 2.61a and iTunes 11.3, most likely other +versions are affected as well. + +@item faststart +Run a second pass moving the index (moov atom) to the beginning of the +file. This operation can take a while, and will not work in various +situations such as fragmented output, thus it is not enabled by +default. + +@item frag_custom +Allow the caller to manually choose when to cut fragments, by calling +@code{av_write_frame(ctx, NULL)} to write a fragment with the packets +written so far. (This is only useful with other applications +integrating libavformat, not from @command{ffmpeg}.) + +@item frag_discont +signal that the next fragment is discontinuous from earlier ones + +@item frag_every_frame +fragment at every frame + +@item frag_keyframe +start a new fragment at each video keyframe + +@item global_sidx +write a global sidx index at the start of the file + +@item isml +create a live smooth streaming feed (for pushing to a publishing point) + +@item negative_cts_offsets +Enables utilization of version 1 of the CTTS box, in which the CTS offsets can +be negative. This enables the initial sample to have DTS/CTS of zero, and +reduces the need for edit lists for some cases such as video tracks with +B-frames. Additionally, eases conformance with the DASH-IF interoperability +guidelines. + +This option is implicitly set when writing @samp{ismv} (Smooth +Streaming) files. + +@item omit_tfhd_offset +Do not write any absolute base_data_offset in tfhd atoms. This avoids +tying fragments to absolute byte positions in the file/streams. + +@item prefer_icc +If writing colr atom prioritise usage of ICC profile if it exists in +stream packet side data. + +@item rtphint +add RTP hinting tracks to the output file + +@item separate_moof +Write a separate moof (movie fragment) atom for each track. Normally, +packets for all tracks are written in a moof atom (which is slightly +more efficient), but with this option set, the muxer writes one +moof/mdat pair for each track, making it easier to separate tracks. + +@item skip_sidx +Skip writing of sidx atom. When bitrate overhead due to sidx atom is +high, this option could be used for cases where sidx atom is not +mandatory. When the @samp{global_sidx} flag is enabled, this option +is ignored. + +@item skip_trailer +skip writing the mfra/tfra/mfro trailer for fragmented files + +@item use_metadata_tags +use mdta atom for metadata + +@item write_colr +write colr atom even if the color info is unspecified. This flag is +experimental, may be renamed or changed, do not use from scripts. + +@item write_gama +write deprecated gama atom +@end table + +@item movie_timescale @var{scale} +Set the timescale written in the movie header box (@code{mvhd}). +Range is 1 to INT_MAX. Default is @code{1000}. + +@item rtpflags @var{flags} +Add RTP hinting tracks to the output file. + +The following flags can be used: +@table @samp +@item h264_mode0 +use mode 0 for H.264 in RTP + +@item latm +use MP4A-LATM packetization instead of MPEG4-GENERIC for AAC + +@item rfc2190 +use RFC 2190 packetization instead of RFC 4629 for H.263 + +@item send_bye +send RTCP BYE packets when finishing + +@item skip_rtcp +do not send RTCP sender reports +@end table + +@item skip_iods @var{bool} +skip writing iods atom (default value is @code{true}) + +@item use_editlist @var{bool} +use edit list (default value is @code{auto}) + +@item use_stream_ids_as_track_ids @var{bool} +use stream ids as track ids (default value is @code{false}) + +@item video_track_timescale @var{scale} +Set the timescale used for video tracks. Range is @code{0} to INT_MAX. If +set to @code{0}, the timescale is automatically set based on the +native stream time base. Default is @code{0}. + +@item write_btrt @var{bool} +Force or disable writing bitrate box inside stsd box of a track. The +box contains decoding buffer size (in bytes), maximum bitrate and +average bitrate for the track. The box will be skipped if none of +these values can be computed. Default is @code{-1} or @code{auto}, +which will write the box only in MP4 mode. + +@item write_prft @var{option} +Write producer time reference box (PRFT) with a specified time source for the +NTP field in the PRFT box. Set value as @samp{wallclock} to specify timesource +as wallclock time and @samp{pts} to specify timesource as input packets' PTS +values. + +@item write_tmcd @var{bool} +Specify @code{on} to force writing a timecode track, @code{off} to disable it +and @code{auto} to write a timecode track only for mov and mp4 output (default). + +Setting value to @samp{pts} is applicable only for a live encoding use case, +where PTS values are set as as wallclock time at the source. For example, an +encoding use case with decklink capture source where @option{video_pts} and +@option{audio_pts} are set to @samp{abs_wallclock}. +@end table + +@subsection Examples + +@itemize +@item +Push Smooth Streaming content in real time to a publishing point on +IIS with the @samp{ismv} muxer using @command{ffmpeg}: +@example +ffmpeg -re @var{} -movflags isml+frag_keyframe -f ismv http://server/publishingpoint.isml/Streams(Encoder1) +@end example +@end itemize + @anchor{a64} @section a64 +A64 Commodore 64 video muxer. -A64 muxer for Commodore 64 video. Accepts a single @code{a64_multi} or @code{a64_multi5} codec video stream. +This muxer accepts a single @code{a64_multi} or @code{a64_multi5} +codec video stream. -@anchor{adts} -@section adts +@section ac4 +Raw AC-4 audio muxer. -Audio Data Transport Stream muxer. It accepts a single AAC stream. +This muxer accepts a single @code{ac4} audio stream. @subsection Options +@table @option +@item write_crc @var{bool} +when enabled, write a CRC checksum for each packet to the output, +default is @code{false} +@end table -It accepts the following options: +@anchor{adts} +@section adts +Audio Data Transport Stream muxer. -@table @option +It accepts a single AAC stream. +@subsection Options +@table @option @item write_id3v2 @var{bool} -Enable to write ID3v2.4 tags at the start of the stream. Default is disabled. +Enable to write ID3v2.4 tags at the start of the stream. Default is +disabled. @item write_apetag @var{bool} -Enable to write APE tags at the end of the stream. Default is disabled. +Enable to write APE tags at the end of the stream. Default is +disabled. @item write_mpeg2 @var{bool} -Enable to set MPEG version bit in the ADTS frame header to 1 which indicates MPEG-2. Default is 0, which indicates MPEG-4. - +Enable to set MPEG version bit in the ADTS frame header to 1 which +indicates MPEG-2. Default is 0, which indicates MPEG-4. @end table +@anchor{aea} +@section aea +MD STUDIO audio muxer. + +This muxer accepts a single ATRAC1 audio stream with either one or two channels +and a sample rate of 44100Hz. + +As AEA supports storing the track title, this muxer will also write +the title from stream's metadata to the container. + @anchor{aiff} @section aiff - Audio Interchange File Format muxer. @subsection Options - -It accepts the following options: - @table @option -@item write_id3v2 +@item write_id3v2 @var{bool} Enable ID3v2 tags writing when set to 1. Default is 0 (disabled). -@item id3v2_version +@item id3v2_version @var{bool} Select ID3v2 version to write. Currently only version 3 and 4 (aka. ID3v2.3 and ID3v2.4) are supported. The default is version 4. - @end table @anchor{alp} @section alp +High Voltage Software's Lego Racers game audio muxer. -Muxer for audio of High Voltage Software's Lego Racers game. It accepts a single ADPCM_IMA_ALP stream -with no more than 2 channels nor a sample rate greater than 44100 Hz. +It accepts a single ADPCM_IMA_ALP stream with no more than 2 channels +and a sample rate not greater than 44100 Hz. -Extensions: tun, pcm +Extensions: @code{tun}, @code{pcm} @subsection Options - -It accepts the following options: - @table @option - @item type @var{type} Set file type. +@var{type} accepts the following values: @table @samp @item tun Set file type as music. Must have a sample rate of 22050 Hz. @@ -90,43 +731,171 @@ Set file type as music. Must have a sample rate of 22050 Hz. Set file type as sfx. @item auto -Set file type as per output file extension. @code{.pcm} results in type @code{pcm} else type @code{tun} is set. @var{(default)} +Set file type as per output file extension. @code{.pcm} results in +type @code{pcm} else type @code{tun} is set. @var{(default)} +@end table +@end table + +@section amr +3GPP AMR (Adaptive Multi-Rate) audio muxer. + +It accepts a single audio stream containing an AMR NB stream. + +@section amv +AMV (Actions Media Video) format muxer. + +@section apm +Ubisoft Rayman 2 APM audio muxer. + +It accepts a single ADPCM IMA APM audio stream. + +@section apng +Animated Portable Network Graphics muxer. + +It accepts a single APNG video stream. + +@subsection Options +@table @option +@item final_delay @var{delay} +Force a delay expressed in seconds after the last frame of each +repetition. Default value is @code{0.0}. + +@item plays @var{repetitions} +specify how many times to play the content, @code{0} causes an infinte +loop, with @code{1} there is no loop +@end table + +@subsection Examples +@itemize +@item +Use @command{ffmpeg} to generate an APNG output with 2 repetitions, +and with a delay of half a second after the first repetition: +@example +ffmpeg -i INPUT -final_delay 0.5 -plays 2 out.apng +@end example +@end itemize + +@section argo_asf +Argonaut Games ASF audio muxer. + +It accepts a single ADPCM audio stream. +@subsection Options +@table @option +@item version_major @var{version} +override file major version, specified as an integer, default value is +@code{2} + +@item version_minor @var{version} +override file minor version, specified as an integer, default value is +@code{1} + +@item name @var{name} +Embed file name into file, if not specified use the output file +name. The name is truncated to 8 characters. @end table +@section argo_cvg +Argonaut Games CVG audio muxer. + +It accepts a single one-channel ADPCM 22050Hz audio stream. + +The @option{loop} and @option{reverb} options set the corresponding +flags in the header which can be later retrieved to process the audio +stream accordingly. + +@subsection Options +@table @option +@item skip_rate_check @var{bool} +skip sample rate check (default is @code{false}) + +@item loop @var{bool} +set loop flag (default is @code{false}) + +@item reverb @var{boolean} +set reverb flag (default is @code{true}) @end table @anchor{asf} -@section asf +@section asf, asf_stream +Advanced / Active Systems (or Streaming) Format audio muxer. -Advanced Systems Format muxer. +The @samp{asf_stream} variant should be selected for streaming. Note that Windows Media Audio (wma) and Windows Media Video (wmv) use this muxer too. @subsection Options +@table @option +@item packet_size @var{size} +Set the muxer packet size as a number of bytes. By tuning this setting +you may reduce data fragmentation or muxer overhead depending on your +source. Default value is @code{3200}, minimum is @code{100}, maximum +is @code{64Ki}. +@end table + +@section ass +ASS/SSA (SubStation Alpha) subtitles muxer. -It accepts the following options: +It accepts a single ASS subtitles stream. +@subsection Options @table @option -@item packet_size -Set the muxer packet size. By tuning this setting you may reduce data -fragmentation or muxer overhead depending on your source. Default value is -3200, minimum is 100, maximum is 64k. +@item ignore_readorder @var{bool} +Write dialogue events immediately, even if they are out-of-order, +default is @code{false}, otherwise they are cached until the expected +time event is found. +@end table + +@section ast +AST (Audio Stream) muxer. + +This format is used to play audio on some Nintendo Wii games. +It accepts a single audio stream. + +The @option{loopstart} and @option{loopend} options can be used to +define a section of the file to loop for players honoring such +options. + +@subsection Options +@table @option +@item loopstart @var{start} +Specify loop start position expressesd in milliseconds, from @code{-1} +to @code{INT_MAX}, in case @code{-1} is set then no loop is specified +(default -1) and the @option{loopend} value is ignored. + +@item loopend @var{end} +Specify loop end position expressed in milliseconds, from @code{0} to +@code{INT_MAX}, default is @code{0}, in case @code{0} is set it +assumes the total stream duration. @end table +@section au +SUN AU audio muxer. + +It accepts a single audio stream. + @anchor{avi} @section avi - Audio Video Interleaved muxer. -@subsection Options +AVI is a proprietary format developed by Microsoft, and later formally specified +through the Open DML specification. -It accepts the following options: +Because of differences in players implementations, it might be required to set +some options to make sure that the generated output can be correctly played by +the target player. +@subsection Options @table @option -@item reserve_index_space +@item flipped_raw_rgb @var{bool} +If set to @code{true}, store positive height for raw RGB bitmaps, which +indicates bitmap is stored bottom-up. Note that this option does not flip the +bitmap which has to be done manually beforehand, e.g. by using the @samp{vflip} +filter. Default is @code{false} and indicates bitmap is stored top down. + +@item reserve_index_space @var{size} Reserve the specified amount of bytes for the OpenDML master index of each stream within the file header. By default additional master indexes are embedded within the data packets if there is no space left in the first master @@ -139,65 +908,109 @@ The required index space depends on the output file size and should be about 16 bytes per gigabyte. When this option is omitted or set to zero the necessary index space is guessed. -@item write_channel_mask +Default value is @code{0}. + +@item write_channel_mask @var{bool} Write the channel layout mask into the audio stream header. This option is enabled by default. Disabling the channel mask can be useful in specific scenarios, e.g. when merging multiple audio streams into one for compatibility with software that only supports a single audio stream in AVI (see @ref{amerge,,the "amerge" section in the ffmpeg-filters manual,ffmpeg-filters}). +@end table -@item flipped_raw_rgb -If set to true, store positive height for raw RGB bitmaps, which indicates -bitmap is stored bottom-up. Note that this option does not flip the bitmap -which has to be done manually beforehand, e.g. by using the vflip filter. -Default is @var{false} and indicates bitmap is stored top down. +@section avif +AV1 (Alliance for Open Media Video codec 1) image format muxer. + +This muxers stores images encoded using the AV1 codec. + +It accepts one or two video streams. In case two video streams are +provided, the second one shall contain a single plane storing the +alpha mask. +In case more than one image is provided, the generated output is +considered an animated AVIF and the number of loops can be specified +with the @option{loop} option. + +This is based on the specification by Alliance for Open Media at url +@url{https://aomediacodec.github.io/av1-avif}. + +@subsection Options +@table @option +@item loop @var{count} +number of times to loop an animated AVIF, @code{0} specify an infinite +loop, default is @code{0} + +@item movie_timescale @var{timescale} +Set the timescale written in the movie header box (@code{mvhd}). +Range is 1 to INT_MAX. Default is @code{1000}. @end table +@section avm2 +ShockWave Flash (SWF) / ActionScript Virtual Machine 2 (AVM2) format muxer. + +It accepts one audio stream, one video stream, or both. + +@section bit +G.729 (.bit) file format muxer. + +It accepts a single G.729 audio stream. + +@section caf +Apple CAF (Core Audio Format) muxer. + +It accepts a single audio stream. + +@section codec2 +Codec2 audio audio muxer. + +It accepts a single codec2 audio stream. + @anchor{chromaprint} @section chromaprint +Chromaprint fingerprinter muxers. -Chromaprint fingerprinter. +To enable compilation of this filter you need to configure FFmpeg with +@code{--enable-chromaprint}. -This muxer feeds audio data to the Chromaprint library, -which generates a fingerprint for the provided audio data. See @url{https://acoustid.org/chromaprint} +This muxer feeds audio data to the Chromaprint library, which +generates a fingerprint for the provided audio data. See: +@url{https://acoustid.org/chromaprint} -It takes a single signed native-endian 16-bit raw audio stream of at most 2 channels. +It takes a single signed native-endian 16-bit raw audio stream of at +most 2 channels. @subsection Options - @table @option -@item silence_threshold -Threshold for detecting silence. Range is from -1 to 32767, where -1 disables -silence detection. Silence detection can only be used with version 3 of the -algorithm. -Silence detection must be disabled for use with the AcoustID service. Default is -1. - -@item algorithm -Version of algorithm to fingerprint with. Range is 0 to 4. -Version 3 enables silence detection. Default is 1. +@item algorithm @var{version} +Select version of algorithm to fingerprint with. Range is @code{0} to +@code{4}. Version @code{3} enables silence detection. Default is @code{1}. -@item fp_format +@item fp_format @var{format} Format to output the fingerprint as. Accepts the following options: @table @samp -@item raw -Binary raw fingerprint +@item base64 +Base64 compressed fingerprint @emph{(default)} @item compressed Binary compressed fingerprint -@item base64 -Base64 compressed fingerprint @emph{(default)} - +@item raw +Binary raw fingerprint @end table +@item silence_threshold @var{threshold} +Threshold for detecting silence. Range is from @code{-1} to +@code{32767}, where @code{-1} disables silence detection. Silence +detection can only be used with version @code{3} of the algorithm. + +Silence detection must be disabled for use with the AcoustID +service. Default is @code{-1}. @end table @anchor{crc} @section crc - -CRC (Cyclic Redundancy Check) testing format. +CRC (Cyclic Redundancy Check) muxer. This muxer computes and prints the Adler-32 CRC of all the input audio and video frames. By default audio frames are converted to signed @@ -211,34 +1024,39 @@ CRC=0x@var{CRC}, where @var{CRC} is a hexadecimal number 0-padded to See also the @ref{framecrc} muxer. @subsection Examples - -For example to compute the CRC of the input, and store it in the file -@file{out.crc}: +@itemize +@item +Use @command{ffmpeg} to compute the CRC of the input, and store it in +the file @file{out.crc}: @example ffmpeg -i INPUT -f crc out.crc @end example -You can print the CRC to stdout with the command: +@item +Use @command{ffmpeg} to print the CRC to stdout with the command: @example ffmpeg -i INPUT -f crc - @end example +@item You can select the output format of each frame with @command{ffmpeg} by -specifying the audio and video codec and format. For example to +specifying the audio and video codec and format. For example, to compute the CRC of the input audio converted to PCM unsigned 8-bit and the input video converted to MPEG-2 video, use the command: @example ffmpeg -i INPUT -c:a pcm_u8 -c:v mpeg2video -f crc - @end example +@end itemize @anchor{dash} @section dash +Dynamic Adaptive Streaming over HTTP (DASH) muxer. -Dynamic Adaptive Streaming over HTTP (DASH) muxer that creates segments -and manifest files according to the MPEG-DASH standard ISO/IEC 23009-1:2014. +This muxer creates segments and manifest files according to the +MPEG-DASH standard ISO/IEC 23009-1:2014 and following standard +updates. For more information see: - @itemize @bullet @item ISO DASH Specification: @url{http://standards.iso.org/ittf/PubliclyAvailableStandards/c065274_ISO_IEC_23009-1_2014.zip} @@ -246,279 +1064,547 @@ ISO DASH Specification: @url{http://standards.iso.org/ittf/PubliclyAvailableStan WebM DASH Specification: @url{https://sites.google.com/a/webmproject.org/wiki/adaptive-streaming/webm-dash-specification} @end itemize -It creates a MPD manifest file and segment files for each stream. +This muxer creates an MPD (Media Presentation Description) manifest +file and segment files for each stream. Segment files are placed in +the same directory of the MPD manifest file. -The segment filename might contain pre-defined identifiers used with SegmentTemplate -as defined in section 5.3.9.4.4 of the standard. Available identifiers are "$RepresentationID$", -"$Number$", "$Bandwidth$" and "$Time$". -In addition to the standard identifiers, an ffmpeg-specific "$ext$" identifier is also supported. -When specified ffmpeg will replace $ext$ in the file name with muxing format's extensions such as mp4, webm etc., +The segment filename might contain pre-defined identifiers used in the +manifest @code{SegmentTemplate} section as defined in section +5.3.9.4.4 of the standard. +Available identifiers are @code{$RepresentationID$}, @code{$Number$}, +@code{$Bandwidth$}, and @code{$Time$}. In addition to the standard +identifiers, an ffmpeg-specific @code{$ext$} identifier is also +supported. When specified, @command{ffmpeg} will replace @code{$ext$} +in the file name with muxing format's extensions such as @code{mp4}, +@code{webm} etc. + +@subsection Options +@table @option +@item adaptation_sets @var{adaptation_sets} +Assign streams to adaptation sets, specified in the MPD manifest +@code{AdaptationSets} section. + +An adaptation set contains a set of one or more streams accessed as a +single subset, e.g. corresponding streams encoded at different size +selectable by the user depending on the available bandwidth, or to +different audio streams with a different language. + +Each adaptation set is specified with the syntax: @example -ffmpeg -re -i -map 0 -map 0 -c:a libfdk_aac -c:v libx264 \ --b:v:0 800k -b:v:1 300k -s:v:1 320x170 -profile:v:1 baseline \ --profile:v:0 main -bf 1 -keyint_min 120 -g 120 -sc_threshold 0 \ --b_strategy 0 -ar:a:1 22050 -use_timeline 1 -use_template 1 \ --window_size 5 -adaptation_sets "id=0,streams=v id=1,streams=a" \ --f dash /path/to/out.mpd +id=@var{index},streams=@var{streams} @end example +where @var{index} must be a numerical index, and @var{streams} is a +sequence of @code{,}-separated stream indices. Multiple adaptation +sets can be specified, separated by spaces. + +To map all video (or audio) streams to an adaptation set, @code{v} (or +@code{a}) can be used as stream identifier instead of IDs. + +When no assignment is defined, this defaults to an adaptation set for +each stream. + +The following optional fields can also be specified: + @table @option -@item seg_duration @var{duration} -Set the segment length in seconds (fractional value can be set). The value is -treated as average segment duration when @var{use_template} is enabled and -@var{use_timeline} is disabled and as minimum segment duration for all the other -use cases. +@item descriptor +Define the descriptor as defined by ISO/IEC 23009-1:2014/Amd.2:2015. + +For example: +@example + +@end example + +The descriptor string should be a self-closing XML tag. + +@item frag_duration +Override the global fragment duration specified with the +@option{frag_duration} option. + +@item frag_type +Override the global fragment type specified with the +@option{frag_type} option. + +@item seg_duration +Override the global segment duration specified with the +@option{seg_duration} option. + +@item trick_id +Mark an adaptation set as containing streams meant to be used for +Trick Mode for the referenced adaptation set. +@end table + +A few examples of possible values for the @option{adaptation_sets} +option follow: +@example +id=0,seg_duration=2,frag_duration=1,frag_type=duration,streams=v id=1,seg_duration=2,frag_type=none,streams=a +@end example + +@example +id=0,seg_duration=2,frag_type=none,streams=0 id=1,seg_duration=10,frag_type=none,trick_id=0,streams=1 +@end example + +@item dash_segment_type @var{type} +Set DASH segment files type. + +Possible values: +@table @samp +@item auto +The dash segment files format will be selected based on the stream +codec. This is the default mode. +@item mp4 +the dash segment files will be in ISOBMFF/MP4 format +@item webm +the dash segment files will be in WebM format +@end table + +@item extra_window_size @var{size} +Set the maximum number of segments kept outside of the manifest before +removing from disk. + +@item format_options @var{options_list} +Set container format (mp4/webm) options using a @code{:}-separated list of +key=value parameters. Values containing @code{:} special characters must be +escaped. + @item frag_duration @var{duration} -Set the length in seconds of fragments within segments (fractional value can be set). +Set the length in seconds of fragments within segments, fractional +value can also be set. + @item frag_type @var{type} Set the type of interval for fragmentation. -@item window_size @var{size} -Set the maximum number of segments kept in the manifest. -@item extra_window_size @var{size} -Set the maximum number of segments kept outside of the manifest before removing from disk. -@item remove_at_exit @var{remove} -Enable (1) or disable (0) removal of all segments when finished. -@item use_template @var{template} -Enable (1) or disable (0) use of SegmentTemplate instead of SegmentList. -@item use_timeline @var{timeline} -Enable (1) or disable (0) use of SegmentTimeline in SegmentTemplate. -@item single_file @var{single_file} -Enable (1) or disable (0) storing all segments in one file, accessed using byte ranges. + +Possible values: +@table @samp +@item auto +set one fragment per segment + +@item every_frame +fragment at every frame + +@item duration +fragment at specific time intervals + +@item pframes +fragment at keyframes and following P-Frame reordering (Video only, +experimental) +@end table + +@item global_sidx @var{bool} +Write global @code{SIDX} atom. Applicable only for single file, mp4 +output, non-streaming mode. + +@item hls_master_name @var{file_name} +HLS master playlist name. Default is @file{master.m3u8}. + +@item hls_playlist @var{bool} +Generate HLS playlist files. The master playlist is generated with +filename specified by the @option{hls_master_name} option. One media +playlist file is generated for each stream with filenames +@file{media_0.m3u8}, @file{media_1.m3u8}, etc. + +@item http_opts @var{http_opts} +Specify a list of @code{:}-separated key=value options to pass to the +underlying HTTP protocol. Applicable only for HTTP output. + +@item http_persistent @var{bool} +Use persistent HTTP connections. Applicable only for HTTP output. + +@item http_user_agent @var{user_agent} +Override User-Agent field in HTTP header. Applicable only for HTTP +output. + +@item ignore_io_errors @var{bool} +Ignore IO errors during open and write. Useful for long-duration runs +with network output. This is disabled by default. + +@item index_correction @var{bool} +Enable or disable segment index correction logic. Applicable only when +@option{use_template} is enabled and @option{use_timeline} is +disabled. This is disabled by default. + +When enabled, the logic monitors the flow of segment indexes. If a +streams's segment index value is not at the expected real time +position, then the logic corrects that index value. + +Typically this logic is needed in live streaming use cases. The +network bandwidth fluctuations are common during long run +streaming. Each fluctuation can cause the segment indexes fall behind +the expected real time position. + +@item init_seg_name @var{init_name} +DASH-templated name to use for the initialization segment. Default is +@code{init-stream$RepresentationID$.$ext$}. @code{$ext$} is replaced +with the file name extension specific for the segment format. + +@item ldash @var{bool} +Enable Low-latency Dash by constraining the presence and values of +some elements. This is disabled by default. + +@item lhls @var{bool} +Enable Low-latency HLS (LHLS). Add @code{#EXT-X-PREFETCH} tag with +current segment's URI. hls.js player folks are trying to standardize +an open LHLS spec. The draft spec is available at +@url{https://github.com/video-dev/hlsjs-rfcs/blob/lhls-spec/proposals/0001-lhls.md}. + +This option tries to comply with the above open spec. It enables +@option{streaming} and @option{hls_playlist} options automatically. +This is an experimental feature. + +Note: This is not Apple's version LHLS. See +@url{https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis} + +@item master_m3u8_publish_rate @var{segment_intervals_count} +Publish master playlist repeatedly every after specified number of +segment intervals. + +@item max_playback_rate @var{rate} +Set the maximum playback rate indicated as appropriate for the +purposes of automatically adjusting playback latency and buffer +occupancy during normal playback by clients. + +@item media_seg_name @var{segment_name} +DASH-templated name to use for the media segments. Default is +@code{chunk-stream$RepresentationID$-$Number%05d$.$ext$}. @code{$ext$} +is replaced with the file name extension specific for the segment +format. + +@item method @var{method} +Use the given HTTP method to create output files. Generally set to @code{PUT} +or @code{POST}. + +@item min_playback_rate @var{rate} +Set the minimum playback rate indicated as appropriate for the +purposes of automatically adjusting playback latency and buffer +occupancy during normal playback by clients. + +@item mpd_profile @var{flags} +Set one or more MPD manifest profiles. + +Possible values: +@table @samp +@item dash +MPEG-DASH ISO Base media file format live profile +@item dvb_dash +DVB-DASH profile +@end table + +Default value is @code{dash}. + +@item remove_at_exit @var{bool} +Enable or disable removal of all segments when finished. This is +disabled by default. + +@item seg_duration @var{duration} +Set the segment length in seconds (fractional value can be set). The +value is treated as average segment duration when the +@option{use_template} option is enabled and the @option{use_timeline} +option is disabled and as minimum segment duration for all the other +use cases. + +Default value is @code{5}. + +@item single_file @var{bool} +Enable or disable storing all segments in one file, accessed using +byte ranges. This is disabled by default. + +The name of the single file can be specified with the +@option{single_file_name} option, if not specified assume the basename +of the manifest file with the output format extension. + @item single_file_name @var{file_name} -DASH-templated name to be used for baseURL. Implies @var{single_file} set to "1". In the template, "$ext$" is replaced with the file name extension specific for the segment format. -@item init_seg_name @var{init_name} -DASH-templated name to used for the initialization segment. Default is "init-stream$RepresentationID$.$ext$". "$ext$" is replaced with the file name extension specific for the segment format. -@item media_seg_name @var{segment_name} -DASH-templated name to used for the media segments. Default is "chunk-stream$RepresentationID$-$Number%05d$.$ext$". "$ext$" is replaced with the file name extension specific for the segment format. -@item utc_timing_url @var{utc_url} -URL of the page that will return the UTC timestamp in ISO format. Example: "https://time.akamai.com/?iso" -@item method @var{method} -Use the given HTTP method to create output files. Generally set to PUT or POST. -@item http_user_agent @var{user_agent} -Override User-Agent field in HTTP header. Applicable only for HTTP output. -@item http_persistent @var{http_persistent} -Use persistent HTTP connections. Applicable only for HTTP output. -@item hls_playlist @var{hls_playlist} -Generate HLS playlist files as well. The master playlist is generated with the filename @var{hls_master_name}. -One media playlist file is generated for each stream with filenames media_0.m3u8, media_1.m3u8, etc. -@item hls_master_name @var{file_name} -HLS master playlist name. Default is "master.m3u8". -@item streaming @var{streaming} -Enable (1) or disable (0) chunk streaming mode of output. In chunk streaming -mode, each frame will be a moof fragment which forms a chunk. -@item adaptation_sets @var{adaptation_sets} -Assign streams to AdaptationSets. Syntax is "id=x,streams=a,b,c id=y,streams=d,e" with x and y being the IDs -of the adaptation sets and a,b,c,d and e are the indices of the mapped streams. +DASH-templated name to use for the manifest @code{baseURL} +element. Imply that the @option{single_file} option is set to +@var{true}. In the template, @code{$ext$} is replaced with the file +name extension specific for the segment format. -To map all video (or audio) streams to an AdaptationSet, "v" (or "a") can be used as stream identifier instead of IDs. +@item streaming @var{bool} +Enable or disable chunk streaming mode of output. In chunk streaming +mode, each frame will be a @code{moof} fragment which forms a +chunk. This is disabled by default. -When no assignment is defined, this defaults to an AdaptationSet for each stream. +@item target_latency @var{target_latency} +Set an intended target latency in seconds for serving (fractional +value can be set). Applicable only when the @option{streaming} and +@option{write_prft} options are enabled. This is an informative fields +clients can use to measure the latency of the service. -Optional syntax is "id=x,seg_duration=x,frag_duration=x,frag_type=type,descriptor=descriptor_string,streams=a,b,c id=y,seg_duration=y,frag_type=type,streams=d,e" and so on, -descriptor is useful to the scheme defined by ISO/IEC 23009-1:2014/Amd.2:2015. -For example, -adaptation_sets "id=0,descriptor=,streams=v". -Please note that descriptor string should be a self-closing xml tag. -seg_duration, frag_duration and frag_type override the global option values for each adaptation set. -For example, -adaptation_sets "id=0,seg_duration=2,frag_duration=1,frag_type=duration,streams=v id=1,seg_duration=2,frag_type=none,streams=a" -type_id marks an adaptation set as containing streams meant to be used for Trick Mode for the referenced adaptation set. -For example, -adaptation_sets "id=0,seg_duration=2,frag_type=none,streams=0 id=1,seg_duration=10,frag_type=none,trick_id=0,streams=1" @item timeout @var{timeout} -Set timeout for socket I/O operations. Applicable only for HTTP output. -@item index_correction @var{index_correction} -Enable (1) or Disable (0) segment index correction logic. Applicable only when -@var{use_template} is enabled and @var{use_timeline} is disabled. +Set timeout for socket I/O operations expressed in seconds (fractional +value can be set). Applicable only for HTTP output. -When enabled, the logic monitors the flow of segment indexes. If a streams's -segment index value is not at the expected real time position, then the logic -corrects that index value. +@item update_period @var{period} +Set the MPD update period, for dynamic content. The unit is +second. If set to @code{0}, the period is automatically computed. -Typically this logic is needed in live streaming use cases. The network bandwidth -fluctuations are common during long run streaming. Each fluctuation can cause -the segment indexes fall behind the expected real time position. -@item format_options @var{options_list} -Set container format (mp4/webm) options using a @code{:} separated list of -key=value parameters. Values containing @code{:} special characters must be -escaped. +Default value is @code{0}. -@item global_sidx @var{global_sidx} -Write global SIDX atom. Applicable only for single file, mp4 output, non-streaming mode. +@item use_template @var{bool} +Enable or disable use of @code{SegmentTemplate} instead of +@code{SegmentList} in the manifest. This is enabled by default. -@item dash_segment_type @var{dash_segment_type} -Possible values: -@table @option -@item auto -If this flag is set, the dash segment files format will be selected based on the stream codec. This is the default mode. +@item use_timeline @var{bool} +Enable or disable use of @code{SegmentTimeline} within the +@code{SegmentTemplate} manifest section. This is enabled by default. -@item mp4 -If this flag is set, the dash segment files will be in in ISOBMFF format. +@item utc_timing_url @var{url} +URL of the page that will return the UTC timestamp in ISO +format, for example @code{https://time.akamai.com/?iso} -@item webm -If this flag is set, the dash segment files will be in in WebM format. +@item window_size @var{size} +Set the maximum number of segments kept in the manifest, discard the +oldest one. This is useful for live streaming. + +If the value is @code{0}, all segments are kept in the +manifest. Default value is @code{0}. + +@item write_prft @var{write_prft} +Write Producer Reference Time elements on supported streams. This also +enables writing prft boxes in the underlying muxer. Applicable only +when the @var{utc_url} option is enabled. It is set to @var{auto} by +default, in which case the muxer will attempt to enable it only in +modes that require it. @end table -@item ignore_io_errors @var{ignore_io_errors} -Ignore IO errors during open and write. Useful for long-duration runs with network output. +@subsection Example +Generate a DASH output reading from an input source in realtime using +@command{ffmpeg}. -@item lhls @var{lhls} -Enable Low-latency HLS(LHLS). Adds #EXT-X-PREFETCH tag with current segment's URI. -hls.js player folks are trying to standardize an open LHLS spec. The draft spec is available in https://github.com/video-dev/hlsjs-rfcs/blob/lhls-spec/proposals/0001-lhls.md -This option tries to comply with the above open spec. -It enables @var{streaming} and @var{hls_playlist} options automatically. -This is an experimental feature. +Two multimedia streams are generated from the input file, both +containing a video stream encoded through @samp{libx264}, and an audio +stream encoded with @samp{libfdk_aac}. The first multimedia stream +contains video with a bitrate of 800k and audio at the default rate, +the second with video scaled to 320x170 pixels at 300k and audio +resampled at 22005 Hz. + +The @option{window_size} option keeps only the latest 5 segments with +the default duration of 5 seconds. + +@example +ffmpeg -re -i -map 0 -map 0 -c:a libfdk_aac -c:v libx264 \ +-b:v:0 800k -profile:v:0 main \ +-b:v:1 300k -s:v:1 320x170 -profile:v:1 baseline -ar:a:1 22050 \ +-bf 1 -keyint_min 120 -g 120 -sc_threshold 0 -b_strategy 0 \ +-use_timeline 1 -use_template 1 -window_size 5 \ +-adaptation_sets "id=0,streams=v id=1,streams=a" \ +-f dash /path/to/out.mpd +@end example -Note: This is not Apple's version LHLS. See @url{https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis} +@section daud +D-Cinema audio muxer. -@item ldash @var{ldash} -Enable Low-latency Dash by constraining the presence and values of some elements. +It accepts a single 6-channels audio stream resampled at 96000 Hz +encoded with the @samp{pcm_24daud} codec. -@item master_m3u8_publish_rate @var{master_m3u8_publish_rate} -Publish master playlist repeatedly every after specified number of segment intervals. +@subsection Example +Use @command{ffmpeg} to mux input audio to a @samp{5.1} channel layout +resampled at 96000Hz: +@example +ffmpeg -i INPUT -af aresample=96000,pan=5.1 slow.302 +@end example -@item write_prft @var{write_prft} -Write Producer Reference Time elements on supported streams. This also enables writing -prft boxes in the underlying muxer. Applicable only when the @var{utc_url} option is enabled. -It's set to auto by default, in which case the muxer will attempt to enable it only in modes -that require it. +For ffmpeg versions before 7.0 you might have to use the @samp{asetnsamples} +filter to limit the muxed packet size, because this format does not support +muxing packets larger than 65535 bytes (3640 samples). For newer ffmpeg +versions audio is automatically packetized to 36000 byte (2000 sample) packets. -@item mpd_profile @var{mpd_profile} -Set one or more manifest profiles. +@section dv +DV (Digital Video) muxer. -@item http_opts @var{http_opts} -A :-separated list of key=value options to pass to the underlying HTTP -protocol. Applicable only for HTTP output. +It accepts exactly one @samp{dvvideo} video stream and at most two +@samp{pcm_s16} audio streams. More constraints are defined by the +property of the video, which must correspond to a DV video supported +profile, and on the framerate. -@item target_latency @var{target_latency} -Set an intended target latency in seconds (fractional value can be set) for serving. Applicable only when @var{streaming} and @var{write_prft} options are enabled. -This is an informative fields clients can use to measure the latency of the service. +@subsection Example +Use @command{ffmpeg} to convert the input: +@example +ffmpeg -i INPUT -s:v 720x480 -pix_fmt yuv411p -r 29.97 -ac 2 -ar 48000 -y out.dv +@end example -@item min_playback_rate @var{min_playback_rate} -Set the minimum playback rate indicated as appropriate for the purposes of automatically -adjusting playback latency and buffer occupancy during normal playback by clients. +@section ffmetadata +FFmpeg metadata muxer. -@item max_playback_rate @var{max_playback_rate} -Set the maximum playback rate indicated as appropriate for the purposes of automatically -adjusting playback latency and buffer occupancy during normal playback by clients. +This muxer writes the streams metadata in the @samp{ffmetadata} +format. -@item update_period @var{update_period} - Set the mpd update period ,for dynamic content. - The unit is second. +See @ref{metadata,,the Metadata chapter,ffmpeg-formats} for +information about the format. -@end table +@subsection Example + +Use @command{ffmpeg} to extract metadata from an input file to a @file{metadata.ffmeta} +file in @samp{ffmetadata} format: +@example +ffmpeg -i INPUT -f ffmetadata metadata.ffmeta +@end example @anchor{fifo} @section fifo +FIFO (First-In First-Out) muxer. -The fifo pseudo-muxer allows the separation of encoding and muxing by using -first-in-first-out queue and running the actual muxer in a separate thread. This -is especially useful in combination with the @ref{tee} muxer and can be used to -send data to several destinations with different reliability/writing speed/latency. +The @samp{fifo} pseudo-muxer allows the separation of encoding and +muxing by using a first-in-first-out queue and running the actual muxer +in a separate thread. -API users should be aware that callback functions (interrupt_callback, -io_open and io_close) used within its AVFormatContext must be thread-safe. +This is especially useful in combination with +the @ref{tee} muxer and can be used to send data to several +destinations with different reliability/writing speed/latency. -The behavior of the fifo muxer if the queue fills up or if the output fails is -selectable, +The target muxer is either selected from the output name or specified +through the @option{fifo_format} option. +The behavior of the @samp{fifo} muxer if the queue fills up or if the +output fails (e.g. if a packet cannot be written to the output) is +selectable: @itemize @bullet - @item -output can be transparently restarted with configurable delay between retries -based on real time or time of the processed stream. +Output can be transparently restarted with configurable delay between +retries based on real time or time of the processed stream. @item -encoding can be blocked during temporary failure, or continue transparently -dropping packets in case fifo queue fills up. - +Encoding can be blocked during temporary failure, or continue transparently +dropping packets in case the FIFO queue fills up. @end itemize +API users should be aware that callback functions +(@code{interrupt_callback}, @code{io_open} and @code{io_close}) used +within its @code{AVFormatContext} must be thread-safe. + +@subsection Options @table @option -@item fifo_format +@item attempt_recovery @var{bool} +If failure occurs, attempt to recover the output. This is especially +useful when used with network output, since it makes it possible to +restart streaming transparently. By default this option is set to +@code{false}. + +@item drop_pkts_on_overflow @var{bool} +If set to @code{true}, in case the fifo queue fills up, packets will +be dropped rather than blocking the encoder. This makes it possible to +continue streaming without delaying the input, at the cost of omitting +part of the stream. By default this option is set to @code{false}, so in +such cases the encoder will be blocked until the muxer processes some +of the packets and none of them is lost. + +@item fifo_format @var{format_name} Specify the format name. Useful if it cannot be guessed from the output name suffix. -@item queue_size -Specify size of the queue (number of packets). Default value is 60. +@item format_opts @var{options} +Specify format options for the underlying muxer. Muxer options can be +specified as a list of @var{key}=@var{value} pairs separated by ':'. -@item format_opts -Specify format options for the underlying muxer. Muxer options can be specified -as a list of @var{key}=@var{value} pairs separated by ':'. +@item max_recovery_attempts @var{count} +Set maximum number of successive unsuccessful recovery attempts after +which the output fails permanently. By default this option is set to +@code{0} (unlimited). -@item drop_pkts_on_overflow @var{bool} -If set to 1 (true), in case the fifo queue fills up, packets will be dropped -rather than blocking the encoder. This makes it possible to continue streaming without -delaying the input, at the cost of omitting part of the stream. By default -this option is set to 0 (false), so in such cases the encoder will be blocked -until the muxer processes some of the packets and none of them is lost. +@item queue_size @var{size} +Specify size of the queue as a number of packets. Default value is +@code{60}. -@item attempt_recovery @var{bool} -If failure occurs, attempt to recover the output. This is especially useful -when used with network output, since it makes it possible to restart streaming transparently. -By default this option is set to 0 (false). +@item recover_any_error @var{bool} +If set to @code{true}, recovery will be attempted regardless of type +of the error causing the failure. By default this option is set to +@code{false} and in case of certain (usually permanent) errors the +recovery is not attempted even when the @option{attempt_recovery} +option is set to @code{true}. -@item max_recovery_attempts -Sets maximum number of successive unsuccessful recovery attempts after which -the output fails permanently. By default this option is set to 0 (unlimited). +@item recovery_wait_streamtime @var{bool} +If set to @code{false}, the real time is used when waiting for the +recovery attempt (i.e. the recovery will be attempted after the time +specified by the @option{recovery_wait_time} option). -@item recovery_wait_time @var{duration} -Waiting time before the next recovery attempt after previous unsuccessful -recovery attempt. Default value is 5 seconds. +If set to @code{true}, the time of the processed stream is taken into +account instead (i.e. the recovery will be attempted after discarding +the packets corresponding to the @option{recovery_wait_time} option). -@item recovery_wait_streamtime @var{bool} -If set to 0 (false), the real time is used when waiting for the recovery -attempt (i.e. the recovery will be attempted after at least -recovery_wait_time seconds). -If set to 1 (true), the time of the processed stream is taken into account -instead (i.e. the recovery will be attempted after at least @var{recovery_wait_time} -seconds of the stream is omitted). -By default, this option is set to 0 (false). +By default this option is set to @code{false}. -@item recover_any_error @var{bool} -If set to 1 (true), recovery will be attempted regardless of type of the error -causing the failure. By default this option is set to 0 (false) and in case of -certain (usually permanent) errors the recovery is not attempted even when -@var{attempt_recovery} is set to 1. +@item recovery_wait_time @var{duration} +Specify waiting time in seconds before the next recovery attempt after +previous unsuccessful recovery attempt. Default value is @code{5}. @item restart_with_keyframe @var{bool} Specify whether to wait for the keyframe after recovering from -queue overflow or failure. This option is set to 0 (false) by default. +queue overflow or failure. This option is set to @code{false} by default. @item timeshift @var{duration} -Buffer the specified amount of packets and delay writing the output. Note that -@var{queue_size} must be big enough to store the packets for timeshift. At the -end of the input the fifo buffer is flushed at realtime speed. - +Buffer the specified amount of packets and delay writing the +output. Note that the value of the @option{queue_size} option must be +big enough to store the packets for timeshift. At the end of the input +the fifo buffer is flushed at realtime speed. @end table -@subsection Examples - -@itemize +@subsection Example -@item -Stream something to rtmp server, continue processing the stream at real-time -rate even in case of temporary failure (network outage) and attempt to recover -streaming every second indefinitely. +Use @command{ffmpeg} to stream to an RTMP server, continue processing +the stream at real-time rate even in case of temporary failure +(network outage) and attempt to recover streaming every second +indefinitely: @example -ffmpeg -re -i ... -c:v libx264 -c:a aac -f fifo -fifo_format flv -map 0:v -map 0:a - -drop_pkts_on_overflow 1 -attempt_recovery 1 -recovery_wait_time 1 rtmp://example.com/live/stream_name +ffmpeg -re -i ... -c:v libx264 -c:a aac -f fifo -fifo_format flv \ + -drop_pkts_on_overflow 1 -attempt_recovery 1 -recovery_wait_time 1 \ + -map 0:v -map 0:a rtmp://example.com/live/stream_name @end example -@end itemize +@section film_cpk +Sega film (.cpk) muxer. -@section flv +This format was used as internal format for several Sega games. -Adobe Flash Video Format muxer. +For more information regarding the Sega film file format, visit +@url{http://wiki.multimedia.cx/index.php?title=Sega_FILM}. + +It accepts at maximum one @samp{cinepak} or raw video stream, and at +maximum one audio stream. + +@section filmstrip +Adobe Filmstrip muxer. + +This format is used by several Adobe tools to store a generated filmstrip export. It +accepts a single raw video stream. + +@section fits +Flexible Image Transport System (FITS) muxer. + +This image format is used to store astronomical data. + +For more information regarding the format, visit +@url{https://fits.gsfc.nasa.gov}. -This muxer accepts the following options: +@section flac +Raw FLAC audio muxer. +This muxer accepts exactly one FLAC audio stream. Additionally, it is possible to add +images with disposition @samp{attached_pic}. + +@subsection Options @table @option +@item write_header @var{bool} +write the file header if set to @code{true}, default is @code{true} +@end table + +@subsection Example +Use @command{ffmpeg} to store the audio stream from an input file, +together with several pictures used with @samp{attached_pic} +disposition: +@example +ffmpeg -i INPUT -i pic1.png -i pic2.jpg -map 0:a -map 1 -map 2 -disposition:v attached_pic OUTPUT +@end example + +@section flv +Adobe Flash Video Format muxer. +@subsection Options +@table @option @item flvflags @var{flags} Possible values: @table @samp - @item aac_seq_header_detect Place AAC sequence header based on audio stream data. @@ -659,24 +1745,26 @@ See also the @ref{framehash} and @ref{md5} muxers. @anchor{gif} @section gif - Animated GIF muxer. -It accepts the following options: +Note that the GIF format has a very large time base: the delay between two frames can +therefore not be smaller than one centi second. +@subsection Options @table @option -@item loop +@item loop @var{bool} Set the number of times to loop the output. Use @code{-1} for no loop, @code{0} for looping indefinitely (default). -@item final_delay +@item final_delay @var{delay} Force the delay (expressed in centiseconds) after the last frame. Each frame ends with a delay until the next frame. The default is @code{-1}, which is a special value to tell the muxer to re-use the previous delay. In case of a loop, you might want to customize this value to mark a pause for instance. @end table -For example, to encode a gif looping 10 times, with a 5 seconds delay between +@subsection Example +Encode a gif looping 10 times, with a 5 seconds delay between the loops: @example ffmpeg -i INPUT -loop 10 -final_delay 500 out.gif @@ -688,8 +1776,17 @@ force the @ref{image2} muxer: ffmpeg -i INPUT -c:v gif -f image2 "out%d.gif" @end example -Note 2: the GIF format has a very large time base: the delay between two frames -can therefore not be smaller than one centi second. +@section gxf +General eXchange Format (GXF) muxer. + +GXF was developed by Grass Valley Group, then standardized by SMPTE as SMPTE +360M and was extended in SMPTE RDD 14-2007 to include high-definition video +resolutions. + +It accepts at most one video stream with codec @samp{mjpeg}, or +@samp{mpeg1video}, or @samp{mpeg2video}, or @samp{dvvideo} with resolution +@samp{512x480} or @samp{608x576}, and several audio streams with rate 48000Hz +and codec @samp{pcm16_le}. @anchor{hash} @section hash @@ -736,6 +1833,45 @@ ffmpeg -i INPUT -f hash -hash md5 - See also the @ref{framehash} muxer. +@anchor{hds} +@section hds +HTTP Dynamic Streaming (HDS) muxer. + +HTTP dynamic streaming, or HDS, is an adaptive bitrate streaming method +developed by Adobe. HDS delivers MP4 video content over HTTP connections. HDS +can be used for on-demand streaming or live streaming. + +This muxer creates an .f4m (Adobe Flash Media Manifest File) manifest, an .abst +(Adobe Bootstrap File) for each stream, and segment files in a directory +specified as the output. + +These needs to be accessed by an HDS player throuhg HTTPS for it to be able to +perform playback on the generated stream. + +@subsection Options +@table @option +@item extra_window_size @var{int} +number of fragments kept outside of the manifest before removing from disk + +@item min_frag_duration @var{microseconds} +minimum fragment duration (in microseconds), default value is 1 second +(@code{10000000}) + +@item remove_at_exit @var{bool} +remove all fragments when finished when set to @code{true} + +@item window_size @var{int} +number of fragments kept in the manifest, if set to a value different from +@code{0}. By default all segments are kept in the output directory. +@end table + +@subsection Example +Use @command{ffmpeg} to generate HDS files to the @file{output.hds} directory in +real-time rate: +@example +ffmpeg -re -i INPUT -f hds -b:v 200k output.hds +@end example + @anchor{hls} @section hls @@ -1325,7 +2461,7 @@ Use persistent HTTP connections. Applicable only for HTTP output. @item timeout Set timeout for socket I/O operations. Applicable only for HTTP output. -@item -ignore_io_errors +@item ignore_io_errors Ignore IO errors during open, write and delete. Useful for long-duration runs with network output. @item headers @@ -1368,7 +2504,6 @@ If a PNG image is used, it must use the rgba pixel format @anchor{image2} @section image2 - Image file muxer. The image file muxer writes video frames to image files. @@ -1397,7 +2532,7 @@ form @file{img%-1.jpg}, @file{img%-2.jpg}, ..., @file{img%-10.jpg}, etc. The image muxer supports the .Y.U.V image file format. This format is -special in that that each image frame consists of three files, for +special in that each image frame consists of three files, for each of the YUV420P components. To read or write this image file format, specify the name of the '.Y' file. The muxer will automatically open the '.U' and '.V' files as required. @@ -1468,7 +2603,7 @@ ffmpeg -f v4l2 -r 1 -i /dev/video0 -f image2 -strftime 1 "%Y-%m-%d_%H-%M-%S.jpg" You can set the file name with current frame's PTS: @example -ffmpeg -f v4l2 -r 1 -i /dev/video0 -copyts -f image2 -frame_pts true %d.jpg" +ffmpeg -f v4l2 -r 1 -i /dev/video0 -copyts -f image2 -frame_pts true %d.jpg @end example A more complex example is to publish contents of your desktop directly to a @@ -1623,151 +2758,6 @@ ffmpeg -i INPUT -f md5 - See also the @ref{hash} and @ref{framemd5} muxers. -@section mov, mp4, ismv - -MOV/MP4/ISMV (Smooth Streaming) muxer. - -The mov/mp4/ismv muxer supports fragmentation. Normally, a MOV/MP4 -file has all the metadata about all packets stored in one location -(written at the end of the file, it can be moved to the start for -better playback by adding @var{faststart} to the @var{movflags}, or -using the @command{qt-faststart} tool). A fragmented -file consists of a number of fragments, where packets and metadata -about these packets are stored together. Writing a fragmented -file has the advantage that the file is decodable even if the -writing is interrupted (while a normal MOV/MP4 is undecodable if -it is not properly finished), and it requires less memory when writing -very long files (since writing normal MOV/MP4 files stores info about -every single packet in memory until the file is closed). The downside -is that it is less compatible with other applications. - -@subsection Options - -Fragmentation is enabled by setting one of the AVOptions that define -how to cut the file into fragments: - -@table @option -@item -moov_size @var{bytes} -Reserves space for the moov atom at the beginning of the file instead of placing the -moov atom at the end. If the space reserved is insufficient, muxing will fail. -@item -movflags frag_keyframe -Start a new fragment at each video keyframe. -@item -frag_duration @var{duration} -Create fragments that are @var{duration} microseconds long. -@item -frag_size @var{size} -Create fragments that contain up to @var{size} bytes of payload data. -@item -movflags frag_custom -Allow the caller to manually choose when to cut fragments, by -calling @code{av_write_frame(ctx, NULL)} to write a fragment with -the packets written so far. (This is only useful with other -applications integrating libavformat, not from @command{ffmpeg}.) -@item -min_frag_duration @var{duration} -Don't create fragments that are shorter than @var{duration} microseconds long. -@end table - -If more than one condition is specified, fragments are cut when -one of the specified conditions is fulfilled. The exception to this is -@code{-min_frag_duration}, which has to be fulfilled for any of the other -conditions to apply. - -Additionally, the way the output file is written can be adjusted -through a few other options: - -@table @option -@item -movflags empty_moov -Write an initial moov atom directly at the start of the file, without -describing any samples in it. Generally, an mdat/moov pair is written -at the start of the file, as a normal MOV/MP4 file, containing only -a short portion of the file. With this option set, there is no initial -mdat atom, and the moov atom only describes the tracks but has -a zero duration. - -This option is implicitly set when writing ismv (Smooth Streaming) files. -@item -movflags separate_moof -Write a separate moof (movie fragment) atom for each track. Normally, -packets for all tracks are written in a moof atom (which is slightly -more efficient), but with this option set, the muxer writes one moof/mdat -pair for each track, making it easier to separate tracks. - -This option is implicitly set when writing ismv (Smooth Streaming) files. -@item -movflags skip_sidx -Skip writing of sidx atom. When bitrate overhead due to sidx atom is high, -this option could be used for cases where sidx atom is not mandatory. -When global_sidx flag is enabled, this option will be ignored. -@item -movflags faststart -Run a second pass moving the index (moov atom) to the beginning of the file. -This operation can take a while, and will not work in various situations such -as fragmented output, thus it is not enabled by default. -@item -movflags rtphint -Add RTP hinting tracks to the output file. -@item -movflags disable_chpl -Disable Nero chapter markers (chpl atom). Normally, both Nero chapters -and a QuickTime chapter track are written to the file. With this option -set, only the QuickTime chapter track will be written. Nero chapters can -cause failures when the file is reprocessed with certain tagging programs, like -mp3Tag 2.61a and iTunes 11.3, most likely other versions are affected as well. -@item -movflags omit_tfhd_offset -Do not write any absolute base_data_offset in tfhd atoms. This avoids -tying fragments to absolute byte positions in the file/streams. -@item -movflags default_base_moof -Similarly to the omit_tfhd_offset, this flag avoids writing the -absolute base_data_offset field in tfhd atoms, but does so by using -the new default-base-is-moof flag instead. This flag is new from -14496-12:2012. This may make the fragments easier to parse in certain -circumstances (avoiding basing track fragment location calculations -on the implicit end of the previous track fragment). -@item -write_tmcd -Specify @code{on} to force writing a timecode track, @code{off} to disable it -and @code{auto} to write a timecode track only for mov and mp4 output (default). -@item -movflags negative_cts_offsets -Enables utilization of version 1 of the CTTS box, in which the CTS offsets can -be negative. This enables the initial sample to have DTS/CTS of zero, and -reduces the need for edit lists for some cases such as video tracks with -B-frames. Additionally, eases conformance with the DASH-IF interoperability -guidelines. - -This option is implicitly set when writing ismv (Smooth Streaming) files. - -@item -write_btrt @var{bool} -Force or disable writing bitrate box inside stsd box of a track. -The box contains decoding buffer size (in bytes), maximum bitrate and -average bitrate for the track. The box will be skipped if none of these values -can be computed. -Default is @code{-1} or @code{auto}, which will write the box only in MP4 mode. - -@item -write_prft -Write producer time reference box (PRFT) with a specified time source for the -NTP field in the PRFT box. Set value as @samp{wallclock} to specify timesource -as wallclock time and @samp{pts} to specify timesource as input packets' PTS -values. - -Setting value to @samp{pts} is applicable only for a live encoding use case, -where PTS values are set as as wallclock time at the source. For example, an -encoding use case with decklink capture source where @option{video_pts} and -@option{audio_pts} are set to @samp{abs_wallclock}. - -@item -empty_hdlr_name @var{bool} -Enable to skip writing the name inside a @code{hdlr} box. -Default is @code{false}. - -@item -movie_timescale @var{scale} -Set the timescale written in the movie header box (@code{mvhd}). -Range is 1 to INT_MAX. Default is 1000. - -@item -video_track_timescale @var{scale} -Set the timescale used for video tracks. Range is 0 to INT_MAX. -If set to @code{0}, the timescale is automatically set based on -the native stream time base. Default is 0. -@end table - -@subsection Example - -Smooth Streaming content can be pushed in real time to a publishing -point on IIS with this muxer. Example: -@example -ffmpeg -re @var{} -movflags isml+frag_keyframe -f ismv http://server/publishingpoint.isml/Streams(Encoder1) -@end example - @section mp3 The MP3 muxer writes a raw MP3 stream with the following optional features: @@ -2048,181 +3038,27 @@ ogg files can be safely chained. @end table -@anchor{raw muxers} -@section raw muxers - -Raw muxers accept a single stream matching the designated codec. They do not store timestamps or metadata. -The recognized extension is the same as the muxer name unless indicated otherwise. - -@subsection ac3 - -Dolby Digital, also known as AC-3, audio. - -@subsection adx - -CRI Middleware ADX audio. - -This muxer will write out the total sample count near the start of the first packet -when the output is seekable and the count can be stored in 32 bits. - -@subsection aptx - -aptX (Audio Processing Technology for Bluetooth) audio. - -@subsection aptx_hd - -aptX HD (Audio Processing Technology for Bluetooth) audio. - -Extensions: aptxhd - -@subsection avs2 - -AVS2-P2/IEEE1857.4 video. - -Extensions: avs, avs2 - -@subsection cavsvideo - -Chinese AVS (Audio Video Standard) video. - -Extensions: cavs - -@subsection codec2raw - -Codec 2 audio. - -No extension is registered so format name has to be supplied e.g. with the ffmpeg CLI tool @code{-f codec2raw}. - -@subsection data - -Data muxer accepts a single stream with any codec of any type. -The input stream has to be selected using the @code{-map} option with the ffmpeg CLI tool. - -No extension is registered so format name has to be supplied e.g. with the ffmpeg CLI tool @code{-f data}. - -@subsection dirac - -BBC Dirac video. The Dirac Pro codec is a subset and is standardized as SMPTE VC-2. - -Extensions: drc, vc2 - -@subsection dnxhd - -Avid DNxHD video. It is standardized as SMPTE VC-3. Accepts DNxHR streams. - -Extensions: dnxhd, dnxhr - -@subsection dts - -DTS Coherent Acoustics (DCA) audio. - -@subsection eac3 - -Dolby Digital Plus, also known as Enhanced AC-3, audio. - -@subsection g722 - -ITU-T G.722 audio. - -@subsection g723_1 - -ITU-T G.723.1 audio. - -Extensions: tco, rco - -@subsection g726 - -ITU-T G.726 big-endian ("left-justified") audio. - -No extension is registered so format name has to be supplied e.g. with the ffmpeg CLI tool @code{-f g726}. - -@subsection g726le - -ITU-T G.726 little-endian ("right-justified") audio. - -No extension is registered so format name has to be supplied e.g. with the ffmpeg CLI tool @code{-f g726le}. - -@subsection gsm - -Global System for Mobile Communications audio. - -@subsection h261 - -ITU-T H.261 video. - -@subsection h263 - -ITU-T H.263 / H.263-1996, H.263+ / H.263-1998 / H.263 version 2 video. - -@subsection h264 - -ITU-T H.264 / MPEG-4 Part 10 AVC video. Bitstream shall be converted to Annex B syntax if it's in length-prefixed mode. - -Extensions: h264, 264 - -@subsection hevc - -ITU-T H.265 / MPEG-H Part 2 HEVC video. Bitstream shall be converted to Annex B syntax if it's in length-prefixed mode. - -Extensions: hevc, h265, 265 - -@subsection m4v - -MPEG-4 Part 2 video. - -@subsection mjpeg - -Motion JPEG video. - -Extensions: mjpg, mjpeg - -@subsection mlp - -Meridian Lossless Packing, also known as Packed PCM, audio. - -@subsection mp2 - -MPEG-1 Audio Layer II audio. - -Extensions: mp2, m2a, mpa - -@subsection mpeg1video - -MPEG-1 Part 2 video. - -Extensions: mpg, mpeg, m1v - -@subsection mpeg2video - -ITU-T H.262 / MPEG-2 Part 2 video. - -Extensions: m2v - -@subsection obu - -AV1 low overhead Open Bitstream Units muxer. Temporal delimiter OBUs will be inserted in all temporal units of the stream. - -@subsection rawvideo - -Raw uncompressed video. - -Extensions: yuv, rgb - -@subsection sbc - -Bluetooth SIG low-complexity subband codec audio. - -Extensions: sbc, msbc - -@subsection truehd +@anchor{rcwt} +@section rcwt -Dolby TrueHD audio. +Raw Captions With Time (RCWT) is a format native to ccextractor, a commonly +used open source tool for processing 608/708 closed caption (CC) sources. +It can be used to archive the original, raw CC bitstream and to produce +a source file for later CC processing or conversion. As a result, +it also allows for interopability with ccextractor for processing CC data +extracted via ffmpeg. The format is simple to parse and can be used +to retain all lines and variants of CC. -Extensions: thd +This muxer implements the specification as of 2024-01-05, which has +been stable and unchanged for 10 years as of this writing. -@subsection vc1 +This muxer will have some nuances from the way that ccextractor muxes RCWT. +No compatibility issues when processing the output with ccextractor +have been observed as a result of this so far, but mileage may vary +and outputs will not be a bit-exact match. -SMPTE 421M / VC-1 video. +A free specification of RCWT can be found here: +@url{https://github.com/CCExtractor/ccextractor/blob/master/docs/BINARY_FILE_FORMAT.TXT} @anchor{segment} @section segment, stream_segment, ssegment diff --git a/doc/outdevs.texi b/doc/outdevs.texi index aa41e295230..9ee857528e7 100644 --- a/doc/outdevs.texi +++ b/doc/outdevs.texi @@ -235,6 +235,11 @@ Enable SMPTE Level A mode on the used output. Must be @samp{unset}, @samp{true} or @samp{false}. Defaults to @option{unset}. +@item vanc_queue_size +Sets maximum output buffer size in bytes for VANC data. If the buffering reaches this value, +outgoing VANC data will be dropped. +Defaults to @samp{1048576}. + @end table @subsection Examples @@ -297,7 +302,7 @@ ffmpeg -re -i INPUT -c:v rawvideo -pix_fmt bgra -f fbdev /dev/fb0 See also @url{http://linux-fbdev.sourceforge.net/}, and fbset(1). @section opengl -OpenGL output device. +OpenGL output device. Deprecated and will be removed. To enable this output device you need to configure FFmpeg with @code{--enable-opengl}. @@ -403,7 +408,13 @@ ffmpeg -i INPUT -f pulse "stream name" @section sdl -SDL (Simple DirectMedia Layer) output device. +SDL (Simple DirectMedia Layer) output device. Deprecated and will be removed. + +For monitoring purposes in FFmpeg, pipes and a video player such as ffplay can be used: + +@example +ffmpeg -i INPUT -f nut -c:v rawvideo - | ffplay - +@end example "sdl2" can be used as alias for "sdl". @@ -421,13 +432,18 @@ For more information about SDL, check: @table @option -@item window_title -Set the SDL window title, if not specified default to the filename -specified for the output device. +@item window_borderless +Set SDL window border off. +Default value is 0 (enable window border). -@item icon_title -Set the name of the iconified SDL window, if not specified it is set -to the same value of @var{window_title}. +@item window_enable_quit +Enable quit action (using window button or keyboard key) +when non-zero value is provided. +Default value is 1 (enable quit action). + +@item window_fullscreen +Set fullscreen mode when non-zero value is provided. +Default value is zero. @item window_size Set the SDL window size, can be a string of the form @@ -435,18 +451,13 @@ Set the SDL window size, can be a string of the form If not specified it defaults to the size of the input video, downscaled according to the aspect ratio. +@item window_title +Set the SDL window title, if not specified default to the filename +specified for the output device. + @item window_x @item window_y Set the position of the window on the screen. - -@item window_fullscreen -Set fullscreen mode when non-zero value is provided. -Default value is zero. - -@item window_enable_quit -Enable quit action (using window button or keyboard key) -when non-zero value is provided. -Default value is 1 (enable quit action) @end table @subsection Interactive commands diff --git a/doc/platform.texi b/doc/platform.texi index 4090b856700..764911d2302 100644 --- a/doc/platform.texi +++ b/doc/platform.texi @@ -92,9 +92,6 @@ For information about compiling FFmpeg on OS/2 see @chapter Windows -To get help and instructions for building FFmpeg under Windows, check out -the FFmpeg Windows Help Forum at @url{http://ffmpeg.zeranoe.com/forum/}. - @section Native Windows compilation using MinGW or MinGW-w64 FFmpeg can be built to run natively on Windows using the MinGW-w64 diff --git a/doc/protocols.texi b/doc/protocols.texi index 21ae6181a05..f54600b846b 100644 --- a/doc/protocols.texi +++ b/doc/protocols.texi @@ -896,6 +896,13 @@ be named, by prefixing the type with 'N' and specifying the name before the value (i.e. @code{NB:myFlag:1}). This option may be used multiple times to construct arbitrary AMF sequences. +@item rtmp_enhanced_codecs +Specify the list of codecs the client advertises to support in an +enhanced RTMP stream. This option should be set to a comma separated +list of fourcc values, like @code{hvc1,av01,vp09} for multiple codecs +or @code{hvc1} for only one codec. The specified list will be presented +in the "fourCcLive" property of the Connect Command Message. + @item rtmp_flashver Version of the Flash plugin used to run the SWF player. The default is LNX 9,0,124,2. (When publishing, the default is FMLE/3.0 (compatible; @@ -1882,6 +1889,12 @@ The list of supported options follows. Listen for an incoming connection. 0 disables listen, 1 enables listen in single client mode, 2 enables listen in multi-client mode. Default value is 0. +@item local_addr=@var{addr} +Local IP address of a network interface used for tcp socket connect. + +@item local_port=@var{port} +Local port used for tcp socket connect. + @item timeout=@var{microseconds} Set raise error timeout, expressed in microseconds. diff --git a/doc/utils.texi b/doc/utils.texi index 8e8bfa76d4a..76e704fc3ca 100644 --- a/doc/utils.texi +++ b/doc/utils.texi @@ -695,6 +695,8 @@ FL+FR+FC+LFE+SL+SR FL+FR+FC+BC+SL+SR @item 6.0(front) FL+FR+FLC+FRC+SL+SR +@item 3.1.2 +FL+FR+FC+LFE+TFL+TFR @item hexagonal FL+FR+FC+BL+BR+BC @item 6.1 @@ -713,12 +715,22 @@ FL+FR+FC+LFE+BL+BR+SL+SR FL+FR+FC+LFE+BL+BR+FLC+FRC @item 7.1(wide-side) FL+FR+FC+LFE+FLC+FRC+SL+SR -@item 7.1(top) +@item 5.1.2 FL+FR+FC+LFE+BL+BR+TFL+TFR @item octagonal FL+FR+FC+BL+BR+BC+SL+SR @item cube FL+FR+BL+BR+TFL+TFR+TBL+TBR +@item 5.1.4 +FL+FR+FC+LFE+BL+BR+TFL+TFR+TBL+TBR +@item 7.1.2 +FL+FR+FC+LFE+BL+BR+SL+SR+TFL+TFR +@item 7.1.4 +FL+FR+FC+LFE+BL+BR+SL+SR+TFL+TFR+TBL+TBR +@item 7.2.3 +FL+FR+FC+LFE+BL+BR+SL+SR+TFL+TFR+TBC+LFE2 +@item 9.1.4 +FL+FR+FC+LFE+BL+BR+FLC+FRC+SL+SR+TFL+TFR+TBL+TBR @item hexadecagonal FL+FR+FC+BL+BR+BC+SL+SR+WL+WR+TBL+TBR+TBC+TFC+TFL+TFR @item downmix @@ -803,7 +815,7 @@ Compute arcsine of @var{x}. @item atan(x) Compute arctangent of @var{x}. -@item atan2(x, y) +@item atan2(y, x) Compute principal value of the arc tangent of @var{y}/@var{x}. @item between(x, min, max) @@ -927,9 +939,15 @@ Returns the value of the expression printed. Prints t with loglevel l -@item random(x) -Return a pseudo random value between 0.0 and 1.0. @var{x} is the index of the -internal variable which will be used to save the seed/state. +@item random(idx) +Return a pseudo random value between 0.0 and 1.0. @var{idx} is the +index of the internal variable which will be used to save the +seed/state. + +@item randomi(idx, min, max) +Return a pseudo random value in the interval between @var{min} and +@var{max}. @var{idx} is the index of the internal variable which will +be used to save the seed/state. @item root(expr, max) Find an input value for which the function represented by @var{expr} diff --git a/ffbuild/arch.mak b/ffbuild/arch.mak index 39d76ee1521..23a3feb090a 100644 --- a/ffbuild/arch.mak +++ b/ffbuild/arch.mak @@ -15,6 +15,7 @@ OBJS-$(HAVE_LASX) += $(LASX-OBJS) $(LASX-OBJS-yes) OBJS-$(HAVE_ALTIVEC) += $(ALTIVEC-OBJS) $(ALTIVEC-OBJS-yes) OBJS-$(HAVE_VSX) += $(VSX-OBJS) $(VSX-OBJS-yes) +OBJS-$(HAVE_RV) += $(RV-OBJS) $(RV-OBJS-yes) OBJS-$(HAVE_RVV) += $(RVV-OBJS) $(RVV-OBJS-yes) OBJS-$(HAVE_MMX) += $(MMX-OBJS) $(MMX-OBJS-yes) diff --git a/ffbuild/common.mak b/ffbuild/common.mak index f52473453eb..87a3ffd2b0e 100644 --- a/ffbuild/common.mak +++ b/ffbuild/common.mak @@ -130,7 +130,7 @@ $(BIN2CEXE): ffbuild/bin2c_host.o ifdef CONFIG_PTX_COMPRESSION %.ptx.gz: TAG = GZIP %.ptx.gz: %.ptx - $(M)gzip -c9 $(patsubst $(SRC_PATH)/%,$(SRC_LINK)/%,$<) >$@ + $(M)gzip -nc9 $(patsubst $(SRC_PATH)/%,$(SRC_LINK)/%,$<) >$@ %.ptx.c: %.ptx.gz $(BIN2CEXE) $(BIN2C) $(patsubst $(SRC_PATH)/%,$(SRC_LINK)/%,$<) $@ $(subst .,_,$(basename $(notdir $@))) @@ -140,7 +140,7 @@ else endif clean:: - $(RM) $(BIN2CEXE) + $(RM) $(BIN2CEXE) $(CLEANSUFFIXES:%=ffbuild/%) %.c %.h %.pc %.ver %.version: TAG = GEN diff --git a/fftools/Makefile b/fftools/Makefile index 8ac38e75d2f..083a1368ce6 100644 --- a/fftools/Makefile +++ b/fftools/Makefile @@ -10,16 +10,21 @@ ALLAVPROGS = $(AVBASENAMES:%=%$(PROGSSUF)$(EXESUF)) ALLAVPROGS_G = $(AVBASENAMES:%=%$(PROGSSUF)_g$(EXESUF)) OBJS-ffmpeg += \ + fftools/ffmpeg_dec.o \ fftools/ffmpeg_demux.o \ + fftools/ffmpeg_enc.o \ fftools/ffmpeg_filter.o \ fftools/ffmpeg_hw.o \ fftools/ffmpeg_mux.o \ fftools/ffmpeg_mux_init.o \ fftools/ffmpeg_opt.o \ + fftools/ffmpeg_sched.o \ fftools/objpool.o \ fftools/sync_queue.o \ fftools/thread_queue.o \ +OBJS-ffplay += fftools/ffplay_renderer.o + define DOFFTOOL OBJS-$(1) += fftools/cmdutils.o fftools/opt_common.o fftools/$(1).o $(OBJS-$(1)-yes) ifdef HAVE_GNU_WINDRES diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index a1de621d1c2..309ec4d32fd 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -37,6 +37,7 @@ #include "libswresample/swresample.h" #include "libavutil/avassert.h" #include "libavutil/avstring.h" +#include "libavutil/bprint.h" #include "libavutil/channel_layout.h" #include "libavutil/display.h" #include "libavutil/getenv_utf8.h" @@ -83,29 +84,8 @@ void init_dynload(void) #endif } -static void (*program_exit)(int ret); - -void register_exit(void (*cb)(int ret)) -{ - program_exit = cb; -} - -void report_and_exit(int ret) -{ - av_log(NULL, AV_LOG_FATAL, "%s\n", av_err2str(ret)); - exit_program(AVUNERROR(ret)); -} - -void exit_program(int ret) -{ - if (program_exit) - program_exit(ret); - - exit(ret); -} - -double parse_number_or_die(const char *context, const char *numstr, int type, - double min, double max) +int parse_number(const char *context, const char *numstr, enum OptionType type, + double min, double max, double *dst) { char *tail; const char *error; @@ -114,31 +94,21 @@ double parse_number_or_die(const char *context, const char *numstr, int type, error = "Expected number for %s but found: %s\n"; else if (d < min || d > max) error = "The value for %s was %s which is not within %f - %f\n"; - else if (type == OPT_INT64 && (int64_t)d != d) + else if (type == OPT_TYPE_INT64 && (int64_t)d != d) error = "Expected int64 for %s but found %s\n"; - else if (type == OPT_INT && (int)d != d) + else if (type == OPT_TYPE_INT && (int)d != d) error = "Expected int for %s but found %s\n"; - else - return d; - av_log(NULL, AV_LOG_FATAL, error, context, numstr, min, max); - exit_program(1); - return 0; -} - -int64_t parse_time_or_die(const char *context, const char *timestr, - int is_duration) -{ - int64_t us; - if (av_parse_time(&us, timestr, is_duration) < 0) { - av_log(NULL, AV_LOG_FATAL, "Invalid %s specification for %s: %s\n", - is_duration ? "duration" : "date", context, timestr); - exit_program(1); + else { + *dst = d; + return 0; } - return us; + + av_log(NULL, AV_LOG_FATAL, error, context, numstr, min, max); + return AVERROR(EINVAL); } void show_help_options(const OptionDef *options, const char *msg, int req_flags, - int rej_flags, int alt_flags) + int rej_flags) { const OptionDef *po; int first; @@ -148,7 +118,6 @@ void show_help_options(const OptionDef *options, const char *msg, int req_flags, char buf[128]; if (((po->flags & req_flags) != req_flags) || - (alt_flags && !(po->flags & alt_flags)) || (po->flags & rej_flags)) continue; @@ -157,10 +126,15 @@ void show_help_options(const OptionDef *options, const char *msg, int req_flags, first = 0; } av_strlcpy(buf, po->name, sizeof(buf)); - if (po->argname) { - av_strlcat(buf, " ", sizeof(buf)); - av_strlcat(buf, po->argname, sizeof(buf)); - } + + if (po->flags & OPT_FLAG_PERSTREAM) + av_strlcat(buf, "[:]", sizeof(buf)); + else if (po->flags & OPT_FLAG_SPEC) + av_strlcat(buf, "[:]", sizeof(buf)); + + if (po->argname) + av_strlcatf(buf, sizeof(buf), " <%s>", po->argname); + printf("-%-17s %s\n", buf, po->help); } printf("\n"); @@ -181,6 +155,9 @@ void show_help_children(const AVClass *class, int flags) static const OptionDef *find_option(const OptionDef *po, const char *name) { + if (*name == '/') + name++; + while (po->name) { const char *end; if (av_strstart(name, po->name, &end) && (!*end || *end == ':')) @@ -254,59 +231,138 @@ static inline void prepare_app_arguments(int *argc_ptr, char ***argv_ptr) } #endif /* HAVE_COMMANDLINETOARGVW */ +static int opt_has_arg(const OptionDef *o) +{ + if (o->type == OPT_TYPE_BOOL) + return 0; + if (o->type == OPT_TYPE_FUNC) + return !!(o->flags & OPT_FUNC_ARG); + return 1; +} + static int write_option(void *optctx, const OptionDef *po, const char *opt, - const char *arg) + const char *arg, const OptionDef *defs) { /* new-style options contain an offset into optctx, old-style address of * a global var*/ - void *dst = po->flags & (OPT_OFFSET | OPT_SPEC) ? + void *dst = po->flags & OPT_FLAG_OFFSET ? (uint8_t *)optctx + po->u.off : po->u.dst_ptr; - int *dstcount; + char *arg_allocated = NULL; + + SpecifierOptList *sol = NULL; + double num; + int ret = 0; + + if (*opt == '/') { + opt++; + + if (po->type == OPT_TYPE_BOOL) { + av_log(NULL, AV_LOG_FATAL, + "Requested to load an argument from file for a bool option '%s'\n", + po->name); + return AVERROR(EINVAL); + } - if (po->flags & OPT_SPEC) { - SpecifierOpt **so = dst; + arg_allocated = file_read(arg); + if (!arg_allocated) { + av_log(NULL, AV_LOG_FATAL, + "Error reading the value for option '%s' from file: %s\n", + opt, arg); + return AVERROR(EINVAL); + } + + arg = arg_allocated; + } + + if (po->flags & OPT_FLAG_SPEC) { char *p = strchr(opt, ':'); char *str; - dstcount = (int *)(so + 1); - *so = grow_array(*so, sizeof(**so), dstcount, *dstcount + 1); + sol = dst; + ret = GROW_ARRAY(sol->opt, sol->nb_opt); + if (ret < 0) + goto finish; + str = av_strdup(p ? p + 1 : ""); - if (!str) - return AVERROR(ENOMEM); - (*so)[*dstcount - 1].specifier = str; - dst = &(*so)[*dstcount - 1].u; + if (!str) { + ret = AVERROR(ENOMEM); + goto finish; + } + sol->opt[sol->nb_opt - 1].specifier = str; + dst = &sol->opt[sol->nb_opt - 1].u; } - if (po->flags & OPT_STRING) { + if (po->type == OPT_TYPE_STRING) { char *str; - str = av_strdup(arg); + if (arg_allocated) { + str = arg_allocated; + arg_allocated = NULL; + } else + str = av_strdup(arg); av_freep(dst); - if (!str) - return AVERROR(ENOMEM); + + if (!str) { + ret = AVERROR(ENOMEM); + goto finish; + } + *(char **)dst = str; - } else if (po->flags & OPT_BOOL || po->flags & OPT_INT) { - *(int *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT_MIN, INT_MAX); - } else if (po->flags & OPT_INT64) { - *(int64_t *)dst = parse_number_or_die(opt, arg, OPT_INT64, INT64_MIN, INT64_MAX); - } else if (po->flags & OPT_TIME) { - *(int64_t *)dst = parse_time_or_die(opt, arg, 1); - } else if (po->flags & OPT_FLOAT) { - *(float *)dst = parse_number_or_die(opt, arg, OPT_FLOAT, -INFINITY, INFINITY); - } else if (po->flags & OPT_DOUBLE) { - *(double *)dst = parse_number_or_die(opt, arg, OPT_DOUBLE, -INFINITY, INFINITY); - } else if (po->u.func_arg) { - int ret = po->u.func_arg(optctx, opt, arg); + } else if (po->type == OPT_TYPE_BOOL || po->type == OPT_TYPE_INT) { + ret = parse_number(opt, arg, OPT_TYPE_INT64, INT_MIN, INT_MAX, &num); + if (ret < 0) + goto finish; + + *(int *)dst = num; + } else if (po->type == OPT_TYPE_INT64) { + ret = parse_number(opt, arg, OPT_TYPE_INT64, INT64_MIN, INT64_MAX, &num); + if (ret < 0) + goto finish; + + *(int64_t *)dst = num; + } else if (po->type == OPT_TYPE_TIME) { + ret = av_parse_time(dst, arg, 1); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Invalid duration for option %s: %s\n", + opt, arg); + goto finish; + } + } else if (po->type == OPT_TYPE_FLOAT) { + ret = parse_number(opt, arg, OPT_TYPE_FLOAT, -INFINITY, INFINITY, &num); + if (ret < 0) + goto finish; + + *(float *)dst = num; + } else if (po->type == OPT_TYPE_DOUBLE) { + ret = parse_number(opt, arg, OPT_TYPE_DOUBLE, -INFINITY, INFINITY, &num); + if (ret < 0) + goto finish; + + *(double *)dst = num; + } else { + av_assert0(po->type == OPT_TYPE_FUNC && po->u.func_arg); + + ret = po->u.func_arg(optctx, opt, arg); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Failed to set value '%s' for option '%s': %s\n", arg, opt, av_err2str(ret)); - return ret; + goto finish; } } - if (po->flags & OPT_EXIT) - exit_program(0); + if (po->flags & OPT_EXIT) { + ret = AVERROR_EXIT; + goto finish; + } - return 0; + if (sol) { + sol->type = po->type; + sol->opt_canon = (po->flags & OPT_HAS_CANON) ? + find_option(defs, po->u1.name_canon) : po; + } + +finish: + av_freep(&arg_allocated); + return ret; } int parse_option(void *optctx, const char *opt, const char *arg, @@ -314,7 +370,8 @@ int parse_option(void *optctx, const char *opt, const char *arg, { static const OptionDef opt_avoptions = { .name = "AVOption passthrough", - .flags = HAS_ARG, + .type = OPT_TYPE_FUNC, + .flags = OPT_FUNC_ARG, .u.func_arg = opt_default, }; @@ -325,9 +382,9 @@ int parse_option(void *optctx, const char *opt, const char *arg, if (!po->name && opt[0] == 'n' && opt[1] == 'o') { /* handle 'no' bool option */ po = find_option(options, opt + 2); - if ((po->name && (po->flags & OPT_BOOL))) + if ((po->name && po->type == OPT_TYPE_BOOL)) arg = "0"; - } else if (po->flags & OPT_BOOL) + } else if (po->type == OPT_TYPE_BOOL) arg = "1"; if (!po->name) @@ -336,20 +393,20 @@ int parse_option(void *optctx, const char *opt, const char *arg, av_log(NULL, AV_LOG_ERROR, "Unrecognized option '%s'\n", opt); return AVERROR(EINVAL); } - if (po->flags & HAS_ARG && !arg) { + if (opt_has_arg(po) && !arg) { av_log(NULL, AV_LOG_ERROR, "Missing argument for option '%s'\n", opt); return AVERROR(EINVAL); } - ret = write_option(optctx, po, opt, arg); + ret = write_option(optctx, po, opt, arg, options); if (ret < 0) return ret; - return !!(po->flags & HAS_ARG); + return opt_has_arg(po); } -void parse_options(void *optctx, int argc, char **argv, const OptionDef *options, - void (*parse_arg_function)(void *, const char*)) +int parse_options(void *optctx, int argc, char **argv, const OptionDef *options, + int (*parse_arg_function)(void *, const char*)) { const char *opt; int optindex, handleoptions = 1, ret; @@ -370,16 +427,21 @@ void parse_options(void *optctx, int argc, char **argv, const OptionDef *options opt++; if ((ret = parse_option(optctx, opt, argv[optindex], options)) < 0) - exit_program(1); + return ret; optindex += ret; } else { - if (parse_arg_function) - parse_arg_function(optctx, opt); + if (parse_arg_function) { + ret = parse_arg_function(optctx, opt); + if (ret < 0) + return ret; + } } } + + return 0; } -int parse_optgroup(void *optctx, OptionGroup *g) +int parse_optgroup(void *optctx, OptionGroup *g, const OptionDef *defs) { int i, ret; @@ -402,7 +464,7 @@ int parse_optgroup(void *optctx, OptionGroup *g) av_log(NULL, AV_LOG_DEBUG, "Applying option %s (%s) with argument %s.\n", o->key, o->opt->help, o->val); - ret = write_option(optctx, o->opt, o->key, o->val); + ret = write_option(optctx, o->opt, o->key, o->val, defs); if (ret < 0) return ret; } @@ -432,7 +494,7 @@ int locate_option(int argc, char **argv, const OptionDef *options, (po->name && !strcmp(optname, po->name))) return i; - if (!po->name || po->flags & HAS_ARG) + if (!po->name || opt_has_arg(po)) i++; } return 0; @@ -466,7 +528,14 @@ static void check_options(const OptionDef *po) { while (po->name) { if (po->flags & OPT_PERFILE) - av_assert0(po->flags & (OPT_INPUT | OPT_OUTPUT)); + av_assert0(po->flags & (OPT_INPUT | OPT_OUTPUT | OPT_DECODER)); + + if (po->type == OPT_TYPE_FUNC) + av_assert0(!(po->flags & (OPT_FLAG_OFFSET | OPT_FLAG_SPEC))); + + // OPT_FUNC_ARG can only be ser for OPT_TYPE_FUNC + av_assert0((po->type == OPT_TYPE_FUNC) || !(po->flags & OPT_FUNC_ARG)); + po++; } } @@ -512,7 +581,7 @@ static const AVOption *opt_find(void *obj, const char *name, const char *unit, return o; } -#define FLAGS (o->type == AV_OPT_TYPE_FLAGS && (arg[0]=='-' || arg[0]=='+')) ? AV_DICT_APPEND : 0 +#define FLAGS ((o->type == AV_OPT_TYPE_FLAGS && (arg[0]=='-' || arg[0]=='+')) ? AV_DICT_APPEND : 0) int opt_default(void *optctx, const char *opt, const char *arg) { const AVOption *o; @@ -605,13 +674,17 @@ static int match_group_separator(const OptionGroupDef *groups, int nb_groups, * @param group_idx which group definition should this group belong to * @param arg argument of the group delimiting option */ -static void finish_group(OptionParseContext *octx, int group_idx, - const char *arg) +static int finish_group(OptionParseContext *octx, int group_idx, + const char *arg) { OptionGroupList *l = &octx->groups[group_idx]; OptionGroup *g; + int ret; + + ret = GROW_ARRAY(l->groups, l->nb_groups); + if (ret < 0) + return ret; - GROW_ARRAY(l->groups, l->nb_groups); g = &l->groups[l->nb_groups - 1]; *g = octx->cur_group; @@ -628,41 +701,51 @@ static void finish_group(OptionParseContext *octx, int group_idx, swr_opts = NULL; memset(&octx->cur_group, 0, sizeof(octx->cur_group)); + + return ret; } /* * Add an option instance to currently parsed group. */ -static void add_opt(OptionParseContext *octx, const OptionDef *opt, - const char *key, const char *val) +static int add_opt(OptionParseContext *octx, const OptionDef *opt, + const char *key, const char *val) { - int global = !(opt->flags & (OPT_PERFILE | OPT_SPEC | OPT_OFFSET)); + int global = !(opt->flags & OPT_PERFILE); OptionGroup *g = global ? &octx->global_opts : &octx->cur_group; + int ret; + + ret = GROW_ARRAY(g->opts, g->nb_opts); + if (ret < 0) + return ret; - GROW_ARRAY(g->opts, g->nb_opts); g->opts[g->nb_opts - 1].opt = opt; g->opts[g->nb_opts - 1].key = key; g->opts[g->nb_opts - 1].val = val; + + return 0; } -static void init_parse_context(OptionParseContext *octx, - const OptionGroupDef *groups, int nb_groups) +static int init_parse_context(OptionParseContext *octx, + const OptionGroupDef *groups, int nb_groups) { static const OptionGroupDef global_group = { "global" }; int i; memset(octx, 0, sizeof(*octx)); - octx->nb_groups = nb_groups; - octx->groups = av_calloc(octx->nb_groups, sizeof(*octx->groups)); + octx->groups = av_calloc(nb_groups, sizeof(*octx->groups)); if (!octx->groups) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); + octx->nb_groups = nb_groups; for (i = 0; i < octx->nb_groups; i++) octx->groups[i].group_def = &groups[i]; octx->global_opts.group_def = &global_group; octx->global_opts.arg = ""; + + return 0; } void uninit_parse_context(OptionParseContext *octx) @@ -694,19 +777,23 @@ int split_commandline(OptionParseContext *octx, int argc, char *argv[], const OptionDef *options, const OptionGroupDef *groups, int nb_groups) { + int ret; int optindex = 1; int dashdash = -2; /* perform system-dependent conversions for arguments list */ prepare_app_arguments(&argc, &argv); - init_parse_context(octx, groups, nb_groups); + ret = init_parse_context(octx, groups, nb_groups); + if (ret < 0) + return ret; + av_log(NULL, AV_LOG_DEBUG, "Splitting the commandline.\n"); while (optindex < argc) { const char *opt = argv[optindex++], *arg; const OptionDef *po; - int ret; + int ret, group_idx; av_log(NULL, AV_LOG_DEBUG, "Reading option '%s' ...", opt); @@ -716,7 +803,10 @@ int split_commandline(OptionParseContext *octx, int argc, char *argv[], } /* unnamed group separators, e.g. output filename */ if (opt[0] != '-' || !opt[1] || dashdash+1 == optindex) { - finish_group(octx, 0, opt); + ret = finish_group(octx, 0, opt); + if (ret < 0) + return ret; + av_log(NULL, AV_LOG_DEBUG, " matched as %s.\n", groups[0].name); continue; } @@ -732,11 +822,15 @@ do { \ } while (0) /* named group separators, e.g. -i */ - if ((ret = match_group_separator(groups, nb_groups, opt)) >= 0) { + group_idx = match_group_separator(groups, nb_groups, opt); + if (group_idx >= 0) { GET_ARG(arg); - finish_group(octx, ret, arg); + ret = finish_group(octx, group_idx, arg); + if (ret < 0) + return ret; + av_log(NULL, AV_LOG_DEBUG, " matched as %s with argument '%s'.\n", - groups[ret].name, arg); + groups[group_idx].name, arg); continue; } @@ -746,13 +840,16 @@ do { \ if (po->flags & OPT_EXIT) { /* optional argument, e.g. -h */ arg = argv[optindex++]; - } else if (po->flags & HAS_ARG) { + } else if (opt_has_arg(po)) { GET_ARG(arg); } else { arg = "1"; } - add_opt(octx, po, opt, arg); + ret = add_opt(octx, po, opt, arg); + if (ret < 0) + return ret; + av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with " "argument '%s'.\n", po->name, po->help, arg); continue; @@ -776,8 +873,11 @@ do { \ /* boolean -nofoo options */ if (opt[0] == 'n' && opt[1] == 'o' && (po = find_option(options, opt + 2)) && - po->name && po->flags & OPT_BOOL) { - add_opt(octx, po, opt, "0"); + po->name && po->type == OPT_TYPE_BOOL) { + ret = add_opt(octx, po, opt, "0"); + if (ret < 0) + return ret; + av_log(NULL, AV_LOG_DEBUG, " matched as option '%s' (%s) with " "argument 0.\n", po->name, po->help); continue; @@ -796,11 +896,6 @@ do { \ return 0; } -void print_error(const char *filename, int err) -{ - av_log(NULL, AV_LOG_ERROR, "%s: %s\n", filename, av_err2str(err)); -} - int read_yesno(void) { int c = getchar(); @@ -892,8 +987,9 @@ int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec) return ret; } -AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id, - AVFormatContext *s, AVStream *st, const AVCodec *codec) +int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id, + AVFormatContext *s, AVStream *st, const AVCodec *codec, + AVDictionary **dst) { AVDictionary *ret = NULL; const AVDictionaryEntry *t = NULL; @@ -926,12 +1022,16 @@ AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id, char *p = strchr(t->key, ':'); /* check stream specification in opt name */ - if (p) - switch (check_stream_specifier(s, st, p + 1)) { - case 1: *p = 0; break; - case 0: continue; - default: exit_program(1); - } + if (p) { + int err = check_stream_specifier(s, st, p + 1); + if (err < 0) { + av_dict_free(&ret); + return err; + } else if (!err) + continue; + + *p = 0; + } if (av_opt_find(&cc, t->key, NULL, flags, AV_OPT_SEARCH_FAKE_OBJ) || !codec || @@ -947,41 +1047,58 @@ AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id, if (p) *p = ':'; } - return ret; + + *dst = ret; + return 0; } -AVDictionary **setup_find_stream_info_opts(AVFormatContext *s, - AVDictionary *codec_opts) +int setup_find_stream_info_opts(AVFormatContext *s, + AVDictionary *codec_opts, + AVDictionary ***dst) { - int i; + int ret; AVDictionary **opts; + *dst = NULL; + if (!s->nb_streams) - return NULL; + return 0; + opts = av_calloc(s->nb_streams, sizeof(*opts)); if (!opts) - report_and_exit(AVERROR(ENOMEM)); - for (i = 0; i < s->nb_streams; i++) - opts[i] = filter_codec_opts(codec_opts, s->streams[i]->codecpar->codec_id, - s, s->streams[i], NULL); - return opts; + return AVERROR(ENOMEM); + + for (int i = 0; i < s->nb_streams; i++) { + ret = filter_codec_opts(codec_opts, s->streams[i]->codecpar->codec_id, + s, s->streams[i], NULL, &opts[i]); + if (ret < 0) + goto fail; + } + *dst = opts; + return 0; +fail: + for (int i = 0; i < s->nb_streams; i++) + av_dict_free(&opts[i]); + av_freep(&opts); + return ret; } -void *grow_array(void *array, int elem_size, int *size, int new_size) +int grow_array(void **array, int elem_size, int *size, int new_size) { if (new_size >= INT_MAX / elem_size) { av_log(NULL, AV_LOG_ERROR, "Array too big.\n"); - exit_program(1); + return AVERROR(ERANGE); } if (*size < new_size) { - uint8_t *tmp = av_realloc_array(array, new_size, elem_size); + uint8_t *tmp = av_realloc_array(*array, new_size, elem_size); if (!tmp) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); memset(tmp + *size*elem_size, 0, (new_size-*size) * elem_size); *size = new_size; - return tmp; + *array = tmp; + return 0; } - return array; + return 0; } void *allocate_array_elem(void *ptr, size_t elem_size, int *nb_elems) @@ -990,15 +1107,15 @@ void *allocate_array_elem(void *ptr, size_t elem_size, int *nb_elems) if (!(new_elem = av_mallocz(elem_size)) || av_dynarray_add_nofree(ptr, nb_elems, new_elem) < 0) - report_and_exit(AVERROR(ENOMEM)); + return NULL; return new_elem; } -double get_rotation(int32_t *displaymatrix) +double get_rotation(const int32_t *displaymatrix) { double theta = 0; if (displaymatrix) - theta = -round(av_display_rotation_get((int32_t*) displaymatrix)); + theta = -round(av_display_rotation_get(displaymatrix)); theta -= 360*floor(theta/360 + 0.9/360); @@ -1010,3 +1127,29 @@ double get_rotation(int32_t *displaymatrix) return theta; } + +/* read file contents into a string */ +char *file_read(const char *filename) +{ + AVIOContext *pb = NULL; + int ret = avio_open(&pb, filename, AVIO_FLAG_READ); + AVBPrint bprint; + char *str; + + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Error opening file %s.\n", filename); + return NULL; + } + + av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED); + ret = avio_read_to_bprint(pb, &bprint, SIZE_MAX); + avio_closep(&pb); + if (ret < 0) { + av_bprint_finalize(&bprint, NULL); + return NULL; + } + ret = av_bprint_finalize(&bprint, &str); + if (ret < 0) + return NULL; + return str; +} diff --git a/fftools/cmdutils.h b/fftools/cmdutils.h index 44962219839..d0c773663ba 100644 --- a/fftools/cmdutils.h +++ b/fftools/cmdutils.h @@ -49,27 +49,6 @@ extern AVDictionary *swr_opts; extern AVDictionary *format_opts, *codec_opts; extern int hide_banner; -/** - * Register a program-specific cleanup routine. - */ -void register_exit(void (*cb)(int ret)); - -/** - * Reports an error corresponding to the provided - * AVERROR code and calls exit_program() with the - * corresponding POSIX error code. - * @note ret must be an AVERROR-value of a POSIX error code - * (i.e. AVERROR(EFOO) and not AVERROR_FOO). - * library functions can return both, so call this only - * with AVERROR(EFOO) of your own. - */ -void report_and_exit(int ret) av_noreturn; - -/** - * Wraps exit with a program-specific cleanup routine. - */ -void exit_program(int ret) av_noreturn; - /** * Initialize dynamic library loading */ @@ -98,10 +77,19 @@ int opt_default(void *optctx, const char *opt, const char *arg); */ int opt_timelimit(void *optctx, const char *opt, const char *arg); +enum OptionType { + OPT_TYPE_FUNC, + OPT_TYPE_BOOL, + OPT_TYPE_STRING, + OPT_TYPE_INT, + OPT_TYPE_INT64, + OPT_TYPE_FLOAT, + OPT_TYPE_DOUBLE, + OPT_TYPE_TIME, +}; + /** * Parse a string and return its corresponding value as a double. - * Exit from the application if the string cannot be correctly - * parsed or the corresponding value is invalid. * * @param context the context of the value to be set (e.g. the * corresponding command line option name) @@ -111,25 +99,8 @@ int opt_timelimit(void *optctx, const char *opt, const char *arg); * @param min the minimum valid accepted value * @param max the maximum valid accepted value */ -double parse_number_or_die(const char *context, const char *numstr, int type, - double min, double max); - -/** - * Parse a string specifying a time and return its corresponding - * value as a number of microseconds. Exit from the application if - * the string cannot be correctly parsed. - * - * @param context the context of the value to be set (e.g. the - * corresponding command line option name) - * @param timestr the string to be parsed - * @param is_duration a flag which tells how to interpret timestr, if - * not zero timestr is interpreted as a duration, otherwise as a - * date - * - * @see av_parse_time() - */ -int64_t parse_time_or_die(const char *context, const char *timestr, - int is_duration); +int parse_number(const char *context, const char *numstr, enum OptionType type, + double min, double max, double *dst); typedef struct SpecifierOpt { char *specifier; /**< stream/chapter/program/... specifier */ @@ -143,31 +114,70 @@ typedef struct SpecifierOpt { } u; } SpecifierOpt; +typedef struct SpecifierOptList { + SpecifierOpt *opt; + int nb_opt; + + /* Canonical option definition that was parsed into this list. */ + const struct OptionDef *opt_canon; + enum OptionType type; +} SpecifierOptList; + typedef struct OptionDef { const char *name; + enum OptionType type; int flags; -#define HAS_ARG 0x0001 -#define OPT_BOOL 0x0002 -#define OPT_EXPERT 0x0004 -#define OPT_STRING 0x0008 -#define OPT_VIDEO 0x0010 -#define OPT_AUDIO 0x0020 -#define OPT_INT 0x0080 -#define OPT_FLOAT 0x0100 -#define OPT_SUBTITLE 0x0200 -#define OPT_INT64 0x0400 -#define OPT_EXIT 0x0800 -#define OPT_DATA 0x1000 -#define OPT_PERFILE 0x2000 /* the option is per-file (currently ffmpeg-only). - implied by OPT_OFFSET or OPT_SPEC */ -#define OPT_OFFSET 0x4000 /* option is specified as an offset in a passed optctx */ -#define OPT_SPEC 0x8000 /* option is to be stored in an array of SpecifierOpt. - Implies OPT_OFFSET. Next element after the offset is - an int containing element count in the array. */ -#define OPT_TIME 0x10000 -#define OPT_DOUBLE 0x20000 -#define OPT_INPUT 0x40000 -#define OPT_OUTPUT 0x80000 + +/* The OPT_TYPE_FUNC option takes an argument. + * Must not be used with other option types, as for those it holds: + * - OPT_TYPE_BOOL do not take an argument + * - all other types do + */ +#define OPT_FUNC_ARG (1 << 0) +/* Program will immediately exit after processing this option */ +#define OPT_EXIT (1 << 1) +/* Option is intended for advanced users. Only affects + * help output. + */ +#define OPT_EXPERT (1 << 2) +#define OPT_VIDEO (1 << 3) +#define OPT_AUDIO (1 << 4) +#define OPT_SUBTITLE (1 << 5) +#define OPT_DATA (1 << 6) +/* The option is per-file (currently ffmpeg-only). At least one of OPT_INPUT, + * OPT_OUTPUT, OPT_DECODER must be set when this flag is in use. + */ +#define OPT_PERFILE (1 << 7) + +/* Option is specified as an offset in a passed optctx. + * Always use as OPT_OFFSET in option definitions. */ +#define OPT_FLAG_OFFSET (1 << 8) +#define OPT_OFFSET (OPT_FLAG_OFFSET | OPT_PERFILE) + +/* Option is to be stored in a SpecifierOptList. + Always use as OPT_SPEC in option definitions. */ +#define OPT_FLAG_SPEC (1 << 9) +#define OPT_SPEC (OPT_FLAG_SPEC | OPT_OFFSET) + +/* Option applies per-stream (implies OPT_SPEC). */ +#define OPT_FLAG_PERSTREAM (1 << 10) +#define OPT_PERSTREAM (OPT_FLAG_PERSTREAM | OPT_SPEC) + +/* ffmpeg-only - specifies whether an OPT_PERFILE option applies to input, + * output, or both. */ +#define OPT_INPUT (1 << 11) +#define OPT_OUTPUT (1 << 12) + +/* This option is a "canonical" form, to which one or more alternatives + * exist. These alternatives are listed in u1.names_alt. */ +#define OPT_HAS_ALT (1 << 13) +/* This option is an alternative form of some other option, whose + * name is stored in u1.name_canon */ +#define OPT_HAS_CANON (1 << 14) + +/* ffmpeg-only - OPT_PERFILE may apply to standalone decoders */ +#define OPT_DECODER (1 << 15) + union { void *dst_ptr; int (*func_arg)(void *, const char *, const char *); @@ -175,6 +185,15 @@ typedef struct OptionDef { } u; const char *help; const char *argname; + + union { + /* Name of the canonical form of this option. + * Is valid when OPT_HAS_CANON is set. */ + const char *name_canon; + /* A NULL-terminated list of alternate forms of this option. + * Is valid when OPT_HAS_ALT is set. */ + const char * const *names_alt; + } u1; } OptionDef; /** @@ -184,10 +203,9 @@ typedef struct OptionDef { * @param msg title of this group. Only printed if at least one option matches. * @param req_flags print only options which have all those flags set. * @param rej_flags don't print options which have any of those flags set. - * @param alt_flags print only options that have at least one of those flags set */ void show_help_options(const OptionDef *options, const char *msg, int req_flags, - int rej_flags, int alt_flags); + int rej_flags); /** * Show help for all options with given flags in class and all its @@ -213,8 +231,8 @@ void show_help_default(const char *opt, const char *arg); * argument without a leading option name flag. NULL if such arguments do * not have to be processed. */ -void parse_options(void *optctx, int argc, char **argv, const OptionDef *options, - void (* parse_arg_function)(void *optctx, const char*)); +int parse_options(void *optctx, int argc, char **argv, const OptionDef *options, + int (* parse_arg_function)(void *optctx, const char*)); /** * Parse one given option. @@ -289,7 +307,7 @@ typedef struct OptionParseContext { * * @param optctx an app-specific options context. NULL for global options group */ -int parse_optgroup(void *optctx, OptionGroup *g); +int parse_optgroup(void *optctx, OptionGroup *g, const OptionDef *defs); /** * Split the commandline into an intermediate form convenient for further @@ -352,10 +370,12 @@ int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec); * @param st A stream from s for which the options should be filtered. * @param codec The particular codec for which the options should be filtered. * If null, the default one is looked up according to the codec id. - * @return a pointer to the created dictionary + * @param dst a pointer to the created dictionary + * @return a non-negative number on success, a negative error code on failure */ -AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id, - AVFormatContext *s, AVStream *st, const AVCodec *codec); +int filter_codec_opts(const AVDictionary *opts, enum AVCodecID codec_id, + AVFormatContext *s, AVStream *st, const AVCodec *codec, + AVDictionary **dst); /** * Setup AVCodecContext options for avformat_find_stream_info(). @@ -364,12 +384,10 @@ AVDictionary *filter_codec_opts(AVDictionary *opts, enum AVCodecID codec_id, * contained in s. * Each dictionary will contain the options from codec_opts which can * be applied to the corresponding stream codec context. - * - * @return pointer to the created array of dictionaries. - * Calls exit() on failure. */ -AVDictionary **setup_find_stream_info_opts(AVFormatContext *s, - AVDictionary *codec_opts); +int setup_find_stream_info_opts(AVFormatContext *s, + AVDictionary *codec_opts, + AVDictionary ***dst); /** * Print an error message to stderr, indicating filename and a human @@ -380,7 +398,10 @@ AVDictionary **setup_find_stream_info_opts(AVFormatContext *s, * * @see av_strerror() */ -void print_error(const char *filename, int err); +static inline void print_error(const char *filename, int err) +{ + av_log(NULL, AV_LOG_ERROR, "%s: %s\n", filename, av_err2str(err)); +} /** * Print the program banner to stderr. The banner contents depend on the @@ -418,35 +439,31 @@ FILE *get_preset_file(char *filename, size_t filename_size, /** * Realloc array to hold new_size elements of elem_size. - * Calls exit() on failure. * - * @param array array to reallocate + * @param array pointer to the array to reallocate, will be updated + * with a new pointer on success * @param elem_size size in bytes of each element * @param size new element count will be written here * @param new_size number of elements to place in reallocated array - * @return reallocated array + * @return a non-negative number on success, a negative error code on failure */ -void *grow_array(void *array, int elem_size, int *size, int new_size); +int grow_array(void **array, int elem_size, int *size, int new_size); /** * Atomically add a new element to an array of pointers, i.e. allocate * a new entry, reallocate the array of pointers and make the new last * member of this array point to the newly allocated buffer. - * Calls exit() on failure. * * @param array array of pointers to reallocate * @param elem_size size of the new element to allocate * @param nb_elems pointer to the number of elements of the array array; * *nb_elems will be incremented by one by this function. - * @return pointer to the newly allocated entry + * @return pointer to the newly allocated entry or NULL on failure */ void *allocate_array_elem(void *array, size_t elem_size, int *nb_elems); #define GROW_ARRAY(array, nb_elems)\ - array = grow_array(array, sizeof(*array), &nb_elems, nb_elems + 1) - -#define ALLOC_ARRAY_ELEM(array, nb_elems)\ - allocate_array_elem(&array, sizeof(*array[0]), &nb_elems) + grow_array((void**)&array, sizeof(*array), &nb_elems, nb_elems + 1) #define GET_PIX_FMT_NAME(pix_fmt)\ const char *name = av_get_pix_fmt_name(pix_fmt); @@ -461,6 +478,9 @@ void *allocate_array_elem(void *array, size_t elem_size, int *nb_elems); char name[16];\ snprintf(name, sizeof(name), "%d", rate); -double get_rotation(int32_t *displaymatrix); +double get_rotation(const int32_t *displaymatrix); + +/* read file contents into a string */ +char *file_read(const char *filename); #endif /* FFTOOLS_CMDUTILS_H */ diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index c819d30ca52..4a0c7d5c4d3 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -24,14 +24,14 @@ */ #include "config.h" -#include -#include -#include -#include + #include #include #include #include +#include +#include +#include #if HAVE_IO_H #include @@ -40,37 +40,6 @@ #include #endif -#include "libavformat/avformat.h" -#include "libavdevice/avdevice.h" -#include "libswresample/swresample.h" -#include "libavutil/opt.h" -#include "libavutil/channel_layout.h" -#include "libavutil/parseutils.h" -#include "libavutil/samplefmt.h" -#include "libavutil/fifo.h" -#include "libavutil/hwcontext.h" -#include "libavutil/internal.h" -#include "libavutil/intreadwrite.h" -#include "libavutil/dict.h" -#include "libavutil/display.h" -#include "libavutil/mathematics.h" -#include "libavutil/pixdesc.h" -#include "libavutil/avstring.h" -#include "libavutil/libm.h" -#include "libavutil/imgutils.h" -#include "libavutil/timestamp.h" -#include "libavutil/bprint.h" -#include "libavutil/time.h" -#include "libavutil/thread.h" -#include "libavutil/threadmessage.h" -#include "libavcodec/mathops.h" -#include "libavcodec/version.h" -#include "libavformat/os_support.h" - -# include "libavfilter/avfilter.h" -# include "libavfilter/buffersrc.h" -# include "libavfilter/buffersink.h" - #if HAVE_SYS_RESOURCE_H #include #include @@ -86,7 +55,6 @@ #include #endif - #if HAVE_SYS_SELECT_H #include #endif @@ -100,25 +68,45 @@ #include #endif -#include +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/bprint.h" +#include "libavutil/channel_layout.h" +#include "libavutil/dict.h" +#include "libavutil/display.h" +#include "libavutil/fifo.h" +#include "libavutil/hwcontext.h" +#include "libavutil/imgutils.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/libm.h" +#include "libavutil/mathematics.h" +#include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "libavutil/pixdesc.h" +#include "libavutil/samplefmt.h" +#include "libavutil/thread.h" +#include "libavutil/threadmessage.h" +#include "libavutil/time.h" +#include "libavutil/timestamp.h" + +#include "libavcodec/version.h" + +#include "libavformat/avformat.h" + +#include "libavdevice/avdevice.h" + +#include "libswresample/swresample.h" -#include "ffmpeg.h" #include "cmdutils.h" +#include "ffmpeg.h" +#include "ffmpeg_sched.h" +#include "ffmpeg_utils.h" #include "sync_queue.h" -#include "libavutil/avassert.h" - const char program_name[] = "ffmpeg"; const int program_birth_year = 2000; -static FILE *vstats_file; - -// optionally attached as opaque_ref to decoded AVFrames -typedef struct FrameData { - uint64_t idx; - int64_t pts; - AVRational tb; -} FrameData; +FILE *vstats_file; typedef struct BenchmarkTimeStamps { int64_t real_usec; @@ -126,16 +114,10 @@ typedef struct BenchmarkTimeStamps { int64_t sys_usec; } BenchmarkTimeStamps; -static int trigger_fix_sub_duration_heartbeat(OutputStream *ost, const AVPacket *pkt); static BenchmarkTimeStamps get_benchmark_time_stamps(void); static int64_t getmaxrss(void); -static int ifilter_has_all_input_formats(FilterGraph *fg); -static int64_t nb_frames_dup = 0; -static uint64_t dup_warning = 1000; -static int64_t nb_frames_drop = 0; -static int64_t decode_error_stat[2]; -unsigned nb_output_dumped = 0; +atomic_uint nb_output_dumped = 0; static BenchmarkTimeStamps current_time; AVIOContext *progress_avio = NULL; @@ -149,6 +131,9 @@ int nb_output_files = 0; FilterGraph **filtergraphs; int nb_filtergraphs; +Decoder **decoders; +int nb_decoders; + #if HAVE_TERMIOS_H /* init terminal so that we can grab keys */ @@ -156,163 +141,6 @@ static struct termios oldtty; static int restore_tty; #endif -/* sub2video hack: - Convert subtitles to video with alpha to insert them in filter graphs. - This is a temporary solution until libavfilter gets real subtitles support. - */ - -static int sub2video_get_blank_frame(InputStream *ist) -{ - int ret; - AVFrame *frame = ist->sub2video.frame; - - av_frame_unref(frame); - ist->sub2video.frame->width = ist->dec_ctx->width ? ist->dec_ctx->width : ist->sub2video.w; - ist->sub2video.frame->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h; - ist->sub2video.frame->format = AV_PIX_FMT_RGB32; - if ((ret = av_frame_get_buffer(frame, 0)) < 0) - return ret; - memset(frame->data[0], 0, frame->height * frame->linesize[0]); - return 0; -} - -static void sub2video_copy_rect(uint8_t *dst, int dst_linesize, int w, int h, - AVSubtitleRect *r) -{ - uint32_t *pal, *dst2; - uint8_t *src, *src2; - int x, y; - - if (r->type != SUBTITLE_BITMAP) { - av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n"); - return; - } - if (r->x < 0 || r->x + r->w > w || r->y < 0 || r->y + r->h > h) { - av_log(NULL, AV_LOG_WARNING, "sub2video: rectangle (%d %d %d %d) overflowing %d %d\n", - r->x, r->y, r->w, r->h, w, h - ); - return; - } - - dst += r->y * dst_linesize + r->x * 4; - src = r->data[0]; - pal = (uint32_t *)r->data[1]; - for (y = 0; y < r->h; y++) { - dst2 = (uint32_t *)dst; - src2 = src; - for (x = 0; x < r->w; x++) - *(dst2++) = pal[*(src2++)]; - dst += dst_linesize; - src += r->linesize[0]; - } -} - -static void sub2video_push_ref(InputStream *ist, int64_t pts) -{ - AVFrame *frame = ist->sub2video.frame; - int i; - int ret; - - av_assert1(frame->data[0]); - ist->sub2video.last_pts = frame->pts = pts; - for (i = 0; i < ist->nb_filters; i++) { - ret = av_buffersrc_add_frame_flags(ist->filters[i]->filter, frame, - AV_BUFFERSRC_FLAG_KEEP_REF | - AV_BUFFERSRC_FLAG_PUSH); - if (ret != AVERROR_EOF && ret < 0) - av_log(NULL, AV_LOG_WARNING, "Error while add the frame to buffer source(%s).\n", - av_err2str(ret)); - } -} - -void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub) -{ - AVFrame *frame = ist->sub2video.frame; - int8_t *dst; - int dst_linesize; - int num_rects, i; - int64_t pts, end_pts; - - if (!frame) - return; - if (sub) { - pts = av_rescale_q(sub->pts + sub->start_display_time * 1000LL, - AV_TIME_BASE_Q, ist->st->time_base); - end_pts = av_rescale_q(sub->pts + sub->end_display_time * 1000LL, - AV_TIME_BASE_Q, ist->st->time_base); - num_rects = sub->num_rects; - } else { - /* If we are initializing the system, utilize current heartbeat - PTS as the start time, and show until the following subpicture - is received. Otherwise, utilize the previous subpicture's end time - as the fall-back value. */ - pts = ist->sub2video.initialize ? - heartbeat_pts : ist->sub2video.end_pts; - end_pts = INT64_MAX; - num_rects = 0; - } - if (sub2video_get_blank_frame(ist) < 0) { - av_log(NULL, AV_LOG_ERROR, - "Impossible to get a blank canvas.\n"); - return; - } - dst = frame->data [0]; - dst_linesize = frame->linesize[0]; - for (i = 0; i < num_rects; i++) - sub2video_copy_rect(dst, dst_linesize, frame->width, frame->height, sub->rects[i]); - sub2video_push_ref(ist, pts); - ist->sub2video.end_pts = end_pts; - ist->sub2video.initialize = 0; -} - -static void sub2video_heartbeat(InputStream *ist, int64_t pts) -{ - InputFile *infile = input_files[ist->file_index]; - int i, j, nb_reqs; - int64_t pts2; - - /* When a frame is read from a file, examine all sub2video streams in - the same file and send the sub2video frame again. Otherwise, decoded - video frames could be accumulating in the filter graph while a filter - (possibly overlay) is desperately waiting for a subtitle frame. */ - for (i = 0; i < infile->nb_streams; i++) { - InputStream *ist2 = infile->streams[i]; - if (!ist2->sub2video.frame) - continue; - /* subtitles seem to be usually muxed ahead of other streams; - if not, subtracting a larger time here is necessary */ - pts2 = av_rescale_q(pts, ist->st->time_base, ist2->st->time_base) - 1; - /* do not send the heartbeat frame if the subtitle is already ahead */ - if (pts2 <= ist2->sub2video.last_pts) - continue; - if (pts2 >= ist2->sub2video.end_pts || ist2->sub2video.initialize) - /* if we have hit the end of the current displayed subpicture, - or if we need to initialize the system, update the - overlayed subpicture and its start/end times */ - sub2video_update(ist2, pts2 + 1, NULL); - for (j = 0, nb_reqs = 0; j < ist2->nb_filters; j++) - nb_reqs += av_buffersrc_get_nb_failed_requests(ist2->filters[j]->filter); - if (nb_reqs) - sub2video_push_ref(ist2, pts2); - } -} - -static void sub2video_flush(InputStream *ist) -{ - int i; - int ret; - - if (ist->sub2video.end_pts < INT64_MAX) - sub2video_update(ist, INT64_MAX, NULL); - for (i = 0; i < ist->nb_filters; i++) { - ret = av_buffersrc_add_frame(ist->filters[i]->filter, NULL); - if (ret != AVERROR_EOF && ret < 0) - av_log(NULL, AV_LOG_WARNING, "Flush the frame error.\n"); - } -} - -/* end of sub2video hack */ - static void term_exit_sigsafe(void) { #if HAVE_TERMIOS_H @@ -331,7 +159,6 @@ static volatile int received_sigterm = 0; static volatile int received_nb_signals = 0; static atomic_int transcode_init_done = ATOMIC_VAR_INIT(0); static volatile int ffmpeg_exited = 0; -int main_return_code = 0; static int64_t copy_ts_first_pts = AV_NOPTS_VALUE; static void @@ -462,7 +289,7 @@ static int read_key(void) return n; } #elif HAVE_KBHIT -# if HAVE_PEEKNAMEDPIPE +# if HAVE_PEEKNAMEDPIPE && HAVE_GETSTDHANDLE static int is_pipe; static HANDLE input_handle; DWORD dw, nchars; @@ -479,8 +306,9 @@ static int read_key(void) } //Read it if(nchars != 0) { - read(0, &ch, 1); - return ch; + if (read(0, &ch, 1) == 1) + return ch; + return 0; }else{ return -1; } @@ -501,60 +329,25 @@ const AVIOInterruptCB int_cb = { decode_interrupt_cb, NULL }; static void ffmpeg_cleanup(int ret) { - int i, j; - if (do_benchmark) { int maxrss = getmaxrss() / 1024; - av_log(NULL, AV_LOG_INFO, "bench: maxrss=%ikB\n", maxrss); + av_log(NULL, AV_LOG_INFO, "bench: maxrss=%iKiB\n", maxrss); } - for (i = 0; i < nb_filtergraphs; i++) { - FilterGraph *fg = filtergraphs[i]; - avfilter_graph_free(&fg->graph); - for (j = 0; j < fg->nb_inputs; j++) { - InputFilter *ifilter = fg->inputs[j]; - struct InputStream *ist = ifilter->ist; - - if (ifilter->frame_queue) { - AVFrame *frame; - while (av_fifo_read(ifilter->frame_queue, &frame, 1) >= 0) - av_frame_free(&frame); - av_fifo_freep2(&ifilter->frame_queue); - } - av_freep(&ifilter->displaymatrix); - if (ist->sub2video.sub_queue) { - AVSubtitle sub; - while (av_fifo_read(ist->sub2video.sub_queue, &sub, 1) >= 0) - avsubtitle_free(&sub); - av_fifo_freep2(&ist->sub2video.sub_queue); - } - av_buffer_unref(&ifilter->hw_frames_ctx); - av_freep(&ifilter->name); - av_freep(&fg->inputs[j]); - } - av_freep(&fg->inputs); - for (j = 0; j < fg->nb_outputs; j++) { - OutputFilter *ofilter = fg->outputs[j]; - - avfilter_inout_free(&ofilter->out_tmp); - av_freep(&ofilter->name); - av_channel_layout_uninit(&ofilter->ch_layout); - av_freep(&fg->outputs[j]); - } - av_freep(&fg->outputs); - av_freep(&fg->graph_desc); - - av_freep(&filtergraphs[i]); - } + for (int i = 0; i < nb_filtergraphs; i++) + fg_free(&filtergraphs[i]); av_freep(&filtergraphs); - /* close files */ - for (i = 0; i < nb_output_files; i++) - of_close(&output_files[i]); + for (int i = 0; i < nb_output_files; i++) + of_free(&output_files[i]); - for (i = 0; i < nb_input_files; i++) + for (int i = 0; i < nb_input_files; i++) ifile_close(&input_files[i]); + for (int i = 0; i < nb_decoders; i++) + dec_free(&decoders[i]); + av_freep(&decoders); + if (vstats_file) { if (fclose(vstats_file)) av_log(NULL, AV_LOG_ERROR, @@ -564,6 +357,8 @@ static void ffmpeg_cleanup(int ret) av_freep(&vstats_filename); of_enc_stats_close(); + hw_device_free_all(); + av_freep(&filter_nbthreads); av_freep(&input_files); @@ -583,11 +378,9 @@ static void ffmpeg_cleanup(int ret) ffmpeg_exited = 1; } -/* iterate over all output streams in all output files; - * pass NULL to start iteration */ -static OutputStream *ost_iter(OutputStream *prev) +OutputStream *ost_iter(OutputStream *prev) { - int of_idx = prev ? prev->file_index : 0; + int of_idx = prev ? prev->file->index : 0; int ost_idx = prev ? prev->index + 1 : 0; for (; of_idx < nb_output_files; of_idx++) { @@ -603,8 +396,8 @@ static OutputStream *ost_iter(OutputStream *prev) InputStream *ist_iter(InputStream *prev) { - int if_idx = prev ? prev->file_index : 0; - int ist_idx = prev ? prev->st->index + 1 : 0; + int if_idx = prev ? prev->file->index : 0; + int ist_idx = prev ? prev->index + 1 : 0; for (; if_idx < nb_input_files; if_idx++) { InputFile *f = input_files[if_idx]; @@ -617,6 +410,91 @@ InputStream *ist_iter(InputStream *prev) return NULL; } +static void frame_data_free(void *opaque, uint8_t *data) +{ + FrameData *fd = (FrameData *)data; + + avcodec_parameters_free(&fd->par_enc); + + av_free(data); +} + +static int frame_data_ensure(AVBufferRef **dst, int writable) +{ + AVBufferRef *src = *dst; + + if (!src || (writable && !av_buffer_is_writable(src))) { + FrameData *fd; + + fd = av_mallocz(sizeof(*fd)); + if (!fd) + return AVERROR(ENOMEM); + + *dst = av_buffer_create((uint8_t *)fd, sizeof(*fd), + frame_data_free, NULL, 0); + if (!*dst) { + av_buffer_unref(&src); + av_freep(&fd); + return AVERROR(ENOMEM); + } + + if (src) { + const FrameData *fd_src = (const FrameData *)src->data; + + memcpy(fd, fd_src, sizeof(*fd)); + fd->par_enc = NULL; + + if (fd_src->par_enc) { + int ret = 0; + + fd->par_enc = avcodec_parameters_alloc(); + ret = fd->par_enc ? + avcodec_parameters_copy(fd->par_enc, fd_src->par_enc) : + AVERROR(ENOMEM); + if (ret < 0) { + av_buffer_unref(dst); + av_buffer_unref(&src); + return ret; + } + } + + av_buffer_unref(&src); + } else { + fd->dec.frame_num = UINT64_MAX; + fd->dec.pts = AV_NOPTS_VALUE; + + for (unsigned i = 0; i < FF_ARRAY_ELEMS(fd->wallclock); i++) + fd->wallclock[i] = INT64_MIN; + } + } + + return 0; +} + +FrameData *frame_data(AVFrame *frame) +{ + int ret = frame_data_ensure(&frame->opaque_ref, 1); + return ret < 0 ? NULL : (FrameData*)frame->opaque_ref->data; +} + +const FrameData *frame_data_c(AVFrame *frame) +{ + int ret = frame_data_ensure(&frame->opaque_ref, 0); + return ret < 0 ? NULL : (const FrameData*)frame->opaque_ref->data; +} + +FrameData *packet_data(AVPacket *pkt) +{ + int ret = frame_data_ensure(&pkt->opaque_ref, 1); + return ret < 0 ? NULL : (FrameData*)pkt->opaque_ref->data; +} + +const FrameData *packet_data_c(AVPacket *pkt) +{ + int ret = frame_data_ensure(&pkt->opaque_ref, 0); + return ret < 0 ? NULL : (const FrameData*)pkt->opaque_ref->data; +} + void remove_avoptions(AVDictionary **a, AVDictionary *b) { const AVDictionaryEntry *t = NULL; @@ -626,21 +504,18 @@ void remove_avoptions(AVDictionary **a, AVDictionary *b) } } -void assert_avoptions(AVDictionary *m) +int check_avoptions(AVDictionary *m) { const AVDictionaryEntry *t; if ((t = av_dict_get(m, "", NULL, AV_DICT_IGNORE_SUFFIX))) { av_log(NULL, AV_LOG_FATAL, "Option %s not found.\n", t->key); - exit_program(1); + return AVERROR_OPTION_NOT_FOUND; } -} -static void abort_codec_experimental(const AVCodec *c, int encoder) -{ - exit_program(1); + return 0; } -static void update_benchmark(const char *fmt, ...) +void update_benchmark(const char *fmt, ...) { if (do_benchmark_all) { BenchmarkTimeStamps t = get_benchmark_time_stamps(); @@ -661,2722 +536,164 @@ static void update_benchmark(const char *fmt, ...) } } -static void close_output_stream(OutputStream *ost) +static void print_report(int is_last_report, int64_t timer_start, int64_t cur_time, int64_t pts) { - OutputFile *of = output_files[ost->file_index]; - ost->finished |= ENCODER_FINISHED; - - if (ost->sq_idx_encode >= 0) - sq_send(of->sq_encode, ost->sq_idx_encode, SQFRAME(NULL)); -} + AVBPrint buf, buf_script; + int64_t total_size = of_filesize(output_files[0]); + int vid; + double bitrate; + double speed; + static int64_t last_time = -1; + static int first_report = 1; + uint64_t nb_frames_dup = 0, nb_frames_drop = 0; + int mins, secs, us; + int64_t hours; + const char *hours_sign; + int ret; + float t; -static int check_recording_time(OutputStream *ost, int64_t ts, AVRational tb) -{ - OutputFile *of = output_files[ost->file_index]; + if (!print_stats && !is_last_report && !progress_avio) + return; - if (of->recording_time != INT64_MAX && - av_compare_ts(ts, tb, of->recording_time, AV_TIME_BASE_Q) >= 0) { - close_output_stream(ost); - return 0; + if (!is_last_report) { + if (last_time == -1) { + last_time = cur_time; + } + if (((cur_time - last_time) < stats_period && !first_report) || + (first_report && atomic_load(&nb_output_dumped) < nb_output_files)) + return; + last_time = cur_time; } - return 1; -} -static double adjust_frame_pts_to_encoder_tb(OutputFile *of, OutputStream *ost, - AVFrame *frame) -{ - double float_pts = AV_NOPTS_VALUE; // this is identical to frame.pts but with higher precision - const int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? - 0 : of->start_time; - - AVCodecContext *const enc = ost->enc_ctx; - - AVRational tb = enc->time_base; - AVRational filter_tb = frame->time_base; - const int extra_bits = av_clip(29 - av_log2(tb.den), 0, 16); - - if (frame->pts == AV_NOPTS_VALUE) - goto early_exit; - - tb.den <<= extra_bits; - float_pts = av_rescale_q(frame->pts, filter_tb, tb) - - av_rescale_q(start_time, AV_TIME_BASE_Q, tb); - float_pts /= 1 << extra_bits; - // avoid exact midoints to reduce the chance of rounding differences, this - // can be removed in case the fps code is changed to work with integers - float_pts += FFSIGN(float_pts) * 1.0 / (1<<17); - - frame->pts = av_rescale_q(frame->pts, filter_tb, enc->time_base) - - av_rescale_q(start_time, AV_TIME_BASE_Q, enc->time_base); - frame->time_base = enc->time_base; - -early_exit: - - if (debug_ts) { - av_log(NULL, AV_LOG_INFO, "filter -> pts:%s pts_time:%s exact:%f time_base:%d/%d\n", - frame ? av_ts2str(frame->pts) : "NULL", - (enc && frame) ? av_ts2timestr(frame->pts, &enc->time_base) : "NULL", - float_pts, - enc ? enc->time_base.num : -1, - enc ? enc->time_base.den : -1); - } + t = (cur_time-timer_start) / 1000000.0; - return float_pts; -} + vid = 0; + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); + av_bprint_init(&buf_script, 0, AV_BPRINT_SIZE_AUTOMATIC); + for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { + const float q = ost->enc ? atomic_load(&ost->quality) / (float) FF_QP2LAMBDA : -1; -static int init_output_stream(OutputStream *ost, AVFrame *frame, - char *error, int error_len); + if (vid && ost->type == AVMEDIA_TYPE_VIDEO) { + av_bprintf(&buf, "q=%2.1f ", q); + av_bprintf(&buf_script, "stream_%d_%d_q=%.1f\n", + ost->file->index, ost->index, q); + } + if (!vid && ost->type == AVMEDIA_TYPE_VIDEO && ost->filter) { + float fps; + uint64_t frame_number = atomic_load(&ost->packets_written); -static int init_output_stream_wrapper(OutputStream *ost, AVFrame *frame, - unsigned int fatal) -{ - int ret = AVERROR_BUG; - char error[1024] = {0}; + fps = t > 1 ? frame_number / t : 0; + av_bprintf(&buf, "frame=%5"PRId64" fps=%3.*f q=%3.1f ", + frame_number, fps < 9.95, fps, q); + av_bprintf(&buf_script, "frame=%"PRId64"\n", frame_number); + av_bprintf(&buf_script, "fps=%.2f\n", fps); + av_bprintf(&buf_script, "stream_%d_%d_q=%.1f\n", + ost->file->index, ost->index, q); + if (is_last_report) + av_bprintf(&buf, "L"); - if (ost->initialized) - return 0; + nb_frames_dup = atomic_load(&ost->filter->nb_frames_dup); + nb_frames_drop = atomic_load(&ost->filter->nb_frames_drop); - ret = init_output_stream(ost, frame, error, sizeof(error)); - if (ret < 0) { - av_log(ost, AV_LOG_ERROR, "Error initializing output stream: %s\n", - error); + vid = 1; + } + } - if (fatal) - exit_program(1); + if (copy_ts) { + if (copy_ts_first_pts == AV_NOPTS_VALUE && pts > 1) + copy_ts_first_pts = pts; + if (copy_ts_first_pts != AV_NOPTS_VALUE) + pts -= copy_ts_first_pts; } - return ret; -} + us = FFABS64U(pts) % AV_TIME_BASE; + secs = FFABS64U(pts) / AV_TIME_BASE % 60; + mins = FFABS64U(pts) / AV_TIME_BASE / 60 % 60; + hours = FFABS64U(pts) / AV_TIME_BASE / 3600; + hours_sign = (pts < 0) ? "-" : ""; -static double psnr(double d) -{ - return -10.0 * log10(d); -} + bitrate = pts != AV_NOPTS_VALUE && pts && total_size >= 0 ? total_size * 8 / (pts / 1000.0) : -1; + speed = pts != AV_NOPTS_VALUE && t != 0.0 ? (double)pts / AV_TIME_BASE / t : -1; -static void update_video_stats(OutputStream *ost, const AVPacket *pkt, int write_vstats) -{ - const uint8_t *sd = av_packet_get_side_data(pkt, AV_PKT_DATA_QUALITY_STATS, - NULL); - AVCodecContext *enc = ost->enc_ctx; - int64_t frame_number; - double ti1, bitrate, avg_bitrate; - - ost->quality = sd ? AV_RL32(sd) : -1; - ost->pict_type = sd ? sd[4] : AV_PICTURE_TYPE_NONE; - - for (int i = 0; ierror); i++) { - if (sd && i < sd[5]) - ost->error[i] = AV_RL64(sd + 8 + 8*i); - else - ost->error[i] = -1; + if (total_size < 0) av_bprintf(&buf, "size=N/A time="); + else av_bprintf(&buf, "size=%8.0fKiB time=", total_size / 1024.0); + if (pts == AV_NOPTS_VALUE) { + av_bprintf(&buf, "N/A "); + } else { + av_bprintf(&buf, "%s%02"PRId64":%02d:%02d.%02d ", + hours_sign, hours, mins, secs, (100 * us) / AV_TIME_BASE); } - if (!write_vstats) - return; - - /* this is executed just the first time update_video_stats is called */ - if (!vstats_file) { - vstats_file = fopen(vstats_filename, "w"); - if (!vstats_file) { - perror("fopen"); - exit_program(1); - } + if (bitrate < 0) { + av_bprintf(&buf, "bitrate=N/A"); + av_bprintf(&buf_script, "bitrate=N/A\n"); + }else{ + av_bprintf(&buf, "bitrate=%6.1fkbits/s", bitrate); + av_bprintf(&buf_script, "bitrate=%6.1fkbits/s\n", bitrate); } - frame_number = ost->packets_encoded; - if (vstats_version <= 1) { - fprintf(vstats_file, "frame= %5"PRId64" q= %2.1f ", frame_number, - ost->quality / (float)FF_QP2LAMBDA); - } else { - fprintf(vstats_file, "out= %2d st= %2d frame= %5"PRId64" q= %2.1f ", ost->file_index, ost->index, frame_number, - ost->quality / (float)FF_QP2LAMBDA); + if (total_size < 0) av_bprintf(&buf_script, "total_size=N/A\n"); + else av_bprintf(&buf_script, "total_size=%"PRId64"\n", total_size); + if (pts == AV_NOPTS_VALUE) { + av_bprintf(&buf_script, "out_time_us=N/A\n"); + av_bprintf(&buf_script, "out_time_ms=N/A\n"); + av_bprintf(&buf_script, "out_time=N/A\n"); + } else { + av_bprintf(&buf_script, "out_time_us=%"PRId64"\n", pts); + av_bprintf(&buf_script, "out_time_ms=%"PRId64"\n", pts); + av_bprintf(&buf_script, "out_time=%s%02"PRId64":%02d:%02d.%06d\n", + hours_sign, hours, mins, secs, us); } - if (ost->error[0]>=0 && (enc->flags & AV_CODEC_FLAG_PSNR)) - fprintf(vstats_file, "PSNR= %6.2f ", psnr(ost->error[0] / (enc->width * enc->height * 255.0 * 255.0))); - - fprintf(vstats_file,"f_size= %6d ", pkt->size); - /* compute pts value */ - ti1 = pkt->dts * av_q2d(pkt->time_base); - if (ti1 < 0.01) - ti1 = 0.01; - - bitrate = (pkt->size * 8) / av_q2d(enc->time_base) / 1000.0; - avg_bitrate = (double)(ost->data_size_enc * 8) / ti1 / 1000.0; - fprintf(vstats_file, "s_size= %8.0fkB time= %0.3f br= %7.1fkbits/s avg_br= %7.1fkbits/s ", - (double)ost->data_size_enc / 1024, ti1, bitrate, avg_bitrate); - fprintf(vstats_file, "type= %c\n", av_get_picture_type_char(ost->pict_type)); -} + if (nb_frames_dup || nb_frames_drop) + av_bprintf(&buf, " dup=%"PRId64" drop=%"PRId64, nb_frames_dup, nb_frames_drop); + av_bprintf(&buf_script, "dup_frames=%"PRId64"\n", nb_frames_dup); + av_bprintf(&buf_script, "drop_frames=%"PRId64"\n", nb_frames_drop); -void enc_stats_write(OutputStream *ost, EncStats *es, - const AVFrame *frame, const AVPacket *pkt, - uint64_t frame_num) -{ - AVIOContext *io = es->io; - AVRational tb = frame ? frame->time_base : pkt->time_base; - int64_t pts = frame ? frame->pts : pkt->pts; - - AVRational tbi = (AVRational){ 0, 1}; - int64_t ptsi = INT64_MAX; - - const FrameData *fd; - - if ((frame && frame->opaque_ref) || (pkt && pkt->opaque_ref)) { - fd = (const FrameData*)(frame ? frame->opaque_ref->data : pkt->opaque_ref->data); - tbi = fd->tb; - ptsi = fd->pts; - } - - for (size_t i = 0; i < es->nb_components; i++) { - const EncStatsComponent *c = &es->components[i]; - - switch (c->type) { - case ENC_STATS_LITERAL: avio_write (io, c->str, c->str_len); continue; - case ENC_STATS_FILE_IDX: avio_printf(io, "%d", ost->file_index); continue; - case ENC_STATS_STREAM_IDX: avio_printf(io, "%d", ost->index); continue; - case ENC_STATS_TIMEBASE: avio_printf(io, "%d/%d", tb.num, tb.den); continue; - case ENC_STATS_TIMEBASE_IN: avio_printf(io, "%d/%d", tbi.num, tbi.den); continue; - case ENC_STATS_PTS: avio_printf(io, "%"PRId64, pts); continue; - case ENC_STATS_PTS_IN: avio_printf(io, "%"PRId64, ptsi); continue; - case ENC_STATS_PTS_TIME: avio_printf(io, "%g", pts * av_q2d(tb)); continue; - case ENC_STATS_PTS_TIME_IN: avio_printf(io, "%g", ptsi == INT64_MAX ? - INFINITY : ptsi * av_q2d(tbi)); continue; - case ENC_STATS_FRAME_NUM: avio_printf(io, "%"PRIu64, frame_num); continue; - case ENC_STATS_FRAME_NUM_IN: avio_printf(io, "%"PRIu64, fd ? fd->idx : -1); continue; - } - - if (frame) { - switch (c->type) { - case ENC_STATS_SAMPLE_NUM: avio_printf(io, "%"PRIu64, ost->samples_encoded); continue; - case ENC_STATS_NB_SAMPLES: avio_printf(io, "%d", frame->nb_samples); continue; - default: av_assert0(0); - } - } else { - switch (c->type) { - case ENC_STATS_DTS: avio_printf(io, "%"PRId64, pkt->dts); continue; - case ENC_STATS_DTS_TIME: avio_printf(io, "%g", pkt->dts * av_q2d(tb)); continue; - case ENC_STATS_PKT_SIZE: avio_printf(io, "%d", pkt->size); continue; - case ENC_STATS_BITRATE: { - double duration = FFMAX(pkt->duration, 1) * av_q2d(tb); - avio_printf(io, "%g", 8.0 * pkt->size / duration); - continue; - } - case ENC_STATS_AVG_BITRATE: { - double duration = pkt->dts * av_q2d(tb); - avio_printf(io, "%g", duration > 0 ? 8.0 * ost->data_size_enc / duration : -1.); - continue; - } - default: av_assert0(0); - } - } - } - avio_w8(io, '\n'); - avio_flush(io); -} - -static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame) -{ - AVCodecContext *enc = ost->enc_ctx; - AVPacket *pkt = ost->pkt; - const char *type_desc = av_get_media_type_string(enc->codec_type); - const char *action = frame ? "encode" : "flush"; - int ret; - - if (frame) { - if (ost->enc_stats_pre.io) - enc_stats_write(ost, &ost->enc_stats_pre, frame, NULL, - ost->frames_encoded); - - ost->frames_encoded++; - ost->samples_encoded += frame->nb_samples; - - if (debug_ts) { - av_log(ost, AV_LOG_INFO, "encoder <- type:%s " - "frame_pts:%s frame_pts_time:%s time_base:%d/%d\n", - type_desc, - av_ts2str(frame->pts), av_ts2timestr(frame->pts, &enc->time_base), - enc->time_base.num, enc->time_base.den); - } - } - - update_benchmark(NULL); - - ret = avcodec_send_frame(enc, frame); - if (ret < 0 && !(ret == AVERROR_EOF && !frame)) { - av_log(ost, AV_LOG_ERROR, "Error submitting %s frame to the encoder\n", - type_desc); - return ret; - } - - while (1) { - ret = avcodec_receive_packet(enc, pkt); - update_benchmark("%s_%s %d.%d", action, type_desc, - ost->file_index, ost->index); - - pkt->time_base = enc->time_base; - - /* if two pass, output log on success and EOF */ - if ((ret >= 0 || ret == AVERROR_EOF) && ost->logfile && enc->stats_out) - fprintf(ost->logfile, "%s", enc->stats_out); - - if (ret == AVERROR(EAGAIN)) { - av_assert0(frame); // should never happen during flushing - return 0; - } else if (ret == AVERROR_EOF) { - of_output_packet(of, pkt, ost, 1); - return ret; - } else if (ret < 0) { - av_log(ost, AV_LOG_ERROR, "%s encoding failed\n", type_desc); - return ret; - } - - if (enc->codec_type == AVMEDIA_TYPE_VIDEO) - update_video_stats(ost, pkt, !!vstats_filename); - if (ost->enc_stats_post.io) - enc_stats_write(ost, &ost->enc_stats_post, NULL, pkt, - ost->packets_encoded); - - if (debug_ts) { - av_log(ost, AV_LOG_INFO, "encoder -> type:%s " - "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s " - "duration:%s duration_time:%s\n", - type_desc, - av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base), - av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base), - av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &enc->time_base)); - } - - av_packet_rescale_ts(pkt, pkt->time_base, ost->mux_timebase); - pkt->time_base = ost->mux_timebase; - - if (debug_ts) { - av_log(ost, AV_LOG_INFO, "encoder -> type:%s " - "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s " - "duration:%s duration_time:%s\n", - type_desc, - av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base), - av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base), - av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &enc->time_base)); - } - - if ((ret = trigger_fix_sub_duration_heartbeat(ost, pkt)) < 0) { - av_log(NULL, AV_LOG_ERROR, - "Subtitle heartbeat logic failed in %s! (%s)\n", - __func__, av_err2str(ret)); - exit_program(1); - } - - ost->data_size_enc += pkt->size; - - ost->packets_encoded++; - - of_output_packet(of, pkt, ost, 0); - } - - av_assert0(0); -} - -static int submit_encode_frame(OutputFile *of, OutputStream *ost, - AVFrame *frame) -{ - int ret; - - if (ost->sq_idx_encode < 0) - return encode_frame(of, ost, frame); - - if (frame) { - ret = av_frame_ref(ost->sq_frame, frame); - if (ret < 0) - return ret; - frame = ost->sq_frame; - } - - ret = sq_send(of->sq_encode, ost->sq_idx_encode, - SQFRAME(frame)); - if (ret < 0) { - if (frame) - av_frame_unref(frame); - if (ret != AVERROR_EOF) - return ret; - } - - while (1) { - AVFrame *enc_frame = ost->sq_frame; - - ret = sq_receive(of->sq_encode, ost->sq_idx_encode, - SQFRAME(enc_frame)); - if (ret == AVERROR_EOF) { - enc_frame = NULL; - } else if (ret < 0) { - return (ret == AVERROR(EAGAIN)) ? 0 : ret; - } - - ret = encode_frame(of, ost, enc_frame); - if (enc_frame) - av_frame_unref(enc_frame); - if (ret < 0) { - if (ret == AVERROR_EOF) - close_output_stream(ost); - return ret; - } - } -} - -static void do_audio_out(OutputFile *of, OutputStream *ost, - AVFrame *frame) -{ - AVCodecContext *enc = ost->enc_ctx; - int ret; - - if (frame->pts == AV_NOPTS_VALUE) - frame->pts = ost->next_pts; - else { - int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time; - frame->pts = - av_rescale_q(frame->pts, frame->time_base, enc->time_base) - - av_rescale_q(start_time, AV_TIME_BASE_Q, enc->time_base); - } - frame->time_base = enc->time_base; - - if (!check_recording_time(ost, frame->pts, frame->time_base)) - return; - - ost->next_pts = frame->pts + frame->nb_samples; - - ret = submit_encode_frame(of, ost, frame); - if (ret < 0 && ret != AVERROR_EOF) - exit_program(1); -} - -static void do_subtitle_out(OutputFile *of, - OutputStream *ost, - AVSubtitle *sub) -{ - int subtitle_out_max_size = 1024 * 1024; - int subtitle_out_size, nb, i, ret; - AVCodecContext *enc; - AVPacket *pkt = ost->pkt; - int64_t pts; - - if (sub->pts == AV_NOPTS_VALUE) { - av_log(ost, AV_LOG_ERROR, "Subtitle packets must have a pts\n"); - if (exit_on_error) - exit_program(1); - return; - } - - enc = ost->enc_ctx; - - /* Note: DVB subtitle need one packet to draw them and one other - packet to clear them */ - /* XXX: signal it in the codec context ? */ - if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) - nb = 2; - else - nb = 1; - - /* shift timestamp to honor -ss and make check_recording_time() work with -t */ - pts = sub->pts; - if (output_files[ost->file_index]->start_time != AV_NOPTS_VALUE) - pts -= output_files[ost->file_index]->start_time; - for (i = 0; i < nb; i++) { - unsigned save_num_rects = sub->num_rects; - - if (!check_recording_time(ost, pts, AV_TIME_BASE_Q)) - return; - - ret = av_new_packet(pkt, subtitle_out_max_size); - if (ret < 0) - report_and_exit(AVERROR(ENOMEM)); - - sub->pts = pts; - // start_display_time is required to be 0 - sub->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q); - sub->end_display_time -= sub->start_display_time; - sub->start_display_time = 0; - if (i == 1) - sub->num_rects = 0; - - ost->frames_encoded++; - - subtitle_out_size = avcodec_encode_subtitle(enc, pkt->data, pkt->size, sub); - if (i == 1) - sub->num_rects = save_num_rects; - if (subtitle_out_size < 0) { - av_log(ost, AV_LOG_FATAL, "Subtitle encoding failed\n"); - exit_program(1); - } - - av_shrink_packet(pkt, subtitle_out_size); - pkt->time_base = ost->mux_timebase; - pkt->pts = av_rescale_q(sub->pts, AV_TIME_BASE_Q, pkt->time_base); - pkt->duration = av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, pkt->time_base); - if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) { - /* XXX: the pts correction is handled here. Maybe handling - it in the codec would be better */ - if (i == 0) - pkt->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, pkt->time_base); - else - pkt->pts += av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, pkt->time_base); - } - pkt->dts = pkt->pts; - - of_output_packet(of, pkt, ost, 0); - } -} - -/* Convert frame timestamps to the encoder timebase and decide how many times - * should this (and possibly previous) frame be repeated in order to conform to - * desired target framerate (if any). - */ -static void video_sync_process(OutputFile *of, OutputStream *ost, - AVFrame *next_picture, double duration, - int64_t *nb_frames, int64_t *nb_frames_prev) -{ - double delta0, delta; - - double sync_ipts = adjust_frame_pts_to_encoder_tb(of, ost, next_picture); - /* delta0 is the "drift" between the input frame (next_picture) and - * where it would fall in the output. */ - delta0 = sync_ipts - ost->next_pts; - delta = delta0 + duration; - - // tracks the number of times the PREVIOUS frame should be duplicated, - // mostly for variable framerate (VFR) - *nb_frames_prev = 0; - /* by default, we output a single frame */ - *nb_frames = 1; - - if (delta0 < 0 && - delta > 0 && - ost->vsync_method != VSYNC_PASSTHROUGH && - ost->vsync_method != VSYNC_DROP) { - if (delta0 < -0.6) { - av_log(ost, AV_LOG_VERBOSE, "Past duration %f too large\n", -delta0); - } else - av_log(ost, AV_LOG_DEBUG, "Clipping frame in rate conversion by %f\n", -delta0); - sync_ipts = ost->next_pts; - duration += delta0; - delta0 = 0; - } - - switch (ost->vsync_method) { - case VSYNC_VSCFR: - if (ost->vsync_frame_number == 0 && delta0 >= 0.5) { - av_log(ost, AV_LOG_DEBUG, "Not duplicating %d initial frames\n", (int)lrintf(delta0)); - delta = duration; - delta0 = 0; - ost->next_pts = llrint(sync_ipts); - } - case VSYNC_CFR: - // FIXME set to 0.5 after we fix some dts/pts bugs like in avidec.c - if (frame_drop_threshold && delta < frame_drop_threshold && ost->vsync_frame_number) { - *nb_frames = 0; - } else if (delta < -1.1) - *nb_frames = 0; - else if (delta > 1.1) { - *nb_frames = llrintf(delta); - if (delta0 > 1.1) - *nb_frames_prev = llrintf(delta0 - 0.6); - } - next_picture->duration = 1; - break; - case VSYNC_VFR: - if (delta <= -0.6) - *nb_frames = 0; - else if (delta > 0.6) - ost->next_pts = llrint(sync_ipts); - next_picture->duration = duration; - break; - case VSYNC_DROP: - case VSYNC_PASSTHROUGH: - next_picture->duration = duration; - ost->next_pts = llrint(sync_ipts); - break; - default: - av_assert0(0); - } -} - -static enum AVPictureType forced_kf_apply(void *logctx, KeyframeForceCtx *kf, - AVRational tb, const AVFrame *in_picture, - int dup_idx) -{ - double pts_time; - - if (kf->ref_pts == AV_NOPTS_VALUE) - kf->ref_pts = in_picture->pts; - - pts_time = (in_picture->pts - kf->ref_pts) * av_q2d(tb); - if (kf->index < kf->nb_pts && - av_compare_ts(in_picture->pts, tb, kf->pts[kf->index], AV_TIME_BASE_Q) >= 0) { - kf->index++; - goto force_keyframe; - } else if (kf->pexpr) { - double res; - kf->expr_const_values[FKF_T] = pts_time; - res = av_expr_eval(kf->pexpr, - kf->expr_const_values, NULL); - ff_dlog(NULL, "force_key_frame: n:%f n_forced:%f prev_forced_n:%f t:%f prev_forced_t:%f -> res:%f\n", - kf->expr_const_values[FKF_N], - kf->expr_const_values[FKF_N_FORCED], - kf->expr_const_values[FKF_PREV_FORCED_N], - kf->expr_const_values[FKF_T], - kf->expr_const_values[FKF_PREV_FORCED_T], - res); - - kf->expr_const_values[FKF_N] += 1; - - if (res) { - kf->expr_const_values[FKF_PREV_FORCED_N] = kf->expr_const_values[FKF_N] - 1; - kf->expr_const_values[FKF_PREV_FORCED_T] = kf->expr_const_values[FKF_T]; - kf->expr_const_values[FKF_N_FORCED] += 1; - goto force_keyframe; - } - } else if (kf->type == KF_FORCE_SOURCE && - in_picture->key_frame == 1 && !dup_idx) { - goto force_keyframe; - } else if (kf->type == KF_FORCE_SOURCE_NO_DROP && !dup_idx) { - kf->dropped_keyframe = 0; - if ((in_picture->key_frame == 1) || kf->dropped_keyframe) - goto force_keyframe; - } - - return AV_PICTURE_TYPE_NONE; - -force_keyframe: - av_log(logctx, AV_LOG_DEBUG, "Forced keyframe at time %f\n", pts_time); - return AV_PICTURE_TYPE_I; -} - -/* May modify/reset next_picture */ -static void do_video_out(OutputFile *of, - OutputStream *ost, - AVFrame *next_picture) -{ - int ret; - AVCodecContext *enc = ost->enc_ctx; - AVRational frame_rate; - int64_t nb_frames, nb_frames_prev, i; - double duration = 0; - InputStream *ist = ost->ist; - AVFilterContext *filter = ost->filter->filter; - - init_output_stream_wrapper(ost, next_picture, 1); - - frame_rate = av_buffersink_get_frame_rate(filter); - if (frame_rate.num > 0 && frame_rate.den > 0) - duration = 1/(av_q2d(frame_rate) * av_q2d(enc->time_base)); - - if(ist && ist->st->start_time != AV_NOPTS_VALUE && ist->first_dts != AV_NOPTS_VALUE && ost->frame_rate.num) - duration = FFMIN(duration, 1/(av_q2d(ost->frame_rate) * av_q2d(enc->time_base))); - - if (!ost->filters_script && - !ost->filters && - (nb_filtergraphs == 0 || !filtergraphs[0]->graph_desc) && - next_picture && - ist && - lrintf(next_picture->duration * av_q2d(ist->st->time_base) / av_q2d(enc->time_base)) > 0) { - duration = lrintf(next_picture->duration * av_q2d(ist->st->time_base) / av_q2d(enc->time_base)); - } - - if (!next_picture) { - //end, flushing - nb_frames_prev = nb_frames = mid_pred(ost->last_nb0_frames[0], - ost->last_nb0_frames[1], - ost->last_nb0_frames[2]); - } else { - video_sync_process(of, ost, next_picture, duration, - &nb_frames, &nb_frames_prev); - } - - memmove(ost->last_nb0_frames + 1, - ost->last_nb0_frames, - sizeof(ost->last_nb0_frames[0]) * (FF_ARRAY_ELEMS(ost->last_nb0_frames) - 1)); - ost->last_nb0_frames[0] = nb_frames_prev; - - if (nb_frames_prev == 0 && ost->last_dropped) { - nb_frames_drop++; - av_log(ost, AV_LOG_VERBOSE, - "*** dropping frame %"PRId64" at ts %"PRId64"\n", - ost->vsync_frame_number, ost->last_frame->pts); - } - if (nb_frames > (nb_frames_prev && ost->last_dropped) + (nb_frames > nb_frames_prev)) { - if (nb_frames > dts_error_threshold * 30) { - av_log(ost, AV_LOG_ERROR, "%"PRId64" frame duplication too large, skipping\n", nb_frames - 1); - nb_frames_drop++; - return; - } - nb_frames_dup += nb_frames - (nb_frames_prev && ost->last_dropped) - (nb_frames > nb_frames_prev); - av_log(ost, AV_LOG_VERBOSE, "*** %"PRId64" dup!\n", nb_frames - 1); - if (nb_frames_dup > dup_warning) { - av_log(ost, AV_LOG_WARNING, "More than %"PRIu64" frames duplicated\n", dup_warning); - dup_warning *= 10; - } - } - ost->last_dropped = nb_frames == nb_frames_prev && next_picture; - ost->kf.dropped_keyframe = ost->last_dropped && next_picture && next_picture->key_frame; - - /* duplicates frame if needed */ - for (i = 0; i < nb_frames; i++) { - AVFrame *in_picture; - - if (i < nb_frames_prev && ost->last_frame->buf[0]) { - in_picture = ost->last_frame; - } else - in_picture = next_picture; - - if (!in_picture) - return; - - in_picture->pts = ost->next_pts; - - if (!check_recording_time(ost, in_picture->pts, ost->enc_ctx->time_base)) - return; - - in_picture->quality = enc->global_quality; - in_picture->pict_type = forced_kf_apply(ost, &ost->kf, enc->time_base, in_picture, i); - - if (ost->top_field_first >= 0) - in_picture->top_field_first = !!ost->top_field_first; - - ret = submit_encode_frame(of, ost, in_picture); - if (ret == AVERROR_EOF) - break; - else if (ret < 0) - exit_program(1); - - ost->next_pts++; - ost->vsync_frame_number++; - } - - av_frame_unref(ost->last_frame); - if (next_picture) - av_frame_move_ref(ost->last_frame, next_picture); -} - -/** - * Get and encode new output from any of the filtergraphs, without causing - * activity. - * - * @return 0 for success, <0 for severe errors - */ -static int reap_filters(int flush) -{ - AVFrame *filtered_frame = NULL; - - /* Reap all buffers present in the buffer sinks */ - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - OutputFile *of = output_files[ost->file_index]; - AVFilterContext *filter; - AVCodecContext *enc = ost->enc_ctx; - int ret = 0; - - if (!ost->filter || !ost->filter->graph->graph) - continue; - filter = ost->filter->filter; - - /* - * Unlike video, with audio the audio frame size matters. - * Currently we are fully reliant on the lavfi filter chain to - * do the buffering deed for us, and thus the frame size parameter - * needs to be set accordingly. Where does one get the required - * frame size? From the initialized AVCodecContext of an audio - * encoder. Thus, if we have gotten to an audio stream, initialize - * the encoder earlier than receiving the first AVFrame. - */ - if (av_buffersink_get_type(filter) == AVMEDIA_TYPE_AUDIO) - init_output_stream_wrapper(ost, NULL, 1); - - filtered_frame = ost->filtered_frame; - - while (1) { - ret = av_buffersink_get_frame_flags(filter, filtered_frame, - AV_BUFFERSINK_FLAG_NO_REQUEST); - if (ret < 0) { - if (ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) { - av_log(NULL, AV_LOG_WARNING, - "Error in av_buffersink_get_frame_flags(): %s\n", av_err2str(ret)); - } else if (flush && ret == AVERROR_EOF) { - if (av_buffersink_get_type(filter) == AVMEDIA_TYPE_VIDEO) - do_video_out(of, ost, NULL); - } - break; - } - if (ost->finished) { - av_frame_unref(filtered_frame); - continue; - } - - if (filtered_frame->pts != AV_NOPTS_VALUE) { - AVRational tb = av_buffersink_get_time_base(filter); - ost->last_filter_pts = av_rescale_q(filtered_frame->pts, tb, - AV_TIME_BASE_Q); - filtered_frame->time_base = tb; - - if (debug_ts) - av_log(NULL, AV_LOG_INFO, "filter_raw -> pts:%s pts_time:%s time_base:%d/%d\n", - av_ts2str(filtered_frame->pts), - av_ts2timestr(filtered_frame->pts, &tb), - tb.num, tb.den); - } - - switch (av_buffersink_get_type(filter)) { - case AVMEDIA_TYPE_VIDEO: - if (!ost->frame_aspect_ratio.num) - enc->sample_aspect_ratio = filtered_frame->sample_aspect_ratio; - - do_video_out(of, ost, filtered_frame); - break; - case AVMEDIA_TYPE_AUDIO: - if (!(enc->codec->capabilities & AV_CODEC_CAP_PARAM_CHANGE) && - enc->ch_layout.nb_channels != filtered_frame->ch_layout.nb_channels) { - av_log(NULL, AV_LOG_ERROR, - "Audio filter graph output is not normalized and encoder does not support parameter changes\n"); - break; - } - do_audio_out(of, ost, filtered_frame); - break; - default: - // TODO support subtitle filters - av_assert0(0); - } - - av_frame_unref(filtered_frame); - } - } - - return 0; -} - -static void print_final_stats(int64_t total_size) -{ - uint64_t video_size = 0, audio_size = 0, extra_size = 0, other_size = 0; - uint64_t subtitle_size = 0; - uint64_t data_size = 0; - float percent = -1.0; - int i, j; - int pass1_used = 1; - - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - AVCodecParameters *par = ost->st->codecpar; - const uint64_t s = ost->data_size_mux; - - switch (par->codec_type) { - case AVMEDIA_TYPE_VIDEO: video_size += s; break; - case AVMEDIA_TYPE_AUDIO: audio_size += s; break; - case AVMEDIA_TYPE_SUBTITLE: subtitle_size += s; break; - default: other_size += s; break; - } - extra_size += par->extradata_size; - data_size += s; - if (ost->enc_ctx && - (ost->enc_ctx->flags & (AV_CODEC_FLAG_PASS1 | AV_CODEC_FLAG_PASS2)) - != AV_CODEC_FLAG_PASS1) - pass1_used = 0; - } - - if (data_size && total_size>0 && total_size >= data_size) - percent = 100.0 * (total_size - data_size) / data_size; - - av_log(NULL, AV_LOG_INFO, "video:%1.0fkB audio:%1.0fkB subtitle:%1.0fkB other streams:%1.0fkB global headers:%1.0fkB muxing overhead: ", - video_size / 1024.0, - audio_size / 1024.0, - subtitle_size / 1024.0, - other_size / 1024.0, - extra_size / 1024.0); - if (percent >= 0.0) - av_log(NULL, AV_LOG_INFO, "%f%%", percent); - else - av_log(NULL, AV_LOG_INFO, "unknown"); - av_log(NULL, AV_LOG_INFO, "\n"); - - /* print verbose per-stream stats */ - for (i = 0; i < nb_input_files; i++) { - InputFile *f = input_files[i]; - uint64_t total_packets = 0, total_size = 0; - - av_log(NULL, AV_LOG_VERBOSE, "Input file #%d (%s):\n", - i, f->ctx->url); - - for (j = 0; j < f->nb_streams; j++) { - InputStream *ist = f->streams[j]; - enum AVMediaType type = ist->par->codec_type; - - total_size += ist->data_size; - total_packets += ist->nb_packets; - - av_log(NULL, AV_LOG_VERBOSE, " Input stream #%d:%d (%s): ", - i, j, av_get_media_type_string(type)); - av_log(NULL, AV_LOG_VERBOSE, "%"PRIu64" packets read (%"PRIu64" bytes); ", - ist->nb_packets, ist->data_size); - - if (ist->decoding_needed) { - av_log(NULL, AV_LOG_VERBOSE, "%"PRIu64" frames decoded", - ist->frames_decoded); - if (type == AVMEDIA_TYPE_AUDIO) - av_log(NULL, AV_LOG_VERBOSE, " (%"PRIu64" samples)", ist->samples_decoded); - av_log(NULL, AV_LOG_VERBOSE, "; "); - } - - av_log(NULL, AV_LOG_VERBOSE, "\n"); - } - - av_log(NULL, AV_LOG_VERBOSE, " Total: %"PRIu64" packets (%"PRIu64" bytes) demuxed\n", - total_packets, total_size); - } - - for (i = 0; i < nb_output_files; i++) { - OutputFile *of = output_files[i]; - uint64_t total_packets = 0, total_size = 0; - - av_log(NULL, AV_LOG_VERBOSE, "Output file #%d (%s):\n", - i, of->url); - - for (j = 0; j < of->nb_streams; j++) { - OutputStream *ost = of->streams[j]; - enum AVMediaType type = ost->st->codecpar->codec_type; - - total_size += ost->data_size_mux; - total_packets += atomic_load(&ost->packets_written); - - av_log(NULL, AV_LOG_VERBOSE, " Output stream #%d:%d (%s): ", - i, j, av_get_media_type_string(type)); - if (ost->enc_ctx) { - av_log(NULL, AV_LOG_VERBOSE, "%"PRIu64" frames encoded", - ost->frames_encoded); - if (type == AVMEDIA_TYPE_AUDIO) - av_log(NULL, AV_LOG_VERBOSE, " (%"PRIu64" samples)", ost->samples_encoded); - av_log(NULL, AV_LOG_VERBOSE, "; "); - } - - av_log(NULL, AV_LOG_VERBOSE, "%"PRIu64" packets muxed (%"PRIu64" bytes); ", - atomic_load(&ost->packets_written), ost->data_size_mux); - - av_log(NULL, AV_LOG_VERBOSE, "\n"); - } - - av_log(NULL, AV_LOG_VERBOSE, " Total: %"PRIu64" packets (%"PRIu64" bytes) muxed\n", - total_packets, total_size); - } - if(video_size + data_size + audio_size + subtitle_size + extra_size == 0){ - av_log(NULL, AV_LOG_WARNING, "Output file is empty, nothing was encoded "); - if (pass1_used) { - av_log(NULL, AV_LOG_WARNING, "\n"); - } else { - av_log(NULL, AV_LOG_WARNING, "(check -ss / -t / -frames parameters if used)\n"); - } - } -} - -static void print_report(int is_last_report, int64_t timer_start, int64_t cur_time) -{ - AVBPrint buf, buf_script; - int64_t total_size = of_filesize(output_files[0]); - int vid; - double bitrate; - double speed; - int64_t pts = INT64_MIN + 1; - static int64_t last_time = -1; - static int first_report = 1; - static int qp_histogram[52]; - int hours, mins, secs, us; - const char *hours_sign; - int ret; - float t; - - if (!print_stats && !is_last_report && !progress_avio) - return; - - if (!is_last_report) { - if (last_time == -1) { - last_time = cur_time; - } - if (((cur_time - last_time) < stats_period && !first_report) || - (first_report && nb_output_dumped < nb_output_files)) - return; - last_time = cur_time; - } - - t = (cur_time-timer_start) / 1000000.0; - - vid = 0; - av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); - av_bprint_init(&buf_script, 0, AV_BPRINT_SIZE_AUTOMATIC); - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - const AVCodecContext * const enc = ost->enc_ctx; - const float q = enc ? ost->quality / (float) FF_QP2LAMBDA : -1; - - if (vid && ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - av_bprintf(&buf, "q=%2.1f ", q); - av_bprintf(&buf_script, "stream_%d_%d_q=%.1f\n", - ost->file_index, ost->index, q); - } - if (!vid && ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - float fps; - uint64_t frame_number = atomic_load(&ost->packets_written); - - fps = t > 1 ? frame_number / t : 0; - av_bprintf(&buf, "frame=%5"PRId64" fps=%3.*f q=%3.1f ", - frame_number, fps < 9.95, fps, q); - av_bprintf(&buf_script, "frame=%"PRId64"\n", frame_number); - av_bprintf(&buf_script, "fps=%.2f\n", fps); - av_bprintf(&buf_script, "stream_%d_%d_q=%.1f\n", - ost->file_index, ost->index, q); - if (is_last_report) - av_bprintf(&buf, "L"); - if (qp_hist) { - int j; - int qp = lrintf(q); - if (qp >= 0 && qp < FF_ARRAY_ELEMS(qp_histogram)) - qp_histogram[qp]++; - for (j = 0; j < 32; j++) - av_bprintf(&buf, "%X", av_log2(qp_histogram[j] + 1)); - } - - if (enc && (enc->flags & AV_CODEC_FLAG_PSNR) && - (ost->pict_type != AV_PICTURE_TYPE_NONE || is_last_report)) { - int j; - double error, error_sum = 0; - double scale, scale_sum = 0; - double p; - char type[3] = { 'Y','U','V' }; - av_bprintf(&buf, "PSNR="); - for (j = 0; j < 3; j++) { - if (is_last_report) { - error = enc->error[j]; - scale = enc->width * enc->height * 255.0 * 255.0 * frame_number; - } else { - error = ost->error[j]; - scale = enc->width * enc->height * 255.0 * 255.0; - } - if (j) - scale /= 4; - error_sum += error; - scale_sum += scale; - p = psnr(error / scale); - av_bprintf(&buf, "%c:%2.2f ", type[j], p); - av_bprintf(&buf_script, "stream_%d_%d_psnr_%c=%2.2f\n", - ost->file_index, ost->index, type[j] | 32, p); - } - p = psnr(error_sum / scale_sum); - av_bprintf(&buf, "*:%2.2f ", psnr(error_sum / scale_sum)); - av_bprintf(&buf_script, "stream_%d_%d_psnr_all=%2.2f\n", - ost->file_index, ost->index, p); - } - vid = 1; - } - /* compute min output value */ - if (ost->last_mux_dts != AV_NOPTS_VALUE) { - pts = FFMAX(pts, ost->last_mux_dts); - if (copy_ts) { - if (copy_ts_first_pts == AV_NOPTS_VALUE && pts > 1) - copy_ts_first_pts = pts; - if (copy_ts_first_pts != AV_NOPTS_VALUE) - pts -= copy_ts_first_pts; - } - } - - if (is_last_report) - nb_frames_drop += ost->last_dropped; - } - - secs = FFABS(pts) / AV_TIME_BASE; - us = FFABS(pts) % AV_TIME_BASE; - mins = secs / 60; - secs %= 60; - hours = mins / 60; - mins %= 60; - hours_sign = (pts < 0) ? "-" : ""; - - bitrate = pts && total_size >= 0 ? total_size * 8 / (pts / 1000.0) : -1; - speed = t != 0.0 ? (double)pts / AV_TIME_BASE / t : -1; - - if (total_size < 0) av_bprintf(&buf, "size=N/A time="); - else av_bprintf(&buf, "size=%8.0fkB time=", total_size / 1024.0); - if (pts == AV_NOPTS_VALUE) { - av_bprintf(&buf, "N/A "); - } else { - av_bprintf(&buf, "%s%02d:%02d:%02d.%02d ", - hours_sign, hours, mins, secs, (100 * us) / AV_TIME_BASE); - } - - if (bitrate < 0) { - av_bprintf(&buf, "bitrate=N/A"); - av_bprintf(&buf_script, "bitrate=N/A\n"); - }else{ - av_bprintf(&buf, "bitrate=%6.1fkbits/s", bitrate); - av_bprintf(&buf_script, "bitrate=%6.1fkbits/s\n", bitrate); - } - - if (total_size < 0) av_bprintf(&buf_script, "total_size=N/A\n"); - else av_bprintf(&buf_script, "total_size=%"PRId64"\n", total_size); - if (pts == AV_NOPTS_VALUE) { - av_bprintf(&buf_script, "out_time_us=N/A\n"); - av_bprintf(&buf_script, "out_time_ms=N/A\n"); - av_bprintf(&buf_script, "out_time=N/A\n"); - } else { - av_bprintf(&buf_script, "out_time_us=%"PRId64"\n", pts); - av_bprintf(&buf_script, "out_time_ms=%"PRId64"\n", pts); - av_bprintf(&buf_script, "out_time=%s%02d:%02d:%02d.%06d\n", - hours_sign, hours, mins, secs, us); - } - - if (nb_frames_dup || nb_frames_drop) - av_bprintf(&buf, " dup=%"PRId64" drop=%"PRId64, nb_frames_dup, nb_frames_drop); - av_bprintf(&buf_script, "dup_frames=%"PRId64"\n", nb_frames_dup); - av_bprintf(&buf_script, "drop_frames=%"PRId64"\n", nb_frames_drop); - - if (speed < 0) { - av_bprintf(&buf, " speed=N/A"); - av_bprintf(&buf_script, "speed=N/A\n"); - } else { - av_bprintf(&buf, " speed=%4.3gx", speed); - av_bprintf(&buf_script, "speed=%4.3gx\n", speed); - } - - if (print_stats || is_last_report) { - const char end = is_last_report ? '\n' : '\r'; - if (print_stats==1 && AV_LOG_INFO > av_log_get_level()) { - fprintf(stderr, "%s %c", buf.str, end); - } else - av_log(NULL, AV_LOG_INFO, "%s %c", buf.str, end); - - fflush(stderr); - } - av_bprint_finalize(&buf, NULL); - - if (progress_avio) { - av_bprintf(&buf_script, "progress=%s\n", - is_last_report ? "end" : "continue"); - avio_write(progress_avio, buf_script.str, - FFMIN(buf_script.len, buf_script.size - 1)); - avio_flush(progress_avio); - av_bprint_finalize(&buf_script, NULL); - if (is_last_report) { - if ((ret = avio_closep(&progress_avio)) < 0) - av_log(NULL, AV_LOG_ERROR, - "Error closing progress log, loss of information possible: %s\n", av_err2str(ret)); - } - } - - first_report = 0; - - if (is_last_report) - print_final_stats(total_size); -} - -static int ifilter_parameters_from_codecpar(InputFilter *ifilter, AVCodecParameters *par) -{ - int ret; - - // We never got any input. Set a fake format, which will - // come from libavformat. - ifilter->format = par->format; - ifilter->sample_rate = par->sample_rate; - ifilter->width = par->width; - ifilter->height = par->height; - ifilter->sample_aspect_ratio = par->sample_aspect_ratio; - ret = av_channel_layout_copy(&ifilter->ch_layout, &par->ch_layout); - if (ret < 0) - return ret; - - return 0; -} - -static void flush_encoders(void) -{ - int ret; - - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - OutputFile *of = output_files[ost->file_index]; - if (ost->sq_idx_encode >= 0) - sq_send(of->sq_encode, ost->sq_idx_encode, SQFRAME(NULL)); - } - - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - AVCodecContext *enc = ost->enc_ctx; - OutputFile *of = output_files[ost->file_index]; - - if (!enc) - continue; - - // Try to enable encoding with no input frames. - // Maybe we should just let encoding fail instead. - if (!ost->initialized) { - FilterGraph *fg = ost->filter->graph; - - av_log(ost, AV_LOG_WARNING, - "Finishing stream without any data written to it.\n"); - - if (ost->filter && !fg->graph) { - int x; - for (x = 0; x < fg->nb_inputs; x++) { - InputFilter *ifilter = fg->inputs[x]; - if (ifilter->format < 0 && - ifilter_parameters_from_codecpar(ifilter, ifilter->ist->par) < 0) { - av_log(ost, AV_LOG_ERROR, "Error copying paramerets from input stream\n"); - exit_program(1); - } - } - - if (!ifilter_has_all_input_formats(fg)) - continue; - - ret = configure_filtergraph(fg); - if (ret < 0) { - av_log(ost, AV_LOG_ERROR, "Error configuring filter graph\n"); - exit_program(1); - } - - of_output_packet(of, ost->pkt, ost, 1); - } - - init_output_stream_wrapper(ost, NULL, 1); - } - - if (enc->codec_type != AVMEDIA_TYPE_VIDEO && enc->codec_type != AVMEDIA_TYPE_AUDIO) - continue; - - ret = submit_encode_frame(of, ost, NULL); - if (ret != AVERROR_EOF) - exit_program(1); - } -} - -/* - * Check whether a packet from ist should be written into ost at this time - */ -static int check_output_constraints(InputStream *ist, OutputStream *ost) -{ - OutputFile *of = output_files[ost->file_index]; - - if (ost->ist != ist) - return 0; - - if (ost->finished & MUXER_FINISHED) - return 0; - - if (of->start_time != AV_NOPTS_VALUE && ist->pts < of->start_time) - return 0; - - return 1; -} - -static void do_streamcopy(InputStream *ist, OutputStream *ost, const AVPacket *pkt) -{ - OutputFile *of = output_files[ost->file_index]; - InputFile *f = input_files [ist->file_index]; - int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time; - int64_t ost_tb_start_time = av_rescale_q(start_time, AV_TIME_BASE_Q, ost->mux_timebase); - AVPacket *opkt = ost->pkt; - - av_packet_unref(opkt); - // EOF: flush output bitstream filters. - if (!pkt) { - of_output_packet(of, opkt, ost, 1); - return; - } - - if (!ost->streamcopy_started && !(pkt->flags & AV_PKT_FLAG_KEY) && - !ost->copy_initial_nonkeyframes) - return; - - if (!ost->streamcopy_started && !ost->copy_prior_start) { - if (pkt->pts == AV_NOPTS_VALUE ? - ist->pts < ost->ts_copy_start : - pkt->pts < av_rescale_q(ost->ts_copy_start, AV_TIME_BASE_Q, ist->st->time_base)) - return; - } - - if (of->recording_time != INT64_MAX && - ist->pts >= of->recording_time + start_time) { - close_output_stream(ost); - return; - } - - if (f->recording_time != INT64_MAX) { - start_time = 0; - if (copy_ts) { - start_time += f->start_time != AV_NOPTS_VALUE ? f->start_time : 0; - start_time += start_at_zero ? 0 : f->start_time_effective; - } - if (ist->pts >= f->recording_time + start_time) { - close_output_stream(ost); - return; - } - } - - if (av_packet_ref(opkt, pkt) < 0) - exit_program(1); - - opkt->time_base = ost->mux_timebase; - - if (pkt->pts != AV_NOPTS_VALUE) - opkt->pts = av_rescale_q(pkt->pts, ist->st->time_base, opkt->time_base) - ost_tb_start_time; - - if (pkt->dts == AV_NOPTS_VALUE) { - opkt->dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, opkt->time_base); - } else if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - int duration = av_get_audio_frame_duration2(ist->par, pkt->size); - if(!duration) - duration = ist->par->frame_size; - opkt->dts = av_rescale_delta(ist->st->time_base, pkt->dts, - (AVRational){1, ist->par->sample_rate}, duration, - &ist->filter_in_rescale_delta_last, opkt->time_base); - /* dts will be set immediately afterwards to what pts is now */ - opkt->pts = opkt->dts - ost_tb_start_time; - } else - opkt->dts = av_rescale_q(pkt->dts, ist->st->time_base, opkt->time_base); - opkt->dts -= ost_tb_start_time; - - opkt->duration = av_rescale_q(pkt->duration, ist->st->time_base, opkt->time_base); - - { - int ret = trigger_fix_sub_duration_heartbeat(ost, pkt); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, - "Subtitle heartbeat logic failed in %s! (%s)\n", - __func__, av_err2str(ret)); - exit_program(1); - } - } - - of_output_packet(of, opkt, ost, 0); - - ost->streamcopy_started = 1; -} - -static void check_decode_result(InputStream *ist, int *got_output, int ret) -{ - if (*got_output || ret<0) - decode_error_stat[ret<0] ++; - - if (ret < 0 && exit_on_error) - exit_program(1); - - if (*got_output && ist) { - if (ist->decoded_frame->decode_error_flags || (ist->decoded_frame->flags & AV_FRAME_FLAG_CORRUPT)) { - av_log(NULL, exit_on_error ? AV_LOG_FATAL : AV_LOG_WARNING, - "%s: corrupt decoded frame in stream %d\n", input_files[ist->file_index]->ctx->url, ist->st->index); - if (exit_on_error) - exit_program(1); - } - } -} - -// Filters can be configured only if the formats of all inputs are known. -static int ifilter_has_all_input_formats(FilterGraph *fg) -{ - int i; - for (i = 0; i < fg->nb_inputs; i++) { - if (fg->inputs[i]->format < 0 && (fg->inputs[i]->type == AVMEDIA_TYPE_AUDIO || - fg->inputs[i]->type == AVMEDIA_TYPE_VIDEO)) - return 0; - } - return 1; -} - -static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame, int keep_reference) -{ - FilterGraph *fg = ifilter->graph; - AVFrameSideData *sd; - int need_reinit, ret; - int buffersrc_flags = AV_BUFFERSRC_FLAG_PUSH; - - if (keep_reference) - buffersrc_flags |= AV_BUFFERSRC_FLAG_KEEP_REF; - - /* determine if the parameters for this input changed */ - need_reinit = ifilter->format != frame->format; - - switch (ifilter->ist->par->codec_type) { - case AVMEDIA_TYPE_AUDIO: - need_reinit |= ifilter->sample_rate != frame->sample_rate || - av_channel_layout_compare(&ifilter->ch_layout, &frame->ch_layout); - break; - case AVMEDIA_TYPE_VIDEO: - need_reinit |= ifilter->width != frame->width || - ifilter->height != frame->height; - break; - } - - if (!ifilter->ist->reinit_filters && fg->graph) - need_reinit = 0; - - if (!!ifilter->hw_frames_ctx != !!frame->hw_frames_ctx || - (ifilter->hw_frames_ctx && ifilter->hw_frames_ctx->data != frame->hw_frames_ctx->data)) - need_reinit = 1; - - if (sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX)) { - if (!ifilter->displaymatrix || memcmp(sd->data, ifilter->displaymatrix, sizeof(int32_t) * 9)) - need_reinit = 1; - } else if (ifilter->displaymatrix) - need_reinit = 1; - - if (need_reinit) { - ret = ifilter_parameters_from_frame(ifilter, frame); - if (ret < 0) - return ret; - } - - /* (re)init the graph if possible, otherwise buffer the frame and return */ - if (need_reinit || !fg->graph) { - if (!ifilter_has_all_input_formats(fg)) { - AVFrame *tmp = av_frame_clone(frame); - if (!tmp) - return AVERROR(ENOMEM); - - ret = av_fifo_write(ifilter->frame_queue, &tmp, 1); - if (ret < 0) - av_frame_free(&tmp); - - return ret; - } - - ret = reap_filters(1); - if (ret < 0 && ret != AVERROR_EOF) { - av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", av_err2str(ret)); - return ret; - } - - ret = configure_filtergraph(fg); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error reinitializing filters!\n"); - return ret; - } - } - - ret = av_buffersrc_add_frame_flags(ifilter->filter, frame, buffersrc_flags); - if (ret < 0) { - if (ret != AVERROR_EOF) - av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", av_err2str(ret)); - return ret; - } - - return 0; -} - -static int ifilter_send_eof(InputFilter *ifilter, int64_t pts) -{ - int ret; - - ifilter->eof = 1; - - if (ifilter->filter) { - ret = av_buffersrc_close(ifilter->filter, pts, AV_BUFFERSRC_FLAG_PUSH); - if (ret < 0) - return ret; - } else { - // the filtergraph was never configured - if (ifilter->format < 0) { - ret = ifilter_parameters_from_codecpar(ifilter, ifilter->ist->par); - if (ret < 0) - return ret; - } - if (ifilter->format < 0 && (ifilter->type == AVMEDIA_TYPE_AUDIO || ifilter->type == AVMEDIA_TYPE_VIDEO)) { - av_log(NULL, AV_LOG_ERROR, "Cannot determine format of input stream %d:%d after EOF\n", ifilter->ist->file_index, ifilter->ist->st->index); - return AVERROR_INVALIDDATA; - } - } - - return 0; -} - -// This does not quite work like avcodec_decode_audio4/avcodec_decode_video2. -// There is the following difference: if you got a frame, you must call -// it again with pkt=NULL. pkt==NULL is treated differently from pkt->size==0 -// (pkt==NULL means get more output, pkt->size==0 is a flush/drain packet) -static int decode(InputStream *ist, AVCodecContext *avctx, - AVFrame *frame, int *got_frame, AVPacket *pkt) -{ - int ret; - - *got_frame = 0; - - if (pkt) { - ret = avcodec_send_packet(avctx, pkt); - // In particular, we don't expect AVERROR(EAGAIN), because we read all - // decoded frames with avcodec_receive_frame() until done. - if (ret < 0 && ret != AVERROR_EOF) - return ret; - } - - ret = avcodec_receive_frame(avctx, frame); - if (ret < 0 && ret != AVERROR(EAGAIN)) - return ret; - if (ret >= 0) { - if (ist->want_frame_data) { - FrameData *fd; - - av_assert0(!frame->opaque_ref); - frame->opaque_ref = av_buffer_allocz(sizeof(*fd)); - if (!frame->opaque_ref) { - av_frame_unref(frame); - return AVERROR(ENOMEM); - } - fd = (FrameData*)frame->opaque_ref->data; - fd->pts = frame->pts; - fd->tb = avctx->pkt_timebase; - fd->idx = avctx->frame_num - 1; - } - - *got_frame = 1; - } - - return 0; -} - -static int send_frame_to_filters(InputStream *ist, AVFrame *decoded_frame) -{ - int i, ret; - - av_assert1(ist->nb_filters > 0); /* ensure ret is initialized */ - for (i = 0; i < ist->nb_filters; i++) { - ret = ifilter_send_frame(ist->filters[i], decoded_frame, i < ist->nb_filters - 1); - if (ret == AVERROR_EOF) - ret = 0; /* ignore */ - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, - "Failed to inject frame into filter network: %s\n", av_err2str(ret)); - break; - } - } - return ret; -} - -static int decode_audio(InputStream *ist, AVPacket *pkt, int *got_output, - int *decode_failed) -{ - AVFrame *decoded_frame = ist->decoded_frame; - AVCodecContext *avctx = ist->dec_ctx; - int ret, err = 0; - AVRational decoded_frame_tb; - - update_benchmark(NULL); - ret = decode(ist, avctx, decoded_frame, got_output, pkt); - update_benchmark("decode_audio %d.%d", ist->file_index, ist->st->index); - if (ret < 0) - *decode_failed = 1; - - if (ret != AVERROR_EOF) - check_decode_result(ist, got_output, ret); - - if (!*got_output || ret < 0) - return ret; - - ist->samples_decoded += decoded_frame->nb_samples; - ist->frames_decoded++; - - /* increment next_dts to use for the case where the input stream does not - have timestamps or there are multiple frames in the packet */ - ist->next_pts += ((int64_t)AV_TIME_BASE * decoded_frame->nb_samples) / - decoded_frame->sample_rate; - ist->next_dts += ((int64_t)AV_TIME_BASE * decoded_frame->nb_samples) / - decoded_frame->sample_rate; - - if (decoded_frame->pts != AV_NOPTS_VALUE) { - decoded_frame_tb = ist->st->time_base; - } else if (pkt && pkt->pts != AV_NOPTS_VALUE) { - decoded_frame->pts = pkt->pts; - decoded_frame_tb = ist->st->time_base; - }else { - decoded_frame->pts = ist->dts; - decoded_frame_tb = AV_TIME_BASE_Q; - } - if (pkt && pkt->duration && ist->prev_pkt_pts != AV_NOPTS_VALUE && - pkt->pts != AV_NOPTS_VALUE && pkt->pts - ist->prev_pkt_pts > pkt->duration) - ist->filter_in_rescale_delta_last = AV_NOPTS_VALUE; - if (pkt) - ist->prev_pkt_pts = pkt->pts; - if (decoded_frame->pts != AV_NOPTS_VALUE) - decoded_frame->pts = av_rescale_delta(decoded_frame_tb, decoded_frame->pts, - (AVRational){1, decoded_frame->sample_rate}, - decoded_frame->nb_samples, - &ist->filter_in_rescale_delta_last, - (AVRational){1, decoded_frame->sample_rate}); - ist->nb_samples = decoded_frame->nb_samples; - err = send_frame_to_filters(ist, decoded_frame); - - av_frame_unref(decoded_frame); - return err < 0 ? err : ret; -} - -static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output, int64_t *duration_pts, int eof, - int *decode_failed) -{ - AVFrame *decoded_frame = ist->decoded_frame; - int i, ret = 0, err = 0; - int64_t best_effort_timestamp; - int64_t dts = AV_NOPTS_VALUE; - - // With fate-indeo3-2, we're getting 0-sized packets before EOF for some - // reason. This seems like a semi-critical bug. Don't trigger EOF, and - // skip the packet. - if (!eof && pkt && pkt->size == 0) - return 0; - - if (ist->dts != AV_NOPTS_VALUE) - dts = av_rescale_q(ist->dts, AV_TIME_BASE_Q, ist->st->time_base); - if (pkt) { - pkt->dts = dts; // ffmpeg.c probably shouldn't do this - } - - // The old code used to set dts on the drain packet, which does not work - // with the new API anymore. - if (eof) { - void *new = av_realloc_array(ist->dts_buffer, ist->nb_dts_buffer + 1, sizeof(ist->dts_buffer[0])); - if (!new) - return AVERROR(ENOMEM); - ist->dts_buffer = new; - ist->dts_buffer[ist->nb_dts_buffer++] = dts; - } - - update_benchmark(NULL); - ret = decode(ist, ist->dec_ctx, decoded_frame, got_output, pkt); - update_benchmark("decode_video %d.%d", ist->file_index, ist->st->index); - if (ret < 0) - *decode_failed = 1; - - // The following line may be required in some cases where there is no parser - // or the parser does not has_b_frames correctly - if (ist->par->video_delay < ist->dec_ctx->has_b_frames) { - if (ist->dec_ctx->codec_id == AV_CODEC_ID_H264) { - ist->par->video_delay = ist->dec_ctx->has_b_frames; - } else - av_log(ist->dec_ctx, AV_LOG_WARNING, - "video_delay is larger in decoder than demuxer %d > %d.\n" - "If you want to help, upload a sample " - "of this file to https://streams.videolan.org/upload/ " - "and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)\n", - ist->dec_ctx->has_b_frames, - ist->par->video_delay); - } - - if (ret != AVERROR_EOF) - check_decode_result(ist, got_output, ret); - - if (*got_output && ret >= 0) { - if (ist->dec_ctx->width != decoded_frame->width || - ist->dec_ctx->height != decoded_frame->height || - ist->dec_ctx->pix_fmt != decoded_frame->format) { - av_log(NULL, AV_LOG_DEBUG, "Frame parameters mismatch context %d,%d,%d != %d,%d,%d\n", - decoded_frame->width, - decoded_frame->height, - decoded_frame->format, - ist->dec_ctx->width, - ist->dec_ctx->height, - ist->dec_ctx->pix_fmt); - } - } - - if (!*got_output || ret < 0) - return ret; - - if(ist->top_field_first>=0) - decoded_frame->top_field_first = ist->top_field_first; - - ist->frames_decoded++; - - if (ist->hwaccel_retrieve_data && decoded_frame->format == ist->hwaccel_pix_fmt) { - err = ist->hwaccel_retrieve_data(ist->dec_ctx, decoded_frame); - if (err < 0) - goto fail; - } - - best_effort_timestamp= decoded_frame->best_effort_timestamp; - *duration_pts = decoded_frame->duration; - - if (ist->framerate.num) - best_effort_timestamp = ist->cfr_next_pts++; - - if (eof && best_effort_timestamp == AV_NOPTS_VALUE && ist->nb_dts_buffer > 0) { - best_effort_timestamp = ist->dts_buffer[0]; - - for (i = 0; i < ist->nb_dts_buffer - 1; i++) - ist->dts_buffer[i] = ist->dts_buffer[i + 1]; - ist->nb_dts_buffer--; - } - - if(best_effort_timestamp != AV_NOPTS_VALUE) { - int64_t ts = av_rescale_q(decoded_frame->pts = best_effort_timestamp, ist->st->time_base, AV_TIME_BASE_Q); - - if (ts != AV_NOPTS_VALUE) - ist->next_pts = ist->pts = ts; - } - - if (debug_ts) { - av_log(NULL, AV_LOG_INFO, "decoder -> ist_index:%d type:video " - "frame_pts:%s frame_pts_time:%s best_effort_ts:%"PRId64" best_effort_ts_time:%s keyframe:%d frame_type:%d time_base:%d/%d\n", - ist->st->index, av_ts2str(decoded_frame->pts), - av_ts2timestr(decoded_frame->pts, &ist->st->time_base), - best_effort_timestamp, - av_ts2timestr(best_effort_timestamp, &ist->st->time_base), - decoded_frame->key_frame, decoded_frame->pict_type, - ist->st->time_base.num, ist->st->time_base.den); - } - - if (ist->st->sample_aspect_ratio.num) - decoded_frame->sample_aspect_ratio = ist->st->sample_aspect_ratio; - - err = send_frame_to_filters(ist, decoded_frame); - -fail: - av_frame_unref(decoded_frame); - return err < 0 ? err : ret; -} - -static int process_subtitle(InputStream *ist, AVSubtitle *subtitle, int *got_output) -{ - int ret = 0; - int free_sub = 1; - - if (ist->fix_sub_duration) { - int end = 1; - if (ist->prev_sub.got_output) { - end = av_rescale(subtitle->pts - ist->prev_sub.subtitle.pts, - 1000, AV_TIME_BASE); - if (end < ist->prev_sub.subtitle.end_display_time) { - av_log(NULL, AV_LOG_DEBUG, - "Subtitle duration reduced from %"PRId32" to %d%s\n", - ist->prev_sub.subtitle.end_display_time, end, - end <= 0 ? ", dropping it" : ""); - ist->prev_sub.subtitle.end_display_time = end; - } - } - FFSWAP(int, *got_output, ist->prev_sub.got_output); - FFSWAP(int, ret, ist->prev_sub.ret); - FFSWAP(AVSubtitle, *subtitle, ist->prev_sub.subtitle); - if (end <= 0) - goto out; - } - - if (!*got_output) - return ret; - - if (ist->sub2video.frame) { - sub2video_update(ist, INT64_MIN, subtitle); - } else if (ist->nb_filters) { - if (!ist->sub2video.sub_queue) - ist->sub2video.sub_queue = av_fifo_alloc2(8, sizeof(AVSubtitle), AV_FIFO_FLAG_AUTO_GROW); - if (!ist->sub2video.sub_queue) - report_and_exit(AVERROR(ENOMEM)); - - ret = av_fifo_write(ist->sub2video.sub_queue, subtitle, 1); - if (ret < 0) - exit_program(1); - free_sub = 0; - } - - if (!subtitle->num_rects) - goto out; - - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - if (!check_output_constraints(ist, ost) || !ost->enc_ctx - || ost->enc_ctx->codec_type != AVMEDIA_TYPE_SUBTITLE) - continue; - - do_subtitle_out(output_files[ost->file_index], ost, subtitle); - } - -out: - if (free_sub) - avsubtitle_free(subtitle); - return ret; -} - -static int copy_av_subtitle(AVSubtitle *dst, AVSubtitle *src) -{ - int ret = AVERROR_BUG; - AVSubtitle tmp = { - .format = src->format, - .start_display_time = src->start_display_time, - .end_display_time = src->end_display_time, - .num_rects = 0, - .rects = NULL, - .pts = src->pts - }; - - if (!src->num_rects) - goto success; - - if (!(tmp.rects = av_calloc(src->num_rects, sizeof(*tmp.rects)))) - return AVERROR(ENOMEM); - - for (int i = 0; i < src->num_rects; i++) { - AVSubtitleRect *src_rect = src->rects[i]; - AVSubtitleRect *dst_rect; - - if (!(dst_rect = tmp.rects[i] = av_mallocz(sizeof(*tmp.rects[0])))) { - ret = AVERROR(ENOMEM); - goto cleanup; - } - - tmp.num_rects++; - - dst_rect->type = src_rect->type; - dst_rect->flags = src_rect->flags; - - dst_rect->x = src_rect->x; - dst_rect->y = src_rect->y; - dst_rect->w = src_rect->w; - dst_rect->h = src_rect->h; - dst_rect->nb_colors = src_rect->nb_colors; - - if (src_rect->text) - if (!(dst_rect->text = av_strdup(src_rect->text))) { - ret = AVERROR(ENOMEM); - goto cleanup; - } - - if (src_rect->ass) - if (!(dst_rect->ass = av_strdup(src_rect->ass))) { - ret = AVERROR(ENOMEM); - goto cleanup; - } - - for (int j = 0; j < 4; j++) { - // SUBTITLE_BITMAP images are special in the sense that they - // are like PAL8 images. first pointer to data, second to - // palette. This makes the size calculation match this. - size_t buf_size = src_rect->type == SUBTITLE_BITMAP && j == 1 ? - AVPALETTE_SIZE : - src_rect->h * src_rect->linesize[j]; - - if (!src_rect->data[j]) - continue; - - if (!(dst_rect->data[j] = av_memdup(src_rect->data[j], buf_size))) { - ret = AVERROR(ENOMEM); - goto cleanup; - } - dst_rect->linesize[j] = src_rect->linesize[j]; - } - } - -success: - *dst = tmp; - - return 0; - -cleanup: - avsubtitle_free(&tmp); - - return ret; -} - -static int fix_sub_duration_heartbeat(InputStream *ist, int64_t signal_pts) -{ - int ret = AVERROR_BUG; - int got_output = 1; - AVSubtitle *prev_subtitle = &ist->prev_sub.subtitle; - AVSubtitle subtitle; - - if (!ist->fix_sub_duration || !prev_subtitle->num_rects || - signal_pts <= prev_subtitle->pts) - return 0; - - if ((ret = copy_av_subtitle(&subtitle, prev_subtitle)) < 0) - return ret; - - subtitle.pts = signal_pts; - - return process_subtitle(ist, &subtitle, &got_output); -} - -static int trigger_fix_sub_duration_heartbeat(OutputStream *ost, const AVPacket *pkt) -{ - OutputFile *of = output_files[ost->file_index]; - int64_t signal_pts = av_rescale_q(pkt->pts, pkt->time_base, - AV_TIME_BASE_Q); - - if (!ost->fix_sub_duration_heartbeat || !(pkt->flags & AV_PKT_FLAG_KEY)) - // we are only interested in heartbeats on streams configured, and - // only on random access points. - return 0; - - for (int i = 0; i < of->nb_streams; i++) { - OutputStream *iter_ost = of->streams[i]; - InputStream *ist = iter_ost->ist; - int ret = AVERROR_BUG; - - if (iter_ost == ost || !ist || !ist->decoding_needed || - ist->dec_ctx->codec_type != AVMEDIA_TYPE_SUBTITLE) - // We wish to skip the stream that causes the heartbeat, - // output streams without an input stream, streams not decoded - // (as fix_sub_duration is only done for decoded subtitles) as - // well as non-subtitle streams. - continue; - - if ((ret = fix_sub_duration_heartbeat(ist, signal_pts)) < 0) - return ret; - } - - return 0; -} - -static int transcode_subtitles(InputStream *ist, const AVPacket *pkt, - int *got_output, int *decode_failed) -{ - AVSubtitle subtitle; - int ret = avcodec_decode_subtitle2(ist->dec_ctx, - &subtitle, got_output, pkt); - - check_decode_result(NULL, got_output, ret); - - if (ret < 0 || !*got_output) { - *decode_failed = 1; - if (!pkt->size) - sub2video_flush(ist); - return ret; - } - - ist->frames_decoded++; - - return process_subtitle(ist, &subtitle, got_output); -} - -static int send_filter_eof(InputStream *ist) -{ - int i, ret; - /* TODO keep pts also in stream time base to avoid converting back */ - int64_t pts = av_rescale_q_rnd(ist->pts, AV_TIME_BASE_Q, ist->st->time_base, - AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - - for (i = 0; i < ist->nb_filters; i++) { - ret = ifilter_send_eof(ist->filters[i], pts); - if (ret < 0) - return ret; - } - return 0; -} - -/* pkt = NULL means EOF (needed to flush decoder buffers) */ -static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eof) -{ - const AVCodecParameters *par = ist->par; - int ret = 0; - int repeating = 0; - int eof_reached = 0; - - AVPacket *avpkt = ist->pkt; - - if (!ist->saw_first_ts) { - ist->first_dts = - ist->dts = ist->st->avg_frame_rate.num ? - ist->dec_ctx->has_b_frames * AV_TIME_BASE / av_q2d(ist->st->avg_frame_rate) : 0; - ist->pts = 0; - if (pkt && pkt->pts != AV_NOPTS_VALUE && !ist->decoding_needed) { - ist->first_dts = - ist->dts += av_rescale_q(pkt->pts, ist->st->time_base, AV_TIME_BASE_Q); - ist->pts = ist->dts; //unused but better to set it to a value thats not totally wrong - } - ist->saw_first_ts = 1; - } - - if (ist->next_dts == AV_NOPTS_VALUE) - ist->next_dts = ist->dts; - if (ist->next_pts == AV_NOPTS_VALUE) - ist->next_pts = ist->pts; - - if (pkt) { - av_packet_unref(avpkt); - ret = av_packet_ref(avpkt, pkt); - if (ret < 0) - return ret; - } - - if (pkt && pkt->dts != AV_NOPTS_VALUE) { - ist->next_dts = ist->dts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q); - if (par->codec_type != AVMEDIA_TYPE_VIDEO || !ist->decoding_needed) - ist->next_pts = ist->pts = ist->dts; - } - - // while we have more to decode or while the decoder did output something on EOF - while (ist->decoding_needed) { - int64_t duration_dts = 0; - int64_t duration_pts = 0; - int got_output = 0; - int decode_failed = 0; - - ist->pts = ist->next_pts; - ist->dts = ist->next_dts; - - switch (par->codec_type) { - case AVMEDIA_TYPE_AUDIO: - ret = decode_audio (ist, repeating ? NULL : avpkt, &got_output, - &decode_failed); - av_packet_unref(avpkt); - break; - case AVMEDIA_TYPE_VIDEO: - ret = decode_video (ist, repeating ? NULL : avpkt, &got_output, &duration_pts, !pkt, - &decode_failed); - if (!repeating || !pkt || got_output) { - if (pkt && pkt->duration) { - duration_dts = av_rescale_q(pkt->duration, ist->st->time_base, AV_TIME_BASE_Q); - } else if(ist->dec_ctx->framerate.num != 0 && ist->dec_ctx->framerate.den != 0) { - int ticks = ist->last_pkt_repeat_pict >= 0 ? - ist->last_pkt_repeat_pict + 1 : - ist->dec_ctx->ticks_per_frame; - duration_dts = ((int64_t)AV_TIME_BASE * - ist->dec_ctx->framerate.den * ticks) / - ist->dec_ctx->framerate.num / ist->dec_ctx->ticks_per_frame; - } - - if(ist->dts != AV_NOPTS_VALUE && duration_dts) { - ist->next_dts += duration_dts; - }else - ist->next_dts = AV_NOPTS_VALUE; - } - - if (got_output) { - if (duration_pts > 0) { - ist->next_pts += av_rescale_q(duration_pts, ist->st->time_base, AV_TIME_BASE_Q); - } else { - ist->next_pts += duration_dts; - } - } - av_packet_unref(avpkt); - break; - case AVMEDIA_TYPE_SUBTITLE: - if (repeating) - break; - ret = transcode_subtitles(ist, avpkt, &got_output, &decode_failed); - if (!pkt && ret >= 0) - ret = AVERROR_EOF; - av_packet_unref(avpkt); - break; - default: - return -1; - } - - if (ret == AVERROR_EOF) { - eof_reached = 1; - break; - } - - if (ret < 0) { - if (decode_failed) { - av_log(NULL, AV_LOG_ERROR, "Error while decoding stream #%d:%d: %s\n", - ist->file_index, ist->st->index, av_err2str(ret)); - } else { - av_log(NULL, AV_LOG_FATAL, "Error while processing the decoded " - "data for stream #%d:%d\n", ist->file_index, ist->st->index); - } - if (!decode_failed || exit_on_error) - exit_program(1); - break; - } - - if (got_output) - ist->got_output = 1; - - if (!got_output) - break; - - // During draining, we might get multiple output frames in this loop. - // ffmpeg.c does not drain the filter chain on configuration changes, - // which means if we send multiple frames at once to the filters, and - // one of those frames changes configuration, the buffered frames will - // be lost. This can upset certain FATE tests. - // Decode only 1 frame per call on EOF to appease these FATE tests. - // The ideal solution would be to rewrite decoding to use the new - // decoding API in a better way. - if (!pkt) - break; - - repeating = 1; - } - - /* after flushing, send an EOF on all the filter inputs attached to the stream */ - /* except when looping we need to flush but not to send an EOF */ - if (!pkt && ist->decoding_needed && eof_reached && !no_eof) { - int ret = send_filter_eof(ist); - if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Error marking filters as finished\n"); - exit_program(1); - } - } - - /* handle stream copy */ - if (!ist->decoding_needed && pkt) { - ist->dts = ist->next_dts; - switch (par->codec_type) { - case AVMEDIA_TYPE_AUDIO: - av_assert1(pkt->duration >= 0); - if (par->sample_rate) { - ist->next_dts += ((int64_t)AV_TIME_BASE * par->frame_size) / - par->sample_rate; - } else { - ist->next_dts += av_rescale_q(pkt->duration, ist->st->time_base, AV_TIME_BASE_Q); - } - break; - case AVMEDIA_TYPE_VIDEO: - if (ist->framerate.num) { - // TODO: Remove work-around for c99-to-c89 issue 7 - AVRational time_base_q = AV_TIME_BASE_Q; - int64_t next_dts = av_rescale_q(ist->next_dts, time_base_q, av_inv_q(ist->framerate)); - ist->next_dts = av_rescale_q(next_dts + 1, av_inv_q(ist->framerate), time_base_q); - } else if (pkt->duration) { - ist->next_dts += av_rescale_q(pkt->duration, ist->st->time_base, AV_TIME_BASE_Q); - } else if(ist->dec_ctx->framerate.num != 0) { - int ticks = ist->last_pkt_repeat_pict >= 0 ? - ist->last_pkt_repeat_pict + 1 : - ist->dec_ctx->ticks_per_frame; - ist->next_dts += ((int64_t)AV_TIME_BASE * - ist->dec_ctx->framerate.den * ticks) / - ist->dec_ctx->framerate.num / ist->dec_ctx->ticks_per_frame; - } - break; - } - ist->pts = ist->dts; - ist->next_pts = ist->next_dts; - } else if (!ist->decoding_needed) - eof_reached = 1; - - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - if (!check_output_constraints(ist, ost) || ost->enc_ctx || - (!pkt && no_eof)) - continue; - - do_streamcopy(ist, ost, pkt); - } - - return !eof_reached; -} - -static enum AVPixelFormat get_format(AVCodecContext *s, const enum AVPixelFormat *pix_fmts) -{ - InputStream *ist = s->opaque; - const enum AVPixelFormat *p; - int ret; - - for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) { - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(*p); - const AVCodecHWConfig *config = NULL; - int i; - - if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) - break; - - if (ist->hwaccel_id == HWACCEL_GENERIC || - ist->hwaccel_id == HWACCEL_AUTO) { - for (i = 0;; i++) { - config = avcodec_get_hw_config(s->codec, i); - if (!config) - break; - if (!(config->methods & - AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) - continue; - if (config->pix_fmt == *p) - break; - } - } - if (config && config->device_type == ist->hwaccel_device_type) { - ret = hwaccel_decode_init(s); - if (ret < 0) { - if (ist->hwaccel_id == HWACCEL_GENERIC) { - av_log(NULL, AV_LOG_FATAL, - "%s hwaccel requested for input stream #%d:%d, " - "but cannot be initialized.\n", - av_hwdevice_get_type_name(config->device_type), - ist->file_index, ist->st->index); - return AV_PIX_FMT_NONE; - } - continue; - } - - ist->hwaccel_pix_fmt = *p; - break; - } - } - - return *p; -} - -static int init_input_stream(InputStream *ist, char *error, int error_len) -{ - int ret; - - if (ist->decoding_needed) { - const AVCodec *codec = ist->dec; - if (!codec) { - snprintf(error, error_len, "Decoder (codec %s) not found for input stream #%d:%d", - avcodec_get_name(ist->dec_ctx->codec_id), ist->file_index, ist->st->index); - return AVERROR(EINVAL); - } - - ist->dec_ctx->opaque = ist; - ist->dec_ctx->get_format = get_format; - - if (ist->dec_ctx->codec_id == AV_CODEC_ID_DVB_SUBTITLE && - (ist->decoding_needed & DECODING_FOR_OST)) { - av_dict_set(&ist->decoder_opts, "compute_edt", "1", AV_DICT_DONT_OVERWRITE); - if (ist->decoding_needed & DECODING_FOR_FILTER) - av_log(NULL, AV_LOG_WARNING, "Warning using DVB subtitles for filtering and output at the same time is not fully supported, also see -compute_edt [0|1]\n"); - } - - /* Useful for subtitles retiming by lavf (FIXME), skipping samples in - * audio, and video decoders such as cuvid or mediacodec */ - ist->dec_ctx->pkt_timebase = ist->st->time_base; - - if (!av_dict_get(ist->decoder_opts, "threads", NULL, 0)) - av_dict_set(&ist->decoder_opts, "threads", "auto", 0); - /* Attached pics are sparse, therefore we would not want to delay their decoding till EOF. */ - if (ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC) - av_dict_set(&ist->decoder_opts, "threads", "1", 0); - - ret = hw_device_setup_for_decode(ist); - if (ret < 0) { - snprintf(error, error_len, "Device setup failed for " - "decoder on input stream #%d:%d : %s", - ist->file_index, ist->st->index, av_err2str(ret)); - return ret; - } - - if ((ret = avcodec_open2(ist->dec_ctx, codec, &ist->decoder_opts)) < 0) { - if (ret == AVERROR_EXPERIMENTAL) - abort_codec_experimental(codec, 0); - - snprintf(error, error_len, - "Error while opening decoder for input stream " - "#%d:%d : %s", - ist->file_index, ist->st->index, av_err2str(ret)); - return ret; - } - assert_avoptions(ist->decoder_opts); - } - - ist->next_pts = AV_NOPTS_VALUE; - ist->next_dts = AV_NOPTS_VALUE; - - return 0; -} - -static int init_output_stream_streamcopy(OutputStream *ost) -{ - OutputFile *of = output_files[ost->file_index]; - InputStream *ist = ost->ist; - InputFile *ifile = input_files[ist->file_index]; - AVCodecParameters *par = ost->st->codecpar; - AVCodecContext *codec_ctx; - AVRational sar; - int i, ret; - uint32_t codec_tag = par->codec_tag; - - av_assert0(ist && !ost->filter); - - codec_ctx = avcodec_alloc_context3(NULL); - if (!codec_ctx) - return AVERROR(ENOMEM); - - ret = avcodec_parameters_to_context(codec_ctx, ist->par); - if (ret >= 0) - ret = av_opt_set_dict(codec_ctx, &ost->encoder_opts); - if (ret < 0) { - av_log(ost, AV_LOG_FATAL, - "Error setting up codec context options.\n"); - avcodec_free_context(&codec_ctx); - return ret; - } - - ret = avcodec_parameters_from_context(par, codec_ctx); - avcodec_free_context(&codec_ctx); - if (ret < 0) { - av_log(ost, AV_LOG_FATAL, - "Error getting reference codec parameters.\n"); - return ret; - } - - if (!codec_tag) { - unsigned int codec_tag_tmp; - if (!of->format->codec_tag || - av_codec_get_id (of->format->codec_tag, par->codec_tag) == par->codec_id || - !av_codec_get_tag2(of->format->codec_tag, par->codec_id, &codec_tag_tmp)) - codec_tag = par->codec_tag; - } - - par->codec_tag = codec_tag; - - if (!ost->frame_rate.num) - ost->frame_rate = ist->framerate; - - if (ost->frame_rate.num) - ost->st->avg_frame_rate = ost->frame_rate; - else - ost->st->avg_frame_rate = ist->st->avg_frame_rate; - - ret = avformat_transfer_internal_stream_timing_info(of->format, ost->st, ist->st, copy_tb); - if (ret < 0) - return ret; - - // copy timebase while removing common factors - if (ost->st->time_base.num <= 0 || ost->st->time_base.den <= 0) { - if (ost->frame_rate.num) - ost->st->time_base = av_inv_q(ost->frame_rate); - else - ost->st->time_base = av_add_q(av_stream_get_codec_timebase(ost->st), (AVRational){0, 1}); - } - - // copy estimated duration as a hint to the muxer - if (ost->st->duration <= 0 && ist->st->duration > 0) - ost->st->duration = av_rescale_q(ist->st->duration, ist->st->time_base, ost->st->time_base); - - if (!ost->copy_prior_start) { - ost->ts_copy_start = (of->start_time == AV_NOPTS_VALUE) ? - 0 : of->start_time; - if (copy_ts && ifile->start_time != AV_NOPTS_VALUE) { - ost->ts_copy_start = FFMAX(ost->ts_copy_start, - ifile->start_time + ifile->ts_offset); - } - } - - if (ist->st->nb_side_data) { - for (i = 0; i < ist->st->nb_side_data; i++) { - const AVPacketSideData *sd_src = &ist->st->side_data[i]; - uint8_t *dst_data; - - dst_data = av_stream_new_side_data(ost->st, sd_src->type, sd_src->size); - if (!dst_data) - return AVERROR(ENOMEM); - memcpy(dst_data, sd_src->data, sd_src->size); - } - } - -#if FFMPEG_ROTATION_METADATA - if (ost->rotate_overridden) { - uint8_t *sd = av_stream_new_side_data(ost->st, AV_PKT_DATA_DISPLAYMATRIX, - sizeof(int32_t) * 9); - if (sd) - av_display_rotation_set((int32_t *)sd, -ost->rotate_override_value); - } -#endif - - switch (par->codec_type) { - case AVMEDIA_TYPE_AUDIO: - if ((par->block_align == 1 || par->block_align == 1152 || par->block_align == 576) && - par->codec_id == AV_CODEC_ID_MP3) - par->block_align = 0; - if (par->codec_id == AV_CODEC_ID_AC3) - par->block_align = 0; - break; - case AVMEDIA_TYPE_VIDEO: - if (ost->frame_aspect_ratio.num) { // overridden by the -aspect cli option - sar = - av_mul_q(ost->frame_aspect_ratio, - (AVRational){ par->height, par->width }); - av_log(ost, AV_LOG_WARNING, "Overriding aspect ratio " - "with stream copy may produce invalid files\n"); - } - else if (ist->st->sample_aspect_ratio.num) - sar = ist->st->sample_aspect_ratio; - else - sar = par->sample_aspect_ratio; - ost->st->sample_aspect_ratio = par->sample_aspect_ratio = sar; - ost->st->avg_frame_rate = ist->st->avg_frame_rate; - ost->st->r_frame_rate = ist->st->r_frame_rate; - break; - } - - ost->mux_timebase = ist->st->time_base; - - return 0; -} - -static void set_encoder_id(OutputFile *of, OutputStream *ost) -{ - const char *cname = ost->enc_ctx->codec->name; - uint8_t *encoder_string; - int encoder_string_len; - - if (av_dict_get(ost->st->metadata, "encoder", NULL, 0)) - return; - - encoder_string_len = sizeof(LIBAVCODEC_IDENT) + strlen(cname) + 2; - encoder_string = av_mallocz(encoder_string_len); - if (!encoder_string) - report_and_exit(AVERROR(ENOMEM)); - - if (!of->bitexact && !ost->bitexact) - av_strlcpy(encoder_string, LIBAVCODEC_IDENT " ", encoder_string_len); - else - av_strlcpy(encoder_string, "Lavc ", encoder_string_len); - av_strlcat(encoder_string, cname, encoder_string_len); - av_dict_set(&ost->st->metadata, "encoder", encoder_string, - AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE); -} - -static void init_encoder_time_base(OutputStream *ost, AVRational default_time_base) -{ - InputStream *ist = ost->ist; - AVCodecContext *enc_ctx = ost->enc_ctx; - - if (ost->enc_timebase.num > 0) { - enc_ctx->time_base = ost->enc_timebase; - return; - } - - if (ost->enc_timebase.num < 0) { - if (ist) { - enc_ctx->time_base = ist->st->time_base; - return; - } - - av_log(ost, AV_LOG_WARNING, - "Input stream data not available, using default time base\n"); - } - - enc_ctx->time_base = default_time_base; -} - -static int init_output_stream_encode(OutputStream *ost, AVFrame *frame) -{ - InputStream *ist = ost->ist; - AVCodecContext *enc_ctx = ost->enc_ctx; - AVCodecContext *dec_ctx = NULL; - OutputFile *of = output_files[ost->file_index]; - int ret; - - set_encoder_id(output_files[ost->file_index], ost); - - if (ist) { - dec_ctx = ist->dec_ctx; - } - - if (enc_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { - if (!ost->frame_rate.num) - ost->frame_rate = av_buffersink_get_frame_rate(ost->filter->filter); - if (!ost->frame_rate.num && !ost->max_frame_rate.num) { - ost->frame_rate = (AVRational){25, 1}; - av_log(ost, AV_LOG_WARNING, - "No information " - "about the input framerate is available. Falling " - "back to a default value of 25fps. Use the -r option " - "if you want a different framerate.\n"); - } - - if (ost->max_frame_rate.num && - (av_q2d(ost->frame_rate) > av_q2d(ost->max_frame_rate) || - !ost->frame_rate.den)) - ost->frame_rate = ost->max_frame_rate; - - if (enc_ctx->codec->supported_framerates && !ost->force_fps) { - int idx = av_find_nearest_q_idx(ost->frame_rate, enc_ctx->codec->supported_framerates); - ost->frame_rate = enc_ctx->codec->supported_framerates[idx]; - } - // reduce frame rate for mpeg4 to be within the spec limits - if (enc_ctx->codec_id == AV_CODEC_ID_MPEG4) { - av_reduce(&ost->frame_rate.num, &ost->frame_rate.den, - ost->frame_rate.num, ost->frame_rate.den, 65535); - } - } - - switch (enc_ctx->codec_type) { - case AVMEDIA_TYPE_AUDIO: - enc_ctx->sample_fmt = av_buffersink_get_format(ost->filter->filter); - enc_ctx->sample_rate = av_buffersink_get_sample_rate(ost->filter->filter); - ret = av_buffersink_get_ch_layout(ost->filter->filter, &enc_ctx->ch_layout); - if (ret < 0) - return ret; - - if (ost->bits_per_raw_sample) - enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample; - else if (dec_ctx && ost->filter->graph->is_meta) - enc_ctx->bits_per_raw_sample = FFMIN(dec_ctx->bits_per_raw_sample, - av_get_bytes_per_sample(enc_ctx->sample_fmt) << 3); - - init_encoder_time_base(ost, av_make_q(1, enc_ctx->sample_rate)); - break; - - case AVMEDIA_TYPE_VIDEO: - init_encoder_time_base(ost, av_inv_q(ost->frame_rate)); - - if (!(enc_ctx->time_base.num && enc_ctx->time_base.den)) - enc_ctx->time_base = av_buffersink_get_time_base(ost->filter->filter); - if ( av_q2d(enc_ctx->time_base) < 0.001 && ost->vsync_method != VSYNC_PASSTHROUGH - && (ost->vsync_method == VSYNC_CFR || ost->vsync_method == VSYNC_VSCFR || - (ost->vsync_method == VSYNC_AUTO && !(of->format->flags & AVFMT_VARIABLE_FPS)))){ - av_log(ost, AV_LOG_WARNING, "Frame rate very high for a muxer not efficiently supporting it.\n" - "Please consider specifying a lower framerate, a different muxer or " - "setting vsync/fps_mode to vfr\n"); - } - - enc_ctx->width = av_buffersink_get_w(ost->filter->filter); - enc_ctx->height = av_buffersink_get_h(ost->filter->filter); - enc_ctx->sample_aspect_ratio = ost->st->sample_aspect_ratio = - ost->frame_aspect_ratio.num ? // overridden by the -aspect cli option - av_mul_q(ost->frame_aspect_ratio, (AVRational){ enc_ctx->height, enc_ctx->width }) : - av_buffersink_get_sample_aspect_ratio(ost->filter->filter); - - enc_ctx->pix_fmt = av_buffersink_get_format(ost->filter->filter); - - if (ost->bits_per_raw_sample) - enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample; - else if (dec_ctx && ost->filter->graph->is_meta) - enc_ctx->bits_per_raw_sample = FFMIN(dec_ctx->bits_per_raw_sample, - av_pix_fmt_desc_get(enc_ctx->pix_fmt)->comp[0].depth); - - if (frame) { - enc_ctx->color_range = frame->color_range; - enc_ctx->color_primaries = frame->color_primaries; - enc_ctx->color_trc = frame->color_trc; - enc_ctx->colorspace = frame->colorspace; - enc_ctx->chroma_sample_location = frame->chroma_location; - } - - enc_ctx->framerate = ost->frame_rate; - - ost->st->avg_frame_rate = ost->frame_rate; - - // Field order: autodetection - if (frame) { - if (enc_ctx->flags & (AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME) && - ost->top_field_first >= 0) - frame->top_field_first = !!ost->top_field_first; - - if (frame->interlaced_frame) { - if (enc_ctx->codec->id == AV_CODEC_ID_MJPEG) - enc_ctx->field_order = frame->top_field_first ? AV_FIELD_TT:AV_FIELD_BB; - else - enc_ctx->field_order = frame->top_field_first ? AV_FIELD_TB:AV_FIELD_BT; - } else - enc_ctx->field_order = AV_FIELD_PROGRESSIVE; - } - - // Field order: override - if (ost->top_field_first == 0) { - enc_ctx->field_order = AV_FIELD_BB; - } else if (ost->top_field_first == 1) { - enc_ctx->field_order = AV_FIELD_TT; - } - - break; - case AVMEDIA_TYPE_SUBTITLE: - enc_ctx->time_base = AV_TIME_BASE_Q; - if (!enc_ctx->width) { - enc_ctx->width = ost->ist->par->width; - enc_ctx->height = ost->ist->par->height; - } - if (dec_ctx && dec_ctx->subtitle_header) { - /* ASS code assumes this buffer is null terminated so add extra byte. */ - ost->enc_ctx->subtitle_header = av_mallocz(dec_ctx->subtitle_header_size + 1); - if (!ost->enc_ctx->subtitle_header) - return AVERROR(ENOMEM); - memcpy(ost->enc_ctx->subtitle_header, dec_ctx->subtitle_header, - dec_ctx->subtitle_header_size); - ost->enc_ctx->subtitle_header_size = dec_ctx->subtitle_header_size; - } - if (ist && ist->dec->type == AVMEDIA_TYPE_SUBTITLE && - enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { - int input_props = 0, output_props = 0; - AVCodecDescriptor const *input_descriptor = - avcodec_descriptor_get(ist->dec->id); - AVCodecDescriptor const *output_descriptor = - avcodec_descriptor_get(ost->enc_ctx->codec_id); - if (input_descriptor) - input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); - if (output_descriptor) - output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); - if (input_props && output_props && input_props != output_props) { - av_log(ost, AV_LOG_ERROR, - "Subtitle encoding currently only possible from text to text " - "or bitmap to bitmap"); - return AVERROR_INVALIDDATA; - } - } - - break; - case AVMEDIA_TYPE_DATA: - break; - default: - abort(); - break; + if (speed < 0) { + av_bprintf(&buf, " speed=N/A"); + av_bprintf(&buf_script, "speed=N/A\n"); + } else { + av_bprintf(&buf, " speed=%4.3gx", speed); + av_bprintf(&buf_script, "speed=%4.3gx\n", speed); } - if (ost->bitexact) - enc_ctx->flags |= AV_CODEC_FLAG_BITEXACT; - - if (ost->sq_idx_encode >= 0) - sq_set_tb(of->sq_encode, ost->sq_idx_encode, enc_ctx->time_base); - - ost->mux_timebase = enc_ctx->time_base; - - return 0; -} - -static int init_output_stream(OutputStream *ost, AVFrame *frame, - char *error, int error_len) -{ - int ret = 0; - - if (ost->enc_ctx) { - const AVCodec *codec = ost->enc_ctx->codec; - InputStream *ist = ost->ist; - - ret = init_output_stream_encode(ost, frame); - if (ret < 0) - return ret; - - if (!av_dict_get(ost->encoder_opts, "threads", NULL, 0)) - av_dict_set(&ost->encoder_opts, "threads", "auto", 0); - - if (codec->capabilities & AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE) { - ret = av_dict_set(&ost->encoder_opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY); - if (ret < 0) - return ret; - } - - ret = hw_device_setup_for_encode(ost); - if (ret < 0) { - snprintf(error, error_len, "Device setup failed for " - "encoder on output stream #%d:%d : %s", - ost->file_index, ost->index, av_err2str(ret)); - return ret; - } - - if ((ret = avcodec_open2(ost->enc_ctx, codec, &ost->encoder_opts)) < 0) { - if (ret == AVERROR_EXPERIMENTAL) - abort_codec_experimental(codec, 1); - snprintf(error, error_len, - "Error while opening encoder for output stream #%d:%d - " - "maybe incorrect parameters such as bit_rate, rate, width or height", - ost->file_index, ost->index); - return ret; - } - if (codec->type == AVMEDIA_TYPE_AUDIO && - !(codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) - av_buffersink_set_frame_size(ost->filter->filter, - ost->enc_ctx->frame_size); - assert_avoptions(ost->encoder_opts); - if (ost->enc_ctx->bit_rate && ost->enc_ctx->bit_rate < 1000 && - ost->enc_ctx->codec_id != AV_CODEC_ID_CODEC2 /* don't complain about 700 bit/s modes */) - av_log(ost, AV_LOG_WARNING, "The bitrate parameter is set too low." - " It takes bits/s as argument, not kbits/s\n"); - - ret = avcodec_parameters_from_context(ost->st->codecpar, ost->enc_ctx); - if (ret < 0) { - av_log(ost, AV_LOG_FATAL, - "Error initializing the output stream codec context.\n"); - exit_program(1); - } - - if (ost->enc_ctx->nb_coded_side_data) { - int i; - - for (i = 0; i < ost->enc_ctx->nb_coded_side_data; i++) { - const AVPacketSideData *sd_src = &ost->enc_ctx->coded_side_data[i]; - uint8_t *dst_data; + if (print_stats || is_last_report) { + const char end = is_last_report ? '\n' : '\r'; + if (print_stats==1 && AV_LOG_INFO > av_log_get_level()) { + fprintf(stderr, "%s %c", buf.str, end); + } else + av_log(NULL, AV_LOG_INFO, "%s %c", buf.str, end); - dst_data = av_stream_new_side_data(ost->st, sd_src->type, sd_src->size); - if (!dst_data) - return AVERROR(ENOMEM); - memcpy(dst_data, sd_src->data, sd_src->size); - } - } + fflush(stderr); + } + av_bprint_finalize(&buf, NULL); - /* - * Add global input side data. For now this is naive, and copies it - * from the input stream's global side data. All side data should - * really be funneled over AVFrame and libavfilter, then added back to - * packet side data, and then potentially using the first packet for - * global side data. - */ - if (ist) { - int i; - for (i = 0; i < ist->st->nb_side_data; i++) { - AVPacketSideData *sd = &ist->st->side_data[i]; - if (sd->type != AV_PKT_DATA_CPB_PROPERTIES) { - uint8_t *dst = av_stream_new_side_data(ost->st, sd->type, sd->size); - if (!dst) - return AVERROR(ENOMEM); - memcpy(dst, sd->data, sd->size); - if (ist->autorotate && sd->type == AV_PKT_DATA_DISPLAYMATRIX) - av_display_rotation_set((int32_t *)dst, 0); - } - } + if (progress_avio) { + av_bprintf(&buf_script, "progress=%s\n", + is_last_report ? "end" : "continue"); + avio_write(progress_avio, buf_script.str, + FFMIN(buf_script.len, buf_script.size - 1)); + avio_flush(progress_avio); + av_bprint_finalize(&buf_script, NULL); + if (is_last_report) { + if ((ret = avio_closep(&progress_avio)) < 0) + av_log(NULL, AV_LOG_ERROR, + "Error closing progress log, loss of information possible: %s\n", av_err2str(ret)); } - - // copy timebase while removing common factors - if (ost->st->time_base.num <= 0 || ost->st->time_base.den <= 0) - ost->st->time_base = av_add_q(ost->enc_ctx->time_base, (AVRational){0, 1}); - - // copy estimated duration as a hint to the muxer - if (ost->st->duration <= 0 && ist && ist->st->duration > 0) - ost->st->duration = av_rescale_q(ist->st->duration, ist->st->time_base, ost->st->time_base); - } else if (ost->ist) { - ret = init_output_stream_streamcopy(ost); - if (ret < 0) - return ret; } - ret = of_stream_init(output_files[ost->file_index], ost); - if (ret < 0) - return ret; - - return ret; + first_report = 0; } -static int transcode_init(void) +static void print_stream_maps(void) { - int ret = 0; - char error[1024] = {0}; - - /* init framerate emulation */ - for (int i = 0; i < nb_input_files; i++) { - InputFile *ifile = input_files[i]; - if (ifile->readrate || ifile->rate_emu) - for (int j = 0; j < ifile->nb_streams; j++) - ifile->streams[j]->start = av_gettime_relative(); - } - - /* init input streams */ - for (InputStream *ist = ist_iter(NULL); ist; ist = ist_iter(ist)) - if ((ret = init_input_stream(ist, error, sizeof(error))) < 0) - goto dump_format; - - /* - * initialize stream copy and subtitle/data streams. - * Encoded AVFrame based streams will get initialized as follows: - * - when the first AVFrame is received in do_video_out - * - just before the first AVFrame is received in either transcode_step - * or reap_filters due to us requiring the filter chain buffer sink - * to be configured with the correct audio frame size, which is only - * known after the encoder is initialized. - */ - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - if (ost->enc_ctx && - (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || - ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)) - continue; - - ret = init_output_stream_wrapper(ost, NULL, 0); - if (ret < 0) - goto dump_format; - } - - /* discard unused programs */ - for (int i = 0; i < nb_input_files; i++) { - InputFile *ifile = input_files[i]; - for (int j = 0; j < ifile->ctx->nb_programs; j++) { - AVProgram *p = ifile->ctx->programs[j]; - int discard = AVDISCARD_ALL; - - for (int k = 0; k < p->nb_stream_indexes; k++) - if (!ifile->streams[p->stream_index[k]]->discard) { - discard = AVDISCARD_DEFAULT; - break; - } - p->discard = discard; - } - } - - dump_format: - /* dump the stream mapping */ av_log(NULL, AV_LOG_INFO, "Stream mapping:\n"); for (InputStream *ist = ist_iter(NULL); ist; ist = ist_iter(ist)) { for (int j = 0; j < ist->nb_filters; j++) { if (!filtergraph_is_simple(ist->filters[j]->graph)) { av_log(NULL, AV_LOG_INFO, " Stream #%d:%d (%s) -> %s", - ist->file_index, ist->st->index, ist->dec ? ist->dec->name : "?", + ist->file->index, ist->index, ist->dec ? ist->dec->name : "?", ist->filters[j]->name); if (nb_filtergraphs > 1) av_log(NULL, AV_LOG_INFO, " (graph %d)", ist->filters[j]->graph->index); @@ -3389,7 +706,7 @@ static int transcode_init(void) if (ost->attachment_filename) { /* an attached file */ av_log(NULL, AV_LOG_INFO, " File %s -> Stream #%d:%d\n", - ost->attachment_filename, ost->file_index, ost->index); + ost->attachment_filename, ost->file->index, ost->index); continue; } @@ -3399,15 +716,15 @@ static int transcode_init(void) if (nb_filtergraphs > 1) av_log(NULL, AV_LOG_INFO, " (graph %d)", ost->filter->graph->index); - av_log(NULL, AV_LOG_INFO, " -> Stream #%d:%d (%s)\n", ost->file_index, + av_log(NULL, AV_LOG_INFO, " -> Stream #%d:%d (%s)\n", ost->file->index, ost->index, ost->enc_ctx->codec->name); continue; } av_log(NULL, AV_LOG_INFO, " Stream #%d:%d -> #%d:%d", - ost->ist->file_index, - ost->ist->st->index, - ost->file_index, + ost->ist->file->index, + ost->ist->index, + ost->file->index, ost->index); if (ost->enc_ctx) { const AVCodec *in_codec = ost->ist->dec; @@ -3443,63 +760,6 @@ static int transcode_init(void) av_log(NULL, AV_LOG_INFO, " (copy)"); av_log(NULL, AV_LOG_INFO, "\n"); } - - if (ret) { - av_log(NULL, AV_LOG_ERROR, "%s\n", error); - return ret; - } - - atomic_store(&transcode_init_done, 1); - - return 0; -} - -/* Return 1 if there remain streams where more output is wanted, 0 otherwise. */ -static int need_output(void) -{ - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - if (ost->finished) - continue; - - return 1; - } - - return 0; -} - -/** - * Select the output stream to process. - * - * @return selected output stream, or NULL if none available - */ -static OutputStream *choose_output(void) -{ - int64_t opts_min = INT64_MAX; - OutputStream *ost_min = NULL; - - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - int64_t opts; - - if (ost->filter && ost->last_filter_pts != AV_NOPTS_VALUE) { - opts = ost->last_filter_pts; - } else { - opts = ost->last_mux_dts == AV_NOPTS_VALUE ? - INT64_MIN : ost->last_mux_dts; - if (ost->last_mux_dts == AV_NOPTS_VALUE) - av_log(ost, AV_LOG_DEBUG, - "cur_dts is invalid [init:%d i_done:%d finish:%d] (this is harmless if it occurs once at the start per stream)\n", - ost->initialized, ost->inputs_done, ost->finished); - } - - if (!ost->initialized && !ost->inputs_done) - return ost->unavailable ? NULL : ost; - - if (!ost->finished && opts < opts_min) { - opts_min = opts; - ost_min = ost->unavailable ? NULL : ost; - } - } - return ost_min; } static void set_tty_echo(int on) @@ -3516,7 +776,7 @@ static void set_tty_echo(int on) static int check_keyboard_interaction(int64_t cur_time) { - int i, ret, key; + int i, key; static int64_t last_time; if (received_nb_signals) return AVERROR_EXIT; @@ -3532,7 +792,6 @@ static int check_keyboard_interaction(int64_t cur_time) } if (key == '+') av_log_set_level(av_log_get_level()+10); if (key == '-') av_log_set_level(av_log_get_level()-10); - if (key == 's') qp_hist ^= 1; if (key == 'c' || key == 'C'){ char buf[4096], target[64], command[256], arg[256] = {0}; double time; @@ -3550,63 +809,15 @@ static int check_keyboard_interaction(int64_t cur_time) (n = sscanf(buf, "%63[^ ] %lf %255[^ ] %255[^\n]", target, &time, command, arg)) >= 3) { av_log(NULL, AV_LOG_DEBUG, "Processing command target:%s time:%f command:%s arg:%s", target, time, command, arg); - for (i = 0; i < nb_filtergraphs; i++) { - FilterGraph *fg = filtergraphs[i]; - if (fg->graph) { - if (time < 0) { - ret = avfilter_graph_send_command(fg->graph, target, command, arg, buf, sizeof(buf), - key == 'c' ? AVFILTER_CMD_FLAG_ONE : 0); - fprintf(stderr, "Command reply for stream %d: ret:%d res:\n%s", i, ret, buf); - } else if (key == 'c') { - fprintf(stderr, "Queuing commands only on filters supporting the specific command is unsupported\n"); - ret = AVERROR_PATCHWELCOME; - } else { - ret = avfilter_graph_queue_command(fg->graph, target, command, arg, 0, time); - if (ret < 0) - fprintf(stderr, "Queuing command failed with error %s\n", av_err2str(ret)); - } - } - } + for (i = 0; i < nb_filtergraphs; i++) + fg_send_command(filtergraphs[i], time, target, command, arg, + key == 'C'); } else { av_log(NULL, AV_LOG_ERROR, "Parse error, at least 3 arguments were expected, " "only %d given in string '%s'\n", n, buf); } } - if (key == 'd' || key == 'D'){ - int debug=0; - if(key == 'D') { - InputStream *ist = ist_iter(NULL); - - if (ist) - debug = ist->dec_ctx->debug << 1; - - if(!debug) debug = 1; - while (debug & FF_DEBUG_DCT_COEFF) //unsupported, would just crash - debug += debug; - }else{ - char buf[32]; - int k = 0; - i = 0; - set_tty_echo(1); - while ((k = read_key()) != '\n' && k != '\r' && i < sizeof(buf)-1) - if (k > 0) - buf[i++] = k; - buf[i] = 0; - set_tty_echo(0); - fprintf(stderr, "\n"); - if (k <= 0 || sscanf(buf, "%d", &debug)!=1) - fprintf(stderr,"error parsing debug value\n"); - } - for (InputStream *ist = ist_iter(NULL); ist; ist = ist_iter(ist)) - ist->dec_ctx->debug = debug; - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - if (ost->enc_ctx) - ost->enc_ctx->debug = debug; - } - if(debug) av_log_set_level(AV_LOG_DEBUG); - fprintf(stderr,"debug=%d\n", debug); - } if (key == '?'){ fprintf(stderr, "key function\n" "? show this help\n" @@ -3614,7 +825,6 @@ static int check_keyboard_interaction(int64_t cur_time) "- decrease verbosity\n" "c Send command to first matching filter supporting it\n" "C Send/Queue command to all matching filters\n" - "D cycle through available debug modes\n" "h dump packets/hex press to cycle through the 3 states\n" "q quit\n" "s Show QP histogram\n" @@ -3623,401 +833,21 @@ static int check_keyboard_interaction(int64_t cur_time) return 0; } -static int got_eagain(void) -{ - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) - if (ost->unavailable) - return 1; - return 0; -} - -static void reset_eagain(void) -{ - int i; - for (i = 0; i < nb_input_files; i++) - input_files[i]->eagain = 0; - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) - ost->unavailable = 0; -} - -static void decode_flush(InputFile *ifile) -{ - for (int i = 0; i < ifile->nb_streams; i++) { - InputStream *ist = ifile->streams[i]; - int ret; - - if (!ist->processing_needed) - continue; - - do { - ret = process_input_packet(ist, NULL, 1); - } while (ret > 0); - - if (ist->decoding_needed) { - /* report last frame duration to the demuxer thread */ - if (ist->par->codec_type == AVMEDIA_TYPE_AUDIO) { - LastFrameDuration dur; - - dur.stream_idx = i; - dur.duration = av_rescale_q(ist->nb_samples, - (AVRational){ 1, ist->dec_ctx->sample_rate}, - ist->st->time_base); - - av_thread_message_queue_send(ifile->audio_duration_queue, &dur, 0); - } - - avcodec_flush_buffers(ist->dec_ctx); - } - } -} - -static void ts_discontinuity_detect(InputFile *ifile, InputStream *ist, - AVPacket *pkt) -{ - const int fmt_is_discont = ifile->ctx->iformat->flags & AVFMT_TS_DISCONT; - int disable_discontinuity_correction = copy_ts; - int64_t pkt_dts = av_rescale_q_rnd(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q, - AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); - - if (copy_ts && ist->next_dts != AV_NOPTS_VALUE && - fmt_is_discont && ist->st->pts_wrap_bits < 60) { - int64_t wrap_dts = av_rescale_q_rnd(pkt->dts + (1LL<st->pts_wrap_bits), - ist->st->time_base, AV_TIME_BASE_Q, - AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); - if (FFABS(wrap_dts - ist->next_dts) < FFABS(pkt_dts - ist->next_dts)/10) - disable_discontinuity_correction = 0; - } - - if (ist->next_dts != AV_NOPTS_VALUE && !disable_discontinuity_correction) { - int64_t delta = pkt_dts - ist->next_dts; - if (fmt_is_discont) { - if (FFABS(delta) > 1LL * dts_delta_threshold * AV_TIME_BASE || - pkt_dts + AV_TIME_BASE/10 < FFMAX(ist->pts, ist->dts)) { - ifile->ts_offset_discont -= delta; - av_log(NULL, AV_LOG_DEBUG, - "timestamp discontinuity for stream #%d:%d " - "(id=%d, type=%s): %"PRId64", new offset= %"PRId64"\n", - ist->file_index, ist->st->index, ist->st->id, - av_get_media_type_string(ist->par->codec_type), - delta, ifile->ts_offset_discont); - pkt->dts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base); - if (pkt->pts != AV_NOPTS_VALUE) - pkt->pts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base); - } - } else { - if (FFABS(delta) > 1LL * dts_error_threshold * AV_TIME_BASE) { - av_log(NULL, AV_LOG_WARNING, "DTS %"PRId64", next:%"PRId64" st:%d invalid dropping\n", pkt->dts, ist->next_dts, pkt->stream_index); - pkt->dts = AV_NOPTS_VALUE; - } - if (pkt->pts != AV_NOPTS_VALUE){ - int64_t pkt_pts = av_rescale_q(pkt->pts, ist->st->time_base, AV_TIME_BASE_Q); - delta = pkt_pts - ist->next_dts; - if (FFABS(delta) > 1LL * dts_error_threshold * AV_TIME_BASE) { - av_log(NULL, AV_LOG_WARNING, "PTS %"PRId64", next:%"PRId64" invalid dropping st:%d\n", pkt->pts, ist->next_dts, pkt->stream_index); - pkt->pts = AV_NOPTS_VALUE; - } - } - } - } else if (ist->next_dts == AV_NOPTS_VALUE && !copy_ts && - fmt_is_discont && ifile->last_ts != AV_NOPTS_VALUE) { - int64_t delta = pkt_dts - ifile->last_ts; - if (FFABS(delta) > 1LL * dts_delta_threshold * AV_TIME_BASE) { - ifile->ts_offset_discont -= delta; - av_log(NULL, AV_LOG_DEBUG, - "Inter stream timestamp discontinuity %"PRId64", new offset= %"PRId64"\n", - delta, ifile->ts_offset_discont); - pkt->dts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base); - if (pkt->pts != AV_NOPTS_VALUE) - pkt->pts -= av_rescale_q(delta, AV_TIME_BASE_Q, ist->st->time_base); - } - } - - ifile->last_ts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q); -} - -static void ts_discontinuity_process(InputFile *ifile, InputStream *ist, - AVPacket *pkt) -{ - int64_t offset = av_rescale_q(ifile->ts_offset_discont, AV_TIME_BASE_Q, - ist->st->time_base); - - // apply previously-detected timestamp-discontinuity offset - // (to all streams, not just audio/video) - if (pkt->dts != AV_NOPTS_VALUE) - pkt->dts += offset; - if (pkt->pts != AV_NOPTS_VALUE) - pkt->pts += offset; - - // detect timestamp discontinuities for audio/video - if ((ist->par->codec_type == AVMEDIA_TYPE_VIDEO || - ist->par->codec_type == AVMEDIA_TYPE_AUDIO) && - pkt->dts != AV_NOPTS_VALUE) - ts_discontinuity_detect(ifile, ist, pkt); -} - /* - * Return - * - 0 -- one packet was read and processed - * - AVERROR(EAGAIN) -- no packets were available for selected file, - * this function should be called again - * - AVERROR_EOF -- this function should not be called again - */ -static int process_input(int file_index) -{ - InputFile *ifile = input_files[file_index]; - AVFormatContext *is; - InputStream *ist; - AVPacket *pkt; - int ret, i; - - is = ifile->ctx; - ret = ifile_get_packet(ifile, &pkt); - - if (ret == AVERROR(EAGAIN)) { - ifile->eagain = 1; - return ret; - } - if (ret == 1) { - /* the input file is looped: flush the decoders */ - decode_flush(ifile); - return AVERROR(EAGAIN); - } - if (ret < 0) { - if (ret != AVERROR_EOF) { - print_error(is->url, ret); - if (exit_on_error) - exit_program(1); - } - - for (i = 0; i < ifile->nb_streams; i++) { - ist = ifile->streams[i]; - if (ist->processing_needed) { - ret = process_input_packet(ist, NULL, 0); - if (ret>0) - return 0; - } - - /* mark all outputs that don't go through lavfi as finished */ - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - if (ost->ist == ist && - (!ost->enc_ctx || ost->enc_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE)) { - OutputFile *of = output_files[ost->file_index]; - of_output_packet(of, ost->pkt, ost, 1); - } - } - } - - ifile->eof_reached = 1; - return AVERROR(EAGAIN); - } - - reset_eagain(); - - ist = ifile->streams[pkt->stream_index]; - - ist->data_size += pkt->size; - ist->nb_packets++; - - if (ist->discard) - goto discard_packet; - - /* add the stream-global side data to the first packet */ - if (ist->nb_packets == 1) { - for (i = 0; i < ist->st->nb_side_data; i++) { - AVPacketSideData *src_sd = &ist->st->side_data[i]; - uint8_t *dst_data; - - if (src_sd->type == AV_PKT_DATA_DISPLAYMATRIX) - continue; - - if (av_packet_get_side_data(pkt, src_sd->type, NULL)) - continue; - - dst_data = av_packet_new_side_data(pkt, src_sd->type, src_sd->size); - if (!dst_data) - report_and_exit(AVERROR(ENOMEM)); - - memcpy(dst_data, src_sd->data, src_sd->size); - } - } - - // detect and try to correct for timestamp discontinuities - ts_discontinuity_process(ifile, ist, pkt); - - if (debug_ts) { - av_log(NULL, AV_LOG_INFO, "demuxer+ffmpeg -> ist_index:%d:%d type:%s pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s off:%s off_time:%s\n", - ifile->index, pkt->stream_index, - av_get_media_type_string(ist->par->codec_type), - av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ist->st->time_base), - av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ist->st->time_base), - av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &ist->st->time_base), - av_ts2str(input_files[ist->file_index]->ts_offset), - av_ts2timestr(input_files[ist->file_index]->ts_offset, &AV_TIME_BASE_Q)); - } - - sub2video_heartbeat(ist, pkt->pts); - - process_input_packet(ist, pkt, 0); - -discard_packet: - av_packet_free(&pkt); - - return 0; -} - -/** - * Perform a step of transcoding for the specified filter graph. - * - * @param[in] graph filter graph to consider - * @param[out] best_ist input stream where a frame would allow to continue - * @return 0 for success, <0 for error - */ -static int transcode_from_filter(FilterGraph *graph, InputStream **best_ist) -{ - int i, ret; - int nb_requests, nb_requests_max = 0; - InputFilter *ifilter; - InputStream *ist; - - *best_ist = NULL; - ret = avfilter_graph_request_oldest(graph->graph); - if (ret >= 0) - return reap_filters(0); - - if (ret == AVERROR_EOF) { - ret = reap_filters(1); - for (i = 0; i < graph->nb_outputs; i++) - close_output_stream(graph->outputs[i]->ost); - return ret; - } - if (ret != AVERROR(EAGAIN)) - return ret; - - for (i = 0; i < graph->nb_inputs; i++) { - ifilter = graph->inputs[i]; - ist = ifilter->ist; - if (input_files[ist->file_index]->eagain || - input_files[ist->file_index]->eof_reached) - continue; - nb_requests = av_buffersrc_get_nb_failed_requests(ifilter->filter); - if (nb_requests > nb_requests_max) { - nb_requests_max = nb_requests; - *best_ist = ist; - } - } - - if (!*best_ist) - for (i = 0; i < graph->nb_outputs; i++) - graph->outputs[i]->ost->unavailable = 1; - - return 0; -} - -/** - * Run a single step of transcoding. - * - * @return 0 for success, <0 for error + * The following code is the main loop of the file converter */ -static int transcode_step(void) +static int transcode(Scheduler *sch) { - OutputStream *ost; - InputStream *ist = NULL; - int ret; - - ost = choose_output(); - if (!ost) { - if (got_eagain()) { - reset_eagain(); - av_usleep(10000); - return 0; - } - av_log(NULL, AV_LOG_VERBOSE, "No more inputs to read from, finishing.\n"); - return AVERROR_EOF; - } - - if (ost->filter && !ost->filter->graph->graph) { - if (ifilter_has_all_input_formats(ost->filter->graph)) { - ret = configure_filtergraph(ost->filter->graph); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error reinitializing filters!\n"); - return ret; - } - } - } - - if (ost->filter && ost->filter->graph->graph) { - /* - * Similar case to the early audio initialization in reap_filters. - * Audio is special in ffmpeg.c currently as we depend on lavfi's - * audio frame buffering/creation to get the output audio frame size - * in samples correct. The audio frame size for the filter chain is - * configured during the output stream initialization. - * - * Apparently avfilter_graph_request_oldest (called in - * transcode_from_filter just down the line) peeks. Peeking already - * puts one frame "ready to be given out", which means that any - * update in filter buffer sink configuration afterwards will not - * help us. And yes, even if it would be utilized, - * av_buffersink_get_samples is affected, as it internally utilizes - * the same early exit for peeked frames. - * - * In other words, if avfilter_graph_request_oldest would not make - * further filter chain configuration or usage of - * av_buffersink_get_samples useless (by just causing the return - * of the peeked AVFrame as-is), we could get rid of this additional - * early encoder initialization. - */ - if (av_buffersink_get_type(ost->filter->filter) == AVMEDIA_TYPE_AUDIO) - init_output_stream_wrapper(ost, NULL, 1); - - if ((ret = transcode_from_filter(ost->filter->graph, &ist)) < 0) - return ret; - if (!ist) - return 0; - } else if (ost->filter) { - int i; - for (i = 0; i < ost->filter->graph->nb_inputs; i++) { - InputFilter *ifilter = ost->filter->graph->inputs[i]; - if (!ifilter->ist->got_output && !input_files[ifilter->ist->file_index]->eof_reached) { - ist = ifilter->ist; - break; - } - } - if (!ist) { - ost->inputs_done = 1; - return 0; - } - } else { - ist = ost->ist; - av_assert0(ist); - } - - ret = process_input(ist->file_index); - if (ret == AVERROR(EAGAIN)) { - if (input_files[ist->file_index]->eagain) - ost->unavailable = 1; - return 0; - } - - if (ret < 0) - return ret == AVERROR_EOF ? 0 : ret; + int ret = 0; + int64_t timer_start, transcode_ts = 0; - return reap_filters(0); -} + print_stream_maps(); -/* - * The following code is the main loop of the file converter - */ -static int transcode(void) -{ - int ret, i; - InputStream *ist; - int64_t timer_start; - int64_t total_packets_written = 0; + atomic_store(&transcode_init_done, 1); - ret = transcode_init(); + ret = sch_start(sch); if (ret < 0) - goto fail; + return ret; if (stdin_interaction) { av_log(NULL, AV_LOG_INFO, "Press [q] to stop, [?] for help\n"); @@ -4025,7 +855,7 @@ static int transcode(void) timer_start = av_gettime_relative(); - while (!received_sigterm) { + while (!sch_wait(sch, stats_period, &transcode_ts)) { int64_t cur_time= av_gettime_relative(); /* if 'q' pressed, exits */ @@ -4033,64 +863,23 @@ static int transcode(void) if (check_keyboard_interaction(cur_time) < 0) break; - /* check if there's any stream where output is still needed */ - if (!need_output()) { - av_log(NULL, AV_LOG_VERBOSE, "No more output streams to write to, finishing.\n"); - break; - } - - ret = transcode_step(); - if (ret < 0 && ret != AVERROR_EOF) { - av_log(NULL, AV_LOG_ERROR, "Error while filtering: %s\n", av_err2str(ret)); - break; - } - /* dump report by using the output first video and audio streams */ - print_report(0, timer_start, cur_time); - } - - /* at the end of stream, we must flush the decoder buffers */ - for (ist = ist_iter(NULL); ist; ist = ist_iter(ist)) { - if (!input_files[ist->file_index]->eof_reached) { - process_input_packet(ist, NULL, 0); - } + print_report(0, timer_start, cur_time, transcode_ts); } - flush_encoders(); - term_exit(); + ret = sch_stop(sch, &transcode_ts); /* write the trailer if needed */ - for (i = 0; i < nb_output_files; i++) { - ret = of_write_trailer(output_files[i]); - if (ret < 0 && exit_on_error) - exit_program(1); - } - - /* dump report by using the first video and audio streams */ - print_report(1, timer_start, av_gettime_relative()); - - /* close each encoder */ - for (OutputStream *ost = ost_iter(NULL); ost; ost = ost_iter(ost)) { - uint64_t packets_written; - packets_written = atomic_load(&ost->packets_written); - total_packets_written += packets_written; - if (!packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM)) { - av_log(ost, AV_LOG_FATAL, "Empty output\n"); - exit_program(1); - } - } - - if (!total_packets_written && (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT)) { - av_log(NULL, AV_LOG_FATAL, "Empty output\n"); - exit_program(1); + for (int i = 0; i < nb_output_files; i++) { + int err = of_write_trailer(output_files[i]); + ret = err_merge(ret, err); } - hw_device_free_all(); + term_exit(); - /* finished ! */ - ret = 0; + /* dump report by using the first video and audio streams */ + print_report(1, timer_start, av_gettime_relative(), transcode_ts); - fail: return ret; } @@ -4140,13 +929,13 @@ static int64_t getmaxrss(void) int main(int argc, char **argv) { + Scheduler *sch = NULL; + int ret; BenchmarkTimeStamps ti; init_dynload(); - register_exit(ffmpeg_cleanup); - setvbuf(stderr,NULL,_IONBF,0); /* win32 runtime needs this */ av_log_set_flags(AV_LOG_SKIP_REPEATED); @@ -4159,27 +948,33 @@ int main(int argc, char **argv) show_banner(argc, argv, options); + sch = sch_alloc(); + if (!sch) { + ret = AVERROR(ENOMEM); + goto finish; + } + /* parse options and open all input/output files */ - ret = ffmpeg_parse_options(argc, argv); + ret = ffmpeg_parse_options(argc, argv, sch); if (ret < 0) - exit_program(1); + goto finish; if (nb_output_files <= 0 && nb_input_files == 0) { show_usage(); av_log(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name); - exit_program(1); + ret = 1; + goto finish; } - /* file converter / grab */ if (nb_output_files <= 0) { av_log(NULL, AV_LOG_FATAL, "At least one output file must be specified\n"); - exit_program(1); + ret = 1; + goto finish; } current_time = ti = get_benchmark_time_stamps(); - if (transcode() < 0) - exit_program(1); - if (do_benchmark) { + ret = transcode(sch); + if (ret >= 0 && do_benchmark) { int64_t utime, stime, rtime; current_time = get_benchmark_time_stamps(); utime = current_time.user_usec - ti.user_usec; @@ -4189,11 +984,17 @@ int main(int argc, char **argv) "bench: utime=%0.3fs stime=%0.3fs rtime=%0.3fs\n", utime / 1000000.0, stime / 1000000.0, rtime / 1000000.0); } - av_log(NULL, AV_LOG_DEBUG, "%"PRIu64" frames successfully decoded, %"PRIu64" decoding errors\n", - decode_error_stat[0], decode_error_stat[1]); - if ((decode_error_stat[0] + decode_error_stat[1]) * max_error_rate < decode_error_stat[1]) - exit_program(69); - exit_program(received_nb_signals ? 255 : main_return_code); - return main_return_code; + ret = received_nb_signals ? 255 : + (ret == FFMPEG_ERROR_RATE_EXCEEDED) ? 69 : ret; + +finish: + if (ret == AVERROR_EXIT) + ret = 0; + + ffmpeg_cleanup(ret); + + sch_free(&sch); + + return ret; } diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index f1412f6446b..6394cca1e77 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -27,6 +27,7 @@ #include #include "cmdutils.h" +#include "ffmpeg_sched.h" #include "sync_queue.h" #include "libavformat/avformat.h" @@ -50,10 +51,16 @@ #include "libswresample/swresample.h" // deprecated features -#define FFMPEG_OPT_PSNR 1 -#define FFMPEG_OPT_MAP_CHANNEL 1 -#define FFMPEG_OPT_MAP_SYNC 1 -#define FFMPEG_ROTATION_METADATA 1 +#define FFMPEG_OPT_QPHIST 1 +#define FFMPEG_OPT_ADRIFT_THRESHOLD 1 +#define FFMPEG_OPT_ENC_TIME_BASE_NUM 1 +#define FFMPEG_OPT_TOP 1 +#define FFMPEG_OPT_FORCE_KF_SOURCE_NO_DROP 1 +#define FFMPEG_OPT_VSYNC_DROP 1 +#define FFMPEG_OPT_VSYNC 1 +#define FFMPEG_OPT_FILTER_SCRIPT 1 + +#define FFMPEG_ERROR_RATE_EXCEEDED FFERRTAG('E', 'R', 'E', 'D') enum VideoSyncMethod { VSYNC_AUTO = -1, @@ -61,10 +68,15 @@ enum VideoSyncMethod { VSYNC_CFR, VSYNC_VFR, VSYNC_VSCFR, +#if FFMPEG_OPT_VSYNC_DROP VSYNC_DROP, +#endif }; -#define MAX_STREAMS 1024 /* arbitrary sanity check value */ +enum EncTimeBase { + ENC_TIME_BASE_DEMUX = -1, + ENC_TIME_BASE_FILTER = -2, +}; enum HWAccelID { HWACCEL_NONE = 0, @@ -72,6 +84,28 @@ enum HWAccelID { HWACCEL_GENERIC, }; +enum FrameOpaque { + FRAME_OPAQUE_SUB_HEARTBEAT = 1, + FRAME_OPAQUE_EOF, + FRAME_OPAQUE_SEND_COMMAND, +}; + +enum PacketOpaque { + PKT_OPAQUE_SUB_HEARTBEAT = 1, + PKT_OPAQUE_FIX_SUB_DURATION, +}; + +enum LatencyProbe { + LATENCY_PROBE_DEMUX, + LATENCY_PROBE_DEC_PRE, + LATENCY_PROBE_DEC_POST, + LATENCY_PROBE_FILTER_PRE, + LATENCY_PROBE_FILTER_POST, + LATENCY_PROBE_ENC_PRE, + LATENCY_PROBE_ENC_POST, + LATENCY_PROBE_NB, +}; + typedef struct HWDevice { const char *name; enum AVHWDeviceType type; @@ -86,13 +120,6 @@ typedef struct StreamMap { char *linklabel; /* name of an output link, for mapping lavfi outputs */ } StreamMap; -#if FFMPEG_OPT_MAP_CHANNEL -typedef struct { - int file_idx, stream_idx, channel_idx; // input - int ofile_idx, ostream_idx; // output -} AudioChannelMap; -#endif - typedef struct OptionsContext { OptionGroup *g; @@ -102,53 +129,36 @@ typedef struct OptionsContext { int seek_timestamp; const char *format; - SpecifierOpt *codec_names; - int nb_codec_names; - SpecifierOpt *audio_ch_layouts; - int nb_audio_ch_layouts; - SpecifierOpt *audio_channels; - int nb_audio_channels; - SpecifierOpt *audio_sample_rate; - int nb_audio_sample_rate; - SpecifierOpt *frame_rates; - int nb_frame_rates; - SpecifierOpt *max_frame_rates; - int nb_max_frame_rates; - SpecifierOpt *frame_sizes; - int nb_frame_sizes; - SpecifierOpt *frame_pix_fmts; - int nb_frame_pix_fmts; + SpecifierOptList codec_names; + SpecifierOptList audio_ch_layouts; + SpecifierOptList audio_channels; + SpecifierOptList audio_sample_rate; + SpecifierOptList frame_rates; + SpecifierOptList max_frame_rates; + SpecifierOptList frame_sizes; + SpecifierOptList frame_pix_fmts; /* input options */ int64_t input_ts_offset; int loop; int rate_emu; float readrate; + double readrate_initial_burst; int accurate_seek; int thread_queue_size; int input_sync_ref; int find_stream_info; - SpecifierOpt *ts_scale; - int nb_ts_scale; - SpecifierOpt *dump_attachment; - int nb_dump_attachment; - SpecifierOpt *hwaccels; - int nb_hwaccels; - SpecifierOpt *hwaccel_devices; - int nb_hwaccel_devices; - SpecifierOpt *hwaccel_output_formats; - int nb_hwaccel_output_formats; - SpecifierOpt *autorotate; - int nb_autorotate; + SpecifierOptList ts_scale; + SpecifierOptList dump_attachment; + SpecifierOptList hwaccels; + SpecifierOptList hwaccel_devices; + SpecifierOptList hwaccel_output_formats; + SpecifierOptList autorotate; /* output options */ StreamMap *stream_maps; int nb_stream_maps; -#if FFMPEG_OPT_MAP_CHANNEL - AudioChannelMap *audio_channel_maps; /* one info entry per -map_channel */ - int nb_audio_channel_maps; /* number of (valid) -map_channel settings */ -#endif const char **attachments; int nb_attachments; @@ -168,163 +178,114 @@ typedef struct OptionsContext { int subtitle_disable; int data_disable; - /* indexed by output file stream index */ - int *streamid_map; - int nb_streamid_map; - - SpecifierOpt *metadata; - int nb_metadata; - SpecifierOpt *max_frames; - int nb_max_frames; - SpecifierOpt *bitstream_filters; - int nb_bitstream_filters; - SpecifierOpt *codec_tags; - int nb_codec_tags; - SpecifierOpt *sample_fmts; - int nb_sample_fmts; - SpecifierOpt *qscale; - int nb_qscale; - SpecifierOpt *forced_key_frames; - int nb_forced_key_frames; - SpecifierOpt *fps_mode; - int nb_fps_mode; - SpecifierOpt *force_fps; - int nb_force_fps; - SpecifierOpt *frame_aspect_ratios; - int nb_frame_aspect_ratios; - SpecifierOpt *display_rotations; - int nb_display_rotations; - SpecifierOpt *display_hflips; - int nb_display_hflips; - SpecifierOpt *display_vflips; - int nb_display_vflips; - SpecifierOpt *rc_overrides; - int nb_rc_overrides; - SpecifierOpt *intra_matrices; - int nb_intra_matrices; - SpecifierOpt *inter_matrices; - int nb_inter_matrices; - SpecifierOpt *chroma_intra_matrices; - int nb_chroma_intra_matrices; - SpecifierOpt *top_field_first; - int nb_top_field_first; - SpecifierOpt *metadata_map; - int nb_metadata_map; - SpecifierOpt *presets; - int nb_presets; - SpecifierOpt *copy_initial_nonkeyframes; - int nb_copy_initial_nonkeyframes; - SpecifierOpt *copy_prior_start; - int nb_copy_prior_start; - SpecifierOpt *filters; - int nb_filters; - SpecifierOpt *filter_scripts; - int nb_filter_scripts; - SpecifierOpt *reinit_filters; - int nb_reinit_filters; - SpecifierOpt *fix_sub_duration; - int nb_fix_sub_duration; - SpecifierOpt *fix_sub_duration_heartbeat; - int nb_fix_sub_duration_heartbeat; - SpecifierOpt *canvas_sizes; - int nb_canvas_sizes; - SpecifierOpt *pass; - int nb_pass; - SpecifierOpt *passlogfiles; - int nb_passlogfiles; - SpecifierOpt *max_muxing_queue_size; - int nb_max_muxing_queue_size; - SpecifierOpt *muxing_queue_data_threshold; - int nb_muxing_queue_data_threshold; - SpecifierOpt *guess_layout_max; - int nb_guess_layout_max; - SpecifierOpt *apad; - int nb_apad; - SpecifierOpt *discard; - int nb_discard; - SpecifierOpt *disposition; - int nb_disposition; - SpecifierOpt *program; - int nb_program; - SpecifierOpt *time_bases; - int nb_time_bases; - SpecifierOpt *enc_time_bases; - int nb_enc_time_bases; - SpecifierOpt *autoscale; - int nb_autoscale; - SpecifierOpt *bits_per_raw_sample; - int nb_bits_per_raw_sample; - SpecifierOpt *enc_stats_pre; - int nb_enc_stats_pre; - SpecifierOpt *enc_stats_post; - int nb_enc_stats_post; - SpecifierOpt *mux_stats; - int nb_mux_stats; - SpecifierOpt *enc_stats_pre_fmt; - int nb_enc_stats_pre_fmt; - SpecifierOpt *enc_stats_post_fmt; - int nb_enc_stats_post_fmt; - SpecifierOpt *mux_stats_fmt; - int nb_mux_stats_fmt; + // keys are stream indices + AVDictionary *streamid; + + SpecifierOptList metadata; + SpecifierOptList max_frames; + SpecifierOptList bitstream_filters; + SpecifierOptList codec_tags; + SpecifierOptList sample_fmts; + SpecifierOptList qscale; + SpecifierOptList forced_key_frames; + SpecifierOptList fps_mode; + SpecifierOptList force_fps; + SpecifierOptList frame_aspect_ratios; + SpecifierOptList display_rotations; + SpecifierOptList display_hflips; + SpecifierOptList display_vflips; + SpecifierOptList rc_overrides; + SpecifierOptList intra_matrices; + SpecifierOptList inter_matrices; + SpecifierOptList chroma_intra_matrices; +#if FFMPEG_OPT_TOP + SpecifierOptList top_field_first; +#endif + SpecifierOptList metadata_map; + SpecifierOptList presets; + SpecifierOptList copy_initial_nonkeyframes; + SpecifierOptList copy_prior_start; + SpecifierOptList filters; +#if FFMPEG_OPT_FILTER_SCRIPT + SpecifierOptList filter_scripts; +#endif + SpecifierOptList reinit_filters; + SpecifierOptList fix_sub_duration; + SpecifierOptList fix_sub_duration_heartbeat; + SpecifierOptList canvas_sizes; + SpecifierOptList pass; + SpecifierOptList passlogfiles; + SpecifierOptList max_muxing_queue_size; + SpecifierOptList muxing_queue_data_threshold; + SpecifierOptList guess_layout_max; + SpecifierOptList apad; + SpecifierOptList discard; + SpecifierOptList disposition; + SpecifierOptList program; + SpecifierOptList stream_groups; + SpecifierOptList time_bases; + SpecifierOptList enc_time_bases; + SpecifierOptList autoscale; + SpecifierOptList bits_per_raw_sample; + SpecifierOptList enc_stats_pre; + SpecifierOptList enc_stats_post; + SpecifierOptList mux_stats; + SpecifierOptList enc_stats_pre_fmt; + SpecifierOptList enc_stats_post_fmt; + SpecifierOptList mux_stats_fmt; } OptionsContext; -typedef struct InputFilter { - AVFilterContext *filter; - struct InputStream *ist; - struct FilterGraph *graph; - uint8_t *name; - enum AVMediaType type; // AVMEDIA_TYPE_SUBTITLE for sub2video +enum IFilterFlags { + IFILTER_FLAG_AUTOROTATE = (1 << 0), + IFILTER_FLAG_REINIT = (1 << 1), + IFILTER_FLAG_CFR = (1 << 2), +}; - AVFifo *frame_queue; +typedef struct InputFilterOptions { + int64_t trim_start_us; + int64_t trim_end_us; - // parameters configured for this input - int format; + uint8_t *name; + + /* When IFILTER_FLAG_CFR is set, the stream is guaranteed to be CFR with + * this framerate. + * + * Otherwise, this is an estimate that should not be relied upon to be + * accurate */ + AVRational framerate; - int width, height; - AVRational sample_aspect_ratio; + int sub2video_width; + int sub2video_height; - int sample_rate; - AVChannelLayout ch_layout; + // a combination of IFILTER_FLAG_* + unsigned flags; - AVBufferRef *hw_frames_ctx; - int32_t *displaymatrix; + AVFrame *fallback; +} InputFilterOptions; - int eof; +typedef struct InputFilter { + struct FilterGraph *graph; + uint8_t *name; } InputFilter; typedef struct OutputFilter { - AVFilterContext *filter; struct OutputStream *ost; struct FilterGraph *graph; uint8_t *name; - /* temporary storage until stream maps are processed */ - AVFilterInOut *out_tmp; + /* for filters that are not yet bound to an output stream, + * this stores the output linklabel, if any */ + uint8_t *linklabel; + enum AVMediaType type; - /* desired output stream properties */ - int width, height; - AVRational frame_rate; - int format; - int sample_rate; - AVChannelLayout ch_layout; - - // those are only set if no format is specified and the encoder gives us multiple options - // They point directly to the relevant lists of the encoder. - const int *formats; - const AVChannelLayout *ch_layouts; - const int *sample_rates; + atomic_uint_least64_t nb_frames_dup; + atomic_uint_least64_t nb_frames_drop; } OutputFilter; typedef struct FilterGraph { + const AVClass *class; int index; - const char *graph_desc; - - AVFilterGraph *graph; - int reconfiguration; - // true when the filtergraph contains only meta filters - // that do not modify the frame data - int is_meta; InputFilter **inputs; int nb_inputs; @@ -332,156 +293,120 @@ typedef struct FilterGraph { int nb_outputs; } FilterGraph; +enum DecoderFlags { + DECODER_FLAG_FIX_SUB_DURATION = (1 << 0), + // input timestamps are unreliable (guessed by demuxer) + DECODER_FLAG_TS_UNRELIABLE = (1 << 1), + // decoder should override timestamps by fixed framerate + // from DecoderOpts.framerate + DECODER_FLAG_FRAMERATE_FORCED = (1 << 2), +#if FFMPEG_OPT_TOP + DECODER_FLAG_TOP_FIELD_FIRST = (1 << 3), +#endif + DECODER_FLAG_SEND_END_TS = (1 << 4), +}; + +typedef struct DecoderOpts { + int flags; + + char *name; + void *log_parent; + + const AVCodec *codec; + const AVCodecParameters *par; + + /* hwaccel options */ + enum HWAccelID hwaccel_id; + enum AVHWDeviceType hwaccel_device_type; + char *hwaccel_device; + enum AVPixelFormat hwaccel_output_format; + + AVRational time_base; + + // Either forced (when DECODER_FLAG_FRAMERATE_FORCED is set) or + // estimated (otherwise) video framerate. + AVRational framerate; +} DecoderOpts; + +typedef struct Decoder { + const AVClass *class; + + enum AVMediaType type; + + const uint8_t *subtitle_header; + int subtitle_header_size; + + // number of frames/samples retrieved from the decoder + uint64_t frames_decoded; + uint64_t samples_decoded; + uint64_t decode_errors; +} Decoder; + typedef struct InputStream { - int file_index; - AVStream *st; - int discard; /* true if stream data should be discarded */ - int user_set_discard; - int decoding_needed; /* non zero if the packets must be decoded in 'raw_fifo', see DECODING_FOR_* */ -#define DECODING_FOR_OST 1 -#define DECODING_FOR_FILTER 2 - int processing_needed; /* non zero if the packets must be processed */ - // should attach FrameData as opaque_ref after decoding - int want_frame_data; + const AVClass *class; + + /* parent source */ + struct InputFile *file; + + int index; + + AVStream *st; + int user_set_discard; /** * Codec parameters - to be used by the decoding/streamcopy code. * st->codecpar should not be accessed, because it may be modified * concurrently by the demuxing thread. */ - AVCodecParameters *par; - AVCodecContext *dec_ctx; - const AVCodec *dec; - AVFrame *decoded_frame; - AVPacket *pkt; - - AVRational framerate_guessed; - - int64_t prev_pkt_pts; - int64_t start; /* time when read started */ - /* predicted dts of the next packet read for this stream or (when there are - * several frames in a packet) of the next frame in current packet (in AV_TIME_BASE units) */ - int64_t next_dts; - int64_t first_dts; ///< dts of the first packet read for this stream (in AV_TIME_BASE units) - int64_t dts; ///< dts of the last packet read for this stream (in AV_TIME_BASE units) - - int64_t next_pts; ///< synthetic pts for the next decode frame (in AV_TIME_BASE units) - int64_t pts; ///< current pts of the decoded frame (in AV_TIME_BASE units) - int wrap_correction_done; - - // the value of AVCodecParserContext.repeat_pict from the AVStream parser - // for the last packet returned from ifile_get_packet() - // -1 if unknown - // FIXME: this is a hack, the avstream parser should not be used - int last_pkt_repeat_pict; - - int64_t filter_in_rescale_delta_last; - - int64_t min_pts; /* pts with the smallest value in a current stream */ - int64_t max_pts; /* pts with the higher value in a current stream */ - - // when forcing constant input framerate through -r, - // this contains the pts that will be given to the next decoded frame - int64_t cfr_next_pts; - - int64_t nb_samples; /* number of samples in the last decoded audio frame before looping */ - - double ts_scale; - int saw_first_ts; - AVDictionary *decoder_opts; - AVRational framerate; /* framerate forced with -r */ - int top_field_first; - int guess_layout_max; - - int autorotate; - - int fix_sub_duration; - struct { /* previous decoded subtitle and related variables */ - int got_output; - int ret; - AVSubtitle subtitle; - } prev_sub; - - struct sub2video { - int64_t last_pts; - int64_t end_pts; - AVFifo *sub_queue; ///< queue of AVSubtitle* before filter init - AVFrame *frame; - int w, h; - unsigned int initialize; ///< marks if sub2video_update should force an initialization - } sub2video; - - /* decoded data from this stream goes into all those filters - * currently video and audio only */ - InputFilter **filters; - int nb_filters; - - int reinit_filters; - - /* hwaccel options */ - enum HWAccelID hwaccel_id; - enum AVHWDeviceType hwaccel_device_type; - char *hwaccel_device; - enum AVPixelFormat hwaccel_output_format; + AVCodecParameters *par; + Decoder *decoder; + const AVCodec *dec; + + /* framerate forced with -r */ + AVRational framerate; +#if FFMPEG_OPT_TOP + int top_field_first; +#endif - int (*hwaccel_retrieve_data)(AVCodecContext *s, AVFrame *frame); - enum AVPixelFormat hwaccel_pix_fmt; + int autorotate; - /* stats */ - // combined size of all the packets read - uint64_t data_size; - /* number of packets successfully read for this stream */ - uint64_t nb_packets; - // number of frames/samples retrieved from the decoder - uint64_t frames_decoded; - uint64_t samples_decoded; + int fix_sub_duration; - int64_t *dts_buffer; - int nb_dts_buffer; + /* decoded data from this stream goes into all those filters + * currently video and audio only */ + InputFilter **filters; + int nb_filters; - int got_output; + /* + * Output targets that do not go through lavfi, i.e. subtitles or + * streamcopy. Those two cases are distinguished by the OutputStream + * having an encoder or not. + */ + struct OutputStream **outputs; + int nb_outputs; } InputStream; -typedef struct LastFrameDuration { - int stream_idx; - int64_t duration; -} LastFrameDuration; - typedef struct InputFile { - int index; + const AVClass *class; + + int index; AVFormatContext *ctx; - int eof_reached; /* true if eof reached */ - int eagain; /* true if last read attempt returned EAGAIN */ - int64_t input_ts_offset; - int input_sync_ref; + int64_t input_ts_offset; + int input_sync_ref; /** * Effective format start time based on enabled streams. */ - int64_t start_time_effective; - int64_t ts_offset; - /** - * Extra timestamp offset added by discontinuity handling. - */ - int64_t ts_offset_discont; - int64_t last_ts; - int64_t start_time; /* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */ - int64_t recording_time; + int64_t start_time_effective; + int64_t ts_offset; + /* user-specified start time in AV_TIME_BASE or AV_NOPTS_VALUE */ + int64_t start_time; /* streams that ffmpeg is aware of; * there may be extra streams in ctx that are not mapped to an InputStream * if new streams appear dynamically during demuxing */ - InputStream **streams; - int nb_streams; - - int rate_emu; - float readrate; - int accurate_seek; - - /* when looping the input file, this queue is used by decoders to report - * the last frame duration back to the demuxer thread */ - AVThreadMessageQueue *audio_duration_queue; - int audio_duration_queue_size; + InputStream **streams; + int nb_streams; } InputFile; enum forced_keyframes_const { @@ -515,6 +440,7 @@ enum EncStatsType { ENC_STATS_PKT_SIZE, ENC_STATS_BITRATE, ENC_STATS_AVG_BITRATE, + ENC_STATS_KEYFRAME, }; typedef struct EncStatsComponent { @@ -529,6 +455,9 @@ typedef struct EncStats { int nb_components; AVIOContext *io; + + pthread_mutex_t lock; + int lock_initialized; } EncStats; extern const char *const forced_keyframes_const_names[]; @@ -540,7 +469,9 @@ typedef enum { enum { KF_FORCE_SOURCE = 1, +#if FFMPEG_OPT_FORCE_KF_SOURCE_NO_DROP KF_FORCE_SOURCE_NO_DROP = 2, +#endif }; typedef struct KeyframeForceCtx { @@ -559,44 +490,35 @@ typedef struct KeyframeForceCtx { int dropped_keyframe; } KeyframeForceCtx; +typedef struct Encoder Encoder; + typedef struct OutputStream { const AVClass *class; - int file_index; /* file index */ + enum AVMediaType type; + + /* parent muxer */ + struct OutputFile *file; + int index; /* stream index in the output file */ + /** + * Codec parameters for packets submitted to the muxer (i.e. before + * bitstream filtering, if any). + */ + AVCodecParameters *par_in; + /* input stream that is the source for this output stream; * may be NULL for streams with no well-defined source, e.g. * attachments or outputs from complex filtergraphs */ InputStream *ist; AVStream *st; /* stream in the output file */ - /* number of frames emitted by the video-encoding sync code */ - int64_t vsync_frame_number; - /* predicted pts of the next frame to be encoded - * audio/video encoding only */ - int64_t next_pts; - /* dts of the last packet sent to the muxing queue, in AV_TIME_BASE_Q */ - int64_t last_mux_dts; - /* pts of the last frame received from the filters, in AV_TIME_BASE_Q */ - int64_t last_filter_pts; - - // timestamp from which the streamcopied streams should start, - // in AV_TIME_BASE_Q; - // everything before it should be discarded - int64_t ts_copy_start; - - // the timebase of the packets sent to the muxer - AVRational mux_timebase; + AVRational enc_timebase; + Encoder *enc; AVCodecContext *enc_ctx; - AVFrame *filtered_frame; - AVFrame *last_frame; - AVFrame *sq_frame; - AVPacket *pkt; - int64_t last_dropped; - int64_t last_nb0_frames[3]; /* video only */ AVRational frame_rate; @@ -604,80 +526,40 @@ typedef struct OutputStream { enum VideoSyncMethod vsync_method; int is_cfr; int force_fps; +#if FFMPEG_OPT_TOP int top_field_first; -#if FFMPEG_ROTATION_METADATA - int rotate_overridden; #endif int autoscale; int bitexact; int bits_per_raw_sample; -#if FFMPEG_ROTATION_METADATA - double rotate_override_value; -#endif AVRational frame_aspect_ratio; KeyframeForceCtx kf; - /* audio only */ -#if FFMPEG_OPT_MAP_CHANNEL - int *audio_channels_map; /* list of the channels id to pick from the source stream */ - int audio_channels_mapped; /* number of channels in audio_channels_map */ -#endif - char *logfile_prefix; FILE *logfile; OutputFilter *filter; - char *avfilter; - char *filters; ///< filtergraph associated to the -filter option - char *filters_script; ///< filtergraph script associated to the -filter_script option AVDictionary *encoder_opts; AVDictionary *sws_dict; AVDictionary *swr_opts; char *apad; - OSTFinished finished; /* no more packets should be written for this stream */ - int unavailable; /* true if the steram is unavailable (possibly temporarily) */ - - // init_output_stream() has been called for this stream - // The encoder and the bitstream filters have been initialized and the stream - // parameters are set in the AVStream. - int initialized; - int inputs_done; - - const char *attachment_filename; - int streamcopy_started; - int copy_initial_nonkeyframes; - int copy_prior_start; + char *attachment_filename; int keep_pix_fmt; /* stats */ - // combined size of all the packets sent to the muxer - uint64_t data_size_mux; - // combined size of all the packets received from the encoder - uint64_t data_size_enc; // number of packets send to the muxer atomic_uint_least64_t packets_written; // number of frames/samples sent to the encoder uint64_t frames_encoded; uint64_t samples_encoded; - // number of packets received from the encoder - uint64_t packets_encoded; /* packet quality factor */ - int quality; - - /* packet picture type */ - int pict_type; - - /* frame encode sum of squared error values */ - int64_t error[4]; - - int sq_idx_encode; - int sq_idx_mux; + atomic_int quality; EncStats enc_stats_pre; EncStats enc_stats_post; @@ -700,8 +582,6 @@ typedef struct OutputFile { OutputStream **streams; int nb_streams; - SyncQueue *sq_encode; - int64_t recording_time; ///< desired length of the resulting file in microseconds == AV_TIME_BASE units int64_t start_time; ///< start time in microseconds == AV_TIME_BASE units @@ -709,6 +589,29 @@ typedef struct OutputFile { int bitexact; } OutputFile; +// optionally attached as opaque_ref to decoded AVFrames +typedef struct FrameData { + // demuxer-estimated dts in AV_TIME_BASE_Q, + // to be used when real dts is missing + int64_t dts_est; + + // properties that come from the decoder + struct { + uint64_t frame_num; + + int64_t pts; + AVRational tb; + } dec; + + AVRational frame_rate_filter; + + int bits_per_raw_sample; + + int64_t wallclock[LATENCY_PROBE_NB]; + + AVCodecParameters *par_enc; +} FrameData; + extern InputFile **input_files; extern int nb_input_files; @@ -718,10 +621,12 @@ extern int nb_output_files; extern FilterGraph **filtergraphs; extern int nb_filtergraphs; +// standalone decoders (not tied to demuxed streams) +extern Decoder **decoders; +extern int nb_decoders; + extern char *vstats_filename; -extern char *sdp_filename; -extern float audio_drift_threshold; extern float dts_delta_threshold; extern float dts_error_threshold; @@ -739,7 +644,6 @@ extern int exit_on_error; extern int abort_on_flags; extern int print_stats; extern int64_t stats_period; -extern int qp_hist; extern int stdin_interaction; extern AVIOContext *progress_avio; extern float max_error_rate; @@ -754,17 +658,14 @@ extern const AVIOInterruptCB int_cb; extern const OptionDef options[]; extern HWDevice *filter_hw_device; -extern unsigned nb_output_dumped; -extern int main_return_code; +extern atomic_uint nb_output_dumped; extern int ignore_unknown_streams; extern int copy_unknown_streams; extern int recast_media; -#if FFMPEG_OPT_PSNR -extern int do_psnr; -#endif +extern FILE *vstats_file; void term_init(void); void term_exit(void); @@ -772,40 +673,101 @@ void term_exit(void); void show_usage(void); void remove_avoptions(AVDictionary **a, AVDictionary *b); -void assert_avoptions(AVDictionary *m); +int check_avoptions(AVDictionary *m); -void assert_file_overwrite(const char *filename); -char *file_read(const char *filename); +int assert_file_overwrite(const char *filename); AVDictionary *strip_specifiers(const AVDictionary *dict); -const AVCodec *find_codec_or_die(void *logctx, const char *name, - enum AVMediaType type, int encoder); +int find_codec(void *logctx, const char *name, + enum AVMediaType type, int encoder, const AVCodec **codec); int parse_and_set_vsync(const char *arg, int *vsync_var, int file_idx, int st_idx, int is_global); -int configure_filtergraph(FilterGraph *fg); -void check_filter_outputs(void); -int filtergraph_is_simple(FilterGraph *fg); -int init_simple_filtergraph(InputStream *ist, OutputStream *ost); -int init_complex_filtergraph(FilterGraph *fg); +int filtergraph_is_simple(const FilterGraph *fg); +int init_simple_filtergraph(InputStream *ist, OutputStream *ost, + char *graph_desc, + Scheduler *sch, unsigned sch_idx_enc); +int fg_finalise_bindings(FilterGraph *fg); + +/** + * Get our axiliary frame data attached to the frame, allocating it + * if needed. + */ +FrameData *frame_data(AVFrame *frame); + +const FrameData *frame_data_c(AVFrame *frame); + +FrameData *packet_data (AVPacket *pkt); +const FrameData *packet_data_c(AVPacket *pkt); -void sub2video_update(InputStream *ist, int64_t heartbeat_pts, AVSubtitle *sub); +int ofilter_bind_ost(OutputFilter *ofilter, OutputStream *ost, + unsigned sched_idx_enc); -int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame); +/** + * Create a new filtergraph in the global filtergraph list. + * + * @param graph_desc Graph description; an av_malloc()ed string, filtergraph + * takes ownership of it. + */ +int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch); -int ffmpeg_parse_options(int argc, char **argv); +void fg_free(FilterGraph **pfg); + +void fg_send_command(FilterGraph *fg, double time, const char *target, + const char *command, const char *arg, int all_filters); + +int ffmpeg_parse_options(int argc, char **argv, Scheduler *sch); void enc_stats_write(OutputStream *ost, EncStats *es, const AVFrame *frame, const AVPacket *pkt, uint64_t frame_num); HWDevice *hw_device_get_by_name(const char *name); +HWDevice *hw_device_get_by_type(enum AVHWDeviceType type); int hw_device_init_from_string(const char *arg, HWDevice **dev); +int hw_device_init_from_type(enum AVHWDeviceType type, + const char *device, + HWDevice **dev_out); void hw_device_free_all(void); -int hw_device_setup_for_decode(InputStream *ist); -int hw_device_setup_for_encode(OutputStream *ost); -int hw_device_setup_for_filter(FilterGraph *fg); +/** + * Get a hardware device to be used with this filtergraph. + * The returned reference is owned by the callee, the caller + * must ref it explicitly for long-term use. + */ +AVBufferRef *hw_device_for_filter(void); + +/** + * Create a standalone decoder. + */ +int dec_create(const OptionsContext *o, const char *arg, Scheduler *sch); + +/** + * @param dec_opts Dictionary filled with decoder options. Its ownership + * is transferred to the decoder. + * @param param_out If non-NULL, media properties after opening the decoder + * are written here. + * + * @retval ">=0" non-negative scheduler index on success + * @retval "<0" an error code on failure + */ +int dec_init(Decoder **pdec, Scheduler *sch, + AVDictionary **dec_opts, const DecoderOpts *o, + AVFrame *param_out); +void dec_free(Decoder **pdec); + +/* + * Called by filters to connect decoder's output to given filtergraph input. + * + * @param opts filtergraph input options, to be filled by this function + */ +int dec_filter_add(Decoder *dec, InputFilter *ifilter, InputFilterOptions *opts); + +int enc_alloc(Encoder **penc, const AVCodec *codec, + Scheduler *sch, unsigned sch_idx); +void enc_free(Encoder **penc); + +int enc_open(void *opaque, const AVFrame *frame); -int hwaccel_decode_init(AVCodecContext *avctx); +int enc_loopback(Encoder *enc); /* * Initialize muxing state for the given stream, should be called @@ -815,44 +777,35 @@ int hwaccel_decode_init(AVCodecContext *avctx); */ int of_stream_init(OutputFile *of, OutputStream *ost); int of_write_trailer(OutputFile *of); -int of_open(const OptionsContext *o, const char *filename); -void of_close(OutputFile **pof); +int of_open(const OptionsContext *o, const char *filename, Scheduler *sch); +void of_free(OutputFile **pof); void of_enc_stats_close(void); -/* - * Send a single packet to the output, applying any bitstream filters - * associated with the output stream. This may result in any number - * of packets actually being written, depending on what bitstream - * filters are applied. The supplied packet is consumed and will be - * blank (as if newly-allocated) when this function returns. - * - * If eof is set, instead indicate EOF to all bitstream filters and - * therefore flush any delayed packets to the output. A blank packet - * must be supplied in this case. - */ -void of_output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int eof); int64_t of_filesize(OutputFile *of); -int ifile_open(const OptionsContext *o, const char *filename); +int ifile_open(const OptionsContext *o, const char *filename, Scheduler *sch); void ifile_close(InputFile **f); +int ist_output_add(InputStream *ist, OutputStream *ost); +int ist_filter_add(InputStream *ist, InputFilter *ifilter, int is_simple, + InputFilterOptions *opts); + /** - * Get next input packet from the demuxer. - * - * @param pkt the packet is written here when this function returns 0 - * @return - * - 0 when a packet has been read successfully - * - 1 when stream end was reached, but the stream is looped; - * caller should flush decoders and read from this demuxer again - * - a negative error code on failure + * Find an unused input stream of given type. */ -int ifile_get_packet(InputFile *f, AVPacket **pkt); +InputStream *ist_find_unused(enum AVMediaType type); /* iterate over all input streams in all input files; * pass NULL to start iteration */ InputStream *ist_iter(InputStream *prev); +/* iterate over all output streams in all output files; + * pass NULL to start iteration */ +OutputStream *ost_iter(OutputStream *prev); + +void update_benchmark(const char *fmt, ...); + #define SPECIFIER_OPT_FMT_str "%s" #define SPECIFIER_OPT_FMT_i "%i" #define SPECIFIER_OPT_FMT_i64 "%"PRId64 @@ -860,46 +813,41 @@ InputStream *ist_iter(InputStream *prev); #define SPECIFIER_OPT_FMT_f "%f" #define SPECIFIER_OPT_FMT_dbl "%lf" -#define WARN_MULTIPLE_OPT_USAGE(name, type, so, st)\ +#define WARN_MULTIPLE_OPT_USAGE(optname, type, idx, st)\ {\ char namestr[128] = "";\ + const SpecifierOpt *so = &o->optname.opt[idx];\ const char *spec = so->specifier && so->specifier[0] ? so->specifier : "";\ - for (int _i = 0; opt_name_##name[_i]; _i++)\ - av_strlcatf(namestr, sizeof(namestr), "-%s%s", opt_name_##name[_i], opt_name_##name[_i+1] ? (opt_name_##name[_i+2] ? ", " : " or ") : "");\ + snprintf(namestr, sizeof(namestr), "-%s", o->optname.opt_canon->name);\ + if (o->optname.opt_canon->flags & OPT_HAS_ALT) {\ + const char * const *names_alt = o->optname.opt_canon->u1.names_alt;\ + for (int _i = 0; names_alt[_i]; _i++)\ + av_strlcatf(namestr, sizeof(namestr), "/-%s", names_alt[_i]);\ + }\ av_log(NULL, AV_LOG_WARNING, "Multiple %s options specified for stream %d, only the last option '-%s%s%s "SPECIFIER_OPT_FMT_##type"' will be used.\n",\ - namestr, st->index, opt_name_##name[0], spec[0] ? ":" : "", spec, so->u.type);\ + namestr, st->index, o->optname.opt_canon->name, spec[0] ? ":" : "", spec, so->u.type);\ } #define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\ {\ - int _ret, _matches = 0;\ - SpecifierOpt *so;\ - for (int _i = 0; _i < o->nb_ ## name; _i++) {\ - char *spec = o->name[_i].specifier;\ + int _ret, _matches = 0, _match_idx;\ + for (int _i = 0; _i < o->name.nb_opt; _i++) {\ + char *spec = o->name.opt[_i].specifier;\ if ((_ret = check_stream_specifier(fmtctx, st, spec)) > 0) {\ - outvar = o->name[_i].u.type;\ - so = &o->name[_i];\ + outvar = o->name.opt[_i].u.type;\ + _match_idx = _i;\ _matches++;\ } else if (_ret < 0)\ - exit_program(1);\ + return _ret;\ }\ - if (_matches > 1)\ - WARN_MULTIPLE_OPT_USAGE(name, type, so, st);\ + if (_matches > 1 && o->name.opt_canon)\ + WARN_MULTIPLE_OPT_USAGE(name, type, _match_idx, st);\ } -#define MATCH_PER_TYPE_OPT(name, type, outvar, fmtctx, mediatype)\ -{\ - int i;\ - for (i = 0; i < o->nb_ ## name; i++) {\ - char *spec = o->name[i].specifier;\ - if (!strcmp(spec, mediatype))\ - outvar = o->name[i].u.type;\ - }\ -} +const char *opt_match_per_type_str(const SpecifierOptList *sol, + char mediatype); -extern const char * const opt_name_codec_names[]; -extern const char * const opt_name_codec_tags[]; -extern const char * const opt_name_frame_rates[]; -extern const char * const opt_name_top_field_first[]; +int muxer_thread(void *arg); +int encoder_thread(void *arg); #endif /* FFTOOLS_FFMPEG_H */ diff --git a/fftools/ffmpeg_dec.c b/fftools/ffmpeg_dec.c new file mode 100644 index 00000000000..ed411b6bf87 --- /dev/null +++ b/fftools/ffmpeg_dec.c @@ -0,0 +1,1361 @@ +/* + * 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/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/dict.h" +#include "libavutil/error.h" +#include "libavutil/log.h" +#include "libavutil/pixdesc.h" +#include "libavutil/pixfmt.h" +#include "libavutil/time.h" +#include "libavutil/timestamp.h" + +#include "libavcodec/avcodec.h" +#include "libavcodec/codec.h" + +#include "libavfilter/buffersrc.h" + +#include "ffmpeg.h" +#include "ffmpeg_utils.h" +#include "thread_queue.h" + +typedef struct DecoderPriv { + Decoder dec; + + AVCodecContext *dec_ctx; + + AVFrame *frame; + AVPacket *pkt; + + // override output video sample aspect ratio with this value + AVRational sar_override; + + AVRational framerate_in; + + // a combination of DECODER_FLAG_*, provided to dec_open() + int flags; + + enum AVPixelFormat hwaccel_pix_fmt; + enum HWAccelID hwaccel_id; + enum AVHWDeviceType hwaccel_device_type; + enum AVPixelFormat hwaccel_output_format; + + // pts/estimated duration of the last decoded frame + // * in decoder timebase for video, + // * in last_frame_tb (may change during decoding) for audio + int64_t last_frame_pts; + int64_t last_frame_duration_est; + AVRational last_frame_tb; + int64_t last_filter_in_rescale_delta; + int last_frame_sample_rate; + + /* previous decoded subtitles */ + AVFrame *sub_prev[2]; + AVFrame *sub_heartbeat; + + Scheduler *sch; + unsigned sch_idx; + + // this decoder's index in decoders or -1 + int index; + void *log_parent; + char log_name[32]; + char *parent_name; + + struct { + AVDictionary *opts; + const AVCodec *codec; + } standalone_init; +} DecoderPriv; + +static DecoderPriv *dp_from_dec(Decoder *d) +{ + return (DecoderPriv*)d; +} + +// data that is local to the decoder thread and not visible outside of it +typedef struct DecThreadContext { + AVFrame *frame; + AVPacket *pkt; +} DecThreadContext; + +void dec_free(Decoder **pdec) +{ + Decoder *dec = *pdec; + DecoderPriv *dp; + + if (!dec) + return; + dp = dp_from_dec(dec); + + avcodec_free_context(&dp->dec_ctx); + + av_frame_free(&dp->frame); + av_packet_free(&dp->pkt); + + av_dict_free(&dp->standalone_init.opts); + + for (int i = 0; i < FF_ARRAY_ELEMS(dp->sub_prev); i++) + av_frame_free(&dp->sub_prev[i]); + av_frame_free(&dp->sub_heartbeat); + + av_freep(&dp->parent_name); + + av_freep(pdec); +} + +static const char *dec_item_name(void *obj) +{ + const DecoderPriv *dp = obj; + + return dp->log_name; +} + +static const AVClass dec_class = { + .class_name = "Decoder", + .version = LIBAVUTIL_VERSION_INT, + .parent_log_context_offset = offsetof(DecoderPriv, log_parent), + .item_name = dec_item_name, +}; + +static int decoder_thread(void *arg); + +static int dec_alloc(DecoderPriv **pdec, Scheduler *sch, int send_end_ts) +{ + DecoderPriv *dp; + int ret = 0; + + *pdec = NULL; + + dp = av_mallocz(sizeof(*dp)); + if (!dp) + return AVERROR(ENOMEM); + + dp->frame = av_frame_alloc(); + if (!dp->frame) + goto fail; + + dp->pkt = av_packet_alloc(); + if (!dp->pkt) + goto fail; + + dp->index = -1; + dp->dec.class = &dec_class; + dp->last_filter_in_rescale_delta = AV_NOPTS_VALUE; + dp->last_frame_pts = AV_NOPTS_VALUE; + dp->last_frame_tb = (AVRational){ 1, 1 }; + dp->hwaccel_pix_fmt = AV_PIX_FMT_NONE; + + ret = sch_add_dec(sch, decoder_thread, dp, send_end_ts); + if (ret < 0) + goto fail; + dp->sch = sch; + dp->sch_idx = ret; + + *pdec = dp; + + return 0; +fail: + dec_free((Decoder**)&dp); + return ret >= 0 ? AVERROR(ENOMEM) : ret; +} + +static AVRational audio_samplerate_update(DecoderPriv *dp, + const AVFrame *frame) +{ + const int prev = dp->last_frame_tb.den; + const int sr = frame->sample_rate; + + AVRational tb_new; + int64_t gcd; + + if (frame->sample_rate == dp->last_frame_sample_rate) + goto finish; + + gcd = av_gcd(prev, sr); + + if (prev / gcd >= INT_MAX / sr) { + av_log(dp, AV_LOG_WARNING, + "Audio timestamps cannot be represented exactly after " + "sample rate change: %d -> %d\n", prev, sr); + + // LCM of 192000, 44100, allows to represent all common samplerates + tb_new = (AVRational){ 1, 28224000 }; + } else + tb_new = (AVRational){ 1, prev / gcd * sr }; + + // keep the frame timebase if it is strictly better than + // the samplerate-defined one + if (frame->time_base.num == 1 && frame->time_base.den > tb_new.den && + !(frame->time_base.den % tb_new.den)) + tb_new = frame->time_base; + + if (dp->last_frame_pts != AV_NOPTS_VALUE) + dp->last_frame_pts = av_rescale_q(dp->last_frame_pts, + dp->last_frame_tb, tb_new); + dp->last_frame_duration_est = av_rescale_q(dp->last_frame_duration_est, + dp->last_frame_tb, tb_new); + + dp->last_frame_tb = tb_new; + dp->last_frame_sample_rate = frame->sample_rate; + +finish: + return dp->last_frame_tb; +} + +static void audio_ts_process(DecoderPriv *dp, AVFrame *frame) +{ + AVRational tb_filter = (AVRational){1, frame->sample_rate}; + AVRational tb; + int64_t pts_pred; + + // on samplerate change, choose a new internal timebase for timestamp + // generation that can represent timestamps from all the samplerates + // seen so far + tb = audio_samplerate_update(dp, frame); + pts_pred = dp->last_frame_pts == AV_NOPTS_VALUE ? 0 : + dp->last_frame_pts + dp->last_frame_duration_est; + + if (frame->pts == AV_NOPTS_VALUE) { + frame->pts = pts_pred; + frame->time_base = tb; + } else if (dp->last_frame_pts != AV_NOPTS_VALUE && + frame->pts > av_rescale_q_rnd(pts_pred, tb, frame->time_base, + AV_ROUND_UP)) { + // there was a gap in timestamps, reset conversion state + dp->last_filter_in_rescale_delta = AV_NOPTS_VALUE; + } + + frame->pts = av_rescale_delta(frame->time_base, frame->pts, + tb, frame->nb_samples, + &dp->last_filter_in_rescale_delta, tb); + + dp->last_frame_pts = frame->pts; + dp->last_frame_duration_est = av_rescale_q(frame->nb_samples, + tb_filter, tb); + + // finally convert to filtering timebase + frame->pts = av_rescale_q(frame->pts, tb, tb_filter); + frame->duration = frame->nb_samples; + frame->time_base = tb_filter; +} + +static int64_t video_duration_estimate(const DecoderPriv *dp, const AVFrame *frame) +{ + const int ts_unreliable = dp->flags & DECODER_FLAG_TS_UNRELIABLE; + const int fr_forced = dp->flags & DECODER_FLAG_FRAMERATE_FORCED; + int64_t codec_duration = 0; + + // XXX lavf currently makes up frame durations when they are not provided by + // the container. As there is no way to reliably distinguish real container + // durations from the fake made-up ones, we use heuristics based on whether + // the container has timestamps. Eventually lavf should stop making up + // durations, then this should be simplified. + + // prefer frame duration for containers with timestamps + if (frame->duration > 0 && (!ts_unreliable || fr_forced)) + return frame->duration; + + if (dp->dec_ctx->framerate.den && dp->dec_ctx->framerate.num) { + int fields = frame->repeat_pict + 2; + AVRational field_rate = av_mul_q(dp->dec_ctx->framerate, + (AVRational){ 2, 1 }); + codec_duration = av_rescale_q(fields, av_inv_q(field_rate), + frame->time_base); + } + + // prefer codec-layer duration for containers without timestamps + if (codec_duration > 0 && ts_unreliable) + return codec_duration; + + // when timestamps are available, repeat last frame's actual duration + // (i.e. pts difference between this and last frame) + if (frame->pts != AV_NOPTS_VALUE && dp->last_frame_pts != AV_NOPTS_VALUE && + frame->pts > dp->last_frame_pts) + return frame->pts - dp->last_frame_pts; + + // try frame/codec duration + if (frame->duration > 0) + return frame->duration; + if (codec_duration > 0) + return codec_duration; + + // try average framerate + if (dp->framerate_in.num && dp->framerate_in.den) { + int64_t d = av_rescale_q(1, av_inv_q(dp->framerate_in), + frame->time_base); + if (d > 0) + return d; + } + + // last resort is last frame's estimated duration, and 1 + return FFMAX(dp->last_frame_duration_est, 1); +} + +static int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input) +{ + DecoderPriv *dp = avctx->opaque; + AVFrame *output = NULL; + enum AVPixelFormat output_format = dp->hwaccel_output_format; + int err; + + if (input->format == output_format) { + // Nothing to do. + return 0; + } + + output = av_frame_alloc(); + if (!output) + return AVERROR(ENOMEM); + + output->format = output_format; + + err = av_hwframe_transfer_data(output, input, 0); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to transfer data to " + "output frame: %d.\n", err); + goto fail; + } + + err = av_frame_copy_props(output, input); + if (err < 0) { + av_frame_unref(output); + goto fail; + } + + av_frame_unref(input); + av_frame_move_ref(input, output); + av_frame_free(&output); + + return 0; + +fail: + av_frame_free(&output); + return err; +} + +static int video_frame_process(DecoderPriv *dp, AVFrame *frame) +{ +#if FFMPEG_OPT_TOP + if (dp->flags & DECODER_FLAG_TOP_FIELD_FIRST) { + av_log(dp, AV_LOG_WARNING, "-top is deprecated, use the setfield filter instead\n"); + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + } +#endif + + if (frame->format == dp->hwaccel_pix_fmt) { + int err = hwaccel_retrieve_data(dp->dec_ctx, frame); + if (err < 0) + return err; + } + + frame->pts = frame->best_effort_timestamp; + + // forced fixed framerate + if (dp->flags & DECODER_FLAG_FRAMERATE_FORCED) { + frame->pts = AV_NOPTS_VALUE; + frame->duration = 1; + frame->time_base = av_inv_q(dp->framerate_in); + } + + // no timestamp available - extrapolate from previous frame duration + if (frame->pts == AV_NOPTS_VALUE) + frame->pts = dp->last_frame_pts == AV_NOPTS_VALUE ? 0 : + dp->last_frame_pts + dp->last_frame_duration_est; + + // update timestamp history + dp->last_frame_duration_est = video_duration_estimate(dp, frame); + dp->last_frame_pts = frame->pts; + dp->last_frame_tb = frame->time_base; + + if (debug_ts) { + av_log(dp, AV_LOG_INFO, + "decoder -> pts:%s pts_time:%s " + "pkt_dts:%s pkt_dts_time:%s " + "duration:%s duration_time:%s " + "keyframe:%d frame_type:%d time_base:%d/%d\n", + av_ts2str(frame->pts), + av_ts2timestr(frame->pts, &frame->time_base), + av_ts2str(frame->pkt_dts), + av_ts2timestr(frame->pkt_dts, &frame->time_base), + av_ts2str(frame->duration), + av_ts2timestr(frame->duration, &frame->time_base), + !!(frame->flags & AV_FRAME_FLAG_KEY), frame->pict_type, + frame->time_base.num, frame->time_base.den); + } + + if (dp->sar_override.num) + frame->sample_aspect_ratio = dp->sar_override; + + return 0; +} + +static int copy_av_subtitle(AVSubtitle *dst, const AVSubtitle *src) +{ + int ret = AVERROR_BUG; + AVSubtitle tmp = { + .format = src->format, + .start_display_time = src->start_display_time, + .end_display_time = src->end_display_time, + .num_rects = 0, + .rects = NULL, + .pts = src->pts + }; + + if (!src->num_rects) + goto success; + + if (!(tmp.rects = av_calloc(src->num_rects, sizeof(*tmp.rects)))) + return AVERROR(ENOMEM); + + for (int i = 0; i < src->num_rects; i++) { + AVSubtitleRect *src_rect = src->rects[i]; + AVSubtitleRect *dst_rect; + + if (!(dst_rect = tmp.rects[i] = av_mallocz(sizeof(*tmp.rects[0])))) { + ret = AVERROR(ENOMEM); + goto cleanup; + } + + tmp.num_rects++; + + dst_rect->type = src_rect->type; + dst_rect->flags = src_rect->flags; + + dst_rect->x = src_rect->x; + dst_rect->y = src_rect->y; + dst_rect->w = src_rect->w; + dst_rect->h = src_rect->h; + dst_rect->nb_colors = src_rect->nb_colors; + + if (src_rect->text) + if (!(dst_rect->text = av_strdup(src_rect->text))) { + ret = AVERROR(ENOMEM); + goto cleanup; + } + + if (src_rect->ass) + if (!(dst_rect->ass = av_strdup(src_rect->ass))) { + ret = AVERROR(ENOMEM); + goto cleanup; + } + + for (int j = 0; j < 4; j++) { + // SUBTITLE_BITMAP images are special in the sense that they + // are like PAL8 images. first pointer to data, second to + // palette. This makes the size calculation match this. + size_t buf_size = src_rect->type == SUBTITLE_BITMAP && j == 1 ? + AVPALETTE_SIZE : + src_rect->h * src_rect->linesize[j]; + + if (!src_rect->data[j]) + continue; + + if (!(dst_rect->data[j] = av_memdup(src_rect->data[j], buf_size))) { + ret = AVERROR(ENOMEM); + goto cleanup; + } + dst_rect->linesize[j] = src_rect->linesize[j]; + } + } + +success: + *dst = tmp; + + return 0; + +cleanup: + avsubtitle_free(&tmp); + + return ret; +} + +static void subtitle_free(void *opaque, uint8_t *data) +{ + AVSubtitle *sub = (AVSubtitle*)data; + avsubtitle_free(sub); + av_free(sub); +} + +static int subtitle_wrap_frame(AVFrame *frame, AVSubtitle *subtitle, int copy) +{ + AVBufferRef *buf; + AVSubtitle *sub; + int ret; + + if (copy) { + sub = av_mallocz(sizeof(*sub)); + ret = sub ? copy_av_subtitle(sub, subtitle) : AVERROR(ENOMEM); + if (ret < 0) { + av_freep(&sub); + return ret; + } + } else { + sub = av_memdup(subtitle, sizeof(*subtitle)); + if (!sub) + return AVERROR(ENOMEM); + memset(subtitle, 0, sizeof(*subtitle)); + } + + buf = av_buffer_create((uint8_t*)sub, sizeof(*sub), + subtitle_free, NULL, 0); + if (!buf) { + avsubtitle_free(sub); + av_freep(&sub); + return AVERROR(ENOMEM); + } + + frame->buf[0] = buf; + + return 0; +} + +static int process_subtitle(DecoderPriv *dp, AVFrame *frame) +{ + const AVSubtitle *subtitle = (AVSubtitle*)frame->buf[0]->data; + int ret = 0; + + if (dp->flags & DECODER_FLAG_FIX_SUB_DURATION) { + AVSubtitle *sub_prev = dp->sub_prev[0]->buf[0] ? + (AVSubtitle*)dp->sub_prev[0]->buf[0]->data : NULL; + int end = 1; + if (sub_prev) { + end = av_rescale(subtitle->pts - sub_prev->pts, + 1000, AV_TIME_BASE); + if (end < sub_prev->end_display_time) { + av_log(dp, AV_LOG_DEBUG, + "Subtitle duration reduced from %"PRId32" to %d%s\n", + sub_prev->end_display_time, end, + end <= 0 ? ", dropping it" : ""); + sub_prev->end_display_time = end; + } + } + + av_frame_unref(dp->sub_prev[1]); + av_frame_move_ref(dp->sub_prev[1], frame); + + frame = dp->sub_prev[0]; + subtitle = frame->buf[0] ? (AVSubtitle*)frame->buf[0]->data : NULL; + + FFSWAP(AVFrame*, dp->sub_prev[0], dp->sub_prev[1]); + + if (end <= 0) + return 0; + } + + if (!subtitle) + return 0; + + ret = sch_dec_send(dp->sch, dp->sch_idx, frame); + if (ret < 0) + av_frame_unref(frame); + + return ret == AVERROR_EOF ? AVERROR_EXIT : ret; +} + +static int fix_sub_duration_heartbeat(DecoderPriv *dp, int64_t signal_pts) +{ + int ret = AVERROR_BUG; + AVSubtitle *prev_subtitle = dp->sub_prev[0]->buf[0] ? + (AVSubtitle*)dp->sub_prev[0]->buf[0]->data : NULL; + AVSubtitle *subtitle; + + if (!(dp->flags & DECODER_FLAG_FIX_SUB_DURATION) || !prev_subtitle || + !prev_subtitle->num_rects || signal_pts <= prev_subtitle->pts) + return 0; + + av_frame_unref(dp->sub_heartbeat); + ret = subtitle_wrap_frame(dp->sub_heartbeat, prev_subtitle, 1); + if (ret < 0) + return ret; + + subtitle = (AVSubtitle*)dp->sub_heartbeat->buf[0]->data; + subtitle->pts = signal_pts; + + return process_subtitle(dp, dp->sub_heartbeat); +} + +static int transcode_subtitles(DecoderPriv *dp, const AVPacket *pkt, + AVFrame *frame) +{ + AVPacket *flush_pkt = NULL; + AVSubtitle subtitle; + int got_output; + int ret; + + if (pkt && (intptr_t)pkt->opaque == PKT_OPAQUE_SUB_HEARTBEAT) { + frame->pts = pkt->pts; + frame->time_base = pkt->time_base; + frame->opaque = (void*)(intptr_t)FRAME_OPAQUE_SUB_HEARTBEAT; + + ret = sch_dec_send(dp->sch, dp->sch_idx, frame); + return ret == AVERROR_EOF ? AVERROR_EXIT : ret; + } else if (pkt && (intptr_t)pkt->opaque == PKT_OPAQUE_FIX_SUB_DURATION) { + return fix_sub_duration_heartbeat(dp, av_rescale_q(pkt->pts, pkt->time_base, + AV_TIME_BASE_Q)); + } + + if (!pkt) { + flush_pkt = av_packet_alloc(); + if (!flush_pkt) + return AVERROR(ENOMEM); + } + + ret = avcodec_decode_subtitle2(dp->dec_ctx, &subtitle, &got_output, + pkt ? pkt : flush_pkt); + av_packet_free(&flush_pkt); + + if (ret < 0) { + av_log(dp, AV_LOG_ERROR, "Error decoding subtitles: %s\n", + av_err2str(ret)); + dp->dec.decode_errors++; + return exit_on_error ? ret : 0; + } + + if (!got_output) + return pkt ? 0 : AVERROR_EOF; + + dp->dec.frames_decoded++; + + // XXX the queue for transferring data to consumers runs + // on AVFrames, so we wrap AVSubtitle in an AVBufferRef and put that + // inside the frame + // eventually, subtitles should be switched to use AVFrames natively + ret = subtitle_wrap_frame(frame, &subtitle, 0); + if (ret < 0) { + avsubtitle_free(&subtitle); + return ret; + } + + frame->width = dp->dec_ctx->width; + frame->height = dp->dec_ctx->height; + + return process_subtitle(dp, frame); +} + +static int packet_decode(DecoderPriv *dp, AVPacket *pkt, AVFrame *frame) +{ + AVCodecContext *dec = dp->dec_ctx; + const char *type_desc = av_get_media_type_string(dec->codec_type); + int ret; + + if (dec->codec_type == AVMEDIA_TYPE_SUBTITLE) + return transcode_subtitles(dp, pkt, frame); + + // With fate-indeo3-2, we're getting 0-sized packets before EOF for some + // reason. This seems like a semi-critical bug. Don't trigger EOF, and + // skip the packet. + if (pkt && pkt->size == 0) + return 0; + + if (pkt && (dp->flags & DECODER_FLAG_TS_UNRELIABLE)) { + pkt->pts = AV_NOPTS_VALUE; + pkt->dts = AV_NOPTS_VALUE; + } + + if (pkt) { + FrameData *fd = packet_data(pkt); + if (!fd) + return AVERROR(ENOMEM); + fd->wallclock[LATENCY_PROBE_DEC_PRE] = av_gettime_relative(); + } + + ret = avcodec_send_packet(dec, pkt); + if (ret < 0 && !(ret == AVERROR_EOF && !pkt)) { + // In particular, we don't expect AVERROR(EAGAIN), because we read all + // decoded frames with avcodec_receive_frame() until done. + if (ret == AVERROR(EAGAIN)) { + av_log(dp, AV_LOG_FATAL, "A decoder returned an unexpected error code. " + "This is a bug, please report it.\n"); + return AVERROR_BUG; + } + av_log(dp, AV_LOG_ERROR, "Error submitting %s to decoder: %s\n", + pkt ? "packet" : "EOF", av_err2str(ret)); + + if (ret != AVERROR_EOF) { + dp->dec.decode_errors++; + if (!exit_on_error) + ret = 0; + } + + return ret; + } + + while (1) { + FrameData *fd; + + av_frame_unref(frame); + + update_benchmark(NULL); + ret = avcodec_receive_frame(dec, frame); + update_benchmark("decode_%s %s", type_desc, dp->parent_name); + + if (ret == AVERROR(EAGAIN)) { + av_assert0(pkt); // should never happen during flushing + return 0; + } else if (ret == AVERROR_EOF) { + return ret; + } else if (ret < 0) { + av_log(dp, AV_LOG_ERROR, "Decoding error: %s\n", av_err2str(ret)); + dp->dec.decode_errors++; + + if (exit_on_error) + return ret; + + continue; + } + + if (frame->decode_error_flags || (frame->flags & AV_FRAME_FLAG_CORRUPT)) { + av_log(dp, exit_on_error ? AV_LOG_FATAL : AV_LOG_WARNING, + "corrupt decoded frame\n"); + if (exit_on_error) + return AVERROR_INVALIDDATA; + } + + fd = frame_data(frame); + if (!fd) { + av_frame_unref(frame); + return AVERROR(ENOMEM); + } + fd->dec.pts = frame->pts; + fd->dec.tb = dec->pkt_timebase; + fd->dec.frame_num = dec->frame_num - 1; + fd->bits_per_raw_sample = dec->bits_per_raw_sample; + + fd->wallclock[LATENCY_PROBE_DEC_POST] = av_gettime_relative(); + + frame->time_base = dec->pkt_timebase; + + if (dec->codec_type == AVMEDIA_TYPE_AUDIO) { + dp->dec.samples_decoded += frame->nb_samples; + + audio_ts_process(dp, frame); + } else { + ret = video_frame_process(dp, frame); + if (ret < 0) { + av_log(dp, AV_LOG_FATAL, + "Error while processing the decoded data\n"); + return ret; + } + } + + dp->dec.frames_decoded++; + + ret = sch_dec_send(dp->sch, dp->sch_idx, frame); + if (ret < 0) { + av_frame_unref(frame); + return ret == AVERROR_EOF ? AVERROR_EXIT : ret; + } + } +} + +static int dec_open(DecoderPriv *dp, AVDictionary **dec_opts, + const DecoderOpts *o, AVFrame *param_out); + +static int dec_standalone_open(DecoderPriv *dp, const AVPacket *pkt) +{ + DecoderOpts o; + const FrameData *fd; + char name[16]; + + if (!pkt->opaque_ref) + return AVERROR_BUG; + fd = (FrameData *)pkt->opaque_ref->data; + + if (!fd->par_enc) + return AVERROR_BUG; + + memset(&o, 0, sizeof(o)); + + o.par = fd->par_enc; + o.time_base = pkt->time_base; + + o.codec = dp->standalone_init.codec; + if (!o.codec) + o.codec = avcodec_find_decoder(o.par->codec_id); + if (!o.codec) { + const AVCodecDescriptor *desc = avcodec_descriptor_get(o.par->codec_id); + + av_log(dp, AV_LOG_ERROR, "Cannot find a decoder for codec ID '%s'\n", + desc ? desc->name : "?"); + return AVERROR_DECODER_NOT_FOUND; + } + + snprintf(name, sizeof(name), "dec%d", dp->index); + o.name = name; + + return dec_open(dp, &dp->standalone_init.opts, &o, NULL); +} + +static void dec_thread_set_name(const DecoderPriv *dp) +{ + char name[16] = "dec"; + + if (dp->index >= 0) + av_strlcatf(name, sizeof(name), "%d", dp->index); + else if (dp->parent_name) + av_strlcat(name, dp->parent_name, sizeof(name)); + + if (dp->dec_ctx) + av_strlcatf(name, sizeof(name), ":%s", dp->dec_ctx->codec->name); + + ff_thread_setname(name); +} + +static void dec_thread_uninit(DecThreadContext *dt) +{ + av_packet_free(&dt->pkt); + av_frame_free(&dt->frame); + + memset(dt, 0, sizeof(*dt)); +} + +static int dec_thread_init(DecThreadContext *dt) +{ + memset(dt, 0, sizeof(*dt)); + + dt->frame = av_frame_alloc(); + if (!dt->frame) + goto fail; + + dt->pkt = av_packet_alloc(); + if (!dt->pkt) + goto fail; + + return 0; + +fail: + dec_thread_uninit(dt); + return AVERROR(ENOMEM); +} + +static int decoder_thread(void *arg) +{ + DecoderPriv *dp = arg; + DecThreadContext dt; + int ret = 0, input_status = 0; + + ret = dec_thread_init(&dt); + if (ret < 0) + goto finish; + + dec_thread_set_name(dp); + + while (!input_status) { + int flush_buffers, have_data; + + input_status = sch_dec_receive(dp->sch, dp->sch_idx, dt.pkt); + have_data = input_status >= 0 && + (dt.pkt->buf || dt.pkt->side_data_elems || + (intptr_t)dt.pkt->opaque == PKT_OPAQUE_SUB_HEARTBEAT || + (intptr_t)dt.pkt->opaque == PKT_OPAQUE_FIX_SUB_DURATION); + flush_buffers = input_status >= 0 && !have_data; + if (!have_data) + av_log(dp, AV_LOG_VERBOSE, "Decoder thread received %s packet\n", + flush_buffers ? "flush" : "EOF"); + + // this is a standalone decoder that has not been initialized yet + if (!dp->dec_ctx) { + if (flush_buffers) + continue; + if (input_status < 0) { + av_log(dp, AV_LOG_ERROR, + "Cannot initialize a standalone decoder\n"); + ret = input_status; + goto finish; + } + + ret = dec_standalone_open(dp, dt.pkt); + if (ret < 0) + goto finish; + } + + ret = packet_decode(dp, have_data ? dt.pkt : NULL, dt.frame); + + av_packet_unref(dt.pkt); + av_frame_unref(dt.frame); + + // AVERROR_EOF - EOF from the decoder + // AVERROR_EXIT - EOF from the scheduler + // we treat them differently when flushing + if (ret == AVERROR_EXIT) { + ret = AVERROR_EOF; + flush_buffers = 0; + } + + if (ret == AVERROR_EOF) { + av_log(dp, AV_LOG_VERBOSE, "Decoder returned EOF, %s\n", + flush_buffers ? "resetting" : "finishing"); + + if (!flush_buffers) + break; + + /* report last frame duration to the scheduler */ + if (dp->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { + dt.pkt->pts = dp->last_frame_pts + dp->last_frame_duration_est; + dt.pkt->time_base = dp->last_frame_tb; + } + + avcodec_flush_buffers(dp->dec_ctx); + } else if (ret < 0) { + av_log(dp, AV_LOG_ERROR, "Error processing packet in decoder: %s\n", + av_err2str(ret)); + break; + } + } + + // EOF is normal thread termination + if (ret == AVERROR_EOF) + ret = 0; + + // on success send EOF timestamp to our downstreams + if (ret >= 0) { + float err_rate; + + av_frame_unref(dt.frame); + + dt.frame->opaque = (void*)(intptr_t)FRAME_OPAQUE_EOF; + dt.frame->pts = dp->last_frame_pts == AV_NOPTS_VALUE ? AV_NOPTS_VALUE : + dp->last_frame_pts + dp->last_frame_duration_est; + dt.frame->time_base = dp->last_frame_tb; + + ret = sch_dec_send(dp->sch, dp->sch_idx, dt.frame); + if (ret < 0 && ret != AVERROR_EOF) { + av_log(dp, AV_LOG_FATAL, + "Error signalling EOF timestamp: %s\n", av_err2str(ret)); + goto finish; + } + ret = 0; + + err_rate = (dp->dec.frames_decoded || dp->dec.decode_errors) ? + dp->dec.decode_errors / (dp->dec.frames_decoded + dp->dec.decode_errors) : 0.f; + if (err_rate > max_error_rate) { + av_log(dp, AV_LOG_FATAL, "Decode error rate %g exceeds maximum %g\n", + err_rate, max_error_rate); + ret = FFMPEG_ERROR_RATE_EXCEEDED; + } else if (err_rate) + av_log(dp, AV_LOG_VERBOSE, "Decode error rate %g\n", err_rate); + } + +finish: + dec_thread_uninit(&dt); + + return ret; +} + +static enum AVPixelFormat get_format(AVCodecContext *s, const enum AVPixelFormat *pix_fmts) +{ + DecoderPriv *dp = s->opaque; + const enum AVPixelFormat *p; + + for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(*p); + const AVCodecHWConfig *config = NULL; + + if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) + break; + + if (dp->hwaccel_id == HWACCEL_GENERIC || + dp->hwaccel_id == HWACCEL_AUTO) { + for (int i = 0;; i++) { + config = avcodec_get_hw_config(s->codec, i); + if (!config) + break; + if (!(config->methods & + AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) + continue; + if (config->pix_fmt == *p) + break; + } + } + if (config && config->device_type == dp->hwaccel_device_type) { + dp->hwaccel_pix_fmt = *p; + break; + } + } + + return *p; +} + +static HWDevice *hw_device_match_by_codec(const AVCodec *codec) +{ + const AVCodecHWConfig *config; + HWDevice *dev; + for (int i = 0;; i++) { + config = avcodec_get_hw_config(codec, i); + if (!config) + return NULL; + if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) + continue; + dev = hw_device_get_by_type(config->device_type); + if (dev) + return dev; + } +} + +static int hw_device_setup_for_decode(DecoderPriv *dp, + const AVCodec *codec, + const char *hwaccel_device) +{ + const AVCodecHWConfig *config; + enum AVHWDeviceType type; + HWDevice *dev = NULL; + int err, auto_device = 0; + + if (hwaccel_device) { + dev = hw_device_get_by_name(hwaccel_device); + if (!dev) { + if (dp->hwaccel_id == HWACCEL_AUTO) { + auto_device = 1; + } else if (dp->hwaccel_id == HWACCEL_GENERIC) { + type = dp->hwaccel_device_type; + err = hw_device_init_from_type(type, hwaccel_device, + &dev); + } else { + // This will be dealt with by API-specific initialisation + // (using hwaccel_device), so nothing further needed here. + return 0; + } + } else { + if (dp->hwaccel_id == HWACCEL_AUTO) { + dp->hwaccel_device_type = dev->type; + } else if (dp->hwaccel_device_type != dev->type) { + av_log(dp, AV_LOG_ERROR, "Invalid hwaccel device " + "specified for decoder: device %s of type %s is not " + "usable with hwaccel %s.\n", dev->name, + av_hwdevice_get_type_name(dev->type), + av_hwdevice_get_type_name(dp->hwaccel_device_type)); + return AVERROR(EINVAL); + } + } + } else { + if (dp->hwaccel_id == HWACCEL_AUTO) { + auto_device = 1; + } else if (dp->hwaccel_id == HWACCEL_GENERIC) { + type = dp->hwaccel_device_type; + dev = hw_device_get_by_type(type); + + // When "-qsv_device device" is used, an internal QSV device named + // as "__qsv_device" is created. Another QSV device is created too + // if "-init_hw_device qsv=name:device" is used. There are 2 QSV devices + // if both "-qsv_device device" and "-init_hw_device qsv=name:device" + // are used, hw_device_get_by_type(AV_HWDEVICE_TYPE_QSV) returns NULL. + // To keep back-compatibility with the removed ad-hoc libmfx setup code, + // call hw_device_get_by_name("__qsv_device") to select the internal QSV + // device. + if (!dev && type == AV_HWDEVICE_TYPE_QSV) + dev = hw_device_get_by_name("__qsv_device"); + + if (!dev) + err = hw_device_init_from_type(type, NULL, &dev); + } else { + dev = hw_device_match_by_codec(codec); + if (!dev) { + // No device for this codec, but not using generic hwaccel + // and therefore may well not need one - ignore. + return 0; + } + } + } + + if (auto_device) { + if (!avcodec_get_hw_config(codec, 0)) { + // Decoder does not support any hardware devices. + return 0; + } + for (int i = 0; !dev; i++) { + config = avcodec_get_hw_config(codec, i); + if (!config) + break; + type = config->device_type; + dev = hw_device_get_by_type(type); + if (dev) { + av_log(dp, AV_LOG_INFO, "Using auto " + "hwaccel type %s with existing device %s.\n", + av_hwdevice_get_type_name(type), dev->name); + } + } + for (int i = 0; !dev; i++) { + config = avcodec_get_hw_config(codec, i); + if (!config) + break; + type = config->device_type; + // Try to make a new device of this type. + err = hw_device_init_from_type(type, hwaccel_device, + &dev); + if (err < 0) { + // Can't make a device of this type. + continue; + } + if (hwaccel_device) { + av_log(dp, AV_LOG_INFO, "Using auto " + "hwaccel type %s with new device created " + "from %s.\n", av_hwdevice_get_type_name(type), + hwaccel_device); + } else { + av_log(dp, AV_LOG_INFO, "Using auto " + "hwaccel type %s with new default device.\n", + av_hwdevice_get_type_name(type)); + } + } + if (dev) { + dp->hwaccel_device_type = type; + } else { + av_log(dp, AV_LOG_INFO, "Auto hwaccel " + "disabled: no device found.\n"); + dp->hwaccel_id = HWACCEL_NONE; + return 0; + } + } + + if (!dev) { + av_log(dp, AV_LOG_ERROR, "No device available " + "for decoder: device type %s needed for codec %s.\n", + av_hwdevice_get_type_name(type), codec->name); + return err; + } + + dp->dec_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref); + if (!dp->dec_ctx->hw_device_ctx) + return AVERROR(ENOMEM); + + return 0; +} + +static int dec_open(DecoderPriv *dp, AVDictionary **dec_opts, + const DecoderOpts *o, AVFrame *param_out) +{ + const AVCodec *codec = o->codec; + int ret; + + dp->flags = o->flags; + dp->log_parent = o->log_parent; + + dp->dec.type = codec->type; + dp->framerate_in = o->framerate; + + dp->hwaccel_id = o->hwaccel_id; + dp->hwaccel_device_type = o->hwaccel_device_type; + dp->hwaccel_output_format = o->hwaccel_output_format; + + snprintf(dp->log_name, sizeof(dp->log_name), "dec:%s", codec->name); + + dp->parent_name = av_strdup(o->name ? o->name : ""); + if (!dp->parent_name) + return AVERROR(ENOMEM); + + if (codec->type == AVMEDIA_TYPE_SUBTITLE && + (dp->flags & DECODER_FLAG_FIX_SUB_DURATION)) { + for (int i = 0; i < FF_ARRAY_ELEMS(dp->sub_prev); i++) { + dp->sub_prev[i] = av_frame_alloc(); + if (!dp->sub_prev[i]) + return AVERROR(ENOMEM); + } + dp->sub_heartbeat = av_frame_alloc(); + if (!dp->sub_heartbeat) + return AVERROR(ENOMEM); + } + + dp->sar_override = o->par->sample_aspect_ratio; + + dp->dec_ctx = avcodec_alloc_context3(codec); + if (!dp->dec_ctx) + return AVERROR(ENOMEM); + + ret = avcodec_parameters_to_context(dp->dec_ctx, o->par); + if (ret < 0) { + av_log(dp, AV_LOG_ERROR, "Error initializing the decoder context.\n"); + return ret; + } + + dp->dec_ctx->opaque = dp; + dp->dec_ctx->get_format = get_format; + dp->dec_ctx->pkt_timebase = o->time_base; + + if (!av_dict_get(*dec_opts, "threads", NULL, 0)) + av_dict_set(dec_opts, "threads", "auto", 0); + + av_dict_set(dec_opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY); + + ret = hw_device_setup_for_decode(dp, codec, o->hwaccel_device); + if (ret < 0) { + av_log(dp, AV_LOG_ERROR, + "Hardware device setup failed for decoder: %s\n", + av_err2str(ret)); + return ret; + } + + if ((ret = avcodec_open2(dp->dec_ctx, codec, dec_opts)) < 0) { + av_log(dp, AV_LOG_ERROR, "Error while opening decoder: %s\n", + av_err2str(ret)); + return ret; + } + + if (dp->dec_ctx->hw_device_ctx) { + // Update decoder extra_hw_frames option to account for the + // frames held in queues inside the ffmpeg utility. This is + // called after avcodec_open2() because the user-set value of + // extra_hw_frames becomes valid in there, and we need to add + // this on top of it. + int extra_frames = DEFAULT_FRAME_THREAD_QUEUE_SIZE; + if (dp->dec_ctx->extra_hw_frames >= 0) + dp->dec_ctx->extra_hw_frames += extra_frames; + else + dp->dec_ctx->extra_hw_frames = extra_frames; + } + + ret = check_avoptions(*dec_opts); + if (ret < 0) + return ret; + + dp->dec.subtitle_header = dp->dec_ctx->subtitle_header; + dp->dec.subtitle_header_size = dp->dec_ctx->subtitle_header_size; + + if (param_out) { + if (dp->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { + param_out->format = dp->dec_ctx->sample_fmt; + param_out->sample_rate = dp->dec_ctx->sample_rate; + + ret = av_channel_layout_copy(¶m_out->ch_layout, &dp->dec_ctx->ch_layout); + if (ret < 0) + return ret; + } else if (dp->dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) { + param_out->format = dp->dec_ctx->pix_fmt; + param_out->width = dp->dec_ctx->width; + param_out->height = dp->dec_ctx->height; + param_out->sample_aspect_ratio = dp->dec_ctx->sample_aspect_ratio; + param_out->colorspace = dp->dec_ctx->colorspace; + param_out->color_range = dp->dec_ctx->color_range; + } + + param_out->time_base = dp->dec_ctx->pkt_timebase; + } + + return 0; +} + +int dec_init(Decoder **pdec, Scheduler *sch, + AVDictionary **dec_opts, const DecoderOpts *o, + AVFrame *param_out) +{ + DecoderPriv *dp; + int ret; + + *pdec = NULL; + + ret = dec_alloc(&dp, sch, !!(o->flags & DECODER_FLAG_SEND_END_TS)); + if (ret < 0) + return ret; + + ret = dec_open(dp, dec_opts, o, param_out); + if (ret < 0) + goto fail; + + *pdec = &dp->dec; + + return dp->sch_idx; +fail: + dec_free((Decoder**)&dp); + return ret; +} + +int dec_create(const OptionsContext *o, const char *arg, Scheduler *sch) +{ + DecoderPriv *dp; + + OutputFile *of; + OutputStream *ost; + int of_index, ost_index; + char *p; + + unsigned enc_idx; + int ret; + + ret = dec_alloc(&dp, sch, 0); + if (ret < 0) + return ret; + + dp->index = nb_decoders; + + ret = GROW_ARRAY(decoders, nb_decoders); + if (ret < 0) { + dec_free((Decoder **)&dp); + return ret; + } + + decoders[nb_decoders - 1] = (Decoder *)dp; + + of_index = strtol(arg, &p, 0); + if (of_index < 0 || of_index >= nb_output_files) { + av_log(dp, AV_LOG_ERROR, "Invalid output file index '%d' in %s\n", of_index, arg); + return AVERROR(EINVAL); + } + of = output_files[of_index]; + + ost_index = strtol(p + 1, NULL, 0); + if (ost_index < 0 || ost_index >= of->nb_streams) { + av_log(dp, AV_LOG_ERROR, "Invalid output stream index '%d' in %s\n", ost_index, arg); + return AVERROR(EINVAL); + } + ost = of->streams[ost_index]; + + if (!ost->enc) { + av_log(dp, AV_LOG_ERROR, "Output stream %s has no encoder\n", arg); + return AVERROR(EINVAL); + } + + dp->dec.type = ost->type; + + ret = enc_loopback(ost->enc); + if (ret < 0) + return ret; + enc_idx = ret; + + ret = sch_connect(sch, SCH_ENC(enc_idx), SCH_DEC(dp->sch_idx)); + if (ret < 0) + return ret; + + ret = av_dict_copy(&dp->standalone_init.opts, o->g->codec_opts, 0); + if (ret < 0) + return ret; + + if (o->codec_names.nb_opt) { + const char *name = o->codec_names.opt[o->codec_names.nb_opt - 1].u.str; + dp->standalone_init.codec = avcodec_find_decoder_by_name(name); + if (!dp->standalone_init.codec) { + av_log(dp, AV_LOG_ERROR, "No such decoder: %s\n", name); + return AVERROR_DECODER_NOT_FOUND; + } + } + + return 0; +} + +int dec_filter_add(Decoder *d, InputFilter *ifilter, InputFilterOptions *opts) +{ + DecoderPriv *dp = dp_from_dec(d); + char name[16]; + + snprintf(name, sizeof(name), "dec%d", dp->index); + opts->name = av_strdup(name); + if (!opts->name) + return AVERROR(ENOMEM); + + return dp->sch_idx; +} diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c index 938ec09e3d2..47312c9fe12 100644 --- a/fftools/ffmpeg_demux.c +++ b/fftools/ffmpeg_demux.c @@ -20,6 +20,8 @@ #include #include "ffmpeg.h" +#include "ffmpeg_sched.h" +#include "ffmpeg_utils.h" #include "libavutil/avassert.h" #include "libavutil/avstring.h" @@ -31,200 +33,616 @@ #include "libavutil/pixdesc.h" #include "libavutil/time.h" #include "libavutil/timestamp.h" -#include "libavutil/thread.h" -#include "libavutil/threadmessage.h" +#include "libavcodec/bsf.h" #include "libavcodec/packet.h" #include "libavformat/avformat.h" -static const char *const opt_name_discard[] = {"discard", NULL}; -static const char *const opt_name_reinit_filters[] = {"reinit_filter", NULL}; -static const char *const opt_name_fix_sub_duration[] = {"fix_sub_duration", NULL}; -static const char *const opt_name_canvas_sizes[] = {"canvas_size", NULL}; -static const char *const opt_name_guess_layout_max[] = {"guess_layout_max", NULL}; -static const char *const opt_name_ts_scale[] = {"itsscale", NULL}; -static const char *const opt_name_hwaccels[] = {"hwaccel", NULL}; -static const char *const opt_name_hwaccel_devices[] = {"hwaccel_device", NULL}; -static const char *const opt_name_hwaccel_output_formats[] = {"hwaccel_output_format", NULL}; -static const char *const opt_name_autorotate[] = {"autorotate", NULL}; -static const char *const opt_name_display_rotations[] = {"display_rotation", NULL}; -static const char *const opt_name_display_hflips[] = {"display_hflip", NULL}; -static const char *const opt_name_display_vflips[] = {"display_vflip", NULL}; +typedef struct DemuxStream { + InputStream ist; + + // name used for logging + char log_name[32]; + + int sch_idx_stream; + int sch_idx_dec; + + double ts_scale; + + /* non zero if the packets must be decoded in 'raw_fifo', see DECODING_FOR_* */ + int decoding_needed; +#define DECODING_FOR_OST 1 +#define DECODING_FOR_FILTER 2 + + /* true if stream data should be discarded */ + int discard; + + // scheduler returned EOF for this stream + int finished; + + int streamcopy_needed; + int have_sub2video; + int reinit_filters; + + int wrap_correction_done; + int saw_first_ts; + ///< dts of the first packet read for this stream (in AV_TIME_BASE units) + int64_t first_dts; + + /* predicted dts of the next packet read for this stream or (when there are + * several frames in a packet) of the next frame in current packet (in AV_TIME_BASE units) */ + int64_t next_dts; + ///< dts of the last packet read for this stream (in AV_TIME_BASE units) + int64_t dts; + + const AVCodecDescriptor *codec_desc; + + AVDictionary *decoder_opts; + DecoderOpts dec_opts; + char dec_name[16]; + // decoded media properties, as estimated by opening the decoder + AVFrame *decoded_params; + + AVBSFContext *bsf; + + /* number of packets successfully read for this stream */ + uint64_t nb_packets; + // combined size of all the packets read + uint64_t data_size; +} DemuxStream; typedef struct Demuxer { - InputFile f; + InputFile f; + + // name used for logging + char log_name[32]; + + int64_t wallclock_start; + + /** + * Extra timestamp offset added by discontinuity handling. + */ + int64_t ts_offset_discont; + int64_t last_ts; + + int64_t recording_time; + int accurate_seek; /* number of times input stream should be looped */ - int loop; - /* actual duration of the longest stream in a file at the moment when - * looping happens */ - int64_t duration; - /* time base of the duration */ - AVRational time_base; + int loop; + int have_audio_dec; + /* duration of the looped segment of the input file */ + Timestamp duration; + /* pts with the smallest/largest values ever seen */ + Timestamp min_pts; + Timestamp max_pts; /* number of streams that the user was warned of */ - int nb_streams_warn; + int nb_streams_warn; + + float readrate; + double readrate_initial_burst; - AVThreadMessageQueue *in_thread_queue; - int thread_queue_size; - pthread_t thread; - int non_blocking; + Scheduler *sch; + + AVPacket *pkt_heartbeat; + + int read_started; + int nb_streams_used; + int nb_streams_finished; } Demuxer; -typedef struct DemuxMsg { - AVPacket *pkt; - int looping; +typedef struct DemuxThreadContext { + // packet used for reading from the demuxer + AVPacket *pkt_demux; + // packet for reading from BSFs + AVPacket *pkt_bsf; +} DemuxThreadContext; - // repeat_pict from the demuxer-internal parser - int repeat_pict; -} DemuxMsg; +static DemuxStream *ds_from_ist(InputStream *ist) +{ + return (DemuxStream*)ist; +} static Demuxer *demuxer_from_ifile(InputFile *f) { return (Demuxer*)f; } +InputStream *ist_find_unused(enum AVMediaType type) +{ + for (InputStream *ist = ist_iter(NULL); ist; ist = ist_iter(ist)) { + DemuxStream *ds = ds_from_ist(ist); + if (ist->par->codec_type == type && ds->discard && + ist->user_set_discard != AVDISCARD_ALL) + return ist; + } + return NULL; +} + static void report_new_stream(Demuxer *d, const AVPacket *pkt) { - AVStream *st = d->f.ctx->streams[pkt->stream_index]; + const AVStream *st = d->f.ctx->streams[pkt->stream_index]; if (pkt->stream_index < d->nb_streams_warn) return; - av_log(NULL, AV_LOG_WARNING, - "New %s stream %d:%d at pos:%"PRId64" and DTS:%ss\n", + av_log(d, AV_LOG_WARNING, + "New %s stream with index %d at pos:%"PRId64" and DTS:%ss\n", av_get_media_type_string(st->codecpar->codec_type), - d->f.index, pkt->stream_index, - pkt->pos, av_ts2timestr(pkt->dts, &st->time_base)); + pkt->stream_index, pkt->pos, av_ts2timestr(pkt->dts, &st->time_base)); d->nb_streams_warn = pkt->stream_index + 1; } -static void ifile_duration_update(Demuxer *d, InputStream *ist, - int64_t last_duration) -{ - /* the total duration of the stream, max_pts - min_pts is - * the duration of the stream without the last frame */ - if (ist->max_pts > ist->min_pts && - ist->max_pts - (uint64_t)ist->min_pts < INT64_MAX - last_duration) - last_duration += ist->max_pts - ist->min_pts; - - if (!d->duration || - av_compare_ts(d->duration, d->time_base, - last_duration, ist->st->time_base) < 0) { - d->duration = last_duration; - d->time_base = ist->st->time_base; - } -} - -static int seek_to_start(Demuxer *d) +static int seek_to_start(Demuxer *d, Timestamp end_pts) { InputFile *ifile = &d->f; AVFormatContext *is = ifile->ctx; - InputStream *ist; int ret; ret = avformat_seek_file(is, -1, INT64_MIN, is->start_time, is->start_time, 0); if (ret < 0) return ret; - if (ifile->audio_duration_queue_size) { - /* duration is the length of the last frame in a stream - * when audio stream is present we don't care about - * last video frame length because it's not defined exactly */ - int got_durations = 0; + if (end_pts.ts != AV_NOPTS_VALUE && + (d->max_pts.ts == AV_NOPTS_VALUE || + av_compare_ts(d->max_pts.ts, d->max_pts.tb, end_pts.ts, end_pts.tb) < 0)) + d->max_pts = end_pts; - while (got_durations < ifile->audio_duration_queue_size) { - LastFrameDuration dur; - ret = av_thread_message_queue_recv(ifile->audio_duration_queue, &dur, 0); - if (ret < 0) - return ret; - got_durations++; + if (d->max_pts.ts != AV_NOPTS_VALUE) { + int64_t min_pts = d->min_pts.ts == AV_NOPTS_VALUE ? 0 : d->min_pts.ts; + d->duration.ts = d->max_pts.ts - av_rescale_q(min_pts, d->min_pts.tb, d->max_pts.tb); + } + d->duration.tb = d->max_pts.tb; - ist = ifile->streams[dur.stream_idx]; - ifile_duration_update(d, ist, dur.duration); - } - } else { - for (int i = 0; i < ifile->nb_streams; i++) { - int64_t duration = 0; - ist = ifile->streams[i]; - - if (ist->framerate.num) { - duration = av_rescale_q(1, av_inv_q(ist->framerate), ist->st->time_base); - } else if (ist->st->avg_frame_rate.num) { - duration = av_rescale_q(1, av_inv_q(ist->st->avg_frame_rate), ist->st->time_base); - } else { - duration = 1; + if (d->loop > 0) + d->loop--; + + return ret; +} + +static void ts_discontinuity_detect(Demuxer *d, InputStream *ist, + AVPacket *pkt) +{ + InputFile *ifile = &d->f; + DemuxStream *ds = ds_from_ist(ist); + const int fmt_is_discont = ifile->ctx->iformat->flags & AVFMT_TS_DISCONT; + int disable_discontinuity_correction = copy_ts; + int64_t pkt_dts = av_rescale_q_rnd(pkt->dts, pkt->time_base, AV_TIME_BASE_Q, + AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + + if (copy_ts && ds->next_dts != AV_NOPTS_VALUE && + fmt_is_discont && ist->st->pts_wrap_bits < 60) { + int64_t wrap_dts = av_rescale_q_rnd(pkt->dts + (1LL<st->pts_wrap_bits), + pkt->time_base, AV_TIME_BASE_Q, + AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX); + if (FFABS(wrap_dts - ds->next_dts) < FFABS(pkt_dts - ds->next_dts)/10) + disable_discontinuity_correction = 0; + } + + if (ds->next_dts != AV_NOPTS_VALUE && !disable_discontinuity_correction) { + int64_t delta = pkt_dts - ds->next_dts; + if (fmt_is_discont) { + if (FFABS(delta) > 1LL * dts_delta_threshold * AV_TIME_BASE || + pkt_dts + AV_TIME_BASE/10 < ds->dts) { + d->ts_offset_discont -= delta; + av_log(ist, AV_LOG_WARNING, + "timestamp discontinuity " + "(stream id=%d): %"PRId64", new offset= %"PRId64"\n", + ist->st->id, delta, d->ts_offset_discont); + pkt->dts -= av_rescale_q(delta, AV_TIME_BASE_Q, pkt->time_base); + if (pkt->pts != AV_NOPTS_VALUE) + pkt->pts -= av_rescale_q(delta, AV_TIME_BASE_Q, pkt->time_base); + } + } else { + if (FFABS(delta) > 1LL * dts_error_threshold * AV_TIME_BASE) { + av_log(NULL, AV_LOG_WARNING, + "DTS %"PRId64", next:%"PRId64" st:%d invalid dropping\n", + pkt->dts, ds->next_dts, pkt->stream_index); + pkt->dts = AV_NOPTS_VALUE; + } + if (pkt->pts != AV_NOPTS_VALUE){ + int64_t pkt_pts = av_rescale_q(pkt->pts, pkt->time_base, AV_TIME_BASE_Q); + delta = pkt_pts - ds->next_dts; + if (FFABS(delta) > 1LL * dts_error_threshold * AV_TIME_BASE) { + av_log(NULL, AV_LOG_WARNING, + "PTS %"PRId64", next:%"PRId64" invalid dropping st:%d\n", + pkt->pts, ds->next_dts, pkt->stream_index); + pkt->pts = AV_NOPTS_VALUE; + } } + } + } else if (ds->next_dts == AV_NOPTS_VALUE && !copy_ts && + fmt_is_discont && d->last_ts != AV_NOPTS_VALUE) { + int64_t delta = pkt_dts - d->last_ts; + if (FFABS(delta) > 1LL * dts_delta_threshold * AV_TIME_BASE) { + d->ts_offset_discont -= delta; + av_log(NULL, AV_LOG_DEBUG, + "Inter stream timestamp discontinuity %"PRId64", new offset= %"PRId64"\n", + delta, d->ts_offset_discont); + pkt->dts -= av_rescale_q(delta, AV_TIME_BASE_Q, pkt->time_base); + if (pkt->pts != AV_NOPTS_VALUE) + pkt->pts -= av_rescale_q(delta, AV_TIME_BASE_Q, pkt->time_base); + } + } + + d->last_ts = av_rescale_q(pkt->dts, pkt->time_base, AV_TIME_BASE_Q); +} + +static void ts_discontinuity_process(Demuxer *d, InputStream *ist, + AVPacket *pkt) +{ + int64_t offset = av_rescale_q(d->ts_offset_discont, AV_TIME_BASE_Q, + pkt->time_base); - ifile_duration_update(d, ist, duration); + // apply previously-detected timestamp-discontinuity offset + // (to all streams, not just audio/video) + if (pkt->dts != AV_NOPTS_VALUE) + pkt->dts += offset; + if (pkt->pts != AV_NOPTS_VALUE) + pkt->pts += offset; + + // detect timestamp discontinuities for audio/video + if ((ist->par->codec_type == AVMEDIA_TYPE_VIDEO || + ist->par->codec_type == AVMEDIA_TYPE_AUDIO) && + pkt->dts != AV_NOPTS_VALUE) + ts_discontinuity_detect(d, ist, pkt); +} + +static int ist_dts_update(DemuxStream *ds, AVPacket *pkt, FrameData *fd) +{ + InputStream *ist = &ds->ist; + const AVCodecParameters *par = ist->par; + + if (!ds->saw_first_ts) { + ds->first_dts = + ds->dts = ist->st->avg_frame_rate.num ? - ist->par->video_delay * AV_TIME_BASE / av_q2d(ist->st->avg_frame_rate) : 0; + if (pkt->pts != AV_NOPTS_VALUE) { + ds->first_dts = + ds->dts += av_rescale_q(pkt->pts, pkt->time_base, AV_TIME_BASE_Q); } + ds->saw_first_ts = 1; } - if (d->loop > 0) - d->loop--; + if (ds->next_dts == AV_NOPTS_VALUE) + ds->next_dts = ds->dts; - return ret; + if (pkt->dts != AV_NOPTS_VALUE) + ds->next_dts = ds->dts = av_rescale_q(pkt->dts, pkt->time_base, AV_TIME_BASE_Q); + + ds->dts = ds->next_dts; + switch (par->codec_type) { + case AVMEDIA_TYPE_AUDIO: + av_assert1(pkt->duration >= 0); + if (par->sample_rate) { + ds->next_dts += ((int64_t)AV_TIME_BASE * par->frame_size) / + par->sample_rate; + } else { + ds->next_dts += av_rescale_q(pkt->duration, pkt->time_base, AV_TIME_BASE_Q); + } + break; + case AVMEDIA_TYPE_VIDEO: + if (ist->framerate.num) { + // TODO: Remove work-around for c99-to-c89 issue 7 + AVRational time_base_q = AV_TIME_BASE_Q; + int64_t next_dts = av_rescale_q(ds->next_dts, time_base_q, av_inv_q(ist->framerate)); + ds->next_dts = av_rescale_q(next_dts + 1, av_inv_q(ist->framerate), time_base_q); + } else if (pkt->duration) { + ds->next_dts += av_rescale_q(pkt->duration, pkt->time_base, AV_TIME_BASE_Q); + } else if (ist->par->framerate.num != 0) { + AVRational field_rate = av_mul_q(ist->par->framerate, + (AVRational){ 2, 1 }); + int fields = 2; + + if (ds->codec_desc && + (ds->codec_desc->props & AV_CODEC_PROP_FIELDS) && + av_stream_get_parser(ist->st)) + fields = 1 + av_stream_get_parser(ist->st)->repeat_pict; + + ds->next_dts += av_rescale_q(fields, av_inv_q(field_rate), AV_TIME_BASE_Q); + } + break; + } + + fd->dts_est = ds->dts; + + return 0; } -static void ts_fixup(Demuxer *d, AVPacket *pkt, int *repeat_pict) +static int ts_fixup(Demuxer *d, AVPacket *pkt, FrameData *fd) { InputFile *ifile = &d->f; InputStream *ist = ifile->streams[pkt->stream_index]; + DemuxStream *ds = ds_from_ist(ist); const int64_t start_time = ifile->start_time_effective; int64_t duration; + int ret; - if (debug_ts) { - av_log(NULL, AV_LOG_INFO, "demuxer -> ist_index:%d:%d type:%s " - "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s\n", - ifile->index, pkt->stream_index, - av_get_media_type_string(ist->st->codecpar->codec_type), - av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ist->st->time_base), - av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ist->st->time_base), - av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &ist->st->time_base)); + pkt->time_base = ist->st->time_base; + +#define SHOW_TS_DEBUG(tag_) \ + if (debug_ts) { \ + av_log(ist, AV_LOG_INFO, "%s -> ist_index:%d:%d type:%s " \ + "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s\n", \ + tag_, ifile->index, pkt->stream_index, \ + av_get_media_type_string(ist->st->codecpar->codec_type), \ + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &pkt->time_base), \ + av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &pkt->time_base), \ + av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &pkt->time_base)); \ } - if (!ist->wrap_correction_done && start_time != AV_NOPTS_VALUE && + SHOW_TS_DEBUG("demuxer"); + + if (!ds->wrap_correction_done && start_time != AV_NOPTS_VALUE && ist->st->pts_wrap_bits < 64) { int64_t stime, stime2; - stime = av_rescale_q(start_time, AV_TIME_BASE_Q, ist->st->time_base); + stime = av_rescale_q(start_time, AV_TIME_BASE_Q, pkt->time_base); stime2= stime + (1ULL<st->pts_wrap_bits); - ist->wrap_correction_done = 1; + ds->wrap_correction_done = 1; if(stime2 > stime && pkt->dts != AV_NOPTS_VALUE && pkt->dts > stime + (1LL<<(ist->st->pts_wrap_bits-1))) { pkt->dts -= 1ULL<st->pts_wrap_bits; - ist->wrap_correction_done = 0; + ds->wrap_correction_done = 0; } if(stime2 > stime && pkt->pts != AV_NOPTS_VALUE && pkt->pts > stime + (1LL<<(ist->st->pts_wrap_bits-1))) { pkt->pts -= 1ULL<st->pts_wrap_bits; - ist->wrap_correction_done = 0; + ds->wrap_correction_done = 0; } } if (pkt->dts != AV_NOPTS_VALUE) - pkt->dts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, ist->st->time_base); + pkt->dts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, pkt->time_base); if (pkt->pts != AV_NOPTS_VALUE) - pkt->pts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, ist->st->time_base); + pkt->pts += av_rescale_q(ifile->ts_offset, AV_TIME_BASE_Q, pkt->time_base); if (pkt->pts != AV_NOPTS_VALUE) - pkt->pts *= ist->ts_scale; + pkt->pts *= ds->ts_scale; if (pkt->dts != AV_NOPTS_VALUE) - pkt->dts *= ist->ts_scale; + pkt->dts *= ds->ts_scale; - duration = av_rescale_q(d->duration, d->time_base, ist->st->time_base); + duration = av_rescale_q(d->duration.ts, d->duration.tb, pkt->time_base); if (pkt->pts != AV_NOPTS_VALUE) { + // audio decoders take precedence for estimating total file duration + int64_t pkt_duration = d->have_audio_dec ? 0 : pkt->duration; + pkt->pts += duration; - ist->max_pts = FFMAX(pkt->pts, ist->max_pts); - ist->min_pts = FFMIN(pkt->pts, ist->min_pts); + + // update max/min pts that will be used to compute total file duration + // when using -stream_loop + if (d->max_pts.ts == AV_NOPTS_VALUE || + av_compare_ts(d->max_pts.ts, d->max_pts.tb, + pkt->pts + pkt_duration, pkt->time_base) < 0) { + d->max_pts = (Timestamp){ .ts = pkt->pts + pkt_duration, + .tb = pkt->time_base }; + } + if (d->min_pts.ts == AV_NOPTS_VALUE || + av_compare_ts(d->min_pts.ts, d->min_pts.tb, + pkt->pts, pkt->time_base) > 0) { + d->min_pts = (Timestamp){ .ts = pkt->pts, + .tb = pkt->time_base }; + } } if (pkt->dts != AV_NOPTS_VALUE) pkt->dts += duration; - *repeat_pict = -1; - if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && - av_stream_get_parser(ist->st)) - *repeat_pict = av_stream_get_parser(ist->st)->repeat_pict; + SHOW_TS_DEBUG("demuxer+tsfixup"); + + // detect and try to correct for timestamp discontinuities + ts_discontinuity_process(d, ist, pkt); + + // update estimated/predicted dts + ret = ist_dts_update(ds, pkt, fd); + if (ret < 0) + return ret; + + return 0; +} + +static int input_packet_process(Demuxer *d, AVPacket *pkt, unsigned *send_flags) +{ + InputFile *f = &d->f; + InputStream *ist = f->streams[pkt->stream_index]; + DemuxStream *ds = ds_from_ist(ist); + FrameData *fd; + int ret = 0; + + fd = packet_data(pkt); + if (!fd) + return AVERROR(ENOMEM); + + ret = ts_fixup(d, pkt, fd); + if (ret < 0) + return ret; + + if (d->recording_time != INT64_MAX) { + int64_t start_time = 0; + if (copy_ts) { + start_time += f->start_time != AV_NOPTS_VALUE ? f->start_time : 0; + start_time += start_at_zero ? 0 : f->start_time_effective; + } + if (ds->dts >= d->recording_time + start_time) + *send_flags |= DEMUX_SEND_STREAMCOPY_EOF; + } + + ds->data_size += pkt->size; + ds->nb_packets++; + + fd->wallclock[LATENCY_PROBE_DEMUX] = av_gettime_relative(); + + if (debug_ts) { + av_log(NULL, AV_LOG_INFO, "demuxer+ffmpeg -> ist_index:%d:%d type:%s pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s off:%s off_time:%s\n", + f->index, pkt->stream_index, + av_get_media_type_string(ist->par->codec_type), + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &pkt->time_base), + av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &pkt->time_base), + av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &pkt->time_base), + av_ts2str(f->ts_offset), av_ts2timestr(f->ts_offset, &AV_TIME_BASE_Q)); + } + + return 0; +} + +static void readrate_sleep(Demuxer *d) +{ + InputFile *f = &d->f; + int64_t file_start = copy_ts * ( + (f->start_time_effective != AV_NOPTS_VALUE ? f->start_time_effective * !start_at_zero : 0) + + (f->start_time != AV_NOPTS_VALUE ? f->start_time : 0) + ); + int64_t burst_until = AV_TIME_BASE * d->readrate_initial_burst; + for (int i = 0; i < f->nb_streams; i++) { + InputStream *ist = f->streams[i]; + DemuxStream *ds = ds_from_ist(ist); + int64_t stream_ts_offset, pts, now; + stream_ts_offset = FFMAX(ds->first_dts != AV_NOPTS_VALUE ? ds->first_dts : 0, file_start); + pts = av_rescale(ds->dts, 1000000, AV_TIME_BASE); + now = (av_gettime_relative() - d->wallclock_start) * d->readrate + stream_ts_offset; + if (pts - burst_until > now) + av_usleep(pts - burst_until - now); + } +} + +static int do_send(Demuxer *d, DemuxStream *ds, AVPacket *pkt, unsigned flags, + const char *pkt_desc) +{ + int ret; + + pkt->stream_index = ds->sch_idx_stream; + + ret = sch_demux_send(d->sch, d->f.index, pkt, flags); + if (ret == AVERROR_EOF) { + av_packet_unref(pkt); + + av_log(ds, AV_LOG_VERBOSE, "All consumers of this stream are done\n"); + ds->finished = 1; + + if (++d->nb_streams_finished == d->nb_streams_used) { + av_log(d, AV_LOG_VERBOSE, "All consumers are done\n"); + return AVERROR_EOF; + } + } else if (ret < 0) { + if (ret != AVERROR_EXIT) + av_log(d, AV_LOG_ERROR, + "Unable to send %s packet to consumers: %s\n", + pkt_desc, av_err2str(ret)); + return ret; + } + + return 0; +} + +static int demux_send(Demuxer *d, DemuxThreadContext *dt, DemuxStream *ds, + AVPacket *pkt, unsigned flags) +{ + InputFile *f = &d->f; + int ret; + + // pkt can be NULL only when flushing BSFs + av_assert0(ds->bsf || pkt); + + // send heartbeat for sub2video streams + if (d->pkt_heartbeat && pkt && pkt->pts != AV_NOPTS_VALUE) { + for (int i = 0; i < f->nb_streams; i++) { + DemuxStream *ds1 = ds_from_ist(f->streams[i]); + + if (ds1->finished || !ds1->have_sub2video) + continue; + + d->pkt_heartbeat->pts = pkt->pts; + d->pkt_heartbeat->time_base = pkt->time_base; + d->pkt_heartbeat->opaque = (void*)(intptr_t)PKT_OPAQUE_SUB_HEARTBEAT; + + ret = do_send(d, ds1, d->pkt_heartbeat, 0, "heartbeat"); + if (ret < 0) + return ret; + } + } + + if (ds->bsf) { + if (pkt) + av_packet_rescale_ts(pkt, pkt->time_base, ds->bsf->time_base_in); + + ret = av_bsf_send_packet(ds->bsf, pkt); + if (ret < 0) { + if (pkt) + av_packet_unref(pkt); + av_log(ds, AV_LOG_ERROR, "Error submitting a packet for filtering: %s\n", + av_err2str(ret)); + return ret; + } + + while (1) { + ret = av_bsf_receive_packet(ds->bsf, dt->pkt_bsf); + if (ret == AVERROR(EAGAIN)) + return 0; + else if (ret < 0) { + if (ret != AVERROR_EOF) + av_log(ds, AV_LOG_ERROR, + "Error applying bitstream filters to a packet: %s\n", + av_err2str(ret)); + return ret; + } + + dt->pkt_bsf->time_base = ds->bsf->time_base_out; + + ret = do_send(d, ds, dt->pkt_bsf, 0, "filtered"); + if (ret < 0) { + av_packet_unref(dt->pkt_bsf); + return ret; + } + } + } else { + ret = do_send(d, ds, pkt, flags, "demuxed"); + if (ret < 0) + return ret; + } + + return 0; +} + +static int demux_bsf_flush(Demuxer *d, DemuxThreadContext *dt) +{ + InputFile *f = &d->f; + int ret; + + for (unsigned i = 0; i < f->nb_streams; i++) { + DemuxStream *ds = ds_from_ist(f->streams[i]); + + if (!ds->bsf) + continue; + + ret = demux_send(d, dt, ds, NULL, 0); + ret = (ret == AVERROR_EOF) ? 0 : (ret < 0) ? ret : AVERROR_BUG; + if (ret < 0) { + av_log(ds, AV_LOG_ERROR, "Error flushing BSFs: %s\n", + av_err2str(ret)); + return ret; + } + + av_bsf_flush(ds->bsf); + } + + return 0; +} + +static void discard_unused_programs(InputFile *ifile) +{ + for (int j = 0; j < ifile->ctx->nb_programs; j++) { + AVProgram *p = ifile->ctx->programs[j]; + int discard = AVDISCARD_ALL; + + for (int k = 0; k < p->nb_stream_indexes; k++) { + DemuxStream *ds = ds_from_ist(ifile->streams[p->stream_index[k]]); + + if (!ds->discard) { + discard = AVDISCARD_DEFAULT; + break; + } + } + p->discard = discard; + } } static void thread_set_name(InputFile *f) @@ -234,280 +652,431 @@ static void thread_set_name(InputFile *f) ff_thread_setname(name); } -static void *input_thread(void *arg) +static void demux_thread_uninit(DemuxThreadContext *dt) +{ + av_packet_free(&dt->pkt_demux); + av_packet_free(&dt->pkt_bsf); + + memset(dt, 0, sizeof(*dt)); +} + +static int demux_thread_init(DemuxThreadContext *dt) +{ + memset(dt, 0, sizeof(*dt)); + + dt->pkt_demux = av_packet_alloc(); + if (!dt->pkt_demux) + return AVERROR(ENOMEM); + + dt->pkt_bsf = av_packet_alloc(); + if (!dt->pkt_bsf) + return AVERROR(ENOMEM); + + return 0; +} + +static int input_thread(void *arg) { Demuxer *d = arg; InputFile *f = &d->f; - AVPacket *pkt; - unsigned flags = d->non_blocking ? AV_THREAD_MESSAGE_NONBLOCK : 0; + + DemuxThreadContext dt; + int ret = 0; - pkt = av_packet_alloc(); - if (!pkt) { - ret = AVERROR(ENOMEM); + ret = demux_thread_init(&dt); + if (ret < 0) goto finish; - } thread_set_name(f); + discard_unused_programs(f); + + d->read_started = 1; + d->wallclock_start = av_gettime_relative(); + while (1) { - DemuxMsg msg = { NULL }; + DemuxStream *ds; + unsigned send_flags = 0; - ret = av_read_frame(f->ctx, pkt); + ret = av_read_frame(f->ctx, dt.pkt_demux); if (ret == AVERROR(EAGAIN)) { av_usleep(10000); continue; } if (ret < 0) { + int ret_bsf; + + if (ret == AVERROR_EOF) + av_log(d, AV_LOG_VERBOSE, "EOF while reading input\n"); + else { + av_log(d, AV_LOG_ERROR, "Error during demuxing: %s\n", + av_err2str(ret)); + ret = exit_on_error ? ret : 0; + } + + ret_bsf = demux_bsf_flush(d, &dt); + ret = err_merge(ret == AVERROR_EOF ? 0 : ret, ret_bsf); + if (d->loop) { - /* signal looping to the consumer thread */ - msg.looping = 1; - ret = av_thread_message_queue_send(d->in_thread_queue, &msg, 0); + /* signal looping to our consumers */ + dt.pkt_demux->stream_index = -1; + ret = sch_demux_send(d->sch, f->index, dt.pkt_demux, 0); if (ret >= 0) - ret = seek_to_start(d); + ret = seek_to_start(d, (Timestamp){ .ts = dt.pkt_demux->pts, + .tb = dt.pkt_demux->time_base }); if (ret >= 0) continue; /* fallthrough to the error path */ } - if (ret == AVERROR_EOF) - av_log(NULL, AV_LOG_VERBOSE, "EOF in input file %d\n", f->index); - else - av_log(NULL, AV_LOG_ERROR, "Error demuxing input file %d: %s\n", - f->index, av_err2str(ret)); - break; } if (do_pkt_dump) { - av_pkt_dump_log2(NULL, AV_LOG_INFO, pkt, do_hex_dump, - f->ctx->streams[pkt->stream_index]); + av_pkt_dump_log2(NULL, AV_LOG_INFO, dt.pkt_demux, do_hex_dump, + f->ctx->streams[dt.pkt_demux->stream_index]); } /* the following test is needed in case new streams appear dynamically in stream : we ignore them */ - if (pkt->stream_index >= f->nb_streams) { - report_new_stream(d, pkt); - av_packet_unref(pkt); + ds = dt.pkt_demux->stream_index < f->nb_streams ? + ds_from_ist(f->streams[dt.pkt_demux->stream_index]) : NULL; + if (!ds || ds->discard || ds->finished) { + report_new_stream(d, dt.pkt_demux); + av_packet_unref(dt.pkt_demux); continue; } - if (pkt->flags & AV_PKT_FLAG_CORRUPT) { - av_log(NULL, exit_on_error ? AV_LOG_FATAL : AV_LOG_WARNING, - "%s: corrupt input packet in stream %d\n", - f->ctx->url, pkt->stream_index); + if (dt.pkt_demux->flags & AV_PKT_FLAG_CORRUPT) { + av_log(d, exit_on_error ? AV_LOG_FATAL : AV_LOG_WARNING, + "corrupt input packet in stream %d\n", + dt.pkt_demux->stream_index); if (exit_on_error) { - av_packet_unref(pkt); + av_packet_unref(dt.pkt_demux); ret = AVERROR_INVALIDDATA; break; } } - ts_fixup(d, pkt, &msg.repeat_pict); - - msg.pkt = av_packet_alloc(); - if (!msg.pkt) { - av_packet_unref(pkt); - ret = AVERROR(ENOMEM); + ret = input_packet_process(d, dt.pkt_demux, &send_flags); + if (ret < 0) break; - } - av_packet_move_ref(msg.pkt, pkt); - ret = av_thread_message_queue_send(d->in_thread_queue, &msg, flags); - if (flags && ret == AVERROR(EAGAIN)) { - flags = 0; - ret = av_thread_message_queue_send(d->in_thread_queue, &msg, flags); - av_log(f->ctx, AV_LOG_WARNING, - "Thread message queue blocking; consider raising the " - "thread_queue_size option (current value: %d)\n", - d->thread_queue_size); - } - if (ret < 0) { - if (ret != AVERROR_EOF) - av_log(f->ctx, AV_LOG_ERROR, - "Unable to send packet to main thread: %s\n", - av_err2str(ret)); - av_packet_free(&msg.pkt); + + if (d->readrate) + readrate_sleep(d); + + ret = demux_send(d, &dt, ds, dt.pkt_demux, send_flags); + if (ret < 0) break; - } } + // EOF/EXIT is normal termination + if (ret == AVERROR_EOF || ret == AVERROR_EXIT) + ret = 0; + finish: - av_assert0(ret < 0); - av_thread_message_queue_set_err_recv(d->in_thread_queue, ret); + demux_thread_uninit(&dt); - av_packet_free(&pkt); + return ret; +} - av_log(NULL, AV_LOG_VERBOSE, "Terminating demuxer thread %d\n", f->index); +static void demux_final_stats(Demuxer *d) +{ + InputFile *f = &d->f; + uint64_t total_packets = 0, total_size = 0; - return NULL; + av_log(f, AV_LOG_VERBOSE, "Input file #%d (%s):\n", + f->index, f->ctx->url); + + for (int j = 0; j < f->nb_streams; j++) { + InputStream *ist = f->streams[j]; + DemuxStream *ds = ds_from_ist(ist); + enum AVMediaType type = ist->par->codec_type; + + if (ds->discard || type == AVMEDIA_TYPE_ATTACHMENT) + continue; + + total_size += ds->data_size; + total_packets += ds->nb_packets; + + av_log(f, AV_LOG_VERBOSE, " Input stream #%d:%d (%s): ", + f->index, j, av_get_media_type_string(type)); + av_log(f, AV_LOG_VERBOSE, "%"PRIu64" packets read (%"PRIu64" bytes); ", + ds->nb_packets, ds->data_size); + + if (ds->decoding_needed) { + av_log(f, AV_LOG_VERBOSE, + "%"PRIu64" frames decoded; %"PRIu64" decode errors", + ist->decoder->frames_decoded, ist->decoder->decode_errors); + if (type == AVMEDIA_TYPE_AUDIO) + av_log(f, AV_LOG_VERBOSE, " (%"PRIu64" samples)", ist->decoder->samples_decoded); + av_log(f, AV_LOG_VERBOSE, "; "); + } + + av_log(f, AV_LOG_VERBOSE, "\n"); + } + + av_log(f, AV_LOG_VERBOSE, " Total: %"PRIu64" packets (%"PRIu64" bytes) demuxed\n", + total_packets, total_size); } -static void thread_stop(Demuxer *d) +static void ist_free(InputStream **pist) { - InputFile *f = &d->f; - DemuxMsg msg; + InputStream *ist = *pist; + DemuxStream *ds; - if (!d->in_thread_queue) + if (!ist) return; - av_thread_message_queue_set_err_send(d->in_thread_queue, AVERROR_EOF); - while (av_thread_message_queue_recv(d->in_thread_queue, &msg, 0) >= 0) - av_packet_free(&msg.pkt); + ds = ds_from_ist(ist); + + dec_free(&ist->decoder); + + av_dict_free(&ds->decoder_opts); + av_freep(&ist->filters); + av_freep(&ist->outputs); + av_freep(&ds->dec_opts.hwaccel_device); + + avcodec_parameters_free(&ist->par); + + av_frame_free(&ds->decoded_params); - pthread_join(d->thread, NULL); - av_thread_message_queue_free(&d->in_thread_queue); - av_thread_message_queue_free(&f->audio_duration_queue); + av_bsf_free(&ds->bsf); + + av_freep(pist); } -static int thread_start(Demuxer *d) +void ifile_close(InputFile **pf) { - int ret; - InputFile *f = &d->f; - - if (d->thread_queue_size <= 0) - d->thread_queue_size = (nb_input_files > 1 ? 8 : 1); + InputFile *f = *pf; + Demuxer *d = demuxer_from_ifile(f); - if (nb_input_files > 1 && - (f->ctx->pb ? !f->ctx->pb->seekable : - strcmp(f->ctx->iformat->name, "lavfi"))) - d->non_blocking = 1; - ret = av_thread_message_queue_alloc(&d->in_thread_queue, - d->thread_queue_size, sizeof(DemuxMsg)); - if (ret < 0) - return ret; + if (!f) + return; - if (d->loop) { - int nb_audio_dec = 0; + if (d->read_started) + demux_final_stats(d); - for (int i = 0; i < f->nb_streams; i++) { - InputStream *ist = f->streams[i]; - nb_audio_dec += !!(ist->decoding_needed && - ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO); - } + for (int i = 0; i < f->nb_streams; i++) + ist_free(&f->streams[i]); + av_freep(&f->streams); - if (nb_audio_dec) { - ret = av_thread_message_queue_alloc(&f->audio_duration_queue, - nb_audio_dec, sizeof(LastFrameDuration)); - if (ret < 0) - goto fail; - f->audio_duration_queue_size = nb_audio_dec; - } - } + avformat_close_input(&f->ctx); - if ((ret = pthread_create(&d->thread, NULL, input_thread, d))) { - av_log(NULL, AV_LOG_ERROR, "pthread_create failed: %s. Try to increase `ulimit -v` or decrease `ulimit -s`.\n", strerror(ret)); - ret = AVERROR(ret); - goto fail; - } + av_packet_free(&d->pkt_heartbeat); - return 0; -fail: - av_thread_message_queue_free(&d->in_thread_queue); - return ret; + av_freep(pf); } -int ifile_get_packet(InputFile *f, AVPacket **pkt) +static int ist_use(InputStream *ist, int decoding_needed) { - Demuxer *d = demuxer_from_ifile(f); - InputStream *ist; - DemuxMsg msg; + Demuxer *d = demuxer_from_ifile(ist->file); + DemuxStream *ds = ds_from_ist(ist); int ret; - if (!d->in_thread_queue) { - ret = thread_start(d); + if (ist->user_set_discard == AVDISCARD_ALL) { + av_log(ist, AV_LOG_ERROR, "Cannot %s a disabled input stream\n", + decoding_needed ? "decode" : "streamcopy"); + return AVERROR(EINVAL); + } + + if (decoding_needed && !ist->dec) { + av_log(ist, AV_LOG_ERROR, + "Decoding requested, but no decoder found for: %s\n", + avcodec_get_name(ist->par->codec_id)); + return AVERROR(EINVAL); + } + + if (ds->sch_idx_stream < 0) { + ret = sch_add_demux_stream(d->sch, d->f.index); if (ret < 0) return ret; + ds->sch_idx_stream = ret; } - if (f->readrate || f->rate_emu) { - int i; - int64_t file_start = copy_ts * ( - (f->start_time_effective != AV_NOPTS_VALUE ? f->start_time_effective * !start_at_zero : 0) + - (f->start_time != AV_NOPTS_VALUE ? f->start_time : 0) - ); - float scale = f->rate_emu ? 1.0 : f->readrate; - for (i = 0; i < f->nb_streams; i++) { - InputStream *ist = f->streams[i]; - int64_t stream_ts_offset, pts, now; - if (!ist->nb_packets || (ist->decoding_needed && !ist->got_output)) continue; - stream_ts_offset = FFMAX(ist->first_dts != AV_NOPTS_VALUE ? ist->first_dts : 0, file_start); - pts = av_rescale(ist->dts, 1000000, AV_TIME_BASE); - now = (av_gettime_relative() - ist->start) * scale + stream_ts_offset; - if (pts > now) - return AVERROR(EAGAIN); - } + if (ds->discard) { + ds->discard = 0; + d->nb_streams_used++; } - ret = av_thread_message_queue_recv(d->in_thread_queue, &msg, - d->non_blocking ? - AV_THREAD_MESSAGE_NONBLOCK : 0); - if (ret < 0) - return ret; - if (msg.looping) - return 1; + ist->st->discard = ist->user_set_discard; + ds->decoding_needed |= decoding_needed; + ds->streamcopy_needed |= !decoding_needed; + + if (decoding_needed && ds->sch_idx_dec < 0) { + int is_audio = ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO; - ist = f->streams[msg.pkt->stream_index]; - ist->last_pkt_repeat_pict = msg.repeat_pict; + ds->dec_opts.flags = (!!ist->fix_sub_duration * DECODER_FLAG_FIX_SUB_DURATION) | + (!!(d->f.ctx->iformat->flags & AVFMT_NOTIMESTAMPS) * DECODER_FLAG_TS_UNRELIABLE) | + (!!(d->loop && is_audio) * DECODER_FLAG_SEND_END_TS) +#if FFMPEG_OPT_TOP + | ((ist->top_field_first >= 0) * DECODER_FLAG_TOP_FIELD_FIRST) +#endif + ; + + if (ist->framerate.num) { + ds->dec_opts.flags |= DECODER_FLAG_FRAMERATE_FORCED; + ds->dec_opts.framerate = ist->framerate; + } else + ds->dec_opts.framerate = ist->st->avg_frame_rate; + + if (ist->dec->id == AV_CODEC_ID_DVB_SUBTITLE && + (ds->decoding_needed & DECODING_FOR_OST)) { + av_dict_set(&ds->decoder_opts, "compute_edt", "1", AV_DICT_DONT_OVERWRITE); + if (ds->decoding_needed & DECODING_FOR_FILTER) + av_log(ist, AV_LOG_WARNING, + "Warning using DVB subtitles for filtering and output at the " + "same time is not fully supported, also see -compute_edt [0|1]\n"); + } + + snprintf(ds->dec_name, sizeof(ds->dec_name), "%d:%d", ist->file->index, ist->index); + ds->dec_opts.name = ds->dec_name; + + ds->dec_opts.codec = ist->dec; + ds->dec_opts.par = ist->par; + + ds->dec_opts.log_parent = ist; + + ds->decoded_params = av_frame_alloc(); + if (!ds->decoded_params) + return AVERROR(ENOMEM); + + ret = dec_init(&ist->decoder, d->sch, + &ds->decoder_opts, &ds->dec_opts, ds->decoded_params); + if (ret < 0) + return ret; + ds->sch_idx_dec = ret; + + ret = sch_connect(d->sch, SCH_DSTREAM(d->f.index, ds->sch_idx_stream), + SCH_DEC(ds->sch_idx_dec)); + if (ret < 0) + return ret; + + d->have_audio_dec |= is_audio; + } - *pkt = msg.pkt; return 0; } -static void ist_free(InputStream **pist) +int ist_output_add(InputStream *ist, OutputStream *ost) { - InputStream *ist = *pist; + DemuxStream *ds = ds_from_ist(ist); + int ret; - if (!ist) - return; + ret = ist_use(ist, ost->enc ? DECODING_FOR_OST : 0); + if (ret < 0) + return ret; - av_frame_free(&ist->decoded_frame); - av_packet_free(&ist->pkt); - av_dict_free(&ist->decoder_opts); - avsubtitle_free(&ist->prev_sub.subtitle); - av_frame_free(&ist->sub2video.frame); - av_freep(&ist->filters); - av_freep(&ist->hwaccel_device); - av_freep(&ist->dts_buffer); + ret = GROW_ARRAY(ist->outputs, ist->nb_outputs); + if (ret < 0) + return ret; - avcodec_free_context(&ist->dec_ctx); - avcodec_parameters_free(&ist->par); + ist->outputs[ist->nb_outputs - 1] = ost; - av_freep(pist); + return ost->enc ? ds->sch_idx_dec : ds->sch_idx_stream; } -void ifile_close(InputFile **pf) +int ist_filter_add(InputStream *ist, InputFilter *ifilter, int is_simple, + InputFilterOptions *opts) { - InputFile *f = *pf; - Demuxer *d = demuxer_from_ifile(f); + Demuxer *d = demuxer_from_ifile(ist->file); + DemuxStream *ds = ds_from_ist(ist); + int64_t tsoffset = 0; + int ret; - if (!f) - return; + ret = ist_use(ist, is_simple ? DECODING_FOR_OST : DECODING_FOR_FILTER); + if (ret < 0) + return ret; - thread_stop(d); + ret = GROW_ARRAY(ist->filters, ist->nb_filters); + if (ret < 0) + return ret; - for (int i = 0; i < f->nb_streams; i++) - ist_free(&f->streams[i]); - av_freep(&f->streams); + ist->filters[ist->nb_filters - 1] = ifilter; - avformat_close_input(&f->ctx); + if (ist->par->codec_type == AVMEDIA_TYPE_VIDEO) { + if (ist->framerate.num > 0 && ist->framerate.den > 0) { + opts->framerate = ist->framerate; + opts->flags |= IFILTER_FLAG_CFR; + } else + opts->framerate = av_guess_frame_rate(d->f.ctx, ist->st, NULL); + } else if (ist->par->codec_type == AVMEDIA_TYPE_SUBTITLE) { + /* Compute the size of the canvas for the subtitles stream. + If the subtitles codecpar has set a size, use it. Otherwise use the + maximum dimensions of the video streams in the same file. */ + opts->sub2video_width = ist->par->width; + opts->sub2video_height = ist->par->height; + if (!(opts->sub2video_width && opts->sub2video_height)) { + for (int j = 0; j < d->f.nb_streams; j++) { + AVCodecParameters *par1 = d->f.streams[j]->par; + if (par1->codec_type == AVMEDIA_TYPE_VIDEO) { + opts->sub2video_width = FFMAX(opts->sub2video_width, par1->width); + opts->sub2video_height = FFMAX(opts->sub2video_height, par1->height); + } + } + } - av_freep(pf); + if (!(opts->sub2video_width && opts->sub2video_height)) { + opts->sub2video_width = FFMAX(opts->sub2video_width, 720); + opts->sub2video_height = FFMAX(opts->sub2video_height, 576); + } + + if (!d->pkt_heartbeat) { + d->pkt_heartbeat = av_packet_alloc(); + if (!d->pkt_heartbeat) + return AVERROR(ENOMEM); + } + ds->have_sub2video = 1; + } + + ret = av_frame_copy_props(opts->fallback, ds->decoded_params); + if (ret < 0) + return ret; + opts->fallback->format = ds->decoded_params->format; + opts->fallback->width = ds->decoded_params->width; + opts->fallback->height = ds->decoded_params->height; + + ret = av_channel_layout_copy(&opts->fallback->ch_layout, &ds->decoded_params->ch_layout); + if (ret < 0) + return ret; + + if (copy_ts) { + tsoffset = d->f.start_time == AV_NOPTS_VALUE ? 0 : d->f.start_time; + if (!start_at_zero && d->f.ctx->start_time != AV_NOPTS_VALUE) + tsoffset += d->f.ctx->start_time; + } + opts->trim_start_us = ((d->f.start_time == AV_NOPTS_VALUE) || !d->accurate_seek) ? + AV_NOPTS_VALUE : tsoffset; + opts->trim_end_us = d->recording_time; + + opts->name = av_strdup(ds->dec_name); + if (!opts->name) + return AVERROR(ENOMEM); + + opts->flags |= IFILTER_FLAG_AUTOROTATE * !!(ist->autorotate) | + IFILTER_FLAG_REINIT * !!(ds->reinit_filters); + + return ds->sch_idx_dec; } -static const AVCodec *choose_decoder(const OptionsContext *o, AVFormatContext *s, AVStream *st, - enum HWAccelID hwaccel_id, enum AVHWDeviceType hwaccel_device_type) +static int choose_decoder(const OptionsContext *o, AVFormatContext *s, AVStream *st, + enum HWAccelID hwaccel_id, enum AVHWDeviceType hwaccel_device_type, + const AVCodec **pcodec) { char *codec_name = NULL; MATCH_PER_STREAM_OPT(codec_names, str, codec_name, s, st); if (codec_name) { - const AVCodec *codec = find_codec_or_die(NULL, codec_name, st->codecpar->codec_type, 0); - st->codecpar->codec_id = codec->id; - if (recast_media && st->codecpar->codec_type != codec->type) - st->codecpar->codec_type = codec->type; - return codec; + int ret = find_codec(NULL, codec_name, st->codecpar->codec_type, 0, pcodec); + if (ret < 0) + return ret; + st->codecpar->codec_id = (*pcodec)->id; + if (recast_media && st->codecpar->codec_type != (*pcodec)->type) + st->codecpar->codec_type = (*pcodec)->type; + return 0; } else { if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && hwaccel_id == HWACCEL_GENERIC && @@ -526,38 +1095,40 @@ static const AVCodec *choose_decoder(const OptionsContext *o, AVFormatContext *s if (config->device_type == hwaccel_device_type) { av_log(NULL, AV_LOG_VERBOSE, "Selecting decoder '%s' because of requested hwaccel method %s\n", c->name, av_hwdevice_get_type_name(hwaccel_device_type)); - return c; + *pcodec = c; + return 0; } } } } - return avcodec_find_decoder(st->codecpar->codec_id); + *pcodec = avcodec_find_decoder(st->codecpar->codec_id); + return 0; } } -static int guess_input_channel_layout(InputStream *ist) +static int guess_input_channel_layout(InputStream *ist, AVCodecParameters *par, + int guess_layout_max) { - AVCodecContext *dec = ist->dec_ctx; - - if (dec->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) { + if (par->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) { char layout_name[256]; - if (dec->ch_layout.nb_channels > ist->guess_layout_max) + if (par->ch_layout.nb_channels > guess_layout_max) return 0; - av_channel_layout_default(&dec->ch_layout, dec->ch_layout.nb_channels); - if (dec->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) + av_channel_layout_default(&par->ch_layout, par->ch_layout.nb_channels); + if (par->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) return 0; - av_channel_layout_describe(&dec->ch_layout, layout_name, sizeof(layout_name)); - av_log(NULL, AV_LOG_WARNING, "Guessed Channel Layout for Input Stream " - "#%d.%d : %s\n", ist->file_index, ist->st->index, layout_name); + av_channel_layout_describe(&par->ch_layout, layout_name, sizeof(layout_name)); + av_log(ist, AV_LOG_WARNING, "Guessed Channel Layout: %s\n", layout_name); } return 1; } -static void add_display_matrix_to_stream(const OptionsContext *o, - AVFormatContext *ctx, AVStream *st) +static int add_display_matrix_to_stream(const OptionsContext *o, + AVFormatContext *ctx, InputStream *ist) { + AVStream *st = ist->st; + AVPacketSideData *sd; double rotation = DBL_MAX; int hflip = -1, vflip = -1; int hflip_set = 0, vflip_set = 0, rotation_set = 0; @@ -572,291 +1143,397 @@ static void add_display_matrix_to_stream(const OptionsContext *o, vflip_set = vflip != -1; if (!rotation_set && !hflip_set && !vflip_set) - return; - - buf = (int32_t *)av_stream_new_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, sizeof(int32_t) * 9); - if (!buf) { - av_log(NULL, AV_LOG_FATAL, "Failed to generate a display matrix!\n"); - exit_program(1); + return 0; + + sd = av_packet_side_data_new(&st->codecpar->coded_side_data, + &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_DISPLAYMATRIX, + sizeof(int32_t) * 9, 0); + if (!sd) { + av_log(ist, AV_LOG_FATAL, "Failed to generate a display matrix!\n"); + return AVERROR(ENOMEM); } + buf = (int32_t *)sd->data; av_display_rotation_set(buf, rotation_set ? -(rotation) : -0.0f); av_display_matrix_flip(buf, hflip_set ? hflip : 0, vflip_set ? vflip : 0); + + return 0; } -/* Add all the streams from the given input file to the demuxer */ -static void add_input_streams(const OptionsContext *o, Demuxer *d) +static const char *input_stream_item_name(void *obj) { - InputFile *f = &d->f; - AVFormatContext *ic = f->ctx; - int i, ret; + const DemuxStream *ds = obj; - for (i = 0; i < ic->nb_streams; i++) { - AVStream *st = ic->streams[i]; - AVCodecParameters *par = st->codecpar; - InputStream *ist; - char *framerate = NULL, *hwaccel_device = NULL; - const char *hwaccel = NULL; - char *hwaccel_output_format = NULL; - char *codec_tag = NULL; - char *next; - char *discard_str = NULL; - const AVClass *cc = avcodec_get_class(); - const AVOption *discard_opt = av_opt_find(&cc, "skip_frame", NULL, - 0, AV_OPT_SEARCH_FAKE_OBJ); - - ist = ALLOC_ARRAY_ELEM(f->streams, f->nb_streams); - ist->st = st; - ist->file_index = f->index; - ist->discard = 1; - st->discard = AVDISCARD_ALL; - ist->nb_samples = 0; - ist->first_dts = AV_NOPTS_VALUE; - ist->min_pts = INT64_MAX; - ist->max_pts = INT64_MIN; - - ist->ts_scale = 1.0; - MATCH_PER_STREAM_OPT(ts_scale, dbl, ist->ts_scale, ic, st); - - ist->autorotate = 1; - MATCH_PER_STREAM_OPT(autorotate, i, ist->autorotate, ic, st); - - MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, ic, st); - if (codec_tag) { - uint32_t tag = strtol(codec_tag, &next, 0); - if (*next) { - uint8_t buf[4] = { 0 }; - memcpy(buf, codec_tag, FFMIN(sizeof(buf), strlen(codec_tag))); - tag = AV_RL32(buf); - } + return ds->log_name; +} - st->codecpar->codec_tag = tag; - } +static const AVClass input_stream_class = { + .class_name = "InputStream", + .version = LIBAVUTIL_VERSION_INT, + .item_name = input_stream_item_name, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - add_display_matrix_to_stream(o, ic, st); +static DemuxStream *demux_stream_alloc(Demuxer *d, AVStream *st) +{ + const char *type_str = av_get_media_type_string(st->codecpar->codec_type); + InputFile *f = &d->f; + DemuxStream *ds; - MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st); - MATCH_PER_STREAM_OPT(hwaccel_output_formats, str, - hwaccel_output_format, ic, st); + ds = allocate_array_elem(&f->streams, sizeof(*ds), &f->nb_streams); + if (!ds) + return NULL; - if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "cuvid")) { - av_log(NULL, AV_LOG_WARNING, - "WARNING: defaulting hwaccel_output_format to cuda for compatibility " - "with old commandlines. This behaviour is DEPRECATED and will be removed " - "in the future. Please explicitly set \"-hwaccel_output_format cuda\".\n"); - ist->hwaccel_output_format = AV_PIX_FMT_CUDA; - } else if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "qsv")) { - av_log(NULL, AV_LOG_WARNING, - "WARNING: defaulting hwaccel_output_format to qsv for compatibility " - "with old commandlines. This behaviour is DEPRECATED and will be removed " - "in the future. Please explicitly set \"-hwaccel_output_format qsv\".\n"); - ist->hwaccel_output_format = AV_PIX_FMT_QSV; - } else if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "mediacodec")) { - // There is no real AVHWFrameContext implementation. Set - // hwaccel_output_format to avoid av_hwframe_transfer_data error. - ist->hwaccel_output_format = AV_PIX_FMT_MEDIACODEC; - } else if (hwaccel_output_format) { - ist->hwaccel_output_format = av_get_pix_fmt(hwaccel_output_format); - if (ist->hwaccel_output_format == AV_PIX_FMT_NONE) { - av_log(NULL, AV_LOG_FATAL, "Unrecognised hwaccel output " - "format: %s", hwaccel_output_format); - } - } else { - ist->hwaccel_output_format = AV_PIX_FMT_NONE; - } + ds->sch_idx_stream = -1; + ds->sch_idx_dec = -1; - if (hwaccel) { - // The NVDEC hwaccels use a CUDA device, so remap the name here. - if (!strcmp(hwaccel, "nvdec") || !strcmp(hwaccel, "cuvid")) - hwaccel = "cuda"; - - if (!strcmp(hwaccel, "none")) - ist->hwaccel_id = HWACCEL_NONE; - else if (!strcmp(hwaccel, "auto")) - ist->hwaccel_id = HWACCEL_AUTO; - else { - enum AVHWDeviceType type = av_hwdevice_find_type_by_name(hwaccel); - if (type != AV_HWDEVICE_TYPE_NONE) { - ist->hwaccel_id = HWACCEL_GENERIC; - ist->hwaccel_device_type = type; - } + ds->ist.st = st; + ds->ist.file = f; + ds->ist.index = st->index; + ds->ist.class = &input_stream_class; - if (!ist->hwaccel_id) { - av_log(NULL, AV_LOG_FATAL, "Unrecognized hwaccel: %s.\n", - hwaccel); - av_log(NULL, AV_LOG_FATAL, "Supported hwaccels: "); - type = AV_HWDEVICE_TYPE_NONE; - while ((type = av_hwdevice_iterate_types(type)) != - AV_HWDEVICE_TYPE_NONE) - av_log(NULL, AV_LOG_FATAL, "%s ", - av_hwdevice_get_type_name(type)); - av_log(NULL, AV_LOG_FATAL, "\n"); - exit_program(1); - } - } - } + snprintf(ds->log_name, sizeof(ds->log_name), "%cist#%d:%d/%s", + type_str ? *type_str : '?', d->f.index, st->index, + avcodec_get_name(st->codecpar->codec_id)); - MATCH_PER_STREAM_OPT(hwaccel_devices, str, hwaccel_device, ic, st); - if (hwaccel_device) { - ist->hwaccel_device = av_strdup(hwaccel_device); - if (!ist->hwaccel_device) - report_and_exit(AVERROR(ENOMEM)); - } + return ds; +} - ist->hwaccel_pix_fmt = AV_PIX_FMT_NONE; - } +static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st) +{ + AVFormatContext *ic = d->f.ctx; + AVCodecParameters *par = st->codecpar; + DemuxStream *ds; + InputStream *ist; + char *framerate = NULL, *hwaccel_device = NULL; + const char *hwaccel = NULL; + char *hwaccel_output_format = NULL; + char *codec_tag = NULL; + char *bsfs = NULL; + char *next; + char *discard_str = NULL; + int ret; + + ds = demux_stream_alloc(d, st); + if (!ds) + return AVERROR(ENOMEM); - ist->dec = choose_decoder(o, ic, st, ist->hwaccel_id, ist->hwaccel_device_type); - ist->decoder_opts = filter_codec_opts(o->g->codec_opts, ist->st->codecpar->codec_id, ic, st, ist->dec); + ist = &ds->ist; - ist->reinit_filters = -1; - MATCH_PER_STREAM_OPT(reinit_filters, i, ist->reinit_filters, ic, st); + ds->discard = 1; + st->discard = AVDISCARD_ALL; + ds->first_dts = AV_NOPTS_VALUE; + ds->next_dts = AV_NOPTS_VALUE; - MATCH_PER_STREAM_OPT(discard, str, discard_str, ic, st); - ist->user_set_discard = AVDISCARD_NONE; + ds->dec_opts.time_base = st->time_base; - if ((o->video_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) || - (o->audio_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) || - (o->subtitle_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) || - (o->data_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_DATA)) - ist->user_set_discard = AVDISCARD_ALL; + ds->ts_scale = 1.0; + MATCH_PER_STREAM_OPT(ts_scale, dbl, ds->ts_scale, ic, st); - if (discard_str && av_opt_eval_int(&cc, discard_opt, discard_str, &ist->user_set_discard) < 0) { - av_log(NULL, AV_LOG_ERROR, "Error parsing discard %s.\n", - discard_str); - exit_program(1); + ist->autorotate = 1; + MATCH_PER_STREAM_OPT(autorotate, i, ist->autorotate, ic, st); + + MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, ic, st); + if (codec_tag) { + uint32_t tag = strtol(codec_tag, &next, 0); + if (*next) { + uint8_t buf[4] = { 0 }; + memcpy(buf, codec_tag, FFMIN(sizeof(buf), strlen(codec_tag))); + tag = AV_RL32(buf); } - ist->filter_in_rescale_delta_last = AV_NOPTS_VALUE; - ist->prev_pkt_pts = AV_NOPTS_VALUE; + st->codecpar->codec_tag = tag; + } - ist->dec_ctx = avcodec_alloc_context3(ist->dec); - if (!ist->dec_ctx) - report_and_exit(AVERROR(ENOMEM)); + if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { + ret = add_display_matrix_to_stream(o, ic, ist); + if (ret < 0) + return ret; - ret = avcodec_parameters_to_context(ist->dec_ctx, par); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error initializing the decoder context.\n"); - exit_program(1); + MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st); + MATCH_PER_STREAM_OPT(hwaccel_output_formats, str, + hwaccel_output_format, ic, st); + + if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "cuvid")) { + av_log(ist, AV_LOG_WARNING, + "WARNING: defaulting hwaccel_output_format to cuda for compatibility " + "with old commandlines. This behaviour is DEPRECATED and will be removed " + "in the future. Please explicitly set \"-hwaccel_output_format cuda\".\n"); + ds->dec_opts.hwaccel_output_format = AV_PIX_FMT_CUDA; + } else if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "qsv")) { + av_log(ist, AV_LOG_WARNING, + "WARNING: defaulting hwaccel_output_format to qsv for compatibility " + "with old commandlines. This behaviour is DEPRECATED and will be removed " + "in the future. Please explicitly set \"-hwaccel_output_format qsv\".\n"); + ds->dec_opts.hwaccel_output_format = AV_PIX_FMT_QSV; + } else if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "mediacodec")) { + // There is no real AVHWFrameContext implementation. Set + // hwaccel_output_format to avoid av_hwframe_transfer_data error. + ds->dec_opts.hwaccel_output_format = AV_PIX_FMT_MEDIACODEC; + } else if (hwaccel_output_format) { + ds->dec_opts.hwaccel_output_format = av_get_pix_fmt(hwaccel_output_format); + if (ds->dec_opts.hwaccel_output_format == AV_PIX_FMT_NONE) { + av_log(ist, AV_LOG_FATAL, "Unrecognised hwaccel output " + "format: %s", hwaccel_output_format); + } + } else { + ds->dec_opts.hwaccel_output_format = AV_PIX_FMT_NONE; } - ist->decoded_frame = av_frame_alloc(); - if (!ist->decoded_frame) - report_and_exit(AVERROR(ENOMEM)); + if (hwaccel) { + // The NVDEC hwaccels use a CUDA device, so remap the name here. + if (!strcmp(hwaccel, "nvdec") || !strcmp(hwaccel, "cuvid")) + hwaccel = "cuda"; + + if (!strcmp(hwaccel, "none")) + ds->dec_opts.hwaccel_id = HWACCEL_NONE; + else if (!strcmp(hwaccel, "auto")) + ds->dec_opts.hwaccel_id = HWACCEL_AUTO; + else { + enum AVHWDeviceType type = av_hwdevice_find_type_by_name(hwaccel); + if (type != AV_HWDEVICE_TYPE_NONE) { + ds->dec_opts.hwaccel_id = HWACCEL_GENERIC; + ds->dec_opts.hwaccel_device_type = type; + } - ist->pkt = av_packet_alloc(); - if (!ist->pkt) - report_and_exit(AVERROR(ENOMEM)); + if (!ds->dec_opts.hwaccel_id) { + av_log(ist, AV_LOG_FATAL, "Unrecognized hwaccel: %s.\n", + hwaccel); + av_log(ist, AV_LOG_FATAL, "Supported hwaccels: "); + type = AV_HWDEVICE_TYPE_NONE; + while ((type = av_hwdevice_iterate_types(type)) != + AV_HWDEVICE_TYPE_NONE) + av_log(ist, AV_LOG_FATAL, "%s ", + av_hwdevice_get_type_name(type)); + av_log(ist, AV_LOG_FATAL, "\n"); + return AVERROR(EINVAL); + } + } + } - if (o->bitexact) - ist->dec_ctx->flags |= AV_CODEC_FLAG_BITEXACT; + MATCH_PER_STREAM_OPT(hwaccel_devices, str, hwaccel_device, ic, st); + if (hwaccel_device) { + ds->dec_opts.hwaccel_device = av_strdup(hwaccel_device); + if (!ds->dec_opts.hwaccel_device) + return AVERROR(ENOMEM); + } + } - switch (par->codec_type) { - case AVMEDIA_TYPE_VIDEO: - // avformat_find_stream_info() doesn't set this for us anymore. - ist->dec_ctx->framerate = st->avg_frame_rate; + ret = choose_decoder(o, ic, st, ds->dec_opts.hwaccel_id, + ds->dec_opts.hwaccel_device_type, &ist->dec); + if (ret < 0) + return ret; - MATCH_PER_STREAM_OPT(frame_rates, str, framerate, ic, st); - if (framerate && av_parse_video_rate(&ist->framerate, - framerate) < 0) { - av_log(NULL, AV_LOG_ERROR, "Error parsing framerate %s.\n", - framerate); - exit_program(1); - } + ret = filter_codec_opts(o->g->codec_opts, ist->st->codecpar->codec_id, + ic, st, ist->dec, &ds->decoder_opts); + if (ret < 0) + return ret; - ist->top_field_first = -1; - MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic, st); + ds->reinit_filters = -1; + MATCH_PER_STREAM_OPT(reinit_filters, i, ds->reinit_filters, ic, st); - ist->framerate_guessed = av_guess_frame_rate(ic, st, NULL); + ist->user_set_discard = AVDISCARD_NONE; - break; - case AVMEDIA_TYPE_AUDIO: - ist->guess_layout_max = INT_MAX; - MATCH_PER_STREAM_OPT(guess_layout_max, i, ist->guess_layout_max, ic, st); - guess_input_channel_layout(ist); - break; - case AVMEDIA_TYPE_DATA: - case AVMEDIA_TYPE_SUBTITLE: { - char *canvas_size = NULL; - MATCH_PER_STREAM_OPT(fix_sub_duration, i, ist->fix_sub_duration, ic, st); - MATCH_PER_STREAM_OPT(canvas_sizes, str, canvas_size, ic, st); - if (canvas_size && - av_parse_video_size(&ist->dec_ctx->width, &ist->dec_ctx->height, canvas_size) < 0) { - av_log(NULL, AV_LOG_FATAL, "Invalid canvas size: %s.\n", canvas_size); - exit_program(1); + if ((o->video_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) || + (o->audio_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) || + (o->subtitle_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) || + (o->data_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_DATA)) + ist->user_set_discard = AVDISCARD_ALL; + + MATCH_PER_STREAM_OPT(discard, str, discard_str, ic, st); + if (discard_str) { + ret = av_opt_set(ist->st, "discard", discard_str, 0); + if (ret < 0) { + av_log(ist, AV_LOG_ERROR, "Error parsing discard %s.\n", discard_str); + return ret; + } + ist->user_set_discard = ist->st->discard; + } + + if (o->bitexact) + av_dict_set(&ds->decoder_opts, "flags", "+bitexact", AV_DICT_MULTIKEY); + + /* Attached pics are sparse, therefore we would not want to delay their decoding + * till EOF. */ + if (ist->st->disposition & AV_DISPOSITION_ATTACHED_PIC) + av_dict_set(&ds->decoder_opts, "thread_type", "-frame", 0); + + switch (par->codec_type) { + case AVMEDIA_TYPE_VIDEO: + MATCH_PER_STREAM_OPT(frame_rates, str, framerate, ic, st); + if (framerate) { + ret = av_parse_video_rate(&ist->framerate, framerate); + if (ret < 0) { + av_log(ist, AV_LOG_ERROR, "Error parsing framerate %s.\n", + framerate); + return ret; } - break; } - case AVMEDIA_TYPE_ATTACHMENT: - case AVMEDIA_TYPE_UNKNOWN: - break; - default: - abort(); + +#if FFMPEG_OPT_TOP + ist->top_field_first = -1; + MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic, st); +#endif + + break; + case AVMEDIA_TYPE_AUDIO: { + int guess_layout_max = INT_MAX; + MATCH_PER_STREAM_OPT(guess_layout_max, i, guess_layout_max, ic, st); + guess_input_channel_layout(ist, par, guess_layout_max); + break; + } + case AVMEDIA_TYPE_DATA: + case AVMEDIA_TYPE_SUBTITLE: { + char *canvas_size = NULL; + MATCH_PER_STREAM_OPT(fix_sub_duration, i, ist->fix_sub_duration, ic, st); + MATCH_PER_STREAM_OPT(canvas_sizes, str, canvas_size, ic, st); + if (canvas_size) { + ret = av_parse_video_size(&par->width, &par->height, + canvas_size); + if (ret < 0) { + av_log(ist, AV_LOG_FATAL, "Invalid canvas size: %s.\n", canvas_size); + return ret; + } + } + break; + } + case AVMEDIA_TYPE_ATTACHMENT: + case AVMEDIA_TYPE_UNKNOWN: + break; + default: av_assert0(0); + } + + ist->par = avcodec_parameters_alloc(); + if (!ist->par) + return AVERROR(ENOMEM); + + ret = avcodec_parameters_copy(ist->par, par); + if (ret < 0) { + av_log(ist, AV_LOG_ERROR, "Error exporting stream parameters.\n"); + return ret; + } + + if (ist->st->sample_aspect_ratio.num) + ist->par->sample_aspect_ratio = ist->st->sample_aspect_ratio; + + MATCH_PER_STREAM_OPT(bitstream_filters, str, bsfs, ic, st); + if (bsfs) { + ret = av_bsf_list_parse_str(bsfs, &ds->bsf); + if (ret < 0) { + av_log(ist, AV_LOG_ERROR, + "Error parsing bitstream filter sequence '%s': %s\n", + bsfs, av_err2str(ret)); + return ret; } - ist->par = avcodec_parameters_alloc(); - if (!ist->par) - report_and_exit(AVERROR(ENOMEM)); + ret = avcodec_parameters_copy(ds->bsf->par_in, ist->par); + if (ret < 0) + return ret; + ds->bsf->time_base_in = ist->st->time_base; - ret = avcodec_parameters_from_context(ist->par, ist->dec_ctx); + ret = av_bsf_init(ds->bsf); if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error initializing the decoder context.\n"); - exit_program(1); + av_log(ist, AV_LOG_ERROR, "Error initializing bitstream filters: %s\n", + av_err2str(ret)); + return ret; } + + ret = avcodec_parameters_copy(ist->par, ds->bsf->par_out); + if (ret < 0) + return ret; } + + ds->codec_desc = avcodec_descriptor_get(ist->par->codec_id); + + return 0; } -static void dump_attachment(AVStream *st, const char *filename) +static int dump_attachment(InputStream *ist, const char *filename) { + AVStream *st = ist->st; int ret; AVIOContext *out = NULL; const AVDictionaryEntry *e; if (!st->codecpar->extradata_size) { - av_log(NULL, AV_LOG_WARNING, "No extradata to dump in stream #%d:%d.\n", - nb_input_files - 1, st->index); - return; + av_log(ist, AV_LOG_WARNING, "No extradata to dump.\n"); + return 0; } if (!*filename && (e = av_dict_get(st->metadata, "filename", NULL, 0))) filename = e->value; if (!*filename) { - av_log(NULL, AV_LOG_FATAL, "No filename specified and no 'filename' tag" - "in stream #%d:%d.\n", nb_input_files - 1, st->index); - exit_program(1); + av_log(ist, AV_LOG_FATAL, "No filename specified and no 'filename' tag"); + return AVERROR(EINVAL); } - assert_file_overwrite(filename); + ret = assert_file_overwrite(filename); + if (ret < 0) + return ret; if ((ret = avio_open2(&out, filename, AVIO_FLAG_WRITE, &int_cb, NULL)) < 0) { - av_log(NULL, AV_LOG_FATAL, "Could not open file %s for writing.\n", + av_log(ist, AV_LOG_FATAL, "Could not open file %s for writing.\n", filename); - exit_program(1); + return ret; } avio_write(out, st->codecpar->extradata, st->codecpar->extradata_size); - avio_flush(out); - avio_close(out); + ret = avio_close(out); + + if (ret >= 0) + av_log(ist, AV_LOG_INFO, "Wrote attachment (%d bytes) to '%s'\n", + st->codecpar->extradata_size, filename); + + return ret; } -int ifile_open(const OptionsContext *o, const char *filename) +static const char *input_file_item_name(void *obj) +{ + const Demuxer *d = obj; + + return d->log_name; +} + +static const AVClass input_file_class = { + .class_name = "InputFile", + .version = LIBAVUTIL_VERSION_INT, + .item_name = input_file_item_name, + .category = AV_CLASS_CATEGORY_DEMUXER, +}; + +static Demuxer *demux_alloc(void) +{ + Demuxer *d = allocate_array_elem(&input_files, sizeof(*d), &nb_input_files); + + if (!d) + return NULL; + + d->f.class = &input_file_class; + d->f.index = nb_input_files - 1; + + snprintf(d->log_name, sizeof(d->log_name), "in#%d", d->f.index); + + return d; +} + +int ifile_open(const OptionsContext *o, const char *filename, Scheduler *sch) { Demuxer *d; InputFile *f; AVFormatContext *ic; const AVInputFormat *file_iformat = NULL; - int err, i, ret; + int err, i, ret = 0; int64_t timestamp; AVDictionary *unused_opts = NULL; const AVDictionaryEntry *e = NULL; - char * video_codec_name = NULL; - char * audio_codec_name = NULL; - char *subtitle_codec_name = NULL; - char * data_codec_name = NULL; + const char* video_codec_name = NULL; + const char* audio_codec_name = NULL; + const char* subtitle_codec_name = NULL; + const char* data_codec_name = NULL; int scan_all_pmts_set = 0; int64_t start_time = o->start_time; @@ -864,16 +1541,27 @@ int ifile_open(const OptionsContext *o, const char *filename) int64_t stop_time = o->stop_time; int64_t recording_time = o->recording_time; + d = demux_alloc(); + if (!d) + return AVERROR(ENOMEM); + + f = &d->f; + + ret = sch_add_demux(sch, input_thread, d); + if (ret < 0) + return ret; + d->sch = sch; + if (stop_time != INT64_MAX && recording_time != INT64_MAX) { stop_time = INT64_MAX; - av_log(NULL, AV_LOG_WARNING, "-t and -to cannot be used together; using -t.\n"); + av_log(d, AV_LOG_WARNING, "-t and -to cannot be used together; using -t.\n"); } if (stop_time != INT64_MAX && recording_time == INT64_MAX) { int64_t start = start_time == AV_NOPTS_VALUE ? 0 : start_time; if (stop_time <= start) { - av_log(NULL, AV_LOG_ERROR, "-to value smaller than -ss; aborting.\n"); - exit_program(1); + av_log(d, AV_LOG_ERROR, "-to value smaller than -ss; aborting.\n"); + return AVERROR(EINVAL); } else { recording_time = stop_time - start; } @@ -881,8 +1569,8 @@ int ifile_open(const OptionsContext *o, const char *filename) if (o->format) { if (!(file_iformat = av_find_input_format(o->format))) { - av_log(NULL, AV_LOG_FATAL, "Unknown input format: '%s'\n", o->format); - exit_program(1); + av_log(d, AV_LOG_FATAL, "Unknown input format: '%s'\n", o->format); + return AVERROR(EINVAL); } } @@ -896,29 +1584,29 @@ int ifile_open(const OptionsContext *o, const char *filename) /* get default parameters from command line */ ic = avformat_alloc_context(); if (!ic) - report_and_exit(AVERROR(ENOMEM)); - if (o->nb_audio_sample_rate) { - av_dict_set_int(&o->g->format_opts, "sample_rate", o->audio_sample_rate[o->nb_audio_sample_rate - 1].u.i, 0); + return AVERROR(ENOMEM); + if (o->audio_sample_rate.nb_opt) { + av_dict_set_int(&o->g->format_opts, "sample_rate", o->audio_sample_rate.opt[o->audio_sample_rate.nb_opt - 1].u.i, 0); } - if (o->nb_audio_channels) { + if (o->audio_channels.nb_opt) { const AVClass *priv_class; if (file_iformat && (priv_class = file_iformat->priv_class) && av_opt_find(&priv_class, "ch_layout", NULL, 0, AV_OPT_SEARCH_FAKE_OBJ)) { char buf[32]; - snprintf(buf, sizeof(buf), "%dC", o->audio_channels[o->nb_audio_channels - 1].u.i); + snprintf(buf, sizeof(buf), "%dC", o->audio_channels.opt[o->audio_channels.nb_opt - 1].u.i); av_dict_set(&o->g->format_opts, "ch_layout", buf, 0); } } - if (o->nb_audio_ch_layouts) { + if (o->audio_ch_layouts.nb_opt) { const AVClass *priv_class; if (file_iformat && (priv_class = file_iformat->priv_class) && av_opt_find(&priv_class, "ch_layout", NULL, 0, AV_OPT_SEARCH_FAKE_OBJ)) { - av_dict_set(&o->g->format_opts, "ch_layout", o->audio_ch_layouts[o->nb_audio_ch_layouts - 1].u.str, 0); + av_dict_set(&o->g->format_opts, "ch_layout", o->audio_ch_layouts.opt[o->audio_ch_layouts.nb_opt - 1].u.str, 0); } } - if (o->nb_frame_rates) { + if (o->frame_rates.nb_opt) { const AVClass *priv_class; /* set the format-level framerate option; * this is important for video grabbers, e.g. x11 */ @@ -926,28 +1614,36 @@ int ifile_open(const OptionsContext *o, const char *filename) av_opt_find(&priv_class, "framerate", NULL, 0, AV_OPT_SEARCH_FAKE_OBJ)) { av_dict_set(&o->g->format_opts, "framerate", - o->frame_rates[o->nb_frame_rates - 1].u.str, 0); + o->frame_rates.opt[o->frame_rates.nb_opt - 1].u.str, 0); } } - if (o->nb_frame_sizes) { - av_dict_set(&o->g->format_opts, "video_size", o->frame_sizes[o->nb_frame_sizes - 1].u.str, 0); + if (o->frame_sizes.nb_opt) { + av_dict_set(&o->g->format_opts, "video_size", o->frame_sizes.opt[o->frame_sizes.nb_opt - 1].u.str, 0); } - if (o->nb_frame_pix_fmts) - av_dict_set(&o->g->format_opts, "pixel_format", o->frame_pix_fmts[o->nb_frame_pix_fmts - 1].u.str, 0); + if (o->frame_pix_fmts.nb_opt) + av_dict_set(&o->g->format_opts, "pixel_format", o->frame_pix_fmts.opt[o->frame_pix_fmts.nb_opt - 1].u.str, 0); - MATCH_PER_TYPE_OPT(codec_names, str, video_codec_name, ic, "v"); - MATCH_PER_TYPE_OPT(codec_names, str, audio_codec_name, ic, "a"); - MATCH_PER_TYPE_OPT(codec_names, str, subtitle_codec_name, ic, "s"); - MATCH_PER_TYPE_OPT(codec_names, str, data_codec_name, ic, "d"); + video_codec_name = opt_match_per_type_str(&o->codec_names, 'v'); + audio_codec_name = opt_match_per_type_str(&o->codec_names, 'a'); + subtitle_codec_name = opt_match_per_type_str(&o->codec_names, 's'); + data_codec_name = opt_match_per_type_str(&o->codec_names, 'd'); if (video_codec_name) - ic->video_codec = find_codec_or_die(NULL, video_codec_name , AVMEDIA_TYPE_VIDEO , 0); + ret = err_merge(ret, find_codec(NULL, video_codec_name , AVMEDIA_TYPE_VIDEO , 0, + &ic->video_codec)); if (audio_codec_name) - ic->audio_codec = find_codec_or_die(NULL, audio_codec_name , AVMEDIA_TYPE_AUDIO , 0); + ret = err_merge(ret, find_codec(NULL, audio_codec_name , AVMEDIA_TYPE_AUDIO , 0, + &ic->audio_codec)); if (subtitle_codec_name) - ic->subtitle_codec = find_codec_or_die(NULL, subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 0); + ret = err_merge(ret, find_codec(NULL, subtitle_codec_name, AVMEDIA_TYPE_SUBTITLE, 0, + &ic->subtitle_codec)); if (data_codec_name) - ic->data_codec = find_codec_or_die(NULL, data_codec_name , AVMEDIA_TYPE_DATA , 0); + ret = err_merge(ret, find_codec(NULL, data_codec_name , AVMEDIA_TYPE_DATA, 0, + &ic->data_codec)); + if (ret < 0) { + avformat_free_context(ic); + return ret; + } ic->video_codec_id = video_codec_name ? ic->video_codec->id : AV_CODEC_ID_NONE; ic->audio_codec_id = audio_codec_name ? ic->audio_codec->id : AV_CODEC_ID_NONE; @@ -966,24 +1662,42 @@ int ifile_open(const OptionsContext *o, const char *filename) /* open the input file with generic avformat function */ err = avformat_open_input(&ic, filename, file_iformat, &o->g->format_opts); if (err < 0) { - print_error(filename, err); + av_log(d, AV_LOG_ERROR, + "Error opening input: %s\n", av_err2str(err)); if (err == AVERROR_PROTOCOL_NOT_FOUND) - av_log(NULL, AV_LOG_ERROR, "Did you mean file:%s?\n", filename); - exit_program(1); + av_log(d, AV_LOG_ERROR, "Did you mean file:%s?\n", filename); + return err; } + f->ctx = ic; + + av_strlcat(d->log_name, "/", sizeof(d->log_name)); + av_strlcat(d->log_name, ic->iformat->name, sizeof(d->log_name)); + if (scan_all_pmts_set) av_dict_set(&o->g->format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE); remove_avoptions(&o->g->format_opts, o->g->codec_opts); - assert_avoptions(o->g->format_opts); + + ret = check_avoptions(o->g->format_opts); + if (ret < 0) + return ret; /* apply forced codec ids */ - for (i = 0; i < ic->nb_streams; i++) - choose_decoder(o, ic, ic->streams[i], HWACCEL_NONE, AV_HWDEVICE_TYPE_NONE); + for (i = 0; i < ic->nb_streams; i++) { + const AVCodec *dummy; + ret = choose_decoder(o, ic, ic->streams[i], HWACCEL_NONE, AV_HWDEVICE_TYPE_NONE, + &dummy); + if (ret < 0) + return ret; + } if (o->find_stream_info) { - AVDictionary **opts = setup_find_stream_info_opts(ic, o->g->codec_opts); + AVDictionary **opts; int orig_nb_streams = ic->nb_streams; + ret = setup_find_stream_info_opts(ic, o->g->codec_opts, &opts); + if (ret < 0) + return ret; + /* If not enough info to get the stream parameters, we decode the first frames to get it. (used in mpeg case for example) */ ret = avformat_find_stream_info(ic, opts); @@ -993,32 +1707,30 @@ int ifile_open(const OptionsContext *o, const char *filename) av_freep(&opts); if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "%s: could not find codec parameters\n", filename); - if (ic->nb_streams == 0) { - avformat_close_input(&ic); - exit_program(1); - } + av_log(d, AV_LOG_FATAL, "could not find codec parameters\n"); + if (ic->nb_streams == 0) + return ret; } } if (start_time != AV_NOPTS_VALUE && start_time_eof != AV_NOPTS_VALUE) { - av_log(NULL, AV_LOG_WARNING, "Cannot use -ss and -sseof both, using -ss for %s\n", filename); + av_log(d, AV_LOG_WARNING, "Cannot use -ss and -sseof both, using -ss\n"); start_time_eof = AV_NOPTS_VALUE; } if (start_time_eof != AV_NOPTS_VALUE) { if (start_time_eof >= 0) { - av_log(NULL, AV_LOG_ERROR, "-sseof value must be negative; aborting\n"); - exit_program(1); + av_log(d, AV_LOG_ERROR, "-sseof value must be negative; aborting\n"); + return AVERROR(EINVAL); } if (ic->duration > 0) { start_time = start_time_eof + ic->duration; if (start_time < 0) { - av_log(NULL, AV_LOG_WARNING, "-sseof value seeks to before start of file %s; ignored\n", filename); + av_log(d, AV_LOG_WARNING, "-sseof value seeks to before start of file; ignored\n"); start_time = AV_NOPTS_VALUE; } } else - av_log(NULL, AV_LOG_WARNING, "Cannot use -sseof, duration of %s not known\n", filename); + av_log(d, AV_LOG_WARNING, "Cannot use -sseof, file duration not known\n"); } timestamp = (start_time == AV_NOPTS_VALUE) ? 0 : start_time; /* add the stream start time */ @@ -1044,41 +1756,55 @@ int ifile_open(const OptionsContext *o, const char *filename) } ret = avformat_seek_file(ic, -1, INT64_MIN, seek_timestamp, seek_timestamp, 0); if (ret < 0) { - av_log(NULL, AV_LOG_WARNING, "%s: could not seek to position %0.3f\n", - filename, (double)timestamp / AV_TIME_BASE); + av_log(d, AV_LOG_WARNING, "could not seek to position %0.3f\n", + (double)timestamp / AV_TIME_BASE); } } - d = allocate_array_elem(&input_files, sizeof(*d), &nb_input_files); - f = &d->f; - - f->ctx = ic; - f->index = nb_input_files - 1; f->start_time = start_time; - f->recording_time = recording_time; + d->recording_time = recording_time; f->input_sync_ref = o->input_sync_ref; f->input_ts_offset = o->input_ts_offset; f->ts_offset = o->input_ts_offset - (copy_ts ? (start_at_zero && ic->start_time != AV_NOPTS_VALUE ? ic->start_time : 0) : timestamp); - f->rate_emu = o->rate_emu; - f->accurate_seek = o->accurate_seek; + d->accurate_seek = o->accurate_seek; d->loop = o->loop; - d->duration = 0; - d->time_base = (AVRational){ 1, 1 }; + d->nb_streams_warn = ic->nb_streams; + + d->duration = (Timestamp){ .ts = 0, .tb = (AVRational){ 1, 1 } }; + d->min_pts = (Timestamp){ .ts = AV_NOPTS_VALUE, .tb = (AVRational){ 1, 1 } }; + d->max_pts = (Timestamp){ .ts = AV_NOPTS_VALUE, .tb = (AVRational){ 1, 1 } }; - f->readrate = o->readrate ? o->readrate : 0.0; - if (f->readrate < 0.0f) { - av_log(NULL, AV_LOG_ERROR, "Option -readrate for Input #%d is %0.3f; it must be non-negative.\n", f->index, f->readrate); - exit_program(1); + d->readrate = o->readrate ? o->readrate : 0.0; + if (d->readrate < 0.0f) { + av_log(d, AV_LOG_ERROR, "Option -readrate is %0.3f; it must be non-negative.\n", d->readrate); + return AVERROR(EINVAL); } - if (f->readrate && f->rate_emu) { - av_log(NULL, AV_LOG_WARNING, "Both -readrate and -re set for Input #%d. Using -readrate %0.3f.\n", f->index, f->readrate); - f->rate_emu = 0; + if (o->rate_emu) { + if (d->readrate) { + av_log(d, AV_LOG_WARNING, "Both -readrate and -re set. Using -readrate %0.3f.\n", d->readrate); + } else + d->readrate = 1.0f; } - d->thread_queue_size = o->thread_queue_size; + if (d->readrate) { + d->readrate_initial_burst = o->readrate_initial_burst ? o->readrate_initial_burst : 0.5; + if (d->readrate_initial_burst < 0.0) { + av_log(d, AV_LOG_ERROR, + "Option -readrate_initial_burst is %0.3f; it must be non-negative.\n", + d->readrate_initial_burst); + return AVERROR(EINVAL); + } + } else if (o->readrate_initial_burst) { + av_log(d, AV_LOG_WARNING, "Option -readrate_initial_burst ignored " + "since neither -readrate nor -re were given\n"); + } - /* update the current parameters so that they match the one of the input stream */ - add_input_streams(o, d); + /* Add all the streams from the given input file to the demuxer */ + for (int i = 0; i < ic->nb_streams; i++) { + ret = ist_add(o, d, ic->streams[i]); + if (ret < 0) + return ret; + } /* dump the file content */ av_dump_format(ic, f->index, filename, 0); @@ -1086,8 +1812,9 @@ int ifile_open(const OptionsContext *o, const char *filename) /* check if all codec options have been used */ unused_opts = strip_specifiers(o->g->codec_opts); for (i = 0; i < f->nb_streams; i++) { + DemuxStream *ds = ds_from_ist(f->streams[i]); e = NULL; - while ((e = av_dict_iterate(f->streams[i]->decoder_opts, e))) + while ((e = av_dict_iterate(ds->decoder_opts, e))) av_dict_set(&unused_opts, e->key, NULL, 0); } @@ -1104,30 +1831,30 @@ int ifile_open(const OptionsContext *o, const char *filename) if (!(option->flags & AV_OPT_FLAG_DECODING_PARAM)) { - av_log(NULL, AV_LOG_ERROR, "Codec AVOption %s (%s) specified for " - "input file #%d (%s) is not a decoding option.\n", e->key, - option->help ? option->help : "", f->index, - filename); - exit_program(1); + av_log(d, AV_LOG_ERROR, "Codec AVOption %s (%s) is not a decoding " + "option.\n", e->key, option->help ? option->help : ""); + return AVERROR(EINVAL); } - av_log(NULL, AV_LOG_WARNING, "Codec AVOption %s (%s) specified for " - "input file #%d (%s) has not been used for any stream. The most " - "likely reason is either wrong type (e.g. a video option with " - "no video streams) or that it is a private option of some decoder " - "which was not actually used for any stream.\n", e->key, - option->help ? option->help : "", f->index, filename); + av_log(d, AV_LOG_WARNING, "Codec AVOption %s (%s) has not been used " + "for any stream. The most likely reason is either wrong type " + "(e.g. a video option with no video streams) or that it is a " + "private option of some decoder which was not actually used " + "for any stream.\n", e->key, option->help ? option->help : ""); } av_dict_free(&unused_opts); - for (i = 0; i < o->nb_dump_attachment; i++) { + for (i = 0; i < o->dump_attachment.nb_opt; i++) { int j; - for (j = 0; j < ic->nb_streams; j++) { - AVStream *st = ic->streams[j]; + for (j = 0; j < f->nb_streams; j++) { + InputStream *ist = f->streams[j]; - if (check_stream_specifier(ic, st, o->dump_attachment[i].specifier) == 1) - dump_attachment(st, o->dump_attachment[i].u.str); + if (check_stream_specifier(ic, ist->st, o->dump_attachment.opt[i].specifier) == 1) { + ret = dump_attachment(ist, o->dump_attachment.opt[i].u.str); + if (ret < 0) + return ret; + } } } diff --git a/fftools/ffmpeg_enc.c b/fftools/ffmpeg_enc.c new file mode 100644 index 00000000000..447f1331370 --- /dev/null +++ b/fftools/ffmpeg_enc.c @@ -0,0 +1,966 @@ +/* + * 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 +#include + +#include "ffmpeg.h" +#include "ffmpeg_utils.h" +#include "thread_queue.h" + +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/avutil.h" +#include "libavutil/dict.h" +#include "libavutil/display.h" +#include "libavutil/eval.h" +#include "libavutil/frame.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/log.h" +#include "libavutil/pixdesc.h" +#include "libavutil/rational.h" +#include "libavutil/time.h" +#include "libavutil/timestamp.h" + +#include "libavcodec/avcodec.h" + +#include "libavformat/avformat.h" + +struct Encoder { + // combined size of all the packets received from the encoder + uint64_t data_size; + + // number of packets received from the encoder + uint64_t packets_encoded; + + int opened; + int attach_par; + + Scheduler *sch; + unsigned sch_idx; +}; + +// data that is local to the decoder thread and not visible outside of it +typedef struct EncoderThread { + AVFrame *frame; + AVPacket *pkt; +} EncoderThread; + +void enc_free(Encoder **penc) +{ + Encoder *enc = *penc; + + if (!enc) + return; + + av_freep(penc); +} + +int enc_alloc(Encoder **penc, const AVCodec *codec, + Scheduler *sch, unsigned sch_idx) +{ + Encoder *enc; + + *penc = NULL; + + enc = av_mallocz(sizeof(*enc)); + if (!enc) + return AVERROR(ENOMEM); + + enc->sch = sch; + enc->sch_idx = sch_idx; + + *penc = enc; + + return 0; +} + +static int hw_device_setup_for_encode(OutputStream *ost, AVBufferRef *frames_ref) +{ + const AVCodecHWConfig *config; + HWDevice *dev = NULL; + + if (frames_ref && + ((AVHWFramesContext*)frames_ref->data)->format == + ost->enc_ctx->pix_fmt) { + // Matching format, will try to use hw_frames_ctx. + } else { + frames_ref = NULL; + } + + for (int i = 0;; i++) { + config = avcodec_get_hw_config(ost->enc_ctx->codec, i); + if (!config) + break; + + if (frames_ref && + config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX && + (config->pix_fmt == AV_PIX_FMT_NONE || + config->pix_fmt == ost->enc_ctx->pix_fmt)) { + av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using input " + "frames context (format %s) with %s encoder.\n", + av_get_pix_fmt_name(ost->enc_ctx->pix_fmt), + ost->enc_ctx->codec->name); + ost->enc_ctx->hw_frames_ctx = av_buffer_ref(frames_ref); + if (!ost->enc_ctx->hw_frames_ctx) + return AVERROR(ENOMEM); + return 0; + } + + if (!dev && + config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) + dev = hw_device_get_by_type(config->device_type); + } + + if (dev) { + av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using device %s " + "(type %s) with %s encoder.\n", dev->name, + av_hwdevice_get_type_name(dev->type), ost->enc_ctx->codec->name); + ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref); + if (!ost->enc_ctx->hw_device_ctx) + return AVERROR(ENOMEM); + } else { + // No device required, or no device available. + } + return 0; +} + +static int set_encoder_id(OutputFile *of, OutputStream *ost) +{ + const char *cname = ost->enc_ctx->codec->name; + uint8_t *encoder_string; + int encoder_string_len; + + if (av_dict_get(ost->st->metadata, "encoder", NULL, 0)) + return 0; + + encoder_string_len = sizeof(LIBAVCODEC_IDENT) + strlen(cname) + 2; + encoder_string = av_mallocz(encoder_string_len); + if (!encoder_string) + return AVERROR(ENOMEM); + + if (!of->bitexact && !ost->bitexact) + av_strlcpy(encoder_string, LIBAVCODEC_IDENT " ", encoder_string_len); + else + av_strlcpy(encoder_string, "Lavc ", encoder_string_len); + av_strlcat(encoder_string, cname, encoder_string_len); + av_dict_set(&ost->st->metadata, "encoder", encoder_string, + AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE); + + return 0; +} + +int enc_open(void *opaque, const AVFrame *frame) +{ + OutputStream *ost = opaque; + InputStream *ist = ost->ist; + Encoder *e = ost->enc; + AVCodecContext *enc_ctx = ost->enc_ctx; + Decoder *dec = NULL; + const AVCodec *enc = enc_ctx->codec; + OutputFile *of = ost->file; + FrameData *fd; + int frame_samples = 0; + int ret; + + if (e->opened) + return 0; + + // frame is always non-NULL for audio and video + av_assert0(frame || (enc->type != AVMEDIA_TYPE_VIDEO && enc->type != AVMEDIA_TYPE_AUDIO)); + + if (frame) { + av_assert0(frame->opaque_ref); + fd = (FrameData*)frame->opaque_ref->data; + } + + ret = set_encoder_id(of, ost); + if (ret < 0) + return ret; + + if (ist) + dec = ist->decoder; + + // the timebase is chosen by filtering code + if (ost->type == AVMEDIA_TYPE_AUDIO || ost->type == AVMEDIA_TYPE_VIDEO) { + enc_ctx->time_base = frame->time_base; + enc_ctx->framerate = fd->frame_rate_filter; + ost->st->avg_frame_rate = fd->frame_rate_filter; + } + + switch (enc_ctx->codec_type) { + case AVMEDIA_TYPE_AUDIO: + av_assert0(frame->format != AV_SAMPLE_FMT_NONE && + frame->sample_rate > 0 && + frame->ch_layout.nb_channels > 0); + enc_ctx->sample_fmt = frame->format; + enc_ctx->sample_rate = frame->sample_rate; + ret = av_channel_layout_copy(&enc_ctx->ch_layout, &frame->ch_layout); + if (ret < 0) + return ret; + + if (ost->bits_per_raw_sample) + enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample; + else + enc_ctx->bits_per_raw_sample = FFMIN(fd->bits_per_raw_sample, + av_get_bytes_per_sample(enc_ctx->sample_fmt) << 3); + break; + + case AVMEDIA_TYPE_VIDEO: { + av_assert0(frame->format != AV_PIX_FMT_NONE && + frame->width > 0 && + frame->height > 0); + enc_ctx->width = frame->width; + enc_ctx->height = frame->height; + enc_ctx->sample_aspect_ratio = ost->st->sample_aspect_ratio = + ost->frame_aspect_ratio.num ? // overridden by the -aspect cli option + av_mul_q(ost->frame_aspect_ratio, (AVRational){ enc_ctx->height, enc_ctx->width }) : + frame->sample_aspect_ratio; + + enc_ctx->pix_fmt = frame->format; + + if (ost->bits_per_raw_sample) + enc_ctx->bits_per_raw_sample = ost->bits_per_raw_sample; + else + enc_ctx->bits_per_raw_sample = FFMIN(fd->bits_per_raw_sample, + av_pix_fmt_desc_get(enc_ctx->pix_fmt)->comp[0].depth); + + enc_ctx->color_range = frame->color_range; + enc_ctx->color_primaries = frame->color_primaries; + enc_ctx->color_trc = frame->color_trc; + enc_ctx->colorspace = frame->colorspace; + enc_ctx->chroma_sample_location = frame->chroma_location; + + for (int i = 0; i < frame->nb_side_data; i++) { + ret = av_frame_side_data_clone( + &enc_ctx->decoded_side_data, &enc_ctx->nb_decoded_side_data, + frame->side_data[i], AV_FRAME_SIDE_DATA_FLAG_UNIQUE); + if (ret < 0) { + av_frame_side_data_free( + &enc_ctx->decoded_side_data, + &enc_ctx->nb_decoded_side_data); + av_log(NULL, AV_LOG_ERROR, + "failed to configure video encoder: %s!\n", + av_err2str(ret)); + return ret; + } + } + + if (enc_ctx->flags & (AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME) || + (frame->flags & AV_FRAME_FLAG_INTERLACED) +#if FFMPEG_OPT_TOP + || ost->top_field_first >= 0 +#endif + ) { + int top_field_first = +#if FFMPEG_OPT_TOP + ost->top_field_first >= 0 ? + ost->top_field_first : +#endif + !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); + + if (enc->id == AV_CODEC_ID_MJPEG) + enc_ctx->field_order = top_field_first ? AV_FIELD_TT : AV_FIELD_BB; + else + enc_ctx->field_order = top_field_first ? AV_FIELD_TB : AV_FIELD_BT; + } else + enc_ctx->field_order = AV_FIELD_PROGRESSIVE; + + break; + } + case AVMEDIA_TYPE_SUBTITLE: + if (ost->enc_timebase.num) + av_log(ost, AV_LOG_WARNING, + "-enc_time_base not supported for subtitles, ignoring\n"); + enc_ctx->time_base = AV_TIME_BASE_Q; + + if (!enc_ctx->width) { + enc_ctx->width = ost->ist->par->width; + enc_ctx->height = ost->ist->par->height; + } + + av_assert0(dec); + if (dec->subtitle_header) { + /* ASS code assumes this buffer is null terminated so add extra byte. */ + enc_ctx->subtitle_header = av_mallocz(dec->subtitle_header_size + 1); + if (!enc_ctx->subtitle_header) + return AVERROR(ENOMEM); + memcpy(enc_ctx->subtitle_header, dec->subtitle_header, + dec->subtitle_header_size); + enc_ctx->subtitle_header_size = dec->subtitle_header_size; + } + + break; + default: + av_assert0(0); + break; + } + + if (ost->bitexact) + enc_ctx->flags |= AV_CODEC_FLAG_BITEXACT; + + if (!av_dict_get(ost->encoder_opts, "threads", NULL, 0)) + av_dict_set(&ost->encoder_opts, "threads", "auto", 0); + + if (enc->capabilities & AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE) { + ret = av_dict_set(&ost->encoder_opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY); + if (ret < 0) + return ret; + } + + av_dict_set(&ost->encoder_opts, "flags", "+frame_duration", AV_DICT_MULTIKEY); + + ret = hw_device_setup_for_encode(ost, frame ? frame->hw_frames_ctx : NULL); + if (ret < 0) { + av_log(ost, AV_LOG_ERROR, + "Encoding hardware device setup failed: %s\n", av_err2str(ret)); + return ret; + } + + if ((ret = avcodec_open2(ost->enc_ctx, enc, &ost->encoder_opts)) < 0) { + if (ret != AVERROR_EXPERIMENTAL) + av_log(ost, AV_LOG_ERROR, "Error while opening encoder - maybe " + "incorrect parameters such as bit_rate, rate, width or height.\n"); + return ret; + } + + e->opened = 1; + + if (ost->enc_ctx->frame_size) + frame_samples = ost->enc_ctx->frame_size; + + ret = check_avoptions(ost->encoder_opts); + if (ret < 0) + return ret; + + if (ost->enc_ctx->bit_rate && ost->enc_ctx->bit_rate < 1000 && + ost->enc_ctx->codec_id != AV_CODEC_ID_CODEC2 /* don't complain about 700 bit/s modes */) + av_log(ost, AV_LOG_WARNING, "The bitrate parameter is set too low." + " It takes bits/s as argument, not kbits/s\n"); + + ret = avcodec_parameters_from_context(ost->par_in, ost->enc_ctx); + if (ret < 0) { + av_log(ost, AV_LOG_FATAL, + "Error initializing the output stream codec context.\n"); + return ret; + } + + /* + * Add global input side data. For now this is naive, and copies it + * from the input stream's global side data. All side data should + * really be funneled over AVFrame and libavfilter, then added back to + * packet side data, and then potentially using the first packet for + * global side data. + */ + if (ist) { + for (int i = 0; i < ist->st->codecpar->nb_coded_side_data; i++) { + AVPacketSideData *sd_src = &ist->st->codecpar->coded_side_data[i]; + if (sd_src->type != AV_PKT_DATA_CPB_PROPERTIES) { + AVPacketSideData *sd_dst = av_packet_side_data_new(&ost->par_in->coded_side_data, + &ost->par_in->nb_coded_side_data, + sd_src->type, sd_src->size, 0); + if (!sd_dst) + return AVERROR(ENOMEM); + memcpy(sd_dst->data, sd_src->data, sd_src->size); + if (ist->autorotate && sd_src->type == AV_PKT_DATA_DISPLAYMATRIX) + av_display_rotation_set((int32_t *)sd_dst->data, 0); + } + } + } + + // copy timebase while removing common factors + if (ost->st->time_base.num <= 0 || ost->st->time_base.den <= 0) + ost->st->time_base = av_add_q(ost->enc_ctx->time_base, (AVRational){0, 1}); + + ret = of_stream_init(of, ost); + if (ret < 0) + return ret; + + return frame_samples; +} + +static int check_recording_time(OutputStream *ost, int64_t ts, AVRational tb) +{ + OutputFile *of = ost->file; + + if (of->recording_time != INT64_MAX && + av_compare_ts(ts, tb, of->recording_time, AV_TIME_BASE_Q) >= 0) { + return 0; + } + return 1; +} + +static int do_subtitle_out(OutputFile *of, OutputStream *ost, const AVSubtitle *sub, + AVPacket *pkt) +{ + Encoder *e = ost->enc; + int subtitle_out_max_size = 1024 * 1024; + int subtitle_out_size, nb, i, ret; + AVCodecContext *enc; + int64_t pts; + + if (sub->pts == AV_NOPTS_VALUE) { + av_log(ost, AV_LOG_ERROR, "Subtitle packets must have a pts\n"); + return exit_on_error ? AVERROR(EINVAL) : 0; + } + if ((of->start_time != AV_NOPTS_VALUE && sub->pts < of->start_time)) + return 0; + + enc = ost->enc_ctx; + + /* Note: DVB subtitle need one packet to draw them and one other + packet to clear them */ + /* XXX: signal it in the codec context ? */ + if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) + nb = 2; + else if (enc->codec_id == AV_CODEC_ID_ASS) + nb = FFMAX(sub->num_rects, 1); + else + nb = 1; + + /* shift timestamp to honor -ss and make check_recording_time() work with -t */ + pts = sub->pts; + if (of->start_time != AV_NOPTS_VALUE) + pts -= of->start_time; + for (i = 0; i < nb; i++) { + AVSubtitle local_sub = *sub; + + if (!check_recording_time(ost, pts, AV_TIME_BASE_Q)) + return AVERROR_EOF; + + ret = av_new_packet(pkt, subtitle_out_max_size); + if (ret < 0) + return AVERROR(ENOMEM); + + local_sub.pts = pts; + // start_display_time is required to be 0 + local_sub.pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, AV_TIME_BASE_Q); + local_sub.end_display_time -= sub->start_display_time; + local_sub.start_display_time = 0; + + if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE && i == 1) + local_sub.num_rects = 0; + else if (enc->codec_id == AV_CODEC_ID_ASS && sub->num_rects > 0) { + local_sub.num_rects = 1; + local_sub.rects += i; + } + + ost->frames_encoded++; + + subtitle_out_size = avcodec_encode_subtitle(enc, pkt->data, pkt->size, &local_sub); + if (subtitle_out_size < 0) { + av_log(ost, AV_LOG_FATAL, "Subtitle encoding failed\n"); + return subtitle_out_size; + } + + av_shrink_packet(pkt, subtitle_out_size); + pkt->time_base = AV_TIME_BASE_Q; + pkt->pts = sub->pts; + pkt->duration = av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, pkt->time_base); + if (enc->codec_id == AV_CODEC_ID_DVB_SUBTITLE) { + /* XXX: the pts correction is handled here. Maybe handling + it in the codec would be better */ + if (i == 0) + pkt->pts += av_rescale_q(sub->start_display_time, (AVRational){ 1, 1000 }, pkt->time_base); + else + pkt->pts += av_rescale_q(sub->end_display_time, (AVRational){ 1, 1000 }, pkt->time_base); + } + pkt->dts = pkt->pts; + + ret = sch_enc_send(e->sch, e->sch_idx, pkt); + if (ret < 0) { + av_packet_unref(pkt); + return ret; + } + } + + return 0; +} + +void enc_stats_write(OutputStream *ost, EncStats *es, + const AVFrame *frame, const AVPacket *pkt, + uint64_t frame_num) +{ + Encoder *e = ost->enc; + AVIOContext *io = es->io; + AVRational tb = frame ? frame->time_base : pkt->time_base; + int64_t pts = frame ? frame->pts : pkt->pts; + + AVRational tbi = (AVRational){ 0, 1}; + int64_t ptsi = INT64_MAX; + + const FrameData *fd = NULL; + + if (frame ? frame->opaque_ref : pkt->opaque_ref) { + fd = (const FrameData*)(frame ? frame->opaque_ref->data : pkt->opaque_ref->data); + tbi = fd->dec.tb; + ptsi = fd->dec.pts; + } + + pthread_mutex_lock(&es->lock); + + for (size_t i = 0; i < es->nb_components; i++) { + const EncStatsComponent *c = &es->components[i]; + + switch (c->type) { + case ENC_STATS_LITERAL: avio_write (io, c->str, c->str_len); continue; + case ENC_STATS_FILE_IDX: avio_printf(io, "%d", ost->file->index); continue; + case ENC_STATS_STREAM_IDX: avio_printf(io, "%d", ost->index); continue; + case ENC_STATS_TIMEBASE: avio_printf(io, "%d/%d", tb.num, tb.den); continue; + case ENC_STATS_TIMEBASE_IN: avio_printf(io, "%d/%d", tbi.num, tbi.den); continue; + case ENC_STATS_PTS: avio_printf(io, "%"PRId64, pts); continue; + case ENC_STATS_PTS_IN: avio_printf(io, "%"PRId64, ptsi); continue; + case ENC_STATS_PTS_TIME: avio_printf(io, "%g", pts * av_q2d(tb)); continue; + case ENC_STATS_PTS_TIME_IN: avio_printf(io, "%g", ptsi == INT64_MAX ? + INFINITY : ptsi * av_q2d(tbi)); continue; + case ENC_STATS_FRAME_NUM: avio_printf(io, "%"PRIu64, frame_num); continue; + case ENC_STATS_FRAME_NUM_IN: avio_printf(io, "%"PRIu64, fd ? fd->dec.frame_num : -1); continue; + } + + if (frame) { + switch (c->type) { + case ENC_STATS_SAMPLE_NUM: avio_printf(io, "%"PRIu64, ost->samples_encoded); continue; + case ENC_STATS_NB_SAMPLES: avio_printf(io, "%d", frame->nb_samples); continue; + default: av_assert0(0); + } + } else { + switch (c->type) { + case ENC_STATS_DTS: avio_printf(io, "%"PRId64, pkt->dts); continue; + case ENC_STATS_DTS_TIME: avio_printf(io, "%g", pkt->dts * av_q2d(tb)); continue; + case ENC_STATS_PKT_SIZE: avio_printf(io, "%d", pkt->size); continue; + case ENC_STATS_KEYFRAME: avio_write(io, (pkt->flags & AV_PKT_FLAG_KEY) ? + "K" : "N", 1); continue; + case ENC_STATS_BITRATE: { + double duration = FFMAX(pkt->duration, 1) * av_q2d(tb); + avio_printf(io, "%g", 8.0 * pkt->size / duration); + continue; + } + case ENC_STATS_AVG_BITRATE: { + double duration = pkt->dts * av_q2d(tb); + avio_printf(io, "%g", duration > 0 ? 8.0 * e->data_size / duration : -1.); + continue; + } + default: av_assert0(0); + } + } + } + avio_w8(io, '\n'); + avio_flush(io); + + pthread_mutex_unlock(&es->lock); +} + +static inline double psnr(double d) +{ + return -10.0 * log10(d); +} + +static int update_video_stats(OutputStream *ost, const AVPacket *pkt, int write_vstats) +{ + Encoder *e = ost->enc; + const uint8_t *sd = av_packet_get_side_data(pkt, AV_PKT_DATA_QUALITY_STATS, + NULL); + AVCodecContext *enc = ost->enc_ctx; + enum AVPictureType pict_type; + int64_t frame_number; + double ti1, bitrate, avg_bitrate; + double psnr_val = -1; + int quality; + + quality = sd ? AV_RL32(sd) : -1; + pict_type = sd ? sd[4] : AV_PICTURE_TYPE_NONE; + + atomic_store(&ost->quality, quality); + + if ((enc->flags & AV_CODEC_FLAG_PSNR) && sd && sd[5]) { + // FIXME the scaling assumes 8bit + double error = AV_RL64(sd + 8) / (enc->width * enc->height * 255.0 * 255.0); + if (error >= 0 && error <= 1) + psnr_val = psnr(error); + } + + if (!write_vstats) + return 0; + + /* this is executed just the first time update_video_stats is called */ + if (!vstats_file) { + vstats_file = fopen(vstats_filename, "w"); + if (!vstats_file) { + perror("fopen"); + return AVERROR(errno); + } + } + + frame_number = e->packets_encoded; + if (vstats_version <= 1) { + fprintf(vstats_file, "frame= %5"PRId64" q= %2.1f ", frame_number, + quality / (float)FF_QP2LAMBDA); + } else { + fprintf(vstats_file, "out= %2d st= %2d frame= %5"PRId64" q= %2.1f ", + ost->file->index, ost->index, frame_number, + quality / (float)FF_QP2LAMBDA); + } + + if (psnr_val >= 0) + fprintf(vstats_file, "PSNR= %6.2f ", psnr_val); + + fprintf(vstats_file,"f_size= %6d ", pkt->size); + /* compute pts value */ + ti1 = pkt->dts * av_q2d(pkt->time_base); + if (ti1 < 0.01) + ti1 = 0.01; + + bitrate = (pkt->size * 8) / av_q2d(enc->time_base) / 1000.0; + avg_bitrate = (double)(e->data_size * 8) / ti1 / 1000.0; + fprintf(vstats_file, "s_size= %8.0fKiB time= %0.3f br= %7.1fkbits/s avg_br= %7.1fkbits/s ", + (double)e->data_size / 1024, ti1, bitrate, avg_bitrate); + fprintf(vstats_file, "type= %c\n", av_get_picture_type_char(pict_type)); + + return 0; +} + +static int encode_frame(OutputFile *of, OutputStream *ost, AVFrame *frame, + AVPacket *pkt) +{ + Encoder *e = ost->enc; + AVCodecContext *enc = ost->enc_ctx; + const char *type_desc = av_get_media_type_string(enc->codec_type); + const char *action = frame ? "encode" : "flush"; + int ret; + + if (frame) { + FrameData *fd = frame_data(frame); + + if (!fd) + return AVERROR(ENOMEM); + + fd->wallclock[LATENCY_PROBE_ENC_PRE] = av_gettime_relative(); + + if (ost->enc_stats_pre.io) + enc_stats_write(ost, &ost->enc_stats_pre, frame, NULL, + ost->frames_encoded); + + ost->frames_encoded++; + ost->samples_encoded += frame->nb_samples; + + if (debug_ts) { + av_log(ost, AV_LOG_INFO, "encoder <- type:%s " + "frame_pts:%s frame_pts_time:%s time_base:%d/%d\n", + type_desc, + av_ts2str(frame->pts), av_ts2timestr(frame->pts, &enc->time_base), + enc->time_base.num, enc->time_base.den); + } + + if (frame->sample_aspect_ratio.num && !ost->frame_aspect_ratio.num) + enc->sample_aspect_ratio = frame->sample_aspect_ratio; + } + + update_benchmark(NULL); + + ret = avcodec_send_frame(enc, frame); + if (ret < 0 && !(ret == AVERROR_EOF && !frame)) { + av_log(ost, AV_LOG_ERROR, "Error submitting %s frame to the encoder\n", + type_desc); + return ret; + } + + while (1) { + FrameData *fd; + + av_packet_unref(pkt); + + ret = avcodec_receive_packet(enc, pkt); + update_benchmark("%s_%s %d.%d", action, type_desc, + of->index, ost->index); + + pkt->time_base = enc->time_base; + + /* if two pass, output log on success and EOF */ + if ((ret >= 0 || ret == AVERROR_EOF) && ost->logfile && enc->stats_out) + fprintf(ost->logfile, "%s", enc->stats_out); + + if (ret == AVERROR(EAGAIN)) { + av_assert0(frame); // should never happen during flushing + return 0; + } else if (ret < 0) { + if (ret != AVERROR_EOF) + av_log(ost, AV_LOG_ERROR, "%s encoding failed\n", type_desc); + return ret; + } + + fd = packet_data(pkt); + if (!fd) + return AVERROR(ENOMEM); + fd->wallclock[LATENCY_PROBE_ENC_POST] = av_gettime_relative(); + + // attach stream parameters to first packet if requested + avcodec_parameters_free(&fd->par_enc); + if (e->attach_par && !e->packets_encoded) { + fd->par_enc = avcodec_parameters_alloc(); + if (!fd->par_enc) + return AVERROR(ENOMEM); + + ret = avcodec_parameters_from_context(fd->par_enc, enc); + if (ret < 0) + return ret; + } + + pkt->flags |= AV_PKT_FLAG_TRUSTED; + + if (enc->codec_type == AVMEDIA_TYPE_VIDEO) { + ret = update_video_stats(ost, pkt, !!vstats_filename); + if (ret < 0) + return ret; + } + + if (ost->enc_stats_post.io) + enc_stats_write(ost, &ost->enc_stats_post, NULL, pkt, + e->packets_encoded); + + if (debug_ts) { + av_log(ost, AV_LOG_INFO, "encoder -> type:%s " + "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s " + "duration:%s duration_time:%s\n", + type_desc, + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &enc->time_base), + av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &enc->time_base), + av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &enc->time_base)); + } + + e->data_size += pkt->size; + + e->packets_encoded++; + + ret = sch_enc_send(e->sch, e->sch_idx, pkt); + if (ret < 0) { + av_packet_unref(pkt); + return ret; + } + } + + av_assert0(0); +} + +static enum AVPictureType forced_kf_apply(void *logctx, KeyframeForceCtx *kf, + const AVFrame *frame) +{ + double pts_time; + + if (kf->ref_pts == AV_NOPTS_VALUE) + kf->ref_pts = frame->pts; + + pts_time = (frame->pts - kf->ref_pts) * av_q2d(frame->time_base); + if (kf->index < kf->nb_pts && + av_compare_ts(frame->pts, frame->time_base, kf->pts[kf->index], AV_TIME_BASE_Q) >= 0) { + kf->index++; + goto force_keyframe; + } else if (kf->pexpr) { + double res; + kf->expr_const_values[FKF_T] = pts_time; + res = av_expr_eval(kf->pexpr, + kf->expr_const_values, NULL); + av_log(logctx, AV_LOG_TRACE, + "force_key_frame: n:%f n_forced:%f prev_forced_n:%f t:%f prev_forced_t:%f -> res:%f\n", + kf->expr_const_values[FKF_N], + kf->expr_const_values[FKF_N_FORCED], + kf->expr_const_values[FKF_PREV_FORCED_N], + kf->expr_const_values[FKF_T], + kf->expr_const_values[FKF_PREV_FORCED_T], + res); + + kf->expr_const_values[FKF_N] += 1; + + if (res) { + kf->expr_const_values[FKF_PREV_FORCED_N] = kf->expr_const_values[FKF_N] - 1; + kf->expr_const_values[FKF_PREV_FORCED_T] = kf->expr_const_values[FKF_T]; + kf->expr_const_values[FKF_N_FORCED] += 1; + goto force_keyframe; + } + } else if (kf->type == KF_FORCE_SOURCE && (frame->flags & AV_FRAME_FLAG_KEY)) { + goto force_keyframe; + } + + return AV_PICTURE_TYPE_NONE; + +force_keyframe: + av_log(logctx, AV_LOG_DEBUG, "Forced keyframe at time %f\n", pts_time); + return AV_PICTURE_TYPE_I; +} + +static int frame_encode(OutputStream *ost, AVFrame *frame, AVPacket *pkt) +{ + OutputFile *of = ost->file; + enum AVMediaType type = ost->type; + + if (type == AVMEDIA_TYPE_SUBTITLE) { + const AVSubtitle *subtitle = frame && frame->buf[0] ? + (AVSubtitle*)frame->buf[0]->data : NULL; + + // no flushing for subtitles + return subtitle && subtitle->num_rects ? + do_subtitle_out(of, ost, subtitle, pkt) : 0; + } + + if (frame) { + if (!check_recording_time(ost, frame->pts, frame->time_base)) + return AVERROR_EOF; + + if (type == AVMEDIA_TYPE_VIDEO) { + frame->quality = ost->enc_ctx->global_quality; + frame->pict_type = forced_kf_apply(ost, &ost->kf, frame); + +#if FFMPEG_OPT_TOP + if (ost->top_field_first >= 0) { + frame->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * (!!ost->top_field_first); + } +#endif + } else { + if (!(ost->enc_ctx->codec->capabilities & AV_CODEC_CAP_PARAM_CHANGE) && + ost->enc_ctx->ch_layout.nb_channels != frame->ch_layout.nb_channels) { + av_log(ost, AV_LOG_ERROR, + "Audio channel count changed and encoder does not support parameter changes\n"); + return 0; + } + } + } + + return encode_frame(of, ost, frame, pkt); +} + +static void enc_thread_set_name(const OutputStream *ost) +{ + char name[16]; + snprintf(name, sizeof(name), "enc%d:%d:%s", ost->file->index, ost->index, + ost->enc_ctx->codec->name); + ff_thread_setname(name); +} + +static void enc_thread_uninit(EncoderThread *et) +{ + av_packet_free(&et->pkt); + av_frame_free(&et->frame); + + memset(et, 0, sizeof(*et)); +} + +static int enc_thread_init(EncoderThread *et) +{ + memset(et, 0, sizeof(*et)); + + et->frame = av_frame_alloc(); + if (!et->frame) + goto fail; + + et->pkt = av_packet_alloc(); + if (!et->pkt) + goto fail; + + return 0; + +fail: + enc_thread_uninit(et); + return AVERROR(ENOMEM); +} + +int encoder_thread(void *arg) +{ + OutputStream *ost = arg; + Encoder *e = ost->enc; + EncoderThread et; + int ret = 0, input_status = 0; + int name_set = 0; + + ret = enc_thread_init(&et); + if (ret < 0) + goto finish; + + /* Open the subtitle encoders immediately. AVFrame-based encoders + * are opened through a callback from the scheduler once they get + * their first frame + * + * N.B.: because the callback is called from a different thread, + * enc_ctx MUST NOT be accessed before sch_enc_receive() returns + * for the first time for audio/video. */ + if (ost->type != AVMEDIA_TYPE_VIDEO && ost->type != AVMEDIA_TYPE_AUDIO) { + ret = enc_open(ost, NULL); + if (ret < 0) + goto finish; + } + + while (!input_status) { + input_status = sch_enc_receive(e->sch, e->sch_idx, et.frame); + if (input_status < 0) { + if (input_status == AVERROR_EOF) { + av_log(ost, AV_LOG_VERBOSE, "Encoder thread received EOF\n"); + if (e->opened) + break; + + av_log(ost, AV_LOG_ERROR, "Could not open encoder before EOF\n"); + ret = AVERROR(EINVAL); + } else { + av_log(ost, AV_LOG_ERROR, "Error receiving a frame for encoding: %s\n", + av_err2str(ret)); + ret = input_status; + } + goto finish; + } + + if (!name_set) { + enc_thread_set_name(ost); + name_set = 1; + } + + ret = frame_encode(ost, et.frame, et.pkt); + + av_packet_unref(et.pkt); + av_frame_unref(et.frame); + + if (ret < 0) { + if (ret == AVERROR_EOF) + av_log(ost, AV_LOG_VERBOSE, "Encoder returned EOF, finishing\n"); + else + av_log(ost, AV_LOG_ERROR, "Error encoding a frame: %s\n", + av_err2str(ret)); + break; + } + } + + // flush the encoder + if (ret == 0 || ret == AVERROR_EOF) { + ret = frame_encode(ost, NULL, et.pkt); + if (ret < 0 && ret != AVERROR_EOF) + av_log(ost, AV_LOG_ERROR, "Error flushing encoder: %s\n", + av_err2str(ret)); + } + + // EOF is normal thread termination + if (ret == AVERROR_EOF) + ret = 0; + +finish: + enc_thread_uninit(&et); + + return ret; +} + +int enc_loopback(Encoder *enc) +{ + enc->attach_par = 1; + return enc->sch_idx; +} diff --git a/fftools/ffmpeg_filter.c b/fftools/ffmpeg_filter.c index 686a33c2bae..171e47be9e2 100644 --- a/fftools/ffmpeg_filter.c +++ b/fftools/ffmpeg_filter.c @@ -36,109 +36,362 @@ #include "libavutil/pixfmt.h" #include "libavutil/imgutils.h" #include "libavutil/samplefmt.h" +#include "libavutil/time.h" +#include "libavutil/timestamp.h" -// FIXME: YUV420P etc. are actually supported with full color range, -// yet the latter information isn't available here. -static const enum AVPixelFormat *get_compliance_normal_pix_fmts(const AVCodec *codec, const enum AVPixelFormat default_formats[]) +// FIXME private header, used for mid_pred() +#include "libavcodec/mathops.h" + +typedef struct FilterGraphPriv { + FilterGraph fg; + + // name used for logging + char log_name[32]; + + int is_simple; + // true when the filtergraph contains only meta filters + // that do not modify the frame data + int is_meta; + // source filters are present in the graph + int have_sources; + int disable_conversions; + + unsigned nb_outputs_done; + + const char *graph_desc; + + // frame for temporarily holding output from the filtergraph + AVFrame *frame; + // frame for sending output to the encoder + AVFrame *frame_enc; + + Scheduler *sch; + unsigned sch_idx; +} FilterGraphPriv; + +static FilterGraphPriv *fgp_from_fg(FilterGraph *fg) { - static const enum AVPixelFormat mjpeg_formats[] = - { AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, - AV_PIX_FMT_NONE }; + return (FilterGraphPriv*)fg; +} - if (!strcmp(codec->name, "mjpeg")) { - return mjpeg_formats; - } else { - return default_formats; +static const FilterGraphPriv *cfgp_from_cfg(const FilterGraph *fg) +{ + return (const FilterGraphPriv*)fg; +} + +// data that is local to the filter thread and not visible outside of it +typedef struct FilterGraphThread { + AVFilterGraph *graph; + + AVFrame *frame; + + // Temporary buffer for output frames, since on filtergraph reset + // we cannot send them to encoders immediately. + // The output index is stored in frame opaque. + AVFifo *frame_queue_out; + + // index of the next input to request from the scheduler + unsigned next_in; + // set to 1 after at least one frame passed through this output + int got_frame; + + // EOF status of each input/output, as received by the thread + uint8_t *eof_in; + uint8_t *eof_out; +} FilterGraphThread; + +typedef struct InputFilterPriv { + InputFilter ifilter; + + InputFilterOptions opts; + + int index; + + AVFilterContext *filter; + + // used to hold submitted input + AVFrame *frame; + + /* for filters that are not yet bound to an input stream, + * this stores the input linklabel, if any */ + uint8_t *linklabel; + + // filter data type + enum AVMediaType type; + // source data type: AVMEDIA_TYPE_SUBTITLE for sub2video, + // same as type otherwise + enum AVMediaType type_src; + + int eof; + int bound; + + // parameters configured for this input + int format; + + int width, height; + AVRational sample_aspect_ratio; + enum AVColorSpace color_space; + enum AVColorRange color_range; + + int sample_rate; + AVChannelLayout ch_layout; + + AVRational time_base; + + AVFifo *frame_queue; + + AVBufferRef *hw_frames_ctx; + + int displaymatrix_present; + int32_t displaymatrix[9]; + + struct { + AVFrame *frame; + + int64_t last_pts; + int64_t end_pts; + + ///< marks if sub2video_update should force an initialization + unsigned int initialize; + } sub2video; +} InputFilterPriv; + +static InputFilterPriv *ifp_from_ifilter(InputFilter *ifilter) +{ + return (InputFilterPriv*)ifilter; +} + +typedef struct FPSConvContext { + AVFrame *last_frame; + /* number of frames emitted by the video-encoding sync code */ + int64_t frame_number; + /* history of nb_frames_prev, i.e. the number of times the + * previous frame was duplicated by vsync code in recent + * do_video_out() calls */ + int64_t frames_prev_hist[3]; + + uint64_t dup_warning; + + int last_dropped; + int dropped_keyframe; + + AVRational framerate; + AVRational framerate_max; + const AVRational *framerate_supported; + int framerate_clip; +} FPSConvContext; + +typedef struct OutputFilterPriv { + OutputFilter ofilter; + + int index; + + AVFilterContext *filter; + + /* desired output stream properties */ + int format; + int width, height; + int sample_rate; + AVChannelLayout ch_layout; + + // time base in which the output is sent to our downstream + // does not need to match the filtersink's timebase + AVRational tb_out; + // at least one frame with the above timebase was sent + // to our downstream, so it cannot change anymore + int tb_out_locked; + + AVRational sample_aspect_ratio; + + // those are only set if no format is specified and the encoder gives us multiple options + // They point directly to the relevant lists of the encoder. + const int *formats; + const AVChannelLayout *ch_layouts; + const int *sample_rates; + + AVRational enc_timebase; + // offset for output timestamps, in AV_TIME_BASE_Q + int64_t ts_offset; + int64_t next_pts; + FPSConvContext fps; +} OutputFilterPriv; + +static OutputFilterPriv *ofp_from_ofilter(OutputFilter *ofilter) +{ + return (OutputFilterPriv*)ofilter; +} + +typedef struct FilterCommand { + char *target; + char *command; + char *arg; + + double time; + int all_filters; +} FilterCommand; + +static void filter_command_free(void *opaque, uint8_t *data) +{ + FilterCommand *fc = (FilterCommand*)data; + + av_freep(&fc->target); + av_freep(&fc->command); + av_freep(&fc->arg); + + av_free(data); +} + +static int sub2video_get_blank_frame(InputFilterPriv *ifp) +{ + AVFrame *frame = ifp->sub2video.frame; + int ret; + + av_frame_unref(frame); + + frame->width = ifp->width; + frame->height = ifp->height; + frame->format = ifp->format; + frame->colorspace = ifp->color_space; + frame->color_range = ifp->color_range; + + ret = av_frame_get_buffer(frame, 0); + if (ret < 0) + return ret; + + memset(frame->data[0], 0, frame->height * frame->linesize[0]); + + return 0; +} + +static void sub2video_copy_rect(uint8_t *dst, int dst_linesize, int w, int h, + AVSubtitleRect *r) +{ + uint32_t *pal, *dst2; + uint8_t *src, *src2; + int x, y; + + if (r->type != SUBTITLE_BITMAP) { + av_log(NULL, AV_LOG_WARNING, "sub2video: non-bitmap subtitle\n"); + return; + } + if (r->x < 0 || r->x + r->w > w || r->y < 0 || r->y + r->h > h) { + av_log(NULL, AV_LOG_WARNING, "sub2video: rectangle (%d %d %d %d) overflowing %d %d\n", + r->x, r->y, r->w, r->h, w, h + ); + return; + } + + dst += r->y * dst_linesize + r->x * 4; + src = r->data[0]; + pal = (uint32_t *)r->data[1]; + for (y = 0; y < r->h; y++) { + dst2 = (uint32_t *)dst; + src2 = src; + for (x = 0; x < r->w; x++) + *(dst2++) = pal[*(src2++)]; + dst += dst_linesize; + src += r->linesize[0]; } } -static enum AVPixelFormat -choose_pixel_fmt(const AVCodec *codec, enum AVPixelFormat target, - int strict_std_compliance) +static void sub2video_push_ref(InputFilterPriv *ifp, int64_t pts) { - if (codec && codec->pix_fmts) { - const enum AVPixelFormat *p = codec->pix_fmts; - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(target); - //FIXME: This should check for AV_PIX_FMT_FLAG_ALPHA after PAL8 pixel format without alpha is implemented - int has_alpha = desc ? desc->nb_components % 2 == 0 : 0; - enum AVPixelFormat best= AV_PIX_FMT_NONE; + AVFrame *frame = ifp->sub2video.frame; + int ret; - if (strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL) { - p = get_compliance_normal_pix_fmts(codec, p); - } - for (; *p != AV_PIX_FMT_NONE; p++) { - best = av_find_best_pix_fmt_of_2(best, *p, target, has_alpha, NULL); - if (*p == target) - break; - } - if (*p == AV_PIX_FMT_NONE) { - if (target != AV_PIX_FMT_NONE) - av_log(NULL, AV_LOG_WARNING, - "Incompatible pixel format '%s' for codec '%s', auto-selecting format '%s'\n", - av_get_pix_fmt_name(target), - codec->name, - av_get_pix_fmt_name(best)); - return best; - } + av_assert1(frame->data[0]); + ifp->sub2video.last_pts = frame->pts = pts; + ret = av_buffersrc_add_frame_flags(ifp->filter, frame, + AV_BUFFERSRC_FLAG_KEEP_REF | + AV_BUFFERSRC_FLAG_PUSH); + if (ret != AVERROR_EOF && ret < 0) + av_log(NULL, AV_LOG_WARNING, "Error while add the frame to buffer source(%s).\n", + av_err2str(ret)); +} + +static void sub2video_update(InputFilterPriv *ifp, int64_t heartbeat_pts, + const AVSubtitle *sub) +{ + AVFrame *frame = ifp->sub2video.frame; + int8_t *dst; + int dst_linesize; + int num_rects; + int64_t pts, end_pts; + + if (sub) { + pts = av_rescale_q(sub->pts + sub->start_display_time * 1000LL, + AV_TIME_BASE_Q, ifp->time_base); + end_pts = av_rescale_q(sub->pts + sub->end_display_time * 1000LL, + AV_TIME_BASE_Q, ifp->time_base); + num_rects = sub->num_rects; + } else { + /* If we are initializing the system, utilize current heartbeat + PTS as the start time, and show until the following subpicture + is received. Otherwise, utilize the previous subpicture's end time + as the fall-back value. */ + pts = ifp->sub2video.initialize ? + heartbeat_pts : ifp->sub2video.end_pts; + end_pts = INT64_MAX; + num_rects = 0; + } + if (sub2video_get_blank_frame(ifp) < 0) { + av_log(NULL, AV_LOG_ERROR, + "Impossible to get a blank canvas.\n"); + return; } - return target; + dst = frame->data [0]; + dst_linesize = frame->linesize[0]; + for (int i = 0; i < num_rects; i++) + sub2video_copy_rect(dst, dst_linesize, frame->width, frame->height, sub->rects[i]); + sub2video_push_ref(ifp, pts); + ifp->sub2video.end_pts = end_pts; + ifp->sub2video.initialize = 0; } -/* May return NULL (no pixel format found), a static string or a string - * backed by the bprint. Nothing has been written to the AVBPrint in case +/* *dst may return be set to NULL (no pixel format found), a static string or a + * string backed by the bprint. Nothing has been written to the AVBPrint in case * NULL is returned. The AVBPrint provided should be clean. */ -static const char *choose_pix_fmts(OutputFilter *ofilter, AVBPrint *bprint) +static int choose_pix_fmts(OutputFilter *ofilter, AVBPrint *bprint, + const char **dst) { + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); OutputStream *ost = ofilter->ost; - AVCodecContext *enc = ost->enc_ctx; - const AVDictionaryEntry *strict_dict = av_dict_get(ost->encoder_opts, "strict", NULL, 0); - if (strict_dict) - // used by choose_pixel_fmt() and below - av_opt_set(ost->enc_ctx, "strict", strict_dict->value, 0); - - if (ost->keep_pix_fmt) { - avfilter_graph_set_auto_convert(ofilter->graph->graph, - AVFILTER_AUTO_CONVERT_NONE); - if (ost->enc_ctx->pix_fmt == AV_PIX_FMT_NONE) - return NULL; - return av_get_pix_fmt_name(ost->enc_ctx->pix_fmt); - } - if (ost->enc_ctx->pix_fmt != AV_PIX_FMT_NONE) { - return av_get_pix_fmt_name(choose_pixel_fmt(enc->codec, enc->pix_fmt, - ost->enc_ctx->strict_std_compliance)); - } else if (enc->codec->pix_fmts) { - const enum AVPixelFormat *p; - - p = enc->codec->pix_fmts; - if (ost->enc_ctx->strict_std_compliance > FF_COMPLIANCE_UNOFFICIAL) { - p = get_compliance_normal_pix_fmts(enc->codec, p); - } + + *dst = NULL; + + if (ost->keep_pix_fmt || ofp->format != AV_PIX_FMT_NONE) { + *dst = ofp->format == AV_PIX_FMT_NONE ? NULL : + av_get_pix_fmt_name(ofp->format); + } else if (ofp->formats) { + const enum AVPixelFormat *p = ofp->formats; for (; *p != AV_PIX_FMT_NONE; p++) { const char *name = av_get_pix_fmt_name(*p); av_bprintf(bprint, "%s%c", name, p[1] == AV_PIX_FMT_NONE ? '\0' : '|'); } if (!av_bprint_is_complete(bprint)) - report_and_exit(AVERROR(ENOMEM)); - return bprint->str; - } else - return NULL; + return AVERROR(ENOMEM); + + *dst = bprint->str; + } + + return 0; } /* Define a function for appending a list of allowed formats * to an AVBPrint. If nonempty, the list will have a header. */ #define DEF_CHOOSE_FORMAT(name, type, var, supported_list, none, printf_format, get_name) \ -static void choose_ ## name (OutputFilter *ofilter, AVBPrint *bprint) \ +static void choose_ ## name (OutputFilterPriv *ofp, AVBPrint *bprint) \ { \ - if (ofilter->var == none && !ofilter->supported_list) \ + if (ofp->var == none && !ofp->supported_list) \ return; \ av_bprintf(bprint, #name "="); \ - if (ofilter->var != none) { \ - av_bprintf(bprint, printf_format, get_name(ofilter->var)); \ + if (ofp->var != none) { \ + av_bprintf(bprint, printf_format, get_name(ofp->var)); \ } else { \ const type *p; \ \ - for (p = ofilter->supported_list; *p != none; p++) { \ + for (p = ofp->supported_list; *p != none; p++) { \ av_bprintf(bprint, printf_format "|", get_name(*p)); \ } \ if (bprint->len > 0) \ @@ -156,16 +409,16 @@ DEF_CHOOSE_FORMAT(sample_fmts, enum AVSampleFormat, format, formats, DEF_CHOOSE_FORMAT(sample_rates, int, sample_rate, sample_rates, 0, "%d", ) -static void choose_channel_layouts(OutputFilter *ofilter, AVBPrint *bprint) +static void choose_channel_layouts(OutputFilterPriv *ofp, AVBPrint *bprint) { - if (av_channel_layout_check(&ofilter->ch_layout)) { + if (av_channel_layout_check(&ofp->ch_layout)) { av_bprintf(bprint, "channel_layouts="); - av_channel_layout_describe_bprint(&ofilter->ch_layout, bprint); - } else if (ofilter->ch_layouts) { + av_channel_layout_describe_bprint(&ofp->ch_layout, bprint); + } else if (ofp->ch_layouts) { const AVChannelLayout *p; av_bprintf(bprint, "channel_layouts="); - for (p = ofilter->ch_layouts; p->nb_channels; p++) { + for (p = ofp->ch_layouts; p->nb_channels; p++) { av_channel_layout_describe_bprint(p, bprint); av_bprintf(bprint, "|"); } @@ -176,144 +429,6 @@ static void choose_channel_layouts(OutputFilter *ofilter, AVBPrint *bprint) av_bprint_chars(bprint, ':', 1); } -int init_simple_filtergraph(InputStream *ist, OutputStream *ost) -{ - FilterGraph *fg = av_mallocz(sizeof(*fg)); - OutputFilter *ofilter; - InputFilter *ifilter; - - if (!fg) - report_and_exit(AVERROR(ENOMEM)); - fg->index = nb_filtergraphs; - - ofilter = ALLOC_ARRAY_ELEM(fg->outputs, fg->nb_outputs); - ofilter->ost = ost; - ofilter->graph = fg; - ofilter->format = -1; - - ost->filter = ofilter; - - ifilter = ALLOC_ARRAY_ELEM(fg->inputs, fg->nb_inputs); - ifilter->ist = ist; - ifilter->graph = fg; - ifilter->format = -1; - - ifilter->frame_queue = av_fifo_alloc2(8, sizeof(AVFrame*), AV_FIFO_FLAG_AUTO_GROW); - if (!ifilter->frame_queue) - report_and_exit(AVERROR(ENOMEM)); - - GROW_ARRAY(ist->filters, ist->nb_filters); - ist->filters[ist->nb_filters - 1] = ifilter; - - GROW_ARRAY(filtergraphs, nb_filtergraphs); - filtergraphs[nb_filtergraphs - 1] = fg; - - return 0; -} - -static char *describe_filter_link(FilterGraph *fg, AVFilterInOut *inout, int in) -{ - AVFilterContext *ctx = inout->filter_ctx; - AVFilterPad *pads = in ? ctx->input_pads : ctx->output_pads; - int nb_pads = in ? ctx->nb_inputs : ctx->nb_outputs; - char *res; - - if (nb_pads > 1) - res = av_strdup(ctx->filter->name); - else - res = av_asprintf("%s:%s", ctx->filter->name, - avfilter_pad_get_name(pads, inout->pad_idx)); - if (!res) - report_and_exit(AVERROR(ENOMEM)); - return res; -} - -static void init_input_filter(FilterGraph *fg, AVFilterInOut *in) -{ - InputStream *ist = NULL; - enum AVMediaType type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx); - InputFilter *ifilter; - int i; - - // TODO: support other filter types - if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) { - av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported " - "currently.\n"); - exit_program(1); - } - - if (in->name) { - AVFormatContext *s; - AVStream *st = NULL; - char *p; - int file_idx = strtol(in->name, &p, 0); - - if (file_idx < 0 || file_idx >= nb_input_files) { - av_log(NULL, AV_LOG_FATAL, "Invalid file index %d in filtergraph description %s.\n", - file_idx, fg->graph_desc); - exit_program(1); - } - s = input_files[file_idx]->ctx; - - for (i = 0; i < s->nb_streams; i++) { - enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type; - if (stream_type != type && - !(stream_type == AVMEDIA_TYPE_SUBTITLE && - type == AVMEDIA_TYPE_VIDEO /* sub2video hack */)) - continue; - if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) { - st = s->streams[i]; - break; - } - } - if (!st) { - av_log(NULL, AV_LOG_FATAL, "Stream specifier '%s' in filtergraph description %s " - "matches no streams.\n", p, fg->graph_desc); - exit_program(1); - } - ist = input_files[file_idx]->streams[st->index]; - if (ist->user_set_discard == AVDISCARD_ALL) { - av_log(NULL, AV_LOG_FATAL, "Stream specifier '%s' in filtergraph description %s " - "matches a disabled input stream.\n", p, fg->graph_desc); - exit_program(1); - } - } else { - /* find the first unused stream of corresponding type */ - for (ist = ist_iter(NULL); ist; ist = ist_iter(ist)) { - if (ist->user_set_discard == AVDISCARD_ALL) - continue; - if (ist->dec_ctx->codec_type == type && ist->discard) - break; - } - if (!ist) { - av_log(NULL, AV_LOG_FATAL, "Cannot find a matching stream for " - "unlabeled input pad %d on filter %s\n", in->pad_idx, - in->filter_ctx->name); - exit_program(1); - } - } - av_assert0(ist); - - ist->discard = 0; - ist->decoding_needed |= DECODING_FOR_FILTER; - ist->processing_needed = 1; - ist->st->discard = AVDISCARD_NONE; - - ifilter = ALLOC_ARRAY_ELEM(fg->inputs, fg->nb_inputs); - ifilter->ist = ist; - ifilter->graph = fg; - ifilter->format = -1; - ifilter->type = ist->st->codecpar->codec_type; - ifilter->name = describe_filter_link(fg, in, 1); - - ifilter->frame_queue = av_fifo_alloc2(8, sizeof(AVFrame*), AV_FIFO_FLAG_AUTO_GROW); - if (!ifilter->frame_queue) - report_and_exit(AVERROR(ENOMEM)); - - GROW_ARRAY(ist->filters, ist->nb_filters); - ist->filters[ist->nb_filters - 1] = ifilter; -} - static int read_binary(const char *path, uint8_t **data, int *len) { AVIOContext *io = NULL; @@ -364,7 +479,7 @@ static int read_binary(const char *path, uint8_t **data, int *len) static int filter_opt_apply(AVFilterContext *f, const char *key, const char *val) { - const AVOption *o; + const AVOption *o = NULL; int ret; ret = av_opt_set(f, key, val, AV_OPT_SEARCH_CHILDREN); @@ -442,11 +557,15 @@ static int graph_opts_apply(AVFilterGraphSegment *seg) } static int graph_parse(AVFilterGraph *graph, const char *desc, - AVFilterInOut **inputs, AVFilterInOut **outputs) + AVFilterInOut **inputs, AVFilterInOut **outputs, + AVBufferRef *hw_device) { AVFilterGraphSegment *seg; int ret; + *inputs = NULL; + *outputs = NULL; + ret = avfilter_graph_segment_parse(graph, desc, 0, &seg); if (ret < 0) return ret; @@ -455,6 +574,20 @@ static int graph_parse(AVFilterGraph *graph, const char *desc, if (ret < 0) goto fail; + if (hw_device) { + for (int i = 0; i < graph->nb_filters; i++) { + AVFilterContext *f = graph->filters[i]; + + if (!(f->filter->flags & AVFILTER_FLAG_HWDEVICE)) + continue; + f->hw_device_ctx = av_buffer_ref(hw_device); + if (!f->hw_device_ctx) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + } + ret = graph_opts_apply(seg); if (ret < 0) goto fail; @@ -466,847 +599,2312 @@ static int graph_parse(AVFilterGraph *graph, const char *desc, return ret; } -int init_complex_filtergraph(FilterGraph *fg) +// Filters can be configured only if the formats of all inputs are known. +static int ifilter_has_all_input_formats(FilterGraph *fg) { - AVFilterInOut *inputs, *outputs, *cur; - AVFilterGraph *graph; - int ret = 0; + for (int i = 0; i < fg->nb_inputs; i++) { + InputFilterPriv *ifp = ifp_from_ifilter(fg->inputs[i]); + if (ifp->format < 0) + return 0; + } + return 1; +} - /* this graph is only used for determining the kinds of inputs - * and outputs we have, and is discarded on exit from this function */ - graph = avfilter_graph_alloc(); - if (!graph) - return AVERROR(ENOMEM); - graph->nb_threads = 1; +static int filter_thread(void *arg); - ret = graph_parse(graph, fg->graph_desc, &inputs, &outputs); - if (ret < 0) - goto fail; +static char *describe_filter_link(FilterGraph *fg, AVFilterInOut *inout, int in) +{ + AVFilterContext *ctx = inout->filter_ctx; + AVFilterPad *pads = in ? ctx->input_pads : ctx->output_pads; + int nb_pads = in ? ctx->nb_inputs : ctx->nb_outputs; - for (cur = inputs; cur; cur = cur->next) - init_input_filter(fg, cur); + if (nb_pads > 1) + return av_strdup(ctx->filter->name); + return av_asprintf("%s:%s", ctx->filter->name, + avfilter_pad_get_name(pads, inout->pad_idx)); +} + +static OutputFilter *ofilter_alloc(FilterGraph *fg) +{ + OutputFilterPriv *ofp; + OutputFilter *ofilter; - for (cur = outputs; cur;) { - OutputFilter *const ofilter = ALLOC_ARRAY_ELEM(fg->outputs, fg->nb_outputs); + ofp = allocate_array_elem(&fg->outputs, sizeof(*ofp), &fg->nb_outputs); + if (!ofp) + return NULL; - ofilter->graph = fg; - ofilter->out_tmp = cur; - ofilter->type = avfilter_pad_get_type(cur->filter_ctx->output_pads, - cur->pad_idx); - ofilter->name = describe_filter_link(fg, cur, 0); - cur = cur->next; - ofilter->out_tmp->next = NULL; - } + ofilter = &ofp->ofilter; + ofilter->graph = fg; + ofp->format = -1; + ofp->index = fg->nb_outputs - 1; -fail: - avfilter_inout_free(&inputs); - avfilter_graph_free(&graph); - return ret; + return ofilter; } -static int insert_trim(int64_t start_time, int64_t duration, - AVFilterContext **last_filter, int *pad_idx, - const char *filter_name) +static int ifilter_bind_ist(InputFilter *ifilter, InputStream *ist) { - AVFilterGraph *graph = (*last_filter)->graph; - AVFilterContext *ctx; - const AVFilter *trim; - enum AVMediaType type = avfilter_pad_get_type((*last_filter)->output_pads, *pad_idx); - const char *name = (type == AVMEDIA_TYPE_VIDEO) ? "trim" : "atrim"; - int ret = 0; + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + FilterGraphPriv *fgp = fgp_from_fg(ifilter->graph); + int ret, dec_idx; - if (duration == INT64_MAX && start_time == AV_NOPTS_VALUE) - return 0; + av_assert0(!ifp->bound); + ifp->bound = 1; - trim = avfilter_get_by_name(name); - if (!trim) { - av_log(NULL, AV_LOG_ERROR, "%s filter not present, cannot limit " - "recording time.\n", name); - return AVERROR_FILTER_NOT_FOUND; + if (ifp->type != ist->par->codec_type && + !(ifp->type == AVMEDIA_TYPE_VIDEO && ist->par->codec_type == AVMEDIA_TYPE_SUBTITLE)) { + av_log(fgp, AV_LOG_ERROR, "Tried to connect %s stream to %s filtergraph input\n", + av_get_media_type_string(ist->par->codec_type), av_get_media_type_string(ifp->type)); + return AVERROR(EINVAL); } - ctx = avfilter_graph_alloc_filter(graph, trim, filter_name); - if (!ctx) + ifp->type_src = ist->st->codecpar->codec_type; + + ifp->opts.fallback = av_frame_alloc(); + if (!ifp->opts.fallback) return AVERROR(ENOMEM); - if (duration != INT64_MAX) { - ret = av_opt_set_int(ctx, "durationi", duration, - AV_OPT_SEARCH_CHILDREN); - } - if (ret >= 0 && start_time != AV_NOPTS_VALUE) { - ret = av_opt_set_int(ctx, "starti", start_time, - AV_OPT_SEARCH_CHILDREN); - } - if (ret < 0) { - av_log(ctx, AV_LOG_ERROR, "Error configuring the %s filter", name); - return ret; - } + dec_idx = ist_filter_add(ist, ifilter, filtergraph_is_simple(ifilter->graph), + &ifp->opts); + if (dec_idx < 0) + return dec_idx; - ret = avfilter_init_str(ctx, NULL); + ret = sch_connect(fgp->sch, SCH_DEC(dec_idx), + SCH_FILTER_IN(fgp->sch_idx, ifp->index)); if (ret < 0) return ret; - ret = avfilter_link(*last_filter, *pad_idx, ctx, 0); - if (ret < 0) - return ret; + if (ifp->type_src == AVMEDIA_TYPE_SUBTITLE) { + ifp->sub2video.frame = av_frame_alloc(); + if (!ifp->sub2video.frame) + return AVERROR(ENOMEM); + + ifp->width = ifp->opts.sub2video_width; + ifp->height = ifp->opts.sub2video_height; + + /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the + palettes for all rectangles are identical or compatible */ + ifp->format = AV_PIX_FMT_RGB32; + + ifp->time_base = AV_TIME_BASE_Q; + + av_log(fgp, AV_LOG_VERBOSE, "sub2video: using %dx%d canvas\n", + ifp->width, ifp->height); + } - *last_filter = ctx; - *pad_idx = 0; return 0; } -static int insert_filter(AVFilterContext **last_filter, int *pad_idx, - const char *filter_name, const char *args) +static int ifilter_bind_dec(InputFilterPriv *ifp, Decoder *dec) { - AVFilterGraph *graph = (*last_filter)->graph; - AVFilterContext *ctx; - int ret; + FilterGraphPriv *fgp = fgp_from_fg(ifp->ifilter.graph); + int ret, dec_idx; - ret = avfilter_graph_create_filter(&ctx, - avfilter_get_by_name(filter_name), - filter_name, args, NULL, graph); - if (ret < 0) - return ret; + av_assert0(!ifp->bound); + ifp->bound = 1; - ret = avfilter_link(*last_filter, *pad_idx, ctx, 0); - if (ret < 0) - return ret; + if (ifp->type != dec->type) { + av_log(fgp, AV_LOG_ERROR, "Tried to connect %s decoder to %s filtergraph input\n", + av_get_media_type_string(dec->type), av_get_media_type_string(ifp->type)); + return AVERROR(EINVAL); + } + + ifp->type_src = ifp->type; + + dec_idx = dec_filter_add(dec, &ifp->ifilter, &ifp->opts); + if (dec_idx < 0) + return dec_idx; + + ret = sch_connect(fgp->sch, SCH_DEC(dec_idx), + SCH_FILTER_IN(fgp->sch_idx, ifp->index)); + if (ret < 0) + return ret; - *last_filter = ctx; - *pad_idx = 0; return 0; } -static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) +static int set_channel_layout(OutputFilterPriv *f, OutputStream *ost) { - OutputStream *ost = ofilter->ost; - OutputFile *of = output_files[ost->file_index]; - AVFilterContext *last_filter = out->filter_ctx; - AVBPrint bprint; - int pad_idx = out->pad_idx; - int ret; - const char *pix_fmts; - char name[255]; + const AVCodec *c = ost->enc_ctx->codec; + int i, err; + + if (ost->enc_ctx->ch_layout.order != AV_CHANNEL_ORDER_UNSPEC) { + /* Pass the layout through for all orders but UNSPEC */ + err = av_channel_layout_copy(&f->ch_layout, &ost->enc_ctx->ch_layout); + if (err < 0) + return err; + return 0; + } - snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index); - ret = avfilter_graph_create_filter(&ofilter->filter, - avfilter_get_by_name("buffersink"), - name, NULL, NULL, fg->graph); + /* Requested layout is of order UNSPEC */ + if (!c->ch_layouts) { + /* Use the default native layout for the requested amount of channels when the + encoder doesn't have a list of supported layouts */ + av_channel_layout_default(&f->ch_layout, ost->enc_ctx->ch_layout.nb_channels); + return 0; + } + /* Encoder has a list of supported layouts. Pick the first layout in it with the + same amount of channels as the requested layout */ + for (i = 0; c->ch_layouts[i].nb_channels; i++) { + if (c->ch_layouts[i].nb_channels == ost->enc_ctx->ch_layout.nb_channels) + break; + } + if (c->ch_layouts[i].nb_channels) { + /* Use it if one is found */ + err = av_channel_layout_copy(&f->ch_layout, &c->ch_layouts[i]); + if (err < 0) + return err; + return 0; + } + /* If no layout for the amount of channels requested was found, use the default + native layout for it. */ + av_channel_layout_default(&f->ch_layout, ost->enc_ctx->ch_layout.nb_channels); - if (ret < 0) - return ret; + return 0; +} - if ((ofilter->width || ofilter->height) && ofilter->ost->autoscale) { - char args[255]; - AVFilterContext *filter; - const AVDictionaryEntry *e = NULL; +int ofilter_bind_ost(OutputFilter *ofilter, OutputStream *ost, + unsigned sched_idx_enc) +{ + const OutputFile *of = ost->file; + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); + FilterGraph *fg = ofilter->graph; + FilterGraphPriv *fgp = fgp_from_fg(fg); + const AVCodec *c = ost->enc_ctx->codec; + int ret; - snprintf(args, sizeof(args), "%d:%d", - ofilter->width, ofilter->height); + av_assert0(!ofilter->ost); - while ((e = av_dict_iterate(ost->sws_dict, e))) { - av_strlcatf(args, sizeof(args), ":%s=%s", e->key, e->value); - } + ofilter->ost = ost; + av_freep(&ofilter->linklabel); - snprintf(name, sizeof(name), "scaler_out_%d_%d", - ost->file_index, ost->index); - if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"), - name, args, NULL, fg->graph)) < 0) - return ret; - if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) - return ret; + ofp->ts_offset = of->start_time == AV_NOPTS_VALUE ? 0 : of->start_time; + ofp->enc_timebase = ost->enc_timebase; - last_filter = filter; - pad_idx = 0; - } + switch (ost->enc_ctx->codec_type) { + case AVMEDIA_TYPE_VIDEO: + ofp->width = ost->enc_ctx->width; + ofp->height = ost->enc_ctx->height; + if (ost->enc_ctx->pix_fmt != AV_PIX_FMT_NONE) { + ofp->format = ost->enc_ctx->pix_fmt; + } else { + ofp->formats = c->pix_fmts; + + // MJPEG encoder exports a full list of supported pixel formats, + // but the full-range ones are experimental-only. + // Restrict the auto-conversion list unless -strict experimental + // has been specified. + if (!strcmp(c->name, "mjpeg")) { + // FIXME: YUV420P etc. are actually supported with full color range, + // yet the latter information isn't available here. + static const enum AVPixelFormat mjpeg_formats[] = + { AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, + AV_PIX_FMT_NONE }; + + const AVDictionaryEntry *strict = av_dict_get(ost->encoder_opts, "strict", NULL, 0); + int strict_val = ost->enc_ctx->strict_std_compliance; + + if (strict) { + const AVOption *o = av_opt_find(ost->enc_ctx, strict->key, NULL, 0, 0); + av_assert0(o); + av_opt_eval_int(ost->enc_ctx, o, strict->value, &strict_val); + } + + if (strict_val > FF_COMPLIANCE_UNOFFICIAL) + ofp->formats = mjpeg_formats; + } + } - av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED); - if ((pix_fmts = choose_pix_fmts(ofilter, &bprint))) { - AVFilterContext *filter; + fgp->disable_conversions |= ost->keep_pix_fmt; - ret = avfilter_graph_create_filter(&filter, - avfilter_get_by_name("format"), - "format", pix_fmts, NULL, fg->graph); - av_bprint_finalize(&bprint, NULL); - if (ret < 0) - return ret; - if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) - return ret; + ofp->fps.last_frame = av_frame_alloc(); + if (!ofp->fps.last_frame) + return AVERROR(ENOMEM); - last_filter = filter; - pad_idx = 0; - } + ofp->fps.framerate = ost->frame_rate; + ofp->fps.framerate_max = ost->max_frame_rate; + ofp->fps.framerate_supported = ost->force_fps ? + NULL : c->supported_framerates; - if (ost->frame_rate.num && 0) { - AVFilterContext *fps; - char args[255]; + // reduce frame rate for mpeg4 to be within the spec limits + if (c->id == AV_CODEC_ID_MPEG4) + ofp->fps.framerate_clip = 65535; - snprintf(args, sizeof(args), "fps=%d/%d", ost->frame_rate.num, - ost->frame_rate.den); - snprintf(name, sizeof(name), "fps_out_%d_%d", - ost->file_index, ost->index); - ret = avfilter_graph_create_filter(&fps, avfilter_get_by_name("fps"), - name, args, NULL, fg->graph); - if (ret < 0) - return ret; + ofp->fps.dup_warning = 1000; - ret = avfilter_link(last_filter, pad_idx, fps, 0); - if (ret < 0) - return ret; - last_filter = fps; - pad_idx = 0; + break; + case AVMEDIA_TYPE_AUDIO: + if (ost->enc_ctx->sample_fmt != AV_SAMPLE_FMT_NONE) { + ofp->format = ost->enc_ctx->sample_fmt; + } else { + ofp->formats = c->sample_fmts; + } + if (ost->enc_ctx->sample_rate) { + ofp->sample_rate = ost->enc_ctx->sample_rate; + } else { + ofp->sample_rates = c->supported_samplerates; + } + if (ost->enc_ctx->ch_layout.nb_channels) { + int ret = set_channel_layout(ofp, ost); + if (ret < 0) + return ret; + } else if (c->ch_layouts) { + ofp->ch_layouts = c->ch_layouts; + } + break; } - snprintf(name, sizeof(name), "trim_out_%d_%d", - ost->file_index, ost->index); - ret = insert_trim(of->start_time, of->recording_time, - &last_filter, &pad_idx, name); + ret = sch_connect(fgp->sch, SCH_FILTER_OUT(fgp->sch_idx, ofp->index), + SCH_ENC(sched_idx_enc)); if (ret < 0) return ret; + return 0; +} - if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) - return ret; +static InputFilter *ifilter_alloc(FilterGraph *fg) +{ + InputFilterPriv *ifp; + InputFilter *ifilter; - return 0; + ifp = allocate_array_elem(&fg->inputs, sizeof(*ifp), &fg->nb_inputs); + if (!ifp) + return NULL; + + ifilter = &ifp->ifilter; + ifilter->graph = fg; + + ifp->frame = av_frame_alloc(); + if (!ifp->frame) + return NULL; + + ifp->index = fg->nb_inputs - 1; + ifp->format = -1; + ifp->color_space = AVCOL_SPC_UNSPECIFIED; + ifp->color_range = AVCOL_RANGE_UNSPECIFIED; + + ifp->frame_queue = av_fifo_alloc2(8, sizeof(AVFrame*), AV_FIFO_FLAG_AUTO_GROW); + if (!ifp->frame_queue) + return NULL; + + return ifilter; } -static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out) +void fg_free(FilterGraph **pfg) { - OutputStream *ost = ofilter->ost; - OutputFile *of = output_files[ost->file_index]; - AVCodecContext *codec = ost->enc_ctx; - AVFilterContext *last_filter = out->filter_ctx; - int pad_idx = out->pad_idx; - AVBPrint args; - char name[255]; - int ret; + FilterGraph *fg = *pfg; + FilterGraphPriv *fgp; - snprintf(name, sizeof(name), "out_%d_%d", ost->file_index, ost->index); - ret = avfilter_graph_create_filter(&ofilter->filter, - avfilter_get_by_name("abuffersink"), - name, NULL, NULL, fg->graph); - if (ret < 0) - return ret; - if ((ret = av_opt_set_int(ofilter->filter, "all_channel_counts", 1, AV_OPT_SEARCH_CHILDREN)) < 0) - return ret; + if (!fg) + return; + fgp = fgp_from_fg(fg); -#define AUTO_INSERT_FILTER(opt_name, filter_name, arg) do { \ - AVFilterContext *filt_ctx; \ - \ - av_log(NULL, AV_LOG_INFO, opt_name " is forwarded to lavfi " \ - "similarly to -af " filter_name "=%s.\n", arg); \ - \ - ret = avfilter_graph_create_filter(&filt_ctx, \ - avfilter_get_by_name(filter_name), \ - filter_name, arg, NULL, fg->graph); \ - if (ret < 0) \ - goto fail; \ - \ - ret = avfilter_link(last_filter, pad_idx, filt_ctx, 0); \ - if (ret < 0) \ - goto fail; \ - \ - last_filter = filt_ctx; \ - pad_idx = 0; \ -} while (0) - av_bprint_init(&args, 0, AV_BPRINT_SIZE_UNLIMITED); -#if FFMPEG_OPT_MAP_CHANNEL - if (ost->audio_channels_mapped) { - AVChannelLayout mapped_layout = { 0 }; - int i; - av_channel_layout_default(&mapped_layout, ost->audio_channels_mapped); - av_channel_layout_describe_bprint(&mapped_layout, &args); - for (i = 0; i < ost->audio_channels_mapped; i++) - if (ost->audio_channels_map[i] != -1) - av_bprintf(&args, "|c%d=c%d", i, ost->audio_channels_map[i]); + for (int j = 0; j < fg->nb_inputs; j++) { + InputFilter *ifilter = fg->inputs[j]; + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + + if (ifp->frame_queue) { + AVFrame *frame; + while (av_fifo_read(ifp->frame_queue, &frame, 1) >= 0) + av_frame_free(&frame); + av_fifo_freep2(&ifp->frame_queue); + } + av_frame_free(&ifp->sub2video.frame); - AUTO_INSERT_FILTER("-map_channel", "pan", args.str); - av_bprint_clear(&args); + av_frame_free(&ifp->frame); + av_frame_free(&ifp->opts.fallback); + + av_buffer_unref(&ifp->hw_frames_ctx); + av_freep(&ifp->linklabel); + av_freep(&ifp->opts.name); + av_freep(&ifilter->name); + av_freep(&fg->inputs[j]); } -#endif + av_freep(&fg->inputs); + for (int j = 0; j < fg->nb_outputs; j++) { + OutputFilter *ofilter = fg->outputs[j]; + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); - if (codec->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) - av_channel_layout_default(&codec->ch_layout, codec->ch_layout.nb_channels); + av_frame_free(&ofp->fps.last_frame); - choose_sample_fmts(ofilter, &args); - choose_sample_rates(ofilter, &args); - choose_channel_layouts(ofilter, &args); - if (!av_bprint_is_complete(&args)) { - ret = AVERROR(ENOMEM); + av_freep(&ofilter->linklabel); + av_freep(&ofilter->name); + av_channel_layout_uninit(&ofp->ch_layout); + av_freep(&fg->outputs[j]); + } + av_freep(&fg->outputs); + av_freep(&fgp->graph_desc); + + av_frame_free(&fgp->frame); + av_frame_free(&fgp->frame_enc); + + av_freep(pfg); +} + +static const char *fg_item_name(void *obj) +{ + const FilterGraphPriv *fgp = obj; + + return fgp->log_name; +} + +static const AVClass fg_class = { + .class_name = "FilterGraph", + .version = LIBAVUTIL_VERSION_INT, + .item_name = fg_item_name, + .category = AV_CLASS_CATEGORY_FILTER, +}; + +int fg_create(FilterGraph **pfg, char *graph_desc, Scheduler *sch) +{ + FilterGraphPriv *fgp; + FilterGraph *fg; + + AVFilterInOut *inputs, *outputs; + AVFilterGraph *graph; + int ret = 0; + + fgp = allocate_array_elem(&filtergraphs, sizeof(*fgp), &nb_filtergraphs); + if (!fgp) + return AVERROR(ENOMEM); + fg = &fgp->fg; + + if (pfg) + *pfg = fg; + + fg->class = &fg_class; + fg->index = nb_filtergraphs - 1; + fgp->graph_desc = graph_desc; + fgp->disable_conversions = !auto_conversion_filters; + fgp->sch = sch; + + snprintf(fgp->log_name, sizeof(fgp->log_name), "fc#%d", fg->index); + + fgp->frame = av_frame_alloc(); + fgp->frame_enc = av_frame_alloc(); + if (!fgp->frame || !fgp->frame_enc) + return AVERROR(ENOMEM); + + /* this graph is only used for determining the kinds of inputs + * and outputs we have, and is discarded on exit from this function */ + graph = avfilter_graph_alloc(); + if (!graph) + return AVERROR(ENOMEM);; + graph->nb_threads = 1; + + ret = graph_parse(graph, fgp->graph_desc, &inputs, &outputs, NULL); + if (ret < 0) goto fail; + + for (unsigned i = 0; i < graph->nb_filters; i++) { + const AVFilter *f = graph->filters[i]->filter; + if (!avfilter_filter_pad_count(f, 0) && + !(f->flags & AVFILTER_FLAG_DYNAMIC_INPUTS)) { + fgp->have_sources = 1; + break; + } } - if (args.len) { - AVFilterContext *format; - snprintf(name, sizeof(name), "format_out_%d_%d", - ost->file_index, ost->index); - ret = avfilter_graph_create_filter(&format, - avfilter_get_by_name("aformat"), - name, args.str, NULL, fg->graph); - if (ret < 0) + for (AVFilterInOut *cur = inputs; cur; cur = cur->next) { + InputFilter *const ifilter = ifilter_alloc(fg); + InputFilterPriv *ifp; + + if (!ifilter) { + ret = AVERROR(ENOMEM); goto fail; + } - ret = avfilter_link(last_filter, pad_idx, format, 0); - if (ret < 0) + ifp = ifp_from_ifilter(ifilter); + ifp->linklabel = cur->name; + cur->name = NULL; + + ifp->type = avfilter_pad_get_type(cur->filter_ctx->input_pads, + cur->pad_idx); + + if (ifp->type != AVMEDIA_TYPE_VIDEO && ifp->type != AVMEDIA_TYPE_AUDIO) { + av_log(fg, AV_LOG_FATAL, "Only video and audio filters supported " + "currently.\n"); + ret = AVERROR(ENOSYS); goto fail; + } - last_filter = format; - pad_idx = 0; + ifilter->name = describe_filter_link(fg, cur, 1); + if (!ifilter->name) { + ret = AVERROR(ENOMEM); + goto fail; + } } - if (ost->apad && of->shortest) { - int i; + for (AVFilterInOut *cur = outputs; cur; cur = cur->next) { + OutputFilter *const ofilter = ofilter_alloc(fg); - for (i = 0; i < of->nb_streams; i++) - if (of->streams[i]->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) - break; + if (!ofilter) { + ret = AVERROR(ENOMEM); + goto fail; + } - if (i < of->nb_streams) { - AUTO_INSERT_FILTER("-apad", "apad", ost->apad); + ofilter->linklabel = cur->name; + cur->name = NULL; + + ofilter->type = avfilter_pad_get_type(cur->filter_ctx->output_pads, + cur->pad_idx); + ofilter->name = describe_filter_link(fg, cur, 0); + if (!ofilter->name) { + ret = AVERROR(ENOMEM); + goto fail; } } - snprintf(name, sizeof(name), "trim for output stream %d:%d", - ost->file_index, ost->index); - ret = insert_trim(of->start_time, of->recording_time, - &last_filter, &pad_idx, name); - if (ret < 0) + if (!fg->nb_outputs) { + av_log(fg, AV_LOG_FATAL, "A filtergraph has zero outputs, this is not supported\n"); + ret = AVERROR(ENOSYS); goto fail; + } - if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0) + ret = sch_add_filtergraph(sch, fg->nb_inputs, fg->nb_outputs, + filter_thread, fgp); + if (ret < 0) goto fail; + fgp->sch_idx = ret; + fail: - av_bprint_finalize(&args, NULL); + avfilter_inout_free(&inputs); + avfilter_inout_free(&outputs); + avfilter_graph_free(&graph); - return ret; + if (ret < 0) + return ret; + + return 0; } -static int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, - AVFilterInOut *out) +int init_simple_filtergraph(InputStream *ist, OutputStream *ost, + char *graph_desc, + Scheduler *sch, unsigned sched_idx_enc) { - if (!ofilter->ost) { - av_log(NULL, AV_LOG_FATAL, "Filter %s has an unconnected output\n", ofilter->name); - exit_program(1); - } + FilterGraph *fg; + FilterGraphPriv *fgp; + int ret; - switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) { - case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out); - case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out); - default: av_assert0(0); return 0; + ret = fg_create(&fg, graph_desc, sch); + if (ret < 0) + return ret; + fgp = fgp_from_fg(fg); + + fgp->is_simple = 1; + + snprintf(fgp->log_name, sizeof(fgp->log_name), "%cf#%d:%d", + av_get_media_type_string(ost->type)[0], + ost->file->index, ost->index); + + if (fg->nb_inputs != 1 || fg->nb_outputs != 1) { + av_log(fg, AV_LOG_ERROR, "Simple filtergraph '%s' was expected " + "to have exactly 1 input and 1 output. " + "However, it had %d input(s) and %d output(s). Please adjust, " + "or use a complex filtergraph (-filter_complex) instead.\n", + graph_desc, fg->nb_inputs, fg->nb_outputs); + return AVERROR(EINVAL); } + + ost->filter = fg->outputs[0]; + + ret = ifilter_bind_ist(fg->inputs[0], ist); + if (ret < 0) + return ret; + + ret = ofilter_bind_ost(fg->outputs[0], ost, sched_idx_enc); + if (ret < 0) + return ret; + + return 0; } -void check_filter_outputs(void) -{ - int i; - for (i = 0; i < nb_filtergraphs; i++) { - int n; - for (n = 0; n < filtergraphs[i]->nb_outputs; n++) { - OutputFilter *output = filtergraphs[i]->outputs[n]; - if (!output->ost) { - av_log(NULL, AV_LOG_FATAL, "Filter %s has an unconnected output\n", output->name); - exit_program(1); +static int fg_complex_bind_input(FilterGraph *fg, InputFilter *ifilter) +{ + FilterGraphPriv *fgp = fgp_from_fg(fg); + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + InputStream *ist = NULL; + enum AVMediaType type = ifp->type; + int i, ret; + + if (ifp->linklabel && !strncmp(ifp->linklabel, "dec:", 4)) { + // bind to a standalone decoder + int dec_idx; + + dec_idx = strtol(ifp->linklabel + 4, NULL, 0); + if (dec_idx < 0 || dec_idx >= nb_decoders) { + av_log(fg, AV_LOG_ERROR, "Invalid decoder index %d in filtergraph description %s\n", + dec_idx, fgp->graph_desc); + return AVERROR(EINVAL); + } + + ret = ifilter_bind_dec(ifp, decoders[dec_idx]); + if (ret < 0) + av_log(fg, AV_LOG_ERROR, "Error binding a decoder to filtergraph input %s\n", + ifilter->name); + return ret; + } else if (ifp->linklabel) { + // bind to an explicitly specified demuxer stream + AVFormatContext *s; + AVStream *st = NULL; + char *p; + int file_idx = strtol(ifp->linklabel, &p, 0); + + if (file_idx < 0 || file_idx >= nb_input_files) { + av_log(fg, AV_LOG_FATAL, "Invalid file index %d in filtergraph description %s.\n", + file_idx, fgp->graph_desc); + return AVERROR(EINVAL); + } + s = input_files[file_idx]->ctx; + + for (i = 0; i < s->nb_streams; i++) { + enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type; + if (stream_type != type && + !(stream_type == AVMEDIA_TYPE_SUBTITLE && + type == AVMEDIA_TYPE_VIDEO /* sub2video hack */)) + continue; + if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) { + st = s->streams[i]; + break; + } + } + if (!st) { + av_log(fg, AV_LOG_FATAL, "Stream specifier '%s' in filtergraph description %s " + "matches no streams.\n", p, fgp->graph_desc); + return AVERROR(EINVAL); + } + ist = input_files[file_idx]->streams[st->index]; + + av_log(fg, AV_LOG_VERBOSE, + "Binding input with label '%s' to input stream %d:%d\n", + ifp->linklabel, ist->file->index, ist->index); + } else { + ist = ist_find_unused(type); + if (!ist) { + av_log(fg, AV_LOG_FATAL, "Cannot find a matching stream for " + "unlabeled input pad %s\n", ifilter->name); + return AVERROR(EINVAL); + } + + av_log(fg, AV_LOG_VERBOSE, + "Binding unlabeled input %d to input stream %d:%d\n", + ifp->index, ist->file->index, ist->index); + } + av_assert0(ist); + + ret = ifilter_bind_ist(ifilter, ist); + if (ret < 0) { + av_log(fg, AV_LOG_ERROR, + "Error binding an input stream to complex filtergraph input %s.\n", + ifilter->name); + return ret; + } + + return 0; +} + +int fg_finalise_bindings(FilterGraph *fg) +{ + // bind filtergraph inputs to input streams + for (int i = 0; i < fg->nb_inputs; i++) { + InputFilterPriv *ifp = ifp_from_ifilter(fg->inputs[i]); + int ret; + + if (ifp->bound) + continue; + + ret = fg_complex_bind_input(fg, &ifp->ifilter); + if (ret < 0) + return ret; + } + + for (int i = 0; i < fg->nb_outputs; i++) { + OutputFilter *output = fg->outputs[i]; + if (!output->ost) { + av_log(filtergraphs[i], AV_LOG_FATAL, + "Filter %s has an unconnected output\n", output->name); + return AVERROR(EINVAL); + } + } + return 0; +} + +static int insert_trim(int64_t start_time, int64_t duration, + AVFilterContext **last_filter, int *pad_idx, + const char *filter_name) +{ + AVFilterGraph *graph = (*last_filter)->graph; + AVFilterContext *ctx; + const AVFilter *trim; + enum AVMediaType type = avfilter_pad_get_type((*last_filter)->output_pads, *pad_idx); + const char *name = (type == AVMEDIA_TYPE_VIDEO) ? "trim" : "atrim"; + int ret = 0; + + if (duration == INT64_MAX && start_time == AV_NOPTS_VALUE) + return 0; + + trim = avfilter_get_by_name(name); + if (!trim) { + av_log(NULL, AV_LOG_ERROR, "%s filter not present, cannot limit " + "recording time.\n", name); + return AVERROR_FILTER_NOT_FOUND; + } + + ctx = avfilter_graph_alloc_filter(graph, trim, filter_name); + if (!ctx) + return AVERROR(ENOMEM); + + if (duration != INT64_MAX) { + ret = av_opt_set_int(ctx, "durationi", duration, + AV_OPT_SEARCH_CHILDREN); + } + if (ret >= 0 && start_time != AV_NOPTS_VALUE) { + ret = av_opt_set_int(ctx, "starti", start_time, + AV_OPT_SEARCH_CHILDREN); + } + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Error configuring the %s filter", name); + return ret; + } + + ret = avfilter_init_str(ctx, NULL); + if (ret < 0) + return ret; + + ret = avfilter_link(*last_filter, *pad_idx, ctx, 0); + if (ret < 0) + return ret; + + *last_filter = ctx; + *pad_idx = 0; + return 0; +} + +static int insert_filter(AVFilterContext **last_filter, int *pad_idx, + const char *filter_name, const char *args) +{ + AVFilterGraph *graph = (*last_filter)->graph; + AVFilterContext *ctx; + int ret; + + ret = avfilter_graph_create_filter(&ctx, + avfilter_get_by_name(filter_name), + filter_name, args, NULL, graph); + if (ret < 0) + return ret; + + ret = avfilter_link(*last_filter, *pad_idx, ctx, 0); + if (ret < 0) + return ret; + + *last_filter = ctx; + *pad_idx = 0; + return 0; +} + +static int configure_output_video_filter(FilterGraph *fg, AVFilterGraph *graph, + OutputFilter *ofilter, AVFilterInOut *out) +{ + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); + OutputStream *ost = ofilter->ost; + OutputFile *of = ost->file; + AVFilterContext *last_filter = out->filter_ctx; + AVBPrint bprint; + int pad_idx = out->pad_idx; + int ret; + const char *pix_fmts; + char name[255]; + + snprintf(name, sizeof(name), "out_%d_%d", ost->file->index, ost->index); + ret = avfilter_graph_create_filter(&ofp->filter, + avfilter_get_by_name("buffersink"), + name, NULL, NULL, graph); + + if (ret < 0) + return ret; + + if ((ofp->width || ofp->height) && ofilter->ost->autoscale) { + char args[255]; + AVFilterContext *filter; + const AVDictionaryEntry *e = NULL; + + snprintf(args, sizeof(args), "%d:%d", + ofp->width, ofp->height); + + while ((e = av_dict_iterate(ost->sws_dict, e))) { + av_strlcatf(args, sizeof(args), ":%s=%s", e->key, e->value); + } + + snprintf(name, sizeof(name), "scaler_out_%d_%d", + ost->file->index, ost->index); + if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"), + name, args, NULL, graph)) < 0) + return ret; + if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) + return ret; + + last_filter = filter; + pad_idx = 0; + } + + av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED); + ret = choose_pix_fmts(ofilter, &bprint, &pix_fmts); + if (ret < 0) + return ret; + + if (pix_fmts) { + AVFilterContext *filter; + + ret = avfilter_graph_create_filter(&filter, + avfilter_get_by_name("format"), + "format", pix_fmts, NULL, graph); + av_bprint_finalize(&bprint, NULL); + if (ret < 0) + return ret; + if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0) + return ret; + + last_filter = filter; + pad_idx = 0; + } + + snprintf(name, sizeof(name), "trim_out_%d_%d", + ost->file->index, ost->index); + ret = insert_trim(of->start_time, of->recording_time, + &last_filter, &pad_idx, name); + if (ret < 0) + return ret; + + + if ((ret = avfilter_link(last_filter, pad_idx, ofp->filter, 0)) < 0) + return ret; + + return 0; +} + +static int configure_output_audio_filter(FilterGraph *fg, AVFilterGraph *graph, + OutputFilter *ofilter, AVFilterInOut *out) +{ + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); + OutputStream *ost = ofilter->ost; + OutputFile *of = ost->file; + AVFilterContext *last_filter = out->filter_ctx; + int pad_idx = out->pad_idx; + AVBPrint args; + char name[255]; + int ret; + + snprintf(name, sizeof(name), "out_%d_%d", ost->file->index, ost->index); + ret = avfilter_graph_create_filter(&ofp->filter, + avfilter_get_by_name("abuffersink"), + name, NULL, NULL, graph); + if (ret < 0) + return ret; + if ((ret = av_opt_set_int(ofp->filter, "all_channel_counts", 1, AV_OPT_SEARCH_CHILDREN)) < 0) + return ret; + +#define AUTO_INSERT_FILTER(opt_name, filter_name, arg) do { \ + AVFilterContext *filt_ctx; \ + \ + av_log(fg, AV_LOG_INFO, opt_name " is forwarded to lavfi " \ + "similarly to -af " filter_name "=%s.\n", arg); \ + \ + ret = avfilter_graph_create_filter(&filt_ctx, \ + avfilter_get_by_name(filter_name), \ + filter_name, arg, NULL, graph); \ + if (ret < 0) \ + goto fail; \ + \ + ret = avfilter_link(last_filter, pad_idx, filt_ctx, 0); \ + if (ret < 0) \ + goto fail; \ + \ + last_filter = filt_ctx; \ + pad_idx = 0; \ +} while (0) + av_bprint_init(&args, 0, AV_BPRINT_SIZE_UNLIMITED); + + choose_sample_fmts(ofp, &args); + choose_sample_rates(ofp, &args); + choose_channel_layouts(ofp, &args); + if (!av_bprint_is_complete(&args)) { + ret = AVERROR(ENOMEM); + goto fail; + } + if (args.len) { + AVFilterContext *format; + + snprintf(name, sizeof(name), "format_out_%d_%d", + ost->file->index, ost->index); + ret = avfilter_graph_create_filter(&format, + avfilter_get_by_name("aformat"), + name, args.str, NULL, graph); + if (ret < 0) + goto fail; + + ret = avfilter_link(last_filter, pad_idx, format, 0); + if (ret < 0) + goto fail; + + last_filter = format; + pad_idx = 0; + } + + if (ost->apad && of->shortest) { + int i; + + for (i = 0; i < of->nb_streams; i++) + if (of->streams[i]->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) + break; + + if (i < of->nb_streams) { + AUTO_INSERT_FILTER("-apad", "apad", ost->apad); + } + } + + snprintf(name, sizeof(name), "trim for output stream %d:%d", + ost->file->index, ost->index); + ret = insert_trim(of->start_time, of->recording_time, + &last_filter, &pad_idx, name); + if (ret < 0) + goto fail; + + if ((ret = avfilter_link(last_filter, pad_idx, ofp->filter, 0)) < 0) + goto fail; +fail: + av_bprint_finalize(&args, NULL); + + return ret; +} + +static int configure_output_filter(FilterGraph *fg, AVFilterGraph *graph, + OutputFilter *ofilter, AVFilterInOut *out) +{ + if (!ofilter->ost) { + av_log(fg, AV_LOG_FATAL, "Filter %s has an unconnected output\n", ofilter->name); + return AVERROR(EINVAL); + } + + switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) { + case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, graph, ofilter, out); + case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, graph, ofilter, out); + default: av_assert0(0); return 0; + } +} + +static void sub2video_prepare(InputFilterPriv *ifp) +{ + ifp->sub2video.last_pts = INT64_MIN; + ifp->sub2video.end_pts = INT64_MIN; + + /* sub2video structure has been (re-)initialized. + Mark it as such so that the system will be + initialized with the first received heartbeat. */ + ifp->sub2video.initialize = 1; +} + +static int configure_input_video_filter(FilterGraph *fg, AVFilterGraph *graph, + InputFilter *ifilter, AVFilterInOut *in) +{ + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + + AVFilterContext *last_filter; + const AVFilter *buffer_filt = avfilter_get_by_name("buffer"); + const AVPixFmtDescriptor *desc; + AVRational fr = ifp->opts.framerate; + AVRational sar; + AVBPrint args; + char name[255]; + int ret, pad_idx = 0; + AVBufferSrcParameters *par = av_buffersrc_parameters_alloc(); + if (!par) + return AVERROR(ENOMEM); + + if (ifp->type_src == AVMEDIA_TYPE_SUBTITLE) + sub2video_prepare(ifp); + + sar = ifp->sample_aspect_ratio; + if(!sar.den) + sar = (AVRational){0,1}; + av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC); + av_bprintf(&args, + "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:" + "pixel_aspect=%d/%d:colorspace=%d:range=%d", + ifp->width, ifp->height, ifp->format, + ifp->time_base.num, ifp->time_base.den, sar.num, sar.den, + ifp->color_space, ifp->color_range); + if (fr.num && fr.den) + av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den); + snprintf(name, sizeof(name), "graph %d input from stream %s", fg->index, + ifp->opts.name); + + + if ((ret = avfilter_graph_create_filter(&ifp->filter, buffer_filt, name, + args.str, NULL, graph)) < 0) + goto fail; + par->hw_frames_ctx = ifp->hw_frames_ctx; + ret = av_buffersrc_parameters_set(ifp->filter, par); + if (ret < 0) + goto fail; + av_freep(&par); + last_filter = ifp->filter; + + desc = av_pix_fmt_desc_get(ifp->format); + av_assert0(desc); + + // TODO: insert hwaccel enabled filters like transpose_vaapi into the graph + if ((ifp->opts.flags & IFILTER_FLAG_AUTOROTATE) && + !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { + int32_t *displaymatrix = ifp->displaymatrix; + double theta; + + theta = get_rotation(displaymatrix); + + if (fabs(theta - 90) < 1.0) { + ret = insert_filter(&last_filter, &pad_idx, "transpose", + displaymatrix[3] > 0 ? "cclock_flip" : "clock"); + } else if (fabs(theta - 180) < 1.0) { + if (displaymatrix[0] < 0) { + ret = insert_filter(&last_filter, &pad_idx, "hflip", NULL); + if (ret < 0) + return ret; + } + if (displaymatrix[4] < 0) { + ret = insert_filter(&last_filter, &pad_idx, "vflip", NULL); + } + } else if (fabs(theta - 270) < 1.0) { + ret = insert_filter(&last_filter, &pad_idx, "transpose", + displaymatrix[3] < 0 ? "clock_flip" : "cclock"); + } else if (fabs(theta) > 1.0) { + char rotate_buf[64]; + snprintf(rotate_buf, sizeof(rotate_buf), "%f*PI/180", theta); + ret = insert_filter(&last_filter, &pad_idx, "rotate", rotate_buf); + } else if (fabs(theta) < 1.0) { + if (displaymatrix && displaymatrix[4] < 0) { + ret = insert_filter(&last_filter, &pad_idx, "vflip", NULL); + } + } + if (ret < 0) + return ret; + } + + snprintf(name, sizeof(name), "trim_in_%s", ifp->opts.name); + ret = insert_trim(ifp->opts.trim_start_us, ifp->opts.trim_end_us, + &last_filter, &pad_idx, name); + if (ret < 0) + return ret; + + if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) + return ret; + return 0; +fail: + av_freep(&par); + + return ret; +} + +static int configure_input_audio_filter(FilterGraph *fg, AVFilterGraph *graph, + InputFilter *ifilter, AVFilterInOut *in) +{ + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + AVFilterContext *last_filter; + const AVFilter *abuffer_filt = avfilter_get_by_name("abuffer"); + AVBPrint args; + char name[255]; + int ret, pad_idx = 0; + + av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC); + av_bprintf(&args, "time_base=%d/%d:sample_rate=%d:sample_fmt=%s", + ifp->time_base.num, ifp->time_base.den, + ifp->sample_rate, + av_get_sample_fmt_name(ifp->format)); + if (av_channel_layout_check(&ifp->ch_layout) && + ifp->ch_layout.order != AV_CHANNEL_ORDER_UNSPEC) { + av_bprintf(&args, ":channel_layout="); + av_channel_layout_describe_bprint(&ifp->ch_layout, &args); + } else + av_bprintf(&args, ":channels=%d", ifp->ch_layout.nb_channels); + snprintf(name, sizeof(name), "graph_%d_in_%s", fg->index, ifp->opts.name); + + if ((ret = avfilter_graph_create_filter(&ifp->filter, abuffer_filt, + name, args.str, NULL, + graph)) < 0) + return ret; + last_filter = ifp->filter; + + snprintf(name, sizeof(name), "trim for input stream %s", ifp->opts.name); + ret = insert_trim(ifp->opts.trim_start_us, ifp->opts.trim_end_us, + &last_filter, &pad_idx, name); + if (ret < 0) + return ret; + + if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) + return ret; + + return 0; +} + +static int configure_input_filter(FilterGraph *fg, AVFilterGraph *graph, + InputFilter *ifilter, AVFilterInOut *in) +{ + switch (ifp_from_ifilter(ifilter)->type) { + case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, graph, ifilter, in); + case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, graph, ifilter, in); + default: av_assert0(0); return 0; + } +} + +static void cleanup_filtergraph(FilterGraph *fg, FilterGraphThread *fgt) +{ + for (int i = 0; i < fg->nb_outputs; i++) + ofp_from_ofilter(fg->outputs[i])->filter = NULL; + for (int i = 0; i < fg->nb_inputs; i++) + ifp_from_ifilter(fg->inputs[i])->filter = NULL; + avfilter_graph_free(&fgt->graph); +} + +static int filter_is_buffersrc(const AVFilterContext *f) +{ + return f->nb_inputs == 0 && + (!strcmp(f->filter->name, "buffer") || + !strcmp(f->filter->name, "abuffer")); +} + +static int graph_is_meta(AVFilterGraph *graph) +{ + for (unsigned i = 0; i < graph->nb_filters; i++) { + const AVFilterContext *f = graph->filters[i]; + + /* in addition to filters flagged as meta, also + * disregard sinks and buffersources (but not other sources, + * since they introduce data we are not aware of) + */ + if (!((f->filter->flags & AVFILTER_FLAG_METADATA_ONLY) || + f->nb_outputs == 0 || + filter_is_buffersrc(f))) + return 0; + } + return 1; +} + +static int sub2video_frame(InputFilter *ifilter, AVFrame *frame, int buffer); + +static int configure_filtergraph(FilterGraph *fg, FilterGraphThread *fgt) +{ + FilterGraphPriv *fgp = fgp_from_fg(fg); + AVBufferRef *hw_device; + AVFilterInOut *inputs, *outputs, *cur; + int ret, i, simple = filtergraph_is_simple(fg); + int have_input_eof = 0; + const char *graph_desc = fgp->graph_desc; + + cleanup_filtergraph(fg, fgt); + fgt->graph = avfilter_graph_alloc(); + if (!fgt->graph) + return AVERROR(ENOMEM); + + if (simple) { + OutputStream *ost = fg->outputs[0]->ost; + + if (filter_nbthreads) { + ret = av_opt_set(fgt->graph, "threads", filter_nbthreads, 0); + if (ret < 0) + goto fail; + } else { + const AVDictionaryEntry *e = NULL; + e = av_dict_get(ost->encoder_opts, "threads", NULL, 0); + if (e) + av_opt_set(fgt->graph, "threads", e->value, 0); + } + + if (av_dict_count(ost->sws_dict)) { + ret = av_dict_get_string(ost->sws_dict, + &fgt->graph->scale_sws_opts, + '=', ':'); + if (ret < 0) + goto fail; + } + + if (av_dict_count(ost->swr_opts)) { + char *args; + ret = av_dict_get_string(ost->swr_opts, &args, '=', ':'); + if (ret < 0) + goto fail; + av_opt_set(fgt->graph, "aresample_swr_opts", args, 0); + av_free(args); + } + } else { + fgt->graph->nb_threads = filter_complex_nbthreads; + } + + hw_device = hw_device_for_filter(); + + if ((ret = graph_parse(fgt->graph, graph_desc, &inputs, &outputs, hw_device)) < 0) + goto fail; + + for (cur = inputs, i = 0; cur; cur = cur->next, i++) + if ((ret = configure_input_filter(fg, fgt->graph, fg->inputs[i], cur)) < 0) { + avfilter_inout_free(&inputs); + avfilter_inout_free(&outputs); + goto fail; + } + avfilter_inout_free(&inputs); + + for (cur = outputs, i = 0; cur; cur = cur->next, i++) { + ret = configure_output_filter(fg, fgt->graph, fg->outputs[i], cur); + if (ret < 0) { + avfilter_inout_free(&outputs); + goto fail; + } + } + avfilter_inout_free(&outputs); + + if (fgp->disable_conversions) + avfilter_graph_set_auto_convert(fgt->graph, AVFILTER_AUTO_CONVERT_NONE); + if ((ret = avfilter_graph_config(fgt->graph, NULL)) < 0) + goto fail; + + fgp->is_meta = graph_is_meta(fgt->graph); + + /* limit the lists of allowed formats to the ones selected, to + * make sure they stay the same if the filtergraph is reconfigured later */ + for (int i = 0; i < fg->nb_outputs; i++) { + OutputFilter *ofilter = fg->outputs[i]; + OutputFilterPriv *ofp = ofp_from_ofilter(ofilter); + AVFilterContext *sink = ofp->filter; + + ofp->format = av_buffersink_get_format(sink); + + ofp->width = av_buffersink_get_w(sink); + ofp->height = av_buffersink_get_h(sink); + + // If the timing parameters are not locked yet, get the tentative values + // here but don't lock them. They will only be used if no output frames + // are ever produced. + if (!ofp->tb_out_locked) { + AVRational fr = av_buffersink_get_frame_rate(sink); + if (ofp->fps.framerate.num <= 0 && ofp->fps.framerate.den <= 0 && + fr.num > 0 && fr.den > 0) + ofp->fps.framerate = fr; + ofp->tb_out = av_buffersink_get_time_base(sink); + } + ofp->sample_aspect_ratio = av_buffersink_get_sample_aspect_ratio(sink); + + ofp->sample_rate = av_buffersink_get_sample_rate(sink); + av_channel_layout_uninit(&ofp->ch_layout); + ret = av_buffersink_get_ch_layout(sink, &ofp->ch_layout); + if (ret < 0) + goto fail; + } + + for (int i = 0; i < fg->nb_inputs; i++) { + InputFilterPriv *ifp = ifp_from_ifilter(fg->inputs[i]); + AVFrame *tmp; + while (av_fifo_read(ifp->frame_queue, &tmp, 1) >= 0) { + if (ifp->type_src == AVMEDIA_TYPE_SUBTITLE) { + sub2video_frame(&ifp->ifilter, tmp, !fgt->graph); + } else { + ret = av_buffersrc_add_frame(ifp->filter, tmp); + } + av_frame_free(&tmp); + if (ret < 0) + goto fail; + } + } + + /* send the EOFs for the finished inputs */ + for (int i = 0; i < fg->nb_inputs; i++) { + InputFilterPriv *ifp = ifp_from_ifilter(fg->inputs[i]); + if (fgt->eof_in[i]) { + ret = av_buffersrc_add_frame(ifp->filter, NULL); + if (ret < 0) + goto fail; + have_input_eof = 1; + } + } + + if (have_input_eof) { + // make sure the EOF propagates to the end of the graph + ret = avfilter_graph_request_oldest(fgt->graph); + if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) + goto fail; + } + + return 0; +fail: + cleanup_filtergraph(fg, fgt); + return ret; +} + +static int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame) +{ + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + AVFrameSideData *sd; + int ret; + + ret = av_buffer_replace(&ifp->hw_frames_ctx, frame->hw_frames_ctx); + if (ret < 0) + return ret; + + ifp->time_base = (ifp->type == AVMEDIA_TYPE_AUDIO) ? (AVRational){ 1, frame->sample_rate } : + (ifp->opts.flags & IFILTER_FLAG_CFR) ? av_inv_q(ifp->opts.framerate) : + frame->time_base; + + ifp->format = frame->format; + + ifp->width = frame->width; + ifp->height = frame->height; + ifp->sample_aspect_ratio = frame->sample_aspect_ratio; + ifp->color_space = frame->colorspace; + ifp->color_range = frame->color_range; + + ifp->sample_rate = frame->sample_rate; + ret = av_channel_layout_copy(&ifp->ch_layout, &frame->ch_layout); + if (ret < 0) + return ret; + + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX); + if (sd) + memcpy(ifp->displaymatrix, sd->data, sizeof(ifp->displaymatrix)); + ifp->displaymatrix_present = !!sd; + + return 0; +} + +int filtergraph_is_simple(const FilterGraph *fg) +{ + const FilterGraphPriv *fgp = cfgp_from_cfg(fg); + return fgp->is_simple; +} + +static void send_command(FilterGraph *fg, AVFilterGraph *graph, + double time, const char *target, + const char *command, const char *arg, int all_filters) +{ + int ret; + + if (!graph) + return; + + if (time < 0) { + char response[4096]; + ret = avfilter_graph_send_command(graph, target, command, arg, + response, sizeof(response), + all_filters ? 0 : AVFILTER_CMD_FLAG_ONE); + fprintf(stderr, "Command reply for stream %d: ret:%d res:\n%s", + fg->index, ret, response); + } else if (!all_filters) { + fprintf(stderr, "Queuing commands only on filters supporting the specific command is unsupported\n"); + } else { + ret = avfilter_graph_queue_command(graph, target, command, arg, 0, time); + if (ret < 0) + fprintf(stderr, "Queuing command failed with error %s\n", av_err2str(ret)); + } +} + +static int choose_input(const FilterGraph *fg, const FilterGraphThread *fgt) +{ + int nb_requests, nb_requests_max = -1; + int best_input = -1; + + for (int i = 0; i < fg->nb_inputs; i++) { + InputFilter *ifilter = fg->inputs[i]; + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + + if (fgt->eof_in[i]) + continue; + + nb_requests = av_buffersrc_get_nb_failed_requests(ifp->filter); + if (nb_requests > nb_requests_max) { + nb_requests_max = nb_requests; + best_input = i; + } + } + + av_assert0(best_input >= 0); + + return best_input; +} + +static int choose_out_timebase(OutputFilterPriv *ofp, AVFrame *frame) +{ + OutputFilter *ofilter = &ofp->ofilter; + FPSConvContext *fps = &ofp->fps; + AVRational tb = (AVRational){ 0, 0 }; + AVRational fr; + const FrameData *fd; + + fd = frame_data_c(frame); + + // apply -enc_time_base + if (ofp->enc_timebase.num == ENC_TIME_BASE_DEMUX && + (fd->dec.tb.num <= 0 || fd->dec.tb.den <= 0)) { + av_log(ofilter->ost, AV_LOG_ERROR, + "Demuxing timebase not available - cannot use it for encoding\n"); + return AVERROR(EINVAL); + } + + switch (ofp->enc_timebase.num) { + case 0: break; + case ENC_TIME_BASE_DEMUX: tb = fd->dec.tb; break; + case ENC_TIME_BASE_FILTER: tb = frame->time_base; break; + default: tb = ofp->enc_timebase; break; + } + + if (ofilter->type == AVMEDIA_TYPE_AUDIO) { + tb = tb.num ? tb : (AVRational){ 1, frame->sample_rate }; + goto finish; + } + + fr = fps->framerate; + if (!fr.num) { + AVRational fr_sink = av_buffersink_get_frame_rate(ofp->filter); + if (fr_sink.num > 0 && fr_sink.den > 0) + fr = fr_sink; + } + + if (ofilter->ost->is_cfr) { + if (!fr.num && !fps->framerate_max.num) { + fr = (AVRational){25, 1}; + av_log(ofilter->ost, AV_LOG_WARNING, + "No information " + "about the input framerate is available. Falling " + "back to a default value of 25fps. Use the -r option " + "if you want a different framerate.\n"); + } + + if (fps->framerate_max.num && + (av_q2d(fr) > av_q2d(fps->framerate_max) || + !fr.den)) + fr = fps->framerate_max; + } + + if (fr.num > 0) { + if (fps->framerate_supported) { + int idx = av_find_nearest_q_idx(fr, fps->framerate_supported); + fr = fps->framerate_supported[idx]; + } + if (fps->framerate_clip) { + av_reduce(&fr.num, &fr.den, + fr.num, fr.den, fps->framerate_clip); + } + } + + if (!(tb.num > 0 && tb.den > 0)) + tb = av_inv_q(fr); + if (!(tb.num > 0 && tb.den > 0)) + tb = frame->time_base; + + fps->framerate = fr; +finish: + ofp->tb_out = tb; + ofp->tb_out_locked = 1; + + return 0; +} + +static double adjust_frame_pts_to_encoder_tb(AVFrame *frame, AVRational tb_dst, + int64_t start_time) +{ + double float_pts = AV_NOPTS_VALUE; // this is identical to frame.pts but with higher precision + + AVRational tb = tb_dst; + AVRational filter_tb = frame->time_base; + const int extra_bits = av_clip(29 - av_log2(tb.den), 0, 16); + + if (frame->pts == AV_NOPTS_VALUE) + goto early_exit; + + tb.den <<= extra_bits; + float_pts = av_rescale_q(frame->pts, filter_tb, tb) - + av_rescale_q(start_time, AV_TIME_BASE_Q, tb); + float_pts /= 1 << extra_bits; + // when float_pts is not exactly an integer, + // avoid exact midpoints to reduce the chance of rounding differences, this + // can be removed in case the fps code is changed to work with integers + if (float_pts != llrint(float_pts)) + float_pts += FFSIGN(float_pts) * 1.0 / (1<<17); + + frame->pts = av_rescale_q(frame->pts, filter_tb, tb_dst) - + av_rescale_q(start_time, AV_TIME_BASE_Q, tb_dst); + frame->time_base = tb_dst; + +early_exit: + + if (debug_ts) { + av_log(NULL, AV_LOG_INFO, "filter -> pts:%s pts_time:%s exact:%f time_base:%d/%d\n", + frame ? av_ts2str(frame->pts) : "NULL", + av_ts2timestr(frame->pts, &tb_dst), + float_pts, tb_dst.num, tb_dst.den); + } + + return float_pts; +} + +/* Convert frame timestamps to the encoder timebase and decide how many times + * should this (and possibly previous) frame be repeated in order to conform to + * desired target framerate (if any). + */ +static void video_sync_process(OutputFilterPriv *ofp, AVFrame *frame, + int64_t *nb_frames, int64_t *nb_frames_prev) +{ + OutputFilter *ofilter = &ofp->ofilter; + OutputStream *ost = ofilter->ost; + FPSConvContext *fps = &ofp->fps; + double delta0, delta, sync_ipts, duration; + + if (!frame) { + *nb_frames_prev = *nb_frames = mid_pred(fps->frames_prev_hist[0], + fps->frames_prev_hist[1], + fps->frames_prev_hist[2]); + + if (!*nb_frames && fps->last_dropped) { + atomic_fetch_add(&ofilter->nb_frames_drop, 1); + fps->last_dropped++; + } + + goto finish; + } + + duration = frame->duration * av_q2d(frame->time_base) / av_q2d(ofp->tb_out); + + sync_ipts = adjust_frame_pts_to_encoder_tb(frame, ofp->tb_out, ofp->ts_offset); + /* delta0 is the "drift" between the input frame and + * where it would fall in the output. */ + delta0 = sync_ipts - ofp->next_pts; + delta = delta0 + duration; + + // tracks the number of times the PREVIOUS frame should be duplicated, + // mostly for variable framerate (VFR) + *nb_frames_prev = 0; + /* by default, we output a single frame */ + *nb_frames = 1; + + if (delta0 < 0 && + delta > 0 && + ost->vsync_method != VSYNC_PASSTHROUGH +#if FFMPEG_OPT_VSYNC_DROP + && ost->vsync_method != VSYNC_DROP +#endif + ) { + if (delta0 < -0.6) { + av_log(ost, AV_LOG_VERBOSE, "Past duration %f too large\n", -delta0); + } else + av_log(ost, AV_LOG_DEBUG, "Clipping frame in rate conversion by %f\n", -delta0); + sync_ipts = ofp->next_pts; + duration += delta0; + delta0 = 0; + } + + switch (ost->vsync_method) { + case VSYNC_VSCFR: + if (fps->frame_number == 0 && delta0 >= 0.5) { + av_log(ost, AV_LOG_DEBUG, "Not duplicating %d initial frames\n", (int)lrintf(delta0)); + delta = duration; + delta0 = 0; + ofp->next_pts = llrint(sync_ipts); + } + case VSYNC_CFR: + // FIXME set to 0.5 after we fix some dts/pts bugs like in avidec.c + if (frame_drop_threshold && delta < frame_drop_threshold && fps->frame_number) { + *nb_frames = 0; + } else if (delta < -1.1) + *nb_frames = 0; + else if (delta > 1.1) { + *nb_frames = llrintf(delta); + if (delta0 > 1.1) + *nb_frames_prev = llrintf(delta0 - 0.6); + } + frame->duration = 1; + break; + case VSYNC_VFR: + if (delta <= -0.6) + *nb_frames = 0; + else if (delta > 0.6) + ofp->next_pts = llrint(sync_ipts); + frame->duration = llrint(duration); + break; +#if FFMPEG_OPT_VSYNC_DROP + case VSYNC_DROP: +#endif + case VSYNC_PASSTHROUGH: + ofp->next_pts = llrint(sync_ipts); + frame->duration = llrint(duration); + break; + default: + av_assert0(0); + } + +finish: + memmove(fps->frames_prev_hist + 1, + fps->frames_prev_hist, + sizeof(fps->frames_prev_hist[0]) * (FF_ARRAY_ELEMS(fps->frames_prev_hist) - 1)); + fps->frames_prev_hist[0] = *nb_frames_prev; + + if (*nb_frames_prev == 0 && fps->last_dropped) { + atomic_fetch_add(&ofilter->nb_frames_drop, 1); + av_log(ost, AV_LOG_VERBOSE, + "*** dropping frame %"PRId64" at ts %"PRId64"\n", + fps->frame_number, fps->last_frame->pts); + } + if (*nb_frames > (*nb_frames_prev && fps->last_dropped) + (*nb_frames > *nb_frames_prev)) { + uint64_t nb_frames_dup; + if (*nb_frames > dts_error_threshold * 30) { + av_log(ost, AV_LOG_ERROR, "%"PRId64" frame duplication too large, skipping\n", *nb_frames - 1); + atomic_fetch_add(&ofilter->nb_frames_drop, 1); + *nb_frames = 0; + return; + } + nb_frames_dup = atomic_fetch_add(&ofilter->nb_frames_dup, + *nb_frames - (*nb_frames_prev && fps->last_dropped) - (*nb_frames > *nb_frames_prev)); + av_log(ost, AV_LOG_VERBOSE, "*** %"PRId64" dup!\n", *nb_frames - 1); + if (nb_frames_dup > fps->dup_warning) { + av_log(ost, AV_LOG_WARNING, "More than %"PRIu64" frames duplicated\n", fps->dup_warning); + fps->dup_warning *= 10; + } + } + + fps->last_dropped = *nb_frames == *nb_frames_prev && frame; + fps->dropped_keyframe |= fps->last_dropped && (frame->flags & AV_FRAME_FLAG_KEY); +} + +static int close_output(OutputFilterPriv *ofp, FilterGraphThread *fgt) +{ + FilterGraphPriv *fgp = fgp_from_fg(ofp->ofilter.graph); + int ret; + + // we are finished and no frames were ever seen at this output, + // at least initialize the encoder with a dummy frame + if (!fgt->got_frame) { + AVFrame *frame = fgt->frame; + FrameData *fd; + + frame->time_base = ofp->tb_out; + frame->format = ofp->format; + + frame->width = ofp->width; + frame->height = ofp->height; + frame->sample_aspect_ratio = ofp->sample_aspect_ratio; + + frame->sample_rate = ofp->sample_rate; + if (ofp->ch_layout.nb_channels) { + ret = av_channel_layout_copy(&frame->ch_layout, &ofp->ch_layout); + if (ret < 0) + return ret; + } + + fd = frame_data(frame); + if (!fd) + return AVERROR(ENOMEM); + + fd->frame_rate_filter = ofp->fps.framerate; + + av_assert0(!frame->buf[0]); + + av_log(ofp->ofilter.ost, AV_LOG_WARNING, + "No filtered frames for output stream, trying to " + "initialize anyway.\n"); + + ret = sch_filter_send(fgp->sch, fgp->sch_idx, ofp->index, frame); + if (ret < 0) { + av_frame_unref(frame); + return ret; + } + } + + fgt->eof_out[ofp->index] = 1; + + ret = sch_filter_send(fgp->sch, fgp->sch_idx, ofp->index, NULL); + return (ret == AVERROR_EOF) ? 0 : ret; +} + +static int fg_output_frame(OutputFilterPriv *ofp, FilterGraphThread *fgt, + AVFrame *frame) +{ + FilterGraphPriv *fgp = fgp_from_fg(ofp->ofilter.graph); + AVFrame *frame_prev = ofp->fps.last_frame; + enum AVMediaType type = ofp->ofilter.type; + + int64_t nb_frames = !!frame, nb_frames_prev = 0; + + if (type == AVMEDIA_TYPE_VIDEO && (frame || fgt->got_frame)) + video_sync_process(ofp, frame, &nb_frames, &nb_frames_prev); + + for (int64_t i = 0; i < nb_frames; i++) { + AVFrame *frame_out; + int ret; + + if (type == AVMEDIA_TYPE_VIDEO) { + AVFrame *frame_in = (i < nb_frames_prev && frame_prev->buf[0]) ? + frame_prev : frame; + if (!frame_in) + break; + + frame_out = fgp->frame_enc; + ret = av_frame_ref(frame_out, frame_in); + if (ret < 0) + return ret; + + frame_out->pts = ofp->next_pts; + + if (ofp->fps.dropped_keyframe) { + frame_out->flags |= AV_FRAME_FLAG_KEY; + ofp->fps.dropped_keyframe = 0; + } + } else { + frame->pts = (frame->pts == AV_NOPTS_VALUE) ? ofp->next_pts : + av_rescale_q(frame->pts, frame->time_base, ofp->tb_out) - + av_rescale_q(ofp->ts_offset, AV_TIME_BASE_Q, ofp->tb_out); + + frame->time_base = ofp->tb_out; + frame->duration = av_rescale_q(frame->nb_samples, + (AVRational){ 1, frame->sample_rate }, + ofp->tb_out); + + ofp->next_pts = frame->pts + frame->duration; + + frame_out = frame; + } + + // send the frame to consumers + ret = sch_filter_send(fgp->sch, fgp->sch_idx, ofp->index, frame_out); + if (ret < 0) { + av_frame_unref(frame_out); + + if (!fgt->eof_out[ofp->index]) { + fgt->eof_out[ofp->index] = 1; + fgp->nb_outputs_done++; + } + + return ret == AVERROR_EOF ? 0 : ret; + } + + if (type == AVMEDIA_TYPE_VIDEO) { + ofp->fps.frame_number++; + ofp->next_pts++; + + if (i == nb_frames_prev && frame) + frame->flags &= ~AV_FRAME_FLAG_KEY; + } + + fgt->got_frame = 1; + } + + if (frame && frame_prev) { + av_frame_unref(frame_prev); + av_frame_move_ref(frame_prev, frame); + } + + if (!frame) + return close_output(ofp, fgt); + + return 0; +} + +static int fg_output_step(OutputFilterPriv *ofp, FilterGraphThread *fgt, + AVFrame *frame) +{ + FilterGraphPriv *fgp = fgp_from_fg(ofp->ofilter.graph); + OutputStream *ost = ofp->ofilter.ost; + AVFilterContext *filter = ofp->filter; + FrameData *fd; + int ret; + + ret = av_buffersink_get_frame_flags(filter, frame, + AV_BUFFERSINK_FLAG_NO_REQUEST); + if (ret == AVERROR_EOF && !fgt->eof_out[ofp->index]) { + ret = fg_output_frame(ofp, fgt, NULL); + return (ret < 0) ? ret : 1; + } else if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + return 1; + } else if (ret < 0) { + av_log(fgp, AV_LOG_WARNING, + "Error in retrieving a frame from the filtergraph: %s\n", + av_err2str(ret)); + return ret; + } + + if (fgt->eof_out[ofp->index]) { + av_frame_unref(frame); + return 0; + } + + frame->time_base = av_buffersink_get_time_base(filter); + + if (debug_ts) + av_log(fgp, AV_LOG_INFO, "filter_raw -> pts:%s pts_time:%s time_base:%d/%d\n", + av_ts2str(frame->pts), av_ts2timestr(frame->pts, &frame->time_base), + frame->time_base.num, frame->time_base.den); + + // Choose the output timebase the first time we get a frame. + if (!ofp->tb_out_locked) { + ret = choose_out_timebase(ofp, frame); + if (ret < 0) { + av_log(ost, AV_LOG_ERROR, "Could not choose an output time base\n"); + av_frame_unref(frame); + return ret; + } + } + + fd = frame_data(frame); + if (!fd) { + av_frame_unref(frame); + return AVERROR(ENOMEM); + } + + fd->wallclock[LATENCY_PROBE_FILTER_POST] = av_gettime_relative(); + + // only use bits_per_raw_sample passed through from the decoder + // if the filtergraph did not touch the frame data + if (!fgp->is_meta) + fd->bits_per_raw_sample = 0; + + if (ost->type == AVMEDIA_TYPE_VIDEO) { + if (!frame->duration) { + AVRational fr = av_buffersink_get_frame_rate(filter); + if (fr.num > 0 && fr.den > 0) + frame->duration = av_rescale_q(1, av_inv_q(fr), frame->time_base); + } + + fd->frame_rate_filter = ofp->fps.framerate; + } + + ret = fg_output_frame(ofp, fgt, frame); + av_frame_unref(frame); + if (ret < 0) + return ret; + + return 0; +} + +/* retrieve all frames available at filtergraph outputs + * and send them to consumers */ +static int read_frames(FilterGraph *fg, FilterGraphThread *fgt, + AVFrame *frame) +{ + FilterGraphPriv *fgp = fgp_from_fg(fg); + int did_step = 0; + + // graph not configured, just select the input to request + if (!fgt->graph) { + for (int i = 0; i < fg->nb_inputs; i++) { + InputFilterPriv *ifp = ifp_from_ifilter(fg->inputs[i]); + if (ifp->format < 0 && !fgt->eof_in[i]) { + fgt->next_in = i; + return 0; + } + } + + // This state - graph is not configured, but all inputs are either + // initialized or EOF - should be unreachable because sending EOF to a + // filter without even a fallback format should fail + av_assert0(0); + return AVERROR_BUG; + } + + while (fgp->nb_outputs_done < fg->nb_outputs) { + int ret; + + ret = avfilter_graph_request_oldest(fgt->graph); + if (ret == AVERROR(EAGAIN)) { + fgt->next_in = choose_input(fg, fgt); + break; + } else if (ret < 0) { + if (ret == AVERROR_EOF) + av_log(fg, AV_LOG_VERBOSE, "Filtergraph returned EOF, finishing\n"); + else + av_log(fg, AV_LOG_ERROR, + "Error requesting a frame from the filtergraph: %s\n", + av_err2str(ret)); + return ret; + } + fgt->next_in = fg->nb_inputs; + + // return after one iteration, so that scheduler can rate-control us + if (did_step && fgp->have_sources) + return 0; + + /* Reap all buffers present in the buffer sinks */ + for (int i = 0; i < fg->nb_outputs; i++) { + OutputFilterPriv *ofp = ofp_from_ofilter(fg->outputs[i]); + + ret = 0; + while (!ret) { + ret = fg_output_step(ofp, fgt, frame); + if (ret < 0) + return ret; + } + } + did_step = 1; + } + + return (fgp->nb_outputs_done == fg->nb_outputs) ? AVERROR_EOF : 0; +} + +static void sub2video_heartbeat(InputFilter *ifilter, int64_t pts, AVRational tb) +{ + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + int64_t pts2; + + /* subtitles seem to be usually muxed ahead of other streams; + if not, subtracting a larger time here is necessary */ + pts2 = av_rescale_q(pts, tb, ifp->time_base) - 1; + + /* do not send the heartbeat frame if the subtitle is already ahead */ + if (pts2 <= ifp->sub2video.last_pts) + return; + + if (pts2 >= ifp->sub2video.end_pts || ifp->sub2video.initialize) + /* if we have hit the end of the current displayed subpicture, + or if we need to initialize the system, update the + overlayed subpicture and its start/end times */ + sub2video_update(ifp, pts2 + 1, NULL); + else + sub2video_push_ref(ifp, pts2); +} + +static int sub2video_frame(InputFilter *ifilter, AVFrame *frame, int buffer) +{ + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + int ret; + + if (buffer) { + AVFrame *tmp; + + if (!frame) + return 0; + + tmp = av_frame_alloc(); + if (!tmp) + return AVERROR(ENOMEM); + + av_frame_move_ref(tmp, frame); + + ret = av_fifo_write(ifp->frame_queue, &tmp, 1); + if (ret < 0) { + av_frame_free(&tmp); + return ret; + } + + return 0; + } + + // heartbeat frame + if (frame && !frame->buf[0]) { + sub2video_heartbeat(ifilter, frame->pts, frame->time_base); + return 0; + } + + if (!frame) { + if (ifp->sub2video.end_pts < INT64_MAX) + sub2video_update(ifp, INT64_MAX, NULL); + + return av_buffersrc_add_frame(ifp->filter, NULL); + } + + ifp->width = frame->width ? frame->width : ifp->width; + ifp->height = frame->height ? frame->height : ifp->height; + + sub2video_update(ifp, INT64_MIN, (const AVSubtitle*)frame->buf[0]->data); + + return 0; +} + +static int send_eof(FilterGraphThread *fgt, InputFilter *ifilter, + int64_t pts, AVRational tb) +{ + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + int ret; + + if (fgt->eof_in[ifp->index]) + return 0; + + fgt->eof_in[ifp->index] = 1; + + if (ifp->filter) { + pts = av_rescale_q_rnd(pts, tb, ifp->time_base, + AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); + + ret = av_buffersrc_close(ifp->filter, pts, AV_BUFFERSRC_FLAG_PUSH); + if (ret < 0) + return ret; + } else { + if (ifp->format < 0) { + // the filtergraph was never configured, use the fallback parameters + ifp->format = ifp->opts.fallback->format; + ifp->sample_rate = ifp->opts.fallback->sample_rate; + ifp->width = ifp->opts.fallback->width; + ifp->height = ifp->opts.fallback->height; + ifp->sample_aspect_ratio = ifp->opts.fallback->sample_aspect_ratio; + ifp->color_space = ifp->opts.fallback->colorspace; + ifp->color_range = ifp->opts.fallback->color_range; + ifp->time_base = ifp->opts.fallback->time_base; + + ret = av_channel_layout_copy(&ifp->ch_layout, + &ifp->opts.fallback->ch_layout); + if (ret < 0) + return ret; + + if (ifilter_has_all_input_formats(ifilter->graph)) { + ret = configure_filtergraph(ifilter->graph, fgt); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Error initializing filters!\n"); + return ret; + } } } - } -} -static int sub2video_prepare(InputStream *ist, InputFilter *ifilter) -{ - AVFormatContext *avf = input_files[ist->file_index]->ctx; - int i, w, h; - - /* Compute the size of the canvas for the subtitles stream. - If the subtitles codecpar has set a size, use it. Otherwise use the - maximum dimensions of the video streams in the same file. */ - w = ifilter->width; - h = ifilter->height; - if (!(w && h)) { - for (i = 0; i < avf->nb_streams; i++) { - if (avf->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - w = FFMAX(w, avf->streams[i]->codecpar->width); - h = FFMAX(h, avf->streams[i]->codecpar->height); - } + if (ifp->format < 0) { + av_log(NULL, AV_LOG_ERROR, + "Cannot determine format of input %s after EOF\n", + ifp->opts.name); + return AVERROR_INVALIDDATA; } - if (!(w && h)) { - w = FFMAX(w, 720); - h = FFMAX(h, 576); - } - av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h); } - ist->sub2video.w = ifilter->width = w; - ist->sub2video.h = ifilter->height = h; - - ifilter->width = ist->dec_ctx->width ? ist->dec_ctx->width : ist->sub2video.w; - ifilter->height = ist->dec_ctx->height ? ist->dec_ctx->height : ist->sub2video.h; - /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the - palettes for all rectangles are identical or compatible */ - ifilter->format = AV_PIX_FMT_RGB32; - - ist->sub2video.frame = av_frame_alloc(); - if (!ist->sub2video.frame) - return AVERROR(ENOMEM); - ist->sub2video.last_pts = INT64_MIN; - ist->sub2video.end_pts = INT64_MIN; + return 0; +} - /* sub2video structure has been (re-)initialized. - Mark it as such so that the system will be - initialized with the first received heartbeat. */ - ist->sub2video.initialize = 1; +enum ReinitReason { + VIDEO_CHANGED = (1 << 0), + AUDIO_CHANGED = (1 << 1), + MATRIX_CHANGED = (1 << 2), + HWACCEL_CHANGED = (1 << 3) +}; - return 0; +static const char *unknown_if_null(const char *str) +{ + return str ? str : "unknown"; } -static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, - AVFilterInOut *in) +static int send_frame(FilterGraph *fg, FilterGraphThread *fgt, + InputFilter *ifilter, AVFrame *frame) { - AVFilterContext *last_filter; - const AVFilter *buffer_filt = avfilter_get_by_name("buffer"); - const AVPixFmtDescriptor *desc; - InputStream *ist = ifilter->ist; - InputFile *f = input_files[ist->file_index]; - AVRational tb = ist->framerate.num ? av_inv_q(ist->framerate) : - ist->st->time_base; - AVRational fr = ist->framerate; - AVRational sar; - AVBPrint args; - char name[255]; - int ret, pad_idx = 0; - int64_t tsoffset = 0; - AVBufferSrcParameters *par = av_buffersrc_parameters_alloc(); + InputFilterPriv *ifp = ifp_from_ifilter(ifilter); + FrameData *fd; + AVFrameSideData *sd; + int need_reinit = 0, ret; + + /* determine if the parameters for this input changed */ + switch (ifp->type) { + case AVMEDIA_TYPE_AUDIO: + if (ifp->format != frame->format || + ifp->sample_rate != frame->sample_rate || + av_channel_layout_compare(&ifp->ch_layout, &frame->ch_layout)) + need_reinit |= AUDIO_CHANGED; + break; + case AVMEDIA_TYPE_VIDEO: + if (ifp->format != frame->format || + ifp->width != frame->width || + ifp->height != frame->height || + ifp->color_space != frame->colorspace || + ifp->color_range != frame->color_range) + need_reinit |= VIDEO_CHANGED; + break; + } - if (!par) - return AVERROR(ENOMEM); - memset(par, 0, sizeof(*par)); - par->format = AV_PIX_FMT_NONE; + if (sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX)) { + if (!ifp->displaymatrix_present || + memcmp(sd->data, ifp->displaymatrix, sizeof(ifp->displaymatrix))) + need_reinit |= MATRIX_CHANGED; + } else if (ifp->displaymatrix_present) + need_reinit |= MATRIX_CHANGED; - if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) { - av_log(NULL, AV_LOG_ERROR, "Cannot connect video filter to audio input\n"); - ret = AVERROR(EINVAL); - goto fail; - } + if (!(ifp->opts.flags & IFILTER_FLAG_REINIT) && fgt->graph) + need_reinit = 0; - if (!fr.num) - fr = ist->framerate_guessed; + if (!!ifp->hw_frames_ctx != !!frame->hw_frames_ctx || + (ifp->hw_frames_ctx && ifp->hw_frames_ctx->data != frame->hw_frames_ctx->data)) + need_reinit |= HWACCEL_CHANGED; - if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) { - ret = sub2video_prepare(ist, ifilter); + if (need_reinit) { + ret = ifilter_parameters_from_frame(ifilter, frame); if (ret < 0) - goto fail; + return ret; } - sar = ifilter->sample_aspect_ratio; - if(!sar.den) - sar = (AVRational){0,1}; - av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC); - av_bprintf(&args, - "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:" - "pixel_aspect=%d/%d", - ifilter->width, ifilter->height, ifilter->format, - tb.num, tb.den, sar.num, sar.den); - if (fr.num && fr.den) - av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den); - snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index, - ist->file_index, ist->st->index); + /* (re)init the graph if possible, otherwise buffer the frame and return */ + if (need_reinit || !fgt->graph) { + AVFrame *tmp = av_frame_alloc(); + if (!tmp) + return AVERROR(ENOMEM); - if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name, - args.str, NULL, fg->graph)) < 0) - goto fail; - par->hw_frames_ctx = ifilter->hw_frames_ctx; - ret = av_buffersrc_parameters_set(ifilter->filter, par); - if (ret < 0) - goto fail; - av_freep(&par); - last_filter = ifilter->filter; + if (!ifilter_has_all_input_formats(fg)) { + av_frame_move_ref(tmp, frame); - desc = av_pix_fmt_desc_get(ifilter->format); - av_assert0(desc); + ret = av_fifo_write(ifp->frame_queue, &tmp, 1); + if (ret < 0) + av_frame_free(&tmp); - // TODO: insert hwaccel enabled filters like transpose_vaapi into the graph - if (ist->autorotate && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) { - int32_t *displaymatrix = ifilter->displaymatrix; - double theta; + return ret; + } - if (!displaymatrix) - displaymatrix = (int32_t *)av_stream_get_side_data(ist->st, AV_PKT_DATA_DISPLAYMATRIX, NULL); - theta = get_rotation(displaymatrix); + ret = fgt->graph ? read_frames(fg, fgt, tmp) : 0; + av_frame_free(&tmp); + if (ret < 0) + return ret; - if (fabs(theta - 90) < 1.0) { - ret = insert_filter(&last_filter, &pad_idx, "transpose", - displaymatrix[3] > 0 ? "cclock_flip" : "clock"); - } else if (fabs(theta - 180) < 1.0) { - if (displaymatrix[0] < 0) { - ret = insert_filter(&last_filter, &pad_idx, "hflip", NULL); - if (ret < 0) - return ret; - } - if (displaymatrix[4] < 0) { - ret = insert_filter(&last_filter, &pad_idx, "vflip", NULL); + if (fgt->graph) { + AVBPrint reason; + av_bprint_init(&reason, 0, AV_BPRINT_SIZE_AUTOMATIC); + if (need_reinit & AUDIO_CHANGED) { + const char *sample_format_name = av_get_sample_fmt_name(frame->format); + av_bprintf(&reason, "audio parameters changed to %d Hz, ", frame->sample_rate); + av_channel_layout_describe_bprint(&frame->ch_layout, &reason); + av_bprintf(&reason, ", %s, ", unknown_if_null(sample_format_name)); } - } else if (fabs(theta - 270) < 1.0) { - ret = insert_filter(&last_filter, &pad_idx, "transpose", - displaymatrix[3] < 0 ? "clock_flip" : "cclock"); - } else if (fabs(theta) > 1.0) { - char rotate_buf[64]; - snprintf(rotate_buf, sizeof(rotate_buf), "%f*PI/180", theta); - ret = insert_filter(&last_filter, &pad_idx, "rotate", rotate_buf); - } else if (fabs(theta) < 1.0) { - if (displaymatrix && displaymatrix[4] < 0) { - ret = insert_filter(&last_filter, &pad_idx, "vflip", NULL); + if (need_reinit & VIDEO_CHANGED) { + const char *pixel_format_name = av_get_pix_fmt_name(frame->format); + const char *color_space_name = av_color_space_name(frame->colorspace); + const char *color_range_name = av_color_range_name(frame->color_range); + av_bprintf(&reason, "video parameters changed to %s(%s, %s), %dx%d, ", + unknown_if_null(pixel_format_name), unknown_if_null(color_range_name), + unknown_if_null(color_space_name), frame->width, frame->height); } + if (need_reinit & MATRIX_CHANGED) + av_bprintf(&reason, "display matrix changed, "); + if (need_reinit & HWACCEL_CHANGED) + av_bprintf(&reason, "hwaccel changed, "); + if (reason.len > 1) + reason.str[reason.len - 2] = '\0'; // remove last comma + av_log(fg, AV_LOG_INFO, "Reconfiguring filter graph%s%s\n", reason.len ? " because " : "", reason.str); } - if (ret < 0) - return ret; - } - snprintf(name, sizeof(name), "trim_in_%d_%d", - ist->file_index, ist->st->index); - if (copy_ts) { - tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time; - if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE) - tsoffset += f->ctx->start_time; + ret = configure_filtergraph(fg, fgt); + if (ret < 0) { + av_log(fg, AV_LOG_ERROR, "Error reinitializing filters!\n"); + return ret; + } } - ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ? - AV_NOPTS_VALUE : tsoffset, f->recording_time, - &last_filter, &pad_idx, name); - if (ret < 0) - return ret; - - if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) - return ret; - return 0; -fail: - av_freep(&par); - - return ret; -} -static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter, - AVFilterInOut *in) -{ - AVFilterContext *last_filter; - const AVFilter *abuffer_filt = avfilter_get_by_name("abuffer"); - InputStream *ist = ifilter->ist; - InputFile *f = input_files[ist->file_index]; - AVBPrint args; - char name[255]; - int ret, pad_idx = 0; - int64_t tsoffset = 0; - - if (ist->dec_ctx->codec_type != AVMEDIA_TYPE_AUDIO) { - av_log(NULL, AV_LOG_ERROR, "Cannot connect audio filter to non audio input\n"); - return AVERROR(EINVAL); - } + frame->pts = av_rescale_q(frame->pts, frame->time_base, ifp->time_base); + frame->duration = av_rescale_q(frame->duration, frame->time_base, ifp->time_base); + frame->time_base = ifp->time_base; - av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC); - av_bprintf(&args, "time_base=%d/%d:sample_rate=%d:sample_fmt=%s", - 1, ifilter->sample_rate, - ifilter->sample_rate, - av_get_sample_fmt_name(ifilter->format)); - if (av_channel_layout_check(&ifilter->ch_layout) && - ifilter->ch_layout.order != AV_CHANNEL_ORDER_UNSPEC) { - av_bprintf(&args, ":channel_layout="); - av_channel_layout_describe_bprint(&ifilter->ch_layout, &args); - } else - av_bprintf(&args, ":channels=%d", ifilter->ch_layout.nb_channels); - snprintf(name, sizeof(name), "graph_%d_in_%d_%d", fg->index, - ist->file_index, ist->st->index); + fd = frame_data(frame); + if (!fd) + return AVERROR(ENOMEM); + fd->wallclock[LATENCY_PROBE_FILTER_PRE] = av_gettime_relative(); - if ((ret = avfilter_graph_create_filter(&ifilter->filter, abuffer_filt, - name, args.str, NULL, - fg->graph)) < 0) + ret = av_buffersrc_add_frame_flags(ifp->filter, frame, + AV_BUFFERSRC_FLAG_PUSH); + if (ret < 0) { + av_frame_unref(frame); + if (ret != AVERROR_EOF) + av_log(fg, AV_LOG_ERROR, "Error while filtering: %s\n", av_err2str(ret)); return ret; - last_filter = ifilter->filter; - -#define AUTO_INSERT_FILTER_INPUT(opt_name, filter_name, arg) do { \ - AVFilterContext *filt_ctx; \ - \ - av_log(NULL, AV_LOG_INFO, opt_name " is forwarded to lavfi " \ - "similarly to -af " filter_name "=%s.\n", arg); \ - \ - snprintf(name, sizeof(name), "graph_%d_%s_in_%d_%d", \ - fg->index, filter_name, ist->file_index, ist->st->index); \ - ret = avfilter_graph_create_filter(&filt_ctx, \ - avfilter_get_by_name(filter_name), \ - name, arg, NULL, fg->graph); \ - if (ret < 0) \ - return ret; \ - \ - ret = avfilter_link(last_filter, 0, filt_ctx, 0); \ - if (ret < 0) \ - return ret; \ - \ - last_filter = filt_ctx; \ -} while (0) - - snprintf(name, sizeof(name), "trim for input stream %d:%d", - ist->file_index, ist->st->index); - if (copy_ts) { - tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time; - if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE) - tsoffset += f->ctx->start_time; } - ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ? - AV_NOPTS_VALUE : tsoffset, f->recording_time, - &last_filter, &pad_idx, name); - if (ret < 0) - return ret; - - if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0) - return ret; return 0; } -static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter, - AVFilterInOut *in) +static void fg_thread_set_name(const FilterGraph *fg) { - if (!ifilter->ist->dec) { - av_log(NULL, AV_LOG_ERROR, - "No decoder for stream #%d:%d, filtering impossible\n", - ifilter->ist->file_index, ifilter->ist->st->index); - return AVERROR_DECODER_NOT_FOUND; - } - switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) { - case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in); - case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in); - default: av_assert0(0); return 0; + char name[16]; + if (filtergraph_is_simple(fg)) { + OutputStream *ost = fg->outputs[0]->ost; + snprintf(name, sizeof(name), "%cf#%d:%d", + av_get_media_type_string(ost->type)[0], + ost->file->index, ost->index); + } else { + snprintf(name, sizeof(name), "fc%d", fg->index); } -} -static void cleanup_filtergraph(FilterGraph *fg) -{ - int i; - for (i = 0; i < fg->nb_outputs; i++) - fg->outputs[i]->filter = (AVFilterContext *)NULL; - for (i = 0; i < fg->nb_inputs; i++) - fg->inputs[i]->filter = (AVFilterContext *)NULL; - avfilter_graph_free(&fg->graph); + ff_thread_setname(name); } -static int filter_is_buffersrc(const AVFilterContext *f) +static void fg_thread_uninit(FilterGraphThread *fgt) { - return f->nb_inputs == 0 && - (!strcmp(f->filter->name, "buffer") || - !strcmp(f->filter->name, "abuffer")); -} + if (fgt->frame_queue_out) { + AVFrame *frame; + while (av_fifo_read(fgt->frame_queue_out, &frame, 1) >= 0) + av_frame_free(&frame); + av_fifo_freep2(&fgt->frame_queue_out); + } -static int graph_is_meta(AVFilterGraph *graph) -{ - for (unsigned i = 0; i < graph->nb_filters; i++) { - const AVFilterContext *f = graph->filters[i]; + av_frame_free(&fgt->frame); + av_freep(&fgt->eof_in); + av_freep(&fgt->eof_out); - /* in addition to filters flagged as meta, also - * disregard sinks and buffersources (but not other sources, - * since they introduce data we are not aware of) - */ - if (!((f->filter->flags & AVFILTER_FLAG_METADATA_ONLY) || - f->nb_outputs == 0 || - filter_is_buffersrc(f))) - return 0; - } - return 1; + avfilter_graph_free(&fgt->graph); + + memset(fgt, 0, sizeof(*fgt)); } -int configure_filtergraph(FilterGraph *fg) +static int fg_thread_init(FilterGraphThread *fgt, const FilterGraph *fg) { - AVFilterInOut *inputs, *outputs, *cur; - int ret, i, simple = filtergraph_is_simple(fg); - const char *graph_desc = simple ? fg->outputs[0]->ost->avfilter : - fg->graph_desc; + memset(fgt, 0, sizeof(*fgt)); - cleanup_filtergraph(fg); - if (!(fg->graph = avfilter_graph_alloc())) - return AVERROR(ENOMEM); + fgt->frame = av_frame_alloc(); + if (!fgt->frame) + goto fail; - if (simple) { - OutputStream *ost = fg->outputs[0]->ost; + fgt->eof_in = av_calloc(fg->nb_inputs, sizeof(*fgt->eof_in)); + if (!fgt->eof_in) + goto fail; - if (filter_nbthreads) { - ret = av_opt_set(fg->graph, "threads", filter_nbthreads, 0); - if (ret < 0) - goto fail; - } else { - const AVDictionaryEntry *e = NULL; - e = av_dict_get(ost->encoder_opts, "threads", NULL, 0); - if (e) - av_opt_set(fg->graph, "threads", e->value, 0); - } + fgt->eof_out = av_calloc(fg->nb_outputs, sizeof(*fgt->eof_out)); + if (!fgt->eof_out) + goto fail; - if (av_dict_count(ost->sws_dict)) { - ret = av_dict_get_string(ost->sws_dict, - &fg->graph->scale_sws_opts, - '=', ':'); - if (ret < 0) - goto fail; - } + fgt->frame_queue_out = av_fifo_alloc2(1, sizeof(AVFrame*), AV_FIFO_FLAG_AUTO_GROW); + if (!fgt->frame_queue_out) + goto fail; - if (av_dict_count(ost->swr_opts)) { - char *args; - ret = av_dict_get_string(ost->swr_opts, &args, '=', ':'); - if (ret < 0) - goto fail; - av_opt_set(fg->graph, "aresample_swr_opts", args, 0); - av_free(args); - } - } else { - fg->graph->nb_threads = filter_complex_nbthreads; - } + return 0; - if ((ret = graph_parse(fg->graph, graph_desc, &inputs, &outputs)) < 0) - goto fail; +fail: + fg_thread_uninit(fgt); + return AVERROR(ENOMEM); +} - ret = hw_device_setup_for_filter(fg); +static int filter_thread(void *arg) +{ + FilterGraphPriv *fgp = arg; + FilterGraph *fg = &fgp->fg; + + FilterGraphThread fgt; + int ret = 0, input_status = 0; + + ret = fg_thread_init(&fgt, fg); if (ret < 0) - goto fail; + goto finish; - if (simple && (!inputs || inputs->next || !outputs || outputs->next)) { - const char *num_inputs; - const char *num_outputs; - if (!outputs) { - num_outputs = "0"; - } else if (outputs->next) { - num_outputs = ">1"; - } else { - num_outputs = "1"; + fg_thread_set_name(fg); + + // if we have all input parameters the graph can now be configured + if (ifilter_has_all_input_formats(fg)) { + ret = configure_filtergraph(fg, &fgt); + if (ret < 0) { + av_log(fg, AV_LOG_ERROR, "Error configuring filter graph: %s\n", + av_err2str(ret)); + goto finish; } - if (!inputs) { - num_inputs = "0"; - } else if (inputs->next) { - num_inputs = ">1"; - } else { - num_inputs = "1"; - } - av_log(NULL, AV_LOG_ERROR, "Simple filtergraph '%s' was expected " - "to have exactly 1 input and 1 output." - " However, it had %s input(s) and %s output(s)." - " Please adjust, or use a complex filtergraph (-filter_complex) instead.\n", - graph_desc, num_inputs, num_outputs); - ret = AVERROR(EINVAL); - goto fail; } - for (cur = inputs, i = 0; cur; cur = cur->next, i++) - if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0) { - avfilter_inout_free(&inputs); - avfilter_inout_free(&outputs); - goto fail; + while (1) { + InputFilter *ifilter; + InputFilterPriv *ifp; + enum FrameOpaque o; + unsigned input_idx = fgt.next_in; + + input_status = sch_filter_receive(fgp->sch, fgp->sch_idx, + &input_idx, fgt.frame); + if (input_status == AVERROR_EOF) { + av_log(fg, AV_LOG_VERBOSE, "Filtering thread received EOF\n"); + break; + } else if (input_status == AVERROR(EAGAIN)) { + // should only happen when we didn't request any input + av_assert0(input_idx == fg->nb_inputs); + goto read_frames; } - avfilter_inout_free(&inputs); + av_assert0(input_status >= 0); - for (cur = outputs, i = 0; cur; cur = cur->next, i++) - configure_output_filter(fg, fg->outputs[i], cur); - avfilter_inout_free(&outputs); + o = (intptr_t)fgt.frame->opaque; - if (!auto_conversion_filters) - avfilter_graph_set_auto_convert(fg->graph, AVFILTER_AUTO_CONVERT_NONE); - if ((ret = avfilter_graph_config(fg->graph, NULL)) < 0) - goto fail; + o = (intptr_t)fgt.frame->opaque; - fg->is_meta = graph_is_meta(fg->graph); + // message on the control stream + if (input_idx == fg->nb_inputs) { + FilterCommand *fc; - /* limit the lists of allowed formats to the ones selected, to - * make sure they stay the same if the filtergraph is reconfigured later */ - for (i = 0; i < fg->nb_outputs; i++) { - OutputFilter *ofilter = fg->outputs[i]; - AVFilterContext *sink = ofilter->filter; + av_assert0(o == FRAME_OPAQUE_SEND_COMMAND && fgt.frame->buf[0]); - ofilter->format = av_buffersink_get_format(sink); + fc = (FilterCommand*)fgt.frame->buf[0]->data; + send_command(fg, fgt.graph, fc->time, fc->target, fc->command, fc->arg, + fc->all_filters); + av_frame_unref(fgt.frame); + continue; + } - ofilter->width = av_buffersink_get_w(sink); - ofilter->height = av_buffersink_get_h(sink); + // we received an input frame or EOF + ifilter = fg->inputs[input_idx]; + ifp = ifp_from_ifilter(ifilter); - ofilter->sample_rate = av_buffersink_get_sample_rate(sink); - av_channel_layout_uninit(&ofilter->ch_layout); - ret = av_buffersink_get_ch_layout(sink, &ofilter->ch_layout); + if (ifp->type_src == AVMEDIA_TYPE_SUBTITLE) { + int hb_frame = input_status >= 0 && o == FRAME_OPAQUE_SUB_HEARTBEAT; + ret = sub2video_frame(ifilter, (fgt.frame->buf[0] || hb_frame) ? fgt.frame : NULL, + !fgt.graph); + } else if (fgt.frame->buf[0]) { + ret = send_frame(fg, &fgt, ifilter, fgt.frame); + } else { + av_assert1(o == FRAME_OPAQUE_EOF); + ret = send_eof(&fgt, ifilter, fgt.frame->pts, fgt.frame->time_base); + } + av_frame_unref(fgt.frame); + if (ret == AVERROR_EOF) { + av_log(fg, AV_LOG_VERBOSE, "Input %u no longer accepts new data\n", + input_idx); + sch_filter_receive_finish(fgp->sch, fgp->sch_idx, input_idx); + continue; + } if (ret < 0) - goto fail; + goto finish; + +read_frames: + // retrieve all newly avalable frames + ret = read_frames(fg, &fgt, fgt.frame); + if (ret == AVERROR_EOF) { + av_log(fg, AV_LOG_VERBOSE, "All consumers returned EOF\n"); + break; + } else if (ret < 0) { + av_log(fg, AV_LOG_ERROR, "Error sending frames to consumers: %s\n", + av_err2str(ret)); + goto finish; + } } - fg->reconfiguration = 1; + for (unsigned i = 0; i < fg->nb_outputs; i++) { + OutputFilterPriv *ofp = ofp_from_ofilter(fg->outputs[i]); - for (i = 0; i < fg->nb_outputs; i++) { - OutputStream *ost = fg->outputs[i]->ost; - if (ost->enc_ctx->codec_type == AVMEDIA_TYPE_AUDIO && - !(ost->enc_ctx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) - av_buffersink_set_frame_size(ost->filter->filter, - ost->enc_ctx->frame_size); - } - - for (i = 0; i < fg->nb_inputs; i++) { - AVFrame *tmp; - while (av_fifo_read(fg->inputs[i]->frame_queue, &tmp, 1) >= 0) { - ret = av_buffersrc_add_frame(fg->inputs[i]->filter, tmp); - av_frame_free(&tmp); - if (ret < 0) - goto fail; - } - } + if (fgt.eof_out[i] || !fgt.graph) + continue; - /* send the EOFs for the finished inputs */ - for (i = 0; i < fg->nb_inputs; i++) { - if (fg->inputs[i]->eof) { - ret = av_buffersrc_add_frame(fg->inputs[i]->filter, NULL); - if (ret < 0) - goto fail; - } + ret = fg_output_frame(ofp, &fgt, NULL); + if (ret < 0) + goto finish; } - /* process queued up subtitle packets */ - for (i = 0; i < fg->nb_inputs; i++) { - InputStream *ist = fg->inputs[i]->ist; - if (ist->sub2video.sub_queue && ist->sub2video.frame) { - AVSubtitle tmp; - while (av_fifo_read(ist->sub2video.sub_queue, &tmp, 1) >= 0) { - sub2video_update(ist, INT64_MIN, &tmp); - avsubtitle_free(&tmp); - } - } - } +finish: + // EOF is normal termination + if (ret == AVERROR_EOF) + ret = 0; - return 0; + fg_thread_uninit(&fgt); -fail: - cleanup_filtergraph(fg); return ret; } -int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame) +void fg_send_command(FilterGraph *fg, double time, const char *target, + const char *command, const char *arg, int all_filters) { - AVFrameSideData *sd; - int ret; + FilterGraphPriv *fgp = fgp_from_fg(fg); + AVBufferRef *buf; + FilterCommand *fc; - av_buffer_unref(&ifilter->hw_frames_ctx); - - ifilter->format = frame->format; - - ifilter->width = frame->width; - ifilter->height = frame->height; - ifilter->sample_aspect_ratio = frame->sample_aspect_ratio; - - ifilter->sample_rate = frame->sample_rate; - ret = av_channel_layout_copy(&ifilter->ch_layout, &frame->ch_layout); - if (ret < 0) - return ret; + fc = av_mallocz(sizeof(*fc)); + if (!fc) + return; - av_freep(&ifilter->displaymatrix); - sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX); - if (sd) - ifilter->displaymatrix = av_memdup(sd->data, sizeof(int32_t) * 9); + buf = av_buffer_create((uint8_t*)fc, sizeof(*fc), filter_command_free, NULL, 0); + if (!buf) { + av_freep(&fc); + return; + } - if (frame->hw_frames_ctx) { - ifilter->hw_frames_ctx = av_buffer_ref(frame->hw_frames_ctx); - if (!ifilter->hw_frames_ctx) - return AVERROR(ENOMEM); + fc->target = av_strdup(target); + fc->command = av_strdup(command); + fc->arg = av_strdup(arg); + if (!fc->target || !fc->command || !fc->arg) { + av_buffer_unref(&buf); + return; } - return 0; -} + fc->time = time; + fc->all_filters = all_filters; -int filtergraph_is_simple(FilterGraph *fg) -{ - return !fg->graph_desc; + fgp->frame->buf[0] = buf; + fgp->frame->opaque = (void*)(intptr_t)FRAME_OPAQUE_SEND_COMMAND; + + sch_filter_command(fgp->sch, fgp->sch_idx, fgp->frame); } diff --git a/fftools/ffmpeg_hw.c b/fftools/ffmpeg_hw.c index 88fa7824701..8608d245171 100644 --- a/fftools/ffmpeg_hw.c +++ b/fftools/ffmpeg_hw.c @@ -27,7 +27,7 @@ static int nb_hw_devices; static HWDevice **hw_devices; -static HWDevice *hw_device_get_by_type(enum AVHWDeviceType type) +HWDevice *hw_device_get_by_type(enum AVHWDeviceType type) { HWDevice *found = NULL; int i; @@ -242,9 +242,9 @@ int hw_device_init_from_string(const char *arg, HWDevice **dev_out) goto done; } -static int hw_device_init_from_type(enum AVHWDeviceType type, - const char *device, - HWDevice **dev_out) +int hw_device_init_from_type(enum AVHWDeviceType type, + const char *device, + HWDevice **dev_out) { AVBufferRef *device_ref = NULL; HWDevice *dev; @@ -297,268 +297,14 @@ void hw_device_free_all(void) nb_hw_devices = 0; } -static HWDevice *hw_device_match_by_codec(const AVCodec *codec) +AVBufferRef *hw_device_for_filter(void) { - const AVCodecHWConfig *config; - HWDevice *dev; - int i; - for (i = 0;; i++) { - config = avcodec_get_hw_config(codec, i); - if (!config) - return NULL; - if (!(config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)) - continue; - dev = hw_device_get_by_type(config->device_type); - if (dev) - return dev; - } -} - -int hw_device_setup_for_decode(InputStream *ist) -{ - const AVCodecHWConfig *config; - enum AVHWDeviceType type; - HWDevice *dev = NULL; - int err, auto_device = 0; - - if (ist->hwaccel_device) { - dev = hw_device_get_by_name(ist->hwaccel_device); - if (!dev) { - if (ist->hwaccel_id == HWACCEL_AUTO) { - auto_device = 1; - } else if (ist->hwaccel_id == HWACCEL_GENERIC) { - type = ist->hwaccel_device_type; - err = hw_device_init_from_type(type, ist->hwaccel_device, - &dev); - } else { - // This will be dealt with by API-specific initialisation - // (using hwaccel_device), so nothing further needed here. - return 0; - } - } else { - if (ist->hwaccel_id == HWACCEL_AUTO) { - ist->hwaccel_device_type = dev->type; - } else if (ist->hwaccel_device_type != dev->type) { - av_log(NULL, AV_LOG_ERROR, "Invalid hwaccel device " - "specified for decoder: device %s of type %s is not " - "usable with hwaccel %s.\n", dev->name, - av_hwdevice_get_type_name(dev->type), - av_hwdevice_get_type_name(ist->hwaccel_device_type)); - return AVERROR(EINVAL); - } - } - } else { - if (ist->hwaccel_id == HWACCEL_AUTO) { - auto_device = 1; - } else if (ist->hwaccel_id == HWACCEL_GENERIC) { - type = ist->hwaccel_device_type; - dev = hw_device_get_by_type(type); - - // When "-qsv_device device" is used, an internal QSV device named - // as "__qsv_device" is created. Another QSV device is created too - // if "-init_hw_device qsv=name:device" is used. There are 2 QSV devices - // if both "-qsv_device device" and "-init_hw_device qsv=name:device" - // are used, hw_device_get_by_type(AV_HWDEVICE_TYPE_QSV) returns NULL. - // To keep back-compatibility with the removed ad-hoc libmfx setup code, - // call hw_device_get_by_name("__qsv_device") to select the internal QSV - // device. - if (!dev && type == AV_HWDEVICE_TYPE_QSV) - dev = hw_device_get_by_name("__qsv_device"); - - if (!dev) - err = hw_device_init_from_type(type, NULL, &dev); - } else { - dev = hw_device_match_by_codec(ist->dec); - if (!dev) { - // No device for this codec, but not using generic hwaccel - // and therefore may well not need one - ignore. - return 0; - } - } - } - - if (auto_device) { - int i; - if (!avcodec_get_hw_config(ist->dec, 0)) { - // Decoder does not support any hardware devices. - return 0; - } - for (i = 0; !dev; i++) { - config = avcodec_get_hw_config(ist->dec, i); - if (!config) - break; - type = config->device_type; - dev = hw_device_get_by_type(type); - if (dev) { - av_log(NULL, AV_LOG_INFO, "Using auto " - "hwaccel type %s with existing device %s.\n", - av_hwdevice_get_type_name(type), dev->name); - } - } - for (i = 0; !dev; i++) { - config = avcodec_get_hw_config(ist->dec, i); - if (!config) - break; - type = config->device_type; - // Try to make a new device of this type. - err = hw_device_init_from_type(type, ist->hwaccel_device, - &dev); - if (err < 0) { - // Can't make a device of this type. - continue; - } - if (ist->hwaccel_device) { - av_log(NULL, AV_LOG_INFO, "Using auto " - "hwaccel type %s with new device created " - "from %s.\n", av_hwdevice_get_type_name(type), - ist->hwaccel_device); - } else { - av_log(NULL, AV_LOG_INFO, "Using auto " - "hwaccel type %s with new default device.\n", - av_hwdevice_get_type_name(type)); - } - } - if (dev) { - ist->hwaccel_device_type = type; - } else { - av_log(NULL, AV_LOG_INFO, "Auto hwaccel " - "disabled: no device found.\n"); - ist->hwaccel_id = HWACCEL_NONE; - return 0; - } - } - - if (!dev) { - av_log(NULL, AV_LOG_ERROR, "No device available " - "for decoder: device type %s needed for codec %s.\n", - av_hwdevice_get_type_name(type), ist->dec->name); - return err; - } - - ist->dec_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref); - if (!ist->dec_ctx->hw_device_ctx) - return AVERROR(ENOMEM); - - return 0; -} - -int hw_device_setup_for_encode(OutputStream *ost) -{ - const AVCodecHWConfig *config; - HWDevice *dev = NULL; - AVBufferRef *frames_ref = NULL; - int i; - - if (ost->filter) { - frames_ref = av_buffersink_get_hw_frames_ctx(ost->filter->filter); - if (frames_ref && - ((AVHWFramesContext*)frames_ref->data)->format == - ost->enc_ctx->pix_fmt) { - // Matching format, will try to use hw_frames_ctx. - } else { - frames_ref = NULL; - } - } - - for (i = 0;; i++) { - config = avcodec_get_hw_config(ost->enc_ctx->codec, i); - if (!config) - break; - - if (frames_ref && - config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX && - (config->pix_fmt == AV_PIX_FMT_NONE || - config->pix_fmt == ost->enc_ctx->pix_fmt)) { - av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using input " - "frames context (format %s) with %s encoder.\n", - av_get_pix_fmt_name(ost->enc_ctx->pix_fmt), - ost->enc_ctx->codec->name); - ost->enc_ctx->hw_frames_ctx = av_buffer_ref(frames_ref); - if (!ost->enc_ctx->hw_frames_ctx) - return AVERROR(ENOMEM); - return 0; - } - - if (!dev && - config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) - dev = hw_device_get_by_type(config->device_type); - } - - if (dev) { - av_log(ost->enc_ctx, AV_LOG_VERBOSE, "Using device %s " - "(type %s) with %s encoder.\n", dev->name, - av_hwdevice_get_type_name(dev->type), ost->enc_ctx->codec->name); - ost->enc_ctx->hw_device_ctx = av_buffer_ref(dev->device_ref); - if (!ost->enc_ctx->hw_device_ctx) - return AVERROR(ENOMEM); - } else { - // No device required, or no device available. - } - return 0; -} - -static int hwaccel_retrieve_data(AVCodecContext *avctx, AVFrame *input) -{ - InputStream *ist = avctx->opaque; - AVFrame *output = NULL; - enum AVPixelFormat output_format = ist->hwaccel_output_format; - int err; - - if (input->format == output_format) { - // Nothing to do. - return 0; - } - - output = av_frame_alloc(); - if (!output) - return AVERROR(ENOMEM); - - output->format = output_format; - - err = av_hwframe_transfer_data(output, input, 0); - if (err < 0) { - av_log(avctx, AV_LOG_ERROR, "Failed to transfer data to " - "output frame: %d.\n", err); - goto fail; - } - - err = av_frame_copy_props(output, input); - if (err < 0) { - av_frame_unref(output); - goto fail; - } - - av_frame_unref(input); - av_frame_move_ref(input, output); - av_frame_free(&output); - - return 0; - -fail: - av_frame_free(&output); - return err; -} - -int hwaccel_decode_init(AVCodecContext *avctx) -{ - InputStream *ist = avctx->opaque; - - ist->hwaccel_retrieve_data = &hwaccel_retrieve_data; - - return 0; -} - -int hw_device_setup_for_filter(FilterGraph *fg) -{ - HWDevice *dev; - int i; - // Pick the last hardware device if the user doesn't pick the device for // filters explicitly with the filter_hw_device option. if (filter_hw_device) - dev = filter_hw_device; + return filter_hw_device->device_ref; else if (nb_hw_devices > 0) { - dev = hw_devices[nb_hw_devices - 1]; + HWDevice *dev = hw_devices[nb_hw_devices - 1]; if (nb_hw_devices > 1) av_log(NULL, AV_LOG_WARNING, "There are %d hardware devices. device " @@ -567,17 +313,9 @@ int hw_device_setup_for_filter(FilterGraph *fg) "%s is not usable for filters.\n", nb_hw_devices, dev->name, av_hwdevice_get_type_name(dev->type), dev->name); - } else - dev = NULL; - - if (dev) { - for (i = 0; i < fg->graph->nb_filters; i++) { - fg->graph->filters[i]->hw_device_ctx = - av_buffer_ref(dev->device_ref); - if (!fg->graph->filters[i]->hw_device_ctx) - return AVERROR(ENOMEM); - } + + return dev->device_ref; } - return 0; + return NULL; } diff --git a/fftools/ffmpeg_mux.c b/fftools/ffmpeg_mux.c index cf580519495..e8e5c677b8a 100644 --- a/fftools/ffmpeg_mux.c +++ b/fftools/ffmpeg_mux.c @@ -22,23 +22,26 @@ #include "ffmpeg.h" #include "ffmpeg_mux.h" -#include "objpool.h" +#include "ffmpeg_utils.h" #include "sync_queue.h" -#include "thread_queue.h" +#include "libavutil/avstring.h" #include "libavutil/fifo.h" #include "libavutil/intreadwrite.h" #include "libavutil/log.h" #include "libavutil/mem.h" +#include "libavutil/time.h" #include "libavutil/timestamp.h" -#include "libavutil/thread.h" #include "libavcodec/packet.h" #include "libavformat/avformat.h" #include "libavformat/avio.h" -int want_sdp = 1; +typedef struct MuxThreadContext { + AVPacket *pkt; + AVPacket *fix_sub_duration_pkt; +} MuxThreadContext; static Muxer *mux_from_of(OutputFile *of) { @@ -58,66 +61,135 @@ static int64_t filesize(AVIOContext *pb) return ret; } -static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) +static void mux_log_debug_ts(OutputStream *ost, const AVPacket *pkt) { - MuxStream *ms = ms_from_ost(ost); - AVFormatContext *s = mux->fc; - AVStream *st = ost->st; - int64_t fs; - uint64_t frame_num; - int ret; + static const char *desc[] = { + [LATENCY_PROBE_DEMUX] = "demux", + [LATENCY_PROBE_DEC_PRE] = "decode", + [LATENCY_PROBE_DEC_POST] = "decode", + [LATENCY_PROBE_FILTER_PRE] = "filter", + [LATENCY_PROBE_FILTER_POST] = "filter", + [LATENCY_PROBE_ENC_PRE] = "encode", + [LATENCY_PROBE_ENC_POST] = "encode", + [LATENCY_PROBE_NB] = "mux", + }; + + char latency[512]; + + *latency = 0; + if (pkt->opaque_ref) { + const FrameData *fd = (FrameData*)pkt->opaque_ref->data; + int64_t now = av_gettime_relative(); + int64_t total = INT64_MIN; + + int next; + + for (unsigned i = 0; i < FF_ARRAY_ELEMS(fd->wallclock); i = next) { + int64_t val = fd->wallclock[i]; + + next = i + 1; + + if (val == INT64_MIN) + continue; + + if (total == INT64_MIN) { + total = now - val; + snprintf(latency, sizeof(latency), "total:%gms", total / 1e3); + } - fs = filesize(s->pb); - atomic_store(&mux->last_filesize, fs); - if (fs >= mux->limit_filesize) { - ret = AVERROR_EOF; - goto fail; - } + // find the next valid entry + for (; next <= FF_ARRAY_ELEMS(fd->wallclock); next++) { + int64_t val_next = (next == FF_ARRAY_ELEMS(fd->wallclock)) ? + now : fd->wallclock[next]; + int64_t diff; - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && ost->vsync_method == VSYNC_DROP) - pkt->pts = pkt->dts = AV_NOPTS_VALUE; + if (val_next == INT64_MIN) + continue; + diff = val_next - val; + + // print those stages that take at least 5% of total + if (100. * diff > 5. * total) { + av_strlcat(latency, ", ", sizeof(latency)); + + if (!strcmp(desc[i], desc[next])) + av_strlcat(latency, desc[i], sizeof(latency)); + else + av_strlcatf(latency, sizeof(latency), "%s-%s:", + desc[i], desc[next]); + + av_strlcatf(latency, sizeof(latency), " %gms/%d%%", + diff / 1e3, (int)(100. * diff / total)); + } + + break; + } - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - if (ost->frame_rate.num && ost->is_cfr) { - if (pkt->duration > 0) - av_log(ost, AV_LOG_WARNING, "Overriding packet duration by frame rate, this should not happen\n"); - pkt->duration = av_rescale_q(1, av_inv_q(ost->frame_rate), - pkt->time_base); } } - av_packet_rescale_ts(pkt, pkt->time_base, ost->st->time_base); + av_log(ost, AV_LOG_INFO, "muxer <- pts:%s pts_time:%s dts:%s dts_time:%s " + "duration:%s duration_time:%s size:%d latency(%s)\n", + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ost->st->time_base), + av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ost->st->time_base), + av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &ost->st->time_base), + pkt->size, *latency ? latency : "N/A"); +} + +static int mux_fixup_ts(Muxer *mux, MuxStream *ms, AVPacket *pkt) +{ + OutputStream *ost = &ms->ost; + +#if FFMPEG_OPT_VSYNC_DROP + if (ost->type == AVMEDIA_TYPE_VIDEO && ost->vsync_method == VSYNC_DROP) + pkt->pts = pkt->dts = AV_NOPTS_VALUE; +#endif + + // rescale timestamps to the stream timebase + if (ost->type == AVMEDIA_TYPE_AUDIO && !ost->enc) { + // use av_rescale_delta() for streamcopying audio, to preserve + // accuracy with coarse input timebases + int duration = av_get_audio_frame_duration2(ost->st->codecpar, pkt->size); + + if (!duration) + duration = ost->st->codecpar->frame_size; + + pkt->dts = av_rescale_delta(pkt->time_base, pkt->dts, + (AVRational){1, ost->st->codecpar->sample_rate}, duration, + &ms->ts_rescale_delta_last, ost->st->time_base); + pkt->pts = pkt->dts; + + pkt->duration = av_rescale_q(pkt->duration, pkt->time_base, ost->st->time_base); + } else + av_packet_rescale_ts(pkt, pkt->time_base, ost->st->time_base); pkt->time_base = ost->st->time_base; - if (!(s->oformat->flags & AVFMT_NOTIMESTAMPS)) { + if (!(mux->fc->oformat->flags & AVFMT_NOTIMESTAMPS)) { if (pkt->dts != AV_NOPTS_VALUE && pkt->pts != AV_NOPTS_VALUE && pkt->dts > pkt->pts) { - av_log(s, AV_LOG_WARNING, "Invalid DTS: %"PRId64" PTS: %"PRId64" in output stream %d:%d, replacing by guess\n", - pkt->dts, pkt->pts, - ost->file_index, ost->st->index); + av_log(ost, AV_LOG_WARNING, "Invalid DTS: %"PRId64" PTS: %"PRId64", replacing by guess\n", + pkt->dts, pkt->pts); pkt->pts = pkt->dts = pkt->pts + pkt->dts + ms->last_mux_dts + 1 - FFMIN3(pkt->pts, pkt->dts, ms->last_mux_dts + 1) - FFMAX3(pkt->pts, pkt->dts, ms->last_mux_dts + 1); } - if ((st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) && + if ((ost->type == AVMEDIA_TYPE_AUDIO || ost->type == AVMEDIA_TYPE_VIDEO || ost->type == AVMEDIA_TYPE_SUBTITLE) && pkt->dts != AV_NOPTS_VALUE && ms->last_mux_dts != AV_NOPTS_VALUE) { - int64_t max = ms->last_mux_dts + !(s->oformat->flags & AVFMT_TS_NONSTRICT); + int64_t max = ms->last_mux_dts + !(mux->fc->oformat->flags & AVFMT_TS_NONSTRICT); if (pkt->dts < max) { - int loglevel = max - pkt->dts > 2 || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? AV_LOG_WARNING : AV_LOG_DEBUG; + int loglevel = max - pkt->dts > 2 || ost->type == AVMEDIA_TYPE_VIDEO ? AV_LOG_WARNING : AV_LOG_DEBUG; if (exit_on_error) loglevel = AV_LOG_ERROR; - av_log(s, loglevel, "Non-monotonous DTS in output stream " - "%d:%d; previous: %"PRId64", current: %"PRId64"; ", - ost->file_index, ost->st->index, ms->last_mux_dts, pkt->dts); + av_log(ost, loglevel, "Non-monotonic DTS; " + "previous: %"PRId64", current: %"PRId64"; ", + ms->last_mux_dts, pkt->dts); if (exit_on_error) { - ret = AVERROR(EINVAL); - goto fail; + return AVERROR(EINVAL); } - av_log(s, loglevel, "changing to %"PRId64". This may result " + av_log(ost, loglevel, "changing to %"PRId64". This may result " "in incorrect timestamps in the output file.\n", max); if (pkt->pts >= pkt->dts) @@ -128,28 +200,44 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) } ms->last_mux_dts = pkt->dts; - ost->data_size_mux += pkt->size; - frame_num = atomic_fetch_add(&ost->packets_written, 1); + if (debug_ts) + mux_log_debug_ts(ost, pkt); - pkt->stream_index = ost->index; + return 0; +} - if (debug_ts) { - av_log(ost, AV_LOG_INFO, "muxer <- type:%s " - "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s size:%d\n", - av_get_media_type_string(st->codecpar->codec_type), - av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ost->st->time_base), - av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ost->st->time_base), - av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &ost->st->time_base), - pkt->size - ); +static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) +{ + MuxStream *ms = ms_from_ost(ost); + AVFormatContext *s = mux->fc; + int64_t fs; + uint64_t frame_num; + int ret; + + fs = filesize(s->pb); + atomic_store(&mux->last_filesize, fs); + if (fs >= mux->limit_filesize) { + ret = AVERROR_EOF; + goto fail; } + ret = mux_fixup_ts(mux, ms, pkt); + if (ret < 0) + goto fail; + + ms->data_size_mux += pkt->size; + frame_num = atomic_fetch_add(&ost->packets_written, 1); + + pkt->stream_index = ost->index; + if (ms->stats.io) enc_stats_write(ost, &ms->stats, NULL, pkt, frame_num); ret = av_interleaved_write_frame(s, pkt); if (ret < 0) { - print_error("av_interleaved_write_frame()", ret); + av_log(ost, AV_LOG_ERROR, + "Error submitting a packet to the muxer: %s\n", + av_err2str(ret)); goto fail; } @@ -159,12 +247,12 @@ static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) return ret; } -static int sync_queue_process(Muxer *mux, OutputStream *ost, AVPacket *pkt, int *stream_eof) +static int sync_queue_process(Muxer *mux, MuxStream *ms, AVPacket *pkt, int *stream_eof) { OutputFile *of = &mux->of; - if (ost->sq_idx_mux >= 0) { - int ret = sq_send(mux->sq_mux, ost->sq_idx_mux, SQPKT(pkt)); + if (ms->sq_idx_mux >= 0) { + int ret = sq_send(mux->sq_mux, ms->sq_idx_mux, SQPKT(pkt)); if (ret < 0) { if (ret == AVERROR_EOF) *stream_eof = 1; @@ -174,8 +262,14 @@ static int sync_queue_process(Muxer *mux, OutputStream *ost, AVPacket *pkt, int while (1) { ret = sq_receive(mux->sq_mux, -1, SQPKT(mux->sq_pkt)); - if (ret < 0) - return (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) ? 0 : ret; + if (ret < 0) { + /* n.b.: We forward EOF from the sync queue, terminating muxing. + * This assumes that if a muxing sync queue is present, then all + * the streams use it. That is true currently, but may change in + * the future, then this code needs to be revisited. + */ + return ret == AVERROR(EAGAIN) ? 0 : ret; + } ret = write_packet(mux, of->streams[ret], mux->sq_pkt); @@ -183,287 +277,244 @@ static int sync_queue_process(Muxer *mux, OutputStream *ost, AVPacket *pkt, int return ret; } } else if (pkt) - return write_packet(mux, ost, pkt); + return write_packet(mux, &ms->ost, pkt); return 0; } -static void thread_set_name(OutputFile *of) -{ - char name[16]; - snprintf(name, sizeof(name), "mux%d:%s", of->index, of->format->name); - ff_thread_setname(name); -} +static int of_streamcopy(OutputFile *of, OutputStream *ost, AVPacket *pkt); -static void *muxer_thread(void *arg) +/* apply the output bitstream filters */ +static int mux_packet_filter(Muxer *mux, MuxThreadContext *mt, + OutputStream *ost, AVPacket *pkt, int *stream_eof) { - Muxer *mux = arg; - OutputFile *of = &mux->of; - AVPacket *pkt = NULL; - int ret = 0; - - pkt = av_packet_alloc(); - if (!pkt) { - ret = AVERROR(ENOMEM); - goto finish; - } - - thread_set_name(of); - - while (1) { - OutputStream *ost; - int stream_idx, stream_eof = 0; + MuxStream *ms = ms_from_ost(ost); + const char *err_msg; + int ret = 0; - ret = tq_receive(mux->tq, &stream_idx, pkt); - if (stream_idx < 0) { - av_log(mux, AV_LOG_VERBOSE, "All streams finished\n"); + if (pkt && !ost->enc) { + ret = of_streamcopy(&mux->of, ost, pkt); + if (ret == AVERROR(EAGAIN)) + return 0; + else if (ret == AVERROR_EOF) { + av_packet_unref(pkt); + pkt = NULL; ret = 0; - break; - } - - ost = of->streams[stream_idx]; - ret = sync_queue_process(mux, ost, ret < 0 ? NULL : pkt, &stream_eof); - av_packet_unref(pkt); - if (ret == AVERROR_EOF && stream_eof) - tq_receive_finish(mux->tq, stream_idx); - else if (ret < 0) { - av_log(mux, AV_LOG_ERROR, "Error muxing a packet\n"); - break; - } + *stream_eof = 1; + } else if (ret < 0) + goto fail; } -finish: - av_packet_free(&pkt); - - for (unsigned int i = 0; i < mux->fc->nb_streams; i++) - tq_receive_finish(mux->tq, i); - - av_log(mux, AV_LOG_VERBOSE, "Terminating muxer thread\n"); - - return (void*)(intptr_t)ret; -} - -static int thread_submit_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) -{ - int ret = 0; - - if (!pkt || ost->finished & MUXER_FINISHED) - goto finish; - - ret = tq_send(mux->tq, ost->index, pkt); - if (ret < 0) - goto finish; - - return 0; - -finish: - if (pkt) - av_packet_unref(pkt); - - ost->finished |= MUXER_FINISHED; - tq_send_finish(mux->tq, ost->index); - return ret == AVERROR_EOF ? 0 : ret; -} + // emit heartbeat for -fix_sub_duration; + // we are only interested in heartbeats on on random access points. + if (pkt && (pkt->flags & AV_PKT_FLAG_KEY)) { + mt->fix_sub_duration_pkt->opaque = (void*)(intptr_t)PKT_OPAQUE_FIX_SUB_DURATION; + mt->fix_sub_duration_pkt->pts = pkt->pts; + mt->fix_sub_duration_pkt->time_base = pkt->time_base; -static int queue_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt) -{ - MuxStream *ms = ms_from_ost(ost); - AVPacket *tmp_pkt = NULL; - int ret; - - if (!av_fifo_can_write(ms->muxing_queue)) { - size_t cur_size = av_fifo_can_read(ms->muxing_queue); - size_t pkt_size = pkt ? pkt->size : 0; - unsigned int are_we_over_size = - (ms->muxing_queue_data_size + pkt_size) > ms->muxing_queue_data_threshold; - size_t limit = are_we_over_size ? ms->max_muxing_queue_size : SIZE_MAX; - size_t new_size = FFMIN(2 * cur_size, limit); - - if (new_size <= cur_size) { - av_log(ost, AV_LOG_ERROR, - "Too many packets buffered for output stream %d:%d.\n", - ost->file_index, ost->st->index); - return AVERROR(ENOSPC); - } - ret = av_fifo_grow2(ms->muxing_queue, new_size - cur_size); - if (ret < 0) - return ret; - } - - if (pkt) { - ret = av_packet_make_refcounted(pkt); + ret = sch_mux_sub_heartbeat(mux->sch, mux->sch_idx, ms->sch_idx, + mt->fix_sub_duration_pkt); if (ret < 0) - return ret; - - tmp_pkt = av_packet_alloc(); - if (!tmp_pkt) - return AVERROR(ENOMEM); - - av_packet_move_ref(tmp_pkt, pkt); - ms->muxing_queue_data_size += tmp_pkt->size; - } - av_fifo_write(ms->muxing_queue, &tmp_pkt, 1); - - return 0; -} - -static int submit_packet(Muxer *mux, AVPacket *pkt, OutputStream *ost) -{ - int ret; - - if (mux->tq) { - return thread_submit_packet(mux, ost, pkt); - } else { - /* the muxer is not initialized yet, buffer the packet */ - ret = queue_packet(mux, ost, pkt); - if (ret < 0) { - if (pkt) - av_packet_unref(pkt); - return ret; - } + goto fail; } - return 0; -} - -void of_output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int eof) -{ - Muxer *mux = mux_from_of(of); - MuxStream *ms = ms_from_ost(ost); - const char *err_msg; - int ret = 0; - - if (!eof && pkt->dts != AV_NOPTS_VALUE) - ost->last_mux_dts = av_rescale_q(pkt->dts, pkt->time_base, AV_TIME_BASE_Q); - - /* apply the output bitstream filters */ if (ms->bsf_ctx) { int bsf_eof = 0; - ret = av_bsf_send_packet(ms->bsf_ctx, eof ? NULL : pkt); + if (pkt) + av_packet_rescale_ts(pkt, pkt->time_base, ms->bsf_ctx->time_base_in); + + ret = av_bsf_send_packet(ms->bsf_ctx, pkt); if (ret < 0) { err_msg = "submitting a packet for bitstream filtering"; goto fail; } while (!bsf_eof) { - ret = av_bsf_receive_packet(ms->bsf_ctx, pkt); + ret = av_bsf_receive_packet(ms->bsf_ctx, ms->bsf_pkt); if (ret == AVERROR(EAGAIN)) - return; + return 0; else if (ret == AVERROR_EOF) bsf_eof = 1; else if (ret < 0) { - err_msg = "applying bitstream filters to a packet"; - goto fail; + av_log(ost, AV_LOG_ERROR, + "Error applying bitstream filters to a packet: %s", + av_err2str(ret)); + if (exit_on_error) + return ret; + continue; } - ret = submit_packet(mux, bsf_eof ? NULL : pkt, ost); + if (!bsf_eof) + ms->bsf_pkt->time_base = ms->bsf_ctx->time_base_out; + + ret = sync_queue_process(mux, ms, bsf_eof ? NULL : ms->bsf_pkt, stream_eof); if (ret < 0) goto mux_fail; } + *stream_eof = 1; } else { - ret = submit_packet(mux, eof ? NULL : pkt, ost); + ret = sync_queue_process(mux, ms, pkt, stream_eof); if (ret < 0) goto mux_fail; } - return; + return *stream_eof ? AVERROR_EOF : 0; mux_fail: err_msg = "submitting a packet to the muxer"; fail: - av_log(ost, AV_LOG_ERROR, "Error %s\n", err_msg); - if (exit_on_error) - exit_program(1); + if (ret != AVERROR_EOF) + av_log(ost, AV_LOG_ERROR, "Error %s: %s\n", err_msg, av_err2str(ret)); + return ret; +} +static void thread_set_name(OutputFile *of) +{ + char name[16]; + snprintf(name, sizeof(name), "mux%d:%s", of->index, of->format->name); + ff_thread_setname(name); } -static int thread_stop(Muxer *mux) +static void mux_thread_uninit(MuxThreadContext *mt) { - void *ret; + av_packet_free(&mt->pkt); + av_packet_free(&mt->fix_sub_duration_pkt); - if (!mux || !mux->tq) - return 0; + memset(mt, 0, sizeof(*mt)); +} - for (unsigned int i = 0; i < mux->fc->nb_streams; i++) - tq_send_finish(mux->tq, i); +static int mux_thread_init(MuxThreadContext *mt) +{ + memset(mt, 0, sizeof(*mt)); - pthread_join(mux->thread, &ret); + mt->pkt = av_packet_alloc(); + if (!mt->pkt) + goto fail; - tq_free(&mux->tq); + mt->fix_sub_duration_pkt = av_packet_alloc(); + if (!mt->fix_sub_duration_pkt) + goto fail; - return (int)(intptr_t)ret; -} + return 0; -static void pkt_move(void *dst, void *src) -{ - av_packet_move_ref(dst, src); +fail: + mux_thread_uninit(mt); + return AVERROR(ENOMEM); } -static int thread_start(Muxer *mux) +int muxer_thread(void *arg) { - AVFormatContext *fc = mux->fc; - ObjPool *op; - int ret; + Muxer *mux = arg; + OutputFile *of = &mux->of; - op = objpool_alloc_packets(); - if (!op) - return AVERROR(ENOMEM); + MuxThreadContext mt; - mux->tq = tq_alloc(fc->nb_streams, mux->thread_queue_size, op, pkt_move); - if (!mux->tq) { - objpool_free(&op); - return AVERROR(ENOMEM); - } + int ret = 0; - ret = pthread_create(&mux->thread, NULL, muxer_thread, (void*)mux); - if (ret) { - tq_free(&mux->tq); - return AVERROR(ret); - } + ret = mux_thread_init(&mt); + if (ret < 0) + goto finish; - /* flush the muxing queues */ - for (int i = 0; i < fc->nb_streams; i++) { - OutputStream *ost = mux->of.streams[i]; - MuxStream *ms = ms_from_ost(ost); - AVPacket *pkt; + thread_set_name(of); - /* try to improve muxing time_base (only possible if nothing has been written yet) */ - if (!av_fifo_can_read(ms->muxing_queue)) - ost->mux_timebase = ost->st->time_base; + while (1) { + OutputStream *ost; + int stream_idx, stream_eof = 0; - while (av_fifo_read(ms->muxing_queue, &pkt, 1) >= 0) { - ret = thread_submit_packet(mux, ost, pkt); - if (pkt) { - ms->muxing_queue_data_size -= pkt->size; - av_packet_free(&pkt); + ret = sch_mux_receive(mux->sch, of->index, mt.pkt); + stream_idx = mt.pkt->stream_index; + if (stream_idx < 0) { + av_log(mux, AV_LOG_VERBOSE, "All streams finished\n"); + ret = 0; + break; + } + + ost = of->streams[mux->sch_stream_idx[stream_idx]]; + mt.pkt->stream_index = ost->index; + mt.pkt->flags &= ~AV_PKT_FLAG_TRUSTED; + + ret = mux_packet_filter(mux, &mt, ost, ret < 0 ? NULL : mt.pkt, &stream_eof); + av_packet_unref(mt.pkt); + if (ret == AVERROR_EOF) { + if (stream_eof) { + sch_mux_receive_finish(mux->sch, of->index, stream_idx); + } else { + av_log(mux, AV_LOG_VERBOSE, "Muxer returned EOF\n"); + ret = 0; + break; } - if (ret < 0) - return ret; + } else if (ret < 0) { + av_log(mux, AV_LOG_ERROR, "Error muxing a packet\n"); + break; } } +finish: + mux_thread_uninit(&mt); + + return ret; +} + +static int of_streamcopy(OutputFile *of, OutputStream *ost, AVPacket *pkt) +{ + MuxStream *ms = ms_from_ost(ost); + FrameData *fd = pkt->opaque_ref ? (FrameData*)pkt->opaque_ref->data : NULL; + int64_t dts = fd ? fd->dts_est : AV_NOPTS_VALUE; + int64_t start_time = (of->start_time == AV_NOPTS_VALUE) ? 0 : of->start_time; + int64_t ts_offset; + + if (of->recording_time != INT64_MAX && + dts >= of->recording_time + start_time) + return AVERROR_EOF; + + if (!ms->streamcopy_started && !(pkt->flags & AV_PKT_FLAG_KEY) && + !ms->copy_initial_nonkeyframes) + return AVERROR(EAGAIN); + + if (!ms->streamcopy_started) { + if (!ms->copy_prior_start && + (pkt->pts == AV_NOPTS_VALUE ? + dts < ms->ts_copy_start : + pkt->pts < av_rescale_q(ms->ts_copy_start, AV_TIME_BASE_Q, pkt->time_base))) + return AVERROR(EAGAIN); + + if (of->start_time != AV_NOPTS_VALUE && dts < of->start_time) + return AVERROR(EAGAIN); + } + + ts_offset = av_rescale_q(start_time, AV_TIME_BASE_Q, pkt->time_base); + + if (pkt->pts != AV_NOPTS_VALUE) + pkt->pts -= ts_offset; + + if (pkt->dts == AV_NOPTS_VALUE) { + pkt->dts = av_rescale_q(dts, AV_TIME_BASE_Q, pkt->time_base); + } else if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { + pkt->pts = pkt->dts - ts_offset; + } + + pkt->dts -= ts_offset; + + ms->streamcopy_started = 1; + return 0; } -static int print_sdp(void) +int print_sdp(const char *filename); + +int print_sdp(const char *filename) { char sdp[16384]; - int i; - int j, ret; + int j = 0, ret; AVIOContext *sdp_pb; AVFormatContext **avc; - for (i = 0; i < nb_output_files; i++) { - if (!mux_from_of(output_files[i])->header_written) - return 0; - } - avc = av_malloc_array(nb_output_files, sizeof(*avc)); if (!avc) return AVERROR(ENOMEM); - for (i = 0, j = 0; i < nb_output_files; i++) { + for (int i = 0; i < nb_output_files; i++) { if (!strcmp(output_files[i]->format->name, "rtp")) { avc[j] = mux_from_of(output_files[i])->fc; j++; @@ -480,40 +531,31 @@ static int print_sdp(void) if (ret < 0) goto fail; - if (!sdp_filename) { + if (!filename) { printf("SDP:\n%s\n", sdp); fflush(stdout); } else { - ret = avio_open2(&sdp_pb, sdp_filename, AVIO_FLAG_WRITE, &int_cb, NULL); + ret = avio_open2(&sdp_pb, filename, AVIO_FLAG_WRITE, &int_cb, NULL); if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Failed to open sdp file '%s'\n", sdp_filename); + av_log(NULL, AV_LOG_ERROR, "Failed to open sdp file '%s'\n", filename); goto fail; } avio_print(sdp_pb, sdp); avio_closep(&sdp_pb); - av_freep(&sdp_filename); } - // SDP successfully written, allow muxer threads to start - ret = 1; - fail: av_freep(&avc); return ret; } -int mux_check_init(Muxer *mux) +int mux_check_init(void *arg) { + Muxer *mux = arg; OutputFile *of = &mux->of; AVFormatContext *fc = mux->fc; - int ret, i; - - for (i = 0; i < fc->nb_streams; i++) { - OutputStream *ost = of->streams[i]; - if (!ost->initialized) - return 0; - } + int ret; ret = avformat_write_header(fc, &mux->opts); if (ret < 0) { @@ -525,27 +567,7 @@ int mux_check_init(Muxer *mux) mux->header_written = 1; av_dump_format(fc, of->index, fc->url, 1); - nb_output_dumped++; - - if (sdp_filename || want_sdp) { - ret = print_sdp(); - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error writing the SDP.\n"); - return ret; - } else if (ret == 1) { - /* SDP is written only after all the muxers are ready, so now we - * start ALL the threads */ - for (i = 0; i < nb_output_files; i++) { - ret = thread_start(mux_from_of(output_files[i])); - if (ret < 0) - return ret; - } - } - } else { - ret = thread_start(mux_from_of(of)); - if (ret < 0) - return ret; - } + atomic_fetch_add(&nb_output_dumped, 1); return 0; } @@ -557,9 +579,9 @@ static int bsf_init(MuxStream *ms) int ret; if (!ctx) - return 0; + return avcodec_parameters_copy(ost->st->codecpar, ost->par_in); - ret = avcodec_parameters_copy(ctx->par_in, ost->st->codecpar); + ret = avcodec_parameters_copy(ctx->par_in, ost->par_in); if (ret < 0) return ret; @@ -577,6 +599,10 @@ static int bsf_init(MuxStream *ms) return ret; ost->st->time_base = ctx->time_base_out; + ms->bsf_pkt = av_packet_alloc(); + if (!ms->bsf_pkt) + return AVERROR(ENOMEM); + return 0; } @@ -586,9 +612,6 @@ int of_stream_init(OutputFile *of, OutputStream *ost) MuxStream *ms = ms_from_ost(ost); int ret; - if (ost->sq_idx_mux >= 0) - sq_set_tb(mux->sq_mux, ost->sq_idx_mux, ost->mux_timebase); - /* initialize bitstream filters for the output stream * needs to be done here, because the codec id for streamcopy is not * known until now */ @@ -596,32 +619,139 @@ int of_stream_init(OutputFile *of, OutputStream *ost) if (ret < 0) return ret; - ost->initialized = 1; + if (ms->stream_duration) { + ost->st->duration = av_rescale_q(ms->stream_duration, ms->stream_duration_tb, + ost->st->time_base); + } + + if (ms->sch_idx >= 0) + return sch_mux_stream_ready(mux->sch, of->index, ms->sch_idx); + + return 0; +} + +static int check_written(OutputFile *of) +{ + int64_t total_packets_written = 0; + int pass1_used = 1; + int ret = 0; + + for (int i = 0; i < of->nb_streams; i++) { + OutputStream *ost = of->streams[i]; + uint64_t packets_written = atomic_load(&ost->packets_written); + + total_packets_written += packets_written; + + if (ost->enc_ctx && + (ost->enc_ctx->flags & (AV_CODEC_FLAG_PASS1 | AV_CODEC_FLAG_PASS2)) + != AV_CODEC_FLAG_PASS1) + pass1_used = 0; + + if (!packets_written && + (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT_STREAM)) { + av_log(ost, AV_LOG_FATAL, "Empty output stream\n"); + ret = err_merge(ret, AVERROR(EINVAL)); + } + } + + if (!total_packets_written) { + int level = AV_LOG_WARNING; + + if (abort_on_flags & ABORT_ON_FLAG_EMPTY_OUTPUT) { + ret = err_merge(ret, AVERROR(EINVAL)); + level = AV_LOG_FATAL; + } + + av_log(of, level, "Output file is empty, nothing was encoded%s\n", + pass1_used ? "" : "(check -ss / -t / -frames parameters if used)"); + } + + return ret; +} + +static void mux_final_stats(Muxer *mux) +{ + OutputFile *of = &mux->of; + uint64_t total_packets = 0, total_size = 0; + uint64_t video_size = 0, audio_size = 0, subtitle_size = 0, + extra_size = 0, other_size = 0; + + uint8_t overhead[16] = "unknown"; + int64_t file_size = of_filesize(of); + + av_log(of, AV_LOG_VERBOSE, "Output file #%d (%s):\n", + of->index, of->url); + + for (int j = 0; j < of->nb_streams; j++) { + OutputStream *ost = of->streams[j]; + MuxStream *ms = ms_from_ost(ost); + const AVCodecParameters *par = ost->st->codecpar; + const enum AVMediaType type = par->codec_type; + const uint64_t s = ms->data_size_mux; + + switch (type) { + case AVMEDIA_TYPE_VIDEO: video_size += s; break; + case AVMEDIA_TYPE_AUDIO: audio_size += s; break; + case AVMEDIA_TYPE_SUBTITLE: subtitle_size += s; break; + default: other_size += s; break; + } + + extra_size += par->extradata_size; + total_size += s; + total_packets += atomic_load(&ost->packets_written); + + av_log(of, AV_LOG_VERBOSE, " Output stream #%d:%d (%s): ", + of->index, j, av_get_media_type_string(type)); + if (ost->enc) { + av_log(of, AV_LOG_VERBOSE, "%"PRIu64" frames encoded", + ost->frames_encoded); + if (type == AVMEDIA_TYPE_AUDIO) + av_log(of, AV_LOG_VERBOSE, " (%"PRIu64" samples)", ost->samples_encoded); + av_log(of, AV_LOG_VERBOSE, "; "); + } + + av_log(of, AV_LOG_VERBOSE, "%"PRIu64" packets muxed (%"PRIu64" bytes); ", + atomic_load(&ost->packets_written), s); + + av_log(of, AV_LOG_VERBOSE, "\n"); + } + + av_log(of, AV_LOG_VERBOSE, " Total: %"PRIu64" packets (%"PRIu64" bytes) muxed\n", + total_packets, total_size); - return mux_check_init(mux); + if (total_size && file_size > 0 && file_size >= total_size) { + snprintf(overhead, sizeof(overhead), "%f%%", + 100.0 * (file_size - total_size) / total_size); + } + + av_log(of, AV_LOG_INFO, + "video:%1.0fKiB audio:%1.0fKiB subtitle:%1.0fKiB other streams:%1.0fKiB " + "global headers:%1.0fKiB muxing overhead: %s\n", + video_size / 1024.0, + audio_size / 1024.0, + subtitle_size / 1024.0, + other_size / 1024.0, + extra_size / 1024.0, + overhead); } int of_write_trailer(OutputFile *of) { Muxer *mux = mux_from_of(of); AVFormatContext *fc = mux->fc; - int ret; + int ret, mux_result = 0; - if (!mux->tq) { + if (!mux->header_written) { av_log(mux, AV_LOG_ERROR, "Nothing was written into output file, because " "at least one of its streams received no packets.\n"); return AVERROR(EINVAL); } - ret = thread_stop(mux); - if (ret < 0) - main_return_code = ret; - ret = av_write_trailer(fc); if (ret < 0) { av_log(mux, AV_LOG_ERROR, "Error writing trailer: %s\n", av_err2str(ret)); - return ret; + mux_result = err_merge(mux_result, ret); } mux->last_filesize = filesize(fc->pb); @@ -630,11 +760,28 @@ int of_write_trailer(OutputFile *of) ret = avio_closep(&fc->pb); if (ret < 0) { av_log(mux, AV_LOG_ERROR, "Error closing file: %s\n", av_err2str(ret)); - return ret; + mux_result = err_merge(mux_result, ret); } } - return 0; + mux_final_stats(mux); + + // check whether anything was actually written + ret = check_written(of); + mux_result = err_merge(mux_result, ret); + + return mux_result; +} + +static void enc_stats_uninit(EncStats *es) +{ + for (int i = 0; i < es->nb_components; i++) + av_freep(&es->components[i].str); + av_freep(&es->components); + + if (es->lock_initialized) + pthread_mutex_destroy(&es->lock); + es->lock_initialized = 0; } static void ost_free(OutputStream **post) @@ -646,6 +793,8 @@ static void ost_free(OutputStream **post) return; ms = ms_from_ost(ost); + enc_free(&ost->enc); + if (ost->logfile) { if (fclose(ost->logfile)) av_log(ms, AV_LOG_ERROR, @@ -654,32 +803,21 @@ static void ost_free(OutputStream **post) ost->logfile = NULL; } - if (ms->muxing_queue) { - AVPacket *pkt; - while (av_fifo_read(ms->muxing_queue, &pkt, 1) >= 0) - av_packet_free(&pkt); - av_fifo_freep2(&ms->muxing_queue); - } + avcodec_parameters_free(&ost->par_in); av_bsf_free(&ms->bsf_ctx); + av_packet_free(&ms->bsf_pkt); - av_frame_free(&ost->filtered_frame); - av_frame_free(&ost->sq_frame); - av_frame_free(&ost->last_frame); - av_packet_free(&ost->pkt); + av_packet_free(&ms->pkt); av_dict_free(&ost->encoder_opts); av_freep(&ost->kf.pts); av_expr_free(ost->kf.pexpr); - av_freep(&ost->avfilter); av_freep(&ost->logfile_prefix); av_freep(&ost->apad); -#if FFMPEG_OPT_MAP_CHANNEL - av_freep(&ost->audio_channels_map); - ost->audio_channels_mapped = 0; -#endif + av_freep(&ost->attachment_filename); av_dict_free(&ost->sws_dict); av_dict_free(&ost->swr_opts); @@ -688,17 +826,9 @@ static void ost_free(OutputStream **post) av_freep(&ost->enc_ctx->stats_in); avcodec_free_context(&ost->enc_ctx); - for (int i = 0; i < ost->enc_stats_pre.nb_components; i++) - av_freep(&ost->enc_stats_pre.components[i].str); - av_freep(&ost->enc_stats_pre.components); - - for (int i = 0; i < ost->enc_stats_post.nb_components; i++) - av_freep(&ost->enc_stats_post.components[i].str); - av_freep(&ost->enc_stats_post.components); - - for (int i = 0; i < ms->stats.nb_components; i++) - av_freep(&ms->stats.components[i].str); - av_freep(&ms->stats.components); + enc_stats_uninit(&ost->enc_stats_pre); + enc_stats_uninit(&ost->enc_stats_post); + enc_stats_uninit(&ms->stats); av_freep(post); } @@ -717,7 +847,7 @@ static void fc_close(AVFormatContext **pfc) *pfc = NULL; } -void of_close(OutputFile **pof) +void of_free(OutputFile **pof) { OutputFile *of = *pof; Muxer *mux; @@ -726,15 +856,14 @@ void of_close(OutputFile **pof) return; mux = mux_from_of(of); - thread_stop(mux); - - sq_free(&of->sq_encode); sq_free(&mux->sq_mux); for (int i = 0; i < of->nb_streams; i++) ost_free(&of->streams[i]); av_freep(&of->streams); + av_freep(&mux->sch_stream_idx); + av_dict_free(&mux->opts); av_packet_free(&mux->sq_pkt); diff --git a/fftools/ffmpeg_mux.h b/fftools/ffmpeg_mux.h index c76dc2e5246..16af6d38ba3 100644 --- a/fftools/ffmpeg_mux.h +++ b/fftools/ffmpeg_mux.h @@ -24,7 +24,7 @@ #include #include -#include "thread_queue.h" +#include "ffmpeg_sched.h" #include "libavformat/avformat.h" @@ -32,67 +32,78 @@ #include "libavutil/dict.h" #include "libavutil/fifo.h" -#include "libavutil/thread.h" typedef struct MuxStream { - OutputStream ost; + OutputStream ost; // name used for logging - char log_name[32]; + char log_name[32]; - /* the packets are buffered here until the muxer is ready to be initialized */ - AVFifo *muxing_queue; + AVBSFContext *bsf_ctx; + AVPacket *bsf_pkt; - AVBSFContext *bsf_ctx; + AVPacket *pkt; - EncStats stats; + EncStats stats; - int64_t max_frames; + int sch_idx; + int sch_idx_enc; + int sch_idx_src; - /* - * The size of the AVPackets' buffers in queue. - * Updated when a packet is either pushed or pulled from the queue. - */ - size_t muxing_queue_data_size; + int sq_idx_mux; - int max_muxing_queue_size; + int64_t max_frames; - /* Threshold after which max_muxing_queue_size will be in effect */ - size_t muxing_queue_data_threshold; + // timestamp from which the streamcopied streams should start, + // in AV_TIME_BASE_Q; + // everything before it should be discarded + int64_t ts_copy_start; /* dts of the last packet sent to the muxer, in the stream timebase * used for making up missing dts values */ - int64_t last_mux_dts; + int64_t last_mux_dts; + + int64_t stream_duration; + AVRational stream_duration_tb; + + // state for av_rescale_delta() call for audio in write_packet() + int64_t ts_rescale_delta_last; + + // combined size of all the packets sent to the muxer + uint64_t data_size_mux; + + int copy_initial_nonkeyframes; + int copy_prior_start; + int streamcopy_started; } MuxStream; typedef struct Muxer { - OutputFile of; + OutputFile of; // name used for logging - char log_name[32]; + char log_name[32]; - AVFormatContext *fc; + AVFormatContext *fc; - pthread_t thread; - ThreadQueue *tq; + Scheduler *sch; + unsigned sch_idx; - AVDictionary *opts; + // OutputStream indices indexed by scheduler stream indices + int *sch_stream_idx; + int nb_sch_stream_idx; - int thread_queue_size; + AVDictionary *opts; /* filesize limit expressed in bytes */ - int64_t limit_filesize; - atomic_int_least64_t last_filesize; - int header_written; + int64_t limit_filesize; + atomic_int_least64_t last_filesize; + int header_written; - SyncQueue *sq_mux; - AVPacket *sq_pkt; + SyncQueue *sq_mux; + AVPacket *sq_pkt; } Muxer; -/* whether we want to print an SDP, set in of_open() */ -extern int want_sdp; - -int mux_check_init(Muxer *mux); +int mux_check_init(void *arg); static MuxStream *ms_from_ost(OutputStream *ost) { diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c index ceadb93545e..d3d7d022ff6 100644 --- a/fftools/ffmpeg_mux_init.c +++ b/fftools/ffmpeg_mux_init.c @@ -23,6 +23,7 @@ #include "cmdutils.h" #include "ffmpeg.h" #include "ffmpeg_mux.h" +#include "ffmpeg_sched.h" #include "fopen_utf8.h" #include "libavformat/avformat.h" @@ -37,7 +38,9 @@ #include "libavutil/avutil.h" #include "libavutil/bprint.h" #include "libavutil/dict.h" +#include "libavutil/display.h" #include "libavutil/getenv_utf8.h" +#include "libavutil/iamf.h" #include "libavutil/intreadwrite.h" #include "libavutil/log.h" #include "libavutil/mem.h" @@ -47,47 +50,6 @@ #define DEFAULT_PASS_LOGFILENAME_PREFIX "ffmpeg2pass" -static const char *const opt_name_apad[] = {"apad", NULL}; -static const char *const opt_name_autoscale[] = {"autoscale", NULL}; -static const char *const opt_name_bits_per_raw_sample[] = {"bits_per_raw_sample", NULL}; -static const char *const opt_name_bitstream_filters[] = {"bsf", "absf", "vbsf", NULL}; -static const char *const opt_name_copy_initial_nonkeyframes[] = {"copyinkf", NULL}; -static const char *const opt_name_copy_prior_start[] = {"copypriorss", NULL}; -static const char *const opt_name_disposition[] = {"disposition", NULL}; -static const char *const opt_name_enc_time_bases[] = {"enc_time_base", NULL}; -static const char *const opt_name_enc_stats_pre[] = {"enc_stats_pre", NULL}; -static const char *const opt_name_enc_stats_post[] = {"enc_stats_post", NULL}; -static const char *const opt_name_mux_stats[] = {"mux_stats", NULL}; -static const char *const opt_name_enc_stats_pre_fmt[] = {"enc_stats_pre_fmt", NULL}; -static const char *const opt_name_enc_stats_post_fmt[] = {"enc_stats_post_fmt", NULL}; -static const char *const opt_name_mux_stats_fmt[] = {"mux_stats_fmt", NULL}; -static const char *const opt_name_filters[] = {"filter", "af", "vf", NULL}; -static const char *const opt_name_filter_scripts[] = {"filter_script", NULL}; -static const char *const opt_name_fix_sub_duration_heartbeat[] = {"fix_sub_duration_heartbeat", NULL}; -static const char *const opt_name_fps_mode[] = {"fps_mode", NULL}; -static const char *const opt_name_force_fps[] = {"force_fps", NULL}; -static const char *const opt_name_forced_key_frames[] = {"forced_key_frames", NULL}; -static const char *const opt_name_frame_aspect_ratios[] = {"aspect", NULL}; -static const char *const opt_name_intra_matrices[] = {"intra_matrix", NULL}; -static const char *const opt_name_inter_matrices[] = {"inter_matrix", NULL}; -static const char *const opt_name_chroma_intra_matrices[] = {"chroma_intra_matrix", NULL}; -static const char *const opt_name_max_frame_rates[] = {"fpsmax", NULL}; -static const char *const opt_name_max_frames[] = {"frames", "aframes", "vframes", "dframes", NULL}; -static const char *const opt_name_max_muxing_queue_size[] = {"max_muxing_queue_size", NULL}; -static const char *const opt_name_muxing_queue_data_threshold[] = {"muxing_queue_data_threshold", NULL}; -static const char *const opt_name_pass[] = {"pass", NULL}; -static const char *const opt_name_passlogfiles[] = {"passlogfile", NULL}; -static const char *const opt_name_presets[] = {"pre", "apre", "vpre", "spre", NULL}; -static const char *const opt_name_qscale[] = {"q", "qscale", NULL}; -static const char *const opt_name_rc_overrides[] = {"rc_override", NULL}; -static const char *const opt_name_time_bases[] = {"time_base", NULL}; -static const char *const opt_name_audio_channels[] = {"ac", NULL}; -static const char *const opt_name_audio_ch_layouts[] = {"channel_layout", "ch_layout", NULL}; -static const char *const opt_name_audio_sample_rate[] = {"ar", NULL}; -static const char *const opt_name_frame_sizes[] = {"s", NULL}; -static const char *const opt_name_frame_pix_fmts[] = {"pix_fmt", NULL}; -static const char *const opt_name_sample_fmts[] = {"sample_fmt", NULL}; - static int check_opt_bitexact(void *ctx, const AVDictionary *opts, const char *opt_name, int flag) { @@ -107,28 +69,41 @@ static int check_opt_bitexact(void *ctx, const AVDictionary *opts, static int choose_encoder(const OptionsContext *o, AVFormatContext *s, OutputStream *ost, const AVCodec **enc) { - enum AVMediaType type = ost->st->codecpar->codec_type; + enum AVMediaType type = ost->type; char *codec_name = NULL; *enc = NULL; - if (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO || type == AVMEDIA_TYPE_SUBTITLE) { - MATCH_PER_STREAM_OPT(codec_names, str, codec_name, s, ost->st); - if (!codec_name) { - ost->st->codecpar->codec_id = av_guess_codec(s->oformat, NULL, s->url, - NULL, ost->st->codecpar->codec_type); - *enc = avcodec_find_encoder(ost->st->codecpar->codec_id); - if (!*enc) { - av_log(ost, AV_LOG_FATAL, "Automatic encoder selection failed " - "Default encoder for format %s (codec %s) is " - "probably disabled. Please choose an encoder manually.\n", - s->oformat->name, avcodec_get_name(ost->st->codecpar->codec_id)); - return AVERROR_ENCODER_NOT_FOUND; - } - } else if (strcmp(codec_name, "copy")) { - *enc = find_codec_or_die(ost, codec_name, ost->st->codecpar->codec_type, 1); - ost->st->codecpar->codec_id = (*enc)->id; + MATCH_PER_STREAM_OPT(codec_names, str, codec_name, s, ost->st); + + if (type != AVMEDIA_TYPE_VIDEO && + type != AVMEDIA_TYPE_AUDIO && + type != AVMEDIA_TYPE_SUBTITLE) { + if (codec_name && strcmp(codec_name, "copy")) { + const char *type_str = av_get_media_type_string(type); + av_log(ost, AV_LOG_FATAL, + "Encoder '%s' specified, but only '-codec copy' supported " + "for %s streams\n", codec_name, type_str); + return AVERROR(ENOSYS); + } + return 0; + } + + if (!codec_name) { + ost->par_in->codec_id = av_guess_codec(s->oformat, NULL, s->url, NULL, ost->type); + *enc = avcodec_find_encoder(ost->par_in->codec_id); + if (!*enc) { + av_log(ost, AV_LOG_FATAL, "Automatic encoder selection failed " + "Default encoder for format %s (codec %s) is " + "probably disabled. Please choose an encoder manually.\n", + s->oformat->name, avcodec_get_name(ost->par_in->codec_id)); + return AVERROR_ENCODER_NOT_FOUND; } + } else if (strcmp(codec_name, "copy")) { + int ret = find_codec(ost, codec_name, ost->type, 1, enc); + if (ret < 0) + return ret; + ost->par_in->codec_id = (*enc)->id; } return 0; @@ -142,7 +117,8 @@ static char *get_line(AVIOContext *s, AVBPrint *bprint) av_bprint_chars(bprint, c, 1); if (!av_bprint_is_complete(bprint)) - report_and_exit(AVERROR(ENOMEM)); + return NULL; + return bprint->str; } @@ -195,7 +171,9 @@ static int enc_stats_get_file(AVIOContext **io, const char *path) return 0; } - GROW_ARRAY(enc_stats_files, nb_enc_stats_files); + ret = GROW_ARRAY(enc_stats_files, nb_enc_stats_files); + if (ret < 0) + return ret; esf = &enc_stats_files[nb_enc_stats_files - 1]; @@ -270,9 +248,9 @@ static int enc_stats_init(OutputStream *ost, EncStats *es, int pre, static const struct { enum EncStatsType type; const char *str; - int pre_only:1; - int post_only:1; - int need_input_data:1; + unsigned pre_only:1; + unsigned post_only:1; + unsigned need_input_data:1; } fmt_specs[] = { { ENC_STATS_FILE_IDX, "fidx" }, { ENC_STATS_STREAM_IDX, "sidx" }, @@ -291,6 +269,7 @@ static int enc_stats_init(OutputStream *ost, EncStats *es, int pre, { ENC_STATS_PKT_SIZE, "size", 0, 1 }, { ENC_STATS_BITRATE, "br", 0, 1 }, { ENC_STATS_AVG_BITRATE, "abr", 0, 1 }, + { ENC_STATS_KEYFRAME, "key", 0, 1 }, }; const char *next = fmt_spec; @@ -307,7 +286,11 @@ static int enc_stats_init(OutputStream *ost, EncStats *es, int pre, return ret; if (val) { - GROW_ARRAY(es->components, es->nb_components); + ret = GROW_ARRAY(es->components, es->nb_components); + if (ret < 0) { + av_freep(&val); + return ret; + } c = &es->components[es->nb_components - 1]; c->type = ENC_STATS_LITERAL; @@ -338,7 +321,10 @@ static int enc_stats_init(OutputStream *ost, EncStats *es, int pre, } next++; - GROW_ARRAY(es->components, es->nb_components); + ret = GROW_ARRAY(es->components, es->nb_components); + if (ret < 0) + goto fail; + c = &es->components[es->nb_components - 1]; for (size_t i = 0; i < FF_ARRAY_ELEMS(fmt_specs); i++) { @@ -353,15 +339,11 @@ static int enc_stats_init(OutputStream *ost, EncStats *es, int pre, c->type = fmt_specs[i].type; - if (fmt_specs[i].need_input_data) { - if (ost->ist) - ost->ist->want_frame_data = 1; - else { - av_log(ost, AV_LOG_WARNING, - "Format directive '%s' is unavailable, because " - "this output stream has no associated input stream\n", - val); - } + if (fmt_specs[i].need_input_data && !ost->ist) { + av_log(ost, AV_LOG_WARNING, + "Format directive '%s' is unavailable, because " + "this output stream has no associated input stream\n", + val); } break; @@ -380,6 +362,11 @@ static int enc_stats_init(OutputStream *ost, EncStats *es, int pre, return ret; } + ret = pthread_mutex_init(&es->lock, NULL); + if (ret) + return AVERROR(ret); + es->lock_initialized = 1; + ret = enc_stats_get_file(&es->io, path); if (ret < 0) return ret; @@ -404,333 +391,219 @@ static const AVClass output_stream_class = { static MuxStream *mux_stream_alloc(Muxer *mux, enum AVMediaType type) { const char *type_str = av_get_media_type_string(type); - MuxStream *ms = allocate_array_elem(&mux->of.streams, sizeof(*ms), - &mux->of.nb_streams); + MuxStream *ms; + + ms = allocate_array_elem(&mux->of.streams, sizeof(*ms), &mux->of.nb_streams); + if (!ms) + return NULL; - ms->ost.file_index = mux->of.index; + ms->ost.file = &mux->of; ms->ost.index = mux->of.nb_streams - 1; + ms->ost.type = type; ms->ost.class = &output_stream_class; + ms->sch_idx = -1; + ms->sch_idx_enc = -1; + snprintf(ms->log_name, sizeof(ms->log_name), "%cost#%d:%d", type_str ? *type_str : '?', mux->of.index, ms->ost.index); return ms; } -static OutputStream *new_output_stream(Muxer *mux, const OptionsContext *o, - enum AVMediaType type, InputStream *ist) +static int ost_get_filters(const OptionsContext *o, AVFormatContext *oc, + OutputStream *ost, char **dst) { - AVFormatContext *oc = mux->fc; - MuxStream *ms; - OutputStream *ost; - const AVCodec *enc; - AVStream *st = avformat_new_stream(oc, NULL); - int ret = 0; - const char *bsfs = NULL, *time_base = NULL; - char *next, *codec_tag = NULL; - double qscale = -1; - int i; - - if (!st) - report_and_exit(AVERROR(ENOMEM)); - - if (oc->nb_streams - 1 < o->nb_streamid_map) - st->id = o->streamid_map[oc->nb_streams - 1]; - - ms = mux_stream_alloc(mux, type); - ost = &ms->ost; - - ms->muxing_queue = av_fifo_alloc2(8, sizeof(AVPacket*), 0); - if (!ms->muxing_queue) - report_and_exit(AVERROR(ENOMEM)); - ms->last_mux_dts = AV_NOPTS_VALUE; - - ost->st = st; - ost->ist = ist; - ost->kf.ref_pts = AV_NOPTS_VALUE; - st->codecpar->codec_type = type; - - ret = choose_encoder(o, oc, ost, &enc); - if (ret < 0) { - av_log(ost, AV_LOG_FATAL, "Error selecting an encoder\n"); - exit_program(1); - } - - if (enc) { - ost->enc_ctx = avcodec_alloc_context3(enc); - if (!ost->enc_ctx) - report_and_exit(AVERROR(ENOMEM)); - - av_strlcat(ms->log_name, "/", sizeof(ms->log_name)); - av_strlcat(ms->log_name, enc->name, sizeof(ms->log_name)); - } else { - av_strlcat(ms->log_name, "/copy", sizeof(ms->log_name)); - } - - ost->filtered_frame = av_frame_alloc(); - if (!ost->filtered_frame) - report_and_exit(AVERROR(ENOMEM)); - - ost->pkt = av_packet_alloc(); - if (!ost->pkt) - report_and_exit(AVERROR(ENOMEM)); - - if (ost->enc_ctx) { - AVCodecContext *enc = ost->enc_ctx; - AVIOContext *s = NULL; - char *buf = NULL, *arg = NULL, *preset = NULL; - const char *enc_stats_pre = NULL, *enc_stats_post = NULL, *mux_stats = NULL; - - ost->encoder_opts = filter_codec_opts(o->g->codec_opts, enc->codec_id, - oc, st, enc->codec); - - MATCH_PER_STREAM_OPT(presets, str, preset, oc, st); - ost->autoscale = 1; - MATCH_PER_STREAM_OPT(autoscale, i, ost->autoscale, oc, st); - if (preset && (!(ret = get_preset_file_2(preset, enc->codec->name, &s)))) { - AVBPrint bprint; - av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED); - do { - av_bprint_clear(&bprint); - buf = get_line(s, &bprint); - if (!buf[0] || buf[0] == '#') - continue; - if (!(arg = strchr(buf, '='))) { - av_log(ost, AV_LOG_FATAL, "Invalid line found in the preset file.\n"); - exit_program(1); - } - *arg++ = 0; - av_dict_set(&ost->encoder_opts, buf, arg, AV_DICT_DONT_OVERWRITE); - } while (!s->eof_reached); - av_bprint_finalize(&bprint, NULL); - avio_closep(&s); - } - if (ret) { - av_log(ost, AV_LOG_FATAL, - "Preset %s specified, but could not be opened.\n", preset); - exit_program(1); - } - - MATCH_PER_STREAM_OPT(enc_stats_pre, str, enc_stats_pre, oc, st); - if (enc_stats_pre && - (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { - const char *format = "{fidx} {sidx} {n} {t}"; - - MATCH_PER_STREAM_OPT(enc_stats_pre_fmt, str, format, oc, st); - - ret = enc_stats_init(ost, &ost->enc_stats_pre, 1, enc_stats_pre, format); - if (ret < 0) - exit_program(1); - } - - MATCH_PER_STREAM_OPT(enc_stats_post, str, enc_stats_post, oc, st); - if (enc_stats_post && - (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { - const char *format = "{fidx} {sidx} {n} {t}"; - - MATCH_PER_STREAM_OPT(enc_stats_post_fmt, str, format, oc, st); - - ret = enc_stats_init(ost, &ost->enc_stats_post, 0, enc_stats_post, format); - if (ret < 0) - exit_program(1); - } - - MATCH_PER_STREAM_OPT(mux_stats, str, mux_stats, oc, st); - if (mux_stats && - (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { - const char *format = "{fidx} {sidx} {n} {t}"; - - MATCH_PER_STREAM_OPT(mux_stats_fmt, str, format, oc, st); - - ret = enc_stats_init(ost, &ms->stats, 0, mux_stats, format); - if (ret < 0) - exit_program(1); - } - } else { - ost->encoder_opts = filter_codec_opts(o->g->codec_opts, AV_CODEC_ID_NONE, oc, st, NULL); - } - + const char *filters = NULL; +#if FFMPEG_OPT_FILTER_SCRIPT + const char *filters_script = NULL; - if (o->bitexact) { - ost->bitexact = 1; - } else if (ost->enc_ctx) { - ost->bitexact = check_opt_bitexact(ost->enc_ctx, ost->encoder_opts, "flags", - AV_CODEC_FLAG_BITEXACT); - } + MATCH_PER_STREAM_OPT(filter_scripts, str, filters_script, oc, ost->st); +#endif + MATCH_PER_STREAM_OPT(filters, str, filters, oc, ost->st); - MATCH_PER_STREAM_OPT(time_bases, str, time_base, oc, st); - if (time_base) { - AVRational q; - if (av_parse_ratio(&q, time_base, INT_MAX, 0, NULL) < 0 || - q.num <= 0 || q.den <= 0) { - av_log(ost, AV_LOG_FATAL, "Invalid time base: %s\n", time_base); - exit_program(1); + if (!ost->enc) { + if ( +#if FFMPEG_OPT_FILTER_SCRIPT + filters_script || +#endif + filters) { + av_log(ost, AV_LOG_ERROR, + "%s '%s' was specified, but codec copy was selected. " + "Filtering and streamcopy cannot be used together.\n", +#if FFMPEG_OPT_FILTER_SCRIPT + filters ? "Filtergraph" : "Filtergraph script", + filters ? filters : filters_script +#else + "Filtergraph", filters +#endif + ); + return AVERROR(ENOSYS); } - st->time_base = q; + return 0; } - MATCH_PER_STREAM_OPT(enc_time_bases, str, time_base, oc, st); - if (time_base) { - AVRational q; - if (av_parse_ratio(&q, time_base, INT_MAX, 0, NULL) < 0 || - q.den <= 0) { - av_log(ost, AV_LOG_FATAL, "Invalid time base: %s\n", time_base); - exit_program(1); + if (!ost->ist) { + if ( +#if FFMPEG_OPT_FILTER_SCRIPT + filters_script || +#endif + filters) { + av_log(ost, AV_LOG_ERROR, + "%s '%s' was specified for a stream fed from a complex " + "filtergraph. Simple and complex filtering cannot be used " + "together for the same stream.\n", +#if FFMPEG_OPT_FILTER_SCRIPT + filters ? "Filtergraph" : "Filtergraph script", + filters ? filters : filters_script +#else + "Filtergraph", filters +#endif + ); + return AVERROR(EINVAL); } - ost->enc_timebase = q; + return 0; } - ms->max_frames = INT64_MAX; - MATCH_PER_STREAM_OPT(max_frames, i64, ms->max_frames, oc, st); - for (i = 0; inb_max_frames; i++) { - char *p = o->max_frames[i].specifier; - if (!*p && type != AVMEDIA_TYPE_VIDEO) { - av_log(ost, AV_LOG_WARNING, "Applying unspecific -frames to non video streams, maybe you meant -vframes ?\n"); - break; - } +#if FFMPEG_OPT_FILTER_SCRIPT + if (filters_script && filters) { + av_log(ost, AV_LOG_ERROR, "Both -filter and -filter_script set\n"); + return AVERROR(EINVAL); } - ost->copy_prior_start = -1; - MATCH_PER_STREAM_OPT(copy_prior_start, i, ost->copy_prior_start, oc ,st); - - MATCH_PER_STREAM_OPT(bitstream_filters, str, bsfs, oc, st); - if (bsfs && *bsfs) { - ret = av_bsf_list_parse_str(bsfs, &ms->bsf_ctx); - if (ret < 0) { - av_log(ost, AV_LOG_ERROR, "Error parsing bitstream filter sequence '%s': %s\n", bsfs, av_err2str(ret)); - exit_program(1); - } - } + if (filters_script) + *dst = file_read(filters_script); + else +#endif + if (filters) + *dst = av_strdup(filters); + else + *dst = av_strdup(ost->type == AVMEDIA_TYPE_VIDEO ? "null" : "anull"); + return *dst ? 0 : AVERROR(ENOMEM); +} - MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, oc, st); - if (codec_tag) { - uint32_t tag = strtol(codec_tag, &next, 0); - if (*next) { - uint8_t buf[4] = { 0 }; - memcpy(buf, codec_tag, FFMIN(sizeof(buf), strlen(codec_tag))); - tag = AV_RL32(buf); +static int parse_matrix_coeffs(void *logctx, uint16_t *dest, const char *str) +{ + const char *p = str; + for (int i = 0;; i++) { + dest[i] = atoi(p); + if (i == 63) + break; + p = strchr(p, ','); + if (!p) { + av_log(logctx, AV_LOG_FATAL, + "Syntax error in matrix \"%s\" at coeff %d\n", str, i); + return AVERROR(EINVAL); } - ost->st->codecpar->codec_tag = tag; - if (ost->enc_ctx) - ost->enc_ctx->codec_tag = tag; - } - - MATCH_PER_STREAM_OPT(qscale, dbl, qscale, oc, st); - if (ost->enc_ctx && qscale >= 0) { - ost->enc_ctx->flags |= AV_CODEC_FLAG_QSCALE; - ost->enc_ctx->global_quality = FF_QP2LAMBDA * qscale; - } - - ms->max_muxing_queue_size = 128; - MATCH_PER_STREAM_OPT(max_muxing_queue_size, i, ms->max_muxing_queue_size, oc, st); - - ms->muxing_queue_data_threshold = 50*1024*1024; - MATCH_PER_STREAM_OPT(muxing_queue_data_threshold, i, ms->muxing_queue_data_threshold, oc, st); - - MATCH_PER_STREAM_OPT(bits_per_raw_sample, i, ost->bits_per_raw_sample, - oc, st); - - MATCH_PER_STREAM_OPT(fix_sub_duration_heartbeat, i, ost->fix_sub_duration_heartbeat, - oc, st); - - if (oc->oformat->flags & AVFMT_GLOBALHEADER && ost->enc_ctx) - ost->enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; - - av_dict_copy(&ost->sws_dict, o->g->sws_dict, 0); - - av_dict_copy(&ost->swr_opts, o->g->swr_opts, 0); - if (ost->enc_ctx && av_get_exact_bits_per_sample(ost->enc_ctx->codec_id) == 24) - av_dict_set(&ost->swr_opts, "output_sample_bits", "24", 0); - - if (ost->ist) { - ost->ist->discard = 0; - ost->ist->st->discard = ost->ist->user_set_discard; + p++; } - ost->last_mux_dts = AV_NOPTS_VALUE; - ost->last_filter_pts = AV_NOPTS_VALUE; - MATCH_PER_STREAM_OPT(copy_initial_nonkeyframes, i, - ost->copy_initial_nonkeyframes, oc, st); - - return ost; + return 0; } -static char *get_ost_filters(const OptionsContext *o, AVFormatContext *oc, - OutputStream *ost) +static int fmt_in_list(const int *formats, int format) { - AVStream *st = ost->st; - - if (ost->filters_script && ost->filters) { - av_log(ost, AV_LOG_ERROR, "Both -filter and -filter_script set\n"); - exit_program(1); - } - - if (ost->filters_script) - return file_read(ost->filters_script); - else if (ost->filters) - return av_strdup(ost->filters); - - return av_strdup(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? - "null" : "anull"); + for (; *formats != -1; formats++) + if (*formats == format) + return 1; + return 0; } -static void check_streamcopy_filters(const OptionsContext *o, AVFormatContext *oc, - OutputStream *ost, enum AVMediaType type) +static enum AVPixelFormat +choose_pixel_fmt(const AVCodec *codec, enum AVPixelFormat target) { - if (ost->filters_script || ost->filters) { - av_log(ost, AV_LOG_ERROR, - "%s '%s' was defined, but codec copy was selected.\n" - "Filtering and streamcopy cannot be used together.\n", - ost->filters ? "Filtergraph" : "Filtergraph script", - ost->filters ? ost->filters : ost->filters_script); - exit_program(1); + const enum AVPixelFormat *p = codec->pix_fmts; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(target); + //FIXME: This should check for AV_PIX_FMT_FLAG_ALPHA after PAL8 pixel format without alpha is implemented + int has_alpha = desc ? desc->nb_components % 2 == 0 : 0; + enum AVPixelFormat best= AV_PIX_FMT_NONE; + + for (; *p != AV_PIX_FMT_NONE; p++) { + best = av_find_best_pix_fmt_of_2(best, *p, target, has_alpha, NULL); + if (*p == target) + break; + } + if (*p == AV_PIX_FMT_NONE) { + if (target != AV_PIX_FMT_NONE) + av_log(NULL, AV_LOG_WARNING, + "Incompatible pixel format '%s' for codec '%s', auto-selecting format '%s'\n", + av_get_pix_fmt_name(target), + codec->name, + av_get_pix_fmt_name(best)); + return best; } + return target; } -static void parse_matrix_coeffs(void *logctx, uint16_t *dest, const char *str) +static enum AVPixelFormat pix_fmt_parse(OutputStream *ost, const char *name) { - int i; - const char *p = str; - for (i = 0;; i++) { - dest[i] = atoi(p); - if (i == 63) - break; - p = strchr(p, ','); - if (!p) { - av_log(logctx, AV_LOG_FATAL, - "Syntax error in matrix \"%s\" at coeff %d\n", str, i); - exit_program(1); + const enum AVPixelFormat *fmts = ost->enc_ctx->codec->pix_fmts; + enum AVPixelFormat fmt; + + fmt = av_get_pix_fmt(name); + if (fmt == AV_PIX_FMT_NONE) { + av_log(ost, AV_LOG_FATAL, "Unknown pixel format requested: %s.\n", name); + return AV_PIX_FMT_NONE; + } + + /* when the user specified-format is an alias for an endianness-specific + * one (e.g. rgb48 -> rgb48be/le), it gets translated into the native + * endianness by av_get_pix_fmt(); + * the following code handles the case when the native endianness is not + * supported by the encoder, but the other one is */ + if (fmts && !fmt_in_list(fmts, fmt)) { + const char *name_canonical = av_get_pix_fmt_name(fmt); + int len = strlen(name_canonical); + + if (strcmp(name, name_canonical) && + (!strcmp(name_canonical + len - 2, "le") || + !strcmp(name_canonical + len - 2, "be"))) { + char name_other[64]; + enum AVPixelFormat fmt_other; + + snprintf(name_other, sizeof(name_other), "%s%ce", + name, name_canonical[len - 2] == 'l' ? 'b' : 'l'); + fmt_other = av_get_pix_fmt(name_other); + if (fmt_other != AV_PIX_FMT_NONE && fmt_in_list(fmts, fmt_other)) { + av_log(ost, AV_LOG_VERBOSE, "Mapping pixel format %s->%s\n", + name, name_other); + fmt = fmt_other; + } } - p++; } + + if (fmts && !fmt_in_list(fmts, fmt)) + fmt = choose_pixel_fmt(ost->enc_ctx->codec, fmt); + + return fmt; } -static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, InputStream *ist) +static int new_stream_video(Muxer *mux, const OptionsContext *o, + OutputStream *ost) { AVFormatContext *oc = mux->fc; AVStream *st; - OutputStream *ost; char *frame_rate = NULL, *max_frame_rate = NULL, *frame_aspect_ratio = NULL; + int ret = 0; - ost = new_output_stream(mux, o, AVMEDIA_TYPE_VIDEO, ist); st = ost->st; MATCH_PER_STREAM_OPT(frame_rates, str, frame_rate, oc, st); if (frame_rate && av_parse_video_rate(&ost->frame_rate, frame_rate) < 0) { av_log(ost, AV_LOG_FATAL, "Invalid framerate value: %s\n", frame_rate); - exit_program(1); + return AVERROR(EINVAL); } MATCH_PER_STREAM_OPT(max_frame_rates, str, max_frame_rate, oc, st); if (max_frame_rate && av_parse_video_rate(&ost->max_frame_rate, max_frame_rate) < 0) { av_log(ost, AV_LOG_FATAL, "Invalid maximum framerate value: %s\n", max_frame_rate); - exit_program(1); + return AVERROR(EINVAL); } if (frame_rate && max_frame_rate) { av_log(ost, AV_LOG_ERROR, "Only one of -fpsmax and -r can be set for a stream.\n"); - exit_program(1); + return AVERROR(EINVAL); } MATCH_PER_STREAM_OPT(frame_aspect_ratios, str, frame_aspect_ratio, oc, st); @@ -739,14 +612,11 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input if (av_parse_ratio(&q, frame_aspect_ratio, 255, 0, NULL) < 0 || q.num <= 0 || q.den <= 0) { av_log(ost, AV_LOG_FATAL, "Invalid aspect ratio: %s\n", frame_aspect_ratio); - exit_program(1); + return AVERROR(EINVAL); } ost->frame_aspect_ratio = q; } - MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, st); - MATCH_PER_STREAM_OPT(filters, str, ost->filters, oc, st); - if (ost->enc_ctx) { AVCodecContext *video_enc = ost->enc_ctx; const char *p = NULL, *fps_mode = NULL; @@ -758,9 +628,12 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input int i; MATCH_PER_STREAM_OPT(frame_sizes, str, frame_size, oc, st); - if (frame_size && av_parse_video_size(&video_enc->width, &video_enc->height, frame_size) < 0) { - av_log(ost, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size); - exit_program(1); + if (frame_size) { + ret = av_parse_video_size(&video_enc->width, &video_enc->height, frame_size); + if (ret < 0) { + av_log(ost, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size); + return AVERROR(EINVAL); + } } MATCH_PER_STREAM_OPT(frame_pix_fmts, str, frame_pix_fmt, oc, st); @@ -769,31 +642,38 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input if (!*++frame_pix_fmt) frame_pix_fmt = NULL; } - if (frame_pix_fmt && (video_enc->pix_fmt = av_get_pix_fmt(frame_pix_fmt)) == AV_PIX_FMT_NONE) { - av_log(ost, AV_LOG_FATAL, "Unknown pixel format requested: %s.\n", frame_pix_fmt); - exit_program(1); + if (frame_pix_fmt) { + video_enc->pix_fmt = pix_fmt_parse(ost, frame_pix_fmt); + if (video_enc->pix_fmt == AV_PIX_FMT_NONE) + return AVERROR(EINVAL); } - st->sample_aspect_ratio = video_enc->sample_aspect_ratio; MATCH_PER_STREAM_OPT(intra_matrices, str, intra_matrix, oc, st); if (intra_matrix) { if (!(video_enc->intra_matrix = av_mallocz(sizeof(*video_enc->intra_matrix) * 64))) - report_and_exit(AVERROR(ENOMEM)); - parse_matrix_coeffs(ost, video_enc->intra_matrix, intra_matrix); + return AVERROR(ENOMEM); + + ret = parse_matrix_coeffs(ost, video_enc->intra_matrix, intra_matrix); + if (ret < 0) + return ret; } MATCH_PER_STREAM_OPT(chroma_intra_matrices, str, chroma_intra_matrix, oc, st); if (chroma_intra_matrix) { uint16_t *p = av_mallocz(sizeof(*video_enc->chroma_intra_matrix) * 64); if (!p) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); video_enc->chroma_intra_matrix = p; - parse_matrix_coeffs(ost, p, chroma_intra_matrix); + ret = parse_matrix_coeffs(ost, p, chroma_intra_matrix); + if (ret < 0) + return ret; } MATCH_PER_STREAM_OPT(inter_matrices, str, inter_matrix, oc, st); if (inter_matrix) { if (!(video_enc->inter_matrix = av_mallocz(sizeof(*video_enc->inter_matrix) * 64))) - report_and_exit(AVERROR(ENOMEM)); - parse_matrix_coeffs(ost, video_enc->inter_matrix, inter_matrix); + return AVERROR(ENOMEM); + ret = parse_matrix_coeffs(ost, video_enc->inter_matrix, inter_matrix); + if (ret < 0) + return ret; } MATCH_PER_STREAM_OPT(rc_overrides, str, p, oc, st); @@ -802,14 +682,14 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input int e = sscanf(p, "%d,%d,%d", &start, &end, &q); if (e != 3) { av_log(ost, AV_LOG_FATAL, "error parsing rc_override\n"); - exit_program(1); + return AVERROR(EINVAL); } video_enc->rc_override = av_realloc_array(video_enc->rc_override, i + 1, sizeof(RcOverride)); if (!video_enc->rc_override) { av_log(ost, AV_LOG_FATAL, "Could not (re)allocate memory for rc_override.\n"); - exit_program(1); + return AVERROR(ENOMEM); } video_enc->rc_override[i].start_frame = start; video_enc->rc_override[i].end_frame = end; @@ -826,13 +706,6 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input } video_enc->rc_override_count = i; -#if FFMPEG_OPT_PSNR - if (do_psnr) { - av_log(ost, AV_LOG_WARNING, "The -psnr option is deprecated, use -flags +psnr\n"); - video_enc->flags|= AV_CODEC_FLAG_PSNR; - } -#endif - /* two pass mode */ MATCH_PER_STREAM_OPT(pass, i, do_pass, oc, st); if (do_pass) { @@ -849,7 +722,7 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input MATCH_PER_STREAM_OPT(passlogfiles, str, ost->logfile_prefix, oc, st); if (ost->logfile_prefix && !(ost->logfile_prefix = av_strdup(ost->logfile_prefix))) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); if (do_pass) { int ost_idx = -1; @@ -857,7 +730,7 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input FILE *f; /* compute this stream's global index */ - for (int i = 0; i <= ost->file_index; i++) + for (int i = 0; i <= ost->file->index; i++) ost_idx += output_files[i]->nb_streams; snprintf(logfilename, sizeof(logfilename), "%s-%d.log", @@ -873,7 +746,7 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input if (!logbuffer) { av_log(ost, AV_LOG_FATAL, "Error reading log file '%s' for pass-2 encoding\n", logfilename); - exit_program(1); + return AVERROR(EIO); } video_enc->stats_in = logbuffer; } @@ -883,7 +756,7 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input av_log(ost, AV_LOG_FATAL, "Cannot write log file '%s' for pass-1 encoding: %s\n", logfilename, strerror(errno)); - exit_program(1); + return AVERROR(errno); } ost->logfile = f; } @@ -892,20 +765,31 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input MATCH_PER_STREAM_OPT(force_fps, i, ost->force_fps, oc, st); +#if FFMPEG_OPT_TOP ost->top_field_first = -1; MATCH_PER_STREAM_OPT(top_field_first, i, ost->top_field_first, oc, st); + if (ost->top_field_first >= 0) + av_log(ost, AV_LOG_WARNING, "-top is deprecated, use the setfield filter instead\n"); +#endif +#if FFMPEG_OPT_VSYNC ost->vsync_method = video_sync_method; +#else + ost->vsync_method = VSYNC_AUTO; +#endif MATCH_PER_STREAM_OPT(fps_mode, str, fps_mode, oc, st); - if (fps_mode) - parse_and_set_vsync(fps_mode, &ost->vsync_method, ost->file_index, ost->index, 0); + if (fps_mode) { + ret = parse_and_set_vsync(fps_mode, &ost->vsync_method, ost->file->index, ost->index, 0); + if (ret < 0) + return ret; + } if ((ost->frame_rate.num || ost->max_frame_rate.num) && !(ost->vsync_method == VSYNC_AUTO || ost->vsync_method == VSYNC_CFR || ost->vsync_method == VSYNC_VSCFR)) { av_log(ost, AV_LOG_FATAL, "One of -r/-fpsmax was specified " "together a non-CFR -vsync/-fps_mode. This is contradictory.\n"); - exit_program(1); + return AVERROR(EINVAL); } if (ost->vsync_method == VSYNC_AUTO) { @@ -921,7 +805,7 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input } if (ost->ist && ost->vsync_method == VSYNC_CFR) { - const InputFile *ifile = input_files[ost->ist->file_index]; + const InputFile *ifile = ost->ist->file; if (ifile->nb_streams == 1 && ifile->input_ts_offset == 0) ost->vsync_method = VSYNC_VSCFR; @@ -932,38 +816,23 @@ static OutputStream *new_video_stream(Muxer *mux, const OptionsContext *o, Input } } ost->is_cfr = (ost->vsync_method == VSYNC_CFR || ost->vsync_method == VSYNC_VSCFR); + } - ost->avfilter = get_ost_filters(o, oc, ost); - if (!ost->avfilter) - exit_program(1); - - ost->last_frame = av_frame_alloc(); - if (!ost->last_frame) - report_and_exit(AVERROR(ENOMEM)); - } else - check_streamcopy_filters(o, oc, ost, AVMEDIA_TYPE_VIDEO); - - return ost; + return 0; } -static OutputStream *new_audio_stream(Muxer *mux, const OptionsContext *o, InputStream *ist) +static int new_stream_audio(Muxer *mux, const OptionsContext *o, + OutputStream *ost) { AVFormatContext *oc = mux->fc; - AVStream *st; - OutputStream *ost; - - ost = new_output_stream(mux, o, AVMEDIA_TYPE_AUDIO, ist); - st = ost->st; - - - MATCH_PER_STREAM_OPT(filter_scripts, str, ost->filters_script, oc, st); - MATCH_PER_STREAM_OPT(filters, str, ost->filters, oc, st); + AVStream *st = ost->st; if (ost->enc_ctx) { AVCodecContext *audio_enc = ost->enc_ctx; int channels = 0; char *layout = NULL; char *sample_fmt = NULL; + const char *apad = NULL; MATCH_PER_STREAM_OPT(audio_channels, i, channels, oc, st); if (channels) { @@ -972,175 +841,601 @@ static OutputStream *new_audio_stream(Muxer *mux, const OptionsContext *o, Input } MATCH_PER_STREAM_OPT(audio_ch_layouts, str, layout, oc, st); - if (layout) { - if (av_channel_layout_from_string(&audio_enc->ch_layout, layout) < 0) { -#if FF_API_OLD_CHANNEL_LAYOUT - uint64_t mask; - AV_NOWARN_DEPRECATED({ - mask = av_get_channel_layout(layout); - }) - if (!mask) { -#endif - av_log(ost, AV_LOG_FATAL, "Unknown channel layout: %s\n", layout); - exit_program(1); -#if FF_API_OLD_CHANNEL_LAYOUT - } - av_log(ost, AV_LOG_WARNING, "Channel layout '%s' uses a deprecated syntax.\n", - layout); - av_channel_layout_from_mask(&audio_enc->ch_layout, mask); -#endif - } + if (layout && av_channel_layout_from_string(&audio_enc->ch_layout, layout) < 0) { + av_log(ost, AV_LOG_FATAL, "Unknown channel layout: %s\n", layout); + return AVERROR(EINVAL); } MATCH_PER_STREAM_OPT(sample_fmts, str, sample_fmt, oc, st); if (sample_fmt && (audio_enc->sample_fmt = av_get_sample_fmt(sample_fmt)) == AV_SAMPLE_FMT_NONE) { av_log(ost, AV_LOG_FATAL, "Invalid sample format '%s'\n", sample_fmt); - exit_program(1); + return AVERROR(EINVAL); } MATCH_PER_STREAM_OPT(audio_sample_rate, i, audio_enc->sample_rate, oc, st); - MATCH_PER_STREAM_OPT(apad, str, ost->apad, oc, st); - ost->apad = av_strdup(ost->apad); - - ost->avfilter = get_ost_filters(o, oc, ost); - if (!ost->avfilter) - exit_program(1); - -#if FFMPEG_OPT_MAP_CHANNEL - /* check for channel mapping for this audio stream */ - for (int n = 0; n < o->nb_audio_channel_maps; n++) { - AudioChannelMap *map = &o->audio_channel_maps[n]; - if ((map->ofile_idx == -1 || ost->file_index == map->ofile_idx) && - (map->ostream_idx == -1 || ost->st->index == map->ostream_idx)) { - InputStream *ist; - - if (map->channel_idx == -1) { - ist = NULL; - } else if (!ost->ist) { - av_log(ost, AV_LOG_FATAL, "Cannot determine input stream for channel mapping %d.%d\n", - ost->file_index, ost->st->index); - continue; - } else { - ist = ost->ist; - } - - if (!ist || (ist->file_index == map->file_idx && ist->st->index == map->stream_idx)) { - if (av_reallocp_array(&ost->audio_channels_map, - ost->audio_channels_mapped + 1, - sizeof(*ost->audio_channels_map) - ) < 0 ) - report_and_exit(AVERROR(ENOMEM)); - - ost->audio_channels_map[ost->audio_channels_mapped++] = map->channel_idx; - } - } + MATCH_PER_STREAM_OPT(apad, str, apad, oc, st); + if (apad) { + ost->apad = av_strdup(apad); + if (!ost->apad) + return AVERROR(ENOMEM); } -#endif - } else - check_streamcopy_filters(o, oc, ost, AVMEDIA_TYPE_AUDIO); + } - return ost; + return 0; } -static OutputStream *new_data_stream(Muxer *mux, const OptionsContext *o, InputStream *ist) +static int new_stream_subtitle(Muxer *mux, const OptionsContext *o, + OutputStream *ost) { - OutputStream *ost; + AVStream *st; + + st = ost->st; - ost = new_output_stream(mux, o, AVMEDIA_TYPE_DATA, ist); if (ost->enc_ctx) { - av_log(ost, AV_LOG_FATAL, "Data stream encoding not supported yet (only streamcopy)\n"); - exit_program(1); + AVCodecContext *subtitle_enc = ost->enc_ctx; + + AVCodecDescriptor const *input_descriptor = + avcodec_descriptor_get(ost->ist->par->codec_id); + AVCodecDescriptor const *output_descriptor = + avcodec_descriptor_get(subtitle_enc->codec_id); + int input_props = 0, output_props = 0; + + char *frame_size = NULL; + + MATCH_PER_STREAM_OPT(frame_sizes, str, frame_size, mux->fc, st); + if (frame_size) { + int ret = av_parse_video_size(&subtitle_enc->width, &subtitle_enc->height, frame_size); + if (ret < 0) { + av_log(ost, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size); + return ret; + } + } + if (input_descriptor) + input_props = input_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); + if (output_descriptor) + output_props = output_descriptor->props & (AV_CODEC_PROP_TEXT_SUB | AV_CODEC_PROP_BITMAP_SUB); + if (input_props && output_props && input_props != output_props) { + av_log(ost, AV_LOG_ERROR, + "Subtitle encoding currently only possible from text to text " + "or bitmap to bitmap\n"); + return AVERROR(EINVAL); + } } - return ost; + return 0; } -static OutputStream *new_unknown_stream(Muxer *mux, const OptionsContext *o, InputStream *ist) +static int streamcopy_init(const Muxer *mux, OutputStream *ost) { - OutputStream *ost; + MuxStream *ms = ms_from_ost(ost); - ost = new_output_stream(mux, o, AVMEDIA_TYPE_UNKNOWN, ist); - if (ost->enc_ctx) { - av_log(ost, AV_LOG_FATAL, "Unknown stream encoding not supported yet (only streamcopy)\n"); - exit_program(1); + const InputStream *ist = ost->ist; + const InputFile *ifile = ist->file; + + AVCodecParameters *par = ost->par_in; + uint32_t codec_tag = par->codec_tag; + + AVCodecContext *codec_ctx = NULL; + AVDictionary *codec_opts = NULL; + + AVRational fr = ost->frame_rate; + + int ret = 0; + + codec_ctx = avcodec_alloc_context3(NULL); + if (!codec_ctx) + return AVERROR(ENOMEM); + + ret = avcodec_parameters_to_context(codec_ctx, ist->par); + if (ret >= 0) + ret = av_opt_set_dict(codec_ctx, &ost->encoder_opts); + if (ret < 0) { + av_log(ost, AV_LOG_FATAL, + "Error setting up codec context options.\n"); + goto fail; } - return ost; -} + ret = avcodec_parameters_from_context(par, codec_ctx); + if (ret < 0) { + av_log(ost, AV_LOG_FATAL, + "Error getting reference codec parameters.\n"); + goto fail; + } -static OutputStream *new_attachment_stream(Muxer *mux, const OptionsContext *o, InputStream *ist) -{ - OutputStream *ost = new_output_stream(mux, o, AVMEDIA_TYPE_ATTACHMENT, ist); - ost->finished = 1; - return ost; + if (!codec_tag) { + const struct AVCodecTag * const *ct = mux->fc->oformat->codec_tag; + unsigned int codec_tag_tmp; + if (!ct || av_codec_get_id (ct, par->codec_tag) == par->codec_id || + !av_codec_get_tag2(ct, par->codec_id, &codec_tag_tmp)) + codec_tag = par->codec_tag; + } + + par->codec_tag = codec_tag; + + if (!fr.num) + fr = ist->framerate; + + if (fr.num) + ost->st->avg_frame_rate = fr; + else + ost->st->avg_frame_rate = ist->st->avg_frame_rate; + + ret = avformat_transfer_internal_stream_timing_info(mux->fc->oformat, + ost->st, ist->st, copy_tb); + if (ret < 0) + goto fail; + + // copy timebase while removing common factors + if (ost->st->time_base.num <= 0 || ost->st->time_base.den <= 0) { + if (fr.num) + ost->st->time_base = av_inv_q(fr); + else + ost->st->time_base = av_add_q(av_stream_get_codec_timebase(ost->st), (AVRational){0, 1}); + } + + if (!ms->copy_prior_start) { + ms->ts_copy_start = (mux->of.start_time == AV_NOPTS_VALUE) ? + 0 : mux->of.start_time; + if (copy_ts && ifile->start_time != AV_NOPTS_VALUE) { + ms->ts_copy_start = FFMAX(ms->ts_copy_start, + ifile->start_time + ifile->ts_offset); + } + } + + for (int i = 0; i < ist->st->codecpar->nb_coded_side_data; i++) { + const AVPacketSideData *sd_src = &ist->st->codecpar->coded_side_data[i]; + AVPacketSideData *sd_dst; + + sd_dst = av_packet_side_data_new(&ost->st->codecpar->coded_side_data, + &ost->st->codecpar->nb_coded_side_data, + sd_src->type, sd_src->size, 0); + if (!sd_dst) { + ret = AVERROR(ENOMEM); + goto fail; + } + memcpy(sd_dst->data, sd_src->data, sd_src->size); + } + + switch (par->codec_type) { + case AVMEDIA_TYPE_AUDIO: + if ((par->block_align == 1 || par->block_align == 1152 || par->block_align == 576) && + par->codec_id == AV_CODEC_ID_MP3) + par->block_align = 0; + if (par->codec_id == AV_CODEC_ID_AC3) + par->block_align = 0; + break; + case AVMEDIA_TYPE_VIDEO: { + AVRational sar; + if (ost->frame_aspect_ratio.num) { // overridden by the -aspect cli option + sar = + av_mul_q(ost->frame_aspect_ratio, + (AVRational){ par->height, par->width }); + av_log(ost, AV_LOG_WARNING, "Overriding aspect ratio " + "with stream copy may produce invalid files\n"); + } + else if (ist->st->sample_aspect_ratio.num) + sar = ist->st->sample_aspect_ratio; + else + sar = par->sample_aspect_ratio; + ost->st->sample_aspect_ratio = par->sample_aspect_ratio = sar; + ost->st->avg_frame_rate = ist->st->avg_frame_rate; + ost->st->r_frame_rate = ist->st->r_frame_rate; + break; + } + } + +fail: + avcodec_free_context(&codec_ctx); + av_dict_free(&codec_opts); + return ret; } -static OutputStream *new_subtitle_stream(Muxer *mux, const OptionsContext *o, InputStream *ist) +static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, + InputStream *ist, OutputFilter *ofilter, + OutputStream **post) { - AVStream *st; + AVFormatContext *oc = mux->fc; + MuxStream *ms; OutputStream *ost; + const AVCodec *enc; + AVStream *st; + int ret = 0; + const char *bsfs = NULL, *time_base = NULL; + char *filters = NULL, *next, *codec_tag = NULL; + double qscale = -1; - ost = new_output_stream(mux, o, AVMEDIA_TYPE_SUBTITLE, ist); - st = ost->st; + st = avformat_new_stream(oc, NULL); + if (!st) + return AVERROR(ENOMEM); + + ms = mux_stream_alloc(mux, type); + if (!ms) + return AVERROR(ENOMEM); + + // only streams with sources (i.e. not attachments) + // are handled by the scheduler + if (ist || ofilter) { + ret = GROW_ARRAY(mux->sch_stream_idx, mux->nb_sch_stream_idx); + if (ret < 0) + return ret; + + ret = sch_add_mux_stream(mux->sch, mux->sch_idx); + if (ret < 0) + return ret; + + av_assert0(ret == mux->nb_sch_stream_idx - 1); + mux->sch_stream_idx[ret] = ms->ost.index; + ms->sch_idx = ret; + } + + ost = &ms->ost; + + if (o->streamid) { + AVDictionaryEntry *e; + char idx[16], *p; + snprintf(idx, sizeof(idx), "%d", ost->index); + + e = av_dict_get(o->streamid, idx, NULL, 0); + if (e) { + st->id = strtol(e->value, &p, 0); + if (!e->value[0] || *p) { + av_log(ost, AV_LOG_FATAL, "Invalid stream id: %s\n", e->value); + return AVERROR(EINVAL); + } + } + } + + ost->par_in = avcodec_parameters_alloc(); + if (!ost->par_in) + return AVERROR(ENOMEM); + + ms->last_mux_dts = AV_NOPTS_VALUE; + + ost->st = st; + ost->ist = ist; + ost->kf.ref_pts = AV_NOPTS_VALUE; + ost->par_in->codec_type = type; + st->codecpar->codec_type = type; + + ret = choose_encoder(o, oc, ost, &enc); + if (ret < 0) { + av_log(ost, AV_LOG_FATAL, "Error selecting an encoder\n"); + return ret; + } + + if (enc) { + ost->enc_ctx = avcodec_alloc_context3(enc); + if (!ost->enc_ctx) + return AVERROR(ENOMEM); + + ret = sch_add_enc(mux->sch, encoder_thread, ost, + ost->type == AVMEDIA_TYPE_SUBTITLE ? NULL : enc_open); + if (ret < 0) + return ret; + ms->sch_idx_enc = ret; + + ret = enc_alloc(&ost->enc, enc, mux->sch, ms->sch_idx_enc); + if (ret < 0) + return ret; + + av_strlcat(ms->log_name, "/", sizeof(ms->log_name)); + av_strlcat(ms->log_name, enc->name, sizeof(ms->log_name)); + } else { + if (ofilter) { + av_log(ost, AV_LOG_ERROR, + "Streamcopy requested for output stream fed " + "from a complex filtergraph. Filtering and streamcopy " + "cannot be used together.\n"); + return AVERROR(EINVAL); + } + + av_strlcat(ms->log_name, "/copy", sizeof(ms->log_name)); + } + + av_log(ost, AV_LOG_VERBOSE, "Created %s stream from ", + av_get_media_type_string(type)); + if (ist) + av_log(ost, AV_LOG_VERBOSE, "input stream %d:%d", + ist->file->index, ist->index); + else if (ofilter) + av_log(ost, AV_LOG_VERBOSE, "complex filtergraph %d:[%s]\n", + ofilter->graph->index, ofilter->name); + else if (type == AVMEDIA_TYPE_ATTACHMENT) + av_log(ost, AV_LOG_VERBOSE, "attached file"); + else av_assert0(0); + av_log(ost, AV_LOG_VERBOSE, "\n"); + + ms->pkt = av_packet_alloc(); + if (!ms->pkt) + return AVERROR(ENOMEM); if (ost->enc_ctx) { - AVCodecContext *subtitle_enc = ost->enc_ctx; - char *frame_size = NULL; + AVCodecContext *enc = ost->enc_ctx; + AVIOContext *s = NULL; + char *buf = NULL, *arg = NULL, *preset = NULL; + const char *enc_stats_pre = NULL, *enc_stats_post = NULL, *mux_stats = NULL; + const char *enc_time_base = NULL; - MATCH_PER_STREAM_OPT(frame_sizes, str, frame_size, mux->fc, st); - if (frame_size && av_parse_video_size(&subtitle_enc->width, &subtitle_enc->height, frame_size) < 0) { - av_log(ost, AV_LOG_FATAL, "Invalid frame size: %s.\n", frame_size); - exit_program(1); + ret = filter_codec_opts(o->g->codec_opts, enc->codec_id, + oc, st, enc->codec, &ost->encoder_opts); + if (ret < 0) + return ret; + + MATCH_PER_STREAM_OPT(presets, str, preset, oc, st); + ost->autoscale = 1; + MATCH_PER_STREAM_OPT(autoscale, i, ost->autoscale, oc, st); + if (preset && (!(ret = get_preset_file_2(preset, enc->codec->name, &s)))) { + AVBPrint bprint; + av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED); + do { + av_bprint_clear(&bprint); + buf = get_line(s, &bprint); + if (!buf) { + ret = AVERROR(ENOMEM); + break; + } + + if (!buf[0] || buf[0] == '#') + continue; + if (!(arg = strchr(buf, '='))) { + av_log(ost, AV_LOG_FATAL, "Invalid line found in the preset file.\n"); + ret = AVERROR(EINVAL); + break; + } + *arg++ = 0; + av_dict_set(&ost->encoder_opts, buf, arg, AV_DICT_DONT_OVERWRITE); + } while (!s->eof_reached); + av_bprint_finalize(&bprint, NULL); + avio_closep(&s); + } + if (ret) { + av_log(ost, AV_LOG_FATAL, + "Preset %s specified, but could not be opened.\n", preset); + return ret; + } + + MATCH_PER_STREAM_OPT(enc_stats_pre, str, enc_stats_pre, oc, st); + if (enc_stats_pre && + (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { + const char *format = "{fidx} {sidx} {n} {t}"; + + MATCH_PER_STREAM_OPT(enc_stats_pre_fmt, str, format, oc, st); + + ret = enc_stats_init(ost, &ost->enc_stats_pre, 1, enc_stats_pre, format); + if (ret < 0) + return ret; + } + + MATCH_PER_STREAM_OPT(enc_stats_post, str, enc_stats_post, oc, st); + if (enc_stats_post && + (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { + const char *format = "{fidx} {sidx} {n} {t}"; + + MATCH_PER_STREAM_OPT(enc_stats_post_fmt, str, format, oc, st); + + ret = enc_stats_init(ost, &ost->enc_stats_post, 0, enc_stats_post, format); + if (ret < 0) + return ret; } + + MATCH_PER_STREAM_OPT(mux_stats, str, mux_stats, oc, st); + if (mux_stats && + (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { + const char *format = "{fidx} {sidx} {n} {t}"; + + MATCH_PER_STREAM_OPT(mux_stats_fmt, str, format, oc, st); + + ret = enc_stats_init(ost, &ms->stats, 0, mux_stats, format); + if (ret < 0) + return ret; + } + + MATCH_PER_STREAM_OPT(enc_time_bases, str, enc_time_base, oc, st); + if (enc_time_base) { + AVRational q; + if (!strcmp(enc_time_base, "demux")) { + q = (AVRational){ ENC_TIME_BASE_DEMUX, 0 }; + } else if (!strcmp(enc_time_base, "filter")) { + q = (AVRational){ ENC_TIME_BASE_FILTER, 0 }; + } else { + ret = av_parse_ratio(&q, enc_time_base, INT_MAX, 0, NULL); + if (ret < 0 || q.den <= 0 +#if !FFMPEG_OPT_ENC_TIME_BASE_NUM + || q.num < 0 +#endif + ) { + av_log(ost, AV_LOG_FATAL, "Invalid time base: %s\n", enc_time_base); + return ret < 0 ? ret : AVERROR(EINVAL); + } +#if FFMPEG_OPT_ENC_TIME_BASE_NUM + if (q.num < 0) + av_log(ost, AV_LOG_WARNING, "-enc_time_base -1 is deprecated," + " use -enc_timebase demux\n"); +#endif + } + + ost->enc_timebase = q; + } + } else { + ret = filter_codec_opts(o->g->codec_opts, AV_CODEC_ID_NONE, oc, st, + NULL, &ost->encoder_opts); + if (ret < 0) + return ret; } - return ost; -} -static void init_output_filter(OutputFilter *ofilter, const OptionsContext *o, - Muxer *mux) -{ - OutputStream *ost; + if (o->bitexact) { + ost->bitexact = 1; + } else if (ost->enc_ctx) { + ost->bitexact = check_opt_bitexact(ost->enc_ctx, ost->encoder_opts, "flags", + AV_CODEC_FLAG_BITEXACT); + } - switch (ofilter->type) { - case AVMEDIA_TYPE_VIDEO: ost = new_video_stream(mux, o, NULL); break; - case AVMEDIA_TYPE_AUDIO: ost = new_audio_stream(mux, o, NULL); break; - default: - av_log(mux, AV_LOG_FATAL, "Only video and audio filters are supported " - "currently.\n"); - exit_program(1); + MATCH_PER_STREAM_OPT(time_bases, str, time_base, oc, st); + if (time_base) { + AVRational q; + if (av_parse_ratio(&q, time_base, INT_MAX, 0, NULL) < 0 || + q.num <= 0 || q.den <= 0) { + av_log(ost, AV_LOG_FATAL, "Invalid time base: %s\n", time_base); + return AVERROR(EINVAL); + } + st->time_base = q; } - ost->filter = ofilter; + ms->max_frames = INT64_MAX; + MATCH_PER_STREAM_OPT(max_frames, i64, ms->max_frames, oc, st); + for (int i = 0; i < o->max_frames.nb_opt; i++) { + char *p = o->max_frames.opt[i].specifier; + if (!*p && type != AVMEDIA_TYPE_VIDEO) { + av_log(ost, AV_LOG_WARNING, "Applying unspecific -frames to non video streams, maybe you meant -vframes ?\n"); + break; + } + } - ofilter->ost = ost; - ofilter->format = -1; + ms->copy_prior_start = -1; + MATCH_PER_STREAM_OPT(copy_prior_start, i, ms->copy_prior_start, oc ,st); - if (!ost->enc_ctx) { - av_log(ost, AV_LOG_ERROR, "Streamcopy requested for output stream fed " - "from a complex filtergraph. Filtering and streamcopy " - "cannot be used together.\n"); - exit_program(1); + MATCH_PER_STREAM_OPT(bitstream_filters, str, bsfs, oc, st); + if (bsfs && *bsfs) { + ret = av_bsf_list_parse_str(bsfs, &ms->bsf_ctx); + if (ret < 0) { + av_log(ost, AV_LOG_ERROR, "Error parsing bitstream filter sequence '%s': %s\n", bsfs, av_err2str(ret)); + return ret; + } } - if (ost->avfilter && (ost->filters || ost->filters_script)) { - const char *opt = ost->filters ? "-vf/-af/-filter" : "-filter_script"; - av_log(ost, AV_LOG_ERROR, - "%s '%s' was specified through the %s option " - "for output stream %d:%d, which is fed from a complex filtergraph.\n" - "%s and -filter_complex cannot be used together for the same stream.\n", - ost->filters ? "Filtergraph" : "Filtergraph script", - ost->filters ? ost->filters : ost->filters_script, - opt, ost->file_index, ost->index, opt); - exit_program(1); + MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, oc, st); + if (codec_tag) { + uint32_t tag = strtol(codec_tag, &next, 0); + if (*next) { + uint8_t buf[4] = { 0 }; + memcpy(buf, codec_tag, FFMIN(sizeof(buf), strlen(codec_tag))); + tag = AV_RL32(buf); + } + ost->st->codecpar->codec_tag = tag; + ost->par_in->codec_tag = tag; + if (ost->enc_ctx) + ost->enc_ctx->codec_tag = tag; + } + + MATCH_PER_STREAM_OPT(qscale, dbl, qscale, oc, st); + if (ost->enc_ctx && qscale >= 0) { + ost->enc_ctx->flags |= AV_CODEC_FLAG_QSCALE; + ost->enc_ctx->global_quality = FF_QP2LAMBDA * qscale; + } + + if (ms->sch_idx >= 0) { + int max_muxing_queue_size = 128; + int muxing_queue_data_threshold = 50 * 1024 * 1024; + + MATCH_PER_STREAM_OPT(max_muxing_queue_size, i, max_muxing_queue_size, oc, st); + MATCH_PER_STREAM_OPT(muxing_queue_data_threshold, i, muxing_queue_data_threshold, oc, st); + + sch_mux_stream_buffering(mux->sch, mux->sch_idx, ms->sch_idx, + max_muxing_queue_size, muxing_queue_data_threshold); } - avfilter_inout_free(&ofilter->out_tmp); + MATCH_PER_STREAM_OPT(bits_per_raw_sample, i, ost->bits_per_raw_sample, + oc, st); + + MATCH_PER_STREAM_OPT(fix_sub_duration_heartbeat, i, ost->fix_sub_duration_heartbeat, + oc, st); + + if (oc->oformat->flags & AVFMT_GLOBALHEADER && ost->enc_ctx) + ost->enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + + av_dict_copy(&ost->sws_dict, o->g->sws_dict, 0); + + av_dict_copy(&ost->swr_opts, o->g->swr_opts, 0); + if (ost->enc_ctx && av_get_exact_bits_per_sample(ost->enc_ctx->codec_id) == 24) + av_dict_set(&ost->swr_opts, "output_sample_bits", "24", 0); + + MATCH_PER_STREAM_OPT(copy_initial_nonkeyframes, i, + ms->copy_initial_nonkeyframes, oc, st); + + switch (type) { + case AVMEDIA_TYPE_VIDEO: ret = new_stream_video (mux, o, ost); break; + case AVMEDIA_TYPE_AUDIO: ret = new_stream_audio (mux, o, ost); break; + case AVMEDIA_TYPE_SUBTITLE: ret = new_stream_subtitle (mux, o, ost); break; + } + if (ret < 0) + return ret; + + if (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO) { + ret = ost_get_filters(o, oc, ost, &filters); + if (ret < 0) + return ret; + } + + if (ost->enc && + (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { + if (ofilter) { + ost->filter = ofilter; + ret = ofilter_bind_ost(ofilter, ost, ms->sch_idx_enc); + if (ret < 0) + return ret; + } else { + ret = init_simple_filtergraph(ost->ist, ost, filters, + mux->sch, ms->sch_idx_enc); + if (ret < 0) { + av_log(ost, AV_LOG_ERROR, + "Error initializing a simple filtergraph\n"); + return ret; + } + } + + ret = sch_connect(mux->sch, SCH_ENC(ms->sch_idx_enc), + SCH_MSTREAM(mux->sch_idx, ms->sch_idx)); + if (ret < 0) + return ret; + } else if (ost->ist) { + int sched_idx = ist_output_add(ost->ist, ost); + if (sched_idx < 0) { + av_log(ost, AV_LOG_ERROR, + "Error binding an input stream\n"); + return sched_idx; + } + ms->sch_idx_src = sched_idx; + + if (ost->enc) { + ret = sch_connect(mux->sch, SCH_DEC(sched_idx), + SCH_ENC(ms->sch_idx_enc)); + if (ret < 0) + return ret; + + ret = sch_connect(mux->sch, SCH_ENC(ms->sch_idx_enc), + SCH_MSTREAM(mux->sch_idx, ms->sch_idx)); + if (ret < 0) + return ret; + } else { + ret = sch_connect(mux->sch, SCH_DSTREAM(ost->ist->file->index, sched_idx), + SCH_MSTREAM(ost->file->index, ms->sch_idx)); + if (ret < 0) + return ret; + } + } + + if (ost->ist && !ost->enc) { + ret = streamcopy_init(mux, ost); + if (ret < 0) + return ret; + } + + // copy estimated duration as a hint to the muxer + if (ost->ist && ost->ist->st->duration > 0) { + ms->stream_duration = ist->st->duration; + ms->stream_duration_tb = ist->st->time_base; + } + + if (post) + *post = ost; + + return 0; } -static void map_auto_video(Muxer *mux, const OptionsContext *o) +static int map_auto_video(Muxer *mux, const OptionsContext *o) { AVFormatContext *oc = mux->fc; InputStream *best_ist = NULL; @@ -1149,7 +1444,7 @@ static void map_auto_video(Muxer *mux, const OptionsContext *o) /* video: highest resolution */ if (av_guess_codec(oc->oformat, NULL, oc->url, NULL, AVMEDIA_TYPE_VIDEO) == AV_CODEC_ID_NONE) - return; + return 0; qcr = avformat_query_codec(oc->oformat, oc->oformat->video_codec, 0); for (int j = 0; j < nb_input_files; j++) { @@ -1188,10 +1483,12 @@ static void map_auto_video(Muxer *mux, const OptionsContext *o) } } if (best_ist) - new_video_stream(mux, o, best_ist); + return ost_add(mux, o, AVMEDIA_TYPE_VIDEO, best_ist, NULL, NULL); + + return 0; } -static void map_auto_audio(Muxer *mux, const OptionsContext *o) +static int map_auto_audio(Muxer *mux, const OptionsContext *o) { AVFormatContext *oc = mux->fc; InputStream *best_ist = NULL; @@ -1199,7 +1496,7 @@ static void map_auto_audio(Muxer *mux, const OptionsContext *o) /* audio: most channels */ if (av_guess_codec(oc->oformat, NULL, oc->url, NULL, AVMEDIA_TYPE_AUDIO) == AV_CODEC_ID_NONE) - return; + return 0; for (int j = 0; j < nb_input_files; j++) { InputFile *ifile = input_files[j]; @@ -1230,18 +1527,20 @@ static void map_auto_audio(Muxer *mux, const OptionsContext *o) } } if (best_ist) - new_audio_stream(mux, o, best_ist); + return ost_add(mux, o, AVMEDIA_TYPE_AUDIO, best_ist, NULL, NULL); + + return 0; } -static void map_auto_subtitle(Muxer *mux, const OptionsContext *o) +static int map_auto_subtitle(Muxer *mux, const OptionsContext *o) { AVFormatContext *oc = mux->fc; - char *subtitle_codec_name = NULL; + const char *subtitle_codec_name = NULL; /* subtitles: pick first */ - MATCH_PER_TYPE_OPT(codec_names, str, subtitle_codec_name, oc, "s"); + subtitle_codec_name = opt_match_per_type_str(&o->codec_names, 's'); if (!avcodec_find_encoder(oc->oformat->subtitle_codec) && !subtitle_codec_name) - return; + return 0; for (InputStream *ist = ist_iter(NULL); ist; ist = ist_iter(ist)) if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) { @@ -1265,36 +1564,43 @@ static void map_auto_subtitle(Muxer *mux, const OptionsContext *o) input_descriptor && output_descriptor && (!input_descriptor->props || !output_descriptor->props)) { - new_subtitle_stream(mux, o, ist); - break; + return ost_add(mux, o, AVMEDIA_TYPE_SUBTITLE, ist, NULL, NULL); } } + + return 0; } -static void map_auto_data(Muxer *mux, const OptionsContext *o) +static int map_auto_data(Muxer *mux, const OptionsContext *o) { AVFormatContext *oc = mux->fc; /* Data only if codec id match */ enum AVCodecID codec_id = av_guess_codec(oc->oformat, NULL, oc->url, NULL, AVMEDIA_TYPE_DATA); if (codec_id == AV_CODEC_ID_NONE) - return; + return 0; for (InputStream *ist = ist_iter(NULL); ist; ist = ist_iter(ist)) { if (ist->user_set_discard == AVDISCARD_ALL) continue; if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_DATA && - ist->st->codecpar->codec_id == codec_id ) - new_data_stream(mux, o, ist); + ist->st->codecpar->codec_id == codec_id) { + int ret = ost_add(mux, o, AVMEDIA_TYPE_DATA, ist, NULL, NULL); + if (ret < 0) + return ret; + } } + + return 0; } -static void map_manual(Muxer *mux, const OptionsContext *o, const StreamMap *map) +static int map_manual(Muxer *mux, const OptionsContext *o, const StreamMap *map) { InputStream *ist; + int ret; if (map->disabled) - return; + return 0; if (map->linklabel) { FilterGraph *fg; @@ -1304,8 +1610,8 @@ static void map_manual(Muxer *mux, const OptionsContext *o, const StreamMap *map for (j = 0; j < nb_filtergraphs; j++) { fg = filtergraphs[j]; for (k = 0; k < fg->nb_outputs; k++) { - AVFilterInOut *out = fg->outputs[k]->out_tmp; - if (out && !strcmp(out->name, map->linklabel)) { + const char *linklabel = fg->outputs[k]->linklabel; + if (linklabel && !strcmp(linklabel, map->linklabel)) { ofilter = fg->outputs[k]; goto loop_end; } @@ -1315,37 +1621,33 @@ static void map_manual(Muxer *mux, const OptionsContext *o, const StreamMap *map if (!ofilter) { av_log(mux, AV_LOG_FATAL, "Output with label '%s' does not exist " "in any defined filter graph, or was already used elsewhere.\n", map->linklabel); - exit_program(1); + return AVERROR(EINVAL); } - init_output_filter(ofilter, o, mux); + + av_log(mux, AV_LOG_VERBOSE, "Creating output stream from an explicitly " + "mapped complex filtergraph %d, output [%s]\n", fg->index, map->linklabel); + + ret = ost_add(mux, o, ofilter->type, NULL, ofilter, NULL); + if (ret < 0) + return ret; } else { ist = input_files[map->file_index]->streams[map->stream_index]; if (ist->user_set_discard == AVDISCARD_ALL) { av_log(mux, AV_LOG_FATAL, "Stream #%d:%d is disabled and cannot be mapped.\n", map->file_index, map->stream_index); - exit_program(1); + return AVERROR(EINVAL); } if(o->subtitle_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) - return; + return 0; if(o-> audio_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) - return; + return 0; if(o-> video_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) - return; + return 0; if(o-> data_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_DATA) - return; - - switch (ist->st->codecpar->codec_type) { - case AVMEDIA_TYPE_VIDEO: new_video_stream (mux, o, ist); break; - case AVMEDIA_TYPE_AUDIO: new_audio_stream (mux, o, ist); break; - case AVMEDIA_TYPE_SUBTITLE: new_subtitle_stream (mux, o, ist); break; - case AVMEDIA_TYPE_DATA: new_data_stream (mux, o, ist); break; - case AVMEDIA_TYPE_ATTACHMENT: new_attachment_stream(mux, o, ist); break; - case AVMEDIA_TYPE_UNKNOWN: - if (copy_unknown_streams) { - new_unknown_stream (mux, o, ist); - break; - } - default: + return 0; + + if (ist->st->codecpar->codec_type == AVMEDIA_TYPE_UNKNOWN && + !copy_unknown_streams) { av_log(mux, ignore_unknown_streams ? AV_LOG_WARNING : AV_LOG_FATAL, "Cannot map stream #%d:%d - unsupported type.\n", map->file_index, map->stream_index); @@ -1354,13 +1656,20 @@ static void map_manual(Muxer *mux, const OptionsContext *o, const StreamMap *map "If you want unsupported types ignored instead " "of failing, please use the -ignore_unknown option\n" "If you want them copied, please use -copy_unknown\n"); - exit_program(1); + return AVERROR(EINVAL); } + return 0; } + + ret = ost_add(mux, o, ist->st->codecpar->codec_type, ist, NULL, NULL); + if (ret < 0) + return ret; } + + return 0; } -static void of_add_attachments(Muxer *mux, const OptionsContext *o) +static int of_add_attachments(Muxer *mux, const OptionsContext *o) { OutputStream *ost; int err; @@ -1368,46 +1677,96 @@ static void of_add_attachments(Muxer *mux, const OptionsContext *o) for (int i = 0; i < o->nb_attachments; i++) { AVIOContext *pb; uint8_t *attachment; + char *attachment_filename; const char *p; int64_t len; if ((err = avio_open2(&pb, o->attachments[i], AVIO_FLAG_READ, &int_cb, NULL)) < 0) { av_log(mux, AV_LOG_FATAL, "Could not open attachment file %s.\n", o->attachments[i]); - exit_program(1); + return err; } if ((len = avio_size(pb)) <= 0) { av_log(mux, AV_LOG_FATAL, "Could not get size of the attachment %s.\n", o->attachments[i]); - exit_program(1); + err = len ? len : AVERROR_INVALIDDATA; + goto read_fail; } - if (len > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE || - !(attachment = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE))) { + if (len > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) { av_log(mux, AV_LOG_FATAL, "Attachment %s too large.\n", o->attachments[i]); - exit_program(1); + err = AVERROR(ERANGE); + goto read_fail; + } + + attachment = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE); + if (!attachment) { + err = AVERROR(ENOMEM); + goto read_fail; + } + + err = avio_read(pb, attachment, len); + if (err < 0) + av_log(mux, AV_LOG_FATAL, "Error reading attachment file %s: %s\n", + o->attachments[i], av_err2str(err)); + else if (err != len) { + av_log(mux, AV_LOG_FATAL, "Could not read all %"PRId64" bytes for " + "attachment file %s\n", len, o->attachments[i]); + err = AVERROR(EIO); } - avio_read(pb, attachment, len); + +read_fail: + avio_closep(&pb); + if (err < 0) + return err; + memset(attachment + len, 0, AV_INPUT_BUFFER_PADDING_SIZE); - ost = new_attachment_stream(mux, o, NULL); - ost->attachment_filename = o->attachments[i]; - ost->st->codecpar->extradata = attachment; - ost->st->codecpar->extradata_size = len; + av_log(mux, AV_LOG_VERBOSE, "Creating attachment stream from file %s\n", + o->attachments[i]); + + attachment_filename = av_strdup(o->attachments[i]); + if (!attachment_filename) { + av_free(attachment); + return AVERROR(ENOMEM); + } + + err = ost_add(mux, o, AVMEDIA_TYPE_ATTACHMENT, NULL, NULL, &ost); + if (err < 0) { + av_free(attachment_filename); + av_freep(&attachment); + return err; + } + + ost->attachment_filename = attachment_filename; + ost->par_in->extradata = attachment; + ost->par_in->extradata_size = len; p = strrchr(o->attachments[i], '/'); av_dict_set(&ost->st->metadata, "filename", (p && *p) ? p + 1 : o->attachments[i], AV_DICT_DONT_OVERWRITE); - avio_closep(&pb); } + + return 0; } -static void create_streams(Muxer *mux, const OptionsContext *o) +static int create_streams(Muxer *mux, const OptionsContext *o) { + static int (* const map_func[])(Muxer *mux, const OptionsContext *o) = { + [AVMEDIA_TYPE_VIDEO] = map_auto_video, + [AVMEDIA_TYPE_AUDIO] = map_auto_audio, + [AVMEDIA_TYPE_SUBTITLE] = map_auto_subtitle, + [AVMEDIA_TYPE_DATA] = map_auto_data, + }; + AVFormatContext *oc = mux->fc; - int auto_disable_v = o->video_disable; - int auto_disable_a = o->audio_disable; - int auto_disable_s = o->subtitle_disable; - int auto_disable_d = o->data_disable; + + int auto_disable = + o->video_disable * (1 << AVMEDIA_TYPE_VIDEO) | + o->audio_disable * (1 << AVMEDIA_TYPE_AUDIO) | + o->subtitle_disable * (1 << AVMEDIA_TYPE_SUBTITLE) | + o->data_disable * (1 << AVMEDIA_TYPE_DATA); + + int ret; /* create streams for all unlabeled output pads */ for (int i = 0; i < nb_filtergraphs; i++) { @@ -1415,46 +1774,82 @@ static void create_streams(Muxer *mux, const OptionsContext *o) for (int j = 0; j < fg->nb_outputs; j++) { OutputFilter *ofilter = fg->outputs[j]; - if (!ofilter->out_tmp || ofilter->out_tmp->name) + if (ofilter->linklabel || ofilter->ost) + continue; + + auto_disable |= 1 << ofilter->type; + + av_log(mux, AV_LOG_VERBOSE, "Creating output stream from unlabeled " + "output of complex filtergraph %d.", fg->index); + if (!o->nb_stream_maps) + av_log(mux, AV_LOG_VERBOSE, " This overrides automatic %s mapping.", + av_get_media_type_string(ofilter->type)); + av_log(mux, AV_LOG_VERBOSE, "\n"); + + ret = ost_add(mux, o, ofilter->type, NULL, ofilter, NULL); + if (ret < 0) + return ret; + } + } + + if (!o->nb_stream_maps) { + av_log(mux, AV_LOG_VERBOSE, "No explicit maps, mapping streams automatically...\n"); + + /* pick the "best" stream of each type */ + for (int i = 0; i < FF_ARRAY_ELEMS(map_func); i++) { + if (!map_func[i] || auto_disable & (1 << i)) + continue; + ret = map_func[i](mux, o); + if (ret < 0) + return ret; + } + } else { + av_log(mux, AV_LOG_VERBOSE, "Adding streams from explicit maps...\n"); + + for (int i = 0; i < o->nb_stream_maps; i++) { + ret = map_manual(mux, o, &o->stream_maps[i]); + if (ret < 0) + return ret; + } + } + + ret = of_add_attachments(mux, o); + if (ret < 0) + return ret; + + // setup fix_sub_duration_heartbeat mappings + for (unsigned i = 0; i < oc->nb_streams; i++) { + MuxStream *src = ms_from_ost(mux->of.streams[i]); + + if (!src->ost.fix_sub_duration_heartbeat) + continue; + + for (unsigned j = 0; j < oc->nb_streams; j++) { + MuxStream *dst = ms_from_ost(mux->of.streams[j]); + + if (src == dst || dst->ost.type != AVMEDIA_TYPE_SUBTITLE || + !dst->ost.enc || !dst->ost.ist || !dst->ost.ist->fix_sub_duration) continue; - switch (ofilter->type) { - case AVMEDIA_TYPE_VIDEO: auto_disable_v = 1; break; - case AVMEDIA_TYPE_AUDIO: auto_disable_a = 1; break; - case AVMEDIA_TYPE_SUBTITLE: auto_disable_s = 1; break; - } - init_output_filter(ofilter, o, mux); - } - } + ret = sch_mux_sub_heartbeat_add(mux->sch, mux->sch_idx, src->sch_idx, + dst->sch_idx_src); - if (!o->nb_stream_maps) { - /* pick the "best" stream of each type */ - if (!auto_disable_v) - map_auto_video(mux, o); - if (!auto_disable_a) - map_auto_audio(mux, o); - if (!auto_disable_s) - map_auto_subtitle(mux, o); - if (!auto_disable_d) - map_auto_data(mux, o); - } else { - for (int i = 0; i < o->nb_stream_maps; i++) - map_manual(mux, o, &o->stream_maps[i]); + } } - of_add_attachments(mux, o); - if (!oc->nb_streams && !(oc->oformat->flags & AVFMT_NOSTREAMS)) { av_dump_format(oc, nb_output_files - 1, oc->url, 1); av_log(mux, AV_LOG_ERROR, "Output file does not contain any stream\n"); - exit_program(1); + return AVERROR(EINVAL); } + + return 0; } static int setup_sync_queues(Muxer *mux, AVFormatContext *oc, int64_t buf_size_us) { OutputFile *of = &mux->of; - int nb_av_enc = 0, nb_interleaved = 0; + int nb_av_enc = 0, nb_audio_fs = 0, nb_interleaved = 0; int limit_frames = 0, limit_frames_av_enc = 0; #define IS_AV_ENC(ost, type) \ @@ -1464,56 +1859,62 @@ static int setup_sync_queues(Muxer *mux, AVFormatContext *oc, int64_t buf_size_u for (int i = 0; i < oc->nb_streams; i++) { OutputStream *ost = of->streams[i]; MuxStream *ms = ms_from_ost(ost); - enum AVMediaType type = ost->st->codecpar->codec_type; + enum AVMediaType type = ost->type; - ost->sq_idx_encode = -1; - ost->sq_idx_mux = -1; + ms->sq_idx_mux = -1; nb_interleaved += IS_INTERLEAVED(type); nb_av_enc += IS_AV_ENC(ost, type); + nb_audio_fs += (ost->enc_ctx && type == AVMEDIA_TYPE_AUDIO && + !(ost->enc_ctx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)); limit_frames |= ms->max_frames < INT64_MAX; limit_frames_av_enc |= (ms->max_frames < INT64_MAX) && IS_AV_ENC(ost, type); } if (!((nb_interleaved > 1 && of->shortest) || - (nb_interleaved > 0 && limit_frames))) + (nb_interleaved > 0 && limit_frames) || + nb_audio_fs)) return 0; - /* if we have more than one encoded audio/video streams, or at least - * one encoded audio/video stream is frame-limited, then we - * synchronize them before encoding */ - if ((of->shortest && nb_av_enc > 1) || limit_frames_av_enc) { - of->sq_encode = sq_alloc(SYNC_QUEUE_FRAMES, buf_size_us); - if (!of->sq_encode) - return AVERROR(ENOMEM); + /* we use a sync queue before encoding when: + * - 'shortest' is in effect and we have two or more encoded audio/video + * streams + * - at least one encoded audio/video stream is frame-limited, since + * that has similar semantics to 'shortest' + * - at least one audio encoder requires constant frame sizes + * + * Note that encoding sync queues are handled in the scheduler, because + * different encoders run in different threads and need external + * synchronization, while muxer sync queues can be handled inside the muxer + */ + if ((of->shortest && nb_av_enc > 1) || limit_frames_av_enc || nb_audio_fs) { + int sq_idx, ret; + + sq_idx = sch_add_sq_enc(mux->sch, buf_size_us, mux); + if (sq_idx < 0) + return sq_idx; for (int i = 0; i < oc->nb_streams; i++) { OutputStream *ost = of->streams[i]; MuxStream *ms = ms_from_ost(ost); - enum AVMediaType type = ost->st->codecpar->codec_type; + enum AVMediaType type = ost->type; if (!IS_AV_ENC(ost, type)) continue; - ost->sq_idx_encode = sq_add_stream(of->sq_encode, - of->shortest || ms->max_frames < INT64_MAX); - if (ost->sq_idx_encode < 0) - return ost->sq_idx_encode; - - ost->sq_frame = av_frame_alloc(); - if (!ost->sq_frame) - return AVERROR(ENOMEM); - - if (ms->max_frames != INT64_MAX) - sq_limit_frames(of->sq_encode, ost->sq_idx_encode, ms->max_frames); + ret = sch_sq_add_enc(mux->sch, sq_idx, ms->sch_idx_enc, + of->shortest || ms->max_frames < INT64_MAX, + ms->max_frames); + if (ret < 0) + return ret; } } /* if there are any additional interleaved streams, then ALL the streams * are also synchronized before sending them to the muxer */ if (nb_interleaved > nb_av_enc) { - mux->sq_mux = sq_alloc(SYNC_QUEUE_PACKETS, buf_size_us); + mux->sq_mux = sq_alloc(SYNC_QUEUE_PACKETS, buf_size_us, mux); if (!mux->sq_mux) return AVERROR(ENOMEM); @@ -1524,18 +1925,18 @@ static int setup_sync_queues(Muxer *mux, AVFormatContext *oc, int64_t buf_size_u for (int i = 0; i < oc->nb_streams; i++) { OutputStream *ost = of->streams[i]; MuxStream *ms = ms_from_ost(ost); - enum AVMediaType type = ost->st->codecpar->codec_type; + enum AVMediaType type = ost->type; if (!IS_INTERLEAVED(type)) continue; - ost->sq_idx_mux = sq_add_stream(mux->sq_mux, - of->shortest || ms->max_frames < INT64_MAX); - if (ost->sq_idx_mux < 0) - return ost->sq_idx_mux; + ms->sq_idx_mux = sq_add_stream(mux->sq_mux, + of->shortest || ms->max_frames < INT64_MAX); + if (ms->sq_idx_mux < 0) + return ms->sq_idx_mux; if (ms->max_frames != INT64_MAX) - sq_limit_frames(mux->sq_mux, ost->sq_idx_mux, ms->max_frames); + sq_limit_frames(mux->sq_mux, ms->sq_idx_mux, ms->max_frames); } } @@ -1545,76 +1946,415 @@ static int setup_sync_queues(Muxer *mux, AVFormatContext *oc, int64_t buf_size_u return 0; } -static void of_add_programs(Muxer *mux, const OptionsContext *o) +static int of_parse_iamf_audio_element_layers(Muxer *mux, AVStreamGroup *stg, char *ptr) { - AVFormatContext *oc = mux->fc; - /* process manually set programs */ - for (int i = 0; i < o->nb_program; i++) { - const char *p = o->program[i].u.str; - int progid = i+1; - AVProgram *program; + AVIAMFAudioElement *audio_element = stg->params.iamf_audio_element; + AVDictionary *dict = NULL; + const char *token; + int ret = 0; - while(*p) { - const char *p2 = av_get_token(&p, ":"); - const char *to_dealloc = p2; - char *key; - if (!p2) - break; + audio_element->demixing_info = + av_iamf_param_definition_alloc(AV_IAMF_PARAMETER_DEFINITION_DEMIXING, 1, NULL); + audio_element->recon_gain_info = + av_iamf_param_definition_alloc(AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN, 1, NULL); - if(*p) p++; + if (!audio_element->demixing_info || + !audio_element->recon_gain_info) + return AVERROR(ENOMEM); - key = av_get_token(&p2, "="); - if (!key || !*p2) { - av_freep(&to_dealloc); - av_freep(&key); - break; + /* process manually set layers and parameters */ + token = av_strtok(NULL, ",", &ptr); + while (token) { + const AVDictionaryEntry *e; + int demixing = 0, recon_gain = 0; + int layer = 0; + + if (ptr) + ptr += strspn(ptr, " \n\t\r"); + if (av_strstart(token, "layer=", &token)) + layer = 1; + else if (av_strstart(token, "demixing=", &token)) + demixing = 1; + else if (av_strstart(token, "recon_gain=", &token)) + recon_gain = 1; + + av_dict_free(&dict); + ret = av_dict_parse_string(&dict, token, "=", ":", 0); + if (ret < 0) { + av_log(mux, AV_LOG_ERROR, "Error parsing audio element specification %s\n", token); + goto fail; + } + + if (layer) { + AVIAMFLayer *audio_layer = av_iamf_audio_element_add_layer(audio_element); + if (!audio_layer) { + av_log(mux, AV_LOG_ERROR, "Error adding layer to stream group %d\n", stg->index); + ret = AVERROR(ENOMEM); + goto fail; } - p2++; + av_opt_set_dict(audio_layer, &dict); + } else if (demixing || recon_gain) { + AVIAMFParamDefinition *param = demixing ? audio_element->demixing_info + : audio_element->recon_gain_info; + void *subblock = av_iamf_param_definition_get_subblock(param, 0); - if (!strcmp(key, "program_num")) - progid = strtol(p2, NULL, 0); - av_freep(&to_dealloc); - av_freep(&key); + av_opt_set_dict(param, &dict); + av_opt_set_dict(subblock, &dict); } - program = av_new_program(oc, progid); - if (!program) - report_and_exit(AVERROR(ENOMEM)); - - p = o->program[i].u.str; - while(*p) { - const char *p2 = av_get_token(&p, ":"); - const char *to_dealloc = p2; - char *key; - if (!p2) - break; - if(*p) p++; + // make sure that no entries are left in the dict + e = NULL; + if (e = av_dict_iterate(dict, e)) { + av_log(mux, AV_LOG_FATAL, "Unknown layer key %s.\n", e->key); + ret = AVERROR(EINVAL); + goto fail; + } + token = av_strtok(NULL, ",", &ptr); + } - key = av_get_token(&p2, "="); - if (!key) { - av_log(mux, AV_LOG_FATAL, - "No '=' character in program string %s.\n", - p2); - exit_program(1); +fail: + av_dict_free(&dict); + if (!ret && !audio_element->nb_layers) { + av_log(mux, AV_LOG_ERROR, "No layer in audio element specification\n"); + ret = AVERROR(EINVAL); + } + + return ret; +} + +static int of_parse_iamf_submixes(Muxer *mux, AVStreamGroup *stg, char *ptr) +{ + AVFormatContext *oc = mux->fc; + AVIAMFMixPresentation *mix = stg->params.iamf_mix_presentation; + AVDictionary *dict = NULL; + const char *token; + char *submix_str = NULL; + int ret = 0; + + /* process manually set submixes */ + token = av_strtok(NULL, ",", &ptr); + while (token) { + AVIAMFSubmix *submix = NULL; + const char *subtoken; + char *subptr = NULL; + + if (ptr) + ptr += strspn(ptr, " \n\t\r"); + if (!av_strstart(token, "submix=", &token)) { + av_log(mux, AV_LOG_ERROR, "No submix in mix presentation specification \"%s\"\n", token); + goto fail; + } + + submix_str = av_strdup(token); + if (!submix_str) + goto fail; + + submix = av_iamf_mix_presentation_add_submix(mix); + if (!submix) { + av_log(mux, AV_LOG_ERROR, "Error adding submix to stream group %d\n", stg->index); + ret = AVERROR(ENOMEM); + goto fail; + } + submix->output_mix_config = + av_iamf_param_definition_alloc(AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, 0, NULL); + if (!submix->output_mix_config) { + ret = AVERROR(ENOMEM); + goto fail; + } + + subptr = NULL; + subtoken = av_strtok(submix_str, "|", &subptr); + while (subtoken) { + const AVDictionaryEntry *e; + int element = 0, layout = 0; + + if (subptr) + subptr += strspn(subptr, " \n\t\r"); + if (av_strstart(subtoken, "element=", &subtoken)) + element = 1; + else if (av_strstart(subtoken, "layout=", &subtoken)) + layout = 1; + + av_dict_free(&dict); + ret = av_dict_parse_string(&dict, subtoken, "=", ":", 0); + if (ret < 0) { + av_log(mux, AV_LOG_ERROR, "Error parsing submix specification \"%s\"\n", subtoken); + goto fail; } - if (!*p2) - exit_program(1); - p2++; - - if (!strcmp(key, "title")) { - av_dict_set(&program->metadata, "title", p2, 0); - } else if (!strcmp(key, "program_num")) { - } else if (!strcmp(key, "st")) { - int st_num = strtol(p2, NULL, 0); - av_program_add_stream_index(oc, progid, st_num); - } else { - av_log(mux, AV_LOG_FATAL, "Unknown program key %s.\n", key); - exit_program(1); + + if (element) { + AVIAMFSubmixElement *submix_element; + char *endptr = NULL; + int64_t idx = -1; + + if (e = av_dict_get(dict, "stg", NULL, 0)) + idx = strtoll(e->value, &endptr, 0); + if (!endptr || *endptr || idx < 0 || idx >= oc->nb_stream_groups - 1 || + oc->stream_groups[idx]->type != AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT) { + av_log(mux, AV_LOG_ERROR, "Invalid or missing stream group index in " + "submix element specification \"%s\"\n", subtoken); + ret = AVERROR(EINVAL); + goto fail; + } + submix_element = av_iamf_submix_add_element(submix); + if (!submix_element) { + av_log(mux, AV_LOG_ERROR, "Error adding element to submix\n"); + ret = AVERROR(ENOMEM); + goto fail; + } + + submix_element->audio_element_id = oc->stream_groups[idx]->id; + + submix_element->element_mix_config = + av_iamf_param_definition_alloc(AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, 0, NULL); + if (!submix_element->element_mix_config) + ret = AVERROR(ENOMEM); + av_dict_set(&dict, "stg", NULL, 0); + av_opt_set_dict2(submix_element, &dict, AV_OPT_SEARCH_CHILDREN); + } else if (layout) { + AVIAMFSubmixLayout *submix_layout = av_iamf_submix_add_layout(submix); + if (!submix_layout) { + av_log(mux, AV_LOG_ERROR, "Error adding layout to submix\n"); + ret = AVERROR(ENOMEM); + goto fail; + } + av_opt_set_dict(submix_layout, &dict); + } else + av_opt_set_dict2(submix, &dict, AV_OPT_SEARCH_CHILDREN); + + if (ret < 0) { + goto fail; + } + + // make sure that no entries are left in the dict + e = NULL; + while (e = av_dict_iterate(dict, e)) { + av_log(mux, AV_LOG_FATAL, "Unknown submix key %s.\n", e->key); + ret = AVERROR(EINVAL); + goto fail; } - av_freep(&to_dealloc); - av_freep(&key); + subtoken = av_strtok(NULL, "|", &subptr); + } + av_freep(&submix_str); + + if (!submix->nb_elements) { + av_log(mux, AV_LOG_ERROR, "No audio elements in submix specification \"%s\"\n", token); + ret = AVERROR(EINVAL); + } + token = av_strtok(NULL, ",", &ptr); + } + +fail: + av_dict_free(&dict); + av_free(submix_str); + + return ret; +} + +static int of_parse_group_token(Muxer *mux, const char *token, char *ptr) +{ + AVFormatContext *oc = mux->fc; + AVStreamGroup *stg; + AVDictionary *dict = NULL, *tmp = NULL; + const AVDictionaryEntry *e; + const AVOption opts[] = { + { "type", "Set group type", offsetof(AVStreamGroup, type), AV_OPT_TYPE_INT, + { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "type" }, + { "iamf_audio_element", NULL, 0, AV_OPT_TYPE_CONST, + { .i64 = AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT }, .unit = "type" }, + { "iamf_mix_presentation", NULL, 0, AV_OPT_TYPE_CONST, + { .i64 = AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION }, .unit = "type" }, + { NULL }, + }; + const AVClass class = { + .class_name = "StreamGroupType", + .item_name = av_default_item_name, + .option = opts, + .version = LIBAVUTIL_VERSION_INT, + }; + const AVClass *pclass = &class; + int type, ret; + + ret = av_dict_parse_string(&dict, token, "=", ":", AV_DICT_MULTIKEY); + if (ret < 0) { + av_log(mux, AV_LOG_ERROR, "Error parsing group specification %s\n", token); + return ret; + } + + // "type" is not a user settable AVOption in AVStreamGroup, so handle it here + e = av_dict_get(dict, "type", NULL, 0); + if (!e) { + av_log(mux, AV_LOG_ERROR, "No type specified for Stream Group in \"%s\"\n", token); + ret = AVERROR(EINVAL); + goto end; + } + + ret = av_opt_eval_int(&pclass, opts, e->value, &type); + if (!ret && type == AV_STREAM_GROUP_PARAMS_NONE) + ret = AVERROR(EINVAL); + if (ret < 0) { + av_log(mux, AV_LOG_ERROR, "Invalid group type \"%s\"\n", e->value); + goto end; + } + + av_dict_copy(&tmp, dict, 0); + stg = avformat_stream_group_create(oc, type, &tmp); + if (!stg) { + ret = AVERROR(ENOMEM); + goto end; + } + + e = NULL; + while (e = av_dict_get(dict, "st", e, 0)) { + char *endptr; + int64_t idx = strtoll(e->value, &endptr, 0); + if (*endptr || idx < 0 || idx >= oc->nb_streams) { + av_log(mux, AV_LOG_ERROR, "Invalid stream index %"PRId64"\n", idx); + ret = AVERROR(EINVAL); + goto end; + } + ret = avformat_stream_group_add_stream(stg, oc->streams[idx]); + if (ret < 0) + goto end; + } + while (e = av_dict_get(dict, "stg", e, 0)) { + char *endptr; + int64_t idx = strtoll(e->value, &endptr, 0); + if (*endptr || idx < 0 || idx >= oc->nb_stream_groups - 1) { + av_log(mux, AV_LOG_ERROR, "Invalid stream group index %"PRId64"\n", idx); + ret = AVERROR(EINVAL); + goto end; + } + for (unsigned i = 0; i < oc->stream_groups[idx]->nb_streams; i++) { + ret = avformat_stream_group_add_stream(stg, oc->stream_groups[idx]->streams[i]); + if (ret < 0) + goto end; + } + } + + switch(type) { + case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT: + ret = of_parse_iamf_audio_element_layers(mux, stg, ptr); + break; + case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: + ret = of_parse_iamf_submixes(mux, stg, ptr); + break; + default: + av_log(mux, AV_LOG_FATAL, "Unknown group type %d.\n", type); + ret = AVERROR(EINVAL); + break; + } + + if (ret < 0) + goto end; + + // make sure that nothing but "st" and "stg" entries are left in the dict + e = NULL; + av_dict_set(&tmp, "type", NULL, 0); + while (e = av_dict_iterate(tmp, e)) { + if (!strcmp(e->key, "st") || !strcmp(e->key, "stg")) + continue; + + av_log(mux, AV_LOG_FATAL, "Unknown group key %s.\n", e->key); + ret = AVERROR(EINVAL); + goto end; + } + + ret = 0; +end: + av_dict_free(&dict); + av_dict_free(&tmp); + + return ret; +} + +static int of_add_groups(Muxer *mux, const OptionsContext *o) +{ + /* process manually set groups */ + for (int i = 0; i < o->stream_groups.nb_opt; i++) { + const char *token; + char *str, *ptr = NULL; + int ret = 0; + + str = av_strdup(o->stream_groups.opt[i].u.str); + if (!str) + return ret; + + token = av_strtok(str, ",", &ptr); + if (token) { + if (ptr) + ptr += strspn(ptr, " \n\t\r"); + ret = of_parse_group_token(mux, token, ptr); + } + + av_free(str); + if (ret < 0) + return ret; + } + + return 0; +} + +static int of_add_programs(Muxer *mux, const OptionsContext *o) +{ + AVFormatContext *oc = mux->fc; + /* process manually set programs */ + for (int i = 0; i < o->program.nb_opt; i++) { + AVDictionary *dict = NULL; + const AVDictionaryEntry *e; + AVProgram *program; + int ret, progid = i + 1; + + ret = av_dict_parse_string(&dict, o->program.opt[i].u.str, "=", ":", + AV_DICT_MULTIKEY); + if (ret < 0) { + av_log(mux, AV_LOG_ERROR, "Error parsing program specification %s\n", + o->program.opt[i].u.str); + return ret; + } + + e = av_dict_get(dict, "program_num", NULL, 0); + if (e) { + progid = strtol(e->value, NULL, 0); + av_dict_set(&dict, e->key, NULL, 0); + } + + program = av_new_program(oc, progid); + if (!program) { + ret = AVERROR(ENOMEM); + goto fail; + } + + e = av_dict_get(dict, "title", NULL, 0); + if (e) { + av_dict_set(&program->metadata, e->key, e->value, 0); + av_dict_set(&dict, e->key, NULL, 0); + } + + e = NULL; + while (e = av_dict_get(dict, "st", e, 0)) { + int st_num = strtol(e->value, NULL, 0); + av_program_add_stream_index(oc, progid, st_num); + } + + // make sure that nothing but "st" entries are left in the dict + e = NULL; + while (e = av_dict_iterate(dict, e)) { + if (!strcmp(e->key, "st")) + continue; + + av_log(mux, AV_LOG_FATAL, "Unknown program key %s.\n", e->key); + ret = AVERROR(EINVAL); + goto fail; } + +fail: + av_dict_free(&dict); + if (ret < 0) + return ret; } + + return 0; } /** @@ -1624,8 +2364,8 @@ static void of_add_programs(Muxer *mux, const OptionsContext *o) * @param index for type c/p, chapter/program index is written here * @param stream_spec for type s, the stream specifier is written here */ -static void parse_meta_type(void *logctx, const char *arg, - char *type, int *index, const char **stream_spec) +static int parse_meta_type(void *logctx, const char *arg, + char *type, int *index, const char **stream_spec) { if (*arg) { *type = *arg; @@ -1635,7 +2375,7 @@ static void parse_meta_type(void *logctx, const char *arg, case 's': if (*(++arg) && *arg != ':') { av_log(logctx, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", arg); - exit_program(1); + return AVERROR(EINVAL); } *stream_spec = *arg == ':' ? arg + 1 : ""; break; @@ -1646,56 +2386,41 @@ static void parse_meta_type(void *logctx, const char *arg, break; default: av_log(logctx, AV_LOG_FATAL, "Invalid metadata type %c.\n", *arg); - exit_program(1); + return AVERROR(EINVAL); } } else *type = 'g'; + + return 0; } -static void of_add_metadata(OutputFile *of, AVFormatContext *oc, - const OptionsContext *o) +static int of_add_metadata(OutputFile *of, AVFormatContext *oc, + const OptionsContext *o) { - for (int i = 0; i < o->nb_metadata; i++) { + for (int i = 0; i < o->metadata.nb_opt; i++) { AVDictionary **m; char type, *val; const char *stream_spec; int index = 0, ret = 0; - val = strchr(o->metadata[i].u.str, '='); + val = strchr(o->metadata.opt[i].u.str, '='); if (!val) { av_log(of, AV_LOG_FATAL, "No '=' character in metadata string %s.\n", - o->metadata[i].u.str); - exit_program(1); + o->metadata.opt[i].u.str); + return AVERROR(EINVAL); } *val++ = 0; - parse_meta_type(of, o->metadata[i].specifier, &type, &index, &stream_spec); + ret = parse_meta_type(of, o->metadata.opt[i].specifier, &type, &index, &stream_spec); + if (ret < 0) + return ret; + if (type == 's') { for (int j = 0; j < oc->nb_streams; j++) { - OutputStream *ost = of->streams[j]; if ((ret = check_stream_specifier(oc, oc->streams[j], stream_spec)) > 0) { -#if FFMPEG_ROTATION_METADATA - if (!strcmp(o->metadata[i].u.str, "rotate")) { - char *tail; - double theta = av_strtod(val, &tail); - if (!*tail) { - ost->rotate_overridden = 1; - ost->rotate_override_value = theta; - } - - av_log(ost, AV_LOG_WARNING, - "Conversion of a 'rotate' metadata key to a " - "proper display matrix rotation is deprecated. " - "See -display_rotation for setting rotation " - "instead."); - } else { -#endif - av_dict_set(&oc->streams[j]->metadata, o->metadata[i].u.str, *val ? val : NULL, 0); -#if FFMPEG_ROTATION_METADATA - } -#endif + av_dict_set(&oc->streams[j]->metadata, o->metadata.opt[i].u.str, *val ? val : NULL, 0); } else if (ret < 0) - exit_program(1); + return ret; } } else { switch (type) { @@ -1705,62 +2430,26 @@ static void of_add_metadata(OutputFile *of, AVFormatContext *oc, case 'c': if (index < 0 || index >= oc->nb_chapters) { av_log(of, AV_LOG_FATAL, "Invalid chapter index %d in metadata specifier.\n", index); - exit_program(1); + return AVERROR(EINVAL); } m = &oc->chapters[index]->metadata; break; case 'p': if (index < 0 || index >= oc->nb_programs) { av_log(of, AV_LOG_FATAL, "Invalid program index %d in metadata specifier.\n", index); - exit_program(1); + return AVERROR(EINVAL); } m = &oc->programs[index]->metadata; break; default: - av_log(of, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", o->metadata[i].specifier); - exit_program(1); + av_log(of, AV_LOG_FATAL, "Invalid metadata specifier %s.\n", o->metadata.opt[i].specifier); + return AVERROR(EINVAL); } - av_dict_set(m, o->metadata[i].u.str, *val ? val : NULL, 0); + av_dict_set(m, o->metadata.opt[i].u.str, *val ? val : NULL, 0); } } -} - -static void set_channel_layout(OutputFilter *f, OutputStream *ost) -{ - const AVCodec *c = ost->enc_ctx->codec; - int i, err; - - if (ost->enc_ctx->ch_layout.order != AV_CHANNEL_ORDER_UNSPEC) { - /* Pass the layout through for all orders but UNSPEC */ - err = av_channel_layout_copy(&f->ch_layout, &ost->enc_ctx->ch_layout); - if (err < 0) - report_and_exit(AVERROR(ENOMEM)); - return; - } - /* Requested layout is of order UNSPEC */ - if (!c->ch_layouts) { - /* Use the default native layout for the requested amount of channels when the - encoder doesn't have a list of supported layouts */ - av_channel_layout_default(&f->ch_layout, ost->enc_ctx->ch_layout.nb_channels); - return; - } - /* Encoder has a list of supported layouts. Pick the first layout in it with the - same amount of channels as the requested layout */ - for (i = 0; c->ch_layouts[i].nb_channels; i++) { - if (c->ch_layouts[i].nb_channels == ost->enc_ctx->ch_layout.nb_channels) - break; - } - if (c->ch_layouts[i].nb_channels) { - /* Use it if one is found */ - err = av_channel_layout_copy(&f->ch_layout, &c->ch_layouts[i]); - if (err < 0) - report_and_exit(AVERROR(ENOMEM)); - return; - } - /* If no layout for the amount of channels requested was found, use the default - native layout for it. */ - av_channel_layout_default(&f->ch_layout, ost->enc_ctx->ch_layout.nb_channels); + return 0; } static int copy_chapters(InputFile *ifile, OutputFile *ofile, AVFormatContext *os, @@ -1768,14 +2457,13 @@ static int copy_chapters(InputFile *ifile, OutputFile *ofile, AVFormatContext *o { AVFormatContext *is = ifile->ctx; AVChapter **tmp; - int i; tmp = av_realloc_f(os->chapters, is->nb_chapters + os->nb_chapters, sizeof(*os->chapters)); if (!tmp) return AVERROR(ENOMEM); os->chapters = tmp; - for (i = 0; i < is->nb_chapters; i++) { + for (int i = 0; i < is->nb_chapters; i++) { AVChapter *in_ch = is->chapters[i], *out_ch; int64_t start_time = (ofile->start_time == AV_NOPTS_VALUE) ? 0 : ofile->start_time; int64_t ts_off = av_rescale_q(start_time - ifile->ts_offset, @@ -1809,7 +2497,7 @@ static int copy_chapters(InputFile *ifile, OutputFile *ofile, AVFormatContext *o static int copy_metadata(Muxer *mux, AVFormatContext *ic, const char *outspec, const char *inspec, int *metadata_global_manual, int *metadata_streams_manual, - int *metadata_chapters_manual, const OptionsContext *o) + int *metadata_chapters_manual) { AVFormatContext *oc = mux->fc; AVDictionary **meta_in = NULL; @@ -1819,8 +2507,11 @@ static int copy_metadata(Muxer *mux, AVFormatContext *ic, const char *istream_spec = NULL, *ostream_spec = NULL; int idx_in = 0, idx_out = 0; - parse_meta_type(mux, inspec, &type_in, &idx_in, &istream_spec); - parse_meta_type(mux, outspec, &type_out, &idx_out, &ostream_spec); + ret = parse_meta_type(mux, inspec, &type_in, &idx_in, &istream_spec); + if (ret >= 0) + ret = parse_meta_type(mux, outspec, &type_out, &idx_out, &ostream_spec); + if (ret < 0) + return ret; if (type_in == 'g' || type_out == 'g' || (!*outspec && !ic)) *metadata_global_manual = 1; @@ -1837,7 +2528,7 @@ static int copy_metadata(Muxer *mux, AVFormatContext *ic, if ((index) < 0 || (index) >= (nb_elems)) {\ av_log(mux, AV_LOG_FATAL, "Invalid %s index %d while processing metadata maps.\n",\ (desc), (index));\ - exit_program(1);\ + return AVERROR(EINVAL);\ } #define SET_DICT(type, meta, context, index)\ @@ -1868,11 +2559,11 @@ static int copy_metadata(Muxer *mux, AVFormatContext *ic, meta_in = &ic->streams[i]->metadata; break; } else if (ret < 0) - exit_program(1); + return ret; } if (!meta_in) { av_log(mux, AV_LOG_FATAL, "Stream specifier %s does not match any streams.\n", istream_spec); - exit_program(1); + return AVERROR(EINVAL); } } @@ -1882,7 +2573,7 @@ static int copy_metadata(Muxer *mux, AVFormatContext *ic, meta_out = &oc->streams[i]->metadata; av_dict_copy(meta_out, *meta_in, AV_DICT_DONT_OVERWRITE); } else if (ret < 0) - exit_program(1); + return ret; } } else av_dict_copy(meta_out, *meta_in, AV_DICT_DONT_OVERWRITE); @@ -1890,7 +2581,7 @@ static int copy_metadata(Muxer *mux, AVFormatContext *ic, return 0; } -static void copy_meta(Muxer *mux, const OptionsContext *o) +static int copy_meta(Muxer *mux, const OptionsContext *o) { OutputFile *of = &mux->of; AVFormatContext *oc = mux->fc; @@ -1898,22 +2589,25 @@ static void copy_meta(Muxer *mux, const OptionsContext *o) int metadata_global_manual = 0; int metadata_streams_manual = 0; int metadata_chapters_manual = 0; + int ret; /* copy metadata */ - for (int i = 0; i < o->nb_metadata_map; i++) { + for (int i = 0; i < o->metadata_map.nb_opt; i++) { char *p; - int in_file_index = strtol(o->metadata_map[i].u.str, &p, 0); + int in_file_index = strtol(o->metadata_map.opt[i].u.str, &p, 0); if (in_file_index >= nb_input_files) { av_log(mux, AV_LOG_FATAL, "Invalid input file index %d while " "processing metadata maps\n", in_file_index); - exit_program(1); + return AVERROR(EINVAL); } - copy_metadata(mux, - in_file_index >= 0 ? input_files[in_file_index]->ctx : NULL, - o->metadata_map[i].specifier, *p ? p + 1 : p, - &metadata_global_manual, &metadata_streams_manual, - &metadata_chapters_manual, o); + ret = copy_metadata(mux, + in_file_index >= 0 ? input_files[in_file_index]->ctx : NULL, + o->metadata_map.opt[i].specifier, *p ? p + 1 : p, + &metadata_global_manual, &metadata_streams_manual, + &metadata_chapters_manual); + if (ret < 0) + return ret; } /* copy chapters */ @@ -1929,7 +2623,7 @@ static void copy_meta(Muxer *mux, const OptionsContext *o) } else { av_log(mux, AV_LOG_FATAL, "Invalid input file index %d in chapter mapping.\n", chapters_input_file); - exit_program(1); + return AVERROR(EINVAL); } } if (chapters_input_file >= 0) @@ -1958,6 +2652,8 @@ static void copy_meta(Muxer *mux, const OptionsContext *o) av_dict_set(&ost->st->metadata, "encoder", NULL, 0); } } + + return 0; } static int set_dispositions(Muxer *mux, const OptionsContext *o) @@ -1965,8 +2661,9 @@ static int set_dispositions(Muxer *mux, const OptionsContext *o) OutputFile *of = &mux->of; AVFormatContext *ctx = mux->fc; - int nb_streams[AVMEDIA_TYPE_NB] = { 0 }; - int have_default[AVMEDIA_TYPE_NB] = { 0 }; + // indexed by type+1, because AVMEDIA_TYPE_UNKNOWN=-1 + int nb_streams[AVMEDIA_TYPE_NB + 1] = { 0 }; + int have_default[AVMEDIA_TYPE_NB + 1] = { 0 }; int have_manual = 0; int ret = 0; @@ -1980,7 +2677,7 @@ static int set_dispositions(Muxer *mux, const OptionsContext *o) for (int i = 0; i < ctx->nb_streams; i++) { OutputStream *ost = of->streams[i]; - nb_streams[ost->st->codecpar->codec_type]++; + nb_streams[ost->type + 1]++; MATCH_PER_STREAM_OPT(disposition, str, dispositions[i], ctx, ost->st); @@ -1990,7 +2687,7 @@ static int set_dispositions(Muxer *mux, const OptionsContext *o) ost->st->disposition = ost->ist->st->disposition; if (ost->st->disposition & AV_DISPOSITION_DEFAULT) - have_default[ost->st->codecpar->codec_type] = 1; + have_default[ost->type + 1] = 1; } } @@ -2013,14 +2710,14 @@ static int set_dispositions(Muxer *mux, const OptionsContext *o) // "Suitable" means the first of that type, skipping attached pictures. for (int i = 0; i < ctx->nb_streams; i++) { OutputStream *ost = of->streams[i]; - enum AVMediaType type = ost->st->codecpar->codec_type; + enum AVMediaType type = ost->type; - if (nb_streams[type] < 2 || have_default[type] || + if (nb_streams[type + 1] < 2 || have_default[type + 1] || ost->st->disposition & AV_DISPOSITION_ATTACHED_PIC) continue; ost->st->disposition |= AV_DISPOSITION_DEFAULT; - have_default[type] = 1; + have_default[type + 1] = 1; } } @@ -2044,11 +2741,11 @@ static int compare_int64(const void *a, const void *b) return FFDIFFSIGN(*(const int64_t *)a, *(const int64_t *)b); } -static void parse_forced_key_frames(KeyframeForceCtx *kf, const Muxer *mux, - const char *spec) +static int parse_forced_key_frames(void *log, KeyframeForceCtx *kf, + const Muxer *mux, const char *spec) { const char *p; - int n = 1, i, size, index = 0; + int n = 1, i, ret, size, index = 0; int64_t t, *pts; for (p = spec; *p; p++) @@ -2057,7 +2754,7 @@ static void parse_forced_key_frames(KeyframeForceCtx *kf, const Muxer *mux, size = n; pts = av_malloc_array(size, sizeof(*pts)); if (!pts) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); p = spec; for (i = 0; i < n; i++) { @@ -2074,8 +2771,17 @@ static void parse_forced_key_frames(KeyframeForceCtx *kf, const Muxer *mux, if (nb_ch > INT_MAX - size || !(pts = av_realloc_f(pts, size += nb_ch - 1, sizeof(*pts)))) - report_and_exit(AVERROR(ENOMEM)); - t = p[8] ? parse_time_or_die("force_key_frames", p + 8, 1) : 0; + return AVERROR(ENOMEM); + + if (p[8]) { + ret = av_parse_time(&t, p + 8, 1); + if (ret < 0) { + av_log(log, AV_LOG_ERROR, + "Invalid chapter time offset: %s\n", p + 8); + goto fail; + } + } else + t = 0; for (j = 0; j < nb_ch; j++) { const AVChapter *c = ch[j]; @@ -2086,7 +2792,13 @@ static void parse_forced_key_frames(KeyframeForceCtx *kf, const Muxer *mux, } else { av_assert1(index < size); - pts[index++] = parse_time_or_die("force_key_frames", p, 1); + ret = av_parse_time(&t, p, 1); + if (ret < 0) { + av_log(log, AV_LOG_ERROR, "Invalid keyframe time: %s\n", p); + goto fail; + } + + pts[index++] = t; } p = next; @@ -2096,6 +2808,11 @@ static void parse_forced_key_frames(KeyframeForceCtx *kf, const Muxer *mux, qsort(pts, size, sizeof(*pts), compare_int64); kf->nb_pts = size; kf->pts = pts; + + return 0; +fail: + av_freep(&pts); + return ret; } static int process_forced_keyframes(Muxer *mux, const OptionsContext *o) @@ -2106,7 +2823,7 @@ static int process_forced_keyframes(Muxer *mux, const OptionsContext *o) MATCH_PER_STREAM_OPT(forced_key_frames, str, forced_keyframes, mux->fc, ost->st); - if (!(ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && + if (!(ost->type == AVMEDIA_TYPE_VIDEO && ost->enc_ctx && forced_keyframes)) continue; @@ -2127,17 +2844,23 @@ static int process_forced_keyframes(Muxer *mux, const OptionsContext *o) // parse it only for static kf timings } else if (!strcmp(forced_keyframes, "source")) { ost->kf.type = KF_FORCE_SOURCE; +#if FFMPEG_OPT_FORCE_KF_SOURCE_NO_DROP } else if (!strcmp(forced_keyframes, "source_no_drop")) { - ost->kf.type = KF_FORCE_SOURCE_NO_DROP; + av_log(ost, AV_LOG_WARNING, "The 'source_no_drop' value for " + "-force_key_frames is deprecated, use just 'source'\n"); + ost->kf.type = KF_FORCE_SOURCE; +#endif } else { - parse_forced_key_frames(&ost->kf, mux, forced_keyframes); + int ret = parse_forced_key_frames(ost, &ost->kf, mux, forced_keyframes); + if (ret < 0) + return ret; } } return 0; } -static void validate_enc_avopt(Muxer *mux, const AVDictionary *codec_avopt) +static int validate_enc_avopt(Muxer *mux, const AVDictionary *codec_avopt) { const AVClass *class = avcodec_get_class(); const AVClass *fclass = avformat_get_class(); @@ -2165,7 +2888,7 @@ static void validate_enc_avopt(Muxer *mux, const AVDictionary *codec_avopt) if (!(option->flags & AV_OPT_FLAG_ENCODING_PARAM)) { av_log(mux, AV_LOG_ERROR, "Codec AVOption %s (%s) is not an " "encoding option.\n", e->key, option->help ? option->help : ""); - exit_program(1); + return AVERROR(EINVAL); } // gop_timecode is injected by generic code but not always used @@ -2179,6 +2902,8 @@ static void validate_enc_avopt(Muxer *mux, const AVDictionary *codec_avopt) "any stream.\n", e->key, option->help ? option->help : ""); } av_dict_free(&unused_opts); + + return 0; } static const char *output_file_item_name(void *obj) @@ -2199,6 +2924,9 @@ static Muxer *mux_alloc(void) { Muxer *mux = allocate_array_elem(&output_files, sizeof(*mux), &nb_output_files); + if (!mux) + return NULL; + mux->of.class = &output_file_class; mux->of.index = nb_output_files - 1; @@ -2207,7 +2935,7 @@ static Muxer *mux_alloc(void) return mux; } -int of_open(const OptionsContext *o, const char *filename) +int of_open(const OptionsContext *o, const char *filename, Scheduler *sch) { Muxer *mux; AVFormatContext *oc; @@ -2218,6 +2946,9 @@ int of_open(const OptionsContext *o, const char *filename) int64_t stop_time = o->stop_time; mux = mux_alloc(); + if (!mux) + return AVERROR(ENOMEM); + of = &mux->of; if (stop_time != INT64_MAX && recording_time != INT64_MAX) { @@ -2229,7 +2960,7 @@ int of_open(const OptionsContext *o, const char *filename) int64_t start_time = o->start_time == AV_NOPTS_VALUE ? 0 : o->start_time; if (stop_time <= start_time) { av_log(mux, AV_LOG_ERROR, "-to value smaller than -ss; aborting.\n"); - exit_program(1); + return AVERROR(EINVAL); } else { recording_time = stop_time - start_time; } @@ -2239,7 +2970,6 @@ int of_open(const OptionsContext *o, const char *filename) of->start_time = o->start_time; of->shortest = o->shortest; - mux->thread_queue_size = o->thread_queue_size > 0 ? o->thread_queue_size : 8; mux->limit_filesize = o->limit_filesize; av_dict_copy(&mux->opts, o->g->format_opts, 0); @@ -2248,16 +2978,15 @@ int of_open(const OptionsContext *o, const char *filename) err = avformat_alloc_output_context2(&oc, NULL, o->format, filename); if (!oc) { - print_error(filename, err); - exit_program(1); + av_log(mux, AV_LOG_FATAL, "Error initializing the muxer for %s: %s\n", + filename, av_err2str(err)); + return err; } mux->fc = oc; av_strlcat(mux->log_name, "/", sizeof(mux->log_name)); av_strlcat(mux->log_name, oc->oformat->name, sizeof(mux->log_name)); - if (strcmp(oc->oformat->name, "rtp")) - want_sdp = 0; of->format = oc->oformat; if (recording_time != INT64_MAX) @@ -2273,91 +3002,51 @@ int of_open(const OptionsContext *o, const char *filename) AVFMT_FLAG_BITEXACT); } + err = sch_add_mux(sch, muxer_thread, mux_check_init, mux, + !strcmp(oc->oformat->name, "rtp"), o->thread_queue_size); + if (err < 0) + return err; + mux->sch = sch; + mux->sch_idx = err; + /* create all output streams for this file */ - create_streams(mux, o); + err = create_streams(mux, o); + if (err < 0) + return err; /* check if all codec options have been used */ - validate_enc_avopt(mux, o->g->codec_opts); - - /* set the decoding_needed flags and create simple filtergraphs */ - for (int i = 0; i < of->nb_streams; i++) { - OutputStream *ost = of->streams[i]; - - if (ost->enc_ctx && ost->ist) { - InputStream *ist = ost->ist; - ist->decoding_needed |= DECODING_FOR_OST; - ist->processing_needed = 1; - - if (ost->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || - ost->st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { - err = init_simple_filtergraph(ist, ost); - if (err < 0) { - av_log(ost, AV_LOG_ERROR, - "Error initializing a simple filtergraph\n"); - exit_program(1); - } - } - } else if (ost->ist) { - ost->ist->processing_needed = 1; - } - - /* set the filter output constraints */ - if (ost->filter) { - const AVCodec *c = ost->enc_ctx->codec; - OutputFilter *f = ost->filter; - switch (ost->enc_ctx->codec_type) { - case AVMEDIA_TYPE_VIDEO: - f->frame_rate = ost->frame_rate; - f->width = ost->enc_ctx->width; - f->height = ost->enc_ctx->height; - if (ost->enc_ctx->pix_fmt != AV_PIX_FMT_NONE) { - f->format = ost->enc_ctx->pix_fmt; - } else { - f->formats = c->pix_fmts; - } - break; - case AVMEDIA_TYPE_AUDIO: - if (ost->enc_ctx->sample_fmt != AV_SAMPLE_FMT_NONE) { - f->format = ost->enc_ctx->sample_fmt; - } else { - f->formats = c->sample_fmts; - } - if (ost->enc_ctx->sample_rate) { - f->sample_rate = ost->enc_ctx->sample_rate; - } else { - f->sample_rates = c->supported_samplerates; - } - if (ost->enc_ctx->ch_layout.nb_channels) { - set_channel_layout(f, ost); - } else if (c->ch_layouts) { - f->ch_layouts = c->ch_layouts; - } - break; - } - } - } + err = validate_enc_avopt(mux, o->g->codec_opts); + if (err < 0) + return err; /* check filename in case of an image number is expected */ - if (oc->oformat->flags & AVFMT_NEEDNUMBER) { - if (!av_filename_number_test(oc->url)) { - print_error(oc->url, AVERROR(EINVAL)); - exit_program(1); - } + if (oc->oformat->flags & AVFMT_NEEDNUMBER && !av_filename_number_test(oc->url)) { + av_log(mux, AV_LOG_FATAL, + "Output filename '%s' does not contain a numeric pattern like " + "'%%d', which is required by output format '%s'.\n", + oc->url, oc->oformat->name); + return AVERROR(EINVAL); } if (!(oc->oformat->flags & AVFMT_NOFILE)) { /* test if it already exists to avoid losing precious files */ - assert_file_overwrite(filename); + err = assert_file_overwrite(filename); + if (err < 0) + return err; /* open the file */ if ((err = avio_open2(&oc->pb, filename, AVIO_FLAG_WRITE, &oc->interrupt_callback, &mux->opts)) < 0) { - print_error(filename, err); - exit_program(1); + av_log(mux, AV_LOG_FATAL, "Error opening output %s: %s\n", + filename, av_err2str(err)); + return err; } - } else if (strcmp(oc->oformat->name, "image2")==0 && !av_filename_number_test(filename)) - assert_file_overwrite(filename); + } else if (strcmp(oc->oformat->name, "image2")==0 && !av_filename_number_test(filename)) { + err = assert_file_overwrite(filename); + if (err < 0) + return err; + } if (o->mux_preload) { av_dict_set_int(&mux->opts, "preload", o->mux_preload*AV_TIME_BASE, 0); @@ -2365,15 +3054,26 @@ int of_open(const OptionsContext *o, const char *filename) oc->max_delay = (int)(o->mux_max_delay * AV_TIME_BASE); /* copy metadata and chapters from input files */ - copy_meta(mux, o); + err = copy_meta(mux, o); + if (err < 0) + return err; - of_add_programs(mux, o); - of_add_metadata(of, oc, o); + err = of_add_groups(mux, o); + if (err < 0) + return err; + + err = of_add_programs(mux, o); + if (err < 0) + return err; + + err = of_add_metadata(of, oc, o); + if (err < 0) + return err; err = set_dispositions(mux, o); if (err < 0) { av_log(mux, AV_LOG_FATAL, "Error setting output stream dispositions\n"); - exit_program(1); + return err; } // parse forced keyframe specifications; @@ -2381,22 +3081,26 @@ int of_open(const OptionsContext *o, const char *filename) err = process_forced_keyframes(mux, o); if (err < 0) { av_log(mux, AV_LOG_FATAL, "Error processing forced keyframes\n"); - exit_program(1); + return err; } err = setup_sync_queues(mux, oc, o->shortest_buf_duration * AV_TIME_BASE); if (err < 0) { av_log(mux, AV_LOG_FATAL, "Error setting up output sync queues\n"); - exit_program(1); + return err; } of->url = filename; - /* write the header for files with no streams */ - if (of->format->flags & AVFMT_NOSTREAMS && oc->nb_streams == 0) { - int ret = mux_check_init(mux); - if (ret < 0) - return ret; + /* initialize streamcopy streams. */ + for (int i = 0; i < of->nb_streams; i++) { + OutputStream *ost = of->streams[i]; + + if (!ost->enc) { + err = of_stream_init(of, ost); + if (err < 0) + return err; + } } return 0; diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index 055275d8139..4b3f9789ba2 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -28,6 +28,7 @@ #endif #include "ffmpeg.h" +#include "ffmpeg_sched.h" #include "cmdutils.h" #include "opt_common.h" #include "sync_queue.h" @@ -53,21 +54,17 @@ #include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" -const char *const opt_name_codec_names[] = {"c", "codec", "acodec", "vcodec", "scodec", "dcodec", NULL}; -const char *const opt_name_frame_rates[] = {"r", NULL}; -const char *const opt_name_codec_tags[] = {"tag", "atag", "vtag", "stag", NULL}; -const char *const opt_name_top_field_first[] = {"top", NULL}; - HWDevice *filter_hw_device; char *vstats_filename; -char *sdp_filename; float audio_drift_threshold = 0.1; float dts_delta_threshold = 10; float dts_error_threshold = 3600*30; +#if FFMPEG_OPT_VSYNC enum VideoSyncMethod video_sync_method = VSYNC_AUTO; +#endif float frame_drop_threshold = 0; int do_benchmark = 0; int do_benchmark_all = 0; @@ -80,7 +77,6 @@ int debug_ts = 0; int exit_on_error = 0; int abort_on_flags = 0; int print_stats = -1; -int qp_hist = 0; int stdin_interaction = 1; float max_error_rate = 2.0/3; char *filter_nbthreads; @@ -92,45 +88,44 @@ int64_t stats_period = 500000; static int file_overwrite = 0; static int no_file_overwrite = 0; -#if FFMPEG_OPT_PSNR -int do_psnr = 0; -#endif int ignore_unknown_streams = 0; int copy_unknown_streams = 0; int recast_media = 0; static void uninit_options(OptionsContext *o) { - const OptionDef *po = options; int i; - /* all OPT_SPEC and OPT_STRING can be freed in generic way */ - while (po->name) { - void *dst = (uint8_t*)o + po->u.off; - - if (po->flags & OPT_SPEC) { - SpecifierOpt **so = dst; - int i, *count = (int*)(so + 1); - for (i = 0; i < *count; i++) { - av_freep(&(*so)[i].specifier); - if (po->flags & OPT_STRING) - av_freep(&(*so)[i].u.str); + /* all OPT_SPEC and OPT_TYPE_STRING can be freed in generic way */ + for (const OptionDef *po = options; po->name; po++) { + void *dst; + + if (!(po->flags & OPT_FLAG_OFFSET)) + continue; + + dst = (uint8_t*)o + po->u.off; + if (po->flags & OPT_FLAG_SPEC) { + SpecifierOptList *so = dst; + for (int i = 0; i < so->nb_opt; i++) { + av_freep(&so->opt[i].specifier); + if (po->type == OPT_TYPE_STRING) + av_freep(&so->opt[i].u.str); } - av_freep(so); - *count = 0; - } else if (po->flags & OPT_OFFSET && po->flags & OPT_STRING) + av_freep(&so->opt); + so->nb_opt = 0; + } else if (po->type == OPT_TYPE_STRING) av_freep(dst); - po++; } for (i = 0; i < o->nb_stream_maps; i++) av_freep(&o->stream_maps[i].linklabel); av_freep(&o->stream_maps); -#if FFMPEG_OPT_MAP_CHANNEL - av_freep(&o->audio_channel_maps); -#endif - av_freep(&o->streamid_map); + + for (i = 0; i < o->nb_attachments; i++) + av_freep(&o->attachments[i]); av_freep(&o->attachments); + + av_dict_free(&o->streamid); } static void init_options(OptionsContext *o) @@ -145,7 +140,7 @@ static void init_options(OptionsContext *o) o->limit_filesize = INT64_MAX; o->chapters_input_file = INT_MAX; o->accurate_seek = 1; - o->thread_queue_size = -1; + o->thread_queue_size = 0; o->input_sync_ref = -1; o->find_stream_info = 1; o->shortest_buf_duration = 10.f; @@ -181,23 +176,51 @@ AVDictionary *strip_specifiers(const AVDictionary *dict) return ret; } +const char *opt_match_per_type_str(const SpecifierOptList *sol, + char mediatype) +{ + av_assert0(!sol->nb_opt || sol->type == OPT_TYPE_STRING); + + for (int i = 0; i < sol->nb_opt; i++) { + const char *spec = sol->opt[i].specifier; + if (spec[0] == mediatype && !spec[1]) + return sol->opt[i].u.str; + } + return NULL; +} + int parse_and_set_vsync(const char *arg, int *vsync_var, int file_idx, int st_idx, int is_global) { if (!av_strcasecmp(arg, "cfr")) *vsync_var = VSYNC_CFR; else if (!av_strcasecmp(arg, "vfr")) *vsync_var = VSYNC_VFR; else if (!av_strcasecmp(arg, "passthrough")) *vsync_var = VSYNC_PASSTHROUGH; - else if (!av_strcasecmp(arg, "drop")) *vsync_var = VSYNC_DROP; +#if FFMPEG_OPT_VSYNC_DROP + else if (!av_strcasecmp(arg, "drop")) { + av_log(NULL, AV_LOG_WARNING, "-vsync/fps_mode drop is deprecated\n"); + *vsync_var = VSYNC_DROP; + } +#endif else if (!is_global && !av_strcasecmp(arg, "auto")) *vsync_var = VSYNC_AUTO; else if (!is_global) { av_log(NULL, AV_LOG_FATAL, "Invalid value %s specified for fps_mode of #%d:%d.\n", arg, file_idx, st_idx); - exit_program(1); + return AVERROR(EINVAL); } +#if FFMPEG_OPT_VSYNC if (is_global && *vsync_var == VSYNC_AUTO) { - video_sync_method = parse_number_or_die("vsync", arg, OPT_INT, VSYNC_AUTO, VSYNC_VFR); + int ret; + double num; + + ret = parse_number("vsync", arg, OPT_TYPE_INT, VSYNC_AUTO, VSYNC_VFR, &num); + if (ret < 0) + return ret; + + video_sync_method = num; av_log(NULL, AV_LOG_WARNING, "Passing a number to -vsync is deprecated," " use a string argument as described in the manual.\n"); } +#endif + return 0; } @@ -250,12 +273,12 @@ static int apply_sync_offsets(void) if (self->input_sync_ref == -1 || self->input_sync_ref == i) continue; if (self->input_sync_ref >= nb_input_files || self->input_sync_ref < -1) { av_log(NULL, AV_LOG_FATAL, "-isync for input %d references non-existent input %d.\n", i, self->input_sync_ref); - exit_program(1); + return AVERROR(EINVAL); } if (copy_ts && !start_at_zero) { av_log(NULL, AV_LOG_FATAL, "Use of -isync requires that start_at_zero be set if copyts is set.\n"); - exit_program(1); + return AVERROR(EINVAL); } ref = input_files[self->input_sync_ref]; @@ -319,7 +342,10 @@ static int opt_abort_on(void *optctx, const char *opt, const char *arg) static int opt_stats_period(void *optctx, const char *opt, const char *arg) { - int64_t user_stats_period = parse_time_or_die(opt, arg, 1); + int64_t user_stats_period; + int ret = av_parse_time(&user_stats_period, arg, 1); + if (ret < 0) + return ret; if (user_stats_period <= 0) { av_log(NULL, AV_LOG_ERROR, "stats_period %s must be positive.\n", arg); @@ -361,9 +387,7 @@ static int opt_map(void *optctx, const char *opt, const char *arg) OptionsContext *o = optctx; StreamMap *m = NULL; int i, negative = 0, file_idx, disabled = 0; -#if FFMPEG_OPT_MAP_SYNC - char *sync; -#endif + int ret; char *map, *p; char *allow_unused; @@ -375,24 +399,20 @@ static int opt_map(void *optctx, const char *opt, const char *arg) if (!map) return AVERROR(ENOMEM); -#if FFMPEG_OPT_MAP_SYNC - /* parse sync stream first, just pick first matching stream */ - if (sync = strchr(map, ',')) { - *sync = 0; - av_log(NULL, AV_LOG_WARNING, "Specifying a sync stream is deprecated and has no effect\n"); - } -#endif - - if (map[0] == '[') { /* this mapping refers to lavfi output */ const char *c = map + 1; - GROW_ARRAY(o->stream_maps, o->nb_stream_maps); + + ret = GROW_ARRAY(o->stream_maps, o->nb_stream_maps); + if (ret < 0) + goto fail; + m = &o->stream_maps[o->nb_stream_maps - 1]; m->linklabel = av_get_token(&c, "]"); if (!m->linklabel) { av_log(NULL, AV_LOG_ERROR, "Invalid output link label: %s.\n", map); - exit_program(1); + ret = AVERROR(EINVAL); + goto fail; } } else { if (allow_unused = strchr(map, '?')) @@ -400,7 +420,8 @@ static int opt_map(void *optctx, const char *opt, const char *arg) file_idx = strtol(map, &p, 0); if (file_idx >= nb_input_files || file_idx < 0) { av_log(NULL, AV_LOG_FATAL, "Invalid input file index: %d.\n", file_idx); - exit_program(1); + ret = AVERROR(EINVAL); + goto fail; } if (negative) /* disable some already defined maps */ @@ -421,7 +442,10 @@ static int opt_map(void *optctx, const char *opt, const char *arg) disabled = 1; continue; } - GROW_ARRAY(o->stream_maps, o->nb_stream_maps); + ret = GROW_ARRAY(o->stream_maps, o->nb_stream_maps); + if (ret < 0) + goto fail; + m = &o->stream_maps[o->nb_stream_maps - 1]; m->file_index = file_idx; @@ -435,116 +459,39 @@ static int opt_map(void *optctx, const char *opt, const char *arg) } else if (disabled) { av_log(NULL, AV_LOG_FATAL, "Stream map '%s' matches disabled streams.\n" "To ignore this, add a trailing '?' to the map.\n", arg); - exit_program(1); + ret = AVERROR(EINVAL); + goto fail; } else { av_log(NULL, AV_LOG_FATAL, "Stream map '%s' matches no streams.\n" "To ignore this, add a trailing '?' to the map.\n", arg); - exit_program(1); + ret = AVERROR(EINVAL); + goto fail; } } - + ret = 0; +fail: av_freep(&map); - return 0; + return ret; } static int opt_attach(void *optctx, const char *opt, const char *arg) { OptionsContext *o = optctx; - GROW_ARRAY(o->attachments, o->nb_attachments); - o->attachments[o->nb_attachments - 1] = arg; - return 0; -} - -#if FFMPEG_OPT_MAP_CHANNEL -static int opt_map_channel(void *optctx, const char *opt, const char *arg) -{ - OptionsContext *o = optctx; - int n; - AVStream *st; - AudioChannelMap *m; - char *allow_unused; - char *mapchan; - - av_log(NULL, AV_LOG_WARNING, - "The -%s option is deprecated and will be removed. " - "It can be replaced by the 'pan' filter, or in some cases by " - "combinations of 'channelsplit', 'channelmap', 'amerge' filters.\n", opt); + int ret = GROW_ARRAY(o->attachments, o->nb_attachments); + if (ret < 0) + return ret; - mapchan = av_strdup(arg); - if (!mapchan) + o->attachments[o->nb_attachments - 1] = av_strdup(arg); + if (!o->attachments[o->nb_attachments - 1]) return AVERROR(ENOMEM); - GROW_ARRAY(o->audio_channel_maps, o->nb_audio_channel_maps); - m = &o->audio_channel_maps[o->nb_audio_channel_maps - 1]; - - /* muted channel syntax */ - n = sscanf(arg, "%d:%d.%d", &m->channel_idx, &m->ofile_idx, &m->ostream_idx); - if ((n == 1 || n == 3) && m->channel_idx == -1) { - m->file_idx = m->stream_idx = -1; - if (n == 1) - m->ofile_idx = m->ostream_idx = -1; - av_free(mapchan); - return 0; - } - - /* normal syntax */ - n = sscanf(arg, "%d.%d.%d:%d.%d", - &m->file_idx, &m->stream_idx, &m->channel_idx, - &m->ofile_idx, &m->ostream_idx); - - if (n != 3 && n != 5) { - av_log(NULL, AV_LOG_FATAL, "Syntax error, mapchan usage: " - "[file.stream.channel|-1][:syncfile:syncstream]\n"); - exit_program(1); - } - - if (n != 5) // only file.stream.channel specified - m->ofile_idx = m->ostream_idx = -1; - - /* check input */ - if (m->file_idx < 0 || m->file_idx >= nb_input_files) { - av_log(NULL, AV_LOG_FATAL, "mapchan: invalid input file index: %d\n", - m->file_idx); - exit_program(1); - } - if (m->stream_idx < 0 || - m->stream_idx >= input_files[m->file_idx]->nb_streams) { - av_log(NULL, AV_LOG_FATAL, "mapchan: invalid input file stream index #%d.%d\n", - m->file_idx, m->stream_idx); - exit_program(1); - } - st = input_files[m->file_idx]->ctx->streams[m->stream_idx]; - if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) { - av_log(NULL, AV_LOG_FATAL, "mapchan: stream #%d.%d is not an audio stream.\n", - m->file_idx, m->stream_idx); - exit_program(1); - } - /* allow trailing ? to map_channel */ - if (allow_unused = strchr(mapchan, '?')) - *allow_unused = 0; - if (m->channel_idx < 0 || m->channel_idx >= st->codecpar->ch_layout.nb_channels || - input_files[m->file_idx]->streams[m->stream_idx]->user_set_discard == AVDISCARD_ALL) { - if (allow_unused) { - av_log(NULL, AV_LOG_VERBOSE, "mapchan: invalid audio channel #%d.%d.%d\n", - m->file_idx, m->stream_idx, m->channel_idx); - } else { - av_log(NULL, AV_LOG_FATAL, "mapchan: invalid audio channel #%d.%d.%d\n" - "To ignore this, add a trailing '?' to the map_channel.\n", - m->file_idx, m->stream_idx, m->channel_idx); - exit_program(1); - } - - } - av_free(mapchan); return 0; } -#endif static int opt_sdp_file(void *optctx, const char *opt, const char *arg) { - av_free(sdp_filename); - sdp_filename = av_strdup(arg); - return 0; + Scheduler *sch = optctx; + return sch_sdp_filename(sch, arg); } #if CONFIG_VAAPI @@ -588,7 +535,7 @@ static int opt_init_hw_device(void *optctx, const char *opt, const char *arg) AV_HWDEVICE_TYPE_NONE) printf("%s\n", av_hwdevice_get_type_name(type)); printf("\n"); - exit_program(0); + return AVERROR_EXIT; } else { return hw_device_init_from_string(arg, NULL); } @@ -612,8 +559,16 @@ static int opt_recording_timestamp(void *optctx, const char *opt, const char *ar { OptionsContext *o = optctx; char buf[128]; - int64_t recording_timestamp = parse_time_or_die(opt, arg, 0) / 1E6; - struct tm time = *gmtime((time_t*)&recording_timestamp); + int64_t recording_timestamp; + int ret; + struct tm time; + + ret = av_parse_time(&recording_timestamp, arg, 0); + if (ret < 0) + return ret; + + recording_timestamp /= 1e6; + time = *gmtime((time_t*)&recording_timestamp); if (!strftime(buf, sizeof(buf), "creation_time=%Y-%m-%dT%H:%M:%S%z", &time)) return -1; parse_option(o, "metadata", buf, options); @@ -623,8 +578,8 @@ static int opt_recording_timestamp(void *optctx, const char *opt, const char *ar return 0; } -const AVCodec *find_codec_or_die(void *logctx, const char *name, - enum AVMediaType type, int encoder) +int find_codec(void *logctx, const char *name, + enum AVMediaType type, int encoder, const AVCodec **pcodec) { const AVCodecDescriptor *desc; const char *codec_string = encoder ? "encoder" : "decoder"; @@ -644,22 +599,25 @@ const AVCodec *find_codec_or_die(void *logctx, const char *name, if (!codec) { av_log(logctx, AV_LOG_FATAL, "Unknown %s '%s'\n", codec_string, name); - exit_program(1); + return encoder ? AVERROR_ENCODER_NOT_FOUND : + AVERROR_DECODER_NOT_FOUND; } if (codec->type != type && !recast_media) { av_log(logctx, AV_LOG_FATAL, "Invalid %s type '%s'\n", codec_string, name); - exit_program(1); + return AVERROR(EINVAL); } - return codec; + + *pcodec = codec; + return 0;; } -void assert_file_overwrite(const char *filename) +int assert_file_overwrite(const char *filename) { const char *proto_name = avio_find_protocol_name(filename); if (file_overwrite && no_file_overwrite) { fprintf(stderr, "Error, both -y and -n supplied. Exiting.\n"); - exit_program(1); + return AVERROR(EINVAL); } if (!file_overwrite) { @@ -671,13 +629,13 @@ void assert_file_overwrite(const char *filename) signal(SIGINT, SIG_DFL); if (!read_yesno()) { av_log(NULL, AV_LOG_FATAL, "Not overwriting - exiting\n"); - exit_program(1); + return AVERROR_EXIT; } term_init(); } else { av_log(NULL, AV_LOG_FATAL, "File '%s' already exists. Exiting.\n", filename); - exit_program(1); + return AVERROR_EXIT; } } } @@ -690,43 +648,18 @@ void assert_file_overwrite(const char *filename) if (!strcmp(filename, file->ctx->url)) { av_log(NULL, AV_LOG_FATAL, "Output %s same as Input #%d - exiting\n", filename, i); av_log(NULL, AV_LOG_WARNING, "FFmpeg cannot edit existing files in-place.\n"); - exit_program(1); + return AVERROR(EINVAL); } } } -} -/* read file contents into a string */ -char *file_read(const char *filename) -{ - AVIOContext *pb = NULL; - int ret = avio_open(&pb, filename, AVIO_FLAG_READ); - AVBPrint bprint; - char *str; - - if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error opening file %s.\n", filename); - return NULL; - } - - av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED); - ret = avio_read_to_bprint(pb, &bprint, SIZE_MAX); - avio_closep(&pb); - if (ret < 0) { - av_bprint_finalize(&bprint, NULL); - return NULL; - } - ret = av_bprint_finalize(&bprint, &str); - if (ret < 0) - return NULL; - return str; + return 0; } /* arg format is "output-stream-index:streamid-value". */ static int opt_streamid(void *optctx, const char *opt, const char *arg) { OptionsContext *o = optctx; - int idx; char *p; char idx_str[16]; @@ -736,25 +669,11 @@ static int opt_streamid(void *optctx, const char *opt, const char *arg) av_log(NULL, AV_LOG_FATAL, "Invalid value '%s' for option '%s', required syntax is 'index:value'\n", arg, opt); - exit_program(1); + return AVERROR(EINVAL); } *p++ = '\0'; - idx = parse_number_or_die(opt, idx_str, OPT_INT, 0, MAX_STREAMS-1); - o->streamid_map = grow_array(o->streamid_map, sizeof(*o->streamid_map), &o->nb_streamid_map, idx+1); - o->streamid_map[idx] = parse_number_or_die(opt, p, OPT_INT, 0, INT_MAX); - return 0; -} -static int init_complex_filters(void) -{ - int i, ret = 0; - - for (i = 0; i < nb_filtergraphs; i++) { - ret = init_complex_filtergraph(filtergraphs[i]); - if (ret < 0) - return ret; - } - return 0; + return av_dict_set(&o->streamid, idx_str, p, 0); } static int opt_target(void *optctx, const char *opt, const char *arg) @@ -803,7 +722,7 @@ static int opt_target(void *optctx, const char *opt, const char *arg) av_log(NULL, AV_LOG_FATAL, "Could not determine norm (PAL/NTSC/NTSC-Film) for target.\n"); av_log(NULL, AV_LOG_FATAL, "Please prefix target with \"pal-\", \"ntsc-\" or \"film-\",\n"); av_log(NULL, AV_LOG_FATAL, "or set a framerate with \"-r xxx\".\n"); - exit_program(1); + return AVERROR(EINVAL); } if (!strcmp(arg, "vcd")) { @@ -915,7 +834,7 @@ static int opt_vstats(void *optctx, const char *opt, const char *arg) if (!today) { // maybe tomorrow av_log(NULL, AV_LOG_FATAL, "Unable to get current time: %s\n", strerror(errno)); - exit_program(1); + return AVERROR(errno); } snprintf(filename, sizeof(filename), "vstats_%02d%02d%02d.log", today->tm_hour, today->tm_min, @@ -967,17 +886,16 @@ static int opt_preset(void *optctx, const char *opt, const char *arg) FILE *f=NULL; char filename[1000], line[1000], tmp_line[1000]; const char *codec_name = NULL; + int ret = 0; - tmp_line[0] = *opt; - tmp_line[1] = 0; - MATCH_PER_TYPE_OPT(codec_names, str, codec_name, NULL, tmp_line); + codec_name = opt_match_per_type_str(&o->codec_names, *opt); if (!(f = get_preset_file(filename, sizeof(filename), arg, *opt == 'f', codec_name))) { if(!strncmp(arg, "libx264-lossless", strlen("libx264-lossless"))){ av_log(NULL, AV_LOG_FATAL, "Please use -preset -qp 0\n"); }else av_log(NULL, AV_LOG_FATAL, "File for preset '%s' not found\n", arg); - exit_program(1); + return AVERROR(ENOENT); } while (fgets(line, sizeof(line), f)) { @@ -989,7 +907,8 @@ static int opt_preset(void *optctx, const char *opt, const char *arg) if (!av_strtok(key, "=", &value) || !av_strtok(value, "\r\n", &endptr)) { av_log(NULL, AV_LOG_FATAL, "%s: Invalid syntax: '%s'\n", filename, line); - exit_program(1); + ret = AVERROR(EINVAL); + goto fail; } av_log(NULL, AV_LOG_DEBUG, "ffpreset[%s]: set '%s' = '%s'\n", filename, key, value); @@ -1000,13 +919,15 @@ static int opt_preset(void *optctx, const char *opt, const char *arg) else if (opt_default_new(o, key, value) < 0) { av_log(NULL, AV_LOG_FATAL, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, key, value); - exit_program(1); + ret = AVERROR(EINVAL); + goto fail; } } +fail: fclose(f); - return 0; + return ret; } static int opt_old2new(void *optctx, const char *opt, const char *arg) @@ -1078,12 +999,13 @@ static int opt_audio_filters(void *optctx, const char *opt, const char *arg) return parse_option(o, "filter:a", arg, options); } +#if FFMPEG_OPT_VSYNC static int opt_vsync(void *optctx, const char *opt, const char *arg) { av_log(NULL, AV_LOG_WARNING, "-vsync is deprecated. Use -fps_mode\n"); - parse_and_set_vsync(arg, &video_sync_method, -1, -1, 1); - return 0; + return parse_and_set_vsync(arg, &video_sync_method, -1, -1, 1); } +#endif static int opt_timecode(void *optctx, const char *opt, const char *arg) { @@ -1107,34 +1029,31 @@ static int opt_audio_qscale(void *optctx, const char *opt, const char *arg) static int opt_filter_complex(void *optctx, const char *opt, const char *arg) { - FilterGraph *fg = ALLOC_ARRAY_ELEM(filtergraphs, nb_filtergraphs); - - fg->index = nb_filtergraphs - 1; - fg->graph_desc = av_strdup(arg); - if (!fg->graph_desc) + Scheduler *sch = optctx; + char *graph_desc = av_strdup(arg); + if (!graph_desc) return AVERROR(ENOMEM); - return 0; + return fg_create(NULL, graph_desc, sch); } +#if FFMPEG_OPT_FILTER_SCRIPT static int opt_filter_complex_script(void *optctx, const char *opt, const char *arg) { - FilterGraph *fg; + Scheduler *sch = optctx; char *graph_desc = file_read(arg); if (!graph_desc) return AVERROR(EINVAL); - fg = ALLOC_ARRAY_ELEM(filtergraphs, nb_filtergraphs); - fg->index = nb_filtergraphs - 1; - fg->graph_desc = graph_desc; + av_log(NULL, AV_LOG_WARNING, "-%s is deprecated, use -/filter_complex %s instead\n", + opt, arg); - return 0; + return fg_create(NULL, graph_desc, sch); } +#endif void show_help_default(const char *opt, const char *arg) { - /* per-file options have at least one of those set */ - const int per_file = OPT_SPEC | OPT_OFFSET | OPT_PERFILE; int show_advanced = 0, show_avoptions = 0; if (opt && *opt) { @@ -1154,38 +1073,85 @@ void show_help_default(const char *opt, const char *arg) " -h full -- print all options (including all format and codec specific options, very long)\n" " -h type=name -- print all options for the named decoder/encoder/demuxer/muxer/filter/bsf/protocol\n" " See man %s for detailed description of the options.\n" + "\n" + "Per-stream options can be followed by : to apply that option to specific streams only. " + " can be a stream index, or v/a/s for video/audio/subtitle (see manual for full syntax).\n" "\n", program_name); show_help_options(options, "Print help / information / capabilities:", - OPT_EXIT, 0, 0); + OPT_EXIT, OPT_EXPERT); + if (show_advanced) + show_help_options(options, "Advanced information / capabilities:", + OPT_EXIT | OPT_EXPERT, 0); show_help_options(options, "Global options (affect whole program " "instead of just one file):", - 0, per_file | OPT_EXIT | OPT_EXPERT, 0); + 0, OPT_PERFILE | OPT_EXIT | OPT_EXPERT); if (show_advanced) show_help_options(options, "Advanced global options:", OPT_EXPERT, - per_file | OPT_EXIT, 0); + OPT_PERFILE | OPT_EXIT); - show_help_options(options, "Per-file main options:", 0, - OPT_EXPERT | OPT_AUDIO | OPT_VIDEO | OPT_SUBTITLE | - OPT_EXIT, per_file); + show_help_options(options, "Per-file options (input and output):", + OPT_PERFILE | OPT_INPUT | OPT_OUTPUT, + OPT_EXIT | OPT_FLAG_PERSTREAM | OPT_EXPERT | + OPT_VIDEO | OPT_AUDIO | OPT_SUBTITLE | OPT_DATA); + if (show_advanced) + show_help_options(options, "Advanced per-file options (input and output):", + OPT_PERFILE | OPT_INPUT | OPT_OUTPUT | OPT_EXPERT, + OPT_EXIT | OPT_FLAG_PERSTREAM | + OPT_VIDEO | OPT_AUDIO | OPT_SUBTITLE | OPT_DATA); + + show_help_options(options, "Per-file options (input-only):", + OPT_PERFILE | OPT_INPUT, + OPT_EXIT | OPT_FLAG_PERSTREAM | OPT_OUTPUT | OPT_EXPERT | + OPT_VIDEO | OPT_AUDIO | OPT_SUBTITLE | OPT_DATA); + if (show_advanced) + show_help_options(options, "Advanced per-file options (input-only):", + OPT_PERFILE | OPT_INPUT | OPT_EXPERT, + OPT_EXIT | OPT_FLAG_PERSTREAM | OPT_OUTPUT | + OPT_VIDEO | OPT_AUDIO | OPT_SUBTITLE | OPT_DATA); + + show_help_options(options, "Per-file options (output-only):", + OPT_PERFILE | OPT_OUTPUT, + OPT_EXIT | OPT_FLAG_PERSTREAM | OPT_INPUT | OPT_EXPERT | + OPT_VIDEO | OPT_AUDIO | OPT_SUBTITLE | OPT_DATA); + if (show_advanced) + show_help_options(options, "Advanced per-file options (output-only):", + OPT_PERFILE | OPT_OUTPUT | OPT_EXPERT, + OPT_EXIT | OPT_FLAG_PERSTREAM | OPT_INPUT | + OPT_VIDEO | OPT_AUDIO | OPT_SUBTITLE | OPT_DATA); + + show_help_options(options, "Per-stream options:", + OPT_FLAG_PERSTREAM, + OPT_EXIT | OPT_EXPERT | + OPT_VIDEO | OPT_AUDIO | OPT_SUBTITLE | OPT_DATA); if (show_advanced) - show_help_options(options, "Advanced per-file options:", - OPT_EXPERT, OPT_AUDIO | OPT_VIDEO | OPT_SUBTITLE, per_file); + show_help_options(options, "Advanced per-stream options:", + OPT_FLAG_PERSTREAM | OPT_EXPERT, + OPT_EXIT | + OPT_VIDEO | OPT_AUDIO | OPT_SUBTITLE | OPT_DATA); show_help_options(options, "Video options:", - OPT_VIDEO, OPT_EXPERT | OPT_AUDIO, 0); + OPT_VIDEO, OPT_EXPERT | OPT_AUDIO | OPT_SUBTITLE | OPT_DATA); if (show_advanced) show_help_options(options, "Advanced Video options:", - OPT_EXPERT | OPT_VIDEO, OPT_AUDIO, 0); + OPT_EXPERT | OPT_VIDEO, OPT_AUDIO | OPT_SUBTITLE | OPT_DATA); show_help_options(options, "Audio options:", - OPT_AUDIO, OPT_EXPERT | OPT_VIDEO, 0); + OPT_AUDIO, OPT_EXPERT | OPT_VIDEO | OPT_SUBTITLE | OPT_DATA); if (show_advanced) show_help_options(options, "Advanced Audio options:", - OPT_EXPERT | OPT_AUDIO, OPT_VIDEO, 0); + OPT_EXPERT | OPT_AUDIO, OPT_VIDEO | OPT_SUBTITLE | OPT_DATA); + show_help_options(options, "Subtitle options:", - OPT_SUBTITLE, 0, 0); + OPT_SUBTITLE, OPT_EXPERT | OPT_VIDEO | OPT_AUDIO | OPT_DATA); + if (show_advanced) + show_help_options(options, "Advanced Subtitle options:", + OPT_EXPERT | OPT_SUBTITLE, OPT_VIDEO | OPT_AUDIO | OPT_DATA); + + if (show_advanced) + show_help_options(options, "Data stream options:", + OPT_DATA, OPT_VIDEO | OPT_AUDIO | OPT_SUBTITLE); printf("\n"); if (show_avoptions) { @@ -1205,7 +1171,7 @@ void show_help_default(const char *opt, const char *arg) void show_usage(void) { - av_log(NULL, AV_LOG_INFO, "Hyper fast Audio and Video encoder\n"); + av_log(NULL, AV_LOG_INFO, "Universal media converter\n"); av_log(NULL, AV_LOG_INFO, "usage: %s [options] [[infile options] -i infile]... {[outfile options] outfile}...\n", program_name); av_log(NULL, AV_LOG_INFO, "\n"); } @@ -1213,15 +1179,18 @@ void show_usage(void) enum OptGroup { GROUP_OUTFILE, GROUP_INFILE, + GROUP_DECODER, }; static const OptionGroupDef groups[] = { [GROUP_OUTFILE] = { "output url", NULL, OPT_OUTPUT }, [GROUP_INFILE] = { "input url", "i", OPT_INPUT }, + [GROUP_DECODER] = { "loopback decoder", "dec", OPT_DECODER }, }; -static int open_files(OptionGroupList *l, const char *inout, - int (*open_file)(const OptionsContext*, const char*)) +static int open_files(OptionGroupList *l, const char *inout, Scheduler *sch, + int (*open_file)(const OptionsContext*, const char*, + Scheduler*)) { int i, ret; @@ -1232,7 +1201,7 @@ static int open_files(OptionGroupList *l, const char *inout, init_options(&o); o.g = g; - ret = parse_optgroup(&o, g); + ret = parse_optgroup(&o, g, options); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Error parsing options for %s file " "%s.\n", inout, g->arg); @@ -1241,7 +1210,7 @@ static int open_files(OptionGroupList *l, const char *inout, } av_log(NULL, AV_LOG_DEBUG, "Opening an %s file: %s.\n", inout, g->arg); - ret = open_file(&o, g->arg); + ret = open_file(&o, g->arg, sch); uninit_options(&o); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Error opening %s file %s.\n", @@ -1254,9 +1223,10 @@ static int open_files(OptionGroupList *l, const char *inout, return 0; } -int ffmpeg_parse_options(int argc, char **argv) +int ffmpeg_parse_options(int argc, char **argv, Scheduler *sch) { OptionParseContext octx; + const char *errmsg = NULL; int ret; memset(&octx, 0, sizeof(octx)); @@ -1265,14 +1235,14 @@ int ffmpeg_parse_options(int argc, char **argv) ret = split_commandline(&octx, argc, argv, options, groups, FF_ARRAY_ELEMS(groups)); if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Error splitting the argument list: "); + errmsg = "splitting the argument list"; goto fail; } /* apply global options */ - ret = parse_optgroup(NULL, &octx.global_opts); + ret = parse_optgroup(sch, &octx.global_opts, options); if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Error parsing global options: "); + errmsg = "parsing global options"; goto fail; } @@ -1280,36 +1250,46 @@ int ffmpeg_parse_options(int argc, char **argv) term_init(); /* open input files */ - ret = open_files(&octx.groups[GROUP_INFILE], "input", ifile_open); + ret = open_files(&octx.groups[GROUP_INFILE], "input", sch, ifile_open); if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Error opening input files: "); + errmsg = "opening input files"; goto fail; } - /* create the complex filtergraphs */ - ret = init_complex_filters(); + /* open output files */ + ret = open_files(&octx.groups[GROUP_OUTFILE], "output", sch, of_open); if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Error initializing complex filters.\n"); + errmsg = "opening output files"; goto fail; } - /* open output files */ - ret = open_files(&octx.groups[GROUP_OUTFILE], "output", of_open); + /* create loopback decoders */ + ret = open_files(&octx.groups[GROUP_DECODER], "decoder", sch, dec_create); if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "Error opening output files: "); + errmsg = "creating loopback decoders"; goto fail; } - correct_input_start_times(); + // bind unbound filtegraph inputs/outputs and check consistency + for (int i = 0; i < nb_filtergraphs; i++) { + ret = fg_finalise_bindings(filtergraphs[i]); + if (ret < 0) { + errmsg = "binding filtergraph inputs/outputs"; + goto fail; + } + } - apply_sync_offsets(); + correct_input_start_times(); - check_filter_outputs(); + ret = apply_sync_offsets(); + if (ret < 0) + goto fail; fail: uninit_parse_context(&octx); - if (ret < 0) { - av_log(NULL, AV_LOG_FATAL, "%s\n", av_err2str(ret)); + if (ret < 0 && ret != AVERROR_EXIT) { + av_log(NULL, AV_LOG_FATAL, "Error %s: %s\n", + errmsg ? errmsg : "", av_err2str(ret)); } return ret; } @@ -1334,8 +1314,15 @@ static int opt_progress(void *optctx, const char *opt, const char *arg) int opt_timelimit(void *optctx, const char *opt, const char *arg) { #if HAVE_SETRLIMIT - int lim = parse_number_or_die(opt, arg, OPT_INT64, 0, INT_MAX); - struct rlimit rl = { lim, lim + 1 }; + int ret; + double lim; + struct rlimit rl; + + ret = parse_number(opt, arg, OPT_TYPE_INT64, 0, INT_MAX, &lim); + if (ret < 0) + return ret; + + rl = (struct rlimit){ lim, lim + 1 }; if (setrlimit(RLIMIT_CPU, &rl)) perror("setrlimit"); #else @@ -1344,432 +1331,585 @@ int opt_timelimit(void *optctx, const char *opt, const char *arg) return 0; } +#if FFMPEG_OPT_QPHIST +static int opt_qphist(void *optctx, const char *opt, const char *arg) +{ + av_log(NULL, AV_LOG_WARNING, "Option -%s is deprecated and has no effect\n", opt); + return 0; +} +#endif + +#if FFMPEG_OPT_ADRIFT_THRESHOLD +static int opt_adrift_threshold(void *optctx, const char *opt, const char *arg) +{ + av_log(NULL, AV_LOG_WARNING, "Option -%s is deprecated and has no effect\n", opt); + return 0; +} +#endif + +static const char *const alt_bsf[] = { "absf", "vbsf", NULL }; +static const char *const alt_channel_layout[] = { "ch_layout", NULL}; +static const char *const alt_codec[] = { "c", "acodec", "vcodec", "scodec", "dcodec", NULL }; +static const char *const alt_filter[] = { "af", "vf", NULL }; +static const char *const alt_frames[] = { "aframes", "vframes", "dframes", NULL }; +static const char *const alt_pre[] = { "apre", "vpre", "spre", NULL}; +static const char *const alt_qscale[] = { "q", NULL}; +static const char *const alt_tag[] = { "atag", "vtag", "stag", NULL }; + #define OFFSET(x) offsetof(OptionsContext, x) const OptionDef options[] = { /* main options */ CMDUTILS_COMMON_OPTIONS - { "f", HAS_ARG | OPT_STRING | OPT_OFFSET | - OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(format) }, - "force format", "fmt" }, - { "y", OPT_BOOL, { &file_overwrite }, + { "f", OPT_TYPE_STRING, OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(format) }, + "force container format (auto-detected otherwise)", "fmt" }, + { "y", OPT_TYPE_BOOL, 0, + { &file_overwrite }, "overwrite output files" }, - { "n", OPT_BOOL, { &no_file_overwrite }, + { "n", OPT_TYPE_BOOL, 0, + { &no_file_overwrite }, "never overwrite output files" }, - { "ignore_unknown", OPT_BOOL, { &ignore_unknown_streams }, + { "ignore_unknown", OPT_TYPE_BOOL, OPT_EXPERT, + { &ignore_unknown_streams }, "Ignore unknown stream types" }, - { "copy_unknown", OPT_BOOL | OPT_EXPERT, { ©_unknown_streams }, + { "copy_unknown", OPT_TYPE_BOOL, OPT_EXPERT, + { ©_unknown_streams }, "Copy unknown stream types" }, - { "recast_media", OPT_BOOL | OPT_EXPERT, { &recast_media }, + { "recast_media", OPT_TYPE_BOOL, OPT_EXPERT, + { &recast_media }, "allow recasting stream type in order to force a decoder of different media type" }, - { "c", HAS_ARG | OPT_STRING | OPT_SPEC | - OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(codec_names) }, - "codec name", "codec" }, - { "codec", HAS_ARG | OPT_STRING | OPT_SPEC | - OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(codec_names) }, - "codec name", "codec" }, - { "pre", HAS_ARG | OPT_STRING | OPT_SPEC | - OPT_OUTPUT, { .off = OFFSET(presets) }, - "preset name", "preset" }, - { "map", HAS_ARG | OPT_EXPERT | OPT_PERFILE | - OPT_OUTPUT, { .func_arg = opt_map }, + { "c", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT | OPT_DECODER | OPT_HAS_CANON, + { .off = OFFSET(codec_names) }, + "select encoder/decoder ('copy' to copy stream without reencoding)", "codec", + .u1.name_canon = "codec", }, + { "codec", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT | OPT_DECODER | OPT_EXPERT | OPT_HAS_ALT, + { .off = OFFSET(codec_names) }, + "alias for -c (select encoder/decoder)", "codec", + .u1.names_alt = alt_codec, }, + { "pre", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_OUTPUT | OPT_EXPERT | OPT_HAS_ALT, + { .off = OFFSET(presets) }, + "preset name", "preset", + .u1.names_alt = alt_pre, }, + { "map", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT, + { .func_arg = opt_map }, "set input stream mapping", "[-]input_file_id[:stream_specifier][,sync_file_id[:stream_specifier]]" }, -#if FFMPEG_OPT_MAP_CHANNEL - { "map_channel", HAS_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_map_channel }, - "map an audio channel from one stream to another (deprecated)", "file.stream.channel[:syncfile.syncstream]" }, -#endif - { "map_metadata", HAS_ARG | OPT_STRING | OPT_SPEC | - OPT_OUTPUT, { .off = OFFSET(metadata_map) }, + { "map_metadata", OPT_TYPE_STRING, OPT_SPEC | OPT_OUTPUT | OPT_EXPERT, + { .off = OFFSET(metadata_map) }, "set metadata information of outfile from infile", "outfile[,metadata]:infile[,metadata]" }, - { "map_chapters", HAS_ARG | OPT_INT | OPT_EXPERT | OPT_OFFSET | - OPT_OUTPUT, { .off = OFFSET(chapters_input_file) }, + { "map_chapters", OPT_TYPE_INT, OPT_EXPERT | OPT_OFFSET | OPT_OUTPUT, + { .off = OFFSET(chapters_input_file) }, "set chapters mapping", "input_file_index" }, - { "t", HAS_ARG | OPT_TIME | OPT_OFFSET | - OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(recording_time) }, - "record or transcode \"duration\" seconds of audio/video", + { "t", OPT_TYPE_TIME, OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(recording_time) }, + "stop transcoding after specified duration", "duration" }, - { "to", HAS_ARG | OPT_TIME | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(stop_time) }, - "record or transcode stop time", "time_stop" }, - { "fs", HAS_ARG | OPT_INT64 | OPT_OFFSET | OPT_OUTPUT, { .off = OFFSET(limit_filesize) }, + { "to", OPT_TYPE_TIME, OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(stop_time) }, + "stop transcoding after specified time is reached", + "time_stop" }, + { "fs", OPT_TYPE_INT64, OPT_OFFSET | OPT_OUTPUT | OPT_EXPERT, + { .off = OFFSET(limit_filesize) }, "set the limit file size in bytes", "limit_size" }, - { "ss", HAS_ARG | OPT_TIME | OPT_OFFSET | - OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(start_time) }, - "set the start time offset", "time_off" }, - { "sseof", HAS_ARG | OPT_TIME | OPT_OFFSET | - OPT_INPUT, { .off = OFFSET(start_time_eof) }, + { "ss", OPT_TYPE_TIME, OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(start_time) }, + "start transcoding at specified time", "time_off" }, + { "sseof", OPT_TYPE_TIME, OPT_OFFSET | OPT_INPUT | OPT_EXPERT, + { .off = OFFSET(start_time_eof) }, "set the start time offset relative to EOF", "time_off" }, - { "seek_timestamp", HAS_ARG | OPT_INT | OPT_OFFSET | - OPT_INPUT, { .off = OFFSET(seek_timestamp) }, + { "seek_timestamp", OPT_TYPE_INT, OPT_OFFSET | OPT_INPUT | OPT_EXPERT, + { .off = OFFSET(seek_timestamp) }, "enable/disable seeking by timestamp with -ss" }, - { "accurate_seek", OPT_BOOL | OPT_OFFSET | OPT_EXPERT | - OPT_INPUT, { .off = OFFSET(accurate_seek) }, + { "accurate_seek", OPT_TYPE_BOOL, OPT_OFFSET | OPT_EXPERT | OPT_INPUT, + { .off = OFFSET(accurate_seek) }, "enable/disable accurate seeking with -ss" }, - { "isync", HAS_ARG | OPT_INT | OPT_OFFSET | - OPT_EXPERT | OPT_INPUT, { .off = OFFSET(input_sync_ref) }, + { "isync", OPT_TYPE_INT, OPT_OFFSET | OPT_EXPERT | OPT_INPUT, + { .off = OFFSET(input_sync_ref) }, "Indicate the input index for sync reference", "sync ref" }, - { "itsoffset", HAS_ARG | OPT_TIME | OPT_OFFSET | - OPT_EXPERT | OPT_INPUT, { .off = OFFSET(input_ts_offset) }, + { "itsoffset", OPT_TYPE_TIME, OPT_OFFSET | OPT_EXPERT | OPT_INPUT, + { .off = OFFSET(input_ts_offset) }, "set the input ts offset", "time_off" }, - { "itsscale", HAS_ARG | OPT_DOUBLE | OPT_SPEC | - OPT_EXPERT | OPT_INPUT, { .off = OFFSET(ts_scale) }, + { "itsscale", OPT_TYPE_DOUBLE, OPT_PERSTREAM | OPT_EXPERT | OPT_INPUT, + { .off = OFFSET(ts_scale) }, "set the input ts scale", "scale" }, - { "timestamp", HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_recording_timestamp }, + { "timestamp", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_PERFILE | OPT_EXPERT | OPT_OUTPUT, + { .func_arg = opt_recording_timestamp }, "set the recording timestamp ('now' to set the current time)", "time" }, - { "metadata", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(metadata) }, - "add metadata", "string=string" }, - { "program", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(program) }, + { "metadata", OPT_TYPE_STRING, OPT_SPEC | OPT_OUTPUT, + { .off = OFFSET(metadata) }, + "add metadata", "key=value" }, + { "program", OPT_TYPE_STRING, OPT_SPEC | OPT_EXPERT | OPT_OUTPUT, + { .off = OFFSET(program) }, "add program with specified streams", "title=string:st=number..." }, - { "dframes", HAS_ARG | OPT_PERFILE | OPT_EXPERT | - OPT_OUTPUT, { .func_arg = opt_data_frames }, - "set the number of data frames to output", "number" }, - { "benchmark", OPT_BOOL | OPT_EXPERT, { &do_benchmark }, + { "stream_group", OPT_TYPE_STRING, OPT_SPEC | OPT_OUTPUT | OPT_EXPERT, + { .off = OFFSET(stream_groups) }, + "add stream group with specified streams and group type-specific arguments", "id=number:st=number..." }, + { "dframes", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_PERFILE | OPT_EXPERT | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_data_frames }, + "set the number of data frames to output", "number", + .u1.name_canon = "frames" }, + { "benchmark", OPT_TYPE_BOOL, OPT_EXPERT, + { &do_benchmark }, "add timings for benchmarking" }, - { "benchmark_all", OPT_BOOL | OPT_EXPERT, { &do_benchmark_all }, + { "benchmark_all", OPT_TYPE_BOOL, OPT_EXPERT, + { &do_benchmark_all }, "add timings for each task" }, - { "progress", HAS_ARG | OPT_EXPERT, { .func_arg = opt_progress }, + { "progress", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_progress }, "write program-readable progress information", "url" }, - { "stdin", OPT_BOOL | OPT_EXPERT, { &stdin_interaction }, + { "stdin", OPT_TYPE_BOOL, OPT_EXPERT, + { &stdin_interaction }, "enable or disable interaction on standard input" }, - { "timelimit", HAS_ARG | OPT_EXPERT, { .func_arg = opt_timelimit }, + { "timelimit", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_timelimit }, "set max runtime in seconds in CPU user time", "limit" }, - { "dump", OPT_BOOL | OPT_EXPERT, { &do_pkt_dump }, + { "dump", OPT_TYPE_BOOL, OPT_EXPERT, + { &do_pkt_dump }, "dump each input packet" }, - { "hex", OPT_BOOL | OPT_EXPERT, { &do_hex_dump }, + { "hex", OPT_TYPE_BOOL, OPT_EXPERT, + { &do_hex_dump }, "when dumping packets, also dump the payload" }, - { "re", OPT_BOOL | OPT_EXPERT | OPT_OFFSET | - OPT_INPUT, { .off = OFFSET(rate_emu) }, + { "re", OPT_TYPE_BOOL, OPT_EXPERT | OPT_OFFSET | OPT_INPUT, + { .off = OFFSET(rate_emu) }, "read input at native frame rate; equivalent to -readrate 1", "" }, - { "readrate", HAS_ARG | OPT_FLOAT | OPT_OFFSET | - OPT_EXPERT | OPT_INPUT, { .off = OFFSET(readrate) }, + { "readrate", OPT_TYPE_FLOAT, OPT_OFFSET | OPT_EXPERT | OPT_INPUT, + { .off = OFFSET(readrate) }, "read input at specified rate", "speed" }, - { "target", HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_target }, + { "readrate_initial_burst", OPT_TYPE_DOUBLE, OPT_OFFSET | OPT_EXPERT | OPT_INPUT, + { .off = OFFSET(readrate_initial_burst) }, + "The initial amount of input to burst read before imposing any readrate", "seconds" }, + { "target", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_PERFILE | OPT_EXPERT | OPT_OUTPUT, + { .func_arg = opt_target }, "specify target file type (\"vcd\", \"svcd\", \"dvd\", \"dv\" or \"dv50\" " "with optional prefixes \"pal-\", \"ntsc-\" or \"film-\")", "type" }, - { "vsync", HAS_ARG | OPT_EXPERT, { .func_arg = opt_vsync }, - "set video sync method globally; deprecated, use -fps_mode", "" }, - { "frame_drop_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT, { &frame_drop_threshold }, + { "frame_drop_threshold", OPT_TYPE_FLOAT, OPT_EXPERT, + { &frame_drop_threshold }, "frame drop threshold", "" }, - { "adrift_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT, { &audio_drift_threshold }, - "audio drift threshold", "threshold" }, - { "copyts", OPT_BOOL | OPT_EXPERT, { ©_ts }, + { "copyts", OPT_TYPE_BOOL, OPT_EXPERT, + { ©_ts }, "copy timestamps" }, - { "start_at_zero", OPT_BOOL | OPT_EXPERT, { &start_at_zero }, + { "start_at_zero", OPT_TYPE_BOOL, OPT_EXPERT, + { &start_at_zero }, "shift input timestamps to start at 0 when using copyts" }, - { "copytb", HAS_ARG | OPT_INT | OPT_EXPERT, { ©_tb }, + { "copytb", OPT_TYPE_INT, OPT_EXPERT, + { ©_tb }, "copy input stream time base when stream copying", "mode" }, - { "shortest", OPT_BOOL | OPT_EXPERT | OPT_OFFSET | - OPT_OUTPUT, { .off = OFFSET(shortest) }, + { "shortest", OPT_TYPE_BOOL, OPT_EXPERT | OPT_OFFSET | OPT_OUTPUT, + { .off = OFFSET(shortest) }, "finish encoding within shortest input" }, - { "shortest_buf_duration", HAS_ARG | OPT_FLOAT | OPT_EXPERT | OPT_OFFSET | OPT_OUTPUT, { .off = OFFSET(shortest_buf_duration) }, + { "shortest_buf_duration", OPT_TYPE_FLOAT, OPT_EXPERT | OPT_OFFSET | OPT_OUTPUT, + { .off = OFFSET(shortest_buf_duration) }, "maximum buffering duration (in seconds) for the -shortest option" }, - { "bitexact", OPT_BOOL | OPT_EXPERT | OPT_OFFSET | - OPT_OUTPUT | OPT_INPUT, { .off = OFFSET(bitexact) }, + { "bitexact", OPT_TYPE_BOOL, OPT_EXPERT | OPT_OFFSET | OPT_OUTPUT | OPT_INPUT, + { .off = OFFSET(bitexact) }, "bitexact mode" }, - { "apad", OPT_STRING | HAS_ARG | OPT_SPEC | - OPT_OUTPUT, { .off = OFFSET(apad) }, + { "apad", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT, + { .off = OFFSET(apad) }, "audio pad", "" }, - { "dts_delta_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT, { &dts_delta_threshold }, + { "dts_delta_threshold", OPT_TYPE_FLOAT, OPT_EXPERT, + { &dts_delta_threshold }, "timestamp discontinuity delta threshold", "threshold" }, - { "dts_error_threshold", HAS_ARG | OPT_FLOAT | OPT_EXPERT, { &dts_error_threshold }, + { "dts_error_threshold", OPT_TYPE_FLOAT, OPT_EXPERT, + { &dts_error_threshold }, "timestamp error delta threshold", "threshold" }, - { "xerror", OPT_BOOL | OPT_EXPERT, { &exit_on_error }, + { "xerror", OPT_TYPE_BOOL, OPT_EXPERT, + { &exit_on_error }, "exit on error", "error" }, - { "abort_on", HAS_ARG | OPT_EXPERT, { .func_arg = opt_abort_on }, + { "abort_on", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_abort_on }, "abort on the specified condition flags", "flags" }, - { "copyinkf", OPT_BOOL | OPT_EXPERT | OPT_SPEC | - OPT_OUTPUT, { .off = OFFSET(copy_initial_nonkeyframes) }, + { "copyinkf", OPT_TYPE_BOOL, OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(copy_initial_nonkeyframes) }, "copy initial non-keyframes" }, - { "copypriorss", OPT_INT | HAS_ARG | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(copy_prior_start) }, + { "copypriorss", OPT_TYPE_INT, OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(copy_prior_start) }, "copy or discard frames before start time" }, - { "frames", OPT_INT64 | HAS_ARG | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(max_frames) }, - "set the number of frames to output", "number" }, - { "tag", OPT_STRING | HAS_ARG | OPT_SPEC | - OPT_EXPERT | OPT_OUTPUT | OPT_INPUT, { .off = OFFSET(codec_tags) }, - "force codec tag/fourcc", "fourcc/tag" }, - { "q", HAS_ARG | OPT_EXPERT | OPT_DOUBLE | - OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(qscale) }, - "use fixed quality scale (VBR)", "q" }, - { "qscale", HAS_ARG | OPT_EXPERT | OPT_PERFILE | - OPT_OUTPUT, { .func_arg = opt_qscale }, - "use fixed quality scale (VBR)", "q" }, - { "profile", HAS_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_profile }, + { "frames", OPT_TYPE_INT64, OPT_PERSTREAM | OPT_OUTPUT | OPT_EXPERT | OPT_HAS_ALT, + { .off = OFFSET(max_frames) }, + "set the number of frames to output", "number", + .u1.names_alt = alt_frames, }, + { "tag", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT | OPT_INPUT | OPT_HAS_ALT, + { .off = OFFSET(codec_tags) }, + "force codec tag/fourcc", "fourcc/tag", + .u1.names_alt = alt_tag, }, + { "q", OPT_TYPE_DOUBLE, OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT | OPT_HAS_CANON, + { .off = OFFSET(qscale) }, + "use fixed quality scale (VBR)", "q", + .u1.name_canon = "qscale", }, + { "qscale", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT | OPT_HAS_ALT, + { .func_arg = opt_qscale }, + "use fixed quality scale (VBR)", "q", + .u1.names_alt = alt_qscale, }, + { "profile", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT, + { .func_arg = opt_profile }, "set profile", "profile" }, - { "filter", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(filters) }, - "set stream filtergraph", "filter_graph" }, - { "filter_threads", HAS_ARG, { .func_arg = opt_filter_threads }, + { "filter", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_OUTPUT | OPT_HAS_ALT, + { .off = OFFSET(filters) }, + "apply specified filters to audio/video", "filter_graph", + .u1.names_alt = alt_filter, }, + { "filter_threads", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_filter_threads }, "number of non-complex filter threads" }, - { "filter_script", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(filter_scripts) }, - "read stream filtergraph description from a file", "filename" }, - { "reinit_filter", HAS_ARG | OPT_INT | OPT_SPEC | OPT_INPUT, { .off = OFFSET(reinit_filters) }, +#if FFMPEG_OPT_FILTER_SCRIPT + { "filter_script", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT, + { .off = OFFSET(filter_scripts) }, + "deprecated, use -/filter", "filename" }, +#endif + { "reinit_filter", OPT_TYPE_INT, OPT_PERSTREAM | OPT_INPUT | OPT_EXPERT, + { .off = OFFSET(reinit_filters) }, "reinit filtergraph on input parameter changes", "" }, - { "filter_complex", HAS_ARG | OPT_EXPERT, { .func_arg = opt_filter_complex }, + { "filter_complex", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_filter_complex }, "create a complex filtergraph", "graph_description" }, - { "filter_complex_threads", HAS_ARG | OPT_INT, { &filter_complex_nbthreads }, + { "filter_complex_threads", OPT_TYPE_INT, OPT_EXPERT, + { &filter_complex_nbthreads }, "number of threads for -filter_complex" }, - { "lavfi", HAS_ARG | OPT_EXPERT, { .func_arg = opt_filter_complex }, + { "lavfi", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_filter_complex }, "create a complex filtergraph", "graph_description" }, - { "filter_complex_script", HAS_ARG | OPT_EXPERT, { .func_arg = opt_filter_complex_script }, - "read complex filtergraph description from a file", "filename" }, - { "auto_conversion_filters", OPT_BOOL | OPT_EXPERT, { &auto_conversion_filters }, +#if FFMPEG_OPT_FILTER_SCRIPT + { "filter_complex_script", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_filter_complex_script }, + "deprecated, use -/filter_complex instead", "filename" }, +#endif + { "auto_conversion_filters", OPT_TYPE_BOOL, OPT_EXPERT, + { &auto_conversion_filters }, "enable automatic conversion filters globally" }, - { "stats", OPT_BOOL, { &print_stats }, + { "stats", OPT_TYPE_BOOL, 0, + { &print_stats }, "print progress report during encoding", }, - { "stats_period", HAS_ARG | OPT_EXPERT, { .func_arg = opt_stats_period }, + { "stats_period", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_stats_period }, "set the period at which ffmpeg updates stats and -progress output", "time" }, - { "attach", HAS_ARG | OPT_PERFILE | OPT_EXPERT | - OPT_OUTPUT, { .func_arg = opt_attach }, + { "attach", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_PERFILE | OPT_EXPERT | OPT_OUTPUT, + { .func_arg = opt_attach }, "add an attachment to the output file", "filename" }, - { "dump_attachment", HAS_ARG | OPT_STRING | OPT_SPEC | - OPT_EXPERT | OPT_INPUT, { .off = OFFSET(dump_attachment) }, + { "dump_attachment", OPT_TYPE_STRING, OPT_SPEC | OPT_EXPERT | OPT_INPUT, + { .off = OFFSET(dump_attachment) }, "extract an attachment into a file", "filename" }, - { "stream_loop", OPT_INT | HAS_ARG | OPT_EXPERT | OPT_INPUT | - OPT_OFFSET, { .off = OFFSET(loop) }, "set number of times input stream shall be looped", "loop count" }, - { "debug_ts", OPT_BOOL | OPT_EXPERT, { &debug_ts }, + { "stream_loop", OPT_TYPE_INT, OPT_EXPERT | OPT_INPUT | OPT_OFFSET, + { .off = OFFSET(loop) }, "set number of times input stream shall be looped", "loop count" }, + { "debug_ts", OPT_TYPE_BOOL, OPT_EXPERT, + { &debug_ts }, "print timestamp debugging info" }, - { "max_error_rate", HAS_ARG | OPT_FLOAT, { &max_error_rate }, + { "max_error_rate", OPT_TYPE_FLOAT, OPT_EXPERT, + { &max_error_rate }, "ratio of decoding errors (0.0: no errors, 1.0: 100% errors) above which ffmpeg returns an error instead of success.", "maximum error rate" }, - { "discard", OPT_STRING | HAS_ARG | OPT_SPEC | - OPT_INPUT, { .off = OFFSET(discard) }, + { "discard", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_INPUT | OPT_EXPERT, + { .off = OFFSET(discard) }, "discard", "" }, - { "disposition", OPT_STRING | HAS_ARG | OPT_SPEC | - OPT_OUTPUT, { .off = OFFSET(disposition) }, + { "disposition", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_OUTPUT | OPT_EXPERT, + { .off = OFFSET(disposition) }, "disposition", "" }, - { "thread_queue_size", HAS_ARG | OPT_INT | OPT_OFFSET | OPT_EXPERT | OPT_INPUT | OPT_OUTPUT, - { .off = OFFSET(thread_queue_size) }, + { "thread_queue_size", OPT_TYPE_INT, OPT_OFFSET | OPT_EXPERT | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(thread_queue_size) }, "set the maximum number of queued packets from the demuxer" }, - { "find_stream_info", OPT_BOOL | OPT_INPUT | OPT_EXPERT | OPT_OFFSET, { .off = OFFSET(find_stream_info) }, + { "find_stream_info", OPT_TYPE_BOOL, OPT_INPUT | OPT_EXPERT | OPT_OFFSET, + { .off = OFFSET(find_stream_info) }, "read and decode the streams to fill missing information with heuristics" }, - { "bits_per_raw_sample", OPT_INT | HAS_ARG | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT, + { "bits_per_raw_sample", OPT_TYPE_INT, OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, { .off = OFFSET(bits_per_raw_sample) }, "set the number of bits per raw sample", "number" }, - { "stats_enc_pre", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(enc_stats_pre) }, + { "stats_enc_pre", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT, + { .off = OFFSET(enc_stats_pre) }, "write encoding stats before encoding" }, - { "stats_enc_post", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(enc_stats_post) }, + { "stats_enc_post", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT, + { .off = OFFSET(enc_stats_post) }, "write encoding stats after encoding" }, - { "stats_mux_pre", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(mux_stats) }, + { "stats_mux_pre", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT, + { .off = OFFSET(mux_stats) }, "write packets stats before muxing" }, - { "stats_enc_pre_fmt", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(enc_stats_pre_fmt) }, + { "stats_enc_pre_fmt", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT, + { .off = OFFSET(enc_stats_pre_fmt) }, "format of the stats written with -stats_enc_pre" }, - { "stats_enc_post_fmt", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(enc_stats_post_fmt) }, + { "stats_enc_post_fmt", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT, + { .off = OFFSET(enc_stats_post_fmt) }, "format of the stats written with -stats_enc_post" }, - { "stats_mux_pre_fmt", HAS_ARG | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT | OPT_STRING, { .off = OFFSET(mux_stats_fmt) }, + { "stats_mux_pre_fmt", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT, + { .off = OFFSET(mux_stats_fmt) }, "format of the stats written with -stats_mux_pre" }, /* video options */ - { "vframes", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_video_frames }, - "set the number of video frames to output", "number" }, - { "r", OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_SPEC | - OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(frame_rates) }, - "set frame rate (Hz value, fraction or abbreviation)", "rate" }, - { "fpsmax", OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_SPEC | - OPT_OUTPUT, { .off = OFFSET(max_frame_rates) }, + { "vframes", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT | OPT_EXPERT | OPT_HAS_CANON, + { .func_arg = opt_video_frames }, + "set the number of video frames to output", "number", + .u1.name_canon = "frames", }, + { "r", OPT_TYPE_STRING, OPT_VIDEO | OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(frame_rates) }, + "override input framerate/convert to given output framerate (Hz value, fraction or abbreviation)", "rate" }, + { "fpsmax", OPT_TYPE_STRING, OPT_VIDEO | OPT_PERSTREAM | OPT_OUTPUT | OPT_EXPERT, + { .off = OFFSET(max_frame_rates) }, "set max frame rate (Hz value, fraction or abbreviation)", "rate" }, - { "s", OPT_VIDEO | HAS_ARG | OPT_SUBTITLE | OPT_STRING | OPT_SPEC | - OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(frame_sizes) }, + { "s", OPT_TYPE_STRING, OPT_VIDEO | OPT_SUBTITLE | OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(frame_sizes) }, "set frame size (WxH or abbreviation)", "size" }, - { "aspect", OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_SPEC | - OPT_OUTPUT, { .off = OFFSET(frame_aspect_ratios) }, + { "aspect", OPT_TYPE_STRING, OPT_VIDEO | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(frame_aspect_ratios) }, "set aspect ratio (4:3, 16:9 or 1.3333, 1.7777)", "aspect" }, - { "pix_fmt", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_STRING | OPT_SPEC | - OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(frame_pix_fmts) }, + { "pix_fmt", OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(frame_pix_fmts) }, "set pixel format", "format" }, - { "display_rotation", OPT_VIDEO | HAS_ARG | OPT_DOUBLE | OPT_SPEC | - OPT_INPUT, { .off = OFFSET(display_rotations) }, + { "display_rotation", OPT_TYPE_DOUBLE, OPT_VIDEO | OPT_PERSTREAM | OPT_INPUT | OPT_EXPERT, + { .off = OFFSET(display_rotations) }, "set pure counter-clockwise rotation in degrees for stream(s)", "angle" }, - { "display_hflip", OPT_VIDEO | OPT_BOOL | OPT_SPEC | OPT_INPUT, { .off = OFFSET(display_hflips) }, + { "display_hflip", OPT_TYPE_BOOL, OPT_VIDEO | OPT_PERSTREAM | OPT_INPUT | OPT_EXPERT, + { .off = OFFSET(display_hflips) }, "set display horizontal flip for stream(s) " "(overrides any display rotation if it is not set)"}, - { "display_vflip", OPT_VIDEO | OPT_BOOL | OPT_SPEC | OPT_INPUT, { .off = OFFSET(display_vflips) }, + { "display_vflip", OPT_TYPE_BOOL, OPT_VIDEO | OPT_PERSTREAM | OPT_INPUT | OPT_EXPERT, + { .off = OFFSET(display_vflips) }, "set display vertical flip for stream(s) " "(overrides any display rotation if it is not set)"}, - { "vn", OPT_VIDEO | OPT_BOOL | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT,{ .off = OFFSET(video_disable) }, + { "vn", OPT_TYPE_BOOL, OPT_VIDEO | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(video_disable) }, "disable video" }, - { "rc_override", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_STRING | OPT_SPEC | - OPT_OUTPUT, { .off = OFFSET(rc_overrides) }, + { "rc_override", OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(rc_overrides) }, "rate control override for specific intervals", "override" }, - { "vcodec", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_INPUT | - OPT_OUTPUT, { .func_arg = opt_video_codec }, - "force video codec ('copy' to copy stream)", "codec" }, - { "timecode", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_timecode }, + { "vcodec", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_video_codec }, + "alias for -c:v (select encoder/decoder for video streams)", "codec", + .u1.name_canon = "codec", }, + { "timecode", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT | OPT_EXPERT, + { .func_arg = opt_timecode }, "set initial TimeCode value.", "hh:mm:ss[:;.]ff" }, - { "pass", OPT_VIDEO | HAS_ARG | OPT_SPEC | OPT_INT | OPT_OUTPUT, { .off = OFFSET(pass) }, + { "pass", OPT_TYPE_INT, OPT_VIDEO | OPT_PERSTREAM | OPT_OUTPUT | OPT_EXPERT, + { .off = OFFSET(pass) }, "select the pass number (1 to 3)", "n" }, - { "passlogfile", OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_EXPERT | OPT_SPEC | - OPT_OUTPUT, { .off = OFFSET(passlogfiles) }, + { "passlogfile", OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(passlogfiles) }, "select two pass log file name prefix", "prefix" }, -#if FFMPEG_OPT_PSNR - { "psnr", OPT_VIDEO | OPT_BOOL | OPT_EXPERT, { &do_psnr }, - "calculate PSNR of compressed frames (deprecated, use -flags +psnr)" }, -#endif - { "vstats", OPT_VIDEO | OPT_EXPERT , { .func_arg = opt_vstats }, + { "vstats", OPT_TYPE_FUNC, OPT_VIDEO | OPT_EXPERT, + { .func_arg = opt_vstats }, "dump video coding statistics to file" }, - { "vstats_file", OPT_VIDEO | HAS_ARG | OPT_EXPERT , { .func_arg = opt_vstats_file }, + { "vstats_file", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_vstats_file }, "dump video coding statistics to file", "file" }, - { "vstats_version", OPT_VIDEO | OPT_INT | HAS_ARG | OPT_EXPERT , { &vstats_version }, + { "vstats_version", OPT_TYPE_INT, OPT_VIDEO | OPT_EXPERT, + { &vstats_version }, "Version of the vstats format to use."}, - { "vf", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_video_filters }, - "set video filters", "filter_graph" }, - { "intra_matrix", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_STRING | OPT_SPEC | - OPT_OUTPUT, { .off = OFFSET(intra_matrices) }, + { "vf", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_video_filters }, + "alias for -filter:v (apply filters to video streams)", "filter_graph", + .u1.name_canon = "filter", }, + { "intra_matrix", OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(intra_matrices) }, "specify intra matrix coeffs", "matrix" }, - { "inter_matrix", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_STRING | OPT_SPEC | - OPT_OUTPUT, { .off = OFFSET(inter_matrices) }, + { "inter_matrix", OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(inter_matrices) }, "specify inter matrix coeffs", "matrix" }, - { "chroma_intra_matrix", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_STRING | OPT_SPEC | - OPT_OUTPUT, { .off = OFFSET(chroma_intra_matrices) }, + { "chroma_intra_matrix", OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(chroma_intra_matrices) }, "specify intra matrix coeffs", "matrix" }, - { "top", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_INT| OPT_SPEC | - OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(top_field_first) }, - "top=1/bottom=0/auto=-1 field first", "" }, - { "vtag", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_PERFILE | - OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_old2new }, - "force video tag/fourcc", "fourcc/tag" }, - { "qphist", OPT_VIDEO | OPT_BOOL | OPT_EXPERT , { &qp_hist }, - "show QP histogram" }, - { "fps_mode", OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_EXPERT | - OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(fps_mode) }, + { "vtag", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_EXPERT | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_old2new }, + "force video tag/fourcc", "fourcc/tag", + .u1.name_canon = "tag", }, + { "fps_mode", OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(fps_mode) }, "set framerate mode for matching video streams; overrides vsync" }, - { "force_fps", OPT_VIDEO | OPT_BOOL | OPT_EXPERT | OPT_SPEC | - OPT_OUTPUT, { .off = OFFSET(force_fps) }, + { "force_fps", OPT_TYPE_BOOL, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(force_fps) }, "force the selected framerate, disable the best supported framerate selection" }, - { "streamid", OPT_VIDEO | HAS_ARG | OPT_EXPERT | OPT_PERFILE | - OPT_OUTPUT, { .func_arg = opt_streamid }, + { "streamid", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT, + { .func_arg = opt_streamid }, "set the value of an outfile streamid", "streamIndex:value" }, - { "force_key_frames", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT | - OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(forced_key_frames) }, + { "force_key_frames", OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(forced_key_frames) }, "force key frames at specified timestamps", "timestamps" }, - { "b", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_bitrate }, + { "b", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT, + { .func_arg = opt_bitrate }, "video bitrate (please use -b:v)", "bitrate" }, - { "hwaccel", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT | - OPT_SPEC | OPT_INPUT, { .off = OFFSET(hwaccels) }, + { "hwaccel", OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_INPUT, + { .off = OFFSET(hwaccels) }, "use HW accelerated decoding", "hwaccel name" }, - { "hwaccel_device", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT | - OPT_SPEC | OPT_INPUT, { .off = OFFSET(hwaccel_devices) }, + { "hwaccel_device", OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_INPUT, + { .off = OFFSET(hwaccel_devices) }, "select a device for HW acceleration", "devicename" }, - { "hwaccel_output_format", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT | - OPT_SPEC | OPT_INPUT, { .off = OFFSET(hwaccel_output_formats) }, + { "hwaccel_output_format", OPT_TYPE_STRING, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_INPUT, + { .off = OFFSET(hwaccel_output_formats) }, "select output format used with HW accelerated decoding", "format" }, - { "hwaccels", OPT_EXIT, { .func_arg = show_hwaccels }, + { "hwaccels", OPT_TYPE_FUNC, OPT_EXIT | OPT_EXPERT, + { .func_arg = show_hwaccels }, "show available HW acceleration methods" }, - { "autorotate", HAS_ARG | OPT_BOOL | OPT_SPEC | - OPT_EXPERT | OPT_INPUT, { .off = OFFSET(autorotate) }, + { "autorotate", OPT_TYPE_BOOL, OPT_PERSTREAM | OPT_EXPERT | OPT_INPUT, + { .off = OFFSET(autorotate) }, "automatically insert correct rotate filters" }, - { "autoscale", HAS_ARG | OPT_BOOL | OPT_SPEC | - OPT_EXPERT | OPT_OUTPUT, { .off = OFFSET(autoscale) }, + { "autoscale", OPT_TYPE_BOOL, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT, + { .off = OFFSET(autoscale) }, "automatically insert a scale filter at the end of the filter graph" }, - { "fix_sub_duration_heartbeat", OPT_VIDEO | OPT_BOOL | OPT_EXPERT | - OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(fix_sub_duration_heartbeat) }, + { "fix_sub_duration_heartbeat", OPT_TYPE_BOOL, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(fix_sub_duration_heartbeat) }, "set this video output stream to be a heartbeat stream for " "fix_sub_duration, according to which subtitles should be split at " "random access points" }, /* audio options */ - { "aframes", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_audio_frames }, - "set the number of audio frames to output", "number" }, - { "aq", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_audio_qscale }, + { "aframes", OPT_TYPE_FUNC, OPT_AUDIO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT | OPT_EXPERT | OPT_HAS_CANON, + { .func_arg = opt_audio_frames }, + "set the number of audio frames to output", "number", + .u1.name_canon = "frames", }, + { "aq", OPT_TYPE_FUNC, OPT_AUDIO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT, + { .func_arg = opt_audio_qscale }, "set audio quality (codec-specific)", "quality", }, - { "ar", OPT_AUDIO | HAS_ARG | OPT_INT | OPT_SPEC | - OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(audio_sample_rate) }, + { "ar", OPT_TYPE_INT, OPT_AUDIO | OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(audio_sample_rate) }, "set audio sampling rate (in Hz)", "rate" }, - { "ac", OPT_AUDIO | HAS_ARG | OPT_INT | OPT_SPEC | - OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(audio_channels) }, + { "ac", OPT_TYPE_INT, OPT_AUDIO | OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(audio_channels) }, "set number of audio channels", "channels" }, - { "an", OPT_AUDIO | OPT_BOOL | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT,{ .off = OFFSET(audio_disable) }, + { "an", OPT_TYPE_BOOL, OPT_AUDIO | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(audio_disable) }, "disable audio" }, - { "acodec", OPT_AUDIO | HAS_ARG | OPT_PERFILE | - OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_audio_codec }, - "force audio codec ('copy' to copy stream)", "codec" }, - { "ab", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_bitrate }, - "audio bitrate (please use -b:a)", "bitrate" }, - { "atag", OPT_AUDIO | HAS_ARG | OPT_EXPERT | OPT_PERFILE | - OPT_OUTPUT, { .func_arg = opt_old2new }, - "force audio tag/fourcc", "fourcc/tag" }, - { "sample_fmt", OPT_AUDIO | HAS_ARG | OPT_EXPERT | OPT_SPEC | - OPT_STRING | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(sample_fmts) }, + { "acodec", OPT_TYPE_FUNC, OPT_AUDIO | OPT_FUNC_ARG | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_audio_codec }, + "alias for -c:a (select encoder/decoder for audio streams)", "codec", + .u1.name_canon = "codec", }, + { "ab", OPT_TYPE_FUNC, OPT_AUDIO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT, + { .func_arg = opt_bitrate }, + "alias for -b:a (select bitrate for audio streams)", "bitrate" }, + { "atag", OPT_TYPE_FUNC, OPT_AUDIO | OPT_FUNC_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_old2new }, + "force audio tag/fourcc", "fourcc/tag", + .u1.name_canon = "tag", }, + { "sample_fmt", OPT_TYPE_STRING, OPT_AUDIO | OPT_EXPERT | OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(sample_fmts) }, "set sample format", "format" }, - { "channel_layout", OPT_AUDIO | HAS_ARG | OPT_EXPERT | OPT_SPEC | - OPT_STRING | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(audio_ch_layouts) }, - "set channel layout", "layout" }, - { "ch_layout", OPT_AUDIO | HAS_ARG | OPT_EXPERT | OPT_SPEC | - OPT_STRING | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(audio_ch_layouts) }, - "set channel layout", "layout" }, - { "af", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_audio_filters }, - "set audio filters", "filter_graph" }, - { "guess_layout_max", OPT_AUDIO | HAS_ARG | OPT_INT | OPT_SPEC | OPT_EXPERT | OPT_INPUT, { .off = OFFSET(guess_layout_max) }, + { "channel_layout", OPT_TYPE_STRING, OPT_AUDIO | OPT_EXPERT | OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT | OPT_HAS_ALT, + { .off = OFFSET(audio_ch_layouts) }, + "set channel layout", "layout", + .u1.names_alt = alt_channel_layout, }, + { "ch_layout", OPT_TYPE_STRING, OPT_AUDIO | OPT_EXPERT | OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON, + { .off = OFFSET(audio_ch_layouts) }, + "set channel layout", "layout", + .u1.name_canon = "channel_layout", }, + { "af", OPT_TYPE_FUNC, OPT_AUDIO | OPT_FUNC_ARG | OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_audio_filters }, + "alias for -filter:a (apply filters to audio streams)", "filter_graph", + .u1.name_canon = "filter", }, + { "guess_layout_max", OPT_TYPE_INT, OPT_AUDIO | OPT_PERSTREAM | OPT_EXPERT | OPT_INPUT, + { .off = OFFSET(guess_layout_max) }, "set the maximum number of channels to try to guess the channel layout" }, /* subtitle options */ - { "sn", OPT_SUBTITLE | OPT_BOOL | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(subtitle_disable) }, + { "sn", OPT_TYPE_BOOL, OPT_SUBTITLE | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(subtitle_disable) }, "disable subtitle" }, - { "scodec", OPT_SUBTITLE | HAS_ARG | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_subtitle_codec }, - "force subtitle codec ('copy' to copy stream)", "codec" }, - { "stag", OPT_SUBTITLE | HAS_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_old2new } - , "force subtitle tag/fourcc", "fourcc/tag" }, - { "fix_sub_duration", OPT_BOOL | OPT_EXPERT | OPT_SUBTITLE | OPT_SPEC | OPT_INPUT, { .off = OFFSET(fix_sub_duration) }, + { "scodec", OPT_TYPE_FUNC, OPT_SUBTITLE | OPT_FUNC_ARG | OPT_PERFILE | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_subtitle_codec }, + "alias for -c:s (select encoder/decoder for subtitle streams)", "codec", + .u1.name_canon = "codec", }, + { "stag", OPT_TYPE_FUNC, OPT_SUBTITLE | OPT_FUNC_ARG | OPT_EXPERT | OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_old2new } + , "force subtitle tag/fourcc", "fourcc/tag", + .u1.name_canon = "tag" }, + { "fix_sub_duration", OPT_TYPE_BOOL, OPT_EXPERT | OPT_SUBTITLE | OPT_PERSTREAM | OPT_INPUT, + { .off = OFFSET(fix_sub_duration) }, "fix subtitles duration" }, - { "canvas_size", OPT_SUBTITLE | HAS_ARG | OPT_STRING | OPT_SPEC | OPT_INPUT, { .off = OFFSET(canvas_sizes) }, + { "canvas_size", OPT_TYPE_STRING, OPT_SUBTITLE | OPT_PERSTREAM | OPT_INPUT | OPT_EXPERT, + { .off = OFFSET(canvas_sizes) }, "set canvas size (WxH or abbreviation)", "size" }, /* muxer options */ - { "muxdelay", OPT_FLOAT | HAS_ARG | OPT_EXPERT | OPT_OFFSET | OPT_OUTPUT, { .off = OFFSET(mux_max_delay) }, + { "muxdelay", OPT_TYPE_FLOAT, OPT_EXPERT | OPT_OFFSET | OPT_OUTPUT, + { .off = OFFSET(mux_max_delay) }, "set the maximum demux-decode delay", "seconds" }, - { "muxpreload", OPT_FLOAT | HAS_ARG | OPT_EXPERT | OPT_OFFSET | OPT_OUTPUT, { .off = OFFSET(mux_preload) }, + { "muxpreload", OPT_TYPE_FLOAT, OPT_EXPERT | OPT_OFFSET | OPT_OUTPUT, + { .off = OFFSET(mux_preload) }, "set the initial demux-decode delay", "seconds" }, - { "sdp_file", HAS_ARG | OPT_EXPERT | OPT_OUTPUT, { .func_arg = opt_sdp_file }, + { "sdp_file", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT | OPT_OUTPUT, + { .func_arg = opt_sdp_file }, "specify a file in which to print sdp information", "file" }, - { "time_base", HAS_ARG | OPT_STRING | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(time_bases) }, + { "time_base", OPT_TYPE_STRING, OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(time_bases) }, "set the desired time base hint for output stream (1:24, 1:48000 or 0.04166, 2.0833e-5)", "ratio" }, - { "enc_time_base", HAS_ARG | OPT_STRING | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(enc_time_bases) }, + { "enc_time_base", OPT_TYPE_STRING, OPT_EXPERT | OPT_PERSTREAM | OPT_OUTPUT, + { .off = OFFSET(enc_time_bases) }, "set the desired time base for the encoder (1:24, 1:48000 or 0.04166, 2.0833e-5). " "two special values are defined - " "0 = use frame rate (video) or sample rate (audio)," "-1 = match source time base", "ratio" }, - { "bsf", HAS_ARG | OPT_STRING | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT, { .off = OFFSET(bitstream_filters) }, - "A comma-separated list of bitstream filters", "bitstream_filters" }, - { "absf", HAS_ARG | OPT_AUDIO | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_old2new }, - "deprecated", "audio bitstream_filters" }, - { "vbsf", OPT_VIDEO | HAS_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_old2new }, - "deprecated", "video bitstream_filters" }, - - { "apre", HAS_ARG | OPT_AUDIO | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_preset }, - "set the audio options to the indicated preset", "preset" }, - { "vpre", OPT_VIDEO | HAS_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_preset }, - "set the video options to the indicated preset", "preset" }, - { "spre", HAS_ARG | OPT_SUBTITLE | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_preset }, - "set the subtitle options to the indicated preset", "preset" }, - { "fpre", HAS_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_preset }, - "set options from indicated preset file", "filename" }, - - { "max_muxing_queue_size", HAS_ARG | OPT_INT | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT, { .off = OFFSET(max_muxing_queue_size) }, + { "bsf", OPT_TYPE_STRING, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT | OPT_INPUT, + { .off = OFFSET(bitstream_filters) }, + "A comma-separated list of bitstream filters", "bitstream_filters", }, + + { "apre", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_AUDIO | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_preset }, + "set the audio options to the indicated preset", "preset", + .u1.name_canon = "pre", }, + { "vpre", OPT_TYPE_FUNC, OPT_VIDEO | OPT_FUNC_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_preset }, + "set the video options to the indicated preset", "preset", + .u1.name_canon = "pre", }, + { "spre", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_SUBTITLE | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_preset }, + "set the subtitle options to the indicated preset", "preset", + .u1.name_canon = "pre", }, + { "fpre", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT| OPT_PERFILE | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_preset }, + "set options from indicated preset file", "filename", + .u1.name_canon = "pre", }, + + { "max_muxing_queue_size", OPT_TYPE_INT, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT, + { .off = OFFSET(max_muxing_queue_size) }, "maximum number of packets that can be buffered while waiting for all streams to initialize", "packets" }, - { "muxing_queue_data_threshold", HAS_ARG | OPT_INT | OPT_SPEC | OPT_EXPERT | OPT_OUTPUT, { .off = OFFSET(muxing_queue_data_threshold) }, + { "muxing_queue_data_threshold", OPT_TYPE_INT, OPT_PERSTREAM | OPT_EXPERT | OPT_OUTPUT, + { .off = OFFSET(muxing_queue_data_threshold) }, "set the threshold after which max_muxing_queue_size is taken into account", "bytes" }, /* data codec support */ - { "dcodec", HAS_ARG | OPT_DATA | OPT_PERFILE | OPT_EXPERT | OPT_INPUT | OPT_OUTPUT, { .func_arg = opt_data_codec }, - "force data codec ('copy' to copy stream)", "codec" }, - { "dn", OPT_BOOL | OPT_VIDEO | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(data_disable) }, - "disable data" }, + { "dcodec", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_DATA | OPT_PERFILE | OPT_EXPERT | OPT_INPUT | OPT_OUTPUT | OPT_HAS_CANON, + { .func_arg = opt_data_codec }, + "alias for -c:d (select encoder/decoder for data streams)", "codec", + .u1.name_canon = "codec", }, + { "dn", OPT_TYPE_BOOL, OPT_DATA | OPT_OFFSET | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(data_disable) }, "disable data" }, #if CONFIG_VAAPI - { "vaapi_device", HAS_ARG | OPT_EXPERT, { .func_arg = opt_vaapi_device }, - "set VAAPI hardware device (DRM path or X11 display name)", "device" }, + { "vaapi_device", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_vaapi_device }, + "set VAAPI hardware device (DirectX adapter index, DRM path or X11 display name)", "device" }, #endif #if CONFIG_QSV - { "qsv_device", HAS_ARG | OPT_EXPERT, { .func_arg = opt_qsv_device }, + { "qsv_device", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_qsv_device }, "set QSV hardware device (DirectX adapter index, DRM path or X11 display name)", "device"}, #endif - { "init_hw_device", HAS_ARG | OPT_EXPERT, { .func_arg = opt_init_hw_device }, + { "init_hw_device", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_init_hw_device }, "initialise hardware device", "args" }, - { "filter_hw_device", HAS_ARG | OPT_EXPERT, { .func_arg = opt_filter_hw_device }, + { "filter_hw_device", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_filter_hw_device }, "set hardware device used when filtering", "device" }, + // deprecated options +#if FFMPEG_OPT_ADRIFT_THRESHOLD + { "adrift_threshold", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_adrift_threshold }, + "deprecated, does nothing", "threshold" }, +#endif +#if FFMPEG_OPT_TOP + { "top", OPT_TYPE_INT, OPT_VIDEO | OPT_EXPERT | OPT_PERSTREAM | OPT_INPUT | OPT_OUTPUT, + { .off = OFFSET(top_field_first) }, + "deprecated, use the setfield video filter", "" }, +#endif +#if FFMPEG_OPT_QPHIST + { "qphist", OPT_TYPE_FUNC, OPT_VIDEO | OPT_EXPERT, + { .func_arg = opt_qphist }, + "deprecated, does nothing" }, +#endif +#if FFMPEG_OPT_VSYNC + { "vsync", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, + { .func_arg = opt_vsync }, + "set video sync method globally; deprecated, use -fps_mode", "" }, +#endif + { NULL, }, }; diff --git a/fftools/ffmpeg_sched.c b/fftools/ffmpeg_sched.c new file mode 100644 index 00000000000..ee3af459089 --- /dev/null +++ b/fftools/ffmpeg_sched.c @@ -0,0 +1,2540 @@ +/* + * Inter-thread scheduling/synchronization. + * Copyright (c) 2023 Anton Khirnov + * + * 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 +#include +#include + +#include "cmdutils.h" +#include "ffmpeg_sched.h" +#include "ffmpeg_utils.h" +#include "sync_queue.h" +#include "thread_queue.h" + +#include "libavcodec/packet.h" + +#include "libavutil/avassert.h" +#include "libavutil/error.h" +#include "libavutil/fifo.h" +#include "libavutil/frame.h" +#include "libavutil/mem.h" +#include "libavutil/thread.h" +#include "libavutil/threadmessage.h" +#include "libavutil/time.h" + +// 100 ms +// FIXME: some other value? make this dynamic? +#define SCHEDULE_TOLERANCE (100 * 1000) + +enum QueueType { + QUEUE_PACKETS, + QUEUE_FRAMES, +}; + +typedef struct SchWaiter { + pthread_mutex_t lock; + pthread_cond_t cond; + atomic_int choked; + + // the following are internal state of schedule_update_locked() and must not + // be accessed outside of it + int choked_prev; + int choked_next; +} SchWaiter; + +typedef struct SchTask { + Scheduler *parent; + SchedulerNode node; + + SchThreadFunc func; + void *func_arg; + + pthread_t thread; + int thread_running; +} SchTask; + +typedef struct SchDec { + const AVClass *class; + + SchedulerNode src; + SchedulerNode *dst; + uint8_t *dst_finished; + unsigned nb_dst; + + SchTask task; + // Queue for receiving input packets, one stream. + ThreadQueue *queue; + + // Queue for sending post-flush end timestamps back to the source + AVThreadMessageQueue *queue_end_ts; + int expect_end_ts; + + // temporary storage used by sch_dec_send() + AVFrame *send_frame; +} SchDec; + +typedef struct SchSyncQueue { + SyncQueue *sq; + AVFrame *frame; + pthread_mutex_t lock; + + unsigned *enc_idx; + unsigned nb_enc_idx; +} SchSyncQueue; + +typedef struct SchEnc { + const AVClass *class; + + SchedulerNode src; + SchedulerNode *dst; + uint8_t *dst_finished; + unsigned nb_dst; + + // [0] - index of the sync queue in Scheduler.sq_enc, + // [1] - index of this encoder in the sq + int sq_idx[2]; + + /* Opening encoders is somewhat nontrivial due to their interaction with + * sync queues, which are (among other things) responsible for maintaining + * constant audio frame size, when it is required by the encoder. + * + * Opening the encoder requires stream parameters, obtained from the first + * frame. However, that frame cannot be properly chunked by the sync queue + * without knowing the required frame size, which is only available after + * opening the encoder. + * + * This apparent circular dependency is resolved in the following way: + * - the caller creating the encoder gives us a callback which opens the + * encoder and returns the required frame size (if any) + * - when the first frame is sent to the encoder, the sending thread + * - calls this callback, opening the encoder + * - passes the returned frame size to the sync queue + */ + int (*open_cb)(void *opaque, const AVFrame *frame); + int opened; + + SchTask task; + // Queue for receiving input frames, one stream. + ThreadQueue *queue; + // tq_send() to queue returned EOF + int in_finished; + + // temporary storage used by sch_enc_send() + AVPacket *send_pkt; +} SchEnc; + +typedef struct SchDemuxStream { + SchedulerNode *dst; + uint8_t *dst_finished; + unsigned nb_dst; +} SchDemuxStream; + +typedef struct SchDemux { + const AVClass *class; + + SchDemuxStream *streams; + unsigned nb_streams; + + SchTask task; + SchWaiter waiter; + + // temporary storage used by sch_demux_send() + AVPacket *send_pkt; + + // protected by schedule_lock + int task_exited; +} SchDemux; + +typedef struct PreMuxQueue { + /** + * Queue for buffering the packets before the muxer task can be started. + */ + AVFifo *fifo; + /** + * Maximum number of packets in fifo. + */ + int max_packets; + /* + * The size of the AVPackets' buffers in queue. + * Updated when a packet is either pushed or pulled from the queue. + */ + size_t data_size; + /* Threshold after which max_packets will be in effect */ + size_t data_threshold; +} PreMuxQueue; + +typedef struct SchMuxStream { + SchedulerNode src; + SchedulerNode src_sched; + + unsigned *sub_heartbeat_dst; + unsigned nb_sub_heartbeat_dst; + + PreMuxQueue pre_mux_queue; + + // an EOF was generated while flushing the pre-mux queue + int init_eof; + + //////////////////////////////////////////////////////////// + // The following are protected by Scheduler.schedule_lock // + + /* dts+duration of the last packet sent to this stream + in AV_TIME_BASE_Q */ + int64_t last_dts; + // this stream no longer accepts input + int source_finished; + //////////////////////////////////////////////////////////// +} SchMuxStream; + +typedef struct SchMux { + const AVClass *class; + + SchMuxStream *streams; + unsigned nb_streams; + unsigned nb_streams_ready; + + int (*init)(void *arg); + + SchTask task; + /** + * Set to 1 after starting the muxer task and flushing the + * pre-muxing queues. + * Set either before any tasks have started, or with + * Scheduler.mux_ready_lock held. + */ + atomic_int mux_started; + ThreadQueue *queue; + unsigned queue_size; + + AVPacket *sub_heartbeat_pkt; +} SchMux; + +typedef struct SchFilterIn { + SchedulerNode src; + SchedulerNode src_sched; + int send_finished; + int receive_finished; +} SchFilterIn; + +typedef struct SchFilterOut { + SchedulerNode dst; +} SchFilterOut; + +typedef struct SchFilterGraph { + const AVClass *class; + + SchFilterIn *inputs; + unsigned nb_inputs; + atomic_uint nb_inputs_finished_send; + unsigned nb_inputs_finished_receive; + + SchFilterOut *outputs; + unsigned nb_outputs; + + SchTask task; + // input queue, nb_inputs+1 streams + // last stream is control + ThreadQueue *queue; + SchWaiter waiter; + + // protected by schedule_lock + unsigned best_input; + int task_exited; +} SchFilterGraph; + +enum SchedulerState { + SCH_STATE_UNINIT, + SCH_STATE_STARTED, + SCH_STATE_STOPPED, +}; + +struct Scheduler { + const AVClass *class; + + SchDemux *demux; + unsigned nb_demux; + + SchMux *mux; + unsigned nb_mux; + + unsigned nb_mux_ready; + pthread_mutex_t mux_ready_lock; + + unsigned nb_mux_done; + pthread_mutex_t mux_done_lock; + pthread_cond_t mux_done_cond; + + + SchDec *dec; + unsigned nb_dec; + + SchEnc *enc; + unsigned nb_enc; + + SchSyncQueue *sq_enc; + unsigned nb_sq_enc; + + SchFilterGraph *filters; + unsigned nb_filters; + + char *sdp_filename; + int sdp_auto; + + enum SchedulerState state; + atomic_int terminate; + atomic_int task_failed; + + pthread_mutex_t schedule_lock; + + atomic_int_least64_t last_dts; +}; + +/** + * Wait until this task is allowed to proceed. + * + * @retval 0 the caller should proceed + * @retval 1 the caller should terminate + */ +static int waiter_wait(Scheduler *sch, SchWaiter *w) +{ + int terminate; + + if (!atomic_load(&w->choked)) + return 0; + + pthread_mutex_lock(&w->lock); + + while (atomic_load(&w->choked) && !atomic_load(&sch->terminate)) + pthread_cond_wait(&w->cond, &w->lock); + + terminate = atomic_load(&sch->terminate); + + pthread_mutex_unlock(&w->lock); + + return terminate; +} + +static void waiter_set(SchWaiter *w, int choked) +{ + pthread_mutex_lock(&w->lock); + + atomic_store(&w->choked, choked); + pthread_cond_signal(&w->cond); + + pthread_mutex_unlock(&w->lock); +} + +static int waiter_init(SchWaiter *w) +{ + int ret; + + atomic_init(&w->choked, 0); + + ret = pthread_mutex_init(&w->lock, NULL); + if (ret) + return AVERROR(ret); + + ret = pthread_cond_init(&w->cond, NULL); + if (ret) + return AVERROR(ret); + + return 0; +} + +static void waiter_uninit(SchWaiter *w) +{ + pthread_mutex_destroy(&w->lock); + pthread_cond_destroy(&w->cond); +} + +static int queue_alloc(ThreadQueue **ptq, unsigned nb_streams, unsigned queue_size, + enum QueueType type) +{ + ThreadQueue *tq; + ObjPool *op; + + if (queue_size <= 0) { + if (type == QUEUE_FRAMES) + queue_size = DEFAULT_FRAME_THREAD_QUEUE_SIZE; + else + queue_size = DEFAULT_PACKET_THREAD_QUEUE_SIZE; + } + + if (type == QUEUE_FRAMES) { + // This queue length is used in the decoder code to ensure that + // there are enough entries in fixed-size frame pools to account + // for frames held in queues inside the ffmpeg utility. If this + // can ever dynamically change then the corresponding decode + // code needs to be updated as well. + av_assert0(queue_size == DEFAULT_FRAME_THREAD_QUEUE_SIZE); + } + + op = (type == QUEUE_PACKETS) ? objpool_alloc_packets() : + objpool_alloc_frames(); + if (!op) + return AVERROR(ENOMEM); + + tq = tq_alloc(nb_streams, queue_size, op, + (type == QUEUE_PACKETS) ? pkt_move : frame_move); + if (!tq) { + objpool_free(&op); + return AVERROR(ENOMEM); + } + + *ptq = tq; + return 0; +} + +static void *task_wrapper(void *arg); + +static int task_start(SchTask *task) +{ + int ret; + + av_log(task->func_arg, AV_LOG_VERBOSE, "Starting thread...\n"); + + av_assert0(!task->thread_running); + + ret = pthread_create(&task->thread, NULL, task_wrapper, task); + if (ret) { + av_log(task->func_arg, AV_LOG_ERROR, "pthread_create() failed: %s\n", + strerror(ret)); + return AVERROR(ret); + } + + task->thread_running = 1; + return 0; +} + +static void task_init(Scheduler *sch, SchTask *task, enum SchedulerNodeType type, unsigned idx, + SchThreadFunc func, void *func_arg) +{ + task->parent = sch; + + task->node.type = type; + task->node.idx = idx; + + task->func = func; + task->func_arg = func_arg; +} + +static int64_t trailing_dts(const Scheduler *sch, int count_finished) +{ + int64_t min_dts = INT64_MAX; + + for (unsigned i = 0; i < sch->nb_mux; i++) { + const SchMux *mux = &sch->mux[i]; + + for (unsigned j = 0; j < mux->nb_streams; j++) { + const SchMuxStream *ms = &mux->streams[j]; + + if (ms->source_finished && !count_finished) + continue; + if (ms->last_dts == AV_NOPTS_VALUE) + return AV_NOPTS_VALUE; + + min_dts = FFMIN(min_dts, ms->last_dts); + } + } + + return min_dts == INT64_MAX ? AV_NOPTS_VALUE : min_dts; +} + +void sch_free(Scheduler **psch) +{ + Scheduler *sch = *psch; + + if (!sch) + return; + + sch_stop(sch, NULL); + + for (unsigned i = 0; i < sch->nb_demux; i++) { + SchDemux *d = &sch->demux[i]; + + for (unsigned j = 0; j < d->nb_streams; j++) { + SchDemuxStream *ds = &d->streams[j]; + av_freep(&ds->dst); + av_freep(&ds->dst_finished); + } + av_freep(&d->streams); + + av_packet_free(&d->send_pkt); + + waiter_uninit(&d->waiter); + } + av_freep(&sch->demux); + + for (unsigned i = 0; i < sch->nb_mux; i++) { + SchMux *mux = &sch->mux[i]; + + for (unsigned j = 0; j < mux->nb_streams; j++) { + SchMuxStream *ms = &mux->streams[j]; + + if (ms->pre_mux_queue.fifo) { + AVPacket *pkt; + while (av_fifo_read(ms->pre_mux_queue.fifo, &pkt, 1) >= 0) + av_packet_free(&pkt); + av_fifo_freep2(&ms->pre_mux_queue.fifo); + } + + av_freep(&ms->sub_heartbeat_dst); + } + av_freep(&mux->streams); + + av_packet_free(&mux->sub_heartbeat_pkt); + + tq_free(&mux->queue); + } + av_freep(&sch->mux); + + for (unsigned i = 0; i < sch->nb_dec; i++) { + SchDec *dec = &sch->dec[i]; + + tq_free(&dec->queue); + + av_thread_message_queue_free(&dec->queue_end_ts); + + av_freep(&dec->dst); + av_freep(&dec->dst_finished); + + av_frame_free(&dec->send_frame); + } + av_freep(&sch->dec); + + for (unsigned i = 0; i < sch->nb_enc; i++) { + SchEnc *enc = &sch->enc[i]; + + tq_free(&enc->queue); + + av_packet_free(&enc->send_pkt); + + av_freep(&enc->dst); + av_freep(&enc->dst_finished); + } + av_freep(&sch->enc); + + for (unsigned i = 0; i < sch->nb_sq_enc; i++) { + SchSyncQueue *sq = &sch->sq_enc[i]; + sq_free(&sq->sq); + av_frame_free(&sq->frame); + pthread_mutex_destroy(&sq->lock); + av_freep(&sq->enc_idx); + } + av_freep(&sch->sq_enc); + + for (unsigned i = 0; i < sch->nb_filters; i++) { + SchFilterGraph *fg = &sch->filters[i]; + + tq_free(&fg->queue); + + av_freep(&fg->inputs); + av_freep(&fg->outputs); + + waiter_uninit(&fg->waiter); + } + av_freep(&sch->filters); + + av_freep(&sch->sdp_filename); + + pthread_mutex_destroy(&sch->schedule_lock); + + pthread_mutex_destroy(&sch->mux_ready_lock); + + pthread_mutex_destroy(&sch->mux_done_lock); + pthread_cond_destroy(&sch->mux_done_cond); + + av_freep(psch); +} + +static const AVClass scheduler_class = { + .class_name = "Scheduler", + .version = LIBAVUTIL_VERSION_INT, +}; + +Scheduler *sch_alloc(void) +{ + Scheduler *sch; + int ret; + + sch = av_mallocz(sizeof(*sch)); + if (!sch) + return NULL; + + sch->class = &scheduler_class; + sch->sdp_auto = 1; + + ret = pthread_mutex_init(&sch->schedule_lock, NULL); + if (ret) + goto fail; + + ret = pthread_mutex_init(&sch->mux_ready_lock, NULL); + if (ret) + goto fail; + + ret = pthread_mutex_init(&sch->mux_done_lock, NULL); + if (ret) + goto fail; + + ret = pthread_cond_init(&sch->mux_done_cond, NULL); + if (ret) + goto fail; + + return sch; +fail: + sch_free(&sch); + return NULL; +} + +int sch_sdp_filename(Scheduler *sch, const char *sdp_filename) +{ + av_freep(&sch->sdp_filename); + sch->sdp_filename = av_strdup(sdp_filename); + return sch->sdp_filename ? 0 : AVERROR(ENOMEM); +} + +static const AVClass sch_mux_class = { + .class_name = "SchMux", + .version = LIBAVUTIL_VERSION_INT, + .parent_log_context_offset = offsetof(SchMux, task.func_arg), +}; + +int sch_add_mux(Scheduler *sch, SchThreadFunc func, int (*init)(void *), + void *arg, int sdp_auto, unsigned thread_queue_size) +{ + const unsigned idx = sch->nb_mux; + + SchMux *mux; + int ret; + + ret = GROW_ARRAY(sch->mux, sch->nb_mux); + if (ret < 0) + return ret; + + mux = &sch->mux[idx]; + mux->class = &sch_mux_class; + mux->init = init; + mux->queue_size = thread_queue_size; + + task_init(sch, &mux->task, SCH_NODE_TYPE_MUX, idx, func, arg); + + sch->sdp_auto &= sdp_auto; + + return idx; +} + +int sch_add_mux_stream(Scheduler *sch, unsigned mux_idx) +{ + SchMux *mux; + SchMuxStream *ms; + unsigned stream_idx; + int ret; + + av_assert0(mux_idx < sch->nb_mux); + mux = &sch->mux[mux_idx]; + + ret = GROW_ARRAY(mux->streams, mux->nb_streams); + if (ret < 0) + return ret; + stream_idx = mux->nb_streams - 1; + + ms = &mux->streams[stream_idx]; + + ms->pre_mux_queue.fifo = av_fifo_alloc2(8, sizeof(AVPacket*), 0); + if (!ms->pre_mux_queue.fifo) + return AVERROR(ENOMEM); + + ms->last_dts = AV_NOPTS_VALUE; + + return stream_idx; +} + +static const AVClass sch_demux_class = { + .class_name = "SchDemux", + .version = LIBAVUTIL_VERSION_INT, + .parent_log_context_offset = offsetof(SchDemux, task.func_arg), +}; + +int sch_add_demux(Scheduler *sch, SchThreadFunc func, void *ctx) +{ + const unsigned idx = sch->nb_demux; + + SchDemux *d; + int ret; + + ret = GROW_ARRAY(sch->demux, sch->nb_demux); + if (ret < 0) + return ret; + + d = &sch->demux[idx]; + + task_init(sch, &d->task, SCH_NODE_TYPE_DEMUX, idx, func, ctx); + + d->class = &sch_demux_class; + d->send_pkt = av_packet_alloc(); + if (!d->send_pkt) + return AVERROR(ENOMEM); + + ret = waiter_init(&d->waiter); + if (ret < 0) + return ret; + + return idx; +} + +int sch_add_demux_stream(Scheduler *sch, unsigned demux_idx) +{ + SchDemux *d; + int ret; + + av_assert0(demux_idx < sch->nb_demux); + d = &sch->demux[demux_idx]; + + ret = GROW_ARRAY(d->streams, d->nb_streams); + return ret < 0 ? ret : d->nb_streams - 1; +} + +static const AVClass sch_dec_class = { + .class_name = "SchDec", + .version = LIBAVUTIL_VERSION_INT, + .parent_log_context_offset = offsetof(SchDec, task.func_arg), +}; + +int sch_add_dec(Scheduler *sch, SchThreadFunc func, void *ctx, + int send_end_ts) +{ + const unsigned idx = sch->nb_dec; + + SchDec *dec; + int ret; + + ret = GROW_ARRAY(sch->dec, sch->nb_dec); + if (ret < 0) + return ret; + + dec = &sch->dec[idx]; + + task_init(sch, &dec->task, SCH_NODE_TYPE_DEC, idx, func, ctx); + + dec->class = &sch_dec_class; + dec->send_frame = av_frame_alloc(); + if (!dec->send_frame) + return AVERROR(ENOMEM); + + ret = queue_alloc(&dec->queue, 1, 0, QUEUE_PACKETS); + if (ret < 0) + return ret; + + if (send_end_ts) { + ret = av_thread_message_queue_alloc(&dec->queue_end_ts, 1, sizeof(Timestamp)); + if (ret < 0) + return ret; + } + + return idx; +} + +static const AVClass sch_enc_class = { + .class_name = "SchEnc", + .version = LIBAVUTIL_VERSION_INT, + .parent_log_context_offset = offsetof(SchEnc, task.func_arg), +}; + +int sch_add_enc(Scheduler *sch, SchThreadFunc func, void *ctx, + int (*open_cb)(void *opaque, const AVFrame *frame)) +{ + const unsigned idx = sch->nb_enc; + + SchEnc *enc; + int ret; + + ret = GROW_ARRAY(sch->enc, sch->nb_enc); + if (ret < 0) + return ret; + + enc = &sch->enc[idx]; + + enc->class = &sch_enc_class; + enc->open_cb = open_cb; + enc->sq_idx[0] = -1; + enc->sq_idx[1] = -1; + + task_init(sch, &enc->task, SCH_NODE_TYPE_ENC, idx, func, ctx); + + enc->send_pkt = av_packet_alloc(); + if (!enc->send_pkt) + return AVERROR(ENOMEM); + + ret = queue_alloc(&enc->queue, 1, 0, QUEUE_FRAMES); + if (ret < 0) + return ret; + + return idx; +} + +static const AVClass sch_fg_class = { + .class_name = "SchFilterGraph", + .version = LIBAVUTIL_VERSION_INT, + .parent_log_context_offset = offsetof(SchFilterGraph, task.func_arg), +}; + +int sch_add_filtergraph(Scheduler *sch, unsigned nb_inputs, unsigned nb_outputs, + SchThreadFunc func, void *ctx) +{ + const unsigned idx = sch->nb_filters; + + SchFilterGraph *fg; + int ret; + + ret = GROW_ARRAY(sch->filters, sch->nb_filters); + if (ret < 0) + return ret; + fg = &sch->filters[idx]; + + fg->class = &sch_fg_class; + + task_init(sch, &fg->task, SCH_NODE_TYPE_FILTER_IN, idx, func, ctx); + + if (nb_inputs) { + fg->inputs = av_calloc(nb_inputs, sizeof(*fg->inputs)); + if (!fg->inputs) + return AVERROR(ENOMEM); + fg->nb_inputs = nb_inputs; + } + + if (nb_outputs) { + fg->outputs = av_calloc(nb_outputs, sizeof(*fg->outputs)); + if (!fg->outputs) + return AVERROR(ENOMEM); + fg->nb_outputs = nb_outputs; + } + + ret = waiter_init(&fg->waiter); + if (ret < 0) + return ret; + + ret = queue_alloc(&fg->queue, fg->nb_inputs + 1, 0, QUEUE_FRAMES); + if (ret < 0) + return ret; + + return idx; +} + +int sch_add_sq_enc(Scheduler *sch, uint64_t buf_size_us, void *logctx) +{ + SchSyncQueue *sq; + int ret; + + ret = GROW_ARRAY(sch->sq_enc, sch->nb_sq_enc); + if (ret < 0) + return ret; + sq = &sch->sq_enc[sch->nb_sq_enc - 1]; + + sq->sq = sq_alloc(SYNC_QUEUE_FRAMES, buf_size_us, logctx); + if (!sq->sq) + return AVERROR(ENOMEM); + + sq->frame = av_frame_alloc(); + if (!sq->frame) + return AVERROR(ENOMEM); + + ret = pthread_mutex_init(&sq->lock, NULL); + if (ret) + return AVERROR(ret); + + return sq - sch->sq_enc; +} + +int sch_sq_add_enc(Scheduler *sch, unsigned sq_idx, unsigned enc_idx, + int limiting, uint64_t max_frames) +{ + SchSyncQueue *sq; + SchEnc *enc; + int ret; + + av_assert0(sq_idx < sch->nb_sq_enc); + sq = &sch->sq_enc[sq_idx]; + + av_assert0(enc_idx < sch->nb_enc); + enc = &sch->enc[enc_idx]; + + ret = GROW_ARRAY(sq->enc_idx, sq->nb_enc_idx); + if (ret < 0) + return ret; + sq->enc_idx[sq->nb_enc_idx - 1] = enc_idx; + + ret = sq_add_stream(sq->sq, limiting); + if (ret < 0) + return ret; + + enc->sq_idx[0] = sq_idx; + enc->sq_idx[1] = ret; + + if (max_frames != INT64_MAX) + sq_limit_frames(sq->sq, enc->sq_idx[1], max_frames); + + return 0; +} + +int sch_connect(Scheduler *sch, SchedulerNode src, SchedulerNode dst) +{ + int ret; + + switch (src.type) { + case SCH_NODE_TYPE_DEMUX: { + SchDemuxStream *ds; + + av_assert0(src.idx < sch->nb_demux && + src.idx_stream < sch->demux[src.idx].nb_streams); + ds = &sch->demux[src.idx].streams[src.idx_stream]; + + ret = GROW_ARRAY(ds->dst, ds->nb_dst); + if (ret < 0) + return ret; + + ds->dst[ds->nb_dst - 1] = dst; + + // demuxed packets go to decoding or streamcopy + switch (dst.type) { + case SCH_NODE_TYPE_DEC: { + SchDec *dec; + + av_assert0(dst.idx < sch->nb_dec); + dec = &sch->dec[dst.idx]; + + av_assert0(!dec->src.type); + dec->src = src; + break; + } + case SCH_NODE_TYPE_MUX: { + SchMuxStream *ms; + + av_assert0(dst.idx < sch->nb_mux && + dst.idx_stream < sch->mux[dst.idx].nb_streams); + ms = &sch->mux[dst.idx].streams[dst.idx_stream]; + + av_assert0(!ms->src.type); + ms->src = src; + + break; + } + default: av_assert0(0); + } + + break; + } + case SCH_NODE_TYPE_DEC: { + SchDec *dec; + + av_assert0(src.idx < sch->nb_dec); + dec = &sch->dec[src.idx]; + + ret = GROW_ARRAY(dec->dst, dec->nb_dst); + if (ret < 0) + return ret; + + dec->dst[dec->nb_dst - 1] = dst; + + // decoded frames go to filters or encoding + switch (dst.type) { + case SCH_NODE_TYPE_FILTER_IN: { + SchFilterIn *fi; + + av_assert0(dst.idx < sch->nb_filters && + dst.idx_stream < sch->filters[dst.idx].nb_inputs); + fi = &sch->filters[dst.idx].inputs[dst.idx_stream]; + + av_assert0(!fi->src.type); + fi->src = src; + break; + } + case SCH_NODE_TYPE_ENC: { + SchEnc *enc; + + av_assert0(dst.idx < sch->nb_enc); + enc = &sch->enc[dst.idx]; + + av_assert0(!enc->src.type); + enc->src = src; + break; + } + default: av_assert0(0); + } + + break; + } + case SCH_NODE_TYPE_FILTER_OUT: { + SchFilterOut *fo; + SchEnc *enc; + + av_assert0(src.idx < sch->nb_filters && + src.idx_stream < sch->filters[src.idx].nb_outputs); + // filtered frames go to encoding + av_assert0(dst.type == SCH_NODE_TYPE_ENC && + dst.idx < sch->nb_enc); + + fo = &sch->filters[src.idx].outputs[src.idx_stream]; + enc = &sch->enc[dst.idx]; + + av_assert0(!fo->dst.type && !enc->src.type); + fo->dst = dst; + enc->src = src; + + break; + } + case SCH_NODE_TYPE_ENC: { + SchEnc *enc; + + av_assert0(src.idx < sch->nb_enc); + enc = &sch->enc[src.idx]; + + ret = GROW_ARRAY(enc->dst, enc->nb_dst); + if (ret < 0) + return ret; + + enc->dst[enc->nb_dst - 1] = dst; + + // encoding packets go to muxing or decoding + switch (dst.type) { + case SCH_NODE_TYPE_MUX: { + SchMuxStream *ms; + + av_assert0(dst.idx < sch->nb_mux && + dst.idx_stream < sch->mux[dst.idx].nb_streams); + ms = &sch->mux[dst.idx].streams[dst.idx_stream]; + + av_assert0(!ms->src.type); + ms->src = src; + + break; + } + case SCH_NODE_TYPE_DEC: { + SchDec *dec; + + av_assert0(dst.idx < sch->nb_dec); + dec = &sch->dec[dst.idx]; + + av_assert0(!dec->src.type); + dec->src = src; + + break; + } + default: av_assert0(0); + } + + break; + } + default: av_assert0(0); + } + + return 0; +} + +static int mux_task_start(SchMux *mux) +{ + int ret = 0; + + ret = task_start(&mux->task); + if (ret < 0) + return ret; + + /* flush the pre-muxing queues */ + for (unsigned i = 0; i < mux->nb_streams; i++) { + SchMuxStream *ms = &mux->streams[i]; + AVPacket *pkt; + + while (av_fifo_read(ms->pre_mux_queue.fifo, &pkt, 1) >= 0) { + if (pkt) { + if (!ms->init_eof) + ret = tq_send(mux->queue, i, pkt); + av_packet_free(&pkt); + if (ret == AVERROR_EOF) + ms->init_eof = 1; + else if (ret < 0) + return ret; + } else + tq_send_finish(mux->queue, i); + } + } + + atomic_store(&mux->mux_started, 1); + + return 0; +} + +int print_sdp(const char *filename); + +static int mux_init(Scheduler *sch, SchMux *mux) +{ + int ret; + + ret = mux->init(mux->task.func_arg); + if (ret < 0) + return ret; + + sch->nb_mux_ready++; + + if (sch->sdp_filename || sch->sdp_auto) { + if (sch->nb_mux_ready < sch->nb_mux) + return 0; + + ret = print_sdp(sch->sdp_filename); + if (ret < 0) { + av_log(sch, AV_LOG_ERROR, "Error writing the SDP.\n"); + return ret; + } + + /* SDP is written only after all the muxers are ready, so now we + * start ALL the threads */ + for (unsigned i = 0; i < sch->nb_mux; i++) { + ret = mux_task_start(&sch->mux[i]); + if (ret < 0) + return ret; + } + } else { + ret = mux_task_start(mux); + if (ret < 0) + return ret; + } + + return 0; +} + +void sch_mux_stream_buffering(Scheduler *sch, unsigned mux_idx, unsigned stream_idx, + size_t data_threshold, int max_packets) +{ + SchMux *mux; + SchMuxStream *ms; + + av_assert0(mux_idx < sch->nb_mux); + mux = &sch->mux[mux_idx]; + + av_assert0(stream_idx < mux->nb_streams); + ms = &mux->streams[stream_idx]; + + ms->pre_mux_queue.max_packets = max_packets; + ms->pre_mux_queue.data_threshold = data_threshold; +} + +int sch_mux_stream_ready(Scheduler *sch, unsigned mux_idx, unsigned stream_idx) +{ + SchMux *mux; + int ret = 0; + + av_assert0(mux_idx < sch->nb_mux); + mux = &sch->mux[mux_idx]; + + av_assert0(stream_idx < mux->nb_streams); + + pthread_mutex_lock(&sch->mux_ready_lock); + + av_assert0(mux->nb_streams_ready < mux->nb_streams); + + // this may be called during initialization - do not start + // threads before sch_start() is called + if (++mux->nb_streams_ready == mux->nb_streams && + sch->state >= SCH_STATE_STARTED) + ret = mux_init(sch, mux); + + pthread_mutex_unlock(&sch->mux_ready_lock); + + return ret; +} + +int sch_mux_sub_heartbeat_add(Scheduler *sch, unsigned mux_idx, unsigned stream_idx, + unsigned dec_idx) +{ + SchMux *mux; + SchMuxStream *ms; + int ret = 0; + + av_assert0(mux_idx < sch->nb_mux); + mux = &sch->mux[mux_idx]; + + av_assert0(stream_idx < mux->nb_streams); + ms = &mux->streams[stream_idx]; + + ret = GROW_ARRAY(ms->sub_heartbeat_dst, ms->nb_sub_heartbeat_dst); + if (ret < 0) + return ret; + + av_assert0(dec_idx < sch->nb_dec); + ms->sub_heartbeat_dst[ms->nb_sub_heartbeat_dst - 1] = dec_idx; + + if (!mux->sub_heartbeat_pkt) { + mux->sub_heartbeat_pkt = av_packet_alloc(); + if (!mux->sub_heartbeat_pkt) + return AVERROR(ENOMEM); + } + + return 0; +} + +static void unchoke_for_stream(Scheduler *sch, SchedulerNode src) +{ + while (1) { + SchFilterGraph *fg; + + // fed directly by a demuxer (i.e. not through a filtergraph) + if (src.type == SCH_NODE_TYPE_DEMUX) { + sch->demux[src.idx].waiter.choked_next = 0; + return; + } + + av_assert0(src.type == SCH_NODE_TYPE_FILTER_OUT); + fg = &sch->filters[src.idx]; + + // the filtergraph contains internal sources and + // requested to be scheduled directly + if (fg->best_input == fg->nb_inputs) { + fg->waiter.choked_next = 0; + return; + } + + src = fg->inputs[fg->best_input].src_sched; + } +} + +static void schedule_update_locked(Scheduler *sch) +{ + int64_t dts; + int have_unchoked = 0; + + // on termination request all waiters are choked, + // we are not to unchoke them + if (atomic_load(&sch->terminate)) + return; + + dts = trailing_dts(sch, 0); + + atomic_store(&sch->last_dts, dts); + + // initialize our internal state + for (unsigned type = 0; type < 2; type++) + for (unsigned i = 0; i < (type ? sch->nb_filters : sch->nb_demux); i++) { + SchWaiter *w = type ? &sch->filters[i].waiter : &sch->demux[i].waiter; + w->choked_prev = atomic_load(&w->choked); + w->choked_next = 1; + } + + // figure out the sources that are allowed to proceed + for (unsigned i = 0; i < sch->nb_mux; i++) { + SchMux *mux = &sch->mux[i]; + + for (unsigned j = 0; j < mux->nb_streams; j++) { + SchMuxStream *ms = &mux->streams[j]; + + // unblock sources for output streams that are not finished + // and not too far ahead of the trailing stream + if (ms->source_finished) + continue; + if (dts == AV_NOPTS_VALUE && ms->last_dts != AV_NOPTS_VALUE) + continue; + if (dts != AV_NOPTS_VALUE && ms->last_dts - dts >= SCHEDULE_TOLERANCE) + continue; + + // resolve the source to unchoke + unchoke_for_stream(sch, ms->src_sched); + have_unchoked = 1; + } + } + + // make sure to unchoke at least one source, if still available + for (unsigned type = 0; !have_unchoked && type < 2; type++) + for (unsigned i = 0; i < (type ? sch->nb_filters : sch->nb_demux); i++) { + int exited = type ? sch->filters[i].task_exited : sch->demux[i].task_exited; + SchWaiter *w = type ? &sch->filters[i].waiter : &sch->demux[i].waiter; + if (!exited) { + w->choked_next = 0; + have_unchoked = 1; + break; + } + } + + + for (unsigned type = 0; type < 2; type++) + for (unsigned i = 0; i < (type ? sch->nb_filters : sch->nb_demux); i++) { + SchWaiter *w = type ? &sch->filters[i].waiter : &sch->demux[i].waiter; + if (w->choked_prev != w->choked_next) + waiter_set(w, w->choked_next); + } + +} + +enum { + CYCLE_NODE_NEW = 0, + CYCLE_NODE_STARTED, + CYCLE_NODE_DONE, +}; + +static int +check_acyclic_for_output(const Scheduler *sch, SchedulerNode src, + uint8_t *filters_visited, SchedulerNode *filters_stack) +{ + unsigned nb_filters_stack = 0; + + memset(filters_visited, 0, sch->nb_filters * sizeof(*filters_visited)); + + while (1) { + const SchFilterGraph *fg = &sch->filters[src.idx]; + + filters_visited[src.idx] = CYCLE_NODE_STARTED; + + // descend into every input, depth first + if (src.idx_stream < fg->nb_inputs) { + const SchFilterIn *fi = &fg->inputs[src.idx_stream++]; + + // connected to demuxer, no cycles possible + if (fi->src_sched.type == SCH_NODE_TYPE_DEMUX) + continue; + + // otherwise connected to another filtergraph + av_assert0(fi->src_sched.type == SCH_NODE_TYPE_FILTER_OUT); + + // found a cycle + if (filters_visited[fi->src_sched.idx] == CYCLE_NODE_STARTED) + return AVERROR(EINVAL); + + // place current position on stack and descend + av_assert0(nb_filters_stack < sch->nb_filters); + filters_stack[nb_filters_stack++] = src; + src = (SchedulerNode){ .idx = fi->src_sched.idx, .idx_stream = 0 }; + continue; + } + + filters_visited[src.idx] = CYCLE_NODE_DONE; + + // previous search finished, + if (nb_filters_stack) { + src = filters_stack[--nb_filters_stack]; + continue; + } + return 0; + } +} + +static int check_acyclic(Scheduler *sch) +{ + uint8_t *filters_visited = NULL; + SchedulerNode *filters_stack = NULL; + + int ret = 0; + + if (!sch->nb_filters) + return 0; + + filters_visited = av_malloc_array(sch->nb_filters, sizeof(*filters_visited)); + if (!filters_visited) + return AVERROR(ENOMEM); + + filters_stack = av_malloc_array(sch->nb_filters, sizeof(*filters_stack)); + if (!filters_stack) { + ret = AVERROR(ENOMEM); + goto fail; + } + + // trace the transcoding graph upstream from every output stream + // fed by a filtergraph + for (unsigned i = 0; i < sch->nb_mux; i++) { + SchMux *mux = &sch->mux[i]; + + for (unsigned j = 0; j < mux->nb_streams; j++) { + SchMuxStream *ms = &mux->streams[j]; + SchedulerNode src = ms->src_sched; + + if (src.type != SCH_NODE_TYPE_FILTER_OUT) + continue; + src.idx_stream = 0; + + ret = check_acyclic_for_output(sch, src, filters_visited, filters_stack); + if (ret < 0) { + av_log(mux, AV_LOG_ERROR, "Transcoding graph has a cycle\n"); + goto fail; + } + } + } + +fail: + av_freep(&filters_visited); + av_freep(&filters_stack); + return ret; +} + +static int start_prepare(Scheduler *sch) +{ + int ret; + + for (unsigned i = 0; i < sch->nb_demux; i++) { + SchDemux *d = &sch->demux[i]; + + for (unsigned j = 0; j < d->nb_streams; j++) { + SchDemuxStream *ds = &d->streams[j]; + + if (!ds->nb_dst) { + av_log(d, AV_LOG_ERROR, + "Demuxer stream %u not connected to any sink\n", j); + return AVERROR(EINVAL); + } + + ds->dst_finished = av_calloc(ds->nb_dst, sizeof(*ds->dst_finished)); + if (!ds->dst_finished) + return AVERROR(ENOMEM); + } + } + + for (unsigned i = 0; i < sch->nb_dec; i++) { + SchDec *dec = &sch->dec[i]; + + if (!dec->src.type) { + av_log(dec, AV_LOG_ERROR, + "Decoder not connected to a source\n"); + return AVERROR(EINVAL); + } + if (!dec->nb_dst) { + av_log(dec, AV_LOG_ERROR, + "Decoder not connected to any sink\n"); + return AVERROR(EINVAL); + } + + dec->dst_finished = av_calloc(dec->nb_dst, sizeof(*dec->dst_finished)); + if (!dec->dst_finished) + return AVERROR(ENOMEM); + } + + for (unsigned i = 0; i < sch->nb_enc; i++) { + SchEnc *enc = &sch->enc[i]; + + if (!enc->src.type) { + av_log(enc, AV_LOG_ERROR, + "Encoder not connected to a source\n"); + return AVERROR(EINVAL); + } + if (!enc->nb_dst) { + av_log(enc, AV_LOG_ERROR, + "Encoder not connected to any sink\n"); + return AVERROR(EINVAL); + } + + enc->dst_finished = av_calloc(enc->nb_dst, sizeof(*enc->dst_finished)); + if (!enc->dst_finished) + return AVERROR(ENOMEM); + } + + for (unsigned i = 0; i < sch->nb_mux; i++) { + SchMux *mux = &sch->mux[i]; + + for (unsigned j = 0; j < mux->nb_streams; j++) { + SchMuxStream *ms = &mux->streams[j]; + + switch (ms->src.type) { + case SCH_NODE_TYPE_ENC: { + SchEnc *enc = &sch->enc[ms->src.idx]; + if (enc->src.type == SCH_NODE_TYPE_DEC) { + ms->src_sched = sch->dec[enc->src.idx].src; + av_assert0(ms->src_sched.type == SCH_NODE_TYPE_DEMUX); + } else { + ms->src_sched = enc->src; + av_assert0(ms->src_sched.type == SCH_NODE_TYPE_FILTER_OUT); + } + break; + } + case SCH_NODE_TYPE_DEMUX: + ms->src_sched = ms->src; + break; + default: + av_log(mux, AV_LOG_ERROR, + "Muxer stream #%u not connected to a source\n", j); + return AVERROR(EINVAL); + } + } + + ret = queue_alloc(&mux->queue, mux->nb_streams, mux->queue_size, + QUEUE_PACKETS); + if (ret < 0) + return ret; + } + + for (unsigned i = 0; i < sch->nb_filters; i++) { + SchFilterGraph *fg = &sch->filters[i]; + + for (unsigned j = 0; j < fg->nb_inputs; j++) { + SchFilterIn *fi = &fg->inputs[j]; + SchDec *dec; + + if (!fi->src.type) { + av_log(fg, AV_LOG_ERROR, + "Filtergraph input %u not connected to a source\n", j); + return AVERROR(EINVAL); + } + av_assert0(fi->src.type == SCH_NODE_TYPE_DEC); + dec = &sch->dec[fi->src.idx]; + + switch (dec->src.type) { + case SCH_NODE_TYPE_DEMUX: fi->src_sched = dec->src; break; + case SCH_NODE_TYPE_ENC: fi->src_sched = sch->enc[dec->src.idx].src; break; + default: av_assert0(0); + } + } + + for (unsigned j = 0; j < fg->nb_outputs; j++) { + SchFilterOut *fo = &fg->outputs[j]; + + if (!fo->dst.type) { + av_log(fg, AV_LOG_ERROR, + "Filtergraph %u output %u not connected to a sink\n", i, j); + return AVERROR(EINVAL); + } + } + } + + // Check that the transcoding graph has no cycles. + ret = check_acyclic(sch); + if (ret < 0) + return ret; + + return 0; +} + +int sch_start(Scheduler *sch) +{ + int ret; + + ret = start_prepare(sch); + if (ret < 0) + return ret; + + av_assert0(sch->state == SCH_STATE_UNINIT); + sch->state = SCH_STATE_STARTED; + + for (unsigned i = 0; i < sch->nb_mux; i++) { + SchMux *mux = &sch->mux[i]; + + if (mux->nb_streams_ready == mux->nb_streams) { + ret = mux_init(sch, mux); + if (ret < 0) + goto fail; + } + } + + for (unsigned i = 0; i < sch->nb_enc; i++) { + SchEnc *enc = &sch->enc[i]; + + ret = task_start(&enc->task); + if (ret < 0) + goto fail; + } + + for (unsigned i = 0; i < sch->nb_filters; i++) { + SchFilterGraph *fg = &sch->filters[i]; + + ret = task_start(&fg->task); + if (ret < 0) + goto fail; + } + + for (unsigned i = 0; i < sch->nb_dec; i++) { + SchDec *dec = &sch->dec[i]; + + ret = task_start(&dec->task); + if (ret < 0) + goto fail; + } + + for (unsigned i = 0; i < sch->nb_demux; i++) { + SchDemux *d = &sch->demux[i]; + + if (!d->nb_streams) + continue; + + ret = task_start(&d->task); + if (ret < 0) + goto fail; + } + + pthread_mutex_lock(&sch->schedule_lock); + schedule_update_locked(sch); + pthread_mutex_unlock(&sch->schedule_lock); + + return 0; +fail: + sch_stop(sch, NULL); + return ret; +} + +int sch_wait(Scheduler *sch, uint64_t timeout_us, int64_t *transcode_ts) +{ + int ret, err; + + // convert delay to absolute timestamp + timeout_us += av_gettime(); + + pthread_mutex_lock(&sch->mux_done_lock); + + if (sch->nb_mux_done < sch->nb_mux) { + struct timespec tv = { .tv_sec = timeout_us / 1000000, + .tv_nsec = (timeout_us % 1000000) * 1000 }; + pthread_cond_timedwait(&sch->mux_done_cond, &sch->mux_done_lock, &tv); + } + + ret = sch->nb_mux_done == sch->nb_mux; + + pthread_mutex_unlock(&sch->mux_done_lock); + + *transcode_ts = atomic_load(&sch->last_dts); + + // abort transcoding if any task failed + err = atomic_load(&sch->task_failed); + + return ret || err; +} + +static int enc_open(Scheduler *sch, SchEnc *enc, const AVFrame *frame) +{ + int ret; + + ret = enc->open_cb(enc->task.func_arg, frame); + if (ret < 0) + return ret; + + // ret>0 signals audio frame size, which means sync queue must + // have been enabled during encoder creation + if (ret > 0) { + SchSyncQueue *sq; + + av_assert0(enc->sq_idx[0] >= 0); + sq = &sch->sq_enc[enc->sq_idx[0]]; + + pthread_mutex_lock(&sq->lock); + + sq_frame_samples(sq->sq, enc->sq_idx[1], ret); + + pthread_mutex_unlock(&sq->lock); + } + + return 0; +} + +static int send_to_enc_thread(Scheduler *sch, SchEnc *enc, AVFrame *frame) +{ + int ret; + + if (!frame) { + tq_send_finish(enc->queue, 0); + return 0; + } + + if (enc->in_finished) + return AVERROR_EOF; + + ret = tq_send(enc->queue, 0, frame); + if (ret < 0) + enc->in_finished = 1; + + return ret; +} + +static int send_to_enc_sq(Scheduler *sch, SchEnc *enc, AVFrame *frame) +{ + SchSyncQueue *sq = &sch->sq_enc[enc->sq_idx[0]]; + int ret = 0; + + // inform the scheduling code that no more input will arrive along this path; + // this is necessary because the sync queue may not send an EOF downstream + // until other streams finish + // TODO: consider a cleaner way of passing this information through + // the pipeline + if (!frame) { + for (unsigned i = 0; i < enc->nb_dst; i++) { + SchMux *mux; + SchMuxStream *ms; + + if (enc->dst[i].type != SCH_NODE_TYPE_MUX) + continue; + + mux = &sch->mux[enc->dst[i].idx]; + ms = &mux->streams[enc->dst[i].idx_stream]; + + pthread_mutex_lock(&sch->schedule_lock); + + ms->source_finished = 1; + schedule_update_locked(sch); + + pthread_mutex_unlock(&sch->schedule_lock); + } + } + + pthread_mutex_lock(&sq->lock); + + ret = sq_send(sq->sq, enc->sq_idx[1], SQFRAME(frame)); + if (ret < 0) + goto finish; + + while (1) { + SchEnc *enc; + + // TODO: the SQ API should be extended to allow returning EOF + // for individual streams + ret = sq_receive(sq->sq, -1, SQFRAME(sq->frame)); + if (ret < 0) { + ret = (ret == AVERROR(EAGAIN)) ? 0 : ret; + break; + } + + enc = &sch->enc[sq->enc_idx[ret]]; + ret = send_to_enc_thread(sch, enc, sq->frame); + if (ret < 0) { + av_frame_unref(sq->frame); + if (ret != AVERROR_EOF) + break; + + sq_send(sq->sq, enc->sq_idx[1], SQFRAME(NULL)); + continue; + } + } + + if (ret < 0) { + // close all encoders fed from this sync queue + for (unsigned i = 0; i < sq->nb_enc_idx; i++) { + int err = send_to_enc_thread(sch, &sch->enc[sq->enc_idx[i]], NULL); + + // if the sync queue error is EOF and closing the encoder + // produces a more serious error, make sure to pick the latter + ret = err_merge((ret == AVERROR_EOF && err < 0) ? 0 : ret, err); + } + } + +finish: + pthread_mutex_unlock(&sq->lock); + + return ret; +} + +static int send_to_enc(Scheduler *sch, SchEnc *enc, AVFrame *frame) +{ + if (enc->open_cb && frame && !enc->opened) { + int ret = enc_open(sch, enc, frame); + if (ret < 0) + return ret; + enc->opened = 1; + + // discard empty frames that only carry encoder init parameters + if (!frame->buf[0]) { + av_frame_unref(frame); + return 0; + } + } + + return (enc->sq_idx[0] >= 0) ? + send_to_enc_sq (sch, enc, frame) : + send_to_enc_thread(sch, enc, frame); +} + +static int mux_queue_packet(SchMux *mux, SchMuxStream *ms, AVPacket *pkt) +{ + PreMuxQueue *q = &ms->pre_mux_queue; + AVPacket *tmp_pkt = NULL; + int ret; + + if (!av_fifo_can_write(q->fifo)) { + size_t packets = av_fifo_can_read(q->fifo); + size_t pkt_size = pkt ? pkt->size : 0; + int thresh_reached = (q->data_size + pkt_size) > q->data_threshold; + size_t max_packets = thresh_reached ? q->max_packets : SIZE_MAX; + size_t new_size = FFMIN(2 * packets, max_packets); + + if (new_size <= packets) { + av_log(mux, AV_LOG_ERROR, + "Too many packets buffered for output stream.\n"); + return AVERROR(ENOSPC); + } + ret = av_fifo_grow2(q->fifo, new_size - packets); + if (ret < 0) + return ret; + } + + if (pkt) { + tmp_pkt = av_packet_alloc(); + if (!tmp_pkt) + return AVERROR(ENOMEM); + + av_packet_move_ref(tmp_pkt, pkt); + q->data_size += tmp_pkt->size; + } + av_fifo_write(q->fifo, &tmp_pkt, 1); + + return 0; +} + +static int send_to_mux(Scheduler *sch, SchMux *mux, unsigned stream_idx, + AVPacket *pkt) +{ + SchMuxStream *ms = &mux->streams[stream_idx]; + int64_t dts = (pkt && pkt->dts != AV_NOPTS_VALUE) ? + av_rescale_q(pkt->dts + pkt->duration, pkt->time_base, AV_TIME_BASE_Q) : + AV_NOPTS_VALUE; + + // queue the packet if the muxer cannot be started yet + if (!atomic_load(&mux->mux_started)) { + int queued = 0; + + // the muxer could have started between the above atomic check and + // locking the mutex, then this block falls through to normal send path + pthread_mutex_lock(&sch->mux_ready_lock); + + if (!atomic_load(&mux->mux_started)) { + int ret = mux_queue_packet(mux, ms, pkt); + queued = ret < 0 ? ret : 1; + } + + pthread_mutex_unlock(&sch->mux_ready_lock); + + if (queued < 0) + return queued; + else if (queued) + goto update_schedule; + } + + if (pkt) { + int ret; + + if (ms->init_eof) + return AVERROR_EOF; + + ret = tq_send(mux->queue, stream_idx, pkt); + if (ret < 0) + return ret; + } else + tq_send_finish(mux->queue, stream_idx); + +update_schedule: + // TODO: use atomics to check whether this changes trailing dts + // to avoid locking unnecesarily + if (dts != AV_NOPTS_VALUE || !pkt) { + pthread_mutex_lock(&sch->schedule_lock); + + if (pkt) ms->last_dts = dts; + else ms->source_finished = 1; + + schedule_update_locked(sch); + + pthread_mutex_unlock(&sch->schedule_lock); + } + + return 0; +} + +static int +demux_stream_send_to_dst(Scheduler *sch, const SchedulerNode dst, + uint8_t *dst_finished, AVPacket *pkt, unsigned flags) +{ + int ret; + + if (*dst_finished) + return AVERROR_EOF; + + if (pkt && dst.type == SCH_NODE_TYPE_MUX && + (flags & DEMUX_SEND_STREAMCOPY_EOF)) { + av_packet_unref(pkt); + pkt = NULL; + } + + if (!pkt) + goto finish; + + ret = (dst.type == SCH_NODE_TYPE_MUX) ? + send_to_mux(sch, &sch->mux[dst.idx], dst.idx_stream, pkt) : + tq_send(sch->dec[dst.idx].queue, 0, pkt); + if (ret == AVERROR_EOF) + goto finish; + + return ret; + +finish: + if (dst.type == SCH_NODE_TYPE_MUX) + send_to_mux(sch, &sch->mux[dst.idx], dst.idx_stream, NULL); + else + tq_send_finish(sch->dec[dst.idx].queue, 0); + + *dst_finished = 1; + return AVERROR_EOF; +} + +static int demux_send_for_stream(Scheduler *sch, SchDemux *d, SchDemuxStream *ds, + AVPacket *pkt, unsigned flags) +{ + unsigned nb_done = 0; + + for (unsigned i = 0; i < ds->nb_dst; i++) { + AVPacket *to_send = pkt; + uint8_t *finished = &ds->dst_finished[i]; + + int ret; + + // sending a packet consumes it, so make a temporary reference if needed + if (pkt && i < ds->nb_dst - 1) { + to_send = d->send_pkt; + + ret = av_packet_ref(to_send, pkt); + if (ret < 0) + return ret; + } + + ret = demux_stream_send_to_dst(sch, ds->dst[i], finished, to_send, flags); + if (to_send) + av_packet_unref(to_send); + if (ret == AVERROR_EOF) + nb_done++; + else if (ret < 0) + return ret; + } + + return (nb_done == ds->nb_dst) ? AVERROR_EOF : 0; +} + +static int demux_flush(Scheduler *sch, SchDemux *d, AVPacket *pkt) +{ + Timestamp max_end_ts = (Timestamp){ .ts = AV_NOPTS_VALUE }; + + av_assert0(!pkt->buf && !pkt->data && !pkt->side_data_elems); + + for (unsigned i = 0; i < d->nb_streams; i++) { + SchDemuxStream *ds = &d->streams[i]; + + for (unsigned j = 0; j < ds->nb_dst; j++) { + const SchedulerNode *dst = &ds->dst[j]; + SchDec *dec; + int ret; + + if (ds->dst_finished[j] || dst->type != SCH_NODE_TYPE_DEC) + continue; + + dec = &sch->dec[dst->idx]; + + ret = tq_send(dec->queue, 0, pkt); + if (ret < 0) + return ret; + + if (dec->queue_end_ts) { + Timestamp ts; + ret = av_thread_message_queue_recv(dec->queue_end_ts, &ts, 0); + if (ret < 0) + return ret; + + if (max_end_ts.ts == AV_NOPTS_VALUE || + (ts.ts != AV_NOPTS_VALUE && + av_compare_ts(max_end_ts.ts, max_end_ts.tb, ts.ts, ts.tb) < 0)) + max_end_ts = ts; + + } + } + } + + pkt->pts = max_end_ts.ts; + pkt->time_base = max_end_ts.tb; + + return 0; +} + +int sch_demux_send(Scheduler *sch, unsigned demux_idx, AVPacket *pkt, + unsigned flags) +{ + SchDemux *d; + int terminate; + + av_assert0(demux_idx < sch->nb_demux); + d = &sch->demux[demux_idx]; + + terminate = waiter_wait(sch, &d->waiter); + if (terminate) + return AVERROR_EXIT; + + // flush the downstreams after seek + if (pkt->stream_index == -1) + return demux_flush(sch, d, pkt); + + av_assert0(pkt->stream_index < d->nb_streams); + + return demux_send_for_stream(sch, d, &d->streams[pkt->stream_index], pkt, flags); +} + +static int demux_done(Scheduler *sch, unsigned demux_idx) +{ + SchDemux *d = &sch->demux[demux_idx]; + int ret = 0; + + for (unsigned i = 0; i < d->nb_streams; i++) { + int err = demux_send_for_stream(sch, d, &d->streams[i], NULL, 0); + if (err != AVERROR_EOF) + ret = err_merge(ret, err); + } + + pthread_mutex_lock(&sch->schedule_lock); + + d->task_exited = 1; + + schedule_update_locked(sch); + + pthread_mutex_unlock(&sch->schedule_lock); + + return ret; +} + +int sch_mux_receive(Scheduler *sch, unsigned mux_idx, AVPacket *pkt) +{ + SchMux *mux; + int ret, stream_idx; + + av_assert0(mux_idx < sch->nb_mux); + mux = &sch->mux[mux_idx]; + + ret = tq_receive(mux->queue, &stream_idx, pkt); + pkt->stream_index = stream_idx; + return ret; +} + +void sch_mux_receive_finish(Scheduler *sch, unsigned mux_idx, unsigned stream_idx) +{ + SchMux *mux; + + av_assert0(mux_idx < sch->nb_mux); + mux = &sch->mux[mux_idx]; + + av_assert0(stream_idx < mux->nb_streams); + tq_receive_finish(mux->queue, stream_idx); + + pthread_mutex_lock(&sch->schedule_lock); + mux->streams[stream_idx].source_finished = 1; + + schedule_update_locked(sch); + + pthread_mutex_unlock(&sch->schedule_lock); +} + +int sch_mux_sub_heartbeat(Scheduler *sch, unsigned mux_idx, unsigned stream_idx, + const AVPacket *pkt) +{ + SchMux *mux; + SchMuxStream *ms; + + av_assert0(mux_idx < sch->nb_mux); + mux = &sch->mux[mux_idx]; + + av_assert0(stream_idx < mux->nb_streams); + ms = &mux->streams[stream_idx]; + + for (unsigned i = 0; i < ms->nb_sub_heartbeat_dst; i++) { + SchDec *dst = &sch->dec[ms->sub_heartbeat_dst[i]]; + int ret; + + ret = av_packet_copy_props(mux->sub_heartbeat_pkt, pkt); + if (ret < 0) + return ret; + + tq_send(dst->queue, 0, mux->sub_heartbeat_pkt); + } + + return 0; +} + +static int mux_done(Scheduler *sch, unsigned mux_idx) +{ + SchMux *mux = &sch->mux[mux_idx]; + + pthread_mutex_lock(&sch->schedule_lock); + + for (unsigned i = 0; i < mux->nb_streams; i++) { + tq_receive_finish(mux->queue, i); + mux->streams[i].source_finished = 1; + } + + schedule_update_locked(sch); + + pthread_mutex_unlock(&sch->schedule_lock); + + pthread_mutex_lock(&sch->mux_done_lock); + + av_assert0(sch->nb_mux_done < sch->nb_mux); + sch->nb_mux_done++; + + pthread_cond_signal(&sch->mux_done_cond); + + pthread_mutex_unlock(&sch->mux_done_lock); + + return 0; +} + +int sch_dec_receive(Scheduler *sch, unsigned dec_idx, AVPacket *pkt) +{ + SchDec *dec; + int ret, dummy; + + av_assert0(dec_idx < sch->nb_dec); + dec = &sch->dec[dec_idx]; + + // the decoder should have given us post-flush end timestamp in pkt + if (dec->expect_end_ts) { + Timestamp ts = (Timestamp){ .ts = pkt->pts, .tb = pkt->time_base }; + ret = av_thread_message_queue_send(dec->queue_end_ts, &ts, 0); + if (ret < 0) + return ret; + + dec->expect_end_ts = 0; + } + + ret = tq_receive(dec->queue, &dummy, pkt); + av_assert0(dummy <= 0); + + // got a flush packet, on the next call to this function the decoder + // will give us post-flush end timestamp + if (ret >= 0 && !pkt->data && !pkt->side_data_elems && dec->queue_end_ts) + dec->expect_end_ts = 1; + + return ret; +} + +static int send_to_filter(Scheduler *sch, SchFilterGraph *fg, + unsigned in_idx, AVFrame *frame) +{ + if (frame) + return tq_send(fg->queue, in_idx, frame); + + if (!fg->inputs[in_idx].send_finished) { + fg->inputs[in_idx].send_finished = 1; + tq_send_finish(fg->queue, in_idx); + + // close the control stream when all actual inputs are done + if (atomic_fetch_add(&fg->nb_inputs_finished_send, 1) == fg->nb_inputs - 1) + tq_send_finish(fg->queue, fg->nb_inputs); + } + return 0; +} + +static int dec_send_to_dst(Scheduler *sch, const SchedulerNode dst, + uint8_t *dst_finished, AVFrame *frame) +{ + int ret; + + if (*dst_finished) + return AVERROR_EOF; + + if (!frame) + goto finish; + + ret = (dst.type == SCH_NODE_TYPE_FILTER_IN) ? + send_to_filter(sch, &sch->filters[dst.idx], dst.idx_stream, frame) : + send_to_enc(sch, &sch->enc[dst.idx], frame); + if (ret == AVERROR_EOF) + goto finish; + + return ret; + +finish: + if (dst.type == SCH_NODE_TYPE_FILTER_IN) + send_to_filter(sch, &sch->filters[dst.idx], dst.idx_stream, NULL); + else + send_to_enc(sch, &sch->enc[dst.idx], NULL); + + *dst_finished = 1; + + return AVERROR_EOF; +} + +int sch_dec_send(Scheduler *sch, unsigned dec_idx, AVFrame *frame) +{ + SchDec *dec; + int ret = 0; + unsigned nb_done = 0; + + av_assert0(dec_idx < sch->nb_dec); + dec = &sch->dec[dec_idx]; + + for (unsigned i = 0; i < dec->nb_dst; i++) { + uint8_t *finished = &dec->dst_finished[i]; + AVFrame *to_send = frame; + + // sending a frame consumes it, so make a temporary reference if needed + if (i < dec->nb_dst - 1) { + to_send = dec->send_frame; + + // frame may sometimes contain props only, + // e.g. to signal EOF timestamp + ret = frame->buf[0] ? av_frame_ref(to_send, frame) : + av_frame_copy_props(to_send, frame); + if (ret < 0) + return ret; + } + + ret = dec_send_to_dst(sch, dec->dst[i], finished, to_send); + if (ret < 0) { + av_frame_unref(to_send); + if (ret == AVERROR_EOF) { + nb_done++; + ret = 0; + continue; + } + return ret; + } + } + + return (nb_done == dec->nb_dst) ? AVERROR_EOF : 0; +} + +static int dec_done(Scheduler *sch, unsigned dec_idx) +{ + SchDec *dec = &sch->dec[dec_idx]; + int ret = 0; + + tq_receive_finish(dec->queue, 0); + + // make sure our source does not get stuck waiting for end timestamps + // that will never arrive + if (dec->queue_end_ts) + av_thread_message_queue_set_err_recv(dec->queue_end_ts, AVERROR_EOF); + + for (unsigned i = 0; i < dec->nb_dst; i++) { + int err = dec_send_to_dst(sch, dec->dst[i], &dec->dst_finished[i], NULL); + if (err < 0 && err != AVERROR_EOF) + ret = err_merge(ret, err); + } + + return ret; +} + +int sch_enc_receive(Scheduler *sch, unsigned enc_idx, AVFrame *frame) +{ + SchEnc *enc; + int ret, dummy; + + av_assert0(enc_idx < sch->nb_enc); + enc = &sch->enc[enc_idx]; + + ret = tq_receive(enc->queue, &dummy, frame); + av_assert0(dummy <= 0); + + return ret; +} + +static int enc_send_to_dst(Scheduler *sch, const SchedulerNode dst, + uint8_t *dst_finished, AVPacket *pkt) +{ + int ret; + + if (*dst_finished) + return AVERROR_EOF; + + if (!pkt) + goto finish; + + ret = (dst.type == SCH_NODE_TYPE_MUX) ? + send_to_mux(sch, &sch->mux[dst.idx], dst.idx_stream, pkt) : + tq_send(sch->dec[dst.idx].queue, 0, pkt); + if (ret == AVERROR_EOF) + goto finish; + + return ret; + +finish: + if (dst.type == SCH_NODE_TYPE_MUX) + send_to_mux(sch, &sch->mux[dst.idx], dst.idx_stream, NULL); + else + tq_send_finish(sch->dec[dst.idx].queue, 0); + + *dst_finished = 1; + + return AVERROR_EOF; +} + +int sch_enc_send(Scheduler *sch, unsigned enc_idx, AVPacket *pkt) +{ + SchEnc *enc; + int ret; + + av_assert0(enc_idx < sch->nb_enc); + enc = &sch->enc[enc_idx]; + + for (unsigned i = 0; i < enc->nb_dst; i++) { + uint8_t *finished = &enc->dst_finished[i]; + AVPacket *to_send = pkt; + + // sending a packet consumes it, so make a temporary reference if needed + if (i < enc->nb_dst - 1) { + to_send = enc->send_pkt; + + ret = av_packet_ref(to_send, pkt); + if (ret < 0) + return ret; + } + + ret = enc_send_to_dst(sch, enc->dst[i], finished, to_send); + if (ret < 0) { + av_packet_unref(to_send); + if (ret == AVERROR_EOF) { + ret = 0; + continue; + } + return ret; + } + } + + return ret; +} + +static int enc_done(Scheduler *sch, unsigned enc_idx) +{ + SchEnc *enc = &sch->enc[enc_idx]; + int ret = 0; + + tq_receive_finish(enc->queue, 0); + + for (unsigned i = 0; i < enc->nb_dst; i++) { + int err = enc_send_to_dst(sch, enc->dst[i], &enc->dst_finished[i], NULL); + if (err < 0 && err != AVERROR_EOF) + ret = err_merge(ret, err); + } + + return ret; +} + +int sch_filter_receive(Scheduler *sch, unsigned fg_idx, + unsigned *in_idx, AVFrame *frame) +{ + SchFilterGraph *fg; + + av_assert0(fg_idx < sch->nb_filters); + fg = &sch->filters[fg_idx]; + + av_assert0(*in_idx <= fg->nb_inputs); + + // update scheduling to account for desired input stream, if it changed + // + // this check needs no locking because only the filtering thread + // updates this value + if (*in_idx != fg->best_input) { + pthread_mutex_lock(&sch->schedule_lock); + + fg->best_input = *in_idx; + schedule_update_locked(sch); + + pthread_mutex_unlock(&sch->schedule_lock); + } + + if (*in_idx == fg->nb_inputs) { + int terminate = waiter_wait(sch, &fg->waiter); + return terminate ? AVERROR_EOF : AVERROR(EAGAIN); + } + + while (1) { + int ret, idx; + + ret = tq_receive(fg->queue, &idx, frame); + if (idx < 0) + return AVERROR_EOF; + else if (ret >= 0) { + *in_idx = idx; + return 0; + } + + // disregard EOFs for specific streams - they should always be + // preceded by an EOF frame + } +} + +void sch_filter_receive_finish(Scheduler *sch, unsigned fg_idx, unsigned in_idx) +{ + SchFilterGraph *fg; + SchFilterIn *fi; + + av_assert0(fg_idx < sch->nb_filters); + fg = &sch->filters[fg_idx]; + + av_assert0(in_idx < fg->nb_inputs); + fi = &fg->inputs[in_idx]; + + if (!fi->receive_finished) { + fi->receive_finished = 1; + tq_receive_finish(fg->queue, in_idx); + + // close the control stream when all actual inputs are done + if (++fg->nb_inputs_finished_receive == fg->nb_inputs) + tq_receive_finish(fg->queue, fg->nb_inputs); + } +} + +int sch_filter_send(Scheduler *sch, unsigned fg_idx, unsigned out_idx, AVFrame *frame) +{ + SchFilterGraph *fg; + + av_assert0(fg_idx < sch->nb_filters); + fg = &sch->filters[fg_idx]; + + av_assert0(out_idx < fg->nb_outputs); + return send_to_enc(sch, &sch->enc[fg->outputs[out_idx].dst.idx], frame); +} + +static int filter_done(Scheduler *sch, unsigned fg_idx) +{ + SchFilterGraph *fg = &sch->filters[fg_idx]; + int ret = 0; + + for (unsigned i = 0; i <= fg->nb_inputs; i++) + tq_receive_finish(fg->queue, i); + + for (unsigned i = 0; i < fg->nb_outputs; i++) { + SchEnc *enc = &sch->enc[fg->outputs[i].dst.idx]; + int err = send_to_enc(sch, enc, NULL); + if (err < 0 && err != AVERROR_EOF) + ret = err_merge(ret, err); + } + + pthread_mutex_lock(&sch->schedule_lock); + + fg->task_exited = 1; + + schedule_update_locked(sch); + + pthread_mutex_unlock(&sch->schedule_lock); + + return ret; +} + +int sch_filter_command(Scheduler *sch, unsigned fg_idx, AVFrame *frame) +{ + SchFilterGraph *fg; + + av_assert0(fg_idx < sch->nb_filters); + fg = &sch->filters[fg_idx]; + + return send_to_filter(sch, fg, fg->nb_inputs, frame); +} + +static int task_cleanup(Scheduler *sch, SchedulerNode node) +{ + switch (node.type) { + case SCH_NODE_TYPE_DEMUX: return demux_done (sch, node.idx); + case SCH_NODE_TYPE_MUX: return mux_done (sch, node.idx); + case SCH_NODE_TYPE_DEC: return dec_done (sch, node.idx); + case SCH_NODE_TYPE_ENC: return enc_done (sch, node.idx); + case SCH_NODE_TYPE_FILTER_IN: return filter_done(sch, node.idx); + default: av_assert0(0); + } +} + +static void *task_wrapper(void *arg) +{ + SchTask *task = arg; + Scheduler *sch = task->parent; + int ret; + int err = 0; + + ret = task->func(task->func_arg); + if (ret < 0) + av_log(task->func_arg, AV_LOG_ERROR, + "Task finished with error code: %d (%s)\n", ret, av_err2str(ret)); + + err = task_cleanup(sch, task->node); + ret = err_merge(ret, err); + + // EOF is considered normal termination + if (ret == AVERROR_EOF) + ret = 0; + if (ret < 0) + atomic_store(&sch->task_failed, 1); + + av_log(task->func_arg, ret < 0 ? AV_LOG_ERROR : AV_LOG_VERBOSE, + "Terminating thread with return code %d (%s)\n", ret, + ret < 0 ? av_err2str(ret) : "success"); + + return (void*)(intptr_t)ret; +} + +static int task_stop(Scheduler *sch, SchTask *task) +{ + int ret; + void *thread_ret; + + if (!task->thread_running) + return task_cleanup(sch, task->node); + + ret = pthread_join(task->thread, &thread_ret); + av_assert0(ret == 0); + + task->thread_running = 0; + + return (intptr_t)thread_ret; +} + +int sch_stop(Scheduler *sch, int64_t *finish_ts) +{ + int ret = 0, err; + + if (sch->state != SCH_STATE_STARTED) + return 0; + + atomic_store(&sch->terminate, 1); + + for (unsigned type = 0; type < 2; type++) + for (unsigned i = 0; i < (type ? sch->nb_demux : sch->nb_filters); i++) { + SchWaiter *w = type ? &sch->demux[i].waiter : &sch->filters[i].waiter; + waiter_set(w, 1); + } + + for (unsigned i = 0; i < sch->nb_demux; i++) { + SchDemux *d = &sch->demux[i]; + + err = task_stop(sch, &d->task); + ret = err_merge(ret, err); + } + + for (unsigned i = 0; i < sch->nb_dec; i++) { + SchDec *dec = &sch->dec[i]; + + err = task_stop(sch, &dec->task); + ret = err_merge(ret, err); + } + + for (unsigned i = 0; i < sch->nb_filters; i++) { + SchFilterGraph *fg = &sch->filters[i]; + + err = task_stop(sch, &fg->task); + ret = err_merge(ret, err); + } + + for (unsigned i = 0; i < sch->nb_enc; i++) { + SchEnc *enc = &sch->enc[i]; + + err = task_stop(sch, &enc->task); + ret = err_merge(ret, err); + } + + for (unsigned i = 0; i < sch->nb_mux; i++) { + SchMux *mux = &sch->mux[i]; + + err = task_stop(sch, &mux->task); + ret = err_merge(ret, err); + } + + if (finish_ts) + *finish_ts = trailing_dts(sch, 1); + + sch->state = SCH_STATE_STOPPED; + + return ret; +} diff --git a/fftools/ffmpeg_sched.h b/fftools/ffmpeg_sched.h new file mode 100644 index 00000000000..e51c26cec90 --- /dev/null +++ b/fftools/ffmpeg_sched.h @@ -0,0 +1,489 @@ +/* + * Inter-thread scheduling/synchronization. + * Copyright (c) 2023 Anton Khirnov + * + * 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 + */ + +#ifndef FFTOOLS_FFMPEG_SCHED_H +#define FFTOOLS_FFMPEG_SCHED_H + +#include +#include + +#include "ffmpeg_utils.h" + +/* + * This file contains the API for the transcode scheduler. + * + * Overall architecture of the transcoding process involves instances of the + * following components: + * - demuxers, each containing any number of demuxed streams; demuxed packets + * belonging to some stream are sent to any number of decoders (transcoding) + * and/or muxers (streamcopy); + * - decoders, which receive encoded packets from some demuxed stream or + * encoder, decode them, and send decoded frames to any number of filtergraph + * inputs (audio/video) or encoders (subtitles); + * - filtergraphs, each containing zero or more inputs (0 in case the + * filtergraph contains a lavfi source filter), and one or more outputs; the + * inputs and outputs need not have matching media types; + * each filtergraph input receives decoded frames from some decoder; + * filtered frames from each output are sent to some encoder; + * - encoders, which receive decoded frames from some decoder (subtitles) or + * some filtergraph output (audio/video), encode them, and send encoded + * packets to any number of muxed streams or decoders; + * - muxers, each containing any number of muxed streams; each muxed stream + * receives encoded packets from some demuxed stream (streamcopy) or some + * encoder (transcoding); those packets are interleaved and written out by the + * muxer. + * + * There must be at least one muxer instance, otherwise the transcode produces + * no output and is meaningless. Otherwise, in a generic transcoding scenario + * there may be arbitrary number of instances of any of the above components, + * interconnected in various ways. + * + * The code tries to keep all the output streams across all the muxers in sync + * (i.e. at the same DTS), which is accomplished by varying the rates at which + * packets are read from different demuxers and lavfi sources. Note that the + * degree of control we have over synchronization is fundamentally limited - if + * some demuxed streams in the same input are interleaved at different rates + * than that at which they are to be muxed (e.g. because an input file is badly + * interleaved, or the user changed their speed by mismatching amounts), then + * there will be increasing amounts of buffering followed by eventual + * transcoding failure. + * + * N.B. 1: there are meaningful transcode scenarios with no demuxers, e.g. + * - encoding and muxing output from filtergraph(s) that have no inputs; + * - creating a file that contains nothing but attachments and/or metadata. + * + * N.B. 2: a filtergraph output could, in principle, feed multiple encoders, but + * this is unnecessary because the (a)split filter provides the same + * functionality. + * + * The scheduler, in the above model, is the master object that oversees and + * facilitates the transcoding process. The basic idea is that all instances + * of the abovementioned components communicate only with the scheduler and not + * with each other. The scheduler is then the single place containing the + * knowledge about the whole transcoding pipeline. + */ + +struct AVFrame; +struct AVPacket; + +typedef struct Scheduler Scheduler; + +enum SchedulerNodeType { + SCH_NODE_TYPE_NONE = 0, + SCH_NODE_TYPE_DEMUX, + SCH_NODE_TYPE_MUX, + SCH_NODE_TYPE_DEC, + SCH_NODE_TYPE_ENC, + SCH_NODE_TYPE_FILTER_IN, + SCH_NODE_TYPE_FILTER_OUT, +}; + +typedef struct SchedulerNode { + enum SchedulerNodeType type; + unsigned idx; + unsigned idx_stream; +} SchedulerNode; + +typedef int (*SchThreadFunc)(void *arg); + +#define SCH_DSTREAM(file, stream) \ + (SchedulerNode){ .type = SCH_NODE_TYPE_DEMUX, \ + .idx = file, .idx_stream = stream } +#define SCH_MSTREAM(file, stream) \ + (SchedulerNode){ .type = SCH_NODE_TYPE_MUX, \ + .idx = file, .idx_stream = stream } +#define SCH_DEC(decoder) \ + (SchedulerNode){ .type = SCH_NODE_TYPE_DEC, \ + .idx = decoder } +#define SCH_ENC(encoder) \ + (SchedulerNode){ .type = SCH_NODE_TYPE_ENC, \ + .idx = encoder } +#define SCH_FILTER_IN(filter, input) \ + (SchedulerNode){ .type = SCH_NODE_TYPE_FILTER_IN, \ + .idx = filter, .idx_stream = input } +#define SCH_FILTER_OUT(filter, output) \ + (SchedulerNode){ .type = SCH_NODE_TYPE_FILTER_OUT, \ + .idx = filter, .idx_stream = output } + +Scheduler *sch_alloc(void); +void sch_free(Scheduler **sch); + +int sch_start(Scheduler *sch); +int sch_stop(Scheduler *sch, int64_t *finish_ts); + +/** + * Wait until transcoding terminates or the specified timeout elapses. + * + * @param timeout_us Amount of time in microseconds after which this function + * will timeout. + * @param transcode_ts Current transcode timestamp in AV_TIME_BASE_Q, for + * informational purposes only. + * + * @retval 0 waiting timed out, transcoding is not finished + * @retval 1 transcoding is finished + */ +int sch_wait(Scheduler *sch, uint64_t timeout_us, int64_t *transcode_ts); + +/** + * Add a demuxer to the scheduler. + * + * @param func Function executed as the demuxer task. + * @param ctx Demuxer state; will be passed to func and used for logging. + * + * @retval ">=0" Index of the newly-created demuxer. + * @retval "<0" Error code. + */ +int sch_add_demux(Scheduler *sch, SchThreadFunc func, void *ctx); +/** + * Add a demuxed stream for a previously added demuxer. + * + * @param demux_idx index previously returned by sch_add_demux() + * + * @retval ">=0" Index of the newly-created demuxed stream. + * @retval "<0" Error code. + */ +int sch_add_demux_stream(Scheduler *sch, unsigned demux_idx); + +/** + * Add a decoder to the scheduler. + * + * @param func Function executed as the decoder task. + * @param ctx Decoder state; will be passed to func and used for logging. + * @param send_end_ts The decoder will return an end timestamp after flush packets + * are delivered to it. See documentation for + * sch_dec_receive() for more details. + * + * @retval ">=0" Index of the newly-created decoder. + * @retval "<0" Error code. + */ +int sch_add_dec(Scheduler *sch, SchThreadFunc func, void *ctx, + int send_end_ts); + +/** + * Add a filtergraph to the scheduler. + * + * @param nb_inputs Number of filtergraph inputs. + * @param nb_outputs number of filtergraph outputs + * @param func Function executed as the filtering task. + * @param ctx Filter state; will be passed to func and used for logging. + * + * @retval ">=0" Index of the newly-created filtergraph. + * @retval "<0" Error code. + */ +int sch_add_filtergraph(Scheduler *sch, unsigned nb_inputs, unsigned nb_outputs, + SchThreadFunc func, void *ctx); + +/** + * Add a muxer to the scheduler. + * + * Note that muxer thread startup is more complicated than for other components, + * because + * - muxer streams fed by audio/video encoders become initialized dynamically at + * runtime, after those encoders receive their first frame and initialize + * themselves, followed by calling sch_mux_stream_ready() + * - the header can be written after all the streams for a muxer are initialized + * - we may need to write an SDP, which must happen + * - AFTER all the headers are written + * - BEFORE any packets are written by any muxer + * - with all the muxers quiescent + * To avoid complicated muxer-thread synchronization dances, we postpone + * starting the muxer threads until after the SDP is written. The sequence of + * events is then as follows: + * - After sch_mux_stream_ready() is called for all the streams in a given muxer, + * the header for that muxer is written (care is taken that headers for + * different muxers are not written concurrently, since they write file + * information to stderr). If SDP is not wanted, the muxer thread then starts + * and muxing begins. + * - When SDP _is_ wanted, no muxer threads start until the header for the last + * muxer is written. After that, the SDP is written, after which all the muxer + * threads are started at once. + * + * In order for the above to work, the scheduler needs to be able to invoke + * just writing the header, which is the reason the init parameter exists. + * + * @param func Function executed as the muxing task. + * @param init Callback that is called to initialize the muxer and write the + * header. Called after sch_mux_stream_ready() is called for all the + * streams in the muxer. + * @param ctx Muxer state; will be passed to func/init and used for logging. + * @param sdp_auto Determines automatic SDP writing - see sch_sdp_filename(). + * @param thread_queue_size number of packets that can be buffered before + * sending to the muxer blocks + * + * @retval ">=0" Index of the newly-created muxer. + * @retval "<0" Error code. + */ +int sch_add_mux(Scheduler *sch, SchThreadFunc func, int (*init)(void *), + void *ctx, int sdp_auto, unsigned thread_queue_size); + +/** + * Default size of a packet thread queue. For muxing this can be overridden by + * the thread_queue_size option as passed to a call to sch_add_mux(). + */ +#define DEFAULT_PACKET_THREAD_QUEUE_SIZE 8 + +/** + * Default size of a frame thread queue. + */ +#define DEFAULT_FRAME_THREAD_QUEUE_SIZE 8 + +/** + * Add a muxed stream for a previously added muxer. + * + * @param mux_idx index previously returned by sch_add_mux() + * + * @retval ">=0" Index of the newly-created muxed stream. + * @retval "<0" Error code. + */ +int sch_add_mux_stream(Scheduler *sch, unsigned mux_idx); + +/** + * Configure limits on packet buffering performed before the muxer task is + * started. + * + * @param mux_idx index previously returned by sch_add_mux() + * @param stream_idx_idx index previously returned by sch_add_mux_stream() + * @param data_threshold Total size of the buffered packets' data after which + * max_packets applies. + * @param max_packets maximum Maximum number of buffered packets after + * data_threshold is reached. + */ +void sch_mux_stream_buffering(Scheduler *sch, unsigned mux_idx, unsigned stream_idx, + size_t data_threshold, int max_packets); + +/** + * Signal to the scheduler that the specified muxed stream is initialized and + * ready. Muxing is started once all the streams are ready. + */ +int sch_mux_stream_ready(Scheduler *sch, unsigned mux_idx, unsigned stream_idx); + +/** + * Set the file path for the SDP. + * + * The SDP is written when either of the following is true: + * - this function is called at least once + * - sdp_auto=1 is passed to EVERY call of sch_add_mux() + */ +int sch_sdp_filename(Scheduler *sch, const char *sdp_filename); + +/** + * Add an encoder to the scheduler. + * + * @param func Function executed as the encoding task. + * @param ctx Encoder state; will be passed to func and used for logging. + * @param open_cb This callback, if specified, will be called when the first + * frame is obtained for this encoder. For audio encoders with a + * fixed frame size (which use a sync queue in the scheduler to + * rechunk frames), it must return that frame size on success. + * Otherwise (non-audio, variable frame size) it should return 0. + * + * @retval ">=0" Index of the newly-created encoder. + * @retval "<0" Error code. + */ +int sch_add_enc(Scheduler *sch, SchThreadFunc func, void *ctx, + int (*open_cb)(void *func_arg, const struct AVFrame *frame)); + +/** + * Add an pre-encoding sync queue to the scheduler. + * + * @param buf_size_us Sync queue buffering size, passed to sq_alloc(). + * @param logctx Logging context for the sync queue. passed to sq_alloc(). + * + * @retval ">=0" Index of the newly-created sync queue. + * @retval "<0" Error code. + */ +int sch_add_sq_enc(Scheduler *sch, uint64_t buf_size_us, void *logctx); +int sch_sq_add_enc(Scheduler *sch, unsigned sq_idx, unsigned enc_idx, + int limiting, uint64_t max_frames); + +int sch_connect(Scheduler *sch, SchedulerNode src, SchedulerNode dst); + +enum DemuxSendFlags { + /** + * Treat the packet as an EOF for SCH_NODE_TYPE_MUX destinations + * send normally to other types. + */ + DEMUX_SEND_STREAMCOPY_EOF = (1 << 0), +}; + +/** + * Called by demuxer tasks to communicate with their downstreams. The following + * may be sent: + * - a demuxed packet for the stream identified by pkt->stream_index; + * - demuxer discontinuity/reset (e.g. after a seek) - this is signalled by an + * empty packet with stream_index=-1. + * + * @param demux_idx demuxer index + * @param pkt A demuxed packet to send. + * When flushing (i.e. pkt->stream_index=-1 on entry to this + * function), on successful return pkt->pts/pkt->time_base will be + * set to the maximum end timestamp of any decoded audio stream, or + * AV_NOPTS_VALUE if no decoded audio streams are present. + * + * @retval "non-negative value" success + * @retval AVERROR_EOF all consumers for the stream are done + * @retval AVERROR_EXIT all consumers are done, should terminate demuxing + * @retval "anoter negative error code" other failure + */ +int sch_demux_send(Scheduler *sch, unsigned demux_idx, struct AVPacket *pkt, + unsigned flags); + +/** + * Called by decoder tasks to receive a packet for decoding. + * + * @param dec_idx decoder index + * @param pkt Input packet will be written here on success. + * + * An empty packet signals that the decoder should be flushed, but + * more packets will follow (e.g. after seeking). When a decoder + * created with send_end_ts=1 receives a flush packet, it must write + * the end timestamp of the stream after flushing to + * pkt->pts/time_base on the next call to this function (if any). + * + * @retval "non-negative value" success + * @retval AVERROR_EOF no more packets will arrive, should terminate decoding + * @retval "another negative error code" other failure + */ +int sch_dec_receive(Scheduler *sch, unsigned dec_idx, struct AVPacket *pkt); + +/** + * Called by decoder tasks to send a decoded frame downstream. + * + * @param dec_idx Decoder index previously returned by sch_add_dec(). + * @param frame Decoded frame; on success it is consumed and cleared by this + * function + * + * @retval ">=0" success + * @retval AVERROR_EOF all consumers are done, should terminate decoding + * @retval "another negative error code" other failure + */ +int sch_dec_send(Scheduler *sch, unsigned dec_idx, struct AVFrame *frame); + +/** + * Called by filtergraph tasks to obtain frames for filtering. Will wait for a + * frame to become available and return it in frame. + * + * Filtergraphs that contain lavfi sources and do not currently require new + * input frames should call this function as a means of rate control - then + * in_idx should be set equal to nb_inputs on entry to this function. + * + * @param fg_idx Filtergraph index previously returned by sch_add_filtergraph(). + * @param[in,out] in_idx On input contains the index of the input on which a frame + * is most desired. May be set to nb_inputs to signal that + * the filtergraph does not need more input currently. + * + * On success, will be replaced with the input index of + * the actually returned frame or EOF timestamp. + * + * @retval ">=0" Frame data or EOF timestamp was delivered into frame, in_idx + * contains the index of the input it belongs to. + * @retval AVERROR(EAGAIN) No frame was returned, the filtergraph should + * resume filtering. May only be returned when + * in_idx=nb_inputs on entry to this function. + * @retval AVERROR_EOF No more frames will arrive, should terminate filtering. + */ +int sch_filter_receive(Scheduler *sch, unsigned fg_idx, + unsigned *in_idx, struct AVFrame *frame); +/** + * Called by filter tasks to signal that a filter input will no longer accept input. + * + * @param fg_idx Filtergraph index previously returned from sch_add_filtergraph(). + * @param in_idx Index of the input to finish. + */ +void sch_filter_receive_finish(Scheduler *sch, unsigned fg_idx, unsigned in_idx); + +/** + * Called by filtergraph tasks to send a filtered frame or EOF to consumers. + * + * @param fg_idx Filtergraph index previously returned by sch_add_filtergraph(). + * @param out_idx Index of the output which produced the frame. + * @param frame The frame to send to consumers. When NULL, signals that no more + * frames will be produced for the specified output. When non-NULL, + * the frame is consumed and cleared by this function on success. + * + * @retval "non-negative value" success + * @retval AVERROR_EOF all consumers are done + * @retval "anoter negative error code" other failure + */ +int sch_filter_send(Scheduler *sch, unsigned fg_idx, unsigned out_idx, + struct AVFrame *frame); + +int sch_filter_command(Scheduler *sch, unsigned fg_idx, struct AVFrame *frame); + +/** + * Called by encoder tasks to obtain frames for encoding. Will wait for a frame + * to become available and return it in frame. + * + * @param enc_idx Encoder index previously returned by sch_add_enc(). + * @param frame Newly-received frame will be stored here on success. Must be + * clean on entrance to this function. + * + * @retval 0 A frame was successfully delivered into frame. + * @retval AVERROR_EOF No more frames will be delivered, the encoder should + * flush everything and terminate. + * + */ +int sch_enc_receive(Scheduler *sch, unsigned enc_idx, struct AVFrame *frame); + +/** + * Called by encoder tasks to send encoded packets downstream. + * + * @param enc_idx Encoder index previously returned by sch_add_enc(). + * @param pkt An encoded packet; it will be consumed and cleared by this + * function on success. + * + * @retval 0 success + * @retval "<0" Error code. + */ +int sch_enc_send (Scheduler *sch, unsigned enc_idx, struct AVPacket *pkt); + +/** + * Called by muxer tasks to obtain packets for muxing. Will wait for a packet + * for any muxed stream to become available and return it in pkt. + * + * @param mux_idx Muxer index previously returned by sch_add_mux(). + * @param pkt Newly-received packet will be stored here on success. Must be + * clean on entrance to this function. + * + * @retval 0 A packet was successfully delivered into pkt. Its stream_index + * corresponds to a stream index previously returned from + * sch_add_mux_stream(). + * @retval AVERROR_EOF When pkt->stream_index is non-negative, this signals that + * no more packets will be delivered for this stream index. + * Otherwise this indicates that no more packets will be + * delivered for any stream and the muxer should therefore + * flush everything and terminate. + */ +int sch_mux_receive(Scheduler *sch, unsigned mux_idx, struct AVPacket *pkt); + +/** + * Called by muxer tasks to signal that a stream will no longer accept input. + * + * @param stream_idx Stream index previously returned from sch_add_mux_stream(). + */ +void sch_mux_receive_finish(Scheduler *sch, unsigned mux_idx, unsigned stream_idx); + +int sch_mux_sub_heartbeat_add(Scheduler *sch, unsigned mux_idx, unsigned stream_idx, + unsigned dec_idx); +int sch_mux_sub_heartbeat(Scheduler *sch, unsigned mux_idx, unsigned stream_idx, + const AVPacket *pkt); + +#endif /* FFTOOLS_FFMPEG_SCHED_H */ diff --git a/fftools/ffmpeg_utils.h b/fftools/ffmpeg_utils.h new file mode 100644 index 00000000000..bd225abc381 --- /dev/null +++ b/fftools/ffmpeg_utils.h @@ -0,0 +1,56 @@ +/* + * 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 + */ + +#ifndef FFTOOLS_FFMPEG_UTILS_H +#define FFTOOLS_FFMPEG_UTILS_H + +#include + +#include "libavutil/common.h" +#include "libavutil/frame.h" +#include "libavutil/rational.h" + +#include "libavcodec/packet.h" + +typedef struct Timestamp { + int64_t ts; + AVRational tb; +} Timestamp; + +/** + * Merge two return codes - return one of the error codes if at least one of + * them was negative, 0 otherwise. + * Currently just picks the first one, eventually we might want to do something + * more sophisticated, like sorting them by priority. + */ +static inline int err_merge(int err0, int err1) +{ + return (err0 < 0) ? err0 : FFMIN(err1, 0); +} + +static inline void pkt_move(void *dst, void *src) +{ + av_packet_move_ref(dst, src); +} + +static inline void frame_move(void *dst, void *src) +{ + av_frame_move_ref(dst, src); +} + +#endif // FFTOOLS_FFMPEG_UTILS_H diff --git a/fftools/ffplay.c b/fftools/ffplay.c index d6479aef5f7..048a4a87046 100644 --- a/fftools/ffplay.c +++ b/fftools/ffplay.c @@ -47,19 +47,18 @@ #include "libavdevice/avdevice.h" #include "libswscale/swscale.h" #include "libavutil/opt.h" -#include "libavcodec/avfft.h" +#include "libavutil/tx.h" #include "libswresample/swresample.h" -#if CONFIG_AVFILTER -# include "libavfilter/avfilter.h" -# include "libavfilter/buffersink.h" -# include "libavfilter/buffersrc.h" -#endif +#include "libavfilter/avfilter.h" +#include "libavfilter/buffersink.h" +#include "libavfilter/buffersrc.h" #include #include #include "cmdutils.h" +#include "ffplay_renderer.h" #include "opt_common.h" const char program_name[] = "ffplay"; @@ -109,8 +108,6 @@ const int program_birth_year = 2003; #define USE_ONEPASS_SUBTITLE_RENDER 1 -static unsigned sws_flags = SWS_BICUBIC; - typedef struct MyAVPacketList { AVPacket *pkt; int serial; @@ -150,6 +147,10 @@ typedef struct Clock { int *queue_serial; /* pointer to the current packet queue serial, used for obsolete clock detection */ } Clock; +typedef struct FrameData { + int64_t pkt_pos; +} FrameData; + /* Common struct for handling all types of decoded data and allocated render buffers. */ typedef struct Frame { AVFrame *frame; @@ -250,9 +251,7 @@ typedef struct VideoState { int audio_volume; int muted; struct AudioParams audio_src; -#if CONFIG_AVFILTER struct AudioParams audio_filter_src; -#endif struct AudioParams audio_tgt; struct SwrContext *swr_ctx; int frame_drops_early; @@ -264,9 +263,11 @@ typedef struct VideoState { int16_t sample_array[SAMPLE_ARRAY_SIZE]; int sample_array_index; int last_i_start; - RDFTContext *rdft; + AVTXContext *rdft; + av_tx_fn rdft_fn; int rdft_bits; - FFTSample *rdft_data; + float *real_data; + AVComplexFloat *rdft_data; int xpos; double last_vis_time; SDL_Texture *vis_texture; @@ -284,7 +285,6 @@ typedef struct VideoState { AVStream *video_st; PacketQueue videoq; double max_frame_duration; // maximum duration of a frame - above this, we consider the jump a timestamp discontinuity - struct SwsContext *img_convert_ctx; struct SwsContext *sub_convert_ctx; int eof; @@ -292,14 +292,12 @@ typedef struct VideoState { int width, height, xleft, ytop; int step; -#if CONFIG_AVFILTER int vfilter_idx; AVFilterContext *in_video_filter; // the first filter in the video chain AVFilterContext *out_video_filter; // the last filter in the video chain AVFilterContext *in_audio_filter; // the first filter in the audio chain AVFilterContext *out_audio_filter; // the last filter in the audio chain AVFilterGraph *agraph; // audio filter graph -#endif int last_video_stream, last_audio_stream, last_subtitle_stream; @@ -347,14 +345,15 @@ static const char *video_codec_name; double rdftspeed = 0.02; static int64_t cursor_last_shown; static int cursor_hidden = 0; -#if CONFIG_AVFILTER static const char **vfilters_list = NULL; static int nb_vfilters = 0; static char *afilters = NULL; -#endif static int autorotate = 1; static int find_stream_info = 1; static int filter_nbthreads = 0; +static int enable_vulkan = 0; +static char *vulkan_params = NULL; +static const char *hwaccel = NULL; /* current context */ static int is_full_screen; @@ -367,6 +366,8 @@ static SDL_Renderer *renderer; static SDL_RendererInfo renderer_info = {0}; static SDL_AudioDeviceID audio_dev; +static VkRenderer *vk_renderer; + static const struct TextureFormatEntry { enum AVPixelFormat format; int texture_fmt; @@ -393,14 +394,18 @@ static const struct TextureFormatEntry { { AV_PIX_FMT_NONE, SDL_PIXELFORMAT_UNKNOWN }, }; -#if CONFIG_AVFILTER static int opt_add_vfilter(void *optctx, const char *opt, const char *arg) { - GROW_ARRAY(vfilters_list, nb_vfilters); - vfilters_list[nb_vfilters - 1] = arg; + int ret = GROW_ARRAY(vfilters_list, nb_vfilters); + if (ret < 0) + return ret; + + vfilters_list[nb_vfilters - 1] = av_strdup(arg); + if (!vfilters_list[nb_vfilters - 1]) + return AVERROR(ENOMEM); + return 0; } -#endif static inline int cmp_audio_fmts(enum AVSampleFormat fmt1, int64_t channel_count1, @@ -653,6 +658,16 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) { } av_packet_unref(d->pkt); } else { + if (d->pkt->buf && !d->pkt->opaque_ref) { + FrameData *fd; + + d->pkt->opaque_ref = av_buffer_allocz(sizeof(*fd)); + if (!d->pkt->opaque_ref) + return AVERROR(ENOMEM); + fd = (FrameData*)d->pkt->opaque_ref->data; + fd->pkt_pos = d->pkt->pos; + } + if (avcodec_send_packet(d->avctx, d->pkt) == AVERROR(EAGAIN)) { av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n"); d->packet_pending = 1; @@ -695,7 +710,7 @@ static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int return 0; } -static void frame_queue_destory(FrameQueue *f) +static void frame_queue_destroy(FrameQueue *f) { int i; for (i = 0; i < f->max_size; i++) { @@ -891,7 +906,8 @@ static void get_sdl_pix_fmt_and_blendmode(int format, Uint32 *sdl_pix_fmt, SDL_B } } -static int upload_texture(SDL_Texture **tex, AVFrame *frame, struct SwsContext **img_convert_ctx) { +static int upload_texture(SDL_Texture **tex, AVFrame *frame) +{ int ret = 0; Uint32 sdl_pix_fmt; SDL_BlendMode sdl_blendmode; @@ -899,24 +915,6 @@ static int upload_texture(SDL_Texture **tex, AVFrame *frame, struct SwsContext * if (realloc_texture(tex, sdl_pix_fmt == SDL_PIXELFORMAT_UNKNOWN ? SDL_PIXELFORMAT_ARGB8888 : sdl_pix_fmt, frame->width, frame->height, sdl_blendmode, 0) < 0) return -1; switch (sdl_pix_fmt) { - case SDL_PIXELFORMAT_UNKNOWN: - /* This should only happen if we are not using avfilter... */ - *img_convert_ctx = sws_getCachedContext(*img_convert_ctx, - frame->width, frame->height, frame->format, frame->width, frame->height, - AV_PIX_FMT_BGRA, sws_flags, NULL, NULL, NULL); - if (*img_convert_ctx != NULL) { - uint8_t *pixels[4]; - int pitch[4]; - if (!SDL_LockTexture(*tex, NULL, (void **)pixels, pitch)) { - sws_scale(*img_convert_ctx, (const uint8_t * const *)frame->data, frame->linesize, - 0, frame->height, pixels, pitch); - SDL_UnlockTexture(*tex); - } - } else { - av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n"); - ret = -1; - } - break; case SDL_PIXELFORMAT_IYUV: if (frame->linesize[0] > 0 && frame->linesize[1] > 0 && frame->linesize[2] > 0) { ret = SDL_UpdateYUVTexture(*tex, NULL, frame->data[0], frame->linesize[0], @@ -942,6 +940,13 @@ static int upload_texture(SDL_Texture **tex, AVFrame *frame, struct SwsContext * return ret; } +static enum AVColorSpace sdl_supported_color_spaces[] = { + AVCOL_SPC_BT709, + AVCOL_SPC_BT470BG, + AVCOL_SPC_SMPTE170M, + AVCOL_SPC_UNSPECIFIED, +}; + static void set_sdl_yuv_conversion_mode(AVFrame *frame) { #if SDL_VERSION_ATLEAST(2,0,8) @@ -965,6 +970,11 @@ static void video_image_display(VideoState *is) SDL_Rect rect; vp = frame_queue_peek_last(&is->pictq); + if (vk_renderer) { + vk_renderer_display(vk_renderer, vp->frame); + return; + } + if (is->subtitle_st) { if (frame_queue_nb_remaining(&is->subpq) > 0) { sp = frame_queue_peek(&is->subpq); @@ -1014,7 +1024,7 @@ static void video_image_display(VideoState *is) set_sdl_yuv_conversion_mode(vp->frame); if (!vp->uploaded) { - if (upload_texture(&is->vid_texture, vp->frame, &is->img_convert_ctx) < 0) { + if (upload_texture(&is->vid_texture, vp->frame) < 0) { set_sdl_yuv_conversion_mode(NULL); return; } @@ -1133,6 +1143,7 @@ static void video_audio_display(VideoState *s) fill_rectangle(s->xleft, y, s->width, 1); } } else { + int err = 0; if (realloc_texture(&s->vis_texture, SDL_PIXELFORMAT_ARGB8888, s->width, s->height, SDL_BLENDMODE_NONE, 1) < 0) return; @@ -1140,31 +1151,39 @@ static void video_audio_display(VideoState *s) s->xpos = 0; nb_display_channels= FFMIN(nb_display_channels, 2); if (rdft_bits != s->rdft_bits) { - av_rdft_end(s->rdft); - av_free(s->rdft_data); - s->rdft = av_rdft_init(rdft_bits, DFT_R2C); + const float rdft_scale = 1.0; + av_tx_uninit(&s->rdft); + av_freep(&s->real_data); + av_freep(&s->rdft_data); s->rdft_bits = rdft_bits; - s->rdft_data = av_malloc_array(nb_freq, 4 *sizeof(*s->rdft_data)); + s->real_data = av_malloc_array(nb_freq, 4 *sizeof(*s->real_data)); + s->rdft_data = av_malloc_array(nb_freq + 1, 2 *sizeof(*s->rdft_data)); + err = av_tx_init(&s->rdft, &s->rdft_fn, AV_TX_FLOAT_RDFT, + 0, 1 << rdft_bits, &rdft_scale, 0); } - if (!s->rdft || !s->rdft_data){ + if (err < 0 || !s->rdft_data) { av_log(NULL, AV_LOG_ERROR, "Failed to allocate buffers for RDFT, switching to waves display\n"); s->show_mode = SHOW_MODE_WAVES; } else { - FFTSample *data[2]; + float *data_in[2]; + AVComplexFloat *data[2]; SDL_Rect rect = {.x = s->xpos, .y = 0, .w = 1, .h = s->height}; uint32_t *pixels; int pitch; for (ch = 0; ch < nb_display_channels; ch++) { - data[ch] = s->rdft_data + 2 * nb_freq * ch; + data_in[ch] = s->real_data + 2 * nb_freq * ch; + data[ch] = s->rdft_data + nb_freq * ch; i = i_start + ch; for (x = 0; x < 2 * nb_freq; x++) { double w = (x-nb_freq) * (1.0 / nb_freq); - data[ch][x] = s->sample_array[i] * (1.0 - w * w); + data_in[ch][x] = s->sample_array[i] * (1.0 - w * w); i += channels; if (i >= SAMPLE_ARRAY_SIZE) i -= SAMPLE_ARRAY_SIZE; } - av_rdft_calc(s->rdft, data[ch]); + s->rdft_fn(s->rdft, data[ch], data_in[ch], sizeof(float)); + data[ch][0].im = data[ch][nb_freq].re; + data[ch][nb_freq].re = 0; } /* Least efficient way to do this, we should of course * directly access it but it is more than fast enough. */ @@ -1173,8 +1192,8 @@ static void video_audio_display(VideoState *s) pixels += pitch * s->height; for (y = 0; y < s->height; y++) { double w = 1 / sqrt(nb_freq); - int a = sqrt(w * sqrt(data[0][2 * y + 0] * data[0][2 * y + 0] + data[0][2 * y + 1] * data[0][2 * y + 1])); - int b = (nb_display_channels == 2 ) ? sqrt(w * hypot(data[1][2 * y + 0], data[1][2 * y + 1])) + int a = sqrt(w * sqrt(data[0][y].re * data[0][y].re + data[0][y].im * data[0][y].im)); + int b = (nb_display_channels == 2 ) ? sqrt(w * hypot(data[1][y].re, data[1][y].im)) : a; a = FFMIN(a, 255); b = FFMIN(b, 255); @@ -1210,7 +1229,8 @@ static void stream_component_close(VideoState *is, int stream_index) is->audio_buf = NULL; if (is->rdft) { - av_rdft_end(is->rdft); + av_tx_uninit(&is->rdft); + av_freep(&is->real_data); av_freep(&is->rdft_data); is->rdft = NULL; is->rdft_bits = 0; @@ -1268,11 +1288,10 @@ static void stream_close(VideoState *is) packet_queue_destroy(&is->subtitleq); /* free all pictures */ - frame_queue_destory(&is->pictq); - frame_queue_destory(&is->sampq); - frame_queue_destory(&is->subpq); + frame_queue_destroy(&is->pictq); + frame_queue_destroy(&is->sampq); + frame_queue_destroy(&is->subpq); SDL_DestroyCond(is->continue_read_thread); - sws_freeContext(is->img_convert_ctx); sws_freeContext(is->sub_convert_ctx); av_free(is->filename); if (is->vis_texture) @@ -1291,12 +1310,18 @@ static void do_exit(VideoState *is) } if (renderer) SDL_DestroyRenderer(renderer); + if (vk_renderer) + vk_renderer_destroy(vk_renderer); if (window) SDL_DestroyWindow(window); uninit_opts(); -#if CONFIG_AVFILTER + for (int i = 0; i < nb_vfilters; i++) + av_freep(&vfilters_list[i]); av_freep(&vfilters_list); -#endif + av_freep(&video_codec_name); + av_freep(&audio_codec_name); + av_freep(&subtitle_codec_name); + av_freep(&input_filename); avformat_network_deinit(); if (show_status) printf("\n"); @@ -1553,7 +1578,8 @@ static double vp_duration(VideoState *is, Frame *vp, Frame *nextvp) { } } -static void update_video_pts(VideoState *is, double pts, int64_t pos, int serial) { +static void update_video_pts(VideoState *is, double pts, int serial) +{ /* update current video pts */ set_clock(&is->vidclk, pts, serial); sync_clock_to_slave(&is->extclk, &is->vidclk); @@ -1618,7 +1644,7 @@ static void video_refresh(void *opaque, double *remaining_time) SDL_LockMutex(is->pictq.mutex); if (!isnan(vp->pts)) - update_video_pts(is, vp->pts, vp->pos, vp->serial); + update_video_pts(is, vp->pts, vp->serial); SDL_UnlockMutex(is->pictq.mutex); if (frame_queue_nb_remaining(&is->pictq) > 1) { @@ -1705,16 +1731,14 @@ static void video_refresh(void *opaque, double *remaining_time) av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); av_bprintf(&buf, - "%7.2f %s:%7.3f fd=%4d aq=%5dKB vq=%5dKB sq=%5dB f=%"PRId64"/%"PRId64" \r", + "%7.2f %s:%7.3f fd=%4d aq=%5dKB vq=%5dKB sq=%5dB \r", get_master_clock(is), (is->audio_st && is->video_st) ? "A-V" : (is->video_st ? "M-V" : (is->audio_st ? "M-A" : " ")), av_diff, is->frame_drops_early + is->frame_drops_late, aqsize / 1024, vqsize / 1024, - sqsize, - is->video_st ? is->viddec.avctx->pts_correction_num_faulty_dts : 0, - is->video_st ? is->viddec.avctx->pts_correction_num_faulty_pts : 0); + sqsize); if (show_status == 1 && AV_LOG_INFO > av_log_get_level()) fprintf(stderr, "%s", buf.str); @@ -1793,7 +1817,6 @@ static int get_video_frame(VideoState *is, AVFrame *frame) return got_picture; } -#if CONFIG_AVFILTER static int configure_filtergraph(AVFilterGraph *graph, const char *filtergraph, AVFilterContext *source_ctx, AVFilterContext *sink_ctx) { @@ -1849,6 +1872,10 @@ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const c const AVDictionaryEntry *e = NULL; int nb_pix_fmts = 0; int i, j; + AVBufferSrcParameters *par = av_buffersrc_parameters_alloc(); + + if (!par) + return AVERROR(ENOMEM); for (i = 0; i < renderer_info.num_texture_formats; i++) { for (j = 0; j < FF_ARRAY_ELEMS(sdl_texture_format_map) - 1; j++) { @@ -1872,10 +1899,12 @@ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const c graph->scale_sws_opts = av_strdup(sws_flags_str); snprintf(buffersrc_args, sizeof(buffersrc_args), - "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", + "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d:" + "colorspace=%d:range=%d", frame->width, frame->height, frame->format, is->video_st->time_base.num, is->video_st->time_base.den, - codecpar->sample_aspect_ratio.num, FFMAX(codecpar->sample_aspect_ratio.den, 1)); + codecpar->sample_aspect_ratio.num, FFMAX(codecpar->sample_aspect_ratio.den, 1), + frame->colorspace, frame->color_range); if (fr.num && fr.den) av_strlcatf(buffersrc_args, sizeof(buffersrc_args), ":frame_rate=%d/%d", fr.num, fr.den); @@ -1884,6 +1913,10 @@ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const c "ffplay_buffer", buffersrc_args, NULL, graph)) < 0) goto fail; + par->hw_frames_ctx = frame->hw_frames_ctx; + ret = av_buffersrc_parameters_set(filt_src, par); + if (ret < 0) + goto fail; ret = avfilter_graph_create_filter(&filt_out, avfilter_get_by_name("buffersink"), @@ -1893,6 +1926,9 @@ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const c if ((ret = av_opt_set_int_list(filt_out, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN)) < 0) goto fail; + if (!vk_renderer && + (ret = av_opt_set_int_list(filt_out, "color_spaces", sdl_supported_color_spaces, AVCOL_SPC_UNSPECIFIED, AV_OPT_SEARCH_CHILDREN)) < 0) + goto fail; last_filter = filt_out; @@ -1920,8 +1956,13 @@ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const c AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX); if (sd) displaymatrix = (int32_t *)sd->data; - if (!displaymatrix) - displaymatrix = (int32_t *)av_stream_get_side_data(is->video_st, AV_PKT_DATA_DISPLAYMATRIX, NULL); + if (!displaymatrix) { + const AVPacketSideData *psd = av_packet_side_data_get(is->video_st->codecpar->coded_side_data, + is->video_st->codecpar->nb_coded_side_data, + AV_PKT_DATA_DISPLAYMATRIX); + if (psd) + displaymatrix = (int32_t *)psd->data; + } theta = get_rotation(displaymatrix); if (fabs(theta - 90) < 1.0) { @@ -1945,6 +1986,7 @@ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const c is->out_video_filter = filt_out; fail: + av_freep(&par); return ret; } @@ -1998,6 +2040,8 @@ static int configure_audio_filters(VideoState *is, const char *afilters, int for goto end; if (force_output_format) { + av_bprint_clear(&bp); + av_channel_layout_describe_bprint(&is->audio_tgt.ch_layout, &bp); sample_rates [0] = is->audio_tgt.freq; if ((ret = av_opt_set_int(filt_asink, "all_channel_counts", 0, AV_OPT_SEARCH_CHILDREN)) < 0) goto end; @@ -2021,17 +2065,14 @@ static int configure_audio_filters(VideoState *is, const char *afilters, int for return ret; } -#endif /* CONFIG_AVFILTER */ static int audio_thread(void *arg) { VideoState *is = arg; AVFrame *frame = av_frame_alloc(); Frame *af; -#if CONFIG_AVFILTER int last_serial = -1; int reconfigure; -#endif int got_frame = 0; AVRational tb; int ret = 0; @@ -2046,7 +2087,6 @@ static int audio_thread(void *arg) if (got_frame) { tb = (AVRational){1, frame->sample_rate}; -#if CONFIG_AVFILTER reconfigure = cmp_audio_fmts(is->audio_filter_src.fmt, is->audio_filter_src.ch_layout.nb_channels, frame->format, frame->ch_layout.nb_channels) || @@ -2078,32 +2118,28 @@ static int audio_thread(void *arg) goto the_end; while ((ret = av_buffersink_get_frame_flags(is->out_audio_filter, frame, 0)) >= 0) { + FrameData *fd = frame->opaque_ref ? (FrameData*)frame->opaque_ref->data : NULL; tb = av_buffersink_get_time_base(is->out_audio_filter); -#endif if (!(af = frame_queue_peek_writable(&is->sampq))) goto the_end; af->pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb); - af->pos = frame->pkt_pos; + af->pos = fd ? fd->pkt_pos : -1; af->serial = is->auddec.pkt_serial; af->duration = av_q2d((AVRational){frame->nb_samples, frame->sample_rate}); av_frame_move_ref(af->frame, frame); frame_queue_push(&is->sampq); -#if CONFIG_AVFILTER if (is->audioq.serial != is->auddec.pkt_serial) break; } if (ret == AVERROR_EOF) is->auddec.finished = is->auddec.pkt_serial; -#endif } } while (ret >= 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF); the_end: -#if CONFIG_AVFILTER avfilter_graph_free(&is->agraph); -#endif av_frame_free(&frame); return ret; } @@ -2129,7 +2165,6 @@ static int video_thread(void *arg) AVRational tb = is->video_st->time_base; AVRational frame_rate = av_guess_frame_rate(is->ic, is->video_st, NULL); -#if CONFIG_AVFILTER AVFilterGraph *graph = NULL; AVFilterContext *filt_out = NULL, *filt_in = NULL; int last_w = 0; @@ -2137,7 +2172,6 @@ static int video_thread(void *arg) enum AVPixelFormat last_format = -2; int last_serial = -1; int last_vfilter_idx = 0; -#endif if (!frame) return AVERROR(ENOMEM); @@ -2149,7 +2183,6 @@ static int video_thread(void *arg) if (!ret) continue; -#if CONFIG_AVFILTER if ( last_w != frame->width || last_h != frame->height || last_format != frame->format @@ -2190,6 +2223,8 @@ static int video_thread(void *arg) goto the_end; while (ret >= 0) { + FrameData *fd; + is->frame_last_returned_time = av_gettime_relative() / 1000000.0; ret = av_buffersink_get_frame_flags(filt_out, frame, 0); @@ -2200,28 +2235,25 @@ static int video_thread(void *arg) break; } + fd = frame->opaque_ref ? (FrameData*)frame->opaque_ref->data : NULL; + is->frame_last_filter_delay = av_gettime_relative() / 1000000.0 - is->frame_last_returned_time; if (fabs(is->frame_last_filter_delay) > AV_NOSYNC_THRESHOLD / 10.0) is->frame_last_filter_delay = 0; tb = av_buffersink_get_time_base(filt_out); -#endif duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0); pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb); - ret = queue_picture(is, frame, pts, duration, frame->pkt_pos, is->viddec.pkt_serial); + ret = queue_picture(is, frame, pts, duration, fd ? fd->pkt_pos : -1, is->viddec.pkt_serial); av_frame_unref(frame); -#if CONFIG_AVFILTER if (is->videoq.serial != is->viddec.pkt_serial) break; } -#endif if (ret < 0) goto the_end; } the_end: -#if CONFIG_AVFILTER avfilter_graph_free(&graph); -#endif av_frame_free(&frame); return 0; } @@ -2362,12 +2394,13 @@ static int audio_decode_frame(VideoState *is) av_channel_layout_compare(&af->frame->ch_layout, &is->audio_src.ch_layout) || af->frame->sample_rate != is->audio_src.freq || (wanted_nb_samples != af->frame->nb_samples && !is->swr_ctx)) { + int ret; swr_free(&is->swr_ctx); - swr_alloc_set_opts2(&is->swr_ctx, + ret = swr_alloc_set_opts2(&is->swr_ctx, &is->audio_tgt.ch_layout, is->audio_tgt.fmt, is->audio_tgt.freq, &af->frame->ch_layout, af->frame->format, af->frame->sample_rate, 0, NULL); - if (!is->swr_ctx || swr_init(is->swr_ctx) < 0) { + if (ret < 0 || swr_init(is->swr_ctx) < 0) { av_log(NULL, AV_LOG_ERROR, "Cannot create sample rate converter for conversion of %d Hz %s %d channels to %d Hz %s %d channels!\n", af->frame->sample_rate, av_get_sample_fmt_name(af->frame->format), af->frame->ch_layout.nb_channels, @@ -2557,6 +2590,37 @@ static int audio_open(void *opaque, AVChannelLayout *wanted_channel_layout, int return spec.size; } +static int create_hwaccel(AVBufferRef **device_ctx) +{ + enum AVHWDeviceType type; + int ret; + AVBufferRef *vk_dev; + + *device_ctx = NULL; + + if (!hwaccel) + return 0; + + type = av_hwdevice_find_type_by_name(hwaccel); + if (type == AV_HWDEVICE_TYPE_NONE) + return AVERROR(ENOTSUP); + + ret = vk_renderer_get_hw_dev(vk_renderer, &vk_dev); + if (ret < 0) + return ret; + + ret = av_hwdevice_ctx_create_derived(device_ctx, type, vk_dev, 0); + if (!ret) + return 0; + + if (ret != AVERROR(ENOSYS)) + return ret; + + av_log(NULL, AV_LOG_WARNING, "Derive %s from vulkan not supported.\n", hwaccel); + ret = av_hwdevice_ctx_create(device_ctx, type, NULL, NULL, 0); + return ret; +} + /* open a given stream. Return 0 if OK */ static int stream_component_open(VideoState *is, int stream_index) { @@ -2612,11 +2676,24 @@ static int stream_component_open(VideoState *is, int stream_index) if (fast) avctx->flags2 |= AV_CODEC_FLAG2_FAST; - opts = filter_codec_opts(codec_opts, avctx->codec_id, ic, ic->streams[stream_index], codec); + ret = filter_codec_opts(codec_opts, avctx->codec_id, ic, + ic->streams[stream_index], codec, &opts); + if (ret < 0) + goto fail; + if (!av_dict_get(opts, "threads", NULL, 0)) av_dict_set(&opts, "threads", "auto", 0); if (stream_lowres) av_dict_set_int(&opts, "lowres", stream_lowres, 0); + + av_dict_set(&opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY); + + if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) { + ret = create_hwaccel(&avctx->hw_device_ctx); + if (ret < 0) + goto fail; + } + if ((ret = avcodec_open2(avctx, codec, &opts)) < 0) { goto fail; } @@ -2630,7 +2707,6 @@ static int stream_component_open(VideoState *is, int stream_index) ic->streams[stream_index]->discard = AVDISCARD_DEFAULT; switch (avctx->codec_type) { case AVMEDIA_TYPE_AUDIO: -#if CONFIG_AVFILTER { AVFilterContext *sink; @@ -2647,12 +2723,6 @@ static int stream_component_open(VideoState *is, int stream_index) if (ret < 0) goto fail; } -#else - sample_rate = avctx->sample_rate; - ret = av_channel_layout_copy(&ch_layout, &avctx->ch_layout); - if (ret < 0) - goto fail; -#endif /* prepare audio output */ if ((ret = audio_open(is, &ch_layout, sample_rate, &is->audio_tgt)) < 0) @@ -2674,7 +2744,7 @@ static int stream_component_open(VideoState *is, int stream_index) if ((ret = decoder_init(&is->auddec, avctx, &is->audioq, is->continue_read_thread)) < 0) goto fail; - if ((is->ic->iformat->flags & (AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK)) && !is->ic->iformat->read_seek) { + if (is->ic->iformat->flags & AVFMT_NOTIMESTAMPS) { is->auddec.start_pts = is->audio_st->start_time; is->auddec.start_pts_tb = is->audio_st->time_base; } @@ -2805,12 +2875,18 @@ static int read_thread(void *arg) if (genpts) ic->flags |= AVFMT_FLAG_GENPTS; - av_format_inject_global_side_data(ic); - if (find_stream_info) { - AVDictionary **opts = setup_find_stream_info_opts(ic, codec_opts); + AVDictionary **opts; int orig_nb_streams = ic->nb_streams; + err = setup_find_stream_info_opts(ic, codec_opts, &opts); + if (err < 0) { + av_log(NULL, AV_LOG_ERROR, + "Error setting up avformat_find_stream_info() options\n"); + ret = err; + goto fail; + } + err = avformat_find_stream_info(ic, opts); for (i = 0; i < orig_nb_streams; i++) @@ -3326,7 +3402,6 @@ static void event_loop(VideoState *cur_stream) stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE); break; case SDLK_w: -#if CONFIG_AVFILTER if (cur_stream->show_mode == SHOW_MODE_VIDEO && cur_stream->vfilter_idx < nb_vfilters - 1) { if (++cur_stream->vfilter_idx >= nb_vfilters) cur_stream->vfilter_idx = 0; @@ -3334,9 +3409,6 @@ static void event_loop(VideoState *cur_stream) cur_stream->vfilter_idx = 0; toggle_audio_display(cur_stream); } -#else - toggle_audio_display(cur_stream); -#endif break; case SDLK_PAGEUP: if (cur_stream->ic->nb_chapters <= 1) { @@ -3456,6 +3528,8 @@ static void event_loop(VideoState *cur_stream) SDL_DestroyTexture(cur_stream->vis_texture); cur_stream->vis_texture = NULL; } + if (vk_renderer) + vk_renderer_resize(vk_renderer, screen_width, screen_height); case SDL_WINDOWEVENT_EXPOSED: cur_stream->force_refresh = 1; } @@ -3472,13 +3546,23 @@ static void event_loop(VideoState *cur_stream) static int opt_width(void *optctx, const char *opt, const char *arg) { - screen_width = parse_number_or_die(opt, arg, OPT_INT64, 1, INT_MAX); + double num; + int ret = parse_number(opt, arg, OPT_TYPE_INT64, 1, INT_MAX, &num); + if (ret < 0) + return ret; + + screen_width = num; return 0; } static int opt_height(void *optctx, const char *opt, const char *arg) { - screen_height = parse_number_or_die(opt, arg, OPT_INT64, 1, INT_MAX); + double num; + int ret = parse_number(opt, arg, OPT_TYPE_INT64, 1, INT_MAX, &num); + if (ret < 0) + return ret; + + screen_height = num; return 0; } @@ -3507,43 +3591,43 @@ static int opt_sync(void *optctx, const char *opt, const char *arg) return 0; } -static int opt_seek(void *optctx, const char *opt, const char *arg) -{ - start_time = parse_time_or_die(opt, arg, 1); - return 0; -} - -static int opt_duration(void *optctx, const char *opt, const char *arg) -{ - duration = parse_time_or_die(opt, arg, 1); - return 0; -} - static int opt_show_mode(void *optctx, const char *opt, const char *arg) { show_mode = !strcmp(arg, "video") ? SHOW_MODE_VIDEO : !strcmp(arg, "waves") ? SHOW_MODE_WAVES : - !strcmp(arg, "rdft" ) ? SHOW_MODE_RDFT : - parse_number_or_die(opt, arg, OPT_INT, 0, SHOW_MODE_NB-1); + !strcmp(arg, "rdft" ) ? SHOW_MODE_RDFT : SHOW_MODE_NONE; + + if (show_mode == SHOW_MODE_NONE) { + double num; + int ret = parse_number(opt, arg, OPT_TYPE_INT, 0, SHOW_MODE_NB-1, &num); + if (ret < 0) + return ret; + show_mode = num; + } return 0; } -static void opt_input_file(void *optctx, const char *filename) +static int opt_input_file(void *optctx, const char *filename) { if (input_filename) { av_log(NULL, AV_LOG_FATAL, "Argument '%s' provided as input filename, but '%s' was already specified.\n", filename, input_filename); - exit(1); + return AVERROR(EINVAL); } if (!strcmp(filename, "-")) filename = "fd:"; - input_filename = filename; + input_filename = av_strdup(filename); + if (!input_filename) + return AVERROR(ENOMEM); + + return 0; } static int opt_codec(void *optctx, const char *opt, const char *arg) { const char *spec = strchr(opt, ':'); + const char **name; if (!spec) { av_log(NULL, AV_LOG_ERROR, "No media specifier was specified in '%s' in option '%s'\n", @@ -3551,70 +3635,75 @@ static int opt_codec(void *optctx, const char *opt, const char *arg) return AVERROR(EINVAL); } spec++; + switch (spec[0]) { - case 'a' : audio_codec_name = arg; break; - case 's' : subtitle_codec_name = arg; break; - case 'v' : video_codec_name = arg; break; + case 'a' : name = &audio_codec_name; break; + case 's' : name = &subtitle_codec_name; break; + case 'v' : name = &video_codec_name; break; default: av_log(NULL, AV_LOG_ERROR, "Invalid media specifier '%s' in option '%s'\n", spec, opt); return AVERROR(EINVAL); } - return 0; + + av_freep(name); + *name = av_strdup(arg); + return *name ? 0 : AVERROR(ENOMEM); } static int dummy; static const OptionDef options[] = { CMDUTILS_COMMON_OPTIONS - { "x", HAS_ARG, { .func_arg = opt_width }, "force displayed width", "width" }, - { "y", HAS_ARG, { .func_arg = opt_height }, "force displayed height", "height" }, - { "fs", OPT_BOOL, { &is_full_screen }, "force full screen" }, - { "an", OPT_BOOL, { &audio_disable }, "disable audio" }, - { "vn", OPT_BOOL, { &video_disable }, "disable video" }, - { "sn", OPT_BOOL, { &subtitle_disable }, "disable subtitling" }, - { "ast", OPT_STRING | HAS_ARG | OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_AUDIO] }, "select desired audio stream", "stream_specifier" }, - { "vst", OPT_STRING | HAS_ARG | OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_VIDEO] }, "select desired video stream", "stream_specifier" }, - { "sst", OPT_STRING | HAS_ARG | OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_SUBTITLE] }, "select desired subtitle stream", "stream_specifier" }, - { "ss", HAS_ARG, { .func_arg = opt_seek }, "seek to a given position in seconds", "pos" }, - { "t", HAS_ARG, { .func_arg = opt_duration }, "play \"duration\" seconds of audio/video", "duration" }, - { "bytes", OPT_INT | HAS_ARG, { &seek_by_bytes }, "seek by bytes 0=off 1=on -1=auto", "val" }, - { "seek_interval", OPT_FLOAT | HAS_ARG, { &seek_interval }, "set seek interval for left/right keys, in seconds", "seconds" }, - { "nodisp", OPT_BOOL, { &display_disable }, "disable graphical display" }, - { "noborder", OPT_BOOL, { &borderless }, "borderless window" }, - { "alwaysontop", OPT_BOOL, { &alwaysontop }, "window always on top" }, - { "volume", OPT_INT | HAS_ARG, { &startup_volume}, "set startup volume 0=min 100=max", "volume" }, - { "f", HAS_ARG, { .func_arg = opt_format }, "force format", "fmt" }, - { "stats", OPT_BOOL | OPT_EXPERT, { &show_status }, "show status", "" }, - { "fast", OPT_BOOL | OPT_EXPERT, { &fast }, "non spec compliant optimizations", "" }, - { "genpts", OPT_BOOL | OPT_EXPERT, { &genpts }, "generate pts", "" }, - { "drp", OPT_INT | HAS_ARG | OPT_EXPERT, { &decoder_reorder_pts }, "let decoder reorder pts 0=off 1=on -1=auto", ""}, - { "lowres", OPT_INT | HAS_ARG | OPT_EXPERT, { &lowres }, "", "" }, - { "sync", HAS_ARG | OPT_EXPERT, { .func_arg = opt_sync }, "set audio-video sync. type (type=audio/video/ext)", "type" }, - { "autoexit", OPT_BOOL | OPT_EXPERT, { &autoexit }, "exit at the end", "" }, - { "exitonkeydown", OPT_BOOL | OPT_EXPERT, { &exit_on_keydown }, "exit on key down", "" }, - { "exitonmousedown", OPT_BOOL | OPT_EXPERT, { &exit_on_mousedown }, "exit on mouse down", "" }, - { "loop", OPT_INT | HAS_ARG | OPT_EXPERT, { &loop }, "set number of times the playback shall be looped", "loop count" }, - { "framedrop", OPT_BOOL | OPT_EXPERT, { &framedrop }, "drop frames when cpu is too slow", "" }, - { "infbuf", OPT_BOOL | OPT_EXPERT, { &infinite_buffer }, "don't limit the input buffer size (useful with realtime streams)", "" }, - { "window_title", OPT_STRING | HAS_ARG, { &window_title }, "set window title", "window title" }, - { "left", OPT_INT | HAS_ARG | OPT_EXPERT, { &screen_left }, "set the x position for the left of the window", "x pos" }, - { "top", OPT_INT | HAS_ARG | OPT_EXPERT, { &screen_top }, "set the y position for the top of the window", "y pos" }, -#if CONFIG_AVFILTER - { "vf", OPT_EXPERT | HAS_ARG, { .func_arg = opt_add_vfilter }, "set video filters", "filter_graph" }, - { "af", OPT_STRING | HAS_ARG, { &afilters }, "set audio filters", "filter_graph" }, -#endif - { "rdftspeed", OPT_INT | HAS_ARG| OPT_AUDIO | OPT_EXPERT, { &rdftspeed }, "rdft speed", "msecs" }, - { "showmode", HAS_ARG, { .func_arg = opt_show_mode}, "select show mode (0 = video, 1 = waves, 2 = RDFT)", "mode" }, - { "i", OPT_BOOL, { &dummy}, "read specified file", "input_file"}, - { "codec", HAS_ARG, { .func_arg = opt_codec}, "force decoder", "decoder_name" }, - { "acodec", HAS_ARG | OPT_STRING | OPT_EXPERT, { &audio_codec_name }, "force audio decoder", "decoder_name" }, - { "scodec", HAS_ARG | OPT_STRING | OPT_EXPERT, { &subtitle_codec_name }, "force subtitle decoder", "decoder_name" }, - { "vcodec", HAS_ARG | OPT_STRING | OPT_EXPERT, { &video_codec_name }, "force video decoder", "decoder_name" }, - { "autorotate", OPT_BOOL, { &autorotate }, "automatically rotate video", "" }, - { "find_stream_info", OPT_BOOL | OPT_INPUT | OPT_EXPERT, { &find_stream_info }, + { "x", OPT_TYPE_FUNC, OPT_FUNC_ARG, { .func_arg = opt_width }, "force displayed width", "width" }, + { "y", OPT_TYPE_FUNC, OPT_FUNC_ARG, { .func_arg = opt_height }, "force displayed height", "height" }, + { "fs", OPT_TYPE_BOOL, 0, { &is_full_screen }, "force full screen" }, + { "an", OPT_TYPE_BOOL, 0, { &audio_disable }, "disable audio" }, + { "vn", OPT_TYPE_BOOL, 0, { &video_disable }, "disable video" }, + { "sn", OPT_TYPE_BOOL, 0, { &subtitle_disable }, "disable subtitling" }, + { "ast", OPT_TYPE_STRING, OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_AUDIO] }, "select desired audio stream", "stream_specifier" }, + { "vst", OPT_TYPE_STRING, OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_VIDEO] }, "select desired video stream", "stream_specifier" }, + { "sst", OPT_TYPE_STRING, OPT_EXPERT, { &wanted_stream_spec[AVMEDIA_TYPE_SUBTITLE] }, "select desired subtitle stream", "stream_specifier" }, + { "ss", OPT_TYPE_TIME, 0, { &start_time }, "seek to a given position in seconds", "pos" }, + { "t", OPT_TYPE_TIME, 0, { &duration }, "play \"duration\" seconds of audio/video", "duration" }, + { "bytes", OPT_TYPE_INT, 0, { &seek_by_bytes }, "seek by bytes 0=off 1=on -1=auto", "val" }, + { "seek_interval", OPT_TYPE_FLOAT, 0, { &seek_interval }, "set seek interval for left/right keys, in seconds", "seconds" }, + { "nodisp", OPT_TYPE_BOOL, 0, { &display_disable }, "disable graphical display" }, + { "noborder", OPT_TYPE_BOOL, 0, { &borderless }, "borderless window" }, + { "alwaysontop", OPT_TYPE_BOOL, 0, { &alwaysontop }, "window always on top" }, + { "volume", OPT_TYPE_INT, 0, { &startup_volume}, "set startup volume 0=min 100=max", "volume" }, + { "f", OPT_TYPE_FUNC, OPT_FUNC_ARG, { .func_arg = opt_format }, "force format", "fmt" }, + { "stats", OPT_TYPE_BOOL, OPT_EXPERT, { &show_status }, "show status", "" }, + { "fast", OPT_TYPE_BOOL, OPT_EXPERT, { &fast }, "non spec compliant optimizations", "" }, + { "genpts", OPT_TYPE_BOOL, OPT_EXPERT, { &genpts }, "generate pts", "" }, + { "drp", OPT_TYPE_INT, OPT_EXPERT, { &decoder_reorder_pts }, "let decoder reorder pts 0=off 1=on -1=auto", ""}, + { "lowres", OPT_TYPE_INT, OPT_EXPERT, { &lowres }, "", "" }, + { "sync", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, { .func_arg = opt_sync }, "set audio-video sync. type (type=audio/video/ext)", "type" }, + { "autoexit", OPT_TYPE_BOOL, OPT_EXPERT, { &autoexit }, "exit at the end", "" }, + { "exitonkeydown", OPT_TYPE_BOOL, OPT_EXPERT, { &exit_on_keydown }, "exit on key down", "" }, + { "exitonmousedown", OPT_TYPE_BOOL, OPT_EXPERT, { &exit_on_mousedown }, "exit on mouse down", "" }, + { "loop", OPT_TYPE_INT, OPT_EXPERT, { &loop }, "set number of times the playback shall be looped", "loop count" }, + { "framedrop", OPT_TYPE_BOOL, OPT_EXPERT, { &framedrop }, "drop frames when cpu is too slow", "" }, + { "infbuf", OPT_TYPE_BOOL, OPT_EXPERT, { &infinite_buffer }, "don't limit the input buffer size (useful with realtime streams)", "" }, + { "window_title", OPT_TYPE_STRING, 0, { &window_title }, "set window title", "window title" }, + { "left", OPT_TYPE_INT, OPT_EXPERT, { &screen_left }, "set the x position for the left of the window", "x pos" }, + { "top", OPT_TYPE_INT, OPT_EXPERT, { &screen_top }, "set the y position for the top of the window", "y pos" }, + { "vf", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, { .func_arg = opt_add_vfilter }, "set video filters", "filter_graph" }, + { "af", OPT_TYPE_STRING, 0, { &afilters }, "set audio filters", "filter_graph" }, + { "rdftspeed", OPT_TYPE_INT, OPT_AUDIO | OPT_EXPERT, { &rdftspeed }, "rdft speed", "msecs" }, + { "showmode", OPT_TYPE_FUNC, OPT_FUNC_ARG, { .func_arg = opt_show_mode}, "select show mode (0 = video, 1 = waves, 2 = RDFT)", "mode" }, + { "i", OPT_TYPE_BOOL, 0, { &dummy}, "read specified file", "input_file"}, + { "codec", OPT_TYPE_FUNC, OPT_FUNC_ARG, { .func_arg = opt_codec}, "force decoder", "decoder_name" }, + { "acodec", OPT_TYPE_STRING, OPT_EXPERT, { &audio_codec_name }, "force audio decoder", "decoder_name" }, + { "scodec", OPT_TYPE_STRING, OPT_EXPERT, { &subtitle_codec_name }, "force subtitle decoder", "decoder_name" }, + { "vcodec", OPT_TYPE_STRING, OPT_EXPERT, { &video_codec_name }, "force video decoder", "decoder_name" }, + { "autorotate", OPT_TYPE_BOOL, 0, { &autorotate }, "automatically rotate video", "" }, + { "find_stream_info", OPT_TYPE_BOOL, OPT_INPUT | OPT_EXPERT, { &find_stream_info }, "read and decode the streams to fill missing information with heuristics" }, - { "filter_threads", HAS_ARG | OPT_INT | OPT_EXPERT, { &filter_nbthreads }, "number of filter threads per graph" }, + { "filter_threads", OPT_TYPE_INT, OPT_EXPERT, { &filter_nbthreads }, "number of filter threads per graph" }, + { "enable_vulkan", OPT_TYPE_BOOL, 0, { &enable_vulkan }, "enable vulkan renderer" }, + { "vulkan_params", OPT_TYPE_STRING, OPT_EXPERT, { &vulkan_params }, "vulkan configuration using a list of key=value pairs separated by ':'" }, + { "hwaccel", OPT_TYPE_STRING, OPT_EXPERT, { &hwaccel }, "use HW accelerated decoding" }, { NULL, }, }; @@ -3629,16 +3718,12 @@ void show_help_default(const char *opt, const char *arg) { av_log_set_callback(log_callback_help); show_usage(); - show_help_options(options, "Main options:", 0, OPT_EXPERT, 0); - show_help_options(options, "Advanced options:", OPT_EXPERT, 0, 0); + show_help_options(options, "Main options:", 0, OPT_EXPERT); + show_help_options(options, "Advanced options:", OPT_EXPERT, 0); printf("\n"); show_help_children(avcodec_get_class(), AV_OPT_FLAG_DECODING_PARAM); show_help_children(avformat_get_class(), AV_OPT_FLAG_DECODING_PARAM); -#if !CONFIG_AVFILTER - show_help_children(sws_get_class(), AV_OPT_FLAG_ENCODING_PARAM); -#else show_help_children(avfilter_get_class(), AV_OPT_FLAG_FILTERING_PARAM); -#endif printf("\nWhile playing:\n" "q, ESC quit\n" "f toggle full screen\n" @@ -3663,7 +3748,7 @@ void show_help_default(const char *opt, const char *arg) /* Called from the main */ int main(int argc, char **argv) { - int flags; + int flags, ret; VideoState *is; init_dynload(); @@ -3682,7 +3767,9 @@ int main(int argc, char **argv) show_banner(argc, argv, options); - parse_options(NULL, argc, argv, options, opt_input_file); + ret = parse_options(NULL, argc, argv, options, opt_input_file); + if (ret < 0) + exit(ret == AVERROR_EXIT ? 0 : 1); if (!input_filename) { show_usage(); @@ -3731,9 +3818,45 @@ int main(int argc, char **argv) #ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); #endif + if (hwaccel && !enable_vulkan) { + av_log(NULL, AV_LOG_INFO, "Enable vulkan renderer to support hwaccel %s\n", hwaccel); + enable_vulkan = 1; + } + if (enable_vulkan) { + vk_renderer = vk_get_renderer(); + if (vk_renderer) { +#if SDL_VERSION_ATLEAST(2, 0, 6) + flags |= SDL_WINDOW_VULKAN; +#endif + } else { + av_log(NULL, AV_LOG_WARNING, "Doesn't support vulkan renderer, fallback to SDL renderer\n"); + enable_vulkan = 0; + } + } window = SDL_CreateWindow(program_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, default_width, default_height, flags); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); - if (window) { + if (!window) { + av_log(NULL, AV_LOG_FATAL, "Failed to create window: %s", SDL_GetError()); + do_exit(NULL); + } + + if (vk_renderer) { + AVDictionary *dict = NULL; + + if (vulkan_params) { + int ret = av_dict_parse_string(&dict, vulkan_params, "=", ":", 0); + if (ret < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to parse, %s\n", vulkan_params); + do_exit(NULL); + } + } + ret = vk_renderer_create(vk_renderer, window, dict); + av_dict_free(&dict); + if (ret < 0) { + av_log(NULL, AV_LOG_FATAL, "Failed to create vulkan renderer, %s\n", av_err2str(ret)); + do_exit(NULL); + } + } else { renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if (!renderer) { av_log(NULL, AV_LOG_WARNING, "Failed to initialize a hardware accelerated renderer: %s\n", SDL_GetError()); @@ -3743,10 +3866,10 @@ int main(int argc, char **argv) if (!SDL_GetRendererInfo(renderer, &renderer_info)) av_log(NULL, AV_LOG_VERBOSE, "Initialized %s renderer.\n", renderer_info.name); } - } - if (!window || !renderer || !renderer_info.num_texture_formats) { - av_log(NULL, AV_LOG_FATAL, "Failed to create window or renderer: %s", SDL_GetError()); - do_exit(NULL); + if (!renderer || !renderer_info.num_texture_formats) { + av_log(NULL, AV_LOG_FATAL, "Failed to create window or renderer: %s", SDL_GetError()); + do_exit(NULL); + } } } diff --git a/fftools/ffplay_renderer.c b/fftools/ffplay_renderer.c new file mode 100644 index 00000000000..fbb68b2376c --- /dev/null +++ b/fftools/ffplay_renderer.c @@ -0,0 +1,835 @@ +/* + * 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 + */ + +#define VK_NO_PROTOTYPES +#define VK_ENABLE_BETA_EXTENSIONS + +#include "config.h" +#include "ffplay_renderer.h" + +#if (SDL_VERSION_ATLEAST(2, 0, 6) && CONFIG_LIBPLACEBO) +/* Get PL_API_VER */ +#include +#define HAVE_VULKAN_RENDERER (PL_API_VER >= 278) +#else +#define HAVE_VULKAN_RENDERER 0 +#endif + +#if HAVE_VULKAN_RENDERER + +#if defined(_WIN32) && !defined(VK_USE_PLATFORM_WIN32_KHR) +#define VK_USE_PLATFORM_WIN32_KHR +#endif + +#include +#include +#include +#include + +#include "libavutil/bprint.h" + +#endif + +struct VkRenderer { + const AVClass *class; + + int (*create)(VkRenderer *renderer, SDL_Window *window, AVDictionary *dict); + + int (*get_hw_dev)(VkRenderer *renderer, AVBufferRef **dev); + + int (*display)(VkRenderer *renderer, AVFrame *frame); + + int (*resize)(VkRenderer *renderer, int width, int height); + + void (*destroy)(VkRenderer *renderer); +}; + +#if HAVE_VULKAN_RENDERER + +typedef struct RendererContext { + VkRenderer api; + + // Can be NULL when vulkan instance is created by avutil + pl_vk_inst placebo_instance; + pl_vulkan placebo_vulkan; + pl_swapchain swapchain; + VkSurfaceKHR vk_surface; + pl_renderer renderer; + pl_tex tex[4]; + + pl_log vk_log; + + AVBufferRef *hw_device_ref; + AVBufferRef *hw_frame_ref; + enum AVPixelFormat *transfer_formats; + AVHWFramesConstraints *constraints; + + PFN_vkGetInstanceProcAddr get_proc_addr; + // This field is a copy from pl_vk_inst->instance or hw_device_ref instance. + VkInstance inst; + + AVFrame *vk_frame; +} RendererContext; + +static void vk_log_cb(void *log_priv, enum pl_log_level level, + const char *msg) +{ + static const int level_map[] = { + AV_LOG_QUIET, + AV_LOG_FATAL, + AV_LOG_ERROR, + AV_LOG_WARNING, + AV_LOG_INFO, + AV_LOG_DEBUG, + AV_LOG_TRACE, + }; + + if (level > 0 && level < FF_ARRAY_ELEMS(level_map)) + av_log(log_priv, level_map[level], "%s\n", msg); +} + +// Should keep sync with optional_device_exts inside hwcontext_vulkan.c +static const char *optional_device_exts[] = { + /* Misc or required by other extensions */ + VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME, + VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, + VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, + VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME, + VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME, + VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME, + VK_KHR_COOPERATIVE_MATRIX_EXTENSION_NAME, + + /* Imports/exports */ + VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, + VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME, + VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME, + VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, + VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, +#ifdef _WIN32 + VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, + VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME, +#endif + + /* Video encoding/decoding */ + VK_KHR_VIDEO_QUEUE_EXTENSION_NAME, + VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME, + VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME, + VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME, + "VK_MESA_video_decode_av1", +}; + +static inline int enable_debug(const AVDictionary *opt) +{ + AVDictionaryEntry *entry = av_dict_get(opt, "debug", NULL, 0); + int debug = entry && strtol(entry->value, NULL, 10); + return debug; +} + +static void hwctx_lock_queue(void *priv, uint32_t qf, uint32_t qidx) +{ + AVHWDeviceContext *avhwctx = priv; + const AVVulkanDeviceContext *hwctx = avhwctx->hwctx; + hwctx->lock_queue(avhwctx, qf, qidx); +} + +static void hwctx_unlock_queue(void *priv, uint32_t qf, uint32_t qidx) +{ + AVHWDeviceContext *avhwctx = priv; + const AVVulkanDeviceContext *hwctx = avhwctx->hwctx; + hwctx->unlock_queue(avhwctx, qf, qidx); +} + +static int add_instance_extension(const char **ext, unsigned num_ext, + const AVDictionary *opt, + AVDictionary **dict) +{ + const char *inst_ext_key = "instance_extensions"; + AVDictionaryEntry *entry; + AVBPrint buf; + char *ext_list = NULL; + int ret; + + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); + for (int i = 0; i < num_ext; i++) { + if (i) + av_bprintf(&buf, "+%s", ext[i]); + else + av_bprintf(&buf, "%s", ext[i]); + } + + entry = av_dict_get(opt, inst_ext_key, NULL, 0); + if (entry && entry->value && entry->value[0]) { + if (num_ext) + av_bprintf(&buf, "+"); + av_bprintf(&buf, "%s", entry->value); + } + + ret = av_bprint_finalize(&buf, &ext_list); + if (ret < 0) + return ret; + return av_dict_set(dict, inst_ext_key, ext_list, AV_DICT_DONT_STRDUP_VAL); +} + +static int add_device_extension(const AVDictionary *opt, + AVDictionary **dict) +{ + const char *dev_ext_key = "device_extensions"; + AVDictionaryEntry *entry; + AVBPrint buf; + char *ext_list = NULL; + int ret; + + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); + av_bprintf(&buf, "%s", VK_KHR_SWAPCHAIN_EXTENSION_NAME); + for (int i = 0; i < pl_vulkan_num_recommended_extensions; i++) + av_bprintf(&buf, "+%s", pl_vulkan_recommended_extensions[i]); + + entry = av_dict_get(opt, dev_ext_key, NULL, 0); + if (entry && entry->value && entry->value[0]) + av_bprintf(&buf, "+%s", entry->value); + + ret = av_bprint_finalize(&buf, &ext_list); + if (ret < 0) + return ret; + return av_dict_set(dict, dev_ext_key, ext_list, AV_DICT_DONT_STRDUP_VAL); +} + +static const char *select_device(const AVDictionary *opt) +{ + const AVDictionaryEntry *entry; + + entry = av_dict_get(opt, "device", NULL, 0); + if (entry) + return entry->value; + return NULL; +} + +static int create_vk_by_hwcontext(VkRenderer *renderer, + const char **ext, unsigned num_ext, + const AVDictionary *opt) +{ + RendererContext *ctx = (RendererContext *) renderer; + AVHWDeviceContext *dev; + AVVulkanDeviceContext *hwctx; + AVDictionary *dict = NULL; + int ret; + + ret = add_instance_extension(ext, num_ext, opt, &dict); + if (ret < 0) + return ret; + ret = add_device_extension(opt, &dict); + if (ret) { + av_dict_free(&dict); + return ret; + } + + ret = av_hwdevice_ctx_create(&ctx->hw_device_ref, AV_HWDEVICE_TYPE_VULKAN, + select_device(opt), dict, 0); + av_dict_free(&dict); + if (ret < 0) + return ret; + + dev = (AVHWDeviceContext *) ctx->hw_device_ref->data; + hwctx = dev->hwctx; + + // There is no way to pass SDL GetInstanceProcAddr to hwdevice. + // Check the result and return error if they don't match. + if (hwctx->get_proc_addr != SDL_Vulkan_GetVkGetInstanceProcAddr()) { + av_log(renderer, AV_LOG_ERROR, + "hwdevice and SDL use different get_proc_addr. " + "Try -vulkan_params create_by_placebo=1\n"); + return AVERROR_PATCHWELCOME; + } + + ctx->get_proc_addr = hwctx->get_proc_addr; + ctx->inst = hwctx->inst; + ctx->placebo_vulkan = pl_vulkan_import(ctx->vk_log, + pl_vulkan_import_params( + .instance = hwctx->inst, + .get_proc_addr = hwctx->get_proc_addr, + .phys_device = hwctx->phys_dev, + .device = hwctx->act_dev, + .extensions = hwctx->enabled_dev_extensions, + .num_extensions = hwctx->nb_enabled_dev_extensions, + .features = &hwctx->device_features, + .lock_queue = hwctx_lock_queue, + .unlock_queue = hwctx_unlock_queue, + .queue_ctx = dev, + .queue_graphics = { + .index = hwctx->queue_family_index, + .count = hwctx->nb_graphics_queues, + }, + .queue_compute = { + .index = hwctx->queue_family_comp_index, + .count = hwctx->nb_comp_queues, + }, + .queue_transfer = { + .index = hwctx->queue_family_tx_index, + .count = hwctx->nb_tx_queues, + }, + )); + if (!ctx->placebo_vulkan) + return AVERROR_EXTERNAL; + + return 0; +} + +static void placebo_lock_queue(struct AVHWDeviceContext *dev_ctx, + uint32_t queue_family, uint32_t index) +{ + RendererContext *ctx = dev_ctx->user_opaque; + pl_vulkan vk = ctx->placebo_vulkan; + vk->lock_queue(vk, queue_family, index); +} + +static void placebo_unlock_queue(struct AVHWDeviceContext *dev_ctx, + uint32_t queue_family, + uint32_t index) +{ + RendererContext *ctx = dev_ctx->user_opaque; + pl_vulkan vk = ctx->placebo_vulkan; + vk->unlock_queue(vk, queue_family, index); +} + +static int get_decode_queue(VkRenderer *renderer, int *index, int *count) +{ + RendererContext *ctx = (RendererContext *) renderer; + VkQueueFamilyProperties *queue_family_prop = NULL; + uint32_t num_queue_family_prop = 0; + PFN_vkGetPhysicalDeviceQueueFamilyProperties get_queue_family_prop; + PFN_vkGetInstanceProcAddr get_proc_addr = ctx->get_proc_addr; + + *index = -1; + *count = 0; + get_queue_family_prop = (PFN_vkGetPhysicalDeviceQueueFamilyProperties) + get_proc_addr(ctx->placebo_instance->instance, + "vkGetPhysicalDeviceQueueFamilyProperties"); + get_queue_family_prop(ctx->placebo_vulkan->phys_device, + &num_queue_family_prop, NULL); + if (!num_queue_family_prop) + return AVERROR_EXTERNAL; + + queue_family_prop = av_calloc(num_queue_family_prop, + sizeof(*queue_family_prop)); + if (!queue_family_prop) + return AVERROR(ENOMEM); + + get_queue_family_prop(ctx->placebo_vulkan->phys_device, + &num_queue_family_prop, + queue_family_prop); + + for (int i = 0; i < num_queue_family_prop; i++) { + if (queue_family_prop[i].queueFlags & VK_QUEUE_VIDEO_DECODE_BIT_KHR) { + *index = i; + *count = queue_family_prop[i].queueCount; + break; + } + } + av_free(queue_family_prop); + + return 0; +} + +static int create_vk_by_placebo(VkRenderer *renderer, + const char **ext, unsigned num_ext, + const AVDictionary *opt) +{ + RendererContext *ctx = (RendererContext *) renderer; + AVHWDeviceContext *device_ctx; + AVVulkanDeviceContext *vk_dev_ctx; + int decode_index; + int decode_count; + int ret; + + ctx->get_proc_addr = SDL_Vulkan_GetVkGetInstanceProcAddr(); + + ctx->placebo_instance = pl_vk_inst_create(ctx->vk_log, pl_vk_inst_params( + .get_proc_addr = ctx->get_proc_addr, + .debug = enable_debug(opt), + .extensions = ext, + .num_extensions = num_ext + )); + if (!ctx->placebo_instance) { + return AVERROR_EXTERNAL; + } + ctx->inst = ctx->placebo_instance->instance; + + ctx->placebo_vulkan = pl_vulkan_create(ctx->vk_log, pl_vulkan_params( + .instance = ctx->placebo_instance->instance, + .get_proc_addr = ctx->placebo_instance->get_proc_addr, + .surface = ctx->vk_surface, + .allow_software = false, + .opt_extensions = optional_device_exts, + .num_opt_extensions = FF_ARRAY_ELEMS(optional_device_exts), + .extra_queues = VK_QUEUE_VIDEO_DECODE_BIT_KHR, + .device_name = select_device(opt), + )); + if (!ctx->placebo_vulkan) + return AVERROR_EXTERNAL; + ctx->hw_device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VULKAN); + if (!ctx->hw_device_ref) { + return AVERROR(ENOMEM); + } + + device_ctx = (AVHWDeviceContext *) ctx->hw_device_ref->data; + device_ctx->user_opaque = ctx; + + vk_dev_ctx = device_ctx->hwctx; + vk_dev_ctx->lock_queue = placebo_lock_queue, + vk_dev_ctx->unlock_queue = placebo_unlock_queue; + + vk_dev_ctx->get_proc_addr = ctx->placebo_instance->get_proc_addr; + + vk_dev_ctx->inst = ctx->placebo_instance->instance; + vk_dev_ctx->phys_dev = ctx->placebo_vulkan->phys_device; + vk_dev_ctx->act_dev = ctx->placebo_vulkan->device; + + vk_dev_ctx->device_features = *ctx->placebo_vulkan->features; + + vk_dev_ctx->enabled_inst_extensions = ctx->placebo_instance->extensions; + vk_dev_ctx->nb_enabled_inst_extensions = ctx->placebo_instance->num_extensions; + + vk_dev_ctx->enabled_dev_extensions = ctx->placebo_vulkan->extensions; + vk_dev_ctx->nb_enabled_dev_extensions = ctx->placebo_vulkan->num_extensions; + + vk_dev_ctx->queue_family_index = ctx->placebo_vulkan->queue_graphics.index; + vk_dev_ctx->nb_graphics_queues = ctx->placebo_vulkan->queue_graphics.count; + + vk_dev_ctx->queue_family_tx_index = ctx->placebo_vulkan->queue_transfer.index; + vk_dev_ctx->nb_tx_queues = ctx->placebo_vulkan->queue_transfer.count; + + vk_dev_ctx->queue_family_comp_index = ctx->placebo_vulkan->queue_compute.index; + vk_dev_ctx->nb_comp_queues = ctx->placebo_vulkan->queue_compute.count; + + ret = get_decode_queue(renderer, &decode_index, &decode_count); + if (ret < 0) + return ret; + + vk_dev_ctx->queue_family_decode_index = decode_index; + vk_dev_ctx->nb_decode_queues = decode_count; + + ret = av_hwdevice_ctx_init(ctx->hw_device_ref); + if (ret < 0) + return ret; + + return 0; +} + +static int create(VkRenderer *renderer, SDL_Window *window, AVDictionary *opt) +{ + int ret = 0; + unsigned num_ext = 0; + const char **ext = NULL; + int w, h; + struct pl_log_params vk_log_params = { + .log_cb = vk_log_cb, + .log_level = PL_LOG_DEBUG, + .log_priv = renderer, + }; + RendererContext *ctx = (RendererContext *) renderer; + AVDictionaryEntry *entry; + + ctx->vk_log = pl_log_create(PL_API_VER, &vk_log_params); + + if (!SDL_Vulkan_GetInstanceExtensions(window, &num_ext, NULL)) { + av_log(NULL, AV_LOG_FATAL, "Failed to get vulkan extensions: %s\n", + SDL_GetError()); + return AVERROR_EXTERNAL; + } + + ext = av_calloc(num_ext, sizeof(*ext)); + if (!ext) { + ret = AVERROR(ENOMEM); + goto out; + } + + SDL_Vulkan_GetInstanceExtensions(window, &num_ext, ext); + + entry = av_dict_get(opt, "create_by_placebo", NULL, 0); + if (entry && strtol(entry->value, NULL, 10)) + ret = create_vk_by_placebo(renderer, ext, num_ext, opt); + else + ret = create_vk_by_hwcontext(renderer, ext, num_ext, opt); + if (ret < 0) + goto out; + + if (!SDL_Vulkan_CreateSurface(window, ctx->inst, &ctx->vk_surface)) { + ret = AVERROR_EXTERNAL; + goto out; + } + + ctx->swapchain = pl_vulkan_create_swapchain( + ctx->placebo_vulkan, + pl_vulkan_swapchain_params( + .surface = ctx->vk_surface, + .present_mode = VK_PRESENT_MODE_FIFO_KHR)); + if (!ctx->swapchain) { + ret = AVERROR_EXTERNAL; + goto out; + } + + SDL_Vulkan_GetDrawableSize(window, &w, &h); + pl_swapchain_resize(ctx->swapchain, &w, &h); + + ctx->renderer = pl_renderer_create(ctx->vk_log, ctx->placebo_vulkan->gpu); + if (!ctx->renderer) { + ret = AVERROR_EXTERNAL; + goto out; + } + + ctx->vk_frame = av_frame_alloc(); + if (!ctx->vk_frame) { + ret = AVERROR(ENOMEM); + goto out; + } + + ret = 0; + +out: + av_free(ext); + return ret; +} + +static int get_hw_dev(VkRenderer *renderer, AVBufferRef **dev) +{ + RendererContext *ctx = (RendererContext *) renderer; + + *dev = ctx->hw_device_ref; + return 0; +} + +static int create_hw_frame(VkRenderer *renderer, AVFrame *frame) +{ + RendererContext *ctx = (RendererContext *) renderer; + AVHWFramesContext *src_hw_frame = (AVHWFramesContext *) + frame->hw_frames_ctx->data; + AVHWFramesContext *hw_frame; + AVVulkanFramesContext *vk_frame_ctx; + int ret; + + if (ctx->hw_frame_ref) { + hw_frame = (AVHWFramesContext *) ctx->hw_frame_ref->data; + + if (hw_frame->width == frame->width && + hw_frame->height == frame->height && + hw_frame->sw_format == src_hw_frame->sw_format) + return 0; + + av_buffer_unref(&ctx->hw_frame_ref); + } + + if (!ctx->constraints) { + ctx->constraints = av_hwdevice_get_hwframe_constraints( + ctx->hw_device_ref, NULL); + if (!ctx->constraints) + return AVERROR(ENOMEM); + } + + // Check constraints and skip create hwframe. Don't take it as error since + // we can fallback to memory copy from GPU to CPU. + if ((ctx->constraints->max_width && + ctx->constraints->max_width < frame->width) || + (ctx->constraints->max_height && + ctx->constraints->max_height < frame->height) || + (ctx->constraints->min_width && + ctx->constraints->min_width > frame->width) || + (ctx->constraints->min_height && + ctx->constraints->min_height > frame->height)) + return 0; + + if (ctx->constraints->valid_sw_formats) { + enum AVPixelFormat *sw_formats = ctx->constraints->valid_sw_formats; + while (*sw_formats != AV_PIX_FMT_NONE) { + if (*sw_formats == src_hw_frame->sw_format) + break; + sw_formats++; + } + if (*sw_formats == AV_PIX_FMT_NONE) + return 0; + } + + ctx->hw_frame_ref = av_hwframe_ctx_alloc(ctx->hw_device_ref); + if (!ctx->hw_frame_ref) + return AVERROR(ENOMEM); + + hw_frame = (AVHWFramesContext *) ctx->hw_frame_ref->data; + hw_frame->format = AV_PIX_FMT_VULKAN; + hw_frame->sw_format = src_hw_frame->sw_format; + hw_frame->width = frame->width; + hw_frame->height = frame->height; + + if (frame->format == AV_PIX_FMT_CUDA) { + vk_frame_ctx = hw_frame->hwctx; + vk_frame_ctx->flags = AV_VK_FRAME_FLAG_DISABLE_MULTIPLANE; + } + + ret = av_hwframe_ctx_init(ctx->hw_frame_ref); + if (ret < 0) { + av_log(renderer, AV_LOG_ERROR, "Create hwframe context failed, %s\n", + av_err2str(ret)); + return ret; + } + + av_hwframe_transfer_get_formats(ctx->hw_frame_ref, + AV_HWFRAME_TRANSFER_DIRECTION_TO, + &ctx->transfer_formats, 0); + + return 0; +} + +static inline int check_hw_transfer(RendererContext *ctx, AVFrame *frame) +{ + if (!ctx->hw_frame_ref || !ctx->transfer_formats) + return 0; + + for (int i = 0; ctx->transfer_formats[i] != AV_PIX_FMT_NONE; i++) + if (ctx->transfer_formats[i] == frame->format) + return 1; + + return 0; +} + +static inline int move_to_output_frame(RendererContext *ctx, AVFrame *frame) +{ + int ret = av_frame_copy_props(ctx->vk_frame, frame); + if (ret < 0) + return ret; + av_frame_unref(frame); + av_frame_move_ref(frame, ctx->vk_frame); + return 0; +} + +static int map_frame(VkRenderer *renderer, AVFrame *frame, int use_hw_frame) +{ + RendererContext *ctx = (RendererContext *) renderer; + int ret; + + if (use_hw_frame && !ctx->hw_frame_ref) + return AVERROR(ENOSYS); + + // Try map data first + av_frame_unref(ctx->vk_frame); + if (use_hw_frame) { + ctx->vk_frame->hw_frames_ctx = av_buffer_ref(ctx->hw_frame_ref); + ctx->vk_frame->format = AV_PIX_FMT_VULKAN; + } + ret = av_hwframe_map(ctx->vk_frame, frame, 0); + if (!ret) + return move_to_output_frame(ctx, frame); + + if (ret != AVERROR(ENOSYS)) + av_log(NULL, AV_LOG_FATAL, "Map frame failed: %s\n", av_err2str(ret)); + return ret; +} + +static int transfer_frame(VkRenderer *renderer, AVFrame *frame, int use_hw_frame) +{ + RendererContext *ctx = (RendererContext *) renderer; + int ret; + + if (use_hw_frame && !check_hw_transfer(ctx, frame)) + return AVERROR(ENOSYS); + + av_frame_unref(ctx->vk_frame); + if (use_hw_frame) + av_hwframe_get_buffer(ctx->hw_frame_ref, ctx->vk_frame, 0); + ret = av_hwframe_transfer_data(ctx->vk_frame, frame, 1); + if (!ret) + return move_to_output_frame(ctx, frame); + + if (ret != AVERROR(ENOSYS)) + av_log(NULL, AV_LOG_FATAL, "Transfer frame failed: %s\n", + av_err2str(ret)); + return ret; +} + +static int convert_frame(VkRenderer *renderer, AVFrame *frame) +{ + int ret; + + if (!frame->hw_frames_ctx) + return 0; + + if (frame->format == AV_PIX_FMT_VULKAN) + return 0; + + ret = create_hw_frame(renderer, frame); + if (ret < 0) + return ret; + + for (int use_hw = 1; use_hw >=0; use_hw--) { + ret = map_frame(renderer, frame, use_hw); + if (!ret) + return 0; + if (ret != AVERROR(ENOSYS)) + return ret; + + ret = transfer_frame(renderer, frame, use_hw); + if (!ret) + return 0; + if (ret != AVERROR(ENOSYS)) + return ret; + } + + return ret; +} + +static int display(VkRenderer *renderer, AVFrame *frame) +{ + struct pl_swapchain_frame swap_frame = {0}; + struct pl_frame pl_frame = {0}; + struct pl_frame target = {0}; + RendererContext *ctx = (RendererContext *) renderer; + int ret = 0; + + ret = convert_frame(renderer, frame); + if (ret < 0) + return ret; + + if (!pl_map_avframe_ex(ctx->placebo_vulkan->gpu, &pl_frame, pl_avframe_params( + .frame = frame, + .tex = ctx->tex))) { + av_log(NULL, AV_LOG_ERROR, "pl_map_avframe_ex failed\n"); + return AVERROR_EXTERNAL; + } + + if (!pl_swapchain_start_frame(ctx->swapchain, &swap_frame)) { + av_log(NULL, AV_LOG_ERROR, "start frame failed\n"); + ret = AVERROR_EXTERNAL; + goto out; + } + + pl_frame_from_swapchain(&target, &swap_frame); + if (!pl_render_image(ctx->renderer, &pl_frame, &target, + &pl_render_default_params)) { + av_log(NULL, AV_LOG_ERROR, "pl_render_image failed\n"); + ret = AVERROR_EXTERNAL; + goto out; + } + + if (!pl_swapchain_submit_frame(ctx->swapchain)) { + av_log(NULL, AV_LOG_ERROR, "pl_swapchain_submit_frame failed\n"); + ret = AVERROR_EXTERNAL; + goto out; + } + pl_swapchain_swap_buffers(ctx->swapchain); + +out: + pl_unmap_avframe(ctx->placebo_vulkan->gpu, &pl_frame); + return ret; +} + +static int resize(VkRenderer *renderer, int width, int height) +{ + RendererContext *ctx = (RendererContext *) renderer; + + if (!pl_swapchain_resize(ctx->swapchain, &width, &height)) + return AVERROR_EXTERNAL; + return 0; +} + +static void destroy(VkRenderer *renderer) +{ + RendererContext *ctx = (RendererContext *) renderer; + PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR; + + av_frame_free(&ctx->vk_frame); + av_freep(&ctx->transfer_formats); + av_hwframe_constraints_free(&ctx->constraints); + av_buffer_unref(&ctx->hw_frame_ref); + + if (ctx->placebo_vulkan) { + for (int i = 0; i < FF_ARRAY_ELEMS(ctx->tex); i++) + pl_tex_destroy(ctx->placebo_vulkan->gpu, &ctx->tex[i]); + pl_renderer_destroy(&ctx->renderer); + pl_swapchain_destroy(&ctx->swapchain); + pl_vulkan_destroy(&ctx->placebo_vulkan); + } + + if (ctx->vk_surface) { + vkDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR) + ctx->get_proc_addr(ctx->inst, "vkDestroySurfaceKHR"); + vkDestroySurfaceKHR(ctx->inst, ctx->vk_surface, NULL); + ctx->vk_surface = VK_NULL_HANDLE; + } + + av_buffer_unref(&ctx->hw_device_ref); + pl_vk_inst_destroy(&ctx->placebo_instance); + + pl_log_destroy(&ctx->vk_log); +} + +static const AVClass vulkan_renderer_class = { + .class_name = "Vulkan Renderer", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, +}; + +VkRenderer *vk_get_renderer(void) +{ + RendererContext *ctx = av_mallocz(sizeof(*ctx)); + VkRenderer *renderer; + + if (!ctx) + return NULL; + + renderer = &ctx->api; + renderer->class = &vulkan_renderer_class; + renderer->get_hw_dev = get_hw_dev; + renderer->create = create; + renderer->display = display; + renderer->resize = resize; + renderer->destroy = destroy; + + return renderer; +} + +#else + +VkRenderer *vk_get_renderer(void) +{ + return NULL; +} + +#endif + +int vk_renderer_create(VkRenderer *renderer, SDL_Window *window, + AVDictionary *opt) +{ + return renderer->create(renderer, window, opt); +} + +int vk_renderer_get_hw_dev(VkRenderer *renderer, AVBufferRef **dev) +{ + return renderer->get_hw_dev(renderer, dev); +} + +int vk_renderer_display(VkRenderer *renderer, AVFrame *frame) +{ + return renderer->display(renderer, frame); +} + +int vk_renderer_resize(VkRenderer *renderer, int width, int height) +{ + return renderer->resize(renderer, width, height); +} + +void vk_renderer_destroy(VkRenderer *renderer) +{ + renderer->destroy(renderer); +} diff --git a/libavcodec/x86/fft.h b/fftools/ffplay_renderer.h similarity index 56% rename from libavcodec/x86/fft.h rename to fftools/ffplay_renderer.h index 37418ec1f4b..dd8e9c06e7c 100644 --- a/libavcodec/x86/fft.h +++ b/fftools/ffplay_renderer.h @@ -16,17 +16,26 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVCODEC_X86_FFT_H -#define AVCODEC_X86_FFT_H +#ifndef FFTOOLS_FFPLAY_RENDERER_H +#define FFTOOLS_FFPLAY_RENDERER_H -#include "libavcodec/fft.h" +#include -void ff_fft_permute_sse(FFTContext *s, FFTComplex *z); -void ff_fft_calc_avx(FFTContext *s, FFTComplex *z); -void ff_fft_calc_sse(FFTContext *s, FFTComplex *z); +#include "libavutil/frame.h" -void ff_imdct_calc_sse(FFTContext *s, FFTSample *output, const FFTSample *input); -void ff_imdct_half_sse(FFTContext *s, FFTSample *output, const FFTSample *input); -void ff_imdct_half_avx(FFTContext *s, FFTSample *output, const FFTSample *input); +typedef struct VkRenderer VkRenderer; -#endif /* AVCODEC_X86_FFT_H */ +VkRenderer *vk_get_renderer(void); + +int vk_renderer_create(VkRenderer *renderer, SDL_Window *window, + AVDictionary *opt); + +int vk_renderer_get_hw_dev(VkRenderer *renderer, AVBufferRef **dev); + +int vk_renderer_display(VkRenderer *renderer, AVFrame *frame); + +int vk_renderer_resize(VkRenderer *renderer, int width, int height); + +void vk_renderer_destroy(VkRenderer *renderer); + +#endif /* FFTOOLS_FFPLAY_RENDERER_H */ diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c index af927cb0843..7d9998b428b 100644 --- a/fftools/ffprobe.c +++ b/fftools/ffprobe.c @@ -39,8 +39,10 @@ #include "libavutil/bprint.h" #include "libavutil/channel_layout.h" #include "libavutil/display.h" +#include "libavutil/film_grain_params.h" #include "libavutil/hash.h" #include "libavutil/hdr_dynamic_metadata.h" +#include "libavutil/iamf.h" #include "libavutil/mastering_display_metadata.h" #include "libavutil/hdr_dynamic_vivid_metadata.h" #include "libavutil/dovi_meta.h" @@ -79,6 +81,12 @@ # define pthread_mutex_unlock(a) do{}while(0) #endif +// attached as opaque_ref to packets/frames +typedef struct FrameData { + int64_t pkt_pos; + int pkt_size; +} FrameData; + typedef struct InputStream { AVStream *st; @@ -106,8 +114,11 @@ static int do_show_format = 0; static int do_show_frames = 0; static int do_show_packets = 0; static int do_show_programs = 0; +static int do_show_stream_groups = 0; +static int do_show_stream_group_components = 0; static int do_show_streams = 0; static int do_show_stream_disposition = 0; +static int do_show_stream_group_disposition = 0; static int do_show_data = 0; static int do_show_program_version = 0; static int do_show_library_versions = 0; @@ -120,6 +131,7 @@ static int do_show_chapter_tags = 0; static int do_show_format_tags = 0; static int do_show_frame_tags = 0; static int do_show_program_tags = 0; +static int do_show_stream_group_tags = 0; static int do_show_stream_tags = 0; static int do_show_packet_tags = 0; @@ -134,7 +146,7 @@ static int show_private_data = 1; #define SHOW_OPTIONAL_FIELDS_ALWAYS 1 static int show_optional_fields = SHOW_OPTIONAL_FIELDS_AUTO; -static char *print_format; +static char *output_format; static char *stream_specifier; static char *show_data_hash; @@ -153,23 +165,7 @@ static int find_stream_info = 1; /* section structure definition */ -#define SECTION_MAX_NB_CHILDREN 10 - -struct section { - int id; ///< unique id identifying a section - const char *name; - -#define SECTION_FLAG_IS_WRAPPER 1 ///< the section only contains other sections, but has no data at its own level -#define SECTION_FLAG_IS_ARRAY 2 ///< the section contains an array of elements of the same type -#define SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys. - /// For these sections the element_name field is mandatory. - int flags; - int children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1 - const char *element_name; ///< name of the contained element, if provided - const char *unique_name; ///< unique section name, in case the name is ambiguous - AVDictionary *entries_to_show; - int show_all_entries; -}; +#define SECTION_MAX_NB_CHILDREN 11 typedef enum { SECTION_ID_NONE = -1, @@ -213,6 +209,24 @@ typedef enum { SECTION_ID_PROGRAM_TAGS, SECTION_ID_PROGRAM_VERSION, SECTION_ID_PROGRAMS, + SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION, + SECTION_ID_STREAM_GROUP_STREAM_TAGS, + SECTION_ID_STREAM_GROUP, + SECTION_ID_STREAM_GROUP_COMPONENTS, + SECTION_ID_STREAM_GROUP_COMPONENT, + SECTION_ID_STREAM_GROUP_SUBCOMPONENTS, + SECTION_ID_STREAM_GROUP_SUBCOMPONENT, + SECTION_ID_STREAM_GROUP_PIECES, + SECTION_ID_STREAM_GROUP_PIECE, + SECTION_ID_STREAM_GROUP_SUBPIECES, + SECTION_ID_STREAM_GROUP_SUBPIECE, + SECTION_ID_STREAM_GROUP_BLOCKS, + SECTION_ID_STREAM_GROUP_BLOCK, + SECTION_ID_STREAM_GROUP_STREAMS, + SECTION_ID_STREAM_GROUP_STREAM, + SECTION_ID_STREAM_GROUP_DISPOSITION, + SECTION_ID_STREAM_GROUP_TAGS, + SECTION_ID_STREAM_GROUPS, SECTION_ID_ROOT, SECTION_ID_STREAM, SECTION_ID_STREAM_DISPOSITION, @@ -223,6 +237,48 @@ typedef enum { SECTION_ID_SUBTITLE, } SectionID; +struct section { + int id; ///< unique id identifying a section + const char *name; + +#define SECTION_FLAG_IS_WRAPPER 1 ///< the section only contains other sections, but has no data at its own level +#define SECTION_FLAG_IS_ARRAY 2 ///< the section contains an array of elements of the same type +#define SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys. + /// For these sections the element_name field is mandatory. +#define SECTION_FLAG_HAS_TYPE 8 ///< the section contains a type to distinguish multiple nested elements + + int flags; + const SectionID children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1 + const char *element_name; ///< name of the contained element, if provided + const char *unique_name; ///< unique section name, in case the name is ambiguous + AVDictionary *entries_to_show; + const char *(* get_type)(const void *data); ///< function returning a type if defined, must be defined when SECTION_FLAG_HAS_TYPE is defined + int show_all_entries; +}; + +static const char *get_packet_side_data_type(const void *data) +{ + const AVPacketSideData *sd = (const AVPacketSideData *)data; + return av_x_if_null(av_packet_side_data_name(sd->type), "unknown"); +} + +static const char *get_frame_side_data_type(const void *data) +{ + const AVFrameSideData *sd = (const AVFrameSideData *)data; + return av_x_if_null(av_frame_side_data_name(sd->type), "unknown"); +} + +static const char *get_raw_string_type(const void *data) +{ + return data; +} + +static const char *get_stream_group_type(const void *data) +{ + const AVStreamGroup *stg = (const AVStreamGroup *)data; + return av_x_if_null(avformat_stream_group_name(stg->type), "unknown"); +} + static struct section sections[] = { [SECTION_ID_CHAPTERS] = { SECTION_ID_CHAPTERS, "chapters", SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } }, [SECTION_ID_CHAPTER] = { SECTION_ID_CHAPTER, "chapter", 0, { SECTION_ID_CHAPTER_TAGS, -1 } }, @@ -234,13 +290,13 @@ static struct section sections[] = { [SECTION_ID_FRAME] = { SECTION_ID_FRAME, "frame", 0, { SECTION_ID_FRAME_TAGS, SECTION_ID_FRAME_SIDE_DATA_LIST, SECTION_ID_FRAME_LOGS, -1 } }, [SECTION_ID_FRAME_TAGS] = { SECTION_ID_FRAME_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "frame_tags" }, [SECTION_ID_FRAME_SIDE_DATA_LIST] ={ SECTION_ID_FRAME_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "frame_side_data_list" }, - [SECTION_ID_FRAME_SIDE_DATA] = { SECTION_ID_FRAME_SIDE_DATA, "side_data", 0, { SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, -1 }, .unique_name = "frame_side_data" }, + [SECTION_ID_FRAME_SIDE_DATA] = { SECTION_ID_FRAME_SIDE_DATA, "side_data", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, -1 }, .unique_name = "frame_side_data", .element_name = "side_datum", .get_type = get_frame_side_data_type }, [SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST] = { SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST, "timecodes", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_TIMECODE, -1 } }, [SECTION_ID_FRAME_SIDE_DATA_TIMECODE] = { SECTION_ID_FRAME_SIDE_DATA_TIMECODE, "timecode", 0, { -1 } }, - [SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, "components", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, -1 } }, - [SECTION_ID_FRAME_SIDE_DATA_COMPONENT] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, "component", 0, { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, -1 } }, - [SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST] = { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_PIECE, -1 } }, - [SECTION_ID_FRAME_SIDE_DATA_PIECE] = { SECTION_ID_FRAME_SIDE_DATA_PIECE, "section", 0, { -1 } }, + [SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST, "components", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, -1 }, .element_name = "component", .unique_name = "frame_side_data_components" }, + [SECTION_ID_FRAME_SIDE_DATA_COMPONENT] = { SECTION_ID_FRAME_SIDE_DATA_COMPONENT, "component", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, -1 }, .unique_name = "frame_side_data_component", .element_name = "component_entry", .get_type = get_raw_string_type }, + [SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST] = { SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_SIDE_DATA_PIECE, -1 }, .element_name = "piece", .unique_name = "frame_side_data_pieces" }, + [SECTION_ID_FRAME_SIDE_DATA_PIECE] = { SECTION_ID_FRAME_SIDE_DATA_PIECE, "piece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "piece_entry", .unique_name = "frame_side_data_piece", .get_type = get_raw_string_type }, [SECTION_ID_FRAME_LOGS] = { SECTION_ID_FRAME_LOGS, "logs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME_LOG, -1 } }, [SECTION_ID_FRAME_LOG] = { SECTION_ID_FRAME_LOG, "log", 0, { -1 }, }, [SECTION_ID_LIBRARY_VERSIONS] = { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } }, @@ -250,7 +306,7 @@ static struct section sections[] = { [SECTION_ID_PACKET] = { SECTION_ID_PACKET, "packet", 0, { SECTION_ID_PACKET_TAGS, SECTION_ID_PACKET_SIDE_DATA_LIST, -1 } }, [SECTION_ID_PACKET_TAGS] = { SECTION_ID_PACKET_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "packet_tags" }, [SECTION_ID_PACKET_SIDE_DATA_LIST] ={ SECTION_ID_PACKET_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "packet_side_data_list" }, - [SECTION_ID_PACKET_SIDE_DATA] = { SECTION_ID_PACKET_SIDE_DATA, "side_data", 0, { -1 }, .unique_name = "packet_side_data" }, + [SECTION_ID_PACKET_SIDE_DATA] = { SECTION_ID_PACKET_SIDE_DATA, "side_data", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .unique_name = "packet_side_data", .element_name = "side_datum", .get_type = get_packet_side_data_type }, [SECTION_ID_PIXEL_FORMATS] = { SECTION_ID_PIXEL_FORMATS, "pixel_formats", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PIXEL_FORMAT, -1 } }, [SECTION_ID_PIXEL_FORMAT] = { SECTION_ID_PIXEL_FORMAT, "pixel_format", 0, { SECTION_ID_PIXEL_FORMAT_FLAGS, SECTION_ID_PIXEL_FORMAT_COMPONENTS, -1 } }, [SECTION_ID_PIXEL_FORMAT_FLAGS] = { SECTION_ID_PIXEL_FORMAT_FLAGS, "flags", 0, { -1 }, .unique_name = "pixel_format_flags" }, @@ -264,8 +320,26 @@ static struct section sections[] = { [SECTION_ID_PROGRAM_TAGS] = { SECTION_ID_PROGRAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "program_tags" }, [SECTION_ID_PROGRAM_VERSION] = { SECTION_ID_PROGRAM_VERSION, "program_version", 0, { -1 } }, [SECTION_ID_PROGRAMS] = { SECTION_ID_PROGRAMS, "programs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } }, + [SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION] = { SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_group_stream_disposition" }, + [SECTION_ID_STREAM_GROUP_STREAM_TAGS] = { SECTION_ID_STREAM_GROUP_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_group_stream_tags" }, + [SECTION_ID_STREAM_GROUP] = { SECTION_ID_STREAM_GROUP, "stream_group", 0, { SECTION_ID_STREAM_GROUP_TAGS, SECTION_ID_STREAM_GROUP_DISPOSITION, SECTION_ID_STREAM_GROUP_COMPONENTS, SECTION_ID_STREAM_GROUP_STREAMS, -1 } }, + [SECTION_ID_STREAM_GROUP_COMPONENTS] = { SECTION_ID_STREAM_GROUP_COMPONENTS, "components", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_COMPONENT, -1 }, .element_name = "component", .unique_name = "stream_group_components" }, + [SECTION_ID_STREAM_GROUP_COMPONENT] = { SECTION_ID_STREAM_GROUP_COMPONENT, "component", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_SUBCOMPONENTS, -1 }, .unique_name = "stream_group_component", .element_name = "component_entry", .get_type = get_stream_group_type }, + [SECTION_ID_STREAM_GROUP_SUBCOMPONENTS] = { SECTION_ID_STREAM_GROUP_SUBCOMPONENTS, "subcomponents", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_SUBCOMPONENT, -1 }, .element_name = "component" }, + [SECTION_ID_STREAM_GROUP_SUBCOMPONENT] = { SECTION_ID_STREAM_GROUP_SUBCOMPONENT, "subcomponent", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_PIECES, -1 }, .element_name = "subcomponent_entry", .get_type = get_raw_string_type }, + [SECTION_ID_STREAM_GROUP_PIECES] = { SECTION_ID_STREAM_GROUP_PIECES, "pieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_PIECE, -1 }, .element_name = "piece", .unique_name = "stream_group_pieces" }, + [SECTION_ID_STREAM_GROUP_PIECE] = { SECTION_ID_STREAM_GROUP_PIECE, "piece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_SUBPIECES, -1 }, .unique_name = "stream_group_piece", .element_name = "piece_entry", .get_type = get_raw_string_type }, + [SECTION_ID_STREAM_GROUP_SUBPIECES] = { SECTION_ID_STREAM_GROUP_SUBPIECES, "subpieces", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_SUBPIECE, -1 }, .element_name = "subpiece" }, + [SECTION_ID_STREAM_GROUP_SUBPIECE] = { SECTION_ID_STREAM_GROUP_SUBPIECE, "subpiece", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_BLOCKS, -1 }, .element_name = "subpiece_entry", .get_type = get_raw_string_type }, + [SECTION_ID_STREAM_GROUP_BLOCKS] = { SECTION_ID_STREAM_GROUP_BLOCKS, "blocks", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_BLOCK, -1 }, .element_name = "block" }, + [SECTION_ID_STREAM_GROUP_BLOCK] = { SECTION_ID_STREAM_GROUP_BLOCK, "block", SECTION_FLAG_HAS_VARIABLE_FIELDS|SECTION_FLAG_HAS_TYPE, { -1 }, .element_name = "block_entry", .get_type = get_raw_string_type }, + [SECTION_ID_STREAM_GROUP_STREAMS] = { SECTION_ID_STREAM_GROUP_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_STREAM, -1 }, .unique_name = "stream_group_streams" }, + [SECTION_ID_STREAM_GROUP_STREAM] = { SECTION_ID_STREAM_GROUP_STREAM, "stream", 0, { SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION, SECTION_ID_STREAM_GROUP_STREAM_TAGS, -1 }, .unique_name = "stream_group_stream" }, + [SECTION_ID_STREAM_GROUP_DISPOSITION] = { SECTION_ID_STREAM_GROUP_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_group_disposition" }, + [SECTION_ID_STREAM_GROUP_TAGS] = { SECTION_ID_STREAM_GROUP_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_group_tags" }, + [SECTION_ID_STREAM_GROUPS] = { SECTION_ID_STREAM_GROUPS, "stream_groups", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP, -1 } }, [SECTION_ID_ROOT] = { SECTION_ID_ROOT, "root", SECTION_FLAG_IS_WRAPPER, - { SECTION_ID_CHAPTERS, SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_PROGRAMS, SECTION_ID_STREAMS, + { SECTION_ID_CHAPTERS, SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_PROGRAMS, SECTION_ID_STREAM_GROUPS, SECTION_ID_STREAMS, SECTION_ID_PACKETS, SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_LIBRARY_VERSIONS, SECTION_ID_PIXEL_FORMATS, -1} }, [SECTION_ID_STREAMS] = { SECTION_ID_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } }, @@ -273,7 +347,7 @@ static struct section sections[] = { [SECTION_ID_STREAM_DISPOSITION] = { SECTION_ID_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_disposition" }, [SECTION_ID_STREAM_TAGS] = { SECTION_ID_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_tags" }, [SECTION_ID_STREAM_SIDE_DATA_LIST] ={ SECTION_ID_STREAM_SIDE_DATA_LIST, "side_data_list", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_SIDE_DATA, -1 }, .element_name = "side_data", .unique_name = "stream_side_data_list" }, - [SECTION_ID_STREAM_SIDE_DATA] = { SECTION_ID_STREAM_SIDE_DATA, "side_data", 0, { -1 }, .unique_name = "stream_side_data" }, + [SECTION_ID_STREAM_SIDE_DATA] = { SECTION_ID_STREAM_SIDE_DATA, "side_data", SECTION_FLAG_HAS_TYPE|SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .unique_name = "stream_side_data", .element_name = "side_datum", .get_type = get_packet_side_data_type }, [SECTION_ID_SUBTITLE] = { SECTION_ID_SUBTITLE, "subtitle", 0, { -1 } }, }; @@ -375,26 +449,15 @@ static void log_callback(void *ptr, int level, const char *fmt, va_list vl) #endif } -static void ffprobe_cleanup(int ret) -{ - int i; - for (i = 0; i < FF_ARRAY_ELEMS(sections); i++) - av_dict_free(&(sections[i].entries_to_show)); - -#if HAVE_THREADS - pthread_mutex_destroy(&log_mutex); -#endif -} - struct unit_value { - union { double d; long long int i; } val; + union { double d; int64_t i; } val; const char *unit; }; static char *value_string(char *buf, int buf_size, struct unit_value uv) { double vald; - long long int vali; + int64_t vali; int show_float = 0; if (uv.unit == unit_second_str) { @@ -417,15 +480,15 @@ static char *value_string(char *buf, int buf_size, struct unit_value uv) const char *prefix_string = ""; if (use_value_prefix && vald > 1) { - long long int index; + int64_t index; if (uv.unit == unit_byte_str && use_byte_value_binary_prefix) { - index = (long long int) (log2(vald)) / 10; + index = (int64_t) (log2(vald)) / 10; index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1); vald /= si_prefixes[index].bin_val; prefix_string = si_prefixes[index].bin_str; } else { - index = (long long int) (log10(vald)) / 3; + index = (int64_t) (log10(vald)) / 3; index = av_clip(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1); vald /= si_prefixes[index].dec_val; prefix_string = si_prefixes[index].dec_str; @@ -433,10 +496,10 @@ static char *value_string(char *buf, int buf_size, struct unit_value uv) vali = vald; } - if (show_float || (use_value_prefix && vald != (long long int)vald)) + if (show_float || (use_value_prefix && vald != (int64_t)vald)) snprintf(buf, buf_size, "%f", vald); else - snprintf(buf, buf_size, "%lld", vali); + snprintf(buf, buf_size, "%"PRId64, vali); av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || show_value_unit ? " " : "", prefix_string, show_value_unit ? uv.unit : ""); } @@ -466,15 +529,15 @@ typedef struct Writer { int (*init) (WriterContext *wctx); void (*uninit)(WriterContext *wctx); - void (*print_section_header)(WriterContext *wctx); + void (*print_section_header)(WriterContext *wctx, const void *data); void (*print_section_footer)(WriterContext *wctx); - void (*print_integer) (WriterContext *wctx, const char *, long long int); + void (*print_integer) (WriterContext *wctx, const char *, int64_t); void (*print_rational) (WriterContext *wctx, AVRational *q, char *sep); void (*print_string) (WriterContext *wctx, const char *, const char *); int flags; ///< a combination or WRITER_FLAG_* } Writer; -#define SECTION_MAX_NB_LEVELS 10 +#define SECTION_MAX_NB_LEVELS 12 struct WriterContext { const AVClass *class; ///< class of the writer @@ -720,6 +783,7 @@ static int writer_open(WriterContext **wctx, const Writer *writer, const char *a } static inline void writer_print_section_header(WriterContext *wctx, + const void *data, int section_id) { int parent_section_id; @@ -740,7 +804,7 @@ static inline void writer_print_section_header(WriterContext *wctx, } if (wctx->writer->print_section_header) - wctx->writer->print_section_header(wctx); + wctx->writer->print_section_header(wctx, data); } static inline void writer_print_section_footer(WriterContext *wctx) @@ -761,7 +825,7 @@ static inline void writer_print_section_footer(WriterContext *wctx) } static inline void writer_print_integer(WriterContext *wctx, - const char *key, long long int val) + const char *key, int64_t val) { const struct section *section = wctx->section[wctx->level]; @@ -780,7 +844,7 @@ static inline int validate_string(WriterContext *wctx, char **dstp, const char * av_bprint_init(&dstbuf, 0, AV_BPRINT_SIZE_UNLIMITED); endp = src + strlen(src); - for (p = (uint8_t *)src; *p;) { + for (p = src; *p;) { uint32_t code; int invalid = 0; const uint8_t *p0 = p; @@ -1047,7 +1111,7 @@ static inline char *upcase_string(char *dst, size_t dst_size, const char *src) return dst; } -static void default_print_section_header(WriterContext *wctx) +static void default_print_section_header(WriterContext *wctx, const void *data) { DefaultContext *def = wctx->priv; char buf[32]; @@ -1094,13 +1158,13 @@ static void default_print_str(WriterContext *wctx, const char *key, const char * writer_printf(wctx, "%s\n", value); } -static void default_print_int(WriterContext *wctx, const char *key, long long int value) +static void default_print_int(WriterContext *wctx, const char *key, int64_t value) { DefaultContext *def = wctx->priv; if (!def->nokey) writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); - writer_printf(wctx, "%lld\n", value); + writer_printf(wctx, "%"PRId64"\n", value); } static const Writer default_writer = { @@ -1217,7 +1281,7 @@ static av_cold int compact_init(WriterContext *wctx) return 0; } -static void compact_print_section_header(WriterContext *wctx) +static void compact_print_section_header(WriterContext *wctx, const void *data) { CompactContext *compact = wctx->priv; const struct section *section = wctx->section[wctx->level]; @@ -1227,19 +1291,39 @@ static void compact_print_section_header(WriterContext *wctx) compact->has_nested_elems[wctx->level] = 0; av_bprint_clear(&wctx->section_pbuf[wctx->level]); - if (!(section->flags & SECTION_FLAG_IS_ARRAY) && parent_section && - !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) { + if (parent_section && + (section->flags & SECTION_FLAG_HAS_TYPE || + (!(section->flags & SECTION_FLAG_IS_ARRAY) && + !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))))) { + + /* define a prefix for elements not contained in an array or + in a wrapper, or for array elements with a type */ + const char *element_name = (char *)av_x_if_null(section->element_name, section->name); + AVBPrint *section_pbuf = &wctx->section_pbuf[wctx->level]; + compact->nested_section[wctx->level] = 1; compact->has_nested_elems[wctx->level-1] = 1; - av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:", - wctx->section_pbuf[wctx->level-1].str, - (char *)av_x_if_null(section->element_name, section->name)); + + av_bprintf(section_pbuf, "%s%s", + wctx->section_pbuf[wctx->level-1].str, element_name); + + if (section->flags & SECTION_FLAG_HAS_TYPE) { + // add /TYPE to prefix + av_bprint_chars(section_pbuf, '/', 1); + + // normalize section type, replace special characters and lower case + for (const char *p = section->get_type(data); *p; p++) { + char c = + (*p >= '0' && *p <= '9') || + (*p >= 'a' && *p <= 'z') || + (*p >= 'A' && *p <= 'Z') ? av_tolower(*p) : '_'; + av_bprint_chars(section_pbuf, c, 1); + } + } + av_bprint_chars(section_pbuf, ':', 1); + wctx->nb_item[wctx->level] = wctx->nb_item[wctx->level-1]; } else { - if (parent_section && compact->has_nested_elems[wctx->level-1] && - (section->flags & SECTION_FLAG_IS_ARRAY)) { - compact->terminate_line[wctx->level-1] = 0; - } if (parent_section && !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)) && wctx->level && wctx->nb_item[wctx->level-1]) writer_w8(wctx, compact->item_sep); @@ -1272,14 +1356,14 @@ static void compact_print_str(WriterContext *wctx, const char *key, const char * av_bprint_finalize(&buf, NULL); } -static void compact_print_int(WriterContext *wctx, const char *key, long long int value) +static void compact_print_int(WriterContext *wctx, const char *key, int64_t value) { CompactContext *compact = wctx->priv; if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep); if (!compact->nokey) writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); - writer_printf(wctx, "%lld", value); + writer_printf(wctx, "%"PRId64, value); } static const Writer compact_writer = { @@ -1394,7 +1478,7 @@ static const char *flat_escape_value_str(AVBPrint *dst, const char *src) return dst->str; } -static void flat_print_section_header(WriterContext *wctx) +static void flat_print_section_header(WriterContext *wctx, const void *data) { FlatContext *flat = wctx->priv; AVBPrint *buf = &wctx->section_pbuf[wctx->level]; @@ -1420,9 +1504,9 @@ static void flat_print_section_header(WriterContext *wctx) } } -static void flat_print_int(WriterContext *wctx, const char *key, long long int value) +static void flat_print_int(WriterContext *wctx, const char *key, int64_t value) { - writer_printf(wctx, "%s%s=%lld\n", wctx->section_pbuf[wctx->level].str, key, value); + writer_printf(wctx, "%s%s=%"PRId64"\n", wctx->section_pbuf[wctx->level].str, key, value); } static void flat_print_str(WriterContext *wctx, const char *key, const char *value) @@ -1494,7 +1578,7 @@ static char *ini_escape_str(AVBPrint *dst, const char *src) return dst->str; } -static void ini_print_section_header(WriterContext *wctx) +static void ini_print_section_header(WriterContext *wctx, const void *data) { INIContext *ini = wctx->priv; AVBPrint *buf = &wctx->section_pbuf[wctx->level]; @@ -1538,9 +1622,9 @@ static void ini_print_str(WriterContext *wctx, const char *key, const char *valu av_bprint_finalize(&buf, NULL); } -static void ini_print_int(WriterContext *wctx, const char *key, long long int value) +static void ini_print_int(WriterContext *wctx, const char *key, int64_t value) { - writer_printf(wctx, "%s=%lld\n", key, value); + writer_printf(wctx, "%s=%"PRId64"\n", key, value); } static const Writer ini_writer = { @@ -1605,7 +1689,7 @@ static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx #define JSON_INDENT() writer_printf(wctx, "%*c", json->indent_level * 4, ' ') -static void json_print_section_header(WriterContext *wctx) +static void json_print_section_header(WriterContext *wctx, const void *data) { JSONContext *json = wctx->priv; AVBPrint buf; @@ -1691,7 +1775,7 @@ static void json_print_str(WriterContext *wctx, const char *key, const char *val json_print_item_str(wctx, key, value); } -static void json_print_int(WriterContext *wctx, const char *key, long long int value) +static void json_print_int(WriterContext *wctx, const char *key, int64_t value) { JSONContext *json = wctx->priv; const struct section *parent_section = wctx->level ? @@ -1704,7 +1788,7 @@ static void json_print_int(WriterContext *wctx, const char *key, long long int v JSON_INDENT(); av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); - writer_printf(wctx, "\"%s\": %lld", json_escape_str(&buf, key, wctx), value); + writer_printf(wctx, "\"%s\": %"PRId64, json_escape_str(&buf, key, wctx), value); av_bprint_finalize(&buf, NULL); } @@ -1766,7 +1850,7 @@ static av_cold int xml_init(WriterContext *wctx) #define XML_INDENT() writer_printf(wctx, "%*c", xml->indent_level * 4, ' ') -static void xml_print_section_header(WriterContext *wctx) +static void xml_print_section_header(WriterContext *wctx, const void *data) { XMLContext *xml = wctx->priv; const struct section *section = wctx->section[wctx->level]; @@ -1789,20 +1873,26 @@ static void xml_print_section_header(WriterContext *wctx) xml->within_tag = 0; writer_put_str(wctx, ">\n"); } - if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) { - xml->indent_level++; - } else { - if (parent_section && (parent_section->flags & SECTION_FLAG_IS_WRAPPER) && - wctx->level && wctx->nb_item[wctx->level-1]) - writer_w8(wctx, '\n'); - xml->indent_level++; - if (section->flags & SECTION_FLAG_IS_ARRAY) { - XML_INDENT(); writer_printf(wctx, "<%s>\n", section->name); - } else { - XML_INDENT(); writer_printf(wctx, "<%s ", section->name); - xml->within_tag = 1; + if (parent_section && (parent_section->flags & SECTION_FLAG_IS_WRAPPER) && + wctx->level && wctx->nb_item[wctx->level-1]) + writer_w8(wctx, '\n'); + xml->indent_level++; + + if (section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_HAS_VARIABLE_FIELDS)) { + XML_INDENT(); writer_printf(wctx, "<%s", section->name); + + if (section->flags & SECTION_FLAG_HAS_TYPE) { + AVBPrint buf; + av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); + av_bprint_escape(&buf, section->get_type(data), NULL, + AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); + writer_printf(wctx, " type=\"%s\"", buf.str); } + writer_printf(wctx, ">\n", section->name); + } else { + XML_INDENT(); writer_printf(wctx, "<%s ", section->name); + xml->within_tag = 1; } } @@ -1817,15 +1907,14 @@ static void xml_print_section_footer(WriterContext *wctx) xml->within_tag = 0; writer_put_str(wctx, "/>\n"); xml->indent_level--; - } else if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) { - xml->indent_level--; } else { XML_INDENT(); writer_printf(wctx, "\n", section->name); xml->indent_level--; } } -static void xml_print_str(WriterContext *wctx, const char *key, const char *value) +static void xml_print_value(WriterContext *wctx, const char *key, + const char *str, int64_t num, const int is_int) { AVBPrint buf; XMLContext *xml = wctx->priv; @@ -1834,6 +1923,7 @@ static void xml_print_str(WriterContext *wctx, const char *key, const char *valu av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) { + xml->indent_level++; XML_INDENT(); av_bprint_escape(&buf, key, NULL, AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); @@ -1841,26 +1931,37 @@ static void xml_print_str(WriterContext *wctx, const char *key, const char *valu section->element_name, buf.str); av_bprint_clear(&buf); - av_bprint_escape(&buf, value, NULL, - AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); - writer_printf(wctx, " value=\"%s\"/>\n", buf.str); + if (is_int) { + writer_printf(wctx, " value=\"%"PRId64"\"/>\n", num); + } else { + av_bprint_escape(&buf, str, NULL, + AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); + writer_printf(wctx, " value=\"%s\"/>\n", buf.str); + } + xml->indent_level--; } else { if (wctx->nb_item[wctx->level]) writer_w8(wctx, ' '); - av_bprint_escape(&buf, value, NULL, - AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); - writer_printf(wctx, "%s=\"%s\"", key, buf.str); + if (is_int) { + writer_printf(wctx, "%s=\"%"PRId64"\"", key, num); + } else { + av_bprint_escape(&buf, str, NULL, + AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); + writer_printf(wctx, "%s=\"%s\"", key, buf.str); + } } av_bprint_finalize(&buf, NULL); } -static void xml_print_int(WriterContext *wctx, const char *key, long long int value) +static inline void xml_print_str(WriterContext *wctx, const char *key, const char *value) { + xml_print_value(wctx, key, value, 0, 0); +} + +static void xml_print_int(WriterContext *wctx, const char *key, int64_t value) { - if (wctx->nb_item[wctx->level]) - writer_w8(wctx, ' '); - writer_printf(wctx, "%s=\"%lld\"", key, value); + xml_print_value(wctx, key, NULL, value, 1); } static Writer xml_writer = { @@ -1926,7 +2027,8 @@ static void writer_register_all(void) writer_print_string(w, k, value_string(val_str, sizeof(val_str), uv), 0); \ } while (0) -#define print_section_header(s) writer_print_section_header(w, s) +#define print_section_header(s) writer_print_section_header(w, NULL, s) +#define print_section_header_data(s, d) writer_print_section_header(w, d, s) #define print_section_footer(s) writer_print_section_footer(w, s) #define REALLOCZ_ARRAY_STREAM(ptr, cur_n, new_n) \ @@ -1944,7 +2046,7 @@ static inline int show_tags(WriterContext *w, AVDictionary *tags, int section_id if (!tags) return 0; - writer_print_section_header(w, section_id); + writer_print_section_header(w, NULL, section_id); while ((tag = av_dict_iterate(tags, tag))) { if ((ret = print_str_validate(tag->key, tag->value)) < 0) @@ -2010,18 +2112,33 @@ static void print_dovi_metadata(WriterContext *w, const AVDOVIMetadata *dovi) print_int("num_x_partitions", mapping->num_x_partitions); print_int("num_y_partitions", mapping->num_y_partitions); - writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST); + writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST); for (int c = 0; c < 3; c++) { const AVDOVIReshapingCurve *curve = &mapping->curves[c]; - writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA_COMPONENT); + writer_print_section_header(w, "Reshaping curve", SECTION_ID_FRAME_SIDE_DATA_COMPONENT); print_list_fmt("pivots", "%"PRIu16, curve->num_pivots, 1, curve->pivots[idx]); - writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST); + writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST); for (int i = 0; i < curve->num_pivots - 1; i++) { + AVBPrint piece_buf; + + av_bprint_init(&piece_buf, 0, AV_BPRINT_SIZE_AUTOMATIC); + switch (curve->mapping_idc[i]) { + case AV_DOVI_MAPPING_POLYNOMIAL: + av_bprintf(&piece_buf, "Polynomial"); + break; + case AV_DOVI_MAPPING_MMR: + av_bprintf(&piece_buf, "MMR"); + break; + default: + av_bprintf(&piece_buf, "Unknown"); + break; + } + av_bprintf(&piece_buf, " mapping"); - writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA_PIECE); + writer_print_section_header(w, piece_buf.str, SECTION_ID_FRAME_SIDE_DATA_PIECE); print_int("mapping_idc", curve->mapping_idc[i]); switch (curve->mapping_idc[i]) { case AV_DOVI_MAPPING_POLYNOMIAL: @@ -2221,8 +2338,8 @@ static void print_dynamic_hdr_vivid(WriterContext *w, const AVDynamicHDRVivid *m const AVHDRVividColorTransformParams *params = &metadata->params[n]; print_int("tone_mapping_mode_flag", params->tone_mapping_mode_flag); - print_int("tone_mapping_param_num", params->tone_mapping_param_num); if (params->tone_mapping_mode_flag) { + print_int("tone_mapping_param_num", params->tone_mapping_param_num); for (int i = 0; i < params->tone_mapping_param_num; i++) { const AVHDRVividColorToneMappingParams *tm_params = ¶ms->tm_params[i]; @@ -2246,14 +2363,16 @@ static void print_dynamic_hdr_vivid(WriterContext *w, const AVDynamicHDRVivid *m print_int("3Spline_enable_flag", tm_params->three_Spline_enable_flag); if (tm_params->three_Spline_enable_flag) { print_int("3Spline_num", tm_params->three_Spline_num); - print_int("3Spline_TH_mode", tm_params->three_Spline_TH_mode); for (int j = 0; j < tm_params->three_Spline_num; j++) { - print_q("3Spline_TH_enable_MB", tm_params->three_Spline_TH_enable_MB, '/'); - print_q("3Spline_TH_enable", tm_params->three_Spline_TH_enable, '/'); - print_q("3Spline_TH_Delta1", tm_params->three_Spline_TH_Delta1, '/'); - print_q("3Spline_TH_Delta2", tm_params->three_Spline_TH_Delta2, '/'); - print_q("3Spline_enable_Strength", tm_params->three_Spline_enable_Strength, '/'); + const AVHDRVivid3SplineParams *three_spline = &tm_params->three_spline[j]; + print_int("3Spline_TH_mode", three_spline->th_mode); + if (three_spline->th_mode == 0 || three_spline->th_mode == 2) + print_q("3Spline_TH_enable_MB", three_spline->th_enable_mb, '/'); + print_q("3Spline_TH_enable", three_spline->th_enable, '/'); + print_q("3Spline_TH_Delta1", three_spline->th_delta1, '/'); + print_q("3Spline_TH_Delta2", three_spline->th_delta2, '/'); + print_q("3Spline_enable_Strength", three_spline->enable_strength, '/'); } } } @@ -2280,21 +2399,139 @@ static void print_ambient_viewing_environment(WriterContext *w, print_q("ambient_light_y", env->ambient_light_y, '/'); } +static void print_film_grain_params(WriterContext *w, + const AVFilmGrainParams *fgp) +{ + const char *color_range, *color_primaries, *color_trc, *color_space; + const char *const film_grain_type_names[] = { + [AV_FILM_GRAIN_PARAMS_NONE] = "none", + [AV_FILM_GRAIN_PARAMS_AV1] = "av1", + [AV_FILM_GRAIN_PARAMS_H274] = "h274", + }; + + AVBPrint pbuf; + if (!fgp || fgp->type >= FF_ARRAY_ELEMS(film_grain_type_names)) + return; + + color_range = av_color_range_name(fgp->color_range); + color_primaries = av_color_primaries_name(fgp->color_primaries); + color_trc = av_color_transfer_name(fgp->color_trc); + color_space = av_color_space_name(fgp->color_space); + + av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); + print_str("type", film_grain_type_names[fgp->type]); + print_fmt("seed", "%"PRIu64, fgp->seed); + print_int("width", fgp->width); + print_int("height", fgp->height); + print_int("subsampling_x", fgp->subsampling_x); + print_int("subsampling_y", fgp->subsampling_y); + print_str("color_range", color_range ? color_range : "unknown"); + print_str("color_primaries", color_primaries ? color_primaries : "unknown"); + print_str("color_trc", color_trc ? color_trc : "unknown"); + print_str("color_space", color_space ? color_space : "unknown"); + + switch (fgp->type) { + case AV_FILM_GRAIN_PARAMS_NONE: + break; + case AV_FILM_GRAIN_PARAMS_AV1: { + const AVFilmGrainAOMParams *aom = &fgp->codec.aom; + const int num_ar_coeffs_y = 2 * aom->ar_coeff_lag * (aom->ar_coeff_lag + 1); + const int num_ar_coeffs_uv = num_ar_coeffs_y + !!aom->num_y_points; + print_int("chroma_scaling_from_luma", aom->chroma_scaling_from_luma); + print_int("scaling_shift", aom->scaling_shift); + print_int("ar_coeff_lag", aom->ar_coeff_lag); + print_int("ar_coeff_shift", aom->ar_coeff_shift); + print_int("grain_scale_shift", aom->grain_scale_shift); + print_int("overlap_flag", aom->overlap_flag); + print_int("limit_output_range", aom->limit_output_range); + + writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST); + + if (aom->num_y_points) { + writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT); + + print_int("bit_depth_luma", fgp->bit_depth_luma); + print_list_fmt("y_points_value", "%"PRIu8, aom->num_y_points, 1, aom->y_points[idx][0]); + print_list_fmt("y_points_scaling", "%"PRIu8, aom->num_y_points, 1, aom->y_points[idx][1]); + print_list_fmt("ar_coeffs_y", "%"PRId8, num_ar_coeffs_y, 1, aom->ar_coeffs_y[idx]); + + // SECTION_ID_FRAME_SIDE_DATA_COMPONENT + writer_print_section_footer(w); + } + + for (int uv = 0; uv < 2; uv++) { + if (!aom->num_uv_points[uv] && !aom->chroma_scaling_from_luma) + continue; + + writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT); + + print_int("bit_depth_chroma", fgp->bit_depth_chroma); + print_list_fmt("uv_points_value", "%"PRIu8, aom->num_uv_points[uv], 1, aom->uv_points[uv][idx][0]); + print_list_fmt("uv_points_scaling", "%"PRIu8, aom->num_uv_points[uv], 1, aom->uv_points[uv][idx][1]); + print_list_fmt("ar_coeffs_uv", "%"PRId8, num_ar_coeffs_uv, 1, aom->ar_coeffs_uv[uv][idx]); + print_int("uv_mult", aom->uv_mult[uv]); + print_int("uv_mult_luma", aom->uv_mult_luma[uv]); + print_int("uv_offset", aom->uv_offset[uv]); + + // SECTION_ID_FRAME_SIDE_DATA_COMPONENT + writer_print_section_footer(w); + } + + // SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST + writer_print_section_footer(w); + break; + } + case AV_FILM_GRAIN_PARAMS_H274: { + const AVFilmGrainH274Params *h274 = &fgp->codec.h274; + print_int("model_id", h274->model_id); + print_int("blending_mode_id", h274->blending_mode_id); + print_int("log2_scale_factor", h274->log2_scale_factor); + + writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST); + + for (int c = 0; c < 3; c++) { + if (!h274->component_model_present[c]) + continue; + + writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_COMPONENT); + print_int(c ? "bit_depth_chroma" : "bit_depth_luma", c ? fgp->bit_depth_chroma : fgp->bit_depth_luma); + + writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST); + for (int i = 0; i < h274->num_intensity_intervals[c]; i++) { + + writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_PIECE); + print_int("intensity_interval_lower_bound", h274->intensity_interval_lower_bound[c][i]); + print_int("intensity_interval_upper_bound", h274->intensity_interval_upper_bound[c][i]); + print_list_fmt("comp_model_value", "%"PRId16, h274->num_model_values[c], 1, h274->comp_model_value[c][i][idx]); + + // SECTION_ID_FRAME_SIDE_DATA_PIECE + writer_print_section_footer(w); + } + + // SECTION_ID_FRAME_SIDE_DATA_PIECE_LIST + writer_print_section_footer(w); + + // SECTION_ID_FRAME_SIDE_DATA_COMPONENT + writer_print_section_footer(w); + } + + // SECTION_ID_FRAME_SIDE_DATA_COMPONENT_LIST + writer_print_section_footer(w); + break; + } + } + + av_bprint_finalize(&pbuf, NULL); +} + static void print_pkt_side_data(WriterContext *w, AVCodecParameters *par, - const AVPacketSideData *side_data, - int nb_side_data, - SectionID id_data_list, + const AVPacketSideData *sd, SectionID id_data) { - int i; - - writer_print_section_header(w, id_data_list); - for (i = 0; i < nb_side_data; i++) { - const AVPacketSideData *sd = &side_data[i]; const char *name = av_packet_side_data_name(sd->type); - writer_print_section_header(w, id_data); + writer_print_section_header(w, sd, id_data); print_str("side_data_type", name ? name : "unknown"); if (sd->type == AV_PKT_DATA_DISPLAYMATRIX && sd->size >= 9*4) { double rotation = av_display_rotation_get((int32_t *)sd->data); @@ -2352,6 +2589,12 @@ static void print_pkt_side_data(WriterContext *w, AVContentLightMetadata *metadata = (AVContentLightMetadata *)sd->data; print_int("max_content", metadata->MaxCLL); print_int("max_average", metadata->MaxFALL); + } else if (sd->type == AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT) { + print_ambient_viewing_environment( + w, (const AVAmbientViewingEnvironment *)sd->data); + } else if (sd->type == AV_PKT_DATA_DYNAMIC_HDR10_PLUS) { + AVDynamicHDRPlus *metadata = (AVDynamicHDRPlus *)sd->data; + print_dynamic_hdr10_plus(w, metadata); } else if (sd->type == AV_PKT_DATA_DOVI_CONF) { AVDOVIDecoderConfigurationRecord *dovi = (AVDOVIDecoderConfigurationRecord *)sd->data; print_int("dv_version_major", dovi->dv_version_major); @@ -2382,9 +2625,19 @@ static void print_pkt_side_data(WriterContext *w, } else if (sd->type == AV_PKT_DATA_AFD && sd->size > 0) { print_int("active_format", *sd->data); } - writer_print_section_footer(w); +} + +static void print_private_data(WriterContext *w, void *priv_data) +{ + const AVOption *opt = NULL; + while (opt = av_opt_next(priv_data, opt)) { + uint8_t *str; + if (!(opt->flags & AV_OPT_FLAG_EXPORT)) continue; + if (av_opt_get(priv_data, opt->name, 0, &str) >= 0) { + print_str(opt->name, str); + av_free(str); + } } - writer_print_section_footer(w); } static void print_color_range(WriterContext *w, enum AVColorRange color_range) @@ -2437,7 +2690,6 @@ static void print_chroma_location(WriterContext *w, enum AVChromaLocation chroma } } - static void clear_log(int need_lock) { int i; @@ -2462,11 +2714,11 @@ static int show_log(WriterContext *w, int section_ids, int section_id, int log_l pthread_mutex_unlock(&log_mutex); return 0; } - writer_print_section_header(w, section_ids); + writer_print_section_header(w, NULL, section_ids); for (i=0; icodecpar->codec_type); if (s) print_str ("codec_type", s); @@ -2532,9 +2784,13 @@ static void show_packet(WriterContext *w, InputFile *ifile, AVPacket *pkt, int p av_dict_free(&dict); } - print_pkt_side_data(w, st->codecpar, pkt->side_data, pkt->side_data_elems, - SECTION_ID_PACKET_SIDE_DATA_LIST, - SECTION_ID_PACKET_SIDE_DATA); + writer_print_section_header(w, NULL, SECTION_ID_PACKET_SIDE_DATA_LIST); + for (int i = 0; i < pkt->side_data_elems; i++) { + print_pkt_side_data(w, st->codecpar, &pkt->side_data[i], + SECTION_ID_PACKET_SIDE_DATA); + writer_print_section_footer(w); + } + writer_print_section_footer(w); } writer_print_section_footer(w); @@ -2550,7 +2806,7 @@ static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream, av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); - writer_print_section_header(w, SECTION_ID_SUBTITLE); + writer_print_section_header(w, NULL, SECTION_ID_SUBTITLE); print_str ("media_type", "subtitle"); print_ts ("pts", sub->pts); @@ -2566,41 +2822,119 @@ static void show_subtitle(WriterContext *w, AVSubtitle *sub, AVStream *stream, fflush(stdout); } +static void print_frame_side_data(WriterContext *w, + const AVFrame *frame, + const AVStream *stream) +{ + writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_LIST); + + for (int i = 0; i < frame->nb_side_data; i++) { + const AVFrameSideData *sd = frame->side_data[i]; + const char *name; + + writer_print_section_header(w, sd, SECTION_ID_FRAME_SIDE_DATA); + name = av_frame_side_data_name(sd->type); + print_str("side_data_type", name ? name : "unknown"); + if (sd->type == AV_FRAME_DATA_DISPLAYMATRIX && sd->size >= 9*4) { + double rotation = av_display_rotation_get((int32_t *)sd->data); + if (isnan(rotation)) + rotation = 0; + writer_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1); + print_int("rotation", rotation); + } else if (sd->type == AV_FRAME_DATA_AFD && sd->size > 0) { + print_int("active_format", *sd->data); + } else if (sd->type == AV_FRAME_DATA_GOP_TIMECODE && sd->size >= 8) { + char tcbuf[AV_TIMECODE_STR_SIZE]; + av_timecode_make_mpeg_tc_string(tcbuf, *(int64_t *)(sd->data)); + print_str("timecode", tcbuf); + } else if (sd->type == AV_FRAME_DATA_S12M_TIMECODE && sd->size == 16) { + uint32_t *tc = (uint32_t*)sd->data; + int m = FFMIN(tc[0],3); + writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST); + for (int j = 1; j <= m ; j++) { + char tcbuf[AV_TIMECODE_STR_SIZE]; + av_timecode_make_smpte_tc_string2(tcbuf, stream->avg_frame_rate, tc[j], 0, 0); + writer_print_section_header(w, NULL, SECTION_ID_FRAME_SIDE_DATA_TIMECODE); + print_str("value", tcbuf); + writer_print_section_footer(w); + } + writer_print_section_footer(w); + } else if (sd->type == AV_FRAME_DATA_MASTERING_DISPLAY_METADATA) { + AVMasteringDisplayMetadata *metadata = (AVMasteringDisplayMetadata *)sd->data; + + if (metadata->has_primaries) { + print_q("red_x", metadata->display_primaries[0][0], '/'); + print_q("red_y", metadata->display_primaries[0][1], '/'); + print_q("green_x", metadata->display_primaries[1][0], '/'); + print_q("green_y", metadata->display_primaries[1][1], '/'); + print_q("blue_x", metadata->display_primaries[2][0], '/'); + print_q("blue_y", metadata->display_primaries[2][1], '/'); + + print_q("white_point_x", metadata->white_point[0], '/'); + print_q("white_point_y", metadata->white_point[1], '/'); + } + + if (metadata->has_luminance) { + print_q("min_luminance", metadata->min_luminance, '/'); + print_q("max_luminance", metadata->max_luminance, '/'); + } + } else if (sd->type == AV_FRAME_DATA_DYNAMIC_HDR_PLUS) { + AVDynamicHDRPlus *metadata = (AVDynamicHDRPlus *)sd->data; + print_dynamic_hdr10_plus(w, metadata); + } else if (sd->type == AV_FRAME_DATA_CONTENT_LIGHT_LEVEL) { + AVContentLightMetadata *metadata = (AVContentLightMetadata *)sd->data; + print_int("max_content", metadata->MaxCLL); + print_int("max_average", metadata->MaxFALL); + } else if (sd->type == AV_FRAME_DATA_ICC_PROFILE) { + const AVDictionaryEntry *tag = av_dict_get(sd->metadata, "name", NULL, AV_DICT_MATCH_CASE); + if (tag) + print_str(tag->key, tag->value); + print_int("size", sd->size); + } else if (sd->type == AV_FRAME_DATA_DOVI_METADATA) { + print_dovi_metadata(w, (const AVDOVIMetadata *)sd->data); + } else if (sd->type == AV_FRAME_DATA_DYNAMIC_HDR_VIVID) { + AVDynamicHDRVivid *metadata = (AVDynamicHDRVivid *)sd->data; + print_dynamic_hdr_vivid(w, metadata); + } else if (sd->type == AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT) { + print_ambient_viewing_environment(w, (const AVAmbientViewingEnvironment *)sd->data); + } else if (sd->type == AV_FRAME_DATA_FILM_GRAIN_PARAMS) { + AVFilmGrainParams *fgp = (AVFilmGrainParams *)sd->data; + print_film_grain_params(w, fgp); + } + writer_print_section_footer(w); + } + writer_print_section_footer(w); +} + static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream, AVFormatContext *fmt_ctx) { + FrameData *fd = frame->opaque_ref ? (FrameData*)frame->opaque_ref->data : NULL; AVBPrint pbuf; char val_str[128]; const char *s; - int i; av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); - writer_print_section_header(w, SECTION_ID_FRAME); + writer_print_section_header(w, NULL, SECTION_ID_FRAME); s = av_get_media_type_string(stream->codecpar->codec_type); if (s) print_str ("media_type", s); else print_str_opt("media_type", "unknown"); print_int("stream_index", stream->index); - print_int("key_frame", frame->key_frame); + print_int("key_frame", !!(frame->flags & AV_FRAME_FLAG_KEY)); print_ts ("pts", frame->pts); print_time("pts_time", frame->pts, &stream->time_base); print_ts ("pkt_dts", frame->pkt_dts); print_time("pkt_dts_time", frame->pkt_dts, &stream->time_base); print_ts ("best_effort_timestamp", frame->best_effort_timestamp); print_time("best_effort_timestamp_time", frame->best_effort_timestamp, &stream->time_base); -#if LIBAVUTIL_VERSION_MAJOR < 59 - AV_NOWARN_DEPRECATED( - print_duration_ts ("pkt_duration", frame->pkt_duration); - print_duration_time("pkt_duration_time", frame->pkt_duration, &stream->time_base); - ) -#endif print_duration_ts ("duration", frame->duration); print_duration_time("duration_time", frame->duration, &stream->time_base); - if (frame->pkt_pos != -1) print_fmt ("pkt_pos", "%"PRId64, frame->pkt_pos); - else print_str_opt("pkt_pos", "N/A"); - if (frame->pkt_size != -1) print_val ("pkt_size", frame->pkt_size, unit_byte_str); - else print_str_opt("pkt_size", "N/A"); + if (fd && fd->pkt_pos != -1) print_fmt ("pkt_pos", "%"PRId64, fd->pkt_pos); + else print_str_opt("pkt_pos", "N/A"); + if (fd && fd->pkt_size != -1) print_val ("pkt_size", fd->pkt_size, unit_byte_str); + else print_str_opt("pkt_size", "N/A"); switch (stream->codecpar->codec_type) { AVRational sar; @@ -2608,6 +2942,10 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream, case AVMEDIA_TYPE_VIDEO: print_int("width", frame->width); print_int("height", frame->height); + print_int("crop_top", frame->crop_top); + print_int("crop_bottom", frame->crop_bottom); + print_int("crop_left", frame->crop_left); + print_int("crop_right", frame->crop_right); s = av_get_pix_fmt_name(frame->format); if (s) print_str ("pix_fmt", s); else print_str_opt("pix_fmt", "unknown"); @@ -2618,14 +2956,8 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream, print_str_opt("sample_aspect_ratio", "N/A"); } print_fmt("pict_type", "%c", av_get_picture_type_char(frame->pict_type)); -#if LIBAVUTIL_VERSION_MAJOR < 59 - AV_NOWARN_DEPRECATED( - print_int("coded_picture_number", frame->coded_picture_number); - print_int("display_picture_number", frame->display_picture_number); - ) -#endif - print_int("interlaced_frame", frame->interlaced_frame); - print_int("top_field_first", frame->top_field_first); + print_int("interlaced_frame", !!(frame->flags & AV_FRAME_FLAG_INTERLACED)); + print_int("top_field_first", !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)); print_int("repeat_pict", frame->repeat_pict); print_color_range(w, frame->color_range); @@ -2652,83 +2984,8 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream, show_tags(w, frame->metadata, SECTION_ID_FRAME_TAGS); if (do_show_log) show_log(w, SECTION_ID_FRAME_LOGS, SECTION_ID_FRAME_LOG, do_show_log); - if (frame->nb_side_data) { - writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA_LIST); - for (i = 0; i < frame->nb_side_data; i++) { - AVFrameSideData *sd = frame->side_data[i]; - const char *name; - - writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA); - name = av_frame_side_data_name(sd->type); - print_str("side_data_type", name ? name : "unknown"); - if (sd->type == AV_FRAME_DATA_DISPLAYMATRIX && sd->size >= 9*4) { - double rotation = av_display_rotation_get((int32_t *)sd->data); - if (isnan(rotation)) - rotation = 0; - writer_print_integers(w, "displaymatrix", sd->data, 9, " %11d", 3, 4, 1); - print_int("rotation", rotation); - } else if (sd->type == AV_FRAME_DATA_AFD && sd->size > 0) { - print_int("active_format", *sd->data); - } else if (sd->type == AV_FRAME_DATA_GOP_TIMECODE && sd->size >= 8) { - char tcbuf[AV_TIMECODE_STR_SIZE]; - av_timecode_make_mpeg_tc_string(tcbuf, *(int64_t *)(sd->data)); - print_str("timecode", tcbuf); - } else if (sd->type == AV_FRAME_DATA_S12M_TIMECODE && sd->size == 16) { - uint32_t *tc = (uint32_t*)sd->data; - int m = FFMIN(tc[0],3); - writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA_TIMECODE_LIST); - for (int j = 1; j <= m ; j++) { - char tcbuf[AV_TIMECODE_STR_SIZE]; - av_timecode_make_smpte_tc_string2(tcbuf, stream->avg_frame_rate, tc[j], 0, 0); - writer_print_section_header(w, SECTION_ID_FRAME_SIDE_DATA_TIMECODE); - print_str("value", tcbuf); - writer_print_section_footer(w); - } - writer_print_section_footer(w); - } else if (sd->type == AV_FRAME_DATA_MASTERING_DISPLAY_METADATA) { - AVMasteringDisplayMetadata *metadata = (AVMasteringDisplayMetadata *)sd->data; - - if (metadata->has_primaries) { - print_q("red_x", metadata->display_primaries[0][0], '/'); - print_q("red_y", metadata->display_primaries[0][1], '/'); - print_q("green_x", metadata->display_primaries[1][0], '/'); - print_q("green_y", metadata->display_primaries[1][1], '/'); - print_q("blue_x", metadata->display_primaries[2][0], '/'); - print_q("blue_y", metadata->display_primaries[2][1], '/'); - - print_q("white_point_x", metadata->white_point[0], '/'); - print_q("white_point_y", metadata->white_point[1], '/'); - } - - if (metadata->has_luminance) { - print_q("min_luminance", metadata->min_luminance, '/'); - print_q("max_luminance", metadata->max_luminance, '/'); - } - } else if (sd->type == AV_FRAME_DATA_DYNAMIC_HDR_PLUS) { - AVDynamicHDRPlus *metadata = (AVDynamicHDRPlus *)sd->data; - print_dynamic_hdr10_plus(w, metadata); - } else if (sd->type == AV_FRAME_DATA_CONTENT_LIGHT_LEVEL) { - AVContentLightMetadata *metadata = (AVContentLightMetadata *)sd->data; - print_int("max_content", metadata->MaxCLL); - print_int("max_average", metadata->MaxFALL); - } else if (sd->type == AV_FRAME_DATA_ICC_PROFILE) { - const AVDictionaryEntry *tag = av_dict_get(sd->metadata, "name", NULL, AV_DICT_MATCH_CASE); - if (tag) - print_str(tag->key, tag->value); - print_int("size", sd->size); - } else if (sd->type == AV_FRAME_DATA_DOVI_METADATA) { - print_dovi_metadata(w, (const AVDOVIMetadata *)sd->data); - } else if (sd->type == AV_FRAME_DATA_DYNAMIC_HDR_VIVID) { - AVDynamicHDRVivid *metadata = (AVDynamicHDRVivid *)sd->data; - print_dynamic_hdr_vivid(w, metadata); - } else if (sd->type == AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT) { - print_ambient_viewing_environment( - w, (const AVAmbientViewingEnvironment *)sd->data); - } - writer_print_section_footer(w); - } - writer_print_section_footer(w); - } + if (frame->nb_side_data) + print_frame_side_data(w, frame, stream); writer_print_section_footer(w); @@ -2909,6 +3166,17 @@ static int read_interval_packets(WriterContext *w, InputFile *ifile, } if (do_read_frames) { int packet_new = 1; + FrameData *fd; + + pkt->opaque_ref = av_buffer_allocz(sizeof(*fd)); + if (!pkt->opaque_ref) { + ret = AVERROR(ENOMEM); + goto end; + } + fd = (FrameData*)pkt->opaque_ref->data; + fd->pkt_pos = pkt->pos; + fd->pkt_size = pkt->size; + while (process_frame(w, ifile, frame, pkt, &packet_new) > 0); } } @@ -2955,7 +3223,22 @@ static int read_packets(WriterContext *w, InputFile *ifile) return ret; } -static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx, InputStream *ist, int in_program) +static void print_dispositions(WriterContext *w, uint32_t disposition, SectionID section_id) +{ + writer_print_section_header(w, NULL, section_id); + for (int i = 0; i < sizeof(disposition) * CHAR_BIT; i++) { + const char *disposition_str = av_disposition_to_string(1U << i); + + if (disposition_str) + print_int(disposition_str, !!(disposition & (1U << i))); + } + writer_print_section_footer(w); +} + +#define IN_PROGRAM 1 +#define IN_STREAM_GROUP 2 + +static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx, InputStream *ist, int container) { AVStream *stream = ist->st; AVCodecParameters *par; @@ -2965,12 +3248,29 @@ static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_id AVRational sar, dar; AVBPrint pbuf; const AVCodecDescriptor *cd; + const SectionID section_header[] = { + SECTION_ID_STREAM, + SECTION_ID_PROGRAM_STREAM, + SECTION_ID_STREAM_GROUP_STREAM, + }; + const SectionID section_disposition[] = { + SECTION_ID_STREAM_DISPOSITION, + SECTION_ID_PROGRAM_STREAM_DISPOSITION, + SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION, + }; + const SectionID section_tags[] = { + SECTION_ID_STREAM_TAGS, + SECTION_ID_PROGRAM_STREAM_TAGS, + SECTION_ID_STREAM_GROUP_STREAM_TAGS, + }; int ret = 0; const char *profile = NULL; + av_assert0(container < FF_ARRAY_ELEMS(section_header)); + av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); - writer_print_section_header(w, in_program ? SECTION_ID_PROGRAM_STREAM : SECTION_ID_STREAM); + writer_print_section_header(w, NULL, section_header[container]); print_int("index", stream->index); @@ -2992,7 +3292,7 @@ static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_id if (!do_bitexact && (profile = avcodec_profile_name(par->codec_id, par->profile))) print_str("profile", profile); else { - if (par->profile != FF_PROFILE_UNKNOWN) { + if (par->profile != AV_PROFILE_UNKNOWN) { char profile_num[12]; snprintf(profile_num, sizeof(profile_num), "%d", par->profile); print_str("profile", profile_num); @@ -3090,16 +3390,11 @@ static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_id break; } - if (dec_ctx && dec_ctx->codec->priv_class && show_private_data) { - const AVOption *opt = NULL; - while (opt = av_opt_next(dec_ctx->priv_data,opt)) { - uint8_t *str; - if (!(opt->flags & AV_OPT_FLAG_EXPORT)) continue; - if (av_opt_get(dec_ctx->priv_data, opt->name, 0, &str) >= 0) { - print_str(opt->name, str); - av_free(str); - } - } + if (show_private_data) { + if (dec_ctx && dec_ctx->codec->priv_class) + print_private_data(w, dec_ctx->priv_data); + if (fmt_ctx->iformat->priv_class) + print_private_data(w, fmt_ctx->priv_data); } if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt ("id", "0x%x", stream->id); @@ -3136,39 +3431,24 @@ static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_id } /* Print disposition information */ -#define PRINT_DISPOSITION(flagname, name) do { \ - print_int(name, !!(stream->disposition & AV_DISPOSITION_##flagname)); \ - } while (0) - if (do_show_stream_disposition) { - writer_print_section_header(w, in_program ? SECTION_ID_PROGRAM_STREAM_DISPOSITION : SECTION_ID_STREAM_DISPOSITION); - PRINT_DISPOSITION(DEFAULT, "default"); - PRINT_DISPOSITION(DUB, "dub"); - PRINT_DISPOSITION(ORIGINAL, "original"); - PRINT_DISPOSITION(COMMENT, "comment"); - PRINT_DISPOSITION(LYRICS, "lyrics"); - PRINT_DISPOSITION(KARAOKE, "karaoke"); - PRINT_DISPOSITION(FORCED, "forced"); - PRINT_DISPOSITION(HEARING_IMPAIRED, "hearing_impaired"); - PRINT_DISPOSITION(VISUAL_IMPAIRED, "visual_impaired"); - PRINT_DISPOSITION(CLEAN_EFFECTS, "clean_effects"); - PRINT_DISPOSITION(ATTACHED_PIC, "attached_pic"); - PRINT_DISPOSITION(TIMED_THUMBNAILS, "timed_thumbnails"); - PRINT_DISPOSITION(CAPTIONS, "captions"); - PRINT_DISPOSITION(DESCRIPTIONS, "descriptions"); - PRINT_DISPOSITION(METADATA, "metadata"); - PRINT_DISPOSITION(DEPENDENT, "dependent"); - PRINT_DISPOSITION(STILL_IMAGE, "still_image"); - writer_print_section_footer(w); + av_assert0(container < FF_ARRAY_ELEMS(section_disposition)); + print_dispositions(w, stream->disposition, section_disposition[container]); } - if (do_show_stream_tags) - ret = show_tags(w, stream->metadata, in_program ? SECTION_ID_PROGRAM_STREAM_TAGS : SECTION_ID_STREAM_TAGS); + if (do_show_stream_tags) { + av_assert0(container < FF_ARRAY_ELEMS(section_tags)); + ret = show_tags(w, stream->metadata, section_tags[container]); + } - if (stream->nb_side_data) { - print_pkt_side_data(w, stream->codecpar, stream->side_data, stream->nb_side_data, - SECTION_ID_STREAM_SIDE_DATA_LIST, - SECTION_ID_STREAM_SIDE_DATA); + if (stream->codecpar->nb_coded_side_data) { + writer_print_section_header(w, NULL, SECTION_ID_STREAM_SIDE_DATA_LIST); + for (int i = 0; i < stream->codecpar->nb_coded_side_data; i++) { + print_pkt_side_data(w, stream->codecpar, &stream->codecpar->coded_side_data[i], + SECTION_ID_STREAM_SIDE_DATA); + writer_print_section_footer(w); + } + writer_print_section_footer(w); } writer_print_section_footer(w); @@ -3183,7 +3463,7 @@ static int show_streams(WriterContext *w, InputFile *ifile) AVFormatContext *fmt_ctx = ifile->fmt_ctx; int i, ret = 0; - writer_print_section_header(w, SECTION_ID_STREAMS); + writer_print_section_header(w, NULL, SECTION_ID_STREAMS); for (i = 0; i < ifile->nb_streams; i++) if (selected_streams[i]) { ret = show_stream(w, fmt_ctx, i, &ifile->streams[i], 0); @@ -3200,7 +3480,7 @@ static int show_program(WriterContext *w, InputFile *ifile, AVProgram *program) AVFormatContext *fmt_ctx = ifile->fmt_ctx; int i, ret = 0; - writer_print_section_header(w, SECTION_ID_PROGRAM); + writer_print_section_header(w, NULL, SECTION_ID_PROGRAM); print_int("program_id", program->id); print_int("program_num", program->program_num); print_int("nb_streams", program->nb_stream_indexes); @@ -3211,10 +3491,10 @@ static int show_program(WriterContext *w, InputFile *ifile, AVProgram *program) if (ret < 0) goto end; - writer_print_section_header(w, SECTION_ID_PROGRAM_STREAMS); + writer_print_section_header(w, NULL, SECTION_ID_PROGRAM_STREAMS); for (i = 0; i < program->nb_stream_indexes; i++) { if (selected_streams[program->stream_index[i]]) { - ret = show_stream(w, fmt_ctx, program->stream_index[i], &ifile->streams[program->stream_index[i]], 1); + ret = show_stream(w, fmt_ctx, program->stream_index[i], &ifile->streams[program->stream_index[i]], IN_PROGRAM); if (ret < 0) break; } @@ -3231,7 +3511,7 @@ static int show_programs(WriterContext *w, InputFile *ifile) AVFormatContext *fmt_ctx = ifile->fmt_ctx; int i, ret = 0; - writer_print_section_header(w, SECTION_ID_PROGRAMS); + writer_print_section_header(w, NULL, SECTION_ID_PROGRAMS); for (i = 0; i < fmt_ctx->nb_programs; i++) { AVProgram *program = fmt_ctx->programs[i]; if (!program) @@ -3244,16 +3524,264 @@ static int show_programs(WriterContext *w, InputFile *ifile) return ret; } +static void print_tile_grid_params(WriterContext *w, const AVStreamGroup *stg, + const AVStreamGroupTileGrid *tile_grid) +{ + writer_print_section_header(w, stg, SECTION_ID_STREAM_GROUP_COMPONENT); + print_int("nb_tiles", tile_grid->nb_tiles); + print_int("coded_width", tile_grid->coded_width); + print_int("coded_height", tile_grid->coded_height); + print_int("horizontal_offset", tile_grid->horizontal_offset); + print_int("vertical_offset", tile_grid->vertical_offset); + print_int("width", tile_grid->width); + print_int("height", tile_grid->height); + writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_SUBCOMPONENTS); + for (int i = 0; i < tile_grid->nb_tiles; i++) { + writer_print_section_header(w, "tile_offset", SECTION_ID_STREAM_GROUP_SUBCOMPONENT); + print_int("stream_index", tile_grid->offsets[i].idx); + print_int("tile_horizontal_offset", tile_grid->offsets[i].horizontal); + print_int("tile_vertical_offset", tile_grid->offsets[i].vertical); + writer_print_section_footer(w); + } + writer_print_section_footer(w); + writer_print_section_footer(w); +} + +static void print_iamf_param_definition(WriterContext *w, const char *name, + const AVIAMFParamDefinition *param, SectionID section_id) +{ + SectionID subsection_id, parameter_section_id; + subsection_id = sections[section_id].children_ids[0]; + av_assert0(subsection_id != -1); + parameter_section_id = sections[subsection_id].children_ids[0]; + av_assert0(parameter_section_id != -1); + writer_print_section_header(w, "IAMF Param Definition", section_id); + print_str("name", name); + print_int("nb_subblocks", param->nb_subblocks); + print_int("type", param->type); + print_int("parameter_id", param->parameter_id); + print_int("parameter_rate", param->parameter_rate); + print_int("duration", param->duration); + print_int("constant_subblock_duration", param->constant_subblock_duration); + if (param->nb_subblocks > 0) + writer_print_section_header(w, NULL, subsection_id); + for (int i = 0; i < param->nb_subblocks; i++) { + const void *subblock = av_iamf_param_definition_get_subblock(param, i); + switch(param->type) { + case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: { + const AVIAMFMixGain *mix = subblock; + writer_print_section_header(w, "IAMF Mix Gain Parameters", parameter_section_id); + print_int("subblock_duration", mix->subblock_duration); + print_int("animation_type", mix->animation_type); + print_q("start_point_value", mix->start_point_value, '/'); + print_q("end_point_value", mix->end_point_value, '/'); + print_q("control_point_value", mix->control_point_value, '/'); + print_q("control_point_relative_time", mix->control_point_relative_time, '/'); + writer_print_section_footer(w); // parameter_section_id + break; + } + case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: { + const AVIAMFDemixingInfo *demix = subblock; + writer_print_section_header(w, "IAMF Demixing Info", parameter_section_id); + print_int("subblock_duration", demix->subblock_duration); + print_int("dmixp_mode", demix->dmixp_mode); + writer_print_section_footer(w); // parameter_section_id + break; + } + case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: { + const AVIAMFReconGain *recon = subblock; + writer_print_section_header(w, "IAMF Recon Gain", parameter_section_id); + print_int("subblock_duration", recon->subblock_duration); + writer_print_section_footer(w); // parameter_section_id + break; + } + } + } + if (param->nb_subblocks > 0) + writer_print_section_footer(w); // subsection_id + writer_print_section_footer(w); // section_id +} + +static void print_iamf_audio_element_params(WriterContext *w, const AVStreamGroup *stg, + const AVIAMFAudioElement *audio_element) +{ + writer_print_section_header(w, stg, SECTION_ID_STREAM_GROUP_COMPONENT); + print_int("nb_layers", audio_element->nb_layers); + print_int("audio_element_type", audio_element->audio_element_type); + print_int("default_w", audio_element->default_w); + writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_SUBCOMPONENTS); + for (int i = 0; i < audio_element->nb_layers; i++) { + const AVIAMFLayer *layer = audio_element->layers[i]; + char val_str[128]; + writer_print_section_header(w, "IAMF Audio Layer", SECTION_ID_STREAM_GROUP_SUBCOMPONENT); + av_channel_layout_describe(&layer->ch_layout, val_str, sizeof(val_str)); + print_str("channel_layout", val_str); + if (audio_element->audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL) { + print_int("output_gain_flags", layer->output_gain_flags); + print_q("output_gain", layer->output_gain, '/'); + } else if (audio_element->audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_SCENE) + print_int("ambisonics_mode", layer->ambisonics_mode); + writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENT + } + if (audio_element->demixing_info) + print_iamf_param_definition(w, "demixing_info", audio_element->demixing_info, + SECTION_ID_STREAM_GROUP_SUBCOMPONENT); + if (audio_element->recon_gain_info) + print_iamf_param_definition(w, "recon_gain_info", audio_element->recon_gain_info, + SECTION_ID_STREAM_GROUP_SUBCOMPONENT); + writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENTS + writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_COMPONENT +} + +static void print_iamf_submix_params(WriterContext *w, const AVIAMFSubmix *submix) +{ + writer_print_section_header(w, "IAMF Submix", SECTION_ID_STREAM_GROUP_SUBCOMPONENT); + print_int("nb_elements", submix->nb_elements); + print_int("nb_layouts", submix->nb_layouts); + print_q("default_mix_gain", submix->default_mix_gain, '/'); + writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_PIECES); + for (int i = 0; i < submix->nb_elements; i++) { + const AVIAMFSubmixElement *element = submix->elements[i]; + writer_print_section_header(w, "IAMF Submix Element", SECTION_ID_STREAM_GROUP_PIECE); + print_int("stream_id", element->audio_element_id); + print_q("default_mix_gain", element->default_mix_gain, '/'); + print_int("headphones_rendering_mode", element->headphones_rendering_mode); + writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_SUBPIECES); + if (element->annotations) { + const AVDictionaryEntry *annotation = NULL; + writer_print_section_header(w, "IAMF Annotations", SECTION_ID_STREAM_GROUP_SUBPIECE); + while (annotation = av_dict_iterate(element->annotations, annotation)) + print_str(annotation->key, annotation->value); + writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBPIECE + } + if (element->element_mix_config) + print_iamf_param_definition(w, "element_mix_config", element->element_mix_config, + SECTION_ID_STREAM_GROUP_SUBPIECE); + writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBPIECES + writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_PIECE + } + if (submix->output_mix_config) + print_iamf_param_definition(w, "output_mix_config", submix->output_mix_config, + SECTION_ID_STREAM_GROUP_PIECE); + for (int i = 0; i < submix->nb_layouts; i++) { + const AVIAMFSubmixLayout *layout = submix->layouts[i]; + char val_str[128]; + writer_print_section_header(w, "IAMF Submix Layout", SECTION_ID_STREAM_GROUP_PIECE); + av_channel_layout_describe(&layout->sound_system, val_str, sizeof(val_str)); + print_str("sound_system", val_str); + print_q("integrated_loudness", layout->integrated_loudness, '/'); + print_q("digital_peak", layout->digital_peak, '/'); + print_q("true_peak", layout->true_peak, '/'); + print_q("dialogue_anchored_loudness", layout->dialogue_anchored_loudness, '/'); + print_q("album_anchored_loudness", layout->album_anchored_loudness, '/'); + writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_PIECE + } + writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_PIECES + writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENT +} + +static void print_iamf_mix_presentation_params(WriterContext *w, const AVStreamGroup *stg, + const AVIAMFMixPresentation *mix_presentation) +{ + writer_print_section_header(w, stg, SECTION_ID_STREAM_GROUP_COMPONENT); + print_int("nb_submixes", mix_presentation->nb_submixes); + writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_SUBCOMPONENTS); + if (mix_presentation->annotations) { + const AVDictionaryEntry *annotation = NULL; + writer_print_section_header(w, "IAMF Annotations", SECTION_ID_STREAM_GROUP_SUBCOMPONENT); + while (annotation = av_dict_iterate(mix_presentation->annotations, annotation)) + print_str(annotation->key, annotation->value); + writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENT + } + for (int i = 0; i < mix_presentation->nb_submixes; i++) + print_iamf_submix_params(w, mix_presentation->submixes[i]); + writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_SUBCOMPONENTS + writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_COMPONENT +} + +static void print_stream_group_params(WriterContext *w, AVStreamGroup *stg) +{ + writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_COMPONENTS); + if (stg->type == AV_STREAM_GROUP_PARAMS_TILE_GRID) + print_tile_grid_params(w, stg, stg->params.tile_grid); + else if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT) + print_iamf_audio_element_params(w, stg, stg->params.iamf_audio_element); + else if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION) + print_iamf_mix_presentation_params(w, stg, stg->params.iamf_mix_presentation); + writer_print_section_footer(w); // SECTION_ID_STREAM_GROUP_COMPONENTS +} + +static int show_stream_group(WriterContext *w, InputFile *ifile, AVStreamGroup *stg) +{ + AVFormatContext *fmt_ctx = ifile->fmt_ctx; + AVBPrint pbuf; + int i, ret = 0; + + av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); + writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP); + print_int("index", stg->index); + if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt ("id", "0x%"PRIx64, stg->id); + else print_str_opt("id", "N/A"); + print_int("nb_streams", stg->nb_streams); + if (stg->type != AV_STREAM_GROUP_PARAMS_NONE) + print_str("type", av_x_if_null(avformat_stream_group_name(stg->type), "unknown")); + else + print_str_opt("type", "unknown"); + if (do_show_stream_group_components) + print_stream_group_params(w, stg); + + /* Print disposition information */ + if (do_show_stream_group_disposition) + print_dispositions(w, stg->disposition, SECTION_ID_STREAM_GROUP_DISPOSITION); + + if (do_show_stream_group_tags) + ret = show_tags(w, stg->metadata, SECTION_ID_STREAM_GROUP_TAGS); + if (ret < 0) + goto end; + + writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_STREAMS); + for (i = 0; i < stg->nb_streams; i++) { + if (selected_streams[stg->streams[i]->index]) { + ret = show_stream(w, fmt_ctx, stg->streams[i]->index, &ifile->streams[stg->streams[i]->index], IN_STREAM_GROUP); + if (ret < 0) + break; + } + } + writer_print_section_footer(w); + +end: + av_bprint_finalize(&pbuf, NULL); + writer_print_section_footer(w); + return ret; +} + +static int show_stream_groups(WriterContext *w, InputFile *ifile) +{ + AVFormatContext *fmt_ctx = ifile->fmt_ctx; + int i, ret = 0; + + writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUPS); + for (i = 0; i < fmt_ctx->nb_stream_groups; i++) { + AVStreamGroup *stg = fmt_ctx->stream_groups[i]; + + ret = show_stream_group(w, ifile, stg); + if (ret < 0) + break; + } + writer_print_section_footer(w); + return ret; +} + static int show_chapters(WriterContext *w, InputFile *ifile) { AVFormatContext *fmt_ctx = ifile->fmt_ctx; int i, ret = 0; - writer_print_section_header(w, SECTION_ID_CHAPTERS); + writer_print_section_header(w, NULL, SECTION_ID_CHAPTERS); for (i = 0; i < fmt_ctx->nb_chapters; i++) { AVChapter *chapter = fmt_ctx->chapters[i]; - writer_print_section_header(w, SECTION_ID_CHAPTER); + writer_print_section_header(w, NULL, SECTION_ID_CHAPTER); print_int("id", chapter->id); print_q ("time_base", chapter->time_base, '/'); print_int("start", chapter->start); @@ -3276,10 +3804,11 @@ static int show_format(WriterContext *w, InputFile *ifile) int64_t size = fmt_ctx->pb ? avio_size(fmt_ctx->pb) : -1; int ret = 0; - writer_print_section_header(w, SECTION_ID_FORMAT); + writer_print_section_header(w, NULL, SECTION_ID_FORMAT); print_str_validate("filename", fmt_ctx->url); print_int("nb_streams", fmt_ctx->nb_streams); print_int("nb_programs", fmt_ctx->nb_programs); + print_int("nb_stream_groups", fmt_ctx->nb_stream_groups); print_str("format_name", fmt_ctx->iformat->name); if (!do_bitexact) { if (fmt_ctx->iformat->long_name) print_str ("format_long_name", fmt_ctx->iformat->long_name); @@ -3302,7 +3831,7 @@ static int show_format(WriterContext *w, InputFile *ifile) static void show_error(WriterContext *w, int err) { - writer_print_section_header(w, SECTION_ID_ERROR); + writer_print_section_header(w, NULL, SECTION_ID_ERROR); print_int("code", err); print_str("string", av_err2str(err)); writer_print_section_footer(w); @@ -3318,7 +3847,7 @@ static int open_input_file(InputFile *ifile, const char *filename, fmt_ctx = avformat_alloc_context(); if (!fmt_ctx) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); if (!av_dict_get(format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) { av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE); @@ -3340,9 +3869,13 @@ static int open_input_file(InputFile *ifile, const char *filename, av_log(NULL, AV_LOG_WARNING, "Option %s skipped - not known to demuxer.\n", t->key); if (find_stream_info) { - AVDictionary **opts = setup_find_stream_info_opts(fmt_ctx, codec_opts); + AVDictionary **opts; int orig_nb_streams = fmt_ctx->nb_streams; + err = setup_find_stream_info_opts(fmt_ctx, codec_opts, &opts); + if (err < 0) + return err; + err = avformat_find_stream_info(fmt_ctx, opts); for (i = 0; i < orig_nb_streams; i++) @@ -3385,8 +3918,12 @@ static int open_input_file(InputFile *ifile, const char *filename, continue; } { - AVDictionary *opts = filter_codec_opts(codec_opts, stream->codecpar->codec_id, - fmt_ctx, stream, codec); + AVDictionary *opts; + + err = filter_codec_opts(codec_opts, stream->codecpar->codec_id, + fmt_ctx, stream, codec, &opts); + if (err < 0) + exit(1); ist->dec_ctx = avcodec_alloc_context3(codec); if (!ist->dec_ctx) @@ -3403,6 +3940,8 @@ static int open_input_file(InputFile *ifile, const char *filename, av_dict_set(&codec_opts, "threads", "1", 0); } + av_dict_set(&opts, "flags", "+copy_opaque", AV_DICT_MULTIKEY); + ist->dec_ctx->pkt_timebase = stream->time_base; if (avcodec_open2(ist->dec_ctx, codec, &opts) < 0) { @@ -3483,7 +4022,7 @@ static int probe_file(WriterContext *wctx, const char *filename, else // (!do_show_packets && do_show_frames) section_id = SECTION_ID_FRAMES; if (do_show_frames || do_show_packets) - writer_print_section_header(wctx, section_id); + writer_print_section_header(wctx, NULL, section_id); ret = read_packets(wctx, &ifile); if (do_show_frames || do_show_packets) writer_print_section_footer(wctx); @@ -3495,6 +4034,11 @@ static int probe_file(WriterContext *wctx, const char *filename, CHECK_END; } + if (do_show_stream_groups) { + ret = show_stream_groups(wctx, &ifile); + CHECK_END; + } + if (do_show_streams) { ret = show_streams(wctx, &ifile); CHECK_END; @@ -3530,7 +4074,7 @@ static void ffprobe_show_program_version(WriterContext *w) AVBPrint pbuf; av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED); - writer_print_section_header(w, SECTION_ID_PROGRAM_VERSION); + writer_print_section_header(w, NULL, SECTION_ID_PROGRAM_VERSION); print_str("version", FFMPEG_VERSION); print_fmt("copyright", "Copyright (c) %d-%d the FFmpeg developers", program_birth_year, CONFIG_THIS_YEAR); @@ -3545,7 +4089,7 @@ static void ffprobe_show_program_version(WriterContext *w) do { \ if (CONFIG_##LIBNAME) { \ unsigned int version = libname##_version(); \ - writer_print_section_header(w, SECTION_ID_LIBRARY_VERSION); \ + writer_print_section_header(w, NULL, SECTION_ID_LIBRARY_VERSION); \ print_str("name", "lib" #libname); \ print_int("major", LIB##LIBNAME##_VERSION_MAJOR); \ print_int("minor", LIB##LIBNAME##_VERSION_MINOR); \ @@ -3558,7 +4102,7 @@ static void ffprobe_show_program_version(WriterContext *w) static void ffprobe_show_library_versions(WriterContext *w) { - writer_print_section_header(w, SECTION_ID_LIBRARY_VERSIONS); + writer_print_section_header(w, NULL, SECTION_ID_LIBRARY_VERSIONS); SHOW_LIB_VERSION(avutil, AVUTIL); SHOW_LIB_VERSION(avcodec, AVCODEC); SHOW_LIB_VERSION(avformat, AVFORMAT); @@ -3580,9 +4124,9 @@ static void ffprobe_show_pixel_formats(WriterContext *w) const AVPixFmtDescriptor *pixdesc = NULL; int i, n; - writer_print_section_header(w, SECTION_ID_PIXEL_FORMATS); + writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMATS); while (pixdesc = av_pix_fmt_desc_next(pixdesc)) { - writer_print_section_header(w, SECTION_ID_PIXEL_FORMAT); + writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT); print_str("name", pixdesc->name); print_int("nb_components", pixdesc->nb_components); if ((pixdesc->nb_components >= 3) && !(pixdesc->flags & AV_PIX_FMT_FLAG_RGB)) { @@ -3596,7 +4140,7 @@ static void ffprobe_show_pixel_formats(WriterContext *w) if (n) print_int ("bits_per_pixel", n); else print_str_opt("bits_per_pixel", "N/A"); if (do_show_pixel_format_flags) { - writer_print_section_header(w, SECTION_ID_PIXEL_FORMAT_FLAGS); + writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_FLAGS); PRINT_PIX_FMT_FLAG(BE, "big_endian"); PRINT_PIX_FMT_FLAG(PAL, "palette"); PRINT_PIX_FMT_FLAG(BITSTREAM, "bitstream"); @@ -3607,9 +4151,9 @@ static void ffprobe_show_pixel_formats(WriterContext *w) writer_print_section_footer(w); } if (do_show_pixel_format_components && (pixdesc->nb_components > 0)) { - writer_print_section_header(w, SECTION_ID_PIXEL_FORMAT_COMPONENTS); + writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_COMPONENTS); for (i = 0; i < pixdesc->nb_components; i++) { - writer_print_section_header(w, SECTION_ID_PIXEL_FORMAT_COMPONENT); + writer_print_section_header(w, NULL, SECTION_ID_PIXEL_FORMAT_COMPONENT); print_int("index", i + 1); print_int("bit_depth", pixdesc->comp[i].depth); writer_print_section_footer(w); @@ -3627,8 +4171,14 @@ static int opt_show_optional_fields(void *optctx, const char *opt, const char *a else if (!av_strcasecmp(arg, "never")) show_optional_fields = SHOW_OPTIONAL_FIELDS_NEVER; else if (!av_strcasecmp(arg, "auto")) show_optional_fields = SHOW_OPTIONAL_FIELDS_AUTO; - if (show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO && av_strcasecmp(arg, "auto")) - show_optional_fields = parse_number_or_die("show_optional_fields", arg, OPT_INT, SHOW_OPTIONAL_FIELDS_AUTO, SHOW_OPTIONAL_FIELDS_ALWAYS); + if (show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO && av_strcasecmp(arg, "auto")) { + double num; + int ret = parse_number("show_optional_fields", arg, OPT_TYPE_INT, + SHOW_OPTIONAL_FIELDS_AUTO, SHOW_OPTIONAL_FIELDS_ALWAYS, &num); + if (ret < 0) + return ret; + show_optional_fields = num; + } return 0; } @@ -3649,8 +4199,7 @@ static inline void mark_section_show_entries(SectionID section_id, section->show_all_entries = show_all_entries; if (show_all_entries) { - SectionID *id; - for (id = section->children_ids; *id != -1; id++) + for (const SectionID *id = section->children_ids; *id != -1; id++) mark_section_show_entries(*id, show_all_entries, entries); } else { av_dict_copy(§ion->entries_to_show, entries, 0); @@ -3726,17 +4275,21 @@ static int opt_show_entries(void *optctx, const char *opt, const char *arg) return ret; } -static void opt_input_file(void *optctx, const char *arg) +static int opt_input_file(void *optctx, const char *arg) { if (input_filename) { av_log(NULL, AV_LOG_ERROR, "Argument '%s' provided as input filename, but '%s' was already specified.\n", arg, input_filename); - exit_program(1); + return AVERROR(EINVAL); } if (!strcmp(arg, "-")) arg = "fd:"; - input_filename = arg; + input_filename = av_strdup(arg); + if (!input_filename) + return AVERROR(ENOMEM); + + return 0; } static int opt_input_file_i(void *optctx, const char *opt, const char *arg) @@ -3745,36 +4298,35 @@ static int opt_input_file_i(void *optctx, const char *opt, const char *arg) return 0; } -static void opt_output_file(void *optctx, const char *arg) +static int opt_output_file_o(void *optctx, const char *opt, const char *arg) { if (output_filename) { av_log(NULL, AV_LOG_ERROR, "Argument '%s' provided as output filename, but '%s' was already specified.\n", arg, output_filename); - exit_program(1); + return AVERROR(EINVAL); } if (!strcmp(arg, "-")) arg = "fd:"; - output_filename = arg; -} + output_filename = av_strdup(arg); + if (!output_filename) + return AVERROR(ENOMEM); -static int opt_output_file_o(void *optctx, const char *opt, const char *arg) -{ - opt_output_file(optctx, arg); return 0; } static int opt_print_filename(void *optctx, const char *opt, const char *arg) { - print_input_filename = arg; - return 0; + av_freep(&print_input_filename); + print_input_filename = av_strdup(arg); + return print_input_filename ? 0 : AVERROR(ENOMEM); } void show_help_default(const char *opt, const char *arg) { av_log_set_callback(log_callback_help); show_usage(); - show_help_options(options, "Main options:", 0, 0, 0); + show_help_options(options, "Main options:", 0, 0); printf("\n"); show_help_children(avformat_get_class(), AV_OPT_FLAG_DECODING_PARAM); @@ -3934,10 +4486,11 @@ static void print_section(SectionID id, int level) { const SectionID *pid; const struct section *section = §ions[id]; - printf("%c%c%c", + printf("%c%c%c%c", section->flags & SECTION_FLAG_IS_WRAPPER ? 'W' : '.', section->flags & SECTION_FLAG_IS_ARRAY ? 'A' : '.', - section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS ? 'V' : '.'); + section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS ? 'V' : '.', + section->flags & SECTION_FLAG_HAS_TYPE ? 'T' : '.'); printf("%*c %s", level * 4, ' ', section->name); if (section->unique_name) printf("/%s", section->unique_name); @@ -3950,11 +4503,12 @@ static void print_section(SectionID id, int level) static int opt_sections(void *optctx, const char *opt, const char *arg) { printf("Sections:\n" - "W.. = Section is a wrapper (contains other sections, no local entries)\n" - ".A. = Section contains an array of elements of the same type\n" - "..V = Section may contain a variable number of fields with variable keys\n" + "W... = Section is a wrapper (contains other sections, no local entries)\n" + ".A.. = Section contains an array of elements of the same type\n" + "..V. = Section may contain a variable number of fields with variable keys\n" + "...T = Section contain a unique type\n" "FLAGS NAME/UNIQUE_NAME\n" - "---\n"); + "----\n"); print_section(SECTION_ID_ROOT, 0); return 0; } @@ -3983,63 +4537,65 @@ DEFINE_OPT_SHOW_SECTION(pixel_formats, PIXEL_FORMATS) DEFINE_OPT_SHOW_SECTION(program_version, PROGRAM_VERSION) DEFINE_OPT_SHOW_SECTION(streams, STREAMS) DEFINE_OPT_SHOW_SECTION(programs, PROGRAMS) +DEFINE_OPT_SHOW_SECTION(stream_groups, STREAM_GROUPS) static const OptionDef real_options[] = { CMDUTILS_COMMON_OPTIONS - { "f", HAS_ARG, {.func_arg = opt_format}, "force format", "format" }, - { "unit", OPT_BOOL, {&show_value_unit}, "show unit of the displayed values" }, - { "prefix", OPT_BOOL, {&use_value_prefix}, "use SI prefixes for the displayed values" }, - { "byte_binary_prefix", OPT_BOOL, {&use_byte_value_binary_prefix}, + { "f", OPT_TYPE_FUNC, OPT_FUNC_ARG, {.func_arg = opt_format}, "force format", "format" }, + { "unit", OPT_TYPE_BOOL, 0, {&show_value_unit}, "show unit of the displayed values" }, + { "prefix", OPT_TYPE_BOOL, 0, {&use_value_prefix}, "use SI prefixes for the displayed values" }, + { "byte_binary_prefix", OPT_TYPE_BOOL, 0, {&use_byte_value_binary_prefix}, "use binary prefixes for byte units" }, - { "sexagesimal", OPT_BOOL, {&use_value_sexagesimal_format}, + { "sexagesimal", OPT_TYPE_BOOL, 0, {&use_value_sexagesimal_format}, "use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units" }, - { "pretty", 0, {.func_arg = opt_pretty}, + { "pretty", OPT_TYPE_FUNC, 0, {.func_arg = opt_pretty}, "prettify the format of displayed values, make it more human readable" }, - { "print_format", OPT_STRING | HAS_ARG, { &print_format }, + { "output_format", OPT_TYPE_STRING, 0, { &output_format }, "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml)", "format" }, - { "of", OPT_STRING | HAS_ARG, { &print_format }, "alias for -print_format", "format" }, - { "select_streams", OPT_STRING | HAS_ARG, { &stream_specifier }, "select the specified streams", "stream_specifier" }, - { "sections", OPT_EXIT, {.func_arg = opt_sections}, "print sections structure and section information, and exit" }, - { "show_data", OPT_BOOL, { &do_show_data }, "show packets data" }, - { "show_data_hash", OPT_STRING | HAS_ARG, { &show_data_hash }, "show packets data hash" }, - { "show_error", 0, { .func_arg = &opt_show_error }, "show probing error" }, - { "show_format", 0, { .func_arg = &opt_show_format }, "show format/container info" }, - { "show_frames", 0, { .func_arg = &opt_show_frames }, "show frames info" }, - { "show_entries", HAS_ARG, {.func_arg = opt_show_entries}, + { "print_format", OPT_TYPE_STRING, 0, { &output_format }, "alias for -output_format (deprecated)" }, + { "of", OPT_TYPE_STRING, 0, { &output_format }, "alias for -output_format", "format" }, + { "select_streams", OPT_TYPE_STRING, 0, { &stream_specifier }, "select the specified streams", "stream_specifier" }, + { "sections", OPT_TYPE_FUNC, OPT_EXIT, {.func_arg = opt_sections}, "print sections structure and section information, and exit" }, + { "show_data", OPT_TYPE_BOOL, 0, { &do_show_data }, "show packets data" }, + { "show_data_hash", OPT_TYPE_STRING, 0, { &show_data_hash }, "show packets data hash" }, + { "show_error", OPT_TYPE_FUNC, 0, { .func_arg = &opt_show_error }, "show probing error" }, + { "show_format", OPT_TYPE_FUNC, 0, { .func_arg = &opt_show_format }, "show format/container info" }, + { "show_frames", OPT_TYPE_FUNC, 0, { .func_arg = &opt_show_frames }, "show frames info" }, + { "show_entries", OPT_TYPE_FUNC, OPT_FUNC_ARG, {.func_arg = opt_show_entries}, "show a set of specified entries", "entry_list" }, #if HAVE_THREADS - { "show_log", OPT_INT|HAS_ARG, { &do_show_log }, "show log" }, + { "show_log", OPT_TYPE_INT, 0, { &do_show_log }, "show log" }, #endif - { "show_packets", 0, { .func_arg = &opt_show_packets }, "show packets info" }, - { "show_programs", 0, { .func_arg = &opt_show_programs }, "show programs info" }, - { "show_streams", 0, { .func_arg = &opt_show_streams }, "show streams info" }, - { "show_chapters", 0, { .func_arg = &opt_show_chapters }, "show chapters info" }, - { "count_frames", OPT_BOOL, { &do_count_frames }, "count the number of frames per stream" }, - { "count_packets", OPT_BOOL, { &do_count_packets }, "count the number of packets per stream" }, - { "show_program_version", 0, { .func_arg = &opt_show_program_version }, "show ffprobe version" }, - { "show_library_versions", 0, { .func_arg = &opt_show_library_versions }, "show library versions" }, - { "show_versions", 0, { .func_arg = &opt_show_versions }, "show program and library versions" }, - { "show_pixel_formats", 0, { .func_arg = &opt_show_pixel_formats }, "show pixel format descriptions" }, - { "show_optional_fields", HAS_ARG, { .func_arg = &opt_show_optional_fields }, "show optional fields" }, - { "show_private_data", OPT_BOOL, { &show_private_data }, "show private data" }, - { "private", OPT_BOOL, { &show_private_data }, "same as show_private_data" }, - { "bitexact", OPT_BOOL, {&do_bitexact}, "force bitexact output" }, - { "read_intervals", HAS_ARG, {.func_arg = opt_read_intervals}, "set read intervals", "read_intervals" }, - { "i", HAS_ARG, {.func_arg = opt_input_file_i}, "read specified file", "input_file"}, - { "o", HAS_ARG, {.func_arg = opt_output_file_o}, "write to specified output", "output_file"}, - { "print_filename", HAS_ARG, {.func_arg = opt_print_filename}, "override the printed input filename", "print_file"}, - { "find_stream_info", OPT_BOOL | OPT_INPUT | OPT_EXPERT, { &find_stream_info }, + { "show_packets", OPT_TYPE_FUNC, 0, { .func_arg = &opt_show_packets }, "show packets info" }, + { "show_programs", OPT_TYPE_FUNC, 0, { .func_arg = &opt_show_programs }, "show programs info" }, + { "show_stream_groups", OPT_TYPE_FUNC, 0, { .func_arg = &opt_show_stream_groups }, "show stream groups info" }, + { "show_streams", OPT_TYPE_FUNC, 0, { .func_arg = &opt_show_streams }, "show streams info" }, + { "show_chapters", OPT_TYPE_FUNC, 0, { .func_arg = &opt_show_chapters }, "show chapters info" }, + { "count_frames", OPT_TYPE_BOOL, 0, { &do_count_frames }, "count the number of frames per stream" }, + { "count_packets", OPT_TYPE_BOOL, 0, { &do_count_packets }, "count the number of packets per stream" }, + { "show_program_version", OPT_TYPE_FUNC, 0, { .func_arg = &opt_show_program_version }, "show ffprobe version" }, + { "show_library_versions", OPT_TYPE_FUNC, 0, { .func_arg = &opt_show_library_versions }, "show library versions" }, + { "show_versions", OPT_TYPE_FUNC, 0, { .func_arg = &opt_show_versions }, "show program and library versions" }, + { "show_pixel_formats", OPT_TYPE_FUNC, 0, { .func_arg = &opt_show_pixel_formats }, "show pixel format descriptions" }, + { "show_optional_fields", OPT_TYPE_FUNC, OPT_FUNC_ARG, { .func_arg = &opt_show_optional_fields }, "show optional fields" }, + { "show_private_data", OPT_TYPE_BOOL, 0, { &show_private_data }, "show private data" }, + { "private", OPT_TYPE_BOOL, 0, { &show_private_data }, "same as show_private_data" }, + { "bitexact", OPT_TYPE_BOOL, 0, {&do_bitexact}, "force bitexact output" }, + { "read_intervals", OPT_TYPE_FUNC, OPT_FUNC_ARG, {.func_arg = opt_read_intervals}, "set read intervals", "read_intervals" }, + { "i", OPT_TYPE_FUNC, OPT_FUNC_ARG, {.func_arg = opt_input_file_i}, "read specified file", "input_file"}, + { "o", OPT_TYPE_FUNC, OPT_FUNC_ARG, {.func_arg = opt_output_file_o}, "write to specified output", "output_file"}, + { "print_filename", OPT_TYPE_FUNC, OPT_FUNC_ARG, {.func_arg = opt_print_filename}, "override the printed input filename", "print_file"}, + { "find_stream_info", OPT_TYPE_BOOL, OPT_INPUT | OPT_EXPERT, { &find_stream_info }, "read and decode the streams to fill missing information with heuristics" }, { NULL, }, }; static inline int check_section_show_entries(int section_id) { - int *id; struct section *section = §ions[section_id]; if (sections[section_id].show_all_entries || sections[section_id].entries_to_show) return 1; - for (id = section->children_ids; *id != -1; id++) + for (const SectionID *id = section->children_ids; *id != -1; id++) if (check_section_show_entries(*id)) return 1; return 0; @@ -4067,7 +4623,6 @@ int main(int argc, char **argv) } #endif av_log_set_flags(AV_LOG_SKIP_REPEATED); - register_exit(ffprobe_cleanup); options = real_options; parse_loglevel(argc, argv, options); @@ -4077,7 +4632,11 @@ int main(int argc, char **argv) #endif show_banner(argc, argv, options); - parse_options(NULL, argc, argv, options, opt_input_file); + ret = parse_options(NULL, argc, argv, options, opt_input_file); + if (ret < 0) { + ret = (ret == AVERROR_EXIT) ? 0 : ret; + goto end; + } if (do_show_log) av_log_set_callback(log_callback); @@ -4094,16 +4653,22 @@ int main(int argc, char **argv) SET_DO_SHOW(PIXEL_FORMAT_COMPONENTS, pixel_format_components); SET_DO_SHOW(PROGRAM_VERSION, program_version); SET_DO_SHOW(PROGRAMS, programs); + SET_DO_SHOW(STREAM_GROUP_DISPOSITION, stream_group_disposition); + SET_DO_SHOW(STREAM_GROUPS, stream_groups); + SET_DO_SHOW(STREAM_GROUP_COMPONENTS, stream_group_components); SET_DO_SHOW(STREAMS, streams); SET_DO_SHOW(STREAM_DISPOSITION, stream_disposition); SET_DO_SHOW(PROGRAM_STREAM_DISPOSITION, stream_disposition); + SET_DO_SHOW(STREAM_GROUP_STREAM_DISPOSITION, stream_disposition); SET_DO_SHOW(CHAPTER_TAGS, chapter_tags); SET_DO_SHOW(FORMAT_TAGS, format_tags); SET_DO_SHOW(FRAME_TAGS, frame_tags); SET_DO_SHOW(PROGRAM_TAGS, program_tags); + SET_DO_SHOW(STREAM_GROUP_TAGS, stream_group_tags); SET_DO_SHOW(STREAM_TAGS, stream_tags); SET_DO_SHOW(PROGRAM_STREAM_TAGS, stream_tags); + SET_DO_SHOW(STREAM_GROUP_STREAM_TAGS, stream_tags); SET_DO_SHOW(PACKET_TAGS, packet_tags); if (do_bitexact && (do_show_program_version || do_show_library_versions)) { @@ -4116,13 +4681,13 @@ int main(int argc, char **argv) writer_register_all(); - if (!print_format) - print_format = av_strdup("default"); - if (!print_format) { + if (!output_format) + output_format = av_strdup("default"); + if (!output_format) { ret = AVERROR(ENOMEM); goto end; } - w_name = av_strtok(print_format, "=", &buf); + w_name = av_strtok(output_format, "=", &buf); if (!w_name) { av_log(NULL, AV_LOG_ERROR, "No name specified for the output format\n"); @@ -4158,7 +4723,7 @@ int main(int argc, char **argv) if (w == &xml_writer) wctx->string_validation_utf8_flags |= AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES; - writer_print_section_header(wctx, SECTION_ID_ROOT); + writer_print_section_header(wctx, NULL, SECTION_ID_ROOT); if (do_show_program_version) ffprobe_show_program_version(wctx); @@ -4168,7 +4733,7 @@ int main(int argc, char **argv) ffprobe_show_pixel_formats(wctx); if (!input_filename && - ((do_show_format || do_show_programs || do_show_streams || do_show_chapters || do_show_packets || do_show_error) || + ((do_show_format || do_show_programs || do_show_stream_groups || do_show_streams || do_show_chapters || do_show_packets || do_show_error) || (!do_show_program_version && !do_show_library_versions && !do_show_pixel_formats))) { show_usage(); av_log(NULL, AV_LOG_ERROR, "You have to specify one input file.\n"); @@ -4191,7 +4756,10 @@ int main(int argc, char **argv) } end: - av_freep(&print_format); + av_freep(&output_format); + av_freep(&output_filename); + av_freep(&input_filename); + av_freep(&print_input_filename); av_freep(&read_intervals); av_hash_freep(&hash); @@ -4201,5 +4769,9 @@ int main(int argc, char **argv) avformat_network_deinit(); +#if HAVE_THREADS + pthread_mutex_destroy(&log_mutex); +#endif + return ret < 0; } diff --git a/fftools/opt_common.c b/fftools/opt_common.c index 8a06df82df2..9d2d5184a08 100644 --- a/fftools/opt_common.c +++ b/fftools/opt_common.c @@ -291,8 +291,6 @@ static void print_codec(const AVCodec *c) printf("delay "); if (c->capabilities & AV_CODEC_CAP_SMALL_LAST_FRAME) printf("small "); - if (c->capabilities & AV_CODEC_CAP_SUBFRAMES) - printf("subframes "); if (c->capabilities & AV_CODEC_CAP_EXPERIMENTAL) printf("exp "); if (c->capabilities & AV_CODEC_CAP_CHANNEL_CONF) @@ -617,10 +615,10 @@ static void print_codecs_for_id(enum AVCodecID id, int encoder) void *iter = NULL; const AVCodec *codec; - printf(" (%s: ", encoder ? "encoders" : "decoders"); + printf(" (%s:", encoder ? "encoders" : "decoders"); while ((codec = next_codec_for_id(id, &iter, encoder))) - printf("%s ", codec->name); + printf(" %s", codec->name); printf(")"); } @@ -634,7 +632,7 @@ static int compare_codec_desc(const void *a, const void *b) strcmp((*da)->name, (*db)->name); } -static unsigned get_codecs_sorted(const AVCodecDescriptor ***rcodecs) +static int get_codecs_sorted(const AVCodecDescriptor ***rcodecs) { const AVCodecDescriptor *desc = NULL; const AVCodecDescriptor **codecs; @@ -643,7 +641,7 @@ static unsigned get_codecs_sorted(const AVCodecDescriptor ***rcodecs) while ((desc = avcodec_descriptor_next(desc))) nb_codecs++; if (!(codecs = av_calloc(nb_codecs, sizeof(*codecs)))) - report_and_exit(AVERROR(ENOMEM)); + return AVERROR(ENOMEM); desc = NULL; while ((desc = avcodec_descriptor_next(desc))) codecs[i++] = desc; @@ -668,7 +666,11 @@ static char get_media_type_char(enum AVMediaType type) int show_codecs(void *optctx, const char *opt, const char *arg) { const AVCodecDescriptor **codecs; - unsigned i, nb_codecs = get_codecs_sorted(&codecs); + unsigned i; + int nb_codecs = get_codecs_sorted(&codecs); + + if (nb_codecs < 0) + return nb_codecs; printf("Codecs:\n" " D..... = Decoding supported\n" @@ -690,14 +692,13 @@ int show_codecs(void *optctx, const char *opt, const char *arg) if (strstr(desc->name, "_deprecated")) continue; - printf(" "); - printf(avcodec_find_decoder(desc->id) ? "D" : "."); - printf(avcodec_find_encoder(desc->id) ? "E" : "."); - - printf("%c", get_media_type_char(desc->type)); - printf((desc->props & AV_CODEC_PROP_INTRA_ONLY) ? "I" : "."); - printf((desc->props & AV_CODEC_PROP_LOSSY) ? "L" : "."); - printf((desc->props & AV_CODEC_PROP_LOSSLESS) ? "S" : "."); + printf(" %c%c%c%c%c%c", + avcodec_find_decoder(desc->id) ? 'D' : '.', + avcodec_find_encoder(desc->id) ? 'E' : '.', + get_media_type_char(desc->type), + (desc->props & AV_CODEC_PROP_INTRA_ONLY) ? 'I' : '.', + (desc->props & AV_CODEC_PROP_LOSSY) ? 'L' : '.', + (desc->props & AV_CODEC_PROP_LOSSLESS) ? 'S' : '.'); printf(" %-20s %s", desc->name, desc->long_name ? desc->long_name : ""); @@ -723,10 +724,13 @@ int show_codecs(void *optctx, const char *opt, const char *arg) return 0; } -static void print_codecs(int encoder) +static int print_codecs(int encoder) { const AVCodecDescriptor **codecs; - unsigned i, nb_codecs = get_codecs_sorted(&codecs); + int i, nb_codecs = get_codecs_sorted(&codecs); + + if (nb_codecs < 0) + return nb_codecs; printf("%s:\n" " V..... = Video\n" @@ -745,12 +749,13 @@ static void print_codecs(int encoder) void *iter = NULL; while ((codec = next_codec_for_id(desc->id, &iter, encoder))) { - printf(" %c", get_media_type_char(desc->type)); - printf((codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) ? "F" : "."); - printf((codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) ? "S" : "."); - printf((codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) ? "X" : "."); - printf((codec->capabilities & AV_CODEC_CAP_DRAW_HORIZ_BAND)?"B" : "."); - printf((codec->capabilities & AV_CODEC_CAP_DR1) ? "D" : "."); + printf(" %c%c%c%c%c%c", + get_media_type_char(desc->type), + (codec->capabilities & AV_CODEC_CAP_FRAME_THREADS) ? 'F' : '.', + (codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) ? 'S' : '.', + (codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) ? 'X' : '.', + (codec->capabilities & AV_CODEC_CAP_DRAW_HORIZ_BAND) ? 'B' : '.', + (codec->capabilities & AV_CODEC_CAP_DR1) ? 'D' : '.'); printf(" %-20s %s", codec->name, codec->long_name ? codec->long_name : ""); if (strcmp(codec->name, desc->name)) @@ -760,18 +765,17 @@ static void print_codecs(int encoder) } } av_free(codecs); + return 0; } int show_decoders(void *optctx, const char *opt, const char *arg) { - print_codecs(0); - return 0; + return print_codecs(0); } int show_encoders(void *optctx, const char *opt, const char *arg) { - print_codecs(1); - return 0; + return print_codecs(1); } int show_bsfs(void *optctx, const char *opt, const char *arg) @@ -850,15 +854,22 @@ static int show_formats_devices(void *optctx, const char *opt, const char *arg, const AVOutputFormat *ofmt = NULL; const char *last_name; int is_dev; + const char *is_device_placeholder = device_only ? "" : "."; + + printf("%s:\n" + " D.%s = Demuxing supported\n" + " .E%s = Muxing supported\n" + "%s" + " ---\n", + device_only ? "Devices" : "Formats", + is_device_placeholder, is_device_placeholder, + device_only ? "": " ..d = Is a device\n"); - printf("%s\n" - " D. = Demuxing supported\n" - " .E = Muxing supported\n" - " --\n", device_only ? "Devices:" : "File formats:"); last_name = "000"; for (;;) { int decode = 0; int encode = 0; + int device = 0; const char *name = NULL; const char *long_name = NULL; @@ -873,6 +884,7 @@ static int show_formats_devices(void *optctx, const char *opt, const char *arg, name = ofmt->name; long_name = ofmt->long_name; encode = 1; + device = is_dev; } } } @@ -887,20 +899,24 @@ static int show_formats_devices(void *optctx, const char *opt, const char *arg, name = ifmt->name; long_name = ifmt->long_name; encode = 0; + device = is_dev; } - if (name && strcmp(ifmt->name, name) == 0) + if (name && strcmp(ifmt->name, name) == 0) { decode = 1; + device = is_dev; + } } } if (!name) break; last_name = name; - printf(" %c%c %-15s %s\n", + printf(" %c%c%s %-15s %s\n", decode ? 'D' : ' ', encode ? 'E' : ' ', + device_only ? "" : (device ? "d" : " "), name, - long_name ? long_name:" "); + long_name ? long_name : " "); } return 0; } @@ -1161,7 +1177,10 @@ int init_report(const char *env, FILE **file) report_file_level = strtol(val, &tail, 10); if (*tail) { av_log(NULL, AV_LOG_FATAL, "Invalid report file level\n"); - exit_program(1); + av_free(key); + av_free(val); + av_free(filename_template); + return AVERROR(EINVAL); } envlevel = 1; } else { @@ -1221,7 +1240,7 @@ int opt_max_alloc(void *optctx, const char *opt, const char *arg) max = strtol(arg, &tail, 10); if (*tail) { av_log(NULL, AV_LOG_FATAL, "Invalid max_alloc \"%s\".\n", arg); - exit_program(1); + return AVERROR(EINVAL); } av_max_alloc(max); return 0; @@ -1295,7 +1314,7 @@ int opt_loglevel(void *optctx, const char *opt, const char *arg) "Possible levels are numbers or:\n", arg); for (i = 0; i < FF_ARRAY_ELEMS(log_levels); i++) av_log(NULL, AV_LOG_FATAL, "\"%s\"\n", log_levels[i].name); - exit_program(1); + return AVERROR(EINVAL); } end: diff --git a/fftools/opt_common.h b/fftools/opt_common.h index ea1d16e769a..9bb5268472e 100644 --- a/fftools/opt_common.h +++ b/fftools/opt_common.h @@ -41,9 +41,9 @@ int show_sources(void *optctx, const char *opt, const char *arg); #if CONFIG_AVDEVICE #define CMDUTILS_COMMON_OPTIONS_AVDEVICE \ - { "sources" , OPT_EXIT | HAS_ARG, { .func_arg = show_sources }, \ + { "sources" , OPT_TYPE_FUNC, OPT_EXIT | OPT_FUNC_ARG | OPT_EXPERT, { .func_arg = show_sources }, \ "list sources of the input device", "device" }, \ - { "sinks" , OPT_EXIT | HAS_ARG, { .func_arg = show_sinks }, \ + { "sinks" , OPT_TYPE_FUNC, OPT_EXIT | OPT_FUNC_ARG | OPT_EXPERT, { .func_arg = show_sinks }, \ "list sinks of the output device", "device" }, \ #else @@ -197,35 +197,35 @@ int opt_cpuflags(void *optctx, const char *opt, const char *arg); int opt_cpucount(void *optctx, const char *opt, const char *arg); #define CMDUTILS_COMMON_OPTIONS \ - { "L", OPT_EXIT, { .func_arg = show_license }, "show license" }, \ - { "h", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" }, \ - { "?", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" }, \ - { "help", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" }, \ - { "-help", OPT_EXIT, { .func_arg = show_help }, "show help", "topic" }, \ - { "version", OPT_EXIT, { .func_arg = show_version }, "show version" }, \ - { "buildconf", OPT_EXIT, { .func_arg = show_buildconf }, "show build configuration" }, \ - { "formats", OPT_EXIT, { .func_arg = show_formats }, "show available formats" }, \ - { "muxers", OPT_EXIT, { .func_arg = show_muxers }, "show available muxers" }, \ - { "demuxers", OPT_EXIT, { .func_arg = show_demuxers }, "show available demuxers" }, \ - { "devices", OPT_EXIT, { .func_arg = show_devices }, "show available devices" }, \ - { "codecs", OPT_EXIT, { .func_arg = show_codecs }, "show available codecs" }, \ - { "decoders", OPT_EXIT, { .func_arg = show_decoders }, "show available decoders" }, \ - { "encoders", OPT_EXIT, { .func_arg = show_encoders }, "show available encoders" }, \ - { "bsfs", OPT_EXIT, { .func_arg = show_bsfs }, "show available bit stream filters" }, \ - { "protocols", OPT_EXIT, { .func_arg = show_protocols }, "show available protocols" }, \ - { "filters", OPT_EXIT, { .func_arg = show_filters }, "show available filters" }, \ - { "pix_fmts", OPT_EXIT, { .func_arg = show_pix_fmts }, "show available pixel formats" }, \ - { "layouts", OPT_EXIT, { .func_arg = show_layouts }, "show standard channel layouts" }, \ - { "sample_fmts", OPT_EXIT, { .func_arg = show_sample_fmts }, "show available audio sample formats" }, \ - { "dispositions", OPT_EXIT, { .func_arg = show_dispositions}, "show available stream dispositions" }, \ - { "colors", OPT_EXIT, { .func_arg = show_colors }, "show available color names" }, \ - { "loglevel", HAS_ARG, { .func_arg = opt_loglevel }, "set logging level", "loglevel" }, \ - { "v", HAS_ARG, { .func_arg = opt_loglevel }, "set logging level", "loglevel" }, \ - { "report", 0, { .func_arg = opt_report }, "generate a report" }, \ - { "max_alloc", HAS_ARG, { .func_arg = opt_max_alloc }, "set maximum size of a single allocated block", "bytes" }, \ - { "cpuflags", HAS_ARG | OPT_EXPERT, { .func_arg = opt_cpuflags }, "force specific cpu flags", "flags" }, \ - { "cpucount", HAS_ARG | OPT_EXPERT, { .func_arg = opt_cpucount }, "force specific cpu count", "count" }, \ - { "hide_banner", OPT_BOOL | OPT_EXPERT, {&hide_banner}, "do not show program banner", "hide_banner" }, \ + { "L", OPT_TYPE_FUNC, OPT_EXIT, { .func_arg = show_license }, "show license" }, \ + { "h", OPT_TYPE_FUNC, OPT_EXIT, { .func_arg = show_help }, "show help", "topic" }, \ + { "?", OPT_TYPE_FUNC, OPT_EXIT | OPT_EXPERT, { .func_arg = show_help }, "show help", "topic" }, \ + { "help", OPT_TYPE_FUNC, OPT_EXIT | OPT_EXPERT, { .func_arg = show_help }, "show help", "topic" }, \ + { "-help", OPT_TYPE_FUNC, OPT_EXIT | OPT_EXPERT, { .func_arg = show_help }, "show help", "topic" }, \ + { "version", OPT_TYPE_FUNC, OPT_EXIT, { .func_arg = show_version }, "show version" }, \ + { "buildconf", OPT_TYPE_FUNC, OPT_EXIT | OPT_EXPERT, { .func_arg = show_buildconf }, "show build configuration" }, \ + { "formats", OPT_TYPE_FUNC, OPT_EXIT | OPT_EXPERT, { .func_arg = show_formats }, "show available formats" }, \ + { "muxers", OPT_TYPE_FUNC, OPT_EXIT, { .func_arg = show_muxers }, "show available muxers" }, \ + { "demuxers", OPT_TYPE_FUNC, OPT_EXIT, { .func_arg = show_demuxers }, "show available demuxers" }, \ + { "devices", OPT_TYPE_FUNC, OPT_EXIT, { .func_arg = show_devices }, "show available devices" }, \ + { "codecs", OPT_TYPE_FUNC, OPT_EXIT | OPT_EXPERT, { .func_arg = show_codecs }, "show available codecs" }, \ + { "decoders", OPT_TYPE_FUNC, OPT_EXIT, { .func_arg = show_decoders }, "show available decoders" }, \ + { "encoders", OPT_TYPE_FUNC, OPT_EXIT, { .func_arg = show_encoders }, "show available encoders" }, \ + { "bsfs", OPT_TYPE_FUNC, OPT_EXIT | OPT_EXPERT, { .func_arg = show_bsfs }, "show available bit stream filters" }, \ + { "protocols", OPT_TYPE_FUNC, OPT_EXIT | OPT_EXPERT, { .func_arg = show_protocols }, "show available protocols" }, \ + { "filters", OPT_TYPE_FUNC, OPT_EXIT, { .func_arg = show_filters }, "show available filters" }, \ + { "pix_fmts", OPT_TYPE_FUNC, OPT_EXIT, { .func_arg = show_pix_fmts }, "show available pixel formats" }, \ + { "layouts", OPT_TYPE_FUNC, OPT_EXIT, { .func_arg = show_layouts }, "show standard channel layouts" }, \ + { "sample_fmts", OPT_TYPE_FUNC, OPT_EXIT, { .func_arg = show_sample_fmts }, "show available audio sample formats" }, \ + { "dispositions", OPT_TYPE_FUNC, OPT_EXIT | OPT_EXPERT, { .func_arg = show_dispositions}, "show available stream dispositions" }, \ + { "colors", OPT_TYPE_FUNC, OPT_EXIT | OPT_EXPERT, { .func_arg = show_colors }, "show available color names" }, \ + { "loglevel", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, { .func_arg = opt_loglevel }, "set logging level", "loglevel" }, \ + { "v", OPT_TYPE_FUNC, OPT_FUNC_ARG, { .func_arg = opt_loglevel }, "set logging level", "loglevel" }, \ + { "report", OPT_TYPE_FUNC, OPT_EXPERT, { .func_arg = opt_report }, "generate a report" }, \ + { "max_alloc", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, { .func_arg = opt_max_alloc }, "set maximum size of a single allocated block", "bytes" }, \ + { "cpuflags", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, { .func_arg = opt_cpuflags }, "force specific cpu flags", "flags" }, \ + { "cpucount", OPT_TYPE_FUNC, OPT_FUNC_ARG | OPT_EXPERT, { .func_arg = opt_cpucount }, "force specific cpu count", "count" }, \ + { "hide_banner", OPT_TYPE_BOOL, OPT_EXPERT, {&hide_banner}, "do not show program banner", "hide_banner" }, \ CMDUTILS_COMMON_OPTIONS_AVDEVICE \ #endif /* FFTOOLS_OPT_COMMON_H */ diff --git a/fftools/sync_queue.c b/fftools/sync_queue.c index c2b23ee4f5a..0e35b5b1cbf 100644 --- a/fftools/sync_queue.c +++ b/fftools/sync_queue.c @@ -20,18 +20,59 @@ #include #include "libavutil/avassert.h" +#include "libavutil/channel_layout.h" +#include "libavutil/cpu.h" #include "libavutil/error.h" #include "libavutil/fifo.h" #include "libavutil/mathematics.h" #include "libavutil/mem.h" +#include "libavutil/samplefmt.h" +#include "libavutil/timestamp.h" #include "objpool.h" #include "sync_queue.h" +/* + * How this works: + * -------------- + * time: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 + * ------------------------------------------------------------------- + * | | | | | | | | | | | | | | + * | ┌───┐┌────────┐┌───┐┌─────────────┐ + * stream 0| │d=1││ d=2 ││d=1││ d=3 │ + * | └───┘└────────┘└───┘└─────────────┘ + * ┌───┐ ┌───────────────────────┐ + * stream 1│d=1│ │ d=5 │ + * └───┘ └───────────────────────┘ + * | ┌───┐┌───┐┌───┐┌───┐ + * stream 2| │d=1││d=1││d=1││d=1│ <- stream 2 is the head stream of the queue + * | └───┘└───┘└───┘└───┘ + * ^ ^ + * [stream 2 tail] [stream 2 head] + * + * We have N streams (N=3 in the diagram), each stream is a FIFO. The *tail* of + * each FIFO is the frame with smallest end time, the *head* is the frame with + * the largest end time. Frames submitted to the queue with sq_send() are placed + * after the head, frames returned to the caller with sq_receive() are taken + * from the tail. + * + * The head stream of the whole queue (SyncQueue.head_stream) is the limiting + * stream with the *smallest* head timestamp, i.e. the stream whose source lags + * furthest behind all other streams. It determines which frames can be output + * from the queue. + * + * In the diagram, the head stream is 2, because it head time is t=5, while + * streams 0 and 1 end at t=8 and t=9 respectively. All frames that _end_ at + * or before t=5 can be output, i.e. the first 3 frames from stream 0, first + * frame from stream 1, and all 4 frames from stream 2. + */ + typedef struct SyncQueueStream { AVFifo *fifo; AVRational tb; + /* number of audio samples in fifo */ + uint64_t samples_queued; /* stream head: largest timestamp seen */ int64_t head_ts; int limiting; @@ -39,12 +80,16 @@ typedef struct SyncQueueStream { int finished; uint64_t frames_sent; + uint64_t samples_sent; uint64_t frames_max; + int frame_samples; } SyncQueueStream; struct SyncQueue { enum SyncQueueType type; + void *logctx; + /* no more frames will be sent for any stream */ int finished; /* sync head: the stream with the _smallest_ head timestamp @@ -61,6 +106,10 @@ struct SyncQueue { // pool of preallocated frames to avoid constant allocations ObjPool *pool; + + int have_limiting; + + uintptr_t align_mask; }; static void frame_move(const SyncQueue *sq, SyncQueueFrame dst, @@ -72,22 +121,62 @@ static void frame_move(const SyncQueue *sq, SyncQueueFrame dst, av_frame_move_ref(dst.f, src.f); } -static int64_t frame_ts(const SyncQueue *sq, SyncQueueFrame frame) +/** + * Compute the end timestamp of a frame. If nb_samples is provided, consider + * the frame to have this number of audio samples, otherwise use frame duration. + */ +static int64_t frame_end(const SyncQueue *sq, SyncQueueFrame frame, int nb_samples) { + if (nb_samples) { + int64_t d = av_rescale_q(nb_samples, (AVRational){ 1, frame.f->sample_rate}, + frame.f->time_base); + return frame.f->pts + d; + } + return (sq->type == SYNC_QUEUE_PACKETS) ? frame.p->pts + frame.p->duration : frame.f->pts + frame.f->duration; } +static int frame_samples(const SyncQueue *sq, SyncQueueFrame frame) +{ + return (sq->type == SYNC_QUEUE_PACKETS) ? 0 : frame.f->nb_samples; +} + static int frame_null(const SyncQueue *sq, SyncQueueFrame frame) { return (sq->type == SYNC_QUEUE_PACKETS) ? (frame.p == NULL) : (frame.f == NULL); } +static void tb_update(const SyncQueue *sq, SyncQueueStream *st, + const SyncQueueFrame frame) +{ + AVRational tb = (sq->type == SYNC_QUEUE_PACKETS) ? + frame.p->time_base : frame.f->time_base; + + av_assert0(tb.num > 0 && tb.den > 0); + + if (tb.num == st->tb.num && tb.den == st->tb.den) + return; + + // timebase should not change after the first frame + av_assert0(!av_fifo_can_read(st->fifo)); + + if (st->head_ts != AV_NOPTS_VALUE) + st->head_ts = av_rescale_q(st->head_ts, st->tb, tb); + + st->tb = tb; +} + static void finish_stream(SyncQueue *sq, unsigned int stream_idx) { SyncQueueStream *st = &sq->streams[stream_idx]; + if (!st->finished) + av_log(sq->logctx, AV_LOG_DEBUG, + "sq: finish %u; head ts %s\n", stream_idx, + av_ts2timestr(st->head_ts, &st->tb)); + st->finished = 1; if (st->limiting && st->head_ts != AV_NOPTS_VALUE) { @@ -105,8 +194,14 @@ static void finish_stream(SyncQueue *sq, unsigned int stream_idx) for (unsigned int i = 0; i < sq->nb_streams; i++) { SyncQueueStream *st1 = &sq->streams[i]; if (st != st1 && st1->head_ts != AV_NOPTS_VALUE && - av_compare_ts(st->head_ts, st->tb, st1->head_ts, st1->tb) <= 0) + av_compare_ts(st->head_ts, st->tb, st1->head_ts, st1->tb) <= 0) { + if (!st1->finished) + av_log(sq->logctx, AV_LOG_DEBUG, + "sq: finish secondary %u; head ts %s\n", i, + av_ts2timestr(st1->head_ts, &st1->tb)); + st1->finished = 1; + } } } @@ -116,21 +211,32 @@ static void finish_stream(SyncQueue *sq, unsigned int stream_idx) return; } sq->finished = 1; + + av_log(sq->logctx, AV_LOG_DEBUG, "sq: finish queue\n"); } static void queue_head_update(SyncQueue *sq) { + av_assert0(sq->have_limiting); + if (sq->head_stream < 0) { + unsigned first_limiting = UINT_MAX; + /* wait for one timestamp in each stream before determining * the queue head */ for (unsigned int i = 0; i < sq->nb_streams; i++) { SyncQueueStream *st = &sq->streams[i]; - if (st->limiting && st->head_ts == AV_NOPTS_VALUE) + if (!st->limiting) + continue; + if (st->head_ts == AV_NOPTS_VALUE) return; + if (first_limiting == UINT_MAX) + first_limiting = i; } // placeholder value, correct one will be found below - sq->head_stream = 0; + av_assert0(first_limiting < UINT_MAX); + sq->head_stream = first_limiting; } for (unsigned int i = 0; i < sq->nb_streams; i++) { @@ -203,7 +309,7 @@ static int overflow_heartbeat(SyncQueue *sq, int stream_idx) /* get the chosen stream's tail timestamp */ for (size_t i = 0; tail_ts == AV_NOPTS_VALUE && av_fifo_peek(st->fifo, &frame, 1, i) >= 0; i++) - tail_ts = frame_ts(sq, frame); + tail_ts = frame_end(sq, frame, 0); /* overflow triggers when the tail is over specified duration behind the head */ if (tail_ts == AV_NOPTS_VALUE || tail_ts >= st->head_ts || @@ -213,7 +319,7 @@ static int overflow_heartbeat(SyncQueue *sq, int stream_idx) /* signal a fake timestamp for all streams that prevent tail_ts from being output */ tail_ts++; for (unsigned int i = 0; i < sq->nb_streams; i++) { - SyncQueueStream *st1 = &sq->streams[i]; + const SyncQueueStream *st1 = &sq->streams[i]; int64_t ts; if (st == st1 || st1->finished || @@ -225,6 +331,9 @@ static int overflow_heartbeat(SyncQueue *sq, int stream_idx) if (st1->head_ts != AV_NOPTS_VALUE) ts = FFMAX(st1->head_ts + 1, ts); + av_log(sq->logctx, AV_LOG_DEBUG, "sq: %u overflow heardbeat %s -> %s\n", + i, av_ts2timestr(st1->head_ts, &st1->tb), av_ts2timestr(ts, &st1->tb)); + stream_update_ts(sq, i, ts); } @@ -236,27 +345,39 @@ int sq_send(SyncQueue *sq, unsigned int stream_idx, SyncQueueFrame frame) SyncQueueStream *st; SyncQueueFrame dst; int64_t ts; - int ret; + int ret, nb_samples; av_assert0(stream_idx < sq->nb_streams); st = &sq->streams[stream_idx]; - av_assert0(st->tb.num > 0 && st->tb.den > 0); - if (frame_null(sq, frame)) { + av_log(sq->logctx, AV_LOG_DEBUG, "sq: %u EOF\n", stream_idx); finish_stream(sq, stream_idx); return 0; } if (st->finished) return AVERROR_EOF; + tb_update(sq, st, frame); + ret = objpool_get(sq->pool, (void**)&dst); if (ret < 0) return ret; frame_move(sq, dst, frame); - ts = frame_ts(sq, dst); + nb_samples = frame_samples(sq, dst); + // make sure frame duration is consistent with sample count + if (nb_samples) { + av_assert0(dst.f->sample_rate > 0); + dst.f->duration = av_rescale_q(nb_samples, (AVRational){ 1, dst.f->sample_rate }, + dst.f->time_base); + } + + ts = frame_end(sq, dst, 0); + + av_log(sq->logctx, AV_LOG_DEBUG, "sq: send %u ts %s\n", stream_idx, + av_ts2timestr(ts, &st->tb)); ret = av_fifo_write(st->fifo, &dst, 1); if (ret < 0) { @@ -267,30 +388,161 @@ int sq_send(SyncQueue *sq, unsigned int stream_idx, SyncQueueFrame frame) stream_update_ts(sq, stream_idx, ts); - st->frames_sent++; - if (st->frames_sent >= st->frames_max) + st->samples_queued += nb_samples; + st->samples_sent += nb_samples; + + if (st->frame_samples) + st->frames_sent = st->samples_sent / st->frame_samples; + else + st->frames_sent++; + + if (st->frames_sent >= st->frames_max) { + av_log(sq->logctx, AV_LOG_DEBUG, "sq: %u frames_max %"PRIu64" reached\n", + stream_idx, st->frames_max); + finish_stream(sq, stream_idx); + } + + return 0; +} + +static void offset_audio(AVFrame *f, int nb_samples) +{ + const int planar = av_sample_fmt_is_planar(f->format); + const int planes = planar ? f->ch_layout.nb_channels : 1; + const int bps = av_get_bytes_per_sample(f->format); + const int offset = nb_samples * bps * (planar ? 1 : f->ch_layout.nb_channels); + + av_assert0(bps > 0); + av_assert0(nb_samples < f->nb_samples); + + for (int i = 0; i < planes; i++) { + f->extended_data[i] += offset; + if (i < FF_ARRAY_ELEMS(f->data)) + f->data[i] = f->extended_data[i]; + } + f->linesize[0] -= offset; + f->nb_samples -= nb_samples; + f->duration = av_rescale_q(f->nb_samples, (AVRational){ 1, f->sample_rate }, + f->time_base); + f->pts += av_rescale_q(nb_samples, (AVRational){ 1, f->sample_rate }, + f->time_base); +} + +static int frame_is_aligned(const SyncQueue *sq, const AVFrame *frame) +{ + // only checks linesize[0], so only works for audio + av_assert0(frame->nb_samples > 0); + av_assert0(sq->align_mask); + + // only check data[0], because we always offset all data pointers + // by the same offset, so if one is aligned, all are + if (!((uintptr_t)frame->data[0] & sq->align_mask) && + !(frame->linesize[0] & sq->align_mask) && + frame->linesize[0] > sq->align_mask) + return 1; return 0; } +static int receive_samples(SyncQueue *sq, SyncQueueStream *st, + AVFrame *dst, int nb_samples) +{ + SyncQueueFrame src; + int ret; + + av_assert0(st->samples_queued >= nb_samples); + + ret = av_fifo_peek(st->fifo, &src, 1, 0); + av_assert0(ret >= 0); + + // peeked frame has enough samples and its data is aligned + // -> we can just make a reference and limit its sample count + if (src.f->nb_samples > nb_samples && frame_is_aligned(sq, src.f)) { + ret = av_frame_ref(dst, src.f); + if (ret < 0) + return ret; + + dst->nb_samples = nb_samples; + offset_audio(src.f, nb_samples); + st->samples_queued -= nb_samples; + + goto finish; + } + + // otherwise allocate a new frame and copy the data + ret = av_channel_layout_copy(&dst->ch_layout, &src.f->ch_layout); + if (ret < 0) + return ret; + + dst->format = src.f->format; + dst->nb_samples = nb_samples; + + ret = av_frame_get_buffer(dst, 0); + if (ret < 0) + goto fail; + + ret = av_frame_copy_props(dst, src.f); + if (ret < 0) + goto fail; + + dst->nb_samples = 0; + while (dst->nb_samples < nb_samples) { + int to_copy; + + ret = av_fifo_peek(st->fifo, &src, 1, 0); + av_assert0(ret >= 0); + + to_copy = FFMIN(nb_samples - dst->nb_samples, src.f->nb_samples); + + av_samples_copy(dst->extended_data, src.f->extended_data, dst->nb_samples, + 0, to_copy, dst->ch_layout.nb_channels, dst->format); + + if (to_copy < src.f->nb_samples) + offset_audio(src.f, to_copy); + else { + av_frame_unref(src.f); + objpool_release(sq->pool, (void**)&src); + av_fifo_drain2(st->fifo, 1); + } + st->samples_queued -= to_copy; + + dst->nb_samples += to_copy; + } + +finish: + dst->duration = av_rescale_q(nb_samples, (AVRational){ 1, dst->sample_rate }, + dst->time_base); + + return 0; + +fail: + av_frame_unref(dst); + return ret; +} + static int receive_for_stream(SyncQueue *sq, unsigned int stream_idx, SyncQueueFrame frame) { - SyncQueueStream *st_head = sq->head_stream >= 0 ? - &sq->streams[sq->head_stream] : NULL; + const SyncQueueStream *st_head = sq->head_stream >= 0 ? + &sq->streams[sq->head_stream] : NULL; SyncQueueStream *st; av_assert0(stream_idx < sq->nb_streams); st = &sq->streams[stream_idx]; - if (av_fifo_can_read(st->fifo)) { + if (av_fifo_can_read(st->fifo) && + (st->frame_samples <= st->samples_queued || st->finished)) { + int nb_samples = st->frame_samples; SyncQueueFrame peek; int64_t ts; int cmp = 1; + if (st->finished) + nb_samples = FFMIN(nb_samples, st->samples_queued); + av_fifo_peek(st->fifo, &peek, 1, 0); - ts = frame_ts(sq, peek); + ts = frame_end(sq, peek, nb_samples); /* check if this stream's tail timestamp does not overtake * the overall queue head */ @@ -299,11 +551,28 @@ static int receive_for_stream(SyncQueue *sq, unsigned int stream_idx, /* We can release frames that do not end after the queue head. * Frames with no timestamps are just passed through with no conditions. + * Frames are also passed through when there are no limiting streams. */ - if (cmp <= 0 || ts == AV_NOPTS_VALUE) { - frame_move(sq, frame, peek); - objpool_release(sq->pool, (void**)&peek); - av_fifo_drain2(st->fifo, 1); + if (cmp <= 0 || ts == AV_NOPTS_VALUE || !sq->have_limiting) { + if (nb_samples && + (nb_samples != peek.f->nb_samples || !frame_is_aligned(sq, peek.f))) { + int ret = receive_samples(sq, st, frame.f, nb_samples); + if (ret < 0) + return ret; + } else { + frame_move(sq, frame, peek); + objpool_release(sq->pool, (void**)&peek); + av_fifo_drain2(st->fifo, 1); + av_assert0(st->samples_queued >= frame_samples(sq, frame)); + st->samples_queued -= frame_samples(sq, frame); + } + + av_log(sq->logctx, AV_LOG_DEBUG, + "sq: receive %u ts %s queue head %d ts %s\n", stream_idx, + av_ts2timestr(frame_end(sq, frame, 0), &st->tb), + sq->head_stream, + st_head ? av_ts2timestr(st_head->head_ts, &st_head->tb) : "N/A"); + return 0; } } @@ -372,37 +641,38 @@ int sq_add_stream(SyncQueue *sq, int limiting) st->frames_max = UINT64_MAX; st->limiting = limiting; + sq->have_limiting |= limiting; + return sq->nb_streams++; } -void sq_set_tb(SyncQueue *sq, unsigned int stream_idx, AVRational tb) +void sq_limit_frames(SyncQueue *sq, unsigned int stream_idx, uint64_t frames) { SyncQueueStream *st; av_assert0(stream_idx < sq->nb_streams); st = &sq->streams[stream_idx]; - av_assert0(!av_fifo_can_read(st->fifo)); - - if (st->head_ts != AV_NOPTS_VALUE) - st->head_ts = av_rescale_q(st->head_ts, st->tb, tb); - - st->tb = tb; + st->frames_max = frames; + if (st->frames_sent >= st->frames_max) + finish_stream(sq, stream_idx); } -void sq_limit_frames(SyncQueue *sq, unsigned int stream_idx, uint64_t frames) +void sq_frame_samples(SyncQueue *sq, unsigned int stream_idx, + int frame_samples) { SyncQueueStream *st; + av_assert0(sq->type == SYNC_QUEUE_FRAMES); av_assert0(stream_idx < sq->nb_streams); st = &sq->streams[stream_idx]; - st->frames_max = frames; - if (st->frames_sent >= st->frames_max) - finish_stream(sq, stream_idx); + st->frame_samples = frame_samples; + + sq->align_mask = av_cpu_max_align() - 1; } -SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us) +SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us, void *logctx) { SyncQueue *sq = av_mallocz(sizeof(*sq)); @@ -411,6 +681,7 @@ SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us) sq->type = type; sq->buf_size_us = buf_size_us; + sq->logctx = logctx; sq->head_stream = -1; sq->head_finished_stream = -1; diff --git a/fftools/sync_queue.h b/fftools/sync_queue.h index 3f823ff0d91..dc5acfd4991 100644 --- a/fftools/sync_queue.h +++ b/fftools/sync_queue.h @@ -38,6 +38,11 @@ typedef union SyncQueueFrame { #define SQFRAME(frame) ((SyncQueueFrame){ .f = (frame) }) #define SQPKT(pkt) ((SyncQueueFrame){ .p = (pkt) }) +/** + * A sync queue provides timestamp synchronization between multiple streams. + * Some of these streams are marked as "limiting", then the queue ensures no + * stream gets ahead of any of the limiting streams. + */ typedef struct SyncQueue SyncQueue; /** @@ -45,7 +50,7 @@ typedef struct SyncQueue SyncQueue; * * @param buf_size_us maximum duration that will be buffered in microseconds */ -SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us); +SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us, void *logctx); void sq_free(SyncQueue **sq); /** @@ -59,12 +64,6 @@ void sq_free(SyncQueue **sq); */ int sq_add_stream(SyncQueue *sq, int limiting); -/** - * Set the timebase for the stream with index stream_idx. Should be called - * before sending any frames for this stream. - */ -void sq_set_tb(SyncQueue *sq, unsigned int stream_idx, AVRational tb); - /** * Limit the number of output frames for stream with index stream_idx * to max_frames. @@ -72,6 +71,16 @@ void sq_set_tb(SyncQueue *sq, unsigned int stream_idx, AVRational tb); void sq_limit_frames(SyncQueue *sq, unsigned int stream_idx, uint64_t max_frames); +/** + * Set a constant output audio frame size, in samples. Can only be used with + * SYNC_QUEUE_FRAMES queues and audio streams. + * + * All output frames will have exactly frame_samples audio samples, except + * possibly for the last one, which may have fewer. + */ +void sq_frame_samples(SyncQueue *sq, unsigned int stream_idx, + int frame_samples); + /** * Submit a frame for the stream with index stream_idx. * diff --git a/fftools/thread_queue.c b/fftools/thread_queue.c index a1ab4ce92e4..fd73cc0a9bf 100644 --- a/fftools/thread_queue.c +++ b/fftools/thread_queue.c @@ -164,7 +164,12 @@ static int receive_locked(ThreadQueue *tq, int *stream_idx, FifoElem elem; unsigned int nb_finished = 0; - if (av_fifo_read(tq->fifo, &elem, 1) >= 0) { + while (av_fifo_read(tq->fifo, &elem, 1) >= 0) { + if (tq->finished[elem.stream_idx] & FINISHED_RECV) { + objpool_release(tq->obj_pool, &elem.obj); + continue; + } + tq->obj_move(data, elem.obj); objpool_release(tq->obj_pool, &elem.obj); *stream_idx = elem.stream_idx; @@ -172,7 +177,7 @@ static int receive_locked(ThreadQueue *tq, int *stream_idx, } for (unsigned int i = 0; i < tq->nb_streams; i++) { - if (!(tq->finished[i] & FINISHED_SEND)) + if (!tq->finished[i]) continue; /* return EOF to the consumer at most once for each stream */ @@ -197,7 +202,14 @@ int tq_receive(ThreadQueue *tq, int *stream_idx, void *data) pthread_mutex_lock(&tq->lock); while (1) { + size_t can_read = av_fifo_can_read(tq->fifo); + ret = receive_locked(tq, stream_idx, data); + + // signal other threads if the fifo state changed + if (can_read != av_fifo_can_read(tq->fifo)) + pthread_cond_broadcast(&tq->cond); + if (ret == AVERROR(EAGAIN)) { pthread_cond_wait(&tq->cond, &tq->lock); continue; @@ -206,9 +218,6 @@ int tq_receive(ThreadQueue *tq, int *stream_idx, void *data) break; } - if (ret == 0) - pthread_cond_broadcast(&tq->cond); - pthread_mutex_unlock(&tq->lock); return ret; diff --git a/libavcodec/012v.c b/libavcodec/012v.c index f0197cd8f90..fa5eb0f95ef 100644 --- a/libavcodec/012v.c +++ b/libavcodec/012v.c @@ -65,7 +65,7 @@ static int zero12v_decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; pic->pict_type = AV_PICTURE_TYPE_I; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; line_end = avpkt->data + stride; for (line = 0; line < avctx->height; line++) { diff --git a/libavcodec/4xm.c b/libavcodec/4xm.c index d5acb2bd398..cfe65c178a4 100644 --- a/libavcodec/4xm.c +++ b/libavcodec/4xm.c @@ -125,7 +125,7 @@ static const uint8_t dequant_table[64] = { 20, 35, 34, 32, 31, 22, 15, 8, }; -static VLC block_type_vlc[2][4]; +static VLCElem block_type_vlc[2][4][32]; typedef struct CFrameBuffer { @@ -250,17 +250,15 @@ static void idct(int16_t block[64]) static av_cold void init_vlcs(void) { - static VLCElem table[2][4][32]; int i, j; for (i = 0; i < 2; i++) { for (j = 0; j < 4; j++) { - block_type_vlc[i][j].table = table[i][j]; - block_type_vlc[i][j].table_allocated = 32; - init_vlc(&block_type_vlc[i][j], BLOCK_TYPE_VLC_BITS, 7, - &block_type_tab[i][j][0][1], 2, 1, - &block_type_tab[i][j][0][0], 2, 1, - INIT_VLC_USE_NEW_STATIC); + ff_vlc_init_table_sparse(block_type_vlc[i][j], FF_ARRAY_ELEMS(block_type_vlc[i][j]), + BLOCK_TYPE_VLC_BITS, 7, + &block_type_tab[i][j][0][1], 2, 1, + &block_type_tab[i][j][0][0], 2, 1, + NULL, 0, 0, 0); } } } @@ -357,7 +355,7 @@ static int decode_p_block(FourXContext *f, uint16_t *dst, const uint16_t *src, if (get_bits_left(&f->gb) < 1) return AVERROR_INVALIDDATA; h = 1 << log2h; - code = get_vlc2(&f->gb, block_type_vlc[1 - (f->version > 1)][index].table, + code = get_vlc2(&f->gb, block_type_vlc[1 - (f->version > 1)][index], BLOCK_TYPE_VLC_BITS, 1); av_assert0(code >= 0 && code <= 6); @@ -706,8 +704,8 @@ static const uint8_t *read_huffman_tables(FourXContext *f, len_tab[j] = len; } - ff_free_vlc(&f->pre_vlc); - if (init_vlc(&f->pre_vlc, ACDC_VLC_BITS, 257, len_tab, 1, 1, + ff_vlc_free(&f->pre_vlc); + if (vlc_init(&f->pre_vlc, ACDC_VLC_BITS, 257, len_tab, 1, 1, bits_tab, 4, 4, 0)) return NULL; @@ -959,7 +957,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, return AVERROR_INVALIDDATA; } - picture->key_frame = picture->pict_type == AV_PICTURE_TYPE_I; + if (picture->pict_type == AV_PICTURE_TYPE_I) + picture->flags |= AV_FRAME_FLAG_KEY; + else + picture->flags &= ~AV_FRAME_FLAG_KEY; av_image_copy_plane(picture->data[0], picture->linesize[0], (const uint8_t*)f->frame_buffer, avctx->width * 2, @@ -984,7 +985,7 @@ static av_cold int decode_end(AVCodecContext *avctx) av_freep(&f->cfrm[i].data); f->cfrm[i].allocated_size = 0; } - ff_free_vlc(&f->pre_vlc); + ff_vlc_free(&f->pre_vlc); return 0; } diff --git a/libavcodec/8bps.c b/libavcodec/8bps.c index 90d6c96fd1b..0060c46d09a 100644 --- a/libavcodec/8bps.c +++ b/libavcodec/8bps.c @@ -26,27 +26,23 @@ * http://www.pcisys.net/~melanson/codecs/ * * Supports: PAL8 (RGB 8bpp, paletted) - * : BGR24 (RGB 24bpp) (can also output it as RGB32) - * : RGB32 (RGB 32bpp, 4th plane is alpha) + * : GBRP (RGB 24bpp) + * : GBRAP (RGB 32bpp, 4th plane is alpha) */ #include -#include "libavutil/bswap.h" +#include "libavutil/intreadwrite.h" #include "libavutil/internal.h" #include "avcodec.h" #include "codec_internal.h" #include "decode.h" - -static const enum AVPixelFormat pixfmt_rgb24[] = { - AV_PIX_FMT_BGR24, AV_PIX_FMT_0RGB32, AV_PIX_FMT_NONE }; - typedef struct EightBpsContext { AVCodecContext *avctx; - unsigned char planes; - unsigned char planemap[4]; + uint8_t planes; + uint8_t planemap[4]; uint32_t pal[256]; } EightBpsContext; @@ -57,18 +53,17 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, const uint8_t *buf = avpkt->data; int buf_size = avpkt->size; EightBpsContext * const c = avctx->priv_data; - const unsigned char *encoded = buf; - unsigned char *pixptr, *pixptr_end; + const uint8_t *encoded = buf; + uint8_t *pixptr, *pixptr_end; unsigned int height = avctx->height; // Real image height unsigned int dlen, p, row; - const unsigned char *lp, *dp, *ep; - unsigned char count; - unsigned int px_inc; - unsigned int planes = c->planes; - unsigned char *planemap = c->planemap; + const uint8_t *lp, *dp, *ep; + uint8_t count; + const uint8_t *planemap = c->planemap; + unsigned int planes = c->planes; int ret; - if (buf_size < planes * height * 2) + if (buf_size < planes * height * (2 + 2*((avctx->width+128)/129))) return AVERROR_INVALIDDATA; if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) @@ -79,19 +74,18 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, /* Set data pointer after line lengths */ dp = encoded + planes * (height << 1); - px_inc = planes + (avctx->pix_fmt == AV_PIX_FMT_0RGB32); - for (p = 0; p < planes; p++) { + const int pi = planemap[p]; /* Lines length pointer for this plane */ lp = encoded + p * (height << 1); /* Decode a plane */ for (row = 0; row < height; row++) { - pixptr = frame->data[0] + row * frame->linesize[0] + planemap[p]; - pixptr_end = pixptr + frame->linesize[0]; + pixptr = frame->data[pi] + row * frame->linesize[pi]; + pixptr_end = pixptr + frame->linesize[pi]; if (ep - lp < row * 2 + 2) return AVERROR_INVALIDDATA; - dlen = av_be2ne16(*(const unsigned short *)(lp + row * 2)); + dlen = AV_RB16(lp + row * 2); /* Decode a row of this plane */ while (dlen > 0) { if (ep - dp <= 1) @@ -99,22 +93,19 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((count = *dp++) <= 127) { count++; dlen -= count + 1; - if (pixptr_end - pixptr < count * px_inc) + if (pixptr_end - pixptr < count) break; if (ep - dp < count) return AVERROR_INVALIDDATA; - while (count--) { - *pixptr = *dp++; - pixptr += px_inc; - } + memcpy(pixptr, dp, count); + pixptr += count; + dp += count; } else { count = 257 - count; - if (pixptr_end - pixptr < count * px_inc) + if (pixptr_end - pixptr < count) break; - while (count--) { - *pixptr = *dp; - pixptr += px_inc; - } + memset(pixptr, dp[0], count); + pixptr += count; dp++; dlen -= 2; } @@ -123,9 +114,16 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } if (avctx->bits_per_coded_sample <= 8) { - frame->palette_has_changed = ff_copy_palette(c->pal, avpkt, avctx); - - memcpy (frame->data[1], c->pal, AVPALETTE_SIZE); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + frame->palette_has_changed = +#endif + ff_copy_palette(c->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + memcpy(frame->data[1], c->pal, AVPALETTE_SIZE); } *got_frame = 1; @@ -147,16 +145,15 @@ static av_cold int decode_init(AVCodecContext *avctx) c->planemap[0] = 0; // 1st plane is palette indexes break; case 24: - avctx->pix_fmt = ff_get_format(avctx, pixfmt_rgb24); + avctx->pix_fmt = AV_PIX_FMT_GBRP; c->planes = 3; c->planemap[0] = 2; // 1st plane is red - c->planemap[1] = 1; // 2nd plane is green - c->planemap[2] = 0; // 3rd plane is blue + c->planemap[1] = 0; // 2nd plane is green + c->planemap[2] = 1; // 3rd plane is blue break; case 32: - avctx->pix_fmt = AV_PIX_FMT_RGB32; + avctx->pix_fmt = AV_PIX_FMT_GBRAP; c->planes = 4; - /* handle planemap setup later for decoding rgb24 data as rbg32 */ break; default: av_log(avctx, AV_LOG_ERROR, "Error: Unsupported color depth: %u.\n", @@ -164,11 +161,11 @@ static av_cold int decode_init(AVCodecContext *avctx) return AVERROR_INVALIDDATA; } - if (avctx->pix_fmt == AV_PIX_FMT_RGB32) { - c->planemap[0] = HAVE_BIGENDIAN ? 1 : 2; // 1st plane is red - c->planemap[1] = HAVE_BIGENDIAN ? 2 : 1; // 2nd plane is green - c->planemap[2] = HAVE_BIGENDIAN ? 3 : 0; // 3rd plane is blue - c->planemap[3] = HAVE_BIGENDIAN ? 0 : 3; // 4th plane is alpha + if (avctx->pix_fmt == AV_PIX_FMT_GBRAP) { + c->planemap[0] = 2; // 1st plane is red + c->planemap[1] = 0; // 2nd plane is green + c->planemap[2] = 1; // 3rd plane is blue + c->planemap[3] = 3; // 4th plane is alpha } return 0; } diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 389253f5d08..113adb22d51 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -25,13 +25,13 @@ HEADERS = ac3_parser.h \ version_major.h \ videotoolbox.h \ vorbis_parser.h \ - xvmc.h \ OBJS = ac3_parser.o \ adts_parser.o \ allcodecs.o \ avcodec.o \ avdct.o \ + avfft.o \ avpacket.o \ bitstream.o \ bitstream_filters.o \ @@ -55,6 +55,7 @@ OBJS = ac3_parser.o \ profiles.o \ qsv_api.o \ raw.o \ + refstruct.o \ utils.o \ version.o \ vlc.o \ @@ -62,6 +63,8 @@ OBJS = ac3_parser.o \ xiph.o \ # subsystems +include $(SRC_PATH)/libavcodec/vvc/Makefile +-include $(SRC_PATH)/libavcodec/$(ARCH)/vvc/Makefile OBJS-$(CONFIG_AANDCTTABLES) += aandcttab.o OBJS-$(CONFIG_AC3DSP) += ac3dsp.o ac3.o ac3tab.o OBJS-$(CONFIG_ADTS_HEADER) += adts_header.o mpeg4audio_sample_rates.o @@ -76,21 +79,19 @@ OBJS-$(CONFIG_CBS) += cbs.o cbs_bsf.o OBJS-$(CONFIG_CBS_AV1) += cbs_av1.o OBJS-$(CONFIG_CBS_H264) += cbs_h2645.o cbs_sei.o h2645_parse.o OBJS-$(CONFIG_CBS_H265) += cbs_h2645.o cbs_sei.o h2645_parse.o +OBJS-$(CONFIG_CBS_H266) += cbs_h2645.o cbs_sei.o h2645_parse.o OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o +OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o -OBJS-$(CONFIG_CRYSTALHD) += crystalhd.o -OBJS-$(CONFIG_DCT) += dct.o dct32_fixed.o dct32_float.o OBJS-$(CONFIG_DEFLATE_WRAPPER) += zlib_wrapper.o OBJS-$(CONFIG_DOVI_RPU) += dovi_rpu.o OBJS-$(CONFIG_ERROR_RESILIENCE) += error_resilience.o +OBJS-$(CONFIG_EVCPARSE) += evc_parse.o evc_ps.o OBJS-$(CONFIG_EXIF) += exif.o tiff_common.o OBJS-$(CONFIG_FAANDCT) += faandct.o OBJS-$(CONFIG_FAANIDCT) += faanidct.o OBJS-$(CONFIG_FDCTDSP) += fdctdsp.o jfdctfst.o jfdctint.o -FFT-OBJS-$(CONFIG_HARDCODED_TABLES) += cos_tables.o -OBJS-$(CONFIG_FFT) += avfft.o fft_float.o fft_fixed_32.o \ - fft_init_table.o $(FFT-OBJS-yes) OBJS-$(CONFIG_FMTCONVERT) += fmtconvert.o OBJS-$(CONFIG_GOLOMB) += golomb.o OBJS-$(CONFIG_H263DSP) += h263dsp.o @@ -104,7 +105,7 @@ OBJS-$(CONFIG_H264_SEI) += h264_sei.o h2645_sei.o OBJS-$(CONFIG_HEVCPARSE) += hevc_parse.o hevc_ps.o hevc_data.o \ h2645data.o h2645_parse.o h2645_vui.o OBJS-$(CONFIG_HEVC_SEI) += hevc_sei.o h2645_sei.o \ - dynamic_hdr10_plus.o dynamic_hdr_vivid.o + dynamic_hdr_vivid.o aom_film_grain.o OBJS-$(CONFIG_HPELDSP) += hpeldsp.o OBJS-$(CONFIG_HUFFMAN) += huffman.o OBJS-$(CONFIG_HUFFYUVDSP) += huffyuvdsp.o @@ -123,7 +124,6 @@ OBJS-$(CONFIG_LLVIDENCDSP) += lossless_videoencdsp.o OBJS-$(CONFIG_LPC) += lpc.o OBJS-$(CONFIG_LSP) += lsp.o OBJS-$(CONFIG_LZF) += lzf.o -OBJS-$(CONFIG_MDCT) += mdct_float.o mdct_fixed_32.o OBJS-$(CONFIG_ME_CMP) += me_cmp.o OBJS-$(CONFIG_MEDIACODEC) += mediacodecdec_common.o mediacodec_surface.o mediacodec_wrapper.o mediacodec_sw_buffer.o OBJS-$(CONFIG_MPEG_ER) += mpeg_er.o @@ -132,7 +132,8 @@ OBJS-$(CONFIG_MPEGAUDIO) += mpegaudio.o mpegaudiodec_common.o \ OBJS-$(CONFIG_MPEGAUDIODSP) += mpegaudiodsp.o \ mpegaudiodsp_data.o \ mpegaudiodsp_fixed.o \ - mpegaudiodsp_float.o + mpegaudiodsp_float.o \ + dct32_fixed.o dct32_float.o OBJS-$(CONFIG_MPEGAUDIOHEADER) += mpegaudiodecheader.o mpegaudiotabs.o OBJS-$(CONFIG_MPEG4AUDIO) += mpeg4audio.o mpeg4audio_sample_rates.o OBJS-$(CONFIG_MPEGVIDEO) += mpegvideo.o rl.o \ @@ -154,7 +155,6 @@ OBJS-$(CONFIG_QSV) += qsv.o OBJS-$(CONFIG_QSVDEC) += qsvdec.o OBJS-$(CONFIG_QSVENC) += qsvenc.o OBJS-$(CONFIG_RANGECODER) += rangecoder.o -OBJS-$(CONFIG_RDFT) += rdft.o OBJS-$(CONFIG_RV34DSP) += rv34dsp.o OBJS-$(CONFIG_SINEWIN) += sinewin.o OBJS-$(CONFIG_SNAPPY) += snappy.o @@ -177,10 +177,12 @@ OBJS-$(CONFIG_WMV2DSP) += wmv2dsp.o OBJS-$(CONFIG_ZERO12V_DECODER) += 012v.o OBJS-$(CONFIG_A64MULTI_ENCODER) += a64multienc.o elbg.o OBJS-$(CONFIG_A64MULTI5_ENCODER) += a64multienc.o elbg.o -OBJS-$(CONFIG_AAC_DECODER) += aacdec.o aactab.o aacsbr.o aacps_common.o aacps_float.o \ +OBJS-$(CONFIG_AAC_DECODER) += aacdec.o aacdec_common.o aactab.o \ + aacsbr.o aacps_common.o aacps_float.o \ kbdwin.o \ sbrdsp.o aacpsdsp_float.o cbrt_data.o -OBJS-$(CONFIG_AAC_FIXED_DECODER) += aacdec_fixed.o aactab.o aacsbr_fixed.o aacps_common.o aacps_fixed.o \ +OBJS-$(CONFIG_AAC_FIXED_DECODER) += aacdec_fixed.o aacdec_common.o aactab.o \ + aacsbr_fixed.o aacps_common.o aacps_fixed.o \ kbdwin.o \ sbrdsp_fixed.o aacpsdsp_fixed.o cbrt_data_fixed.o OBJS-$(CONFIG_AAC_ENCODER) += aacenc.o aaccoder.o aacenctab.o \ @@ -250,11 +252,13 @@ OBJS-$(CONFIG_ATRAC3PAL_DECODER) += atrac3plusdec.o atrac3plus.o \ OBJS-$(CONFIG_ATRAC9_DECODER) += atrac9dec.o OBJS-$(CONFIG_AURA_DECODER) += cyuv.o OBJS-$(CONFIG_AURA2_DECODER) += aura.o -OBJS-$(CONFIG_AV1_DECODER) += av1dec.o +OBJS-$(CONFIG_AV1_DECODER) += av1dec.o av1_parse.o OBJS-$(CONFIG_AV1_CUVID_DECODER) += cuviddec.o OBJS-$(CONFIG_AV1_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_AV1_MEDIACODEC_ENCODER) += mediacodecenc.o OBJS-$(CONFIG_AV1_NVENC_ENCODER) += nvenc_av1.o nvenc.o OBJS-$(CONFIG_AV1_QSV_ENCODER) += qsvenc_av1.o +OBJS-$(CONFIG_AV1_VAAPI_ENCODER) += vaapi_encode_av1.o av1_levels.o OBJS-$(CONFIG_AVRN_DECODER) += avrndec.o OBJS-$(CONFIG_AVRP_DECODER) += r210dec.o OBJS-$(CONFIG_AVRP_ENCODER) += r210enc.o @@ -336,6 +340,7 @@ OBJS-$(CONFIG_DVVIDEO_ENCODER) += dvenc.o dv.o dvdata.o OBJS-$(CONFIG_DXA_DECODER) += dxa.o OBJS-$(CONFIG_DXTORY_DECODER) += dxtory.o OBJS-$(CONFIG_DXV_DECODER) += dxv.o +OBJS-$(CONFIG_DXV_ENCODER) += dxvenc.o OBJS-$(CONFIG_EAC3_DECODER) += eac3_data.o OBJS-$(CONFIG_EAC3_ENCODER) += eac3enc.o eac3_data.o OBJS-$(CONFIG_EACMV_DECODER) += eacmv.o @@ -427,7 +432,7 @@ OBJS-$(CONFIG_HDR_ENCODER) += hdrenc.o OBJS-$(CONFIG_HEVC_DECODER) += hevcdec.o hevc_mvs.o \ hevc_cabac.o hevc_refs.o hevcpred.o \ hevcdsp.o hevc_filter.o hevc_data.o \ - h274.o + h274.o aom_film_grain.o OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o OBJS-$(CONFIG_HEVC_CUVID_DECODER) += cuviddec.o OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o @@ -444,8 +449,7 @@ OBJS-$(CONFIG_HEVC_V4L2M2M_DECODER) += v4l2_m2m_dec.o OBJS-$(CONFIG_HEVC_V4L2M2M_ENCODER) += v4l2_m2m_enc.o OBJS-$(CONFIG_HEVC_VIDEOTOOLBOX_ENCODER) += videotoolboxenc.o OBJS-$(CONFIG_HNM4_VIDEO_DECODER) += hnm4video.o -OBJS-$(CONFIG_HQ_HQA_DECODER) += hq_hqa.o hq_hqadata.o hq_hqadsp.o \ - canopus.o +OBJS-$(CONFIG_HQ_HQA_DECODER) += hq_hqa.o hq_hqadsp.o canopus.o OBJS-$(CONFIG_HQX_DECODER) += hqx.o hqxvlc.o hqxdsp.o canopus.o OBJS-$(CONFIG_HUFFYUV_DECODER) += huffyuv.o huffyuvdec.o OBJS-$(CONFIG_HUFFYUV_ENCODER) += huffyuv.o huffyuvenc.o @@ -469,13 +473,14 @@ OBJS-$(CONFIG_JACOSUB_DECODER) += jacosubdec.o ass.o OBJS-$(CONFIG_JPEG2000_ENCODER) += j2kenc.o mqcenc.o mqc.o jpeg2000.o \ jpeg2000dwt.o OBJS-$(CONFIG_JPEG2000_DECODER) += jpeg2000dec.o jpeg2000.o jpeg2000dsp.o \ - jpeg2000dwt.o mqcdec.o mqc.o + jpeg2000dwt.o mqcdec.o mqc.o jpeg2000htdec.o OBJS-$(CONFIG_JPEGLS_DECODER) += jpeglsdec.o jpegls.o OBJS-$(CONFIG_JPEGLS_ENCODER) += jpeglsenc.o jpegls.o OBJS-$(CONFIG_JV_DECODER) += jvdec.o OBJS-$(CONFIG_KGV1_DECODER) += kgv1dec.o OBJS-$(CONFIG_KMVC_DECODER) += kmvc.o OBJS-$(CONFIG_LAGARITH_DECODER) += lagarith.o lagarithrac.o +OBJS-$(CONFIG_LEAD_DECODER) += leaddec.o jpegquanttables.o OBJS-$(CONFIG_LJPEG_ENCODER) += ljpegenc.o mjpegenc_common.o OBJS-$(CONFIG_LOCO_DECODER) += loco.o OBJS-$(CONFIG_LSCR_DECODER) += lscrdec.o png.o pngdec.o pngdsp.o @@ -542,6 +547,7 @@ OBJS-$(CONFIG_MPEG4_DECODER) += mpeg4videodsp.o xvididct.o OBJS-$(CONFIG_MPEG4_ENCODER) += mpeg4videoenc.o OBJS-$(CONFIG_MPEG4_CUVID_DECODER) += cuviddec.o OBJS-$(CONFIG_MPEG4_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_MPEG4_MEDIACODEC_ENCODER) += mediacodecenc.o OBJS-$(CONFIG_MPEG4_OMX_ENCODER) += omx.o OBJS-$(CONFIG_MPEG4_V4L2M2M_DECODER) += v4l2_m2m_dec.o OBJS-$(CONFIG_MPEG4_V4L2M2M_ENCODER) += v4l2_m2m_enc.o @@ -550,6 +556,7 @@ OBJS-$(CONFIG_MSA1_DECODER) += mss3.o OBJS-$(CONFIG_MSCC_DECODER) += mscc.o OBJS-$(CONFIG_MSNSIREN_DECODER) += siren.o OBJS-$(CONFIG_MSP2_DECODER) += msp2dec.o +OBJS-$(CONFIG_MSRLE_ENCODER) += msrleenc.o OBJS-$(CONFIG_MSRLE_DECODER) += msrle.o msrledec.o OBJS-$(CONFIG_MSS1_DECODER) += mss1.o mss12.o OBJS-$(CONFIG_MSS2_DECODER) += mss2.o mss12.o mss2dsp.o wmv2data.o @@ -574,6 +581,7 @@ OBJS-$(CONFIG_OPUS_DECODER) += opusdec.o opusdec_celt.o opus_celt.o \ opusdsp.o opus_parse.o opus_rc.o OBJS-$(CONFIG_OPUS_ENCODER) += opusenc.o opusenc_psy.o opus_celt.o \ opus_pvq.o opus_rc.o opustab.o +OBJS-$(CONFIG_OSQ_DECODER) += osq.o OBJS-$(CONFIG_PAF_AUDIO_DECODER) += pafaudio.o OBJS-$(CONFIG_PAF_VIDEO_DECODER) += pafvideo.o OBJS-$(CONFIG_PAM_DECODER) += pnmdec.o pnm.o @@ -582,6 +590,7 @@ OBJS-$(CONFIG_PBM_DECODER) += pnmdec.o pnm.o OBJS-$(CONFIG_PBM_ENCODER) += pnmenc.o OBJS-$(CONFIG_PCX_DECODER) += pcx.o OBJS-$(CONFIG_PCX_ENCODER) += pcxenc.o +OBJS-$(CONFIG_PDV_DECODER) += pdvdec.o OBJS-$(CONFIG_PFM_DECODER) += pnmdec.o pnm.o OBJS-$(CONFIG_PFM_ENCODER) += pnmenc.o OBJS-$(CONFIG_PGM_DECODER) += pnmdec.o pnm.o @@ -614,6 +623,7 @@ OBJS-$(CONFIG_QCELP_DECODER) += qcelpdec.o \ OBJS-$(CONFIG_QDM2_DECODER) += qdm2.o OBJS-$(CONFIG_QDMC_DECODER) += qdmc.o OBJS-$(CONFIG_QDRAW_DECODER) += qdrw.o +OBJS-$(CONFIG_QOA_DECODER) += qoadec.o OBJS-$(CONFIG_QOI_DECODER) += qoidec.o OBJS-$(CONFIG_QOI_ENCODER) += qoienc.o OBJS-$(CONFIG_QPEG_DECODER) += qpeg.o @@ -640,6 +650,7 @@ OBJS-$(CONFIG_ROQ_DPCM_ENCODER) += roqaudioenc.o OBJS-$(CONFIG_RPZA_DECODER) += rpza.o OBJS-$(CONFIG_RPZA_ENCODER) += rpzaenc.o OBJS-$(CONFIG_RSCC_DECODER) += rscc.o +OBJS-$(CONFIG_RTV1_DECODER) += rtv1.o OBJS-$(CONFIG_RV10_DECODER) += rv10.o OBJS-$(CONFIG_RV10_ENCODER) += rv10enc.o OBJS-$(CONFIG_RV20_DECODER) += rv10.o @@ -749,6 +760,7 @@ OBJS-$(CONFIG_VC2_ENCODER) += vc2enc.o vc2enc_dwt.o diractab.o OBJS-$(CONFIG_VCR1_DECODER) += vcr1.o OBJS-$(CONFIG_VMDAUDIO_DECODER) += vmdaudio.o OBJS-$(CONFIG_VMDVIDEO_DECODER) += vmdvideo.o +OBJS-$(CONFIG_VMIX_DECODER) += vmixdec.o OBJS-$(CONFIG_VMNC_DECODER) += vmnc.o OBJS-$(CONFIG_VNULL_DECODER) += null.o OBJS-$(CONFIG_VNULL_ENCODER) += null.o @@ -760,10 +772,11 @@ OBJS-$(CONFIG_VP3_DECODER) += vp3.o jpegquanttables.o OBJS-$(CONFIG_VP5_DECODER) += vp5.o vp56.o vp56data.o vpx_rac.o OBJS-$(CONFIG_VP6_DECODER) += vp6.o vp56.o vp56data.o \ vp6dsp.o vpx_rac.o -OBJS-$(CONFIG_VP7_DECODER) += vp8.o vpx_rac.o -OBJS-$(CONFIG_VP8_DECODER) += vp8.o vpx_rac.o +OBJS-$(CONFIG_VP7_DECODER) += vp8.o vp8data.o vpx_rac.o +OBJS-$(CONFIG_VP8_DECODER) += vp8.o vp8data.o vpx_rac.o OBJS-$(CONFIG_VP8_CUVID_DECODER) += cuviddec.o OBJS-$(CONFIG_VP8_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_VP8_MEDIACODEC_ENCODER) += mediacodecenc.o OBJS-$(CONFIG_VP8_QSV_DECODER) += qsvdec.o OBJS-$(CONFIG_VP8_RKMPP_DECODER) += rkmppdec.o OBJS-$(CONFIG_VP8_VAAPI_ENCODER) += vaapi_encode_vp8.o @@ -774,6 +787,7 @@ OBJS-$(CONFIG_VP9_DECODER) += vp9.o vp9data.o vp9dsp.o vp9lpf.o vp9r vp9dsp_8bpp.o vp9dsp_10bpp.o vp9dsp_12bpp.o OBJS-$(CONFIG_VP9_CUVID_DECODER) += cuviddec.o OBJS-$(CONFIG_VP9_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_VP9_MEDIACODEC_ENCODER) += mediacodecenc.o OBJS-$(CONFIG_VP9_RKMPP_DECODER) += rkmppdec.o OBJS-$(CONFIG_VP9_VAAPI_ENCODER) += vaapi_encode_vp9.o OBJS-$(CONFIG_VP9_QSV_ENCODER) += qsvenc_vp9.o @@ -972,32 +986,40 @@ OBJS-$(CONFIG_ADPCM_ZORK_DECODER) += adpcm.o adpcm_data.o # hardware accelerators OBJS-$(CONFIG_D3D11VA) += dxva2.o +OBJS-$(CONFIG_D3D12VA) += dxva2.o d3d12va_decode.o OBJS-$(CONFIG_DXVA2) += dxva2.o OBJS-$(CONFIG_NVDEC) += nvdec.o OBJS-$(CONFIG_VAAPI) += vaapi_decode.o OBJS-$(CONFIG_VIDEOTOOLBOX) += videotoolbox.o OBJS-$(CONFIG_VDPAU) += vdpau.o +OBJS-$(CONFIG_VULKAN) += vulkan.o vulkan_video.o OBJS-$(CONFIG_AV1_D3D11VA_HWACCEL) += dxva2_av1.o OBJS-$(CONFIG_AV1_DXVA2_HWACCEL) += dxva2_av1.o +OBJS-$(CONFIG_AV1_D3D12VA_HWACCEL) += dxva2_av1.o d3d12va_av1.o OBJS-$(CONFIG_AV1_NVDEC_HWACCEL) += nvdec_av1.o OBJS-$(CONFIG_AV1_VAAPI_HWACCEL) += vaapi_av1.o OBJS-$(CONFIG_AV1_VDPAU_HWACCEL) += vdpau_av1.o +OBJS-$(CONFIG_AV1_VULKAN_HWACCEL) += vulkan_decode.o vulkan_av1.o OBJS-$(CONFIG_H263_VAAPI_HWACCEL) += vaapi_mpeg4.o OBJS-$(CONFIG_H263_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o OBJS-$(CONFIG_H264_D3D11VA_HWACCEL) += dxva2_h264.o OBJS-$(CONFIG_H264_DXVA2_HWACCEL) += dxva2_h264.o +OBJS-$(CONFIG_H264_D3D12VA_HWACCEL) += dxva2_h264.o d3d12va_h264.o OBJS-$(CONFIG_H264_NVDEC_HWACCEL) += nvdec_h264.o OBJS-$(CONFIG_H264_QSV_HWACCEL) += qsvdec.o OBJS-$(CONFIG_H264_VAAPI_HWACCEL) += vaapi_h264.o OBJS-$(CONFIG_H264_VDPAU_HWACCEL) += vdpau_h264.o OBJS-$(CONFIG_H264_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o +OBJS-$(CONFIG_H264_VULKAN_HWACCEL) += vulkan_decode.o vulkan_h264.o OBJS-$(CONFIG_HEVC_D3D11VA_HWACCEL) += dxva2_hevc.o OBJS-$(CONFIG_HEVC_DXVA2_HWACCEL) += dxva2_hevc.o +OBJS-$(CONFIG_HEVC_D3D12VA_HWACCEL) += dxva2_hevc.o d3d12va_hevc.o OBJS-$(CONFIG_HEVC_NVDEC_HWACCEL) += nvdec_hevc.o OBJS-$(CONFIG_HEVC_QSV_HWACCEL) += qsvdec.o OBJS-$(CONFIG_HEVC_VAAPI_HWACCEL) += vaapi_hevc.o h265_profile_level.o OBJS-$(CONFIG_HEVC_VDPAU_HWACCEL) += vdpau_hevc.o h265_profile_level.o +OBJS-$(CONFIG_HEVC_VULKAN_HWACCEL) += vulkan_decode.o vulkan_hevc.o OBJS-$(CONFIG_MJPEG_NVDEC_HWACCEL) += nvdec_mjpeg.o OBJS-$(CONFIG_MJPEG_VAAPI_HWACCEL) += vaapi_mjpeg.o OBJS-$(CONFIG_MPEG1_NVDEC_HWACCEL) += nvdec_mpeg12.o @@ -1005,6 +1027,7 @@ OBJS-$(CONFIG_MPEG1_VDPAU_HWACCEL) += vdpau_mpeg12.o OBJS-$(CONFIG_MPEG1_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o OBJS-$(CONFIG_MPEG2_D3D11VA_HWACCEL) += dxva2_mpeg2.o OBJS-$(CONFIG_MPEG2_DXVA2_HWACCEL) += dxva2_mpeg2.o +OBJS-$(CONFIG_MPEG2_D3D12VA_HWACCEL) += dxva2_mpeg2.o d3d12va_mpeg2.o OBJS-$(CONFIG_MPEG2_NVDEC_HWACCEL) += nvdec_mpeg12.o OBJS-$(CONFIG_MPEG2_QSV_HWACCEL) += qsvdec.o OBJS-$(CONFIG_MPEG2_VAAPI_HWACCEL) += vaapi_mpeg2.o @@ -1016,6 +1039,7 @@ OBJS-$(CONFIG_MPEG4_VDPAU_HWACCEL) += vdpau_mpeg4.o OBJS-$(CONFIG_MPEG4_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o OBJS-$(CONFIG_VC1_D3D11VA_HWACCEL) += dxva2_vc1.o OBJS-$(CONFIG_VC1_DXVA2_HWACCEL) += dxva2_vc1.o +OBJS-$(CONFIG_VC1_D3D12VA_HWACCEL) += dxva2_vc1.o d3d12va_vc1.o OBJS-$(CONFIG_VC1_NVDEC_HWACCEL) += nvdec_vc1.o OBJS-$(CONFIG_VC1_QSV_HWACCEL) += qsvdec.o OBJS-$(CONFIG_VC1_VAAPI_HWACCEL) += vaapi_vc1.o @@ -1024,6 +1048,7 @@ OBJS-$(CONFIG_VP8_NVDEC_HWACCEL) += nvdec_vp8.o OBJS-$(CONFIG_VP8_VAAPI_HWACCEL) += vaapi_vp8.o OBJS-$(CONFIG_VP9_D3D11VA_HWACCEL) += dxva2_vp9.o OBJS-$(CONFIG_VP9_DXVA2_HWACCEL) += dxva2_vp9.o +OBJS-$(CONFIG_VP9_D3D12VA_HWACCEL) += dxva2_vp9.o d3d12va_vp9.o OBJS-$(CONFIG_VP9_NVDEC_HWACCEL) += nvdec_vp9.o OBJS-$(CONFIG_VP9_VAAPI_HWACCEL) += vaapi_vp9.o OBJS-$(CONFIG_VP9_VDPAU_HWACCEL) += vdpau_vp9.o @@ -1042,6 +1067,9 @@ STLIBOBJS-$(CONFIG_AVFORMAT) += to_upper4.o STLIBOBJS-$(CONFIG_ISO_MEDIA) += mpegaudiotabs.o STLIBOBJS-$(CONFIG_FLV_MUXER) += mpeg4audio_sample_rates.o STLIBOBJS-$(CONFIG_HLS_DEMUXER) += ac3_channel_layout_tab.o +STLIBOBJS-$(CONFIG_IMAGE_JPEGXL_PIPE_DEMUXER) += jpegxl_parse.o +STLIBOBJS-$(CONFIG_JNI) += ffjni.o +STLIBOBJS-$(CONFIG_JPEGXL_ANIM_DEMUXER) += jpegxl_parse.o STLIBOBJS-$(CONFIG_MATROSKA_DEMUXER) += mpeg4audio_sample_rates.o STLIBOBJS-$(CONFIG_MOV_DEMUXER) += ac3_channel_layout_tab.o STLIBOBJS-$(CONFIG_MXF_MUXER) += golomb.o @@ -1079,10 +1107,11 @@ OBJS-$(CONFIG_PCM_MULAW_AT_ENCODER) += audiotoolboxenc.o OBJS-$(CONFIG_LIBAOM_AV1_DECODER) += libaomdec.o libaom.o OBJS-$(CONFIG_LIBAOM_AV1_ENCODER) += libaomenc.o libaom.o OBJS-$(CONFIG_LIBARIBB24_DECODER) += libaribb24.o ass.o +OBJS-$(CONFIG_LIBARIBCAPTION_DECODER) += libaribcaption.o ass.o OBJS-$(CONFIG_LIBCELT_DECODER) += libcelt_dec.o OBJS-$(CONFIG_LIBCODEC2_DECODER) += libcodec2.o OBJS-$(CONFIG_LIBCODEC2_ENCODER) += libcodec2.o -OBJS-$(CONFIG_LIBDAV1D_DECODER) += libdav1d.o +OBJS-$(CONFIG_LIBDAV1D_DECODER) += libdav1d.o av1_parse.o OBJS-$(CONFIG_LIBDAVS2_DECODER) += libdavs2.o OBJS-$(CONFIG_LIBFDK_AAC_DECODER) += libfdk-aacdec.o OBJS-$(CONFIG_LIBFDK_AAC_ENCODER) += libfdk-aacenc.o @@ -1101,7 +1130,6 @@ OBJS-$(CONFIG_LIBOPENCORE_AMRNB_ENCODER) += libopencore-amr.o OBJS-$(CONFIG_LIBOPENCORE_AMRWB_DECODER) += libopencore-amr.o OBJS-$(CONFIG_LIBOPENH264_DECODER) += libopenh264dec.o libopenh264.o OBJS-$(CONFIG_LIBOPENH264_ENCODER) += libopenh264enc.o libopenh264.o -OBJS-$(CONFIG_LIBOPENJPEG_DECODER) += libopenjpegdec.o OBJS-$(CONFIG_LIBOPENJPEG_ENCODER) += libopenjpegenc.o OBJS-$(CONFIG_LIBOPUS_DECODER) += libopusdec.o libopus.o \ vorbis_data.o @@ -1121,8 +1149,8 @@ OBJS-$(CONFIG_LIBVORBIS_ENCODER) += libvorbisenc.o \ vorbis_data.o OBJS-$(CONFIG_LIBVPX_VP8_DECODER) += libvpxdec.o OBJS-$(CONFIG_LIBVPX_VP8_ENCODER) += libvpxenc.o -OBJS-$(CONFIG_LIBVPX_VP9_DECODER) += libvpxdec.o libvpx.o -OBJS-$(CONFIG_LIBVPX_VP9_ENCODER) += libvpxenc.o libvpx.o +OBJS-$(CONFIG_LIBVPX_VP9_DECODER) += libvpxdec.o +OBJS-$(CONFIG_LIBVPX_VP9_ENCODER) += libvpxenc.o OBJS-$(CONFIG_LIBWEBP_ENCODER) += libwebpenc_common.o libwebpenc.o OBJS-$(CONFIG_LIBWEBP_ANIM_ENCODER) += libwebpenc_common.o libwebpenc_animencoder.o OBJS-$(CONFIG_LIBX262_ENCODER) += libx264.o @@ -1130,6 +1158,8 @@ OBJS-$(CONFIG_LIBX264_ENCODER) += libx264.o OBJS-$(CONFIG_LIBX265_ENCODER) += libx265.o OBJS-$(CONFIG_LIBXAVS_ENCODER) += libxavs.o OBJS-$(CONFIG_LIBXAVS2_ENCODER) += libxavs2.o +OBJS-$(CONFIG_LIBXEVD_DECODER) += libxevd.o +OBJS-$(CONFIG_LIBXEVE_ENCODER) += libxeve.o OBJS-$(CONFIG_LIBXVID_ENCODER) += libxvid.o OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER) += libzvbi-teletextdec.o ass.o @@ -1140,7 +1170,7 @@ OBJS-$(CONFIG_AC3_PARSER) += aac_ac3_parser.o ac3tab.o \ ac3_channel_layout_tab.o OBJS-$(CONFIG_ADX_PARSER) += adx_parser.o OBJS-$(CONFIG_AMR_PARSER) += amr_parser.o -OBJS-$(CONFIG_AV1_PARSER) += av1_parser.o +OBJS-$(CONFIG_AV1_PARSER) += av1_parser.o av1_parse.o OBJS-$(CONFIG_AVS2_PARSER) += avs2.o avs2_parser.o OBJS-$(CONFIG_AVS3_PARSER) += avs3_parser.o OBJS-$(CONFIG_BMP_PARSER) += bmp_parser.o @@ -1157,6 +1187,7 @@ OBJS-$(CONFIG_DVAUDIO_PARSER) += dvaudio_parser.o OBJS-$(CONFIG_DVBSUB_PARSER) += dvbsub_parser.o OBJS-$(CONFIG_DVD_NAV_PARSER) += dvd_nav_parser.o OBJS-$(CONFIG_DVDSUB_PARSER) += dvdsub_parser.o +OBJS-$(CONFIG_EVC_PARSER) += evc_parser.o OBJS-$(CONFIG_FLAC_PARSER) += flac_parser.o flacdata.o flac.o OBJS-$(CONFIG_FTR_PARSER) += ftr_parser.o OBJS-$(CONFIG_G723_1_PARSER) += g723_1_parser.o @@ -1170,6 +1201,7 @@ OBJS-$(CONFIG_HEVC_PARSER) += hevc_parser.o hevc_data.o OBJS-$(CONFIG_HDR_PARSER) += hdr_parser.o OBJS-$(CONFIG_IPU_PARSER) += ipu_parser.o OBJS-$(CONFIG_JPEG2000_PARSER) += jpeg2000_parser.o +OBJS-$(CONFIG_JPEGXL_PARSER) += jpegxl_parser.o jpegxl_parse.o OBJS-$(CONFIG_MISC4_PARSER) += misc4_parser.o OBJS-$(CONFIG_MJPEG_PARSER) += mjpeg_parser.o OBJS-$(CONFIG_MLP_PARSER) += mlp_parse.o mlp_parser.o mlp.o @@ -1184,8 +1216,7 @@ OBJS-$(CONFIG_OPUS_PARSER) += opus_parser.o opus_parse.o \ OBJS-$(CONFIG_PNG_PARSER) += png_parser.o OBJS-$(CONFIG_PNM_PARSER) += pnm_parser.o pnm.o OBJS-$(CONFIG_QOI_PARSER) += qoi_parser.o -OBJS-$(CONFIG_RV30_PARSER) += rv34_parser.o -OBJS-$(CONFIG_RV40_PARSER) += rv34_parser.o +OBJS-$(CONFIG_RV34_PARSER) += rv34_parser.o OBJS-$(CONFIG_SBC_PARSER) += sbc_parser.o OBJS-$(CONFIG_SIPR_PARSER) += sipr_parser.o OBJS-$(CONFIG_TAK_PARSER) += tak_parser.o tak.o @@ -1194,57 +1225,21 @@ OBJS-$(CONFIG_VC1_PARSER) += vc1_parser.o vc1.o vc1data.o \ OBJS-$(CONFIG_VP3_PARSER) += vp3_parser.o OBJS-$(CONFIG_VP8_PARSER) += vp8_parser.o OBJS-$(CONFIG_VP9_PARSER) += vp9_parser.o +OBJS-$(CONFIG_VVC_PARSER) += vvc_parser.o OBJS-$(CONFIG_WEBP_PARSER) += webp_parser.o OBJS-$(CONFIG_XBM_PARSER) += xbm_parser.o OBJS-$(CONFIG_XMA_PARSER) += xma_parser.o OBJS-$(CONFIG_XWD_PARSER) += xwd_parser.o # bitstream filters -OBJS-$(CONFIG_AAC_ADTSTOASC_BSF) += aac_adtstoasc_bsf.o -OBJS-$(CONFIG_AV1_METADATA_BSF) += av1_metadata_bsf.o -OBJS-$(CONFIG_AV1_FRAME_MERGE_BSF) += av1_frame_merge_bsf.o -OBJS-$(CONFIG_AV1_FRAME_SPLIT_BSF) += av1_frame_split_bsf.o -OBJS-$(CONFIG_CHOMP_BSF) += chomp_bsf.o -OBJS-$(CONFIG_DUMP_EXTRADATA_BSF) += dump_extradata_bsf.o -OBJS-$(CONFIG_DCA_CORE_BSF) += dca_core_bsf.o -OBJS-$(CONFIG_DTS2PTS_BSF) += dts2pts_bsf.o -OBJS-$(CONFIG_DV_ERROR_MARKER_BSF) += dv_error_marker_bsf.o -OBJS-$(CONFIG_EAC3_CORE_BSF) += eac3_core_bsf.o -OBJS-$(CONFIG_EXTRACT_EXTRADATA_BSF) += extract_extradata_bsf.o \ - av1_parse.o h2645_parse.o -OBJS-$(CONFIG_FILTER_UNITS_BSF) += filter_units_bsf.o -OBJS-$(CONFIG_H264_METADATA_BSF) += h264_metadata_bsf.o h264_levels.o \ - h2645data.o -OBJS-$(CONFIG_H264_MP4TOANNEXB_BSF) += h264_mp4toannexb_bsf.o -OBJS-$(CONFIG_H264_REDUNDANT_PPS_BSF) += h264_redundant_pps_bsf.o -OBJS-$(CONFIG_HAPQA_EXTRACT_BSF) += hapqa_extract_bsf.o hap.o -OBJS-$(CONFIG_HEVC_METADATA_BSF) += h265_metadata_bsf.o h265_profile_level.o \ - h2645data.o -OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF) += hevc_mp4toannexb_bsf.o -OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += imx_dump_header_bsf.o -OBJS-$(CONFIG_MEDIA100_TO_MJPEGB_BSF) += media100_to_mjpegb_bsf.o -OBJS-$(CONFIG_MJPEG2JPEG_BSF) += mjpeg2jpeg_bsf.o -OBJS-$(CONFIG_MJPEGA_DUMP_HEADER_BSF) += mjpega_dump_header_bsf.o -OBJS-$(CONFIG_MPEG4_UNPACK_BFRAMES_BSF) += mpeg4_unpack_bframes_bsf.o -OBJS-$(CONFIG_MOV2TEXTSUB_BSF) += movsub_bsf.o -OBJS-$(CONFIG_MP3_HEADER_DECOMPRESS_BSF) += mp3_header_decompress_bsf.o \ - mpegaudiotabs.o -OBJS-$(CONFIG_MPEG2_METADATA_BSF) += mpeg2_metadata_bsf.o -OBJS-$(CONFIG_NOISE_BSF) += noise_bsf.o -OBJS-$(CONFIG_NULL_BSF) += null_bsf.o -OBJS-$(CONFIG_OPUS_METADATA_BSF) += opus_metadata_bsf.o -OBJS-$(CONFIG_PCM_RECHUNK_BSF) += pcm_rechunk_bsf.o -OBJS-$(CONFIG_PGS_FRAME_MERGE_BSF) += pgs_frame_merge_bsf.o -OBJS-$(CONFIG_PRORES_METADATA_BSF) += prores_metadata_bsf.o -OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF) += remove_extradata_bsf.o av1_parse.o -OBJS-$(CONFIG_SETTS_BSF) += setts_bsf.o -OBJS-$(CONFIG_TEXT2MOVSUB_BSF) += movsub_bsf.o -OBJS-$(CONFIG_TRACE_HEADERS_BSF) += trace_headers_bsf.o -OBJS-$(CONFIG_TRUEHD_CORE_BSF) += truehd_core_bsf.o mlp_parse.o mlp.o -OBJS-$(CONFIG_VP9_METADATA_BSF) += vp9_metadata_bsf.o -OBJS-$(CONFIG_VP9_RAW_REORDER_BSF) += vp9_raw_reorder_bsf.o -OBJS-$(CONFIG_VP9_SUPERFRAME_BSF) += vp9_superframe_bsf.o -OBJS-$(CONFIG_VP9_SUPERFRAME_SPLIT_BSF) += vp9_superframe_split_bsf.o +include $(SRC_PATH)/libavcodec/bsf/Makefile + +OBJS-$(CONFIG_EXTRACT_EXTRADATA_BSF) += av1_parse.o h2645_parse.o +OBJS-$(CONFIG_H264_METADATA_BSF) += h264_levels.o h2645data.o +OBJS-$(CONFIG_HAPQA_EXTRACT_BSF) += hap.o +OBJS-$(CONFIG_HEVC_METADATA_BSF) += h265_profile_level.o h2645data.o +OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF) += av1_parse.o +OBJS-$(CONFIG_TRUEHD_CORE_BSF) += mlp_parse.o mlp.o # thread libraries OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o @@ -1264,10 +1259,11 @@ SKIPHEADERS += %_tablegen.h \ aacenc_quantization.h \ aacenc_quantization_misc.h \ bitstream_template.h \ - $(ARCH)/vpx_arith.h \ + $(ARCH)/vpx_arith.h \ SKIPHEADERS-$(CONFIG_AMF) += amfenc.h SKIPHEADERS-$(CONFIG_D3D11VA) += d3d11va.h dxva2_internal.h +SKIPHEADERS-$(CONFIG_D3D12VA) += d3d12va_decode.h SKIPHEADERS-$(CONFIG_DXVA2) += dxva2.h dxva2_internal.h SKIPHEADERS-$(CONFIG_JNI) += ffjni.h SKIPHEADERS-$(CONFIG_LCMS2) += fflcms2.h @@ -1281,10 +1277,10 @@ SKIPHEADERS-$(CONFIG_NVDEC) += nvdec.h SKIPHEADERS-$(CONFIG_NVENC) += nvenc.h SKIPHEADERS-$(CONFIG_QSV) += qsv.h qsv_internal.h SKIPHEADERS-$(CONFIG_QSVENC) += qsvenc.h -SKIPHEADERS-$(CONFIG_XVMC) += xvmc.h SKIPHEADERS-$(CONFIG_VAAPI) += vaapi_decode.h vaapi_hevc.h vaapi_encode.h SKIPHEADERS-$(CONFIG_VDPAU) += vdpau.h vdpau_internal.h SKIPHEADERS-$(CONFIG_VIDEOTOOLBOX) += videotoolbox.h vt_internal.h +SKIPHEADERS-$(CONFIG_VULKAN) += vulkan.h vulkan_video.h vulkan_decode.h SKIPHEADERS-$(CONFIG_V4L2_M2M) += v4l2_buffers.h v4l2_context.h v4l2_m2m.h SKIPHEADERS-$(CONFIG_ZLIB) += zlib_wrapper.h @@ -1298,9 +1294,8 @@ TESTPROGS = avcodec \ jpeg2000dwt \ mathops \ +TESTPROGS-$(CONFIG_AV1_VAAPI_ENCODER) += av1_levels TESTPROGS-$(CONFIG_CABAC) += cabac -TESTPROGS-$(CONFIG_DCT) += avfft -TESTPROGS-$(CONFIG_FFT) += fft fft-fixed32 TESTPROGS-$(CONFIG_GOLOMB) += golomb TESTPROGS-$(CONFIG_IDCTDSP) += dct TESTPROGS-$(CONFIG_IIRFILTER) += iirfilter @@ -1320,7 +1315,6 @@ HOSTPROGS = aacps_tablegen \ aacps_fixed_tablegen \ cbrt_tablegen \ cbrt_fixed_tablegen \ - cos_tablegen \ dv_tablegen \ motionpixels_tablegen \ mpegaudio_tablegen \ @@ -1335,12 +1329,6 @@ CLEANFILES = *_tables.c *_tables.h *_tablegen$(HOSTEXESUF) $(SUBDIR)tests/dct$(EXESUF): $(SUBDIR)dctref.o $(SUBDIR)aandcttab.o $(SUBDIR)dv_tablegen$(HOSTEXESUF): $(SUBDIR)dvdata_host.o -TRIG_TABLES = cos cos_fixed sin -TRIG_TABLES := $(TRIG_TABLES:%=$(SUBDIR)%_tables.c) - -$(TRIG_TABLES): $(SUBDIR)%_tables.c: $(SUBDIR)cos_tablegen$(HOSTEXESUF) - $(M)./$< $* > $@ - ifdef CONFIG_SMALL $(SUBDIR)%_tablegen$(HOSTEXESUF): HOSTCFLAGS += -DCONFIG_SMALL=1 else diff --git a/libavcodec/aac.h b/libavcodec/aac.h index 285d3b74827..89f838eab5e 100644 --- a/libavcodec/aac.h +++ b/libavcodec/aac.h @@ -32,16 +32,6 @@ #include "aac_defines.h" -#include "libavutil/channel_layout.h" -#include "libavutil/float_dsp.h" -#include "libavutil/fixed_dsp.h" -#include "libavutil/mem_internal.h" -#include "libavutil/tx.h" -#include "avcodec.h" -#include "mpeg4audio.h" -#include "sbr.h" - -#include #define MAX_CHANNELS 64 #define MAX_ELEM_ID 16 @@ -49,8 +39,6 @@ #define TNS_MAX_ORDER 20 #define MAX_LTP_LONG_SFB 40 -#define CLIP_AVOIDANCE_FACTOR 0.95f - enum RawDataBlockType { TYPE_SCE, TYPE_CPE, @@ -88,8 +76,6 @@ enum BandType { INTENSITY_BT = 15, ///< Scalefactor data are intensity stereo positions (in phase). }; -#define IS_CODEBOOK_UNSIGNED(x) (((x) - 1) & 10) - enum ChannelPosition { AAC_CHANNEL_OFF = 0, AAC_CHANNEL_FRONT = 1, @@ -99,34 +85,6 @@ enum ChannelPosition { AAC_CHANNEL_CC = 5, }; -/** - * The point during decoding at which channel coupling is applied. - */ -enum CouplingPoint { - BEFORE_TNS, - BETWEEN_TNS_AND_IMDCT, - AFTER_IMDCT = 3, -}; - -/** - * Output configuration status - */ -enum OCStatus { - OC_NONE, ///< Output unconfigured - OC_TRIAL_PCE, ///< Output configuration under trial specified by an inband PCE - OC_TRIAL_FRAME, ///< Output configuration under trial specified by a frame header - OC_GLOBAL_HDR, ///< Output configuration set in a global header but not yet locked - OC_LOCKED, ///< Output configuration locked in place -}; - -typedef struct OutputConfiguration { - MPEG4AudioConfig m4ac; - uint8_t layout_map[MAX_ELEM_ID*4][3]; - int layout_map_tags; - AVChannelLayout ch_layout; - enum OCStatus status; -} OutputConfiguration; - /** * Predictor State */ @@ -155,70 +113,6 @@ typedef struct PredictorState { #define NOISE_PRE_BITS 9 ///< length of preamble #define NOISE_OFFSET 90 ///< subtracted from global gain, used as offset for the preamble -/** - * Long Term Prediction - */ -typedef struct LongTermPrediction { - int8_t present; - int16_t lag; - int coef_idx; - INTFLOAT coef; - int8_t used[MAX_LTP_LONG_SFB]; -} LongTermPrediction; - -/** - * Individual Channel Stream - */ -typedef struct IndividualChannelStream { - uint8_t max_sfb; ///< number of scalefactor bands per group - enum WindowSequence window_sequence[2]; - uint8_t use_kb_window[2]; ///< If set, use Kaiser-Bessel window, otherwise use a sine window. - int num_window_groups; - uint8_t group_len[8]; - LongTermPrediction ltp; - const uint16_t *swb_offset; ///< table of offsets to the lowest spectral coefficient of a scalefactor band, sfb, for a particular window - const uint8_t *swb_sizes; ///< table of scalefactor band sizes for a particular window - int num_swb; ///< number of scalefactor window bands - int num_windows; - int tns_max_bands; - int predictor_present; - int predictor_initialized; - int predictor_reset_group; - int predictor_reset_count[31]; ///< used by encoder to count prediction resets - uint8_t prediction_used[41]; - uint8_t window_clipping[8]; ///< set if a certain window is near clipping - float clip_avoidance_factor; ///< set if any window is near clipping to the necessary atennuation factor to avoid it -} IndividualChannelStream; - -/** - * Temporal Noise Shaping - */ -typedef struct TemporalNoiseShaping { - int present; - int n_filt[8]; - int length[8][4]; - int direction[8][4]; - int order[8][4]; - int coef_idx[8][4][TNS_MAX_ORDER]; - INTFLOAT coef[8][4][TNS_MAX_ORDER]; -} TemporalNoiseShaping; - -/** - * Dynamic Range Control - decoded from the bitstream but not processed further. - */ -typedef struct DynamicRangeControl { - int pce_instance_tag; ///< Indicates with which program the DRC info is associated. - int dyn_rng_sgn[17]; ///< DRC sign information; 0 - positive, 1 - negative - int dyn_rng_ctl[17]; ///< DRC magnitude information - int exclude_mask[MAX_CHANNELS]; ///< Channels to be excluded from DRC processing. - int band_incr; ///< Number of DRC bands greater than 1 having DRC info. - int interpolation_scheme; ///< Indicates the interpolation scheme used in the SBR QMF domain. - int band_top[17]; ///< Indicates the top of the i-th DRC band in units of 4 spectral lines. - int prog_ref_level; /**< A reference level for the long-term program audio level for all - * channels combined. - */ -} DynamicRangeControl; - typedef struct Pulse { int num_pulse; int start; @@ -226,166 +120,4 @@ typedef struct Pulse { int amp[4]; } Pulse; -/** - * coupling parameters - */ -typedef struct ChannelCoupling { - enum CouplingPoint coupling_point; ///< The point during decoding at which coupling is applied. - int num_coupled; ///< number of target elements - enum RawDataBlockType type[8]; ///< Type of channel element to be coupled - SCE or CPE. - int id_select[8]; ///< element id - int ch_select[8]; /**< [0] shared list of gains; [1] list of gains for right channel; - * [2] list of gains for left channel; [3] lists of gains for both channels - */ - INTFLOAT gain[16][120]; -} ChannelCoupling; - -/** - * Single Channel Element - used for both SCE and LFE elements. - */ -typedef struct SingleChannelElement { - IndividualChannelStream ics; - TemporalNoiseShaping tns; - Pulse pulse; - enum BandType band_type[128]; ///< band types - enum BandType band_alt[128]; ///< alternative band type (used by encoder) - int band_type_run_end[120]; ///< band type run end points - INTFLOAT sf[120]; ///< scalefactors - int sf_idx[128]; ///< scalefactor indices (used by encoder) - uint8_t zeroes[128]; ///< band is not coded (used by encoder) - uint8_t can_pns[128]; ///< band is allowed to PNS (informative) - float is_ener[128]; ///< Intensity stereo pos (used by encoder) - float pns_ener[128]; ///< Noise energy values (used by encoder) - DECLARE_ALIGNED(32, INTFLOAT, pcoeffs)[1024]; ///< coefficients for IMDCT, pristine - DECLARE_ALIGNED(32, INTFLOAT, coeffs)[1024]; ///< coefficients for IMDCT, maybe processed - DECLARE_ALIGNED(32, INTFLOAT, saved)[1536]; ///< overlap - DECLARE_ALIGNED(32, INTFLOAT, ret_buf)[2048]; ///< PCM output buffer - DECLARE_ALIGNED(16, INTFLOAT, ltp_state)[3072]; ///< time signal for LTP - DECLARE_ALIGNED(32, AAC_FLOAT, lcoeffs)[1024]; ///< MDCT of LTP coefficients (used by encoder) - DECLARE_ALIGNED(32, AAC_FLOAT, prcoeffs)[1024]; ///< Main prediction coefs (used by encoder) - PredictorState predictor_state[MAX_PREDICTORS]; - INTFLOAT *ret; ///< PCM output -} SingleChannelElement; - -/** - * channel element - generic struct for SCE/CPE/CCE/LFE - */ -typedef struct ChannelElement { - int present; - // CPE specific - int common_window; ///< Set if channels share a common 'IndividualChannelStream' in bitstream. - int ms_mode; ///< Signals mid/side stereo flags coding mode (used by encoder) - uint8_t is_mode; ///< Set if any bands have been encoded using intensity stereo (used by encoder) - uint8_t ms_mask[128]; ///< Set if mid/side stereo is used for each scalefactor window band - uint8_t is_mask[128]; ///< Set if intensity stereo is used (used by encoder) - // shared - SingleChannelElement ch[2]; - // CCE specific - ChannelCoupling coup; - SpectralBandReplication sbr; -} ChannelElement; - -enum AACOutputChannelOrder { - CHANNEL_ORDER_DEFAULT, - CHANNEL_ORDER_CODED, -}; - -/** - * main AAC context - */ -struct AACContext { - AVClass *class; - AVCodecContext *avctx; - AVFrame *frame; - - int is_saved; ///< Set if elements have stored overlap from previous frame. - DynamicRangeControl che_drc; - - /** - * @name Channel element related data - * @{ - */ - ChannelElement *che[4][MAX_ELEM_ID]; - ChannelElement *tag_che_map[4][MAX_ELEM_ID]; - int tags_mapped; - int warned_remapping_once; - /** @} */ - - /** - * @name temporary aligned temporary buffers - * (We do not want to have these on the stack.) - * @{ - */ - DECLARE_ALIGNED(32, INTFLOAT, buf_mdct)[1024]; - /** @} */ - - /** - * @name Computed / set up during initialization - * @{ - */ - AVTXContext *mdct120; - AVTXContext *mdct128; - AVTXContext *mdct480; - AVTXContext *mdct512; - AVTXContext *mdct960; - AVTXContext *mdct1024; - AVTXContext *mdct_ltp; - - av_tx_fn mdct120_fn; - av_tx_fn mdct128_fn; - av_tx_fn mdct480_fn; - av_tx_fn mdct512_fn; - av_tx_fn mdct960_fn; - av_tx_fn mdct1024_fn; - av_tx_fn mdct_ltp_fn; -#if USE_FIXED - AVFixedDSPContext *fdsp; -#else - AVFloatDSPContext *fdsp; -#endif /* USE_FIXED */ - int random_state; - /** @} */ - - /** - * @name Members used for output - * @{ - */ - SingleChannelElement *output_element[MAX_CHANNELS]; ///< Points to each SingleChannelElement - /** @} */ - - - /** - * @name Japanese DTV specific extension - * @{ - */ - int force_dmono_mode;///< 0->not dmono, 1->use first channel, 2->use second channel - int dmono_mode; ///< 0->not dmono, 1->use first channel, 2->use second channel - /** @} */ - - enum AACOutputChannelOrder output_channel_order; - - DECLARE_ALIGNED(32, INTFLOAT, temp)[128]; - - OutputConfiguration oc[2]; - int warned_num_aac_frames; - int warned_960_sbr; - unsigned warned_71_wide; - int warned_gain_control; - int warned_he_aac_mono; - - /* aacdec functions pointers */ - void (*imdct_and_windowing)(AACContext *ac, SingleChannelElement *sce); - void (*apply_ltp)(AACContext *ac, SingleChannelElement *sce); - void (*apply_tns)(INTFLOAT coef[1024], TemporalNoiseShaping *tns, - IndividualChannelStream *ics, int decode); - void (*windowing_and_mdct_ltp)(AACContext *ac, INTFLOAT *out, - INTFLOAT *in, IndividualChannelStream *ics); - void (*update_ltp)(AACContext *ac, SingleChannelElement *sce); - void (*vector_pow43)(int *coefs, int len); - void (*subband_scale)(int *dst, int *src, int scale, int offset, int len, void *log_context); - -}; - -void ff_aacdec_init_mips(AACContext *c); - #endif /* AVCODEC_AAC_H */ diff --git a/libavcodec/aac_ac3_parser.c b/libavcodec/aac_ac3_parser.c index 83d515b5a83..9305b4c50fc 100644 --- a/libavcodec/aac_ac3_parser.c +++ b/libavcodec/aac_ac3_parser.c @@ -135,12 +135,6 @@ int ff_aac_ac3_parse(AVCodecParserContext *s1, avctx->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; avctx->ch_layout.nb_channels = hdr.channels; } -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - avctx->channels = avctx->ch_layout.nb_channels; - avctx->channel_layout = hdr.channel_layout; -FF_ENABLE_DEPRECATION_WARNINGS -#endif } s1->duration = hdr.num_blocks * 256; avctx->audio_service_type = hdr.bitstream_mode; diff --git a/libavcodec/aac_defines.h b/libavcodec/aac_defines.h index bd0d93275a8..a0c23c33ffd 100644 --- a/libavcodec/aac_defines.h +++ b/libavcodec/aac_defines.h @@ -30,7 +30,6 @@ #include "libavutil/softfloat.h" #define AAC_RENAME(x) x ## _fixed -#define AAC_RENAME_32(x) x ## _fixed_32 #define AAC_RENAME2(x) x ## _fixed typedef int INTFLOAT; typedef unsigned UINTFLOAT; ///< Equivalent to INTFLOAT, Used as temporal cast to avoid undefined sign overflow operations. @@ -72,12 +71,19 @@ typedef int AAC_SIGNE; ((int64_t)(y) * (z)) + \ 0x40000000) >> 31) #define AAC_HALF_SUM(x, y) (((x) >> 1) + ((y) >> 1)) -#define AAC_SRA_R(x, y) (int)(((x) + (1 << ((y) - 1))) >> (y)) + +#ifdef LPC_USE_FIXED +#error aac_defines.h must be included before lpc_functions.h for fixed point decoder +#endif + +#define LPC_USE_FIXED 1 +#define LPC_MUL26(x, y) AAC_MUL26((x), (y)) +#define LPC_FIXR(x) FIXR(x) +#define LPC_SRA_R(x, y) (int)(((x) + (1 << ((y) - 1))) >> (y)) #else #define AAC_RENAME(x) x -#define AAC_RENAME_32(x) x #define AAC_RENAME2(x) ff_ ## x typedef float INTFLOAT; typedef float UINTFLOAT; @@ -105,7 +111,6 @@ typedef unsigned AAC_SIGNE; (c) * (d) - (e) * (f)) #define AAC_MSUB31_V3(x, y, z) ((x) - (y)) * (z) #define AAC_HALF_SUM(x, y) ((x) + (y)) * 0.5f -#define AAC_SRA_R(x, y) (x) #endif /* USE_FIXED */ diff --git a/libavcodec/aaccoder.c b/libavcodec/aaccoder.c index 6291c161234..4ce54ca8867 100644 --- a/libavcodec/aaccoder.c +++ b/libavcodec/aaccoder.c @@ -108,10 +108,10 @@ static av_always_inline float quantize_and_encode_band_cost_template( return cost * lambda; } if (!scaled) { - s->abs_pow34(s->scoefs, in, size); + s->aacdsp.abs_pow34(s->scoefs, in, size); scaled = s->scoefs; } - s->quant_bands(s->qcoefs, in, scaled, size, !BT_UNSIGNED, aac_cb_maxval[cb], Q34, ROUNDING); + s->aacdsp.quant_bands(s->qcoefs, in, scaled, size, !BT_UNSIGNED, aac_cb_maxval[cb], Q34, ROUNDING); if (BT_UNSIGNED) { off = 0; } else { @@ -311,7 +311,7 @@ static void encode_window_bands_info(AACEncContext *s, SingleChannelElement *sce float next_minrd = INFINITY; int next_mincb = 0; - s->abs_pow34(s->scoefs, sce->coeffs, 1024); + s->aacdsp.abs_pow34(s->scoefs, sce->coeffs, 1024); start = win*128; for (cb = 0; cb < CB_TOT_ALL; cb++) { path[0][cb].cost = 0.0f; @@ -522,7 +522,7 @@ static void search_for_quantizers_anmr(AVCodecContext *avctx, AACEncContext *s, } } idx = 1; - s->abs_pow34(s->scoefs, sce->coeffs, 1024); + s->aacdsp.abs_pow34(s->scoefs, sce->coeffs, 1024); for (w = 0; w < sce->ics.num_windows; w += sce->ics.group_len[w]) { start = w*128; for (g = 0; g < sce->ics.num_swb; g++) { @@ -668,7 +668,7 @@ static void search_for_quantizers_fast(AVCodecContext *avctx, AACEncContext *s, if (!allz) return; - s->abs_pow34(s->scoefs, sce->coeffs, 1024); + s->aacdsp.abs_pow34(s->scoefs, sce->coeffs, 1024); ff_quantize_band_cost_cache_init(s); for (w = 0; w < sce->ics.num_windows; w += sce->ics.group_len[w]) { @@ -874,8 +874,8 @@ static void search_for_pns(AACEncContext *s, AVCodecContext *avctx, SingleChanne s->fdsp->vector_fmul_scalar(PNS, PNS, scale, sce->ics.swb_sizes[g]); pns_senergy = s->fdsp->scalarproduct_float(PNS, PNS, sce->ics.swb_sizes[g]); pns_energy += pns_senergy; - s->abs_pow34(NOR34, &sce->coeffs[start_c], sce->ics.swb_sizes[g]); - s->abs_pow34(PNS34, PNS, sce->ics.swb_sizes[g]); + s->aacdsp.abs_pow34(NOR34, &sce->coeffs[start_c], sce->ics.swb_sizes[g]); + s->aacdsp.abs_pow34(PNS34, PNS, sce->ics.swb_sizes[g]); dist1 += quantize_band_cost(s, &sce->coeffs[start_c], NOR34, sce->ics.swb_sizes[g], @@ -1012,8 +1012,8 @@ static void search_for_ms(AACEncContext *s, ChannelElement *cpe) S[i] = M[i] - sce1->coeffs[start+(w+w2)*128+i]; } - s->abs_pow34(M34, M, sce0->ics.swb_sizes[g]); - s->abs_pow34(S34, S, sce0->ics.swb_sizes[g]); + s->aacdsp.abs_pow34(M34, M, sce0->ics.swb_sizes[g]); + s->aacdsp.abs_pow34(S34, S, sce0->ics.swb_sizes[g]); for (i = 0; i < sce0->ics.swb_sizes[g]; i++ ) { Mmax = FFMAX(Mmax, M34[i]); Smax = FFMAX(Smax, S34[i]); @@ -1056,10 +1056,10 @@ static void search_for_ms(AACEncContext *s, ChannelElement *cpe) - sce1->coeffs[start+(w+w2)*128+i]; } - s->abs_pow34(L34, sce0->coeffs+start+(w+w2)*128, sce0->ics.swb_sizes[g]); - s->abs_pow34(R34, sce1->coeffs+start+(w+w2)*128, sce0->ics.swb_sizes[g]); - s->abs_pow34(M34, M, sce0->ics.swb_sizes[g]); - s->abs_pow34(S34, S, sce0->ics.swb_sizes[g]); + s->aacdsp.abs_pow34(L34, sce0->coeffs+start+(w+w2)*128, sce0->ics.swb_sizes[g]); + s->aacdsp.abs_pow34(R34, sce1->coeffs+start+(w+w2)*128, sce0->ics.swb_sizes[g]); + s->aacdsp.abs_pow34(M34, M, sce0->ics.swb_sizes[g]); + s->aacdsp.abs_pow34(S34, S, sce0->ics.swb_sizes[g]); dist1 += quantize_band_cost(s, &sce0->coeffs[start + (w+w2)*128], L34, sce0->ics.swb_sizes[g], diff --git a/libavcodec/aaccoder_trellis.h b/libavcodec/aaccoder_trellis.h index 4810ff32084..245aa1c11b1 100644 --- a/libavcodec/aaccoder_trellis.h +++ b/libavcodec/aaccoder_trellis.h @@ -70,7 +70,7 @@ static void codebook_trellis_rate(AACEncContext *s, SingleChannelElement *sce, float next_minbits = INFINITY; int next_mincb = 0; - s->abs_pow34(s->scoefs, sce->coeffs, 1024); + s->aacdsp.abs_pow34(s->scoefs, sce->coeffs, 1024); start = win*128; for (cb = 0; cb < CB_TOT_ALL; cb++) { path[0][cb].cost = run_bits+4; diff --git a/libavcodec/aaccoder_twoloop.h b/libavcodec/aaccoder_twoloop.h index 0504a916ad0..92dc2911a35 100644 --- a/libavcodec/aaccoder_twoloop.h +++ b/libavcodec/aaccoder_twoloop.h @@ -291,7 +291,7 @@ static void search_for_quantizers_twoloop(AVCodecContext *avctx, if (!allz) return; - s->abs_pow34(s->scoefs, sce->coeffs, 1024); + s->aacdsp.abs_pow34(s->scoefs, sce->coeffs, 1024); ff_quantize_band_cost_cache_init(s); for (i = 0; i < sizeof(minsf) / sizeof(minsf[0]); ++i) diff --git a/libavcodec/aacdec.c b/libavcodec/aacdec.c index ca31540d3cc..97e51dd72a5 100644 --- a/libavcodec/aacdec.c +++ b/libavcodec/aacdec.c @@ -40,11 +40,11 @@ #include "avcodec.h" #include "codec_internal.h" #include "get_bits.h" -#include "lpc.h" #include "kbdwin.h" #include "sinewin.h" #include "aac.h" +#include "aacdec.h" #include "aactab.h" #include "aacdectab.h" #include "adts_header.h" @@ -209,7 +209,7 @@ static av_always_inline void predict(PredictorState *ps, float *coef, * * @param index index into coupling gain array */ -static void apply_dependent_coupling(AACContext *ac, +static void apply_dependent_coupling(AACDecContext *ac, SingleChannelElement *target, ChannelElement *cce, int index) { @@ -245,7 +245,7 @@ static void apply_dependent_coupling(AACContext *ac, * * @param index index into coupling gain array */ -static void apply_independent_coupling(AACContext *ac, +static void apply_independent_coupling(AACDecContext *ac, SingleChannelElement *target, ChannelElement *cce, int index) { @@ -262,7 +262,7 @@ static void apply_independent_coupling(AACContext *ac, #define LOAS_SYNC_WORD 0x2b7 ///< 11 bits LOAS sync word struct LATMContext { - AACContext aac_ctx; ///< containing AACContext + AACDecContext aac_ctx; ///< containing AACContext int initialized; ///< initialized after a valid extradata was seen // parser data @@ -281,7 +281,7 @@ static inline uint32_t latm_get_value(GetBitContext *b) static int latm_decode_audio_specific_config(struct LATMContext *latmctx, GetBitContext *gb, int asclen) { - AACContext *ac = &latmctx->aac_ctx; + AACDecContext *ac = &latmctx->aac_ctx; AVCodecContext *avctx = ac->avctx; MPEG4AudioConfig m4ac = { 0 }; GetBitContext gbc; @@ -555,7 +555,7 @@ const FFCodec ff_aac_decoder = { CODEC_LONG_NAME("AAC (Advanced Audio Coding)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_AAC, - .priv_data_size = sizeof(AACContext), + .priv_data_size = sizeof(AACDecContext), .init = aac_decode_init, .close = aac_decode_close, FF_CODEC_DECODE_CB(aac_decode_frame), @@ -564,8 +564,7 @@ const FFCodec ff_aac_decoder = { }, .p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, - CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(aac_channel_layout) - .p.ch_layouts = aac_ch_layout, + .p.ch_layouts = ff_aac_ch_layout, .flush = flush, .p.priv_class = &aac_decoder_class, .p.profiles = NULL_IF_CONFIG_SMALL(ff_aac_profiles), @@ -590,8 +589,7 @@ const FFCodec ff_aac_latm_decoder = { }, .p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, - CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(aac_channel_layout) - .p.ch_layouts = aac_ch_layout, + .p.ch_layouts = ff_aac_ch_layout, .flush = flush, .p.profiles = NULL_IF_CONFIG_SMALL(ff_aac_profiles), }; diff --git a/libavcodec/aacdec.h b/libavcodec/aacdec.h new file mode 100644 index 00000000000..1b245f9258c --- /dev/null +++ b/libavcodec/aacdec.h @@ -0,0 +1,279 @@ +/* + * AAC decoder definitions and structures + * Copyright (c) 2005-2006 Oded Shimon ( ods15 ods15 dyndns org ) + * Copyright (c) 2006-2007 Maxim Gavrilov ( maxim.gavrilov gmail com ) + * + * 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 + * AAC decoder definitions and structures + * @author Oded Shimon ( ods15 ods15 dyndns org ) + * @author Maxim Gavrilov ( maxim.gavrilov gmail com ) + */ + +#ifndef AVCODEC_AACDEC_H +#define AVCODEC_AACDEC_H + +#include + +#include "libavutil/channel_layout.h" +#include "libavutil/float_dsp.h" +#include "libavutil/fixed_dsp.h" +#include "libavutil/mem_internal.h" +#include "libavutil/tx.h" + +#include "aac.h" +#include "aac_defines.h" +#include "mpeg4audio.h" +#include "sbr.h" + +/** + * Output configuration status + */ +enum OCStatus { + OC_NONE, ///< Output unconfigured + OC_TRIAL_PCE, ///< Output configuration under trial specified by an inband PCE + OC_TRIAL_FRAME, ///< Output configuration under trial specified by a frame header + OC_GLOBAL_HDR, ///< Output configuration set in a global header but not yet locked + OC_LOCKED, ///< Output configuration locked in place +}; + +enum AACOutputChannelOrder { + CHANNEL_ORDER_DEFAULT, + CHANNEL_ORDER_CODED, +}; + +/** + * The point during decoding at which channel coupling is applied. + */ +enum CouplingPoint { + BEFORE_TNS, + BETWEEN_TNS_AND_IMDCT, + AFTER_IMDCT = 3, +}; + +/** + * Long Term Prediction + */ +typedef struct LongTermPrediction { + int8_t present; + int16_t lag; + INTFLOAT coef; + int8_t used[MAX_LTP_LONG_SFB]; +} LongTermPrediction; + +/** + * Individual Channel Stream + */ +typedef struct IndividualChannelStream { + uint8_t max_sfb; ///< number of scalefactor bands per group + enum WindowSequence window_sequence[2]; + uint8_t use_kb_window[2]; ///< If set, use Kaiser-Bessel window, otherwise use a sine window. + int num_window_groups; + uint8_t group_len[8]; + LongTermPrediction ltp; + const uint16_t *swb_offset; ///< table of offsets to the lowest spectral coefficient of a scalefactor band, sfb, for a particular window + int num_swb; ///< number of scalefactor window bands + int num_windows; + int tns_max_bands; + int predictor_present; + int predictor_initialized; + int predictor_reset_group; + uint8_t prediction_used[41]; + uint8_t window_clipping[8]; ///< set if a certain window is near clipping +} IndividualChannelStream; + +/** + * Temporal Noise Shaping + */ +typedef struct TemporalNoiseShaping { + int present; + int n_filt[8]; + int length[8][4]; + int direction[8][4]; + int order[8][4]; + INTFLOAT coef[8][4][TNS_MAX_ORDER]; +} TemporalNoiseShaping; + +/** + * coupling parameters + */ +typedef struct ChannelCoupling { + enum CouplingPoint coupling_point; ///< The point during decoding at which coupling is applied. + int num_coupled; ///< number of target elements + enum RawDataBlockType type[8]; ///< Type of channel element to be coupled - SCE or CPE. + int id_select[8]; ///< element id + int ch_select[8]; /**< [0] shared list of gains; [1] list of gains for right channel; + * [2] list of gains for left channel; [3] lists of gains for both channels + */ + INTFLOAT gain[16][120]; +} ChannelCoupling; + +/** + * Single Channel Element - used for both SCE and LFE elements. + */ +typedef struct SingleChannelElement { + IndividualChannelStream ics; + TemporalNoiseShaping tns; + enum BandType band_type[128]; ///< band types + int band_type_run_end[120]; ///< band type run end points + INTFLOAT sf[120]; ///< scalefactors + DECLARE_ALIGNED(32, INTFLOAT, coeffs)[1024]; ///< coefficients for IMDCT, maybe processed + DECLARE_ALIGNED(32, INTFLOAT, saved)[1536]; ///< overlap + DECLARE_ALIGNED(32, INTFLOAT, ret_buf)[2048]; ///< PCM output buffer + DECLARE_ALIGNED(16, INTFLOAT, ltp_state)[3072]; ///< time signal for LTP + PredictorState predictor_state[MAX_PREDICTORS]; + INTFLOAT *ret; ///< PCM output +} SingleChannelElement; + +/** + * channel element - generic struct for SCE/CPE/CCE/LFE + */ +typedef struct ChannelElement { + int present; + // CPE specific + uint8_t ms_mask[128]; ///< Set if mid/side stereo is used for each scalefactor window band + // shared + SingleChannelElement ch[2]; + // CCE specific + ChannelCoupling coup; + SpectralBandReplication sbr; +} ChannelElement; + +typedef struct OutputConfiguration { + MPEG4AudioConfig m4ac; + uint8_t layout_map[MAX_ELEM_ID*4][3]; + int layout_map_tags; + AVChannelLayout ch_layout; + enum OCStatus status; +} OutputConfiguration; + +/** + * Dynamic Range Control - decoded from the bitstream but not processed further. + */ +typedef struct DynamicRangeControl { + int pce_instance_tag; ///< Indicates with which program the DRC info is associated. + int dyn_rng_sgn[17]; ///< DRC sign information; 0 - positive, 1 - negative + int dyn_rng_ctl[17]; ///< DRC magnitude information + int exclude_mask[MAX_CHANNELS]; ///< Channels to be excluded from DRC processing. + int band_incr; ///< Number of DRC bands greater than 1 having DRC info. + int interpolation_scheme; ///< Indicates the interpolation scheme used in the SBR QMF domain. + int band_top[17]; ///< Indicates the top of the i-th DRC band in units of 4 spectral lines. + int prog_ref_level; /**< A reference level for the long-term program audio level for all + * channels combined. + */ +} DynamicRangeControl; + +/** + * main AAC decoding context + */ +typedef struct AACDecContext { + const struct AVClass *class; + struct AVCodecContext *avctx; + struct AVFrame *frame; + + int is_saved; ///< Set if elements have stored overlap from previous frame. + DynamicRangeControl che_drc; + + /** + * @name Channel element related data + * @{ + */ + ChannelElement *che[4][MAX_ELEM_ID]; + ChannelElement *tag_che_map[4][MAX_ELEM_ID]; + int tags_mapped; + int warned_remapping_once; + /** @} */ + + /** + * @name temporary aligned temporary buffers + * (We do not want to have these on the stack.) + * @{ + */ + DECLARE_ALIGNED(32, INTFLOAT, buf_mdct)[1024]; + DECLARE_ALIGNED(32, INTFLOAT, temp)[128]; + /** @} */ + + /** + * @name Computed / set up during initialization + * @{ + */ + AVTXContext *mdct120; + AVTXContext *mdct128; + AVTXContext *mdct480; + AVTXContext *mdct512; + AVTXContext *mdct960; + AVTXContext *mdct1024; + AVTXContext *mdct_ltp; + + av_tx_fn mdct120_fn; + av_tx_fn mdct128_fn; + av_tx_fn mdct480_fn; + av_tx_fn mdct512_fn; + av_tx_fn mdct960_fn; + av_tx_fn mdct1024_fn; + av_tx_fn mdct_ltp_fn; +#if USE_FIXED + AVFixedDSPContext *fdsp; +#else + AVFloatDSPContext *fdsp; +#endif /* USE_FIXED */ + int random_state; + /** @} */ + + /** + * @name Members used for output + * @{ + */ + SingleChannelElement *output_element[MAX_CHANNELS]; ///< Points to each SingleChannelElement + /** @} */ + + + /** + * @name Japanese DTV specific extension + * @{ + */ + int force_dmono_mode;///< 0->not dmono, 1->use first channel, 2->use second channel + int dmono_mode; ///< 0->not dmono, 1->use first channel, 2->use second channel + /** @} */ + + enum AACOutputChannelOrder output_channel_order; + + OutputConfiguration oc[2]; + int warned_num_aac_frames; + int warned_960_sbr; + unsigned warned_71_wide; + int warned_gain_control; + int warned_he_aac_mono; + + /* aacdec functions pointers */ + void (*imdct_and_windowing)(struct AACDecContext *ac, SingleChannelElement *sce); + void (*apply_ltp)(struct AACDecContext *ac, SingleChannelElement *sce); + void (*apply_tns)(INTFLOAT coef[1024], TemporalNoiseShaping *tns, + IndividualChannelStream *ics, int decode); + void (*windowing_and_mdct_ltp)(struct AACDecContext *ac, INTFLOAT *out, + INTFLOAT *in, IndividualChannelStream *ics); + void (*update_ltp)(struct AACDecContext *ac, SingleChannelElement *sce); + void (*vector_pow43)(int *coefs, int len); + void (*subband_scale)(int *dst, int *src, int scale, int offset, int len, void *log_context); +} AACDecContext; + +void ff_aacdec_init_mips(AACDecContext *c); + +#endif /* AVCODEC_AACDEC_H */ diff --git a/libavcodec/aacdec_common.c b/libavcodec/aacdec_common.c new file mode 100644 index 00000000000..145c7180477 --- /dev/null +++ b/libavcodec/aacdec_common.c @@ -0,0 +1,307 @@ +/* + * Common code and tables of the AAC fixed- and floating-point decoders + * Copyright (c) 2005-2006 Oded Shimon ( ods15 ods15 dyndns org ) + * Copyright (c) 2006-2007 Maxim Gavrilov ( maxim.gavrilov gmail com ) + * + * 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 + * Common code and tables of the AAC fixed- and floating-point decoders + */ + +#include "aac.h" +#include "aacdectab.h" +#include "aacps.h" +#include "aactab.h" +#include "vlc.h" + +#include "libavutil/attributes.h" +#include "libavutil/thread.h" + +const int8_t ff_tags_per_config[16] = { 0, 1, 1, 2, 3, 3, 4, 5, 0, 0, 0, 5, 5, 16, 5, 0 }; + +const uint8_t ff_aac_channel_layout_map[16][16][3] = { + { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, }, + { { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, }, + { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, }, + { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_SCE, 1, AAC_CHANNEL_BACK }, }, + { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, }, + { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, }, + { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, }, + { { 0, } }, + { { 0, } }, + { { 0, } }, + { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, { TYPE_SCE, 1, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, }, + { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, { TYPE_CPE, 2, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, }, + { + { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, // SCE1 = FC, + { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, // CPE1 = FLc and FRc, + { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, // CPE2 = FL and FR, + { TYPE_CPE, 2, AAC_CHANNEL_BACK }, // CPE3 = SiL and SiR, + { TYPE_CPE, 3, AAC_CHANNEL_BACK }, // CPE4 = BL and BR, + { TYPE_SCE, 1, AAC_CHANNEL_BACK }, // SCE2 = BC, + { TYPE_LFE, 0, AAC_CHANNEL_LFE }, // LFE1 = LFE1, + { TYPE_LFE, 1, AAC_CHANNEL_LFE }, // LFE2 = LFE2, + { TYPE_SCE, 2, AAC_CHANNEL_FRONT }, // SCE3 = TpFC, + { TYPE_CPE, 4, AAC_CHANNEL_FRONT }, // CPE5 = TpFL and TpFR, + { TYPE_CPE, 5, AAC_CHANNEL_SIDE }, // CPE6 = TpSiL and TpSiR, + { TYPE_SCE, 3, AAC_CHANNEL_SIDE }, // SCE4 = TpC, + { TYPE_CPE, 6, AAC_CHANNEL_BACK }, // CPE7 = TpBL and TpBR, + { TYPE_SCE, 4, AAC_CHANNEL_BACK }, // SCE5 = TpBC, + { TYPE_SCE, 5, AAC_CHANNEL_FRONT }, // SCE6 = BtFC, + { TYPE_CPE, 7, AAC_CHANNEL_FRONT }, // CPE8 = BtFL and BtFR + }, + { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, { TYPE_CPE, 2, AAC_CHANNEL_FRONT }, }, + { { 0, } }, +}; + +const int16_t ff_aac_channel_map[3][4][6] = { + { + { AV_CHAN_FRONT_CENTER, AV_CHAN_FRONT_LEFT_OF_CENTER, AV_CHAN_FRONT_RIGHT_OF_CENTER, AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_NONE }, + { AV_CHAN_UNUSED, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE }, + { AV_CHAN_UNUSED, AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, AV_CHAN_BACK_LEFT, AV_CHAN_BACK_RIGHT, AV_CHAN_BACK_CENTER }, + { AV_CHAN_LOW_FREQUENCY, AV_CHAN_LOW_FREQUENCY_2, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE }, + }, + { + { AV_CHAN_TOP_FRONT_CENTER, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_TOP_FRONT_LEFT, AV_CHAN_TOP_FRONT_RIGHT, AV_CHAN_NONE }, + { AV_CHAN_UNUSED, AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_TOP_SIDE_RIGHT, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_TOP_CENTER}, + { AV_CHAN_UNUSED, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_TOP_BACK_LEFT, AV_CHAN_TOP_BACK_RIGHT, AV_CHAN_TOP_BACK_CENTER}, + { AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE}, + }, + { + { AV_CHAN_BOTTOM_FRONT_CENTER, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_BOTTOM_FRONT_LEFT, AV_CHAN_BOTTOM_FRONT_RIGHT, AV_CHAN_NONE }, + { AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE }, + { AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE }, + { AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE }, + }, +}; + +const AVChannelLayout ff_aac_ch_layout[] = { + AV_CHANNEL_LAYOUT_MONO, + AV_CHANNEL_LAYOUT_STEREO, + AV_CHANNEL_LAYOUT_SURROUND, + AV_CHANNEL_LAYOUT_4POINT0, + AV_CHANNEL_LAYOUT_5POINT0_BACK, + AV_CHANNEL_LAYOUT_5POINT1_BACK, + AV_CHANNEL_LAYOUT_7POINT1_WIDE_BACK, + AV_CHANNEL_LAYOUT_6POINT1_BACK, + AV_CHANNEL_LAYOUT_7POINT1, + AV_CHANNEL_LAYOUT_22POINT2, + AV_CHANNEL_LAYOUT_7POINT1_TOP_BACK, + { 0 }, +}; + +VLCElem ff_vlc_scalefactors[352]; +const VLCElem *ff_vlc_spectral[11]; + +/// Huffman tables for SBR + +static const uint8_t sbr_huffman_tab[][2] = { + /* t_huffman_env_1_5dB - 121 entries */ + { 60, 2 }, { 59, 2 }, { 61, 3 }, { 58, 3 }, { 62, 4 }, + { 57, 4 }, { 63, 5 }, { 56, 5 }, { 64, 6 }, { 55, 6 }, + { 65, 7 }, { 54, 7 }, { 66, 8 }, { 53, 8 }, { 67, 9 }, + { 52, 9 }, { 51, 10 }, { 68, 10 }, { 50, 11 }, { 69, 12 }, + { 49, 12 }, { 70, 13 }, { 48, 13 }, { 47, 13 }, { 71, 14 }, + { 46, 14 }, { 72, 14 }, { 45, 14 }, { 44, 15 }, { 73, 15 }, + { 41, 16 }, { 42, 16 }, { 43, 16 }, { 74, 16 }, { 36, 16 }, + { 40, 16 }, { 76, 16 }, { 34, 17 }, { 39, 17 }, { 75, 17 }, + { 37, 17 }, { 35, 18 }, { 38, 18 }, { 0, 18 }, { 1, 18 }, + { 2, 18 }, { 3, 18 }, { 4, 18 }, { 5, 18 }, { 6, 19 }, + { 7, 19 }, { 8, 19 }, { 9, 19 }, { 10, 19 }, { 11, 19 }, + { 12, 19 }, { 13, 19 }, { 14, 19 }, { 15, 19 }, { 16, 19 }, + { 17, 19 }, { 18, 19 }, { 19, 19 }, { 20, 19 }, { 21, 19 }, + { 22, 19 }, { 23, 19 }, { 24, 19 }, { 25, 19 }, { 26, 19 }, + { 27, 19 }, { 28, 19 }, { 29, 19 }, { 30, 19 }, { 31, 19 }, + { 32, 19 }, { 33, 19 }, { 77, 19 }, { 78, 19 }, { 79, 19 }, + { 80, 19 }, { 81, 19 }, { 82, 19 }, { 83, 19 }, { 84, 19 }, + { 85, 19 }, { 86, 19 }, { 87, 19 }, { 88, 19 }, { 89, 19 }, + { 90, 19 }, { 91, 19 }, { 92, 19 }, { 93, 19 }, { 94, 19 }, + { 95, 19 }, { 96, 19 }, { 97, 19 }, { 98, 19 }, { 99, 19 }, + { 100, 19 }, { 101, 19 }, { 102, 19 }, { 103, 19 }, { 104, 19 }, + { 105, 19 }, { 106, 19 }, { 107, 19 }, { 108, 19 }, { 109, 19 }, + { 110, 19 }, { 111, 19 }, { 112, 19 }, { 113, 19 }, { 114, 19 }, + { 115, 19 }, { 116, 19 }, { 117, 19 }, { 118, 19 }, { 119, 19 }, + { 120, 19 }, + /* f_huffman_env_1_5dB - 121 entries */ + { 60, 2 }, { 59, 2 }, { 61, 3 }, { 58, 3 }, { 57, 4 }, + { 62, 4 }, { 56, 5 }, { 63, 5 }, { 55, 6 }, { 64, 6 }, + { 54, 7 }, { 65, 8 }, { 53, 8 }, { 66, 8 }, { 52, 9 }, + { 67, 9 }, { 51, 9 }, { 68, 10 }, { 50, 10 }, { 69, 11 }, + { 49, 11 }, { 70, 11 }, { 71, 11 }, { 48, 12 }, { 72, 12 }, + { 47, 12 }, { 73, 12 }, { 74, 13 }, { 46, 13 }, { 45, 13 }, + { 75, 13 }, { 76, 14 }, { 77, 14 }, { 44, 14 }, { 43, 15 }, + { 42, 15 }, { 41, 16 }, { 78, 16 }, { 79, 16 }, { 40, 16 }, + { 39, 16 }, { 80, 17 }, { 81, 17 }, { 36, 17 }, { 37, 17 }, + { 38, 17 }, { 34, 17 }, { 32, 18 }, { 82, 18 }, { 83, 18 }, + { 85, 18 }, { 19, 18 }, { 35, 18 }, { 86, 18 }, { 87, 18 }, + { 30, 18 }, { 33, 18 }, { 84, 18 }, { 88, 18 }, { 104, 18 }, + { 9, 19 }, { 14, 19 }, { 16, 19 }, { 17, 19 }, { 23, 19 }, + { 27, 19 }, { 29, 19 }, { 31, 19 }, { 90, 19 }, { 97, 19 }, + { 102, 19 }, { 107, 19 }, { 108, 19 }, { 0, 19 }, { 1, 19 }, + { 2, 20 }, { 3, 20 }, { 4, 20 }, { 5, 20 }, { 6, 20 }, + { 7, 20 }, { 8, 20 }, { 10, 20 }, { 11, 20 }, { 12, 20 }, + { 13, 20 }, { 15, 20 }, { 18, 20 }, { 20, 20 }, { 21, 20 }, + { 22, 20 }, { 24, 20 }, { 25, 20 }, { 26, 20 }, { 28, 20 }, + { 89, 20 }, { 91, 20 }, { 92, 20 }, { 93, 20 }, { 94, 20 }, + { 95, 20 }, { 96, 20 }, { 98, 20 }, { 99, 20 }, { 100, 20 }, + { 101, 20 }, { 103, 20 }, { 105, 20 }, { 106, 20 }, { 109, 20 }, + { 110, 20 }, { 111, 20 }, { 112, 20 }, { 113, 20 }, { 114, 20 }, + { 115, 20 }, { 116, 20 }, { 117, 20 }, { 118, 20 }, { 119, 20 }, + { 120, 20 }, + /* t_huffman_env_bal_1_5dB - 49 entries */ + { 24, 1 }, { 25, 2 }, { 23, 3 }, { 26, 4 }, { 22, 5 }, + { 27, 6 }, { 21, 7 }, { 28, 8 }, { 20, 9 }, { 19, 11 }, + { 29, 11 }, { 18, 12 }, { 30, 12 }, { 31, 15 }, { 17, 16 }, + { 32, 16 }, { 0, 16 }, { 1, 16 }, { 2, 16 }, { 3, 16 }, + { 4, 16 }, { 5, 16 }, { 6, 16 }, { 7, 16 }, { 8, 16 }, + { 9, 16 }, { 10, 16 }, { 11, 16 }, { 12, 16 }, { 13, 16 }, + { 14, 16 }, { 15, 16 }, { 16, 16 }, { 33, 16 }, { 34, 16 }, + { 35, 16 }, { 36, 16 }, { 37, 16 }, { 38, 16 }, { 39, 17 }, + { 40, 17 }, { 41, 17 }, { 42, 17 }, { 43, 17 }, { 44, 17 }, + { 45, 17 }, { 46, 17 }, { 47, 17 }, { 48, 17 }, + /* f_huffman_env_bal_1_5dB - 49 entries */ + { 24, 1 }, { 23, 2 }, { 25, 3 }, { 22, 4 }, { 26, 5 }, + { 27, 6 }, { 21, 7 }, { 20, 8 }, { 28, 9 }, { 19, 11 }, + { 29, 11 }, { 18, 11 }, { 30, 12 }, { 17, 14 }, { 31, 15 }, + { 32, 16 }, { 15, 16 }, { 16, 17 }, { 0, 18 }, { 1, 18 }, + { 2, 18 }, { 3, 18 }, { 4, 18 }, { 5, 18 }, { 6, 18 }, + { 7, 18 }, { 8, 18 }, { 9, 18 }, { 10, 18 }, { 11, 18 }, + { 12, 18 }, { 13, 18 }, { 14, 18 }, { 33, 18 }, { 34, 18 }, + { 35, 18 }, { 36, 18 }, { 37, 18 }, { 38, 18 }, { 39, 18 }, + { 40, 18 }, { 41, 18 }, { 42, 18 }, { 43, 18 }, { 44, 18 }, + { 45, 18 }, { 46, 18 }, { 47, 19 }, { 48, 19 }, + /* t_huffman_env_3_0dB - 63 entries */ + { 31, 1 }, { 30, 2 }, { 32, 3 }, { 29, 4 }, { 33, 5 }, + { 28, 6 }, { 34, 7 }, { 27, 8 }, { 35, 9 }, { 26, 11 }, + { 36, 11 }, { 25, 12 }, { 24, 13 }, { 37, 13 }, { 23, 14 }, + { 38, 14 }, { 22, 14 }, { 21, 14 }, { 39, 14 }, { 40, 15 }, + { 41, 16 }, { 18, 16 }, { 20, 16 }, { 19, 16 }, { 17, 17 }, + { 42, 17 }, { 43, 18 }, { 0, 18 }, { 1, 18 }, { 2, 19 }, + { 3, 19 }, { 4, 19 }, { 5, 19 }, { 6, 19 }, { 7, 19 }, + { 8, 19 }, { 9, 19 }, { 10, 19 }, { 11, 19 }, { 12, 19 }, + { 13, 19 }, { 14, 19 }, { 15, 19 }, { 16, 19 }, { 44, 19 }, + { 45, 19 }, { 46, 19 }, { 47, 19 }, { 48, 19 }, { 49, 19 }, + { 50, 19 }, { 51, 19 }, { 52, 19 }, { 53, 19 }, { 54, 19 }, + { 55, 19 }, { 56, 19 }, { 57, 19 }, { 58, 19 }, { 59, 19 }, + { 60, 19 }, { 61, 19 }, { 62, 19 }, + /* f_huffman_env_3_0dB - 63 entries */ + { 31, 1 }, { 30, 2 }, { 32, 3 }, { 29, 4 }, { 33, 5 }, + { 28, 6 }, { 34, 8 }, { 27, 8 }, { 35, 9 }, { 26, 9 }, + { 36, 10 }, { 25, 10 }, { 37, 11 }, { 24, 11 }, { 38, 12 }, + { 23, 12 }, { 39, 13 }, { 40, 14 }, { 22, 14 }, { 21, 15 }, + { 41, 15 }, { 42, 15 }, { 20, 16 }, { 19, 16 }, { 43, 16 }, + { 44, 16 }, { 18, 17 }, { 16, 17 }, { 45, 17 }, { 46, 17 }, + { 17, 18 }, { 49, 18 }, { 13, 18 }, { 7, 18 }, { 12, 18 }, + { 47, 18 }, { 48, 18 }, { 9, 19 }, { 10, 19 }, { 15, 19 }, + { 51, 19 }, { 52, 19 }, { 53, 19 }, { 56, 19 }, { 8, 19 }, + { 11, 19 }, { 55, 19 }, { 0, 20 }, { 1, 20 }, { 2, 20 }, + { 3, 20 }, { 4, 20 }, { 5, 20 }, { 6, 20 }, { 14, 20 }, + { 50, 20 }, { 54, 20 }, { 57, 20 }, { 58, 20 }, { 59, 20 }, + { 60, 20 }, { 61, 20 }, { 62, 20 }, + /* t_huffman_env_bal_3_0dB - 25 entries */ + { 12, 1 }, { 13, 2 }, { 11, 3 }, { 10, 4 }, { 14, 5 }, + { 15, 6 }, { 9, 7 }, { 8, 8 }, { 16, 9 }, { 7, 12 }, + { 0, 13 }, { 1, 13 }, { 2, 13 }, { 3, 13 }, { 4, 13 }, + { 5, 13 }, { 6, 13 }, { 17, 13 }, { 18, 13 }, { 19, 13 }, + { 20, 13 }, { 21, 13 }, { 22, 13 }, { 23, 14 }, { 24, 14 }, + /* f_huffman_env_bal_3_0dB - 25 entries */ + { 12, 1 }, { 11, 2 }, { 13, 3 }, { 10, 4 }, { 14, 5 }, + { 15, 6 }, { 9, 7 }, { 8, 8 }, { 16, 9 }, { 7, 11 }, + { 17, 12 }, { 18, 13 }, { 0, 13 }, { 1, 13 }, { 2, 13 }, + { 3, 13 }, { 4, 13 }, { 5, 14 }, { 6, 14 }, { 19, 14 }, + { 20, 14 }, { 21, 14 }, { 22, 14 }, { 23, 14 }, { 24, 14 }, + /* t_huffman_noise_3_0dB - 63 entries */ + { 31, 1 }, { 32, 2 }, { 30, 3 }, { 29, 4 }, { 33, 5 }, + { 28, 6 }, { 34, 8 }, { 27, 8 }, { 35, 10 }, { 26, 11 }, + { 36, 13 }, { 42, 13 }, { 0, 13 }, { 1, 13 }, { 2, 13 }, + { 3, 13 }, { 4, 13 }, { 5, 13 }, { 6, 13 }, { 7, 13 }, + { 8, 13 }, { 9, 13 }, { 10, 13 }, { 11, 13 }, { 12, 13 }, + { 13, 13 }, { 14, 13 }, { 15, 13 }, { 16, 13 }, { 17, 13 }, + { 18, 13 }, { 19, 13 }, { 20, 13 }, { 21, 13 }, { 22, 13 }, + { 23, 13 }, { 24, 13 }, { 25, 13 }, { 37, 13 }, { 38, 13 }, + { 39, 13 }, { 40, 13 }, { 41, 13 }, { 43, 13 }, { 44, 13 }, + { 45, 13 }, { 46, 13 }, { 47, 13 }, { 48, 13 }, { 49, 13 }, + { 50, 13 }, { 51, 13 }, { 52, 13 }, { 53, 13 }, { 54, 13 }, + { 55, 13 }, { 56, 13 }, { 57, 13 }, { 58, 13 }, { 59, 13 }, + { 60, 13 }, { 61, 14 }, { 62, 14 }, + /* t_huffman_noise_bal_3_0dB - 25 entries */ + { 12, 1 }, { 11, 2 }, { 13, 3 }, { 10, 5 }, { 14, 6 }, + { 0, 8 }, { 1, 8 }, { 2, 8 }, { 3, 8 }, { 4, 8 }, + { 5, 8 }, { 6, 8 }, { 7, 8 }, { 8, 8 }, { 9, 8 }, + { 15, 8 }, { 16, 8 }, { 17, 8 }, { 18, 8 }, { 19, 8 }, + { 20, 8 }, { 21, 8 }, { 22, 8 }, { 23, 8 }, { 24, 8 }, +}; + +static const uint8_t sbr_huffman_nb_codes[] = { + 121, 121, 49, 49, 63, 63, 25, 25, 63, 25 +}; + +static const int8_t sbr_vlc_offsets[10] = { + -60, -60, -24, -24, -31, -31, -12, -12, -31, -12 +}; + +const VLCElem *ff_aac_sbr_vlc[10]; + +static av_cold void aacdec_common_init(void) +{ + static VLCElem vlc_buf[(304 + 270 + 550 + 300 + 328 + + 294 + 306 + 268 + 510 + 366 + 462) + + (1098 + 1092 + 768 + 1026 + 1058 + + 1052 + 544 + 544 + 592 + 512)]; + VLCInitState state = VLC_INIT_STATE(vlc_buf); + const uint8_t (*tab)[2] = sbr_huffman_tab; + + for (unsigned i = 0; i < 11; i++) { +#define TAB_WRAP_SIZE(name) name[i], sizeof(name[i][0]), sizeof(name[i][0]) + ff_vlc_spectral[i] = + ff_vlc_init_tables_sparse(&state, 8, ff_aac_spectral_sizes[i], + TAB_WRAP_SIZE(ff_aac_spectral_bits), + TAB_WRAP_SIZE(ff_aac_spectral_codes), + TAB_WRAP_SIZE(ff_aac_codebook_vector_idx), + 0); + } + + VLC_INIT_STATIC_TABLE(ff_vlc_scalefactors, 7, + FF_ARRAY_ELEMS(ff_aac_scalefactor_code), + ff_aac_scalefactor_bits, + sizeof(ff_aac_scalefactor_bits[0]), + sizeof(ff_aac_scalefactor_bits[0]), + ff_aac_scalefactor_code, + sizeof(ff_aac_scalefactor_code[0]), + sizeof(ff_aac_scalefactor_code[0]), 0); + + // SBR VLC table initialization + for (int i = 0; i < FF_ARRAY_ELEMS(ff_aac_sbr_vlc); i++) { + ff_aac_sbr_vlc[i] = + ff_vlc_init_tables_from_lengths(&state, 9, sbr_huffman_nb_codes[i], + &tab[0][1], 2, + &tab[0][0], 2, 1, + sbr_vlc_offsets[i], 0); + tab += sbr_huffman_nb_codes[i]; + } + + ff_ps_init_common(); +} + +av_cold void ff_aacdec_common_init_once(void) +{ + static AVOnce init_static_once = AV_ONCE_INIT; + ff_thread_once(&init_static_once, aacdec_common_init); +} diff --git a/libavcodec/aacdec_fixed.c b/libavcodec/aacdec_fixed.c index 46f0ebf91bd..305bb0ba9a3 100644 --- a/libavcodec/aacdec_fixed.c +++ b/libavcodec/aacdec_fixed.c @@ -66,11 +66,11 @@ #include "avcodec.h" #include "codec_internal.h" #include "get_bits.h" -#include "lpc.h" #include "kbdwin.h" #include "sinewin_fixed_tablegen.h" #include "aac.h" +#include "aacdec.h" #include "aactab.h" #include "aacdectab.h" #include "adts_header.h" @@ -89,6 +89,49 @@ DECLARE_ALIGNED(32, static int, AAC_RENAME2(aac_kbd_short_128))[128]; DECLARE_ALIGNED(32, static int, AAC_RENAME2(aac_kbd_long_960))[960]; DECLARE_ALIGNED(32, static int, AAC_RENAME2(aac_kbd_short_120))[120]; +/* @name ltp_coef + * Table of the LTP coefficients + */ +static const int ltp_coef_fixed[8] = { + Q30(0.570829), Q30(0.696616), Q30(0.813004), Q30(0.911304), + Q30(0.984900), Q30(1.067894), Q30(1.194601), Q30(1.369533), +}; + +/* @name tns_tmp2_map + * Tables of the tmp2[] arrays of LPC coefficients used for TNS. + * The suffix _M_N[] indicate the values of coef_compress and coef_res + * respectively. + * @{ + */ +static const int tns_tmp2_map_1_3[4] = { + Q31(0.00000000), Q31(-0.43388373), Q31(0.64278758), Q31(0.34202015), +}; + +static const int tns_tmp2_map_0_3[8] = { + Q31(0.00000000), Q31(-0.43388373), Q31(-0.78183150), Q31(-0.97492790), + Q31(0.98480773), Q31( 0.86602539), Q31( 0.64278758), Q31( 0.34202015), +}; + +static const int tns_tmp2_map_1_4[8] = { + Q31(0.00000000), Q31(-0.20791170), Q31(-0.40673664), Q31(-0.58778524), + Q31(0.67369562), Q31( 0.52643216), Q31( 0.36124167), Q31( 0.18374951), +}; + +static const int tns_tmp2_map_0_4[16] = { + Q31( 0.00000000), Q31(-0.20791170), Q31(-0.40673664), Q31(-0.58778524), + Q31(-0.74314481), Q31(-0.86602539), Q31(-0.95105654), Q31(-0.99452192), + Q31( 0.99573416), Q31( 0.96182561), Q31( 0.89516330), Q31( 0.79801720), + Q31( 0.67369562), Q31( 0.52643216), Q31( 0.36124167), Q31( 0.18374951), +}; + +static const int * const tns_tmp2_map_fixed[4] = { + tns_tmp2_map_0_3, + tns_tmp2_map_0_4, + tns_tmp2_map_1_3, + tns_tmp2_map_1_4 +}; +// @} + static av_always_inline void reset_predict_state(PredictorState *ps) { ps->r0.mant = 0; @@ -354,7 +397,7 @@ static const int cce_scale_fixed[8] = { * * @param index index into coupling gain array */ -static void apply_dependent_coupling_fixed(AACContext *ac, +static void apply_dependent_coupling_fixed(AACDecContext *ac, SingleChannelElement *target, ChannelElement *cce, int index) { @@ -418,7 +461,7 @@ static void apply_dependent_coupling_fixed(AACContext *ac, * * @param index index into coupling gain array */ -static void apply_independent_coupling_fixed(AACContext *ac, +static void apply_independent_coupling_fixed(AACDecContext *ac, SingleChannelElement *target, ChannelElement *cce, int index) { @@ -456,7 +499,7 @@ const FFCodec ff_aac_fixed_decoder = { CODEC_LONG_NAME("AAC (Advanced Audio Coding)"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_AAC, - .priv_data_size = sizeof(AACContext), + .priv_data_size = sizeof(AACDecContext), .init = aac_decode_init, .close = aac_decode_close, FF_CODEC_DECODE_CB(aac_decode_frame), @@ -465,8 +508,7 @@ const FFCodec ff_aac_fixed_decoder = { }, .p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, - CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(aac_channel_layout) - .p.ch_layouts = aac_ch_layout, + .p.ch_layouts = ff_aac_ch_layout, .p.priv_class = &aac_decoder_class, .p.profiles = NULL_IF_CONFIG_SMALL(ff_aac_profiles), .flush = flush, diff --git a/libavcodec/aacdec_template.c b/libavcodec/aacdec_template.c index f7ebf5eca61..aa856d7fe10 100644 --- a/libavcodec/aacdec_template.c +++ b/libavcodec/aacdec_template.c @@ -93,11 +93,9 @@ #include "libavutil/thread.h" #include "decode.h" #include "internal.h" +#include "lpc_functions.h" -static VLC vlc_scalefactors; -static VLC vlc_spectral[11]; - -static int output_configure(AACContext *ac, +static int output_configure(AACDecContext *ac, uint8_t layout_map[MAX_ELEM_ID*4][3], int tags, enum OCStatus oc_type, int get_new_frame); @@ -127,7 +125,7 @@ static int count_channels(uint8_t (*layout)[3], int tags) * * @return Returns error status. 0 - OK, !0 - error */ -static av_cold int che_configure(AACContext *ac, +static av_cold int che_configure(AACDecContext *ac, enum ChannelPosition che_pos, int type, int id, int *channels) { @@ -163,7 +161,7 @@ static av_cold int che_configure(AACContext *ac, static int frame_configure_elements(AVCodecContext *avctx) { - AACContext *ac = avctx->priv_data; + AACDecContext *ac = avctx->priv_data; int type, id, ch, ret; /* set channel pointers to internal buffers by default */ @@ -283,10 +281,10 @@ static int assign_channels(struct elem_to_channel e2c_vec[MAX_ELEM_ID], uint8_t if (pos == AAC_CHANNEL_LFE) { while (nb_channels) { - if (aac_channel_map[layer][pos - 1][j] == AV_CHAN_NONE) + if (ff_aac_channel_map[layer][pos - 1][j] == AV_CHAN_NONE) return -1; e2c_vec[i] = (struct elem_to_channel) { - .av_position = 1ULL << aac_channel_map[layer][pos - 1][j], + .av_position = 1ULL << ff_aac_channel_map[layer][pos - 1][j], .syn_ele = layout_map[i][0], .elem_id = layout_map[i][1], .aac_position = pos @@ -302,12 +300,12 @@ static int assign_channels(struct elem_to_channel e2c_vec[MAX_ELEM_ID], uint8_t } while (nb_channels & 1) { - if (aac_channel_map[layer][pos - 1][0] == AV_CHAN_NONE) + if (ff_aac_channel_map[layer][pos - 1][0] == AV_CHAN_NONE) return -1; - if (aac_channel_map[layer][pos - 1][0] == AV_CHAN_UNUSED) + if (ff_aac_channel_map[layer][pos - 1][0] == AV_CHAN_UNUSED) break; e2c_vec[i] = (struct elem_to_channel) { - .av_position = 1ULL << aac_channel_map[layer][pos - 1][0], + .av_position = 1ULL << ff_aac_channel_map[layer][pos - 1][0], .syn_ele = layout_map[i][0], .elem_id = layout_map[i][1], .aac_position = pos @@ -319,21 +317,21 @@ static int assign_channels(struct elem_to_channel e2c_vec[MAX_ELEM_ID], uint8_t j = (pos != AAC_CHANNEL_SIDE) && nb_channels <= 3 ? 3 : 1; while (nb_channels >= 2) { - if (aac_channel_map[layer][pos - 1][j] == AV_CHAN_NONE || - aac_channel_map[layer][pos - 1][j+1] == AV_CHAN_NONE) + if (ff_aac_channel_map[layer][pos - 1][j] == AV_CHAN_NONE || + ff_aac_channel_map[layer][pos - 1][j+1] == AV_CHAN_NONE) return -1; i += assign_pair(e2c_vec, layout_map, i, - 1ULL << aac_channel_map[layer][pos - 1][j], - 1ULL << aac_channel_map[layer][pos - 1][j+1], + 1ULL << ff_aac_channel_map[layer][pos - 1][j], + 1ULL << ff_aac_channel_map[layer][pos - 1][j+1], pos, layout); j += 2; nb_channels -= 2; } while (nb_channels & 1) { - if (aac_channel_map[layer][pos - 1][5] == AV_CHAN_NONE) + if (ff_aac_channel_map[layer][pos - 1][5] == AV_CHAN_NONE) return -1; e2c_vec[i] = (struct elem_to_channel) { - .av_position = 1ULL << aac_channel_map[layer][pos - 1][5], + .av_position = 1ULL << ff_aac_channel_map[layer][pos - 1][5], .syn_ele = layout_map[i][0], .elem_id = layout_map[i][1], .aac_position = pos @@ -414,7 +412,8 @@ static uint64_t sniff_channel_order(uint8_t (*layout_map)[3], int tags) /** * Save current output configuration if and only if it has been locked. */ -static int push_output_configuration(AACContext *ac) { +static int push_output_configuration(AACDecContext *ac) +{ int pushed = 0; if (ac->oc[1].status == OC_LOCKED || ac->oc[0].status == OC_NONE) { @@ -429,7 +428,8 @@ static int push_output_configuration(AACContext *ac) { * Restore the previous output configuration if and only if the current * configuration is unlocked. */ -static void pop_output_configuration(AACContext *ac) { +static void pop_output_configuration(AACDecContext *ac) +{ if (ac->oc[1].status != OC_LOCKED && ac->oc[0].status != OC_NONE) { ac->oc[1] = ac->oc[0]; ac->avctx->ch_layout = ac->oc[1].ch_layout; @@ -444,7 +444,7 @@ static void pop_output_configuration(AACContext *ac) { * * @return Returns error status. 0 - OK, !0 - error */ -static int output_configure(AACContext *ac, +static int output_configure(AACDecContext *ac, uint8_t layout_map[MAX_ELEM_ID * 4][3], int tags, enum OCStatus oc_type, int get_new_frame) { @@ -469,13 +469,6 @@ static int output_configure(AACContext *ac, } // Try to sniff a reasonable channel order, otherwise output the // channels in the order the PCE declared them. -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (avctx->request_channel_layout == AV_CH_LAYOUT_NATIVE) - ac->output_channel_order = CHANNEL_ORDER_CODED; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (ac->output_channel_order == CHANNEL_ORDER_DEFAULT) layout = sniff_channel_order(layout_map, tags); for (i = 0; i < tags; i++) { @@ -519,7 +512,7 @@ FF_ENABLE_DEPRECATION_WARNINGS static void flush(AVCodecContext *avctx) { - AACContext *ac= avctx->priv_data; + AACDecContext *ac= avctx->priv_data; int type, i, j; for (type = 3; type >= 0; type--) { @@ -540,7 +533,7 @@ static void flush(AVCodecContext *avctx) * * @return Returns error status. 0 - OK, !0 - error */ -static int set_default_channel_config(AACContext *ac, AVCodecContext *avctx, +static int set_default_channel_config(AACDecContext *ac, AVCodecContext *avctx, uint8_t (*layout_map)[3], int *tags, int channel_config) @@ -552,8 +545,8 @@ static int set_default_channel_config(AACContext *ac, AVCodecContext *avctx, channel_config); return AVERROR_INVALIDDATA; } - *tags = tags_per_config[channel_config]; - memcpy(layout_map, aac_channel_layout_map[channel_config - 1], + *tags = ff_tags_per_config[channel_config]; + memcpy(layout_map, ff_aac_channel_layout_map[channel_config - 1], *tags * sizeof(*layout_map)); /* @@ -580,7 +573,7 @@ static int set_default_channel_config(AACContext *ac, AVCodecContext *avctx, return 0; } -static ChannelElement *get_che(AACContext *ac, int type, int elem_id) +static ChannelElement *get_che(AACDecContext *ac, int type, int elem_id) { /* For PCE based channel configurations map the channels solely based * on tags. */ @@ -661,7 +654,7 @@ static ChannelElement *get_che(AACContext *ac, int type, int elem_id) * SCE[0] CPE[0] CPE[1] LFE[0]. * If we seem to have encountered such a stream, transfer * the LFE[0] element to the SCE[1]'s mapping */ - if (ac->tags_mapped == tags_per_config[ac->oc[1].m4ac.chan_config] - 1 && (type == TYPE_LFE || type == TYPE_SCE)) { + if (ac->tags_mapped == ff_tags_per_config[ac->oc[1].m4ac.chan_config] - 1 && (type == TYPE_LFE || type == TYPE_SCE)) { if (!ac->warned_remapping_once && (type != TYPE_LFE || elem_id != 0)) { av_log(ac->avctx, AV_LOG_WARNING, "This stream seems to incorrectly report its last channel as %s[%d], mapping to LFE[0]\n", @@ -683,7 +676,7 @@ static ChannelElement *get_che(AACContext *ac, int type, int elem_id) * SCE[0] CPE[0] SCE[1]. * If we seem to have encountered such a stream, transfer * the SCE[1] element to the LFE[0]'s mapping */ - if (ac->tags_mapped == tags_per_config[ac->oc[1].m4ac.chan_config] - 1 && (type == TYPE_LFE || type == TYPE_SCE)) { + if (ac->tags_mapped == ff_tags_per_config[ac->oc[1].m4ac.chan_config] - 1 && (type == TYPE_LFE || type == TYPE_SCE)) { if (!ac->warned_remapping_once && (type != TYPE_SCE || elem_id != 1)) { av_log(ac->avctx, AV_LOG_WARNING, "This stream seems to incorrectly report its last channel as %s[%d], mapping to SCE[1]\n", @@ -833,12 +826,12 @@ static int decode_pce(AVCodecContext *avctx, MPEG4AudioConfig *m4ac, /** * Decode GA "General Audio" specific configuration; reference: table 4.1. * - * @param ac pointer to AACContext, may be null + * @param ac pointer to AACDecContext, may be null * @param avctx pointer to AVCCodecContext, used for logging * * @return Returns error status. 0 - OK, !0 - error */ -static int decode_ga_specific_config(AACContext *ac, AVCodecContext *avctx, +static int decode_ga_specific_config(AACDecContext *ac, AVCodecContext *avctx, GetBitContext *gb, int get_bit_alignment, MPEG4AudioConfig *m4ac, @@ -919,7 +912,7 @@ static int decode_ga_specific_config(AACContext *ac, AVCodecContext *avctx, return 0; } -static int decode_eld_specific_config(AACContext *ac, AVCodecContext *avctx, +static int decode_eld_specific_config(AACDecContext *ac, AVCodecContext *avctx, GetBitContext *gb, MPEG4AudioConfig *m4ac, int channel_config) @@ -979,7 +972,7 @@ static int decode_eld_specific_config(AACContext *ac, AVCodecContext *avctx, /** * Decode audio specific configuration; reference: table 1.13. * - * @param ac pointer to AACContext, may be null + * @param ac pointer to AACDecContext, may be null * @param avctx pointer to AVCCodecContext, used for logging * @param m4ac pointer to MPEG4AudioConfig, used for parsing * @param gb buffer holding an audio specific config @@ -988,7 +981,7 @@ static int decode_eld_specific_config(AACContext *ac, AVCodecContext *avctx, * * @return Returns error status or number of consumed bits. <0 - error */ -static int decode_audio_specific_config_gb(AACContext *ac, +static int decode_audio_specific_config_gb(AACDecContext *ac, AVCodecContext *avctx, MPEG4AudioConfig *m4ac, GetBitContext *gb, @@ -1055,7 +1048,7 @@ static int decode_audio_specific_config_gb(AACContext *ac, return get_bits_count(gb); } -static int decode_audio_specific_config(AACContext *ac, +static int decode_audio_specific_config(AACDecContext *ac, AVCodecContext *avctx, MPEG4AudioConfig *m4ac, const uint8_t *data, int64_t bit_size, @@ -1124,39 +1117,13 @@ static void reset_predictor_group(PredictorState *ps, int group_num) reset_predict_state(&ps[i]); } -static void aacdec_init(AACContext *ac); +static void aacdec_init(AACDecContext *ac); static av_cold void aac_static_table_init(void) { - static VLCElem vlc_buf[304 + 270 + 550 + 300 + 328 + - 294 + 306 + 268 + 510 + 366 + 462]; - for (unsigned i = 0, offset = 0; i < 11; i++) { - vlc_spectral[i].table = &vlc_buf[offset]; - vlc_spectral[i].table_allocated = FF_ARRAY_ELEMS(vlc_buf) - offset; - ff_init_vlc_sparse(&vlc_spectral[i], 8, ff_aac_spectral_sizes[i], - ff_aac_spectral_bits[i], sizeof(ff_aac_spectral_bits[i][0]), - sizeof(ff_aac_spectral_bits[i][0]), - ff_aac_spectral_codes[i], sizeof(ff_aac_spectral_codes[i][0]), - sizeof(ff_aac_spectral_codes[i][0]), - ff_aac_codebook_vector_idx[i], sizeof(ff_aac_codebook_vector_idx[i][0]), - sizeof(ff_aac_codebook_vector_idx[i][0]), - INIT_VLC_STATIC_OVERLONG); - offset += vlc_spectral[i].table_size; - } - AAC_RENAME(ff_aac_sbr_init)(); - ff_aac_tableinit(); - - INIT_VLC_STATIC(&vlc_scalefactors, 7, - FF_ARRAY_ELEMS(ff_aac_scalefactor_code), - ff_aac_scalefactor_bits, - sizeof(ff_aac_scalefactor_bits[0]), - sizeof(ff_aac_scalefactor_bits[0]), - ff_aac_scalefactor_code, - sizeof(ff_aac_scalefactor_code[0]), - sizeof(ff_aac_scalefactor_code[0]), - 352); + ff_aacdec_common_init_once(); // window initialization AAC_RENAME(ff_kbd_window_init)(AAC_RENAME(aac_kbd_long_960), 4.0, 960); @@ -1181,7 +1148,7 @@ static AVOnce aac_table_init = AV_ONCE_INIT; static av_cold int aac_decode_init(AVCodecContext *avctx) { float scale; - AACContext *ac = avctx->priv_data; + AACDecContext *ac = avctx->priv_data; int ret; if (avctx->sample_rate > 96000) @@ -1279,7 +1246,7 @@ static av_cold int aac_decode_init(AVCodecContext *avctx) /** * Skip data_stream_element; reference: table 4.10. */ -static int skip_data_stream_element(AACContext *ac, GetBitContext *gb) +static int skip_data_stream_element(AACDecContext *ac, GetBitContext *gb) { int byte_align = get_bits1(gb); int count = get_bits(gb, 8); @@ -1296,7 +1263,7 @@ static int skip_data_stream_element(AACContext *ac, GetBitContext *gb) return 0; } -static int decode_prediction(AACContext *ac, IndividualChannelStream *ics, +static int decode_prediction(AACDecContext *ac, IndividualChannelStream *ics, GetBitContext *gb) { int sfb; @@ -1324,7 +1291,7 @@ static void decode_ltp(LongTermPrediction *ltp, int sfb; ltp->lag = get_bits(gb, 11); - ltp->coef = ltp_coef[get_bits(gb, 3)]; + ltp->coef = AAC_RENAME2(ltp_coef)[get_bits(gb, 3)]; for (sfb = 0; sfb < FFMIN(max_sfb, MAX_LTP_LONG_SFB); sfb++) ltp->used[sfb] = get_bits1(gb); } @@ -1332,7 +1299,7 @@ static void decode_ltp(LongTermPrediction *ltp, /** * Decode Individual Channel Stream info; reference: table 4.6. */ -static int decode_ics_info(AACContext *ac, IndividualChannelStream *ics, +static int decode_ics_info(AACDecContext *ac, IndividualChannelStream *ics, GetBitContext *gb) { const MPEG4AudioConfig *const m4ac = &ac->oc[1].m4ac; @@ -1458,7 +1425,7 @@ static int decode_ics_info(AACContext *ac, IndividualChannelStream *ics, * * @return Returns error status. 0 - OK, !0 - error */ -static int decode_band_types(AACContext *ac, enum BandType band_type[120], +static int decode_band_types(AACDecContext *ac, enum BandType band_type[120], int band_type_run_end[120], GetBitContext *gb, IndividualChannelStream *ics) { @@ -1507,7 +1474,7 @@ static int decode_band_types(AACContext *ac, enum BandType band_type[120], * * @return Returns error status. 0 - OK, !0 - error */ -static int decode_scalefactors(AACContext *ac, INTFLOAT sf[120], GetBitContext *gb, +static int decode_scalefactors(AACDecContext *ac, INTFLOAT sf[120], GetBitContext *gb, unsigned int global_gain, IndividualChannelStream *ics, enum BandType band_type[120], @@ -1526,7 +1493,7 @@ static int decode_scalefactors(AACContext *ac, INTFLOAT sf[120], GetBitContext * } else if ((band_type[idx] == INTENSITY_BT) || (band_type[idx] == INTENSITY_BT2)) { for (; i < run_end; i++, idx++) { - offset[2] += get_vlc2(gb, vlc_scalefactors.table, 7, 3) - SCALE_DIFF_ZERO; + offset[2] += get_vlc2(gb, ff_vlc_scalefactors, 7, 3) - SCALE_DIFF_ZERO; clipped_offset = av_clip(offset[2], -155, 100); if (offset[2] != clipped_offset) { avpriv_request_sample(ac->avctx, @@ -1545,7 +1512,7 @@ static int decode_scalefactors(AACContext *ac, INTFLOAT sf[120], GetBitContext * if (noise_flag-- > 0) offset[1] += get_bits(gb, NOISE_PRE_BITS) - NOISE_PRE; else - offset[1] += get_vlc2(gb, vlc_scalefactors.table, 7, 3) - SCALE_DIFF_ZERO; + offset[1] += get_vlc2(gb, ff_vlc_scalefactors, 7, 3) - SCALE_DIFF_ZERO; clipped_offset = av_clip(offset[1], -100, 155); if (offset[1] != clipped_offset) { avpriv_request_sample(ac->avctx, @@ -1561,7 +1528,7 @@ static int decode_scalefactors(AACContext *ac, INTFLOAT sf[120], GetBitContext * } } else { for (; i < run_end; i++, idx++) { - offset[0] += get_vlc2(gb, vlc_scalefactors.table, 7, 3) - SCALE_DIFF_ZERO; + offset[0] += get_vlc2(gb, ff_vlc_scalefactors, 7, 3) - SCALE_DIFF_ZERO; if (offset[0] > 255U) { av_log(ac->avctx, AV_LOG_ERROR, "Scalefactor (%d) out of range.\n", offset[0]); @@ -1609,7 +1576,7 @@ static int decode_pulses(Pulse *pulse, GetBitContext *gb, * * @return Returns error status. 0 - OK, !0 - error */ -static int decode_tns(AACContext *ac, TemporalNoiseShaping *tns, +static int decode_tns(AACDecContext *ac, TemporalNoiseShaping *tns, GetBitContext *gb, const IndividualChannelStream *ics) { int w, filt, i, coef_len, coef_res, coef_compress; @@ -1637,7 +1604,7 @@ static int decode_tns(AACContext *ac, TemporalNoiseShaping *tns, tmp2_idx = 2 * coef_compress + coef_res; for (i = 0; i < tns->order[w][filt]; i++) - tns->coef[w][filt][i] = tns_tmp2_map[tmp2_idx][get_bits(gb, coef_len)]; + tns->coef[w][filt][i] = AAC_RENAME2(tns_tmp2_map)[tmp2_idx][get_bits(gb, coef_len)]; } } } @@ -1677,7 +1644,7 @@ static void decode_mid_side_stereo(ChannelElement *cpe, GetBitContext *gb, * * @return Returns error status. 0 - OK, !0 - error */ -static int decode_spectrum_and_dequant(AACContext *ac, INTFLOAT coef[1024], +static int decode_spectrum_and_dequant(AACDecContext *ac, INTFLOAT coef[1024], GetBitContext *gb, const INTFLOAT sf[120], int pulse_present, const Pulse *pulse, const IndividualChannelStream *ics, @@ -1734,7 +1701,7 @@ static int decode_spectrum_and_dequant(AACContext *ac, INTFLOAT coef[1024], #if !USE_FIXED const float *vq = ff_aac_codebook_vector_vals[cbt_m1]; #endif /* !USE_FIXED */ - const VLCElem *vlc_tab = vlc_spectral[cbt_m1].table; + const VLCElem *vlc_tab = ff_vlc_spectral[cbt_m1]; OPEN_READER(re, gb); switch (cbt_m1 >> 1) { @@ -1972,7 +1939,7 @@ static int decode_spectrum_and_dequant(AACContext *ac, INTFLOAT coef[1024], /** * Apply AAC-Main style frequency domain prediction. */ -static void apply_prediction(AACContext *ac, SingleChannelElement *sce) +static void apply_prediction(AACDecContext *ac, SingleChannelElement *sce) { int sfb, k; @@ -2035,7 +2002,7 @@ static void decode_gain_control(SingleChannelElement * sce, GetBitContext * gb) * * @return Returns error status. 0 - OK, !0 - error */ -static int decode_ics(AACContext *ac, SingleChannelElement *sce, +static int decode_ics(AACDecContext *ac, SingleChannelElement *sce, GetBitContext *gb, int common_window, int scale_flag) { Pulse pulse; @@ -2126,7 +2093,7 @@ static int decode_ics(AACContext *ac, SingleChannelElement *sce, /** * Mid/Side stereo decoding; reference: 4.6.8.1.3. */ -static void apply_mid_side_stereo(AACContext *ac, ChannelElement *cpe) +static void apply_mid_side_stereo(AACDecContext *ac, ChannelElement *cpe) { const IndividualChannelStream *ics = &cpe->ch[0].ics; INTFLOAT *ch0 = cpe->ch[0].coeffs; @@ -2164,7 +2131,7 @@ static void apply_mid_side_stereo(AACContext *ac, ChannelElement *cpe) * [1] mask is decoded from bitstream; [2] mask is all 1s; * [3] reserved for scalable AAC */ -static void apply_intensity_stereo(AACContext *ac, +static void apply_intensity_stereo(AACDecContext *ac, ChannelElement *cpe, int ms_present) { const IndividualChannelStream *ics = &cpe->ch[1].ics; @@ -2214,7 +2181,7 @@ static void apply_intensity_stereo(AACContext *ac, * * @return Returns error status. 0 - OK, !0 - error */ -static int decode_cpe(AACContext *ac, GetBitContext *gb, ChannelElement *cpe) +static int decode_cpe(AACDecContext *ac, GetBitContext *gb, ChannelElement *cpe) { int i, ret, common_window, ms_present = 0; int eld_syntax = ac->oc[1].m4ac.object_type == AOT_ER_AAC_ELD; @@ -2267,7 +2234,7 @@ static const float cce_scale[] = { * * @return Returns error status. 0 - OK, !0 - error */ -static int decode_cce(AACContext *ac, GetBitContext *gb, ChannelElement *che) +static int decode_cce(AACDecContext *ac, GetBitContext *gb, ChannelElement *che) { int num_gain = 0; int c, g, sfb, ret; @@ -2308,7 +2275,7 @@ static int decode_cce(AACContext *ac, GetBitContext *gb, ChannelElement *che) INTFLOAT gain_cache = FIXR10(1.); if (c) { cge = coup->coupling_point == AFTER_IMDCT ? 1 : get_bits1(gb); - gain = cge ? get_vlc2(gb, vlc_scalefactors.table, 7, 3) - 60: 0; + gain = cge ? get_vlc2(gb, ff_vlc_scalefactors, 7, 3) - 60: 0; gain_cache = GET_GAIN(scale, gain); #if USE_FIXED if ((abs(gain_cache)-1024) >> 3 > 30) @@ -2322,7 +2289,7 @@ static int decode_cce(AACContext *ac, GetBitContext *gb, ChannelElement *che) for (sfb = 0; sfb < sce->ics.max_sfb; sfb++, idx++) { if (sce->band_type[idx] != ZERO_BT) { if (!cge) { - int t = get_vlc2(gb, vlc_scalefactors.table, 7, 3) - 60; + int t = get_vlc2(gb, ff_vlc_scalefactors, 7, 3) - 60; if (t) { int s = 1; t = gain += t; @@ -2417,7 +2384,7 @@ static int decode_dynamic_range(DynamicRangeControl *che_drc, return n; } -static int decode_fill(AACContext *ac, GetBitContext *gb, int len) { +static int decode_fill(AACDecContext *ac, GetBitContext *gb, int len) { uint8_t buf[256]; int i, major, minor; @@ -2450,7 +2417,7 @@ static int decode_fill(AACContext *ac, GetBitContext *gb, int len) { * * @return Returns number of bytes consumed */ -static int decode_extension_payload(AACContext *ac, GetBitContext *gb, int cnt, +static int decode_extension_payload(AACDecContext *ac, GetBitContext *gb, int cnt, ChannelElement *che, enum RawDataBlockType elem_type) { int crc_flag = 0; @@ -2486,12 +2453,12 @@ static int decode_extension_payload(AACContext *ac, GetBitContext *gb, int cnt, ac->avctx->ch_layout.nb_channels == 1) { ac->oc[1].m4ac.sbr = 1; ac->oc[1].m4ac.ps = 1; - ac->avctx->profile = FF_PROFILE_AAC_HE_V2; + ac->avctx->profile = AV_PROFILE_AAC_HE_V2; output_configure(ac, ac->oc[1].layout_map, ac->oc[1].layout_map_tags, ac->oc[1].status, 1); } else { ac->oc[1].m4ac.sbr = 1; - ac->avctx->profile = FF_PROFILE_AAC_HE; + ac->avctx->profile = AV_PROFILE_AAC_HE; } res = AAC_RENAME(ff_decode_sbr_extension)(ac, &che->sbr, gb, crc_flag, cnt, elem_type); if (ac->oc[1].m4ac.ps == 1 && !ac->warned_he_aac_mono) { @@ -2543,7 +2510,7 @@ static void apply_tns(INTFLOAT coef_param[1024], TemporalNoiseShaping *tns, continue; // tns_decode_coef - AAC_RENAME(compute_lpc_coefs)(tns->coef[w][filt], order, lpc, 0, 0, 0); + compute_lpc_coefs(tns->coef[w][filt], order, lpc, 0, 0, 0); start = ics->swb_offset[FFMIN(bottom, mmm)]; end = ics->swb_offset[FFMIN( top, mmm)]; @@ -2580,7 +2547,7 @@ static void apply_tns(INTFLOAT coef_param[1024], TemporalNoiseShaping *tns, * Apply windowing and MDCT to obtain the spectral * coefficient from the predicted sample by LTP. */ -static void windowing_and_mdct_ltp(AACContext *ac, INTFLOAT *out, +static void windowing_and_mdct_ltp(AACDecContext *ac, INTFLOAT *out, INTFLOAT *in, IndividualChannelStream *ics) { const INTFLOAT *lwindow = ics->use_kb_window[0] ? AAC_RENAME2(aac_kbd_long_1024) : AAC_RENAME2(sine_1024); @@ -2606,7 +2573,7 @@ static void windowing_and_mdct_ltp(AACContext *ac, INTFLOAT *out, /** * Apply the long term prediction */ -static void apply_ltp(AACContext *ac, SingleChannelElement *sce) +static void apply_ltp(AACDecContext *ac, SingleChannelElement *sce) { const LongTermPrediction *ltp = &sce->ics.ltp; const uint16_t *offsets = sce->ics.swb_offset; @@ -2638,7 +2605,7 @@ static void apply_ltp(AACContext *ac, SingleChannelElement *sce) /** * Update the LTP buffer for next frame */ -static void update_ltp(AACContext *ac, SingleChannelElement *sce) +static void update_ltp(AACDecContext *ac, SingleChannelElement *sce) { IndividualChannelStream *ics = &sce->ics; INTFLOAT *saved = sce->saved; @@ -2676,7 +2643,7 @@ static void update_ltp(AACContext *ac, SingleChannelElement *sce) /** * Conduct IMDCT and windowing. */ -static void imdct_and_windowing(AACContext *ac, SingleChannelElement *sce) +static void imdct_and_windowing(AACDecContext *ac, SingleChannelElement *sce) { IndividualChannelStream *ics = &sce->ics; INTFLOAT *in = sce->coeffs; @@ -2740,7 +2707,7 @@ static void imdct_and_windowing(AACContext *ac, SingleChannelElement *sce) /** * Conduct IMDCT and windowing. */ -static void imdct_and_windowing_960(AACContext *ac, SingleChannelElement *sce) +static void imdct_and_windowing_960(AACDecContext *ac, SingleChannelElement *sce) { IndividualChannelStream *ics = &sce->ics; INTFLOAT *in = sce->coeffs; @@ -2801,7 +2768,7 @@ static void imdct_and_windowing_960(AACContext *ac, SingleChannelElement *sce) memcpy( saved, buf + 480, 480 * sizeof(*saved)); } } -static void imdct_and_windowing_ld(AACContext *ac, SingleChannelElement *sce) +static void imdct_and_windowing_ld(AACDecContext *ac, SingleChannelElement *sce) { IndividualChannelStream *ics = &sce->ics; INTFLOAT *in = sce->coeffs; @@ -2826,7 +2793,7 @@ static void imdct_and_windowing_ld(AACContext *ac, SingleChannelElement *sce) memcpy(saved, buf + 256, 256 * sizeof(*saved)); } -static void imdct_and_windowing_eld(AACContext *ac, SingleChannelElement *sce) +static void imdct_and_windowing_eld(AACDecContext *ac, SingleChannelElement *sce) { UINTFLOAT *in = sce->coeffs; INTFLOAT *out = sce->ret; @@ -2894,10 +2861,10 @@ static void imdct_and_windowing_eld(AACContext *ac, SingleChannelElement *sce) * * @param apply_coupling_method pointer to (in)dependent coupling function */ -static void apply_channel_coupling(AACContext *ac, ChannelElement *cc, +static void apply_channel_coupling(AACDecContext *ac, ChannelElement *cc, enum RawDataBlockType type, int elem_id, enum CouplingPoint coupling_point, - void (*apply_coupling_method)(AACContext *ac, SingleChannelElement *target, ChannelElement *cce, int index)) + void (*apply_coupling_method)(AACDecContext *ac, SingleChannelElement *target, ChannelElement *cce, int index)) { int i, c; @@ -2927,10 +2894,10 @@ static void apply_channel_coupling(AACContext *ac, ChannelElement *cc, /** * Convert spectral data to samples, applying all supported tools as appropriate. */ -static void spectral_to_sample(AACContext *ac, int samples) +static void spectral_to_sample(AACDecContext *ac, int samples) { int i, type; - void (*imdct_and_window)(AACContext *ac, SingleChannelElement *sce); + void (*imdct_and_window)(AACDecContext *ac, SingleChannelElement *sce); switch (ac->oc[1].m4ac.object_type) { case AOT_ER_AAC_LD: imdct_and_window = imdct_and_windowing_ld; @@ -2999,7 +2966,7 @@ static void spectral_to_sample(AACContext *ac, int samples) } } -static int parse_adts_frame_header(AACContext *ac, GetBitContext *gb) +static int parse_adts_frame_header(AACDecContext *ac, GetBitContext *gb) { int size; AACADTSHeaderInfo hdr_info; @@ -3061,10 +3028,10 @@ static int parse_adts_frame_header(AACContext *ac, GetBitContext *gb) return size; } -static int aac_decode_er_frame(AVCodecContext *avctx, void *data, +static int aac_decode_er_frame(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, GetBitContext *gb) { - AACContext *ac = avctx->priv_data; + AACDecContext *ac = avctx->priv_data; const MPEG4AudioConfig *const m4ac = &ac->oc[1].m4ac; ChannelElement *che; int err, i; @@ -3075,12 +3042,12 @@ static int aac_decode_er_frame(AVCodecContext *avctx, void *data, if (aot == AOT_ER_AAC_LD || aot == AOT_ER_AAC_ELD) samples >>= 1; - ac->frame = data; + ac->frame = frame; if ((err = frame_configure_elements(avctx)) < 0) return err; - // The FF_PROFILE_AAC_* defines are all object_type - 1 + // The AV_PROFILE_AAC_* defines are all object_type - 1 // This may lead to an undefined profile being signaled ac->avctx->profile = aot - 1; @@ -3091,9 +3058,9 @@ static int aac_decode_er_frame(AVCodecContext *avctx, void *data, chan_config); return AVERROR_INVALIDDATA; } - for (i = 0; i < tags_per_config[chan_config]; i++) { - const int elem_type = aac_channel_layout_map[chan_config-1][i][0]; - const int elem_id = aac_channel_layout_map[chan_config-1][i][1]; + for (i = 0; i < ff_tags_per_config[chan_config]; i++) { + const int elem_type = ff_aac_channel_layout_map[chan_config-1][i][0]; + const int elem_id = ff_aac_channel_layout_map[chan_config-1][i][1]; if (!(che=get_che(ac, elem_type, elem_id))) { av_log(ac->avctx, AV_LOG_ERROR, "channel element %d.%d is not allocated\n", @@ -3137,7 +3104,7 @@ static int aac_decode_frame_int(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, GetBitContext *gb, const AVPacket *avpkt) { - AACContext *ac = avctx->priv_data; + AACDecContext *ac = avctx->priv_data; ChannelElement *che = NULL, *che_prev = NULL; enum RawDataBlockType elem_type, che_prev_type = TYPE_END; int err, elem_id; @@ -3163,7 +3130,7 @@ static int aac_decode_frame_int(AVCodecContext *avctx, AVFrame *frame, if ((err = frame_configure_elements(avctx)) < 0) goto fail; - // The FF_PROFILE_AAC_* defines are all object_type - 1 + // The AV_PROFILE_AAC_* defines are all object_type - 1 // This may lead to an undefined profile being signaled ac->avctx->profile = ac->oc[1].m4ac.object_type - 1; @@ -3346,7 +3313,7 @@ static int aac_decode_frame_int(AVCodecContext *avctx, AVFrame *frame, static int aac_decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, AVPacket *avpkt) { - AACContext *ac = avctx->priv_data; + AACDecContext *ac = avctx->priv_data; const uint8_t *buf = avpkt->data; int buf_size = avpkt->size; GetBitContext gb; @@ -3408,7 +3375,7 @@ static int aac_decode_frame(AVCodecContext *avctx, AVFrame *frame, static av_cold int aac_decode_close(AVCodecContext *avctx) { - AACContext *ac = avctx->priv_data; + AACDecContext *ac = avctx->priv_data; int i, type; for (i = 0; i < MAX_ELEM_ID; i++) { @@ -3431,7 +3398,7 @@ static av_cold int aac_decode_close(AVCodecContext *avctx) return 0; } -static void aacdec_init(AACContext *c) +static void aacdec_init(AACDecContext *c) { c->imdct_and_windowing = imdct_and_windowing; c->apply_ltp = apply_ltp; @@ -3453,23 +3420,24 @@ static void aacdec_init(AACContext *c) * AVOptions for Japanese DTV specific extensions (ADTS only) */ #define AACDEC_FLAGS AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM +#define OFF(field) offsetof(AACDecContext, field) static const AVOption options[] = { {"dual_mono_mode", "Select the channel to decode for dual mono", - offsetof(AACContext, force_dmono_mode), AV_OPT_TYPE_INT, {.i64=-1}, -1, 2, - AACDEC_FLAGS, "dual_mono_mode"}, + OFF(force_dmono_mode), AV_OPT_TYPE_INT, {.i64=-1}, -1, 2, + AACDEC_FLAGS, .unit = "dual_mono_mode"}, - {"auto", "autoselection", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, AACDEC_FLAGS, "dual_mono_mode"}, - {"main", "Select Main/Left channel", 0, AV_OPT_TYPE_CONST, {.i64= 1}, INT_MIN, INT_MAX, AACDEC_FLAGS, "dual_mono_mode"}, - {"sub" , "Select Sub/Right channel", 0, AV_OPT_TYPE_CONST, {.i64= 2}, INT_MIN, INT_MAX, AACDEC_FLAGS, "dual_mono_mode"}, - {"both", "Select both channels", 0, AV_OPT_TYPE_CONST, {.i64= 0}, INT_MIN, INT_MAX, AACDEC_FLAGS, "dual_mono_mode"}, + {"auto", "autoselection", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, AACDEC_FLAGS, .unit = "dual_mono_mode"}, + {"main", "Select Main/Left channel", 0, AV_OPT_TYPE_CONST, {.i64= 1}, INT_MIN, INT_MAX, AACDEC_FLAGS, .unit = "dual_mono_mode"}, + {"sub" , "Select Sub/Right channel", 0, AV_OPT_TYPE_CONST, {.i64= 2}, INT_MIN, INT_MAX, AACDEC_FLAGS, .unit = "dual_mono_mode"}, + {"both", "Select both channels", 0, AV_OPT_TYPE_CONST, {.i64= 0}, INT_MIN, INT_MAX, AACDEC_FLAGS, .unit = "dual_mono_mode"}, { "channel_order", "Order in which the channels are to be exported", - offsetof(AACContext, output_channel_order), AV_OPT_TYPE_INT, - { .i64 = CHANNEL_ORDER_DEFAULT }, 0, 1, AACDEC_FLAGS, "channel_order" }, + OFF(output_channel_order), AV_OPT_TYPE_INT, + { .i64 = CHANNEL_ORDER_DEFAULT }, 0, 1, AACDEC_FLAGS, .unit = "channel_order" }, { "default", "normal libavcodec channel order", 0, AV_OPT_TYPE_CONST, - { .i64 = CHANNEL_ORDER_DEFAULT }, .flags = AACDEC_FLAGS, "channel_order" }, + { .i64 = CHANNEL_ORDER_DEFAULT }, .flags = AACDEC_FLAGS, .unit = "channel_order" }, { "coded", "order in which the channels are coded in the bitstream", - 0, AV_OPT_TYPE_CONST, { .i64 = CHANNEL_ORDER_CODED }, .flags = AACDEC_FLAGS, "channel_order" }, + 0, AV_OPT_TYPE_CONST, { .i64 = CHANNEL_ORDER_CODED }, .flags = AACDEC_FLAGS, .unit = "channel_order" }, {NULL}, }; diff --git a/libavcodec/aacdectab.h b/libavcodec/aacdectab.h index 41f1db781d8..184508f2f34 100644 --- a/libavcodec/aacdectab.h +++ b/libavcodec/aacdectab.h @@ -1,7 +1,5 @@ /* * AAC decoder data - * Copyright (c) 2005-2006 Oded Shimon ( ods15 ods15 dyndns org ) - * Copyright (c) 2006-2007 Maxim Gavrilov ( maxim.gavrilov gmail com ) * * This file is part of FFmpeg. * @@ -30,99 +28,28 @@ #ifndef AVCODEC_AACDECTAB_H #define AVCODEC_AACDECTAB_H +#include + +#include "vlc.h" + +#include "libavutil/attributes_internal.h" #include "libavutil/channel_layout.h" -#include "aac.h" -#include +FF_VISIBILITY_PUSH_HIDDEN +void ff_aacdec_common_init_once(void); + +extern const VLCElem *ff_aac_sbr_vlc[10]; -static const int8_t tags_per_config[16] = { 0, 1, 1, 2, 3, 3, 4, 5, 0, 0, 0, 5, 5, 16, 5, 0 }; +extern VLCElem ff_vlc_scalefactors[]; +extern const VLCElem *ff_vlc_spectral[11]; -static const uint8_t aac_channel_layout_map[16][16][3] = { - { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, }, - { { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, }, - { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, }, - { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_SCE, 1, AAC_CHANNEL_BACK }, }, - { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, }, - { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, }, - { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, { TYPE_CPE, 2, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, }, - { { 0, } }, - { { 0, } }, - { { 0, } }, - { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, { TYPE_SCE, 1, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, }, - { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, { TYPE_CPE, 2, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, }, - { - { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, // SCE1 = FC, - { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, // CPE1 = FLc and FRc, - { TYPE_CPE, 1, AAC_CHANNEL_FRONT }, // CPE2 = FL and FR, - { TYPE_CPE, 2, AAC_CHANNEL_BACK }, // CPE3 = SiL and SiR, - { TYPE_CPE, 3, AAC_CHANNEL_BACK }, // CPE4 = BL and BR, - { TYPE_SCE, 1, AAC_CHANNEL_BACK }, // SCE2 = BC, - { TYPE_LFE, 0, AAC_CHANNEL_LFE }, // LFE1 = LFE1, - { TYPE_LFE, 1, AAC_CHANNEL_LFE }, // LFE2 = LFE2, - { TYPE_SCE, 2, AAC_CHANNEL_FRONT }, // SCE3 = TpFC, - { TYPE_CPE, 4, AAC_CHANNEL_FRONT }, // CPE5 = TpFL and TpFR, - { TYPE_CPE, 5, AAC_CHANNEL_SIDE }, // CPE6 = TpSiL and TpSiR, - { TYPE_SCE, 3, AAC_CHANNEL_SIDE }, // SCE4 = TpC, - { TYPE_CPE, 6, AAC_CHANNEL_BACK }, // CPE7 = TpBL and TpBR, - { TYPE_SCE, 4, AAC_CHANNEL_BACK }, // SCE5 = TpBC, - { TYPE_SCE, 5, AAC_CHANNEL_FRONT }, // SCE6 = BtFC, - { TYPE_CPE, 7, AAC_CHANNEL_FRONT }, // CPE8 = BtFL and BtFR - }, - { { TYPE_SCE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 0, AAC_CHANNEL_FRONT }, { TYPE_CPE, 1, AAC_CHANNEL_BACK }, { TYPE_LFE, 0, AAC_CHANNEL_LFE }, { TYPE_CPE, 2, AAC_CHANNEL_FRONT }, }, - { { 0, } }, -}; +extern const int8_t ff_tags_per_config[16]; -static const int16_t aac_channel_map[3][4][6] = { - { - { AV_CHAN_FRONT_CENTER, AV_CHAN_FRONT_LEFT_OF_CENTER, AV_CHAN_FRONT_RIGHT_OF_CENTER, AV_CHAN_FRONT_LEFT, AV_CHAN_FRONT_RIGHT, AV_CHAN_NONE }, - { AV_CHAN_UNUSED, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE }, - { AV_CHAN_UNUSED, AV_CHAN_SIDE_LEFT, AV_CHAN_SIDE_RIGHT, AV_CHAN_BACK_LEFT, AV_CHAN_BACK_RIGHT, AV_CHAN_BACK_CENTER }, - { AV_CHAN_LOW_FREQUENCY, AV_CHAN_LOW_FREQUENCY_2, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE }, - }, - { - { AV_CHAN_TOP_FRONT_CENTER, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_TOP_FRONT_LEFT, AV_CHAN_TOP_FRONT_RIGHT, AV_CHAN_NONE }, - { AV_CHAN_UNUSED, AV_CHAN_TOP_SIDE_LEFT, AV_CHAN_TOP_SIDE_RIGHT, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_TOP_CENTER}, - { AV_CHAN_UNUSED, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_TOP_BACK_LEFT, AV_CHAN_TOP_BACK_RIGHT, AV_CHAN_TOP_BACK_CENTER}, - { AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE}, - }, - { - { AV_CHAN_BOTTOM_FRONT_CENTER, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_BOTTOM_FRONT_LEFT, AV_CHAN_BOTTOM_FRONT_RIGHT, AV_CHAN_NONE }, - { AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE }, - { AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE }, - { AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE, AV_CHAN_NONE }, - }, -}; +extern const uint8_t ff_aac_channel_layout_map[16][16][3]; -#if FF_API_OLD_CHANNEL_LAYOUT -static const uint64_t aac_channel_layout[] = { - AV_CH_LAYOUT_MONO, - AV_CH_LAYOUT_STEREO, - AV_CH_LAYOUT_SURROUND, - AV_CH_LAYOUT_4POINT0, - AV_CH_LAYOUT_5POINT0_BACK, - AV_CH_LAYOUT_5POINT1_BACK, - AV_CH_LAYOUT_7POINT1_WIDE_BACK, - AV_CH_LAYOUT_6POINT1_BACK, - AV_CH_LAYOUT_7POINT1, - AV_CH_LAYOUT_22POINT2, - AV_CH_LAYOUT_7POINT1_TOP_BACK, - 0, -}; -#endif +extern const int16_t ff_aac_channel_map[3][4][6]; -static const AVChannelLayout aac_ch_layout[] = { - AV_CHANNEL_LAYOUT_MONO, - AV_CHANNEL_LAYOUT_STEREO, - AV_CHANNEL_LAYOUT_SURROUND, - AV_CHANNEL_LAYOUT_4POINT0, - AV_CHANNEL_LAYOUT_5POINT0_BACK, - AV_CHANNEL_LAYOUT_5POINT1_BACK, - AV_CHANNEL_LAYOUT_7POINT1_WIDE_BACK, - AV_CHANNEL_LAYOUT_6POINT1_BACK, - AV_CHANNEL_LAYOUT_7POINT1, - AV_CHANNEL_LAYOUT_22POINT2, - AV_CHANNEL_LAYOUT_7POINT1_TOP_BACK, - { 0 }, -}; +extern const AVChannelLayout ff_aac_ch_layout[]; +FF_VISIBILITY_POP_HIDDEN #endif /* AVCODEC_AACDECTAB_H */ diff --git a/libavcodec/aacenc.c b/libavcodec/aacenc.c index ed036209e92..55fa3078095 100644 --- a/libavcodec/aacenc.c +++ b/libavcodec/aacenc.c @@ -1106,6 +1106,18 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, too_many_bits = FFMIN(too_many_bits, 6144 * s->channels - 3); too_few_bits = FFMIN(FFMAX(rate_bits - rate_bits/4, target_bits), too_many_bits); + /* When strict bit-rate control is demanded */ + if (avctx->bit_rate_tolerance == 0) { + if (rate_bits < frame_bits) { + float ratio = ((float)rate_bits) / frame_bits; + s->lambda *= FFMIN(0.9f, ratio); + continue; + } + /* reset lambda when solution is found */ + s->lambda = avctx->global_quality > 0 ? avctx->global_quality : 120; + break; + } + /* When using ABR, be strict (but only for increasing) */ too_few_bits = too_few_bits - too_few_bits/8; too_many_bits = too_many_bits + too_many_bits/2; @@ -1198,9 +1210,6 @@ static av_cold int dsp_init(AVCodecContext *avctx, AACEncContext *s) if (!s->fdsp) return AVERROR(ENOMEM); - // window init - ff_aac_float_common_init(); - if ((ret = av_tx_init(&s->mdct1024, &s->mdct1024_fn, AV_TX_FLOAT_MDCT, 0, 1024, &scale, 0)) < 0) return ret; @@ -1295,13 +1304,13 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) avctx->bit_rate); /* Profile and option setting */ - avctx->profile = avctx->profile == FF_PROFILE_UNKNOWN ? FF_PROFILE_AAC_LOW : + avctx->profile = avctx->profile == AV_PROFILE_UNKNOWN ? AV_PROFILE_AAC_LOW : avctx->profile; for (i = 0; i < FF_ARRAY_ELEMS(aacenc_profiles); i++) if (avctx->profile == aacenc_profiles[i]) break; - if (avctx->profile == FF_PROFILE_MPEG2_AAC_LOW) { - avctx->profile = FF_PROFILE_AAC_LOW; + if (avctx->profile == AV_PROFILE_MPEG2_AAC_LOW) { + avctx->profile = AV_PROFILE_AAC_LOW; ERROR_IF(s->options.pred, "Main prediction unavailable in the \"mpeg2_aac_low\" profile\n"); ERROR_IF(s->options.ltp, @@ -1309,22 +1318,22 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) WARN_IF(s->options.pns, "PNS unavailable in the \"mpeg2_aac_low\" profile, turning off\n"); s->options.pns = 0; - } else if (avctx->profile == FF_PROFILE_AAC_LTP) { + } else if (avctx->profile == AV_PROFILE_AAC_LTP) { s->options.ltp = 1; ERROR_IF(s->options.pred, "Main prediction unavailable in the \"aac_ltp\" profile\n"); - } else if (avctx->profile == FF_PROFILE_AAC_MAIN) { + } else if (avctx->profile == AV_PROFILE_AAC_MAIN) { s->options.pred = 1; ERROR_IF(s->options.ltp, "LTP prediction unavailable in the \"aac_main\" profile\n"); } else if (s->options.ltp) { - avctx->profile = FF_PROFILE_AAC_LTP; + avctx->profile = AV_PROFILE_AAC_LTP; WARN_IF(1, "Chainging profile to \"aac_ltp\"\n"); ERROR_IF(s->options.pred, "Main prediction unavailable in the \"aac_ltp\" profile\n"); } else if (s->options.pred) { - avctx->profile = FF_PROFILE_AAC_MAIN; + avctx->profile = AV_PROFILE_AAC_MAIN; WARN_IF(1, "Chainging profile to \"aac_main\"\n"); ERROR_IF(s->options.ltp, @@ -1347,6 +1356,9 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) if (s->channels > 3) s->options.mid_side = 0; + // Initialize static tables + ff_aac_float_common_init(); + if ((ret = dsp_init(avctx, s)) < 0) return ret; @@ -1369,29 +1381,19 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) ff_lpc_init(&s->lpc, 2*avctx->frame_size, TNS_MAX_ORDER, FF_LPC_TYPE_LEVINSON); s->random_state = 0x1f2e3d4c; - s->abs_pow34 = abs_pow34_v; - s->quant_bands = quantize_bands; - -#if ARCH_X86 - ff_aac_dsp_init_x86(s); -#endif - -#if HAVE_MIPSDSP - ff_aac_coder_init_mips(s); -#endif + ff_aacenc_dsp_init(&s->aacdsp); ff_af_queue_init(avctx, &s->afq); - ff_aac_tableinit(); return 0; } #define AACENC_FLAGS AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM static const AVOption aacenc_options[] = { - {"aac_coder", "Coding algorithm", offsetof(AACEncContext, options.coder), AV_OPT_TYPE_INT, {.i64 = AAC_CODER_TWOLOOP}, 0, AAC_CODER_NB-1, AACENC_FLAGS, "coder"}, - {"anmr", "ANMR method", 0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_ANMR}, INT_MIN, INT_MAX, AACENC_FLAGS, "coder"}, - {"twoloop", "Two loop searching method", 0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_TWOLOOP}, INT_MIN, INT_MAX, AACENC_FLAGS, "coder"}, - {"fast", "Default fast search", 0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_FAST}, INT_MIN, INT_MAX, AACENC_FLAGS, "coder"}, + {"aac_coder", "Coding algorithm", offsetof(AACEncContext, options.coder), AV_OPT_TYPE_INT, {.i64 = AAC_CODER_TWOLOOP}, 0, AAC_CODER_NB-1, AACENC_FLAGS, .unit = "coder"}, + {"anmr", "ANMR method", 0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_ANMR}, INT_MIN, INT_MAX, AACENC_FLAGS, .unit = "coder"}, + {"twoloop", "Two loop searching method", 0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_TWOLOOP}, INT_MIN, INT_MAX, AACENC_FLAGS, .unit = "coder"}, + {"fast", "Default fast search", 0, AV_OPT_TYPE_CONST, {.i64 = AAC_CODER_FAST}, INT_MIN, INT_MAX, AACENC_FLAGS, .unit = "coder"}, {"aac_ms", "Force M/S stereo coding", offsetof(AACEncContext, options.mid_side), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, AACENC_FLAGS}, {"aac_is", "Intensity stereo coding", offsetof(AACEncContext, options.intensity_stereo), AV_OPT_TYPE_BOOL, {.i64 = 1}, -1, 1, AACENC_FLAGS}, {"aac_pns", "Perceptual noise substitution", offsetof(AACEncContext, options.pns), AV_OPT_TYPE_BOOL, {.i64 = 1}, -1, 1, AACENC_FLAGS}, diff --git a/libavcodec/aacenc.h b/libavcodec/aacenc.h index b030c652aec..8899f90ac7e 100644 --- a/libavcodec/aacenc.h +++ b/libavcodec/aacenc.h @@ -22,19 +22,25 @@ #ifndef AVCODEC_AACENC_H #define AVCODEC_AACENC_H +#include + #include "libavutil/channel_layout.h" #include "libavutil/float_dsp.h" #include "libavutil/mem_internal.h" +#include "libavutil/tx.h" #include "avcodec.h" #include "put_bits.h" #include "aac.h" +#include "aacencdsp.h" #include "audio_frame_queue.h" #include "psymodel.h" #include "lpc.h" +#define CLIP_AVOIDANCE_FACTOR 0.95f + typedef enum AACCoder { AAC_CODER_ANMR = 0, AAC_CODER_TWOLOOP, @@ -54,6 +60,90 @@ typedef struct AACEncOptions { int intensity_stereo; } AACEncOptions; +/** + * Long Term Prediction + */ +typedef struct LongTermPrediction { + int8_t present; + int16_t lag; + int coef_idx; + float coef; + int8_t used[MAX_LTP_LONG_SFB]; +} LongTermPrediction; + +/** + * Individual Channel Stream + */ +typedef struct IndividualChannelStream { + uint8_t max_sfb; ///< number of scalefactor bands per group + enum WindowSequence window_sequence[2]; + uint8_t use_kb_window[2]; ///< If set, use Kaiser-Bessel window, otherwise use a sine window. + uint8_t group_len[8]; + LongTermPrediction ltp; + const uint16_t *swb_offset; ///< table of offsets to the lowest spectral coefficient of a scalefactor band, sfb, for a particular window + const uint8_t *swb_sizes; ///< table of scalefactor band sizes for a particular window + int num_swb; ///< number of scalefactor window bands + int num_windows; + int tns_max_bands; + int predictor_present; + int predictor_initialized; + int predictor_reset_group; + int predictor_reset_count[31]; ///< used to count prediction resets + uint8_t prediction_used[41]; + uint8_t window_clipping[8]; ///< set if a certain window is near clipping + float clip_avoidance_factor; ///< set if any window is near clipping to the necessary atennuation factor to avoid it +} IndividualChannelStream; + +/** + * Temporal Noise Shaping + */ +typedef struct TemporalNoiseShaping { + int present; + int n_filt[8]; + int length[8][4]; + int direction[8][4]; + int order[8][4]; + int coef_idx[8][4][TNS_MAX_ORDER]; + float coef[8][4][TNS_MAX_ORDER]; +} TemporalNoiseShaping; + +/** + * Single Channel Element - used for both SCE and LFE elements. + */ +typedef struct SingleChannelElement { + IndividualChannelStream ics; + TemporalNoiseShaping tns; + Pulse pulse; + enum BandType band_type[128]; ///< band types + enum BandType band_alt[128]; ///< alternative band type + int sf_idx[128]; ///< scalefactor indices + uint8_t zeroes[128]; ///< band is not coded + uint8_t can_pns[128]; ///< band is allowed to PNS (informative) + float is_ener[128]; ///< Intensity stereo pos + float pns_ener[128]; ///< Noise energy values + DECLARE_ALIGNED(32, float, pcoeffs)[1024]; ///< coefficients for IMDCT, pristine + DECLARE_ALIGNED(32, float, coeffs)[1024]; ///< coefficients for IMDCT, maybe processed + DECLARE_ALIGNED(32, float, ret_buf)[2048]; ///< PCM output buffer + DECLARE_ALIGNED(16, float, ltp_state)[3072]; ///< time signal for LTP + DECLARE_ALIGNED(32, float, lcoeffs)[1024]; ///< MDCT of LTP coefficients + DECLARE_ALIGNED(32, float, prcoeffs)[1024]; ///< Main prediction coefs + PredictorState predictor_state[MAX_PREDICTORS]; +} SingleChannelElement; + +/** + * channel element - generic struct for SCE/CPE/CCE/LFE + */ +typedef struct ChannelElement { + // CPE specific + int common_window; ///< Set if channels share a common 'IndividualChannelStream' in bitstream. + int ms_mode; ///< Signals mid/side stereo flags coding mode + uint8_t is_mode; ///< Set if any bands have been encoded using intensity stereo + uint8_t ms_mask[128]; ///< Set if mid/side stereo is used for each scalefactor window band + uint8_t is_mask[128]; ///< Set if intensity stereo is used + // shared + SingleChannelElement ch[2]; +} ChannelElement; + struct AACEncContext; typedef struct AACCoefficientsEncoder { @@ -144,18 +234,13 @@ typedef struct AACEncContext { uint16_t quantize_band_cost_cache_generation; AACQuantizeBandCostCacheEntry quantize_band_cost_cache[256][128]; ///< memoization area for quantize_band_cost - void (*abs_pow34)(float *out, const float *in, const int size); - void (*quant_bands)(int *out, const float *in, const float *scaled, - int size, int is_signed, int maxval, const float Q34, - const float rounding); + AACEncDSPContext aacdsp; struct { float *samples; } buffer; } AACEncContext; -void ff_aac_dsp_init_x86(AACEncContext *s); -void ff_aac_coder_init_mips(AACEncContext *c); void ff_quantize_band_cost_cache_init(struct AACEncContext *s); diff --git a/libavcodec/aacenc_is.c b/libavcodec/aacenc_is.c index 1810790d881..4943b6450c5 100644 --- a/libavcodec/aacenc_is.c +++ b/libavcodec/aacenc_is.c @@ -59,9 +59,9 @@ struct AACISError ff_aac_is_encoding_err(AACEncContext *s, ChannelElement *cpe, float minthr = FFMIN(band0->threshold, band1->threshold); for (i = 0; i < sce0->ics.swb_sizes[g]; i++) IS[i] = (L[start+(w+w2)*128+i] + phase*R[start+(w+w2)*128+i])*sqrt(ener0/ener01); - s->abs_pow34(L34, &L[start+(w+w2)*128], sce0->ics.swb_sizes[g]); - s->abs_pow34(R34, &R[start+(w+w2)*128], sce0->ics.swb_sizes[g]); - s->abs_pow34(I34, IS, sce0->ics.swb_sizes[g]); + s->aacdsp.abs_pow34(L34, &L[start+(w+w2)*128], sce0->ics.swb_sizes[g]); + s->aacdsp.abs_pow34(R34, &R[start+(w+w2)*128], sce0->ics.swb_sizes[g]); + s->aacdsp.abs_pow34(I34, IS, sce0->ics.swb_sizes[g]); maxval = find_max_val(1, sce0->ics.swb_sizes[g], I34); is_band_type = find_min_book(maxval, is_sf_idx); dist1 += quantize_band_cost(s, &L[start + (w+w2)*128], L34, diff --git a/libavcodec/aacenc_ltp.c b/libavcodec/aacenc_ltp.c index f7fb85bbf8d..58f7921074b 100644 --- a/libavcodec/aacenc_ltp.c +++ b/libavcodec/aacenc_ltp.c @@ -37,7 +37,7 @@ void ff_aac_encode_ltp_info(AACEncContext *s, SingleChannelElement *sce, { int i; IndividualChannelStream *ics = &sce->ics; - if (s->profile != FF_PROFILE_AAC_LTP || !ics->predictor_present) + if (s->profile != AV_PROFILE_AAC_LTP || !ics->predictor_present) return; if (common_window) put_bits(&s->pb, 1, 0); @@ -92,8 +92,8 @@ static void get_lag(float *buf, const float *new, LongTermPrediction *ltp) } } ltp->lag = FFMAX(av_clip_uintp2(lag, 11), 0); - ltp->coef_idx = quant_array_idx(max_ratio, ltp_coef, 8); - ltp->coef = ltp_coef[ltp->coef_idx]; + ltp->coef_idx = quant_array_idx(max_ratio, ff_ltp_coef, 8); + ltp->coef = ff_ltp_coef[ltp->coef_idx]; } static void generate_samples(float *buf, LongTermPrediction *ltp) @@ -119,7 +119,7 @@ void ff_aac_update_ltp(AACEncContext *s, SingleChannelElement *sce) float *pred_signal = &sce->ltp_state[0]; const float *samples = &s->planar_samples[s->cur_channel][1024]; - if (s->profile != FF_PROFILE_AAC_LTP) + if (s->profile != AV_PROFILE_AAC_LTP) return; /* Calculate lag */ @@ -190,8 +190,8 @@ void ff_aac_search_for_ltp(AACEncContext *s, SingleChannelElement *sce, FFPsyBand *band = &s->psy.ch[s->cur_channel].psy_bands[(w+w2)*16+g]; for (i = 0; i < sce->ics.swb_sizes[g]; i++) PCD[i] = sce->coeffs[start+(w+w2)*128+i] - sce->lcoeffs[start+(w+w2)*128+i]; - s->abs_pow34(C34, &sce->coeffs[start+(w+w2)*128], sce->ics.swb_sizes[g]); - s->abs_pow34(PCD34, PCD, sce->ics.swb_sizes[g]); + s->aacdsp.abs_pow34(C34, &sce->coeffs[start+(w+w2)*128], sce->ics.swb_sizes[g]); + s->aacdsp.abs_pow34(PCD34, PCD, sce->ics.swb_sizes[g]); dist1 += quantize_band_cost(s, &sce->coeffs[start+(w+w2)*128], C34, sce->ics.swb_sizes[g], sce->sf_idx[(w+w2)*16+g], sce->band_type[(w+w2)*16+g], s->lambda/band->threshold, INFINITY, &bits_tmp1, NULL); diff --git a/libavcodec/aacenc_pred.c b/libavcodec/aacenc_pred.c index 447444cb823..a486c44d427 100644 --- a/libavcodec/aacenc_pred.c +++ b/libavcodec/aacenc_pred.c @@ -270,7 +270,7 @@ void ff_aac_search_for_pred(AACEncContext *s, SingleChannelElement *sce) continue; /* Normal coefficients */ - s->abs_pow34(O34, &sce->coeffs[start_coef], num_coeffs); + s->aacdsp.abs_pow34(O34, &sce->coeffs[start_coef], num_coeffs); dist1 = ff_quantize_and_encode_band_cost(s, NULL, &sce->coeffs[start_coef], NULL, O34, num_coeffs, sce->sf_idx[sfb], cb_n, s->lambda / band->threshold, INFINITY, &cost1, NULL); @@ -279,7 +279,7 @@ void ff_aac_search_for_pred(AACEncContext *s, SingleChannelElement *sce) /* Encoded coefficients - needed for #bits, band type and quant. error */ for (i = 0; i < num_coeffs; i++) SENT[i] = sce->coeffs[start_coef + i] - sce->prcoeffs[start_coef + i]; - s->abs_pow34(S34, SENT, num_coeffs); + s->aacdsp.abs_pow34(S34, SENT, num_coeffs); if (cb_n < RESERVED_BT) cb_p = av_clip(find_min_book(find_max_val(1, num_coeffs, S34), sce->sf_idx[sfb]), cb_min, cb_max); else @@ -291,7 +291,7 @@ void ff_aac_search_for_pred(AACEncContext *s, SingleChannelElement *sce) /* Reconstructed coefficients - needed for distortion measurements */ for (i = 0; i < num_coeffs; i++) sce->prcoeffs[start_coef + i] += QERR[i] != 0.0f ? (sce->prcoeffs[start_coef + i] - QERR[i]) : 0.0f; - s->abs_pow34(P34, &sce->prcoeffs[start_coef], num_coeffs); + s->aacdsp.abs_pow34(P34, &sce->prcoeffs[start_coef], num_coeffs); if (cb_n < RESERVED_BT) cb_p = av_clip(find_min_book(find_max_val(1, num_coeffs, P34), sce->sf_idx[sfb]), cb_min, cb_max); else @@ -335,7 +335,7 @@ void ff_aac_encode_main_pred(AACEncContext *s, SingleChannelElement *sce) IndividualChannelStream *ics = &sce->ics; const int pmax = FFMIN(ics->max_sfb, ff_aac_pred_sfb_max[s->samplerate_index]); - if (s->profile != FF_PROFILE_AAC_MAIN || + if (s->profile != AV_PROFILE_AAC_MAIN || !ics->predictor_present) return; diff --git a/libavcodec/aacenc_tns.c b/libavcodec/aacenc_tns.c index 195ff5e2b74..60888fece76 100644 --- a/libavcodec/aacenc_tns.c +++ b/libavcodec/aacenc_tns.c @@ -30,6 +30,7 @@ #include "aacenc_tns.h" #include "aactab.h" #include "aacenc_utils.h" +#include "lpc_functions.h" /* Could be set to 3 to save an additional bit at the cost of little quality */ #define TNS_Q_BITS 4 @@ -147,7 +148,7 @@ static inline void quantize_coefs(double *coef, int *idx, float *lpc, int order, int c_bits) { int i; - const float *quant_arr = tns_tmp2_map[c_bits]; + const float *quant_arr = ff_tns_tmp2_map[c_bits]; for (i = 0; i < order; i++) { idx[i] = quant_array_idx(coef[i], quant_arr, c_bits ? 16 : 8); lpc[i] = quant_arr[idx[i]]; @@ -167,7 +168,7 @@ void ff_aac_search_for_tns(AACEncContext *s, SingleChannelElement *sce) const int c_bits = is8 ? TNS_Q_BITS_IS8 == 4 : TNS_Q_BITS == 4; const int sfb_start = av_clip(tns_min_sfb[is8][s->samplerate_index], 0, mmm); const int sfb_end = av_clip(sce->ics.num_swb, 0, mmm); - const int order = is8 ? 7 : s->profile == FF_PROFILE_AAC_LOW ? 12 : TNS_MAX_ORDER; + const int order = is8 ? 7 : s->profile == AV_PROFILE_AAC_LOW ? 12 : TNS_MAX_ORDER; const int slant = sce->ics.window_sequence[0] == LONG_STOP_SEQUENCE ? 1 : sce->ics.window_sequence[0] == LONG_START_SEQUENCE ? 0 : 2; const int sfb_len = sfb_end - sfb_start; diff --git a/libavcodec/aacenc_utils.h b/libavcodec/aacenc_utils.h index bef4c103f38..cc747c3ea6e 100644 --- a/libavcodec/aacenc_utils.h +++ b/libavcodec/aacenc_utils.h @@ -29,7 +29,7 @@ #define AVCODEC_AACENC_UTILS_H #include "libavutil/ffmath.h" -#include "aac.h" +#include "aacenc.h" #include "aacenctab.h" #include "aactab.h" @@ -37,15 +37,6 @@ #define ROUND_TO_ZERO 0.1054f #define C_QUANT 0.4054f -static inline void abs_pow34_v(float *out, const float *in, const int size) -{ - int i; - for (i = 0; i < size; i++) { - float a = fabsf(in[i]); - out[i] = sqrtf(a * sqrtf(a)); - } -} - static inline float pos_pow34(float a) { return sqrtf(a * sqrtf(a)); @@ -62,21 +53,6 @@ static inline int quant(float coef, const float Q, const float rounding) return sqrtf(a * sqrtf(a)) + rounding; } -static inline void quantize_bands(int *out, const float *in, const float *scaled, - int size, int is_signed, int maxval, const float Q34, - const float rounding) -{ - int i; - for (i = 0; i < size; i++) { - float qc = scaled[i] * Q34; - int tmp = (int)FFMIN(qc + rounding, (float)maxval); - if (is_signed && in[i] < 0.0f) { - tmp = -tmp; - } - out[i] = tmp; - } -} - static inline float find_max_val(int group_len, int swb_size, const float *scaled) { float maxval = 0.0f; diff --git a/libavcodec/aacencdsp.h b/libavcodec/aacencdsp.h new file mode 100644 index 00000000000..67836d8cf74 --- /dev/null +++ b/libavcodec/aacencdsp.h @@ -0,0 +1,72 @@ +/* + * 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 + */ + +#ifndef AVCODEC_AACENCDSP_H +#define AVCODEC_AACENCDSP_H + +#include + +#include "config.h" + +#include "libavutil/macros.h" + +typedef struct AACEncDSPContext { + void (*abs_pow34)(float *out, const float *in, const int size); + void (*quant_bands)(int *out, const float *in, const float *scaled, + int size, int is_signed, int maxval, const float Q34, + const float rounding); +} AACEncDSPContext; + +void ff_aacenc_dsp_init_riscv(AACEncDSPContext *s); +void ff_aacenc_dsp_init_x86(AACEncDSPContext *s); + +static inline void abs_pow34_v(float *out, const float *in, const int size) +{ + for (int i = 0; i < size; i++) { + float a = fabsf(in[i]); + out[i] = sqrtf(a * sqrtf(a)); + } +} + +static inline void quantize_bands(int *out, const float *in, const float *scaled, + int size, int is_signed, int maxval, const float Q34, + const float rounding) +{ + for (int i = 0; i < size; i++) { + float qc = scaled[i] * Q34; + int tmp = (int)FFMIN(qc + rounding, (float)maxval); + if (is_signed && in[i] < 0.0f) { + tmp = -tmp; + } + out[i] = tmp; + } +} + +static inline void ff_aacenc_dsp_init(AACEncDSPContext *s) +{ + s->abs_pow34 = abs_pow34_v; + s->quant_bands = quantize_bands; + +#if ARCH_RISCV + ff_aacenc_dsp_init_riscv(s); +#elif ARCH_X86 + ff_aacenc_dsp_init_x86(s); +#endif +} + +#endif diff --git a/libavcodec/aacenctab.h b/libavcodec/aacenctab.h index 2d96621945e..f2d6f597bc6 100644 --- a/libavcodec/aacenctab.h +++ b/libavcodec/aacenctab.h @@ -30,6 +30,7 @@ #include "libavutil/channel_layout.h" #include "aac.h" +#include "defs.h" /** Total number of usable codebooks **/ #define CB_TOT 12 @@ -124,10 +125,10 @@ static const unsigned char aac_maxval_cb[] = { }; static const int aacenc_profiles[] = { - FF_PROFILE_AAC_MAIN, - FF_PROFILE_AAC_LOW, - FF_PROFILE_AAC_LTP, - FF_PROFILE_MPEG2_AAC_LOW, + AV_PROFILE_AAC_MAIN, + AV_PROFILE_AAC_LOW, + AV_PROFILE_AAC_LTP, + AV_PROFILE_MPEG2_AAC_LOW, }; #endif /* AVCODEC_AACENCTAB_H */ diff --git a/libavcodec/aacps.c b/libavcodec/aacps.c index 655e8fe5b46..6008edd332f 100644 --- a/libavcodec/aacps.c +++ b/libavcodec/aacps.c @@ -26,7 +26,6 @@ #include "libavutil/common.h" #include "libavutil/mathematics.h" #include "libavutil/mem_internal.h" -#include "avcodec.h" #include "aacps.h" #if USE_FIXED #include "aacps_fixed_tablegen.h" @@ -717,7 +716,7 @@ static void stereo_processing(PSContext *ps, INTFLOAT (*l)[32][2], INTFLOAT (*r) } } -int AAC_RENAME(ff_ps_apply)(AVCodecContext *avctx, PSContext *ps, INTFLOAT L[2][38][64], INTFLOAT R[2][38][64], int top) +int AAC_RENAME(ff_ps_apply)(PSContext *ps, INTFLOAT L[2][38][64], INTFLOAT R[2][38][64], int top) { INTFLOAT (*Lbuf)[32][2] = ps->Lbuf; INTFLOAT (*Rbuf)[32][2] = ps->Rbuf; @@ -740,10 +739,4 @@ int AAC_RENAME(ff_ps_apply)(AVCodecContext *avctx, PSContext *ps, INTFLOAT L[2][ av_cold void AAC_RENAME(ff_ps_init)(void) { ps_tableinit(); - ff_ps_init_common(); -} - -av_cold void AAC_RENAME(ff_ps_ctx_init)(PSContext *ps) -{ - AAC_RENAME(ff_psdsp_init)(&ps->dsp); } diff --git a/libavcodec/aacps.h b/libavcodec/aacps.h index 3efa38ad889..08c92dc4044 100644 --- a/libavcodec/aacps.h +++ b/libavcodec/aacps.h @@ -27,7 +27,6 @@ #include "libavutil/mem_internal.h" #include "aacpsdsp.h" -#include "avcodec.h" #include "get_bits.h" #define PS_MAX_NUM_ENV 5 @@ -94,9 +93,14 @@ extern const int8_t ff_k_to_i_34[]; void ff_ps_init_common(void); void AAC_RENAME(ff_ps_init)(void); -void AAC_RENAME(ff_ps_ctx_init)(PSContext *ps); -int ff_ps_read_data(AVCodecContext *avctx, GetBitContext *gb, + +static inline void AAC_RENAME(ff_ps_ctx_init)(PSContext *ps) +{ + AAC_RENAME(ff_psdsp_init)(&ps->dsp); +} + +int ff_ps_read_data(void *logctx, GetBitContext *gb, PSCommonContext *ps, int bits_left); -int AAC_RENAME(ff_ps_apply)(AVCodecContext *avctx, PSContext *ps, INTFLOAT L[2][38][64], INTFLOAT R[2][38][64], int top); +int AAC_RENAME(ff_ps_apply)(PSContext *ps, INTFLOAT L[2][38][64], INTFLOAT R[2][38][64], int top); #endif /* AVCODEC_AACPS_H */ diff --git a/libavcodec/aacps_common.c b/libavcodec/aacps_common.c index c388d5b9bc5..74fa005aaf0 100644 --- a/libavcodec/aacps_common.c +++ b/libavcodec/aacps_common.c @@ -21,7 +21,6 @@ #include #include "libavutil/common.h" -#include "libavutil/thread.h" #include "aacps.h" #include "get_bits.h" #include "aacpsdata.c" @@ -59,31 +58,31 @@ static const int huff_iid[] = { huff_iid_dt1, }; -static VLC vlc_ps[10]; +static const VLCElem *vlc_ps[10]; -#define READ_PAR_DATA(PAR, OFFSET, MASK, ERR_CONDITION, NB_BITS, MAX_DEPTH) \ +#define READ_PAR_DATA(PAR, MASK, ERR_CONDITION, NB_BITS, MAX_DEPTH) \ /** \ * Read Inter-channel Intensity Difference/Inter-Channel Coherence/ \ * Inter-channel Phase Difference/Overall Phase Difference parameters from the \ * bitstream. \ * \ - * @param avctx contains the current codec context \ + * @param logctx a context for logging \ * @param gb pointer to the input bitstream \ * @param ps pointer to the Parametric Stereo context \ * @param PAR pointer to the parameter to be read \ * @param e envelope to decode \ * @param dt 1: time delta-coded, 0: frequency delta-coded \ */ \ -static int read_ ## PAR ## _data(AVCodecContext *avctx, GetBitContext *gb, PSCommonContext *ps, \ +static int read_ ## PAR ## _data(void *logctx, GetBitContext *gb, PSCommonContext *ps, \ int8_t (*PAR)[PS_MAX_NR_IIDICC], int table_idx, int e, int dt) \ { \ int b, num = ps->nr_ ## PAR ## _par; \ - const VLCElem *vlc_table = vlc_ps[table_idx].table; \ + const VLCElem *vlc_table = vlc_ps[table_idx]; \ if (dt) { \ int e_prev = e ? e - 1 : ps->num_env_old - 1; \ e_prev = FFMAX(e_prev, 0); \ for (b = 0; b < num; b++) { \ - int val = PAR[e_prev][b] + get_vlc2(gb, vlc_table, NB_BITS, MAX_DEPTH) - OFFSET; \ + int val = PAR[e_prev][b] + get_vlc2(gb, vlc_table, NB_BITS, MAX_DEPTH); \ if (MASK) val &= MASK; \ PAR[e][b] = val; \ if (ERR_CONDITION) \ @@ -92,7 +91,7 @@ static int read_ ## PAR ## _data(AVCodecContext *avctx, GetBitContext *gb, PSCom } else { \ int val = 0; \ for (b = 0; b < num; b++) { \ - val += get_vlc2(gb, vlc_table, NB_BITS, MAX_DEPTH) - OFFSET; \ + val += get_vlc2(gb, vlc_table, NB_BITS, MAX_DEPTH); \ if (MASK) val &= MASK; \ PAR[e][b] = val; \ if (ERR_CONDITION) \ @@ -101,13 +100,13 @@ static int read_ ## PAR ## _data(AVCodecContext *avctx, GetBitContext *gb, PSCom } \ return 0; \ err: \ - av_log(avctx, AV_LOG_ERROR, "illegal "#PAR"\n"); \ + av_log(logctx, AV_LOG_ERROR, "illegal "#PAR"\n"); \ return AVERROR_INVALIDDATA; \ } -READ_PAR_DATA(iid, huff_offset[table_idx], 0, FFABS(ps->iid_par[e][b]) > 7 + 8 * ps->iid_quant, 9, 3) -READ_PAR_DATA(icc, huff_offset[table_idx], 0, ps->icc_par[e][b] > 7U, 9, 2) -READ_PAR_DATA(ipdopd, 0, 0x07, 0, 5, 1) +READ_PAR_DATA(iid, 0, FFABS(ps->iid_par[e][b]) > 7 + 8 * ps->iid_quant, 9, 3) +READ_PAR_DATA(icc, 0, ps->icc_par[e][b] > 7U, 9, 2) +READ_PAR_DATA(ipdopd, 0x07, 0, 5, 1) static int ps_read_extension_data(GetBitContext *gb, PSCommonContext *ps, int ps_extension_id) @@ -131,7 +130,7 @@ static int ps_read_extension_data(GetBitContext *gb, PSCommonContext *ps, return get_bits_count(gb) - count; } -int ff_ps_read_data(AVCodecContext *avctx, GetBitContext *gb_host, +int ff_ps_read_data(void *logctx, GetBitContext *gb_host, PSCommonContext *ps, int bits_left) { int e; @@ -146,7 +145,7 @@ int ff_ps_read_data(AVCodecContext *avctx, GetBitContext *gb_host, if (ps->enable_iid) { int iid_mode = get_bits(gb, 3); if (iid_mode > 5) { - av_log(avctx, AV_LOG_ERROR, "iid_mode %d is reserved.\n", + av_log(logctx, AV_LOG_ERROR, "iid_mode %d is reserved.\n", iid_mode); goto err; } @@ -158,7 +157,7 @@ int ff_ps_read_data(AVCodecContext *avctx, GetBitContext *gb_host, if (ps->enable_icc) { ps->icc_mode = get_bits(gb, 3); if (ps->icc_mode > 5) { - av_log(avctx, AV_LOG_ERROR, "icc_mode %d is reserved.\n", + av_log(logctx, AV_LOG_ERROR, "icc_mode %d is reserved.\n", ps->icc_mode); goto err; } @@ -176,7 +175,7 @@ int ff_ps_read_data(AVCodecContext *avctx, GetBitContext *gb_host, for (e = 1; e <= ps->num_env; e++) { ps->border_position[e] = get_bits(gb, 5); if (ps->border_position[e] < ps->border_position[e-1]) { - av_log(avctx, AV_LOG_ERROR, "border_position non monotone.\n"); + av_log(logctx, AV_LOG_ERROR, "border_position non monotone.\n"); goto err; } } @@ -187,7 +186,7 @@ int ff_ps_read_data(AVCodecContext *avctx, GetBitContext *gb_host, if (ps->enable_iid) { for (e = 0; e < ps->num_env; e++) { int dt = get_bits1(gb); - if (read_iid_data(avctx, gb, ps, ps->iid_par, huff_iid[2*dt+ps->iid_quant], e, dt)) + if (read_iid_data(logctx, gb, ps, ps->iid_par, huff_iid[2*dt+ps->iid_quant], e, dt)) goto err; } } else @@ -196,7 +195,7 @@ int ff_ps_read_data(AVCodecContext *avctx, GetBitContext *gb_host, if (ps->enable_icc) for (e = 0; e < ps->num_env; e++) { int dt = get_bits1(gb); - if (read_icc_data(avctx, gb, ps, ps->icc_par, dt ? huff_icc_dt : huff_icc_df, e, dt)) + if (read_icc_data(logctx, gb, ps, ps->icc_par, dt ? huff_icc_dt : huff_icc_df, e, dt)) goto err; } else @@ -213,7 +212,7 @@ int ff_ps_read_data(AVCodecContext *avctx, GetBitContext *gb_host, cnt -= 2 + ps_read_extension_data(gb, ps, ps_extension_id); } if (cnt < 0) { - av_log(avctx, AV_LOG_ERROR, "ps extension overflow %d\n", cnt); + av_log(logctx, AV_LOG_ERROR, "ps extension overflow %d\n", cnt); goto err; } skip_bits(gb, cnt); @@ -241,7 +240,7 @@ int ff_ps_read_data(AVCodecContext *avctx, GetBitContext *gb_host, if (ps->enable_iid){ for (b = 0; b < ps->nr_iid_par; b++) { if (FFABS(ps->iid_par[ps->num_env][b]) > 7 + 8 * ps->iid_quant) { - av_log(avctx, AV_LOG_ERROR, "iid_par invalid\n"); + av_log(logctx, AV_LOG_ERROR, "iid_par invalid\n"); goto err; } } @@ -249,7 +248,7 @@ int ff_ps_read_data(AVCodecContext *avctx, GetBitContext *gb_host, if (ps->enable_icc){ for (b = 0; b < ps->nr_iid_par; b++) { if (ps->icc_par[ps->num_env][b] > 7U) { - av_log(avctx, AV_LOG_ERROR, "icc_par invalid\n"); + av_log(logctx, AV_LOG_ERROR, "icc_par invalid\n"); goto err; } } @@ -278,7 +277,7 @@ int ff_ps_read_data(AVCodecContext *avctx, GetBitContext *gb_host, skip_bits_long(gb_host, bits_consumed); return bits_consumed; } - av_log(avctx, AV_LOG_ERROR, "Expected to read %d PS bits actually read %d.\n", bits_left, bits_consumed); + av_log(logctx, AV_LOG_ERROR, "Expected to read %d PS bits actually read %d.\n", bits_left, bits_consumed); err: ps->start = 0; skip_bits_long(gb_host, bits_left); @@ -289,48 +288,19 @@ int ff_ps_read_data(AVCodecContext *avctx, GetBitContext *gb_host, return bits_left; } -#define PS_INIT_VLC_STATIC(num, nb_bits, size) \ - INIT_VLC_STATIC(&vlc_ps[num], nb_bits, ps_tmp[num].table_size / ps_tmp[num].elem_size, \ - ps_tmp[num].ps_bits, 1, 1, \ - ps_tmp[num].ps_codes, ps_tmp[num].elem_size, ps_tmp[num].elem_size, \ - size); - -#define PS_VLC_ROW(name) \ - { name ## _codes, name ## _bits, sizeof(name ## _codes), sizeof(name ## _codes[0]) } - -static av_cold void ps_init_common(void) -{ - // Syntax initialization - static const struct { - const void *ps_codes, *ps_bits; - const unsigned int table_size, elem_size; - } ps_tmp[] = { - PS_VLC_ROW(huff_iid_df1), - PS_VLC_ROW(huff_iid_dt1), - PS_VLC_ROW(huff_iid_df0), - PS_VLC_ROW(huff_iid_dt0), - PS_VLC_ROW(huff_icc_df), - PS_VLC_ROW(huff_icc_dt), - PS_VLC_ROW(huff_ipd_df), - PS_VLC_ROW(huff_ipd_dt), - PS_VLC_ROW(huff_opd_df), - PS_VLC_ROW(huff_opd_dt), - }; - - PS_INIT_VLC_STATIC(0, 9, 1544); - PS_INIT_VLC_STATIC(1, 9, 832); - PS_INIT_VLC_STATIC(2, 9, 1024); - PS_INIT_VLC_STATIC(3, 9, 1036); - PS_INIT_VLC_STATIC(4, 9, 544); - PS_INIT_VLC_STATIC(5, 9, 544); - PS_INIT_VLC_STATIC(6, 5, 32); - PS_INIT_VLC_STATIC(7, 5, 32); - PS_INIT_VLC_STATIC(8, 5, 32); - PS_INIT_VLC_STATIC(9, 5, 32); -} - av_cold void ff_ps_init_common(void) { - static AVOnce init_static_once = AV_ONCE_INIT; - ff_thread_once(&init_static_once, ps_init_common); + static VLCElem vlc_buf[(1544 + 832 + 1024 + 1036) + + (544 + 544) + (32 + 32 + 32 + 32)]; + VLCInitState state = VLC_INIT_STATE(vlc_buf); + const uint8_t (*tab)[2] = aacps_huff_tabs; + + for (int i = 0; i < FF_ARRAY_ELEMS(vlc_ps); i++) { + vlc_ps[i] = + ff_vlc_init_tables_from_lengths(&state, i <= 5 ? 9 : 5, huff_sizes[i], + &tab[0][1], 2, + &tab[0][0], 2, 1, + huff_offset[i], 0); + tab += huff_sizes[i]; + } } diff --git a/libavcodec/aacps_tablegen_template.c b/libavcodec/aacps_tablegen_template.c index e70edf884b2..e05887b9b19 100644 --- a/libavcodec/aacps_tablegen_template.c +++ b/libavcodec/aacps_tablegen_template.c @@ -22,6 +22,8 @@ #include #define BUILD_TABLES +#include "config.h" +#undef CONFIG_HARDCODED_TABLES #define CONFIG_HARDCODED_TABLES 0 #include "aac_defines.h" diff --git a/libavcodec/aacpsdata.c b/libavcodec/aacpsdata.c index 7a1f490060b..8c79a154e22 100644 --- a/libavcodec/aacpsdata.c +++ b/libavcodec/aacpsdata.c @@ -19,124 +19,79 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -static const uint8_t huff_iid_df1_bits[] = { - 18, 18, 18, 18, 18, 18, 18, 18, 18, 17, 18, 17, 17, 16, 16, 15, 14, 14, - 13, 12, 12, 11, 10, 10, 8, 7, 6, 5, 4, 3, 1, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 11, 12, 13, 14, 14, 15, 16, 16, 17, 17, 18, 17, 18, 18, - 18, 18, 18, 18, 18, 18, 18, -}; - -static const uint32_t huff_iid_df1_codes[] = { - 0x01FEB4, 0x01FEB5, 0x01FD76, 0x01FD77, 0x01FD74, 0x01FD75, 0x01FE8A, - 0x01FE8B, 0x01FE88, 0x00FE80, 0x01FEB6, 0x00FE82, 0x00FEB8, 0x007F42, - 0x007FAE, 0x003FAF, 0x001FD1, 0x001FE9, 0x000FE9, 0x0007EA, 0x0007FB, - 0x0003FB, 0x0001FB, 0x0001FF, 0x00007C, 0x00003C, 0x00001C, 0x00000C, - 0x000000, 0x000001, 0x000001, 0x000002, 0x000001, 0x00000D, 0x00001D, - 0x00003D, 0x00007D, 0x0000FC, 0x0001FC, 0x0003FC, 0x0003F4, 0x0007EB, - 0x000FEA, 0x001FEA, 0x001FD6, 0x003FD0, 0x007FAF, 0x007F43, 0x00FEB9, - 0x00FE83, 0x01FEB7, 0x00FE81, 0x01FE89, 0x01FE8E, 0x01FE8F, 0x01FE8C, - 0x01FE8D, 0x01FEB2, 0x01FEB3, 0x01FEB0, 0x01FEB1, -}; - -static const uint8_t huff_iid_dt1_bits[] = { - 16, 16, 16, 16, 16, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 13, - 13, 13, 12, 12, 11, 10, 9, 9, 7, 6, 5, 3, 1, 2, 5, 6, 7, 8, - 9, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, -}; - -static const uint16_t huff_iid_dt1_codes[] = { - 0x004ED4, 0x004ED5, 0x004ECE, 0x004ECF, 0x004ECC, 0x004ED6, 0x004ED8, - 0x004F46, 0x004F60, 0x002718, 0x002719, 0x002764, 0x002765, 0x00276D, - 0x0027B1, 0x0013B7, 0x0013D6, 0x0009C7, 0x0009E9, 0x0009ED, 0x0004EE, - 0x0004F7, 0x000278, 0x000139, 0x00009A, 0x00009F, 0x000020, 0x000011, - 0x00000A, 0x000003, 0x000001, 0x000000, 0x00000B, 0x000012, 0x000021, - 0x00004C, 0x00009B, 0x00013A, 0x000279, 0x000270, 0x0004EF, 0x0004E2, - 0x0009EA, 0x0009D8, 0x0013D7, 0x0013D0, 0x0027B2, 0x0027A2, 0x00271A, - 0x00271B, 0x004F66, 0x004F67, 0x004F61, 0x004F47, 0x004ED9, 0x004ED7, - 0x004ECD, 0x004ED2, 0x004ED3, 0x004ED0, 0x004ED1, -}; - -static const uint8_t huff_iid_df0_bits[] = { - 17, 17, 17, 17, 16, 15, 13, 10, 9, 7, 6, 5, 4, 3, 1, 3, 4, 5, - 6, 6, 8, 11, 13, 14, 14, 15, 17, 18, 18, -}; - -static const uint32_t huff_iid_df0_codes[] = { - 0x01FFFB, 0x01FFFC, 0x01FFFD, 0x01FFFA, 0x00FFFC, 0x007FFC, 0x001FFD, - 0x0003FE, 0x0001FE, 0x00007E, 0x00003C, 0x00001D, 0x00000D, 0x000005, - 0x000000, 0x000004, 0x00000C, 0x00001C, 0x00003D, 0x00003E, 0x0000FE, - 0x0007FE, 0x001FFC, 0x003FFC, 0x003FFD, 0x007FFD, 0x01FFFE, 0x03FFFE, - 0x03FFFF, -}; - -static const uint8_t huff_iid_dt0_bits[] = { - 19, 19, 19, 20, 20, 20, 17, 15, 12, 10, 8, 6, 4, 2, 1, 3, 5, 7, - 9, 11, 13, 14, 17, 19, 20, 20, 20, 20, 20, -}; - -static const uint32_t huff_iid_dt0_codes[] = { - 0x07FFF9, 0x07FFFA, 0x07FFFB, 0x0FFFF8, 0x0FFFF9, 0x0FFFFA, 0x01FFFD, - 0x007FFE, 0x000FFE, 0x0003FE, 0x0000FE, 0x00003E, 0x00000E, 0x000002, - 0x000000, 0x000006, 0x00001E, 0x00007E, 0x0001FE, 0x0007FE, 0x001FFE, - 0x003FFE, 0x01FFFC, 0x07FFF8, 0x0FFFFB, 0x0FFFFC, 0x0FFFFD, 0x0FFFFE, - 0x0FFFFF, -}; - -static const uint8_t huff_icc_df_bits[] = { - 14, 14, 12, 10, 7, 5, 3, 1, 2, 4, 6, 8, 9, 11, 13, -}; - -static const uint16_t huff_icc_df_codes[] = { - 0x3FFF, 0x3FFE, 0x0FFE, 0x03FE, 0x007E, 0x001E, 0x0006, 0x0000, - 0x0002, 0x000E, 0x003E, 0x00FE, 0x01FE, 0x07FE, 0x1FFE, -}; - -static const uint8_t huff_icc_dt_bits[] = { - 14, 13, 11, 9, 7, 5, 3, 1, 2, 4, 6, 8, 10, 12, 14, -}; - -static const uint16_t huff_icc_dt_codes[] = { - 0x3FFE, 0x1FFE, 0x07FE, 0x01FE, 0x007E, 0x001E, 0x0006, 0x0000, - 0x0002, 0x000E, 0x003E, 0x00FE, 0x03FE, 0x0FFE, 0x3FFF, -}; - -static const uint8_t huff_ipd_df_bits[] = { - 1, 3, 4, 4, 4, 4, 4, 4, -}; - -static const uint8_t huff_ipd_df_codes[] = { - 0x01, 0x00, 0x06, 0x04, 0x02, 0x03, 0x05, 0x07, -}; - -static const uint8_t huff_ipd_dt_bits[] = { - 1, 3, 4, 5, 5, 4, 4, 3, -}; - -static const uint8_t huff_ipd_dt_codes[] = { - 0x01, 0x02, 0x02, 0x03, 0x02, 0x00, 0x03, 0x03, -}; - -static const uint8_t huff_opd_df_bits[] = { - 1, 3, 4, 4, 5, 5, 4, 3, -}; - -static const uint8_t huff_opd_df_codes[] = { - 0x01, 0x01, 0x06, 0x04, 0x0F, 0x0E, 0x05, 0x00, -}; - -static const uint8_t huff_opd_dt_bits[] = { - 1, 3, 4, 5, 5, 4, 4, 3, -}; - -static const uint8_t huff_opd_dt_codes[] = { - 0x01, 0x02, 0x01, 0x07, 0x06, 0x00, 0x02, 0x03, +#include + +static const uint8_t huff_sizes[] = { 61, 61, 29, 29, 15, 15, 8, 8, 8, 8 }; + +static const uint8_t aacps_huff_tabs[][2] = { + /* huff_iid_df1 - 61 entries */ + { 28, 4 }, { 32, 4 }, { 29, 3 }, { 31, 3 }, { 27, 5 }, + { 33, 5 }, { 26, 6 }, { 34, 6 }, { 25, 7 }, { 35, 7 }, + { 24, 8 }, { 36, 8 }, { 37, 9 }, { 40, 11 }, { 19, 12 }, + { 41, 12 }, { 22, 10 }, { 38, 10 }, { 9, 17 }, { 51, 17 }, + { 11, 17 }, { 49, 17 }, { 13, 16 }, { 47, 16 }, { 16, 14 }, + { 18, 13 }, { 42, 13 }, { 44, 14 }, { 12, 17 }, { 48, 17 }, + { 4, 18 }, { 5, 18 }, { 2, 18 }, { 3, 18 }, { 15, 15 }, + { 21, 11 }, { 39, 11 }, { 45, 15 }, { 8, 18 }, { 52, 18 }, + { 6, 18 }, { 7, 18 }, { 55, 18 }, { 56, 18 }, { 53, 18 }, + { 54, 18 }, { 17, 14 }, { 43, 14 }, { 59, 18 }, { 60, 18 }, + { 57, 18 }, { 58, 18 }, { 0, 18 }, { 1, 18 }, { 10, 18 }, + { 50, 18 }, { 14, 16 }, { 46, 16 }, { 20, 12 }, { 23, 10 }, + { 30, 1 }, + /* huff_iid_dt1 - 61 entries */ + { 31, 2 }, { 26, 7 }, { 34, 7 }, { 27, 6 }, { 33, 6 }, + { 35, 8 }, { 24, 9 }, { 36, 9 }, { 39, 11 }, { 41, 12 }, + { 9, 15 }, { 10, 15 }, { 48, 15 }, { 49, 15 }, { 17, 13 }, + { 23, 10 }, { 37, 10 }, { 43, 13 }, { 11, 15 }, { 12, 15 }, + { 4, 16 }, { 56, 16 }, { 2, 16 }, { 3, 16 }, { 59, 16 }, + { 60, 16 }, { 57, 16 }, { 58, 16 }, { 0, 16 }, { 1, 16 }, + { 5, 16 }, { 55, 16 }, { 6, 16 }, { 54, 16 }, { 13, 15 }, + { 15, 14 }, { 20, 12 }, { 40, 12 }, { 22, 11 }, { 38, 11 }, + { 45, 14 }, { 47, 15 }, { 7, 16 }, { 53, 16 }, { 18, 13 }, + { 42, 13 }, { 16, 14 }, { 44, 14 }, { 8, 16 }, { 52, 16 }, + { 14, 15 }, { 46, 15 }, { 50, 16 }, { 51, 16 }, { 19, 13 }, + { 21, 12 }, { 25, 9 }, { 28, 5 }, { 32, 5 }, { 29, 3 }, + { 30, 1 }, + /* huff_iid_df0 - 29 entries */ + { 14, 1 }, { 15, 3 }, { 13, 3 }, { 16, 4 }, { 12, 4 }, + { 17, 5 }, { 11, 5 }, { 10, 6 }, { 18, 6 }, { 19, 6 }, + { 9, 7 }, { 20, 8 }, { 8, 9 }, { 7, 10 }, { 21, 11 }, + { 22, 13 }, { 6, 13 }, { 23, 14 }, { 24, 14 }, { 5, 15 }, + { 25, 15 }, { 4, 16 }, { 3, 17 }, { 0, 17 }, { 1, 17 }, + { 2, 17 }, { 26, 17 }, { 27, 18 }, { 28, 18 }, + /* huff_iid_dt0 - 29 entries */ + { 14, 1 }, { 13, 2 }, { 15, 3 }, { 12, 4 }, { 16, 5 }, + { 11, 6 }, { 17, 7 }, { 10, 8 }, { 18, 9 }, { 9, 10 }, + { 19, 11 }, { 8, 12 }, { 20, 13 }, { 21, 14 }, { 7, 15 }, + { 22, 17 }, { 6, 17 }, { 23, 19 }, { 0, 19 }, { 1, 19 }, + { 2, 19 }, { 3, 20 }, { 4, 20 }, { 5, 20 }, { 24, 20 }, + { 25, 20 }, { 26, 20 }, { 27, 20 }, { 28, 20 }, + /* huff_icc_df - 15 entries */ + { 7, 1 }, { 8, 2 }, { 6, 3 }, { 9, 4 }, { 5, 5 }, + { 10, 6 }, { 4, 7 }, { 11, 8 }, { 12, 9 }, { 3, 10 }, + { 13, 11 }, { 2, 12 }, { 14, 13 }, { 1, 14 }, { 0, 14 }, + /* huff_icc_dt - 15 entries */ + { 7, 1 }, { 8, 2 }, { 6, 3 }, { 9, 4 }, { 5, 5 }, + { 10, 6 }, { 4, 7 }, { 11, 8 }, { 3, 9 }, { 12, 10 }, + { 2, 11 }, { 13, 12 }, { 1, 13 }, { 0, 14 }, { 14, 14 }, + /* huff_ipd_df - 8 entries */ + { 1, 3 }, { 4, 4 }, { 5, 4 }, { 3, 4 }, { 6, 4 }, + { 2, 4 }, { 7, 4 }, { 0, 1 }, + /* huff_ipd_dt - 8 entries */ + { 5, 4 }, { 4, 5 }, { 3, 5 }, { 2, 4 }, { 6, 4 }, + { 1, 3 }, { 7, 3 }, { 0, 1 }, + /* huff_opd_df - 8 entries */ + { 7, 3 }, { 1, 3 }, { 3, 4 }, { 6, 4 }, { 2, 4 }, + { 5, 5 }, { 4, 5 }, { 0, 1 }, + /* huff_opd_dt - 8 entries */ + { 5, 4 }, { 2, 4 }, { 6, 4 }, { 4, 5 }, { 3, 5 }, + { 1, 3 }, { 7, 3 }, { 0, 1 }, }; static const int8_t huff_offset[] = { - 30, 30, - 14, 14, - 7, 7, + -30, -30, + -14, -14, + -7, -7, 0, 0, 0, 0, }; diff --git a/libavcodec/aacpsdsp_template.c b/libavcodec/aacpsdsp_template.c index c063788b897..7100ae7bcbd 100644 --- a/libavcodec/aacpsdsp_template.c +++ b/libavcodec/aacpsdsp_template.c @@ -26,24 +26,25 @@ #include "libavutil/attributes.h" #include "aacpsdsp.h" -static void ps_add_squares_c(INTFLOAT *dst, const INTFLOAT (*src)[2], int n) +static void ps_add_squares_c(INTFLOAT *restrict dst, + const INTFLOAT (*src)[2], int n) { - int i; - for (i = 0; i < n; i++) + for (int i = 0; i < n; i++) dst[i] += (UINTFLOAT)AAC_MADD28(src[i][0], src[i][0], src[i][1], src[i][1]); } -static void ps_mul_pair_single_c(INTFLOAT (*dst)[2], INTFLOAT (*src0)[2], INTFLOAT *src1, +static void ps_mul_pair_single_c(INTFLOAT (*restrict dst)[2], + INTFLOAT (*src0)[2], INTFLOAT *src1, int n) { - int i; - for (i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { dst[i][0] = AAC_MUL16(src0[i][0], src1[i]); dst[i][1] = AAC_MUL16(src0[i][1], src1[i]); } } -static void ps_hybrid_analysis_c(INTFLOAT (*out)[2], INTFLOAT (*in)[2], +static void ps_hybrid_analysis_c(INTFLOAT (*restrict out)[2], + INTFLOAT (*in)[2], const INTFLOAT (*filter)[8][2], ptrdiff_t stride, int n) { @@ -76,13 +77,12 @@ static void ps_hybrid_analysis_c(INTFLOAT (*out)[2], INTFLOAT (*in)[2], } } -static void ps_hybrid_analysis_ileave_c(INTFLOAT (*out)[32][2], INTFLOAT L[2][38][64], - int i, int len) +static void ps_hybrid_analysis_ileave_c(INTFLOAT (*restrict out)[32][2], + INTFLOAT L[2][38][64], + int i, int len) { - int j; - for (; i < 64; i++) { - for (j = 0; j < len; j++) { + for (int j = 0; j < len; j++) { out[i][j][0] = L[0][j][i]; out[i][j][1] = L[1][j][i]; } @@ -90,13 +90,11 @@ static void ps_hybrid_analysis_ileave_c(INTFLOAT (*out)[32][2], INTFLOAT L[2][38 } static void ps_hybrid_synthesis_deint_c(INTFLOAT out[2][38][64], - INTFLOAT (*in)[32][2], - int i, int len) + INTFLOAT (*restrict in)[32][2], + int i, int len) { - int n; - for (; i < 64; i++) { - for (n = 0; n < len; n++) { + for (int n = 0; n < len; n++) { out[0][n][i] = in[i][n][0]; out[1][n][i] = in[i][n][1]; } diff --git a/libavcodec/aacpsy.c b/libavcodec/aacpsy.c index 933369e445a..1fbd259e518 100644 --- a/libavcodec/aacpsy.c +++ b/libavcodec/aacpsy.c @@ -28,7 +28,7 @@ #include "libavutil/ffmath.h" #include "avcodec.h" -#include "aactab.h" +#include "aac.h" #include "psymodel.h" /*********************************** diff --git a/libavcodec/aacsbr.c b/libavcodec/aacsbr.c index 47aa6cb3c1f..aafc00049a7 100644 --- a/libavcodec/aacsbr.c +++ b/libavcodec/aacsbr.c @@ -31,10 +31,10 @@ #include "sbr.h" #include "aacsbr.h" #include "aacsbrdata.h" -#include "internal.h" #include "aacps.h" #include "sbrdsp.h" #include "libavutil/internal.h" +#include "libavutil/intfloat.h" #include "libavutil/libm.h" #include "libavutil/avassert.h" #include "libavutil/mem_internal.h" @@ -47,7 +47,25 @@ #include "mips/aacsbr_mips.h" #endif /* ARCH_MIPS */ -static VLC vlc_sbr[10]; +/** + * 2^(x) for integer x + * @return correctly rounded float + */ +static av_always_inline float exp2fi(int x) { + /* Normal range */ + if (-126 <= x && x <= 128) + return av_int2float((x+127) << 23); + /* Too large */ + else if (x > 128) + return INFINITY; + /* Subnormal numbers */ + else if (x > -150) + return av_int2float(1 << (x+149)); + /* Negligibly small */ + else + return 0; +} + static void aacsbr_func_ptr_init(AACSBRContext *c); static void make_bands(int16_t* bands, int start, int stop, int num_bands) @@ -80,13 +98,13 @@ static void sbr_dequant(SpectralBandReplication *sbr, int id_aac) for (k = 0; k < sbr->n[sbr->data[0].bs_freq_res[e]]; k++) { float temp1, temp2, fac; if (sbr->data[0].bs_amp_res) { - temp1 = ff_exp2fi(sbr->data[0].env_facs_q[e][k] + 7); - temp2 = ff_exp2fi(pan_offset - sbr->data[1].env_facs_q[e][k]); + temp1 = exp2fi(sbr->data[0].env_facs_q[e][k] + 7); + temp2 = exp2fi(pan_offset - sbr->data[1].env_facs_q[e][k]); } else { - temp1 = ff_exp2fi((sbr->data[0].env_facs_q[e][k]>>1) + 7) * + temp1 = exp2fi((sbr->data[0].env_facs_q[e][k]>>1) + 7) * exp2_tab[sbr->data[0].env_facs_q[e][k] & 1]; - temp2 = ff_exp2fi((pan_offset - sbr->data[1].env_facs_q[e][k])>>1) * + temp2 = exp2fi((pan_offset - sbr->data[1].env_facs_q[e][k])>>1) * exp2_tab[(pan_offset - sbr->data[1].env_facs_q[e][k]) & 1]; } if (temp1 > 1E20) { @@ -100,8 +118,8 @@ static void sbr_dequant(SpectralBandReplication *sbr, int id_aac) } for (e = 1; e <= sbr->data[0].bs_num_noise; e++) { for (k = 0; k < sbr->n_q; k++) { - float temp1 = ff_exp2fi(NOISE_FLOOR_OFFSET - sbr->data[0].noise_facs_q[e][k] + 1); - float temp2 = ff_exp2fi(12 - sbr->data[1].noise_facs_q[e][k]); + float temp1 = exp2fi(NOISE_FLOOR_OFFSET - sbr->data[0].noise_facs_q[e][k] + 1); + float temp2 = exp2fi(12 - sbr->data[1].noise_facs_q[e][k]); float fac; av_assert0(temp1 <= 1E20); fac = temp1 / (1.0f + temp2); @@ -114,9 +132,9 @@ static void sbr_dequant(SpectralBandReplication *sbr, int id_aac) for (e = 1; e <= sbr->data[ch].bs_num_env; e++) for (k = 0; k < sbr->n[sbr->data[ch].bs_freq_res[e]]; k++){ if (sbr->data[ch].bs_amp_res) - sbr->data[ch].env_facs[e][k] = ff_exp2fi(sbr->data[ch].env_facs_q[e][k] + 6); + sbr->data[ch].env_facs[e][k] = exp2fi(sbr->data[ch].env_facs_q[e][k] + 6); else - sbr->data[ch].env_facs[e][k] = ff_exp2fi((sbr->data[ch].env_facs_q[e][k]>>1) + 6) + sbr->data[ch].env_facs[e][k] = exp2fi((sbr->data[ch].env_facs_q[e][k]>>1) + 6) * exp2_tab[sbr->data[ch].env_facs_q[e][k] & 1]; if (sbr->data[ch].env_facs[e][k] > 1E20) { av_log(NULL, AV_LOG_ERROR, "envelope scalefactor overflow in dequant\n"); @@ -127,7 +145,7 @@ static void sbr_dequant(SpectralBandReplication *sbr, int id_aac) for (e = 1; e <= sbr->data[ch].bs_num_noise; e++) for (k = 0; k < sbr->n_q; k++) sbr->data[ch].noise_facs[e][k] = - ff_exp2fi(NOISE_FLOOR_OFFSET - sbr->data[ch].noise_facs_q[e][k]); + exp2fi(NOISE_FLOOR_OFFSET - sbr->data[ch].noise_facs_q[e][k]); } } } @@ -215,7 +233,7 @@ static void sbr_chirp(SpectralBandReplication *sbr, SBRData *ch_data) * Calculation of levels of additional HF signal components (14496-3 sp04 p219) * and Calculation of gain (14496-3 sp04 p219) */ -static void sbr_gain_calc(AACContext *ac, SpectralBandReplication *sbr, +static void sbr_gain_calc(SpectralBandReplication *sbr, SBRData *ch_data, const int e_a[2]) { int e, k, m; diff --git a/libavcodec/aacsbr.h b/libavcodec/aacsbr.h index d70b19e11c6..cb680cc5486 100644 --- a/libavcodec/aacsbr.h +++ b/libavcodec/aacsbr.h @@ -30,12 +30,14 @@ #define AVCODEC_AACSBR_H #include "get_bits.h" -#include "aac.h" +#include "aac_defines.h" #include "sbr.h" #define ENVELOPE_ADJUSTMENT_OFFSET 2 #define NOISE_FLOOR_OFFSET 6 +struct AACDecContext; + /** * SBR VLC tables */ @@ -66,29 +68,17 @@ enum { EXTENSION_ID_PS = 2, }; -static const int8_t vlc_sbr_lav[10] = - { 60, 60, 24, 24, 31, 31, 12, 12, 31, 12 }; - -#define SBR_INIT_VLC_STATIC(num, size) \ - INIT_VLC_STATIC(&vlc_sbr[num], 9, sbr_tmp[num].table_size / sbr_tmp[num].elem_size, \ - sbr_tmp[num].sbr_bits , 1, 1, \ - sbr_tmp[num].sbr_codes, sbr_tmp[num].elem_size, sbr_tmp[num].elem_size, \ - size) - -#define SBR_VLC_ROW(name) \ - { name ## _codes, name ## _bits, sizeof(name ## _codes), sizeof(name ## _codes[0]) } - /** Initialize SBR. */ void AAC_RENAME(ff_aac_sbr_init)(void); /** Initialize one SBR context. */ -int AAC_RENAME(ff_aac_sbr_ctx_init)(AACContext *ac, SpectralBandReplication *sbr, int id_aac); +int AAC_RENAME(ff_aac_sbr_ctx_init)(struct AACDecContext *ac, SpectralBandReplication *sbr, int id_aac); /** Close one SBR context. */ void AAC_RENAME(ff_aac_sbr_ctx_close)(SpectralBandReplication *sbr); /** Decode one SBR element. */ -int AAC_RENAME(ff_decode_sbr_extension)(AACContext *ac, SpectralBandReplication *sbr, +int AAC_RENAME(ff_decode_sbr_extension)(struct AACDecContext *ac, SpectralBandReplication *sbr, GetBitContext *gb, int crc, int cnt, int id_aac); /** Apply one SBR element to one AAC element. */ -void AAC_RENAME(ff_sbr_apply)(AACContext *ac, SpectralBandReplication *sbr, int id_aac, +void AAC_RENAME(ff_sbr_apply)(struct AACDecContext *ac, SpectralBandReplication *sbr, int id_aac, INTFLOAT* L, INTFLOAT *R); void ff_aacsbr_func_ptr_init_mips(AACSBRContext *c); diff --git a/libavcodec/aacsbr_fixed.c b/libavcodec/aacsbr_fixed.c index cac472552e5..06d07e19413 100644 --- a/libavcodec/aacsbr_fixed.c +++ b/libavcodec/aacsbr_fixed.c @@ -70,7 +70,6 @@ #include #include -static VLC vlc_sbr[10]; static void aacsbr_func_ptr_init(AACSBRContext *c); static const int CONST_LN2 = Q31(0.6931471806/256); // ln(2)/256 static const int CONST_RECIP_LN2 = Q31(0.7213475204); // 0.5/ln(2) @@ -395,7 +394,7 @@ static void sbr_chirp(SpectralBandReplication *sbr, SBRData *ch_data) * Calculation of levels of additional HF signal components (14496-3 sp04 p219) * and Calculation of gain (14496-3 sp04 p219) */ -static void sbr_gain_calc(AACContext *ac, SpectralBandReplication *sbr, +static void sbr_gain_calc(SpectralBandReplication *sbr, SBRData *ch_data, const int e_a[2]) { int e, k, m; diff --git a/libavcodec/aacsbr_template.c b/libavcodec/aacsbr_template.c index cdca402f04c..cdfaed636b1 100644 --- a/libavcodec/aacsbr_template.c +++ b/libavcodec/aacsbr_template.c @@ -32,6 +32,9 @@ * @author Zoran Basaric ( zoran.basaric@imgtec.com ) */ +#include "aacdec.h" +#include "aacdectab.h" +#include "avcodec.h" #include "libavutil/qsort.h" static av_cold void aacsbr_tableinit(void) @@ -44,34 +47,6 @@ static av_cold void aacsbr_tableinit(void) av_cold void AAC_RENAME(ff_aac_sbr_init)(void) { - static const struct { - const void *sbr_codes, *sbr_bits; - const unsigned int table_size, elem_size; - } sbr_tmp[] = { - SBR_VLC_ROW(t_huffman_env_1_5dB), - SBR_VLC_ROW(f_huffman_env_1_5dB), - SBR_VLC_ROW(t_huffman_env_bal_1_5dB), - SBR_VLC_ROW(f_huffman_env_bal_1_5dB), - SBR_VLC_ROW(t_huffman_env_3_0dB), - SBR_VLC_ROW(f_huffman_env_3_0dB), - SBR_VLC_ROW(t_huffman_env_bal_3_0dB), - SBR_VLC_ROW(f_huffman_env_bal_3_0dB), - SBR_VLC_ROW(t_huffman_noise_3_0dB), - SBR_VLC_ROW(t_huffman_noise_bal_3_0dB), - }; - - // SBR VLC table initialization - SBR_INIT_VLC_STATIC(0, 1098); - SBR_INIT_VLC_STATIC(1, 1092); - SBR_INIT_VLC_STATIC(2, 768); - SBR_INIT_VLC_STATIC(3, 1026); - SBR_INIT_VLC_STATIC(4, 1058); - SBR_INIT_VLC_STATIC(5, 1052); - SBR_INIT_VLC_STATIC(6, 544); - SBR_INIT_VLC_STATIC(7, 544); - SBR_INIT_VLC_STATIC(8, 592); - SBR_INIT_VLC_STATIC(9, 512); - aacsbr_tableinit(); AAC_RENAME(ff_ps_init)(); @@ -89,7 +64,7 @@ static void sbr_turnoff(SpectralBandReplication *sbr) { memset(&sbr->spectrum_params, -1, sizeof(SpectrumParameters)); } -av_cold int AAC_RENAME(ff_aac_sbr_ctx_init)(AACContext *ac, SpectralBandReplication *sbr, int id_aac) +av_cold int AAC_RENAME(ff_aac_sbr_ctx_init)(AACDecContext *ac, SpectralBandReplication *sbr, int id_aac) { int ret; float scale; @@ -279,7 +254,7 @@ static int check_n_master(AVCodecContext *avctx, int n_master, int bs_xover_band } /// Master Frequency Band Table (14496-3 sp04 p194) -static int sbr_make_f_master(AACContext *ac, SpectralBandReplication *sbr, +static int sbr_make_f_master(AACDecContext *ac, SpectralBandReplication *sbr, SpectrumParameters *spectrum) { unsigned int temp, max_qmf_subbands = 0; @@ -499,7 +474,7 @@ static int sbr_make_f_master(AACContext *ac, SpectralBandReplication *sbr, } /// High Frequency Generation - Patch Construction (14496-3 sp04 p216 fig. 4.46) -static int sbr_hf_calc_npatches(AACContext *ac, SpectralBandReplication *sbr) +static int sbr_hf_calc_npatches(AACDecContext *ac, SpectralBandReplication *sbr) { int i, k, last_k = -1, last_msb = -1, sb = 0; int msb = sbr->k[0]; @@ -557,7 +532,7 @@ static int sbr_hf_calc_npatches(AACContext *ac, SpectralBandReplication *sbr) } /// Derived Frequency Band Tables (14496-3 sp04 p197) -static int sbr_make_f_derived(AACContext *ac, SpectralBandReplication *sbr) +static int sbr_make_f_derived(AACDecContext *ac, SpectralBandReplication *sbr) { int k, temp; #if USE_FIXED @@ -642,7 +617,7 @@ static const int8_t ceil_log2[] = { 0, 1, 2, 2, 3, 3, }; -static int read_sbr_grid(AACContext *ac, SpectralBandReplication *sbr, +static int read_sbr_grid(AACDecContext *ac, SpectralBandReplication *sbr, GetBitContext *gb, SBRData *ch_data) { int i; @@ -825,43 +800,34 @@ static void read_sbr_invf(SpectralBandReplication *sbr, GetBitContext *gb, ch_data->bs_invf_mode[0][i] = get_bits(gb, 2); } -static int read_sbr_envelope(AACContext *ac, SpectralBandReplication *sbr, GetBitContext *gb, +static int read_sbr_envelope(AACDecContext *ac, SpectralBandReplication *sbr, GetBitContext *gb, SBRData *ch_data, int ch) { int bits; int i, j, k; const VLCElem *t_huff, *f_huff; - int t_lav, f_lav; const int delta = (ch == 1 && sbr->bs_coupling == 1) + 1; const int odd = sbr->n[1] & 1; if (sbr->bs_coupling && ch) { if (ch_data->bs_amp_res) { bits = 5; - t_huff = vlc_sbr[T_HUFFMAN_ENV_BAL_3_0DB].table; - t_lav = vlc_sbr_lav[T_HUFFMAN_ENV_BAL_3_0DB]; - f_huff = vlc_sbr[F_HUFFMAN_ENV_BAL_3_0DB].table; - f_lav = vlc_sbr_lav[F_HUFFMAN_ENV_BAL_3_0DB]; + t_huff = ff_aac_sbr_vlc[T_HUFFMAN_ENV_BAL_3_0DB]; + f_huff = ff_aac_sbr_vlc[F_HUFFMAN_ENV_BAL_3_0DB]; } else { bits = 6; - t_huff = vlc_sbr[T_HUFFMAN_ENV_BAL_1_5DB].table; - t_lav = vlc_sbr_lav[T_HUFFMAN_ENV_BAL_1_5DB]; - f_huff = vlc_sbr[F_HUFFMAN_ENV_BAL_1_5DB].table; - f_lav = vlc_sbr_lav[F_HUFFMAN_ENV_BAL_1_5DB]; + t_huff = ff_aac_sbr_vlc[T_HUFFMAN_ENV_BAL_1_5DB]; + f_huff = ff_aac_sbr_vlc[F_HUFFMAN_ENV_BAL_1_5DB]; } } else { if (ch_data->bs_amp_res) { bits = 6; - t_huff = vlc_sbr[T_HUFFMAN_ENV_3_0DB].table; - t_lav = vlc_sbr_lav[T_HUFFMAN_ENV_3_0DB]; - f_huff = vlc_sbr[F_HUFFMAN_ENV_3_0DB].table; - f_lav = vlc_sbr_lav[F_HUFFMAN_ENV_3_0DB]; + t_huff = ff_aac_sbr_vlc[T_HUFFMAN_ENV_3_0DB]; + f_huff = ff_aac_sbr_vlc[F_HUFFMAN_ENV_3_0DB]; } else { bits = 7; - t_huff = vlc_sbr[T_HUFFMAN_ENV_1_5DB].table; - t_lav = vlc_sbr_lav[T_HUFFMAN_ENV_1_5DB]; - f_huff = vlc_sbr[F_HUFFMAN_ENV_1_5DB].table; - f_lav = vlc_sbr_lav[F_HUFFMAN_ENV_1_5DB]; + t_huff = ff_aac_sbr_vlc[T_HUFFMAN_ENV_1_5DB]; + f_huff = ff_aac_sbr_vlc[F_HUFFMAN_ENV_1_5DB]; } } @@ -870,7 +836,7 @@ static int read_sbr_envelope(AACContext *ac, SpectralBandReplication *sbr, GetBi // bs_freq_res[0] == bs_freq_res[bs_num_env] from prev frame if (ch_data->bs_freq_res[i + 1] == ch_data->bs_freq_res[i]) { for (j = 0; j < sbr->n[ch_data->bs_freq_res[i + 1]]; j++) { - ch_data->env_facs_q[i + 1][j] = ch_data->env_facs_q[i][j] + delta * (get_vlc2(gb, t_huff, 9, 3) - t_lav); + ch_data->env_facs_q[i + 1][j] = ch_data->env_facs_q[i][j] + delta * get_vlc2(gb, t_huff, 9, 3); if (ch_data->env_facs_q[i + 1][j] > 127U) { av_log(ac->avctx, AV_LOG_ERROR, "env_facs_q %d is invalid\n", ch_data->env_facs_q[i + 1][j]); return AVERROR_INVALIDDATA; @@ -879,7 +845,7 @@ static int read_sbr_envelope(AACContext *ac, SpectralBandReplication *sbr, GetBi } else if (ch_data->bs_freq_res[i + 1]) { for (j = 0; j < sbr->n[ch_data->bs_freq_res[i + 1]]; j++) { k = (j + odd) >> 1; // find k such that f_tablelow[k] <= f_tablehigh[j] < f_tablelow[k + 1] - ch_data->env_facs_q[i + 1][j] = ch_data->env_facs_q[i][k] + delta * (get_vlc2(gb, t_huff, 9, 3) - t_lav); + ch_data->env_facs_q[i + 1][j] = ch_data->env_facs_q[i][k] + delta * get_vlc2(gb, t_huff, 9, 3); if (ch_data->env_facs_q[i + 1][j] > 127U) { av_log(ac->avctx, AV_LOG_ERROR, "env_facs_q %d is invalid\n", ch_data->env_facs_q[i + 1][j]); return AVERROR_INVALIDDATA; @@ -888,7 +854,7 @@ static int read_sbr_envelope(AACContext *ac, SpectralBandReplication *sbr, GetBi } else { for (j = 0; j < sbr->n[ch_data->bs_freq_res[i + 1]]; j++) { k = j ? 2*j - odd : 0; // find k such that f_tablehigh[k] == f_tablelow[j] - ch_data->env_facs_q[i + 1][j] = ch_data->env_facs_q[i][k] + delta * (get_vlc2(gb, t_huff, 9, 3) - t_lav); + ch_data->env_facs_q[i + 1][j] = ch_data->env_facs_q[i][k] + delta * get_vlc2(gb, t_huff, 9, 3); if (ch_data->env_facs_q[i + 1][j] > 127U) { av_log(ac->avctx, AV_LOG_ERROR, "env_facs_q %d is invalid\n", ch_data->env_facs_q[i + 1][j]); return AVERROR_INVALIDDATA; @@ -898,7 +864,7 @@ static int read_sbr_envelope(AACContext *ac, SpectralBandReplication *sbr, GetBi } else { ch_data->env_facs_q[i + 1][0] = delta * get_bits(gb, bits); // bs_env_start_value_balance for (j = 1; j < sbr->n[ch_data->bs_freq_res[i + 1]]; j++) { - ch_data->env_facs_q[i + 1][j] = ch_data->env_facs_q[i + 1][j - 1] + delta * (get_vlc2(gb, f_huff, 9, 3) - f_lav); + ch_data->env_facs_q[i + 1][j] = ch_data->env_facs_q[i + 1][j - 1] + delta * get_vlc2(gb, f_huff, 9, 3); if (ch_data->env_facs_q[i + 1][j] > 127U) { av_log(ac->avctx, AV_LOG_ERROR, "env_facs_q %d is invalid\n", ch_data->env_facs_q[i + 1][j]); return AVERROR_INVALIDDATA; @@ -914,30 +880,25 @@ static int read_sbr_envelope(AACContext *ac, SpectralBandReplication *sbr, GetBi return 0; } -static int read_sbr_noise(AACContext *ac, SpectralBandReplication *sbr, GetBitContext *gb, +static int read_sbr_noise(AACDecContext *ac, SpectralBandReplication *sbr, GetBitContext *gb, SBRData *ch_data, int ch) { int i, j; const VLCElem *t_huff, *f_huff; - int t_lav, f_lav; int delta = (ch == 1 && sbr->bs_coupling == 1) + 1; if (sbr->bs_coupling && ch) { - t_huff = vlc_sbr[T_HUFFMAN_NOISE_BAL_3_0DB].table; - t_lav = vlc_sbr_lav[T_HUFFMAN_NOISE_BAL_3_0DB]; - f_huff = vlc_sbr[F_HUFFMAN_ENV_BAL_3_0DB].table; - f_lav = vlc_sbr_lav[F_HUFFMAN_ENV_BAL_3_0DB]; + t_huff = ff_aac_sbr_vlc[T_HUFFMAN_NOISE_BAL_3_0DB]; + f_huff = ff_aac_sbr_vlc[F_HUFFMAN_ENV_BAL_3_0DB]; } else { - t_huff = vlc_sbr[T_HUFFMAN_NOISE_3_0DB].table; - t_lav = vlc_sbr_lav[T_HUFFMAN_NOISE_3_0DB]; - f_huff = vlc_sbr[F_HUFFMAN_ENV_3_0DB].table; - f_lav = vlc_sbr_lav[F_HUFFMAN_ENV_3_0DB]; + t_huff = ff_aac_sbr_vlc[T_HUFFMAN_NOISE_3_0DB]; + f_huff = ff_aac_sbr_vlc[F_HUFFMAN_ENV_3_0DB]; } for (i = 0; i < ch_data->bs_num_noise; i++) { if (ch_data->bs_df_noise[i]) { for (j = 0; j < sbr->n_q; j++) { - ch_data->noise_facs_q[i + 1][j] = ch_data->noise_facs_q[i][j] + delta * (get_vlc2(gb, t_huff, 9, 2) - t_lav); + ch_data->noise_facs_q[i + 1][j] = ch_data->noise_facs_q[i][j] + delta * get_vlc2(gb, t_huff, 9, 2); if (ch_data->noise_facs_q[i + 1][j] > 30U) { av_log(ac->avctx, AV_LOG_ERROR, "noise_facs_q %d is invalid\n", ch_data->noise_facs_q[i + 1][j]); return AVERROR_INVALIDDATA; @@ -946,7 +907,7 @@ static int read_sbr_noise(AACContext *ac, SpectralBandReplication *sbr, GetBitCo } else { ch_data->noise_facs_q[i + 1][0] = delta * get_bits(gb, 5); // bs_noise_start_value_balance or bs_noise_start_value_level for (j = 1; j < sbr->n_q; j++) { - ch_data->noise_facs_q[i + 1][j] = ch_data->noise_facs_q[i + 1][j - 1] + delta * (get_vlc2(gb, f_huff, 9, 3) - f_lav); + ch_data->noise_facs_q[i + 1][j] = ch_data->noise_facs_q[i + 1][j - 1] + delta * get_vlc2(gb, f_huff, 9, 3); if (ch_data->noise_facs_q[i + 1][j] > 30U) { av_log(ac->avctx, AV_LOG_ERROR, "noise_facs_q %d is invalid\n", ch_data->noise_facs_q[i + 1][j]); return AVERROR_INVALIDDATA; @@ -961,7 +922,7 @@ static int read_sbr_noise(AACContext *ac, SpectralBandReplication *sbr, GetBitCo return 0; } -static void read_sbr_extension(AACContext *ac, SpectralBandReplication *sbr, +static void read_sbr_extension(AACDecContext *ac, SpectralBandReplication *sbr, GetBitContext *gb, int bs_extension_id, int *num_bits_left) { @@ -973,7 +934,7 @@ static void read_sbr_extension(AACContext *ac, SpectralBandReplication *sbr, *num_bits_left = 0; } else { *num_bits_left -= ff_ps_read_data(ac->avctx, gb, &sbr->ps.common, *num_bits_left); - ac->avctx->profile = FF_PROFILE_AAC_HE_V2; + ac->avctx->profile = AV_PROFILE_AAC_HE_V2; // ensure the warning is not printed if PS extension is present ac->warned_he_aac_mono = 1; } @@ -988,7 +949,7 @@ static void read_sbr_extension(AACContext *ac, SpectralBandReplication *sbr, } } -static int read_sbr_single_channel_element(AACContext *ac, +static int read_sbr_single_channel_element(AACDecContext *ac, SpectralBandReplication *sbr, GetBitContext *gb) { @@ -1012,7 +973,7 @@ static int read_sbr_single_channel_element(AACContext *ac, return 0; } -static int read_sbr_channel_pair_element(AACContext *ac, +static int read_sbr_channel_pair_element(AACDecContext *ac, SpectralBandReplication *sbr, GetBitContext *gb) { @@ -1064,7 +1025,7 @@ static int read_sbr_channel_pair_element(AACContext *ac, return 0; } -static unsigned int read_sbr_data(AACContext *ac, SpectralBandReplication *sbr, +static unsigned int read_sbr_data(AACDecContext *ac, SpectralBandReplication *sbr, GetBitContext *gb, int id_aac) { unsigned int cnt = get_bits_count(gb); @@ -1108,7 +1069,7 @@ static unsigned int read_sbr_data(AACContext *ac, SpectralBandReplication *sbr, return get_bits_count(gb) - cnt; } -static void sbr_reset(AACContext *ac, SpectralBandReplication *sbr) +static void sbr_reset(AACDecContext *ac, SpectralBandReplication *sbr) { int err; err = sbr_make_f_master(ac, sbr, &sbr->spectrum_params); @@ -1129,7 +1090,7 @@ static void sbr_reset(AACContext *ac, SpectralBandReplication *sbr) * * @return Returns number of bytes consumed from the TYPE_FIL element. */ -int AAC_RENAME(ff_decode_sbr_extension)(AACContext *ac, SpectralBandReplication *sbr, +int AAC_RENAME(ff_decode_sbr_extension)(AACDecContext *ac, SpectralBandReplication *sbr, GetBitContext *gb_host, int crc, int cnt, int id_aac) { unsigned int num_sbr_bits = 0, num_align_bits; @@ -1282,7 +1243,7 @@ static void sbr_qmf_synthesis(AVTXContext *mdct, av_tx_fn mdct_fn, #endif /// Generate the subband filtered lowband -static int sbr_lf_gen(AACContext *ac, SpectralBandReplication *sbr, +static int sbr_lf_gen(SpectralBandReplication *sbr, INTFLOAT X_low[32][40][2], const INTFLOAT W[2][32][32][2], int buf_idx) { @@ -1307,7 +1268,7 @@ static int sbr_lf_gen(AACContext *ac, SpectralBandReplication *sbr, } /// High Frequency Generator (14496-3 sp04 p215) -static int sbr_hf_gen(AACContext *ac, SpectralBandReplication *sbr, +static int sbr_hf_gen(AACDecContext *ac, SpectralBandReplication *sbr, INTFLOAT X_high[64][40][2], const INTFLOAT X_low[32][40][2], const INTFLOAT (*alpha0)[2], const INTFLOAT (*alpha1)[2], const INTFLOAT bw_array[5], const uint8_t *t_env, @@ -1381,7 +1342,7 @@ static int sbr_x_gen(SpectralBandReplication *sbr, INTFLOAT X[2][38][64], /** High Frequency Adjustment (14496-3 sp04 p217) and Mapping * (14496-3 sp04 p217) */ -static int sbr_mapping(AACContext *ac, SpectralBandReplication *sbr, +static int sbr_mapping(AACDecContext *ac, SpectralBandReplication *sbr, SBRData *ch_data, int e_a[2]) { int e, i, m; @@ -1495,7 +1456,7 @@ static void sbr_env_estimate(AAC_FLOAT (*e_curr)[48], INTFLOAT X_high[64][40][2] } } -void AAC_RENAME(ff_sbr_apply)(AACContext *ac, SpectralBandReplication *sbr, int id_aac, +void AAC_RENAME(ff_sbr_apply)(AACDecContext *ac, SpectralBandReplication *sbr, int id_aac, INTFLOAT* L, INTFLOAT* R) { int downsampled = ac->oc[1].m4ac.ext_sample_rate < sbr->sample_rate; @@ -1532,7 +1493,7 @@ void AAC_RENAME(ff_sbr_apply)(AACContext *ac, SpectralBandReplication *sbr, int ch ? R : L, sbr->data[ch].analysis_filterbank_samples, (INTFLOAT*)sbr->qmf_filter_scratch, sbr->data[ch].W, sbr->data[ch].Ypos); - sbr->c.sbr_lf_gen(ac, sbr, sbr->X_low, + sbr->c.sbr_lf_gen(sbr, sbr->X_low, (const INTFLOAT (*)[32][32][2]) sbr->data[ch].W, sbr->data[ch].Ypos); sbr->data[ch].Ypos ^= 1; @@ -1552,7 +1513,7 @@ void AAC_RENAME(ff_sbr_apply)(AACContext *ac, SpectralBandReplication *sbr, int err = sbr_mapping(ac, sbr, &sbr->data[ch], sbr->data[ch].e_a); if (!err) { sbr_env_estimate(sbr->e_curr, sbr->X_high, sbr, &sbr->data[ch]); - sbr_gain_calc(ac, sbr, &sbr->data[ch], sbr->data[ch].e_a); + sbr_gain_calc(sbr, &sbr->data[ch], sbr->data[ch].e_a); sbr->c.sbr_hf_assemble(sbr->data[ch].Y[sbr->data[ch].Ypos], (const INTFLOAT (*)[40][2]) sbr->X_high, sbr, &sbr->data[ch], @@ -1569,7 +1530,7 @@ void AAC_RENAME(ff_sbr_apply)(AACContext *ac, SpectralBandReplication *sbr, int if (ac->oc[1].m4ac.ps == 1) { if (sbr->ps.common.start) { - AAC_RENAME(ff_ps_apply)(ac->avctx, &sbr->ps, sbr->X[0], sbr->X[1], sbr->kx[1] + sbr->m[1]); + AAC_RENAME(ff_ps_apply)(&sbr->ps, sbr->X[0], sbr->X[1], sbr->kx[1] + sbr->m[1]); } else { memcpy(sbr->X[1], sbr->X[0], sizeof(sbr->X[0])); } diff --git a/libavcodec/aacsbrdata.h b/libavcodec/aacsbrdata.h index 7bb45b229e2..b0585309e0a 100644 --- a/libavcodec/aacsbrdata.h +++ b/libavcodec/aacsbrdata.h @@ -29,236 +29,9 @@ #define AVCODEC_AACSBRDATA_H #include -#include "libavutil/attributes_internal.h" #include "libavutil/mem_internal.h" #include "aac_defines.h" -///< Huffman tables for SBR - -static const uint8_t t_huffman_env_1_5dB_bits[121] = { - 18, 18, 18, 18, 18, 18, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 17, 18, 16, 17, 18, 17, - 16, 16, 16, 16, 15, 14, 14, 13, - 13, 12, 11, 10, 9, 8, 7, 6, - 5, 4, 3, 2, 2, 3, 4, 5, - 6, 7, 8, 9, 10, 12, 13, 14, - 14, 15, 16, 17, 16, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, - 19, -}; - -static const uint32_t t_huffman_env_1_5dB_codes[121] = { - 0x3ffd6, 0x3ffd7, 0x3ffd8, 0x3ffd9, 0x3ffda, 0x3ffdb, 0x7ffb8, 0x7ffb9, - 0x7ffba, 0x7ffbb, 0x7ffbc, 0x7ffbd, 0x7ffbe, 0x7ffbf, 0x7ffc0, 0x7ffc1, - 0x7ffc2, 0x7ffc3, 0x7ffc4, 0x7ffc5, 0x7ffc6, 0x7ffc7, 0x7ffc8, 0x7ffc9, - 0x7ffca, 0x7ffcb, 0x7ffcc, 0x7ffcd, 0x7ffce, 0x7ffcf, 0x7ffd0, 0x7ffd1, - 0x7ffd2, 0x7ffd3, 0x1ffe6, 0x3ffd4, 0x0fff0, 0x1ffe9, 0x3ffd5, 0x1ffe7, - 0x0fff1, 0x0ffec, 0x0ffed, 0x0ffee, 0x07ff4, 0x03ff9, 0x03ff7, 0x01ffa, - 0x01ff9, 0x00ffb, 0x007fc, 0x003fc, 0x001fd, 0x000fd, 0x0007d, 0x0003d, - 0x0001d, 0x0000d, 0x00005, 0x00001, 0x00000, 0x00004, 0x0000c, 0x0001c, - 0x0003c, 0x0007c, 0x000fc, 0x001fc, 0x003fd, 0x00ffa, 0x01ff8, 0x03ff6, - 0x03ff8, 0x07ff5, 0x0ffef, 0x1ffe8, 0x0fff2, 0x7ffd4, 0x7ffd5, 0x7ffd6, - 0x7ffd7, 0x7ffd8, 0x7ffd9, 0x7ffda, 0x7ffdb, 0x7ffdc, 0x7ffdd, 0x7ffde, - 0x7ffdf, 0x7ffe0, 0x7ffe1, 0x7ffe2, 0x7ffe3, 0x7ffe4, 0x7ffe5, 0x7ffe6, - 0x7ffe7, 0x7ffe8, 0x7ffe9, 0x7ffea, 0x7ffeb, 0x7ffec, 0x7ffed, 0x7ffee, - 0x7ffef, 0x7fff0, 0x7fff1, 0x7fff2, 0x7fff3, 0x7fff4, 0x7fff5, 0x7fff6, - 0x7fff7, 0x7fff8, 0x7fff9, 0x7fffa, 0x7fffb, 0x7fffc, 0x7fffd, 0x7fffe, - 0x7ffff, -}; - -static const uint8_t f_huffman_env_1_5dB_bits[121] = { - 19, 19, 20, 20, 20, 20, 20, 20, - 20, 19, 20, 20, 20, 20, 19, 20, - 19, 19, 20, 18, 20, 20, 20, 19, - 20, 20, 20, 19, 20, 19, 18, 19, - 18, 18, 17, 18, 17, 17, 17, 16, - 16, 16, 15, 15, 14, 13, 13, 12, - 12, 11, 10, 9, 9, 8, 7, 6, - 5, 4, 3, 2, 2, 3, 4, 5, - 6, 8, 8, 9, 10, 11, 11, 11, - 12, 12, 13, 13, 14, 14, 16, 16, - 17, 17, 18, 18, 18, 18, 18, 18, - 18, 20, 19, 20, 20, 20, 20, 20, - 20, 19, 20, 20, 20, 20, 19, 20, - 18, 20, 20, 19, 19, 20, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, - 20, -}; - -static const uint32_t f_huffman_env_1_5dB_codes[121] = { - 0x7ffe7, 0x7ffe8, 0xfffd2, 0xfffd3, 0xfffd4, 0xfffd5, 0xfffd6, 0xfffd7, - 0xfffd8, 0x7ffda, 0xfffd9, 0xfffda, 0xfffdb, 0xfffdc, 0x7ffdb, 0xfffdd, - 0x7ffdc, 0x7ffdd, 0xfffde, 0x3ffe4, 0xfffdf, 0xfffe0, 0xfffe1, 0x7ffde, - 0xfffe2, 0xfffe3, 0xfffe4, 0x7ffdf, 0xfffe5, 0x7ffe0, 0x3ffe8, 0x7ffe1, - 0x3ffe0, 0x3ffe9, 0x1ffef, 0x3ffe5, 0x1ffec, 0x1ffed, 0x1ffee, 0x0fff4, - 0x0fff3, 0x0fff0, 0x07ff7, 0x07ff6, 0x03ffa, 0x01ffa, 0x01ff9, 0x00ffa, - 0x00ff8, 0x007f9, 0x003fb, 0x001fc, 0x001fa, 0x000fb, 0x0007c, 0x0003c, - 0x0001c, 0x0000c, 0x00005, 0x00001, 0x00000, 0x00004, 0x0000d, 0x0001d, - 0x0003d, 0x000fa, 0x000fc, 0x001fb, 0x003fa, 0x007f8, 0x007fa, 0x007fb, - 0x00ff9, 0x00ffb, 0x01ff8, 0x01ffb, 0x03ff8, 0x03ff9, 0x0fff1, 0x0fff2, - 0x1ffea, 0x1ffeb, 0x3ffe1, 0x3ffe2, 0x3ffea, 0x3ffe3, 0x3ffe6, 0x3ffe7, - 0x3ffeb, 0xfffe6, 0x7ffe2, 0xfffe7, 0xfffe8, 0xfffe9, 0xfffea, 0xfffeb, - 0xfffec, 0x7ffe3, 0xfffed, 0xfffee, 0xfffef, 0xffff0, 0x7ffe4, 0xffff1, - 0x3ffec, 0xffff2, 0xffff3, 0x7ffe5, 0x7ffe6, 0xffff4, 0xffff5, 0xffff6, - 0xffff7, 0xffff8, 0xffff9, 0xffffa, 0xffffb, 0xffffc, 0xffffd, 0xffffe, - 0xfffff, -}; - -static const uint8_t t_huffman_env_bal_1_5dB_bits[49] = { - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 12, 11, 9, 7, 5, 3, - 1, 2, 4, 6, 8, 11, 12, 15, - 16, 16, 16, 16, 16, 16, 16, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, -}; - -static const uint32_t t_huffman_env_bal_1_5dB_codes[49] = { - 0x0ffe4, 0x0ffe5, 0x0ffe6, 0x0ffe7, 0x0ffe8, 0x0ffe9, 0x0ffea, 0x0ffeb, - 0x0ffec, 0x0ffed, 0x0ffee, 0x0ffef, 0x0fff0, 0x0fff1, 0x0fff2, 0x0fff3, - 0x0fff4, 0x0ffe2, 0x00ffc, 0x007fc, 0x001fe, 0x0007e, 0x0001e, 0x00006, - 0x00000, 0x00002, 0x0000e, 0x0003e, 0x000fe, 0x007fd, 0x00ffd, 0x07ff0, - 0x0ffe3, 0x0fff5, 0x0fff6, 0x0fff7, 0x0fff8, 0x0fff9, 0x0fffa, 0x1fff6, - 0x1fff7, 0x1fff8, 0x1fff9, 0x1fffa, 0x1fffb, 0x1fffc, 0x1fffd, 0x1fffe, - 0x1ffff, -}; - -static const uint8_t f_huffman_env_bal_1_5dB_bits[49] = { - 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 16, - 17, 14, 11, 11, 8, 7, 4, 2, - 1, 3, 5, 6, 9, 11, 12, 15, - 16, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 18, 19, - 19, -}; - -static const uint32_t f_huffman_env_bal_1_5dB_codes[49] = { - 0x3ffe2, 0x3ffe3, 0x3ffe4, 0x3ffe5, 0x3ffe6, 0x3ffe7, 0x3ffe8, 0x3ffe9, - 0x3ffea, 0x3ffeb, 0x3ffec, 0x3ffed, 0x3ffee, 0x3ffef, 0x3fff0, 0x0fff7, - 0x1fff0, 0x03ffc, 0x007fe, 0x007fc, 0x000fe, 0x0007e, 0x0000e, 0x00002, - 0x00000, 0x00006, 0x0001e, 0x0003e, 0x001fe, 0x007fd, 0x00ffe, 0x07ffa, - 0x0fff6, 0x3fff1, 0x3fff2, 0x3fff3, 0x3fff4, 0x3fff5, 0x3fff6, 0x3fff7, - 0x3fff8, 0x3fff9, 0x3fffa, 0x3fffb, 0x3fffc, 0x3fffd, 0x3fffe, 0x7fffe, - 0x7ffff, -}; - -static const uint8_t t_huffman_env_3_0dB_bits[63] = { - 18, 18, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, - 19, 17, 16, 16, 16, 14, 14, 14, - 13, 12, 11, 8, 6, 4, 2, 1, - 3, 5, 7, 9, 11, 13, 14, 14, - 15, 16, 17, 18, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, -}; - -static const uint32_t t_huffman_env_3_0dB_codes[63] = { - 0x3ffed, 0x3ffee, 0x7ffde, 0x7ffdf, 0x7ffe0, 0x7ffe1, 0x7ffe2, 0x7ffe3, - 0x7ffe4, 0x7ffe5, 0x7ffe6, 0x7ffe7, 0x7ffe8, 0x7ffe9, 0x7ffea, 0x7ffeb, - 0x7ffec, 0x1fff4, 0x0fff7, 0x0fff9, 0x0fff8, 0x03ffb, 0x03ffa, 0x03ff8, - 0x01ffa, 0x00ffc, 0x007fc, 0x000fe, 0x0003e, 0x0000e, 0x00002, 0x00000, - 0x00006, 0x0001e, 0x0007e, 0x001fe, 0x007fd, 0x01ffb, 0x03ff9, 0x03ffc, - 0x07ffa, 0x0fff6, 0x1fff5, 0x3ffec, 0x7ffed, 0x7ffee, 0x7ffef, 0x7fff0, - 0x7fff1, 0x7fff2, 0x7fff3, 0x7fff4, 0x7fff5, 0x7fff6, 0x7fff7, 0x7fff8, - 0x7fff9, 0x7fffa, 0x7fffb, 0x7fffc, 0x7fffd, 0x7fffe, 0x7ffff, -}; - -static const uint8_t f_huffman_env_3_0dB_bits[63] = { - 20, 20, 20, 20, 20, 20, 20, 18, - 19, 19, 19, 19, 18, 18, 20, 19, - 17, 18, 17, 16, 16, 15, 14, 12, - 11, 10, 9, 8, 6, 4, 2, 1, - 3, 5, 8, 9, 10, 11, 12, 13, - 14, 15, 15, 16, 16, 17, 17, 18, - 18, 18, 20, 19, 19, 19, 20, 19, - 19, 20, 20, 20, 20, 20, 20, -}; - -static const uint32_t f_huffman_env_3_0dB_codes[63] = { - 0xffff0, 0xffff1, 0xffff2, 0xffff3, 0xffff4, 0xffff5, 0xffff6, 0x3fff3, - 0x7fff5, 0x7ffee, 0x7ffef, 0x7fff6, 0x3fff4, 0x3fff2, 0xffff7, 0x7fff0, - 0x1fff5, 0x3fff0, 0x1fff4, 0x0fff7, 0x0fff6, 0x07ff8, 0x03ffb, 0x00ffd, - 0x007fd, 0x003fd, 0x001fd, 0x000fd, 0x0003e, 0x0000e, 0x00002, 0x00000, - 0x00006, 0x0001e, 0x000fc, 0x001fc, 0x003fc, 0x007fc, 0x00ffc, 0x01ffc, - 0x03ffa, 0x07ff9, 0x07ffa, 0x0fff8, 0x0fff9, 0x1fff6, 0x1fff7, 0x3fff5, - 0x3fff6, 0x3fff1, 0xffff8, 0x7fff1, 0x7fff2, 0x7fff3, 0xffff9, 0x7fff7, - 0x7fff4, 0xffffa, 0xffffb, 0xffffc, 0xffffd, 0xffffe, 0xfffff, -}; - -static const uint8_t t_huffman_env_bal_3_0dB_bits[25] = { - 13, 13, 13, 13, 13, 13, 13, 12, - 8, 7, 4, 3, 1, 2, 5, 6, - 9, 13, 13, 13, 13, 13, 13, 14, - 14, -}; - -static const uint16_t t_huffman_env_bal_3_0dB_codes[25] = { - 0x1ff2, 0x1ff3, 0x1ff4, 0x1ff5, 0x1ff6, 0x1ff7, 0x1ff8, 0x0ff8, - 0x00fe, 0x007e, 0x000e, 0x0006, 0x0000, 0x0002, 0x001e, 0x003e, - 0x01fe, 0x1ff9, 0x1ffa, 0x1ffb, 0x1ffc, 0x1ffd, 0x1ffe, 0x3ffe, - 0x3fff, -}; - -static const uint8_t f_huffman_env_bal_3_0dB_bits[25] = { - 13, 13, 13, 13, 13, 14, 14, 11, - 8, 7, 4, 2, 1, 3, 5, 6, - 9, 12, 13, 14, 14, 14, 14, 14, - 14, -}; - -static const uint16_t f_huffman_env_bal_3_0dB_codes[25] = { - 0x1ff7, 0x1ff8, 0x1ff9, 0x1ffa, 0x1ffb, 0x3ff8, 0x3ff9, 0x07fc, - 0x00fe, 0x007e, 0x000e, 0x0002, 0x0000, 0x0006, 0x001e, 0x003e, - 0x01fe, 0x0ffa, 0x1ff6, 0x3ffa, 0x3ffb, 0x3ffc, 0x3ffd, 0x3ffe, - 0x3fff, -}; - -static const uint8_t t_huffman_noise_3_0dB_bits[63] = { - 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 11, 8, 6, 4, 3, 1, - 2, 5, 8, 10, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 14, 14, -}; - -static const uint16_t t_huffman_noise_3_0dB_codes[63] = { - 0x1fce, 0x1fcf, 0x1fd0, 0x1fd1, 0x1fd2, 0x1fd3, 0x1fd4, 0x1fd5, - 0x1fd6, 0x1fd7, 0x1fd8, 0x1fd9, 0x1fda, 0x1fdb, 0x1fdc, 0x1fdd, - 0x1fde, 0x1fdf, 0x1fe0, 0x1fe1, 0x1fe2, 0x1fe3, 0x1fe4, 0x1fe5, - 0x1fe6, 0x1fe7, 0x07f2, 0x00fd, 0x003e, 0x000e, 0x0006, 0x0000, - 0x0002, 0x001e, 0x00fc, 0x03f8, 0x1fcc, 0x1fe8, 0x1fe9, 0x1fea, - 0x1feb, 0x1fec, 0x1fcd, 0x1fed, 0x1fee, 0x1fef, 0x1ff0, 0x1ff1, - 0x1ff2, 0x1ff3, 0x1ff4, 0x1ff5, 0x1ff6, 0x1ff7, 0x1ff8, 0x1ff9, - 0x1ffa, 0x1ffb, 0x1ffc, 0x1ffd, 0x1ffe, 0x3ffe, 0x3fff, -}; - -static const uint8_t t_huffman_noise_bal_3_0dB_bits[25] = { - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 5, 2, 1, 3, 6, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, -}; - -static const uint8_t t_huffman_noise_bal_3_0dB_codes[25] = { - 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, - 0xf4, 0xf5, 0x1c, 0x02, 0x00, 0x06, 0x3a, 0xf6, - 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, - 0xff, -}; - static const int8_t sbr_offset[6][16] = { {-8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7}, // fs_sbr = 16000 Hz {-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13}, // fs_sbr = 22050 Hz @@ -268,271 +41,6 @@ static const int8_t sbr_offset[6][16] = { {-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16, 20, 24}, // 64000 Hz < fs_sbr }; -/* First eight entries repeated at end to simplify SIMD implementations. */ -const attribute_visibility_hidden DECLARE_ALIGNED(16, INTFLOAT, AAC_RENAME(ff_sbr_noise_table))[][2] = { -{Q31(-0.99948153278296f), Q31(-0.59483417516607f)}, {Q31( 0.97113454393991f), Q31(-0.67528515225647f)}, -{Q31( 0.14130051758487f), Q31(-0.95090983575689f)}, {Q31(-0.47005496701697f), Q31(-0.37340549728647f)}, -{Q31( 0.80705063769351f), Q31( 0.29653668284408f)}, {Q31(-0.38981478896926f), Q31( 0.89572605717087f)}, -{Q31(-0.01053049862020f), Q31(-0.66959058036166f)}, {Q31(-0.91266367957293f), Q31(-0.11522938140034f)}, -{Q31( 0.54840422910309f), Q31( 0.75221367176302f)}, {Q31( 0.40009252867955f), Q31(-0.98929400334421f)}, -{Q31(-0.99867974711855f), Q31(-0.88147068645358f)}, {Q31(-0.95531076805040f), Q31( 0.90908757154593f)}, -{Q31(-0.45725933317144f), Q31(-0.56716323646760f)}, {Q31(-0.72929675029275f), Q31(-0.98008272727324f)}, -{Q31( 0.75622801399036f), Q31( 0.20950329995549f)}, {Q31( 0.07069442601050f), Q31(-0.78247898470706f)}, -{Q31( 0.74496252926055f), Q31(-0.91169004445807f)}, {Q31(-0.96440182703856f), Q31(-0.94739918296622f)}, -{Q31( 0.30424629369539f), Q31(-0.49438267012479f)}, {Q31( 0.66565033746925f), Q31( 0.64652935542491f)}, -{Q31( 0.91697008020594f), Q31( 0.17514097332009f)}, {Q31(-0.70774918760427f), Q31( 0.52548653416543f)}, -{Q31(-0.70051415345560f), Q31(-0.45340028808763f)}, {Q31(-0.99496513054797f), Q31(-0.90071908066973f)}, -{Q31( 0.98164490790123f), Q31(-0.77463155528697f)}, {Q31(-0.54671580548181f), Q31(-0.02570928536004f)}, -{Q31(-0.01689629065389f), Q31( 0.00287506445732f)}, {Q31(-0.86110349531986f), Q31( 0.42548583726477f)}, -{Q31(-0.98892980586032f), Q31(-0.87881132267556f)}, {Q31( 0.51756627678691f), Q31( 0.66926784710139f)}, -{Q31(-0.99635026409640f), Q31(-0.58107730574765f)}, {Q31(-0.99969370862163f), Q31( 0.98369989360250f)}, -{Q31( 0.55266258627194f), Q31( 0.59449057465591f)}, {Q31( 0.34581177741673f), Q31( 0.94879421061866f)}, -{Q31( 0.62664209577999f), Q31(-0.74402970906471f)}, {Q31(-0.77149701404973f), Q31(-0.33883658042801f)}, -{Q31(-0.91592244254432f), Q31( 0.03687901376713f)}, {Q31(-0.76285492357887f), Q31(-0.91371867919124f)}, -{Q31( 0.79788337195331f), Q31(-0.93180971199849f)}, {Q31( 0.54473080610200f), Q31(-0.11919206037186f)}, -{Q31(-0.85639281671058f), Q31( 0.42429854760451f)}, {Q31(-0.92882402971423f), Q31( 0.27871809078609f)}, -{Q31(-0.11708371046774f), Q31(-0.99800843444966f)}, {Q31( 0.21356749817493f), Q31(-0.90716295627033f)}, -{Q31(-0.76191692573909f), Q31( 0.99768118356265f)}, {Q31( 0.98111043100884f), Q31(-0.95854459734407f)}, -{Q31(-0.85913269895572f), Q31( 0.95766566168880f)}, {Q31(-0.93307242253692f), Q31( 0.49431757696466f)}, -{Q31( 0.30485754879632f), Q31(-0.70540034357529f)}, {Q31( 0.85289650925190f), Q31( 0.46766131791044f)}, -{Q31( 0.91328082618125f), Q31(-0.99839597361769f)}, {Q31(-0.05890199924154f), Q31( 0.70741827819497f)}, -{Q31( 0.28398686150148f), Q31( 0.34633555702188f)}, {Q31( 0.95258164539612f), Q31(-0.54893416026939f)}, -{Q31(-0.78566324168507f), Q31(-0.75568541079691f)}, {Q31(-0.95789495447877f), Q31(-0.20423194696966f)}, -{Q31( 0.82411158711197f), Q31( 0.96654618432562f)}, {Q31(-0.65185446735885f), Q31(-0.88734990773289f)}, -{Q31(-0.93643603134666f), Q31( 0.99870790442385f)}, {Q31( 0.91427159529618f), Q31(-0.98290505544444f)}, -{Q31(-0.70395684036886f), Q31( 0.58796798221039f)}, {Q31( 0.00563771969365f), Q31( 0.61768196727244f)}, -{Q31( 0.89065051931895f), Q31( 0.52783352697585f)}, {Q31(-0.68683707712762f), Q31( 0.80806944710339f)}, -{Q31( 0.72165342518718f), Q31(-0.69259857349564f)}, {Q31(-0.62928247730667f), Q31( 0.13627037407335f)}, -{Q31( 0.29938434065514f), Q31(-0.46051329682246f)}, {Q31(-0.91781958879280f), Q31(-0.74012716684186f)}, -{Q31( 0.99298717043688f), Q31( 0.40816610075661f)}, {Q31( 0.82368298622748f), Q31(-0.74036047190173f)}, -{Q31(-0.98512833386833f), Q31(-0.99972330709594f)}, {Q31(-0.95915368242257f), Q31(-0.99237800466040f)}, -{Q31(-0.21411126572790f), Q31(-0.93424819052545f)}, {Q31(-0.68821476106884f), Q31(-0.26892306315457f)}, -{Q31( 0.91851997982317f), Q31( 0.09358228901785f)}, {Q31(-0.96062769559127f), Q31( 0.36099095133739f)}, -{Q31( 0.51646184922287f), Q31(-0.71373332873917f)}, {Q31( 0.61130721139669f), Q31( 0.46950141175917f)}, -{Q31( 0.47336129371299f), Q31(-0.27333178296162f)}, {Q31( 0.90998308703519f), Q31( 0.96715662938132f)}, -{Q31( 0.44844799194357f), Q31( 0.99211574628306f)}, {Q31( 0.66614891079092f), Q31( 0.96590176169121f)}, -{Q31( 0.74922239129237f), Q31(-0.89879858826087f)}, {Q31(-0.99571588506485f), Q31( 0.52785521494349f)}, -{Q31( 0.97401082477563f), Q31(-0.16855870075190f)}, {Q31( 0.72683747733879f), Q31(-0.48060774432251f)}, -{Q31( 0.95432193457128f), Q31( 0.68849603408441f)}, {Q31(-0.72962208425191f), Q31(-0.76608443420917f)}, -{Q31(-0.85359479233537f), Q31( 0.88738125901579f)}, {Q31(-0.81412430338535f), Q31(-0.97480768049637f)}, -{Q31(-0.87930772356786f), Q31( 0.74748307690436f)}, {Q31(-0.71573331064977f), Q31(-0.98570608178923f)}, -{Q31( 0.83524300028228f), Q31( 0.83702537075163f)}, {Q31(-0.48086065601423f), Q31(-0.98848504923531f)}, -{Q31( 0.97139128574778f), Q31( 0.80093621198236f)}, {Q31( 0.51992825347895f), Q31( 0.80247631400510f)}, -{Q31(-0.00848591195325f), Q31(-0.76670128000486f)}, {Q31(-0.70294374303036f), Q31( 0.55359910445577f)}, -{Q31(-0.95894428168140f), Q31(-0.43265504344783f)}, {Q31( 0.97079252950321f), Q31( 0.09325857238682f)}, -{Q31(-0.92404293670797f), Q31( 0.85507704027855f)}, {Q31(-0.69506469500450f), Q31( 0.98633412625459f)}, -{Q31( 0.26559203620024f), Q31( 0.73314307966524f)}, {Q31( 0.28038443336943f), Q31( 0.14537913654427f)}, -{Q31(-0.74138124825523f), Q31( 0.99310339807762f)}, {Q31(-0.01752795995444f), Q31(-0.82616635284178f)}, -{Q31(-0.55126773094930f), Q31(-0.98898543862153f)}, {Q31( 0.97960898850996f), Q31(-0.94021446752851f)}, -{Q31(-0.99196309146936f), Q31( 0.67019017358456f)}, {Q31(-0.67684928085260f), Q31( 0.12631491649378f)}, -{Q31( 0.09140039465500f), Q31(-0.20537731453108f)}, {Q31(-0.71658965751996f), Q31(-0.97788200391224f)}, -{Q31( 0.81014640078925f), Q31( 0.53722648362443f)}, {Q31( 0.40616991671205f), Q31(-0.26469008598449f)}, -{Q31(-0.67680188682972f), Q31( 0.94502052337695f)}, {Q31( 0.86849774348749f), Q31(-0.18333598647899f)}, -{Q31(-0.99500381284851f), Q31(-0.02634122068550f)}, {Q31( 0.84329189340667f), Q31( 0.10406957462213f)}, -{Q31(-0.09215968531446f), Q31( 0.69540012101253f)}, {Q31( 0.99956173327206f), Q31(-0.12358542001404f)}, -{Q31(-0.79732779473535f), Q31(-0.91582524736159f)}, {Q31( 0.96349973642406f), Q31( 0.96640458041000f)}, -{Q31(-0.79942778496547f), Q31( 0.64323902822857f)}, {Q31(-0.11566039853896f), Q31( 0.28587846253726f)}, -{Q31(-0.39922954514662f), Q31( 0.94129601616966f)}, {Q31( 0.99089197565987f), Q31(-0.92062625581587f)}, -{Q31( 0.28631285179909f), Q31(-0.91035047143603f)}, {Q31(-0.83302725605608f), Q31(-0.67330410892084f)}, -{Q31( 0.95404443402072f), Q31( 0.49162765398743f)}, {Q31(-0.06449863579434f), Q31( 0.03250560813135f)}, -{Q31(-0.99575054486311f), Q31( 0.42389784469507f)}, {Q31(-0.65501142790847f), Q31( 0.82546114655624f)}, -{Q31(-0.81254441908887f), Q31(-0.51627234660629f)}, {Q31(-0.99646369485481f), Q31( 0.84490533520752f)}, -{Q31( 0.00287840603348f), Q31( 0.64768261158166f)}, {Q31( 0.70176989408455f), Q31(-0.20453028573322f)}, -{Q31( 0.96361882270190f), Q31( 0.40706967140989f)}, {Q31(-0.68883758192426f), Q31( 0.91338958840772f)}, -{Q31(-0.34875585502238f), Q31( 0.71472290693300f)}, {Q31( 0.91980081243087f), Q31( 0.66507455644919f)}, -{Q31(-0.99009048343881f), Q31( 0.85868021604848f)}, {Q31( 0.68865791458395f), Q31( 0.55660316809678f)}, -{Q31(-0.99484402129368f), Q31(-0.20052559254934f)}, {Q31( 0.94214511408023f), Q31(-0.99696425367461f)}, -{Q31(-0.67414626793544f), Q31( 0.49548221180078f)}, {Q31(-0.47339353684664f), Q31(-0.85904328834047f)}, -{Q31( 0.14323651387360f), Q31(-0.94145598222488f)}, {Q31(-0.29268293575672f), Q31( 0.05759224927952f)}, -{Q31( 0.43793861458754f), Q31(-0.78904969892724f)}, {Q31(-0.36345126374441f), Q31( 0.64874435357162f)}, -{Q31(-0.08750604656825f), Q31( 0.97686944362527f)}, {Q31(-0.96495267812511f), Q31(-0.53960305946511f)}, -{Q31( 0.55526940659947f), Q31( 0.78891523734774f)}, {Q31( 0.73538215752630f), Q31( 0.96452072373404f)}, -{Q31(-0.30889773919437f), Q31(-0.80664389776860f)}, {Q31( 0.03574995626194f), Q31(-0.97325616900959f)}, -{Q31( 0.98720684660488f), Q31( 0.48409133691962f)}, {Q31(-0.81689296271203f), Q31(-0.90827703628298f)}, -{Q31( 0.67866860118215f), Q31( 0.81284503870856f)}, {Q31(-0.15808569732583f), Q31( 0.85279555024382f)}, -{Q31( 0.80723395114371f), Q31(-0.24717418514605f)}, {Q31( 0.47788757329038f), Q31(-0.46333147839295f)}, -{Q31( 0.96367554763201f), Q31( 0.38486749303242f)}, {Q31(-0.99143875716818f), Q31(-0.24945277239809f)}, -{Q31( 0.83081876925833f), Q31(-0.94780851414763f)}, {Q31(-0.58753191905341f), Q31( 0.01290772389163f)}, -{Q31( 0.95538108220960f), Q31(-0.85557052096538f)}, {Q31(-0.96490920476211f), Q31(-0.64020970923102f)}, -{Q31(-0.97327101028521f), Q31( 0.12378128133110f)}, {Q31( 0.91400366022124f), Q31( 0.57972471346930f)}, -{Q31(-0.99925837363824f), Q31( 0.71084847864067f)}, {Q31(-0.86875903507313f), Q31(-0.20291699203564f)}, -{Q31(-0.26240034795124f), Q31(-0.68264554369108f)}, {Q31(-0.24664412953388f), Q31(-0.87642273115183f)}, -{Q31( 0.02416275806869f), Q31( 0.27192914288905f)}, {Q31( 0.82068619590515f), Q31(-0.85087787994476f)}, -{Q31( 0.88547373760759f), Q31(-0.89636802901469f)}, {Q31(-0.18173078152226f), Q31(-0.26152145156800f)}, -{Q31( 0.09355476558534f), Q31( 0.54845123045604f)}, {Q31(-0.54668414224090f), Q31( 0.95980774020221f)}, -{Q31( 0.37050990604091f), Q31(-0.59910140383171f)}, {Q31(-0.70373594262891f), Q31( 0.91227665827081f)}, -{Q31(-0.34600785879594f), Q31(-0.99441426144200f)}, {Q31(-0.68774481731008f), Q31(-0.30238837956299f)}, -{Q31(-0.26843291251234f), Q31( 0.83115668004362f)}, {Q31( 0.49072334613242f), Q31(-0.45359708737775f)}, -{Q31( 0.38975993093975f), Q31( 0.95515358099121f)}, {Q31(-0.97757125224150f), Q31( 0.05305894580606f)}, -{Q31(-0.17325552859616f), Q31(-0.92770672250494f)}, {Q31( 0.99948035025744f), Q31( 0.58285545563426f)}, -{Q31(-0.64946246527458f), Q31( 0.68645507104960f)}, {Q31(-0.12016920576437f), Q31(-0.57147322153312f)}, -{Q31(-0.58947456517751f), Q31(-0.34847132454388f)}, {Q31(-0.41815140454465f), Q31( 0.16276422358861f)}, -{Q31( 0.99885650204884f), Q31( 0.11136095490444f)}, {Q31(-0.56649614128386f), Q31(-0.90494866361587f)}, -{Q31( 0.94138021032330f), Q31( 0.35281916733018f)}, {Q31(-0.75725076534641f), Q31( 0.53650549640587f)}, -{Q31( 0.20541973692630f), Q31(-0.94435144369918f)}, {Q31( 0.99980371023351f), Q31( 0.79835913565599f)}, -{Q31( 0.29078277605775f), Q31( 0.35393777921520f)}, {Q31(-0.62858772103030f), Q31( 0.38765693387102f)}, -{Q31( 0.43440904467688f), Q31(-0.98546330463232f)}, {Q31(-0.98298583762390f), Q31( 0.21021524625209f)}, -{Q31( 0.19513029146934f), Q31(-0.94239832251867f)}, {Q31(-0.95476662400101f), Q31( 0.98364554179143f)}, -{Q31( 0.93379635304810f), Q31(-0.70881994583682f)}, {Q31(-0.85235410573336f), Q31(-0.08342347966410f)}, -{Q31(-0.86425093011245f), Q31(-0.45795025029466f)}, {Q31( 0.38879779059045f), Q31( 0.97274429344593f)}, -{Q31( 0.92045124735495f), Q31(-0.62433652524220f)}, {Q31( 0.89162532251878f), Q31( 0.54950955570563f)}, -{Q31(-0.36834336949252f), Q31( 0.96458298020975f)}, {Q31( 0.93891760988045f), Q31(-0.89968353740388f)}, -{Q31( 0.99267657565094f), Q31(-0.03757034316958f)}, {Q31(-0.94063471614176f), Q31( 0.41332338538963f)}, -{Q31( 0.99740224117019f), Q31(-0.16830494996370f)}, {Q31(-0.35899413170555f), Q31(-0.46633226649613f)}, -{Q31( 0.05237237274947f), Q31(-0.25640361602661f)}, {Q31( 0.36703583957424f), Q31(-0.38653265641875f)}, -{Q31( 0.91653180367913f), Q31(-0.30587628726597f)}, {Q31( 0.69000803499316f), Q31( 0.90952171386132f)}, -{Q31(-0.38658751133527f), Q31( 0.99501571208985f)}, {Q31(-0.29250814029851f), Q31( 0.37444994344615f)}, -{Q31(-0.60182204677608f), Q31( 0.86779651036123f)}, {Q31(-0.97418588163217f), Q31( 0.96468523666475f)}, -{Q31( 0.88461574003963f), Q31( 0.57508405276414f)}, {Q31( 0.05198933055162f), Q31( 0.21269661669964f)}, -{Q31(-0.53499621979720f), Q31( 0.97241553731237f)}, {Q31(-0.49429560226497f), Q31( 0.98183865291903f)}, -{Q31(-0.98935142339139f), Q31(-0.40249159006933f)}, {Q31(-0.98081380091130f), Q31(-0.72856895534041f)}, -{Q31(-0.27338148835532f), Q31( 0.99950922447209f)}, {Q31( 0.06310802338302f), Q31(-0.54539587529618f)}, -{Q31(-0.20461677199539f), Q31(-0.14209977628489f)}, {Q31( 0.66223843141647f), Q31( 0.72528579940326f)}, -{Q31(-0.84764345483665f), Q31( 0.02372316801261f)}, {Q31(-0.89039863483811f), Q31( 0.88866581484602f)}, -{Q31( 0.95903308477986f), Q31( 0.76744927173873f)}, {Q31( 0.73504123909879f), Q31(-0.03747203173192f)}, -{Q31(-0.31744434966056f), Q31(-0.36834111883652f)}, {Q31(-0.34110827591623f), Q31( 0.40211222807691f)}, -{Q31( 0.47803883714199f), Q31(-0.39423219786288f)}, {Q31( 0.98299195879514f), Q31( 0.01989791390047f)}, -{Q31(-0.30963073129751f), Q31(-0.18076720599336f)}, {Q31( 0.99992588229018f), Q31(-0.26281872094289f)}, -{Q31(-0.93149731080767f), Q31(-0.98313162570490f)}, {Q31( 0.99923472302773f), Q31(-0.80142993767554f)}, -{Q31(-0.26024169633417f), Q31(-0.75999759855752f)}, {Q31(-0.35712514743563f), Q31( 0.19298963768574f)}, -{Q31(-0.99899084509530f), Q31( 0.74645156992493f)}, {Q31( 0.86557171579452f), Q31( 0.55593866696299f)}, -{Q31( 0.33408042438752f), Q31( 0.86185953874709f)}, {Q31( 0.99010736374716f), Q31( 0.04602397576623f)}, -{Q31(-0.66694269691195f), Q31(-0.91643611810148f)}, {Q31( 0.64016792079480f), Q31( 0.15649530836856f)}, -{Q31( 0.99570534804836f), Q31( 0.45844586038111f)}, {Q31(-0.63431466947340f), Q31( 0.21079116459234f)}, -{Q31(-0.07706847005931f), Q31(-0.89581437101329f)}, {Q31( 0.98590090577724f), Q31( 0.88241721133981f)}, -{Q31( 0.80099335254678f), Q31(-0.36851896710853f)}, {Q31( 0.78368131392666f), Q31( 0.45506999802597f)}, -{Q31( 0.08707806671691f), Q31( 0.80938994918745f)}, {Q31(-0.86811883080712f), Q31( 0.39347308654705f)}, -{Q31(-0.39466529740375f), Q31(-0.66809432114456f)}, {Q31( 0.97875325649683f), Q31(-0.72467840967746f)}, -{Q31(-0.95038560288864f), Q31( 0.89563219587625f)}, {Q31( 0.17005239424212f), Q31( 0.54683053962658f)}, -{Q31(-0.76910792026848f), Q31(-0.96226617549298f)}, {Q31( 0.99743281016846f), Q31( 0.42697157037567f)}, -{Q31( 0.95437383549973f), Q31( 0.97002324109952f)}, {Q31( 0.99578905365569f), Q31(-0.54106826257356f)}, -{Q31( 0.28058259829990f), Q31(-0.85361420634036f)}, {Q31( 0.85256524470573f), Q31(-0.64567607735589f)}, -{Q31(-0.50608540105128f), Q31(-0.65846015480300f)}, {Q31(-0.97210735183243f), Q31(-0.23095213067791f)}, -{Q31( 0.95424048234441f), Q31(-0.99240147091219f)}, {Q31(-0.96926570524023f), Q31( 0.73775654896574f)}, -{Q31( 0.30872163214726f), Q31( 0.41514960556126f)}, {Q31(-0.24523839572639f), Q31( 0.63206633394807f)}, -{Q31(-0.33813265086024f), Q31(-0.38661779441897f)}, {Q31(-0.05826828420146f), Q31(-0.06940774188029f)}, -{Q31(-0.22898461455054f), Q31( 0.97054853316316f)}, {Q31(-0.18509915019881f), Q31( 0.47565762892084f)}, -{Q31(-0.10488238045009f), Q31(-0.87769947402394f)}, {Q31(-0.71886586182037f), Q31( 0.78030982480538f)}, -{Q31( 0.99793873738654f), Q31( 0.90041310491497f)}, {Q31( 0.57563307626120f), Q31(-0.91034337352097f)}, -{Q31( 0.28909646383717f), Q31( 0.96307783970534f)}, {Q31( 0.42188998312520f), Q31( 0.48148651230437f)}, -{Q31( 0.93335049681047f), Q31(-0.43537023883588f)}, {Q31(-0.97087374418267f), Q31( 0.86636445711364f)}, -{Q31( 0.36722871286923f), Q31( 0.65291654172961f)}, {Q31(-0.81093025665696f), Q31( 0.08778370229363f)}, -{Q31(-0.26240603062237f), Q31(-0.92774095379098f)}, {Q31( 0.83996497984604f), Q31( 0.55839849139647f)}, -{Q31(-0.99909615720225f), Q31(-0.96024605713970f)}, {Q31( 0.74649464155061f), Q31( 0.12144893606462f)}, -{Q31(-0.74774595569805f), Q31(-0.26898062008959f)}, {Q31( 0.95781667469567f), Q31(-0.79047927052628f)}, -{Q31( 0.95472308713099f), Q31(-0.08588776019550f)}, {Q31( 0.48708332746299f), Q31( 0.99999041579432f)}, -{Q31( 0.46332038247497f), Q31( 0.10964126185063f)}, {Q31(-0.76497004940162f), Q31( 0.89210929242238f)}, -{Q31( 0.57397389364339f), Q31( 0.35289703373760f)}, {Q31( 0.75374316974495f), Q31( 0.96705214651335f)}, -{Q31(-0.59174397685714f), Q31(-0.89405370422752f)}, {Q31( 0.75087906691890f), Q31(-0.29612672982396f)}, -{Q31(-0.98607857336230f), Q31( 0.25034911730023f)}, {Q31(-0.40761056640505f), Q31(-0.90045573444695f)}, -{Q31( 0.66929266740477f), Q31( 0.98629493401748f)}, {Q31(-0.97463695257310f), Q31(-0.00190223301301f)}, -{Q31( 0.90145509409859f), Q31( 0.99781390365446f)}, {Q31(-0.87259289048043f), Q31( 0.99233587353666f)}, -{Q31(-0.91529461447692f), Q31(-0.15698707534206f)}, {Q31(-0.03305738840705f), Q31(-0.37205262859764f)}, -{Q31( 0.07223051368337f), Q31(-0.88805001733626f)}, {Q31( 0.99498012188353f), Q31( 0.97094358113387f)}, -{Q31(-0.74904939500519f), Q31( 0.99985483641521f)}, {Q31( 0.04585228574211f), Q31( 0.99812337444082f)}, -{Q31(-0.89054954257993f), Q31(-0.31791913188064f)}, {Q31(-0.83782144651251f), Q31( 0.97637632547466f)}, -{Q31( 0.33454804933804f), Q31(-0.86231516800408f)}, {Q31(-0.99707579362824f), Q31( 0.93237990079441f)}, -{Q31(-0.22827527843994f), Q31( 0.18874759397997f)}, {Q31( 0.67248046289143f), Q31(-0.03646211390569f)}, -{Q31(-0.05146538187944f), Q31(-0.92599700120679f)}, {Q31( 0.99947295749905f), Q31( 0.93625229707912f)}, -{Q31( 0.66951124390363f), Q31( 0.98905825623893f)}, {Q31(-0.99602956559179f), Q31(-0.44654715757688f)}, -{Q31( 0.82104905483590f), Q31( 0.99540741724928f)}, {Q31( 0.99186510988782f), Q31( 0.72023001312947f)}, -{Q31(-0.65284592392918f), Q31( 0.52186723253637f)}, {Q31( 0.93885443798188f), Q31(-0.74895312615259f)}, -{Q31( 0.96735248738388f), Q31( 0.90891816978629f)}, {Q31(-0.22225968841114f), Q31( 0.57124029781228f)}, -{Q31(-0.44132783753414f), Q31(-0.92688840659280f)}, {Q31(-0.85694974219574f), Q31( 0.88844532719844f)}, -{Q31( 0.91783042091762f), Q31(-0.46356892383970f)}, {Q31( 0.72556974415690f), Q31(-0.99899555770747f)}, -{Q31(-0.99711581834508f), Q31( 0.58211560180426f)}, {Q31( 0.77638976371966f), Q31( 0.94321834873819f)}, -{Q31( 0.07717324253925f), Q31( 0.58638399856595f)}, {Q31(-0.56049829194163f), Q31( 0.82522301569036f)}, -{Q31( 0.98398893639988f), Q31( 0.39467440420569f)}, {Q31( 0.47546946844938f), Q31( 0.68613044836811f)}, -{Q31( 0.65675089314631f), Q31( 0.18331637134880f)}, {Q31( 0.03273375457980f), Q31(-0.74933109564108f)}, -{Q31(-0.38684144784738f), Q31( 0.51337349030406f)}, {Q31(-0.97346267944545f), Q31(-0.96549364384098f)}, -{Q31(-0.53282156061942f), Q31(-0.91423265091354f)}, {Q31( 0.99817310731176f), Q31( 0.61133572482148f)}, -{Q31(-0.50254500772635f), Q31(-0.88829338134294f)}, {Q31( 0.01995873238855f), Q31( 0.85223515096765f)}, -{Q31( 0.99930381973804f), Q31( 0.94578896296649f)}, {Q31( 0.82907767600783f), Q31(-0.06323442598128f)}, -{Q31(-0.58660709669728f), Q31( 0.96840773806582f)}, {Q31(-0.17573736667267f), Q31(-0.48166920859485f)}, -{Q31( 0.83434292401346f), Q31(-0.13023450646997f)}, {Q31( 0.05946491307025f), Q31( 0.20511047074866f)}, -{Q31( 0.81505484574602f), Q31(-0.94685947861369f)}, {Q31(-0.44976380954860f), Q31( 0.40894572671545f)}, -{Q31(-0.89746474625671f), Q31( 0.99846578838537f)}, {Q31( 0.39677256130792f), Q31(-0.74854668609359f)}, -{Q31(-0.07588948563079f), Q31( 0.74096214084170f)}, {Q31( 0.76343198951445f), Q31( 0.41746629422634f)}, -{Q31(-0.74490104699626f), Q31( 0.94725911744610f)}, {Q31( 0.64880119792759f), Q31( 0.41336660830571f)}, -{Q31( 0.62319537462542f), Q31(-0.93098313552599f)}, {Q31( 0.42215817594807f), Q31(-0.07712787385208f)}, -{Q31( 0.02704554141885f), Q31(-0.05417518053666f)}, {Q31( 0.80001773566818f), Q31( 0.91542195141039f)}, -{Q31(-0.79351832348816f), Q31(-0.36208897989136f)}, {Q31( 0.63872359151636f), Q31( 0.08128252493444f)}, -{Q31( 0.52890520960295f), Q31( 0.60048872455592f)}, {Q31( 0.74238552914587f), Q31( 0.04491915291044f)}, -{Q31( 0.99096131449250f), Q31(-0.19451182854402f)}, {Q31(-0.80412329643109f), Q31(-0.88513818199457f)}, -{Q31(-0.64612616129736f), Q31( 0.72198674804544f)}, {Q31( 0.11657770663191f), Q31(-0.83662833815041f)}, -{Q31(-0.95053182488101f), Q31(-0.96939905138082f)}, {Q31(-0.62228872928622f), Q31( 0.82767262846661f)}, -{Q31( 0.03004475787316f), Q31(-0.99738896333384f)}, {Q31(-0.97987214341034f), Q31( 0.36526129686425f)}, -{Q31(-0.99986980746200f), Q31(-0.36021610299715f)}, {Q31( 0.89110648599879f), Q31(-0.97894250343044f)}, -{Q31( 0.10407960510582f), Q31( 0.77357793811619f)}, {Q31( 0.95964737821728f), Q31(-0.35435818285502f)}, -{Q31( 0.50843233159162f), Q31( 0.96107691266205f)}, {Q31( 0.17006334670615f), Q31(-0.76854025314829f)}, -{Q31( 0.25872675063360f), Q31( 0.99893303933816f)}, {Q31(-0.01115998681937f), Q31( 0.98496019742444f)}, -{Q31(-0.79598702973261f), Q31( 0.97138411318894f)}, {Q31(-0.99264708948101f), Q31(-0.99542822402536f)}, -{Q31(-0.99829663752818f), Q31( 0.01877138824311f)}, {Q31(-0.70801016548184f), Q31( 0.33680685948117f)}, -{Q31(-0.70467057786826f), Q31( 0.93272777501857f)}, {Q31( 0.99846021905254f), Q31(-0.98725746254433f)}, -{Q31(-0.63364968534650f), Q31(-0.16473594423746f)}, {Q31(-0.16258217500792f), Q31(-0.95939125400802f)}, -{Q31(-0.43645594360633f), Q31(-0.94805030113284f)}, {Q31(-0.99848471702976f), Q31( 0.96245166923809f)}, -{Q31(-0.16796458968998f), Q31(-0.98987511890470f)}, {Q31(-0.87979225745213f), Q31(-0.71725725041680f)}, -{Q31( 0.44183099021786f), Q31(-0.93568974498761f)}, {Q31( 0.93310180125532f), Q31(-0.99913308068246f)}, -{Q31(-0.93941931782002f), Q31(-0.56409379640356f)}, {Q31(-0.88590003188677f), Q31( 0.47624600491382f)}, -{Q31( 0.99971463703691f), Q31(-0.83889954253462f)}, {Q31(-0.75376385639978f), Q31( 0.00814643438625f)}, -{Q31( 0.93887685615875f), Q31(-0.11284528204636f)}, {Q31( 0.85126435782309f), Q31( 0.52349251543547f)}, -{Q31( 0.39701421446381f), Q31( 0.81779634174316f)}, {Q31(-0.37024464187437f), Q31(-0.87071656222959f)}, -{Q31(-0.36024828242896f), Q31( 0.34655735648287f)}, {Q31(-0.93388812549209f), Q31(-0.84476541096429f)}, -{Q31(-0.65298804552119f), Q31(-0.18439575450921f)}, {Q31( 0.11960319006843f), Q31( 0.99899346780168f)}, -{Q31( 0.94292565553160f), Q31( 0.83163906518293f)}, {Q31( 0.75081145286948f), Q31(-0.35533223142265f)}, -{Q31( 0.56721979748394f), Q31(-0.24076836414499f)}, {Q31( 0.46857766746029f), Q31(-0.30140233457198f)}, -{Q31( 0.97312313923635f), Q31(-0.99548191630031f)}, {Q31(-0.38299976567017f), Q31( 0.98516909715427f)}, -{Q31( 0.41025800019463f), Q31( 0.02116736935734f)}, {Q31( 0.09638062008048f), Q31( 0.04411984381457f)}, -{Q31(-0.85283249275397f), Q31( 0.91475563922421f)}, {Q31( 0.88866808958124f), Q31(-0.99735267083226f)}, -{Q31(-0.48202429536989f), Q31(-0.96805608884164f)}, {Q31( 0.27572582416567f), Q31( 0.58634753335832f)}, -{Q31(-0.65889129659168f), Q31( 0.58835634138583f)}, {Q31( 0.98838086953732f), Q31( 0.99994349600236f)}, -{Q31(-0.20651349620689f), Q31( 0.54593044066355f)}, {Q31(-0.62126416356920f), Q31(-0.59893681700392f)}, -{Q31( 0.20320105410437f), Q31(-0.86879180355289f)}, {Q31(-0.97790548600584f), Q31( 0.96290806999242f)}, -{Q31( 0.11112534735126f), Q31( 0.21484763313301f)}, {Q31(-0.41368337314182f), Q31( 0.28216837680365f)}, -{Q31( 0.24133038992960f), Q31( 0.51294362630238f)}, {Q31(-0.66393410674885f), Q31(-0.08249679629081f)}, -{Q31(-0.53697829178752f), Q31(-0.97649903936228f)}, {Q31(-0.97224737889348f), Q31( 0.22081333579837f)}, -{Q31( 0.87392477144549f), Q31(-0.12796173740361f)}, {Q31( 0.19050361015753f), Q31( 0.01602615387195f)}, -{Q31(-0.46353441212724f), Q31(-0.95249041539006f)}, {Q31(-0.07064096339021f), Q31(-0.94479803205886f)}, -{Q31(-0.92444085484466f), Q31(-0.10457590187436f)}, {Q31(-0.83822593578728f), Q31(-0.01695043208885f)}, -{Q31( 0.75214681811150f), Q31(-0.99955681042665f)}, {Q31(-0.42102998829339f), Q31( 0.99720941999394f)}, -{Q31(-0.72094786237696f), Q31(-0.35008961934255f)}, {Q31( 0.78843311019251f), Q31( 0.52851398958271f)}, -{Q31( 0.97394027897442f), Q31(-0.26695944086561f)}, {Q31( 0.99206463477946f), Q31(-0.57010120849429f)}, -{Q31( 0.76789609461795f), Q31(-0.76519356730966f)}, {Q31(-0.82002421836409f), Q31(-0.73530179553767f)}, -{Q31( 0.81924990025724f), Q31( 0.99698425250579f)}, {Q31(-0.26719850873357f), Q31( 0.68903369776193f)}, -{Q31(-0.43311260380975f), Q31( 0.85321815947490f)}, {Q31( 0.99194979673836f), Q31( 0.91876249766422f)}, -{Q31(-0.80692001248487f), Q31(-0.32627540663214f)}, {Q31( 0.43080003649976f), Q31(-0.21919095636638f)}, -{Q31( 0.67709491937357f), Q31(-0.95478075822906f)}, {Q31( 0.56151770568316f), Q31(-0.70693811747778f)}, -{Q31( 0.10831862810749f), Q31(-0.08628837174592f)}, {Q31( 0.91229417540436f), Q31(-0.65987351408410f)}, -{Q31(-0.48972893932274f), Q31( 0.56289246362686f)}, {Q31(-0.89033658689697f), Q31(-0.71656563987082f)}, -{Q31( 0.65269447475094f), Q31( 0.65916004833932f)}, {Q31( 0.67439478141121f), Q31(-0.81684380846796f)}, -{Q31(-0.47770832416973f), Q31(-0.16789556203025f)}, {Q31(-0.99715979260878f), Q31(-0.93565784007648f)}, -{Q31(-0.90889593602546f), Q31( 0.62034397054380f)}, {Q31(-0.06618622548177f), Q31(-0.23812217221359f)}, -{Q31( 0.99430266919728f), Q31( 0.18812555317553f)}, {Q31( 0.97686402381843f), Q31(-0.28664534366620f)}, -{Q31( 0.94813650221268f), Q31(-0.97506640027128f)}, {Q31(-0.95434497492853f), Q31(-0.79607978501983f)}, -{Q31(-0.49104783137150f), Q31( 0.32895214359663f)}, {Q31( 0.99881175120751f), Q31( 0.88993983831354f)}, -{Q31( 0.50449166760303f), Q31(-0.85995072408434f)}, {Q31( 0.47162891065108f), Q31(-0.18680204049569f)}, -{Q31(-0.62081581361840f), Q31( 0.75000676218956f)}, {Q31(-0.43867015250812f), Q31( 0.99998069244322f)}, -{Q31( 0.98630563232075f), Q31(-0.53578899600662f)}, {Q31(-0.61510362277374f), Q31(-0.89515019899997f)}, -{Q31(-0.03841517601843f), Q31(-0.69888815681179f)}, {Q31(-0.30102157304644f), Q31(-0.07667808922205f)}, -{Q31( 0.41881284182683f), Q31( 0.02188098922282f)}, {Q31(-0.86135454941237f), Q31( 0.98947480909359f)}, -{Q31( 0.67226861393788f), Q31(-0.13494389011014f)}, {Q31(-0.70737398842068f), Q31(-0.76547349325992f)}, -{Q31( 0.94044946687963f), Q31( 0.09026201157416f)}, {Q31(-0.82386352534327f), Q31( 0.08924768823676f)}, -{Q31(-0.32070666698656f), Q31( 0.50143421908753f)}, {Q31( 0.57593163224487f), Q31(-0.98966422921509f)}, -{Q31(-0.36326018419965f), Q31( 0.07440243123228f)}, {Q31( 0.99979044674350f), Q31(-0.14130287347405f)}, -{Q31(-0.92366023326932f), Q31(-0.97979298068180f)}, {Q31(-0.44607178518598f), Q31(-0.54233252016394f)}, -{Q31( 0.44226800932956f), Q31( 0.71326756742752f)}, {Q31( 0.03671907158312f), Q31( 0.63606389366675f)}, -{Q31( 0.52175424682195f), Q31(-0.85396826735705f)}, {Q31(-0.94701139690956f), Q31(-0.01826348194255f)}, -{Q31(-0.98759606946049f), Q31( 0.82288714303073f)}, {Q31( 0.87434794743625f), Q31( 0.89399495655433f)}, -{Q31(-0.93412041758744f), Q31( 0.41374052024363f)}, {Q31( 0.96063943315511f), Q31( 0.93116709541280f)}, -{Q31( 0.97534253457837f), Q31( 0.86150930812689f)}, {Q31( 0.99642466504163f), Q31( 0.70190043427512f)}, -{Q31(-0.94705089665984f), Q31(-0.29580042814306f)}, {Q31( 0.91599807087376f), Q31(-0.98147830385781f)}, -// Start of duplicated table -{Q31(-0.99948153278296f), Q31(-0.59483417516607f)}, {Q31( 0.97113454393991f), Q31(-0.67528515225647f)}, -{Q31( 0.14130051758487f), Q31(-0.95090983575689f)}, {Q31(-0.47005496701697f), Q31(-0.37340549728647f)}, -{Q31( 0.80705063769351f), Q31( 0.29653668284408f)}, {Q31(-0.38981478896926f), Q31( 0.89572605717087f)}, -{Q31(-0.01053049862020f), Q31(-0.66959058036166f)}, {Q31(-0.91266367957293f), Q31(-0.11522938140034f)}, -}; - ///< window coefficients for analysis/synthesis QMF banks static DECLARE_ALIGNED(32, INTFLOAT, sbr_qmf_window_ds)[320]; /* This table contains redundancy: It is symmetric about the entry #320 diff --git a/libavcodec/aactab.c b/libavcodec/aactab.c index 0f4941d5dff..3cef9c5d2b0 100644 --- a/libavcodec/aactab.c +++ b/libavcodec/aactab.c @@ -27,27 +27,73 @@ * @author Maxim Gavrilov ( maxim.gavrilov gmail com ) */ -#include "config.h" +#include +#include + #include "config_components.h" +#include "libavutil/attributes.h" #include "libavutil/mem_internal.h" #include "libavutil/thread.h" -#include "aac.h" #include "aactab.h" -#include - -float ff_aac_pow2sf_tab[428]; -float ff_aac_pow34sf_tab[428]; - #if CONFIG_AAC_ENCODER || CONFIG_AAC_DECODER #include "kbdwin.h" #include "sinewin.h" +float ff_aac_pow2sf_tab[428]; +float ff_aac_pow34sf_tab[428]; + DECLARE_ALIGNED(32, float, ff_aac_kbd_long_1024)[1024]; DECLARE_ALIGNED(32, float, ff_aac_kbd_short_128)[128]; +static av_cold void aac_tableinit(void) +{ + /* 2^(i/16) for 0 <= i <= 15 */ + static const float exp2_lut[] = { + 1.00000000000000000000, + 1.04427378242741384032, + 1.09050773266525765921, + 1.13878863475669165370, + 1.18920711500272106672, + 1.24185781207348404859, + 1.29683955465100966593, + 1.35425554693689272830, + 1.41421356237309504880, + 1.47682614593949931139, + 1.54221082540794082361, + 1.61049033194925430818, + 1.68179283050742908606, + 1.75625216037329948311, + 1.83400808640934246349, + 1.91520656139714729387, + }; + float t1 = 8.8817841970012523233890533447265625e-16; // 2^(-50) + float t2 = 3.63797880709171295166015625e-12; // 2^(-38) + int t1_inc_cur, t2_inc_cur; + int t1_inc_prev = 0; + int t2_inc_prev = 8; + + for (int i = 0; i < 428; i++) { + t1_inc_cur = 4 * (i % 4); + t2_inc_cur = (8 + 3*i) % 16; + if (t1_inc_cur < t1_inc_prev) + t1 *= 2; + if (t2_inc_cur < t2_inc_prev) + t2 *= 2; + // A much more efficient and accurate way of doing: + // ff_aac_pow2sf_tab[i] = pow(2, (i - POW_SF2_ZERO) / 4.0); + // ff_aac_pow34sf_tab[i] = pow(ff_aac_pow2sf_tab[i], 3.0/4.0); + ff_aac_pow2sf_tab[i] = t1 * exp2_lut[t1_inc_cur]; + ff_aac_pow34sf_tab[i] = t2 * exp2_lut[t2_inc_cur]; + t1_inc_prev = t1_inc_cur; + t2_inc_prev = t2_inc_cur; + } +} + static av_cold void aac_float_common_init(void) { + aac_tableinit(); + ff_kbd_window_init(ff_aac_kbd_long_1024, 4.0, 1024); ff_kbd_window_init(ff_aac_kbd_short_128, 6.0, 128); ff_init_ff_sine_windows(10); @@ -59,6 +105,45 @@ av_cold void ff_aac_float_common_init(void) static AVOnce init_static_once = AV_ONCE_INIT; ff_thread_once(&init_static_once, aac_float_common_init); } + +const float ff_ltp_coef[8] = { + 0.570829, 0.696616, 0.813004, 0.911304, + 0.984900, 1.067894, 1.194601, 1.369533, +}; + +/* @name tns_tmp2_map + * Tables of the tmp2[] arrays of LPC coefficients used for TNS. + * The suffix _M_N[] indicate the values of coef_compress and coef_res + * respectively. + * @{ + */ +static const float tns_tmp2_map_1_3[4] = { + 0.00000000, -0.43388373, 0.64278758, 0.34202015, +}; + +static const float tns_tmp2_map_0_3[8] = { + 0.00000000, -0.43388373, -0.78183150, -0.97492790, + 0.98480773, 0.86602539, 0.64278758, 0.34202015, +}; + +static const float tns_tmp2_map_1_4[8] = { + 0.00000000, -0.20791170, -0.40673664, -0.58778524, + 0.67369562, 0.52643216, 0.36124167, 0.18374951, +}; + +static const float tns_tmp2_map_0_4[16] = { + 0.00000000, -0.20791170, -0.40673664, -0.58778524, + -0.74314481, -0.86602539, -0.95105654, -0.99452192, + 0.99573416, 0.96182561, 0.89516330, 0.79801720, + 0.67369562, 0.52643216, 0.36124167, 0.18374951, +}; + +const float * const ff_tns_tmp2_map[4] = { + tns_tmp2_map_0_3, + tns_tmp2_map_0_4, + tns_tmp2_map_1_3, + tns_tmp2_map_1_4 +}; #endif const uint8_t ff_aac_num_swb_1024[] = { @@ -1285,14 +1370,7 @@ static const uint16_t swb_offset_960_48[] = 672, 704, 736, 768, 800, 832, 864, 896, 928, 960 }; -static const uint16_t swb_offset_960_32[] = -{ - 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, - 40, 48, 56, 64, 72, 80, 88, 96, 108, 120, - 132, 144, 160, 176, 196, 216, 240, 264, 292, 320, - 352, 384, 416, 448, 480, 512, 544, 576, 608, 640, - 672, 704, 736, 768, 800, 832, 864, 896, 928, 960 -}; +#define swb_offset_960_32 swb_offset_960_48 static const uint16_t swb_offset_960_24[] = { @@ -3299,53 +3377,3 @@ const DECLARE_ALIGNED(32, int, ff_aac_eld_window_480_fixed)[1800] = { 0xffecff1c, 0xffed391e, 0xffed740c, 0xffedafb1, 0xffedebe1, 0xffee287d, 0xffee654e, 0xffeea23f, }; - -static void aac_tableinit(void) -{ - /* 2^(i/16) for 0 <= i <= 15 */ - static const float exp2_lut[] = { - 1.00000000000000000000, - 1.04427378242741384032, - 1.09050773266525765921, - 1.13878863475669165370, - 1.18920711500272106672, - 1.24185781207348404859, - 1.29683955465100966593, - 1.35425554693689272830, - 1.41421356237309504880, - 1.47682614593949931139, - 1.54221082540794082361, - 1.61049033194925430818, - 1.68179283050742908606, - 1.75625216037329948311, - 1.83400808640934246349, - 1.91520656139714729387, - }; - float t1 = 8.8817841970012523233890533447265625e-16; // 2^(-50) - float t2 = 3.63797880709171295166015625e-12; // 2^(-38) - int t1_inc_cur, t2_inc_cur; - int t1_inc_prev = 0; - int t2_inc_prev = 8; - - for (int i = 0; i < 428; i++) { - t1_inc_cur = 4 * (i % 4); - t2_inc_cur = (8 + 3*i) % 16; - if (t1_inc_cur < t1_inc_prev) - t1 *= 2; - if (t2_inc_cur < t2_inc_prev) - t2 *= 2; - // A much more efficient and accurate way of doing: - // ff_aac_pow2sf_tab[i] = pow(2, (i - POW_SF2_ZERO) / 4.0); - // ff_aac_pow34sf_tab[i] = pow(ff_aac_pow2sf_tab[i], 3.0/4.0); - ff_aac_pow2sf_tab[i] = t1 * exp2_lut[t1_inc_cur]; - ff_aac_pow34sf_tab[i] = t2 * exp2_lut[t2_inc_cur]; - t1_inc_prev = t1_inc_cur; - t2_inc_prev = t2_inc_cur; - } -} - -void ff_aac_tableinit(void) -{ - static AVOnce init_static_once = AV_ONCE_INIT; - ff_thread_once(&init_static_once, aac_tableinit); -} diff --git a/libavcodec/aactab.h b/libavcodec/aactab.h index 9b1450c2ebd..e1a2d8b9a1d 100644 --- a/libavcodec/aactab.h +++ b/libavcodec/aactab.h @@ -31,7 +31,6 @@ #define AVCODEC_AACTAB_H #include "libavutil/mem_internal.h" -#include "aac.h" #include @@ -42,49 +41,16 @@ extern float ff_aac_pow2sf_tab[428]; extern float ff_aac_pow34sf_tab[428]; -void ff_aac_tableinit(void); - /* @name ltp_coef * Table of the LTP coefficients */ -static const INTFLOAT ltp_coef[8] = { - Q30(0.570829), Q30(0.696616), Q30(0.813004), Q30(0.911304), - Q30(0.984900), Q30(1.067894), Q30(1.194601), Q30(1.369533), -}; +extern const float ff_ltp_coef[8]; /* @name tns_tmp2_map * Tables of the tmp2[] arrays of LPC coefficients used for TNS. - * The suffix _M_N[] indicate the values of coef_compress and coef_res - * respectively. * @{ */ -static const INTFLOAT tns_tmp2_map_1_3[4] = { - Q31(0.00000000), Q31(-0.43388373), Q31(0.64278758), Q31(0.34202015), -}; - -static const INTFLOAT tns_tmp2_map_0_3[8] = { - Q31(0.00000000), Q31(-0.43388373), Q31(-0.78183150), Q31(-0.97492790), - Q31(0.98480773), Q31( 0.86602539), Q31( 0.64278758), Q31( 0.34202015), -}; - -static const INTFLOAT tns_tmp2_map_1_4[8] = { - Q31(0.00000000), Q31(-0.20791170), Q31(-0.40673664), Q31(-0.58778524), - Q31(0.67369562), Q31( 0.52643216), Q31( 0.36124167), Q31( 0.18374951), -}; - -static const INTFLOAT tns_tmp2_map_0_4[16] = { - Q31( 0.00000000), Q31(-0.20791170), Q31(-0.40673664), Q31(-0.58778524), - Q31(-0.74314481), Q31(-0.86602539), Q31(-0.95105654), Q31(-0.99452192), - Q31( 0.99573416), Q31( 0.96182561), Q31( 0.89516330), Q31( 0.79801720), - Q31( 0.67369562), Q31( 0.52643216), Q31( 0.36124167), Q31( 0.18374951), -}; - -static const INTFLOAT * const tns_tmp2_map[4] = { - tns_tmp2_map_0_3, - tns_tmp2_map_0_4, - tns_tmp2_map_1_3, - tns_tmp2_map_1_4 -}; +extern const float *const ff_tns_tmp2_map[4]; // @} /* @name window coefficients diff --git a/libavcodec/aarch64/Makefile b/libavcodec/aarch64/Makefile index 02fb51c3ab9..beb6a02f5f8 100644 --- a/libavcodec/aarch64/Makefile +++ b/libavcodec/aarch64/Makefile @@ -1,5 +1,4 @@ # subsystems -OBJS-$(CONFIG_FFT) += aarch64/fft_init_aarch64.o OBJS-$(CONFIG_FMTCONVERT) += aarch64/fmtconvert_init.o OBJS-$(CONFIG_H264CHROMA) += aarch64/h264chroma_init_aarch64.o OBJS-$(CONFIG_H264DSP) += aarch64/h264dsp_init_aarch64.o @@ -36,7 +35,6 @@ ARMV8-OBJS-$(CONFIG_VIDEODSP) += aarch64/videodsp.o # subsystems NEON-OBJS-$(CONFIG_AAC_DECODER) += aarch64/sbrdsp_neon.o -NEON-OBJS-$(CONFIG_FFT) += aarch64/fft_neon.o NEON-OBJS-$(CONFIG_FMTCONVERT) += aarch64/fmtconvert_neon.o NEON-OBJS-$(CONFIG_H264CHROMA) += aarch64/h264cmc_neon.o NEON-OBJS-$(CONFIG_H264DSP) += aarch64/h264dsp_neon.o \ @@ -47,7 +45,6 @@ NEON-OBJS-$(CONFIG_H264QPEL) += aarch64/h264qpel_neon.o \ NEON-OBJS-$(CONFIG_HPELDSP) += aarch64/hpeldsp_neon.o NEON-OBJS-$(CONFIG_IDCTDSP) += aarch64/idctdsp_neon.o \ aarch64/simple_idct_neon.o -NEON-OBJS-$(CONFIG_MDCT) += aarch64/mdct_neon.o NEON-OBJS-$(CONFIG_ME_CMP) += aarch64/me_cmp_neon.o NEON-OBJS-$(CONFIG_MPEGAUDIODSP) += aarch64/mpegaudiodsp_neon.o NEON-OBJS-$(CONFIG_PIXBLOCKDSP) += aarch64/pixblockdsp_neon.o @@ -65,7 +62,9 @@ NEON-OBJS-$(CONFIG_VP9_DECODER) += aarch64/vp9itxfm_16bpp_neon.o \ aarch64/vp9lpf_neon.o \ aarch64/vp9mc_16bpp_neon.o \ aarch64/vp9mc_neon.o -NEON-OBJS-$(CONFIG_HEVC_DECODER) += aarch64/hevcdsp_idct_neon.o \ +NEON-OBJS-$(CONFIG_HEVC_DECODER) += aarch64/hevcdsp_deblock_neon.o \ + aarch64/hevcdsp_idct_neon.o \ aarch64/hevcdsp_init_aarch64.o \ aarch64/hevcdsp_qpel_neon.o \ + aarch64/hevcdsp_epel_neon.o \ aarch64/hevcdsp_sao_neon.o diff --git a/libavcodec/aarch64/aacpsdsp_neon.S b/libavcodec/aarch64/aacpsdsp_neon.S index ff4e6e244a4..f8cb0b29596 100644 --- a/libavcodec/aarch64/aacpsdsp_neon.S +++ b/libavcodec/aarch64/aacpsdsp_neon.S @@ -19,130 +19,130 @@ #include "libavutil/aarch64/asm.S" function ff_ps_add_squares_neon, export=1 -1: ld1 {v0.4S,v1.4S}, [x1], #32 - fmul v0.4S, v0.4S, v0.4S - fmul v1.4S, v1.4S, v1.4S - faddp v2.4S, v0.4S, v1.4S - ld1 {v3.4S}, [x0] - fadd v3.4S, v3.4S, v2.4S - st1 {v3.4S}, [x0], #16 - subs w2, w2, #4 - b.gt 1b +1: ld1 {v0.4s,v1.4s}, [x1], #32 + fmul v0.4s, v0.4s, v0.4s + fmul v1.4s, v1.4s, v1.4s + faddp v2.4s, v0.4s, v1.4s + ld1 {v3.4s}, [x0] + fadd v3.4s, v3.4s, v2.4s + st1 {v3.4s}, [x0], #16 + subs w2, w2, #4 + b.gt 1b ret endfunc function ff_ps_mul_pair_single_neon, export=1 -1: ld1 {v0.4S,v1.4S}, [x1], #32 - ld1 {v2.4S}, [x2], #16 - zip1 v3.4S, v2.4S, v2.4S - zip2 v4.4S, v2.4S, v2.4S - fmul v0.4S, v0.4S, v3.4S - fmul v1.4S, v1.4S, v4.4S - st1 {v0.4S,v1.4S}, [x0], #32 - subs w3, w3, #4 - b.gt 1b +1: ld1 {v0.4s,v1.4s}, [x1], #32 + ld1 {v2.4s}, [x2], #16 + zip1 v3.4s, v2.4s, v2.4s + zip2 v4.4s, v2.4s, v2.4s + fmul v0.4s, v0.4s, v3.4s + fmul v1.4s, v1.4s, v4.4s + st1 {v0.4s,v1.4s}, [x0], #32 + subs w3, w3, #4 + b.gt 1b ret endfunc function ff_ps_stereo_interpolate_neon, export=1 - ld1 {v0.4S}, [x2] - ld1 {v1.4S}, [x3] - zip1 v4.4S, v0.4S, v0.4S - zip2 v5.4S, v0.4S, v0.4S - zip1 v6.4S, v1.4S, v1.4S - zip2 v7.4S, v1.4S, v1.4S -1: ld1 {v2.2S}, [x0] - ld1 {v3.2S}, [x1] - fadd v4.4S, v4.4S, v6.4S - fadd v5.4S, v5.4S, v7.4S - mov v2.D[1], v2.D[0] - mov v3.D[1], v3.D[0] - fmul v2.4S, v2.4S, v4.4S - fmla v2.4S, v3.4S, v5.4S - st1 {v2.D}[0], [x0], #8 - st1 {v2.D}[1], [x1], #8 - subs w4, w4, #1 - b.gt 1b + ld1 {v0.4s}, [x2] + ld1 {v1.4s}, [x3] + zip1 v4.4s, v0.4s, v0.4s + zip2 v5.4s, v0.4s, v0.4s + zip1 v6.4s, v1.4s, v1.4s + zip2 v7.4s, v1.4s, v1.4s +1: ld1 {v2.2s}, [x0] + ld1 {v3.2s}, [x1] + fadd v4.4s, v4.4s, v6.4s + fadd v5.4s, v5.4s, v7.4s + mov v2.d[1], v2.d[0] + mov v3.d[1], v3.d[0] + fmul v2.4s, v2.4s, v4.4s + fmla v2.4s, v3.4s, v5.4s + st1 {v2.d}[0], [x0], #8 + st1 {v2.d}[1], [x1], #8 + subs w4, w4, #1 + b.gt 1b ret endfunc function ff_ps_stereo_interpolate_ipdopd_neon, export=1 - ld1 {v0.4S,v1.4S}, [x2] - ld1 {v6.4S,v7.4S}, [x3] - fneg v2.4S, v1.4S - fneg v3.4S, v7.4S - zip1 v16.4S, v0.4S, v0.4S - zip2 v17.4S, v0.4S, v0.4S - zip1 v18.4S, v2.4S, v1.4S - zip2 v19.4S, v2.4S, v1.4S - zip1 v20.4S, v6.4S, v6.4S - zip2 v21.4S, v6.4S, v6.4S - zip1 v22.4S, v3.4S, v7.4S - zip2 v23.4S, v3.4S, v7.4S -1: ld1 {v2.2S}, [x0] - ld1 {v3.2S}, [x1] - fadd v16.4S, v16.4S, v20.4S - fadd v17.4S, v17.4S, v21.4S - mov v2.D[1], v2.D[0] - mov v3.D[1], v3.D[0] - fmul v4.4S, v2.4S, v16.4S - fmla v4.4S, v3.4S, v17.4S - fadd v18.4S, v18.4S, v22.4S - fadd v19.4S, v19.4S, v23.4S - ext v2.16B, v2.16B, v2.16B, #4 - ext v3.16B, v3.16B, v3.16B, #4 - fmla v4.4S, v2.4S, v18.4S - fmla v4.4S, v3.4S, v19.4S - st1 {v4.D}[0], [x0], #8 - st1 {v4.D}[1], [x1], #8 - subs w4, w4, #1 - b.gt 1b + ld1 {v0.4s,v1.4s}, [x2] + ld1 {v6.4s,v7.4s}, [x3] + fneg v2.4s, v1.4s + fneg v3.4s, v7.4s + zip1 v16.4s, v0.4s, v0.4s + zip2 v17.4s, v0.4s, v0.4s + zip1 v18.4s, v2.4s, v1.4s + zip2 v19.4s, v2.4s, v1.4s + zip1 v20.4s, v6.4s, v6.4s + zip2 v21.4s, v6.4s, v6.4s + zip1 v22.4s, v3.4s, v7.4s + zip2 v23.4s, v3.4s, v7.4s +1: ld1 {v2.2s}, [x0] + ld1 {v3.2s}, [x1] + fadd v16.4s, v16.4s, v20.4s + fadd v17.4s, v17.4s, v21.4s + mov v2.d[1], v2.d[0] + mov v3.d[1], v3.d[0] + fmul v4.4s, v2.4s, v16.4s + fmla v4.4s, v3.4s, v17.4s + fadd v18.4s, v18.4s, v22.4s + fadd v19.4s, v19.4s, v23.4s + ext v2.16b, v2.16b, v2.16b, #4 + ext v3.16b, v3.16b, v3.16b, #4 + fmla v4.4s, v2.4s, v18.4s + fmla v4.4s, v3.4s, v19.4s + st1 {v4.d}[0], [x0], #8 + st1 {v4.d}[1], [x1], #8 + subs w4, w4, #1 + b.gt 1b ret endfunc function ff_ps_hybrid_analysis_neon, export=1 - lsl x3, x3, #3 - ld2 {v0.4S,v1.4S}, [x1], #32 - ld2 {v2.2S,v3.2S}, [x1], #16 - ld1 {v24.2S}, [x1], #8 - ld2 {v4.2S,v5.2S}, [x1], #16 - ld2 {v6.4S,v7.4S}, [x1] - rev64 v6.4S, v6.4S - rev64 v7.4S, v7.4S - ext v6.16B, v6.16B, v6.16B, #8 - ext v7.16B, v7.16B, v7.16B, #8 - rev64 v4.2S, v4.2S - rev64 v5.2S, v5.2S - mov v2.D[1], v3.D[0] - mov v4.D[1], v5.D[0] - mov v5.D[1], v2.D[0] - mov v3.D[1], v4.D[0] - fadd v16.4S, v0.4S, v6.4S - fadd v17.4S, v1.4S, v7.4S - fsub v18.4S, v1.4S, v7.4S - fsub v19.4S, v0.4S, v6.4S - fadd v22.4S, v2.4S, v4.4S - fsub v23.4S, v5.4S, v3.4S - trn1 v20.2D, v22.2D, v23.2D // {re4+re8, re5+re7, im8-im4, im7-im5} - trn2 v21.2D, v22.2D, v23.2D // {im4+im8, im5+im7, re4-re8, re5-re7} -1: ld2 {v2.4S,v3.4S}, [x2], #32 - ld2 {v4.2S,v5.2S}, [x2], #16 - ld1 {v6.2S}, [x2], #8 - add x2, x2, #8 - mov v4.D[1], v5.D[0] - mov v6.S[1], v6.S[0] - fmul v6.2S, v6.2S, v24.2S - fmul v0.4S, v2.4S, v16.4S - fmul v1.4S, v2.4S, v17.4S - fmls v0.4S, v3.4S, v18.4S - fmla v1.4S, v3.4S, v19.4S - fmla v0.4S, v4.4S, v20.4S - fmla v1.4S, v4.4S, v21.4S - faddp v0.4S, v0.4S, v1.4S - faddp v0.4S, v0.4S, v0.4S - fadd v0.2S, v0.2S, v6.2S - st1 {v0.2S}, [x0], x3 - subs w4, w4, #1 - b.gt 1b + lsl x3, x3, #3 + ld2 {v0.4s,v1.4s}, [x1], #32 + ld2 {v2.2s,v3.2s}, [x1], #16 + ld1 {v24.2s}, [x1], #8 + ld2 {v4.2s,v5.2s}, [x1], #16 + ld2 {v6.4s,v7.4s}, [x1] + rev64 v6.4s, v6.4s + rev64 v7.4s, v7.4s + ext v6.16b, v6.16b, v6.16b, #8 + ext v7.16b, v7.16b, v7.16b, #8 + rev64 v4.2s, v4.2s + rev64 v5.2s, v5.2s + mov v2.d[1], v3.d[0] + mov v4.d[1], v5.d[0] + mov v5.d[1], v2.d[0] + mov v3.d[1], v4.d[0] + fadd v16.4s, v0.4s, v6.4s + fadd v17.4s, v1.4s, v7.4s + fsub v18.4s, v1.4s, v7.4s + fsub v19.4s, v0.4s, v6.4s + fadd v22.4s, v2.4s, v4.4s + fsub v23.4s, v5.4s, v3.4s + trn1 v20.2d, v22.2d, v23.2d // {re4+re8, re5+re7, im8-im4, im7-im5} + trn2 v21.2d, v22.2d, v23.2d // {im4+im8, im5+im7, re4-re8, re5-re7} +1: ld2 {v2.4s,v3.4s}, [x2], #32 + ld2 {v4.2s,v5.2s}, [x2], #16 + ld1 {v6.2s}, [x2], #8 + add x2, x2, #8 + mov v4.d[1], v5.d[0] + mov v6.s[1], v6.s[0] + fmul v6.2s, v6.2s, v24.2s + fmul v0.4s, v2.4s, v16.4s + fmul v1.4s, v2.4s, v17.4s + fmls v0.4s, v3.4s, v18.4s + fmla v1.4s, v3.4s, v19.4s + fmla v0.4s, v4.4s, v20.4s + fmla v1.4s, v4.4s, v21.4s + faddp v0.4s, v0.4s, v1.4s + faddp v0.4s, v0.4s, v0.4s + fadd v0.2s, v0.2s, v6.2s + st1 {v0.2s}, [x0], x3 + subs w4, w4, #1 + b.gt 1b ret endfunc diff --git a/libavcodec/aarch64/fft_init_aarch64.c b/libavcodec/aarch64/fft_init_aarch64.c deleted file mode 100644 index 77f56079608..00000000000 --- a/libavcodec/aarch64/fft_init_aarch64.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2009 Mans Rullgard - * - * 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 "config.h" - -#include "libavutil/attributes.h" -#include "libavutil/cpu.h" -#include "libavutil/aarch64/cpu.h" - -#include "libavcodec/fft.h" - -void ff_fft_permute_neon(FFTContext *s, FFTComplex *z); -void ff_fft_calc_neon(FFTContext *s, FFTComplex *z); - -void ff_imdct_calc_neon(FFTContext *s, FFTSample *output, const FFTSample *input); -void ff_imdct_half_neon(FFTContext *s, FFTSample *output, const FFTSample *input); -void ff_mdct_calc_neon(FFTContext *s, FFTSample *output, const FFTSample *input); - -av_cold void ff_fft_init_aarch64(FFTContext *s) -{ - int cpu_flags = av_get_cpu_flags(); - - if (have_neon(cpu_flags)) { - if (s->nbits < 17) { - s->fft_permute = ff_fft_permute_neon; - s->fft_calc = ff_fft_calc_neon; - } -#if CONFIG_MDCT - s->imdct_calc = ff_imdct_calc_neon; - s->imdct_half = ff_imdct_half_neon; - s->mdct_calc = ff_mdct_calc_neon; - s->mdct_permutation = FF_MDCT_PERM_INTERLEAVE; -#endif - } -} diff --git a/libavcodec/aarch64/fft_neon.S b/libavcodec/aarch64/fft_neon.S deleted file mode 100644 index d7225511dda..00000000000 --- a/libavcodec/aarch64/fft_neon.S +++ /dev/null @@ -1,447 +0,0 @@ -/* - * ARM NEON optimised FFT - * - * Copyright (c) 2009 Mans Rullgard - * Copyright (c) 2009 Naotoshi Nojiri - * Copyright (c) 2014 Janne Grunau - * - * This algorithm (though not any of the implementation details) is - * based on libdjbfft by D. J. Bernstein. - * - * 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/aarch64/asm.S" - -#define M_SQRT1_2 0.70710678118654752440 - -.macro transpose d0, d1, s0, s1 - trn1 \d0, \s0, \s1 - trn2 \d1, \s0, \s1 -.endm - - -function fft4_neon - AARCH64_VALID_JUMP_TARGET - ld1 {v0.2s,v1.2s,v2.2s,v3.2s}, [x0] - - fadd v4.2s, v0.2s, v1.2s // r0+r1,i0+i1 - fsub v6.2s, v0.2s, v1.2s // r0-r1,i0-i1 - - ext v16.8b, v2.8b, v3.8b, #4 - ext v17.8b, v3.8b, v2.8b, #4 - - fadd v5.2s, v2.2s, v3.2s // i2+i3,r2+r3 - fsub v7.2s, v16.2s, v17.2s // r3-r2,i2-i3 - - fadd v0.2s, v4.2s, v5.2s - fsub v2.2s, v4.2s, v5.2s - fadd v1.2s, v6.2s, v7.2s - fsub v3.2s, v6.2s, v7.2s - - st1 {v0.2s,v1.2s,v2.2s,v3.2s}, [x0] - - ret -endfunc - -function fft8_neon - AARCH64_VALID_JUMP_TARGET - mov x1, x0 - ld1 {v0.2s, v1.2s, v2.2s, v3.2s}, [x0], #32 - ld1 {v16.2s,v17.2s,v18.2s,v19.2s}, [x0] - ext v22.8b, v2.8b, v3.8b, #4 - ext v23.8b, v3.8b, v2.8b, #4 - fadd v4.2s, v16.2s, v17.2s // r4+r5,i4+i5 - fadd v5.2s, v18.2s, v19.2s // r6+r7,i6+i7 - fsub v17.2s, v16.2s, v17.2s // r4-r5,i4-i5 - fsub v19.2s, v18.2s, v19.2s // r6-r7,i6-i7 - rev64 v27.2s, v28.2s // ??? - fadd v20.2s, v0.2s, v1.2s // r0+r1,i0+i1 - fadd v21.2s, v2.2s, v3.2s // r2+r3,i2+i3 - fmul v26.2s, v17.2s, v28.2s // -a2r*w,a2i*w - ext v6.8b, v4.8b, v5.8b, #4 - ext v7.8b, v5.8b, v4.8b, #4 - fmul v27.2s, v19.2s, v27.2s // a3r*w,-a3i*w - fsub v23.2s, v22.2s, v23.2s // i2-i3,r3-r2 - fsub v22.2s, v0.2s, v1.2s // r0-r1,i0-i1 - fmul v24.2s, v17.2s, v28.s[1] // a2r*w,a2i*w - fmul v25.2s, v19.2s, v28.s[1] // a3r*w,a3i*w - fadd v0.2s, v20.2s, v21.2s - fsub v2.2s, v20.2s, v21.2s - fadd v1.2s, v22.2s, v23.2s - rev64 v26.2s, v26.2s - rev64 v27.2s, v27.2s - fsub v3.2s, v22.2s, v23.2s - fsub v6.2s, v6.2s, v7.2s - fadd v24.2s, v24.2s, v26.2s // a2r+a2i,a2i-a2r t1,t2 - fadd v25.2s, v25.2s, v27.2s // a3r-a3i,a3i+a3r t5,t6 - fadd v7.2s, v4.2s, v5.2s - fsub v18.2s, v2.2s, v6.2s - ext v26.8b, v24.8b, v25.8b, #4 - ext v27.8b, v25.8b, v24.8b, #4 - fadd v2.2s, v2.2s, v6.2s - fsub v16.2s, v0.2s, v7.2s - fadd v5.2s, v25.2s, v24.2s - fsub v4.2s, v26.2s, v27.2s - fadd v0.2s, v0.2s, v7.2s - fsub v17.2s, v1.2s, v5.2s - fsub v19.2s, v3.2s, v4.2s - fadd v3.2s, v3.2s, v4.2s - fadd v1.2s, v1.2s, v5.2s - - st1 {v16.2s,v17.2s,v18.2s,v19.2s}, [x0] - st1 {v0.2s, v1.2s, v2.2s, v3.2s}, [x1] - - ret -endfunc - -function fft16_neon - AARCH64_VALID_JUMP_TARGET - mov x1, x0 - ld1 {v0.2s, v1.2s, v2.2s, v3.2s}, [x0], #32 - ld1 {v16.2s,v17.2s,v18.2s,v19.2s}, [x0], #32 - ext v22.8b, v2.8b, v3.8b, #4 - ext v23.8b, v3.8b, v2.8b, #4 - fadd v4.2s, v16.2s, v17.2s // r4+r5,i4+i5 - fadd v5.2s, v18.2s, v19.2s // r6+r7,i6+i7 - fsub v17.2s, v16.2s, v17.2s // r4-r5,i4-i5 - fsub v19.2s, v18.2s, v19.2s // r6-r7,i6-i7 - rev64 v27.2s, v28.2s // ??? - fadd v20.2s, v0.2s, v1.2s // r0+r1,i0+i1 - fadd v21.2s, v2.2s, v3.2s // r2+r3,i2+i3 - fmul v26.2s, v17.2s, v28.2s // -a2r*w,a2i*w - ext v6.8b, v4.8b, v5.8b, #4 - ext v7.8b, v5.8b, v4.8b, #4 - fmul v27.2s, v19.2s, v27.2s // a3r*w,-a3i*w - fsub v23.2s, v22.2s, v23.2s // i2-i3,r3-r2 - fsub v22.2s, v0.2s, v1.2s // r0-r1,i0-i1 - fmul v24.2s, v17.2s, v28.s[1] // a2r*w,a2i*w - fmul v25.2s, v19.2s, v28.s[1] // a3r*w,a3i*w - fadd v0.2s, v20.2s, v21.2s - fsub v2.2s, v20.2s, v21.2s - fadd v1.2s, v22.2s, v23.2s - rev64 v26.2s, v26.2s - rev64 v27.2s, v27.2s - fsub v3.2s, v22.2s, v23.2s - fsub v6.2s, v6.2s, v7.2s - fadd v24.2s, v24.2s, v26.2s // a2r+a2i,a2i-a2r t1,t2 - fadd v25.2s, v25.2s, v27.2s // a3r-a3i,a3i+a3r t5,t6 - fadd v7.2s, v4.2s, v5.2s - fsub v18.2s, v2.2s, v6.2s - ld1 {v20.4s,v21.4s}, [x0], #32 - ld1 {v22.4s,v23.4s}, [x0], #32 - ext v26.8b, v24.8b, v25.8b, #4 - ext v27.8b, v25.8b, v24.8b, #4 - fadd v2.2s, v2.2s, v6.2s - fsub v16.2s, v0.2s, v7.2s - fadd v5.2s, v25.2s, v24.2s - fsub v4.2s, v26.2s, v27.2s - transpose v24.2d, v25.2d, v20.2d, v22.2d - transpose v26.2d, v27.2d, v21.2d, v23.2d - fadd v0.2s, v0.2s, v7.2s - fsub v17.2s, v1.2s, v5.2s - fsub v19.2s, v3.2s, v4.2s - fadd v3.2s, v3.2s, v4.2s - fadd v1.2s, v1.2s, v5.2s - ext v20.16b, v21.16b, v21.16b, #4 - ext v21.16b, v23.16b, v23.16b, #4 - - zip1 v0.2d, v0.2d, v1.2d // {z[0], z[1]} - zip1 v1.2d, v2.2d, v3.2d // {z[2], z[3]} - zip1 v2.2d, v16.2d, v17.2d // {z[o1], z[o1+1]} - zip1 v3.2d, v18.2d, v19.2d // {z[o1+2],z[o1+3]} - - // 2 x fft4 - transpose v22.2d, v23.2d, v20.2d, v21.2d - - fadd v4.4s, v24.4s, v25.4s - fadd v5.4s, v26.4s, v27.4s - fsub v6.4s, v24.4s, v25.4s - fsub v7.4s, v22.4s, v23.4s - - ld1 {v23.4s}, [x14] - - fadd v24.4s, v4.4s, v5.4s // {z[o2+0],z[o2+1]} - fsub v26.4s, v4.4s, v5.4s // {z[o2+2],z[o2+3]} - fadd v25.4s, v6.4s, v7.4s // {z[o3+0],z[o3+1]} - fsub v27.4s, v6.4s, v7.4s // {z[o3+2],z[o3+3]} - - //fft_pass_neon_16 - rev64 v7.4s, v25.4s - fmul v25.4s, v25.4s, v23.s[1] - fmul v7.4s, v7.4s, v29.4s - fmla v25.4s, v7.4s, v23.s[3] // {t1a,t2a,t5a,t6a} - - zip1 v20.4s, v24.4s, v25.4s - zip2 v21.4s, v24.4s, v25.4s - fneg v22.4s, v20.4s - fadd v4.4s, v21.4s, v20.4s - fsub v6.4s, v20.4s, v21.4s // just the second half - fadd v5.4s, v21.4s, v22.4s // just the first half - - tbl v4.16b, {v4.16b}, v30.16b // trans4_float - tbl v5.16b, {v5.16b,v6.16b}, v31.16b // trans8_float - - fsub v20.4s, v0.4s, v4.4s // {z[o2],z[o2+1]} - fadd v16.4s, v0.4s, v4.4s // {z[0], z[1]} - fsub v22.4s, v2.4s, v5.4s // {z[o3],z[o3+1]} - fadd v18.4s, v2.4s, v5.4s // {z[o1],z[o1+1]} - -//second half - rev64 v6.4s, v26.4s - fmul v26.4s, v26.4s, v23.s[2] - rev64 v7.4s, v27.4s - fmul v27.4s, v27.4s, v23.s[3] - fmul v6.4s, v6.4s, v29.4s - fmul v7.4s, v7.4s, v29.4s - fmla v26.4s, v6.4s, v23.s[2] // {t1,t2,t5,t6} - fmla v27.4s, v7.4s, v23.s[1] // {t1a,t2a,t5a,t6a} - - zip1 v24.4s, v26.4s, v27.4s - zip2 v25.4s, v26.4s, v27.4s - fneg v26.4s, v24.4s - fadd v4.4s, v25.4s, v24.4s - fsub v6.4s, v24.4s, v25.4s // just the second half - fadd v5.4s, v25.4s, v26.4s // just the first half - - tbl v4.16b, {v4.16b}, v30.16b // trans4_float - tbl v5.16b, {v5.16b,v6.16b}, v31.16b // trans8_float - - fadd v17.4s, v1.4s, v4.4s // {z[2], z[3]} - fsub v21.4s, v1.4s, v4.4s // {z[o2+2],z[o2+3]} - fadd v19.4s, v3.4s, v5.4s // {z[o1+2],z[o1+3]} - fsub v23.4s, v3.4s, v5.4s // {z[o3+2],z[o3+3]} - - st1 {v16.4s,v17.4s}, [x1], #32 - st1 {v18.4s,v19.4s}, [x1], #32 - st1 {v20.4s,v21.4s}, [x1], #32 - st1 {v22.4s,v23.4s}, [x1], #32 - - ret -endfunc - - -const trans4_float, align=4 - .byte 0, 1, 2, 3 - .byte 8, 9, 10, 11 - .byte 4, 5, 6, 7 - .byte 12, 13, 14, 15 -endconst - -const trans8_float, align=4 - .byte 24, 25, 26, 27 - .byte 0, 1, 2, 3 - .byte 28, 29, 30, 31 - .byte 4, 5, 6, 7 -endconst - -function fft_pass_neon - sub x6, x2, #1 // n - 1, loop counter - lsl x5, x2, #3 // 2 * n * sizeof FFTSample - lsl x1, x2, #4 // 2 * n * sizeof FFTComplex - add x5, x4, x5 // wim - add x3, x1, x2, lsl #5 // 4 * n * sizeof FFTComplex - add x2, x0, x2, lsl #5 // &z[o2] - add x3, x0, x3 // &z[o3] - add x1, x0, x1 // &z[o1] - ld1 {v20.4s},[x2] // {z[o2],z[o2+1]} - ld1 {v22.4s},[x3] // {z[o3],z[o3+1]} - ld1 {v4.2s}, [x4], #8 // {wre[0],wre[1]} - trn2 v25.2d, v20.2d, v22.2d - sub x5, x5, #4 // wim-- - trn1 v24.2d, v20.2d, v22.2d - ld1 {v5.s}[0], [x5], x7 // d5[0] = wim[-1] - rev64 v7.4s, v25.4s - fmul v25.4s, v25.4s, v4.s[1] - ld1 {v16.4s}, [x0] // {z[0],z[1]} - fmul v7.4s, v7.4s, v29.4s - ld1 {v17.4s}, [x1] // {z[o1],z[o1+1]} - prfm pldl1keep, [x2, #16] - prfm pldl1keep, [x3, #16] - fmla v25.4s, v7.4s, v5.s[0] // {t1a,t2a,t5a,t6a} - prfm pldl1keep, [x0, #16] - prfm pldl1keep, [x1, #16] - - zip1 v20.4s, v24.4s, v25.4s - zip2 v21.4s, v24.4s, v25.4s - fneg v22.4s, v20.4s - fadd v4.4s, v21.4s, v20.4s - fsub v6.4s, v20.4s, v21.4s // just the second half - fadd v5.4s, v21.4s, v22.4s // just the first half - - tbl v4.16b, {v4.16b}, v30.16b // trans4_float - tbl v5.16b, {v5.16b,v6.16b}, v31.16b // trans8_float - - fadd v20.4s, v16.4s, v4.4s - fsub v22.4s, v16.4s, v4.4s - fadd v21.4s, v17.4s, v5.4s - st1 {v20.4s}, [x0], #16 // {z[0], z[1]} - fsub v23.4s, v17.4s, v5.4s - - st1 {v21.4s}, [x1], #16 // {z[o1],z[o1+1]} - st1 {v22.4s}, [x2], #16 // {z[o2],z[o2+1]} - st1 {v23.4s}, [x3], #16 // {z[o3],z[o3+1]} -1: - ld1 {v20.4s},[x2] // {z[o2],z[o2+1]} - ld1 {v22.4s},[x3] // {z[o3],z[o3+1]} - ld1 {v4.2s}, [x4], #8 // {wre[0],wre[1]} - transpose v26.2d, v27.2d, v20.2d, v22.2d - ld1 {v5.2s}, [x5], x7 // {wim[-1],wim[0]} - rev64 v6.4s, v26.4s - fmul v26.4s, v26.4s, v4.s[0] - rev64 v7.4s, v27.4s - fmul v27.4s, v27.4s, v4.s[1] - fmul v6.4s, v6.4s, v29.4s - fmul v7.4s, v7.4s, v29.4s - ld1 {v16.4s},[x0] // {z[0],z[1]} - fmla v26.4s, v6.4s, v5.s[1] // {t1,t2,t5,t6} - fmla v27.4s, v7.4s, v5.s[0] // {t1a,t2a,t5a,t6a} - ld1 {v17.4s},[x1] // {z[o1],z[o1+1]} - - subs x6, x6, #1 // n-- - - zip1 v20.4s, v26.4s, v27.4s - zip2 v21.4s, v26.4s, v27.4s - fneg v22.4s, v20.4s - fadd v4.4s, v21.4s, v20.4s - fsub v6.4s, v20.4s, v21.4s // just the second half - fadd v5.4s, v21.4s, v22.4s // just the first half - - tbl v4.16b, {v4.16b}, v30.16b // trans4_float - tbl v5.16b, {v5.16b,v6.16b}, v31.16b // trans8_float - - fadd v20.4s, v16.4s, v4.4s - fsub v22.4s, v16.4s, v4.4s - fadd v21.4s, v17.4s, v5.4s - st1 {v20.4s}, [x0], #16 // {z[0], z[1]} - fsub v23.4s, v17.4s, v5.4s - - st1 {v21.4s}, [x1], #16 // {z[o1],z[o1+1]} - st1 {v22.4s}, [x2], #16 // {z[o2],z[o2+1]} - st1 {v23.4s}, [x3], #16 // {z[o3],z[o3+1]} - b.ne 1b - - ret -endfunc - -.macro def_fft n, n2, n4 -function fft\n\()_neon, align=6 - AARCH64_VALID_JUMP_TARGET - AARCH64_SIGN_LINK_REGISTER - stp x28, x30, [sp, #-16]! - add x28, x0, #\n4*2*8 - bl fft\n2\()_neon - mov x0, x28 - bl fft\n4\()_neon - add x0, x28, #\n4*1*8 - bl fft\n4\()_neon - sub x0, x28, #\n4*2*8 - ldp x28, x30, [sp], #16 - AARCH64_VALIDATE_LINK_REGISTER - movrel x4, X(ff_cos_\n) - mov x2, #\n4>>1 - b fft_pass_neon -endfunc -.endm - - def_fft 32, 16, 8 - def_fft 64, 32, 16 - def_fft 128, 64, 32 - def_fft 256, 128, 64 - def_fft 512, 256, 128 - def_fft 1024, 512, 256 - def_fft 2048, 1024, 512 - def_fft 4096, 2048, 1024 - def_fft 8192, 4096, 2048 - def_fft 16384, 8192, 4096 - def_fft 32768, 16384, 8192 - def_fft 65536, 32768, 16384 - -function ff_fft_calc_neon, export=1 - prfm pldl1keep, [x1] - movrel x10, trans4_float - ldr w2, [x0] - movrel x11, trans8_float - sub w2, w2, #2 - movrel x3, fft_tab_neon - ld1 {v30.16b}, [x10] - mov x7, #-8 - movrel x12, pmmp - ldr x3, [x3, x2, lsl #3] - movrel x13, mppm - movrel x14, X(ff_cos_16) - ld1 {v31.16b}, [x11] - mov x0, x1 - ld1 {v29.4s}, [x12] // pmmp - ld1 {v28.4s}, [x13] - br x3 -endfunc - -function ff_fft_permute_neon, export=1 - mov x6, #1 - ldr w2, [x0] // nbits - ldr x3, [x0, #16] // tmp_buf - ldr x0, [x0, #8] // revtab - lsl x6, x6, x2 - mov x2, x6 -1: - ld1 {v0.2s,v1.2s}, [x1], #16 - ldr w4, [x0], #4 - uxth w5, w4 - lsr w4, w4, #16 - add x5, x3, x5, lsl #3 - add x4, x3, x4, lsl #3 - st1 {v0.2s}, [x5] - st1 {v1.2s}, [x4] - subs x6, x6, #2 - b.gt 1b - - sub x1, x1, x2, lsl #3 -1: - ld1 {v0.4s,v1.4s}, [x3], #32 - st1 {v0.4s,v1.4s}, [x1], #32 - subs x2, x2, #4 - b.gt 1b - - ret -endfunc - -const fft_tab_neon, relocate=1 - .quad fft4_neon - .quad fft8_neon - .quad fft16_neon - .quad fft32_neon - .quad fft64_neon - .quad fft128_neon - .quad fft256_neon - .quad fft512_neon - .quad fft1024_neon - .quad fft2048_neon - .quad fft4096_neon - .quad fft8192_neon - .quad fft16384_neon - .quad fft32768_neon - .quad fft65536_neon -endconst - -const pmmp, align=4 - .float +1.0, -1.0, -1.0, +1.0 -endconst - -const mppm, align=4 - .float -M_SQRT1_2, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2 -endconst diff --git a/libavcodec/aarch64/h264cmc_neon.S b/libavcodec/aarch64/h264cmc_neon.S index 88ccd727d0e..2ddd5c8a538 100644 --- a/libavcodec/aarch64/h264cmc_neon.S +++ b/libavcodec/aarch64/h264cmc_neon.S @@ -38,11 +38,11 @@ function ff_\type\()_\codec\()_chroma_mc8_neon, export=1 lsl w9, w9, #3 lsl w10, w10, #1 add w9, w9, w10 - add x6, x6, w9, UXTW - ld1r {v22.8H}, [x6] + add x6, x6, w9, uxtw + ld1r {v22.8h}, [x6] .endif .ifc \codec,vc1 - movi v22.8H, #28 + movi v22.8h, #28 .endif mul w7, w4, w5 lsl w14, w5, #3 @@ -55,139 +55,139 @@ function ff_\type\()_\codec\()_chroma_mc8_neon, export=1 add w4, w4, #64 b.eq 2f - dup v0.8B, w4 - dup v1.8B, w12 - ld1 {v4.8B, v5.8B}, [x1], x2 - dup v2.8B, w6 - dup v3.8B, w7 - ext v5.8B, v4.8B, v5.8B, #1 -1: ld1 {v6.8B, v7.8B}, [x1], x2 - umull v16.8H, v4.8B, v0.8B - umlal v16.8H, v5.8B, v1.8B - ext v7.8B, v6.8B, v7.8B, #1 - ld1 {v4.8B, v5.8B}, [x1], x2 - umlal v16.8H, v6.8B, v2.8B + dup v0.8b, w4 + dup v1.8b, w12 + ld1 {v4.8b, v5.8b}, [x1], x2 + dup v2.8b, w6 + dup v3.8b, w7 + ext v5.8b, v4.8b, v5.8b, #1 +1: ld1 {v6.8b, v7.8b}, [x1], x2 + umull v16.8h, v4.8b, v0.8b + umlal v16.8h, v5.8b, v1.8b + ext v7.8b, v6.8b, v7.8b, #1 + ld1 {v4.8b, v5.8b}, [x1], x2 + umlal v16.8h, v6.8b, v2.8b prfm pldl1strm, [x1] - ext v5.8B, v4.8B, v5.8B, #1 - umlal v16.8H, v7.8B, v3.8B - umull v17.8H, v6.8B, v0.8B + ext v5.8b, v4.8b, v5.8b, #1 + umlal v16.8h, v7.8b, v3.8b + umull v17.8h, v6.8b, v0.8b subs w3, w3, #2 - umlal v17.8H, v7.8B, v1.8B - umlal v17.8H, v4.8B, v2.8B - umlal v17.8H, v5.8B, v3.8B + umlal v17.8h, v7.8b, v1.8b + umlal v17.8h, v4.8b, v2.8b + umlal v17.8h, v5.8b, v3.8b prfm pldl1strm, [x1, x2] .ifc \codec,h264 - rshrn v16.8B, v16.8H, #6 - rshrn v17.8B, v17.8H, #6 + rshrn v16.8b, v16.8h, #6 + rshrn v17.8b, v17.8h, #6 .else - add v16.8H, v16.8H, v22.8H - add v17.8H, v17.8H, v22.8H - shrn v16.8B, v16.8H, #6 - shrn v17.8B, v17.8H, #6 + add v16.8h, v16.8h, v22.8h + add v17.8h, v17.8h, v22.8h + shrn v16.8b, v16.8h, #6 + shrn v17.8b, v17.8h, #6 .endif .ifc \type,avg - ld1 {v20.8B}, [x8], x2 - ld1 {v21.8B}, [x8], x2 - urhadd v16.8B, v16.8B, v20.8B - urhadd v17.8B, v17.8B, v21.8B + ld1 {v20.8b}, [x8], x2 + ld1 {v21.8b}, [x8], x2 + urhadd v16.8b, v16.8b, v20.8b + urhadd v17.8b, v17.8b, v21.8b .endif - st1 {v16.8B}, [x0], x2 - st1 {v17.8B}, [x0], x2 + st1 {v16.8b}, [x0], x2 + st1 {v17.8b}, [x0], x2 b.gt 1b ret 2: adds w12, w12, w6 - dup v0.8B, w4 + dup v0.8b, w4 b.eq 5f tst w6, w6 - dup v1.8B, w12 + dup v1.8b, w12 b.eq 4f - ld1 {v4.8B}, [x1], x2 -3: ld1 {v6.8B}, [x1], x2 - umull v16.8H, v4.8B, v0.8B - umlal v16.8H, v6.8B, v1.8B - ld1 {v4.8B}, [x1], x2 - umull v17.8H, v6.8B, v0.8B - umlal v17.8H, v4.8B, v1.8B + ld1 {v4.8b}, [x1], x2 +3: ld1 {v6.8b}, [x1], x2 + umull v16.8h, v4.8b, v0.8b + umlal v16.8h, v6.8b, v1.8b + ld1 {v4.8b}, [x1], x2 + umull v17.8h, v6.8b, v0.8b + umlal v17.8h, v4.8b, v1.8b prfm pldl1strm, [x1] .ifc \codec,h264 - rshrn v16.8B, v16.8H, #6 - rshrn v17.8B, v17.8H, #6 + rshrn v16.8b, v16.8h, #6 + rshrn v17.8b, v17.8h, #6 .else - add v16.8H, v16.8H, v22.8H - add v17.8H, v17.8H, v22.8H - shrn v16.8B, v16.8H, #6 - shrn v17.8B, v17.8H, #6 + add v16.8h, v16.8h, v22.8h + add v17.8h, v17.8h, v22.8h + shrn v16.8b, v16.8h, #6 + shrn v17.8b, v17.8h, #6 .endif prfm pldl1strm, [x1, x2] .ifc \type,avg - ld1 {v20.8B}, [x8], x2 - ld1 {v21.8B}, [x8], x2 - urhadd v16.8B, v16.8B, v20.8B - urhadd v17.8B, v17.8B, v21.8B + ld1 {v20.8b}, [x8], x2 + ld1 {v21.8b}, [x8], x2 + urhadd v16.8b, v16.8b, v20.8b + urhadd v17.8b, v17.8b, v21.8b .endif subs w3, w3, #2 - st1 {v16.8B}, [x0], x2 - st1 {v17.8B}, [x0], x2 + st1 {v16.8b}, [x0], x2 + st1 {v17.8b}, [x0], x2 b.gt 3b ret -4: ld1 {v4.8B, v5.8B}, [x1], x2 - ld1 {v6.8B, v7.8B}, [x1], x2 - ext v5.8B, v4.8B, v5.8B, #1 - ext v7.8B, v6.8B, v7.8B, #1 +4: ld1 {v4.8b, v5.8b}, [x1], x2 + ld1 {v6.8b, v7.8b}, [x1], x2 + ext v5.8b, v4.8b, v5.8b, #1 + ext v7.8b, v6.8b, v7.8b, #1 prfm pldl1strm, [x1] subs w3, w3, #2 - umull v16.8H, v4.8B, v0.8B - umlal v16.8H, v5.8B, v1.8B - umull v17.8H, v6.8B, v0.8B - umlal v17.8H, v7.8B, v1.8B + umull v16.8h, v4.8b, v0.8b + umlal v16.8h, v5.8b, v1.8b + umull v17.8h, v6.8b, v0.8b + umlal v17.8h, v7.8b, v1.8b prfm pldl1strm, [x1, x2] .ifc \codec,h264 - rshrn v16.8B, v16.8H, #6 - rshrn v17.8B, v17.8H, #6 + rshrn v16.8b, v16.8h, #6 + rshrn v17.8b, v17.8h, #6 .else - add v16.8H, v16.8H, v22.8H - add v17.8H, v17.8H, v22.8H - shrn v16.8B, v16.8H, #6 - shrn v17.8B, v17.8H, #6 + add v16.8h, v16.8h, v22.8h + add v17.8h, v17.8h, v22.8h + shrn v16.8b, v16.8h, #6 + shrn v17.8b, v17.8h, #6 .endif .ifc \type,avg - ld1 {v20.8B}, [x8], x2 - ld1 {v21.8B}, [x8], x2 - urhadd v16.8B, v16.8B, v20.8B - urhadd v17.8B, v17.8B, v21.8B + ld1 {v20.8b}, [x8], x2 + ld1 {v21.8b}, [x8], x2 + urhadd v16.8b, v16.8b, v20.8b + urhadd v17.8b, v17.8b, v21.8b .endif - st1 {v16.8B}, [x0], x2 - st1 {v17.8B}, [x0], x2 + st1 {v16.8b}, [x0], x2 + st1 {v17.8b}, [x0], x2 b.gt 4b ret -5: ld1 {v4.8B}, [x1], x2 - ld1 {v5.8B}, [x1], x2 +5: ld1 {v4.8b}, [x1], x2 + ld1 {v5.8b}, [x1], x2 prfm pldl1strm, [x1] subs w3, w3, #2 - umull v16.8H, v4.8B, v0.8B - umull v17.8H, v5.8B, v0.8B + umull v16.8h, v4.8b, v0.8b + umull v17.8h, v5.8b, v0.8b prfm pldl1strm, [x1, x2] .ifc \codec,h264 - rshrn v16.8B, v16.8H, #6 - rshrn v17.8B, v17.8H, #6 + rshrn v16.8b, v16.8h, #6 + rshrn v17.8b, v17.8h, #6 .else - add v16.8H, v16.8H, v22.8H - add v17.8H, v17.8H, v22.8H - shrn v16.8B, v16.8H, #6 - shrn v17.8B, v17.8H, #6 + add v16.8h, v16.8h, v22.8h + add v17.8h, v17.8h, v22.8h + shrn v16.8b, v16.8h, #6 + shrn v17.8b, v17.8h, #6 .endif .ifc \type,avg - ld1 {v20.8B}, [x8], x2 - ld1 {v21.8B}, [x8], x2 - urhadd v16.8B, v16.8B, v20.8B - urhadd v17.8B, v17.8B, v21.8B + ld1 {v20.8b}, [x8], x2 + ld1 {v21.8b}, [x8], x2 + urhadd v16.8b, v16.8b, v20.8b + urhadd v17.8b, v17.8b, v21.8b .endif - st1 {v16.8B}, [x0], x2 - st1 {v17.8B}, [x0], x2 + st1 {v16.8b}, [x0], x2 + st1 {v17.8b}, [x0], x2 b.gt 5b ret endfunc @@ -208,11 +208,11 @@ function ff_\type\()_\codec\()_chroma_mc4_neon, export=1 lsl w9, w9, #3 lsl w10, w10, #1 add w9, w9, w10 - add x6, x6, w9, UXTW - ld1r {v22.8H}, [x6] + add x6, x6, w9, uxtw + ld1r {v22.8h}, [x6] .endif .ifc \codec,vc1 - movi v22.8H, #28 + movi v22.8h, #28 .endif mul w7, w4, w5 lsl w14, w5, #3 @@ -225,133 +225,133 @@ function ff_\type\()_\codec\()_chroma_mc4_neon, export=1 add w4, w4, #64 b.eq 2f - dup v24.8B, w4 - dup v25.8B, w12 - ld1 {v4.8B}, [x1], x2 - dup v26.8B, w6 - dup v27.8B, w7 - ext v5.8B, v4.8B, v5.8B, #1 - trn1 v0.2S, v24.2S, v25.2S - trn1 v2.2S, v26.2S, v27.2S - trn1 v4.2S, v4.2S, v5.2S -1: ld1 {v6.8B}, [x1], x2 - ext v7.8B, v6.8B, v7.8B, #1 - trn1 v6.2S, v6.2S, v7.2S - umull v18.8H, v4.8B, v0.8B - umlal v18.8H, v6.8B, v2.8B - ld1 {v4.8B}, [x1], x2 - ext v5.8B, v4.8B, v5.8B, #1 - trn1 v4.2S, v4.2S, v5.2S + dup v24.8b, w4 + dup v25.8b, w12 + ld1 {v4.8b}, [x1], x2 + dup v26.8b, w6 + dup v27.8b, w7 + ext v5.8b, v4.8b, v5.8b, #1 + trn1 v0.2s, v24.2s, v25.2s + trn1 v2.2s, v26.2s, v27.2s + trn1 v4.2s, v4.2s, v5.2s +1: ld1 {v6.8b}, [x1], x2 + ext v7.8b, v6.8b, v7.8b, #1 + trn1 v6.2s, v6.2s, v7.2s + umull v18.8h, v4.8b, v0.8b + umlal v18.8h, v6.8b, v2.8b + ld1 {v4.8b}, [x1], x2 + ext v5.8b, v4.8b, v5.8b, #1 + trn1 v4.2s, v4.2s, v5.2s prfm pldl1strm, [x1] - umull v19.8H, v6.8B, v0.8B - umlal v19.8H, v4.8B, v2.8B - trn1 v30.2D, v18.2D, v19.2D - trn2 v31.2D, v18.2D, v19.2D - add v18.8H, v30.8H, v31.8H + umull v19.8h, v6.8b, v0.8b + umlal v19.8h, v4.8b, v2.8b + trn1 v30.2d, v18.2d, v19.2d + trn2 v31.2d, v18.2d, v19.2d + add v18.8h, v30.8h, v31.8h .ifc \codec,h264 - rshrn v16.8B, v18.8H, #6 + rshrn v16.8b, v18.8h, #6 .else - add v18.8H, v18.8H, v22.8H - shrn v16.8B, v18.8H, #6 + add v18.8h, v18.8h, v22.8h + shrn v16.8b, v18.8h, #6 .endif subs w3, w3, #2 prfm pldl1strm, [x1, x2] .ifc \type,avg - ld1 {v20.S}[0], [x8], x2 - ld1 {v20.S}[1], [x8], x2 - urhadd v16.8B, v16.8B, v20.8B + ld1 {v20.s}[0], [x8], x2 + ld1 {v20.s}[1], [x8], x2 + urhadd v16.8b, v16.8b, v20.8b .endif - st1 {v16.S}[0], [x0], x2 - st1 {v16.S}[1], [x0], x2 + st1 {v16.s}[0], [x0], x2 + st1 {v16.s}[1], [x0], x2 b.gt 1b ret 2: adds w12, w12, w6 - dup v30.8B, w4 + dup v30.8b, w4 b.eq 5f tst w6, w6 - dup v31.8B, w12 - trn1 v0.2S, v30.2S, v31.2S - trn2 v1.2S, v30.2S, v31.2S + dup v31.8b, w12 + trn1 v0.2s, v30.2s, v31.2s + trn2 v1.2s, v30.2s, v31.2s b.eq 4f - ext v1.8B, v0.8B, v1.8B, #4 - ld1 {v4.S}[0], [x1], x2 -3: ld1 {v4.S}[1], [x1], x2 - umull v18.8H, v4.8B, v0.8B - ld1 {v4.S}[0], [x1], x2 - umull v19.8H, v4.8B, v1.8B - trn1 v30.2D, v18.2D, v19.2D - trn2 v31.2D, v18.2D, v19.2D - add v18.8H, v30.8H, v31.8H + ext v1.8b, v0.8b, v1.8b, #4 + ld1 {v4.s}[0], [x1], x2 +3: ld1 {v4.s}[1], [x1], x2 + umull v18.8h, v4.8b, v0.8b + ld1 {v4.s}[0], [x1], x2 + umull v19.8h, v4.8b, v1.8b + trn1 v30.2d, v18.2d, v19.2d + trn2 v31.2d, v18.2d, v19.2d + add v18.8h, v30.8h, v31.8h prfm pldl1strm, [x1] .ifc \codec,h264 - rshrn v16.8B, v18.8H, #6 + rshrn v16.8b, v18.8h, #6 .else - add v18.8H, v18.8H, v22.8H - shrn v16.8B, v18.8H, #6 + add v18.8h, v18.8h, v22.8h + shrn v16.8b, v18.8h, #6 .endif .ifc \type,avg - ld1 {v20.S}[0], [x8], x2 - ld1 {v20.S}[1], [x8], x2 - urhadd v16.8B, v16.8B, v20.8B + ld1 {v20.s}[0], [x8], x2 + ld1 {v20.s}[1], [x8], x2 + urhadd v16.8b, v16.8b, v20.8b .endif subs w3, w3, #2 prfm pldl1strm, [x1, x2] - st1 {v16.S}[0], [x0], x2 - st1 {v16.S}[1], [x0], x2 + st1 {v16.s}[0], [x0], x2 + st1 {v16.s}[1], [x0], x2 b.gt 3b ret -4: ld1 {v4.8B}, [x1], x2 - ld1 {v6.8B}, [x1], x2 - ext v5.8B, v4.8B, v5.8B, #1 - ext v7.8B, v6.8B, v7.8B, #1 - trn1 v4.2S, v4.2S, v5.2S - trn1 v6.2S, v6.2S, v7.2S - umull v18.8H, v4.8B, v0.8B - umull v19.8H, v6.8B, v0.8B +4: ld1 {v4.8b}, [x1], x2 + ld1 {v6.8b}, [x1], x2 + ext v5.8b, v4.8b, v5.8b, #1 + ext v7.8b, v6.8b, v7.8b, #1 + trn1 v4.2s, v4.2s, v5.2s + trn1 v6.2s, v6.2s, v7.2s + umull v18.8h, v4.8b, v0.8b + umull v19.8h, v6.8b, v0.8b subs w3, w3, #2 - trn1 v30.2D, v18.2D, v19.2D - trn2 v31.2D, v18.2D, v19.2D - add v18.8H, v30.8H, v31.8H + trn1 v30.2d, v18.2d, v19.2d + trn2 v31.2d, v18.2d, v19.2d + add v18.8h, v30.8h, v31.8h prfm pldl1strm, [x1] .ifc \codec,h264 - rshrn v16.8B, v18.8H, #6 + rshrn v16.8b, v18.8h, #6 .else - add v18.8H, v18.8H, v22.8H - shrn v16.8B, v18.8H, #6 + add v18.8h, v18.8h, v22.8h + shrn v16.8b, v18.8h, #6 .endif .ifc \type,avg - ld1 {v20.S}[0], [x8], x2 - ld1 {v20.S}[1], [x8], x2 - urhadd v16.8B, v16.8B, v20.8B + ld1 {v20.s}[0], [x8], x2 + ld1 {v20.s}[1], [x8], x2 + urhadd v16.8b, v16.8b, v20.8b .endif prfm pldl1strm, [x1] - st1 {v16.S}[0], [x0], x2 - st1 {v16.S}[1], [x0], x2 + st1 {v16.s}[0], [x0], x2 + st1 {v16.s}[1], [x0], x2 b.gt 4b ret -5: ld1 {v4.S}[0], [x1], x2 - ld1 {v4.S}[1], [x1], x2 - umull v18.8H, v4.8B, v30.8B +5: ld1 {v4.s}[0], [x1], x2 + ld1 {v4.s}[1], [x1], x2 + umull v18.8h, v4.8b, v30.8b subs w3, w3, #2 prfm pldl1strm, [x1] .ifc \codec,h264 - rshrn v16.8B, v18.8H, #6 + rshrn v16.8b, v18.8h, #6 .else - add v18.8H, v18.8H, v22.8H - shrn v16.8B, v18.8H, #6 + add v18.8h, v18.8h, v22.8h + shrn v16.8b, v18.8h, #6 .endif .ifc \type,avg - ld1 {v20.S}[0], [x8], x2 - ld1 {v20.S}[1], [x8], x2 - urhadd v16.8B, v16.8B, v20.8B + ld1 {v20.s}[0], [x8], x2 + ld1 {v20.s}[1], [x8], x2 + urhadd v16.8b, v16.8b, v20.8b .endif prfm pldl1strm, [x1] - st1 {v16.S}[0], [x0], x2 - st1 {v16.S}[1], [x0], x2 + st1 {v16.s}[0], [x0], x2 + st1 {v16.s}[1], [x0], x2 b.gt 5b ret endfunc @@ -372,51 +372,51 @@ function ff_\type\()_h264_chroma_mc2_neon, export=1 sub w4, w7, w13 sub w4, w4, w14 add w4, w4, #64 - dup v0.8B, w4 - dup v2.8B, w12 - dup v1.8B, w6 - dup v3.8B, w7 - trn1 v0.4H, v0.4H, v2.4H - trn1 v1.4H, v1.4H, v3.4H + dup v0.8b, w4 + dup v2.8b, w12 + dup v1.8b, w6 + dup v3.8b, w7 + trn1 v0.4h, v0.4h, v2.4h + trn1 v1.4h, v1.4h, v3.4h 1: - ld1 {v4.S}[0], [x1], x2 - ld1 {v4.S}[1], [x1], x2 - rev64 v5.2S, v4.2S - ld1 {v5.S}[1], [x1] - ext v6.8B, v4.8B, v5.8B, #1 - ext v7.8B, v5.8B, v4.8B, #1 - trn1 v4.4H, v4.4H, v6.4H - trn1 v5.4H, v5.4H, v7.4H - umull v16.8H, v4.8B, v0.8B - umlal v16.8H, v5.8B, v1.8B + ld1 {v4.s}[0], [x1], x2 + ld1 {v4.s}[1], [x1], x2 + rev64 v5.2s, v4.2s + ld1 {v5.s}[1], [x1] + ext v6.8b, v4.8b, v5.8b, #1 + ext v7.8b, v5.8b, v4.8b, #1 + trn1 v4.4h, v4.4h, v6.4h + trn1 v5.4h, v5.4h, v7.4h + umull v16.8h, v4.8b, v0.8b + umlal v16.8h, v5.8b, v1.8b .ifc \type,avg - ld1 {v18.H}[0], [x0], x2 - ld1 {v18.H}[2], [x0] + ld1 {v18.h}[0], [x0], x2 + ld1 {v18.h}[2], [x0] sub x0, x0, x2 .endif - rev64 v17.4S, v16.4S - add v16.8H, v16.8H, v17.8H - rshrn v16.8B, v16.8H, #6 + rev64 v17.4s, v16.4s + add v16.8h, v16.8h, v17.8h + rshrn v16.8b, v16.8h, #6 .ifc \type,avg - urhadd v16.8B, v16.8B, v18.8B + urhadd v16.8b, v16.8b, v18.8b .endif - st1 {v16.H}[0], [x0], x2 - st1 {v16.H}[2], [x0], x2 + st1 {v16.h}[0], [x0], x2 + st1 {v16.h}[2], [x0], x2 subs w3, w3, #2 b.gt 1b ret 2: - ld1 {v16.H}[0], [x1], x2 - ld1 {v16.H}[1], [x1], x2 + ld1 {v16.h}[0], [x1], x2 + ld1 {v16.h}[1], [x1], x2 .ifc \type,avg - ld1 {v18.H}[0], [x0], x2 - ld1 {v18.H}[1], [x0] + ld1 {v18.h}[0], [x0], x2 + ld1 {v18.h}[1], [x0] sub x0, x0, x2 - urhadd v16.8B, v16.8B, v18.8B + urhadd v16.8b, v16.8b, v18.8b .endif - st1 {v16.H}[0], [x0], x2 - st1 {v16.H}[1], [x0], x2 + st1 {v16.h}[0], [x0], x2 + st1 {v16.h}[1], [x0], x2 subs w3, w3, #2 b.gt 2b ret diff --git a/libavcodec/aarch64/h264dsp_neon.S b/libavcodec/aarch64/h264dsp_neon.S index ea221e6862e..723b6920199 100644 --- a/libavcodec/aarch64/h264dsp_neon.S +++ b/libavcodec/aarch64/h264dsp_neon.S @@ -27,7 +27,7 @@ cmp w2, #0 ldr w6, [x4] ccmp w3, #0, #0, ne - mov v24.S[0], w6 + mov v24.s[0], w6 and w8, w6, w6, lsl #16 b.eq 1f ands w8, w8, w8, lsl #8 @@ -38,95 +38,95 @@ .endm .macro h264_loop_filter_luma - dup v22.16B, w2 // alpha - uxtl v24.8H, v24.8B - uabd v21.16B, v16.16B, v0.16B // abs(p0 - q0) - uxtl v24.4S, v24.4H - uabd v28.16B, v18.16B, v16.16B // abs(p1 - p0) - sli v24.8H, v24.8H, #8 - uabd v30.16B, v2.16B, v0.16B // abs(q1 - q0) - sli v24.4S, v24.4S, #16 - cmhi v21.16B, v22.16B, v21.16B // < alpha - dup v22.16B, w3 // beta - cmlt v23.16B, v24.16B, #0 - cmhi v28.16B, v22.16B, v28.16B // < beta - cmhi v30.16B, v22.16B, v30.16B // < beta - bic v21.16B, v21.16B, v23.16B - uabd v17.16B, v20.16B, v16.16B // abs(p2 - p0) - and v21.16B, v21.16B, v28.16B - uabd v19.16B, v4.16B, v0.16B // abs(q2 - q0) - and v21.16B, v21.16B, v30.16B // < beta + dup v22.16b, w2 // alpha + uxtl v24.8h, v24.8b + uabd v21.16b, v16.16b, v0.16b // abs(p0 - q0) + uxtl v24.4s, v24.4h + uabd v28.16b, v18.16b, v16.16b // abs(p1 - p0) + sli v24.8h, v24.8h, #8 + uabd v30.16b, v2.16b, v0.16b // abs(q1 - q0) + sli v24.4s, v24.4s, #16 + cmhi v21.16b, v22.16b, v21.16b // < alpha + dup v22.16b, w3 // beta + cmlt v23.16b, v24.16b, #0 + cmhi v28.16b, v22.16b, v28.16b // < beta + cmhi v30.16b, v22.16b, v30.16b // < beta + bic v21.16b, v21.16b, v23.16b + uabd v17.16b, v20.16b, v16.16b // abs(p2 - p0) + and v21.16b, v21.16b, v28.16b + uabd v19.16b, v4.16b, v0.16b // abs(q2 - q0) + and v21.16b, v21.16b, v30.16b // < beta shrn v30.8b, v21.8h, #4 mov x7, v30.d[0] - cmhi v17.16B, v22.16B, v17.16B // < beta - cmhi v19.16B, v22.16B, v19.16B // < beta + cmhi v17.16b, v22.16b, v17.16b // < beta + cmhi v19.16b, v22.16b, v19.16b // < beta cbz x7, 9f - and v17.16B, v17.16B, v21.16B - and v19.16B, v19.16B, v21.16B - and v24.16B, v24.16B, v21.16B - urhadd v28.16B, v16.16B, v0.16B - sub v21.16B, v24.16B, v17.16B - uqadd v23.16B, v18.16B, v24.16B - uhadd v20.16B, v20.16B, v28.16B - sub v21.16B, v21.16B, v19.16B - uhadd v28.16B, v4.16B, v28.16B - umin v23.16B, v23.16B, v20.16B - uqsub v22.16B, v18.16B, v24.16B - uqadd v4.16B, v2.16B, v24.16B - umax v23.16B, v23.16B, v22.16B - uqsub v22.16B, v2.16B, v24.16B - umin v28.16B, v4.16B, v28.16B - uxtl v4.8H, v0.8B - umax v28.16B, v28.16B, v22.16B - uxtl2 v20.8H, v0.16B - usubw v4.8H, v4.8H, v16.8B - usubw2 v20.8H, v20.8H, v16.16B - shl v4.8H, v4.8H, #2 - shl v20.8H, v20.8H, #2 - uaddw v4.8H, v4.8H, v18.8B - uaddw2 v20.8H, v20.8H, v18.16B - usubw v4.8H, v4.8H, v2.8B - usubw2 v20.8H, v20.8H, v2.16B - rshrn v4.8B, v4.8H, #3 - rshrn2 v4.16B, v20.8H, #3 - bsl v17.16B, v23.16B, v18.16B - bsl v19.16B, v28.16B, v2.16B - neg v23.16B, v21.16B - uxtl v28.8H, v16.8B - smin v4.16B, v4.16B, v21.16B - uxtl2 v21.8H, v16.16B - smax v4.16B, v4.16B, v23.16B - uxtl v22.8H, v0.8B - uxtl2 v24.8H, v0.16B - saddw v28.8H, v28.8H, v4.8B - saddw2 v21.8H, v21.8H, v4.16B - ssubw v22.8H, v22.8H, v4.8B - ssubw2 v24.8H, v24.8H, v4.16B - sqxtun v16.8B, v28.8H - sqxtun2 v16.16B, v21.8H - sqxtun v0.8B, v22.8H - sqxtun2 v0.16B, v24.8H + and v17.16b, v17.16b, v21.16b + and v19.16b, v19.16b, v21.16b + and v24.16b, v24.16b, v21.16b + urhadd v28.16b, v16.16b, v0.16b + sub v21.16b, v24.16b, v17.16b + uqadd v23.16b, v18.16b, v24.16b + uhadd v20.16b, v20.16b, v28.16b + sub v21.16b, v21.16b, v19.16b + uhadd v28.16b, v4.16b, v28.16b + umin v23.16b, v23.16b, v20.16b + uqsub v22.16b, v18.16b, v24.16b + uqadd v4.16b, v2.16b, v24.16b + umax v23.16b, v23.16b, v22.16b + uqsub v22.16b, v2.16b, v24.16b + umin v28.16b, v4.16b, v28.16b + uxtl v4.8h, v0.8b + umax v28.16b, v28.16b, v22.16b + uxtl2 v20.8h, v0.16b + usubw v4.8h, v4.8h, v16.8b + usubw2 v20.8h, v20.8h, v16.16b + shl v4.8h, v4.8h, #2 + shl v20.8h, v20.8h, #2 + uaddw v4.8h, v4.8h, v18.8b + uaddw2 v20.8h, v20.8h, v18.16b + usubw v4.8h, v4.8h, v2.8b + usubw2 v20.8h, v20.8h, v2.16b + rshrn v4.8b, v4.8h, #3 + rshrn2 v4.16b, v20.8h, #3 + bsl v17.16b, v23.16b, v18.16b + bsl v19.16b, v28.16b, v2.16b + neg v23.16b, v21.16b + uxtl v28.8h, v16.8b + smin v4.16b, v4.16b, v21.16b + uxtl2 v21.8h, v16.16b + smax v4.16b, v4.16b, v23.16b + uxtl v22.8h, v0.8b + uxtl2 v24.8h, v0.16b + saddw v28.8h, v28.8h, v4.8b + saddw2 v21.8h, v21.8h, v4.16b + ssubw v22.8h, v22.8h, v4.8b + ssubw2 v24.8h, v24.8h, v4.16b + sqxtun v16.8b, v28.8h + sqxtun2 v16.16b, v21.8h + sqxtun v0.8b, v22.8h + sqxtun2 v0.16b, v24.8h .endm function ff_h264_v_loop_filter_luma_neon, export=1 h264_loop_filter_start - ld1 {v0.16B}, [x0], x1 - ld1 {v2.16B}, [x0], x1 - ld1 {v4.16B}, [x0], x1 + ld1 {v0.16b}, [x0], x1 + ld1 {v2.16b}, [x0], x1 + ld1 {v4.16b}, [x0], x1 sub x0, x0, x1, lsl #2 sub x0, x0, x1, lsl #1 - ld1 {v20.16B}, [x0], x1 - ld1 {v18.16B}, [x0], x1 - ld1 {v16.16B}, [x0], x1 + ld1 {v20.16b}, [x0], x1 + ld1 {v18.16b}, [x0], x1 + ld1 {v16.16b}, [x0], x1 h264_loop_filter_luma sub x0, x0, x1, lsl #1 - st1 {v17.16B}, [x0], x1 - st1 {v16.16B}, [x0], x1 - st1 {v0.16B}, [x0], x1 - st1 {v19.16B}, [x0] + st1 {v17.16b}, [x0], x1 + st1 {v16.16b}, [x0], x1 + st1 {v0.16b}, [x0], x1 + st1 {v19.16b}, [x0] 9: ret endfunc @@ -135,22 +135,22 @@ function ff_h264_h_loop_filter_luma_neon, export=1 h264_loop_filter_start sub x0, x0, #4 - ld1 {v6.8B}, [x0], x1 - ld1 {v20.8B}, [x0], x1 - ld1 {v18.8B}, [x0], x1 - ld1 {v16.8B}, [x0], x1 - ld1 {v0.8B}, [x0], x1 - ld1 {v2.8B}, [x0], x1 - ld1 {v4.8B}, [x0], x1 - ld1 {v26.8B}, [x0], x1 - ld1 {v6.D}[1], [x0], x1 - ld1 {v20.D}[1], [x0], x1 - ld1 {v18.D}[1], [x0], x1 - ld1 {v16.D}[1], [x0], x1 - ld1 {v0.D}[1], [x0], x1 - ld1 {v2.D}[1], [x0], x1 - ld1 {v4.D}[1], [x0], x1 - ld1 {v26.D}[1], [x0], x1 + ld1 {v6.8b}, [x0], x1 + ld1 {v20.8b}, [x0], x1 + ld1 {v18.8b}, [x0], x1 + ld1 {v16.8b}, [x0], x1 + ld1 {v0.8b}, [x0], x1 + ld1 {v2.8b}, [x0], x1 + ld1 {v4.8b}, [x0], x1 + ld1 {v26.8b}, [x0], x1 + ld1 {v6.d}[1], [x0], x1 + ld1 {v20.d}[1], [x0], x1 + ld1 {v18.d}[1], [x0], x1 + ld1 {v16.d}[1], [x0], x1 + ld1 {v0.d}[1], [x0], x1 + ld1 {v2.d}[1], [x0], x1 + ld1 {v4.d}[1], [x0], x1 + ld1 {v26.d}[1], [x0], x1 transpose_8x16B v6, v20, v18, v16, v0, v2, v4, v26, v21, v23 @@ -160,22 +160,22 @@ function ff_h264_h_loop_filter_luma_neon, export=1 sub x0, x0, x1, lsl #4 add x0, x0, #2 - st1 {v17.S}[0], [x0], x1 - st1 {v16.S}[0], [x0], x1 - st1 {v0.S}[0], [x0], x1 - st1 {v19.S}[0], [x0], x1 - st1 {v17.S}[1], [x0], x1 - st1 {v16.S}[1], [x0], x1 - st1 {v0.S}[1], [x0], x1 - st1 {v19.S}[1], [x0], x1 - st1 {v17.S}[2], [x0], x1 - st1 {v16.S}[2], [x0], x1 - st1 {v0.S}[2], [x0], x1 - st1 {v19.S}[2], [x0], x1 - st1 {v17.S}[3], [x0], x1 - st1 {v16.S}[3], [x0], x1 - st1 {v0.S}[3], [x0], x1 - st1 {v19.S}[3], [x0], x1 + st1 {v17.s}[0], [x0], x1 + st1 {v16.s}[0], [x0], x1 + st1 {v0.s}[0], [x0], x1 + st1 {v19.s}[0], [x0], x1 + st1 {v17.s}[1], [x0], x1 + st1 {v16.s}[1], [x0], x1 + st1 {v0.s}[1], [x0], x1 + st1 {v19.s}[1], [x0], x1 + st1 {v17.s}[2], [x0], x1 + st1 {v16.s}[2], [x0], x1 + st1 {v0.s}[2], [x0], x1 + st1 {v19.s}[2], [x0], x1 + st1 {v17.s}[3], [x0], x1 + st1 {v16.s}[3], [x0], x1 + st1 {v0.s}[3], [x0], x1 + st1 {v19.s}[3], [x0], x1 9: ret endfunc @@ -377,52 +377,52 @@ function ff_h264_h_loop_filter_luma_intra_neon, export=1 endfunc .macro h264_loop_filter_chroma - dup v22.8B, w2 // alpha - dup v23.8B, w3 // beta - uxtl v24.8H, v24.8B - uabd v26.8B, v16.8B, v0.8B // abs(p0 - q0) - uabd v28.8B, v18.8B, v16.8B // abs(p1 - p0) - uabd v30.8B, v2.8B, v0.8B // abs(q1 - q0) - cmhi v26.8B, v22.8B, v26.8B // < alpha - cmhi v28.8B, v23.8B, v28.8B // < beta - cmhi v30.8B, v23.8B, v30.8B // < beta - uxtl v4.8H, v0.8B - and v26.8B, v26.8B, v28.8B - usubw v4.8H, v4.8H, v16.8B - and v26.8B, v26.8B, v30.8B - shl v4.8H, v4.8H, #2 + dup v22.8b, w2 // alpha + dup v23.8b, w3 // beta + uxtl v24.8h, v24.8b + uabd v26.8b, v16.8b, v0.8b // abs(p0 - q0) + uabd v28.8b, v18.8b, v16.8b // abs(p1 - p0) + uabd v30.8b, v2.8b, v0.8b // abs(q1 - q0) + cmhi v26.8b, v22.8b, v26.8b // < alpha + cmhi v28.8b, v23.8b, v28.8b // < beta + cmhi v30.8b, v23.8b, v30.8b // < beta + uxtl v4.8h, v0.8b + and v26.8b, v26.8b, v28.8b + usubw v4.8h, v4.8h, v16.8b + and v26.8b, v26.8b, v30.8b + shl v4.8h, v4.8h, #2 mov x8, v26.d[0] - sli v24.8H, v24.8H, #8 - uaddw v4.8H, v4.8H, v18.8B + sli v24.8h, v24.8h, #8 + uaddw v4.8h, v4.8h, v18.8b cbz x8, 9f - usubw v4.8H, v4.8H, v2.8B - rshrn v4.8B, v4.8H, #3 - smin v4.8B, v4.8B, v24.8B - neg v25.8B, v24.8B - smax v4.8B, v4.8B, v25.8B - uxtl v22.8H, v0.8B - and v4.8B, v4.8B, v26.8B - uxtl v28.8H, v16.8B - saddw v28.8H, v28.8H, v4.8B - ssubw v22.8H, v22.8H, v4.8B - sqxtun v16.8B, v28.8H - sqxtun v0.8B, v22.8H + usubw v4.8h, v4.8h, v2.8b + rshrn v4.8b, v4.8h, #3 + smin v4.8b, v4.8b, v24.8b + neg v25.8b, v24.8b + smax v4.8b, v4.8b, v25.8b + uxtl v22.8h, v0.8b + and v4.8b, v4.8b, v26.8b + uxtl v28.8h, v16.8b + saddw v28.8h, v28.8h, v4.8b + ssubw v22.8h, v22.8h, v4.8b + sqxtun v16.8b, v28.8h + sqxtun v0.8b, v22.8h .endm function ff_h264_v_loop_filter_chroma_neon, export=1 h264_loop_filter_start sub x0, x0, x1, lsl #1 - ld1 {v18.8B}, [x0], x1 - ld1 {v16.8B}, [x0], x1 - ld1 {v0.8B}, [x0], x1 - ld1 {v2.8B}, [x0] + ld1 {v18.8b}, [x0], x1 + ld1 {v16.8b}, [x0], x1 + ld1 {v0.8b}, [x0], x1 + ld1 {v2.8b}, [x0] h264_loop_filter_chroma sub x0, x0, x1, lsl #1 - st1 {v16.8B}, [x0], x1 - st1 {v0.8B}, [x0], x1 + st1 {v16.8b}, [x0], x1 + st1 {v0.8b}, [x0], x1 9: ret endfunc @@ -432,14 +432,14 @@ function ff_h264_h_loop_filter_chroma_neon, export=1 sub x0, x0, #2 h_loop_filter_chroma420: - ld1 {v18.S}[0], [x0], x1 - ld1 {v16.S}[0], [x0], x1 - ld1 {v0.S}[0], [x0], x1 - ld1 {v2.S}[0], [x0], x1 - ld1 {v18.S}[1], [x0], x1 - ld1 {v16.S}[1], [x0], x1 - ld1 {v0.S}[1], [x0], x1 - ld1 {v2.S}[1], [x0], x1 + ld1 {v18.s}[0], [x0], x1 + ld1 {v16.s}[0], [x0], x1 + ld1 {v0.s}[0], [x0], x1 + ld1 {v2.s}[0], [x0], x1 + ld1 {v18.s}[1], [x0], x1 + ld1 {v16.s}[1], [x0], x1 + ld1 {v0.s}[1], [x0], x1 + ld1 {v2.s}[1], [x0], x1 transpose_4x8B v18, v16, v0, v2, v28, v29, v30, v31 @@ -448,14 +448,14 @@ h_loop_filter_chroma420: transpose_4x8B v18, v16, v0, v2, v28, v29, v30, v31 sub x0, x0, x1, lsl #3 - st1 {v18.S}[0], [x0], x1 - st1 {v16.S}[0], [x0], x1 - st1 {v0.S}[0], [x0], x1 - st1 {v2.S}[0], [x0], x1 - st1 {v18.S}[1], [x0], x1 - st1 {v16.S}[1], [x0], x1 - st1 {v0.S}[1], [x0], x1 - st1 {v2.S}[1], [x0], x1 + st1 {v18.s}[0], [x0], x1 + st1 {v16.s}[0], [x0], x1 + st1 {v0.s}[0], [x0], x1 + st1 {v2.s}[0], [x0], x1 + st1 {v18.s}[1], [x0], x1 + st1 {v16.s}[1], [x0], x1 + st1 {v0.s}[1], [x0], x1 + st1 {v2.s}[1], [x0], x1 9: ret endfunc @@ -526,7 +526,7 @@ function ff_h264_h_loop_filter_chroma_mbaff_intra_neon, export=1 ld1 {v17.8b}, [x4], x1 ld1 {v19.8b}, [x4], x1 - transpose_4x8B v18, v16, v17, v19, v26, v27, v28, v29 + transpose_4x8B v18, v16, v17, v19, v26, v27, v28, v29 h264_loop_filter_chroma_intra @@ -554,7 +554,7 @@ h_loop_filter_chroma420_intra: ld1 {v17.s}[1], [x4], x1 ld1 {v19.s}[1], [x4], x1 - transpose_4x8B v18, v16, v17, v19, v26, v27, v28, v29 + transpose_4x8B v18, v16, v17, v19, v26, v27, v28, v29 h264_loop_filter_chroma_intra @@ -584,102 +584,102 @@ function ff_h264_h_loop_filter_chroma422_intra_neon, export=1 endfunc .macro biweight_16 macs, macd - dup v0.16B, w5 - dup v1.16B, w6 - mov v4.16B, v16.16B - mov v6.16B, v16.16B + dup v0.16b, w5 + dup v1.16b, w6 + mov v4.16b, v16.16b + mov v6.16b, v16.16b 1: subs w3, w3, #2 - ld1 {v20.16B}, [x0], x2 - \macd v4.8H, v0.8B, v20.8B + ld1 {v20.16b}, [x0], x2 + \macd v4.8h, v0.8b, v20.8b \macd\()2 v6.8H, v0.16B, v20.16B - ld1 {v22.16B}, [x1], x2 - \macs v4.8H, v1.8B, v22.8B + ld1 {v22.16b}, [x1], x2 + \macs v4.8h, v1.8b, v22.8b \macs\()2 v6.8H, v1.16B, v22.16B - mov v24.16B, v16.16B - ld1 {v28.16B}, [x0], x2 - mov v26.16B, v16.16B - \macd v24.8H, v0.8B, v28.8B + mov v24.16b, v16.16b + ld1 {v28.16b}, [x0], x2 + mov v26.16b, v16.16b + \macd v24.8h, v0.8b, v28.8b \macd\()2 v26.8H, v0.16B, v28.16B - ld1 {v30.16B}, [x1], x2 - \macs v24.8H, v1.8B, v30.8B + ld1 {v30.16b}, [x1], x2 + \macs v24.8h, v1.8b, v30.8b \macs\()2 v26.8H, v1.16B, v30.16B - sshl v4.8H, v4.8H, v18.8H - sshl v6.8H, v6.8H, v18.8H - sqxtun v4.8B, v4.8H - sqxtun2 v4.16B, v6.8H - sshl v24.8H, v24.8H, v18.8H - sshl v26.8H, v26.8H, v18.8H - sqxtun v24.8B, v24.8H - sqxtun2 v24.16B, v26.8H - mov v6.16B, v16.16B - st1 {v4.16B}, [x7], x2 - mov v4.16B, v16.16B - st1 {v24.16B}, [x7], x2 + sshl v4.8h, v4.8h, v18.8h + sshl v6.8h, v6.8h, v18.8h + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v6.8h + sshl v24.8h, v24.8h, v18.8h + sshl v26.8h, v26.8h, v18.8h + sqxtun v24.8b, v24.8h + sqxtun2 v24.16b, v26.8h + mov v6.16b, v16.16b + st1 {v4.16b}, [x7], x2 + mov v4.16b, v16.16b + st1 {v24.16b}, [x7], x2 b.ne 1b ret .endm .macro biweight_8 macs, macd - dup v0.8B, w5 - dup v1.8B, w6 - mov v2.16B, v16.16B - mov v20.16B, v16.16B + dup v0.8b, w5 + dup v1.8b, w6 + mov v2.16b, v16.16b + mov v20.16b, v16.16b 1: subs w3, w3, #2 - ld1 {v4.8B}, [x0], x2 - \macd v2.8H, v0.8B, v4.8B - ld1 {v5.8B}, [x1], x2 - \macs v2.8H, v1.8B, v5.8B - ld1 {v6.8B}, [x0], x2 - \macd v20.8H, v0.8B, v6.8B - ld1 {v7.8B}, [x1], x2 - \macs v20.8H, v1.8B, v7.8B - sshl v2.8H, v2.8H, v18.8H - sqxtun v2.8B, v2.8H - sshl v20.8H, v20.8H, v18.8H - sqxtun v4.8B, v20.8H - mov v20.16B, v16.16B - st1 {v2.8B}, [x7], x2 - mov v2.16B, v16.16B - st1 {v4.8B}, [x7], x2 + ld1 {v4.8b}, [x0], x2 + \macd v2.8h, v0.8b, v4.8b + ld1 {v5.8b}, [x1], x2 + \macs v2.8h, v1.8b, v5.8b + ld1 {v6.8b}, [x0], x2 + \macd v20.8h, v0.8b, v6.8b + ld1 {v7.8b}, [x1], x2 + \macs v20.8h, v1.8b, v7.8b + sshl v2.8h, v2.8h, v18.8h + sqxtun v2.8b, v2.8h + sshl v20.8h, v20.8h, v18.8h + sqxtun v4.8b, v20.8h + mov v20.16b, v16.16b + st1 {v2.8b}, [x7], x2 + mov v2.16b, v16.16b + st1 {v4.8b}, [x7], x2 b.ne 1b ret .endm .macro biweight_4 macs, macd - dup v0.8B, w5 - dup v1.8B, w6 - mov v2.16B, v16.16B - mov v20.16B,v16.16B + dup v0.8b, w5 + dup v1.8b, w6 + mov v2.16b, v16.16b + mov v20.16b,v16.16b 1: subs w3, w3, #4 - ld1 {v4.S}[0], [x0], x2 - ld1 {v4.S}[1], [x0], x2 - \macd v2.8H, v0.8B, v4.8B - ld1 {v5.S}[0], [x1], x2 - ld1 {v5.S}[1], [x1], x2 - \macs v2.8H, v1.8B, v5.8B + ld1 {v4.s}[0], [x0], x2 + ld1 {v4.s}[1], [x0], x2 + \macd v2.8h, v0.8b, v4.8b + ld1 {v5.s}[0], [x1], x2 + ld1 {v5.s}[1], [x1], x2 + \macs v2.8h, v1.8b, v5.8b b.lt 2f - ld1 {v6.S}[0], [x0], x2 - ld1 {v6.S}[1], [x0], x2 - \macd v20.8H, v0.8B, v6.8B - ld1 {v7.S}[0], [x1], x2 - ld1 {v7.S}[1], [x1], x2 - \macs v20.8H, v1.8B, v7.8B - sshl v2.8H, v2.8H, v18.8H - sqxtun v2.8B, v2.8H - sshl v20.8H, v20.8H, v18.8H - sqxtun v4.8B, v20.8H - mov v20.16B, v16.16B - st1 {v2.S}[0], [x7], x2 - st1 {v2.S}[1], [x7], x2 - mov v2.16B, v16.16B - st1 {v4.S}[0], [x7], x2 - st1 {v4.S}[1], [x7], x2 + ld1 {v6.s}[0], [x0], x2 + ld1 {v6.s}[1], [x0], x2 + \macd v20.8h, v0.8b, v6.8b + ld1 {v7.s}[0], [x1], x2 + ld1 {v7.s}[1], [x1], x2 + \macs v20.8h, v1.8b, v7.8b + sshl v2.8h, v2.8h, v18.8h + sqxtun v2.8b, v2.8h + sshl v20.8h, v20.8h, v18.8h + sqxtun v4.8b, v20.8h + mov v20.16b, v16.16b + st1 {v2.s}[0], [x7], x2 + st1 {v2.s}[1], [x7], x2 + mov v2.16b, v16.16b + st1 {v4.s}[0], [x7], x2 + st1 {v4.s}[1], [x7], x2 b.ne 1b ret -2: sshl v2.8H, v2.8H, v18.8H - sqxtun v2.8B, v2.8H - st1 {v2.S}[0], [x7], x2 - st1 {v2.S}[1], [x7], x2 +2: sshl v2.8h, v2.8h, v18.8h + sqxtun v2.8b, v2.8h + st1 {v2.s}[0], [x7], x2 + st1 {v2.s}[1], [x7], x2 ret .endm @@ -689,10 +689,10 @@ function ff_biweight_h264_pixels_\w\()_neon, export=1 add w7, w7, #1 eor w8, w8, w6, lsr #30 orr w7, w7, #1 - dup v18.8H, w4 + dup v18.8h, w4 lsl w7, w7, w4 - not v18.16B, v18.16B - dup v16.8H, w7 + not v18.16b, v18.16b + dup v16.8h, w7 mov x7, x0 cbz w8, 10f subs w8, w8, #1 @@ -716,78 +716,78 @@ endfunc biweight_func 4 .macro weight_16 add - dup v0.16B, w4 + dup v0.16b, w4 1: subs w2, w2, #2 - ld1 {v20.16B}, [x0], x1 - umull v4.8H, v0.8B, v20.8B - umull2 v6.8H, v0.16B, v20.16B - ld1 {v28.16B}, [x0], x1 - umull v24.8H, v0.8B, v28.8B - umull2 v26.8H, v0.16B, v28.16B - \add v4.8H, v16.8H, v4.8H - srshl v4.8H, v4.8H, v18.8H - \add v6.8H, v16.8H, v6.8H - srshl v6.8H, v6.8H, v18.8H - sqxtun v4.8B, v4.8H - sqxtun2 v4.16B, v6.8H - \add v24.8H, v16.8H, v24.8H - srshl v24.8H, v24.8H, v18.8H - \add v26.8H, v16.8H, v26.8H - srshl v26.8H, v26.8H, v18.8H - sqxtun v24.8B, v24.8H - sqxtun2 v24.16B, v26.8H - st1 {v4.16B}, [x5], x1 - st1 {v24.16B}, [x5], x1 + ld1 {v20.16b}, [x0], x1 + umull v4.8h, v0.8b, v20.8b + umull2 v6.8h, v0.16b, v20.16b + ld1 {v28.16b}, [x0], x1 + umull v24.8h, v0.8b, v28.8b + umull2 v26.8h, v0.16b, v28.16b + \add v4.8h, v16.8h, v4.8h + srshl v4.8h, v4.8h, v18.8h + \add v6.8h, v16.8h, v6.8h + srshl v6.8h, v6.8h, v18.8h + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v6.8h + \add v24.8h, v16.8h, v24.8h + srshl v24.8h, v24.8h, v18.8h + \add v26.8h, v16.8h, v26.8h + srshl v26.8h, v26.8h, v18.8h + sqxtun v24.8b, v24.8h + sqxtun2 v24.16b, v26.8h + st1 {v4.16b}, [x5], x1 + st1 {v24.16b}, [x5], x1 b.ne 1b ret .endm .macro weight_8 add - dup v0.8B, w4 + dup v0.8b, w4 1: subs w2, w2, #2 - ld1 {v4.8B}, [x0], x1 - umull v2.8H, v0.8B, v4.8B - ld1 {v6.8B}, [x0], x1 - umull v20.8H, v0.8B, v6.8B - \add v2.8H, v16.8H, v2.8H - srshl v2.8H, v2.8H, v18.8H - sqxtun v2.8B, v2.8H - \add v20.8H, v16.8H, v20.8H - srshl v20.8H, v20.8H, v18.8H - sqxtun v4.8B, v20.8H - st1 {v2.8B}, [x5], x1 - st1 {v4.8B}, [x5], x1 + ld1 {v4.8b}, [x0], x1 + umull v2.8h, v0.8b, v4.8b + ld1 {v6.8b}, [x0], x1 + umull v20.8h, v0.8b, v6.8b + \add v2.8h, v16.8h, v2.8h + srshl v2.8h, v2.8h, v18.8h + sqxtun v2.8b, v2.8h + \add v20.8h, v16.8h, v20.8h + srshl v20.8h, v20.8h, v18.8h + sqxtun v4.8b, v20.8h + st1 {v2.8b}, [x5], x1 + st1 {v4.8b}, [x5], x1 b.ne 1b ret .endm .macro weight_4 add - dup v0.8B, w4 + dup v0.8b, w4 1: subs w2, w2, #4 - ld1 {v4.S}[0], [x0], x1 - ld1 {v4.S}[1], [x0], x1 - umull v2.8H, v0.8B, v4.8B + ld1 {v4.s}[0], [x0], x1 + ld1 {v4.s}[1], [x0], x1 + umull v2.8h, v0.8b, v4.8b b.lt 2f - ld1 {v6.S}[0], [x0], x1 - ld1 {v6.S}[1], [x0], x1 - umull v20.8H, v0.8B, v6.8B - \add v2.8H, v16.8H, v2.8H - srshl v2.8H, v2.8H, v18.8H - sqxtun v2.8B, v2.8H - \add v20.8H, v16.8H, v20.8H - srshl v20.8H, v20.8h, v18.8H - sqxtun v4.8B, v20.8H - st1 {v2.S}[0], [x5], x1 - st1 {v2.S}[1], [x5], x1 - st1 {v4.S}[0], [x5], x1 - st1 {v4.S}[1], [x5], x1 + ld1 {v6.s}[0], [x0], x1 + ld1 {v6.s}[1], [x0], x1 + umull v20.8h, v0.8b, v6.8b + \add v2.8h, v16.8h, v2.8h + srshl v2.8h, v2.8h, v18.8h + sqxtun v2.8b, v2.8h + \add v20.8h, v16.8h, v20.8h + srshl v20.8h, v20.8h, v18.8h + sqxtun v4.8b, v20.8h + st1 {v2.s}[0], [x5], x1 + st1 {v2.s}[1], [x5], x1 + st1 {v4.s}[0], [x5], x1 + st1 {v4.s}[1], [x5], x1 b.ne 1b ret -2: \add v2.8H, v16.8H, v2.8H - srshl v2.8H, v2.8H, v18.8H - sqxtun v2.8B, v2.8H - st1 {v2.S}[0], [x5], x1 - st1 {v2.S}[1], [x5], x1 +2: \add v2.8h, v16.8h, v2.8h + srshl v2.8h, v2.8h, v18.8h + sqxtun v2.8b, v2.8h + st1 {v2.s}[0], [x5], x1 + st1 {v2.s}[1], [x5], x1 ret .endm @@ -796,18 +796,18 @@ function ff_weight_h264_pixels_\w\()_neon, export=1 cmp w3, #1 mov w6, #1 lsl w5, w5, w3 - dup v16.8H, w5 + dup v16.8h, w5 mov x5, x0 b.le 20f sub w6, w6, w3 - dup v18.8H, w6 + dup v18.8h, w6 cmp w4, #0 b.lt 10f weight_\w shadd 10: neg w4, w4 weight_\w shsub 20: neg w6, w3 - dup v18.8H, w6 + dup v18.8h, w6 cmp w4, #0 b.lt 10f weight_\w add @@ -825,7 +825,7 @@ endfunc ldr w6, [x4] ccmp w3, #0, #0, ne lsl w2, w2, #2 - mov v24.S[0], w6 + mov v24.s[0], w6 lsl w3, w3, #2 and w8, w6, w6, lsl #16 b.eq 1f @@ -1017,7 +1017,7 @@ function ff_h264_h_loop_filter_chroma_mbaff_intra_neon_10, export=1 ld1 {v16.8h}, [x4], x1 ld1 {v19.8h}, [x9], x1 - transpose_4x8H v18, v16, v17, v19, v26, v27, v28, v29 + transpose_4x8H v18, v16, v17, v19, v26, v27, v28, v29 h264_loop_filter_chroma_intra_10 @@ -1045,7 +1045,7 @@ h_loop_filter_chroma420_intra_10: ld1 {v19.4h}, [x4], x1 ld1 {v19.d}[1], [x9], x1 - transpose_4x8H v18, v16, v17, v19, v26, v27, v28, v29 + transpose_4x8H v18, v16, v17, v19, v26, v27, v28, v29 h264_loop_filter_chroma_intra_10 diff --git a/libavcodec/aarch64/h264idct_neon.S b/libavcodec/aarch64/h264idct_neon.S index 375da31d655..3f7ff2c49e3 100644 --- a/libavcodec/aarch64/h264idct_neon.S +++ b/libavcodec/aarch64/h264idct_neon.S @@ -25,54 +25,54 @@ function ff_h264_idct_add_neon, export=1 .L_ff_h264_idct_add_neon: AARCH64_VALID_CALL_TARGET - ld1 {v0.4H, v1.4H, v2.4H, v3.4H}, [x1] + ld1 {v0.4h, v1.4h, v2.4h, v3.4h}, [x1] sxtw x2, w2 - movi v30.8H, #0 + movi v30.8h, #0 - add v4.4H, v0.4H, v2.4H - sshr v16.4H, v1.4H, #1 - st1 {v30.8H}, [x1], #16 - sshr v17.4H, v3.4H, #1 - st1 {v30.8H}, [x1], #16 - sub v5.4H, v0.4H, v2.4H - sub v6.4H, v16.4H, v3.4H - add v7.4H, v1.4H, v17.4H - add v0.4H, v4.4H, v7.4H - add v1.4H, v5.4H, v6.4H - sub v2.4H, v5.4H, v6.4H - sub v3.4H, v4.4H, v7.4H + add v4.4h, v0.4h, v2.4h + sshr v16.4h, v1.4h, #1 + st1 {v30.8h}, [x1], #16 + sshr v17.4h, v3.4h, #1 + st1 {v30.8h}, [x1], #16 + sub v5.4h, v0.4h, v2.4h + sub v6.4h, v16.4h, v3.4h + add v7.4h, v1.4h, v17.4h + add v0.4h, v4.4h, v7.4h + add v1.4h, v5.4h, v6.4h + sub v2.4h, v5.4h, v6.4h + sub v3.4h, v4.4h, v7.4h transpose_4x4H v0, v1, v2, v3, v4, v5, v6, v7 - add v4.4H, v0.4H, v2.4H - ld1 {v18.S}[0], [x0], x2 - sshr v16.4H, v3.4H, #1 - sshr v17.4H, v1.4H, #1 - ld1 {v18.S}[1], [x0], x2 - sub v5.4H, v0.4H, v2.4H - ld1 {v19.S}[1], [x0], x2 - add v6.4H, v16.4H, v1.4H - ins v4.D[1], v5.D[0] - sub v7.4H, v17.4H, v3.4H - ld1 {v19.S}[0], [x0], x2 - ins v6.D[1], v7.D[0] + add v4.4h, v0.4h, v2.4h + ld1 {v18.s}[0], [x0], x2 + sshr v16.4h, v3.4h, #1 + sshr v17.4h, v1.4h, #1 + ld1 {v18.s}[1], [x0], x2 + sub v5.4h, v0.4h, v2.4h + ld1 {v19.s}[1], [x0], x2 + add v6.4h, v16.4h, v1.4h + ins v4.d[1], v5.d[0] + sub v7.4h, v17.4h, v3.4h + ld1 {v19.s}[0], [x0], x2 + ins v6.d[1], v7.d[0] sub x0, x0, x2, lsl #2 - add v0.8H, v4.8H, v6.8H - sub v1.8H, v4.8H, v6.8H + add v0.8h, v4.8h, v6.8h + sub v1.8h, v4.8h, v6.8h - srshr v0.8H, v0.8H, #6 - srshr v1.8H, v1.8H, #6 + srshr v0.8h, v0.8h, #6 + srshr v1.8h, v1.8h, #6 - uaddw v0.8H, v0.8H, v18.8B - uaddw v1.8H, v1.8H, v19.8B + uaddw v0.8h, v0.8h, v18.8b + uaddw v1.8h, v1.8h, v19.8b - sqxtun v0.8B, v0.8H - sqxtun v1.8B, v1.8H + sqxtun v0.8b, v0.8h + sqxtun v1.8b, v1.8h - st1 {v0.S}[0], [x0], x2 - st1 {v0.S}[1], [x0], x2 - st1 {v1.S}[1], [x0], x2 - st1 {v1.S}[0], [x0], x2 + st1 {v0.s}[0], [x0], x2 + st1 {v0.s}[1], [x0], x2 + st1 {v1.s}[1], [x0], x2 + st1 {v1.s}[0], [x0], x2 sub x1, x1, #32 ret @@ -83,22 +83,22 @@ function ff_h264_idct_dc_add_neon, export=1 AARCH64_VALID_CALL_TARGET sxtw x2, w2 mov w3, #0 - ld1r {v2.8H}, [x1] + ld1r {v2.8h}, [x1] strh w3, [x1] - srshr v2.8H, v2.8H, #6 - ld1 {v0.S}[0], [x0], x2 - ld1 {v0.S}[1], [x0], x2 - uaddw v3.8H, v2.8H, v0.8B - ld1 {v1.S}[0], [x0], x2 - ld1 {v1.S}[1], [x0], x2 - uaddw v4.8H, v2.8H, v1.8B - sqxtun v0.8B, v3.8H - sqxtun v1.8B, v4.8H + srshr v2.8h, v2.8h, #6 + ld1 {v0.s}[0], [x0], x2 + ld1 {v0.s}[1], [x0], x2 + uaddw v3.8h, v2.8h, v0.8b + ld1 {v1.s}[0], [x0], x2 + ld1 {v1.s}[1], [x0], x2 + uaddw v4.8h, v2.8h, v1.8b + sqxtun v0.8b, v3.8h + sqxtun v1.8b, v4.8h sub x0, x0, x2, lsl #2 - st1 {v0.S}[0], [x0], x2 - st1 {v0.S}[1], [x0], x2 - st1 {v1.S}[0], [x0], x2 - st1 {v1.S}[1], [x0], x2 + st1 {v0.s}[0], [x0], x2 + st1 {v0.s}[1], [x0], x2 + st1 {v1.s}[0], [x0], x2 + st1 {v1.s}[1], [x0], x2 ret endfunc @@ -194,71 +194,71 @@ endfunc .if \pass == 0 va .req v18 vb .req v30 - sshr v18.8H, v26.8H, #1 - add v16.8H, v24.8H, v28.8H - ld1 {v30.8H, v31.8H}, [x1] - st1 {v19.8H}, [x1], #16 - st1 {v19.8H}, [x1], #16 - sub v17.8H, v24.8H, v28.8H - sshr v19.8H, v30.8H, #1 - sub v18.8H, v18.8H, v30.8H - add v19.8H, v19.8H, v26.8H + sshr v18.8h, v26.8h, #1 + add v16.8h, v24.8h, v28.8h + ld1 {v30.8h, v31.8h}, [x1] + st1 {v19.8h}, [x1], #16 + st1 {v19.8h}, [x1], #16 + sub v17.8h, v24.8h, v28.8h + sshr v19.8h, v30.8h, #1 + sub v18.8h, v18.8h, v30.8h + add v19.8h, v19.8h, v26.8h .else va .req v30 vb .req v18 - sshr v30.8H, v26.8H, #1 - sshr v19.8H, v18.8H, #1 - add v16.8H, v24.8H, v28.8H - sub v17.8H, v24.8H, v28.8H - sub v30.8H, v30.8H, v18.8H - add v19.8H, v19.8H, v26.8H + sshr v30.8h, v26.8h, #1 + sshr v19.8h, v18.8h, #1 + add v16.8h, v24.8h, v28.8h + sub v17.8h, v24.8h, v28.8h + sub v30.8h, v30.8h, v18.8h + add v19.8h, v19.8h, v26.8h .endif - add v26.8H, v17.8H, va.8H - sub v28.8H, v17.8H, va.8H - add v24.8H, v16.8H, v19.8H - sub vb.8H, v16.8H, v19.8H - sub v16.8H, v29.8H, v27.8H - add v17.8H, v31.8H, v25.8H - sub va.8H, v31.8H, v25.8H - add v19.8H, v29.8H, v27.8H - sub v16.8H, v16.8H, v31.8H - sub v17.8H, v17.8H, v27.8H - add va.8H, va.8H, v29.8H - add v19.8H, v19.8H, v25.8H - sshr v25.8H, v25.8H, #1 - sshr v27.8H, v27.8H, #1 - sshr v29.8H, v29.8H, #1 - sshr v31.8H, v31.8H, #1 - sub v16.8H, v16.8H, v31.8H - sub v17.8H, v17.8H, v27.8H - add va.8H, va.8H, v29.8H - add v19.8H, v19.8H, v25.8H - sshr v25.8H, v16.8H, #2 - sshr v27.8H, v17.8H, #2 - sshr v29.8H, va.8H, #2 - sshr v31.8H, v19.8H, #2 - sub v19.8H, v19.8H, v25.8H - sub va.8H, v27.8H, va.8H - add v17.8H, v17.8H, v29.8H - add v16.8H, v16.8H, v31.8H + add v26.8h, v17.8h, va.8h + sub v28.8h, v17.8h, va.8h + add v24.8h, v16.8h, v19.8h + sub vb.8h, v16.8h, v19.8h + sub v16.8h, v29.8h, v27.8h + add v17.8h, v31.8h, v25.8h + sub va.8h, v31.8h, v25.8h + add v19.8h, v29.8h, v27.8h + sub v16.8h, v16.8h, v31.8h + sub v17.8h, v17.8h, v27.8h + add va.8h, va.8h, v29.8h + add v19.8h, v19.8h, v25.8h + sshr v25.8h, v25.8h, #1 + sshr v27.8h, v27.8h, #1 + sshr v29.8h, v29.8h, #1 + sshr v31.8h, v31.8h, #1 + sub v16.8h, v16.8h, v31.8h + sub v17.8h, v17.8h, v27.8h + add va.8h, va.8h, v29.8h + add v19.8h, v19.8h, v25.8h + sshr v25.8h, v16.8h, #2 + sshr v27.8h, v17.8h, #2 + sshr v29.8h, va.8h, #2 + sshr v31.8h, v19.8h, #2 + sub v19.8h, v19.8h, v25.8h + sub va.8h, v27.8h, va.8h + add v17.8h, v17.8h, v29.8h + add v16.8h, v16.8h, v31.8h .if \pass == 0 - sub v31.8H, v24.8H, v19.8H - add v24.8H, v24.8H, v19.8H - add v25.8H, v26.8H, v18.8H - sub v18.8H, v26.8H, v18.8H - add v26.8H, v28.8H, v17.8H - add v27.8H, v30.8H, v16.8H - sub v29.8H, v28.8H, v17.8H - sub v28.8H, v30.8H, v16.8H + sub v31.8h, v24.8h, v19.8h + add v24.8h, v24.8h, v19.8h + add v25.8h, v26.8h, v18.8h + sub v18.8h, v26.8h, v18.8h + add v26.8h, v28.8h, v17.8h + add v27.8h, v30.8h, v16.8h + sub v29.8h, v28.8h, v17.8h + sub v28.8h, v30.8h, v16.8h .else - sub v31.8H, v24.8H, v19.8H - add v24.8H, v24.8H, v19.8H - add v25.8H, v26.8H, v30.8H - sub v30.8H, v26.8H, v30.8H - add v26.8H, v28.8H, v17.8H - sub v29.8H, v28.8H, v17.8H - add v27.8H, v18.8H, v16.8H - sub v28.8H, v18.8H, v16.8H + sub v31.8h, v24.8h, v19.8h + add v24.8h, v24.8h, v19.8h + add v25.8h, v26.8h, v30.8h + sub v30.8h, v26.8h, v30.8h + add v26.8h, v28.8h, v17.8h + sub v29.8h, v28.8h, v17.8h + add v27.8h, v18.8h, v16.8h + sub v28.8h, v18.8h, v16.8h .endif .unreq va .unreq vb @@ -267,63 +267,63 @@ endfunc function ff_h264_idct8_add_neon, export=1 .L_ff_h264_idct8_add_neon: AARCH64_VALID_CALL_TARGET - movi v19.8H, #0 + movi v19.8h, #0 sxtw x2, w2 - ld1 {v24.8H, v25.8H}, [x1] - st1 {v19.8H}, [x1], #16 - st1 {v19.8H}, [x1], #16 - ld1 {v26.8H, v27.8H}, [x1] - st1 {v19.8H}, [x1], #16 - st1 {v19.8H}, [x1], #16 - ld1 {v28.8H, v29.8H}, [x1] - st1 {v19.8H}, [x1], #16 - st1 {v19.8H}, [x1], #16 + ld1 {v24.8h, v25.8h}, [x1] + st1 {v19.8h}, [x1], #16 + st1 {v19.8h}, [x1], #16 + ld1 {v26.8h, v27.8h}, [x1] + st1 {v19.8h}, [x1], #16 + st1 {v19.8h}, [x1], #16 + ld1 {v28.8h, v29.8h}, [x1] + st1 {v19.8h}, [x1], #16 + st1 {v19.8h}, [x1], #16 idct8x8_cols 0 transpose_8x8H v24, v25, v26, v27, v28, v29, v18, v31, v6, v7 idct8x8_cols 1 mov x3, x0 - srshr v24.8H, v24.8H, #6 - ld1 {v0.8B}, [x0], x2 - srshr v25.8H, v25.8H, #6 - ld1 {v1.8B}, [x0], x2 - srshr v26.8H, v26.8H, #6 - ld1 {v2.8B}, [x0], x2 - srshr v27.8H, v27.8H, #6 - ld1 {v3.8B}, [x0], x2 - srshr v28.8H, v28.8H, #6 - ld1 {v4.8B}, [x0], x2 - srshr v29.8H, v29.8H, #6 - ld1 {v5.8B}, [x0], x2 - srshr v30.8H, v30.8H, #6 - ld1 {v6.8B}, [x0], x2 - srshr v31.8H, v31.8H, #6 - ld1 {v7.8B}, [x0], x2 - uaddw v24.8H, v24.8H, v0.8B - uaddw v25.8H, v25.8H, v1.8B - uaddw v26.8H, v26.8H, v2.8B - sqxtun v0.8B, v24.8H - uaddw v27.8H, v27.8H, v3.8B - sqxtun v1.8B, v25.8H - uaddw v28.8H, v28.8H, v4.8B - sqxtun v2.8B, v26.8H - st1 {v0.8B}, [x3], x2 - uaddw v29.8H, v29.8H, v5.8B - sqxtun v3.8B, v27.8H - st1 {v1.8B}, [x3], x2 - uaddw v30.8H, v30.8H, v6.8B - sqxtun v4.8B, v28.8H - st1 {v2.8B}, [x3], x2 - uaddw v31.8H, v31.8H, v7.8B - sqxtun v5.8B, v29.8H - st1 {v3.8B}, [x3], x2 - sqxtun v6.8B, v30.8H - sqxtun v7.8B, v31.8H - st1 {v4.8B}, [x3], x2 - st1 {v5.8B}, [x3], x2 - st1 {v6.8B}, [x3], x2 - st1 {v7.8B}, [x3], x2 + srshr v24.8h, v24.8h, #6 + ld1 {v0.8b}, [x0], x2 + srshr v25.8h, v25.8h, #6 + ld1 {v1.8b}, [x0], x2 + srshr v26.8h, v26.8h, #6 + ld1 {v2.8b}, [x0], x2 + srshr v27.8h, v27.8h, #6 + ld1 {v3.8b}, [x0], x2 + srshr v28.8h, v28.8h, #6 + ld1 {v4.8b}, [x0], x2 + srshr v29.8h, v29.8h, #6 + ld1 {v5.8b}, [x0], x2 + srshr v30.8h, v30.8h, #6 + ld1 {v6.8b}, [x0], x2 + srshr v31.8h, v31.8h, #6 + ld1 {v7.8b}, [x0], x2 + uaddw v24.8h, v24.8h, v0.8b + uaddw v25.8h, v25.8h, v1.8b + uaddw v26.8h, v26.8h, v2.8b + sqxtun v0.8b, v24.8h + uaddw v27.8h, v27.8h, v3.8b + sqxtun v1.8b, v25.8h + uaddw v28.8h, v28.8h, v4.8b + sqxtun v2.8b, v26.8h + st1 {v0.8b}, [x3], x2 + uaddw v29.8h, v29.8h, v5.8b + sqxtun v3.8b, v27.8h + st1 {v1.8b}, [x3], x2 + uaddw v30.8h, v30.8h, v6.8b + sqxtun v4.8b, v28.8h + st1 {v2.8b}, [x3], x2 + uaddw v31.8h, v31.8h, v7.8b + sqxtun v5.8b, v29.8h + st1 {v3.8b}, [x3], x2 + sqxtun v6.8b, v30.8h + sqxtun v7.8b, v31.8h + st1 {v4.8b}, [x3], x2 + st1 {v5.8b}, [x3], x2 + st1 {v6.8b}, [x3], x2 + st1 {v7.8b}, [x3], x2 sub x1, x1, #128 ret @@ -334,42 +334,42 @@ function ff_h264_idct8_dc_add_neon, export=1 AARCH64_VALID_CALL_TARGET mov w3, #0 sxtw x2, w2 - ld1r {v31.8H}, [x1] + ld1r {v31.8h}, [x1] strh w3, [x1] - ld1 {v0.8B}, [x0], x2 - srshr v31.8H, v31.8H, #6 - ld1 {v1.8B}, [x0], x2 - ld1 {v2.8B}, [x0], x2 - uaddw v24.8H, v31.8H, v0.8B - ld1 {v3.8B}, [x0], x2 - uaddw v25.8H, v31.8H, v1.8B - ld1 {v4.8B}, [x0], x2 - uaddw v26.8H, v31.8H, v2.8B - ld1 {v5.8B}, [x0], x2 - uaddw v27.8H, v31.8H, v3.8B - ld1 {v6.8B}, [x0], x2 - uaddw v28.8H, v31.8H, v4.8B - ld1 {v7.8B}, [x0], x2 - uaddw v29.8H, v31.8H, v5.8B - uaddw v30.8H, v31.8H, v6.8B - uaddw v31.8H, v31.8H, v7.8B - sqxtun v0.8B, v24.8H - sqxtun v1.8B, v25.8H - sqxtun v2.8B, v26.8H - sqxtun v3.8B, v27.8H + ld1 {v0.8b}, [x0], x2 + srshr v31.8h, v31.8h, #6 + ld1 {v1.8b}, [x0], x2 + ld1 {v2.8b}, [x0], x2 + uaddw v24.8h, v31.8h, v0.8b + ld1 {v3.8b}, [x0], x2 + uaddw v25.8h, v31.8h, v1.8b + ld1 {v4.8b}, [x0], x2 + uaddw v26.8h, v31.8h, v2.8b + ld1 {v5.8b}, [x0], x2 + uaddw v27.8h, v31.8h, v3.8b + ld1 {v6.8b}, [x0], x2 + uaddw v28.8h, v31.8h, v4.8b + ld1 {v7.8b}, [x0], x2 + uaddw v29.8h, v31.8h, v5.8b + uaddw v30.8h, v31.8h, v6.8b + uaddw v31.8h, v31.8h, v7.8b + sqxtun v0.8b, v24.8h + sqxtun v1.8b, v25.8h + sqxtun v2.8b, v26.8h + sqxtun v3.8b, v27.8h sub x0, x0, x2, lsl #3 - st1 {v0.8B}, [x0], x2 - sqxtun v4.8B, v28.8H - st1 {v1.8B}, [x0], x2 - sqxtun v5.8B, v29.8H - st1 {v2.8B}, [x0], x2 - sqxtun v6.8B, v30.8H - st1 {v3.8B}, [x0], x2 - sqxtun v7.8B, v31.8H - st1 {v4.8B}, [x0], x2 - st1 {v5.8B}, [x0], x2 - st1 {v6.8B}, [x0], x2 - st1 {v7.8B}, [x0], x2 + st1 {v0.8b}, [x0], x2 + sqxtun v4.8b, v28.8h + st1 {v1.8b}, [x0], x2 + sqxtun v5.8b, v29.8h + st1 {v2.8b}, [x0], x2 + sqxtun v6.8b, v30.8h + st1 {v3.8b}, [x0], x2 + sqxtun v7.8b, v31.8h + st1 {v4.8b}, [x0], x2 + st1 {v5.8b}, [x0], x2 + st1 {v6.8b}, [x0], x2 + st1 {v7.8b}, [x0], x2 ret endfunc @@ -385,7 +385,7 @@ function ff_h264_idct8_add4_neon, export=1 movrel x14, .L_ff_h264_idct8_add_neon 1: ldrb w9, [x7], #4 ldrsw x0, [x5], #16 - ldrb w9, [x4, w9, UXTW] + ldrb w9, [x4, w9, uxtw] subs w9, w9, #1 b.lt 2f ldrsh w11, [x1] diff --git a/libavcodec/aarch64/h264qpel_init_aarch64.c b/libavcodec/aarch64/h264qpel_init_aarch64.c index 77f41d9a21e..93fa5246c40 100644 --- a/libavcodec/aarch64/h264qpel_init_aarch64.c +++ b/libavcodec/aarch64/h264qpel_init_aarch64.c @@ -95,12 +95,55 @@ void ff_avg_h264_qpel8_mc13_neon(uint8_t *dst, const uint8_t *src, ptrdiff_t str void ff_avg_h264_qpel8_mc23_neon(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); void ff_avg_h264_qpel8_mc33_neon(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel16_mc10_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel16_mc20_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel16_mc30_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel16_mc01_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel16_mc11_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel16_mc31_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel16_mc02_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel16_mc03_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel16_mc13_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel16_mc33_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); + +void ff_put_h264_qpel8_mc10_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel8_mc20_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel8_mc30_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel8_mc01_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel8_mc11_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel8_mc31_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel8_mc02_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel8_mc03_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel8_mc13_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_put_h264_qpel8_mc33_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); + +void ff_avg_h264_qpel16_mc10_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel16_mc20_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel16_mc30_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel16_mc01_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel16_mc11_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel16_mc31_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel16_mc02_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel16_mc03_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel16_mc13_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel16_mc33_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); + +void ff_avg_h264_qpel8_mc10_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel8_mc20_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel8_mc30_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel8_mc01_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel8_mc11_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel8_mc31_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel8_mc02_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel8_mc03_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel8_mc13_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); +void ff_avg_h264_qpel8_mc33_neon_10(uint8_t *dst, const uint8_t *src, ptrdiff_t stride); + av_cold void ff_h264qpel_init_aarch64(H264QpelContext *c, int bit_depth) { - const int high_bit_depth = bit_depth > 8; int cpu_flags = av_get_cpu_flags(); - if (have_neon(cpu_flags) && !high_bit_depth) { + if (have_neon(cpu_flags) && bit_depth <= 8) { c->put_h264_qpel_pixels_tab[0][ 0] = ff_put_h264_qpel16_mc00_neon; c->put_h264_qpel_pixels_tab[0][ 1] = ff_put_h264_qpel16_mc10_neon; c->put_h264_qpel_pixels_tab[0][ 2] = ff_put_h264_qpel16_mc20_neon; @@ -168,5 +211,49 @@ av_cold void ff_h264qpel_init_aarch64(H264QpelContext *c, int bit_depth) c->avg_h264_qpel_pixels_tab[1][13] = ff_avg_h264_qpel8_mc13_neon; c->avg_h264_qpel_pixels_tab[1][14] = ff_avg_h264_qpel8_mc23_neon; c->avg_h264_qpel_pixels_tab[1][15] = ff_avg_h264_qpel8_mc33_neon; + } else if (have_neon(cpu_flags) && bit_depth == 10) { + c->put_h264_qpel_pixels_tab[0][ 1] = ff_put_h264_qpel16_mc10_neon_10; + c->put_h264_qpel_pixels_tab[0][ 2] = ff_put_h264_qpel16_mc20_neon_10; + c->put_h264_qpel_pixels_tab[0][ 3] = ff_put_h264_qpel16_mc30_neon_10; + c->put_h264_qpel_pixels_tab[0][ 4] = ff_put_h264_qpel16_mc01_neon_10; + c->put_h264_qpel_pixels_tab[0][ 5] = ff_put_h264_qpel16_mc11_neon_10; + c->put_h264_qpel_pixels_tab[0][ 7] = ff_put_h264_qpel16_mc31_neon_10; + c->put_h264_qpel_pixels_tab[0][ 8] = ff_put_h264_qpel16_mc02_neon_10; + c->put_h264_qpel_pixels_tab[0][12] = ff_put_h264_qpel16_mc03_neon_10; + c->put_h264_qpel_pixels_tab[0][13] = ff_put_h264_qpel16_mc13_neon_10; + c->put_h264_qpel_pixels_tab[0][15] = ff_put_h264_qpel16_mc33_neon_10; + + c->put_h264_qpel_pixels_tab[1][ 1] = ff_put_h264_qpel8_mc10_neon_10; + c->put_h264_qpel_pixels_tab[1][ 2] = ff_put_h264_qpel8_mc20_neon_10; + c->put_h264_qpel_pixels_tab[1][ 3] = ff_put_h264_qpel8_mc30_neon_10; + c->put_h264_qpel_pixels_tab[1][ 4] = ff_put_h264_qpel8_mc01_neon_10; + c->put_h264_qpel_pixels_tab[1][ 5] = ff_put_h264_qpel8_mc11_neon_10; + c->put_h264_qpel_pixels_tab[1][ 7] = ff_put_h264_qpel8_mc31_neon_10; + c->put_h264_qpel_pixels_tab[1][ 8] = ff_put_h264_qpel8_mc02_neon_10; + c->put_h264_qpel_pixels_tab[1][12] = ff_put_h264_qpel8_mc03_neon_10; + c->put_h264_qpel_pixels_tab[1][13] = ff_put_h264_qpel8_mc13_neon_10; + c->put_h264_qpel_pixels_tab[1][15] = ff_put_h264_qpel8_mc33_neon_10; + + c->avg_h264_qpel_pixels_tab[0][ 1] = ff_avg_h264_qpel16_mc10_neon_10; + c->avg_h264_qpel_pixels_tab[0][ 2] = ff_avg_h264_qpel16_mc20_neon_10; + c->avg_h264_qpel_pixels_tab[0][ 3] = ff_avg_h264_qpel16_mc30_neon_10; + c->avg_h264_qpel_pixels_tab[0][ 4] = ff_avg_h264_qpel16_mc01_neon_10; + c->avg_h264_qpel_pixels_tab[0][ 5] = ff_avg_h264_qpel16_mc11_neon_10; + c->avg_h264_qpel_pixels_tab[0][ 7] = ff_avg_h264_qpel16_mc31_neon_10; + c->avg_h264_qpel_pixels_tab[0][ 8] = ff_avg_h264_qpel16_mc02_neon_10; + c->avg_h264_qpel_pixels_tab[0][12] = ff_avg_h264_qpel16_mc03_neon_10; + c->avg_h264_qpel_pixels_tab[0][13] = ff_avg_h264_qpel16_mc13_neon_10; + c->avg_h264_qpel_pixels_tab[0][15] = ff_avg_h264_qpel16_mc33_neon_10; + + c->avg_h264_qpel_pixels_tab[1][ 1] = ff_avg_h264_qpel8_mc10_neon_10; + c->avg_h264_qpel_pixels_tab[1][ 2] = ff_avg_h264_qpel8_mc20_neon_10; + c->avg_h264_qpel_pixels_tab[1][ 3] = ff_avg_h264_qpel8_mc30_neon_10; + c->avg_h264_qpel_pixels_tab[1][ 4] = ff_avg_h264_qpel8_mc01_neon_10; + c->avg_h264_qpel_pixels_tab[1][ 5] = ff_avg_h264_qpel8_mc11_neon_10; + c->avg_h264_qpel_pixels_tab[1][ 7] = ff_avg_h264_qpel8_mc31_neon_10; + c->avg_h264_qpel_pixels_tab[1][ 8] = ff_avg_h264_qpel8_mc02_neon_10; + c->avg_h264_qpel_pixels_tab[1][12] = ff_avg_h264_qpel8_mc03_neon_10; + c->avg_h264_qpel_pixels_tab[1][13] = ff_avg_h264_qpel8_mc13_neon_10; + c->avg_h264_qpel_pixels_tab[1][15] = ff_avg_h264_qpel8_mc33_neon_10; } } diff --git a/libavcodec/aarch64/h264qpel_neon.S b/libavcodec/aarch64/h264qpel_neon.S index 451fd8af246..301dd19fe28 100644 --- a/libavcodec/aarch64/h264qpel_neon.S +++ b/libavcodec/aarch64/h264qpel_neon.S @@ -27,127 +27,127 @@ .macro lowpass_const r movz \r, #20, lsl #16 movk \r, #5 - mov v6.S[0], \r + mov v6.s[0], \r .endm //trashes v0-v5 .macro lowpass_8 r0, r1, r2, r3, d0, d1, narrow=1 - ext v2.8B, \r0\().8B, \r1\().8B, #2 - ext v3.8B, \r0\().8B, \r1\().8B, #3 - uaddl v2.8H, v2.8B, v3.8B - ext v4.8B, \r0\().8B, \r1\().8B, #1 - ext v5.8B, \r0\().8B, \r1\().8B, #4 - uaddl v4.8H, v4.8B, v5.8B - ext v1.8B, \r0\().8B, \r1\().8B, #5 - uaddl \d0\().8H, \r0\().8B, v1.8B - ext v0.8B, \r2\().8B, \r3\().8B, #2 - mla \d0\().8H, v2.8H, v6.H[1] - ext v1.8B, \r2\().8B, \r3\().8B, #3 - uaddl v0.8H, v0.8B, v1.8B - ext v1.8B, \r2\().8B, \r3\().8B, #1 - mls \d0\().8H, v4.8H, v6.H[0] - ext v3.8B, \r2\().8B, \r3\().8B, #4 - uaddl v1.8H, v1.8B, v3.8B - ext v2.8B, \r2\().8B, \r3\().8B, #5 - uaddl \d1\().8H, \r2\().8B, v2.8B - mla \d1\().8H, v0.8H, v6.H[1] - mls \d1\().8H, v1.8H, v6.H[0] + ext v2.8b, \r0\().8b, \r1\().8b, #2 + ext v3.8b, \r0\().8b, \r1\().8b, #3 + uaddl v2.8h, v2.8b, v3.8b + ext v4.8b, \r0\().8b, \r1\().8b, #1 + ext v5.8b, \r0\().8b, \r1\().8b, #4 + uaddl v4.8h, v4.8b, v5.8b + ext v1.8b, \r0\().8b, \r1\().8b, #5 + uaddl \d0\().8h, \r0\().8b, v1.8b + ext v0.8b, \r2\().8b, \r3\().8b, #2 + mla \d0\().8h, v2.8h, v6.h[1] + ext v1.8b, \r2\().8b, \r3\().8b, #3 + uaddl v0.8h, v0.8b, v1.8b + ext v1.8b, \r2\().8b, \r3\().8b, #1 + mls \d0\().8h, v4.8h, v6.h[0] + ext v3.8b, \r2\().8b, \r3\().8b, #4 + uaddl v1.8h, v1.8b, v3.8b + ext v2.8b, \r2\().8b, \r3\().8b, #5 + uaddl \d1\().8h, \r2\().8b, v2.8b + mla \d1\().8h, v0.8h, v6.h[1] + mls \d1\().8h, v1.8h, v6.h[0] .if \narrow - sqrshrun \d0\().8B, \d0\().8H, #5 - sqrshrun \d1\().8B, \d1\().8H, #5 + sqrshrun \d0\().8b, \d0\().8h, #5 + sqrshrun \d1\().8b, \d1\().8h, #5 .endif .endm //trashes v0-v4 .macro lowpass_8_v r0, r1, r2, r3, r4, r5, r6, d0, d1, narrow=1 - uaddl v2.8H, \r2\().8B, \r3\().8B - uaddl v0.8H, \r3\().8B, \r4\().8B - uaddl v4.8H, \r1\().8B, \r4\().8B - uaddl v1.8H, \r2\().8B, \r5\().8B - uaddl \d0\().8H, \r0\().8B, \r5\().8B - uaddl \d1\().8H, \r1\().8B, \r6\().8B - mla \d0\().8H, v2.8H, v6.H[1] - mls \d0\().8H, v4.8H, v6.H[0] - mla \d1\().8H, v0.8H, v6.H[1] - mls \d1\().8H, v1.8H, v6.H[0] + uaddl v2.8h, \r2\().8b, \r3\().8b + uaddl v0.8h, \r3\().8b, \r4\().8b + uaddl v4.8h, \r1\().8b, \r4\().8b + uaddl v1.8h, \r2\().8b, \r5\().8b + uaddl \d0\().8h, \r0\().8b, \r5\().8b + uaddl \d1\().8h, \r1\().8b, \r6\().8b + mla \d0\().8h, v2.8h, v6.h[1] + mls \d0\().8h, v4.8h, v6.h[0] + mla \d1\().8h, v0.8h, v6.h[1] + mls \d1\().8h, v1.8h, v6.h[0] .if \narrow - sqrshrun \d0\().8B, \d0\().8H, #5 - sqrshrun \d1\().8B, \d1\().8H, #5 + sqrshrun \d0\().8b, \d0\().8h, #5 + sqrshrun \d1\().8b, \d1\().8h, #5 .endif .endm //trashes v0-v5, v7, v30-v31 .macro lowpass_8H r0, r1 - ext v0.16B, \r0\().16B, \r0\().16B, #2 - ext v1.16B, \r0\().16B, \r0\().16B, #3 - uaddl v0.8H, v0.8B, v1.8B - ext v2.16B, \r0\().16B, \r0\().16B, #1 - ext v3.16B, \r0\().16B, \r0\().16B, #4 - uaddl v2.8H, v2.8B, v3.8B - ext v30.16B, \r0\().16B, \r0\().16B, #5 - uaddl \r0\().8H, \r0\().8B, v30.8B - ext v4.16B, \r1\().16B, \r1\().16B, #2 - mla \r0\().8H, v0.8H, v6.H[1] - ext v5.16B, \r1\().16B, \r1\().16B, #3 - uaddl v4.8H, v4.8B, v5.8B - ext v7.16B, \r1\().16B, \r1\().16B, #1 - mls \r0\().8H, v2.8H, v6.H[0] - ext v0.16B, \r1\().16B, \r1\().16B, #4 - uaddl v7.8H, v7.8B, v0.8B - ext v31.16B, \r1\().16B, \r1\().16B, #5 - uaddl \r1\().8H, \r1\().8B, v31.8B - mla \r1\().8H, v4.8H, v6.H[1] - mls \r1\().8H, v7.8H, v6.H[0] + ext v0.16b, \r0\().16b, \r0\().16b, #2 + ext v1.16b, \r0\().16b, \r0\().16b, #3 + uaddl v0.8h, v0.8b, v1.8b + ext v2.16b, \r0\().16b, \r0\().16b, #1 + ext v3.16b, \r0\().16b, \r0\().16b, #4 + uaddl v2.8h, v2.8b, v3.8b + ext v30.16b, \r0\().16b, \r0\().16b, #5 + uaddl \r0\().8h, \r0\().8b, v30.8b + ext v4.16b, \r1\().16b, \r1\().16b, #2 + mla \r0\().8h, v0.8h, v6.h[1] + ext v5.16b, \r1\().16b, \r1\().16b, #3 + uaddl v4.8h, v4.8b, v5.8b + ext v7.16b, \r1\().16b, \r1\().16b, #1 + mls \r0\().8h, v2.8h, v6.h[0] + ext v0.16b, \r1\().16b, \r1\().16b, #4 + uaddl v7.8h, v7.8b, v0.8b + ext v31.16b, \r1\().16b, \r1\().16b, #5 + uaddl \r1\().8h, \r1\().8b, v31.8b + mla \r1\().8h, v4.8h, v6.h[1] + mls \r1\().8h, v7.8h, v6.h[0] .endm // trashes v2-v5, v30 .macro lowpass_8_1 r0, r1, d0, narrow=1 - ext v2.8B, \r0\().8B, \r1\().8B, #2 - ext v3.8B, \r0\().8B, \r1\().8B, #3 - uaddl v2.8H, v2.8B, v3.8B - ext v4.8B, \r0\().8B, \r1\().8B, #1 - ext v5.8B, \r0\().8B, \r1\().8B, #4 - uaddl v4.8H, v4.8B, v5.8B - ext v30.8B, \r0\().8B, \r1\().8B, #5 - uaddl \d0\().8H, \r0\().8B, v30.8B - mla \d0\().8H, v2.8H, v6.H[1] - mls \d0\().8H, v4.8H, v6.H[0] + ext v2.8b, \r0\().8b, \r1\().8b, #2 + ext v3.8b, \r0\().8b, \r1\().8b, #3 + uaddl v2.8h, v2.8b, v3.8b + ext v4.8b, \r0\().8b, \r1\().8b, #1 + ext v5.8b, \r0\().8b, \r1\().8b, #4 + uaddl v4.8h, v4.8b, v5.8b + ext v30.8b, \r0\().8b, \r1\().8b, #5 + uaddl \d0\().8h, \r0\().8b, v30.8b + mla \d0\().8h, v2.8h, v6.h[1] + mls \d0\().8h, v4.8h, v6.h[0] .if \narrow - sqrshrun \d0\().8B, \d0\().8H, #5 + sqrshrun \d0\().8b, \d0\().8h, #5 .endif .endm // trashed v0-v7 .macro lowpass_8.16 r0, r1, r2, r3, r4, r5 - saddl v5.4S, \r2\().4H, \r3\().4H - saddl2 v1.4S, \r2\().8H, \r3\().8H - saddl v6.4S, \r1\().4H, \r4\().4H - saddl2 v2.4S, \r1\().8H, \r4\().8H - saddl v0.4S, \r0\().4H, \r5\().4H - saddl2 v4.4S, \r0\().8H, \r5\().8H - - shl v3.4S, v5.4S, #4 - shl v5.4S, v5.4S, #2 - shl v7.4S, v6.4S, #2 - add v5.4S, v5.4S, v3.4S - add v6.4S, v6.4S, v7.4S - - shl v3.4S, v1.4S, #4 - shl v1.4S, v1.4S, #2 - shl v7.4S, v2.4S, #2 - add v1.4S, v1.4S, v3.4S - add v2.4S, v2.4S, v7.4S - - add v5.4S, v5.4S, v0.4S - sub v5.4S, v5.4S, v6.4S - - add v1.4S, v1.4S, v4.4S - sub v1.4S, v1.4S, v2.4S - - rshrn v5.4H, v5.4S, #10 - rshrn2 v5.8H, v1.4S, #10 - - sqxtun \r0\().8B, v5.8H + saddl v5.4s, \r2\().4h, \r3\().4h + saddl2 v1.4s, \r2\().8h, \r3\().8h + saddl v6.4s, \r1\().4h, \r4\().4h + saddl2 v2.4s, \r1\().8h, \r4\().8h + saddl v0.4s, \r0\().4h, \r5\().4h + saddl2 v4.4s, \r0\().8h, \r5\().8h + + shl v3.4s, v5.4s, #4 + shl v5.4s, v5.4s, #2 + shl v7.4s, v6.4s, #2 + add v5.4s, v5.4s, v3.4s + add v6.4s, v6.4s, v7.4s + + shl v3.4s, v1.4s, #4 + shl v1.4s, v1.4s, #2 + shl v7.4s, v2.4s, #2 + add v1.4s, v1.4s, v3.4s + add v2.4s, v2.4s, v7.4s + + add v5.4s, v5.4s, v0.4s + sub v5.4s, v5.4s, v6.4s + + add v1.4s, v1.4s, v4.4s + sub v1.4s, v1.4s, v2.4s + + rshrn v5.4h, v5.4s, #10 + rshrn2 v5.8h, v1.4s, #10 + + sqxtun \r0\().8b, v5.8h .endm function put_h264_qpel16_h_lowpass_neon_packed @@ -176,19 +176,19 @@ function \type\()_h264_qpel16_h_lowpass_neon endfunc function \type\()_h264_qpel8_h_lowpass_neon -1: ld1 {v28.8B, v29.8B}, [x1], x2 - ld1 {v16.8B, v17.8B}, [x1], x2 +1: ld1 {v28.8b, v29.8b}, [x1], x2 + ld1 {v16.8b, v17.8b}, [x1], x2 subs x12, x12, #2 lowpass_8 v28, v29, v16, v17, v28, v16 .ifc \type,avg - ld1 {v2.8B}, [x0], x3 - ld1 {v3.8B}, [x0] - urhadd v28.8B, v28.8B, v2.8B - urhadd v16.8B, v16.8B, v3.8B + ld1 {v2.8b}, [x0], x3 + ld1 {v3.8b}, [x0] + urhadd v28.8b, v28.8b, v2.8b + urhadd v16.8b, v16.8b, v3.8b sub x0, x0, x3 .endif - st1 {v28.8B}, [x0], x3 - st1 {v16.8B}, [x0], x3 + st1 {v28.8b}, [x0], x3 + st1 {v16.8b}, [x0], x3 b.ne 1b ret endfunc @@ -213,23 +213,23 @@ function \type\()_h264_qpel16_h_lowpass_l2_neon endfunc function \type\()_h264_qpel8_h_lowpass_l2_neon -1: ld1 {v26.8B, v27.8B}, [x1], x2 - ld1 {v16.8B, v17.8B}, [x1], x2 - ld1 {v28.8B}, [x3], x2 - ld1 {v29.8B}, [x3], x2 +1: ld1 {v26.8b, v27.8b}, [x1], x2 + ld1 {v16.8b, v17.8b}, [x1], x2 + ld1 {v28.8b}, [x3], x2 + ld1 {v29.8b}, [x3], x2 subs x12, x12, #2 lowpass_8 v26, v27, v16, v17, v26, v27 - urhadd v26.8B, v26.8B, v28.8B - urhadd v27.8B, v27.8B, v29.8B + urhadd v26.8b, v26.8b, v28.8b + urhadd v27.8b, v27.8b, v29.8b .ifc \type,avg - ld1 {v2.8B}, [x0], x2 - ld1 {v3.8B}, [x0] - urhadd v26.8B, v26.8B, v2.8B - urhadd v27.8B, v27.8B, v3.8B + ld1 {v2.8b}, [x0], x2 + ld1 {v3.8b}, [x0] + urhadd v26.8b, v26.8b, v2.8b + urhadd v27.8b, v27.8b, v3.8b sub x0, x0, x2 .endif - st1 {v26.8B}, [x0], x2 - st1 {v27.8B}, [x0], x2 + st1 {v26.8b}, [x0], x2 + st1 {v27.8b}, [x0], x2 b.ne 1b ret endfunc @@ -270,52 +270,52 @@ function \type\()_h264_qpel16_v_lowpass_neon endfunc function \type\()_h264_qpel8_v_lowpass_neon - ld1 {v16.8B}, [x1], x3 - ld1 {v17.8B}, [x1], x3 - ld1 {v18.8B}, [x1], x3 - ld1 {v19.8B}, [x1], x3 - ld1 {v20.8B}, [x1], x3 - ld1 {v21.8B}, [x1], x3 - ld1 {v22.8B}, [x1], x3 - ld1 {v23.8B}, [x1], x3 - ld1 {v24.8B}, [x1], x3 - ld1 {v25.8B}, [x1], x3 - ld1 {v26.8B}, [x1], x3 - ld1 {v27.8B}, [x1], x3 - ld1 {v28.8B}, [x1] + ld1 {v16.8b}, [x1], x3 + ld1 {v17.8b}, [x1], x3 + ld1 {v18.8b}, [x1], x3 + ld1 {v19.8b}, [x1], x3 + ld1 {v20.8b}, [x1], x3 + ld1 {v21.8b}, [x1], x3 + ld1 {v22.8b}, [x1], x3 + ld1 {v23.8b}, [x1], x3 + ld1 {v24.8b}, [x1], x3 + ld1 {v25.8b}, [x1], x3 + ld1 {v26.8b}, [x1], x3 + ld1 {v27.8b}, [x1], x3 + ld1 {v28.8b}, [x1] lowpass_8_v v16, v17, v18, v19, v20, v21, v22, v16, v17 lowpass_8_v v18, v19, v20, v21, v22, v23, v24, v18, v19 lowpass_8_v v20, v21, v22, v23, v24, v25, v26, v20, v21 lowpass_8_v v22, v23, v24, v25, v26, v27, v28, v22, v23 .ifc \type,avg - ld1 {v24.8B}, [x0], x2 - ld1 {v25.8B}, [x0], x2 - ld1 {v26.8B}, [x0], x2 - urhadd v16.8B, v16.8B, v24.8B - ld1 {v27.8B}, [x0], x2 - urhadd v17.8B, v17.8B, v25.8B - ld1 {v28.8B}, [x0], x2 - urhadd v18.8B, v18.8B, v26.8B - ld1 {v29.8B}, [x0], x2 - urhadd v19.8B, v19.8B, v27.8B - ld1 {v30.8B}, [x0], x2 - urhadd v20.8B, v20.8B, v28.8B - ld1 {v31.8B}, [x0], x2 - urhadd v21.8B, v21.8B, v29.8B - urhadd v22.8B, v22.8B, v30.8B - urhadd v23.8B, v23.8B, v31.8B + ld1 {v24.8b}, [x0], x2 + ld1 {v25.8b}, [x0], x2 + ld1 {v26.8b}, [x0], x2 + urhadd v16.8b, v16.8b, v24.8b + ld1 {v27.8b}, [x0], x2 + urhadd v17.8b, v17.8b, v25.8b + ld1 {v28.8b}, [x0], x2 + urhadd v18.8b, v18.8b, v26.8b + ld1 {v29.8b}, [x0], x2 + urhadd v19.8b, v19.8b, v27.8b + ld1 {v30.8b}, [x0], x2 + urhadd v20.8b, v20.8b, v28.8b + ld1 {v31.8b}, [x0], x2 + urhadd v21.8b, v21.8b, v29.8b + urhadd v22.8b, v22.8b, v30.8b + urhadd v23.8b, v23.8b, v31.8b sub x0, x0, x2, lsl #3 .endif - st1 {v16.8B}, [x0], x2 - st1 {v17.8B}, [x0], x2 - st1 {v18.8B}, [x0], x2 - st1 {v19.8B}, [x0], x2 - st1 {v20.8B}, [x0], x2 - st1 {v21.8B}, [x0], x2 - st1 {v22.8B}, [x0], x2 - st1 {v23.8B}, [x0], x2 + st1 {v16.8b}, [x0], x2 + st1 {v17.8b}, [x0], x2 + st1 {v18.8b}, [x0], x2 + st1 {v19.8b}, [x0], x2 + st1 {v20.8b}, [x0], x2 + st1 {v21.8b}, [x0], x2 + st1 {v22.8b}, [x0], x2 + st1 {v23.8b}, [x0], x2 ret endfunc @@ -343,70 +343,70 @@ function \type\()_h264_qpel16_v_lowpass_l2_neon endfunc function \type\()_h264_qpel8_v_lowpass_l2_neon - ld1 {v16.8B}, [x1], x3 - ld1 {v17.8B}, [x1], x3 - ld1 {v18.8B}, [x1], x3 - ld1 {v19.8B}, [x1], x3 - ld1 {v20.8B}, [x1], x3 - ld1 {v21.8B}, [x1], x3 - ld1 {v22.8B}, [x1], x3 - ld1 {v23.8B}, [x1], x3 - ld1 {v24.8B}, [x1], x3 - ld1 {v25.8B}, [x1], x3 - ld1 {v26.8B}, [x1], x3 - ld1 {v27.8B}, [x1], x3 - ld1 {v28.8B}, [x1] + ld1 {v16.8b}, [x1], x3 + ld1 {v17.8b}, [x1], x3 + ld1 {v18.8b}, [x1], x3 + ld1 {v19.8b}, [x1], x3 + ld1 {v20.8b}, [x1], x3 + ld1 {v21.8b}, [x1], x3 + ld1 {v22.8b}, [x1], x3 + ld1 {v23.8b}, [x1], x3 + ld1 {v24.8b}, [x1], x3 + ld1 {v25.8b}, [x1], x3 + ld1 {v26.8b}, [x1], x3 + ld1 {v27.8b}, [x1], x3 + ld1 {v28.8b}, [x1] lowpass_8_v v16, v17, v18, v19, v20, v21, v22, v16, v17 lowpass_8_v v18, v19, v20, v21, v22, v23, v24, v18, v19 lowpass_8_v v20, v21, v22, v23, v24, v25, v26, v20, v21 lowpass_8_v v22, v23, v24, v25, v26, v27, v28, v22, v23 - ld1 {v24.8B}, [x12], x2 - ld1 {v25.8B}, [x12], x2 - ld1 {v26.8B}, [x12], x2 - ld1 {v27.8B}, [x12], x2 - ld1 {v28.8B}, [x12], x2 - urhadd v16.8B, v24.8B, v16.8B - urhadd v17.8B, v25.8B, v17.8B - ld1 {v29.8B}, [x12], x2 - urhadd v18.8B, v26.8B, v18.8B - urhadd v19.8B, v27.8B, v19.8B - ld1 {v30.8B}, [x12], x2 - urhadd v20.8B, v28.8B, v20.8B - urhadd v21.8B, v29.8B, v21.8B - ld1 {v31.8B}, [x12], x2 - urhadd v22.8B, v30.8B, v22.8B - urhadd v23.8B, v31.8B, v23.8B + ld1 {v24.8b}, [x12], x2 + ld1 {v25.8b}, [x12], x2 + ld1 {v26.8b}, [x12], x2 + ld1 {v27.8b}, [x12], x2 + ld1 {v28.8b}, [x12], x2 + urhadd v16.8b, v24.8b, v16.8b + urhadd v17.8b, v25.8b, v17.8b + ld1 {v29.8b}, [x12], x2 + urhadd v18.8b, v26.8b, v18.8b + urhadd v19.8b, v27.8b, v19.8b + ld1 {v30.8b}, [x12], x2 + urhadd v20.8b, v28.8b, v20.8b + urhadd v21.8b, v29.8b, v21.8b + ld1 {v31.8b}, [x12], x2 + urhadd v22.8b, v30.8b, v22.8b + urhadd v23.8b, v31.8b, v23.8b .ifc \type,avg - ld1 {v24.8B}, [x0], x3 - ld1 {v25.8B}, [x0], x3 - ld1 {v26.8B}, [x0], x3 - urhadd v16.8B, v16.8B, v24.8B - ld1 {v27.8B}, [x0], x3 - urhadd v17.8B, v17.8B, v25.8B - ld1 {v28.8B}, [x0], x3 - urhadd v18.8B, v18.8B, v26.8B - ld1 {v29.8B}, [x0], x3 - urhadd v19.8B, v19.8B, v27.8B - ld1 {v30.8B}, [x0], x3 - urhadd v20.8B, v20.8B, v28.8B - ld1 {v31.8B}, [x0], x3 - urhadd v21.8B, v21.8B, v29.8B - urhadd v22.8B, v22.8B, v30.8B - urhadd v23.8B, v23.8B, v31.8B + ld1 {v24.8b}, [x0], x3 + ld1 {v25.8b}, [x0], x3 + ld1 {v26.8b}, [x0], x3 + urhadd v16.8b, v16.8b, v24.8b + ld1 {v27.8b}, [x0], x3 + urhadd v17.8b, v17.8b, v25.8b + ld1 {v28.8b}, [x0], x3 + urhadd v18.8b, v18.8b, v26.8b + ld1 {v29.8b}, [x0], x3 + urhadd v19.8b, v19.8b, v27.8b + ld1 {v30.8b}, [x0], x3 + urhadd v20.8b, v20.8b, v28.8b + ld1 {v31.8b}, [x0], x3 + urhadd v21.8b, v21.8b, v29.8b + urhadd v22.8b, v22.8b, v30.8b + urhadd v23.8b, v23.8b, v31.8b sub x0, x0, x3, lsl #3 .endif - st1 {v16.8B}, [x0], x3 - st1 {v17.8B}, [x0], x3 - st1 {v18.8B}, [x0], x3 - st1 {v19.8B}, [x0], x3 - st1 {v20.8B}, [x0], x3 - st1 {v21.8B}, [x0], x3 - st1 {v22.8B}, [x0], x3 - st1 {v23.8B}, [x0], x3 + st1 {v16.8b}, [x0], x3 + st1 {v17.8b}, [x0], x3 + st1 {v18.8b}, [x0], x3 + st1 {v19.8b}, [x0], x3 + st1 {v20.8b}, [x0], x3 + st1 {v21.8b}, [x0], x3 + st1 {v22.8b}, [x0], x3 + st1 {v23.8b}, [x0], x3 ret endfunc @@ -417,19 +417,19 @@ endfunc function put_h264_qpel8_hv_lowpass_neon_top lowpass_const w12 - ld1 {v16.8H}, [x1], x3 - ld1 {v17.8H}, [x1], x3 - ld1 {v18.8H}, [x1], x3 - ld1 {v19.8H}, [x1], x3 - ld1 {v20.8H}, [x1], x3 - ld1 {v21.8H}, [x1], x3 - ld1 {v22.8H}, [x1], x3 - ld1 {v23.8H}, [x1], x3 - ld1 {v24.8H}, [x1], x3 - ld1 {v25.8H}, [x1], x3 - ld1 {v26.8H}, [x1], x3 - ld1 {v27.8H}, [x1], x3 - ld1 {v28.8H}, [x1] + ld1 {v16.8h}, [x1], x3 + ld1 {v17.8h}, [x1], x3 + ld1 {v18.8h}, [x1], x3 + ld1 {v19.8h}, [x1], x3 + ld1 {v20.8h}, [x1], x3 + ld1 {v21.8h}, [x1], x3 + ld1 {v22.8h}, [x1], x3 + ld1 {v23.8h}, [x1], x3 + ld1 {v24.8h}, [x1], x3 + ld1 {v25.8h}, [x1], x3 + ld1 {v26.8h}, [x1], x3 + ld1 {v27.8h}, [x1], x3 + ld1 {v28.8h}, [x1] lowpass_8H v16, v17 lowpass_8H v18, v19 lowpass_8H v20, v21 @@ -458,33 +458,33 @@ function \type\()_h264_qpel8_hv_lowpass_neon mov x10, x30 bl put_h264_qpel8_hv_lowpass_neon_top .ifc \type,avg - ld1 {v0.8B}, [x0], x2 - ld1 {v1.8B}, [x0], x2 - ld1 {v2.8B}, [x0], x2 - urhadd v16.8B, v16.8B, v0.8B - ld1 {v3.8B}, [x0], x2 - urhadd v17.8B, v17.8B, v1.8B - ld1 {v4.8B}, [x0], x2 - urhadd v18.8B, v18.8B, v2.8B - ld1 {v5.8B}, [x0], x2 - urhadd v19.8B, v19.8B, v3.8B - ld1 {v6.8B}, [x0], x2 - urhadd v20.8B, v20.8B, v4.8B - ld1 {v7.8B}, [x0], x2 - urhadd v21.8B, v21.8B, v5.8B - urhadd v22.8B, v22.8B, v6.8B - urhadd v23.8B, v23.8B, v7.8B + ld1 {v0.8b}, [x0], x2 + ld1 {v1.8b}, [x0], x2 + ld1 {v2.8b}, [x0], x2 + urhadd v16.8b, v16.8b, v0.8b + ld1 {v3.8b}, [x0], x2 + urhadd v17.8b, v17.8b, v1.8b + ld1 {v4.8b}, [x0], x2 + urhadd v18.8b, v18.8b, v2.8b + ld1 {v5.8b}, [x0], x2 + urhadd v19.8b, v19.8b, v3.8b + ld1 {v6.8b}, [x0], x2 + urhadd v20.8b, v20.8b, v4.8b + ld1 {v7.8b}, [x0], x2 + urhadd v21.8b, v21.8b, v5.8b + urhadd v22.8b, v22.8b, v6.8b + urhadd v23.8b, v23.8b, v7.8b sub x0, x0, x2, lsl #3 .endif - st1 {v16.8B}, [x0], x2 - st1 {v17.8B}, [x0], x2 - st1 {v18.8B}, [x0], x2 - st1 {v19.8B}, [x0], x2 - st1 {v20.8B}, [x0], x2 - st1 {v21.8B}, [x0], x2 - st1 {v22.8B}, [x0], x2 - st1 {v23.8B}, [x0], x2 + st1 {v16.8b}, [x0], x2 + st1 {v17.8b}, [x0], x2 + st1 {v18.8b}, [x0], x2 + st1 {v19.8b}, [x0], x2 + st1 {v20.8b}, [x0], x2 + st1 {v21.8b}, [x0], x2 + st1 {v22.8b}, [x0], x2 + st1 {v23.8b}, [x0], x2 ret x10 endfunc @@ -498,45 +498,45 @@ function \type\()_h264_qpel8_hv_lowpass_l2_neon mov x10, x30 bl put_h264_qpel8_hv_lowpass_neon_top - ld1 {v0.8B, v1.8B}, [x2], #16 - ld1 {v2.8B, v3.8B}, [x2], #16 - urhadd v0.8B, v0.8B, v16.8B - urhadd v1.8B, v1.8B, v17.8B - ld1 {v4.8B, v5.8B}, [x2], #16 - urhadd v2.8B, v2.8B, v18.8B - urhadd v3.8B, v3.8B, v19.8B - ld1 {v6.8B, v7.8B}, [x2], #16 - urhadd v4.8B, v4.8B, v20.8B - urhadd v5.8B, v5.8B, v21.8B - urhadd v6.8B, v6.8B, v22.8B - urhadd v7.8B, v7.8B, v23.8B + ld1 {v0.8b, v1.8b}, [x2], #16 + ld1 {v2.8b, v3.8b}, [x2], #16 + urhadd v0.8b, v0.8b, v16.8b + urhadd v1.8b, v1.8b, v17.8b + ld1 {v4.8b, v5.8b}, [x2], #16 + urhadd v2.8b, v2.8b, v18.8b + urhadd v3.8b, v3.8b, v19.8b + ld1 {v6.8b, v7.8b}, [x2], #16 + urhadd v4.8b, v4.8b, v20.8b + urhadd v5.8b, v5.8b, v21.8b + urhadd v6.8b, v6.8b, v22.8b + urhadd v7.8b, v7.8b, v23.8b .ifc \type,avg - ld1 {v16.8B}, [x0], x3 - ld1 {v17.8B}, [x0], x3 - ld1 {v18.8B}, [x0], x3 - urhadd v0.8B, v0.8B, v16.8B - ld1 {v19.8B}, [x0], x3 - urhadd v1.8B, v1.8B, v17.8B - ld1 {v20.8B}, [x0], x3 - urhadd v2.8B, v2.8B, v18.8B - ld1 {v21.8B}, [x0], x3 - urhadd v3.8B, v3.8B, v19.8B - ld1 {v22.8B}, [x0], x3 - urhadd v4.8B, v4.8B, v20.8B - ld1 {v23.8B}, [x0], x3 - urhadd v5.8B, v5.8B, v21.8B - urhadd v6.8B, v6.8B, v22.8B - urhadd v7.8B, v7.8B, v23.8B + ld1 {v16.8b}, [x0], x3 + ld1 {v17.8b}, [x0], x3 + ld1 {v18.8b}, [x0], x3 + urhadd v0.8b, v0.8b, v16.8b + ld1 {v19.8b}, [x0], x3 + urhadd v1.8b, v1.8b, v17.8b + ld1 {v20.8b}, [x0], x3 + urhadd v2.8b, v2.8b, v18.8b + ld1 {v21.8b}, [x0], x3 + urhadd v3.8b, v3.8b, v19.8b + ld1 {v22.8b}, [x0], x3 + urhadd v4.8b, v4.8b, v20.8b + ld1 {v23.8b}, [x0], x3 + urhadd v5.8b, v5.8b, v21.8b + urhadd v6.8b, v6.8b, v22.8b + urhadd v7.8b, v7.8b, v23.8b sub x0, x0, x3, lsl #3 .endif - st1 {v0.8B}, [x0], x3 - st1 {v1.8B}, [x0], x3 - st1 {v2.8B}, [x0], x3 - st1 {v3.8B}, [x0], x3 - st1 {v4.8B}, [x0], x3 - st1 {v5.8B}, [x0], x3 - st1 {v6.8B}, [x0], x3 - st1 {v7.8B}, [x0], x3 + st1 {v0.8b}, [x0], x3 + st1 {v1.8b}, [x0], x3 + st1 {v2.8b}, [x0], x3 + st1 {v3.8b}, [x0], x3 + st1 {v4.8b}, [x0], x3 + st1 {v5.8b}, [x0], x3 + st1 {v6.8b}, [x0], x3 + st1 {v7.8b}, [x0], x3 ret x10 endfunc @@ -580,8 +580,8 @@ function \type\()_h264_qpel16_hv_lowpass_l2_neon endfunc .endm - h264_qpel16_hv put - h264_qpel16_hv avg + h264_qpel16_hv put + h264_qpel16_hv avg .macro h264_qpel8 type function ff_\type\()_h264_qpel8_mc10_neon, export=1 @@ -759,8 +759,8 @@ function ff_\type\()_h264_qpel8_mc33_neon, export=1 endfunc .endm - h264_qpel8 put - h264_qpel8 avg + h264_qpel8 put + h264_qpel8 avg .macro h264_qpel16 type function ff_\type\()_h264_qpel16_mc10_neon, export=1 @@ -931,5 +931,537 @@ function ff_\type\()_h264_qpel16_mc33_neon, export=1 endfunc .endm - h264_qpel16 put - h264_qpel16 avg + h264_qpel16 put + h264_qpel16 avg + +//trashes v0-v5 +.macro lowpass_8_10 r0, r1, r2, r3, d0, d1 + ext v2.16b, \r0\().16b, \r1\().16b, #4 + ext v3.16b, \r0\().16b, \r1\().16b, #6 + add v2.8h, v2.8h, v3.8h + ext v4.16b, \r0\().16b, \r1\().16b, #2 + ext v5.16b, \r0\().16b, \r1\().16b, #8 + add v4.8h, v4.8h, v5.8h + ext v1.16b, \r0\().16b, \r1\().16b, #10 + + add \d0\().8h, \r0\().8h, v1.8h + ext v0.16b, \r2\().16b, \r3\().16b, #4 + mla \d0\().8h, v2.8h, v6.h[1] + ext v1.16b, \r2\().16b, \r3\().16b, #6 + add v0.8h, v0.8h, v1.8h + ext v1.16b, \r2\().16b, \r3\().16b, #2 + mul v5.8h, v4.8h, v6.h[0] + uqsub \d0\().8h, \d0\().8h, v5.8h + urshr \d0\().8h, \d0\().8h, #5 + + ext v3.16b, \r2\().16b, \r3\().16b, #8 + add v1.8h, v1.8h, v3.8h + ext v2.16b, \r2\().16b, \r3\().16b, #10 + + add \d1\().8h, \r2\().8h, v2.8h + mla \d1\().8h, v0.8h, v6.h[1] + mul v5.8h, v1.8h, v6.h[0] + uqsub \d1\().8h, \d1\().8h, v5.8h + mvni v5.8h, #0xFC, lsl #8 // 1023 for clipping + urshr \d1\().8h, \d1\().8h, #5 + + umin \d0\().8h, \d0\().8h, v5.8h + umin \d1\().8h, \d1\().8h, v5.8h +.endm + +//trashes v0-v4 +.macro lowpass_8_10_v r0, r1, r2, r3, r4, r5, r6, d0, d1 + add v2.8h, \r2\().8h, \r3\().8h + add v0.8h, \r3\().8h, \r4\().8h + add v4.8h, \r1\().8h, \r4\().8h + add v1.8h, \r2\().8h, \r5\().8h + + add \d0\().8h, \r0\().8h, \r5\().8h + add \d1\().8h, \r1\().8h, \r6\().8h + mla \d0\().8h, v2.8h, v6.h[1] + mla \d1\().8h, v0.8h, v6.h[1] + mul v2.8h, v4.8h, v6.h[0] + mul v0.8h, v1.8h, v6.h[0] + uqsub \d0\().8h, \d0\().8h, v2.8h + uqsub \d1\().8h, \d1\().8h, v0.8h + + mvni v0.8h, #0xFC, lsl #8 // 1023 for clipping + + urshr \d0\().8h, \d0\().8h, #5 + urshr \d1\().8h, \d1\().8h, #5 + + umin \d0\().8h, \d0\().8h, v0.8h + umin \d1\().8h, \d1\().8h, v0.8h +.endm + +function put_h264_qpel16_h_lowpass_neon_packed_10 + mov x4, x30 + mov x12, #32 + mov x3, #16 + bl put_h264_qpel8_h_lowpass_neon_10 + sub x1, x1, x2, lsl #4 + add x1, x1, #16 + mov x12, #32 + mov x30, x4 + b put_h264_qpel8_h_lowpass_neon_10 +endfunc + +.macro h264_qpel_h_lowpass_10 type +function \type\()_h264_qpel16_h_lowpass_neon_10 + mov x13, x30 + mov x12, #32 + bl \type\()_h264_qpel8_h_lowpass_neon_10 + sub x0, x0, x3, lsl #4 + sub x1, x1, x2, lsl #4 + add x0, x0, #16 + add x1, x1, #16 + mov x12, #32 + mov x30, x13 +endfunc + +function \type\()_h264_qpel8_h_lowpass_neon_10 +1: ld1 {v28.8h, v29.8h}, [x1], x2 + ld1 {v16.8h, v17.8h}, [x1], x2 + subs x12, x12, #4 + lowpass_8_10 v28, v29, v16, v17, v28, v20 + .ifc \type,avg + ld1 {v2.8h}, [x0], x3 + ld1 {v3.8h}, [x0] + urhadd v28.8h, v28.8h, v2.8h + urhadd v20.8h, v20.8h, v3.8h + sub x0, x0, x3 + .endif + st1 {v28.8h}, [x0], x3 + st1 {v20.8h}, [x0], x3 + b.ne 1b + ret +endfunc +.endm + + h264_qpel_h_lowpass_10 put + h264_qpel_h_lowpass_10 avg + +.macro h264_qpel_h_lowpass_l2_10 type +function \type\()_h264_qpel16_h_lowpass_l2_neon_10 + mov x13, x30 + mov x12, #32 + bl \type\()_h264_qpel8_h_lowpass_l2_neon_10 + sub x0, x0, x2, lsl #4 + sub x1, x1, x2, lsl #4 + sub x3, x3, x2, lsl #4 + add x0, x0, #16 + add x1, x1, #16 + add x3, x3, #16 + mov x12, #32 + mov x30, x13 +endfunc + +function \type\()_h264_qpel8_h_lowpass_l2_neon_10 +1: ld1 {v26.8h, v27.8h}, [x1], x2 + ld1 {v16.8h, v17.8h}, [x1], x2 + ld1 {v28.8h}, [x3], x2 + ld1 {v29.8h}, [x3], x2 + subs x12, x12, #4 + lowpass_8_10 v26, v27, v16, v17, v26, v27 + urhadd v26.8h, v26.8h, v28.8h + urhadd v27.8h, v27.8h, v29.8h + .ifc \type,avg + ld1 {v2.8h}, [x0], x2 + ld1 {v3.8h}, [x0] + urhadd v26.8h, v26.8h, v2.8h + urhadd v27.8h, v27.8h, v3.8h + sub x0, x0, x2 + .endif + st1 {v26.8h}, [x0], x2 + st1 {v27.8h}, [x0], x2 + b.ne 1b + ret +endfunc +.endm + + h264_qpel_h_lowpass_l2_10 put + h264_qpel_h_lowpass_l2_10 avg + +function put_h264_qpel16_v_lowpass_neon_packed_10 + mov x4, x30 + mov x2, #8 + bl put_h264_qpel8_v_lowpass_neon + sub x1, x1, x3, lsl #2 + bl put_h264_qpel8_v_lowpass_neon + sub x1, x1, x3, lsl #4 + sub x1, x1, x3, lsl #2 + add x1, x1, #8 + bl put_h264_qpel8_v_lowpass_neon + sub x1, x1, x3, lsl #2 + mov x30, x4 + b put_h264_qpel8_v_lowpass_neon +endfunc + +.macro h264_qpel_v_lowpass_10 type +function \type\()_h264_qpel16_v_lowpass_neon_10 + mov x4, x30 + bl \type\()_h264_qpel8_v_lowpass_neon_10 + sub x1, x1, x3, lsl #2 + bl \type\()_h264_qpel8_v_lowpass_neon_10 + sub x0, x0, x2, lsl #4 + add x0, x0, #16 + sub x1, x1, x3, lsl #4 + sub x1, x1, x3, lsl #2 + add x1, x1, #16 + bl \type\()_h264_qpel8_v_lowpass_neon_10 + sub x1, x1, x3, lsl #2 + mov x30, x4 +endfunc + +function \type\()_h264_qpel8_v_lowpass_neon_10 + ld1 {v16.8h}, [x1], x3 + ld1 {v17.8h}, [x1], x3 + ld1 {v18.8h}, [x1], x3 + ld1 {v19.8h}, [x1], x3 + ld1 {v20.8h}, [x1], x3 + ld1 {v21.8h}, [x1], x3 + ld1 {v22.8h}, [x1], x3 + ld1 {v23.8h}, [x1], x3 + ld1 {v24.8h}, [x1], x3 + ld1 {v25.8h}, [x1], x3 + ld1 {v26.8h}, [x1], x3 + ld1 {v27.8h}, [x1], x3 + ld1 {v28.8h}, [x1] + + lowpass_8_10_v v16, v17, v18, v19, v20, v21, v22, v16, v17 + lowpass_8_10_v v18, v19, v20, v21, v22, v23, v24, v18, v19 + lowpass_8_10_v v20, v21, v22, v23, v24, v25, v26, v20, v21 + lowpass_8_10_v v22, v23, v24, v25, v26, v27, v28, v22, v23 + + .ifc \type,avg + ld1 {v24.8h}, [x0], x2 + ld1 {v25.8h}, [x0], x2 + ld1 {v26.8h}, [x0], x2 + urhadd v16.8h, v16.8h, v24.8h + ld1 {v27.8h}, [x0], x2 + urhadd v17.8h, v17.8h, v25.8h + ld1 {v28.8h}, [x0], x2 + urhadd v18.8h, v18.8h, v26.8h + ld1 {v29.8h}, [x0], x2 + urhadd v19.8h, v19.8h, v27.8h + ld1 {v30.8h}, [x0], x2 + urhadd v20.8h, v20.8h, v28.8h + ld1 {v31.8h}, [x0], x2 + urhadd v21.8h, v21.8h, v29.8h + urhadd v22.8h, v22.8h, v30.8h + urhadd v23.8h, v23.8h, v31.8h + sub x0, x0, x2, lsl #3 + .endif + + st1 {v16.8h}, [x0], x2 + st1 {v17.8h}, [x0], x2 + st1 {v18.8h}, [x0], x2 + st1 {v19.8h}, [x0], x2 + st1 {v20.8h}, [x0], x2 + st1 {v21.8h}, [x0], x2 + st1 {v22.8h}, [x0], x2 + st1 {v23.8h}, [x0], x2 + + ret +endfunc +.endm + + h264_qpel_v_lowpass_10 put + h264_qpel_v_lowpass_10 avg + +.macro h264_qpel_v_lowpass_l2_10 type +function \type\()_h264_qpel16_v_lowpass_l2_neon_10 + mov x4, x30 + bl \type\()_h264_qpel8_v_lowpass_l2_neon_10 + sub x1, x1, x3, lsl #2 + bl \type\()_h264_qpel8_v_lowpass_l2_neon_10 + sub x0, x0, x3, lsl #4 + sub x12, x12, x2, lsl #4 + add x0, x0, #16 + add x12, x12, #16 + sub x1, x1, x3, lsl #4 + sub x1, x1, x3, lsl #2 + add x1, x1, #16 + bl \type\()_h264_qpel8_v_lowpass_l2_neon_10 + sub x1, x1, x3, lsl #2 + mov x30, x4 +endfunc + +function \type\()_h264_qpel8_v_lowpass_l2_neon_10 + ld1 {v16.8h}, [x1], x3 + ld1 {v17.8h}, [x1], x3 + ld1 {v18.8h}, [x1], x3 + ld1 {v19.8h}, [x1], x3 + ld1 {v20.8h}, [x1], x3 + ld1 {v21.8h}, [x1], x3 + ld1 {v22.8h}, [x1], x3 + ld1 {v23.8h}, [x1], x3 + ld1 {v24.8h}, [x1], x3 + ld1 {v25.8h}, [x1], x3 + ld1 {v26.8h}, [x1], x3 + ld1 {v27.8h}, [x1], x3 + ld1 {v28.8h}, [x1] + + lowpass_8_10_v v16, v17, v18, v19, v20, v21, v22, v16, v17 + lowpass_8_10_v v18, v19, v20, v21, v22, v23, v24, v18, v19 + lowpass_8_10_v v20, v21, v22, v23, v24, v25, v26, v20, v21 + lowpass_8_10_v v22, v23, v24, v25, v26, v27, v28, v22, v23 + + ld1 {v24.8h}, [x12], x2 + ld1 {v25.8h}, [x12], x2 + ld1 {v26.8h}, [x12], x2 + ld1 {v27.8h}, [x12], x2 + ld1 {v28.8h}, [x12], x2 + urhadd v16.8h, v24.8h, v16.8h + urhadd v17.8h, v25.8h, v17.8h + ld1 {v29.8h}, [x12], x2 + urhadd v18.8h, v26.8h, v18.8h + urhadd v19.8h, v27.8h, v19.8h + ld1 {v30.8h}, [x12], x2 + urhadd v20.8h, v28.8h, v20.8h + urhadd v21.8h, v29.8h, v21.8h + ld1 {v31.8h}, [x12], x2 + urhadd v22.8h, v30.8h, v22.8h + urhadd v23.8h, v31.8h, v23.8h + + .ifc \type,avg + ld1 {v24.8h}, [x0], x3 + ld1 {v25.8h}, [x0], x3 + ld1 {v26.8h}, [x0], x3 + urhadd v16.8h, v16.8h, v24.8h + ld1 {v27.8h}, [x0], x3 + urhadd v17.8h, v17.8h, v25.8h + ld1 {v28.8h}, [x0], x3 + urhadd v18.8h, v18.8h, v26.8h + ld1 {v29.8h}, [x0], x3 + urhadd v19.8h, v19.8h, v27.8h + ld1 {v30.8h}, [x0], x3 + urhadd v20.8h, v20.8h, v28.8h + ld1 {v31.8h}, [x0], x3 + urhadd v21.8h, v21.8h, v29.8h + urhadd v22.8h, v22.8h, v30.8h + urhadd v23.8h, v23.8h, v31.8h + sub x0, x0, x3, lsl #3 + .endif + + st1 {v16.8h}, [x0], x3 + st1 {v17.8h}, [x0], x3 + st1 {v18.8h}, [x0], x3 + st1 {v19.8h}, [x0], x3 + st1 {v20.8h}, [x0], x3 + st1 {v21.8h}, [x0], x3 + st1 {v22.8h}, [x0], x3 + st1 {v23.8h}, [x0], x3 + + ret +endfunc +.endm + + h264_qpel_v_lowpass_l2_10 put + h264_qpel_v_lowpass_l2_10 avg + +.macro h264_qpel8_10 type +function ff_\type\()_h264_qpel8_mc10_neon_10, export=1 + lowpass_const w3 + mov x3, x1 + sub x1, x1, #4 + mov x12, #16 + b \type\()_h264_qpel8_h_lowpass_l2_neon_10 +endfunc + +function ff_\type\()_h264_qpel8_mc20_neon_10, export=1 + lowpass_const w3 + sub x1, x1, #4 + mov x3, x2 + mov x12, #16 + b \type\()_h264_qpel8_h_lowpass_neon_10 +endfunc + +function ff_\type\()_h264_qpel8_mc30_neon_10, export=1 + lowpass_const w3 + add x3, x1, #2 + sub x1, x1, #4 + mov x12, #16 + b \type\()_h264_qpel8_h_lowpass_l2_neon_10 +endfunc + +function ff_\type\()_h264_qpel8_mc01_neon_10, export=1 + mov x14, x30 + mov x12, x1 +\type\()_h264_qpel8_mc01_10: + lowpass_const w3 + mov x3, x2 + sub x1, x1, x2, lsl #1 + bl \type\()_h264_qpel8_v_lowpass_l2_neon_10 + ret x14 +endfunc + +function ff_\type\()_h264_qpel8_mc11_neon_10, export=1 + mov x14, x30 + mov x8, x0 + mov x9, x1 +\type\()_h264_qpel8_mc11_10: + lowpass_const w3 + mov x11, sp + sub sp, sp, #128 + mov x0, sp + sub x1, x1, #4 + mov x3, #16 + mov x12, #16 + bl put_h264_qpel8_h_lowpass_neon_10 + mov x0, x8 + mov x3, x2 + mov x12, sp + sub x1, x9, x2, lsl #1 + mov x2, #16 + bl \type\()_h264_qpel8_v_lowpass_l2_neon_10 + mov sp, x11 + ret x14 +endfunc + +function ff_\type\()_h264_qpel8_mc31_neon_10, export=1 + add x1, x1, #2 + mov x14, x30 + mov x8, x0 + mov x9, x1 + sub x1, x1, #2 + b \type\()_h264_qpel8_mc11_10 +endfunc + +function ff_\type\()_h264_qpel8_mc02_neon_10, export=1 + mov x14, x30 + lowpass_const w3 + sub x1, x1, x2, lsl #1 + mov x3, x2 + bl \type\()_h264_qpel8_v_lowpass_neon_10 + ret x14 +endfunc + +function ff_\type\()_h264_qpel8_mc03_neon_10, export=1 + mov x14, x30 + add x12, x1, x2 + b \type\()_h264_qpel8_mc01_10 +endfunc + +function ff_\type\()_h264_qpel8_mc13_neon_10, export=1 + mov x14, x30 + mov x8, x0 + mov x9, x1 + add x1, x1, x2 + b \type\()_h264_qpel8_mc11_10 +endfunc + +function ff_\type\()_h264_qpel8_mc33_neon_10, export=1 + add x1, x1, #2 + mov x14, x30 + mov x8, x0 + mov x9, x1 + add x1, x1, x2 + sub x1, x1, #2 + b \type\()_h264_qpel8_mc11_10 +endfunc +.endm + + h264_qpel8_10 put + h264_qpel8_10 avg + +.macro h264_qpel16_10 type +function ff_\type\()_h264_qpel16_mc10_neon_10, export=1 + lowpass_const w3 + mov x3, x1 + sub x1, x1, #4 + b \type\()_h264_qpel16_h_lowpass_l2_neon_10 +endfunc + +function ff_\type\()_h264_qpel16_mc20_neon_10, export=1 + lowpass_const w3 + sub x1, x1, #4 + mov x3, x2 + b \type\()_h264_qpel16_h_lowpass_neon_10 +endfunc + +function ff_\type\()_h264_qpel16_mc30_neon_10, export=1 + lowpass_const w3 + add x3, x1, #2 + sub x1, x1, #4 + b \type\()_h264_qpel16_h_lowpass_l2_neon_10 +endfunc + +function ff_\type\()_h264_qpel16_mc01_neon_10, export=1 + mov x14, x30 + mov x12, x1 +\type\()_h264_qpel16_mc01_10: + lowpass_const w3 + mov x3, x2 + sub x1, x1, x2, lsl #1 + bl \type\()_h264_qpel16_v_lowpass_l2_neon_10 + ret x14 +endfunc + +function ff_\type\()_h264_qpel16_mc11_neon_10, export=1 + mov x14, x30 + mov x8, x0 + mov x9, x1 +\type\()_h264_qpel16_mc11_10: + lowpass_const w3 + mov x11, sp + sub sp, sp, #512 + mov x0, sp + sub x1, x1, #4 + mov x3, #32 + bl put_h264_qpel16_h_lowpass_neon_10 + mov x0, x8 + mov x3, x2 + mov x12, sp + sub x1, x9, x2, lsl #1 + mov x2, #32 + bl \type\()_h264_qpel16_v_lowpass_l2_neon_10 + mov sp, x11 + ret x14 +endfunc + +function ff_\type\()_h264_qpel16_mc31_neon_10, export=1 + add x1, x1, #2 + mov x14, x30 + mov x8, x0 + mov x9, x1 + sub x1, x1, #2 + b \type\()_h264_qpel16_mc11_10 +endfunc + +function ff_\type\()_h264_qpel16_mc02_neon_10, export=1 + mov x14, x30 + lowpass_const w3 + sub x1, x1, x2, lsl #1 + mov x3, x2 + bl \type\()_h264_qpel16_v_lowpass_neon_10 + ret x14 +endfunc + +function ff_\type\()_h264_qpel16_mc03_neon_10, export=1 + mov x14, x30 + add x12, x1, x2 + b \type\()_h264_qpel16_mc01_10 +endfunc + +function ff_\type\()_h264_qpel16_mc13_neon_10, export=1 + mov x14, x30 + mov x8, x0 + mov x9, x1 + add x1, x1, x2 + b \type\()_h264_qpel16_mc11_10 +endfunc + +function ff_\type\()_h264_qpel16_mc33_neon_10, export=1 + add x1, x1, #2 + mov x14, x30 + mov x8, x0 + mov x9, x1 + add x1, x1, x2 + sub x1, x1, #2 + b \type\()_h264_qpel16_mc11_10 +endfunc +.endm + + h264_qpel16_10 put + h264_qpel16_10 avg diff --git a/libavcodec/aarch64/hevcdsp_deblock_neon.S b/libavcodec/aarch64/hevcdsp_deblock_neon.S new file mode 100644 index 00000000000..581056a91e6 --- /dev/null +++ b/libavcodec/aarch64/hevcdsp_deblock_neon.S @@ -0,0 +1,600 @@ +/* -*-arm64-*- + * vim: syntax=arm64asm + * + * Copyright (c) 2014 Seppo Tomperi + * Copyright (c) 2023 J. Dekker + * + * 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/aarch64/asm.S" +#include "neon.S" + +.macro hevc_loop_filter_chroma_start bitdepth + mov x4, x30 + ldr w14, [x2] + ldr w15, [x2, #4] +.if \bitdepth > 8 + lsl w14, w14, #(\bitdepth - 8) + lsl w15, w15, #(\bitdepth - 8) +.endif + adds w2, w14, w15 + b.eq 1f + dup v16.4h, w14 + dup v17.4h, w15 + trn1 v16.2d, v16.2d, v17.2d +.if \bitdepth > 8 + mvni v19.8h, #((0xff << (\bitdepth - 8)) & 0xff), lsl #8 + movi v18.8h, #0 +.endif + neg v17.8h, v16.8h +.endm + +.macro hevc_loop_filter_chroma_body bitdepth +.if \bitdepth <= 8 + uxtl v20.8h, v0.8b // p1 + uxtl v1.8h, v1.8b // p0 + uxtl v2.8h, v2.8b // q0 + uxtl v23.8h, v3.8b // q1 + va .req v20 + vb .req v23 +.else // required to specify both cases as we are unable to do: v0 .req v20 + va .req v0 + vb .req v3 +.endif + sub v5.8h, v2.8h, v1.8h // q0 - p0 + sub v6.8h, va.8h, vb.8h // p1 - q1 + shl v5.8h, v5.8h, #2 + add v5.8h, v6.8h, v5.8h + srshr v5.8h, v5.8h, #3 + clip v17.8h, v16.8h, v5.8h + sqadd v1.8h, v1.8h, v5.8h // p0 + delta + sqsub v2.8h, v2.8h, v5.8h // q0 - delta +.if \bitdepth <= 8 + sqxtun v1.8b, v1.8h + sqxtun v2.8b, v2.8h +.else + clip v18.8h, v19.8h, v1.8h, v2.8h +.endif +.unreq va +.unreq vb +.endm + +function hevc_loop_filter_chroma_body_8_neon, export=0 + hevc_loop_filter_chroma_body 8 + ret +endfunc + +function hevc_loop_filter_chroma_body_10_neon, export=0 +hevc_loop_filter_chroma_body_12_neon: + hevc_loop_filter_chroma_body 10 + ret +endfunc + +// void ff_hevc_h_loop_filter_chroma_8_neon(uint8_t *_pix, ptrdiff_t _stride, int *_tc, uint8_t *_no_p, uint8_t *_no_q); + +.macro hevc_h_loop_filter_chroma bitdepth +function ff_hevc_h_loop_filter_chroma_\bitdepth\()_neon, export=1 + hevc_loop_filter_chroma_start \bitdepth + sub x0, x0, x1, lsl #1 +.if \bitdepth > 8 + ld1 {v0.8h}, [x0], x1 + ld1 {v1.8h}, [x0], x1 + ld1 {v2.8h}, [x0], x1 + ld1 {v3.8h}, [x0] +.else + ld1 {v0.8b}, [x0], x1 + ld1 {v1.8b}, [x0], x1 + ld1 {v2.8b}, [x0], x1 + ld1 {v3.8b}, [x0] +.endif + sub x0, x0, x1, lsl #1 + bl hevc_loop_filter_chroma_body_\bitdepth\()_neon +.if \bitdepth > 8 + st1 {v1.8h}, [x0], x1 + st1 {v2.8h}, [x0] +.else + st1 {v1.8b}, [x0], x1 + st1 {v2.8b}, [x0] +.endif +1: ret x4 +endfunc +.endm + +.macro hevc_v_loop_filter_chroma bitdepth +function ff_hevc_v_loop_filter_chroma_\bitdepth\()_neon, export=1 + hevc_loop_filter_chroma_start \bitdepth +.if \bitdepth > 8 + sub x0, x0, #4 + add x3, x0, x1 + lsl x1, x1, #1 + ld1 {v0.d}[0], [x0], x1 + ld1 {v1.d}[0], [x3], x1 + ld1 {v2.d}[0], [x0], x1 + ld1 {v3.d}[0], [x3], x1 + ld1 {v0.d}[1], [x0], x1 + ld1 {v1.d}[1], [x3], x1 + ld1 {v2.d}[1], [x0], x1 + ld1 {v3.d}[1], [x3], x1 + transpose_4x8H v0, v1, v2, v3, v28, v29, v30, v31 +.else + sub x0, x0, #2 + add x3, x0, x1 + lsl x1, x1, #1 + ld1 {v0.s}[0], [x0], x1 + ld1 {v1.s}[0], [x3], x1 + ld1 {v2.s}[0], [x0], x1 + ld1 {v3.s}[0], [x3], x1 + ld1 {v0.s}[1], [x0], x1 + ld1 {v1.s}[1], [x3], x1 + ld1 {v2.s}[1], [x0], x1 + ld1 {v3.s}[1], [x3], x1 + transpose_4x8B v0, v1, v2, v3, v28, v29, v30, v31 +.endif + sub x0, x0, x1, lsl #2 + sub x3, x3, x1, lsl #2 + bl hevc_loop_filter_chroma_body_\bitdepth\()_neon +.if \bitdepth > 8 + transpose_4x8H v0, v1, v2, v3, v28, v29, v30, v31 + st1 {v0.d}[0], [x0], x1 + st1 {v1.d}[0], [x3], x1 + st1 {v2.d}[0], [x0], x1 + st1 {v3.d}[0], [x3], x1 + st1 {v0.d}[1], [x0], x1 + st1 {v1.d}[1], [x3], x1 + st1 {v2.d}[1], [x0], x1 + st1 {v3.d}[1], [x3] +.else + transpose_4x8B v0, v1, v2, v3, v28, v29, v30, v31 + st1 {v0.s}[0], [x0], x1 + st1 {v1.s}[0], [x3], x1 + st1 {v2.s}[0], [x0], x1 + st1 {v3.s}[0], [x3], x1 + st1 {v0.s}[1], [x0], x1 + st1 {v1.s}[1], [x3], x1 + st1 {v2.s}[1], [x0], x1 + st1 {v3.s}[1], [x3] +.endif +1: ret x4 +endfunc +.endm + +hevc_h_loop_filter_chroma 8 +hevc_h_loop_filter_chroma 10 +hevc_h_loop_filter_chroma 12 + +hevc_v_loop_filter_chroma 8 +hevc_v_loop_filter_chroma 10 +hevc_v_loop_filter_chroma 12 + +.macro hevc_loop_filter_luma_body bitdepth +function hevc_loop_filter_luma_body_\bitdepth\()_neon, export=0 +.if \bitdepth > 8 + lsl w2, w2, #(\bitdepth - 8) // beta <<= BIT_DEPTH - 8 +.else + uxtl v0.8h, v0.8b + uxtl v1.8h, v1.8b + uxtl v2.8h, v2.8b + uxtl v3.8h, v3.8b + uxtl v4.8h, v4.8b + uxtl v5.8h, v5.8b + uxtl v6.8h, v6.8b + uxtl v7.8h, v7.8b +.endif + ldr w7, [x3] // tc[0] + ldr w8, [x3, #4] // tc[1] + dup v18.4h, w7 + dup v19.4h, w8 + trn1 v18.2d, v18.2d, v19.2d +.if \bitdepth > 8 + shl v18.8h, v18.8h, #(\bitdepth - 8) +.endif + dup v27.8h, w2 // beta + // tc25 + shl v19.8h, v18.8h, #2 // * 4 + add v19.8h, v19.8h, v18.8h // (tc * 5) + srshr v19.8h, v19.8h, #1 // (tc * 5 + 1) >> 1 + sshr v17.8h, v27.8h, #2 // beta2 + + ////// beta_2 check + // dp0 = abs(P2 - 2 * P1 + P0) + add v22.8h, v3.8h, v1.8h + shl v23.8h, v2.8h, #1 + sabd v30.8h, v22.8h, v23.8h + // dq0 = abs(Q2 - 2 * Q1 + Q0) + add v21.8h, v6.8h, v4.8h + shl v26.8h, v5.8h, #1 + sabd v31.8h, v21.8h, v26.8h + // d0 = dp0 + dq0 + add v20.8h, v30.8h, v31.8h + shl v25.8h, v20.8h, #1 + // (d0 << 1) < beta_2 + cmgt v23.8h, v17.8h, v25.8h + + ////// beta check + // d0 + d3 < beta + mov x9, #0xFFFF00000000FFFF + dup v24.2d, x9 + and v25.16b, v24.16b, v20.16b + addp v25.8h, v25.8h, v25.8h // 1+0 0+1 1+0 0+1 + addp v25.4h, v25.4h, v25.4h // 1+0+0+1 1+0+0+1 + cmgt v25.4h, v27.4h, v25.4h // lower/upper mask in h[0/1] + mov w9, v25.s[0] + cmp w9, #0 + sxtl v26.4s, v25.4h + sxtl v16.2d, v26.2s // full skip mask + b.eq 3f // skip both blocks + + // TODO: we can check the full skip mask with the weak/strong mask to + // potentially skip weak or strong calculation entirely if we only have one + + ////// beta_3 check + // abs(P3 - P0) + abs(Q3 - Q0) < beta_3 + sshr v17.8h, v17.8h, #1 // beta_3 + sabd v20.8h, v0.8h, v3.8h + saba v20.8h, v7.8h, v4.8h + cmgt v21.8h, v17.8h, v20.8h + + and v23.16b, v23.16b, v21.16b + + ////// tc25 check + // abs(P0 - Q0) < tc25 + sabd v20.8h, v3.8h, v4.8h + cmgt v21.8h, v19.8h, v20.8h + + and v23.16b, v23.16b, v21.16b + + ////// Generate low/high line max from lines 0/3/4/7 + // mask out lines 2/3/5/6 + not v20.16b, v24.16b // 0x0000FFFFFFFF0000 + orr v23.16b, v23.16b, v20.16b + + // generate weak/strong mask + uminp v23.8h, v23.8h, v23.8h // extend to singles + sxtl v23.4s, v23.4h + uminp v26.4s, v23.4s, v23.4s // check lines + // extract to gpr + ext v25.16b, v26.16b, v26.16b, #2 + zip1 v17.4s, v26.4s, v26.4s + mov w12, v25.s[0] + mov w11, #0x0000FFFF + mov w13, #0xFFFF0000 + // FFFF FFFF -> strong strong + // FFFF 0000 -> strong weak + // 0000 FFFF -> weak strong + // 0000 0000 -> weak weak + cmp w12, w13 + b.hi 0f // only strong/strong, skip weak nd_p/nd_q calc + + ////// weak nd_p/nd_q + // d0+d3 + and v30.16b, v30.16b, v24.16b // d0 __ __ d3 d4 __ __ d7 + and v31.16b, v31.16b, v24.16b + addp v30.8h, v30.8h, v30.8h // [d0+__ __+d3 d4+__ __+d7] [ ... ] + addp v31.8h, v31.8h, v31.8h // [d0+d3 d4+d7] + addp v30.4h, v30.4h, v30.4h + addp v31.4h, v31.4h, v31.4h + + // ((beta + (beta >> 1)) >> 3) + sshr v21.8h, v27.8h, #1 + add v21.8h, v21.8h, v27.8h + sshr v21.8h, v21.8h, #3 + + // nd_p = dp0 + dp3 < ((beta + (beta >> 1)) >> 3) + cmgt v30.8h, v21.8h, v30.8h + // nd_q = dq0 + dq3 < ((beta + (beta >> 1)) >> 3) + cmgt v31.8h, v21.8h, v31.8h + + sxtl v30.4s, v30.4h + sxtl v31.4s, v31.4h + sxtl v28.2d, v30.2s + sxtl v29.2d, v31.2s + + cmp w12, w11 + b.lo 1f // can only be weak weak, skip strong + +0: // STRONG FILTER + + // P0 = p0 + av_clip(((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3) - p0, -tc3, tc3); + add v21.8h, v2.8h, v3.8h // (p1 + p0 + add v21.8h, v4.8h, v21.8h // + q0) + shl v21.8h, v21.8h, #1 // * 2 + add v22.8h, v1.8h, v5.8h // (p2 + q1) + add v21.8h, v22.8h, v21.8h // + + srshr v21.8h, v21.8h, #3 // >> 3 + sub v21.8h, v21.8h, v3.8h // - p0 + + // P1 = p1 + av_clip(((p2 + p1 + p0 + q0 + 2) >> 2) - p1, -tc2, tc2); + + add v22.8h, v1.8h, v2.8h + add v23.8h, v3.8h, v4.8h + add v22.8h, v22.8h, v23.8h + srshr v22.8h, v22.8h, #2 + sub v22.8h, v22.8h, v2.8h + + // P2 = p2 + av_clip(((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3) - p2, -tc, tc); + + add v23.8h, v0.8h, v1.8h // p3 + p2 + add v24.8h, v3.8h, v4.8h // p0 + q0 + shl v23.8h, v23.8h, #1 // * 2 + add v23.8h, v23.8h, v24.8h + add v24.8h, v1.8h, v2.8h // p2 + p1 + add v23.8h, v23.8h, v24.8h + srshr v23.8h, v23.8h, #3 + sub v23.8h, v23.8h, v1.8h + + // Q0 = q0 + av_clip(((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3) - q0, -tc3, tc3); + add v24.8h, v3.8h, v4.8h // (p0 + q0 + add v24.8h, v5.8h, v24.8h // + q1) + shl v24.8h, v24.8h, #1 // * 2 + add v25.8h, v2.8h, v6.8h // (p1 + q2) + add v24.8h, v25.8h, v24.8h // + + srshr v24.8h, v24.8h, #3 // >> 3 + sub v24.8h, v24.8h, v4.8h // - q0 + + // Q1 = q1 + av_clip(((p0 + q0 + q1 + q2 + 2) >> 2) - q1, -tc2, tc2); + + add v25.8h, v6.8h, v5.8h + add v26.8h, v3.8h, v4.8h + add v25.8h, v25.8h, v26.8h + srshr v25.8h, v25.8h, #2 + sub v25.8h, v25.8h, v5.8h + + // Q2 = q2 + av_clip(((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3) - q2, -tc, tc); + + add v26.8h, v7.8h, v6.8h + add v27.8h, v6.8h, v5.8h + shl v26.8h, v26.8h, #1 + add v26.8h, v26.8h, v27.8h + add v27.8h, v3.8h, v4.8h + add v26.8h, v26.8h, v27.8h + srshr v26.8h, v26.8h, #3 + sub v26.8h, v26.8h, v6.8h + + // this clip should work properly + shl v30.8h, v18.8h, #1 // tc2 + neg v31.8h, v30.8h // -tc2 + clip v31.8h, v30.8h, v21.8h, v22.8h, v23.8h, v24.8h, v25.8h, v26.8h + + and v21.16b, v21.16b, v16.16b + and v22.16b, v22.16b, v16.16b + and v23.16b, v23.16b, v16.16b + and v24.16b, v24.16b, v16.16b + and v25.16b, v25.16b, v16.16b + and v26.16b, v26.16b, v16.16b + + add v23.8h, v23.8h, v1.8h // careful + add v22.8h, v22.8h, v2.8h + add v21.8h, v21.8h, v3.8h + add v24.8h, v24.8h, v4.8h + add v25.8h, v25.8h, v5.8h + add v26.8h, v26.8h, v6.8h + + cmp w12, w13 + b.hi 2f // only strong/strong, skip weak + +1: // WEAK FILTER + + // delta0 = (9 * (q0 - p0) - 3 * (q1 - p1) + 8) >> 4 +.if \bitdepth < 12 + sub v27.8h, v4.8h, v3.8h // q0 - p0 + shl v30.8h, v27.8h, #3 // * 8 + add v27.8h, v27.8h, v30.8h // 9 * (q0 - p0) + + sub v30.8h, v5.8h, v2.8h // q1 - p1 + shl v31.8h, v30.8h, #1 // * 2 + + sub v27.8h, v27.8h, v31.8h + sub v27.8h, v27.8h, v30.8h // - 3 * (q1 - p1) + srshr v27.8h, v27.8h, #4 +.else + sub v19.8h, v4.8h, v3.8h // q0 - p0 + sub v20.8h, v5.8h, v2.8h // q1 - p1 + + sshll v30.4s, v19.4h, #3 // * 8 + sshll2 v31.4s, v19.8h, #3 + + shl v27.8h, v20.8h, #1 + + saddw v30.4s, v30.4s, v19.4h // 9 * (q0 - p0) + saddw2 v31.4s, v31.4s, v19.8h + + saddl v19.4s, v27.4h, v20.4h // 3 * (q1 - p1) + saddl2 v20.4s, v27.8h, v20.8h + + sub v19.4s, v30.4s, v19.4s + sub v20.4s, v31.4s, v20.4s + + sqrshrn v27.4h, v19.4s, #4 + sqrshrn2 v27.8h, v20.4s, #4 +.endif + + // delta0 10tc check mask + shl v30.8h, v18.8h, #1 // * 2 + shl v31.8h, v18.8h, #3 // * 8 + add v30.8h, v30.8h, v31.8h // 10 * tc + abs v31.8h, v27.8h + cmgt v20.8h, v30.8h, v31.8h // abs(delta0) < 10 * tc + + and v20.16b, v20.16b, v16.16b // combine with full mask + + neg v31.8h, v18.8h // -tc + clip v31.8h, v18.8h, v27.8h // delta0 = av_clip(delta0, -tc, tc) + + // deltap1 = av_clip((((p2 + p0 + 1) >> 1) - p1 + delta0) >> 1, -tc_2, tc_2) + add v30.8h, v1.8h, v3.8h + srshr v30.8h, v30.8h, #1 + sub v30.8h, v30.8h, v2.8h + add v30.8h, v30.8h, v27.8h + sshr v30.8h, v30.8h, #1 + + // p3 p2 p1 p0 q0 q1 q2 q3 + // v0 v1 v2 v3 v4 v5 v6 v7 + + // deltaq1 = av_clip((((q2 + q0 + 1) >> 1) - q1 - delta0) >> 1, -tc_2, tc_2); + add v31.8h, v6.8h, v4.8h + srshr v31.8h, v31.8h, #1 + sub v31.8h, v31.8h, v5.8h + sub v31.8h, v31.8h, v27.8h + sshr v31.8h, v31.8h, #1 + + // apply nd_p nd_q mask to deltap1/deltaq1 + and v30.16b, v30.16b, v28.16b + and v31.16b, v31.16b, v29.16b + + // apply full skip mask to deltap1/deltaq1/delta0 + and v30.16b, v30.16b, v20.16b + and v27.16b, v27.16b, v20.16b + and v31.16b, v31.16b, v20.16b + + // clip P1/Q1 to -tc_2, tc_2 + sshr v18.8h, v18.8h, #1 // tc2 + neg v28.8h, v18.8h + clip v28.8h, v18.8h, v30.8h, v31.8h + + // P0 = av_clip_pixel(p0 + delta0) + // Q0 = av_clip_pixel(q0 - delta0) + add v29.8h, v3.8h, v27.8h // P0 + sub v27.8h, v4.8h, v27.8h // Q0 + + // P1 = av_clip_pixel(p1 + deltap1) + // Q1 = av_clip_pixel(q1 + deltaq1) + add v30.8h, v2.8h, v30.8h // P1 + add v31.8h, v5.8h, v31.8h // Q1 + +2: // MIX WEAK/STRONG + + mov v19.16b, v1.16b + mov v20.16b, v6.16b + // copy selection mask + mov v1.16b, v17.16b + mov v2.16b, v17.16b + mov v3.16b, v17.16b + mov v4.16b, v17.16b + mov v5.16b, v17.16b + mov v6.16b, v17.16b + // select + bsl v1.16b, v23.16b, v19.16b // P2 strong/orig + bsl v2.16b, v22.16b, v30.16b // P1 strong/weak + bsl v3.16b, v21.16b, v29.16b // P0 strong/weak + bsl v4.16b, v24.16b, v27.16b // Q0 strong/weak + bsl v5.16b, v25.16b, v31.16b // Q1 strong/weak + bsl v6.16b, v26.16b, v20.16b // Q2 strong/orig + // NOTE: Q3/P3 are unchanged + +.if \bitdepth > 8 + movi v19.8h, #0 + dup v20.8h, w14 + clip v19.8h, v20.8h, v1.8h, v2.8h, v3.8h, v4.8h, v5.8h, v6.8h +.else + sqxtun v0.8b, v0.8h + sqxtun v1.8b, v1.8h + sqxtun v2.8b, v2.8h + sqxtun v3.8b, v3.8h + sqxtun v4.8b, v4.8h + sqxtun v5.8b, v5.8h + sqxtun v6.8b, v6.8h + sqxtun v7.8b, v7.8h +.endif + ret +3: ret x6 +endfunc +.endm + +hevc_loop_filter_luma_body 8 +hevc_loop_filter_luma_body 10 +hevc_loop_filter_luma_body 12 + +// hevc_v_loop_filter_luma(uint8_t *pix, ptrdiff_t stride, int beta, const int32_t *tc, const uint8_t *no_p, const uint8_t *no_q) + +.macro hevc_loop_filter_luma dir, bitdepth +function ff_hevc_\dir\()_loop_filter_luma_\bitdepth\()_neon, export=1 + mov x6, x30 +.ifc \dir, v +.if \bitdepth > 8 + sub x0, x0, #8 +.else + sub x0, x0, #4 +.endif +.else + sub x0, x0, x1, lsl #2 // -4 * xstride +.endif + mov x10, x0 +.if \bitdepth > 8 + ld1 {v0.8h}, [x0], x1 + ld1 {v1.8h}, [x0], x1 + ld1 {v2.8h}, [x0], x1 + ld1 {v3.8h}, [x0], x1 + ld1 {v4.8h}, [x0], x1 + ld1 {v5.8h}, [x0], x1 + ld1 {v6.8h}, [x0], x1 + ld1 {v7.8h}, [x0] + mov w14, #((1 << \bitdepth) - 1) +.ifc \dir, v + transpose_8x8H v0, v1, v2, v3, v4, v5, v6, v7, v16, v17 +.endif +.else + ld1 {v0.8b}, [x0], x1 + ld1 {v1.8b}, [x0], x1 + ld1 {v2.8b}, [x0], x1 + ld1 {v3.8b}, [x0], x1 + ld1 {v4.8b}, [x0], x1 + ld1 {v5.8b}, [x0], x1 + ld1 {v6.8b}, [x0], x1 + ld1 {v7.8b}, [x0] +.ifc \dir, v + transpose_8x8B v0, v1, v2, v3, v4, v5, v6, v7, v16, v17 +.endif +.endif + bl hevc_loop_filter_luma_body_\bitdepth\()_neon +.if \bitdepth > 8 +.ifc \dir, v + transpose_8x8H v0, v1, v2, v3, v4, v5, v6, v7, v16, v17 +.endif + st1 {v0.8h}, [x10], x1 + st1 {v1.8h}, [x10], x1 + st1 {v2.8h}, [x10], x1 + st1 {v3.8h}, [x10], x1 + st1 {v4.8h}, [x10], x1 + st1 {v5.8h}, [x10], x1 + st1 {v6.8h}, [x10], x1 + st1 {v7.8h}, [x10] +.else +.ifc \dir, v + transpose_8x8B v0, v1, v2, v3, v4, v5, v6, v7, v16, v17 +.endif + st1 {v0.8b}, [x10], x1 + st1 {v1.8b}, [x10], x1 + st1 {v2.8b}, [x10], x1 + st1 {v3.8b}, [x10], x1 + st1 {v4.8b}, [x10], x1 + st1 {v5.8b}, [x10], x1 + st1 {v6.8b}, [x10], x1 + st1 {v7.8b}, [x10] +.endif + ret x6 +endfunc +.endm + +hevc_loop_filter_luma h, 8 +hevc_loop_filter_luma h, 10 +hevc_loop_filter_luma h, 12 + +hevc_loop_filter_luma v, 8 +hevc_loop_filter_luma v, 10 +hevc_loop_filter_luma v, 12 diff --git a/libavcodec/aarch64/hevcdsp_epel_neon.S b/libavcodec/aarch64/hevcdsp_epel_neon.S new file mode 100644 index 00000000000..378b0f7fb2a --- /dev/null +++ b/libavcodec/aarch64/hevcdsp_epel_neon.S @@ -0,0 +1,4668 @@ +/* -*-arm64-*- + * vim: syntax=arm64asm + * + * 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/aarch64/asm.S" +#define MAX_PB_SIZE 64 + +const epel_filters, align=4 + .byte 0, 0, 0, 0 + .byte -2, 58, 10, -2 + .byte -4, 54, 16, -2 + .byte -6, 46, 28, -4 + .byte -4, 36, 36, -4 + .byte -4, 28, 46, -6 + .byte -2, 16, 54, -4 + .byte -2, 10, 58, -2 +endconst + +const epel_filters_abs, align=4 + .byte 0, 0, 0, 0 + .byte 2, 58, 10, 2 + .byte 4, 54, 16, 2 + .byte 6, 46, 28, 4 + .byte 4, 36, 36, 4 + .byte 4, 28, 46, 6 + .byte 2, 16, 54, 4 + .byte 2, 10, 58, 2 +endconst + + +.macro load_epel_filterb freg, xreg + movrel \xreg, epel_filters_abs + add \xreg, \xreg, \freg, lsl #2 + ld4r {v0.16b, v1.16b, v2.16b, v3.16b}, [\xreg] // filter +.endm + +.macro calc_epelb dst, src0, src1, src2, src3 + umull \dst\().8h, \src1\().8b, v1.8b + umlsl \dst\().8h, \src0\().8b, v0.8b + umlal \dst\().8h, \src2\().8b, v2.8b + umlsl \dst\().8h, \src3\().8b, v3.8b +.endm + +.macro calc_epelb2 dst, src0, src1, src2, src3 + umull2 \dst\().8h, \src1\().16b, v1.16b + umlsl2 \dst\().8h, \src0\().16b, v0.16b + umlal2 \dst\().8h, \src2\().16b, v2.16b + umlsl2 \dst\().8h, \src3\().16b, v3.16b +.endm + +.macro load_epel_filterh freg, xreg + movrel \xreg, epel_filters + add \xreg, \xreg, \freg, lsl #2 + ld1 {v0.8b}, [\xreg] + sxtl v0.8h, v0.8b +.endm + +.macro calc_epelh dst, src0, src1, src2, src3 + smull \dst\().4s, \src0\().4h, v0.h[0] + smlal \dst\().4s, \src1\().4h, v0.h[1] + smlal \dst\().4s, \src2\().4h, v0.h[2] + smlal \dst\().4s, \src3\().4h, v0.h[3] + sqshrn \dst\().4h, \dst\().4s, #6 +.endm + +.macro calc_epelh2 dst, tmp, src0, src1, src2, src3 + smull2 \tmp\().4s, \src0\().8h, v0.h[0] + smlal2 \tmp\().4s, \src1\().8h, v0.h[1] + smlal2 \tmp\().4s, \src2\().8h, v0.h[2] + smlal2 \tmp\().4s, \src3\().8h, v0.h[3] + sqshrn2 \dst\().8h, \tmp\().4s, #6 +.endm + +.macro calc_all4 + calc v16, v17, v18, v19 + b.eq 2f + calc v17, v18, v19, v16 + b.eq 2f + calc v18, v19, v16, v17 + b.eq 2f + calc v19, v16, v17, v18 + b.ne 1b +.endm + +.macro calc_all8 + calc v16, v17, v18, v19, v20, v21, v22, v23 + b.eq 2f + calc v18, v19, v20, v21, v22, v23, v16, v17 + b.eq 2f + calc v20, v21, v22, v23, v16, v17, v18, v19 + b.eq 2f + calc v22, v23, v16, v17, v18, v19, v20, v21 + b.ne 1b +.endm + +.macro calc_all12 + calc v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27 + b.eq 2f + calc v19, v20, v21, v22, v23, v24, v25, v26, v27, v16, v17, v18 + b.eq 2f + calc v22, v23, v24, v25, v26, v27, v16, v17, v18, v19, v20, v21 + b.eq 2f + calc v25, v26, v27, v16, v17, v18, v19, v20, v21, v22, v23, v24 + b.ne 1b +.endm + +.macro calc_all16 + calc v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31 + b.eq 2f + calc v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v16, v17, v18, v19 + b.eq 2f + calc v24, v25, v26, v27, v28, v29, v30, v31, v16, v17, v18, v19, v20, v21, v22, v23 + b.eq 2f + calc v28, v29, v30, v31, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27 + b.ne 1b +.endm + +function ff_hevc_put_hevc_pel_pixels4_8_neon, export=1 + mov x7, #(MAX_PB_SIZE * 2) +1: ld1 {v0.s}[0], [x1], x2 + ushll v4.8h, v0.8b, #6 + subs w3, w3, #1 + st1 {v4.d}[0], [x0], x7 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_pixels6_8_neon, export=1 + mov x7, #(MAX_PB_SIZE * 2 - 8) +1: ld1 {v0.8b}, [x1], x2 + ushll v4.8h, v0.8b, #6 + st1 {v4.d}[0], [x0], #8 + subs w3, w3, #1 + st1 {v4.s}[2], [x0], x7 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_pixels8_8_neon, export=1 + mov x7, #(MAX_PB_SIZE * 2) +1: ld1 {v0.8b}, [x1], x2 + ushll v4.8h, v0.8b, #6 + subs w3, w3, #1 + st1 {v4.8h}, [x0], x7 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_pixels12_8_neon, export=1 + mov x7, #(MAX_PB_SIZE * 2 - 16) +1: ld1 {v0.8b, v1.8b}, [x1], x2 + ushll v4.8h, v0.8b, #6 + st1 {v4.8h}, [x0], #16 + ushll v5.8h, v1.8b, #6 + subs w3, w3, #1 + st1 {v5.d}[0], [x0], x7 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_pixels16_8_neon, export=1 + mov x7, #(MAX_PB_SIZE * 2) +1: ld1 {v0.8b, v1.8b}, [x1], x2 + ushll v4.8h, v0.8b, #6 + ushll v5.8h, v1.8b, #6 + subs w3, w3, #1 + st1 {v4.8h, v5.8h}, [x0], x7 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_pixels24_8_neon, export=1 + mov x7, #(MAX_PB_SIZE * 2) +1: ld1 {v0.8b-v2.8b}, [x1], x2 + ushll v4.8h, v0.8b, #6 + ushll v5.8h, v1.8b, #6 + ushll v6.8h, v2.8b, #6 + subs w3, w3, #1 + st1 {v4.8h-v6.8h}, [x0], x7 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_pixels32_8_neon, export=1 + mov x7, #(MAX_PB_SIZE * 2) +1: ld1 {v0.8b-v3.8b}, [x1], x2 + ushll v4.8h, v0.8b, #6 + ushll v5.8h, v1.8b, #6 + ushll v6.8h, v2.8b, #6 + ushll v7.8h, v3.8b, #6 + subs w3, w3, #1 + st1 {v4.8h-v7.8h}, [x0], x7 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_pixels48_8_neon, export=1 + mov x7, #(MAX_PB_SIZE) +1: ld1 {v0.16b-v2.16b}, [x1], x2 + ushll v4.8h, v0.8b, #6 + ushll2 v5.8h, v0.16b, #6 + ushll v6.8h, v1.8b, #6 + ushll2 v7.8h, v1.16b, #6 + st1 {v4.8h-v7.8h}, [x0], #64 + ushll v16.8h, v2.8b, #6 + ushll2 v17.8h, v2.16b, #6 + subs w3, w3, #1 + st1 {v16.8h-v17.8h}, [x0], x7 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_pixels64_8_neon, export=1 +1: ld1 {v0.16b-v3.16b}, [x1], x2 + ushll v4.8h, v0.8b, #6 + ushll2 v5.8h, v0.16b, #6 + ushll v6.8h, v1.8b, #6 + ushll2 v7.8h, v1.16b, #6 + st1 {v4.8h-v7.8h}, [x0], #(MAX_PB_SIZE) + ushll v16.8h, v2.8b, #6 + ushll2 v17.8h, v2.16b, #6 + ushll v18.8h, v3.8b, #6 + ushll2 v19.8h, v3.16b, #6 + subs w3, w3, #1 + st1 {v16.8h-v19.8h}, [x0], #(MAX_PB_SIZE) + b.ne 1b + ret +endfunc + + +function ff_hevc_put_hevc_pel_bi_pixels4_8_neon, export=1 + mov x10, #(MAX_PB_SIZE * 2) +1: ld1 {v0.s}[0], [x2], x3 // src + ushll v16.8h, v0.8b, #6 + ld1 {v20.4h}, [x4], x10 // src2 + sqadd v16.8h, v16.8h, v20.8h + sqrshrun v0.8b, v16.8h, #7 + st1 {v0.s}[0], [x0], x1 + subs w5, w5, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_bi_pixels6_8_neon, export=1 + mov x10, #(MAX_PB_SIZE * 2) + sub x1, x1, #4 +1: ld1 {v0.8b}, [x2], x3 + ushll v16.8h, v0.8b, #6 + ld1 {v20.8h}, [x4], x10 + sqadd v16.8h, v16.8h, v20.8h + sqrshrun v0.8b, v16.8h, #7 + st1 {v0.s}[0], [x0], #4 + st1 {v0.h}[2], [x0], x1 + subs w5, w5, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_bi_pixels8_8_neon, export=1 + mov x10, #(MAX_PB_SIZE * 2) +1: ld1 {v0.8b}, [x2], x3 // src + ushll v16.8h, v0.8b, #6 + ld1 {v20.8h}, [x4], x10 // src2 + sqadd v16.8h, v16.8h, v20.8h + sqrshrun v0.8b, v16.8h, #7 + subs w5, w5, #1 + st1 {v0.8b}, [x0], x1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_bi_pixels12_8_neon, export=1 + mov x10, #(MAX_PB_SIZE * 2) + sub x1, x1, #8 +1: ld1 {v0.16b}, [x2], x3 + ushll v16.8h, v0.8b, #6 + ushll2 v17.8h, v0.16b, #6 + ld1 {v20.8h, v21.8h}, [x4], x10 + sqadd v16.8h, v16.8h, v20.8h + sqadd v17.8h, v17.8h, v21.8h + sqrshrun v0.8b, v16.8h, #7 + sqrshrun2 v0.16b, v17.8h, #7 + st1 {v0.8b}, [x0], #8 + subs w5, w5, #1 + st1 {v0.s}[2], [x0], x1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_bi_pixels16_8_neon, export=1 + mov x10, #(MAX_PB_SIZE * 2) +1: ld1 {v0.16b}, [x2], x3 // src + ushll v16.8h, v0.8b, #6 + ushll2 v17.8h, v0.16b, #6 + ld1 {v20.8h, v21.8h}, [x4], x10 // src2 + sqadd v16.8h, v16.8h, v20.8h + sqadd v17.8h, v17.8h, v21.8h + sqrshrun v0.8b, v16.8h, #7 + sqrshrun2 v0.16b, v17.8h, #7 + subs w5, w5, #1 + st1 {v0.16b}, [x0], x1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_bi_pixels24_8_neon, export=1 + mov x10, #(MAX_PB_SIZE * 2) +1: ld1 {v0.8b-v2.8b}, [x2], x3 // src + ushll v16.8h, v0.8b, #6 + ushll v17.8h, v1.8b, #6 + ushll v18.8h, v2.8b, #6 + ld1 {v20.8h-v22.8h}, [x4], x10 // src2 + sqadd v16.8h, v16.8h, v20.8h + sqadd v17.8h, v17.8h, v21.8h + sqadd v18.8h, v18.8h, v22.8h + sqrshrun v0.8b, v16.8h, #7 + sqrshrun v1.8b, v17.8h, #7 + sqrshrun v2.8b, v18.8h, #7 + subs w5, w5, #1 + st1 {v0.8b-v2.8b}, [x0], x1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_bi_pixels32_8_neon, export=1 + mov x10, #(MAX_PB_SIZE * 2) +1: ld1 {v0.16b-v1.16b}, [x2], x3 // src + ushll v16.8h, v0.8b, #6 + ushll2 v17.8h, v0.16b, #6 + ushll v18.8h, v1.8b, #6 + ushll2 v19.8h, v1.16b, #6 + ld1 {v20.8h-v23.8h}, [x4], x10 // src2 + sqadd v16.8h, v16.8h, v20.8h + sqadd v17.8h, v17.8h, v21.8h + sqadd v18.8h, v18.8h, v22.8h + sqadd v19.8h, v19.8h, v23.8h + sqrshrun v0.8b, v16.8h, #7 + sqrshrun2 v0.16b, v17.8h, #7 + sqrshrun v1.8b, v18.8h, #7 + sqrshrun2 v1.16b, v19.8h, #7 + st1 {v0.16b-v1.16b}, [x0], x1 + subs w5, w5, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_bi_pixels48_8_neon, export=1 + mov x10, #(MAX_PB_SIZE) +1: ld1 {v0.16b-v2.16b}, [x2], x3 // src + ushll v16.8h, v0.8b, #6 + ushll2 v17.8h, v0.16b, #6 + ushll v18.8h, v1.8b, #6 + ushll2 v19.8h, v1.16b, #6 + ushll v20.8h, v2.8b, #6 + ushll2 v21.8h, v2.16b, #6 + ld1 {v24.8h-v27.8h}, [x4], #(MAX_PB_SIZE) // src2 + sqadd v16.8h, v16.8h, v24.8h + sqadd v17.8h, v17.8h, v25.8h + sqadd v18.8h, v18.8h, v26.8h + sqadd v19.8h, v19.8h, v27.8h + ld1 {v24.8h-v25.8h}, [x4], x10 + sqadd v20.8h, v20.8h, v24.8h + sqadd v21.8h, v21.8h, v25.8h + sqrshrun v0.8b, v16.8h, #7 + sqrshrun2 v0.16b, v17.8h, #7 + sqrshrun v1.8b, v18.8h, #7 + sqrshrun2 v1.16b, v19.8h, #7 + sqrshrun v2.8b, v20.8h, #7 + sqrshrun2 v2.16b, v21.8h, #7 + subs w5, w5, #1 + st1 {v0.16b-v2.16b}, [x0], x1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_bi_pixels64_8_neon, export=1 +1: ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x2], x3 // src + ushll v16.8h, v0.8b, #6 + ushll2 v17.8h, v0.16b, #6 + ushll v18.8h, v1.8b, #6 + ushll2 v19.8h, v1.16b, #6 + ushll v20.8h, v2.8b, #6 + ushll2 v21.8h, v2.16b, #6 + ushll v22.8h, v3.8b, #6 + ushll2 v23.8h, v3.16b, #6 + ld1 {v24.8h, v25.8h, v26.8h, v27.8h}, [x4], #(MAX_PB_SIZE) // src2 + sqadd v16.8h, v16.8h, v24.8h + sqadd v17.8h, v17.8h, v25.8h + sqadd v18.8h, v18.8h, v26.8h + sqadd v19.8h, v19.8h, v27.8h + ld1 {v24.8h, v25.8h, v26.8h, v27.8h}, [x4], #(MAX_PB_SIZE) + sqadd v20.8h, v20.8h, v24.8h + sqadd v21.8h, v21.8h, v25.8h + sqadd v22.8h, v22.8h, v26.8h + sqadd v23.8h, v23.8h, v27.8h + sqrshrun v0.8b, v16.8h, #7 + sqrshrun2 v0.16b, v17.8h, #7 + sqrshrun v1.8b, v18.8h, #7 + sqrshrun2 v1.16b, v19.8h, #7 + sqrshrun v2.8b, v20.8h, #7 + sqrshrun2 v2.16b, v21.8h, #7 + sqrshrun v3.8b, v22.8h, #7 + sqrshrun2 v3.16b, v23.8h, #7 + st1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x0], x1 + subs w5, w5, #1 + b.ne 1b + ret +endfunc + + +function ff_hevc_put_hevc_epel_bi_h4_8_neon, export=1 + load_epel_filterb x6, x7 + sub x2, x2, #1 + mov x10, #(MAX_PB_SIZE * 2) +1: ld1 {v4.8b}, [x2], x3 + ext v5.8b, v4.8b, v4.8b, #1 + ext v6.8b, v4.8b, v4.8b, #2 + ext v7.8b, v4.8b, v4.8b, #3 + calc_epelb v16, v4, v5, v6, v7 + ld1 {v20.4h}, [x4], x10 + sqadd v16.8h, v16.8h, v20.8h + sqrshrun v4.8b, v16.8h, #7 + st1 {v4.s}[0], [x0], x1 + subs w5, w5, #1 // height + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_bi_h6_8_neon, export=1 + load_epel_filterb x6, x7 + sub w1, w1, #4 + sub x2, x2, #1 + mov x10, #(MAX_PB_SIZE * 2) +1: ld1 {v24.16b}, [x2], x3 + ext v26.16b, v24.16b, v24.16b, #1 + ext v27.16b, v24.16b, v24.16b, #2 + ext v28.16b, v24.16b, v24.16b, #3 + calc_epelb v16, v24, v26, v27, v28 + ld1 {v20.8h}, [x4], x10 + sqadd v16.8h, v16.8h, v20.8h + sqrshrun v16.8b, v16.8h, #7 + st1 {v16.s}[0], [x0], #4 + st1 {v16.h}[2], [x0], x1 + subs w5, w5, #1 // height + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_bi_h8_8_neon, export=1 + load_epel_filterb x6, x7 + sub x2, x2, #1 + mov x10, #(MAX_PB_SIZE * 2) +1: ld1 {v24.16b}, [x2], x3 + ext v26.16b, v24.16b, v24.16b, #1 + ext v27.16b, v24.16b, v24.16b, #2 + ext v28.16b, v24.16b, v24.16b, #3 + calc_epelb v16, v24, v26, v27, v28 + ld1 {v20.8h}, [x4], x10 + sqadd v16.8h, v16.8h, v20.8h + sqrshrun v16.8b, v16.8h, #7 + st1 {v16.8b}, [x0], x1 + subs w5, w5, #1 // height + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_bi_h12_8_neon, export=1 + load_epel_filterb x6, x7 + sub x1, x1, #8 + sub x2, x2, #1 + mov x10, #(MAX_PB_SIZE * 2) +1: ld1 {v24.16b}, [x2], x3 + ext v26.16b, v24.16b, v24.16b, #1 + ext v27.16b, v24.16b, v24.16b, #2 + ext v28.16b, v24.16b, v24.16b, #3 + calc_epelb v16, v24, v26, v27, v28 + calc_epelb2 v17, v24, v26, v27, v28 + ld1 {v20.8h, v21.8h}, [x4], x10 + sqadd v18.8h, v16.8h, v20.8h + sqadd v19.8h, v17.8h, v21.8h + sqrshrun v20.8b, v18.8h, #7 + sqrshrun v21.8b, v19.8h, #7 + st1 {v20.8b}, [x0], #8 + st1 {v21.s}[0], [x0], x1 + subs w5, w5, #1 // height + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_bi_h16_8_neon, export=1 + load_epel_filterb x6, x7 + sub x2, x2, #1 + mov x10, #(MAX_PB_SIZE * 2) +1: ldr q24, [x2] + ldr s25, [x2, #16] + add x2, x2, x3 + ext v26.16b, v24.16b, v25.16b, #1 + ext v27.16b, v24.16b, v25.16b, #2 + ext v28.16b, v24.16b, v25.16b, #3 + calc_epelb v16, v24, v26, v27, v28 + calc_epelb2 v17, v24, v26, v27, v28 + ld1 {v24.8h, v25.8h}, [x4], x10 + sqadd v16.8h, v16.8h, v24.8h + sqadd v17.8h, v17.8h, v25.8h + sqrshrun v4.8b, v16.8h, #7 + sqrshrun2 v4.16b, v17.8h, #7 + st1 {v4.16b}, [x0], x1 + subs w5, w5, #1 // height + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_bi_h24_8_neon, export=1 + load_epel_filterb x6, x7 + sub x2, x2, #1 + mov x10, #(MAX_PB_SIZE * 2) +1: ld1 {v24.16b, v25.16b}, [x2], x3 + ext v26.16b, v24.16b, v25.16b, #1 + ext v27.16b, v24.16b, v25.16b, #2 + ext v28.16b, v24.16b, v25.16b, #3 + calc_epelb v16, v24, v26, v27, v28 + calc_epelb2 v17, v24, v26, v27, v28 + ext v26.16b, v25.16b, v25.16b, #1 + ext v27.16b, v25.16b, v25.16b, #2 + ext v28.16b, v25.16b, v25.16b, #3 + calc_epelb v18, v25, v26, v27, v28 + ld1 {v20.8h, v21.8h, v22.8h}, [x4], x10 + sqadd v16.8h, v16.8h, v20.8h + sqadd v17.8h, v17.8h, v21.8h + sqadd v18.8h, v18.8h, v22.8h + sqrshrun v4.8b, v16.8h, #7 + sqrshrun v5.8b, v17.8h, #7 + sqrshrun v6.8b, v18.8h, #7 + st1 {v4.8b, v5.8b, v6.8b}, [x0], x1 + subs w5, w5, #1 // height + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_bi_h32_8_neon, export=1 + load_epel_filterb x6, x7 + sub x2, x2, #1 + mov x10, #(MAX_PB_SIZE * 2) +1: ldp q24, q25, [x2] + ldr s26, [x2, #32] + add x2, x2, x3 + ext v27.16b, v24.16b, v25.16b, #1 + ext v28.16b, v24.16b, v25.16b, #2 + ext v29.16b, v24.16b, v25.16b, #3 + calc_epelb v16, v24, v27, v28, v29 + calc_epelb2 v17, v24, v27, v28, v29 + ext v27.16b, v25.16b, v26.16b, #1 + ext v28.16b, v25.16b, v26.16b, #2 + ext v29.16b, v25.16b, v26.16b, #3 + calc_epelb v18, v25, v27, v28, v29 + calc_epelb2 v19, v25, v27, v28, v29 + ld1 {v24.8h, v25.8h, v26.8h, v27.8h}, [x4], x10 + sqadd v16.8h, v16.8h, v24.8h + sqadd v17.8h, v17.8h, v25.8h + sqadd v18.8h, v18.8h, v26.8h + sqadd v19.8h, v19.8h, v27.8h + sqrshrun v4.8b, v16.8h, #7 + sqrshrun2 v4.16b, v17.8h, #7 + sqrshrun v5.8b, v18.8h, #7 + sqrshrun2 v5.16b, v19.8h, #7 + st1 {v4.16b, v5.16b}, [x0], x1 + subs w5, w5, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_bi_h48_8_neon, export=1 + load_epel_filterb x6, x7 + sub x2, x2, #1 + mov x7, #24 + mov x10, #(MAX_PB_SIZE * 2 - 48) +1: ld1 {v24.16b, v25.16b, v26.16b}, [x2] + ldr s27, [x2, #48] + add x2, x2, x3 + ext v28.16b, v24.16b, v25.16b, #1 + ext v29.16b, v24.16b, v25.16b, #2 + ext v30.16b, v24.16b, v25.16b, #3 + calc_epelb v16, v24, v28, v29, v30 + calc_epelb2 v17, v24, v28, v29, v30 + ext v28.16b, v25.16b, v26.16b, #1 + ext v29.16b, v25.16b, v26.16b, #2 + ext v30.16b, v25.16b, v26.16b, #3 + calc_epelb v18, v25, v28, v29, v30 + calc_epelb2 v19, v25, v28, v29, v30 + ext v28.16b, v26.16b, v27.16b, #1 + ext v29.16b, v26.16b, v27.16b, #2 + ext v30.16b, v26.16b, v27.16b, #3 + calc_epelb v20, v26, v28, v29, v30 + calc_epelb2 v21, v26, v28, v29, v30 + + ld1 {v24.8h, v25.8h, v26.8h}, [x4], #48 + sqadd v16.8h, v16.8h, v24.8h + sqadd v17.8h, v17.8h, v25.8h + sqadd v18.8h, v18.8h, v26.8h + ld1 {v27.8h, v28.8h, v29.8h}, [x4], x10 + sqadd v19.8h, v19.8h, v27.8h + sqadd v20.8h, v20.8h, v28.8h + sqadd v21.8h, v21.8h, v29.8h + sqrshrun v4.8b, v16.8h, #7 + sqrshrun2 v4.16b, v17.8h, #7 + sqrshrun v5.8b, v18.8h, #7 + sqrshrun2 v5.16b, v19.8h, #7 + sqrshrun v6.8b, v20.8h, #7 + sqrshrun2 v6.16b, v21.8h, #7 + st1 {v4.16b, v5.16b, v6.16b}, [x0], x1 + subs w5, w5, #1 // height + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_bi_h64_8_neon, export=1 + load_epel_filterb x6, x7 + sub x2, x2, #1 +1: ld1 {v24.16b, v25.16b, v26.16b, v27.16b}, [x2] + ldr s28, [x2, #64] + add x2, x2, x3 + ext v29.16b, v24.16b, v25.16b, #1 + ext v30.16b, v24.16b, v25.16b, #2 + ext v31.16b, v24.16b, v25.16b, #3 + calc_epelb v16, v24, v29, v30, v31 + calc_epelb2 v17, v24, v29, v30, v31 + ext v29.16b, v25.16b, v26.16b, #1 + ext v30.16b, v25.16b, v26.16b, #2 + ext v31.16b, v25.16b, v26.16b, #3 + calc_epelb v18, v25, v29, v30, v31 + calc_epelb2 v19, v25, v29, v30, v31 + ld1 {v4.8h, v5.8h, v6.8h, v7.8h}, [x4], #64 + sqadd v16.8h, v16.8h, v4.8h + sqadd v17.8h, v17.8h, v5.8h + sqadd v18.8h, v18.8h, v6.8h + sqadd v19.8h, v19.8h, v7.8h + sqrshrun v16.8b, v16.8h, #7 + sqrshrun2 v16.16b, v17.8h, #7 + sqrshrun v17.8b, v18.8h, #7 + sqrshrun2 v17.16b, v19.8h, #7 + + ext v29.16b, v26.16b, v27.16b, #1 + ext v30.16b, v26.16b, v27.16b, #2 + ext v31.16b, v26.16b, v27.16b, #3 + calc_epelb v20, v26, v29, v30, v31 + calc_epelb2 v21, v26, v29, v30, v31 + ext v29.16b, v27.16b, v28.16b, #1 + ext v30.16b, v27.16b, v28.16b, #2 + ext v31.16b, v27.16b, v28.16b, #3 + calc_epelb v22, v27, v29, v30, v31 + calc_epelb2 v23, v27, v29, v30, v31 + ld1 {v4.8h, v5.8h, v6.8h, v7.8h}, [x4], #64 + sqadd v20.8h, v20.8h, v4.8h + sqadd v21.8h, v21.8h, v5.8h + sqadd v22.8h, v22.8h, v6.8h + sqadd v23.8h, v23.8h, v7.8h + sqrshrun v18.8b, v20.8h, #7 + sqrshrun2 v18.16b, v21.8h, #7 + sqrshrun v19.8b, v22.8h, #7 + sqrshrun2 v19.16b, v23.8h, #7 + st1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x0], x1 + subs w5, w5, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_bi_v4_8_neon, export=1 + load_epel_filterb x7, x6 + sub x2, x2, x3 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.s}[0], [x2], x3 + ld1 {v17.s}[0], [x2], x3 + ld1 {v18.s}[0], [x2], x3 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().s}[0], [x2], x3 + calc_epelb v4, \src0, \src1, \src2, \src3 + ld1 {v24.4h}, [x4], x10 + sqadd v4.8h, v4.8h, v24.8h + sqrshrun v4.8b, v4.8h, #7 + subs w5, w5, #1 + st1 {v4.s}[0], [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_bi_v6_8_neon, export=1 + load_epel_filterb x7, x6 + sub x2, x2, x3 + sub x1, x1, #4 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8b}, [x2], x3 + ld1 {v17.8b}, [x2], x3 + ld1 {v18.8b}, [x2], x3 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().8b}, [x2], x3 + calc_epelb v4, \src0, \src1, \src2, \src3 + ld1 {v24.8h}, [x4], x10 + sqadd v4.8h, v4.8h, v24.8h + sqrshrun v4.8b, v4.8h, #7 + st1 {v4.s}[0], [x0], #4 + subs w5, w5, #1 + st1 {v4.h}[2], [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_bi_v8_8_neon, export=1 + load_epel_filterb x7, x6 + sub x2, x2, x3 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8b}, [x2], x3 + ld1 {v17.8b}, [x2], x3 + ld1 {v18.8b}, [x2], x3 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().8b}, [x2], x3 + calc_epelb v4, \src0, \src1, \src2, \src3 + ld1 {v24.8h}, [x4], x10 + sqadd v4.8h, v4.8h, v24.8h + sqrshrun v4.8b, v4.8h, #7 + subs w5, w5, #1 + st1 {v4.8b}, [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_bi_v12_8_neon, export=1 + load_epel_filterb x7, x6 + sub x1, x1, #8 + sub x2, x2, x3 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.16b}, [x2], x3 + ld1 {v17.16b}, [x2], x3 + ld1 {v18.16b}, [x2], x3 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().16b}, [x2], x3 + calc_epelb v4, \src0, \src1, \src2, \src3 + calc_epelb2 v5, \src0, \src1, \src2, \src3 + ld1 {v24.8h, v25.8h}, [x4], x10 + sqadd v4.8h, v4.8h, v24.8h + sqadd v5.8h, v5.8h, v25.8h + sqrshrun v4.8b, v4.8h, #7 + sqrshrun2 v4.16b, v5.8h, #7 + st1 {v4.8b}, [x0], #8 + subs w5, w5, #1 + st1 {v4.s}[2], [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_bi_v16_8_neon, export=1 + load_epel_filterb x7, x6 + sub x2, x2, x3 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.16b}, [x2], x3 + ld1 {v17.16b}, [x2], x3 + ld1 {v18.16b}, [x2], x3 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().16b}, [x2], x3 + calc_epelb v4, \src0, \src1, \src2, \src3 + calc_epelb2 v5, \src0, \src1, \src2, \src3 + ld1 {v24.8h, v25.8h}, [x4], x10 + sqadd v4.8h, v4.8h, v24.8h + sqadd v5.8h, v5.8h, v25.8h + sqrshrun v4.8b, v4.8h, #7 + sqrshrun2 v4.16b, v5.8h, #7 + st1 {v4.16b}, [x0], x1 + subs w5, w5, #1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_bi_v24_8_neon, export=1 + load_epel_filterb x7, x6 + sub x2, x2, x3 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8b, v17.8b, v18.8b}, [x2], x3 + ld1 {v19.8b, v20.8b, v21.8b}, [x2], x3 + ld1 {v22.8b, v23.8b, v24.8b}, [x2], x3 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11 + ld1 {\src9\().8b, \src10\().8b, \src11\().8b}, [x2], x3 + calc_epelb v4, \src0, \src3, \src6, \src9 + calc_epelb v5, \src1, \src4, \src7, \src10 + calc_epelb v6, \src2, \src5, \src8, \src11 + ld1 {v28.8h, v29.8h, v30.8h}, [x4], x10 + sqadd v4.8h, v4.8h, v28.8h + sqadd v5.8h, v5.8h, v29.8h + sqadd v6.8h, v6.8h, v30.8h + sqrshrun v4.8b, v4.8h, #7 + sqrshrun v5.8b, v5.8h, #7 + sqrshrun v6.8b, v6.8h, #7 + subs w5, w5, #1 + st1 {v4.8b, v5.8b, v6.8b}, [x0], x1 +.endm +1: calc_all12 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_bi_v32_8_neon, export=1 + load_epel_filterb x7, x6 + sub x2, x2, x3 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.16b, v17.16b}, [x2], x3 + ld1 {v18.16b, v19.16b}, [x2], x3 + ld1 {v20.16b, v21.16b}, [x2], x3 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\src6\().16b, \src7\().16b}, [x2], x3 + calc_epelb v4, \src0, \src2, \src4, \src6 + calc_epelb2 v5, \src0, \src2, \src4, \src6 + calc_epelb v6, \src1, \src3, \src5, \src7 + calc_epelb2 v7, \src1, \src3, \src5, \src7 + ld1 {v24.8h-v27.8h}, [x4], x10 + sqadd v4.8h, v4.8h, v24.8h + sqadd v5.8h, v5.8h, v25.8h + sqadd v6.8h, v6.8h, v26.8h + sqadd v7.8h, v7.8h, v27.8h + sqrshrun v4.8b, v4.8h, #7 + sqrshrun2 v4.16b, v5.8h, #7 + sqrshrun v5.8b, v6.8h, #7 + sqrshrun2 v5.16b, v7.8h, #7 + st1 {v4.16b, v5.16b}, [x0], x1 + subs w5, w5, #1 +.endm +1: calc_all8 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_bi_v48_8_neon, export=1 + stp x4, x5, [sp, #-64]! + stp x2, x3, [sp, #16] + stp x0, x1, [sp, #32] + stp x7, x30, [sp, #48] + bl X(ff_hevc_put_hevc_epel_bi_v24_8_neon) + ldp x4, x5, [sp] + ldp x2, x3, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x7, [sp, #48] + add sp, sp, #48 + add x0, x0, #24 + add x2, x2, #24 + add x4, x4, #48 + bl X(ff_hevc_put_hevc_epel_bi_v24_8_neon) + ldr x30, [sp, #8] + add sp, sp, #16 + ret +endfunc + +function ff_hevc_put_hevc_epel_bi_v64_8_neon, export=1 + stp x4, x5, [sp, #-64]! + stp x2, x3, [sp, #16] + stp x0, x1, [sp, #32] + stp x7, x30, [sp, #48] + bl X(ff_hevc_put_hevc_epel_bi_v32_8_neon) + ldp x4, x5, [sp] + ldp x2, x3, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x7, [sp, #48] + add sp, sp, #48 + add x0, x0, #32 + add x2, x2, #32 + add x4, x4, #64 + bl X(ff_hevc_put_hevc_epel_bi_v32_8_neon) + ldr x30, [sp, #8] + add sp, sp, #16 + ret +endfunc + +function ff_hevc_put_hevc_epel_v4_8_neon, export=1 + load_epel_filterb x5, x4 + sub x1, x1, x2 + mov x10, #(MAX_PB_SIZE * 2) + ldr s16, [x1] + ldr s17, [x1, x2] + add x1, x1, x2, lsl #1 + ld1 {v18.s}[0], [x1], x2 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().s}[0], [x1], x2 + movi v4.8h, #0 + calc_epelb v4, \src0, \src1, \src2, \src3 + subs w3, w3, #1 + st1 {v4.4h}, [x0], x10 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_v6_8_neon, export=1 + load_epel_filterb x5, x4 + sub x1, x1, x2 + mov x10, #(MAX_PB_SIZE * 2 - 8) + ldr d16, [x1] + ldr d17, [x1, x2] + add x1, x1, x2, lsl #1 + ld1 {v18.8b}, [x1], x2 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().8b}, [x1], x2 + movi v4.8h, #0 + calc_epelb v4, \src0, \src1, \src2, \src3 + st1 {v4.d}[0], [x0], #8 + subs w3, w3, #1 + st1 {v4.s}[2], [x0], x10 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_v8_8_neon, export=1 + load_epel_filterb x5, x4 + sub x1, x1, x2 + mov x10, #(MAX_PB_SIZE * 2) + ldr d16, [x1] + ldr d17, [x1, x2] + add x1, x1, x2, lsl #1 + ld1 {v18.8b}, [x1], x2 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().8b}, [x1], x2 + movi v4.8h, #0 + calc_epelb v4, \src0, \src1, \src2, \src3 + subs w3, w3, #1 + st1 {v4.8h}, [x0], x10 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_v12_8_neon, export=1 + load_epel_filterb x5, x4 + sub x1, x1, x2 + mov x10, #(MAX_PB_SIZE * 2) + ldr q16, [x1] + ldr q17, [x1, x2] + add x1, x1, x2, lsl #1 + ld1 {v18.16b}, [x1], x2 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().16b}, [x1], x2 + movi v4.8h, #0 + movi v5.8h, #0 + calc_epelb v4, \src0, \src1, \src2, \src3 + calc_epelb2 v5, \src0, \src1, \src2, \src3 + str q4, [x0] + subs w3, w3, #1 + str d5, [x0, #16] + add x0, x0, x10 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_v16_8_neon, export=1 + load_epel_filterb x5, x4 + sub x1, x1, x2 + mov x10, #(MAX_PB_SIZE * 2) + ldr q16, [x1] + ldr q17, [x1, x2] + add x1, x1, x2, lsl #1 + ld1 {v18.16b}, [x1], x2 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().16b}, [x1], x2 + movi v4.8h, #0 + movi v5.8h, #0 + calc_epelb v4, \src0, \src1, \src2, \src3 + calc_epelb2 v5, \src0, \src1, \src2, \src3 + subs w3, w3, #1 + st1 {v4.8h, v5.8h}, [x0], x10 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_v24_8_neon, export=1 + load_epel_filterb x5, x4 + sub x1, x1, x2 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8b, v17.8b, v18.8b}, [x1], x2 + ld1 {v19.8b, v20.8b, v21.8b}, [x1], x2 + ld1 {v22.8b, v23.8b, v24.8b}, [x1], x2 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11 + ld1 {\src9\().8b, \src10\().8b, \src11\().8b}, [x1], x2 + movi v4.8h, #0 + movi v5.8h, #0 + movi v6.8h, #0 + calc_epelb v4, \src0, \src3, \src6, \src9 + calc_epelb v5, \src1, \src4, \src7, \src10 + calc_epelb v6, \src2, \src5, \src8, \src11 + subs w3, w3, #1 + st1 {v4.8h-v6.8h}, [x0], x10 +.endm +1: calc_all12 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_v32_8_neon, export=1 + load_epel_filterb x5, x4 + sub x1, x1, x2 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.16b, v17.16b}, [x1], x2 + ld1 {v18.16b, v19.16b}, [x1], x2 + ld1 {v20.16b, v21.16b}, [x1], x2 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\src6\().16b, \src7\().16b}, [x1], x2 + movi v4.8h, #0 + movi v5.8h, #0 + movi v6.8h, #0 + movi v7.8h, #0 + calc_epelb v4, \src0, \src2, \src4, \src6 + calc_epelb2 v5, \src0, \src2, \src4, \src6 + calc_epelb v6, \src1, \src3, \src5, \src7 + calc_epelb2 v7, \src1, \src3, \src5, \src7 + subs w3, w3, #1 + st1 {v4.8h-v7.8h}, [x0], x10 +.endm +1: calc_all8 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_v48_8_neon, export=1 + load_epel_filterb x5, x4 + sub x1, x1, x2 + mov x10, #64 + ld1 {v16.16b, v17.16b, v18.16b}, [x1], x2 + ld1 {v19.16b, v20.16b, v21.16b}, [x1], x2 + ld1 {v22.16b, v23.16b, v24.16b}, [x1], x2 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11 + ld1 {\src9\().16b, \src10\().16b, \src11\().16b}, [x1], x2 + movi v4.8h, #0 + movi v5.8h, #0 + movi v6.8h, #0 + movi v7.8h, #0 + movi v28.8h, #0 + movi v29.8h, #0 + calc_epelb v4, \src0, \src3, \src6, \src9 + calc_epelb2 v5, \src0, \src3, \src6, \src9 + calc_epelb v6, \src1, \src4, \src7, \src10 + calc_epelb2 v7, \src1, \src4, \src7, \src10 + calc_epelb v28, \src2, \src5, \src8, \src11 + calc_epelb2 v29, \src2, \src5, \src8, \src11 + st1 {v4.8h-v7.8h}, [x0], #64 + subs w3, w3, #1 + st1 {v28.8h-v29.8h}, [x0], x10 +.endm +1: calc_all12 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_v64_8_neon, export=1 + load_epel_filterb x5, x4 + sub sp, sp, #32 + st1 {v8.8b-v11.8b}, [sp] + sub x1, x1, x2 + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x1], x2 + ld1 {v20.16b, v21.16b, v22.16b, v23.16b}, [x1], x2 + ld1 {v24.16b, v25.16b, v26.16b, v27.16b}, [x1], x2 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15 + ld1 {\src12\().16b-\src15\().16b}, [x1], x2 + movi v4.8h, #0 + movi v5.8h, #0 + movi v6.8h, #0 + movi v7.8h, #0 + movi v8.8h, #0 + movi v9.8h, #0 + movi v10.8h, #0 + movi v11.8h, #0 + calc_epelb v4, \src0, \src4, \src8, \src12 + calc_epelb2 v5, \src0, \src4, \src8, \src12 + calc_epelb v6, \src1, \src5, \src9, \src13 + calc_epelb2 v7, \src1, \src5, \src9, \src13 + calc_epelb v8, \src2, \src6, \src10, \src14 + calc_epelb2 v9, \src2, \src6, \src10, \src14 + calc_epelb v10, \src3, \src7, \src11, \src15 + calc_epelb2 v11, \src3, \src7, \src11, \src15 + st1 {v4.8h-v7.8h}, [x0], #64 + subs w3, w3, #1 + st1 {v8.8h-v11.8h}, [x0], #64 +.endm +1: calc_all16 +.purgem calc +2: ld1 {v8.8b-v11.8b}, [sp] + add sp, sp, #32 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_v4_8_neon, export=1 + load_epel_filterb x6, x5 + sub x2, x2, x3 + ld1 {v16.s}[0], [x2], x3 + ld1 {v17.s}[0], [x2], x3 + ld1 {v18.s}[0], [x2], x3 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().s}[0], [x2], x3 + calc_epelb v4, \src0, \src1, \src2, \src3 + sqrshrun v4.8b, v4.8h, #6 + subs w4, w4, #1 + st1 {v4.s}[0], [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_uni_v6_8_neon, export=1 + load_epel_filterb x6, x5 + sub x2, x2, x3 + sub x1, x1, #4 + ld1 {v16.8b}, [x2], x3 + ld1 {v17.8b}, [x2], x3 + ld1 {v18.8b}, [x2], x3 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().8b}, [x2], x3 + calc_epelb v4, \src0, \src1, \src2, \src3 + sqrshrun v4.8b, v4.8h, #6 + st1 {v4.s}[0], [x0], #4 + subs w4, w4, #1 + st1 {v4.h}[2], [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_uni_v8_8_neon, export=1 + load_epel_filterb x6, x5 + sub x2, x2, x3 + ld1 {v16.8b}, [x2], x3 + ld1 {v17.8b}, [x2], x3 + ld1 {v18.8b}, [x2], x3 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().8b}, [x2], x3 + calc_epelb v4, \src0, \src1, \src2, \src3 + sqrshrun v4.8b, v4.8h, #6 + subs w4, w4, #1 + st1 {v4.8b}, [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_uni_v12_8_neon, export=1 + load_epel_filterb x6, x5 + sub x2, x2, x3 + sub x1, x1, #8 + ld1 {v16.16b}, [x2], x3 + ld1 {v17.16b}, [x2], x3 + ld1 {v18.16b}, [x2], x3 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().16b}, [x2], x3 + calc_epelb v4, \src0, \src1, \src2, \src3 + calc_epelb2 v5, \src0, \src1, \src2, \src3 + sqrshrun v4.8b, v4.8h, #6 + sqrshrun2 v4.16b, v5.8h, #6 + subs w4, w4, #1 + st1 {v4.8b}, [x0], #8 + st1 {v4.s}[2], [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_uni_v16_8_neon, export=1 + load_epel_filterb x6, x5 + sub x2, x2, x3 + ld1 {v16.16b}, [x2], x3 + ld1 {v17.16b}, [x2], x3 + ld1 {v18.16b}, [x2], x3 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().16b}, [x2], x3 + calc_epelb v4, \src0, \src1, \src2, \src3 + calc_epelb2 v5, \src0, \src1, \src2, \src3 + sqrshrun v4.8b, v4.8h, #6 + sqrshrun2 v4.16b, v5.8h, #6 + subs w4, w4, #1 + st1 {v4.16b}, [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_uni_v24_8_neon, export=1 + load_epel_filterb x6, x5 + sub x2, x2, x3 + ld1 {v16.8b, v17.8b, v18.8b}, [x2], x3 + ld1 {v19.8b, v20.8b, v21.8b}, [x2], x3 + ld1 {v22.8b, v23.8b, v24.8b}, [x2], x3 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11 + ld1 {\src9\().8b, \src10\().8b, \src11\().8b}, [x2], x3 + calc_epelb v4, \src0, \src3, \src6, \src9 + calc_epelb v5, \src1, \src4, \src7, \src10 + calc_epelb v6, \src2, \src5, \src8, \src11 + sqrshrun v4.8b, v4.8h, #6 + sqrshrun v5.8b, v5.8h, #6 + sqrshrun v6.8b, v6.8h, #6 + subs w4, w4, #1 + st1 {v4.8b-v6.8b}, [x0], x1 +.endm +1: calc_all12 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_uni_v32_8_neon, export=1 + load_epel_filterb x6, x5 + sub x2, x2, x3 + ld1 {v16.16b, v17.16b}, [x2], x3 + ld1 {v18.16b, v19.16b}, [x2], x3 + ld1 {v20.16b, v21.16b}, [x2], x3 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\src6\().16b, \src7\().16b}, [x2], x3 + calc_epelb v4, \src0, \src2, \src4, \src6 + calc_epelb2 v5, \src0, \src2, \src4, \src6 + calc_epelb v6, \src1, \src3, \src5, \src7 + calc_epelb2 v7, \src1, \src3, \src5, \src7 + sqrshrun v4.8b, v4.8h, #6 + sqrshrun2 v4.16b, v5.8h, #6 + sqrshrun v5.8b, v6.8h, #6 + sqrshrun2 v5.16b, v7.8h, #6 + subs w4, w4, #1 + st1 {v4.16b, v5.16b}, [x0], x1 +.endm +1: calc_all8 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_uni_v48_8_neon, export=1 + load_epel_filterb x6, x5 + sub x2, x2, x3 + ld1 {v16.16b, v17.16b, v18.16b}, [x2], x3 + ld1 {v19.16b, v20.16b, v21.16b}, [x2], x3 + ld1 {v22.16b, v23.16b, v24.16b}, [x2], x3 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11 + ld1 {\src9\().16b, \src10\().16b, \src11\().16b}, [x2], x3 + calc_epelb v4, \src0, \src3, \src6, \src9 + calc_epelb2 v5, \src0, \src3, \src6, \src9 + calc_epelb v6, \src1, \src4, \src7, \src10 + calc_epelb2 v7, \src1, \src4, \src7, \src10 + calc_epelb v28, \src2, \src5, \src8, \src11 + calc_epelb2 v29, \src2, \src5, \src8, \src11 + sqrshrun v4.8b, v4.8h, #6 + sqrshrun2 v4.16b, v5.8h, #6 + sqrshrun v5.8b, v6.8h, #6 + sqrshrun2 v5.16b, v7.8h, #6 + sqrshrun v6.8b, v28.8h, #6 + sqrshrun2 v6.16b, v29.8h, #6 + subs w4, w4, #1 + st1 {v4.16b, v5.16b, v6.16b}, [x0], x1 +.endm +1: calc_all12 +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_epel_uni_v64_8_neon, export=1 + load_epel_filterb x6, x5 + sub sp, sp, #32 + st1 {v8.8b-v11.8b}, [sp] + sub x2, x2, x3 + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x2], x3 + ld1 {v20.16b, v21.16b, v22.16b, v23.16b}, [x2], x3 + ld1 {v24.16b, v25.16b, v26.16b, v27.16b}, [x2], x3 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15 + ld1 {\src12\().16b, \src13\().16b, \src14\().16b, \src15\().16b}, [x2], x3 + calc_epelb v10, \src3, \src7, \src11, \src15 + calc_epelb2 v11, \src3, \src7, \src11, \src15 + calc_epelb v4, \src0, \src4, \src8, \src12 + calc_epelb2 v5, \src0, \src4, \src8, \src12 + calc_epelb v6, \src1, \src5, \src9, \src13 + calc_epelb2 v7, \src1, \src5, \src9, \src13 + calc_epelb v8, \src2, \src6, \src10, \src14 + calc_epelb2 v9, \src2, \src6, \src10, \src14 + sqrshrun v4.8b, v4.8h, #6 + sqrshrun2 v4.16b, v5.8h, #6 + sqrshrun v5.8b, v6.8h, #6 + sqrshrun2 v5.16b, v7.8h, #6 + sqrshrun v6.8b, v8.8h, #6 + sqrshrun2 v6.16b, v9.8h, #6 + sqrshrun v7.8b, v10.8h, #6 + sqrshrun2 v7.16b, v11.8h, #6 + subs w4, w4, #1 + st1 {v4.16b, v5.16b, v6.16b, v7.16b}, [x0], x1 +.endm +1: calc_all16 +.purgem calc +2: ld1 {v8.8b-v11.8b}, [sp], #32 + ret +endfunc + + +.macro EPEL_H_HEADER + movrel x5, epel_filters + add x5, x5, x4, lsl #2 + ld1r {v30.4s}, [x5] + sub x1, x1, #1 + mov x10, #(MAX_PB_SIZE * 2) +.endm + +function ff_hevc_put_hevc_epel_h4_8_neon, export=1 + EPEL_H_HEADER + sxtl v0.8h, v30.8b +1: ld1 {v4.8b}, [x1], x2 + subs w3, w3, #1 // height + uxtl v4.8h, v4.8b + ext v5.16b, v4.16b, v4.16b, #2 + ext v6.16b, v4.16b, v4.16b, #4 + ext v7.16b, v4.16b, v4.16b, #6 + mul v16.4h, v4.4h, v0.h[0] + mla v16.4h, v5.4h, v0.h[1] + mla v16.4h, v6.4h, v0.h[2] + mla v16.4h, v7.4h, v0.h[3] + st1 {v16.4h}, [x0], x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h6_8_neon, export=1 + EPEL_H_HEADER + sxtl v0.8h, v30.8b + add x6, x0, #8 +1: ld1 {v3.16b}, [x1], x2 + subs w3, w3, #1 // height + uxtl2 v4.8h, v3.16b + uxtl v3.8h, v3.8b + ext v5.16b, v3.16b, v4.16b, #2 + ext v6.16b, v3.16b, v4.16b, #4 + ext v7.16b, v3.16b, v4.16b, #6 + mul v16.8h, v3.8h, v0.h[0] + mla v16.8h, v5.8h, v0.h[1] + mla v16.8h, v6.8h, v0.h[2] + mla v16.8h, v7.8h, v0.h[3] + st1 {v16.4h}, [x0], x10 + st1 {v16.s}[2], [x6], x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h8_8_neon, export=1 + EPEL_H_HEADER + sxtl v0.8h, v30.8b +1: ld1 {v3.16b}, [x1], x2 + subs w3, w3, #1 // height + uxtl2 v4.8h, v3.16b + uxtl v3.8h, v3.8b + ext v5.16b, v3.16b, v4.16b, #2 + ext v6.16b, v3.16b, v4.16b, #4 + ext v7.16b, v3.16b, v4.16b, #6 + mul v16.8h, v3.8h, v0.h[0] + mla v16.8h, v5.8h, v0.h[1] + mla v16.8h, v6.8h, v0.h[2] + mla v16.8h, v7.8h, v0.h[3] + st1 {v16.8h}, [x0], x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h12_8_neon, export=1 + EPEL_H_HEADER + add x6, x0, #16 + sxtl v0.8h, v30.8b +1: ld1 {v3.16b}, [x1], x2 + subs w3, w3, #1 // height + uxtl2 v4.8h, v3.16b + uxtl v3.8h, v3.8b + ext v5.16b, v3.16b, v4.16b, #2 + ext v6.16b, v3.16b, v4.16b, #4 + ext v7.16b, v3.16b, v4.16b, #6 + ext v20.16b, v4.16b, v4.16b, #2 + ext v21.16b, v4.16b, v4.16b, #4 + ext v22.16b, v4.16b, v4.16b, #6 + mul v16.8h, v3.8h, v0.h[0] + mla v16.8h, v5.8h, v0.h[1] + mla v16.8h, v6.8h, v0.h[2] + mla v16.8h, v7.8h, v0.h[3] + mul v17.4h, v4.4h, v0.h[0] + mla v17.4h, v20.4h, v0.h[1] + mla v17.4h, v21.4h, v0.h[2] + mla v17.4h, v22.4h, v0.h[3] + st1 {v16.8h}, [x0], x10 + st1 {v17.4h}, [x6], x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h16_8_neon, export=1 + EPEL_H_HEADER + sxtl v0.8h, v30.8b +1: ld1 {v1.8b, v2.8b, v3.8b}, [x1], x2 + subs w3, w3, #1 // height + uxtl v1.8h, v1.8b + uxtl v2.8h, v2.8b + uxtl v3.8h, v3.8b + ext v5.16b, v1.16b, v2.16b, #2 + ext v6.16b, v1.16b, v2.16b, #4 + ext v7.16b, v1.16b, v2.16b, #6 + ext v20.16b, v2.16b, v3.16b, #2 + ext v21.16b, v2.16b, v3.16b, #4 + ext v22.16b, v2.16b, v3.16b, #6 + mul v16.8h, v1.8h, v0.h[0] + mla v16.8h, v5.8h, v0.h[1] + mla v16.8h, v6.8h, v0.h[2] + mla v16.8h, v7.8h, v0.h[3] + mul v17.8h, v2.8h, v0.h[0] + mla v17.8h, v20.8h, v0.h[1] + mla v17.8h, v21.8h, v0.h[2] + mla v17.8h, v22.8h, v0.h[3] + st1 {v16.8h, v17.8h}, [x0], x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h24_8_neon, export=1 + EPEL_H_HEADER + sxtl v0.8h, v30.8b +1: ld1 {v1.8b, v2.8b, v3.8b, v4.8b}, [x1], x2 + subs w3, w3, #1 // height + uxtl v1.8h, v1.8b + uxtl v2.8h, v2.8b + uxtl v3.8h, v3.8b + uxtl v4.8h, v4.8b + ext v5.16b, v1.16b, v2.16b, #2 + ext v6.16b, v1.16b, v2.16b, #4 + ext v7.16b, v1.16b, v2.16b, #6 + ext v20.16b, v2.16b, v3.16b, #2 + ext v21.16b, v2.16b, v3.16b, #4 + ext v22.16b, v2.16b, v3.16b, #6 + ext v23.16b, v3.16b, v4.16b, #2 + ext v24.16b, v3.16b, v4.16b, #4 + ext v25.16b, v3.16b, v4.16b, #6 + mul v16.8h, v1.8h, v0.h[0] + mla v16.8h, v5.8h, v0.h[1] + mla v16.8h, v6.8h, v0.h[2] + mla v16.8h, v7.8h, v0.h[3] + mul v17.8h, v2.8h, v0.h[0] + mla v17.8h, v20.8h, v0.h[1] + mla v17.8h, v21.8h, v0.h[2] + mla v17.8h, v22.8h, v0.h[3] + mul v18.8h, v3.8h, v0.h[0] + mla v18.8h, v23.8h, v0.h[1] + mla v18.8h, v24.8h, v0.h[2] + mla v18.8h, v25.8h, v0.h[3] + st1 {v16.8h, v17.8h, v18.8h}, [x0], x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h32_8_neon, export=1 + EPEL_H_HEADER + ld1 {v1.8b}, [x1], #8 + sub x2, x2, w6, uxtw // decrement src stride + mov w7, w6 // original width + sub x2, x2, #8 // decrement src stride + sub x10, x10, w6, uxtw #1 // decrement dst stride + sxtl v0.8h, v30.8b + uxtl v1.8h, v1.8b +1: ld1 {v2.8b, v3.8b}, [x1], #16 + subs w6, w6, #16 // width + uxtl v2.8h, v2.8b + uxtl v3.8h, v3.8b + ext v5.16b, v1.16b, v2.16b, #2 + ext v6.16b, v1.16b, v2.16b, #4 + ext v7.16b, v1.16b, v2.16b, #6 + ext v20.16b, v2.16b, v3.16b, #2 + ext v21.16b, v2.16b, v3.16b, #4 + ext v22.16b, v2.16b, v3.16b, #6 + mul v16.8h, v1.8h, v0.h[0] + mla v16.8h, v5.8h, v0.h[1] + mla v16.8h, v6.8h, v0.h[2] + mla v16.8h, v7.8h, v0.h[3] + mul v17.8h, v2.8h, v0.h[0] + mla v17.8h, v20.8h, v0.h[1] + mla v17.8h, v21.8h, v0.h[2] + mla v17.8h, v22.8h, v0.h[3] + st1 {v16.8h, v17.8h}, [x0], #32 + mov v1.16b, v3.16b + b.gt 1b + subs w3, w3, #1 // height + add x1, x1, x2 + b.le 9f + ld1 {v1.8b}, [x1], #8 + mov w6, w7 + add x0, x0, x10 + uxtl v1.8h, v1.8b + b 1b +9: + ret +endfunc + +.macro EPEL_UNI_W_H_HEADER elems=4s + ldr x12, [sp] + sub x2, x2, #1 + movrel x9, epel_filters + add x9, x9, x12, lsl #2 + ld1r {v28.4s}, [x9] + mov w10, #-6 + sub w10, w10, w5 + dup v30.\elems, w6 + dup v31.4s, w10 + dup v29.4s, w7 +.endm + +function ff_hevc_put_hevc_epel_uni_w_h4_8_neon, export=1 + EPEL_UNI_W_H_HEADER 4h + sxtl v0.8h, v28.8b +1: + ld1 {v4.8b}, [x2], x3 + subs w4, w4, #1 + uxtl v4.8h, v4.8b + ext v5.16b, v4.16b, v4.16b, #2 + ext v6.16b, v4.16b, v4.16b, #4 + ext v7.16b, v4.16b, v4.16b, #6 + mul v16.4h, v4.4h, v0.h[0] + mla v16.4h, v5.4h, v0.h[1] + mla v16.4h, v6.4h, v0.h[2] + mla v16.4h, v7.4h, v0.h[3] + smull v16.4s, v16.4h, v30.4h + sqrshl v16.4s, v16.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtun v16.8b, v16.8h + str s16, [x0] + add x0, x0, x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h6_8_neon, export=1 + EPEL_UNI_W_H_HEADER 8h + sub x1, x1, #4 + sxtl v0.8h, v28.8b +1: + ld1 {v3.8b, v4.8b}, [x2], x3 + subs w4, w4, #1 + uxtl v3.8h, v3.8b + uxtl v4.8h, v4.8b + ext v5.16b, v3.16b, v4.16b, #2 + ext v6.16b, v3.16b, v4.16b, #4 + ext v7.16b, v3.16b, v4.16b, #6 + mul v16.8h, v3.8h, v0.h[0] + mla v16.8h, v5.8h, v0.h[1] + mla v16.8h, v6.8h, v0.h[2] + mla v16.8h, v7.8h, v0.h[3] + smull v17.4s, v16.4h, v30.4h + smull2 v18.4s, v16.8h, v30.8h + sqrshl v17.4s, v17.4s, v31.4s + sqrshl v18.4s, v18.4s, v31.4s + sqadd v17.4s, v17.4s, v29.4s + sqadd v18.4s, v18.4s, v29.4s + sqxtn v16.4h, v17.4s + sqxtn2 v16.8h, v18.4s + sqxtun v16.8b, v16.8h + str s16, [x0], #4 + st1 {v16.h}[2], [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h8_8_neon, export=1 + EPEL_UNI_W_H_HEADER 8h + sxtl v0.8h, v28.8b +1: + ld1 {v3.8b, v4.8b}, [x2], x3 + subs w4, w4, #1 + uxtl v3.8h, v3.8b + uxtl v4.8h, v4.8b + ext v5.16b, v3.16b, v4.16b, #2 + ext v6.16b, v3.16b, v4.16b, #4 + ext v7.16b, v3.16b, v4.16b, #6 + mul v16.8h, v3.8h, v0.h[0] + mla v16.8h, v5.8h, v0.h[1] + mla v16.8h, v6.8h, v0.h[2] + mla v16.8h, v7.8h, v0.h[3] + smull v17.4s, v16.4h, v30.4h + smull2 v18.4s, v16.8h, v30.8h + sqrshl v17.4s, v17.4s, v31.4s + sqrshl v18.4s, v18.4s, v31.4s + sqadd v17.4s, v17.4s, v29.4s + sqadd v18.4s, v18.4s, v29.4s + sqxtn v16.4h, v17.4s + sqxtn2 v16.8h, v18.4s + sqxtun v16.8b, v16.8h + st1 {v16.8b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h12_8_neon, export=1 + EPEL_UNI_W_H_HEADER 8h + sxtl v0.8h, v28.8b +1: + ld1 {v3.8b, v4.8b}, [x2], x3 + subs w4, w4, #1 + uxtl v3.8h, v3.8b + uxtl v4.8h, v4.8b + ext v5.16b, v3.16b, v4.16b, #2 + ext v6.16b, v3.16b, v4.16b, #4 + ext v7.16b, v3.16b, v4.16b, #6 + ext v20.16b, v4.16b, v4.16b, #2 + ext v21.16b, v4.16b, v4.16b, #4 + ext v22.16b, v4.16b, v4.16b, #6 + mul v16.8h, v3.8h, v0.h[0] + mla v16.8h, v5.8h, v0.h[1] + mla v16.8h, v6.8h, v0.h[2] + mla v16.8h, v7.8h, v0.h[3] + mul v17.4h, v4.4h, v0.h[0] + mla v17.4h, v20.4h, v0.h[1] + mla v17.4h, v21.4h, v0.h[2] + mla v17.4h, v22.4h, v0.h[3] + smull v18.4s, v16.4h, v30.4h + smull2 v19.4s, v16.8h, v30.8h + smull v20.4s, v17.4h, v30.4h + sqrshl v18.4s, v18.4s, v31.4s + sqrshl v19.4s, v19.4s, v31.4s + sqrshl v20.4s, v20.4s, v31.4s + sqadd v18.4s, v18.4s, v29.4s + sqadd v19.4s, v19.4s, v29.4s + sqadd v20.4s, v20.4s, v29.4s + sqxtn v16.4h, v18.4s + sqxtn2 v16.8h, v19.4s + sqxtn v17.4h, v20.4s + sqxtun v16.8b, v16.8h + sqxtun v17.8b, v17.8h + str d16, [x0] + str s17, [x0, #8] + add x0, x0, x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h16_8_neon, export=1 + EPEL_UNI_W_H_HEADER 8h + sxtl v0.8h, v28.8b +1: + ld1 {v1.8b, v2.8b, v3.8b}, [x2], x3 + subs w4, w4, #1 + uxtl v1.8h, v1.8b + uxtl v2.8h, v2.8b + uxtl v3.8h, v3.8b + ext v5.16b, v1.16b, v2.16b, #2 + ext v6.16b, v1.16b, v2.16b, #4 + ext v7.16b, v1.16b, v2.16b, #6 + ext v20.16b, v2.16b, v3.16b, #2 + ext v21.16b, v2.16b, v3.16b, #4 + ext v22.16b, v2.16b, v3.16b, #6 + mul v16.8h, v1.8h, v0.h[0] + mla v16.8h, v5.8h, v0.h[1] + mla v16.8h, v6.8h, v0.h[2] + mla v16.8h, v7.8h, v0.h[3] + mul v17.8h, v2.8h, v0.h[0] + mla v17.8h, v20.8h, v0.h[1] + mla v17.8h, v21.8h, v0.h[2] + mla v17.8h, v22.8h, v0.h[3] + smull v18.4s, v16.4h, v30.4h + smull2 v19.4s, v16.8h, v30.8h + smull v20.4s, v17.4h, v30.4h + smull2 v21.4s, v17.8h, v30.8h + sqrshl v18.4s, v18.4s, v31.4s + sqrshl v19.4s, v19.4s, v31.4s + sqrshl v20.4s, v20.4s, v31.4s + sqrshl v21.4s, v21.4s, v31.4s + sqadd v18.4s, v18.4s, v29.4s + sqadd v19.4s, v19.4s, v29.4s + sqadd v20.4s, v20.4s, v29.4s + sqadd v21.4s, v21.4s, v29.4s + sqxtn v16.4h, v18.4s + sqxtn2 v16.8h, v19.4s + sqxtn v17.4h, v20.4s + sqxtn2 v17.8h, v21.4s + sqxtun v16.8b, v16.8h + sqxtun v17.8b, v17.8h + st1 {v16.8b, v17.8b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h24_8_neon, export=1 + EPEL_UNI_W_H_HEADER 8h + sxtl v0.8h, v28.8b +1: + ld1 {v1.8b, v2.8b, v3.8b, v4.8b}, [x2], x3 + subs w4, w4, #1 + uxtl v1.8h, v1.8b + uxtl v2.8h, v2.8b + uxtl v3.8h, v3.8b + uxtl v4.8h, v4.8b + ext v5.16b, v1.16b, v2.16b, #2 + ext v6.16b, v1.16b, v2.16b, #4 + ext v7.16b, v1.16b, v2.16b, #6 + ext v20.16b, v2.16b, v3.16b, #2 + ext v21.16b, v2.16b, v3.16b, #4 + ext v22.16b, v2.16b, v3.16b, #6 + ext v23.16b, v3.16b, v4.16b, #2 + ext v24.16b, v3.16b, v4.16b, #4 + ext v25.16b, v3.16b, v4.16b, #6 + mul v16.8h, v1.8h, v0.h[0] + mla v16.8h, v5.8h, v0.h[1] + mla v16.8h, v6.8h, v0.h[2] + mla v16.8h, v7.8h, v0.h[3] + mul v17.8h, v2.8h, v0.h[0] + mla v17.8h, v20.8h, v0.h[1] + mla v17.8h, v21.8h, v0.h[2] + mla v17.8h, v22.8h, v0.h[3] + mul v18.8h, v3.8h, v0.h[0] + mla v18.8h, v23.8h, v0.h[1] + mla v18.8h, v24.8h, v0.h[2] + mla v18.8h, v25.8h, v0.h[3] + smull v20.4s, v16.4h, v30.4h + smull2 v21.4s, v16.8h, v30.8h + smull v22.4s, v17.4h, v30.4h + smull2 v23.4s, v17.8h, v30.8h + smull v24.4s, v18.4h, v30.4h + smull2 v25.4s, v18.8h, v30.8h + sqrshl v20.4s, v20.4s, v31.4s + sqrshl v21.4s, v21.4s, v31.4s + sqrshl v22.4s, v22.4s, v31.4s + sqrshl v23.4s, v23.4s, v31.4s + sqrshl v24.4s, v24.4s, v31.4s + sqrshl v25.4s, v25.4s, v31.4s + sqadd v20.4s, v20.4s, v29.4s + sqadd v21.4s, v21.4s, v29.4s + sqadd v22.4s, v22.4s, v29.4s + sqadd v23.4s, v23.4s, v29.4s + sqadd v24.4s, v24.4s, v29.4s + sqadd v25.4s, v25.4s, v29.4s + sqxtn v16.4h, v20.4s + sqxtn2 v16.8h, v21.4s + sqxtn v17.4h, v22.4s + sqxtn2 v17.8h, v23.4s + sqxtn v18.4h, v24.4s + sqxtn2 v18.8h, v25.4s + sqxtun v16.8b, v16.8h + sqxtun v17.8b, v17.8h + sqxtun v18.8b, v18.8h + st1 {v16.8b, v17.8b, v18.8b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h32_8_neon, export=1 + EPEL_UNI_W_H_HEADER 8h + ldr w10, [sp, #16] // width + ld1 {v1.8b}, [x2], #8 + sub x3, x3, w10, uxtw // decrement src stride + mov w11, w10 // original width + sub x3, x3, #8 // decrement src stride + sub x1, x1, w10, uxtw // decrement dst stride + sxtl v0.8h, v28.8b + uxtl v1.8h, v1.8b +1: + ld1 {v2.8b, v3.8b}, [x2], #16 + subs w10, w10, #16 // width + uxtl v2.8h, v2.8b + uxtl v3.8h, v3.8b + ext v5.16b, v1.16b, v2.16b, #2 + ext v6.16b, v1.16b, v2.16b, #4 + ext v7.16b, v1.16b, v2.16b, #6 + ext v20.16b, v2.16b, v3.16b, #2 + ext v21.16b, v2.16b, v3.16b, #4 + ext v22.16b, v2.16b, v3.16b, #6 + mul v16.8h, v1.8h, v0.h[0] + mla v16.8h, v5.8h, v0.h[1] + mla v16.8h, v6.8h, v0.h[2] + mla v16.8h, v7.8h, v0.h[3] + mul v17.8h, v2.8h, v0.h[0] + mla v17.8h, v20.8h, v0.h[1] + mla v17.8h, v21.8h, v0.h[2] + mla v17.8h, v22.8h, v0.h[3] + smull v18.4s, v16.4h, v30.4h + smull2 v19.4s, v16.8h, v30.8h + smull v20.4s, v17.4h, v30.4h + smull2 v21.4s, v17.8h, v30.8h + sqrshl v18.4s, v18.4s, v31.4s + sqrshl v19.4s, v19.4s, v31.4s + sqrshl v20.4s, v20.4s, v31.4s + sqrshl v21.4s, v21.4s, v31.4s + sqadd v18.4s, v18.4s, v29.4s + sqadd v19.4s, v19.4s, v29.4s + sqadd v20.4s, v20.4s, v29.4s + sqadd v21.4s, v21.4s, v29.4s + sqxtn v16.4h, v18.4s + sqxtn2 v16.8h, v19.4s + sqxtn v17.4h, v20.4s + sqxtn2 v17.8h, v21.4s + sqxtun v16.8b, v16.8h + sqxtun v17.8b, v17.8h + st1 {v16.8b, v17.8b}, [x0], #16 + mov v1.16b, v3.16b + b.gt 1b + subs w4, w4, #1 // height + add x2, x2, x3 + b.le 9f + ld1 {v1.8b}, [x2], #8 + mov w10, w11 + add x0, x0, x1 + uxtl v1.8h, v1.8b + b 1b +9: + ret +endfunc + + +#if HAVE_I8MM +ENABLE_I8MM +function ff_hevc_put_hevc_epel_h4_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v4.8b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.8b, v4.8b, v4.8b, #1 + ext v6.8b, v4.8b, v4.8b, #2 + ext v7.8b, v4.8b, v4.8b, #3 + trn1 v4.2s, v4.2s, v5.2s + trn1 v6.2s, v6.2s, v7.2s + trn1 v4.2d, v4.2d, v6.2d + movi v16.16b, #0 + usdot v16.4s, v4.16b, v30.16b + xtn v16.4h, v16.4s + st1 {v16.4h}, [x0], x10 + b.ne 1b + ret +endfunc + + +function ff_hevc_put_hevc_epel_h6_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v4.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.16b, v4.16b, v4.16b, #1 + ext v6.8b, v4.8b, v4.8b, #2 + ext v7.8b, v4.8b, v4.8b, #3 + trn1 v16.2s, v4.2s, v5.2s + trn2 v17.2s, v4.2s, v5.2s + trn1 v6.2s, v6.2s, v7.2s + trn1 v16.2d, v16.2d, v6.2d + movi v18.16b, #0 + movi v19.16b, #0 + usdot v18.4s, v16.16b, v30.16b + usdot v19.2s, v17.8b, v30.8b + xtn v18.4h, v18.4s + xtn v19.4h, v19.4s + str d18, [x0] + str s19, [x0, #8] + add x0, x0, x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h8_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v4.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.16b, v4.16b, v4.16b, #1 + ext v6.16b, v4.16b, v4.16b, #2 + ext v7.16b, v4.16b, v4.16b, #3 + zip1 v20.4s, v4.4s, v6.4s + zip1 v21.4s, v5.4s, v7.4s + movi v16.16b, #0 + movi v17.16b, #0 + usdot v16.4s, v20.16b, v30.16b + usdot v17.4s, v21.16b, v30.16b + xtn v16.4h, v16.4s + xtn v17.4h, v17.4s + st2 {v16.4h, v17.4h}, [x0], x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h12_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v4.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.16b, v4.16b, v4.16b, #1 + ext v6.16b, v4.16b, v4.16b, #2 + ext v7.16b, v4.16b, v4.16b, #3 + trn1 v20.2d, v4.2d, v6.2d + trn2 v22.2d, v4.2d, v6.2d + trn1 v21.2d, v5.2d, v7.2d + trn2 v23.2d, v5.2d, v7.2d + trn1 v4.4s, v20.4s, v21.4s + trn2 v5.4s, v20.4s, v21.4s + trn1 v6.4s, v22.4s, v23.4s + movi v16.16b, #0 + movi v17.16b, #0 + movi v18.16b, #0 + usdot v16.4s, v4.16b, v30.16b + usdot v17.4s, v5.16b, v30.16b + usdot v18.4s, v6.16b, v30.16b + xtn v16.4h, v16.4s + xtn2 v16.8h, v17.4s + xtn v18.4h, v18.4s + str q16, [x0] + str d18, [x0, #16] + add x0, x0, x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h16_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v0.16b, v1.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.16b, v0.16b, v1.16b, #1 + ext v6.16b, v0.16b, v1.16b, #2 + ext v7.16b, v0.16b, v1.16b, #3 + zip1 v20.4s, v0.4s, v6.4s + zip2 v22.4s, v0.4s, v6.4s + zip1 v21.4s, v5.4s, v7.4s + zip2 v23.4s, v5.4s, v7.4s + movi v16.16b, #0 + movi v17.16b, #0 + movi v18.16b, #0 + movi v19.16b, #0 + usdot v16.4s, v20.16b, v30.16b + usdot v17.4s, v21.16b, v30.16b + usdot v18.4s, v22.16b, v30.16b + usdot v19.4s, v23.16b, v30.16b + xtn v16.4h, v16.4s + xtn2 v16.8h, v18.4s + xtn v17.4h, v17.4s + xtn2 v17.8h, v19.4s + st2 {v16.8h, v17.8h}, [x0], x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h24_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v0.16b, v1.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.16b, v0.16b, v1.16b, #1 + ext v6.16b, v0.16b, v1.16b, #2 + ext v7.16b, v0.16b, v1.16b, #3 + ext v26.16b, v1.16b, v1.16b, #1 + ext v27.16b, v1.16b, v1.16b, #2 + ext v28.16b, v1.16b, v1.16b, #3 + movi v16.16b, #0 + movi v17.16b, #0 + movi v18.16b, #0 + movi v19.16b, #0 + movi v20.16b, #0 + movi v21.16b, #0 + movi v22.16b, #0 + movi v23.16b, #0 + usdot v16.4s, v0.16b, v30.16b + usdot v17.4s, v5.16b, v30.16b + usdot v18.4s, v6.16b, v30.16b + usdot v19.4s, v7.16b, v30.16b + usdot v20.4s, v1.16b, v30.16b + usdot v21.4s, v26.16b, v30.16b + usdot v22.4s, v27.16b, v30.16b + usdot v23.4s, v28.16b, v30.16b + xtn v16.4h, v16.4s + xtn2 v16.8h, v20.4s + xtn v17.4h, v17.4s + xtn2 v17.8h, v21.4s + xtn v18.4h, v18.4s + xtn2 v18.8h, v22.4s + xtn v19.4h, v19.4s + xtn2 v19.8h, v23.4s + zip1 v20.8h, v16.8h, v18.8h + zip1 v21.8h, v17.8h, v19.8h + zip2 v22.8h, v16.8h, v18.8h + zip2 v23.8h, v17.8h, v19.8h + zip1 v22.8h, v22.8h, v23.8h + add x7, x0, #32 + st2 {v20.8h, v21.8h}, [x0], x10 + st1 {v22.8h}, [x7] + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h32_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v0.16b, v1.16b, v2.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v5.16b, v0.16b, v1.16b, #1 + ext v6.16b, v0.16b, v1.16b, #2 + ext v7.16b, v0.16b, v1.16b, #3 + ext v26.16b, v1.16b, v2.16b, #1 + ext v27.16b, v1.16b, v2.16b, #2 + ext v28.16b, v1.16b, v2.16b, #3 + movi v16.16b, #0 + movi v17.16b, #0 + movi v18.16b, #0 + movi v19.16b, #0 + movi v20.16b, #0 + movi v21.16b, #0 + movi v22.16b, #0 + movi v23.16b, #0 + usdot v16.4s, v0.16b, v30.16b + usdot v17.4s, v5.16b, v30.16b + usdot v18.4s, v6.16b, v30.16b + usdot v19.4s, v7.16b, v30.16b + usdot v20.4s, v1.16b, v30.16b + usdot v21.4s, v26.16b, v30.16b + usdot v22.4s, v27.16b, v30.16b + usdot v23.4s, v28.16b, v30.16b + xtn v16.4h, v16.4s + xtn2 v16.8h, v20.4s + xtn v17.4h, v17.4s + xtn2 v17.8h, v21.4s + xtn v18.4h, v18.4s + xtn2 v18.8h, v22.4s + xtn v19.4h, v19.4s + xtn2 v19.8h, v23.4s + st4 {v16.8h, v17.8h, v18.8h, v19.8h}, [x0], x10 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h48_8_neon_i8mm, export=1 + EPEL_H_HEADER +1: ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x1], x2 + subs w3, w3, #1 // height + ext v4.16b, v0.16b, v1.16b, #1 + ext v5.16b, v0.16b, v1.16b, #2 + ext v6.16b, v0.16b, v1.16b, #3 + ext v16.16b, v1.16b, v2.16b, #1 + ext v17.16b, v1.16b, v2.16b, #2 + ext v18.16b, v1.16b, v2.16b, #3 + movi v20.16b, #0 + movi v21.16b, #0 + movi v22.16b, #0 + movi v23.16b, #0 + usdot v20.4s, v0.16b, v30.16b + usdot v21.4s, v4.16b, v30.16b + usdot v22.4s, v5.16b, v30.16b + usdot v23.4s, v6.16b, v30.16b + movi v24.16b, #0 + movi v25.16b, #0 + movi v26.16b, #0 + movi v27.16b, #0 + usdot v24.4s, v1.16b, v30.16b + usdot v25.4s, v16.16b, v30.16b + usdot v26.4s, v17.16b, v30.16b + usdot v27.4s, v18.16b, v30.16b + xtn v20.4h, v20.4s + xtn2 v20.8h, v24.4s + xtn v21.4h, v21.4s + xtn2 v21.8h, v25.4s + xtn v22.4h, v22.4s + xtn2 v22.8h, v26.4s + xtn v23.4h, v23.4s + xtn2 v23.8h, v27.4s + add x7, x0, #64 + st4 {v20.8h, v21.8h, v22.8h, v23.8h}, [x0], x10 + ext v4.16b, v2.16b, v3.16b, #1 + ext v5.16b, v2.16b, v3.16b, #2 + ext v6.16b, v2.16b, v3.16b, #3 + movi v20.16b, #0 + movi v21.16b, #0 + movi v22.16b, #0 + movi v23.16b, #0 + usdot v20.4s, v2.16b, v30.16b + usdot v21.4s, v4.16b, v30.16b + usdot v22.4s, v5.16b, v30.16b + usdot v23.4s, v6.16b, v30.16b + zip1 v24.4s, v20.4s, v22.4s + zip2 v25.4s, v20.4s, v22.4s + zip1 v26.4s, v21.4s, v23.4s + zip2 v27.4s, v21.4s, v23.4s + xtn v20.4h, v24.4s + xtn2 v20.8h, v25.4s + xtn v21.4h, v26.4s + xtn2 v21.8h, v27.4s + st2 {v20.8h, v21.8h}, [x7] + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_h64_8_neon_i8mm, export=1 + EPEL_H_HEADER + sub x2, x2, #64 +1: ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x1], #64 + subs w3, w3, #1 // height + ext v4.16b, v0.16b, v1.16b, #1 + ext v5.16b, v0.16b, v1.16b, #2 + ext v6.16b, v0.16b, v1.16b, #3 + ext v16.16b, v1.16b, v2.16b, #1 + ext v17.16b, v1.16b, v2.16b, #2 + ext v18.16b, v1.16b, v2.16b, #3 + movi v20.16b, #0 + movi v21.16b, #0 + movi v22.16b, #0 + movi v23.16b, #0 + usdot v20.4s, v0.16b, v30.16b + usdot v21.4s, v4.16b, v30.16b + usdot v22.4s, v5.16b, v30.16b + usdot v23.4s, v6.16b, v30.16b + movi v24.16b, #0 + movi v25.16b, #0 + movi v26.16b, #0 + movi v27.16b, #0 + usdot v24.4s, v1.16b, v30.16b + usdot v25.4s, v16.16b, v30.16b + usdot v26.4s, v17.16b, v30.16b + usdot v27.4s, v18.16b, v30.16b + xtn v20.4h, v20.4s + xtn2 v20.8h, v24.4s + xtn v21.4h, v21.4s + xtn2 v21.8h, v25.4s + xtn v22.4h, v22.4s + xtn2 v22.8h, v26.4s + xtn v23.4h, v23.4s + xtn2 v23.8h, v27.4s + st4 {v20.8h, v21.8h, v22.8h, v23.8h}, [x0], #64 + ld1 {v7.8b}, [x1], x2 + ext v4.16b, v2.16b, v3.16b, #1 + ext v5.16b, v2.16b, v3.16b, #2 + ext v6.16b, v2.16b, v3.16b, #3 + ext v16.16b, v3.16b, v7.16b, #1 + ext v17.16b, v3.16b, v7.16b, #2 + ext v18.16b, v3.16b, v7.16b, #3 + movi v20.16b, #0 + movi v21.16b, #0 + movi v22.16b, #0 + movi v23.16b, #0 + usdot v20.4s, v2.16b, v30.16b + usdot v21.4s, v4.16b, v30.16b + usdot v22.4s, v5.16b, v30.16b + usdot v23.4s, v6.16b, v30.16b + movi v24.16b, #0 + movi v25.16b, #0 + movi v26.16b, #0 + movi v27.16b, #0 + usdot v24.4s, v3.16b, v30.16b + usdot v25.4s, v16.16b, v30.16b + usdot v26.4s, v17.16b, v30.16b + usdot v27.4s, v18.16b, v30.16b + xtn v20.4h, v20.4s + xtn2 v20.8h, v24.4s + xtn v21.4h, v21.4s + xtn2 v21.8h, v25.4s + xtn v22.4h, v22.4s + xtn2 v22.8h, v26.4s + xtn v23.4h, v23.4s + xtn2 v23.8h, v27.4s + st4 {v20.8h, v21.8h, v22.8h, v23.8h}, [x0], #64 + b.ne 1b + ret +endfunc + +DISABLE_I8MM +#endif + + +function hevc_put_hevc_epel_hv4_8_end_neon + load_epel_filterh x5, x4 + mov x10, #(MAX_PB_SIZE * 2) + ldr d16, [sp] + ldr d17, [sp, x10] + add sp, sp, x10, lsl #1 + ld1 {v18.4h}, [sp], x10 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().4h}, [sp], x10 + calc_epelh v4, \src0, \src1, \src2, \src3 + subs w3, w3, #1 + st1 {v4.4h}, [x0], x10 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_hv6_8_end_neon + load_epel_filterh x5, x4 + mov x5, #120 + mov x10, #(MAX_PB_SIZE * 2) + ldr q16, [sp] + ldr q17, [sp, x10] + add sp, sp, x10, lsl #1 + ld1 {v18.8h}, [sp], x10 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().8h}, [sp], x10 + calc_epelh v4, \src0, \src1, \src2, \src3 + calc_epelh2 v4, v5, \src0, \src1, \src2, \src3 + st1 {v4.d}[0], [x0], #8 + subs w3, w3, #1 + st1 {v4.s}[2], [x0], x5 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_hv8_8_end_neon + load_epel_filterh x5, x4 + mov x10, #(MAX_PB_SIZE * 2) + ldr q16, [sp] + ldr q17, [sp, x10] + add sp, sp, x10, lsl #1 + ld1 {v18.8h}, [sp], x10 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().8h}, [sp], x10 + calc_epelh v4, \src0, \src1, \src2, \src3 + calc_epelh2 v4, v5, \src0, \src1, \src2, \src3 + subs w3, w3, #1 + st1 {v4.8h}, [x0], x10 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_hv12_8_end_neon + load_epel_filterh x5, x4 + mov x5, #112 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h}, [sp], x10 + ld1 {v18.8h, v19.8h}, [sp], x10 + ld1 {v20.8h, v21.8h}, [sp], x10 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\src6\().8h, \src7\().8h}, [sp], x10 + calc_epelh v4, \src0, \src2, \src4, \src6 + calc_epelh2 v4, v5, \src0, \src2, \src4, \src6 + calc_epelh v5, \src1, \src3, \src5, \src7 + st1 {v4.8h}, [x0], #16 + subs w3, w3, #1 + st1 {v5.4h}, [x0], x5 +.endm +1: calc_all8 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_hv16_8_end_neon + load_epel_filterh x5, x4 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h}, [sp], x10 + ld1 {v18.8h, v19.8h}, [sp], x10 + ld1 {v20.8h, v21.8h}, [sp], x10 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\src6\().8h, \src7\().8h}, [sp], x10 + calc_epelh v4, \src0, \src2, \src4, \src6 + calc_epelh2 v4, v5, \src0, \src2, \src4, \src6 + calc_epelh v5, \src1, \src3, \src5, \src7 + calc_epelh2 v5, v6, \src1, \src3, \src5, \src7 + subs w3, w3, #1 + st1 {v4.8h, v5.8h}, [x0], x10 +.endm +1: calc_all8 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_hv24_8_end_neon + load_epel_filterh x5, x4 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h, v18.8h}, [sp], x10 + ld1 {v19.8h, v20.8h, v21.8h}, [sp], x10 + ld1 {v22.8h, v23.8h, v24.8h}, [sp], x10 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11 + ld1 {\src9\().8h-\src11\().8h}, [sp], x10 + calc_epelh v4, \src0, \src3, \src6, \src9 + calc_epelh2 v4, v5, \src0, \src3, \src6, \src9 + calc_epelh v5, \src1, \src4, \src7, \src10 + calc_epelh2 v5, v6, \src1, \src4, \src7, \src10 + calc_epelh v6, \src2, \src5, \src8, \src11 + calc_epelh2 v6, v7, \src2, \src5, \src8, \src11 + subs w3, w3, #1 + st1 {v4.8h-v6.8h}, [x0], x10 +.endm +1: calc_all12 +.purgem calc +2: ret +endfunc + +.macro epel_hv suffix +function ff_hevc_put_hevc_epel_hv4_8_\suffix, export=1 + add w10, w3, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + stp x5, x30, [sp, #-32]! + stp x0, x3, [sp, #16] + add x0, sp, #32 + sub x1, x1, x2 + add w3, w3, #3 + bl X(ff_hevc_put_hevc_epel_h4_8_\suffix) + ldp x0, x3, [sp, #16] + ldp x5, x30, [sp], #32 + b hevc_put_hevc_epel_hv4_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_hv6_8_\suffix, export=1 + add w10, w3, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + stp x5, x30, [sp, #-32]! + stp x0, x3, [sp, #16] + add x0, sp, #32 + sub x1, x1, x2 + add w3, w3, #3 + bl X(ff_hevc_put_hevc_epel_h6_8_\suffix) + ldp x0, x3, [sp, #16] + ldp x5, x30, [sp], #32 + b hevc_put_hevc_epel_hv6_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_hv8_8_\suffix, export=1 + add w10, w3, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + stp x5, x30, [sp, #-32]! + stp x0, x3, [sp, #16] + add x0, sp, #32 + sub x1, x1, x2 + add w3, w3, #3 + bl X(ff_hevc_put_hevc_epel_h8_8_\suffix) + ldp x0, x3, [sp, #16] + ldp x5, x30, [sp], #32 + b hevc_put_hevc_epel_hv8_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_hv12_8_\suffix, export=1 + add w10, w3, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + stp x5, x30, [sp, #-32]! + stp x0, x3, [sp, #16] + add x0, sp, #32 + sub x1, x1, x2 + add w3, w3, #3 + bl X(ff_hevc_put_hevc_epel_h12_8_\suffix) + ldp x0, x3, [sp, #16] + ldp x5, x30, [sp], #32 + b hevc_put_hevc_epel_hv12_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_hv16_8_\suffix, export=1 + add w10, w3, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + stp x5, x30, [sp, #-32]! + stp x0, x3, [sp, #16] + add x0, sp, #32 + sub x1, x1, x2 + add w3, w3, #3 + bl X(ff_hevc_put_hevc_epel_h16_8_\suffix) + ldp x0, x3, [sp, #16] + ldp x5, x30, [sp], #32 + b hevc_put_hevc_epel_hv16_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_hv24_8_\suffix, export=1 + add w10, w3, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + stp x5, x30, [sp, #-32]! + stp x0, x3, [sp, #16] + add x0, sp, #32 + sub x1, x1, x2 + add w3, w3, #3 + bl X(ff_hevc_put_hevc_epel_h24_8_\suffix) + ldp x0, x3, [sp, #16] + ldp x5, x30, [sp], #32 + b hevc_put_hevc_epel_hv24_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_hv32_8_\suffix, export=1 + stp x4, x5, [sp, #-64]! + stp x2, x3, [sp, #16] + stp x0, x1, [sp, #32] + str x30, [sp, #48] + mov x6, #16 + bl X(ff_hevc_put_hevc_epel_hv16_8_\suffix) + ldp x0, x1, [sp, #32] + ldp x2, x3, [sp, #16] + ldp x4, x5, [sp], #48 + add x0, x0, #32 + add x1, x1, #16 + mov x6, #16 + bl X(ff_hevc_put_hevc_epel_hv16_8_\suffix) + ldr x30, [sp], #16 + ret +endfunc + +function ff_hevc_put_hevc_epel_hv48_8_\suffix, export=1 + stp x4, x5, [sp, #-64]! + stp x2, x3, [sp, #16] + stp x0, x1, [sp, #32] + str x30, [sp, #48] + mov x6, #24 + bl X(ff_hevc_put_hevc_epel_hv24_8_\suffix) + ldp x0, x1, [sp, #32] + ldp x2, x3, [sp, #16] + ldp x4, x5, [sp], #48 + add x0, x0, #48 + add x1, x1, #24 + mov x6, #24 + bl X(ff_hevc_put_hevc_epel_hv24_8_\suffix) + ldr x30, [sp], #16 + ret +endfunc + +function ff_hevc_put_hevc_epel_hv64_8_\suffix, export=1 + stp x4, x5, [sp, #-64]! + stp x2, x3, [sp, #16] + stp x0, x1, [sp, #32] + str x30, [sp, #48] + mov x6, #16 + bl X(ff_hevc_put_hevc_epel_hv16_8_\suffix) + ldp x4, x5, [sp] + ldp x2, x3, [sp, #16] + ldp x0, x1, [sp, #32] + add x0, x0, #32 + add x1, x1, #16 + mov x6, #16 + bl X(ff_hevc_put_hevc_epel_hv16_8_\suffix) + ldp x4, x5, [sp] + ldp x2, x3, [sp, #16] + ldp x0, x1, [sp, #32] + add x0, x0, #64 + add x1, x1, #32 + mov x6, #16 + bl X(ff_hevc_put_hevc_epel_hv16_8_\suffix) + ldp x0, x1, [sp, #32] + ldp x2, x3, [sp, #16] + ldp x4, x5, [sp], #48 + add x0, x0, #96 + add x1, x1, #48 + mov x6, #16 + bl X(ff_hevc_put_hevc_epel_hv16_8_\suffix) + ldr x30, [sp], #16 + ret +endfunc +.endm + +epel_hv neon + +function hevc_put_hevc_epel_uni_hv4_8_end_neon + load_epel_filterh x6, x5 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.4h}, [sp], x10 + ld1 {v17.4h}, [sp], x10 + ld1 {v18.4h}, [sp], x10 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().4h}, [sp], x10 + calc_epelh v4, \src0, \src1, \src2, \src3 + sqrshrun v4.8b, v4.8h, #6 + subs w4, w4, #1 + st1 {v4.s}[0], [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_uni_hv6_8_end_neon + load_epel_filterh x6, x5 + sub x1, x1, #4 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h}, [sp], x10 + ld1 {v17.8h}, [sp], x10 + ld1 {v18.8h}, [sp], x10 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().8h}, [sp], x10 + calc_epelh v4, \src0, \src1, \src2, \src3 + calc_epelh2 v4, v5, \src0, \src1, \src2, \src3 + sqrshrun v4.8b, v4.8h, #6 + st1 {v4.s}[0], [x0], #4 + subs w4, w4, #1 + st1 {v4.h}[2], [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_uni_hv8_8_end_neon + load_epel_filterh x6, x5 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h}, [sp], x10 + ld1 {v17.8h}, [sp], x10 + ld1 {v18.8h}, [sp], x10 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().8h}, [sp], x10 + calc_epelh v4, \src0, \src1, \src2, \src3 + calc_epelh2 v4, v5, \src0, \src1, \src2, \src3 + sqrshrun v4.8b, v4.8h, #6 + subs w4, w4, #1 + st1 {v4.8b}, [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_uni_hv12_8_end_neon + load_epel_filterh x6, x5 + sub x1, x1, #8 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h}, [sp], x10 + ld1 {v18.8h, v19.8h}, [sp], x10 + ld1 {v20.8h, v21.8h}, [sp], x10 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\src6\().8h, \src7\().8h}, [sp], x10 + calc_epelh v4, \src0, \src2, \src4, \src6 + calc_epelh2 v4, v5, \src0, \src2, \src4, \src6 + calc_epelh v5, \src1, \src3, \src5, \src7 + sqrshrun v4.8b, v4.8h, #6 + sqrshrun2 v4.16b, v5.8h, #6 + st1 {v4.8b}, [x0], #8 + st1 {v4.s}[2], [x0], x1 + subs w4, w4, #1 +.endm +1: calc_all8 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_uni_hv16_8_end_neon + load_epel_filterh x6, x5 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h}, [sp], x10 + ld1 {v18.8h, v19.8h}, [sp], x10 + ld1 {v20.8h, v21.8h}, [sp], x10 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\src6\().8h, \src7\().8h}, [sp], x10 + calc_epelh v4, \src0, \src2, \src4, \src6 + calc_epelh2 v4, v5, \src0, \src2, \src4, \src6 + calc_epelh v5, \src1, \src3, \src5, \src7 + calc_epelh2 v5, v6, \src1, \src3, \src5, \src7 + sqrshrun v4.8b, v4.8h, #6 + sqrshrun2 v4.16b, v5.8h, #6 + subs w4, w4, #1 + st1 {v4.16b}, [x0], x1 +.endm +1: calc_all8 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_uni_hv24_8_end_neon + load_epel_filterh x6, x5 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h, v18.8h}, [sp], x10 + ld1 {v19.8h, v20.8h, v21.8h}, [sp], x10 + ld1 {v22.8h, v23.8h, v24.8h}, [sp], x10 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11 + ld1 {\src9\().8h, \src10\().8h, \src11\().8h}, [sp], x10 + calc_epelh v4, \src0, \src3, \src6, \src9 + calc_epelh2 v4, v5, \src0, \src3, \src6, \src9 + calc_epelh v5, \src1, \src4, \src7, \src10 + calc_epelh2 v5, v6, \src1, \src4, \src7, \src10 + calc_epelh v6, \src2, \src5, \src8, \src11 + calc_epelh2 v6, v7, \src2, \src5, \src8, \src11 + sqrshrun v4.8b, v4.8h, #6 + sqrshrun v5.8b, v5.8h, #6 + sqrshrun v6.8b, v6.8h, #6 + subs w4, w4, #1 + st1 {v4.8b, v5.8b, v6.8b}, [x0], x1 +.endm +1: calc_all12 +.purgem calc +2: ret +endfunc + +.macro epel_uni_hv suffix +function ff_hevc_put_hevc_epel_uni_hv4_8_\suffix, export=1 + add w10, w4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add w3, w4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h4_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + b hevc_put_hevc_epel_uni_hv4_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_uni_hv6_8_\suffix, export=1 + add w10, w4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add w3, w4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h6_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + b hevc_put_hevc_epel_uni_hv6_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_uni_hv8_8_\suffix, export=1 + add w10, w4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add w3, w4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h8_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + b hevc_put_hevc_epel_uni_hv8_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_uni_hv12_8_\suffix, export=1 + add w10, w4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add w3, w4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h12_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + b hevc_put_hevc_epel_uni_hv12_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_uni_hv16_8_\suffix, export=1 + add w10, w4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add w3, w4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h16_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + b hevc_put_hevc_epel_uni_hv16_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_uni_hv24_8_\suffix, export=1 + add w10, w4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add w3, w4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h24_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + b hevc_put_hevc_epel_uni_hv24_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_uni_hv32_8_\suffix, export=1 + stp x5, x6, [sp, #-64]! + stp x3, x4, [sp, #16] + stp x1, x2, [sp, #32] + stp x0, x30, [sp, #48] + mov x7, #16 + bl X(ff_hevc_put_hevc_epel_uni_hv16_8_\suffix) + ldp x5, x6, [sp] + ldp x3, x4, [sp, #16] + ldp x1, x2, [sp, #32] + ldr x0, [sp, #48] + add x0, x0, #16 + add x2, x2, #16 + mov x7, #16 + bl X(ff_hevc_put_hevc_epel_uni_hv16_8_\suffix) + ldr x30, [sp, #56] + add sp, sp, #64 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_hv48_8_\suffix, export=1 + stp x5, x6, [sp, #-64]! + stp x3, x4, [sp, #16] + stp x1, x2, [sp, #32] + stp x0, x30, [sp, #48] + mov x7, #24 + bl X(ff_hevc_put_hevc_epel_uni_hv24_8_\suffix) + ldp x5, x6, [sp] + ldp x3, x4, [sp, #16] + ldp x1, x2, [sp, #32] + ldr x0, [sp, #48] + add x0, x0, #24 + add x2, x2, #24 + mov x7, #24 + bl X(ff_hevc_put_hevc_epel_uni_hv24_8_\suffix) + ldr x30, [sp, #56] + add sp, sp, #64 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_hv64_8_\suffix, export=1 + stp x5, x6, [sp, #-64]! + stp x3, x4, [sp, #16] + stp x1, x2, [sp, #32] + stp x0, x30, [sp, #48] + mov x7, #16 + bl X(ff_hevc_put_hevc_epel_uni_hv16_8_\suffix) + ldp x5, x6, [sp] + ldp x3, x4, [sp, #16] + ldp x1, x2, [sp, #32] + ldr x0, [sp, #48] + add x0, x0, #16 + add x2, x2, #16 + mov x7, #16 + bl X(ff_hevc_put_hevc_epel_uni_hv16_8_\suffix) + ldp x5, x6, [sp] + ldp x3, x4, [sp, #16] + ldp x1, x2, [sp, #32] + ldr x0, [sp, #48] + add x0, x0, #32 + add x2, x2, #32 + mov x7, #16 + bl X(ff_hevc_put_hevc_epel_uni_hv16_8_\suffix) + ldp x5, x6, [sp] + ldp x3, x4, [sp, #16] + ldp x1, x2, [sp, #32] + ldr x0, [sp, #48] + add x0, x0, #48 + add x2, x2, #48 + mov x7, #16 + bl X(ff_hevc_put_hevc_epel_uni_hv16_8_\suffix) + ldr x30, [sp, #56] + add sp, sp, #64 + ret +endfunc +.endm + +epel_uni_hv neon + +#if HAVE_I8MM +ENABLE_I8MM + +epel_hv neon_i8mm + +epel_uni_hv neon_i8mm + +function ff_hevc_put_hevc_epel_uni_w_h4_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER +1: + ld1 {v0.8b}, [x2], x3 + subs w4, w4, #1 + ext v1.8b, v0.8b, v0.8b, #1 + ext v2.8b, v0.8b, v0.8b, #2 + ext v3.8b, v0.8b, v0.8b, #3 + trn1 v0.2s, v0.2s, v2.2s + trn1 v1.2s, v1.2s, v3.2s + zip1 v0.4s, v0.4s, v1.4s + movi v16.16b, #0 + usdot v16.4s, v0.16b, v28.16b + mul v16.4s, v16.4s, v30.4s + sqrshl v16.4s, v16.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtun v16.8b, v16.8h + str s16, [x0] + add x0, x0, x1 + b.hi 1b + ret +endfunc + + +function ff_hevc_put_hevc_epel_uni_w_h6_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER + sub x1, x1, #4 +1: + ld1 {v0.16b}, [x2], x3 + subs w4, w4, #1 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + trn1 v4.2s, v0.2s, v1.2s + trn2 v6.2s, v0.2s, v1.2s + trn1 v5.2s, v2.2s, v3.2s + zip1 v4.2d, v4.2d, v5.2d + movi v16.16b, #0 + movi v17.16b, #0 + usdot v16.4s, v4.16b, v28.16b + usdot v17.2s, v6.8b, v28.8b + mul v16.4s, v16.4s, v30.4s + mul v17.2s, v17.2s, v30.2s + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v17.2s, v17.2s, v31.2s + sqadd v16.4s, v16.4s, v29.4s + sqadd v17.2s, v17.2s, v29.2s + sqxtn v16.4h, v16.4s + sqxtn2 v16.8h, v17.4s + sqxtun v16.8b, v16.8h + str s16, [x0], #4 + st1 {v16.h}[2], [x0], x1 + b.hi 1b + ret +endfunc + +.macro EPEL_UNI_W_H_CALC s0, s1, d0, d1 + movi \d0\().16b, #0 + movi \d1\().16b, #0 + usdot \d0\().4s, \s0\().16b, v28.16b + usdot \d1\().4s, \s1\().16b, v28.16b + mul \d0\().4s, \d0\().4s, v30.4s + mul \d1\().4s, \d1\().4s, v30.4s + sqrshl \d0\().4s, \d0\().4s, v31.4s + sqrshl \d1\().4s, \d1\().4s, v31.4s + sqadd \d0\().4s, \d0\().4s, v29.4s + sqadd \d1\().4s, \d1\().4s, v29.4s +.endm + +function ff_hevc_put_hevc_epel_uni_w_h8_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER +1: + ld1 {v0.16b}, [x2], x3 + subs w4, w4, #1 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + zip1 v4.4s, v0.4s, v2.4s + zip1 v5.4s, v1.4s, v3.4s + EPEL_UNI_W_H_CALC v4, v5, v16, v17 + sqxtn v16.4h, v16.4s + sqxtn v17.4h, v17.4s + zip1 v16.8h, v16.8h, v17.8h + sqxtun v16.8b, v16.8h + str d16, [x0] + add x0, x0, x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h12_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER +1: + ld1 {v0.16b}, [x2], x3 + subs w4, w4, #1 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + zip1 v4.4s, v0.4s, v2.4s + zip1 v5.4s, v1.4s, v3.4s + zip2 v6.4s, v0.4s, v2.4s + zip2 v7.4s, v1.4s, v3.4s + zip1 v6.4s, v6.4s, v7.4s + EPEL_UNI_W_H_CALC v4, v5, v16, v17 + movi v18.16b, #0 + usdot v18.4s, v6.16b, v28.16b + mul v18.4s, v18.4s, v30.4s + sqrshl v18.4s, v18.4s, v31.4s + sqadd v18.4s, v18.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtn v17.4h, v17.4s + sqxtn v18.4h, v18.4s + zip1 v16.8h, v16.8h, v17.8h + sqxtun v16.8b, v16.8h + sqxtun v18.8b, v18.8h + str d16, [x0] + str s18, [x0, #8] + add x0, x0, x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h16_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER +1: + ld1 {v0.16b, v1.16b}, [x2], x3 + subs w4, w4, #1 + ext v4.16b, v0.16b, v1.16b, #1 + ext v5.16b, v0.16b, v1.16b, #2 + ext v6.16b, v0.16b, v1.16b, #3 + zip1 v20.4s, v0.4s, v5.4s + zip1 v21.4s, v4.4s, v6.4s + zip2 v22.4s, v0.4s, v5.4s + zip2 v23.4s, v4.4s, v6.4s + EPEL_UNI_W_H_CALC v20, v21, v16, v17 + EPEL_UNI_W_H_CALC v22, v23, v18, v19 + sqxtn v16.4h, v16.4s + sqxtn v17.4h, v17.4s + sqxtn2 v16.8h, v18.4s + sqxtn2 v17.8h, v19.4s + sqxtun v16.8b, v16.8h + sqxtun v17.8b, v17.8h + st2 {v16.8b, v17.8b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h24_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER +1: + ld1 {v0.16b, v1.16b}, [x2], x3 + subs w4, w4, #1 + ext v2.16b, v0.16b, v1.16b, #1 + ext v3.16b, v0.16b, v1.16b, #2 + ext v4.16b, v0.16b, v1.16b, #3 + ext v5.16b, v1.16b, v1.16b, #1 + ext v6.16b, v1.16b, v1.16b, #2 + ext v7.16b, v1.16b, v1.16b, #3 + zip1 v20.4s, v0.4s, v3.4s + zip1 v21.4s, v2.4s, v4.4s + zip2 v22.4s, v0.4s, v3.4s + zip2 v23.4s, v2.4s, v4.4s + zip1 v24.4s, v1.4s, v6.4s + zip1 v25.4s, v5.4s, v7.4s + EPEL_UNI_W_H_CALC v20, v21, v16, v17 + EPEL_UNI_W_H_CALC v22, v23, v18, v19 + EPEL_UNI_W_H_CALC v24, v25, v26, v27 + sqxtn v16.4h, v16.4s + sqxtn v17.4h, v17.4s + sqxtn v18.4h, v18.4s + sqxtn v19.4h, v19.4s + sqxtn v26.4h, v26.4s + sqxtn v27.4h, v27.4s + zip1 v16.8h, v16.8h, v17.8h + zip1 v18.8h, v18.8h, v19.8h + zip1 v26.8h, v26.8h, v27.8h + sqxtun v16.8b, v16.8h + sqxtun2 v16.16b, v18.8h + sqxtun v26.8b, v26.8h + str q16, [x0] + str d26, [x0, #16] + add x0, x0, x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h32_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER +1: + ld1 {v0.16b, v1.16b, v2.16b}, [x2], x3 + subs w4, w4, #1 + ext v3.16b, v0.16b, v1.16b, #1 + ext v4.16b, v0.16b, v1.16b, #2 + ext v5.16b, v0.16b, v1.16b, #3 + ext v16.16b, v1.16b, v2.16b, #1 + ext v17.16b, v1.16b, v2.16b, #2 + ext v18.16b, v1.16b, v2.16b, #3 + EPEL_UNI_W_H_CALC v0, v3, v6, v7 + EPEL_UNI_W_H_CALC v4, v5, v19, v20 + EPEL_UNI_W_H_CALC v1, v16, v21, v22 + EPEL_UNI_W_H_CALC v17, v18, v23, v24 + sqxtn v6.4h, v6.4s + sqxtn2 v6.8h, v21.4s + sqxtn v7.4h, v7.4s + sqxtn2 v7.8h, v22.4s + sqxtn v19.4h, v19.4s + sqxtn2 v19.8h, v23.4s + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v24.4s + sqxtun v0.8b, v6.8h + sqxtun v1.8b, v7.8h + sqxtun v2.8b, v19.8h + sqxtun v3.8b, v20.8h + st4 {v0.8b, v1.8b, v2.8b, v3.8b}, [x0], x1 + b.hi 1b + ret +endfunc + + + +function ff_hevc_put_hevc_epel_uni_w_h48_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER + sub x1, x1, #32 +1: + ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x2], x3 + subs w4, w4, #1 + ext v4.16b, v0.16b, v1.16b, #1 + ext v5.16b, v0.16b, v1.16b, #2 + ext v6.16b, v0.16b, v1.16b, #3 + ext v16.16b, v1.16b, v2.16b, #1 + ext v17.16b, v1.16b, v2.16b, #2 + ext v18.16b, v1.16b, v2.16b, #3 + EPEL_UNI_W_H_CALC v0, v4, v19, v20 + EPEL_UNI_W_H_CALC v5, v6, v21, v22 + EPEL_UNI_W_H_CALC v1, v16, v23, v24 + EPEL_UNI_W_H_CALC v17, v18, v25, v26 + sqxtn v19.4h, v19.4s + sqxtn2 v19.8h, v23.4s + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v24.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v25.4s + sqxtn v22.4h, v22.4s + sqxtn2 v22.8h, v26.4s + sqxtun v19.8b, v19.8h + sqxtun v20.8b, v20.8h + sqxtun v21.8b, v21.8h + sqxtun v22.8b, v22.8h + st4 {v19.8b, v20.8b, v21.8b, v22.8b}, [x0], #32 + ext v5.16b, v2.16b, v3.16b, #1 + ext v6.16b, v2.16b, v3.16b, #2 + ext v7.16b, v2.16b, v3.16b, #3 + EPEL_UNI_W_H_CALC v2, v5, v19, v20 + EPEL_UNI_W_H_CALC v6, v7, v21, v22 + sqxtn v19.4h, v19.4s + sqxtn v20.4h, v20.4s + sqxtn v21.4h, v21.4s + sqxtn v22.4h, v22.4s + zip1 v4.8h, v19.8h, v21.8h + zip1 v5.8h, v20.8h, v22.8h + sqxtun v4.8b, v4.8h + sqxtun v5.8b, v5.8h + st2 {v4.8b, v5.8b}, [x0], x1 + b.hi 1b + ret +endfunc + + +function ff_hevc_put_hevc_epel_uni_w_h64_8_neon_i8mm, export=1 + EPEL_UNI_W_H_HEADER + sub x1, x1, #32 + sub x3, x3, #64 +1: + ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x2], #64 + subs w4, w4, #1 + ext v4.16b, v0.16b, v1.16b, #1 + ext v5.16b, v0.16b, v1.16b, #2 + ext v6.16b, v0.16b, v1.16b, #3 + ext v16.16b, v1.16b, v2.16b, #1 + ext v17.16b, v1.16b, v2.16b, #2 + ext v18.16b, v1.16b, v2.16b, #3 + EPEL_UNI_W_H_CALC v0, v4, v19, v20 + EPEL_UNI_W_H_CALC v5, v6, v21, v22 + EPEL_UNI_W_H_CALC v1, v16, v23, v24 + EPEL_UNI_W_H_CALC v17, v18, v25, v26 + sqxtn v19.4h, v19.4s + sqxtn2 v19.8h, v23.4s + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v24.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v25.4s + sqxtn v22.4h, v22.4s + sqxtn2 v22.8h, v26.4s + sqxtun v19.8b, v19.8h + sqxtun v20.8b, v20.8h + sqxtun v21.8b, v21.8h + sqxtun v22.8b, v22.8h + st4 {v19.8b, v20.8b, v21.8b, v22.8b}, [x0], #32 + ld1 {v7.8b}, [x2], x3 + ext v4.16b, v2.16b, v3.16b, #1 + ext v5.16b, v2.16b, v3.16b, #2 + ext v6.16b, v2.16b, v3.16b, #3 + ext v16.16b, v3.16b, v7.16b, #1 + ext v17.16b, v3.16b, v7.16b, #2 + ext v18.16b, v3.16b, v7.16b, #3 + EPEL_UNI_W_H_CALC v2, v4, v19, v20 + EPEL_UNI_W_H_CALC v5, v6, v21, v22 + EPEL_UNI_W_H_CALC v3, v16, v23, v24 + EPEL_UNI_W_H_CALC v17, v18, v25, v26 + sqxtn v19.4h, v19.4s + sqxtn2 v19.8h, v23.4s + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v24.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v25.4s + sqxtn v22.4h, v22.4s + sqxtn2 v22.8h, v26.4s + sqxtun v19.8b, v19.8h + sqxtun v20.8b, v20.8h + sqxtun v21.8b, v21.8h + sqxtun v22.8b, v22.8h + st4 {v19.8b, v20.8b, v21.8b, v22.8b}, [x0], x1 + b.hi 1b + ret +endfunc +DISABLE_I8MM +#endif + +.macro epel_uni_w_hv_start + mov x15, x5 //denom + mov x16, x6 //wx + mov x17, x7 //ox + add w15, w15, #6 //shift = denom+6 + + + ldp x5, x6, [sp] + ldr x7, [sp, #16] + + stp d14, d15, [sp, #-64]! + stp d8, d9, [sp, #16] + stp d10, d11, [sp, #32] + stp d12, d13, [sp, #48] + + dup v13.8h, w16 //wx + dup v14.4s, w17 //ox + + mov w17, #1 + lsl w17, w17, w15 + lsr w17, w17, #1 + dup v15.4s, w17 + + neg w15, w15 // -shift + dup v12.4s, w15 //shift +.endm + +.macro epel_uni_w_hv_end + smull v28.4s, v4.4h, v13.4h + smull2 v29.4s, v4.8h, v13.8h + add v28.4s, v28.4s, v15.4s + add v29.4s, v29.4s, v15.4s + sshl v28.4s, v28.4s, v12.4s + sshl v29.4s, v29.4s, v12.4s + add v28.4s, v28.4s, v14.4s + add v29.4s, v29.4s, v14.4s + sqxtn v4.4h, v28.4s + sqxtn2 v4.8h, v29.4s +.endm + +.macro epel_uni_w_hv_end2 + smull v28.4s, v4.4h, v13.4h + smull2 v29.4s, v4.8h, v13.8h + smull v30.4s, v5.4h, v13.4h + smull2 v31.4s, v5.8h, v13.8h + add v28.4s, v28.4s, v15.4s + add v29.4s, v29.4s, v15.4s + add v30.4s, v30.4s, v15.4s + add v31.4s, v31.4s, v15.4s + + sshl v28.4s, v28.4s, v12.4s + sshl v29.4s, v29.4s, v12.4s + sshl v30.4s, v30.4s, v12.4s + sshl v31.4s, v31.4s, v12.4s + + add v28.4s, v28.4s, v14.4s + add v29.4s, v29.4s, v14.4s + add v30.4s, v30.4s, v14.4s + add v31.4s, v31.4s, v14.4s + + sqxtn v4.4h, v28.4s + sqxtn2 v4.8h, v29.4s + sqxtn v5.4h, v30.4s + sqxtn2 v5.8h, v31.4s +.endm + +.macro epel_uni_w_hv_end3 + smull v1.4s, v4.4h, v13.4h + smull2 v2.4s, v4.8h, v13.8h + smull v28.4s, v5.4h, v13.4h + smull2 v29.4s, v5.8h, v13.8h + smull v30.4s, v6.4h, v13.4h + smull2 v31.4s, v6.8h, v13.8h + add v1.4s, v1.4s, v15.4s + add v2.4s, v2.4s, v15.4s + add v28.4s, v28.4s, v15.4s + add v29.4s, v29.4s, v15.4s + add v30.4s, v30.4s, v15.4s + add v31.4s, v31.4s, v15.4s + + sshl v1.4s, v1.4s, v12.4s + sshl v2.4s, v2.4s, v12.4s + sshl v28.4s, v28.4s, v12.4s + sshl v29.4s, v29.4s, v12.4s + sshl v30.4s, v30.4s, v12.4s + sshl v31.4s, v31.4s, v12.4s + add v1.4s, v1.4s, v14.4s + add v2.4s, v2.4s, v14.4s + add v28.4s, v28.4s, v14.4s + add v29.4s, v29.4s, v14.4s + add v30.4s, v30.4s, v14.4s + add v31.4s, v31.4s, v14.4s + + sqxtn v4.4h, v1.4s + sqxtn2 v4.8h, v2.4s + sqxtn v5.4h, v28.4s + sqxtn2 v5.8h, v29.4s + sqxtn v6.4h, v30.4s + sqxtn2 v6.8h, v31.4s +.endm + + + +function hevc_put_hevc_epel_uni_w_hv4_8_end_neon + load_epel_filterh x6, x5 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.4h}, [sp], x10 + ld1 {v17.4h}, [sp], x10 + ld1 {v18.4h}, [sp], x10 +1: ld1 {v19.4h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v16, v17, v18, v19 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + str s4, [x0] + add x0, x0, x1 + b.eq 2f + + ld1 {v16.4h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v17, v18, v19, v16 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + str s4, [x0] + add x0, x0, x1 + b.eq 2f + + ld1 {v17.4h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v18, v19, v16, v17 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + str s4, [x0] + add x0, x0, x1 + b.eq 2f + + ld1 {v18.4h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v19, v16, v17, v18 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + str s4, [x0] + add x0, x0, x1 + b.ne 1b +2: + ldp d8, d9, [sp, #16] + ldp d10, d11, [sp, #32] + ldp d12, d13, [sp, #48] + ldp d14, d15, [sp], #64 + ret +endfunc + +function hevc_put_hevc_epel_uni_w_hv6_8_end_neon + load_epel_filterh x6, x5 + sub x1, x1, #4 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h}, [sp], x10 + ld1 {v17.8h}, [sp], x10 + ld1 {v18.8h}, [sp], x10 +1: ld1 {v19.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v16, v17, v18, v19 + calc_epelh2 v4, v5, v16, v17, v18, v19 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.s}[0], [x0], #4 + st1 {v4.h}[2], [x0], x1 + b.eq 2f + + ld1 {v16.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v17, v18, v19, v16 + calc_epelh2 v4, v5, v17, v18, v19, v16 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.s}[0], [x0], #4 + st1 {v4.h}[2], [x0], x1 + b.eq 2f + + ld1 {v17.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v18, v19, v16, v17 + calc_epelh2 v4, v5, v18, v19, v16, v17 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.s}[0], [x0], #4 + st1 {v4.h}[2], [x0], x1 + b.eq 2f + + ld1 {v18.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v19, v16, v17, v18 + calc_epelh2 v4, v5, v19, v16, v17, v18 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.s}[0], [x0], #4 + st1 {v4.h}[2], [x0], x1 + b.ne 1b +2: + ldp d8, d9, [sp, #16] + ldp d10, d11, [sp, #32] + ldp d12, d13, [sp, #48] + ldp d14, d15, [sp], #64 + ret +endfunc + +function hevc_put_hevc_epel_uni_w_hv8_8_end_neon + load_epel_filterh x6, x5 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h}, [sp], x10 + ld1 {v17.8h}, [sp], x10 + ld1 {v18.8h}, [sp], x10 +1: ld1 {v19.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v16, v17, v18, v19 + calc_epelh2 v4, v5, v16, v17, v18, v19 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.8b}, [x0], x1 + b.eq 2f + + ld1 {v16.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v17, v18, v19, v16 + calc_epelh2 v4, v5, v17, v18, v19, v16 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.8b}, [x0], x1 + b.eq 2f + + ld1 {v17.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v18, v19, v16, v17 + calc_epelh2 v4, v5, v18, v19, v16, v17 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.8b}, [x0], x1 + b.eq 2f + + ld1 {v18.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v19, v16, v17, v18 + calc_epelh2 v4, v5, v19, v16, v17, v18 + epel_uni_w_hv_end + sqxtun v4.8b, v4.8h + st1 {v4.8b}, [x0], x1 + b.ne 1b +2: + ldp d8, d9, [sp, #16] + ldp d10, d11, [sp, #32] + ldp d12, d13, [sp, #48] + ldp d14, d15, [sp], #64 + ret +endfunc + +function hevc_put_hevc_epel_uni_w_hv12_8_end_neon + load_epel_filterh x6, x5 + sub x1, x1, #8 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h}, [sp], x10 + ld1 {v18.8h, v19.8h}, [sp], x10 + ld1 {v20.8h, v21.8h}, [sp], x10 +1: ld1 {v22.8h, v23.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v16, v18, v20, v22 + calc_epelh2 v4, v5, v16, v18, v20, v22 + calc_epelh v5, v17, v19, v21, v23 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.8b}, [x0], #8 + st1 {v4.s}[2], [x0], x1 + b.eq 2f + + ld1 {v16.8h, v17.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v18, v20, v22, v16 + calc_epelh2 v4, v5, v18, v20, v22, v16 + calc_epelh v5, v19, v21, v23, v17 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.8b}, [x0], #8 + st1 {v4.s}[2], [x0], x1 + b.eq 2f + + ld1 {v18.8h, v19.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v20, v22, v16, v18 + calc_epelh2 v4, v5, v20, v22, v16, v18 + calc_epelh v5, v21, v23, v17, v19 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.8b}, [x0], #8 + st1 {v4.s}[2], [x0], x1 + b.eq 2f + + ld1 {v20.8h, v21.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v22, v16, v18, v20 + calc_epelh2 v4, v5, v22, v16, v18, v20 + calc_epelh v5, v23, v17, v19, v21 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.8b}, [x0], #8 + st1 {v4.s}[2], [x0], x1 + b.ne 1b +2: + ldp d8, d9, [sp, #16] + ldp d10, d11, [sp, #32] + ldp d12, d13, [sp, #48] + ldp d14, d15, [sp], #64 + ret +endfunc + +function hevc_put_hevc_epel_uni_w_hv16_8_end_neon + load_epel_filterh x6, x5 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h}, [sp], x10 + ld1 {v18.8h, v19.8h}, [sp], x10 + ld1 {v20.8h, v21.8h}, [sp], x10 +1: ld1 {v22.8h, v23.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v16, v18, v20, v22 + calc_epelh2 v4, v5, v16, v18, v20, v22 + calc_epelh v5, v17, v19, v21, v23 + calc_epelh2 v5, v6, v17, v19, v21, v23 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.16b}, [x0], x1 + b.eq 2f + + ld1 {v16.8h, v17.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v18, v20, v22, v16 + calc_epelh2 v4, v5, v18, v20, v22, v16 + calc_epelh v5, v19, v21, v23, v17 + calc_epelh2 v5, v6, v19, v21, v23, v17 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.16b}, [x0], x1 + b.eq 2f + + ld1 {v18.8h, v19.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v20, v22, v16, v18 + calc_epelh2 v4, v5, v20, v22, v16, v18 + calc_epelh v5, v21, v23, v17, v19 + calc_epelh2 v5, v6, v21, v23, v17, v19 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.16b}, [x0], x1 + b.eq 2f + + ld1 {v20.8h, v21.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v22, v16, v18, v20 + calc_epelh2 v4, v5, v22, v16, v18, v20 + calc_epelh v5, v23, v17, v19, v21 + calc_epelh2 v5, v6, v23, v17, v19, v21 + epel_uni_w_hv_end2 + sqxtun v4.8b, v4.8h + sqxtun2 v4.16b, v5.8h + st1 {v4.16b}, [x0], x1 + b.ne 1b +2: + ldp d8, d9, [sp, #16] + ldp d10, d11, [sp, #32] + ldp d12, d13, [sp, #48] + ldp d14, d15, [sp], #64 + ret +endfunc + +function hevc_put_hevc_epel_uni_w_hv24_8_end_neon + load_epel_filterh x6, x5 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h, v18.8h}, [sp], x10 + ld1 {v19.8h, v20.8h, v21.8h}, [sp], x10 + ld1 {v22.8h, v23.8h, v24.8h}, [sp], x10 +1: ld1 {v25.8h, v26.8h, v27.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v16, v19, v22, v25 + calc_epelh2 v4, v5, v16, v19, v22, v25 + calc_epelh v5, v17, v20, v23, v26 + calc_epelh2 v5, v6, v17, v20, v23, v26 + calc_epelh v6, v18, v21, v24, v27 + calc_epelh2 v6, v7, v18, v21, v24, v27 + + epel_uni_w_hv_end3 + sqxtun v4.8b, v4.8h + sqxtun v5.8b, v5.8h + sqxtun v6.8b, v6.8h + st1 {v4.8b, v5.8b, v6.8b}, [x0], x1 + b.eq 2f + + ld1 {v16.8h, v17.8h, v18.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v19, v22, v25, v16 + calc_epelh2 v4, v5, v19, v22, v25, v16 + calc_epelh v5, v20, v23, v26, v17 + calc_epelh2 v5, v6, v20, v23, v26, v17 + calc_epelh v6, v21, v24, v27, v18 + calc_epelh2 v6, v7, v21, v24, v27, v18 + epel_uni_w_hv_end3 + + sqxtun v4.8b, v4.8h + sqxtun v5.8b, v5.8h + sqxtun v6.8b, v6.8h + st1 {v4.8b, v5.8b, v6.8b}, [x0], x1 + b.eq 2f + + ld1 {v19.8h, v20.8h, v21.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v22, v25, v16, v19 + calc_epelh2 v4, v5, v22, v25, v16, v19 + calc_epelh v5, v23, v26, v17, v20 + calc_epelh2 v5, v6, v23, v26, v17, v20 + calc_epelh v6, v24, v27, v18, v21 + calc_epelh2 v6, v7, v24, v27, v18, v21 + epel_uni_w_hv_end3 + + sqxtun v4.8b, v4.8h + sqxtun v5.8b, v5.8h + sqxtun v6.8b, v6.8h + st1 {v4.8b, v5.8b, v6.8b}, [x0], x1 + b.eq 2f + + ld1 {v22.8h, v23.8h, v24.8h}, [sp], x10 + subs x4, x4, #1 + calc_epelh v4, v25, v16, v19, v22 + calc_epelh2 v4, v5, v25, v16, v19, v22 + calc_epelh v5, v26, v17, v20, v23 + calc_epelh2 v5, v6, v26, v17, v20, v23 + calc_epelh v6, v27, v18, v21, v24 + calc_epelh2 v6, v7, v27, v18, v21, v24 + epel_uni_w_hv_end3 + + sqxtun v4.8b, v4.8h + sqxtun v5.8b, v5.8h + sqxtun v6.8b, v6.8h + st1 {v4.8b, v5.8b, v6.8b}, [x0], x1 + b.ne 1b +2: + ldp d8, d9, [sp, #16] + ldp d10, d11, [sp, #32] + ldp d12, d13, [sp, #48] + ldp d14, d15, [sp], #64 + ret +endfunc + +.macro epel_uni_w_hv suffix +function ff_hevc_put_hevc_epel_uni_w_hv4_8_\suffix, export=1 + epel_uni_w_hv_start + sxtw x4, w4 + + add x10, x4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add x3, x4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h4_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + b hevc_put_hevc_epel_uni_w_hv4_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv6_8_\suffix, export=1 + epel_uni_w_hv_start + sxtw x4, w4 + + add x10, x4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add x3, x4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h6_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + b hevc_put_hevc_epel_uni_w_hv6_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv8_8_\suffix, export=1 + epel_uni_w_hv_start + sxtw x4, w4 + + add x10, x4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add x3, x4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h8_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + b hevc_put_hevc_epel_uni_w_hv8_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv12_8_\suffix, export=1 + epel_uni_w_hv_start + sxtw x4, w4 + + add x10, x4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add x3, x4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h12_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + b hevc_put_hevc_epel_uni_w_hv12_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv16_8_\suffix, export=1 + epel_uni_w_hv_start + sxtw x4, w4 + + add x10, x4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add x3, x4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h16_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + b hevc_put_hevc_epel_uni_w_hv16_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv24_8_\suffix, export=1 + epel_uni_w_hv_start + sxtw x4, w4 + + add x10, x4, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + str x30, [sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add x3, x4, #3 + mov x4, x5 + bl X(ff_hevc_put_hevc_epel_h24_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x30, [sp], #48 + b hevc_put_hevc_epel_uni_w_hv24_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv32_8_\suffix, export=1 + ldp x15, x16, [sp] + mov x17, #16 + stp x15, x16, [sp, #-96]! + stp x0, x30, [sp, #16] + stp x1, x2, [sp, #32] + stp x3, x4, [sp, #48] + stp x5, x6, [sp, #64] + stp x17, x7, [sp, #80] + + bl X(ff_hevc_put_hevc_epel_uni_w_hv16_8_\suffix) + ldp x0, x30, [sp, #16] + ldp x1, x2, [sp, #32] + ldp x3, x4, [sp, #48] + ldp x5, x6, [sp, #64] + ldp x17, x7, [sp, #80] + ldp x15, x16, [sp], #96 + add x0, x0, #16 + add x2, x2, #16 + mov x17, #16 + stp x15, x16, [sp, #-32]! + stp x17, x30, [sp, #16] + bl X(ff_hevc_put_hevc_epel_uni_w_hv16_8_\suffix) + ldp x17, x30, [sp, #16] + ldp x15, x16, [sp], #32 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv48_8_\suffix, export=1 + ldp x15, x16, [sp] + mov x17, #24 + stp x15, x16, [sp, #-96]! + stp x0, x30, [sp, #16] + stp x1, x2, [sp, #32] + stp x3, x4, [sp, #48] + stp x5, x6, [sp, #64] + stp x17, x7, [sp, #80] + bl X(ff_hevc_put_hevc_epel_uni_w_hv24_8_\suffix) + ldp x0, x30, [sp, #16] + ldp x1, x2, [sp, #32] + ldp x3, x4, [sp, #48] + ldp x5, x6, [sp, #64] + ldp x17, x7, [sp, #80] + ldp x15, x16, [sp], #96 + add x0, x0, #24 + add x2, x2, #24 + mov x17, #24 + stp x15, x16, [sp, #-32]! + stp x17, x30, [sp, #16] + bl X(ff_hevc_put_hevc_epel_uni_w_hv24_8_\suffix) + ldp x17, x30, [sp, #16] + ldp x15, x16, [sp], #32 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv64_8_\suffix, export=1 + ldp x15, x16, [sp] + mov x17, #32 + stp x15, x16, [sp, #-96]! + stp x0, x30, [sp, #16] + stp x1, x2, [sp, #32] + stp x3, x4, [sp, #48] + stp x5, x6, [sp, #64] + stp x17, x7, [sp, #80] + + bl X(ff_hevc_put_hevc_epel_uni_w_hv32_8_\suffix) + ldp x0, x30, [sp, #16] + ldp x1, x2, [sp, #32] + ldp x3, x4, [sp, #48] + ldp x5, x6, [sp, #64] + ldp x17, x7, [sp, #80] + ldp x15, x16, [sp], #96 + add x0, x0, #32 + add x2, x2, #32 + mov x17, #32 + stp x15, x16, [sp, #-32]! + stp x17, x30, [sp, #16] + bl X(ff_hevc_put_hevc_epel_uni_w_hv32_8_\suffix) + ldp x17, x30, [sp, #16] + ldp x15, x16, [sp], #32 + ret +endfunc +.endm + +epel_uni_w_hv neon + + +function hevc_put_hevc_epel_bi_hv4_8_end_neon + load_epel_filterh x7, x6 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.4h}, [sp], x10 + ld1 {v17.4h}, [sp], x10 + ld1 {v18.4h}, [sp], x10 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().4h}, [sp], x10 + calc_epelh v4, \src0, \src1, \src2, \src3 + ld1 {v6.4h}, [x4], x10 + sqadd v4.4h, v4.4h, v6.4h + sqrshrun v4.8b, v4.8h, #7 + subs w5, w5, #1 + st1 {v4.s}[0], [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_bi_hv6_8_end_neon + load_epel_filterh x7, x6 + sub x1, x1, #4 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h}, [sp], x10 + ld1 {v17.8h}, [sp], x10 + ld1 {v18.8h}, [sp], x10 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().8h}, [sp], x10 + calc_epelh v4, \src0, \src1, \src2, \src3 + calc_epelh2 v4, v5, \src0, \src1, \src2, \src3 + ld1 {v6.8h}, [x4], x10 + sqadd v4.8h, v4.8h, v6.8h + sqrshrun v4.8b, v4.8h, #7 + st1 {v4.s}[0], [x0], #4 + subs w5, w5, #1 + st1 {v4.h}[2], [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_bi_hv8_8_end_neon + load_epel_filterh x7, x6 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h}, [sp], x10 + ld1 {v17.8h}, [sp], x10 + ld1 {v18.8h}, [sp], x10 +.macro calc src0, src1, src2, src3 + ld1 {\src3\().8h}, [sp], x10 + calc_epelh v4, \src0, \src1, \src2, \src3 + calc_epelh2 v4, v5, \src0, \src1, \src2, \src3 + ld1 {v6.8h}, [x4], x10 + sqadd v4.8h, v4.8h, v6.8h + sqrshrun v4.8b, v4.8h, #7 + subs w5, w5, #1 + st1 {v4.8b}, [x0], x1 +.endm +1: calc_all4 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_bi_hv12_8_end_neon + load_epel_filterh x7, x6 + sub x1, x1, #8 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h}, [sp], x10 + ld1 {v18.8h, v19.8h}, [sp], x10 + ld1 {v20.8h, v21.8h}, [sp], x10 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\src6\().8h, \src7\().8h}, [sp], x10 + calc_epelh v4, \src0, \src2, \src4, \src6 + calc_epelh2 v4, v5, \src0, \src2, \src4, \src6 + calc_epelh v5, \src1, \src3, \src5, \src7 + ld1 {v6.8h, v7.8h}, [x4], x10 + sqadd v4.8h, v4.8h, v6.8h + sqadd v5.8h, v5.8h, v7.8h + sqrshrun v4.8b, v4.8h, #7 + sqrshrun2 v4.16b, v5.8h, #7 + st1 {v4.8b}, [x0], #8 + subs w5, w5, #1 + st1 {v4.s}[2], [x0], x1 +.endm +1: calc_all8 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_bi_hv16_8_end_neon + load_epel_filterh x7, x6 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h}, [sp], x10 + ld1 {v18.8h, v19.8h}, [sp], x10 + ld1 {v20.8h, v21.8h}, [sp], x10 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\src6\().8h, \src7\().8h}, [sp], x10 + calc_epelh v4, \src0, \src2, \src4, \src6 + calc_epelh2 v4, v5, \src0, \src2, \src4, \src6 + calc_epelh v5, \src1, \src3, \src5, \src7 + calc_epelh2 v5, v6, \src1, \src3, \src5, \src7 + ld1 {v6.8h, v7.8h}, [x4], x10 + sqadd v4.8h, v4.8h, v6.8h + sqadd v5.8h, v5.8h, v7.8h + sqrshrun v4.8b, v4.8h, #7 + sqrshrun2 v4.16b, v5.8h, #7 + st1 {v4.16b}, [x0], x1 + subs w5, w5, #1 +.endm +1: calc_all8 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_bi_hv24_8_end_neon + load_epel_filterh x7, x6 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h, v18.8h}, [sp], x10 + ld1 {v19.8h, v20.8h, v21.8h}, [sp], x10 + ld1 {v22.8h, v23.8h, v24.8h}, [sp], x10 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11 + ld1 {\src9\().8h, \src10\().8h, \src11\().8h}, [sp], x10 + calc_epelh v1, \src0, \src3, \src6, \src9 + calc_epelh2 v1, v2, \src0, \src3, \src6, \src9 + calc_epelh v2, \src1, \src4, \src7, \src10 + calc_epelh2 v2, v3, \src1, \src4, \src7, \src10 + calc_epelh v3, \src2, \src5, \src8, \src11 + calc_epelh2 v3, v4, \src2, \src5, \src8, \src11 + ld1 {v4.8h, v5.8h, v6.8h}, [x4], x10 + sqadd v1.8h, v1.8h, v4.8h + sqadd v2.8h, v2.8h, v5.8h + sqadd v3.8h, v3.8h, v6.8h + sqrshrun v1.8b, v1.8h, #7 + sqrshrun v2.8b, v2.8h, #7 + sqrshrun v3.8b, v3.8h, #7 + subs w5, w5, #1 + st1 {v1.8b, v2.8b, v3.8b}, [x0], x1 +.endm +1: calc_all12 +.purgem calc +2: ret +endfunc + +function hevc_put_hevc_epel_bi_hv32_8_end_neon + load_epel_filterh x7, x6 + mov x10, #(MAX_PB_SIZE * 2) + ld1 {v16.8h, v17.8h, v18.8h, v19.8h}, [sp], x10 + ld1 {v20.8h, v21.8h, v22.8h, v23.8h}, [sp], x10 + ld1 {v24.8h, v25.8h, v26.8h, v27.8h}, [sp], x10 +.macro calc src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15 + ld1 {\src12\().8h, \src13\().8h, \src14\().8h, \src15\().8h}, [sp], x10 + calc_epelh v1, \src0, \src4, \src8, \src12 + calc_epelh2 v1, v2, \src0, \src4, \src8, \src12 + calc_epelh v2, \src1, \src5, \src9, \src13 + calc_epelh2 v2, v3, \src1, \src5, \src9, \src13 + calc_epelh v3, \src2, \src6, \src10, \src14 + calc_epelh2 v3, v4, \src2, \src6, \src10, \src14 + calc_epelh v4, \src3, \src7, \src11, \src15 + calc_epelh2 v4, v5, \src3, \src7, \src11, \src15 + ld1 {v5.8h, v6.8h, v7.8h, v8.8h}, [x4], x10 + sqadd v1.8h, v1.8h, v5.8h + sqadd v2.8h, v2.8h, v6.8h + sqadd v3.8h, v3.8h, v7.8h + sqadd v4.8h, v4.8h, v8.8h + sqrshrun v1.8b, v1.8h, #7 + sqrshrun v2.8b, v2.8h, #7 + sqrshrun v3.8b, v3.8h, #7 + sqrshrun v4.8b, v4.8h, #7 + st1 {v1.8b, v2.8b, v3.8b, v4.8b}, [x0], x1 + subs w5, w5, #1 +.endm +1: calc_all16 +.purgem calc +2: ldr d8, [sp], #16 + ret +endfunc + +.macro epel_bi_hv suffix +function ff_hevc_put_hevc_epel_bi_hv4_8_\suffix, export=1 + add w10, w5, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-48]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add w3, w5, #3 + mov x4, x6 + mov x5, x7 + bl X(ff_hevc_put_hevc_epel_h4_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x7, x30, [sp], #48 + b hevc_put_hevc_epel_bi_hv4_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_bi_hv6_8_\suffix, export=1 + add w10, w5, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-48]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add w3, w5, #3 + mov x4, x6 + mov x5, x7 + bl X(ff_hevc_put_hevc_epel_h6_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x7, x30, [sp], #48 + b hevc_put_hevc_epel_bi_hv6_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_bi_hv8_8_\suffix, export=1 + add w10, w5, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-48]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add w3, w5, #3 + mov x4, x6 + mov x5, x7 + bl X(ff_hevc_put_hevc_epel_h8_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x7, x30, [sp], #48 + b hevc_put_hevc_epel_bi_hv8_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_bi_hv12_8_\suffix, export=1 + add w10, w5, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-48]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add w3, w5, #3 + mov x4, x6 + mov x5, x7 + bl X(ff_hevc_put_hevc_epel_h12_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x7, x30, [sp], #48 + b hevc_put_hevc_epel_bi_hv12_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_bi_hv16_8_\suffix, export=1 + add w10, w5, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-48]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add w3, w5, #3 + mov x4, x6 + mov x5, x7 + bl X(ff_hevc_put_hevc_epel_h16_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x7, x30, [sp], #48 + b hevc_put_hevc_epel_bi_hv16_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_bi_hv24_8_\suffix, export=1 + add w10, w5, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-48]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add w3, w5, #3 + mov x4, x6 + mov x5, x7 + bl X(ff_hevc_put_hevc_epel_h24_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x7, x30, [sp], #48 + b hevc_put_hevc_epel_bi_hv24_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_bi_hv32_8_\suffix, export=1 + str d8, [sp, #-16]! + add w10, w5, #3 + lsl x10, x10, #7 + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-48]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + add x0, sp, #48 + sub x1, x2, x3 + mov x2, x3 + add w3, w5, #3 + mov x4, x6 + mov x5, x7 + mov w6, #32 + bl X(ff_hevc_put_hevc_epel_h32_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x7, x30, [sp], #48 + b hevc_put_hevc_epel_bi_hv32_8_end_neon +endfunc + +function ff_hevc_put_hevc_epel_bi_hv48_8_\suffix, export=1 + stp x6, x7, [sp, #-80]! + stp x4, x5, [sp, #16] + stp x2, x3, [sp, #32] + stp x0, x1, [sp, #48] + str x30, [sp, #64] + bl X(ff_hevc_put_hevc_epel_bi_hv24_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x2, x3, [sp, #32] + ldp x0, x1, [sp, #48] + ldp x6, x7, [sp], #64 + add x0, x0, #24 + add x2, x2, #24 + add x4, x4, #48 + bl X(ff_hevc_put_hevc_epel_bi_hv24_8_\suffix) + ldr x30, [sp], #16 + ret +endfunc + +function ff_hevc_put_hevc_epel_bi_hv64_8_\suffix, export=1 + stp x6, x7, [sp, #-80]! + stp x4, x5, [sp, #16] + stp x2, x3, [sp, #32] + stp x0, x1, [sp, #48] + str x30, [sp, #64] + bl X(ff_hevc_put_hevc_epel_bi_hv32_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x2, x3, [sp, #32] + ldp x0, x1, [sp, #48] + ldp x6, x7, [sp], #64 + add x0, x0, #32 + add x2, x2, #32 + add x4, x4, #64 + bl X(ff_hevc_put_hevc_epel_bi_hv32_8_\suffix) + ldr x30, [sp], #16 + ret +endfunc +.endm + +epel_bi_hv neon + +#if HAVE_I8MM +ENABLE_I8MM + +epel_uni_w_hv neon_i8mm +epel_bi_hv neon_i8mm + +DISABLE_I8MM +#endif + + +.macro EPEL_UNI_W_V_HEADER + ldr x12, [sp, #8] + movrel x9, epel_filters + add x9, x9, x12, lsl #2 + ld4r {v0.16b, v1.16b, v2.16b, v3.16b}, [x9] // filter + neg v0.16b, v0.16b + neg v3.16b, v3.16b + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 + sub x2, x2, x3 +.endm + +.macro EPEL_UNI_W_V4_CALC d0, s0, s1, s2, s3 + movi \d0\().16b, #0 + umlsl \d0\().8h, \s0\().8b, v0.8b + umlal \d0\().8h, \s1\().8b, v1.8b + umlal \d0\().8h, \s2\().8b, v2.8b + umlsl \d0\().8h, \s3\().8b, v3.8b + smull \d0\().4s, \d0\().4h, v30.4h + sqrshl \d0\().4s, \d0\().4s, v31.4s + sqadd \d0\().4s, \d0\().4s, v29.4s + sqxtn \d0\().4h, \d0\().4s + sqxtun \d0\().8b, \d0\().8h +.endm + +function ff_hevc_put_hevc_epel_uni_w_v4_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + ldr s4, [x2] + ldr s5, [x2, x3] + add x2, x2, x3, lsl #1 + ldr s6, [x2] +1: + ldr s7, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V4_CALC v16, v4, v5, v6, v7 + str s16, [x0] + b.eq 2f + add x0, x0, x1 + ldr s4, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V4_CALC v17, v5, v6, v7, v4 + str s17, [x0] + add x0, x0, x1 + b.eq 2f + ldr s5, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V4_CALC v18, v6, v7, v4, v5 + str s18, [x0] + add x0, x0, x1 + b.eq 2f + ldr s6, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V4_CALC v19, v7, v4, v5, v6 + str s19, [x0] + add x0, x0, x1 + b.hi 1b +2: + ret +endfunc + +.macro EPEL_UNI_W_V8_CALC d0, s0, s1, s2, s3, t0, t1 + movi \d0\().16b, #0 + umlsl \d0\().8h, \s0\().8b, v0.8b + umlal \d0\().8h, \s1\().8b, v1.8b + umlal \d0\().8h, \s2\().8b, v2.8b + umlsl \d0\().8h, \s3\().8b, v3.8b + smull \t0\().4s, \d0\().4h, v30.4h + smull2 \t1\().4s, \d0\().8h, v30.8h + sqrshl \t0\().4s, \t0\().4s, v31.4s + sqrshl \t1\().4s, \t1\().4s, v31.4s + sqadd \t0\().4s, \t0\().4s, v29.4s + sqadd \t1\().4s, \t1\().4s, v29.4s + sqxtn \d0\().4h, \t0\().4s + sqxtn2 \d0\().8h, \t1\().4s + sqxtun \d0\().8b, \d0\().8h +.endm + +function ff_hevc_put_hevc_epel_uni_w_v6_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + sub x1, x1, #4 + ldr d4, [x2] + ldr d5, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d6, [x2] +1: + ldr d7, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V8_CALC v16, v4, v5, v6, v7, v20, v21 + str s16, [x0], #4 + st1 {v16.h}[2], [x0], x1 + b.eq 2f + ldr d4, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V8_CALC v17, v5, v6, v7, v4, v20, v21 + str s17, [x0], #4 + st1 {v17.h}[2], [x0], x1 + b.eq 2f + ldr d5, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V8_CALC v18, v6, v7, v4, v5, v20, v21 + str s18, [x0], #4 + st1 {v18.h}[2], [x0], x1 + b.eq 2f + ldr d6, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V8_CALC v19, v7, v4, v5, v6, v20, v21 + str s19, [x0], #4 + st1 {v19.h}[2], [x0], x1 + b.hi 1b +2: + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v8_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + ldr d4, [x2] + ldr d5, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d6, [x2] +1: + ldr d7, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V8_CALC v16, v4, v5, v6, v7, v20, v21 + str d16, [x0] + add x0, x0, x1 + b.eq 2f + ldr d4, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V8_CALC v17, v5, v6, v7, v4, v20, v21 + str d17, [x0] + add x0, x0, x1 + b.eq 2f + ldr d5, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V8_CALC v18, v6, v7, v4, v5, v20, v21 + str d18, [x0] + add x0, x0, x1 + b.eq 2f + ldr d6, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V8_CALC v19, v7, v4, v5, v6, v20, v21 + str d19, [x0] + add x0, x0, x1 + b.hi 1b +2: + ret +endfunc + +.macro EPEL_UNI_W_V12_CALC d0, d1, s0, s1, s2, s3, t0, t1, t2, t3 + movi \d0\().16b, #0 + movi \d1\().16b, #0 + umlsl \d0\().8h, \s0\().8b, v0.8b + umlsl2 \d1\().8h, \s0\().16b, v0.16b + umlal \d0\().8h, \s1\().8b, v1.8b + umlal2 \d1\().8h, \s1\().16b, v1.16b + umlal \d0\().8h, \s2\().8b, v2.8b + umlal2 \d1\().8h, \s2\().16b, v2.16b + umlsl \d0\().8h, \s3\().8b, v3.8b + umlsl2 \d1\().8h, \s3\().16b, v3.16b + + smull \t0\().4s, \d0\().4h, v30.4h + smull2 \t1\().4s, \d0\().8h, v30.8h + smull \t2\().4s, \d1\().4h, v30.4h + + sqrshl \t0\().4s, \t0\().4s, v31.4s + sqrshl \t1\().4s, \t1\().4s, v31.4s + sqrshl \t2\().4s, \t2\().4s, v31.4s + sqadd \t0\().4s, \t0\().4s, v29.4s + sqadd \t1\().4s, \t1\().4s, v29.4s + sqadd \t2\().4s, \t2\().4s, v29.4s + + sqxtn \d0\().4h, \t0\().4s + sqxtn2 \d0\().8h, \t1\().4s + sqxtn \d1\().4h, \t2\().4s + sqxtun \d0\().8b, \d0\().8h + sqxtun2 \d0\().16b, \d1\().8h +.endm + +function ff_hevc_put_hevc_epel_uni_w_v12_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + ldr q4, [x2] + ldr q5, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q6, [x2] + sub x1, x1, #8 +1: + ldr q7, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V12_CALC v16, v17, v4, v5, v6, v7, v24, v25, v26, v27 + str d16, [x0], #8 + st1 {v16.s}[2], [x0] + add x0, x0, x1 + b.eq 2f + ldr q4, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V12_CALC v18, v19, v5, v6, v7, v4, v24, v25, v26, v27 + str d18, [x0], #8 + st1 {v18.s}[2], [x0] + add x0, x0, x1 + b.eq 2f + ldr q5, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V12_CALC v20, v21, v6, v7, v4, v5, v24, v25, v26, v27 + str d20, [x0], #8 + st1 {v20.s}[2], [x0] + add x0, x0, x1 + b.eq 2f + ldr q6, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V12_CALC v22, v23, v7, v4, v5, v6, v24, v25, v26, v27 + str d22, [x0], #8 + st1 {v22.s}[2], [x0] + add x0, x0, x1 + b.hi 1b +2: + ret +endfunc + +.macro EPEL_UNI_W_V16_CALC d0, d1, s0, s1, s2, s3, t0, t1, t2, t3 + movi \d0\().16b, #0 + movi \d1\().16b, #0 + umlsl \d0\().8h, \s0\().8b, v0.8b + umlsl2 \d1\().8h, \s0\().16b, v0.16b + umlal \d0\().8h, \s1\().8b, v1.8b + umlal2 \d1\().8h, \s1\().16b, v1.16b + umlal \d0\().8h, \s2\().8b, v2.8b + umlal2 \d1\().8h, \s2\().16b, v2.16b + umlsl \d0\().8h, \s3\().8b, v3.8b + umlsl2 \d1\().8h, \s3\().16b, v3.16b + + smull \t0\().4s, \d0\().4h, v30.4h + smull2 \t1\().4s, \d0\().8h, v30.8h + smull \t2\().4s, \d1\().4h, v30.4h + smull2 \t3\().4s, \d1\().8h, v30.8h + + sqrshl \t0\().4s, \t0\().4s, v31.4s + sqrshl \t1\().4s, \t1\().4s, v31.4s + sqrshl \t2\().4s, \t2\().4s, v31.4s + sqrshl \t3\().4s, \t3\().4s, v31.4s + sqadd \t0\().4s, \t0\().4s, v29.4s + sqadd \t1\().4s, \t1\().4s, v29.4s + sqadd \t2\().4s, \t2\().4s, v29.4s + sqadd \t3\().4s, \t3\().4s, v29.4s + + sqxtn \d0\().4h, \t0\().4s + sqxtn2 \d0\().8h, \t1\().4s + sqxtn \d1\().4h, \t2\().4s + sqxtn2 \d1\().8h, \t3\().4s + sqxtun \d0\().8b, \d0\().8h + sqxtun2 \d0\().16b, \d1\().8h +.endm + + +function ff_hevc_put_hevc_epel_uni_w_v16_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + ldr q4, [x2] + ldr q5, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q6, [x2] +1: + ldr q7, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V16_CALC v16, v17, v4, v5, v6, v7, v24, v25, v26, v27 + str q16, [x0] + add x0, x0, x1 + b.eq 2f + ldr q4, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v18, v19, v5, v6, v7, v4, v24, v25, v26, v27 + str q18, [x0] + add x0, x0, x1 + b.eq 2f + ldr q5, [x2, x3] + subs w4, w4, #1 + add x2, x2, x3, lsl #1 + EPEL_UNI_W_V16_CALC v20, v21, v6, v7, v4, v5, v24, v25, v26, v27 + str q20, [x0] + add x0, x0, x1 + b.eq 2f + ldr q6, [x2] + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v22, v23, v7, v4, v5, v6, v24, v25, v26, v27 + str q22, [x0] + add x0, x0, x1 + b.hi 1b +2: + ret +endfunc + + + +function ff_hevc_put_hevc_epel_uni_w_v24_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + ldp q16, q17, [x2] + add x2, x2, x3 + ldp q18, q19, [x2] + add x2, x2, x3 + ldp q20, q21, [x2] + add x2, x2, x3 +1: + ldp q22, q23, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v16, v18, v20, v22, v24, v25, v26, v27 + EPEL_UNI_W_V8_CALC v6, v17, v19, v21, v23, v24, v25 + str q4, [x0] + str d6, [x0, #16] + add x0, x0, x1 + b.eq 2f + ldp q16, q17, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v18, v20, v22, v16, v24, v25, v26, v27 + EPEL_UNI_W_V8_CALC v6, v19, v21, v23, v17, v24, v25 + str q4, [x0] + str d6, [x0, #16] + add x0, x0, x1 + b.eq 2f + ldp q18, q19, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v20, v22, v16, v18, v24, v25, v26, v27 + EPEL_UNI_W_V8_CALC v6, v21, v23, v17, v19, v24, v25 + str q4, [x0] + str d6, [x0, #16] + add x0, x0, x1 + b.eq 2f + ldp q20, q21, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v22, v16, v18, v20, v24, v25, v26, v27 + EPEL_UNI_W_V8_CALC v6, v23, v17, v19, v21, v24, v25 + str q4, [x0] + str d6, [x0, #16] + add x0, x0, x1 + b.hi 1b +2: + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v32_8_neon, export=1 + EPEL_UNI_W_V_HEADER + + ldp q16, q17, [x2] + add x2, x2, x3 + ldp q18, q19, [x2] + add x2, x2, x3 + ldp q20, q21, [x2] + add x2, x2, x3 +1: + ldp q22, q23, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v16, v18, v20, v22, v24, v25, v26, v27 + EPEL_UNI_W_V16_CALC v6, v7, v17, v19, v21, v23, v24, v25, v26, v27 + str q4, [x0] + str q6, [x0, #16] + add x0, x0, x1 + b.eq 2f + ldp q16, q17, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v18, v20, v22, v16, v24, v25, v26, v27 + EPEL_UNI_W_V16_CALC v6, v7, v19, v21, v23, v17, v24, v25, v26, v27 + str q4, [x0] + str q6, [x0, #16] + add x0, x0, x1 + b.eq 2f + ldp q18, q19, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v20, v22, v16, v18, v24, v25, v26, v27 + EPEL_UNI_W_V16_CALC v6, v7, v21, v23, v17, v19, v24, v25, v26, v27 + str q4, [x0] + str q6, [x0, #16] + add x0, x0, x1 + b.eq 2f + ldp q20, q21, [x2] + subs w4, w4, #1 + add x2, x2, x3 + EPEL_UNI_W_V16_CALC v4, v5, v22, v16, v18, v20, v24, v25, v26, v27 + EPEL_UNI_W_V16_CALC v6, v7, v23, v17, v19, v21, v24, v25, v26, v27 + str q4, [x0] + str q6, [x0, #16] + add x0, x0, x1 + b.hi 1b +2: + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v48_8_neon, export=1 + EPEL_UNI_W_V_HEADER + stp d8, d9, [sp, #-32]! + stp d10, d11, [sp, #16] + + ld1 {v16.16b, v17.16b, v18.16b}, [x2], x3 + ld1 {v19.16b, v20.16b, v21.16b}, [x2], x3 + ld1 {v22.16b, v23.16b, v24.16b}, [x2], x3 +1: + ld1 {v25.16b, v26.16b, v27.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v16, v19, v22, v25, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v17, v20, v23, v26, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v18, v21, v24, v27, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b}, [x0], x1 + b.eq 2f + ld1 {v16.16b, v17.16b, v18.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v19, v22, v25, v16, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v20, v23, v26, v17, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v21, v24, v27, v18, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b}, [x0], x1 + b.eq 2f + ld1 {v19.16b, v20.16b, v21.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v22, v25, v16, v19, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v23, v26, v17, v20, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v24, v27, v18, v21, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b}, [x0], x1 + b.eq 2f + ld1 {v22.16b, v23.16b, v24.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v25, v16, v19, v22, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v26, v17, v20, v23, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v27, v18, v21, v24, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b}, [x0], x1 + b.hi 1b +2: + ldp d10, d11, [sp, #16] + ldp d8, d9, [sp], #32 + ret +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v64_8_neon, export=1 + EPEL_UNI_W_V_HEADER + stp d8, d9, [sp, #-64]! + stp d10, d11, [sp, #16] + stp d12, d13, [sp, #32] + stp d14, d15, [sp, #48] + + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x2], x3 + ld1 {v20.16b, v21.16b, v22.16b, v23.16b}, [x2], x3 + ld1 {v24.16b, v25.16b, v26.16b, v27.16b}, [x2], x3 +1: + ld1 {v12.16b, v13.16b, v14.16b, v15.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v16, v20, v24, v12, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v17, v21, v25, v13, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v18, v22, v26, v14, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v7,v28, v19, v23, v27, v15, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b, v7.16b}, [x0], x1 + b.eq 2f + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v20, v24, v12, v16, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v21, v25, v13, v17, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v22, v26, v14, v18, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v7,v28, v23, v27, v15, v19, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b, v7.16b}, [x0], x1 + b.eq 2f + ld1 {v20.16b, v21.16b, v22.16b, v23.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v24, v12, v16, v20, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v25, v13, v17, v21, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v26, v14, v18, v22, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v7,v28, v27, v15, v19, v23, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b, v7.16b}, [x0], x1 + b.eq 2f + ld1 {v24.16b, v25.16b, v26.16b, v27.16b}, [x2], x3 + subs w4, w4, #1 + EPEL_UNI_W_V16_CALC v4, v6, v12, v16, v20, v24, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v5, v7, v13, v17, v21, v25, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v6, v7, v14, v18, v22, v26, v8, v9, v10, v11 + EPEL_UNI_W_V16_CALC v7,v28, v15, v19, v23, v27, v8, v9, v10, v11 + st1 {v4.16b, v5.16b, v6.16b, v7.16b}, [x0], x1 + b.hi 1b +2: + ldp d10, d11, [sp, #16] + ldp d12, d13, [sp, #32] + ldp d14, d15, [sp, #48] + ldp d8, d9, [sp], #64 + ret +endfunc diff --git a/libavcodec/aarch64/hevcdsp_idct_neon.S b/libavcodec/aarch64/hevcdsp_idct_neon.S index 124c50998a7..3cac6e6db95 100644 --- a/libavcodec/aarch64/hevcdsp_idct_neon.S +++ b/libavcodec/aarch64/hevcdsp_idct_neon.S @@ -5,7 +5,8 @@ * * Ported from arm/hevcdsp_idct_neon.S by * Copyright (c) 2020 Reimar Döffinger - * Copyright (c) 2020 J. Dekker + * Copyright (c) 2023 J. Dekker + * Copyright (c) 2023 xu fulong <839789740@qq.com> * * This file is part of FFmpeg. * @@ -25,6 +26,7 @@ */ #include "libavutil/aarch64/asm.S" +#include "neon.S" const trans, align=4 .short 64, 83, 64, 36 @@ -37,13 +39,6 @@ const trans, align=4 .short 31, 22, 13, 4 endconst -.macro clip2 in1, in2, min, max - smax \in1, \in1, \min - smax \in2, \in2, \min - smin \in1, \in1, \max - smin \in2, \in2, \max -.endm - function ff_hevc_add_residual_4x4_8_neon, export=1 ld1 {v0.8h-v1.8h}, [x1] ld1 {v2.s}[0], [x0], x2 @@ -181,7 +176,7 @@ function hevc_add_residual_4x4_16_neon, export=0 ld1 {v3.d}[1], [x12], x2 movi v4.8h, #0 sqadd v1.8h, v1.8h, v3.8h - clip2 v0.8h, v1.8h, v4.8h, v21.8h + clip v4.8h, v21.8h, v0.8h, v1.8h st1 {v0.d}[0], [x0], x2 st1 {v0.d}[1], [x0], x2 st1 {v1.d}[0], [x0], x2 @@ -200,7 +195,7 @@ function hevc_add_residual_8x8_16_neon, export=0 sqadd v0.8h, v0.8h, v2.8h ld1 {v3.8h}, [x12] sqadd v1.8h, v1.8h, v3.8h - clip2 v0.8h, v1.8h, v4.8h, v21.8h + clip v4.8h, v21.8h, v0.8h, v1.8h st1 {v0.8h}, [x0], x2 st1 {v1.8h}, [x12], x2 bne 1b @@ -220,8 +215,7 @@ function hevc_add_residual_16x16_16_neon, export=0 sqadd v1.8h, v1.8h, v17.8h sqadd v2.8h, v2.8h, v18.8h sqadd v3.8h, v3.8h, v19.8h - clip2 v0.8h, v1.8h, v20.8h, v21.8h - clip2 v2.8h, v3.8h, v20.8h, v21.8h + clip v20.8h, v21.8h, v0.8h, v1.8h, v2.8h, v3.8h st1 {v0.8h-v1.8h}, [x0], x2 st1 {v2.8h-v3.8h}, [x12], x2 bne 1b @@ -238,13 +232,49 @@ function hevc_add_residual_32x32_16_neon, export=0 sqadd v1.8h, v1.8h, v17.8h sqadd v2.8h, v2.8h, v18.8h sqadd v3.8h, v3.8h, v19.8h - clip2 v0.8h, v1.8h, v20.8h, v21.8h - clip2 v2.8h, v3.8h, v20.8h, v21.8h + clip v20.8h, v21.8h, v0.8h, v1.8h, v2.8h, v3.8h st1 {v0.8h-v3.8h}, [x0], x2 bne 1b ret endfunc +.macro tr_4x4 in0, in1, in2, in3, out0, out1, out2, out3, shift + sshll v20.4s, \in0, #6 + sshll v21.4s, \in0, #6 + smull v22.4s, \in1, v4.h[1] + smull v23.4s, \in1, v4.h[3] + smlal v20.4s, \in2, v4.h[0] //e0 + smlsl v21.4s, \in2, v4.h[0] //e1 + smlal v22.4s, \in3, v4.h[3] //o0 + smlsl v23.4s, \in3, v4.h[1] //o1 + + add v24.4s, v20.4s, v22.4s + sub v20.4s, v20.4s, v22.4s + add v22.4s, v21.4s, v23.4s + sub v21.4s, v21.4s, v23.4s + sqrshrn \out0, v24.4s, #\shift + sqrshrn \out3, v20.4s, #\shift + sqrshrn \out1, v22.4s, #\shift + sqrshrn \out2, v21.4s, #\shift +.endm + +.macro idct_4x4 bitdepth +function ff_hevc_idct_4x4_\bitdepth\()_neon, export=1 + ld1 {v0.4h-v3.4h}, [x0] + + movrel x1, trans + ld1 {v4.4h}, [x1] + + tr_4x4 v0.4h, v1.4h, v2.4h, v3.4h, v16.4h, v17.4h, v18.4h, v19.4h, 7 + transpose_4x8H v16, v17, v18, v19, v26, v27, v28, v29 + + tr_4x4 v16.4h, v17.4h, v18.4h, v19.4h, v0.4h, v1.4h, v2.4h, v3.4h, 20 - \bitdepth + transpose_4x8H v0, v1, v2, v3, v26, v27, v28, v29 + st1 {v0.4h-v3.4h}, [x0] + ret +endfunc +.endm + .macro sum_sub out, in, c, op, p .ifc \op, + smlal\p \out, \in, \c @@ -264,35 +294,24 @@ endfunc // uses and clobbers v28-v31 as temp registers .macro tr_4x4_8 in0, in1, in2, in3, out0, out1, out2, out3, p1, p2 - sshll\p1 v28.4s, \in0, #6 - mov v29.16b, v28.16b - smull\p1 v30.4s, \in1, v0.h[1] - smull\p1 v31.4s, \in1, v0.h[3] - smlal\p2 v28.4s, \in2, v0.h[0] //e0 - smlsl\p2 v29.4s, \in2, v0.h[0] //e1 - smlal\p2 v30.4s, \in3, v0.h[3] //o0 - smlsl\p2 v31.4s, \in3, v0.h[1] //o1 - - add \out0, v28.4s, v30.4s - add \out1, v29.4s, v31.4s - sub \out2, v29.4s, v31.4s - sub \out3, v28.4s, v30.4s -.endm - -.macro transpose8_4x4 r0, r1, r2, r3 - trn1 v2.8h, \r0\().8h, \r1\().8h - trn2 v3.8h, \r0\().8h, \r1\().8h - trn1 v4.8h, \r2\().8h, \r3\().8h - trn2 v5.8h, \r2\().8h, \r3\().8h - trn1 \r0\().4s, v2.4s, v4.4s - trn2 \r2\().4s, v2.4s, v4.4s - trn1 \r1\().4s, v3.4s, v5.4s - trn2 \r3\().4s, v3.4s, v5.4s + sshll\p1 v28.4s, \in0, #6 + mov v29.16b, v28.16b + smull\p1 v30.4s, \in1, v0.h[1] + smull\p1 v31.4s, \in1, v0.h[3] + smlal\p2 v28.4s, \in2, v0.h[0] //e0 + smlsl\p2 v29.4s, \in2, v0.h[0] //e1 + smlal\p2 v30.4s, \in3, v0.h[3] //o0 + smlsl\p2 v31.4s, \in3, v0.h[1] //o1 + + add \out0, v28.4s, v30.4s + add \out1, v29.4s, v31.4s + sub \out2, v29.4s, v31.4s + sub \out3, v28.4s, v30.4s .endm .macro transpose_8x8 r0, r1, r2, r3, r4, r5, r6, r7 - transpose8_4x4 \r0, \r1, \r2, \r3 - transpose8_4x4 \r4, \r5, \r6, \r7 + transpose_4x8H \r0, \r1, \r2, \r3, v2, v3, v4, v5 + transpose_4x8H \r4, \r5, \r6, \r7, v2, v3, v4, v5 .endm .macro tr_8x4 shift, in0,in0t, in1,in1t, in2,in2t, in3,in3t, in4,in4t, in5,in5t, in6,in6t, in7,in7t, p1, p2 @@ -343,11 +362,11 @@ endfunc .macro idct_8x8 bitdepth function ff_hevc_idct_8x8_\bitdepth\()_neon, export=1 //x0 - coeffs - mov x1, x0 + mov x1, x0 ld1 {v16.8h-v19.8h}, [x1], #64 ld1 {v20.8h-v23.8h}, [x1] - movrel x1, trans + movrel x1, trans ld1 {v0.8h}, [x1] tr_8x4 7, v16,.4h, v17,.4h, v18,.4h, v19,.4h, v20,.4h, v21,.4h, v22,.4h, v23,.4h @@ -360,7 +379,7 @@ function ff_hevc_idct_8x8_\bitdepth\()_neon, export=1 transpose_8x8 v16, v17, v18, v19, v20, v21, v22, v23 - mov x1, x0 + mov x1, x0 st1 {v16.8h-v19.8h}, [x1], #64 st1 {v20.8h-v23.8h}, [x1] @@ -369,8 +388,8 @@ endfunc .endm .macro butterfly e, o, tmp_p, tmp_m - add \tmp_p, \e, \o - sub \tmp_m, \e, \o + add \tmp_p, \e, \o + sub \tmp_m, \e, \o .endm .macro tr16_8x4 in0, in1, in2, in3, offset @@ -399,7 +418,7 @@ endfunc butterfly v25.4s, v29.4s, v17.4s, v22.4s butterfly v26.4s, v30.4s, v18.4s, v21.4s butterfly v27.4s, v31.4s, v19.4s, v20.4s - add x4, sp, #\offset + add x4, sp, #\offset st1 {v16.4s-v19.4s}, [x4], #64 st1 {v20.4s-v23.4s}, [x4] .endm @@ -416,14 +435,14 @@ endfunc .endm .macro add_member in, t0, t1, t2, t3, t4, t5, t6, t7, op0, op1, op2, op3, op4, op5, op6, op7, p - sum_sub v21.4s, \in, \t0, \op0, \p - sum_sub v22.4s, \in, \t1, \op1, \p - sum_sub v23.4s, \in, \t2, \op2, \p - sum_sub v24.4s, \in, \t3, \op3, \p - sum_sub v25.4s, \in, \t4, \op4, \p - sum_sub v26.4s, \in, \t5, \op5, \p - sum_sub v27.4s, \in, \t6, \op6, \p - sum_sub v28.4s, \in, \t7, \op7, \p + sum_sub v21.4s, \in, \t0, \op0, \p + sum_sub v22.4s, \in, \t1, \op1, \p + sum_sub v23.4s, \in, \t2, \op2, \p + sum_sub v24.4s, \in, \t3, \op3, \p + sum_sub v25.4s, \in, \t4, \op4, \p + sum_sub v26.4s, \in, \t5, \op5, \p + sum_sub v27.4s, \in, \t6, \op6, \p + sum_sub v28.4s, \in, \t7, \op7, \p .endm .macro butterfly16 in0, in1, in2, in3, in4, in5, in6, in7 @@ -459,52 +478,70 @@ endfunc sqrshrn2 \out3\().8h, \in7, \shift .endm -.macro transpose16_4x4_2 r0, r1, r2, r3 +// use temp register to transpose, then we can reuse it +.macro transpose16_4x4_2 r0, r1, r2, r3, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5 // lower halves - trn1 v2.4h, \r0\().4h, \r1\().4h - trn2 v3.4h, \r0\().4h, \r1\().4h - trn1 v4.4h, \r2\().4h, \r3\().4h - trn2 v5.4h, \r2\().4h, \r3\().4h - trn1 v6.2s, v2.2s, v4.2s - trn2 v7.2s, v2.2s, v4.2s - trn1 v2.2s, v3.2s, v5.2s - trn2 v4.2s, v3.2s, v5.2s - mov \r0\().d[0], v6.d[0] - mov \r2\().d[0], v7.d[0] - mov \r1\().d[0], v2.d[0] - mov \r3\().d[0], v4.d[0] + trn1 \tmp0\().4h, \r0\().4h, \r1\().4h + trn2 \tmp1\().4h, \r0\().4h, \r1\().4h + trn1 \tmp2\().4h, \r2\().4h, \r3\().4h + trn2 \tmp3\().4h, \r2\().4h, \r3\().4h + trn1 \tmp4\().2s, \tmp0\().2s, \tmp2\().2s + trn2 \tmp5\().2s, \tmp0\().2s, \tmp2\().2s + trn1 \tmp0\().2s, \tmp1\().2s, \tmp3\().2s + trn2 \tmp2\().2s, \tmp1\().2s, \tmp3\().2s + mov \r0\().d[0], \tmp4\().d[0] + mov \r2\().d[0], \tmp5\().d[0] + mov \r1\().d[0], \tmp0\().d[0] + mov \r3\().d[0], \tmp2\().d[0] // upper halves in reverse order - trn1 v2.8h, \r3\().8h, \r2\().8h - trn2 v3.8h, \r3\().8h, \r2\().8h - trn1 v4.8h, \r1\().8h, \r0\().8h - trn2 v5.8h, \r1\().8h, \r0\().8h - trn1 v6.4s, v2.4s, v4.4s - trn2 v7.4s, v2.4s, v4.4s - trn1 v2.4s, v3.4s, v5.4s - trn2 v4.4s, v3.4s, v5.4s - mov \r3\().d[1], v6.d[1] - mov \r1\().d[1], v7.d[1] - mov \r2\().d[1], v2.d[1] - mov \r0\().d[1], v4.d[1] + trn1 \tmp0\().8h, \r3\().8h, \r2\().8h + trn2 \tmp1\().8h, \r3\().8h, \r2\().8h + trn1 \tmp2\().8h, \r1\().8h, \r0\().8h + trn2 \tmp3\().8h, \r1\().8h, \r0\().8h + trn1 \tmp4\().4s, \tmp0\().4s, \tmp2\().4s + trn2 \tmp5\().4s, \tmp0\().4s, \tmp2\().4s + trn1 \tmp0\().4s, \tmp1\().4s, \tmp3\().4s + trn2 \tmp2\().4s, \tmp1\().4s, \tmp3\().4s + mov \r3\().d[1], \tmp4\().d[1] + mov \r1\().d[1], \tmp5\().d[1] + mov \r2\().d[1], \tmp0\().d[1] + mov \r0\().d[1], \tmp2\().d[1] +.endm + +// stores in0, in2, in4, in6 ascending from off1 and +// stores in1, in3, in5, in7 descending from off2 +.macro store_to_stack off1, off2, in0, in2, in4, in6, in7, in5, in3, in1 + add x1, sp, #\off1 + add x3, sp, #\off2 + mov x2, #-16 + mov x4, #16 + st1 {\in0}, [x1], x4 + st1 {\in1}, [x3], x2 + st1 {\in2}, [x1], x4 + st1 {\in3}, [x3], x2 + st1 {\in4}, [x1], x4 + st1 {\in5}, [x3], x2 + st1 {\in6}, [x1] + st1 {\in7}, [x3] .endm .macro tr_16x4 name, shift, offset, step function func_tr_16x4_\name - mov x1, x5 - add x3, x5, #(\step * 64) - mov x2, #(\step * 128) + mov x1, x5 + add x3, x5, #(\step * 64) + mov x2, #(\step * 128) load16 v16.d, v17.d, v18.d, v19.d - movrel x1, trans + movrel x1, trans ld1 {v0.8h}, [x1] tr16_8x4 v16, v17, v18, v19, \offset - add x1, x5, #(\step * 32) - add x3, x5, #(\step * 3 *32) - mov x2, #(\step * 128) + add x1, x5, #(\step * 32) + add x3, x5, #(\step * 3 *32) + mov x2, #(\step * 128) load16 v20.d, v17.d, v18.d, v19.d - movrel x1, trans, 16 + movrel x1, trans, 16 ld1 {v1.8h}, [x1] smull v21.4s, v20.4h, v1.h[0] smull v22.4s, v20.4h, v1.h[1] @@ -523,29 +560,36 @@ function func_tr_16x4_\name add_member v19.4h, v1.h[6], v1.h[3], v1.h[0], v1.h[2], v1.h[5], v1.h[7], v1.h[4], v1.h[1], +, -, +, -, +, +, -, + add_member v19.8h, v1.h[7], v1.h[6], v1.h[5], v1.h[4], v1.h[3], v1.h[2], v1.h[1], v1.h[0], +, -, +, -, +, -, +, -, 2 - add x4, sp, #\offset + add x4, sp, #\offset ld1 {v16.4s-v19.4s}, [x4], #64 - butterfly16 v16.4s, v21.4s, v17.4s, v22.4s, v18.4s, v23.4s, v19.4s, v24.4s + .if \shift > 0 scale v29, v30, v31, v24, v20.4s, v16.4s, v21.4s, v17.4s, v22.4s, v18.4s, v23.4s, v19.4s, \shift - transpose16_4x4_2 v29, v30, v31, v24 - mov x1, x6 - add x3, x6, #(24 +3*32) - mov x2, #32 - mov x4, #-32 + transpose16_4x4_2 v29, v30, v31, v24, v2, v3, v4, v5, v6, v7 + mov x1, x6 + add x3, x6, #(24 +3*32) + mov x2, #32 + mov x4, #-32 store16 v29.d, v30.d, v31.d, v24.d, x4 + .else + store_to_stack \offset, (\offset + 240), v20.4s, v21.4s, v22.4s, v23.4s, v19.4s, v18.4s, v17.4s, v16.4s + .endif add x4, sp, #(\offset + 64) ld1 {v16.4s-v19.4s}, [x4] butterfly16 v16.4s, v25.4s, v17.4s, v26.4s, v18.4s, v27.4s, v19.4s, v28.4s + .if \shift > 0 scale v29, v30, v31, v20, v20.4s, v16.4s, v25.4s, v17.4s, v26.4s, v18.4s, v27.4s, v19.4s, \shift - transpose16_4x4_2 v29, v30, v31, v20 + transpose16_4x4_2 v29, v30, v31, v20, v2, v3, v4, v5, v6, v7 - add x1, x6, #8 - add x3, x6, #(16 + 3 * 32) - mov x2, #32 - mov x4, #-32 + add x1, x6, #8 + add x3, x6, #(16 + 3 * 32) + mov x2, #32 + mov x4, #-32 store16 v29.d, v30.d, v31.d, v20.d, x4 + .else + store_to_stack (\offset + 64), (\offset + 176), v20.4s, v25.4s, v26.4s, v27.4s, v19.4s, v18.4s, v17.4s, v16.4s + .endif ret endfunc @@ -557,69 +601,325 @@ function ff_hevc_idct_16x16_\bitdepth\()_neon, export=1 mov x15, x30 // allocate a temp buffer - sub sp, sp, #640 + sub sp, sp, #640 .irp i, 0, 1, 2, 3 - add x5, x0, #(8 * \i) - add x6, sp, #(8 * \i * 16) + add x5, x0, #(8 * \i) + add x6, sp, #(8 * \i * 16) bl func_tr_16x4_firstpass .endr .irp i, 0, 1, 2, 3 - add x5, sp, #(8 * \i) - add x6, x0, #(8 * \i * 16) + add x5, sp, #(8 * \i) + add x6, x0, #(8 * \i * 16) bl func_tr_16x4_secondpass_\bitdepth .endr - add sp, sp, #640 + add sp, sp, #640 + + ret x15 +endfunc +.endm + +.macro load32 + add x1, x5, #64 + add x3, x1, #128 + mov x2, #256 + ld1 {v4.d}[0], [x1], x2 + ld1 {v4.d}[1], [x3], x2 + ld1 {v5.d}[0], [x1], x2 + ld1 {v5.d}[1], [x3], x2 + ld1 {v6.d}[0], [x1], x2 + ld1 {v6.d}[1], [x3], x2 + ld1 {v7.d}[0], [x1], x2 + ld1 {v7.d}[1], [x3], x2 + ld1 {v16.d}[0], [x1], x2 + ld1 {v16.d}[1], [x3], x2 + ld1 {v17.d}[0], [x1], x2 + ld1 {v17.d}[1], [x3], x2 + ld1 {v18.d}[0], [x1], x2 + ld1 {v18.d}[1], [x3], x2 + ld1 {v19.d}[0], [x1], x2 + ld1 {v19.d}[1], [x3], x2 +.endm + +.macro add_member32 in, t0, t1, t2, t3, op0, op1, op2, op3, p + sum_sub v24.4s, \in, \t0, \op0, \p + sum_sub v25.4s, \in, \t1, \op1, \p + sum_sub v26.4s, \in, \t2, \op2, \p + sum_sub v27.4s, \in, \t3, \op3, \p +.endm + +.macro butterfly32 in0, in1, in2, in3, out + add \out, \in0, \in1 + sub \in0, \in0, \in1 + add \in1, \in2, \in3 + sub \in2, \in2, \in3 +.endm + +.macro multiply in + smull v24.4s, v4.4h, \in\().h[0] + smull v25.4s, v4.4h, \in\().h[1] + smull v26.4s, v4.4h, \in\().h[2] + smull v27.4s, v4.4h, \in\().h[3] +.endm + +.macro scale_store shift + ld1 {v28.8h-v31.8h}, [x4], #64 + butterfly32 v28.4s, v24.4s, v29.4s, v25.4s, v2.4s + butterfly32 v30.4s, v26.4s, v31.4s, v27.4s, v3.4s + scale v20, v21, v22, v23, v2.4s, v28.4s, v24.4s, v29.4s, v3.4s, v30.4s, v26.4s, v31.4s, \shift + + transpose16_4x4_2 v20, v21, v22, v23, v24, v25, v26, v27, v28, v29 + store16 v20.d, v21.d, v22.d, v23.d, x8 - mov x30, x15 + // reload coefficients + ld1 {v2.4h-v3.4h}, [x9] +.endm + +function tr_block1 + multiply v0 + add_member32 v4.8h, v0.h[1], v1.h[0], v1.h[3], v2.h[2], +, +, +, +, 2 + add_member32 v5.4h, v0.h[2], v1.h[3], v3.h[0], v3.h[2], +, +, +, - + add_member32 v5.8h, v0.h[3], v2.h[2], v3.h[2], v1.h[3], +, +, -, -, 2 + add_member32 v6.4h, v1.h[0], v3.h[1], v2.h[1], v0.h[0], +, +, -, - + add_member32 v6.8h, v1.h[1], v3.h[3], v1.h[0], v1.h[2], +, -, -, -, 2 + add_member32 v7.4h, v1.h[2], v3.h[0], v0.h[0], v3.h[1], +, -, -, - + add_member32 v7.8h, v1.h[3], v2.h[1], v1.h[1], v2.h[3], +, -, -, +, 2 + add_member32 v16.4h, v2.h[0], v1.h[2], v2.h[2], v1.h[0], +, -, -, + + add_member32 v16.8h, v2.h[1], v0.h[3], v3.h[3], v0.h[2], +, -, -, +, 2 + add_member32 v17.4h, v2.h[2], v0.h[1], v2.h[3], v2.h[1], +, -, +, + + add_member32 v17.8h, v2.h[3], v0.h[2], v1.h[2], v3.h[3], +, -, +, -, 2 + add_member32 v18.4h, v3.h[0], v1.h[1], v0.h[1], v2.h[0], +, -, +, - + add_member32 v18.8h, v3.h[1], v2.h[0], v0.h[3], v0.h[1], +, -, +, -, 2 + add_member32 v19.4h, v3.h[2], v2.h[3], v2.h[0], v1.h[1], +, -, +, - + add_member32 v19.8h, v3.h[3], v3.h[2], v3.h[1], v3.h[0], +, -, +, -, 2 + ret +endfunc + +function tr_block2 + multiply v1 + add_member32 v4.8h, v3.h[1], v3.h[3], v3.h[0], v2.h[1], +, -, -, -, 2 + add_member32 v5.4h, v2.h[1], v1.h[0], v0.h[0], v1.h[1], -, -, -, - + add_member32 v5.8h, v0.h[0], v1.h[2], v3.h[1], v2.h[3], -, -, -, +, 2 + add_member32 v6.4h, v2.h[0], v3.h[2], v1.h[1], v0.h[3], -, +, +, + + add_member32 v6.8h, v3.h[2], v0.h[3], v1.h[3], v3.h[1], +, +, +, -, 2 + add_member32 v7.4h, v1.h[1], v1.h[3], v2.h[3], v0.h[0], +, +, -, - + add_member32 v7.8h, v0.h[3], v3.h[1], v0.h[1], v3.h[3], +, -, -, +, 2 + add_member32 v16.4h, v3.h[0], v0.h[2], v3.h[2], v0.h[1], +, -, -, + + add_member32 v16.8h, v2.h[2], v2.h[0], v1.h[0], v3.h[2], -, -, +, +, 2 + add_member32 v17.4h, v0.h[1], v3.h[0], v2.h[0], v0.h[2], -, +, +, - + add_member32 v17.8h, v1.h[3], v0.h[1], v2.h[2], v3.h[0], -, +, -, -, 2 + add_member32 v18.4h, v3.h[3], v2.h[1], v0.h[2], v1.h[0], +, +, -, + + add_member32 v18.8h, v1.h[2], v2.h[3], v3.h[3], v2.h[2], +, -, -, +, 2 + add_member32 v19.4h, v0.h[2], v0.h[1], v0.h[3], v1.h[2], +, -, +, - + add_member32 v19.8h, v2.h[3], v2.h[2], v2.h[1], v2.h[0], +, -, +, -, 2 + ret +endfunc + +function tr_block3 + multiply v2 + add_member32 v4.8h, v1.h[2], v0.h[3], v0.h[0], v0.h[2], -, -, -, -, 2 + add_member32 v5.4h, v2.h[2], v3.h[3], v2.h[3], v1.h[2], -, -, +, + + add_member32 v5.8h, v1.h[0], v0.h[2], v2.h[1], v3.h[3], +, +, +, -, 2 + add_member32 v6.4h, v3.h[0], v2.h[2], v0.h[1], v1.h[3], +, -, -, - + add_member32 v6.8h, v0.h[2], v2.h[0], v3.h[0], v0.h[0], -, -, +, +, 2 + add_member32 v7.4h, v3.h[2], v1.h[0], v2.h[0], v2.h[2], -, +, +, - + add_member32 v7.8h, v0.h[0], v3.h[2], v0.h[2], v3.h[0], +, +, -, -, 2 + add_member32 v16.4h, v3.h[3], v0.h[1], v3.h[1], v0.h[3], -, -, +, + + add_member32 v16.8h, v0.h[1], v2.h[3], v1.h[3], v1.h[1], -, +, +, -, 2 + add_member32 v17.4h, v3.h[1], v1.h[3], v0.h[3], v3.h[2], +, +, -, + + add_member32 v17.8h, v0.h[3], v1.h[1], v3.h[2], v2.h[0], +, -, +, +, 2 + add_member32 v18.4h, v2.h[3], v3.h[1], v1.h[2], v0.h[1], -, -, +, - + add_member32 v18.8h, v1.h[1], v0.h[0], v1.h[0], v2.h[1], -, +, -, +, 2 + add_member32 v19.4h, v2.h[1], v3.h[0], v3.h[3], v3.h[1], +, -, +, + + add_member32 v19.8h, v1.h[3], v1.h[2], v1.h[1], v1.h[0], +, -, +, -, 2 ret endfunc + +function tr_block4 + multiply v3 + add_member32 v4.8h, v1.h[1], v2.h[0], v2.h[3], v3.h[2], -, -, -, -, 2 + add_member32 v5.4h, v0.h[0], v0.h[3], v2.h[0], v3.h[1], +, +, +, + + add_member32 v5.8h, v2.h[0], v0.h[0], v1.h[1], v3.h[0], -, -, -, -, 2 + add_member32 v6.4h, v3.h[3], v1.h[2], v0.h[2], v2.h[3], +, +, +, + + add_member32 v6.8h, v2.h[1], v2.h[3], v0.h[0], v2.h[2], +, -, -, -, 2 + add_member32 v7.4h, v0.h[2], v3.h[3], v0.h[3], v2.h[1], -, -, +, + + add_member32 v7.8h, v1.h[0], v2.h[2], v1.h[2], v2.h[0], +, +, -, -, 2 + add_member32 v16.4h, v2.h[3], v1.h[1], v2.h[1], v1.h[3], -, -, +, + + add_member32 v16.8h, v3.h[1], v0.h[1], v3.h[0], v1.h[2], -, +, -, -, 2 + add_member32 v17.4h, v1.h[2], v1.h[0], v3.h[3], v1.h[1], +, -, +, + + add_member32 v17.8h, v0.h[1], v2.h[1], v3.h[1], v1.h[0], -, +, +, -, 2 + add_member32 v18.4h, v1.h[3], v3.h[2], v2.h[2], v0.h[3], +, -, -, + + add_member32 v18.8h, v3.h[2], v3.h[0], v1.h[3], v0.h[2], -, -, +, -, 2 + add_member32 v19.4h, v2.h[2], v1.h[3], v1.h[0], v0.h[1], -, +, -, + + add_member32 v19.8h, v0.h[3], v0.h[2], v0.h[1], v0.h[0], +, -, +, -, 2 + ret +endfunc + +.macro tr_32x4 name, shift +function func_tr_32x4_\name + mov x10, x30 + bl func_tr_16x4_noscale + + load32 + movrel x9, trans, 32 + ld1 {v0.4h-v1.4h}, [x9], #16 + ld1 {v2.4h-v3.4h}, [x9] + add x4, sp, #2048 + mov x2, #64 + mov x8, #-64 + + bl tr_block1 + mov x1, x11 + add x3, x11, #(56 + 3 * 64) + scale_store \shift + + bl tr_block2 + add x1, x11, #8 + add x3, x11, #(48 + 3 * 64) + scale_store \shift + + bl tr_block3 + add x1, x11, #16 + add x3, x11, #(40 + 3 * 64) + scale_store \shift + + bl tr_block4 + add x1, x11, #24 + add x3, x11, #(32 + 3 * 64) + scale_store \shift + + ret x10 +endfunc +.endm + +.macro idct_32x32 bitdepth +function ff_hevc_idct_32x32_\bitdepth\()_neon, export=1 + mov x15, x30 + // allocate a temp buffer + sub sp, sp, #2432 + +.irp i, 0, 1, 2, 3, 4, 5, 6, 7 + add x5, x0, #(8 * \i) + add x11, sp, #(8 * \i * 32) + bl func_tr_32x4_firstpass +.endr + +.irp i, 0, 1, 2, 3, 4, 5, 6, 7 + add x5, sp, #(8 * \i) + add x11, x0, #(8 * \i * 32) + bl func_tr_32x4_secondpass_\bitdepth +.endr + + add sp, sp, #2432 + ret x15 +endfunc .endm +idct_4x4 8 +idct_4x4 10 + idct_8x8 8 idct_8x8 10 tr_16x4 firstpass, 7, 512, 1 tr_16x4 secondpass_8, 20 - 8, 512, 1 tr_16x4 secondpass_10, 20 - 10, 512, 1 +tr_16x4 noscale, 0, 2048, 4 idct_16x16 8 idct_16x16 10 +.ltorg +tr_32x4 firstpass, 7 +tr_32x4 secondpass_8, 20 - 8 +tr_32x4 secondpass_10, 20 - 10 +.ltorg + +idct_32x32 8 +idct_32x32 10 + +.macro tr4_luma_shift r0, r1, r2, r3, shift + saddl v0.4s, \r0, \r2 // c0 = src0 + src2 + saddl v1.4s, \r2, \r3 // c1 = src2 + src3 + ssubl v2.4s, \r0, \r3 // c2 = src0 - src3 + smull v3.4s, \r1, v21.4h // c3 = 74 * src1 + + saddl v7.4s, \r0, \r3 // src0 + src3 + ssubw v7.4s, v7.4s, \r2 // src0 - src2 + src3 + mul v7.4s, v7.4s, v18.4s // dst2 = 74 * (src0 - src2 + src3) + + mul v5.4s, v0.4s, v19.4s // 29 * c0 + mul v6.4s, v1.4s, v20.4s // 55 * c1 + add v5.4s, v5.4s, v6.4s // 29 * c0 + 55 * c1 + add v5.4s, v5.4s, v3.4s // dst0 = 29 * c0 + 55 * c1 + c3 + + mul v1.4s, v1.4s, v19.4s // 29 * c1 + mul v6.4s, v2.4s, v20.4s // 55 * c2 + sub v6.4s, v6.4s, v1.4s // 55 * c2 - 29 * c1 + add v6.4s, v6.4s, v3.4s // dst1 = 55 * c2 - 29 * c1 + c3 + + mul v0.4s, v0.4s, v20.4s // 55 * c0 + mul v2.4s, v2.4s, v19.4s // 29 * c2 + add v0.4s, v0.4s, v2.4s // 55 * c0 + 29 * c2 + sub v0.4s, v0.4s, v3.4s // dst3 = 55 * c0 + 29 * c2 - c3 + + sqrshrn \r0, v5.4s, \shift + sqrshrn \r1, v6.4s, \shift + sqrshrn \r2, v7.4s, \shift + sqrshrn \r3, v0.4s, \shift +.endm + +function ff_hevc_transform_luma_4x4_neon_8, export=1 + ld1 {v28.4h-v31.4h}, [x0] + movi v18.4s, #74 + movi v19.4s, #29 + movi v20.4s, #55 + movi v21.4h, #74 + + tr4_luma_shift v28.4h, v29.4h, v30.4h, v31.4h, #7 + transpose_4x4H v28, v29, v30, v31, v22, v23, v24, v25 + + tr4_luma_shift v28.4h, v29.4h, v30.4h, v31.4h, #12 + transpose_4x4H v28, v29, v30, v31, v22, v23, v24, v25 + + st1 {v28.4h-v31.4h}, [x0] + ret +endfunc + // void ff_hevc_idct_NxN_dc_DEPTH_neon(int16_t *coeffs) .macro idct_dc size, bitdepth function ff_hevc_idct_\size\()x\size\()_dc_\bitdepth\()_neon, export=1 - ld1r {v4.8h}, [x0] - srshr v4.8h, v4.8h, #1 - srshr v0.8h, v4.8h, #(14 - \bitdepth) - srshr v1.8h, v4.8h, #(14 - \bitdepth) + ld1r {v4.8h}, [x0] + srshr v4.8h, v4.8h, #1 + srshr v0.8h, v4.8h, #(14 - \bitdepth) + srshr v1.8h, v4.8h, #(14 - \bitdepth) .if \size > 4 - srshr v2.8h, v4.8h, #(14 - \bitdepth) - srshr v3.8h, v4.8h, #(14 - \bitdepth) + srshr v2.8h, v4.8h, #(14 - \bitdepth) + srshr v3.8h, v4.8h, #(14 - \bitdepth) .if \size > 16 /* dc 32x32 */ - mov x2, #4 + mov x2, #4 1: - subs x2, x2, #1 + subs x2, x2, #1 .endif add x12, x0, #64 mov x13, #128 .if \size > 8 /* dc 16x16 */ - st1 {v0.8h-v3.8h}, [x0], x13 - st1 {v0.8h-v3.8h}, [x12], x13 - st1 {v0.8h-v3.8h}, [x0], x13 - st1 {v0.8h-v3.8h}, [x12], x13 - st1 {v0.8h-v3.8h}, [x0], x13 - st1 {v0.8h-v3.8h}, [x12], x13 + st1 {v0.8h-v3.8h}, [x0], x13 + st1 {v0.8h-v3.8h}, [x12], x13 + st1 {v0.8h-v3.8h}, [x0], x13 + st1 {v0.8h-v3.8h}, [x12], x13 + st1 {v0.8h-v3.8h}, [x0], x13 + st1 {v0.8h-v3.8h}, [x12], x13 .endif /* dc 8x8 */ - st1 {v0.8h-v3.8h}, [x0], x13 - st1 {v0.8h-v3.8h}, [x12], x13 + st1 {v0.8h-v3.8h}, [x0], x13 + st1 {v0.8h-v3.8h}, [x12], x13 .if \size > 16 /* dc 32x32 */ bne 1b .endif .else /* dc 4x4 */ - st1 {v0.8h-v1.8h}, [x0] + st1 {v0.8h-v1.8h}, [x0] .endif ret endfunc diff --git a/libavcodec/aarch64/hevcdsp_init_aarch64.c b/libavcodec/aarch64/hevcdsp_init_aarch64.c index 88a797f3931..e24dd0cbda2 100644 --- a/libavcodec/aarch64/hevcdsp_init_aarch64.c +++ b/libavcodec/aarch64/hevcdsp_init_aarch64.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2020 Reimar Döffinger + * Copyright (c) 2023 xu fulong <839789740@qq.com> * * This file is part of FFmpeg. * @@ -25,6 +26,30 @@ #include "libavutil/aarch64/cpu.h" #include "libavcodec/hevcdsp.h" +void ff_hevc_v_loop_filter_chroma_8_neon(uint8_t *_pix, ptrdiff_t _stride, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_v_loop_filter_chroma_10_neon(uint8_t *_pix, ptrdiff_t _stride, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_v_loop_filter_chroma_12_neon(uint8_t *_pix, ptrdiff_t _stride, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_h_loop_filter_chroma_8_neon(uint8_t *_pix, ptrdiff_t _stride, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_h_loop_filter_chroma_10_neon(uint8_t *_pix, ptrdiff_t _stride, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_h_loop_filter_chroma_12_neon(uint8_t *_pix, ptrdiff_t _stride, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_v_loop_filter_luma_8_neon(uint8_t *_pix, ptrdiff_t _stride, int beta, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_v_loop_filter_luma_10_neon(uint8_t *_pix, ptrdiff_t _stride, int beta, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_v_loop_filter_luma_12_neon(uint8_t *_pix, ptrdiff_t _stride, int beta, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_h_loop_filter_luma_8_neon(uint8_t *_pix, ptrdiff_t _stride, int beta, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_h_loop_filter_luma_10_neon(uint8_t *_pix, ptrdiff_t _stride, int beta, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); +void ff_hevc_h_loop_filter_luma_12_neon(uint8_t *_pix, ptrdiff_t _stride, int beta, + const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q); void ff_hevc_add_residual_4x4_8_neon(uint8_t *_dst, const int16_t *coeffs, ptrdiff_t stride); void ff_hevc_add_residual_4x4_10_neon(uint8_t *_dst, const int16_t *coeffs, @@ -49,10 +74,14 @@ void ff_hevc_add_residual_32x32_10_neon(uint8_t *_dst, const int16_t *coeffs, ptrdiff_t stride); void ff_hevc_add_residual_32x32_12_neon(uint8_t *_dst, const int16_t *coeffs, ptrdiff_t stride); +void ff_hevc_idct_4x4_8_neon(int16_t *coeffs, int col_limit); +void ff_hevc_idct_4x4_10_neon(int16_t *coeffs, int col_limit); void ff_hevc_idct_8x8_8_neon(int16_t *coeffs, int col_limit); void ff_hevc_idct_8x8_10_neon(int16_t *coeffs, int col_limit); void ff_hevc_idct_16x16_8_neon(int16_t *coeffs, int col_limit); void ff_hevc_idct_16x16_10_neon(int16_t *coeffs, int col_limit); +void ff_hevc_idct_32x32_8_neon(int16_t *coeffs, int col_limit); +void ff_hevc_idct_32x32_10_neon(int16_t *coeffs, int col_limit); void ff_hevc_idct_4x4_dc_8_neon(int16_t *coeffs); void ff_hevc_idct_8x8_dc_8_neon(int16_t *coeffs); void ff_hevc_idct_16x16_dc_8_neon(int16_t *coeffs); @@ -61,6 +90,7 @@ void ff_hevc_idct_4x4_dc_10_neon(int16_t *coeffs); void ff_hevc_idct_8x8_dc_10_neon(int16_t *coeffs); void ff_hevc_idct_16x16_dc_10_neon(int16_t *coeffs); void ff_hevc_idct_32x32_dc_10_neon(int16_t *coeffs); +void ff_hevc_transform_luma_4x4_neon_8(int16_t *coeffs); void ff_hevc_sao_band_filter_8x8_8_neon(uint8_t *_dst, const uint8_t *_src, ptrdiff_t stride_dst, ptrdiff_t stride_src, const int16_t *sao_offset_val, int sao_left_class, @@ -79,6 +109,8 @@ void ff_hevc_put_hevc_qpel_h12_8_neon(int16_t *dst, const uint8_t *_src, ptrdiff intptr_t mx, intptr_t my, int width); void ff_hevc_put_hevc_qpel_h16_8_neon(int16_t *dst, const uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width); +void ff_hevc_put_hevc_qpel_h32_8_neon(int16_t *dst, const uint8_t *_src, ptrdiff_t _srcstride, int height, + intptr_t mx, intptr_t my, int width); void ff_hevc_put_hevc_qpel_uni_h4_8_neon(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width); @@ -94,6 +126,9 @@ void ff_hevc_put_hevc_qpel_uni_h12_8_neon(uint8_t *_dst, ptrdiff_t _dststride, c void ff_hevc_put_hevc_qpel_uni_h16_8_neon(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t my, int width); +void ff_hevc_put_hevc_qpel_uni_h32_8_neon(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, + ptrdiff_t _srcstride, int height, intptr_t mx, intptr_t + my, int width); void ff_hevc_put_hevc_qpel_bi_h4_8_neon(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, intptr_t mx, intptr_t my, int width); @@ -109,22 +144,249 @@ void ff_hevc_put_hevc_qpel_bi_h12_8_neon(uint8_t *_dst, ptrdiff_t _dststride, co void ff_hevc_put_hevc_qpel_bi_h16_8_neon(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, intptr_t mx, intptr_t my, int width); +void ff_hevc_put_hevc_qpel_bi_h32_8_neon(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, + ptrdiff_t _srcstride, const int16_t *src2, int height, intptr_t + mx, intptr_t my, int width); + +#define NEON8_FNPROTO(fn, args, ext) \ + void ff_hevc_put_hevc_##fn##4_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##6_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##8_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##12_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##16_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##24_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##32_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##48_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##64_8_neon##ext args + +#define NEON8_FNPROTO_PARTIAL_4(fn, args, ext) \ + void ff_hevc_put_hevc_##fn##4_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##8_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##16_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##64_8_neon##ext args + +#define NEON8_FNPROTO_PARTIAL_5(fn, args, ext) \ + void ff_hevc_put_hevc_##fn##4_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##8_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##16_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##32_8_neon##ext args; \ + void ff_hevc_put_hevc_##fn##64_8_neon##ext args + +NEON8_FNPROTO(pel_pixels, (int16_t *dst, + const uint8_t *src, ptrdiff_t srcstride, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(pel_bi_pixels, (uint8_t *dst, ptrdiff_t dststride, + const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(epel_bi_h, (uint8_t *dst, ptrdiff_t dststride, + const uint8_t *src, ptrdiff_t srcstride, const int16_t *src2, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(epel_bi_v, (uint8_t *dst, ptrdiff_t dststride, + const uint8_t *src, ptrdiff_t srcstride, const int16_t *src2, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(epel_bi_hv, (uint8_t *dst, ptrdiff_t dststride, + const uint8_t *src, ptrdiff_t srcstride, const int16_t *src2, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(epel_bi_hv, (uint8_t *dst, ptrdiff_t dststride, + const uint8_t *src, ptrdiff_t srcstride, const int16_t *src2, + int height, intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(epel_v, (int16_t *dst, + const uint8_t *src, ptrdiff_t srcstride, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(pel_uni_pixels, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(pel_uni_w_pixels, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(epel_uni_v, (uint8_t *dst, ptrdiff_t dststride, + const uint8_t *src, ptrdiff_t srcstride, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(epel_uni_hv, (uint8_t *dst, ptrdiff_t _dststride, + const uint8_t *src, ptrdiff_t srcstride, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(epel_uni_hv, (uint8_t *dst, ptrdiff_t _dststride, + const uint8_t *src, ptrdiff_t srcstride, + int height, intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(epel_uni_w_v, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO_PARTIAL_4(qpel_uni_w_v, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(epel_h, (int16_t *dst, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(epel_hv, (int16_t *dst, + const uint8_t *src, ptrdiff_t srcstride, + int height, intptr_t mx, intptr_t my, int width), ); + +NEON8_FNPROTO(epel_h, (int16_t *dst, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(epel_hv, (int16_t *dst, + const uint8_t *src, ptrdiff_t srcstride, + int height, intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(epel_uni_w_h, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(epel_uni_w_h, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(qpel_h, (int16_t *dst, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(qpel_v, (int16_t *dst, + const uint8_t *src, ptrdiff_t srcstride, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(qpel_hv, (int16_t *dst, + const uint8_t *src, ptrdiff_t srcstride, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(qpel_hv, (int16_t *dst, + const uint8_t *src, ptrdiff_t srcstride, + int height, intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(qpel_uni_v, (uint8_t *dst, ptrdiff_t dststride, + const uint8_t *src, ptrdiff_t srcstride, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(qpel_uni_hv, (uint8_t *dst, ptrdiff_t dststride, + const uint8_t *src, ptrdiff_t srcstride, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(qpel_uni_hv, (uint8_t *dst, ptrdiff_t dststride, + const uint8_t *src, ptrdiff_t srcstride, + int height, intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(qpel_uni_w_h, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(qpel_uni_w_h, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(epel_uni_w_hv, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(epel_uni_w_hv, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO_PARTIAL_5(qpel_uni_w_hv, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO_PARTIAL_5(qpel_uni_w_hv, (uint8_t *_dst, ptrdiff_t _dststride, + const uint8_t *_src, ptrdiff_t _srcstride, + int height, int denom, int wx, int ox, + intptr_t mx, intptr_t my, int width), _i8mm); + +NEON8_FNPROTO(qpel_bi_v, (uint8_t *dst, ptrdiff_t dststride, + const uint8_t *src, ptrdiff_t srcstride, const int16_t *src2, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(qpel_bi_hv, (uint8_t *dst, ptrdiff_t dststride, + const uint8_t *src, ptrdiff_t srcstride, const int16_t *src2, + int height, intptr_t mx, intptr_t my, int width),); + +NEON8_FNPROTO(qpel_bi_hv, (uint8_t *dst, ptrdiff_t dststride, + const uint8_t *src, ptrdiff_t srcstride, const int16_t *src2, + int height, intptr_t mx, intptr_t my, int width), _i8mm); + +#define NEON8_FNASSIGN(member, v, h, fn, ext) \ + member[1][v][h] = ff_hevc_put_hevc_##fn##4_8_neon##ext; \ + member[2][v][h] = ff_hevc_put_hevc_##fn##6_8_neon##ext; \ + member[3][v][h] = ff_hevc_put_hevc_##fn##8_8_neon##ext; \ + member[4][v][h] = ff_hevc_put_hevc_##fn##12_8_neon##ext; \ + member[5][v][h] = ff_hevc_put_hevc_##fn##16_8_neon##ext; \ + member[6][v][h] = ff_hevc_put_hevc_##fn##24_8_neon##ext; \ + member[7][v][h] = ff_hevc_put_hevc_##fn##32_8_neon##ext; \ + member[8][v][h] = ff_hevc_put_hevc_##fn##48_8_neon##ext; \ + member[9][v][h] = ff_hevc_put_hevc_##fn##64_8_neon##ext; + +#define NEON8_FNASSIGN_SHARED_32(member, v, h, fn, ext) \ + member[1][v][h] = ff_hevc_put_hevc_##fn##4_8_neon##ext; \ + member[2][v][h] = ff_hevc_put_hevc_##fn##6_8_neon##ext; \ + member[3][v][h] = ff_hevc_put_hevc_##fn##8_8_neon##ext; \ + member[4][v][h] = ff_hevc_put_hevc_##fn##12_8_neon##ext; \ + member[5][v][h] = ff_hevc_put_hevc_##fn##16_8_neon##ext; \ + member[6][v][h] = ff_hevc_put_hevc_##fn##24_8_neon##ext; \ + member[7][v][h] = \ + member[8][v][h] = \ + member[9][v][h] = ff_hevc_put_hevc_##fn##32_8_neon##ext; + +#define NEON8_FNASSIGN_PARTIAL_4(member, v, h, fn, ext) \ + member[1][v][h] = ff_hevc_put_hevc_##fn##4_8_neon##ext; \ + member[3][v][h] = ff_hevc_put_hevc_##fn##8_8_neon##ext; \ + member[5][v][h] = ff_hevc_put_hevc_##fn##16_8_neon##ext; \ + member[7][v][h] = ff_hevc_put_hevc_##fn##64_8_neon##ext; \ + member[8][v][h] = ff_hevc_put_hevc_##fn##64_8_neon##ext; \ + member[9][v][h] = ff_hevc_put_hevc_##fn##64_8_neon##ext; + +#define NEON8_FNASSIGN_PARTIAL_5(member, v, h, fn, ext) \ + member[1][v][h] = ff_hevc_put_hevc_##fn##4_8_neon##ext; \ + member[3][v][h] = ff_hevc_put_hevc_##fn##8_8_neon##ext; \ + member[5][v][h] = ff_hevc_put_hevc_##fn##16_8_neon##ext; \ + member[7][v][h] = ff_hevc_put_hevc_##fn##32_8_neon##ext; \ + member[9][v][h] = ff_hevc_put_hevc_##fn##64_8_neon##ext; av_cold void ff_hevc_dsp_init_aarch64(HEVCDSPContext *c, const int bit_depth) { - if (!have_neon(av_get_cpu_flags())) return; + int cpu_flags = av_get_cpu_flags(); + if (!have_neon(cpu_flags)) return; if (bit_depth == 8) { + c->hevc_h_loop_filter_luma = ff_hevc_h_loop_filter_luma_8_neon; + c->hevc_v_loop_filter_luma = ff_hevc_v_loop_filter_luma_8_neon; + c->hevc_h_loop_filter_chroma = ff_hevc_h_loop_filter_chroma_8_neon; + c->hevc_v_loop_filter_chroma = ff_hevc_v_loop_filter_chroma_8_neon; c->add_residual[0] = ff_hevc_add_residual_4x4_8_neon; c->add_residual[1] = ff_hevc_add_residual_8x8_8_neon; c->add_residual[2] = ff_hevc_add_residual_16x16_8_neon; c->add_residual[3] = ff_hevc_add_residual_32x32_8_neon; + c->idct[0] = ff_hevc_idct_4x4_8_neon; c->idct[1] = ff_hevc_idct_8x8_8_neon; c->idct[2] = ff_hevc_idct_16x16_8_neon; + c->idct[3] = ff_hevc_idct_32x32_8_neon; c->idct_dc[0] = ff_hevc_idct_4x4_dc_8_neon; c->idct_dc[1] = ff_hevc_idct_8x8_dc_8_neon; c->idct_dc[2] = ff_hevc_idct_16x16_dc_8_neon; c->idct_dc[3] = ff_hevc_idct_32x32_dc_8_neon; + c->transform_4x4_luma = ff_hevc_transform_luma_4x4_neon_8; c->sao_band_filter[0] = c->sao_band_filter[1] = c->sao_band_filter[2] = @@ -140,42 +402,101 @@ av_cold void ff_hevc_dsp_init_aarch64(HEVCDSPContext *c, const int bit_depth) c->put_hevc_qpel[3][0][1] = ff_hevc_put_hevc_qpel_h8_8_neon; c->put_hevc_qpel[4][0][1] = c->put_hevc_qpel[6][0][1] = ff_hevc_put_hevc_qpel_h12_8_neon; - c->put_hevc_qpel[5][0][1] = + c->put_hevc_qpel[5][0][1] = ff_hevc_put_hevc_qpel_h16_8_neon; c->put_hevc_qpel[7][0][1] = c->put_hevc_qpel[8][0][1] = - c->put_hevc_qpel[9][0][1] = ff_hevc_put_hevc_qpel_h16_8_neon; + c->put_hevc_qpel[9][0][1] = ff_hevc_put_hevc_qpel_h32_8_neon; c->put_hevc_qpel_uni[1][0][1] = ff_hevc_put_hevc_qpel_uni_h4_8_neon; c->put_hevc_qpel_uni[2][0][1] = ff_hevc_put_hevc_qpel_uni_h6_8_neon; c->put_hevc_qpel_uni[3][0][1] = ff_hevc_put_hevc_qpel_uni_h8_8_neon; c->put_hevc_qpel_uni[4][0][1] = c->put_hevc_qpel_uni[6][0][1] = ff_hevc_put_hevc_qpel_uni_h12_8_neon; - c->put_hevc_qpel_uni[5][0][1] = + c->put_hevc_qpel_uni[5][0][1] = ff_hevc_put_hevc_qpel_uni_h16_8_neon; c->put_hevc_qpel_uni[7][0][1] = c->put_hevc_qpel_uni[8][0][1] = - c->put_hevc_qpel_uni[9][0][1] = ff_hevc_put_hevc_qpel_uni_h16_8_neon; + c->put_hevc_qpel_uni[9][0][1] = ff_hevc_put_hevc_qpel_uni_h32_8_neon; c->put_hevc_qpel_bi[1][0][1] = ff_hevc_put_hevc_qpel_bi_h4_8_neon; c->put_hevc_qpel_bi[2][0][1] = ff_hevc_put_hevc_qpel_bi_h6_8_neon; c->put_hevc_qpel_bi[3][0][1] = ff_hevc_put_hevc_qpel_bi_h8_8_neon; c->put_hevc_qpel_bi[4][0][1] = c->put_hevc_qpel_bi[6][0][1] = ff_hevc_put_hevc_qpel_bi_h12_8_neon; - c->put_hevc_qpel_bi[5][0][1] = + c->put_hevc_qpel_bi[5][0][1] = ff_hevc_put_hevc_qpel_bi_h16_8_neon; c->put_hevc_qpel_bi[7][0][1] = c->put_hevc_qpel_bi[8][0][1] = - c->put_hevc_qpel_bi[9][0][1] = ff_hevc_put_hevc_qpel_bi_h16_8_neon; + c->put_hevc_qpel_bi[9][0][1] = ff_hevc_put_hevc_qpel_bi_h32_8_neon; + + NEON8_FNASSIGN(c->put_hevc_epel, 0, 0, pel_pixels,); + NEON8_FNASSIGN(c->put_hevc_epel, 1, 0, epel_v,); + NEON8_FNASSIGN(c->put_hevc_qpel, 0, 0, pel_pixels,); + NEON8_FNASSIGN(c->put_hevc_qpel, 1, 0, qpel_v,); + NEON8_FNASSIGN(c->put_hevc_epel_bi, 0, 0, pel_bi_pixels,); + NEON8_FNASSIGN(c->put_hevc_epel_bi, 0, 1, epel_bi_h,); + NEON8_FNASSIGN(c->put_hevc_epel_bi, 1, 0, epel_bi_v,); + NEON8_FNASSIGN(c->put_hevc_qpel_bi, 0, 0, pel_bi_pixels,); + NEON8_FNASSIGN(c->put_hevc_qpel_bi, 1, 0, qpel_bi_v,); + NEON8_FNASSIGN(c->put_hevc_epel_uni, 0, 0, pel_uni_pixels,); + NEON8_FNASSIGN(c->put_hevc_epel_uni, 1, 0, epel_uni_v,); + NEON8_FNASSIGN(c->put_hevc_qpel_uni, 0, 0, pel_uni_pixels,); + NEON8_FNASSIGN(c->put_hevc_qpel_uni, 1, 0, qpel_uni_v,); + NEON8_FNASSIGN(c->put_hevc_epel_uni_w, 0, 0, pel_uni_w_pixels,); + NEON8_FNASSIGN(c->put_hevc_qpel_uni_w, 0, 0, pel_uni_w_pixels,); + NEON8_FNASSIGN(c->put_hevc_epel_uni_w, 1, 0, epel_uni_w_v,); + NEON8_FNASSIGN_PARTIAL_4(c->put_hevc_qpel_uni_w, 1, 0, qpel_uni_w_v,); + + NEON8_FNASSIGN_SHARED_32(c->put_hevc_epel, 0, 1, epel_h,); + NEON8_FNASSIGN_SHARED_32(c->put_hevc_epel_uni_w, 0, 1, epel_uni_w_h,); + + NEON8_FNASSIGN(c->put_hevc_epel, 1, 1, epel_hv,); + NEON8_FNASSIGN(c->put_hevc_epel_uni, 1, 1, epel_uni_hv,); + NEON8_FNASSIGN(c->put_hevc_epel_uni_w, 1, 1, epel_uni_w_hv,); + NEON8_FNASSIGN(c->put_hevc_epel_bi, 1, 1, epel_bi_hv,); + + NEON8_FNASSIGN_SHARED_32(c->put_hevc_qpel_uni_w, 0, 1, qpel_uni_w_h,); + + NEON8_FNASSIGN(c->put_hevc_qpel, 1, 1, qpel_hv,); + NEON8_FNASSIGN(c->put_hevc_qpel_uni, 1, 1, qpel_uni_hv,); + NEON8_FNASSIGN_PARTIAL_5(c->put_hevc_qpel_uni_w, 1, 1, qpel_uni_w_hv,); + NEON8_FNASSIGN(c->put_hevc_qpel_bi, 1, 1, qpel_bi_hv,); + + if (have_i8mm(cpu_flags)) { + NEON8_FNASSIGN(c->put_hevc_epel, 0, 1, epel_h, _i8mm); + NEON8_FNASSIGN(c->put_hevc_epel, 1, 1, epel_hv, _i8mm); + NEON8_FNASSIGN(c->put_hevc_epel_uni, 1, 1, epel_uni_hv, _i8mm); + NEON8_FNASSIGN(c->put_hevc_epel_uni_w, 0, 1, epel_uni_w_h ,_i8mm); + NEON8_FNASSIGN(c->put_hevc_epel_uni_w, 1, 1, epel_uni_w_hv, _i8mm); + NEON8_FNASSIGN(c->put_hevc_epel_bi, 1, 1, epel_bi_hv, _i8mm); + NEON8_FNASSIGN(c->put_hevc_qpel, 0, 1, qpel_h, _i8mm); + NEON8_FNASSIGN(c->put_hevc_qpel, 1, 1, qpel_hv, _i8mm); + NEON8_FNASSIGN(c->put_hevc_qpel_uni, 1, 1, qpel_uni_hv, _i8mm); + NEON8_FNASSIGN(c->put_hevc_qpel_uni_w, 0, 1, qpel_uni_w_h, _i8mm); + NEON8_FNASSIGN_PARTIAL_5(c->put_hevc_qpel_uni_w, 1, 1, qpel_uni_w_hv, _i8mm); + NEON8_FNASSIGN(c->put_hevc_qpel_bi, 1, 1, qpel_bi_hv, _i8mm); + } + } if (bit_depth == 10) { + c->hevc_h_loop_filter_luma = ff_hevc_h_loop_filter_luma_10_neon; + c->hevc_v_loop_filter_luma = ff_hevc_v_loop_filter_luma_10_neon; + c->hevc_h_loop_filter_chroma = ff_hevc_h_loop_filter_chroma_10_neon; + c->hevc_v_loop_filter_chroma = ff_hevc_v_loop_filter_chroma_10_neon; c->add_residual[0] = ff_hevc_add_residual_4x4_10_neon; c->add_residual[1] = ff_hevc_add_residual_8x8_10_neon; c->add_residual[2] = ff_hevc_add_residual_16x16_10_neon; c->add_residual[3] = ff_hevc_add_residual_32x32_10_neon; + c->idct[0] = ff_hevc_idct_4x4_10_neon; c->idct[1] = ff_hevc_idct_8x8_10_neon; c->idct[2] = ff_hevc_idct_16x16_10_neon; + c->idct[3] = ff_hevc_idct_32x32_10_neon; c->idct_dc[0] = ff_hevc_idct_4x4_dc_10_neon; c->idct_dc[1] = ff_hevc_idct_8x8_dc_10_neon; c->idct_dc[2] = ff_hevc_idct_16x16_dc_10_neon; c->idct_dc[3] = ff_hevc_idct_32x32_dc_10_neon; } if (bit_depth == 12) { + c->hevc_h_loop_filter_luma = ff_hevc_h_loop_filter_luma_12_neon; + c->hevc_v_loop_filter_luma = ff_hevc_v_loop_filter_luma_12_neon; + c->hevc_h_loop_filter_chroma = ff_hevc_h_loop_filter_chroma_12_neon; + c->hevc_v_loop_filter_chroma = ff_hevc_v_loop_filter_chroma_12_neon; c->add_residual[0] = ff_hevc_add_residual_4x4_12_neon; c->add_residual[1] = ff_hevc_add_residual_8x8_12_neon; c->add_residual[2] = ff_hevc_add_residual_16x16_12_neon; diff --git a/libavcodec/aarch64/hevcdsp_qpel_neon.S b/libavcodec/aarch64/hevcdsp_qpel_neon.S index 0e7b912678d..8ddaa32b70a 100644 --- a/libavcodec/aarch64/hevcdsp_qpel_neon.S +++ b/libavcodec/aarch64/hevcdsp_qpel_neon.S @@ -30,6 +30,13 @@ const qpel_filters, align=4 .byte 0, 1, -5, 17, 58,-10, 4, -1 endconst +const qpel_filters_abs, align=4 + .byte 0, 0, 0, 0, 0, 0, 0, 0 + .byte 1, 4, 10, 58, 17, 5, 1, 0 + .byte 1, 4, 11, 40, 40, 11, 4, 1 + .byte 0, 1, 5, 17, 58, 10, 4, 1 +endconst + .macro load_filter m movrel x15, qpel_filters add x15, x15, \m, lsl #3 @@ -37,6 +44,112 @@ endconst sxtl v0.8h, v0.8b .endm +.macro load_qpel_filterb freg, xreg + movrel \xreg, qpel_filters_abs + add \xreg, \xreg, \freg, lsl #3 + ld4r {v0.16b, v1.16b, v2.16b, v3.16b}, [\xreg], #4 + ld4r {v4.16b, v5.16b, v6.16b, v7.16b}, [\xreg] +.endm + +.macro calc_qpelb dst, src0, src1, src2, src3, src4, src5, src6, src7 + umull \dst\().8h, \src1\().8b, v1.8b + umlsl \dst\().8h, \src0\().8b, v0.8b + umlsl \dst\().8h, \src2\().8b, v2.8b + umlal \dst\().8h, \src3\().8b, v3.8b + umlal \dst\().8h, \src4\().8b, v4.8b + umlsl \dst\().8h, \src5\().8b, v5.8b + umlal \dst\().8h, \src6\().8b, v6.8b + umlsl \dst\().8h, \src7\().8b, v7.8b +.endm + +.macro calc_qpelb2 dst, src0, src1, src2, src3, src4, src5, src6, src7 + umull2 \dst\().8h, \src1\().16b, v1.16b + umlsl2 \dst\().8h, \src0\().16b, v0.16b + umlsl2 \dst\().8h, \src2\().16b, v2.16b + umlal2 \dst\().8h, \src3\().16b, v3.16b + umlal2 \dst\().8h, \src4\().16b, v4.16b + umlsl2 \dst\().8h, \src5\().16b, v5.16b + umlal2 \dst\().8h, \src6\().16b, v6.16b + umlsl2 \dst\().8h, \src7\().16b, v7.16b +.endm + +.macro load_qpel_filterh freg, xreg + movrel \xreg, qpel_filters + add \xreg, \xreg, \freg, lsl #3 + ld1 {v0.8b}, [\xreg] + sxtl v0.8h, v0.8b +.endm + +.macro calc_qpelh dst, src0, src1, src2, src3, src4, src5, src6, src7, op, shift=6 + smull \dst\().4s, \src0\().4h, v0.h[0] + smlal \dst\().4s, \src1\().4h, v0.h[1] + smlal \dst\().4s, \src2\().4h, v0.h[2] + smlal \dst\().4s, \src3\().4h, v0.h[3] + smlal \dst\().4s, \src4\().4h, v0.h[4] + smlal \dst\().4s, \src5\().4h, v0.h[5] + smlal \dst\().4s, \src6\().4h, v0.h[6] + smlal \dst\().4s, \src7\().4h, v0.h[7] +.ifc \op, sshr + sshr \dst\().4s, \dst\().4s, \shift +.else + \op \dst\().4h, \dst\().4s, \shift +.endif +.endm + +.macro calc_qpelh2 dst, dstt, src0, src1, src2, src3, src4, src5, src6, src7, op, shift=6 + smull2 \dstt\().4s, \src0\().8h, v0.h[0] + smlal2 \dstt\().4s, \src1\().8h, v0.h[1] + smlal2 \dstt\().4s, \src2\().8h, v0.h[2] + smlal2 \dstt\().4s, \src3\().8h, v0.h[3] + smlal2 \dstt\().4s, \src4\().8h, v0.h[4] + smlal2 \dstt\().4s, \src5\().8h, v0.h[5] + smlal2 \dstt\().4s, \src6\().8h, v0.h[6] + smlal2 \dstt\().4s, \src7\().8h, v0.h[7] +.ifc \op, sshr + sshr \dst\().4s, \dstt\().4s, \shift +.else + \op \dst\().8h, \dstt\().4s, \shift +.endif +.endm + +.macro calc_all + calc v23, v16, v17, v18, v19, v20, v21, v22, v23 + b.eq 2f + calc v16, v17, v18, v19, v20, v21, v22, v23, v16 + b.eq 2f + calc v17, v18, v19, v20, v21, v22, v23, v16, v17 + b.eq 2f + calc v18, v19, v20, v21, v22, v23, v16, v17, v18 + b.eq 2f + calc v19, v20, v21, v22, v23, v16, v17, v18, v19 + b.eq 2f + calc v20, v21, v22, v23, v16, v17, v18, v19, v20 + b.eq 2f + calc v21, v22, v23, v16, v17, v18, v19, v20, v21 + b.eq 2f + calc v22, v23, v16, v17, v18, v19, v20, v21, v22 + b.hi 1b +.endm + +.macro calc_all2 + calc v30, v31, v16, v18, v20, v22, v24, v26, v28, v30, v17, v19, v21, v23, v25, v27, v29, v31 + b.eq 2f + calc v16, v17, v18, v20, v22, v24, v26, v28, v30, v16, v19, v21, v23, v25, v27, v29, v31, v17 + b.eq 2f + calc v18, v19, v20, v22, v24, v26, v28, v30, v16, v18, v21, v23, v25, v27, v29, v31, v17, v19 + b.eq 2f + calc v20, v21, v22, v24, v26, v28, v30, v16, v18, v20, v23, v25, v27, v29, v31, v17, v19, v21 + b.eq 2f + calc v22, v23, v24, v26, v28, v30, v16, v18, v20, v22, v25, v27, v29, v31, v17, v19, v21, v23 + b.eq 2f + calc v24, v25, v26, v28, v30, v16, v18, v20, v22, v24, v27, v29, v31, v17, v19, v21, v23, v25 + b.eq 2f + calc v26, v27, v28, v30, v16, v18, v20, v22, v24, v26, v29, v31, v17, v19, v21, v23, v25, v27 + b.eq 2f + calc v28, v29, v30, v16, v18, v20, v22, v24, v26, v28, v31, v17, v19, v21, v23, v25, v27, v29 + b.hi 1b +.endm + .macro put_hevc type .ifc \type, qpel // void put_hevc_qpel_h(int16_t *dst, @@ -270,11 +383,9 @@ endfunc .ifc \type, qpel function ff_hevc_put_hevc_h16_8_neon, export=0 - uxtl v16.8h, v16.8b uxtl v17.8h, v17.8b uxtl v18.8h, v18.8b - uxtl v19.8h, v19.8b uxtl v20.8h, v20.8b uxtl v21.8h, v21.8b @@ -295,7 +406,6 @@ function ff_hevc_put_hevc_h16_8_neon, export=0 mla v28.8h, v24.8h, v0.h[\i] mla v29.8h, v25.8h, v0.h[\i] .endr - subs x9, x9, #2 ret endfunc .endif @@ -326,7 +436,10 @@ function ff_hevc_put_hevc_\type\()_h12_8_neon, export=1 1: ld1 {v16.8b-v18.8b}, [src], x13 ld1 {v19.8b-v21.8b}, [x12], x13 + uxtl v16.8h, v16.8b + uxtl v19.8h, v19.8b bl ff_hevc_put_hevc_h16_8_neon + subs x9, x9, #2 .ifc \type, qpel st1 {v26.8h}, [dst], #16 @@ -391,7 +504,6 @@ function ff_hevc_put_hevc_\type\()_h16_8_neon, export=1 .ifc \type, qpel_bi ldrh w8, [sp] // width mov x16, #(MAX_PB_SIZE << 2) // src2bstridel - lsl x17, x5, #7 // src2b reset add x15, x4, #(MAX_PB_SIZE << 1) // src2b .endif sub src, src, #3 @@ -399,25 +511,25 @@ function ff_hevc_put_hevc_\type\()_h16_8_neon, export=1 .ifc \type, qpel mov dststride, #(MAX_PB_SIZE << 1) lsl x13, srcstride, #1 // srcstridel - mov x14, #((MAX_PB_SIZE << 2) - 16) + mov x14, #(MAX_PB_SIZE << 2) .else lsl x14, dststride, #1 // dststridel lsl x13, srcstride, #1 // srcstridel - sub x14, x14, #8 .endif add x10, dst, dststride // dstb add x12, src, srcstride // srcb -0: mov x9, height + 1: ld1 {v16.8b-v18.8b}, [src], x13 ld1 {v19.8b-v21.8b}, [x12], x13 + uxtl v16.8h, v16.8b + uxtl v19.8h, v19.8b bl ff_hevc_put_hevc_h16_8_neon + subs height, height, #2 .ifc \type, qpel - st1 {v26.8h}, [dst], #16 - st1 {v28.8h}, [x10], #16 - st1 {v27.8h}, [dst], x14 - st1 {v29.8h}, [x10], x14 + st1 {v26.8h, v27.8h}, [dst], x14 + st1 {v28.8h, v29.8h}, [x10], x14 .else .ifc \type, qpel_bi ld1 {v16.8h, v17.8h}, [ x4], x16 @@ -436,34 +548,87 @@ function ff_hevc_put_hevc_\type\()_h16_8_neon, export=1 sqrshrun v28.8b, v28.8h, #6 sqrshrun v29.8b, v29.8h, #6 .endif - st1 {v26.8b}, [dst], #8 - st1 {v28.8b}, [x10], #8 - st1 {v27.8b}, [dst], x14 - st1 {v29.8b}, [x10], x14 + st1 {v26.8b, v27.8b}, [dst], x14 + st1 {v28.8b, v29.8b}, [x10], x14 .endif b.gt 1b // double line - subs width, width, #16 - // reset src - msub src, srcstride, height, src - msub x12, srcstride, height, x12 - // reset dst - msub dst, dststride, height, dst - msub x10, dststride, height, x10 + ret mx +endfunc + +function ff_hevc_put_hevc_\type\()_h32_8_neon, export=1 + load_filter mx + sxtw height, heightw + mov mx, x30 .ifc \type, qpel_bi - // reset xsrc - sub x4, x4, x17 - sub x15, x15, x17 - add x4, x4, #32 - add x15, x15, #32 + ldrh w8, [sp] // width + mov x16, #(MAX_PB_SIZE << 2) // src2bstridel + lsl x17, x5, #7 // src2b reset + add x15, x4, #(MAX_PB_SIZE << 1) // src2b + sub x16, x16, width, uxtw #1 +.endif + sub src, src, #3 + mov mx, x30 +.ifc \type, qpel + mov dststride, #(MAX_PB_SIZE << 1) + lsl x13, srcstride, #1 // srcstridel + mov x14, #(MAX_PB_SIZE << 2) + sub x14, x14, width, uxtw #1 +.else + lsl x14, dststride, #1 // dststridel + lsl x13, srcstride, #1 // srcstridel + sub x14, x14, width, uxtw .endif - add src, src, #16 - add x12, x12, #16 + sub x13, x13, width, uxtw + sub x13, x13, #8 + add x10, dst, dststride // dstb + add x12, src, srcstride // srcb +0: mov w9, width + ld1 {v16.8b}, [src], #8 + ld1 {v19.8b}, [x12], #8 + uxtl v16.8h, v16.8b + uxtl v19.8h, v19.8b +1: + ld1 {v17.8b-v18.8b}, [src], #16 + ld1 {v20.8b-v21.8b}, [x12], #16 + + bl ff_hevc_put_hevc_h16_8_neon + subs w9, w9, #16 + + mov v16.16b, v18.16b + mov v19.16b, v21.16b .ifc \type, qpel - add dst, dst, #32 - add x10, x10, #32 + st1 {v26.8h, v27.8h}, [dst], #32 + st1 {v28.8h, v29.8h}, [x10], #32 +.else +.ifc \type, qpel_bi + ld1 {v20.8h, v21.8h}, [ x4], #32 + ld1 {v22.8h, v23.8h}, [x15], #32 + sqadd v26.8h, v26.8h, v20.8h + sqadd v27.8h, v27.8h, v21.8h + sqadd v28.8h, v28.8h, v22.8h + sqadd v29.8h, v29.8h, v23.8h + sqrshrun v26.8b, v26.8h, #7 + sqrshrun v27.8b, v27.8h, #7 + sqrshrun v28.8b, v28.8h, #7 + sqrshrun v29.8b, v29.8h, #7 .else - add dst, dst, #16 - add x10, x10, #16 + sqrshrun v26.8b, v26.8h, #6 + sqrshrun v27.8b, v27.8h, #6 + sqrshrun v28.8b, v28.8h, #6 + sqrshrun v29.8b, v29.8h, #6 +.endif + st1 {v26.8b, v27.8b}, [dst], #16 + st1 {v28.8b, v29.8b}, [x10], #16 +.endif + b.gt 1b // double line + subs height, height, #2 + add src, src, x13 + add x12, x12, x13 + add dst, dst, x14 + add x10, x10, x14 +.ifc \type, qpel_bi + add x4, x4, x16 + add x15, x15, x16 .endif b.gt 0b ret mx @@ -482,3 +647,4305 @@ endfunc put_hevc qpel put_hevc qpel_uni put_hevc qpel_bi + +function ff_hevc_put_hevc_qpel_v4_8_neon, export=1 + load_qpel_filterb x5, x4 + sub x1, x1, x2, lsl #1 + mov x9, #(MAX_PB_SIZE * 2) + sub x1, x1, x2 + ldr s16, [x1] + ldr s17, [x1, x2] + add x1, x1, x2, lsl #1 + ldr s18, [x1] + ldr s19, [x1, x2] + add x1, x1, x2, lsl #1 + ldr s20, [x1] + ldr s21, [x1, x2] + add x1, x1, x2, lsl #1 + ldr s22, [x1] + add x1, x1, x2 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().s}[0], [x1], x2 + movi v24.8h, #0 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + st1 {v24.4h}, [x0], x9 + subs w3, w3, #1 + b.eq 2f +.endm +1: calc_all +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_qpel_v6_8_neon, export=1 + load_qpel_filterb x5, x4 + sub x1, x1, x2, lsl #1 + mov x9, #(MAX_PB_SIZE * 2 - 8) + sub x1, x1, x2 + ldr d16, [x1] + ldr d17, [x1, x2] + add x1, x1, x2, lsl #1 + ldr d18, [x1] + ldr d19, [x1, x2] + add x1, x1, x2, lsl #1 + ldr d20, [x1] + ldr d21, [x1, x2] + add x1, x1, x2, lsl #1 + ldr d22, [x1] + add x1, x1, x2 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().8b}, [x1], x2 + movi v24.8h, #0 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + st1 {v24.4h}, [x0], #8 + st1 {v24.s}[2], [x0], x9 + subs w3, w3, #1 +.endm +1: calc_all +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_qpel_v8_8_neon, export=1 + load_qpel_filterb x5, x4 + sub x1, x1, x2, lsl #1 + mov x9, #(MAX_PB_SIZE * 2) + sub x1, x1, x2 + ldr d16, [x1] + ldr d17, [x1, x2] + add x1, x1, x2, lsl #1 + ldr d18, [x1] + ldr d19, [x1, x2] + add x1, x1, x2, lsl #1 + ldr d20, [x1] + ldr d21, [x1, x2] + add x1, x1, x2, lsl #1 + ldr d22, [x1] + add x1, x1, x2 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().8b}, [x1], x2 + movi v24.8h, #0 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + st1 {v24.8h}, [x0], x9 + subs w3, w3, #1 +.endm +1: calc_all +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_qpel_v12_8_neon, export=1 + load_qpel_filterb x5, x4 + sub x1, x1, x2, lsl #1 + mov x9, #(MAX_PB_SIZE * 2 - 16) + sub x1, x1, x2 + ldr q16, [x1] + ldr q17, [x1, x2] + add x1, x1, x2, lsl #1 + ldr q18, [x1] + ldr q19, [x1, x2] + add x1, x1, x2, lsl #1 + ldr q20, [x1] + ldr q21, [x1, x2] + add x1, x1, x2, lsl #1 + ldr q22, [x1] + add x1, x1, x2 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().16b}, [x1], x2 + movi v24.8h, #0 + movi v25.8h, #0 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb2 v25, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + st1 {v24.8h}, [x0], #16 + subs w3, w3, #1 + st1 {v25.4h}, [x0], x9 +.endm +1: calc_all +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_qpel_v16_8_neon, export=1 + load_qpel_filterb x5, x4 + sub x1, x1, x2, lsl #1 + mov x9, #(MAX_PB_SIZE * 2) + sub x1, x1, x2 + ldr q16, [x1] + ldr q17, [x1, x2] + add x1, x1, x2, lsl #1 + ldr q18, [x1] + ldr q19, [x1, x2] + add x1, x1, x2, lsl #1 + ldr q20, [x1] + ldr q21, [x1, x2] + add x1, x1, x2, lsl #1 + ldr q22, [x1] + add x1, x1, x2 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().16b}, [x1], x2 + movi v24.8h, #0 + movi v25.8h, #0 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb2 v25, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + subs w3, w3, #1 + st1 {v24.8h, v25.8h}, [x0], x9 +.endm +1: calc_all +.purgem calc +2: ret +endfunc + +// todo: reads #32 bytes +function ff_hevc_put_hevc_qpel_v24_8_neon, export=1 + sub sp, sp, #32 + st1 {v8.8b, v9.8b, v10.8b}, [sp] + load_qpel_filterb x5, x4 + sub x1, x1, x2, lsl #1 + sub x1, x1, x2 + mov x9, #(MAX_PB_SIZE * 2) + ld1 {v16.16b, v17.16b}, [x1], x2 + ld1 {v18.16b, v19.16b}, [x1], x2 + ld1 {v20.16b, v21.16b}, [x1], x2 + ld1 {v22.16b, v23.16b}, [x1], x2 + ld1 {v24.16b, v25.16b}, [x1], x2 + ld1 {v26.16b, v27.16b}, [x1], x2 + ld1 {v28.16b, v29.16b}, [x1], x2 +.macro calc tmp0, tmp1, src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15 + ld1 {\tmp0\().16b, \tmp1\().16b}, [x1], x2 + movi v8.8h, #0 + movi v9.8h, #0 + movi v10.8h, #0 + calc_qpelb v8, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb2 v9, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb v10, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15 + subs w3, w3, #1 + st1 {v8.8h, v9.8h, v10.8h}, [x0], x9 +.endm +1: calc_all2 +.purgem calc +2: ld1 {v8.8b, v9.8b, v10.8b}, [sp] + add sp, sp, #32 + ret +endfunc + +function ff_hevc_put_hevc_qpel_v32_8_neon, export=1 + sub sp, sp, #32 + st1 {v8.8b-v11.8b}, [sp] + load_qpel_filterb x5, x4 + sub x1, x1, x2, lsl #1 + mov x9, #(MAX_PB_SIZE * 2) + sub x1, x1, x2 + ld1 {v16.16b, v17.16b}, [x1], x2 + ld1 {v18.16b, v19.16b}, [x1], x2 + ld1 {v20.16b, v21.16b}, [x1], x2 + ld1 {v22.16b, v23.16b}, [x1], x2 + ld1 {v24.16b, v25.16b}, [x1], x2 + ld1 {v26.16b, v27.16b}, [x1], x2 + ld1 {v28.16b, v29.16b}, [x1], x2 +.macro calc tmp0, tmp1, src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15 + ld1 {\tmp0\().16b, \tmp1\().16b}, [x1], x2 + movi v8.8h, #0 + movi v9.8h, #0 + movi v10.8h, #0 + movi v11.8h, #0 + calc_qpelb v8, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb2 v9, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb v10, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15 + calc_qpelb2 v11, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15 + subs w3, w3, #1 + st1 {v8.8h-v11.8h}, [x0], x9 +.endm +1: calc_all2 +.purgem calc +2: ld1 {v8.8b-v11.8b}, [sp], #32 + ret +endfunc + +function ff_hevc_put_hevc_qpel_v48_8_neon, export=1 + stp x2, x3, [sp, #-48]! + stp x0, x1, [sp, #16] + stp x5, x30, [sp, #32] + bl X(ff_hevc_put_hevc_qpel_v24_8_neon) + ldr x5, [sp, #32] + ldp x0, x1, [sp, #16] + ldp x2, x3, [sp], #32 + add x0, x0, #48 + add x1, x1, #24 + bl X(ff_hevc_put_hevc_qpel_v24_8_neon) + ldr x30, [sp, #8] + add sp, sp, #16 + ret +endfunc + +function ff_hevc_put_hevc_qpel_v64_8_neon, export=1 + sub sp, sp, #32 + st1 {v8.8b-v11.8b}, [sp] + load_qpel_filterb x5, x4 + sub x1, x1, x2, lsl #1 + sub x1, x1, x2 + mov x9, #(MAX_PB_SIZE * 2) +0: mov x8, x1 // src + ld1 {v16.16b, v17.16b}, [x8], x2 + mov w11, w3 // height + ld1 {v18.16b, v19.16b}, [x8], x2 + mov x10, x0 // dst + ld1 {v20.16b, v21.16b}, [x8], x2 + ld1 {v22.16b, v23.16b}, [x8], x2 + ld1 {v24.16b, v25.16b}, [x8], x2 + ld1 {v26.16b, v27.16b}, [x8], x2 + ld1 {v28.16b, v29.16b}, [x8], x2 +.macro calc tmp0, tmp1, src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15 + ld1 {\tmp0\().16b, \tmp1\().16b}, [x8], x2 + movi v8.8h, #0 + movi v9.8h, #0 + movi v10.8h, #0 + movi v11.8h, #0 + calc_qpelb v8, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb2 v9, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb v10, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15 + calc_qpelb2 v11, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15 + subs x11, x11, #1 + st1 {v8.8h-v11.8h}, [x10], x9 +.endm +1: calc_all2 +.purgem calc +2: add x0, x0, #64 + add x1, x1, #32 + subs w6, w6, #32 + b.hi 0b + ld1 {v8.8b-v11.8b}, [sp], #32 + ret +endfunc + +function ff_hevc_put_hevc_qpel_bi_v4_8_neon, export=1 + load_qpel_filterb x7, x6 + sub x2, x2, x3, lsl #1 + sub x2, x2, x3 + mov x12, #(MAX_PB_SIZE * 2) + ld1 {v16.s}[0], [x2], x3 + ld1 {v17.s}[0], [x2], x3 + ld1 {v18.s}[0], [x2], x3 + ld1 {v19.s}[0], [x2], x3 + ld1 {v20.s}[0], [x2], x3 + ld1 {v21.s}[0], [x2], x3 + ld1 {v22.s}[0], [x2], x3 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().s}[0], [x2], x3 + movi v24.8h, #0 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + ld1 {v25.4h}, [x4], x12 // src2 + sqadd v24.8h, v24.8h, v25.8h + sqrshrun v25.8b, v24.8h, #7 + subs w5, w5, #1 + st1 {v25.s}[0], [x0], x1 +.endm +1: calc_all +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_qpel_bi_v6_8_neon, export=1 + load_qpel_filterb x7, x6 + sub x2, x2, x3, lsl #1 + sub x2, x2, x3 + ld1 {v16.8b}, [x2], x3 + sub x1, x1, #4 + ld1 {v17.8b}, [x2], x3 + mov x12, #(MAX_PB_SIZE * 2) + ld1 {v18.8b}, [x2], x3 + ld1 {v19.8b}, [x2], x3 + ld1 {v20.8b}, [x2], x3 + ld1 {v21.8b}, [x2], x3 + ld1 {v22.8b}, [x2], x3 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().8b}, [x2], x3 + movi v24.8h, #0 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + ld1 {v25.8h}, [x4], x12 // src2 + sqadd v24.8h, v24.8h, v25.8h + sqrshrun v25.8b, v24.8h, #7 + st1 {v25.s}[0], [x0], #4 + subs w5, w5, #1 + st1 {v25.h}[2], [x0], x1 +.endm +1: calc_all +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_qpel_bi_v8_8_neon, export=1 + load_qpel_filterb x7, x6 + sub x2, x2, x3, lsl #1 + sub x2, x2, x3 + mov x12, #(MAX_PB_SIZE * 2) + ld1 {v16.8b}, [x2], x3 + ld1 {v17.8b}, [x2], x3 + ld1 {v18.8b}, [x2], x3 + ld1 {v19.8b}, [x2], x3 + ld1 {v20.8b}, [x2], x3 + ld1 {v21.8b}, [x2], x3 + ld1 {v22.8b}, [x2], x3 + .macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().8b}, [x2], x3 + movi v24.8h, #0 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + ld1 {v25.8h}, [x4], x12 // src2 + sqadd v24.8h, v24.8h, v25.8h + sqrshrun v25.8b, v24.8h, #7 + subs w5, w5, #1 + st1 {v25.8b}, [x0], x1 +.endm +1: calc_all +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_qpel_bi_v12_8_neon, export=1 + load_qpel_filterb x7, x6 + sub x2, x2, x3, lsl #1 + sub x2, x2, x3 + sub x1, x1, #8 + ld1 {v16.16b}, [x2], x3 + mov x12, #(MAX_PB_SIZE * 2) + ld1 {v17.16b}, [x2], x3 + ld1 {v18.16b}, [x2], x3 + ld1 {v19.16b}, [x2], x3 + ld1 {v20.16b}, [x2], x3 + ld1 {v21.16b}, [x2], x3 + ld1 {v22.16b}, [x2], x3 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().16b}, [x2], x3 + movi v24.8h, #0 + movi v25.8h, #0 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb2 v25, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + ld1 {v26.8h, v27.8h}, [x4], x12 // src2 + sqadd v24.8h, v24.8h, v26.8h + sqadd v25.8h, v25.8h, v27.8h + sqrshrun v26.8b, v24.8h, #7 + sqrshrun2 v26.16b, v25.8h, #7 + st1 {v26.8b}, [x0], #8 + subs w5, w5, #1 + st1 {v26.s}[2], [x0], x1 +.endm +1: calc_all +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_qpel_bi_v16_8_neon, export=1 + load_qpel_filterb x7, x6 + sub x2, x2, x3, lsl #1 + sub x2, x2, x3 + mov x12, #(MAX_PB_SIZE * 2) + ld1 {v16.16b}, [x2], x3 + ld1 {v17.16b}, [x2], x3 + ld1 {v18.16b}, [x2], x3 + ld1 {v19.16b}, [x2], x3 + ld1 {v20.16b}, [x2], x3 + ld1 {v21.16b}, [x2], x3 + ld1 {v22.16b}, [x2], x3 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().16b}, [x2], x3 + movi v24.8h, #0 + movi v25.8h, #0 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb2 v25, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + ld1 {v26.8h, v27.8h}, [x4], x12 // src2 + sqadd v24.8h, v24.8h, v26.8h + sqadd v25.8h, v25.8h, v27.8h + sqrshrun v26.8b, v24.8h, #7 + subs w5, w5, #1 + sqrshrun2 v26.16b, v25.8h, #7 + st1 {v26.16b}, [x0], x1 +.endm +1: calc_all +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_qpel_bi_v24_8_neon, export=1 + stp x4, x5, [sp, #-64]! + stp x2, x3, [sp, #16] + stp x0, x1, [sp, #32] + stp x7, x30, [sp, #48] + bl X(ff_hevc_put_hevc_qpel_bi_v16_8_neon) + ldp x2, x3, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x7, [sp, #48] + ldp x4, x5, [sp], #48 + add x0, x0, #16 + add x2, x2, #16 + add x4, x4, #32 + bl X(ff_hevc_put_hevc_qpel_bi_v8_8_neon) + ldr x30, [sp, #8] + add sp, sp, #16 + ret +endfunc + +function ff_hevc_put_hevc_qpel_bi_v32_8_neon, export=1 + stp d8, d9, [sp, #-64]! + stp d10, d11, [sp, #16] + stp d12, d13, [sp, #32] + stp d14, d15, [sp, #48] + sub x2, x2, x3, lsl #1 + sub x2, x2, x3 + load_qpel_filterb x7, x6 + ldr w6, [sp, #64] + mov x12, #(MAX_PB_SIZE * 2) +0: mov x8, x2 // src + ld1 {v16.16b, v17.16b}, [x8], x3 + mov w11, w5 // height + ld1 {v18.16b, v19.16b}, [x8], x3 + mov x10, x0 // dst + ld1 {v20.16b, v21.16b}, [x8], x3 + mov x9, x4 // src2 + ld1 {v22.16b, v23.16b}, [x8], x3 + ld1 {v24.16b, v25.16b}, [x8], x3 + ld1 {v26.16b, v27.16b}, [x8], x3 + ld1 {v28.16b, v29.16b}, [x8], x3 +.macro calc tmp0, tmp1, src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15 + ld1 {\tmp0\().8h, \tmp1\().8h}, [x8], x3 + movi v8.8h, #0 + movi v9.8h, #0 + movi v10.8h, #0 + movi v11.8h, #0 + calc_qpelb v8, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb2 v9, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb v10, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15 + calc_qpelb2 v11, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15 + ld1 {v12.8h, v13.8h, v14.8h, v15.8h}, [x9], x12 // src2 + sqadd v8.8h, v8.8h, v12.8h + sqadd v9.8h, v9.8h, v13.8h + sqadd v10.8h, v10.8h, v14.8h + sqadd v11.8h, v11.8h, v15.8h + sqrshrun v12.8b, v8.8h, #7 + sqrshrun2 v12.16b, v9.8h, #7 + sqrshrun v13.8b, v10.8h, #7 + sqrshrun2 v13.16b, v11.8h, #7 + subs x11, x11, #1 + st1 {v12.16b, v13.16b}, [x10], x1 +.endm +1: calc_all2 +.purgem calc +2: add x0, x0, #32 // dst + add x2, x2, #32 // src + add x4, x4, #64 // src2 + subs w6, w6, #32 + b.ne 0b + ldp d10, d11, [sp, #16] + ldp d12, d13, [sp, #32] + ldp d14, d15, [sp, #48] + ldp d8, d9, [sp], #64 + ret +endfunc + +function ff_hevc_put_hevc_qpel_bi_v48_8_neon, export=1 + mov x8, #32 + str x8, [sp, #-80]! + stp x4, x5, [sp, #16] + stp x2, x3, [sp, #32] + stp x0, x1, [sp, #48] + stp x7, x30, [sp, #64] + bl X(ff_hevc_put_hevc_qpel_bi_v32_8_neon) + ldp x4, x5, [sp, #16] + ldp x2, x3, [sp, #32] + ldp x0, x1, [sp, #48] + ldr x7, [sp, #64] + add sp, sp, #64 + add x0, x0, #32 + add x2, x2, #32 + add x4, x4, #64 + bl X(ff_hevc_put_hevc_qpel_bi_v16_8_neon) + ldr x30, [sp, #8] + add sp, sp, #16 + ret +endfunc + +function ff_hevc_put_hevc_qpel_bi_v64_8_neon, export=1 + b X(ff_hevc_put_hevc_qpel_bi_v32_8_neon) +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels4_8_neon, export=1 +1: + ldr s0, [x2] + ldr s1, [x2, x3] + subs w4, w4, #2 + add x2, x2, x3, lsl #1 + str s0, [x0] + str s1, [x0, x1] + add x0, x0, x1, lsl #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels6_8_neon, export=1 + sub x1, x1, #4 +1: + ldr d0, [x2] + ldr d1, [x2, x3] + subs w4, w4, #2 + add x2, x2, x3, lsl #1 + str s0, [x0], #4 + st1 {v0.h}[2], [x0], x1 + str s1, [x0], #4 + st1 {v1.h}[2], [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels8_8_neon, export=1 +1: + ldr d0, [x2] + ldr d1, [x2, x3] + subs w4, w4, #2 + add x2, x2, x3, lsl #1 + str d0, [x0] + str d1, [x0, x1] + add x0, x0, x1, lsl #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels12_8_neon, export=1 + sub x1, x1, #8 +1: + ldr q0, [x2] + ldr q1, [x2, x3] + subs w4, w4, #2 + add x2, x2, x3, lsl #1 + str d0, [x0], #8 + st1 {v0.s}[2], [x0], x1 + str d1, [x0], #8 + st1 {v1.s}[2], [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels16_8_neon, export=1 +1: + ldr q0, [x2] + ldr q1, [x2, x3] + subs w4, w4, #2 + add x2, x2, x3, lsl #1 + str q0, [x0] + str q1, [x0, x1] + add x0, x0, x1, lsl #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels24_8_neon, export=1 +1: + ld1 {v0.8b, v1.8b, v2.8b}, [x2], x3 + subs w4, w4, #1 + st1 {v0.8b, v1.8b, v2.8b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels32_8_neon, export=1 +1: + ld1 {v0.16b, v1.16b}, [x2], x3 + subs w4, w4, #1 + st1 {v0.16b, v1.16b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels48_8_neon, export=1 +1: + ld1 {v0.16b, v1.16b, v2.16b}, [x2], x3 + subs w4, w4, #1 + st1 {v0.16b, v1.16b, v2.16b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_pixels64_8_neon, export=1 +1: + ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x2], x3 + subs w4, w4, #1 + st1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_v4_8_neon, export=1 + load_qpel_filterb x6, x5 + sub x2, x2, x3, lsl #1 + sub x2, x2, x3 + ldr s16, [x2] + ldr s17, [x2, x3] + add x2, x2, x3, lsl #1 + ldr s18, [x2] + ldr s19, [x2, x3] + add x2, x2, x3, lsl #1 + ldr s20, [x2] + ldr s21, [x2, x3] + add x2, x2, x3, lsl #1 + ldr s22, [x2] + add x2, x2, x3 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().s}[0], [x2], x3 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + sqrshrun v24.8b, v24.8h, #6 + subs w4, w4, #1 + st1 {v24.s}[0], [x0], x1 +.endm +1: calc_all +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_v6_8_neon, export=1 + load_qpel_filterb x6, x5 + sub x2, x2, x3, lsl #1 + sub x1, x1, #4 + sub x2, x2, x3 + ldr d16, [x2] + ldr d17, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d18, [x2] + ldr d19, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d20, [x2] + ldr d21, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d22, [x2] + add x2, x2, x3 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().8b}, [x2], x3 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + sqrshrun v24.8b, v24.8h, #6 + st1 {v24.s}[0], [x0], #4 + subs w4, w4, #1 + st1 {v24.h}[2], [x0], x1 +.endm +1: calc_all +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_v8_8_neon, export=1 + load_qpel_filterb x6, x5 + sub x2, x2, x3, lsl #1 + sub x2, x2, x3 + ldr d16, [x2] + ldr d17, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d18, [x2] + ldr d19, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d20, [x2] + ldr d21, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d22, [x2] + add x2, x2, x3 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().8b}, [x2], x3 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + sqrshrun v24.8b, v24.8h, #6 + subs w4, w4, #1 + st1 {v24.8b}, [x0], x1 +.endm +1: calc_all +.purgem calc +2: ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_v12_8_neon, export=1 + load_qpel_filterb x6, x5 + sub x2, x2, x3, lsl #1 + sub x1, x1, #8 + sub x2, x2, x3 +0: mov x8, x2 // src + mov w11, w4 // height + mov x10, x0 // dst + ldr q16, [x8] + ldr q17, [x8, x3] + add x8, x8, x3, lsl #1 + ldr q18, [x8] + ldr q19, [x8, x3] + add x8, x8, x3, lsl #1 + ldr q20, [x8] + ldr q21, [x8, x3] + add x8, x8, x3, lsl #1 + ldr q22, [x8] + add x8, x8, x3 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().16b}, [x8], x3 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb2 v25, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + sqrshrun v24.8b, v24.8h, #6 + sqrshrun2 v24.16b, v25.8h, #6 + st1 {v24.8b}, [x10], #8 + subs x11, x11, #1 + st1 {v24.s}[2], [x10], x1 +.endm +1: calc_all +.purgem calc +2: add x0, x0, #12 + add x2, x2, #12 + subs w7, w7, #12 + b.ne 0b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_v16_8_neon, export=1 + load_qpel_filterb x6, x5 + sub x2, x2, x3, lsl #1 + sub x2, x2, x3 +0: mov x8, x2 // src + mov w11, w4 // height + mov x10, x0 // dst + ldr q16, [x8] + ldr q17, [x8, x3] + add x8, x8, x3, lsl #1 + ldr q18, [x8] + ldr q19, [x8, x3] + add x8, x8, x3, lsl #1 + ldr q20, [x8] + ldr q21, [x8, x3] + add x8, x8, x3, lsl #1 + ldr q22, [x8] + add x8, x8, x3 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().16b}, [x8], x3 + calc_qpelb v24, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + calc_qpelb2 v25, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7 + sqrshrun v24.8b, v24.8h, #6 + sqrshrun2 v24.16b, v25.8h, #6 + subs x11, x11, #1 + st1 {v24.16b}, [x10], x1 +.endm +1: calc_all +.purgem calc +2: add x0, x0, #16 + add x2, x2, #16 + subs w7, w7, #16 + b.ne 0b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_v24_8_neon, export=1 + b X(ff_hevc_put_hevc_qpel_uni_v12_8_neon) +endfunc + +function ff_hevc_put_hevc_qpel_uni_v32_8_neon, export=1 + b X(ff_hevc_put_hevc_qpel_uni_v16_8_neon) +endfunc + +function ff_hevc_put_hevc_qpel_uni_v48_8_neon, export=1 + b X(ff_hevc_put_hevc_qpel_uni_v16_8_neon) +endfunc + +function ff_hevc_put_hevc_qpel_uni_v64_8_neon, export=1 + b X(ff_hevc_put_hevc_qpel_uni_v16_8_neon) +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels4_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ldr s0, [x2] + ldr s1, [x2, x3] + add x2, x2, x3, lsl #1 + ushll v0.8h, v0.8b, #6 + ushll v1.8h, v1.8b, #6 + smull v0.4s, v0.4h, v30.4h + smull v1.4s, v1.4h, v30.4h + sqrshl v0.4s, v0.4s, v31.4s + sqrshl v1.4s, v1.4s, v31.4s + sqadd v0.4s, v0.4s, v29.4s + sqadd v1.4s, v1.4s, v29.4s + sqxtn v0.4h, v0.4s + sqxtn v1.4h, v1.4s + sqxtun v0.8b, v0.8h + sqxtun v1.8b, v1.8h + str s0, [x0] + str s1, [x0, x1] + add x0, x0, x1, lsl #1 + subs w4, w4, #2 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels6_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 + sub x1, x1, #4 +1: + ldr d0, [x2] + ldr d1, [x2, x3] + add x2, x2, x3, lsl #1 + ushll v0.8h, v0.8b, #6 + ushll v1.8h, v1.8b, #6 + smull v4.4s, v0.4h, v30.4h + smull2 v5.4s, v0.8h, v30.8h + smull v6.4s, v1.4h, v30.4h + smull2 v7.4s, v1.8h, v30.8h + sqrshl v4.4s, v4.4s, v31.4s + sqrshl v5.4s, v5.4s, v31.4s + sqrshl v6.4s, v6.4s, v31.4s + sqrshl v7.4s, v7.4s, v31.4s + sqadd v4.4s, v4.4s, v29.4s + sqadd v5.4s, v5.4s, v29.4s + sqadd v6.4s, v6.4s, v29.4s + sqadd v7.4s, v7.4s, v29.4s + sqxtn v0.4h, v4.4s + sqxtn2 v0.8h, v5.4s + sqxtn v1.4h, v6.4s + sqxtn2 v1.8h, v7.4s + sqxtun v0.8b, v0.8h + sqxtun v1.8b, v1.8h + str s0, [x0], #4 + st1 {v0.h}[2], [x0], x1 + str s1, [x0], #4 + st1 {v1.h}[2], [x0], x1 + subs w4, w4, #2 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels8_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ldr d0, [x2] + ldr d1, [x2, x3] + add x2, x2, x3, lsl #1 + ushll v0.8h, v0.8b, #6 + ushll v1.8h, v1.8b, #6 + smull v4.4s, v0.4h, v30.4h + smull2 v5.4s, v0.8h, v30.8h + smull v6.4s, v1.4h, v30.4h + smull2 v7.4s, v1.8h, v30.8h + sqrshl v4.4s, v4.4s, v31.4s + sqrshl v5.4s, v5.4s, v31.4s + sqrshl v6.4s, v6.4s, v31.4s + sqrshl v7.4s, v7.4s, v31.4s + sqadd v4.4s, v4.4s, v29.4s + sqadd v5.4s, v5.4s, v29.4s + sqadd v6.4s, v6.4s, v29.4s + sqadd v7.4s, v7.4s, v29.4s + sqxtn v0.4h, v4.4s + sqxtn2 v0.8h, v5.4s + sqxtn v1.4h, v6.4s + sqxtn2 v1.8h, v7.4s + sqxtun v0.8b, v0.8h + sqxtun v1.8b, v1.8h + str d0, [x0] + str d1, [x0, x1] + add x0, x0, x1, lsl #1 + subs w4, w4, #2 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels12_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 + sub x1, x1, #8 +1: + ldr q0, [x2] + ldr q1, [x2, x3] + add x2, x2, x3, lsl #1 + ushll v4.8h, v0.8b, #6 + ushll2 v5.8h, v0.16b, #6 + ushll v6.8h, v1.8b, #6 + ushll2 v7.8h, v1.16b, #6 + smull v16.4s, v4.4h, v30.4h + smull2 v17.4s, v4.8h, v30.8h + smull v18.4s, v5.4h, v30.4h + smull2 v19.4s, v5.8h, v30.8h + smull v20.4s, v6.4h, v30.4h + smull2 v21.4s, v6.8h, v30.8h + smull v22.4s, v7.4h, v30.4h + smull2 v23.4s, v7.8h, v30.8h + + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v17.4s, v17.4s, v31.4s + sqrshl v18.4s, v18.4s, v31.4s + sqrshl v19.4s, v19.4s, v31.4s + sqrshl v20.4s, v20.4s, v31.4s + sqrshl v21.4s, v21.4s, v31.4s + sqrshl v22.4s, v22.4s, v31.4s + sqrshl v23.4s, v23.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqadd v17.4s, v17.4s, v29.4s + sqadd v18.4s, v18.4s, v29.4s + sqadd v19.4s, v19.4s, v29.4s + sqadd v20.4s, v20.4s, v29.4s + sqadd v21.4s, v21.4s, v29.4s + sqadd v22.4s, v22.4s, v29.4s + sqadd v23.4s, v23.4s, v29.4s + sqxtn v0.4h, v16.4s + sqxtn2 v0.8h, v17.4s + sqxtn v1.4h, v18.4s + sqxtn2 v1.8h, v19.4s + sqxtn v2.4h, v20.4s + sqxtn2 v2.8h, v21.4s + sqxtn v3.4h, v22.4s + sqxtn2 v3.8h, v23.4s + sqxtun v0.8b, v0.8h + sqxtun2 v0.16b, v1.8h + sqxtun v2.8b, v2.8h + sqxtun2 v2.16b, v3.8h + str d0, [x0], #8 + st1 {v0.s}[2], [x0], x1 + str d2, [x0], #8 + st1 {v2.s}[2], [x0], x1 + subs w4, w4, #2 + b.ne 1b + ret +endfunc + +.macro PEL_UNI_W_PIXEL_CALC s0, t0, t1, d0, d1, d2, d3 + ushll \t0\().8h, \s0\().8b, #6 + ushll2 \t1\().8h, \s0\().16b, #6 + smull \d0\().4s, \t0\().4h, v30.4h + smull2 \d1\().4s, \t0\().8h, v30.8h + smull \d2\().4s, \t1\().4h, v30.4h + smull2 \d3\().4s, \t1\().8h, v30.8h + sqrshl \d0\().4s, \d0\().4s, v31.4s + sqrshl \d1\().4s, \d1\().4s, v31.4s + sqrshl \d2\().4s, \d2\().4s, v31.4s + sqrshl \d3\().4s, \d3\().4s, v31.4s + sqadd \d0\().4s, \d0\().4s, v29.4s + sqadd \d1\().4s, \d1\().4s, v29.4s + sqadd \d2\().4s, \d2\().4s, v29.4s + sqadd \d3\().4s, \d3\().4s, v29.4s + sqxtn \t0\().4h, \d0\().4s + sqxtn2 \t0\().8h, \d1\().4s + sqxtn \t1\().4h, \d2\().4s + sqxtn2 \t1\().8h, \d3\().4s + sqxtun \s0\().8b, \t0\().8h + sqxtun2 \s0\().16b, \t1\().8h +.endm + + +function ff_hevc_put_hevc_pel_uni_w_pixels16_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ldr q0, [x2] + ldr q1, [x2, x3] + add x2, x2, x3, lsl #1 + PEL_UNI_W_PIXEL_CALC v0, v4, v5, v16, v17, v18, v19 + PEL_UNI_W_PIXEL_CALC v1, v6, v7, v20, v21, v22, v23 + str q0, [x0] + str q1, [x0, x1] + add x0, x0, x1, lsl #1 + subs w4, w4, #2 + b.ne 1b + ret +endfunc + + + +function ff_hevc_put_hevc_pel_uni_w_pixels24_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ld1 {v0.16b, v1.16b}, [x2], x3 + ushll v4.8h, v0.8b, #6 + ushll2 v5.8h, v0.16b, #6 + ushll v6.8h, v1.8b, #6 + smull v16.4s, v4.4h, v30.4h + smull2 v17.4s, v4.8h, v30.8h + smull v18.4s, v5.4h, v30.4h + smull2 v19.4s, v5.8h, v30.8h + smull v20.4s, v6.4h, v30.4h + smull2 v21.4s, v6.8h, v30.8h + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v17.4s, v17.4s, v31.4s + sqrshl v18.4s, v18.4s, v31.4s + sqrshl v19.4s, v19.4s, v31.4s + sqrshl v20.4s, v20.4s, v31.4s + sqrshl v21.4s, v21.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqadd v17.4s, v17.4s, v29.4s + sqadd v18.4s, v18.4s, v29.4s + sqadd v19.4s, v19.4s, v29.4s + sqadd v20.4s, v20.4s, v29.4s + sqadd v21.4s, v21.4s, v29.4s + sqxtn v0.4h, v16.4s + sqxtn2 v0.8h, v17.4s + sqxtn v1.4h, v18.4s + sqxtn2 v1.8h, v19.4s + sqxtn v2.4h, v20.4s + sqxtn2 v2.8h, v21.4s + sqxtun v0.8b, v0.8h + sqxtun v1.8b, v1.8h + sqxtun v2.8b, v2.8h + st1 {v0.8b, v1.8b, v2.8b}, [x0], x1 + subs w4, w4, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels32_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ld1 {v0.16b, v1.16b}, [x2], x3 + PEL_UNI_W_PIXEL_CALC v0, v4, v5, v16, v17, v18, v19 + PEL_UNI_W_PIXEL_CALC v1, v6, v7, v20, v21, v22, v23 + st1 {v0.16b, v1.16b}, [x0], x1 + subs w4, w4, #1 + b.ne 1b + ret +endfunc + + +function ff_hevc_put_hevc_pel_uni_w_pixels48_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ld1 {v0.16b, v1.16b, v2.16b}, [x2], x3 + PEL_UNI_W_PIXEL_CALC v0, v4, v5, v16, v17, v18, v19 + PEL_UNI_W_PIXEL_CALC v1, v6, v7, v20, v21, v22, v23 + PEL_UNI_W_PIXEL_CALC v2, v4, v5, v16, v17, v18, v19 + st1 {v0.16b, v1.16b, v2.16b}, [x0], x1 + subs w4, w4, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels64_8_neon, export=1 + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 + dup v31.4s, w10 + dup v29.4s, w7 +1: + ld1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x2], x3 + PEL_UNI_W_PIXEL_CALC v0, v4, v5, v16, v17, v18, v19 + PEL_UNI_W_PIXEL_CALC v1, v6, v7, v20, v21, v22, v23 + PEL_UNI_W_PIXEL_CALC v2, v4, v5, v16, v17, v18, v19 + PEL_UNI_W_PIXEL_CALC v3, v6, v7, v20, v21, v22, v23 + st1 {v0.16b, v1.16b, v2.16b, v3.16b}, [x0], x1 + subs w4, w4, #1 + b.ne 1b + ret +endfunc + +.macro QPEL_UNI_W_V_HEADER + ldur x12, [sp, #8] // my + sub x2, x2, x3, lsl #1 + sub x2, x2, x3 + movrel x9, qpel_filters_abs + add x9, x9, x12, lsl #3 + ldr d28, [x9] + dup v0.16b, v28.b[0] + dup v1.16b, v28.b[1] + dup v2.16b, v28.b[2] + dup v3.16b, v28.b[3] + dup v4.16b, v28.b[4] + dup v5.16b, v28.b[5] + dup v6.16b, v28.b[6] + dup v7.16b, v28.b[7] + + mov w10, #-6 + sub w10, w10, w5 + dup v30.8h, w6 // wx + dup v31.4s, w10 // shift + dup v29.4s, w7 // ox +.endm + +.macro QPEL_FILTER_B dst, src0, src1, src2, src3, src4, src5, src6, src7 + umull \dst\().8h, \src1\().8b, v1.8b + umlsl \dst\().8h, \src0\().8b, v0.8b + umlsl \dst\().8h, \src2\().8b, v2.8b + umlal \dst\().8h, \src3\().8b, v3.8b + umlal \dst\().8h, \src4\().8b, v4.8b + umlsl \dst\().8h, \src5\().8b, v5.8b + umlal \dst\().8h, \src6\().8b, v6.8b + umlsl \dst\().8h, \src7\().8b, v7.8b +.endm + +.macro QPEL_FILTER_B2 dst, src0, src1, src2, src3, src4, src5, src6, src7 + umull2 \dst\().8h, \src1\().16b, v1.16b + umlsl2 \dst\().8h, \src0\().16b, v0.16b + umlsl2 \dst\().8h, \src2\().16b, v2.16b + umlal2 \dst\().8h, \src3\().16b, v3.16b + umlal2 \dst\().8h, \src4\().16b, v4.16b + umlsl2 \dst\().8h, \src5\().16b, v5.16b + umlal2 \dst\().8h, \src6\().16b, v6.16b + umlsl2 \dst\().8h, \src7\().16b, v7.16b +.endm + +.macro QPEL_UNI_W_V_4 + smull v24.4s, v24.4h, v30.4h + sqrshl v24.4s, v24.4s, v31.4s + sqadd v24.4s, v24.4s, v29.4s + sqxtn v24.4h, v24.4s + sqxtun v24.8b, v24.8h + st1 {v24.s}[0], [x0], x1 +.endm + +function ff_hevc_put_hevc_qpel_uni_w_v4_8_neon, export=1 + QPEL_UNI_W_V_HEADER + ldr s16, [x2] + ldr s17, [x2, x3] + add x2, x2, x3, lsl #1 + ldr s18, [x2] + ldr s19, [x2, x3] + add x2, x2, x3, lsl #1 + ldr s20, [x2] + ldr s21, [x2, x3] + add x2, x2, x3, lsl #1 + ldr s22, [x2] + +1: ldr s23, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v24, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s16, [x2] + QPEL_FILTER_B v24, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s17, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v24, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s18, [x2] + QPEL_FILTER_B v24, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s19, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v24, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s20, [x2] + QPEL_FILTER_B v24, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s21, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v24, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.eq 2f + + ldr s22, [x2] + QPEL_FILTER_B v24, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_UNI_W_V_4 + subs w4, w4, #1 + b.ne 1b +2: + ret +endfunc + +.macro QPEL_UNI_W_V_8 + smull v24.4s, v26.4h, v30.4h + smull2 v25.4s, v26.8h, v30.8h + sqrshl v24.4s, v24.4s, v31.4s + sqrshl v25.4s, v25.4s, v31.4s + sqadd v24.4s, v24.4s, v29.4s + sqadd v25.4s, v25.4s, v29.4s + sqxtn v24.4h, v24.4s + sqxtn2 v24.8h, v25.4s + sqxtun v24.8b, v24.8h + st1 {v24.d}[0], [x0], x1 +.endm + +function ff_hevc_put_hevc_qpel_uni_w_v8_8_neon, export=1 + QPEL_UNI_W_V_HEADER + ldr d16, [x2] + ldr d17, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d18, [x2] + ldr d19, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d20, [x2] + ldr d21, [x2, x3] + add x2, x2, x3, lsl #1 + ldr d22, [x2] + +1: ldr d23, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d16, [x2] + QPEL_FILTER_B v26, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d17, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d18, [x2] + QPEL_FILTER_B v26, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d19, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d20, [x2] + QPEL_FILTER_B v26, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d21, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.eq 2f + + ldr d22, [x2] + QPEL_FILTER_B v26, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_UNI_W_V_8 + subs w4, w4, #1 + b.ne 1b +2: + ret +endfunc + +.macro QPEL_UNI_W_V_16 + smull v24.4s, v26.4h, v30.4h + smull2 v25.4s, v26.8h, v30.8h + smull v26.4s, v27.4h, v30.4h + smull2 v27.4s, v27.8h, v30.8h + sqrshl v24.4s, v24.4s, v31.4s + sqrshl v25.4s, v25.4s, v31.4s + sqrshl v26.4s, v26.4s, v31.4s + sqrshl v27.4s, v27.4s, v31.4s + sqadd v24.4s, v24.4s, v29.4s + sqadd v25.4s, v25.4s, v29.4s + sqadd v26.4s, v26.4s, v29.4s + sqadd v27.4s, v27.4s, v29.4s + sqxtn v24.4h, v24.4s + sqxtn2 v24.8h, v25.4s + sqxtn v26.4h, v26.4s + sqxtn2 v26.8h, v27.4s + sqxtun v24.8b, v24.8h + sqxtun2 v24.16b, v26.8h + st1 {v24.16b}, [x0], x1 +.endm + +function ff_hevc_put_hevc_qpel_uni_w_v16_8_neon, export=1 + QPEL_UNI_W_V_HEADER + ldr q16, [x2] + ldr q17, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q18, [x2] + ldr q19, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q20, [x2] + ldr q21, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q22, [x2] + +1: ldr q23, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_B2 v27, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q16, [x2] + QPEL_FILTER_B v26, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_B2 v27, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q17, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_B2 v27, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q18, [x2] + QPEL_FILTER_B v26, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_B2 v27, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q19, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_B2 v27, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q20, [x2] + QPEL_FILTER_B v26, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_B2 v27, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q21, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_B2 v27, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q22, [x2] + QPEL_FILTER_B v26, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_B2 v27, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.ne 1b +2: + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_v64_8_neon, export=1 + QPEL_UNI_W_V_HEADER + ldur w13, [sp, #16] + mov x14, x0 + mov x15, x2 + mov w11, w4 + +3: + ldr q16, [x2] + ldr q17, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q18, [x2] + ldr q19, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q20, [x2] + ldr q21, [x2, x3] + add x2, x2, x3, lsl #1 + ldr q22, [x2] + + +1: ldr q23, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_B2 v27, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q16, [x2] + QPEL_FILTER_B v26, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_B2 v27, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q17, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_B2 v27, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q18, [x2] + QPEL_FILTER_B v26, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_B2 v27, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q19, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_B2 v27, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q20, [x2] + QPEL_FILTER_B v26, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_B2 v27, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q21, [x2, x3] + add x2, x2, x3, lsl #1 + QPEL_FILTER_B v26, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_B2 v27, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.eq 2f + + ldr q22, [x2] + QPEL_FILTER_B v26, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_B2 v27, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_UNI_W_V_16 + subs w4, w4, #1 + b.ne 1b +2: + subs w13, w13, #16 + add x14, x14, #16 + add x15, x15, #16 + mov x0, x14 + mov x2, x15 + mov w4, w11 + b.hi 3b + ret +endfunc + +function hevc_put_hevc_qpel_uni_hv4_8_end_neon + mov x9, #(MAX_PB_SIZE * 2) + load_qpel_filterh x6, x5 + ldr d16, [sp] + ldr d17, [sp, x9] + add sp, sp, x9, lsl #1 + ldr d18, [sp] + ldr d19, [sp, x9] + add sp, sp, x9, lsl #1 + ldr d20, [sp] + ldr d21, [sp, x9] + add sp, sp, x9, lsl #1 + ldr d22, [sp] + add sp, sp, x9 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().4h}, [sp], x9 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqrshrn, #12 + sqxtun v1.8b, v1.8h + subs w4, w4, #1 + st1 {v1.s}[0], [x0], x1 +.endm +1: calc_all +.purgem calc +2: mov sp, x14 + ret +endfunc + +function hevc_put_hevc_qpel_uni_hv6_8_end_neon + mov x9, #(MAX_PB_SIZE * 2) + load_qpel_filterh x6, x5 + sub x1, x1, #4 + ldr q16, [sp] + ldr q17, [sp, x9] + add sp, sp, x9, lsl #1 + ldr q18, [sp] + ldr q19, [sp, x9] + add sp, sp, x9, lsl #1 + ldr q20, [sp] + ldr q21, [sp, x9] + add sp, sp, x9, lsl #1 + ldr q22, [sp] + add sp, sp, x9 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().8h}, [sp], x9 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqrshrn, #12 + calc_qpelh2 v1, v2, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqrshrn2, #12 + sqxtun v1.8b, v1.8h + st1 {v1.s}[0], [x0], #4 + subs w4, w4, #1 + st1 {v1.h}[2], [x0], x1 +.endm +1: calc_all +.purgem calc +2: mov sp, x14 + ret +endfunc + +function hevc_put_hevc_qpel_uni_hv8_8_end_neon + mov x9, #(MAX_PB_SIZE * 2) + load_qpel_filterh x6, x5 + ldr q16, [sp] + ldr q17, [sp, x9] + add sp, sp, x9, lsl #1 + ldr q18, [sp] + ldr q19, [sp, x9] + add sp, sp, x9, lsl #1 + ldr q20, [sp] + ldr q21, [sp, x9] + add sp, sp, x9, lsl #1 + ldr q22, [sp] + add sp, sp, x9 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().8h}, [sp], x9 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqrshrn, #12 + calc_qpelh2 v1, v2, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqrshrn2, #12 + sqxtun v1.8b, v1.8h + subs w4, w4, #1 + st1 {v1.8b}, [x0], x1 +.endm +1: calc_all +.purgem calc +2: mov sp, x14 + ret +endfunc + +function hevc_put_hevc_qpel_uni_hv12_8_end_neon + mov x9, #(MAX_PB_SIZE * 2) + load_qpel_filterh x6, x5 + sub x1, x1, #8 + ld1 {v16.8h, v17.8h}, [sp], x9 + ld1 {v18.8h, v19.8h}, [sp], x9 + ld1 {v20.8h, v21.8h}, [sp], x9 + ld1 {v22.8h, v23.8h}, [sp], x9 + ld1 {v24.8h, v25.8h}, [sp], x9 + ld1 {v26.8h, v27.8h}, [sp], x9 + ld1 {v28.8h, v29.8h}, [sp], x9 +.macro calc tmp0, tmp1, src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15 + ld1 {\tmp0\().8h, \tmp1\().8h}, [sp], x9 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqrshrn, #12 + calc_qpelh2 v1, v2, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqrshrn2, #12 + calc_qpelh v2, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15, sqrshrn, #12 + sqxtun v1.8b, v1.8h + sqxtun2 v1.16b, v2.8h + st1 {v1.8b}, [x0], #8 + subs w4, w4, #1 + st1 {v1.s}[2], [x0], x1 +.endm +1: calc_all2 +.purgem calc +2: mov sp, x14 + ret +endfunc + +function hevc_put_hevc_qpel_uni_hv16_8_end_neon + mov x9, #(MAX_PB_SIZE * 2) + load_qpel_filterh x6, x5 + sub w12, w9, w7, lsl #1 +0: mov x8, sp // src + ld1 {v16.8h, v17.8h}, [x8], x9 + mov w11, w4 // height + ld1 {v18.8h, v19.8h}, [x8], x9 + mov x10, x0 // dst + ld1 {v20.8h, v21.8h}, [x8], x9 + ld1 {v22.8h, v23.8h}, [x8], x9 + ld1 {v24.8h, v25.8h}, [x8], x9 + ld1 {v26.8h, v27.8h}, [x8], x9 + ld1 {v28.8h, v29.8h}, [x8], x9 +.macro calc tmp0, tmp1, src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15 + ld1 {\tmp0\().8h, \tmp1\().8h}, [x8], x9 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqrshrn, #12 + calc_qpelh2 v1, v2, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqrshrn2, #12 + calc_qpelh v2, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15, sqrshrn, #12 + calc_qpelh2 v2, v3, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15, sqrshrn2, #12 + sqxtun v1.8b, v1.8h + subs x11, x11, #1 + sqxtun2 v1.16b, v2.8h + st1 {v1.16b}, [x10], x1 +.endm +1: calc_all2 +.purgem calc +2: add x0, x0, #16 + add sp, sp, #32 + subs w7, w7, #16 + b.ne 0b + mov sp, x14 + ret +endfunc + +.macro qpel_uni_hv suffix +function ff_hevc_put_hevc_qpel_uni_hv4_8_\suffix, export=1 + add w10, w4, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x30, x14,[sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + sub x1, x2, x3, lsl #1 + sub x1, x1, x3 + add x0, sp, #48 + mov x2, x3 + add x3, x4, #7 + mov x4, x5 + bl X(ff_hevc_put_hevc_qpel_h4_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x30, x14, [sp], #48 + b hevc_put_hevc_qpel_uni_hv4_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_uni_hv6_8_\suffix, export=1 + add w10, w4, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x30, x14,[sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + sub x1, x2, x3, lsl #1 + sub x1, x1, x3 + add x0, sp, #48 + mov x2, x3 + add w3, w4, #7 + mov x4, x5 + bl X(ff_hevc_put_hevc_qpel_h6_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x30, x14, [sp], #48 + b hevc_put_hevc_qpel_uni_hv6_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_uni_hv8_8_\suffix, export=1 + add w10, w4, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x30, x14,[sp, #-48]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + sub x1, x2, x3, lsl #1 + sub x1, x1, x3 + add x0, sp, #48 + mov x2, x3 + add w3, w4, #7 + mov x4, x5 + bl X(ff_hevc_put_hevc_qpel_h8_8_\suffix) + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x30, x14, [sp], #48 + b hevc_put_hevc_qpel_uni_hv8_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_uni_hv12_8_\suffix, export=1 + add w10, w4, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-64]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + str x14, [sp, #48] + sub x1, x2, x3, lsl #1 + sub x1, x1, x3 + mov x2, x3 + add x0, sp, #64 + add w3, w4, #7 + mov x4, x5 + mov w6, #12 + bl X(ff_hevc_put_hevc_qpel_h12_8_\suffix) + ldr x14, [sp, #48] + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x7, x30, [sp], #64 + b hevc_put_hevc_qpel_uni_hv12_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_uni_hv16_8_\suffix, export=1 + add w10, w4, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-64]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + str x14, [sp, #48] + add x0, sp, #64 + sub x1, x2, x3, lsl #1 + sub x1, x1, x3 + mov x2, x3 + add w3, w4, #7 + mov x4, x5 + bl X(ff_hevc_put_hevc_qpel_h16_8_\suffix) + ldr x14, [sp, #48] + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x7, x30, [sp], #64 + b hevc_put_hevc_qpel_uni_hv16_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_uni_hv24_8_\suffix, export=1 + stp x4, x5, [sp, #-64]! + stp x2, x3, [sp, #16] + stp x0, x1, [sp, #32] + stp x6, x30, [sp, #48] + mov x7, #16 + bl X(ff_hevc_put_hevc_qpel_uni_hv16_8_\suffix) + ldp x2, x3, [sp, #16] + add x2, x2, #16 + ldp x0, x1, [sp, #32] + ldp x4, x5, [sp], #48 + mov x7, #8 + add x0, x0, #16 + ldr x6, [sp] + bl X(ff_hevc_put_hevc_qpel_uni_hv8_8_\suffix) + ldr x30, [sp, #8] + add sp, sp, #16 + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_hv32_8_\suffix, export=1 + add w10, w4, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-64]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + str x14, [sp, #48] + sub x1, x2, x3, lsl #1 + add x0, sp, #64 + sub x1, x1, x3 + mov x2, x3 + add w3, w4, #7 + mov x4, x5 + mov w6, #32 + bl X(ff_hevc_put_hevc_qpel_h32_8_\suffix) + ldr x14, [sp, #48] + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x7, x30, [sp], #64 + b hevc_put_hevc_qpel_uni_hv16_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_uni_hv48_8_\suffix, export=1 + add w10, w4, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-64]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + str x14, [sp, #48] + sub x1, x2, x3, lsl #1 + sub x1, x1, x3 + mov x2, x3 + add x0, sp, #64 + add w3, w4, #7 + mov x4, x5 +.ifc \suffix, neon + mov w6, #48 + bl X(ff_hevc_put_hevc_qpel_h32_8_\suffix) +.else + bl X(ff_hevc_put_hevc_qpel_h48_8_\suffix) +.endif + ldr x14, [sp, #48] + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x7, x30, [sp], #64 + b hevc_put_hevc_qpel_uni_hv16_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_uni_hv64_8_\suffix, export=1 + add w10, w4, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-64]! + stp x4, x6, [sp, #16] + stp x0, x1, [sp, #32] + str x14, [sp, #48] + add x0, sp, #64 + sub x1, x2, x3, lsl #1 + mov x2, x3 + sub x1, x1, x3 + add w3, w4, #7 + mov x4, x5 +.ifc \suffix, neon + mov w6, #64 + bl X(ff_hevc_put_hevc_qpel_h32_8_\suffix) +.else + bl X(ff_hevc_put_hevc_qpel_h64_8_\suffix) +.endif + ldr x14, [sp, #48] + ldp x4, x6, [sp, #16] + ldp x0, x1, [sp, #32] + ldp x7, x30, [sp], #64 + b hevc_put_hevc_qpel_uni_hv16_8_end_neon +endfunc +.endm + +qpel_uni_hv neon + +#if HAVE_I8MM +ENABLE_I8MM + +qpel_uni_hv neon_i8mm + +DISABLE_I8MM +#endif + +.macro QPEL_UNI_W_H_HEADER elems=4s + ldr x12, [sp] + sub x2, x2, #3 + movrel x9, qpel_filters + add x9, x9, x12, lsl #3 + ld1r {v28.2d}, [x9] + mov w10, #-6 + sub w10, w10, w5 + dup v30.\elems, w6 // wx + dup v31.4s, w10 // shift + dup v29.4s, w7 // ox +.endm + +function ff_hevc_put_hevc_qpel_uni_w_h4_8_neon, export=1 + QPEL_UNI_W_H_HEADER 4h + sxtl v0.8h, v28.8b +1: + ld1 {v1.8b, v2.8b}, [x2], x3 + subs w4, w4, #1 + uxtl v1.8h, v1.8b + uxtl v2.8h, v2.8b + ext v3.16b, v1.16b, v2.16b, #2 + ext v4.16b, v1.16b, v2.16b, #4 + ext v5.16b, v1.16b, v2.16b, #6 + ext v6.16b, v1.16b, v2.16b, #8 + ext v7.16b, v1.16b, v2.16b, #10 + ext v16.16b, v1.16b, v2.16b, #12 + ext v17.16b, v1.16b, v2.16b, #14 + mul v18.4h, v1.4h, v0.h[0] + mla v18.4h, v3.4h, v0.h[1] + mla v18.4h, v4.4h, v0.h[2] + mla v18.4h, v5.4h, v0.h[3] + mla v18.4h, v6.4h, v0.h[4] + mla v18.4h, v7.4h, v0.h[5] + mla v18.4h, v16.4h, v0.h[6] + mla v18.4h, v17.4h, v0.h[7] + smull v16.4s, v18.4h, v30.4h + sqrshl v16.4s, v16.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtun v16.8b, v16.8h + str s16, [x0] + add x0, x0, x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h6_8_neon, export=1 + QPEL_UNI_W_H_HEADER 8h + sub x1, x1, #4 + sxtl v0.8h, v28.8b +1: + ld1 {v1.8b, v2.8b}, [x2], x3 + subs w4, w4, #1 + uxtl v1.8h, v1.8b + uxtl v2.8h, v2.8b + ext v3.16b, v1.16b, v2.16b, #2 + ext v4.16b, v1.16b, v2.16b, #4 + ext v5.16b, v1.16b, v2.16b, #6 + ext v6.16b, v1.16b, v2.16b, #8 + ext v7.16b, v1.16b, v2.16b, #10 + ext v16.16b, v1.16b, v2.16b, #12 + ext v17.16b, v1.16b, v2.16b, #14 + mul v18.8h, v1.8h, v0.h[0] + mla v18.8h, v3.8h, v0.h[1] + mla v18.8h, v4.8h, v0.h[2] + mla v18.8h, v5.8h, v0.h[3] + mla v18.8h, v6.8h, v0.h[4] + mla v18.8h, v7.8h, v0.h[5] + mla v18.8h, v16.8h, v0.h[6] + mla v18.8h, v17.8h, v0.h[7] + smull v16.4s, v18.4h, v30.4h + smull2 v17.4s, v18.8h, v30.8h + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v17.4s, v17.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqadd v17.4s, v17.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtn2 v16.8h, v17.4s + sqxtun v16.8b, v16.8h + str s16, [x0], #4 + st1 {v16.h}[2], [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h8_8_neon, export=1 + QPEL_UNI_W_H_HEADER 8h + sxtl v0.8h, v28.8b +1: + ld1 {v1.8b, v2.8b}, [x2], x3 + subs w4, w4, #1 + uxtl v1.8h, v1.8b + uxtl v2.8h, v2.8b + ext v3.16b, v1.16b, v2.16b, #2 + ext v4.16b, v1.16b, v2.16b, #4 + ext v5.16b, v1.16b, v2.16b, #6 + ext v6.16b, v1.16b, v2.16b, #8 + ext v7.16b, v1.16b, v2.16b, #10 + ext v16.16b, v1.16b, v2.16b, #12 + ext v17.16b, v1.16b, v2.16b, #14 + mul v18.8h, v1.8h, v0.h[0] + mla v18.8h, v3.8h, v0.h[1] + mla v18.8h, v4.8h, v0.h[2] + mla v18.8h, v5.8h, v0.h[3] + mla v18.8h, v6.8h, v0.h[4] + mla v18.8h, v7.8h, v0.h[5] + mla v18.8h, v16.8h, v0.h[6] + mla v18.8h, v17.8h, v0.h[7] + smull v16.4s, v18.4h, v30.4h + smull2 v17.4s, v18.8h, v30.8h + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v17.4s, v17.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqadd v17.4s, v17.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtn2 v16.8h, v17.4s + sqxtun v16.8b, v16.8h + st1 {v16.8b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h12_8_neon, export=1 + QPEL_UNI_W_H_HEADER 8h + add x13, x0, #8 + sxtl v0.8h, v28.8b +1: + ld1 {v1.8b, v2.8b, v3.8b}, [x2], x3 + subs w4, w4, #1 + uxtl v1.8h, v1.8b + uxtl v2.8h, v2.8b + uxtl v3.8h, v3.8b + ext v4.16b, v1.16b, v2.16b, #2 + ext v5.16b, v1.16b, v2.16b, #4 + ext v6.16b, v1.16b, v2.16b, #6 + ext v7.16b, v1.16b, v2.16b, #8 + ext v16.16b, v1.16b, v2.16b, #10 + ext v17.16b, v1.16b, v2.16b, #12 + ext v18.16b, v1.16b, v2.16b, #14 + mul v19.8h, v1.8h, v0.h[0] + mla v19.8h, v4.8h, v0.h[1] + mla v19.8h, v5.8h, v0.h[2] + mla v19.8h, v6.8h, v0.h[3] + mla v19.8h, v7.8h, v0.h[4] + mla v19.8h, v16.8h, v0.h[5] + mla v19.8h, v17.8h, v0.h[6] + mla v19.8h, v18.8h, v0.h[7] + ext v4.16b, v2.16b, v3.16b, #2 + ext v5.16b, v2.16b, v3.16b, #4 + ext v6.16b, v2.16b, v3.16b, #6 + ext v7.16b, v2.16b, v3.16b, #8 + ext v16.16b, v2.16b, v3.16b, #10 + ext v17.16b, v2.16b, v3.16b, #12 + ext v18.16b, v2.16b, v3.16b, #14 + mul v20.4h, v2.4h, v0.h[0] + mla v20.4h, v4.4h, v0.h[1] + mla v20.4h, v5.4h, v0.h[2] + mla v20.4h, v6.4h, v0.h[3] + mla v20.4h, v7.4h, v0.h[4] + mla v20.4h, v16.4h, v0.h[5] + mla v20.4h, v17.4h, v0.h[6] + mla v20.4h, v18.4h, v0.h[7] + smull v16.4s, v19.4h, v30.4h + smull2 v17.4s, v19.8h, v30.8h + smull v18.4s, v20.4h, v30.4h + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v17.4s, v17.4s, v31.4s + sqrshl v18.4s, v18.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqadd v17.4s, v17.4s, v29.4s + sqadd v18.4s, v18.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtn2 v16.8h, v17.4s + sqxtn v17.4h, v18.4s + sqxtun v16.8b, v16.8h + sqxtun v17.8b, v17.8h + st1 {v16.8b}, [x0], x1 + st1 {v17.s}[0], [x13], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h16_8_neon, export=1 + QPEL_UNI_W_H_HEADER 8h + sxtl v0.8h, v28.8b +1: + ld1 {v1.8b, v2.8b, v3.8b}, [x2], x3 + subs w4, w4, #1 + uxtl v1.8h, v1.8b + uxtl v2.8h, v2.8b + uxtl v3.8h, v3.8b + ext v4.16b, v1.16b, v2.16b, #2 + ext v5.16b, v1.16b, v2.16b, #4 + ext v6.16b, v1.16b, v2.16b, #6 + ext v7.16b, v1.16b, v2.16b, #8 + ext v16.16b, v1.16b, v2.16b, #10 + ext v17.16b, v1.16b, v2.16b, #12 + ext v18.16b, v1.16b, v2.16b, #14 + mul v19.8h, v1.8h, v0.h[0] + mla v19.8h, v4.8h, v0.h[1] + mla v19.8h, v5.8h, v0.h[2] + mla v19.8h, v6.8h, v0.h[3] + mla v19.8h, v7.8h, v0.h[4] + mla v19.8h, v16.8h, v0.h[5] + mla v19.8h, v17.8h, v0.h[6] + mla v19.8h, v18.8h, v0.h[7] + ext v4.16b, v2.16b, v3.16b, #2 + ext v5.16b, v2.16b, v3.16b, #4 + ext v6.16b, v2.16b, v3.16b, #6 + ext v7.16b, v2.16b, v3.16b, #8 + ext v16.16b, v2.16b, v3.16b, #10 + ext v17.16b, v2.16b, v3.16b, #12 + ext v18.16b, v2.16b, v3.16b, #14 + mul v20.8h, v2.8h, v0.h[0] + mla v20.8h, v4.8h, v0.h[1] + mla v20.8h, v5.8h, v0.h[2] + mla v20.8h, v6.8h, v0.h[3] + mla v20.8h, v7.8h, v0.h[4] + mla v20.8h, v16.8h, v0.h[5] + mla v20.8h, v17.8h, v0.h[6] + mla v20.8h, v18.8h, v0.h[7] + smull v16.4s, v19.4h, v30.4h + smull2 v17.4s, v19.8h, v30.8h + smull v18.4s, v20.4h, v30.4h + smull2 v19.4s, v20.8h, v30.8h + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v17.4s, v17.4s, v31.4s + sqrshl v18.4s, v18.4s, v31.4s + sqrshl v19.4s, v19.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqadd v17.4s, v17.4s, v29.4s + sqadd v18.4s, v18.4s, v29.4s + sqadd v19.4s, v19.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtn2 v16.8h, v17.4s + sqxtn v17.4h, v18.4s + sqxtn2 v17.8h, v19.4s + sqxtun v16.8b, v16.8h + sqxtun v17.8b, v17.8h + st1 {v16.8b, v17.8b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h24_8_neon, export=1 + QPEL_UNI_W_H_HEADER 8h + sxtl v0.8h, v28.8b +1: + ld1 {v1.8b, v2.8b, v3.8b, v4.8b}, [x2], x3 + subs w4, w4, #1 + uxtl v1.8h, v1.8b + uxtl v2.8h, v2.8b + uxtl v3.8h, v3.8b + uxtl v4.8h, v4.8b + ext v5.16b, v1.16b, v2.16b, #2 + ext v6.16b, v1.16b, v2.16b, #4 + ext v7.16b, v1.16b, v2.16b, #6 + ext v16.16b, v1.16b, v2.16b, #8 + ext v17.16b, v1.16b, v2.16b, #10 + ext v18.16b, v1.16b, v2.16b, #12 + ext v19.16b, v1.16b, v2.16b, #14 + mul v20.8h, v1.8h, v0.h[0] + mla v20.8h, v5.8h, v0.h[1] + mla v20.8h, v6.8h, v0.h[2] + mla v20.8h, v7.8h, v0.h[3] + mla v20.8h, v16.8h, v0.h[4] + mla v20.8h, v17.8h, v0.h[5] + mla v20.8h, v18.8h, v0.h[6] + mla v20.8h, v19.8h, v0.h[7] + ext v5.16b, v2.16b, v3.16b, #2 + ext v6.16b, v2.16b, v3.16b, #4 + ext v7.16b, v2.16b, v3.16b, #6 + ext v16.16b, v2.16b, v3.16b, #8 + ext v17.16b, v2.16b, v3.16b, #10 + ext v18.16b, v2.16b, v3.16b, #12 + ext v19.16b, v2.16b, v3.16b, #14 + mul v21.8h, v2.8h, v0.h[0] + mla v21.8h, v5.8h, v0.h[1] + mla v21.8h, v6.8h, v0.h[2] + mla v21.8h, v7.8h, v0.h[3] + mla v21.8h, v16.8h, v0.h[4] + mla v21.8h, v17.8h, v0.h[5] + mla v21.8h, v18.8h, v0.h[6] + mla v21.8h, v19.8h, v0.h[7] + ext v5.16b, v3.16b, v4.16b, #2 + ext v6.16b, v3.16b, v4.16b, #4 + ext v7.16b, v3.16b, v4.16b, #6 + ext v16.16b, v3.16b, v4.16b, #8 + ext v17.16b, v3.16b, v4.16b, #10 + ext v18.16b, v3.16b, v4.16b, #12 + ext v19.16b, v3.16b, v4.16b, #14 + mul v22.8h, v3.8h, v0.h[0] + mla v22.8h, v5.8h, v0.h[1] + mla v22.8h, v6.8h, v0.h[2] + mla v22.8h, v7.8h, v0.h[3] + mla v22.8h, v16.8h, v0.h[4] + mla v22.8h, v17.8h, v0.h[5] + mla v22.8h, v18.8h, v0.h[6] + mla v22.8h, v19.8h, v0.h[7] + smull v16.4s, v20.4h, v30.4h + smull2 v17.4s, v20.8h, v30.8h + smull v18.4s, v21.4h, v30.4h + smull2 v19.4s, v21.8h, v30.8h + smull v20.4s, v22.4h, v30.4h + smull2 v21.4s, v22.8h, v30.8h + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v17.4s, v17.4s, v31.4s + sqrshl v18.4s, v18.4s, v31.4s + sqrshl v19.4s, v19.4s, v31.4s + sqrshl v20.4s, v20.4s, v31.4s + sqrshl v21.4s, v21.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqadd v17.4s, v17.4s, v29.4s + sqadd v18.4s, v18.4s, v29.4s + sqadd v19.4s, v19.4s, v29.4s + sqadd v20.4s, v20.4s, v29.4s + sqadd v21.4s, v21.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtn2 v16.8h, v17.4s + sqxtn v17.4h, v18.4s + sqxtn2 v17.8h, v19.4s + sqxtn v18.4h, v20.4s + sqxtn2 v18.8h, v21.4s + sqxtun v16.8b, v16.8h + sqxtun v17.8b, v17.8h + sqxtun v18.8b, v18.8h + st1 {v16.8b, v17.8b, v18.8b}, [x0], x1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h32_8_neon, export=1 + QPEL_UNI_W_H_HEADER 8h + ldr w10, [sp, #16] // width + ld1 {v1.8b}, [x2], #8 + sub x3, x3, w10, uxtw // decrement src stride + mov w11, w10 // original width + sub x3, x3, #8 // decrement src stride + sub x1, x1, w10, uxtw // decrement dst stride + sxtl v0.8h, v28.8b + uxtl v1.8h, v1.8b +1: + ld1 {v2.8b, v3.8b}, [x2], #16 + subs w10, w10, #16 // width + uxtl v2.8h, v2.8b + uxtl v3.8h, v3.8b + ext v4.16b, v1.16b, v2.16b, #2 + ext v5.16b, v1.16b, v2.16b, #4 + ext v6.16b, v1.16b, v2.16b, #6 + ext v7.16b, v1.16b, v2.16b, #8 + ext v16.16b, v1.16b, v2.16b, #10 + ext v17.16b, v1.16b, v2.16b, #12 + ext v18.16b, v1.16b, v2.16b, #14 + mul v19.8h, v1.8h, v0.h[0] + mla v19.8h, v4.8h, v0.h[1] + mla v19.8h, v5.8h, v0.h[2] + mla v19.8h, v6.8h, v0.h[3] + mla v19.8h, v7.8h, v0.h[4] + mla v19.8h, v16.8h, v0.h[5] + mla v19.8h, v17.8h, v0.h[6] + mla v19.8h, v18.8h, v0.h[7] + ext v4.16b, v2.16b, v3.16b, #2 + ext v5.16b, v2.16b, v3.16b, #4 + ext v6.16b, v2.16b, v3.16b, #6 + ext v7.16b, v2.16b, v3.16b, #8 + ext v16.16b, v2.16b, v3.16b, #10 + ext v17.16b, v2.16b, v3.16b, #12 + ext v18.16b, v2.16b, v3.16b, #14 + mul v20.8h, v2.8h, v0.h[0] + mla v20.8h, v4.8h, v0.h[1] + mla v20.8h, v5.8h, v0.h[2] + mla v20.8h, v6.8h, v0.h[3] + mla v20.8h, v7.8h, v0.h[4] + mla v20.8h, v16.8h, v0.h[5] + mla v20.8h, v17.8h, v0.h[6] + mla v20.8h, v18.8h, v0.h[7] + smull v16.4s, v19.4h, v30.4h + smull2 v17.4s, v19.8h, v30.8h + smull v18.4s, v20.4h, v30.4h + smull2 v19.4s, v20.8h, v30.8h + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v17.4s, v17.4s, v31.4s + sqrshl v18.4s, v18.4s, v31.4s + sqrshl v19.4s, v19.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqadd v17.4s, v17.4s, v29.4s + sqadd v18.4s, v18.4s, v29.4s + sqadd v19.4s, v19.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtn2 v16.8h, v17.4s + sqxtn v17.4h, v18.4s + sqxtn2 v17.8h, v19.4s + sqxtun v16.8b, v16.8h + sqxtun v17.8b, v17.8h + st1 {v16.8b, v17.8b}, [x0], #16 + mov v1.16b, v3.16b + b.gt 1b + subs w4, w4, #1 // height + add x2, x2, x3 + b.le 9f + ld1 {v1.8b}, [x2], #8 + mov w10, w11 + add x0, x0, x1 + uxtl v1.8h, v1.8b + b 1b +9: + ret +endfunc + +#if HAVE_I8MM +ENABLE_I8MM +function ff_hevc_put_hevc_qpel_uni_w_h4_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER +1: + ld1 {v0.16b}, [x2], x3 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + zip1 v0.2d, v0.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + movi v16.16b, #0 + movi v17.16b, #0 + usdot v16.4s, v0.16b, v28.16b + usdot v17.4s, v2.16b, v28.16b + addp v16.4s, v16.4s, v17.4s + mul v16.4s, v16.4s, v30.4s + sqrshl v16.4s, v16.4s, v31.4s + sqadd v16.4s, v16.4s, v29.4s + sqxtn v16.4h, v16.4s + sqxtun v16.8b, v16.8h + str s16, [x0] + add x0, x0, x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h6_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER + sub x1, x1, #4 +1: + ld1 {v0.16b}, [x2], x3 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + ext v4.16b, v0.16b, v0.16b, #4 + ext v5.16b, v0.16b, v0.16b, #5 + zip1 v0.2d, v0.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + zip1 v4.2d, v4.2d, v5.2d + movi v16.16b, #0 + movi v17.16b, #0 + movi v18.16b, #0 + usdot v16.4s, v0.16b, v28.16b + usdot v17.4s, v2.16b, v28.16b + usdot v18.4s, v4.16b, v28.16b + addp v16.4s, v16.4s, v17.4s + addp v18.4s, v18.4s, v18.4s + mul v16.4s, v16.4s, v30.4s + mul v18.2s, v18.2s, v30.2s + sqrshl v16.4s, v16.4s, v31.4s + sqrshl v18.2s, v18.2s, v31.2s + sqadd v16.4s, v16.4s, v29.4s + sqadd v18.2s, v18.2s, v29.2s + sqxtn v16.4h, v16.4s + sqxtn2 v16.8h, v18.4s + sqxtun v16.8b, v16.8h + str s16, [x0], #4 + st1 {v16.h}[2], [x0], x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + + +.macro QPEL_UNI_W_H_CALC s0, s1, s2, s3, d0, d1, d2, d3 + movi \d0\().16b, #0 + movi \d1\().16b, #0 + movi \d2\().16b, #0 + movi \d3\().16b, #0 + usdot \d0\().4s, \s0\().16b, v28.16b + usdot \d1\().4s, \s1\().16b, v28.16b + usdot \d2\().4s, \s2\().16b, v28.16b + usdot \d3\().4s, \s3\().16b, v28.16b + addp \d0\().4s, \d0\().4s, \d1\().4s + addp \d2\().4s, \d2\().4s, \d3\().4s + mul \d0\().4s, \d0\().4s, v30.4s + mul \d2\().4s, \d2\().4s, v30.4s + sqrshl \d0\().4s, \d0\().4s, v31.4s + sqrshl \d2\().4s, \d2\().4s, v31.4s + sqadd \d0\().4s, \d0\().4s, v29.4s + sqadd \d2\().4s, \d2\().4s, v29.4s +.endm + +.macro QPEL_UNI_W_H_CALC_HALF s0, s1, d0, d1 + movi \d0\().16b, #0 + movi \d1\().16b, #0 + usdot \d0\().4s, \s0\().16b, v28.16b + usdot \d1\().4s, \s1\().16b, v28.16b + addp \d0\().4s, \d0\().4s, \d1\().4s + mul \d0\().4s, \d0\().4s, v30.4s + sqrshl \d0\().4s, \d0\().4s, v31.4s + sqadd \d0\().4s, \d0\().4s, v29.4s +.endm + + +function ff_hevc_put_hevc_qpel_uni_w_h8_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER +1: + ld1 {v16.16b, v17.16b}, [x2], x3 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + zip1 v0.2d, v16.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + zip1 v4.2d, v4.2d, v5.2d + zip1 v6.2d, v6.2d, v7.2d + QPEL_UNI_W_H_CALC v0, v2, v4, v6, v18, v19, v20, v21 + sqxtn v18.4h, v18.4s + sqxtn2 v18.8h, v20.4s + sqxtun v18.8b, v18.8h + str d18, [x0] + add x0, x0, x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h12_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER + add x13, x0, #8 +1: + ld1 {v16.16b, v17.16b}, [x2], x3 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + zip1 v18.2d, v16.2d, v1.2d + zip1 v19.2d, v2.2d, v3.2d + zip1 v20.2d, v4.2d, v5.2d + zip1 v21.2d, v6.2d, v7.2d + zip2 v22.2d, v16.2d, v1.2d + zip2 v23.2d, v2.2d, v3.2d + QPEL_UNI_W_H_CALC v18, v19, v20, v21, v0, v2, v4, v6 + QPEL_UNI_W_H_CALC_HALF v22, v23, v24, v25 + sqxtn v0.4h, v0.4s + sqxtn2 v0.8h, v4.4s + sqxtn v1.4h, v24.4s + sqxtun v0.8b, v0.8h + sqxtun v1.8b, v1.8h + + str d0, [x0] + str s1, [x13] + add x0, x0, x1 + add x13, x13, x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h16_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER +1: + ld1 {v16.16b, v17.16b}, [x2], x3 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_UNI_W_H_CALC v16, v2, v1, v3, v18, v19, v20, v21 // v18: 0, 8, 2, 10 v20: 1, 9, 3, 11 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v23, v24, v25 // v22: 4, 12, 6, 14 v24: 5, 13, 7, 15 + sqxtn v0.4h, v18.4s + sqxtn2 v0.8h, v22.4s + sqxtn v1.4h, v20.4s + sqxtn2 v1.8h, v24.4s + trn1 v2.8h, v0.8h, v1.8h + trn2 v3.8h, v0.8h, v1.8h + sqxtun v0.8b, v2.8h + sqxtun2 v0.16b, v3.8h + st1 {v0.16b}, [x0], x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h24_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER + sub x1, x1, #16 +1: + ld1 {v16.16b, v17.16b}, [x2], x3 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_UNI_W_H_CALC v16, v2, v1, v3, v18, v19, v20, v21 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v23, v24, v25 + sqxtn v18.4h, v18.4s + sqxtn2 v18.8h, v22.4s + sqxtn v19.4h, v20.4s + sqxtn2 v19.8h, v24.4s + trn1 v20.8h, v18.8h, v19.8h + trn2 v21.8h, v18.8h, v19.8h + sqxtun v26.8b, v20.8h + sqxtun2 v26.16b, v21.8h // 0-15 + ext v1.16b, v17.16b, v17.16b, #1 + ext v2.16b, v17.16b, v17.16b, #2 + ext v3.16b, v17.16b, v17.16b, #3 + ext v4.16b, v17.16b, v17.16b, #4 + ext v5.16b, v17.16b, v17.16b, #5 + ext v6.16b, v17.16b, v17.16b, #6 + ext v7.16b, v17.16b, v17.16b, #7 + zip1 v0.2d, v17.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + zip1 v4.2d, v4.2d, v5.2d + zip1 v6.2d, v6.2d, v7.2d + QPEL_UNI_W_H_CALC v0, v2, v4, v6, v18, v19, v20, v21 + sqxtn v18.4h, v18.4s + sqxtn2 v18.8h, v20.4s + sqxtun v27.8b, v18.8h + + st1 {v26.16b}, [x0], #16 + st1 {v27.8b}, [x0], x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + + +function ff_hevc_put_hevc_qpel_uni_w_h32_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER +1: + ld1 {v16.16b, v17.16b, v18.16b}, [x2], x3 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_UNI_W_H_CALC v16, v2, v1, v3, v0, v19, v20, v21 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v23, v24, v25 + sqxtn v0.4h, v0.4s + sqxtn2 v0.8h, v22.4s + sqxtn v19.4h, v20.4s + sqxtn2 v19.8h, v24.4s + trn1 v20.8h, v0.8h, v19.8h + trn2 v21.8h, v0.8h, v19.8h + sqxtun v26.8b, v20.8h + sqxtun2 v26.16b, v21.8h // 0-15 + ext v1.16b, v17.16b, v18.16b, #1 + ext v2.16b, v17.16b, v18.16b, #2 + ext v3.16b, v17.16b, v18.16b, #3 + ext v4.16b, v17.16b, v18.16b, #4 + ext v5.16b, v17.16b, v18.16b, #5 + ext v6.16b, v17.16b, v18.16b, #6 + ext v7.16b, v17.16b, v18.16b, #7 + QPEL_UNI_W_H_CALC v17, v2, v1, v3, v0, v19, v20, v21 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v23, v24, v25 + sqxtn v0.4h, v0.4s + sqxtn2 v0.8h, v22.4s + sqxtn v19.4h, v20.4s + sqxtn2 v19.8h, v24.4s + trn1 v20.8h, v0.8h, v19.8h + trn2 v21.8h, v0.8h, v19.8h + sqxtun v27.8b, v20.8h + sqxtun2 v27.16b, v21.8h // 16-31 + st1 {v26.16b, v27.16b}, [x0], x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h48_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER +1: + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x2], x3 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_UNI_W_H_CALC v16, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v25.8b, v22.8h + sqxtun2 v25.16b, v23.8h // 0-15 + ext v1.16b, v17.16b, v18.16b, #1 + ext v2.16b, v17.16b, v18.16b, #2 + ext v3.16b, v17.16b, v18.16b, #3 + ext v4.16b, v17.16b, v18.16b, #4 + ext v5.16b, v17.16b, v18.16b, #5 + ext v6.16b, v17.16b, v18.16b, #6 + ext v7.16b, v17.16b, v18.16b, #7 + QPEL_UNI_W_H_CALC v17, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v26.8b, v22.8h + sqxtun2 v26.16b, v23.8h // 16-31 + ext v1.16b, v18.16b, v19.16b, #1 + ext v2.16b, v18.16b, v19.16b, #2 + ext v3.16b, v18.16b, v19.16b, #3 + ext v4.16b, v18.16b, v19.16b, #4 + ext v5.16b, v18.16b, v19.16b, #5 + ext v6.16b, v18.16b, v19.16b, #6 + ext v7.16b, v18.16b, v19.16b, #7 + QPEL_UNI_W_H_CALC v18, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v27.8b, v22.8h + sqxtun2 v27.16b, v23.8h // 32-47 + st1 {v25.16b, v26.16b, v27.16b}, [x0], x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + + + +function ff_hevc_put_hevc_qpel_uni_w_h64_8_neon_i8mm, export=1 + QPEL_UNI_W_H_HEADER + sub x3, x3, #64 +1: + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x2], #64 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_UNI_W_H_CALC v16, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v16.8b, v22.8h + sqxtun2 v16.16b, v23.8h // 0-15 + ext v1.16b, v17.16b, v18.16b, #1 + ext v2.16b, v17.16b, v18.16b, #2 + ext v3.16b, v17.16b, v18.16b, #3 + ext v4.16b, v17.16b, v18.16b, #4 + ext v5.16b, v17.16b, v18.16b, #5 + ext v6.16b, v17.16b, v18.16b, #6 + ext v7.16b, v17.16b, v18.16b, #7 + QPEL_UNI_W_H_CALC v17, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v17.8b, v22.8h + sqxtun2 v17.16b, v23.8h // 16-31 + ext v1.16b, v18.16b, v19.16b, #1 + ext v2.16b, v18.16b, v19.16b, #2 + ext v3.16b, v18.16b, v19.16b, #3 + ext v4.16b, v18.16b, v19.16b, #4 + ext v5.16b, v18.16b, v19.16b, #5 + ext v6.16b, v18.16b, v19.16b, #6 + ext v7.16b, v18.16b, v19.16b, #7 + QPEL_UNI_W_H_CALC v18, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + ld1 {v0.16b}, [x2], x3 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v18.8b, v22.8h + sqxtun2 v18.16b, v23.8h // 32-47 + ext v1.16b, v19.16b, v0.16b, #1 + ext v2.16b, v19.16b, v0.16b, #2 + ext v3.16b, v19.16b, v0.16b, #3 + ext v4.16b, v19.16b, v0.16b, #4 + ext v5.16b, v19.16b, v0.16b, #5 + ext v6.16b, v19.16b, v0.16b, #6 + ext v7.16b, v19.16b, v0.16b, #7 + QPEL_UNI_W_H_CALC v19, v2, v1, v3, v20, v24, v21, v0 + QPEL_UNI_W_H_CALC v4, v6, v5, v7, v22, v24, v23, v0 + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + sqxtn v21.4h, v21.4s + sqxtn2 v21.8h, v23.4s + trn1 v22.8h, v20.8h, v21.8h + trn2 v23.8h, v20.8h, v21.8h + sqxtun v19.8b, v22.8h + sqxtun2 v19.16b, v23.8h // 48-63 + + st1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x0], x1 + subs w4, w4, #1 + b.hi 1b + ret +endfunc + +.macro QPEL_H_HEADER + movrel x9, qpel_filters + add x9, x9, x4, lsl #3 + ldr x11, [x9] + dup v31.2d, x11 + sub x1, x1, #3 +.endm + +function ff_hevc_put_hevc_qpel_h4_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 +1: + ld1 {v0.16b}, [x1], x2 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + zip1 v0.2d, v0.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + movi v16.16b, #0 + movi v17.16b, #0 + usdot v16.4s, v0.16b, v31.16b + usdot v17.4s, v2.16b, v31.16b + addp v16.4s, v16.4s, v17.4s + sqxtn v16.4h, v16.4s + str d16, [x0] + add x0, x0, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h6_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 + add x15, x0, #8 +1: + ld1 {v0.16b}, [x1], x2 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + ext v4.16b, v0.16b, v0.16b, #4 + ext v5.16b, v0.16b, v0.16b, #5 + zip1 v0.2d, v0.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + zip1 v4.2d, v4.2d, v5.2d + movi v16.16b, #0 + movi v17.16b, #0 + movi v18.16b, #0 + usdot v16.4s, v0.16b, v31.16b + usdot v17.4s, v2.16b, v31.16b + usdot v18.4s, v4.16b, v31.16b + addp v16.4s, v16.4s, v17.4s + addp v18.4s, v18.4s, v18.4s + sqxtn v16.4h, v16.4s + sqxtn v18.4h, v18.4s + str d16, [x0] + str s18, [x15] + add x0, x0, x10 + add x15, x15, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h8_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 +1: + ld1 {v0.16b}, [x1], x2 + ext v1.16b, v0.16b, v0.16b, #1 + ext v2.16b, v0.16b, v0.16b, #2 + ext v3.16b, v0.16b, v0.16b, #3 + ext v4.16b, v0.16b, v0.16b, #4 + ext v5.16b, v0.16b, v0.16b, #5 + ext v6.16b, v0.16b, v0.16b, #6 + ext v7.16b, v0.16b, v0.16b, #7 + zip1 v0.2d, v0.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + zip1 v4.2d, v4.2d, v5.2d + zip1 v6.2d, v6.2d, v7.2d + movi v16.16b, #0 + movi v17.16b, #0 + movi v18.16b, #0 + movi v19.16b, #0 + usdot v16.4s, v0.16b, v31.16b + usdot v17.4s, v2.16b, v31.16b + usdot v18.4s, v4.16b, v31.16b + usdot v19.4s, v6.16b, v31.16b + addp v16.4s, v16.4s, v17.4s + addp v18.4s, v18.4s, v19.4s + sqxtn v16.4h, v16.4s + sqxtn2 v16.8h, v18.4s + str q16, [x0] + add x0, x0, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +.macro QPEL_H_CALC s0, s1, s2, s3, d0, d1, d2, d3 + movi \d0\().16b, #0 + movi \d1\().16b, #0 + movi \d2\().16b, #0 + movi \d3\().16b, #0 + usdot \d0\().4s, \s0\().16b, v31.16b + usdot \d1\().4s, \s1\().16b, v31.16b + usdot \d2\().4s, \s2\().16b, v31.16b + usdot \d3\().4s, \s3\().16b, v31.16b +.endm + +function ff_hevc_put_hevc_qpel_h12_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 + add x15, x0, #16 +1: + ld1 {v16.16b, v17.16b}, [x1], x2 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + zip1 v18.2d, v4.2d, v5.2d + zip1 v19.2d, v6.2d, v7.2d + QPEL_H_CALC v16, v1, v2, v3, v20, v21, v22, v23 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + movi v24.16b, #0 + movi v25.16b, #0 + usdot v24.4s, v18.16b, v31.16b + usdot v25.4s, v19.16b, v31.16b + addp v24.4s, v24.4s, v25.4s + trn1 v26.4s, v20.4s, v21.4s + trn2 v27.4s, v20.4s, v21.4s + sqxtn v26.4h, v26.4s + sqxtn v27.4h, v27.4s + sqxtn2 v26.8h, v24.4s + + str q26, [x0] + str d27, [x15] + add x0, x0, x10 + add x15, x15, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h16_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 +1: + ld1 {v16.16b, v17.16b}, [x1], x2 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + + QPEL_H_CALC v16, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + + sqxtn v18.4h, v22.4s + sqxtn2 v18.8h, v26.4s + sqxtn v19.4h, v23.4s + sqxtn2 v19.8h, v27.4s + + stp q18, q19, [x0] + add x0, x0, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h24_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 + add x15, x0, #32 +1: + ld1 {v16.16b, v17.16b}, [x1], x2 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_H_CALC v16, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v18.4h, v22.4s + sqxtn2 v18.8h, v26.4s + sqxtn v19.4h, v23.4s + sqxtn2 v19.8h, v27.4s + stp q18, q19, [x0] + add x0, x0, x10 + ext v1.16b, v17.16b, v17.16b, #1 + ext v2.16b, v17.16b, v17.16b, #2 + ext v3.16b, v17.16b, v17.16b, #3 + ext v4.16b, v17.16b, v17.16b, #4 + ext v5.16b, v17.16b, v17.16b, #5 + ext v6.16b, v17.16b, v17.16b, #6 + ext v7.16b, v17.16b, v17.16b, #7 + zip1 v0.2d, v17.2d, v1.2d + zip1 v2.2d, v2.2d, v3.2d + zip1 v4.2d, v4.2d, v5.2d + zip1 v6.2d, v6.2d, v7.2d + QPEL_H_CALC v0, v2, v4, v6, v20, v21, v22, v23 + addp v20.4s, v20.4s, v21.4s + addp v22.4s, v22.4s, v23.4s + sqxtn v20.4h, v20.4s + sqxtn2 v20.8h, v22.4s + str q20, [x15] + add x15, x15, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h32_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 + add x15, x0, #32 +1: + ld1 {v16.16b, v17.16b, v18.16b}, [x1], x2 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_H_CALC v16, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0] + add x0, x0, x10 + ext v1.16b, v17.16b, v18.16b, #1 + ext v2.16b, v17.16b, v18.16b, #2 + ext v3.16b, v17.16b, v18.16b, #3 + ext v4.16b, v17.16b, v18.16b, #4 + ext v5.16b, v17.16b, v18.16b, #5 + ext v6.16b, v17.16b, v18.16b, #6 + ext v7.16b, v17.16b, v18.16b, #7 + QPEL_H_CALC v17, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x15] + add x15, x15, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h48_8_neon_i8mm, export=1 + QPEL_H_HEADER + mov x10, #MAX_PB_SIZE * 2 - 64 +1: + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x1], x2 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_H_CALC v16, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0], #32 + + ext v1.16b, v17.16b, v18.16b, #1 + ext v2.16b, v17.16b, v18.16b, #2 + ext v3.16b, v17.16b, v18.16b, #3 + ext v4.16b, v17.16b, v18.16b, #4 + ext v5.16b, v17.16b, v18.16b, #5 + ext v6.16b, v17.16b, v18.16b, #6 + ext v7.16b, v17.16b, v18.16b, #7 + QPEL_H_CALC v17, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0], #32 + ext v1.16b, v18.16b, v19.16b, #1 + ext v2.16b, v18.16b, v19.16b, #2 + ext v3.16b, v18.16b, v19.16b, #3 + ext v4.16b, v18.16b, v19.16b, #4 + ext v5.16b, v18.16b, v19.16b, #5 + ext v6.16b, v18.16b, v19.16b, #6 + ext v7.16b, v18.16b, v19.16b, #7 + QPEL_H_CALC v18, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0] + add x0, x0, x10 + subs w3, w3, #1 + b.ne 1b + ret +endfunc + +function ff_hevc_put_hevc_qpel_h64_8_neon_i8mm, export=1 + QPEL_H_HEADER + sub x2, x2, #64 +1: + ld1 {v16.16b, v17.16b, v18.16b, v19.16b}, [x1], #64 + ext v1.16b, v16.16b, v17.16b, #1 + ext v2.16b, v16.16b, v17.16b, #2 + ext v3.16b, v16.16b, v17.16b, #3 + ext v4.16b, v16.16b, v17.16b, #4 + ext v5.16b, v16.16b, v17.16b, #5 + ext v6.16b, v16.16b, v17.16b, #6 + ext v7.16b, v16.16b, v17.16b, #7 + QPEL_H_CALC v16, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0], #32 + + ext v1.16b, v17.16b, v18.16b, #1 + ext v2.16b, v17.16b, v18.16b, #2 + ext v3.16b, v17.16b, v18.16b, #3 + ext v4.16b, v17.16b, v18.16b, #4 + ext v5.16b, v17.16b, v18.16b, #5 + ext v6.16b, v17.16b, v18.16b, #6 + ext v7.16b, v17.16b, v18.16b, #7 + QPEL_H_CALC v17, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0], #32 + ext v1.16b, v18.16b, v19.16b, #1 + ext v2.16b, v18.16b, v19.16b, #2 + ext v3.16b, v18.16b, v19.16b, #3 + ext v4.16b, v18.16b, v19.16b, #4 + ext v5.16b, v18.16b, v19.16b, #5 + ext v6.16b, v18.16b, v19.16b, #6 + ext v7.16b, v18.16b, v19.16b, #7 + QPEL_H_CALC v18, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0], #32 + ld1 {v28.8b}, [x1], x2 + ext v1.16b, v19.16b, v28.16b, #1 + ext v2.16b, v19.16b, v28.16b, #2 + ext v3.16b, v19.16b, v28.16b, #3 + ext v4.16b, v19.16b, v28.16b, #4 + ext v5.16b, v19.16b, v28.16b, #5 + ext v6.16b, v19.16b, v28.16b, #6 + ext v7.16b, v19.16b, v28.16b, #7 + QPEL_H_CALC v19, v1, v2, v3, v20, v21, v22, v23 + QPEL_H_CALC v4, v5, v6, v7, v24, v25, v26, v27 + addp v20.4s, v20.4s, v22.4s + addp v21.4s, v21.4s, v23.4s + addp v24.4s, v24.4s, v26.4s + addp v25.4s, v25.4s, v27.4s + trn1 v22.4s, v20.4s, v21.4s + trn2 v23.4s, v20.4s, v21.4s + trn1 v26.4s, v24.4s, v25.4s + trn2 v27.4s, v24.4s, v25.4s + sqxtn v20.4h, v22.4s + sqxtn2 v20.8h, v26.4s + sqxtn v21.4h, v23.4s + sqxtn2 v21.8h, v27.4s + stp q20, q21, [x0], #32 + subs w3, w3, #1 + b.ne 1b + ret +endfunc +DISABLE_I8MM +#endif + + +function hevc_put_hevc_qpel_hv4_8_end_neon + load_qpel_filterh x5, x4 + ldr d16, [sp] + ldr d17, [sp, x7] + add sp, sp, x7, lsl #1 + ldr d18, [sp] + ldr d19, [sp, x7] + add sp, sp, x7, lsl #1 + ldr d20, [sp] + ldr d21, [sp, x7] + add sp, sp, x7, lsl #1 + ldr d22, [sp] + add sp, sp, x7 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().4h}, [sp], x7 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqshrn + subs w3, w3, #1 + st1 {v1.4h}, [x0], x7 +.endm +1: calc_all +.purgem calc +2: mov sp, x14 + ret +endfunc + +function hevc_put_hevc_qpel_hv6_8_end_neon + mov x8, #120 + load_qpel_filterh x5, x4 + ldr q16, [sp] + ldr q17, [sp, x7] + add sp, sp, x7, lsl #1 + ldr q18, [sp] + ldr q19, [sp, x7] + add sp, sp, x7, lsl #1 + ldr q20, [sp] + ldr q21, [sp, x7] + add sp, sp, x7, lsl #1 + ldr q22, [sp] + add sp, sp, x7 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().8h}, [sp], x7 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqshrn + calc_qpelh2 v1, v2, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqshrn2 + st1 {v1.4h}, [x0], #8 + subs w3, w3, #1 + st1 {v1.s}[2], [x0], x8 +.endm +1: calc_all +.purgem calc +2: mov sp, x14 + ret +endfunc + +function hevc_put_hevc_qpel_hv8_8_end_neon + mov x7, #128 + load_qpel_filterh x5, x4 + ldr q16, [sp] + ldr q17, [sp, x7] + add sp, sp, x7, lsl #1 + ldr q18, [sp] + ldr q19, [sp, x7] + add sp, sp, x7, lsl #1 + ldr q20, [sp] + ldr q21, [sp, x7] + add sp, sp, x7, lsl #1 + ldr q22, [sp] + add sp, sp, x7 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().8h}, [sp], x7 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqshrn + calc_qpelh2 v1, v2, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqshrn2 + subs w3, w3, #1 + st1 {v1.8h}, [x0], x7 +.endm +1: calc_all +.purgem calc +2: mov sp, x14 + ret +endfunc + +function hevc_put_hevc_qpel_hv12_8_end_neon + mov x7, #128 + load_qpel_filterh x5, x4 + mov x8, #112 + ld1 {v16.8h, v17.8h}, [sp], x7 + ld1 {v18.8h, v19.8h}, [sp], x7 + ld1 {v20.8h, v21.8h}, [sp], x7 + ld1 {v22.8h, v23.8h}, [sp], x7 + ld1 {v24.8h, v25.8h}, [sp], x7 + ld1 {v26.8h, v27.8h}, [sp], x7 + ld1 {v28.8h, v29.8h}, [sp], x7 +.macro calc tmp0, tmp1, src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15 + ld1 {\tmp0\().8h, \tmp1\().8h}, [sp], x7 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqshrn + calc_qpelh2 v1, v2, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqshrn2 + calc_qpelh v2, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15, sqshrn + st1 {v1.8h}, [x0], #16 + subs w3, w3, #1 + st1 {v2.4h}, [x0], x8 +.endm +1: calc_all2 +.purgem calc +2: mov sp, x14 + ret +endfunc + +function hevc_put_hevc_qpel_hv16_8_end_neon + mov x7, #128 + load_qpel_filterh x5, x4 + ld1 {v16.8h, v17.8h}, [sp], x7 + ld1 {v18.8h, v19.8h}, [sp], x7 + ld1 {v20.8h, v21.8h}, [sp], x7 + ld1 {v22.8h, v23.8h}, [sp], x7 + ld1 {v24.8h, v25.8h}, [sp], x7 + ld1 {v26.8h, v27.8h}, [sp], x7 + ld1 {v28.8h, v29.8h}, [sp], x7 +.macro calc tmp0, tmp1, src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15 + ld1 {\tmp0\().8h, \tmp1\().8h}, [sp], x7 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqshrn + calc_qpelh2 v1, v2, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqshrn2 + calc_qpelh v2, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15, sqshrn + calc_qpelh2 v2, v3, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15, sqshrn2 + subs w3, w3, #1 + st1 {v1.8h, v2.8h}, [x0], x7 +.endm +1: calc_all2 +.purgem calc +2: mov sp, x14 + ret +endfunc + +function hevc_put_hevc_qpel_hv32_8_end_neon + mov x7, #128 + load_qpel_filterh x5, x4 +0: mov x8, sp // src + ld1 {v16.8h, v17.8h}, [x8], x7 + mov w9, w3 // height + ld1 {v18.8h, v19.8h}, [x8], x7 + mov x5, x0 // dst + ld1 {v20.8h, v21.8h}, [x8], x7 + ld1 {v22.8h, v23.8h}, [x8], x7 + ld1 {v24.8h, v25.8h}, [x8], x7 + ld1 {v26.8h, v27.8h}, [x8], x7 + ld1 {v28.8h, v29.8h}, [x8], x7 +.macro calc tmp0, tmp1, src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15 + ld1 {\tmp0\().8h, \tmp1\().8h}, [x8], x7 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqshrn + calc_qpelh2 v1, v2, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sqshrn2 + calc_qpelh v2, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15, sqshrn + calc_qpelh2 v2, v3, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15, sqshrn2 + subs x9, x9, #1 + st1 {v1.8h, v2.8h}, [x5], x7 +.endm +1: calc_all2 +.purgem calc +2: add x0, x0, #32 + add sp, sp, #32 + subs w6, w6, #16 + b.hi 0b + mov sp, x14 + ret +endfunc + +.macro qpel_hv suffix +function ff_hevc_put_hevc_qpel_hv4_8_\suffix, export=1 + add w10, w3, #8 + mov x7, #128 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x5, x30, [sp, #-48]! + stp x0, x3, [sp, #16] + str x14, [sp, #32] + add x0, sp, #48 + sub x1, x1, x2, lsl #1 + add x3, x3, #7 + sub x1, x1, x2 + bl X(ff_hevc_put_hevc_qpel_h4_8_\suffix) + ldr x14, [sp, #32] + ldp x0, x3, [sp, #16] + ldp x5, x30, [sp], #48 + b hevc_put_hevc_qpel_hv4_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_hv6_8_\suffix, export=1 + add w10, w3, #8 + mov x7, #128 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x5, x30, [sp, #-48]! + stp x0, x3, [sp, #16] + str x14, [sp, #32] + add x0, sp, #48 + sub x1, x1, x2, lsl #1 + add x3, x3, #7 + sub x1, x1, x2 + bl X(ff_hevc_put_hevc_qpel_h6_8_\suffix) + ldr x14, [sp, #32] + ldp x0, x3, [sp, #16] + ldp x5, x30, [sp], #48 + b hevc_put_hevc_qpel_hv6_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_hv8_8_\suffix, export=1 + add w10, w3, #8 + lsl x10, x10, #7 + sub x1, x1, x2, lsl #1 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x5, x30, [sp, #-48]! + stp x0, x3, [sp, #16] + str x14, [sp, #32] + add x0, sp, #48 + add x3, x3, #7 + sub x1, x1, x2 + bl X(ff_hevc_put_hevc_qpel_h8_8_\suffix) + ldr x14, [sp, #32] + ldp x0, x3, [sp, #16] + ldp x5, x30, [sp], #48 + b hevc_put_hevc_qpel_hv8_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_hv12_8_\suffix, export=1 + add w10, w3, #8 + lsl x10, x10, #7 + sub x1, x1, x2, lsl #1 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x5, x30, [sp, #-48]! + stp x0, x3, [sp, #16] + str x14, [sp, #32] + add x0, sp, #48 + add x3, x3, #7 + sub x1, x1, x2 + mov w6, #12 + bl X(ff_hevc_put_hevc_qpel_h12_8_\suffix) + ldr x14, [sp, #32] + ldp x0, x3, [sp, #16] + ldp x5, x30, [sp], #48 + b hevc_put_hevc_qpel_hv12_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_hv16_8_\suffix, export=1 + add w10, w3, #8 + lsl x10, x10, #7 + sub x1, x1, x2, lsl #1 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x5, x30, [sp, #-48]! + stp x0, x3, [sp, #16] + str x14, [sp, #32] + add x3, x3, #7 + add x0, sp, #48 + sub x1, x1, x2 + bl X(ff_hevc_put_hevc_qpel_h16_8_\suffix) + ldr x14, [sp, #32] + ldp x0, x3, [sp, #16] + ldp x5, x30, [sp], #48 + b hevc_put_hevc_qpel_hv16_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_hv24_8_\suffix, export=1 + stp x4, x5, [sp, #-64]! + stp x2, x3, [sp, #16] + stp x0, x1, [sp, #32] + str x30, [sp, #48] + bl X(ff_hevc_put_hevc_qpel_hv12_8_\suffix) + ldp x0, x1, [sp, #32] + ldp x2, x3, [sp, #16] + ldp x4, x5, [sp], #48 + add x1, x1, #12 + add x0, x0, #24 + bl X(ff_hevc_put_hevc_qpel_hv12_8_\suffix) + ldr x30, [sp], #16 + ret +endfunc + +function ff_hevc_put_hevc_qpel_hv32_8_\suffix, export=1 + add w10, w3, #8 + sub x1, x1, x2, lsl #1 + lsl x10, x10, #7 + sub x1, x1, x2 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x5, x30, [sp, #-48]! + stp x0, x3, [sp, #16] + str x14, [sp, #32] + add x3, x3, #7 + add x0, sp, #48 + mov w6, #32 + bl X(ff_hevc_put_hevc_qpel_h32_8_\suffix) + ldr x14, [sp, #32] + ldp x0, x3, [sp, #16] + ldp x5, x30, [sp], #48 + b hevc_put_hevc_qpel_hv32_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_hv48_8_\suffix, export=1 + stp x4, x5, [sp, #-64]! + stp x2, x3, [sp, #16] + stp x0, x1, [sp, #32] + str x30, [sp, #48] + bl X(ff_hevc_put_hevc_qpel_hv24_8_\suffix) + ldp x0, x1, [sp, #32] + ldp x2, x3, [sp, #16] + ldp x4, x5, [sp], #48 + add x1, x1, #24 + add x0, x0, #48 + bl X(ff_hevc_put_hevc_qpel_hv24_8_\suffix) + ldr x30, [sp], #16 + ret +endfunc + +function ff_hevc_put_hevc_qpel_hv64_8_\suffix, export=1 + stp x4, x5, [sp, #-64]! + stp x2, x3, [sp, #16] + stp x0, x1, [sp, #32] + str x30, [sp, #48] + mov x6, #32 + bl X(ff_hevc_put_hevc_qpel_hv32_8_\suffix) + ldp x0, x1, [sp, #32] + ldp x2, x3, [sp, #16] + ldp x4, x5, [sp], #48 + add x1, x1, #32 + add x0, x0, #64 + mov x6, #32 + bl X(ff_hevc_put_hevc_qpel_hv32_8_\suffix) + ldr x30, [sp], #16 + ret +endfunc +.endm + +qpel_hv neon + +#if HAVE_I8MM +ENABLE_I8MM + +qpel_hv neon_i8mm + +DISABLE_I8MM +#endif + +.macro QPEL_UNI_W_HV_HEADER width, suffix + ldp x14, x15, [sp] // mx, my + ldr w13, [sp, #16] // width + stp x19, x30, [sp, #-80]! + stp x20, x21, [sp, #16] + stp x22, x23, [sp, #32] + stp x24, x25, [sp, #48] + stp x26, x27, [sp, #64] + mov x19, sp + mov x11, #(MAX_PB_SIZE*(MAX_PB_SIZE+8)*2) + sub sp, sp, x11 + mov x20, x0 + mov x21, x1 + mov x0, sp + sub x1, x2, x3, lsl #1 + sub x1, x1, x3 + mov x2, x3 + add w3, w4, #7 + mov w22, w4 // height + mov x4, x14 // mx + mov x23, x15 // my + mov w24, w6 // wx + mov w25, w7 // ox + mov w26, #-6 + sub w26, w26, w5 // -shift + mov w27, w13 // width +.ifc \suffix, neon +.if \width >= 32 + mov w6, #\width + bl X(ff_hevc_put_hevc_qpel_h32_8_neon) +.else + bl X(ff_hevc_put_hevc_qpel_h\width\()_8_\suffix) +.endif +.else + bl X(ff_hevc_put_hevc_qpel_h\width\()_8_\suffix) +.endif + movrel x9, qpel_filters + add x9, x9, x23, lsl #3 + ld1 {v0.8b}, [x9] + sxtl v0.8h, v0.8b + mov x10, #(MAX_PB_SIZE * 2) + dup v28.4s, w24 + dup v29.4s, w25 + dup v30.4s, w26 +.endm + +.macro QPEL_UNI_W_HV_END + mov sp, x19 + ldp x20, x21, [sp, #16] + ldp x22, x23, [sp, #32] + ldp x24, x25, [sp, #48] + ldp x26, x27, [sp, #64] + ldp x19, x30, [sp], #80 +.endm + +.macro QPEL_UNI_W_HV_4 + sshr v26.4s, v26.4s, #6 + mul v24.4s, v26.4s, v28.4s + sqrshl v24.4s, v24.4s, v30.4s + sqadd v24.4s, v24.4s, v29.4s + sqxtn v24.4h, v24.4s + sqxtun v24.8b, v24.8h + st1 {v24.s}[0], [x20], x21 +.endm + +.macro QPEL_FILTER_H dst, src0, src1, src2, src3, src4, src5, src6, src7 + smull \dst\().4s, \src0\().4h, v0.h[0] + smlal \dst\().4s, \src1\().4h, v0.h[1] + smlal \dst\().4s, \src2\().4h, v0.h[2] + smlal \dst\().4s, \src3\().4h, v0.h[3] + smlal \dst\().4s, \src4\().4h, v0.h[4] + smlal \dst\().4s, \src5\().4h, v0.h[5] + smlal \dst\().4s, \src6\().4h, v0.h[6] + smlal \dst\().4s, \src7\().4h, v0.h[7] +.endm + +.macro QPEL_FILTER_H2 dst, src0, src1, src2, src3, src4, src5, src6, src7 + smull2 \dst\().4s, \src0\().8h, v0.h[0] + smlal2 \dst\().4s, \src1\().8h, v0.h[1] + smlal2 \dst\().4s, \src2\().8h, v0.h[2] + smlal2 \dst\().4s, \src3\().8h, v0.h[3] + smlal2 \dst\().4s, \src4\().8h, v0.h[4] + smlal2 \dst\().4s, \src5\().8h, v0.h[5] + smlal2 \dst\().4s, \src6\().8h, v0.h[6] + smlal2 \dst\().4s, \src7\().8h, v0.h[7] +.endm + +function hevc_put_hevc_qpel_uni_w_hv4_8_end_neon + ldr d16, [sp] + ldr d17, [sp, x10] + add sp, sp, x10, lsl #1 + ldr d18, [sp] + ldr d19, [sp, x10] + add sp, sp, x10, lsl #1 + ldr d20, [sp] + ldr d21, [sp, x10] + add sp, sp, x10, lsl #1 + ldr d22, [sp] + add sp, sp, x10 +1: + ldr d23, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d16, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d17, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d18, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d19, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d20, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d21, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.eq 2f + + ldr d22, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_UNI_W_HV_4 + subs w22, w22, #1 + b.hi 1b + +2: + QPEL_UNI_W_HV_END + ret +endfunc + +.macro QPEL_UNI_W_HV_8 + sshr v26.4s, v26.4s, #6 + sshr v27.4s, v27.4s, #6 + mul v24.4s, v26.4s, v28.4s + mul v25.4s, v27.4s, v28.4s + sqrshl v24.4s, v24.4s, v30.4s + sqrshl v25.4s, v25.4s, v30.4s + sqadd v24.4s, v24.4s, v29.4s + sqadd v25.4s, v25.4s, v29.4s + sqxtn v24.4h, v24.4s + sqxtn2 v24.8h, v25.4s + sqxtun v24.8b, v24.8h + st1 {v24.d}[0], [x20], x21 +.endm + +function hevc_put_hevc_qpel_uni_w_hv8_8_end_neon + ldr q16, [sp] + ldr q17, [sp, x10] + add sp, sp, x10, lsl #1 + ldr q18, [sp] + ldr q19, [sp, x10] + add sp, sp, x10, lsl #1 + ldr q20, [sp] + ldr q21, [sp, x10] + add sp, sp, x10, lsl #1 + ldr q22, [sp] + add sp, sp, x10 +1: + ldr q23, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_H2 v27, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q16, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_H2 v27, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q17, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_H2 v27, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q18, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_H2 v27, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q19, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_H2 v27, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q20, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_H2 v27, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q21, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_H2 v27, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.eq 2f + + ldr q22, [sp] + add sp, sp, x10 + QPEL_FILTER_H v26, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_H2 v27, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_UNI_W_HV_8 + subs w22, w22, #1 + b.hi 1b + +2: + QPEL_UNI_W_HV_END + ret +endfunc + +.macro QPEL_UNI_W_HV_16 + sshr v24.4s, v24.4s, #6 + sshr v25.4s, v25.4s, #6 + sshr v26.4s, v26.4s, #6 + sshr v27.4s, v27.4s, #6 + mul v24.4s, v24.4s, v28.4s + mul v25.4s, v25.4s, v28.4s + mul v26.4s, v26.4s, v28.4s + mul v27.4s, v27.4s, v28.4s + sqrshl v24.4s, v24.4s, v30.4s + sqrshl v25.4s, v25.4s, v30.4s + sqrshl v26.4s, v26.4s, v30.4s + sqrshl v27.4s, v27.4s, v30.4s + sqadd v24.4s, v24.4s, v29.4s + sqadd v25.4s, v25.4s, v29.4s + sqadd v26.4s, v26.4s, v29.4s + sqadd v27.4s, v27.4s, v29.4s + sqxtn v24.4h, v24.4s + sqxtn2 v24.8h, v25.4s + sqxtn v26.4h, v26.4s + sqxtn2 v26.8h, v27.4s + sqxtun v24.8b, v24.8h + sqxtun2 v24.16b, v26.8h + + st1 {v24.16b}, [x20], x21 +.endm + +function hevc_put_hevc_qpel_uni_w_hv16_8_end_neon + mov x11, sp + mov w12, w22 + mov x13, x20 + mov x14, sp +3: + ldp q16, q1, [x11] + add x11, x11, x10 + ldp q17, q2, [x11] + add x11, x11, x10 + ldp q18, q3, [x11] + add x11, x11, x10 + ldp q19, q4, [x11] + add x11, x11, x10 + ldp q20, q5, [x11] + add x11, x11, x10 + ldp q21, q6, [x11] + add x11, x11, x10 + ldp q22, q7, [x11] + add x11, x11, x10 +1: + ldp q23, q31, [x11] + add x11, x11, x10 + QPEL_FILTER_H v24, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_H2 v25, v16, v17, v18, v19, v20, v21, v22, v23 + QPEL_FILTER_H v26, v1, v2, v3, v4, v5, v6, v7, v31 + QPEL_FILTER_H2 v27, v1, v2, v3, v4, v5, v6, v7, v31 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q16, q1, [x11] + add x11, x11, x10 + QPEL_FILTER_H v24, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_H2 v25, v17, v18, v19, v20, v21, v22, v23, v16 + QPEL_FILTER_H v26, v2, v3, v4, v5, v6, v7, v31, v1 + QPEL_FILTER_H2 v27, v2, v3, v4, v5, v6, v7, v31, v1 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q17, q2, [x11] + add x11, x11, x10 + QPEL_FILTER_H v24, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_H2 v25, v18, v19, v20, v21, v22, v23, v16, v17 + QPEL_FILTER_H v26, v3, v4, v5, v6, v7, v31, v1, v2 + QPEL_FILTER_H2 v27, v3, v4, v5, v6, v7, v31, v1, v2 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q18, q3, [x11] + add x11, x11, x10 + QPEL_FILTER_H v24, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_H2 v25, v19, v20, v21, v22, v23, v16, v17, v18 + QPEL_FILTER_H v26, v4, v5, v6, v7, v31, v1, v2, v3 + QPEL_FILTER_H2 v27, v4, v5, v6, v7, v31, v1, v2, v3 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q19, q4, [x11] + add x11, x11, x10 + QPEL_FILTER_H v24, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_H2 v25, v20, v21, v22, v23, v16, v17, v18, v19 + QPEL_FILTER_H v26, v5, v6, v7, v31, v1, v2, v3, v4 + QPEL_FILTER_H2 v27, v5, v6, v7, v31, v1, v2, v3, v4 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q20, q5, [x11] + add x11, x11, x10 + QPEL_FILTER_H v24, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_H2 v25, v21, v22, v23, v16, v17, v18, v19, v20 + QPEL_FILTER_H v26, v6, v7, v31, v1, v2, v3, v4, v5 + QPEL_FILTER_H2 v27, v6, v7, v31, v1, v2, v3, v4, v5 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q21, q6, [x11] + add x11, x11, x10 + QPEL_FILTER_H v24, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_H2 v25, v22, v23, v16, v17, v18, v19, v20, v21 + QPEL_FILTER_H v26, v7, v31, v1, v2, v3, v4, v5, v6 + QPEL_FILTER_H2 v27, v7, v31, v1, v2, v3, v4, v5, v6 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.eq 2f + + ldp q22, q7, [x11] + add x11, x11, x10 + QPEL_FILTER_H v24, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_H2 v25, v23, v16, v17, v18, v19, v20, v21, v22 + QPEL_FILTER_H v26, v31, v1, v2, v3, v4, v5, v6, v7 + QPEL_FILTER_H2 v27, v31, v1, v2, v3, v4, v5, v6, v7 + QPEL_UNI_W_HV_16 + subs w22, w22, #1 + b.hi 1b +2: + subs w27, w27, #16 + add x11, x14, #32 + add x20, x13, #16 + mov w22, w12 + mov x14, x11 + mov x13, x20 + b.hi 3b + QPEL_UNI_W_HV_END + ret +endfunc + +.macro qpel_uni_w_hv suffix +function ff_hevc_put_hevc_qpel_uni_w_hv4_8_\suffix, export=1 + QPEL_UNI_W_HV_HEADER 4, \suffix + b hevc_put_hevc_qpel_uni_w_hv4_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_hv8_8_\suffix, export=1 + QPEL_UNI_W_HV_HEADER 8, \suffix + b hevc_put_hevc_qpel_uni_w_hv8_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_hv16_8_\suffix, export=1 + QPEL_UNI_W_HV_HEADER 16, \suffix + b hevc_put_hevc_qpel_uni_w_hv16_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_hv32_8_\suffix, export=1 + QPEL_UNI_W_HV_HEADER 32, \suffix + b hevc_put_hevc_qpel_uni_w_hv16_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_hv64_8_\suffix, export=1 + QPEL_UNI_W_HV_HEADER 64, \suffix + b hevc_put_hevc_qpel_uni_w_hv16_8_end_neon +endfunc +.endm + +qpel_uni_w_hv neon + +function hevc_put_hevc_qpel_bi_hv4_8_end_neon + mov x9, #(MAX_PB_SIZE * 2) + load_qpel_filterh x7, x6 + ld1 {v16.4h}, [sp], x9 + ld1 {v17.4h}, [sp], x9 + ld1 {v18.4h}, [sp], x9 + ld1 {v19.4h}, [sp], x9 + ld1 {v20.4h}, [sp], x9 + ld1 {v21.4h}, [sp], x9 + ld1 {v22.4h}, [sp], x9 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().4h}, [sp], x9 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sshr + ld1 {v5.4h}, [x4], x9 // src2 + saddw v1.4s, v1.4s, v5.4h + rshrn v1.4h, v1.4s, #7 + sqxtun v1.8b, v1.8h + subs w5, w5, #1 + st1 {v1.s}[0], [x0], x1 +.endm +1: calc_all +.purgem calc +2: mov sp, x14 + ret +endfunc + +function hevc_put_hevc_qpel_bi_hv6_8_end_neon + mov x9, #(MAX_PB_SIZE * 2) + load_qpel_filterh x7, x6 + sub x1, x1, #4 + ld1 {v16.8h}, [sp], x9 + ld1 {v17.8h}, [sp], x9 + ld1 {v18.8h}, [sp], x9 + ld1 {v19.8h}, [sp], x9 + ld1 {v20.8h}, [sp], x9 + ld1 {v21.8h}, [sp], x9 + ld1 {v22.8h}, [sp], x9 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().8h}, [sp], x9 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sshr + calc_qpelh2 v2, v2, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sshr + ld1 {v5.8h}, [x4], x9 // src2 + saddw v1.4s, v1.4s, v5.4h + saddw2 v2.4s, v2.4s, v5.8h + rshrn v1.4h, v1.4s, #7 + rshrn2 v1.8h, v2.4s, #7 + sqxtun v1.8b, v1.8h + st1 {v1.s}[0], [x0], #4 + subs w5, w5, #1 + st1 {v1.h}[2], [x0], x1 +.endm +1: calc_all +.purgem calc +2: mov sp, x14 + ret +endfunc + +function hevc_put_hevc_qpel_bi_hv8_8_end_neon + mov x9, #(MAX_PB_SIZE * 2) + load_qpel_filterh x7, x6 + ld1 {v16.8h}, [sp], x9 + ld1 {v17.8h}, [sp], x9 + ld1 {v18.8h}, [sp], x9 + ld1 {v19.8h}, [sp], x9 + ld1 {v20.8h}, [sp], x9 + ld1 {v21.8h}, [sp], x9 + ld1 {v22.8h}, [sp], x9 +.macro calc tmp, src0, src1, src2, src3, src4, src5, src6, src7 + ld1 {\tmp\().8h}, [sp], x9 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sshr + calc_qpelh2 v2, v2, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sshr + ld1 {v5.8h}, [x4], x9 // src2 + saddw v1.4s, v1.4s, v5.4h + saddw2 v2.4s, v2.4s, v5.8h + rshrn v1.4h, v1.4s, #7 + rshrn2 v1.8h, v2.4s, #7 + sqxtun v1.8b, v1.8h + subs w5, w5, #1 + st1 {v1.8b}, [x0], x1 +.endm +1: calc_all +.purgem calc +2: mov sp, x14 + ret +endfunc + +function hevc_put_hevc_qpel_bi_hv16_8_end_neon + load_qpel_filterh x7, x8 + mov x9, #(MAX_PB_SIZE * 2) + mov x10, x6 +0: mov x8, sp // src + ld1 {v16.8h, v17.8h}, [x8], x9 + mov w11, w5 // height + ld1 {v18.8h, v19.8h}, [x8], x9 + mov x12, x4 // src2 + ld1 {v20.8h, v21.8h}, [x8], x9 + mov x7, x0 // dst + ld1 {v22.8h, v23.8h}, [x8], x9 + ld1 {v24.8h, v25.8h}, [x8], x9 + ld1 {v26.8h, v27.8h}, [x8], x9 + ld1 {v28.8h, v29.8h}, [x8], x9 +.macro calc tmp0, tmp1, src0, src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15 + ld1 {\tmp0\().8h, \tmp1\().8h}, [x8], x9 + calc_qpelh v1, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sshr + calc_qpelh2 v2, v2, \src0, \src1, \src2, \src3, \src4, \src5, \src6, \src7, sshr + calc_qpelh v3, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15, sshr + calc_qpelh2 v4, v4, \src8, \src9, \src10, \src11, \src12, \src13, \src14, \src15, sshr + ld1 {v5.8h, v6.8h}, [x12], x9 // src2 + saddw v1.4s, v1.4s, v5.4h + saddw2 v2.4s, v2.4s, v5.8h + saddw v3.4s, v3.4s, v6.4h + saddw2 v4.4s, v4.4s, v6.8h + rshrn v1.4h, v1.4s, #7 + rshrn2 v1.8h, v2.4s, #7 + rshrn v2.4h, v3.4s, #7 + rshrn2 v2.8h, v4.4s, #7 + sqxtun v1.8b, v1.8h + sqxtun2 v1.16b, v2.8h + subs x11, x11, #1 + st1 {v1.16b}, [x7], x1 +.endm +1: calc_all2 +.purgem calc +2: add x0, x0, #16 + add sp, sp, #32 + subs x10, x10, #16 + add x4, x4, #32 + b.ne 0b + mov sp, x14 + ret +endfunc + +.macro qpel_bi_hv suffix +function ff_hevc_put_hevc_qpel_bi_hv4_8_\suffix, export=1 + add w10, w5, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-64]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + str x14, [sp, #48] + sub x1, x2, x3, lsl #1 + sub x1, x1, x3 + add x0, sp, #64 + mov x2, x3 + add w3, w5, #7 + mov x4, x6 + bl X(ff_hevc_put_hevc_qpel_h4_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x14, [sp, #48] + ldp x7, x30, [sp], #64 + b hevc_put_hevc_qpel_bi_hv4_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_bi_hv6_8_\suffix, export=1 + add w10, w5, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-64]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + str x14, [sp, #48] + sub x1, x2, x3, lsl #1 + sub x1, x1, x3 + add x0, sp, #64 + mov x2, x3 + add x3, x5, #7 + mov x4, x6 + bl X(ff_hevc_put_hevc_qpel_h6_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x14, [sp, #48] + ldp x7, x30, [sp], #64 + b hevc_put_hevc_qpel_bi_hv6_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_bi_hv8_8_\suffix, export=1 + add w10, w5, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-64]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + str x14, [sp, #48] + sub x1, x2, x3, lsl #1 + sub x1, x1, x3 + add x0, sp, #64 + mov x2, x3 + add x3, x5, #7 + mov x4, x6 + bl X(ff_hevc_put_hevc_qpel_h8_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x14, [sp, #48] + ldp x7, x30, [sp], #64 + b hevc_put_hevc_qpel_bi_hv8_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_bi_hv12_8_\suffix, export=1 + stp x6, x7, [sp, #-80]! + stp x4, x5, [sp, #16] + stp x2, x3, [sp, #32] + stp x0, x1, [sp, #48] + str x30, [sp, #64] + bl X(ff_hevc_put_hevc_qpel_bi_hv8_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x2, x3, [sp, #32] + ldp x0, x1, [sp, #48] + ldp x6, x7, [sp], #64 + add x4, x4, #16 + add x2, x2, #8 + add x0, x0, #8 + bl X(ff_hevc_put_hevc_qpel_bi_hv4_8_\suffix) + ldr x30, [sp], #16 + ret +endfunc + +function ff_hevc_put_hevc_qpel_bi_hv16_8_\suffix, export=1 + add w10, w5, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-64]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + str x14, [sp, #48] + add x0, sp, #64 + sub x1, x2, x3, lsl #1 + sub x1, x1, x3 + mov x2, x3 + add w3, w5, #7 + mov x4, x6 + bl X(ff_hevc_put_hevc_qpel_h16_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x14, [sp, #48] + ldp x7, x30, [sp], #64 + mov x6, #16 // width + b hevc_put_hevc_qpel_bi_hv16_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_bi_hv24_8_\suffix, export=1 + stp x6, x7, [sp, #-80]! + stp x4, x5, [sp, #16] + stp x2, x3, [sp, #32] + stp x0, x1, [sp, #48] + str x30, [sp, #64] + bl X(ff_hevc_put_hevc_qpel_bi_hv16_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x2, x3, [sp, #32] + ldp x0, x1, [sp, #48] + ldp x6, x7, [sp], #64 + add x4, x4, #32 + add x2, x2, #16 + add x0, x0, #16 + bl X(ff_hevc_put_hevc_qpel_bi_hv8_8_\suffix) + ldr x30, [sp], #16 + ret +endfunc + +function ff_hevc_put_hevc_qpel_bi_hv32_8_\suffix, export=1 + add w10, w5, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-64]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + str x14, [sp, #48] + add x0, sp, #64 + sub x1, x2, x3, lsl #1 + mov x2, x3 + sub x1, x1, x3 + add w3, w5, #7 + mov x4, x6 + mov w6, #32 + bl X(ff_hevc_put_hevc_qpel_h32_8_\suffix) + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x14, [sp, #48] + ldp x7, x30, [sp], #64 + mov x6, #32 // width + b hevc_put_hevc_qpel_bi_hv16_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_bi_hv48_8_\suffix, export=1 + add w10, w5, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-64]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + str x14, [sp, #48] + add x0, sp, #64 + sub x1, x2, x3, lsl #1 + mov x2, x3 + sub x1, x1, x3 + add w3, w5, #7 + mov x4, x6 +.ifc \suffix, neon + mov w6, #48 + bl X(ff_hevc_put_hevc_qpel_h32_8_\suffix) +.else + bl X(ff_hevc_put_hevc_qpel_h48_8_\suffix) +.endif + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x14, [sp, #48] + ldp x7, x30, [sp], #64 + mov x6, #48 // width + b hevc_put_hevc_qpel_bi_hv16_8_end_neon +endfunc + +function ff_hevc_put_hevc_qpel_bi_hv64_8_\suffix, export=1 + add w10, w5, #8 + lsl x10, x10, #7 + mov x14, sp + sub sp, sp, x10 // tmp_array + stp x7, x30, [sp, #-64]! + stp x4, x5, [sp, #16] + stp x0, x1, [sp, #32] + str x14, [sp, #48] + add x0, sp, #64 + sub x1, x2, x3, lsl #1 + mov x2, x3 + sub x1, x1, x3 + add w3, w5, #7 + mov x4, x6 +.ifc \suffix, neon + mov w6, #64 + bl X(ff_hevc_put_hevc_qpel_h32_8_\suffix) +.else + bl X(ff_hevc_put_hevc_qpel_h64_8_\suffix) +.endif + ldp x4, x5, [sp, #16] + ldp x0, x1, [sp, #32] + ldr x14, [sp, #48] + ldp x7, x30, [sp], #64 + mov x6, #64 // width + b hevc_put_hevc_qpel_bi_hv16_8_end_neon +endfunc +.endm + +qpel_bi_hv neon + +#if HAVE_I8MM +ENABLE_I8MM + +qpel_uni_w_hv neon_i8mm + +qpel_bi_hv neon_i8mm + +DISABLE_I8MM +#endif // HAVE_I8MM diff --git a/libavcodec/aarch64/hpeldsp_neon.S b/libavcodec/aarch64/hpeldsp_neon.S index a491c173bbc..e7c1549c406 100644 --- a/libavcodec/aarch64/hpeldsp_neon.S +++ b/libavcodec/aarch64/hpeldsp_neon.S @@ -26,295 +26,295 @@ .if \avg mov x12, x0 .endif -1: ld1 {v0.16B}, [x1], x2 - ld1 {v1.16B}, [x1], x2 - ld1 {v2.16B}, [x1], x2 - ld1 {v3.16B}, [x1], x2 +1: ld1 {v0.16b}, [x1], x2 + ld1 {v1.16b}, [x1], x2 + ld1 {v2.16b}, [x1], x2 + ld1 {v3.16b}, [x1], x2 .if \avg - ld1 {v4.16B}, [x12], x2 - urhadd v0.16B, v0.16B, v4.16B - ld1 {v5.16B}, [x12], x2 - urhadd v1.16B, v1.16B, v5.16B - ld1 {v6.16B}, [x12], x2 - urhadd v2.16B, v2.16B, v6.16B - ld1 {v7.16B}, [x12], x2 - urhadd v3.16B, v3.16B, v7.16B + ld1 {v4.16b}, [x12], x2 + urhadd v0.16b, v0.16b, v4.16b + ld1 {v5.16b}, [x12], x2 + urhadd v1.16b, v1.16b, v5.16b + ld1 {v6.16b}, [x12], x2 + urhadd v2.16b, v2.16b, v6.16b + ld1 {v7.16b}, [x12], x2 + urhadd v3.16b, v3.16b, v7.16b .endif subs w3, w3, #4 - st1 {v0.16B}, [x0], x2 - st1 {v1.16B}, [x0], x2 - st1 {v2.16B}, [x0], x2 - st1 {v3.16B}, [x0], x2 + st1 {v0.16b}, [x0], x2 + st1 {v1.16b}, [x0], x2 + st1 {v2.16b}, [x0], x2 + st1 {v3.16b}, [x0], x2 b.ne 1b ret .endm .macro pixels16_x2 rnd=1, avg=0 -1: ld1 {v0.16B, v1.16B}, [x1], x2 - ld1 {v2.16B, v3.16B}, [x1], x2 +1: ld1 {v0.16b, v1.16b}, [x1], x2 + ld1 {v2.16b, v3.16b}, [x1], x2 subs w3, w3, #2 - ext v1.16B, v0.16B, v1.16B, #1 - avg v0.16B, v0.16B, v1.16B - ext v3.16B, v2.16B, v3.16B, #1 - avg v2.16B, v2.16B, v3.16B + ext v1.16b, v0.16b, v1.16b, #1 + avg v0.16b, v0.16b, v1.16b + ext v3.16b, v2.16b, v3.16b, #1 + avg v2.16b, v2.16b, v3.16b .if \avg - ld1 {v1.16B}, [x0], x2 - ld1 {v3.16B}, [x0] - urhadd v0.16B, v0.16B, v1.16B - urhadd v2.16B, v2.16B, v3.16B + ld1 {v1.16b}, [x0], x2 + ld1 {v3.16b}, [x0] + urhadd v0.16b, v0.16b, v1.16b + urhadd v2.16b, v2.16b, v3.16b sub x0, x0, x2 .endif - st1 {v0.16B}, [x0], x2 - st1 {v2.16B}, [x0], x2 + st1 {v0.16b}, [x0], x2 + st1 {v2.16b}, [x0], x2 b.ne 1b ret .endm .macro pixels16_y2 rnd=1, avg=0 sub w3, w3, #2 - ld1 {v0.16B}, [x1], x2 - ld1 {v1.16B}, [x1], x2 + ld1 {v0.16b}, [x1], x2 + ld1 {v1.16b}, [x1], x2 1: subs w3, w3, #2 - avg v2.16B, v0.16B, v1.16B - ld1 {v0.16B}, [x1], x2 - avg v3.16B, v0.16B, v1.16B - ld1 {v1.16B}, [x1], x2 + avg v2.16b, v0.16b, v1.16b + ld1 {v0.16b}, [x1], x2 + avg v3.16b, v0.16b, v1.16b + ld1 {v1.16b}, [x1], x2 .if \avg - ld1 {v4.16B}, [x0], x2 - ld1 {v5.16B}, [x0] - urhadd v2.16B, v2.16B, v4.16B - urhadd v3.16B, v3.16B, v5.16B + ld1 {v4.16b}, [x0], x2 + ld1 {v5.16b}, [x0] + urhadd v2.16b, v2.16b, v4.16b + urhadd v3.16b, v3.16b, v5.16b sub x0, x0, x2 .endif - st1 {v2.16B}, [x0], x2 - st1 {v3.16B}, [x0], x2 + st1 {v2.16b}, [x0], x2 + st1 {v3.16b}, [x0], x2 b.ne 1b - avg v2.16B, v0.16B, v1.16B - ld1 {v0.16B}, [x1], x2 - avg v3.16B, v0.16B, v1.16B + avg v2.16b, v0.16b, v1.16b + ld1 {v0.16b}, [x1], x2 + avg v3.16b, v0.16b, v1.16b .if \avg - ld1 {v4.16B}, [x0], x2 - ld1 {v5.16B}, [x0] - urhadd v2.16B, v2.16B, v4.16B - urhadd v3.16B, v3.16B, v5.16B + ld1 {v4.16b}, [x0], x2 + ld1 {v5.16b}, [x0] + urhadd v2.16b, v2.16b, v4.16b + urhadd v3.16b, v3.16b, v5.16b sub x0, x0, x2 .endif - st1 {v2.16B}, [x0], x2 - st1 {v3.16B}, [x0], x2 + st1 {v2.16b}, [x0], x2 + st1 {v3.16b}, [x0], x2 ret .endm .macro pixels16_xy2 rnd=1, avg=0 sub w3, w3, #2 - ld1 {v0.16B, v1.16B}, [x1], x2 - ld1 {v4.16B, v5.16B}, [x1], x2 + ld1 {v0.16b, v1.16b}, [x1], x2 + ld1 {v4.16b, v5.16b}, [x1], x2 NRND movi v26.8H, #1 - ext v1.16B, v0.16B, v1.16B, #1 - ext v5.16B, v4.16B, v5.16B, #1 - uaddl v16.8H, v0.8B, v1.8B - uaddl2 v20.8H, v0.16B, v1.16B - uaddl v18.8H, v4.8B, v5.8B - uaddl2 v22.8H, v4.16B, v5.16B + ext v1.16b, v0.16b, v1.16b, #1 + ext v5.16b, v4.16b, v5.16b, #1 + uaddl v16.8h, v0.8b, v1.8b + uaddl2 v20.8h, v0.16b, v1.16b + uaddl v18.8h, v4.8b, v5.8b + uaddl2 v22.8h, v4.16b, v5.16b 1: subs w3, w3, #2 - ld1 {v0.16B, v1.16B}, [x1], x2 - add v24.8H, v16.8H, v18.8H + ld1 {v0.16b, v1.16b}, [x1], x2 + add v24.8h, v16.8h, v18.8h NRND add v24.8H, v24.8H, v26.8H - ext v30.16B, v0.16B, v1.16B, #1 - add v1.8H, v20.8H, v22.8H - mshrn v28.8B, v24.8H, #2 + ext v30.16b, v0.16b, v1.16b, #1 + add v1.8h, v20.8h, v22.8h + mshrn v28.8b, v24.8h, #2 NRND add v1.8H, v1.8H, v26.8H - mshrn2 v28.16B, v1.8H, #2 + mshrn2 v28.16b, v1.8h, #2 .if \avg - ld1 {v16.16B}, [x0] - urhadd v28.16B, v28.16B, v16.16B + ld1 {v16.16b}, [x0] + urhadd v28.16b, v28.16b, v16.16b .endif - uaddl v16.8H, v0.8B, v30.8B - ld1 {v2.16B, v3.16B}, [x1], x2 - uaddl2 v20.8H, v0.16B, v30.16B - st1 {v28.16B}, [x0], x2 - add v24.8H, v16.8H, v18.8H + uaddl v16.8h, v0.8b, v30.8b + ld1 {v2.16b, v3.16b}, [x1], x2 + uaddl2 v20.8h, v0.16b, v30.16b + st1 {v28.16b}, [x0], x2 + add v24.8h, v16.8h, v18.8h NRND add v24.8H, v24.8H, v26.8H - ext v3.16B, v2.16B, v3.16B, #1 - add v0.8H, v20.8H, v22.8H - mshrn v30.8B, v24.8H, #2 + ext v3.16b, v2.16b, v3.16b, #1 + add v0.8h, v20.8h, v22.8h + mshrn v30.8b, v24.8h, #2 NRND add v0.8H, v0.8H, v26.8H - mshrn2 v30.16B, v0.8H, #2 + mshrn2 v30.16b, v0.8h, #2 .if \avg - ld1 {v18.16B}, [x0] - urhadd v30.16B, v30.16B, v18.16B + ld1 {v18.16b}, [x0] + urhadd v30.16b, v30.16b, v18.16b .endif - uaddl v18.8H, v2.8B, v3.8B - uaddl2 v22.8H, v2.16B, v3.16B - st1 {v30.16B}, [x0], x2 + uaddl v18.8h, v2.8b, v3.8b + uaddl2 v22.8h, v2.16b, v3.16b + st1 {v30.16b}, [x0], x2 b.gt 1b - ld1 {v0.16B, v1.16B}, [x1], x2 - add v24.8H, v16.8H, v18.8H + ld1 {v0.16b, v1.16b}, [x1], x2 + add v24.8h, v16.8h, v18.8h NRND add v24.8H, v24.8H, v26.8H - ext v30.16B, v0.16B, v1.16B, #1 - add v1.8H, v20.8H, v22.8H - mshrn v28.8B, v24.8H, #2 + ext v30.16b, v0.16b, v1.16b, #1 + add v1.8h, v20.8h, v22.8h + mshrn v28.8b, v24.8h, #2 NRND add v1.8H, v1.8H, v26.8H - mshrn2 v28.16B, v1.8H, #2 + mshrn2 v28.16b, v1.8h, #2 .if \avg - ld1 {v16.16B}, [x0] - urhadd v28.16B, v28.16B, v16.16B + ld1 {v16.16b}, [x0] + urhadd v28.16b, v28.16b, v16.16b .endif - uaddl v16.8H, v0.8B, v30.8B - uaddl2 v20.8H, v0.16B, v30.16B - st1 {v28.16B}, [x0], x2 - add v24.8H, v16.8H, v18.8H + uaddl v16.8h, v0.8b, v30.8b + uaddl2 v20.8h, v0.16b, v30.16b + st1 {v28.16b}, [x0], x2 + add v24.8h, v16.8h, v18.8h NRND add v24.8H, v24.8H, v26.8H - add v0.8H, v20.8H, v22.8H - mshrn v30.8B, v24.8H, #2 + add v0.8h, v20.8h, v22.8h + mshrn v30.8b, v24.8h, #2 NRND add v0.8H, v0.8H, v26.8H - mshrn2 v30.16B, v0.8H, #2 + mshrn2 v30.16b, v0.8h, #2 .if \avg - ld1 {v18.16B}, [x0] - urhadd v30.16B, v30.16B, v18.16B + ld1 {v18.16b}, [x0] + urhadd v30.16b, v30.16b, v18.16b .endif - st1 {v30.16B}, [x0], x2 + st1 {v30.16b}, [x0], x2 ret .endm .macro pixels8 rnd=1, avg=0 -1: ld1 {v0.8B}, [x1], x2 - ld1 {v1.8B}, [x1], x2 - ld1 {v2.8B}, [x1], x2 - ld1 {v3.8B}, [x1], x2 +1: ld1 {v0.8b}, [x1], x2 + ld1 {v1.8b}, [x1], x2 + ld1 {v2.8b}, [x1], x2 + ld1 {v3.8b}, [x1], x2 .if \avg - ld1 {v4.8B}, [x0], x2 - urhadd v0.8B, v0.8B, v4.8B - ld1 {v5.8B}, [x0], x2 - urhadd v1.8B, v1.8B, v5.8B - ld1 {v6.8B}, [x0], x2 - urhadd v2.8B, v2.8B, v6.8B - ld1 {v7.8B}, [x0], x2 - urhadd v3.8B, v3.8B, v7.8B + ld1 {v4.8b}, [x0], x2 + urhadd v0.8b, v0.8b, v4.8b + ld1 {v5.8b}, [x0], x2 + urhadd v1.8b, v1.8b, v5.8b + ld1 {v6.8b}, [x0], x2 + urhadd v2.8b, v2.8b, v6.8b + ld1 {v7.8b}, [x0], x2 + urhadd v3.8b, v3.8b, v7.8b sub x0, x0, x2, lsl #2 .endif subs w3, w3, #4 - st1 {v0.8B}, [x0], x2 - st1 {v1.8B}, [x0], x2 - st1 {v2.8B}, [x0], x2 - st1 {v3.8B}, [x0], x2 + st1 {v0.8b}, [x0], x2 + st1 {v1.8b}, [x0], x2 + st1 {v2.8b}, [x0], x2 + st1 {v3.8b}, [x0], x2 b.ne 1b ret .endm .macro pixels8_x2 rnd=1, avg=0 -1: ld1 {v0.8B, v1.8B}, [x1], x2 - ext v1.8B, v0.8B, v1.8B, #1 - ld1 {v2.8B, v3.8B}, [x1], x2 - ext v3.8B, v2.8B, v3.8B, #1 +1: ld1 {v0.8b, v1.8b}, [x1], x2 + ext v1.8b, v0.8b, v1.8b, #1 + ld1 {v2.8b, v3.8b}, [x1], x2 + ext v3.8b, v2.8b, v3.8b, #1 subs w3, w3, #2 - avg v0.8B, v0.8B, v1.8B - avg v2.8B, v2.8B, v3.8B + avg v0.8b, v0.8b, v1.8b + avg v2.8b, v2.8b, v3.8b .if \avg - ld1 {v4.8B}, [x0], x2 - ld1 {v5.8B}, [x0] - urhadd v0.8B, v0.8B, v4.8B - urhadd v2.8B, v2.8B, v5.8B + ld1 {v4.8b}, [x0], x2 + ld1 {v5.8b}, [x0] + urhadd v0.8b, v0.8b, v4.8b + urhadd v2.8b, v2.8b, v5.8b sub x0, x0, x2 .endif - st1 {v0.8B}, [x0], x2 - st1 {v2.8B}, [x0], x2 + st1 {v0.8b}, [x0], x2 + st1 {v2.8b}, [x0], x2 b.ne 1b ret .endm .macro pixels8_y2 rnd=1, avg=0 sub w3, w3, #2 - ld1 {v0.8B}, [x1], x2 - ld1 {v1.8B}, [x1], x2 + ld1 {v0.8b}, [x1], x2 + ld1 {v1.8b}, [x1], x2 1: subs w3, w3, #2 - avg v4.8B, v0.8B, v1.8B - ld1 {v0.8B}, [x1], x2 - avg v5.8B, v0.8B, v1.8B - ld1 {v1.8B}, [x1], x2 + avg v4.8b, v0.8b, v1.8b + ld1 {v0.8b}, [x1], x2 + avg v5.8b, v0.8b, v1.8b + ld1 {v1.8b}, [x1], x2 .if \avg - ld1 {v2.8B}, [x0], x2 - ld1 {v3.8B}, [x0] - urhadd v4.8B, v4.8B, v2.8B - urhadd v5.8B, v5.8B, v3.8B + ld1 {v2.8b}, [x0], x2 + ld1 {v3.8b}, [x0] + urhadd v4.8b, v4.8b, v2.8b + urhadd v5.8b, v5.8b, v3.8b sub x0, x0, x2 .endif - st1 {v4.8B}, [x0], x2 - st1 {v5.8B}, [x0], x2 + st1 {v4.8b}, [x0], x2 + st1 {v5.8b}, [x0], x2 b.ne 1b - avg v4.8B, v0.8B, v1.8B - ld1 {v0.8B}, [x1], x2 - avg v5.8B, v0.8B, v1.8B + avg v4.8b, v0.8b, v1.8b + ld1 {v0.8b}, [x1], x2 + avg v5.8b, v0.8b, v1.8b .if \avg - ld1 {v2.8B}, [x0], x2 - ld1 {v3.8B}, [x0] - urhadd v4.8B, v4.8B, v2.8B - urhadd v5.8B, v5.8B, v3.8B + ld1 {v2.8b}, [x0], x2 + ld1 {v3.8b}, [x0] + urhadd v4.8b, v4.8b, v2.8b + urhadd v5.8b, v5.8b, v3.8b sub x0, x0, x2 .endif - st1 {v4.8B}, [x0], x2 - st1 {v5.8B}, [x0], x2 + st1 {v4.8b}, [x0], x2 + st1 {v5.8b}, [x0], x2 ret .endm .macro pixels8_xy2 rnd=1, avg=0 sub w3, w3, #2 - ld1 {v0.16B}, [x1], x2 - ld1 {v1.16B}, [x1], x2 + ld1 {v0.16b}, [x1], x2 + ld1 {v1.16b}, [x1], x2 NRND movi v19.8H, #1 - ext v4.16B, v0.16B, v4.16B, #1 - ext v6.16B, v1.16B, v6.16B, #1 - uaddl v16.8H, v0.8B, v4.8B - uaddl v17.8H, v1.8B, v6.8B + ext v4.16b, v0.16b, v4.16b, #1 + ext v6.16b, v1.16b, v6.16b, #1 + uaddl v16.8h, v0.8b, v4.8b + uaddl v17.8h, v1.8b, v6.8b 1: subs w3, w3, #2 - ld1 {v0.16B}, [x1], x2 - add v18.8H, v16.8H, v17.8H - ext v4.16B, v0.16B, v4.16B, #1 + ld1 {v0.16b}, [x1], x2 + add v18.8h, v16.8h, v17.8h + ext v4.16b, v0.16b, v4.16b, #1 NRND add v18.8H, v18.8H, v19.8H - uaddl v16.8H, v0.8B, v4.8B - mshrn v5.8B, v18.8H, #2 - ld1 {v1.16B}, [x1], x2 - add v18.8H, v16.8H, v17.8H + uaddl v16.8h, v0.8b, v4.8b + mshrn v5.8b, v18.8h, #2 + ld1 {v1.16b}, [x1], x2 + add v18.8h, v16.8h, v17.8h .if \avg - ld1 {v7.8B}, [x0] - urhadd v5.8B, v5.8B, v7.8B + ld1 {v7.8b}, [x0] + urhadd v5.8b, v5.8b, v7.8b .endif NRND add v18.8H, v18.8H, v19.8H - st1 {v5.8B}, [x0], x2 - mshrn v7.8B, v18.8H, #2 + st1 {v5.8b}, [x0], x2 + mshrn v7.8b, v18.8h, #2 .if \avg - ld1 {v5.8B}, [x0] - urhadd v7.8B, v7.8B, v5.8B + ld1 {v5.8b}, [x0] + urhadd v7.8b, v7.8b, v5.8b .endif - ext v6.16B, v1.16B, v6.16B, #1 - uaddl v17.8H, v1.8B, v6.8B - st1 {v7.8B}, [x0], x2 + ext v6.16b, v1.16b, v6.16b, #1 + uaddl v17.8h, v1.8b, v6.8b + st1 {v7.8b}, [x0], x2 b.gt 1b - ld1 {v0.16B}, [x1], x2 - add v18.8H, v16.8H, v17.8H - ext v4.16B, v0.16B, v4.16B, #1 + ld1 {v0.16b}, [x1], x2 + add v18.8h, v16.8h, v17.8h + ext v4.16b, v0.16b, v4.16b, #1 NRND add v18.8H, v18.8H, v19.8H - uaddl v16.8H, v0.8B, v4.8B - mshrn v5.8B, v18.8H, #2 - add v18.8H, v16.8H, v17.8H + uaddl v16.8h, v0.8b, v4.8b + mshrn v5.8b, v18.8h, #2 + add v18.8h, v16.8h, v17.8h .if \avg - ld1 {v7.8B}, [x0] - urhadd v5.8B, v5.8B, v7.8B + ld1 {v7.8b}, [x0] + urhadd v5.8b, v5.8b, v7.8b .endif NRND add v18.8H, v18.8H, v19.8H - st1 {v5.8B}, [x0], x2 - mshrn v7.8B, v18.8H, #2 + st1 {v5.8b}, [x0], x2 + mshrn v7.8b, v18.8h, #2 .if \avg - ld1 {v5.8B}, [x0] - urhadd v7.8B, v7.8B, v5.8B + ld1 {v5.8b}, [x0] + urhadd v7.8b, v7.8b, v5.8b .endif - st1 {v7.8B}, [x0], x2 + st1 {v7.8b}, [x0], x2 ret .endm diff --git a/libavcodec/aarch64/mdct_neon.S b/libavcodec/aarch64/mdct_neon.S deleted file mode 100644 index 98b09bf1abe..00000000000 --- a/libavcodec/aarch64/mdct_neon.S +++ /dev/null @@ -1,326 +0,0 @@ -/* - * AArch64 NEON optimised MDCT - * Copyright (c) 2009 Mans Rullgard - * Copyright (c) 2014 Janne Grunau - * - * 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/aarch64/asm.S" - -function ff_imdct_half_neon, export=1 - stp x19, x20, [sp, #-32]! - AARCH64_SIGN_LINK_REGISTER - str x30, [sp, #16] - mov x12, #1 - ldr w14, [x0, #28] // mdct_bits - ldr x4, [x0, #32] // tcos - ldr x3, [x0, #8] // revtab - lsl x12, x12, x14 // n = 1 << nbits - lsr x14, x12, #2 // n4 = n >> 2 - add x7, x2, x12, lsl #1 - mov x12, #-16 - sub x7, x7, #16 - - ld2 {v16.2s,v17.2s}, [x7], x12 // d16=x,n1 d17=x,n0 - ld2 {v0.2s,v1.2s}, [x2], #16 // d0 =m0,x d1 =m1,x - rev64 v17.2s, v17.2s - ld2 {v2.2s,v3.2s}, [x4], #16 // d2=c0,c1 d3=s0,s2 - fmul v6.2s, v17.2s, v2.2s - fmul v7.2s, v0.2s, v2.2s -1: - subs x14, x14, #2 - ldr w6, [x3], #4 - fmul v4.2s, v0.2s, v3.2s - fmul v5.2s, v17.2s, v3.2s - fsub v4.2s, v6.2s, v4.2s - fadd v5.2s, v5.2s, v7.2s - ubfm x8, x6, #16, #31 - ubfm x6, x6, #0, #15 - add x8, x1, x8, lsl #3 - add x6, x1, x6, lsl #3 - b.eq 2f - ld2 {v16.2s,v17.2s}, [x7], x12 - ld2 {v0.2s,v1.2s}, [x2], #16 - rev64 v17.2s, v17.2s - ld2 {v2.2s,v3.2s}, [x4], #16 // d2=c0,c1 d3=s0,s2 - fmul v6.2s, v17.2s, v2.2s - fmul v7.2s, v0.2s, v2.2s - st2 {v4.s,v5.s}[0], [x6] - st2 {v4.s,v5.s}[1], [x8] - b 1b -2: - st2 {v4.s,v5.s}[0], [x6] - st2 {v4.s,v5.s}[1], [x8] - - mov x19, x0 - mov x20, x1 - bl X(ff_fft_calc_neon) - - mov x12, #1 - ldr w14, [x19, #28] // mdct_bits - ldr x4, [x19, #32] // tcos - lsl x12, x12, x14 // n = 1 << nbits - lsr x14, x12, #3 // n8 = n >> 3 - - add x4, x4, x14, lsl #3 - add x6, x20, x14, lsl #3 - sub x1, x4, #16 - sub x3, x6, #16 - - mov x7, #-16 - mov x8, x6 - mov x0, x3 - - ld2 {v0.2s,v1.2s}, [x3], x7 // d0 =i1,r1 d1 =i0,r0 - ld2 {v20.2s,v21.2s},[x6], #16 // d20=i2,r2 d21=i3,r3 - ld2 {v16.2s,v17.2s},[x1], x7 // d16=c1,c0 d18=s1,s0 -3: - subs x14, x14, #2 - fmul v7.2s, v0.2s, v17.2s - ld2 {v18.2s,v19.2s},[x4], #16 // d17=c2,c3 d19=s2,s3 - fmul v4.2s, v1.2s, v17.2s - fmul v6.2s, v21.2s, v19.2s - fmul v5.2s, v20.2s, v19.2s - fmul v22.2s, v1.2s, v16.2s - fmul v23.2s, v21.2s, v18.2s - fmul v24.2s, v0.2s, v16.2s - fmul v25.2s, v20.2s, v18.2s - fadd v7.2s, v7.2s, v22.2s - fadd v5.2s, v5.2s, v23.2s - fsub v4.2s, v4.2s, v24.2s - fsub v6.2s, v6.2s, v25.2s - b.eq 4f - ld2 {v0.2s,v1.2s}, [x3], x7 - ld2 {v20.2s,v21.2s},[x6], #16 - ld2 {v16.2s,v17.2s},[x1], x7 // d16=c1,c0 d18=s1,s0 - rev64 v5.2s, v5.2s - rev64 v7.2s, v7.2s - st2 {v4.2s,v5.2s}, [x0], x7 - st2 {v6.2s,v7.2s}, [x8], #16 - b 3b -4: - rev64 v5.2s, v5.2s - rev64 v7.2s, v7.2s - st2 {v4.2s,v5.2s}, [x0] - st2 {v6.2s,v7.2s}, [x8] - - ldr x30, [sp, #16] - AARCH64_VALIDATE_LINK_REGISTER - ldp x19, x20, [sp], #32 - - ret -endfunc - -function ff_imdct_calc_neon, export=1 - stp x19, x20, [sp, #-32]! - AARCH64_SIGN_LINK_REGISTER - str x30, [sp, #16] - ldr w3, [x0, #28] // mdct_bits - mov x19, #1 - mov x20, x1 - lsl x19, x19, x3 - add x1, x1, x19 - - bl X(ff_imdct_half_neon) - - add x0, x20, x19, lsl #2 - add x1, x20, x19, lsl #1 - sub x0, x0, #8 - sub x2, x1, #16 - mov x3, #-16 - mov x6, #-8 -1: - ld1 {v0.4s}, [x2], x3 - prfum pldl1keep, [x0, #-16] - rev64 v0.4s, v0.4s - ld1 {v2.2s,v3.2s}, [x1], #16 - fneg v4.4s, v0.4s - prfum pldl1keep, [x2, #-16] - rev64 v2.2s, v2.2s - rev64 v3.2s, v3.2s - ext v4.16b, v4.16b, v4.16b, #8 - st1 {v2.2s}, [x0], x6 - st1 {v3.2s}, [x0], x6 - st1 {v4.4s}, [x20], #16 - subs x19, x19, #16 - b.gt 1b - - ldr x30, [sp, #16] - AARCH64_VALIDATE_LINK_REGISTER - ldp x19, x20, [sp], #32 - - ret -endfunc - - -function ff_mdct_calc_neon, export=1 - stp x19, x20, [sp, #-32]! - AARCH64_SIGN_LINK_REGISTER - str x30, [sp, #16] - - mov x12, #1 - ldr w14, [x0, #28] // mdct_bits - ldr x4, [x0, #32] // tcos - ldr x3, [x0, #8] // revtab - lsl x14, x12, x14 // n = 1 << nbits - add x7, x2, x14 // in4u - sub x9, x7, #16 // in4d - add x2, x7, x14, lsl #1 // in3u - add x8, x9, x14, lsl #1 // in3d - add x5, x4, x14, lsl #1 - sub x5, x5, #16 - sub x3, x3, #4 - mov x12, #-16 - lsr x13, x14, #1 - - ld2 {v16.2s,v17.2s}, [x9], x12 // in0u0,in0u1 in4d1,in4d0 - ld2 {v18.2s,v19.2s}, [x8], x12 // in2u0,in2u1 in3d1,in3d0 - ld2 {v0.2s, v1.2s}, [x7], #16 // in4u0,in4u1 in2d1,in2d0 - rev64 v17.2s, v17.2s // in4d0,in4d1 in3d0,in3d1 - rev64 v19.2s, v19.2s // in4d0,in4d1 in3d0,in3d1 - ld2 {v2.2s, v3.2s}, [x2], #16 // in3u0,in3u1 in1d1,in1d0 - fsub v0.2s, v17.2s, v0.2s // in4d-in4u I - ld2 {v20.2s,v21.2s}, [x4], #16 // c0,c1 s0,s1 - rev64 v1.2s, v1.2s // in2d0,in2d1 in1d0,in1d1 - rev64 v3.2s, v3.2s // in2d0,in2d1 in1d0,in1d1 - ld2 {v30.2s,v31.2s}, [x5], x12 // c2,c3 s2,s3 - fadd v2.2s, v2.2s, v19.2s // in3u+in3d -R - fsub v16.2s, v16.2s, v1.2s // in0u-in2d R - fadd v18.2s, v18.2s, v3.2s // in2u+in1d -I -1: - fmul v7.2s, v0.2s, v21.2s // I*s - ldr w10, [x3, x13] - fmul v6.2s, v2.2s, v20.2s // -R*c - ldr w6, [x3, #4]! - fmul v4.2s, v2.2s, v21.2s // -R*s - fmul v5.2s, v0.2s, v20.2s // I*c - fmul v24.2s, v16.2s, v30.2s // R*c - fmul v25.2s, v18.2s, v31.2s // -I*s - fmul v22.2s, v16.2s, v31.2s // R*s - fmul v23.2s, v18.2s, v30.2s // I*c - subs x14, x14, #16 - subs x13, x13, #8 - fsub v6.2s, v6.2s, v7.2s // -R*c-I*s - fadd v7.2s, v4.2s, v5.2s // -R*s+I*c - fsub v24.2s, v25.2s, v24.2s // I*s-R*c - fadd v25.2s, v22.2s, v23.2s // R*s-I*c - b.eq 1f - mov x12, #-16 - ld2 {v16.2s,v17.2s}, [x9], x12 // in0u0,in0u1 in4d1,in4d0 - ld2 {v18.2s,v19.2s}, [x8], x12 // in2u0,in2u1 in3d1,in3d0 - fneg v7.2s, v7.2s // R*s-I*c - ld2 {v0.2s, v1.2s}, [x7], #16 // in4u0,in4u1 in2d1,in2d0 - rev64 v17.2s, v17.2s // in4d0,in4d1 in3d0,in3d1 - rev64 v19.2s, v19.2s // in4d0,in4d1 in3d0,in3d1 - ld2 {v2.2s, v3.2s}, [x2], #16 // in3u0,in3u1 in1d1,in1d0 - fsub v0.2s, v17.2s, v0.2s // in4d-in4u I - ld2 {v20.2s,v21.2s}, [x4], #16 // c0,c1 s0,s1 - rev64 v1.2s, v1.2s // in2d0,in2d1 in1d0,in1d1 - rev64 v3.2s, v3.2s // in2d0,in2d1 in1d0,in1d1 - ld2 {v30.2s,v31.2s}, [x5], x12 // c2,c3 s2,s3 - fadd v2.2s, v2.2s, v19.2s // in3u+in3d -R - fsub v16.2s, v16.2s, v1.2s // in0u-in2d R - fadd v18.2s, v18.2s, v3.2s // in2u+in1d -I - ubfm x12, x6, #16, #31 - ubfm x6, x6, #0, #15 - add x12, x1, x12, lsl #3 - add x6, x1, x6, lsl #3 - st2 {v6.s,v7.s}[0], [x6] - st2 {v6.s,v7.s}[1], [x12] - ubfm x6, x10, #16, #31 - ubfm x10, x10, #0, #15 - add x6 , x1, x6, lsl #3 - add x10, x1, x10, lsl #3 - st2 {v24.s,v25.s}[0], [x10] - st2 {v24.s,v25.s}[1], [x6] - b 1b -1: - fneg v7.2s, v7.2s // R*s-I*c - ubfm x12, x6, #16, #31 - ubfm x6, x6, #0, #15 - add x12, x1, x12, lsl #3 - add x6, x1, x6, lsl #3 - st2 {v6.s,v7.s}[0], [x6] - st2 {v6.s,v7.s}[1], [x12] - ubfm x6, x10, #16, #31 - ubfm x10, x10, #0, #15 - add x6 , x1, x6, lsl #3 - add x10, x1, x10, lsl #3 - st2 {v24.s,v25.s}[0], [x10] - st2 {v24.s,v25.s}[1], [x6] - - mov x19, x0 - mov x20, x1 - bl X(ff_fft_calc_neon) - - mov x12, #1 - ldr w14, [x19, #28] // mdct_bits - ldr x4, [x19, #32] // tcos - lsl x12, x12, x14 // n = 1 << nbits - lsr x14, x12, #3 // n8 = n >> 3 - - add x4, x4, x14, lsl #3 - add x6, x20, x14, lsl #3 - sub x1, x4, #16 - sub x3, x6, #16 - - mov x7, #-16 - mov x8, x6 - mov x0, x3 - - ld2 {v0.2s,v1.2s}, [x3], x7 // d0 =r1,i1 d1 =r0,i0 - ld2 {v20.2s,v21.2s}, [x6], #16 // d20=r2,i2 d21=r3,i3 - ld2 {v16.2s,v17.2s}, [x1], x7 // c1,c0 s1,s0 -1: - subs x14, x14, #2 - fmul v7.2s, v0.2s, v17.2s // r1*s1,r0*s0 - ld2 {v18.2s,v19.2s}, [x4], #16 // c2,c3 s2,s3 - fmul v4.2s, v1.2s, v17.2s // i1*s1,i0*s0 - fmul v6.2s, v21.2s, v19.2s // i2*s2,i3*s3 - fmul v5.2s, v20.2s, v19.2s // r2*s2,r3*s3 - fmul v24.2s, v0.2s, v16.2s // r1*c1,r0*c0 - fmul v25.2s, v20.2s, v18.2s // r2*c2,r3*c3 - fmul v22.2s, v21.2s, v18.2s // i2*c2,i3*c3 - fmul v23.2s, v1.2s, v16.2s // i1*c1,i0*c0 - fadd v4.2s, v4.2s, v24.2s // i1*s1+r1*c1,i0*s0+r0*c0 - fadd v6.2s, v6.2s, v25.2s // i2*s2+r2*c2,i3*s3+r3*c3 - fsub v5.2s, v22.2s, v5.2s // i2*c2-r2*s2,i3*c3-r3*s3 - fsub v7.2s, v23.2s, v7.2s // i1*c1-r1*s1,i0*c0-r0*s0 - fneg v4.2s, v4.2s - fneg v6.2s, v6.2s - b.eq 1f - ld2 {v0.2s, v1.2s}, [x3], x7 - ld2 {v20.2s,v21.2s}, [x6], #16 - ld2 {v16.2s,v17.2s}, [x1], x7 // c1,c0 s1,s0 - rev64 v5.2s, v5.2s - rev64 v7.2s, v7.2s - st2 {v4.2s,v5.2s}, [x0], x7 - st2 {v6.2s,v7.2s}, [x8], #16 - b 1b -1: - rev64 v5.2s, v5.2s - rev64 v7.2s, v7.2s - st2 {v4.2s,v5.2s}, [x0] - st2 {v6.2s,v7.2s}, [x8] - - ldr x30, [sp, #16] - AARCH64_VALIDATE_LINK_REGISTER - ldp x19, x20, [sp], #32 - - ret -endfunc diff --git a/libavcodec/aarch64/me_cmp_neon.S b/libavcodec/aarch64/me_cmp_neon.S index cf86e5081db..7500c324bd7 100644 --- a/libavcodec/aarch64/me_cmp_neon.S +++ b/libavcodec/aarch64/me_cmp_neon.S @@ -1099,7 +1099,7 @@ function vsse_intra16_neon, export=1 cbnz w4, 2b 3: - add v16.4s, v16.4s, v17.4S + add v16.4s, v16.4s, v17.4s uaddlv d17, v16.4s fmov w0, s17 diff --git a/libavcodec/aarch64/mpegaudiodsp_neon.S b/libavcodec/aarch64/mpegaudiodsp_neon.S index b6ef131228a..9799c271d09 100644 --- a/libavcodec/aarch64/mpegaudiodsp_neon.S +++ b/libavcodec/aarch64/mpegaudiodsp_neon.S @@ -56,7 +56,7 @@ function ff_mpadsp_apply_window_\type\()_neon, export=1 .ifc \type, fixed ld1r {v16.2s}, [x2] // dither_state sxtl v16.2d, v16.2s - movi v29.2d, #0 + movi v29.16b, #0 movi v30.2d, #(1< + * * 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 @@ -16,147 +18,156 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +.macro clip min, max, regs:vararg +.irp x, \regs + smax \x, \x, \min +.endr +.irp x, \regs + smin \x, \x, \max +.endr +.endm + .macro transpose_8x8B r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 - trn1 \r8\().8B, \r0\().8B, \r1\().8B - trn2 \r9\().8B, \r0\().8B, \r1\().8B - trn1 \r1\().8B, \r2\().8B, \r3\().8B - trn2 \r3\().8B, \r2\().8B, \r3\().8B - trn1 \r0\().8B, \r4\().8B, \r5\().8B - trn2 \r5\().8B, \r4\().8B, \r5\().8B - trn1 \r2\().8B, \r6\().8B, \r7\().8B - trn2 \r7\().8B, \r6\().8B, \r7\().8B - - trn1 \r4\().4H, \r0\().4H, \r2\().4H - trn2 \r2\().4H, \r0\().4H, \r2\().4H - trn1 \r6\().4H, \r5\().4H, \r7\().4H - trn2 \r7\().4H, \r5\().4H, \r7\().4H - trn1 \r5\().4H, \r9\().4H, \r3\().4H - trn2 \r9\().4H, \r9\().4H, \r3\().4H - trn1 \r3\().4H, \r8\().4H, \r1\().4H - trn2 \r8\().4H, \r8\().4H, \r1\().4H - - trn1 \r0\().2S, \r3\().2S, \r4\().2S - trn2 \r4\().2S, \r3\().2S, \r4\().2S - - trn1 \r1\().2S, \r5\().2S, \r6\().2S - trn2 \r5\().2S, \r5\().2S, \r6\().2S - - trn2 \r6\().2S, \r8\().2S, \r2\().2S - trn1 \r2\().2S, \r8\().2S, \r2\().2S - - trn1 \r3\().2S, \r9\().2S, \r7\().2S - trn2 \r7\().2S, \r9\().2S, \r7\().2S + trn1 \r8\().8b, \r0\().8b, \r1\().8b + trn2 \r9\().8b, \r0\().8b, \r1\().8b + trn1 \r1\().8b, \r2\().8b, \r3\().8b + trn2 \r3\().8b, \r2\().8b, \r3\().8b + trn1 \r0\().8b, \r4\().8b, \r5\().8b + trn2 \r5\().8b, \r4\().8b, \r5\().8b + trn1 \r2\().8b, \r6\().8b, \r7\().8b + trn2 \r7\().8b, \r6\().8b, \r7\().8b + + trn1 \r4\().4h, \r0\().4h, \r2\().4h + trn2 \r2\().4h, \r0\().4h, \r2\().4h + trn1 \r6\().4h, \r5\().4h, \r7\().4h + trn2 \r7\().4h, \r5\().4h, \r7\().4h + trn1 \r5\().4h, \r9\().4h, \r3\().4h + trn2 \r9\().4h, \r9\().4h, \r3\().4h + trn1 \r3\().4h, \r8\().4h, \r1\().4h + trn2 \r8\().4h, \r8\().4h, \r1\().4h + + trn1 \r0\().2s, \r3\().2s, \r4\().2s + trn2 \r4\().2s, \r3\().2s, \r4\().2s + + trn1 \r1\().2s, \r5\().2s, \r6\().2s + trn2 \r5\().2s, \r5\().2s, \r6\().2s + + trn2 \r6\().2s, \r8\().2s, \r2\().2s + trn1 \r2\().2s, \r8\().2s, \r2\().2s + + trn1 \r3\().2s, \r9\().2s, \r7\().2s + trn2 \r7\().2s, \r9\().2s, \r7\().2s .endm .macro transpose_8x16B r0, r1, r2, r3, r4, r5, r6, r7, t0, t1 - trn1 \t0\().16B, \r0\().16B, \r1\().16B - trn2 \t1\().16B, \r0\().16B, \r1\().16B - trn1 \r1\().16B, \r2\().16B, \r3\().16B - trn2 \r3\().16B, \r2\().16B, \r3\().16B - trn1 \r0\().16B, \r4\().16B, \r5\().16B - trn2 \r5\().16B, \r4\().16B, \r5\().16B - trn1 \r2\().16B, \r6\().16B, \r7\().16B - trn2 \r7\().16B, \r6\().16B, \r7\().16B - - trn1 \r4\().8H, \r0\().8H, \r2\().8H - trn2 \r2\().8H, \r0\().8H, \r2\().8H - trn1 \r6\().8H, \r5\().8H, \r7\().8H - trn2 \r7\().8H, \r5\().8H, \r7\().8H - trn1 \r5\().8H, \t1\().8H, \r3\().8H - trn2 \t1\().8H, \t1\().8H, \r3\().8H - trn1 \r3\().8H, \t0\().8H, \r1\().8H - trn2 \t0\().8H, \t0\().8H, \r1\().8H - - trn1 \r0\().4S, \r3\().4S, \r4\().4S - trn2 \r4\().4S, \r3\().4S, \r4\().4S - - trn1 \r1\().4S, \r5\().4S, \r6\().4S - trn2 \r5\().4S, \r5\().4S, \r6\().4S - - trn2 \r6\().4S, \t0\().4S, \r2\().4S - trn1 \r2\().4S, \t0\().4S, \r2\().4S - - trn1 \r3\().4S, \t1\().4S, \r7\().4S - trn2 \r7\().4S, \t1\().4S, \r7\().4S + trn1 \t0\().16b, \r0\().16b, \r1\().16b + trn2 \t1\().16b, \r0\().16b, \r1\().16b + trn1 \r1\().16b, \r2\().16b, \r3\().16b + trn2 \r3\().16b, \r2\().16b, \r3\().16b + trn1 \r0\().16b, \r4\().16b, \r5\().16b + trn2 \r5\().16b, \r4\().16b, \r5\().16b + trn1 \r2\().16b, \r6\().16b, \r7\().16b + trn2 \r7\().16b, \r6\().16b, \r7\().16b + + trn1 \r4\().8h, \r0\().8h, \r2\().8h + trn2 \r2\().8h, \r0\().8h, \r2\().8h + trn1 \r6\().8h, \r5\().8h, \r7\().8h + trn2 \r7\().8h, \r5\().8h, \r7\().8h + trn1 \r5\().8h, \t1\().8h, \r3\().8h + trn2 \t1\().8h, \t1\().8h, \r3\().8h + trn1 \r3\().8h, \t0\().8h, \r1\().8h + trn2 \t0\().8h, \t0\().8h, \r1\().8h + + trn1 \r0\().4s, \r3\().4s, \r4\().4s + trn2 \r4\().4s, \r3\().4s, \r4\().4s + + trn1 \r1\().4s, \r5\().4s, \r6\().4s + trn2 \r5\().4s, \r5\().4s, \r6\().4s + + trn2 \r6\().4s, \t0\().4s, \r2\().4s + trn1 \r2\().4s, \t0\().4s, \r2\().4s + + trn1 \r3\().4s, \t1\().4s, \r7\().4s + trn2 \r7\().4s, \t1\().4s, \r7\().4s .endm .macro transpose_4x16B r0, r1, r2, r3, t4, t5, t6, t7 - trn1 \t4\().16B, \r0\().16B, \r1\().16B - trn2 \t5\().16B, \r0\().16B, \r1\().16B - trn1 \t6\().16B, \r2\().16B, \r3\().16B - trn2 \t7\().16B, \r2\().16B, \r3\().16B - - trn1 \r0\().8H, \t4\().8H, \t6\().8H - trn2 \r2\().8H, \t4\().8H, \t6\().8H - trn1 \r1\().8H, \t5\().8H, \t7\().8H - trn2 \r3\().8H, \t5\().8H, \t7\().8H + trn1 \t4\().16b, \r0\().16b, \r1\().16b + trn2 \t5\().16b, \r0\().16b, \r1\().16b + trn1 \t6\().16b, \r2\().16b, \r3\().16b + trn2 \t7\().16b, \r2\().16b, \r3\().16b + + trn1 \r0\().8h, \t4\().8h, \t6\().8h + trn2 \r2\().8h, \t4\().8h, \t6\().8h + trn1 \r1\().8h, \t5\().8h, \t7\().8h + trn2 \r3\().8h, \t5\().8h, \t7\().8h .endm .macro transpose_4x8B r0, r1, r2, r3, t4, t5, t6, t7 - trn1 \t4\().8B, \r0\().8B, \r1\().8B - trn2 \t5\().8B, \r0\().8B, \r1\().8B - trn1 \t6\().8B, \r2\().8B, \r3\().8B - trn2 \t7\().8B, \r2\().8B, \r3\().8B - - trn1 \r0\().4H, \t4\().4H, \t6\().4H - trn2 \r2\().4H, \t4\().4H, \t6\().4H - trn1 \r1\().4H, \t5\().4H, \t7\().4H - trn2 \r3\().4H, \t5\().4H, \t7\().4H + trn1 \t4\().8b, \r0\().8b, \r1\().8b + trn2 \t5\().8b, \r0\().8b, \r1\().8b + trn1 \t6\().8b, \r2\().8b, \r3\().8b + trn2 \t7\().8b, \r2\().8b, \r3\().8b + + trn1 \r0\().4h, \t4\().4h, \t6\().4h + trn2 \r2\().4h, \t4\().4h, \t6\().4h + trn1 \r1\().4h, \t5\().4h, \t7\().4h + trn2 \r3\().4h, \t5\().4h, \t7\().4h .endm .macro transpose_4x4H r0, r1, r2, r3, r4, r5, r6, r7 - trn1 \r4\().4H, \r0\().4H, \r1\().4H - trn2 \r5\().4H, \r0\().4H, \r1\().4H - trn1 \r6\().4H, \r2\().4H, \r3\().4H - trn2 \r7\().4H, \r2\().4H, \r3\().4H - - trn1 \r0\().2S, \r4\().2S, \r6\().2S - trn2 \r2\().2S, \r4\().2S, \r6\().2S - trn1 \r1\().2S, \r5\().2S, \r7\().2S - trn2 \r3\().2S, \r5\().2S, \r7\().2S + trn1 \r4\().4h, \r0\().4h, \r1\().4h + trn2 \r5\().4h, \r0\().4h, \r1\().4h + trn1 \r6\().4h, \r2\().4h, \r3\().4h + trn2 \r7\().4h, \r2\().4h, \r3\().4h + + trn1 \r0\().2s, \r4\().2s, \r6\().2s + trn2 \r2\().2s, \r4\().2s, \r6\().2s + trn1 \r1\().2s, \r5\().2s, \r7\().2s + trn2 \r3\().2s, \r5\().2s, \r7\().2s .endm .macro transpose_4x8H r0, r1, r2, r3, t4, t5, t6, t7 - trn1 \t4\().8H, \r0\().8H, \r1\().8H - trn2 \t5\().8H, \r0\().8H, \r1\().8H - trn1 \t6\().8H, \r2\().8H, \r3\().8H - trn2 \t7\().8H, \r2\().8H, \r3\().8H - - trn1 \r0\().4S, \t4\().4S, \t6\().4S - trn2 \r2\().4S, \t4\().4S, \t6\().4S - trn1 \r1\().4S, \t5\().4S, \t7\().4S - trn2 \r3\().4S, \t5\().4S, \t7\().4S + trn1 \t4\().8h, \r0\().8h, \r1\().8h + trn2 \t5\().8h, \r0\().8h, \r1\().8h + trn1 \t6\().8h, \r2\().8h, \r3\().8h + trn2 \t7\().8h, \r2\().8h, \r3\().8h + + trn1 \r0\().4s, \t4\().4s, \t6\().4s + trn2 \r2\().4s, \t4\().4s, \t6\().4s + trn1 \r1\().4s, \t5\().4s, \t7\().4s + trn2 \r3\().4s, \t5\().4s, \t7\().4s .endm .macro transpose_8x8H r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 - trn1 \r8\().8H, \r0\().8H, \r1\().8H - trn2 \r9\().8H, \r0\().8H, \r1\().8H - trn1 \r1\().8H, \r2\().8H, \r3\().8H - trn2 \r3\().8H, \r2\().8H, \r3\().8H - trn1 \r0\().8H, \r4\().8H, \r5\().8H - trn2 \r5\().8H, \r4\().8H, \r5\().8H - trn1 \r2\().8H, \r6\().8H, \r7\().8H - trn2 \r7\().8H, \r6\().8H, \r7\().8H - - trn1 \r4\().4S, \r0\().4S, \r2\().4S - trn2 \r2\().4S, \r0\().4S, \r2\().4S - trn1 \r6\().4S, \r5\().4S, \r7\().4S - trn2 \r7\().4S, \r5\().4S, \r7\().4S - trn1 \r5\().4S, \r9\().4S, \r3\().4S - trn2 \r9\().4S, \r9\().4S, \r3\().4S - trn1 \r3\().4S, \r8\().4S, \r1\().4S - trn2 \r8\().4S, \r8\().4S, \r1\().4S - - trn1 \r0\().2D, \r3\().2D, \r4\().2D - trn2 \r4\().2D, \r3\().2D, \r4\().2D - - trn1 \r1\().2D, \r5\().2D, \r6\().2D - trn2 \r5\().2D, \r5\().2D, \r6\().2D - - trn2 \r6\().2D, \r8\().2D, \r2\().2D - trn1 \r2\().2D, \r8\().2D, \r2\().2D - - trn1 \r3\().2D, \r9\().2D, \r7\().2D - trn2 \r7\().2D, \r9\().2D, \r7\().2D + trn1 \r8\().8h, \r0\().8h, \r1\().8h + trn2 \r9\().8h, \r0\().8h, \r1\().8h + trn1 \r1\().8h, \r2\().8h, \r3\().8h + trn2 \r3\().8h, \r2\().8h, \r3\().8h + trn1 \r0\().8h, \r4\().8h, \r5\().8h + trn2 \r5\().8h, \r4\().8h, \r5\().8h + trn1 \r2\().8h, \r6\().8h, \r7\().8h + trn2 \r7\().8h, \r6\().8h, \r7\().8h + + trn1 \r4\().4s, \r0\().4s, \r2\().4s + trn2 \r2\().4s, \r0\().4s, \r2\().4s + trn1 \r6\().4s, \r5\().4s, \r7\().4s + trn2 \r7\().4s, \r5\().4s, \r7\().4s + trn1 \r5\().4s, \r9\().4s, \r3\().4s + trn2 \r9\().4s, \r9\().4s, \r3\().4s + trn1 \r3\().4s, \r8\().4s, \r1\().4s + trn2 \r8\().4s, \r8\().4s, \r1\().4s + + trn1 \r0\().2d, \r3\().2d, \r4\().2d + trn2 \r4\().2d, \r3\().2d, \r4\().2d + + trn1 \r1\().2d, \r5\().2d, \r6\().2d + trn2 \r5\().2d, \r5\().2d, \r6\().2d + + trn2 \r6\().2d, \r8\().2d, \r2\().2d + trn1 \r2\().2d, \r8\().2d, \r2\().2d + + trn1 \r3\().2d, \r9\().2d, \r7\().2d + trn2 \r7\().2d, \r9\().2d, \r7\().2d .endm diff --git a/libavcodec/aarch64/opusdsp_neon.S b/libavcodec/aarch64/opusdsp_neon.S index 46c2be0874c..e933151ab45 100644 --- a/libavcodec/aarch64/opusdsp_neon.S +++ b/libavcodec/aarch64/opusdsp_neon.S @@ -33,81 +33,81 @@ const tab_x2, align=4 endconst function ff_opus_deemphasis_neon, export=1 - movrel x4, tab_st - ld1 {v4.4s}, [x4] - movrel x4, tab_x0 - ld1 {v5.4s}, [x4] - movrel x4, tab_x1 - ld1 {v6.4s}, [x4] - movrel x4, tab_x2 - ld1 {v7.4s}, [x4] + movrel x4, tab_st + ld1 {v4.4s}, [x4] + movrel x4, tab_x0 + ld1 {v5.4s}, [x4] + movrel x4, tab_x1 + ld1 {v6.4s}, [x4] + movrel x4, tab_x2 + ld1 {v7.4s}, [x4] - fmul v0.4s, v4.4s, v0.s[0] + fmul v0.4s, v4.4s, v0.s[0] -1: ld1 {v1.4s, v2.4s}, [x1], #32 +1: ld1 {v1.4s, v2.4s}, [x1], #32 - fmla v0.4s, v5.4s, v1.s[0] - fmul v3.4s, v7.4s, v2.s[2] + fmla v0.4s, v5.4s, v1.s[0] + fmul v3.4s, v7.4s, v2.s[2] - fmla v0.4s, v6.4s, v1.s[1] - fmla v3.4s, v6.4s, v2.s[1] + fmla v0.4s, v6.4s, v1.s[1] + fmla v3.4s, v6.4s, v2.s[1] - fmla v0.4s, v7.4s, v1.s[2] - fmla v3.4s, v5.4s, v2.s[0] + fmla v0.4s, v7.4s, v1.s[2] + fmla v3.4s, v5.4s, v2.s[0] - fadd v1.4s, v1.4s, v0.4s - fadd v2.4s, v2.4s, v3.4s + fadd v1.4s, v1.4s, v0.4s + fadd v2.4s, v2.4s, v3.4s - fmla v2.4s, v4.4s, v1.s[3] + fmla v2.4s, v4.4s, v1.s[3] - st1 {v1.4s, v2.4s}, [x0], #32 - fmul v0.4s, v4.4s, v2.s[3] + st1 {v1.4s, v2.4s}, [x0], #32 + fmul v0.4s, v4.4s, v2.s[3] - subs w2, w2, #8 - b.gt 1b + subs w2, w2, #8 + b.gt 1b - mov s0, v2.s[3] + mov s0, v2.s[3] ret endfunc function ff_opus_postfilter_neon, export=1 - ld1 {v0.4s}, [x2] - dup v1.4s, v0.s[1] - dup v2.4s, v0.s[2] - dup v0.4s, v0.s[0] + ld1 {v0.4s}, [x2] + dup v1.4s, v0.s[1] + dup v2.4s, v0.s[2] + dup v0.4s, v0.s[0] - add w1, w1, #2 - sub x1, x0, x1, lsl #2 + add w1, w1, #2 + sub x1, x0, x1, lsl #2 - ld1 {v3.4s}, [x1] - fmul v3.4s, v3.4s, v2.4s + ld1 {v3.4s}, [x1] + fmul v3.4s, v3.4s, v2.4s -1: add x1, x1, #4 - ld1 {v4.4s}, [x1] - add x1, x1, #4 - ld1 {v5.4s}, [x1] - add x1, x1, #4 - ld1 {v6.4s}, [x1] - add x1, x1, #4 - ld1 {v7.4s}, [x1] +1: add x1, x1, #4 + ld1 {v4.4s}, [x1] + add x1, x1, #4 + ld1 {v5.4s}, [x1] + add x1, x1, #4 + ld1 {v6.4s}, [x1] + add x1, x1, #4 + ld1 {v7.4s}, [x1] - fmla v3.4s, v7.4s, v2.4s - fadd v6.4s, v6.4s, v4.4s + fmla v3.4s, v7.4s, v2.4s + fadd v6.4s, v6.4s, v4.4s - ld1 {v4.4s}, [x0] - fmla v4.4s, v5.4s, v0.4s + ld1 {v4.4s}, [x0] + fmla v4.4s, v5.4s, v0.4s - fmul v6.4s, v6.4s, v1.4s - fadd v6.4s, v6.4s, v3.4s + fmul v6.4s, v6.4s, v1.4s + fadd v6.4s, v6.4s, v3.4s - fadd v4.4s, v4.4s, v6.4s - fmul v3.4s, v7.4s, v2.4s + fadd v4.4s, v4.4s, v6.4s + fmul v3.4s, v7.4s, v2.4s - st1 {v4.4s}, [x0], #16 + st1 {v4.4s}, [x0], #16 - subs w3, w3, #4 - b.gt 1b + subs w3, w3, #4 + b.gt 1b ret endfunc diff --git a/libavcodec/aarch64/sbrdsp_neon.S b/libavcodec/aarch64/sbrdsp_neon.S index d23717e7605..1fdde6ccb68 100644 --- a/libavcodec/aarch64/sbrdsp_neon.S +++ b/libavcodec/aarch64/sbrdsp_neon.S @@ -46,49 +46,49 @@ function ff_sbr_sum64x5_neon, export=1 add x3, x0, #192*4 add x4, x0, #256*4 mov x5, #64 -1: ld1 {v0.4S}, [x0] - ld1 {v1.4S}, [x1], #16 - fadd v0.4S, v0.4S, v1.4S - ld1 {v2.4S}, [x2], #16 - fadd v0.4S, v0.4S, v2.4S - ld1 {v3.4S}, [x3], #16 - fadd v0.4S, v0.4S, v3.4S - ld1 {v4.4S}, [x4], #16 - fadd v0.4S, v0.4S, v4.4S - st1 {v0.4S}, [x0], #16 +1: ld1 {v0.4s}, [x0] + ld1 {v1.4s}, [x1], #16 + fadd v0.4s, v0.4s, v1.4s + ld1 {v2.4s}, [x2], #16 + fadd v0.4s, v0.4s, v2.4s + ld1 {v3.4s}, [x3], #16 + fadd v0.4s, v0.4s, v3.4s + ld1 {v4.4s}, [x4], #16 + fadd v0.4s, v0.4s, v4.4s + st1 {v0.4s}, [x0], #16 subs x5, x5, #4 b.gt 1b ret endfunc function ff_sbr_sum_square_neon, export=1 - movi v0.4S, #0 -1: ld1 {v1.4S}, [x0], #16 - fmla v0.4S, v1.4S, v1.4S + movi v0.4s, #0 +1: ld1 {v1.4s}, [x0], #16 + fmla v0.4s, v1.4s, v1.4s subs w1, w1, #2 b.gt 1b - faddp v0.4S, v0.4S, v0.4S - faddp v0.4S, v0.4S, v0.4S + faddp v0.4s, v0.4s, v0.4s + faddp v0.4s, v0.4s, v0.4s ret endfunc function ff_sbr_neg_odd_64_neon, export=1 mov x1, x0 - movi v5.4S, #1<<7, lsl #24 - ld2 {v0.4S, v1.4S}, [x0], #32 - eor v1.16B, v1.16B, v5.16B - ld2 {v2.4S, v3.4S}, [x0], #32 + movi v5.4s, #1<<7, lsl #24 + ld2 {v0.4s, v1.4s}, [x0], #32 + eor v1.16b, v1.16b, v5.16b + ld2 {v2.4s, v3.4s}, [x0], #32 .rept 3 - st2 {v0.4S, v1.4S}, [x1], #32 - eor v3.16B, v3.16B, v5.16B - ld2 {v0.4S, v1.4S}, [x0], #32 - st2 {v2.4S, v3.4S}, [x1], #32 - eor v1.16B, v1.16B, v5.16B - ld2 {v2.4S, v3.4S}, [x0], #32 + st2 {v0.4s, v1.4s}, [x1], #32 + eor v3.16b, v3.16b, v5.16b + ld2 {v0.4s, v1.4s}, [x0], #32 + st2 {v2.4s, v3.4s}, [x1], #32 + eor v1.16b, v1.16b, v5.16b + ld2 {v2.4s, v3.4s}, [x0], #32 .endr - eor v3.16B, v3.16B, v5.16B - st2 {v0.4S, v1.4S}, [x1], #32 - st2 {v2.4S, v3.4S}, [x1], #32 + eor v3.16b, v3.16b, v5.16b + st2 {v0.4s, v1.4s}, [x1], #32 + st2 {v2.4s, v3.4s}, [x1], #32 ret endfunc @@ -97,26 +97,26 @@ function ff_sbr_qmf_pre_shuffle_neon, export=1 add x2, x0, #64*4 mov x3, #-16 mov x4, #-4 - movi v6.4S, #1<<7, lsl #24 - ld1 {v0.2S}, [x0], #8 - st1 {v0.2S}, [x2], #8 + movi v6.4s, #1<<7, lsl #24 + ld1 {v0.2s}, [x0], #8 + st1 {v0.2s}, [x2], #8 .rept 7 - ld1 {v1.4S}, [x1], x3 - ld1 {v2.4S}, [x0], #16 - eor v1.16B, v1.16B, v6.16B - rev64 v1.4S, v1.4S - ext v1.16B, v1.16B, v1.16B, #8 - st2 {v1.4S, v2.4S}, [x2], #32 + ld1 {v1.4s}, [x1], x3 + ld1 {v2.4s}, [x0], #16 + eor v1.16b, v1.16b, v6.16b + rev64 v1.4s, v1.4s + ext v1.16b, v1.16b, v1.16b, #8 + st2 {v1.4s, v2.4s}, [x2], #32 .endr add x1, x1, #8 - ld1 {v1.2S}, [x1], x4 - ld1 {v2.2S}, [x0], #8 - ld1 {v1.S}[3], [x1] - ld1 {v2.S}[2], [x0] - eor v1.16B, v1.16B, v6.16B - rev64 v1.4S, v1.4S - st2 {v1.2S, v2.2S}, [x2], #16 - st2 {v1.S, v2.S}[2], [x2] + ld1 {v1.2s}, [x1], x4 + ld1 {v2.2s}, [x0], #8 + ld1 {v1.s}[3], [x1] + ld1 {v2.s}[2], [x0] + eor v1.16b, v1.16b, v6.16b + rev64 v1.4s, v1.4s + st2 {v1.2s, v2.2s}, [x2], #16 + st2 {v1.s, v2.s}[2], [x2] ret endfunc @@ -124,13 +124,13 @@ function ff_sbr_qmf_post_shuffle_neon, export=1 add x2, x1, #60*4 mov x3, #-16 mov x4, #32 - movi v6.4S, #1<<7, lsl #24 -1: ld1 {v0.4S}, [x2], x3 - ld1 {v1.4S}, [x1], #16 - eor v0.16B, v0.16B, v6.16B - rev64 v0.4S, v0.4S - ext v0.16B, v0.16B, v0.16B, #8 - st2 {v0.4S, v1.4S}, [x0], #32 + movi v6.4s, #1<<7, lsl #24 +1: ld1 {v0.4s}, [x2], x3 + ld1 {v1.4s}, [x1], #16 + eor v0.16b, v0.16b, v6.16b + rev64 v0.4s, v0.4s + ext v0.16b, v0.16b, v0.16b, #8 + st2 {v0.4s, v1.4s}, [x0], #32 subs x4, x4, #4 b.gt 1b ret @@ -141,13 +141,13 @@ function ff_sbr_qmf_deint_neg_neon, export=1 add x2, x0, #60*4 mov x3, #-32 mov x4, #32 - movi v2.4S, #1<<7, lsl #24 -1: ld2 {v0.4S, v1.4S}, [x1], x3 - eor v0.16B, v0.16B, v2.16B - rev64 v1.4S, v1.4S - ext v1.16B, v1.16B, v1.16B, #8 - st1 {v0.4S}, [x2] - st1 {v1.4S}, [x0], #16 + movi v2.4s, #1<<7, lsl #24 +1: ld2 {v0.4s, v1.4s}, [x1], x3 + eor v0.16b, v0.16b, v2.16b + rev64 v1.4s, v1.4s + ext v1.16b, v1.16b, v1.16b, #8 + st1 {v0.4s}, [x2] + st1 {v1.4s}, [x0], #16 sub x2, x2, #16 subs x4, x4, #4 b.gt 1b @@ -159,16 +159,16 @@ function ff_sbr_qmf_deint_bfly_neon, export=1 add x3, x0, #124*4 mov x4, #64 mov x5, #-16 -1: ld1 {v0.4S}, [x1], #16 - ld1 {v1.4S}, [x2], x5 - rev64 v2.4S, v0.4S - ext v2.16B, v2.16B, v2.16B, #8 - rev64 v3.4S, v1.4S - ext v3.16B, v3.16B, v3.16B, #8 - fadd v1.4S, v1.4S, v2.4S - fsub v0.4S, v0.4S, v3.4S - st1 {v0.4S}, [x0], #16 - st1 {v1.4S}, [x3], x5 +1: ld1 {v0.4s}, [x1], #16 + ld1 {v1.4s}, [x2], x5 + rev64 v2.4s, v0.4s + ext v2.16b, v2.16b, v2.16b, #8 + rev64 v3.4s, v1.4s + ext v3.16b, v3.16b, v3.16b, #8 + fadd v1.4s, v1.4s, v2.4s + fsub v0.4s, v0.4s, v3.4s + st1 {v0.4s}, [x0], #16 + st1 {v1.4s}, [x3], x5 subs x4, x4, #4 b.gt 1b ret @@ -178,32 +178,32 @@ function ff_sbr_hf_gen_neon, export=1 sxtw x4, w4 sxtw x5, w5 movrel x6, factors - ld1 {v7.4S}, [x6] - dup v1.4S, v0.S[0] - mov v2.8B, v1.8B - mov v2.S[2], v7.S[0] - mov v2.S[3], v7.S[0] - fmul v1.4S, v1.4S, v2.4S - ld1 {v0.D}[0], [x3] - ld1 {v0.D}[1], [x2] - fmul v0.4S, v0.4S, v1.4S - fmul v1.4S, v0.4S, v7.4S - rev64 v0.4S, v0.4S + ld1 {v7.4s}, [x6] + dup v1.4s, v0.s[0] + mov v2.8b, v1.8b + mov v2.s[2], v7.s[0] + mov v2.s[3], v7.s[0] + fmul v1.4s, v1.4s, v2.4s + ld1 {v0.d}[0], [x3] + ld1 {v0.d}[1], [x2] + fmul v0.4s, v0.4s, v1.4s + fmul v1.4s, v0.4s, v7.4s + rev64 v0.4s, v0.4s sub x7, x5, x4 add x0, x0, x4, lsl #3 add x1, x1, x4, lsl #3 sub x1, x1, #16 -1: ld1 {v2.4S}, [x1], #16 - ld1 {v3.2S}, [x1] - fmul v4.4S, v2.4S, v1.4S - fmul v5.4S, v2.4S, v0.4S - faddp v4.4S, v4.4S, v4.4S - faddp v5.4S, v5.4S, v5.4S - faddp v4.4S, v4.4S, v4.4S - faddp v5.4S, v5.4S, v5.4S - mov v4.S[1], v5.S[0] - fadd v4.2S, v4.2S, v3.2S - st1 {v4.2S}, [x0], #8 +1: ld1 {v2.4s}, [x1], #16 + ld1 {v3.2s}, [x1] + fmul v4.4s, v2.4s, v1.4s + fmul v5.4s, v2.4s, v0.4s + faddp v4.4s, v4.4s, v4.4s + faddp v5.4s, v5.4s, v5.4s + faddp v4.4s, v4.4s, v4.4s + faddp v5.4s, v5.4s, v5.4s + mov v4.s[1], v5.s[0] + fadd v4.2s, v4.2s, v3.2s + st1 {v4.2s}, [x0], #8 sub x1, x1, #8 subs x7, x7, #1 b.gt 1b @@ -215,10 +215,10 @@ function ff_sbr_hf_g_filt_neon, export=1 sxtw x4, w4 mov x5, #40*2*4 add x1, x1, x4, lsl #3 -1: ld1 {v0.2S}, [x1], x5 - ld1 {v1.S}[0], [x2], #4 - fmul v2.4S, v0.4S, v1.S[0] - st1 {v2.2S}, [x0], #8 +1: ld1 {v0.2s}, [x1], x5 + ld1 {v1.s}[0], [x2], #4 + fmul v2.4s, v0.4s, v1.s[0] + st1 {v2.2s}, [x0], #8 subs x3, x3, #1 b.gt 1b ret @@ -227,46 +227,46 @@ endfunc function ff_sbr_autocorrelate_neon, export=1 mov x2, #38 movrel x3, factors - ld1 {v0.4S}, [x3] - movi v1.4S, #0 - movi v2.4S, #0 - movi v3.4S, #0 - ld1 {v4.2S}, [x0], #8 - ld1 {v5.2S}, [x0], #8 - fmul v16.2S, v4.2S, v4.2S - fmul v17.2S, v5.2S, v4.S[0] - fmul v18.2S, v5.2S, v4.S[1] -1: ld1 {v5.D}[1], [x0], #8 - fmla v1.2S, v4.2S, v4.2S - fmla v2.4S, v5.4S, v4.S[0] - fmla v3.4S, v5.4S, v4.S[1] - mov v4.D[0], v5.D[0] - mov v5.D[0], v5.D[1] + ld1 {v0.4s}, [x3] + movi v1.4s, #0 + movi v2.4s, #0 + movi v3.4s, #0 + ld1 {v4.2s}, [x0], #8 + ld1 {v5.2s}, [x0], #8 + fmul v16.2s, v4.2s, v4.2s + fmul v17.2s, v5.2s, v4.s[0] + fmul v18.2s, v5.2s, v4.s[1] +1: ld1 {v5.d}[1], [x0], #8 + fmla v1.2s, v4.2s, v4.2s + fmla v2.4s, v5.4s, v4.s[0] + fmla v3.4s, v5.4s, v4.s[1] + mov v4.d[0], v5.d[0] + mov v5.d[0], v5.d[1] subs x2, x2, #1 b.gt 1b - fmul v19.2S, v4.2S, v4.2S - fmul v20.2S, v5.2S, v4.S[0] - fmul v21.2S, v5.2S, v4.S[1] - fadd v22.4S, v2.4S, v20.4S - fsub v22.4S, v22.4S, v17.4S - fadd v23.4S, v3.4S, v21.4S - fsub v23.4S, v23.4S, v18.4S - rev64 v23.4S, v23.4S - fmul v23.4S, v23.4S, v0.4S - fadd v22.4S, v22.4S, v23.4S - st1 {v22.4S}, [x1], #16 - fadd v23.2S, v1.2S, v19.2S - fsub v23.2S, v23.2S, v16.2S - faddp v23.2S, v23.2S, v23.2S - st1 {v23.S}[0], [x1] + fmul v19.2s, v4.2s, v4.2s + fmul v20.2s, v5.2s, v4.s[0] + fmul v21.2s, v5.2s, v4.s[1] + fadd v22.4s, v2.4s, v20.4s + fsub v22.4s, v22.4s, v17.4s + fadd v23.4s, v3.4s, v21.4s + fsub v23.4s, v23.4s, v18.4s + rev64 v23.4s, v23.4s + fmul v23.4s, v23.4s, v0.4s + fadd v22.4s, v22.4s, v23.4s + st1 {v22.4s}, [x1], #16 + fadd v23.2s, v1.2s, v19.2s + fsub v23.2s, v23.2s, v16.2s + faddp v23.2s, v23.2s, v23.2s + st1 {v23.s}[0], [x1] add x1, x1, #8 - rev64 v3.2S, v3.2S - fmul v3.2S, v3.2S, v0.2S - fadd v2.2S, v2.2S, v3.2S - st1 {v2.2S}, [x1] + rev64 v3.2s, v3.2s + fmul v3.2s, v3.2s, v0.2s + fadd v2.2s, v2.2s, v3.2s + st1 {v2.2s}, [x1] add x1, x1, #16 - faddp v1.2S, v1.2S, v1.2S - st1 {v1.S}[0], [x1] + faddp v1.2s, v1.2s, v1.2s + st1 {v1.s}[0], [x1] ret endfunc @@ -278,25 +278,25 @@ endfunc 1: and x3, x3, #0x1ff add x8, x7, x3, lsl #3 add x3, x3, #2 - ld1 {v2.4S}, [x0] - ld1 {v3.2S}, [x1], #8 - ld1 {v4.2S}, [x2], #8 - ld1 {v5.4S}, [x8] - mov v6.16B, v2.16B - zip1 v3.4S, v3.4S, v3.4S - zip1 v4.4S, v4.4S, v4.4S - fmla v6.4S, v1.4S, v3.4S - fmla v2.4S, v5.4S, v4.4S - fcmeq v7.4S, v3.4S, #0 - bif v2.16B, v6.16B, v7.16B - st1 {v2.4S}, [x0], #16 + ld1 {v2.4s}, [x0] + ld1 {v3.2s}, [x1], #8 + ld1 {v4.2s}, [x2], #8 + ld1 {v5.4s}, [x8] + mov v6.16b, v2.16b + zip1 v3.4s, v3.4s, v3.4s + zip1 v4.4s, v4.4s, v4.4s + fmla v6.4s, v1.4s, v3.4s + fmla v2.4s, v5.4s, v4.4s + fcmeq v7.4s, v3.4s, #0 + bif v2.16b, v6.16b, v7.16b + st1 {v2.4s}, [x0], #16 subs x5, x5, #2 b.gt 1b .endm function ff_sbr_hf_apply_noise_0_neon, export=1 movrel x9, phi_noise_0 - ld1 {v1.4S}, [x9] + ld1 {v1.4s}, [x9] apply_noise_common ret endfunc @@ -305,14 +305,14 @@ function ff_sbr_hf_apply_noise_1_neon, export=1 movrel x9, phi_noise_1 and x4, x4, #1 add x9, x9, x4, lsl #4 - ld1 {v1.4S}, [x9] + ld1 {v1.4s}, [x9] apply_noise_common ret endfunc function ff_sbr_hf_apply_noise_2_neon, export=1 movrel x9, phi_noise_2 - ld1 {v1.4S}, [x9] + ld1 {v1.4s}, [x9] apply_noise_common ret endfunc @@ -321,7 +321,7 @@ function ff_sbr_hf_apply_noise_3_neon, export=1 movrel x9, phi_noise_3 and x4, x4, #1 add x9, x9, x4, lsl #4 - ld1 {v1.4S}, [x9] + ld1 {v1.4s}, [x9] apply_noise_common ret endfunc diff --git a/libavcodec/aarch64/simple_idct_neon.S b/libavcodec/aarch64/simple_idct_neon.S index 210182ff210..a4438e9922f 100644 --- a/libavcodec/aarch64/simple_idct_neon.S +++ b/libavcodec/aarch64/simple_idct_neon.S @@ -54,7 +54,7 @@ endconst prfm pldl1keep, [\data] mov x10, x30 movrel x3, idct_coeff_neon - ld1 {v0.2D}, [x3] + ld1 {v0.2d}, [x3] .endm .macro idct_end @@ -74,146 +74,146 @@ endconst .endm .macro idct_col4_top y1, y2, y3, y4, i, l - smull\i v7.4S, \y3\l, z2 - smull\i v16.4S, \y3\l, z6 - smull\i v17.4S, \y2\l, z1 - add v19.4S, v23.4S, v7.4S - smull\i v18.4S, \y2\l, z3 - add v20.4S, v23.4S, v16.4S - smull\i v5.4S, \y2\l, z5 - sub v21.4S, v23.4S, v16.4S - smull\i v6.4S, \y2\l, z7 - sub v22.4S, v23.4S, v7.4S - - smlal\i v17.4S, \y4\l, z3 - smlsl\i v18.4S, \y4\l, z7 - smlsl\i v5.4S, \y4\l, z1 - smlsl\i v6.4S, \y4\l, z5 + smull\i v7.4s, \y3\l, z2 + smull\i v16.4s, \y3\l, z6 + smull\i v17.4s, \y2\l, z1 + add v19.4s, v23.4s, v7.4s + smull\i v18.4s, \y2\l, z3 + add v20.4s, v23.4s, v16.4s + smull\i v5.4s, \y2\l, z5 + sub v21.4s, v23.4s, v16.4s + smull\i v6.4s, \y2\l, z7 + sub v22.4s, v23.4s, v7.4s + + smlal\i v17.4s, \y4\l, z3 + smlsl\i v18.4s, \y4\l, z7 + smlsl\i v5.4s, \y4\l, z1 + smlsl\i v6.4s, \y4\l, z5 .endm .macro idct_row4_neon y1, y2, y3, y4, pass - ld1 {\y1\().2D,\y2\().2D}, [x2], #32 - movi v23.4S, #1<<2, lsl #8 - orr v5.16B, \y1\().16B, \y2\().16B - ld1 {\y3\().2D,\y4\().2D}, [x2], #32 - orr v6.16B, \y3\().16B, \y4\().16B - orr v5.16B, v5.16B, v6.16B - mov x3, v5.D[1] - smlal v23.4S, \y1\().4H, z4 + ld1 {\y1\().2d,\y2\().2d}, [x2], #32 + movi v23.4s, #1<<2, lsl #8 + orr v5.16b, \y1\().16b, \y2\().16b + ld1 {\y3\().2d,\y4\().2d}, [x2], #32 + orr v6.16b, \y3\().16b, \y4\().16b + orr v5.16b, v5.16b, v6.16b + mov x3, v5.d[1] + smlal v23.4s, \y1\().4h, z4 - idct_col4_top \y1, \y2, \y3, \y4, 1, .4H + idct_col4_top \y1, \y2, \y3, \y4, 1, .4h cmp x3, #0 b.eq \pass\()f - smull2 v7.4S, \y1\().8H, z4 - smlal2 v17.4S, \y2\().8H, z5 - smlsl2 v18.4S, \y2\().8H, z1 - smull2 v16.4S, \y3\().8H, z2 - smlal2 v5.4S, \y2\().8H, z7 - add v19.4S, v19.4S, v7.4S - sub v20.4S, v20.4S, v7.4S - sub v21.4S, v21.4S, v7.4S - add v22.4S, v22.4S, v7.4S - smlal2 v6.4S, \y2\().8H, z3 - smull2 v7.4S, \y3\().8H, z6 - smlal2 v17.4S, \y4\().8H, z7 - smlsl2 v18.4S, \y4\().8H, z5 - smlal2 v5.4S, \y4\().8H, z3 - smlsl2 v6.4S, \y4\().8H, z1 - add v19.4S, v19.4S, v7.4S - sub v20.4S, v20.4S, v16.4S - add v21.4S, v21.4S, v16.4S - sub v22.4S, v22.4S, v7.4S + smull2 v7.4s, \y1\().8h, z4 + smlal2 v17.4s, \y2\().8h, z5 + smlsl2 v18.4s, \y2\().8h, z1 + smull2 v16.4s, \y3\().8h, z2 + smlal2 v5.4s, \y2\().8h, z7 + add v19.4s, v19.4s, v7.4s + sub v20.4s, v20.4s, v7.4s + sub v21.4s, v21.4s, v7.4s + add v22.4s, v22.4s, v7.4s + smlal2 v6.4s, \y2\().8h, z3 + smull2 v7.4s, \y3\().8h, z6 + smlal2 v17.4s, \y4\().8h, z7 + smlsl2 v18.4s, \y4\().8h, z5 + smlal2 v5.4s, \y4\().8h, z3 + smlsl2 v6.4s, \y4\().8h, z1 + add v19.4s, v19.4s, v7.4s + sub v20.4s, v20.4s, v16.4s + add v21.4s, v21.4s, v16.4s + sub v22.4s, v22.4s, v7.4s \pass: add \y3\().4S, v19.4S, v17.4S - add \y4\().4S, v20.4S, v18.4S - shrn \y1\().4H, \y3\().4S, #ROW_SHIFT - shrn \y2\().4H, \y4\().4S, #ROW_SHIFT - add v7.4S, v21.4S, v5.4S - add v16.4S, v22.4S, v6.4S - shrn \y3\().4H, v7.4S, #ROW_SHIFT - shrn \y4\().4H, v16.4S, #ROW_SHIFT - sub v22.4S, v22.4S, v6.4S - sub v19.4S, v19.4S, v17.4S - sub v21.4S, v21.4S, v5.4S - shrn2 \y1\().8H, v22.4S, #ROW_SHIFT - sub v20.4S, v20.4S, v18.4S - shrn2 \y2\().8H, v21.4S, #ROW_SHIFT - shrn2 \y3\().8H, v20.4S, #ROW_SHIFT - shrn2 \y4\().8H, v19.4S, #ROW_SHIFT - - trn1 v16.8H, \y1\().8H, \y2\().8H - trn2 v17.8H, \y1\().8H, \y2\().8H - trn1 v18.8H, \y3\().8H, \y4\().8H - trn2 v19.8H, \y3\().8H, \y4\().8H - trn1 \y1\().4S, v16.4S, v18.4S - trn1 \y2\().4S, v17.4S, v19.4S - trn2 \y3\().4S, v16.4S, v18.4S - trn2 \y4\().4S, v17.4S, v19.4S + add \y4\().4s, v20.4s, v18.4s + shrn \y1\().4h, \y3\().4s, #ROW_SHIFT + shrn \y2\().4h, \y4\().4s, #ROW_SHIFT + add v7.4s, v21.4s, v5.4s + add v16.4s, v22.4s, v6.4s + shrn \y3\().4h, v7.4s, #ROW_SHIFT + shrn \y4\().4h, v16.4s, #ROW_SHIFT + sub v22.4s, v22.4s, v6.4s + sub v19.4s, v19.4s, v17.4s + sub v21.4s, v21.4s, v5.4s + shrn2 \y1\().8h, v22.4s, #ROW_SHIFT + sub v20.4s, v20.4s, v18.4s + shrn2 \y2\().8h, v21.4s, #ROW_SHIFT + shrn2 \y3\().8h, v20.4s, #ROW_SHIFT + shrn2 \y4\().8h, v19.4s, #ROW_SHIFT + + trn1 v16.8h, \y1\().8h, \y2\().8h + trn2 v17.8h, \y1\().8h, \y2\().8h + trn1 v18.8h, \y3\().8h, \y4\().8h + trn2 v19.8h, \y3\().8h, \y4\().8h + trn1 \y1\().4s, v16.4s, v18.4s + trn1 \y2\().4s, v17.4s, v19.4s + trn2 \y3\().4s, v16.4s, v18.4s + trn2 \y4\().4s, v17.4s, v19.4s .endm .macro declare_idct_col4_neon i, l function idct_col4_neon\i - dup v23.4H, z4c + dup v23.4h, z4c .if \i == 1 - add v23.4H, v23.4H, v24.4H + add v23.4h, v23.4h, v24.4h .else - mov v5.D[0], v24.D[1] - add v23.4H, v23.4H, v5.4H + mov v5.d[0], v24.d[1] + add v23.4h, v23.4h, v5.4h .endif - smull v23.4S, v23.4H, z4 + smull v23.4s, v23.4h, z4 idct_col4_top v24, v25, v26, v27, \i, \l - mov x4, v28.D[\i - 1] - mov x5, v29.D[\i - 1] + mov x4, v28.d[\i - 1] + mov x5, v29.d[\i - 1] cmp x4, #0 b.eq 1f - smull\i v7.4S, v28\l, z4 - add v19.4S, v19.4S, v7.4S - sub v20.4S, v20.4S, v7.4S - sub v21.4S, v21.4S, v7.4S - add v22.4S, v22.4S, v7.4S + smull\i v7.4s, v28\l, z4 + add v19.4s, v19.4s, v7.4s + sub v20.4s, v20.4s, v7.4s + sub v21.4s, v21.4s, v7.4s + add v22.4s, v22.4s, v7.4s -1: mov x4, v30.D[\i - 1] +1: mov x4, v30.d[\i - 1] cmp x5, #0 b.eq 2f - smlal\i v17.4S, v29\l, z5 - smlsl\i v18.4S, v29\l, z1 - smlal\i v5.4S, v29\l, z7 - smlal\i v6.4S, v29\l, z3 + smlal\i v17.4s, v29\l, z5 + smlsl\i v18.4s, v29\l, z1 + smlal\i v5.4s, v29\l, z7 + smlal\i v6.4s, v29\l, z3 -2: mov x5, v31.D[\i - 1] +2: mov x5, v31.d[\i - 1] cmp x4, #0 b.eq 3f - smull\i v7.4S, v30\l, z6 - smull\i v16.4S, v30\l, z2 - add v19.4S, v19.4S, v7.4S - sub v22.4S, v22.4S, v7.4S - sub v20.4S, v20.4S, v16.4S - add v21.4S, v21.4S, v16.4S + smull\i v7.4s, v30\l, z6 + smull\i v16.4s, v30\l, z2 + add v19.4s, v19.4s, v7.4s + sub v22.4s, v22.4s, v7.4s + sub v20.4s, v20.4s, v16.4s + add v21.4s, v21.4s, v16.4s 3: cmp x5, #0 b.eq 4f - smlal\i v17.4S, v31\l, z7 - smlsl\i v18.4S, v31\l, z5 - smlal\i v5.4S, v31\l, z3 - smlsl\i v6.4S, v31\l, z1 + smlal\i v17.4s, v31\l, z7 + smlsl\i v18.4s, v31\l, z5 + smlal\i v5.4s, v31\l, z3 + smlsl\i v6.4s, v31\l, z1 -4: addhn v7.4H, v19.4S, v17.4S - addhn2 v7.8H, v20.4S, v18.4S - subhn v18.4H, v20.4S, v18.4S - subhn2 v18.8H, v19.4S, v17.4S +4: addhn v7.4h, v19.4s, v17.4s + addhn2 v7.8h, v20.4s, v18.4s + subhn v18.4h, v20.4s, v18.4s + subhn2 v18.8h, v19.4s, v17.4s - addhn v16.4H, v21.4S, v5.4S - addhn2 v16.8H, v22.4S, v6.4S - subhn v17.4H, v22.4S, v6.4S - subhn2 v17.8H, v21.4S, v5.4S + addhn v16.4h, v21.4s, v5.4s + addhn2 v16.8h, v22.4s, v6.4s + subhn v17.4h, v22.4s, v6.4s + subhn2 v17.8h, v21.4s, v5.4s ret endfunc @@ -229,33 +229,33 @@ function ff_simple_idct_put_neon, export=1 idct_row4_neon v28, v29, v30, v31, 2 bl idct_col4_neon1 - sqshrun v1.8B, v7.8H, #COL_SHIFT-16 - sqshrun2 v1.16B, v16.8H, #COL_SHIFT-16 - sqshrun v3.8B, v17.8H, #COL_SHIFT-16 - sqshrun2 v3.16B, v18.8H, #COL_SHIFT-16 + sqshrun v1.8b, v7.8h, #COL_SHIFT-16 + sqshrun2 v1.16b, v16.8h, #COL_SHIFT-16 + sqshrun v3.8b, v17.8h, #COL_SHIFT-16 + sqshrun2 v3.16b, v18.8h, #COL_SHIFT-16 bl idct_col4_neon2 - sqshrun v2.8B, v7.8H, #COL_SHIFT-16 - sqshrun2 v2.16B, v16.8H, #COL_SHIFT-16 - sqshrun v4.8B, v17.8H, #COL_SHIFT-16 - sqshrun2 v4.16B, v18.8H, #COL_SHIFT-16 + sqshrun v2.8b, v7.8h, #COL_SHIFT-16 + sqshrun2 v2.16b, v16.8h, #COL_SHIFT-16 + sqshrun v4.8b, v17.8h, #COL_SHIFT-16 + sqshrun2 v4.16b, v18.8h, #COL_SHIFT-16 - zip1 v16.4S, v1.4S, v2.4S - zip2 v17.4S, v1.4S, v2.4S + zip1 v16.4s, v1.4s, v2.4s + zip2 v17.4s, v1.4s, v2.4s - st1 {v16.D}[0], [x0], x1 - st1 {v16.D}[1], [x0], x1 + st1 {v16.d}[0], [x0], x1 + st1 {v16.d}[1], [x0], x1 - zip1 v18.4S, v3.4S, v4.4S - zip2 v19.4S, v3.4S, v4.4S + zip1 v18.4s, v3.4s, v4.4s + zip2 v19.4s, v3.4s, v4.4s - st1 {v17.D}[0], [x0], x1 - st1 {v17.D}[1], [x0], x1 - st1 {v18.D}[0], [x0], x1 - st1 {v18.D}[1], [x0], x1 - st1 {v19.D}[0], [x0], x1 - st1 {v19.D}[1], [x0], x1 + st1 {v17.d}[0], [x0], x1 + st1 {v17.d}[1], [x0], x1 + st1 {v18.d}[0], [x0], x1 + st1 {v18.d}[1], [x0], x1 + st1 {v19.d}[0], [x0], x1 + st1 {v19.d}[1], [x0], x1 idct_end endfunc @@ -267,59 +267,59 @@ function ff_simple_idct_add_neon, export=1 idct_row4_neon v28, v29, v30, v31, 2 bl idct_col4_neon1 - sshr v1.8H, v7.8H, #COL_SHIFT-16 - sshr v2.8H, v16.8H, #COL_SHIFT-16 - sshr v3.8H, v17.8H, #COL_SHIFT-16 - sshr v4.8H, v18.8H, #COL_SHIFT-16 + sshr v1.8h, v7.8h, #COL_SHIFT-16 + sshr v2.8h, v16.8h, #COL_SHIFT-16 + sshr v3.8h, v17.8h, #COL_SHIFT-16 + sshr v4.8h, v18.8h, #COL_SHIFT-16 bl idct_col4_neon2 - sshr v7.8H, v7.8H, #COL_SHIFT-16 - sshr v16.8H, v16.8H, #COL_SHIFT-16 - sshr v17.8H, v17.8H, #COL_SHIFT-16 - sshr v18.8H, v18.8H, #COL_SHIFT-16 + sshr v7.8h, v7.8h, #COL_SHIFT-16 + sshr v16.8h, v16.8h, #COL_SHIFT-16 + sshr v17.8h, v17.8h, #COL_SHIFT-16 + sshr v18.8h, v18.8h, #COL_SHIFT-16 mov x9, x0 - ld1 {v19.D}[0], [x0], x1 - zip1 v23.2D, v1.2D, v7.2D - zip2 v24.2D, v1.2D, v7.2D - ld1 {v19.D}[1], [x0], x1 - zip1 v25.2D, v2.2D, v16.2D - zip2 v26.2D, v2.2D, v16.2D - ld1 {v20.D}[0], [x0], x1 - zip1 v27.2D, v3.2D, v17.2D - zip2 v28.2D, v3.2D, v17.2D - ld1 {v20.D}[1], [x0], x1 - zip1 v29.2D, v4.2D, v18.2D - zip2 v30.2D, v4.2D, v18.2D - ld1 {v21.D}[0], [x0], x1 - uaddw v23.8H, v23.8H, v19.8B - uaddw2 v24.8H, v24.8H, v19.16B - ld1 {v21.D}[1], [x0], x1 - sqxtun v23.8B, v23.8H - sqxtun2 v23.16B, v24.8H - ld1 {v22.D}[0], [x0], x1 - uaddw v24.8H, v25.8H, v20.8B - uaddw2 v25.8H, v26.8H, v20.16B - ld1 {v22.D}[1], [x0], x1 - sqxtun v24.8B, v24.8H - sqxtun2 v24.16B, v25.8H - st1 {v23.D}[0], [x9], x1 - uaddw v25.8H, v27.8H, v21.8B - uaddw2 v26.8H, v28.8H, v21.16B - st1 {v23.D}[1], [x9], x1 - sqxtun v25.8B, v25.8H - sqxtun2 v25.16B, v26.8H - st1 {v24.D}[0], [x9], x1 - uaddw v26.8H, v29.8H, v22.8B - uaddw2 v27.8H, v30.8H, v22.16B - st1 {v24.D}[1], [x9], x1 - sqxtun v26.8B, v26.8H - sqxtun2 v26.16B, v27.8H - st1 {v25.D}[0], [x9], x1 - st1 {v25.D}[1], [x9], x1 - st1 {v26.D}[0], [x9], x1 - st1 {v26.D}[1], [x9], x1 + ld1 {v19.d}[0], [x0], x1 + zip1 v23.2d, v1.2d, v7.2d + zip2 v24.2d, v1.2d, v7.2d + ld1 {v19.d}[1], [x0], x1 + zip1 v25.2d, v2.2d, v16.2d + zip2 v26.2d, v2.2d, v16.2d + ld1 {v20.d}[0], [x0], x1 + zip1 v27.2d, v3.2d, v17.2d + zip2 v28.2d, v3.2d, v17.2d + ld1 {v20.d}[1], [x0], x1 + zip1 v29.2d, v4.2d, v18.2d + zip2 v30.2d, v4.2d, v18.2d + ld1 {v21.d}[0], [x0], x1 + uaddw v23.8h, v23.8h, v19.8b + uaddw2 v24.8h, v24.8h, v19.16b + ld1 {v21.d}[1], [x0], x1 + sqxtun v23.8b, v23.8h + sqxtun2 v23.16b, v24.8h + ld1 {v22.d}[0], [x0], x1 + uaddw v24.8h, v25.8h, v20.8b + uaddw2 v25.8h, v26.8h, v20.16b + ld1 {v22.d}[1], [x0], x1 + sqxtun v24.8b, v24.8h + sqxtun2 v24.16b, v25.8h + st1 {v23.d}[0], [x9], x1 + uaddw v25.8h, v27.8h, v21.8b + uaddw2 v26.8h, v28.8h, v21.16b + st1 {v23.d}[1], [x9], x1 + sqxtun v25.8b, v25.8h + sqxtun2 v25.16b, v26.8h + st1 {v24.d}[0], [x9], x1 + uaddw v26.8h, v29.8h, v22.8b + uaddw2 v27.8h, v30.8h, v22.16b + st1 {v24.d}[1], [x9], x1 + sqxtun v26.8b, v26.8h + sqxtun2 v26.16b, v27.8h + st1 {v25.d}[0], [x9], x1 + st1 {v25.d}[1], [x9], x1 + st1 {v26.d}[0], [x9], x1 + st1 {v26.d}[1], [x9], x1 idct_end endfunc @@ -333,30 +333,30 @@ function ff_simple_idct_neon, export=1 sub x2, x2, #128 bl idct_col4_neon1 - sshr v1.8H, v7.8H, #COL_SHIFT-16 - sshr v2.8H, v16.8H, #COL_SHIFT-16 - sshr v3.8H, v17.8H, #COL_SHIFT-16 - sshr v4.8H, v18.8H, #COL_SHIFT-16 + sshr v1.8h, v7.8h, #COL_SHIFT-16 + sshr v2.8h, v16.8h, #COL_SHIFT-16 + sshr v3.8h, v17.8h, #COL_SHIFT-16 + sshr v4.8h, v18.8h, #COL_SHIFT-16 bl idct_col4_neon2 - sshr v7.8H, v7.8H, #COL_SHIFT-16 - sshr v16.8H, v16.8H, #COL_SHIFT-16 - sshr v17.8H, v17.8H, #COL_SHIFT-16 - sshr v18.8H, v18.8H, #COL_SHIFT-16 - - zip1 v23.2D, v1.2D, v7.2D - zip2 v24.2D, v1.2D, v7.2D - st1 {v23.2D,v24.2D}, [x2], #32 - zip1 v25.2D, v2.2D, v16.2D - zip2 v26.2D, v2.2D, v16.2D - st1 {v25.2D,v26.2D}, [x2], #32 - zip1 v27.2D, v3.2D, v17.2D - zip2 v28.2D, v3.2D, v17.2D - st1 {v27.2D,v28.2D}, [x2], #32 - zip1 v29.2D, v4.2D, v18.2D - zip2 v30.2D, v4.2D, v18.2D - st1 {v29.2D,v30.2D}, [x2], #32 + sshr v7.8h, v7.8h, #COL_SHIFT-16 + sshr v16.8h, v16.8h, #COL_SHIFT-16 + sshr v17.8h, v17.8h, #COL_SHIFT-16 + sshr v18.8h, v18.8h, #COL_SHIFT-16 + + zip1 v23.2d, v1.2d, v7.2d + zip2 v24.2d, v1.2d, v7.2d + st1 {v23.2d,v24.2d}, [x2], #32 + zip1 v25.2d, v2.2d, v16.2d + zip2 v26.2d, v2.2d, v16.2d + st1 {v25.2d,v26.2d}, [x2], #32 + zip1 v27.2d, v3.2d, v17.2d + zip2 v28.2d, v3.2d, v17.2d + st1 {v27.2d,v28.2d}, [x2], #32 + zip1 v29.2d, v4.2d, v18.2d + zip2 v30.2d, v4.2d, v18.2d + st1 {v29.2d,v30.2d}, [x2], #32 idct_end endfunc diff --git a/libavcodec/aarch64/synth_filter_init.c b/libavcodec/aarch64/synth_filter_init.c index 801b46e2176..aea6aaf419f 100644 --- a/libavcodec/aarch64/synth_filter_init.c +++ b/libavcodec/aarch64/synth_filter_init.c @@ -23,15 +23,8 @@ #include "libavutil/aarch64/cpu.h" #include "libavutil/attributes.h" #include "libavutil/internal.h" -#include "libavcodec/fft.h" #include "libavcodec/synth_filter.h" -#include "asm-offsets.h" - -#if HAVE_NEON || HAVE_VFP -AV_CHECK_OFFSET(FFTContext, imdct_half, IMDCT_HALF); -#endif - void ff_synth_filter_float_neon(AVTXContext *imdct, float *synth_buf_ptr, int *synth_buf_offset, float synth_buf2[32], const float window[512], diff --git a/libavcodec/aarch64/synth_filter_neon.S b/libavcodec/aarch64/synth_filter_neon.S index 259fa6e66c7..63aefcb56ef 100644 --- a/libavcodec/aarch64/synth_filter_neon.S +++ b/libavcodec/aarch64/synth_filter_neon.S @@ -19,8 +19,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "asm-offsets.h" - #include "libavutil/aarch64/asm.S" .macro inner_loop diff --git a/libavcodec/aarch64/vp8dsp_neon.S b/libavcodec/aarch64/vp8dsp_neon.S index 4bbf16d1a4c..e385293ba78 100644 --- a/libavcodec/aarch64/vp8dsp_neon.S +++ b/libavcodec/aarch64/vp8dsp_neon.S @@ -330,32 +330,32 @@ endfunc // v17: hev // convert to signed value: - eor v3.16b, v3.16b, v21.16b // PS0 = P0 ^ 0x80 - eor v4.16b, v4.16b, v21.16b // QS0 = Q0 ^ 0x80 - - movi v20.8h, #3 - ssubl v18.8h, v4.8b, v3.8b // QS0 - PS0 - ssubl2 v19.8h, v4.16b, v3.16b // (widened to 16bit) - eor v2.16b, v2.16b, v21.16b // PS1 = P1 ^ 0x80 - eor v5.16b, v5.16b, v21.16b // QS1 = Q1 ^ 0x80 - mul v18.8h, v18.8h, v20.8h // w = 3 * (QS0 - PS0) - mul v19.8h, v19.8h, v20.8h - - sqsub v20.16b, v2.16b, v5.16b // clamp(PS1-QS1) - movi v22.16b, #4 - movi v23.16b, #3 + eor v3.16b, v3.16b, v21.16b // PS0 = P0 ^ 0x80 + eor v4.16b, v4.16b, v21.16b // QS0 = Q0 ^ 0x80 + + movi v20.8h, #3 + ssubl v18.8h, v4.8b, v3.8b // QS0 - PS0 + ssubl2 v19.8h, v4.16b, v3.16b // (widened to 16bit) + eor v2.16b, v2.16b, v21.16b // PS1 = P1 ^ 0x80 + eor v5.16b, v5.16b, v21.16b // QS1 = Q1 ^ 0x80 + mul v18.8h, v18.8h, v20.8h // w = 3 * (QS0 - PS0) + mul v19.8h, v19.8h, v20.8h + + sqsub v20.16b, v2.16b, v5.16b // clamp(PS1-QS1) + movi v22.16b, #4 + movi v23.16b, #3 .if \inner - and v20.16b, v20.16b, v17.16b // if(hev) w += clamp(PS1-QS1) + and v20.16b, v20.16b, v17.16b // if(hev) w += clamp(PS1-QS1) .endif - saddw v18.8h, v18.8h, v20.8b // w += clamp(PS1-QS1) - saddw2 v19.8h, v19.8h, v20.16b - sqxtn v18.8b, v18.8h // narrow result back into v18 - sqxtn2 v18.16b, v19.8h + saddw v18.8h, v18.8h, v20.8b // w += clamp(PS1-QS1) + saddw2 v19.8h, v19.8h, v20.16b + sqxtn v18.8b, v18.8h // narrow result back into v18 + sqxtn2 v18.16b, v19.8h .if !\inner && !\simple - eor v1.16b, v1.16b, v21.16b // PS2 = P2 ^ 0x80 - eor v6.16b, v6.16b, v21.16b // QS2 = Q2 ^ 0x80 + eor v1.16b, v1.16b, v21.16b // PS2 = P2 ^ 0x80 + eor v6.16b, v6.16b, v21.16b // QS2 = Q2 ^ 0x80 .endif - and v18.16b, v18.16b, v16.16b // w &= normal_limit + and v18.16b, v18.16b, v16.16b // w &= normal_limit // registers used at this point.. // v0 -> P3 (don't corrupt) @@ -375,44 +375,44 @@ endfunc // P0 = s2u(PS0 + c2); .if \simple - sqadd v19.16b, v18.16b, v22.16b // c1 = clamp((w&hev)+4) - sqadd v20.16b, v18.16b, v23.16b // c2 = clamp((w&hev)+3) - sshr v19.16b, v19.16b, #3 // c1 >>= 3 - sshr v20.16b, v20.16b, #3 // c2 >>= 3 - sqsub v4.16b, v4.16b, v19.16b // QS0 = clamp(QS0-c1) - sqadd v3.16b, v3.16b, v20.16b // PS0 = clamp(PS0+c2) - eor v4.16b, v4.16b, v21.16b // Q0 = QS0 ^ 0x80 - eor v3.16b, v3.16b, v21.16b // P0 = PS0 ^ 0x80 - eor v5.16b, v5.16b, v21.16b // Q1 = QS1 ^ 0x80 - eor v2.16b, v2.16b, v21.16b // P1 = PS1 ^ 0x80 + sqadd v19.16b, v18.16b, v22.16b // c1 = clamp((w&hev)+4) + sqadd v20.16b, v18.16b, v23.16b // c2 = clamp((w&hev)+3) + sshr v19.16b, v19.16b, #3 // c1 >>= 3 + sshr v20.16b, v20.16b, #3 // c2 >>= 3 + sqsub v4.16b, v4.16b, v19.16b // QS0 = clamp(QS0-c1) + sqadd v3.16b, v3.16b, v20.16b // PS0 = clamp(PS0+c2) + eor v4.16b, v4.16b, v21.16b // Q0 = QS0 ^ 0x80 + eor v3.16b, v3.16b, v21.16b // P0 = PS0 ^ 0x80 + eor v5.16b, v5.16b, v21.16b // Q1 = QS1 ^ 0x80 + eor v2.16b, v2.16b, v21.16b // P1 = PS1 ^ 0x80 .elseif \inner // the !is4tap case of filter_common, only used for inner blocks // c3 = ((c1&~hev) + 1) >> 1; // Q1 = s2u(QS1 - c3); // P1 = s2u(PS1 + c3); - sqadd v19.16b, v18.16b, v22.16b // c1 = clamp((w&hev)+4) - sqadd v20.16b, v18.16b, v23.16b // c2 = clamp((w&hev)+3) - sshr v19.16b, v19.16b, #3 // c1 >>= 3 - sshr v20.16b, v20.16b, #3 // c2 >>= 3 - sqsub v4.16b, v4.16b, v19.16b // QS0 = clamp(QS0-c1) - sqadd v3.16b, v3.16b, v20.16b // PS0 = clamp(PS0+c2) - bic v19.16b, v19.16b, v17.16b // c1 & ~hev - eor v4.16b, v4.16b, v21.16b // Q0 = QS0 ^ 0x80 - srshr v19.16b, v19.16b, #1 // c3 >>= 1 - eor v3.16b, v3.16b, v21.16b // P0 = PS0 ^ 0x80 - sqsub v5.16b, v5.16b, v19.16b // QS1 = clamp(QS1-c3) - sqadd v2.16b, v2.16b, v19.16b // PS1 = clamp(PS1+c3) - eor v5.16b, v5.16b, v21.16b // Q1 = QS1 ^ 0x80 - eor v2.16b, v2.16b, v21.16b // P1 = PS1 ^ 0x80 + sqadd v19.16b, v18.16b, v22.16b // c1 = clamp((w&hev)+4) + sqadd v20.16b, v18.16b, v23.16b // c2 = clamp((w&hev)+3) + sshr v19.16b, v19.16b, #3 // c1 >>= 3 + sshr v20.16b, v20.16b, #3 // c2 >>= 3 + sqsub v4.16b, v4.16b, v19.16b // QS0 = clamp(QS0-c1) + sqadd v3.16b, v3.16b, v20.16b // PS0 = clamp(PS0+c2) + bic v19.16b, v19.16b, v17.16b // c1 & ~hev + eor v4.16b, v4.16b, v21.16b // Q0 = QS0 ^ 0x80 + srshr v19.16b, v19.16b, #1 // c3 >>= 1 + eor v3.16b, v3.16b, v21.16b // P0 = PS0 ^ 0x80 + sqsub v5.16b, v5.16b, v19.16b // QS1 = clamp(QS1-c3) + sqadd v2.16b, v2.16b, v19.16b // PS1 = clamp(PS1+c3) + eor v5.16b, v5.16b, v21.16b // Q1 = QS1 ^ 0x80 + eor v2.16b, v2.16b, v21.16b // P1 = PS1 ^ 0x80 .else - and v20.16b, v18.16b, v17.16b // w & hev - sqadd v19.16b, v20.16b, v22.16b // c1 = clamp((w&hev)+4) - sqadd v20.16b, v20.16b, v23.16b // c2 = clamp((w&hev)+3) - sshr v19.16b, v19.16b, #3 // c1 >>= 3 - sshr v20.16b, v20.16b, #3 // c2 >>= 3 - bic v18.16b, v18.16b, v17.16b // w &= ~hev - sqsub v4.16b, v4.16b, v19.16b // QS0 = clamp(QS0-c1) - sqadd v3.16b, v3.16b, v20.16b // PS0 = clamp(PS0+c2) + and v20.16b, v18.16b, v17.16b // w & hev + sqadd v19.16b, v20.16b, v22.16b // c1 = clamp((w&hev)+4) + sqadd v20.16b, v20.16b, v23.16b // c2 = clamp((w&hev)+3) + sshr v19.16b, v19.16b, #3 // c1 >>= 3 + sshr v20.16b, v20.16b, #3 // c2 >>= 3 + bic v18.16b, v18.16b, v17.16b // w &= ~hev + sqsub v4.16b, v4.16b, v19.16b // QS0 = clamp(QS0-c1) + sqadd v3.16b, v3.16b, v20.16b // PS0 = clamp(PS0+c2) // filter_mbedge: // a = clamp((27*w + 63) >> 7); @@ -424,35 +424,35 @@ endfunc // a = clamp((9*w + 63) >> 7); // Q2 = s2u(QS2 - a); // P2 = s2u(PS2 + a); - movi v17.8h, #63 - sshll v22.8h, v18.8b, #3 - sshll2 v23.8h, v18.16b, #3 - saddw v22.8h, v22.8h, v18.8b - saddw2 v23.8h, v23.8h, v18.16b - add v16.8h, v17.8h, v22.8h - add v17.8h, v17.8h, v23.8h // 9*w + 63 - add v19.8h, v16.8h, v22.8h - add v20.8h, v17.8h, v23.8h // 18*w + 63 - add v22.8h, v19.8h, v22.8h - add v23.8h, v20.8h, v23.8h // 27*w + 63 - sqshrn v16.8b, v16.8h, #7 - sqshrn2 v16.16b, v17.8h, #7 // clamp(( 9*w + 63)>>7) - sqshrn v19.8b, v19.8h, #7 - sqshrn2 v19.16b, v20.8h, #7 // clamp((18*w + 63)>>7) - sqshrn v22.8b, v22.8h, #7 - sqshrn2 v22.16b, v23.8h, #7 // clamp((27*w + 63)>>7) - sqadd v1.16b, v1.16b, v16.16b // PS2 = clamp(PS2+a) - sqsub v6.16b, v6.16b, v16.16b // QS2 = clamp(QS2-a) - sqadd v2.16b, v2.16b, v19.16b // PS1 = clamp(PS1+a) - sqsub v5.16b, v5.16b, v19.16b // QS1 = clamp(QS1-a) - sqadd v3.16b, v3.16b, v22.16b // PS0 = clamp(PS0+a) - sqsub v4.16b, v4.16b, v22.16b // QS0 = clamp(QS0-a) - eor v3.16b, v3.16b, v21.16b // P0 = PS0 ^ 0x80 - eor v4.16b, v4.16b, v21.16b // Q0 = QS0 ^ 0x80 - eor v2.16b, v2.16b, v21.16b // P1 = PS1 ^ 0x80 - eor v5.16b, v5.16b, v21.16b // Q1 = QS1 ^ 0x80 - eor v1.16b, v1.16b, v21.16b // P2 = PS2 ^ 0x80 - eor v6.16b, v6.16b, v21.16b // Q2 = QS2 ^ 0x80 + movi v17.8h, #63 + sshll v22.8h, v18.8b, #3 + sshll2 v23.8h, v18.16b, #3 + saddw v22.8h, v22.8h, v18.8b + saddw2 v23.8h, v23.8h, v18.16b + add v16.8h, v17.8h, v22.8h + add v17.8h, v17.8h, v23.8h // 9*w + 63 + add v19.8h, v16.8h, v22.8h + add v20.8h, v17.8h, v23.8h // 18*w + 63 + add v22.8h, v19.8h, v22.8h + add v23.8h, v20.8h, v23.8h // 27*w + 63 + sqshrn v16.8b, v16.8h, #7 + sqshrn2 v16.16b, v17.8h, #7 // clamp(( 9*w + 63)>>7) + sqshrn v19.8b, v19.8h, #7 + sqshrn2 v19.16b, v20.8h, #7 // clamp((18*w + 63)>>7) + sqshrn v22.8b, v22.8h, #7 + sqshrn2 v22.16b, v23.8h, #7 // clamp((27*w + 63)>>7) + sqadd v1.16b, v1.16b, v16.16b // PS2 = clamp(PS2+a) + sqsub v6.16b, v6.16b, v16.16b // QS2 = clamp(QS2-a) + sqadd v2.16b, v2.16b, v19.16b // PS1 = clamp(PS1+a) + sqsub v5.16b, v5.16b, v19.16b // QS1 = clamp(QS1-a) + sqadd v3.16b, v3.16b, v22.16b // PS0 = clamp(PS0+a) + sqsub v4.16b, v4.16b, v22.16b // QS0 = clamp(QS0-a) + eor v3.16b, v3.16b, v21.16b // P0 = PS0 ^ 0x80 + eor v4.16b, v4.16b, v21.16b // Q0 = QS0 ^ 0x80 + eor v2.16b, v2.16b, v21.16b // P1 = PS1 ^ 0x80 + eor v5.16b, v5.16b, v21.16b // Q1 = QS1 ^ 0x80 + eor v1.16b, v1.16b, v21.16b // P2 = PS2 ^ 0x80 + eor v6.16b, v6.16b, v21.16b // Q2 = QS2 ^ 0x80 .endif .endm @@ -507,48 +507,48 @@ function ff_vp8_v_loop_filter8uv\name\()_neon, export=1 sub x0, x0, x2, lsl #2 sub x1, x1, x2, lsl #2 // Load pixels: - ld1 {v0.d}[0], [x0], x2 // P3 - ld1 {v0.d}[1], [x1], x2 // P3 - ld1 {v1.d}[0], [x0], x2 // P2 - ld1 {v1.d}[1], [x1], x2 // P2 - ld1 {v2.d}[0], [x0], x2 // P1 - ld1 {v2.d}[1], [x1], x2 // P1 - ld1 {v3.d}[0], [x0], x2 // P0 - ld1 {v3.d}[1], [x1], x2 // P0 - ld1 {v4.d}[0], [x0], x2 // Q0 - ld1 {v4.d}[1], [x1], x2 // Q0 - ld1 {v5.d}[0], [x0], x2 // Q1 - ld1 {v5.d}[1], [x1], x2 // Q1 - ld1 {v6.d}[0], [x0], x2 // Q2 - ld1 {v6.d}[1], [x1], x2 // Q2 - ld1 {v7.d}[0], [x0] // Q3 - ld1 {v7.d}[1], [x1] // Q3 - - dup v22.16b, w3 // flim_E - dup v23.16b, w4 // flim_I + ld1 {v0.d}[0], [x0], x2 // P3 + ld1 {v0.d}[1], [x1], x2 // P3 + ld1 {v1.d}[0], [x0], x2 // P2 + ld1 {v1.d}[1], [x1], x2 // P2 + ld1 {v2.d}[0], [x0], x2 // P1 + ld1 {v2.d}[1], [x1], x2 // P1 + ld1 {v3.d}[0], [x0], x2 // P0 + ld1 {v3.d}[1], [x1], x2 // P0 + ld1 {v4.d}[0], [x0], x2 // Q0 + ld1 {v4.d}[1], [x1], x2 // Q0 + ld1 {v5.d}[0], [x0], x2 // Q1 + ld1 {v5.d}[1], [x1], x2 // Q1 + ld1 {v6.d}[0], [x0], x2 // Q2 + ld1 {v6.d}[1], [x1], x2 // Q2 + ld1 {v7.d}[0], [x0] // Q3 + ld1 {v7.d}[1], [x1] // Q3 + + dup v22.16b, w3 // flim_E + dup v23.16b, w4 // flim_I vp8_loop_filter inner=\inner, hev_thresh=w5 // back up to P2: u,v -= stride * 6 - sub x0, x0, x2, lsl #2 - sub x1, x1, x2, lsl #2 - sub x0, x0, x2, lsl #1 - sub x1, x1, x2, lsl #1 + sub x0, x0, x2, lsl #2 + sub x1, x1, x2, lsl #2 + sub x0, x0, x2, lsl #1 + sub x1, x1, x2, lsl #1 // Store pixels: - st1 {v1.d}[0], [x0], x2 // P2 - st1 {v1.d}[1], [x1], x2 // P2 - st1 {v2.d}[0], [x0], x2 // P1 - st1 {v2.d}[1], [x1], x2 // P1 - st1 {v3.d}[0], [x0], x2 // P0 - st1 {v3.d}[1], [x1], x2 // P0 - st1 {v4.d}[0], [x0], x2 // Q0 - st1 {v4.d}[1], [x1], x2 // Q0 - st1 {v5.d}[0], [x0], x2 // Q1 - st1 {v5.d}[1], [x1], x2 // Q1 - st1 {v6.d}[0], [x0] // Q2 - st1 {v6.d}[1], [x1] // Q2 + st1 {v1.d}[0], [x0], x2 // P2 + st1 {v1.d}[1], [x1], x2 // P2 + st1 {v2.d}[0], [x0], x2 // P1 + st1 {v2.d}[1], [x1], x2 // P1 + st1 {v3.d}[0], [x0], x2 // P0 + st1 {v3.d}[1], [x1], x2 // P0 + st1 {v4.d}[0], [x0], x2 // Q0 + st1 {v4.d}[1], [x1], x2 // Q0 + st1 {v5.d}[0], [x0], x2 // Q1 + st1 {v5.d}[1], [x1], x2 // Q1 + st1 {v6.d}[0], [x0] // Q2 + st1 {v6.d}[1], [x1] // Q2 ret endfunc @@ -579,7 +579,7 @@ function ff_vp8_h_loop_filter16\name\()_neon, export=1 ld1 {v6.d}[1], [x0], x1 ld1 {v7.d}[1], [x0], x1 - transpose_8x16B v0, v1, v2, v3, v4, v5, v6, v7, v30, v31 + transpose_8x16B v0, v1, v2, v3, v4, v5, v6, v7, v30, v31 dup v22.16b, w2 // flim_E .if !\simple @@ -590,7 +590,7 @@ function ff_vp8_h_loop_filter16\name\()_neon, export=1 sub x0, x0, x1, lsl #4 // backup 16 rows - transpose_8x16B v0, v1, v2, v3, v4, v5, v6, v7, v30, v31 + transpose_8x16B v0, v1, v2, v3, v4, v5, v6, v7, v30, v31 // Store pixels: st1 {v0.d}[0], [x0], x1 @@ -624,24 +624,24 @@ function ff_vp8_h_loop_filter8uv\name\()_neon, export=1 sub x1, x1, #4 // Load pixels: - ld1 {v0.d}[0], [x0], x2 // load u - ld1 {v0.d}[1], [x1], x2 // load v - ld1 {v1.d}[0], [x0], x2 - ld1 {v1.d}[1], [x1], x2 - ld1 {v2.d}[0], [x0], x2 - ld1 {v2.d}[1], [x1], x2 - ld1 {v3.d}[0], [x0], x2 - ld1 {v3.d}[1], [x1], x2 - ld1 {v4.d}[0], [x0], x2 - ld1 {v4.d}[1], [x1], x2 - ld1 {v5.d}[0], [x0], x2 - ld1 {v5.d}[1], [x1], x2 - ld1 {v6.d}[0], [x0], x2 - ld1 {v6.d}[1], [x1], x2 - ld1 {v7.d}[0], [x0], x2 - ld1 {v7.d}[1], [x1], x2 - - transpose_8x16B v0, v1, v2, v3, v4, v5, v6, v7, v30, v31 + ld1 {v0.d}[0], [x0], x2 // load u + ld1 {v0.d}[1], [x1], x2 // load v + ld1 {v1.d}[0], [x0], x2 + ld1 {v1.d}[1], [x1], x2 + ld1 {v2.d}[0], [x0], x2 + ld1 {v2.d}[1], [x1], x2 + ld1 {v3.d}[0], [x0], x2 + ld1 {v3.d}[1], [x1], x2 + ld1 {v4.d}[0], [x0], x2 + ld1 {v4.d}[1], [x1], x2 + ld1 {v5.d}[0], [x0], x2 + ld1 {v5.d}[1], [x1], x2 + ld1 {v6.d}[0], [x0], x2 + ld1 {v6.d}[1], [x1], x2 + ld1 {v7.d}[0], [x0], x2 + ld1 {v7.d}[1], [x1], x2 + + transpose_8x16B v0, v1, v2, v3, v4, v5, v6, v7, v30, v31 dup v22.16b, w3 // flim_E dup v23.16b, w4 // flim_I @@ -651,25 +651,25 @@ function ff_vp8_h_loop_filter8uv\name\()_neon, export=1 sub x0, x0, x2, lsl #3 // backup u 8 rows sub x1, x1, x2, lsl #3 // backup v 8 rows - transpose_8x16B v0, v1, v2, v3, v4, v5, v6, v7, v30, v31 + transpose_8x16B v0, v1, v2, v3, v4, v5, v6, v7, v30, v31 // Store pixels: - st1 {v0.d}[0], [x0], x2 // load u - st1 {v0.d}[1], [x1], x2 // load v - st1 {v1.d}[0], [x0], x2 - st1 {v1.d}[1], [x1], x2 - st1 {v2.d}[0], [x0], x2 - st1 {v2.d}[1], [x1], x2 - st1 {v3.d}[0], [x0], x2 - st1 {v3.d}[1], [x1], x2 - st1 {v4.d}[0], [x0], x2 - st1 {v4.d}[1], [x1], x2 - st1 {v5.d}[0], [x0], x2 - st1 {v5.d}[1], [x1], x2 - st1 {v6.d}[0], [x0], x2 - st1 {v6.d}[1], [x1], x2 - st1 {v7.d}[0], [x0] - st1 {v7.d}[1], [x1] + st1 {v0.d}[0], [x0], x2 // load u + st1 {v0.d}[1], [x1], x2 // load v + st1 {v1.d}[0], [x0], x2 + st1 {v1.d}[1], [x1], x2 + st1 {v2.d}[0], [x0], x2 + st1 {v2.d}[1], [x1], x2 + st1 {v3.d}[0], [x0], x2 + st1 {v3.d}[1], [x1], x2 + st1 {v4.d}[0], [x0], x2 + st1 {v4.d}[1], [x1], x2 + st1 {v5.d}[0], [x0], x2 + st1 {v5.d}[1], [x1], x2 + st1 {v6.d}[0], [x0], x2 + st1 {v6.d}[1], [x1], x2 + st1 {v7.d}[0], [x0] + st1 {v7.d}[1], [x1] ret diff --git a/libavcodec/ac3_parser.c b/libavcodec/ac3_parser.c index 13b8d3b7d89..283139288cc 100644 --- a/libavcodec/ac3_parser.c +++ b/libavcodec/ac3_parser.c @@ -204,7 +204,9 @@ int av_ac3_parse_header(const uint8_t *buf, size_t size, AC3HeaderInfo hdr; int err; - init_get_bits8(&gb, buf, size); + err = init_get_bits8(&gb, buf, size); + if (err < 0) + return AVERROR_INVALIDDATA; err = ff_ac3_parse_header(&gb, &hdr); if (err < 0) return AVERROR_INVALIDDATA; diff --git a/libavcodec/ac3dec.c b/libavcodec/ac3dec.c index 0b120e6140a..504c75cdd45 100644 --- a/libavcodec/ac3dec.c +++ b/libavcodec/ac3dec.c @@ -190,14 +190,6 @@ static void ac3_downmix(AVCodecContext *avctx) const AVChannelLayout stereo = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO; /* allow downmixing to stereo or mono */ -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (avctx->request_channel_layout) { - av_channel_layout_uninit(&s->downmix_layout); - av_channel_layout_from_mask(&s->downmix_layout, avctx->request_channel_layout); - } -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (avctx->ch_layout.nb_channels > 1 && !av_channel_layout_compare(&s->downmix_layout, &mono)) { av_channel_layout_uninit(&avctx->ch_layout); @@ -1714,6 +1706,7 @@ static int ac3_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (!err) { avctx->sample_rate = s->sample_rate; avctx->bit_rate = s->bit_rate + s->prev_bit_rate; + avctx->profile = s->eac3_extension_type_a == 1 ? AV_PROFILE_EAC3_DDP_ATMOS : AV_PROFILE_UNKNOWN; } if (!avctx->sample_rate) { diff --git a/libavcodec/ac3dec.h b/libavcodec/ac3dec.h index 138b462abb5..98de7b5abff 100644 --- a/libavcodec/ac3dec.h +++ b/libavcodec/ac3dec.h @@ -90,7 +90,6 @@ typedef struct AC3DecodeContext { int lfe_on; ///< lfe channel in use int dialog_normalization[2]; ///< dialog level in dBFS (dialnorm) int compression_exists[2]; ///< compression field is valid for frame (compre) - int compression_gain[2]; ///< gain to apply for heavy compression (compr) int channel_map; ///< custom channel map (chanmap) int preferred_downmix; ///< Preferred 2-channel downmix mode (dmixmod) int center_mix_level; ///< Center mix level index @@ -100,8 +99,8 @@ typedef struct AC3DecodeContext { int lfe_mix_level_exists; ///< indicates if lfemixlevcod is specified (lfemixlevcode) int lfe_mix_level; ///< LFE mix level index (lfemixlevcod) int eac3; ///< indicates if current frame is E-AC-3 - int eac3_frame_dependent_found; ///< bitstream has E-AC-3 dependent frame(s) int eac3_subsbtreamid_found; ///< bitstream has E-AC-3 additional substream(s) + int eac3_extension_type_a; ///< bitstream has E-AC-3 extension type A enabled frame(s) int dolby_surround_mode; ///< dolby surround mode (dsurmod) int dolby_surround_ex_mode; ///< dolby surround ex mode (dsurexmod) int dolby_headphone_mode; ///< dolby headphone mode (dheadphonmod) diff --git a/libavcodec/ac3dec_float.c b/libavcodec/ac3dec_float.c index b8868d8ee15..550a9017de1 100644 --- a/libavcodec/ac3dec_float.c +++ b/libavcodec/ac3dec_float.c @@ -33,6 +33,7 @@ #include "ac3dec.h" #include "codec_internal.h" +#include "profiles.h" #include "eac3dec.c" #include "ac3dec.c" @@ -42,7 +43,7 @@ static const AVOption options[] = { { "heavy_compr", "enable heavy dynamic range compression", OFFSET(heavy_compression), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, PAR }, { "target_level", "target level in -dBFS (0 not applied)", OFFSET(target_level), AV_OPT_TYPE_INT, {.i64 = 0 }, -31, 0, PAR }, -{"dmix_mode", "Preferred Stereo Downmix Mode", OFFSET(preferred_stereo_downmix), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 2, 0, "dmix_mode"}, +{"dmix_mode", "Preferred Stereo Downmix Mode", OFFSET(preferred_stereo_downmix), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 2, 0, .unit = "dmix_mode"}, {"ltrt_cmixlev", "Lt/Rt Center Mix Level", OFFSET(ltrt_center_mix_level), AV_OPT_TYPE_FLOAT, {.dbl = -1.0 }, -1.0, 2.0, 0}, {"ltrt_surmixlev", "Lt/Rt Surround Mix Level", OFFSET(ltrt_surround_mix_level), AV_OPT_TYPE_FLOAT, {.dbl = -1.0 }, -1.0, 2.0, 0}, {"loro_cmixlev", "Lo/Ro Center Mix Level", OFFSET(loro_center_mix_level), AV_OPT_TYPE_FLOAT, {.dbl = -1.0 }, -1.0, 2.0, 0}, @@ -92,6 +93,7 @@ const FFCodec ff_eac3_decoder = { .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .p.priv_class = &ac3_eac3_decoder_class, + .p.profiles = NULL_IF_CONFIG_SMALL(ff_eac3_profiles), .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; #endif diff --git a/libavcodec/ac3dsp.c b/libavcodec/ac3dsp.c index 22cb5f242e8..8397e03d32e 100644 --- a/libavcodec/ac3dsp.c +++ b/libavcodec/ac3dsp.c @@ -54,7 +54,7 @@ static void ac3_exponent_min_c(uint8_t *exp, int num_reuse_blocks, int nb_coefs) } } -static void float_to_fixed24_c(int32_t *dst, const float *src, unsigned int len) +static void float_to_fixed24_c(int32_t *dst, const float *src, size_t len) { const float scale = 1 << 24; do { @@ -395,5 +395,7 @@ av_cold void ff_ac3dsp_init(AC3DSPContext *c) ff_ac3dsp_init_x86(c); #elif ARCH_MIPS ff_ac3dsp_init_mips(c); +#elif ARCH_RISCV + ff_ac3dsp_init_riscv(c); #endif } diff --git a/libavcodec/ac3dsp.h b/libavcodec/ac3dsp.h index 33e51e202ea..ae33b361a9b 100644 --- a/libavcodec/ac3dsp.h +++ b/libavcodec/ac3dsp.h @@ -22,6 +22,7 @@ #ifndef AVCODEC_AC3DSP_H #define AVCODEC_AC3DSP_H +#include #include /** @@ -47,13 +48,13 @@ typedef struct AC3DSPContext { * [-(1<<24),(1<<24)] * * @param dst destination array of int32_t. - * constraints: 16-byte aligned + * constraints: 32-byte aligned * @param src source array of float. - * constraints: 16-byte aligned + * constraints: 32-byte aligned * @param len number of elements to convert. * constraints: multiple of 32 greater than zero */ - void (*float_to_fixed24)(int32_t *dst, const float *src, unsigned int len); + void (*float_to_fixed24)(int32_t *dst, const float *src, size_t len); /** * Calculate bit allocation pointers. @@ -109,6 +110,7 @@ void ff_ac3dsp_init (AC3DSPContext *c); void ff_ac3dsp_init_arm(AC3DSPContext *c); void ff_ac3dsp_init_x86(AC3DSPContext *c); void ff_ac3dsp_init_mips(AC3DSPContext *c); +void ff_ac3dsp_init_riscv(AC3DSPContext *c); void ff_ac3dsp_downmix(AC3DSPContext *c, float **samples, float **matrix, int out_ch, int in_ch, int len); diff --git a/libavcodec/ac3enc.c b/libavcodec/ac3enc.c index 3cb4e5f0295..ff43088df55 100644 --- a/libavcodec/ac3enc.c +++ b/libavcodec/ac3enc.c @@ -33,6 +33,7 @@ #include "libavutil/avstring.h" #include "libavutil/channel_layout.h" #include "libavutil/crc.h" +#include "libavutil/emms.h" #include "libavutil/internal.h" #include "libavutil/mem_internal.h" #include "libavutil/opt.h" @@ -83,47 +84,47 @@ const AVOption ff_ac3_enc_options[] = { {"surround_mixlev", "Surround Mix Level", OFFSET(surround_mix_level), AV_OPT_TYPE_FLOAT, {.dbl = LEVEL_MINUS_6DB }, 0.0, 1.0, AC3ENC_PARAM}, /* audio production information */ {"mixing_level", "Mixing Level", OFFSET(mixing_level), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, 111, AC3ENC_PARAM}, -{"room_type", "Room Type", OFFSET(room_type), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, AC3ENC_OPT_SMALL_ROOM, AC3ENC_PARAM, "room_type"}, - {"notindicated", "Not Indicated (default)", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_NOT_INDICATED }, INT_MIN, INT_MAX, AC3ENC_PARAM, "room_type"}, - {"large", "Large Room", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_LARGE_ROOM }, INT_MIN, INT_MAX, AC3ENC_PARAM, "room_type"}, - {"small", "Small Room", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_SMALL_ROOM }, INT_MIN, INT_MAX, AC3ENC_PARAM, "room_type"}, +{"room_type", "Room Type", OFFSET(room_type), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, AC3ENC_OPT_SMALL_ROOM, AC3ENC_PARAM, .unit = "room_type"}, + {"notindicated", "Not Indicated (default)", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_NOT_INDICATED }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "room_type"}, + {"large", "Large Room", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_LARGE_ROOM }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "room_type"}, + {"small", "Small Room", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_SMALL_ROOM }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "room_type"}, /* Metadata Options */ {"per_frame_metadata", "Allow Changing Metadata Per-Frame", OFFSET(allow_per_frame_metadata), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, AC3ENC_PARAM}, {"copyright", "Copyright Bit", OFFSET(copyright), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, 1, AC3ENC_PARAM}, {"dialnorm", "Dialogue Level (dB)", OFFSET(dialogue_level), AV_OPT_TYPE_INT, {.i64 = -31 }, -31, -1, AC3ENC_PARAM}, -{"dsur_mode", "Dolby Surround Mode", OFFSET(dolby_surround_mode), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, AC3ENC_OPT_MODE_ON, AC3ENC_PARAM, "dsur_mode"}, - {"notindicated", "Not Indicated (default)", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_NOT_INDICATED }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dsur_mode"}, - {"on", "Dolby Surround Encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_MODE_ON }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dsur_mode"}, - {"off", "Not Dolby Surround Encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_MODE_OFF }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dsur_mode"}, +{"dsur_mode", "Dolby Surround Mode", OFFSET(dolby_surround_mode), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, AC3ENC_OPT_MODE_ON, AC3ENC_PARAM, .unit = "dsur_mode"}, + {"notindicated", "Not Indicated (default)", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_NOT_INDICATED }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dsur_mode"}, + {"on", "Dolby Surround Encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_MODE_ON }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dsur_mode"}, + {"off", "Not Dolby Surround Encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_MODE_OFF }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dsur_mode"}, {"original", "Original Bit Stream", OFFSET(original), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, 1, AC3ENC_PARAM}, /* extended bitstream information */ -{"dmix_mode", "Preferred Stereo Downmix Mode", OFFSET(preferred_stereo_downmix), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, AC3ENC_OPT_DOWNMIX_DPLII, AC3ENC_PARAM, "dmix_mode"}, - {"notindicated", "Not Indicated (default)", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_NOT_INDICATED }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dmix_mode"}, - {"ltrt", "Lt/Rt Downmix Preferred", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_DOWNMIX_LTRT }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dmix_mode"}, - {"loro", "Lo/Ro Downmix Preferred", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_DOWNMIX_LORO }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dmix_mode"}, - {"dplii", "Dolby Pro Logic II Downmix Preferred", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_DOWNMIX_DPLII }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dmix_mode"}, +{"dmix_mode", "Preferred Stereo Downmix Mode", OFFSET(preferred_stereo_downmix), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, AC3ENC_OPT_DOWNMIX_DPLII, AC3ENC_PARAM, .unit = "dmix_mode"}, + {"notindicated", "Not Indicated (default)", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_NOT_INDICATED }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dmix_mode"}, + {"ltrt", "Lt/Rt Downmix Preferred", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_DOWNMIX_LTRT }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dmix_mode"}, + {"loro", "Lo/Ro Downmix Preferred", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_DOWNMIX_LORO }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dmix_mode"}, + {"dplii", "Dolby Pro Logic II Downmix Preferred", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_DOWNMIX_DPLII }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dmix_mode"}, {"ltrt_cmixlev", "Lt/Rt Center Mix Level", OFFSET(ltrt_center_mix_level), AV_OPT_TYPE_FLOAT, {.dbl = -1.0 }, -1.0, 2.0, AC3ENC_PARAM}, {"ltrt_surmixlev", "Lt/Rt Surround Mix Level", OFFSET(ltrt_surround_mix_level), AV_OPT_TYPE_FLOAT, {.dbl = -1.0 }, -1.0, 2.0, AC3ENC_PARAM}, {"loro_cmixlev", "Lo/Ro Center Mix Level", OFFSET(loro_center_mix_level), AV_OPT_TYPE_FLOAT, {.dbl = -1.0 }, -1.0, 2.0, AC3ENC_PARAM}, {"loro_surmixlev", "Lo/Ro Surround Mix Level", OFFSET(loro_surround_mix_level), AV_OPT_TYPE_FLOAT, {.dbl = -1.0 }, -1.0, 2.0, AC3ENC_PARAM}, -{"dsurex_mode", "Dolby Surround EX Mode", OFFSET(dolby_surround_ex_mode), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, AC3ENC_OPT_DSUREX_DPLIIZ, AC3ENC_PARAM, "dsurex_mode"}, - {"notindicated", "Not Indicated (default)", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_NOT_INDICATED }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dsurex_mode"}, - {"on", "Dolby Surround EX Encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_MODE_ON }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dsurex_mode"}, - {"off", "Not Dolby Surround EX Encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_MODE_OFF }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dsurex_mode"}, - {"dpliiz", "Dolby Pro Logic IIz-encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_DSUREX_DPLIIZ }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dsurex_mode"}, -{"dheadphone_mode", "Dolby Headphone Mode", OFFSET(dolby_headphone_mode), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, AC3ENC_OPT_MODE_ON, AC3ENC_PARAM, "dheadphone_mode"}, - {"notindicated", "Not Indicated (default)", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_NOT_INDICATED }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dheadphone_mode"}, - {"on", "Dolby Headphone Encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_MODE_ON }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dheadphone_mode"}, - {"off", "Not Dolby Headphone Encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_MODE_OFF }, INT_MIN, INT_MAX, AC3ENC_PARAM, "dheadphone_mode"}, -{"ad_conv_type", "A/D Converter Type", OFFSET(ad_converter_type), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, AC3ENC_OPT_ADCONV_HDCD, AC3ENC_PARAM, "ad_conv_type"}, - {"standard", "Standard (default)", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_ADCONV_STANDARD }, INT_MIN, INT_MAX, AC3ENC_PARAM, "ad_conv_type"}, - {"hdcd", "HDCD", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_ADCONV_HDCD }, INT_MIN, INT_MAX, AC3ENC_PARAM, "ad_conv_type"}, +{"dsurex_mode", "Dolby Surround EX Mode", OFFSET(dolby_surround_ex_mode), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, AC3ENC_OPT_DSUREX_DPLIIZ, AC3ENC_PARAM, .unit = "dsurex_mode"}, + {"notindicated", "Not Indicated (default)", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_NOT_INDICATED }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dsurex_mode"}, + {"on", "Dolby Surround EX Encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_MODE_ON }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dsurex_mode"}, + {"off", "Not Dolby Surround EX Encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_MODE_OFF }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dsurex_mode"}, + {"dpliiz", "Dolby Pro Logic IIz-encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_DSUREX_DPLIIZ }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dsurex_mode"}, +{"dheadphone_mode", "Dolby Headphone Mode", OFFSET(dolby_headphone_mode), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, AC3ENC_OPT_MODE_ON, AC3ENC_PARAM, .unit = "dheadphone_mode"}, + {"notindicated", "Not Indicated (default)", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_NOT_INDICATED }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dheadphone_mode"}, + {"on", "Dolby Headphone Encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_MODE_ON }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dheadphone_mode"}, + {"off", "Not Dolby Headphone Encoded", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_MODE_OFF }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "dheadphone_mode"}, +{"ad_conv_type", "A/D Converter Type", OFFSET(ad_converter_type), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_NONE }, AC3ENC_OPT_NONE, AC3ENC_OPT_ADCONV_HDCD, AC3ENC_PARAM, .unit = "ad_conv_type"}, + {"standard", "Standard (default)", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_ADCONV_STANDARD }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "ad_conv_type"}, + {"hdcd", "HDCD", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_ADCONV_HDCD }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "ad_conv_type"}, /* Other Encoding Options */ {"stereo_rematrixing", "Stereo Rematrixing", OFFSET(stereo_rematrixing), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, AC3ENC_PARAM}, -{"channel_coupling", "Channel Coupling", OFFSET(channel_coupling), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_AUTO }, AC3ENC_OPT_AUTO, AC3ENC_OPT_ON, AC3ENC_PARAM, "channel_coupling"}, - {"auto", "Selected by the Encoder", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_AUTO }, INT_MIN, INT_MAX, AC3ENC_PARAM, "channel_coupling"}, -{"cpl_start_band", "Coupling Start Band", OFFSET(cpl_start), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_AUTO }, AC3ENC_OPT_AUTO, 15, AC3ENC_PARAM, "cpl_start_band"}, - {"auto", "Selected by the Encoder", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_AUTO }, INT_MIN, INT_MAX, AC3ENC_PARAM, "cpl_start_band"}, +{"channel_coupling", "Channel Coupling", OFFSET(channel_coupling), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_AUTO }, AC3ENC_OPT_AUTO, AC3ENC_OPT_ON, AC3ENC_PARAM, .unit = "channel_coupling"}, + {"auto", "Selected by the Encoder", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_AUTO }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "channel_coupling"}, +{"cpl_start_band", "Coupling Start Band", OFFSET(cpl_start), AV_OPT_TYPE_INT, {.i64 = AC3ENC_OPT_AUTO }, AC3ENC_OPT_AUTO, 15, AC3ENC_PARAM, .unit = "cpl_start_band"}, + {"auto", "Selected by the Encoder", 0, AV_OPT_TYPE_CONST, {.i64 = AC3ENC_OPT_AUTO }, INT_MIN, INT_MAX, AC3ENC_PARAM, .unit = "cpl_start_band"}, {NULL} }; @@ -149,30 +150,6 @@ static uint8_t exponent_group_tab[2][3][256]; /** * List of supported channel layouts. */ -#if FF_API_OLD_CHANNEL_LAYOUT -const uint64_t ff_ac3_channel_layouts[19] = { - AV_CH_LAYOUT_MONO, - AV_CH_LAYOUT_STEREO, - AV_CH_LAYOUT_2_1, - AV_CH_LAYOUT_SURROUND, - AV_CH_LAYOUT_2_2, - AV_CH_LAYOUT_QUAD, - AV_CH_LAYOUT_4POINT0, - AV_CH_LAYOUT_5POINT0, - AV_CH_LAYOUT_5POINT0_BACK, - (AV_CH_LAYOUT_MONO | AV_CH_LOW_FREQUENCY), - (AV_CH_LAYOUT_STEREO | AV_CH_LOW_FREQUENCY), - (AV_CH_LAYOUT_2_1 | AV_CH_LOW_FREQUENCY), - (AV_CH_LAYOUT_SURROUND | AV_CH_LOW_FREQUENCY), - (AV_CH_LAYOUT_2_2 | AV_CH_LOW_FREQUENCY), - (AV_CH_LAYOUT_QUAD | AV_CH_LOW_FREQUENCY), - (AV_CH_LAYOUT_4POINT0 | AV_CH_LOW_FREQUENCY), - AV_CH_LAYOUT_5POINT1, - AV_CH_LAYOUT_5POINT1_BACK, - 0 -}; -#endif - const AVChannelLayout ff_ac3_ch_layouts[19] = { AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, diff --git a/libavcodec/ac3enc.h b/libavcodec/ac3enc.h index 55e88d69e4e..1cb1aac4b28 100644 --- a/libavcodec/ac3enc.h +++ b/libavcodec/ac3enc.h @@ -268,9 +268,6 @@ typedef struct AC3EncodeContext { void (*output_frame_header)(struct AC3EncodeContext *s); } AC3EncodeContext; -#if FF_API_OLD_CHANNEL_LAYOUT -extern const uint64_t ff_ac3_channel_layouts[19]; -#endif extern const AVChannelLayout ff_ac3_ch_layouts[19]; extern const AVOption ff_ac3_enc_options[]; extern const AVClass ff_ac3enc_class; diff --git a/libavcodec/ac3enc_fixed.c b/libavcodec/ac3enc_fixed.c index 88dfd66b91d..a9bbb0a04ed 100644 --- a/libavcodec/ac3enc_fixed.c +++ b/libavcodec/ac3enc_fixed.c @@ -122,7 +122,6 @@ const FFCodec ff_ac3_fixed_encoder = { .p.priv_class = &ff_ac3enc_class, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.supported_samplerates = ff_ac3_sample_rate_tab, - CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(ff_ac3_channel_layouts) .p.ch_layouts = ff_ac3_ch_layouts, .defaults = ff_ac3_enc_defaults, }; diff --git a/libavcodec/ac3enc_float.c b/libavcodec/ac3enc_float.c index ae351a535e9..3462c5f474b 100644 --- a/libavcodec/ac3enc_float.c +++ b/libavcodec/ac3enc_float.c @@ -125,7 +125,6 @@ const FFCodec ff_ac3_encoder = { AV_SAMPLE_FMT_NONE }, .p.priv_class = &ff_ac3enc_class, .p.supported_samplerates = ff_ac3_sample_rate_tab, - CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(ff_ac3_channel_layouts) .p.ch_layouts = ff_ac3_ch_layouts, .defaults = ff_ac3_enc_defaults, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, diff --git a/libavcodec/ac3enc_template.c b/libavcodec/ac3enc_template.c index be4ecebc9cc..34d07cc9e5b 100644 --- a/libavcodec/ac3enc_template.c +++ b/libavcodec/ac3enc_template.c @@ -110,9 +110,9 @@ static void apply_mdct(AC3EncodeContext *s) */ static void apply_channel_coupling(AC3EncodeContext *s) { - LOCAL_ALIGNED_16(CoefType, cpl_coords, [AC3_MAX_BLOCKS], [AC3_MAX_CHANNELS][16]); + LOCAL_ALIGNED_32(CoefType, cpl_coords, [AC3_MAX_BLOCKS], [AC3_MAX_CHANNELS][16]); #if AC3ENC_FLOAT - LOCAL_ALIGNED_16(int32_t, fixed_cpl_coords, [AC3_MAX_BLOCKS], [AC3_MAX_CHANNELS][16]); + LOCAL_ALIGNED_32(int32_t, fixed_cpl_coords, [AC3_MAX_BLOCKS], [AC3_MAX_CHANNELS][16]); #else int32_t (*fixed_cpl_coords)[AC3_MAX_CHANNELS][16] = cpl_coords; #endif @@ -223,6 +223,8 @@ static void apply_channel_coupling(AC3EncodeContext *s) } } + av_assert1(s->fbw_channels > 0); + /* calculate final coupling coordinates, taking into account reusing of coordinates in successive blocks */ for (bnd = 0; bnd < s->num_cpl_bands; bnd++) { diff --git a/libavcodec/ac3tab.h b/libavcodec/ac3tab.h index 2531d80677e..dcef643acb8 100644 --- a/libavcodec/ac3tab.h +++ b/libavcodec/ac3tab.h @@ -45,22 +45,6 @@ extern const uint8_t ff_ac3_band_start_tab[AC3_CRITICAL_BANDS+1]; extern const uint8_t ff_ac3_bin_to_band_tab[253]; extern const uint64_t ff_eac3_custom_channel_map_locations[16][2]; - -/** Custom channel map locations bitmask - * Other channels described in documentation: - * Lc/Rc pair, Lrs/Rrs pair, Ts, Lsd/Rsd pair, - * Lw/Rw pair, Lvh/Rvh pair, Cvh, Reserved, LFE2 - */ -enum CustomChannelMapLocation{ - AC3_CHMAP_L= 1<<(15-0), - AC3_CHMAP_C= 1<<(15-1), - AC3_CHMAP_R= 1<<(15-2), - AC3_CHMAP_L_SUR= 1<<(15-3), - AC3_CHMAP_R_SUR = 1<<(15-4), - AC3_CHMAP_C_SUR= 1<<(15-7), - AC3_CHMAP_LFE = 1<<(15-15) -}; - #define COMMON_CHANNEL_MAP \ { { 0, 1, }, { 0, 1, 2, } },\ { { 0, }, { 0, 1, } },\ diff --git a/libavcodec/adts_parser.c b/libavcodec/adts_parser.c index f2e155fc99d..28ad5ef5ebf 100644 --- a/libavcodec/adts_parser.c +++ b/libavcodec/adts_parser.c @@ -27,9 +27,14 @@ int av_adts_header_parse(const uint8_t *buf, uint32_t *samples, uint8_t *frames) { #if CONFIG_ADTS_HEADER + uint8_t tmpbuf[AV_AAC_ADTS_HEADER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; GetBitContext gb; AACADTSHeaderInfo hdr; - int err = init_get_bits8(&gb, buf, AV_AAC_ADTS_HEADER_SIZE); + int err; + if (!buf) + return AVERROR(EINVAL); + memcpy(tmpbuf, buf, AV_AAC_ADTS_HEADER_SIZE); + err = init_get_bits8(&gb, tmpbuf, AV_AAC_ADTS_HEADER_SIZE); if (err < 0) return err; err = ff_adts_header_parse(&gb, &hdr); diff --git a/libavcodec/adx.h b/libavcodec/adx.h index 8d5e0869ae5..60ce07ad614 100644 --- a/libavcodec/adx.h +++ b/libavcodec/adx.h @@ -35,9 +35,11 @@ typedef struct ADXChannelState { int s1,s2; } ADXChannelState; +#define MAX_CHANNELS 6 + typedef struct ADXContext { int channels; - ADXChannelState prev[2]; + ADXChannelState prev[MAX_CHANNELS]; int header_parsed; int eof; int cutoff; diff --git a/libavcodec/adx_parser.c b/libavcodec/adx_parser.c index 52aa14b7ad3..62b4415bc19 100644 --- a/libavcodec/adx_parser.c +++ b/libavcodec/adx_parser.c @@ -73,6 +73,16 @@ static int adx_parse(AVCodecParserContext *s1, s->remaining = 0; } else s->remaining -= buf_size; + } else if (avctx->ch_layout.nb_channels > 0) { + if (!s->block_size) + s->block_size = avctx->ch_layout.nb_channels * BLOCK_SIZE; + if (!s->remaining) + s->remaining = s->block_size; + if (s->remaining <= buf_size) { + next = s->remaining; + s->remaining = 0; + } else + s->remaining -= buf_size; } if (ff_combine_frame(pc, next, &buf, &buf_size) < 0 || !buf_size) { diff --git a/libavcodec/adxdec.c b/libavcodec/adxdec.c index 97a7e59686f..4300dede5e8 100644 --- a/libavcodec/adxdec.c +++ b/libavcodec/adxdec.c @@ -70,7 +70,7 @@ static int adx_decode_header(AVCodecContext *avctx, const uint8_t *buf, /* channels */ channels = buf[7]; - if (channels <= 0 || channels > 2) + if (channels <= 0 || channels > MAX_CHANNELS) return AVERROR_INVALIDDATA; if (avctx->ch_layout.nb_channels != channels) { diff --git a/libavcodec/agm.c b/libavcodec/agm.c index b37f1a42c96..88272cc3aa6 100644 --- a/libavcodec/agm.c +++ b/libavcodec/agm.c @@ -1015,12 +1015,12 @@ static int build_huff(const uint8_t *bitlen, VLC *vlc) } } - ff_free_vlc(vlc); - return ff_init_vlc_sparse(vlc, 13, nb_codes, + ff_vlc_free(vlc); + return ff_vlc_init_sparse(vlc, 13, nb_codes, bits, 1, 1, codes, 4, 4, symbols, 1, 1, - INIT_VLC_LE); + VLC_INIT_LE); } static int decode_huffman2(AVCodecContext *avctx, int header, int size) @@ -1100,7 +1100,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return AVERROR_INVALIDDATA; s->key_frame = (avpkt->flags & AV_PKT_FLAG_KEY); - frame->key_frame = s->key_frame; + if (s->key_frame) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = s->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if (!s->key_frame) { @@ -1171,7 +1174,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0) return ret; - if (frame->key_frame) { + if (frame->flags & AV_FRAME_FLAG_KEY) { if (!s->dct && !s->rgb) ret = decode_raw_intra(avctx, gbyte, frame); else if (!s->dct && s->rgb) @@ -1200,8 +1203,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (ret < 0) return ret; - av_frame_unref(s->prev_frame); - if ((ret = av_frame_ref(s->prev_frame, frame)) < 0) + if ((ret = av_frame_replace(s->prev_frame, frame)) < 0) return ret; frame->crop_top = avctx->coded_height - avctx->height; @@ -1253,7 +1255,7 @@ static av_cold int decode_close(AVCodecContext *avctx) { AGMContext *s = avctx->priv_data; - ff_free_vlc(&s->vlc); + ff_vlc_free(&s->vlc); av_frame_free(&s->prev_frame); av_freep(&s->mvectors); s->mvectors_size = 0; diff --git a/libavcodec/aic.c b/libavcodec/aic.c index 7ba1c02fdd4..f8b0f60354c 100644 --- a/libavcodec/aic.c +++ b/libavcodec/aic.c @@ -393,7 +393,7 @@ static int aic_decode_frame(AVCodecContext *avctx, AVFrame *frame, ctx->frame = frame; ctx->frame->pict_type = AV_PICTURE_TYPE_I; - ctx->frame->key_frame = 1; + ctx->frame->flags |= AV_FRAME_FLAG_KEY; off = FFALIGN(AIC_HDR_SIZE + ctx->num_x_slices * ctx->mb_height * 2, 4); diff --git a/libavcodec/alac.c b/libavcodec/alac.c index d10ecba7e63..538d1e5984e 100644 --- a/libavcodec/alac.c +++ b/libavcodec/alac.c @@ -112,6 +112,7 @@ static inline unsigned int decode_scalar(GetBitContext *gb, int k, int bps) static int rice_decompress(ALACContext *alac, int32_t *output_buffer, int nb_samples, int bps, int rice_history_mult) { + GetBitContext *gb = &alac->gb; int i; unsigned int history = alac->rice_initial_history; int sign_modifier = 0; @@ -120,13 +121,13 @@ static int rice_decompress(ALACContext *alac, int32_t *output_buffer, int k; unsigned int x; - if(get_bits_left(&alac->gb) <= 0) + if (get_bits_left(gb) <= 0) return AVERROR_INVALIDDATA; /* calculate rice param and decode next value */ k = av_log2((history >> 9) + 3); k = FFMIN(k, alac->rice_limit); - x = decode_scalar(&alac->gb, k, bps); + x = decode_scalar(gb, k, bps); x += sign_modifier; sign_modifier = 0; output_buffer[i] = (x >> 1) ^ -(x & 1); @@ -145,7 +146,7 @@ static int rice_decompress(ALACContext *alac, int32_t *output_buffer, /* calculate rice param and decode block size */ k = 7 - av_log2(history) + ((history + 16) >> 6); k = FFMIN(k, alac->rice_limit); - block_size = decode_scalar(&alac->gb, k, 16); + block_size = decode_scalar(gb, k, 16); if (block_size > 0) { if (block_size >= nb_samples - i) { @@ -168,7 +169,7 @@ static int rice_decompress(ALACContext *alac, int32_t *output_buffer, static inline int sign_only(int v) { - return v ? FFSIGN(v) : 0; + return FFDIFFSIGN(v, 0); } static void lpc_prediction(int32_t *error_buffer, uint32_t *buffer_out, @@ -239,16 +240,17 @@ static int decode_element(AVCodecContext *avctx, AVFrame *frame, int ch_index, { ALACContext *alac = avctx->priv_data; int has_size, bps, is_compressed, decorr_shift, decorr_left_weight, ret; + GetBitContext *gb = &alac->gb; uint32_t output_samples; int i, ch; - skip_bits(&alac->gb, 4); /* element instance tag */ - skip_bits(&alac->gb, 12); /* unused header bits */ + skip_bits(gb, 4); /* element instance tag */ + skip_bits(gb, 12); /* unused header bits */ /* the number of output samples is stored in the frame */ - has_size = get_bits1(&alac->gb); + has_size = get_bits1(gb); - alac->extra_bits = get_bits(&alac->gb, 2) << 3; + alac->extra_bits = get_bits(gb, 2) << 3; bps = alac->sample_size - alac->extra_bits + channels - 1; if (bps > 32) { avpriv_report_missing_feature(avctx, "bps %d", bps); @@ -258,10 +260,10 @@ static int decode_element(AVCodecContext *avctx, AVFrame *frame, int ch_index, return AVERROR_INVALIDDATA; /* whether the frame is compressed */ - is_compressed = !get_bits1(&alac->gb); + is_compressed = !get_bits1(gb); if (has_size) - output_samples = get_bits_long(&alac->gb, 32); + output_samples = get_bits_long(gb, 32); else output_samples = alac->max_samples_per_frame; if (!output_samples || output_samples > alac->max_samples_per_frame) { @@ -298,39 +300,40 @@ static int decode_element(AVCodecContext *avctx, AVFrame *frame, int ch_index, return AVERROR(ENOSYS); } - decorr_shift = get_bits(&alac->gb, 8); - decorr_left_weight = get_bits(&alac->gb, 8); + decorr_shift = get_bits(gb, 8); + decorr_left_weight = get_bits(gb, 8); if (channels == 2 && decorr_left_weight && decorr_shift > 31) return AVERROR_INVALIDDATA; for (ch = 0; ch < channels; ch++) { - prediction_type[ch] = get_bits(&alac->gb, 4); - lpc_quant[ch] = get_bits(&alac->gb, 4); - rice_history_mult[ch] = get_bits(&alac->gb, 3); - lpc_order[ch] = get_bits(&alac->gb, 5); + prediction_type[ch] = get_bits(gb, 4); + lpc_quant[ch] = get_bits(gb, 4); + rice_history_mult[ch] = get_bits(gb, 3); + lpc_order[ch] = get_bits(gb, 5); if (lpc_order[ch] >= alac->max_samples_per_frame || !lpc_quant[ch]) return AVERROR_INVALIDDATA; /* read the predictor table */ for (i = lpc_order[ch] - 1; i >= 0; i--) - lpc_coefs[ch][i] = get_sbits(&alac->gb, 16); + lpc_coefs[ch][i] = get_sbits(gb, 16); } if (alac->extra_bits) { + const int extra_bits = alac->extra_bits; + if (get_bits_left(gb) < (int64_t)alac->nb_samples * channels * extra_bits) + return AVERROR_INVALIDDATA; for (i = 0; i < alac->nb_samples; i++) { - if(get_bits_left(&alac->gb) <= 0) - return AVERROR_INVALIDDATA; for (ch = 0; ch < channels; ch++) - alac->extra_bits_buffer[ch][i] = get_bits(&alac->gb, alac->extra_bits); + alac->extra_bits_buffer[ch][i] = get_bits(gb, extra_bits); } } for (ch = 0; ch < channels; ch++) { - int ret=rice_decompress(alac, alac->predict_error_buffer[ch], + int ret = rice_decompress(alac, alac->predict_error_buffer[ch], alac->nb_samples, bps, rice_history_mult[ch] * alac->rice_history_mult / 4); - if(ret<0) + if (ret < 0) return ret; /* adaptive FIR filter */ @@ -354,13 +357,14 @@ static int decode_element(AVCodecContext *avctx, AVFrame *frame, int ch_index, bps, lpc_coefs[ch], lpc_order[ch], lpc_quant[ch]); } } else { + const int sample_size = alac->sample_size; /* not compressed, easy case */ + if (get_bits_left(gb) < (int64_t)alac->nb_samples * channels * sample_size) + return AVERROR_INVALIDDATA; for (i = 0; i < alac->nb_samples; i++) { - if(get_bits_left(&alac->gb) <= 0) - return AVERROR_INVALIDDATA; for (ch = 0; ch < channels; ch++) { alac->output_samples_buffer[ch][i] = - get_sbits_long(&alac->gb, alac->sample_size); + get_sbits_long(gb, sample_size); } } alac->extra_bits = 0; diff --git a/libavcodec/alacenc.c b/libavcodec/alacenc.c index 9598e5861e5..f43adb57f94 100644 --- a/libavcodec/alacenc.c +++ b/libavcodec/alacenc.c @@ -617,21 +617,6 @@ static int alac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, return 0; } -#if FF_API_OLD_CHANNEL_LAYOUT -static const uint64_t alac_channel_layouts[ALAC_MAX_CHANNELS + 1] = { - AV_CH_LAYOUT_MONO, - AV_CH_LAYOUT_STEREO, - AV_CH_LAYOUT_SURROUND, - AV_CH_LAYOUT_4POINT0, - AV_CH_LAYOUT_5POINT0_BACK, - AV_CH_LAYOUT_5POINT1_BACK, - AV_CH_LAYOUT_6POINT1_BACK, - AV_CH_LAYOUT_7POINT1_WIDE_BACK, - 0 -}; -#endif - - #define OFFSET(x) offsetof(AlacEncodeContext, x) #define AE AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { @@ -660,7 +645,6 @@ const FFCodec ff_alac_encoder = { .init = alac_encode_init, FF_CODEC_ENCODE_CB(alac_encode_frame), .close = alac_encode_close, - CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(alac_channel_layouts) .p.ch_layouts = ff_alac_ch_layouts, .p.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_S16P, diff --git a/libavcodec/aliaspixdec.c b/libavcodec/aliaspixdec.c index 45155d79cde..72f810d408e 100644 --- a/libavcodec/aliaspixdec.c +++ b/libavcodec/aliaspixdec.c @@ -70,7 +70,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f, return ret; f->pict_type = AV_PICTURE_TYPE_I; - f->key_frame = 1; + f->flags |= AV_FRAME_FLAG_KEY; x = 0; y = 1; diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index e593ad19afc..2386b450a6b 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -61,10 +61,6 @@ extern const FFCodec ff_avrn_decoder; extern const FFCodec ff_avs_decoder; extern const FFCodec ff_avui_encoder; extern const FFCodec ff_avui_decoder; -#if FF_API_AYUV_CODECID -extern const FFCodec ff_ayuv_encoder; -extern const FFCodec ff_ayuv_decoder; -#endif extern const FFCodec ff_bethsoftvid_decoder; extern const FFCodec ff_bfi_decoder; extern const FFCodec ff_bink_decoder; @@ -106,6 +102,7 @@ extern const FFCodec ff_dvvideo_encoder; extern const FFCodec ff_dvvideo_decoder; extern const FFCodec ff_dxa_decoder; extern const FFCodec ff_dxtory_decoder; +extern const FFCodec ff_dxv_encoder; extern const FFCodec ff_dxv_decoder; extern const FFCodec ff_eacmv_decoder; extern const FFCodec ff_eamad_decoder; @@ -151,7 +148,6 @@ extern const FFCodec ff_h263p_encoder; extern const FFCodec ff_h263p_decoder; extern const FFCodec ff_h263_v4l2m2m_decoder; extern const FFCodec ff_h264_decoder; -extern const FFCodec ff_h264_crystalhd_decoder; extern const FFCodec ff_h264_v4l2m2m_decoder; extern const FFCodec ff_h264_mediacodec_decoder; extern const FFCodec ff_h264_mediacodec_encoder; @@ -188,6 +184,7 @@ extern const FFCodec ff_jv_decoder; extern const FFCodec ff_kgv1_decoder; extern const FFCodec ff_kmvc_decoder; extern const FFCodec ff_lagarith_decoder; +extern const FFCodec ff_lead_decoder; extern const FFCodec ff_ljpeg_encoder; extern const FFCodec ff_loco_decoder; extern const FFCodec ff_lscr_decoder; @@ -209,13 +206,11 @@ extern const FFCodec ff_mpeg2video_encoder; extern const FFCodec ff_mpeg2video_decoder; extern const FFCodec ff_mpeg4_encoder; extern const FFCodec ff_mpeg4_decoder; -extern const FFCodec ff_mpeg4_crystalhd_decoder; extern const FFCodec ff_mpeg4_v4l2m2m_decoder; extern const FFCodec ff_mpeg4_mmal_decoder; extern const FFCodec ff_mpegvideo_decoder; extern const FFCodec ff_mpeg1_v4l2m2m_decoder; extern const FFCodec ff_mpeg2_mmal_decoder; -extern const FFCodec ff_mpeg2_crystalhd_decoder; extern const FFCodec ff_mpeg2_v4l2m2m_decoder; extern const FFCodec ff_mpeg2_qsv_decoder; extern const FFCodec ff_mpeg2_mediacodec_decoder; @@ -226,8 +221,8 @@ extern const FFCodec ff_msmpeg4v2_encoder; extern const FFCodec ff_msmpeg4v2_decoder; extern const FFCodec ff_msmpeg4v3_encoder; extern const FFCodec ff_msmpeg4v3_decoder; -extern const FFCodec ff_msmpeg4_crystalhd_decoder; extern const FFCodec ff_msp2_decoder; +extern const FFCodec ff_msrle_encoder; extern const FFCodec ff_msrle_decoder; extern const FFCodec ff_mss1_decoder; extern const FFCodec ff_mss2_decoder; @@ -251,6 +246,7 @@ extern const FFCodec ff_pbm_encoder; extern const FFCodec ff_pbm_decoder; extern const FFCodec ff_pcx_encoder; extern const FFCodec ff_pcx_decoder; +extern const FFCodec ff_pdv_decoder; extern const FFCodec ff_pfm_encoder; extern const FFCodec ff_pfm_decoder; extern const FFCodec ff_pgm_encoder; @@ -294,6 +290,7 @@ extern const FFCodec ff_roq_decoder; extern const FFCodec ff_rpza_encoder; extern const FFCodec ff_rpza_decoder; extern const FFCodec ff_rscc_decoder; +extern const FFCodec ff_rtv1_decoder; extern const FFCodec ff_rv10_encoder; extern const FFCodec ff_rv10_decoder; extern const FFCodec ff_rv20_encoder; @@ -360,7 +357,6 @@ extern const FFCodec ff_vbn_encoder; extern const FFCodec ff_vbn_decoder; extern const FFCodec ff_vble_decoder; extern const FFCodec ff_vc1_decoder; -extern const FFCodec ff_vc1_crystalhd_decoder; extern const FFCodec ff_vc1image_decoder; extern const FFCodec ff_vc1_mmal_decoder; extern const FFCodec ff_vc1_qsv_decoder; @@ -368,6 +364,7 @@ extern const FFCodec ff_vc1_v4l2m2m_decoder; extern const FFCodec ff_vc2_encoder; extern const FFCodec ff_vcr1_decoder; extern const FFCodec ff_vmdvideo_decoder; +extern const FFCodec ff_vmix_decoder; extern const FFCodec ff_vmnc_decoder; extern const FFCodec ff_vp3_decoder; extern const FFCodec ff_vp4_decoder; @@ -384,6 +381,7 @@ extern const FFCodec ff_vp9_rkmpp_decoder; extern const FFCodec ff_vp9_v4l2m2m_decoder; extern const FFCodec ff_vqa_decoder; extern const FFCodec ff_vqc_decoder; +extern const FFCodec ff_vvc_decoder; extern const FFCodec ff_wbmp_decoder; extern const FFCodec ff_wbmp_encoder; extern const FFCodec ff_webp_decoder; @@ -395,7 +393,6 @@ extern const FFCodec ff_wmv1_decoder; extern const FFCodec ff_wmv2_encoder; extern const FFCodec ff_wmv2_decoder; extern const FFCodec ff_wmv3_decoder; -extern const FFCodec ff_wmv3_crystalhd_decoder; extern const FFCodec ff_wmv3image_decoder; extern const FFCodec ff_wnv1_decoder; extern const FFCodec ff_xan_wc3_decoder; @@ -512,10 +509,12 @@ extern const FFCodec ff_nellymoser_decoder; extern const FFCodec ff_on2avc_decoder; extern const FFCodec ff_opus_encoder; extern const FFCodec ff_opus_decoder; +extern const FFCodec ff_osq_decoder; extern const FFCodec ff_paf_audio_decoder; extern const FFCodec ff_qcelp_decoder; extern const FFCodec ff_qdm2_decoder; extern const FFCodec ff_qdmc_decoder; +extern const FFCodec ff_qoa_decoder; extern const FFCodec ff_ra_144_encoder; extern const FFCodec ff_ra_144_decoder; extern const FFCodec ff_ra_288_decoder; @@ -759,6 +758,8 @@ extern const FFCodec ff_pcm_mulaw_at_decoder; extern const FFCodec ff_qdmc_at_decoder; extern const FFCodec ff_qdm2_at_decoder; extern FFCodec ff_libaom_av1_encoder; +/* preferred over libaribb24 */ +extern const FFCodec ff_libaribcaption_decoder; extern const FFCodec ff_libaribb24_decoder; extern const FFCodec ff_libcelt_decoder; extern const FFCodec ff_libcodec2_encoder; @@ -780,7 +781,6 @@ extern const FFCodec ff_libopencore_amrnb_encoder; extern const FFCodec ff_libopencore_amrnb_decoder; extern const FFCodec ff_libopencore_amrwb_decoder; extern const FFCodec ff_libopenjpeg_encoder; -extern const FFCodec ff_libopenjpeg_decoder; extern const FFCodec ff_libopus_encoder; extern const FFCodec ff_libopus_decoder; extern const FFCodec ff_librav1e_encoder; @@ -798,7 +798,7 @@ extern const FFCodec ff_libvorbis_decoder; extern const FFCodec ff_libvpx_vp8_encoder; extern const FFCodec ff_libvpx_vp8_decoder; extern FFCodec ff_libvpx_vp9_encoder; -extern FFCodec ff_libvpx_vp9_decoder; +extern const FFCodec ff_libvpx_vp9_decoder; /* preferred over libwebp */ extern const FFCodec ff_libwebp_anim_encoder; extern const FFCodec ff_libwebp_encoder; @@ -814,6 +814,8 @@ extern LIBX264_CONST FFCodec ff_libx264_encoder; #endif extern const FFCodec ff_libx264rgb_encoder; extern FFCodec ff_libx265_encoder; +extern const FFCodec ff_libxeve_encoder; +extern const FFCodec ff_libxevd_decoder; extern const FFCodec ff_libxavs_encoder; extern const FFCodec ff_libxavs2_encoder; extern const FFCodec ff_libxvid_encoder; @@ -834,10 +836,12 @@ extern const FFCodec ff_libaom_av1_decoder; extern const FFCodec ff_av1_decoder; extern const FFCodec ff_av1_cuvid_decoder; extern const FFCodec ff_av1_mediacodec_decoder; +extern const FFCodec ff_av1_mediacodec_encoder; extern const FFCodec ff_av1_nvenc_encoder; extern const FFCodec ff_av1_qsv_decoder; extern const FFCodec ff_av1_qsv_encoder; extern const FFCodec ff_av1_amf_encoder; +extern const FFCodec ff_av1_vaapi_encoder; extern const FFCodec ff_libopenh264_encoder; extern const FFCodec ff_libopenh264_decoder; extern const FFCodec ff_h264_amf_encoder; @@ -871,17 +875,20 @@ extern const FFCodec ff_mpeg2_qsv_encoder; extern const FFCodec ff_mpeg2_vaapi_encoder; extern const FFCodec ff_mpeg4_cuvid_decoder; extern const FFCodec ff_mpeg4_mediacodec_decoder; +extern const FFCodec ff_mpeg4_mediacodec_encoder; extern const FFCodec ff_mpeg4_omx_encoder; extern const FFCodec ff_mpeg4_v4l2m2m_encoder; extern const FFCodec ff_prores_videotoolbox_encoder; extern const FFCodec ff_vc1_cuvid_decoder; extern const FFCodec ff_vp8_cuvid_decoder; extern const FFCodec ff_vp8_mediacodec_decoder; +extern const FFCodec ff_vp8_mediacodec_encoder; extern const FFCodec ff_vp8_qsv_decoder; extern const FFCodec ff_vp8_v4l2m2m_encoder; extern const FFCodec ff_vp8_vaapi_encoder; extern const FFCodec ff_vp9_cuvid_decoder; extern const FFCodec ff_vp9_mediacodec_decoder; +extern const FFCodec ff_vp9_mediacodec_encoder; extern const FFCodec ff_vp9_qsv_decoder; extern const FFCodec ff_vp9_vaapi_encoder; extern const FFCodec ff_vp9_qsv_encoder; diff --git a/libavcodec/alpha/idctdsp_alpha.c b/libavcodec/alpha/idctdsp_alpha.c index bd43842535a..ff770c15fd8 100644 --- a/libavcodec/alpha/idctdsp_alpha.c +++ b/libavcodec/alpha/idctdsp_alpha.c @@ -19,6 +19,7 @@ */ #include "libavutil/attributes.h" +#include "libavcodec/avcodec.h" #include "libavcodec/idctdsp.h" #include "idctdsp_alpha.h" #include "asm.h" diff --git a/libavcodec/alsdec.c b/libavcodec/alsdec.c index 4605b2248fa..7262cdb4b36 100644 --- a/libavcodec/alsdec.c +++ b/libavcodec/alsdec.c @@ -2110,8 +2110,8 @@ static av_cold int decode_init(AVCodecContext *avctx) if (sconf->floating) { ctx->acf = av_malloc_array(channels, sizeof(*ctx->acf)); - ctx->shift_value = av_malloc_array(channels, sizeof(*ctx->shift_value)); - ctx->last_shift_value = av_malloc_array(channels, sizeof(*ctx->last_shift_value)); + ctx->shift_value = av_calloc(channels, sizeof(*ctx->shift_value)); + ctx->last_shift_value = av_calloc(channels, sizeof(*ctx->last_shift_value)); ctx->last_acf_mantissa = av_malloc_array(channels, sizeof(*ctx->last_acf_mantissa)); ctx->raw_mantissa = av_calloc(channels, sizeof(*ctx->raw_mantissa)); @@ -2190,6 +2190,10 @@ const FFCodec ff_als_decoder = { .close = decode_end, FF_CODEC_DECODE_CB(decode_frame), .flush = flush, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c index c487fc48aad..061859f85ce 100644 --- a/libavcodec/amfenc.c +++ b/libavcodec/amfenc.c @@ -430,9 +430,9 @@ static int amf_copy_surface(AVCodecContext *avctx, const AVFrame *frame, dst_data[i] = plane->pVtbl->GetNative(plane); dst_linesize[i] = plane->pVtbl->GetHPitch(plane); } - av_image_copy(dst_data, dst_linesize, - (const uint8_t**)frame->data, frame->linesize, frame->format, - avctx->width, avctx->height); + av_image_copy2(dst_data, dst_linesize, + frame->data, frame->linesize, frame->format, + avctx->width, avctx->height); return 0; } @@ -481,7 +481,7 @@ static int amf_copy_buffer(AVCodecContext *avctx, AVPacket *pkt, AMFBuffer *buff AVERROR_UNKNOWN, "timestamp_list is empty\n"); // calc dts shift if max_b_frames > 0 - if (avctx->max_b_frames > 0 && ctx->dts_delay == 0) { + if ((ctx->max_b_frames > 0 || ((ctx->pa_adaptive_mini_gop == 1) ? true : false)) && ctx->dts_delay == 0) { int64_t timestamp_last = AV_NOPTS_VALUE; size_t can_read = av_fifo_can_read(ctx->timestamp_list); @@ -593,6 +593,8 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) AMFData *data = NULL; AVFrame *frame = ctx->delayed_frame; int block_and_wait; + int query_output_data_flag = 0; + AMF_RESULT res_resubmit; if (!ctx->encoder) return AVERROR(EINVAL); @@ -715,56 +717,61 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) do { block_and_wait = 0; // poll data - res_query = ctx->encoder->pVtbl->QueryOutput(ctx->encoder, &data); - if (data) { - // copy data to packet - AMFBuffer* buffer; - AMFGuid guid = IID_AMFBuffer(); - data->pVtbl->QueryInterface(data, &guid, (void**)&buffer); // query for buffer interface - ret = amf_copy_buffer(avctx, avpkt, buffer); - - buffer->pVtbl->Release(buffer); - - if (data->pVtbl->HasProperty(data, L"av_frame_ref")) { - AMFBuffer *frame_ref_storage_buffer; - res = amf_get_property_buffer(data, L"av_frame_ref", &frame_ref_storage_buffer); - AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetProperty failed for \"av_frame_ref\" with error %d\n", res); - amf_release_buffer_with_frame_ref(frame_ref_storage_buffer); - ctx->hwsurfaces_in_queue--; - } - - data->pVtbl->Release(data); + if (!avpkt->data && !avpkt->buf) { + res_query = ctx->encoder->pVtbl->QueryOutput(ctx->encoder, &data); + if (data) { + // copy data to packet + AMFBuffer *buffer; + AMFGuid guid = IID_AMFBuffer(); + query_output_data_flag = 1; + data->pVtbl->QueryInterface(data, &guid, (void**)&buffer); // query for buffer interface + ret = amf_copy_buffer(avctx, avpkt, buffer); + + buffer->pVtbl->Release(buffer); + + if (data->pVtbl->HasProperty(data, L"av_frame_ref")) { + AMFBuffer* frame_ref_storage_buffer; + res = amf_get_property_buffer(data, L"av_frame_ref", &frame_ref_storage_buffer); + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "GetProperty failed for \"av_frame_ref\" with error %d\n", res); + amf_release_buffer_with_frame_ref(frame_ref_storage_buffer); + ctx->hwsurfaces_in_queue--; + } - AMF_RETURN_IF_FALSE(ctx, ret >= 0, ret, "amf_copy_buffer() failed with error %d\n", ret); + data->pVtbl->Release(data); - if (ctx->delayed_surface != NULL) { // try to resubmit frame - res = ctx->encoder->pVtbl->SubmitInput(ctx->encoder, (AMFData*)ctx->delayed_surface); - if (res != AMF_INPUT_FULL) { - int64_t pts = ctx->delayed_surface->pVtbl->GetPts(ctx->delayed_surface); - ctx->delayed_surface->pVtbl->Release(ctx->delayed_surface); - ctx->delayed_surface = NULL; - av_frame_unref(ctx->delayed_frame); - AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "Repeated SubmitInput() failed with error %d\n", res); + AMF_RETURN_IF_FALSE(ctx, ret >= 0, ret, "amf_copy_buffer() failed with error %d\n", ret); + } + } + res_resubmit = AMF_OK; + if (ctx->delayed_surface != NULL) { // try to resubmit frame + res_resubmit = ctx->encoder->pVtbl->SubmitInput(ctx->encoder, (AMFData*)ctx->delayed_surface); + if (res_resubmit != AMF_INPUT_FULL) { + int64_t pts = ctx->delayed_surface->pVtbl->GetPts(ctx->delayed_surface); + ctx->delayed_surface->pVtbl->Release(ctx->delayed_surface); + ctx->delayed_surface = NULL; + av_frame_unref(ctx->delayed_frame); + AMF_RETURN_IF_FALSE(ctx, res_resubmit == AMF_OK, AVERROR_UNKNOWN, "Repeated SubmitInput() failed with error %d\n", res_resubmit); + + ret = av_fifo_write(ctx->timestamp_list, &pts, 1); + if (ret < 0) + return ret; + } + } else if (ctx->delayed_drain) { // try to resubmit drain + res = ctx->encoder->pVtbl->Drain(ctx->encoder); + if (res != AMF_INPUT_FULL) { + ctx->delayed_drain = 0; + ctx->eof = 1; // drain started + AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "Repeated Drain() failed with error %d\n", res); + } else { + av_log(avctx, AV_LOG_WARNING, "Data acquired but delayed drain submission got AMF_INPUT_FULL- should not happen\n"); + } + } - ret = av_fifo_write(ctx->timestamp_list, &pts, 1); - if (ret < 0) - return ret; - } else { - av_log(avctx, AV_LOG_WARNING, "Data acquired but delayed frame submission got AMF_INPUT_FULL- should not happen\n"); - } - } else if (ctx->delayed_drain) { // try to resubmit drain - res = ctx->encoder->pVtbl->Drain(ctx->encoder); - if (res != AMF_INPUT_FULL) { - ctx->delayed_drain = 0; - ctx->eof = 1; // drain started - AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_UNKNOWN, "Repeated Drain() failed with error %d\n", res); - } else { - av_log(avctx, AV_LOG_WARNING, "Data acquired but delayed drain submission got AMF_INPUT_FULL- should not happen\n"); - } + if (query_output_data_flag == 0) { + if (res_resubmit == AMF_INPUT_FULL || ctx->delayed_drain || (ctx->eof && res_query != AMF_EOF) || (ctx->hwsurfaces_in_queue >= ctx->hwsurfaces_in_queue_max)) { + block_and_wait = 1; + av_usleep(1000); } - } else if (ctx->delayed_surface != NULL || ctx->delayed_drain || (ctx->eof && res_query != AMF_EOF) || (ctx->hwsurfaces_in_queue >= ctx->hwsurfaces_in_queue_max)) { - block_and_wait = 1; - av_usleep(1000); // wait and poll again } } while (block_and_wait); diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h index 66e06807196..2dbd378ef87 100644 --- a/libavcodec/amfenc.h +++ b/libavcodec/amfenc.h @@ -30,6 +30,7 @@ #include "avcodec.h" #include "hwconfig.h" +#define MAX_LOOKAHEAD_DEPTH 41 /** * AMF trace writer callback class @@ -84,7 +85,7 @@ typedef struct AmfContext { int usage; int profile; int level; - int preanalysis; + int preencode; int quality; int b_frame_delta_qp; int ref_b_frame_delta_qp; @@ -107,6 +108,10 @@ typedef struct AmfContext { int me_half_pel; int me_quarter_pel; int aud; + int max_consecutive_b_frames; + int max_b_frames; + int qvbr_quality_level; + int hw_high_motion_quality_boost; // HEVC - specific options @@ -122,6 +127,26 @@ typedef struct AmfContext { enum AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_ENUM align; + // Preanalysis - specific options + + int preanalysis; + int pa_activity_type; + int pa_scene_change_detection; + int pa_scene_change_detection_sensitivity; + int pa_static_scene_detection; + int pa_static_scene_detection_sensitivity; + int pa_initial_qp; + int pa_max_qp; + int pa_caq_strength; + int pa_frame_sad; + int pa_ltr; + int pa_lookahead_buffer_depth; + int pa_paq_mode; + int pa_taq_mode; + int pa_high_motion_quality_boost_mode; + int pa_adaptive_mini_gop; + + } AmfContext; extern const AVCodecHWConfigInternal *const ff_amfenc_hw_configs[]; diff --git a/libavcodec/amfenc_av1.c b/libavcodec/amfenc_av1.c index 8093cb73575..3878f0d4613 100644 --- a/libavcodec/amfenc_av1.c +++ b/libavcodec/amfenc_av1.c @@ -25,61 +25,69 @@ #define OFFSET(x) offsetof(AmfContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "usage", "Set the encoding usage", OFFSET(usage), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING }, AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING, AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY, VE, "usage" }, - { "transcoding", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING }, 0, 0, VE, "usage" }, - { "lowlatency", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY }, 0, 0, VE, "usage" }, - - { "profile", "Set the profile (default main)", OFFSET(profile), AV_OPT_TYPE_INT,{.i64 = AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN }, AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN, AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN, VE, "profile" }, - { "main", "", 0, AV_OPT_TYPE_CONST,{.i64 = AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN }, 0, 0, VE, "profile" }, - - { "level", "Set the encoding level (default auto)", OFFSET(level), AV_OPT_TYPE_INT,{.i64 = 0 }, 0, AMF_VIDEO_ENCODER_AV1_LEVEL_7_3, VE, "level" }, - { "auto", "", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, VE, "level" }, - { "2.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_2_0 }, 0, 0, VE, "level" }, - { "2.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_2_1 }, 0, 0, VE, "level" }, - { "2.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_2_2 }, 0, 0, VE, "level" }, - { "2.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_2_3 }, 0, 0, VE, "level" }, - { "3.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_3_0 }, 0, 0, VE, "level" }, - { "3.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_3_1 }, 0, 0, VE, "level" }, - { "3.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_3_2 }, 0, 0, VE, "level" }, - { "3.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_3_3 }, 0, 0, VE, "level" }, - { "4.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_4_0 }, 0, 0, VE, "level" }, - { "4.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_4_1 }, 0, 0, VE, "level" }, - { "4.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_4_2 }, 0, 0, VE, "level" }, - { "4.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_4_3 }, 0, 0, VE, "level" }, - { "5.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_5_0 }, 0, 0, VE, "level" }, - { "5.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_5_1 }, 0, 0, VE, "level" }, - { "5.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_5_2 }, 0, 0, VE, "level" }, - { "5.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_5_3 }, 0, 0, VE, "level" }, - { "6.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_6_0 }, 0, 0, VE, "level" }, - { "6.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_6_1 }, 0, 0, VE, "level" }, - { "6.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_6_2 }, 0, 0, VE, "level" }, - { "6.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_6_3 }, 0, 0, VE, "level" }, - { "7.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_7_0 }, 0, 0, VE, "level" }, - { "7.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_7_1 }, 0, 0, VE, "level" }, - { "7.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_7_2 }, 0, 0, VE, "level" }, - { "7.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_7_3 }, 0, 0, VE, "level" }, - - { "quality", "Set the encoding quality", OFFSET(quality), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED }, AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_HIGH_QUALITY, AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED, VE, "quality" }, - { "balanced", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_BALANCED }, 0, 0, VE, "quality" }, - { "speed", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED }, 0, 0, VE, "quality" }, - { "quality", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_QUALITY }, 0, 0, VE, "quality" }, - { "high_quality", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_HIGH_QUALITY }, 0, 0, VE, "quality" }, - - { "rc", "Set the rate control mode", OFFSET(rate_control_mode), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_UNKNOWN }, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_UNKNOWN, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR, VE, "rc" }, - { "cqp", "Constant Quantization Parameter", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP }, 0, 0, VE, "rc" }, - { "vbr_latency", "Latency Constrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR }, 0, 0, VE, "rc" }, - { "vbr_peak", "Peak Contrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR }, 0, 0, VE, "rc" }, - { "cbr", "Constant Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR }, 0, 0, VE, "rc" }, - - { "header_insertion_mode", "Set header insertion mode", OFFSET(header_insertion_mode), AV_OPT_TYPE_INT,{.i64 = AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_NONE }, AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_NONE, AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_KEY_FRAME_ALIGNED, VE, "hdrmode" }, - { "none", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_NONE }, 0, 0, VE, "hdrmode" }, - { "gop", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_GOP_ALIGNED }, 0, 0, VE, "hdrmode" }, - { "frame", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_KEY_FRAME_ALIGNED }, 0, 0, VE, "hdrmode" }, - - { "preanalysis", "Enable preanalysis", OFFSET(preanalysis), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, VE}, + { "usage", "Set the encoding usage", OFFSET(usage), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING }, AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING, AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY, VE, .unit = "usage" }, + { "transcoding", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING }, 0, 0, VE, .unit = "usage" }, + { "lowlatency", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY }, 0, 0, VE, .unit = "usage" }, + + { "profile", "Set the profile (default main)", OFFSET(profile), AV_OPT_TYPE_INT,{.i64 = AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN }, AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN, AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN, VE, .unit = "profile" }, + { "main", "", 0, AV_OPT_TYPE_CONST,{.i64 = AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN }, 0, 0, VE, .unit = "profile" }, + + { "level", "Set the encoding level (default auto)", OFFSET(level), AV_OPT_TYPE_INT,{.i64 = 0 }, 0, AMF_VIDEO_ENCODER_AV1_LEVEL_7_3, VE, .unit = "level" }, + { "auto", "", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, VE, .unit = "level" }, + { "2.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_2_0 }, 0, 0, VE, .unit = "level" }, + { "2.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_2_1 }, 0, 0, VE, .unit = "level" }, + { "2.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_2_2 }, 0, 0, VE, .unit = "level" }, + { "2.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_2_3 }, 0, 0, VE, .unit = "level" }, + { "3.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_3_0 }, 0, 0, VE, .unit = "level" }, + { "3.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_3_1 }, 0, 0, VE, .unit = "level" }, + { "3.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_3_2 }, 0, 0, VE, .unit = "level" }, + { "3.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_3_3 }, 0, 0, VE, .unit = "level" }, + { "4.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_4_0 }, 0, 0, VE, .unit = "level" }, + { "4.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_4_1 }, 0, 0, VE, .unit = "level" }, + { "4.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_4_2 }, 0, 0, VE, .unit = "level" }, + { "4.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_4_3 }, 0, 0, VE, .unit = "level" }, + { "5.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_5_0 }, 0, 0, VE, .unit = "level" }, + { "5.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_5_1 }, 0, 0, VE, .unit = "level" }, + { "5.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_5_2 }, 0, 0, VE, .unit = "level" }, + { "5.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_5_3 }, 0, 0, VE, .unit = "level" }, + { "6.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_6_0 }, 0, 0, VE, .unit = "level" }, + { "6.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_6_1 }, 0, 0, VE, .unit = "level" }, + { "6.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_6_2 }, 0, 0, VE, .unit = "level" }, + { "6.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_6_3 }, 0, 0, VE, .unit = "level" }, + { "7.0", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_7_0 }, 0, 0, VE, .unit = "level" }, + { "7.1", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_7_1 }, 0, 0, VE, .unit = "level" }, + { "7.2", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_7_2 }, 0, 0, VE, .unit = "level" }, + { "7.3", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_LEVEL_7_3 }, 0, 0, VE, .unit = "level" }, + + { "quality", "Set the encoding quality", OFFSET(quality), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED }, AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_HIGH_QUALITY, AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED, VE, .unit = "quality" }, + { "balanced", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_BALANCED }, 0, 0, VE, .unit = "quality" }, + { "speed", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED }, 0, 0, VE, .unit = "quality" }, + { "quality", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_QUALITY }, 0, 0, VE, .unit = "quality" }, + { "high_quality", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_HIGH_QUALITY }, 0, 0, VE, .unit = "quality" }, + + { "rc", "Set the rate control mode", OFFSET(rate_control_mode), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_UNKNOWN }, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_UNKNOWN, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR, VE, .unit = "rc" }, + { "cqp", "Constant Quantization Parameter", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP }, 0, 0, VE, .unit = "rc" }, + { "vbr_latency", "Latency Constrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR }, 0, 0, VE, .unit = "rc" }, + { "vbr_peak", "Peak Contrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR }, 0, 0, VE, .unit = "rc" }, + { "cbr", "Constant Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR }, 0, 0, VE, .unit = "rc" }, + { "qvbr", "Quality Variable Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_QUALITY_VBR }, 0, 0, VE, .unit = "rc" }, + { "hqvbr", "High Quality Variable Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR }, 0, 0, VE, .unit = "rc" }, + { "hqcbr", "High Quality Constant Bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR }, 0, 0, VE, .unit = "rc" }, + + { "qvbr_quality_level", "Sets the QVBR quality level", OFFSET(qvbr_quality_level), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + + + { "header_insertion_mode", "Set header insertion mode", OFFSET(header_insertion_mode), AV_OPT_TYPE_INT,{.i64 = -1 }, -1, AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_KEY_FRAME_ALIGNED, VE, .unit = "hdrmode" }, + { "none", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_NONE }, 0, 0, VE, .unit = "hdrmode" }, + { "gop", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_GOP_ALIGNED }, 0, 0, VE, .unit = "hdrmode" }, + { "frame", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE_KEY_FRAME_ALIGNED }, 0, 0, VE, .unit = "hdrmode" }, + + { "preencode", "Enable preencode", OFFSET(preencode), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, VE}, { "enforce_hrd", "Enforce HRD", OFFSET(enforce_hrd), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, VE}, { "filler_data", "Filler Data Enable", OFFSET(filler_data), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, VE}, + { "high_motion_quality_boost_enable", "Enable High motion quality boost mode", OFFSET(hw_high_motion_quality_boost), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + // min_qp_i -> min_qp_intra, min_qp_p -> min_qp_inter { "min_qp_i", "min quantization parameter for I-frame", OFFSET(min_qp_i), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 255, VE }, { "max_qp_i", "max quantization parameter for I-frame", OFFSET(max_qp_i), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 255, VE }, @@ -89,11 +97,58 @@ static const AVOption options[] = { { "qp_i", "quantization parameter for I-frame", OFFSET(qp_i), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 255, VE }, { "skip_frame", "Rate Control Based Frame Skip", OFFSET(skip_frame), AV_OPT_TYPE_BOOL,{.i64 = 0 }, 0, 1, VE }, - { "align", "alignment mode", OFFSET(align), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS }, AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_ONLY, AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS, VE, "align" }, - { "64x16", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_ONLY }, 0, 0, VE, "align" }, - { "1080p", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_1080P_CODED_1082 }, 0, 0, VE, "align" }, - { "none", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS }, 0, 0, VE, "align" }, + { "align", "alignment mode", OFFSET(align), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS }, AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_ONLY, AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS, VE, .unit = "align" }, + { "64x16", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_ONLY }, 0, 0, VE, .unit = "align" }, + { "1080p", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_64X16_1080P_CODED_1082 }, 0, 0, VE, .unit = "align" }, + { "none", "", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE_NO_RESTRICTIONS }, 0, 0, VE, .unit = "align" }, + + { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg), AV_OPT_TYPE_BOOL,{.i64 = 0 }, 0, 1, VE }, + + //Pre Analysis options + { "preanalysis", "Enable preanalysis", OFFSET(preanalysis), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_activity_type", "Set the type of activity analysis", OFFSET(pa_activity_type), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_ACTIVITY_YUV, VE, .unit = "activity_type" }, + { "y", "activity y", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_ACTIVITY_Y }, 0, 0, VE, .unit = "activity_type" }, + { "yuv", "activity yuv", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_ACTIVITY_YUV }, 0, 0, VE, .unit = "activity_type" }, + + { "pa_scene_change_detection_enable", "Enable scene change detection", OFFSET(pa_scene_change_detection), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_scene_change_detection_sensitivity", "Set the sensitivity of scene change detection", OFFSET(pa_scene_change_detection_sensitivity), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_HIGH, VE, .unit = "scene_change_sensitivity" }, + { "low", "low scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_LOW }, 0, 0, VE, .unit = "scene_change_sensitivity" }, + { "medium", "medium scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_MEDIUM }, 0, 0, VE, .unit = "scene_change_sensitivity" }, + { "high", "high scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_HIGH }, 0, 0, VE, .unit = "scene_change_sensitivity" }, + + { "pa_static_scene_detection_enable", "Enable static scene detection", OFFSET(pa_static_scene_detection), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_static_scene_detection_sensitivity", "Set the sensitivity of static scene detection", OFFSET(pa_static_scene_detection_sensitivity), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_HIGH, VE , .unit = "static_scene_sensitivity" }, + { "low", "low static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_LOW }, 0, 0, VE, .unit = "static_scene_sensitivity" }, + { "medium", "medium static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_MEDIUM }, 0, 0, VE, .unit = "static_scene_sensitivity" }, + { "high", "high static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_HIGH }, 0, 0, VE, .unit = "static_scene_sensitivity" }, + + { "pa_initial_qp_after_scene_change", "The QP value that is used immediately after a scene change", OFFSET(pa_initial_qp), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + { "pa_max_qp_before_force_skip", "The QP threshold to allow a skip frame", OFFSET(pa_max_qp), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + + { "pa_caq_strength", "Content Adaptive Quantization strength", OFFSET(pa_caq_strength), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_CAQ_STRENGTH_HIGH, VE , .unit = "caq_strength" }, + { "low", "low Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_LOW }, 0, 0, VE, .unit = "caq_strength" }, + { "medium", "medium Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_MEDIUM }, 0, 0, VE, .unit = "caq_strength" }, + { "high", "high Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_HIGH }, 0, 0, VE, .unit = "caq_strength" }, + + { "pa_frame_sad_enable", "Enable Frame SAD algorithm", OFFSET(pa_frame_sad), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "pa_ltr_enable", "Enable long term reference frame management", OFFSET(pa_ltr), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "pa_lookahead_buffer_depth", "Sets the PA lookahead buffer size", OFFSET(pa_lookahead_buffer_depth), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, MAX_LOOKAHEAD_DEPTH, VE }, + { "pa_paq_mode", "Sets the perceptual adaptive quantization mode", OFFSET(pa_paq_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_PAQ_MODE_CAQ, VE , .unit = "paq_mode" }, + { "none", "no perceptual adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_NONE }, 0, 0, VE, .unit = "paq_mode" }, + { "caq", "caq perceptual adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_CAQ }, 0, 0, VE, .unit = "paq_mode" }, + + { "pa_taq_mode", "Sets the temporal adaptive quantization mode", OFFSET(pa_taq_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_TAQ_MODE_2, VE , .unit = "taq_mode" }, + { "none", "no temporal adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_NONE }, 0, 0, VE, .unit = "taq_mode" }, + { "1", "temporal adaptive quantization mode 1", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_1 }, 0, 0, VE, .unit = "taq_mode" }, + { "2", "temporal adaptive quantization mode 2", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_2 }, 0, 0, VE, .unit = "taq_mode" }, + + { "pa_high_motion_quality_boost_mode", "Sets the PA high motion quality boost mode", OFFSET(pa_high_motion_quality_boost_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_AUTO, VE , .unit = "high_motion_quality_boost_mode" }, + { "none", "no high motion quality boost", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_NONE }, 0, 0, VE, .unit = "high_motion_quality_boost_mode" }, + { "auto", "auto high motion quality boost", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_AUTO }, 0, 0, VE, .unit = "high_motion_quality_boost_mode" }, { NULL } }; @@ -117,7 +172,13 @@ static av_cold int amf_encode_init_av1(AVCodecContext* avctx) framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den); } else { - framerate = AMFConstructRate(avctx->time_base.den, avctx->time_base.num * avctx->ticks_per_frame); +FF_DISABLE_DEPRECATION_WARNINGS + framerate = AMFConstructRate(avctx->time_base.den, avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ); +FF_ENABLE_DEPRECATION_WARNINGS } if ((ret = ff_amf_encode_init(avctx)) < 0) @@ -131,7 +192,7 @@ static av_cold int amf_encode_init_av1(AVCodecContext* avctx) AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_FRAMERATE, framerate); switch (avctx->profile) { - case FF_PROFILE_AV1_MAIN: + case AV_PROFILE_AV1_MAIN: profile = AMF_VIDEO_ENCODER_AV1_PROFILE_MAIN; break; default: @@ -143,7 +204,7 @@ static av_cold int amf_encode_init_av1(AVCodecContext* avctx) AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_PROFILE, profile); profile_level = avctx->level; - if (profile_level == FF_LEVEL_UNKNOWN) { + if (profile_level == AV_LEVEL_UNKNOWN) { profile_level = ctx->level; } if (profile_level != 0) { @@ -159,7 +220,9 @@ static av_cold int amf_encode_init_av1(AVCodecContext* avctx) // Picture control properties AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_GOP_SIZE, avctx->gop_size); - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE, ctx->header_insertion_mode); + // Setup header insertion mode only if this option was defined explicitly + if (ctx->header_insertion_mode != -1) + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_HEADER_INSERTION_MODE, ctx->header_insertion_mode); // Rate control // autodetect rate control method @@ -180,6 +243,26 @@ static av_cold int amf_encode_init_av1(AVCodecContext* avctx) } } + // Pre-Pass, Pre-Analysis, Two-Pass + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_PREENCODE, 0); + if (ctx->preencode) + av_log(ctx, AV_LOG_WARNING, "Preencode is not supported by cqp Rate Control Method, automatically disabled\n"); + } + else { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_PREENCODE, ctx->preencode); + } + + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_QUALITY_VBR) { + if (ctx->qvbr_quality_level != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_QVBR_QUALITY_LEVEL, ctx->qvbr_quality_level); + } + } + + if (ctx->hw_high_motion_quality_boost != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_HIGH_MOTION_QUALITY_BOOST, ((ctx->hw_high_motion_quality_boost == 0) ? false : true)); + } + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD, ctx->rate_control_mode); if (avctx->rc_buffer_size) { AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_VBV_BUFFER_SIZE, avctx->rc_buffer_size); @@ -192,9 +275,6 @@ static av_cold int amf_encode_init_av1(AVCodecContext* avctx) } } - // Pre-Pass, Pre-Analysis, Two-Pass - AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_PRE_ANALYSIS_ENABLE, ctx->preanalysis); - // init dynamic rate control params if (ctx->max_au_size) ctx->enforce_hrd = 1; @@ -249,6 +329,56 @@ static av_cold int amf_encode_init_av1(AVCodecContext* avctx) } AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_ALIGNMENT_MODE, ctx->align); + if (ctx->preanalysis != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_PRE_ANALYSIS_ENABLE, !!((ctx->preanalysis == 0) ? false : true)); + } + + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_AV1_PRE_ANALYSIS_ENABLE, &var); + if ((int)var.int64Value) + { + if (ctx->pa_activity_type != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_ACTIVITY_TYPE, ctx->pa_activity_type); + } + if (ctx->pa_scene_change_detection != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_SCENE_CHANGE_DETECTION_ENABLE, ((ctx->pa_scene_change_detection == 0) ? false : true)); + } + if (ctx->pa_scene_change_detection_sensitivity != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY, ctx->pa_scene_change_detection_sensitivity); + } + if (ctx->pa_static_scene_detection != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_STATIC_SCENE_DETECTION_ENABLE, ((ctx->pa_static_scene_detection == 0) ? false : true)); + } + if (ctx->pa_static_scene_detection_sensitivity != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY, ctx->pa_static_scene_detection_sensitivity); + } + if (ctx->pa_initial_qp != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_INITIAL_QP_AFTER_SCENE_CHANGE, ctx->pa_initial_qp); + } + if (ctx->pa_max_qp != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_MAX_QP_BEFORE_FORCE_SKIP, ctx->pa_max_qp); + } + if (ctx->pa_caq_strength != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_CAQ_STRENGTH, ctx->pa_caq_strength); + } + if (ctx->pa_frame_sad != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_FRAME_SAD_ENABLE, ((ctx->pa_frame_sad == 0) ? false : true)); + } + if (ctx->pa_paq_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_PAQ_MODE, ctx->pa_paq_mode); + } + if (ctx->pa_taq_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_TAQ_MODE, ctx->pa_taq_mode); + } + if (ctx->pa_ltr != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_LTR_ENABLE, ((ctx->pa_ltr == 0) ? false : true)); + } + if (ctx->pa_lookahead_buffer_depth != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, ctx->pa_lookahead_buffer_depth); + } + if (ctx->pa_high_motion_quality_boost_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE, ctx->pa_high_motion_quality_boost_mode); + } + } // init encoder res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height); @@ -292,7 +422,6 @@ static av_cold int amf_encode_init_av1(AVCodecContext* avctx) } AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_SKIP_FRAME, ctx->skip_frame); - // fill extradata res = AMFVariantInit(&var); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "AMFVariantInit() failed with error %d\n", res); diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c index eaf7f974f3c..c1d5f4054ed 100644 --- a/libavcodec/amfenc_h264.c +++ b/libavcodec/amfenc_h264.c @@ -22,6 +22,7 @@ #include "amfenc.h" #include "codec_internal.h" #include "internal.h" +#include #define OFFSET(x) offsetof(AmfContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM @@ -29,56 +30,64 @@ static const AVOption options[] = { // Static /// Usage - { "usage", "Encoder Usage", OFFSET(usage), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_USAGE_TRANSCONDING }, AMF_VIDEO_ENCODER_USAGE_TRANSCONDING, AMF_VIDEO_ENCODER_USAGE_WEBCAM, VE, "usage" }, - { "transcoding", "Generic Transcoding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_TRANSCONDING }, 0, 0, VE, "usage" }, - { "ultralowlatency","", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY }, 0, 0, VE, "usage" }, - { "lowlatency", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY }, 0, 0, VE, "usage" }, - { "webcam", "Webcam", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_WEBCAM }, 0, 0, VE, "usage" }, + { "usage", "Encoder Usage", OFFSET(usage), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_USAGE_TRANSCODING }, AMF_VIDEO_ENCODER_USAGE_TRANSCODING, AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY_HIGH_QUALITY, VE, .unit = "usage" }, + { "transcoding", "Generic Transcoding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_TRANSCODING }, 0, 0, VE, .unit = "usage" }, + { "ultralowlatency","ultra low latency trancoding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY }, 0, 0, VE, .unit = "usage" }, + { "lowlatency", "low latency trancoding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY }, 0, 0, VE, .unit = "usage" }, + { "webcam", "Webcam", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_WEBCAM }, 0, 0, VE, .unit = "usage" }, + { "high_quality", "high quality trancoding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_HIGH_QUALITY }, 0, 0, VE, .unit = "usage" }, + { "lowlatency_high_quality", "low latency yet high quality trancoding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY_HIGH_QUALITY }, 0, 0, VE, .unit = "usage" }, + /// Profile, - { "profile", "Profile", OFFSET(profile),AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_PROFILE_MAIN }, AMF_VIDEO_ENCODER_PROFILE_BASELINE, AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_HIGH, VE, "profile" }, - { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_PROFILE_MAIN }, 0, 0, VE, "profile" }, - { "high", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_PROFILE_HIGH }, 0, 0, VE, "profile" }, - { "constrained_baseline", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_BASELINE }, 0, 0, VE, "profile" }, - { "constrained_high", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_HIGH }, 0, 0, VE, "profile" }, + { "profile", "Profile", OFFSET(profile),AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_PROFILE_MAIN }, AMF_VIDEO_ENCODER_PROFILE_BASELINE, AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_HIGH, VE, .unit = "profile" }, + { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_PROFILE_MAIN }, 0, 0, VE, .unit = "profile" }, + { "high", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_PROFILE_HIGH }, 0, 0, VE, .unit = "profile" }, + { "constrained_baseline", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_BASELINE }, 0, 0, VE, .unit = "profile" }, + { "constrained_high", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_HIGH }, 0, 0, VE, .unit = "profile" }, /// Profile Level - { "level", "Profile Level", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 62, VE, "level" }, - { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, VE, "level" }, - { "1.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 10 }, 0, 0, VE, "level" }, - { "1.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 11 }, 0, 0, VE, "level" }, - { "1.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 12 }, 0, 0, VE, "level" }, - { "1.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = 13 }, 0, 0, VE, "level" }, - { "2.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 20 }, 0, 0, VE, "level" }, - { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 21 }, 0, 0, VE, "level" }, - { "2.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 22 }, 0, 0, VE, "level" }, - { "3.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 30 }, 0, 0, VE, "level" }, - { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 31 }, 0, 0, VE, "level" }, - { "3.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 32 }, 0, 0, VE, "level" }, - { "4.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 40 }, 0, 0, VE, "level" }, - { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 41 }, 0, 0, VE, "level" }, - { "4.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 42 }, 0, 0, VE, "level" }, - { "5.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 50 }, 0, 0, VE, "level" }, - { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 51 }, 0, 0, VE, "level" }, - { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 52 }, 0, 0, VE, "level" }, - { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 60 }, 0, 0, VE, "level" }, - { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 61 }, 0, 0, VE, "level" }, - { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 62 }, 0, 0, VE, "level" }, + { "level", "Profile Level", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 62, VE, .unit = "level" }, + { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, VE, .unit = "level" }, + { "1.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 10 }, 0, 0, VE, .unit = "level" }, + { "1.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 11 }, 0, 0, VE, .unit = "level" }, + { "1.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 12 }, 0, 0, VE, .unit = "level" }, + { "1.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = 13 }, 0, 0, VE, .unit = "level" }, + { "2.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 20 }, 0, 0, VE, .unit = "level" }, + { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 21 }, 0, 0, VE, .unit = "level" }, + { "2.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 22 }, 0, 0, VE, .unit = "level" }, + { "3.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 30 }, 0, 0, VE, .unit = "level" }, + { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 31 }, 0, 0, VE, .unit = "level" }, + { "3.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 32 }, 0, 0, VE, .unit = "level" }, + { "4.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 40 }, 0, 0, VE, .unit = "level" }, + { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 41 }, 0, 0, VE, .unit = "level" }, + { "4.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 42 }, 0, 0, VE, .unit = "level" }, + { "5.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 50 }, 0, 0, VE, .unit = "level" }, + { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 51 }, 0, 0, VE, .unit = "level" }, + { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 52 }, 0, 0, VE, .unit = "level" }, + { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 60 }, 0, 0, VE, .unit = "level" }, + { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 61 }, 0, 0, VE, .unit = "level" }, + { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 62 }, 0, 0, VE, .unit = "level" }, /// Quality Preset - { "quality", "Quality Preference", OFFSET(quality), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_SPEED }, AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED, AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY, VE, "quality" }, - { "speed", "Prefer Speed", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_SPEED }, 0, 0, VE, "quality" }, - { "balanced", "Balanced", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED }, 0, 0, VE, "quality" }, - { "quality", "Prefer Quality", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY }, 0, 0, VE, "quality" }, + { "quality", "Quality Preference", OFFSET(quality), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_SPEED }, AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED, AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY, VE, .unit = "quality" }, + { "speed", "Prefer Speed", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_SPEED }, 0, 0, VE, .unit = "quality" }, + { "balanced", "Balanced", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED }, 0, 0, VE, .unit = "quality" }, + { "quality", "Prefer Quality", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY }, 0, 0, VE, .unit = "quality" }, // Dynamic /// Rate Control Method - { "rc", "Rate Control Method", OFFSET(rate_control_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN }, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR, VE, "rc" }, - { "cqp", "Constant Quantization Parameter", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP }, 0, 0, VE, "rc" }, - { "cbr", "Constant Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR }, 0, 0, VE, "rc" }, - { "vbr_peak", "Peak Contrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR }, 0, 0, VE, "rc" }, - { "vbr_latency", "Latency Constrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR }, 0, 0, VE, "rc" }, + { "rc", "Rate Control Method", OFFSET(rate_control_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN }, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_UNKNOWN, AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR, VE, .unit = "rc" }, + { "cqp", "Constant Quantization Parameter", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP }, 0, 0, VE, .unit = "rc" }, + { "cbr", "Constant Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR }, 0, 0, VE, .unit = "rc" }, + { "vbr_peak", "Peak Contrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR }, 0, 0, VE, .unit = "rc" }, + { "vbr_latency", "Latency Constrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR }, 0, 0, VE, .unit = "rc" }, + { "qvbr", "Quality Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_QUALITY_VBR }, 0, 0, VE, .unit = "rc" }, + { "hqvbr", "High Quality Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR }, 0, 0, VE, .unit = "rc" }, + { "hqcbr", "High Quality Constant Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR }, 0, 0, VE, .unit = "rc" }, + + { "qvbr_quality_level", "Sets the QVBR quality level", OFFSET(qvbr_quality_level),AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 51, VE }, /// Enforce HRD, Filler Data, VBAQ, Frame Skipping { "enforce_hrd", "Enforce HRD", OFFSET(enforce_hrd), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, @@ -92,7 +101,7 @@ static const AVOption options[] = { { "qp_b", "Quantization Parameter for B-Frame", OFFSET(qp_b), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 51, VE }, /// Pre-Pass, Pre-Analysis, Two-Pass - { "preanalysis", "Pre-Analysis Mode", OFFSET(preanalysis), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, NULL }, + { "preencode", "Pre-encode assisted rate control", OFFSET(preencode), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE, .unit = NULL }, /// Maximum Access Unit Size { "max_au_size", "Maximum Access Unit Size for rate control (in bits)", OFFSET(max_au_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, @@ -106,14 +115,19 @@ static const AVOption options[] = { { "bf_ref", "Enable Reference to B-Frames", OFFSET(b_frame_ref), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, { "bf_ref_delta_qp","Reference B-Picture Delta QP", OFFSET(ref_b_frame_delta_qp), AV_OPT_TYPE_INT, { .i64 = 4 }, -10, 10, VE }, + { "max_b_frames", "Maximum number of consecutive B Pictures", OFFSET(max_consecutive_b_frames), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 3, VE }, + { "bf", "B Picture Pattern", OFFSET(max_b_frames), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 3, VE }, + /// Intra-Refresh { "intra_refresh_mb","Intra Refresh MBs Number Per Slot in Macroblocks", OFFSET(intra_refresh_mb), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, /// coder - { "coder", "Coding Type", OFFSET(coding_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_UNDEFINED }, AMF_VIDEO_ENCODER_UNDEFINED, AMF_VIDEO_ENCODER_CALV, VE, "coder" }, - { "auto", "Automatic", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_UNDEFINED }, 0, 0, VE, "coder" }, - { "cavlc", "Context Adaptive Variable-Length Coding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_CALV }, 0, 0, VE, "coder" }, - { "cabac", "Context Adaptive Binary Arithmetic Coding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_CABAC }, 0, 0, VE, "coder" }, + { "coder", "Coding Type", OFFSET(coding_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_UNDEFINED }, AMF_VIDEO_ENCODER_UNDEFINED, AMF_VIDEO_ENCODER_CALV, VE, .unit = "coder" }, + { "auto", "Automatic", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_UNDEFINED }, 0, 0, VE, .unit = "coder" }, + { "cavlc", "Context Adaptive Variable-Length Coding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_CALV }, 0, 0, VE, .unit = "coder" }, + { "cabac", "Context Adaptive Binary Arithmetic Coding", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_CABAC }, 0, 0, VE, .unit = "coder" }, + + { "high_motion_quality_boost_enable", "Enable High motion quality boost mode", OFFSET(hw_high_motion_quality_boost), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, { "me_half_pel", "Enable ME Half Pixel", OFFSET(me_half_pel), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, { "me_quarter_pel", "Enable ME Quarter Pixel", OFFSET(me_quarter_pel),AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, @@ -122,6 +136,53 @@ static const AVOption options[] = { { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg) , AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, + //Pre Analysis options + { "preanalysis", "Enable preanalysis", OFFSET(preanalysis), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_activity_type", "Set the type of activity analysis", OFFSET(pa_activity_type), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_ACTIVITY_YUV, VE, .unit = "activity_type" }, + { "y", "activity y", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_ACTIVITY_Y }, 0, 0, VE, .unit = "activity_type" }, + { "yuv", "activity yuv", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_ACTIVITY_YUV }, 0, 0, VE, .unit = "activity_type" }, + + { "pa_scene_change_detection_enable", "Enable scene change detection", OFFSET(pa_scene_change_detection), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_scene_change_detection_sensitivity", "Set the sensitivity of scene change detection", OFFSET(pa_scene_change_detection_sensitivity), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_HIGH, VE, .unit = "scene_change_sensitivity" }, + { "low", "low scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_LOW }, 0, 0, VE, .unit = "scene_change_sensitivity" }, + { "medium", "medium scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_MEDIUM }, 0, 0, VE, .unit = "scene_change_sensitivity" }, + { "high", "high scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_HIGH }, 0, 0, VE, .unit = "scene_change_sensitivity" }, + + { "pa_static_scene_detection_enable", "Enable static scene detection", OFFSET(pa_static_scene_detection), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_static_scene_detection_sensitivity", "Set the sensitivity of static scene detection", OFFSET(pa_static_scene_detection_sensitivity), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_HIGH, VE , .unit = "static_scene_sensitivity" }, + { "low", "low static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_LOW }, 0, 0, VE, .unit = "static_scene_sensitivity" }, + { "medium", "medium static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_MEDIUM }, 0, 0, VE, .unit = "static_scene_sensitivity" }, + { "high", "high static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_HIGH }, 0, 0, VE, .unit = "static_scene_sensitivity" }, + + { "pa_initial_qp_after_scene_change", "The QP value that is used immediately after a scene change", OFFSET(pa_initial_qp), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + { "pa_max_qp_before_force_skip", "The QP threshold to allow a skip frame", OFFSET(pa_max_qp), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + + { "pa_caq_strength", "Content Adaptive Quantization strength", OFFSET(pa_caq_strength), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_CAQ_STRENGTH_HIGH, VE , .unit = "caq_strength" }, + { "low", "low Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_LOW }, 0, 0, VE, .unit = "caq_strength" }, + { "medium", "medium Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_MEDIUM }, 0, 0, VE, .unit = "caq_strength" }, + { "high", "high Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_HIGH }, 0, 0, VE, .unit = "caq_strength" }, + + { "pa_frame_sad_enable", "Enable Frame SAD algorithm", OFFSET(pa_frame_sad), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "pa_ltr_enable", "Enable long term reference frame management", OFFSET(pa_ltr), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "pa_lookahead_buffer_depth", "Sets the PA lookahead buffer size", OFFSET(pa_lookahead_buffer_depth), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, MAX_LOOKAHEAD_DEPTH, VE }, + + { "pa_paq_mode", "Sets the perceptual adaptive quantization mode", OFFSET(pa_paq_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_PAQ_MODE_CAQ, VE , .unit = "paq_mode" }, + { "none", "no perceptual adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_NONE }, 0, 0, VE, .unit = "paq_mode" }, + { "caq", "caq perceptual adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_CAQ }, 0, 0, VE, .unit = "paq_mode" }, + + { "pa_taq_mode", "Sets the temporal adaptive quantization mode", OFFSET(pa_taq_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_TAQ_MODE_2, VE , .unit = "taq_mode" }, + { "none", "no temporal adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_NONE }, 0, 0, VE, .unit = "taq_mode" }, + { "1", "temporal adaptive quantization mode 1", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_1 }, 0, 0, VE, .unit = "taq_mode" }, + { "2", "temporal adaptive quantization mode 2", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_2 }, 0, 0, VE, .unit = "taq_mode" }, + + { "pa_high_motion_quality_boost_mode", "Sets the PA high motion quality boost mode", OFFSET(pa_high_motion_quality_boost_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_AUTO, VE , .unit = "high_motion_quality_boost_mode" }, + { "none", "no high motion quality boost", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_NONE }, 0, 0, VE, .unit = "high_motion_quality_boost_mode" }, + { "auto", "auto high motion quality boost", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_AUTO }, 0, 0, VE, .unit = "high_motion_quality_boost_mode" }, + + { "pa_adaptive_mini_gop", "Enable Adaptive MiniGOP", OFFSET(pa_adaptive_mini_gop), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE }, { NULL } }; @@ -142,7 +203,13 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den); } else { - framerate = AMFConstructRate(avctx->time_base.den, avctx->time_base.num * avctx->ticks_per_frame); +FF_DISABLE_DEPRECATION_WARNINGS + framerate = AMFConstructRate(avctx->time_base.den, avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ); +FF_ENABLE_DEPRECATION_WARNINGS } if ((ret = ff_amf_encode_init(avctx)) != 0) @@ -156,19 +223,19 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_FRAMERATE, framerate); switch (avctx->profile) { - case FF_PROFILE_H264_BASELINE: + case AV_PROFILE_H264_BASELINE: profile = AMF_VIDEO_ENCODER_PROFILE_BASELINE; break; - case FF_PROFILE_H264_MAIN: + case AV_PROFILE_H264_MAIN: profile = AMF_VIDEO_ENCODER_PROFILE_MAIN; break; - case FF_PROFILE_H264_HIGH: + case AV_PROFILE_H264_HIGH: profile = AMF_VIDEO_ENCODER_PROFILE_HIGH; break; - case FF_PROFILE_H264_CONSTRAINED_BASELINE: + case AV_PROFILE_H264_CONSTRAINED_BASELINE: profile = AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_BASELINE; break; - case (FF_PROFILE_H264_HIGH | FF_PROFILE_H264_CONSTRAINED): + case (AV_PROFILE_H264_HIGH | AV_PROFILE_H264_CONSTRAINED): profile = AMF_VIDEO_ENCODER_PROFILE_CONSTRAINED_HIGH; break; } @@ -179,7 +246,7 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_PROFILE, profile); profile_level = avctx->level; - if (profile_level == FF_LEVEL_UNKNOWN) { + if (profile_level == AV_LEVEL_UNKNOWN) { profile_level = ctx->level; } if (profile_level != 0) { @@ -215,11 +282,21 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) } if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP) { - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_RATE_CONTROL_PREANALYSIS_ENABLE, AMF_VIDEO_ENCODER_PREENCODE_DISABLED); - if (ctx->preanalysis) - av_log(ctx, AV_LOG_WARNING, "Pre-Analysis is not supported by cqp Rate Control Method, automatically disabled\n"); + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_PREENCODE_ENABLE, AMF_VIDEO_ENCODER_PREENCODE_DISABLED); + if (ctx->preencode) + av_log(ctx, AV_LOG_WARNING, "Preencode is not supported by cqp Rate Control Method, automatically disabled\n"); } else { - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_RATE_CONTROL_PREANALYSIS_ENABLE, ctx->preanalysis); + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_PREENCODE_ENABLE, ctx->preencode); + } + + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_QUALITY_VBR) { + if (ctx->qvbr_quality_level != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QVBR_QUALITY_LEVEL, ctx->qvbr_quality_level); + } + } + + if (ctx->hw_high_motion_quality_boost != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HIGH_MOTION_QUALITY_BOOST_ENABLE, ((ctx->hw_high_motion_quality_boost == 0) ? false : true)); } AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_QUALITY_PRESET, ctx->quality); @@ -276,6 +353,88 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) av_log(ctx, AV_LOG_WARNING, "rate control mode is PEAK_CONSTRAINED_VBR but rc_max_rate is not set\n"); } + if (ctx->preanalysis != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_PRE_ANALYSIS_ENABLE, !!((ctx->preanalysis == 0) ? false : true)); + } + + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_PRE_ANALYSIS_ENABLE, &var); + if ((int)var.int64Value) + { + if (ctx->pa_activity_type != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_ACTIVITY_TYPE, ctx->pa_activity_type); + } + if (ctx->pa_scene_change_detection != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_SCENE_CHANGE_DETECTION_ENABLE, ((ctx->pa_scene_change_detection == 0) ? false : true)); + } + if (ctx->pa_scene_change_detection_sensitivity != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY, ctx->pa_scene_change_detection_sensitivity); + } + if (ctx->pa_static_scene_detection != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_STATIC_SCENE_DETECTION_ENABLE, ((ctx->pa_static_scene_detection == 0) ? false : true)); + } + if (ctx->pa_static_scene_detection_sensitivity != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY, ctx->pa_static_scene_detection_sensitivity); + } + if (ctx->pa_initial_qp != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_INITIAL_QP_AFTER_SCENE_CHANGE, ctx->pa_initial_qp); + } + if (ctx->pa_max_qp != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_MAX_QP_BEFORE_FORCE_SKIP, ctx->pa_max_qp); + } + if (ctx->pa_caq_strength != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_CAQ_STRENGTH, ctx->pa_caq_strength); + } + if (ctx->pa_frame_sad != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_FRAME_SAD_ENABLE, ((ctx->pa_frame_sad == 0) ? false : true)); + } + if (ctx->pa_paq_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_PAQ_MODE, ctx->pa_paq_mode); + } + if (ctx->pa_taq_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_TAQ_MODE, ctx->pa_taq_mode); + } + if (ctx->pa_adaptive_mini_gop != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_ADAPTIVE_MINIGOP, ((ctx->pa_adaptive_mini_gop == 0) ? false : true)); + } + if (ctx->pa_ltr != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_LTR_ENABLE, ((ctx->pa_ltr == 0) ? false : true)); + } + if (ctx->pa_lookahead_buffer_depth != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, ctx->pa_lookahead_buffer_depth); + } + if (ctx->pa_high_motion_quality_boost_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE, ctx->pa_high_motion_quality_boost_mode); + } + } + + // B-Frames + if (ctx->max_consecutive_b_frames != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_MAX_CONSECUTIVE_BPICTURES, ctx->max_consecutive_b_frames); + if (ctx->max_b_frames != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_PATTERN, ctx->max_b_frames); + if (res != AMF_OK) { + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_PATTERN, &var); + av_log(ctx, AV_LOG_WARNING, "B-frames=%d is not supported by this GPU, switched to %d\n", + ctx->max_b_frames, (int)var.int64Value); + ctx->max_b_frames = (int)var.int64Value; + } + if (ctx->max_consecutive_b_frames < ctx->max_b_frames) { + av_log(ctx, AVERROR_BUG, "Maxium B frames needs to be greater than the specified B frame count.\n"); + } + } + } + else { + if (ctx->max_b_frames != -1) { + av_log(ctx, AVERROR_BUG, "Maxium number of B frames needs to be specified.\n"); + } + } + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_PATTERN, &var); + if ((int)var.int64Value) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_DELTA_QP, ctx->b_frame_delta_qp); + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_B_REFERENCE_ENABLE, !!ctx->b_frame_ref); + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_REF_B_PIC_DELTA_QP, ctx->ref_b_frame_delta_qp); + } + // Initialize Encoder res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "encoder->Init() failed with error %d\n", res); @@ -293,20 +452,6 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx) } AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_DE_BLOCKING_FILTER, !!deblocking_filter); - // B-Frames - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_PATTERN, avctx->max_b_frames); - if (res != AMF_OK) { - res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_PATTERN, &var); - av_log(ctx, AV_LOG_WARNING, "B-frames=%d is not supported by this GPU, switched to %d\n", - avctx->max_b_frames, (int)var.int64Value); - avctx->max_b_frames = (int)var.int64Value; - } - if (avctx->max_b_frames) { - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_B_PIC_DELTA_QP, ctx->b_frame_delta_qp); - AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_B_REFERENCE_ENABLE, !!ctx->b_frame_ref); - AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_REF_B_PIC_DELTA_QP, ctx->ref_b_frame_delta_qp); - } - // Keyframe Interval AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_IDR_PERIOD, avctx->gop_size); diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c index 9b46946f1ec..33a167aa520 100644 --- a/libavcodec/amfenc_hevc.c +++ b/libavcodec/amfenc_hevc.c @@ -21,57 +21,66 @@ #include "amfenc.h" #include "codec_internal.h" #include "internal.h" +#include #define OFFSET(x) offsetof(AmfContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "usage", "Set the encoding usage", OFFSET(usage), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING }, AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING, AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM, VE, "usage" }, - { "transcoding", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING }, 0, 0, VE, "usage" }, - { "ultralowlatency","", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY }, 0, 0, VE, "usage" }, - { "lowlatency", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY }, 0, 0, VE, "usage" }, - { "webcam", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM }, 0, 0, VE, "usage" }, - - { "profile", "Set the profile (default main)", OFFSET(profile), AV_OPT_TYPE_INT,{ .i64 = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN }, AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN, AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN, VE, "profile" }, - { "main", "", 0, AV_OPT_TYPE_CONST,{ .i64 = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN }, 0, 0, VE, "profile" }, - - { "profile_tier", "Set the profile tier (default main)", OFFSET(tier), AV_OPT_TYPE_INT,{ .i64 = AMF_VIDEO_ENCODER_HEVC_TIER_MAIN }, AMF_VIDEO_ENCODER_HEVC_TIER_MAIN, AMF_VIDEO_ENCODER_HEVC_TIER_HIGH, VE, "tier" }, - { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_TIER_MAIN }, 0, 0, VE, "tier" }, - { "high", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_TIER_HIGH }, 0, 0, VE, "tier" }, - - { "level", "Set the encoding level (default auto)", OFFSET(level), AV_OPT_TYPE_INT,{ .i64 = 0 }, 0, AMF_LEVEL_6_2, VE, "level" }, - { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, VE, "level" }, - { "1.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_1 }, 0, 0, VE, "level" }, - { "2.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_2 }, 0, 0, VE, "level" }, - { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_2_1 }, 0, 0, VE, "level" }, - { "3.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_3 }, 0, 0, VE, "level" }, - { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_3_1 }, 0, 0, VE, "level" }, - { "4.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_4 }, 0, 0, VE, "level" }, - { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_4_1 }, 0, 0, VE, "level" }, - { "5.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_5 }, 0, 0, VE, "level" }, - { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_5_1 }, 0, 0, VE, "level" }, - { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_5_2 }, 0, 0, VE, "level" }, - { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_6 }, 0, 0, VE, "level" }, - { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_6_1 }, 0, 0, VE, "level" }, - { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_6_2 }, 0, 0, VE, "level" }, - - { "quality", "Set the encoding quality", OFFSET(quality), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED }, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED, VE, "quality" }, - { "balanced", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_BALANCED }, 0, 0, VE, "quality" }, - { "speed", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED }, 0, 0, VE, "quality" }, - { "quality", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY }, 0, 0, VE, "quality" }, - - { "rc", "Set the rate control mode", OFFSET(rate_control_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN }, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR, VE, "rc" }, - { "cqp", "Constant Quantization Parameter", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP }, 0, 0, VE, "rc" }, - { "cbr", "Constant Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR }, 0, 0, VE, "rc" }, - { "vbr_peak", "Peak Contrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR }, 0, 0, VE, "rc" }, - { "vbr_latency", "Latency Constrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR }, 0, 0, VE, "rc" }, - - { "header_insertion_mode", "Set header insertion mode", OFFSET(header_insertion_mode), AV_OPT_TYPE_INT,{ .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_NONE }, AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_NONE, AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_IDR_ALIGNED, VE, "hdrmode" }, - { "none", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_NONE }, 0, 0, VE, "hdrmode" }, - { "gop", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_GOP_ALIGNED }, 0, 0, VE, "hdrmode" }, - { "idr", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_IDR_ALIGNED }, 0, 0, VE, "hdrmode" }, - - { "gops_per_idr", "GOPs per IDR 0-no IDR will be inserted", OFFSET(gops_per_idr), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, VE }, - { "preanalysis", "Enable preanalysis", OFFSET(preanalysis), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE}, + { "usage", "Set the encoding usage", OFFSET(usage), AV_OPT_TYPE_INT, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCODING }, AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCODING, AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY_HIGH_QUALITY, VE, .unit = "usage" }, + { "transcoding", "Generic Transcoding", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCODING }, 0, 0, VE, .unit = "usage" }, + { "ultralowlatency", "ultra low latency trancoding", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY }, 0, 0, VE, .unit = "usage" }, + { "lowlatency", "low latency trancoding", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY }, 0, 0, VE, .unit = "usage" }, + { "webcam", "Webcam", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM }, 0, 0, VE, .unit = "usage" }, + { "high_quality", "high quality trancoding", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_HIGH_QUALITY }, 0, 0, VE, .unit = "usage" }, + { "lowlatency_high_quality","low latency yet high quality trancoding", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY_HIGH_QUALITY }, 0, 0, VE, .unit = "usage" }, + + { "profile", "Set the profile (default main)", OFFSET(profile), AV_OPT_TYPE_INT,{ .i64 = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN }, AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN, AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN, VE, .unit = "profile" }, + { "main", "", 0, AV_OPT_TYPE_CONST,{ .i64 = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN }, 0, 0, VE, .unit = "profile" }, + + { "profile_tier", "Set the profile tier (default main)", OFFSET(tier), AV_OPT_TYPE_INT,{ .i64 = AMF_VIDEO_ENCODER_HEVC_TIER_MAIN }, AMF_VIDEO_ENCODER_HEVC_TIER_MAIN, AMF_VIDEO_ENCODER_HEVC_TIER_HIGH, VE, .unit = "tier" }, + { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_TIER_MAIN }, 0, 0, VE, .unit = "tier" }, + { "high", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_TIER_HIGH }, 0, 0, VE, .unit = "tier" }, + + { "level", "Set the encoding level (default auto)", OFFSET(level), AV_OPT_TYPE_INT,{ .i64 = 0 }, 0, AMF_LEVEL_6_2, VE, .unit = "level" }, + { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, VE, .unit = "level" }, + { "1.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_1 }, 0, 0, VE, .unit = "level" }, + { "2.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_2 }, 0, 0, VE, .unit = "level" }, + { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_2_1 }, 0, 0, VE, .unit = "level" }, + { "3.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_3 }, 0, 0, VE, .unit = "level" }, + { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_3_1 }, 0, 0, VE, .unit = "level" }, + { "4.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_4 }, 0, 0, VE, .unit = "level" }, + { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_4_1 }, 0, 0, VE, .unit = "level" }, + { "5.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_5 }, 0, 0, VE, .unit = "level" }, + { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_5_1 }, 0, 0, VE, .unit = "level" }, + { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_5_2 }, 0, 0, VE, .unit = "level" }, + { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_6 }, 0, 0, VE, .unit = "level" }, + { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_6_1 }, 0, 0, VE, .unit = "level" }, + { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_LEVEL_6_2 }, 0, 0, VE, .unit = "level" }, + + { "quality", "Set the encoding quality", OFFSET(quality), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED }, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY, AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED, VE, .unit = "quality" }, + { "balanced", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_BALANCED }, 0, 0, VE, .unit = "quality" }, + { "speed", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED }, 0, 0, VE, .unit = "quality" }, + { "quality", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY }, 0, 0, VE, .unit = "quality" }, + + { "rc", "Set the rate control mode", OFFSET(rate_control_mode), AV_OPT_TYPE_INT, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN }, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_UNKNOWN, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR, VE, .unit = "rc" }, + { "cqp", "Constant Quantization Parameter", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP }, 0, 0, VE, .unit = "rc" }, + { "cbr", "Constant Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR }, 0, 0, VE, .unit = "rc" }, + { "vbr_peak", "Peak Contrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR }, 0, 0, VE, .unit = "rc" }, + { "vbr_latency", "Latency Constrained Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR }, 0, 0, VE, .unit = "rc" }, + { "qvbr", "Quality Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_QUALITY_VBR }, 0, 0, VE, .unit = "rc" }, + { "hqvbr", "High Quality Variable Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR }, 0, 0, VE, .unit = "rc" }, + { "hqcbr", "High Quality Constant Bitrate", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR }, 0, 0, VE, .unit = "rc" }, + + { "qvbr_quality_level", "Sets the QVBR quality level", OFFSET(qvbr_quality_level), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + + { "header_insertion_mode", "Set header insertion mode", OFFSET(header_insertion_mode), AV_OPT_TYPE_INT,{ .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_NONE }, AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_NONE, AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_IDR_ALIGNED, VE, .unit = "hdrmode" }, + { "none", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_NONE }, 0, 0, VE, .unit = "hdrmode" }, + { "gop", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_GOP_ALIGNED }, 0, 0, VE, .unit = "hdrmode" }, + { "idr", "", 0, AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_ENCODER_HEVC_HEADER_INSERTION_MODE_IDR_ALIGNED }, 0, 0, VE, .unit = "hdrmode" }, + + { "high_motion_quality_boost_enable", "Enable High motion quality boost mode", OFFSET(hw_high_motion_quality_boost), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "gops_per_idr", "GOPs per IDR 0-no IDR will be inserted", OFFSET(gops_per_idr), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, VE }, + { "preencode", "Enable preencode", OFFSET(preencode), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE}, { "vbaq", "Enable VBAQ", OFFSET(enable_vbaq), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE}, { "enforce_hrd", "Enforce HRD", OFFSET(enforce_hrd), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE}, { "filler_data", "Filler Data Enable", OFFSET(filler_data), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE}, @@ -89,6 +98,52 @@ static const AVOption options[] = { { "aud", "Inserts AU Delimiter NAL unit", OFFSET(aud) ,AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE }, { "log_to_dbg", "Enable AMF logging to debug output", OFFSET(log_to_dbg), AV_OPT_TYPE_BOOL,{ .i64 = 0 }, 0, 1, VE }, + + //Pre Analysis options + { "preanalysis", "Enable preanalysis", OFFSET(preanalysis), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_activity_type", "Set the type of activity analysis", OFFSET(pa_activity_type), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_ACTIVITY_YUV, VE, .unit = "activity_type" }, + { "y", "activity y", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_ACTIVITY_Y }, 0, 0, VE, .unit = "activity_type" }, + { "yuv", "activity yuv", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_ACTIVITY_YUV }, 0, 0, VE, .unit = "activity_type" }, + + { "pa_scene_change_detection_enable", "Enable scene change detection", OFFSET(pa_scene_change_detection), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_scene_change_detection_sensitivity", "Set the sensitivity of scene change detection", OFFSET(pa_scene_change_detection_sensitivity), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_HIGH, VE, .unit = "scene_change_sensitivity" }, + { "low", "low scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_LOW }, 0, 0, VE, .unit = "scene_change_sensitivity" }, + { "medium", "medium scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_MEDIUM }, 0, 0, VE, .unit = "scene_change_sensitivity" }, + { "high", "high scene change dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY_HIGH }, 0, 0, VE, .unit = "scene_change_sensitivity" }, + + { "pa_static_scene_detection_enable", "Enable static scene detection", OFFSET(pa_static_scene_detection), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + + { "pa_static_scene_detection_sensitivity", "Set the sensitivity of static scene detection", OFFSET(pa_static_scene_detection_sensitivity), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_HIGH, VE , .unit = "static_scene_sensitivity" }, + { "low", "low static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_LOW }, 0, 0, VE, .unit = "static_scene_sensitivity" }, + { "medium", "medium static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_MEDIUM }, 0, 0, VE, .unit = "static_scene_sensitivity" }, + { "high", "high static scene dectection sensitivity", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY_HIGH }, 0, 0, VE, .unit = "static_scene_sensitivity" }, + + { "pa_initial_qp_after_scene_change", "The QP value that is used immediately after a scene change", OFFSET(pa_initial_qp), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + { "pa_max_qp_before_force_skip", "The QP threshold to allow a skip frame", OFFSET(pa_max_qp), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 51, VE }, + + { "pa_caq_strength", "Content Adaptive Quantization strength", OFFSET(pa_caq_strength), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_CAQ_STRENGTH_HIGH, VE , .unit = "caq_strength" }, + { "low", "low Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_LOW }, 0, 0, VE, .unit = "caq_strength" }, + { "medium", "medium Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_MEDIUM }, 0, 0, VE, .unit = "caq_strength" }, + { "high", "high Content Adaptive Quantization strength", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_CAQ_STRENGTH_HIGH }, 0, 0, VE, .unit = "caq_strength" }, + + { "pa_frame_sad_enable", "Enable Frame SAD algorithm", OFFSET(pa_frame_sad), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "pa_ltr_enable", "Enable long term reference frame management", OFFSET(pa_ltr), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE }, + { "pa_lookahead_buffer_depth", "Sets the PA lookahead buffer size", OFFSET(pa_lookahead_buffer_depth), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, MAX_LOOKAHEAD_DEPTH, VE }, + + { "pa_paq_mode", "Sets the perceptual adaptive quantization mode", OFFSET(pa_paq_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_PAQ_MODE_CAQ, VE , .unit = "paq_mode" }, + { "none", "no perceptual adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_NONE }, 0, 0, VE, .unit = "paq_mode" }, + { "caq", "caq perceptual adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_PAQ_MODE_CAQ }, 0, 0, VE, .unit = "paq_mode" }, + + { "pa_taq_mode", "Sets the temporal adaptive quantization mode", OFFSET(pa_taq_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_TAQ_MODE_2, VE , .unit = "taq_mode" }, + { "none", "no temporal adaptive quantization", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_NONE }, 0, 0, VE, .unit = "taq_mode" }, + { "1", "temporal adaptive quantization mode 1", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_1 }, 0, 0, VE, .unit = "taq_mode" }, + { "2", "temporal adaptive quantization mode 2", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_TAQ_MODE_2 }, 0, 0, VE, .unit = "taq_mode" }, + + { "pa_high_motion_quality_boost_mode", "Sets the PA high motion quality boost mode", OFFSET(pa_high_motion_quality_boost_mode), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_AUTO, VE , .unit = "high_motion_quality_boost_mode" }, + { "none", "no high motion quality boost", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_NONE }, 0, 0, VE, .unit = "high_motion_quality_boost_mode" }, + { "auto", "auto high motion quality boost", 0, AV_OPT_TYPE_CONST, {.i64 = AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE_AUTO }, 0, 0, VE, .unit = "high_motion_quality_boost_mode" }, { NULL } }; @@ -109,7 +164,13 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { framerate = AMFConstructRate(avctx->framerate.num, avctx->framerate.den); } else { - framerate = AMFConstructRate(avctx->time_base.den, avctx->time_base.num * avctx->ticks_per_frame); +FF_DISABLE_DEPRECATION_WARNINGS + framerate = AMFConstructRate(avctx->time_base.den, avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ); +FF_ENABLE_DEPRECATION_WARNINGS } if ((ret = ff_amf_encode_init(avctx)) < 0) @@ -123,7 +184,7 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) AMF_ASSIGN_PROPERTY_RATE(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_FRAMERATE, framerate); switch (avctx->profile) { - case FF_PROFILE_HEVC_MAIN: + case AV_PROFILE_HEVC_MAIN: profile = AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN; break; default: @@ -137,7 +198,7 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_TIER, ctx->tier); profile_level = avctx->level; - if (profile_level == FF_LEVEL_UNKNOWN) { + if (profile_level == AV_LEVEL_UNKNOWN) { profile_level = ctx->level; } if (profile_level != 0) { @@ -180,6 +241,25 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) } } + // Pre-Pass, Pre-Analysis, Two-Pass + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_PREENCODE_ENABLE, 0); + if (ctx->preencode) + av_log(ctx, AV_LOG_WARNING, "Preencode is not supported by cqp Rate Control Method, automatically disabled\n"); + } + else { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_PREENCODE_ENABLE, ctx->preencode); + } + + if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_QUALITY_VBR) { + if (ctx->qvbr_quality_level != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_QVBR_QUALITY_LEVEL, ctx->qvbr_quality_level); + } + } + + if (ctx->hw_high_motion_quality_boost != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_HIGH_MOTION_QUALITY_BOOST_ENABLE, ((ctx->hw_high_motion_quality_boost == 0) ? false : true)); + } AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD, ctx->rate_control_mode); if (avctx->rc_buffer_size) { @@ -192,8 +272,6 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_INITIAL_VBV_BUFFER_FULLNESS, amf_buffer_fullness); } } - // Pre-Pass, Pre-Analysis, Two-Pass - AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_PREANALYSIS_ENABLE, ctx->preanalysis); if (ctx->rate_control_mode == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP) { AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_ENABLE_VBAQ, false); @@ -222,6 +300,57 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) av_log(ctx, AV_LOG_WARNING, "rate control mode is PEAK_CONSTRAINED_VBR but rc_max_rate is not set\n"); } + if (ctx->preanalysis != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_PRE_ANALYSIS_ENABLE, !!((ctx->preanalysis == 0) ? false : true)); + } + + res = ctx->encoder->pVtbl->GetProperty(ctx->encoder, AMF_VIDEO_ENCODER_HEVC_PRE_ANALYSIS_ENABLE, &var); + if ((int)var.int64Value) + { + if (ctx->pa_activity_type != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_ACTIVITY_TYPE, ctx->pa_activity_type); + } + if (ctx->pa_scene_change_detection != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_SCENE_CHANGE_DETECTION_ENABLE, ((ctx->pa_scene_change_detection == 0) ? false : true)); + } + if (ctx->pa_scene_change_detection_sensitivity != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_SCENE_CHANGE_DETECTION_SENSITIVITY, ctx->pa_scene_change_detection_sensitivity); + } + if (ctx->pa_static_scene_detection != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_STATIC_SCENE_DETECTION_ENABLE, ((ctx->pa_static_scene_detection == 0) ? false : true)); + } + if (ctx->pa_static_scene_detection_sensitivity != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_STATIC_SCENE_DETECTION_SENSITIVITY, ctx->pa_static_scene_detection_sensitivity); + } + if (ctx->pa_initial_qp != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_INITIAL_QP_AFTER_SCENE_CHANGE, ctx->pa_initial_qp); + } + if (ctx->pa_max_qp != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_MAX_QP_BEFORE_FORCE_SKIP, ctx->pa_max_qp); + } + if (ctx->pa_caq_strength != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_CAQ_STRENGTH, ctx->pa_caq_strength); + } + if (ctx->pa_frame_sad != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_FRAME_SAD_ENABLE, ((ctx->pa_frame_sad == 0) ? false : true)); + } + if (ctx->pa_paq_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_PAQ_MODE, ctx->pa_paq_mode); + } + if (ctx->pa_taq_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_TAQ_MODE, ctx->pa_taq_mode); + } + if (ctx->pa_ltr != -1) { + AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_PA_LTR_ENABLE, ((ctx->pa_ltr == 0) ? false : true)); + } + if (ctx->pa_lookahead_buffer_depth != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_LOOKAHEAD_BUFFER_DEPTH, ctx->pa_lookahead_buffer_depth); + } + if (ctx->pa_high_motion_quality_boost_mode != -1) { + AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_PA_HIGH_MOTION_QUALITY_BOOST_MODE, ctx->pa_high_motion_quality_boost_mode); + } + } + // init encoder res = ctx->encoder->pVtbl->Init(ctx->encoder, ctx->format, avctx->width, avctx->height); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "encoder->Init() failed with error %d\n", res); @@ -262,7 +391,6 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx) } AMF_ASSIGN_PROPERTY_BOOL(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_SKIP_FRAME_ENABLE, ctx->skip_frame); - // fill extradata res = AMFVariantInit(&var); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_BUG, "AMFVariantInit() failed with error %d\n", res); diff --git a/libavcodec/amrwbdec.c b/libavcodec/amrwbdec.c index 9d75b972fa7..21a730b835d 100644 --- a/libavcodec/amrwbdec.c +++ b/libavcodec/amrwbdec.c @@ -26,6 +26,7 @@ #include "config.h" +#include "libavutil/avassert.h" #include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/lfg.h" @@ -554,6 +555,8 @@ static void decode_fixed_vector(float *fixed_vector, const uint16_t *pulse_hi, decode_6p_track(sig_pos[i], (int) pulse_lo[i] + ((int) pulse_hi[i] << 11), 4, 1); break; + default: + av_assert2(0); } memset(fixed_vector, 0, sizeof(float) * AMRWB_SFR_SIZE); diff --git a/libavcodec/ansi.c b/libavcodec/ansi.c index c1e31266ec3..d8d32bafbdc 100644 --- a/libavcodec/ansi.c +++ b/libavcodec/ansi.c @@ -262,7 +262,11 @@ static int execute_code(AVCodecContext * avctx, int c) AV_GET_BUFFER_FLAG_REF)) < 0) return ret; s->frame->pict_type = AV_PICTURE_TYPE_I; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS s->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif set_palette((uint32_t *)s->frame->data[1]); erase_screen(avctx); } else if (c == 'l') { @@ -326,7 +330,7 @@ static int execute_code(AVCodecContext * avctx, int c) s->bg = index < 16 ? ansi_to_cga[index] : index; i += 2; } else if (m == 49) { - s->fg = ansi_to_cga[DEFAULT_BG_COLOR]; + s->bg = ansi_to_cga[DEFAULT_BG_COLOR]; } else { avpriv_request_sample(avctx, "Unsupported rendition parameter"); } @@ -371,7 +375,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, } s->frame->pict_type = AV_PICTURE_TYPE_I; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS s->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif set_palette((uint32_t *)s->frame->data[1]); if (!s->first_frame) { erase_screen(avctx); diff --git a/libavcodec/aom_film_grain.c b/libavcodec/aom_film_grain.c new file mode 100644 index 00000000000..e302567ba5f --- /dev/null +++ b/libavcodec/aom_film_grain.c @@ -0,0 +1,548 @@ +/* + * AOM film grain synthesis + * Copyright (c) 2023 Niklas Haas + * + * 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 + * AOM film grain synthesis. + * @author Niklas Haas + */ + +#include "libavutil/avassert.h" +#include "libavutil/imgutils.h" + +#include "aom_film_grain.h" +#include "get_bits.h" + +// Common/shared helpers (not dependent on BIT_DEPTH) +static inline int get_random_number(const int bits, unsigned *const state) { + const int r = *state; + unsigned bit = ((r >> 0) ^ (r >> 1) ^ (r >> 3) ^ (r >> 12)) & 1; + *state = (r >> 1) | (bit << 15); + + return (*state >> (16 - bits)) & ((1 << bits) - 1); +} + +static inline int round2(const int x, const uint64_t shift) { + return (x + ((1 << shift) >> 1)) >> shift; +} + +enum { + GRAIN_WIDTH = 82, + GRAIN_HEIGHT = 73, + SUB_GRAIN_WIDTH = 44, + SUB_GRAIN_HEIGHT = 38, + FG_BLOCK_SIZE = 32, +}; + +static const int16_t gaussian_sequence[2048]; + +#define BIT_DEPTH 16 +#include "aom_film_grain_template.c" +#undef BIT_DEPTH + +#define BIT_DEPTH 8 +#include "aom_film_grain_template.c" +#undef BIT_DEPTH + + +int ff_aom_apply_film_grain(AVFrame *out, const AVFrame *in, + const AVFilmGrainParams *params) +{ + const AVFilmGrainAOMParams *const data = ¶ms->codec.aom; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(out->format); + const int subx = desc->log2_chroma_w, suby = desc->log2_chroma_h; + const int pxstep = desc->comp[0].step; + + av_assert0(out->format == in->format); + av_assert0(params->type == AV_FILM_GRAIN_PARAMS_AV1); + + // Copy over the non-modified planes + if (!params->codec.aom.num_y_points) { + av_image_copy_plane(out->data[0], out->linesize[0], + in->data[0], in->linesize[0], + out->width * pxstep, out->height); + } + for (int uv = 0; uv < 2; uv++) { + if (!data->num_uv_points[uv]) { + av_image_copy_plane(out->data[1+uv], out->linesize[1+uv], + in->data[1+uv], in->linesize[1+uv], + AV_CEIL_RSHIFT(out->width, subx) * pxstep, + AV_CEIL_RSHIFT(out->height, suby)); + } + } + + switch (in->format) { + case AV_PIX_FMT_GRAY8: + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUV444P: + case AV_PIX_FMT_YUVJ420P: + case AV_PIX_FMT_YUVJ422P: + case AV_PIX_FMT_YUVJ444P: + return apply_film_grain_8(out, in, params); + case AV_PIX_FMT_GRAY9: + case AV_PIX_FMT_YUV420P9: + case AV_PIX_FMT_YUV422P9: + case AV_PIX_FMT_YUV444P9: + return apply_film_grain_16(out, in, params, 9); + case AV_PIX_FMT_GRAY10: + case AV_PIX_FMT_YUV420P10: + case AV_PIX_FMT_YUV422P10: + case AV_PIX_FMT_YUV444P10: + return apply_film_grain_16(out, in, params, 10); + case AV_PIX_FMT_GRAY12: + case AV_PIX_FMT_YUV420P12: + case AV_PIX_FMT_YUV422P12: + case AV_PIX_FMT_YUV444P12: + return apply_film_grain_16(out, in, params, 12); + } + + /* The AV1 spec only defines film grain synthesis for these formats */ + return AVERROR_INVALIDDATA; +} + +int ff_aom_parse_film_grain_sets(AVFilmGrainAFGS1Params *s, + const uint8_t *payload, int payload_size) +{ + GetBitContext gbc, *gb = &gbc; + AVFilmGrainAOMParams *aom; + AVFilmGrainParams *fgp, *ref = NULL; + int ret, num_sets, n, i, uv, num_y_coeffs, update_grain, luma_only; + + ret = init_get_bits8(gb, payload, payload_size); + if (ret < 0) + return ret; + + s->enable = get_bits1(gb); + if (!s->enable) + return 0; + + skip_bits(gb, 4); // reserved + num_sets = get_bits(gb, 3) + 1; + for (n = 0; n < num_sets; n++) { + int payload_4byte, payload_size, set_idx, apply_units_log2, vsc_flag; + int predict_scaling, predict_y_scaling, predict_uv_scaling[2]; + int payload_bits, start_position; + + start_position = get_bits_count(gb); + payload_4byte = get_bits1(gb); + payload_size = get_bits(gb, payload_4byte ? 2 : 8); + set_idx = get_bits(gb, 3); + fgp = &s->sets[set_idx]; + aom = &fgp->codec.aom; + + fgp->type = get_bits1(gb) ? AV_FILM_GRAIN_PARAMS_AV1 : AV_FILM_GRAIN_PARAMS_NONE; + if (!fgp->type) + continue; + + fgp->seed = get_bits(gb, 16); + update_grain = get_bits1(gb); + if (!update_grain) + continue; + + apply_units_log2 = get_bits(gb, 4); + fgp->width = get_bits(gb, 12) << apply_units_log2; + fgp->height = get_bits(gb, 12) << apply_units_log2; + luma_only = get_bits1(gb); + if (luma_only) { + fgp->subsampling_x = fgp->subsampling_y = 0; + } else { + fgp->subsampling_x = get_bits1(gb); + fgp->subsampling_y = get_bits1(gb); + } + + fgp->bit_depth_luma = fgp->bit_depth_chroma = 0; + fgp->color_primaries = AVCOL_PRI_UNSPECIFIED; + fgp->color_trc = AVCOL_TRC_UNSPECIFIED; + fgp->color_space = AVCOL_SPC_UNSPECIFIED; + fgp->color_range = AVCOL_RANGE_UNSPECIFIED; + + vsc_flag = get_bits1(gb); // video_signal_characteristics_flag + if (vsc_flag) { + int cicp_flag; + fgp->bit_depth_luma = get_bits(gb, 3) + 8; + if (!luma_only) + fgp->bit_depth_chroma = fgp->bit_depth_luma; + cicp_flag = get_bits1(gb); + if (cicp_flag) { + fgp->color_primaries = get_bits(gb, 8); + fgp->color_trc = get_bits(gb, 8); + fgp->color_space = get_bits(gb, 8); + fgp->color_range = get_bits1(gb) ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; + if (fgp->color_primaries > AVCOL_PRI_NB || + fgp->color_primaries == AVCOL_PRI_RESERVED || + fgp->color_primaries == AVCOL_PRI_RESERVED0 || + fgp->color_trc > AVCOL_TRC_NB || + fgp->color_trc == AVCOL_TRC_RESERVED || + fgp->color_trc == AVCOL_TRC_RESERVED0 || + fgp->color_space > AVCOL_SPC_NB || + fgp->color_space == AVCOL_SPC_RESERVED) + goto error; + } + } + + predict_scaling = get_bits1(gb); + if (predict_scaling && (!ref || ref == fgp)) + goto error; // prediction must be from valid, different set + + predict_y_scaling = predict_scaling ? get_bits1(gb) : 0; + if (predict_y_scaling) { + int y_scale, y_offset, bits_res; + y_scale = get_bits(gb, 9) - 256; + y_offset = get_bits(gb, 9) - 256; + bits_res = get_bits(gb, 3); + if (bits_res) { + int res[14], pred, granularity; + aom->num_y_points = ref->codec.aom.num_y_points; + for (i = 0; i < aom->num_y_points; i++) + res[i] = get_bits(gb, bits_res); + granularity = get_bits(gb, 3); + for (i = 0; i < aom->num_y_points; i++) { + pred = ref->codec.aom.y_points[i][1]; + pred = ((pred * y_scale + 8) >> 4) + y_offset; + pred += (res[i] - (1 << (bits_res - 1))) * granularity; + aom->y_points[i][0] = ref->codec.aom.y_points[i][0]; + aom->y_points[i][1] = av_clip_uint8(pred); + } + } + } else { + aom->num_y_points = get_bits(gb, 4); + if (aom->num_y_points > 14) { + goto error; + } else if (aom->num_y_points) { + int bits_inc, bits_scaling; + int y_value = 0; + bits_inc = get_bits(gb, 3) + 1; + bits_scaling = get_bits(gb, 2) + 5; + for (i = 0; i < aom->num_y_points; i++) { + y_value += get_bits(gb, bits_inc); + if (y_value > UINT8_MAX) + goto error; + aom->y_points[i][0] = y_value; + aom->y_points[i][1] = get_bits(gb, bits_scaling); + } + } + } + + if (luma_only) { + aom->chroma_scaling_from_luma = 0; + aom->num_uv_points[0] = aom->num_uv_points[1] = 0; + } else { + aom->chroma_scaling_from_luma = get_bits1(gb); + if (aom->chroma_scaling_from_luma) { + aom->num_uv_points[0] = aom->num_uv_points[1] = 0; + } else { + for (uv = 0; uv < 2; uv++) { + predict_uv_scaling[uv] = predict_scaling ? get_bits1(gb) : 0; + if (predict_uv_scaling[uv]) { + int uv_scale, uv_offset, bits_res; + uv_scale = get_bits(gb, 9) - 256; + uv_offset = get_bits(gb, 9) - 256; + bits_res = get_bits(gb, 3); + aom->uv_mult[uv] = ref->codec.aom.uv_mult[uv]; + aom->uv_mult_luma[uv] = ref->codec.aom.uv_mult_luma[uv]; + aom->uv_offset[uv] = ref->codec.aom.uv_offset[uv]; + if (bits_res) { + int res[10], pred, granularity; + aom->num_uv_points[uv] = ref->codec.aom.num_uv_points[uv]; + for (i = 0; i < aom->num_uv_points[uv]; i++) + res[i] = get_bits(gb, bits_res); + granularity = get_bits(gb, 3); + for (i = 0; i < aom->num_uv_points[uv]; i++) { + pred = ref->codec.aom.uv_points[uv][i][1]; + pred = ((pred * uv_scale + 8) >> 4) + uv_offset; + pred += (res[i] - (1 << (bits_res - 1))) * granularity; + aom->uv_points[uv][i][0] = ref->codec.aom.uv_points[uv][i][0]; + aom->uv_points[uv][i][1] = av_clip_uint8(pred); + } + } + } else { + int bits_inc, bits_scaling, uv_offset; + int uv_value = 0; + aom->num_uv_points[uv] = get_bits(gb, 4); + if (aom->num_uv_points[uv] > 10) + goto error; + bits_inc = get_bits(gb, 3) + 1; + bits_scaling = get_bits(gb, 2) + 5; + uv_offset = get_bits(gb, 8); + for (i = 0; i < aom->num_uv_points[uv]; i++) { + uv_value += get_bits(gb, bits_inc); + if (uv_value > UINT8_MAX) + goto error; + aom->uv_points[uv][i][0] = uv_value; + aom->uv_points[uv][i][1] = get_bits(gb, bits_scaling) + uv_offset; + } + } + } + } + } + + aom->scaling_shift = get_bits(gb, 2) + 8; + aom->ar_coeff_lag = get_bits(gb, 2); + num_y_coeffs = 2 * aom->ar_coeff_lag * (aom->ar_coeff_lag + 1); + if (aom->num_y_points) { + int ar_bits = get_bits(gb, 2) + 5; + for (i = 0; i < num_y_coeffs; i++) + aom->ar_coeffs_y[i] = get_bits(gb, ar_bits) - (1 << (ar_bits - 1)); + } + for (uv = 0; uv < 2; uv++) { + if (aom->chroma_scaling_from_luma || aom->num_uv_points[uv]) { + int ar_bits = get_bits(gb, 2) + 5; + for (i = 0; i < num_y_coeffs + !!aom->num_y_points; i++) + aom->ar_coeffs_uv[uv][i] = get_bits(gb, ar_bits) - (1 << (ar_bits - 1)); + } + } + aom->ar_coeff_shift = get_bits(gb, 2) + 6; + aom->grain_scale_shift = get_bits(gb, 2); + for (uv = 0; uv < 2; uv++) { + if (aom->num_uv_points[uv] && !predict_uv_scaling[uv]) { + aom->uv_mult[uv] = get_bits(gb, 8) - 128; + aom->uv_mult_luma[uv] = get_bits(gb, 8) - 128; + aom->uv_offset[uv] = get_bits(gb, 9) - 256; + } + } + aom->overlap_flag = get_bits1(gb); + aom->limit_output_range = get_bits1(gb); + + // use first set as reference only if it was fully transmitted + if (n == 0) + ref = fgp; + + payload_bits = get_bits_count(gb) - start_position; + if (payload_bits > payload_size * 8) + goto error; + skip_bits(gb, payload_size * 8 - payload_bits); + } + return 0; + +error: + memset(s, 0, sizeof(*s)); + return AVERROR_INVALIDDATA; +} + +int ff_aom_attach_film_grain_sets(const AVFilmGrainAFGS1Params *s, AVFrame *frame) +{ + AVFilmGrainParams *fgp; + if (!s->enable) + return 0; + + for (int i = 0; i < FF_ARRAY_ELEMS(s->sets); i++) { + if (s->sets[i].type != AV_FILM_GRAIN_PARAMS_AV1) + continue; + fgp = av_film_grain_params_create_side_data(frame); + if (!fgp) + return AVERROR(ENOMEM); + memcpy(fgp, &s->sets[i], sizeof(*fgp)); + } + + return 0; +} + +// Taken from the AV1 spec. Range is [-2048, 2047], mean is 0 and stddev is 512 +static const int16_t gaussian_sequence[2048] = { + 56, 568, -180, 172, 124, -84, 172, -64, -900, 24, 820, + 224, 1248, 996, 272, -8, -916, -388, -732, -104, -188, 800, + 112, -652, -320, -376, 140, -252, 492, -168, 44, -788, 588, + -584, 500, -228, 12, 680, 272, -476, 972, -100, 652, 368, + 432, -196, -720, -192, 1000, -332, 652, -136, -552, -604, -4, + 192, -220, -136, 1000, -52, 372, -96, -624, 124, -24, 396, + 540, -12, -104, 640, 464, 244, -208, -84, 368, -528, -740, + 248, -968, -848, 608, 376, -60, -292, -40, -156, 252, -292, + 248, 224, -280, 400, -244, 244, -60, 76, -80, 212, 532, + 340, 128, -36, 824, -352, -60, -264, -96, -612, 416, -704, + 220, -204, 640, -160, 1220, -408, 900, 336, 20, -336, -96, + -792, 304, 48, -28, -1232, -1172, -448, 104, -292, -520, 244, + 60, -948, 0, -708, 268, 108, 356, -548, 488, -344, -136, + 488, -196, -224, 656, -236, -1128, 60, 4, 140, 276, -676, + -376, 168, -108, 464, 8, 564, 64, 240, 308, -300, -400, + -456, -136, 56, 120, -408, -116, 436, 504, -232, 328, 844, + -164, -84, 784, -168, 232, -224, 348, -376, 128, 568, 96, + -1244, -288, 276, 848, 832, -360, 656, 464, -384, -332, -356, + 728, -388, 160, -192, 468, 296, 224, 140, -776, -100, 280, + 4, 196, 44, -36, -648, 932, 16, 1428, 28, 528, 808, + 772, 20, 268, 88, -332, -284, 124, -384, -448, 208, -228, + -1044, -328, 660, 380, -148, -300, 588, 240, 540, 28, 136, + -88, -436, 256, 296, -1000, 1400, 0, -48, 1056, -136, 264, + -528, -1108, 632, -484, -592, -344, 796, 124, -668, -768, 388, + 1296, -232, -188, -200, -288, -4, 308, 100, -168, 256, -500, + 204, -508, 648, -136, 372, -272, -120, -1004, -552, -548, -384, + 548, -296, 428, -108, -8, -912, -324, -224, -88, -112, -220, + -100, 996, -796, 548, 360, -216, 180, 428, -200, -212, 148, + 96, 148, 284, 216, -412, -320, 120, -300, -384, -604, -572, + -332, -8, -180, -176, 696, 116, -88, 628, 76, 44, -516, + 240, -208, -40, 100, -592, 344, -308, -452, -228, 20, 916, + -1752, -136, -340, -804, 140, 40, 512, 340, 248, 184, -492, + 896, -156, 932, -628, 328, -688, -448, -616, -752, -100, 560, + -1020, 180, -800, -64, 76, 576, 1068, 396, 660, 552, -108, + -28, 320, -628, 312, -92, -92, -472, 268, 16, 560, 516, + -672, -52, 492, -100, 260, 384, 284, 292, 304, -148, 88, + -152, 1012, 1064, -228, 164, -376, -684, 592, -392, 156, 196, + -524, -64, -884, 160, -176, 636, 648, 404, -396, -436, 864, + 424, -728, 988, -604, 904, -592, 296, -224, 536, -176, -920, + 436, -48, 1176, -884, 416, -776, -824, -884, 524, -548, -564, + -68, -164, -96, 692, 364, -692, -1012, -68, 260, -480, 876, + -1116, 452, -332, -352, 892, -1088, 1220, -676, 12, -292, 244, + 496, 372, -32, 280, 200, 112, -440, -96, 24, -644, -184, + 56, -432, 224, -980, 272, -260, 144, -436, 420, 356, 364, + -528, 76, 172, -744, -368, 404, -752, -416, 684, -688, 72, + 540, 416, 92, 444, 480, -72, -1416, 164, -1172, -68, 24, + 424, 264, 1040, 128, -912, -524, -356, 64, 876, -12, 4, + -88, 532, 272, -524, 320, 276, -508, 940, 24, -400, -120, + 756, 60, 236, -412, 100, 376, -484, 400, -100, -740, -108, + -260, 328, -268, 224, -200, -416, 184, -604, -564, -20, 296, + 60, 892, -888, 60, 164, 68, -760, 216, -296, 904, -336, + -28, 404, -356, -568, -208, -1480, -512, 296, 328, -360, -164, + -1560, -776, 1156, -428, 164, -504, -112, 120, -216, -148, -264, + 308, 32, 64, -72, 72, 116, 176, -64, -272, 460, -536, + -784, -280, 348, 108, -752, -132, 524, -540, -776, 116, -296, + -1196, -288, -560, 1040, -472, 116, -848, -1116, 116, 636, 696, + 284, -176, 1016, 204, -864, -648, -248, 356, 972, -584, -204, + 264, 880, 528, -24, -184, 116, 448, -144, 828, 524, 212, + -212, 52, 12, 200, 268, -488, -404, -880, 824, -672, -40, + 908, -248, 500, 716, -576, 492, -576, 16, 720, -108, 384, + 124, 344, 280, 576, -500, 252, 104, -308, 196, -188, -8, + 1268, 296, 1032, -1196, 436, 316, 372, -432, -200, -660, 704, + -224, 596, -132, 268, 32, -452, 884, 104, -1008, 424, -1348, + -280, 4, -1168, 368, 476, 696, 300, -8, 24, 180, -592, + -196, 388, 304, 500, 724, -160, 244, -84, 272, -256, -420, + 320, 208, -144, -156, 156, 364, 452, 28, 540, 316, 220, + -644, -248, 464, 72, 360, 32, -388, 496, -680, -48, 208, + -116, -408, 60, -604, -392, 548, -840, 784, -460, 656, -544, + -388, -264, 908, -800, -628, -612, -568, 572, -220, 164, 288, + -16, -308, 308, -112, -636, -760, 280, -668, 432, 364, 240, + -196, 604, 340, 384, 196, 592, -44, -500, 432, -580, -132, + 636, -76, 392, 4, -412, 540, 508, 328, -356, -36, 16, + -220, -64, -248, -60, 24, -192, 368, 1040, 92, -24, -1044, + -32, 40, 104, 148, 192, -136, -520, 56, -816, -224, 732, + 392, 356, 212, -80, -424, -1008, -324, 588, -1496, 576, 460, + -816, -848, 56, -580, -92, -1372, -112, -496, 200, 364, 52, + -140, 48, -48, -60, 84, 72, 40, 132, -356, -268, -104, + -284, -404, 732, -520, 164, -304, -540, 120, 328, -76, -460, + 756, 388, 588, 236, -436, -72, -176, -404, -316, -148, 716, + -604, 404, -72, -88, -888, -68, 944, 88, -220, -344, 960, + 472, 460, -232, 704, 120, 832, -228, 692, -508, 132, -476, + 844, -748, -364, -44, 1116, -1104, -1056, 76, 428, 552, -692, + 60, 356, 96, -384, -188, -612, -576, 736, 508, 892, 352, + -1132, 504, -24, -352, 324, 332, -600, -312, 292, 508, -144, + -8, 484, 48, 284, -260, -240, 256, -100, -292, -204, -44, + 472, -204, 908, -188, -1000, -256, 92, 1164, -392, 564, 356, + 652, -28, -884, 256, 484, -192, 760, -176, 376, -524, -452, + -436, 860, -736, 212, 124, 504, -476, 468, 76, -472, 552, + -692, -944, -620, 740, -240, 400, 132, 20, 192, -196, 264, + -668, -1012, -60, 296, -316, -828, 76, -156, 284, -768, -448, + -832, 148, 248, 652, 616, 1236, 288, -328, -400, -124, 588, + 220, 520, -696, 1032, 768, -740, -92, -272, 296, 448, -464, + 412, -200, 392, 440, -200, 264, -152, -260, 320, 1032, 216, + 320, -8, -64, 156, -1016, 1084, 1172, 536, 484, -432, 132, + 372, -52, -256, 84, 116, -352, 48, 116, 304, -384, 412, + 924, -300, 528, 628, 180, 648, 44, -980, -220, 1320, 48, + 332, 748, 524, -268, -720, 540, -276, 564, -344, -208, -196, + 436, 896, 88, -392, 132, 80, -964, -288, 568, 56, -48, + -456, 888, 8, 552, -156, -292, 948, 288, 128, -716, -292, + 1192, -152, 876, 352, -600, -260, -812, -468, -28, -120, -32, + -44, 1284, 496, 192, 464, 312, -76, -516, -380, -456, -1012, + -48, 308, -156, 36, 492, -156, -808, 188, 1652, 68, -120, + -116, 316, 160, -140, 352, 808, -416, 592, 316, -480, 56, + 528, -204, -568, 372, -232, 752, -344, 744, -4, 324, -416, + -600, 768, 268, -248, -88, -132, -420, -432, 80, -288, 404, + -316, -1216, -588, 520, -108, 92, -320, 368, -480, -216, -92, + 1688, -300, 180, 1020, -176, 820, -68, -228, -260, 436, -904, + 20, 40, -508, 440, -736, 312, 332, 204, 760, -372, 728, + 96, -20, -632, -520, -560, 336, 1076, -64, -532, 776, 584, + 192, 396, -728, -520, 276, -188, 80, -52, -612, -252, -48, + 648, 212, -688, 228, -52, -260, 428, -412, -272, -404, 180, + 816, -796, 48, 152, 484, -88, -216, 988, 696, 188, -528, + 648, -116, -180, 316, 476, 12, -564, 96, 476, -252, -364, + -376, -392, 556, -256, -576, 260, -352, 120, -16, -136, -260, + -492, 72, 556, 660, 580, 616, 772, 436, 424, -32, -324, + -1268, 416, -324, -80, 920, 160, 228, 724, 32, -516, 64, + 384, 68, -128, 136, 240, 248, -204, -68, 252, -932, -120, + -480, -628, -84, 192, 852, -404, -288, -132, 204, 100, 168, + -68, -196, -868, 460, 1080, 380, -80, 244, 0, 484, -888, + 64, 184, 352, 600, 460, 164, 604, -196, 320, -64, 588, + -184, 228, 12, 372, 48, -848, -344, 224, 208, -200, 484, + 128, -20, 272, -468, -840, 384, 256, -720, -520, -464, -580, + 112, -120, 644, -356, -208, -608, -528, 704, 560, -424, 392, + 828, 40, 84, 200, -152, 0, -144, 584, 280, -120, 80, + -556, -972, -196, -472, 724, 80, 168, -32, 88, 160, -688, + 0, 160, 356, 372, -776, 740, -128, 676, -248, -480, 4, + -364, 96, 544, 232, -1032, 956, 236, 356, 20, -40, 300, + 24, -676, -596, 132, 1120, -104, 532, -1096, 568, 648, 444, + 508, 380, 188, -376, -604, 1488, 424, 24, 756, -220, -192, + 716, 120, 920, 688, 168, 44, -460, 568, 284, 1144, 1160, + 600, 424, 888, 656, -356, -320, 220, 316, -176, -724, -188, + -816, -628, -348, -228, -380, 1012, -452, -660, 736, 928, 404, + -696, -72, -268, -892, 128, 184, -344, -780, 360, 336, 400, + 344, 428, 548, -112, 136, -228, -216, -820, -516, 340, 92, + -136, 116, -300, 376, -244, 100, -316, -520, -284, -12, 824, + 164, -548, -180, -128, 116, -924, -828, 268, -368, -580, 620, + 192, 160, 0, -1676, 1068, 424, -56, -360, 468, -156, 720, + 288, -528, 556, -364, 548, -148, 504, 316, 152, -648, -620, + -684, -24, -376, -384, -108, -920, -1032, 768, 180, -264, -508, + -1268, -260, -60, 300, -240, 988, 724, -376, -576, -212, -736, + 556, 192, 1092, -620, -880, 376, -56, -4, -216, -32, 836, + 268, 396, 1332, 864, -600, 100, 56, -412, -92, 356, 180, + 884, -468, -436, 292, -388, -804, -704, -840, 368, -348, 140, + -724, 1536, 940, 372, 112, -372, 436, -480, 1136, 296, -32, + -228, 132, -48, -220, 868, -1016, -60, -1044, -464, 328, 916, + 244, 12, -736, -296, 360, 468, -376, -108, -92, 788, 368, + -56, 544, 400, -672, -420, 728, 16, 320, 44, -284, -380, + -796, 488, 132, 204, -596, -372, 88, -152, -908, -636, -572, + -624, -116, -692, -200, -56, 276, -88, 484, -324, 948, 864, + 1000, -456, -184, -276, 292, -296, 156, 676, 320, 160, 908, + -84, -1236, -288, -116, 260, -372, -644, 732, -756, -96, 84, + 344, -520, 348, -688, 240, -84, 216, -1044, -136, -676, -396, + -1500, 960, -40, 176, 168, 1516, 420, -504, -344, -364, -360, + 1216, -940, -380, -212, 252, -660, -708, 484, -444, -152, 928, + -120, 1112, 476, -260, 560, -148, -344, 108, -196, 228, -288, + 504, 560, -328, -88, 288, -1008, 460, -228, 468, -836, -196, + 76, 388, 232, 412, -1168, -716, -644, 756, -172, -356, -504, + 116, 432, 528, 48, 476, -168, -608, 448, 160, -532, -272, + 28, -676, -12, 828, 980, 456, 520, 104, -104, 256, -344, + -4, -28, -368, -52, -524, -572, -556, -200, 768, 1124, -208, + -512, 176, 232, 248, -148, -888, 604, -600, -304, 804, -156, + -212, 488, -192, -804, -256, 368, -360, -916, -328, 228, -240, + -448, -472, 856, -556, -364, 572, -12, -156, -368, -340, 432, + 252, -752, -152, 288, 268, -580, -848, -592, 108, -76, 244, + 312, -716, 592, -80, 436, 360, 4, -248, 160, 516, 584, + 732, 44, -468, -280, -292, -156, -588, 28, 308, 912, 24, + 124, 156, 180, -252, 944, -924, -772, -520, -428, -624, 300, + -212, -1144, 32, -724, 800, -1128, -212, -1288, -848, 180, -416, + 440, 192, -576, -792, -76, -1080, 80, -532, -352, -132, 380, + -820, 148, 1112, 128, 164, 456, 700, -924, 144, -668, -384, + 648, -832, 508, 552, -52, -100, -656, 208, -568, 748, -88, + 680, 232, 300, 192, -408, -1012, -152, -252, -268, 272, -876, + -664, -648, -332, -136, 16, 12, 1152, -28, 332, -536, 320, + -672, -460, -316, 532, -260, 228, -40, 1052, -816, 180, 88, + -496, -556, -672, -368, 428, 92, 356, 404, -408, 252, 196, + -176, -556, 792, 268, 32, 372, 40, 96, -332, 328, 120, + 372, -900, -40, 472, -264, -592, 952, 128, 656, 112, 664, + -232, 420, 4, -344, -464, 556, 244, -416, -32, 252, 0, + -412, 188, -696, 508, -476, 324, -1096, 656, -312, 560, 264, + -136, 304, 160, -64, -580, 248, 336, -720, 560, -348, -288, + -276, -196, -500, 852, -544, -236, -1128, -992, -776, 116, 56, + 52, 860, 884, 212, -12, 168, 1020, 512, -552, 924, -148, + 716, 188, 164, -340, -520, -184, 880, -152, -680, -208, -1156, + -300, -528, -472, 364, 100, -744, -1056, -32, 540, 280, 144, + -676, -32, -232, -280, -224, 96, 568, -76, 172, 148, 148, + 104, 32, -296, -32, 788, -80, 32, -16, 280, 288, 944, + 428, -484 +}; diff --git a/libavcodec/aom_film_grain.h b/libavcodec/aom_film_grain.h new file mode 100644 index 00000000000..1f8c78f657a --- /dev/null +++ b/libavcodec/aom_film_grain.h @@ -0,0 +1,51 @@ +/* + * AOM film grain synthesis + * Copyright (c) 2021 Niklas Haas + * + * 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 + * AOM film grain synthesis. + * @author Niklas Haas + */ + +#ifndef AVCODEC_AOM_FILM_GRAIN_H +#define AVCODEC_AOM_FILM_GRAIN_H + +#include "libavutil/film_grain_params.h" + +typedef struct AVFilmGrainAFGS1Params { + int enable; + AVFilmGrainParams sets[8]; +} AVFilmGrainAFGS1Params; + +// Synthesizes film grain on top of `in` and stores the result to `out`. `out` +// must already have been allocated and set to the same size and format as `in`. +int ff_aom_apply_film_grain(AVFrame *out, const AVFrame *in, + const AVFilmGrainParams *params); + +// Parse AFGS1 parameter sets from an ITU-T T.35 payload. Returns 0 on success, +// or a negative error code. +int ff_aom_parse_film_grain_sets(AVFilmGrainAFGS1Params *s, + const uint8_t *payload, int payload_size); + +// Attach all valid film grain param sets to `frame`. +int ff_aom_attach_film_grain_sets(const AVFilmGrainAFGS1Params *s, AVFrame *frame); + +#endif /* AVCODEC_AOM_FILM_GRAIN_H */ diff --git a/libavcodec/aom_film_grain_template.c b/libavcodec/aom_film_grain_template.c new file mode 100644 index 00000000000..5f9f29f1fab --- /dev/null +++ b/libavcodec/aom_film_grain_template.c @@ -0,0 +1,577 @@ +/* + * AOM film grain synthesis + * Copyright (c) 2023 Niklas Haas + * + * 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 + */ + +/* + * Copyright © 2018, Niklas Haas + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "bit_depth_template.c" + +#undef entry +#undef bitdepth +#undef bitdepth_max +#undef HBD_DECL +#undef HBD_CALL +#undef SCALING_SIZE + +#if BIT_DEPTH > 8 +# define entry int16_t +# define bitdepth_max ((1 << bitdepth) - 1) +# define HBD_DECL , const int bitdepth +# define HBD_CALL , bitdepth +# define SCALING_SIZE 4096 +#else +# define entry int8_t +# define bitdepth 8 +# define bitdepth_max UINT8_MAX +# define HBD_DECL +# define HBD_CALL +# define SCALING_SIZE 256 +#endif + +static void FUNC(generate_grain_y_c)(entry buf[][GRAIN_WIDTH], + const AVFilmGrainParams *const params + HBD_DECL) +{ + const AVFilmGrainAOMParams *const data = ¶ms->codec.aom; + const int bitdepth_min_8 = bitdepth - 8; + unsigned seed = params->seed; + const int shift = 4 - bitdepth_min_8 + data->grain_scale_shift; + const int grain_ctr = 128 << bitdepth_min_8; + const int grain_min = -grain_ctr, grain_max = grain_ctr - 1; + + const int ar_pad = 3; + const int ar_lag = data->ar_coeff_lag; + + for (int y = 0; y < GRAIN_HEIGHT; y++) { + for (int x = 0; x < GRAIN_WIDTH; x++) { + const int value = get_random_number(11, &seed); + buf[y][x] = round2(gaussian_sequence[ value ], shift); + } + } + + for (int y = ar_pad; y < GRAIN_HEIGHT; y++) { + for (int x = ar_pad; x < GRAIN_WIDTH - ar_pad; x++) { + const int8_t *coeff = data->ar_coeffs_y; + int sum = 0, grain; + for (int dy = -ar_lag; dy <= 0; dy++) { + for (int dx = -ar_lag; dx <= ar_lag; dx++) { + if (!dx && !dy) + break; + sum += *(coeff++) * buf[y + dy][x + dx]; + } + } + + grain = buf[y][x] + round2(sum, data->ar_coeff_shift); + buf[y][x] = av_clip(grain, grain_min, grain_max); + } + } +} + +static void +FUNC(generate_grain_uv_c)(entry buf[][GRAIN_WIDTH], + const entry buf_y[][GRAIN_WIDTH], + const AVFilmGrainParams *const params, const intptr_t uv, + const int subx, const int suby HBD_DECL) +{ + const AVFilmGrainAOMParams *const data = ¶ms->codec.aom; + const int bitdepth_min_8 = bitdepth - 8; + unsigned seed = params->seed ^ (uv ? 0x49d8 : 0xb524); + const int shift = 4 - bitdepth_min_8 + data->grain_scale_shift; + const int grain_ctr = 128 << bitdepth_min_8; + const int grain_min = -grain_ctr, grain_max = grain_ctr - 1; + + const int chromaW = subx ? SUB_GRAIN_WIDTH : GRAIN_WIDTH; + const int chromaH = suby ? SUB_GRAIN_HEIGHT : GRAIN_HEIGHT; + + const int ar_pad = 3; + const int ar_lag = data->ar_coeff_lag; + + for (int y = 0; y < chromaH; y++) { + for (int x = 0; x < chromaW; x++) { + const int value = get_random_number(11, &seed); + buf[y][x] = round2(gaussian_sequence[ value ], shift); + } + } + + for (int y = ar_pad; y < chromaH; y++) { + for (int x = ar_pad; x < chromaW - ar_pad; x++) { + const int8_t *coeff = data->ar_coeffs_uv[uv]; + int sum = 0, grain; + for (int dy = -ar_lag; dy <= 0; dy++) { + for (int dx = -ar_lag; dx <= ar_lag; dx++) { + // For the final (current) pixel, we need to add in the + // contribution from the luma grain texture + if (!dx && !dy) { + const int lumaX = ((x - ar_pad) << subx) + ar_pad; + const int lumaY = ((y - ar_pad) << suby) + ar_pad; + int luma = 0; + if (!data->num_y_points) + break; + for (int i = 0; i <= suby; i++) { + for (int j = 0; j <= subx; j++) { + luma += buf_y[lumaY + i][lumaX + j]; + } + } + luma = round2(luma, subx + suby); + sum += luma * (*coeff); + break; + } + + sum += *(coeff++) * buf[y + dy][x + dx]; + } + } + + grain = buf[y][x] + round2(sum, data->ar_coeff_shift); + buf[y][x] = av_clip(grain, grain_min, grain_max); + } + } +} + +// samples from the correct block of a grain LUT, while taking into account the +// offsets provided by the offsets cache +static inline entry FUNC(sample_lut)(const entry grain_lut[][GRAIN_WIDTH], + const int offsets[2][2], + const int subx, const int suby, + const int bx, const int by, + const int x, const int y) +{ + const int randval = offsets[bx][by]; + const int offx = 3 + (2 >> subx) * (3 + (randval >> 4)); + const int offy = 3 + (2 >> suby) * (3 + (randval & 0xF)); + return grain_lut[offy + y + (FG_BLOCK_SIZE >> suby) * by] + [offx + x + (FG_BLOCK_SIZE >> subx) * bx]; +} + +static void FUNC(fgy_32x32xn_c)(pixel *const dst_row, const pixel *const src_row, + const ptrdiff_t stride, + const AVFilmGrainParams *const params, const size_t pw, + const uint8_t scaling[SCALING_SIZE], + const entry grain_lut[][GRAIN_WIDTH], + const int bh, const int row_num HBD_DECL) +{ + const AVFilmGrainAOMParams *const data = ¶ms->codec.aom; + const int rows = 1 + (data->overlap_flag && row_num > 0); + const int bitdepth_min_8 = bitdepth - 8; + const int grain_ctr = 128 << bitdepth_min_8; + const int grain_min = -grain_ctr, grain_max = grain_ctr - 1; + unsigned seed[2]; + int offsets[2 /* col offset */][2 /* row offset */]; + + int min_value, max_value; + if (data->limit_output_range) { + min_value = 16 << bitdepth_min_8; + max_value = 235 << bitdepth_min_8; + } else { + min_value = 0; + max_value = bitdepth_max; + } + + // seed[0] contains the current row, seed[1] contains the previous + for (int i = 0; i < rows; i++) { + seed[i] = params->seed; + seed[i] ^= (((row_num - i) * 37 + 178) & 0xFF) << 8; + seed[i] ^= (((row_num - i) * 173 + 105) & 0xFF); + } + + av_assert1(stride % (FG_BLOCK_SIZE * sizeof(pixel)) == 0); + + // process this row in FG_BLOCK_SIZE^2 blocks + for (unsigned bx = 0; bx < pw; bx += FG_BLOCK_SIZE) { + const int bw = FFMIN(FG_BLOCK_SIZE, (int) pw - bx); + const pixel *src; + pixel *dst; + int noise; + + // x/y block offsets to compensate for overlapped regions + const int ystart = data->overlap_flag && row_num ? FFMIN(2, bh) : 0; + const int xstart = data->overlap_flag && bx ? FFMIN(2, bw) : 0; + + static const int w[2][2] = { { 27, 17 }, { 17, 27 } }; + + if (data->overlap_flag && bx) { + // shift previous offsets left + for (int i = 0; i < rows; i++) + offsets[1][i] = offsets[0][i]; + } + + // update current offsets + for (int i = 0; i < rows; i++) + offsets[0][i] = get_random_number(8, &seed[i]); + +#define add_noise_y(x, y, grain) \ + src = (const pixel*)((const char*)src_row + (y) * stride) + (x) + bx; \ + dst = (pixel*)((char*)dst_row + (y) * stride) + (x) + bx; \ + noise = round2(scaling[ *src ] * (grain), data->scaling_shift); \ + *dst = av_clip(*src + noise, min_value, max_value); + + for (int y = ystart; y < bh; y++) { + // Non-overlapped image region (straightforward) + for (int x = xstart; x < bw; x++) { + int grain = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 0, 0, x, y); + add_noise_y(x, y, grain); + } + + // Special case for overlapped column + for (int x = 0; x < xstart; x++) { + int grain = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 0, 0, x, y); + int old = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 1, 0, x, y); + grain = round2(old * w[x][0] + grain * w[x][1], 5); + grain = av_clip(grain, grain_min, grain_max); + add_noise_y(x, y, grain); + } + } + + for (int y = 0; y < ystart; y++) { + // Special case for overlapped row (sans corner) + for (int x = xstart; x < bw; x++) { + int grain = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 0, 0, x, y); + int old = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 0, 1, x, y); + grain = round2(old * w[y][0] + grain * w[y][1], 5); + grain = av_clip(grain, grain_min, grain_max); + add_noise_y(x, y, grain); + } + + // Special case for doubly-overlapped corner + for (int x = 0; x < xstart; x++) { + int grain = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 0, 0, x, y); + int top = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 0, 1, x, y); + int old = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 1, 1, x, y); + + // Blend the top pixel with the top left block + top = round2(old * w[x][0] + top * w[x][1], 5); + top = av_clip(top, grain_min, grain_max); + + // Blend the current pixel with the left block + old = FUNC(sample_lut)(grain_lut, offsets, 0, 0, 1, 0, x, y); + grain = round2(old * w[x][0] + grain * w[x][1], 5); + grain = av_clip(grain, grain_min, grain_max); + + // Mix the row rows together and apply grain + grain = round2(top * w[y][0] + grain * w[y][1], 5); + grain = av_clip(grain, grain_min, grain_max); + add_noise_y(x, y, grain); + } + } + } +} + +static void +FUNC(fguv_32x32xn_c)(pixel *const dst_row, const pixel *const src_row, + const ptrdiff_t stride, const AVFilmGrainParams *const params, + const size_t pw, const uint8_t scaling[SCALING_SIZE], + const entry grain_lut[][GRAIN_WIDTH], const int bh, + const int row_num, const pixel *const luma_row, + const ptrdiff_t luma_stride, const int uv, const int is_id, + const int sx, const int sy HBD_DECL) +{ + const AVFilmGrainAOMParams *const data = ¶ms->codec.aom; + const int rows = 1 + (data->overlap_flag && row_num > 0); + const int bitdepth_min_8 = bitdepth - 8; + const int grain_ctr = 128 << bitdepth_min_8; + const int grain_min = -grain_ctr, grain_max = grain_ctr - 1; + unsigned seed[2]; + int offsets[2 /* col offset */][2 /* row offset */]; + + int min_value, max_value; + if (data->limit_output_range) { + min_value = 16 << bitdepth_min_8; + max_value = (is_id ? 235 : 240) << bitdepth_min_8; + } else { + min_value = 0; + max_value = bitdepth_max; + } + + // seed[0] contains the current row, seed[1] contains the previous + for (int i = 0; i < rows; i++) { + seed[i] = params->seed; + seed[i] ^= (((row_num - i) * 37 + 178) & 0xFF) << 8; + seed[i] ^= (((row_num - i) * 173 + 105) & 0xFF); + } + + av_assert1(stride % (FG_BLOCK_SIZE * sizeof(pixel)) == 0); + + // process this row in FG_BLOCK_SIZE^2 blocks (subsampled) + for (unsigned bx = 0; bx < pw; bx += FG_BLOCK_SIZE >> sx) { + const int bw = FFMIN(FG_BLOCK_SIZE >> sx, (int)(pw - bx)); + int val, lx, ly, noise; + const pixel *src, *luma; + pixel *dst, avg; + + // x/y block offsets to compensate for overlapped regions + const int ystart = data->overlap_flag && row_num ? FFMIN(2 >> sy, bh) : 0; + const int xstart = data->overlap_flag && bx ? FFMIN(2 >> sx, bw) : 0; + + static const int w[2 /* sub */][2 /* off */][2] = { + { { 27, 17 }, { 17, 27 } }, + { { 23, 22 } }, + }; + + if (data->overlap_flag && bx) { + // shift previous offsets left + for (int i = 0; i < rows; i++) + offsets[1][i] = offsets[0][i]; + } + + // update current offsets + for (int i = 0; i < rows; i++) + offsets[0][i] = get_random_number(8, &seed[i]); + +#define add_noise_uv(x, y, grain) \ + lx = (bx + x) << sx; \ + ly = y << sy; \ + luma = (const pixel*)((const char*)luma_row + ly * luma_stride) + lx;\ + avg = luma[0]; \ + if (sx) \ + avg = (avg + luma[1] + 1) >> 1; \ + src = (const pixel*)((const char *)src_row + (y) * stride) + bx + (x);\ + dst = (pixel *) ((char *) dst_row + (y) * stride) + bx + (x); \ + val = avg; \ + if (!data->chroma_scaling_from_luma) { \ + const int combined = avg * data->uv_mult_luma[uv] + \ + *src * data->uv_mult[uv]; \ + val = av_clip( (combined >> 6) + \ + (data->uv_offset[uv] * (1 << bitdepth_min_8)), \ + 0, bitdepth_max ); \ + } \ + noise = round2(scaling[ val ] * (grain), data->scaling_shift); \ + *dst = av_clip(*src + noise, min_value, max_value); + + for (int y = ystart; y < bh; y++) { + // Non-overlapped image region (straightforward) + for (int x = xstart; x < bw; x++) { + int grain = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 0, 0, x, y); + add_noise_uv(x, y, grain); + } + + // Special case for overlapped column + for (int x = 0; x < xstart; x++) { + int grain = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 0, 0, x, y); + int old = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 1, 0, x, y); + grain = round2(old * w[sx][x][0] + grain * w[sx][x][1], 5); + grain = av_clip(grain, grain_min, grain_max); + add_noise_uv(x, y, grain); + } + } + + for (int y = 0; y < ystart; y++) { + // Special case for overlapped row (sans corner) + for (int x = xstart; x < bw; x++) { + int grain = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 0, 0, x, y); + int old = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 0, 1, x, y); + grain = round2(old * w[sy][y][0] + grain * w[sy][y][1], 5); + grain = av_clip(grain, grain_min, grain_max); + add_noise_uv(x, y, grain); + } + + // Special case for doubly-overlapped corner + for (int x = 0; x < xstart; x++) { + int top = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 0, 1, x, y); + int old = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 1, 1, x, y); + int grain = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 0, 0, x, y); + + // Blend the top pixel with the top left block + top = round2(old * w[sx][x][0] + top * w[sx][x][1], 5); + top = av_clip(top, grain_min, grain_max); + + // Blend the current pixel with the left block + old = FUNC(sample_lut)(grain_lut, offsets, sx, sy, 1, 0, x, y); + grain = round2(old * w[sx][x][0] + grain * w[sx][x][1], 5); + grain = av_clip(grain, grain_min, grain_max); + + // Mix the row rows together and apply to image + grain = round2(top * w[sy][y][0] + grain * w[sy][y][1], 5); + grain = av_clip(grain, grain_min, grain_max); + add_noise_uv(x, y, grain); + } + } + } +} + +static void FUNC(generate_scaling)(const uint8_t points[][2], const int num, + uint8_t scaling[SCALING_SIZE] HBD_DECL) +{ + const int shift_x = bitdepth - 8; + const int scaling_size = 1 << bitdepth; + const int max_value = points[num - 1][0] << shift_x; + av_assert0(scaling_size <= SCALING_SIZE); + + if (num == 0) { + memset(scaling, 0, scaling_size); + return; + } + + // Fill up the preceding entries with the initial value + memset(scaling, points[0][1], points[0][0] << shift_x); + + // Linearly interpolate the values in the middle + for (int i = 0; i < num - 1; i++) { + const int bx = points[i][0]; + const int by = points[i][1]; + const int ex = points[i+1][0]; + const int ey = points[i+1][1]; + const int dx = ex - bx; + const int dy = ey - by; + const int delta = dy * ((0x10000 + (dx >> 1)) / dx); + av_assert1(dx > 0); + for (int x = 0, d = 0x8000; x < dx; x++) { + scaling[(bx + x) << shift_x] = by + (d >> 16); + d += delta; + } + } + + // Fill up the remaining entries with the final value + memset(&scaling[max_value], points[num - 1][1], scaling_size - max_value); + +#if BIT_DEPTH != 8 + for (int i = 0; i < num - 1; i++) { + const int pad = 1 << shift_x, rnd = pad >> 1; + const int bx = points[i][0] << shift_x; + const int ex = points[i+1][0] << shift_x; + const int dx = ex - bx; + for (int x = 0; x < dx; x += pad) { + const int range = scaling[bx + x + pad] - scaling[bx + x]; + for (int n = 1, r = rnd; n < pad; n++) { + r += range; + scaling[bx + x + n] = scaling[bx + x] + (r >> shift_x); + } + } + } +#endif +} + +static av_always_inline void +FUNC(apply_grain_row)(AVFrame *out, const AVFrame *in, + const int ss_x, const int ss_y, + const uint8_t scaling[3][SCALING_SIZE], + const entry grain_lut[3][GRAIN_HEIGHT+1][GRAIN_WIDTH], + const AVFilmGrainParams *params, + const int row HBD_DECL) +{ + // Synthesize grain for the affected planes + const AVFilmGrainAOMParams *const data = ¶ms->codec.aom; + const int cpw = (out->width + ss_x) >> ss_x; + const int is_id = out->colorspace == AVCOL_SPC_RGB; + const int bh = (FFMIN(out->height - row * FG_BLOCK_SIZE, FG_BLOCK_SIZE) + ss_y) >> ss_y; + const ptrdiff_t uv_off = row * FG_BLOCK_SIZE * out->linesize[1] >> ss_y; + pixel *const luma_src = (pixel *) + ((char *) in->data[0] + row * FG_BLOCK_SIZE * in->linesize[0]); + + if (data->num_y_points) { + const int bh = FFMIN(out->height - row * FG_BLOCK_SIZE, FG_BLOCK_SIZE); + const ptrdiff_t off = row * FG_BLOCK_SIZE * out->linesize[0]; + FUNC(fgy_32x32xn_c)((pixel *) ((char *) out->data[0] + off), luma_src, + out->linesize[0], params, out->width, scaling[0], + grain_lut[0], bh, row HBD_CALL); + } + + if (!data->num_uv_points[0] && !data->num_uv_points[1] && + !data->chroma_scaling_from_luma) + { + return; + } + + // extend padding pixels + if (out->width & ss_x) { + pixel *ptr = luma_src; + for (int y = 0; y < bh; y++) { + ptr[out->width] = ptr[out->width - 1]; + ptr = (pixel *) ((char *) ptr + (in->linesize[0] << ss_y)); + } + } + + if (data->chroma_scaling_from_luma) { + for (int pl = 0; pl < 2; pl++) + FUNC(fguv_32x32xn_c)((pixel *) ((char *) out->data[1 + pl] + uv_off), + (const pixel *) ((const char *) in->data[1 + pl] + uv_off), + in->linesize[1], params, cpw, scaling[0], + grain_lut[1 + pl], bh, row, luma_src, + in->linesize[0], pl, is_id, ss_x, ss_y HBD_CALL); + } else { + for (int pl = 0; pl < 2; pl++) { + if (data->num_uv_points[pl]) { + FUNC(fguv_32x32xn_c)((pixel *) ((char *) out->data[1 + pl] + uv_off), + (const pixel *) ((const char *) in->data[1 + pl] + uv_off), + in->linesize[1], params, cpw, scaling[1 + pl], + grain_lut[1 + pl], bh, row, luma_src, + in->linesize[0], pl, is_id, ss_x, ss_y HBD_CALL); + } + } + } +} + +static int FUNC(apply_film_grain)(AVFrame *out_frame, const AVFrame *in_frame, + const AVFilmGrainParams *params HBD_DECL) +{ + entry grain_lut[3][GRAIN_HEIGHT + 1][GRAIN_WIDTH]; + uint8_t scaling[3][SCALING_SIZE]; + + const AVFilmGrainAOMParams *const data = ¶ms->codec.aom; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(out_frame->format); + const int rows = AV_CEIL_RSHIFT(out_frame->height, 5); /* log2(FG_BLOCK_SIZE) */ + const int subx = desc->log2_chroma_w, suby = desc->log2_chroma_h; + + // Generate grain LUTs as needed + FUNC(generate_grain_y_c)(grain_lut[0], params HBD_CALL); + if (data->num_uv_points[0] || data->chroma_scaling_from_luma) + FUNC(generate_grain_uv_c)(grain_lut[1], grain_lut[0], params, 0, subx, suby HBD_CALL); + if (data->num_uv_points[1] || data->chroma_scaling_from_luma) + FUNC(generate_grain_uv_c)(grain_lut[2], grain_lut[0], params, 1, subx, suby HBD_CALL); + + // Generate scaling LUTs as needed + if (data->num_y_points || data->chroma_scaling_from_luma) + FUNC(generate_scaling)(data->y_points, data->num_y_points, scaling[0] HBD_CALL); + if (data->num_uv_points[0]) + FUNC(generate_scaling)(data->uv_points[0], data->num_uv_points[0], scaling[1] HBD_CALL); + if (data->num_uv_points[1]) + FUNC(generate_scaling)(data->uv_points[1], data->num_uv_points[1], scaling[2] HBD_CALL); + + for (int row = 0; row < rows; row++) { + FUNC(apply_grain_row)(out_frame, in_frame, subx, suby, scaling, grain_lut, + params, row HBD_CALL); + } + + return 0; +} diff --git a/libavcodec/apac.c b/libavcodec/apac.c index 3408f75292d..b6cb6c669ec 100644 --- a/libavcodec/apac.c +++ b/libavcodec/apac.c @@ -269,8 +269,10 @@ const FFCodec ff_apac_decoder = { FF_CODEC_DECODE_CB(apac_decode), .close = apac_close, .p.capabilities = AV_CODEC_CAP_DELAY | - AV_CODEC_CAP_DR1 | - AV_CODEC_CAP_SUBFRAMES, +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_S16P, diff --git a/libavcodec/apedec.c b/libavcodec/apedec.c index aba63ce74bc..7d96182d0ca 100644 --- a/libavcodec/apedec.c +++ b/libavcodec/apedec.c @@ -171,6 +171,9 @@ typedef struct APEContext { int32_t *decoded_buffer; int decoded_size; int32_t *decoded[MAX_CHANNELS]; ///< decoded data for each channel + int32_t *interim_buffer; + int interim_size; + int32_t *interim[MAX_CHANNELS]; ///< decoded data for each channel int blocks_per_loop; ///< maximum number of samples to decode for each call int16_t* filterbuf[APE_FILTER_LEVELS]; ///< filter memory @@ -187,6 +190,7 @@ typedef struct APEContext { const uint8_t *ptr; ///< current position in frame data int error; + int interim_mode; void (*entropy_decode_mono)(struct APEContext *ctx, int blockstodecode); void (*entropy_decode_stereo)(struct APEContext *ctx, int blockstodecode); @@ -223,6 +227,7 @@ static av_cold int ape_decode_close(AVCodecContext *avctx) av_freep(&s->filterbuf[i]); av_freep(&s->decoded_buffer); + av_freep(&s->interim_buffer); av_freep(&s->data); s->decoded_size = s->data_size = 0; @@ -248,12 +253,15 @@ static av_cold int ape_decode_init(AVCodecContext *avctx) switch (s->bps) { case 8: avctx->sample_fmt = AV_SAMPLE_FMT_U8P; + s->interim_mode = 0; break; case 16: avctx->sample_fmt = AV_SAMPLE_FMT_S16P; + s->interim_mode = 0; break; case 24: avctx->sample_fmt = AV_SAMPLE_FMT_S32P; + s->interim_mode = -1; break; default: avpriv_request_sample(avctx, @@ -940,7 +948,7 @@ static void long_filter_high_3800(int32_t *buffer, int order, int shift, int len { int i, j; int32_t dotprod, sign; - int32_t coeffs[256], delay[256]; + int32_t coeffs[256], delay[256+256], *delayp = delay; if (order >= length) return; @@ -951,14 +959,28 @@ static void long_filter_high_3800(int32_t *buffer, int order, int shift, int len for (i = order; i < length; i++) { dotprod = 0; sign = APESIGN(buffer[i]); - for (j = 0; j < order; j++) { - dotprod += delay[j] * (unsigned)coeffs[j]; - coeffs[j] += ((delay[j] >> 31) | 1) * sign; + if (sign == 1) { + for (j = 0; j < order; j++) { + dotprod += delayp[j] * (unsigned)coeffs[j]; + coeffs[j] += (delayp[j] >> 31) | 1; + } + } else if (sign == -1) { + for (j = 0; j < order; j++) { + dotprod += delayp[j] * (unsigned)coeffs[j]; + coeffs[j] -= (delayp[j] >> 31) | 1; + } + } else { + for (j = 0; j < order; j++) { + dotprod += delayp[j] * (unsigned)coeffs[j]; + } } buffer[i] -= (unsigned)(dotprod >> shift); - for (j = 0; j < order - 1; j++) - delay[j] = delay[j + 1]; - delay[order - 1] = buffer[i]; + delayp ++; + delayp[order - 1] = buffer[i]; + if (delayp - delay == 256) { + memcpy(delay, delayp, sizeof(*delay)*256); + delayp = delay; + } } } @@ -1167,7 +1189,7 @@ static av_always_inline int predictor_update_filter(APEPredictor64 *p, const int decoded, const int filter, const int delayA, const int delayB, const int adaptA, const int adaptB, - int compression_level) + int interim_mode) { int64_t predictionA, predictionB; int32_t sign; @@ -1195,7 +1217,7 @@ static av_always_inline int predictor_update_filter(APEPredictor64 *p, p->buf[delayB - 3] * p->coeffsB[filter][3] + p->buf[delayB - 4] * p->coeffsB[filter][4]; - if (compression_level < COMPRESSION_LEVEL_INSANE) { + if (interim_mode < 1) { predictionA = (int32_t)predictionA; predictionB = (int32_t)predictionB; p->lastA[filter] = (int32_t)(decoded + (unsigned)((int32_t)(predictionA + (predictionB >> 1)) >> 10)); @@ -1220,33 +1242,74 @@ static av_always_inline int predictor_update_filter(APEPredictor64 *p, static void predictor_decode_stereo_3950(APEContext *ctx, int count) { - APEPredictor64 *p = &ctx->predictor64; - int32_t *decoded0 = ctx->decoded[0]; - int32_t *decoded1 = ctx->decoded[1]; + APEPredictor64 *p_default = &ctx->predictor64; + APEPredictor64 p_interim; + int lcount = count; + int num_passes = 1; ape_apply_filters(ctx, ctx->decoded[0], ctx->decoded[1], count); + if (ctx->interim_mode == -1) { + p_interim = *p_default; + num_passes ++; + memcpy(ctx->interim[0], ctx->decoded[0], sizeof(*ctx->interim[0])*count); + memcpy(ctx->interim[1], ctx->decoded[1], sizeof(*ctx->interim[1])*count); + } - while (count--) { - /* Predictor Y */ - *decoded0 = predictor_update_filter(p, *decoded0, 0, YDELAYA, YDELAYB, - YADAPTCOEFFSA, YADAPTCOEFFSB, - ctx->compression_level); - decoded0++; - *decoded1 = predictor_update_filter(p, *decoded1, 1, XDELAYA, XDELAYB, - XADAPTCOEFFSA, XADAPTCOEFFSB, - ctx->compression_level); - decoded1++; + for (int pass = 0; pass < num_passes; pass++) { + int32_t *decoded0, *decoded1; + int interim_mode = ctx->interim_mode > 0 || pass; + APEPredictor64 *p; - /* Combined */ - p->buf++; + if (pass) { + p = &p_interim; + decoded0 = ctx->interim[0]; + decoded1 = ctx->interim[1]; + } else { + p = p_default; + decoded0 = ctx->decoded[0]; + decoded1 = ctx->decoded[1]; + } + p->buf = p->historybuffer; + + count = lcount; + while (count--) { + /* Predictor Y */ + int32_t a0 = predictor_update_filter(p, *decoded0, 0, YDELAYA, YDELAYB, + YADAPTCOEFFSA, YADAPTCOEFFSB, + interim_mode); + int32_t a1 = predictor_update_filter(p, *decoded1, 1, XDELAYA, XDELAYB, + XADAPTCOEFFSA, XADAPTCOEFFSB, + interim_mode); + *decoded0++ = a0; + *decoded1++ = a1; + if (num_passes > 1) { + int32_t left = a1 - (unsigned)(a0 / 2); + int32_t right = left + (unsigned)a0; + + if (FFMIN(FFNABS(left), FFNABS(right)) < -(1<<23)) { + ctx->interim_mode = !interim_mode; + av_log(ctx->avctx, AV_LOG_VERBOSE, "Interim mode: %d\n", ctx->interim_mode); + break; + } + } - /* Have we filled the history buffer? */ - if (p->buf == p->historybuffer + HISTORY_SIZE) { - memmove(p->historybuffer, p->buf, - PREDICTOR_SIZE * sizeof(*p->historybuffer)); - p->buf = p->historybuffer; + /* Combined */ + p->buf++; + + /* Have we filled the history buffer? */ + if (p->buf == p->historybuffer + HISTORY_SIZE) { + memmove(p->historybuffer, p->buf, + PREDICTOR_SIZE * sizeof(*p->historybuffer)); + p->buf = p->historybuffer; + } } } + if (num_passes > 1 && ctx->interim_mode > 0) { + memcpy(ctx->decoded[0], ctx->interim[0], sizeof(*ctx->interim[0])*lcount); + memcpy(ctx->decoded[1], ctx->interim[1], sizeof(*ctx->interim[1])*lcount); + *p_default = p_interim; + p_default->buf = p_default->historybuffer; + } } static void predictor_decode_mono_3950(APEContext *ctx, int count) @@ -1576,6 +1639,19 @@ static int ape_decode_frame(AVCodecContext *avctx, AVFrame *frame, s->decoded[0] = s->decoded_buffer; s->decoded[1] = s->decoded_buffer + FFALIGN(blockstodecode, 8); + if (s->interim_mode < 0) { + av_fast_malloc(&s->interim_buffer, &s->interim_size, decoded_buffer_size); + if (!s->interim_buffer) + return AVERROR(ENOMEM); + memset(s->interim_buffer, 0, decoded_buffer_size); + s->interim[0] = s->interim_buffer; + s->interim[1] = s->interim_buffer + FFALIGN(blockstodecode, 8); + } else { + av_freep(&s->interim_buffer); + s->interim_size = 0; + memset(s->interim, 0, sizeof(s->interim)); + } + s->error=0; if ((s->channels == 1) || (s->frameflags & APE_FRAMECODE_PSEUDO_STEREO)) @@ -1661,8 +1737,8 @@ static void ape_flush(AVCodecContext *avctx) #define OFFSET(x) offsetof(APEContext, x) #define PAR (AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM) static const AVOption options[] = { - { "max_samples", "maximum number of samples decoded per call", OFFSET(blocks_per_loop), AV_OPT_TYPE_INT, { .i64 = 4608 }, 1, INT_MAX, PAR, "max_samples" }, - { "all", "no maximum. decode all samples for each packet at once", 0, AV_OPT_TYPE_CONST, { .i64 = INT_MAX }, INT_MIN, INT_MAX, PAR, "max_samples" }, + { "max_samples", "maximum number of samples decoded per call", OFFSET(blocks_per_loop), AV_OPT_TYPE_INT, { .i64 = 4608 }, 1, INT_MAX, PAR, .unit = "max_samples" }, + { "all", "no maximum. decode all samples for each packet at once", 0, AV_OPT_TYPE_CONST, { .i64 = INT_MAX }, INT_MIN, INT_MAX, PAR, .unit = "max_samples" }, { NULL}, }; @@ -1682,7 +1758,11 @@ const FFCodec ff_ape_decoder = { .init = ape_decode_init, .close = ape_decode_close, FF_CODEC_DECODE_CB(ape_decode_frame), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DELAY | + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .flush = ape_flush, diff --git a/libavcodec/aptxdec.c b/libavcodec/aptxdec.c index 3ae7a008034..4b083b65582 100644 --- a/libavcodec/aptxdec.c +++ b/libavcodec/aptxdec.c @@ -183,7 +183,6 @@ const FFCodec ff_aptx_decoder = { .init = ff_aptx_init, FF_CODEC_DECODE_CB(aptx_decode_frame), .p.capabilities = AV_CODEC_CAP_DR1, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_STEREO) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_STEREO, { 0 } }, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_NONE }, @@ -200,7 +199,6 @@ const FFCodec ff_aptx_hd_decoder = { .init = ff_aptx_init, FF_CODEC_DECODE_CB(aptx_decode_frame), .p.capabilities = AV_CODEC_CAP_DR1, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_STEREO) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_STEREO, { 0 } }, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_NONE }, diff --git a/libavcodec/aptxenc.c b/libavcodec/aptxenc.c index 6deebaf2cbd..5e041b9a1b3 100644 --- a/libavcodec/aptxenc.c +++ b/libavcodec/aptxenc.c @@ -276,7 +276,6 @@ const FFCodec ff_aptx_encoder = { .init = aptx_encode_init, FF_CODEC_ENCODE_CB(aptx_encode_frame), .close = aptx_close, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_STEREO) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_STEREO, { 0 } }, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_NONE }, @@ -295,7 +294,6 @@ const FFCodec ff_aptx_hd_encoder = { .init = aptx_encode_init, FF_CODEC_ENCODE_CB(aptx_encode_frame), .close = aptx_close, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_STEREO) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_STEREO, { 0 } }, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_NONE }, diff --git a/libavcodec/arbc.c b/libavcodec/arbc.c index 343c56695ea..46b0275e9a8 100644 --- a/libavcodec/arbc.c +++ b/libavcodec/arbc.c @@ -166,12 +166,14 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, prev_pixels -= fill_tile4(avctx, fill, frame); } - av_frame_unref(s->prev_frame); - if ((ret = av_frame_ref(s->prev_frame, frame)) < 0) + if ((ret = av_frame_replace(s->prev_frame, frame)) < 0) return ret; frame->pict_type = prev_pixels <= 0 ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; - frame->key_frame = prev_pixels <= 0; + if (prev_pixels <= 0) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/argo.c b/libavcodec/argo.c index 9bedb1394d0..589feed4101 100644 --- a/libavcodec/argo.c +++ b/libavcodec/argo.c @@ -666,7 +666,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; frame->pict_type = s->key ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; - frame->key_frame = s->key; + if (s->key) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/arm/Makefile b/libavcodec/arm/Makefile index 5d284bdc01f..becf316eb66 100644 --- a/libavcodec/arm/Makefile +++ b/libavcodec/arm/Makefile @@ -5,7 +5,6 @@ OBJS-$(CONFIG_AC3DSP) += arm/ac3dsp_init_arm.o \ arm/ac3dsp_arm.o OBJS-$(CONFIG_AUDIODSP) += arm/audiodsp_init_arm.o OBJS-$(CONFIG_BLOCKDSP) += arm/blockdsp_init_arm.o -OBJS-$(CONFIG_FFT) += arm/fft_init_arm.o OBJS-$(CONFIG_FMTCONVERT) += arm/fmtconvert_init_arm.o OBJS-$(CONFIG_G722DSP) += arm/g722dsp_init_arm.o OBJS-$(CONFIG_H264CHROMA) += arm/h264chroma_init_arm.o @@ -25,7 +24,6 @@ OBJS-$(CONFIG_MPEGVIDEO) += arm/mpegvideo_arm.o OBJS-$(CONFIG_MPEGVIDEOENC) += arm/mpegvideoencdsp_init_arm.o OBJS-$(CONFIG_NEON_CLOBBER_TEST) += arm/neontest.o OBJS-$(CONFIG_PIXBLOCKDSP) += arm/pixblockdsp_init_arm.o -OBJS-$(CONFIG_RDFT) += arm/rdft_init_arm.o OBJS-$(CONFIG_RV34DSP) += arm/rv34dsp_init_arm.o OBJS-$(CONFIG_VC1DSP) += arm/vc1dsp_init_arm.o OBJS-$(CONFIG_VIDEODSP) += arm/videodsp_init_arm.o @@ -90,9 +88,7 @@ ARMV6-OBJS-$(CONFIG_TRUEHD_DECODER) += arm/mlpdsp_armv6.o # VFP optimizations # subsystems -VFP-OBJS-$(CONFIG_FFT) += arm/fft_vfp.o VFP-OBJS-$(CONFIG_FMTCONVERT) += arm/fmtconvert_vfp.o -VFP-OBJS-$(CONFIG_MDCT) += arm/mdct_vfp.o # decoders/encoders VFP-OBJS-$(CONFIG_DCA_DECODER) += arm/synth_filter_vfp.o @@ -107,7 +103,6 @@ NEON-OBJS-$(CONFIG_AUDIODSP) += arm/audiodsp_init_neon.o \ arm/int_neon.o NEON-OBJS-$(CONFIG_BLOCKDSP) += arm/blockdsp_init_neon.o \ arm/blockdsp_neon.o -NEON-OBJS-$(CONFIG_FFT) += arm/fft_neon.o NEON-OBJS-$(CONFIG_FMTCONVERT) += arm/fmtconvert_neon.o NEON-OBJS-$(CONFIG_G722DSP) += arm/g722dsp_neon.o NEON-OBJS-$(CONFIG_H264CHROMA) += arm/h264cmc_neon.o @@ -121,10 +116,8 @@ NEON-OBJS-$(CONFIG_HPELDSP) += arm/hpeldsp_init_neon.o \ NEON-OBJS-$(CONFIG_IDCTDSP) += arm/idctdsp_init_neon.o \ arm/idctdsp_neon.o \ arm/simple_idct_neon.o -NEON-OBJS-$(CONFIG_MDCT) += arm/mdct_neon.o NEON-OBJS-$(CONFIG_MPEGVIDEO) += arm/mpegvideo_neon.o NEON-OBJS-$(CONFIG_PIXBLOCKDSP) += arm/pixblockdsp_neon.o -NEON-OBJS-$(CONFIG_RDFT) += arm/rdft_neon.o NEON-OBJS-$(CONFIG_VC1DSP) += arm/vc1dsp_init_neon.o \ arm/vc1dsp_neon.o NEON-OBJS-$(CONFIG_VP3DSP) += arm/vp3dsp_neon.o diff --git a/libavcodec/arm/ac3dsp_init_arm.c b/libavcodec/arm/ac3dsp_init_arm.c index a64aa6ae822..ae989069c98 100644 --- a/libavcodec/arm/ac3dsp_init_arm.c +++ b/libavcodec/arm/ac3dsp_init_arm.c @@ -26,7 +26,7 @@ #include "config.h" void ff_ac3_exponent_min_neon(uint8_t *exp, int num_reuse_blocks, int nb_coefs); -void ff_float_to_fixed24_neon(int32_t *dst, const float *src, unsigned int len); +void ff_float_to_fixed24_neon(int32_t *dst, const float *src, size_t len); void ff_ac3_extract_exponents_neon(uint8_t *exp, int32_t *coef, int nb_coefs); void ff_ac3_sum_square_butterfly_int32_neon(int64_t sum[4], const int32_t *coef0, diff --git a/libavcodec/arm/fft_init_arm.c b/libavcodec/arm/fft_init_arm.c deleted file mode 100644 index 8ae22dfb4e7..00000000000 --- a/libavcodec/arm/fft_init_arm.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2009 Mans Rullgard - * - * 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/attributes.h" -#include "libavutil/cpu.h" -#include "libavutil/arm/cpu.h" - -#include "libavcodec/fft.h" - -void ff_fft_calc_vfp(FFTContext *s, FFTComplex *z); - -void ff_fft_permute_neon(FFTContext *s, FFTComplex *z); -void ff_fft_calc_neon(FFTContext *s, FFTComplex *z); - -void ff_imdct_half_vfp(FFTContext *s, FFTSample *output, const FFTSample *input); - -void ff_imdct_calc_neon(FFTContext *s, FFTSample *output, const FFTSample *input); -void ff_imdct_half_neon(FFTContext *s, FFTSample *output, const FFTSample *input); -void ff_mdct_calc_neon(FFTContext *s, FFTSample *output, const FFTSample *input); - -av_cold void ff_fft_init_arm(FFTContext *s) -{ - int cpu_flags = av_get_cpu_flags(); - - if (have_vfp_vm(cpu_flags)) { - s->fft_calc = ff_fft_calc_vfp; -#if CONFIG_MDCT - s->imdct_half = ff_imdct_half_vfp; -#endif - } - - if (have_neon(cpu_flags)) { -#if CONFIG_FFT - if (s->nbits < 17) { - s->fft_permute = ff_fft_permute_neon; - s->fft_calc = ff_fft_calc_neon; - } -#endif -#if CONFIG_MDCT - s->imdct_calc = ff_imdct_calc_neon; - s->imdct_half = ff_imdct_half_neon; - s->mdct_calc = ff_mdct_calc_neon; - s->mdct_permutation = FF_MDCT_PERM_INTERLEAVE; -#endif - } -} diff --git a/libavcodec/arm/fft_neon.S b/libavcodec/arm/fft_neon.S deleted file mode 100644 index 48f8dfc424c..00000000000 --- a/libavcodec/arm/fft_neon.S +++ /dev/null @@ -1,375 +0,0 @@ -/* - * ARM NEON optimised FFT - * - * Copyright (c) 2009 Mans Rullgard - * Copyright (c) 2009 Naotoshi Nojiri - * - * This algorithm (though not any of the implementation details) is - * based on libdjbfft by D. J. Bernstein. - * - * 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/arm/asm.S" - -#define M_SQRT1_2 0.70710678118654752440 - - -function fft4_neon - vld1.32 {d0-d3}, [r0,:128] - - vext.32 q8, q1, q1, #1 @ i2,r3 d3=i3,r2 - vsub.f32 d6, d0, d1 @ r0-r1,i0-i1 - vsub.f32 d7, d16, d17 @ r3-r2,i2-i3 - vadd.f32 d4, d0, d1 @ r0+r1,i0+i1 - vadd.f32 d5, d2, d3 @ i2+i3,r2+r3 - vadd.f32 d1, d6, d7 - vsub.f32 d3, d6, d7 - vadd.f32 d0, d4, d5 - vsub.f32 d2, d4, d5 - - vst1.32 {d0-d3}, [r0,:128] - - bx lr -endfunc - -function fft8_neon - mov r1, r0 - vld1.32 {d0-d3}, [r1,:128]! - vld1.32 {d16-d19}, [r1,:128] - - movw r2, #0x04f3 @ sqrt(1/2) - movt r2, #0x3f35 - eor r3, r2, #1<<31 - vdup.32 d31, r2 - - vext.32 q11, q1, q1, #1 @ i2,r3,i3,r2 - vadd.f32 d4, d16, d17 @ r4+r5,i4+i5 - vmov d28, r3, r2 - vadd.f32 d5, d18, d19 @ r6+r7,i6+i7 - vsub.f32 d17, d16, d17 @ r4-r5,i4-i5 - vsub.f32 d19, d18, d19 @ r6-r7,i6-i7 - vrev64.32 d29, d28 - vadd.f32 d20, d0, d1 @ r0+r1,i0+i1 - vadd.f32 d21, d2, d3 @ r2+r3,i2+i3 - vmul.f32 d26, d17, d28 @ -a2r*w,a2i*w - vext.32 q3, q2, q2, #1 - vmul.f32 d27, d19, d29 @ a3r*w,-a3i*w - vsub.f32 d23, d22, d23 @ i2-i3,r3-r2 - vsub.f32 d22, d0, d1 @ r0-r1,i0-i1 - vmul.f32 d24, d17, d31 @ a2r*w,a2i*w - vmul.f32 d25, d19, d31 @ a3r*w,a3i*w - vadd.f32 d0, d20, d21 - vsub.f32 d2, d20, d21 - vadd.f32 d1, d22, d23 - vrev64.32 q13, q13 - vsub.f32 d3, d22, d23 - vsub.f32 d6, d6, d7 - vadd.f32 d24, d24, d26 @ a2r+a2i,a2i-a2r t1,t2 - vadd.f32 d25, d25, d27 @ a3r-a3i,a3i+a3r t5,t6 - vadd.f32 d7, d4, d5 - vsub.f32 d18, d2, d6 - vext.32 q13, q12, q12, #1 - vadd.f32 d2, d2, d6 - vsub.f32 d16, d0, d7 - vadd.f32 d5, d25, d24 - vsub.f32 d4, d26, d27 - vadd.f32 d0, d0, d7 - vsub.f32 d17, d1, d5 - vsub.f32 d19, d3, d4 - vadd.f32 d3, d3, d4 - vadd.f32 d1, d1, d5 - - vst1.32 {d16-d19}, [r1,:128] - vst1.32 {d0-d3}, [r0,:128] - - bx lr -endfunc - -function fft16_neon - movrel r1, mppm - vld1.32 {d16-d19}, [r0,:128]! @ q8{r0,i0,r1,i1} q9{r2,i2,r3,i3} - pld [r0, #32] - vld1.32 {d2-d3}, [r1,:128] - vext.32 q13, q9, q9, #1 - vld1.32 {d22-d25}, [r0,:128]! @ q11{r4,i4,r5,i5} q12{r6,i5,r7,i7} - vadd.f32 d4, d16, d17 - vsub.f32 d5, d16, d17 - vadd.f32 d18, d18, d19 - vsub.f32 d19, d26, d27 - - vadd.f32 d20, d22, d23 - vsub.f32 d22, d22, d23 - vsub.f32 d23, d24, d25 - vadd.f32 q8, q2, q9 @ {r0,i0,r1,i1} - vadd.f32 d21, d24, d25 - vmul.f32 d24, d22, d2 - vsub.f32 q9, q2, q9 @ {r2,i2,r3,i3} - vmul.f32 d25, d23, d3 - vuzp.32 d16, d17 @ {r0,r1,i0,i1} - vmul.f32 q1, q11, d2[1] - vuzp.32 d18, d19 @ {r2,r3,i2,i3} - vrev64.32 q12, q12 - vadd.f32 q11, q12, q1 @ {t1a,t2a,t5,t6} - vld1.32 {d24-d27}, [r0,:128]! @ q12{r8,i8,r9,i9} q13{r10,i10,r11,i11} - vzip.32 q10, q11 - vld1.32 {d28-d31}, [r0,:128] @ q14{r12,i12,r13,i13} q15{r14,i14,r15,i15} - vadd.f32 d0, d22, d20 - vadd.f32 d1, d21, d23 - vsub.f32 d2, d21, d23 - vsub.f32 d3, d22, d20 - sub r0, r0, #96 - vext.32 q13, q13, q13, #1 - vsub.f32 q10, q8, q0 @ {r4,r5,i4,i5} - vadd.f32 q8, q8, q0 @ {r0,r1,i0,i1} - vext.32 q15, q15, q15, #1 - vsub.f32 q11, q9, q1 @ {r6,r7,i6,i7} - vswp d25, d26 @ q12{r8,i8,i10,r11} q13{r9,i9,i11,r10} - vadd.f32 q9, q9, q1 @ {r2,r3,i2,i3} - vswp d29, d30 @ q14{r12,i12,i14,r15} q15{r13,i13,i15,r14} - vadd.f32 q0, q12, q13 @ {t1,t2,t5,t6} - vadd.f32 q1, q14, q15 @ {t1a,t2a,t5a,t6a} - movrelx r2, X(ff_cos_16) - vsub.f32 q13, q12, q13 @ {t3,t4,t7,t8} - vrev64.32 d1, d1 - vsub.f32 q15, q14, q15 @ {t3a,t4a,t7a,t8a} - vrev64.32 d3, d3 - movrel r3, pmmp - vswp d1, d26 @ q0{t1,t2,t3,t4} q13{t6,t5,t7,t8} - vswp d3, d30 @ q1{t1a,t2a,t3a,t4a} q15{t6a,t5a,t7a,t8a} - vadd.f32 q12, q0, q13 @ {r8,i8,r9,i9} - vadd.f32 q14, q1, q15 @ {r12,i12,r13,i13} - vld1.32 {d4-d5}, [r2,:64] - vsub.f32 q13, q0, q13 @ {r10,i10,r11,i11} - vsub.f32 q15, q1, q15 @ {r14,i14,r15,i15} - vswp d25, d28 @ q12{r8,i8,r12,i12} q14{r9,i9,r13,i13} - vld1.32 {d6-d7}, [r3,:128] - vrev64.32 q1, q14 - vmul.f32 q14, q14, d4[1] - vmul.f32 q1, q1, q3 - vmla.f32 q14, q1, d5[1] @ {t1a,t2a,t5a,t6a} - vswp d27, d30 @ q13{r10,i10,r14,i14} q15{r11,i11,r15,i15} - vzip.32 q12, q14 - vadd.f32 d0, d28, d24 - vadd.f32 d1, d25, d29 - vsub.f32 d2, d25, d29 - vsub.f32 d3, d28, d24 - vsub.f32 q12, q8, q0 @ {r8,r9,i8,i9} - vadd.f32 q8, q8, q0 @ {r0,r1,i0,i1} - vsub.f32 q14, q10, q1 @ {r12,r13,i12,i13} - mov r1, #32 - vadd.f32 q10, q10, q1 @ {r4,r5,i4,i5} - vrev64.32 q0, q13 - vmul.f32 q13, q13, d5[0] - vrev64.32 q1, q15 - vmul.f32 q15, q15, d5[1] - vst2.32 {d16-d17},[r0,:128], r1 - vmul.f32 q0, q0, q3 - vst2.32 {d20-d21},[r0,:128], r1 - vmul.f32 q1, q1, q3 - vmla.f32 q13, q0, d5[0] @ {t1,t2,t5,t6} - vmla.f32 q15, q1, d4[1] @ {t1a,t2a,t5a,t6a} - vst2.32 {d24-d25},[r0,:128], r1 - vst2.32 {d28-d29},[r0,:128] - vzip.32 q13, q15 - sub r0, r0, #80 - vadd.f32 d0, d30, d26 - vadd.f32 d1, d27, d31 - vsub.f32 d2, d27, d31 - vsub.f32 d3, d30, d26 - vsub.f32 q13, q9, q0 @ {r10,r11,i10,i11} - vadd.f32 q9, q9, q0 @ {r2,r3,i2,i3} - vsub.f32 q15, q11, q1 @ {r14,r15,i14,i15} - vadd.f32 q11, q11, q1 @ {r6,r7,i6,i7} - vst2.32 {d18-d19},[r0,:128], r1 - vst2.32 {d22-d23},[r0,:128], r1 - vst2.32 {d26-d27},[r0,:128], r1 - vst2.32 {d30-d31},[r0,:128] - bx lr -endfunc - -function fft_pass_neon - push {r4-r6,lr} - mov r6, r2 @ n - lsl r5, r2, #3 @ 2 * n * sizeof FFTSample - lsl r4, r2, #4 @ 2 * n * sizeof FFTComplex - lsl r2, r2, #5 @ 4 * n * sizeof FFTComplex - add r3, r2, r4 - add r4, r4, r0 @ &z[o1] - add r2, r2, r0 @ &z[o2] - add r3, r3, r0 @ &z[o3] - vld1.32 {d20-d21},[r2,:128] @ {z[o2],z[o2+1]} - movrel r12, pmmp - vld1.32 {d22-d23},[r3,:128] @ {z[o3],z[o3+1]} - add r5, r5, r1 @ wim - vld1.32 {d6-d7}, [r12,:128] @ pmmp - vswp d21, d22 - vld1.32 {d4}, [r1,:64]! @ {wre[0],wre[1]} - sub r5, r5, #4 @ wim-- - vrev64.32 q1, q11 - vmul.f32 q11, q11, d4[1] - vmul.f32 q1, q1, q3 - vld1.32 {d5[0]}, [r5,:32] @ d5[0] = wim[-1] - vmla.f32 q11, q1, d5[0] @ {t1a,t2a,t5a,t6a} - vld2.32 {d16-d17},[r0,:128] @ {z[0],z[1]} - sub r6, r6, #1 @ n-- - vld2.32 {d18-d19},[r4,:128] @ {z[o1],z[o1+1]} - vzip.32 q10, q11 - vadd.f32 d0, d22, d20 - vadd.f32 d1, d21, d23 - vsub.f32 d2, d21, d23 - vsub.f32 d3, d22, d20 - vsub.f32 q10, q8, q0 - vadd.f32 q8, q8, q0 - vsub.f32 q11, q9, q1 - vadd.f32 q9, q9, q1 - vst2.32 {d20-d21},[r2,:128]! @ {z[o2],z[o2+1]} - vst2.32 {d16-d17},[r0,:128]! @ {z[0],z[1]} - vst2.32 {d22-d23},[r3,:128]! @ {z[o3],z[o3+1]} - vst2.32 {d18-d19},[r4,:128]! @ {z[o1],z[o1+1]} - sub r5, r5, #8 @ wim -= 2 -1: - vld1.32 {d20-d21},[r2,:128] @ {z[o2],z[o2+1]} - vld1.32 {d22-d23},[r3,:128] @ {z[o3],z[o3+1]} - vswp d21, d22 - vld1.32 {d4}, [r1]! @ {wre[0],wre[1]} - vrev64.32 q0, q10 - vmul.f32 q10, q10, d4[0] - vrev64.32 q1, q11 - vmul.f32 q11, q11, d4[1] - vld1.32 {d5}, [r5] @ {wim[-1],wim[0]} - vmul.f32 q0, q0, q3 - sub r5, r5, #8 @ wim -= 2 - vmul.f32 q1, q1, q3 - vmla.f32 q10, q0, d5[1] @ {t1,t2,t5,t6} - vmla.f32 q11, q1, d5[0] @ {t1a,t2a,t5a,t6a} - vld2.32 {d16-d17},[r0,:128] @ {z[0],z[1]} - subs r6, r6, #1 @ n-- - vld2.32 {d18-d19},[r4,:128] @ {z[o1],z[o1+1]} - vzip.32 q10, q11 - vadd.f32 d0, d22, d20 - vadd.f32 d1, d21, d23 - vsub.f32 d2, d21, d23 - vsub.f32 d3, d22, d20 - vsub.f32 q10, q8, q0 - vadd.f32 q8, q8, q0 - vsub.f32 q11, q9, q1 - vadd.f32 q9, q9, q1 - vst2.32 {d20-d21}, [r2,:128]! @ {z[o2],z[o2+1]} - vst2.32 {d16-d17}, [r0,:128]! @ {z[0],z[1]} - vst2.32 {d22-d23}, [r3,:128]! @ {z[o3],z[o3+1]} - vst2.32 {d18-d19}, [r4,:128]! @ {z[o1],z[o1+1]} - bne 1b - - pop {r4-r6,pc} -endfunc - -.macro def_fft n, n2, n4 - .align 6 -function fft\n\()_neon - push {r4, lr} - mov r4, r0 - bl fft\n2\()_neon - add r0, r4, #\n4*2*8 - bl fft\n4\()_neon - add r0, r4, #\n4*3*8 - bl fft\n4\()_neon - mov r0, r4 - pop {r4, lr} - movrelx r1, X(ff_cos_\n) - mov r2, #\n4/2 - b fft_pass_neon -endfunc -.endm - - def_fft 32, 16, 8 - def_fft 64, 32, 16 - def_fft 128, 64, 32 - def_fft 256, 128, 64 - def_fft 512, 256, 128 - def_fft 1024, 512, 256 - def_fft 2048, 1024, 512 - def_fft 4096, 2048, 1024 - def_fft 8192, 4096, 2048 - def_fft 16384, 8192, 4096 - def_fft 32768, 16384, 8192 - def_fft 65536, 32768, 16384 - -function ff_fft_calc_neon, export=1 - ldr r2, [r0] - sub r2, r2, #2 - movrel r3, fft_tab_neon - ldr r3, [r3, r2, lsl #2] - mov r0, r1 - bx r3 -endfunc - -function ff_fft_permute_neon, export=1 - push {r4,lr} - mov r12, #1 - ldr r2, [r0] @ nbits - ldr r3, [r0, #12] @ tmp_buf - ldr r0, [r0, #8] @ revtab - lsl r12, r12, r2 - mov r2, r12 -1: - vld1.32 {d0-d1}, [r1,:128]! - ldr r4, [r0], #4 - uxth lr, r4 - uxth r4, r4, ror #16 - add lr, r3, lr, lsl #3 - add r4, r3, r4, lsl #3 - vst1.32 {d0}, [lr,:64] - vst1.32 {d1}, [r4,:64] - subs r12, r12, #2 - bgt 1b - - sub r1, r1, r2, lsl #3 -1: - vld1.32 {d0-d3}, [r3,:128]! - vst1.32 {d0-d3}, [r1,:128]! - subs r2, r2, #4 - bgt 1b - - pop {r4,pc} -endfunc - -const fft_tab_neon, relocate=1 - .word fft4_neon - .word fft8_neon - .word fft16_neon - .word fft32_neon - .word fft64_neon - .word fft128_neon - .word fft256_neon - .word fft512_neon - .word fft1024_neon - .word fft2048_neon - .word fft4096_neon - .word fft8192_neon - .word fft16384_neon - .word fft32768_neon - .word fft65536_neon -endconst - -const pmmp, align=4 - .float +1.0, -1.0, -1.0, +1.0 -endconst - -const mppm, align=4 - .float -M_SQRT1_2, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2 -endconst diff --git a/libavcodec/arm/fft_vfp.S b/libavcodec/arm/fft_vfp.S deleted file mode 100644 index ac601325f25..00000000000 --- a/libavcodec/arm/fft_vfp.S +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright (c) 2013 RISC OS Open Ltd - * Author: Ben Avison - * - * 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/arm/asm.S" - -@ The fftx_internal_vfp versions of the functions obey a modified AAPCS: -@ VFP is in RunFast mode, vector length 4, stride 1 thoroughout, and -@ all single-precision VFP registers may be corrupted on exit. The a2 -@ register may not be clobbered in these functions, as it holds the -@ stored original FPSCR. - -function ff_fft_calc_vfp, export=1 - ldr ip, [a1, #0] @ nbits - mov a1, a2 - movrel a2, (fft_tab_vfp - 8) - ldr pc, [a2, ip, lsl #2] -endfunc -const fft_tab_vfp, relocate=1 - .word fft4_vfp - .word fft8_vfp - .word X(ff_fft16_vfp) @ this one alone is exported - .word fft32_vfp - .word fft64_vfp - .word fft128_vfp - .word fft256_vfp - .word fft512_vfp - .word fft1024_vfp - .word fft2048_vfp - .word fft4096_vfp - .word fft8192_vfp - .word fft16384_vfp - .word fft32768_vfp - .word fft65536_vfp -endconst - -function fft4_vfp - vldr d0, [a1, #0*2*4] @ s0,s1 = z[0] - vldr d4, [a1, #1*2*4] @ s8,s9 = z[1] - vldr d1, [a1, #2*2*4] @ s2,s3 = z[2] - vldr d5, [a1, #3*2*4] @ s10,s11 = z[3] - @ stall - vadd.f s12, s0, s8 @ i0 - vadd.f s13, s1, s9 @ i1 - vadd.f s14, s2, s10 @ i2 - vadd.f s15, s3, s11 @ i3 - vsub.f s8, s0, s8 @ i4 - vsub.f s9, s1, s9 @ i5 - vsub.f s10, s2, s10 @ i6 - vsub.f s11, s3, s11 @ i7 - @ stall - @ stall - vadd.f s0, s12, s14 @ z[0].re - vsub.f s4, s12, s14 @ z[2].re - vadd.f s1, s13, s15 @ z[0].im - vsub.f s5, s13, s15 @ z[2].im - vadd.f s7, s9, s10 @ z[3].im - vsub.f s3, s9, s10 @ z[1].im - vadd.f s2, s8, s11 @ z[1].re - vsub.f s6, s8, s11 @ z[3].re - @ stall - @ stall - vstr d0, [a1, #0*2*4] - vstr d2, [a1, #2*2*4] - @ stall - @ stall - vstr d1, [a1, #1*2*4] - vstr d3, [a1, #3*2*4] - - bx lr -endfunc - -.macro macro_fft8_head - @ FFT4 - vldr d4, [a1, #0 * 2*4] - vldr d6, [a1, #1 * 2*4] - vldr d5, [a1, #2 * 2*4] - vldr d7, [a1, #3 * 2*4] - @ BF - vldr d12, [a1, #4 * 2*4] - vadd.f s16, s8, s12 @ vector op - vldr d14, [a1, #5 * 2*4] - vldr d13, [a1, #6 * 2*4] - vldr d15, [a1, #7 * 2*4] - vsub.f s20, s8, s12 @ vector op - vadd.f s0, s16, s18 - vsub.f s2, s16, s18 - vadd.f s1, s17, s19 - vsub.f s3, s17, s19 - vadd.f s7, s21, s22 - vsub.f s5, s21, s22 - vadd.f s4, s20, s23 - vsub.f s6, s20, s23 - vsub.f s20, s24, s28 @ vector op - vstr d0, [a1, #0 * 2*4] @ transfer s0-s7 to s24-s31 via memory - vstr d1, [a1, #1 * 2*4] - vldr s0, cos1pi4 - vadd.f s16, s24, s28 @ vector op - vstr d2, [a1, #2 * 2*4] - vstr d3, [a1, #3 * 2*4] - vldr d12, [a1, #0 * 2*4] - @ TRANSFORM - vmul.f s20, s20, s0 @ vector x scalar op - vldr d13, [a1, #1 * 2*4] - vldr d14, [a1, #2 * 2*4] - vldr d15, [a1, #3 * 2*4] - @ BUTTERFLIES - vadd.f s0, s18, s16 - vadd.f s1, s17, s19 - vsub.f s2, s17, s19 - vsub.f s3, s18, s16 - vadd.f s4, s21, s20 - vsub.f s5, s21, s20 - vadd.f s6, s22, s23 - vsub.f s7, s22, s23 - vadd.f s8, s0, s24 @ vector op - vstr d0, [a1, #0 * 2*4] @ transfer s0-s3 to s12-s15 via memory - vstr d1, [a1, #1 * 2*4] - vldr d6, [a1, #0 * 2*4] - vldr d7, [a1, #1 * 2*4] - vadd.f s1, s5, s6 - vadd.f s0, s7, s4 - vsub.f s2, s5, s6 - vsub.f s3, s7, s4 - vsub.f s12, s24, s12 @ vector op - vsub.f s5, s29, s1 - vsub.f s4, s28, s0 - vsub.f s6, s30, s2 - vsub.f s7, s31, s3 - vadd.f s16, s0, s28 @ vector op - vstr d6, [a1, #4 * 2*4] - vstr d7, [a1, #6 * 2*4] - vstr d4, [a1, #0 * 2*4] - vstr d5, [a1, #2 * 2*4] - vstr d2, [a1, #5 * 2*4] - vstr d3, [a1, #7 * 2*4] -.endm - -.macro macro_fft8_tail - vstr d8, [a1, #1 * 2*4] - vstr d9, [a1, #3 * 2*4] -.endm - -function .Lfft8_internal_vfp - macro_fft8_head - macro_fft8_tail - bx lr -endfunc - -function fft8_vfp - ldr a3, =0x03030000 @ RunFast mode, vector length 4, stride 1 - fmrx a2, FPSCR - fmxr FPSCR, a3 - vpush {s16-s31} - mov ip, lr - bl .Lfft8_internal_vfp - vpop {s16-s31} - fmxr FPSCR, a2 - bx ip -endfunc - -.align 3 -cos1pi4: @ cos(1*pi/4) = sqrt(2) - .float 0.707106769084930419921875 -cos1pi8: @ cos(1*pi/8) = sqrt(2+sqrt(2))/2 - .float 0.92387950420379638671875 -cos3pi8: @ cos(2*pi/8) = sqrt(2-sqrt(2))/2 - .float 0.3826834261417388916015625 - -function .Lfft16_internal_vfp - macro_fft8_head - @ FFT4(z+8) - vldr d10, [a1, #8 * 2*4] - vldr d12, [a1, #9 * 2*4] - vldr d11, [a1, #10 * 2*4] - vldr d13, [a1, #11 * 2*4] - macro_fft8_tail - vadd.f s16, s20, s24 @ vector op - @ FFT4(z+12) - vldr d4, [a1, #12 * 2*4] - vldr d6, [a1, #13 * 2*4] - vldr d5, [a1, #14 * 2*4] - vsub.f s20, s20, s24 @ vector op - vldr d7, [a1, #15 * 2*4] - vadd.f s0, s16, s18 - vsub.f s4, s16, s18 - vadd.f s1, s17, s19 - vsub.f s5, s17, s19 - vadd.f s7, s21, s22 - vsub.f s3, s21, s22 - vadd.f s2, s20, s23 - vsub.f s6, s20, s23 - vadd.f s16, s8, s12 @ vector op - vstr d0, [a1, #8 * 2*4] - vstr d2, [a1, #10 * 2*4] - vstr d1, [a1, #9 * 2*4] - vsub.f s20, s8, s12 - vstr d3, [a1, #11 * 2*4] - @ TRANSFORM(z[2],z[6],z[10],z[14],cos1pi4,cos1pi4) - vldr d12, [a1, #10 * 2*4] - vadd.f s0, s16, s18 - vadd.f s1, s17, s19 - vsub.f s6, s16, s18 - vsub.f s7, s17, s19 - vsub.f s3, s21, s22 - vadd.f s2, s20, s23 - vadd.f s5, s21, s22 - vsub.f s4, s20, s23 - vstr d0, [a1, #12 * 2*4] - vmov s0, s6 - @ TRANSFORM(z[1],z[5],z[9],z[13],cos1pi8,cos3pi8) - vldr d6, [a1, #9 * 2*4] - vstr d1, [a1, #13 * 2*4] - vldr d1, cos1pi4 @ s2 = cos1pi4, s3 = cos1pi8 - vstr d2, [a1, #15 * 2*4] - vldr d7, [a1, #13 * 2*4] - vadd.f s4, s25, s24 - vsub.f s5, s25, s24 - vsub.f s6, s0, s7 - vadd.f s7, s0, s7 - vmul.f s20, s12, s3 @ vector op - @ TRANSFORM(z[3],z[7],z[11],z[15],cos3pi8,cos1pi8) - vldr d4, [a1, #11 * 2*4] - vldr d5, [a1, #15 * 2*4] - vldr s1, cos3pi8 - vmul.f s24, s4, s2 @ vector * scalar op - vmul.f s28, s12, s1 @ vector * scalar op - vmul.f s12, s8, s1 @ vector * scalar op - vadd.f s4, s20, s29 - vsub.f s5, s21, s28 - vsub.f s6, s22, s31 - vadd.f s7, s23, s30 - vmul.f s8, s8, s3 @ vector * scalar op - vldr d8, [a1, #1 * 2*4] - vldr d9, [a1, #5 * 2*4] - vldr d10, [a1, #3 * 2*4] - vldr d11, [a1, #7 * 2*4] - vldr d14, [a1, #2 * 2*4] - vadd.f s0, s6, s4 - vadd.f s1, s5, s7 - vsub.f s2, s5, s7 - vsub.f s3, s6, s4 - vadd.f s4, s12, s9 - vsub.f s5, s13, s8 - vsub.f s6, s14, s11 - vadd.f s7, s15, s10 - vadd.f s12, s0, s16 @ vector op - vstr d0, [a1, #1 * 2*4] - vstr d1, [a1, #5 * 2*4] - vldr d4, [a1, #1 * 2*4] - vldr d5, [a1, #5 * 2*4] - vadd.f s0, s6, s4 - vadd.f s1, s5, s7 - vsub.f s2, s5, s7 - vsub.f s3, s6, s4 - vsub.f s8, s16, s8 @ vector op - vstr d6, [a1, #1 * 2*4] - vstr d7, [a1, #5 * 2*4] - vldr d15, [a1, #6 * 2*4] - vsub.f s4, s20, s0 - vsub.f s5, s21, s1 - vsub.f s6, s22, s2 - vsub.f s7, s23, s3 - vadd.f s20, s0, s20 @ vector op - vstr d4, [a1, #9 * 2*4] - @ TRANSFORM_ZERO(z[0],z[4],z[8],z[12]) - vldr d6, [a1, #8 * 2*4] - vstr d5, [a1, #13 * 2*4] - vldr d7, [a1, #12 * 2*4] - vstr d2, [a1, #11 * 2*4] - vldr d8, [a1, #0 * 2*4] - vstr d3, [a1, #15 * 2*4] - vldr d9, [a1, #4 * 2*4] - vadd.f s0, s26, s24 - vadd.f s1, s25, s27 - vsub.f s2, s25, s27 - vsub.f s3, s26, s24 - vadd.f s4, s14, s12 - vadd.f s5, s13, s15 - vsub.f s6, s13, s15 - vsub.f s7, s14, s12 - vadd.f s8, s0, s28 @ vector op - vstr d0, [a1, #3 * 2*4] - vstr d1, [a1, #7 * 2*4] - vldr d6, [a1, #3 * 2*4] - vldr d7, [a1, #7 * 2*4] - vsub.f s0, s16, s4 - vsub.f s1, s17, s5 - vsub.f s2, s18, s6 - vsub.f s3, s19, s7 - vsub.f s12, s28, s12 @ vector op - vadd.f s16, s4, s16 @ vector op - vstr d10, [a1, #3 * 2*4] - vstr d11, [a1, #7 * 2*4] - vstr d4, [a1, #2 * 2*4] - vstr d5, [a1, #6 * 2*4] - vstr d0, [a1, #8 * 2*4] - vstr d1, [a1, #12 * 2*4] - vstr d6, [a1, #10 * 2*4] - vstr d7, [a1, #14 * 2*4] - vstr d8, [a1, #0 * 2*4] - vstr d9, [a1, #4 * 2*4] - - bx lr -endfunc - -function ff_fft16_vfp, export=1 - ldr a3, =0x03030000 @ RunFast mode, vector length 4, stride 1 - fmrx a2, FPSCR - fmxr FPSCR, a3 - vpush {s16-s31} - mov ip, lr - bl .Lfft16_internal_vfp - vpop {s16-s31} - fmxr FPSCR, a2 - bx ip -endfunc - -.macro pass n, z0, z1, z2, z3 - add v6, v5, #4*2*\n - @ TRANSFORM_ZERO(z[0],z[o1],z[o2],z[o3]) - @ TRANSFORM(z[1],z[o1+1],z[o2+1],z[o3+1],wre[1],wim[-1]) - @ TRANSFORM(z[0],z[o1],z[o2],z[o3],wre[0],wim[0]) - @ TRANSFORM(z[1],z[o1+1],z[o2+1],z[o3+1],wre[1],wim[-1]) - vldr d8, [\z2, #8*(o2+1)] @ s16,s17 - vldmdb v6!, {s2} - vldr d9, [\z3, #8*(o3+1)] @ s18,s19 - vldmia v5!, {s0,s1} @ s0 is unused - vldr s7, [\z2, #8*o2] @ t1 - vmul.f s20, s16, s2 @ vector * scalar - vldr s0, [\z3, #8*o3] @ t5 - vldr s6, [\z2, #8*o2+4] @ t2 - vldr s3, [\z3, #8*o3+4] @ t6 - vmul.f s16, s16, s1 @ vector * scalar - ldr a4, =\n-1 -1: add \z0, \z0, #8*2 - .if \n*4*2 >= 512 - add \z1, \z1, #8*2 - .endif - .if \n*4*2 >= 256 - add \z2, \z2, #8*2 - .endif - .if \n*4*2 >= 512 - add \z3, \z3, #8*2 - .endif - @ up to 2 stalls (VFP vector issuing / waiting for s0) - @ depending upon whether this is the first iteration and - @ how many add instructions are inserted above - vadd.f s4, s0, s7 @ t5 - vadd.f s5, s6, s3 @ t6 - vsub.f s6, s6, s3 @ t4 - vsub.f s7, s0, s7 @ t3 - vldr d6, [\z0, #8*0-8*2] @ s12,s13 - vadd.f s0, s16, s21 @ t1 - vldr d7, [\z1, #8*o1-8*2] @ s14,s15 - vsub.f s1, s18, s23 @ t5 - vadd.f s8, s4, s12 @ vector + vector - @ stall (VFP vector issuing) - @ stall (VFP vector issuing) - @ stall (VFP vector issuing) - vsub.f s4, s12, s4 - vsub.f s5, s13, s5 - vsub.f s6, s14, s6 - vsub.f s7, s15, s7 - vsub.f s2, s17, s20 @ t2 - vadd.f s3, s19, s22 @ t6 - vstr d4, [\z0, #8*0-8*2] @ s8,s9 - vstr d5, [\z1, #8*o1-8*2] @ s10,s11 - @ stall (waiting for s5) - vstr d2, [\z2, #8*o2-8*2] @ s4,s5 - vadd.f s4, s1, s0 @ t5 - vstr d3, [\z3, #8*o3-8*2] @ s6,s7 - vsub.f s7, s1, s0 @ t3 - vadd.f s5, s2, s3 @ t6 - vsub.f s6, s2, s3 @ t4 - vldr d6, [\z0, #8*1-8*2] @ s12,s13 - vldr d7, [\z1, #8*(o1+1)-8*2] @ s14,s15 - vldr d4, [\z2, #8*o2] @ s8,s9 - vldmdb v6!, {s2,s3} - vldr d5, [\z3, #8*o3] @ s10,s11 - vadd.f s20, s4, s12 @ vector + vector - vldmia v5!, {s0,s1} - vldr d8, [\z2, #8*(o2+1)] @ s16,s17 - @ stall (VFP vector issuing) - vsub.f s4, s12, s4 - vsub.f s5, s13, s5 - vsub.f s6, s14, s6 - vsub.f s7, s15, s7 - vmul.f s12, s8, s3 @ vector * scalar - vstr d10, [\z0, #8*1-8*2] @ s20,s21 - vldr d9, [\z3, #8*(o3+1)] @ s18,s19 - vstr d11, [\z1, #8*(o1+1)-8*2] @ s22,s23 - vmul.f s8, s8, s0 @ vector * scalar - vstr d2, [\z2, #8*(o2+1)-8*2] @ s4,s5 - @ stall (waiting for s7) - vstr d3, [\z3, #8*(o3+1)-8*2] @ s6,s7 - vmul.f s20, s16, s2 @ vector * scalar - @ stall (VFP vector issuing) - @ stall (VFP vector issuing) - @ stall (VFP vector issuing) - vadd.f s7, s8, s13 @ t1 - vsub.f s6, s9, s12 @ t2 - vsub.f s0, s10, s15 @ t5 - vadd.f s3, s11, s14 @ t6 - vmul.f s16, s16, s1 @ vector * scalar - subs a4, a4, #1 - bne 1b - @ What remains is identical to the first two indentations of - @ the above, but without the increment of z - vadd.f s4, s0, s7 @ t5 - vadd.f s5, s6, s3 @ t6 - vsub.f s6, s6, s3 @ t4 - vsub.f s7, s0, s7 @ t3 - vldr d6, [\z0, #8*0] @ s12,s13 - vadd.f s0, s16, s21 @ t1 - vldr d7, [\z1, #8*o1] @ s14,s15 - vsub.f s1, s18, s23 @ t5 - vadd.f s8, s4, s12 @ vector + vector - vsub.f s4, s12, s4 - vsub.f s5, s13, s5 - vsub.f s6, s14, s6 - vsub.f s7, s15, s7 - vsub.f s2, s17, s20 @ t2 - vadd.f s3, s19, s22 @ t6 - vstr d4, [\z0, #8*0] @ s8,s9 - vstr d5, [\z1, #8*o1] @ s10,s11 - vstr d2, [\z2, #8*o2] @ s4,s5 - vadd.f s4, s1, s0 @ t5 - vstr d3, [\z3, #8*o3] @ s6,s7 - vsub.f s7, s1, s0 @ t3 - vadd.f s5, s2, s3 @ t6 - vsub.f s6, s2, s3 @ t4 - vldr d6, [\z0, #8*1] @ s12,s13 - vldr d7, [\z1, #8*(o1+1)] @ s14,s15 - vadd.f s20, s4, s12 @ vector + vector - vsub.f s4, s12, s4 - vsub.f s5, s13, s5 - vsub.f s6, s14, s6 - vsub.f s7, s15, s7 - vstr d10, [\z0, #8*1] @ s20,s21 - vstr d11, [\z1, #8*(o1+1)] @ s22,s23 - vstr d2, [\z2, #8*(o2+1)] @ s4,s5 - vstr d3, [\z3, #8*(o3+1)] @ s6,s7 -.endm - -.macro def_fft n, n2, n4 -function .Lfft\n\()_internal_vfp - .if \n >= 512 - push {v1-v6,lr} - .elseif \n >= 256 - push {v1-v2,v5-v6,lr} - .else - push {v1,v5-v6,lr} - .endif - mov v1, a1 - bl .Lfft\n2\()_internal_vfp - add a1, v1, #8*(\n/4)*2 - bl .Lfft\n4\()_internal_vfp - movrelx v5, X(ff_cos_\n), a1 - add a1, v1, #8*(\n/4)*3 - bl .Lfft\n4\()_internal_vfp - .if \n >= 512 - .set o1, 0*(\n/4/2) - .set o2, 0*(\n/4/2) - .set o3, 0*(\n/4/2) - add v2, v1, #8*2*(\n/4/2) - add v3, v1, #8*4*(\n/4/2) - add v4, v1, #8*6*(\n/4/2) - pass (\n/4/2), v1, v2, v3, v4 - pop {v1-v6,pc} - .elseif \n >= 256 - .set o1, 2*(\n/4/2) - .set o2, 0*(\n/4/2) - .set o3, 2*(\n/4/2) - add v2, v1, #8*4*(\n/4/2) - pass (\n/4/2), v1, v1, v2, v2 - pop {v1-v2,v5-v6,pc} - .else - .set o1, 2*(\n/4/2) - .set o2, 4*(\n/4/2) - .set o3, 6*(\n/4/2) - pass (\n/4/2), v1, v1, v1, v1 - pop {v1,v5-v6,pc} - .endif -endfunc - -function fft\n\()_vfp - ldr a3, =0x03030000 /* RunFast mode, vector length 4, stride 1 */ - fmrx a2, FPSCR - fmxr FPSCR, a3 - vpush {s16-s31} - mov ip, lr - bl .Lfft\n\()_internal_vfp - vpop {s16-s31} - fmxr FPSCR, a2 - bx ip -endfunc - -.ltorg -.endm - - def_fft 32, 16, 8 - def_fft 64, 32, 16 - def_fft 128, 64, 32 - def_fft 256, 128, 64 - def_fft 512, 256, 128 - def_fft 1024, 512, 256 - def_fft 2048, 1024, 512 - def_fft 4096, 2048, 1024 - def_fft 8192, 4096, 2048 - def_fft 16384, 8192, 4096 - def_fft 32768, 16384, 8192 - def_fft 65536, 32768, 16384 diff --git a/libavcodec/arm/hevcdsp_idct_neon.S b/libavcodec/arm/hevcdsp_idct_neon.S index 75795e6a6ae..66ed1c67852 100644 --- a/libavcodec/arm/hevcdsp_idct_neon.S +++ b/libavcodec/arm/hevcdsp_idct_neon.S @@ -876,48 +876,28 @@ function func_tr_32x4_\name movrel r9, trans + 32 vld1.s16 {q0}, [r9, :128]! vld1.s16 {q1}, [r9, :128] - - bl tr_block1 - add r4, sp, #2048 - vld1.s16 {q14-q15}, [r4, :128]! - butterfly32 q14, q10, q15, q11 - scale32 d22, d23, d20, d21, q1, q14, q10, q15, \shift - - vld1.s16 {q14-q15}, [r4, :128]! - butterfly32 q14, q12, q15, q13 - scale32 d2, d3, d28, d29, q1, q14, q12, q15, \shift - - transpose8_4x4 d22, d20, d2, d28 - transpose8_4x4 d29, d3, d21, d23 - mov r1, r11 mov r2, #64 mov r8, #-64 - add r3, r11, #(56 + 3 * 64) - store16 d22, d23, d20, d21, d2, d3, d28, d29, r8 - @ reload multiplication coefficiens to q1 - vld1.s16 {q1}, [r9, :128] + bl tr_block1 + mov r1, r11 + add r3, r11, #(56 + 3 * 64) + scale_store \shift bl tr_block2 add r1, r11, #8 add r3, r11, #(48 + 3 * 64) - mov r2, #64 - mov r8, #-64 scale_store \shift bl tr_block3 add r1, r11, #16 add r3, r11, #(40 + 3 * 64) - mov r2, #64 - mov r8, #-64 scale_store \shift bl tr_block4 add r1, r11, #24 add r3, r11, #(32 + 3 * 64) - mov r2, #64 - mov r8, #-64 scale_store \shift bx r10 diff --git a/libavcodec/arm/hevcdsp_init_neon.c b/libavcodec/arm/hevcdsp_init_neon.c index 8094e6c62e2..1f26fc64541 100644 --- a/libavcodec/arm/hevcdsp_init_neon.c +++ b/libavcodec/arm/hevcdsp_init_neon.c @@ -119,7 +119,7 @@ QPEL_FUNC(ff_hevc_put_qpel_h3v3_neon_8); #define QPEL_FUNC_UW_PIX(name) \ void name(uint8_t *dst, ptrdiff_t dststride, const uint8_t *_src, ptrdiff_t _srcstride, \ - int height, intptr_t mx, intptr_t my, int width); + int height, intptr_t mx, intptr_t my, int width) QPEL_FUNC_UW_PIX(ff_hevc_put_qpel_uw_pixels_w4_neon_8); QPEL_FUNC_UW_PIX(ff_hevc_put_qpel_uw_pixels_w8_neon_8); QPEL_FUNC_UW_PIX(ff_hevc_put_qpel_uw_pixels_w16_neon_8); @@ -131,7 +131,7 @@ QPEL_FUNC_UW_PIX(ff_hevc_put_qpel_uw_pixels_w64_neon_8); #define QPEL_FUNC_UW(name) \ void name(uint8_t *dst, ptrdiff_t dststride, const uint8_t *_src, ptrdiff_t _srcstride, \ - int width, int height, const int16_t* src2, ptrdiff_t src2stride); + int width, int height, const int16_t* src2, ptrdiff_t src2stride) QPEL_FUNC_UW(ff_hevc_put_qpel_uw_pixels_neon_8); QPEL_FUNC_UW(ff_hevc_put_qpel_uw_v1_neon_8); QPEL_FUNC_UW(ff_hevc_put_qpel_uw_v2_neon_8); diff --git a/libavcodec/arm/mdct_neon.S b/libavcodec/arm/mdct_neon.S deleted file mode 100644 index a6952fa5716..00000000000 --- a/libavcodec/arm/mdct_neon.S +++ /dev/null @@ -1,301 +0,0 @@ -/* - * ARM NEON optimised MDCT - * Copyright (c) 2009 Mans Rullgard - * - * 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/arm/asm.S" - -#define ff_fft_calc_neon X(ff_fft_calc_neon) - -function ff_imdct_half_neon, export=1 - push {r4-r8,lr} - - mov r12, #1 - ldr lr, [r0, #20] @ mdct_bits - ldr r4, [r0, #24] @ tcos - ldr r3, [r0, #8] @ revtab - lsl r12, r12, lr @ n = 1 << nbits - lsr lr, r12, #2 @ n4 = n >> 2 - add r7, r2, r12, lsl #1 - mov r12, #-16 - sub r7, r7, #16 - - vld2.32 {d16-d17},[r7,:128],r12 @ d16=x,n1 d17=x,n0 - vld2.32 {d0-d1}, [r2,:128]! @ d0 =m0,x d1 =m1,x - vrev64.32 d17, d17 - vld2.32 {d2,d3}, [r4,:128]! @ d2=c0,c1 d3=s0,s2 - vmul.f32 d6, d17, d2 - vmul.f32 d7, d0, d2 -1: - subs lr, lr, #2 - ldr r6, [r3], #4 - vmul.f32 d4, d0, d3 - vmul.f32 d5, d17, d3 - vsub.f32 d4, d6, d4 - vadd.f32 d5, d5, d7 - uxth r8, r6, ror #16 - uxth r6, r6 - add r8, r1, r8, lsl #3 - add r6, r1, r6, lsl #3 - beq 1f - vld2.32 {d16-d17},[r7,:128],r12 - vld2.32 {d0-d1}, [r2,:128]! - vrev64.32 d17, d17 - vld2.32 {d2,d3}, [r4,:128]! @ d2=c0,c1 d3=s0,s2 - vmul.f32 d6, d17, d2 - vmul.f32 d7, d0, d2 - vst2.32 {d4[0],d5[0]}, [r6,:64] - vst2.32 {d4[1],d5[1]}, [r8,:64] - b 1b -1: - vst2.32 {d4[0],d5[0]}, [r6,:64] - vst2.32 {d4[1],d5[1]}, [r8,:64] - - mov r4, r0 - mov r6, r1 - bl ff_fft_calc_neon - - mov r12, #1 - ldr lr, [r4, #20] @ mdct_bits - ldr r4, [r4, #24] @ tcos - lsl r12, r12, lr @ n = 1 << nbits - lsr lr, r12, #3 @ n8 = n >> 3 - - add r4, r4, lr, lsl #3 - add r6, r6, lr, lsl #3 - sub r1, r4, #16 - sub r3, r6, #16 - - mov r7, #-16 - mov r8, r6 - mov r0, r3 - - vld2.32 {d0-d1}, [r3,:128], r7 @ d0 =i1,r1 d1 =i0,r0 - vld2.32 {d20-d21},[r6,:128]! @ d20=i2,r2 d21=i3,r3 - vld2.32 {d16,d18},[r1,:128], r7 @ d16=c1,c0 d18=s1,s0 -1: - subs lr, lr, #2 - vmul.f32 d7, d0, d18 - vld2.32 {d17,d19},[r4,:128]! @ d17=c2,c3 d19=s2,s3 - vmul.f32 d4, d1, d18 - vmul.f32 d5, d21, d19 - vmul.f32 d6, d20, d19 - vmul.f32 d22, d1, d16 - vmul.f32 d23, d21, d17 - vmul.f32 d24, d0, d16 - vmul.f32 d25, d20, d17 - vadd.f32 d7, d7, d22 - vadd.f32 d6, d6, d23 - vsub.f32 d4, d4, d24 - vsub.f32 d5, d5, d25 - beq 1f - vld2.32 {d0-d1}, [r3,:128], r7 - vld2.32 {d20-d21},[r6,:128]! - vld2.32 {d16,d18},[r1,:128], r7 @ d16=c1,c0 d18=s1,s0 - vrev64.32 q3, q3 - vst2.32 {d4,d6}, [r0,:128], r7 - vst2.32 {d5,d7}, [r8,:128]! - b 1b -1: - vrev64.32 q3, q3 - vst2.32 {d4,d6}, [r0,:128] - vst2.32 {d5,d7}, [r8,:128] - - pop {r4-r8,pc} -endfunc - -function ff_imdct_calc_neon, export=1 - push {r4-r6,lr} - - ldr r3, [r0, #20] - mov r4, #1 - mov r5, r1 - lsl r4, r4, r3 - add r1, r1, r4 - - bl X(ff_imdct_half_neon) - - add r0, r5, r4, lsl #2 - add r1, r5, r4, lsl #1 - sub r0, r0, #8 - sub r2, r1, #16 - mov r3, #-16 - mov r6, #-8 - vmov.i32 d30, #1<<31 -1: - vld1.32 {d0-d1}, [r2,:128], r3 - pld [r0, #-16] - vrev64.32 q0, q0 - vld1.32 {d2-d3}, [r1,:128]! - veor d4, d1, d30 - pld [r2, #-16] - vrev64.32 q1, q1 - veor d5, d0, d30 - vst1.32 {d2}, [r0,:64], r6 - vst1.32 {d3}, [r0,:64], r6 - vst1.32 {d4-d5}, [r5,:128]! - subs r4, r4, #16 - bgt 1b - - pop {r4-r6,pc} -endfunc - -function ff_mdct_calc_neon, export=1 - push {r4-r10,lr} - - mov r12, #1 - ldr lr, [r0, #20] @ mdct_bits - ldr r4, [r0, #24] @ tcos - ldr r3, [r0, #8] @ revtab - lsl lr, r12, lr @ n = 1 << nbits - add r7, r2, lr @ in4u - sub r9, r7, #16 @ in4d - add r2, r7, lr, lsl #1 @ in3u - add r8, r9, lr, lsl #1 @ in3d - add r5, r4, lr, lsl #1 - sub r5, r5, #16 - sub r3, r3, #4 - mov r12, #-16 - - vld2.32 {d16,d18},[r9,:128],r12 @ in0u0,in0u1 in4d1,in4d0 - vld2.32 {d17,d19},[r8,:128],r12 @ in2u0,in2u1 in3d1,in3d0 - vld2.32 {d0, d2}, [r7,:128]! @ in4u0,in4u1 in2d1,in2d0 - vrev64.32 q9, q9 @ in4d0,in4d1 in3d0,in3d1 - vld2.32 {d1, d3}, [r2,:128]! @ in3u0,in3u1 in1d1,in1d0 - vsub.f32 d0, d18, d0 @ in4d-in4u I - vld2.32 {d20,d21},[r4,:128]! @ c0,c1 s0,s1 - vrev64.32 q1, q1 @ in2d0,in2d1 in1d0,in1d1 - vld2.32 {d30,d31},[r5,:128],r12 @ c2,c3 s2,s3 - vadd.f32 d1, d1, d19 @ in3u+in3d -R - vsub.f32 d16, d16, d2 @ in0u-in2d R - vadd.f32 d17, d17, d3 @ in2u+in1d -I -1: - vmul.f32 d7, d0, d21 @ I*s -A ldr r10, [r3, lr, lsr #1] -T lsr r10, lr, #1 -T ldr r10, [r3, r10] - vmul.f32 d6, d1, d20 @ -R*c - ldr r6, [r3, #4]! - vmul.f32 d4, d1, d21 @ -R*s - vmul.f32 d5, d0, d20 @ I*c - vmul.f32 d24, d16, d30 @ R*c - vmul.f32 d25, d17, d31 @ -I*s - vmul.f32 d22, d16, d31 @ R*s - vmul.f32 d23, d17, d30 @ I*c - subs lr, lr, #16 - vsub.f32 d6, d6, d7 @ -R*c-I*s - vadd.f32 d7, d4, d5 @ -R*s+I*c - vsub.f32 d24, d25, d24 @ I*s-R*c - vadd.f32 d25, d22, d23 @ R*s-I*c - beq 1f - mov r12, #-16 - vld2.32 {d16,d18},[r9,:128],r12 @ in0u0,in0u1 in4d1,in4d0 - vld2.32 {d17,d19},[r8,:128],r12 @ in2u0,in2u1 in3d1,in3d0 - vneg.f32 d7, d7 @ R*s-I*c - vld2.32 {d0, d2}, [r7,:128]! @ in4u0,in4u1 in2d1,in2d0 - vrev64.32 q9, q9 @ in4d0,in4d1 in3d0,in3d1 - vld2.32 {d1, d3}, [r2,:128]! @ in3u0,in3u1 in1d1,in1d0 - vsub.f32 d0, d18, d0 @ in4d-in4u I - vld2.32 {d20,d21},[r4,:128]! @ c0,c1 s0,s1 - vrev64.32 q1, q1 @ in2d0,in2d1 in1d0,in1d1 - vld2.32 {d30,d31},[r5,:128],r12 @ c2,c3 s2,s3 - vadd.f32 d1, d1, d19 @ in3u+in3d -R - vsub.f32 d16, d16, d2 @ in0u-in2d R - vadd.f32 d17, d17, d3 @ in2u+in1d -I - uxth r12, r6, ror #16 - uxth r6, r6 - add r12, r1, r12, lsl #3 - add r6, r1, r6, lsl #3 - vst2.32 {d6[0],d7[0]}, [r6,:64] - vst2.32 {d6[1],d7[1]}, [r12,:64] - uxth r6, r10, ror #16 - uxth r10, r10 - add r6 , r1, r6, lsl #3 - add r10, r1, r10, lsl #3 - vst2.32 {d24[0],d25[0]},[r10,:64] - vst2.32 {d24[1],d25[1]},[r6,:64] - b 1b -1: - vneg.f32 d7, d7 @ R*s-I*c - uxth r12, r6, ror #16 - uxth r6, r6 - add r12, r1, r12, lsl #3 - add r6, r1, r6, lsl #3 - vst2.32 {d6[0],d7[0]}, [r6,:64] - vst2.32 {d6[1],d7[1]}, [r12,:64] - uxth r6, r10, ror #16 - uxth r10, r10 - add r6 , r1, r6, lsl #3 - add r10, r1, r10, lsl #3 - vst2.32 {d24[0],d25[0]},[r10,:64] - vst2.32 {d24[1],d25[1]},[r6,:64] - - mov r4, r0 - mov r6, r1 - bl ff_fft_calc_neon - - mov r12, #1 - ldr lr, [r4, #20] @ mdct_bits - ldr r4, [r4, #24] @ tcos - lsl r12, r12, lr @ n = 1 << nbits - lsr lr, r12, #3 @ n8 = n >> 3 - - add r4, r4, lr, lsl #3 - add r6, r6, lr, lsl #3 - sub r1, r4, #16 - sub r3, r6, #16 - - mov r7, #-16 - mov r8, r6 - mov r0, r3 - - vld2.32 {d0-d1}, [r3,:128], r7 @ d0 =r1,i1 d1 =r0,i0 - vld2.32 {d20-d21},[r6,:128]! @ d20=r2,i2 d21=r3,i3 - vld2.32 {d16,d18},[r1,:128], r7 @ c1,c0 s1,s0 -1: - subs lr, lr, #2 - vmul.f32 d7, d0, d18 @ r1*s1,r0*s0 - vld2.32 {d17,d19},[r4,:128]! @ c2,c3 s2,s3 - vmul.f32 d4, d1, d18 @ i1*s1,i0*s0 - vmul.f32 d5, d21, d19 @ i2*s2,i3*s3 - vmul.f32 d6, d20, d19 @ r2*s2,r3*s3 - vmul.f32 d24, d0, d16 @ r1*c1,r0*c0 - vmul.f32 d25, d20, d17 @ r2*c2,r3*c3 - vmul.f32 d22, d21, d17 @ i2*c2,i3*c3 - vmul.f32 d23, d1, d16 @ i1*c1,i0*c0 - vadd.f32 d4, d4, d24 @ i1*s1+r1*c1,i0*s0+r0*c0 - vadd.f32 d5, d5, d25 @ i2*s2+r2*c2,i3*s3+r3*c3 - vsub.f32 d6, d22, d6 @ i2*c2-r2*s2,i3*c3-r3*s3 - vsub.f32 d7, d23, d7 @ i1*c1-r1*s1,i0*c0-r0*s0 - vneg.f32 q2, q2 - beq 1f - vld2.32 {d0-d1}, [r3,:128], r7 - vld2.32 {d20-d21},[r6,:128]! - vld2.32 {d16,d18},[r1,:128], r7 @ c1,c0 s1,s0 - vrev64.32 q3, q3 - vst2.32 {d4,d6}, [r0,:128], r7 - vst2.32 {d5,d7}, [r8,:128]! - b 1b -1: - vrev64.32 q3, q3 - vst2.32 {d4,d6}, [r0,:128] - vst2.32 {d5,d7}, [r8,:128] - - pop {r4-r10,pc} -endfunc diff --git a/libavcodec/arm/mdct_vfp.S b/libavcodec/arm/mdct_vfp.S deleted file mode 100644 index 43f6d14c0c1..00000000000 --- a/libavcodec/arm/mdct_vfp.S +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (c) 2013 RISC OS Open Ltd - * Author: Ben Avison - * - * 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/arm/asm.S" - -CONTEXT .req a1 -ORIGOUT .req a2 -IN .req a3 -OUT .req v1 -REVTAB .req v2 -TCOS .req v3 -TSIN .req v4 -OLDFPSCR .req v5 -J0 .req a2 -J1 .req a4 -J2 .req ip -J3 .req lr -REVTAB_HI .req v5 -IN_HI .req v6 -OUT_HI .req v6 -TCOS_HI .req sl -TSIN_HI .req fp - -.macro prerotation_innerloop - .set trig_lo, k - .set trig_hi, n4 - k - 2 - .set in_lo, trig_lo * 2 - .set in_hi, trig_hi * 2 - vldr d8, [TCOS, #trig_lo*4] @ s16,s17 - vldr d9, [TCOS, #trig_hi*4] @ s18,s19 - vldr s0, [IN, #in_hi*4 + 12] - vldr s1, [IN, #in_hi*4 + 4] - vldr s2, [IN, #in_lo*4 + 12] - vldr s3, [IN, #in_lo*4 + 4] - vmul.f s8, s0, s16 @ vector operation - vldr d10, [TSIN, #trig_lo*4] @ s20,s21 - vldr d11, [TSIN, #trig_hi*4] @ s22,s23 - vldr s4, [IN, #in_lo*4] - vldr s5, [IN, #in_lo*4 + 8] - vldr s6, [IN, #in_hi*4] - vldr s7, [IN, #in_hi*4 + 8] - ldr J0, [REVTAB, #trig_lo*2] - vmul.f s12, s0, s20 @ vector operation - ldr J2, [REVTAB, #trig_hi*2] - mov J1, J0, lsr #16 - and J0, J0, #255 @ halfword value will be < n4 - vmls.f s8, s4, s20 @ vector operation - mov J3, J2, lsr #16 - and J2, J2, #255 @ halfword value will be < n4 - add J0, OUT, J0, lsl #3 - vmla.f s12, s4, s16 @ vector operation - add J1, OUT, J1, lsl #3 - add J2, OUT, J2, lsl #3 - add J3, OUT, J3, lsl #3 - vstr s8, [J0] - vstr s9, [J1] - vstr s10, [J2] - vstr s11, [J3] - vstr s12, [J0, #4] - vstr s13, [J1, #4] - vstr s14, [J2, #4] - vstr s15, [J3, #4] - .set k, k + 2 -.endm - -.macro prerotation_innerloop_rolled - vldmia TCOS!, {s16,s17} - vldmdb TCOS_HI!, {s18,s19} - vldr s0, [IN_HI, #-4] - vldr s1, [IN_HI, #-12] - vldr s2, [IN, #12] - vldr s3, [IN, #4] - vmul.f s8, s0, s16 @ vector operation - vldmia TSIN!, {s20,s21} - vldmdb TSIN_HI!, {s22,s23} - vldr s4, [IN] - vldr s5, [IN, #8] - vldr s6, [IN_HI, #-16] - vldr s7, [IN_HI, #-8] - vmul.f s12, s0, s20 @ vector operation - add IN, IN, #16 - sub IN_HI, IN_HI, #16 - ldrh J0, [REVTAB], #2 - ldrh J1, [REVTAB], #2 - vmls.f s8, s4, s20 @ vector operation - ldrh J3, [REVTAB_HI, #-2]! - ldrh J2, [REVTAB_HI, #-2]! - add J0, OUT, J0, lsl #3 - vmla.f s12, s4, s16 @ vector operation - add J1, OUT, J1, lsl #3 - add J2, OUT, J2, lsl #3 - add J3, OUT, J3, lsl #3 - vstr s8, [J0] - vstr s9, [J1] - vstr s10, [J2] - vstr s11, [J3] - vstr s12, [J0, #4] - vstr s13, [J1, #4] - vstr s14, [J2, #4] - vstr s15, [J3, #4] -.endm - -.macro postrotation_innerloop tail, head - .set trig_lo_head, n8 - k - 2 - .set trig_hi_head, n8 + k - .set out_lo_head, trig_lo_head * 2 - .set out_hi_head, trig_hi_head * 2 - .set trig_lo_tail, n8 - (k - 2) - 2 - .set trig_hi_tail, n8 + (k - 2) - .set out_lo_tail, trig_lo_tail * 2 - .set out_hi_tail, trig_hi_tail * 2 - .if (k & 2) == 0 - TCOS_D0_HEAD .req d10 @ s20,s21 - TCOS_D1_HEAD .req d11 @ s22,s23 - TCOS_S0_TAIL .req s24 - .else - TCOS_D0_HEAD .req d12 @ s24,s25 - TCOS_D1_HEAD .req d13 @ s26,s27 - TCOS_S0_TAIL .req s20 - .endif - .ifnc "\tail","" - vmls.f s8, s0, TCOS_S0_TAIL @ vector operation - .endif - .ifnc "\head","" - vldr d8, [TSIN, #trig_lo_head*4] @ s16,s17 - vldr d9, [TSIN, #trig_hi_head*4] @ s18,s19 - vldr TCOS_D0_HEAD, [TCOS, #trig_lo_head*4] - .endif - .ifnc "\tail","" - vmla.f s12, s4, TCOS_S0_TAIL @ vector operation - .endif - .ifnc "\head","" - vldr s0, [OUT, #out_lo_head*4] - vldr s1, [OUT, #out_lo_head*4 + 8] - vldr s2, [OUT, #out_hi_head*4] - vldr s3, [OUT, #out_hi_head*4 + 8] - vldr s4, [OUT, #out_lo_head*4 + 4] - vldr s5, [OUT, #out_lo_head*4 + 12] - vldr s6, [OUT, #out_hi_head*4 + 4] - vldr s7, [OUT, #out_hi_head*4 + 12] - .endif - .ifnc "\tail","" - vstr s8, [OUT, #out_lo_tail*4] - vstr s9, [OUT, #out_lo_tail*4 + 8] - vstr s10, [OUT, #out_hi_tail*4] - vstr s11, [OUT, #out_hi_tail*4 + 8] - .endif - .ifnc "\head","" - vmul.f s8, s4, s16 @ vector operation - .endif - .ifnc "\tail","" - vstr s12, [OUT, #out_hi_tail*4 + 12] - vstr s13, [OUT, #out_hi_tail*4 + 4] - vstr s14, [OUT, #out_lo_tail*4 + 12] - vstr s15, [OUT, #out_lo_tail*4 + 4] - .endif - .ifnc "\head","" - vmul.f s12, s0, s16 @ vector operation - vldr TCOS_D1_HEAD, [TCOS, #trig_hi_head*4] - .endif - .unreq TCOS_D0_HEAD - .unreq TCOS_D1_HEAD - .unreq TCOS_S0_TAIL - .ifnc "\head","" - .set k, k + 2 - .endif -.endm - -.macro postrotation_innerloop_rolled tail, head, tcos_s0_head, tcos_s1_head, tcos_s2_head, tcos_s3_head, tcos_s0_tail, out_offset_head, out_offset_tail - .ifnc "\tail","" - vmls.f s8, s0, \tcos_s0_tail @ vector operation - .endif - .ifnc "\head","" - vldmia TSIN!, {s16,s17} - vldmdb TSIN_HI!, {s18,s19} - vldmia TCOS!, {\tcos_s0_head,\tcos_s1_head} - .endif - .ifnc "\tail","" - vmla.f s12, s4, \tcos_s0_tail @ vector operation - .endif - .ifnc "\head","" - vldr s0, [OUT, #+\out_offset_head+0] - vldr s1, [OUT, #+\out_offset_head+8] - vldr s2, [OUT_HI, #-\out_offset_head-16] - vldr s3, [OUT_HI, #-\out_offset_head-8] - vldr s4, [OUT, #+\out_offset_head+4] - vldr s5, [OUT, #+\out_offset_head+12] - vldr s6, [OUT_HI, #-\out_offset_head-12] - vldr s7, [OUT_HI, #-\out_offset_head-4] - .endif - .ifnc "\tail","" - vstr s8, [OUT, #+\out_offset_tail+0] - vstr s9, [OUT, #+\out_offset_tail+8] - vstr s10, [OUT_HI, #-\out_offset_tail-16] - vstr s11, [OUT_HI, #-\out_offset_tail-8] - .endif - .ifnc "\head","" - vmul.f s8, s4, s16 @ vector operation - .endif - .ifnc "\tail","" - vstr s12, [OUT_HI, #-\out_offset_tail-4] - vstr s13, [OUT_HI, #-\out_offset_tail-12] - vstr s14, [OUT, #+\out_offset_tail+12] - vstr s15, [OUT, #+\out_offset_tail+4] - .endif - .ifnc "\head","" - vmul.f s12, s0, s16 @ vector operation - vldmdb TCOS_HI!, {\tcos_s2_head,\tcos_s3_head} - .endif -.endm - - -/* void ff_imdct_half_vfp(FFTContext *s, - * FFTSample *output, - * const FFTSample *input) - */ -function ff_imdct_half_vfp, export=1 - ldr ip, [CONTEXT, #5*4] @ mdct_bits - teq ip, #6 - bne 10f - - .set n, 1<<6 - .set n2, n/2 - .set n4, n/4 - .set n8, n/8 - - push {v1-v5,lr} - vpush {s16-s27} - fmrx OLDFPSCR, FPSCR - ldr lr, =0x03030000 @ RunFast mode, short vectors of length 4, stride 1 - fmxr FPSCR, lr - mov OUT, ORIGOUT - ldr REVTAB, [CONTEXT, #2*4] - ldr TCOS, [CONTEXT, #6*4] - ldr TSIN, [CONTEXT, #7*4] - - .set k, 0 - .rept n8/2 - prerotation_innerloop - .endr - - fmxr FPSCR, OLDFPSCR - mov a1, OUT - bl X(ff_fft16_vfp) - ldr lr, =0x03030000 @ RunFast mode, short vectors of length 4, stride 1 - fmxr FPSCR, lr - - .set k, 0 - postrotation_innerloop , head - .rept n8/2 - 1 - postrotation_innerloop tail, head - .endr - postrotation_innerloop tail - - fmxr FPSCR, OLDFPSCR - vpop {s16-s27} - pop {v1-v5,pc} - -10: - push {v1-v6,sl,fp,lr} - vpush {s16-s27} - fmrx OLDFPSCR, FPSCR - ldr lr, =0x03030000 @ RunFast mode, short vectors of length 4, stride 1 - fmxr FPSCR, lr - mov lr, #1 - mov OUT, ORIGOUT - ldr REVTAB, [CONTEXT, #2*4] - ldr TCOS, [CONTEXT, #6*4] - ldr TSIN, [CONTEXT, #7*4] - mov lr, lr, lsl ip - - push {CONTEXT,OLDFPSCR} - add IN_HI, IN, lr, lsl #1 - add REVTAB_HI, REVTAB, lr, lsr #1 - add TCOS_HI, TCOS, lr - add TSIN_HI, TSIN, lr -0: prerotation_innerloop_rolled - teq IN, IN_HI - bne 0b - ldmia sp, {CONTEXT,OLDFPSCR} - - mov ORIGOUT, OUT - fmxr FPSCR, OLDFPSCR - ldr ip, [CONTEXT, #9*4] - blx ip @ s->fft_calc(s, output) - - pop {CONTEXT,OLDFPSCR} - ldr lr, =0x03030000 @ RunFast mode, short vectors of length 4, stride 1 - ldr ip, [CONTEXT, #5*4] @ mdct_bits - fmxr FPSCR, lr - mov lr, #1 - mov lr, lr, lsl ip - sub TCOS, TCOS, lr, lsr #1 - sub TSIN, TSIN, lr, lsr #1 - add OUT_HI, OUT, lr, lsl #1 - add TCOS_HI, TCOS, lr - add TSIN_HI, TSIN, lr - postrotation_innerloop_rolled , head, s20, s21, s22, s23,, 0 - b 1f -0: add OUT, OUT, #32 - sub OUT_HI, OUT_HI, #32 - postrotation_innerloop_rolled tail, head, s20, s21, s22, s23, s24, 0, -16 -1: postrotation_innerloop_rolled tail, head, s24, s25, s26, s27, s20, 16, 0 - teq TSIN, TSIN_HI - bne 0b - postrotation_innerloop_rolled tail,,,,,, s24,, 16 - - fmxr FPSCR, OLDFPSCR - vpop {s16-s27} - pop {v1-v6,sl,fp,pc} -endfunc - - .unreq CONTEXT - .unreq ORIGOUT - .unreq IN - .unreq OUT - .unreq REVTAB - .unreq TCOS - .unreq TSIN - .unreq OLDFPSCR - .unreq J0 - .unreq J1 - .unreq J2 - .unreq J3 - .unreq REVTAB_HI - .unreq IN_HI - .unreq OUT_HI - .unreq TCOS_HI - .unreq TSIN_HI diff --git a/libavcodec/arm/rdft_neon.S b/libavcodec/arm/rdft_neon.S deleted file mode 100644 index eabb92b4bdb..00000000000 --- a/libavcodec/arm/rdft_neon.S +++ /dev/null @@ -1,155 +0,0 @@ -/* - * ARM NEON optimised RDFT - * Copyright (c) 2009 Mans Rullgard - * - * 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/arm/asm.S" - -function ff_rdft_calc_neon, export=1 - push {r4-r8,lr} - - ldr r6, [r0, #4] @ inverse - mov r4, r0 - mov r5, r1 - - lsls r6, r6, #31 - bne 1f - add r0, r4, #24 - bl X(ff_fft_permute_neon) - add r0, r4, #24 - mov r1, r5 - bl X(ff_fft_calc_neon) -1: - ldr r12, [r4, #0] @ nbits - mov r2, #1 - ldr r8, [r4, #20] @ negative_sin - lsl r12, r2, r12 - add r0, r5, #8 - lsl r8, r8, #31 - add r1, r5, r12, lsl #2 - lsr r12, r12, #2 - vdup.32 d26, r8 - ldr r2, [r4, #12] @ tcos - sub r12, r12, #2 - ldr r3, [r4, #16] @ tsin - mov r7, r0 - sub r1, r1, #8 - mov lr, r1 - mov r8, #-8 - vld1.32 {d0}, [r0,:64]! @ d1[0,1] - vld1.32 {d1}, [r1,:64], r8 @ d2[0,1] - vld1.32 {d4}, [r2,:64]! @ tcos[i] - vld1.32 {d5}, [r3,:64]! @ tsin[i] - vmov.f32 d18, #0.5 @ k1 - vdup.32 d19, r6 - veor d5, d26, d5 - pld [r0, #32] - veor d19, d18, d19 @ k2 - vmov.i32 d16, #0 - vmov.i32 d17, #1<<31 - pld [r1, #-32] - vtrn.32 d16, d17 - pld [r2, #32] - vrev64.32 d16, d16 @ d16=1,0 d17=0,1 - pld [r3, #32] -2: - veor q1, q0, q8 @ -d1[0],d1[1], d2[0],-d2[1] - vld1.32 {d24}, [r0,:64]! @ d1[0,1] - vadd.f32 d0, d0, d3 @ d1[0]+d2[0], d1[1]-d2[1] - vld1.32 {d25}, [r1,:64], r8 @ d2[0,1] - vadd.f32 d1, d2, d1 @ -d1[0]+d2[0], d1[1]+d2[1] - veor q3, q12, q8 @ -d1[0],d1[1], d2[0],-d2[1] - pld [r0, #32] - vmul.f32 q10, q0, q9 @ ev.re, ev.im, od.im, od.re - pld [r1, #-32] - vadd.f32 d0, d24, d7 @ d1[0]+d2[0], d1[1]-d2[1] - vadd.f32 d1, d6, d25 @ -d1[0]+d2[0], d1[1]+d2[1] - vmul.f32 q11, q0, q9 @ ev.re, ev.im, od.im, od.re - veor d7, d21, d16 @ -od.im, od.re - vrev64.32 d3, d21 @ od.re, od.im - veor d6, d20, d17 @ ev.re,-ev.im - veor d2, d3, d16 @ -od.re, od.im - vmla.f32 d20, d3, d4[1] - vmla.f32 d20, d7, d5[1] - vmla.f32 d6, d2, d4[1] - vmla.f32 d6, d21, d5[1] - vld1.32 {d4}, [r2,:64]! @ tcos[i] - veor d7, d23, d16 @ -od.im, od.re - vld1.32 {d5}, [r3,:64]! @ tsin[i] - veor d24, d22, d17 @ ev.re,-ev.im - vrev64.32 d3, d23 @ od.re, od.im - veor d5, d26, d5 - pld [r2, #32] - veor d2, d3, d16 @ -od.re, od.im - pld [r3, #32] - vmla.f32 d22, d3, d4[0] - vmla.f32 d22, d7, d5[0] - vmla.f32 d24, d2, d4[0] - vmla.f32 d24, d23, d5[0] - vld1.32 {d0}, [r0,:64]! @ d1[0,1] - vld1.32 {d1}, [r1,:64], r8 @ d2[0,1] - vst1.32 {d20}, [r7,:64]! - vst1.32 {d6}, [lr,:64], r8 - vst1.32 {d22}, [r7,:64]! - vst1.32 {d24}, [lr,:64], r8 - subs r12, r12, #2 - bgt 2b - - veor q1, q0, q8 @ -d1[0],d1[1], d2[0],-d2[1] - vadd.f32 d0, d0, d3 @ d1[0]+d2[0], d1[1]-d2[1] - vadd.f32 d1, d2, d1 @ -d1[0]+d2[0], d1[1]+d2[1] - ldr r2, [r4, #8] @ sign_convention - vmul.f32 q10, q0, q9 @ ev.re, ev.im, od.im, od.re - add r0, r0, #4 - bfc r2, #0, #31 - vld1.32 {d0[0]}, [r0,:32] - veor d7, d21, d16 @ -od.im, od.re - vrev64.32 d3, d21 @ od.re, od.im - veor d6, d20, d17 @ ev.re,-ev.im - vld1.32 {d22}, [r5,:64] - vdup.32 d1, r2 - vmov d23, d22 - veor d2, d3, d16 @ -od.re, od.im - vtrn.32 d22, d23 - veor d0, d0, d1 - veor d23, d23, d17 - vmla.f32 d20, d3, d4[1] - vmla.f32 d20, d7, d5[1] - vmla.f32 d6, d2, d4[1] - vmla.f32 d6, d21, d5[1] - vadd.f32 d22, d22, d23 - vst1.32 {d20}, [r7,:64] - vst1.32 {d6}, [lr,:64] - vst1.32 {d0[0]}, [r0,:32] - vst1.32 {d22}, [r5,:64] - - cmp r6, #0 - it eq - popeq {r4-r8,pc} - - vmul.f32 d22, d22, d18 - vst1.32 {d22}, [r5,:64] - add r0, r4, #24 - mov r1, r5 - bl X(ff_fft_permute_neon) - add r0, r4, #24 - mov r1, r5 - pop {r4-r8,lr} - b X(ff_fft_calc_neon) -endfunc diff --git a/libavcodec/arm/synth_filter_init_arm.c b/libavcodec/arm/synth_filter_init_arm.c index 858c117d39b..10689b62e60 100644 --- a/libavcodec/arm/synth_filter_init_arm.c +++ b/libavcodec/arm/synth_filter_init_arm.c @@ -23,7 +23,6 @@ #include "libavutil/arm/cpu.h" #include "libavutil/attributes.h" #include "libavutil/internal.h" -#include "libavcodec/fft.h" #include "libavcodec/synth_filter.h" void ff_synth_filter_float_vfp(AVTXContext *imdct, diff --git a/libavcodec/assenc.c b/libavcodec/assenc.c index db6fd25dd7c..e7345227290 100644 --- a/libavcodec/assenc.c +++ b/libavcodec/assenc.c @@ -45,27 +45,26 @@ static int ass_encode_frame(AVCodecContext *avctx, unsigned char *buf, int bufsize, const AVSubtitle *sub) { - int i, len, total_len = 0; + size_t len; - for (i=0; inum_rects; i++) { - const char *ass = sub->rects[i]->ass; - - if (sub->rects[i]->type != SUBTITLE_ASS) { - av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); - return AVERROR(EINVAL); - } + if (sub->num_rects != 1) { + av_log(avctx, AV_LOG_ERROR, "Only one rect per AVSubtitle is supported in ASS.\n"); + return AVERROR_INVALIDDATA; + } - len = av_strlcpy(buf+total_len, ass, bufsize-total_len); + if (sub->rects[0]->type != SUBTITLE_ASS) { + av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n"); + return AVERROR(EINVAL); + } - if (len > bufsize-total_len-1) { - av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); - return AVERROR_BUFFER_TOO_SMALL; - } + len = av_strlcpy(buf, sub->rects[0]->ass, bufsize); - total_len += len; + if (len >= bufsize) { + av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); + return AVERROR_BUFFER_TOO_SMALL; } - return total_len; + return len; } #if CONFIG_SSA_ENCODER diff --git a/libavcodec/asvdec.c b/libavcodec/asvdec.c index 699aab9f8f9..568881ccd27 100644 --- a/libavcodec/asvdec.c +++ b/libavcodec/asvdec.c @@ -45,11 +45,11 @@ #define ASV1_LEVEL_VLC_BITS 4 #define ASV2_LEVEL_VLC_BITS 10 -static VLC ccp_vlc; -static VLC level_vlc; -static VLC dc_ccp_vlc; -static VLC ac_ccp_vlc; -static VLC asv2_level_vlc; +static VLCElem ccp_vlc[32]; +static VLCElem level_vlc[16]; +static VLCElem dc_ccp_vlc[16]; +static VLCElem ac_ccp_vlc[64]; +static VLCElem asv2_level_vlc[1024]; typedef struct ASVDecContext { ASVCommonContext c; @@ -67,26 +67,26 @@ typedef struct ASVDecContext { static av_cold void init_vlcs(void) { - INIT_VLC_STATIC(&ccp_vlc, CCP_VLC_BITS, 17, - &ff_asv_ccp_tab[0][1], 2, 1, - &ff_asv_ccp_tab[0][0], 2, 1, 32); - INIT_LE_VLC_STATIC(&dc_ccp_vlc, DC_CCP_VLC_BITS, 8, - &ff_asv_dc_ccp_tab[0][1], 2, 1, - &ff_asv_dc_ccp_tab[0][0], 2, 1, 16); - INIT_LE_VLC_STATIC(&ac_ccp_vlc, AC_CCP_VLC_BITS, 16, - &ff_asv_ac_ccp_tab[0][1], 2, 1, - &ff_asv_ac_ccp_tab[0][0], 2, 1, 64); - INIT_VLC_STATIC(&level_vlc, ASV1_LEVEL_VLC_BITS, 7, - &ff_asv_level_tab[0][1], 2, 1, - &ff_asv_level_tab[0][0], 2, 1, 16); - INIT_LE_VLC_STATIC(&asv2_level_vlc, ASV2_LEVEL_VLC_BITS, 63, - &ff_asv2_level_tab[0][1], 4, 2, - &ff_asv2_level_tab[0][0], 4, 2, 1024); + VLC_INIT_STATIC_TABLE(ccp_vlc, CCP_VLC_BITS, 17, + &ff_asv_ccp_tab[0][1], 2, 1, + &ff_asv_ccp_tab[0][0], 2, 1, 0); + VLC_INIT_STATIC_TABLE(dc_ccp_vlc, DC_CCP_VLC_BITS, 8, + &ff_asv_dc_ccp_tab[0][1], 2, 1, + &ff_asv_dc_ccp_tab[0][0], 2, 1, VLC_INIT_LE); + VLC_INIT_STATIC_TABLE(ac_ccp_vlc, AC_CCP_VLC_BITS, 16, + &ff_asv_ac_ccp_tab[0][1], 2, 1, + &ff_asv_ac_ccp_tab[0][0], 2, 1, VLC_INIT_LE); + VLC_INIT_STATIC_TABLE(level_vlc, ASV1_LEVEL_VLC_BITS, 7, + &ff_asv_level_tab[0][1], 2, 1, + &ff_asv_level_tab[0][0], 2, 1, 0); + VLC_INIT_STATIC_TABLE(asv2_level_vlc, ASV2_LEVEL_VLC_BITS, 63, + &ff_asv2_level_tab[0][1], 4, 2, + &ff_asv2_level_tab[0][0], 4, 2, VLC_INIT_LE); } static inline int asv1_get_level(GetBitContext *gb) { - int code = get_vlc2(gb, level_vlc.table, ASV1_LEVEL_VLC_BITS, 1); + int code = get_vlc2(gb, level_vlc, ASV1_LEVEL_VLC_BITS, 1); if (code == 3) return get_sbits(gb, 8); @@ -115,7 +115,7 @@ static inline int asv2_get_vlc2(GetBitContext *gb, const VLCElem *table, int bit static inline int asv2_get_level(GetBitContext *gb) { - int code = asv2_get_vlc2(gb, asv2_level_vlc.table, ASV2_LEVEL_VLC_BITS); + int code = asv2_get_vlc2(gb, asv2_level_vlc, ASV2_LEVEL_VLC_BITS); if (code == 31) return (int8_t) get_bits_le(gb, 8); @@ -130,7 +130,7 @@ static inline int asv1_decode_block(ASVDecContext *a, int16_t block[64]) block[0] = 8 * get_bits(&a->gb, 8); for (i = 0; i < 11; i++) { - const int ccp = get_vlc2(&a->gb, ccp_vlc.table, CCP_VLC_BITS, 1); + const int ccp = get_vlc2(&a->gb, ccp_vlc, CCP_VLC_BITS, 1); if (ccp) { if (ccp == 16) @@ -162,7 +162,7 @@ static inline int asv2_decode_block(ASVDecContext *a, int16_t block[64]) block[0] = 8 * get_bits_le(&a->gb, 8); - ccp = asv2_get_vlc2(&a->gb, dc_ccp_vlc.table, DC_CCP_VLC_BITS); + ccp = asv2_get_vlc2(&a->gb, dc_ccp_vlc, DC_CCP_VLC_BITS); if (ccp) { if (ccp & 4) block[a->permutated_scantable[1]] = (asv2_get_level(&a->gb) * a->intra_matrix[1]) >> 4; @@ -173,7 +173,7 @@ static inline int asv2_decode_block(ASVDecContext *a, int16_t block[64]) } for (i = 1; i < count + 1; i++) { - const int ccp = asv2_get_vlc2(&a->gb, ac_ccp_vlc.table, AC_CCP_VLC_BITS); + const int ccp = asv2_get_vlc2(&a->gb, ac_ccp_vlc, AC_CCP_VLC_BITS); if (ccp) { if (ccp & 8) @@ -245,7 +245,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; if (avctx->codec_id == AV_CODEC_ID_ASV1) { av_fast_padded_malloc(&a->bitstream_buffer, &a->bitstream_buffer_size, diff --git a/libavcodec/asvenc.c b/libavcodec/asvenc.c index 4a14bcf8fa2..6179b505836 100644 --- a/libavcodec/asvenc.c +++ b/libavcodec/asvenc.c @@ -33,7 +33,6 @@ #include "asv.h" #include "avcodec.h" #include "codec_internal.h" -#include "dct.h" #include "encode.h" #include "fdctdsp.h" #include "mpeg12data.h" @@ -273,7 +272,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, } if ((ret = ff_alloc_packet(avctx, pkt, c->mb_height * c->mb_width * MAX_MB_SIZE + - AV_INPUT_BUFFER_MIN_SIZE)) < 0) + FF_INPUT_BUFFER_MIN_SIZE)) < 0) return ret; init_put_bits(&a->pb, pkt->data, pkt->size); diff --git a/libavcodec/atrac3.c b/libavcodec/atrac3.c index 7415da590bc..fad2299aafe 100644 --- a/libavcodec/atrac3.c +++ b/libavcodec/atrac3.c @@ -863,10 +863,10 @@ static av_cold void atrac3_init_static_data(void) for (i = 0; i < 7; i++) { spectral_coeff_tab[i].table = table; spectral_coeff_tab[i].table_allocated = 256; - ff_init_vlc_from_lengths(&spectral_coeff_tab[i], ATRAC3_VLC_BITS, huff_tab_sizes[i], + ff_vlc_init_from_lengths(&spectral_coeff_tab[i], ATRAC3_VLC_BITS, huff_tab_sizes[i], &hufftabs[0][1], 2, &hufftabs[0][0], 2, 1, - -31, INIT_VLC_USE_NEW_STATIC, NULL); + -31, VLC_INIT_USE_STATIC, NULL); hufftabs += huff_tab_sizes[i]; table += 256; } @@ -1026,7 +1026,11 @@ const FFCodec ff_atrac3_decoder = { .init = atrac3_decode_init, .close = atrac3_decode_close, FF_CODEC_DECODE_CB(atrac3_decode_frame), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, @@ -1041,7 +1045,11 @@ const FFCodec ff_atrac3al_decoder = { .init = atrac3_decode_init, .close = atrac3_decode_close, FF_CODEC_DECODE_CB(atrac3al_decode_frame), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, diff --git a/libavcodec/atrac3plus.c b/libavcodec/atrac3plus.c index 5661654ce31..61753af73e2 100644 --- a/libavcodec/atrac3plus.c +++ b/libavcodec/atrac3plus.c @@ -66,8 +66,8 @@ static av_cold void build_canonical_huff(const uint8_t *cb, const uint8_t **xlat out_vlc->table = &tables_data[*tab_offset]; out_vlc->table_allocated = 1 << max_len; - ff_init_vlc_from_lengths(out_vlc, max_len, index, bits, 1, - *xlat, 1, 1, 0, INIT_VLC_USE_NEW_STATIC, NULL); + ff_vlc_init_from_lengths(out_vlc, max_len, index, bits, 1, + *xlat, 1, 1, 0, VLC_INIT_USE_STATIC, NULL); *tab_offset += 1 << max_len; *xlat += index; diff --git a/libavcodec/atrac9dec.c b/libavcodec/atrac9dec.c index 60962b16762..91f2e50b056 100644 --- a/libavcodec/atrac9dec.c +++ b/libavcodec/atrac9dec.c @@ -105,8 +105,8 @@ typedef struct ATRAC9Context { DECLARE_ALIGNED(32, float, temp)[2048]; } ATRAC9Context; -static VLC sf_vlc[2][8]; /* Signed/unsigned, length */ -static VLC coeff_vlc[2][8][4]; /* Cookbook, precision, cookbook index */ +static const VLCElem *sf_vlc[2][8]; /* Signed/unsigned, length */ +static const VLCElem *coeff_vlc[2][8][4]; /* Cookbook, precision, cookbook index */ static inline int parse_gradient(ATRAC9Context *s, ATRAC9BlockData *b, GetBitContext *gb) @@ -277,12 +277,12 @@ static inline int read_scalefactors(ATRAC9Context *s, ATRAC9BlockData *b, const uint8_t *sf_weights = at9_tab_sf_weights[get_bits(gb, 3)]; const int base = get_bits(gb, 5); const int len = get_bits(gb, 2) + 3; - const VLC *tab = &sf_vlc[0][len]; + const VLCElem *tab = sf_vlc[0][len]; c->scalefactors[0] = get_bits(gb, len); for (int i = 1; i < b->band_ext_q_unit; i++) { - int val = c->scalefactors[i - 1] + get_vlc2(gb, tab->table, + int val = c->scalefactors[i - 1] + get_vlc2(gb, tab, ATRAC9_SF_VLC_BITS, 1); c->scalefactors[i] = val & ((1 << len) - 1); } @@ -310,10 +310,10 @@ static inline int read_scalefactors(ATRAC9Context *s, ATRAC9BlockData *b, const int len = get_bits(gb, 2) + 2; const int unit_cnt = FFMIN(b->band_ext_q_unit, baseline_len); - const VLC *tab = &sf_vlc[1][len]; + const VLCElem *tab = sf_vlc[1][len]; for (int i = 0; i < unit_cnt; i++) { - int dist = get_vlc2(gb, tab->table, ATRAC9_SF_VLC_BITS, 1); + int dist = get_vlc2(gb, tab, ATRAC9_SF_VLC_BITS, 1); c->scalefactors[i] = baseline[i] + dist; } @@ -331,12 +331,12 @@ static inline int read_scalefactors(ATRAC9Context *s, ATRAC9BlockData *b, const int base = get_bits(gb, 5) - (1 << (5 - 1)); const int len = get_bits(gb, 2) + 1; const int unit_cnt = FFMIN(b->band_ext_q_unit, baseline_len); - const VLC *tab = &sf_vlc[0][len]; + const VLCElem *tab = sf_vlc[0][len]; c->scalefactors[0] = get_bits(gb, len); for (int i = 1; i < unit_cnt; i++) { - int val = c->scalefactors[i - 1] + get_vlc2(gb, tab->table, + int val = c->scalefactors[i - 1] + get_vlc2(gb, tab, ATRAC9_SF_VLC_BITS, 1); c->scalefactors[i] = val & ((1 << len) - 1); } @@ -418,12 +418,12 @@ static inline void read_coeffs_coarse(ATRAC9Context *s, ATRAC9BlockData *b, if (prec <= max_prec) { const int cb = c->codebookset[i]; const int cbi = at9_q_unit_to_codebookidx[i]; - const VLC *tab = &coeff_vlc[cb][prec][cbi]; + const VLCElem *tab = coeff_vlc[cb][prec][cbi]; const HuffmanCodebook *huff = &at9_huffman_coeffs[cb][prec][cbi]; const int groups = bands >> huff->value_cnt_pow; for (int j = 0; j < groups; j++) { - uint16_t val = get_vlc2(gb, tab->table, ATRAC9_COEFF_VLC_BITS, 2); + uint16_t val = get_vlc2(gb, tab, ATRAC9_COEFF_VLC_BITS, 2); for (int k = 0; k < huff->value_cnt; k++) { coeffs[k] = sign_extend(val, huff->value_bits); @@ -801,7 +801,9 @@ static int atrac9_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (ret < 0) return ret; - init_get_bits8(&gb, avpkt->data, avpkt->size); + ret = init_get_bits8(&gb, avpkt->data, avpkt->size); + if (ret < 0) + return ret; for (int i = 0; i < frames; i++) { for (int j = 0; j < s->block_config->count; j++) { @@ -841,33 +843,31 @@ static av_cold int atrac9_decode_close(AVCodecContext *avctx) return 0; } -static av_cold void atrac9_init_vlc(VLC *vlc, int nb_bits, int nb_codes, - const uint8_t (**tab)[2], - unsigned *buf_offset, int offset) +static av_cold const VLCElem *atrac9_init_vlc(VLCInitState *state, + int nb_bits, int nb_codes, + const uint8_t (**tab)[2], int offset) { - static VLCElem vlc_buf[24812]; + const uint8_t (*table)[2] = *tab; - vlc->table = &vlc_buf[*buf_offset]; - vlc->table_allocated = FF_ARRAY_ELEMS(vlc_buf) - *buf_offset; - ff_init_vlc_from_lengths(vlc, nb_bits, nb_codes, - &(*tab)[0][1], 2, &(*tab)[0][0], 2, 1, - offset, INIT_VLC_STATIC_OVERLONG, NULL); - *buf_offset += vlc->table_size; *tab += nb_codes; + return ff_vlc_init_tables_from_lengths(state, nb_bits, nb_codes, + &table[0][1], 2, &table[0][0], 2, 1, + offset, 0); } static av_cold void atrac9_init_static(void) { + static VLCElem vlc_buf[24812]; + VLCInitState state = VLC_INIT_STATE(vlc_buf); const uint8_t (*tab)[2]; - unsigned offset = 0; /* Unsigned scalefactor VLCs */ tab = at9_sfb_a_tab; for (int i = 1; i < 7; i++) { const HuffmanCodebook *hf = &at9_huffman_sf_unsigned[i]; - atrac9_init_vlc(&sf_vlc[0][i], ATRAC9_SF_VLC_BITS, - hf->size, &tab, &offset, 0); + sf_vlc[0][i] = atrac9_init_vlc(&state, ATRAC9_SF_VLC_BITS, + hf->size, &tab, 0); } /* Signed scalefactor VLCs */ @@ -878,8 +878,8 @@ static av_cold void atrac9_init_static(void) /* The symbols are signed integers in the range -16..15; * the values in the source table are offset by 16 to make * them fit into an uint8_t; the -16 reverses this shift. */ - atrac9_init_vlc(&sf_vlc[1][i], ATRAC9_SF_VLC_BITS, - hf->size, &tab, &offset, -16); + sf_vlc[1][i] = atrac9_init_vlc(&state, ATRAC9_SF_VLC_BITS, + hf->size, &tab, -16); } /* Coefficient VLCs */ @@ -888,8 +888,8 @@ static av_cold void atrac9_init_static(void) for (int j = 2; j < 8; j++) { for (int k = i; k < 4; k++) { const HuffmanCodebook *hf = &at9_huffman_coeffs[i][j][k]; - atrac9_init_vlc(&coeff_vlc[i][j][k], ATRAC9_COEFF_VLC_BITS, - hf->size, &tab, &offset, 0); + coeff_vlc[i][j][k] = atrac9_init_vlc(&state, ATRAC9_COEFF_VLC_BITS, + hf->size, &tab, 0); } } } @@ -923,7 +923,9 @@ static av_cold int atrac9_decode_init(AVCodecContext *avctx) return AVERROR_INVALIDDATA; } - init_get_bits8(&gb, avctx->extradata + 4, avctx->extradata_size); + err = init_get_bits8(&gb, avctx->extradata + 4, avctx->extradata_size); + if (err < 0) + return err; if (get_bits(&gb, 8) != 0xFE) { av_log(avctx, AV_LOG_ERROR, "Incorrect magic byte!\n"); @@ -1003,5 +1005,9 @@ const FFCodec ff_atrac9_decoder = { FF_CODEC_DECODE_CB(atrac9_decode_frame), .flush = atrac9_decode_flush, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, }; diff --git a/libavcodec/audiodsp.c b/libavcodec/audiodsp.c index eba6e809fd6..c5427d3535d 100644 --- a/libavcodec/audiodsp.c +++ b/libavcodec/audiodsp.c @@ -22,36 +22,36 @@ #include "libavutil/common.h" #include "audiodsp.h" -static inline uint32_t clipf_c_one(uint32_t a, uint32_t mini, - uint32_t maxi, uint32_t maxisign) +static inline float clipf_c_one(float a, uint32_t mini, + uint32_t maxi, uint32_t maxisign) { - if (a > mini) - return mini; - else if ((a ^ (1U << 31)) > maxisign) - return maxi; + uint32_t ai = av_float2int(a); + + if (ai > mini) + return av_int2float(mini); + else if ((ai ^ (1U << 31)) > maxisign) + return av_int2float(maxi); else return a; } static void vector_clipf_c_opposite_sign(float *dst, const float *src, - float *min, float *max, int len) + float min, float max, int len) { int i; - uint32_t mini = *(uint32_t *) min; - uint32_t maxi = *(uint32_t *) max; + uint32_t mini = av_float2int(min); + uint32_t maxi = av_float2int(max); uint32_t maxisign = maxi ^ (1U << 31); - uint32_t *dsti = (uint32_t *) dst; - const uint32_t *srci = (const uint32_t *) src; for (i = 0; i < len; i += 8) { - dsti[i + 0] = clipf_c_one(srci[i + 0], mini, maxi, maxisign); - dsti[i + 1] = clipf_c_one(srci[i + 1], mini, maxi, maxisign); - dsti[i + 2] = clipf_c_one(srci[i + 2], mini, maxi, maxisign); - dsti[i + 3] = clipf_c_one(srci[i + 3], mini, maxi, maxisign); - dsti[i + 4] = clipf_c_one(srci[i + 4], mini, maxi, maxisign); - dsti[i + 5] = clipf_c_one(srci[i + 5], mini, maxi, maxisign); - dsti[i + 6] = clipf_c_one(srci[i + 6], mini, maxi, maxisign); - dsti[i + 7] = clipf_c_one(srci[i + 7], mini, maxi, maxisign); + dst[i + 0] = clipf_c_one(src[i + 0], mini, maxi, maxisign); + dst[i + 1] = clipf_c_one(src[i + 1], mini, maxi, maxisign); + dst[i + 2] = clipf_c_one(src[i + 2], mini, maxi, maxisign); + dst[i + 3] = clipf_c_one(src[i + 3], mini, maxi, maxisign); + dst[i + 4] = clipf_c_one(src[i + 4], mini, maxi, maxisign); + dst[i + 5] = clipf_c_one(src[i + 5], mini, maxi, maxisign); + dst[i + 6] = clipf_c_one(src[i + 6], mini, maxi, maxisign); + dst[i + 7] = clipf_c_one(src[i + 7], mini, maxi, maxisign); } } @@ -61,7 +61,7 @@ static void vector_clipf_c(float *dst, const float *src, int len, int i; if (min < 0 && max > 0) { - vector_clipf_c_opposite_sign(dst, src, &min, &max, len); + vector_clipf_c_opposite_sign(dst, src, min, max, len); } else { for (i = 0; i < len; i += 8) { dst[i] = av_clipf(src[i], min, max); diff --git a/libavcodec/audiotoolboxdec.c b/libavcodec/audiotoolboxdec.c index 82babe3d317..5456f916cf4 100644 --- a/libavcodec/audiotoolboxdec.c +++ b/libavcodec/audiotoolboxdec.c @@ -71,10 +71,12 @@ static UInt32 ffat_get_format_id(enum AVCodecID codec, int profile) return kAudioFormatAMR; case AV_CODEC_ID_EAC3: return kAudioFormatEnhancedAC3; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 case AV_CODEC_ID_GSM_MS: return kAudioFormatMicrosoftGSM; case AV_CODEC_ID_ILBC: return kAudioFormatiLBC; +#endif case AV_CODEC_ID_MP1: return kAudioFormatMPEGLayer1; case AV_CODEC_ID_MP2: diff --git a/libavcodec/audiotoolboxenc.c b/libavcodec/audiotoolboxenc.c index 1ccfda4d207..2c65101fdcf 100644 --- a/libavcodec/audiotoolboxenc.c +++ b/libavcodec/audiotoolboxenc.c @@ -60,24 +60,28 @@ static UInt32 ffat_get_format_id(enum AVCodecID codec, int profile) switch (codec) { case AV_CODEC_ID_AAC: switch (profile) { - case FF_PROFILE_AAC_LOW: + case AV_PROFILE_AAC_LOW: default: return kAudioFormatMPEG4AAC; - case FF_PROFILE_AAC_HE: + case AV_PROFILE_AAC_HE: return kAudioFormatMPEG4AAC_HE; - case FF_PROFILE_AAC_HE_V2: + case AV_PROFILE_AAC_HE_V2: return kAudioFormatMPEG4AAC_HE_V2; - case FF_PROFILE_AAC_LD: + case AV_PROFILE_AAC_LD: return kAudioFormatMPEG4AAC_LD; - case FF_PROFILE_AAC_ELD: +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 + case AV_PROFILE_AAC_ELD: return kAudioFormatMPEG4AAC_ELD; +#endif } case AV_CODEC_ID_ADPCM_IMA_QT: return kAudioFormatAppleIMA4; case AV_CODEC_ID_ALAC: return kAudioFormatAppleLossless; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 case AV_CODEC_ID_ILBC: return kAudioFormatiLBC; +#endif case AV_CODEC_ID_PCM_ALAW: return kAudioFormatALaw; case AV_CODEC_ID_PCM_MULAW: @@ -88,7 +92,7 @@ static UInt32 ffat_get_format_id(enum AVCodecID codec, int profile) } } -static void ffat_update_ctx(AVCodecContext *avctx) +static int ffat_update_ctx(AVCodecContext *avctx) { ATDecodeContext *at = avctx->priv_data; UInt32 size = sizeof(unsigned); @@ -114,10 +118,23 @@ static void ffat_update_ctx(AVCodecContext *avctx) if (!AudioConverterGetProperty(at->converter, kAudioConverterCurrentOutputStreamDescription, &size, &out_format)) { - if (out_format.mFramesPerPacket) + if (out_format.mFramesPerPacket) { avctx->frame_size = out_format.mFramesPerPacket; + } else { + /* The doc on mFramesPerPacket says: + * For formats with a variable number of frames per packet, such as + * Ogg Vorbis, set this field to 0. + * Looks like it means for decoding. There is no known case that + * mFramesPerPacket is zero for encoding. Use a default value for safety. + */ + avctx->frame_size = 1024; + av_log(avctx, AV_LOG_WARNING, "Missing mFramesPerPacket\n"); + } if (out_format.mBytesPerPacket && avctx->codec_id == AV_CODEC_ID_ILBC) avctx->block_align = out_format.mBytesPerPacket; + } else { + av_log(avctx, AV_LOG_ERROR, "Get OutputStreamDescription failed\n"); + return AVERROR_EXTERNAL; } at->frame_size = avctx->frame_size; @@ -126,6 +143,8 @@ static void ffat_update_ctx(AVCodecContext *avctx) at->pkt_size *= 1024; avctx->frame_size *= 1024; } + + return 0; } static int read_descr(GetByteContext *gb, int *tag) @@ -232,6 +251,7 @@ static av_cold int ffat_init_encoder(AVCodecContext *avctx) { ATDecodeContext *at = avctx->priv_data; OSStatus status; + int ret; AudioStreamBasicDescription in_format = { .mSampleRate = avctx->sample_rate, @@ -428,7 +448,9 @@ static av_cold int ffat_init_encoder(AVCodecContext *avctx) } } - ffat_update_ctx(avctx); + ret = ffat_update_ctx(avctx); + if (ret < 0) + return ret; #if !TARGET_OS_IPHONE && defined(__MAC_10_9) if (at->mode == kAudioCodecBitRateControlMode_Variable && avctx->rc_max_rate) { @@ -479,8 +501,7 @@ static OSStatus ffat_encode_callback(AudioConverterRef converter, UInt32 *nb_pac if (*nb_packets > frame->nb_samples) *nb_packets = frame->nb_samples; - av_frame_unref(at->encoding_frame); - ret = av_frame_ref(at->encoding_frame, frame); + ret = av_frame_replace(at->encoding_frame, frame); if (ret < 0) { *nb_packets = 0; return ret; @@ -582,23 +603,23 @@ static av_cold int ffat_close_encoder(AVCodecContext *avctx) } static const AVProfile aac_profiles[] = { - { FF_PROFILE_AAC_LOW, "LC" }, - { FF_PROFILE_AAC_HE, "HE-AAC" }, - { FF_PROFILE_AAC_HE_V2, "HE-AACv2" }, - { FF_PROFILE_AAC_LD, "LD" }, - { FF_PROFILE_AAC_ELD, "ELD" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_AAC_LOW, "LC" }, + { AV_PROFILE_AAC_HE, "HE-AAC" }, + { AV_PROFILE_AAC_HE_V2, "HE-AACv2" }, + { AV_PROFILE_AAC_LD, "LD" }, + { AV_PROFILE_AAC_ELD, "ELD" }, + { AV_PROFILE_UNKNOWN }, }; #define AE AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { #if !TARGET_OS_IPHONE - {"aac_at_mode", "ratecontrol mode", offsetof(ATDecodeContext, mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, kAudioCodecBitRateControlMode_Variable, AE, "mode"}, - {"auto", "VBR if global quality is given; CBR otherwise", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, INT_MIN, INT_MAX, AE, "mode"}, - {"cbr", "constant bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = kAudioCodecBitRateControlMode_Constant}, INT_MIN, INT_MAX, AE, "mode"}, - {"abr", "long-term average bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = kAudioCodecBitRateControlMode_LongTermAverage}, INT_MIN, INT_MAX, AE, "mode"}, - {"cvbr", "constrained variable bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = kAudioCodecBitRateControlMode_VariableConstrained}, INT_MIN, INT_MAX, AE, "mode"}, - {"vbr" , "variable bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = kAudioCodecBitRateControlMode_Variable}, INT_MIN, INT_MAX, AE, "mode"}, + {"aac_at_mode", "ratecontrol mode", offsetof(ATDecodeContext, mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, kAudioCodecBitRateControlMode_Variable, AE, .unit = "mode"}, + {"auto", "VBR if global quality is given; CBR otherwise", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, INT_MIN, INT_MAX, AE, .unit = "mode"}, + {"cbr", "constant bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = kAudioCodecBitRateControlMode_Constant}, INT_MIN, INT_MAX, AE, .unit = "mode"}, + {"abr", "long-term average bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = kAudioCodecBitRateControlMode_LongTermAverage}, INT_MIN, INT_MAX, AE, .unit = "mode"}, + {"cvbr", "constrained variable bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = kAudioCodecBitRateControlMode_VariableConstrained}, INT_MIN, INT_MAX, AE, .unit = "mode"}, + {"vbr" , "variable bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = kAudioCodecBitRateControlMode_Variable}, INT_MIN, INT_MAX, AE, .unit = "mode"}, #endif {"aac_at_quality", "quality vs speed control", offsetof(ATDecodeContext, quality), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 2, AE}, { NULL }, @@ -627,7 +648,6 @@ static const AVOption options[] = { .p.priv_class = &ffat_##NAME##_enc_class, \ .p.capabilities = AV_CODEC_CAP_DELAY | \ AV_CODEC_CAP_ENCODER_FLUSH CAPS, \ - CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(CHANNEL_LAYOUTS) \ .p.ch_layouts = CH_LAYOUTS, \ .p.sample_fmts = (const enum AVSampleFormat[]) { \ AV_SAMPLE_FMT_S16, \ @@ -653,27 +673,9 @@ static const AVChannelLayout aac_at_ch_layouts[] = { { 0 }, }; -#if FF_API_OLD_CHANNEL_LAYOUT -static const uint64_t aac_at_channel_layouts[] = { - AV_CH_LAYOUT_MONO, - AV_CH_LAYOUT_STEREO, - AV_CH_LAYOUT_SURROUND, - AV_CH_LAYOUT_4POINT0, - AV_CH_LAYOUT_5POINT0, - AV_CH_LAYOUT_5POINT1, - AV_CH_LAYOUT_6POINT0, - AV_CH_LAYOUT_6POINT1, - AV_CH_LAYOUT_7POINT0, - AV_CH_LAYOUT_7POINT1_WIDE_BACK, - AV_CH_LAYOUT_QUAD, - AV_CH_LAYOUT_OCTAGONAL, - 0, -}; -#endif - FFAT_ENC(aac, AV_CODEC_ID_AAC, aac_profiles, , aac_at_channel_layouts, aac_at_ch_layouts) //FFAT_ENC(adpcm_ima_qt, AV_CODEC_ID_ADPCM_IMA_QT, NULL) -FFAT_ENC(alac, AV_CODEC_ID_ALAC, NULL, | AV_CODEC_CAP_VARIABLE_FRAME_SIZE, NULL, NULL) +FFAT_ENC(alac, AV_CODEC_ID_ALAC, NULL, , NULL, NULL) FFAT_ENC(ilbc, AV_CODEC_ID_ILBC, NULL, , NULL, NULL) FFAT_ENC(pcm_alaw, AV_CODEC_ID_PCM_ALAW, NULL, , NULL, NULL) FFAT_ENC(pcm_mulaw, AV_CODEC_ID_PCM_MULAW, NULL, , NULL, NULL) diff --git a/libavcodec/av1.h b/libavcodec/av1.h index 384f7cddc7e..94e88f84845 100644 --- a/libavcodec/av1.h +++ b/libavcodec/av1.h @@ -58,6 +58,7 @@ enum { // Reference frames (section 6.10.24). enum { + AV1_REF_FRAME_NONE = -1, AV1_REF_FRAME_INTRA = 0, AV1_REF_FRAME_LAST = 1, AV1_REF_FRAME_LAST2 = 2, @@ -175,6 +176,13 @@ enum { AV1_RESTORE_SWITCHABLE = 3, }; +// TX mode (section 6.8.21) +enum { + AV1_ONLY_4X4 = 0, + AV1_TX_MODE_LARGEST = 1, + AV1_TX_MODE_SELECT = 2, +}; + // Sequence Headers are actually unbounded because one can use // an arbitrary number of leading zeroes when encoding via uvlc. // The following estimate is based around using the lowest number diff --git a/libavcodec/av1_levels.c b/libavcodec/av1_levels.c new file mode 100644 index 00000000000..19b6ee17362 --- /dev/null +++ b/libavcodec/av1_levels.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023 Intel Corporation + * + * 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 +#include "libavutil/macros.h" +#include "av1_levels.h" + +/** ignore entries which named in spec but no details. Like level 2.2 and 7.0. */ +static const AV1LevelDescriptor av1_levels[] = { + // Name MaxVSize MainMbps MaxTiles + // | level_idx | MaxDisplayRate | HighMbps | MaxTileCols + // | | MaxPicSize | | MaxDecodeRate | | MainCR | | + // | | | MaxHSize | | | MaxHeaderRate | | | HighCR| | + // | | | | | | | | | | | | | | + { "2.0", 0, 147456, 2048, 1152, 4423680, 5529600, 150, 1.5, 0, 2, 0, 8, 4 }, + { "2.1", 1, 278784, 2816, 1584, 8363520, 10454400, 150, 3.0, 0, 2, 0, 8, 4 }, + { "3.0", 4, 665856, 4352, 2448, 19975680, 24969600, 150, 6.0, 0, 2, 0, 16, 6 }, + { "3.1", 5, 1065024, 5504, 3096, 31950720, 39938400, 150, 10.0, 0, 2, 0, 16, 6 }, + { "4.0", 8, 2359296, 6144, 3456, 70778880, 77856768, 300, 12.0, 30.0, 4, 4, 32, 8 }, + { "4.1", 9, 2359296, 6144, 3456, 141557760, 155713536, 300, 20.0, 50.0, 4, 4, 32, 8 }, + { "5.0", 12, 8912896, 8192, 4352, 267386880, 273715200, 300, 30.0, 100.0, 6, 4, 64, 8 }, + { "5.1", 13, 8912896, 8192, 4352, 534773760, 547430400, 300, 40.0, 160.0, 8, 4, 64, 8 }, + { "5.2", 14, 8912896, 8192, 4352, 1069547520, 1094860800, 300, 60.0, 240.0, 8, 4, 64, 8 }, + { "5.3", 15, 8912896, 8192, 4352, 1069547520, 1176502272, 300, 60.0, 240.0, 8, 4, 64, 8 }, + { "6.0", 16, 35651584, 16384, 8704, 1069547520, 1176502272, 300, 60.0, 240.0, 8, 4, 128, 16 }, + { "6.1", 17, 35651584, 16384, 8704, 2139095040, 2189721600, 300, 100.0, 480.0, 8, 4, 128, 16 }, + { "6.2", 18, 35651584, 16384, 8704, 4278190080, 4379443200, 300, 160.0, 800.0, 8, 4, 128, 16 }, + { "6.3", 19, 35651584, 16384, 8704, 4278190080, 4706009088, 300, 160.0, 800.0, 8, 4, 128, 16 }, +}; + +const AV1LevelDescriptor *ff_av1_guess_level(int64_t bitrate, + int tier, + int width, + int height, + int tiles, + int tile_cols, + float fps) +{ + int pic_size; + uint64_t display_rate; + float max_br; + + pic_size = width * height; + display_rate = (uint64_t)pic_size * fps; + + for (int i = 0; i < FF_ARRAY_ELEMS(av1_levels); i++) { + const AV1LevelDescriptor *level = &av1_levels[i]; + // Limitation: decode rate, header rate, compress rate, etc. are not considered. + if (pic_size > level->max_pic_size) + continue; + if (width > level->max_h_size) + continue; + if (height > level->max_v_size) + continue; + if (display_rate > level->max_display_rate) + continue; + + if (tier) + max_br = level->high_mbps; + else + max_br = level->main_mbps; + if (!max_br) + continue; + if (bitrate > (int64_t)(1000000.0 * max_br)) + continue; + + if (tiles > level->max_tiles) + continue; + if (tile_cols > level->max_tile_cols) + continue; + return level; + } + + return NULL; +} diff --git a/libavcodec/av1_levels.h b/libavcodec/av1_levels.h new file mode 100644 index 00000000000..164cb876ba1 --- /dev/null +++ b/libavcodec/av1_levels.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 Intel Corporation + * + * 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 + */ + +#ifndef AVCODEC_AV1_LEVELS_H +#define AVCODEC_AV1_LEVELS_H + +#include + +typedef struct AV1LevelDescriptor { + char name[4]; + uint8_t level_idx; + + uint32_t max_pic_size; + uint32_t max_h_size; + uint32_t max_v_size; + uint64_t max_display_rate; + uint64_t max_decode_rate; + + uint32_t max_header_rate; + float main_mbps; + float high_mbps; + uint32_t main_cr; + uint32_t high_cr; + uint32_t max_tiles; + uint32_t max_tile_cols; +} AV1LevelDescriptor; + +/** + * Guess the level of a stream from some parameters. + * + * Unknown parameters may be zero, in which case they will be ignored. + */ +const AV1LevelDescriptor *ff_av1_guess_level(int64_t bitrate, + int tier, + int width, + int height, + int tile_rows, + int tile_cols, + float fps); + +#endif /* AVCODEC_AV1_LEVELS_H */ diff --git a/libavcodec/av1_parse.c b/libavcodec/av1_parse.c index 59ea0bc6e75..061636815fd 100644 --- a/libavcodec/av1_parse.c +++ b/libavcodec/av1_parse.c @@ -56,7 +56,7 @@ int ff_av1_extract_obu(AV1OBU *obu, const uint8_t *buf, int length, void *logctx int ff_av1_packet_split(AV1Packet *pkt, const uint8_t *buf, int length, void *logctx) { GetByteContext bc; - int ret, consumed; + int consumed; bytestream2_init(&bc, buf, length); pkt->nb_obus = 0; @@ -88,16 +88,14 @@ int ff_av1_packet_split(AV1Packet *pkt, const uint8_t *buf, int length, void *lo obu->size_bits = get_obu_bit_length(obu->data, obu->size, obu->type); - if (obu->size_bits < 0 || (!obu->size_bits && obu->type != AV1_OBU_TEMPORAL_DELIMITER)) { + if (obu->size_bits < 0 || + (obu->size_bits == 0 && (obu->type != AV1_OBU_TEMPORAL_DELIMITER && + obu->type != AV1_OBU_PADDING))) { av_log(logctx, AV_LOG_ERROR, "Invalid OBU of type %d, skipping.\n", obu->type); continue; } pkt->nb_obus++; - - ret = init_get_bits(&obu->gb, obu->data, obu->size_bits); - if (ret < 0) - return ret; } return 0; @@ -108,3 +106,17 @@ void ff_av1_packet_uninit(AV1Packet *pkt) av_freep(&pkt->obus); pkt->obus_allocated = pkt->obus_allocated_size = 0; } + +AVRational ff_av1_framerate(int64_t ticks_per_frame, int64_t units_per_tick, + int64_t time_scale) +{ + AVRational fr; + + if (ticks_per_frame && units_per_tick && time_scale && + ticks_per_frame < INT64_MAX / units_per_tick && + av_reduce(&fr.den, &fr.num, units_per_tick * ticks_per_frame, + time_scale, INT_MAX)) + return fr; + + return (AVRational){ 0, 1 }; +} diff --git a/libavcodec/av1_parse.h b/libavcodec/av1_parse.h index f4a5d2830ec..2b8cce4835a 100644 --- a/libavcodec/av1_parse.h +++ b/libavcodec/av1_parse.h @@ -30,6 +30,7 @@ #include "av1.h" #include "get_bits.h" +#include "leb.h" // OBU header fields + max leb128 length #define MAX_OBU_HEADER_SIZE (2 + 8) @@ -49,9 +50,6 @@ typedef struct AV1OBU { int raw_size; const uint8_t *raw_data; - /** GetBitContext initialized to the start of the payload */ - GetBitContext gb; - int type; int temporal_id; @@ -91,19 +89,6 @@ int ff_av1_packet_split(AV1Packet *pkt, const uint8_t *buf, int length, */ void ff_av1_packet_uninit(AV1Packet *pkt); -static inline int64_t leb128(GetBitContext *gb) { - int64_t ret = 0; - int i; - - for (i = 0; i < 8; i++) { - int byte = get_bits(gb, 8); - ret |= (int64_t)(byte & 0x7f) << (i * 7); - if (!(byte & 0x80)) - break; - } - return ret; -} - static inline int parse_obu_header(const uint8_t *buf, int buf_size, int64_t *obu_size, int *start_pos, int *type, int *temporal_id, int *spatial_id) @@ -132,7 +117,7 @@ static inline int parse_obu_header(const uint8_t *buf, int buf_size, *temporal_id = *spatial_id = 0; } - *obu_size = has_size_flag ? leb128(&gb) + *obu_size = has_size_flag ? get_leb128(&gb) : buf_size - 1 - extension_flag; if (get_bits_left(&gb) < 0) @@ -181,4 +166,7 @@ static inline int get_obu_bit_length(const uint8_t *buf, int size, int type) return size; } +AVRational ff_av1_framerate(int64_t ticks_per_frame, int64_t units_per_tick, + int64_t time_scale); + #endif /* AVCODEC_AV1_PARSE_H */ diff --git a/libavcodec/av1_parser.c b/libavcodec/av1_parser.c index 14dae92fe9f..2b79493bf8f 100644 --- a/libavcodec/av1_parser.c +++ b/libavcodec/av1_parser.c @@ -21,6 +21,8 @@ */ #include "libavutil/avassert.h" + +#include "av1_parse.h" #include "cbs.h" #include "cbs_av1.h" #include "parser.h" @@ -162,11 +164,10 @@ static int av1_parser_parse(AVCodecParserContext *ctx, avctx->color_trc = (enum AVColorTransferCharacteristic) color->transfer_characteristics; avctx->color_range = color->color_range ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; - if (seq->timing_info_present_flag) { - const AV1RawTimingInfo *timing = &seq->timing_info; - av_reduce(&avctx->framerate.den, &avctx->framerate.num, - timing->num_units_in_display_tick, timing->time_scale, INT_MAX); - } + if (seq->timing_info_present_flag) + avctx->framerate = ff_av1_framerate(1LL + seq->timing_info.num_ticks_per_picture_minus_1, + seq->timing_info.num_units_in_display_tick, + seq->timing_info.time_scale); end: ff_cbs_fragment_reset(td); diff --git a/libavcodec/av1dec.c b/libavcodec/av1dec.c index d83c902f1f8..32c2379a450 100644 --- a/libavcodec/av1dec.c +++ b/libavcodec/av1dec.c @@ -20,16 +20,24 @@ #include "config_components.h" +#include "libavutil/hdr_dynamic_metadata.h" #include "libavutil/film_grain_params.h" +#include "libavutil/mastering_display_metadata.h" #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avcodec.h" +#include "av1_parse.h" #include "av1dec.h" +#include "atsc_a53.h" #include "bytestream.h" #include "codec_internal.h" #include "decode.h" +#include "hwaccel_internal.h" +#include "internal.h" +#include "itut35.h" #include "hwconfig.h" #include "profiles.h" +#include "refstruct.h" #include "thread.h" /**< same with Div_Lut defined in spec 7.11.3.7 */ @@ -170,7 +178,7 @@ static uint8_t get_shear_params_valid(AV1DecContext *s, int idx) int16_t alpha, beta, gamma, delta, divf, divs; int64_t v, w; int32_t *param = &s->cur_frame.gm_params[idx][0]; - if (param[2] < 0) + if (param[2] <= 0) return 0; alpha = av_clip_int16(param[2] - (1 << AV1_WARPEDMODEL_PREC_BITS)); @@ -264,7 +272,9 @@ static void skip_mode_params(AV1DecContext *s) int second_forward_idx, second_forward_hint; int ref_hint, dist, i; - if (!header->skip_mode_present) + if (header->frame_type == AV1_FRAME_KEY || + header->frame_type == AV1_FRAME_INTRA_ONLY || + !header->reference_select || !seq->enable_order_hint) return; forward_idx = -1; @@ -348,6 +358,30 @@ static void coded_lossless_param(AV1DecContext *s) } } +static void order_hint_info(AV1DecContext *s) +{ + const AV1RawFrameHeader *header = s->raw_frame_header; + const AV1RawSequenceHeader *seq = s->raw_seq; + AV1Frame *frame = &s->cur_frame; + + frame->order_hint = header->order_hint; + + for (int i = 0; i < AV1_REFS_PER_FRAME; i++) { + int ref_name = i + AV1_REF_FRAME_LAST; + int ref_slot = header->ref_frame_idx[i]; + int ref_order_hint = s->ref[ref_slot].order_hint; + + frame->order_hints[ref_name] = ref_order_hint; + if (!seq->enable_order_hint) { + frame->ref_frame_sign_bias[ref_name] = 0; + } else { + frame->ref_frame_sign_bias[ref_name] = + get_relative_dist(seq, ref_order_hint, + frame->order_hint) > 0; + } + } +} + static void load_grain_params(AV1DecContext *s) { const AV1RawFrameHeader *header = s->raw_frame_header; @@ -431,28 +465,20 @@ static int get_tiles_info(AVCodecContext *avctx, const AV1RawTileGroup *tile_gro } -static int get_pixel_format(AVCodecContext *avctx) +static enum AVPixelFormat get_sw_pixel_format(void *logctx, + const AV1RawSequenceHeader *seq) { - AV1DecContext *s = avctx->priv_data; - const AV1RawSequenceHeader *seq = s->raw_seq; - uint8_t bit_depth; - int ret; + int bit_depth; enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE; -#define HWACCEL_MAX (CONFIG_AV1_DXVA2_HWACCEL + \ - CONFIG_AV1_D3D11VA_HWACCEL * 2 + \ - CONFIG_AV1_NVDEC_HWACCEL + \ - CONFIG_AV1_VAAPI_HWACCEL + \ - CONFIG_AV1_VDPAU_HWACCEL) - enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmtp = pix_fmts; if (seq->seq_profile == 2 && seq->color_config.high_bitdepth) bit_depth = seq->color_config.twelve_bit ? 12 : 10; else if (seq->seq_profile <= 2) bit_depth = seq->color_config.high_bitdepth ? 10 : 8; else { - av_log(avctx, AV_LOG_ERROR, + av_log(logctx, AV_LOG_ERROR, "Unknown AV1 profile %d.\n", seq->seq_profile); - return -1; + return AV_PIX_FMT_NONE; } if (!seq->color_config.mono_chrome) { @@ -466,7 +492,7 @@ static int get_pixel_format(AVCodecContext *avctx) else if (bit_depth == 12) pix_fmt = AV_PIX_FMT_YUV444P12; else - av_log(avctx, AV_LOG_WARNING, "Unknown AV1 pixel format.\n"); + av_assert0(0); } else if (seq->color_config.subsampling_x == 1 && seq->color_config.subsampling_y == 0) { if (bit_depth == 8) @@ -476,7 +502,7 @@ static int get_pixel_format(AVCodecContext *avctx) else if (bit_depth == 12) pix_fmt = AV_PIX_FMT_YUV422P12; else - av_log(avctx, AV_LOG_WARNING, "Unknown AV1 pixel format.\n"); + av_assert0(0); } else if (seq->color_config.subsampling_x == 1 && seq->color_config.subsampling_y == 1) { if (bit_depth == 8) @@ -486,7 +512,7 @@ static int get_pixel_format(AVCodecContext *avctx) else if (bit_depth == 12) pix_fmt = AV_PIX_FMT_YUV420P12; else - av_log(avctx, AV_LOG_WARNING, "Unknown AV1 pixel format.\n"); + av_assert0(0); } } else { if (bit_depth == 8) @@ -496,11 +522,26 @@ static int get_pixel_format(AVCodecContext *avctx) else if (bit_depth == 12) pix_fmt = AV_PIX_FMT_GRAY12; else - av_log(avctx, AV_LOG_WARNING, "Unknown AV1 pixel format.\n"); + av_assert0(0); } - av_log(avctx, AV_LOG_DEBUG, "AV1 decode get format: %s.\n", - av_get_pix_fmt_name(pix_fmt)); + return pix_fmt; +} + +static int get_pixel_format(AVCodecContext *avctx) +{ + AV1DecContext *s = avctx->priv_data; + const AV1RawSequenceHeader *seq = s->raw_seq; + int ret; + enum AVPixelFormat pix_fmt = get_sw_pixel_format(avctx, seq); +#define HWACCEL_MAX (CONFIG_AV1_DXVA2_HWACCEL + \ + CONFIG_AV1_D3D11VA_HWACCEL * 2 + \ + CONFIG_AV1_D3D12VA_HWACCEL + \ + CONFIG_AV1_NVDEC_HWACCEL + \ + CONFIG_AV1_VAAPI_HWACCEL + \ + CONFIG_AV1_VDPAU_HWACCEL + \ + CONFIG_AV1_VULKAN_HWACCEL) + enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmtp = pix_fmts; if (pix_fmt == AV_PIX_FMT_NONE) return -1; @@ -514,6 +555,9 @@ static int get_pixel_format(AVCodecContext *avctx) *fmtp++ = AV_PIX_FMT_D3D11VA_VLD; *fmtp++ = AV_PIX_FMT_D3D11; #endif +#if CONFIG_AV1_D3D12VA_HWACCEL + *fmtp++ = AV_PIX_FMT_D3D12; +#endif #if CONFIG_AV1_NVDEC_HWACCEL *fmtp++ = AV_PIX_FMT_CUDA; #endif @@ -522,6 +566,9 @@ static int get_pixel_format(AVCodecContext *avctx) #endif #if CONFIG_AV1_VDPAU_HWACCEL *fmtp++ = AV_PIX_FMT_VDPAU; +#endif +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; #endif break; case AV_PIX_FMT_YUV420P10: @@ -532,6 +579,9 @@ static int get_pixel_format(AVCodecContext *avctx) *fmtp++ = AV_PIX_FMT_D3D11VA_VLD; *fmtp++ = AV_PIX_FMT_D3D11; #endif +#if CONFIG_AV1_D3D12VA_HWACCEL + *fmtp++ = AV_PIX_FMT_D3D12; +#endif #if CONFIG_AV1_NVDEC_HWACCEL *fmtp++ = AV_PIX_FMT_CUDA; #endif @@ -540,6 +590,44 @@ static int get_pixel_format(AVCodecContext *avctx) #endif #if CONFIG_AV1_VDPAU_HWACCEL *fmtp++ = AV_PIX_FMT_VDPAU; +#endif +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV420P12: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV422P: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV422P10: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV422P12: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV444P: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV444P10: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV444P12: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; #endif break; case AV_PIX_FMT_GRAY8: @@ -557,9 +645,13 @@ static int get_pixel_format(AVCodecContext *avctx) *fmtp++ = pix_fmt; *fmtp = AV_PIX_FMT_NONE; - ret = ff_thread_get_format(avctx, pix_fmts); - if (ret < 0) - return ret; + for (int i = 0; pix_fmts[i] != pix_fmt; i++) + if (pix_fmts[i] == avctx->pix_fmt) { + s->pix_fmt = pix_fmt; + return 1; + } + + ret = ff_get_format(avctx, pix_fmts); /** * check if the HW accel is inited correctly. If not, return un-implemented. @@ -569,21 +661,24 @@ static int get_pixel_format(AVCodecContext *avctx) if (!avctx->hwaccel) { av_log(avctx, AV_LOG_ERROR, "Your platform doesn't support" " hardware accelerated AV1 decoding.\n"); + avctx->pix_fmt = AV_PIX_FMT_NONE; return AVERROR(ENOSYS); } s->pix_fmt = pix_fmt; avctx->pix_fmt = ret; + av_log(avctx, AV_LOG_DEBUG, "AV1 decode get format: %s.\n", + av_get_pix_fmt_name(avctx->pix_fmt)); + return 0; } -static void av1_frame_unref(AVCodecContext *avctx, AV1Frame *f) +static void av1_frame_unref(AV1Frame *f) { - ff_thread_release_buffer(avctx, f->f); - av_buffer_unref(&f->hwaccel_priv_buf); - f->hwaccel_picture_private = NULL; - av_buffer_unref(&f->header_ref); + av_frame_unref(f->f); + ff_refstruct_unref(&f->hwaccel_picture_private); + ff_refstruct_unref(&f->header_ref); f->raw_frame_header = NULL; f->spatial_id = f->temporal_id = 0; memset(f->skip_mode_frame_idx, 0, @@ -596,9 +691,7 @@ static int av1_frame_ref(AVCodecContext *avctx, AV1Frame *dst, const AV1Frame *s { int ret; - ret = av_buffer_replace(&dst->header_ref, src->header_ref); - if (ret < 0) - return ret; + ff_refstruct_replace(&dst->header_ref, src->header_ref); dst->raw_frame_header = src->raw_frame_header; @@ -609,12 +702,8 @@ static int av1_frame_ref(AVCodecContext *avctx, AV1Frame *dst, const AV1Frame *s if (ret < 0) goto fail; - if (src->hwaccel_picture_private) { - dst->hwaccel_priv_buf = av_buffer_ref(src->hwaccel_priv_buf); - if (!dst->hwaccel_priv_buf) - goto fail; - dst->hwaccel_picture_private = dst->hwaccel_priv_buf->data; - } + ff_refstruct_replace(&dst->hwaccel_picture_private, + src->hwaccel_picture_private); dst->spatial_id = src->spatial_id; dst->temporal_id = src->temporal_id; @@ -635,30 +724,50 @@ static int av1_frame_ref(AVCodecContext *avctx, AV1Frame *dst, const AV1Frame *s sizeof(dst->film_grain)); dst->coded_lossless = src->coded_lossless; + dst->order_hint = src->order_hint; + memcpy(dst->ref_frame_sign_bias, src->ref_frame_sign_bias, + sizeof(dst->ref_frame_sign_bias)); + memcpy(dst->order_hints, src->order_hints, + sizeof(dst->order_hints)); + + dst->force_integer_mv = src->force_integer_mv; + return 0; fail: - av1_frame_unref(avctx, dst); + av1_frame_unref(dst); return AVERROR(ENOMEM); } static av_cold int av1_decode_free(AVCodecContext *avctx) { AV1DecContext *s = avctx->priv_data; + AV1RawMetadataITUTT35 itut_t35; for (int i = 0; i < FF_ARRAY_ELEMS(s->ref); i++) { - av1_frame_unref(avctx, &s->ref[i]); - av_frame_free(&s->ref[i].f); + if (s->ref[i].f) { + av1_frame_unref(&s->ref[i]); + av_frame_free(&s->ref[i].f); + } } - av1_frame_unref(avctx, &s->cur_frame); - av_frame_free(&s->cur_frame.f); - - av_buffer_unref(&s->seq_ref); - av_buffer_unref(&s->header_ref); + if (s->cur_frame.f) { + av1_frame_unref(&s->cur_frame); + av_frame_free(&s->cur_frame.f); + } + av_buffer_unref(&s->seq_data_ref); + ff_refstruct_unref(&s->seq_ref); + ff_refstruct_unref(&s->header_ref); + ff_refstruct_unref(&s->cll_ref); + ff_refstruct_unref(&s->mdcv_ref); av_freep(&s->tile_group_info); + while (s->itut_t35_fifo && av_fifo_read(s->itut_t35_fifo, &itut_t35, 1) >= 0) + av_buffer_unref(&itut_t35.payload_ref); + av_fifo_freep2(&s->itut_t35_fifo); + ff_cbs_fragment_free(&s->current_obu); ff_cbs_close(&s->cbc); + ff_dovi_ctx_unref(&s->dovi); return 0; } @@ -675,7 +784,7 @@ static int set_context_with_sequence(AVCodecContext *avctx, avctx->color_range = seq->color_config.color_range ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; avctx->color_primaries = seq->color_config.color_primaries; - avctx->colorspace = seq->color_config.color_primaries; + avctx->colorspace = seq->color_config.matrix_coefficients; avctx->color_trc = seq->color_config.transfer_characteristics; switch (seq->color_config.chroma_sample_position) { @@ -697,17 +806,14 @@ static int set_context_with_sequence(AVCodecContext *avctx, if (ret < 0) return ret; } - avctx->sample_aspect_ratio = (AVRational) { 1, 1 }; - - if (seq->timing_info.num_units_in_display_tick && - seq->timing_info.time_scale) { - av_reduce(&avctx->framerate.den, &avctx->framerate.num, - seq->timing_info.num_units_in_display_tick, - seq->timing_info.time_scale, - INT_MAX); - if (seq->timing_info.equal_picture_interval) - avctx->ticks_per_frame = seq->timing_info.num_ticks_per_picture_minus_1 + 1; - } + + if (seq->timing_info_present_flag) + avctx->framerate = ff_av1_framerate(1LL + seq->timing_info.num_ticks_per_picture_minus_1, + seq->timing_info.num_units_in_display_tick, + seq->timing_info.time_scale); + + if (avctx->pix_fmt == AV_PIX_FMT_NONE) + avctx->pix_fmt = get_sw_pixel_format(avctx, seq); return 0; } @@ -742,35 +848,49 @@ static int update_context_with_frame_header(AVCodecContext *avctx, return 0; } +static const CodedBitstreamUnitType decompose_unit_types[] = { + AV1_OBU_FRAME, + AV1_OBU_FRAME_HEADER, + AV1_OBU_METADATA, + AV1_OBU_REDUNDANT_FRAME_HEADER, + AV1_OBU_SEQUENCE_HEADER, + AV1_OBU_TEMPORAL_DELIMITER, + AV1_OBU_TILE_GROUP, +}; + static av_cold int av1_decode_init(AVCodecContext *avctx) { AV1DecContext *s = avctx->priv_data; AV1RawSequenceHeader *seq; + const AVPacketSideData *sd; int ret; s->avctx = avctx; + s->pkt = avctx->internal->in_pkt; s->pix_fmt = AV_PIX_FMT_NONE; for (int i = 0; i < FF_ARRAY_ELEMS(s->ref); i++) { s->ref[i].f = av_frame_alloc(); - if (!s->ref[i].f) { - av_log(avctx, AV_LOG_ERROR, - "Failed to allocate reference frame buffer %d.\n", i); + if (!s->ref[i].f) return AVERROR(ENOMEM); - } } s->cur_frame.f = av_frame_alloc(); - if (!s->cur_frame.f) { - av_log(avctx, AV_LOG_ERROR, - "Failed to allocate current frame buffer.\n"); + if (!s->cur_frame.f) return AVERROR(ENOMEM); - } ret = ff_cbs_init(&s->cbc, AV_CODEC_ID_AV1, avctx); if (ret < 0) return ret; + s->cbc->decompose_unit_types = decompose_unit_types; + s->cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types); + + s->itut_t35_fifo = av_fifo_alloc2(1, sizeof(AV1RawMetadataITUTT35), + AV_FIFO_FLAG_AUTO_GROW); + if (!s->itut_t35_fifo) + return AVERROR(ENOMEM); + av_opt_set_int(s->cbc->priv_data, "operating_point", s->operating_point, 0); if (avctx->extradata && avctx->extradata_size) { @@ -779,7 +899,7 @@ static av_cold int av1_decode_init(AVCodecContext *avctx) avctx); if (ret < 0) { av_log(avctx, AV_LOG_WARNING, "Failed to read extradata.\n"); - return ret; + goto end; } seq = ((CodedBitstreamAV1Context *)(s->cbc->priv_data))->sequence_header; @@ -798,6 +918,12 @@ static av_cold int av1_decode_init(AVCodecContext *avctx) ff_cbs_fragment_reset(&s->current_obu); } + s->dovi.logctx = avctx; + s->dovi.dv_profile = 10; // default for AV1 + sd = ff_get_coded_side_data(avctx, AV_PKT_DATA_DOVI_CONF); + if (sd && sd->size > 0) + ff_dovi_update_cfg(&s->dovi, (AVDOVIDecoderConfigurationRecord *) sd->data); + return ret; } @@ -818,7 +944,10 @@ static int av1_frame_alloc(AVCodecContext *avctx, AV1Frame *f) goto fail; frame = f->f; - frame->key_frame = header->frame_type == AV1_FRAME_KEY; + if (header->frame_type == AV1_FRAME_KEY) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; switch (header->frame_type) { case AV1_FRAME_KEY: @@ -833,22 +962,143 @@ static int av1_frame_alloc(AVCodecContext *avctx, AV1Frame *f) break; } - if (avctx->hwaccel) { - const AVHWAccel *hwaccel = avctx->hwaccel; - if (hwaccel->frame_priv_data_size) { - f->hwaccel_priv_buf = - av_buffer_allocz(hwaccel->frame_priv_data_size); - if (!f->hwaccel_priv_buf) { - ret = AVERROR(ENOMEM); - goto fail; - } - f->hwaccel_picture_private = f->hwaccel_priv_buf->data; + ret = ff_hwaccel_frame_priv_alloc(avctx, &f->hwaccel_picture_private); + if (ret < 0) + goto fail; + + return 0; + +fail: + av1_frame_unref(f); + return ret; +} + +static int export_itut_t35(AVCodecContext *avctx, AVFrame *frame, + const AV1RawMetadataITUTT35 *itut_t35) +{ + GetByteContext gb; + AV1DecContext *s = avctx->priv_data; + int ret, provider_code; + + bytestream2_init(&gb, itut_t35->payload, itut_t35->payload_size); + + provider_code = bytestream2_get_be16(&gb); + switch (provider_code) { + case ITU_T_T35_PROVIDER_CODE_ATSC: { + uint32_t user_identifier = bytestream2_get_be32(&gb); + switch (user_identifier) { + case MKBETAG('G', 'A', '9', '4'): { // closed captions + AVBufferRef *buf = NULL; + + ret = ff_parse_a53_cc(&buf, gb.buffer, bytestream2_get_bytes_left(&gb)); + if (ret < 0) + return ret; + if (!ret) + break; + + ret = ff_frame_new_side_data_from_buf(avctx, frame, AV_FRAME_DATA_A53_CC, &buf, NULL); + if (ret < 0) + return ret; + + avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; + break; + } + default: // ignore unsupported identifiers + break; + } + break; + } + case ITU_T_T35_PROVIDER_CODE_SMTPE: { + AVDynamicHDRPlus *hdrplus; + int provider_oriented_code = bytestream2_get_be16(&gb); + int application_identifier = bytestream2_get_byte(&gb); + + if (itut_t35->itu_t_t35_country_code != ITU_T_T35_COUNTRY_CODE_US || + provider_oriented_code != 1 || application_identifier != 4) + break; + + hdrplus = av_dynamic_hdr_plus_create_side_data(frame); + if (!hdrplus) + return AVERROR(ENOMEM); + + ret = av_dynamic_hdr_plus_from_t35(hdrplus, gb.buffer, + bytestream2_get_bytes_left(&gb)); + if (ret < 0) + return ret; + break; + } + case ITU_T_T35_PROVIDER_CODE_DOLBY: { + int provider_oriented_code = bytestream2_get_be32(&gb); + if (itut_t35->itu_t_t35_country_code != ITU_T_T35_COUNTRY_CODE_US || + provider_oriented_code != 0x800) + break; + + ret = ff_dovi_rpu_parse(&s->dovi, gb.buffer, gb.buffer_end - gb.buffer); + if (ret < 0) { + av_log(avctx, AV_LOG_WARNING, "Error parsing DOVI OBU.\n"); + break; // ignore } + + ret = ff_dovi_attach_side_data(&s->dovi, frame); + if (ret < 0) + return ret; + break; + } + default: // ignore unsupported provider codes + break; } + return 0; +} + +static int export_metadata(AVCodecContext *avctx, AVFrame *frame) +{ + AV1DecContext *s = avctx->priv_data; + AV1RawMetadataITUTT35 itut_t35; + int ret = 0; + + if (s->mdcv) { + AVMasteringDisplayMetadata *mastering; + + ret = ff_decode_mastering_display_new(avctx, frame, &mastering); + if (ret < 0) + return ret; + + if (mastering) { + for (int i = 0; i < 3; i++) { + mastering->display_primaries[i][0] = av_make_q(s->mdcv->primary_chromaticity_x[i], 1 << 16); + mastering->display_primaries[i][1] = av_make_q(s->mdcv->primary_chromaticity_y[i], 1 << 16); + } + mastering->white_point[0] = av_make_q(s->mdcv->white_point_chromaticity_x, 1 << 16); + mastering->white_point[1] = av_make_q(s->mdcv->white_point_chromaticity_y, 1 << 16); + + mastering->max_luminance = av_make_q(s->mdcv->luminance_max, 1 << 8); + mastering->min_luminance = av_make_q(s->mdcv->luminance_min, 1 << 14); + + mastering->has_primaries = 1; + mastering->has_luminance = 1; + } + } + + if (s->cll) { + AVContentLightMetadata *light; + + ret = ff_decode_content_light_new(avctx, frame, &light); + if (ret < 0) + return ret; + + if (light) { + light->MaxCLL = s->cll->max_cll; + light->MaxFALL = s->cll->max_fall; + } + } + + while (av_fifo_read(s->itut_t35_fifo, &itut_t35, 1) >= 0) { + if (ret >= 0) + ret = export_itut_t35(avctx, frame, &itut_t35); + av_buffer_unref(&itut_t35.payload_ref); + } -fail: - av1_frame_unref(avctx, f); return ret; } @@ -856,9 +1106,11 @@ static int export_film_grain(AVCodecContext *avctx, AVFrame *frame) { AV1DecContext *s = avctx->priv_data; const AV1RawFilmGrainParams *film_grain = &s->cur_frame.film_grain; + const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(frame->format); AVFilmGrainParams *fgp; AVFilmGrainAOMParams *aom; + av_assert0(pixdesc); if (!film_grain->apply_grain) return 0; @@ -868,6 +1120,14 @@ static int export_film_grain(AVCodecContext *avctx, AVFrame *frame) fgp->type = AV_FILM_GRAIN_PARAMS_AV1; fgp->seed = film_grain->grain_seed; + fgp->width = frame->width; + fgp->height = frame->height; + fgp->color_range = frame->color_range; + fgp->color_primaries = frame->color_primaries; + fgp->color_trc = frame->color_trc; + fgp->color_space = frame->colorspace; + fgp->subsampling_x = pixdesc->log2_chroma_w; + fgp->subsampling_y = pixdesc->log2_chroma_h; aom = &fgp->codec.aom; aom->chroma_scaling_from_luma = film_grain->chroma_scaling_from_luma; @@ -912,11 +1172,11 @@ static int export_film_grain(AVCodecContext *avctx, AVFrame *frame) return 0; } -static int set_output_frame(AVCodecContext *avctx, AVFrame *frame, - const AVPacket *pkt, int *got_frame) +static int set_output_frame(AVCodecContext *avctx, AVFrame *frame) { AV1DecContext *s = avctx->priv_data; const AVFrame *srcframe = s->cur_frame.f; + AVPacket *pkt = s->pkt; int ret; // TODO: all layers @@ -928,6 +1188,12 @@ static int set_output_frame(AVCodecContext *avctx, AVFrame *frame, if (ret < 0) return ret; + ret = export_metadata(avctx, frame); + if (ret < 0) { + av_frame_unref(frame); + return ret; + } + if (avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN) { ret = export_film_grain(avctx, frame); if (ret < 0) { @@ -938,9 +1204,14 @@ static int set_output_frame(AVCodecContext *avctx, AVFrame *frame, frame->pts = pkt->pts; frame->pkt_dts = pkt->dts; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS frame->pkt_size = pkt->size; + frame->pkt_pos = pkt->pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif - *got_frame = 1; + av_packet_unref(pkt); return 0; } @@ -953,7 +1224,7 @@ static int update_reference_list(AVCodecContext *avctx) for (int i = 0; i < AV1_NUM_REF_FRAMES; i++) { if (header->refresh_frame_flags & (1 << i)) { - av1_frame_unref(avctx, &s->ref[i]); + av1_frame_unref(&s->ref[i]); if ((ret = av1_frame_ref(avctx, &s->ref[i], &s->cur_frame)) < 0) { av_log(avctx, AV_LOG_ERROR, "Failed to update frame %d in reference list\n", i); @@ -969,11 +1240,9 @@ static int get_current_frame(AVCodecContext *avctx) AV1DecContext *s = avctx->priv_data; int ret; - av1_frame_unref(avctx, &s->cur_frame); + av1_frame_unref(&s->cur_frame); - s->cur_frame.header_ref = av_buffer_ref(s->header_ref); - if (!s->cur_frame.header_ref) - return AVERROR(ENOMEM); + s->cur_frame.header_ref = ff_refstruct_ref(s->header_ref); s->cur_frame.raw_frame_header = s->raw_frame_header; @@ -991,6 +1260,23 @@ static int get_current_frame(AVCodecContext *avctx) avctx->skip_frame >= AVDISCARD_ALL) return 0; + if (s->pix_fmt == AV_PIX_FMT_NONE) { + ret = get_pixel_format(avctx); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to get pixel format.\n"); + return ret; + } + + if (!ret && FF_HW_HAS_CB(avctx, decode_params)) { + ret = FF_HW_CALL(avctx, decode_params, AV1_OBU_SEQUENCE_HEADER, + s->seq_data_ref->data, s->seq_data_ref->size); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "HW accel decode params fail.\n"); + return ret; + } + } + } + ret = av1_frame_alloc(avctx, &s->cur_frame); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, @@ -1001,45 +1287,50 @@ static int get_current_frame(AVCodecContext *avctx) global_motion_params(s); skip_mode_params(s); coded_lossless_param(s); + order_hint_info(s); load_grain_params(s); + s->cur_frame.force_integer_mv = + s->raw_frame_header->force_integer_mv || + s->raw_frame_header->frame_type == AV1_FRAME_KEY || + s->raw_frame_header->frame_type == AV1_FRAME_INTRA_ONLY; + return ret; } -static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, - int *got_frame, AVPacket *pkt) +static int av1_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) { AV1DecContext *s = avctx->priv_data; AV1RawTileGroup *raw_tile_group = NULL; - int ret; + int i = 0, ret; - ret = ff_cbs_read_packet(s->cbc, &s->current_obu, pkt); - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, "Failed to read packet.\n"); - goto end; - } - av_log(avctx, AV_LOG_DEBUG, "Total obu for this frame:%d.\n", - s->current_obu.nb_units); - - for (int i = 0; i < s->current_obu.nb_units; i++) { + for (i = s->nb_unit; i < s->current_obu.nb_units; i++) { CodedBitstreamUnit *unit = &s->current_obu.units[i]; AV1RawOBU *obu = unit->content; const AV1RawOBUHeader *header; + av_log(avctx, AV_LOG_DEBUG, "OBU idx:%d, type:%d, content available:%d.\n", i, unit->type, !!obu); + + if (unit->type == AV1_OBU_TILE_LIST) { + av_log(avctx, AV_LOG_ERROR, "Large scale tile decoding is unsupported.\n"); + ret = AVERROR_PATCHWELCOME; + goto end; + } + if (!obu) continue; header = &obu->header; - av_log(avctx, AV_LOG_DEBUG, "Obu idx:%d, obu type:%d.\n", i, unit->type); switch (unit->type) { case AV1_OBU_SEQUENCE_HEADER: - av_buffer_unref(&s->seq_ref); - s->seq_ref = av_buffer_ref(unit->content_ref); - if (!s->seq_ref) { - ret = AVERROR(ENOMEM); + ret = av_buffer_replace(&s->seq_data_ref, unit->data_ref); + if (ret < 0) goto end; - } + + s->seq_data_ref->data = unit->data; + s->seq_data_ref->size = unit->data_size; + ff_refstruct_replace(&s->seq_ref, unit->content_ref); s->raw_seq = &obu->obu.sequence_header; @@ -1052,25 +1343,8 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, s->operating_point_idc = s->raw_seq->operating_point_idc[s->operating_point]; - if (s->pix_fmt == AV_PIX_FMT_NONE) { - ret = get_pixel_format(avctx); - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, - "Failed to get pixel format.\n"); - s->raw_seq = NULL; - goto end; - } - } + s->pix_fmt = AV_PIX_FMT_NONE; - if (avctx->hwaccel && avctx->hwaccel->decode_params) { - ret = avctx->hwaccel->decode_params(avctx, unit->type, unit->data, - unit->data_size); - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, "HW accel decode params fail.\n"); - s->raw_seq = NULL; - goto end; - } - } break; case AV1_OBU_REDUNDANT_FRAME_HEADER: if (s->raw_frame_header) @@ -1084,12 +1358,7 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, goto end; } - av_buffer_unref(&s->header_ref); - s->header_ref = av_buffer_ref(unit->content_ref); - if (!s->header_ref) { - ret = AVERROR(ENOMEM); - goto end; - } + ff_refstruct_replace(&s->header_ref, unit->content_ref); if (unit->type == AV1_OBU_FRAME) s->raw_frame_header = &obu->obu.frame.header; @@ -1097,7 +1366,7 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, s->raw_frame_header = &obu->obu.frame_header; if (s->raw_frame_header->show_existing_frame) { - av1_frame_unref(avctx, &s->cur_frame); + av1_frame_unref(&s->cur_frame); ret = av1_frame_ref(avctx, &s->cur_frame, &s->ref[s->raw_frame_header->frame_to_show_map_idx]); @@ -1113,12 +1382,13 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, } if (s->cur_frame.f->buf[0]) { - ret = set_output_frame(avctx, frame, pkt, got_frame); + ret = set_output_frame(avctx, frame); if (ret < 0) av_log(avctx, AV_LOG_ERROR, "Set output frame error.\n"); } s->raw_frame_header = NULL; + i++; goto end; } @@ -1133,8 +1403,7 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, s->cur_frame.temporal_id = header->temporal_id; if (avctx->hwaccel && s->cur_frame.f->buf[0]) { - ret = avctx->hwaccel->start_frame(avctx, unit->data, - unit->data_size); + ret = FF_HW_CALL(avctx, start_frame, unit->data, unit->data_size); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "HW accel start frame fail.\n"); goto end; @@ -1160,9 +1429,8 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, goto end; if (avctx->hwaccel && s->cur_frame.f->buf[0]) { - ret = avctx->hwaccel->decode_slice(avctx, - raw_tile_group->tile_data.data, - raw_tile_group->tile_data.data_size); + ret = FF_HW_CALL(avctx, decode_slice, raw_tile_group->tile_data.data, + raw_tile_group->tile_data.data_size); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "HW accel decode slice fail.\n"); @@ -1173,7 +1441,35 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, case AV1_OBU_TILE_LIST: case AV1_OBU_TEMPORAL_DELIMITER: case AV1_OBU_PADDING: + break; case AV1_OBU_METADATA: + switch (obu->obu.metadata.metadata_type) { + case AV1_METADATA_TYPE_HDR_CLL: + ff_refstruct_replace(&s->cll_ref, unit->content_ref); + s->cll = &obu->obu.metadata.metadata.hdr_cll; + break; + case AV1_METADATA_TYPE_HDR_MDCV: + ff_refstruct_replace(&s->mdcv_ref, unit->content_ref); + s->mdcv = &obu->obu.metadata.metadata.hdr_mdcv; + break; + case AV1_METADATA_TYPE_ITUT_T35: { + AV1RawMetadataITUTT35 itut_t35; + memcpy(&itut_t35, &obu->obu.metadata.metadata.itut_t35, sizeof(itut_t35)); + itut_t35.payload_ref = av_buffer_ref(obu->obu.metadata.metadata.itut_t35.payload_ref); + if (!itut_t35.payload_ref) { + ret = AVERROR(ENOMEM); + goto end; + } + ret = av_fifo_write(s->itut_t35_fifo, &itut_t35, 1); + if (ret < 0) { + av_buffer_unref(&itut_t35.payload_ref); + goto end; + } + break; + } + default: + break; + } break; default: av_log(avctx, AV_LOG_DEBUG, @@ -1182,8 +1478,9 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, } if (raw_tile_group && (s->tile_num == raw_tile_group->tg_end + 1)) { + int show_frame = s->raw_frame_header->show_frame; if (avctx->hwaccel && s->cur_frame.f->buf[0]) { - ret = avctx->hwaccel->end_frame(avctx); + ret = FF_HW_SIMPLE_CALL(avctx, end_frame); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "HW accel end frame fail.\n"); goto end; @@ -1197,7 +1494,7 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, } if (s->raw_frame_header->show_frame && s->cur_frame.f->buf[0]) { - ret = set_output_frame(avctx, frame, pkt, got_frame); + ret = set_output_frame(avctx, frame); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "Set output frame error\n"); goto end; @@ -1205,29 +1502,84 @@ static int av1_decode_frame(AVCodecContext *avctx, AVFrame *frame, } raw_tile_group = NULL; s->raw_frame_header = NULL; + if (show_frame) { + i++; + goto end; + } } } + ret = AVERROR(EAGAIN); end: - ff_cbs_fragment_reset(&s->current_obu); - if (ret < 0) - s->raw_frame_header = NULL; + av_assert0(i <= s->current_obu.nb_units); + s->nb_unit = i; + + if ((ret < 0 && ret != AVERROR(EAGAIN)) || s->current_obu.nb_units == i) { + if (ret < 0) + s->raw_frame_header = NULL; + av_packet_unref(s->pkt); + ff_cbs_fragment_reset(&s->current_obu); + s->nb_unit = 0; + } + if (!ret && !frame->buf[0]) + ret = AVERROR(EAGAIN); + + return ret; +} + +static int av1_receive_frame(AVCodecContext *avctx, AVFrame *frame) +{ + AV1DecContext *s = avctx->priv_data; + int ret; + + do { + if (!s->current_obu.nb_units) { + ret = ff_decode_get_packet(avctx, s->pkt); + if (ret < 0) + return ret; + + ret = ff_cbs_read_packet(s->cbc, &s->current_obu, s->pkt); + if (ret < 0) { + ff_cbs_fragment_reset(&s->current_obu); + av_packet_unref(s->pkt); + av_log(avctx, AV_LOG_ERROR, "Failed to read packet.\n"); + return ret; + } + + s->nb_unit = 0; + av_log(avctx, AV_LOG_DEBUG, "Total OBUs on this packet: %d.\n", + s->current_obu.nb_units); + } + + ret = av1_receive_frame_internal(avctx, frame); + } while (ret == AVERROR(EAGAIN)); + return ret; } static void av1_decode_flush(AVCodecContext *avctx) { AV1DecContext *s = avctx->priv_data; + AV1RawMetadataITUTT35 itut_t35; for (int i = 0; i < FF_ARRAY_ELEMS(s->ref); i++) - av1_frame_unref(avctx, &s->ref[i]); + av1_frame_unref(&s->ref[i]); - av1_frame_unref(avctx, &s->cur_frame); + av1_frame_unref(&s->cur_frame); s->operating_point_idc = 0; + s->nb_unit = 0; s->raw_frame_header = NULL; s->raw_seq = NULL; + s->cll = NULL; + s->mdcv = NULL; + while (av_fifo_read(s->itut_t35_fifo, &itut_t35, 1) >= 0) + av_buffer_unref(&itut_t35.payload_ref); + ff_cbs_fragment_reset(&s->current_obu); ff_cbs_flush(s->cbc); + + if (FF_HW_HAS_CB(avctx, flush)) + FF_HW_SIMPLE_CALL(avctx, flush); } #define OFFSET(x) offsetof(AV1DecContext, x) @@ -1253,14 +1605,12 @@ const FFCodec ff_av1_decoder = { .priv_data_size = sizeof(AV1DecContext), .init = av1_decode_init, .close = av1_decode_free, - FF_CODEC_DECODE_CB(av1_decode_frame), - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_AVOID_PROBING, - .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | - FF_CODEC_CAP_SETS_PKT_DTS, + FF_CODEC_RECEIVE_FRAME_CB(av1_receive_frame), + .p.capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, .flush = av1_decode_flush, .p.profiles = NULL_IF_CONFIG_SMALL(ff_av1_profiles), .p.priv_class = &av1_class, - .bsfs = "av1_frame_split", .hw_configs = (const AVCodecHWConfigInternal *const []) { #if CONFIG_AV1_DXVA2_HWACCEL HWACCEL_DXVA2(av1), @@ -1271,6 +1621,9 @@ const FFCodec ff_av1_decoder = { #if CONFIG_AV1_D3D11VA2_HWACCEL HWACCEL_D3D11VA2(av1), #endif +#if CONFIG_AV1_D3D12VA_HWACCEL + HWACCEL_D3D12VA(av1), +#endif #if CONFIG_AV1_NVDEC_HWACCEL HWACCEL_NVDEC(av1), #endif @@ -1280,6 +1633,9 @@ const FFCodec ff_av1_decoder = { #if CONFIG_AV1_VDPAU_HWACCEL HWACCEL_VDPAU(av1), #endif +#if CONFIG_AV1_VULKAN_HWACCEL + HWACCEL_VULKAN(av1), +#endif NULL }, diff --git a/libavcodec/av1dec.h b/libavcodec/av1dec.h index 82c7084e99f..b903b359c5f 100644 --- a/libavcodec/av1dec.h +++ b/libavcodec/av1dec.h @@ -24,19 +24,21 @@ #include #include "libavutil/buffer.h" +#include "libavutil/fifo.h" #include "libavutil/frame.h" #include "libavutil/pixfmt.h" #include "avcodec.h" +#include "packet.h" #include "cbs.h" #include "cbs_av1.h" +#include "dovi_rpu.h" typedef struct AV1Frame { AVFrame *f; - AVBufferRef *hwaccel_priv_buf; - void *hwaccel_picture_private; + void *hwaccel_picture_private; ///< RefStruct reference - AVBufferRef *header_ref; + AV1RawOBU *header_ref; ///< RefStruct reference backing raw_frame_header. AV1RawFrameHeader *raw_frame_header; int temporal_id; @@ -51,6 +53,20 @@ typedef struct AV1Frame { AV1RawFilmGrainParams film_grain; uint8_t coded_lossless; + + // OrderHint for this frame. + uint8_t order_hint; + // RefFrameSignBias[] used when decoding this frame. + uint8_t ref_frame_sign_bias[AV1_TOTAL_REFS_PER_FRAME]; + // OrderHints[] when this is the current frame, otherwise + // SavedOrderHints[s][] when is the reference frame in slot s. + uint8_t order_hints[AV1_TOTAL_REFS_PER_FRAME]; + + // force_integer_mv value at the end of the frame header parsing. + // This is not the same as the syntax element value in + // raw_frame_header because the specification parsing tables + // override the value on intra frames. + uint8_t force_integer_mv; } AV1Frame; typedef struct TileGroupInfo { @@ -67,12 +83,22 @@ typedef struct AV1DecContext { enum AVPixelFormat pix_fmt; CodedBitstreamContext *cbc; CodedBitstreamFragment current_obu; + AVPacket *pkt; - AVBufferRef *seq_ref; + AVBufferRef *seq_data_ref; + AV1RawOBU *seq_ref; ///< RefStruct reference backing raw_seq AV1RawSequenceHeader *raw_seq; - AVBufferRef *header_ref; + AV1RawOBU *header_ref; ///< RefStruct reference backing raw_frame_header AV1RawFrameHeader *raw_frame_header; TileGroupInfo *tile_group_info; + + AV1RawOBU *cll_ref; ///< RefStruct reference backing cll + AV1RawMetadataHDRCLL *cll; + AV1RawOBU *mdcv_ref; ///< RefStruct reference backing mdcv + AV1RawMetadataHDRMDCV *mdcv; + DOVIContext dovi; + AVFifo *itut_t35_fifo; + uint16_t tile_num; uint16_t tg_start; uint16_t tg_end; @@ -82,6 +108,8 @@ typedef struct AV1DecContext { AV1Frame ref[AV1_NUM_REF_FRAMES]; AV1Frame cur_frame; + int nb_unit; + // AVOptions int operating_point; } AV1DecContext; diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c index fb1362290f5..a9a87bb58ca 100644 --- a/libavcodec/avcodec.c +++ b/libavcodec/avcodec.c @@ -28,26 +28,39 @@ #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/channel_layout.h" +#include "libavutil/emms.h" #include "libavutil/fifo.h" #include "libavutil/imgutils.h" #include "libavutil/mem.h" #include "libavutil/opt.h" #include "libavutil/thread.h" #include "avcodec.h" +#include "avcodec_internal.h" #include "bsf.h" +#include "codec_desc.h" #include "codec_internal.h" #include "decode.h" #include "encode.h" #include "frame_thread_encoder.h" +#include "hwconfig.h" #include "internal.h" +#include "refstruct.h" #include "thread.h" +/** + * Maximum size in bytes of extradata. + * This value was chosen such that every bit of the buffer is + * addressable by a 32-bit signed integer as used by get_bits. + */ +#define FF_MAX_EXTRADATA_SIZE ((1 << 28) - AV_INPUT_BUFFER_PADDING_SIZE) + int avcodec_default_execute(AVCodecContext *c, int (*func)(AVCodecContext *c2, void *arg2), void *arg, int *ret, int count, int size) { - int i; + size_t i; for (i = 0; i < count; i++) { - int r = func(c, (char *)arg + i * size); + size_t offset = i * size; + int r = func(c, FF_PTR_ADD((char *)arg, offset)); if (ret) ret[i] = r; } @@ -147,7 +160,9 @@ int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *code if (avctx->extradata_size < 0 || avctx->extradata_size >= FF_MAX_EXTRADATA_SIZE) return AVERROR(EINVAL); - avci = av_mallocz(sizeof(*avci)); + avci = av_codec_is_decoder(codec) ? + ff_decode_internal_alloc() : + ff_encode_internal_alloc(); if (!avci) { ret = AVERROR(ENOMEM); goto end; @@ -226,26 +241,6 @@ int attribute_align_arg avcodec_open2(AVCodecContext *avctx, const AVCodec *code goto free_and_end; } -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - /* compat wrapper for old-style callers */ - if (avctx->channel_layout && !avctx->channels) - avctx->channels = av_popcount64(avctx->channel_layout); - - if ((avctx->channels && avctx->ch_layout.nb_channels != avctx->channels) || - (avctx->channel_layout && (avctx->ch_layout.order != AV_CHANNEL_ORDER_NATIVE || - avctx->ch_layout.u.mask != avctx->channel_layout))) { - av_channel_layout_uninit(&avctx->ch_layout); - if (avctx->channel_layout) { - av_channel_layout_from_mask(&avctx->ch_layout, avctx->channel_layout); - } else { - avctx->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; - } - avctx->ch_layout.nb_channels = avctx->channels; - } -FF_ENABLE_DEPRECATION_WARNINGS -#endif - /* AV_CODEC_CAP_CHANNEL_CONF is a decoder-only flag; so the code below * in particular checks that nb_channels is set for all audio encoders. */ if (avctx->codec_type == AVMEDIA_TYPE_AUDIO && !avctx->ch_layout.nb_channels @@ -267,11 +262,6 @@ FF_ENABLE_DEPRECATION_WARNINGS } avctx->frame_num = 0; -#if FF_API_AVCTX_FRAME_NUMBER -FF_DISABLE_DEPRECATION_WARNINGS - avctx->frame_number = avctx->frame_num; -FF_ENABLE_DEPRECATION_WARNINGS -#endif avctx->codec_descriptor = avcodec_descriptor_get(avctx->codec_id); if ((avctx->codec->capabilities & AV_CODEC_CAP_EXPERIMENTAL) && @@ -335,15 +325,6 @@ FF_ENABLE_DEPRECATION_WARNINGS if (!avctx->bit_rate) avctx->bit_rate = get_bit_rate(avctx); -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - /* update the deprecated fields for old-style callers */ - avctx->channels = avctx->ch_layout.nb_channels; - avctx->channel_layout = avctx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ? - avctx->ch_layout.u.mask : 0; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - /* validate channel layout from the decoder */ if ((avctx->ch_layout.nb_channels && !av_channel_layout_check(&avctx->ch_layout)) || avctx->ch_layout.nb_channels > FF_SANE_NB_CHANNELS) { @@ -362,7 +343,7 @@ FF_ENABLE_DEPRECATION_WARNINGS return ret; free_and_end: - avcodec_close(avctx); + ff_codec_close(avctx); goto end; } @@ -380,23 +361,12 @@ void avcodec_flush_buffers(AVCodecContext *avctx) "that doesn't support it\n"); return; } - if (avci->in_frame) - av_frame_unref(avci->in_frame); - if (avci->recon_frame) - av_frame_unref(avci->recon_frame); - } else { - av_packet_unref(avci->last_pkt_props); - av_packet_unref(avci->in_pkt); - - avctx->pts_correction_last_pts = - avctx->pts_correction_last_dts = INT64_MIN; - - av_bsf_flush(avci->bsf); - } + ff_encode_flush_buffers(avctx); + } else + ff_decode_flush_buffers(avctx); avci->draining = 0; avci->draining_done = 0; - avci->nb_draining_errors = 0; av_frame_unref(avci->buffer_frame); av_packet_unref(avci->buffer_pkt); @@ -428,12 +398,12 @@ void avsubtitle_free(AVSubtitle *sub) memset(sub, 0, sizeof(*sub)); } -av_cold int avcodec_close(AVCodecContext *avctx) +av_cold void ff_codec_close(AVCodecContext *avctx) { int i; if (!avctx) - return 0; + return; if (avcodec_is_open(avctx)) { AVCodecInternal *avci = avctx->internal; @@ -456,15 +426,15 @@ av_cold int avcodec_close(AVCodecContext *avctx) av_frame_free(&avci->in_frame); av_frame_free(&avci->recon_frame); - av_buffer_unref(&avci->pool); + ff_refstruct_unref(&avci->pool); - if (avctx->hwaccel && avctx->hwaccel->uninit) - avctx->hwaccel->uninit(avctx); - av_freep(&avci->hwaccel_priv_data); + ff_hwaccel_uninit(avctx); av_bsf_free(&avci->bsf); +#if FF_API_DROPCHANGED av_channel_layout_uninit(&avci->initial_ch_layout); +#endif #if CONFIG_LCMS2 ff_icc_context_uninit(&avci->icc); @@ -493,9 +463,15 @@ av_cold int avcodec_close(AVCodecContext *avctx) avctx->codec = NULL; avctx->active_thread_type = 0; +} +#if FF_API_AVCODEC_CLOSE +int avcodec_close(AVCodecContext *avctx) +{ + ff_codec_close(avctx); return 0; } +#endif static const char *unknown_if_null(const char *str) { @@ -615,6 +591,7 @@ void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode) enc->width, enc->height); if (av_log_get_level() >= AV_LOG_VERBOSE && + enc->coded_width && enc->coded_height && (enc->width != enc->coded_width || enc->height != enc->coded_height)) av_bprintf(&bprint, " (%dx%d)", @@ -652,12 +629,7 @@ void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode) if (enc->sample_rate) { av_bprintf(&bprint, "%d Hz, ", enc->sample_rate); } - { - char buf[512]; - int ret = av_channel_layout_describe(&enc->ch_layout, buf, sizeof(buf)); - if (ret >= 0) - av_bprintf(&bprint, "%s", buf); - } + av_channel_layout_describe_bprint(&enc->ch_layout, &bprint); if (enc->sample_fmt != AV_SAMPLE_FMT_NONE && (str = av_get_sample_fmt_name(enc->sample_fmt))) { av_bprintf(&bprint, ", %s", str); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 39881a1d2bc..83dc487251c 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -31,6 +31,7 @@ #include "libavutil/attributes.h" #include "libavutil/avutil.h" #include "libavutil/buffer.h" +#include "libavutil/channel_layout.h" #include "libavutil/dict.h" #include "libavutil/frame.h" #include "libavutil/log.h" @@ -38,8 +39,6 @@ #include "libavutil/rational.h" #include "codec.h" -#include "codec_desc.h" -#include "codec_par.h" #include "codec_id.h" #include "defs.h" #include "packet.h" @@ -49,8 +48,13 @@ * to avoid unnecessary rebuilds. When included externally, keep including * the full version information. */ #include "version.h" + +#include "codec_desc.h" +#include "codec_par.h" #endif +struct AVCodecParameters; + /** * @defgroup libavc libavcodec * Encoding/Decoding Library @@ -183,12 +187,16 @@ * @{ */ +#if FF_API_BUFFER_MIN_SIZE /** * @ingroup lavc_encoding * minimum encoding buffer size * Used to avoid some checks during header writing. + * @deprecated Unused: avcodec_receive_packet() does not work + * with preallocated packet buffers. */ #define AV_INPUT_BUFFER_MIN_SIZE 16384 +#endif /** * @ingroup lavc_encoding @@ -226,11 +234,15 @@ typedef struct RcOverride{ * Use qpel MC. */ #define AV_CODEC_FLAG_QPEL (1 << 4) +#if FF_API_DROPCHANGED /** * Don't output frames whose parameters differ from first * decoded frame in stream. + * + * @deprecated callers should implement this functionality in their own code */ #define AV_CODEC_FLAG_DROPCHANGED (1 << 5) +#endif /** * Request the encoder to output reconstructed frames, i.e.\ frames that would * be produced by decoding the encoded bistream. These frames may be retrieved @@ -239,11 +251,20 @@ typedef struct RcOverride{ * * Should only be used with encoders flagged with the * @ref AV_CODEC_CAP_ENCODER_RECON_FRAME capability. + * + * @note + * Each reconstructed frame returned by the encoder corresponds to the last + * encoded packet, i.e. the frames are returned in coded order rather than + * presentation order. + * + * @note + * Frame parameters (like pixel format or dimensions) do not have to match the + * AVCodecContext values. Make sure to use the values from the returned frame. */ #define AV_CODEC_FLAG_RECON_FRAME (1 << 6) /** * @par decoding - * Request the decoder to propagate each packets AVPacket.opaque and + * Request the decoder to propagate each packet's AVPacket.opaque and * AVPacket.opaque_ref to its corresponding output AVFrame. * * @par encoding: @@ -408,8 +429,6 @@ typedef struct RcOverride{ */ #define AV_GET_ENCODE_BUFFER_FLAG_REF (1 << 0) -struct AVCodecInternal; - /** * main external API structure. * New fields can be added to the end with minor version bumps. @@ -475,29 +494,6 @@ typedef struct AVCodecContext { */ int64_t bit_rate; - /** - * number of bits the bitstream is allowed to diverge from the reference. - * the reference can be CBR (for CBR pass1) or VBR (for pass2) - * - encoding: Set by user; unused for constant quantizer encoding. - * - decoding: unused - */ - int bit_rate_tolerance; - - /** - * Global quality for codecs which cannot change it per frame. - * This should be proportional to MPEG-1/2/4 qscale. - * - encoding: Set by user. - * - decoding: unused - */ - int global_quality; - - /** - * - encoding: Set by user. - * - decoding: unused - */ - int compression_level; -#define FF_COMPRESSION_DEFAULT -1 - /** * AV_CODEC_FLAG_*. * - encoding: Set by user. @@ -547,14 +543,38 @@ typedef struct AVCodecContext { */ AVRational time_base; + /** + * Timebase in which pkt_dts/pts and AVPacket.dts/pts are expressed. + * - encoding: unused. + * - decoding: set by user. + */ + AVRational pkt_timebase; + + /** + * - decoding: For codecs that store a framerate value in the compressed + * bitstream, the decoder may export it here. { 0, 1} when + * unknown. + * - encoding: May be used to signal the framerate of CFR content to an + * encoder. + */ + AVRational framerate; + +#if FF_API_TICKS_PER_FRAME /** * For some codecs, the time base is closer to the field rate than the frame rate. * Most notably, H.264 and MPEG-2 specify time_base as half of frame duration * if no telecine is used ... * * Set to time_base ticks per frame. Default 1, e.g., H.264/MPEG-2 set it to 2. + * + * @deprecated + * - decoding: Use AVCodecDescriptor.props & AV_CODEC_PROP_FIELDS + * - encoding: Set AVCodecContext.framerate instead + * */ + attribute_deprecated int ticks_per_frame; +#endif /** * Codec delay. @@ -613,11 +633,13 @@ typedef struct AVCodecContext { int coded_width, coded_height; /** - * the number of pictures in a group of pictures, or 0 for intra_only + * sample aspect ratio (0 if unknown) + * That is the width of a pixel divided by the height of the pixel. + * Numerator and denominator must be relatively prime and smaller than 256 for some video standards. * - encoding: Set by user. - * - decoding: unused + * - decoding: Set by libavcodec. */ - int gop_size; + AVRational sample_aspect_ratio; /** * Pixel format, see AV_PIX_FMT_xxx. @@ -634,6 +656,82 @@ typedef struct AVCodecContext { */ enum AVPixelFormat pix_fmt; + /** + * Nominal unaccelerated pixel format, see AV_PIX_FMT_xxx. + * - encoding: unused. + * - decoding: Set by libavcodec before calling get_format() + */ + enum AVPixelFormat sw_pix_fmt; + + /** + * Chromaticity coordinates of the source primaries. + * - encoding: Set by user + * - decoding: Set by libavcodec + */ + enum AVColorPrimaries color_primaries; + + /** + * Color Transfer Characteristic. + * - encoding: Set by user + * - decoding: Set by libavcodec + */ + enum AVColorTransferCharacteristic color_trc; + + /** + * YUV colorspace type. + * - encoding: Set by user + * - decoding: Set by libavcodec + */ + enum AVColorSpace colorspace; + + /** + * MPEG vs JPEG YUV range. + * - encoding: Set by user to override the default output color range value, + * If not specified, libavcodec sets the color range depending on the + * output format. + * - decoding: Set by libavcodec, can be set by the user to propagate the + * color range to components reading from the decoder context. + */ + enum AVColorRange color_range; + + /** + * This defines the location of chroma samples. + * - encoding: Set by user + * - decoding: Set by libavcodec + */ + enum AVChromaLocation chroma_sample_location; + + /** Field order + * - encoding: set by libavcodec + * - decoding: Set by user. + */ + enum AVFieldOrder field_order; + + /** + * number of reference frames + * - encoding: Set by user. + * - decoding: Set by lavc. + */ + int refs; + + /** + * Size of the frame reordering buffer in the decoder. + * For MPEG-2 it is 1 IPB or 0 low delay IP. + * - encoding: Set by libavcodec. + * - decoding: Set by libavcodec. + */ + int has_b_frames; + + /** + * slice flags + * - encoding: unused + * - decoding: Set by user. + */ + int slice_flags; +#define SLICE_FLAG_CODED_ORDER 0x0001 ///< draw_horiz_band() is called in coded order instead of display +#define SLICE_FLAG_ALLOW_FIELD 0x0002 ///< allow draw_horiz_band() with field slices (MPEG-2 field pics) +#define SLICE_FLAG_ALLOW_PLANE 0x0004 ///< allow draw_horiz_band() with 1 component at a time (SVQ1) + /** * If non NULL, 'draw_horiz_band' is called by the libavcodec * decoder to draw a horizontal band. It improves cache usage. Not @@ -712,14 +810,6 @@ typedef struct AVCodecContext { */ float b_quant_offset; - /** - * Size of the frame reordering buffer in the decoder. - * For MPEG-2 it is 1 IPB or 0 low delay IP. - * - encoding: Set by libavcodec. - * - decoding: Set by libavcodec. - */ - int has_b_frames; - /** * qscale factor between P- and I-frames * If > 0 then the last P-frame quantizer will be used (q = lastp_q * factor + offset). @@ -772,27 +862,11 @@ typedef struct AVCodecContext { float dark_masking; /** - * slice count - * - encoding: Set by libavcodec. - * - decoding: Set by user (or 0). - */ - int slice_count; - - /** - * slice offsets in the frame in bytes - * - encoding: Set/allocated by libavcodec. - * - decoding: Set/allocated by user (or NULL). - */ - int *slice_offset; - - /** - * sample aspect ratio (0 if unknown) - * That is the width of a pixel divided by the height of the pixel. - * Numerator and denominator must be relatively prime and smaller than 256 for some video standards. + * noise vs. sse weight for the nsse comparison function * - encoding: Set by user. - * - decoding: Set by libavcodec. + * - decoding: unused */ - AVRational sample_aspect_ratio; + int nsse_weight; /** * motion estimation comparison function @@ -880,16 +954,6 @@ typedef struct AVCodecContext { */ int me_range; - /** - * slice flags - * - encoding: unused - * - decoding: Set by user. - */ - int slice_flags; -#define SLICE_FLAG_CODED_ORDER 0x0001 ///< draw_horiz_band() is called in coded order instead of display -#define SLICE_FLAG_ALLOW_FIELD 0x0002 ///< allow draw_horiz_band() with field slices (MPEG-2 field pics) -#define SLICE_FLAG_ALLOW_PLANE 0x0004 ///< allow draw_horiz_band() with 1 component at a time (SVQ1) - /** * macroblock decision mode * - encoding: Set by user. @@ -918,6 +982,13 @@ typedef struct AVCodecContext { */ uint16_t *inter_matrix; + /** + * custom intra quantization matrix + * - encoding: Set by user, can be NULL. + * - decoding: unused. + */ + uint16_t *chroma_intra_matrix; + /** * precision of the intra DC coefficient - 8 * - encoding: Set by user. @@ -925,20 +996,6 @@ typedef struct AVCodecContext { */ int intra_dc_precision; - /** - * Number of macroblock rows at the top which are skipped. - * - encoding: unused - * - decoding: Set by user. - */ - int skip_top; - - /** - * Number of macroblock rows at the bottom which are skipped. - * - encoding: unused - * - decoding: Set by user. - */ - int skip_bottom; - /** * minimum MB Lagrange multiplier * - encoding: Set by user. @@ -967,11 +1024,11 @@ typedef struct AVCodecContext { int keyint_min; /** - * number of reference frames + * the number of pictures in a group of pictures, or 0 for intra_only * - encoding: Set by user. - * - decoding: Set by lavc. + * - decoding: unused */ - int refs; + int gop_size; /** * Note: Value depends upon the compare function used for fullpel ME. @@ -980,41 +1037,6 @@ typedef struct AVCodecContext { */ int mv0_threshold; - /** - * Chromaticity coordinates of the source primaries. - * - encoding: Set by user - * - decoding: Set by libavcodec - */ - enum AVColorPrimaries color_primaries; - - /** - * Color Transfer Characteristic. - * - encoding: Set by user - * - decoding: Set by libavcodec - */ - enum AVColorTransferCharacteristic color_trc; - - /** - * YUV colorspace type. - * - encoding: Set by user - * - decoding: Set by libavcodec - */ - enum AVColorSpace colorspace; - - /** - * MPEG vs JPEG YUV range. - * - encoding: Set by user - * - decoding: Set by libavcodec - */ - enum AVColorRange color_range; - - /** - * This defines the location of chroma samples. - * - encoding: Set by user - * - decoding: Set by libavcodec - */ - enum AVChromaLocation chroma_sample_location; - /** * Number of slices. * Indicates number of picture subdivisions. Used for parallelized @@ -1024,24 +1046,9 @@ typedef struct AVCodecContext { */ int slices; - /** Field order - * - encoding: set by libavcodec - * - decoding: Set by user. - */ - enum AVFieldOrder field_order; - /* audio only */ int sample_rate; ///< samples per second -#if FF_API_OLD_CHANNEL_LAYOUT - /** - * number of audio channels - * @deprecated use ch_layout.nb_channels - */ - attribute_deprecated - int channels; -#endif - /** * audio sample format * - encoding: Set by user. @@ -1049,6 +1056,14 @@ typedef struct AVCodecContext { */ enum AVSampleFormat sample_fmt; ///< sample format + /** + * Audio channel layout. + * - encoding: must be set by the caller, to one of AVCodec.ch_layouts. + * - decoding: may be set by the caller if known e.g. from the container. + * The decoder can then override during decoding as needed. + */ + AVChannelLayout ch_layout; + /* The following data should not be initialized. */ /** * Number of samples per channel in an audio frame. @@ -1061,21 +1076,6 @@ typedef struct AVCodecContext { */ int frame_size; -#if FF_API_AVCTX_FRAME_NUMBER - /** - * Frame counter, set by libavcodec. - * - * - decoding: total number of frames returned from the decoder so far. - * - encoding: total number of frames passed to the encoder so far. - * - * @note the counter is not incremented if encoding/decoding resulted in - * an error. - * @deprecated use frame_num instead - */ - attribute_deprecated - int frame_number; -#endif - /** * number of bytes per packet if constant and known or 0 * Used by some WAV based audio codecs. @@ -1089,26 +1089,6 @@ typedef struct AVCodecContext { */ int cutoff; -#if FF_API_OLD_CHANNEL_LAYOUT - /** - * Audio channel layout. - * - encoding: set by user. - * - decoding: set by user, may be overwritten by libavcodec. - * @deprecated use ch_layout - */ - attribute_deprecated - uint64_t channel_layout; - - /** - * Request decoder to use this channel layout if it can (0 for default) - * - encoding: unused - * - decoding: Set by user. - * @deprecated use "downmix" codec private option - */ - attribute_deprecated - uint64_t request_channel_layout; -#endif - /** * Type of service that the audio stream conveys. * - encoding: Set by user. @@ -1124,6 +1104,41 @@ typedef struct AVCodecContext { */ enum AVSampleFormat request_sample_fmt; + /** + * Audio only. The number of "priming" samples (padding) inserted by the + * encoder at the beginning of the audio. I.e. this number of leading + * decoded samples must be discarded by the caller to get the original audio + * without leading padding. + * + * - decoding: unused + * - encoding: Set by libavcodec. The timestamps on the output packets are + * adjusted by the encoder so that they always refer to the + * first sample of the data actually contained in the packet, + * including any added padding. E.g. if the timebase is + * 1/samplerate and the timestamp of the first input sample is + * 0, the timestamp of the first output packet will be + * -initial_padding. + */ + int initial_padding; + + /** + * Audio only. The amount of padding (in samples) appended by the encoder to + * the end of the audio. I.e. this number of decoded samples must be + * discarded by the caller from the end of the stream to get the original + * audio without any trailing padding. + * + * - decoding: unused + * - encoding: unused + */ + int trailing_padding; + + /** + * Number of samples to skip after a discontinuity + * - decoding: unused + * - encoding: set by libavcodec + */ + int seek_preroll; + /** * This callback is called at the beginning of each frame to get data * buffer(s) for it. There may be one contiguous buffer for all the data or @@ -1207,6 +1222,29 @@ typedef struct AVCodecContext { int (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, int flags); /* - encoding parameters */ + /** + * number of bits the bitstream is allowed to diverge from the reference. + * the reference can be CBR (for CBR pass1) or VBR (for pass2) + * - encoding: Set by user; unused for constant quantizer encoding. + * - decoding: unused + */ + int bit_rate_tolerance; + + /** + * Global quality for codecs which cannot change it per frame. + * This should be proportional to MPEG-1/2/4 qscale. + * - encoding: Set by user. + * - decoding: unused + */ + int global_quality; + + /** + * - encoding: Set by user. + * - decoding: unused + */ + int compression_level; +#define FF_COMPRESSION_DEFAULT -1 + float qcompress; ///< amount of qscale change between easy & hard scenes (0.0-1.0) float qblur; ///< amount of qscale smoothing over time (0.0-1.0) @@ -1234,7 +1272,7 @@ typedef struct AVCodecContext { /** * decoder bitstream buffer size * - encoding: Set by user. - * - decoding: unused + * - decoding: May be set by libavcodec. */ int rc_buffer_size; @@ -1381,22 +1419,6 @@ typedef struct AVCodecContext { */ int err_recognition; -#if FF_API_REORDERED_OPAQUE - /** - * opaque 64-bit number (generally a PTS) that will be reordered and - * output in AVFrame.reordered_opaque - * - encoding: Set by libavcodec to the reordered_opaque of the input - * frame corresponding to the last returned packet. Only - * supported by encoders with the - * AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE capability. - * - decoding: Set by user. - * - * @deprecated Use AV_CODEC_FLAG_COPY_OPAQUE instead - */ - attribute_deprecated - int64_t reordered_opaque; -#endif - /** * Hardware accelerator in use * - encoding: unused. @@ -1428,6 +1450,75 @@ typedef struct AVCodecContext { */ void *hwaccel_context; + /** + * A reference to the AVHWFramesContext describing the input (for encoding) + * or output (decoding) frames. The reference is set by the caller and + * afterwards owned (and freed) by libavcodec - it should never be read by + * the caller after being set. + * + * - decoding: This field should be set by the caller from the get_format() + * callback. The previous reference (if any) will always be + * unreffed by libavcodec before the get_format() call. + * + * If the default get_buffer2() is used with a hwaccel pixel + * format, then this AVHWFramesContext will be used for + * allocating the frame buffers. + * + * - encoding: For hardware encoders configured to use a hwaccel pixel + * format, this field should be set by the caller to a reference + * to the AVHWFramesContext describing input frames. + * AVHWFramesContext.format must be equal to + * AVCodecContext.pix_fmt. + * + * This field should be set before avcodec_open2() is called. + */ + AVBufferRef *hw_frames_ctx; + + /** + * A reference to the AVHWDeviceContext describing the device which will + * be used by a hardware encoder/decoder. The reference is set by the + * caller and afterwards owned (and freed) by libavcodec. + * + * This should be used if either the codec device does not require + * hardware frames or any that are used are to be allocated internally by + * libavcodec. If the user wishes to supply any of the frames used as + * encoder input or decoder output then hw_frames_ctx should be used + * instead. When hw_frames_ctx is set in get_format() for a decoder, this + * field will be ignored while decoding the associated stream segment, but + * may again be used on a following one after another get_format() call. + * + * For both encoders and decoders this field should be set before + * avcodec_open2() is called and must not be written to thereafter. + * + * Note that some decoders may require this field to be set initially in + * order to support hw_frames_ctx at all - in that case, all frames + * contexts used must be created on the same device. + */ + AVBufferRef *hw_device_ctx; + + /** + * Bit set of AV_HWACCEL_FLAG_* flags, which affect hardware accelerated + * decoding (if active). + * - encoding: unused + * - decoding: Set by user (either before avcodec_open2(), or in the + * AVCodecContext.get_format callback) + */ + int hwaccel_flags; + + /** + * Video decoding only. Sets the number of extra hardware frames which + * the decoder will allocate for use by the caller. This must be set + * before avcodec_open2() is called. + * + * Some hardware decoders require all frames that they will use for + * output to be defined in advance before decoding starts. For such + * decoders, the hardware frame pool must therefore be of a fixed size. + * The extra frames set here are on top of any number that the decoder + * needs internally in order to operate normally (for example, frames + * used as reference pictures). + */ + int extra_hw_frames; + /** * error * - encoding: Set by libavcodec if flags & AV_CODEC_FLAG_PSNR. @@ -1466,10 +1557,6 @@ typedef struct AVCodecContext { #define FF_IDCT_SIMPLEARMV6 17 #define FF_IDCT_FAAN 20 #define FF_IDCT_SIMPLENEON 22 -#if FF_API_IDCT_NONE -// formerly used by xvmc -#define FF_IDCT_NONE 24 -#endif #define FF_IDCT_SIMPLEAUTO 128 /** @@ -1486,13 +1573,6 @@ typedef struct AVCodecContext { */ int bits_per_raw_sample; - /** - * low resolution decoding, 1-> 1/2 size, 2->1/4 size - * - encoding: unused - * - decoding: Set by user. - */ - int lowres; - /** * thread count * is used to decide how many independent tasks should be passed to execute() @@ -1550,19 +1630,16 @@ typedef struct AVCodecContext { */ int (*execute2)(struct AVCodecContext *c, int (*func)(struct AVCodecContext *c2, void *arg, int jobnr, int threadnr), void *arg2, int *ret, int count); - /** - * noise vs. sse weight for the nsse comparison function - * - encoding: Set by user. - * - decoding: unused - */ - int nsse_weight; - /** * profile * - encoding: Set by user. * - decoding: Set by libavcodec. + * See the AV_PROFILE_* defines in defs.h. */ int profile; +#if FF_API_FF_PROFILE_LEVEL + /** @deprecated The following defines are deprecated; use AV_PROFILE_* + * in defs.h instead. */ #define FF_PROFILE_UNKNOWN -99 #define FF_PROFILE_RESERVED -100 @@ -1584,12 +1661,19 @@ typedef struct AVCodecContext { #define FF_PROFILE_DNXHR_HQX 4 #define FF_PROFILE_DNXHR_444 5 -#define FF_PROFILE_DTS 20 -#define FF_PROFILE_DTS_ES 30 -#define FF_PROFILE_DTS_96_24 40 -#define FF_PROFILE_DTS_HD_HRA 50 -#define FF_PROFILE_DTS_HD_MA 60 -#define FF_PROFILE_DTS_EXPRESS 70 +#define FF_PROFILE_DTS 20 +#define FF_PROFILE_DTS_ES 30 +#define FF_PROFILE_DTS_96_24 40 +#define FF_PROFILE_DTS_HD_HRA 50 +#define FF_PROFILE_DTS_HD_MA 60 +#define FF_PROFILE_DTS_EXPRESS 70 +#define FF_PROFILE_DTS_HD_MA_X 61 +#define FF_PROFILE_DTS_HD_MA_X_IMAX 62 + + +#define FF_PROFILE_EAC3_DDP_ATMOS 30 + +#define FF_PROFILE_TRUEHD_ATMOS 30 #define FF_PROFILE_MPEG2_422 0 #define FF_PROFILE_MPEG2_HIGH 1 @@ -1654,6 +1738,7 @@ typedef struct AVCodecContext { #define FF_PROFILE_HEVC_MAIN_10 2 #define FF_PROFILE_HEVC_MAIN_STILL_PICTURE 3 #define FF_PROFILE_HEVC_REXT 4 +#define FF_PROFILE_HEVC_SCC 9 #define FF_PROFILE_VVC_MAIN_10 1 #define FF_PROFILE_VVC_MAIN_10_444 33 @@ -1683,13 +1768,34 @@ typedef struct AVCodecContext { #define FF_PROFILE_KLVA_SYNC 0 #define FF_PROFILE_KLVA_ASYNC 1 +#define FF_PROFILE_EVC_BASELINE 0 +#define FF_PROFILE_EVC_MAIN 1 +#endif + /** - * level - * - encoding: Set by user. + * Encoding level descriptor. + * - encoding: Set by user, corresponds to a specific level defined by the + * codec, usually corresponding to the profile level, if not specified it + * is set to FF_LEVEL_UNKNOWN. * - decoding: Set by libavcodec. + * See AV_LEVEL_* in defs.h. */ int level; +#if FF_API_FF_PROFILE_LEVEL + /** @deprecated The following define is deprecated; use AV_LEVEL_UNKOWN + * in defs.h instead. */ #define FF_LEVEL_UNKNOWN -99 +#endif + + /** + * Properties of the stream that gets decoded + * - encoding: unused + * - decoding: set by libavcodec + */ + unsigned properties; +#define FF_CODEC_PROPERTY_LOSSLESS 0x00000001 +#define FF_CODEC_PROPERTY_CLOSED_CAPTIONS 0x00000002 +#define FF_CODEC_PROPERTY_FILM_GRAIN 0x00000004 /** * Skip loop filtering for selected frames. @@ -1713,72 +1819,46 @@ typedef struct AVCodecContext { enum AVDiscard skip_frame; /** - * Header containing style information for text subtitles. - * For SUBTITLE_ASS subtitle type, it should contain the whole ASS - * [Script Info] and [V4+ Styles] section, plus the [Events] line and - * the Format line following. It shouldn't include any Dialogue line. - * - encoding: Set/allocated/freed by user (before avcodec_open2()) - * - decoding: Set/allocated/freed by libavcodec (by avcodec_open2()) - */ - uint8_t *subtitle_header; - int subtitle_header_size; - - /** - * Audio only. The number of "priming" samples (padding) inserted by the - * encoder at the beginning of the audio. I.e. this number of leading - * decoded samples must be discarded by the caller to get the original audio - * without leading padding. + * Skip processing alpha if supported by codec. + * Note that if the format uses pre-multiplied alpha (common with VP6, + * and recommended due to better video quality/compression) + * the image will look as if alpha-blended onto a black background. + * However for formats that do not use pre-multiplied alpha + * there might be serious artefacts (though e.g. libswscale currently + * assumes pre-multiplied alpha anyway). * - * - decoding: unused - * - encoding: Set by libavcodec. The timestamps on the output packets are - * adjusted by the encoder so that they always refer to the - * first sample of the data actually contained in the packet, - * including any added padding. E.g. if the timebase is - * 1/samplerate and the timestamp of the first input sample is - * 0, the timestamp of the first output packet will be - * -initial_padding. + * - decoding: set by user + * - encoding: unused */ - int initial_padding; + int skip_alpha; /** - * - decoding: For codecs that store a framerate value in the compressed - * bitstream, the decoder may export it here. { 0, 1} when - * unknown. - * - encoding: May be used to signal the framerate of CFR content to an - * encoder. + * Number of macroblock rows at the top which are skipped. + * - encoding: unused + * - decoding: Set by user. */ - AVRational framerate; + int skip_top; /** - * Nominal unaccelerated pixel format, see AV_PIX_FMT_xxx. - * - encoding: unused. - * - decoding: Set by libavcodec before calling get_format() + * Number of macroblock rows at the bottom which are skipped. + * - encoding: unused + * - decoding: Set by user. */ - enum AVPixelFormat sw_pix_fmt; + int skip_bottom; /** - * Timebase in which pkt_dts/pts and AVPacket.dts/pts are. - * - encoding unused. - * - decoding set by user. + * low resolution decoding, 1-> 1/2 size, 2->1/4 size + * - encoding: unused + * - decoding: Set by user. */ - AVRational pkt_timebase; + int lowres; /** * AVCodecDescriptor * - encoding: unused. * - decoding: set by libavcodec. */ - const AVCodecDescriptor *codec_descriptor; - - /** - * Current statistics for PTS correction. - * - decoding: maintained and used by libavcodec, not intended to be used by user apps - * - encoding: unused - */ - int64_t pts_correction_num_faulty_pts; /// Number of incorrect PTS values so far - int64_t pts_correction_num_faulty_dts; /// Number of incorrect DTS values so far - int64_t pts_correction_last_pts; /// PTS of the last frame - int64_t pts_correction_last_dts; /// DTS of the last frame + const struct AVCodecDescriptor *codec_descriptor; /** * Character encoding of the input subtitles file. @@ -1800,32 +1880,15 @@ typedef struct AVCodecContext { #define FF_SUB_CHARENC_MODE_IGNORE 2 ///< neither convert the subtitles, nor check them for valid UTF-8 /** - * Skip processing alpha if supported by codec. - * Note that if the format uses pre-multiplied alpha (common with VP6, - * and recommended due to better video quality/compression) - * the image will look as if alpha-blended onto a black background. - * However for formats that do not use pre-multiplied alpha - * there might be serious artefacts (though e.g. libswscale currently - * assumes pre-multiplied alpha anyway). - * - * - decoding: set by user - * - encoding: unused - */ - int skip_alpha; - - /** - * Number of samples to skip after a discontinuity - * - decoding: unused - * - encoding: set by libavcodec - */ - int seek_preroll; - - /** - * custom intra quantization matrix - * - encoding: Set by user, can be NULL. - * - decoding: unused. + * Header containing style information for text subtitles. + * For SUBTITLE_ASS subtitle type, it should contain the whole ASS + * [Script Info] and [V4+ Styles] section, plus the [Events] line and + * the Format line following. It shouldn't include any Dialogue line. + * - encoding: Set/allocated/freed by user (before avcodec_open2()) + * - decoding: Set/allocated/freed by libavcodec (by avcodec_open2()) */ - uint16_t *chroma_intra_matrix; + int subtitle_header_size; + uint8_t *subtitle_header; /** * dump format separator. @@ -1843,59 +1906,24 @@ typedef struct AVCodecContext { */ char *codec_whitelist; - /** - * Properties of the stream that gets decoded - * - encoding: unused - * - decoding: set by libavcodec - */ - unsigned properties; -#define FF_CODEC_PROPERTY_LOSSLESS 0x00000001 -#define FF_CODEC_PROPERTY_CLOSED_CAPTIONS 0x00000002 -#define FF_CODEC_PROPERTY_FILM_GRAIN 0x00000004 - /** * Additional data associated with the entire coded stream. * - * - decoding: unused + * - decoding: may be set by user before calling avcodec_open2(). * - encoding: may be set by libavcodec after avcodec_open2(). */ AVPacketSideData *coded_side_data; int nb_coded_side_data; /** - * A reference to the AVHWFramesContext describing the input (for encoding) - * or output (decoding) frames. The reference is set by the caller and - * afterwards owned (and freed) by libavcodec - it should never be read by - * the caller after being set. - * - * - decoding: This field should be set by the caller from the get_format() - * callback. The previous reference (if any) will always be - * unreffed by libavcodec before the get_format() call. - * - * If the default get_buffer2() is used with a hwaccel pixel - * format, then this AVHWFramesContext will be used for - * allocating the frame buffers. - * - * - encoding: For hardware encoders configured to use a hwaccel pixel - * format, this field should be set by the caller to a reference - * to the AVHWFramesContext describing input frames. - * AVHWFramesContext.format must be equal to - * AVCodecContext.pix_fmt. - * - * This field should be set before avcodec_open2() is called. - */ - AVBufferRef *hw_frames_ctx; - - /** - * Audio only. The amount of padding (in samples) appended by the encoder to - * the end of the audio. I.e. this number of decoded samples must be - * discarded by the caller from the end of the stream to get the original - * audio without any trailing padding. + * Bit set of AV_CODEC_EXPORT_DATA_* flags, which affects the kind of + * metadata exported in frame, packet, or coded stream side data by + * decoders and encoders. * - * - decoding: unused - * - encoding: unused + * - decoding: set by user + * - encoding: set by user */ - int trailing_padding; + int export_side_data; /** * The number of pixels per image to maximally accept. @@ -1905,37 +1933,6 @@ typedef struct AVCodecContext { */ int64_t max_pixels; - /** - * A reference to the AVHWDeviceContext describing the device which will - * be used by a hardware encoder/decoder. The reference is set by the - * caller and afterwards owned (and freed) by libavcodec. - * - * This should be used if either the codec device does not require - * hardware frames or any that are used are to be allocated internally by - * libavcodec. If the user wishes to supply any of the frames used as - * encoder input or decoder output then hw_frames_ctx should be used - * instead. When hw_frames_ctx is set in get_format() for a decoder, this - * field will be ignored while decoding the associated stream segment, but - * may again be used on a following one after another get_format() call. - * - * For both encoders and decoders this field should be set before - * avcodec_open2() is called and must not be written to thereafter. - * - * Note that some decoders may require this field to be set initially in - * order to support hw_frames_ctx at all - in that case, all frames - * contexts used must be created on the same device. - */ - AVBufferRef *hw_device_ctx; - - /** - * Bit set of AV_HWACCEL_FLAG_* flags, which affect hardware accelerated - * decoding (if active). - * - encoding: unused - * - decoding: Set by user (either before avcodec_open2(), or in the - * AVCodecContext.get_format callback) - */ - int hwaccel_flags; - /** * Video decoding only. Certain video codecs support cropping, meaning that * only a sub-rectangle of the decoded frame is intended for display. This @@ -1963,20 +1960,6 @@ typedef struct AVCodecContext { */ int apply_cropping; - /* - * Video decoding only. Sets the number of extra hardware frames which - * the decoder will allocate for use by the caller. This must be set - * before avcodec_open2() is called. - * - * Some hardware decoders require all frames that they will use for - * output to be defined in advance before decoding starts. For such - * decoders, the hardware frame pool must therefore be of a fixed size. - * The extra frames set here are on top of any number that the decoder - * needs internally in order to operate normally (for example, frames - * used as reference pictures). - */ - int extra_hw_frames; - /** * The percentage of damaged samples to discard a frame. * @@ -1993,16 +1976,6 @@ typedef struct AVCodecContext { */ int64_t max_samples; - /** - * Bit set of AV_CODEC_EXPORT_DATA_* flags, which affects the kind of - * metadata exported in frame, packet, or coded stream side data by - * decoders and encoders. - * - * - decoding: set by user - * - encoding: set by user - */ - int export_side_data; - /** * This callback is called at the beginning of each packet to get a data * buffer for it. @@ -2045,14 +2018,6 @@ typedef struct AVCodecContext { */ int (*get_encode_buffer)(struct AVCodecContext *s, AVPacket *pkt, int flags); - /** - * Audio channel layout. - * - encoding: must be set by the caller, to one of AVCodec.ch_layouts. - * - decoding: may be set by the caller if known e.g. from the container. - * The decoder can then override during decoding as needed. - */ - AVChannelLayout ch_layout; - /** * Frame counter, set by libavcodec. * @@ -2063,6 +2028,53 @@ typedef struct AVCodecContext { * an error. */ int64_t frame_num; + + /** + * Decoding only. May be set by the caller before avcodec_open2() to an + * av_malloc()'ed array (or via AVOptions). Owned and freed by the decoder + * afterwards. + * + * Side data attached to decoded frames may come from several sources: + * 1. coded_side_data, which the decoder will for certain types translate + * from packet-type to frame-type and attach to frames; + * 2. side data attached to an AVPacket sent for decoding (same + * considerations as above); + * 3. extracted from the coded bytestream. + * The first two cases are supplied by the caller and typically come from a + * container. + * + * This array configures decoder behaviour in cases when side data of the + * same type is present both in the coded bytestream and in the + * user-supplied side data (items 1. and 2. above). In all cases, at most + * one instance of each side data type will be attached to output frames. By + * default it will be the bytestream side data. Adding an + * AVPacketSideDataType value to this array will flip the preference for + * this type, thus making the decoder prefer user-supplied side data over + * bytestream. In case side data of the same type is present both in + * coded_data and attacked to a packet, the packet instance always has + * priority. + * + * The array may also contain a single -1, in which case the preference is + * switched for all side data types. + */ + int *side_data_prefer_packet; + /** + * Number of entries in side_data_prefer_packet. + */ + unsigned nb_side_data_prefer_packet; + + /** + * Array containing static side data, such as HDR10 CLL / MDCV structures. + * Side data entries should be allocated by usage of helpers defined in + * libavutil/frame.h. + * + * - encoding: may be set by user before calling avcodec_open2() for + * encoder configuration. Afterwards owned and freed by the + * encoder. + * - decoding: unused + */ + AVFrameSideData **decoded_side_data; + int nb_decoded_side_data; } AVCodecContext; /** @@ -2107,120 +2119,6 @@ typedef struct AVHWAccel { * see AV_HWACCEL_CODEC_CAP_* */ int capabilities; - - /***************************************************************** - * No fields below this line are part of the public API. They - * may not be used outside of libavcodec and can be changed and - * removed at will. - * New public fields should be added right above. - ***************************************************************** - */ - - /** - * Allocate a custom buffer - */ - int (*alloc_frame)(AVCodecContext *avctx, AVFrame *frame); - - /** - * Called at the beginning of each frame or field picture. - * - * Meaningful frame information (codec specific) is guaranteed to - * be parsed at this point. This function is mandatory. - * - * Note that buf can be NULL along with buf_size set to 0. - * Otherwise, this means the whole frame is available at this point. - * - * @param avctx the codec context - * @param buf the frame data buffer base - * @param buf_size the size of the frame in bytes - * @return zero if successful, a negative value otherwise - */ - int (*start_frame)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size); - - /** - * Callback for parameter data (SPS/PPS/VPS etc). - * - * Useful for hardware decoders which keep persistent state about the - * video parameters, and need to receive any changes to update that state. - * - * @param avctx the codec context - * @param type the nal unit type - * @param buf the nal unit data buffer - * @param buf_size the size of the nal unit in bytes - * @return zero if successful, a negative value otherwise - */ - int (*decode_params)(AVCodecContext *avctx, int type, const uint8_t *buf, uint32_t buf_size); - - /** - * Callback for each slice. - * - * Meaningful slice information (codec specific) is guaranteed to - * be parsed at this point. This function is mandatory. - * - * @param avctx the codec context - * @param buf the slice data buffer base - * @param buf_size the size of the slice in bytes - * @return zero if successful, a negative value otherwise - */ - int (*decode_slice)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size); - - /** - * Called at the end of each frame or field picture. - * - * The whole picture is parsed at this point and can now be sent - * to the hardware accelerator. This function is mandatory. - * - * @param avctx the codec context - * @return zero if successful, a negative value otherwise - */ - int (*end_frame)(AVCodecContext *avctx); - - /** - * Size of per-frame hardware accelerator private data. - * - * Private data is allocated with av_mallocz() before - * AVCodecContext.get_buffer() and deallocated after - * AVCodecContext.release_buffer(). - */ - int frame_priv_data_size; - - /** - * Initialize the hwaccel private data. - * - * This will be called from ff_get_format(), after hwaccel and - * hwaccel_context are set and the hwaccel private data in AVCodecInternal - * is allocated. - */ - int (*init)(AVCodecContext *avctx); - - /** - * Uninitialize the hwaccel private data. - * - * This will be called from get_format() or avcodec_close(), after hwaccel - * and hwaccel_context are already uninitialized. - */ - int (*uninit)(AVCodecContext *avctx); - - /** - * Size of the private data to allocate in - * AVCodecInternal.hwaccel_priv_data. - */ - int priv_data_size; - - /** - * Internal hwaccel capabilities. - */ - int caps_internal; - - /** - * Fill the given hw_frames context with current codec parameters. Called - * from get_format. Refer to avcodec_get_hw_frames_parameters() for - * details. - * - * This CAN be called before AVHWAccel.init is called, and you must assume - * that avctx->hwaccel_priv_data is invalid. - */ - int (*frame_params)(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx); } AVHWAccel; /** @@ -2313,6 +2211,7 @@ typedef struct AVSubtitleRect { uint8_t *data[4]; int linesize[4]; + int flags; enum AVSubtitleType type; char *text; ///< 0 terminated plain UTF-8 text @@ -2323,8 +2222,6 @@ typedef struct AVSubtitleRect { * struct. */ char *ass; - - int flags; } AVSubtitleRect; typedef struct AVSubtitle { @@ -2395,7 +2292,7 @@ const AVClass *avcodec_get_subtitle_rect_class(void); * * @return >= 0 on success, a negative AVERROR code on failure */ -int avcodec_parameters_from_context(AVCodecParameters *par, +int avcodec_parameters_from_context(struct AVCodecParameters *par, const AVCodecContext *codec); /** @@ -2407,7 +2304,7 @@ int avcodec_parameters_from_context(AVCodecParameters *par, * @return >= 0 on success, a negative AVERROR code on failure. */ int avcodec_parameters_to_context(AVCodecContext *codec, - const AVCodecParameters *par); + const struct AVCodecParameters *par); /** * Initialize the AVCodecContext to use the given AVCodec. Prior to using this @@ -2417,9 +2314,16 @@ int avcodec_parameters_to_context(AVCodecContext *codec, * avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for * retrieving a codec. * - * @note Always call this function before using decoding routines (such as - * @ref avcodec_receive_frame()). + * Depending on the codec, you might need to set options in the codec context + * also for decoding (e.g. width, height, or the pixel or audio sample format in + * the case the information is not available in the bitstream, as when decoding + * raw audio or video). * + * Options in the codec context can be set either by setting them in the options + * AVDictionary, or by setting the values in the context itself, directly or by + * using the av_opt_set() API before calling this function. + * + * Example: * @code * av_dict_set(&opts, "b", "2.5M", 0); * codec = avcodec_find_decoder(AV_CODEC_ID_H264); @@ -2432,20 +2336,40 @@ int avcodec_parameters_to_context(AVCodecContext *codec, * exit(1); * @endcode * + * In the case AVCodecParameters are available (e.g. when demuxing a stream + * using libavformat, and accessing the AVStream contained in the demuxer), the + * codec parameters can be copied to the codec context using + * avcodec_parameters_to_context(), as in the following example: + * + * @code + * AVStream *stream = ...; + * context = avcodec_alloc_context3(codec); + * if (avcodec_parameters_to_context(context, stream->codecpar) < 0) + * exit(1); + * if (avcodec_open2(context, codec, NULL) < 0) + * exit(1); + * @endcode + * + * @note Always call this function before using decoding routines (such as + * @ref avcodec_receive_frame()). + * * @param avctx The context to initialize. * @param codec The codec to open this context for. If a non-NULL codec has been * previously passed to avcodec_alloc_context3() or * for this context, then this parameter MUST be either NULL or * equal to the previously passed codec. - * @param options A dictionary filled with AVCodecContext and codec-private options. - * On return this object will be filled with options that were not found. + * @param options A dictionary filled with AVCodecContext and codec-private + * options, which are set on top of the options already set in + * avctx, can be NULL. On return this object will be filled with + * options that were not found in the avctx codec context. * * @return zero on success, a negative value on error * @see avcodec_alloc_context3(), avcodec_find_decoder(), avcodec_find_encoder(), - * av_dict_set(), av_opt_find(). + * av_dict_set(), av_opt_set(), av_opt_find(), avcodec_parameters_to_context() */ int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options); +#if FF_API_AVCODEC_CLOSE /** * Close a given AVCodecContext and free all the data associated with it * (but not the AVCodecContext itself). @@ -2454,12 +2378,14 @@ int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **op * the codec-specific data allocated in avcodec_alloc_context3() with a non-NULL * codec. Subsequent calls will do nothing. * - * @note Do not use this function. Use avcodec_free_context() to destroy a + * @deprecated Do not use this function. Use avcodec_free_context() to destroy a * codec context (either open or closed). Opening and closing a codec context * multiple times is not supported anymore -- use multiple codec contexts * instead. */ +attribute_deprecated int avcodec_close(AVCodecContext *avctx); +#endif /** * Free all allocated data in the given subtitle struct. @@ -2510,34 +2436,6 @@ void avcodec_align_dimensions(AVCodecContext *s, int *width, int *height); void avcodec_align_dimensions2(AVCodecContext *s, int *width, int *height, int linesize_align[AV_NUM_DATA_POINTERS]); -#ifdef FF_API_AVCODEC_CHROMA_POS -/** - * Converts AVChromaLocation to swscale x/y chroma position. - * - * The positions represent the chroma (0,0) position in a coordinates system - * with luma (0,0) representing the origin and luma(1,1) representing 256,256 - * - * @param xpos horizontal chroma sample position - * @param ypos vertical chroma sample position - * @deprecated Use av_chroma_location_enum_to_pos() instead. - */ - attribute_deprecated -int avcodec_enum_to_chroma_pos(int *xpos, int *ypos, enum AVChromaLocation pos); - -/** - * Converts swscale x/y chroma position to AVChromaLocation. - * - * The positions represent the chroma (0,0) position in a coordinates system - * with luma (0,0) representing the origin and luma(1,1) representing 256,256 - * - * @param xpos horizontal chroma sample position - * @param ypos vertical chroma sample position - * @deprecated Use av_chroma_location_pos_to_enum() instead. - */ - attribute_deprecated -enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos); -#endif - /** * Decode a subtitle message. * Return a negative value on error, otherwise return the number of bytes used. @@ -2618,7 +2516,7 @@ int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt); /** * Return decoded output data from a decoder or encoder (when the - * AV_CODEC_FLAG_RECON_FRAME flag is used). + * @ref AV_CODEC_FLAG_RECON_FRAME flag is used). * * @param avctx codec context * @param frame This will be set to a reference-counted video or audio @@ -2632,10 +2530,7 @@ int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt); * @retval AVERROR_EOF the codec has been fully flushed, and there will be * no more output frames * @retval AVERROR(EINVAL) codec not opened, or it is an encoder without the - * AV_CODEC_FLAG_RECON_FRAME flag enabled - * @retval AVERROR_INPUT_CHANGED current decoded frame has changed parameters with - * respect to first decoded frame. Applicable when flag - * AV_CODEC_FLAG_DROPCHANGED is set. + * @ref AV_CODEC_FLAG_RECON_FRAME flag enabled * @retval "other negative error code" legitimate decoding errors */ int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame); diff --git a/libavcodec/avcodec_internal.h b/libavcodec/avcodec_internal.h new file mode 100644 index 00000000000..4d1cb3a3145 --- /dev/null +++ b/libavcodec/avcodec_internal.h @@ -0,0 +1,61 @@ +/* + * 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 + */ + +/* + * APIs internal to the generic codec layer. + * + * MUST NOT be included by individual encoders or decoders. + */ + +#ifndef AVCODEC_AVCODEC_INTERNAL_H +#define AVCODEC_AVCODEC_INTERNAL_H + +struct AVCodecContext; +struct AVFrame; + +/** + * avcodec_receive_frame() implementation for decoders. + */ +int ff_decode_receive_frame(struct AVCodecContext *avctx, struct AVFrame *frame); + +/** + * avcodec_receive_frame() implementation for encoders. + */ +int ff_encode_receive_frame(struct AVCodecContext *avctx, struct AVFrame *frame); + +/* + * Perform encoder initialization and validation. + * Called when opening the encoder, before the FFCodec.init() call. + */ +int ff_encode_preinit(struct AVCodecContext *avctx); + +/** + * Perform decoder initialization and validation. + * Called when opening the decoder, before the FFCodec.init() call. + */ +int ff_decode_preinit(struct AVCodecContext *avctx); + +void ff_decode_flush_buffers(struct AVCodecContext *avctx); +void ff_encode_flush_buffers(struct AVCodecContext *avctx); + +struct AVCodecInternal *ff_decode_internal_alloc(void); +struct AVCodecInternal *ff_encode_internal_alloc(void); + +void ff_codec_close(struct AVCodecContext *avctx); + +#endif // AVCODEC_AVCODEC_INTERNAL_H diff --git a/libavcodec/avdct.c b/libavcodec/avdct.c index e8fa41f73be..37266f4b4ed 100644 --- a/libavcodec/avdct.c +++ b/libavcodec/avdct.c @@ -33,29 +33,29 @@ #define D AV_OPT_FLAG_DECODING_PARAM static const AVOption avdct_options[] = { -{"dct", "DCT algorithm", OFFSET(dct_algo), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, V|E, "dct"}, -{"auto", "autoselect a good one", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_AUTO }, INT_MIN, INT_MAX, V|E, "dct"}, -{"fastint", "fast integer (experimental / for debugging)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_FASTINT }, INT_MIN, INT_MAX, V|E, "dct"}, -{"int", "accurate integer", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_INT }, INT_MIN, INT_MAX, V|E, "dct"}, -{"mmx", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_MMX }, INT_MIN, INT_MAX, V|E, "dct"}, -{"altivec", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_ALTIVEC }, INT_MIN, INT_MAX, V|E, "dct"}, -{"faan", "floating point AAN DCT (experimental / for debugging)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_FAAN }, INT_MIN, INT_MAX, V|E, "dct"}, - -{"idct", "select IDCT implementation", OFFSET(idct_algo), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, V|E|D, "idct"}, -{"auto", "autoselect a good one", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_AUTO }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"int", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_INT }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"simple", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLE }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"simplemmx", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEMMX }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"arm", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_ARM }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"altivec", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_ALTIVEC }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"simplearm", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEARM }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"simplearmv5te", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEARMV5TE }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"simplearmv6", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEARMV6 }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"simpleneon", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLENEON }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"xvid", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_XVID }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"xvidmmx", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_XVID }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"faani", "floating point AAN IDCT (experimental / for debugging)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_FAAN }, INT_MIN, INT_MAX, V|D|E, "idct"}, -{"simpleauto", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEAUTO }, INT_MIN, INT_MAX, V|E|D, "idct"}, +{"dct", "DCT algorithm", OFFSET(dct_algo), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, V|E, .unit = "dct"}, +{"auto", "autoselect a good one", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_AUTO }, INT_MIN, INT_MAX, V|E, .unit = "dct"}, +{"fastint", "fast integer (experimental / for debugging)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_FASTINT }, INT_MIN, INT_MAX, V|E, .unit = "dct"}, +{"int", "accurate integer", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_INT }, INT_MIN, INT_MAX, V|E, .unit = "dct"}, +{"mmx", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_MMX }, INT_MIN, INT_MAX, V|E, .unit = "dct"}, +{"altivec", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_ALTIVEC }, INT_MIN, INT_MAX, V|E, .unit = "dct"}, +{"faan", "floating point AAN DCT (experimental / for debugging)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_FAAN }, INT_MIN, INT_MAX, V|E, .unit = "dct"}, + +{"idct", "select IDCT implementation", OFFSET(idct_algo), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, V|E|D, .unit = "idct"}, +{"auto", "autoselect a good one", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_AUTO }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"int", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_INT }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"simple", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLE }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"simplemmx", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEMMX }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"arm", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_ARM }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"altivec", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_ALTIVEC }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"simplearm", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEARM }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"simplearmv5te", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEARMV5TE }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"simplearmv6", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEARMV6 }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"simpleneon", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLENEON }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"xvid", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_XVID }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"xvidmmx", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_XVID }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"faani", "floating point AAN IDCT (experimental / for debugging)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_FAAN }, INT_MIN, INT_MAX, V|D|E, .unit = "idct"}, +{"simpleauto", "experimental / for debugging", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEAUTO }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, {"bits_per_sample", "", OFFSET(bits_per_sample), AV_OPT_TYPE_INT, {.i64 = 8 }, 0, 14, 0,}, {NULL}, diff --git a/libavcodec/avfft.c b/libavcodec/avfft.c index 2200f37708f..627fd7a0be2 100644 --- a/libavcodec/avfft.c +++ b/libavcodec/avfft.c @@ -16,130 +16,253 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include +#include + #include "libavutil/attributes.h" +#include "libavutil/macros.h" #include "libavutil/mem.h" +#include "libavutil/tx.h" #include "avfft.h" -#include "fft.h" -#include "rdft.h" -#include "dct.h" + +typedef struct AVTXWrapper { + AVTXContext *ctx; + av_tx_fn fn; + + AVTXContext *ctx2; + av_tx_fn fn2; + + ptrdiff_t stride; + int len; + int inv; + + float *tmp; + int out_of_place; +} AVTXWrapper; /* FFT */ FFTContext *av_fft_init(int nbits, int inverse) { - FFTContext *s = av_mallocz(sizeof(*s)); + int ret; + float scale = 1.0f; + AVTXWrapper *s = av_mallocz(sizeof(*s)); + if (!s) + return NULL; - if (s && ff_fft_init(s, nbits, inverse)) - av_freep(&s); + ret = av_tx_init(&s->ctx, &s->fn, AV_TX_FLOAT_FFT, inverse, 1 << nbits, + &scale, AV_TX_INPLACE); + if (ret < 0) { + av_free(s); + return NULL; + } - return s; + return (FFTContext *)s; } void av_fft_permute(FFTContext *s, FFTComplex *z) { - s->fft_permute(s, z); + /* Empty */ } void av_fft_calc(FFTContext *s, FFTComplex *z) { - s->fft_calc(s, z); + AVTXWrapper *w = (AVTXWrapper *)s; + w->fn(w->ctx, z, (void *)z, sizeof(AVComplexFloat)); } av_cold void av_fft_end(FFTContext *s) { if (s) { - ff_fft_end(s); - av_free(s); + AVTXWrapper *w = (AVTXWrapper *)s; + av_tx_uninit(&w->ctx); + av_tx_uninit(&w->ctx2); + av_free(w); } } -#if CONFIG_MDCT - FFTContext *av_mdct_init(int nbits, int inverse, double scale) { - FFTContext *s = av_malloc(sizeof(*s)); + int ret; + float scale_f = scale; + AVTXWrapper *s = av_mallocz(sizeof(*s)); + if (!s) + return NULL; - if (s && ff_mdct_init(s, nbits, inverse, scale)) - av_freep(&s); + ret = av_tx_init(&s->ctx, &s->fn, AV_TX_FLOAT_MDCT, inverse, 1 << (nbits - 1), &scale_f, 0); + if (ret < 0) { + av_free(s); + return NULL; + } - return s; + if (inverse) { + ret = av_tx_init(&s->ctx2, &s->fn2, AV_TX_FLOAT_MDCT, inverse, 1 << (nbits - 1), + &scale_f, AV_TX_FULL_IMDCT); + if (ret < 0) { + av_tx_uninit(&s->ctx); + av_free(s); + return NULL; + } + } + + return (FFTContext *)s; } void av_imdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input) { - s->imdct_calc(s, output, input); + AVTXWrapper *w = (AVTXWrapper *)s; + w->fn2(w->ctx2, output, (void *)input, sizeof(float)); } void av_imdct_half(FFTContext *s, FFTSample *output, const FFTSample *input) { - s->imdct_half(s, output, input); + AVTXWrapper *w = (AVTXWrapper *)s; + w->fn(w->ctx, output, (void *)input, sizeof(float)); } void av_mdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input) { - s->mdct_calc(s, output, input); + AVTXWrapper *w = (AVTXWrapper *)s; + w->fn(w->ctx, output, (void *)input, sizeof(float)); } av_cold void av_mdct_end(FFTContext *s) { if (s) { - ff_mdct_end(s); - av_free(s); + AVTXWrapper *w = (AVTXWrapper *)s; + av_tx_uninit(&w->ctx2); + av_tx_uninit(&w->ctx); + av_free(w); } } -#endif /* CONFIG_MDCT */ - -#if CONFIG_RDFT - RDFTContext *av_rdft_init(int nbits, enum RDFTransformType trans) { - RDFTContext *s = av_malloc(sizeof(*s)); + int ret; + float scale = trans == IDFT_C2R ? 0.5f : 1.0f; + AVTXWrapper *s; + + /* The other 2 modes are unconventional, do not form an orthogonal + * transform, have never been useful, and so they're not implemented. */ + if (trans != IDFT_C2R && trans != DFT_R2C) + return NULL; + + s = av_mallocz(sizeof(*s)); + if (!s) + return NULL; + + ret = av_tx_init(&s->ctx, &s->fn, AV_TX_FLOAT_RDFT, trans == IDFT_C2R, + 1 << nbits, &scale, 0x0); + if (ret < 0) { + av_free(s); + return NULL; + } - if (s && ff_rdft_init(s, nbits, trans)) - av_freep(&s); + s->stride = (trans == DFT_C2R) ? sizeof(float) : sizeof(AVComplexFloat); + s->len = 1 << nbits; + s->inv = trans == IDFT_C2R; - return s; + s->tmp = av_malloc((s->len + 2)*sizeof(float)); + if (!s->tmp) { + av_tx_uninit(&s->ctx); + av_free(s); + return NULL; + } + + return (RDFTContext *)s; } void av_rdft_calc(RDFTContext *s, FFTSample *data) { - s->rdft_calc(s, data); + AVTXWrapper *w = (AVTXWrapper *)s; + float *src = w->inv ? w->tmp : (float *)data; + float *dst = w->inv ? (float *)data : w->tmp; + + if (w->inv) { + memcpy(src, data, w->len*sizeof(float)); + + src[w->len] = src[1]; + src[1] = 0.0f; + } + + w->fn(w->ctx, dst, (void *)src, w->stride); + + if (!w->inv) { + dst[1] = dst[w->len]; + memcpy(data, dst, w->len*sizeof(float)); + } } av_cold void av_rdft_end(RDFTContext *s) { if (s) { - ff_rdft_end(s); - av_free(s); + AVTXWrapper *w = (AVTXWrapper *)s; + av_tx_uninit(&w->ctx); + av_free(w->tmp); + av_free(w); } } -#endif /* CONFIG_RDFT */ - -#if CONFIG_DCT - DCTContext *av_dct_init(int nbits, enum DCTTransformType inverse) { - DCTContext *s = av_malloc(sizeof(*s)); + int ret; + const float scale_map[] = { + [DCT_II] = 0.5f, + [DCT_III] = 1.0f / (1 << nbits), + [DCT_I] = 0.5f, + [DST_I] = 2.0f, + }; + static const enum AVTXType type_map[] = { + [DCT_II] = AV_TX_FLOAT_DCT, + [DCT_III] = AV_TX_FLOAT_DCT, + [DCT_I] = AV_TX_FLOAT_DCT_I, + [DST_I] = AV_TX_FLOAT_DST_I, + }; + + AVTXWrapper *s = av_mallocz(sizeof(*s)); + if (!s) + return NULL; + + s->len = (1 << nbits); + s->out_of_place = (inverse == DCT_I) || (inverse == DST_I); + + ret = av_tx_init(&s->ctx, &s->fn, type_map[inverse], + (inverse == DCT_III), 1 << (nbits - (inverse == DCT_III)), + &scale_map[inverse], s->out_of_place ? 0 : AV_TX_INPLACE); + if (ret < 0) { + av_free(s); + return NULL; + } - if (s && ff_dct_init(s, nbits, inverse)) - av_freep(&s); + if (s->out_of_place) { + s->tmp = av_malloc((1 << (nbits + 1))*sizeof(float)); + if (!s->tmp) { + av_tx_uninit(&s->ctx); + av_free(s); + return NULL; + } + } - return s; + return (DCTContext *)s; } void av_dct_calc(DCTContext *s, FFTSample *data) { - s->dct_calc(s, data); + AVTXWrapper *w = (AVTXWrapper *)s; + if (w->out_of_place) { + memcpy(w->tmp, data, w->len*sizeof(float)); + w->fn(w->ctx, (void *)data, w->tmp, sizeof(float)); + } else { + w->fn(w->ctx, data, (void *)data, sizeof(float)); + } } av_cold void av_dct_end(DCTContext *s) { if (s) { - ff_dct_end(s); - av_free(s); + AVTXWrapper *w = (AVTXWrapper *)s; + av_tx_uninit(&w->ctx); + av_free(w->tmp); + av_free(w); } } - -#endif /* CONFIG_DCT */ diff --git a/libavcodec/avfft.h b/libavcodec/avfft.h index 0c0f9b8d8da..e3a0da1eb91 100644 --- a/libavcodec/avfft.h +++ b/libavcodec/avfft.h @@ -19,6 +19,10 @@ #ifndef AVCODEC_AVFFT_H #define AVCODEC_AVFFT_H +#include "libavutil/attributes.h" +#include "version_major.h" +#if FF_API_AVFFT + /** * @file * @ingroup lavc_fft @@ -44,26 +48,42 @@ typedef struct FFTContext FFTContext; * Set up a complex FFT. * @param nbits log2 of the length of the input array * @param inverse if 0 perform the forward transform, if 1 perform the inverse + * @deprecated use av_tx_init from libavutil/tx.h with a type of AV_TX_FLOAT_FFT */ +attribute_deprecated FFTContext *av_fft_init(int nbits, int inverse); /** * Do the permutation needed BEFORE calling ff_fft_calc(). + * @deprecated without replacement */ +attribute_deprecated void av_fft_permute(FFTContext *s, FFTComplex *z); /** * Do a complex FFT with the parameters defined in av_fft_init(). The * input data must be permuted before. No 1.0/sqrt(n) normalization is done. + * @deprecated use the av_tx_fn value returned by av_tx_init, which also does permutation */ +attribute_deprecated void av_fft_calc(FFTContext *s, FFTComplex *z); +attribute_deprecated void av_fft_end(FFTContext *s); +/** + * @deprecated use av_tx_init from libavutil/tx.h with a type of AV_TX_FLOAT_MDCT, + * with a flag of AV_TX_FULL_IMDCT for a replacement to av_imdct_calc. + */ +attribute_deprecated FFTContext *av_mdct_init(int nbits, int inverse, double scale); +attribute_deprecated void av_imdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input); +attribute_deprecated void av_imdct_half(FFTContext *s, FFTSample *output, const FFTSample *input); +attribute_deprecated void av_mdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input); +attribute_deprecated void av_mdct_end(FFTContext *s); /* Real Discrete Fourier Transform */ @@ -81,9 +101,14 @@ typedef struct RDFTContext RDFTContext; * Set up a real FFT. * @param nbits log2 of the length of the input array * @param trans the type of transform + * + * @deprecated use av_tx_init from libavutil/tx.h with a type of AV_TX_FLOAT_RDFT */ +attribute_deprecated RDFTContext *av_rdft_init(int nbits, enum RDFTransformType trans); +attribute_deprecated void av_rdft_calc(RDFTContext *s, FFTSample *data); +attribute_deprecated void av_rdft_end(RDFTContext *s); /* Discrete Cosine Transform */ @@ -106,13 +131,19 @@ enum DCTTransformType { * @param type the type of transform * * @note the first element of the input of DST-I is ignored + * + * @deprecated use av_tx_init from libavutil/tx.h with an appropriate type of AV_TX_FLOAT_DCT */ +attribute_deprecated DCTContext *av_dct_init(int nbits, enum DCTTransformType type); +attribute_deprecated void av_dct_calc(DCTContext *s, FFTSample *data); +attribute_deprecated void av_dct_end (DCTContext *s); /** * @} */ +#endif /* FF_API_AVFFT */ #endif /* AVCODEC_AVFFT_H */ diff --git a/libavcodec/avpacket.c b/libavcodec/avpacket.c index 5fef65e97ae..e118bbaad1b 100644 --- a/libavcodec/avpacket.c +++ b/libavcodec/avpacket.c @@ -22,6 +22,7 @@ #include #include "libavutil/avassert.h" +#include "libavutil/avutil.h" #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/mem.h" @@ -300,6 +301,10 @@ const char *av_packet_side_data_name(enum AVPacketSideDataType type) case AV_PKT_DATA_DOVI_CONF: return "DOVI configuration record"; case AV_PKT_DATA_S12M_TIMECODE: return "SMPTE ST 12-1:2014 timecode"; case AV_PKT_DATA_DYNAMIC_HDR10_PLUS: return "HDR10+ Dynamic Metadata (SMPTE 2094-40)"; + case AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT:return "Ambient viewing environment"; + case AV_PKT_DATA_IAMF_MIX_GAIN_PARAM: return "IAMF Mix Gain Parameter Data"; + case AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM: return "IAMF Demixing Info Parameter Data"; + case AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM: return "IAMF Recon Gain Info Parameter Data"; } return NULL; } @@ -645,3 +650,103 @@ int ff_side_data_set_prft(AVPacket *pkt, int64_t timestamp) return 0; } + +const AVPacketSideData *av_packet_side_data_get(const AVPacketSideData *sd, int nb_sd, + enum AVPacketSideDataType type) +{ + for (int i = 0; i < nb_sd; i++) + if (sd[i].type == type) + return &sd[i]; + + return NULL; +} + +static AVPacketSideData *packet_side_data_add(AVPacketSideData **psd, int *pnb_sd, + enum AVPacketSideDataType type, + void *data, size_t size) +{ + AVPacketSideData *sd = *psd, *tmp; + int nb_sd = *pnb_sd; + + for (int i = 0; i < nb_sd; i++) { + if (sd[i].type != type) + continue; + + av_free(sd[i].data); + sd[i].data = data; + sd[i].size = size; + return &sd[i]; + } + + if (nb_sd == INT_MAX) + return NULL; + + tmp = av_realloc_array(sd, nb_sd + 1, sizeof(*tmp)); + if (!tmp) + return NULL; + + *psd = sd = tmp; + sd[nb_sd].type = type; + sd[nb_sd].data = data; + sd[nb_sd].size = size; + *pnb_sd = nb_sd + 1; + + return &sd[nb_sd]; +} + +AVPacketSideData *av_packet_side_data_add(AVPacketSideData **psd, int *pnb_sd, + enum AVPacketSideDataType type, + void *data, size_t size, int flags) +{ + return packet_side_data_add(psd, pnb_sd, type, data, size); +} + +AVPacketSideData *av_packet_side_data_new(AVPacketSideData **psd, int *pnb_sd, + enum AVPacketSideDataType type, + size_t size, int flags) +{ + AVPacketSideData *sd = NULL; + uint8_t *data; + + if (size > SIZE_MAX - AV_INPUT_BUFFER_PADDING_SIZE) + return NULL; + + data = av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!data) + return NULL; + memset(data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + sd = packet_side_data_add(psd, pnb_sd, type, data, size); + if (!sd) + av_freep(&data); + + return sd; +} + +void av_packet_side_data_remove(AVPacketSideData *sd, int *pnb_sd, + enum AVPacketSideDataType type) +{ + int nb_sd = *pnb_sd; + + for (int i = nb_sd - 1; i >= 0; i--) { + if (sd[i].type != type) + continue; + av_free(sd[i].data); + sd[i] = sd[--nb_sd]; + break; + } + + *pnb_sd = nb_sd; +} + +void av_packet_side_data_free(AVPacketSideData **psd, int *pnb_sd) +{ + AVPacketSideData *sd = *psd; + int nb_sd = *pnb_sd; + + for (int i = 0; i < nb_sd; i++) + av_free(sd[i].data); + + av_freep(psd); + *pnb_sd = 0; +} diff --git a/libavcodec/avrndec.c b/libavcodec/avrndec.c index ef194058fc5..97d28246255 100644 --- a/libavcodec/avrndec.c +++ b/libavcodec/avrndec.c @@ -68,7 +68,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type= AV_PICTURE_TYPE_I; - p->key_frame= 1; + p->flags |= AV_FRAME_FLAG_KEY; if(a->interlace) { buf += (true_height - avctx->height)*avctx->width; diff --git a/libavcodec/avs.c b/libavcodec/avs.c index a1c9d3c436f..b53175f6400 100644 --- a/libavcodec/avs.c +++ b/libavcodec/avs.c @@ -61,7 +61,7 @@ static int avs_decode_frame(AVCodecContext * avctx, AVFrame *picture, if ((ret = ff_reget_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_P; - p->key_frame = 0; + p->flags &= ~AV_FRAME_FLAG_KEY; out = p->data[0]; stride = p->linesize[0]; @@ -97,7 +97,7 @@ static int avs_decode_frame(AVCodecContext * avctx, AVFrame *picture, switch (sub_type) { case AVS_I_FRAME: p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; case AVS_P_FRAME_3X3: vect_w = 3; vect_h = 3; diff --git a/libavcodec/avs2_parser.c b/libavcodec/avs2_parser.c index 200134f91db..0d68ab1d00e 100644 --- a/libavcodec/avs2_parser.c +++ b/libavcodec/avs2_parser.c @@ -72,13 +72,15 @@ static void parse_avs2_seq_header(AVCodecParserContext *s, const uint8_t *buf, unsigned aspect_ratio; unsigned frame_rate_code; int low_delay; + av_unused int ret; // update buf_size_min if parse more deeper const int buf_size_min = 15; if (buf_size < buf_size_min) return; - init_get_bits8(&gb, buf, buf_size_min); + ret = init_get_bits8(&gb, buf, buf_size_min); + av_assert1(ret >= 0); s->key_frame = 1; s->pict_type = AV_PICTURE_TYPE_I; diff --git a/libavcodec/avs3_parser.c b/libavcodec/avs3_parser.c index a819b5783d6..ea495b1c7c6 100644 --- a/libavcodec/avs3_parser.c +++ b/libavcodec/avs3_parser.c @@ -73,7 +73,8 @@ static void parse_avs3_nal_units(AVCodecParserContext *s, const uint8_t *buf, GetBitContext gb; int profile, ratecode, low_delay; - init_get_bits8(&gb, buf + 4, buf_size - 4); + av_unused int ret = init_get_bits(&gb, buf + 4, 100); + av_assert1(ret >= 0); s->key_frame = 1; s->pict_type = AV_PICTURE_TYPE_I; diff --git a/libavcodec/avuidec.c b/libavcodec/avuidec.c index ba157e167cd..48b23d4875f 100644 --- a/libavcodec/avuidec.c +++ b/libavcodec/avuidec.c @@ -71,7 +71,7 @@ static int avui_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; if (!interlaced) { diff --git a/libavcodec/bethsoftvideo.c b/libavcodec/bethsoftvideo.c index e095d04fa56..6de502822b1 100644 --- a/libavcodec/bethsoftvideo.c +++ b/libavcodec/bethsoftvideo.c @@ -63,7 +63,11 @@ static int set_palette(BethsoftvidContext *ctx, GetByteContext *g) palette[a] = 0xFFU << 24 | bytestream2_get_be24u(g) * 4; palette[a] |= palette[a] >> 6 & 0x30303; } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS ctx->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif return 0; } diff --git a/libavcodec/bfi.c b/libavcodec/bfi.c index c2682724515..901669a3a99 100644 --- a/libavcodec/bfi.c +++ b/libavcodec/bfi.c @@ -68,7 +68,7 @@ static int bfi_decode_frame(AVCodecContext *avctx, AVFrame *frame, /* Set frame parameters and palette, if necessary */ if (!avctx->frame_num) { frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; /* Setting the palette */ if (avctx->extradata_size > 768) { av_log(avctx, AV_LOG_ERROR, "Palette is too large.\n"); @@ -84,11 +84,19 @@ static int bfi_decode_frame(AVCodecContext *avctx, AVFrame *frame, pal++; } memcpy(bfi->pal, frame->data[1], sizeof(bfi->pal)); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else { frame->pict_type = AV_PICTURE_TYPE_P; - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(frame->data[1], bfi->pal, sizeof(bfi->pal)); } diff --git a/libavcodec/bgmc.c b/libavcodec/bgmc.c index 361f7c52e62..0e41a39e009 100644 --- a/libavcodec/bgmc.c +++ b/libavcodec/bgmc.c @@ -457,7 +457,7 @@ static uint8_t *bgmc_lut_getp(uint8_t *lut, int *lut_status, int delta) /** Initialize the lookup table arrays */ -av_cold int ff_bgmc_init(AVCodecContext *avctx, +av_cold int ff_bgmc_init(void *logctx, uint8_t **cf_lut, int **cf_lut_status) { *cf_lut = av_malloc(sizeof(**cf_lut) * LUT_BUFF * 16 * LUT_SIZE); @@ -465,7 +465,7 @@ av_cold int ff_bgmc_init(AVCodecContext *avctx, if (!*cf_lut || !*cf_lut_status) { ff_bgmc_end(cf_lut, cf_lut_status); - av_log(avctx, AV_LOG_ERROR, "Allocating buffer memory failed.\n"); + av_log(logctx, AV_LOG_ERROR, "Allocating buffer memory failed.\n"); return AVERROR(ENOMEM); } else { // initialize lut_status buffer to a value never used to compare against diff --git a/libavcodec/bgmc.h b/libavcodec/bgmc.h index 466df31a2e0..81771d4aefc 100644 --- a/libavcodec/bgmc.h +++ b/libavcodec/bgmc.h @@ -30,11 +30,10 @@ #define AVCODEC_BGMC_H -#include "avcodec.h" #include "get_bits.h" -int ff_bgmc_init(AVCodecContext *avctx, uint8_t **cf_lut, int **cf_lut_status); +int ff_bgmc_init(void *logctx, uint8_t **cf_lut, int **cf_lut_status); void ff_bgmc_end(uint8_t **cf_lut, int **cf_lut_status); diff --git a/libavcodec/bink.c b/libavcodec/bink.c index 15fc9d29ed8..804c1419813 100644 --- a/libavcodec/bink.c +++ b/libavcodec/bink.c @@ -21,6 +21,7 @@ */ #include "libavutil/attributes.h" +#include "libavutil/emms.h" #include "libavutil/imgutils.h" #include "libavutil/internal.h" #include "libavutil/mem_internal.h" @@ -59,11 +60,11 @@ enum OldSources { BINKB_NB_SRC }; -static const int binkb_bundle_sizes[BINKB_NB_SRC] = { +static const uint8_t binkb_bundle_sizes[BINKB_NB_SRC] = { 4, 8, 8, 5, 5, 11, 11, 4, 4, 7 }; -static const int binkb_bundle_signed[BINKB_NB_SRC] = { +static const uint8_t binkb_bundle_signed[BINKB_NB_SRC] = { 0, 0, 0, 1, 1, 0, 1, 0, 0, 0 }; @@ -1299,8 +1300,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, emms_c(); if (c->version > 'b') { - av_frame_unref(c->last); - if ((ret = av_frame_ref(c->last, frame)) < 0) + if ((ret = av_frame_replace(c->last, frame)) < 0) return ret; } @@ -1318,9 +1318,9 @@ static av_cold void bink_init_vlcs(void) bink_trees[i].table = table + offset; bink_trees[i].table_allocated = 1 << maxbits; offset += bink_trees[i].table_allocated; - init_vlc(&bink_trees[i], maxbits, 16, + vlc_init(&bink_trees[i], maxbits, 16, bink_tree_lens[i], 1, 1, - bink_tree_bits[i], 1, 1, INIT_VLC_USE_NEW_STATIC | INIT_VLC_LE); + bink_tree_bits[i], 1, 1, VLC_INIT_USE_STATIC | VLC_INIT_LE); } } diff --git a/libavcodec/binkaudio.c b/libavcodec/binkaudio.c index f28ecba7603..265f93a8222 100644 --- a/libavcodec/binkaudio.c +++ b/libavcodec/binkaudio.c @@ -325,7 +325,7 @@ static int binkaudio_receive_frame(AVCodecContext *avctx, AVFrame *frame) if (s->ch_offset == 0) { frame->nb_samples = s->frame_len; if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) - return ret; + goto fail; if (!new_pkt) frame->pts = AV_NOPTS_VALUE; } @@ -334,8 +334,8 @@ static int binkaudio_receive_frame(AVCodecContext *avctx, AVFrame *frame) avctx->codec->id == AV_CODEC_ID_BINKAUDIO_DCT, FFMIN(MAX_CHANNELS, s->channels - s->ch_offset), s->ch_offset)) { av_log(avctx, AV_LOG_ERROR, "Incomplete packet\n"); - s->ch_offset = 0; - return AVERROR_INVALIDDATA; + ret = AVERROR_INVALIDDATA; + goto fail; } s->ch_offset += MAX_CHANNELS; get_bits_align32(gb); diff --git a/libavcodec/binkdsp.c b/libavcodec/binkdsp.c index a357d316720..166dca1ba18 100644 --- a/libavcodec/binkdsp.c +++ b/libavcodec/binkdsp.c @@ -24,7 +24,6 @@ * Bink DSP routines */ -#include "config.h" #include "libavutil/attributes.h" #include "binkdsp.h" @@ -131,7 +130,7 @@ static void scale_block_c(const uint8_t src[64]/*align 8*/, uint8_t *dst/*align } } -static void add_pixels8_c(uint8_t *av_restrict pixels, int16_t *block, +static void add_pixels8_c(uint8_t *restrict pixels, int16_t *block, int line_size) { int i; diff --git a/libavcodec/binkdsp.h b/libavcodec/binkdsp.h index b089a9863fb..8f084ec259a 100644 --- a/libavcodec/binkdsp.h +++ b/libavcodec/binkdsp.h @@ -29,13 +29,11 @@ #include -#include "config.h" - typedef struct BinkDSPContext { void (*idct_put)(uint8_t *dest/*align 8*/, int line_size, int32_t *block/*align 16*/); void (*idct_add)(uint8_t *dest/*align 8*/, int line_size, int32_t *block/*align 16*/); void (*scale_block)(const uint8_t src[64]/*align 8*/, uint8_t *dst/*align 8*/, int linesize); - void (*add_pixels8)(uint8_t *av_restrict pixels, int16_t *block, int line_size); + void (*add_pixels8)(uint8_t *restrict pixels, int16_t *block, int line_size); } BinkDSPContext; void ff_binkdsp_init(BinkDSPContext *c); diff --git a/libavcodec/bintext.c b/libavcodec/bintext.c index ce814f76939..b20d6ce176e 100644 --- a/libavcodec/bintext.c +++ b/libavcodec/bintext.c @@ -157,7 +157,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, s->frame, 0)) < 0) return ret; s->frame->pict_type = AV_PICTURE_TYPE_I; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS s->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(s->frame->data[1], s->palette, 16 * 4); if (avctx->codec_id == AV_CODEC_ID_XBIN) { diff --git a/libavcodec/bitpacked_dec.c b/libavcodec/bitpacked_dec.c index a1ffef185ce..54c008bd86a 100644 --- a/libavcodec/bitpacked_dec.c +++ b/libavcodec/bitpacked_dec.c @@ -28,7 +28,6 @@ #include "avcodec.h" #include "codec_internal.h" -#include "get_bits.h" #include "libavutil/imgutils.h" #include "thread.h" @@ -65,7 +64,7 @@ static int bitpacked_decode_yuv422p10(AVCodecContext *avctx, AVFrame *frame, { uint64_t frame_size = (uint64_t)avctx->width * (uint64_t)avctx->height * 20; uint64_t packet_size = (uint64_t)avpkt->size * 8; - GetBitContext bc; + uint8_t *src; uint16_t *y, *u, *v; int ret, i, j; @@ -79,20 +78,18 @@ static int bitpacked_decode_yuv422p10(AVCodecContext *avctx, AVFrame *frame, if (avctx->width % 2) return AVERROR_PATCHWELCOME; - ret = init_get_bits(&bc, avpkt->data, avctx->width * avctx->height * 20); - if (ret) - return ret; - + src = avpkt->data; for (i = 0; i < avctx->height; i++) { y = (uint16_t*)(frame->data[0] + i * frame->linesize[0]); u = (uint16_t*)(frame->data[1] + i * frame->linesize[1]); v = (uint16_t*)(frame->data[2] + i * frame->linesize[2]); for (j = 0; j < avctx->width; j += 2) { - *u++ = get_bits(&bc, 10); - *y++ = get_bits(&bc, 10); - *v++ = get_bits(&bc, 10); - *y++ = get_bits(&bc, 10); + *u++ = (src[0] << 2) | (src[1] >> 6); + *v++ = ((src[2] << 6) | (src[3] >> 2)) & 0x3ff; + *y++ = ((src[1] << 4) | (src[2] >> 4)) & 0x3ff; + *y++ = ((src[3] << 8) | (src[4])) & 0x3ff; + src += 5; } } @@ -134,7 +131,7 @@ static int bitpacked_decode(AVCodecContext *avctx, AVFrame *frame, return res; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return buf_size; diff --git a/libavcodec/bitstream.h b/libavcodec/bitstream.h index b60f0c296d1..35b7873b9cc 100644 --- a/libavcodec/bitstream.h +++ b/libavcodec/bitstream.h @@ -102,6 +102,7 @@ # define bits_decode210 bits_decode210_le # define bits_apply_sign bits_apply_sign_le # define bits_read_vlc bits_read_vlc_le +# define bits_read_vlc_multi bits_read_vlc_multi_le #elif defined(BITS_DEFAULT_BE) @@ -130,6 +131,7 @@ # define bits_decode210 bits_decode210_be # define bits_apply_sign bits_apply_sign_be # define bits_read_vlc bits_read_vlc_be +# define bits_read_vlc_multi bits_read_vlc_multi_be #endif diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index e8216819cae..12860c332b5 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -46,7 +46,6 @@ extern const FFBitStreamFilter ff_imx_dump_header_bsf; extern const FFBitStreamFilter ff_media100_to_mjpegb_bsf; extern const FFBitStreamFilter ff_mjpeg2jpeg_bsf; extern const FFBitStreamFilter ff_mjpega_dump_header_bsf; -extern const FFBitStreamFilter ff_mp3_header_decompress_bsf; extern const FFBitStreamFilter ff_mpeg2_metadata_bsf; extern const FFBitStreamFilter ff_mpeg4_unpack_bframes_bsf; extern const FFBitStreamFilter ff_mov2textsub_bsf; @@ -58,6 +57,7 @@ extern const FFBitStreamFilter ff_pgs_frame_merge_bsf; extern const FFBitStreamFilter ff_prores_metadata_bsf; extern const FFBitStreamFilter ff_remove_extradata_bsf; extern const FFBitStreamFilter ff_setts_bsf; +extern const FFBitStreamFilter ff_showinfo_bsf; extern const FFBitStreamFilter ff_text2movsub_bsf; extern const FFBitStreamFilter ff_trace_headers_bsf; extern const FFBitStreamFilter ff_truehd_core_bsf; @@ -65,16 +65,19 @@ extern const FFBitStreamFilter ff_vp9_metadata_bsf; extern const FFBitStreamFilter ff_vp9_raw_reorder_bsf; extern const FFBitStreamFilter ff_vp9_superframe_bsf; extern const FFBitStreamFilter ff_vp9_superframe_split_bsf; +extern const FFBitStreamFilter ff_vvc_metadata_bsf; +extern const FFBitStreamFilter ff_vvc_mp4toannexb_bsf; +extern const FFBitStreamFilter ff_evc_frame_merge_bsf; #include "libavcodec/bsf_list.c" const AVBitStreamFilter *av_bsf_iterate(void **opaque) { - uintptr_t i = (uintptr_t)*opaque; + uintptr_t i = (uintptr_t) * opaque; const FFBitStreamFilter *f = bitstream_filters[i]; if (f) { - *opaque = (void*)(i + 1); + *opaque = (void *)(i + 1); return &f->p; } return NULL; diff --git a/libavcodec/bitstream_template.h b/libavcodec/bitstream_template.h index 30bea84add6..c8e4a5131e1 100644 --- a/libavcodec/bitstream_template.h +++ b/libavcodec/bitstream_template.h @@ -491,7 +491,7 @@ static inline int BS_FUNC(priv_set_idx)(BSCTX *bc, int code, int *n, /** * Parse a vlc code. * @param bits is the number of bits which will be read at once, must be - * identical to nb_bits in init_vlc() + * identical to nb_bits in vlc_init() * @param max_depth is the number of times bits bits must be read to completely * read the longest vlc code * = (max_vlc_length + bits - 1) / bits @@ -520,6 +520,52 @@ static inline int BS_FUNC(read_vlc)(BSCTX *bc, const VLCElem *table, return code; } +/** + * Parse a vlc / vlc_multi code. + * @param bits is the number of bits which will be read at once, must be + * identical to nb_bits in vlc_init() + * @param max_depth is the number of times bits bits must be read to completely + * read the longest vlc code + * = (max_vlc_length + bits - 1) / bits + * @param dst the parsed symbol(s) will be stored here. Up to 8 bytes are written + * @returns number of symbols parsed + * If the vlc code is invalid and max_depth=1, then no bits will be removed. + * If the vlc code is invalid and max_depth>1, then the number of bits removed + * is undefined. + */ +static inline int BS_FUNC(read_vlc_multi)(BSCTX *bc, uint8_t dst[8], + const VLC_MULTI_ELEM *const Jtable, + const VLCElem *const table, + const int bits, const int max_depth, + const int symbols_size) +{ + unsigned idx = BS_FUNC(peek)(bc, bits); + int ret, nb_bits, code, n = Jtable[idx].len; + if (Jtable[idx].num) { + AV_COPY64U(dst, Jtable[idx].val); + ret = Jtable[idx].num; + } else { + code = table[idx].sym; + n = table[idx].len; + if (max_depth > 1 && n < 0) { + BS_FUNC(priv_skip_remaining)(bc, bits); + code = BS_FUNC(priv_set_idx)(bc, code, &n, &nb_bits, table); + if (max_depth > 2 && n < 0) { + BS_FUNC(priv_skip_remaining)(bc, nb_bits); + code = BS_FUNC(priv_set_idx)(bc, code, &n, &nb_bits, table); + } + } + if (symbols_size == 1) + *dst = code; + else + AV_WN16(dst, code); + ret = n > 0; + } + BS_FUNC(priv_skip_remaining)(bc, n); + + return ret; +} + #undef BSCTX #undef BS_FUNC #undef BS_JOIN3 diff --git a/libavcodec/blockdsp.c b/libavcodec/blockdsp.c index 98f06c5d161..79a8814262e 100644 --- a/libavcodec/blockdsp.c +++ b/libavcodec/blockdsp.c @@ -69,6 +69,8 @@ av_cold void ff_blockdsp_init(BlockDSPContext *c) ff_blockdsp_init_arm(c); #elif ARCH_PPC ff_blockdsp_init_ppc(c); +#elif ARCH_RISCV + ff_blockdsp_init_riscv(c); #elif ARCH_X86 ff_blockdsp_init_x86(c); #elif ARCH_MIPS diff --git a/libavcodec/blockdsp.h b/libavcodec/blockdsp.h index d853adada21..6d751d797b2 100644 --- a/libavcodec/blockdsp.h +++ b/libavcodec/blockdsp.h @@ -41,6 +41,7 @@ void ff_blockdsp_init(BlockDSPContext *c); void ff_blockdsp_init_alpha(BlockDSPContext *c); void ff_blockdsp_init_arm(BlockDSPContext *c); void ff_blockdsp_init_ppc(BlockDSPContext *c); +void ff_blockdsp_init_riscv(BlockDSPContext *c); void ff_blockdsp_init_x86(BlockDSPContext *c); void ff_blockdsp_init_mips(BlockDSPContext *c); diff --git a/libavcodec/bmp.c b/libavcodec/bmp.c index d7e01f07256..d117c06cf41 100644 --- a/libavcodec/bmp.c +++ b/libavcodec/bmp.c @@ -210,7 +210,7 @@ static int bmp_decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; buf = buf0 + hsize; dsize = buf_size - hsize; diff --git a/libavcodec/bmvvideo.c b/libavcodec/bmvvideo.c index 92ce41c8363..20f07ca556f 100644 --- a/libavcodec/bmvvideo.c +++ b/libavcodec/bmvvideo.c @@ -251,7 +251,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } memcpy(frame->data[1], c->pal, AVPALETTE_SIZE); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = type & BMV_PALETTE; +FF_ENABLE_DEPRECATION_WARNINGS +#endif outptr = frame->data[0]; srcptr = c->frame; diff --git a/libavcodec/bonk.c b/libavcodec/bonk.c index 53711896d0e..65679e5fb65 100644 --- a/libavcodec/bonk.c +++ b/libavcodec/bonk.c @@ -428,8 +428,10 @@ const FFCodec ff_bonk_decoder = { FF_CODEC_DECODE_CB(bonk_decode), .close = bonk_close, .p.capabilities = AV_CODEC_CAP_DELAY | - AV_CODEC_CAP_DR1 | - AV_CODEC_CAP_SUBFRAMES, +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_NONE }, diff --git a/libavcodec/brenderpix.c b/libavcodec/brenderpix.c index e95ab3d4afd..70a3e6be2a3 100644 --- a/libavcodec/brenderpix.c +++ b/libavcodec/brenderpix.c @@ -245,7 +245,11 @@ static int pix_decode_frame(AVCodecContext *avctx, AVFrame *frame, *pal_out++ = (0xFFU << 24) | bytestream2_get_be32u(&gb); bytestream2_skip(&gb, 8); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif chunk_type = bytestream2_get_be32(&gb); } else if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { @@ -257,7 +261,11 @@ static int pix_decode_frame(AVCodecContext *avctx, AVFrame *frame, "Using default palette, colors might be off.\n"); memcpy(pal_out, std_pal_table, sizeof(uint32_t) * 256); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } data_len = bytestream2_get_be32(&gb); @@ -278,7 +286,7 @@ static int pix_decode_frame(AVCodecContext *avctx, AVFrame *frame, bytes_per_scanline, hdr.height); frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/bsf.c b/libavcodec/bsf.c index 42cc1b5ab0c..1e710f7d4af 100644 --- a/libavcodec/bsf.c +++ b/libavcodec/bsf.c @@ -31,8 +31,7 @@ #include "bsf_internal.h" #include "codec_desc.h" #include "codec_par.h" - -#define IS_EMPTY(pkt) (!(pkt)->data && !(pkt)->side_data_elems) +#include "packet_internal.h" static av_always_inline const FFBitStreamFilter *ff_bsf(const AVBitStreamFilter *bsf) { @@ -205,7 +204,7 @@ int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt) FFBSFContext *const bsfi = ffbsfcontext(ctx); int ret; - if (!pkt || IS_EMPTY(pkt)) { + if (!pkt || AVPACKET_IS_EMPTY(pkt)) { if (pkt) av_packet_unref(pkt); bsfi->eof = 1; @@ -217,7 +216,7 @@ int av_bsf_send_packet(AVBSFContext *ctx, AVPacket *pkt) return AVERROR(EINVAL); } - if (!IS_EMPTY(bsfi->buffer_pkt)) + if (!AVPACKET_IS_EMPTY(bsfi->buffer_pkt)) return AVERROR(EAGAIN); ret = av_packet_make_refcounted(pkt); @@ -241,7 +240,7 @@ int ff_bsf_get_packet(AVBSFContext *ctx, AVPacket **pkt) if (bsfi->eof) return AVERROR_EOF; - if (IS_EMPTY(bsfi->buffer_pkt)) + if (AVPACKET_IS_EMPTY(bsfi->buffer_pkt)) return AVERROR(EAGAIN); tmp_pkt = av_packet_alloc(); @@ -261,7 +260,7 @@ int ff_bsf_get_packet_ref(AVBSFContext *ctx, AVPacket *pkt) if (bsfi->eof) return AVERROR_EOF; - if (IS_EMPTY(bsfi->buffer_pkt)) + if (AVPACKET_IS_EMPTY(bsfi->buffer_pkt)) return AVERROR(EAGAIN); av_packet_move_ref(pkt, bsfi->buffer_pkt); diff --git a/libavcodec/bsf/Makefile b/libavcodec/bsf/Makefile new file mode 100644 index 00000000000..fb70ad0c21c --- /dev/null +++ b/libavcodec/bsf/Makefile @@ -0,0 +1,49 @@ +clean:: + $(RM) $(CLEANSUFFIXES:%=libavcodec/bsf/%) + +OBJS-$(CONFIG_AAC_ADTSTOASC_BSF) += bsf/aac_adtstoasc.o +OBJS-$(CONFIG_AV1_FRAME_MERGE_BSF) += bsf/av1_frame_merge.o +OBJS-$(CONFIG_AV1_FRAME_SPLIT_BSF) += bsf/av1_frame_split.o +OBJS-$(CONFIG_AV1_METADATA_BSF) += bsf/av1_metadata.o +OBJS-$(CONFIG_CHOMP_BSF) += bsf/chomp.o +OBJS-$(CONFIG_DCA_CORE_BSF) += bsf/dca_core.o +OBJS-$(CONFIG_DTS2PTS_BSF) += bsf/dts2pts.o +OBJS-$(CONFIG_DUMP_EXTRADATA_BSF) += bsf/dump_extradata.o +OBJS-$(CONFIG_DV_ERROR_MARKER_BSF) += bsf/dv_error_marker.o +OBJS-$(CONFIG_EAC3_CORE_BSF) += bsf/eac3_core.o +OBJS-$(CONFIG_EVC_FRAME_MERGE_BSF) += bsf/evc_frame_merge.o +OBJS-$(CONFIG_EXTRACT_EXTRADATA_BSF) += bsf/extract_extradata.o +OBJS-$(CONFIG_FILTER_UNITS_BSF) += bsf/filter_units.o +OBJS-$(CONFIG_H264_METADATA_BSF) += bsf/h264_metadata.o +OBJS-$(CONFIG_H264_MP4TOANNEXB_BSF) += bsf/h264_mp4toannexb.o +OBJS-$(CONFIG_H264_REDUNDANT_PPS_BSF) += bsf/h264_redundant_pps.o +OBJS-$(CONFIG_HAPQA_EXTRACT_BSF) += bsf/hapqa_extract.o +OBJS-$(CONFIG_HEVC_METADATA_BSF) += bsf/h265_metadata.o +OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF) += bsf/hevc_mp4toannexb.o +OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += bsf/imx_dump_header.o +OBJS-$(CONFIG_MEDIA100_TO_MJPEGB_BSF) += bsf/media100_to_mjpegb.o +OBJS-$(CONFIG_MJPEG2JPEG_BSF) += bsf/mjpeg2jpeg.o +OBJS-$(CONFIG_MJPEGA_DUMP_HEADER_BSF) += bsf/mjpega_dump_header.o +OBJS-$(CONFIG_MOV2TEXTSUB_BSF) += bsf/movsub.o +OBJS-$(CONFIG_MPEG2_METADATA_BSF) += bsf/mpeg2_metadata.o +OBJS-$(CONFIG_MPEG4_UNPACK_BFRAMES_BSF) += bsf/mpeg4_unpack_bframes.o +OBJS-$(CONFIG_NOISE_BSF) += bsf/noise.o +OBJS-$(CONFIG_NULL_BSF) += bsf/null.o +OBJS-$(CONFIG_OPUS_METADATA_BSF) += bsf/opus_metadata.o +OBJS-$(CONFIG_PCM_RECHUNK_BSF) += bsf/pcm_rechunk.o +OBJS-$(CONFIG_PGS_FRAME_MERGE_BSF) += bsf/pgs_frame_merge.o +OBJS-$(CONFIG_PRORES_METADATA_BSF) += bsf/prores_metadata.o +OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF) += bsf/remove_extradata.o +OBJS-$(CONFIG_SETTS_BSF) += bsf/setts.o +OBJS-$(CONFIG_SHOWINFO_BSF) += bsf/showinfo.o +OBJS-$(CONFIG_TEXT2MOVSUB_BSF) += bsf/movsub.o +OBJS-$(CONFIG_TRACE_HEADERS_BSF) += bsf/trace_headers.o +OBJS-$(CONFIG_TRUEHD_CORE_BSF) += bsf/truehd_core.o +OBJS-$(CONFIG_VP9_METADATA_BSF) += bsf/vp9_metadata.o +OBJS-$(CONFIG_VP9_RAW_REORDER_BSF) += bsf/vp9_raw_reorder.o +OBJS-$(CONFIG_VP9_SUPERFRAME_BSF) += bsf/vp9_superframe.o +OBJS-$(CONFIG_VP9_SUPERFRAME_SPLIT_BSF) += bsf/vp9_superframe_split.o +OBJS-$(CONFIG_VVC_METADATA_BSF) += bsf/h266_metadata.o +OBJS-$(CONFIG_VVC_MP4TOANNEXB_BSF) += bsf/vvc_mp4toannexb.o + +libavcodec/bsf/%.o: CPPFLAGS += -I$(SRC_PATH)/libavcodec/ diff --git a/libavcodec/aac_adtstoasc_bsf.c b/libavcodec/bsf/aac_adtstoasc.c similarity index 100% rename from libavcodec/aac_adtstoasc_bsf.c rename to libavcodec/bsf/aac_adtstoasc.c diff --git a/libavcodec/av1_frame_merge_bsf.c b/libavcodec/bsf/av1_frame_merge.c similarity index 100% rename from libavcodec/av1_frame_merge_bsf.c rename to libavcodec/bsf/av1_frame_merge.c diff --git a/libavcodec/av1_frame_split_bsf.c b/libavcodec/bsf/av1_frame_split.c similarity index 100% rename from libavcodec/av1_frame_split_bsf.c rename to libavcodec/bsf/av1_frame_split.c diff --git a/libavcodec/av1_metadata_bsf.c b/libavcodec/bsf/av1_metadata.c similarity index 98% rename from libavcodec/av1_metadata_bsf.c rename to libavcodec/bsf/av1_metadata.c index 41b02cc836b..cb51d2eee66 100644 --- a/libavcodec/av1_metadata_bsf.c +++ b/libavcodec/bsf/av1_metadata.c @@ -179,7 +179,7 @@ static const AVOption av1_metadata_options[] = { { "color_range", "Set color range flag (section 6.4.2)", OFFSET(color_range), AV_OPT_TYPE_INT, - { .i64 = -1 }, -1, 1, FLAGS, "cr" }, + { .i64 = -1 }, -1, 1, FLAGS, .unit = "cr" }, { "tv", "TV (limited) range", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, .flags = FLAGS, .unit = "cr" }, { "pc", "PC (full) range", 0, AV_OPT_TYPE_CONST, @@ -187,7 +187,7 @@ static const AVOption av1_metadata_options[] = { { "chroma_sample_position", "Set chroma sample position (section 6.4.2)", OFFSET(chroma_sample_position), AV_OPT_TYPE_INT, - { .i64 = -1 }, -1, 3, FLAGS, "csp" }, + { .i64 = -1 }, -1, 3, FLAGS, .unit = "csp" }, { "unknown", "Unknown chroma sample position", 0, AV_OPT_TYPE_CONST, { .i64 = AV1_CSP_UNKNOWN }, .flags = FLAGS, .unit = "csp" }, { "vertical", "Left chroma sample position", 0, AV_OPT_TYPE_CONST, diff --git a/libavcodec/chomp_bsf.c b/libavcodec/bsf/chomp.c similarity index 100% rename from libavcodec/chomp_bsf.c rename to libavcodec/bsf/chomp.c diff --git a/libavcodec/dca_core_bsf.c b/libavcodec/bsf/dca_core.c similarity index 100% rename from libavcodec/dca_core_bsf.c rename to libavcodec/bsf/dca_core.c diff --git a/libavcodec/dts2pts_bsf.c b/libavcodec/bsf/dts2pts.c similarity index 100% rename from libavcodec/dts2pts_bsf.c rename to libavcodec/bsf/dts2pts.c diff --git a/libavcodec/dump_extradata_bsf.c b/libavcodec/bsf/dump_extradata.c similarity index 99% rename from libavcodec/dump_extradata_bsf.c rename to libavcodec/bsf/dump_extradata.c index 5506d5ed656..43619879cc7 100644 --- a/libavcodec/dump_extradata_bsf.c +++ b/libavcodec/bsf/dump_extradata.c @@ -83,7 +83,7 @@ static int dump_extradata(AVBSFContext *ctx, AVPacket *out) #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM) static const AVOption options[] = { { "freq", "When to dump extradata", OFFSET(freq), AV_OPT_TYPE_INT, - { .i64 = DUMP_FREQ_KEYFRAME }, DUMP_FREQ_KEYFRAME, DUMP_FREQ_ALL, FLAGS, "freq" }, + { .i64 = DUMP_FREQ_KEYFRAME }, DUMP_FREQ_KEYFRAME, DUMP_FREQ_ALL, FLAGS, .unit = "freq" }, { "k", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = DUMP_FREQ_KEYFRAME }, .flags = FLAGS, .unit = "freq" }, { "keyframe", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = DUMP_FREQ_KEYFRAME }, .flags = FLAGS, .unit = "freq" }, { "e", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = DUMP_FREQ_ALL }, .flags = FLAGS, .unit = "freq" }, diff --git a/libavcodec/dv_error_marker_bsf.c b/libavcodec/bsf/dv_error_marker.c similarity index 77% rename from libavcodec/dv_error_marker_bsf.c rename to libavcodec/bsf/dv_error_marker.c index 131c6fcbe64..c8bb9e062fd 100644 --- a/libavcodec/dv_error_marker_bsf.c +++ b/libavcodec/bsf/dv_error_marker.c @@ -99,25 +99,25 @@ static int dv_error_marker_filter(AVBSFContext *ctx, AVPacket *pkt) static const AVOption options[] = { { "color" , "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "yellow"}, 0, 0, FLAGS }, { "sta" , "specify which error status value to match" - , OFFSET(sta ), AV_OPT_TYPE_FLAGS, {.i64 = 0xFFFE}, 0, 0xFFFF, FLAGS, "sta" }, - { "ok" , "No error, no concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0001}, 0, 0xFFFF, FLAGS, "sta"}, - { "Aa" , "No error, concealment from previous frame type a",0, AV_OPT_TYPE_CONST, {.i64 = 0x0004}, 0, 0xFFFF, FLAGS, "sta"}, - { "Ba" , "No error, concealment from next frame type a", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0010}, 0, 0xFFFF, FLAGS, "sta"}, - { "Ca" , "No error, unspecified concealment type a", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0040}, 0, 0xFFFF, FLAGS, "sta"}, - { "erri" , "Error with inserted code, No concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0080}, 0, 0xFFFF, FLAGS, "sta"}, - { "erru" , "Error with unidentified pos, No concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x8000}, 0, 0xFFFF, FLAGS, "sta"}, - { "err" , "Error, No concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x8080}, 0, 0xFFFF, FLAGS, "sta"}, - { "Ab" , "No error, concealment from previous frame type b",0, AV_OPT_TYPE_CONST, {.i64 = 0x0400}, 0, 0xFFFF, FLAGS, "sta"}, - { "Bb" , "No error, concealment from next frame type b", 0, AV_OPT_TYPE_CONST, {.i64 = 0x1000}, 0, 0xFFFF, FLAGS, "sta"}, - { "Cb" , "No error, unspecified concealment type b", 0, AV_OPT_TYPE_CONST, {.i64 = 0x4000}, 0, 0xFFFF, FLAGS, "sta"}, - { "A" , "No error, concealment from previous frame", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0404}, 0, 0xFFFF, FLAGS, "sta"}, - { "B" , "No error, concealment from next frame", 0, AV_OPT_TYPE_CONST, {.i64 = 0x1010}, 0, 0xFFFF, FLAGS, "sta"}, - { "C" , "No error, unspecified concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x4040}, 0, 0xFFFF, FLAGS, "sta"}, - { "a" , "No error, concealment type a", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0054}, 0, 0xFFFF, FLAGS, "sta"}, - { "b" , "No error, concealment type b", 0, AV_OPT_TYPE_CONST, {.i64 = 0x5400}, 0, 0xFFFF, FLAGS, "sta"}, - { "res" , "Reserved", 0, AV_OPT_TYPE_CONST, {.i64 = 0x2B2A}, 0, 0xFFFF, FLAGS, "sta"}, - { "notok" , "Error or concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0xD4D4}, 0, 0xFFFF, FLAGS, "sta"}, - { "notres" , "Not reserved", 0, AV_OPT_TYPE_CONST, {.i64 = 0xD4D5}, 0, 0xFFFF, FLAGS, "sta"}, + , OFFSET(sta ), AV_OPT_TYPE_FLAGS, {.i64 = 0xFFFE}, 0, 0xFFFF, FLAGS, .unit = "sta" }, + { "ok" , "No error, no concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0001}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "Aa" , "No error, concealment from previous frame type a",0, AV_OPT_TYPE_CONST, {.i64 = 0x0004}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "Ba" , "No error, concealment from next frame type a", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0010}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "Ca" , "No error, unspecified concealment type a", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0040}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "erri" , "Error with inserted code, No concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0080}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "erru" , "Error with unidentified pos, No concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x8000}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "err" , "Error, No concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x8080}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "Ab" , "No error, concealment from previous frame type b",0, AV_OPT_TYPE_CONST, {.i64 = 0x0400}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "Bb" , "No error, concealment from next frame type b", 0, AV_OPT_TYPE_CONST, {.i64 = 0x1000}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "Cb" , "No error, unspecified concealment type b", 0, AV_OPT_TYPE_CONST, {.i64 = 0x4000}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "A" , "No error, concealment from previous frame", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0404}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "B" , "No error, concealment from next frame", 0, AV_OPT_TYPE_CONST, {.i64 = 0x1010}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "C" , "No error, unspecified concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0x4040}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "a" , "No error, concealment type a", 0, AV_OPT_TYPE_CONST, {.i64 = 0x0054}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "b" , "No error, concealment type b", 0, AV_OPT_TYPE_CONST, {.i64 = 0x5400}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "res" , "Reserved", 0, AV_OPT_TYPE_CONST, {.i64 = 0x2B2A}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "notok" , "Error or concealment", 0, AV_OPT_TYPE_CONST, {.i64 = 0xD4D4}, 0, 0xFFFF, FLAGS, .unit = "sta"}, + { "notres" , "Not reserved", 0, AV_OPT_TYPE_CONST, {.i64 = 0xD4D5}, 0, 0xFFFF, FLAGS, .unit = "sta"}, { NULL }, }; diff --git a/libavcodec/eac3_core_bsf.c b/libavcodec/bsf/eac3_core.c similarity index 100% rename from libavcodec/eac3_core_bsf.c rename to libavcodec/bsf/eac3_core.c diff --git a/libavcodec/bsf/evc_frame_merge.c b/libavcodec/bsf/evc_frame_merge.c new file mode 100644 index 00000000000..7b8e6b1c9ee --- /dev/null +++ b/libavcodec/bsf/evc_frame_merge.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2019 James Almer + * + * 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 "get_bits.h" +#include "bsf.h" +#include "bsf_internal.h" + +#include "evc.h" +#include "evc_parse.h" +#include "evc_ps.h" + +// Access unit data +typedef struct AccessUnitBuffer { + uint8_t *data; // the data buffer + size_t data_size; // size of data in bytes + unsigned capacity; // buffer capacity +} AccessUnitBuffer; + +typedef struct EVCFMergeContext { + AVPacket *in, *buffer_pkt; + EVCParamSets ps; + EVCParserPoc poc; + AccessUnitBuffer au_buffer; +} EVCFMergeContext; + +static int end_of_access_unit_found(const EVCParamSets *ps, const EVCParserSliceHeader *sh, + const EVCParserPoc *poc, enum EVCNALUnitType nalu_type) +{ + EVCParserPPS *pps = ps->pps[sh->slice_pic_parameter_set_id]; + EVCParserSPS *sps = ps->sps[pps->pps_seq_parameter_set_id]; + + av_assert0(sps && pps); + + if (sps->profile_idc == 0) { // BASELINE profile + if (nalu_type == EVC_NOIDR_NUT || nalu_type == EVC_IDR_NUT) + return 1; + } else { // MAIN profile + if (nalu_type == EVC_NOIDR_NUT) { + if (poc->PicOrderCntVal != poc->prevPicOrderCntVal) + return 1; + } else if (nalu_type == EVC_IDR_NUT) + return 1; + } + return 0; +} + +static void evc_frame_merge_flush(AVBSFContext *bsf) +{ + EVCFMergeContext *ctx = bsf->priv_data; + + ff_evc_ps_free(&ctx->ps); + av_packet_unref(ctx->in); + av_packet_unref(ctx->buffer_pkt); + ctx->au_buffer.data_size = 0; +} + +static int parse_nal_unit(AVBSFContext *bsf, const uint8_t *buf, int buf_size) +{ + EVCFMergeContext *ctx = bsf->priv_data; + GetBitContext gb; + enum EVCNALUnitType nalu_type; + int tid, err; + + err = init_get_bits8(&gb, buf, buf_size); + if (err < 0) + return err; + + // @see ISO_IEC_23094-1_2020, 7.4.2.2 NAL unit header semantic (Table 4 - NAL unit type codes and NAL unit type classes) + // @see enum EVCNALUnitType in evc.h + if (get_bits1(&gb)) {// forbidden_zero_bit + av_log(bsf, AV_LOG_ERROR, "Invalid NAL unit header\n"); + return AVERROR_INVALIDDATA; + } + + nalu_type = get_bits(&gb, 6) - 1; + if (nalu_type < EVC_NOIDR_NUT || nalu_type > EVC_UNSPEC_NUT62) { + av_log(bsf, AV_LOG_ERROR, "Invalid NAL unit type: (%d)\n", nalu_type); + return AVERROR_INVALIDDATA; + } + + tid = get_bits(&gb, 3); + skip_bits(&gb, 5); // nuh_reserved_zero_5bits + skip_bits1(&gb); // nuh_extension_flag + + switch (nalu_type) { + case EVC_SPS_NUT: + err = ff_evc_parse_sps(&gb, &ctx->ps); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "SPS parsing error\n"); + return err; + } + break; + case EVC_PPS_NUT: + err = ff_evc_parse_pps(&gb, &ctx->ps); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "PPS parsing error\n"); + return err; + } + break; + case EVC_IDR_NUT: // Coded slice of a IDR or non-IDR picture + case EVC_NOIDR_NUT: { + EVCParserSliceHeader sh; + + err = ff_evc_parse_slice_header(&gb, &sh, &ctx->ps, nalu_type); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Slice header parsing error\n"); + return err; + } + + // POC (picture order count of the current picture) derivation + // @see ISO/IEC 23094-1:2020(E) 8.3.1 Decoding process for picture order count + err = ff_evc_derive_poc(&ctx->ps, &sh, &ctx->poc, nalu_type, tid); + if (err < 0) + return err; + + return end_of_access_unit_found(&ctx->ps, &sh, &ctx->poc, nalu_type); + + break; + } + case EVC_SEI_NUT: // Supplemental Enhancement Information + case EVC_APS_NUT: // Adaptation parameter set + case EVC_FD_NUT: // Filler data + default: + break; + } + + return 0; +} + +static int evc_frame_merge_filter(AVBSFContext *bsf, AVPacket *out) +{ + EVCFMergeContext *ctx = bsf->priv_data; + AVPacket *in = ctx->in, *buffer_pkt = ctx->buffer_pkt; + size_t data_size; + int au_end_found = 0, err; + + while (!au_end_found) { + uint8_t *buffer; + uint32_t nalu_size; + + if (!in->size) { + av_packet_unref(in); + err = ff_bsf_get_packet_ref(bsf, in); + if (err < 0) { + if (err == AVERROR_EOF && ctx->au_buffer.data_size > 0) + break; + return err; + } + /* Buffer packets with timestamps (there should be at most one per AU) + * or any packet if buffer_pkt is empty. The latter is needed to + * passthrough positions in case there are no timestamps like with + * the raw EVC demuxer. */ + if (!buffer_pkt->data || + in->pts != AV_NOPTS_VALUE && buffer_pkt->pts == AV_NOPTS_VALUE) { + err = av_packet_ref(buffer_pkt, in); + if (err < 0) + goto end; + } + } + + // Buffer size is not enough for buffer to store NAL unit 4-bytes prefix (length) + if (in->size < EVC_NALU_LENGTH_PREFIX_SIZE) + return AVERROR_INVALIDDATA; + + nalu_size = evc_read_nal_unit_length(in->data, EVC_NALU_LENGTH_PREFIX_SIZE, bsf); + if (!nalu_size || nalu_size > INT_MAX) { + av_log(bsf, AV_LOG_ERROR, "Invalid NAL unit size: (%u)\n", nalu_size); + err = AVERROR_INVALIDDATA; + goto end; + } + + if (in->size < nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE) { + err = AVERROR_INVALIDDATA; + goto end; + } + + err = parse_nal_unit(bsf, in->data + EVC_NALU_LENGTH_PREFIX_SIZE, nalu_size); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Parsing of NAL unit failed\n"); + goto end; + } + au_end_found = err; + + nalu_size += EVC_NALU_LENGTH_PREFIX_SIZE; + + data_size = ctx->au_buffer.data_size + nalu_size; + if (data_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) { + av_log(bsf, AV_LOG_ERROR, "Assembled packet is too big\n"); + err = AVERROR(ERANGE); + goto end; + } + + buffer = av_fast_realloc(ctx->au_buffer.data, &ctx->au_buffer.capacity, + data_size); + if (!buffer) { + av_freep(&ctx->au_buffer.data); + err = AVERROR_INVALIDDATA; + goto end; + } + + ctx->au_buffer.data = buffer; + memcpy(ctx->au_buffer.data + ctx->au_buffer.data_size, in->data, nalu_size); + + ctx->au_buffer.data_size = data_size; + + in->data += nalu_size; + in->size -= nalu_size; + } + + av_packet_unref(in); + data_size = ctx->au_buffer.data_size; + + ctx->au_buffer.data_size = 0; + // drop the data in buffer_pkt, if any, but keep the props + av_buffer_unref(&buffer_pkt->buf); + err = av_buffer_realloc(&buffer_pkt->buf, data_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (err < 0) + goto end; + + buffer_pkt->data = buffer_pkt->buf->data; + buffer_pkt->size = data_size; + av_packet_move_ref(out, buffer_pkt); + memcpy(out->data, ctx->au_buffer.data, data_size); + memset(out->data + data_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + err = 0; +end: + if (err < 0) { + av_packet_unref(in); + av_packet_unref(buffer_pkt); + ctx->au_buffer.data_size = 0; + } + return err; +} + +static int evc_frame_merge_init(AVBSFContext *bsf) +{ + EVCFMergeContext *ctx = bsf->priv_data; + + ctx->in = av_packet_alloc(); + ctx->buffer_pkt = av_packet_alloc(); + if (!ctx->in || !ctx->buffer_pkt) + return AVERROR(ENOMEM); + + return 0; +} + +static void evc_frame_merge_close(AVBSFContext *bsf) +{ + EVCFMergeContext *ctx = bsf->priv_data; + + av_packet_free(&ctx->in); + av_packet_free(&ctx->buffer_pkt); + ff_evc_ps_free(&ctx->ps); + + ctx->au_buffer.capacity = 0; + av_freep(&ctx->au_buffer.data); + ctx->au_buffer.data_size = 0; +} + +static const enum AVCodecID evc_frame_merge_codec_ids[] = { + AV_CODEC_ID_EVC, AV_CODEC_ID_NONE, +}; + +const FFBitStreamFilter ff_evc_frame_merge_bsf = { + .p.name = "evc_frame_merge", + .p.codec_ids = evc_frame_merge_codec_ids, + .priv_data_size = sizeof(EVCFMergeContext), + .init = evc_frame_merge_init, + .flush = evc_frame_merge_flush, + .close = evc_frame_merge_close, + .filter = evc_frame_merge_filter, +}; diff --git a/libavcodec/extract_extradata_bsf.c b/libavcodec/bsf/extract_extradata.c similarity index 88% rename from libavcodec/extract_extradata_bsf.c rename to libavcodec/bsf/extract_extradata.c index 329b1a61744..5d5d80c90f8 100644 --- a/libavcodec/extract_extradata_bsf.c +++ b/libavcodec/bsf/extract_extradata.c @@ -31,6 +31,7 @@ #include "hevc.h" #include "startcode.h" #include "vc1_common.h" +#include "vvc.h" typedef struct ExtractExtradataContext { const AVClass *class; @@ -48,25 +49,53 @@ typedef struct ExtractExtradataContext { int remove; } ExtractExtradataContext; -static int val_in_array(const int *arr, int len, int val) +static int val_in_array(const int *arr, size_t len, int val) { - int i; - for (i = 0; i < len; i++) + for (size_t i = 0; i < len; i++) if (arr[i] == val) return 1; return 0; } -static int extract_extradata_av1(AVBSFContext *ctx, AVPacket *pkt, - uint8_t **data, int *size) +static int metadata_is_global(const AV1OBU *obu) +{ + static const int metadata_obu_types[] = { + AV1_METADATA_TYPE_HDR_CLL, AV1_METADATA_TYPE_HDR_MDCV, + }; + GetBitContext gb; + int metadata_type; + + if (init_get_bits(&gb, obu->data, obu->size_bits) < 0) + return 0; + + metadata_type = get_leb(&gb); + + return val_in_array(metadata_obu_types, FF_ARRAY_ELEMS(metadata_obu_types), + metadata_type); +} + +static int obu_is_global(const AV1OBU *obu) { static const int extradata_obu_types[] = { AV1_OBU_SEQUENCE_HEADER, AV1_OBU_METADATA, }; + + if (!val_in_array(extradata_obu_types, FF_ARRAY_ELEMS(extradata_obu_types), + obu->type)) + return 0; + if (obu->type != AV1_OBU_METADATA) + return 1; + + return metadata_is_global(obu); +} + +static int extract_extradata_av1(AVBSFContext *ctx, AVPacket *pkt, + uint8_t **data, int *size) +{ + ExtractExtradataContext *s = ctx->priv_data; int extradata_size = 0, filtered_size = 0; - int nb_extradata_obu_types = FF_ARRAY_ELEMS(extradata_obu_types); int i, has_seq = 0, ret = 0; ret = ff_av1_packet_split(&s->av1_pkt, pkt->data, pkt->size, ctx); @@ -75,7 +104,7 @@ static int extract_extradata_av1(AVBSFContext *ctx, AVPacket *pkt, for (i = 0; i < s->av1_pkt.nb_obus; i++) { AV1OBU *obu = &s->av1_pkt.obus[i]; - if (val_in_array(extradata_obu_types, nb_extradata_obu_types, obu->type)) { + if (obu_is_global(obu)) { extradata_size += obu->raw_size; if (obu->type == AV1_OBU_SEQUENCE_HEADER) has_seq = 1; @@ -112,8 +141,7 @@ static int extract_extradata_av1(AVBSFContext *ctx, AVPacket *pkt, for (i = 0; i < s->av1_pkt.nb_obus; i++) { AV1OBU *obu = &s->av1_pkt.obus[i]; - if (val_in_array(extradata_obu_types, nb_extradata_obu_types, - obu->type)) { + if (obu_is_global(obu)) { bytestream2_put_bufferu(&pb_extradata, obu->raw_data, obu->raw_size); } else if (s->remove) { bytestream2_put_bufferu(&pb_filtered_data, obu->raw_data, obu->raw_size); @@ -134,6 +162,9 @@ static int extract_extradata_av1(AVBSFContext *ctx, AVPacket *pkt, static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt, uint8_t **data, int *size) { + static const int extradata_nal_types_vvc[] = { + VVC_VPS_NUT, VVC_SPS_NUT, VVC_PPS_NUT, + }; static const int extradata_nal_types_hevc[] = { HEVC_NAL_VPS, HEVC_NAL_SPS, HEVC_NAL_PPS, }; @@ -145,10 +176,13 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt, int extradata_size = 0, filtered_size = 0; const int *extradata_nal_types; - int nb_extradata_nal_types; + size_t nb_extradata_nal_types; int i, has_sps = 0, has_vps = 0, ret = 0; - if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) { + if (ctx->par_in->codec_id == AV_CODEC_ID_VVC) { + extradata_nal_types = extradata_nal_types_vvc; + nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types_vvc); + } else if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) { extradata_nal_types = extradata_nal_types_hevc; nb_extradata_nal_types = FF_ARRAY_ELEMS(extradata_nal_types_hevc); } else { @@ -165,7 +199,10 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt, H2645NAL *nal = &s->h2645_pkt.nals[i]; if (val_in_array(extradata_nal_types, nb_extradata_nal_types, nal->type)) { extradata_size += nal->raw_size + 3; - if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) { + if (ctx->par_in->codec_id == AV_CODEC_ID_VVC) { + if (nal->type == VVC_SPS_NUT) has_sps = 1; + if (nal->type == VVC_VPS_NUT) has_vps = 1; + } else if (ctx->par_in->codec_id == AV_CODEC_ID_HEVC) { if (nal->type == HEVC_NAL_SPS) has_sps = 1; if (nal->type == HEVC_NAL_VPS) has_vps = 1; } else { @@ -177,7 +214,8 @@ static int extract_extradata_h2645(AVBSFContext *ctx, AVPacket *pkt, } if (extradata_size && - ((ctx->par_in->codec_id == AV_CODEC_ID_HEVC && has_sps && has_vps) || + ((ctx->par_in->codec_id == AV_CODEC_ID_VVC && has_sps) || + (ctx->par_in->codec_id == AV_CODEC_ID_HEVC && has_sps && has_vps) || (ctx->par_in->codec_id == AV_CODEC_ID_H264 && has_sps))) { AVBufferRef *filtered_buf = NULL; PutByteContext pb_filtered_data, pb_extradata; @@ -335,6 +373,7 @@ static const struct { { AV_CODEC_ID_MPEG2VIDEO, extract_extradata_mpeg12 }, { AV_CODEC_ID_MPEG4, extract_extradata_mpeg4 }, { AV_CODEC_ID_VC1, extract_extradata_vc1 }, + { AV_CODEC_ID_VVC, extract_extradata_h2645 }, }; static int extract_extradata_init(AVBSFContext *ctx) @@ -404,6 +443,7 @@ static const enum AVCodecID codec_ids[] = { AV_CODEC_ID_MPEG2VIDEO, AV_CODEC_ID_MPEG4, AV_CODEC_ID_VC1, + AV_CODEC_ID_VVC, AV_CODEC_ID_NONE, }; diff --git a/libavcodec/filter_units_bsf.c b/libavcodec/bsf/filter_units.c similarity index 71% rename from libavcodec/filter_units_bsf.c rename to libavcodec/bsf/filter_units.c index 38756baf420..9aead542cc7 100644 --- a/libavcodec/filter_units_bsf.c +++ b/libavcodec/bsf/filter_units.c @@ -34,6 +34,8 @@ typedef struct FilterUnitsContext { const char *pass_types; const char *remove_types; + enum AVDiscard discard; + int discard_flags; enum { NOOP, @@ -109,7 +111,7 @@ static int filter_units_filter(AVBSFContext *bsf, AVPacket *pkt) if (err < 0) return err; - if (ctx->mode == NOOP) + if (ctx->mode == NOOP && ctx->discard <= AVDISCARD_DEFAULT) return 0; err = ff_cbs_read_packet(ctx->cbc, frag, pkt); @@ -118,14 +120,17 @@ static int filter_units_filter(AVBSFContext *bsf, AVPacket *pkt) goto fail; } - for (i = frag->nb_units - 1; i >= 0; i--) { - for (j = 0; j < ctx->nb_types; j++) { - if (frag->units[i].type == ctx->type_list[j]) - break; + ff_cbs_discard_units(ctx->cbc, frag, ctx->discard, ctx->discard_flags); + if (ctx->mode != NOOP) { + for (i = frag->nb_units - 1; i >= 0; i--) { + for (j = 0; j < ctx->nb_types; j++) { + if (frag->units[i].type == ctx->type_list[j]) + break; + } + if (ctx->mode == REMOVE ? j < ctx->nb_types + : j >= ctx->nb_types) + ff_cbs_delete_unit(frag, i); } - if (ctx->mode == REMOVE ? j < ctx->nb_types - : j >= ctx->nb_types) - ff_cbs_delete_unit(frag, i); } if (frag->nb_units == 0) { @@ -175,7 +180,7 @@ static int filter_units_init(AVBSFContext *bsf) av_log(bsf, AV_LOG_ERROR, "Failed to parse remove_types.\n"); return err; } - } else { + } else if (ctx->discard == AVDISCARD_NONE) { return 0; } @@ -183,9 +188,11 @@ static int filter_units_init(AVBSFContext *bsf) if (err < 0) return err; - // Don't actually decompose anything, we only want the unit data. - ctx->cbc->decompose_unit_types = ctx->type_list; - ctx->cbc->nb_decompose_unit_types = 0; + if (ctx->discard == AVDISCARD_NONE) { + // Don't actually decompose anything, we only want the unit data. + ctx->cbc->decompose_unit_types = ctx->type_list; + ctx->cbc->nb_decompose_unit_types = 0; + } if (bsf->par_in->extradata) { CodedBitstreamFragment *frag = &ctx->fragment; @@ -225,6 +232,37 @@ static const AVOption filter_units_options[] = { OFFSET(remove_types), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + { "discard", "Remove the selected frames", + OFFSET(discard), AV_OPT_TYPE_INT, + { .i64 = AVDISCARD_NONE }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"}, + { "none" , "discard none", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_NONE }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"}, + { "default" , "discard none, but can be changed after dynamically", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"}, + { "nonref", "discard all non-reference frames", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_NONREF }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"}, + { "bidir", "discard all bidirectional frames", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_BIDIR }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"}, + { "nonintra", "discard all frames except I frames", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_NONINTRA }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"}, + { "nonkey", "discard all frames except keyframes", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_NONKEY }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"}, + { "all", "discard all frames", + 0, AV_OPT_TYPE_CONST, + { .i64 = AVDISCARD_ALL }, INT_MIN, INT_MAX, FLAGS, .unit = "discard"}, + + { "discard_flags", "flags to control the discard frame behavior", + OFFSET(discard_flags), AV_OPT_TYPE_FLAGS, + { .i64 = DISCARD_FLAG_NONE }, INT_MIN, INT_MAX, FLAGS, .unit = "discard_flags"}, + { "keep_non_vcl", "non-vcl units even if the picture has been dropped", + 0, AV_OPT_TYPE_CONST, + { .i64 = DISCARD_FLAG_KEEP_NON_VCL }, INT_MIN, INT_MAX, FLAGS, .unit = "discard_flags"}, { NULL } }; diff --git a/libavcodec/h264_metadata_bsf.c b/libavcodec/bsf/h264_metadata.c similarity index 99% rename from libavcodec/h264_metadata_bsf.c rename to libavcodec/bsf/h264_metadata.c index b9cfeaba946..239e82aa3ab 100644 --- a/libavcodec/h264_metadata_bsf.c +++ b/libavcodec/bsf/h264_metadata.c @@ -26,10 +26,11 @@ #include "cbs.h" #include "cbs_bsf.h" #include "cbs_h264.h" +#include "cbs_sei.h" #include "h264.h" #include "h264_levels.h" -#include "h264_sei.h" #include "h2645data.h" +#include "sei.h" enum { FLIP_HORIZONTAL = 1, @@ -659,7 +660,7 @@ static const AVOption h264_metadata_options[] = { { .dbl = NAN }, -360.0, +360.0, FLAGS }, { "flip", "Set flip in display orientation SEI", OFFSET(flip), AV_OPT_TYPE_FLAGS, - { .i64 = 0 }, 0, FLIP_HORIZONTAL | FLIP_VERTICAL, FLAGS, "flip" }, + { .i64 = 0 }, 0, FLIP_HORIZONTAL | FLIP_VERTICAL, FLAGS, .unit = "flip" }, { "horizontal", "Set hor_flip", 0, AV_OPT_TYPE_CONST, { .i64 = FLIP_HORIZONTAL }, .flags = FLAGS, .unit = "flip" }, @@ -669,7 +670,7 @@ static const AVOption h264_metadata_options[] = { { "level", "Set level (table A-1)", OFFSET(level), AV_OPT_TYPE_INT, - { .i64 = LEVEL_UNSET }, LEVEL_UNSET, 0xff, FLAGS, "level" }, + { .i64 = LEVEL_UNSET }, LEVEL_UNSET, 0xff, FLAGS, .unit = "level" }, { "auto", "Attempt to guess level from stream properties", 0, AV_OPT_TYPE_CONST, { .i64 = LEVEL_AUTO }, .flags = FLAGS, .unit = "level" }, diff --git a/libavcodec/h264_mp4toannexb_bsf.c b/libavcodec/bsf/h264_mp4toannexb.c similarity index 65% rename from libavcodec/h264_mp4toannexb_bsf.c rename to libavcodec/bsf/h264_mp4toannexb.c index d11be455c28..92af6a68810 100644 --- a/libavcodec/h264_mp4toannexb_bsf.c +++ b/libavcodec/bsf/h264_mp4toannexb.c @@ -36,6 +36,8 @@ typedef struct H264BSFContext { uint8_t *pps; int sps_size; int pps_size; + unsigned sps_buf_size; + unsigned pps_buf_size; uint8_t length_size; uint8_t new_idr; uint8_t idr_sps_seen; @@ -43,10 +45,26 @@ typedef struct H264BSFContext { int extradata_parsed; } H264BSFContext; +enum PsSource { + PS_OUT_OF_BAND = -1, + PS_NONE = 0, + PS_IN_BAND = 1, +}; + static void count_or_copy(uint8_t **out, uint64_t *out_size, - const uint8_t *in, int in_size, int ps, int copy) + const uint8_t *in, int in_size, enum PsSource ps, int copy) { - uint8_t start_code_size = ps < 0 ? 0 : *out_size == 0 || ps ? 4 : 3; + uint8_t start_code_size; + + if (ps == PS_OUT_OF_BAND) + /* start code already present in out-of-band ps data, so don't need to + * add it manually again + */ + start_code_size = 0; + else if (ps == PS_IN_BAND || *out_size == 0) + start_code_size = 4; + else + start_code_size = 3; if (copy) { memcpy(*out + start_code_size, in, in_size); @@ -62,7 +80,8 @@ static void count_or_copy(uint8_t **out, uint64_t *out_size, *out_size += start_code_size + in_size; } -static int h264_extradata_to_annexb(AVBSFContext *ctx, const int padding) +static int h264_extradata_to_annexb(AVBSFContext *ctx, + uint8_t *extradata, int extradata_size) { H264BSFContext *s = ctx->priv_data; GetByteContext ogb, *gb = &ogb; @@ -70,9 +89,10 @@ static int h264_extradata_to_annexb(AVBSFContext *ctx, const int padding) uint32_t total_size = 0; uint8_t *out = NULL, unit_nb, sps_done = 0; static const uint8_t nalu_header[4] = { 0, 0, 0, 1 }; + const int padding = AV_INPUT_BUFFER_PADDING_SIZE; int length_size, pps_offset = 0; - bytestream2_init(gb, ctx->par_in->extradata, ctx->par_in->extradata_size); + bytestream2_init(gb, extradata, extradata_size); bytestream2_skipu(gb, 4); @@ -113,16 +133,33 @@ static int h264_extradata_to_annexb(AVBSFContext *ctx, const int padding) memset(out + total_size, 0, padding); if (pps_offset) { - s->sps = out; + uint8_t *sps; + s->sps_size = pps_offset; + sps = av_fast_realloc(s->sps, &s->sps_buf_size, s->sps_size); + if (!sps) { + av_free(out); + return AVERROR(ENOMEM); + } + s->sps = sps; + memcpy(s->sps, out, s->sps_size); } else { av_log(ctx, AV_LOG_WARNING, "Warning: SPS NALU missing or invalid. " "The resulting stream may not play.\n"); } if (pps_offset < total_size) { - s->pps = out + pps_offset; + uint8_t *pps; + s->pps_size = total_size - pps_offset; + pps = av_fast_realloc(s->pps, &s->pps_buf_size, s->pps_size); + if (!pps) { + av_freep(&s->sps); + av_free(out); + return AVERROR(ENOMEM); + } + s->pps = pps; + memcpy(s->pps, out + pps_offset, s->pps_size); } else { av_log(ctx, AV_LOG_WARNING, "Warning: PPS NALU missing or invalid. " @@ -133,14 +170,90 @@ static int h264_extradata_to_annexb(AVBSFContext *ctx, const int padding) ctx->par_out->extradata = out; ctx->par_out->extradata_size = total_size; - return length_size; + s->length_size = length_size; + s->new_idr = 1; + s->idr_sps_seen = 0; + s->idr_pps_seen = 0; + s->extradata_parsed = 1; + + return 0; +} + +static int h264_mp4toannexb_save_ps(uint8_t **dst, int *dst_size, + unsigned *dst_buf_size, + const uint8_t *nal, uint32_t nal_size, + int first) +{ + static const uint8_t nalu_header[4] = { 0, 0, 0, 1 }; + const int start_code_size = sizeof(nalu_header); + uint8_t *ptr; + uint32_t size; + + if (first) + size = 0; + else + size = *dst_size; + + ptr = av_fast_realloc(*dst, dst_buf_size, size + nal_size + start_code_size); + if (!ptr) + return AVERROR(ENOMEM); + + memcpy(ptr + size, nalu_header, start_code_size); + size += start_code_size; + memcpy(ptr + size, nal, nal_size); + size += nal_size; + + *dst = ptr; + *dst_size = size; + return 0; +} + +static int h264_mp4toannexb_filter_ps(H264BSFContext *s, + const uint8_t *buf, + const uint8_t *buf_end) +{ + int sps_count = 0; + int pps_count = 0; + uint8_t unit_type; + + do { + uint32_t nal_size = 0; + + /* possible overread ok due to padding */ + for (int i = 0; i < s->length_size; i++) + nal_size = (nal_size << 8) | buf[i]; + + buf += s->length_size; + + /* This check requires the cast as the right side might + * otherwise be promoted to an unsigned value. */ + if ((int64_t)nal_size > buf_end - buf) + return AVERROR_INVALIDDATA; + + if (!nal_size) + continue; + + unit_type = *buf & 0x1f; + + if (unit_type == H264_NAL_SPS) { + h264_mp4toannexb_save_ps(&s->sps, &s->sps_size, &s->sps_buf_size, buf, + nal_size, !sps_count); + sps_count++; + } else if (unit_type == H264_NAL_PPS) { + h264_mp4toannexb_save_ps(&s->pps, &s->pps_size, &s->pps_buf_size, buf, + nal_size, !pps_count); + pps_count++; + } + + buf += nal_size; + } while (buf < buf_end); + + return 0; } static int h264_mp4toannexb_init(AVBSFContext *ctx) { - H264BSFContext *s = ctx->priv_data; int extra_size = ctx->par_in->extradata_size; - int ret; /* retrieve sps and pps NAL units from extradata */ if (!extra_size || @@ -149,15 +262,9 @@ static int h264_mp4toannexb_init(AVBSFContext *ctx) av_log(ctx, AV_LOG_VERBOSE, "The input looks like it is Annex B already\n"); } else if (extra_size >= 7) { - ret = h264_extradata_to_annexb(ctx, AV_INPUT_BUFFER_PADDING_SIZE); - if (ret < 0) - return ret; - - s->length_size = ret; - s->new_idr = 1; - s->idr_sps_seen = 0; - s->idr_pps_seen = 0; - s->extradata_parsed = 1; + return h264_extradata_to_annexb(ctx, + ctx->par_in->extradata, + ctx->par_in->extradata_size); } else { av_log(ctx, AV_LOG_ERROR, "Invalid extradata size: %d\n", extra_size); return AVERROR_INVALIDDATA; @@ -176,11 +283,21 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt) uint8_t *out; uint64_t out_size; int ret; + size_t extradata_size; + uint8_t *extradata; ret = ff_bsf_get_packet(ctx, &in); if (ret < 0) return ret; + extradata = av_packet_get_side_data(in, AV_PKT_DATA_NEW_EXTRADATA, + &extradata_size); + if (extradata) { + ret = h264_extradata_to_annexb(ctx, extradata, extradata_size); + if (ret < 0) + goto fail; + } + /* nothing to filter */ if (!s->extradata_parsed) { av_packet_move_ref(opkt, in); @@ -189,6 +306,9 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt) } buf_end = in->data + in->size; + ret = h264_mp4toannexb_filter_ps(s, in->data, buf_end); + if (ret < 0) + goto fail; #define LOG_ONCE(...) \ if (j) \ @@ -202,6 +322,7 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt) do { uint32_t nal_size = 0; + enum PsSource ps; /* possible overread ok due to padding */ for (int i = 0; i < s->length_size; i++) @@ -230,7 +351,7 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt) if (!s->sps_size) { LOG_ONCE(ctx, AV_LOG_WARNING, "SPS not present in the stream, nor in AVCC, stream may be unreadable\n"); } else { - count_or_copy(&out, &out_size, s->sps, s->sps_size, -1, j); + count_or_copy(&out, &out_size, s->sps, s->sps_size, PS_OUT_OF_BAND, j); sps_seen = 1; } } @@ -244,22 +365,26 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt) /* prepend only to the first type 5 NAL unit of an IDR picture, if no sps/pps are already present */ if (new_idr && unit_type == H264_NAL_IDR_SLICE && !sps_seen && !pps_seen) { - if (ctx->par_out->extradata) - count_or_copy(&out, &out_size, ctx->par_out->extradata, - ctx->par_out->extradata_size, -1, j); + if (s->sps_size) + count_or_copy(&out, &out_size, s->sps, s->sps_size, PS_OUT_OF_BAND, j); + if (s->pps_size) + count_or_copy(&out, &out_size, s->pps, s->pps_size, PS_OUT_OF_BAND, j); new_idr = 0; /* if only SPS has been seen, also insert PPS */ } else if (new_idr && unit_type == H264_NAL_IDR_SLICE && sps_seen && !pps_seen) { if (!s->pps_size) { LOG_ONCE(ctx, AV_LOG_WARNING, "PPS not present in the stream, nor in AVCC, stream may be unreadable\n"); } else { - count_or_copy(&out, &out_size, s->pps, s->pps_size, -1, j); + count_or_copy(&out, &out_size, s->pps, s->pps_size, PS_OUT_OF_BAND, j); } } - count_or_copy(&out, &out_size, buf, nal_size, - unit_type == H264_NAL_SPS || unit_type == H264_NAL_PPS, j); - if (!new_idr && unit_type == H264_NAL_SLICE) { + if (unit_type == H264_NAL_SPS || unit_type == H264_NAL_PPS) + ps = PS_IN_BAND; + else + ps = PS_NONE; + count_or_copy(&out, &out_size, buf, nal_size, ps, j); + if (unit_type == H264_NAL_SLICE) { new_idr = 1; sps_seen = 0; pps_seen = 0; @@ -299,6 +424,14 @@ static int h264_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *opkt) return ret; } +static void h264_mp4toannexb_close(AVBSFContext *ctx) +{ + H264BSFContext *s = ctx->priv_data; + + av_freep(&s->sps); + av_freep(&s->pps); +} + static void h264_mp4toannexb_flush(AVBSFContext *ctx) { H264BSFContext *s = ctx->priv_data; @@ -318,5 +451,6 @@ const FFBitStreamFilter ff_h264_mp4toannexb_bsf = { .priv_data_size = sizeof(H264BSFContext), .init = h264_mp4toannexb_init, .filter = h264_mp4toannexb_filter, + .close = h264_mp4toannexb_close, .flush = h264_mp4toannexb_flush, }; diff --git a/libavcodec/h264_redundant_pps_bsf.c b/libavcodec/bsf/h264_redundant_pps.c similarity index 100% rename from libavcodec/h264_redundant_pps_bsf.c rename to libavcodec/bsf/h264_redundant_pps.c diff --git a/libavcodec/h265_metadata_bsf.c b/libavcodec/bsf/h265_metadata.c similarity index 99% rename from libavcodec/h265_metadata_bsf.c rename to libavcodec/bsf/h265_metadata.c index 6787bd14a12..c9e1cc3eed6 100644 --- a/libavcodec/h265_metadata_bsf.c +++ b/libavcodec/bsf/h265_metadata.c @@ -456,7 +456,7 @@ static const AVOption h265_metadata_options[] = { { "level", "Set level (tables A.6 and A.7)", OFFSET(level), AV_OPT_TYPE_INT, - { .i64 = LEVEL_UNSET }, LEVEL_UNSET, 0xff, FLAGS, "level" }, + { .i64 = LEVEL_UNSET }, LEVEL_UNSET, 0xff, FLAGS, .unit = "level" }, { "auto", "Attempt to guess level from stream properties", 0, AV_OPT_TYPE_CONST, { .i64 = LEVEL_AUTO }, .flags = FLAGS, .unit = "level" }, diff --git a/libavcodec/bsf/h266_metadata.c b/libavcodec/bsf/h266_metadata.c new file mode 100644 index 00000000000..1f0f875cfee --- /dev/null +++ b/libavcodec/bsf/h266_metadata.c @@ -0,0 +1,150 @@ +/* + * 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/common.h" +#include "libavutil/opt.h" + +#include "bsf.h" +#include "bsf_internal.h" +#include "cbs.h" +#include "cbs_bsf.h" +#include "cbs_h266.h" +#include "vvc.h" + +#define IS_H266_SLICE(nut) (nut <= VVC_RASL_NUT || (nut >= VVC_IDR_W_RADL && nut <= VVC_GDR_NUT)) + +typedef struct H266MetadataContext { + CBSBSFContext common; + + H266RawAUD aud_nal; + + int aud; +} H266MetadataContext; + +static int h266_metadata_update_fragment(AVBSFContext *bsf, AVPacket *pkt, + CodedBitstreamFragment *pu) +{ + H266MetadataContext *ctx = bsf->priv_data; + int err, i; + + // If an AUD is present, it must be the first NAL unit. + if (pu->nb_units && pu->units[0].type == VVC_AUD_NUT) { + if (ctx->aud == BSF_ELEMENT_REMOVE) + ff_cbs_delete_unit(pu, 0); + } else if ( pkt && ctx->aud == BSF_ELEMENT_INSERT) { + const H266RawSlice *first_slice = NULL; + const H266RawPictureHeader *ph = NULL; + H266RawAUD *aud = &ctx->aud_nal; + int pic_type = 0, temporal_id = 8, layer_id = 0; + for (i = 0; i < pu->nb_units; i++) { + const H266RawNALUnitHeader *nal = pu->units[i].content; + if (!nal) + continue; + if (nal->nuh_temporal_id_plus1 < temporal_id + 1) + temporal_id = nal->nuh_temporal_id_plus1 - 1; + if ( nal->nal_unit_type == VVC_PH_NUT ) { + const H266RawPH *header = pu->units[i].content; + ph = &header->ph_picture_header; + } else if (IS_H266_SLICE(nal->nal_unit_type)) { + const H266RawSlice *slice = pu->units[i].content; + layer_id = nal->nuh_layer_id; + if (slice->header.sh_slice_type == VVC_SLICE_TYPE_B && + pic_type < 2) + pic_type = 2; + if (slice->header.sh_slice_type == VVC_SLICE_TYPE_P && + pic_type < 1) + pic_type = 1; + if (!first_slice) { + first_slice = slice; + if (first_slice->header. + sh_picture_header_in_slice_header_flag) + ph = &first_slice->header.sh_picture_header; + else if (!ph) + break; + } + } + } + if (!ph) { + av_log(bsf, AV_LOG_ERROR, "no avaliable picture header"); + return AVERROR_INVALIDDATA; + } + + aud->nal_unit_header = (H266RawNALUnitHeader) { + .nal_unit_type = VVC_AUD_NUT, + .nuh_layer_id = layer_id, + .nuh_temporal_id_plus1 = temporal_id + 1, + }; + aud->aud_pic_type = pic_type; + aud->aud_irap_or_gdr_flag = ph->ph_gdr_or_irap_pic_flag; + + err = ff_cbs_insert_unit_content(pu, 0, VVC_AUD_NUT, aud, NULL); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n"); + return err; + } + } + + /* TODO: implement more metadata parsing, like VUI, Levels etc. */ + //for (i = 0; i < pu->nb_units; i++) { + // if (pu->units[i].type == VVC_SPS_NUT) { + // } + //} + return 0; +} + +static const CBSBSFType h266_metadata_type = { + .codec_id = AV_CODEC_ID_VVC, + .fragment_name = "access unit", + .unit_name = "NAL unit", + .update_fragment = &h266_metadata_update_fragment, +}; + +static int h266_metadata_init(AVBSFContext *bsf) +{ + return ff_cbs_bsf_generic_init(bsf, &h266_metadata_type); +} + +#define OFFSET(x) offsetof(H266MetadataContext, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM) +static const AVOption h266_metadata_options[] = { + BSF_ELEMENT_OPTIONS_PIR("aud", "Access Unit Delimiter NAL units", + aud, FLAGS), + + { NULL } +}; + +static const AVClass h266_metadata_class = { + .class_name = "h266_metadata_bsf", + .item_name = av_default_item_name, + .option = h266_metadata_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static const enum AVCodecID h266_metadata_codec_ids[] = { + AV_CODEC_ID_VVC, AV_CODEC_ID_NONE, +}; + +const FFBitStreamFilter ff_vvc_metadata_bsf = { + .p.name = "vvc_metadata", + .p.codec_ids = h266_metadata_codec_ids, + .p.priv_class = &h266_metadata_class, + .priv_data_size = sizeof(H266MetadataContext), + .init = &h266_metadata_init, + .close = &ff_cbs_bsf_generic_close, + .filter = &ff_cbs_bsf_generic_filter, +}; diff --git a/libavcodec/hapqa_extract_bsf.c b/libavcodec/bsf/hapqa_extract.c similarity index 95% rename from libavcodec/hapqa_extract_bsf.c rename to libavcodec/bsf/hapqa_extract.c index 0d9b40aaa67..5c49a4de227 100644 --- a/libavcodec/hapqa_extract_bsf.c +++ b/libavcodec/bsf/hapqa_extract.c @@ -30,6 +30,8 @@ #include "bytestream.h" #include "hap.h" +#include "libavutil/opt.h" + typedef struct HapqaExtractContext { const AVClass *class; int texture;/* index of the texture to keep (0 for rgb or 1 for alpha) */ @@ -112,9 +114,9 @@ static const enum AVCodecID codec_ids[] = { #define OFFSET(x) offsetof(HapqaExtractContext, x) #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_BSF_PARAM) static const AVOption options[] = { - { "texture", "texture to keep", OFFSET(texture), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, "texture" }, - { "color", "keep HapQ texture", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "texture" }, - { "alpha", "keep HapAlphaOnly texture", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "texture" }, + { "texture", "texture to keep", OFFSET(texture), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, .unit = "texture" }, + { "color", "keep HapQ texture", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, .unit = "texture" }, + { "alpha", "keep HapAlphaOnly texture", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, .unit = "texture" }, { NULL }, }; diff --git a/libavcodec/hevc_mp4toannexb_bsf.c b/libavcodec/bsf/hevc_mp4toannexb.c similarity index 94% rename from libavcodec/hevc_mp4toannexb_bsf.c rename to libavcodec/bsf/hevc_mp4toannexb.c index f9a025a36b1..8eec18f31e6 100644 --- a/libavcodec/hevc_mp4toannexb_bsf.c +++ b/libavcodec/bsf/hevc_mp4toannexb.c @@ -65,9 +65,11 @@ static int hevc_extradata_to_annexb(AVBSFContext *ctx) } for (j = 0; j < cnt; j++) { - int nalu_len = bytestream2_get_be16(&gb); + const int nalu_len = bytestream2_get_be16(&gb); - if (4 + AV_INPUT_BUFFER_PADDING_SIZE + nalu_len > SIZE_MAX - new_extradata_size) { + if (!nalu_len || + nalu_len > bytestream2_get_bytes_left(&gb) || + 4 + AV_INPUT_BUFFER_PADDING_SIZE + nalu_len > SIZE_MAX - new_extradata_size) { ret = AVERROR_INVALIDDATA; goto fail; } @@ -157,7 +159,8 @@ static int hevc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out) nalu_type = (bytestream2_peek_byte(&gb) >> 1) & 0x3f; /* prepend extradata to IRAP frames */ - is_irap = nalu_type >= 16 && nalu_type <= 23; + is_irap = nalu_type >= HEVC_NAL_BLA_W_LP && + nalu_type <= HEVC_NAL_RSV_IRAP_VCL23; add_extradata = is_irap && !got_irap; extra_size = add_extradata * ctx->par_out->extradata_size; got_irap |= is_irap; diff --git a/libavcodec/imx_dump_header_bsf.c b/libavcodec/bsf/imx_dump_header.c similarity index 100% rename from libavcodec/imx_dump_header_bsf.c rename to libavcodec/bsf/imx_dump_header.c diff --git a/libavcodec/media100_to_mjpegb_bsf.c b/libavcodec/bsf/media100_to_mjpegb.c similarity index 100% rename from libavcodec/media100_to_mjpegb_bsf.c rename to libavcodec/bsf/media100_to_mjpegb.c diff --git a/libavcodec/mjpeg2jpeg_bsf.c b/libavcodec/bsf/mjpeg2jpeg.c similarity index 100% rename from libavcodec/mjpeg2jpeg_bsf.c rename to libavcodec/bsf/mjpeg2jpeg.c diff --git a/libavcodec/mjpega_dump_header_bsf.c b/libavcodec/bsf/mjpega_dump_header.c similarity index 100% rename from libavcodec/mjpega_dump_header_bsf.c rename to libavcodec/bsf/mjpega_dump_header.c diff --git a/libavcodec/movsub_bsf.c b/libavcodec/bsf/movsub.c similarity index 100% rename from libavcodec/movsub_bsf.c rename to libavcodec/bsf/movsub.c diff --git a/libavcodec/mpeg2_metadata_bsf.c b/libavcodec/bsf/mpeg2_metadata.c similarity index 100% rename from libavcodec/mpeg2_metadata_bsf.c rename to libavcodec/bsf/mpeg2_metadata.c diff --git a/libavcodec/mpeg4_unpack_bframes_bsf.c b/libavcodec/bsf/mpeg4_unpack_bframes.c similarity index 100% rename from libavcodec/mpeg4_unpack_bframes_bsf.c rename to libavcodec/bsf/mpeg4_unpack_bframes.c diff --git a/libavcodec/noise_bsf.c b/libavcodec/bsf/noise.c similarity index 88% rename from libavcodec/noise_bsf.c rename to libavcodec/bsf/noise.c index 7bdaa3c1db6..3a0f388dd62 100644 --- a/libavcodec/noise_bsf.c +++ b/libavcodec/bsf/noise.c @@ -28,18 +28,18 @@ #include "libavutil/eval.h" static const char *const var_names[] = { - "n", /// packet index, starting from zero - "tb", /// timebase - "pts", /// packet presentation timestamp - "dts", /// packet decoding timestamp - "nopts", /// AV_NOPTS_VALUE - "startpts", /// first seen non-AV_NOPTS_VALUE packet timestamp - "startdts", /// first seen non-AV_NOPTS_VALUE packet timestamp - "duration", "d", /// packet duration - "pos", /// original position of packet in its source - "size", /// packet size - "key" , /// packet keyframe flag - "state", /// random-ish state + "n", ///< packet index, starting from zero + "tb", ///< timebase + "pts", ///< packet presentation timestamp + "dts", ///< packet decoding timestamp + "nopts", ///< AV_NOPTS_VALUE + "startpts", ///< first seen non-AV_NOPTS_VALUE packet timestamp + "startdts", ///< first seen non-AV_NOPTS_VALUE packet timestamp + "duration", "d", ///< packet duration + "pos", ///< original position of packet in its source + "size", ///< packet size + "key" , ///< packet keyframe flag + "state", ///< random-ish state NULL }; diff --git a/libavcodec/null_bsf.c b/libavcodec/bsf/null.c similarity index 100% rename from libavcodec/null_bsf.c rename to libavcodec/bsf/null.c diff --git a/libavcodec/opus_metadata_bsf.c b/libavcodec/bsf/opus_metadata.c similarity index 100% rename from libavcodec/opus_metadata_bsf.c rename to libavcodec/bsf/opus_metadata.c diff --git a/libavcodec/pcm_rechunk_bsf.c b/libavcodec/bsf/pcm_rechunk.c similarity index 91% rename from libavcodec/pcm_rechunk_bsf.c rename to libavcodec/bsf/pcm_rechunk.c index 108d9e90b99..b1b57f96a93 100644 --- a/libavcodec/pcm_rechunk_bsf.c +++ b/libavcodec/bsf/pcm_rechunk.c @@ -108,6 +108,18 @@ static int get_next_nb_samples(AVBSFContext *ctx) } } +static void set_silence(AVCodecParameters *par, uint8_t *buf, size_t size) +{ + int c = 0; + switch (par->codec_id) { + case AV_CODEC_ID_PCM_ALAW: c = 0xd5; break; + case AV_CODEC_ID_PCM_MULAW: + case AV_CODEC_ID_PCM_VIDC: c = 0xff; break; + case AV_CODEC_ID_PCM_U8: c = 0x80; break; + } + memset(buf, c, size); +} + static int rechunk_filter(AVBSFContext *ctx, AVPacket *pkt) { PCMContext *s = ctx->priv_data; @@ -139,6 +151,7 @@ static int rechunk_filter(AVBSFContext *ctx, AVPacket *pkt) av_packet_move_ref(pkt, s->out_pkt); return send_packet(s, nb_samples, pkt); } + av_assert0(!s->in_pkt->size); } else if (s->in_pkt->size > data_size) { ret = av_packet_ref(pkt, s->in_pkt); if (ret < 0) @@ -151,12 +164,13 @@ static int rechunk_filter(AVBSFContext *ctx, AVPacket *pkt) av_packet_move_ref(pkt, s->in_pkt); return send_packet(s, nb_samples, pkt); } - } + } else + av_packet_unref(s->in_pkt); ret = ff_bsf_get_packet_ref(ctx, s->in_pkt); if (ret == AVERROR_EOF && s->out_pkt->size) { if (s->pad) { - memset(s->out_pkt->data + s->out_pkt->size, 0, data_size - s->out_pkt->size); + set_silence(ctx->par_in, s->out_pkt->data + s->out_pkt->size, data_size - s->out_pkt->size); s->out_pkt->size = data_size; } else { nb_samples = s->out_pkt->size / s->sample_size; @@ -191,21 +205,27 @@ static const AVClass pcm_rechunk_class = { }; static const enum AVCodecID codec_ids[] = { - AV_CODEC_ID_PCM_S16LE, - AV_CODEC_ID_PCM_S16BE, - AV_CODEC_ID_PCM_S8, - AV_CODEC_ID_PCM_S32LE, - AV_CODEC_ID_PCM_S32BE, - AV_CODEC_ID_PCM_S24LE, - AV_CODEC_ID_PCM_S24BE, + AV_CODEC_ID_PCM_ALAW, + AV_CODEC_ID_PCM_F16LE, + AV_CODEC_ID_PCM_F24LE, AV_CODEC_ID_PCM_F32BE, AV_CODEC_ID_PCM_F32LE, AV_CODEC_ID_PCM_F64BE, AV_CODEC_ID_PCM_F64LE, - AV_CODEC_ID_PCM_S64LE, + AV_CODEC_ID_PCM_MULAW, + AV_CODEC_ID_PCM_S16BE, + AV_CODEC_ID_PCM_S16LE, + AV_CODEC_ID_PCM_S24BE, + AV_CODEC_ID_PCM_S24DAUD, + AV_CODEC_ID_PCM_S24LE, + AV_CODEC_ID_PCM_S32BE, + AV_CODEC_ID_PCM_S32LE, AV_CODEC_ID_PCM_S64BE, - AV_CODEC_ID_PCM_F16LE, - AV_CODEC_ID_PCM_F24LE, + AV_CODEC_ID_PCM_S64LE, + AV_CODEC_ID_PCM_S8, + AV_CODEC_ID_PCM_SGA, + AV_CODEC_ID_PCM_U8, + AV_CODEC_ID_PCM_VIDC, AV_CODEC_ID_NONE, }; diff --git a/libavcodec/pgs_frame_merge_bsf.c b/libavcodec/bsf/pgs_frame_merge.c similarity index 100% rename from libavcodec/pgs_frame_merge_bsf.c rename to libavcodec/bsf/pgs_frame_merge.c diff --git a/libavcodec/prores_metadata_bsf.c b/libavcodec/bsf/prores_metadata.c similarity index 81% rename from libavcodec/prores_metadata_bsf.c rename to libavcodec/bsf/prores_metadata.c index fb001b38d73..32af17cedf7 100644 --- a/libavcodec/prores_metadata_bsf.c +++ b/libavcodec/bsf/prores_metadata.c @@ -132,29 +132,29 @@ static int prores_metadata_init(AVBSFContext *bsf) #define OFFSET(x) offsetof(ProresMetadataContext, x) #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_BSF_PARAM) static const AVOption options[] = { - {"color_primaries", "select color primaries", OFFSET(color_primaries), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_PRI_SMPTE432, FLAGS, "color_primaries"}, - {"auto", "keep the same color primaries", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT709}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470BG}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE170M}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"bt2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT2020}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"smpte431", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE431}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"smpte432", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE432}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - - {"color_trc", "select color transfer", OFFSET(transfer_characteristics), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_TRC_NB - 1, FLAGS, "color_trc"}, - {"auto", "keep the same color transfer", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT709}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"smpte2084", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE2084}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"arib-std-b67", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_ARIB_STD_B67}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - - {"colorspace", "select colorspace", OFFSET(matrix_coefficients), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_SPC_BT2020_NCL, FLAGS, "colorspace"}, - {"auto", "keep the same colorspace", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT709}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE170M}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"bt2020nc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_NCL}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, + {"color_primaries", "select color primaries", OFFSET(color_primaries), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_PRI_SMPTE432, FLAGS, .unit = "color_primaries"}, + {"auto", "keep the same color primaries", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT709}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470BG}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE170M}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"bt2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT2020}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"smpte431", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE431}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"smpte432", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE432}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + + {"color_trc", "select color transfer", OFFSET(transfer_characteristics), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_TRC_NB - 1, FLAGS, .unit = "color_trc"}, + {"auto", "keep the same color transfer", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT709}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"smpte2084", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE2084}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"arib-std-b67", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_ARIB_STD_B67}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + + {"colorspace", "select colorspace", OFFSET(matrix_coefficients), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_SPC_BT2020_NCL, FLAGS, .unit = "colorspace"}, + {"auto", "keep the same colorspace", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT709}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE170M}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"bt2020nc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_NCL}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, { NULL }, }; diff --git a/libavcodec/remove_extradata_bsf.c b/libavcodec/bsf/remove_extradata.c similarity index 99% rename from libavcodec/remove_extradata_bsf.c rename to libavcodec/bsf/remove_extradata.c index 66b7d00bd88..3010eba058b 100644 --- a/libavcodec/remove_extradata_bsf.c +++ b/libavcodec/bsf/remove_extradata.c @@ -230,7 +230,7 @@ static int remove_extradata(AVBSFContext *ctx, AVPacket *pkt) #define OFFSET(x) offsetof(RemoveExtradataContext, x) #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_BSF_PARAM) static const AVOption options[] = { - { "freq", NULL, OFFSET(freq), AV_OPT_TYPE_INT, { .i64 = REMOVE_FREQ_KEYFRAME }, REMOVE_FREQ_KEYFRAME, REMOVE_FREQ_NONKEYFRAME, FLAGS, "freq" }, + { "freq", NULL, OFFSET(freq), AV_OPT_TYPE_INT, { .i64 = REMOVE_FREQ_KEYFRAME }, REMOVE_FREQ_KEYFRAME, REMOVE_FREQ_NONKEYFRAME, FLAGS, .unit = "freq" }, { "k", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = REMOVE_FREQ_NONKEYFRAME }, .flags = FLAGS, .unit = "freq" }, { "keyframe", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = REMOVE_FREQ_KEYFRAME }, .flags = FLAGS, .unit = "freq" }, { "e", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = REMOVE_FREQ_ALL }, .flags = FLAGS, .unit = "freq" }, diff --git a/libavcodec/setts_bsf.c b/libavcodec/bsf/setts.c similarity index 100% rename from libavcodec/setts_bsf.c rename to libavcodec/bsf/setts.c diff --git a/libavcodec/bsf/showinfo.c b/libavcodec/bsf/showinfo.c new file mode 100644 index 00000000000..4e31e0b5cba --- /dev/null +++ b/libavcodec/bsf/showinfo.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024 Anton Khirnov + * + * 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 + +#include "bsf.h" +#include "bsf_internal.h" + +#include "libavutil/log.h" +#include "libavutil/timestamp.h" + +typedef struct ShowinfoContext { + uint64_t nb_packets; +} ShowinfoContext; + +static int showinfo_filter(AVBSFContext *ctx, AVPacket *pkt) +{ + ShowinfoContext *priv = ctx->priv_data; + int ret; + + ret = ff_bsf_get_packet_ref(ctx, pkt); + if (ret < 0) + return ret; + + av_log(ctx, AV_LOG_INFO, + "n:%7"PRIu64" " + "size:%7d " + "pts:%s pt:%s " + "dts:%s dt:%s " + "ds:%"PRId64" d:%s " + "\n", + priv->nb_packets, pkt->size, + av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ctx->time_base_in), + av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ctx->time_base_in), + pkt->duration, av_ts2timestr(pkt->duration, &ctx->time_base_in)); + + priv->nb_packets++; + + return 0; +} + +const FFBitStreamFilter ff_showinfo_bsf = { + .p.name = "showinfo", + .filter = showinfo_filter, + .priv_data_size = sizeof(ShowinfoContext), +}; diff --git a/libavcodec/trace_headers_bsf.c b/libavcodec/bsf/trace_headers.c similarity index 97% rename from libavcodec/trace_headers_bsf.c rename to libavcodec/bsf/trace_headers.c index 028b0a1e599..8781f5f1001 100644 --- a/libavcodec/trace_headers_bsf.c +++ b/libavcodec/bsf/trace_headers.c @@ -44,6 +44,8 @@ static int trace_headers_init(AVBSFContext *bsf) ctx->cbc->trace_enable = 1; ctx->cbc->trace_level = AV_LOG_INFO; + ctx->cbc->trace_context = ctx->cbc; + ctx->cbc->trace_read_callback = ff_cbs_trace_read_log; if (bsf->par_in->extradata) { CodedBitstreamFragment *frag = &ctx->fragment; diff --git a/libavcodec/truehd_core_bsf.c b/libavcodec/bsf/truehd_core.c similarity index 100% rename from libavcodec/truehd_core_bsf.c rename to libavcodec/bsf/truehd_core.c diff --git a/libavcodec/vp9_metadata_bsf.c b/libavcodec/bsf/vp9_metadata.c similarity index 97% rename from libavcodec/vp9_metadata_bsf.c rename to libavcodec/bsf/vp9_metadata.c index 40d1b21c67a..355ffd5f8dc 100644 --- a/libavcodec/vp9_metadata_bsf.c +++ b/libavcodec/bsf/vp9_metadata.c @@ -97,7 +97,7 @@ static int vp9_metadata_init(AVBSFContext *bsf) static const AVOption vp9_metadata_options[] = { { "color_space", "Set colour space (section 7.2.2)", OFFSET(color_space), AV_OPT_TYPE_INT, - { .i64 = -1 }, -1, VP9_CS_RGB, FLAGS, "cs" }, + { .i64 = -1 }, -1, VP9_CS_RGB, FLAGS, .unit = "cs" }, { "unknown", "Unknown/unspecified", 0, AV_OPT_TYPE_CONST, { .i64 = VP9_CS_UNKNOWN }, .flags = FLAGS, .unit = "cs" }, { "bt601", "ITU-R BT.601-7", 0, AV_OPT_TYPE_CONST, @@ -115,7 +115,7 @@ static const AVOption vp9_metadata_options[] = { { "color_range", "Set colour range (section 7.2.2)", OFFSET(color_range), AV_OPT_TYPE_INT, - { .i64 = -1 }, -1, 1, FLAGS, "cr" }, + { .i64 = -1 }, -1, 1, FLAGS, .unit = "cr" }, { "tv", "TV (limited) range", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, .flags = FLAGS, .unit = "cr" }, { "pc", "PC (full) range", 0, AV_OPT_TYPE_CONST, diff --git a/libavcodec/vp9_raw_reorder_bsf.c b/libavcodec/bsf/vp9_raw_reorder.c similarity index 100% rename from libavcodec/vp9_raw_reorder_bsf.c rename to libavcodec/bsf/vp9_raw_reorder.c diff --git a/libavcodec/vp9_superframe_bsf.c b/libavcodec/bsf/vp9_superframe.c similarity index 100% rename from libavcodec/vp9_superframe_bsf.c rename to libavcodec/bsf/vp9_superframe.c diff --git a/libavcodec/vp9_superframe_split_bsf.c b/libavcodec/bsf/vp9_superframe_split.c similarity index 100% rename from libavcodec/vp9_superframe_split_bsf.c rename to libavcodec/bsf/vp9_superframe_split.c diff --git a/libavcodec/bsf/vvc_mp4toannexb.c b/libavcodec/bsf/vvc_mp4toannexb.c new file mode 100644 index 00000000000..36bdae8f494 --- /dev/null +++ b/libavcodec/bsf/vvc_mp4toannexb.c @@ -0,0 +1,328 @@ +/* + * H.266/VVC MP4 to Annex B byte stream format filter + * Copyright (c) 2022, Thomas Siedel + * + * 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 + +#include "libavutil/intreadwrite.h" +#include "libavutil/mem.h" + +#include "bsf.h" +#include "bsf_internal.h" +#include "bytestream.h" +#include "defs.h" +#include "vvc.h" + +#define MIN_VVCC_LENGTH 23 + +typedef struct VVCBSFContext { + uint8_t length_size; + int extradata_parsed; +} VVCBSFContext; + +static int vvc_extradata_to_annexb(AVBSFContext *ctx) +{ + GetByteContext gb; + int length_size, num_arrays, i, j; + int ret = 0; + int temp = 0; + int ptl_present; + + uint8_t *new_extradata = NULL; + size_t new_extradata_size = 0; + + int max_picture_width = 0; + int max_picture_height = 0; + int avg_frame_rate = 0; + + bytestream2_init(&gb, ctx->par_in->extradata, ctx->par_in->extradata_size); + temp = bytestream2_get_byte(&gb); + length_size = ((temp & 6) >> 1) + 1; + ptl_present = temp & 1; + if (ptl_present) { + int num_bytes_constraint_info; + int general_profile_idc; + int general_tier_flag; + int general_level_idc; + int ptl_frame_only_constraint_flag; + int ptl_multi_layer_enabled_flag; + int ptl_num_sub_profiles; + int temp3, temp4, temp5; + int temp2 = bytestream2_get_be16(&gb); + int ols_idx = (temp2 >> 7) & 0x1ff; + int num_sublayers = (temp2 >> 4) & 0x7; + int constant_frame_rate = (temp2 >> 2) & 0x3; + int chroma_format_idc = temp2 & 0x3; + int bit_depth_minus8 = (bytestream2_get_byte(&gb) >> 5) & 0x7; + av_log(ctx, AV_LOG_DEBUG, + "bit_depth_minus8 %d chroma_format_idc %d\n", bit_depth_minus8, + chroma_format_idc); + av_log(ctx, AV_LOG_DEBUG, "constant_frame_rate %d, ols_idx %d\n", + constant_frame_rate, ols_idx); + // VvcPTLRecord(num_sublayers) native_ptl + temp3 = bytestream2_get_byte(&gb); + num_bytes_constraint_info = (temp3) & 0x3f; + temp4 = bytestream2_get_byte(&gb); + general_profile_idc = (temp4 >> 1) & 0x7f; + general_tier_flag = (temp4) & 1; + general_level_idc = bytestream2_get_byte(&gb); + av_log(ctx, AV_LOG_DEBUG, + "general_profile_idc %d, general_tier_flag %d, general_level_idc %d, num_sublayers %d num_bytes_constraint_info %d\n", + general_profile_idc, general_tier_flag, general_level_idc, + num_sublayers, num_bytes_constraint_info); + + temp5 = bytestream2_get_byte(&gb); + ptl_frame_only_constraint_flag = (temp5 >> 7) & 0x1; + ptl_multi_layer_enabled_flag = (temp5 >> 6) & 0x1; + for (i = 0; i < num_bytes_constraint_info - 1; i++) { + // unsigned int(8*num_bytes_constraint_info - 2) general_constraint_info; + bytestream2_get_byte(&gb); + } + + av_log(ctx, AV_LOG_DEBUG, + "ptl_multi_layer_enabled_flag %d, ptl_frame_only_constraint_flag %d\n", + ptl_multi_layer_enabled_flag, ptl_frame_only_constraint_flag); + + if (num_sublayers > 1) { + int temp6 = bytestream2_get_byte(&gb); + uint8_t ptl_sublayer_level_present_flag[8] = { 0 }; + //uint8_t sublayer_level_idc[8] = {0}; + for (i = num_sublayers - 2; i >= 0; i--) { + ptl_sublayer_level_present_flag[i] = + (temp6 >> (7 - (num_sublayers - 2 - i))) & 0x01; + } + // for (j=num_sublayers; j<=8 && num_sublayers > 1; j++) + // bit(1) ptl_reserved_zero_bit = 0; + for (i = num_sublayers - 2; i >= 0; i--) { + if (ptl_sublayer_level_present_flag[i]) { + //sublayer_level_idc[i] = bytestream2_get_byte(&gb); + } + } + } + + ptl_num_sub_profiles = bytestream2_get_byte(&gb); + for (j = 0; j < ptl_num_sub_profiles; j++) { + // unsigned int(32) general_sub_profile_idc[j]; + bytestream2_get_be16(&gb); + bytestream2_get_be16(&gb); + } + + max_picture_width = bytestream2_get_be16(&gb); // unsigned_int(16) max_picture_width; + max_picture_height = bytestream2_get_be16(&gb); // unsigned_int(16) max_picture_height; + avg_frame_rate = bytestream2_get_be16(&gb); // unsigned int(16) avg_frame_rate; } + av_log(ctx, AV_LOG_DEBUG, + "max_picture_width %d, max_picture_height %d, avg_frame_rate %d\n", + max_picture_width, max_picture_height, avg_frame_rate); + } + + num_arrays = bytestream2_get_byte(&gb); + + for (i = 0; i < num_arrays; i++) { + int cnt; + int type = bytestream2_get_byte(&gb) & 0x1f; + + if (type == VVC_OPI_NUT || type == VVC_DCI_NUT) + cnt = 1; + else + cnt = bytestream2_get_be16(&gb); + + av_log(ctx, AV_LOG_DEBUG, "nalu_type %d cnt %d\n", type, cnt); + + if (!(type == VVC_OPI_NUT || type == VVC_DCI_NUT || + type == VVC_VPS_NUT || type == VVC_SPS_NUT || type == VVC_PPS_NUT + || type == VVC_PREFIX_SEI_NUT || type == VVC_SUFFIX_SEI_NUT)) { + av_log(ctx, AV_LOG_ERROR, + "Invalid NAL unit type in extradata: %d\n", type); + ret = AVERROR_INVALIDDATA; + goto fail; + } + + for (j = 0; j < cnt; j++) { + const int nalu_len = bytestream2_get_be16(&gb); + + if (!nalu_len || + nalu_len > bytestream2_get_bytes_left(&gb) || + 4 + AV_INPUT_BUFFER_PADDING_SIZE + nalu_len > SIZE_MAX - new_extradata_size) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + ret = av_reallocp(&new_extradata, new_extradata_size + nalu_len + 4 + + AV_INPUT_BUFFER_PADDING_SIZE); + if (ret < 0) + goto fail; + + AV_WB32(new_extradata + new_extradata_size, 1); // add the startcode + bytestream2_get_buffer(&gb, new_extradata + new_extradata_size + 4, + nalu_len); + new_extradata_size += 4 + nalu_len; + memset(new_extradata + new_extradata_size, 0, + AV_INPUT_BUFFER_PADDING_SIZE); + } + } + + av_freep(&ctx->par_out->extradata); + ctx->par_out->extradata = new_extradata; + ctx->par_out->extradata_size = new_extradata_size; + + if (!new_extradata_size) + av_log(ctx, AV_LOG_WARNING, "No parameter sets in the extradata\n"); + + return length_size; + fail: + av_freep(&new_extradata); + return ret; +} + +static int vvc_mp4toannexb_init(AVBSFContext *ctx) +{ + VVCBSFContext *s = ctx->priv_data; + int ret; + + if (ctx->par_in->extradata_size < MIN_VVCC_LENGTH || + AV_RB24(ctx->par_in->extradata) == 1 || + AV_RB32(ctx->par_in->extradata) == 1) { + av_log(ctx, AV_LOG_VERBOSE, + "The input looks like it is Annex B already\n"); + } else { + ret = vvc_extradata_to_annexb(ctx); + if (ret < 0) + return ret; + s->length_size = ret; + s->extradata_parsed = 1; + } + + return 0; +} + +static int vvc_mp4toannexb_filter(AVBSFContext *ctx, AVPacket *out) +{ + VVCBSFContext *s = ctx->priv_data; + AVPacket *in; + GetByteContext gb; + + int is_irap = 0; + int added_extra = 0; + int i, ret = 0; + + ret = ff_bsf_get_packet(ctx, &in); + if (ret < 0) + return ret; + + if (!s->extradata_parsed) { + av_packet_move_ref(out, in); + av_packet_free(&in); + return 0; + } + + bytestream2_init(&gb, in->data, in->size); + + /* check if this packet contains an IRAP. The extradata will need to be added before any potential PH_NUT */ + while (bytestream2_get_bytes_left(&gb)) { + uint32_t nalu_size = 0; + int nalu_type; + + if (bytestream2_get_bytes_left(&gb) < s->length_size) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + for (i = 0; i < s->length_size; i++) + nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb); + + if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + nalu_type = (bytestream2_peek_be16(&gb) >> 3) & 0x1f; + is_irap = nalu_type >= VVC_IDR_W_RADL && nalu_type <= VVC_RSV_IRAP_11; + if (is_irap) { + break; + } + bytestream2_seek(&gb, nalu_size, SEEK_CUR); + } + + bytestream2_seek(&gb, 0, SEEK_SET); + while (bytestream2_get_bytes_left(&gb)) { + uint32_t nalu_size = 0; + int nalu_type; + int add_extradata, extra_size, prev_size; + + if (bytestream2_get_bytes_left(&gb) < s->length_size) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + for (i = 0; i < s->length_size; i++) + nalu_size = (nalu_size << 8) | bytestream2_get_byte(&gb); + + if (nalu_size < 2 || nalu_size > bytestream2_get_bytes_left(&gb)) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + nalu_type = (bytestream2_peek_be16(&gb) >> 3) & 0x1f; + + /* prepend extradata to IRAP frames */ + add_extradata = is_irap && nalu_type != VVC_AUD_NUT && !added_extra; + extra_size = add_extradata * ctx->par_out->extradata_size; + added_extra |= add_extradata; + + if (FFMIN(INT_MAX, SIZE_MAX) < 4ULL + nalu_size + extra_size) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + prev_size = out->size; + + ret = av_grow_packet(out, 4 + nalu_size + extra_size); + if (ret < 0) + goto fail; + + if (extra_size) + memcpy(out->data + prev_size, ctx->par_out->extradata, extra_size); + AV_WB32(out->data + prev_size + extra_size, 1); + bytestream2_get_buffer(&gb, out->data + prev_size + 4 + extra_size, + nalu_size); + } + + ret = av_packet_copy_props(out, in); + if (ret < 0) + goto fail; + + fail: + if (ret < 0) + av_packet_unref(out); + av_packet_free(&in); + + return ret; +} + +static const enum AVCodecID codec_ids[] = { + AV_CODEC_ID_VVC, AV_CODEC_ID_NONE, +}; + +const FFBitStreamFilter ff_vvc_mp4toannexb_bsf = { + .p.name = "vvc_mp4toannexb", + .p.codec_ids = codec_ids, + .priv_data_size = sizeof(VVCBSFContext), + .init = vvc_mp4toannexb_init, + .filter = vvc_mp4toannexb_filter, +}; diff --git a/libavcodec/bytestream.h b/libavcodec/bytestream.h index d0033f14f36..67080604b92 100644 --- a/libavcodec/bytestream.h +++ b/libavcodec/bytestream.h @@ -180,7 +180,7 @@ static av_always_inline void bytestream2_skipu(GetByteContext *g, static av_always_inline void bytestream2_skip_p(PutByteContext *p, unsigned int size) { - int size2; + unsigned int size2; if (p->eof) return; size2 = FFMIN(p->buffer_end - p->buffer, size); @@ -268,7 +268,7 @@ static av_always_inline unsigned int bytestream2_get_buffer(GetByteContext *g, uint8_t *dst, unsigned int size) { - int size2 = FFMIN(g->buffer_end - g->buffer, size); + unsigned int size2 = FFMIN(g->buffer_end - g->buffer, size); memcpy(dst, g->buffer, size2); g->buffer += size2; return size2; @@ -287,7 +287,7 @@ static av_always_inline unsigned int bytestream2_put_buffer(PutByteContext *p, const uint8_t *src, unsigned int size) { - int size2; + unsigned int size2; if (p->eof) return 0; size2 = FFMIN(p->buffer_end - p->buffer, size); @@ -311,7 +311,7 @@ static av_always_inline void bytestream2_set_buffer(PutByteContext *p, const uint8_t c, unsigned int size) { - int size2; + unsigned int size2; if (p->eof) return; size2 = FFMIN(p->buffer_end - p->buffer, size); @@ -348,7 +348,7 @@ static av_always_inline unsigned int bytestream2_copy_buffer(PutByteContext *p, GetByteContext *g, unsigned int size) { - int size2; + unsigned int size2; if (p->eof) return 0; diff --git a/libavcodec/c93.c b/libavcodec/c93.c index bfcbc7c1504..2a4fe459580 100644 --- a/libavcodec/c93.c +++ b/libavcodec/c93.c @@ -147,10 +147,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, b = bytestream2_get_byte(&gb); if (b & C93_FIRST_FRAME) { newpic->pict_type = AV_PICTURE_TYPE_I; - newpic->key_frame = 1; + newpic->flags |= AV_FRAME_FLAG_KEY; } else { newpic->pict_type = AV_PICTURE_TYPE_P; - newpic->key_frame = 0; + newpic->flags &= ~AV_FRAME_FLAG_KEY; } for (y = 0; y < HEIGHT; y += 8) { @@ -246,7 +246,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, for (i = 0; i < 256; i++) { palette[i] = 0xFFU << 24 | bytestream2_get_be24(&gb); } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS newpic->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else { if (oldpic->data[1]) memcpy(newpic->data[1], oldpic->data[1], 256 * 4); diff --git a/libavcodec/cavs_parser.c b/libavcodec/cavs_parser.c index 03f392c2e5a..4a03effd0f7 100644 --- a/libavcodec/cavs_parser.c +++ b/libavcodec/cavs_parser.c @@ -59,12 +59,11 @@ static int cavs_find_frame_end(ParseContext *pc, const uint8_t *buf, return 0; for(; i SLICE_MAX_START_CODE){ - pc->frame_start_found=0; - pc->state=-1; - return i-3; - } + if (state == PIC_I_START_CODE || state == PIC_PB_START_CODE || + state == CAVS_START_CODE) { + pc->frame_start_found=0; + pc->state=-1; + return i-3; } } } diff --git a/libavcodec/cavsdec.c b/libavcodec/cavsdec.c index b1fa9a981d4..b356da0b045 100644 --- a/libavcodec/cavsdec.c +++ b/libavcodec/cavsdec.c @@ -26,6 +26,7 @@ */ #include "libavutil/avassert.h" +#include "libavutil/emms.h" #include "avcodec.h" #include "get_bits.h" #include "golomb.h" @@ -1020,6 +1021,9 @@ static int decode_pic(AVSContext *h) skip_bits(&h->gb, 1); //marker_bit } + if (get_bits_left(&h->gb) < 23) + return AVERROR_INVALIDDATA; + ret = ff_get_buffer(h->avctx, h->cur.f, h->cur.f->pict_type == AV_PICTURE_TYPE_B ? 0 : AV_GET_BUFFER_FLAG_REF); if (ret < 0) diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c index 504197e06d4..de7b1361aae 100644 --- a/libavcodec/cbs.c +++ b/libavcodec/cbs.c @@ -28,6 +28,7 @@ #include "avcodec.h" #include "cbs.h" #include "cbs_internal.h" +#include "refstruct.h" static const CodedBitstreamType *const cbs_type_table[] = { @@ -40,12 +41,18 @@ static const CodedBitstreamType *const cbs_type_table[] = { #if CONFIG_CBS_H265 &ff_cbs_type_h265, #endif +#if CONFIG_CBS_H266 + &ff_cbs_type_h266, +#endif #if CONFIG_CBS_JPEG &ff_cbs_type_jpeg, #endif #if CONFIG_CBS_MPEG2 &ff_cbs_type_mpeg2, #endif +#if CONFIG_CBS_VP8 + &ff_cbs_type_vp8, +#endif #if CONFIG_CBS_VP9 &ff_cbs_type_vp9, #endif @@ -61,12 +68,18 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = { #if CONFIG_CBS_H265 AV_CODEC_ID_H265, #endif +#if CONFIG_CBS_H266 + AV_CODEC_ID_H266, +#endif #if CONFIG_CBS_JPEG AV_CODEC_ID_MJPEG, #endif #if CONFIG_CBS_MPEG2 AV_CODEC_ID_MPEG2VIDEO, #endif +#if CONFIG_CBS_VP8 + AV_CODEC_ID_VP8, +#endif #if CONFIG_CBS_VP9 AV_CODEC_ID_VP9, #endif @@ -111,8 +124,9 @@ av_cold int ff_cbs_init(CodedBitstreamContext **ctx_ptr, ctx->decompose_unit_types = NULL; - ctx->trace_enable = 0; - ctx->trace_level = AV_LOG_TRACE; + ctx->trace_enable = 0; + ctx->trace_level = AV_LOG_TRACE; + ctx->trace_context = ctx; *ctx_ptr = ctx; return 0; @@ -145,7 +159,7 @@ av_cold void ff_cbs_close(CodedBitstreamContext **ctx_ptr) static void cbs_unit_uninit(CodedBitstreamUnit *unit) { - av_buffer_unref(&unit->content_ref); + ff_refstruct_unref(&unit->content_ref); unit->content = NULL; av_buffer_unref(&unit->data_ref); @@ -193,7 +207,7 @@ static int cbs_read_fragment_content(CodedBitstreamContext *ctx, continue; } - av_buffer_unref(&unit->content_ref); + ff_refstruct_unref(&unit->content_ref); unit->content = NULL; av_assert0(unit->data && unit->data_ref); @@ -207,7 +221,7 @@ static int cbs_read_fragment_content(CodedBitstreamContext *ctx, av_log(ctx->log_ctx, AV_LOG_VERBOSE, "Skipping decomposition of unit %d " "(type %"PRIu32").\n", i, unit->type); - av_buffer_unref(&unit->content_ref); + ff_refstruct_unref(&unit->content_ref); unit->content = NULL; } else if (err < 0) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to read unit %d " @@ -490,19 +504,27 @@ void ff_cbs_trace_header(CodedBitstreamContext *ctx, av_log(ctx->log_ctx, ctx->trace_level, "%s\n", name); } -void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position, - const char *str, const int *subscripts, - const char *bits, int64_t value) +void ff_cbs_trace_read_log(void *trace_context, + GetBitContext *gbc, int length, + const char *str, const int *subscripts, + int64_t value) { + CodedBitstreamContext *ctx = trace_context; char name[256]; + char bits[256]; size_t name_len, bits_len; int pad, subs, i, j, k, n; - - if (!ctx->trace_enable) - return; + int position; av_assert0(value >= INT_MIN && value <= UINT32_MAX); + position = get_bits_count(gbc); + + av_assert0(length < 256); + for (i = 0; i < length; i++) + bits[i] = get_bits1(gbc) ? '1' : '0'; + bits[length] = 0; + subs = subscripts ? subscripts[0] : 0; n = 0; for (i = j = 0; str[i];) { @@ -529,7 +551,7 @@ void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position, av_assert0(n == subs); name_len = strlen(name); - bits_len = strlen(bits); + bits_len = length; if (name_len + bits_len > 60) pad = bits_len + 2; @@ -540,14 +562,48 @@ void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position, position, name, pad, bits, value); } -int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, - int width, const char *name, - const int *subscripts, uint32_t *write_to, - uint32_t range_min, uint32_t range_max) +void ff_cbs_trace_write_log(void *trace_context, + PutBitContext *pbc, int length, + const char *str, const int *subscripts, + int64_t value) { - uint32_t value; + CodedBitstreamContext *ctx = trace_context; + + // Ensure that the syntax element is written to the output buffer, + // make a GetBitContext pointed at the start position, then call the + // read log function which can read the bits back to log them. + + GetBitContext gbc; int position; + if (length > 0) { + PutBitContext flush; + flush = *pbc; + flush_put_bits(&flush); + } + + position = put_bits_count(pbc); + av_assert0(position >= length); + + init_get_bits(&gbc, pbc->buf, position); + + skip_bits_long(&gbc, position - length); + + ff_cbs_trace_read_log(ctx, &gbc, length, str, subscripts, value); +} + +static av_always_inline int cbs_read_unsigned(CodedBitstreamContext *ctx, + GetBitContext *gbc, + int width, const char *name, + const int *subscripts, + uint32_t *write_to, + uint32_t range_min, + uint32_t range_max) +{ + uint32_t value; + + CBS_TRACE_READ_START(); + av_assert0(width > 0 && width <= 32); if (get_bits_left(gbc) < width) { @@ -556,21 +612,9 @@ int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, return AVERROR_INVALIDDATA; } - if (ctx->trace_enable) - position = get_bits_count(gbc); - value = get_bits_long(gbc, width); - if (ctx->trace_enable) { - char bits[33]; - int i; - for (i = 0; i < width; i++) - bits[i] = value >> (width - i - 1) & 1 ? '1' : '0'; - bits[i] = 0; - - ff_cbs_trace_syntax_element(ctx, position, name, subscripts, - bits, value); - } + CBS_TRACE_READ_END(); if (value < range_min || value > range_max) { av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " @@ -583,11 +627,29 @@ int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, return 0; } +int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, + int width, const char *name, + const int *subscripts, uint32_t *write_to, + uint32_t range_min, uint32_t range_max) +{ + return cbs_read_unsigned(ctx, gbc, width, name, subscripts, + write_to, range_min, range_max); +} + +int ff_cbs_read_simple_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, + int width, const char *name, uint32_t *write_to) +{ + return cbs_read_unsigned(ctx, gbc, width, name, NULL, + write_to, 0, UINT32_MAX); +} + int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, int width, const char *name, const int *subscripts, uint32_t value, uint32_t range_min, uint32_t range_max) { + CBS_TRACE_WRITE_START(); + av_assert0(width > 0 && width <= 32); if (value < range_min || value > range_max) { @@ -600,32 +662,31 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, if (put_bits_left(pbc) < width) return AVERROR(ENOSPC); - if (ctx->trace_enable) { - char bits[33]; - int i; - for (i = 0; i < width; i++) - bits[i] = value >> (width - i - 1) & 1 ? '1' : '0'; - bits[i] = 0; - - ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), - name, subscripts, bits, value); - } - if (width < 32) put_bits(pbc, width, value); else put_bits32(pbc, value); + CBS_TRACE_WRITE_END(); + return 0; } +int ff_cbs_write_simple_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, + int width, const char *name, uint32_t value) +{ + return ff_cbs_write_unsigned(ctx, pbc, width, name, NULL, + value, 0, MAX_UINT_BITS(width)); +} + int ff_cbs_read_signed(CodedBitstreamContext *ctx, GetBitContext *gbc, int width, const char *name, const int *subscripts, int32_t *write_to, int32_t range_min, int32_t range_max) { int32_t value; - int position; + + CBS_TRACE_READ_START(); av_assert0(width > 0 && width <= 32); @@ -635,21 +696,9 @@ int ff_cbs_read_signed(CodedBitstreamContext *ctx, GetBitContext *gbc, return AVERROR_INVALIDDATA; } - if (ctx->trace_enable) - position = get_bits_count(gbc); - value = get_sbits_long(gbc, width); - if (ctx->trace_enable) { - char bits[33]; - int i; - for (i = 0; i < width; i++) - bits[i] = value & (1U << (width - i - 1)) ? '1' : '0'; - bits[i] = 0; - - ff_cbs_trace_syntax_element(ctx, position, name, subscripts, - bits, value); - } + CBS_TRACE_READ_END(); if (value < range_min || value > range_max) { av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " @@ -667,6 +716,8 @@ int ff_cbs_write_signed(CodedBitstreamContext *ctx, PutBitContext *pbc, const int *subscripts, int32_t value, int32_t range_min, int32_t range_max) { + CBS_TRACE_WRITE_START(); + av_assert0(width > 0 && width <= 32); if (value < range_min || value > range_max) { @@ -679,22 +730,13 @@ int ff_cbs_write_signed(CodedBitstreamContext *ctx, PutBitContext *pbc, if (put_bits_left(pbc) < width) return AVERROR(ENOSPC); - if (ctx->trace_enable) { - char bits[33]; - int i; - for (i = 0; i < width; i++) - bits[i] = value & (1U << (width - i - 1)) ? '1' : '0'; - bits[i] = 0; - - ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), - name, subscripts, bits, value); - } - if (width < 32) put_sbits(pbc, width, value); else put_bits32(pbc, value); + CBS_TRACE_WRITE_END(); + return 0; } @@ -741,28 +783,22 @@ int ff_cbs_insert_unit_content(CodedBitstreamFragment *frag, int position, CodedBitstreamUnitType type, void *content, - AVBufferRef *content_buf) + void *content_ref) { CodedBitstreamUnit *unit; - AVBufferRef *content_ref; int err; if (position == -1) position = frag->nb_units; av_assert0(position >= 0 && position <= frag->nb_units); - if (content_buf) { - content_ref = av_buffer_ref(content_buf); - if (!content_ref) - return AVERROR(ENOMEM); - } else { - content_ref = NULL; - } - err = cbs_insert_unit(frag, position); - if (err < 0) { - av_buffer_unref(&content_ref); + if (err < 0) return err; + + if (content_ref) { + // Create our own reference out of the user-supplied one. + content_ref = ff_refstruct_ref(content_ref); } unit = &frag->units[position]; @@ -836,15 +872,14 @@ void ff_cbs_delete_unit(CodedBitstreamFragment *frag, (frag->nb_units - position) * sizeof(*frag->units)); } -static void cbs_default_free_unit_content(void *opaque, uint8_t *data) +static void cbs_default_free_unit_content(FFRefStructOpaque opaque, void *content) { - const CodedBitstreamUnitTypeDescriptor *desc = opaque; + const CodedBitstreamUnitTypeDescriptor *desc = opaque.c; for (int i = 0; i < desc->type.ref.nb_offsets; i++) { - void **ptr = (void**)(data + desc->type.ref.offsets[i]); + void **ptr = (void**)((char*)content + desc->type.ref.offsets[i]); av_buffer_unref((AVBufferRef**)(ptr + 1)); } - av_free(data); } static const CodedBitstreamUnitTypeDescriptor @@ -875,6 +910,15 @@ static const CodedBitstreamUnitTypeDescriptor return NULL; } +static void *cbs_alloc_content(const CodedBitstreamUnitTypeDescriptor *desc) +{ + return ff_refstruct_alloc_ext_c(desc->content_size, 0, + (FFRefStructOpaque){ .c = desc }, + desc->content_type == CBS_CONTENT_TYPE_COMPLEX + ? desc->type.complex.content_free + : cbs_default_free_unit_content); +} + int ff_cbs_alloc_unit_content(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit) { @@ -886,27 +930,17 @@ int ff_cbs_alloc_unit_content(CodedBitstreamContext *ctx, if (!desc) return AVERROR(ENOSYS); - unit->content = av_mallocz(desc->content_size); - if (!unit->content) + unit->content_ref = cbs_alloc_content(desc); + if (!unit->content_ref) return AVERROR(ENOMEM); - - unit->content_ref = - av_buffer_create(unit->content, desc->content_size, - desc->content_type == CBS_CONTENT_TYPE_COMPLEX - ? desc->type.complex.content_free - : cbs_default_free_unit_content, - (void*)desc, 0); - if (!unit->content_ref) { - av_freep(&unit->content); - return AVERROR(ENOMEM); - } + unit->content = unit->content_ref; return 0; } -static int cbs_clone_internal_refs_unit_content(AVBufferRef **clone_ref, - const CodedBitstreamUnit *unit, - const CodedBitstreamUnitTypeDescriptor *desc) +static int cbs_clone_noncomplex_unit_content(void **clonep, + const CodedBitstreamUnit *unit, + const CodedBitstreamUnitTypeDescriptor *desc) { const uint8_t *src; uint8_t *copy; @@ -915,9 +949,15 @@ static int cbs_clone_internal_refs_unit_content(AVBufferRef **clone_ref, av_assert0(unit->content); src = unit->content; - copy = av_memdup(src, desc->content_size); + copy = cbs_alloc_content(desc); if (!copy) return AVERROR(ENOMEM); + memcpy(copy, src, desc->content_size); + for (int i = 0; i < desc->type.ref.nb_offsets; i++) { + void **ptr = (void**)(copy + desc->type.ref.offsets[i]); + /* Zero all the AVBufferRefs as they are owned by src. */ + *(ptr + 1) = NULL; + } for (i = 0; i < desc->type.ref.nb_offsets; i++) { const uint8_t *const *src_ptr = (const uint8_t* const*)(src + desc->type.ref.offsets[i]); @@ -943,22 +983,12 @@ static int cbs_clone_internal_refs_unit_content(AVBufferRef **clone_ref, goto fail; } } - - *clone_ref = av_buffer_create(copy, desc->content_size, - cbs_default_free_unit_content, - (void*)desc, 0); - if (!*clone_ref) { - err = AVERROR(ENOMEM); - goto fail; - } + *clonep = copy; return 0; fail: - for (--i; i >= 0; i--) - av_buffer_unref((AVBufferRef**)(copy + desc->type.ref.offsets[i])); - av_freep(©); - *clone_ref = NULL; + ff_refstruct_unref(©); return err; } @@ -971,7 +1001,7 @@ static int cbs_clone_unit_content(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit) { const CodedBitstreamUnitTypeDescriptor *desc; - AVBufferRef *ref; + void *new_content; int err; desc = cbs_find_unit_type_desc(ctx, unit); @@ -980,13 +1010,13 @@ static int cbs_clone_unit_content(CodedBitstreamContext *ctx, switch (desc->content_type) { case CBS_CONTENT_TYPE_INTERNAL_REFS: - err = cbs_clone_internal_refs_unit_content(&ref, unit, desc); + err = cbs_clone_noncomplex_unit_content(&new_content, unit, desc); break; case CBS_CONTENT_TYPE_COMPLEX: if (!desc->type.complex.content_clone) return AVERROR_PATCHWELCOME; - err = desc->type.complex.content_clone(&ref, unit); + err = desc->type.complex.content_clone(&new_content, unit); break; default: @@ -996,8 +1026,8 @@ static int cbs_clone_unit_content(CodedBitstreamContext *ctx, if (err < 0) return err; - unit->content_ref = ref; - unit->content = ref->data; + unit->content_ref = new_content; + unit->content = new_content; return 0; } @@ -1013,16 +1043,37 @@ int ff_cbs_make_unit_refcounted(CodedBitstreamContext *ctx, int ff_cbs_make_unit_writable(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit) { - AVBufferRef *ref = unit->content_ref; + void *ref = unit->content_ref; int err; av_assert0(unit->content); - if (ref && av_buffer_is_writable(ref)) + if (ref && ff_refstruct_exclusive(ref)) return 0; err = cbs_clone_unit_content(ctx, unit); if (err < 0) return err; - av_buffer_unref(&ref); + ff_refstruct_unref(&ref); return 0; } + +void ff_cbs_discard_units(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + enum AVDiscard skip, + int flags) +{ + if (!ctx->codec->discarded_unit) + return; + + for (int i = frag->nb_units - 1; i >= 0; i--) { + if (ctx->codec->discarded_unit(ctx, &frag->units[i], skip)) { + // discard all units + if (!(flags & DISCARD_FLAG_KEEP_NON_VCL)) { + ff_cbs_fragment_free(frag); + return; + } + + ff_cbs_delete_unit(frag, i); + } + } +} diff --git a/libavcodec/cbs.h b/libavcodec/cbs.h index ee21623dacb..d479b1ac2da 100644 --- a/libavcodec/cbs.h +++ b/libavcodec/cbs.h @@ -26,6 +26,7 @@ #include "codec_id.h" #include "codec_par.h" +#include "defs.h" #include "packet.h" @@ -105,10 +106,10 @@ typedef struct CodedBitstreamUnit { */ void *content; /** - * If content is reference counted, a reference to the buffer containing - * content. Null if content is not reference counted. + * If content is reference counted, a RefStruct reference backing content. + * NULL if content is not reference counted. */ - AVBufferRef *content_ref; + void *content_ref; } CodedBitstreamUnit; /** @@ -167,6 +168,51 @@ typedef struct CodedBitstreamFragment { CodedBitstreamUnit *units; } CodedBitstreamFragment; + +struct CodedBitstreamContext; +struct GetBitContext; +struct PutBitContext; + +/** + * Callback type for read tracing. + * + * @param ctx User-set trace context. + * @param gbc A GetBitContext set at the start of the syntax + * element. This is a copy, the callee does not + * need to preserve it. + * @param length Length in bits of the syntax element. + * @param name String name of the syntax elements. + * @param subscripts If the syntax element is an array, a pointer to + * an array of subscripts into the array. + * @param value Parsed value of the syntax element. + */ +typedef void (*CBSTraceReadCallback)(void *trace_context, + struct GetBitContext *gbc, + int start_position, + const char *name, + const int *subscripts, + int64_t value); + +/** + * Callback type for write tracing. + * + * @param ctx User-set trace context. + * @param pbc A PutBitContext set at the end of the syntax + * element. The user must not modify this, but may + * inspect it to determine state. + * @param length Length in bits of the syntax element. + * @param name String name of the syntax elements. + * @param subscripts If the syntax element is an array, a pointer to + * an array of subscripts into the array. + * @param value Written value of the syntax element. + */ +typedef void (*CBSTraceWriteCallback)(void *trace_context, + struct PutBitContext *pbc, + int start_position, + const char *name, + const int *subscripts, + int64_t value); + /** * Context structure for coded bitstream operations. */ @@ -210,11 +256,29 @@ typedef struct CodedBitstreamContext { */ int trace_enable; /** - * Log level to use for trace output. + * Log level to use for default trace output. * * From AV_LOG_*; defaults to AV_LOG_TRACE. */ int trace_level; + /** + * User context pointer to pass to trace callbacks. + */ + void *trace_context; + /** + * Callback for read tracing. + * + * If tracing is enabled then this is called once for each syntax + * element parsed. + */ + CBSTraceReadCallback trace_read_callback; + /** + * Callback for write tracing. + * + * If tracing is enabled then this is called once for each syntax + * element written. + */ + CBSTraceWriteCallback trace_write_callback; /** * Write buffer. Used as intermediate buffer when writing units. @@ -374,14 +438,16 @@ int ff_cbs_alloc_unit_content(CodedBitstreamContext *ctx, /** * Insert a new unit into a fragment with the given content. * + * If content_ref is supplied, it has to be a RefStruct reference + * backing content; the user keeps ownership of the supplied reference. * The content structure continues to be owned by the caller if - * content_buf is not supplied. + * content_ref is not supplied. */ int ff_cbs_insert_unit_content(CodedBitstreamFragment *frag, int position, CodedBitstreamUnitType type, void *content, - AVBufferRef *content_buf); + void *content_ref); /** * Add a new unit to a fragment with the given data bitstream. @@ -432,5 +498,44 @@ int ff_cbs_make_unit_refcounted(CodedBitstreamContext *ctx, int ff_cbs_make_unit_writable(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit); +enum CbsDiscardFlags { + DISCARD_FLAG_NONE = 0, + + /** + * keep non-vcl units even if the picture has been dropped. + */ + DISCARD_FLAG_KEEP_NON_VCL = 0x01, +}; + +/** + * Discard units accroding to 'skip'. + */ +void ff_cbs_discard_units(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + enum AVDiscard skip, + int flags); + + +/** + * Helper function for read tracing which formats the syntax element + * and logs the result. + * + * Trace context should be set to the CodedBitstreamContext. + */ +void ff_cbs_trace_read_log(void *trace_context, + struct GetBitContext *gbc, int length, + const char *str, const int *subscripts, + int64_t value); + +/** + * Helper function for write tracing which formats the syntax element + * and logs the result. + * + * Trace context should be set to the CodedBitstreamContext. + */ +void ff_cbs_trace_write_log(void *trace_context, + struct PutBitContext *pbc, int length, + const char *str, const int *subscripts, + int64_t value); #endif /* AVCODEC_CBS_H */ diff --git a/libavcodec/cbs_av1.c b/libavcodec/cbs_av1.c index 45e1288a519..458381f0385 100644 --- a/libavcodec/cbs_av1.c +++ b/libavcodec/cbs_av1.c @@ -20,10 +20,11 @@ #include "libavutil/opt.h" #include "libavutil/pixfmt.h" -#include "avcodec.h" #include "cbs.h" #include "cbs_internal.h" #include "cbs_av1.h" +#include "defs.h" +#include "refstruct.h" static int cbs_av1_read_uvlc(CodedBitstreamContext *ctx, GetBitContext *gbc, @@ -31,13 +32,11 @@ static int cbs_av1_read_uvlc(CodedBitstreamContext *ctx, GetBitContext *gbc, uint32_t range_min, uint32_t range_max) { uint32_t zeroes, bits_value, value; - int position; - if (ctx->trace_enable) - position = get_bits_count(gbc); + CBS_TRACE_READ_START(); zeroes = 0; - while (1) { + while (zeroes < 32) { if (get_bits_left(gbc) < 1) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid uvlc code at " "%s: bitstream ended.\n", name); @@ -50,7 +49,18 @@ static int cbs_av1_read_uvlc(CodedBitstreamContext *ctx, GetBitContext *gbc, } if (zeroes >= 32) { - value = MAX_UINT_BITS(32); + // The spec allows at least thirty-two zero bits followed by a + // one to mean 2^32-1, with no constraint on the number of + // zeroes. The libaom reference decoder does not match this, + // instead reading thirty-two zeroes but not the following one + // to mean 2^32-1. These two interpretations are incompatible + // and other implementations may follow one or the other. + // Therefore we reject thirty-two zeroes because the intended + // behaviour is not clear. + av_log(ctx->log_ctx, AV_LOG_ERROR, "Thirty-two zero bits in " + "%s uvlc code: considered invalid due to conflicting " + "standard and reference decoder behaviour.\n", name); + return AVERROR_INVALIDDATA; } else { if (get_bits_left(gbc) < zeroes) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid uvlc code at " @@ -62,36 +72,7 @@ static int cbs_av1_read_uvlc(CodedBitstreamContext *ctx, GetBitContext *gbc, value = bits_value + (UINT32_C(1) << zeroes) - 1; } - if (ctx->trace_enable) { - char bits[65]; - int i, j, k; - - if (zeroes >= 32) { - while (zeroes > 32) { - k = FFMIN(zeroes - 32, 32); - for (i = 0; i < k; i++) - bits[i] = '0'; - bits[i] = 0; - ff_cbs_trace_syntax_element(ctx, position, name, - NULL, bits, 0); - zeroes -= k; - position += k; - } - } - - for (i = 0; i < zeroes; i++) - bits[i] = '0'; - bits[i++] = '1'; - - if (zeroes < 32) { - for (j = 0; j < zeroes; j++) - bits[i++] = (bits_value >> (zeroes - j - 1) & 1) ? '1' : '0'; - } - - bits[i] = 0; - ff_cbs_trace_syntax_element(ctx, position, name, - NULL, bits, value); - } + CBS_TRACE_READ_END_NO_SUBSCRIPTS(); if (value < range_min || value > range_max) { av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " @@ -109,7 +90,9 @@ static int cbs_av1_write_uvlc(CodedBitstreamContext *ctx, PutBitContext *pbc, uint32_t range_min, uint32_t range_max) { uint32_t v; - int position, zeroes; + int zeroes; + + CBS_TRACE_WRITE_START(); if (value < range_min || value > range_max) { av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " @@ -118,28 +101,17 @@ static int cbs_av1_write_uvlc(CodedBitstreamContext *ctx, PutBitContext *pbc, return AVERROR_INVALIDDATA; } - if (ctx->trace_enable) - position = put_bits_count(pbc); - zeroes = av_log2(value + 1); v = value - (1U << zeroes) + 1; + + if (put_bits_left(pbc) < 2 * zeroes + 1) + return AVERROR(ENOSPC); + put_bits(pbc, zeroes, 0); put_bits(pbc, 1, 1); put_bits(pbc, zeroes, v); - if (ctx->trace_enable) { - char bits[65]; - int i, j; - i = 0; - for (j = 0; j < zeroes; j++) - bits[i++] = '0'; - bits[i++] = '1'; - for (j = 0; j < zeroes; j++) - bits[i++] = (v >> (zeroes - j - 1) & 1) ? '1' : '0'; - bits[i++] = 0; - ff_cbs_trace_syntax_element(ctx, position, name, NULL, - bits, value); - } + CBS_TRACE_WRITE_END_NO_SUBSCRIPTS(); return 0; } @@ -148,20 +120,19 @@ static int cbs_av1_read_leb128(CodedBitstreamContext *ctx, GetBitContext *gbc, const char *name, uint64_t *write_to) { uint64_t value; - int position, err, i; + uint32_t byte; + int i; - if (ctx->trace_enable) - position = get_bits_count(gbc); + CBS_TRACE_READ_START(); value = 0; for (i = 0; i < 8; i++) { - int subscript[2] = { 1, i }; - uint32_t byte; - err = ff_cbs_read_unsigned(ctx, gbc, 8, "leb128_byte[i]", subscript, - &byte, 0x00, 0xff); - if (err < 0) - return err; - + if (get_bits_left(gbc) < 8) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid leb128 at " + "%s: bitstream ended.\n", name); + return AVERROR_INVALIDDATA; + } + byte = get_bits(gbc, 8); value |= (uint64_t)(byte & 0x7f) << (i * 7); if (!(byte & 0x80)) break; @@ -170,39 +141,44 @@ static int cbs_av1_read_leb128(CodedBitstreamContext *ctx, GetBitContext *gbc, if (value > UINT32_MAX) return AVERROR_INVALIDDATA; - if (ctx->trace_enable) - ff_cbs_trace_syntax_element(ctx, position, name, NULL, "", value); + CBS_TRACE_READ_END_NO_SUBSCRIPTS(); *write_to = value; return 0; } static int cbs_av1_write_leb128(CodedBitstreamContext *ctx, PutBitContext *pbc, - const char *name, uint64_t value) + const char *name, uint64_t value, int fixed_length) { - int position, err, len, i; + int len, i; uint8_t byte; + CBS_TRACE_WRITE_START(); + len = (av_log2(value) + 7) / 7; - if (ctx->trace_enable) - position = put_bits_count(pbc); + if (fixed_length) { + if (fixed_length < len) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "OBU is too large for " + "fixed length size field (%d > %d).\n", + len, fixed_length); + return AVERROR(EINVAL); + } + len = fixed_length; + } for (i = 0; i < len; i++) { - int subscript[2] = { 1, i }; + if (put_bits_left(pbc) < 8) + return AVERROR(ENOSPC); byte = value >> (7 * i) & 0x7f; if (i < len - 1) byte |= 0x80; - err = ff_cbs_write_unsigned(ctx, pbc, 8, "leb128_byte[i]", subscript, - byte, 0x00, 0xff); - if (err < 0) - return err; + put_bits(pbc, 8, byte); } - if (ctx->trace_enable) - ff_cbs_trace_syntax_element(ctx, position, name, NULL, "", value); + CBS_TRACE_WRITE_END_NO_SUBSCRIPTS(); return 0; } @@ -212,12 +188,11 @@ static int cbs_av1_read_ns(CodedBitstreamContext *ctx, GetBitContext *gbc, const int *subscripts, uint32_t *write_to) { uint32_t m, v, extra_bit, value; - int position, w; + int w; - av_assert0(n > 0); + CBS_TRACE_READ_START(); - if (ctx->trace_enable) - position = get_bits_count(gbc); + av_assert0(n > 0); w = av_log2(n) + 1; m = (1 << w) - n; @@ -240,18 +215,7 @@ static int cbs_av1_read_ns(CodedBitstreamContext *ctx, GetBitContext *gbc, value = (v << 1) - m + extra_bit; } - if (ctx->trace_enable) { - char bits[33]; - int i; - for (i = 0; i < w - 1; i++) - bits[i] = (v >> i & 1) ? '1' : '0'; - if (v >= m) - bits[i++] = extra_bit ? '1' : '0'; - bits[i] = 0; - - ff_cbs_trace_syntax_element(ctx, position, - name, subscripts, bits, value); - } + CBS_TRACE_READ_END(); *write_to = value; return 0; @@ -262,7 +226,8 @@ static int cbs_av1_write_ns(CodedBitstreamContext *ctx, PutBitContext *pbc, const int *subscripts, uint32_t value) { uint32_t w, m, v, extra_bit; - int position; + + CBS_TRACE_WRITE_START(); if (value > n) { av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " @@ -271,9 +236,6 @@ static int cbs_av1_write_ns(CodedBitstreamContext *ctx, PutBitContext *pbc, return AVERROR_INVALIDDATA; } - if (ctx->trace_enable) - position = put_bits_count(pbc); - w = av_log2(n) + 1; m = (1 << w) - n; @@ -290,18 +252,7 @@ static int cbs_av1_write_ns(CodedBitstreamContext *ctx, PutBitContext *pbc, put_bits(pbc, 1, extra_bit); } - if (ctx->trace_enable) { - char bits[33]; - int i; - for (i = 0; i < w - 1; i++) - bits[i] = (v >> i & 1) ? '1' : '0'; - if (value >= m) - bits[i++] = extra_bit ? '1' : '0'; - bits[i] = 0; - - ff_cbs_trace_syntax_element(ctx, position, - name, subscripts, bits, value); - } + CBS_TRACE_WRITE_END(); return 0; } @@ -311,33 +262,24 @@ static int cbs_av1_read_increment(CodedBitstreamContext *ctx, GetBitContext *gbc const char *name, uint32_t *write_to) { uint32_t value; - int position, i; - char bits[33]; - av_assert0(range_min <= range_max && range_max - range_min < sizeof(bits) - 1); - if (ctx->trace_enable) - position = get_bits_count(gbc); + CBS_TRACE_READ_START(); - for (i = 0, value = range_min; value < range_max;) { + av_assert0(range_min <= range_max && range_max - range_min < 32); + + for (value = range_min; value < range_max;) { if (get_bits_left(gbc) < 1) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid increment value at " "%s: bitstream ended.\n", name); return AVERROR_INVALIDDATA; } - if (get_bits1(gbc)) { - bits[i++] = '1'; + if (get_bits1(gbc)) ++value; - } else { - bits[i++] = '0'; + else break; - } } - if (ctx->trace_enable) { - bits[i] = 0; - ff_cbs_trace_syntax_element(ctx, position, - name, NULL, bits, value); - } + CBS_TRACE_READ_END_NO_SUBSCRIPTS(); *write_to = value; return 0; @@ -349,6 +291,8 @@ static int cbs_av1_write_increment(CodedBitstreamContext *ctx, PutBitContext *pb { int len; + CBS_TRACE_WRITE_START(); + av_assert0(range_min <= range_max && range_max - range_min < 32); if (value < range_min || value > range_max) { av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " @@ -364,22 +308,10 @@ static int cbs_av1_write_increment(CodedBitstreamContext *ctx, PutBitContext *pb if (put_bits_left(pbc) < len) return AVERROR(ENOSPC); - if (ctx->trace_enable) { - char bits[33]; - int i; - for (i = 0; i < len; i++) { - if (range_min + i == value) - bits[i] = '0'; - else - bits[i] = '1'; - } - bits[i] = 0; - ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), - name, NULL, bits, value); - } - if (len > 0) - put_bits(pbc, len, (1 << len) - 1 - (value != range_max)); + put_bits(pbc, len, (1U << len) - 1 - (value != range_max)); + + CBS_TRACE_WRITE_END_NO_SUBSCRIPTS(); return 0; } @@ -388,12 +320,10 @@ static int cbs_av1_read_subexp(CodedBitstreamContext *ctx, GetBitContext *gbc, uint32_t range_max, const char *name, const int *subscripts, uint32_t *write_to) { - uint32_t value; - int position, err; - uint32_t max_len, len, range_offset, range_bits; + uint32_t value, max_len, len, range_offset, range_bits; + int err; - if (ctx->trace_enable) - position = get_bits_count(gbc); + CBS_TRACE_READ_START(); av_assert0(range_max > 0); max_len = av_log2(range_max - 1) - 3; @@ -412,9 +342,8 @@ static int cbs_av1_read_subexp(CodedBitstreamContext *ctx, GetBitContext *gbc, } if (len < max_len) { - err = ff_cbs_read_unsigned(ctx, gbc, range_bits, - "subexp_bits", NULL, &value, - 0, MAX_UINT_BITS(range_bits)); + err = ff_cbs_read_simple_unsigned(ctx, gbc, range_bits, + "subexp_bits", &value); if (err < 0) return err; @@ -426,9 +355,7 @@ static int cbs_av1_read_subexp(CodedBitstreamContext *ctx, GetBitContext *gbc, } value += range_offset; - if (ctx->trace_enable) - ff_cbs_trace_syntax_element(ctx, position, - name, subscripts, "", value); + CBS_TRACE_READ_END_VALUE_ONLY(); *write_to = value; return err; @@ -438,9 +365,11 @@ static int cbs_av1_write_subexp(CodedBitstreamContext *ctx, PutBitContext *pbc, uint32_t range_max, const char *name, const int *subscripts, uint32_t value) { - int position, err; + int err; uint32_t max_len, len, range_offset, range_bits; + CBS_TRACE_WRITE_START(); + if (value > range_max) { av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " "%"PRIu32", but must be in [0,%"PRIu32"].\n", @@ -448,9 +377,6 @@ static int cbs_av1_write_subexp(CodedBitstreamContext *ctx, PutBitContext *pbc, return AVERROR_INVALIDDATA; } - if (ctx->trace_enable) - position = put_bits_count(pbc); - av_assert0(range_max > 0); max_len = av_log2(range_max - 1) - 3; @@ -476,10 +402,9 @@ static int cbs_av1_write_subexp(CodedBitstreamContext *ctx, PutBitContext *pbc, return err; if (len < max_len) { - err = ff_cbs_write_unsigned(ctx, pbc, range_bits, - "subexp_bits", NULL, - value - range_offset, - 0, MAX_UINT_BITS(range_bits)); + err = ff_cbs_write_simple_unsigned(ctx, pbc, range_bits, + "subexp_bits", + value - range_offset); if (err < 0) return err; @@ -491,9 +416,7 @@ static int cbs_av1_write_subexp(CodedBitstreamContext *ctx, PutBitContext *pbc, return err; } - if (ctx->trace_enable) - ff_cbs_trace_syntax_element(ctx, position, - name, subscripts, "", value); + CBS_TRACE_WRITE_END_VALUE_ONLY(); return err; } @@ -546,8 +469,6 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL) -#define fb(width, name) \ - xf(width, name, current->name, 0, MAX_UINT_BITS(width), 0, ) #define fc(width, name, range_min, range_max) \ xf(width, name, current->name, range_min, range_max, 0, ) #define flag(name) fb(1, name) @@ -573,6 +494,13 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #define READWRITE read #define RWContext GetBitContext +#define fb(width, name) do { \ + uint32_t value; \ + CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, \ + #name, &value)); \ + current->name = value; \ + } while (0) + #define xf(width, name, var, range_min, range_max, subs, ...) do { \ uint32_t value; \ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ @@ -645,6 +573,7 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #undef READ #undef READWRITE #undef RWContext +#undef fb #undef xf #undef xsu #undef uvlc @@ -661,6 +590,11 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #define READWRITE write #define RWContext PutBitContext +#define fb(width, name) do { \ + CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \ + current->name)); \ + } while (0) + #define xf(width, name, var, range_min, range_max, subs, ...) do { \ CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), \ @@ -703,7 +637,7 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) } while (0) #define leb128(name) do { \ - CHECK(cbs_av1_write_leb128(ctx, rw, #name, current->name)); \ + CHECK(cbs_av1_write_leb128(ctx, rw, #name, current->name, 0)); \ } while (0) #define infer(name, value) do { \ @@ -723,6 +657,7 @@ static size_t cbs_av1_get_payload_bytes_left(GetBitContext *gbc) #undef WRITE #undef READWRITE #undef RWContext +#undef fb #undef xf #undef xsu #undef uvlc @@ -943,12 +878,7 @@ static int cbs_av1_read_unit(CodedBitstreamContext *ctx, priv->operating_point_idc = sequence_header->operating_point_idc[priv->operating_point]; } - av_buffer_unref(&priv->sequence_header_ref); - priv->sequence_header = NULL; - - priv->sequence_header_ref = av_buffer_ref(unit->content_ref); - if (!priv->sequence_header_ref) - return AVERROR(ENOMEM); + ff_refstruct_replace(&priv->sequence_header_ref, unit->content_ref); priv->sequence_header = &obu->obu.sequence_header; } break; @@ -1067,9 +997,7 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx, av1ctx = *priv; if (priv->sequence_header_ref) { - av1ctx.sequence_header_ref = av_buffer_ref(priv->sequence_header_ref); - if (!av1ctx.sequence_header_ref) - return AVERROR(ENOMEM); + av1ctx.sequence_header_ref = ff_refstruct_ref(priv->sequence_header_ref); } if (priv->frame_header_ref) { @@ -1086,9 +1014,14 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx, if (obu->header.obu_has_size_field) { pbc_tmp = *pbc; - // Add space for the size field to fill later. - put_bits32(pbc, 0); - put_bits32(pbc, 0); + if (priv->fixed_obu_size_length) { + for (int i = 0; i < priv->fixed_obu_size_length; i++) + put_bits(pbc, 8, 0); + } else { + // Add space for the size field to fill later. + put_bits32(pbc, 0); + put_bits32(pbc, 0); + } } td = NULL; @@ -1102,19 +1035,14 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx, if (err < 0) goto error; - av_buffer_unref(&priv->sequence_header_ref); + ff_refstruct_unref(&priv->sequence_header_ref); priv->sequence_header = NULL; err = ff_cbs_make_unit_refcounted(ctx, unit); if (err < 0) goto error; - priv->sequence_header_ref = av_buffer_ref(unit->content_ref); - if (!priv->sequence_header_ref) { - err = AVERROR(ENOMEM); - goto error; - } - + priv->sequence_header_ref = ff_refstruct_ref(unit->content_ref); priv->sequence_header = &obu->obu.sequence_header; } break; @@ -1208,7 +1136,8 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx, end_pos /= 8; *pbc = pbc_tmp; - err = cbs_av1_write_leb128(ctx, pbc, "obu_size", obu->obu_size); + err = cbs_av1_write_leb128(ctx, pbc, "obu_size", obu->obu_size, + priv->fixed_obu_size_length); if (err < 0) goto error; @@ -1217,7 +1146,7 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx, av_assert0(data_pos <= start_pos); if (8 * obu->obu_size > put_bits_left(pbc)) { - av_buffer_unref(&priv->sequence_header_ref); + ff_refstruct_unref(&priv->sequence_header_ref); av_buffer_unref(&priv->frame_header_ref); *priv = av1ctx; @@ -1225,8 +1154,13 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx, } if (obu->obu_size > 0) { - memmove(pbc->buf + data_pos, - pbc->buf + start_pos, header_size); + if (!priv->fixed_obu_size_length) { + memmove(pbc->buf + data_pos, + pbc->buf + start_pos, header_size); + } else { + // The size was fixed so the following data was + // already written in the correct place. + } skip_put_bytes(pbc, header_size); if (td) { @@ -1241,7 +1175,7 @@ static int cbs_av1_write_obu(CodedBitstreamContext *ctx, err = 0; error: - av_buffer_unref(&av1ctx.sequence_header_ref); + ff_refstruct_unref(&av1ctx.sequence_header_ref); av_buffer_unref(&av1ctx.frame_header_ref); return err; @@ -1293,24 +1227,30 @@ static void cbs_av1_close(CodedBitstreamContext *ctx) { CodedBitstreamAV1Context *priv = ctx->priv_data; - av_buffer_unref(&priv->sequence_header_ref); + ff_refstruct_unref(&priv->sequence_header_ref); av_buffer_unref(&priv->frame_header_ref); } -static void cbs_av1_free_metadata(void *unit, uint8_t *content) +static void cbs_av1_free_metadata(FFRefStructOpaque unused, void *content) { - AV1RawOBU *obu = (AV1RawOBU*)content; + AV1RawOBU *obu = content; AV1RawMetadata *md; av_assert0(obu->header.obu_type == AV1_OBU_METADATA); md = &obu->obu.metadata; switch (md->metadata_type) { + case AV1_METADATA_TYPE_HDR_CLL: + case AV1_METADATA_TYPE_HDR_MDCV: + case AV1_METADATA_TYPE_SCALABILITY: + case AV1_METADATA_TYPE_TIMECODE: + break; case AV1_METADATA_TYPE_ITUT_T35: av_buffer_unref(&md->metadata.itut_t35.payload_ref); break; + default: + av_buffer_unref(&md->metadata.unknown.payload_ref); } - av_free(content); } static const CodedBitstreamUnitTypeDescriptor cbs_av1_unit_types[] = { @@ -1338,6 +1278,8 @@ static const CodedBitstreamUnitTypeDescriptor cbs_av1_unit_types[] = { static const AVOption cbs_av1_options[] = { { "operating_point", "Set operating point to select layers to parse from a scalable bitstream", OFFSET(operating_point), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, AV1_MAX_OPERATING_POINTS - 1, 0 }, + { "fixed_obu_size_length", "Set fixed length of the obu_size field", + OFFSET(fixed_obu_size_length), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 8, 0 }, { NULL } }; diff --git a/libavcodec/cbs_av1.h b/libavcodec/cbs_av1.h index 1fc80dcfa05..a027013bc72 100644 --- a/libavcodec/cbs_av1.h +++ b/libavcodec/cbs_av1.h @@ -215,6 +215,8 @@ typedef struct AV1RawFrameHeader { uint8_t uniform_tile_spacing_flag; uint8_t tile_cols_log2; uint8_t tile_rows_log2; + uint8_t tile_start_col_sb[AV1_MAX_TILE_COLS]; + uint8_t tile_start_row_sb[AV1_MAX_TILE_COLS]; uint8_t width_in_sbs_minus_1[AV1_MAX_TILE_COLS]; uint8_t height_in_sbs_minus_1[AV1_MAX_TILE_ROWS]; uint16_t context_update_tile_id; @@ -370,6 +372,12 @@ typedef struct AV1RawMetadataTimecode { uint32_t time_offset_value; } AV1RawMetadataTimecode; +typedef struct AV1RawMetadataUnknown { + uint8_t *payload; + AVBufferRef *payload_ref; + size_t payload_size; +} AV1RawMetadataUnknown; + typedef struct AV1RawMetadata { uint64_t metadata_type; union { @@ -378,6 +386,7 @@ typedef struct AV1RawMetadata { AV1RawMetadataScalability scalability; AV1RawMetadataITUTT35 itut_t35; AV1RawMetadataTimecode timecode; + AV1RawMetadataUnknown unknown; } metadata; } AV1RawMetadata; @@ -418,6 +427,8 @@ typedef struct AV1ReferenceFrameState { int bit_depth; // RefBitDepth int order_hint; // RefOrderHint + int saved_order_hints[AV1_TOTAL_REFS_PER_FRAME]; // SavedOrderHints[ref] + int8_t loop_filter_ref_deltas[AV1_TOTAL_REFS_PER_FRAME]; int8_t loop_filter_mode_deltas[2]; uint8_t feature_enabled[AV1_MAX_SEGMENTS][AV1_SEG_LVL_MAX]; @@ -428,7 +439,8 @@ typedef struct CodedBitstreamAV1Context { const AVClass *class; AV1RawSequenceHeader *sequence_header; - AVBufferRef *sequence_header_ref; + /** A RefStruct reference backing sequence_header. */ + AV1RawOBU *sequence_header_ref; int seen_frame_header; AVBufferRef *frame_header_ref; @@ -454,10 +466,17 @@ typedef struct CodedBitstreamAV1Context { int tile_rows; int tile_num; + int order_hints[AV1_TOTAL_REFS_PER_FRAME]; // OrderHints + int ref_frame_sign_bias[AV1_TOTAL_REFS_PER_FRAME]; // RefFrameSignBias + AV1ReferenceFrameState ref[AV1_NUM_REF_FRAMES]; // AVOptions int operating_point; + // When writing, fix the length in bytes of the obu_size field. + // Writing will fail with an error if an OBU larger than can be + // represented by the fixed size is encountered. + int fixed_obu_size_length; } CodedBitstreamAV1Context; diff --git a/libavcodec/cbs_av1_syntax_template.c b/libavcodec/cbs_av1_syntax_template.c index e95925a493e..3f4b13a1771 100644 --- a/libavcodec/cbs_av1_syntax_template.c +++ b/libavcodec/cbs_av1_syntax_template.c @@ -82,7 +82,7 @@ static int FUNC(color_config)(CodedBitstreamContext *ctx, RWContext *rw, flag(high_bitdepth); - if (seq_profile == FF_PROFILE_AV1_PROFESSIONAL && + if (seq_profile == AV_PROFILE_AV1_PROFESSIONAL && current->high_bitdepth) { flag(twelve_bit); priv->bit_depth = current->twelve_bit ? 12 : 10; @@ -90,7 +90,7 @@ static int FUNC(color_config)(CodedBitstreamContext *ctx, RWContext *rw, priv->bit_depth = current->high_bitdepth ? 10 : 8; } - if (seq_profile == FF_PROFILE_AV1_HIGH) + if (seq_profile == AV_PROFILE_AV1_HIGH) infer(mono_chrome, 0); else flag(mono_chrome); @@ -126,10 +126,10 @@ static int FUNC(color_config)(CodedBitstreamContext *ctx, RWContext *rw, } else { flag(color_range); - if (seq_profile == FF_PROFILE_AV1_MAIN) { + if (seq_profile == AV_PROFILE_AV1_MAIN) { infer(subsampling_x, 1); infer(subsampling_y, 1); - } else if (seq_profile == FF_PROFILE_AV1_HIGH) { + } else if (seq_profile == AV_PROFILE_AV1_HIGH) { infer(subsampling_x, 0); infer(subsampling_y, 0); } else { @@ -176,7 +176,7 @@ static int FUNC(decoder_model_info)(CodedBitstreamContext *ctx, RWContext *rw, int err; fb(5, buffer_delay_length_minus_1); - fb(32, num_units_in_decoding_tick); + fc(32, num_units_in_decoding_tick, 1, MAX_UINT_BITS(32)); fb(5, buffer_removal_time_length_minus_1); fb(5, frame_presentation_time_length_minus_1); @@ -190,8 +190,8 @@ static int FUNC(sequence_header_obu)(CodedBitstreamContext *ctx, RWContext *rw, HEADER("Sequence Header"); - fc(3, seq_profile, FF_PROFILE_AV1_MAIN, - FF_PROFILE_AV1_PROFESSIONAL); + fc(3, seq_profile, AV_PROFILE_AV1_MAIN, + AV_PROFILE_AV1_PROFESSIONAL); flag(still_picture); flag(reduced_still_picture_header); @@ -360,7 +360,7 @@ static int FUNC(set_frame_refs)(CodedBitstreamContext *ctx, RWContext *rw, int i, j; for (i = 0; i < AV1_REFS_PER_FRAME; i++) - ref_frame_idx[i] = -1; + ref_frame_idx[i] = AV1_REF_FRAME_NONE; ref_frame_idx[AV1_REF_FRAME_LAST - AV1_REF_FRAME_LAST] = current->last_frame_idx; ref_frame_idx[AV1_REF_FRAME_GOLDEN - AV1_REF_FRAME_LAST] = current->golden_frame_idx; @@ -378,7 +378,7 @@ static int FUNC(set_frame_refs)(CodedBitstreamContext *ctx, RWContext *rw, latest_order_hint = shifted_order_hints[current->last_frame_idx]; earliest_order_hint = shifted_order_hints[current->golden_frame_idx]; - ref = -1; + ref = AV1_REF_FRAME_NONE; for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { int hint = shifted_order_hints[i]; if (!used_frame[i] && hint >= cur_frame_hint && @@ -392,7 +392,7 @@ static int FUNC(set_frame_refs)(CodedBitstreamContext *ctx, RWContext *rw, used_frame[ref] = 1; } - ref = -1; + ref = AV1_REF_FRAME_NONE; for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { int hint = shifted_order_hints[i]; if (!used_frame[i] && hint >= cur_frame_hint && @@ -406,7 +406,7 @@ static int FUNC(set_frame_refs)(CodedBitstreamContext *ctx, RWContext *rw, used_frame[ref] = 1; } - ref = -1; + ref = AV1_REF_FRAME_NONE; for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { int hint = shifted_order_hints[i]; if (!used_frame[i] && hint >= cur_frame_hint && @@ -423,7 +423,7 @@ static int FUNC(set_frame_refs)(CodedBitstreamContext *ctx, RWContext *rw, for (i = 0; i < AV1_REFS_PER_FRAME - 2; i++) { int ref_frame = ref_frame_list[i]; if (ref_frame_idx[ref_frame - AV1_REF_FRAME_LAST] < 0 ) { - ref = -1; + ref = AV1_REF_FRAME_NONE; for (j = 0; j < AV1_NUM_REF_FRAMES; j++) { int hint = shifted_order_hints[j]; if (!used_frame[j] && hint < cur_frame_hint && @@ -439,7 +439,7 @@ static int FUNC(set_frame_refs)(CodedBitstreamContext *ctx, RWContext *rw, } } - ref = -1; + ref = AV1_REF_FRAME_NONE; for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { int hint = shifted_order_hints[i]; if (ref < 0 || hint < earliest_order_hint) { @@ -626,6 +626,10 @@ static int FUNC(tile_info)(CodedBitstreamContext *ctx, RWContext *rw, tile_width_sb = (sb_cols + (1 << current->tile_cols_log2) - 1) >> current->tile_cols_log2; + + for (int off = 0, i = 0; off < sb_cols; off += tile_width_sb) + current->tile_start_col_sb[i++] = off; + current->tile_cols = (sb_cols + tile_width_sb - 1) / tile_width_sb; min_log2_tile_rows = FFMAX(min_log2_tiles - current->tile_cols_log2, 0); @@ -634,6 +638,10 @@ static int FUNC(tile_info)(CodedBitstreamContext *ctx, RWContext *rw, tile_height_sb = (sb_rows + (1 << current->tile_rows_log2) - 1) >> current->tile_rows_log2; + + for (int off = 0, i = 0; off < sb_rows; off += tile_height_sb) + current->tile_start_row_sb[i++] = off; + current->tile_rows = (sb_rows + tile_height_sb - 1) / tile_height_sb; for (i = 0; i < current->tile_cols - 1; i++) @@ -652,6 +660,7 @@ static int FUNC(tile_info)(CodedBitstreamContext *ctx, RWContext *rw, start_sb = 0; for (i = 0; start_sb < sb_cols && i < AV1_MAX_TILE_COLS; i++) { + current->tile_start_col_sb[i] = start_sb; max_width = FFMIN(sb_cols - start_sb, max_tile_width_sb); ns(max_width, width_in_sbs_minus_1[i], 1, i); size_sb = current->width_in_sbs_minus_1[i] + 1; @@ -669,6 +678,7 @@ static int FUNC(tile_info)(CodedBitstreamContext *ctx, RWContext *rw, start_sb = 0; for (i = 0; start_sb < sb_rows && i < AV1_MAX_TILE_ROWS; i++) { + current->tile_start_row_sb[i] = start_sb; max_height = FFMIN(sb_rows - start_sb, max_tile_height_sb); ns(max_height, height_in_sbs_minus_1[i], 1, i); size_sb = current->height_in_sbs_minus_1[i] + 1; @@ -1018,9 +1028,9 @@ static int FUNC(read_tx_mode)(CodedBitstreamContext *ctx, RWContext *rw, int err; if (priv->coded_lossless) - infer(tx_mode, 0); + infer(tx_mode, AV1_ONLY_4X4); else - increment(tx_mode, 1, 2); + increment(tx_mode, AV1_TX_MODE_LARGEST, AV1_TX_MODE_SELECT); return 0; } @@ -1404,6 +1414,8 @@ static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx, RWContext *rw, priv->ref[i].valid = 0; priv->ref[i].order_hint = 0; } + for (i = 0; i < AV1_REFS_PER_FRAME; i++) + priv->order_hints[i + AV1_REF_FRAME_LAST] = 0; } flag(disable_cdf_update); @@ -1558,11 +1570,20 @@ static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx, RWContext *rw, else flag(use_ref_frame_mvs); - infer(allow_intrabc, 0); - } + for (i = 0; i < AV1_REFS_PER_FRAME; i++) { + int ref_frame = AV1_REF_FRAME_LAST + i; + int hint = priv->ref[current->ref_frame_idx[i]].order_hint; + priv->order_hints[ref_frame] = hint; + if (!seq->enable_order_hint) { + priv->ref_frame_sign_bias[ref_frame] = 0; + } else { + priv->ref_frame_sign_bias[ref_frame] = + cbs_av1_get_relative_dist(seq, hint, + current->order_hint) > 0; + } + } - if (!frame_is_intra) { - // Derive reference frame sign biases. + infer(allow_intrabc, 0); } if (seq->reduced_still_picture_header || current->disable_cdf_update) @@ -1664,6 +1685,12 @@ static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx, RWContext *rw, .bit_depth = priv->bit_depth, .order_hint = priv->order_hint, }; + + for (int j = 0; j < AV1_REFS_PER_FRAME; j++) { + priv->ref[i].saved_order_hints[j + AV1_REF_FRAME_LAST] = + priv->order_hints[j + AV1_REF_FRAME_LAST]; + } + memcpy(priv->ref[i].loop_filter_ref_deltas, current->loop_filter_ref_deltas, sizeof(current->loop_filter_ref_deltas)); memcpy(priv->ref[i].loop_filter_mode_deltas, current->loop_filter_mode_deltas, @@ -1843,6 +1870,8 @@ static int FUNC(metadata_hdr_cll)(CodedBitstreamContext *ctx, RWContext *rw, { int err; + HEADER("HDR CLL Metadata"); + fb(16, max_cll); fb(16, max_fall); @@ -1854,6 +1883,8 @@ static int FUNC(metadata_hdr_mdcv)(CodedBitstreamContext *ctx, RWContext *rw, { int err, i; + HEADER("HDR MDCV Metadata"); + for (i = 0; i < 3; i++) { fbs(16, primary_chromaticity_x[i], 1, i); fbs(16, primary_chromaticity_y[i], 1, i); @@ -1920,6 +1951,8 @@ static int FUNC(metadata_scalability)(CodedBitstreamContext *ctx, RWContext *rw, { int err; + HEADER("Scalability Metadata"); + fb(8, scalability_mode_idc); if (current->scalability_mode_idc == AV1_SCALABILITY_SS) @@ -1934,6 +1967,8 @@ static int FUNC(metadata_itut_t35)(CodedBitstreamContext *ctx, RWContext *rw, int err; size_t i; + HEADER("ITU-T T.35 Metadata"); + fb(8, itu_t_t35_country_code); if (current->itu_t_t35_country_code == 0xff) fb(8, itu_t_t35_country_code_extension_byte); @@ -1961,6 +1996,8 @@ static int FUNC(metadata_timecode)(CodedBitstreamContext *ctx, RWContext *rw, { int err; + HEADER("Timecode Metadata"); + fb(5, counting_type); flag(full_timestamp_flag); flag(discontinuity_flag); @@ -1994,6 +2031,29 @@ static int FUNC(metadata_timecode)(CodedBitstreamContext *ctx, RWContext *rw, return 0; } +static int FUNC(metadata_unknown)(CodedBitstreamContext *ctx, RWContext *rw, + AV1RawMetadataUnknown *current) +{ + int err; + size_t i; + + HEADER("Unknown Metadata"); + +#ifdef READ + current->payload_size = cbs_av1_get_payload_bytes_left(rw); + + current->payload_ref = av_buffer_alloc(current->payload_size); + if (!current->payload_ref) + return AVERROR(ENOMEM); + current->payload = current->payload_ref->data; +#endif + + for (i = 0; i < current->payload_size; i++) + fbs(8, payload[i], 1, i); + + return 0; +} + static int FUNC(metadata_obu)(CodedBitstreamContext *ctx, RWContext *rw, AV1RawMetadata *current) { @@ -2018,8 +2078,7 @@ static int FUNC(metadata_obu)(CodedBitstreamContext *ctx, RWContext *rw, CHECK(FUNC(metadata_timecode)(ctx, rw, ¤t->metadata.timecode)); break; default: - // Unknown metadata type. - return AVERROR_PATCHWELCOME; + CHECK(FUNC(metadata_unknown)(ctx, rw, ¤t->metadata.unknown)); } return 0; diff --git a/libavcodec/cbs_bsf.c b/libavcodec/cbs_bsf.c index 069f6e99186..b25285483bc 100644 --- a/libavcodec/cbs_bsf.c +++ b/libavcodec/cbs_bsf.c @@ -123,6 +123,11 @@ int ff_cbs_bsf_generic_init(AVBSFContext *bsf, const CBSBSFType *type) if (err < 0) return err; + ctx->output->trace_enable = 1; + ctx->output->trace_level = AV_LOG_TRACE; + ctx->output->trace_context = ctx->output; + ctx->output->trace_write_callback = ff_cbs_trace_write_log; + if (bsf->par_in->extradata) { err = ff_cbs_read_extradata(ctx->input, frag, bsf->par_in); if (err < 0) { diff --git a/libavcodec/cbs_bsf.h b/libavcodec/cbs_bsf.h index aa7385c8f22..26bd448b876 100644 --- a/libavcodec/cbs_bsf.h +++ b/libavcodec/cbs_bsf.h @@ -98,7 +98,7 @@ enum { // Pass this element through unchanged. BSF_ELEMENT_PASS, // Insert this element, replacing any existing instances of it. - // Associated values may be provided explicitly (as addtional options) + // Associated values may be provided explicitly (as additional options) // or implicitly (either as side data or deduced from other parts of // the stream). BSF_ELEMENT_INSERT, @@ -112,7 +112,7 @@ enum { #define BSF_ELEMENT_OPTIONS_PIR(name, help, field, opt_flags) \ { name, help, OFFSET(field), AV_OPT_TYPE_INT, \ { .i64 = BSF_ELEMENT_PASS }, \ - BSF_ELEMENT_PASS, BSF_ELEMENT_REMOVE, opt_flags, name }, \ + BSF_ELEMENT_PASS, BSF_ELEMENT_REMOVE, opt_flags, .unit = name }, \ { "pass", NULL, 0, AV_OPT_TYPE_CONST, \ { .i64 = BSF_ELEMENT_PASS }, .flags = opt_flags, .unit = name }, \ { "insert", NULL, 0, AV_OPT_TYPE_CONST, \ @@ -123,7 +123,7 @@ enum { #define BSF_ELEMENT_OPTIONS_PIRE(name, help, field, opt_flags) \ { name, help, OFFSET(field), AV_OPT_TYPE_INT, \ { .i64 = BSF_ELEMENT_PASS }, \ - BSF_ELEMENT_PASS, BSF_ELEMENT_EXTRACT, opt_flags, name }, \ + BSF_ELEMENT_PASS, BSF_ELEMENT_EXTRACT, opt_flags, .unit = name }, \ { "pass", NULL, 0, AV_OPT_TYPE_CONST, \ { .i64 = BSF_ELEMENT_PASS }, .flags = opt_flags, .unit = name }, \ { "insert", NULL, 0, AV_OPT_TYPE_CONST, \ diff --git a/libavcodec/cbs_h264.h b/libavcodec/cbs_h264.h index ca9b688c057..db91231337d 100644 --- a/libavcodec/cbs_h264.h +++ b/libavcodec/cbs_h264.h @@ -407,10 +407,8 @@ typedef struct CodedBitstreamH264Context { // All currently available parameter sets. These are updated when // any parameter set NAL unit is read/written with this context. - AVBufferRef *sps_ref[H264_MAX_SPS_COUNT]; - AVBufferRef *pps_ref[H264_MAX_PPS_COUNT]; - H264RawSPS *sps[H264_MAX_SPS_COUNT]; - H264RawPPS *pps[H264_MAX_PPS_COUNT]; + H264RawSPS *sps[H264_MAX_SPS_COUNT]; ///< RefStruct references + H264RawPPS *pps[H264_MAX_PPS_COUNT]; ///< RefStruct references // The currently active parameter sets. These are updated when any // NAL unit refers to the relevant parameter set. These pointers diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c index 80e48829af9..db803ea351d 100644 --- a/libavcodec/cbs_h2645.c +++ b/libavcodec/cbs_h2645.c @@ -24,9 +24,12 @@ #include "cbs_internal.h" #include "cbs_h264.h" #include "cbs_h265.h" +#include "cbs_h266.h" #include "h264.h" #include "h2645_parse.h" #include "hevc.h" +#include "refstruct.h" +#include "vvc.h" static int cbs_read_ue_golomb(CodedBitstreamContext *ctx, GetBitContext *gbc, @@ -34,41 +37,38 @@ static int cbs_read_ue_golomb(CodedBitstreamContext *ctx, GetBitContext *gbc, uint32_t *write_to, uint32_t range_min, uint32_t range_max) { - uint32_t value; - int position, i, j; - unsigned int k; - char bits[65]; + uint32_t leading_bits, value; + int max_length, leading_zeroes; - position = get_bits_count(gbc); + CBS_TRACE_READ_START(); - for (i = 0; i < 32; i++) { - if (get_bits_left(gbc) < i + 1) { + max_length = FFMIN(get_bits_left(gbc), 32); + + leading_bits = max_length ? show_bits_long(gbc, max_length) : 0; + if (leading_bits == 0) { + if (max_length >= 32) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid ue-golomb code at " + "%s: more than 31 zeroes.\n", name); + return AVERROR_INVALIDDATA; + } else { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid ue-golomb code at " "%s: bitstream ended.\n", name); return AVERROR_INVALIDDATA; } - k = get_bits1(gbc); - bits[i] = k ? '1' : '0'; - if (k) - break; } - if (i >= 32) { + + leading_zeroes = max_length - 1 - av_log2(leading_bits); + skip_bits_long(gbc, leading_zeroes); + + if (get_bits_left(gbc) < leading_zeroes + 1) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid ue-golomb code at " - "%s: more than 31 zeroes.\n", name); + "%s: bitstream ended.\n", name); return AVERROR_INVALIDDATA; } - value = 1; - for (j = 0; j < i; j++) { - k = get_bits1(gbc); - bits[i + j + 1] = k ? '1' : '0'; - value = value << 1 | k; - } - bits[i + j + 1] = 0; - --value; - if (ctx->trace_enable) - ff_cbs_trace_syntax_element(ctx, position, name, subscripts, - bits, value); + value = get_bits_long(gbc, leading_zeroes + 1) - 1; + + CBS_TRACE_READ_END(); if (value < range_min || value > range_max) { av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " @@ -86,45 +86,44 @@ static int cbs_read_se_golomb(CodedBitstreamContext *ctx, GetBitContext *gbc, int32_t *write_to, int32_t range_min, int32_t range_max) { + uint32_t leading_bits, unsigned_value; + int max_length, leading_zeroes; int32_t value; - int position, i, j; - unsigned int k; - uint32_t v; - char bits[65]; - position = get_bits_count(gbc); + CBS_TRACE_READ_START(); + + max_length = FFMIN(get_bits_left(gbc), 32); - for (i = 0; i < 32; i++) { - if (get_bits_left(gbc) < i + 1) { + leading_bits = max_length ? show_bits_long(gbc, max_length) : 0; + if (leading_bits == 0) { + if (max_length >= 32) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid se-golomb code at " + "%s: more than 31 zeroes.\n", name); + return AVERROR_INVALIDDATA; + } else { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid se-golomb code at " "%s: bitstream ended.\n", name); return AVERROR_INVALIDDATA; } - k = get_bits1(gbc); - bits[i] = k ? '1' : '0'; - if (k) - break; } - if (i >= 32) { + + leading_zeroes = max_length - 1 - av_log2(leading_bits); + skip_bits_long(gbc, leading_zeroes); + + if (get_bits_left(gbc) < leading_zeroes + 1) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid se-golomb code at " - "%s: more than 31 zeroes.\n", name); + "%s: bitstream ended.\n", name); return AVERROR_INVALIDDATA; } - v = 1; - for (j = 0; j < i; j++) { - k = get_bits1(gbc); - bits[i + j + 1] = k ? '1' : '0'; - v = v << 1 | k; - } - bits[i + j + 1] = 0; - if (v & 1) - value = -(int32_t)(v / 2); + + unsigned_value = get_bits_long(gbc, leading_zeroes + 1); + + if (unsigned_value & 1) + value = -(int32_t)(unsigned_value / 2); else - value = v / 2; + value = unsigned_value / 2; - if (ctx->trace_enable) - ff_cbs_trace_syntax_element(ctx, position, name, subscripts, - bits, value); + CBS_TRACE_READ_END(); if (value < range_min || value > range_max) { av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " @@ -144,6 +143,8 @@ static int cbs_write_ue_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc, { int len; + CBS_TRACE_WRITE_START(); + if (value < range_min || value > range_max) { av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " "%"PRIu32", but must be in [%"PRIu32",%"PRIu32"].\n", @@ -156,27 +157,14 @@ static int cbs_write_ue_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc, if (put_bits_left(pbc) < 2 * len + 1) return AVERROR(ENOSPC); - if (ctx->trace_enable) { - char bits[65]; - int i; - - for (i = 0; i < len; i++) - bits[i] = '0'; - bits[len] = '1'; - for (i = 0; i < len; i++) - bits[len + i + 1] = (value + 1) >> (len - i - 1) & 1 ? '1' : '0'; - bits[len + len + 1] = 0; - - ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), - name, subscripts, bits, value); - } - put_bits(pbc, len, 0); if (len + 1 < 32) put_bits(pbc, len + 1, value + 1); else put_bits32(pbc, value + 1); + CBS_TRACE_WRITE_END(); + return 0; } @@ -188,6 +176,8 @@ static int cbs_write_se_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc, int len; uint32_t uvalue; + CBS_TRACE_WRITE_START(); + if (value < range_min || value > range_max) { av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " "%"PRId32", but must be in [%"PRId32",%"PRId32"].\n", @@ -207,27 +197,14 @@ static int cbs_write_se_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc, if (put_bits_left(pbc) < 2 * len + 1) return AVERROR(ENOSPC); - if (ctx->trace_enable) { - char bits[65]; - int i; - - for (i = 0; i < len; i++) - bits[i] = '0'; - bits[len] = '1'; - for (i = 0; i < len; i++) - bits[len + i + 1] = (uvalue + 1) >> (len - i - 1) & 1 ? '1' : '0'; - bits[len + len + 1] = 0; - - ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), - name, subscripts, bits, value); - } - put_bits(pbc, len, 0); if (len + 1 < 32) put_bits(pbc, len + 1, uvalue + 1); else put_bits32(pbc, uvalue + 1); + CBS_TRACE_WRITE_END(); + return 0; } @@ -255,14 +232,23 @@ static int cbs_h265_payload_extension_present(GetBitContext *gbc, uint32_t paylo #define FUNC_NAME1(rw, codec, name) FUNC_NAME2(rw, codec, name) #define FUNC_H264(name) FUNC_NAME1(READWRITE, h264, name) #define FUNC_H265(name) FUNC_NAME1(READWRITE, h265, name) +#define FUNC_H266(name) FUNC_NAME1(READWRITE, h266, name) #define FUNC_SEI(name) FUNC_NAME1(READWRITE, sei, name) +#define SEI_FUNC(name, args) \ +static int FUNC(name) args; \ +static int FUNC(name ## _internal)(CodedBitstreamContext *ctx, \ + RWContext *rw, void *cur, \ + SEIMessageState *state) \ +{ \ + return FUNC(name)(ctx, rw, cur, state); \ +} \ +static int FUNC(name) args + #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL) #define u(width, name, range_min, range_max) \ xu(width, name, current->name, range_min, range_max, 0, ) -#define ub(width, name) \ - xu(width, name, current->name, 0, MAX_UINT_BITS(width), 0, ) #define flag(name) ub(1, name) #define ue(name, range_min, range_max) \ xue(name, current->name, range_min, range_max, 0, ) @@ -298,6 +284,12 @@ static int cbs_h265_payload_extension_present(GetBitContext *gbc, uint32_t paylo #define READWRITE read #define RWContext GetBitContext +#define ub(width, name) do { \ + uint32_t value; \ + CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, #name, \ + &value)); \ + current->name = value; \ + } while (0) #define xu(width, name, var, range_min, range_max, subs, ...) do { \ uint32_t value; \ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ @@ -349,18 +341,31 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc) #define bit_position(rw) (get_bits_count(rw)) #define byte_alignment(rw) (get_bits_count(rw) % 8) +/* The CBS SEI code uses the refstruct API for the allocation + * of its child buffers. */ #define allocate(name, size) do { \ - name ## _ref = av_buffer_allocz(size + \ + name = ff_refstruct_allocz(size + \ AV_INPUT_BUFFER_PADDING_SIZE); \ - if (!name ## _ref) \ + if (!name) \ return AVERROR(ENOMEM); \ - name = name ## _ref->data; \ } while (0) #define FUNC(name) FUNC_SEI(name) #include "cbs_sei_syntax_template.c" #undef FUNC +#undef allocate + +/* The other code uses the refstruct API for the allocation + * of its child buffers. */ +#define allocate(name, size) do { \ + name ## _ref = av_buffer_allocz(size + \ + AV_INPUT_BUFFER_PADDING_SIZE); \ + if (!name ## _ref) \ + return AVERROR(ENOMEM); \ + name = name ## _ref->data; \ + } while (0) + #define FUNC(name) FUNC_H264(name) #include "cbs_h264_syntax_template.c" #undef FUNC @@ -369,9 +374,14 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc) #include "cbs_h265_syntax_template.c" #undef FUNC +#define FUNC(name) FUNC_H266(name) +#include "cbs_h266_syntax_template.c" +#undef FUNC + #undef READ #undef READWRITE #undef RWContext +#undef ub #undef xu #undef xi #undef xue @@ -387,6 +397,11 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc) #define READWRITE write #define RWContext PutBitContext +#define ub(width, name) do { \ + uint32_t value = current->name; \ + CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \ + value)); \ + } while (0) #define xu(width, name, var, range_min, range_max, subs, ...) do { \ uint32_t value = var; \ CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ @@ -447,9 +462,14 @@ static int cbs_h2645_read_more_rbsp_data(GetBitContext *gbc) #include "cbs_h265_syntax_template.c" #undef FUNC +#define FUNC(name) FUNC_H266(name) +#include "cbs_h266_syntax_template.c" +#undef FUNC + #undef WRITE #undef READWRITE #undef RWContext +#undef ub #undef xu #undef xi #undef xue @@ -476,8 +496,9 @@ static int cbs_h2645_fragment_add_nals(CodedBitstreamContext *ctx, const H2645NAL *nal = &packet->nals[i]; AVBufferRef *ref; size_t size = nal->size; + enum AVCodecID codec_id = ctx->codec->codec_id; - if (nal->nuh_layer_id > 0) + if (codec_id != AV_CODEC_ID_VVC && nal->nuh_layer_id > 0) continue; // Remove trailing zeroes. @@ -640,6 +661,75 @@ static int cbs_h2645_split_fragment(CodedBitstreamContext *ctx, return err; } + } else if(header && frag->data[0] && codec_id == AV_CODEC_ID_VVC) { + // VVCC header. + int ptl_present_flag, num_arrays; + int b, i, j; + + priv->mp4 = 1; + + bytestream2_init(&gbc, frag->data, frag->data_size); + + b = bytestream2_get_byte(&gbc); + priv->nal_length_size = ((b >> 1) & 3) + 1; + ptl_present_flag = b & 1; + + if(ptl_present_flag) { + int num_sublayers, num_bytes_constraint_info, num_sub_profiles; + num_sublayers = (bytestream2_get_be16u(&gbc) >> 4) & 7; + bytestream2_skip(&gbc, 1); + + // begin VvcPTLRecord(num_sublayers); + num_bytes_constraint_info = bytestream2_get_byte(&gbc) & 0x3f; + bytestream2_skip(&gbc, 2 + num_bytes_constraint_info); + if(num_sublayers > 1) { + int count_present_flags = 0; + b = bytestream2_get_byte(&gbc); + for(i = num_sublayers - 2; i >= 0; i--) { + if((b >> (7 - (num_sublayers - 2 - i))) & 0x01) + count_present_flags++; + } + bytestream2_skip(&gbc, count_present_flags); + } + num_sub_profiles = bytestream2_get_byte(&gbc); + bytestream2_skip(&gbc, num_sub_profiles * 4); + // end VvcPTLRecord(num_sublayers); + + bytestream2_skip(&gbc, 3 * 2); + } + + num_arrays = bytestream2_get_byte(&gbc); + for(j = 0; j < num_arrays; j++) { + size_t start, end, size; + int nal_unit_type = bytestream2_get_byte(&gbc) & 0x1f; + unsigned int num_nalus = 1; + if(nal_unit_type != VVC_DCI_NUT && nal_unit_type != VVC_OPI_NUT) + num_nalus = bytestream2_get_be16(&gbc); + + start = bytestream2_tell(&gbc); + for(i = 0; i < num_nalus; i++) { + if (bytestream2_get_bytes_left(&gbc) < 2) + return AVERROR_INVALIDDATA; + size = bytestream2_get_be16(&gbc); + if (bytestream2_get_bytes_left(&gbc) < size) + return AVERROR_INVALIDDATA; + bytestream2_skip(&gbc, size); + } + end = bytestream2_tell(&gbc); + + err = ff_h2645_packet_split(&priv->read_packet, + frag->data + start, end - start, + ctx->log_ctx, 1, 2, AV_CODEC_ID_VVC, 1, 1); + if (err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to split " + "VVCC array %d (%d NAL units of type %d).\n", + i, num_nalus, nal_unit_type); + return err; + } + err = cbs_h2645_fragment_add_nals(ctx, frag, &priv->read_packet); + if (err < 0) + return err; + } } else { // Annex B, or later MP4 with already-known parameters. @@ -671,12 +761,8 @@ static int cbs_h26 ## h26n ## _replace_ ## ps_var(CodedBitstreamContext *ctx, \ return err; \ if (priv->ps_var[id] == priv->active_ ## ps_var) \ priv->active_ ## ps_var = NULL ; \ - av_buffer_unref(&priv->ps_var ## _ref[id]); \ av_assert0(unit->content_ref); \ - priv->ps_var ## _ref[id] = av_buffer_ref(unit->content_ref); \ - if (!priv->ps_var ## _ref[id]) \ - return AVERROR(ENOMEM); \ - priv->ps_var[id] = (H26 ## h26n ## Raw ## ps_name *)priv->ps_var ## _ref[id]->data; \ + ff_refstruct_replace(&priv->ps_var[id], unit->content_ref); \ return 0; \ } @@ -686,6 +772,41 @@ cbs_h2645_replace_ps(5, VPS, vps, vps_video_parameter_set_id) cbs_h2645_replace_ps(5, SPS, sps, sps_seq_parameter_set_id) cbs_h2645_replace_ps(5, PPS, pps, pps_pic_parameter_set_id) +#define cbs_h266_replace_ps(h26n, ps_name, ps_var, id_element) \ +static int cbs_h26 ## h26n ## _replace_ ## ps_var(CodedBitstreamContext *ctx, \ + CodedBitstreamUnit *unit) \ +{ \ + CodedBitstreamH26 ## h26n ## Context *priv = ctx->priv_data; \ + H26 ## h26n ## Raw ## ps_name *ps_var = unit->content; \ + unsigned int id = ps_var->id_element; \ + int err = ff_cbs_make_unit_refcounted(ctx, unit); \ + if (err < 0) \ + return err; \ + av_assert0(unit->content_ref); \ + ff_refstruct_replace(&priv->ps_var[id], unit->content_ref); \ + return 0; \ +} + +cbs_h266_replace_ps(6, VPS, vps, vps_video_parameter_set_id) +cbs_h266_replace_ps(6, SPS, sps, sps_seq_parameter_set_id) +cbs_h266_replace_ps(6, PPS, pps, pps_pic_parameter_set_id) + +static int cbs_h266_replace_ph(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + H266RawPictureHeader *ph) +{ + CodedBitstreamH266Context *h266 = ctx->priv_data; + int err; + + err = ff_cbs_make_unit_refcounted(ctx, unit); + if (err < 0) + return err; + av_assert0(unit->content_ref); + ff_refstruct_replace(&h266->ph_ref, unit->content_ref); + h266->ph = ph; + return 0; +} + static int cbs_h264_read_nal_unit(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit) { @@ -926,6 +1047,163 @@ static int cbs_h265_read_nal_unit(CodedBitstreamContext *ctx, return 0; } +static int cbs_h266_read_nal_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) +{ + GetBitContext gbc; + int err; + + err = init_get_bits8(&gbc, unit->data, unit->data_size); + if (err < 0) + return err; + + err = ff_cbs_alloc_unit_content(ctx, unit); + if (err < 0) + return err; + + switch (unit->type) { + case VVC_DCI_NUT: + { + err = cbs_h266_read_dci(ctx, &gbc, unit->content); + + if (err < 0) + return err; + } + break; + case VVC_OPI_NUT: + { + err = cbs_h266_read_opi(ctx, &gbc, unit->content); + + if (err < 0) + return err; + } + break; + case VVC_VPS_NUT: + { + H266RawVPS *vps = unit->content; + + err = cbs_h266_read_vps(ctx, &gbc, vps); + if (err < 0) + return err; + + err = cbs_h266_replace_vps(ctx, unit); + if (err < 0) + return err; + } + break; + case VVC_SPS_NUT: + { + H266RawSPS *sps = unit->content; + + err = cbs_h266_read_sps(ctx, &gbc, sps); + if (err < 0) + return err; + + err = cbs_h266_replace_sps(ctx, unit); + if (err < 0) + return err; + } + break; + + case VVC_PPS_NUT: + { + H266RawPPS *pps = unit->content; + + err = cbs_h266_read_pps(ctx, &gbc, pps); + if (err < 0) + return err; + + err = cbs_h266_replace_pps(ctx, unit); + if (err < 0) + return err; + } + break; + + case VVC_PREFIX_APS_NUT: + case VVC_SUFFIX_APS_NUT: + { + err = cbs_h266_read_aps(ctx, &gbc, unit->content, + unit->type == VVC_PREFIX_APS_NUT); + + if (err < 0) + return err; + } + break; + case VVC_PH_NUT: + { + H266RawPH *ph = unit->content; + err = cbs_h266_read_ph(ctx, &gbc, ph); + if (err < 0) + return err; + err = cbs_h266_replace_ph(ctx, unit, &ph->ph_picture_header); + if (err < 0) + return err; + } + break; + + case VVC_TRAIL_NUT: + case VVC_STSA_NUT: + case VVC_RADL_NUT: + case VVC_RASL_NUT: + case VVC_IDR_W_RADL: + case VVC_IDR_N_LP: + case VVC_CRA_NUT: + case VVC_GDR_NUT: + { + H266RawSlice *slice = unit->content; + int pos, len; + + err = cbs_h266_read_slice_header(ctx, &gbc, &slice->header); + if (err < 0) + return err; + + if (!cbs_h2645_read_more_rbsp_data(&gbc)) + return AVERROR_INVALIDDATA; + + pos = get_bits_count(&gbc); + len = unit->data_size; + + if (slice->header.sh_picture_header_in_slice_header_flag) { + err = cbs_h266_replace_ph(ctx, unit, &slice->header.sh_picture_header); + if (err < 0) + return err; + } + + slice->header_size = pos / 8; + slice->data_size = len - pos / 8; + slice->data_ref = av_buffer_ref(unit->data_ref); + if (!slice->data_ref) + return AVERROR(ENOMEM); + slice->data = unit->data + pos / 8; + slice->data_bit_start = pos % 8; + } + break; + + case VVC_AUD_NUT: + { + err = cbs_h266_read_aud(ctx, &gbc, unit->content); + if (err < 0) + return err; + } + break; + + case VVC_PREFIX_SEI_NUT: + case VVC_SUFFIX_SEI_NUT: + { + err = cbs_h266_read_sei(ctx, &gbc, unit->content, + unit->type == VVC_PREFIX_SEI_NUT); + + if (err < 0) + return err; + } + break; + + default: + return AVERROR(ENOSYS); + } + return 0; +} + static int cbs_h2645_write_slice_data(CodedBitstreamContext *ctx, PutBitContext *pbc, const uint8_t *data, size_t data_size, int data_bit_start) @@ -1213,11 +1491,285 @@ static int cbs_h265_write_nal_unit(CodedBitstreamContext *ctx, return 0; } +static int cbs_h264_discarded_nal_unit(CodedBitstreamContext *ctx, + const CodedBitstreamUnit *unit, + enum AVDiscard skip) +{ + H264RawNALUnitHeader *header; + H264RawSliceHeader *slice; + int slice_type_i, slice_type_b, slice_type_si; + + if (skip <= AVDISCARD_DEFAULT) + return 0; + + // keep non-VCL + if (unit->type != H264_NAL_SLICE && + unit->type != H264_NAL_IDR_SLICE && + unit->type != H264_NAL_AUXILIARY_SLICE) + return 0; + + if (skip >= AVDISCARD_ALL) + return 1; + + if (skip >= AVDISCARD_NONKEY && unit->type != H264_NAL_IDR_SLICE) + return 1; + + header = (H264RawNALUnitHeader *)unit->content; + if (!header) { + av_log(ctx->log_ctx, AV_LOG_WARNING, + "h264 nal unit header is null, missing decompose?\n"); + return 0; + } + + if (skip >= AVDISCARD_NONREF && !header->nal_ref_idc) + return 1; + + slice = (H264RawSliceHeader *)unit->content; + if (!slice) { + av_log(ctx->log_ctx, AV_LOG_WARNING, + "h264 slice header is null, missing decompose?\n"); + return 0; + } + + slice_type_i = slice->slice_type % 5 == 2; + slice_type_b = slice->slice_type % 5 == 1; + slice_type_si = slice->slice_type % 5 == 4; + + if (skip >= AVDISCARD_BIDIR && slice_type_b) + return 1; + if (skip >= AVDISCARD_NONINTRA && !slice_type_i && !slice_type_si) + return 1; + + return 0; +} + +static int cbs_h265_discarded_nal_unit(CodedBitstreamContext *ctx, + const CodedBitstreamUnit *unit, + enum AVDiscard skip) +{ + H265RawSliceHeader *slice; + + if (skip <= AVDISCARD_DEFAULT) + return 0; + + switch (unit->type) { + case HEVC_NAL_BLA_W_LP: + case HEVC_NAL_BLA_W_RADL: + case HEVC_NAL_BLA_N_LP: + case HEVC_NAL_IDR_W_RADL: + case HEVC_NAL_IDR_N_LP: + case HEVC_NAL_CRA_NUT: + // IRAP slice + if (skip < AVDISCARD_ALL) + return 0; + break; + + case HEVC_NAL_TRAIL_R: + case HEVC_NAL_TRAIL_N: + case HEVC_NAL_TSA_N: + case HEVC_NAL_TSA_R: + case HEVC_NAL_STSA_N: + case HEVC_NAL_STSA_R: + case HEVC_NAL_RADL_N: + case HEVC_NAL_RADL_R: + case HEVC_NAL_RASL_N: + case HEVC_NAL_RASL_R: + // Slice + break; + default: + // Don't discard non-slice nal. + return 0; + } + + if (skip >= AVDISCARD_NONKEY) + return 1; + + slice = (H265RawSliceHeader *)unit->content; + if (!slice) { + av_log(ctx->log_ctx, AV_LOG_WARNING, + "h265 slice header is null, missing decompose?\n"); + return 0; + } + + if (skip >= AVDISCARD_NONINTRA && slice->slice_type != HEVC_SLICE_I) + return 1; + if (skip >= AVDISCARD_BIDIR && slice->slice_type == HEVC_SLICE_B) + return 1; + + if (skip >= AVDISCARD_NONREF) { + switch (unit->type) { + case HEVC_NAL_TRAIL_N: + case HEVC_NAL_TSA_N: + case HEVC_NAL_STSA_N: + case HEVC_NAL_RADL_N: + case HEVC_NAL_RASL_N: + case HEVC_NAL_VCL_N10: + case HEVC_NAL_VCL_N12: + case HEVC_NAL_VCL_N14: + // non-ref + return 1; + default: + break; + } + } + + return 0; +} + +static int cbs_h266_write_nal_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) +{ + int err; + + switch (unit->type) { + case VVC_DCI_NUT: + { + H266RawDCI *dci = unit->content; + + err = cbs_h266_write_dci(ctx, pbc, dci); + if (err < 0) + return err; + } + break; + case VVC_OPI_NUT: + { + H266RawOPI *opi = unit->content; + + err = cbs_h266_write_opi(ctx, pbc, opi); + if (err < 0) + return err; + } + break; + case VVC_VPS_NUT: + { + H266RawVPS *vps = unit->content; + + err = cbs_h266_write_vps(ctx, pbc, vps); + if (err < 0) + return err; + + err = cbs_h266_replace_vps(ctx, unit); + if (err < 0) + return err; + } + break; + case VVC_SPS_NUT: + { + H266RawSPS *sps = unit->content; + + err = cbs_h266_write_sps(ctx, pbc, sps); + if (err < 0) + return err; + + err = cbs_h266_replace_sps(ctx, unit); + if (err < 0) + return err; + } + break; + + case VVC_PPS_NUT: + { + H266RawPPS *pps = unit->content; + + err = cbs_h266_write_pps(ctx, pbc, pps); + if (err < 0) + return err; + + err = cbs_h266_replace_pps(ctx, unit); + if (err < 0) + return err; + } + break; + + case VVC_PREFIX_APS_NUT: + case VVC_SUFFIX_APS_NUT: + { + err = cbs_h266_write_aps(ctx, pbc, unit->content, + unit->type == VVC_PREFIX_APS_NUT); + if (err < 0) + return err; + } + break; + case VVC_PH_NUT: + { + H266RawPH *ph = unit->content; + err = cbs_h266_write_ph(ctx, pbc, ph); + if (err < 0) + return err; + + err = cbs_h266_replace_ph(ctx, unit, &ph->ph_picture_header); + if (err < 0) + return err; + } + break; + + case VVC_TRAIL_NUT: + case VVC_STSA_NUT: + case VVC_RADL_NUT: + case VVC_RASL_NUT: + case VVC_IDR_W_RADL: + case VVC_IDR_N_LP: + case VVC_CRA_NUT: + case VVC_GDR_NUT: + { + H266RawSlice *slice = unit->content; + + err = cbs_h266_write_slice_header(ctx, pbc, &slice->header); + if (err < 0) + return err; + + if (slice->header.sh_picture_header_in_slice_header_flag) { + err = cbs_h266_replace_ph(ctx, unit, &slice->header.sh_picture_header); + if (err < 0) + return err; + } + + if (slice->data) { + err = cbs_h2645_write_slice_data(ctx, pbc, slice->data, + slice->data_size, + slice->data_bit_start); + if (err < 0) + return err; + } else { + // No slice data - that was just the header. + } + } + break; + + case VVC_AUD_NUT: + { + err = cbs_h266_write_aud(ctx, pbc, unit->content); + if (err < 0) + return err; + } + break; + + case VVC_PREFIX_SEI_NUT: + case VVC_SUFFIX_SEI_NUT: + { + err = cbs_h266_write_sei(ctx, pbc, unit->content, + unit->type == VVC_PREFIX_SEI_NUT); + + if (err < 0) + return err; + } + break; + + default: + av_log(ctx->log_ctx, AV_LOG_ERROR, "Write unimplemented for " + "NAL unit type %"PRIu32".\n", unit->type); + return AVERROR_PATCHWELCOME; + } + + return 0; +} + static int cbs_h2645_unit_requires_zero_byte(enum AVCodecID codec_id, CodedBitstreamUnitType type, int nal_unit_index) { - // Section B.1.2 in H.264, section B.2.2 in H.265. + // Section B.1.2 in H.264, section B.2.2 in H.265, H.266. if (nal_unit_index == 0) { // Assume that this is the first NAL unit in an access unit. return 1; @@ -1226,6 +1778,8 @@ static int cbs_h2645_unit_requires_zero_byte(enum AVCodecID codec_id, return type == H264_NAL_SPS || type == H264_NAL_PPS; if (codec_id == AV_CODEC_ID_HEVC) return type == HEVC_NAL_VPS || type == HEVC_NAL_SPS || type == HEVC_NAL_PPS; + if (codec_id == AV_CODEC_ID_VVC) + return type >= VVC_OPI_NUT && type <= VVC_SUFFIX_APS_NUT; return 0; } @@ -1313,14 +1867,10 @@ static void cbs_h264_flush(CodedBitstreamContext *ctx) { CodedBitstreamH264Context *h264 = ctx->priv_data; - for (int i = 0; i < FF_ARRAY_ELEMS(h264->sps); i++) { - av_buffer_unref(&h264->sps_ref[i]); - h264->sps[i] = NULL; - } - for (int i = 0; i < FF_ARRAY_ELEMS(h264->pps); i++) { - av_buffer_unref(&h264->pps_ref[i]); - h264->pps[i] = NULL; - } + for (int i = 0; i < FF_ARRAY_ELEMS(h264->sps); i++) + ff_refstruct_unref(&h264->sps[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(h264->pps); i++) + ff_refstruct_unref(&h264->pps[i]); h264->active_sps = NULL; h264->active_pps = NULL; @@ -1335,27 +1885,21 @@ static void cbs_h264_close(CodedBitstreamContext *ctx) ff_h2645_packet_uninit(&h264->common.read_packet); for (i = 0; i < FF_ARRAY_ELEMS(h264->sps); i++) - av_buffer_unref(&h264->sps_ref[i]); + ff_refstruct_unref(&h264->sps[i]); for (i = 0; i < FF_ARRAY_ELEMS(h264->pps); i++) - av_buffer_unref(&h264->pps_ref[i]); + ff_refstruct_unref(&h264->pps[i]); } static void cbs_h265_flush(CodedBitstreamContext *ctx) { CodedBitstreamH265Context *h265 = ctx->priv_data; - for (int i = 0; i < FF_ARRAY_ELEMS(h265->vps); i++) { - av_buffer_unref(&h265->vps_ref[i]); - h265->vps[i] = NULL; - } - for (int i = 0; i < FF_ARRAY_ELEMS(h265->sps); i++) { - av_buffer_unref(&h265->sps_ref[i]); - h265->sps[i] = NULL; - } - for (int i = 0; i < FF_ARRAY_ELEMS(h265->pps); i++) { - av_buffer_unref(&h265->pps_ref[i]); - h265->pps[i] = NULL; - } + for (int i = 0; i < FF_ARRAY_ELEMS(h265->vps); i++) + ff_refstruct_unref(&h265->vps[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(h265->sps); i++) + ff_refstruct_unref(&h265->sps[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(h265->pps); i++) + ff_refstruct_unref(&h265->pps[i]); h265->active_vps = NULL; h265->active_sps = NULL; @@ -1370,18 +1914,38 @@ static void cbs_h265_close(CodedBitstreamContext *ctx) ff_h2645_packet_uninit(&h265->common.read_packet); for (i = 0; i < FF_ARRAY_ELEMS(h265->vps); i++) - av_buffer_unref(&h265->vps_ref[i]); + ff_refstruct_unref(&h265->vps[i]); for (i = 0; i < FF_ARRAY_ELEMS(h265->sps); i++) - av_buffer_unref(&h265->sps_ref[i]); + ff_refstruct_unref(&h265->sps[i]); for (i = 0; i < FF_ARRAY_ELEMS(h265->pps); i++) - av_buffer_unref(&h265->pps_ref[i]); + ff_refstruct_unref(&h265->pps[i]); } -static void cbs_h264_free_sei(void *opaque, uint8_t *content) +static void cbs_h266_flush(CodedBitstreamContext *ctx) { - H264RawSEI *sei = (H264RawSEI*)content; + CodedBitstreamH266Context *h266 = ctx->priv_data; + + for (int i = 0; i < FF_ARRAY_ELEMS(h266->vps); i++) + ff_refstruct_unref(&h266->vps[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(h266->sps); i++) + ff_refstruct_unref(&h266->sps[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(h266->pps); i++) + ff_refstruct_unref(&h266->pps[i]); + ff_refstruct_unref(&h266->ph_ref); +} + +static void cbs_h266_close(CodedBitstreamContext *ctx) +{ + CodedBitstreamH266Context *h266 = ctx->priv_data; + + cbs_h266_flush(ctx); + ff_h2645_packet_uninit(&h266->common.read_packet); + } + +static void cbs_h264_free_sei(FFRefStructOpaque unused, void *content) +{ + H264RawSEI *sei = content; ff_cbs_sei_free_message_list(&sei->message_list); - av_free(content); } static const CodedBitstreamUnitTypeDescriptor cbs_h264_unit_types[] = { @@ -1404,11 +1968,10 @@ static const CodedBitstreamUnitTypeDescriptor cbs_h264_unit_types[] = { CBS_UNIT_TYPE_END_OF_LIST }; -static void cbs_h265_free_sei(void *opaque, uint8_t *content) +static void cbs_h265_free_sei(FFRefStructOpaque unused, void *content) { - H265RawSEI *sei = (H265RawSEI*)content; + H265RawSEI *sei = content; ff_cbs_sei_free_message_list(&sei->message_list); - av_free(content); } static const CodedBitstreamUnitTypeDescriptor cbs_h265_unit_types[] = { @@ -1431,6 +1994,46 @@ static const CodedBitstreamUnitTypeDescriptor cbs_h265_unit_types[] = { CBS_UNIT_TYPE_END_OF_LIST }; +static void cbs_h266_free_sei(FFRefStructOpaque unused, void *content) +{ + H266RawSEI *sei = content; + ff_cbs_sei_free_message_list(&sei->message_list); +} + +static const CodedBitstreamUnitTypeDescriptor cbs_h266_unit_types[] = { + CBS_UNIT_TYPE_INTERNAL_REF(VVC_DCI_NUT, H266RawDCI, extension_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(VVC_OPI_NUT, H266RawOPI, extension_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(VVC_VPS_NUT, H266RawVPS, extension_data.data), + { + .nb_unit_types = 1, + .unit_type.list[0] = VVC_SPS_NUT, + .content_type = CBS_CONTENT_TYPE_INTERNAL_REFS, + .content_size = sizeof(H266RawSPS), + .type.ref = { + .nb_offsets = 2, + .offsets = { offsetof(H266RawSPS, extension_data.data), + offsetof(H266RawSPS, vui.extension_data.data) } + }, + }, + CBS_UNIT_TYPE_INTERNAL_REF(VVC_PPS_NUT, H266RawPPS, extension_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(VVC_PREFIX_APS_NUT, H266RawAPS, extension_data.data), + CBS_UNIT_TYPE_INTERNAL_REF(VVC_SUFFIX_APS_NUT, H266RawAPS, extension_data.data), + + CBS_UNIT_TYPE_POD(VVC_PH_NUT , H266RawPH), + CBS_UNIT_TYPE_POD(VVC_AUD_NUT, H266RawAUD), + + CBS_UNIT_RANGE_INTERNAL_REF(VVC_TRAIL_NUT, VVC_RASL_NUT, + H266RawSlice, data), + + CBS_UNIT_RANGE_INTERNAL_REF(VVC_IDR_W_RADL, VVC_GDR_NUT, + H266RawSlice, data), + + CBS_UNIT_TYPES_COMPLEX((VVC_PREFIX_SEI_NUT, VVC_SUFFIX_SEI_NUT), + H266RawSEI, cbs_h266_free_sei), + + CBS_UNIT_TYPE_END_OF_LIST +}; + const CodedBitstreamType ff_cbs_type_h264 = { .codec_id = AV_CODEC_ID_H264, @@ -1441,6 +2044,7 @@ const CodedBitstreamType ff_cbs_type_h264 = { .split_fragment = &cbs_h2645_split_fragment, .read_unit = &cbs_h264_read_nal_unit, .write_unit = &cbs_h264_write_nal_unit, + .discarded_unit = &cbs_h264_discarded_nal_unit, .assemble_fragment = &cbs_h2645_assemble_fragment, .flush = &cbs_h264_flush, @@ -1457,12 +2061,34 @@ const CodedBitstreamType ff_cbs_type_h265 = { .split_fragment = &cbs_h2645_split_fragment, .read_unit = &cbs_h265_read_nal_unit, .write_unit = &cbs_h265_write_nal_unit, + .discarded_unit = &cbs_h265_discarded_nal_unit, .assemble_fragment = &cbs_h2645_assemble_fragment, .flush = &cbs_h265_flush, .close = &cbs_h265_close, }; +const CodedBitstreamType ff_cbs_type_h266 = { + .codec_id = AV_CODEC_ID_VVC, + + .priv_data_size = sizeof(CodedBitstreamH266Context), + + .unit_types = cbs_h266_unit_types, + + .split_fragment = &cbs_h2645_split_fragment, + .read_unit = &cbs_h266_read_nal_unit, + .write_unit = &cbs_h266_write_nal_unit, + .assemble_fragment = &cbs_h2645_assemble_fragment, + + .flush = &cbs_h266_flush, + .close = &cbs_h266_close, +}; + +// Macro for the read/write pair. +#define SEI_MESSAGE_RW(codec, name) \ + .read = cbs_ ## codec ## _read_ ## name ## _internal, \ + .write = cbs_ ## codec ## _write_ ## name ## _internal + static const SEIMessageTypeDescriptor cbs_sei_common_types[] = { { SEI_TYPE_FILLER_PAYLOAD, @@ -1613,6 +2239,16 @@ static const SEIMessageTypeDescriptor cbs_sei_h265_types[] = { SEI_MESSAGE_TYPE_END }; +static const SEIMessageTypeDescriptor cbs_sei_h266_types[] = { + { + SEI_TYPE_DECODED_PICTURE_HASH, + 0, 1, + sizeof(H266RawSEIDecodedPictureHash), + SEI_MESSAGE_RW(h266, sei_decoded_picture_hash), + }, + SEI_MESSAGE_TYPE_END +}; + const SEIMessageTypeDescriptor *ff_cbs_sei_find_type(CodedBitstreamContext *ctx, int payload_type) { @@ -1631,6 +2267,9 @@ const SEIMessageTypeDescriptor *ff_cbs_sei_find_type(CodedBitstreamContext *ctx, case AV_CODEC_ID_H265: codec_list = cbs_sei_h265_types; break; + case AV_CODEC_ID_H266: + codec_list = cbs_sei_h266_types; + break; default: return NULL; } diff --git a/libavcodec/cbs_h264_syntax_template.c b/libavcodec/cbs_h264_syntax_template.c index 0f8bba4a0da..4d2d303722a 100644 --- a/libavcodec/cbs_h264_syntax_template.c +++ b/libavcodec/cbs_h264_syntax_template.c @@ -510,9 +510,9 @@ static int FUNC(pps)(CodedBitstreamContext *ctx, RWContext *rw, return 0; } -static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw, - H264RawSEIBufferingPeriod *current, - SEIMessageState *sei) +SEI_FUNC(sei_buffering_period, (CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIBufferingPeriod *current, + SEIMessageState *sei)) { CodedBitstreamH264Context *h264 = ctx->priv_data; const H264RawSPS *sps; @@ -604,9 +604,8 @@ static int FUNC(sei_pic_timestamp)(CodedBitstreamContext *ctx, RWContext *rw, return 0; } -static int FUNC(sei_pic_timing)(CodedBitstreamContext *ctx, RWContext *rw, - H264RawSEIPicTiming *current, - SEIMessageState *sei) +SEI_FUNC(sei_pic_timing, (CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIPicTiming *current, SEIMessageState *sei)) { CodedBitstreamH264Context *h264 = ctx->priv_data; const H264RawSPS *sps; @@ -676,9 +675,9 @@ static int FUNC(sei_pic_timing)(CodedBitstreamContext *ctx, RWContext *rw, return 0; } -static int FUNC(sei_pan_scan_rect)(CodedBitstreamContext *ctx, RWContext *rw, - H264RawSEIPanScanRect *current, - SEIMessageState *sei) +SEI_FUNC(sei_pan_scan_rect, (CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIPanScanRect *current, + SEIMessageState *sei)) { int err, i; @@ -703,9 +702,9 @@ static int FUNC(sei_pan_scan_rect)(CodedBitstreamContext *ctx, RWContext *rw, return 0; } -static int FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext *rw, - H264RawSEIRecoveryPoint *current, - SEIMessageState *sei) +SEI_FUNC(sei_recovery_point, (CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIRecoveryPoint *current, + SEIMessageState *sei)) { int err; @@ -719,9 +718,9 @@ static int FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext *rw, return 0; } -static int FUNC(film_grain_characteristics)(CodedBitstreamContext *ctx, RWContext *rw, - H264RawFilmGrainCharacteristics *current, - SEIMessageState *state) +SEI_FUNC(film_grain_characteristics, (CodedBitstreamContext *ctx, RWContext *rw, + H264RawFilmGrainCharacteristics *current, + SEIMessageState *state)) { CodedBitstreamH264Context *h264 = ctx->priv_data; const H264RawSPS *sps; @@ -802,9 +801,9 @@ static int FUNC(film_grain_characteristics)(CodedBitstreamContext *ctx, RWContex return 0; } -static int FUNC(sei_display_orientation)(CodedBitstreamContext *ctx, RWContext *rw, - H264RawSEIDisplayOrientation *current, - SEIMessageState *sei) +SEI_FUNC(sei_display_orientation, (CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIDisplayOrientation *current, + SEIMessageState *sei)) { int err; diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h index f7cbd4970d0..1b1195f198e 100644 --- a/libavcodec/cbs_h265.h +++ b/libavcodec/cbs_h265.h @@ -681,12 +681,9 @@ typedef struct CodedBitstreamH265Context { // All currently available parameter sets. These are updated when // any parameter set NAL unit is read/written with this context. - AVBufferRef *vps_ref[HEVC_MAX_VPS_COUNT]; - AVBufferRef *sps_ref[HEVC_MAX_SPS_COUNT]; - AVBufferRef *pps_ref[HEVC_MAX_PPS_COUNT]; - H265RawVPS *vps[HEVC_MAX_VPS_COUNT]; - H265RawSPS *sps[HEVC_MAX_SPS_COUNT]; - H265RawPPS *pps[HEVC_MAX_PPS_COUNT]; + H265RawVPS *vps[HEVC_MAX_VPS_COUNT]; ///< RefStruct references + H265RawSPS *sps[HEVC_MAX_SPS_COUNT]; ///< RefStruct references + H265RawPPS *pps[HEVC_MAX_PPS_COUNT]; ///< RefStruct references // The currently active parameter sets. These are updated when any // NAL unit refers to the relevant parameter set. These pointers diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c index 2d4b9547185..86ca00a0c99 100644 --- a/libavcodec/cbs_h265_syntax_template.c +++ b/libavcodec/cbs_h265_syntax_template.c @@ -1618,9 +1618,9 @@ static int FUNC(slice_segment_header)(CodedBitstreamContext *ctx, RWContext *rw, return 0; } -static int FUNC(sei_buffering_period) - (CodedBitstreamContext *ctx, RWContext *rw, - H265RawSEIBufferingPeriod *current, SEIMessageState *sei) +SEI_FUNC(sei_buffering_period, (CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIBufferingPeriod *current, + SEIMessageState *sei)) { CodedBitstreamH265Context *h265 = ctx->priv_data; const H265RawSPS *sps; @@ -1728,9 +1728,8 @@ static int FUNC(sei_buffering_period) return 0; } -static int FUNC(sei_pic_timing) - (CodedBitstreamContext *ctx, RWContext *rw, - H265RawSEIPicTiming *current, SEIMessageState *sei) +SEI_FUNC(sei_pic_timing, (CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIPicTiming *current, SEIMessageState *sei)) { CodedBitstreamH265Context *h265 = ctx->priv_data; const H265RawSPS *sps; @@ -1804,9 +1803,9 @@ static int FUNC(sei_pic_timing) return 0; } -static int FUNC(sei_pan_scan_rect) - (CodedBitstreamContext *ctx, RWContext *rw, - H265RawSEIPanScanRect *current, SEIMessageState *sei) +SEI_FUNC(sei_pan_scan_rect, (CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIPanScanRect *current, + SEIMessageState *sei)) { int err, i; @@ -1831,9 +1830,9 @@ static int FUNC(sei_pan_scan_rect) return 0; } -static int FUNC(sei_recovery_point) - (CodedBitstreamContext *ctx, RWContext *rw, - H265RawSEIRecoveryPoint *current, SEIMessageState *sei) +SEI_FUNC(sei_recovery_point, (CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIRecoveryPoint *current, + SEIMessageState *sei)) { int err; @@ -1847,9 +1846,9 @@ static int FUNC(sei_recovery_point) return 0; } -static int FUNC(film_grain_characteristics)(CodedBitstreamContext *ctx, RWContext *rw, - H265RawFilmGrainCharacteristics *current, - SEIMessageState *state) +SEI_FUNC(film_grain_characteristics, (CodedBitstreamContext *ctx, RWContext *rw, + H265RawFilmGrainCharacteristics *current, + SEIMessageState *state)) { CodedBitstreamH265Context *h265 = ctx->priv_data; const H265RawSPS *sps = h265->active_sps; @@ -1912,9 +1911,9 @@ static int FUNC(film_grain_characteristics)(CodedBitstreamContext *ctx, RWContex return 0; } -static int FUNC(sei_display_orientation) - (CodedBitstreamContext *ctx, RWContext *rw, - H265RawSEIDisplayOrientation *current, SEIMessageState *sei) +SEI_FUNC(sei_display_orientation, (CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIDisplayOrientation *current, + SEIMessageState *sei)) { int err; @@ -1931,9 +1930,9 @@ static int FUNC(sei_display_orientation) return 0; } -static int FUNC(sei_active_parameter_sets) - (CodedBitstreamContext *ctx, RWContext *rw, - H265RawSEIActiveParameterSets *current, SEIMessageState *sei) +SEI_FUNC(sei_active_parameter_sets, (CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIActiveParameterSets *current, + SEIMessageState *sei)) { CodedBitstreamH265Context *h265 = ctx->priv_data; const H265RawVPS *vps; @@ -1968,9 +1967,9 @@ static int FUNC(sei_active_parameter_sets) return 0; } -static int FUNC(sei_decoded_picture_hash) - (CodedBitstreamContext *ctx, RWContext *rw, - H265RawSEIDecodedPictureHash *current, SEIMessageState *sei) +SEI_FUNC(sei_decoded_picture_hash, (CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIDecodedPictureHash *current, + SEIMessageState *sei)) { CodedBitstreamH265Context *h265 = ctx->priv_data; const H265RawSPS *sps = h265->active_sps; @@ -2000,9 +1999,8 @@ static int FUNC(sei_decoded_picture_hash) return 0; } -static int FUNC(sei_time_code) - (CodedBitstreamContext *ctx, RWContext *rw, - H265RawSEITimeCode *current, SEIMessageState *sei) +SEI_FUNC(sei_time_code, (CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEITimeCode *current, SEIMessageState *sei)) { int err, i; @@ -2051,9 +2049,9 @@ static int FUNC(sei_time_code) return 0; } -static int FUNC(sei_alpha_channel_info) - (CodedBitstreamContext *ctx, RWContext *rw, - H265RawSEIAlphaChannelInfo *current, SEIMessageState *sei) +SEI_FUNC(sei_alpha_channel_info, (CodedBitstreamContext *ctx, RWContext *rw, + H265RawSEIAlphaChannelInfo *current, + SEIMessageState *sei)) { int err, length; diff --git a/libavcodec/cbs_h266.h b/libavcodec/cbs_h266.h new file mode 100644 index 00000000000..73d94157d46 --- /dev/null +++ b/libavcodec/cbs_h266.h @@ -0,0 +1,879 @@ +/* + * 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 + */ + +#ifndef AVCODEC_CBS_H266_H +#define AVCODEC_CBS_H266_H + +#include +#include + +#include "cbs_h2645.h" +#include "cbs_sei.h" +#include "vvc.h" + +typedef struct H266RawNALUnitHeader { + uint8_t nuh_layer_id; + uint8_t nal_unit_type; + uint8_t nuh_temporal_id_plus1; + uint8_t nuh_reserved_zero_bit; +} H266RawNALUnitHeader; + +typedef struct H266GeneralConstraintsInfo { + uint8_t gci_present_flag; + /* general */ + uint8_t gci_intra_only_constraint_flag; + uint8_t gci_all_layers_independent_constraint_flag; + uint8_t gci_one_au_only_constraint_flag; + + /* picture format */ + uint8_t gci_sixteen_minus_max_bitdepth_constraint_idc; + uint8_t gci_three_minus_max_chroma_format_constraint_idc; + + /* NAL unit type related */ + uint8_t gci_no_mixed_nalu_types_in_pic_constraint_flag; + uint8_t gci_no_trail_constraint_flag; + uint8_t gci_no_stsa_constraint_flag; + uint8_t gci_no_rasl_constraint_flag; + uint8_t gci_no_radl_constraint_flag; + uint8_t gci_no_idr_constraint_flag; + uint8_t gci_no_cra_constraint_flag; + uint8_t gci_no_gdr_constraint_flag; + uint8_t gci_no_aps_constraint_flag; + uint8_t gci_no_idr_rpl_constraint_flag; + + /* tile, slice, subpicture partitioning */ + uint8_t gci_one_tile_per_pic_constraint_flag; + uint8_t gci_pic_header_in_slice_header_constraint_flag; + uint8_t gci_one_slice_per_pic_constraint_flag; + uint8_t gci_no_rectangular_slice_constraint_flag; + uint8_t gci_one_slice_per_subpic_constraint_flag; + uint8_t gci_no_subpic_info_constraint_flag; + + /* CTU and block partitioning */ + uint8_t gci_three_minus_max_log2_ctu_size_constraint_idc; + uint8_t gci_no_partition_constraints_override_constraint_flag; + uint8_t gci_no_mtt_constraint_flag; + uint8_t gci_no_qtbtt_dual_tree_intra_constraint_flag; + + /* intra */ + uint8_t gci_no_palette_constraint_flag; + uint8_t gci_no_ibc_constraint_flag; + uint8_t gci_no_isp_constraint_flag; + uint8_t gci_no_mrl_constraint_flag; + uint8_t gci_no_mip_constraint_flag; + uint8_t gci_no_cclm_constraint_flag; + + /* inter */ + uint8_t gci_no_ref_pic_resampling_constraint_flag; + uint8_t gci_no_res_change_in_clvs_constraint_flag; + uint8_t gci_no_weighted_prediction_constraint_flag; + uint8_t gci_no_ref_wraparound_constraint_flag; + uint8_t gci_no_temporal_mvp_constraint_flag; + uint8_t gci_no_sbtmvp_constraint_flag; + uint8_t gci_no_amvr_constraint_flag; + uint8_t gci_no_bdof_constraint_flag; + uint8_t gci_no_smvd_constraint_flag; + uint8_t gci_no_dmvr_constraint_flag; + uint8_t gci_no_mmvd_constraint_flag; + uint8_t gci_no_affine_motion_constraint_flag; + uint8_t gci_no_prof_constraint_flag; + uint8_t gci_no_bcw_constraint_flag; + uint8_t gci_no_ciip_constraint_flag; + uint8_t gci_no_gpm_constraint_flag; + + /* transform, quantization, residual */ + uint8_t gci_no_luma_transform_size_64_constraint_flag; + uint8_t gci_no_transform_skip_constraint_flag; + uint8_t gci_no_bdpcm_constraint_flag; + uint8_t gci_no_mts_constraint_flag; + uint8_t gci_no_lfnst_constraint_flag; + uint8_t gci_no_joint_cbcr_constraint_flag; + uint8_t gci_no_sbt_constraint_flag; + uint8_t gci_no_act_constraint_flag; + uint8_t gci_no_explicit_scaling_list_constraint_flag; + uint8_t gci_no_dep_quant_constraint_flag; + uint8_t gci_no_sign_data_hiding_constraint_flag; + uint8_t gci_no_cu_qp_delta_constraint_flag; + uint8_t gci_no_chroma_qp_offset_constraint_flag; + + /* loop filter */ + uint8_t gci_no_sao_constraint_flag; + uint8_t gci_no_alf_constraint_flag; + uint8_t gci_no_ccalf_constraint_flag; + uint8_t gci_no_lmcs_constraint_flag; + uint8_t gci_no_ladf_constraint_flag; + uint8_t gci_no_virtual_boundaries_constraint_flag; + + uint8_t gci_num_additional_bits; + uint8_t gci_reserved_bit[255]; + + uint8_t gci_all_rap_pictures_constraint_flag; + uint8_t gci_no_extended_precision_processing_constraint_flag; + uint8_t gci_no_ts_residual_coding_rice_constraint_flag; + uint8_t gci_no_rrc_rice_extension_constraint_flag; + uint8_t gci_no_persistent_rice_adaptation_constraint_flag; + uint8_t gci_no_reverse_last_sig_coeff_constraint_flag; +} H266GeneralConstraintsInfo; + +typedef struct H266RawProfileTierLevel { + uint8_t general_profile_idc; + uint8_t general_tier_flag; + uint8_t general_level_idc; + uint8_t ptl_frame_only_constraint_flag; + uint8_t ptl_multilayer_enabled_flag; + H266GeneralConstraintsInfo general_constraints_info; + uint8_t ptl_sublayer_level_present_flag[VVC_MAX_SUBLAYERS - 1]; + uint8_t sublayer_level_idc[VVC_MAX_SUBLAYERS - 1]; + uint8_t ptl_num_sub_profiles; + uint32_t general_sub_profile_idc[VVC_MAX_SUB_PROFILES]; + + uint8_t ptl_reserved_zero_bit; +} H266RawProfileTierLevel; + +typedef struct H266RawExtensionData { + uint8_t *data; + AVBufferRef *data_ref; + size_t bit_length; +} H266RawExtensionData; + +typedef struct H266DpbParameters { + uint8_t dpb_max_dec_pic_buffering_minus1[VVC_MAX_SUBLAYERS]; + uint8_t dpb_max_num_reorder_pics[VVC_MAX_SUBLAYERS]; + uint8_t dpb_max_latency_increase_plus1[VVC_MAX_SUBLAYERS]; +} H266DpbParameters; + +typedef struct H266RefPicListStruct { + uint8_t num_ref_entries; + uint8_t ltrp_in_header_flag; + uint8_t inter_layer_ref_pic_flag[VVC_MAX_REF_ENTRIES]; + uint8_t st_ref_pic_flag[VVC_MAX_REF_ENTRIES]; + uint8_t abs_delta_poc_st[VVC_MAX_REF_ENTRIES]; + uint8_t strp_entry_sign_flag[VVC_MAX_REF_ENTRIES]; + uint8_t rpls_poc_lsb_lt[VVC_MAX_REF_ENTRIES]; + uint8_t ilrp_idx[VVC_MAX_REF_ENTRIES]; +} H266RefPicListStruct; + +typedef struct H266RefPicLists { + uint8_t rpl_sps_flag[2]; + uint8_t rpl_idx[2]; + H266RefPicListStruct rpl_ref_list[2]; + uint16_t poc_lsb_lt[2][VVC_MAX_REF_ENTRIES]; + uint8_t delta_poc_msb_cycle_present_flag[2][VVC_MAX_REF_ENTRIES]; + uint16_t delta_poc_msb_cycle_lt[2][VVC_MAX_REF_ENTRIES]; +} H266RefPicLists; + +typedef struct H266RawGeneralTimingHrdParameters { + uint32_t num_units_in_tick; + uint32_t time_scale; + uint8_t general_nal_hrd_params_present_flag; + uint8_t general_vcl_hrd_params_present_flag; + uint8_t general_same_pic_timing_in_all_ols_flag; + uint8_t general_du_hrd_params_present_flag; + uint8_t tick_divisor_minus2; + uint8_t bit_rate_scale; + uint8_t cpb_size_scale; + uint8_t cpb_size_du_scale; + uint8_t hrd_cpb_cnt_minus1; +} H266RawGeneralTimingHrdParameters; + +typedef struct H266RawSubLayerHRDParameters { + uint32_t bit_rate_value_minus1[VVC_MAX_SUBLAYERS][VVC_MAX_CPB_CNT]; + uint32_t cpb_size_value_minus1[VVC_MAX_SUBLAYERS][VVC_MAX_CPB_CNT]; + uint32_t cpb_size_du_value_minus1[VVC_MAX_SUBLAYERS][VVC_MAX_CPB_CNT]; + uint32_t bit_rate_du_value_minus1[VVC_MAX_SUBLAYERS][VVC_MAX_CPB_CNT]; + uint8_t cbr_flag[VVC_MAX_SUBLAYERS][VVC_MAX_CPB_CNT]; +} H266RawSubLayerHRDParameters; + +typedef struct H266RawOlsTimingHrdParameters { + uint8_t fixed_pic_rate_general_flag[VVC_MAX_SUBLAYERS]; + uint8_t fixed_pic_rate_within_cvs_flag[VVC_MAX_SUBLAYERS]; + uint16_t elemental_duration_in_tc_minus1[VVC_MAX_SUBLAYERS]; + uint8_t low_delay_hrd_flag[VVC_MAX_SUBLAYERS]; + H266RawSubLayerHRDParameters nal_sub_layer_hrd_parameters; + H266RawSubLayerHRDParameters vcl_sub_layer_hrd_parameters; +} H266RawOlsTimingHrdParameters; + +typedef struct H266RawVUI { + uint8_t vui_progressive_source_flag; + uint8_t vui_interlaced_source_flag; + uint8_t vui_non_packed_constraint_flag; + uint8_t vui_non_projected_constraint_flag; + + uint8_t vui_aspect_ratio_info_present_flag; + uint8_t vui_aspect_ratio_constant_flag; + uint8_t vui_aspect_ratio_idc; + + uint16_t vui_sar_width; + uint16_t vui_sar_height; + + uint8_t vui_overscan_info_present_flag; + uint8_t vui_overscan_appropriate_flag; + + uint8_t vui_colour_description_present_flag; + uint8_t vui_colour_primaries; + + uint8_t vui_transfer_characteristics; + uint8_t vui_matrix_coeffs; + uint8_t vui_full_range_flag; + + uint8_t vui_chroma_loc_info_present_flag; + uint8_t vui_chroma_sample_loc_type_frame; + uint8_t vui_chroma_sample_loc_type_top_field; + uint8_t vui_chroma_sample_loc_type_bottom_field; + H266RawExtensionData extension_data; +} H266RawVUI; + +typedef struct H266RawOPI { + H266RawNALUnitHeader nal_unit_header; + + uint8_t opi_ols_info_present_flag; + uint8_t opi_htid_info_present_flag; + uint16_t opi_ols_idx; + uint8_t opi_htid_plus1; + uint8_t opi_extension_flag; + H266RawExtensionData extension_data; +} H266RawOPI; + +typedef struct H266RawDCI { + H266RawNALUnitHeader nal_unit_header; + + uint8_t dci_reserved_zero_4bits; + uint8_t dci_num_ptls_minus1; + H266RawProfileTierLevel dci_profile_tier_level[VVC_MAX_DCI_PTLS]; + uint8_t dci_extension_flag; + H266RawExtensionData extension_data; +} H266RawDCI; + +typedef struct H266RawVPS { + H266RawNALUnitHeader nal_unit_header; + + uint8_t vps_video_parameter_set_id; + uint8_t vps_max_layers_minus1; + uint8_t vps_max_sublayers_minus1; + uint8_t vps_default_ptl_dpb_hrd_max_tid_flag; + uint8_t vps_all_independent_layers_flag; + uint8_t vps_layer_id[VVC_MAX_LAYERS]; + uint8_t vps_independent_layer_flag[VVC_MAX_LAYERS]; + uint8_t vps_max_tid_ref_present_flag[VVC_MAX_LAYERS]; + uint8_t vps_direct_ref_layer_flag[VVC_MAX_LAYERS][VVC_MAX_LAYERS - 1]; + uint8_t vps_max_tid_il_ref_pics_plus1[VVC_MAX_LAYERS][VVC_MAX_LAYERS - 1]; + uint8_t vps_each_layer_is_an_ols_flag; + uint8_t vps_ols_mode_idc; + uint8_t vps_num_output_layer_sets_minus2; + uint8_t vps_ols_output_layer_flag[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_LAYERS]; + + uint8_t vps_num_ptls_minus1; + uint8_t vps_pt_present_flag[VVC_MAX_PTLS]; + uint8_t vps_ptl_max_tid[VVC_MAX_PTLS]; + H266RawProfileTierLevel vps_profile_tier_level[VVC_MAX_PTLS]; + uint8_t vps_ols_ptl_idx[VVC_MAX_TOTAL_NUM_OLSS]; + + uint16_t vps_num_dpb_params_minus1; + uint8_t vps_sublayer_dpb_params_present_flag; + uint8_t vps_dpb_max_tid[VVC_MAX_TOTAL_NUM_OLSS]; + H266DpbParameters vps_dpb_params[VVC_MAX_TOTAL_NUM_OLSS]; + uint16_t vps_ols_dpb_pic_width[VVC_MAX_TOTAL_NUM_OLSS]; + uint16_t vps_ols_dpb_pic_height[VVC_MAX_TOTAL_NUM_OLSS]; + uint8_t vps_ols_dpb_chroma_format[VVC_MAX_TOTAL_NUM_OLSS]; + uint8_t vps_ols_dpb_bitdepth_minus8[VVC_MAX_TOTAL_NUM_OLSS]; + uint16_t vps_ols_dpb_params_idx[VVC_MAX_TOTAL_NUM_OLSS]; + + uint8_t vps_timing_hrd_params_present_flag; + H266RawGeneralTimingHrdParameters vps_general_timing_hrd_parameters; + uint8_t vps_sublayer_cpb_params_present_flag; + uint16_t vps_num_ols_timing_hrd_params_minus1; + uint8_t vps_hrd_max_tid[VVC_MAX_TOTAL_NUM_OLSS]; + H266RawOlsTimingHrdParameters vps_ols_timing_hrd_parameters; + uint8_t vps_ols_timing_hrd_idx[VVC_MAX_TOTAL_NUM_OLSS]; + + uint8_t vps_extension_flag; + H266RawExtensionData extension_data; +} H266RawVPS; + +typedef struct H266RawSPS { + H266RawNALUnitHeader nal_unit_header; + + uint8_t sps_seq_parameter_set_id; + uint8_t sps_video_parameter_set_id; + uint8_t sps_max_sublayers_minus1; + uint8_t sps_chroma_format_idc; + uint8_t sps_log2_ctu_size_minus5; + uint8_t sps_ptl_dpb_hrd_params_present_flag; + H266RawProfileTierLevel profile_tier_level; + uint8_t sps_gdr_enabled_flag; + uint8_t sps_ref_pic_resampling_enabled_flag; + uint8_t sps_res_change_in_clvs_allowed_flag; + + uint16_t sps_pic_width_max_in_luma_samples; + uint16_t sps_pic_height_max_in_luma_samples; + + uint8_t sps_conformance_window_flag; + uint16_t sps_conf_win_left_offset; + uint16_t sps_conf_win_right_offset; + uint16_t sps_conf_win_top_offset; + uint16_t sps_conf_win_bottom_offset; + + uint8_t sps_subpic_info_present_flag; + uint16_t sps_num_subpics_minus1; + uint8_t sps_independent_subpics_flag; + uint8_t sps_subpic_same_size_flag; + uint16_t sps_subpic_ctu_top_left_x[VVC_MAX_SLICES]; + uint16_t sps_subpic_ctu_top_left_y[VVC_MAX_SLICES]; + uint16_t sps_subpic_width_minus1[VVC_MAX_SLICES]; + uint16_t sps_subpic_height_minus1[VVC_MAX_SLICES]; + uint8_t sps_subpic_treated_as_pic_flag[VVC_MAX_SLICES]; + uint8_t sps_loop_filter_across_subpic_enabled_flag[VVC_MAX_SLICES]; + uint8_t sps_subpic_id_len_minus1; + uint8_t sps_subpic_id_mapping_explicitly_signalled_flag; + uint8_t sps_subpic_id_mapping_present_flag; + uint32_t sps_subpic_id[VVC_MAX_SLICES]; + + + uint8_t sps_bitdepth_minus8; + uint8_t sps_entropy_coding_sync_enabled_flag; + uint8_t sps_entry_point_offsets_present_flag; + + uint8_t sps_log2_max_pic_order_cnt_lsb_minus4; + uint8_t sps_poc_msb_cycle_flag; + uint8_t sps_poc_msb_cycle_len_minus1; + + uint8_t sps_num_extra_ph_bytes; + uint8_t sps_extra_ph_bit_present_flag[16]; + + uint8_t sps_num_extra_sh_bytes; + uint8_t sps_extra_sh_bit_present_flag[16]; + + uint8_t sps_sublayer_dpb_params_flag; + H266DpbParameters sps_dpb_params; + + uint8_t sps_log2_min_luma_coding_block_size_minus2; + uint8_t sps_partition_constraints_override_enabled_flag; + uint8_t sps_log2_diff_min_qt_min_cb_intra_slice_luma; + uint8_t sps_max_mtt_hierarchy_depth_intra_slice_luma; + uint8_t sps_log2_diff_max_bt_min_qt_intra_slice_luma; + uint8_t sps_log2_diff_max_tt_min_qt_intra_slice_luma; + + uint8_t sps_qtbtt_dual_tree_intra_flag; + uint8_t sps_log2_diff_min_qt_min_cb_intra_slice_chroma; + uint8_t sps_max_mtt_hierarchy_depth_intra_slice_chroma; + uint8_t sps_log2_diff_max_bt_min_qt_intra_slice_chroma; + uint8_t sps_log2_diff_max_tt_min_qt_intra_slice_chroma; + + uint8_t sps_log2_diff_min_qt_min_cb_inter_slice; + uint8_t sps_max_mtt_hierarchy_depth_inter_slice; + uint8_t sps_log2_diff_max_bt_min_qt_inter_slice; + uint8_t sps_log2_diff_max_tt_min_qt_inter_slice; + + uint8_t sps_max_luma_transform_size_64_flag; + + uint8_t sps_transform_skip_enabled_flag; + uint8_t sps_log2_transform_skip_max_size_minus2; + uint8_t sps_bdpcm_enabled_flag; + + uint8_t sps_mts_enabled_flag; + uint8_t sps_explicit_mts_intra_enabled_flag; + uint8_t sps_explicit_mts_inter_enabled_flag; + + uint8_t sps_lfnst_enabled_flag; + + uint8_t sps_joint_cbcr_enabled_flag; + uint8_t sps_same_qp_table_for_chroma_flag; + + int8_t sps_qp_table_start_minus26[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t sps_num_points_in_qp_table_minus1[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t sps_delta_qp_in_val_minus1[VVC_MAX_SAMPLE_ARRAYS][VVC_MAX_POINTS_IN_QP_TABLE]; + uint8_t sps_delta_qp_diff_val[VVC_MAX_SAMPLE_ARRAYS][VVC_MAX_POINTS_IN_QP_TABLE]; + + uint8_t sps_sao_enabled_flag; + uint8_t sps_alf_enabled_flag; + uint8_t sps_ccalf_enabled_flag; + uint8_t sps_lmcs_enabled_flag; + uint8_t sps_weighted_pred_flag; + uint8_t sps_weighted_bipred_flag; + uint8_t sps_long_term_ref_pics_flag; + uint8_t sps_inter_layer_prediction_enabled_flag; + uint8_t sps_idr_rpl_present_flag; + uint8_t sps_rpl1_same_as_rpl0_flag; + + uint8_t sps_num_ref_pic_lists[2]; + H266RefPicListStruct sps_ref_pic_list_struct[2][VVC_MAX_REF_PIC_LISTS]; + + uint8_t sps_ref_wraparound_enabled_flag; + uint8_t sps_temporal_mvp_enabled_flag; + uint8_t sps_sbtmvp_enabled_flag; + uint8_t sps_amvr_enabled_flag; + uint8_t sps_bdof_enabled_flag; + uint8_t sps_bdof_control_present_in_ph_flag; + uint8_t sps_smvd_enabled_flag; + uint8_t sps_dmvr_enabled_flag; + uint8_t sps_dmvr_control_present_in_ph_flag; + uint8_t sps_mmvd_enabled_flag; + uint8_t sps_mmvd_fullpel_only_enabled_flag; + uint8_t sps_six_minus_max_num_merge_cand; + uint8_t sps_sbt_enabled_flag; + uint8_t sps_affine_enabled_flag; + uint8_t sps_five_minus_max_num_subblock_merge_cand; + uint8_t sps_6param_affine_enabled_flag; + uint8_t sps_affine_amvr_enabled_flag; + uint8_t sps_affine_prof_enabled_flag; + uint8_t sps_prof_control_present_in_ph_flag; + uint8_t sps_bcw_enabled_flag; + uint8_t sps_ciip_enabled_flag; + uint8_t sps_gpm_enabled_flag; + uint8_t sps_max_num_merge_cand_minus_max_num_gpm_cand; + uint8_t sps_log2_parallel_merge_level_minus2; + uint8_t sps_isp_enabled_flag; + uint8_t sps_mrl_enabled_flag; + uint8_t sps_mip_enabled_flag; + uint8_t sps_cclm_enabled_flag; + uint8_t sps_chroma_horizontal_collocated_flag; + uint8_t sps_chroma_vertical_collocated_flag; + uint8_t sps_palette_enabled_flag; + uint8_t sps_act_enabled_flag; + uint8_t sps_min_qp_prime_ts; + uint8_t sps_ibc_enabled_flag; + uint8_t sps_six_minus_max_num_ibc_merge_cand; + uint8_t sps_ladf_enabled_flag; + uint8_t sps_num_ladf_intervals_minus2; + int8_t sps_ladf_lowest_interval_qp_offset; + int8_t sps_ladf_qp_offset[4]; + uint16_t sps_ladf_delta_threshold_minus1[4]; + + uint8_t sps_explicit_scaling_list_enabled_flag; + uint8_t sps_scaling_matrix_for_lfnst_disabled_flag; + uint8_t sps_scaling_matrix_for_alternative_colour_space_disabled_flag; + uint8_t sps_scaling_matrix_designated_colour_space_flag; + uint8_t sps_dep_quant_enabled_flag; + uint8_t sps_sign_data_hiding_enabled_flag; + + uint8_t sps_virtual_boundaries_enabled_flag; + uint8_t sps_virtual_boundaries_present_flag; + uint8_t sps_num_ver_virtual_boundaries; + uint16_t sps_virtual_boundary_pos_x_minus1[3]; + uint8_t sps_num_hor_virtual_boundaries; + uint16_t sps_virtual_boundary_pos_y_minus1[3]; + + uint8_t sps_timing_hrd_params_present_flag; + uint8_t sps_sublayer_cpb_params_present_flag; + H266RawGeneralTimingHrdParameters sps_general_timing_hrd_parameters; + H266RawOlsTimingHrdParameters sps_ols_timing_hrd_parameters; + + uint8_t sps_field_seq_flag; + uint8_t sps_vui_parameters_present_flag; + uint16_t sps_vui_payload_size_minus1; + H266RawVUI vui; + + uint8_t sps_extension_flag; + + uint8_t sps_range_extension_flag; + uint8_t sps_extension_7bits; + + uint8_t sps_extended_precision_flag; + uint8_t sps_ts_residual_coding_rice_present_in_sh_flag; + uint8_t sps_rrc_rice_extension_flag; + uint8_t sps_persistent_rice_adaptation_enabled_flag; + uint8_t sps_reverse_last_sig_coeff_enabled_flag; + + H266RawExtensionData extension_data; + +} H266RawSPS; + +typedef struct H266RawPPS { + H266RawNALUnitHeader nal_unit_header; + + uint8_t pps_pic_parameter_set_id; + uint8_t pps_seq_parameter_set_id; + uint8_t pps_mixed_nalu_types_in_pic_flag; + uint16_t pps_pic_width_in_luma_samples; + uint16_t pps_pic_height_in_luma_samples; + + uint8_t pps_conformance_window_flag; + uint16_t pps_conf_win_left_offset; + uint16_t pps_conf_win_right_offset; + uint16_t pps_conf_win_top_offset; + uint16_t pps_conf_win_bottom_offset; + + uint8_t pps_scaling_window_explicit_signalling_flag; + int16_t pps_scaling_win_left_offset; + int16_t pps_scaling_win_right_offset; + int16_t pps_scaling_win_top_offset; + int16_t pps_scaling_win_bottom_offset; + + uint8_t pps_output_flag_present_flag; + uint8_t pps_no_pic_partition_flag; + + uint8_t pps_subpic_id_mapping_present_flag; + uint16_t pps_num_subpics_minus1; + uint8_t pps_subpic_id_len_minus1; + uint16_t pps_subpic_id[VVC_MAX_SLICES]; + + uint8_t pps_log2_ctu_size_minus5; + uint8_t pps_num_exp_tile_columns_minus1; + uint8_t pps_num_exp_tile_rows_minus1; + uint16_t pps_tile_column_width_minus1[VVC_MAX_TILE_COLUMNS]; + uint16_t pps_tile_row_height_minus1[VVC_MAX_TILE_ROWS]; + + uint8_t pps_loop_filter_across_tiles_enabled_flag; + uint8_t pps_rect_slice_flag; + uint8_t pps_single_slice_per_subpic_flag; + + uint16_t pps_num_slices_in_pic_minus1; + uint8_t pps_tile_idx_delta_present_flag; + uint16_t pps_slice_width_in_tiles_minus1[VVC_MAX_SLICES]; + uint16_t pps_slice_height_in_tiles_minus1[VVC_MAX_SLICES]; + uint16_t pps_num_exp_slices_in_tile[VVC_MAX_SLICES]; + uint16_t pps_exp_slice_height_in_ctus_minus1[VVC_MAX_SLICES][VVC_MAX_TILE_ROWS]; + int16_t pps_tile_idx_delta_val[VVC_MAX_SLICES]; + + uint8_t pps_loop_filter_across_slices_enabled_flag; + uint8_t pps_cabac_init_present_flag; + uint8_t pps_num_ref_idx_default_active_minus1[2]; + uint8_t pps_rpl1_idx_present_flag; + uint8_t pps_weighted_pred_flag; + uint8_t pps_weighted_bipred_flag; + uint8_t pps_ref_wraparound_enabled_flag; + uint16_t pps_pic_width_minus_wraparound_offset; + int8_t pps_init_qp_minus26; + uint8_t pps_cu_qp_delta_enabled_flag; + uint8_t pps_chroma_tool_offsets_present_flag; + int8_t pps_cb_qp_offset; + int8_t pps_cr_qp_offset; + uint8_t pps_joint_cbcr_qp_offset_present_flag; + int8_t pps_joint_cbcr_qp_offset_value; + uint8_t pps_slice_chroma_qp_offsets_present_flag; + uint8_t pps_cu_chroma_qp_offset_list_enabled_flag; + uint8_t pps_chroma_qp_offset_list_len_minus1; + int8_t pps_cb_qp_offset_list[6]; + int8_t pps_cr_qp_offset_list[6]; + int8_t pps_joint_cbcr_qp_offset_list[6]; + uint8_t pps_deblocking_filter_control_present_flag; + uint8_t pps_deblocking_filter_override_enabled_flag; + uint8_t pps_deblocking_filter_disabled_flag; + uint8_t pps_dbf_info_in_ph_flag; + + int8_t pps_luma_beta_offset_div2; + int8_t pps_luma_tc_offset_div2; + int8_t pps_cb_beta_offset_div2; + int8_t pps_cb_tc_offset_div2; + int8_t pps_cr_beta_offset_div2; + int8_t pps_cr_tc_offset_div2; + + uint8_t pps_rpl_info_in_ph_flag; + uint8_t pps_sao_info_in_ph_flag; + uint8_t pps_alf_info_in_ph_flag; + uint8_t pps_wp_info_in_ph_flag; + uint8_t pps_qp_delta_info_in_ph_flag; + + uint8_t pps_picture_header_extension_present_flag; + uint8_t pps_slice_header_extension_present_flag; + uint8_t pps_extension_flag; + H266RawExtensionData extension_data; + + //calculated value; + uint16_t num_tile_columns; + uint16_t num_tile_rows; + uint16_t num_tiles_in_pic; + uint16_t slice_height_in_ctus[VVC_MAX_SLICES]; + uint16_t num_slices_in_subpic[VVC_MAX_SLICES]; + uint16_t sub_pic_id_val[VVC_MAX_SLICES]; + uint16_t col_width_val[VVC_MAX_TILE_COLUMNS]; + uint16_t row_height_val[VVC_MAX_TILE_ROWS]; +} H266RawPPS; + +typedef struct H266RawAPS { + H266RawNALUnitHeader nal_unit_header; + uint8_t aps_params_type; + uint8_t aps_adaptation_parameter_set_id; + uint8_t aps_chroma_present_flag; + + uint8_t alf_luma_filter_signal_flag; + uint8_t alf_chroma_filter_signal_flag; + uint8_t alf_cc_cb_filter_signal_flag; + uint8_t alf_cc_cr_filter_signal_flag; + uint8_t alf_luma_clip_flag; + uint8_t alf_luma_num_filters_signalled_minus1; + uint8_t alf_luma_coeff_delta_idx[VVC_NUM_ALF_FILTERS]; + uint8_t alf_luma_coeff_abs[VVC_NUM_ALF_FILTERS][12]; + uint8_t alf_luma_coeff_sign[VVC_NUM_ALF_FILTERS][12]; + uint8_t alf_luma_clip_idx[VVC_NUM_ALF_FILTERS][12]; + uint8_t alf_chroma_clip_flag; + uint8_t alf_chroma_num_alt_filters_minus1; + uint8_t alf_chroma_coeff_abs[8][6]; + uint8_t alf_chroma_coeff_sign[8][6]; + uint8_t alf_chroma_clip_idx[8][6]; + uint8_t alf_cc_cb_filters_signalled_minus1; + uint8_t alf_cc_cb_mapped_coeff_abs[4][7]; + uint8_t alf_cc_cb_coeff_sign[4][7]; + uint8_t alf_cc_cr_filters_signalled_minus1; + uint8_t alf_cc_cr_mapped_coeff_abs[4][7]; + uint8_t alf_cc_cr_coeff_sign[4][7]; + + uint8_t scaling_list_copy_mode_flag[28]; + uint8_t scaling_list_pred_mode_flag[28]; + uint8_t scaling_list_pred_id_delta[28]; + int8_t scaling_list_dc_coef[14]; + int8_t scaling_list_delta_coef[28][64]; + + uint8_t lmcs_min_bin_idx; + uint8_t lmcs_delta_max_bin_idx; + uint8_t lmcs_delta_cw_prec_minus1; + uint16_t lmcs_delta_abs_cw[16]; + uint8_t lmcs_delta_sign_cw_flag[16]; + uint8_t lmcs_delta_abs_crs; + uint8_t lmcs_delta_sign_crs_flag; + + uint8_t aps_extension_flag; + H266RawExtensionData extension_data; +} H266RawAPS; + +typedef struct H266RawAUD { + H266RawNALUnitHeader nal_unit_header; + uint8_t aud_irap_or_gdr_flag; + uint8_t aud_pic_type; +} H266RawAUD; + +typedef struct H266RawPredWeightTable { + uint8_t luma_log2_weight_denom; + int8_t delta_chroma_log2_weight_denom; + + uint8_t num_l0_weights; + uint8_t luma_weight_l0_flag[15]; + uint8_t chroma_weight_l0_flag[15]; + int8_t delta_luma_weight_l0[15]; + int8_t luma_offset_l0[15]; + int8_t delta_chroma_weight_l0[15][2]; + int16_t delta_chroma_offset_l0[15][2]; + + uint8_t num_l1_weights; + uint8_t luma_weight_l1_flag[15]; + uint8_t chroma_weight_l1_flag[15]; + int8_t delta_luma_weight_l1[15]; + int8_t luma_offset_l1[15]; + int8_t delta_chroma_weight_l1[15][2]; + int16_t delta_chroma_offset_l1[15][2]; + + uint8_t num_weights_l0; ///< NumWeightsL0 + uint8_t num_weights_l1; ///< NumWeightsL1 +} H266RawPredWeightTable; + +typedef struct H266RawPictureHeader { + uint8_t ph_gdr_or_irap_pic_flag; + uint8_t ph_non_ref_pic_flag; + uint8_t ph_gdr_pic_flag; + uint8_t ph_inter_slice_allowed_flag; + uint8_t ph_intra_slice_allowed_flag; + uint8_t ph_pic_parameter_set_id; + uint16_t ph_pic_order_cnt_lsb; + uint8_t ph_recovery_poc_cnt; + uint8_t ph_extra_bit[16]; + uint8_t ph_poc_msb_cycle_present_flag; + uint8_t ph_poc_msb_cycle_val; + + uint8_t ph_alf_enabled_flag; + uint8_t ph_num_alf_aps_ids_luma; + uint8_t ph_alf_aps_id_luma[8]; + uint8_t ph_alf_cb_enabled_flag; + uint8_t ph_alf_cr_enabled_flag; + uint8_t ph_alf_aps_id_chroma; + uint8_t ph_alf_cc_cb_enabled_flag; + uint8_t ph_alf_cc_cb_aps_id; + uint8_t ph_alf_cc_cr_enabled_flag; + uint8_t ph_alf_cc_cr_aps_id; + + uint8_t ph_lmcs_enabled_flag; + uint8_t ph_lmcs_aps_id; + uint8_t ph_chroma_residual_scale_flag; + uint8_t ph_explicit_scaling_list_enabled_flag; + uint8_t ph_scaling_list_aps_id; + + uint8_t ph_virtual_boundaries_present_flag; + uint8_t ph_num_ver_virtual_boundaries; + uint16_t ph_virtual_boundary_pos_x_minus1[3]; + uint8_t ph_num_hor_virtual_boundaries; + uint16_t ph_virtual_boundary_pos_y_minus1[3]; + + uint8_t ph_pic_output_flag; + H266RefPicLists ph_ref_pic_lists; + + uint8_t ph_partition_constraints_override_flag; + + uint8_t ph_log2_diff_min_qt_min_cb_intra_slice_luma; + uint8_t ph_max_mtt_hierarchy_depth_intra_slice_luma; + uint8_t ph_log2_diff_max_bt_min_qt_intra_slice_luma; + uint8_t ph_log2_diff_max_tt_min_qt_intra_slice_luma; + uint8_t ph_log2_diff_min_qt_min_cb_intra_slice_chroma; + + uint8_t ph_max_mtt_hierarchy_depth_intra_slice_chroma; + uint8_t ph_log2_diff_max_bt_min_qt_intra_slice_chroma; + uint8_t ph_log2_diff_max_tt_min_qt_intra_slice_chroma; + + uint8_t ph_cu_qp_delta_subdiv_intra_slice; + uint8_t ph_cu_chroma_qp_offset_subdiv_intra_slice; + + uint8_t ph_log2_diff_min_qt_min_cb_inter_slice; + uint8_t ph_max_mtt_hierarchy_depth_inter_slice; + uint8_t ph_log2_diff_max_bt_min_qt_inter_slice; + uint8_t ph_log2_diff_max_tt_min_qt_inter_slice; + uint8_t ph_cu_qp_delta_subdiv_inter_slice; + uint8_t ph_cu_chroma_qp_offset_subdiv_inter_slice; + + uint8_t ph_temporal_mvp_enabled_flag; + uint8_t ph_collocated_from_l0_flag; + uint8_t ph_collocated_ref_idx; + uint8_t ph_mmvd_fullpel_only_flag; + uint8_t ph_mvd_l1_zero_flag; + uint8_t ph_bdof_disabled_flag; + uint8_t ph_dmvr_disabled_flag; + uint8_t ph_prof_disabled_flag; + + H266RawPredWeightTable ph_pred_weight_table; + + int8_t ph_qp_delta; + uint8_t ph_joint_cbcr_sign_flag; + uint8_t ph_sao_luma_enabled_flag; + uint8_t ph_sao_chroma_enabled_flag; + + uint8_t ph_deblocking_params_present_flag; + uint8_t ph_deblocking_filter_disabled_flag; + int8_t ph_luma_beta_offset_div2; + int8_t ph_luma_tc_offset_div2; + int8_t ph_cb_beta_offset_div2; + int8_t ph_cb_tc_offset_div2; + int8_t ph_cr_beta_offset_div2; + int8_t ph_cr_tc_offset_div2; + + uint8_t ph_extension_length; + uint8_t ph_extension_data_byte[256]; +} H266RawPictureHeader; + +typedef struct H266RawPH { + H266RawNALUnitHeader nal_unit_header; + H266RawPictureHeader ph_picture_header; +} H266RawPH; + +typedef struct H266RawSliceHeader { + H266RawNALUnitHeader nal_unit_header; + uint8_t sh_picture_header_in_slice_header_flag; + H266RawPictureHeader sh_picture_header; + + uint16_t sh_subpic_id; + uint16_t sh_slice_address; + uint8_t sh_extra_bit[16]; + uint8_t sh_num_tiles_in_slice_minus1; + uint8_t sh_slice_type; + uint8_t sh_no_output_of_prior_pics_flag; + + uint8_t sh_alf_enabled_flag; + uint8_t sh_num_alf_aps_ids_luma; + uint8_t sh_alf_aps_id_luma[8]; + uint8_t sh_alf_cb_enabled_flag; + uint8_t sh_alf_cr_enabled_flag; + uint8_t sh_alf_aps_id_chroma; + uint8_t sh_alf_cc_cb_enabled_flag; + uint8_t sh_alf_cc_cb_aps_id; + uint8_t sh_alf_cc_cr_enabled_flag; + uint8_t sh_alf_cc_cr_aps_id; + + uint8_t sh_lmcs_used_flag; + uint8_t sh_explicit_scaling_list_used_flag; + + H266RefPicLists sh_ref_pic_lists; + + uint8_t sh_num_ref_idx_active_override_flag; + uint8_t sh_num_ref_idx_active_minus1[2]; + uint8_t sh_cabac_init_flag; + uint8_t sh_collocated_from_l0_flag; + uint8_t sh_collocated_ref_idx; + + H266RawPredWeightTable sh_pred_weight_table; + + int8_t sh_qp_delta; + int8_t sh_cb_qp_offset; + int8_t sh_cr_qp_offset; + int8_t sh_joint_cbcr_qp_offset; + uint8_t sh_cu_chroma_qp_offset_enabled_flag; + + uint8_t sh_sao_luma_used_flag; + uint8_t sh_sao_chroma_used_flag; + + uint8_t sh_deblocking_params_present_flag; + uint8_t sh_deblocking_filter_disabled_flag; + int8_t sh_luma_beta_offset_div2; + int8_t sh_luma_tc_offset_div2; + int8_t sh_cb_beta_offset_div2; + int8_t sh_cb_tc_offset_div2; + int8_t sh_cr_beta_offset_div2; + int8_t sh_cr_tc_offset_div2; + uint8_t sh_dep_quant_used_flag; + + uint8_t sh_sign_data_hiding_used_flag; + uint8_t sh_ts_residual_coding_disabled_flag; + uint8_t sh_ts_residual_coding_rice_idx_minus1; + uint8_t sh_reverse_last_sig_coeff_flag; + uint16_t sh_slice_header_extension_length; + uint8_t sh_slice_header_extension_data_byte[256]; + + uint8_t sh_entry_offset_len_minus1; + uint32_t sh_entry_point_offset_minus1[VVC_MAX_ENTRY_POINTS]; + + // derived values + uint16_t curr_subpic_idx; ///< CurrSubpicIdx + uint32_t num_entry_points; ///< NumEntryPoints + uint8_t num_ref_idx_active[2]; ///< NumRefIdxActive[] + +} H266RawSliceHeader; + +typedef struct H266RawSlice { + H266RawSliceHeader header; + + uint8_t *data; + AVBufferRef *data_ref; + size_t header_size; + size_t data_size; + int data_bit_start; +} H266RawSlice; + +typedef struct H266RawSEIDecodedPictureHash { + uint8_t dph_sei_hash_type; + uint8_t dph_sei_single_component_flag; + uint8_t dph_sei_picture_md5[3][16]; + uint16_t dph_sei_picture_crc[3]; + uint32_t dph_sei_picture_checksum[3]; + + uint8_t dph_sei_reserved_zero_7bits; +} H266RawSEIDecodedPictureHash; + +typedef struct H266RawSEI { + H266RawNALUnitHeader nal_unit_header; + SEIRawMessageList message_list; +} H266RawSEI; + +typedef struct CodedBitstreamH266Context { + // Reader/writer context in common with the H.264 implementation. + CodedBitstreamH2645Context common; + + // All currently available parameter sets. These are updated when + // any parameter set NAL unit is read/written with this context. + H266RawVPS *vps[VVC_MAX_VPS_COUNT]; ///< RefStruct references + H266RawSPS *sps[VVC_MAX_SPS_COUNT]; ///< RefStruct references + H266RawPPS *pps[VVC_MAX_PPS_COUNT]; ///< RefStruct references + H266RawPictureHeader *ph; + void *ph_ref; ///< RefStruct reference backing ph above +} CodedBitstreamH266Context; + +#endif /* AVCODEC_CBS_H266_H */ diff --git a/libavcodec/cbs_h266_syntax_template.c b/libavcodec/cbs_h266_syntax_template.c new file mode 100644 index 00000000000..281069f06ea --- /dev/null +++ b/libavcodec/cbs_h266_syntax_template.c @@ -0,0 +1,3489 @@ +/* + * 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 + */ + +static int FUNC(rbsp_trailing_bits) (CodedBitstreamContext *ctx, + RWContext *rw) +{ + int err; + + fixed(1, rbsp_stop_one_bit, 1); + while (byte_alignment(rw) != 0) + fixed(1, rbsp_alignment_zero_bit, 0); + return 0; +} + +static int FUNC(nal_unit_header) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawNALUnitHeader *current, + int expected_nal_unit_type) +{ + int err; + + fixed(1, forbidden_zero_bit, 0); + flag(nuh_reserved_zero_bit); + + u(6, nuh_layer_id, 0, 55); + + if (expected_nal_unit_type >= 0) + u(5, nal_unit_type, expected_nal_unit_type, expected_nal_unit_type); + else + ub(5, nal_unit_type); + + u(3, nuh_temporal_id_plus1, 1, 7); + return 0; +} + +static int FUNC(byte_alignment) (CodedBitstreamContext *ctx, RWContext *rw) +{ + int err; + + fixed(1, byte_alignment_bit_equal_to_one, 1); + while (byte_alignment(rw) != 0) + fixed(1, byte_alignment_bit_equal_to_zero, 0); + return 0; +} + +static int FUNC(general_constraints_info) (CodedBitstreamContext *ctx, + RWContext *rw, + H266GeneralConstraintsInfo *current) +{ + int err, i, num_additional_bits_used; + + flag(gci_present_flag); + if (current->gci_present_flag) { + /* general */ + flag(gci_intra_only_constraint_flag); + flag(gci_all_layers_independent_constraint_flag); + flag(gci_one_au_only_constraint_flag); + + /* picture format */ + u(4, gci_sixteen_minus_max_bitdepth_constraint_idc, 0, 8); + ub(2, gci_three_minus_max_chroma_format_constraint_idc); + + /* NAL unit type related */ + flag(gci_no_mixed_nalu_types_in_pic_constraint_flag); + flag(gci_no_trail_constraint_flag); + flag(gci_no_stsa_constraint_flag); + flag(gci_no_rasl_constraint_flag); + flag(gci_no_radl_constraint_flag); + flag(gci_no_idr_constraint_flag); + flag(gci_no_cra_constraint_flag); + flag(gci_no_gdr_constraint_flag); + flag(gci_no_aps_constraint_flag); + flag(gci_no_idr_rpl_constraint_flag); + + /* tile, slice, subpicture partitioning */ + flag(gci_one_tile_per_pic_constraint_flag); + flag(gci_pic_header_in_slice_header_constraint_flag); + flag(gci_one_slice_per_pic_constraint_flag); + flag(gci_no_rectangular_slice_constraint_flag); + flag(gci_one_slice_per_subpic_constraint_flag); + flag(gci_no_subpic_info_constraint_flag); + + /* CTU and block partitioning */ + ub(2, gci_three_minus_max_log2_ctu_size_constraint_idc); + flag(gci_no_partition_constraints_override_constraint_flag); + flag(gci_no_mtt_constraint_flag); + flag(gci_no_qtbtt_dual_tree_intra_constraint_flag); + + /* intra */ + flag(gci_no_palette_constraint_flag); + flag(gci_no_ibc_constraint_flag); + flag(gci_no_isp_constraint_flag); + flag(gci_no_mrl_constraint_flag); + flag(gci_no_mip_constraint_flag); + flag(gci_no_cclm_constraint_flag); + + /* inter */ + flag(gci_no_ref_pic_resampling_constraint_flag); + flag(gci_no_res_change_in_clvs_constraint_flag); + flag(gci_no_weighted_prediction_constraint_flag); + flag(gci_no_ref_wraparound_constraint_flag); + flag(gci_no_temporal_mvp_constraint_flag); + flag(gci_no_sbtmvp_constraint_flag); + flag(gci_no_amvr_constraint_flag); + flag(gci_no_bdof_constraint_flag); + flag(gci_no_smvd_constraint_flag); + flag(gci_no_dmvr_constraint_flag); + flag(gci_no_mmvd_constraint_flag); + flag(gci_no_affine_motion_constraint_flag); + flag(gci_no_prof_constraint_flag); + flag(gci_no_bcw_constraint_flag); + flag(gci_no_ciip_constraint_flag); + flag(gci_no_gpm_constraint_flag); + + /* transform, quantization, residual */ + flag(gci_no_luma_transform_size_64_constraint_flag); + flag(gci_no_transform_skip_constraint_flag); + flag(gci_no_bdpcm_constraint_flag); + flag(gci_no_mts_constraint_flag); + flag(gci_no_lfnst_constraint_flag); + flag(gci_no_joint_cbcr_constraint_flag); + flag(gci_no_sbt_constraint_flag); + flag(gci_no_act_constraint_flag); + flag(gci_no_explicit_scaling_list_constraint_flag); + flag(gci_no_dep_quant_constraint_flag); + flag(gci_no_sign_data_hiding_constraint_flag); + flag(gci_no_cu_qp_delta_constraint_flag); + flag(gci_no_chroma_qp_offset_constraint_flag); + + /* loop filter */ + flag(gci_no_sao_constraint_flag); + flag(gci_no_alf_constraint_flag); + flag(gci_no_ccalf_constraint_flag); + flag(gci_no_lmcs_constraint_flag); + flag(gci_no_ladf_constraint_flag); + flag(gci_no_virtual_boundaries_constraint_flag); + ub(8, gci_num_additional_bits); + if (current->gci_num_additional_bits > 5) { + flag(gci_all_rap_pictures_constraint_flag); + flag(gci_no_extended_precision_processing_constraint_flag); + flag(gci_no_ts_residual_coding_rice_constraint_flag); + flag(gci_no_rrc_rice_extension_constraint_flag); + flag(gci_no_persistent_rice_adaptation_constraint_flag); + flag(gci_no_reverse_last_sig_coeff_constraint_flag); + num_additional_bits_used = 6; + } else { + infer(gci_all_rap_pictures_constraint_flag, 0); + infer(gci_no_extended_precision_processing_constraint_flag, 0); + infer(gci_no_ts_residual_coding_rice_constraint_flag, 0); + infer(gci_no_rrc_rice_extension_constraint_flag, 0); + infer(gci_no_persistent_rice_adaptation_constraint_flag, 0); + infer(gci_no_reverse_last_sig_coeff_constraint_flag, 0); + num_additional_bits_used = 0; + } + + for (i = 0; i < current->gci_num_additional_bits - num_additional_bits_used; i++) + flags(gci_reserved_bit[i], 1, i); + } + while (byte_alignment(rw) != 0) + fixed(1, gci_alignment_zero_bit, 0); + return 0; +} + +static int FUNC(profile_tier_level) (CodedBitstreamContext *ctx, + RWContext *rw, + H266RawProfileTierLevel *current, + int profile_tier_present_flag, + int max_num_sub_layers_minus1) +{ + int err, i; + + if (profile_tier_present_flag) { + ub(7, general_profile_idc); + flag(general_tier_flag); + } + ub(8, general_level_idc); + flag(ptl_frame_only_constraint_flag); + flag(ptl_multilayer_enabled_flag); + if (profile_tier_present_flag) { + CHECK(FUNC(general_constraints_info) (ctx, rw, + ¤t-> + general_constraints_info)); + } + for (i = max_num_sub_layers_minus1 - 1; i >= 0; i--) + flags(ptl_sublayer_level_present_flag[i], 1, i); + while (byte_alignment(rw) != 0) + flag(ptl_reserved_zero_bit); + for (i = max_num_sub_layers_minus1 - 1; i >= 0; i--) + if (current->ptl_sublayer_level_present_flag[i]) + ubs(8, sublayer_level_idc[i], 1, i); + if (profile_tier_present_flag) { + ub(8, ptl_num_sub_profiles); + for (i = 0; i < current->ptl_num_sub_profiles; i++) + ubs(32, general_sub_profile_idc[i], 1, i); + } + return 0; +} + +static int FUNC(vui_parameters_default) (CodedBitstreamContext *ctx, + RWContext *rw, H266RawVUI *current) +{ + //defined in D.8 + infer(vui_progressive_source_flag, 0); + infer(vui_interlaced_source_flag, 0); + + infer(vui_non_packed_constraint_flag, 0); + infer(vui_non_projected_constraint_flag, 0); + + infer(vui_aspect_ratio_constant_flag, 0); + infer(vui_aspect_ratio_idc, 0); + + infer(vui_overscan_info_present_flag, 0); + + infer(vui_colour_primaries, 2); + infer(vui_transfer_characteristics, 2); + infer(vui_matrix_coeffs, 2); + infer(vui_full_range_flag, 0); + + infer(vui_chroma_sample_loc_type_frame, 6); + infer(vui_chroma_sample_loc_type_top_field, 6); + infer(vui_chroma_sample_loc_type_bottom_field, 6); + return 0; +} + +static int FUNC(vui_parameters) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawVUI *current, + uint8_t chroma_format_idc) +{ + int err; + + flag(vui_progressive_source_flag); + flag(vui_interlaced_source_flag); + flag(vui_non_packed_constraint_flag); + flag(vui_non_projected_constraint_flag); + flag(vui_aspect_ratio_info_present_flag); + if (current->vui_aspect_ratio_info_present_flag) { + flag(vui_aspect_ratio_constant_flag); + ub(8, vui_aspect_ratio_idc); + if (current->vui_aspect_ratio_idc == 255) { + ub(16, vui_sar_width); + ub(16, vui_sar_height); + } + } else { + infer(vui_aspect_ratio_constant_flag, 0); + infer(vui_aspect_ratio_idc, 0); + } + flag(vui_overscan_info_present_flag); + if (current->vui_overscan_info_present_flag) + flag(vui_overscan_appropriate_flag); + flag(vui_colour_description_present_flag); + if (current->vui_colour_description_present_flag) { + ub(8, vui_colour_primaries); + av_log(ctx->log_ctx, AV_LOG_DEBUG, "vui_colour_primaries == %d \n", + current->vui_colour_primaries); + ub(8, vui_transfer_characteristics); + av_log(ctx->log_ctx, AV_LOG_DEBUG, + "vui_transfer_characteristics == %d \n", + current->vui_transfer_characteristics); + ub(8, vui_matrix_coeffs); + av_log(ctx->log_ctx, AV_LOG_DEBUG, "vui_matrix_coeffs == %d \n", + current->vui_matrix_coeffs); + flag(vui_full_range_flag); + } else { + infer(vui_colour_primaries, 2); + infer(vui_transfer_characteristics, 2); + infer(vui_matrix_coeffs, 2); + infer(vui_full_range_flag, 0); + } + flag(vui_chroma_loc_info_present_flag); + if (chroma_format_idc != 1 && current->vui_chroma_loc_info_present_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "chroma_format_idc == %d," + "vui_chroma_loc_info_present_flag can't not be true", + chroma_format_idc); + return AVERROR_INVALIDDATA; + } + if (current->vui_chroma_loc_info_present_flag) { + if (current->vui_progressive_source_flag && + !current->vui_interlaced_source_flag) { + ue(vui_chroma_sample_loc_type_frame, 0, 6); + } else { + ue(vui_chroma_sample_loc_type_top_field, 0, 6); + ue(vui_chroma_sample_loc_type_bottom_field, 0, 6); + } + } else { + if (chroma_format_idc == 1) { + infer(vui_chroma_sample_loc_type_frame, 6); + infer(vui_chroma_sample_loc_type_top_field, + current->vui_chroma_sample_loc_type_frame); + infer(vui_chroma_sample_loc_type_bottom_field, + current->vui_chroma_sample_loc_type_frame); + } + } + return 0; +} + +static int FUNC(payload_extension) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawExtensionData *current, + uint32_t payload_size, int cur_pos) +{ + int err; + size_t byte_length, k; + +#ifdef READ + GetBitContext tmp; + int bits_left, payload_zero_bits; + + if (!cbs_h265_payload_extension_present(rw, payload_size, cur_pos)) + return 0; + + bits_left = 8 * payload_size - cur_pos; + tmp = *rw; + if (bits_left > 8) + skip_bits_long(&tmp, bits_left - 8); + payload_zero_bits = get_bits(&tmp, FFMIN(bits_left, 8)); + if (!payload_zero_bits) + return AVERROR_INVALIDDATA; + payload_zero_bits = ff_ctz(payload_zero_bits); + current->bit_length = bits_left - payload_zero_bits - 1; + allocate(current->data, (current->bit_length + 7) / 8); +#endif + + byte_length = (current->bit_length + 7) / 8; + for (k = 0; k < byte_length; k++) { + int length = FFMIN(current->bit_length - k * 8, 8); + xu(length, reserved_payload_extension_data, current->data[k], + 0, MAX_UINT_BITS(length), 0); + } + + return 0; +} + +static int FUNC(vui_payload) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawVUI *current, uint16_t vui_payload_size, + uint8_t chroma_format_idc) +{ + int err; + int start_position, current_position; + + start_position = bit_position(rw); + CHECK(FUNC(vui_parameters) (ctx, rw, current, chroma_format_idc)); + current_position = bit_position(rw) - start_position; + + if (current_position < 8 * vui_payload_size) { + CHECK(FUNC(payload_extension) (ctx, rw, ¤t->extension_data, + vui_payload_size, current_position)); + fixed(1, vui_payload_bit_equal_to_one, 1); + while (byte_alignment(rw) != 0) + fixed(1, vui_payload_bit_equal_to_zero, 0); + } + return 0; +} + +static int FUNC(extension_data) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawExtensionData *current) +{ + int err; + size_t k; +#ifdef READ + GetBitContext start; + uint8_t bit; + start = *rw; + for (k = 0; cbs_h2645_read_more_rbsp_data(rw); k++) + skip_bits(rw, 1); + current->bit_length = k; + if (k > 0) { + *rw = start; + allocate(current->data, (current->bit_length + 7) / 8); + for (k = 0; k < current->bit_length; k++) { + xu(1, extension_data, bit, 0, 1, 0); + current->data[k / 8] |= bit << (7 - k % 8); + } + } +#else + for (k = 0; k < current->bit_length; k++) + xu(1, extension_data, current->data[k / 8] >> (7 - k % 8) & 1, 0, 1, 0); +#endif + return 0; +} + +static int FUNC(dpb_parameters) (CodedBitstreamContext *ctx, RWContext *rw, + H266DpbParameters *current, + uint8_t max_sublayers_minus1, + uint8_t sublayer_info_flag) +{ + int err, i; + for (i = (sublayer_info_flag ? 0 : max_sublayers_minus1); + i <= max_sublayers_minus1; i++) { + ues(dpb_max_dec_pic_buffering_minus1[i], 0, VVC_MAX_DPB_SIZE - 1, 1, i); + ues(dpb_max_num_reorder_pics[i], + 0, current->dpb_max_dec_pic_buffering_minus1[i], 1, i); + ues(dpb_max_latency_increase_plus1[i], 0, UINT32_MAX - 1, 1, i); + } + return 0; +} + +static int FUNC(ref_pic_list_struct) (CodedBitstreamContext *ctx, + RWContext *rw, + H266RefPicListStruct *current, + uint8_t list_idx, uint8_t rpls_idx, + const H266RawSPS *sps) +{ + CodedBitstreamH266Context *h266 = ctx->priv_data; + int err, i, j, general_layer_idx = -1, num_direct_ref_layers = 0; + const H266RawVPS *vps = h266->vps[sps->sps_video_parameter_set_id]; + + if (!vps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "VPS id %d not available.\n", sps->sps_video_parameter_set_id); + return AVERROR_INVALIDDATA; + } + //7.4.3.3 (29) + for (i = 0; i <= vps->vps_max_layers_minus1; i++) { + if (sps->nal_unit_header.nuh_layer_id == vps->vps_layer_id[i]) { + general_layer_idx = i; + break; + } + } + if (general_layer_idx < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "vps_layer_id %d not available.\n", + sps->nal_unit_header.nuh_layer_id); + return AVERROR_INVALIDDATA; + } + //7.4.3.3 (28) + for (j = 0; j <= vps->vps_max_layers_minus1; j++) { + if (vps->vps_direct_ref_layer_flag[general_layer_idx][j]) + num_direct_ref_layers++; + } + + ue(num_ref_entries, 0, VVC_MAX_REF_ENTRIES); + if (sps->sps_long_term_ref_pics_flag && + rpls_idx < sps->sps_num_ref_pic_lists[list_idx] && + current->num_ref_entries > 0) + flag(ltrp_in_header_flag); + if (sps->sps_long_term_ref_pics_flag && + rpls_idx == sps->sps_num_ref_pic_lists[list_idx]) + infer(ltrp_in_header_flag, 1); + for (i = 0, j = 0; i < current->num_ref_entries; i++) { + if (sps->sps_inter_layer_prediction_enabled_flag) + flags(inter_layer_ref_pic_flag[i], 1, i); + else + infer(inter_layer_ref_pic_flag[i], 0); + + if (!current->inter_layer_ref_pic_flag[i]) { + if (sps->sps_long_term_ref_pics_flag) + flags(st_ref_pic_flag[i], 1, i); + else + infer(st_ref_pic_flag[i], 1); + if (current->st_ref_pic_flag[i]) { + int abs_delta_poc_st; + ues(abs_delta_poc_st[i], 0, MAX_UINT_BITS(15), 1, i); + if ((sps->sps_weighted_pred_flag || + sps->sps_weighted_bipred_flag) && i != 0) + abs_delta_poc_st = current->abs_delta_poc_st[i]; + else + abs_delta_poc_st = current->abs_delta_poc_st[i] + 1; + if (abs_delta_poc_st > 0) + flags(strp_entry_sign_flag[i], 1, i); + } else { + if (!current->ltrp_in_header_flag) { + uint8_t bits = sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4; + ubs(bits, rpls_poc_lsb_lt[j], 1, j); + j++; + } + } + } else { + if (num_direct_ref_layers == 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "num_direct_ref_layers needs > 0.\n"); + return AVERROR_INVALIDDATA; + } + ues(ilrp_idx[i], 0, num_direct_ref_layers - 1, 1, i); + } + } + return 0; +} + +static int FUNC(ref_pic_lists) (CodedBitstreamContext *ctx, RWContext *rw, + const H266RawSPS *sps, const H266RawPPS *pps, + H266RefPicLists *current) { + const H266RefPicListStruct * ref_list; + int err, i, j, num_ltrp_entries; + for (i = 0; i < 2; i++) { + if (sps->sps_num_ref_pic_lists[i] > 0 && + (i == 0 || (i == 1 && pps->pps_rpl1_idx_present_flag))) { + flags(rpl_sps_flag[i], 1, i); + } else { + if (sps->sps_num_ref_pic_lists[i] == 0) { + infer(rpl_sps_flag[i], 0); + } else { + if (!pps->pps_rpl1_idx_present_flag && i == 1) + infer(rpl_sps_flag[1], current->rpl_sps_flag[0]); + } + } + if (current->rpl_sps_flag[i]) { + if (sps->sps_num_ref_pic_lists[i] > 1 && + (i == 0 || (i == 1 && pps->pps_rpl1_idx_present_flag))) { + uint8_t bits = av_ceil_log2(sps->sps_num_ref_pic_lists[i]); + us(bits, rpl_idx[i], 0, sps->sps_num_ref_pic_lists[i] - 1, 1, i); + } else if (sps->sps_num_ref_pic_lists[i] == 1) { + infer(rpl_idx[i], 0); + } else if (i == 1 && !pps->pps_rpl1_idx_present_flag) { + infer(rpl_idx[1], current->rpl_idx[0]); + } else { + //how to handle this? or never happpend? + av_log(ctx->log_ctx, AV_LOG_ERROR, + "can't infer the rpl_idx[i]\n"); + return AVERROR_PATCHWELCOME; + } + memcpy(¤t->rpl_ref_list[i], + &sps->sps_ref_pic_list_struct[i][current->rpl_idx[i]], + sizeof(current->rpl_ref_list[i])); + } else { + CHECK(FUNC(ref_pic_list_struct) (ctx, rw, ¤t->rpl_ref_list[i], + i, sps->sps_num_ref_pic_lists[i], + sps)); + } + ref_list = ¤t->rpl_ref_list[i]; + + num_ltrp_entries = 0; + for (int k = 0; k < ref_list->num_ref_entries; k++) { + if (!ref_list->inter_layer_ref_pic_flag[k]) { + if (!ref_list->st_ref_pic_flag[k]) { + num_ltrp_entries++; + } + } + } + + for (j = 0; j < num_ltrp_entries; j++) { + if (ref_list->ltrp_in_header_flag) { + ubs(sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4, + poc_lsb_lt[i][j], 2, i, j); + } + flags(delta_poc_msb_cycle_present_flag[i][j], 2, i, j); + if (current->delta_poc_msb_cycle_present_flag[i][j]) { + uint32_t max = + 1 << (32 - sps->sps_log2_max_pic_order_cnt_lsb_minus4 - 4); + ues(delta_poc_msb_cycle_lt[i][j], 0, max, 2, i, j); + } + } + } + return 0; +} + +static int FUNC(general_timing_hrd_parameters)(CodedBitstreamContext *ctx, + RWContext *rw, + H266RawGeneralTimingHrdParameters *current) +{ + int err; + ub(32, num_units_in_tick); + u(32, time_scale, 1, MAX_UINT_BITS(32)); + flag(general_nal_hrd_params_present_flag); + flag(general_vcl_hrd_params_present_flag); + + if (current->general_nal_hrd_params_present_flag || + current->general_vcl_hrd_params_present_flag) { + flag(general_same_pic_timing_in_all_ols_flag); + flag(general_du_hrd_params_present_flag); + if (current->general_du_hrd_params_present_flag) + ub(8, tick_divisor_minus2); + ub(4, bit_rate_scale); + ub(4, cpb_size_scale); + if (current->general_du_hrd_params_present_flag) + ub(4, cpb_size_du_scale); + ue(hrd_cpb_cnt_minus1, 0, 31); + } else { + //infer general_same_pic_timing_in_all_ols_flag? + infer(general_du_hrd_params_present_flag, 0); + } + return 0; +} + +static int FUNC(sublayer_hrd_parameters) (CodedBitstreamContext *ctx, + RWContext *rw, + H266RawSubLayerHRDParameters *current, + int sublayer_id, + const H266RawGeneralTimingHrdParameters *general) +{ + int err, i; + for (i = 0; i <= general->hrd_cpb_cnt_minus1; i++) { + ues(bit_rate_value_minus1[sublayer_id][i], 0, UINT32_MAX - 1, 2, + sublayer_id, i); + ues(cpb_size_value_minus1[sublayer_id][i], 0, UINT32_MAX - 1, 2, + sublayer_id, i); + if (general->general_du_hrd_params_present_flag) { + ues(cpb_size_du_value_minus1[sublayer_id][i], + 0, UINT32_MAX - 1, 2, sublayer_id, i); + ues(bit_rate_du_value_minus1[sublayer_id][i], + 0, UINT32_MAX - 1, 2, sublayer_id, i); + } + flags(cbr_flag[sublayer_id][i], 2, sublayer_id, i); + } + return 0; +} + +static int FUNC(ols_timing_hrd_parameters) (CodedBitstreamContext *ctx, + RWContext *rw, H266RawOlsTimingHrdParameters *current, + uint8_t first_sublayer, uint8_t max_sublayers_minus1, + const H266RawGeneralTimingHrdParameters *general) +{ + int err, i; + for (i = first_sublayer; i <= max_sublayers_minus1; i++) { + flags(fixed_pic_rate_general_flag[i], 1, i); + if (!current->fixed_pic_rate_general_flag[i]) + flags(fixed_pic_rate_within_cvs_flag[i], 1, i); + else + infer(fixed_pic_rate_within_cvs_flag[i], 1); + if (current->fixed_pic_rate_within_cvs_flag[i]) { + ues(elemental_duration_in_tc_minus1[i], 0, 2047, 1, i); + infer(low_delay_hrd_flag[i], 0); + } else if ((general->general_nal_hrd_params_present_flag || + general->general_vcl_hrd_params_present_flag) && + general->hrd_cpb_cnt_minus1 == 0) { + flags(low_delay_hrd_flag[i], 1, i); + } else { + infer(low_delay_hrd_flag[i], 0); + } + if (general->general_nal_hrd_params_present_flag) + CHECK(FUNC(sublayer_hrd_parameters) (ctx, rw, + ¤t->nal_sub_layer_hrd_parameters, + i, general)); + if (general->general_vcl_hrd_params_present_flag) + CHECK(FUNC(sublayer_hrd_parameters) (ctx, rw, + ¤t->nal_sub_layer_hrd_parameters, + i, general)); + } + return 0; +} + +static int FUNC(opi)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawOPI *current) +{ + int err; + + HEADER("Operating point information"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, + ¤t->nal_unit_header, VVC_OPI_NUT)); + + flag(opi_ols_info_present_flag); + flag(opi_htid_info_present_flag); + + if(current->opi_ols_info_present_flag) + ue(opi_ols_idx, 0, VVC_MAX_TOTAL_NUM_OLSS - 1); + + if(current->opi_htid_info_present_flag) + ub(3, opi_htid_plus1); + + flag(opi_extension_flag); + if (current->opi_extension_flag) + CHECK(FUNC(extension_data) (ctx, rw, ¤t->extension_data)); + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + + return 0; +} + +static int FUNC(dci)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawDCI *current) +{ + int err, i; + + HEADER("Decoding capability information"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, + ¤t->nal_unit_header, VVC_DCI_NUT)); + + ub(4, dci_reserved_zero_4bits); + ub(4, dci_num_ptls_minus1); + for (i = 0; i <= current->dci_num_ptls_minus1; i++) + CHECK(FUNC(profile_tier_level)(ctx, rw, + current->dci_profile_tier_level + i, 1, 0)); + + flag(dci_extension_flag); + if (current->dci_extension_flag) + CHECK(FUNC(extension_data)(ctx, rw, ¤t->extension_data)); + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(vps) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawVPS *current) +{ + int err, i, j, k; + uint16_t total_num_olss = 0; + uint8_t ols_mode_idc = 0; + uint16_t num_multi_layer_olss = 0; + uint8_t layer_included_in_ols_flag[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_LAYERS]; + uint8_t num_ref_layers[VVC_MAX_LAYERS]; + uint8_t reference_layer_idx[VVC_MAX_LAYERS][VVC_MAX_LAYERS]; + + HEADER("Video Parameter Set"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, + ¤t->nal_unit_header, VVC_VPS_NUT)); + + u(4, vps_video_parameter_set_id, 1, VVC_MAX_VPS_COUNT - 1); + ub(6, vps_max_layers_minus1); + u(3, vps_max_sublayers_minus1, 0, 6); + if (current->vps_max_layers_minus1 > 0 + && current->vps_max_sublayers_minus1 > 0) + flag(vps_default_ptl_dpb_hrd_max_tid_flag); + else + infer(vps_default_ptl_dpb_hrd_max_tid_flag, 1); + + if (current->vps_max_layers_minus1 > 0) + flag(vps_all_independent_layers_flag); + else + infer(vps_all_independent_layers_flag, 1); + + for (i = 0; i <= current->vps_max_layers_minus1; i++) { + ubs(6, vps_layer_id[i], 1, i); + if (i > 0 && current->vps_layer_id[i] <= current->vps_layer_id[i - 1]) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "vps_layer_id[%d](%d) should > vps_layer_id[%d](%d).\n", + i, current->vps_layer_id[i], i - 1, + current->vps_layer_id[i - 1]); + return AVERROR_INVALIDDATA; + } + if (i > 0 && !current->vps_all_independent_layers_flag) { + flags(vps_independent_layer_flag[i], 1, i); + if (!current->vps_independent_layer_flag[i]) { + flags(vps_max_tid_ref_present_flag[i], 1, i); + for (j = 0; j < i; j++) { + flags(vps_direct_ref_layer_flag[i][j], 2, i, j); + if (current->vps_max_tid_ref_present_flag[i] && + current->vps_direct_ref_layer_flag[i][j]) { + ubs(3, vps_max_tid_il_ref_pics_plus1[i][j], 2, i, j); + } else { + infer(vps_max_tid_il_ref_pics_plus1[i][j], + current->vps_max_sublayers_minus1 + 1); + } + } + } else { + for (j = 0; j < i; j++) { + infer(vps_direct_ref_layer_flag[i][j], 0); + } + } + } else { + infer(vps_independent_layer_flag[i], 1); + for (j = 0; j < i; j++) { + infer(vps_direct_ref_layer_flag[i][j], 0); + } + } + } + + if (current->vps_max_layers_minus1 > 0) { + if (current->vps_all_independent_layers_flag) + flag(vps_each_layer_is_an_ols_flag); + else + infer(vps_each_layer_is_an_ols_flag, 0); + if (!current->vps_each_layer_is_an_ols_flag) { + if (!current->vps_all_independent_layers_flag) + ub(2, vps_ols_mode_idc); + else + infer(vps_ols_mode_idc, 2); + if (current->vps_ols_mode_idc == 2) { + ub(8, vps_num_output_layer_sets_minus2); + for (i = 1; i <= current->vps_num_output_layer_sets_minus2 + 1; + i++) + for (j = 0; j <= current->vps_max_layers_minus1; j++) + flags(vps_ols_output_layer_flag[i][j], 2, i, j); + } + ols_mode_idc = current->vps_ols_mode_idc; + } else { + ols_mode_idc = 4; + } + if (ols_mode_idc == 4 || ols_mode_idc == 0 || ols_mode_idc == 1) + total_num_olss = current->vps_max_layers_minus1 + 1; + else if (ols_mode_idc == 2) + total_num_olss = current->vps_num_output_layer_sets_minus2 + 2; + else + av_log(ctx->log_ctx, AV_LOG_ERROR, + "ols_mode_idc == 3, patch welcome"); + u(8, vps_num_ptls_minus1, 0, total_num_olss - 1); + } else { + infer(vps_each_layer_is_an_ols_flag, 1); + infer(vps_num_ptls_minus1, 0); + } + + for (i = 0; i <= current->vps_num_ptls_minus1; i++) { + if (i > 0) + flags(vps_pt_present_flag[i], 1, i); + else + infer(vps_pt_present_flag[i], 1); + + if (!current->vps_default_ptl_dpb_hrd_max_tid_flag) + us(3, vps_ptl_max_tid[i], 0, current->vps_max_sublayers_minus1, 1, i); + else + infer(vps_ptl_max_tid[i], current->vps_max_sublayers_minus1); + } + while (byte_alignment(rw) != 0) + fixed(1, vps_ptl_alignment_zero_bit, 0); + + { + //calc NumMultiLayerOlss + int m; + uint8_t dependency_flag[VVC_MAX_LAYERS][VVC_MAX_LAYERS]; + uint16_t num_output_layers_in_ols[VVC_MAX_TOTAL_NUM_OLSS]; + uint8_t num_sub_layers_in_layer_in_ols[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_TOTAL_NUM_OLSS]; + uint8_t output_layer_idx[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_LAYERS]; + + //7.4.3.3 vps_direct_ref_layer_flag section + for (i = 0; i <= current->vps_max_layers_minus1; i++) { + for (j = 0; j <= current->vps_max_layers_minus1; j++) { + dependency_flag[i][j] = current->vps_direct_ref_layer_flag[i][j]; + for (k = 0; k < i; k++) { + if (current->vps_direct_ref_layer_flag[i][k] && + dependency_flag[k][j]) + dependency_flag[i][j] = 1; + } + } + } + for (i = 0; i <= current->vps_max_layers_minus1; i++) { + int r; + for (j = 0, r = 0; j <= current->vps_max_layers_minus1; j++) { + if (dependency_flag[i][j]) + reference_layer_idx[i][r++] = j; + } + num_ref_layers[i] = r; + } + + //7.4.3.3 vps_ols_output_layer_flag section + num_output_layers_in_ols[0] = 1; + num_sub_layers_in_layer_in_ols[0][0] = + current->vps_ptl_max_tid[current->vps_ols_ptl_idx[0]] + 1; + for (i = 1; i < total_num_olss; i++) { + if (ols_mode_idc == 4 || ols_mode_idc == 0) { + num_output_layers_in_ols[i] = 1; + if (current->vps_each_layer_is_an_ols_flag) { + num_sub_layers_in_layer_in_ols[i][0] = + current->vps_ptl_max_tid[current->vps_ols_ptl_idx[i]] + 1; + } else { + num_sub_layers_in_layer_in_ols[i][i] = + current->vps_ptl_max_tid[current->vps_ols_ptl_idx[i]] + 1; + for (k = i - 1; k >= 0; k--) { + num_sub_layers_in_layer_in_ols[i][k] = 0; + for (m = k + 1; m <= i; m++) { + uint8_t max_sublayer_needed = + FFMIN(num_sub_layers_in_layer_in_ols[i][m], + current->vps_max_tid_il_ref_pics_plus1[m][k]); + if (current->vps_direct_ref_layer_flag[m][k] && + num_sub_layers_in_layer_in_ols[i][k] < max_sublayer_needed) + num_sub_layers_in_layer_in_ols[i][k] = max_sublayer_needed; + } + } + } + } else if (current->vps_ols_mode_idc == 1) { + num_output_layers_in_ols[i] = i + 1; + for (j = 0; j < num_output_layers_in_ols[i]; j++) { + num_sub_layers_in_layer_in_ols[i][j] = + current->vps_ptl_max_tid[current->vps_ols_ptl_idx[i]] + 1; + } + } else if (current->vps_ols_mode_idc == 2) { + uint8_t highest_included_layer = 0; + for (j = 0; j <= current->vps_max_layers_minus1; j++) { + layer_included_in_ols_flag[i][j] = 0; + num_sub_layers_in_layer_in_ols[i][j] = 0; + } + for (k = 0, j = 0; k <= current->vps_max_layers_minus1; k++) { + if (current->vps_ols_output_layer_flag[i][k]) { + layer_included_in_ols_flag[i][k] = 1; + highest_included_layer = k; + output_layer_idx[i][j] = k; + num_sub_layers_in_layer_in_ols[i][k] = + current->vps_ptl_max_tid[current-> + vps_ols_ptl_idx[i]] + 1; + j++; + } + } + num_output_layers_in_ols[i] = j; + for (j = 0; j < num_output_layers_in_ols[i]; j++) { + int idx = output_layer_idx[i][j]; + for (k = 0; k < num_ref_layers[idx]; k++) { + if (!layer_included_in_ols_flag[i][reference_layer_idx[idx][k]]) + layer_included_in_ols_flag[i][reference_layer_idx[idx][k]] = 1; + } + } + for (k = highest_included_layer - 1; k >= 0; k--) { + if (layer_included_in_ols_flag[i][k] && + !current->vps_ols_output_layer_flag[i][k]) { + for (m = k + 1; m <= highest_included_layer; m++) { + uint8_t max_sublayer_needed = + FFMIN(num_sub_layers_in_layer_in_ols[i][m], + current->vps_max_tid_il_ref_pics_plus1[m][k]); + if (current->vps_direct_ref_layer_flag[m][k] && + layer_included_in_ols_flag[i][m] && + num_sub_layers_in_layer_in_ols[i][k] < + max_sublayer_needed) + num_sub_layers_in_layer_in_ols[i][k] = + max_sublayer_needed; + } + } + } + } + if (!num_output_layers_in_ols[i]) + return AVERROR_INVALIDDATA; + } + for (i = 1; i < total_num_olss; i++) { + int num_layers_in_ols = 0; + if (current->vps_each_layer_is_an_ols_flag) { + num_layers_in_ols = 1; + } else if (current->vps_ols_mode_idc == 0 || + current->vps_ols_mode_idc == 1) { + num_layers_in_ols = i + 1; + } else if (current->vps_ols_mode_idc == 2) { + for (k = 0, j = 0; k <= current->vps_max_layers_minus1; k++) + if (layer_included_in_ols_flag[i][k]) + j++; + num_layers_in_ols = j; + } + if (num_layers_in_ols > 1) { + num_multi_layer_olss++; + } + } + if (!current->vps_each_layer_is_an_ols_flag && num_multi_layer_olss == 0) + return AVERROR_INVALIDDATA; + } + + for (i = 0; i <= current->vps_num_ptls_minus1; i++) { + CHECK(FUNC(profile_tier_level) (ctx, rw, + current->vps_profile_tier_level + i, + current->vps_pt_present_flag[i], + current->vps_ptl_max_tid[i])); + } + for (i = 0; i < total_num_olss; i++) { + if (current->vps_num_ptls_minus1 > 0 && + current->vps_num_ptls_minus1 + 1 != total_num_olss) { + us(8, vps_ols_ptl_idx[i], 0, current->vps_num_ptls_minus1, 1, i); + } else if (current->vps_num_ptls_minus1 == 0) { + infer(vps_ols_ptl_idx[i], 0); + } else { + infer(vps_ols_ptl_idx[i], i); + } + } + + if (!current->vps_each_layer_is_an_ols_flag) { + uint16_t vps_num_dpb_params; + ue(vps_num_dpb_params_minus1, 0, num_multi_layer_olss - 1); + if (current->vps_each_layer_is_an_ols_flag) + vps_num_dpb_params = 0; + else + vps_num_dpb_params = current->vps_num_dpb_params_minus1 + 1; + + if (current->vps_max_sublayers_minus1 > 0) + flag(vps_sublayer_dpb_params_present_flag); + else + infer(vps_sublayer_dpb_params_present_flag, 0); + + for (i = 0; i < vps_num_dpb_params; i++) { + if (!current->vps_default_ptl_dpb_hrd_max_tid_flag) + us(3, vps_dpb_max_tid[i], 0, current->vps_max_sublayers_minus1, + 1, i); + else + infer(vps_dpb_max_tid[i], current->vps_max_sublayers_minus1); + CHECK(FUNC(dpb_parameters) (ctx, rw, current->vps_dpb_params + i, + current->vps_dpb_max_tid[i], + current-> + vps_sublayer_dpb_params_present_flag)); + } + for (i = 0; i < num_multi_layer_olss; i++) { + ues(vps_ols_dpb_pic_width[i], 0, UINT16_MAX, 1, i); + ues(vps_ols_dpb_pic_height[i], 0, UINT16_MAX, 1, i); + ubs(2, vps_ols_dpb_chroma_format[i], 1, i); + ues(vps_ols_dpb_bitdepth_minus8[i], 0, 8, 1, i); + if (vps_num_dpb_params > 1 + && vps_num_dpb_params != num_multi_layer_olss) + ues(vps_ols_dpb_params_idx[i], 0, vps_num_dpb_params - 1, 1, i); + else if (vps_num_dpb_params == 1) + infer(vps_ols_dpb_params_idx[i], 0); + else + infer(vps_ols_dpb_params_idx[i], i); + } + flag(vps_timing_hrd_params_present_flag); + if (current->vps_timing_hrd_params_present_flag) { + CHECK(FUNC(general_timing_hrd_parameters) (ctx, rw, + ¤t-> + vps_general_timing_hrd_parameters)); + if (current->vps_max_sublayers_minus1 > 0) + flag(vps_sublayer_cpb_params_present_flag); + else + infer(vps_sublayer_cpb_params_present_flag, 0); + ue(vps_num_ols_timing_hrd_params_minus1, 0, + num_multi_layer_olss - 1); + for (i = 0; i <= current->vps_num_ols_timing_hrd_params_minus1; i++) { + uint8_t first_sublayer; + if (!current->vps_default_ptl_dpb_hrd_max_tid_flag) + us(3, vps_hrd_max_tid[i], 0, + current->vps_max_sublayers_minus1, 1, i); + else + infer(vps_hrd_max_tid[i], + current->vps_max_sublayers_minus1); + first_sublayer = current->vps_sublayer_cpb_params_present_flag ? + 0 : current->vps_hrd_max_tid[i]; + CHECK(FUNC(ols_timing_hrd_parameters) + (ctx, rw, ¤t->vps_ols_timing_hrd_parameters, + first_sublayer, current->vps_max_sublayers_minus1, + ¤t->vps_general_timing_hrd_parameters)); + + } + if (current->vps_num_ols_timing_hrd_params_minus1 > 0 && + current->vps_num_ols_timing_hrd_params_minus1 + 1 != + num_multi_layer_olss) { + for (i = 0; i < num_multi_layer_olss; i++) { + ues(vps_ols_timing_hrd_idx[i], 0, + current->vps_num_ols_timing_hrd_params_minus1, 1, i); + } + } else if (current->vps_num_ols_timing_hrd_params_minus1 == 0) { + for (i = 0; i < num_multi_layer_olss; i++) + infer(vps_ols_timing_hrd_idx[i], 0); + } else { + for (i = 0; i < num_multi_layer_olss; i++) + infer(vps_ols_timing_hrd_idx[i], i); + } + } + } + + flag(vps_extension_flag); + if (current->vps_extension_flag) + CHECK(FUNC(extension_data) (ctx, rw, ¤t->extension_data)); + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + + return 0; +} + +static int FUNC(sps_range_extension)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawSPS *current) +{ + int err; + + flag(sps_extended_precision_flag); + if (current->sps_transform_skip_enabled_flag) + flag(sps_ts_residual_coding_rice_present_in_sh_flag); + else + infer(sps_ts_residual_coding_rice_present_in_sh_flag, 0); + flag(sps_rrc_rice_extension_flag); + flag(sps_persistent_rice_adaptation_enabled_flag); + flag(sps_reverse_last_sig_coeff_enabled_flag); + + return 0; +} + +static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawSPS *current) +{ + CodedBitstreamH266Context *h266 = ctx->priv_data; + int err, i, j; + unsigned int ctb_log2_size_y, min_cb_log2_size_y, + min_qt_log2_size_intra_y, min_qt_log2_size_inter_y, + ctb_size_y, max_num_merge_cand, tmp_width_val, tmp_height_val; + uint8_t qp_bd_offset; + + static const uint8_t h266_sub_width_c[] = { + 1, 2, 2, 1 + }; + static const uint8_t h266_sub_height_c[] = { + 1, 2, 1, 1 + }; + + HEADER("Sequence Parameter Set"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, + ¤t->nal_unit_header, VVC_SPS_NUT)); + + ub(4, sps_seq_parameter_set_id); + ub(4, sps_video_parameter_set_id); + if (current->sps_video_parameter_set_id == 0 && !h266->vps[0]) { + H266RawVPS *vps = ff_refstruct_allocz(sizeof(*vps)); + if (!vps) + return AVERROR(ENOMEM); + vps->vps_max_layers_minus1 = 0; + vps->vps_independent_layer_flag[0] = 1; + vps->vps_layer_id[0] = current->nal_unit_header.nuh_layer_id; + h266->vps[0] = vps; + } + + u(3, sps_max_sublayers_minus1, 0, VVC_MAX_SUBLAYERS - 1); + u(2, sps_chroma_format_idc, 0, 3); + u(2, sps_log2_ctu_size_minus5, 0, 3); + ctb_log2_size_y = current->sps_log2_ctu_size_minus5 + 5; + ctb_size_y = 1 << ctb_log2_size_y; + + flag(sps_ptl_dpb_hrd_params_present_flag); + if (current->sps_ptl_dpb_hrd_params_present_flag) { + CHECK(FUNC(profile_tier_level) (ctx, rw, ¤t->profile_tier_level, + 1, current->sps_max_sublayers_minus1)); + } + flag(sps_gdr_enabled_flag); + flag(sps_ref_pic_resampling_enabled_flag); + if (current->sps_ref_pic_resampling_enabled_flag) + flag(sps_res_change_in_clvs_allowed_flag); + else + infer(sps_res_change_in_clvs_allowed_flag, 0); + + ue(sps_pic_width_max_in_luma_samples, 1, VVC_MAX_WIDTH); + ue(sps_pic_height_max_in_luma_samples, 1, VVC_MAX_HEIGHT); + + flag(sps_conformance_window_flag); + if (current->sps_conformance_window_flag) { + uint8_t sub_width_c = h266_sub_width_c[current->sps_chroma_format_idc]; + uint8_t sub_height_c = h266_sub_height_c[current->sps_chroma_format_idc]; + uint16_t width = current->sps_pic_width_max_in_luma_samples / sub_width_c; + uint16_t height = current->sps_pic_height_max_in_luma_samples / sub_height_c; + ue(sps_conf_win_left_offset, 0, width); + ue(sps_conf_win_right_offset, 0, width - current->sps_conf_win_left_offset); + ue(sps_conf_win_top_offset, 0, height); + ue(sps_conf_win_bottom_offset, 0, height - current->sps_conf_win_top_offset); + } else { + infer(sps_conf_win_left_offset, 0); + infer(sps_conf_win_right_offset, 0); + infer(sps_conf_win_top_offset, 0); + infer(sps_conf_win_bottom_offset, 0); + } + + tmp_width_val = AV_CEIL_RSHIFT(current->sps_pic_width_max_in_luma_samples, + ctb_log2_size_y); + tmp_height_val = AV_CEIL_RSHIFT(current->sps_pic_height_max_in_luma_samples, + ctb_log2_size_y); + + flag(sps_subpic_info_present_flag); + if (current->sps_subpic_info_present_flag) { + ue(sps_num_subpics_minus1, 0, VVC_MAX_SLICES - 1); + if (current->sps_num_subpics_minus1 > 0) { + flag(sps_independent_subpics_flag); + flag(sps_subpic_same_size_flag); + } + + if (current->sps_num_subpics_minus1 > 0) { + int wlen = av_ceil_log2(tmp_width_val); + int hlen = av_ceil_log2(tmp_height_val); + infer(sps_subpic_ctu_top_left_x[0], 0); + infer(sps_subpic_ctu_top_left_y[0], 0); + if (current->sps_pic_width_max_in_luma_samples > ctb_size_y) + ubs(wlen, sps_subpic_width_minus1[0], 1, 0); + else + infer(sps_subpic_width_minus1[0], tmp_width_val - 1); + if (current->sps_pic_height_max_in_luma_samples > ctb_size_y) + ubs(hlen, sps_subpic_height_minus1[0], 1, 0); + else + infer(sps_subpic_height_minus1[0], tmp_height_val - 1); + if (!current->sps_independent_subpics_flag) { + flags(sps_subpic_treated_as_pic_flag[0], 1, 0); + flags(sps_loop_filter_across_subpic_enabled_flag[0], 1, 0); + } else { + infer(sps_subpic_treated_as_pic_flag[0], 1); + infer(sps_loop_filter_across_subpic_enabled_flag[0], 1); + } + for (i = 1; i <= current->sps_num_subpics_minus1; i++) { + if (!current->sps_subpic_same_size_flag) { + if (current->sps_pic_width_max_in_luma_samples > ctb_size_y) + ubs(wlen, sps_subpic_ctu_top_left_x[i], 1, i); + else + infer(sps_subpic_ctu_top_left_x[i], 0); + if (current->sps_pic_height_max_in_luma_samples > + ctb_size_y) + ubs(hlen, sps_subpic_ctu_top_left_y[i], 1, i); + else + infer(sps_subpic_ctu_top_left_y[i], 0); + if (i < current->sps_num_subpics_minus1 && + current->sps_pic_width_max_in_luma_samples > + ctb_size_y) { + ubs(wlen, sps_subpic_width_minus1[i], 1, i); + } else { + infer(sps_subpic_width_minus1[i], + tmp_width_val - + current->sps_subpic_ctu_top_left_x[i] - 1); + } + if (i < current->sps_num_subpics_minus1 && + current->sps_pic_height_max_in_luma_samples > + ctb_size_y) { + ubs(hlen, sps_subpic_height_minus1[i], 1, i); + } else { + infer(sps_subpic_height_minus1[i], + tmp_height_val - + current->sps_subpic_ctu_top_left_y[i] - 1); + } + } else { + int num_subpic_cols = tmp_width_val / + (current->sps_subpic_width_minus1[0] + 1); + if (tmp_width_val % (current->sps_subpic_width_minus1[0] + 1) || + tmp_height_val % (current->sps_subpic_width_minus1[0] + 1) || + current->sps_num_subpics_minus1 != + (num_subpic_cols * tmp_height_val / + (current->sps_subpic_height_minus1[0] + 1) - 1)) + return AVERROR_INVALIDDATA; + infer(sps_subpic_ctu_top_left_x[i], + (i % num_subpic_cols) * + (current->sps_subpic_width_minus1[0] + 1)); + infer(sps_subpic_ctu_top_left_y[i], + (i / num_subpic_cols) * + (current->sps_subpic_height_minus1[0] + 1)); + infer(sps_subpic_width_minus1[i], + current->sps_subpic_width_minus1[0]); + infer(sps_subpic_height_minus1[i], + current->sps_subpic_height_minus1[0]); + } + if (!current->sps_independent_subpics_flag) { + flags(sps_subpic_treated_as_pic_flag[i], 1, i); + flags(sps_loop_filter_across_subpic_enabled_flag[i], 1, i); + } else { + infer(sps_subpic_treated_as_pic_flag[i], 1); + infer(sps_loop_filter_across_subpic_enabled_flag[i], 0); + } + } + } else { + infer(sps_subpic_ctu_top_left_x[0], 0); + infer(sps_subpic_ctu_top_left_y[0], 0); + infer(sps_subpic_width_minus1[0], tmp_width_val - 1); + infer(sps_subpic_height_minus1[0], tmp_height_val - 1); + } + ue(sps_subpic_id_len_minus1, 0, 15); + if ((1 << (current->sps_subpic_id_len_minus1 + 1)) < + current->sps_num_subpics_minus1 + 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "sps_subpic_id_len_minus1(%d) is too small\n", + current->sps_subpic_id_len_minus1); + return AVERROR_INVALIDDATA; + } + flag(sps_subpic_id_mapping_explicitly_signalled_flag); + if (current->sps_subpic_id_mapping_explicitly_signalled_flag) { + flag(sps_subpic_id_mapping_present_flag); + if (current->sps_subpic_id_mapping_present_flag) { + for (i = 0; i <= current->sps_num_subpics_minus1; i++) { + ubs(current->sps_subpic_id_len_minus1 + 1, + sps_subpic_id[i], 1, i); + } + } + } + } else { + infer(sps_num_subpics_minus1, 0); + infer(sps_independent_subpics_flag, 1); + infer(sps_subpic_same_size_flag, 0); + infer(sps_subpic_id_mapping_explicitly_signalled_flag, 0); + infer(sps_subpic_ctu_top_left_x[0], 0); + infer(sps_subpic_ctu_top_left_y[0], 0); + infer(sps_subpic_width_minus1[0], tmp_width_val - 1); + infer(sps_subpic_height_minus1[0], tmp_height_val - 1); + } + + + ue(sps_bitdepth_minus8, 0, 8); + qp_bd_offset = 6 * current->sps_bitdepth_minus8; + + flag(sps_entropy_coding_sync_enabled_flag); + flag(sps_entry_point_offsets_present_flag); + + u(4, sps_log2_max_pic_order_cnt_lsb_minus4, 0, 12); + flag(sps_poc_msb_cycle_flag); + if (current->sps_poc_msb_cycle_flag) + ue(sps_poc_msb_cycle_len_minus1, + 0, 32 - current->sps_log2_max_pic_order_cnt_lsb_minus4 - 5); + + u(2, sps_num_extra_ph_bytes, 0, 2); + for (i = 0; i < (current->sps_num_extra_ph_bytes * 8); i++) { + flags(sps_extra_ph_bit_present_flag[i], 1, i); + } + + u(2, sps_num_extra_sh_bytes, 0, 2); + for (i = 0; i < (current->sps_num_extra_sh_bytes * 8); i++) { + flags(sps_extra_sh_bit_present_flag[i], 1, i); + } + + if (current->sps_ptl_dpb_hrd_params_present_flag) { + if (current->sps_max_sublayers_minus1 > 0) + flag(sps_sublayer_dpb_params_flag); + else + infer(sps_sublayer_dpb_params_flag, 0); + CHECK(FUNC(dpb_parameters) (ctx, rw, ¤t->sps_dpb_params, + current->sps_max_sublayers_minus1, + current->sps_sublayer_dpb_params_flag)); + } + + ue(sps_log2_min_luma_coding_block_size_minus2, + 0, FFMIN(4, current->sps_log2_ctu_size_minus5 + 3)); + min_cb_log2_size_y = + current->sps_log2_min_luma_coding_block_size_minus2 + 2; + + flag(sps_partition_constraints_override_enabled_flag); + + ue(sps_log2_diff_min_qt_min_cb_intra_slice_luma, + 0, FFMIN(6, ctb_log2_size_y) - min_cb_log2_size_y); + min_qt_log2_size_intra_y = + current->sps_log2_diff_min_qt_min_cb_intra_slice_luma + + min_cb_log2_size_y; + + ue(sps_max_mtt_hierarchy_depth_intra_slice_luma, + 0, 2 * (ctb_log2_size_y - min_cb_log2_size_y)); + + if (current->sps_max_mtt_hierarchy_depth_intra_slice_luma != 0) { + ue(sps_log2_diff_max_bt_min_qt_intra_slice_luma, + 0, ctb_log2_size_y - min_qt_log2_size_intra_y); + ue(sps_log2_diff_max_tt_min_qt_intra_slice_luma, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_intra_y); + } else { + infer(sps_log2_diff_max_bt_min_qt_intra_slice_luma, 0); + infer(sps_log2_diff_max_tt_min_qt_intra_slice_luma, 0); + } + + if (current->sps_chroma_format_idc != 0) { + flag(sps_qtbtt_dual_tree_intra_flag); + } else { + infer(sps_qtbtt_dual_tree_intra_flag, 0); + } + + if (current->sps_qtbtt_dual_tree_intra_flag) { + ue(sps_log2_diff_min_qt_min_cb_intra_slice_chroma, + 0, FFMIN(6, ctb_log2_size_y) - min_cb_log2_size_y); + ue(sps_max_mtt_hierarchy_depth_intra_slice_chroma, + 0, 2 * (ctb_log2_size_y - min_cb_log2_size_y)); + if (current->sps_max_mtt_hierarchy_depth_intra_slice_chroma != 0) { + unsigned int min_qt_log2_size_intra_c = + current->sps_log2_diff_min_qt_min_cb_intra_slice_chroma + + min_cb_log2_size_y; + ue(sps_log2_diff_max_bt_min_qt_intra_slice_chroma, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_intra_c); + ue(sps_log2_diff_max_tt_min_qt_intra_slice_chroma, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_intra_c); + } + } else { + infer(sps_log2_diff_min_qt_min_cb_intra_slice_chroma, 0); + infer(sps_max_mtt_hierarchy_depth_intra_slice_chroma, 0); + } + if (current->sps_max_mtt_hierarchy_depth_intra_slice_chroma == 0) { + infer(sps_log2_diff_max_bt_min_qt_intra_slice_chroma, 0); + infer(sps_log2_diff_max_tt_min_qt_intra_slice_chroma, 0); + } + + ue(sps_log2_diff_min_qt_min_cb_inter_slice, + 0, FFMIN(6, ctb_log2_size_y) - min_cb_log2_size_y); + min_qt_log2_size_inter_y = + current->sps_log2_diff_min_qt_min_cb_inter_slice + min_cb_log2_size_y; + + ue(sps_max_mtt_hierarchy_depth_inter_slice, + 0, 2 * (ctb_log2_size_y - min_cb_log2_size_y)); + if (current->sps_max_mtt_hierarchy_depth_inter_slice != 0) { + ue(sps_log2_diff_max_bt_min_qt_inter_slice, + 0, ctb_log2_size_y - min_qt_log2_size_inter_y); + ue(sps_log2_diff_max_tt_min_qt_inter_slice, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_inter_y); + } else { + infer(sps_log2_diff_max_bt_min_qt_inter_slice, 0); + infer(sps_log2_diff_max_tt_min_qt_inter_slice, 0); + } + + if (ctb_size_y > 32) + flag(sps_max_luma_transform_size_64_flag); + else + infer(sps_max_luma_transform_size_64_flag, 0); + + flag(sps_transform_skip_enabled_flag); + if (current->sps_transform_skip_enabled_flag) { + ue(sps_log2_transform_skip_max_size_minus2, 0, 3); + flag(sps_bdpcm_enabled_flag); + } + + flag(sps_mts_enabled_flag); + if (current->sps_mts_enabled_flag) { + flag(sps_explicit_mts_intra_enabled_flag); + flag(sps_explicit_mts_inter_enabled_flag); + } else { + infer(sps_explicit_mts_intra_enabled_flag, 0); + infer(sps_explicit_mts_inter_enabled_flag, 0); + } + + flag(sps_lfnst_enabled_flag); + + if (current->sps_chroma_format_idc != 0) { + uint8_t num_qp_tables; + flag(sps_joint_cbcr_enabled_flag); + flag(sps_same_qp_table_for_chroma_flag); + num_qp_tables = current->sps_same_qp_table_for_chroma_flag ? + 1 : (current->sps_joint_cbcr_enabled_flag ? 3 : 2); + for (i = 0; i < num_qp_tables; i++) { + ses(sps_qp_table_start_minus26[i], -26 - qp_bd_offset, 36, 1, i); + ues(sps_num_points_in_qp_table_minus1[i], + 0, 36 - current->sps_qp_table_start_minus26[i], 1, i); + for (j = 0; j <= current->sps_num_points_in_qp_table_minus1[i]; j++) { + uint8_t max = MAX_UINT_BITS(8); + ues(sps_delta_qp_in_val_minus1[i][j], 0, max, 2, i, j); + ues(sps_delta_qp_diff_val[i][j], 0, max, 2, i, j); + } + } + } else { + infer(sps_joint_cbcr_enabled_flag, 0); + infer(sps_same_qp_table_for_chroma_flag, 0); + } + + flag(sps_sao_enabled_flag); + flag(sps_alf_enabled_flag); + if (current->sps_alf_enabled_flag && current->sps_chroma_format_idc) + flag(sps_ccalf_enabled_flag); + else + infer(sps_ccalf_enabled_flag, 0); + flag(sps_lmcs_enabled_flag); + flag(sps_weighted_pred_flag); + flag(sps_weighted_bipred_flag); + flag(sps_long_term_ref_pics_flag); + if (current->sps_video_parameter_set_id > 0) + flag(sps_inter_layer_prediction_enabled_flag); + else + infer(sps_inter_layer_prediction_enabled_flag, 0); + flag(sps_idr_rpl_present_flag); + flag(sps_rpl1_same_as_rpl0_flag); + + for (i = 0; i < (current->sps_rpl1_same_as_rpl0_flag ? 1 : 2); i++) { + ues(sps_num_ref_pic_lists[i], 0, VVC_MAX_REF_PIC_LISTS, 1, i); + for (j = 0; j < current->sps_num_ref_pic_lists[i]; j++) + CHECK(FUNC(ref_pic_list_struct) (ctx, rw, + ¤t-> + sps_ref_pic_list_struct[i][j], i, + j, current)); + } + + if (current->sps_rpl1_same_as_rpl0_flag) { + current->sps_num_ref_pic_lists[1] = current->sps_num_ref_pic_lists[0]; + for (j = 0; j < current->sps_num_ref_pic_lists[0]; j++) + memcpy(¤t->sps_ref_pic_list_struct[1][j], + ¤t->sps_ref_pic_list_struct[0][j], + sizeof(current->sps_ref_pic_list_struct[0][j])); + } + + flag(sps_ref_wraparound_enabled_flag); + + flag(sps_temporal_mvp_enabled_flag); + if (current->sps_temporal_mvp_enabled_flag) + flag(sps_sbtmvp_enabled_flag); + else + infer(sps_sbtmvp_enabled_flag, 0); + + flag(sps_amvr_enabled_flag); + flag(sps_bdof_enabled_flag); + if (current->sps_bdof_enabled_flag) + flag(sps_bdof_control_present_in_ph_flag); + else + infer(sps_bdof_control_present_in_ph_flag, 0); + + flag(sps_smvd_enabled_flag); + flag(sps_dmvr_enabled_flag); + if (current->sps_dmvr_enabled_flag) + flag(sps_dmvr_control_present_in_ph_flag); + else + infer(sps_dmvr_control_present_in_ph_flag, 0); + + flag(sps_mmvd_enabled_flag); + if (current->sps_mmvd_enabled_flag) + flag(sps_mmvd_fullpel_only_enabled_flag); + else + infer(sps_mmvd_fullpel_only_enabled_flag, 0); + + ue(sps_six_minus_max_num_merge_cand, 0, 5); + max_num_merge_cand = 6 - current->sps_six_minus_max_num_merge_cand; + + flag(sps_sbt_enabled_flag); + + flag(sps_affine_enabled_flag); + if (current->sps_affine_enabled_flag) { + ue(sps_five_minus_max_num_subblock_merge_cand, + 0, 5 - current->sps_sbtmvp_enabled_flag); + flag(sps_6param_affine_enabled_flag); + if (current->sps_amvr_enabled_flag) + flag(sps_affine_amvr_enabled_flag); + else + infer(sps_affine_amvr_enabled_flag, 0); + flag(sps_affine_prof_enabled_flag); + if (current->sps_affine_prof_enabled_flag) + flag(sps_prof_control_present_in_ph_flag); + else + infer(sps_prof_control_present_in_ph_flag, 0); + } else { + infer(sps_6param_affine_enabled_flag, 0); + infer(sps_affine_amvr_enabled_flag, 0); + infer(sps_affine_prof_enabled_flag, 0); + infer(sps_prof_control_present_in_ph_flag, 0); + } + + flag(sps_bcw_enabled_flag); + flag(sps_ciip_enabled_flag); + + if (max_num_merge_cand >= 2) { + flag(sps_gpm_enabled_flag); + if (current->sps_gpm_enabled_flag && max_num_merge_cand >= 3) + ue(sps_max_num_merge_cand_minus_max_num_gpm_cand, + 0, max_num_merge_cand - 2); + } else { + infer(sps_gpm_enabled_flag, 0); + } + + ue(sps_log2_parallel_merge_level_minus2, 0, ctb_log2_size_y - 2); + + flag(sps_isp_enabled_flag); + flag(sps_mrl_enabled_flag); + flag(sps_mip_enabled_flag); + + if (current->sps_chroma_format_idc != 0) + flag(sps_cclm_enabled_flag); + else + infer(sps_cclm_enabled_flag, 0); + if (current->sps_chroma_format_idc == 1) { + flag(sps_chroma_horizontal_collocated_flag); + flag(sps_chroma_vertical_collocated_flag); + } else { + infer(sps_chroma_horizontal_collocated_flag, 1); + infer(sps_chroma_vertical_collocated_flag, 1); + } + + flag(sps_palette_enabled_flag); + if (current->sps_chroma_format_idc == 3 && + !current->sps_max_luma_transform_size_64_flag) + flag(sps_act_enabled_flag); + else + infer(sps_act_enabled_flag, 0); + if (current->sps_transform_skip_enabled_flag || + current->sps_palette_enabled_flag) + ue(sps_min_qp_prime_ts, 0, 8); + + flag(sps_ibc_enabled_flag); + if (current->sps_ibc_enabled_flag) + ue(sps_six_minus_max_num_ibc_merge_cand, 0, 5); + + flag(sps_ladf_enabled_flag); + if (current->sps_ladf_enabled_flag) { + ub(2, sps_num_ladf_intervals_minus2); + se(sps_ladf_lowest_interval_qp_offset, -63, 63); + for (i = 0; i < current->sps_num_ladf_intervals_minus2 + 1; i++) { + ses(sps_ladf_qp_offset[i], -63, 63, 1, i); + ues(sps_ladf_delta_threshold_minus1[i], + 0, (2 << (8 + current->sps_bitdepth_minus8)) - 3, 1, i); + } + } + + flag(sps_explicit_scaling_list_enabled_flag); + if (current->sps_lfnst_enabled_flag && + current->sps_explicit_scaling_list_enabled_flag) + flag(sps_scaling_matrix_for_lfnst_disabled_flag); + + if (current->sps_act_enabled_flag && + current->sps_explicit_scaling_list_enabled_flag) + flag(sps_scaling_matrix_for_alternative_colour_space_disabled_flag); + else + infer(sps_scaling_matrix_for_alternative_colour_space_disabled_flag, 0); + if (current->sps_scaling_matrix_for_alternative_colour_space_disabled_flag) + flag(sps_scaling_matrix_designated_colour_space_flag); + + flag(sps_dep_quant_enabled_flag); + flag(sps_sign_data_hiding_enabled_flag); + + flag(sps_virtual_boundaries_enabled_flag); + if (current->sps_virtual_boundaries_enabled_flag) { + flag(sps_virtual_boundaries_present_flag); + if (current->sps_virtual_boundaries_present_flag) { + ue(sps_num_ver_virtual_boundaries, + 0, current->sps_pic_width_max_in_luma_samples <= 8 ? 0 : 3); + for (i = 0; i < current->sps_num_ver_virtual_boundaries; i++) + ues(sps_virtual_boundary_pos_x_minus1[i], + 0, (current->sps_pic_width_max_in_luma_samples + 7) / 8 - 2, + 1, i); + ue(sps_num_hor_virtual_boundaries, + 0, current->sps_pic_height_max_in_luma_samples <= 8 ? 0 : 3); + for (i = 0; i < current->sps_num_hor_virtual_boundaries; i++) + ues(sps_virtual_boundary_pos_y_minus1[i], + 0, (current->sps_pic_height_max_in_luma_samples + 7) / + 8 - 2, 1, i); + } + } else { + infer(sps_virtual_boundaries_present_flag, 0); + infer(sps_num_ver_virtual_boundaries, 0); + infer(sps_num_hor_virtual_boundaries, 0); + } + + if (current->sps_ptl_dpb_hrd_params_present_flag) { + flag(sps_timing_hrd_params_present_flag); + if (current->sps_timing_hrd_params_present_flag) { + uint8_t first_sublayer; + CHECK(FUNC(general_timing_hrd_parameters) (ctx, rw, + ¤t->sps_general_timing_hrd_parameters)); + if (current->sps_max_sublayers_minus1 > 0) + flag(sps_sublayer_cpb_params_present_flag); + else + infer(sps_sublayer_cpb_params_present_flag, 0); + first_sublayer = current->sps_sublayer_cpb_params_present_flag ? + 0 : current->sps_max_sublayers_minus1; + CHECK(FUNC(ols_timing_hrd_parameters) (ctx, rw, + ¤t->sps_ols_timing_hrd_parameters, first_sublayer, + current->sps_max_sublayers_minus1, + ¤t->sps_general_timing_hrd_parameters)); + } + } + + flag(sps_field_seq_flag); + flag(sps_vui_parameters_present_flag); + if (current->sps_vui_parameters_present_flag) { + ue(sps_vui_payload_size_minus1, 0, 1023); + while (byte_alignment(rw) != 0) + fixed(1, sps_vui_alignment_zero_bit, 0); + CHECK(FUNC(vui_payload) (ctx, rw, ¤t->vui, + current->sps_vui_payload_size_minus1 + 1, + current->sps_chroma_format_idc)); + } else { + CHECK(FUNC(vui_parameters_default) (ctx, rw, ¤t->vui)); + } + + flag(sps_extension_flag); + if (current->sps_extension_flag) { + flag(sps_range_extension_flag); + ub(7, sps_extension_7bits); + + if (current->sps_range_extension_flag) { + CHECK(FUNC(sps_range_extension)(ctx, rw, current)); + } else { + infer(sps_extended_precision_flag, 0); + infer(sps_ts_residual_coding_rice_present_in_sh_flag, 0); + infer(sps_rrc_rice_extension_flag, 0); + infer(sps_persistent_rice_adaptation_enabled_flag, 0); + infer(sps_reverse_last_sig_coeff_enabled_flag, 0); + } + } else { + infer(sps_range_extension_flag, 0); + infer(sps_extension_7bits, 0); + infer(sps_extended_precision_flag, 0); + infer(sps_ts_residual_coding_rice_present_in_sh_flag, 0); + infer(sps_rrc_rice_extension_flag, 0); + infer(sps_persistent_rice_adaptation_enabled_flag, 0); + infer(sps_reverse_last_sig_coeff_enabled_flag, 0); + } + + if (current->sps_extension_7bits) + CHECK(FUNC(extension_data)(ctx, rw, ¤t->extension_data)); + + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + + return 0; +} + +static int FUNC(pps) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawPPS *current) +{ + CodedBitstreamH266Context *h266 = ctx->priv_data; + const H266RawSPS *sps; + int err, i; + unsigned int min_cb_size_y, divisor, ctb_size_y, + pic_width_in_ctbs_y, pic_height_in_ctbs_y; + uint8_t sub_width_c, sub_height_c, qp_bd_offset; + + static const uint8_t h266_sub_width_c[] = { + 1, 2, 2, 1 + }; + static const uint8_t h266_sub_height_c[] = { + 1, 2, 1, 1 + }; + + HEADER("Picture Parameter Set"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, + ¤t->nal_unit_header, VVC_PPS_NUT)); + + ub(6, pps_pic_parameter_set_id); + ub(4, pps_seq_parameter_set_id); + sps = h266->sps[current->pps_seq_parameter_set_id]; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + current->pps_seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + + flag(pps_mixed_nalu_types_in_pic_flag); + ue(pps_pic_width_in_luma_samples, + 1, sps->sps_pic_width_max_in_luma_samples); + ue(pps_pic_height_in_luma_samples, + 1, sps->sps_pic_height_max_in_luma_samples); + + min_cb_size_y = 1 << (sps->sps_log2_min_luma_coding_block_size_minus2 + 2); + divisor = FFMAX(min_cb_size_y, 8); + if (current->pps_pic_width_in_luma_samples % divisor || + current->pps_pic_height_in_luma_samples % divisor) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid dimensions: %ux%u not divisible " + "by %u, MinCbSizeY = %u.\n", + current->pps_pic_width_in_luma_samples, + current->pps_pic_height_in_luma_samples, divisor, min_cb_size_y); + return AVERROR_INVALIDDATA; + } + if (!sps->sps_res_change_in_clvs_allowed_flag && + (current->pps_pic_width_in_luma_samples != + sps->sps_pic_width_max_in_luma_samples || + current->pps_pic_height_in_luma_samples != + sps->sps_pic_height_max_in_luma_samples)) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Resoltuion change is not allowed, " + "in max resolution (%ux%u) mismatched with pps(%ux%u).\n", + sps->sps_pic_width_max_in_luma_samples, + sps->sps_pic_height_max_in_luma_samples, + current->pps_pic_width_in_luma_samples, + current->pps_pic_height_in_luma_samples); + return AVERROR_INVALIDDATA; + } + + ctb_size_y = 1 << (sps->sps_log2_ctu_size_minus5 + 5); + if (sps->sps_ref_wraparound_enabled_flag) { + if ((ctb_size_y / min_cb_size_y + 1) > + (current->pps_pic_width_in_luma_samples / min_cb_size_y - 1)) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid width(%u), ctb_size_y = %u, min_cb_size_y = %u.\n", + current->pps_pic_width_in_luma_samples, + ctb_size_y, min_cb_size_y); + return AVERROR_INVALIDDATA; + } + } + + flag(pps_conformance_window_flag); + if (current->pps_pic_width_in_luma_samples == + sps->sps_pic_width_max_in_luma_samples && + current->pps_pic_height_in_luma_samples == + sps->sps_pic_height_max_in_luma_samples && + current->pps_conformance_window_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Conformance window flag should not true.\n"); + return AVERROR_INVALIDDATA; + } + + sub_width_c = h266_sub_width_c[sps->sps_chroma_format_idc]; + sub_height_c = h266_sub_height_c[sps->sps_chroma_format_idc]; + if (current->pps_conformance_window_flag) { + ue(pps_conf_win_left_offset, 0, current->pps_pic_width_in_luma_samples); + ue(pps_conf_win_right_offset, + 0, current->pps_pic_width_in_luma_samples); + ue(pps_conf_win_top_offset, 0, current->pps_pic_height_in_luma_samples); + ue(pps_conf_win_bottom_offset, + 0, current->pps_pic_height_in_luma_samples); + if (sub_width_c * + (current->pps_conf_win_left_offset + + current->pps_conf_win_right_offset) >= + current->pps_pic_width_in_luma_samples || + sub_height_c * + (current->pps_conf_win_top_offset + + current->pps_conf_win_bottom_offset) >= + current->pps_pic_height_in_luma_samples) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid pps conformance window: (%u, %u, %u, %u), " + "resolution is %ux%u, sub wxh is %ux%u.\n", + current->pps_conf_win_left_offset, + current->pps_conf_win_right_offset, + current->pps_conf_win_top_offset, + current->pps_conf_win_bottom_offset, + current->pps_pic_width_in_luma_samples, + current->pps_pic_height_in_luma_samples, + sub_width_c, sub_height_c); + return AVERROR_INVALIDDATA; + } + } else { + if (current->pps_pic_width_in_luma_samples == + sps->sps_pic_width_max_in_luma_samples && + current->pps_pic_height_in_luma_samples == + sps->sps_pic_height_max_in_luma_samples) { + infer(pps_conf_win_left_offset, sps->sps_conf_win_left_offset); + infer(pps_conf_win_right_offset, sps->sps_conf_win_right_offset); + infer(pps_conf_win_top_offset, sps->sps_conf_win_top_offset); + infer(pps_conf_win_bottom_offset, sps->sps_conf_win_bottom_offset); + } else { + infer(pps_conf_win_left_offset, 0); + infer(pps_conf_win_right_offset, 0); + infer(pps_conf_win_top_offset, 0); + infer(pps_conf_win_bottom_offset, 0); + } + + } + + flag(pps_scaling_window_explicit_signalling_flag); + if (!sps->sps_ref_pic_resampling_enabled_flag && + current->pps_scaling_window_explicit_signalling_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid data: sps_ref_pic_resampling_enabled_flag is false, " + "but pps_scaling_window_explicit_signalling_flag is true.\n"); + return AVERROR_INVALIDDATA; + } + if (current->pps_scaling_window_explicit_signalling_flag) { + se(pps_scaling_win_left_offset, + -current->pps_pic_width_in_luma_samples * 15 / sub_width_c, + current->pps_pic_width_in_luma_samples / sub_width_c); + se(pps_scaling_win_right_offset, + -current->pps_pic_width_in_luma_samples * 15 / sub_width_c, + current->pps_pic_width_in_luma_samples / sub_width_c); + se(pps_scaling_win_top_offset, + -current->pps_pic_height_in_luma_samples * 15 / sub_height_c, + current->pps_pic_height_in_luma_samples / sub_height_c); + se(pps_scaling_win_bottom_offset, + -current->pps_pic_height_in_luma_samples * 15 / sub_height_c, + current->pps_pic_height_in_luma_samples / sub_height_c); + } else { + infer(pps_scaling_win_left_offset, current->pps_conf_win_left_offset); + infer(pps_scaling_win_right_offset, current->pps_conf_win_right_offset); + infer(pps_scaling_win_top_offset, current->pps_conf_win_top_offset); + infer(pps_scaling_win_bottom_offset, current->pps_conf_win_bottom_offset); + } + + flag(pps_output_flag_present_flag); + flag(pps_no_pic_partition_flag); + flag(pps_subpic_id_mapping_present_flag); + + if (current->pps_subpic_id_mapping_present_flag) { + if (!current->pps_no_pic_partition_flag) { + ue(pps_num_subpics_minus1, + sps->sps_num_subpics_minus1, sps->sps_num_subpics_minus1); + } else { + infer(pps_num_subpics_minus1, 0); + } + ue(pps_subpic_id_len_minus1, sps->sps_subpic_id_len_minus1, + sps->sps_subpic_id_len_minus1); + for (i = 0; i <= current->pps_num_subpics_minus1; i++) { + ubs(sps->sps_subpic_id_len_minus1 + 1, pps_subpic_id[i], 1, i); + } + } + + for (i = 0; i <= sps->sps_num_subpics_minus1; i++) { + if (sps->sps_subpic_id_mapping_explicitly_signalled_flag) + current->sub_pic_id_val[i] = current->pps_subpic_id_mapping_present_flag + ? current->pps_subpic_id[i] + : sps->sps_subpic_id[i]; + else + current->sub_pic_id_val[i] = i; + } + + pic_width_in_ctbs_y = AV_CEIL_RSHIFT + (current->pps_pic_width_in_luma_samples, (sps->sps_log2_ctu_size_minus5 + 5)); + pic_height_in_ctbs_y = AV_CEIL_RSHIFT( + current->pps_pic_height_in_luma_samples,(sps->sps_log2_ctu_size_minus5 + 5)); + if (!current->pps_no_pic_partition_flag) { + unsigned int exp_tile_width = 0, exp_tile_height = 0; + unsigned int unified_size, remaining_size; + + u(2, pps_log2_ctu_size_minus5, + sps->sps_log2_ctu_size_minus5, sps->sps_log2_ctu_size_minus5); + ue(pps_num_exp_tile_columns_minus1, + 0, FFMIN(pic_width_in_ctbs_y - 1, VVC_MAX_TILE_COLUMNS - 1)); + ue(pps_num_exp_tile_rows_minus1, + 0, FFMIN(pic_height_in_ctbs_y - 1, VVC_MAX_TILE_ROWS - 1)); + + for (i = 0; i <= current->pps_num_exp_tile_columns_minus1; i++) { + ues(pps_tile_column_width_minus1[i], + 0, pic_width_in_ctbs_y - exp_tile_width - 1, 1, i); + exp_tile_width += current->pps_tile_column_width_minus1[i] + 1; + } + for (i = 0; i <= current->pps_num_exp_tile_rows_minus1; i++) { + ues(pps_tile_row_height_minus1[i], + 0, pic_height_in_ctbs_y - exp_tile_height - 1, 1, i); + exp_tile_height += current->pps_tile_row_height_minus1[i] + 1; + } + + remaining_size = pic_width_in_ctbs_y; + for (i = 0; i <= current->pps_num_exp_tile_columns_minus1; i++) { + if (current->pps_tile_column_width_minus1[i] >= remaining_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Tile column width(%d) exceeds picture width\n",i); + return AVERROR_INVALIDDATA; + } + current->col_width_val[i] = current->pps_tile_column_width_minus1[i] + 1; + remaining_size -= (current->pps_tile_column_width_minus1[i] + 1); + } + unified_size = current->pps_tile_column_width_minus1[i - 1] + 1; + while (remaining_size > 0) { + if (current->num_tile_columns > VVC_MAX_TILE_COLUMNS) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "NumTileColumns(%d) > than VVC_MAX_TILE_COLUMNS(%d)\n", + current->num_tile_columns, VVC_MAX_TILE_COLUMNS); + return AVERROR_INVALIDDATA; + } + unified_size = FFMIN(remaining_size, unified_size); + current->col_width_val[i] = unified_size; + remaining_size -= unified_size; + i++; + } + current->num_tile_columns = i; + if (current->num_tile_columns > VVC_MAX_TILE_COLUMNS) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "NumTileColumns(%d) > than VVC_MAX_TILE_COLUMNS(%d)\n", + current->num_tile_columns, VVC_MAX_TILE_COLUMNS); + return AVERROR_INVALIDDATA; + } + + remaining_size = pic_height_in_ctbs_y; + for (i = 0; i <= current->pps_num_exp_tile_rows_minus1; i++) { + if (current->pps_tile_row_height_minus1[i] >= remaining_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Tile row height(%d) exceeds picture height\n",i); + return AVERROR_INVALIDDATA; + } + current->row_height_val[i] = current->pps_tile_row_height_minus1[i] + 1; + remaining_size -= (current->pps_tile_row_height_minus1[i] + 1); + } + unified_size = current->pps_tile_row_height_minus1[i - 1] + 1; + + while (remaining_size > 0) { + unified_size = FFMIN(remaining_size, unified_size); + current->row_height_val[i] = unified_size; + remaining_size -= unified_size; + i++; + } + current->num_tile_rows=i; + if (current->num_tile_rows > VVC_MAX_TILE_ROWS) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "NumTileRows(%d) > than VVC_MAX_TILE_ROWS(%d)\n", + current->num_tile_rows, VVC_MAX_TILE_ROWS); + return AVERROR_INVALIDDATA; + } + + current->num_tiles_in_pic = current->num_tile_columns * + current->num_tile_rows; + if (current->num_tiles_in_pic > VVC_MAX_TILES_PER_AU) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "NumTilesInPic(%d) > than VVC_MAX_TILES_PER_AU(%d)\n", + current->num_tiles_in_pic, VVC_MAX_TILES_PER_AU); + return AVERROR_INVALIDDATA; + } + + if (current->num_tiles_in_pic > 1) { + flag(pps_loop_filter_across_tiles_enabled_flag); + flag(pps_rect_slice_flag); + } else { + infer(pps_loop_filter_across_tiles_enabled_flag, 0); + infer(pps_rect_slice_flag, 1); + } + if (current->pps_rect_slice_flag) + flag(pps_single_slice_per_subpic_flag); + else + infer(pps_single_slice_per_subpic_flag, 1); + if (current->pps_rect_slice_flag && + !current->pps_single_slice_per_subpic_flag) { + int j; + uint16_t tile_idx = 0, tile_x, tile_y, ctu_x, ctu_y; + uint16_t slice_top_left_ctu_x[VVC_MAX_SLICES]; + uint16_t slice_top_left_ctu_y[VVC_MAX_SLICES]; + ue(pps_num_slices_in_pic_minus1, 0, VVC_MAX_SLICES - 1); + if (current->pps_num_slices_in_pic_minus1 > 1) + flag(pps_tile_idx_delta_present_flag); + else + infer(pps_tile_idx_delta_present_flag, 0); + for (i = 0; i < current->pps_num_slices_in_pic_minus1; i++) { + tile_x = tile_idx % current->num_tile_columns; + tile_y = tile_idx / current->num_tile_columns; + if (tile_x != current->num_tile_columns - 1) { + ues(pps_slice_width_in_tiles_minus1[i], + 0, current->num_tile_columns - 1, 1, i); + } else { + infer(pps_slice_width_in_tiles_minus1[i], 0); + } + if (tile_y != current->num_tile_rows - 1 && + (current->pps_tile_idx_delta_present_flag || tile_x == 0)) { + ues(pps_slice_height_in_tiles_minus1[i], + 0, current->num_tile_rows - 1, 1, i); + } else { + if (tile_y == current->num_tile_rows - 1) + infer(pps_slice_height_in_tiles_minus1[i], 0); + else + infer(pps_slice_height_in_tiles_minus1[i], + current->pps_slice_height_in_tiles_minus1[i - 1]); + } + + ctu_x = ctu_y = 0; + for (j = 0; j < tile_x; j++) { + ctu_x += current->col_width_val[j]; + } + for (j = 0; j < tile_y; j++) { + ctu_y += current->row_height_val[j]; + } + if (current->pps_slice_width_in_tiles_minus1[i] == 0 && + current->pps_slice_height_in_tiles_minus1[i] == 0 && + current->row_height_val[tile_y] > 1) { + int num_slices_in_tile, + uniform_slice_height, remaining_height_in_ctbs_y; + remaining_height_in_ctbs_y = + current->row_height_val[tile_y]; + ues(pps_num_exp_slices_in_tile[i], + 0, current->row_height_val[tile_y] - 1, 1, i); + if (current->pps_num_exp_slices_in_tile[i] == 0) { + num_slices_in_tile = 1; + current->slice_height_in_ctus[i] = current->row_height_val[tile_y]; + slice_top_left_ctu_x[i] = ctu_x; + slice_top_left_ctu_y[i] = ctu_y; + } else { + uint16_t slice_height_in_ctus; + for (j = 0; j < current->pps_num_exp_slices_in_tile[i]; + j++) { + ues(pps_exp_slice_height_in_ctus_minus1[i][j], 0, + current->row_height_val[tile_y] - 1, 2, + i, j); + slice_height_in_ctus = + current-> + pps_exp_slice_height_in_ctus_minus1[i][j] + 1; + + current->slice_height_in_ctus[i + j] = + slice_height_in_ctus; + slice_top_left_ctu_x[i + j] = ctu_x; + slice_top_left_ctu_y[i + j] = ctu_y; + ctu_y += slice_height_in_ctus; + + remaining_height_in_ctbs_y -= slice_height_in_ctus; + } + uniform_slice_height = 1 + + (j == 0 ? current->row_height_val[tile_y] - 1: + current->pps_exp_slice_height_in_ctus_minus1[i][j-1]); + while (remaining_height_in_ctbs_y > uniform_slice_height) { + current->slice_height_in_ctus[i + j] = + uniform_slice_height; + slice_top_left_ctu_x[i + j] = ctu_x; + slice_top_left_ctu_y[i + j] = ctu_y; + ctu_y += uniform_slice_height; + + remaining_height_in_ctbs_y -= uniform_slice_height; + j++; + } + if (remaining_height_in_ctbs_y > 0) { + current->slice_height_in_ctus[i + j] = + remaining_height_in_ctbs_y; + slice_top_left_ctu_x[i + j] = ctu_x; + slice_top_left_ctu_y[i + j] = ctu_y; + j++; + } + num_slices_in_tile = j; + } + i += num_slices_in_tile - 1; + } else { + uint16_t height = 0; + infer(pps_num_exp_slices_in_tile[i], 0); + for (j = 0; + j <= current->pps_slice_height_in_tiles_minus1[i]; + j++) { + height += + current->row_height_val[tile_y + j]; + } + current->slice_height_in_ctus[i] = height; + + slice_top_left_ctu_x[i] = ctu_x; + slice_top_left_ctu_y[i] = ctu_y; + } + if (i < current->pps_num_slices_in_pic_minus1) { + if (current->pps_tile_idx_delta_present_flag) { + // Two conditions must be met: + // 1. −NumTilesInPic + 1 <= pps_tile_idx_delta_val[i] <= NumTilesInPic − 1 + // 2. 0 <= tile_idx + pps_tile_idx_delta_val[i] <= NumTilesInPic − 1 + // Combining these conditions yields: -tile_idx <= pps_tile_idx_delta_val[i] <= NumTilesInPic - 1 - tile_idx + ses(pps_tile_idx_delta_val[i], + -tile_idx, current->num_tiles_in_pic - 1 - tile_idx, 1, i); + if (current->pps_tile_idx_delta_val[i] == 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "pps_tile_idx_delta_val[i] shall not be equal to 0.\n"); + } + tile_idx += current->pps_tile_idx_delta_val[i]; + } else { + infer(pps_tile_idx_delta_val[i], 0); + tile_idx += + current->pps_slice_width_in_tiles_minus1[i] + 1; + if (tile_idx % current->num_tile_columns == 0) { + tile_idx += + current->pps_slice_height_in_tiles_minus1[i] * + current->num_tile_columns; + } + } + } + } + if (i == current->pps_num_slices_in_pic_minus1) { + uint16_t height = 0; + + tile_x = tile_idx % current->num_tile_columns; + tile_y = tile_idx / current->num_tile_columns; + if (tile_y >= current->num_tile_rows) + return AVERROR_INVALIDDATA; + + ctu_x = 0, ctu_y = 0; + for (j = 0; j < tile_x; j++) { + ctu_x += current->col_width_val[j]; + } + for (j = 0; j < tile_y; j++) { + ctu_y += current->row_height_val[j]; + } + slice_top_left_ctu_x[i] = ctu_x; + slice_top_left_ctu_y[i] = ctu_y; + + current->pps_slice_width_in_tiles_minus1[i] = + current->num_tile_columns - tile_x - 1; + current->pps_slice_height_in_tiles_minus1[i] = + current->num_tile_rows - tile_y - 1; + + for (j = 0; j <= current->pps_slice_height_in_tiles_minus1[i]; + j++) { + height += + current->row_height_val[tile_y + j]; + } + current->slice_height_in_ctus[i] = height; + + infer(pps_num_exp_slices_in_tile[i], 0); + } + //now, we got all slice information, let's resolve NumSlicesInSubpic + for (i = 0; i <= sps->sps_num_subpics_minus1; i++) { + current->num_slices_in_subpic[i] = 0; + for (j = 0; j <= current->pps_num_slices_in_pic_minus1; j++) { + uint16_t pos_x = 0, pos_y = 0; + pos_x = slice_top_left_ctu_x[j]; + pos_y = slice_top_left_ctu_y[j]; + if ((pos_x >= sps->sps_subpic_ctu_top_left_x[i]) && + (pos_x < + sps->sps_subpic_ctu_top_left_x[i] + + sps->sps_subpic_width_minus1[i] + 1) && + (pos_y >= sps->sps_subpic_ctu_top_left_y[i]) && + (pos_y < sps->sps_subpic_ctu_top_left_y[i] + + sps->sps_subpic_height_minus1[i] + 1)) { + current->num_slices_in_subpic[i]++; + } + } + } + } else { + if (current->pps_no_pic_partition_flag) + infer(pps_num_slices_in_pic_minus1, 0); + else if (current->pps_single_slice_per_subpic_flag) + infer(pps_num_slices_in_pic_minus1, + sps->sps_num_subpics_minus1); + // else? + } + if (!current->pps_rect_slice_flag || + current->pps_single_slice_per_subpic_flag || + current->pps_num_slices_in_pic_minus1 > 0) + flag(pps_loop_filter_across_slices_enabled_flag); + else + infer(pps_loop_filter_across_slices_enabled_flag, 0); + } else { + infer(pps_num_exp_tile_columns_minus1, 0); + infer(pps_tile_column_width_minus1[0], pic_width_in_ctbs_y - 1); + infer(pps_num_exp_tile_rows_minus1, 0); + infer(pps_tile_row_height_minus1[0], pic_height_in_ctbs_y - 1); + current->col_width_val[0] = pic_width_in_ctbs_y; + current->row_height_val[0] = pic_height_in_ctbs_y; + current->num_tile_columns = 1; + current->num_tile_rows = 1; + current->num_tiles_in_pic = 1; + } + + flag(pps_cabac_init_present_flag); + for (i = 0; i < 2; i++) + ues(pps_num_ref_idx_default_active_minus1[i], 0, 14, 1, i); + flag(pps_rpl1_idx_present_flag); + flag(pps_weighted_pred_flag); + flag(pps_weighted_bipred_flag); + flag(pps_ref_wraparound_enabled_flag); + if (current->pps_ref_wraparound_enabled_flag) { + ue(pps_pic_width_minus_wraparound_offset, + 0, (current->pps_pic_width_in_luma_samples / min_cb_size_y) + - (ctb_size_y / min_cb_size_y) - 2); + } + + qp_bd_offset = 6 * sps->sps_bitdepth_minus8; + se(pps_init_qp_minus26, -(26 + qp_bd_offset), 37); + flag(pps_cu_qp_delta_enabled_flag); + flag(pps_chroma_tool_offsets_present_flag); + if (current->pps_chroma_tool_offsets_present_flag) { + se(pps_cb_qp_offset, -12, 12); + se(pps_cr_qp_offset, -12, 12); + flag(pps_joint_cbcr_qp_offset_present_flag); + if (current->pps_joint_cbcr_qp_offset_present_flag) + se(pps_joint_cbcr_qp_offset_value, -12, 12); + else + infer(pps_joint_cbcr_qp_offset_value, 0); + flag(pps_slice_chroma_qp_offsets_present_flag); + flag(pps_cu_chroma_qp_offset_list_enabled_flag); + if (current->pps_cu_chroma_qp_offset_list_enabled_flag) { + ue(pps_chroma_qp_offset_list_len_minus1, 0, 5); + for (i = 0; i <= current->pps_chroma_qp_offset_list_len_minus1; i++) { + ses(pps_cb_qp_offset_list[i], -12, 12, 1, i); + ses(pps_cr_qp_offset_list[i], -12, 12, 1, i); + if (current->pps_joint_cbcr_qp_offset_present_flag) + ses(pps_joint_cbcr_qp_offset_list[i], -12, 12, 1, i); + else + infer(pps_joint_cbcr_qp_offset_list[i], 0); + } + } + } else { + infer(pps_cb_qp_offset, 0); + infer(pps_cr_qp_offset, 0); + infer(pps_joint_cbcr_qp_offset_present_flag, 0); + infer(pps_joint_cbcr_qp_offset_value, 0); + infer(pps_slice_chroma_qp_offsets_present_flag, 0); + infer(pps_cu_chroma_qp_offset_list_enabled_flag, 0); + } + flag(pps_deblocking_filter_control_present_flag); + if (current->pps_deblocking_filter_control_present_flag) { + flag(pps_deblocking_filter_override_enabled_flag); + flag(pps_deblocking_filter_disabled_flag); + if (!current->pps_no_pic_partition_flag && + current->pps_deblocking_filter_override_enabled_flag) + flag(pps_dbf_info_in_ph_flag); + else + infer(pps_dbf_info_in_ph_flag, 0); + if (!current->pps_deblocking_filter_disabled_flag) { + se(pps_luma_beta_offset_div2, -12, 12); + se(pps_luma_tc_offset_div2, -12, 12); + if (current->pps_chroma_tool_offsets_present_flag) { + se(pps_cb_beta_offset_div2, -12, 12); + se(pps_cb_tc_offset_div2, -12, 12); + se(pps_cr_beta_offset_div2, -12, 12); + se(pps_cr_tc_offset_div2, -12, 12); + } else { + infer(pps_cb_beta_offset_div2, + current->pps_luma_beta_offset_div2); + infer(pps_cb_tc_offset_div2, current->pps_luma_tc_offset_div2); + infer(pps_cr_beta_offset_div2, + current->pps_luma_beta_offset_div2); + infer(pps_cr_tc_offset_div2, current->pps_luma_tc_offset_div2); + } + } else { + infer(pps_luma_beta_offset_div2, 0); + infer(pps_luma_tc_offset_div2, 0); + infer(pps_cb_beta_offset_div2, 0); + infer(pps_cb_tc_offset_div2, 0); + infer(pps_cr_beta_offset_div2, 0); + infer(pps_cr_tc_offset_div2, 0); + } + } else { + infer(pps_deblocking_filter_override_enabled_flag, 0); + infer(pps_deblocking_filter_disabled_flag, 0); + infer(pps_dbf_info_in_ph_flag, 0); + infer(pps_luma_beta_offset_div2, 0); + infer(pps_luma_tc_offset_div2, 0); + infer(pps_cb_beta_offset_div2, 0); + infer(pps_cb_tc_offset_div2, 0); + infer(pps_cr_beta_offset_div2, 0); + infer(pps_cr_tc_offset_div2, 0); + } + + if (!current->pps_no_pic_partition_flag) { + flag(pps_rpl_info_in_ph_flag); + flag(pps_sao_info_in_ph_flag); + flag(pps_alf_info_in_ph_flag); + if ((current->pps_weighted_pred_flag || + current->pps_weighted_bipred_flag) && + current->pps_rpl_info_in_ph_flag) + flag(pps_wp_info_in_ph_flag); + flag(pps_qp_delta_info_in_ph_flag); + } + flag(pps_picture_header_extension_present_flag); + flag(pps_slice_header_extension_present_flag); + + flag(pps_extension_flag); + if (current->pps_extension_flag) + CHECK(FUNC(extension_data) (ctx, rw, ¤t->extension_data)); + + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + return 0; +} + +static int FUNC(alf_data)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawAPS *current) +{ + int err, j, k; + + flag(alf_luma_filter_signal_flag); + + if (current->aps_chroma_present_flag) { + flag(alf_chroma_filter_signal_flag); + flag(alf_cc_cb_filter_signal_flag); + flag(alf_cc_cr_filter_signal_flag); + } else { + infer(alf_chroma_filter_signal_flag, 0); + infer(alf_cc_cb_filter_signal_flag, 0); + infer(alf_cc_cr_filter_signal_flag, 0); + } + + if (current->alf_luma_filter_signal_flag) { + flag(alf_luma_clip_flag); + ue(alf_luma_num_filters_signalled_minus1, 0, VVC_NUM_ALF_FILTERS - 1); + if (current->alf_luma_num_filters_signalled_minus1 > 0) { + unsigned int bits = av_ceil_log2(current->alf_luma_num_filters_signalled_minus1 + 1); + for (int filt_idx = 0; filt_idx < VVC_NUM_ALF_FILTERS; filt_idx++) + us(bits, alf_luma_coeff_delta_idx[filt_idx], + 0, current->alf_luma_num_filters_signalled_minus1, + 1, filt_idx); + } + for (int sf_idx = 0; sf_idx <= current->alf_luma_num_filters_signalled_minus1; sf_idx++) + for (j = 0; j < 12; j++) { + ues(alf_luma_coeff_abs[sf_idx][j], 0, 128, 2, sf_idx, j); + if (current->alf_luma_coeff_abs[sf_idx][j]) + ubs(1, alf_luma_coeff_sign[sf_idx][j], 2, sf_idx, j); + else + infer(alf_luma_coeff_sign[sf_idx][j], 0); + } + } else { + infer(alf_luma_clip_flag, 0); + infer(alf_luma_num_filters_signalled_minus1, 0); + } + for (int sf_idx = 0; sf_idx <= current->alf_luma_num_filters_signalled_minus1; sf_idx++) { + for (j = 0; j < 12; j++) { + if (current->alf_luma_clip_flag) + ubs(2, alf_luma_clip_idx[sf_idx][j], 2, sf_idx, j); + else + infer(alf_luma_clip_idx[sf_idx][j], 0); + } + } + + if (current->alf_chroma_filter_signal_flag) { + flag(alf_chroma_clip_flag); + ue(alf_chroma_num_alt_filters_minus1, 0, 7); + } else { + infer(alf_chroma_clip_flag, 0); + infer(alf_chroma_num_alt_filters_minus1, 0); + } + for (int alt_idx = 0; alt_idx <= current->alf_chroma_num_alt_filters_minus1; alt_idx++) { + for (j = 0; j < 6; j++) { + if (current->alf_chroma_filter_signal_flag) + ues(alf_chroma_coeff_abs[alt_idx][j], 0, 128, 2, alt_idx, j); + else + infer(alf_chroma_coeff_abs[alt_idx][j], 0); + if (current->alf_chroma_coeff_abs[alt_idx][j] > 0) + ubs(1, alf_chroma_coeff_sign[alt_idx][j], 2, alt_idx, j); + else + infer(alf_chroma_coeff_sign[alt_idx][j], 0); + } + for (j = 0; j < 6; j++) { + if (current->alf_chroma_clip_flag) + ubs(2, alf_chroma_clip_idx[alt_idx][j], 2, alt_idx, j); + else + infer(alf_chroma_clip_idx[alt_idx][j], 0); + } + } + + if (current->alf_cc_cb_filter_signal_flag) + ue(alf_cc_cb_filters_signalled_minus1, 0, 3); + else + infer(alf_cc_cb_filters_signalled_minus1, 0); + for (k = 0; k <= current->alf_cc_cb_filters_signalled_minus1; k++) { + for (j = 0; j < 7; j++) { + if (current->alf_cc_cb_filter_signal_flag) + ubs(3, alf_cc_cb_mapped_coeff_abs[k][j], 2, k, j); + else + infer(alf_cc_cb_mapped_coeff_abs[k][j], 0); + if (current->alf_cc_cb_mapped_coeff_abs[k][j]) + ubs(1, alf_cc_cb_coeff_sign[k][j], 2, k, j); + else + infer(alf_cc_cb_coeff_sign[k][j], 0); + } + } + + if (current->alf_cc_cr_filter_signal_flag) + ue(alf_cc_cr_filters_signalled_minus1, 0, 3); + else + infer(alf_cc_cr_filters_signalled_minus1, 0); + for (k = 0; k < current->alf_cc_cr_filters_signalled_minus1 + 1; k++) { + for (j = 0; j < 7; j++) { + if (current->alf_cc_cr_filter_signal_flag) + ubs(3, alf_cc_cr_mapped_coeff_abs[k][j], 2, k, j); + else + infer(alf_cc_cr_mapped_coeff_abs[k][j], 0); + if (current->alf_cc_cr_mapped_coeff_abs[k][j]) + ubs(1, alf_cc_cr_coeff_sign[k][j], 2, k, j); + else + infer(alf_cc_cr_coeff_sign[k][j], 0); + } + } + + return 0; +} + +static int FUNC(lmcs_data)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawAPS *current) +{ + int err, i, lmcs_max_bin_idx; + + ue(lmcs_min_bin_idx, 0, 15); + ue(lmcs_delta_max_bin_idx, 0, 15); + ue(lmcs_delta_cw_prec_minus1, 0, 14); + + lmcs_max_bin_idx = 15 - current->lmcs_delta_max_bin_idx; + + if (lmcs_max_bin_idx < current->lmcs_min_bin_idx) + return AVERROR_INVALIDDATA; + + for (i = current->lmcs_min_bin_idx; i <= lmcs_max_bin_idx; i++) { + ubs(current->lmcs_delta_cw_prec_minus1 + 1, lmcs_delta_abs_cw[i], 1, i); + if (current->lmcs_delta_abs_cw[i] > 0) + flags(lmcs_delta_sign_cw_flag[i], 1, i); + else + infer(lmcs_delta_sign_cw_flag[i], 0); + } + + if (current->aps_chroma_present_flag) { + ub(3, lmcs_delta_abs_crs); + if (current->lmcs_delta_abs_crs > 0) + flag(lmcs_delta_sign_crs_flag); + else + infer(lmcs_delta_sign_crs_flag, 0); + } else { + infer(lmcs_delta_abs_crs, 0); + infer(lmcs_delta_sign_crs_flag, 0); + } + + return 0; +} + +static int FUNC(scaling_list_data)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawAPS *current) +{ + // 7.4.3.4, deriving DiagScanOrder + static const uint8_t diag_scan_order[64][2] = { + { 0, 0, }, { 0, 1, }, { 1, 0, }, { 0, 2, }, { 1, 1, }, { 2, 0, }, { 0, 3, }, { 1, 2, }, + { 2, 1, }, { 3, 0, }, { 0, 4, }, { 1, 3, }, { 2, 2, }, { 3, 1, }, { 4, 0, }, { 0, 5, }, + { 1, 4, }, { 2, 3, }, { 3, 2, }, { 4, 1, }, { 5, 0, }, { 0, 6, }, { 1, 5, }, { 2, 4, }, + { 3, 3, }, { 4, 2, }, { 5, 1, }, { 6, 0, }, { 0, 7, }, { 1, 6, }, { 2, 5, }, { 3, 4, }, + { 4, 3, }, { 5, 2, }, { 6, 1, }, { 7, 0, }, { 1, 7, }, { 2, 6, }, { 3, 5, }, { 4, 4, }, + { 5, 3, }, { 6, 2, }, { 7, 1, }, { 2, 7, }, { 3, 6, }, { 4, 5, }, { 5, 4, }, { 6, 3, }, + { 7, 2, }, { 3, 7, }, { 4, 6, }, { 5, 5, }, { 6, 4, }, { 7, 3, }, { 4, 7, }, { 5, 6, }, + { 6, 5, }, { 7, 4, }, { 5, 7, }, { 6, 6, }, { 7, 5, }, { 6, 7, }, { 7, 6, }, { 7, 7, }, }; + int err; + + for (int id = 0; id < 28; id ++) { + if (current->aps_chroma_present_flag || id % 3 == 2 || id == 27) { + flags(scaling_list_copy_mode_flag[id], 1, id); + if (!current->scaling_list_copy_mode_flag[id]) + flags(scaling_list_pred_mode_flag[id], 1, id); + else + infer(scaling_list_pred_mode_flag[id], 0); + if ((current->scaling_list_copy_mode_flag[id] || + current->scaling_list_pred_mode_flag[id]) && + id != 0 && id != 2 && id != 8) { + int max_id_delta = (id < 2) ? id : ((id < 8) ? (id - 2) : (id - 8)); + ues(scaling_list_pred_id_delta[id], 0, max_id_delta, 1, id); + } + if (!current->scaling_list_copy_mode_flag[id]) { + int matrix_size = id < 2 ? 2 : (id < 8 ? 4 : 8); + if (id > 13) { + int idx = id - 14; + ses(scaling_list_dc_coef[idx], -128, 127, 1, idx); + } + for (int i = 0; i < matrix_size * matrix_size; i++) { + int x = diag_scan_order[i][0]; + int y = diag_scan_order[i][1]; + if (!(id > 25 && x >= 4 && y >= 4)) + ses(scaling_list_delta_coef[id][i], -128, 127, 2, id, i); + } + } else if (id > 13) { + int idx = id - 14; + infer(scaling_list_dc_coef[idx], 0); + } + } else { + infer(scaling_list_copy_mode_flag[id], 1); + infer(scaling_list_pred_mode_flag[id], 0); + } + } + + return 0; +} + +static int FUNC(aps)(CodedBitstreamContext *ctx, RWContext *rw, + H266RawAPS *current, int prefix) +{ + int aps_id_max = MAX_UINT_BITS(5); + int err; + + if (prefix) + HEADER("Prefix Adaptation parameter set"); + else + HEADER("Suffix Adaptation parameter set"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + prefix ? VVC_PREFIX_APS_NUT + : VVC_SUFFIX_APS_NUT)); + + ub(3, aps_params_type); + if (current->aps_params_type == VVC_ASP_TYPE_ALF || + current->aps_params_type == VVC_ASP_TYPE_SCALING) + aps_id_max = 7; + else if (current->aps_params_type == VVC_ASP_TYPE_LMCS) + aps_id_max = 3; + u(5, aps_adaptation_parameter_set_id, 0, aps_id_max); + flag(aps_chroma_present_flag); + if (current->aps_params_type == VVC_ASP_TYPE_ALF) + CHECK(FUNC(alf_data)(ctx, rw, current)); + else if(current->aps_params_type == VVC_ASP_TYPE_LMCS) + CHECK(FUNC(lmcs_data)(ctx, rw, current)); + else if (current->aps_params_type == VVC_ASP_TYPE_SCALING) + CHECK(FUNC(scaling_list_data)(ctx, rw, current)); + flag(aps_extension_flag); + if (current->aps_extension_flag) + CHECK(FUNC(extension_data) (ctx, rw, ¤t->extension_data)); + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + + return 0; +} + +static int FUNC(aud) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawAUD *current) +{ + int err; + + HEADER("Access Unit Delimiter"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, + ¤t->nal_unit_header, VVC_AUD_NUT)); + + flag(aud_irap_or_gdr_flag); + u(3, aud_pic_type, 0, 2); + + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + return 0; +} + +static int FUNC(pred_weight_table) (CodedBitstreamContext *ctx, RWContext *rw, + const H266RawSPS *sps, + const H266RawPPS *pps, + const H266RefPicLists *ref_lists, + uint8_t num_ref_idx_active[2], + H266RawPredWeightTable *current) +{ + int err, i, j; + ue(luma_log2_weight_denom, 0, 7); + if (sps->sps_chroma_format_idc != 0) { + se(delta_chroma_log2_weight_denom, + -current->luma_log2_weight_denom, + 7 - current->luma_log2_weight_denom); + } else { + infer(delta_chroma_log2_weight_denom, 0); + } + if (pps->pps_wp_info_in_ph_flag) { + ue(num_l0_weights, 0, + FFMIN(15, ref_lists->rpl_ref_list[0].num_ref_entries)); + infer(num_weights_l0, current->num_l0_weights); + } else { + infer(num_weights_l0, num_ref_idx_active[0]); + } + for (i = 0; i < current->num_weights_l0; i++) { + flags(luma_weight_l0_flag[i], 1, i); + } + if (sps->sps_chroma_format_idc != 0) { + for (i = 0; i < current->num_weights_l0; i++) + flags(chroma_weight_l0_flag[i], 1, i); + } + for (i = 0; i < current->num_weights_l0; i++) { + if (current->luma_weight_l0_flag[i]) { + ses(delta_luma_weight_l0[i], -128, 127, 1, i); + ses(luma_offset_l0[i], -128, 127, 1, i); + } else { + infer(delta_luma_weight_l0[i], 0); + infer(luma_offset_l0[i], 0); + } + if (current->chroma_weight_l0_flag[i]) { + for (j = 0; j < 2; j++) { + ses(delta_chroma_weight_l0[i][j], -128, 127, 2, i, j); + ses(delta_chroma_offset_l0[i][j], -4 * 128, 4 * 127, 2, i, j); + } + } + } + + if (pps->pps_weighted_bipred_flag && + ref_lists->rpl_ref_list[1].num_ref_entries > 0) { + if (pps->pps_wp_info_in_ph_flag) { + ue(num_l1_weights, 0, + FFMIN(15, ref_lists->rpl_ref_list[1].num_ref_entries)); + infer(num_weights_l1, current->num_l1_weights); + } else { + infer(num_weights_l1, num_ref_idx_active[1]); + } + } else { + infer(num_weights_l1, 0); + } + + for (i = 0; i < current->num_weights_l1; i++) + flags(luma_weight_l1_flag[i], 1, i); + if (sps->sps_chroma_format_idc != 0) { + for (i = 0; i < current->num_weights_l1; i++) + flags(chroma_weight_l1_flag[i], 1, i); + } + for (i = 0; i < current->num_weights_l1; i++) { + if (current->luma_weight_l1_flag[i]) { + ses(delta_luma_weight_l1[i], -128, 127, 1, i); + ses(luma_offset_l1[i], -128, 127, 1, i); + } else { + infer(delta_luma_weight_l1[i], 0); + infer(luma_offset_l1[i], 0); + } + if (current->chroma_weight_l1_flag[i]) { + for (j = 0; j < 2; j++) { + ses(delta_chroma_weight_l1[i][j], -128, 127, 2, i, j); + ses(delta_chroma_offset_l1[i][j], -4 * 128, 4 * 127, 2, i, j); + } + } + } + return 0; +} + +static int FUNC(picture_header) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawPictureHeader *current) { + CodedBitstreamH266Context *h266 = ctx->priv_data; + const H266RawVPS *vps; + const H266RawSPS *sps; + const H266RawPPS *pps; + int err, i; + unsigned int ctb_log2_size_y, min_cb_log2_size_y, + min_qt_log2_size_intra_y, min_qt_log2_size_inter_y; + uint8_t qp_bd_offset; + + flag(ph_gdr_or_irap_pic_flag); + flag(ph_non_ref_pic_flag); + if (current->ph_gdr_or_irap_pic_flag) + flag(ph_gdr_pic_flag); + else + infer(ph_gdr_pic_flag, 0); + flag(ph_inter_slice_allowed_flag); + if (current->ph_inter_slice_allowed_flag) + flag(ph_intra_slice_allowed_flag); + else + infer(ph_intra_slice_allowed_flag, 1); + ue(ph_pic_parameter_set_id, 0, VVC_MAX_PPS_COUNT - 1); + pps = h266->pps[current->ph_pic_parameter_set_id]; + if (!pps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "PPS id %d not available.\n", + current->ph_pic_parameter_set_id); + return AVERROR_INVALIDDATA; + } + sps = h266->sps[pps->pps_seq_parameter_set_id]; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + pps->pps_seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + vps = h266->vps[sps->sps_video_parameter_set_id]; + if (!vps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "VPS id %d not available.\n", + sps->sps_video_parameter_set_id); + return AVERROR_INVALIDDATA; + } + + ub(sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4, ph_pic_order_cnt_lsb); + if (current->ph_gdr_pic_flag) + ue(ph_recovery_poc_cnt, 0, + 1 << (sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4)); + + for (i = 0; i < sps->sps_num_extra_ph_bytes * 8; i++) { + if (sps->sps_extra_ph_bit_present_flag[i]) + flags(ph_extra_bit[i], 1, i); + } + if (sps->sps_poc_msb_cycle_flag) { + flag(ph_poc_msb_cycle_present_flag); + if (current->ph_poc_msb_cycle_present_flag) + ub(sps->sps_poc_msb_cycle_len_minus1 + 1, ph_poc_msb_cycle_val); + } + if (sps->sps_alf_enabled_flag && pps->pps_alf_info_in_ph_flag) { + flag(ph_alf_enabled_flag); + if (current->ph_alf_enabled_flag) { + + ub(3, ph_num_alf_aps_ids_luma); + for (i = 0; i < current->ph_num_alf_aps_ids_luma; i++) + ubs(3, ph_alf_aps_id_luma[i], 1, i); + + if (sps->sps_chroma_format_idc != 0) { + flag(ph_alf_cb_enabled_flag); + flag(ph_alf_cr_enabled_flag); + } else { + infer(ph_alf_cb_enabled_flag, 0); + infer(ph_alf_cr_enabled_flag, 0); + } + + if (current->ph_alf_cb_enabled_flag + || current->ph_alf_cr_enabled_flag) { + ub(3, ph_alf_aps_id_chroma); + } + + if (sps->sps_ccalf_enabled_flag) { + flag(ph_alf_cc_cb_enabled_flag); + if (current->ph_alf_cc_cb_enabled_flag) + ub(3, ph_alf_cc_cb_aps_id); + flag(ph_alf_cc_cr_enabled_flag); + if (current->ph_alf_cc_cr_enabled_flag) + ub(3, ph_alf_cc_cr_aps_id); + } + } + } else { + infer(ph_alf_enabled_flag, 0); + } + if (sps->sps_lmcs_enabled_flag) { + flag(ph_lmcs_enabled_flag); + if (current->ph_lmcs_enabled_flag) { + ub(2, ph_lmcs_aps_id); + if (sps->sps_chroma_format_idc != 0) + flag(ph_chroma_residual_scale_flag); + else + infer(ph_chroma_residual_scale_flag, 0); + } + } else { + infer(ph_lmcs_enabled_flag, 0); + infer(ph_chroma_residual_scale_flag, 0); + } + + if (sps->sps_explicit_scaling_list_enabled_flag) { + flag(ph_explicit_scaling_list_enabled_flag); + if (current->ph_explicit_scaling_list_enabled_flag) { + //todo: check the ph_scaling_list_aps_id range, when aps ready + ub(3, ph_scaling_list_aps_id); + } + } else { + infer(ph_explicit_scaling_list_enabled_flag, 0); + } + if (sps->sps_virtual_boundaries_enabled_flag && + !sps->sps_virtual_boundaries_present_flag) { + flag(ph_virtual_boundaries_present_flag); + if (current->ph_virtual_boundaries_present_flag) { + ue(ph_num_ver_virtual_boundaries, + 0, pps->pps_pic_width_in_luma_samples <= 8 ? 0 : 3); + for (i = 0; i < current->ph_num_ver_virtual_boundaries; i++) { + ues(ph_virtual_boundary_pos_x_minus1[i], + 0, (pps->pps_pic_width_in_luma_samples + 7) / 8 - 2, 1, i); + } + ue(ph_num_hor_virtual_boundaries, + 0, pps->pps_pic_height_in_luma_samples <= 8 ? 0 : 3); + for (i = 0; i < current->ph_num_hor_virtual_boundaries; i++) { + ues(ph_virtual_boundary_pos_y_minus1[i], + 0, (pps->pps_pic_height_in_luma_samples + 7) / 8 - 2, 1, i); + } + } else { + infer(ph_num_ver_virtual_boundaries, 0); + infer(ph_num_hor_virtual_boundaries, 0); + } + } + if (pps->pps_output_flag_present_flag && !current->ph_non_ref_pic_flag) + flag(ph_pic_output_flag); + else + infer(ph_pic_output_flag, 1); + if (pps->pps_rpl_info_in_ph_flag) { + CHECK(FUNC(ref_pic_lists) + (ctx, rw, sps, pps, ¤t->ph_ref_pic_lists)); + } + if (sps->sps_partition_constraints_override_enabled_flag) + flag(ph_partition_constraints_override_flag); + else + infer(ph_partition_constraints_override_flag, 0); + + ctb_log2_size_y = sps->sps_log2_ctu_size_minus5 + 5; + min_cb_log2_size_y = sps->sps_log2_min_luma_coding_block_size_minus2 + 2; + if (current->ph_intra_slice_allowed_flag) { + if (current->ph_partition_constraints_override_flag) { + ue(ph_log2_diff_min_qt_min_cb_intra_slice_luma, + 0, FFMIN(6, ctb_log2_size_y) - min_cb_log2_size_y); + ue(ph_max_mtt_hierarchy_depth_intra_slice_luma, + 0, 2 * (ctb_log2_size_y - min_cb_log2_size_y)); + if (current->ph_max_mtt_hierarchy_depth_intra_slice_luma != 0) { + min_qt_log2_size_intra_y = + current->ph_log2_diff_min_qt_min_cb_intra_slice_luma + + min_cb_log2_size_y; + ue(ph_log2_diff_max_bt_min_qt_intra_slice_luma, + 0, (sps->sps_qtbtt_dual_tree_intra_flag ? + FFMIN(6, ctb_log2_size_y) : + ctb_log2_size_y) - min_qt_log2_size_intra_y); + ue(ph_log2_diff_max_tt_min_qt_intra_slice_luma, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_intra_y); + } else { + infer(ph_log2_diff_max_bt_min_qt_intra_slice_luma, + sps->sps_log2_diff_max_bt_min_qt_intra_slice_luma); + infer(ph_log2_diff_max_tt_min_qt_intra_slice_luma, + sps->sps_log2_diff_max_tt_min_qt_intra_slice_luma); + } + if (sps->sps_qtbtt_dual_tree_intra_flag) { + ue(ph_log2_diff_min_qt_min_cb_intra_slice_chroma, + 0, FFMIN(6, ctb_log2_size_y) - min_cb_log2_size_y); + ue(ph_max_mtt_hierarchy_depth_intra_slice_chroma, + 0, 2 * (ctb_log2_size_y - min_cb_log2_size_y)); + if (sps->sps_max_mtt_hierarchy_depth_intra_slice_chroma != 0) { + unsigned int min_qt_log2_size_intra_c = + sps->sps_log2_diff_min_qt_min_cb_intra_slice_chroma + + min_cb_log2_size_y; + ue(ph_log2_diff_max_bt_min_qt_intra_slice_chroma, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_intra_c); + ue(ph_log2_diff_max_tt_min_qt_intra_slice_chroma, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_intra_c); + } else { + infer(ph_log2_diff_max_bt_min_qt_intra_slice_chroma, + sps->sps_log2_diff_max_bt_min_qt_intra_slice_chroma); + infer(ph_log2_diff_max_tt_min_qt_intra_slice_chroma, + sps->sps_log2_diff_max_tt_min_qt_intra_slice_chroma); + } + } + } else { + infer(ph_log2_diff_min_qt_min_cb_intra_slice_luma, + sps->sps_log2_diff_min_qt_min_cb_intra_slice_luma); + infer(ph_max_mtt_hierarchy_depth_intra_slice_luma, + sps->sps_max_mtt_hierarchy_depth_intra_slice_luma); + infer(ph_log2_diff_max_bt_min_qt_intra_slice_luma, + sps->sps_log2_diff_max_bt_min_qt_intra_slice_luma); + infer(ph_log2_diff_max_tt_min_qt_intra_slice_luma, + sps->sps_log2_diff_max_tt_min_qt_intra_slice_luma); + infer(ph_log2_diff_min_qt_min_cb_intra_slice_chroma, + sps->sps_log2_diff_min_qt_min_cb_intra_slice_chroma); + infer(ph_max_mtt_hierarchy_depth_intra_slice_chroma, + sps->sps_max_mtt_hierarchy_depth_intra_slice_chroma); + infer(ph_log2_diff_max_bt_min_qt_intra_slice_chroma, + sps->sps_log2_diff_max_bt_min_qt_intra_slice_chroma); + infer(ph_log2_diff_max_tt_min_qt_intra_slice_chroma, + sps->sps_log2_diff_max_tt_min_qt_intra_slice_chroma); + } + + min_qt_log2_size_intra_y = + current->ph_log2_diff_min_qt_min_cb_intra_slice_luma + + min_cb_log2_size_y; + if (pps->pps_cu_qp_delta_enabled_flag) + ue(ph_cu_qp_delta_subdiv_intra_slice, 0, + 2 * (ctb_log2_size_y - min_qt_log2_size_intra_y + + current->ph_max_mtt_hierarchy_depth_intra_slice_luma)); + else + infer(ph_cu_qp_delta_subdiv_intra_slice, 0); + + if (pps->pps_cu_chroma_qp_offset_list_enabled_flag) + ue(ph_cu_chroma_qp_offset_subdiv_intra_slice, 0, + 2 * (ctb_log2_size_y - min_qt_log2_size_intra_y + + current->ph_max_mtt_hierarchy_depth_intra_slice_luma)); + else + infer(ph_cu_chroma_qp_offset_subdiv_intra_slice, 0); + } + if (current->ph_inter_slice_allowed_flag) { + if (current->ph_partition_constraints_override_flag) { + ue(ph_log2_diff_min_qt_min_cb_inter_slice, + 0, FFMIN(6, ctb_log2_size_y) - min_cb_log2_size_y); + min_qt_log2_size_inter_y = + current->ph_log2_diff_min_qt_min_cb_inter_slice + + min_cb_log2_size_y; + ue(ph_max_mtt_hierarchy_depth_inter_slice, 0, + 2 * (ctb_log2_size_y - min_cb_log2_size_y)); + if (current->ph_max_mtt_hierarchy_depth_inter_slice != 0) { + ue(ph_log2_diff_max_bt_min_qt_inter_slice, + 0, ctb_log2_size_y - min_qt_log2_size_inter_y); + ue(ph_log2_diff_max_tt_min_qt_inter_slice, + 0, FFMIN(6, ctb_log2_size_y) - min_qt_log2_size_inter_y); + } + } else { + infer(ph_log2_diff_min_qt_min_cb_inter_slice, + sps->sps_log2_diff_min_qt_min_cb_inter_slice); + min_qt_log2_size_inter_y = + current->ph_log2_diff_min_qt_min_cb_inter_slice + + min_cb_log2_size_y; + infer(ph_max_mtt_hierarchy_depth_inter_slice, + sps->sps_max_mtt_hierarchy_depth_inter_slice); + infer(ph_log2_diff_max_bt_min_qt_inter_slice, + sps->sps_log2_diff_max_bt_min_qt_inter_slice); + infer(ph_log2_diff_max_tt_min_qt_inter_slice, + sps->sps_log2_diff_max_tt_min_qt_inter_slice); + } + + if (pps->pps_cu_qp_delta_enabled_flag) + ue(ph_cu_qp_delta_subdiv_inter_slice, 0, + 2 * (ctb_log2_size_y - min_qt_log2_size_inter_y + + current->ph_max_mtt_hierarchy_depth_inter_slice)); + else + infer(ph_cu_qp_delta_subdiv_inter_slice, 0); + + if (pps->pps_cu_chroma_qp_offset_list_enabled_flag) + ue(ph_cu_chroma_qp_offset_subdiv_inter_slice, 0, + 2 * (ctb_log2_size_y - min_qt_log2_size_inter_y + + current->ph_max_mtt_hierarchy_depth_inter_slice)); + else + infer(ph_cu_chroma_qp_offset_subdiv_inter_slice, 0); + if (sps->sps_temporal_mvp_enabled_flag) { + flag(ph_temporal_mvp_enabled_flag); + if (current->ph_temporal_mvp_enabled_flag && + pps->pps_rpl_info_in_ph_flag) { + if (current->ph_ref_pic_lists.rpl_ref_list[1].num_ref_entries > 0) + flag(ph_collocated_from_l0_flag); + else + infer(ph_collocated_from_l0_flag, 1); + if ((current->ph_collocated_from_l0_flag && + current->ph_ref_pic_lists.rpl_ref_list[0].num_ref_entries > 1) + || (!current->ph_collocated_from_l0_flag && + current->ph_ref_pic_lists.rpl_ref_list[1].num_ref_entries > 1)) { + unsigned int idx = + current->ph_collocated_from_l0_flag ? 0 : 1; + ue(ph_collocated_ref_idx, 0, + current->ph_ref_pic_lists.rpl_ref_list[idx]. + num_ref_entries - 1); + } else { + infer(ph_collocated_ref_idx, 0); + } + } + } + if (sps->sps_mmvd_fullpel_only_enabled_flag) + flag(ph_mmvd_fullpel_only_flag); + else + infer(ph_mmvd_fullpel_only_flag, 0); + if (!pps->pps_rpl_info_in_ph_flag || + current->ph_ref_pic_lists.rpl_ref_list[1].num_ref_entries > 0) { + flag(ph_mvd_l1_zero_flag); + if (sps->sps_bdof_control_present_in_ph_flag) { + flag(ph_bdof_disabled_flag); + } else { + if (!sps->sps_bdof_control_present_in_ph_flag) + infer(ph_bdof_disabled_flag, + 1 - sps->sps_bdof_enabled_flag); + else + infer(ph_bdof_disabled_flag, 1); + } + if (sps->sps_dmvr_control_present_in_ph_flag) { + flag(ph_dmvr_disabled_flag); + } else { + if (!sps->sps_dmvr_control_present_in_ph_flag) + infer(ph_dmvr_disabled_flag, + 1 - sps->sps_dmvr_enabled_flag); + else + infer(ph_dmvr_disabled_flag, 1); + } + } else { + infer(ph_mvd_l1_zero_flag, 1); + } + if (sps->sps_prof_control_present_in_ph_flag) + flag(ph_prof_disabled_flag); + else + infer(ph_prof_disabled_flag, !sps->sps_affine_prof_enabled_flag); + if ((pps->pps_weighted_pred_flag || + pps->pps_weighted_bipred_flag) && pps->pps_wp_info_in_ph_flag) { + + // if pps->pps_wp_info_in_ph_fla == 1 + // pred_weight_table will not use num_ref_idx_active + uint8_t num_ref_idx_active[2] = { 0, 0 }; + CHECK(FUNC(pred_weight_table) + (ctx, rw, sps, pps, ¤t->ph_ref_pic_lists, + num_ref_idx_active, ¤t->ph_pred_weight_table)); + } + } + + qp_bd_offset = 6 * sps->sps_bitdepth_minus8; + if (pps->pps_qp_delta_info_in_ph_flag) + se(ph_qp_delta, -qp_bd_offset - (26 + pps->pps_init_qp_minus26), + 63 - (26 + pps->pps_init_qp_minus26)); + + if (sps->sps_joint_cbcr_enabled_flag) + flag(ph_joint_cbcr_sign_flag); + else + infer(ph_joint_cbcr_sign_flag, 0); + if (sps->sps_sao_enabled_flag && pps->pps_sao_info_in_ph_flag) { + flag(ph_sao_luma_enabled_flag); + if (sps->sps_chroma_format_idc != 0) + flag(ph_sao_chroma_enabled_flag); + else + infer(ph_sao_chroma_enabled_flag, 0); + } else { + infer(ph_sao_luma_enabled_flag, 0); + infer(ph_sao_chroma_enabled_flag, 0); + } + + if (pps->pps_dbf_info_in_ph_flag) + flag(ph_deblocking_params_present_flag); + else + infer(ph_deblocking_params_present_flag, 0); + + if (current->ph_deblocking_params_present_flag) { + if (!pps->pps_deblocking_filter_disabled_flag) { + flag(ph_deblocking_filter_disabled_flag); + if (!current->ph_deblocking_filter_disabled_flag) { + se(ph_luma_beta_offset_div2, -12, 12); + se(ph_luma_tc_offset_div2, -12, 12); + if (pps->pps_chroma_tool_offsets_present_flag) { + se(ph_cb_beta_offset_div2, -12, 12); + se(ph_cb_tc_offset_div2, -12, 12); + se(ph_cr_beta_offset_div2, -12, 12); + se(ph_cr_tc_offset_div2, -12, 12); + } else { + infer(ph_cb_beta_offset_div2, + current->ph_luma_beta_offset_div2); + infer(ph_cb_tc_offset_div2, + current->ph_luma_tc_offset_div2); + infer(ph_cr_beta_offset_div2, + current->ph_luma_beta_offset_div2); + infer(ph_cr_tc_offset_div2, + current->ph_luma_tc_offset_div2); + } + } + } else { + infer(ph_deblocking_filter_disabled_flag, 0); + } + } else { + infer(ph_deblocking_filter_disabled_flag, pps->pps_deblocking_filter_disabled_flag); + if (!current->ph_deblocking_filter_disabled_flag) { + infer(ph_luma_beta_offset_div2, pps->pps_luma_beta_offset_div2); + infer(ph_luma_tc_offset_div2, pps->pps_luma_tc_offset_div2); + infer(ph_cb_beta_offset_div2, pps->pps_cb_beta_offset_div2); + infer(ph_cb_tc_offset_div2, pps->pps_cb_tc_offset_div2); + infer(ph_cr_beta_offset_div2, pps->pps_cr_beta_offset_div2); + infer(ph_cr_tc_offset_div2, pps->pps_cr_tc_offset_div2); + } + } + + if (pps->pps_picture_header_extension_present_flag) { + ue(ph_extension_length, 0, 256); + for (i = 0; i < current->ph_extension_length; i++) + us(8, ph_extension_data_byte[i], 0x00, 0xff, 1, i); + } + + return 0; +} + +static int FUNC(ph) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawPH *current) +{ + int err; + + HEADER("Picture Header"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, ¤t->nal_unit_header, VVC_PH_NUT)); + CHECK(FUNC(picture_header) (ctx, rw, ¤t->ph_picture_header)); + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + return 0; +} + +static int FUNC(slice_header) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawSliceHeader *current) +{ + CodedBitstreamH266Context *h266 = ctx->priv_data; + const H266RawSPS *sps; + const H266RawPPS *pps; + const H266RawPictureHeader *ph; + const H266RefPicLists *ref_pic_lists; + int err, i; + uint8_t nal_unit_type, qp_bd_offset; + uint16_t num_slices_in_subpic; + + HEADER("Slice Header"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, ¤t->nal_unit_header, -1)); + + flag(sh_picture_header_in_slice_header_flag); + if (current->sh_picture_header_in_slice_header_flag) { + // 7.4.8 if sh_picture_header_in_slice_header_flag is true, we do not have a PH NAL unit + CHECK(FUNC(picture_header) (ctx, rw, ¤t->sh_picture_header)); + ph = ¤t->sh_picture_header; + } else { + ph = h266->ph; + if (!ph) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Picture header not available.\n"); + return AVERROR_INVALIDDATA; + } + } + + pps = h266->pps[ph->ph_pic_parameter_set_id]; + if (!pps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "PPS id %d not available.\n", + ph->ph_pic_parameter_set_id); + return AVERROR_INVALIDDATA; + } + sps = h266->sps[pps->pps_seq_parameter_set_id]; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + pps->pps_seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + + if (sps->sps_subpic_info_present_flag) { + ub(sps->sps_subpic_id_len_minus1 + 1, sh_subpic_id); + for (i = 0; i <= sps->sps_num_subpics_minus1; i++) { + if (pps->sub_pic_id_val[i] == current->sh_subpic_id) { + current->curr_subpic_idx = i; + break; + } + } + if (i > sps->sps_num_subpics_minus1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "invalid CurrSubpicIdx %d\n", i); + return AVERROR_INVALIDDATA; + } + } else { + current->curr_subpic_idx = 0; + } + + num_slices_in_subpic = pps->num_slices_in_subpic[current->curr_subpic_idx]; + + if ((pps->pps_rect_slice_flag && num_slices_in_subpic > 1) || + (!pps->pps_rect_slice_flag && pps->num_tiles_in_pic > 1)) { + unsigned int bits, max; + if (!pps->pps_rect_slice_flag) { + bits = av_ceil_log2(pps->num_tiles_in_pic); + max = pps->num_tiles_in_pic - 1; + } else { + bits = av_ceil_log2(num_slices_in_subpic); + max = num_slices_in_subpic - 1; + } + u(bits, sh_slice_address, 0, max); + } else { + infer(sh_slice_address, 0); + } + + for (i = 0; i < sps->sps_num_extra_sh_bytes * 8; i++) { + if (sps->sps_extra_sh_bit_present_flag[i]) + flags(sh_extra_bit[i], 1, i); + } + + if (!pps->pps_rect_slice_flag && + pps->num_tiles_in_pic - current->sh_slice_address > 1) + ue(sh_num_tiles_in_slice_minus1, 0, pps->num_tiles_in_pic - 1); + else + infer(sh_num_tiles_in_slice_minus1, 0); + + if (ph->ph_inter_slice_allowed_flag) + ue(sh_slice_type, 0, 2); + else + infer(sh_slice_type, 2); + + nal_unit_type = current->nal_unit_header.nal_unit_type; + if (nal_unit_type == VVC_IDR_W_RADL || nal_unit_type == VVC_IDR_N_LP || + nal_unit_type == VVC_CRA_NUT || nal_unit_type == VVC_GDR_NUT) + flag(sh_no_output_of_prior_pics_flag); + + if (sps->sps_alf_enabled_flag) { + if (!pps->pps_alf_info_in_ph_flag) { + flag(sh_alf_enabled_flag); + if (current->sh_alf_enabled_flag) { + ub(3, sh_num_alf_aps_ids_luma); + for (i = 0; i < current->sh_num_alf_aps_ids_luma; i++) + ubs(3, sh_alf_aps_id_luma[i], 1, i); + + if (sps->sps_chroma_format_idc != 0) { + flag(sh_alf_cb_enabled_flag); + flag(sh_alf_cr_enabled_flag); + } + if (current->sh_alf_cb_enabled_flag || + current->sh_alf_cr_enabled_flag) { + ub(3, sh_alf_aps_id_chroma); + } + + if (sps->sps_ccalf_enabled_flag) { + flag(sh_alf_cc_cb_enabled_flag); + if (current->sh_alf_cc_cb_enabled_flag) + ub(3, sh_alf_cc_cb_aps_id); + + flag(sh_alf_cc_cr_enabled_flag); + if (current->sh_alf_cc_cr_enabled_flag) + ub(3, sh_alf_cc_cr_aps_id); + } + } + } else { + infer(sh_alf_enabled_flag, ph->ph_alf_enabled_flag); + if (current->sh_alf_enabled_flag) { + infer(sh_num_alf_aps_ids_luma, ph->ph_num_alf_aps_ids_luma); + for (i = 0; i < current->sh_num_alf_aps_ids_luma; i++) + infer(sh_alf_aps_id_luma[i], ph->ph_alf_aps_id_luma[i]); + + infer(sh_alf_cb_enabled_flag, ph->ph_alf_cb_enabled_flag); + infer(sh_alf_cr_enabled_flag, ph->ph_alf_cr_enabled_flag); + if (current->sh_alf_cb_enabled_flag ||current->sh_alf_cr_enabled_flag) + infer(sh_alf_aps_id_chroma, ph->ph_alf_aps_id_chroma); + + if (sps->sps_ccalf_enabled_flag) { + infer(sh_alf_cc_cb_enabled_flag, ph->ph_alf_cc_cb_enabled_flag); + if (current->sh_alf_cc_cb_enabled_flag) + infer(sh_alf_cc_cb_aps_id, ph->ph_alf_cc_cb_aps_id); + + infer(sh_alf_cc_cr_enabled_flag, ph->ph_alf_cc_cr_enabled_flag); + if (current->sh_alf_cc_cr_enabled_flag) + infer(sh_alf_cc_cr_aps_id, ph->ph_alf_cc_cr_aps_id); + } + } + } + } + + if (current->sh_picture_header_in_slice_header_flag) { + infer(sh_lmcs_used_flag, ph->ph_lmcs_enabled_flag); + infer(sh_explicit_scaling_list_used_flag, + ph->ph_explicit_scaling_list_enabled_flag); + } else { + if (ph->ph_lmcs_enabled_flag) + flag(sh_lmcs_used_flag); + else + infer(sh_lmcs_used_flag, 0); + + if (ph->ph_explicit_scaling_list_enabled_flag) + flag(sh_explicit_scaling_list_used_flag); + else + infer(sh_explicit_scaling_list_used_flag, 0); + } + + if (!pps->pps_rpl_info_in_ph_flag && + ((nal_unit_type != VVC_IDR_W_RADL && + nal_unit_type != VVC_IDR_N_LP) || sps->sps_idr_rpl_present_flag)) { + CHECK(FUNC(ref_pic_lists) + (ctx, rw, sps, pps, ¤t->sh_ref_pic_lists)); + ref_pic_lists = ¤t->sh_ref_pic_lists; + } else { + ref_pic_lists = &ph->ph_ref_pic_lists; + } + if ((current->sh_slice_type != VVC_SLICE_TYPE_I && + ref_pic_lists->rpl_ref_list[0].num_ref_entries > 1) || + (current->sh_slice_type == VVC_SLICE_TYPE_B && + ref_pic_lists->rpl_ref_list[1].num_ref_entries > 1)) { + flag(sh_num_ref_idx_active_override_flag); + if (current->sh_num_ref_idx_active_override_flag) { + for (i = 0; + i < (current->sh_slice_type == VVC_SLICE_TYPE_B ? 2 : 1); i++) + if (ref_pic_lists->rpl_ref_list[i].num_ref_entries > 1) + ues(sh_num_ref_idx_active_minus1[i], 0, 14, 1, i); + else + infer(sh_num_ref_idx_active_minus1[i], 0); + } + } else { + infer(sh_num_ref_idx_active_override_flag, 1); + } + + for (i = 0; i < 2; i++) { + if (current->sh_slice_type == VVC_SLICE_TYPE_B || + (current->sh_slice_type == VVC_SLICE_TYPE_P && i == 0)) { + if (current->sh_num_ref_idx_active_override_flag) { + current->num_ref_idx_active[i] = current->sh_num_ref_idx_active_minus1[i] + 1; + } else { + current->num_ref_idx_active[i] = + FFMIN(ref_pic_lists->rpl_ref_list[i].num_ref_entries, + pps->pps_num_ref_idx_default_active_minus1[i] + 1); + } + } else { + current->num_ref_idx_active[i] = 0; + } + } + + if (current->sh_slice_type != VVC_SLICE_TYPE_I) { + if (pps->pps_cabac_init_present_flag) + flag(sh_cabac_init_flag); + else + infer(sh_cabac_init_flag, 0); + if (ph->ph_temporal_mvp_enabled_flag) { + if (!pps->pps_rpl_info_in_ph_flag) { + if (current->sh_slice_type == VVC_SLICE_TYPE_B) + flag(sh_collocated_from_l0_flag); + else + infer(sh_collocated_from_l0_flag, 1); + if ((current->sh_collocated_from_l0_flag && + current->num_ref_idx_active[0] > 1) || + (!current->sh_collocated_from_l0_flag && + current->num_ref_idx_active[1] > 1)) { + unsigned int idx = current->sh_collocated_from_l0_flag ? 0 : 1; + ue(sh_collocated_ref_idx, 0, current->num_ref_idx_active[idx] - 1); + } else { + infer(sh_collocated_ref_idx, 0); + } + } else { + if (current->sh_slice_type == VVC_SLICE_TYPE_B) + infer(sh_collocated_from_l0_flag, ph->ph_collocated_from_l0_flag); + else + infer(sh_collocated_from_l0_flag, 1); + infer(sh_collocated_ref_idx, ph->ph_collocated_ref_idx); + } + } + if (!pps->pps_wp_info_in_ph_flag && + ((pps->pps_weighted_pred_flag && + current->sh_slice_type == VVC_SLICE_TYPE_P) || + (pps->pps_weighted_bipred_flag && + current->sh_slice_type == VVC_SLICE_TYPE_B))) { + CHECK(FUNC(pred_weight_table) (ctx, rw, sps, pps, ref_pic_lists, + current->num_ref_idx_active, + ¤t->sh_pred_weight_table)); + } + } + qp_bd_offset = 6 * sps->sps_bitdepth_minus8; + if (!pps->pps_qp_delta_info_in_ph_flag) + se(sh_qp_delta, -qp_bd_offset - (26 + pps->pps_init_qp_minus26), + 63 - (26 + pps->pps_init_qp_minus26)); + if (pps->pps_slice_chroma_qp_offsets_present_flag) { + int8_t off; + + se(sh_cb_qp_offset, -12, 12); + off = pps->pps_cb_qp_offset + current->sh_cb_qp_offset; + if (off < -12 || off > 12) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "pps_cb_qp_offset + sh_cb_qp_offset (%d) not in range [-12, 12].\n", + off); + return AVERROR_INVALIDDATA; + } + + se(sh_cr_qp_offset, -12, 12); + off = pps->pps_cr_qp_offset + current->sh_cr_qp_offset; + if (off < -12 || off > 12) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "pps_cr_qp_offset + sh_cr_qp_offset (%d) not in range [-12, 12].\n", + off); + return AVERROR_INVALIDDATA; + } + + if (sps->sps_joint_cbcr_enabled_flag) { + se(sh_joint_cbcr_qp_offset, -12, 12); + off = + pps->pps_joint_cbcr_qp_offset_value + + current->sh_joint_cbcr_qp_offset; + if (off < -12 || off > 12) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "pps_joint_cbcr_qp_offset_value + sh_joint_cbcr_qp_offset (%d)" + "not in range [-12, 12]. \n", off); + return AVERROR_INVALIDDATA; + } + } else { + infer(sh_joint_cbcr_qp_offset, 0); + } + } else { + infer(sh_cb_qp_offset, 0); + infer(sh_cr_qp_offset, 0); + infer(sh_joint_cbcr_qp_offset, 0); + } + if (pps->pps_cu_chroma_qp_offset_list_enabled_flag) + flag(sh_cu_chroma_qp_offset_enabled_flag); + else + infer(sh_cu_chroma_qp_offset_enabled_flag, 0); + if (sps->sps_sao_enabled_flag && !pps->pps_sao_info_in_ph_flag) { + flag(sh_sao_luma_used_flag); + if (sps->sps_chroma_format_idc != 0) + flag(sh_sao_chroma_used_flag); + else + infer(sh_sao_chroma_used_flag, ph->ph_sao_chroma_enabled_flag); + } else { + infer(sh_sao_luma_used_flag, ph->ph_sao_luma_enabled_flag); + infer(sh_sao_chroma_used_flag, ph->ph_sao_chroma_enabled_flag); + } + + if (pps->pps_deblocking_filter_override_enabled_flag && + !pps->pps_dbf_info_in_ph_flag) + flag(sh_deblocking_params_present_flag); + else + infer(sh_deblocking_params_present_flag, 0); + if (current->sh_deblocking_params_present_flag) { + if (!pps->pps_deblocking_filter_disabled_flag) + flag(sh_deblocking_filter_disabled_flag); + else + infer(sh_deblocking_filter_disabled_flag, 0); + if (!current->sh_deblocking_filter_disabled_flag) { + se(sh_luma_beta_offset_div2, -12, 12); + se(sh_luma_tc_offset_div2, -12, 12); + if (pps->pps_chroma_tool_offsets_present_flag) { + se(sh_cb_beta_offset_div2, -12, 12); + se(sh_cb_tc_offset_div2, -12, 12); + se(sh_cr_beta_offset_div2, -12, 12); + se(sh_cr_tc_offset_div2, -12, 12); + } else { + infer(sh_cb_beta_offset_div2, + current->sh_luma_beta_offset_div2); + infer(sh_cb_tc_offset_div2, current->sh_luma_tc_offset_div2); + infer(sh_cr_beta_offset_div2, + current->sh_luma_beta_offset_div2); + infer(sh_cr_tc_offset_div2, current->sh_luma_tc_offset_div2); + } + } + } else { + infer(sh_deblocking_filter_disabled_flag, ph->ph_deblocking_filter_disabled_flag); + if (!current->sh_deblocking_filter_disabled_flag) { + infer(sh_luma_beta_offset_div2, ph->ph_luma_beta_offset_div2); + infer(sh_luma_tc_offset_div2, ph->ph_luma_tc_offset_div2); + infer(sh_cb_beta_offset_div2, ph->ph_cb_beta_offset_div2); + infer(sh_cb_tc_offset_div2, ph->ph_cb_tc_offset_div2); + infer(sh_cr_beta_offset_div2, ph->ph_cr_beta_offset_div2); + infer(sh_cr_tc_offset_div2, ph->ph_cr_tc_offset_div2); + } + } + + if (sps->sps_dep_quant_enabled_flag) + flag(sh_dep_quant_used_flag); + else + infer(sh_dep_quant_used_flag, 0); + + if (sps->sps_sign_data_hiding_enabled_flag && + !current->sh_dep_quant_used_flag) + flag(sh_sign_data_hiding_used_flag); + else + infer(sh_sign_data_hiding_used_flag, 0); + + if (sps->sps_transform_skip_enabled_flag && + !current->sh_dep_quant_used_flag && + !current->sh_sign_data_hiding_used_flag) + flag(sh_ts_residual_coding_disabled_flag); + else + infer(sh_ts_residual_coding_disabled_flag, 0); + + if (!current->sh_ts_residual_coding_disabled_flag && + sps->sps_ts_residual_coding_rice_present_in_sh_flag) + ub(3, sh_ts_residual_coding_rice_idx_minus1); + else + infer(sh_ts_residual_coding_rice_idx_minus1, 0); + + if (sps->sps_reverse_last_sig_coeff_enabled_flag) + flag(sh_reverse_last_sig_coeff_flag); + else + infer(sh_reverse_last_sig_coeff_flag, 0); + + if (pps->pps_slice_header_extension_present_flag) { + ue(sh_slice_header_extension_length, 0, 256); + for (i = 0; i < current->sh_slice_header_extension_length; i++) + us(8, sh_slice_header_extension_data_byte[i], 0x00, 0xff, 1, i); + } + + current->num_entry_points = 0; + if (sps->sps_entry_point_offsets_present_flag) { + uint8_t entropy_sync = sps->sps_entropy_coding_sync_enabled_flag; + int height; + if (pps->pps_rect_slice_flag) { + int width_in_tiles; + int slice_idx = current->sh_slice_address; + for (i = 0; i < current->curr_subpic_idx; i++) { + slice_idx += pps->num_slices_in_subpic[i]; + } + width_in_tiles = + pps->pps_slice_width_in_tiles_minus1[slice_idx] + 1; + + if (entropy_sync) + height = pps->slice_height_in_ctus[slice_idx]; + else + height = pps->pps_slice_height_in_tiles_minus1[slice_idx] + 1; + + current->num_entry_points = width_in_tiles * height; + } else { + int tile_idx; + int tile_y; + for (tile_idx = current->sh_slice_address; + tile_idx <= + current->sh_slice_address + + current->sh_num_tiles_in_slice_minus1; tile_idx++) { + tile_y = tile_idx / pps->num_tile_rows; + height = pps->row_height_val[tile_y]; + current->num_entry_points += (entropy_sync ? height : 1); + } + } + current->num_entry_points--; + if (current->num_entry_points > VVC_MAX_ENTRY_POINTS) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many entry points: " + "%" PRIu16 ".\n", current->num_entry_points); + return AVERROR_PATCHWELCOME; + } + if (current->num_entry_points > 0) { + ue(sh_entry_offset_len_minus1, 0, 31); + for (i = 0; i < current->num_entry_points; i++) { + ubs(current->sh_entry_offset_len_minus1 + 1, + sh_entry_point_offset_minus1[i], 1, i); + } + } + } + CHECK(FUNC(byte_alignment) (ctx, rw)); + + return 0; +} + +SEI_FUNC(sei_decoded_picture_hash, (CodedBitstreamContext *ctx, + RWContext *rw, + H266RawSEIDecodedPictureHash *current, + SEIMessageState *unused)) +{ + int err, c_idx, i; + + HEADER("Decoded Picture Hash"); + + u(8, dph_sei_hash_type, 0, 2); + flag(dph_sei_single_component_flag); + ub(7, dph_sei_reserved_zero_7bits); + + for (c_idx = 0; c_idx < (current->dph_sei_single_component_flag ? 1 : 3); + c_idx++) { + if (current->dph_sei_hash_type == 0) { + for (i = 0; i < 16; i++) + us(8, dph_sei_picture_md5[c_idx][i], 0x00, 0xff, 2, c_idx, i); + } else if (current->dph_sei_hash_type == 1) { + us(16, dph_sei_picture_crc[c_idx], 0x0000, 0xffff, 1, c_idx); + } else if (current->dph_sei_hash_type == 2) { + us(32, dph_sei_picture_checksum[c_idx], 0x00000000, 0xffffffff, 1, + c_idx); + } + } + return 0; +} + +static int FUNC(sei) (CodedBitstreamContext *ctx, RWContext *rw, + H266RawSEI *current, int prefix) +{ + int err; + + if (prefix) + HEADER("Prefix Supplemental Enhancement Information"); + else + HEADER("Suffix Supplemental Enhancement Information"); + + CHECK(FUNC(nal_unit_header) (ctx, rw, ¤t->nal_unit_header, + prefix ? VVC_PREFIX_SEI_NUT + : VVC_SUFFIX_SEI_NUT)); + + CHECK(FUNC_SEI(message_list) (ctx, rw, ¤t->message_list, prefix)); + + CHECK(FUNC(rbsp_trailing_bits) (ctx, rw)); + + return 0; +} diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h index e585c779341..d982262bd91 100644 --- a/libavcodec/cbs_internal.h +++ b/libavcodec/cbs_internal.h @@ -19,15 +19,16 @@ #ifndef AVCODEC_CBS_INTERNAL_H #define AVCODEC_CBS_INTERNAL_H +#include #include -#include "libavutil/buffer.h" #include "libavutil/log.h" #include "cbs.h" #include "codec_id.h" #include "get_bits.h" #include "put_bits.h" +#include "refstruct.h" enum CBSContentType { @@ -92,8 +93,8 @@ typedef const struct CodedBitstreamUnitTypeDescriptor { } ref; struct { - void (*content_free)(void *opaque, uint8_t *data); - int (*content_clone)(AVBufferRef **ref, CodedBitstreamUnit *unit); + void (*content_free)(FFRefStructOpaque opaque, void *content); + int (*content_clone)(void **new_content, CodedBitstreamUnit *unit); } complex; } type; } CodedBitstreamUnitTypeDescriptor; @@ -133,6 +134,12 @@ typedef struct CodedBitstreamType { CodedBitstreamUnit *unit, PutBitContext *pbc); + // Return 1 when the unit should be dropped according to 'skip', + // 0 otherwise. + int (*discarded_unit)(CodedBitstreamContext *ctx, + const CodedBitstreamUnit *unit, + enum AVDiscard skip); + // Read the data from all of frag->units and assemble it into // a bitstream for the whole fragment. int (*assemble_fragment)(CodedBitstreamContext *ctx, @@ -151,24 +158,29 @@ typedef struct CodedBitstreamType { void ff_cbs_trace_header(CodedBitstreamContext *ctx, const char *name); -void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position, - const char *name, const int *subscripts, - const char *bitstring, int64_t value); - // Helper functions for read/write of common bitstream elements, including -// generation of trace output. +// generation of trace output. The simple functions are equivalent to +// their non-simple counterparts except that their range is unrestricted +// (i.e. only limited by the amount of bits used) and they lack +// the ability to use subscripts. int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, int width, const char *name, const int *subscripts, uint32_t *write_to, uint32_t range_min, uint32_t range_max); +int ff_cbs_read_simple_unsigned(CodedBitstreamContext *ctx, GetBitContext *gbc, + int width, const char *name, uint32_t *write_to); + int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, int width, const char *name, const int *subscripts, uint32_t value, uint32_t range_min, uint32_t range_max); +int ff_cbs_write_simple_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, + int width, const char *name, uint32_t value); + int ff_cbs_read_signed(CodedBitstreamContext *ctx, GetBitContext *gbc, int width, const char *name, const int *subscripts, int32_t *write_to, @@ -191,6 +203,87 @@ int ff_cbs_write_signed(CodedBitstreamContext *ctx, PutBitContext *pbc, // range_min in the above functions. #define MIN_INT_BITS(length) (-(INT64_C(1) << ((length) - 1))) + +// Start of a syntax element during read tracing. +#define CBS_TRACE_READ_START() \ + GetBitContext trace_start; \ + do { \ + if (ctx->trace_enable) \ + trace_start = *gbc; \ + } while (0) + +// End of a syntax element for tracing, make callback. +#define CBS_TRACE_READ_END() \ + do { \ + if (ctx->trace_enable) { \ + int start_position = get_bits_count(&trace_start); \ + int end_position = get_bits_count(gbc); \ + av_assert0(start_position <= end_position); \ + ctx->trace_read_callback(ctx->trace_context, &trace_start, \ + end_position - start_position, \ + name, subscripts, value); \ + } \ + } while (0) + +// End of a syntax element with no subscript entries. +#define CBS_TRACE_READ_END_NO_SUBSCRIPTS() \ + do { \ + const int *subscripts = NULL; \ + CBS_TRACE_READ_END(); \ + } while (0) + +// End of a syntax element which is made up of subelements which +// are aleady traced, so we are only showing the value. +#define CBS_TRACE_READ_END_VALUE_ONLY() \ + do { \ + if (ctx->trace_enable) { \ + ctx->trace_read_callback(ctx->trace_context, &trace_start, 0, \ + name, subscripts, value); \ + } \ + } while (0) + +// Start of a syntax element during write tracing. +#define CBS_TRACE_WRITE_START() \ + int start_position; \ + do { \ + if (ctx->trace_enable) \ + start_position = put_bits_count(pbc);; \ + } while (0) + +// End of a syntax element for tracing, make callback. +#define CBS_TRACE_WRITE_END() \ + do { \ + if (ctx->trace_enable) { \ + int end_position = put_bits_count(pbc); \ + av_assert0(start_position <= end_position); \ + ctx->trace_write_callback(ctx->trace_context, pbc, \ + end_position - start_position, \ + name, subscripts, value); \ + } \ + } while (0) + +// End of a syntax element with no subscript entries. +#define CBS_TRACE_WRITE_END_NO_SUBSCRIPTS() \ + do { \ + const int *subscripts = NULL; \ + CBS_TRACE_WRITE_END(); \ + } while (0) + +// End of a syntax element which is made up of subelements which are +// aleady traced, so we are only showing the value. This forges a +// PutBitContext to point to the position of the start of the syntax +// element, but the other state doesn't matter because length is zero. +#define CBS_TRACE_WRITE_END_VALUE_ONLY() \ + do { \ + if (ctx->trace_enable) { \ + PutBitContext tmp; \ + init_put_bits(&tmp, pbc->buf, start_position); \ + skip_put_bits(&tmp, start_position); \ + ctx->trace_write_callback(ctx->trace_context, &tmp, 0, \ + name, subscripts, value); \ + } \ + } while (0) + #define TYPE_LIST(...) { __VA_ARGS__ } #define CBS_UNIT_TYPE_POD(type_, structure) { \ .nb_unit_types = 1, \ @@ -245,8 +338,10 @@ int ff_cbs_write_signed(CodedBitstreamContext *ctx, PutBitContext *pbc, extern const CodedBitstreamType ff_cbs_type_av1; extern const CodedBitstreamType ff_cbs_type_h264; extern const CodedBitstreamType ff_cbs_type_h265; +extern const CodedBitstreamType ff_cbs_type_h266; extern const CodedBitstreamType ff_cbs_type_jpeg; extern const CodedBitstreamType ff_cbs_type_mpeg2; +extern const CodedBitstreamType ff_cbs_type_vp8; extern const CodedBitstreamType ff_cbs_type_vp9; diff --git a/libavcodec/cbs_jpeg.c b/libavcodec/cbs_jpeg.c index 5921d624a18..f2aa496610c 100644 --- a/libavcodec/cbs_jpeg.c +++ b/libavcodec/cbs_jpeg.c @@ -145,13 +145,13 @@ static int cbs_jpeg_split_fragment(CodedBitstreamContext *ctx, } } else { i = start; - if (i + 2 > frag->data_size) { + if (i > frag->data_size - 2) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " "truncated at %02x marker.\n", marker); return AVERROR_INVALIDDATA; } length = AV_RB16(frag->data + i); - if (i + length > frag->data_size) { + if (length > frag->data_size - i) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: " "truncated at %02x marker segment.\n", marker); return AVERROR_INVALIDDATA; diff --git a/libavcodec/cbs_mpeg2.c b/libavcodec/cbs_mpeg2.c index 04b0c7f87dd..37fc28a4e62 100644 --- a/libavcodec/cbs_mpeg2.c +++ b/libavcodec/cbs_mpeg2.c @@ -40,8 +40,6 @@ #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL) -#define ui(width, name) \ - xui(width, name, current->name, 0, MAX_UINT_BITS(width), 0, ) #define uir(width, name) \ xui(width, name, current->name, 1, MAX_UINT_BITS(width), 0, ) #define uis(width, name, subs, ...) \ @@ -65,6 +63,12 @@ #define READWRITE read #define RWContext GetBitContext +#define ui(width, name) do { \ + uint32_t value; \ + CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, #name, \ + &value)); \ + current->name = value; \ + } while (0) #define xuia(width, string, var, range_min, range_max, subs, ...) do { \ uint32_t value; \ CHECK(ff_cbs_read_unsigned(ctx, rw, width, string, \ @@ -95,6 +99,7 @@ #undef READ #undef READWRITE #undef RWContext +#undef ui #undef xuia #undef xsi #undef nextbits @@ -105,6 +110,11 @@ #define READWRITE write #define RWContext PutBitContext +#define ui(width, name) do { \ + CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \ + current->name)); \ + } while (0) + #define xuia(width, string, var, range_min, range_max, subs, ...) do { \ CHECK(ff_cbs_write_unsigned(ctx, rw, width, string, \ SUBSCRIPTS(subs, __VA_ARGS__), \ @@ -134,6 +144,7 @@ #undef WRITE #undef READWRITE #undef RWContext +#undef ui #undef xuia #undef xsi #undef nextbits diff --git a/libavcodec/cbs_sei.c b/libavcodec/cbs_sei.c index 50a513f592a..e28c2f90935 100644 --- a/libavcodec/cbs_sei.c +++ b/libavcodec/cbs_sei.c @@ -20,26 +20,27 @@ #include "cbs_internal.h" #include "cbs_h264.h" #include "cbs_h265.h" +#include "cbs_h266.h" #include "cbs_sei.h" +#include "refstruct.h" -static void cbs_free_user_data_registered(void *opaque, uint8_t *data) +static void cbs_free_user_data_registered(FFRefStructOpaque unused, void *obj) { - SEIRawUserDataRegistered *udr = (SEIRawUserDataRegistered*)data; - av_buffer_unref(&udr->data_ref); - av_free(udr); + SEIRawUserDataRegistered *udr = obj; + ff_refstruct_unref(&udr->data); } -static void cbs_free_user_data_unregistered(void *opaque, uint8_t *data) +static void cbs_free_user_data_unregistered(FFRefStructOpaque unused, void *obj) { - SEIRawUserDataUnregistered *udu = (SEIRawUserDataUnregistered*)data; - av_buffer_unref(&udu->data_ref); - av_free(udu); + SEIRawUserDataUnregistered *udu = obj; + ff_refstruct_unref(&udu->data); } int ff_cbs_sei_alloc_message_payload(SEIRawMessage *message, const SEIMessageTypeDescriptor *desc) { - void (*free_func)(void*, uint8_t*); + void (*free_func)(FFRefStructOpaque, void*); + unsigned flags = 0; av_assert0(message->payload == NULL && message->payload_ref == NULL); @@ -49,24 +50,16 @@ int ff_cbs_sei_alloc_message_payload(SEIRawMessage *message, free_func = &cbs_free_user_data_registered; else if (desc->type == SEI_TYPE_USER_DATA_UNREGISTERED) free_func = &cbs_free_user_data_unregistered; - else + else { free_func = NULL; - - if (free_func) { - message->payload = av_mallocz(desc->size); - if (!message->payload) - return AVERROR(ENOMEM); - message->payload_ref = - av_buffer_create(message->payload, desc->size, - free_func, NULL, 0); - } else { - message->payload_ref = av_buffer_alloc(desc->size); + flags = FF_REFSTRUCT_FLAG_NO_ZEROING; } - if (!message->payload_ref) { - av_freep(&message->payload); + + message->payload_ref = ff_refstruct_alloc_ext(desc->size, flags, + NULL, free_func); + if (!message->payload_ref) return AVERROR(ENOMEM); - } - message->payload = message->payload_ref->data; + message->payload = message->payload_ref; return 0; } @@ -100,8 +93,8 @@ void ff_cbs_sei_free_message_list(SEIRawMessageList *list) { for (int i = 0; i < list->nb_messages; i++) { SEIRawMessage *message = &list->messages[i]; - av_buffer_unref(&message->payload_ref); - av_buffer_unref(&message->extension_data_ref); + ff_refstruct_unref(&message->payload_ref); + ff_refstruct_unref(&message->extension_data); } av_free(list->messages); } @@ -132,6 +125,13 @@ static int cbs_sei_get_unit(CodedBitstreamContext *ctx, else sei_type = HEVC_NAL_SEI_SUFFIX; break; + case AV_CODEC_ID_H266: + highest_vcl_type = VVC_RSV_IRAP_11; + if (prefix) + sei_type = VVC_PREFIX_SEI_NUT; + else + sei_type = VVC_SUFFIX_SEI_NUT; + break; default: return AVERROR(EINVAL); } @@ -207,6 +207,18 @@ static int cbs_sei_get_unit(CodedBitstreamContext *ctx, memcpy(unit->content, &sei, sizeof(sei)); } break; + case AV_CODEC_ID_H266: + { + H266RawSEI sei = { + .nal_unit_header = { + .nal_unit_type = sei_type, + .nuh_layer_id = 0, + .nuh_temporal_id_plus1 = 1, + }, + }; + memcpy(unit->content, &sei, sizeof(sei)); + } + break; default: av_assert0(0); } @@ -237,6 +249,15 @@ static int cbs_sei_get_message_list(CodedBitstreamContext *ctx, *list = &sei->message_list; } break; + case AV_CODEC_ID_H266: + { + H266RawSEI *sei = unit->content; + if (unit->type != VVC_PREFIX_SEI_NUT && + unit->type != VVC_SUFFIX_SEI_NUT) + return AVERROR(EINVAL); + *list = &sei->message_list; + } + break; default: return AVERROR(EINVAL); } @@ -249,13 +270,12 @@ int ff_cbs_sei_add_message(CodedBitstreamContext *ctx, int prefix, uint32_t payload_type, void *payload_data, - AVBufferRef *payload_buf) + void *payload_ref) { const SEIMessageTypeDescriptor *desc; CodedBitstreamUnit *unit; SEIRawMessageList *list; SEIRawMessage *message; - AVBufferRef *payload_ref; int err; desc = ff_cbs_sei_find_type(ctx, payload_type); @@ -277,12 +297,10 @@ int ff_cbs_sei_add_message(CodedBitstreamContext *ctx, if (err < 0) return err; - if (payload_buf) { - payload_ref = av_buffer_ref(payload_buf); - if (!payload_ref) - return AVERROR(ENOMEM); - } else { - payload_ref = NULL; + if (payload_ref) { + /* The following just increments payload_ref's refcount, + * so that payload_ref is now owned by us. */ + payload_ref = ff_refstruct_ref(payload_ref); } message = &list->messages[list->nb_messages - 1]; @@ -335,8 +353,8 @@ static void cbs_sei_delete_message(SEIRawMessageList *list, av_assert0(0 <= position && position < list->nb_messages); message = &list->messages[position]; - av_buffer_unref(&message->payload_ref); - av_buffer_unref(&message->extension_data_ref); + ff_refstruct_unref(&message->payload_ref); + ff_refstruct_unref(&message->extension_data); --list->nb_messages; diff --git a/libavcodec/cbs_sei.h b/libavcodec/cbs_sei.h index 1c327a4689e..ec7cdb62f0e 100644 --- a/libavcodec/cbs_sei.h +++ b/libavcodec/cbs_sei.h @@ -22,8 +22,6 @@ #include #include -#include "libavutil/buffer.h" - #include "cbs.h" #include "sei.h" @@ -35,15 +33,13 @@ typedef struct SEIRawFillerPayload { typedef struct SEIRawUserDataRegistered { uint8_t itu_t_t35_country_code; uint8_t itu_t_t35_country_code_extension_byte; - uint8_t *data; - AVBufferRef *data_ref; + uint8_t *data; ///< RefStruct reference size_t data_length; } SEIRawUserDataRegistered; typedef struct SEIRawUserDataUnregistered { uint8_t uuid_iso_iec_11578[16]; - uint8_t *data; - AVBufferRef *data_ref; + uint8_t *data; ///< RefStruct reference size_t data_length; } SEIRawUserDataUnregistered; @@ -75,9 +71,8 @@ typedef struct SEIRawMessage { uint32_t payload_type; uint32_t payload_size; void *payload; - AVBufferRef *payload_ref; - uint8_t *extension_data; - AVBufferRef *extension_data_ref; + void *payload_ref; ///< RefStruct reference + uint8_t *extension_data; ///< RefStruct reference size_t extension_bit_length; } SEIRawMessage; @@ -131,13 +126,6 @@ typedef struct SEIMessageTypeDescriptor { SEIMessageWriteFunction write; } SEIMessageTypeDescriptor; -// Macro for the read/write pair. The clumsy cast is needed because the -// current pointer is typed in all of the read/write functions but has to -// be void here to fit all cases. -#define SEI_MESSAGE_RW(codec, name) \ - .read = (SEIMessageReadFunction) cbs_ ## codec ## _read_ ## name, \ - .write = (SEIMessageWriteFunction)cbs_ ## codec ## _write_ ## name - // End-of-list sentinel element. #define SEI_MESSAGE_TYPE_END { .type = -1 } @@ -174,15 +162,16 @@ void ff_cbs_sei_free_message_list(SEIRawMessageList *list); * Will add to an existing SEI NAL unit, or create a new one for the * message if there is no suitable existing one. * - * Takes a new reference to payload_buf, if set. If payload_buf is - * NULL then the new message will not be reference counted. + * If set, payload_ref must be a RefStruct reference backing payload_data. + * This function creates a new reference to payload_ref in this case. + * If payload_ref is NULL, the new message will not be reference counted. */ int ff_cbs_sei_add_message(CodedBitstreamContext *ctx, CodedBitstreamFragment *au, int prefix, uint32_t payload_type, void *payload_data, - AVBufferRef *payload_buf); + void *payload_ref); /** * Iterate over messages with the given payload type in an access unit. diff --git a/libavcodec/cbs_sei_syntax_template.c b/libavcodec/cbs_sei_syntax_template.c index 6a7cc36ddaf..81448ef3f21 100644 --- a/libavcodec/cbs_sei_syntax_template.c +++ b/libavcodec/cbs_sei_syntax_template.c @@ -16,9 +16,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -static int FUNC(filler_payload) - (CodedBitstreamContext *ctx, RWContext *rw, - SEIRawFillerPayload *current, SEIMessageState *state) +SEI_FUNC(filler_payload, (CodedBitstreamContext *ctx, RWContext *rw, + SEIRawFillerPayload *current, + SEIMessageState *state)) { int err, i; @@ -34,9 +34,9 @@ static int FUNC(filler_payload) return 0; } -static int FUNC(user_data_registered) - (CodedBitstreamContext *ctx, RWContext *rw, - SEIRawUserDataRegistered *current, SEIMessageState *state) +SEI_FUNC(user_data_registered, (CodedBitstreamContext *ctx, RWContext *rw, + SEIRawUserDataRegistered *current, + SEIMessageState *state)) { int err, i, j; @@ -66,9 +66,9 @@ static int FUNC(user_data_registered) return 0; } -static int FUNC(user_data_unregistered) - (CodedBitstreamContext *ctx, RWContext *rw, - SEIRawUserDataUnregistered *current, SEIMessageState *state) +SEI_FUNC(user_data_unregistered, (CodedBitstreamContext *ctx, RWContext *rw, + SEIRawUserDataUnregistered *current, + SEIMessageState *state)) { int err, i; @@ -94,9 +94,10 @@ static int FUNC(user_data_unregistered) return 0; } -static int FUNC(mastering_display_colour_volume) - (CodedBitstreamContext *ctx, RWContext *rw, - SEIRawMasteringDisplayColourVolume *current, SEIMessageState *state) +SEI_FUNC(mastering_display_colour_volume, + (CodedBitstreamContext *ctx, RWContext *rw, + SEIRawMasteringDisplayColourVolume *current, + SEIMessageState *state)) { int err, c; @@ -116,9 +117,9 @@ static int FUNC(mastering_display_colour_volume) return 0; } -static int FUNC(content_light_level_info) - (CodedBitstreamContext *ctx, RWContext *rw, - SEIRawContentLightLevelInfo *current, SEIMessageState *state) +SEI_FUNC(content_light_level_info, (CodedBitstreamContext *ctx, RWContext *rw, + SEIRawContentLightLevelInfo *current, + SEIMessageState *state)) { int err; @@ -130,10 +131,10 @@ static int FUNC(content_light_level_info) return 0; } -static int FUNC(alternative_transfer_characteristics) - (CodedBitstreamContext *ctx, RWContext *rw, - SEIRawAlternativeTransferCharacteristics *current, - SEIMessageState *state) +SEI_FUNC(alternative_transfer_characteristics, + (CodedBitstreamContext *ctx, RWContext *rw, + SEIRawAlternativeTransferCharacteristics *current, + SEIMessageState *state)) { int err; @@ -144,10 +145,10 @@ static int FUNC(alternative_transfer_characteristics) return 0; } -static int FUNC(ambient_viewing_environment) - (CodedBitstreamContext *ctx, RWContext *rw, - SEIRawAmbientViewingEnvironment *current, - SEIMessageState *state) +SEI_FUNC(ambient_viewing_environment, + (CodedBitstreamContext *ctx, RWContext *rw, + SEIRawAmbientViewingEnvironment *current, + SEIMessageState *state)) { static const uint16_t max_ambient_light_value = 50000; int err; @@ -234,7 +235,12 @@ static int FUNC(message)(CodedBitstreamContext *ctx, RWContext *rw, } else { uint8_t *data; +#ifdef READ + allocate(current->payload_ref, current->payload_size); + current->payload = current->payload_ref; +#else allocate(current->payload, current->payload_size); +#endif data = current->payload; for (i = 0; i < current->payload_size; i++) diff --git a/libavcodec/cbs_vp8.c b/libavcodec/cbs_vp8.c new file mode 100644 index 00000000000..1f80f34fafe --- /dev/null +++ b/libavcodec/cbs_vp8.c @@ -0,0 +1,386 @@ +/* + * 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/avassert.h" + +#include "cbs.h" +#include "cbs_internal.h" +#include "cbs_vp8.h" + +#include + +#define DEFAULT_PROB 0x80 + +// The probability table is defined in 'vp8data.c'. +extern const uint8_t ff_vp8_token_update_probs[4][8][3][11]; + +// Implements VP8 boolean decoder using GetBitContext to read the bitstream. +typedef struct CBSVP8BoolDecoder { + GetBitContext *gbc; + + uint8_t value; + uint8_t range; + + uint8_t count; // Store the number of bits in the `value` buffer. + +} CBSVP8BoolDecoder; + +static int cbs_vp8_bool_decoder_init(CBSVP8BoolDecoder *decoder, GetBitContext *gbc) +{ + av_assert0(decoder); + av_assert0(gbc); + + decoder->gbc = gbc; + decoder->value = 0; + decoder->range = 255; + + decoder->count = 0; + + return 0; +} + +static bool cbs_vp8_bool_decoder_fill_value(CBSVP8BoolDecoder *decoder) +{ + int bits = 8 - decoder->count; + + av_assert0(decoder->count <= 8); + if (decoder->count == 8) { + return true; + } + + if (get_bits_left(decoder->gbc) >= bits) { + decoder->value |= get_bits(decoder->gbc, bits); + decoder->count += bits; + } + + return (decoder->count == 8); +} + +static int cbs_vp8_bool_decoder_read_bool(CBSVP8BoolDecoder *decoder, + const uint8_t prob, uint8_t *output) +{ + uint8_t split = 1 + (((decoder->range - 1) * prob) >> 8); + + if (!cbs_vp8_bool_decoder_fill_value(decoder)) { + return AVERROR_INVALIDDATA; + } + + av_assert0(decoder->count == 8); + if (decoder->value >= split) { + *output = 1; + decoder->range -= split; + decoder->value -= split; + } else { + *output = 0; + decoder->range = split; + } + + while (decoder->range < 128) { + decoder->value <<= 1; + decoder->range <<= 1; + --decoder->count; + } + + return 0; +} + +static int cbs_vp8_bool_decoder_read_literal(CBSVP8BoolDecoder *decoder, + const uint8_t prob, + uint32_t num_bits, + uint32_t *output) +{ + int ret = 0; + + av_assert0(num_bits <= 32); + + *output = 0; + for (; num_bits > 0; --num_bits) { + uint8_t bit_output = 0; + if ((ret = cbs_vp8_bool_decoder_read_bool(decoder, prob, + &bit_output)) != 0) { + return ret; + } + + *output = (*output << 1) | bit_output; + } + + return 0; +} + +static int cbs_vp8_bool_decoder_read_unsigned( + CodedBitstreamContext *ctx, CBSVP8BoolDecoder *bool_decoder, int width, + uint8_t prob, const char *name, const int *subscripts, uint32_t *write_to, + bool trace_enable) +{ + int ret = 0; + GetBitContext *gbc = bool_decoder->gbc; + uint32_t value; + + CBS_TRACE_READ_START(); + + av_assert0(width >= 0 && width <= 8); + + ret = cbs_vp8_bool_decoder_read_literal(bool_decoder, prob, width, &value); + if (ret != 0) { + return ret; + } + + if (trace_enable) { + CBS_TRACE_READ_END(); + } + + *write_to = value; + return 0; +} + +static int cbs_vp8_bool_decoder_read_signed( + CodedBitstreamContext *ctx, CBSVP8BoolDecoder *bool_decoder, int width, + uint8_t prob, const char *name, const int *subscripts, int32_t *write_to) +{ + int ret = 0; + GetBitContext *gbc = bool_decoder->gbc; + int32_t value; + uint8_t sign = 0; + + CBS_TRACE_READ_START(); + + av_assert0(width >= 0 && width <= 8); + + ret = cbs_vp8_bool_decoder_read_literal(bool_decoder, prob, width, &value); + if (ret != 0) { + return ret; + } + + ret = cbs_vp8_bool_decoder_read_bool(bool_decoder, prob, &sign); + if (ret != 0) { + return ret; + } + + if (sign) { + value = -value; + } + + CBS_TRACE_READ_END(); + + *write_to = value; + return 0; +} + +static int cbs_vp8_read_unsigned_le(CodedBitstreamContext *ctx, + GetBitContext *gbc, int width, + const char *name, const int *subscripts, + uint32_t *write_to, uint32_t range_min, + uint32_t range_max) +{ + int32_t value; + + CBS_TRACE_READ_START(); + + av_assert0(width > 0 && width <= 24); + + if (get_bits_left(gbc) < width) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid value: bitstream ended.\n"); + return AVERROR_INVALIDDATA; + } + + value = get_bits_le(gbc, width); + + CBS_TRACE_READ_END(); + + if (value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "%s out of range: " + "%" PRIu32 ", but must be in [%" PRIu32 ",%" PRIu32 "].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + *write_to = value; + return 0; +} + +#define HEADER(name) \ + do { \ + ff_cbs_trace_header(ctx, name); \ + } while (0) + +#define CHECK(call) \ + do { \ + int err = (call); \ + if (err < 0) \ + return err; \ + } while (0) + +#define FUNC_NAME(rw, codec, name) cbs_##codec##_##rw##_##name +#define FUNC_VP8(rw, name) FUNC_NAME(rw, vp8, name) +#define FUNC(name) FUNC_VP8(READWRITE, name) + +#define SUBSCRIPTS(subs, ...) \ + (subs > 0 ? ((int[subs + 1]){subs, __VA_ARGS__}) : NULL) + +#define f(width, name) xf(width, name, 0, ) + +// bool [de|en]coder methods. +#define bc_f(width, name) bc_unsigned_subs(width, DEFAULT_PROB, true, name, 0, ) +#define bc_s(width, name) bc_signed_subs(width, DEFAULT_PROB, name, 0, ) +#define bc_fs(width, name, subs, ...) \ + bc_unsigned_subs(width, DEFAULT_PROB, true, name, subs, __VA_ARGS__) +#define bc_ss(width, name, subs, ...) \ + bc_signed_subs(width, DEFAULT_PROB, name, subs, __VA_ARGS__) + +// bool [de|en]coder methods for boolean value and disable tracing. +#define bc_b(name) bc_unsigned_subs(1, DEFAULT_PROB, false, name, 0, ) +#define bc_b_prob(prob, name) bc_unsigned_subs(1, prob, false, name, 0, ) + +#define READ +#define READWRITE read +#define RWContext GetBitContext +#define CBSVP8BoolCodingRW CBSVP8BoolDecoder + +#define xf(width, name, subs, ...) \ + do { \ + uint32_t value; \ + CHECK(cbs_vp8_read_unsigned_le(ctx, rw, width, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), &value, \ + 0, MAX_UINT_BITS(width))); \ + current->name = value; \ + } while (0) + +#define fixed(width, name, value) \ + do { \ + uint32_t fixed_value; \ + CHECK(cbs_vp8_read_unsigned_le(ctx, rw, width, #name, 0, &fixed_value, \ + value, value)); \ + } while (0) + +#define bc_unsigned_subs(width, prob, enable_trace, name, subs, ...) \ + do { \ + uint32_t value; \ + CHECK(cbs_vp8_bool_decoder_read_unsigned( \ + ctx, bool_coding_rw, width, prob, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), &value, enable_trace)); \ + current->name = value; \ + } while (0) + +#define bc_signed_subs(width, prob, name, subs, ...) \ + do { \ + int32_t value; \ + CHECK(cbs_vp8_bool_decoder_read_signed( \ + ctx, bool_coding_rw, width, prob, #name, \ + SUBSCRIPTS(subs, __VA_ARGS__), &value)); \ + current->name = value; \ + } while (0) + +#include "cbs_vp8_syntax_template.c" + +static int cbs_vp8_split_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, int header) +{ + int err; + + if (frag->data_size == 0) + return AVERROR_INVALIDDATA; + + err = ff_cbs_append_unit_data(frag, 0, frag->data, frag->data_size, + frag->data_ref); + if (err < 0) + return err; + + return 0; +} + +static int cbs_vp8_read_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) +{ + VP8RawFrame *frame; + GetBitContext gbc; + CBSVP8BoolDecoder bool_decoder; + int err, pos; + + err = ff_cbs_alloc_unit_content(ctx, unit); + if (err < 0) + return err; + frame = unit->content; + + // Create GetBitContext for uncompressed header. + err = init_get_bits8_le(&gbc, unit->data, unit->data_size); + if (err < 0) + return err; + + err = cbs_vp8_read_uncompressed_header(ctx, &gbc, frame); + if (err < 0) + return err; + + pos = get_bits_count(&gbc); + av_assert0(pos % 8 == 0); + + // Create boolean decoder for compressed header. + err = cbs_vp8_bool_decoder_init(&bool_decoder, &gbc); + if (err < 0) + return err; + + err = cbs_vp8_read_compressed_header(ctx, &bool_decoder, frame); + if (err < 0) + return err; + + pos = get_bits_count(&gbc); + // Position may not be byte-aligned after compressed header; Round up byte + // count for accurate data positioning. + pos = (pos + 7) / 8; + av_assert0(pos <= unit->data_size); + + frame->data_ref = av_buffer_ref(unit->data_ref); + if (!frame->data_ref) + return AVERROR(ENOMEM); + + frame->data = unit->data + pos; + frame->data_size = unit->data_size - pos; + + return 0; +} + +static int cbs_vp8_write_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, PutBitContext *pbc) +{ + return AVERROR_PATCHWELCOME; +} + +static int cbs_vp8_assemble_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) +{ + return AVERROR_PATCHWELCOME; +} + +static const CodedBitstreamUnitTypeDescriptor cbs_vp8_unit_types[] = { + CBS_UNIT_TYPE_INTERNAL_REF(0, VP8RawFrame, data), + CBS_UNIT_TYPE_END_OF_LIST, +}; + +const CodedBitstreamType ff_cbs_type_vp8 = { + .codec_id = AV_CODEC_ID_VP8, + + .priv_data_size = 0, + + .unit_types = cbs_vp8_unit_types, + + .split_fragment = &cbs_vp8_split_fragment, + .read_unit = &cbs_vp8_read_unit, + .write_unit = &cbs_vp8_write_unit, + + .assemble_fragment = &cbs_vp8_assemble_fragment, +}; diff --git a/libavcodec/cbs_vp8.h b/libavcodec/cbs_vp8.h new file mode 100644 index 00000000000..9c09e21a1c1 --- /dev/null +++ b/libavcodec/cbs_vp8.h @@ -0,0 +1,133 @@ +/* + * 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 + */ + +#ifndef AVCODEC_CBS_VP8_H +#define AVCODEC_CBS_VP8_H + +#include +#include + +#include "cbs.h" + +enum { + VP8_START_CODE_0 = 0x9D, + VP8_START_CODE_1 = 0x01, + VP8_START_CODE_2 = 0x2A, +}; + +enum { + VP8_KEY_FRAME = 0, + VP8_NON_KEY_FRAME = 1, +}; + +typedef struct VP8RawFrameHeader { + // frame tag + uint8_t frame_type; + uint8_t profile; + uint8_t show_frame; + uint32_t first_partition_length_in_bytes; + + uint16_t width; + uint8_t horizontal_scale; + uint16_t height; + uint8_t vertical_scale; + + // frame header + uint8_t color_space; + uint8_t clamping_type; + + // segmentation + uint8_t segmentation_enable; + uint8_t update_segment_map; + uint8_t update_segment_feature_data; + uint8_t segment_feature_mode; + uint8_t segment_qp_update[4]; + int8_t segment_qp[4]; + uint8_t segment_loop_filter_level_update[4]; + int8_t segment_loop_filter_level[4]; + uint8_t segment_probs_update[3]; + uint8_t segment_probs[3]; + + // loop filter + uint8_t loop_filter_type; + uint8_t loop_filter_level; + uint8_t loop_filter_sharpness; + uint8_t mode_ref_lf_delta_enable; + uint8_t mode_ref_lf_delta_update; + uint8_t ref_lf_deltas_update[4]; + int8_t ref_lf_deltas[4]; + uint8_t mode_lf_deltas_update[4]; + int8_t mode_lf_deltas[4]; + + uint8_t log2_token_partitions; + + // qp + uint8_t base_qindex; + uint8_t y1dc_delta_q_present; + int8_t y1dc_delta_q; + uint8_t y2dc_delta_q_present; + int8_t y2dc_delta_q; + uint8_t y2ac_delta_q_present; + int8_t y2ac_delta_q; + uint8_t uvdc_delta_q_present; + int8_t uvdc_delta_q; + uint8_t uvac_delta_q_present; + int8_t uvac_delta_q; + + // ref + uint8_t refresh_golden_frame; + uint8_t refresh_alternate_frame; + uint8_t copy_buffer_to_golden; + uint8_t copy_buffer_to_alternate; + uint8_t ref_frame_sign_bias_golden; + uint8_t ref_frame_sign_bias_alternate; + uint8_t refresh_last_frame; + + uint8_t refresh_entropy_probs; + + // token probs + uint8_t coeff_prob_update[4][8][3][11]; + uint8_t coeff_prob[4][8][3][11]; + + uint8_t mb_no_skip_coeff; + uint8_t prob_skip_false; + + uint8_t prob_intra; + uint8_t prob_last; + uint8_t prob_golden; + + uint8_t intra_16x16_prob_update; + uint8_t intra_16x16_prob[4]; + + uint8_t intra_chrome_prob_update; + uint8_t intra_chrome_prob[3]; + + // mv probs + uint8_t mv_prob_update[2][19]; + uint8_t mv_prob[2][19]; +} VP8RawFrameHeader; + +typedef struct VP8RawFrame { + VP8RawFrameHeader header; + + uint8_t *data; + AVBufferRef *data_ref; + size_t data_size; +} VP8RawFrame; + +#endif /* AVCODEC_CBS_VP8_H */ diff --git a/libavcodec/cbs_vp8_syntax_template.c b/libavcodec/cbs_vp8_syntax_template.c new file mode 100644 index 00000000000..264aa2f7589 --- /dev/null +++ b/libavcodec/cbs_vp8_syntax_template.c @@ -0,0 +1,248 @@ +/* + * 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 + */ + +static int FUNC(update_segmentation)(CodedBitstreamContext *ctx, + CBSVP8BoolCodingRW *bool_coding_rw, + VP8RawFrameHeader *current) +{ + bc_f(1, update_segment_map); + bc_f(1, update_segment_feature_data); + + if (current->update_segment_feature_data) { + bc_f(1, segment_feature_mode); + // quantizer + for (int i = 0; i < 4; i++) { + bc_b(segment_qp_update[i]); + if (current->segment_qp_update[i]) + bc_ss(7, segment_qp[i], 1, i); + } + // loop filter + for (int i = 0; i < 4; i++) { + bc_b(segment_loop_filter_level_update[i]); + if (current->segment_loop_filter_level_update[i]) + bc_ss(6, segment_loop_filter_level[i], 1, i); + } + } + + if (current->update_segment_map) { + for (int i = 0; i < 3; i++) { + bc_b(segment_probs_update[i]); + if (current->segment_probs_update[i]) + bc_fs(8, segment_probs[i], 1, i); + } + } + + return 0; +} + +static int FUNC(mode_ref_lf_deltas)(CodedBitstreamContext *ctx, + CBSVP8BoolCodingRW *bool_coding_rw, + VP8RawFrameHeader *current) +{ + bc_f(1, mode_ref_lf_delta_enable); + if (current->mode_ref_lf_delta_enable) { + bc_b(mode_ref_lf_delta_update); + if (current->mode_ref_lf_delta_update) { + // ref_lf_deltas + for (int i = 0; i < 4; i++) { + bc_b(ref_lf_deltas_update[i]); + if (current->ref_lf_deltas_update[i]) + bc_ss(6, ref_lf_deltas[i], 1, i); + } + // mode_lf_deltas + for (int i = 0; i < 4; i++) { + bc_b(mode_lf_deltas_update[i]); + if (current->mode_lf_deltas_update[i]) + bc_ss(6, mode_lf_deltas[i], 1, i); + } + } + } + + return 0; +} + +static int FUNC(quantization_params)(CodedBitstreamContext *ctx, + CBSVP8BoolCodingRW *bool_coding_rw, + VP8RawFrameHeader *current) +{ + bc_f(7, base_qindex); + + bc_b(y1dc_delta_q_present); + if (current->y1dc_delta_q_present) + bc_s(4, y1dc_delta_q); + + bc_b(y2dc_delta_q_present); + if (current->y2dc_delta_q_present) + bc_s(4, y2dc_delta_q); + + bc_b(y2ac_delta_q_present); + if (current->y2ac_delta_q_present) + bc_s(4, y2ac_delta_q); + + bc_b(uvdc_delta_q_present); + if (current->uvdc_delta_q_present) + bc_s(4, uvdc_delta_q); + + bc_b(uvac_delta_q_present); + if (current->uvac_delta_q_present) + bc_s(4, uvac_delta_q); + + return 0; +} + +static int FUNC(update_token_probs)(CodedBitstreamContext *ctx, + CBSVP8BoolCodingRW *bool_coding_rw, + VP8RawFrameHeader *current) +{ + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 8; ++j) { + for (int k = 0; k < 3; ++k) { + for (int l = 0; l < 11; ++l) { + bc_b_prob(ff_vp8_token_update_probs[i][j][k][l], + coeff_prob_update[i][j][k][l]); + if (current->coeff_prob_update[i][j][k][l]) + bc_fs(8, coeff_prob[i][j][k][l], 4, i, j, k, l); + } + } + } + } + + return 0; +} + +static int FUNC(update_mv_probs)(CodedBitstreamContext *ctx, + CBSVP8BoolCodingRW *bool_coding_rw, + VP8RawFrameHeader *current) +{ + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 19; ++j) { + bc_b(mv_prob_update[i][j]); + if (current->mv_prob_update[i][j]) + bc_fs(7, mv_prob[i][j], 2, i, j); + } + } + + return 0; +} + +static int FUNC(frame_tag)(CodedBitstreamContext *ctx, RWContext *rw, + VP8RawFrameHeader *current) +{ + f(1, frame_type); + f(3, profile); + f(1, show_frame); + f(19, first_partition_length_in_bytes); + + if (current->frame_type == VP8_KEY_FRAME) { + fixed(8, start_code_0, VP8_START_CODE_0); + fixed(8, start_code_1, VP8_START_CODE_1); + fixed(8, start_code_2, VP8_START_CODE_2); + + f(14, width); + f(2, horizontal_scale); + f(14, height); + f(2, vertical_scale); + } + + return 0; +} + +static int FUNC(frame_header)(CodedBitstreamContext *ctx, + CBSVP8BoolCodingRW *bool_coding_rw, + VP8RawFrameHeader *current) +{ + if (current->frame_type == VP8_KEY_FRAME) { + bc_f(1, color_space); + bc_f(1, clamping_type); + } + + bc_f(1, segmentation_enable); + if (current->segmentation_enable) + CHECK(FUNC(update_segmentation)(ctx, bool_coding_rw, current)); + + bc_f(1, loop_filter_type); + bc_f(6, loop_filter_level); + bc_f(3, loop_filter_sharpness); + + CHECK(FUNC(mode_ref_lf_deltas)(ctx, bool_coding_rw, current)); + + bc_f(2, log2_token_partitions); + + CHECK(FUNC(quantization_params)(ctx, bool_coding_rw, current)); + + if (current->frame_type != VP8_KEY_FRAME) { + bc_f(1, refresh_golden_frame); + bc_f(1, refresh_alternate_frame); + if (!current->refresh_golden_frame) + bc_f(2, copy_buffer_to_golden); + if (!current->refresh_alternate_frame) + bc_f(2, copy_buffer_to_alternate); + bc_f(1, ref_frame_sign_bias_golden); + bc_f(1, ref_frame_sign_bias_alternate); + } + bc_f(1, refresh_entropy_probs); + if (current->frame_type != VP8_KEY_FRAME) + bc_f(1, refresh_last_frame); + + CHECK(FUNC(update_token_probs)(ctx, bool_coding_rw, current)); + + bc_f(1, mb_no_skip_coeff); + if (current->mb_no_skip_coeff) + bc_f(8, prob_skip_false); + + if (current->frame_type != VP8_KEY_FRAME) { + bc_f(8, prob_intra); + bc_f(8, prob_last); + bc_f(8, prob_golden); + + // intra_16x16_prob + bc_b(intra_16x16_prob_update); + if (current->intra_16x16_prob_update) + for (int i = 0; i < 4; i++) + bc_fs(8, intra_16x16_prob[i], 1, i); + + // intra_chroma_prob + bc_b(intra_chrome_prob_update); + if (current->intra_chrome_prob_update) + for (int i = 0; i < 3; i++) + bc_fs(8, intra_chrome_prob[i], 1, i); + + CHECK(FUNC(update_mv_probs)(ctx, bool_coding_rw, current)); + } + + return 0; +} + +static int FUNC(uncompressed_header)(CodedBitstreamContext *ctx, RWContext *rw, + VP8RawFrame *current) +{ + HEADER("Frame"); + + CHECK(FUNC(frame_tag)(ctx, rw, ¤t->header)); + + return 0; +} + +static int FUNC(compressed_header)(CodedBitstreamContext *ctx, + CBSVP8BoolCodingRW *bool_coding_rw, + VP8RawFrame *current) +{ + CHECK(FUNC(frame_header)(ctx, bool_coding_rw, ¤t->header)); + + return 0; +} diff --git a/libavcodec/cbs_vp9.c b/libavcodec/cbs_vp9.c index 184fdcade6f..816d06da04d 100644 --- a/libavcodec/cbs_vp9.c +++ b/libavcodec/cbs_vp9.c @@ -28,11 +28,10 @@ static int cbs_vp9_read_s(CodedBitstreamContext *ctx, GetBitContext *gbc, const int *subscripts, int32_t *write_to) { uint32_t magnitude; - int position, sign; + int sign; int32_t value; - if (ctx->trace_enable) - position = get_bits_count(gbc); + CBS_TRACE_READ_START(); if (get_bits_left(gbc) < width + 1) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid signed value at " @@ -44,17 +43,7 @@ static int cbs_vp9_read_s(CodedBitstreamContext *ctx, GetBitContext *gbc, sign = get_bits1(gbc); value = sign ? -(int32_t)magnitude : magnitude; - if (ctx->trace_enable) { - char bits[33]; - int i; - for (i = 0; i < width; i++) - bits[i] = magnitude >> (width - i - 1) & 1 ? '1' : '0'; - bits[i] = sign ? '1' : '0'; - bits[i + 1] = 0; - - ff_cbs_trace_syntax_element(ctx, position, name, subscripts, - bits, value); - } + CBS_TRACE_READ_END(); *write_to = value; return 0; @@ -67,27 +56,19 @@ static int cbs_vp9_write_s(CodedBitstreamContext *ctx, PutBitContext *pbc, uint32_t magnitude; int sign; + CBS_TRACE_WRITE_START(); + if (put_bits_left(pbc) < width + 1) return AVERROR(ENOSPC); sign = value < 0; magnitude = sign ? -value : value; - if (ctx->trace_enable) { - char bits[33]; - int i; - for (i = 0; i < width; i++) - bits[i] = magnitude >> (width - i - 1) & 1 ? '1' : '0'; - bits[i] = sign ? '1' : '0'; - bits[i + 1] = 0; - - ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), - name, subscripts, bits, value); - } - put_bits(pbc, width, magnitude); put_bits(pbc, 1, sign); + CBS_TRACE_WRITE_END(); + return 0; } @@ -96,32 +77,24 @@ static int cbs_vp9_read_increment(CodedBitstreamContext *ctx, GetBitContext *gbc const char *name, uint32_t *write_to) { uint32_t value; - int position, i; - char bits[8]; - av_assert0(range_min <= range_max && range_max - range_min < sizeof(bits) - 1); - if (ctx->trace_enable) - position = get_bits_count(gbc); + CBS_TRACE_READ_START(); + + av_assert0(range_min <= range_max && range_max - range_min < 32); - for (i = 0, value = range_min; value < range_max;) { + for (value = range_min; value < range_max;) { if (get_bits_left(gbc) < 1) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid increment value at " "%s: bitstream ended.\n", name); return AVERROR_INVALIDDATA; } - if (get_bits1(gbc)) { - bits[i++] = '1'; + if (get_bits1(gbc)) ++value; - } else { - bits[i++] = '0'; + else break; - } } - if (ctx->trace_enable) { - bits[i] = 0; - ff_cbs_trace_syntax_element(ctx, position, name, NULL, bits, value); - } + CBS_TRACE_READ_END_NO_SUBSCRIPTS(); *write_to = value; return 0; @@ -133,6 +106,8 @@ static int cbs_vp9_write_increment(CodedBitstreamContext *ctx, PutBitContext *pb { int len; + CBS_TRACE_WRITE_START(); + av_assert0(range_min <= range_max && range_max - range_min < 8); if (value < range_min || value > range_max) { av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " @@ -148,23 +123,11 @@ static int cbs_vp9_write_increment(CodedBitstreamContext *ctx, PutBitContext *pb if (put_bits_left(pbc) < len) return AVERROR(ENOSPC); - if (ctx->trace_enable) { - char bits[8]; - int i; - for (i = 0; i < len; i++) { - if (range_min + i == value) - bits[i] = '0'; - else - bits[i] = '1'; - } - bits[i] = 0; - ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), - name, NULL, bits, value); - } - if (len > 0) put_bits(pbc, len, (1 << len) - 1 - (value != range_max)); + CBS_TRACE_WRITE_END_NO_SUBSCRIPTS(); + return 0; } @@ -173,12 +136,11 @@ static int cbs_vp9_read_le(CodedBitstreamContext *ctx, GetBitContext *gbc, const int *subscripts, uint32_t *write_to) { uint32_t value; - int position, b; + int b; - av_assert0(width % 8 == 0); + CBS_TRACE_READ_START(); - if (ctx->trace_enable) - position = get_bits_count(gbc); + av_assert0(width % 8 == 0); if (get_bits_left(gbc) < width) { av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid le value at " @@ -190,17 +152,7 @@ static int cbs_vp9_read_le(CodedBitstreamContext *ctx, GetBitContext *gbc, for (b = 0; b < width; b += 8) value |= get_bits(gbc, 8) << b; - if (ctx->trace_enable) { - char bits[33]; - int i; - for (b = 0; b < width; b += 8) - for (i = 0; i < 8; i++) - bits[b + i] = value >> (b + i) & 1 ? '1' : '0'; - bits[b] = 0; - - ff_cbs_trace_syntax_element(ctx, position, name, subscripts, - bits, value); - } + CBS_TRACE_READ_END(); *write_to = value; return 0; @@ -212,26 +164,18 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, { int b; + CBS_TRACE_WRITE_START(); + av_assert0(width % 8 == 0); if (put_bits_left(pbc) < width) return AVERROR(ENOSPC); - if (ctx->trace_enable) { - char bits[33]; - int i; - for (b = 0; b < width; b += 8) - for (i = 0; i < 8; i++) - bits[b + i] = value >> (b + i) & 1 ? '1' : '0'; - bits[b] = 0; - - ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), - name, subscripts, bits, value); - } - for (b = 0; b < width; b += 8) put_bits(pbc, 8, value >> b & 0xff); + CBS_TRACE_WRITE_END(); + return 0; } @@ -251,8 +195,6 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, #define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL) -#define f(width, name) \ - xf(width, name, current->name, 0, ) #define s(width, name) \ xs(width, name, current->name, 0, ) #define fs(width, name, subs, ...) \ @@ -264,6 +206,12 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, #define READWRITE read #define RWContext GetBitContext +#define f(width, name) do { \ + uint32_t value; \ + CHECK(ff_cbs_read_simple_unsigned(ctx, rw, width, #name, \ + &value)); \ + current->name = value; \ + } while (0) #define xf(width, name, var, subs, ...) do { \ uint32_t value; \ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ @@ -329,6 +277,7 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, #undef READ #undef READWRITE #undef RWContext +#undef f #undef xf #undef xs #undef increment @@ -344,6 +293,10 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, #define READWRITE write #define RWContext PutBitContext +#define f(width, name) do { \ + CHECK(ff_cbs_write_simple_unsigned(ctx, rw, width, #name, \ + current->name)); \ + } while (0) #define xf(width, name, var, subs, ...) do { \ CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ SUBSCRIPTS(subs, __VA_ARGS__), \ @@ -396,6 +349,7 @@ static int cbs_vp9_write_le(CodedBitstreamContext *ctx, PutBitContext *pbc, #undef WRITE #undef READWRITE #undef RWContext +#undef f #undef xf #undef xs #undef increment diff --git a/libavcodec/ccaption_dec.c b/libavcodec/ccaption_dec.c index 61eda9ff56f..d8b992bb949 100644 --- a/libavcodec/ccaption_dec.c +++ b/libavcodec/ccaption_dec.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include "avcodec.h" #include "ass.h" #include "codec_internal.h" @@ -67,108 +68,123 @@ enum cc_charset { CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH, }; -static const char *charset_overrides[4][128] = +#define CHARSET_OVERRIDE_LIST(START_SET, ENTRY, END_SET) \ + START_SET(CCSET_BASIC_AMERICAN) \ + ENTRY(0x27, "\u2019") \ + ENTRY(0x2a, "\u00e1") \ + ENTRY(0x5c, "\u00e9") \ + ENTRY(0x5e, "\u00ed") \ + ENTRY(0x5f, "\u00f3") \ + ENTRY(0x60, "\u00fa") \ + ENTRY(0x7b, "\u00e7") \ + ENTRY(0x7c, "\u00f7") \ + ENTRY(0x7d, "\u00d1") \ + ENTRY(0x7e, "\u00f1") \ + ENTRY(0x7f, "\u2588") \ + END_SET \ + START_SET(CCSET_SPECIAL_AMERICAN) \ + ENTRY(0x30, "\u00ae") \ + ENTRY(0x31, "\u00b0") \ + ENTRY(0x32, "\u00bd") \ + ENTRY(0x33, "\u00bf") \ + ENTRY(0x34, "\u2122") \ + ENTRY(0x35, "\u00a2") \ + ENTRY(0x36, "\u00a3") \ + ENTRY(0x37, "\u266a") \ + ENTRY(0x38, "\u00e0") \ + ENTRY(0x39, "\u00A0") \ + ENTRY(0x3a, "\u00e8") \ + ENTRY(0x3b, "\u00e2") \ + ENTRY(0x3c, "\u00ea") \ + ENTRY(0x3d, "\u00ee") \ + ENTRY(0x3e, "\u00f4") \ + ENTRY(0x3f, "\u00fb") \ + END_SET \ + START_SET(CCSET_EXTENDED_SPANISH_FRENCH_MISC) \ + ENTRY(0x20, "\u00c1") \ + ENTRY(0x21, "\u00c9") \ + ENTRY(0x22, "\u00d3") \ + ENTRY(0x23, "\u00da") \ + ENTRY(0x24, "\u00dc") \ + ENTRY(0x25, "\u00fc") \ + ENTRY(0x26, "\u00b4") \ + ENTRY(0x27, "\u00a1") \ + ENTRY(0x28, "*") \ + ENTRY(0x29, "\u2018") \ + ENTRY(0x2a, "-") \ + ENTRY(0x2b, "\u00a9") \ + ENTRY(0x2c, "\u2120") \ + ENTRY(0x2d, "\u00b7") \ + ENTRY(0x2e, "\u201c") \ + ENTRY(0x2f, "\u201d") \ + ENTRY(0x30, "\u00c0") \ + ENTRY(0x31, "\u00c2") \ + ENTRY(0x32, "\u00c7") \ + ENTRY(0x33, "\u00c8") \ + ENTRY(0x34, "\u00ca") \ + ENTRY(0x35, "\u00cb") \ + ENTRY(0x36, "\u00eb") \ + ENTRY(0x37, "\u00ce") \ + ENTRY(0x38, "\u00cf") \ + ENTRY(0x39, "\u00ef") \ + ENTRY(0x3a, "\u00d4") \ + ENTRY(0x3b, "\u00d9") \ + ENTRY(0x3c, "\u00f9") \ + ENTRY(0x3d, "\u00db") \ + ENTRY(0x3e, "\u00ab") \ + ENTRY(0x3f, "\u00bb") \ + END_SET \ + START_SET(CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH) \ + ENTRY(0x20, "\u00c3") \ + ENTRY(0x21, "\u00e3") \ + ENTRY(0x22, "\u00cd") \ + ENTRY(0x23, "\u00cc") \ + ENTRY(0x24, "\u00ec") \ + ENTRY(0x25, "\u00d2") \ + ENTRY(0x26, "\u00f2") \ + ENTRY(0x27, "\u00d5") \ + ENTRY(0x28, "\u00f5") \ + ENTRY(0x29, "{") \ + ENTRY(0x2a, "}") \ + ENTRY(0x2b, "\\") \ + ENTRY(0x2c, "^") \ + ENTRY(0x2d, "_") \ + ENTRY(0x2e, "|") \ + ENTRY(0x2f, "~") \ + ENTRY(0x30, "\u00c4") \ + ENTRY(0x31, "\u00e4") \ + ENTRY(0x32, "\u00d6") \ + ENTRY(0x33, "\u00f6") \ + ENTRY(0x34, "\u00df") \ + ENTRY(0x35, "\u00a5") \ + ENTRY(0x36, "\u00a4") \ + ENTRY(0x37, "\u00a6") \ + ENTRY(0x38, "\u00c5") \ + ENTRY(0x39, "\u00e5") \ + ENTRY(0x3a, "\u00d8") \ + ENTRY(0x3b, "\u00f8") \ + ENTRY(0x3c, "\u250c") \ + ENTRY(0x3d, "\u2510") \ + ENTRY(0x3e, "\u2514") \ + ENTRY(0x3f, "\u2518") \ + END_SET \ + +static const char charset_overrides[4][128][sizeof("\u266a")] = { - [CCSET_BASIC_AMERICAN] = { - [0x27] = "\u2019", - [0x2a] = "\u00e1", - [0x5c] = "\u00e9", - [0x5e] = "\u00ed", - [0x5f] = "\u00f3", - [0x60] = "\u00fa", - [0x7b] = "\u00e7", - [0x7c] = "\u00f7", - [0x7d] = "\u00d1", - [0x7e] = "\u00f1", - [0x7f] = "\u2588" - }, - [CCSET_SPECIAL_AMERICAN] = { - [0x30] = "\u00ae", - [0x31] = "\u00b0", - [0x32] = "\u00bd", - [0x33] = "\u00bf", - [0x34] = "\u2122", - [0x35] = "\u00a2", - [0x36] = "\u00a3", - [0x37] = "\u266a", - [0x38] = "\u00e0", - [0x39] = "\u00A0", - [0x3a] = "\u00e8", - [0x3b] = "\u00e2", - [0x3c] = "\u00ea", - [0x3d] = "\u00ee", - [0x3e] = "\u00f4", - [0x3f] = "\u00fb", - }, - [CCSET_EXTENDED_SPANISH_FRENCH_MISC] = { - [0x20] = "\u00c1", - [0x21] = "\u00c9", - [0x22] = "\u00d3", - [0x23] = "\u00da", - [0x24] = "\u00dc", - [0x25] = "\u00fc", - [0x26] = "\u00b4", - [0x27] = "\u00a1", - [0x28] = "*", - [0x29] = "\u2018", - [0x2a] = "-", - [0x2b] = "\u00a9", - [0x2c] = "\u2120", - [0x2d] = "\u00b7", - [0x2e] = "\u201c", - [0x2f] = "\u201d", - [0x30] = "\u00c0", - [0x31] = "\u00c2", - [0x32] = "\u00c7", - [0x33] = "\u00c8", - [0x34] = "\u00ca", - [0x35] = "\u00cb", - [0x36] = "\u00eb", - [0x37] = "\u00ce", - [0x38] = "\u00cf", - [0x39] = "\u00ef", - [0x3a] = "\u00d4", - [0x3b] = "\u00d9", - [0x3c] = "\u00f9", - [0x3d] = "\u00db", - [0x3e] = "\u00ab", - [0x3f] = "\u00bb", - }, - [CCSET_EXTENDED_PORTUGUESE_GERMAN_DANISH] = { - [0x20] = "\u00c3", - [0x21] = "\u00e3", - [0x22] = "\u00cd", - [0x23] = "\u00cc", - [0x24] = "\u00ec", - [0x25] = "\u00d2", - [0x26] = "\u00f2", - [0x27] = "\u00d5", - [0x28] = "\u00f5", - [0x29] = "{", - [0x2a] = "}", - [0x2b] = "\\", - [0x2c] = "^", - [0x2d] = "_", - [0x2e] = "|", - [0x2f] = "~", - [0x30] = "\u00c4", - [0x31] = "\u00e4", - [0x32] = "\u00d6", - [0x33] = "\u00f6", - [0x34] = "\u00df", - [0x35] = "\u00a5", - [0x36] = "\u00a4", - [0x37] = "\u00a6", - [0x38] = "\u00c5", - [0x39] = "\u00e5", - [0x3a] = "\u00d8", - [0x3b] = "\u00f8", - [0x3c] = "\u250c", - [0x3d] = "\u2510", - [0x3e] = "\u2514", - [0x3f] = "\u2518", +#define START_SET(IDX) \ + [IDX] = { +#define ENTRY(idx, string) \ + [idx] = string, +#define END_SET \ }, + CHARSET_OVERRIDE_LIST(START_SET, ENTRY, END_SET) }; +#define EMPTY_START(IDX) +#define EMPTY_END +#define ASSERT_ENTRY(IDX, str) \ + static_assert(sizeof(str) <= sizeof(charset_overrides[0][0]), \ + "'" str "' string takes too much space"); +CHARSET_OVERRIDE_LIST(EMPTY_START, ASSERT_ENTRY, EMPTY_END) static const unsigned char bg_attribs[8] = // Color { @@ -262,7 +278,6 @@ typedef struct CCaptionSubContext { static av_cold int init_decoder(AVCodecContext *avctx) { - int ret; CCaptionSubContext *ctx = avctx->priv_data; av_bprint_init(&ctx->buffer[0], 0, AV_BPRINT_SIZE_UNLIMITED); @@ -272,7 +287,7 @@ static av_cold int init_decoder(AVCodecContext *avctx) ctx->bg_color = CCCOL_BLACK; ctx->rollup = 2; ctx->cursor_row = 10; - ret = ff_ass_subtitle_header(avctx, "Monospace", + return ff_ass_subtitle_header(avctx, "Monospace", ASS_DEFAULT_FONT_SIZE, ASS_DEFAULT_COLOR, ASS_DEFAULT_BACK_COLOR, @@ -281,11 +296,6 @@ static av_cold int init_decoder(AVCodecContext *avctx) ASS_DEFAULT_UNDERLINE, 3, ASS_DEFAULT_ALIGNMENT); - if (ret < 0) { - return ret; - } - - return ret; } static av_cold int close_decoder(AVCodecContext *avctx) @@ -349,7 +359,7 @@ static void write_char(CCaptionSubContext *ctx, struct Screen *screen, char ch) return; } else { - av_log(ctx, AV_LOG_WARNING, "Data Ignored since exceeding screen width\n"); + av_log(ctx, AV_LOG_WARNING, "Data ignored due to columns exceeding screen width\n"); return; } } @@ -577,7 +587,7 @@ static int capture_screen(CCaptionSubContext *ctx) prev_color = color[j]; prev_bg_color = bg[j]; override = charset_overrides[(int)charset[j]][(int)row[j]]; - if (override) { + if (override[0]) { av_bprintf(&ctx->buffer[bidx], "%s%s%s%s%s", e_tag, s_tag, c_tag, b_tag, override); seen_char = 1; } else if (row[j] == ' ' && !seen_char) { @@ -900,6 +910,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub, ret = ff_ass_add_rect2(sub, ctx->buffer[bidx].str, ctx->readorder++, 0, NULL, NULL, &nb_rect_allocated); if (ret < 0) return ret; + av_bprint_clear(&ctx->buffer[bidx]); sub->pts = ctx->buffer_time[1]; sub->end_display_time = av_rescale_q(ctx->buffer_time[1] - ctx->buffer_time[0], AV_TIME_BASE_Q, ms_tb); @@ -922,7 +933,7 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub, } *got_sub = sub->num_rects > 0; - return ret; + return avpkt->size; } #define OFFSET(x) offsetof(CCaptionSubContext, x) @@ -930,15 +941,15 @@ static int decode(AVCodecContext *avctx, AVSubtitle *sub, static const AVOption options[] = { { "real_time", "emit subtitle events as they are decoded for real-time display", OFFSET(real_time), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD }, { "real_time_latency_msec", "minimum elapsed time between emitting real-time subtitle events", OFFSET(real_time_latency_msec), AV_OPT_TYPE_INT, { .i64 = 200 }, 0, 500, SD }, - { "data_field", "select data field", OFFSET(data_field), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, SD, "data_field" }, - { "auto", "pick first one that appears", 0, AV_OPT_TYPE_CONST, { .i64 =-1 }, 0, 0, SD, "data_field" }, - { "first", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, SD, "data_field" }, - { "second", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, SD, "data_field" }, + { "data_field", "select data field", OFFSET(data_field), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, SD, .unit = "data_field" }, + { "auto", "pick first one that appears", 0, AV_OPT_TYPE_CONST, { .i64 =-1 }, 0, 0, SD, .unit = "data_field" }, + { "first", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, SD, .unit = "data_field" }, + { "second", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, SD, .unit = "data_field" }, {NULL} }; static const AVClass ccaption_dec_class = { - .class_name = "Closed caption Decoder", + .class_name = "Closed Captions Decoder", .item_name = av_default_item_name, .option = options, .version = LIBAVUTIL_VERSION_INT, @@ -946,7 +957,7 @@ static const AVClass ccaption_dec_class = { const FFCodec ff_ccaption_decoder = { .p.name = "cc_dec", - CODEC_LONG_NAME("Closed Caption (EIA-608 / CEA-708)"), + CODEC_LONG_NAME("Closed Captions (EIA-608 / CEA-708)"), .p.type = AVMEDIA_TYPE_SUBTITLE, .p.id = AV_CODEC_ID_EIA_608, .p.priv_class = &ccaption_dec_class, diff --git a/libavcodec/cdgraphics.c b/libavcodec/cdgraphics.c index 431e99cd762..f33f7fbf00b 100644 --- a/libavcodec/cdgraphics.c +++ b/libavcodec/cdgraphics.c @@ -90,19 +90,19 @@ static av_cold int cdg_decode_init(AVCodecContext *avctx) static void cdg_border_preset(CDGraphicsContext *cc, uint8_t *data) { - int y; - int lsize = cc->frame->linesize[0]; + ptrdiff_t lsize = cc->frame->linesize[0]; uint8_t *buf = cc->frame->data[0]; int color = data[0] & 0x0F; if (!(data[1] & 0x0F)) { /// fill the top and bottom borders - memset(buf, color, CDG_BORDER_HEIGHT * lsize); - memset(buf + (CDG_FULL_HEIGHT - CDG_BORDER_HEIGHT) * lsize, - color, CDG_BORDER_HEIGHT * lsize); + for (int y = 0; y < CDG_BORDER_HEIGHT; y++) + memset(buf + y * lsize, color, cc->frame->width); + for (int y = CDG_FULL_HEIGHT-CDG_BORDER_HEIGHT; y < CDG_FULL_HEIGHT; y++) + memset(buf + y * lsize, color, cc->frame->width); /// fill the side borders - for (y = CDG_BORDER_HEIGHT; y < CDG_FULL_HEIGHT - CDG_BORDER_HEIGHT; y++) { + for (int y = CDG_BORDER_HEIGHT; y < CDG_FULL_HEIGHT - CDG_BORDER_HEIGHT; y++) { memset(buf + y * lsize, color, CDG_BORDER_WIDTH); memset(buf + CDG_FULL_WIDTH - CDG_BORDER_WIDTH + y * lsize, color, CDG_BORDER_WIDTH); @@ -125,7 +125,11 @@ static void cdg_load_palette(CDGraphicsContext *cc, uint8_t *data, int low) b = ((color ) & 0x000F) * 17; palette[i + array_offset] = (uint32_t)cc->alpha[i + array_offset] << 24 | r << 16 | g << 8 | b; } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS cc->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } static int cdg_tile_block(CDGraphicsContext *cc, uint8_t *data, int b) @@ -134,7 +138,7 @@ static int cdg_tile_block(CDGraphicsContext *cc, uint8_t *data, int b) int color; int x, y; int ai; - int stride = cc->frame->linesize[0]; + ptrdiff_t stride = cc->frame->linesize[0]; uint8_t *buf = cc->frame->data[0]; ri = (data[2] & 0x1F) * CDG_TILE_HEIGHT + cc->vscroll; @@ -206,7 +210,7 @@ static void cdg_scroll(CDGraphicsContext *cc, uint8_t *data, int color; int hscmd, h_off, hinc, vscmd, v_off, vinc; int y; - int stride = cc->frame->linesize[0]; + ptrdiff_t stride = cc->frame->linesize[0]; uint8_t *in = cc->frame->data[0]; uint8_t *out = new_frame->data[0]; @@ -245,11 +249,11 @@ static void cdg_scroll(CDGraphicsContext *cc, uint8_t *data, if (vinc > 0) cdg_fill_wrapper(0, 0, out, 0, CDG_FULL_HEIGHT - vinc, in, color, - stride, vinc, stride, roll_over); + FFABS(stride), vinc, stride, roll_over); else if (vinc < 0) cdg_fill_wrapper(0, CDG_FULL_HEIGHT + vinc, out, 0, 0, in, color, - stride, -1 * vinc, stride, roll_over); + FFABS(stride), -1 * vinc, stride, roll_over); if (hinc > 0) cdg_fill_wrapper(0, 0, out, @@ -286,7 +290,8 @@ static int cdg_decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_reget_buffer(avctx, cc->frame, 0)) < 0) return ret; if (!cc->cleared) { - memset(cc->frame->data[0], 0, cc->frame->linesize[0] * avctx->height); + for (int y = 0; y < avctx->height; y++) + memset(cc->frame->data[0] + y * cc->frame->linesize[0], 0, avctx->width); memset(cc->frame->data[1], 0, AVPALETTE_SIZE); cc->cleared = 1; } @@ -300,9 +305,11 @@ static int cdg_decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((command & CDG_MASK) == CDG_COMMAND) { switch (inst) { case CDG_INST_MEMORY_PRESET: - if (!(cdg_data[1] & 0x0F)) - memset(cc->frame->data[0], cdg_data[0] & 0x0F, - cc->frame->linesize[0] * CDG_FULL_HEIGHT); + if (!(cdg_data[1] & 0x0F)) { + for (int y = 0; y < avctx->height; y++) + memset(cc->frame->data[0] + y * cc->frame->linesize[0], + cdg_data[0] & 0x0F, avctx->width); + } break; case CDG_INST_LOAD_PAL_LO: case CDG_INST_LOAD_PAL_HIGH: @@ -340,8 +347,7 @@ static int cdg_decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; cdg_scroll(cc, cdg_data, frame, inst == CDG_INST_SCROLL_COPY); - av_frame_unref(cc->frame); - ret = av_frame_ref(cc->frame, frame); + ret = av_frame_replace(cc->frame, frame); if (ret < 0) return ret; break; @@ -373,7 +379,8 @@ static void cdg_decode_flush(AVCodecContext *avctx) if (!cc->frame->data[0]) return; - memset(cc->frame->data[0], 0, cc->frame->linesize[0] * avctx->height); + for (int y = 0; y < avctx->height; y++) + memset(cc->frame->data[0] + y * cc->frame->linesize[0], 0, avctx->width); if (!avctx->frame_num) memset(cc->frame->data[1], 0, AVPALETTE_SIZE); } diff --git a/libavcodec/cdtoons.c b/libavcodec/cdtoons.c index 3ebed2267cd..94c49f0c814 100644 --- a/libavcodec/cdtoons.c +++ b/libavcodec/cdtoons.c @@ -384,7 +384,11 @@ static int cdtoons_decode_frame(AVCodecContext *avctx, AVFrame *rframe, } /* first palette entry indicates transparency */ c->pal[0] = 0; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS c->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } } diff --git a/libavcodec/cdxl.c b/libavcodec/cdxl.c index 6b3b3e85e0f..6843fceb2ff 100644 --- a/libavcodec/cdxl.c +++ b/libavcodec/cdxl.c @@ -125,9 +125,10 @@ static void chunky2chunky(CDXLVideoContext *c, int linesize, uint8_t *out) } } -static void import_format(CDXLVideoContext *c, int linesize, uint8_t *out) +static void import_format(CDXLVideoContext *c, ptrdiff_t linesize, uint8_t *out) { - memset(out, 0, linesize * c->avctx->height); + for (int y = 0; y < c->avctx->height; y++) + memset(out + y * linesize, 0, c->avctx->width); switch (c->format) { case BIT_PLANAR: @@ -305,7 +306,7 @@ static int cdxl_decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; if (encoding) { av_fast_padded_malloc(&c->new_video, &c->new_video_size, diff --git a/libavcodec/cfhd.c b/libavcodec/cfhd.c index c23eb069c65..42d7dcc3f67 100644 --- a/libavcodec/cfhd.c +++ b/libavcodec/cfhd.c @@ -1087,8 +1087,8 @@ static int cfhd_decode(AVCodecContext *avctx, AVFrame *pic, dst += dst_linesize; } } else { - av_log(avctx, AV_LOG_DEBUG, "interlaced frame ? %d", pic->interlaced_frame); - pic->interlaced_frame = 1; + av_log(avctx, AV_LOG_DEBUG, "interlaced frame ? %d", !!(pic->flags & AV_FRAME_FLAG_INTERLACED)); + pic->flags |= AV_FRAME_FLAG_INTERLACED; low = s->plane[plane].subband[0]; high = s->plane[plane].subband[7]; output = s->plane[plane].l_h[6]; @@ -1284,7 +1284,7 @@ static int cfhd_decode(AVCodecContext *avctx, AVFrame *pic, dst += dst_linesize; } } else { - pic->interlaced_frame = 1; + pic->flags |= AV_FRAME_FLAG_INTERLACED; low = s->plane[plane].l_h[7]; high = s->plane[plane].subband[14]; output = s->plane[plane].l_h[6]; diff --git a/libavcodec/cfhddata.c b/libavcodec/cfhddata.c index fd5cc8174e2..a3948a14caf 100644 --- a/libavcodec/cfhddata.c +++ b/libavcodec/cfhddata.c @@ -150,7 +150,7 @@ static av_cold int cfhd_init_vlc(CFHD_RL_VLC_ELEM out[], unsigned out_size, } } - ret = ff_init_vlc_from_lengths(&vlc, VLC_BITS, j, + ret = ff_vlc_init_from_lengths(&vlc, VLC_BITS, j, &tmp[0].len, sizeof(tmp[0]), NULL, 0, 0, 0, 0, logctx); if (ret < 0) @@ -173,7 +173,7 @@ static av_cold int cfhd_init_vlc(CFHD_RL_VLC_ELEM out[], unsigned out_size, out[i].level = level; out[i].run = run; } - ff_free_vlc(&vlc); + ff_vlc_free(&vlc); return 0; } diff --git a/libavcodec/cfhdenc.c b/libavcodec/cfhdenc.c index f447438491d..1412d35d3f5 100644 --- a/libavcodec/cfhdenc.c +++ b/libavcodec/cfhdenc.c @@ -258,8 +258,8 @@ static av_cold int cfhd_encode_init(AVCodecContext *avctx) if (ret < 0) return ret; - if (avctx->height < 4) { - av_log(avctx, AV_LOG_ERROR, "Height must be >= 4.\n"); + if (avctx->height < 32) { + av_log(avctx, AV_LOG_ERROR, "Height must be >= 32.\n"); return AVERROR_INVALIDDATA; } @@ -553,7 +553,7 @@ static int cfhd_encode_frame(AVCodecContext *avctx, AVPacket *pkt, width, height * 2); } - ret = ff_alloc_packet(avctx, pkt, 256LL + s->planes * (2LL * avctx->width * (avctx->height + 15) + 2048LL)); + ret = ff_alloc_packet(avctx, pkt, 256LL + s->planes * (4LL * avctx->width * (avctx->height + 15) + 2048LL)); if (ret < 0) return ret; @@ -761,7 +761,6 @@ static int cfhd_encode_frame(AVCodecContext *avctx, AVPacket *pkt, } else if (count > 0) { count = put_runcode(pb, count, rb); } - put_bits(pb, cb[index].size, cb[index].bits); } @@ -830,20 +829,20 @@ static av_cold int cfhd_encode_close(AVCodecContext *avctx) #define OFFSET(x) offsetof(CFHDEncContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "quality", "set quality", OFFSET(quality), AV_OPT_TYPE_INT, {.i64= 0}, 0, 12, VE, "q" }, - { "film3+", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 0}, 0, 0, VE, "q" }, - { "film3", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 1}, 0, 0, VE, "q" }, - { "film2+", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 2}, 0, 0, VE, "q" }, - { "film2", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 3}, 0, 0, VE, "q" }, - { "film1.5", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 4}, 0, 0, VE, "q" }, - { "film1+", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 5}, 0, 0, VE, "q" }, - { "film1", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 6}, 0, 0, VE, "q" }, - { "high+", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 7}, 0, 0, VE, "q" }, - { "high", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 8}, 0, 0, VE, "q" }, - { "medium+", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 9}, 0, 0, VE, "q" }, - { "medium", NULL, 0, AV_OPT_TYPE_CONST, {.i64=10}, 0, 0, VE, "q" }, - { "low+", NULL, 0, AV_OPT_TYPE_CONST, {.i64=11}, 0, 0, VE, "q" }, - { "low", NULL, 0, AV_OPT_TYPE_CONST, {.i64=12}, 0, 0, VE, "q" }, + { "quality", "set quality", OFFSET(quality), AV_OPT_TYPE_INT, {.i64= 0}, 0, 12, VE, .unit = "q" }, + { "film3+", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 0}, 0, 0, VE, .unit = "q" }, + { "film3", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 1}, 0, 0, VE, .unit = "q" }, + { "film2+", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 2}, 0, 0, VE, .unit = "q" }, + { "film2", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 3}, 0, 0, VE, .unit = "q" }, + { "film1.5", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 4}, 0, 0, VE, .unit = "q" }, + { "film1+", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 5}, 0, 0, VE, .unit = "q" }, + { "film1", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 6}, 0, 0, VE, .unit = "q" }, + { "high+", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 7}, 0, 0, VE, .unit = "q" }, + { "high", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 8}, 0, 0, VE, .unit = "q" }, + { "medium+", NULL, 0, AV_OPT_TYPE_CONST, {.i64= 9}, 0, 0, VE, .unit = "q" }, + { "medium", NULL, 0, AV_OPT_TYPE_CONST, {.i64=10}, 0, 0, VE, .unit = "q" }, + { "low+", NULL, 0, AV_OPT_TYPE_CONST, {.i64=11}, 0, 0, VE, .unit = "q" }, + { "low", NULL, 0, AV_OPT_TYPE_CONST, {.i64=12}, 0, 0, VE, .unit = "q" }, { NULL}, }; diff --git a/libavcodec/cinepak.c b/libavcodec/cinepak.c index 282614fd1db..2ec0ce88829 100644 --- a/libavcodec/cinepak.c +++ b/libavcodec/cinepak.c @@ -379,7 +379,7 @@ static int cinepak_decode (CinepakContext *s) num_strips = FFMIN(num_strips, MAX_STRIPS); - s->frame->key_frame = 0; + s->frame->flags &= ~AV_FRAME_FLAG_KEY; for (i=0; i < num_strips; i++) { if ((s->data + 12) > eod) @@ -395,7 +395,7 @@ static int cinepak_decode (CinepakContext *s) s->strips[i].x2 = AV_RB16 (&s->data[10]); if (s->strips[i].id == 0x10) - s->frame->key_frame = 1; + s->frame->flags |= AV_FRAME_FLAG_KEY; strip_size = AV_RB24 (&s->data[1]) - 12; if (strip_size < 0) @@ -476,7 +476,14 @@ static int cinepak_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; if (s->palette_video) { - s->frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + s->frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif } if ((ret = cinepak_decode(s)) < 0) { diff --git a/libavcodec/clearvideo.c b/libavcodec/clearvideo.c index e77661d187b..0b59d44cd61 100644 --- a/libavcodec/clearvideo.c +++ b/libavcodec/clearvideo.c @@ -39,9 +39,9 @@ #define CLV_VLC_BITS 9 typedef struct LevelCodes { - VLC flags_cb; - VLC mv_cb; - VLC bias_cb; + const VLCElem *flags_cb; + const VLCElem *mv_cb; + const VLCElem *bias_cb; } LevelCodes; typedef struct MV { @@ -75,9 +75,8 @@ typedef struct CLVContext { int top_dc[3], left_dc[4]; } CLVContext; -static VLC dc_vlc, ac_vlc; +static VLCElem dc_vlc[1104], ac_vlc[554]; static LevelCodes lev[4 + 3 + 3]; // 0..3: Y, 4..6: U, 7..9: V -static VLCElem vlc_buf[16716]; static inline int decode_block(CLVContext *ctx, int16_t *blk, int has_ac, int ac_quant) @@ -86,13 +85,13 @@ static inline int decode_block(CLVContext *ctx, int16_t *blk, int has_ac, int idx = 1, last = 0, val, skip; memset(blk, 0, sizeof(*blk) * 64); - blk[0] = get_vlc2(gb, dc_vlc.table, CLV_VLC_BITS, 3); + blk[0] = get_vlc2(gb, dc_vlc, CLV_VLC_BITS, 3); if (!has_ac) return 0; while (idx < 64 && !last) { - val = get_vlc2(gb, ac_vlc.table, CLV_VLC_BITS, 2); + val = get_vlc2(gb, ac_vlc, CLV_VLC_BITS, 2); if (val < 0) return AVERROR_INVALIDDATA; if (val != 0x1BFF) { @@ -387,11 +386,11 @@ static int decode_tile(AVCodecContext *avctx, GetBitContext *gb, MV mv = { 0 }; int err; - if (lc->flags_cb.table) - flags = get_vlc2(gb, lc->flags_cb.table, CLV_VLC_BITS, 2); + if (lc->flags_cb) + flags = get_vlc2(gb, lc->flags_cb, CLV_VLC_BITS, 2); - if (lc->mv_cb.table) { - uint16_t mv_code = get_vlc2(gb, lc->mv_cb.table, CLV_VLC_BITS, 2); + if (lc->mv_cb) { + uint16_t mv_code = get_vlc2(gb, lc->mv_cb, CLV_VLC_BITS, 2); if (mv_code != MV_ESC) { mv.x = (int8_t)(mv_code & 0xff); @@ -406,8 +405,8 @@ static int decode_tile(AVCodecContext *avctx, GetBitContext *gb, mv.x += root_mv.x; mv.y += root_mv.y; - if (lc->bias_cb.table) { - uint16_t bias_val = get_vlc2(gb, lc->bias_cb.table, CLV_VLC_BITS, 2); + if (lc->bias_cb) { + uint16_t bias_val = get_vlc2(gb, lc->bias_cb, CLV_VLC_BITS, 2); if (bias_val != BIAS_ESC) { bias = (int16_t)(bias_val); @@ -511,7 +510,7 @@ static int clv_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; - c->pic->key_frame = 1; + c->pic->flags |= AV_FRAME_FLAG_KEY; c->pic->pict_type = AV_PICTURE_TYPE_I; bytestream2_get_be32(&gb); // frame size; @@ -605,7 +604,7 @@ static int clv_decode_frame(AVCodecContext *avctx, AVFrame *rframe, } extend_edges(c->pic, c->tile_size); - c->pic->key_frame = 0; + c->pic->flags &= ~AV_FRAME_FLAG_KEY; c->pic->pict_type = AV_PICTURE_TYPE_P; } @@ -622,10 +621,12 @@ static int clv_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return mb_ret < 0 ? mb_ret : buf_size; } -static av_cold void build_vlc(VLC *vlc, const uint8_t counts[16], - const uint16_t **syms, unsigned *offset) +static av_cold const VLCElem *build_vlc(VLCInitState *state, + const uint8_t counts[16], + const uint16_t **syms) { uint8_t lens[MAX_VLC_ENTRIES]; + const uint16_t *symbols = *syms; unsigned num = 0; for (int i = 0; i < 16; i++) { @@ -635,42 +636,39 @@ static av_cold void build_vlc(VLC *vlc, const uint8_t counts[16], for (count += num; num < count; num++) lens[num] = i + 1; } - vlc->table = &vlc_buf[*offset]; - vlc->table_allocated = FF_ARRAY_ELEMS(vlc_buf) - *offset; - ff_init_vlc_from_lengths(vlc, CLV_VLC_BITS, num, lens, 1, - *syms, 2, 2, 0, INIT_VLC_STATIC_OVERLONG, NULL); *syms += num; - *offset += vlc->table_size; + return ff_vlc_init_tables_from_lengths(state, CLV_VLC_BITS, num, lens, 1, + symbols, 2, 2, 0, 0); } static av_cold void clv_init_static(void) { + static VLCElem vlc_buf[16716]; + VLCInitState state = VLC_INIT_STATE(vlc_buf); const uint16_t *mv_syms = clv_mv_syms, *bias_syms = clv_bias_syms; - INIT_VLC_STATIC_FROM_LENGTHS(&dc_vlc, CLV_VLC_BITS, NUM_DC_CODES, - clv_dc_lens, 1, - clv_dc_syms, 1, 1, -63, 0, 1104); - INIT_VLC_STATIC_FROM_LENGTHS(&ac_vlc, CLV_VLC_BITS, NUM_AC_CODES, - clv_ac_bits, 1, - clv_ac_syms, 2, 2, 0, 0, 554); - for (unsigned i = 0, j = 0, k = 0, offset = 0;; i++) { + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(dc_vlc, CLV_VLC_BITS, NUM_DC_CODES, + clv_dc_lens, 1, + clv_dc_syms, 1, 1, -63, 0); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(ac_vlc, CLV_VLC_BITS, NUM_AC_CODES, + clv_ac_bits, 1, + clv_ac_syms, 2, 2, 0, 0); + for (unsigned i = 0, j = 0, k = 0;; i++) { if (0x36F & (1 << i)) { - build_vlc(&lev[i].mv_cb, clv_mv_len_counts[k], &mv_syms, &offset); + lev[i].mv_cb = build_vlc(&state, clv_mv_len_counts[k], &mv_syms); k++; } if (i == FF_ARRAY_ELEMS(lev) - 1) break; if (0x1B7 & (1 << i)) { - lev[i].flags_cb.table = &vlc_buf[offset]; - lev[i].flags_cb.table_allocated = FF_ARRAY_ELEMS(vlc_buf) - offset; - ff_init_vlc_from_lengths(&lev[i].flags_cb, CLV_VLC_BITS, 16, - clv_flags_bits[j], 1, - clv_flags_syms[j], 1, 1, - 0, INIT_VLC_STATIC_OVERLONG, NULL); - offset += lev[i].flags_cb.table_size; - - build_vlc(&lev[i + 1].bias_cb, clv_bias_len_counts[j], - &bias_syms, &offset); + lev[i].flags_cb = + ff_vlc_init_tables_from_lengths(&state, CLV_VLC_BITS, 16, + clv_flags_bits[j], 1, + clv_flags_syms[j], 1, 1, + 0, 0); + + lev[i + 1].bias_cb = build_vlc(&state, clv_bias_len_counts[j], + &bias_syms); j++; } } diff --git a/libavcodec/cljrdec.c b/libavcodec/cljrdec.c index 914f853c8fd..a4baa015f68 100644 --- a/libavcodec/cljrdec.c +++ b/libavcodec/cljrdec.c @@ -51,7 +51,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; init_get_bits(&gb, buf, buf_size * 8); diff --git a/libavcodec/cllc.c b/libavcodec/cllc.c index 911717b68db..0c6ae13d081 100644 --- a/libavcodec/cllc.c +++ b/libavcodec/cllc.c @@ -77,7 +77,7 @@ static int read_code_table(CLLCContext *ctx, GetBitContext *gb, VLC *vlc) } } - return ff_init_vlc_from_lengths(vlc, VLC_BITS, count, bits, 1, + return ff_vlc_init_from_lengths(vlc, VLC_BITS, count, bits, 1, symbols, 1, 1, 0, 0, ctx->avctx); } @@ -235,7 +235,7 @@ static int decode_argb_frame(CLLCContext *ctx, GetBitContext *gb, AVFrame *pic) ret = read_code_table(ctx, gb, &vlc[i]); if (ret < 0) { for (j = 0; j < i; j++) - ff_free_vlc(&vlc[j]); + ff_vlc_free(&vlc[j]); av_log(ctx->avctx, AV_LOG_ERROR, "Could not read code table %d.\n", i); @@ -251,7 +251,7 @@ static int decode_argb_frame(CLLCContext *ctx, GetBitContext *gb, AVFrame *pic) } for (i = 0; i < 4; i++) - ff_free_vlc(&vlc[i]); + ff_vlc_free(&vlc[i]); return 0; } @@ -278,7 +278,7 @@ static int decode_rgb24_frame(CLLCContext *ctx, GetBitContext *gb, AVFrame *pic) ret = read_code_table(ctx, gb, &vlc[i]); if (ret < 0) { for (j = 0; j < i; j++) - ff_free_vlc(&vlc[j]); + ff_vlc_free(&vlc[j]); av_log(ctx->avctx, AV_LOG_ERROR, "Could not read code table %d.\n", i); @@ -295,7 +295,7 @@ static int decode_rgb24_frame(CLLCContext *ctx, GetBitContext *gb, AVFrame *pic) } for (i = 0; i < 3; i++) - ff_free_vlc(&vlc[i]); + ff_vlc_free(&vlc[i]); return 0; } @@ -331,7 +331,7 @@ static int decode_yuv_frame(CLLCContext *ctx, GetBitContext *gb, AVFrame *pic) ret = read_code_table(ctx, gb, &vlc[i]); if (ret < 0) { for (j = 0; j < i; j++) - ff_free_vlc(&vlc[j]); + ff_vlc_free(&vlc[j]); av_log(ctx->avctx, AV_LOG_ERROR, "Could not read code table %d.\n", i); @@ -350,7 +350,7 @@ static int decode_yuv_frame(CLLCContext *ctx, GetBitContext *gb, AVFrame *pic) } for (i = 0; i < 2; i++) - ff_free_vlc(&vlc[i]); + ff_vlc_free(&vlc[i]); return 0; } @@ -460,7 +460,7 @@ static int cllc_decode_frame(AVCodecContext *avctx, AVFrame *pic, return AVERROR_INVALIDDATA; } - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; *got_picture_ptr = 1; diff --git a/libavcodec/cngenc.c b/libavcodec/cngenc.c index 596d6f8c2ae..15a410ea506 100644 --- a/libavcodec/cngenc.c +++ b/libavcodec/cngenc.c @@ -21,6 +21,7 @@ #include +#include "libavutil/avassert.h" #include "libavutil/common.h" #include "avcodec.h" #include "codec_internal.h" diff --git a/libavcodec/codec.h b/libavcodec/codec.h index 3b1995bcfef..6f9b42760d7 100644 --- a/libavcodec/codec.h +++ b/libavcodec/codec.h @@ -80,6 +80,7 @@ */ #define AV_CODEC_CAP_SMALL_LAST_FRAME (1 << 6) +#if FF_API_SUBFRAMES /** * Codec can output multiple frames per AVPacket * Normally demuxers return one frame at a time, demuxers which do not do @@ -92,6 +93,8 @@ * as a last resort. */ #define AV_CODEC_CAP_SUBFRAMES (1 << 8) +#endif + /** * Codec is experimental and is thus avoided in favor of non experimental * encoders @@ -206,15 +209,8 @@ typedef struct AVCodec { const enum AVPixelFormat *pix_fmts; ///< array of supported pixel formats, or NULL if unknown, array is terminated by -1 const int *supported_samplerates; ///< array of supported audio samplerates, or NULL if unknown, array is terminated by 0 const enum AVSampleFormat *sample_fmts; ///< array of supported sample formats, or NULL if unknown, array is terminated by -1 -#if FF_API_OLD_CHANNEL_LAYOUT - /** - * @deprecated use ch_layouts instead - */ - attribute_deprecated - const uint64_t *channel_layouts; ///< array of support channel layouts, or NULL if unknown. array is terminated by 0 -#endif const AVClass *priv_class; ///< AVClass for the private context - const AVProfile *profiles; ///< array of recognized profiles, or NULL if unknown, array is terminated by {FF_PROFILE_UNKNOWN} + const AVProfile *profiles; ///< array of recognized profiles, or NULL if unknown, array is terminated by {AV_PROFILE_UNKNOWN} /** * Group name of the codec implementation. diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 199f62df15b..3bab86db626 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -38,14 +38,20 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_VIDEO, .name = "mpeg1video", .long_name = NULL_IF_CONFIG_SMALL("MPEG-1 video"), - .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER, + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER | + // FIXME this is strigly speaking not true, as MPEG-1 does + // not allow field coding, but our mpeg12 code (decoder and + // parser) can sometimes change codec id at runtime, so + // this is safer + AV_CODEC_PROP_FIELDS, }, { .id = AV_CODEC_ID_MPEG2VIDEO, .type = AVMEDIA_TYPE_VIDEO, .name = "mpeg2video", .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 video"), - .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER, + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER | + AV_CODEC_PROP_FIELDS, .profiles = NULL_IF_CONFIG_SMALL(ff_mpeg2_video_profiles), }, { @@ -225,7 +231,8 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_VIDEO, .name = "h264", .long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"), - .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS | AV_CODEC_PROP_REORDER, + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS | + AV_CODEC_PROP_REORDER | AV_CODEC_PROP_FIELDS, .profiles = NULL_IF_CONFIG_SMALL(ff_h264_profiles), }, { @@ -529,7 +536,8 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_VIDEO, .name = "vc1", .long_name = NULL_IF_CONFIG_SMALL("SMPTE VC-1"), - .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER, + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER | + AV_CODEC_PROP_FIELDS, .profiles = NULL_IF_CONFIG_SMALL(ff_vc1_profiles), }, { @@ -1462,15 +1470,6 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("Avid Meridien Uncompressed"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, }, -#if FF_API_AYUV_CODECID - { - .id = AV_CODEC_ID_AYUV, - .type = AVMEDIA_TYPE_VIDEO, - .name = "ayuv", - .long_name = NULL_IF_CONFIG_SMALL("Uncompressed packed MS 4:4:4:4"), - .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, - }, -#endif { .id = AV_CODEC_ID_TARGA_Y216, .type = AVMEDIA_TYPE_VIDEO, @@ -1923,6 +1922,42 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("ViewQuest VQC"), .props = AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_PDV, + .type = AVMEDIA_TYPE_VIDEO, + .name = "pdv", + .long_name = NULL_IF_CONFIG_SMALL("PDV (PlayDate Video)"), + .props = AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_EVC, + .type = AVMEDIA_TYPE_VIDEO, + .name = "evc", + .long_name = NULL_IF_CONFIG_SMALL("MPEG-5 EVC (Essential Video Coding)"), + .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_REORDER, + .profiles = NULL_IF_CONFIG_SMALL(ff_evc_profiles), + }, + { + .id = AV_CODEC_ID_RTV1, + .type = AVMEDIA_TYPE_VIDEO, + .name = "rtv1", + .long_name = NULL_IF_CONFIG_SMALL("RTV1 (RivaTuner Video)"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_VMIX, + .type = AVMEDIA_TYPE_VIDEO, + .name = "vmix", + .long_name = NULL_IF_CONFIG_SMALL("vMix Video"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_LEAD, + .type = AVMEDIA_TYPE_VIDEO, + .name = "lead", + .long_name = NULL_IF_CONFIG_SMALL("LEAD MCMP"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, /* various PCM "codecs" */ { @@ -2931,6 +2966,7 @@ static const AVCodecDescriptor codec_descriptors[] = { .name = "eac3", .long_name = NULL_IF_CONFIG_SMALL("ATSC A/52B (AC-3, E-AC-3)"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + .profiles = NULL_IF_CONFIG_SMALL(ff_eac3_profiles), }, { .id = AV_CODEC_ID_SIPR, @@ -2959,6 +2995,7 @@ static const AVCodecDescriptor codec_descriptors[] = { .name = "truehd", .long_name = NULL_IF_CONFIG_SMALL("TrueHD"), .props = AV_CODEC_PROP_LOSSLESS, + .profiles = NULL_IF_CONFIG_SMALL(ff_truehd_profiles), }, { .id = AV_CODEC_ID_MP4ALS, @@ -3367,6 +3404,27 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("RKA (RK Audio)"), .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_LOSSLESS, }, + { + .id = AV_CODEC_ID_AC4, + .type = AVMEDIA_TYPE_AUDIO, + .name = "ac4", + .long_name = NULL_IF_CONFIG_SMALL("AC-4"), + .props = AV_CODEC_PROP_LOSSY, + }, + { + .id = AV_CODEC_ID_OSQ, + .type = AVMEDIA_TYPE_AUDIO, + .name = "osq", + .long_name = NULL_IF_CONFIG_SMALL("OSQ (Original Sound Quality)"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, + }, + { + .id = AV_CODEC_ID_QOA, + .type = AVMEDIA_TYPE_AUDIO, + .name = "qoa", + .long_name = NULL_IF_CONFIG_SMALL("QOA (Quite OK Audio)"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, /* subtitle codecs */ { @@ -3548,7 +3606,6 @@ static const AVCodecDescriptor codec_descriptors[] = { .type = AVMEDIA_TYPE_SUBTITLE, .name = "arib_caption", .long_name = NULL_IF_CONFIG_SMALL("ARIB STD-B24 caption"), - .props = AV_CODEC_PROP_TEXT_SUB, .profiles = NULL_IF_CONFIG_SMALL(ff_arib_caption_profiles), }, @@ -3625,6 +3682,12 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("binary data"), .mime_types= MT("application/octet-stream"), }, + { + .id = AV_CODEC_ID_SMPTE_2038, + .type = AVMEDIA_TYPE_DATA, + .name = "smpte_2038", + .long_name = NULL_IF_CONFIG_SMALL("SMPTE ST 2038 VANC in MPEG-2 TS"), + }, { .id = AV_CODEC_ID_MPEG2TS, .type = AVMEDIA_TYPE_DATA, diff --git a/libavcodec/codec_desc.h b/libavcodec/codec_desc.h index 126b52df476..96afd20208b 100644 --- a/libavcodec/codec_desc.h +++ b/libavcodec/codec_desc.h @@ -60,7 +60,7 @@ typedef struct AVCodecDescriptor { const char *const *mime_types; /** * If non-NULL, an array of profiles recognized for this codec. - * Terminated with FF_PROFILE_UNKNOWN. + * Terminated with AV_PROFILE_UNKNOWN. */ const struct AVProfile *profiles; } AVCodecDescriptor; @@ -90,6 +90,12 @@ typedef struct AVCodecDescriptor { * equal. */ #define AV_CODEC_PROP_REORDER (1 << 3) + +/** + * Video codec supports separate coding of fields in interlaced frames. + */ +#define AV_CODEC_PROP_FIELDS (1 << 4) + /** * Subtitle codec is bitmap based * Decoded AVSubtitle data can be read from the AVSubtitleRect->pict field. diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index 89a4a0cb893..c8dc21da740 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -253,9 +253,6 @@ enum AVCodecID { AV_CODEC_ID_AVRP, AV_CODEC_ID_012V, AV_CODEC_ID_AVUI, -#if FF_API_AYUV_CODECID - AV_CODEC_ID_AYUV, -#endif AV_CODEC_ID_TARGA_Y216, AV_CODEC_ID_V308, AV_CODEC_ID_V408, @@ -320,6 +317,11 @@ enum AVCodecID { AV_CODEC_ID_WBMP, AV_CODEC_ID_MEDIA100, AV_CODEC_ID_VQC, + AV_CODEC_ID_PDV, + AV_CODEC_ID_EVC, + AV_CODEC_ID_RTV1, + AV_CODEC_ID_VMIX, + AV_CODEC_ID_LEAD, /* various PCM "codecs" */ AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs @@ -538,6 +540,9 @@ enum AVCodecID { AV_CODEC_ID_FTR, AV_CODEC_ID_WAVARC, AV_CODEC_ID_RKA, + AV_CODEC_ID_AC4, + AV_CODEC_ID_OSQ, + AV_CODEC_ID_QOA, /* subtitle codecs */ AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs. @@ -582,6 +587,7 @@ enum AVCodecID { AV_CODEC_ID_DVD_NAV, AV_CODEC_ID_TIMED_ID3, AV_CODEC_ID_BIN_DATA, + AV_CODEC_ID_SMPTE_2038, AV_CODEC_ID_PROBE = 0x19000, ///< codec_id is not known (like AV_CODEC_ID_NONE) but lavf should attempt to identify it diff --git a/libavcodec/codec_internal.h b/libavcodec/codec_internal.h index 130a7dc3cd7..d6757e2deff 100644 --- a/libavcodec/codec_internal.h +++ b/libavcodec/codec_internal.h @@ -284,25 +284,6 @@ typedef struct FFCodec { .update_thread_context_for_user = NULL #endif -#if FF_API_OLD_CHANNEL_LAYOUT -#define CODEC_OLD_CHANNEL_LAYOUTS(...) CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(((const uint64_t[]) { __VA_ARGS__, 0 })) -#if defined(__clang__) -#define CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(array) \ - FF_DISABLE_DEPRECATION_WARNINGS \ - .p.channel_layouts = (array), \ - FF_ENABLE_DEPRECATION_WARNINGS -#else -#define CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(array) .p.channel_layouts = (array), -#endif -#else -/* This is only provided to allow to test disabling FF_API_OLD_CHANNEL_LAYOUT - * without removing all the FF_API_OLD_CHANNEL_LAYOUT codeblocks. - * It is of course still expected to be removed when FF_API_OLD_CHANNEL_LAYOUT - * will be finally removed (along with all usages of these macros). */ -#define CODEC_OLD_CHANNEL_LAYOUTS(...) -#define CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(array) -#endif - #define FF_CODEC_DECODE_CB(func) \ .cb_type = FF_CODEC_CB_TYPE_DECODE, \ .cb.decode = (func) diff --git a/libavcodec/codec_par.c b/libavcodec/codec_par.c index abda649aa88..790ea01d10c 100644 --- a/libavcodec/codec_par.c +++ b/libavcodec/codec_par.c @@ -27,11 +27,13 @@ #include "libavutil/mem.h" #include "avcodec.h" #include "codec_par.h" +#include "packet.h" static void codec_parameters_reset(AVCodecParameters *par) { av_freep(&par->extradata); av_channel_layout_uninit(&par->ch_layout); + av_packet_side_data_free(&par->coded_side_data, &par->nb_coded_side_data); memset(par, 0, sizeof(*par)); @@ -46,8 +48,9 @@ static void codec_parameters_reset(AVCodecParameters *par) par->color_space = AVCOL_SPC_UNSPECIFIED; par->chroma_location = AVCHROMA_LOC_UNSPECIFIED; par->sample_aspect_ratio = (AVRational){ 0, 1 }; - par->profile = FF_PROFILE_UNKNOWN; - par->level = FF_LEVEL_UNKNOWN; + par->framerate = (AVRational){ 0, 1 }; + par->profile = AV_PROFILE_UNKNOWN; + par->level = AV_LEVEL_UNKNOWN; } AVCodecParameters *avcodec_parameters_alloc(void) @@ -71,6 +74,35 @@ void avcodec_parameters_free(AVCodecParameters **ppar) av_freep(ppar); } +static int codec_parameters_copy_side_data(AVPacketSideData **pdst, int *pnb_dst, + const AVPacketSideData *src, int nb_src) +{ + AVPacketSideData *dst; + int nb_dst = *pnb_dst; + + if (!src) + return 0; + + *pdst = dst = av_calloc(nb_src, sizeof(*dst)); + if (!dst) + return AVERROR(ENOMEM); + + for (int i = 0; i < nb_src; i++) { + const AVPacketSideData *src_sd = &src[i]; + AVPacketSideData *dst_sd = &dst[i]; + + dst_sd->data = av_memdup(src_sd->data, src_sd->size); + if (!dst_sd->data) + return AVERROR(ENOMEM); + + dst_sd->type = src_sd->type; + dst_sd->size = src_sd->size; + *pnb_dst = ++nb_dst; + } + + return 0; +} + int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src) { int ret; @@ -81,6 +113,8 @@ int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src dst->ch_layout = (AVChannelLayout){0}; dst->extradata = NULL; dst->extradata_size = 0; + dst->coded_side_data = NULL; + dst->nb_coded_side_data = 0; if (src->extradata) { dst->extradata = av_mallocz(src->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!dst->extradata) @@ -88,6 +122,10 @@ int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src memcpy(dst->extradata, src->extradata, src->extradata_size); dst->extradata_size = src->extradata_size; } + ret = codec_parameters_copy_side_data(&dst->coded_side_data, &dst->nb_coded_side_data, + src->coded_side_data, src->nb_coded_side_data); + if (ret < 0) + return ret; ret = av_channel_layout_copy(&dst->ch_layout, &src->ch_layout); if (ret < 0) @@ -126,35 +164,13 @@ int avcodec_parameters_from_context(AVCodecParameters *par, par->chroma_location = codec->chroma_sample_location; par->sample_aspect_ratio = codec->sample_aspect_ratio; par->video_delay = codec->has_b_frames; + par->framerate = codec->framerate; break; case AVMEDIA_TYPE_AUDIO: par->format = codec->sample_fmt; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - // if the old/new fields are set inconsistently, prefer the old ones - if ((codec->channels && codec->channels != codec->ch_layout.nb_channels) || - (codec->channel_layout && (codec->ch_layout.order != AV_CHANNEL_ORDER_NATIVE || - codec->ch_layout.u.mask != codec->channel_layout))) { - if (codec->channel_layout) - av_channel_layout_from_mask(&par->ch_layout, codec->channel_layout); - else { - par->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; - par->ch_layout.nb_channels = codec->channels; - } -FF_ENABLE_DEPRECATION_WARNINGS - } else { -#endif ret = av_channel_layout_copy(&par->ch_layout, &codec->ch_layout); if (ret < 0) return ret; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - } - par->channel_layout = par->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ? - par->ch_layout.u.mask : 0; - par->channels = par->ch_layout.nb_channels; -FF_ENABLE_DEPRECATION_WARNINGS -#endif par->sample_rate = codec->sample_rate; par->block_align = codec->block_align; par->frame_size = codec->frame_size; @@ -176,6 +192,11 @@ FF_ENABLE_DEPRECATION_WARNINGS par->extradata_size = codec->extradata_size; } + ret = codec_parameters_copy_side_data(&par->coded_side_data, &par->nb_coded_side_data, + codec->coded_side_data, codec->nb_coded_side_data); + if (ret < 0) + return ret; + return 0; } @@ -207,35 +228,13 @@ int avcodec_parameters_to_context(AVCodecContext *codec, codec->chroma_sample_location = par->chroma_location; codec->sample_aspect_ratio = par->sample_aspect_ratio; codec->has_b_frames = par->video_delay; + codec->framerate = par->framerate; break; case AVMEDIA_TYPE_AUDIO: codec->sample_fmt = par->format; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - // if the old/new fields are set inconsistently, prefer the old ones - if ((par->channels && par->channels != par->ch_layout.nb_channels) || - (par->channel_layout && (par->ch_layout.order != AV_CHANNEL_ORDER_NATIVE || - par->ch_layout.u.mask != par->channel_layout))) { - if (par->channel_layout) - av_channel_layout_from_mask(&codec->ch_layout, par->channel_layout); - else { - codec->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; - codec->ch_layout.nb_channels = par->channels; - } -FF_ENABLE_DEPRECATION_WARNINGS - } else { -#endif ret = av_channel_layout_copy(&codec->ch_layout, &par->ch_layout); if (ret < 0) return ret; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - } - codec->channel_layout = codec->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ? - codec->ch_layout.u.mask : 0; - codec->channels = codec->ch_layout.nb_channels; -FF_ENABLE_DEPRECATION_WARNINGS -#endif codec->sample_rate = par->sample_rate; codec->block_align = par->block_align; codec->frame_size = par->frame_size; @@ -250,8 +249,9 @@ FF_ENABLE_DEPRECATION_WARNINGS break; } + av_freep(&codec->extradata); + codec->extradata_size = 0; if (par->extradata) { - av_freep(&codec->extradata); codec->extradata = av_mallocz(par->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!codec->extradata) return AVERROR(ENOMEM); @@ -259,5 +259,11 @@ FF_ENABLE_DEPRECATION_WARNINGS codec->extradata_size = par->extradata_size; } + av_packet_side_data_free(&codec->coded_side_data, &codec->nb_coded_side_data); + ret = codec_parameters_copy_side_data(&codec->coded_side_data, &codec->nb_coded_side_data, + par->coded_side_data, par->nb_coded_side_data); + if (ret < 0) + return ret; + return 0; } diff --git a/libavcodec/codec_par.h b/libavcodec/codec_par.h index f51d27c5908..f4b9bb5c06d 100644 --- a/libavcodec/codec_par.h +++ b/libavcodec/codec_par.h @@ -29,21 +29,14 @@ #include "libavutil/pixfmt.h" #include "codec_id.h" +#include "defs.h" +#include "packet.h" /** * @addtogroup lavc_core * @{ */ -enum AVFieldOrder { - AV_FIELD_UNKNOWN, - AV_FIELD_PROGRESSIVE, - AV_FIELD_TT, ///< Top coded_first, top displayed first - AV_FIELD_BB, ///< Bottom coded first, bottom displayed first - AV_FIELD_TB, ///< Top coded first, bottom displayed first - AV_FIELD_BT, ///< Bottom coded first, top displayed first -}; - /** * This struct describes the properties of an encoded stream. * @@ -79,6 +72,19 @@ typedef struct AVCodecParameters { */ int extradata_size; + /** + * Additional data associated with the entire stream. + * + * Should be allocated with av_packet_side_data_new() or + * av_packet_side_data_add(), and will be freed by avcodec_parameters_free(). + */ + AVPacketSideData *coded_side_data; + + /** + * Amount of entries in @ref coded_side_data. + */ + int nb_coded_side_data; + /** * - video: the pixel format, the value corresponds to enum AVPixelFormat. * - audio: the sample format, the value corresponds to enum AVSampleFormat. @@ -137,6 +143,18 @@ typedef struct AVCodecParameters { */ AVRational sample_aspect_ratio; + /** + * Video only. Number of frames per second, for streams with constant frame + * durations. Should be set to { 0, 1 } when some frames have differing + * durations or if the value is not known. + * + * @note This field correponds to values that are stored in codec-level + * headers and is typically overridden by container/transport-layer + * timestamps, when available. It should thus be used only as a last resort, + * when no higher-level timing information is available. + */ + AVRational framerate; + /** * Video only. The order of the fields in interlaced video. */ @@ -156,22 +174,10 @@ typedef struct AVCodecParameters { */ int video_delay; -#if FF_API_OLD_CHANNEL_LAYOUT - /** - * Audio only. The channel layout bitmask. May be 0 if the channel layout is - * unknown or unspecified, otherwise the number of bits set must be equal to - * the channels field. - * @deprecated use ch_layout - */ - attribute_deprecated - uint64_t channel_layout; /** - * Audio only. The number of audio channels. - * @deprecated use ch_layout.nb_channels + * Audio only. The channel layout and number of channels. */ - attribute_deprecated - int channels; -#endif + AVChannelLayout ch_layout; /** * Audio only. The number of audio samples per second. */ @@ -206,11 +212,6 @@ typedef struct AVCodecParameters { * Audio only. Number of samples to skip after a discontinuity. */ int seek_preroll; - - /** - * Audio only. The channel layout and number of channels. - */ - AVChannelLayout ch_layout; } AVCodecParameters; /** diff --git a/libavcodec/cook.c b/libavcodec/cook.c index 7d05afcca80..705213de499 100644 --- a/libavcodec/cook.c +++ b/libavcodec/cook.c @@ -208,7 +208,7 @@ static av_cold int build_vlc(VLC *vlc, int nb_bits, const uint8_t counts[16], for (unsigned count = num + counts[i]; num < count; num++) lens[num] = i + 1; - return ff_init_vlc_from_lengths(vlc, nb_bits, num, lens, 1, + return ff_vlc_init_from_lengths(vlc, nb_bits, num, lens, 1, syms, symbol_size, symbol_size, offset, 0, logctx); } @@ -341,11 +341,11 @@ static av_cold int cook_decode_close(AVCodecContext *avctx) /* Free the VLC tables. */ for (i = 0; i < 13; i++) - ff_free_vlc(&q->envelope_quant_index[i]); + ff_vlc_free(&q->envelope_quant_index[i]); for (i = 0; i < 7; i++) - ff_free_vlc(&q->sqvh[i]); + ff_vlc_free(&q->sqvh[i]); for (i = 0; i < q->num_subpackets; i++) - ff_free_vlc(&q->subpacket[i].channel_coupling); + ff_vlc_free(&q->subpacket[i].channel_coupling); av_log(avctx, AV_LOG_DEBUG, "Memory deallocated.\n"); diff --git a/libavcodec/cos_tablegen.c b/libavcodec/cos_tablegen.c deleted file mode 100644 index 7206aad5dd8..00000000000 --- a/libavcodec/cos_tablegen.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Generate a header file for hardcoded ff_cos_* tables - * - * Copyright (c) 2009 Reimar Döffinger - * - * 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 -#include -#include - -#include "libavutil/mathematics.h" - -#define BITS 17 -#define FLOATFMT "%.18e" -#define FIXEDFMT "%6d" - -static int clip_f15(int v) -{ - return v < -32767 ? -32767 : - v > 32767 ? 32767 : - v; -} - -static void printval(double val, int fixed) -{ - if (fixed) { - /* lrint() isn't always available, so round and cast manually. */ - double new_val = val * (double) (1 << 15); - - new_val = new_val >= 0 ? floor(new_val + 0.5) : ceil(new_val - 0.5); - - printf(" "FIXEDFMT",", clip_f15((long int) new_val)); - } else { - printf(" "FLOATFMT",", val); - } -} - -int main(int argc, char *argv[]) -{ - int i, j; - int do_sin = argc > 1 && !strcmp(argv[1], "sin"); - int fixed = argc > 1 && strstr(argv[1], "fixed"); - double (*func)(double) = do_sin ? sin : cos; - - printf("/* This file was automatically generated. */\n"); - printf("#define FFT_FLOAT %d\n", !fixed); - printf("#include \"libavcodec/%s\"\n", do_sin ? "rdft.h" : "fft.h"); - for (i = 4; i <= BITS; i++) { - int m = 1 << i; - double freq = 2*M_PI/m; - printf("%s(%i) = {\n ", do_sin ? "SINTABLE" : "COSTABLE", m); - for (j = 0; j < m/2 - 1; j++) { - int idx = j > m/4 ? m/2 - j : j; - if (do_sin && j >= m/4) - idx = m/4 - j; - printval(func(idx*freq), fixed); - if ((j & 3) == 3) - printf("\n "); - } - printval(func(do_sin ? -(m/4 - 1)*freq : freq), fixed); - printf("\n};\n"); - } - return 0; -} diff --git a/libavcodec/cpia.c b/libavcodec/cpia.c index bfd270dae2b..f62100c4193 100644 --- a/libavcodec/cpia.c +++ b/libavcodec/cpia.c @@ -94,10 +94,10 @@ static int cpia_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if (header[28] == NOT_COMPRESSED) { frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; } else { frame->pict_type = AV_PICTURE_TYPE_P; - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; } // Get buffer filled with previous frame diff --git a/libavcodec/cri.c b/libavcodec/cri.c index 5761152c2d5..7297c0350ff 100644 --- a/libavcodec/cri.c +++ b/libavcodec/cri.c @@ -70,7 +70,6 @@ static av_cold int cri_decode_init(AVCodecContext *avctx) return AVERROR(ENOMEM); s->jpeg_avctx->flags = avctx->flags; s->jpeg_avctx->flags2 = avctx->flags2; - s->jpeg_avctx->dct_algo = avctx->dct_algo; s->jpeg_avctx->idct_algo = avctx->idct_algo; ret = avcodec_open2(s->jpeg_avctx, codec, NULL); if (ret < 0) @@ -235,10 +234,14 @@ static int cri_decode_frame(AVCodecContext *avctx, AVFrame *p, s->data_size = length; goto skip; case 105: + if (length <= 0) + return AVERROR_INVALIDDATA; hflip = bytestream2_get_byte(gb) != 0; length--; goto skip; case 106: + if (length <= 0) + return AVERROR_INVALIDDATA; vflip = bytestream2_get_byte(gb) != 0; length--; goto skip; @@ -399,8 +402,8 @@ static int cri_decode_frame(AVCodecContext *avctx, AVFrame *p, } if (hflip || vflip) { - rotation = av_frame_new_side_data(p, AV_FRAME_DATA_DISPLAYMATRIX, - sizeof(int32_t) * 9); + ff_frame_new_side_data(avctx, p, AV_FRAME_DATA_DISPLAYMATRIX, + sizeof(int32_t) * 9, &rotation); if (rotation) { av_display_rotation_set((int32_t *)rotation->data, 0.f); av_display_matrix_flip((int32_t *)rotation->data, hflip, vflip); @@ -408,7 +411,7 @@ static int cri_decode_frame(AVCodecContext *avctx, AVFrame *p, } p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; diff --git a/libavcodec/crystalhd.c b/libavcodec/crystalhd.c deleted file mode 100644 index 688cdffbcb5..00000000000 --- a/libavcodec/crystalhd.c +++ /dev/null @@ -1,822 +0,0 @@ -/* - * - CrystalHD decoder module - - * - * Copyright(C) 2010,2011 Philip Langdale - * - * 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 - */ - -/* - * - Principles of Operation - - * - * The CrystalHD decoder operates at the bitstream level - which is an even - * higher level than the decoding hardware you typically see in modern GPUs. - * This means it has a very simple interface, in principle. You feed demuxed - * packets in one end and get decoded picture (fields/frames) out the other. - * - * Of course, nothing is ever that simple. Due, at the very least, to b-frame - * dependencies in the supported formats, the hardware has a delay between - * when a packet goes in, and when a picture comes out. Furthermore, this delay - * is not just a function of time, but also one of the dependency on additional - * frames being fed into the decoder to satisfy the b-frame dependencies. - * - * As such, the hardware can only be used effectively with a decode API that - * doesn't assume a 1:1 relationship between input packets and output frames. - * The new avcodec decode API is such an API (an m:n API) while the old one is - * 1:1. Consequently, we no longer support the old API, which allows us to avoid - * the vicious hacks that are required to approximate 1:1 operation. - */ - -/***************************************************************************** - * Includes - ****************************************************************************/ - -#include "config_components.h" - -#define _XOPEN_SOURCE 600 -#include -#include -#include - -#include -#include -#include - -#include "avcodec.h" -#include "codec_internal.h" -#include "decode.h" -#include "internal.h" -#include "libavutil/imgutils.h" -#include "libavutil/intreadwrite.h" -#include "libavutil/opt.h" - -#if HAVE_UNISTD_H -#include -#endif - -/** Timeout parameter passed to DtsProcOutput() in us */ -#define OUTPUT_PROC_TIMEOUT 50 -/** Step between fake timestamps passed to hardware in units of 100ns */ -#define TIMESTAMP_UNIT 100000 - - -/***************************************************************************** - * Module private data - ****************************************************************************/ - -typedef enum { - RET_ERROR = -1, - RET_OK = 0, - RET_COPY_AGAIN = 1, -} CopyRet; - -typedef struct OpaqueList { - struct OpaqueList *next; - uint64_t fake_timestamp; - uint64_t reordered_opaque; -} OpaqueList; - -typedef struct { - AVClass *av_class; - AVCodecContext *avctx; - /* This packet coincides with AVCodecInternal.in_pkt - * and is not owned by us. */ - AVPacket *pkt; - HANDLE dev; - - uint8_t is_70012; - uint8_t need_second_field; - uint8_t draining; - - OpaqueList *head; - OpaqueList *tail; - - /* Options */ - uint32_t sWidth; -} CHDContext; - -static const AVOption options[] = { - { "crystalhd_downscale_width", - "Turn on downscaling to the specified width", - offsetof(CHDContext, sWidth), - AV_OPT_TYPE_INT, {.i64 = 0}, 0, UINT32_MAX, - AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM, }, - { NULL, }, -}; - - -/***************************************************************************** - * Helper functions - ****************************************************************************/ - -static inline BC_MEDIA_SUBTYPE id2subtype(CHDContext *priv, enum AVCodecID id) -{ - switch (id) { - case AV_CODEC_ID_MPEG4: - return BC_MSUBTYPE_DIVX; - case AV_CODEC_ID_MSMPEG4V3: - return BC_MSUBTYPE_DIVX311; - case AV_CODEC_ID_MPEG2VIDEO: - return BC_MSUBTYPE_MPEG2VIDEO; - case AV_CODEC_ID_VC1: - return BC_MSUBTYPE_VC1; - case AV_CODEC_ID_WMV3: - return BC_MSUBTYPE_WMV3; - case AV_CODEC_ID_H264: - return BC_MSUBTYPE_H264; - default: - return BC_MSUBTYPE_INVALID; - } -} - -static inline void print_frame_info(CHDContext *priv, BC_DTS_PROC_OUT *output) -{ - av_log(priv->avctx, AV_LOG_TRACE, "\tYBuffSz: %u\n", output->YbuffSz); - av_log(priv->avctx, AV_LOG_TRACE, "\tYBuffDoneSz: %u\n", - output->YBuffDoneSz); - av_log(priv->avctx, AV_LOG_TRACE, "\tUVBuffDoneSz: %u\n", - output->UVBuffDoneSz); - av_log(priv->avctx, AV_LOG_TRACE, "\tTimestamp: %"PRIu64"\n", - output->PicInfo.timeStamp); - av_log(priv->avctx, AV_LOG_TRACE, "\tPicture Number: %u\n", - output->PicInfo.picture_number); - av_log(priv->avctx, AV_LOG_TRACE, "\tWidth: %u\n", - output->PicInfo.width); - av_log(priv->avctx, AV_LOG_TRACE, "\tHeight: %u\n", - output->PicInfo.height); - av_log(priv->avctx, AV_LOG_TRACE, "\tChroma: 0x%03x\n", - output->PicInfo.chroma_format); - av_log(priv->avctx, AV_LOG_TRACE, "\tPulldown: %u\n", - output->PicInfo.pulldown); - av_log(priv->avctx, AV_LOG_TRACE, "\tFlags: 0x%08x\n", - output->PicInfo.flags); - av_log(priv->avctx, AV_LOG_TRACE, "\tFrame Rate/Res: %u\n", - output->PicInfo.frame_rate); - av_log(priv->avctx, AV_LOG_TRACE, "\tAspect Ratio: %u\n", - output->PicInfo.aspect_ratio); - av_log(priv->avctx, AV_LOG_TRACE, "\tColor Primaries: %u\n", - output->PicInfo.colour_primaries); - av_log(priv->avctx, AV_LOG_TRACE, "\tMetaData: %u\n", - output->PicInfo.picture_meta_payload); - av_log(priv->avctx, AV_LOG_TRACE, "\tSession Number: %u\n", - output->PicInfo.sess_num); - av_log(priv->avctx, AV_LOG_TRACE, "\tycom: %u\n", - output->PicInfo.ycom); - av_log(priv->avctx, AV_LOG_TRACE, "\tCustom Aspect: %u\n", - output->PicInfo.custom_aspect_ratio_width_height); - av_log(priv->avctx, AV_LOG_TRACE, "\tFrames to Drop: %u\n", - output->PicInfo.n_drop); - av_log(priv->avctx, AV_LOG_TRACE, "\tH264 Valid Fields: 0x%08x\n", - output->PicInfo.other.h264.valid); -} - - -/***************************************************************************** - * OpaqueList functions - ****************************************************************************/ - -static uint64_t opaque_list_push(CHDContext *priv, uint64_t reordered_opaque) -{ - OpaqueList *newNode = av_mallocz(sizeof (OpaqueList)); - if (!newNode) { - av_log(priv->avctx, AV_LOG_ERROR, - "Unable to allocate new node in OpaqueList.\n"); - return 0; - } - if (!priv->head) { - newNode->fake_timestamp = TIMESTAMP_UNIT; - priv->head = newNode; - } else { - newNode->fake_timestamp = priv->tail->fake_timestamp + TIMESTAMP_UNIT; - priv->tail->next = newNode; - } - priv->tail = newNode; - newNode->reordered_opaque = reordered_opaque; - - return newNode->fake_timestamp; -} - -/* - * The OpaqueList is built in decode order, while elements will be removed - * in presentation order. If frames are reordered, this means we must be - * able to remove elements that are not the first element. - * - * Returned node must be freed by caller. - */ -static OpaqueList *opaque_list_pop(CHDContext *priv, uint64_t fake_timestamp) -{ - OpaqueList *node = priv->head; - - if (!priv->head) { - av_log(priv->avctx, AV_LOG_ERROR, - "CrystalHD: Attempted to query non-existent timestamps.\n"); - return NULL; - } - - /* - * The first element is special-cased because we have to manipulate - * the head pointer rather than the previous element in the list. - */ - if (priv->head->fake_timestamp == fake_timestamp) { - priv->head = node->next; - - if (!priv->head->next) - priv->tail = priv->head; - - node->next = NULL; - return node; - } - - /* - * The list is processed at arm's length so that we have the - * previous element available to rewrite its next pointer. - */ - while (node->next) { - OpaqueList *current = node->next; - if (current->fake_timestamp == fake_timestamp) { - node->next = current->next; - - if (!node->next) - priv->tail = node; - - current->next = NULL; - return current; - } else { - node = current; - } - } - - av_log(priv->avctx, AV_LOG_VERBOSE, - "CrystalHD: Couldn't match fake_timestamp.\n"); - return NULL; -} - - -/***************************************************************************** - * Video decoder API function definitions - ****************************************************************************/ - -static void flush(AVCodecContext *avctx) -{ - CHDContext *priv = avctx->priv_data; - - priv->need_second_field = 0; - priv->draining = 0; - - /* Flush mode 4 flushes all software and hardware buffers. */ - DtsFlushInput(priv->dev, 4); -} - - -static av_cold int uninit(AVCodecContext *avctx) -{ - CHDContext *priv = avctx->priv_data; - HANDLE device; - - device = priv->dev; - DtsStopDecoder(device); - DtsCloseDecoder(device); - DtsDeviceClose(device); - - if (priv->head) { - OpaqueList *node = priv->head; - while (node) { - OpaqueList *next = node->next; - av_free(node); - node = next; - } - } - - return 0; -} - -static av_cold int init(AVCodecContext *avctx) -{ - CHDContext* priv; - BC_STATUS ret; - BC_INFO_CRYSTAL version; - BC_INPUT_FORMAT format = { - .FGTEnable = FALSE, - .Progressive = TRUE, - .OptFlags = 0x80000000 | vdecFrameRate59_94 | 0x40, - .width = avctx->width, - .height = avctx->height, - }; - - BC_MEDIA_SUBTYPE subtype; - - uint32_t mode = DTS_PLAYBACK_MODE | - DTS_LOAD_FILE_PLAY_FW | - DTS_SKIP_TX_CHK_CPB | - DTS_PLAYBACK_DROP_RPT_MODE | - DTS_SINGLE_THREADED_MODE | - DTS_DFLT_RESOLUTION(vdecRESOLUTION_1080p23_976); - - av_log(avctx, AV_LOG_VERBOSE, "CrystalHD Init for %s\n", - avctx->codec->name); - - av_log(avctx, AV_LOG_WARNING, "CrystalHD support is deprecated and will " - "be removed. Please contact the developers if you are interested in " - "maintaining it.\n"); - - avctx->pix_fmt = AV_PIX_FMT_YUYV422; - - /* Initialize the library */ - priv = avctx->priv_data; - priv->avctx = avctx; - priv->pkt = avctx->internal->in_pkt; - priv->draining = 0; - - subtype = id2subtype(priv, avctx->codec->id); - switch (subtype) { - case BC_MSUBTYPE_H264: - format.startCodeSz = 4; - // Fall-through - case BC_MSUBTYPE_VC1: - case BC_MSUBTYPE_WVC1: - case BC_MSUBTYPE_WMV3: - case BC_MSUBTYPE_WMVA: - case BC_MSUBTYPE_MPEG2VIDEO: - case BC_MSUBTYPE_DIVX: - case BC_MSUBTYPE_DIVX311: - format.pMetaData = avctx->extradata; - format.metaDataSz = avctx->extradata_size; - break; - default: - av_log(avctx, AV_LOG_ERROR, "CrystalHD: Unknown codec name\n"); - return AVERROR(EINVAL); - } - format.mSubtype = subtype; - - if (priv->sWidth) { - format.bEnableScaling = 1; - format.ScalingParams.sWidth = priv->sWidth; - } - - /* Get a decoder instance */ - av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: starting up\n"); - // Initialize the Link and Decoder devices - ret = DtsDeviceOpen(&priv->dev, mode); - if (ret != BC_STS_SUCCESS) { - av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: DtsDeviceOpen failed\n"); - goto fail; - } - - ret = DtsCrystalHDVersion(priv->dev, &version); - if (ret != BC_STS_SUCCESS) { - av_log(avctx, AV_LOG_VERBOSE, - "CrystalHD: DtsCrystalHDVersion failed\n"); - goto fail; - } - priv->is_70012 = version.device == 0; - - if (priv->is_70012 && - (subtype == BC_MSUBTYPE_DIVX || subtype == BC_MSUBTYPE_DIVX311)) { - av_log(avctx, AV_LOG_VERBOSE, - "CrystalHD: BCM70012 doesn't support MPEG4-ASP/DivX/Xvid\n"); - goto fail; - } - - ret = DtsSetInputFormat(priv->dev, &format); - if (ret != BC_STS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "CrystalHD: SetInputFormat failed\n"); - goto fail; - } - - ret = DtsOpenDecoder(priv->dev, BC_STREAM_TYPE_ES); - if (ret != BC_STS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "CrystalHD: DtsOpenDecoder failed\n"); - goto fail; - } - - ret = DtsSetColorSpace(priv->dev, OUTPUT_MODE422_YUY2); - if (ret != BC_STS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "CrystalHD: DtsSetColorSpace failed\n"); - goto fail; - } - ret = DtsStartDecoder(priv->dev); - if (ret != BC_STS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "CrystalHD: DtsStartDecoder failed\n"); - goto fail; - } - ret = DtsStartCapture(priv->dev); - if (ret != BC_STS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "CrystalHD: DtsStartCapture failed\n"); - goto fail; - } - - av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Init complete.\n"); - - return 0; - - fail: - uninit(avctx); - return -1; -} - - -static inline CopyRet copy_frame(AVCodecContext *avctx, - BC_DTS_PROC_OUT *output, - AVFrame *frame, int *got_frame) -{ - BC_STATUS ret; - BC_DTS_STATUS decoder_status = { 0, }; - uint8_t interlaced; - - CHDContext *priv = avctx->priv_data; - int64_t pkt_pts = AV_NOPTS_VALUE; - - uint8_t bottom_field = (output->PicInfo.flags & VDEC_FLAG_BOTTOMFIELD) == - VDEC_FLAG_BOTTOMFIELD; - uint8_t bottom_first = !!(output->PicInfo.flags & VDEC_FLAG_BOTTOM_FIRST); - - int width = output->PicInfo.width; - int height = output->PicInfo.height; - int bwidth; - uint8_t *src = output->Ybuff; - int sStride; - uint8_t *dst; - int dStride; - - if (output->PicInfo.timeStamp != 0) { - OpaqueList *node = opaque_list_pop(priv, output->PicInfo.timeStamp); - if (node) { - pkt_pts = node->reordered_opaque; - av_free(node); - } else { - /* - * We will encounter a situation where a timestamp cannot be - * popped if a second field is being returned. In this case, - * each field has the same timestamp and the first one will - * cause it to be popped. We'll avoid overwriting the valid - * timestamp below. - */ - } - av_log(avctx, AV_LOG_VERBOSE, "output \"pts\": %"PRIu64"\n", - output->PicInfo.timeStamp); - } - - ret = DtsGetDriverStatus(priv->dev, &decoder_status); - if (ret != BC_STS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, - "CrystalHD: GetDriverStatus failed: %u\n", ret); - return RET_ERROR; - } - - interlaced = output->PicInfo.flags & VDEC_FLAG_INTERLACED_SRC; - - av_log(avctx, AV_LOG_VERBOSE, "Interlaced state: %d\n", - interlaced); - - priv->need_second_field = interlaced && !priv->need_second_field; - - if (!frame->data[0]) { - if (ff_get_buffer(avctx, frame, 0) < 0) - return RET_ERROR; - } - - bwidth = av_image_get_linesize(avctx->pix_fmt, width, 0); - if (bwidth < 0) - return RET_ERROR; - - if (priv->is_70012) { - int pStride; - - if (width <= 720) - pStride = 720; - else if (width <= 1280) - pStride = 1280; - else pStride = 1920; - sStride = av_image_get_linesize(avctx->pix_fmt, pStride, 0); - if (sStride < 0) - return RET_ERROR; - } else { - sStride = bwidth; - } - - dStride = frame->linesize[0]; - dst = frame->data[0]; - - av_log(priv->avctx, AV_LOG_VERBOSE, "CrystalHD: Copying out frame\n"); - - /* - * The hardware doesn't return the first sample of a picture. - * Ignoring why it behaves this way, it's better to copy the sample from - * the second line, rather than the next sample across because the chroma - * values should be correct (assuming the decoded video was 4:2:0, which - * it was). - */ - *((uint32_t *)src) = *((uint32_t *)(src + sStride)); - - if (interlaced) { - int dY = 0; - int sY = 0; - - height /= 2; - if (bottom_field) { - av_log(priv->avctx, AV_LOG_VERBOSE, "Interlaced: bottom field\n"); - dY = 1; - } else { - av_log(priv->avctx, AV_LOG_VERBOSE, "Interlaced: top field\n"); - dY = 0; - } - - for (sY = 0; sY < height; dY++, sY++) { - memcpy(&(dst[dY * dStride]), &(src[sY * sStride]), bwidth); - dY++; - } - } else { - av_image_copy_plane(dst, dStride, src, sStride, bwidth, height); - } - - frame->interlaced_frame = interlaced; - if (interlaced) - frame->top_field_first = !bottom_first; - - frame->pts = pkt_pts; - - frame->pkt_pos = -1; - frame->duration = 0; - frame->pkt_size = -1; - - if (!priv->need_second_field) { - *got_frame = 1; - } else { - return RET_COPY_AGAIN; - } - - return RET_OK; -} - - -static inline CopyRet receive_frame(AVCodecContext *avctx, - AVFrame *frame, int *got_frame) -{ - BC_STATUS ret; - BC_DTS_PROC_OUT output = { - .PicInfo.width = avctx->width, - .PicInfo.height = avctx->height, - }; - CHDContext *priv = avctx->priv_data; - HANDLE dev = priv->dev; - - *got_frame = 0; - - // Request decoded data from the driver - ret = DtsProcOutputNoCopy(dev, OUTPUT_PROC_TIMEOUT, &output); - if (ret == BC_STS_FMT_CHANGE) { - av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Initial format change\n"); - avctx->width = output.PicInfo.width; - avctx->height = output.PicInfo.height; - switch ( output.PicInfo.aspect_ratio ) { - case vdecAspectRatioSquare: - avctx->sample_aspect_ratio = (AVRational) { 1, 1}; - break; - case vdecAspectRatio12_11: - avctx->sample_aspect_ratio = (AVRational) { 12, 11}; - break; - case vdecAspectRatio10_11: - avctx->sample_aspect_ratio = (AVRational) { 10, 11}; - break; - case vdecAspectRatio16_11: - avctx->sample_aspect_ratio = (AVRational) { 16, 11}; - break; - case vdecAspectRatio40_33: - avctx->sample_aspect_ratio = (AVRational) { 40, 33}; - break; - case vdecAspectRatio24_11: - avctx->sample_aspect_ratio = (AVRational) { 24, 11}; - break; - case vdecAspectRatio20_11: - avctx->sample_aspect_ratio = (AVRational) { 20, 11}; - break; - case vdecAspectRatio32_11: - avctx->sample_aspect_ratio = (AVRational) { 32, 11}; - break; - case vdecAspectRatio80_33: - avctx->sample_aspect_ratio = (AVRational) { 80, 33}; - break; - case vdecAspectRatio18_11: - avctx->sample_aspect_ratio = (AVRational) { 18, 11}; - break; - case vdecAspectRatio15_11: - avctx->sample_aspect_ratio = (AVRational) { 15, 11}; - break; - case vdecAspectRatio64_33: - avctx->sample_aspect_ratio = (AVRational) { 64, 33}; - break; - case vdecAspectRatio160_99: - avctx->sample_aspect_ratio = (AVRational) {160, 99}; - break; - case vdecAspectRatio4_3: - avctx->sample_aspect_ratio = (AVRational) { 4, 3}; - break; - case vdecAspectRatio16_9: - avctx->sample_aspect_ratio = (AVRational) { 16, 9}; - break; - case vdecAspectRatio221_1: - avctx->sample_aspect_ratio = (AVRational) {221, 1}; - break; - } - return RET_COPY_AGAIN; - } else if (ret == BC_STS_SUCCESS) { - int copy_ret = -1; - if (output.PoutFlags & BC_POUT_FLAGS_PIB_VALID) { - print_frame_info(priv, &output); - - copy_ret = copy_frame(avctx, &output, frame, got_frame); - } else { - /* - * An invalid frame has been consumed. - */ - av_log(avctx, AV_LOG_ERROR, "CrystalHD: ProcOutput succeeded with " - "invalid PIB\n"); - copy_ret = RET_COPY_AGAIN; - } - DtsReleaseOutputBuffs(dev, NULL, FALSE); - - return copy_ret; - } else if (ret == BC_STS_BUSY) { - return RET_COPY_AGAIN; - } else { - av_log(avctx, AV_LOG_ERROR, "CrystalHD: ProcOutput failed %d\n", ret); - return RET_ERROR; - } -} - -static int crystalhd_decode_packet(AVCodecContext *avctx, const AVPacket *avpkt) -{ - BC_STATUS bc_ret; - CHDContext *priv = avctx->priv_data; - HANDLE dev = priv->dev; - int ret = 0; - - av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: decode_packet\n"); - - if (avpkt && avpkt->size) { - uint64_t pts; - - /* - * Despite being notionally opaque, either libcrystalhd or - * the hardware itself will mangle pts values that are too - * small or too large. The docs claim it should be in units - * of 100ns. Given that we're nominally dealing with a black - * box on both sides, any transform we do has no guarantee of - * avoiding mangling so we need to build a mapping to values - * we know will not be mangled. - */ - pts = opaque_list_push(priv, avpkt->pts); - if (!pts) { - ret = AVERROR(ENOMEM); - goto exit; - } - av_log(priv->avctx, AV_LOG_VERBOSE, - "input \"pts\": %"PRIu64"\n", pts); - bc_ret = DtsProcInput(dev, avpkt->data, avpkt->size, pts, 0); - if (bc_ret == BC_STS_BUSY) { - av_log(avctx, AV_LOG_WARNING, - "CrystalHD: ProcInput returned busy\n"); - ret = AVERROR(EAGAIN); - goto exit; - } else if (bc_ret != BC_STS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, - "CrystalHD: ProcInput failed: %u\n", ret); - ret = -1; - goto exit; - } - } else { - av_log(avctx, AV_LOG_INFO, "CrystalHD: No more input data\n"); - priv->draining = 1; - ret = AVERROR_EOF; - goto exit; - } - exit: - return ret; -} - -static int crystalhd_receive_frame(AVCodecContext *avctx, AVFrame *frame) -{ - BC_STATUS bc_ret; - BC_DTS_STATUS decoder_status = { 0, }; - CopyRet rec_ret; - CHDContext *priv = avctx->priv_data; - AVPacket *const pkt = priv->pkt; - HANDLE dev = priv->dev; - int got_frame = 0; - int ret = 0; - - av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: receive_frame\n"); - - ret = ff_decode_get_packet(avctx, pkt); - if (ret < 0 && ret != AVERROR_EOF) { - return ret; - } - - while (pkt->size > DtsTxFreeSize(dev)) { - /* - * Block until there is space in the buffer for the next packet. - * We assume that the hardware will make forward progress at this - * point, although in pathological cases that may not happen. - */ - av_log(avctx, AV_LOG_TRACE, "CrystalHD: Waiting for space in input buffer\n"); - } - - ret = crystalhd_decode_packet(avctx, pkt); - av_packet_unref(pkt); - // crystalhd_is_buffer_full() should avoid this. - if (ret == AVERROR(EAGAIN)) { - ret = AVERROR_EXTERNAL; - } - if (ret < 0 && ret != AVERROR_EOF) { - return ret; - } - - do { - bc_ret = DtsGetDriverStatus(dev, &decoder_status); - if (bc_ret != BC_STS_SUCCESS) { - av_log(avctx, AV_LOG_ERROR, "CrystalHD: GetDriverStatus failed\n"); - return -1; - } - - if (decoder_status.ReadyListCount == 0) { - av_log(avctx, AV_LOG_VERBOSE, "CrystalHD: Insufficient frames ready. Returning\n"); - got_frame = 0; - rec_ret = RET_OK; - break; - } - - rec_ret = receive_frame(avctx, frame, &got_frame); - } while (rec_ret == RET_COPY_AGAIN); - - if (rec_ret == RET_ERROR) { - return -1; - } else if (got_frame == 0) { - return priv->draining ? AVERROR_EOF : AVERROR(EAGAIN); - } else { - return 0; - } -} - -#define DEFINE_CRYSTALHD_DECODER(x, X, bsf_name) \ - static const AVClass x##_crystalhd_class = { \ - .class_name = #x "_crystalhd", \ - .item_name = av_default_item_name, \ - .option = options, \ - .version = LIBAVUTIL_VERSION_INT, \ - }; \ - const FFCodec ff_##x##_crystalhd_decoder = { \ - .p.name = #x "_crystalhd", \ - CODEC_LONG_NAME("CrystalHD " #X " decoder"), \ - .p.type = AVMEDIA_TYPE_VIDEO, \ - .p.id = AV_CODEC_ID_##X, \ - .priv_data_size = sizeof(CHDContext), \ - .p.priv_class = &x##_crystalhd_class, \ - .init = init, \ - .close = uninit, \ - FF_CODEC_RECEIVE_FRAME_CB(crystalhd_receive_frame), \ - .flush = flush, \ - .bsfs = bsf_name, \ - .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, \ - .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | \ - FF_CODEC_CAP_SETS_FRAME_PROPS, \ - .p.pix_fmts = (const enum AVPixelFormat[]){AV_PIX_FMT_YUYV422, AV_PIX_FMT_NONE}, \ - .p.wrapper_name = "crystalhd", \ - }; - -#if CONFIG_H264_CRYSTALHD_DECODER -DEFINE_CRYSTALHD_DECODER(h264, H264, "h264_mp4toannexb") -#endif - -#if CONFIG_MPEG2_CRYSTALHD_DECODER -DEFINE_CRYSTALHD_DECODER(mpeg2, MPEG2VIDEO, NULL) -#endif - -#if CONFIG_MPEG4_CRYSTALHD_DECODER -DEFINE_CRYSTALHD_DECODER(mpeg4, MPEG4, "mpeg4_unpack_bframes") -#endif - -#if CONFIG_MSMPEG4_CRYSTALHD_DECODER -DEFINE_CRYSTALHD_DECODER(msmpeg4, MSMPEG4V3, NULL) -#endif - -#if CONFIG_VC1_CRYSTALHD_DECODER -DEFINE_CRYSTALHD_DECODER(vc1, VC1, NULL) -#endif - -#if CONFIG_WMV3_CRYSTALHD_DECODER -DEFINE_CRYSTALHD_DECODER(wmv3, WMV3, NULL) -#endif diff --git a/libavcodec/cscd.c b/libavcodec/cscd.c index 393f5c59aed..685cf18cec4 100644 --- a/libavcodec/cscd.c +++ b/libavcodec/cscd.c @@ -113,12 +113,12 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, // flip upside down, add difference frame if (buf[0] & 1) { // keyframe c->pic->pict_type = AV_PICTURE_TYPE_I; - c->pic->key_frame = 1; + c->pic->flags |= AV_FRAME_FLAG_KEY; copy_frame_default(c->pic, c->decomp_buf, c->linelen, c->height); } else { c->pic->pict_type = AV_PICTURE_TYPE_P; - c->pic->key_frame = 0; + c->pic->flags &= ~AV_FRAME_FLAG_KEY; add_frame_default(c->pic, c->decomp_buf, c->linelen, c->height); } diff --git a/libavcodec/cuviddec.c b/libavcodec/cuviddec.c index 4ba7918b644..acc405f78aa 100644 --- a/libavcodec/cuviddec.c +++ b/libavcodec/cuviddec.c @@ -115,6 +115,12 @@ typedef struct CuvidParsedFrame #define CHECK_CU(x) FF_CUDA_CHECK_DL(avctx, ctx->cudl, x) +// NV recommends [2;4] range +#define CUVID_MAX_DISPLAY_DELAY (4) + +// Actual pool size will be determined by parser. +#define CUVID_DEFAULT_NUM_SURFACES (CUVID_MAX_DISPLAY_DELAY + 1) + static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* format) { AVCodecContext *avctx = opaque; @@ -124,6 +130,7 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form CUVIDDECODECREATEINFO cuinfo; int surface_fmt; int chroma_444; + int fifo_size_inc; int old_width = avctx->width; int old_height = avctx->height; @@ -309,6 +316,25 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form return 0; } + fifo_size_inc = ctx->nb_surfaces; + ctx->nb_surfaces = FFMAX(ctx->nb_surfaces, format->min_num_decode_surfaces + 3); + + if (avctx->extra_hw_frames > 0) + ctx->nb_surfaces += avctx->extra_hw_frames; + + fifo_size_inc = ctx->nb_surfaces - fifo_size_inc; + if (fifo_size_inc > 0 && av_fifo_grow2(ctx->frame_queue, fifo_size_inc) < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to grow frame queue on video sequence callback\n"); + ctx->internal_error = AVERROR(ENOMEM); + return 0; + } + + if (fifo_size_inc > 0 && av_reallocp_array(&ctx->key_frame, ctx->nb_surfaces, sizeof(int)) < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to grow key frame array on video sequence callback\n"); + ctx->internal_error = AVERROR(ENOMEM); + return 0; + } + cuinfo.ulNumDecodeSurfaces = ctx->nb_surfaces; cuinfo.ulNumOutputSurfaces = 1; cuinfo.ulCreationFlags = cudaVideoCreate_PreferCUVID; @@ -334,6 +360,11 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form } } + if(ctx->cuparseinfo.ulMaxNumDecodeSurfaces != cuinfo.ulNumDecodeSurfaces) { + ctx->cuparseinfo.ulMaxNumDecodeSurfaces = cuinfo.ulNumDecodeSurfaces; + return cuinfo.ulNumDecodeSurfaces; + } + return 1; } @@ -599,7 +630,10 @@ static int cuvid_output_frame(AVCodecContext *avctx, AVFrame *frame) goto error; } - frame->key_frame = ctx->key_frame[parsed_frame.dispinfo.picture_index]; + if (ctx->key_frame[parsed_frame.dispinfo.picture_index]) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; ctx->key_frame[parsed_frame.dispinfo.picture_index] = 0; frame->width = avctx->width; @@ -623,14 +657,19 @@ static int cuvid_output_frame(AVCodecContext *avctx, AVFrame *frame) /* CUVIDs opaque reordering breaks the internal pkt logic. * So set pkt_pts and clear all the other pkt_ fields. */ - frame->pkt_pos = -1; frame->duration = 0; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + frame->pkt_pos = -1; frame->pkt_size = -1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif - frame->interlaced_frame = !parsed_frame.is_deinterlacing && !parsed_frame.dispinfo.progressive_frame; + if (!parsed_frame.is_deinterlacing && !parsed_frame.dispinfo.progressive_frame) + frame->flags |= AV_FRAME_FLAG_INTERLACED; - if (frame->interlaced_frame) - frame->top_field_first = parsed_frame.dispinfo.top_field_first; + if ((frame->flags & AV_FRAME_FLAG_INTERLACED) && parsed_frame.dispinfo.top_field_first) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } else if (ctx->decoder_flushing) { ret = AVERROR_EOF; } else { @@ -838,6 +877,10 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx) goto error; } + // respect the deprecated "surfaces" option if non-default value is given by user; + if(ctx->nb_surfaces < 0) + ctx->nb_surfaces = CUVID_DEFAULT_NUM_SURFACES; + ctx->frame_queue = av_fifo_alloc2(ctx->nb_surfaces, sizeof(CuvidParsedFrame), 0); if (!ctx->frame_queue) { ret = AVERROR(ENOMEM); @@ -984,8 +1027,8 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx) goto error; } - ctx->cuparseinfo.ulMaxNumDecodeSurfaces = ctx->nb_surfaces; - ctx->cuparseinfo.ulMaxDisplayDelay = (avctx->flags & AV_CODEC_FLAG_LOW_DELAY) ? 0 : 4; + ctx->cuparseinfo.ulMaxNumDecodeSurfaces = 1; + ctx->cuparseinfo.ulMaxDisplayDelay = (avctx->flags & AV_CODEC_FLAG_LOW_DELAY) ? 0 : CUVID_MAX_DISPLAY_DELAY; ctx->cuparseinfo.pUserData = avctx; ctx->cuparseinfo.pfnSequenceCallback = cuvid_handle_video_sequence; ctx->cuparseinfo.pfnDecodePicture = cuvid_handle_picture_decode; @@ -1084,12 +1127,12 @@ static void cuvid_flush(AVCodecContext *avctx) #define OFFSET(x) offsetof(CuvidContext, x) #define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM static const AVOption options[] = { - { "deint", "Set deinterlacing mode", OFFSET(deint_mode), AV_OPT_TYPE_INT, { .i64 = cudaVideoDeinterlaceMode_Weave }, cudaVideoDeinterlaceMode_Weave, cudaVideoDeinterlaceMode_Adaptive, VD, "deint" }, - { "weave", "Weave deinterlacing (do nothing)", 0, AV_OPT_TYPE_CONST, { .i64 = cudaVideoDeinterlaceMode_Weave }, 0, 0, VD, "deint" }, - { "bob", "Bob deinterlacing", 0, AV_OPT_TYPE_CONST, { .i64 = cudaVideoDeinterlaceMode_Bob }, 0, 0, VD, "deint" }, - { "adaptive", "Adaptive deinterlacing", 0, AV_OPT_TYPE_CONST, { .i64 = cudaVideoDeinterlaceMode_Adaptive }, 0, 0, VD, "deint" }, + { "deint", "Set deinterlacing mode", OFFSET(deint_mode), AV_OPT_TYPE_INT, { .i64 = cudaVideoDeinterlaceMode_Weave }, cudaVideoDeinterlaceMode_Weave, cudaVideoDeinterlaceMode_Adaptive, VD, .unit = "deint" }, + { "weave", "Weave deinterlacing (do nothing)", 0, AV_OPT_TYPE_CONST, { .i64 = cudaVideoDeinterlaceMode_Weave }, 0, 0, VD, .unit = "deint" }, + { "bob", "Bob deinterlacing", 0, AV_OPT_TYPE_CONST, { .i64 = cudaVideoDeinterlaceMode_Bob }, 0, 0, VD, .unit = "deint" }, + { "adaptive", "Adaptive deinterlacing", 0, AV_OPT_TYPE_CONST, { .i64 = cudaVideoDeinterlaceMode_Adaptive }, 0, 0, VD, .unit = "deint" }, { "gpu", "GPU to be used for decoding", OFFSET(cu_gpu), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VD }, - { "surfaces", "Maximum surfaces to be used for decoding", OFFSET(nb_surfaces), AV_OPT_TYPE_INT, { .i64 = 25 }, 0, INT_MAX, VD }, + { "surfaces", "Maximum surfaces to be used for decoding", OFFSET(nb_surfaces), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VD | AV_OPT_FLAG_DEPRECATED }, { "drop_second_field", "Drop second field when deinterlacing", OFFSET(drop_second_field), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD }, { "crop", "Crop (top)x(bottom)x(left)x(right)", OFFSET(crop_expr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VD }, { "resize", "Resize (width)x(height)", OFFSET(resize_expr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VD }, @@ -1131,11 +1174,6 @@ static const AVCodecHWConfigInternal *const cuvid_hw_configs[] = { .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, \ .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | \ FF_CODEC_CAP_SETS_FRAME_PROPS, \ - .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, \ - AV_PIX_FMT_NV12, \ - AV_PIX_FMT_P010, \ - AV_PIX_FMT_P016, \ - AV_PIX_FMT_NONE }, \ .hw_configs = cuvid_hw_configs, \ .p.wrapper_name = "cuvid", \ }; diff --git a/libavcodec/d3d11va.h b/libavcodec/d3d11va.h index 6816b6c1e68..27f40e5519b 100644 --- a/libavcodec/d3d11va.h +++ b/libavcodec/d3d11va.h @@ -45,9 +45,6 @@ * @{ */ -#define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for Direct3D11 and old UVD/UVD+ ATI video cards -#define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO 2 ///< Work around for Direct3D11 and old Intel GPUs with ClearVideo interface - /** * This structure is used to provides the necessary configurations and data * to the Direct3D11 FFmpeg HWAccel implementation. diff --git a/libavcodec/d3d12va_av1.c b/libavcodec/d3d12va_av1.c new file mode 100644 index 00000000000..76d97299008 --- /dev/null +++ b/libavcodec/d3d12va_av1.c @@ -0,0 +1,206 @@ +/* + * Direct3D 12 AV1 HW acceleration + * + * copyright (c) 2022-2023 Wu Jianhua + * + * 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 "config_components.h" +#include "libavutil/avassert.h" +#include "libavutil/hwcontext_d3d12va_internal.h" +#include "av1dec.h" +#include "dxva2_internal.h" +#include "d3d12va_decode.h" + +#define MAX_TILES 256 + +typedef struct D3D12AV1DecodeContext { + D3D12VADecodeContext ctx; + uint8_t *bitstream_buffer; +} D3D12AV1DecodeContext; + +#define D3D12_AV1_DECODE_CONTEXT(avctx) ((D3D12AV1DecodeContext *)D3D12VA_DECODE_CONTEXT(avctx)) + +typedef struct AV1DecodePictureContext { + DXVA_PicParams_AV1 pp; + unsigned tile_count; + DXVA_Tile_AV1 tiles[MAX_TILES]; + uint8_t *bitstream; + unsigned bitstream_size; +} AV1DecodePictureContext; + +static int d3d12va_av1_start_frame(AVCodecContext *avctx, av_unused const uint8_t *buffer, av_unused uint32_t size) +{ + const AV1DecContext *h = avctx->priv_data; + AV1DecodePictureContext *ctx_pic = h->cur_frame.hwaccel_picture_private; + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + if (!ctx) + return -1; + + av_assert0(ctx_pic); + + ctx->used_mask = 0; + + if (ff_dxva2_av1_fill_picture_parameters(avctx, (AVDXVAContext *)ctx, &ctx_pic->pp) < 0) + return -1; + + ctx_pic->bitstream = NULL; + ctx_pic->bitstream_size = 0; + ctx_pic->tile_count = 0; + + return 0; +} + +static int d3d12va_av1_decode_slice(AVCodecContext *avctx, + const uint8_t *buffer, + uint32_t size) +{ + const AV1DecContext *h = avctx->priv_data; + const AV1RawFrameHeader *frame_header = h->raw_frame_header; + AV1DecodePictureContext *ctx_pic = h->cur_frame.hwaccel_picture_private; + int offset = 0; + uint32_t tg_start, tg_end; + + ctx_pic->tile_count = frame_header->tile_cols * frame_header->tile_rows; + + if (ctx_pic->tile_count > MAX_TILES) + return AVERROR(ENOSYS); + + if (ctx_pic->tile_count == h->tg_end - h->tg_start + 1) { + tg_start = 0; + tg_end = ctx_pic->tile_count - 1; + ctx_pic->bitstream = (uint8_t *)buffer; + ctx_pic->bitstream_size = size; + } else { + ctx_pic->bitstream = D3D12_AV1_DECODE_CONTEXT(avctx)->bitstream_buffer; + memcpy(ctx_pic->bitstream + ctx_pic->bitstream_size, buffer, size); + tg_start = h->tg_start; + tg_end = h->tg_end; + offset = ctx_pic->bitstream_size; + ctx_pic->bitstream_size += size; + } + + for (uint32_t tile_num = tg_start; tile_num <= tg_end; tile_num++) { + ctx_pic->tiles[tile_num].DataOffset = offset + h->tile_group_info[tile_num].tile_offset; + ctx_pic->tiles[tile_num].DataSize = h->tile_group_info[tile_num].tile_size; + ctx_pic->tiles[tile_num].row = h->tile_group_info[tile_num].tile_row; + ctx_pic->tiles[tile_num].column = h->tile_group_info[tile_num].tile_column; + ctx_pic->tiles[tile_num].anchor_frame = 0xFF; + } + + return 0; +} + +static int update_input_arguments(AVCodecContext *avctx, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *input_args, ID3D12Resource *buffer) +{ + const AV1DecContext *h = avctx->priv_data; + AV1DecodePictureContext *ctx_pic = h->cur_frame.hwaccel_picture_private; + void *mapped_data; + + D3D12_VIDEO_DECODE_FRAME_ARGUMENT *args = &input_args->FrameArguments[input_args->NumFrameArguments++]; + args->Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_SLICE_CONTROL; + args->Size = sizeof(DXVA_Tile_AV1) * ctx_pic->tile_count; + args->pData = ctx_pic->tiles; + + input_args->CompressedBitstream = (D3D12_VIDEO_DECODE_COMPRESSED_BITSTREAM){ + .pBuffer = buffer, + .Offset = 0, + .Size = ctx_pic->bitstream_size, + }; + + if (FAILED(ID3D12Resource_Map(buffer, 0, NULL, &mapped_data))) { + av_log(avctx, AV_LOG_ERROR, "Failed to map D3D12 Buffer resource!\n"); + return AVERROR(EINVAL); + } + + memcpy(mapped_data, ctx_pic->bitstream, ctx_pic->bitstream_size); + + ID3D12Resource_Unmap(buffer, 0, NULL); + + return 0; +} + +static int d3d12va_av1_end_frame(AVCodecContext *avctx) +{ + int ret; + const AV1DecContext *h = avctx->priv_data; + AV1DecodePictureContext *ctx_pic = h->cur_frame.hwaccel_picture_private; + + if (ctx_pic->tiles <= 0 || ctx_pic->bitstream_size <= 0) + return -1; + + ret = ff_d3d12va_common_end_frame(avctx, h->cur_frame.f, &ctx_pic->pp, sizeof(ctx_pic->pp), + NULL, 0, update_input_arguments); + + return ret; +} + +static int d3d12va_av1_decode_init(AVCodecContext *avctx) +{ + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + D3D12AV1DecodeContext *av1_ctx = D3D12_AV1_DECODE_CONTEXT(avctx); + DXVA_PicParams_AV1 pp; + + int ret; + + if (avctx->profile != AV_PROFILE_AV1_MAIN) + return AVERROR(EINVAL); + + ctx->cfg.DecodeProfile = D3D12_VIDEO_DECODE_PROFILE_AV1_PROFILE0; + + ctx->max_num_ref = FF_ARRAY_ELEMS(pp.RefFrameMapTextureIndex) + 1; + + ret = ff_d3d12va_decode_init(avctx); + if (ret < 0) + return ret; + + if (!av1_ctx->bitstream_buffer) { + av1_ctx->bitstream_buffer = av_malloc(ff_d3d12va_get_suitable_max_bitstream_size(avctx)); + if (!av1_ctx->bitstream_buffer) + return AVERROR(ENOMEM); + } + + return 0; +} + +static int d3d12va_av1_decode_uninit(AVCodecContext *avctx) +{ + D3D12AV1DecodeContext *ctx = D3D12_AV1_DECODE_CONTEXT(avctx); + + if (ctx->bitstream_buffer) + av_freep(&ctx->bitstream_buffer); + + return ff_d3d12va_decode_uninit(avctx); +} + +#if CONFIG_AV1_D3D12VA_HWACCEL +const FFHWAccel ff_av1_d3d12va_hwaccel = { + .p.name = "av1_d3d12va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_D3D12, + .init = d3d12va_av1_decode_init, + .uninit = d3d12va_av1_decode_uninit, + .start_frame = d3d12va_av1_start_frame, + .decode_slice = d3d12va_av1_decode_slice, + .end_frame = d3d12va_av1_end_frame, + .frame_params = ff_d3d12va_common_frame_params, + .frame_priv_data_size = sizeof(AV1DecodePictureContext), + .priv_data_size = sizeof(D3D12AV1DecodeContext), +}; +#endif diff --git a/libavcodec/d3d12va_decode.c b/libavcodec/d3d12va_decode.c new file mode 100644 index 00000000000..af7cf11640c --- /dev/null +++ b/libavcodec/d3d12va_decode.c @@ -0,0 +1,536 @@ +/* + * Direct3D 12 HW acceleration video decoder + * + * copyright (c) 2022-2023 Wu Jianhua + * + * 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 +#include + +#include "libavutil/common.h" +#include "libavutil/log.h" +#include "libavutil/time.h" +#include "libavutil/imgutils.h" +#include "libavutil/hwcontext_d3d12va_internal.h" +#include "libavutil/hwcontext_d3d12va.h" +#include "avcodec.h" +#include "decode.h" +#include "d3d12va_decode.h" +#include "dxva2_internal.h" + +typedef struct HelperObjects { + ID3D12CommandAllocator *command_allocator; + ID3D12Resource *buffer; + uint64_t fence_value; +} HelperObjects; + +int ff_d3d12va_get_suitable_max_bitstream_size(AVCodecContext *avctx) +{ + AVHWFramesContext *frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx); + return av_image_get_buffer_size(frames_ctx->sw_format, avctx->coded_width, avctx->coded_height, 1); +} + +unsigned ff_d3d12va_get_surface_index(const AVCodecContext *avctx, + D3D12VADecodeContext *ctx, const AVFrame *frame, + int curr) +{ + AVD3D12VAFrame *f; + ID3D12Resource *res; + unsigned i; + + f = (AVD3D12VAFrame *)frame->data[0]; + if (!f) + goto fail; + + res = f->texture; + if (!res) + goto fail; + + for (i = 0; i < ctx->max_num_ref; i++) { + if (ctx->ref_resources[i] && res == ctx->ref_resources[i]) { + ctx->used_mask |= 1 << i; + return i; + } + } + + if (curr) { + for (i = 0; i < ctx->max_num_ref; i++) { + if (!((ctx->used_mask >> i) & 0x1)) { + ctx->ref_resources[i] = res; + return i; + } + } + } + +fail: + av_log((AVCodecContext *)avctx, AV_LOG_WARNING, "Could not get surface index. Using 0 instead.\n"); + return 0; +} + +static int d3d12va_get_valid_helper_objects(AVCodecContext *avctx, ID3D12CommandAllocator **ppAllocator, + ID3D12Resource **ppBuffer) +{ + HRESULT hr; + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + HelperObjects obj = { 0 }; + D3D12_HEAP_PROPERTIES heap_props = { .Type = D3D12_HEAP_TYPE_UPLOAD }; + + D3D12_RESOURCE_DESC desc = { + .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, + .Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT, + .Width = ctx->bitstream_size, + .Height = 1, + .DepthOrArraySize = 1, + .MipLevels = 1, + .Format = DXGI_FORMAT_UNKNOWN, + .SampleDesc = { .Count = 1, .Quality = 0 }, + .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + .Flags = D3D12_RESOURCE_FLAG_NONE, + }; + + if (av_fifo_peek(ctx->objects_queue, &obj, 1, 0) >= 0) { + uint64_t completion = ID3D12Fence_GetCompletedValue(ctx->sync_ctx.fence); + if (completion >= obj.fence_value) { + *ppAllocator = obj.command_allocator; + *ppBuffer = obj.buffer; + av_fifo_read(ctx->objects_queue, &obj, 1); + return 0; + } + } + + hr = ID3D12Device_CreateCommandAllocator(ctx->device_ctx->device, D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE, + &IID_ID3D12CommandAllocator, (void **)ppAllocator); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create a new command allocator!\n"); + return AVERROR(EINVAL); + } + + hr = ID3D12Device_CreateCommittedResource(ctx->device_ctx->device, &heap_props, D3D12_HEAP_FLAG_NONE, + &desc, D3D12_RESOURCE_STATE_GENERIC_READ, NULL, + &IID_ID3D12Resource, (void **)ppBuffer); + + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "Failed to create a new d3d12 buffer!\n"); + return AVERROR(EINVAL); + } + + return 0; +} + +static int d3d12va_discard_helper_objects(AVCodecContext *avctx, ID3D12CommandAllocator *pAllocator, + ID3D12Resource *pBuffer, uint64_t fence_value) +{ + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + + HelperObjects obj = { + .command_allocator = pAllocator, + .buffer = pBuffer, + .fence_value = fence_value, + }; + + if (av_fifo_write(ctx->objects_queue, &obj, 1) < 0) { + D3D12_OBJECT_RELEASE(pAllocator); + D3D12_OBJECT_RELEASE(pBuffer); + return AVERROR(ENOMEM); + } + + return 0; +} + +static int d3d12va_fence_completion(AVD3D12VASyncContext *psync_ctx) +{ + uint64_t completion = ID3D12Fence_GetCompletedValue(psync_ctx->fence); + if (completion < psync_ctx->fence_value) { + if (FAILED(ID3D12Fence_SetEventOnCompletion(psync_ctx->fence, psync_ctx->fence_value, psync_ctx->event))) + return AVERROR(EINVAL); + + WaitForSingleObjectEx(psync_ctx->event, INFINITE, FALSE); + } + + return 0; +} + +static void bufref_free_interface(void *opaque, uint8_t *data) +{ + D3D12_OBJECT_RELEASE(opaque); +} + +static AVBufferRef *bufref_wrap_interface(IUnknown *iface) +{ + return av_buffer_create((uint8_t*)iface, 1, bufref_free_interface, iface, 0); +} + +static int d3d12va_sync_with_gpu(AVCodecContext *avctx) +{ + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + + DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, ctx->sync_ctx.fence, ++ctx->sync_ctx.fence_value)); + return d3d12va_fence_completion(&ctx->sync_ctx); + +fail: + return AVERROR(EINVAL); +} + +static int d3d12va_create_decoder_heap(AVCodecContext *avctx) +{ + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + AVHWFramesContext *frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx); + AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx; + AVD3D12VAFramesContext *frames_hwctx = frames_ctx->hwctx; + + D3D12_VIDEO_DECODER_HEAP_DESC desc = { + .NodeMask = 0, + .Configuration = ctx->cfg, + .DecodeWidth = frames_ctx->width, + .DecodeHeight = frames_ctx->height, + .Format = frames_hwctx->format, + .FrameRate = { avctx->framerate.num, avctx->framerate.den }, + .BitRate = avctx->bit_rate, + .MaxDecodePictureBufferCount = ctx->max_num_ref, + }; + + DX_CHECK(ID3D12VideoDevice_CreateVideoDecoderHeap(device_hwctx->video_device, &desc, + &IID_ID3D12VideoDecoderHeap, (void **)&ctx->decoder_heap)); + + return 0; + +fail: + if (ctx->decoder) { + av_log(avctx, AV_LOG_ERROR, "D3D12 doesn't support decoding frames with an extent " + "[width(%d), height(%d)], on your device!\n", frames_ctx->width, frames_ctx->height); + } + + return AVERROR(EINVAL); +} + +static int d3d12va_create_decoder(AVCodecContext *avctx) +{ + D3D12_VIDEO_DECODER_DESC desc; + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + AVHWFramesContext *frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx); + AVD3D12VADeviceContext *device_hwctx = ctx->device_ctx; + AVD3D12VAFramesContext *frames_hwctx = frames_ctx->hwctx; + + D3D12_FEATURE_DATA_VIDEO_DECODE_SUPPORT feature = { + .NodeIndex = 0, + .Configuration = ctx->cfg, + .Width = frames_ctx->width, + .Height = frames_ctx->height, + .DecodeFormat = frames_hwctx->format, + .FrameRate = { avctx->framerate.num, avctx->framerate.den }, + .BitRate = avctx->bit_rate, + }; + + DX_CHECK(ID3D12VideoDevice_CheckFeatureSupport(device_hwctx->video_device, D3D12_FEATURE_VIDEO_DECODE_SUPPORT, + &feature, sizeof(feature))); + if (!(feature.SupportFlags & D3D12_VIDEO_DECODE_SUPPORT_FLAG_SUPPORTED)) { + av_log(avctx, AV_LOG_ERROR, "D3D12 video decode is not supported on this device.\n"); + return AVERROR(ENOSYS); + } + if (!(feature.DecodeTier >= D3D12_VIDEO_DECODE_TIER_2)) { + av_log(avctx, AV_LOG_ERROR, "D3D12 video decode on this device requires tier %d support, " + "but it is not implemented.\n", feature.DecodeTier); + return AVERROR_PATCHWELCOME; + } + + desc = (D3D12_VIDEO_DECODER_DESC) { + .NodeMask = 0, + .Configuration = ctx->cfg, + }; + + DX_CHECK(ID3D12VideoDevice_CreateVideoDecoder(device_hwctx->video_device, &desc, &IID_ID3D12VideoDecoder, + (void **)&ctx->decoder)); + + ctx->decoder_ref = bufref_wrap_interface((IUnknown *)ctx->decoder); + if (!ctx->decoder_ref) + return AVERROR(ENOMEM); + + return 0; + +fail: + return AVERROR(EINVAL); +} + +int ff_d3d12va_common_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx) +{ + AVHWFramesContext *frames_ctx = (AVHWFramesContext *)hw_frames_ctx->data; + + frames_ctx->format = AV_PIX_FMT_D3D12; + frames_ctx->sw_format = avctx->sw_pix_fmt == AV_PIX_FMT_YUV420P10 ? AV_PIX_FMT_P010 : AV_PIX_FMT_NV12; + frames_ctx->width = avctx->width; + frames_ctx->height = avctx->height; + + return 0; +} + +int ff_d3d12va_decode_init(AVCodecContext *avctx) +{ + int ret; + AVHWFramesContext *frames_ctx; + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + ID3D12Resource *buffer = NULL; + ID3D12CommandAllocator *command_allocator = NULL; + D3D12_COMMAND_QUEUE_DESC queue_desc = { + .Type = D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE, + .Priority = 0, + .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE, + .NodeMask = 0, + }; + + ctx->pix_fmt = avctx->hwaccel->pix_fmt; + + ret = ff_decode_get_hw_frames_ctx(avctx, AV_HWDEVICE_TYPE_D3D12VA); + if (ret < 0) + return ret; + + frames_ctx = D3D12VA_FRAMES_CONTEXT(avctx); + ctx->device_ctx = (AVD3D12VADeviceContext *)frames_ctx->device_ctx->hwctx; + + if (frames_ctx->format != ctx->pix_fmt) { + av_log(avctx, AV_LOG_ERROR, "Invalid pixfmt for hwaccel!\n"); + goto fail; + } + + ret = d3d12va_create_decoder(avctx); + if (ret < 0) + goto fail; + + ret = d3d12va_create_decoder_heap(avctx); + if (ret < 0) + goto fail; + + ctx->bitstream_size = ff_d3d12va_get_suitable_max_bitstream_size(avctx); + + ctx->ref_resources = av_calloc(ctx->max_num_ref, sizeof(*ctx->ref_resources)); + if (!ctx->ref_resources) + return AVERROR(ENOMEM); + + ctx->ref_subresources = av_calloc(ctx->max_num_ref, sizeof(*ctx->ref_subresources)); + if (!ctx->ref_subresources) + return AVERROR(ENOMEM); + + ctx->objects_queue = av_fifo_alloc2(D3D12VA_VIDEO_DEC_ASYNC_DEPTH, + sizeof(HelperObjects), AV_FIFO_FLAG_AUTO_GROW); + if (!ctx->objects_queue) + return AVERROR(ENOMEM); + + DX_CHECK(ID3D12Device_CreateFence(ctx->device_ctx->device, 0, D3D12_FENCE_FLAG_NONE, + &IID_ID3D12Fence, (void **)&ctx->sync_ctx.fence)); + + ctx->sync_ctx.event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!ctx->sync_ctx.event) + goto fail; + + ret = d3d12va_get_valid_helper_objects(avctx, &command_allocator, &buffer); + if (ret < 0) + goto fail; + + DX_CHECK(ID3D12Device_CreateCommandQueue(ctx->device_ctx->device, &queue_desc, + &IID_ID3D12CommandQueue, (void **)&ctx->command_queue)); + + DX_CHECK(ID3D12Device_CreateCommandList(ctx->device_ctx->device, 0, queue_desc.Type, + command_allocator, NULL, &IID_ID3D12CommandList, (void **)&ctx->command_list)); + + DX_CHECK(ID3D12VideoDecodeCommandList_Close(ctx->command_list)); + + ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, (ID3D12CommandList **)&ctx->command_list); + + ret = d3d12va_sync_with_gpu(avctx); + if (ret < 0) + goto fail; + + d3d12va_discard_helper_objects(avctx, command_allocator, buffer, ctx->sync_ctx.fence_value); + if (ret < 0) + goto fail; + + return 0; + +fail: + D3D12_OBJECT_RELEASE(command_allocator); + D3D12_OBJECT_RELEASE(buffer); + ff_d3d12va_decode_uninit(avctx); + + return AVERROR(EINVAL); +} + +int ff_d3d12va_decode_uninit(AVCodecContext *avctx) +{ + int num_allocator = 0; + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + HelperObjects obj; + + if (ctx->sync_ctx.fence) + d3d12va_sync_with_gpu(avctx); + + av_freep(&ctx->ref_resources); + av_freep(&ctx->ref_subresources); + + D3D12_OBJECT_RELEASE(ctx->command_list); + D3D12_OBJECT_RELEASE(ctx->command_queue); + + if (ctx->objects_queue) { + while (av_fifo_read(ctx->objects_queue, &obj, 1) >= 0) { + num_allocator++; + D3D12_OBJECT_RELEASE(obj.buffer); + D3D12_OBJECT_RELEASE(obj.command_allocator); + } + + av_log(avctx, AV_LOG_VERBOSE, "Total number of command allocators reused: %d\n", num_allocator); + } + + av_fifo_freep2(&ctx->objects_queue); + + D3D12_OBJECT_RELEASE(ctx->sync_ctx.fence); + if (ctx->sync_ctx.event) + CloseHandle(ctx->sync_ctx.event); + + D3D12_OBJECT_RELEASE(ctx->decoder_heap); + + av_buffer_unref(&ctx->decoder_ref); + + return 0; +} + +static inline int d3d12va_update_reference_frames_state(AVCodecContext *avctx, D3D12_RESOURCE_BARRIER *barriers, + ID3D12Resource *current_resource, int state_before, int state_end) +{ + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + + int num_barrier = 0; + for (int i = 0; i < ctx->max_num_ref; i++) { + if (((ctx->used_mask >> i) & 0x1) && ctx->ref_resources[i] && ctx->ref_resources[i] != current_resource) { + barriers[num_barrier].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barriers[num_barrier].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barriers[num_barrier].Transition = (D3D12_RESOURCE_TRANSITION_BARRIER){ + .pResource = ctx->ref_resources[i], + .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + .StateBefore = state_before, + .StateAfter = state_end, + }; + num_barrier++; + } + } + + return num_barrier; +} + +int ff_d3d12va_common_end_frame(AVCodecContext *avctx, AVFrame *frame, + const void *pp, unsigned pp_size, + const void *qm, unsigned qm_size, + int(*update_input_arguments)(AVCodecContext *, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *, ID3D12Resource *)) +{ + int ret; + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + ID3D12Resource *buffer = NULL; + ID3D12CommandAllocator *command_allocator = NULL; + AVD3D12VAFrame *f = (AVD3D12VAFrame *)frame->data[0]; + ID3D12Resource *resource = (ID3D12Resource *)f->texture; + + ID3D12VideoDecodeCommandList *cmd_list = ctx->command_list; + D3D12_RESOURCE_BARRIER barriers[32] = { 0 }; + + D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS input_args = { + .NumFrameArguments = 2, + .FrameArguments = { + [0] = { + .Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_PICTURE_PARAMETERS, + .Size = pp_size, + .pData = (void *)pp, + }, + [1] = { + .Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_INVERSE_QUANTIZATION_MATRIX, + .Size = qm_size, + .pData = (void *)qm, + }, + }, + .pHeap = ctx->decoder_heap, + }; + + D3D12_VIDEO_DECODE_OUTPUT_STREAM_ARGUMENTS output_args = { + .ConversionArguments = { 0 }, + .OutputSubresource = 0, + .pOutputTexture2D = resource, + }; + + UINT num_barrier = 1; + barriers[0] = (D3D12_RESOURCE_BARRIER) { + .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE, + .Transition = { + .pResource = resource, + .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + .StateBefore = D3D12_RESOURCE_STATE_COMMON, + .StateAfter = D3D12_RESOURCE_STATE_VIDEO_DECODE_WRITE, + }, + }; + + memset(ctx->ref_subresources, 0, sizeof(UINT) * ctx->max_num_ref); + input_args.ReferenceFrames.NumTexture2Ds = ctx->max_num_ref; + input_args.ReferenceFrames.ppTexture2Ds = ctx->ref_resources; + input_args.ReferenceFrames.pSubresources = ctx->ref_subresources; + + ret = d3d12va_fence_completion(&f->sync_ctx); + if (ret < 0) + goto fail; + + if (!qm) + input_args.NumFrameArguments = 1; + + ret = d3d12va_get_valid_helper_objects(avctx, &command_allocator, &buffer); + if (ret < 0) + goto fail; + + ret = update_input_arguments(avctx, &input_args, buffer); + if (ret < 0) + goto fail; + + DX_CHECK(ID3D12CommandAllocator_Reset(command_allocator)); + + DX_CHECK(ID3D12VideoDecodeCommandList_Reset(cmd_list, command_allocator)); + + num_barrier += d3d12va_update_reference_frames_state(avctx, &barriers[1], resource, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VIDEO_DECODE_READ); + + ID3D12VideoDecodeCommandList_ResourceBarrier(cmd_list, num_barrier, barriers); + + ID3D12VideoDecodeCommandList_DecodeFrame(cmd_list, ctx->decoder, &output_args, &input_args); + + for (int i = 0; i < num_barrier; i++) + FFSWAP(D3D12_RESOURCE_STATES, barriers[i].Transition.StateBefore, barriers[i].Transition.StateAfter); + + ID3D12VideoDecodeCommandList_ResourceBarrier(cmd_list, num_barrier, barriers); + + DX_CHECK(ID3D12VideoDecodeCommandList_Close(cmd_list)); + + ID3D12CommandQueue_ExecuteCommandLists(ctx->command_queue, 1, (ID3D12CommandList **)&ctx->command_list); + + DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, f->sync_ctx.fence, ++f->sync_ctx.fence_value)); + + DX_CHECK(ID3D12CommandQueue_Signal(ctx->command_queue, ctx->sync_ctx.fence, ++ctx->sync_ctx.fence_value)); + + ret = d3d12va_discard_helper_objects(avctx, command_allocator, buffer, ctx->sync_ctx.fence_value); + if (ret < 0) + return ret; + + return 0; + +fail: + if (command_allocator) + d3d12va_discard_helper_objects(avctx, command_allocator, buffer, ctx->sync_ctx.fence_value); + return AVERROR(EINVAL); +} diff --git a/libavcodec/d3d12va_decode.h b/libavcodec/d3d12va_decode.h new file mode 100644 index 00000000000..b64994760a8 --- /dev/null +++ b/libavcodec/d3d12va_decode.h @@ -0,0 +1,179 @@ +/* + * Direct3D 12 HW acceleration video decoder + * + * copyright (c) 2022-2023 Wu Jianhua + * + * 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 + */ + +#ifndef AVCODEC_D3D12VA_DECODE_H +#define AVCODEC_D3D12VA_DECODE_H + +#include "libavutil/fifo.h" +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_d3d12va.h" +#include "avcodec.h" +#include "internal.h" +#include "hwaccel_internal.h" + +/** + * @brief This structure is used to provide the necessary configurations and data + * to the FFmpeg Direct3D 12 HWAccel implementation for video decoder. + */ +typedef struct D3D12VADecodeContext { + AVBufferRef *decoder_ref; + + /** + * D3D12 video decoder + */ + ID3D12VideoDecoder *decoder; + + /** + * D3D12 video decoder heap + */ + ID3D12VideoDecoderHeap *decoder_heap; + + /** + * D3D12 configuration used to create the decoder + * + * Specified by decoders + */ + D3D12_VIDEO_DECODE_CONFIGURATION cfg; + + /** + * A cached queue for reusing the D3D12 command allocators and upload buffers + * + * @see https://learn.microsoft.com/en-us/windows/win32/direct3d12/recording-command-lists-and-bundles#id3d12commandallocator + */ + AVFifo *objects_queue; + + /** + * D3D12 command queue + */ + ID3D12CommandQueue *command_queue; + + /** + * D3D12 video decode command list + */ + ID3D12VideoDecodeCommandList *command_list; + + /** + * The array of resources used for reference frames + * + * The ref_resources.length is the same as D3D12VADecodeContext.max_num_ref + */ + ID3D12Resource **ref_resources; + + /** + * The array of subresources used for reference frames + * + * The ref_subresources.length is the same as D3D12VADecodeContext.max_num_ref + */ + UINT *ref_subresources; + + /** + * Maximum number of reference frames + */ + UINT max_num_ref; + + /** + * Used mask used to record reference frames indices + */ + UINT used_mask; + + /** + * Bitstream size for each frame + */ + UINT bitstream_size; + + /** + * The sync context used to sync command queue + */ + AVD3D12VASyncContext sync_ctx; + + /** + * A pointer to AVD3D12VADeviceContext used to create D3D12 objects + */ + AVD3D12VADeviceContext *device_ctx; + + /** + * Pixel format + */ + enum AVPixelFormat pix_fmt; + + /** + * Private to the FFmpeg AVHWAccel implementation + */ + unsigned report_id; +} D3D12VADecodeContext; + +/** + * @} + */ +#define D3D12VA_VIDEO_DEC_ASYNC_DEPTH 36 +#define D3D12VA_DECODE_CONTEXT(avctx) ((D3D12VADecodeContext *)((avctx)->internal->hwaccel_priv_data)) +#define D3D12VA_FRAMES_CONTEXT(avctx) ((AVHWFramesContext *)(avctx)->hw_frames_ctx->data) + +/** + * @brief Get a suitable maximum bitstream size + * + * Creating and destroying a resource on d3d12 needs sync and reallocation, so use this function + * to help allocate a big enough bitstream buffer to avoid recreating resources when decoding. + * + * @return the suitable size + */ +int ff_d3d12va_get_suitable_max_bitstream_size(AVCodecContext *avctx); + +/** + * @brief init D3D12VADecodeContext + * + * @return Error code (ret < 0 if failed) + */ +int ff_d3d12va_decode_init(AVCodecContext *avctx); + +/** + * @brief uninit D3D12VADecodeContext + * + * @return Error code (ret < 0 if failed) + */ +int ff_d3d12va_decode_uninit(AVCodecContext *avctx); + +/** + * @brief d3d12va common frame params + * + * @return Error code (ret < 0 if failed) + */ +int ff_d3d12va_common_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx); + +/** + * @brief d3d12va common end frame + * + * @param avctx codec context + * @param frame current output frame + * @param pp picture parameters + * @param pp_size the size of the picture parameters + * @param qm quantization matrix + * @param qm_size the size of the quantization matrix + * @param callback update decoder-specified input stream arguments + * @return Error code (ret < 0 if failed) + */ +int ff_d3d12va_common_end_frame(AVCodecContext *avctx, AVFrame *frame, + const void *pp, unsigned pp_size, + const void *qm, unsigned qm_size, + int(*)(AVCodecContext *, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *, ID3D12Resource *)); + +#endif /* AVCODEC_D3D12VA_DEC_H */ diff --git a/libavcodec/d3d12va_h264.c b/libavcodec/d3d12va_h264.c new file mode 100644 index 00000000000..b2fe2955c88 --- /dev/null +++ b/libavcodec/d3d12va_h264.c @@ -0,0 +1,203 @@ +/* + * Direct3D 12 h264 HW acceleration + * + * copyright (c) 2022-2023 Wu Jianhua + * + * 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 "config_components.h" +#include "libavutil/avassert.h" +#include "h264dec.h" +#include "h264data.h" +#include "h264_ps.h" +#include "mpegutils.h" +#include "dxva2_internal.h" +#include "d3d12va_decode.h" +#include "libavutil/hwcontext_d3d12va_internal.h" +#include + +typedef struct H264DecodePictureContext { + DXVA_PicParams_H264 pp; + DXVA_Qmatrix_H264 qm; + unsigned slice_count; + DXVA_Slice_H264_Short slice_short[MAX_SLICES]; + const uint8_t *bitstream; + unsigned bitstream_size; +} H264DecodePictureContext; + +static void fill_slice_short(DXVA_Slice_H264_Short *slice, + unsigned position, unsigned size) +{ + memset(slice, 0, sizeof(*slice)); + slice->BSNALunitDataLocation = position; + slice->SliceBytesInBuffer = size; + slice->wBadSliceChopping = 0; +} + +static int d3d12va_h264_start_frame(AVCodecContext *avctx, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + const H264Context *h = avctx->priv_data; + H264DecodePictureContext *ctx_pic = h->cur_pic_ptr->hwaccel_picture_private; + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + + if (!ctx) + return -1; + + av_assert0(ctx_pic); + + ctx->used_mask = 0; + + ff_dxva2_h264_fill_picture_parameters(avctx, (AVDXVAContext *)ctx, &ctx_pic->pp); + + ff_dxva2_h264_fill_scaling_lists(avctx, (AVDXVAContext *)ctx, &ctx_pic->qm); + + ctx_pic->slice_count = 0; + ctx_pic->bitstream_size = 0; + ctx_pic->bitstream = NULL; + + return 0; +} + +static int d3d12va_h264_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) +{ + unsigned position; + const H264Context *h = avctx->priv_data; + const H264SliceContext *sl = &h->slice_ctx[0]; + const H264Picture *current_picture = h->cur_pic_ptr; + H264DecodePictureContext *ctx_pic = current_picture->hwaccel_picture_private; + + if (ctx_pic->slice_count >= MAX_SLICES) + return AVERROR(ERANGE); + + if (!ctx_pic->bitstream) + ctx_pic->bitstream = buffer; + ctx_pic->bitstream_size += size; + + position = buffer - ctx_pic->bitstream; + fill_slice_short(&ctx_pic->slice_short[ctx_pic->slice_count], position, size); + ctx_pic->slice_count++; + + if (sl->slice_type != AV_PICTURE_TYPE_I && sl->slice_type != AV_PICTURE_TYPE_SI) + ctx_pic->pp.wBitFields &= ~(1 << 15); /* Set IntraPicFlag to 0 */ + + return 0; +} + +#define START_CODE 65536 +#define START_CODE_SIZE 3 +static int update_input_arguments(AVCodecContext *avctx, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *input_args, ID3D12Resource *buffer) +{ + const H264Context *h = avctx->priv_data; + const H264Picture *current_picture = h->cur_pic_ptr; + H264DecodePictureContext *ctx_pic = current_picture->hwaccel_picture_private; + + int i; + uint8_t *mapped_data, *mapped_ptr; + DXVA_Slice_H264_Short *slice; + D3D12_VIDEO_DECODE_FRAME_ARGUMENT *args; + + if (FAILED(ID3D12Resource_Map(buffer, 0, NULL, (void **)&mapped_data))) { + av_log(avctx, AV_LOG_ERROR, "Failed to map D3D12 Buffer resource!\n"); + return AVERROR(EINVAL); + } + + mapped_ptr = mapped_data; + for (i = 0; i < ctx_pic->slice_count; i++) { + UINT position, size; + slice = &ctx_pic->slice_short[i]; + + position = slice->BSNALunitDataLocation; + size = slice->SliceBytesInBuffer; + + slice->SliceBytesInBuffer += START_CODE_SIZE; + slice->BSNALunitDataLocation = mapped_ptr - mapped_data; + + *(uint32_t *)mapped_ptr = START_CODE; + mapped_ptr += START_CODE_SIZE; + + memcpy(mapped_ptr, &ctx_pic->bitstream[position], size); + mapped_ptr += size; + } + + ID3D12Resource_Unmap(buffer, 0, NULL); + + input_args->CompressedBitstream = (D3D12_VIDEO_DECODE_COMPRESSED_BITSTREAM){ + .pBuffer = buffer, + .Offset = 0, + .Size = mapped_ptr - mapped_data, + }; + + args = &input_args->FrameArguments[input_args->NumFrameArguments++]; + args->Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_SLICE_CONTROL; + args->Size = sizeof(DXVA_Slice_H264_Short) * ctx_pic->slice_count; + args->pData = ctx_pic->slice_short; + + return 0; +} + +static int d3d12va_h264_end_frame(AVCodecContext *avctx) +{ + H264Context *h = avctx->priv_data; + H264DecodePictureContext *ctx_pic = h->cur_pic_ptr->hwaccel_picture_private; + H264SliceContext *sl = &h->slice_ctx[0]; + + int ret; + + if (ctx_pic->slice_count <= 0 || ctx_pic->bitstream_size <= 0) + return -1; + + ret = ff_d3d12va_common_end_frame(avctx, h->cur_pic_ptr->f, + &ctx_pic->pp, sizeof(ctx_pic->pp), + &ctx_pic->qm, sizeof(ctx_pic->qm), + update_input_arguments); + if (!ret) + ff_h264_draw_horiz_band(h, sl, 0, h->avctx->height); + + return ret; +} + +static int d3d12va_h264_decode_init(AVCodecContext *avctx) +{ + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + DXVA_PicParams_H264 pp; + + ctx->cfg.DecodeProfile = D3D12_VIDEO_DECODE_PROFILE_H264; + + ctx->max_num_ref = FF_ARRAY_ELEMS(pp.RefFrameList) + 1; + + return ff_d3d12va_decode_init(avctx); +} + +#if CONFIG_H264_D3D12VA_HWACCEL +const FFHWAccel ff_h264_d3d12va_hwaccel = { + .p.name = "h264_d3d12va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_D3D12, + .init = d3d12va_h264_decode_init, + .uninit = ff_d3d12va_decode_uninit, + .start_frame = d3d12va_h264_start_frame, + .decode_slice = d3d12va_h264_decode_slice, + .end_frame = d3d12va_h264_end_frame, + .frame_params = ff_d3d12va_common_frame_params, + .frame_priv_data_size = sizeof(H264DecodePictureContext), + .priv_data_size = sizeof(D3D12VADecodeContext), +}; +#endif diff --git a/libavcodec/d3d12va_hevc.c b/libavcodec/d3d12va_hevc.c new file mode 100644 index 00000000000..a4964a05c6f --- /dev/null +++ b/libavcodec/d3d12va_hevc.c @@ -0,0 +1,208 @@ +/* + * Direct3D 12 HEVC HW acceleration + * + * copyright (c) 2022-2023 Wu Jianhua + * + * 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 "config_components.h" + +#include "libavutil/avassert.h" +#include "libavutil/hwcontext_d3d12va_internal.h" +#include "hevc_data.h" +#include "hevcdec.h" +#include "dxva2_internal.h" +#include "d3d12va_decode.h" +#include + +#define MAX_SLICES 256 + +typedef struct HEVCDecodePictureContext { + DXVA_PicParams_HEVC pp; + DXVA_Qmatrix_HEVC qm; + unsigned slice_count; + DXVA_Slice_HEVC_Short slice_short[MAX_SLICES]; + const uint8_t *bitstream; + unsigned bitstream_size; +} HEVCDecodePictureContext; + +static void fill_slice_short(DXVA_Slice_HEVC_Short *slice, unsigned position, unsigned size) +{ + memset(slice, 0, sizeof(*slice)); + slice->BSNALunitDataLocation = position; + slice->SliceBytesInBuffer = size; + slice->wBadSliceChopping = 0; +} + +static int d3d12va_hevc_start_frame(AVCodecContext *avctx, av_unused const uint8_t *buffer, av_unused uint32_t size) +{ + const HEVCContext *h = avctx->priv_data; + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + HEVCDecodePictureContext *ctx_pic = h->ref->hwaccel_picture_private; + + if (!ctx) + return -1; + + av_assert0(ctx_pic); + + ctx->used_mask = 0; + + ff_dxva2_hevc_fill_picture_parameters(avctx, (AVDXVAContext *)ctx, &ctx_pic->pp); + + ff_dxva2_hevc_fill_scaling_lists(avctx, (AVDXVAContext *)ctx, &ctx_pic->qm); + + ctx_pic->slice_count = 0; + ctx_pic->bitstream_size = 0; + ctx_pic->bitstream = NULL; + + return 0; +} + +static int d3d12va_hevc_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) +{ + const HEVCContext *h = avctx->priv_data; + const HEVCFrame *current_picture = h->ref; + HEVCDecodePictureContext *ctx_pic = current_picture->hwaccel_picture_private; + unsigned position; + + if (ctx_pic->slice_count >= MAX_SLICES) + return AVERROR(ERANGE); + + if (!ctx_pic->bitstream) + ctx_pic->bitstream = buffer; + ctx_pic->bitstream_size += size; + + position = buffer - ctx_pic->bitstream; + fill_slice_short(&ctx_pic->slice_short[ctx_pic->slice_count], position, size); + ctx_pic->slice_count++; + + return 0; +} + +#define START_CODE 65536 +#define START_CODE_SIZE 3 +static int update_input_arguments(AVCodecContext *avctx, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *input_args, ID3D12Resource *buffer) +{ + const HEVCContext *h = avctx->priv_data; + const HEVCFrame *current_picture = h->ref; + HEVCDecodePictureContext *ctx_pic = current_picture->hwaccel_picture_private; + + int i; + uint8_t *mapped_data, *mapped_ptr; + DXVA_Slice_HEVC_Short *slice; + D3D12_VIDEO_DECODE_FRAME_ARGUMENT *args; + + if (FAILED(ID3D12Resource_Map(buffer, 0, NULL, (void **)&mapped_data))) { + av_log(avctx, AV_LOG_ERROR, "Failed to map D3D12 Buffer resource!\n"); + return AVERROR(EINVAL); + } + + mapped_ptr = mapped_data; + for (i = 0; i < ctx_pic->slice_count; i++) { + UINT position, size; + slice = &ctx_pic->slice_short[i]; + + position = slice->BSNALunitDataLocation; + size = slice->SliceBytesInBuffer; + + slice->SliceBytesInBuffer += START_CODE_SIZE; + slice->BSNALunitDataLocation = mapped_ptr - mapped_data; + + *(uint32_t *)mapped_ptr = START_CODE; + mapped_ptr += START_CODE_SIZE; + + memcpy(mapped_ptr, &ctx_pic->bitstream[position], size); + mapped_ptr += size; + } + + ID3D12Resource_Unmap(buffer, 0, NULL); + + input_args->CompressedBitstream = (D3D12_VIDEO_DECODE_COMPRESSED_BITSTREAM){ + .pBuffer = buffer, + .Offset = 0, + .Size = mapped_ptr - mapped_data, + }; + + args = &input_args->FrameArguments[input_args->NumFrameArguments++]; + args->Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_SLICE_CONTROL; + args->Size = sizeof(DXVA_Slice_HEVC_Short) * ctx_pic->slice_count; + args->pData = ctx_pic->slice_short; + + return 0; +} + +static int d3d12va_hevc_end_frame(AVCodecContext *avctx) +{ + HEVCContext *h = avctx->priv_data; + HEVCDecodePictureContext *ctx_pic = h->ref->hwaccel_picture_private; + + int scale = ctx_pic->pp.dwCodingParamToolFlags & 1; + + if (ctx_pic->slice_count <= 0 || ctx_pic->bitstream_size <= 0) + return -1; + + return ff_d3d12va_common_end_frame(avctx, h->ref->frame, &ctx_pic->pp, sizeof(ctx_pic->pp), + scale ? &ctx_pic->qm : NULL, scale ? sizeof(ctx_pic->qm) : 0, update_input_arguments); +} + +static int d3d12va_hevc_decode_init(AVCodecContext *avctx) +{ + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + DXVA_PicParams_HEVC pp; + + switch (avctx->profile) { + case AV_PROFILE_HEVC_MAIN_10: + ctx->cfg.DecodeProfile = D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN10; + break; + + case AV_PROFILE_HEVC_MAIN_STILL_PICTURE: + if (avctx->hwaccel_flags & AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH) { + ctx->cfg.DecodeProfile = D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN; + break; + } else { + av_log(avctx, AV_LOG_ERROR, "D3D12 doesn't support PROFILE_HEVC_MAIN_STILL_PICTURE!\n"); + return AVERROR(EINVAL); + } + + case AV_PROFILE_HEVC_MAIN: + default: + ctx->cfg.DecodeProfile = D3D12_VIDEO_DECODE_PROFILE_HEVC_MAIN; + break; + }; + + ctx->max_num_ref = FF_ARRAY_ELEMS(pp.RefPicList) + 1; + + return ff_d3d12va_decode_init(avctx); +} + +#if CONFIG_HEVC_D3D12VA_HWACCEL +const FFHWAccel ff_hevc_d3d12va_hwaccel = { + .p.name = "hevc_d3d12va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_D3D12, + .init = d3d12va_hevc_decode_init, + .uninit = ff_d3d12va_decode_uninit, + .start_frame = d3d12va_hevc_start_frame, + .decode_slice = d3d12va_hevc_decode_slice, + .end_frame = d3d12va_hevc_end_frame, + .frame_params = ff_d3d12va_common_frame_params, + .frame_priv_data_size = sizeof(HEVCDecodePictureContext), + .priv_data_size = sizeof(D3D12VADecodeContext), +}; +#endif diff --git a/libavcodec/d3d12va_mpeg2.c b/libavcodec/d3d12va_mpeg2.c new file mode 100644 index 00000000000..936af5f86a6 --- /dev/null +++ b/libavcodec/d3d12va_mpeg2.c @@ -0,0 +1,179 @@ +/* + * Direct3D12 MPEG-2 HW acceleration + * + * copyright (c) 2022-2023 Wu Jianhua + * + * 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 "config_components.h" +#include "libavutil/avassert.h" +#include "libavutil/hwcontext_d3d12va_internal.h" +#include "mpegutils.h" +#include "mpegvideodec.h" +#include "d3d12va_decode.h" +#include "dxva2_internal.h" + +#define MAX_SLICES 1024 +#define INVALID_REF 0xffff + +typedef struct D3D12DecodePictureContext { + DXVA_PictureParameters pp; + DXVA_QmatrixData qm; + unsigned slice_count; + DXVA_SliceInfo slices[MAX_SLICES]; + const uint8_t *bitstream; + unsigned bitstream_size; +} D3D12DecodePictureContext; + +static int d3d12va_mpeg2_start_frame(AVCodecContext *avctx, av_unused const uint8_t *buffer, av_unused uint32_t size) +{ + const MpegEncContext *s = avctx->priv_data; + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + D3D12DecodePictureContext *ctx_pic = s->current_picture_ptr->hwaccel_picture_private; + + if (!ctx) + return -1; + + av_assert0(ctx_pic); + + ctx->used_mask = 0; + + ff_dxva2_mpeg2_fill_picture_parameters(avctx, (AVDXVAContext *)ctx, &ctx_pic->pp); + ff_dxva2_mpeg2_fill_quantization_matrices(avctx, (AVDXVAContext *)ctx, &ctx_pic->qm); + + // Post processing operations are not supported in D3D12 Video + ctx_pic->pp.wDeblockedPictureIndex = INVALID_REF; + + ctx_pic->bitstream = NULL; + ctx_pic->bitstream_size = 0; + ctx_pic->slice_count = 0; + + return 0; +} + +static int d3d12va_mpeg2_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) +{ + const MpegEncContext *s = avctx->priv_data; + D3D12DecodePictureContext *ctx_pic = s->current_picture_ptr->hwaccel_picture_private; + + if (ctx_pic->slice_count >= MAX_SLICES) { + return AVERROR(ERANGE); + } + + if (!ctx_pic->bitstream) + ctx_pic->bitstream = buffer; + ctx_pic->bitstream_size += size; + + ff_dxva2_mpeg2_fill_slice(avctx, &ctx_pic->slices[ctx_pic->slice_count++], + buffer - ctx_pic->bitstream, buffer, size); + + return 0; +} + +static int update_input_arguments(AVCodecContext *avctx, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *input_args, ID3D12Resource *buffer) +{ + const MpegEncContext *s = avctx->priv_data; + D3D12DecodePictureContext *ctx_pic = s->current_picture_ptr->hwaccel_picture_private; + + const int is_field = s->picture_structure != PICT_FRAME; + const unsigned mb_count = s->mb_width * (s->mb_height >> is_field); + + int i; + void *mapped_data = NULL; + D3D12_VIDEO_DECODE_FRAME_ARGUMENT *args = &input_args->FrameArguments[input_args->NumFrameArguments++]; + + D3D12_RANGE range = { + .Begin = 0, + .End = ctx_pic->bitstream_size, + }; + + if (FAILED(ID3D12Resource_Map(buffer, 0, &range, &mapped_data))) { + av_log(avctx, AV_LOG_ERROR, "Failed to map D3D12 Buffer resource!\n"); + return AVERROR(EINVAL); + } + + for (i = 0; i < ctx_pic->slice_count; i++) { + DXVA_SliceInfo *slice = &ctx_pic->slices[i]; + + if (i < ctx_pic->slice_count - 1) + slice->wNumberMBsInSlice = slice[1].wNumberMBsInSlice - slice[0].wNumberMBsInSlice; + else + slice->wNumberMBsInSlice = mb_count - slice[0].wNumberMBsInSlice; + } + + memcpy(mapped_data, ctx_pic->bitstream, ctx_pic->bitstream_size); + + ID3D12Resource_Unmap(buffer, 0, &range); + + args->Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_SLICE_CONTROL; + args->Size = sizeof(DXVA_SliceInfo) * ctx_pic->slice_count; + args->pData = ctx_pic->slices; + + input_args->CompressedBitstream = (D3D12_VIDEO_DECODE_COMPRESSED_BITSTREAM){ + .pBuffer = buffer, + .Offset = 0, + .Size = ctx_pic->bitstream_size, + }; + + return 0; +} + +static int d3d12va_mpeg2_end_frame(AVCodecContext *avctx) +{ + int ret; + MpegEncContext *s = avctx->priv_data; + D3D12DecodePictureContext *ctx_pic = s->current_picture_ptr->hwaccel_picture_private; + + if (ctx_pic->slice_count <= 0 || ctx_pic->bitstream_size <= 0) + return -1; + + ret = ff_d3d12va_common_end_frame(avctx, s->current_picture_ptr->f, &ctx_pic->pp, sizeof(ctx_pic->pp), + &ctx_pic->qm, sizeof(ctx_pic->qm), update_input_arguments); + if (!ret) + ff_mpeg_draw_horiz_band(s, 0, avctx->height); + + return ret; +} + +static int d3d12va_mpeg2_decode_init(AVCodecContext *avctx) +{ + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + + ctx->cfg.DecodeProfile = D3D12_VIDEO_DECODE_PROFILE_MPEG2; + + ctx->max_num_ref = 3; + + return ff_d3d12va_decode_init(avctx); +} + +#if CONFIG_MPEG2_D3D12VA_HWACCEL +const FFHWAccel ff_mpeg2_d3d12va_hwaccel = { + .p.name = "mpeg2_d3d12va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_D3D12, + .init = d3d12va_mpeg2_decode_init, + .uninit = ff_d3d12va_decode_uninit, + .start_frame = d3d12va_mpeg2_start_frame, + .decode_slice = d3d12va_mpeg2_decode_slice, + .end_frame = d3d12va_mpeg2_end_frame, + .frame_params = ff_d3d12va_common_frame_params, + .frame_priv_data_size = sizeof(D3D12DecodePictureContext), + .priv_data_size = sizeof(D3D12VADecodeContext), +}; +#endif diff --git a/libavcodec/d3d12va_vc1.c b/libavcodec/d3d12va_vc1.c new file mode 100644 index 00000000000..110926be829 --- /dev/null +++ b/libavcodec/d3d12va_vc1.c @@ -0,0 +1,214 @@ +/* + * Direct3D12 WMV3/VC-1 HW acceleration + * + * copyright (c) 2022-2023 Wu Jianhua + * + * 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 "config_components.h" +#include "libavutil/avassert.h" +#include "libavutil/hwcontext_d3d12va_internal.h" +#include "mpegutils.h" +#include "mpegvideodec.h" +#include "vc1.h" +#include "vc1data.h" +#include "d3d12va_decode.h" +#include "dxva2_internal.h" + +#define MAX_SLICES 1024 +#define INVALID_REF 0xffff + +typedef struct D3D12DecodePictureContext { + DXVA_PictureParameters pp; + unsigned slice_count; + DXVA_SliceInfo slices[MAX_SLICES]; + const uint8_t *bitstream; + unsigned bitstream_size; +} D3D12DecodePictureContext; + +static int d3d12va_vc1_start_frame(AVCodecContext *avctx, av_unused const uint8_t *buffer, av_unused uint32_t size) +{ + const VC1Context *v = avctx->priv_data; + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + D3D12DecodePictureContext *ctx_pic = v->s.current_picture_ptr->hwaccel_picture_private; + + if (!ctx) + return -1; + + av_assert0(ctx_pic); + + ctx->used_mask = 0; + + ff_dxva2_vc1_fill_picture_parameters(avctx, (AVDXVAContext *)ctx, &ctx_pic->pp); + ctx_pic->pp.wDeblockedPictureIndex = INVALID_REF; + + ctx_pic->bitstream = NULL; + ctx_pic->bitstream_size = 0; + ctx_pic->slice_count = 0; + + return 0; +} + +static int d3d12va_vc1_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) +{ + const VC1Context *v = avctx->priv_data; + D3D12DecodePictureContext *ctx_pic = v->s.current_picture_ptr->hwaccel_picture_private; + + if (ctx_pic->slice_count >= MAX_SLICES) { + return AVERROR(ERANGE); + } + + if (avctx->codec_id == AV_CODEC_ID_VC1 && + size >= 4 && IS_MARKER(AV_RB32(buffer))) { + buffer += 4; + size -= 4; + } + + if (!ctx_pic->bitstream) + ctx_pic->bitstream = buffer; + ctx_pic->bitstream_size += size; + + ff_dxva2_vc1_fill_slice(avctx, &ctx_pic->slices[ctx_pic->slice_count++], + buffer - ctx_pic->bitstream, size); + + return 0; +} + +static int update_input_arguments(AVCodecContext *avctx, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *input_args, ID3D12Resource *buffer) +{ + const VC1Context *v = avctx->priv_data; + const MpegEncContext *s = &v->s; + D3D12DecodePictureContext *ctx_pic = s->current_picture_ptr->hwaccel_picture_private; + D3D12_VIDEO_DECODE_FRAME_ARGUMENT *args = &input_args->FrameArguments[input_args->NumFrameArguments++]; + + const unsigned mb_count = s->mb_width * (s->mb_height >> v->field_mode); + uint8_t *mapped_data, *mapped_ptr; + + static const uint8_t start_code[] = { 0, 0, 1, 0x0d }; + + if (FAILED(ID3D12Resource_Map(buffer, 0, NULL, (void **)&mapped_data))) { + av_log(avctx, AV_LOG_ERROR, "Failed to map D3D12 Buffer resource!\n"); + return AVERROR(EINVAL); + } + + mapped_ptr = mapped_data; + for (int i = 0; i < ctx_pic->slice_count; i++) { + DXVA_SliceInfo *slice = &ctx_pic->slices[i]; + unsigned position = slice->dwSliceDataLocation; + unsigned size = slice->dwSliceBitsInBuffer / 8; + + slice->dwSliceDataLocation = mapped_ptr - mapped_data; + if (i < ctx_pic->slice_count - 1) + slice->wNumberMBsInSlice = slice[1].wNumberMBsInSlice - slice[0].wNumberMBsInSlice; + else + slice->wNumberMBsInSlice = mb_count - slice[0].wNumberMBsInSlice; + + if (avctx->codec_id == AV_CODEC_ID_VC1) { + memcpy(mapped_ptr, start_code, sizeof(start_code)); + if (i == 0 && v->second_field) + mapped_ptr[3] = 0x0c; + else if (i > 0) + mapped_ptr[3] = 0x0b; + + mapped_ptr += sizeof(start_code); + slice->dwSliceBitsInBuffer += sizeof(start_code) * 8; + } + + memcpy(mapped_ptr, &ctx_pic->bitstream[position], size); + mapped_ptr += size; + } + + ID3D12Resource_Unmap(buffer, 0, NULL); + + args->Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_SLICE_CONTROL; + args->Size = sizeof(DXVA_SliceInfo) * ctx_pic->slice_count; + args->pData = ctx_pic->slices; + + input_args->CompressedBitstream = (D3D12_VIDEO_DECODE_COMPRESSED_BITSTREAM){ + .pBuffer = buffer, + .Offset = 0, + .Size = mapped_ptr - mapped_data, + }; + + return 0; +} + +static int d3d12va_vc1_end_frame(AVCodecContext *avctx) +{ + const VC1Context *v = avctx->priv_data; + D3D12DecodePictureContext *ctx_pic = v->s.current_picture_ptr->hwaccel_picture_private; + + if (ctx_pic->slice_count <= 0 || ctx_pic->bitstream_size <= 0) + return -1; + + return ff_d3d12va_common_end_frame(avctx, v->s.current_picture_ptr->f, + &ctx_pic->pp, sizeof(ctx_pic->pp), + NULL, 0, + update_input_arguments); +} + +static int d3d12va_vc1_decode_init(AVCodecContext *avctx) +{ + int ret; + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + ctx->cfg.DecodeProfile = D3D12_VIDEO_DECODE_PROFILE_VC1_D2010; + + ctx->max_num_ref = 3; + + ret = ff_d3d12va_decode_init(avctx); + if (ret < 0) { + ctx->cfg.DecodeProfile = D3D12_VIDEO_DECODE_PROFILE_VC1; + ret = ff_d3d12va_decode_init(avctx); + } + + return ret; +} + +#if CONFIG_WMV3_D3D12VA_HWACCEL +const FFHWAccel ff_wmv3_d3d12va_hwaccel = { + .p.name = "wmv3_d3d12va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_WMV3, + .p.pix_fmt = AV_PIX_FMT_D3D12, + .init = d3d12va_vc1_decode_init, + .uninit = ff_d3d12va_decode_uninit, + .start_frame = d3d12va_vc1_start_frame, + .decode_slice = d3d12va_vc1_decode_slice, + .end_frame = d3d12va_vc1_end_frame, + .frame_params = ff_d3d12va_common_frame_params, + .frame_priv_data_size = sizeof(D3D12DecodePictureContext), + .priv_data_size = sizeof(D3D12VADecodeContext), +}; +#endif + +#if CONFIG_VC1_D3D12VA_HWACCEL +const FFHWAccel ff_vc1_d3d12va_hwaccel = { + .p.name = "vc1_d3d12va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VC1, + .p.pix_fmt = AV_PIX_FMT_D3D12, + .init = d3d12va_vc1_decode_init, + .uninit = ff_d3d12va_decode_uninit, + .start_frame = d3d12va_vc1_start_frame, + .decode_slice = d3d12va_vc1_decode_slice, + .end_frame = d3d12va_vc1_end_frame, + .frame_params = ff_d3d12va_common_frame_params, + .frame_priv_data_size = sizeof(D3D12DecodePictureContext), + .priv_data_size = sizeof(D3D12VADecodeContext), +}; +#endif diff --git a/libavcodec/d3d12va_vp9.c b/libavcodec/d3d12va_vp9.c new file mode 100644 index 00000000000..3476768e615 --- /dev/null +++ b/libavcodec/d3d12va_vp9.c @@ -0,0 +1,167 @@ +/* + * Direct3D 12 VP9 HW acceleration + * + * copyright (c) 2022-2023 Wu Jianhua + * + * 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 "config_components.h" + +#include "libavutil/avassert.h" +#include "libavutil/pixdesc.h" +#include "libavutil/hwcontext_d3d12va_internal.h" + +#include "vp9shared.h" +#include "dxva2_internal.h" +#include "d3d12va_decode.h" + +typedef struct VP9DecodePictureContext { + DXVA_PicParams_VP9 pp; + DXVA_Slice_VPx_Short slice; + const uint8_t *bitstream; + unsigned bitstream_size; +} VP9DecodePictureContext; + +static void fill_slice_short(DXVA_Slice_VPx_Short *slice, unsigned position, unsigned size) +{ + memset(slice, 0, sizeof(*slice)); + slice->BSNALunitDataLocation = position; + slice->SliceBytesInBuffer = size; + slice->wBadSliceChopping = 0; +} + +static int d3d12va_vp9_start_frame(AVCodecContext *avctx, av_unused const uint8_t *buffer, av_unused uint32_t size) +{ + const VP9SharedContext *h = avctx->priv_data; + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + VP9DecodePictureContext *ctx_pic = h->frames[CUR_FRAME].hwaccel_picture_private; + + if (!ctx) + return -1; + + av_assert0(ctx_pic); + + ctx->used_mask = 0; + + if (ff_dxva2_vp9_fill_picture_parameters(avctx, (AVDXVAContext *)ctx, &ctx_pic->pp) < 0) + return -1; + + ctx_pic->bitstream_size = 0; + ctx_pic->bitstream = NULL; + + return 0; +} + +static int d3d12va_vp9_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) +{ + const VP9SharedContext *h = avctx->priv_data; + VP9DecodePictureContext *ctx_pic = h->frames[CUR_FRAME].hwaccel_picture_private; + unsigned position; + + if (!ctx_pic->bitstream) + ctx_pic->bitstream = buffer; + ctx_pic->bitstream_size += size; + + position = buffer - ctx_pic->bitstream; + fill_slice_short(&ctx_pic->slice, position, size); + + return 0; +} + +static int update_input_arguments(AVCodecContext *avctx, D3D12_VIDEO_DECODE_INPUT_STREAM_ARGUMENTS *input_args, ID3D12Resource *buffer) +{ + const VP9SharedContext *h = avctx->priv_data; + VP9DecodePictureContext *ctx_pic = h->frames[CUR_FRAME].hwaccel_picture_private; + + void *mapped_data; + D3D12_VIDEO_DECODE_FRAME_ARGUMENT *args; + + if (FAILED(ID3D12Resource_Map(buffer, 0, NULL, &mapped_data))) { + av_log(avctx, AV_LOG_ERROR, "Failed to map D3D12 Buffer resource!\n"); + return AVERROR(EINVAL); + } + + args = &input_args->FrameArguments[input_args->NumFrameArguments++]; + args->Type = D3D12_VIDEO_DECODE_ARGUMENT_TYPE_SLICE_CONTROL; + args->Size = sizeof(ctx_pic->slice); + args->pData = &ctx_pic->slice; + + memcpy(mapped_data, ctx_pic->bitstream, ctx_pic->slice.SliceBytesInBuffer); + + ID3D12Resource_Unmap(buffer, 0, NULL); + + input_args->CompressedBitstream = (D3D12_VIDEO_DECODE_COMPRESSED_BITSTREAM){ + .pBuffer = buffer, + .Offset = 0, + .Size = ctx_pic->slice.SliceBytesInBuffer, + }; + + return 0; +} + +static int d3d12va_vp9_end_frame(AVCodecContext *avctx) +{ + VP9SharedContext *h = avctx->priv_data; + VP9DecodePictureContext *ctx_pic = h->frames[CUR_FRAME].hwaccel_picture_private; + + if (ctx_pic->bitstream_size <= 0) + return -1; + + return ff_d3d12va_common_end_frame(avctx, h->frames[CUR_FRAME].tf.f, + &ctx_pic->pp, sizeof(ctx_pic->pp), NULL, 0, update_input_arguments); +} + +static int d3d12va_vp9_decode_init(AVCodecContext *avctx) +{ + D3D12VADecodeContext *ctx = D3D12VA_DECODE_CONTEXT(avctx); + DXVA_PicParams_VP9 pp; + + switch (avctx->profile) { + case AV_PROFILE_VP9_2: + case AV_PROFILE_VP9_3: + ctx->cfg.DecodeProfile = D3D12_VIDEO_DECODE_PROFILE_VP9_10BIT_PROFILE2; + break; + + case AV_PROFILE_VP9_0: + case AV_PROFILE_VP9_1: + default: + ctx->cfg.DecodeProfile = D3D12_VIDEO_DECODE_PROFILE_VP9; + break; + }; + + ctx->max_num_ref = FF_ARRAY_ELEMS(pp.ref_frame_map) + 1; + + return ff_d3d12va_decode_init(avctx); +} + +#if CONFIG_VP9_D3D12VA_HWACCEL +const FFHWAccel ff_vp9_d3d12va_hwaccel = { + .p.name = "vp9_d3d12va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_D3D12, + .init = d3d12va_vp9_decode_init, + .uninit = ff_d3d12va_decode_uninit, + .start_frame = d3d12va_vp9_start_frame, + .decode_slice = d3d12va_vp9_decode_slice, + .end_frame = d3d12va_vp9_end_frame, + .frame_params = ff_d3d12va_common_frame_params, + .frame_priv_data_size = sizeof(VP9DecodePictureContext), + .priv_data_size = sizeof(D3D12VADecodeContext), +}; +#endif diff --git a/libavcodec/dca_core.c b/libavcodec/dca_core.c index c50d005a56b..697fc742952 100644 --- a/libavcodec/dca_core.c +++ b/libavcodec/dca_core.c @@ -769,10 +769,6 @@ static void erase_adpcm_history(DCACoreDecoder *s) for (ch = 0; ch < DCA_CHANNELS; ch++) for (band = 0; band < DCA_SUBBANDS; band++) AV_ZERO128(s->subband_samples[ch][band] - DCA_ADPCM_COEFFS); - -#ifdef FF_COPY_SWAP_ZERO_USES_MMX - emms_c(); -#endif } static int alloc_sample_buffer(DCACoreDecoder *s) @@ -836,10 +832,6 @@ static int parse_frame_data(DCACoreDecoder *s, enum HeaderType header, int xch_b } } -#ifdef FF_COPY_SWAP_ZERO_USES_MMX - emms_c(); -#endif - return 0; } @@ -1282,10 +1274,6 @@ static void erase_x96_adpcm_history(DCACoreDecoder *s) for (ch = 0; ch < DCA_CHANNELS; ch++) for (band = 0; band < DCA_SUBBANDS_X96; band++) AV_ZERO128(s->x96_subband_samples[ch][band] - DCA_ADPCM_COEFFS); - -#ifdef FF_COPY_SWAP_ZERO_USES_MMX - emms_c(); -#endif } static int alloc_x96_sample_buffer(DCACoreDecoder *s) @@ -1515,10 +1503,6 @@ static int parse_x96_frame_data(DCACoreDecoder *s, int exss, int xch_base) } } -#ifdef FF_COPY_SWAP_ZERO_USES_MMX - emms_c(); -#endif - return 0; } @@ -2386,13 +2370,13 @@ int ff_dca_core_filter_frame(DCACoreDecoder *s, AVFrame *frame) // Set profile, bit rate, etc if (s->ext_audio_mask & DCA_EXSS_MASK) - avctx->profile = FF_PROFILE_DTS_HD_HRA; + avctx->profile = AV_PROFILE_DTS_HD_HRA; else if (s->ext_audio_mask & (DCA_CSS_XXCH | DCA_CSS_XCH)) - avctx->profile = FF_PROFILE_DTS_ES; + avctx->profile = AV_PROFILE_DTS_ES; else if (s->ext_audio_mask & DCA_CSS_X96) - avctx->profile = FF_PROFILE_DTS_96_24; + avctx->profile = AV_PROFILE_DTS_96_24; else - avctx->profile = FF_PROFILE_DTS; + avctx->profile = AV_PROFILE_DTS; if (s->bit_rate > 3 && !(s->ext_audio_mask & DCA_EXSS_MASK)) avctx->bit_rate = s->bit_rate; diff --git a/libavcodec/dca_lbr.c b/libavcodec/dca_lbr.c index bef0054dbed..c9c5c0f856a 100644 --- a/libavcodec/dca_lbr.c +++ b/libavcodec/dca_lbr.c @@ -1744,7 +1744,7 @@ int ff_dca_lbr_filter_frame(DCALbrDecoder *s, AVFrame *frame) avctx->sample_rate = s->sample_rate; avctx->sample_fmt = AV_SAMPLE_FMT_FLTP; avctx->bits_per_raw_sample = 0; - avctx->profile = FF_PROFILE_DTS_EXPRESS; + avctx->profile = AV_PROFILE_DTS_EXPRESS; avctx->bit_rate = s->bit_rate_scaled; if (s->flags & LBR_FLAG_LFE_PRESENT) { diff --git a/libavcodec/dca_parser.c b/libavcodec/dca_parser.c index 3148397b7dc..eb0ef55d46d 100644 --- a/libavcodec/dca_parser.c +++ b/libavcodec/dca_parser.c @@ -228,7 +228,7 @@ static int dca_parse_params(DCAParseContext *pc1, const uint8_t *buf, *sample_rate = ff_dca_sampling_freqs[pc1->sr_code]; *duration = 1024 << ff_dca_freq_ranges[pc1->sr_code]; - *profile = FF_PROFILE_DTS_EXPRESS; + *profile = AV_PROFILE_DTS_EXPRESS; return 0; } @@ -253,7 +253,7 @@ static int dca_parse_params(DCAParseContext *pc1, const uint8_t *buf, *sample_rate = asset->max_sample_rate; *duration = (1 + (*sample_rate > 96000)) << nsamples_log2; - *profile = FF_PROFILE_DTS_HD_MA; + *profile = AV_PROFILE_DTS_HD_MA; return 0; } @@ -268,18 +268,18 @@ static int dca_parse_params(DCAParseContext *pc1, const uint8_t *buf, *duration = h.npcmblocks * DCA_PCMBLOCK_SAMPLES; *sample_rate = ff_dca_sample_rates[h.sr_code]; - if (*profile != FF_PROFILE_UNKNOWN) + if (*profile != AV_PROFILE_UNKNOWN) return 0; - *profile = FF_PROFILE_DTS; + *profile = AV_PROFILE_DTS; if (h.ext_audio_present) { switch (h.ext_audio_type) { case DCA_EXT_AUDIO_XCH: case DCA_EXT_AUDIO_XXCH: - *profile = FF_PROFILE_DTS_ES; + *profile = AV_PROFILE_DTS_ES; break; case DCA_EXT_AUDIO_X96: - *profile = FF_PROFILE_DTS_96_24; + *profile = AV_PROFILE_DTS_96_24; break; } } @@ -296,9 +296,9 @@ static int dca_parse_params(DCAParseContext *pc1, const uint8_t *buf, return 0; if (asset->extension_mask & DCA_EXSS_XLL) - *profile = FF_PROFILE_DTS_HD_MA; + *profile = AV_PROFILE_DTS_HD_MA; else if (asset->extension_mask & (DCA_EXSS_XBR | DCA_EXSS_XXCH | DCA_EXSS_X96)) - *profile = FF_PROFILE_DTS_HD_HRA; + *profile = AV_PROFILE_DTS_HD_HRA; return 0; } diff --git a/libavcodec/dca_syncwords.h b/libavcodec/dca_syncwords.h index 4d2cd5f56d5..649bbd90dcf 100644 --- a/libavcodec/dca_syncwords.h +++ b/libavcodec/dca_syncwords.h @@ -33,4 +33,7 @@ #define DCA_SYNCWORD_SUBSTREAM_CORE 0x02B09261U #define DCA_SYNCWORD_REV1AUX 0x9A1105A0U +#define DCA_SYNCWORD_XLL_X 0x02000850U +#define DCA_SYNCWORD_XLL_X_IMAX 0xF14000D0U + #endif /* AVCODEC_DCA_SYNCWORDS_H */ diff --git a/libavcodec/dca_xll.c b/libavcodec/dca_xll.c index fe2c766d981..15f9bd9196c 100644 --- a/libavcodec/dca_xll.c +++ b/libavcodec/dca_xll.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "avcodec.h" #include "libavutil/channel_layout.h" #include "dcadec.h" #include "dcadata.h" @@ -1054,6 +1055,22 @@ static int parse_frame(DCAXllDecoder *s, const uint8_t *data, int size, DCAExssA return ret; if ((ret = parse_band_data(s)) < 0) return ret; + + if (s->frame_size * 8 > FFALIGN(get_bits_count(&s->gb), 32)) { + unsigned int extradata_syncword; + + // Align to dword + skip_bits_long(&s->gb, -get_bits_count(&s->gb) & 31); + + extradata_syncword = show_bits_long(&s->gb, 32); + + if (extradata_syncword == DCA_SYNCWORD_XLL_X) { + s->x_syncword_present = 1; + } else if ((extradata_syncword >> 1) == (DCA_SYNCWORD_XLL_X_IMAX >> 1)) { + s->x_imax_syncword_present = 1; + } + } + if (ff_dca_seek_bits(&s->gb, s->frame_size * 8)) { av_log(s->avctx, AV_LOG_ERROR, "Read past end of XLL frame\n"); return AVERROR_INVALIDDATA; @@ -1428,8 +1445,15 @@ int ff_dca_xll_filter_frame(DCAXllDecoder *s, AVFrame *frame) return AVERROR(EINVAL); } + if (s->x_imax_syncword_present) { + avctx->profile = AV_PROFILE_DTS_HD_MA_X_IMAX; + } else if (s->x_syncword_present) { + avctx->profile = AV_PROFILE_DTS_HD_MA_X; + } else { + avctx->profile = AV_PROFILE_DTS_HD_MA; + } + avctx->bits_per_raw_sample = p->storage_bit_res; - avctx->profile = FF_PROFILE_DTS_HD_MA; avctx->bit_rate = 0; frame->nb_samples = nsamples = s->nframesamples << (s->nfreqbands - 1); diff --git a/libavcodec/dca_xll.h b/libavcodec/dca_xll.h index d7c1a13ec86..a22bbb8d770 100644 --- a/libavcodec/dca_xll.h +++ b/libavcodec/dca_xll.h @@ -135,6 +135,9 @@ typedef struct DCAXllDecoder { DCADSPContext *dcadsp; + int x_syncword_present; ///< Syncword for extension data at end of frame (DTS:X) is present + int x_imax_syncword_present; ///< Syncword for extension data at end of frame (DTS:X IMAX) is present + int output_mask; int32_t *output_samples[DCA_SPEAKER_COUNT]; } DCAXllDecoder; diff --git a/libavcodec/dcadec.c b/libavcodec/dcadec.c index 3e3e3053bbe..8a62d3b8704 100644 --- a/libavcodec/dcadec.c +++ b/libavcodec/dcadec.c @@ -217,11 +217,10 @@ static int dcadec_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (asset && (asset->extension_mask & DCA_EXSS_XLL)) { if ((ret = ff_dca_xll_parse(&s->xll, input, asset)) < 0) { // Conceal XLL synchronization error - if (ret == AVERROR(EAGAIN) - && (prev_packet & DCA_PACKET_XLL) - && (s->packet & DCA_PACKET_CORE)) - s->packet |= DCA_PACKET_XLL | DCA_PACKET_RECOVERY; - else if (ret == AVERROR(ENOMEM) || (avctx->err_recognition & AV_EF_EXPLODE)) + if (ret == AVERROR(EAGAIN)) { + if ((prev_packet & DCA_PACKET_XLL) && (s->packet & DCA_PACKET_CORE)) + s->packet |= DCA_PACKET_XLL | DCA_PACKET_RECOVERY; + } else if (ret == AVERROR(ENOMEM) || (avctx->err_recognition & AV_EF_EXPLODE)) return ret; } else { s->packet |= DCA_PACKET_XLL; @@ -353,26 +352,21 @@ static av_cold int dcadec_init(AVCodecContext *avctx) s->crctab = av_crc_get_table(AV_CRC_16_CCITT); -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (avctx->request_channel_layout & AV_CH_LAYOUT_NATIVE) - s->output_channel_order = CHANNEL_ORDER_CODED; - - if (avctx->request_channel_layout & ~AV_CH_LAYOUT_NATIVE) { - av_channel_layout_uninit(&s->downmix_layout); - av_channel_layout_from_mask(&s->downmix_layout, avctx->request_channel_layout & ~AV_CH_LAYOUT_NATIVE); - } -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (s->downmix_layout.nb_channels) { if (!av_channel_layout_compare(&s->downmix_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO) || - !av_channel_layout_compare(&s->downmix_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO_DOWNMIX)) + !av_channel_layout_compare(&s->downmix_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO_DOWNMIX)) { s->request_channel_layout = DCA_SPEAKER_LAYOUT_STEREO; - else if (!av_channel_layout_compare(&s->downmix_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT0)) + av_channel_layout_uninit(&avctx->ch_layout); + avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO; + } else if (!av_channel_layout_compare(&s->downmix_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT0)) { s->request_channel_layout = DCA_SPEAKER_LAYOUT_5POINT0; - else if (!av_channel_layout_compare(&s->downmix_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1)) + av_channel_layout_uninit(&avctx->ch_layout); + avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT0; + } else if (!av_channel_layout_compare(&s->downmix_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1)) { s->request_channel_layout = DCA_SPEAKER_LAYOUT_5POINT1; + av_channel_layout_uninit(&avctx->ch_layout); + avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1; + } else av_log(avctx, AV_LOG_WARNING, "Invalid downmix layout\n"); } @@ -390,11 +384,11 @@ static const AVOption dcadec_options[] = { { "channel_order", "Order in which the channels are to be exported", OFFSET(output_channel_order), AV_OPT_TYPE_INT, - { .i64 = CHANNEL_ORDER_DEFAULT }, 0, 1, PARAM, "channel_order" }, + { .i64 = CHANNEL_ORDER_DEFAULT }, 0, 1, PARAM, .unit = "channel_order" }, { "default", "normal libavcodec channel order", 0, AV_OPT_TYPE_CONST, - { .i64 = CHANNEL_ORDER_DEFAULT }, .flags = PARAM, "channel_order" }, + { .i64 = CHANNEL_ORDER_DEFAULT }, .flags = PARAM, .unit = "channel_order" }, { "coded", "order in which the channels are coded in the bitstream", - 0, AV_OPT_TYPE_CONST, { .i64 = CHANNEL_ORDER_CODED }, .flags = PARAM, "channel_order" }, + 0, AV_OPT_TYPE_CONST, { .i64 = CHANNEL_ORDER_CODED }, .flags = PARAM, .unit = "channel_order" }, { "downmix", "Request a specific channel layout from the decoder", OFFSET(downmix_layout), AV_OPT_TYPE_CHLAYOUT, {.str = NULL}, .flags = PARAM }, diff --git a/libavcodec/dcaenc.c b/libavcodec/dcaenc.c index c731d79381c..192121524b3 100644 --- a/libavcodec/dcaenc.c +++ b/libavcodec/dcaenc.c @@ -1325,9 +1325,6 @@ const FFCodec ff_dca_encoder = { .p.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_NONE }, .p.supported_samplerates = sample_rates, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO, - AV_CH_LAYOUT_2_2, AV_CH_LAYOUT_5POINT0, - AV_CH_LAYOUT_5POINT1) .p.ch_layouts = (const AVChannelLayout[]){ AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, diff --git a/libavcodec/dcahuff.c b/libavcodec/dcahuff.c index af3a6e5326b..7c4b1d8251f 100644 --- a/libavcodec/dcahuff.c +++ b/libavcodec/dcahuff.c @@ -796,9 +796,9 @@ av_cold void ff_dca_init_vlcs(void) do { \ vlc.table = &dca_table[offset]; \ vlc.table_allocated = FF_ARRAY_ELEMS(dca_table) - offset; \ - ff_init_vlc_from_lengths(&vlc, nb_bits, nb_codes, &src_table[0][1], 2, \ + ff_vlc_init_from_lengths(&vlc, nb_bits, nb_codes, &src_table[0][1], 2, \ &src_table[0][0], 2, 1, entry_offset, \ - INIT_VLC_STATIC_OVERLONG, NULL); \ + VLC_INIT_STATIC_OVERLONG, NULL); \ offset += vlc.table_size; \ src_table += nb_codes; \ } while (0) @@ -822,9 +822,9 @@ av_cold void ff_dca_init_vlcs(void) do { \ vlc.table = &dca_table[offset]; \ vlc.table_allocated = FF_ARRAY_ELEMS(dca_table) - offset; \ - ff_init_vlc_from_lengths(&vlc, nb_bits, nb_codes, &src_table[0][1], 2, \ + ff_vlc_init_from_lengths(&vlc, nb_bits, nb_codes, &src_table[0][1], 2, \ &src_table[0][0], 2, 1, entry_offset, \ - INIT_VLC_STATIC_OVERLONG | INIT_VLC_LE,\ + VLC_INIT_STATIC_OVERLONG | VLC_INIT_LE,\ NULL); \ offset += vlc.table_size; \ src_table += nb_codes; \ diff --git a/libavcodec/dct.c b/libavcodec/dct.c deleted file mode 100644 index eeb4d154e08..00000000000 --- a/libavcodec/dct.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * (I)DCT Transforms - * Copyright (c) 2009 Peter Ross - * Copyright (c) 2010 Alex Converse - * Copyright (c) 2010 Vitor Sessak - * - * 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 St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/** - * @file - * (Inverse) Discrete Cosine Transforms. These are also known as the - * type II and type III DCTs respectively. - */ - -#include -#include - -#include "libavutil/error.h" -#include "libavutil/mathematics.h" -#include "libavutil/mem.h" -#include "dct.h" -#include "dct32.h" - -/* sin((M_PI * x / (2 * n)) */ -#define SIN(s, n, x) (s->costab[(n) - (x)]) - -/* cos((M_PI * x / (2 * n)) */ -#define COS(s, n, x) (s->costab[x]) - -static void dst_calc_I_c(DCTContext *ctx, FFTSample *data) -{ - int n = 1 << ctx->nbits; - int i; - - data[0] = 0; - for (i = 1; i < n / 2; i++) { - float tmp1 = data[i ]; - float tmp2 = data[n - i]; - float s = SIN(ctx, n, 2 * i); - - s *= tmp1 + tmp2; - tmp1 = (tmp1 - tmp2) * 0.5f; - data[i] = s + tmp1; - data[n - i] = s - tmp1; - } - - data[n / 2] *= 2; - ctx->rdft.rdft_calc(&ctx->rdft, data); - - data[0] *= 0.5f; - - for (i = 1; i < n - 2; i += 2) { - data[i + 1] += data[i - 1]; - data[i] = -data[i + 2]; - } - - data[n - 1] = 0; -} - -static void dct_calc_I_c(DCTContext *ctx, FFTSample *data) -{ - int n = 1 << ctx->nbits; - int i; - float next = -0.5f * (data[0] - data[n]); - - for (i = 0; i < n / 2; i++) { - float tmp1 = data[i]; - float tmp2 = data[n - i]; - float s = SIN(ctx, n, 2 * i); - float c = COS(ctx, n, 2 * i); - - c *= tmp1 - tmp2; - s *= tmp1 - tmp2; - - next += c; - - tmp1 = (tmp1 + tmp2) * 0.5f; - data[i] = tmp1 - s; - data[n - i] = tmp1 + s; - } - - ctx->rdft.rdft_calc(&ctx->rdft, data); - data[n] = data[1]; - data[1] = next; - - for (i = 3; i <= n; i += 2) - data[i] = data[i - 2] - data[i]; -} - -static void dct_calc_III_c(DCTContext *ctx, FFTSample *data) -{ - int n = 1 << ctx->nbits; - int i; - - float next = data[n - 1]; - float inv_n = 1.0f / n; - - for (i = n - 2; i >= 2; i -= 2) { - float val1 = data[i]; - float val2 = data[i - 1] - data[i + 1]; - float c = COS(ctx, n, i); - float s = SIN(ctx, n, i); - - data[i] = c * val1 + s * val2; - data[i + 1] = s * val1 - c * val2; - } - - data[1] = 2 * next; - - ctx->rdft.rdft_calc(&ctx->rdft, data); - - for (i = 0; i < n / 2; i++) { - float tmp1 = data[i] * inv_n; - float tmp2 = data[n - i - 1] * inv_n; - float csc = ctx->csc2[i] * (tmp1 - tmp2); - - tmp1 += tmp2; - data[i] = tmp1 + csc; - data[n - i - 1] = tmp1 - csc; - } -} - -static void dct_calc_II_c(DCTContext *ctx, FFTSample *data) -{ - int n = 1 << ctx->nbits; - int i; - float next; - - for (i = 0; i < n / 2; i++) { - float tmp1 = data[i]; - float tmp2 = data[n - i - 1]; - float s = SIN(ctx, n, 2 * i + 1); - - s *= tmp1 - tmp2; - tmp1 = (tmp1 + tmp2) * 0.5f; - - data[i] = tmp1 + s; - data[n-i-1] = tmp1 - s; - } - - ctx->rdft.rdft_calc(&ctx->rdft, data); - - next = data[1] * 0.5; - data[1] *= -1; - - for (i = n - 2; i >= 0; i -= 2) { - float inr = data[i ]; - float ini = data[i + 1]; - float c = COS(ctx, n, i); - float s = SIN(ctx, n, i); - - data[i] = c * inr + s * ini; - data[i + 1] = next; - - next += s * inr - c * ini; - } -} - -static void dct32_func(DCTContext *ctx, FFTSample *data) -{ - ctx->dct32(data, data); -} - -av_cold int ff_dct_init(DCTContext *s, int nbits, enum DCTTransformType inverse) -{ - int n = 1 << nbits; - int i; - int ret; - - memset(s, 0, sizeof(*s)); - - s->nbits = nbits; - s->inverse = inverse; - - if (inverse == DCT_II && nbits == 5) { - s->dct_calc = dct32_func; - } else { - ff_init_ff_cos_tabs(nbits + 2); - - s->costab = ff_cos_tabs[nbits + 2]; - s->csc2 = av_malloc_array(n / 2, sizeof(FFTSample)); - if (!s->csc2) - return AVERROR(ENOMEM); - - if ((ret = ff_rdft_init(&s->rdft, nbits, inverse == DCT_III)) < 0) { - av_freep(&s->csc2); - return ret; - } - - for (i = 0; i < n / 2; i++) - s->csc2[i] = 0.5 / sin((M_PI / (2 * n) * (2 * i + 1))); - - switch (inverse) { - case DCT_I : s->dct_calc = dct_calc_I_c; break; - case DCT_II : s->dct_calc = dct_calc_II_c; break; - case DCT_III: s->dct_calc = dct_calc_III_c; break; - case DST_I : s->dct_calc = dst_calc_I_c; break; - } - } - - s->dct32 = ff_dct32_float; -#if ARCH_X86 - ff_dct_init_x86(s); -#endif - - return 0; -} - -av_cold void ff_dct_end(DCTContext *s) -{ - ff_rdft_end(&s->rdft); - av_freep(&s->csc2); -} diff --git a/libavcodec/dct.h b/libavcodec/dct.h index 0a03e256d13..17c881a695d 100644 --- a/libavcodec/dct.h +++ b/libavcodec/dct.h @@ -21,44 +21,12 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#if !defined(AVCODEC_DCT_H) && (!defined(FFT_FLOAT) || FFT_FLOAT) +#ifndef AVCODEC_DCT_H #define AVCODEC_DCT_H #include #include -#include "rdft.h" - -struct DCTContext { - int nbits; - int inverse; - RDFTContext rdft; - const float *costab; - FFTSample *csc2; - void (*dct_calc)(struct DCTContext *s, FFTSample *data); - void (*dct32)(FFTSample *out, const FFTSample *in); -}; - -/** - * Set up DCT. - * @param nbits size of the input array: - * (1 << nbits) for DCT-II, DCT-III and DST-I - * (1 << nbits) + 1 for DCT-I - * - * @note the first element of the input of DST-I is ignored - */ -int ff_dct_init(DCTContext *s, int nbits, enum DCTTransformType type); -void ff_dct_end (DCTContext *s); - -void ff_dct_init_x86(DCTContext *s); - -void ff_fdct_ifast(int16_t *data); -void ff_fdct_ifast248(int16_t *data); -void ff_jpeg_fdct_islow_8(int16_t *data); -void ff_jpeg_fdct_islow_10(int16_t *data); -void ff_fdct248_islow_8(int16_t *data); -void ff_fdct248_islow_10(int16_t *data); - void ff_j_rev_dct(int16_t *data); void ff_j_rev_dct4(int16_t *data); void ff_j_rev_dct2(int16_t *data); diff --git a/libavcodec/dds.c b/libavcodec/dds.c index 4bb425dbb3c..89cf225f25c 100644 --- a/libavcodec/dds.c +++ b/libavcodec/dds.c @@ -636,7 +636,9 @@ static int dds_decode(AVCodecContext *avctx, AVFrame *frame, ctx->dec.tex_data.in = gbc->buffer; ctx->dec.frame_data.out = frame->data[0]; ctx->dec.stride = frame->linesize[0]; - avctx->execute2(avctx, ff_texturedsp_decompress_thread, &ctx->dec, NULL, ctx->dec.slice_count); + ctx->dec.width = avctx->coded_width; + ctx->dec.height = avctx->coded_height; + ff_texturedsp_exec_decompress_threads(avctx, &ctx->dec); } else if (!ctx->paletted && ctx->bpp == 4 && avctx->pix_fmt == AV_PIX_FMT_PAL8) { uint8_t *dst = frame->data[0]; int x, y, i; @@ -651,7 +653,11 @@ static int dds_decode(AVCodecContext *avctx, AVFrame *frame, ((unsigned)frame->data[1][3+i*4]<<24) ); } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (bytestream2_get_bytes_left(gbc) < frame->height * frame->width / 2) { av_log(avctx, AV_LOG_ERROR, "Buffer is too small (%d < %d).\n", @@ -682,7 +688,11 @@ static int dds_decode(AVCodecContext *avctx, AVFrame *frame, ((unsigned)frame->data[1][3+i*4]<<24) ); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } if (bytestream2_get_bytes_left(gbc) < frame->height * linesize) { @@ -702,7 +712,7 @@ static int dds_decode(AVCodecContext *avctx, AVFrame *frame, /* Frame is ready to be output. */ frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/decode.c b/libavcodec/decode.c index d1ba7f167fa..ac18544b2ed 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -28,27 +28,57 @@ #endif #include "libavutil/avassert.h" -#include "libavutil/avstring.h" -#include "libavutil/bprint.h" #include "libavutil/channel_layout.h" #include "libavutil/common.h" -#include "libavutil/fifo.h" +#include "libavutil/emms.h" #include "libavutil/frame.h" #include "libavutil/hwcontext.h" #include "libavutil/imgutils.h" #include "libavutil/internal.h" -#include "libavutil/intmath.h" -#include "libavutil/opt.h" +#include "libavutil/mastering_display_metadata.h" #include "avcodec.h" +#include "avcodec_internal.h" #include "bytestream.h" #include "bsf.h" +#include "codec_desc.h" #include "codec_internal.h" #include "decode.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "internal.h" +#include "packet_internal.h" +#include "refstruct.h" #include "thread.h" +typedef struct DecodeContext { + AVCodecInternal avci; + + /* to prevent infinite loop on errors when draining */ + int nb_draining_errors; + + /** + * The caller has submitted a NULL packet on input. + */ + int draining_started; + + int64_t pts_correction_num_faulty_pts; /// Number of incorrect PTS values so far + int64_t pts_correction_num_faulty_dts; /// Number of incorrect DTS values so far + int64_t pts_correction_last_pts; /// PTS of the last frame + int64_t pts_correction_last_dts; /// DTS of the last frame + + /** + * Bitmask indicating for which side data types we prefer user-supplied + * (global or attached to packets) side data over bytestream. + */ + uint64_t side_data_pref_mask; +} DecodeContext; + +static DecodeContext *decode_ctx(AVCodecInternal *avci) +{ + return (DecodeContext *)avci; +} + static int apply_param_change(AVCodecContext *avctx, const AVPacket *avpkt) { int ret; @@ -74,28 +104,6 @@ static int apply_param_change(AVCodecContext *avctx, const AVPacket *avpkt) flags = bytestream_get_le32(&data); size -= 4; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT) { - if (size < 4) - goto fail; - val = bytestream_get_le32(&data); - if (val <= 0 || val > INT_MAX) { - av_log(avctx, AV_LOG_ERROR, "Invalid channel count"); - ret = AVERROR_INVALIDDATA; - goto fail2; - } - avctx->channels = val; - size -= 4; - } - if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT) { - if (size < 8) - goto fail; - avctx->channel_layout = bytestream_get_le64(&data); - size -= 8; - } -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE) { if (size < 4) goto fail; @@ -139,8 +147,10 @@ static int extract_packet_props(AVCodecInternal *avci, const AVPacket *pkt) av_packet_unref(avci->last_pkt_props); if (pkt) { ret = av_packet_copy_props(avci->last_pkt_props, pkt); +#if FF_API_FRAME_PKT if (!ret) avci->last_pkt_props->stream_index = pkt->size; // Needed for ff_decode_frame_props(). +#endif } return ret; } @@ -180,14 +190,11 @@ static int decode_bsfs_init(AVCodecContext *avctx) return ret; } -int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt) +static int decode_get_packet(AVCodecContext *avctx, AVPacket *pkt) { AVCodecInternal *avci = avctx->internal; int ret; - if (avci->draining) - return AVERROR_EOF; - ret = av_bsf_receive_packet(avci->bsf, pkt); if (ret == AVERROR_EOF) avci->draining = 1; @@ -210,6 +217,31 @@ int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt) return ret; } +int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt) +{ + AVCodecInternal *avci = avctx->internal; + DecodeContext *dc = decode_ctx(avci); + + if (avci->draining) + return AVERROR_EOF; + + while (1) { + int ret = decode_get_packet(avctx, pkt); + if (ret == AVERROR(EAGAIN) && + (!AVPACKET_IS_EMPTY(avci->buffer_pkt) || dc->draining_started)) { + ret = av_bsf_send_packet(avci->bsf, avci->buffer_pkt); + if (ret < 0) { + av_packet_unref(avci->buffer_pkt); + return ret; + } + + continue; + } + + return ret; + } +} + /** * Attempt to guess proper monotonic timestamps for decoded video frames * which might have incorrect times. Input timestamps may wrap around, in @@ -220,24 +252,24 @@ int ff_decode_get_packet(AVCodecContext *avctx, AVPacket *pkt) * @param dts the dts field of the decoded AVPacket * @return one of the input values, may be AV_NOPTS_VALUE */ -static int64_t guess_correct_pts(AVCodecContext *ctx, +static int64_t guess_correct_pts(DecodeContext *dc, int64_t reordered_pts, int64_t dts) { int64_t pts = AV_NOPTS_VALUE; if (dts != AV_NOPTS_VALUE) { - ctx->pts_correction_num_faulty_dts += dts <= ctx->pts_correction_last_dts; - ctx->pts_correction_last_dts = dts; + dc->pts_correction_num_faulty_dts += dts <= dc->pts_correction_last_dts; + dc->pts_correction_last_dts = dts; } else if (reordered_pts != AV_NOPTS_VALUE) - ctx->pts_correction_last_dts = reordered_pts; + dc->pts_correction_last_dts = reordered_pts; if (reordered_pts != AV_NOPTS_VALUE) { - ctx->pts_correction_num_faulty_pts += reordered_pts <= ctx->pts_correction_last_pts; - ctx->pts_correction_last_pts = reordered_pts; + dc->pts_correction_num_faulty_pts += reordered_pts <= dc->pts_correction_last_pts; + dc->pts_correction_last_pts = reordered_pts; } else if(dts != AV_NOPTS_VALUE) - ctx->pts_correction_last_pts = dts; + dc->pts_correction_last_pts = dts; - if ((ctx->pts_correction_num_faulty_pts<=ctx->pts_correction_num_faulty_dts || dts == AV_NOPTS_VALUE) + if ((dc->pts_correction_num_faulty_pts<=dc->pts_correction_num_faulty_dts || dts == AV_NOPTS_VALUE) && reordered_pts != AV_NOPTS_VALUE) pts = reordered_pts; else @@ -246,6 +278,98 @@ static int64_t guess_correct_pts(AVCodecContext *ctx, return pts; } +static int discard_samples(AVCodecContext *avctx, AVFrame *frame, int64_t *discarded_samples) +{ + AVCodecInternal *avci = avctx->internal; + AVFrameSideData *side; + uint32_t discard_padding = 0; + uint8_t skip_reason = 0; + uint8_t discard_reason = 0; + + side = av_frame_get_side_data(frame, AV_FRAME_DATA_SKIP_SAMPLES); + if (side && side->size >= 10) { + avci->skip_samples = AV_RL32(side->data); + avci->skip_samples = FFMAX(0, avci->skip_samples); + discard_padding = AV_RL32(side->data + 4); + av_log(avctx, AV_LOG_DEBUG, "skip %d / discard %d samples due to side data\n", + avci->skip_samples, (int)discard_padding); + skip_reason = AV_RL8(side->data + 8); + discard_reason = AV_RL8(side->data + 9); + } + + if ((avctx->flags2 & AV_CODEC_FLAG2_SKIP_MANUAL)) { + if (!side && (avci->skip_samples || discard_padding)) + side = av_frame_new_side_data(frame, AV_FRAME_DATA_SKIP_SAMPLES, 10); + if (side && (avci->skip_samples || discard_padding)) { + AV_WL32(side->data, avci->skip_samples); + AV_WL32(side->data + 4, discard_padding); + AV_WL8(side->data + 8, skip_reason); + AV_WL8(side->data + 9, discard_reason); + avci->skip_samples = 0; + } + return 0; + } + av_frame_remove_side_data(frame, AV_FRAME_DATA_SKIP_SAMPLES); + + if ((frame->flags & AV_FRAME_FLAG_DISCARD)) { + avci->skip_samples = FFMAX(0, avci->skip_samples - frame->nb_samples); + *discarded_samples += frame->nb_samples; + return AVERROR(EAGAIN); + } + + if (avci->skip_samples > 0) { + if (frame->nb_samples <= avci->skip_samples){ + *discarded_samples += frame->nb_samples; + avci->skip_samples -= frame->nb_samples; + av_log(avctx, AV_LOG_DEBUG, "skip whole frame, skip left: %d\n", + avci->skip_samples); + return AVERROR(EAGAIN); + } else { + av_samples_copy(frame->extended_data, frame->extended_data, 0, avci->skip_samples, + frame->nb_samples - avci->skip_samples, avctx->ch_layout.nb_channels, frame->format); + if (avctx->pkt_timebase.num && avctx->sample_rate) { + int64_t diff_ts = av_rescale_q(avci->skip_samples, + (AVRational){1, avctx->sample_rate}, + avctx->pkt_timebase); + if (frame->pts != AV_NOPTS_VALUE) + frame->pts += diff_ts; + if (frame->pkt_dts != AV_NOPTS_VALUE) + frame->pkt_dts += diff_ts; + if (frame->duration >= diff_ts) + frame->duration -= diff_ts; + } else + av_log(avctx, AV_LOG_WARNING, "Could not update timestamps for skipped samples.\n"); + + av_log(avctx, AV_LOG_DEBUG, "skip %d/%d samples\n", + avci->skip_samples, frame->nb_samples); + *discarded_samples += avci->skip_samples; + frame->nb_samples -= avci->skip_samples; + avci->skip_samples = 0; + } + } + + if (discard_padding > 0 && discard_padding <= frame->nb_samples) { + if (discard_padding == frame->nb_samples) { + *discarded_samples += frame->nb_samples; + return AVERROR(EAGAIN); + } else { + if (avctx->pkt_timebase.num && avctx->sample_rate) { + int64_t diff_ts = av_rescale_q(frame->nb_samples - discard_padding, + (AVRational){1, avctx->sample_rate}, + avctx->pkt_timebase); + frame->duration = diff_ts; + } else + av_log(avctx, AV_LOG_WARNING, "Could not update timestamps for discarded samples.\n"); + + av_log(avctx, AV_LOG_DEBUG, "discard %d/%d samples\n", + (int)discard_padding, frame->nb_samples); + frame->nb_samples -= discard_padding; + } + } + + return 0; +} + /* * The core of the receive_frame_wrapper for the decoders implementing * the simple API. Certain decoders might consume partial packets without @@ -257,7 +381,7 @@ static inline int decode_simple_internal(AVCodecContext *avctx, AVFrame *frame, AVCodecInternal *avci = avctx->internal; AVPacket *const pkt = avci->in_pkt; const FFCodec *const codec = ffcodec(avctx->codec); - int got_frame, actual_got_frame; + int got_frame, consumed; int ret; if (!pkt->data && !avci->draining) { @@ -280,166 +404,58 @@ static inline int decode_simple_internal(AVCodecContext *avctx, AVFrame *frame, got_frame = 0; if (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME) { - ret = ff_thread_decode_frame(avctx, frame, &got_frame, pkt); + consumed = ff_thread_decode_frame(avctx, frame, &got_frame, pkt); } else { - ret = codec->cb.decode(avctx, frame, &got_frame, pkt); + consumed = codec->cb.decode(avctx, frame, &got_frame, pkt); if (!(codec->caps_internal & FF_CODEC_CAP_SETS_PKT_DTS)) frame->pkt_dts = pkt->dts; if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) { +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS if(!avctx->has_b_frames) frame->pkt_pos = pkt->pos; - //FIXME these should be under if(!avctx->has_b_frames) - /* get_buffer is supposed to set frame parameters */ - if (!(avctx->codec->capabilities & AV_CODEC_CAP_DR1)) { - if (!frame->sample_aspect_ratio.num) frame->sample_aspect_ratio = avctx->sample_aspect_ratio; - if (!frame->width) frame->width = avctx->width; - if (!frame->height) frame->height = avctx->height; - if (frame->format == AV_PIX_FMT_NONE) frame->format = avctx->pix_fmt; - } +FF_ENABLE_DEPRECATION_WARNINGS +#endif } } emms_c(); - actual_got_frame = got_frame; if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) { - if (frame->flags & AV_FRAME_FLAG_DISCARD) - got_frame = 0; + ret = (!got_frame || frame->flags & AV_FRAME_FLAG_DISCARD) + ? AVERROR(EAGAIN) + : 0; } else if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) { - uint8_t *side; - size_t side_size; - uint32_t discard_padding = 0; - uint8_t skip_reason = 0; - uint8_t discard_reason = 0; - - if (ret >= 0 && got_frame) { - if (frame->format == AV_SAMPLE_FMT_NONE) - frame->format = avctx->sample_fmt; - if (!frame->ch_layout.nb_channels) { - int ret2 = av_channel_layout_copy(&frame->ch_layout, &avctx->ch_layout); - if (ret2 < 0) { - ret = ret2; - got_frame = 0; - } - } -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (!frame->channel_layout) - frame->channel_layout = avctx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ? - avctx->ch_layout.u.mask : 0; - if (!frame->channels) - frame->channels = avctx->ch_layout.nb_channels; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (!frame->sample_rate) - frame->sample_rate = avctx->sample_rate; - } - - side= av_packet_get_side_data(avci->last_pkt_props, AV_PKT_DATA_SKIP_SAMPLES, &side_size); - if(side && side_size>=10) { - avci->skip_samples = AV_RL32(side); - avci->skip_samples = FFMAX(0, avci->skip_samples); - discard_padding = AV_RL32(side + 4); - av_log(avctx, AV_LOG_DEBUG, "skip %d / discard %d samples due to side data\n", - avci->skip_samples, (int)discard_padding); - skip_reason = AV_RL8(side + 8); - discard_reason = AV_RL8(side + 9); - } - - if ((frame->flags & AV_FRAME_FLAG_DISCARD) && got_frame && - !(avctx->flags2 & AV_CODEC_FLAG2_SKIP_MANUAL)) { - avci->skip_samples = FFMAX(0, avci->skip_samples - frame->nb_samples); - got_frame = 0; - *discarded_samples += frame->nb_samples; - } - - if (avci->skip_samples > 0 && got_frame && - !(avctx->flags2 & AV_CODEC_FLAG2_SKIP_MANUAL)) { - if(frame->nb_samples <= avci->skip_samples){ - got_frame = 0; - *discarded_samples += frame->nb_samples; - avci->skip_samples -= frame->nb_samples; - av_log(avctx, AV_LOG_DEBUG, "skip whole frame, skip left: %d\n", - avci->skip_samples); - } else { - av_samples_copy(frame->extended_data, frame->extended_data, 0, avci->skip_samples, - frame->nb_samples - avci->skip_samples, avctx->ch_layout.nb_channels, frame->format); - if(avctx->pkt_timebase.num && avctx->sample_rate) { - int64_t diff_ts = av_rescale_q(avci->skip_samples, - (AVRational){1, avctx->sample_rate}, - avctx->pkt_timebase); - if(frame->pts!=AV_NOPTS_VALUE) - frame->pts += diff_ts; - if(frame->pkt_dts!=AV_NOPTS_VALUE) - frame->pkt_dts += diff_ts; - if (frame->duration >= diff_ts) - frame->duration -= diff_ts; - } else { - av_log(avctx, AV_LOG_WARNING, "Could not update timestamps for skipped samples.\n"); - } - av_log(avctx, AV_LOG_DEBUG, "skip %d/%d samples\n", - avci->skip_samples, frame->nb_samples); - *discarded_samples += avci->skip_samples; - frame->nb_samples -= avci->skip_samples; - avci->skip_samples = 0; - } - } - - if (discard_padding > 0 && discard_padding <= frame->nb_samples && got_frame && - !(avctx->flags2 & AV_CODEC_FLAG2_SKIP_MANUAL)) { - if (discard_padding == frame->nb_samples) { - *discarded_samples += frame->nb_samples; - got_frame = 0; - } else { - if(avctx->pkt_timebase.num && avctx->sample_rate) { - int64_t diff_ts = av_rescale_q(frame->nb_samples - discard_padding, - (AVRational){1, avctx->sample_rate}, - avctx->pkt_timebase); - frame->duration = diff_ts; - } else { - av_log(avctx, AV_LOG_WARNING, "Could not update timestamps for discarded samples.\n"); - } - av_log(avctx, AV_LOG_DEBUG, "discard %d/%d samples\n", - (int)discard_padding, frame->nb_samples); - frame->nb_samples -= discard_padding; - } - } - - if ((avctx->flags2 & AV_CODEC_FLAG2_SKIP_MANUAL) && got_frame) { - AVFrameSideData *fside = av_frame_new_side_data(frame, AV_FRAME_DATA_SKIP_SAMPLES, 10); - if (fside) { - AV_WL32(fside->data, avci->skip_samples); - AV_WL32(fside->data + 4, discard_padding); - AV_WL8(fside->data + 8, skip_reason); - AV_WL8(fside->data + 9, discard_reason); - avci->skip_samples = 0; - } - } - } - - if (avctx->codec->type == AVMEDIA_TYPE_AUDIO && - !avci->showed_multi_packet_warning && - ret >= 0 && ret != pkt->size && !(avctx->codec->capabilities & AV_CODEC_CAP_SUBFRAMES)) { - av_log(avctx, AV_LOG_WARNING, "Multiple frames in a packet.\n"); - avci->showed_multi_packet_warning = 1; - } + ret = !got_frame ? AVERROR(EAGAIN) + : discard_samples(avctx, frame, discarded_samples); + } else + av_assert0(0); - if (!got_frame) + if (ret == AVERROR(EAGAIN)) av_frame_unref(frame); - if (ret >= 0 && avctx->codec->type == AVMEDIA_TYPE_VIDEO) - ret = pkt->size; + // FF_CODEC_CB_TYPE_DECODE decoders must not return AVERROR EAGAIN + // code later will add AVERROR(EAGAIN) to a pointer + av_assert0(consumed != AVERROR(EAGAIN)); + if (consumed < 0) + ret = consumed; + if (consumed >= 0 && avctx->codec->type == AVMEDIA_TYPE_VIDEO) + consumed = pkt->size; - /* do not stop draining when actual_got_frame != 0 or ret < 0 */ - /* got_frame == 0 but actual_got_frame != 0 when frame is discarded */ - if (avci->draining && !actual_got_frame) { + if (!ret) + av_assert0(frame->buf[0]); + if (ret == AVERROR(EAGAIN)) + ret = 0; + + /* do not stop draining when got_frame != 0 or ret < 0 */ + if (avci->draining && !got_frame) { if (ret < 0) { /* prevent infinite loop if a decoder wrongly always return error on draining */ /* reasonable nb_errors_max = maximum b frames + thread count */ int nb_errors_max = 20 + (HAVE_THREADS && avctx->active_thread_type & FF_THREAD_FRAME ? avctx->thread_count : 1); - if (avci->nb_draining_errors++ >= nb_errors_max) { + if (decode_ctx(avci)->nb_draining_errors++ >= nb_errors_max) { av_log(avctx, AV_LOG_ERROR, "Too many errors when draining, this is a bug. " "Stop draining and force EOF.\n"); avci->draining_done = 1; @@ -450,27 +466,24 @@ FF_ENABLE_DEPRECATION_WARNINGS } } - if (ret >= pkt->size || ret < 0) { + if (consumed >= pkt->size || ret < 0) { av_packet_unref(pkt); } else { - int consumed = ret; - pkt->data += consumed; pkt->size -= consumed; pkt->pts = AV_NOPTS_VALUE; pkt->dts = AV_NOPTS_VALUE; if (!(codec->caps_internal & FF_CODEC_CAP_SETS_FRAME_PROPS)) { +#if FF_API_FRAME_PKT // See extract_packet_props() comment. avci->last_pkt_props->stream_index = avci->last_pkt_props->stream_index - consumed; +#endif avci->last_pkt_props->pts = AV_NOPTS_VALUE; avci->last_pkt_props->dts = AV_NOPTS_VALUE; } } - if (got_frame) - av_assert0(frame->buf[0]); - - return ret < 0 ? ret : 0; + return ret; } #if CONFIG_LCMS2 @@ -500,7 +513,9 @@ static int detect_colorspace(AVCodecContext *avctx, AVFrame *frame) if (!profile) return AVERROR_INVALIDDATA; - ret = ff_icc_profile_read_primaries(&avci->icc, profile, &coeffs); + ret = ff_icc_profile_sanitize(&avci->icc, profile); + if (!ret) + ret = ff_icc_profile_read_primaries(&avci->icc, profile, &coeffs); if (!ret) ret = ff_icc_profile_detect_transfer(&avci->icc, profile, &trc); cmsCloseProfile(profile); @@ -521,6 +536,39 @@ static int detect_colorspace(av_unused AVCodecContext *c, av_unused AVFrame *f) } #endif +static int fill_frame_props(const AVCodecContext *avctx, AVFrame *frame) +{ + int ret; + + if (frame->color_primaries == AVCOL_PRI_UNSPECIFIED) + frame->color_primaries = avctx->color_primaries; + if (frame->color_trc == AVCOL_TRC_UNSPECIFIED) + frame->color_trc = avctx->color_trc; + if (frame->colorspace == AVCOL_SPC_UNSPECIFIED) + frame->colorspace = avctx->colorspace; + if (frame->color_range == AVCOL_RANGE_UNSPECIFIED) + frame->color_range = avctx->color_range; + if (frame->chroma_location == AVCHROMA_LOC_UNSPECIFIED) + frame->chroma_location = avctx->chroma_sample_location; + + if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) { + if (!frame->sample_aspect_ratio.num) frame->sample_aspect_ratio = avctx->sample_aspect_ratio; + if (frame->format == AV_PIX_FMT_NONE) frame->format = avctx->pix_fmt; + } else if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) { + if (frame->format == AV_SAMPLE_FMT_NONE) + frame->format = avctx->sample_fmt; + if (!frame->ch_layout.nb_channels) { + ret = av_channel_layout_copy(&frame->ch_layout, &avctx->ch_layout); + if (ret < 0) + return ret; + } + if (!frame->sample_rate) + frame->sample_rate = avctx->sample_rate; + } + + return 0; +} + static int decode_simple_receive_frame(AVCodecContext *avctx, AVFrame *frame) { int ret; @@ -540,6 +588,7 @@ static int decode_simple_receive_frame(AVCodecContext *avctx, AVFrame *frame) static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) { AVCodecInternal *avci = avctx->internal; + DecodeContext *dc = decode_ctx(avci); const FFCodec *const codec = ffcodec(avctx->codec); int ret, ok; @@ -547,6 +596,15 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) if (codec->cb_type == FF_CODEC_CB_TYPE_RECEIVE_FRAME) { ret = codec->cb.receive_frame(avctx, frame); + emms_c(); + if (!ret) { + if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) + ret = (frame->flags & AV_FRAME_FLAG_DISCARD) ? AVERROR(EAGAIN) : 0; + else if (avctx->codec->type == AVMEDIA_TYPE_AUDIO) { + int64_t discarded_samples = 0; + ret = discard_samples(avctx, frame, &discarded_samples); + } + } } else ret = decode_simple_receive_frame(avctx, frame); @@ -561,15 +619,34 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) } if (!ret) { - frame->best_effort_timestamp = guess_correct_pts(avctx, - frame->pts, - frame->pkt_dts); + if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) { + if (!frame->width) + frame->width = avctx->width; + if (!frame->height) + frame->height = avctx->height; + } else + frame->flags |= AV_FRAME_FLAG_KEY; + + ret = fill_frame_props(avctx, frame); + if (ret < 0) { + av_frame_unref(frame); + return ret; + } -#if FF_API_PKT_DURATION +#if FF_API_FRAME_KEY +FF_DISABLE_DEPRECATION_WARNINGS + frame->key_frame = !!(frame->flags & AV_FRAME_FLAG_KEY); +FF_ENABLE_DEPRECATION_WARNINGS +#endif +#if FF_API_INTERLACED_FRAME FF_DISABLE_DEPRECATION_WARNINGS - frame->pkt_duration = frame->duration; + frame->interlaced_frame = !!(frame->flags & AV_FRAME_FLAG_INTERLACED); + frame->top_field_first = !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); FF_ENABLE_DEPRECATION_WARNINGS #endif + frame->best_effort_timestamp = guess_correct_pts(dc, + frame->pts, + frame->pkt_dts); /* the only case where decode data is not set should be decoders * that do not call ff_get_buffer() */ @@ -598,31 +675,28 @@ FF_ENABLE_DEPRECATION_WARNINGS int attribute_align_arg avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt) { AVCodecInternal *avci = avctx->internal; + DecodeContext *dc = decode_ctx(avci); int ret; if (!avcodec_is_open(avctx) || !av_codec_is_decoder(avctx->codec)) return AVERROR(EINVAL); - if (avctx->internal->draining) + if (dc->draining_started) return AVERROR_EOF; if (avpkt && !avpkt->size && avpkt->data) return AVERROR(EINVAL); - av_packet_unref(avci->buffer_pkt); if (avpkt && (avpkt->data || avpkt->side_data_elems)) { + if (!AVPACKET_IS_EMPTY(avci->buffer_pkt)) + return AVERROR(EAGAIN); ret = av_packet_ref(avci->buffer_pkt, avpkt); if (ret < 0) return ret; - } - - ret = av_bsf_send_packet(avci->bsf, avci->buffer_pkt); - if (ret < 0) { - av_packet_unref(avci->buffer_pkt); - return ret; - } + } else + dc->draining_started = 1; - if (!avci->buffer_frame->buf[0]) { + if (!avci->buffer_frame->buf[0] && !dc->draining_started) { ret = decode_receive_frame_internal(avctx, avci->buffer_frame); if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) return ret; @@ -688,7 +762,7 @@ static int frame_validate(AVCodecContext *avctx, AVFrame *frame) int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame) { AVCodecInternal *avci = avctx->internal; - int ret, changed; + int ret; if (!avcodec_is_open(avctx) || !av_codec_is_decoder(avctx->codec)) return AVERROR(EINVAL); @@ -712,12 +786,8 @@ int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame) } avctx->frame_num++; -#if FF_API_AVCTX_FRAME_NUMBER -FF_DISABLE_DEPRECATION_WARNINGS - avctx->frame_number = avctx->frame_num; -FF_ENABLE_DEPRECATION_WARNINGS -#endif +#if FF_API_DROPCHANGED if (avctx->flags & AV_CODEC_FLAG_DROPCHANGED) { if (avctx->frame_num == 1) { @@ -738,7 +808,7 @@ FF_ENABLE_DEPRECATION_WARNINGS } if (avctx->frame_num > 1) { - changed = avci->initial_format != frame->format; + int changed = avci->initial_format != frame->format; switch(avctx->codec_type) { case AVMEDIA_TYPE_VIDEO: @@ -763,6 +833,7 @@ FF_ENABLE_DEPRECATION_WARNINGS } } } +#endif return 0; fail: av_frame_unref(frame); @@ -867,8 +938,8 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, } if (!avctx->codec) return AVERROR(EINVAL); - if (avctx->codec->type != AVMEDIA_TYPE_SUBTITLE) { - av_log(avctx, AV_LOG_ERROR, "Invalid media type for subtitles\n"); + if (ffcodec(avctx->codec)->cb_type != FF_CODEC_CB_TYPE_DECODE_SUB) { + av_log(avctx, AV_LOG_ERROR, "Codec not subtitle decoder\n"); return AVERROR(EINVAL); } @@ -922,11 +993,6 @@ int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, if (*got_sub_ptr) avctx->frame_num++; -#if FF_API_AVCTX_FRAME_NUMBER -FF_DISABLE_DEPRECATION_WARNINGS - avctx->frame_number = avctx->frame_num; -FF_ENABLE_DEPRECATION_WARNINGS -#endif } return ret; @@ -1053,7 +1119,7 @@ int avcodec_get_hw_frames_parameters(AVCodecContext *avctx, { AVBufferRef *frames_ref = NULL; const AVCodecHWConfigInternal *hw_config; - const AVHWAccel *hwa; + const FFHWAccel *hwa; int i, ret; for (i = 0;; i++) { @@ -1072,6 +1138,15 @@ int avcodec_get_hw_frames_parameters(AVCodecContext *avctx, if (!frames_ref) return AVERROR(ENOMEM); + if (!avctx->internal->hwaccel_priv_data) { + avctx->internal->hwaccel_priv_data = + av_mallocz(hwa->priv_data_size); + if (!avctx->internal->hwaccel_priv_data) { + av_buffer_unref(&frames_ref); + return AVERROR(ENOMEM); + } + } + ret = hwa->frame_params(avctx, frames_ref); if (ret >= 0) { AVHWFramesContext *frames_ctx = (AVHWFramesContext*)frames_ref->data; @@ -1096,33 +1171,31 @@ int avcodec_get_hw_frames_parameters(AVCodecContext *avctx, } static int hwaccel_init(AVCodecContext *avctx, - const AVCodecHWConfigInternal *hw_config) + const FFHWAccel *hwaccel) { - const AVHWAccel *hwaccel; int err; - hwaccel = hw_config->hwaccel; - if (hwaccel->capabilities & AV_HWACCEL_CODEC_CAP_EXPERIMENTAL && + if (hwaccel->p.capabilities & AV_HWACCEL_CODEC_CAP_EXPERIMENTAL && avctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { av_log(avctx, AV_LOG_WARNING, "Ignoring experimental hwaccel: %s\n", - hwaccel->name); + hwaccel->p.name); return AVERROR_PATCHWELCOME; } - if (hwaccel->priv_data_size) { + if (!avctx->internal->hwaccel_priv_data && hwaccel->priv_data_size) { avctx->internal->hwaccel_priv_data = av_mallocz(hwaccel->priv_data_size); if (!avctx->internal->hwaccel_priv_data) return AVERROR(ENOMEM); } - avctx->hwaccel = hwaccel; + avctx->hwaccel = &hwaccel->p; if (hwaccel->init) { err = hwaccel->init(avctx); if (err < 0) { av_log(avctx, AV_LOG_ERROR, "Failed setup for format %s: " "hwaccel initialisation returned error.\n", - av_get_pix_fmt_name(hw_config->public.pix_fmt)); + av_get_pix_fmt_name(hwaccel->p.pix_fmt)); av_freep(&avctx->internal->hwaccel_priv_data); avctx->hwaccel = NULL; return err; @@ -1132,10 +1205,10 @@ static int hwaccel_init(AVCodecContext *avctx, return 0; } -static void hwaccel_uninit(AVCodecContext *avctx) +void ff_hwaccel_uninit(AVCodecContext *avctx) { - if (avctx->hwaccel && avctx->hwaccel->uninit) - avctx->hwaccel->uninit(avctx); + if (FF_HW_HAS_CB(avctx, uninit)) + FF_HW_SIMPLE_CALL(avctx, uninit); av_freep(&avctx->internal->hwaccel_priv_data); @@ -1171,7 +1244,7 @@ int ff_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt) for (;;) { // Remove the previous hwaccel, if there was one. - hwaccel_uninit(avctx); + ff_hwaccel_uninit(avctx); user_choice = avctx->get_format(avctx, choices); if (user_choice == AV_PIX_FMT_NONE) { @@ -1254,9 +1327,9 @@ int ff_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt) goto try_again; } if (hw_config->hwaccel) { - av_log(avctx, AV_LOG_DEBUG, "Format %s requires hwaccel " - "initialisation.\n", desc->name); - err = hwaccel_init(avctx, hw_config); + av_log(avctx, AV_LOG_DEBUG, "Format %s requires hwaccel %s " + "initialisation.\n", desc->name, hw_config->hwaccel->p.name); + err = hwaccel_init(avctx, hw_config->hwaccel); if (err < 0) goto try_again; } @@ -1275,10 +1348,23 @@ int ff_get_format(AVCodecContext *avctx, const enum AVPixelFormat *fmt) --n; } + if (ret < 0) + ff_hwaccel_uninit(avctx); + av_freep(&choices); return ret; } +const AVPacketSideData *ff_get_coded_side_data(const AVCodecContext *avctx, + enum AVPacketSideDataType type) +{ + for (int i = 0; i < avctx->nb_coded_side_data; i++) + if (avctx->coded_side_data[i].type == type) + return &avctx->coded_side_data[i]; + + return NULL; +} + static int add_metadata_from_side_data(const AVPacket *avpkt, AVFrame *frame) { size_t size; @@ -1291,6 +1377,21 @@ static int add_metadata_from_side_data(const AVPacket *avpkt, AVFrame *frame) return av_packet_unpack_dictionary(side_metadata, size, frame_md); } +static const struct { + enum AVPacketSideDataType packet; + enum AVFrameSideDataType frame; +} sd_global_map[] = { + { AV_PKT_DATA_REPLAYGAIN , AV_FRAME_DATA_REPLAYGAIN }, + { AV_PKT_DATA_DISPLAYMATRIX, AV_FRAME_DATA_DISPLAYMATRIX }, + { AV_PKT_DATA_SPHERICAL, AV_FRAME_DATA_SPHERICAL }, + { AV_PKT_DATA_STEREO3D, AV_FRAME_DATA_STEREO3D }, + { AV_PKT_DATA_AUDIO_SERVICE_TYPE, AV_FRAME_DATA_AUDIO_SERVICE_TYPE }, + { AV_PKT_DATA_MASTERING_DISPLAY_METADATA, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA }, + { AV_PKT_DATA_CONTENT_LIGHT_LEVEL, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL }, + { AV_PKT_DATA_ICC_PROFILE, AV_FRAME_DATA_ICC_PROFILE }, + { AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT,AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT }, +}; + int ff_decode_frame_props_from_pkt(const AVCodecContext *avctx, AVFrame *frame, const AVPacket *pkt) { @@ -1298,24 +1399,34 @@ int ff_decode_frame_props_from_pkt(const AVCodecContext *avctx, enum AVPacketSideDataType packet; enum AVFrameSideDataType frame; } sd[] = { - { AV_PKT_DATA_REPLAYGAIN , AV_FRAME_DATA_REPLAYGAIN }, - { AV_PKT_DATA_DISPLAYMATRIX, AV_FRAME_DATA_DISPLAYMATRIX }, - { AV_PKT_DATA_SPHERICAL, AV_FRAME_DATA_SPHERICAL }, - { AV_PKT_DATA_STEREO3D, AV_FRAME_DATA_STEREO3D }, - { AV_PKT_DATA_AUDIO_SERVICE_TYPE, AV_FRAME_DATA_AUDIO_SERVICE_TYPE }, - { AV_PKT_DATA_MASTERING_DISPLAY_METADATA, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA }, - { AV_PKT_DATA_CONTENT_LIGHT_LEVEL, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL }, { AV_PKT_DATA_A53_CC, AV_FRAME_DATA_A53_CC }, - { AV_PKT_DATA_ICC_PROFILE, AV_FRAME_DATA_ICC_PROFILE }, - { AV_PKT_DATA_S12M_TIMECODE, AV_FRAME_DATA_S12M_TIMECODE }, + { AV_PKT_DATA_AFD, AV_FRAME_DATA_AFD }, { AV_PKT_DATA_DYNAMIC_HDR10_PLUS, AV_FRAME_DATA_DYNAMIC_HDR_PLUS }, + { AV_PKT_DATA_S12M_TIMECODE, AV_FRAME_DATA_S12M_TIMECODE }, + { AV_PKT_DATA_SKIP_SAMPLES, AV_FRAME_DATA_SKIP_SAMPLES }, }; frame->pts = pkt->pts; - frame->pkt_pos = pkt->pos; frame->duration = pkt->duration; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + frame->pkt_pos = pkt->pos; frame->pkt_size = pkt->size; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + for (int i = 0; i < FF_ARRAY_ELEMS(sd_global_map); i++) { + size_t size; + const uint8_t *packet_sd = av_packet_get_side_data(pkt, sd_global_map[i].packet, &size); + if (packet_sd) { + AVFrameSideData *frame_sd; + frame_sd = av_frame_new_side_data(frame, sd_global_map[i].frame, size); + if (!frame_sd) + return AVERROR(ENOMEM); + memcpy(frame_sd->data, packet_sd, size); + } + } for (int i = 0; i < FF_ARRAY_ELEMS(sd); i++) { size_t size; uint8_t *packet_sd = av_packet_get_side_data(pkt, sd[i].packet, &size); @@ -1349,37 +1460,41 @@ int ff_decode_frame_props_from_pkt(const AVCodecContext *avctx, int ff_decode_frame_props(AVCodecContext *avctx, AVFrame *frame) { - const AVPacket *pkt = avctx->internal->last_pkt_props; + int ret; + + for (int i = 0; i < FF_ARRAY_ELEMS(sd_global_map); i++) { + const AVPacketSideData *packet_sd = ff_get_coded_side_data(avctx, + sd_global_map[i].packet); + if (packet_sd) { + AVFrameSideData *frame_sd = av_frame_new_side_data(frame, + sd_global_map[i].frame, + packet_sd->size); + if (!frame_sd) + return AVERROR(ENOMEM); + + memcpy(frame_sd->data, packet_sd->data, packet_sd->size); + } + } if (!(ffcodec(avctx->codec)->caps_internal & FF_CODEC_CAP_SETS_FRAME_PROPS)) { - int ret = ff_decode_frame_props_from_pkt(avctx, frame, pkt); + const AVPacket *pkt = avctx->internal->last_pkt_props; + + ret = ff_decode_frame_props_from_pkt(avctx, frame, pkt); if (ret < 0) return ret; - frame->pkt_size = pkt->stream_index; - } -#if FF_API_REORDERED_OPAQUE +#if FF_API_FRAME_PKT FF_DISABLE_DEPRECATION_WARNINGS - frame->reordered_opaque = avctx->reordered_opaque; + frame->pkt_size = pkt->stream_index; FF_ENABLE_DEPRECATION_WARNINGS #endif + } - if (frame->color_primaries == AVCOL_PRI_UNSPECIFIED) - frame->color_primaries = avctx->color_primaries; - if (frame->color_trc == AVCOL_TRC_UNSPECIFIED) - frame->color_trc = avctx->color_trc; - if (frame->colorspace == AVCOL_SPC_UNSPECIFIED) - frame->colorspace = avctx->colorspace; - if (frame->color_range == AVCOL_RANGE_UNSPECIFIED) - frame->color_range = avctx->color_range; - if (frame->chroma_location == AVCHROMA_LOC_UNSPECIFIED) - frame->chroma_location = avctx->chroma_sample_location; + ret = fill_frame_props(avctx, frame); + if (ret < 0) + return ret; switch (avctx->codec->type) { case AVMEDIA_TYPE_VIDEO: - frame->format = avctx->pix_fmt; - if (!frame->sample_aspect_ratio.num) - frame->sample_aspect_ratio = avctx->sample_aspect_ratio; - if (frame->width && frame->height && av_image_check_sar(frame->width, frame->height, frame->sample_aspect_ratio) < 0) { @@ -1388,25 +1503,6 @@ FF_ENABLE_DEPRECATION_WARNINGS frame->sample_aspect_ratio.den); frame->sample_aspect_ratio = (AVRational){ 0, 1 }; } - - break; - case AVMEDIA_TYPE_AUDIO: - if (!frame->sample_rate) - frame->sample_rate = avctx->sample_rate; - if (frame->format < 0) - frame->format = avctx->sample_fmt; - if (!frame->ch_layout.nb_channels) { - int ret = av_channel_layout_copy(&frame->ch_layout, &avctx->ch_layout); - if (ret < 0) - return ret; - } -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - frame->channels = frame->ch_layout.nb_channels; - frame->channel_layout = frame->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ? - frame->ch_layout.u.mask : 0; -FF_ENABLE_DEPRECATION_WARNINGS -#endif break; } return 0; @@ -1472,7 +1568,7 @@ int ff_attach_decode_data(AVFrame *frame) int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags) { - const AVHWAccel *hwaccel = avctx->hwaccel; + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); int override_dimensions = 1; int ret; @@ -1498,15 +1594,6 @@ int ff_get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags) goto fail; } } else if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) { -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - /* compat layer for old-style get_buffer() implementations */ - avctx->channels = avctx->ch_layout.nb_channels; - avctx->channel_layout = (avctx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE) ? - avctx->ch_layout.u.mask : 0; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (frame->nb_samples * (int64_t)avctx->ch_layout.nb_channels > avctx->max_samples) { av_log(avctx, AV_LOG_ERROR, "samples per frame %d, exceeds max_samples %"PRId64"\n", frame->nb_samples, avctx->max_samples); ret = AVERROR(EINVAL); @@ -1599,6 +1686,7 @@ int ff_reget_buffer(AVCodecContext *avctx, AVFrame *frame, int flags) int ff_decode_preinit(AVCodecContext *avctx) { AVCodecInternal *avci = avctx->internal; + DecodeContext *dc = decode_ctx(avci); int ret = 0; /* if the decoder init function was already called previously, @@ -1646,10 +1734,10 @@ int ff_decode_preinit(AVCodecContext *avctx) } } - avctx->pts_correction_num_faulty_pts = - avctx->pts_correction_num_faulty_dts = 0; - avctx->pts_correction_last_pts = - avctx->pts_correction_last_dts = INT64_MIN; + dc->pts_correction_num_faulty_pts = + dc->pts_correction_num_faulty_dts = 0; + dc->pts_correction_last_pts = + dc->pts_correction_last_dts = INT64_MIN; if ( !CONFIG_GRAY && avctx->flags & AV_CODEC_FLAG_GRAY && avctx->codec_descriptor->type == AVMEDIA_TYPE_VIDEO) @@ -1659,6 +1747,35 @@ int ff_decode_preinit(AVCodecContext *avctx) avctx->export_side_data |= AV_CODEC_EXPORT_DATA_MVS; } + if (avctx->nb_side_data_prefer_packet == 1 && + avctx->side_data_prefer_packet[0] == -1) + dc->side_data_pref_mask = ~0ULL; + else { + for (unsigned i = 0; i < avctx->nb_side_data_prefer_packet; i++) { + int val = avctx->side_data_prefer_packet[i]; + + if (val < 0 || val >= AV_PKT_DATA_NB) { + av_log(avctx, AV_LOG_ERROR, "Invalid side data type: %d\n", val); + return AVERROR(EINVAL); + } + + for (unsigned j = 0; j < FF_ARRAY_ELEMS(sd_global_map); j++) { + if (sd_global_map[j].packet == val) { + val = sd_global_map[j].frame; + + // this code will need to be changed when we have more than + // 64 frame side data types + if (val >= 64) { + av_log(avctx, AV_LOG_ERROR, "Side data type too big\n"); + return AVERROR_BUG; + } + + dc->side_data_pref_mask |= 1ULL << val; + } + } + } + } + avci->in_pkt = av_packet_alloc(); avci->last_pkt_props = av_packet_alloc(); if (!avci->in_pkt || !avci->last_pkt_props) @@ -1668,9 +1785,104 @@ int ff_decode_preinit(AVCodecContext *avctx) if (ret < 0) return ret; +#if FF_API_DROPCHANGED + if (avctx->flags & AV_CODEC_FLAG_DROPCHANGED) + av_log(avctx, AV_LOG_WARNING, "The dropchanged flag is deprecated.\n"); +#endif + + return 0; +} + +/** + * Check side data preference and clear existing side data from frame + * if needed. + * + * @retval 0 side data of this type can be added to frame + * @retval 1 side data of this type should not be added to frame + */ +static int side_data_pref(const AVCodecContext *avctx, AVFrame *frame, + enum AVFrameSideDataType type) +{ + DecodeContext *dc = decode_ctx(avctx->internal); + + // Note: could be skipped for `type` without corresponding packet sd + if (av_frame_get_side_data(frame, type)) { + if (dc->side_data_pref_mask & (1ULL << type)) + return 1; + av_frame_remove_side_data(frame, type); + } + return 0; } + +int ff_frame_new_side_data(const AVCodecContext *avctx, AVFrame *frame, + enum AVFrameSideDataType type, size_t size, + AVFrameSideData **psd) +{ + AVFrameSideData *sd; + + if (side_data_pref(avctx, frame, type)) { + if (psd) + *psd = NULL; + return 0; + } + + sd = av_frame_new_side_data(frame, type, size); + if (psd) + *psd = sd; + + return sd ? 0 : AVERROR(ENOMEM); +} + +int ff_frame_new_side_data_from_buf(const AVCodecContext *avctx, + AVFrame *frame, enum AVFrameSideDataType type, + AVBufferRef **buf, AVFrameSideData **psd) +{ + AVFrameSideData *sd = NULL; + int ret = 0; + + if (side_data_pref(avctx, frame, type)) + goto finish; + + sd = av_frame_new_side_data_from_buf(frame, type, *buf); + if (sd) + *buf = NULL; + else + ret = AVERROR(ENOMEM); + +finish: + av_buffer_unref(buf); + if (psd) + *psd = sd; + + return ret; +} + +int ff_decode_mastering_display_new(const AVCodecContext *avctx, AVFrame *frame, + AVMasteringDisplayMetadata **mdm) +{ + if (side_data_pref(avctx, frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA)) { + *mdm = NULL; + return 0; + } + + *mdm = av_mastering_display_metadata_create_side_data(frame); + return *mdm ? 0 : AVERROR(ENOMEM); +} + +int ff_decode_content_light_new(const AVCodecContext *avctx, AVFrame *frame, + AVContentLightMetadata **clm) +{ + if (side_data_pref(avctx, frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL)) { + *clm = NULL; + return 0; + } + + *clm = av_content_light_metadata_create_side_data(frame); + return *clm ? 0 : AVERROR(ENOMEM); +} + int ff_copy_palette(void *dst, const AVPacket *src, void *logctx) { size_t size; @@ -1685,3 +1897,54 @@ int ff_copy_palette(void *dst, const AVPacket *src, void *logctx) } return 0; } + +int ff_hwaccel_frame_priv_alloc(AVCodecContext *avctx, void **hwaccel_picture_private) +{ + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); + + if (!hwaccel || !hwaccel->frame_priv_data_size) + return 0; + + av_assert0(!*hwaccel_picture_private); + + if (hwaccel->free_frame_priv) { + AVHWFramesContext *frames_ctx; + + if (!avctx->hw_frames_ctx) + return AVERROR(EINVAL); + + frames_ctx = (AVHWFramesContext *) avctx->hw_frames_ctx->data; + *hwaccel_picture_private = ff_refstruct_alloc_ext(hwaccel->frame_priv_data_size, 0, + frames_ctx->device_ctx, + hwaccel->free_frame_priv); + } else { + *hwaccel_picture_private = ff_refstruct_allocz(hwaccel->frame_priv_data_size); + } + + if (!*hwaccel_picture_private) + return AVERROR(ENOMEM); + + return 0; +} + +void ff_decode_flush_buffers(AVCodecContext *avctx) +{ + AVCodecInternal *avci = avctx->internal; + DecodeContext *dc = decode_ctx(avci); + + av_packet_unref(avci->last_pkt_props); + av_packet_unref(avci->in_pkt); + + dc->pts_correction_last_pts = + dc->pts_correction_last_dts = INT64_MIN; + + av_bsf_flush(avci->bsf); + + dc->nb_draining_errors = 0; + dc->draining_started = 0; +} + +AVCodecInternal *ff_decode_internal_alloc(void) +{ + return av_mallocz(sizeof(DecodeContext)); +} diff --git a/libavcodec/decode.h b/libavcodec/decode.h index 8430ffbd664..4ffbd9db8eb 100644 --- a/libavcodec/decode.h +++ b/libavcodec/decode.h @@ -21,7 +21,6 @@ #ifndef AVCODEC_DECODE_H #define AVCODEC_DECODE_H -#include "libavutil/buffer.h" #include "libavutil/frame.h" #include "libavutil/hwcontext.h" @@ -53,11 +52,6 @@ typedef struct FrameDecodeData { void (*hwaccel_priv_free)(void *priv); } FrameDecodeData; -/** - * avcodec_receive_frame() implementation for decoders. - */ -int ff_decode_receive_frame(AVCodecContext *avctx, AVFrame *frame); - /** * Called by decoders to get the next packet for decoding. * @@ -99,12 +93,6 @@ int ff_attach_decode_data(AVFrame *frame); */ int ff_copy_palette(void *dst, const AVPacket *src, void *logctx); -/** - * Perform decoder initialization and validation. - * Called when opening the decoder, before the FFCodec.init() call. - */ -int ff_decode_preinit(AVCodecContext *avctx); - /** * Check that the provided frame dimensions are valid and set them on the codec * context. @@ -150,4 +138,62 @@ int ff_reget_buffer(AVCodecContext *avctx, AVFrame *frame, int flags); int ff_side_data_update_matrix_encoding(AVFrame *frame, enum AVMatrixEncoding matrix_encoding); +/** + * Allocate a hwaccel frame private data if the provided avctx + * uses a hwaccel method that needs it. The returned data is + * a RefStruct reference (if allocated). + * + * @param avctx The codec context + * @param hwaccel_picture_private Pointer to return hwaccel_picture_private + * @return 0 on success, < 0 on error + */ +int ff_hwaccel_frame_priv_alloc(AVCodecContext *avctx, void **hwaccel_picture_private); + +/** + * Get side data of the given type from a decoding context. + */ +const AVPacketSideData *ff_get_coded_side_data(const AVCodecContext *avctx, + enum AVPacketSideDataType type); + +/** + * Wrapper around av_frame_new_side_data, which rejects side data overridden by + * the demuxer. Returns 0 on success, and a negative error code otherwise. + * If successful and sd is not NULL, *sd may either contain a pointer to the new + * side data, or NULL in case the side data was already present. + */ +int ff_frame_new_side_data(const AVCodecContext *avctx, AVFrame *frame, + enum AVFrameSideDataType type, size_t size, + AVFrameSideData **sd); + +/** + * Similar to `ff_frame_new_side_data`, but using an existing buffer ref. + * + * *buf is ALWAYS consumed by this function and NULL written in its place, even + * on failure. + */ +int ff_frame_new_side_data_from_buf(const AVCodecContext *avctx, + AVFrame *frame, enum AVFrameSideDataType type, + AVBufferRef **buf, AVFrameSideData **sd); + +struct AVMasteringDisplayMetadata; +struct AVContentLightMetadata; + +/** + * Wrapper around av_mastering_display_metadata_create_side_data(), which + * rejects side data overridden by the demuxer. Returns 0 on success, and a + * negative error code otherwise. If successful, *mdm may either be a pointer to + * the new side data, or NULL in case the side data was already present. + */ +int ff_decode_mastering_display_new(const AVCodecContext *avctx, AVFrame *frame, + struct AVMasteringDisplayMetadata **mdm); + +/** + * Wrapper around av_content_light_metadata_create_side_data(), which + * rejects side data overridden by the demuxer. Returns 0 on success, and a + * negative error code otherwise. If successful, *clm may either be a pointer to + * the new side data, or NULL in case the side data was already present. + */ +int ff_decode_content_light_new(const AVCodecContext *avctx, AVFrame *frame, + struct AVContentLightMetadata **clm); + #endif /* AVCODEC_DECODE_H */ diff --git a/libavcodec/defs.h b/libavcodec/defs.h index fbe3254db25..00d840ec19b 100644 --- a/libavcodec/defs.h +++ b/libavcodec/defs.h @@ -61,6 +61,149 @@ #define FF_COMPLIANCE_UNOFFICIAL -1 ///< Allow unofficial extensions #define FF_COMPLIANCE_EXPERIMENTAL -2 ///< Allow nonstandardized experimental things. + +#define AV_PROFILE_UNKNOWN -99 +#define AV_PROFILE_RESERVED -100 + +#define AV_PROFILE_AAC_MAIN 0 +#define AV_PROFILE_AAC_LOW 1 +#define AV_PROFILE_AAC_SSR 2 +#define AV_PROFILE_AAC_LTP 3 +#define AV_PROFILE_AAC_HE 4 +#define AV_PROFILE_AAC_HE_V2 28 +#define AV_PROFILE_AAC_LD 22 +#define AV_PROFILE_AAC_ELD 38 +#define AV_PROFILE_MPEG2_AAC_LOW 128 +#define AV_PROFILE_MPEG2_AAC_HE 131 + +#define AV_PROFILE_DNXHD 0 +#define AV_PROFILE_DNXHR_LB 1 +#define AV_PROFILE_DNXHR_SQ 2 +#define AV_PROFILE_DNXHR_HQ 3 +#define AV_PROFILE_DNXHR_HQX 4 +#define AV_PROFILE_DNXHR_444 5 + +#define AV_PROFILE_DTS 20 +#define AV_PROFILE_DTS_ES 30 +#define AV_PROFILE_DTS_96_24 40 +#define AV_PROFILE_DTS_HD_HRA 50 +#define AV_PROFILE_DTS_HD_MA 60 +#define AV_PROFILE_DTS_EXPRESS 70 +#define AV_PROFILE_DTS_HD_MA_X 61 +#define AV_PROFILE_DTS_HD_MA_X_IMAX 62 + +#define AV_PROFILE_EAC3_DDP_ATMOS 30 + +#define AV_PROFILE_TRUEHD_ATMOS 30 + +#define AV_PROFILE_MPEG2_422 0 +#define AV_PROFILE_MPEG2_HIGH 1 +#define AV_PROFILE_MPEG2_SS 2 +#define AV_PROFILE_MPEG2_SNR_SCALABLE 3 +#define AV_PROFILE_MPEG2_MAIN 4 +#define AV_PROFILE_MPEG2_SIMPLE 5 + +#define AV_PROFILE_H264_CONSTRAINED (1<<9) // 8+1; constraint_set1_flag +#define AV_PROFILE_H264_INTRA (1<<11) // 8+3; constraint_set3_flag + +#define AV_PROFILE_H264_BASELINE 66 +#define AV_PROFILE_H264_CONSTRAINED_BASELINE (66|AV_PROFILE_H264_CONSTRAINED) +#define AV_PROFILE_H264_MAIN 77 +#define AV_PROFILE_H264_EXTENDED 88 +#define AV_PROFILE_H264_HIGH 100 +#define AV_PROFILE_H264_HIGH_10 110 +#define AV_PROFILE_H264_HIGH_10_INTRA (110|AV_PROFILE_H264_INTRA) +#define AV_PROFILE_H264_MULTIVIEW_HIGH 118 +#define AV_PROFILE_H264_HIGH_422 122 +#define AV_PROFILE_H264_HIGH_422_INTRA (122|AV_PROFILE_H264_INTRA) +#define AV_PROFILE_H264_STEREO_HIGH 128 +#define AV_PROFILE_H264_HIGH_444 144 +#define AV_PROFILE_H264_HIGH_444_PREDICTIVE 244 +#define AV_PROFILE_H264_HIGH_444_INTRA (244|AV_PROFILE_H264_INTRA) +#define AV_PROFILE_H264_CAVLC_444 44 + +#define AV_PROFILE_VC1_SIMPLE 0 +#define AV_PROFILE_VC1_MAIN 1 +#define AV_PROFILE_VC1_COMPLEX 2 +#define AV_PROFILE_VC1_ADVANCED 3 + +#define AV_PROFILE_MPEG4_SIMPLE 0 +#define AV_PROFILE_MPEG4_SIMPLE_SCALABLE 1 +#define AV_PROFILE_MPEG4_CORE 2 +#define AV_PROFILE_MPEG4_MAIN 3 +#define AV_PROFILE_MPEG4_N_BIT 4 +#define AV_PROFILE_MPEG4_SCALABLE_TEXTURE 5 +#define AV_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION 6 +#define AV_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE 7 +#define AV_PROFILE_MPEG4_HYBRID 8 +#define AV_PROFILE_MPEG4_ADVANCED_REAL_TIME 9 +#define AV_PROFILE_MPEG4_CORE_SCALABLE 10 +#define AV_PROFILE_MPEG4_ADVANCED_CODING 11 +#define AV_PROFILE_MPEG4_ADVANCED_CORE 12 +#define AV_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE 13 +#define AV_PROFILE_MPEG4_SIMPLE_STUDIO 14 +#define AV_PROFILE_MPEG4_ADVANCED_SIMPLE 15 + +#define AV_PROFILE_JPEG2000_CSTREAM_RESTRICTION_0 1 +#define AV_PROFILE_JPEG2000_CSTREAM_RESTRICTION_1 2 +#define AV_PROFILE_JPEG2000_CSTREAM_NO_RESTRICTION 32768 +#define AV_PROFILE_JPEG2000_DCINEMA_2K 3 +#define AV_PROFILE_JPEG2000_DCINEMA_4K 4 + +#define AV_PROFILE_VP9_0 0 +#define AV_PROFILE_VP9_1 1 +#define AV_PROFILE_VP9_2 2 +#define AV_PROFILE_VP9_3 3 + +#define AV_PROFILE_HEVC_MAIN 1 +#define AV_PROFILE_HEVC_MAIN_10 2 +#define AV_PROFILE_HEVC_MAIN_STILL_PICTURE 3 +#define AV_PROFILE_HEVC_REXT 4 +#define AV_PROFILE_HEVC_SCC 9 + +#define AV_PROFILE_VVC_MAIN_10 1 +#define AV_PROFILE_VVC_MAIN_10_444 33 + +#define AV_PROFILE_AV1_MAIN 0 +#define AV_PROFILE_AV1_HIGH 1 +#define AV_PROFILE_AV1_PROFESSIONAL 2 + +#define AV_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT 0xc0 +#define AV_PROFILE_MJPEG_HUFFMAN_EXTENDED_SEQUENTIAL_DCT 0xc1 +#define AV_PROFILE_MJPEG_HUFFMAN_PROGRESSIVE_DCT 0xc2 +#define AV_PROFILE_MJPEG_HUFFMAN_LOSSLESS 0xc3 +#define AV_PROFILE_MJPEG_JPEG_LS 0xf7 + +#define AV_PROFILE_SBC_MSBC 1 + +#define AV_PROFILE_PRORES_PROXY 0 +#define AV_PROFILE_PRORES_LT 1 +#define AV_PROFILE_PRORES_STANDARD 2 +#define AV_PROFILE_PRORES_HQ 3 +#define AV_PROFILE_PRORES_4444 4 +#define AV_PROFILE_PRORES_XQ 5 + +#define AV_PROFILE_ARIB_PROFILE_A 0 +#define AV_PROFILE_ARIB_PROFILE_C 1 + +#define AV_PROFILE_KLVA_SYNC 0 +#define AV_PROFILE_KLVA_ASYNC 1 + +#define AV_PROFILE_EVC_BASELINE 0 +#define AV_PROFILE_EVC_MAIN 1 + + +#define AV_LEVEL_UNKNOWN -99 + +enum AVFieldOrder { + AV_FIELD_UNKNOWN, + AV_FIELD_PROGRESSIVE, + AV_FIELD_TT, ///< Top coded_first, top displayed first + AV_FIELD_BB, ///< Bottom coded first, bottom displayed first + AV_FIELD_TB, ///< Top coded first, bottom displayed first + AV_FIELD_BT, ///< Bottom coded first, top displayed first +}; + /** * @ingroup lavc_decoding */ diff --git a/libavcodec/dfa.c b/libavcodec/dfa.c index 114c803f32e..9114feb0b3a 100644 --- a/libavcodec/dfa.c +++ b/libavcodec/dfa.c @@ -367,7 +367,11 @@ static int dfa_decode_frame(AVCodecContext *avctx, AVFrame *frame, s->pal[i] = bytestream2_get_be24(&gb) << 2; s->pal[i] |= 0xFFU << 24 | (s->pal[i] >> 6) & 0x30303; } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else if (chunk_type <= 9) { if (decoder[chunk_type - 2](&gb, s->frame_buf, avctx->width, avctx->height)) { av_log(avctx, AV_LOG_ERROR, "Error decoding %s chunk\n", diff --git a/libavcodec/dirac.c b/libavcodec/dirac.c index bc51a2fbd72..4736304977c 100644 --- a/libavcodec/dirac.c +++ b/libavcodec/dirac.c @@ -26,9 +26,12 @@ * @author Marco Gerards , David Conrad, Jordi Ortiz */ +#include "config.h" + #include "libavutil/pixdesc.h" #include "dirac.h" +#include "get_bits.h" #include "golomb.h" #include "mpeg12data.h" diff --git a/libavcodec/dirac.h b/libavcodec/dirac.h index e6d9d346d9c..8c348cdc027 100644 --- a/libavcodec/dirac.h +++ b/libavcodec/dirac.h @@ -31,7 +31,11 @@ * @author Jordi Ortiz */ -#include "avcodec.h" +#include +#include + +#include "libavutil/pixfmt.h" +#include "libavutil/rational.h" /** * The spec limits the number of wavelet decompositions to 4 for both diff --git a/libavcodec/dirac_arith.h b/libavcodec/dirac_arith.h index 350a58fca6b..203d481603b 100644 --- a/libavcodec/dirac_arith.h +++ b/libavcodec/dirac_arith.h @@ -28,7 +28,11 @@ #ifndef AVCODEC_DIRAC_ARITH_H #define AVCODEC_DIRAC_ARITH_H +#include "config.h" + +#if ARCH_X86 #include "libavutil/x86/asm.h" +#endif #include "bytestream.h" #include "get_bits.h" diff --git a/libavcodec/dirac_dwt.c b/libavcodec/dirac_dwt.c index 4039899cf01..d473f64daa5 100644 --- a/libavcodec/dirac_dwt.c +++ b/libavcodec/dirac_dwt.c @@ -21,6 +21,7 @@ #include "libavutil/attributes.h" #include "libavutil/common.h" +#include "libavutil/log.h" #include "dirac_dwt.h" #define TEMPLATE_8bit diff --git a/libavcodec/diracdec.c b/libavcodec/diracdec.c index 0ae582befeb..24c7df00010 100644 --- a/libavcodec/diracdec.c +++ b/libavcodec/diracdec.c @@ -2103,11 +2103,6 @@ static int get_delayed_pic(DiracContext *s, AVFrame *picture, int *got_frame) out->reference ^= DELAYED_PIC_REF; if((ret = av_frame_ref(picture, out->avframe)) < 0) return ret; -#if FF_API_FRAME_PICTURE_NUMBER -FF_DISABLE_DEPRECATION_WARNINGS - picture->display_picture_number = out->picture_number; -FF_ENABLE_DEPRECATION_WARNINGS -#endif *got_frame = 1; } @@ -2230,7 +2225,10 @@ static int dirac_decode_data_unit(AVCodecContext *avctx, const uint8_t *buf, int s->hq_picture = (parse_code & 0xF8) == 0xE8; /* [DIRAC_STD] is_hq_picture() */ s->dc_prediction = (parse_code & 0x28) == 0x08; /* [DIRAC_STD] using_dc_prediction() */ pic->reference = (parse_code & 0x0C) == 0x0C; /* [DIRAC_STD] is_reference() */ - pic->avframe->key_frame = s->num_refs == 0; /* [DIRAC_STD] is_intra() */ + if (s->num_refs == 0) /* [DIRAC_STD] is_intra() */ + pic->avframe->flags |= AV_FRAME_FLAG_KEY; + else + pic->avframe->flags &= ~AV_FRAME_FLAG_KEY; pic->avframe->pict_type = s->num_refs + 1; /* Definition of AVPictureType in avutil.h */ /* VC-2 Low Delay has a different parse code than the Dirac Low Delay */ @@ -2347,11 +2345,6 @@ static int dirac_decode_frame(AVCodecContext *avctx, AVFrame *picture, if((ret = av_frame_ref(picture, delayed_frame->avframe)) < 0) return ret; s->frame_number = delayed_frame->picture_number + 1LL; -#if FF_API_FRAME_PICTURE_NUMBER -FF_DISABLE_DEPRECATION_WARNINGS - picture->display_picture_number = delayed_frame->picture_number; -FF_ENABLE_DEPRECATION_WARNINGS -#endif *got_frame = 1; } } else if (s->current_picture->picture_number == s->frame_number) { @@ -2359,11 +2352,6 @@ FF_ENABLE_DEPRECATION_WARNINGS if((ret = av_frame_ref(picture, s->current_picture->avframe)) < 0) return ret; s->frame_number = s->current_picture->picture_number + 1LL; -#if FF_API_FRAME_PICTURE_NUMBER -FF_DISABLE_DEPRECATION_WARNINGS - picture->display_picture_number = s->current_picture->picture_number; -FF_ENABLE_DEPRECATION_WARNINGS -#endif *got_frame = 1; } diff --git a/libavcodec/dnxhddata.c b/libavcodec/dnxhddata.c index d52abe87dd0..ce06d77f18e 100644 --- a/libavcodec/dnxhddata.c +++ b/libavcodec/dnxhddata.c @@ -22,7 +22,7 @@ #include #include "libavutil/log.h" #include "libavutil/macros.h" -#include "avcodec.h" +#include "defs.h" #include "dnxhddata.h" /* The quantization tables below are in zigzag order! */ @@ -1110,15 +1110,15 @@ int ff_dnxhd_get_hr_frame_size(int cid, int w, int h) static int dnxhd_find_hr_cid(AVCodecContext *avctx) { switch (avctx->profile) { - case FF_PROFILE_DNXHR_444: + case AV_PROFILE_DNXHR_444: return 1270; - case FF_PROFILE_DNXHR_HQX: + case AV_PROFILE_DNXHR_HQX: return 1271; - case FF_PROFILE_DNXHR_HQ: + case AV_PROFILE_DNXHR_HQ: return 1272; - case FF_PROFILE_DNXHR_SQ: + case AV_PROFILE_DNXHR_SQ: return 1273; - case FF_PROFILE_DNXHR_LB: + case AV_PROFILE_DNXHR_LB: return 1274; } return 0; @@ -1129,7 +1129,7 @@ int ff_dnxhd_find_cid(AVCodecContext *avctx, int bit_depth) int i, j; int mbs = avctx->bit_rate / 1000000; - if (avctx->profile != FF_PROFILE_DNXHD) + if (avctx->profile != AV_PROFILE_DNXHD) return dnxhd_find_hr_cid(avctx); if (!mbs) diff --git a/libavcodec/dnxhddec.c b/libavcodec/dnxhddec.c index 7cc4f94c7f8..703d0e341cf 100644 --- a/libavcodec/dnxhddec.c +++ b/libavcodec/dnxhddec.c @@ -130,21 +130,22 @@ static int dnxhd_init_vlc(DNXHDContext *ctx, uint32_t cid, int bitdepth) ctx->cid_table = cid_table; av_log(ctx->avctx, AV_LOG_VERBOSE, "Profile cid %"PRIu32".\n", cid); - ff_free_vlc(&ctx->ac_vlc); - ff_free_vlc(&ctx->dc_vlc); - ff_free_vlc(&ctx->run_vlc); + ff_vlc_free(&ctx->ac_vlc); + ff_vlc_free(&ctx->dc_vlc); + ff_vlc_free(&ctx->run_vlc); - if ((ret = init_vlc(&ctx->ac_vlc, DNXHD_VLC_BITS, 257, + if ((ret = vlc_init(&ctx->ac_vlc, DNXHD_VLC_BITS, 257, ctx->cid_table->ac_bits, 1, 1, ctx->cid_table->ac_codes, 2, 2, 0)) < 0) goto out; - if ((ret = init_vlc(&ctx->dc_vlc, DNXHD_DC_VLC_BITS, bitdepth > 8 ? 14 : 12, + if ((ret = vlc_init(&ctx->dc_vlc, DNXHD_DC_VLC_BITS, bitdepth > 8 ? 14 : 12, ctx->cid_table->dc_bits, 1, 1, ctx->cid_table->dc_codes, 1, 1, 0)) < 0) goto out; - if ((ret = init_vlc(&ctx->run_vlc, DNXHD_VLC_BITS, 62, + if ((ret = ff_vlc_init_sparse(&ctx->run_vlc, DNXHD_VLC_BITS, 62, ctx->cid_table->run_bits, 1, 1, - ctx->cid_table->run_codes, 2, 2, 0)) < 0) + ctx->cid_table->run_codes, 2, 2, + ctx->cid_table->run, 1, 1, 0)) < 0) goto out; ctx->cid = cid; @@ -152,7 +153,7 @@ static int dnxhd_init_vlc(DNXHDContext *ctx, uint32_t cid, int bitdepth) ret = 0; out: if (ret < 0) - av_log(ctx->avctx, AV_LOG_ERROR, "init_vlc failed\n"); + av_log(ctx->avctx, AV_LOG_ERROR, "vlc_init failed\n"); return ret; } @@ -160,17 +161,17 @@ static int dnxhd_get_profile(int cid) { switch(cid) { case 1270: - return FF_PROFILE_DNXHR_444; + return AV_PROFILE_DNXHR_444; case 1271: - return FF_PROFILE_DNXHR_HQX; + return AV_PROFILE_DNXHR_HQX; case 1272: - return FF_PROFILE_DNXHR_HQ; + return AV_PROFILE_DNXHR_HQ; case 1273: - return FF_PROFILE_DNXHR_SQ; + return AV_PROFILE_DNXHR_SQ; case 1274: - return FF_PROFILE_DNXHR_LB; + return AV_PROFILE_DNXHR_LB; } - return FF_PROFILE_DNXHD; + return AV_PROFILE_DNXHD; } static int dnxhd_decode_header(DNXHDContext *ctx, AVFrame *frame, @@ -195,8 +196,9 @@ static int dnxhd_decode_header(DNXHDContext *ctx, AVFrame *frame, } if (buf[5] & 2) { /* interlaced */ ctx->cur_field = first_field ? buf[5] & 1 : !ctx->cur_field; - frame->interlaced_frame = 1; - frame->top_field_first = first_field ^ ctx->cur_field; + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (first_field ^ ctx->cur_field) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; av_log(ctx->avctx, AV_LOG_DEBUG, "interlaced %d, cur field %d\n", buf[5] & 3, ctx->cur_field); } else { @@ -261,7 +263,7 @@ static int dnxhd_decode_header(DNXHDContext *ctx, AVFrame *frame, ctx->decode_dct_block = dnxhd_decode_dct_block_12; ctx->pix_fmt = AV_PIX_FMT_YUV422P12; } else if (bitdepth == 10) { - if (ctx->avctx->profile == FF_PROFILE_DNXHR_HQX) + if (ctx->avctx->profile == AV_PROFILE_DNXHR_HQX) ctx->decode_dct_block = dnxhd_decode_dct_block_10_444; else ctx->decode_dct_block = dnxhd_decode_dct_block_10; @@ -298,7 +300,7 @@ static int dnxhd_decode_header(DNXHDContext *ctx, AVFrame *frame, ctx->mb_width = (ctx->width + 15)>> 4; ctx->mb_height = AV_RB16(buf + 0x16c); - if ((ctx->height + 15) >> 4 == ctx->mb_height && frame->interlaced_frame) + if ((ctx->height + 15) >> 4 == ctx->mb_height && (frame->flags & AV_FRAME_FLAG_INTERLACED)) ctx->height <<= 1; av_log(ctx->avctx, AV_LOG_VERBOSE, "%dx%d, 4:%s %d bits, MBAFF=%d ACT=%d\n", @@ -316,7 +318,7 @@ static int dnxhd_decode_header(DNXHDContext *ctx, AVFrame *frame, } ctx->data_offset = 0x280; } - if ((ctx->mb_height << frame->interlaced_frame) > (ctx->height + 15) >> 4) { + if ((ctx->mb_height << !!(frame->flags & AV_FRAME_FLAG_INTERLACED)) > (ctx->height + 15) >> 4) { av_log(ctx->avctx, AV_LOG_ERROR, "mb height too big: %d\n", ctx->mb_height); return AVERROR_INVALIDDATA; @@ -357,7 +359,7 @@ static av_always_inline int dnxhd_decode_dct_block(const DNXHDContext *ctx, int level_shift, int dc_shift) { - int i, j, index1, index2, len, flags; + int i, j, index1, len, flags; int level, component, sign; const int *scale; const uint8_t *weight_matrix; @@ -424,10 +426,11 @@ static av_always_inline int dnxhd_decode_dct_block(const DNXHDContext *ctx, } if (flags & 2) { + int run; UPDATE_CACHE(bs, &row->gb); - GET_VLC(index2, bs, &row->gb, ctx->run_vlc.table, + GET_VLC(run, bs, &row->gb, ctx->run_vlc.table, DNXHD_VLC_BITS, 2); - i += ctx->cid_table->run[index2]; + i += run; } if (++i > 63) { @@ -530,7 +533,7 @@ static int dnxhd_decode_macroblock(const DNXHDContext *ctx, RowContext *row, return AVERROR_INVALIDDATA; } - if (frame->interlaced_frame) { + if (frame->flags & AV_FRAME_FLAG_INTERLACED) { dct_linesize_luma <<= 1; dct_linesize_chroma <<= 1; } @@ -539,7 +542,7 @@ static int dnxhd_decode_macroblock(const DNXHDContext *ctx, RowContext *row, dest_u = frame->data[1] + ((y * dct_linesize_chroma) << 4) + (x << (3 + shift1 + ctx->is_444)); dest_v = frame->data[2] + ((y * dct_linesize_chroma) << 4) + (x << (3 + shift1 + ctx->is_444)); - if (frame->interlaced_frame && ctx->cur_field) { + if ((frame->flags & AV_FRAME_FLAG_INTERLACED) && ctx->cur_field) { dest_y += frame->linesize[0]; dest_u += frame->linesize[1]; dest_v += frame->linesize[2]; @@ -652,14 +655,14 @@ static int dnxhd_decode_frame(AVCodecContext *avctx, AVFrame *picture, if ((ret = ff_thread_get_buffer(avctx, picture, 0)) < 0) return ret; picture->pict_type = AV_PICTURE_TYPE_I; - picture->key_frame = 1; + picture->flags |= AV_FRAME_FLAG_KEY; } ctx->buf_size = buf_size - ctx->data_offset; ctx->buf = buf + ctx->data_offset; avctx->execute2(avctx, dnxhd_decode_row, picture, NULL, ctx->mb_height); - if (first_field && picture->interlaced_frame) { + if (first_field && (picture->flags & AV_FRAME_FLAG_INTERLACED)) { buf += ctx->cid_table->coding_unit_size; buf_size -= ctx->cid_table->coding_unit_size; first_field = 0; @@ -715,9 +718,9 @@ static av_cold int dnxhd_decode_close(AVCodecContext *avctx) { DNXHDContext *ctx = avctx->priv_data; - ff_free_vlc(&ctx->ac_vlc); - ff_free_vlc(&ctx->dc_vlc); - ff_free_vlc(&ctx->run_vlc); + ff_vlc_free(&ctx->ac_vlc); + ff_vlc_free(&ctx->dc_vlc); + ff_vlc_free(&ctx->run_vlc); av_freep(&ctx->rows); diff --git a/libavcodec/dnxhdenc.c b/libavcodec/dnxhdenc.c index 176bf972d81..2316083b548 100644 --- a/libavcodec/dnxhdenc.c +++ b/libavcodec/dnxhdenc.c @@ -54,20 +54,20 @@ static const AVOption options[] = { offsetof(DNXHDEncContext, intra_quant_bias), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, VE }, { "profile", NULL, offsetof(DNXHDEncContext, profile), AV_OPT_TYPE_INT, - { .i64 = FF_PROFILE_DNXHD }, - FF_PROFILE_DNXHD, FF_PROFILE_DNXHR_444, VE, "profile" }, - { "dnxhd", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_DNXHD }, - 0, 0, VE, "profile" }, - { "dnxhr_444", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_DNXHR_444 }, - 0, 0, VE, "profile" }, - { "dnxhr_hqx", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_DNXHR_HQX }, - 0, 0, VE, "profile" }, - { "dnxhr_hq", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_DNXHR_HQ }, - 0, 0, VE, "profile" }, - { "dnxhr_sq", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_DNXHR_SQ }, - 0, 0, VE, "profile" }, - { "dnxhr_lb", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_DNXHR_LB }, - 0, 0, VE, "profile" }, + { .i64 = AV_PROFILE_DNXHD }, + AV_PROFILE_DNXHD, AV_PROFILE_DNXHR_444, VE, .unit = "profile" }, + { "dnxhd", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_DNXHD }, + 0, 0, VE, .unit = "profile" }, + { "dnxhr_444", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_DNXHR_444 }, + 0, 0, VE, .unit = "profile" }, + { "dnxhr_hqx", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_DNXHR_HQX }, + 0, 0, VE, .unit = "profile" }, + { "dnxhr_hq", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_DNXHR_HQ }, + 0, 0, VE, .unit = "profile" }, + { "dnxhr_sq", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_DNXHR_SQ }, + 0, 0, VE, .unit = "profile" }, + { "dnxhr_lb", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_DNXHR_LB }, + 0, 0, VE, .unit = "profile" }, { NULL } }; @@ -78,7 +78,7 @@ static const AVClass dnxhd_class = { .version = LIBAVUTIL_VERSION_INT, }; -static void dnxhd_8bit_get_pixels_8x4_sym(int16_t *av_restrict block, +static void dnxhd_8bit_get_pixels_8x4_sym(int16_t *restrict block, const uint8_t *pixels, ptrdiff_t line_size) { @@ -102,7 +102,7 @@ static void dnxhd_8bit_get_pixels_8x4_sym(int16_t *av_restrict block, } static av_always_inline -void dnxhd_10bit_get_pixels_8x4_sym(int16_t *av_restrict block, +void dnxhd_10bit_get_pixels_8x4_sym(int16_t *restrict block, const uint8_t *pixels, ptrdiff_t line_size) { @@ -367,30 +367,30 @@ static av_cold int dnxhd_encode_init(AVCodecContext *avctx) break; } - if ((ctx->profile == FF_PROFILE_DNXHR_444 && (avctx->pix_fmt != AV_PIX_FMT_YUV444P10 && + if ((ctx->profile == AV_PROFILE_DNXHR_444 && (avctx->pix_fmt != AV_PIX_FMT_YUV444P10 && avctx->pix_fmt != AV_PIX_FMT_GBRP10)) || - (ctx->profile != FF_PROFILE_DNXHR_444 && (avctx->pix_fmt == AV_PIX_FMT_YUV444P10 || + (ctx->profile != AV_PROFILE_DNXHR_444 && (avctx->pix_fmt == AV_PIX_FMT_YUV444P10 || avctx->pix_fmt == AV_PIX_FMT_GBRP10))) { av_log(avctx, AV_LOG_ERROR, "pixel format is incompatible with DNxHD profile\n"); return AVERROR(EINVAL); } - if (ctx->profile == FF_PROFILE_DNXHR_HQX && avctx->pix_fmt != AV_PIX_FMT_YUV422P10) { + if (ctx->profile == AV_PROFILE_DNXHR_HQX && avctx->pix_fmt != AV_PIX_FMT_YUV422P10) { av_log(avctx, AV_LOG_ERROR, "pixel format is incompatible with DNxHR HQX profile\n"); return AVERROR(EINVAL); } - if ((ctx->profile == FF_PROFILE_DNXHR_LB || - ctx->profile == FF_PROFILE_DNXHR_SQ || - ctx->profile == FF_PROFILE_DNXHR_HQ) && avctx->pix_fmt != AV_PIX_FMT_YUV422P) { + if ((ctx->profile == AV_PROFILE_DNXHR_LB || + ctx->profile == AV_PROFILE_DNXHR_SQ || + ctx->profile == AV_PROFILE_DNXHR_HQ) && avctx->pix_fmt != AV_PIX_FMT_YUV422P) { av_log(avctx, AV_LOG_ERROR, "pixel format is incompatible with DNxHR LB/SQ/HQ profile\n"); return AVERROR(EINVAL); } - ctx->is_444 = ctx->profile == FF_PROFILE_DNXHR_444; + ctx->is_444 = ctx->profile == AV_PROFILE_DNXHR_444; avctx->profile = ctx->profile; ctx->cid = ff_dnxhd_find_cid(avctx, ctx->bit_depth); if (!ctx->cid) { @@ -426,13 +426,13 @@ static av_cold int dnxhd_encode_init(AVCodecContext *avctx) ff_pixblockdsp_init(&ctx->m.pdsp, avctx); ff_dct_encode_init(&ctx->m); - if (ctx->profile != FF_PROFILE_DNXHD) + if (ctx->profile != AV_PROFILE_DNXHD) ff_videodsp_init(&ctx->m.vdsp, ctx->bit_depth); if (!ctx->m.dct_quantize) ctx->m.dct_quantize = ff_dct_quantize_c; - if (ctx->is_444 || ctx->profile == FF_PROFILE_DNXHR_HQX) { + if (ctx->is_444 || ctx->profile == AV_PROFILE_DNXHR_HQX) { ctx->m.dct_quantize = dnxhd_10bit_dct_quantize_444; ctx->get_pixels_8x4_sym = dnxhd_10bit_get_pixels_8x4_sym; ctx->block_width_l2 = 4; @@ -445,9 +445,7 @@ static av_cold int dnxhd_encode_init(AVCodecContext *avctx) ctx->block_width_l2 = 3; } -#if ARCH_X86 - ff_dnxhdenc_init_x86(ctx); -#endif + ff_dnxhdenc_init(ctx); ctx->m.mb_height = (avctx->height + 15) / 16; ctx->m.mb_width = (avctx->width + 15) / 16; @@ -457,7 +455,7 @@ static av_cold int dnxhd_encode_init(AVCodecContext *avctx) ctx->m.mb_height /= 2; } - if (ctx->interlaced && ctx->profile != FF_PROFILE_DNXHD) { + if (ctx->interlaced && ctx->profile != AV_PROFILE_DNXHD) { av_log(avctx, AV_LOG_ERROR, "Interlaced encoding is not supported for DNxHR profiles.\n"); return AVERROR(EINVAL); @@ -1251,7 +1249,8 @@ static void dnxhd_load_picture(DNXHDEncContext *ctx, const AVFrame *frame) ctx->thread[i]->dct_uv_offset = ctx->m.uvlinesize*8; } - ctx->cur_field = frame->interlaced_frame && !frame->top_field_first; + ctx->cur_field = (frame->flags & AV_FRAME_FLAG_INTERLACED) && + !(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); } static int dnxhd_encode_picture(AVCodecContext *avctx, AVPacket *pkt, @@ -1376,3 +1375,10 @@ const FFCodec ff_dnxhd_encoder = { .p.profiles = NULL_IF_CONFIG_SMALL(ff_dnxhd_profiles), .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; + +void ff_dnxhdenc_init(DNXHDEncContext *ctx) +{ +#if ARCH_X86 + ff_dnxhdenc_init_x86(ctx); +#endif +} diff --git a/libavcodec/dnxhdenc.h b/libavcodec/dnxhdenc.h index e581312ce4a..72077d631d6 100644 --- a/libavcodec/dnxhdenc.h +++ b/libavcodec/dnxhdenc.h @@ -26,8 +26,6 @@ #include -#include "config.h" - #include "libavutil/mem_internal.h" #include "mpegvideo.h" @@ -107,10 +105,11 @@ typedef struct DNXHDEncContext { RCCMPEntry *mb_cmp_tmp; RCEntry *mb_rc; - void (*get_pixels_8x4_sym)(int16_t *av_restrict /* align 16 */ block, + void (*get_pixels_8x4_sym)(int16_t *restrict /* align 16 */ block, const uint8_t *pixels, ptrdiff_t line_size); } DNXHDEncContext; +void ff_dnxhdenc_init(DNXHDEncContext *ctx); void ff_dnxhdenc_init_x86(DNXHDEncContext *ctx); #endif /* AVCODEC_DNXHDENC_H */ diff --git a/libavcodec/dolby_e.c b/libavcodec/dolby_e.c index 921c33f3ba5..9c3f6006c24 100644 --- a/libavcodec/dolby_e.c +++ b/libavcodec/dolby_e.c @@ -1272,13 +1272,6 @@ static av_cold int dolby_e_init(AVCodecContext *avctx) if (!(s->fdsp = avpriv_float_dsp_alloc(0))) return AVERROR(ENOMEM); -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (avctx->request_channel_layout & AV_CH_LAYOUT_NATIVE) - s->dectx.metadata.output_channel_order = CHANNEL_ORDER_CODED; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - s->dectx.metadata.multi_prog_warned = s->dectx.metadata.output_channel_order == CHANNEL_ORDER_CODED; s->dectx.avctx = s->avctx = avctx; return 0; @@ -1289,11 +1282,11 @@ FF_ENABLE_DEPRECATION_WARNINGS static const AVOption options[] = { { "channel_order", "Order in which the channels are to be exported", OFFSET(dectx.metadata.output_channel_order), AV_OPT_TYPE_INT, - { .i64 = CHANNEL_ORDER_DEFAULT }, 0, 1, FLAGS, "channel_order" }, + { .i64 = CHANNEL_ORDER_DEFAULT }, 0, 1, FLAGS, .unit = "channel_order" }, { "default", "normal libavcodec channel order", 0, AV_OPT_TYPE_CONST, - { .i64 = CHANNEL_ORDER_DEFAULT }, .flags = FLAGS, "channel_order" }, + { .i64 = CHANNEL_ORDER_DEFAULT }, .flags = FLAGS, .unit = "channel_order" }, { "coded", "order in which the channels are coded in the bitstream", - 0, AV_OPT_TYPE_CONST, { .i64 = CHANNEL_ORDER_CODED }, .flags = FLAGS, "channel_order" }, + 0, AV_OPT_TYPE_CONST, { .i64 = CHANNEL_ORDER_CODED }, .flags = FLAGS, .unit = "channel_order" }, { NULL }, }; diff --git a/libavcodec/dovi_rpu.c b/libavcodec/dovi_rpu.c index 107d1ab441c..31c64fb0602 100644 --- a/libavcodec/dovi_rpu.c +++ b/libavcodec/dovi_rpu.c @@ -26,6 +26,7 @@ #include "dovi_rpu.h" #include "golomb.h" #include "get_bits.h" +#include "refstruct.h" enum { RPU_COEFF_FIXED = 0, @@ -33,17 +34,17 @@ enum { }; /** - * Private contents of vdr_ref. + * Private contents of vdr. */ -typedef struct DOVIVdrRef { +typedef struct DOVIVdr { AVDOVIDataMapping mapping; AVDOVIColorMetadata color; -} DOVIVdrRef; +} DOVIVdr; void ff_dovi_ctx_unref(DOVIContext *s) { - for (int i = 0; i < FF_ARRAY_ELEMS(s->vdr_ref); i++) - av_buffer_unref(&s->vdr_ref[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(s->vdr); i++) + ff_refstruct_unref(&s->vdr[i]); *s = (DOVIContext) { .logctx = s->logctx, @@ -52,8 +53,8 @@ void ff_dovi_ctx_unref(DOVIContext *s) void ff_dovi_ctx_flush(DOVIContext *s) { - for (int i = 0; i < FF_ARRAY_ELEMS(s->vdr_ref); i++) - av_buffer_unref(&s->vdr_ref[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(s->vdr); i++) + ff_refstruct_unref(&s->vdr[i]); *s = (DOVIContext) { .logctx = s->logctx, @@ -61,23 +62,14 @@ void ff_dovi_ctx_flush(DOVIContext *s) }; } -int ff_dovi_ctx_replace(DOVIContext *s, const DOVIContext *s0) +void ff_dovi_ctx_replace(DOVIContext *s, const DOVIContext *s0) { - int ret; s->logctx = s0->logctx; s->mapping = s0->mapping; s->color = s0->color; s->dv_profile = s0->dv_profile; - for (int i = 0; i < DOVI_MAX_DM_ID; i++) { - if ((ret = av_buffer_replace(&s->vdr_ref[i], s0->vdr_ref[i])) < 0) - goto fail; - } - - return 0; - -fail: - ff_dovi_ctx_unref(s); - return ret; + for (int i = 0; i <= DOVI_MAX_DM_ID; i++) + ff_refstruct_replace(&s->vdr[i], s0->vdr[i]); } void ff_dovi_update_cfg(DOVIContext *s, const AVDOVIDecoderConfigurationRecord *cfg) @@ -153,7 +145,7 @@ static inline uint64_t get_ue_coef(GetBitContext *gb, const AVDOVIRpuDataHeader case RPU_COEFF_FIXED: ipart = get_ue_golomb_long(gb); fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom); - return (ipart << hdr->coef_log2_denom) + fpart.u32; + return (ipart << hdr->coef_log2_denom) | fpart.u32; case RPU_COEFF_FLOAT: fpart.u32 = get_bits_long(gb, 32); @@ -172,7 +164,7 @@ static inline int64_t get_se_coef(GetBitContext *gb, const AVDOVIRpuDataHeader * case RPU_COEFF_FIXED: ipart = get_se_golomb_long(gb); fpart.u32 = get_bits_long(gb, hdr->coef_log2_denom); - return ipart * (1LL << hdr->coef_log2_denom) + fpart.u32; + return ipart * (1LL << hdr->coef_log2_denom) | fpart.u32; case RPU_COEFF_FLOAT: fpart.u32 = get_bits_long(gb, 32); @@ -182,6 +174,18 @@ static inline int64_t get_se_coef(GetBitContext *gb, const AVDOVIRpuDataHeader * return 0; /* unreachable */ } +static inline unsigned get_variable_bits(GetBitContext *gb, int n) +{ + unsigned int value = get_bits(gb, n); + int read_more = get_bits1(gb); + while (read_more) { + value = (value + 1) << n; + value |= get_bits(gb, n); + read_more = get_bits1(gb); + } + return value; +} + #define VALIDATE(VAR, MIN, MAX) \ do { \ if (VAR < MIN || VAR > MAX) { \ @@ -195,7 +199,7 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size) { AVDOVIRpuDataHeader *hdr = &s->header; GetBitContext *gb = &(GetBitContext){0}; - DOVIVdrRef *vdr; + DOVIVdr *vdr; int ret; uint8_t nal_prefix; @@ -208,9 +212,36 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size) if ((ret = init_get_bits8(gb, rpu, rpu_size)) < 0) return ret; - /* RPU header, common values */ - nal_prefix = get_bits(gb, 8); - VALIDATE(nal_prefix, 25, 25); + /* Container header */ + if (s->dv_profile == 10 /* dav1.10 */) { + /* DV inside AV1 re-uses an EMDF container skeleton, but with fixed + * values - so we can effectively treat this as a magic byte sequence. + * + * The exact fields are, as follows: + * emdf_version : f(2) = 0 + * key_id : f(3) = 6 + * emdf_payload_id : f(5) = 31 + * emdf_payload_id_ext : var(5) = 225 + * smploffste : f(1) = 0 + * duratione : f(1) = 0 + * groupide : f(1) = 0 + * codecdatae : f(1) = 0 + * discard_unknown_payload : f(1) = 1 + */ + const unsigned header_magic = 0x01be6841u; + unsigned header, emdf_payload_size; + header = get_bits_long(gb, 27); + VALIDATE(header, header_magic, header_magic); + emdf_payload_size = get_variable_bits(gb, 8); + VALIDATE(emdf_payload_size, 6, 512); + if (emdf_payload_size * 8 > get_bits_left(gb)) + return AVERROR_INVALIDDATA; + } else { + nal_prefix = get_bits(gb, 8); + VALIDATE(nal_prefix, 25, 25); + } + + /* RPU header */ rpu_type = get_bits(gb, 6); if (rpu_type != 2) { av_log(s->logctx, AV_LOG_WARNING, "Unrecognized RPU type " @@ -278,23 +309,23 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size) if (use_prev_vdr_rpu) { int prev_vdr_rpu_id = get_ue_golomb_31(gb); VALIDATE(prev_vdr_rpu_id, 0, DOVI_MAX_DM_ID); - if (!s->vdr_ref[prev_vdr_rpu_id]) { + if (!s->vdr[prev_vdr_rpu_id]) { av_log(s->logctx, AV_LOG_ERROR, "Unknown previous RPU ID: %u\n", prev_vdr_rpu_id); goto fail; } - vdr = (DOVIVdrRef *) s->vdr_ref[prev_vdr_rpu_id]->data; + vdr = s->vdr[prev_vdr_rpu_id]; s->mapping = &vdr->mapping; } else { int vdr_rpu_id = get_ue_golomb_31(gb); VALIDATE(vdr_rpu_id, 0, DOVI_MAX_DM_ID); - if (!s->vdr_ref[vdr_rpu_id]) { - s->vdr_ref[vdr_rpu_id] = av_buffer_allocz(sizeof(DOVIVdrRef)); - if (!s->vdr_ref[vdr_rpu_id]) + if (!s->vdr[vdr_rpu_id]) { + s->vdr[vdr_rpu_id] = ff_refstruct_allocz(sizeof(DOVIVdr)); + if (!s->vdr[vdr_rpu_id]) return AVERROR(ENOMEM); } - vdr = (DOVIVdrRef *) s->vdr_ref[vdr_rpu_id]->data; + vdr = s->vdr[vdr_rpu_id]; s->mapping = &vdr->mapping; vdr->mapping.vdr_rpu_id = vdr_rpu_id; @@ -390,24 +421,24 @@ int ff_dovi_rpu_parse(DOVIContext *s, const uint8_t *rpu, size_t rpu_size) int current_dm_id = get_ue_golomb_31(gb); VALIDATE(affected_dm_id, 0, DOVI_MAX_DM_ID); VALIDATE(current_dm_id, 0, DOVI_MAX_DM_ID); - if (!s->vdr_ref[affected_dm_id]) { - s->vdr_ref[affected_dm_id] = av_buffer_allocz(sizeof(DOVIVdrRef)); - if (!s->vdr_ref[affected_dm_id]) + if (!s->vdr[affected_dm_id]) { + s->vdr[affected_dm_id] = ff_refstruct_allocz(sizeof(DOVIVdr)); + if (!s->vdr[affected_dm_id]) return AVERROR(ENOMEM); } - if (!s->vdr_ref[current_dm_id]) { + if (!s->vdr[current_dm_id]) { av_log(s->logctx, AV_LOG_ERROR, "Unknown previous RPU DM ID: %u\n", current_dm_id); goto fail; } /* Update current pointer based on current_dm_id */ - vdr = (DOVIVdrRef *) s->vdr_ref[current_dm_id]->data; + vdr = s->vdr[current_dm_id]; s->color = &vdr->color; /* Update values of affected_dm_id */ - vdr = (DOVIVdrRef *) s->vdr_ref[affected_dm_id]->data; + vdr = s->vdr[affected_dm_id]; color = &vdr->color; color->dm_metadata_id = affected_dm_id; color->scene_refresh_flag = get_ue_golomb_31(gb); diff --git a/libavcodec/dovi_rpu.h b/libavcodec/dovi_rpu.h index f6ca5bbbc56..51c5fdbb879 100644 --- a/libavcodec/dovi_rpu.h +++ b/libavcodec/dovi_rpu.h @@ -47,12 +47,12 @@ typedef struct DOVIContext { /** * Private fields internal to dovi_rpu.c */ - AVBufferRef *vdr_ref[DOVI_MAX_DM_ID+1]; + struct DOVIVdr *vdr[DOVI_MAX_DM_ID+1]; ///< RefStruct references uint8_t dv_profile; } DOVIContext; -int ff_dovi_ctx_replace(DOVIContext *s, const DOVIContext *s0); +void ff_dovi_ctx_replace(DOVIContext *s, const DOVIContext *s0); /** * Completely reset a DOVIContext, preserving only logctx. diff --git a/libavcodec/dpx.c b/libavcodec/dpx.c index 4f50608461e..80616d98a23 100644 --- a/libavcodec/dpx.c +++ b/libavcodec/dpx.c @@ -287,19 +287,21 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, tc = av_bswap32(read32(&buf, endian)); if (i != 0xFFFFFFFF) { - AVFrameSideData *tcside = - av_frame_new_side_data(p, AV_FRAME_DATA_S12M_TIMECODE, - sizeof(uint32_t) * 4); - if (!tcside) - return AVERROR(ENOMEM); - - tc_sd = (uint32_t*)tcside->data; - tc_sd[0] = 1; - tc_sd[1] = tc; - - av_timecode_make_smpte_tc_string2(tcbuf, avctx->framerate, - tc_sd[1], 0, 0); - av_dict_set(&p->metadata, "timecode", tcbuf, 0); + AVFrameSideData *tcside; + ret = ff_frame_new_side_data(avctx, p, AV_FRAME_DATA_S12M_TIMECODE, + sizeof(uint32_t) * 4, &tcside); + if (ret < 0) + return ret; + + if (tcside) { + tc_sd = (uint32_t*)tcside->data; + tc_sd[0] = 1; + tc_sd[1] = tc; + + av_timecode_make_smpte_tc_string2(tcbuf, avctx->framerate, + tc_sd[1], 0, 0); + av_dict_set(&p->metadata, "timecode", tcbuf, 0); + } } } @@ -476,14 +478,31 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, avctx->colorspace = AVCOL_SPC_RGB; } + av_strlcpy(creator, avpkt->data + 160, 100); + creator[100] = '\0'; + av_dict_set(&p->metadata, "Creator", creator, 0); + + av_strlcpy(input_device, avpkt->data + 1556, 32); + input_device[32] = '\0'; + av_dict_set(&p->metadata, "Input Device", input_device, 0); + + // Some devices do not pad 10bit samples to whole 32bit words per row + if (!memcmp(input_device, "Scanity", 7) || + !memcmp(creator, "Lasergraphics Inc.", 18)) { + if (bits_per_color == 10) + unpadded_10bit = 1; + } + // Table 3c: Runs will always break at scan line boundaries. Packing // will always break to the next 32-bit word at scan-line boundaries. // Unfortunately, the encoder produced invalid files, so attempt // to detect it + // Also handle special case with unpadded content need_align = FFALIGN(stride, 4); - if (need_align*avctx->height + (int64_t)offset > avpkt->size) { + if (need_align*avctx->height + (int64_t)offset > avpkt->size && + (!unpadded_10bit || (avctx->width * avctx->height * elements + 2) / 3 * 4 + (int64_t)offset > avpkt->size)) { // Alignment seems unappliable, try without - if (stride*avctx->height + (int64_t)offset > avpkt->size) { + if (stride*avctx->height + (int64_t)offset > avpkt->size || unpadded_10bit) { av_log(avctx, AV_LOG_ERROR, "Overread buffer. Invalid header?\n"); return AVERROR_INVALIDDATA; } else { @@ -609,20 +628,6 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; - av_strlcpy(creator, avpkt->data + 160, 100); - creator[100] = '\0'; - av_dict_set(&p->metadata, "Creator", creator, 0); - - av_strlcpy(input_device, avpkt->data + 1556, 32); - input_device[32] = '\0'; - av_dict_set(&p->metadata, "Input Device", input_device, 0); - - // Some devices do not pad 10bit samples to whole 32bit words per row - if (!memcmp(input_device, "Scanity", 7) || - !memcmp(creator, "Lasergraphics Inc.", 18)) { - unpadded_10bit = 1; - } - // Move pointer to offset from start of file buf = avpkt->data + offset; diff --git a/libavcodec/dsicinvideo.c b/libavcodec/dsicinvideo.c index 222044d125a..000d79e169e 100644 --- a/libavcodec/dsicinvideo.c +++ b/libavcodec/dsicinvideo.c @@ -293,7 +293,11 @@ static int cinvideo_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return res; memcpy(cin->frame->data[1], cin->palette, sizeof(cin->palette)); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS cin->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif for (y = 0; y < cin->avctx->height; ++y) memcpy(cin->frame->data[0] + (cin->avctx->height - 1 - y) * cin->frame->linesize[0], cin->bitmap_table[CIN_CUR_BMP] + y * cin->avctx->width, diff --git a/libavcodec/dv.h b/libavcodec/dv.h index 29f97b6089a..abff9f1ea9a 100644 --- a/libavcodec/dv.h +++ b/libavcodec/dv.h @@ -60,6 +60,12 @@ enum DVPackType { */ #define DV_MAX_FRAME_SIZE 576000 +// LCM of video framerate numerators +#define DV_TIMESCALE_VIDEO 60000 + +// LCM of audio sample rates +#define DV_TIMESCALE_AUDIO 14112000 + /** * maximum number of blocks per macroblock in any DV format */ diff --git a/libavcodec/dvdec.c b/libavcodec/dvdec.c index afc4bb0bcdd..a06e4807e7b 100644 --- a/libavcodec/dvdec.c +++ b/libavcodec/dvdec.c @@ -36,6 +36,7 @@ */ #include "libavutil/avassert.h" +#include "libavutil/emms.h" #include "libavutil/internal.h" #include "libavutil/mem_internal.h" #include "libavutil/thread.h" @@ -174,9 +175,9 @@ static av_cold void dv_init_static(void) /* NOTE: as a trick, we use the fact the no codes are unused * to accelerate the parsing of partial codes */ - ff_init_vlc_from_lengths(&dv_vlc, TEX_VLC_BITS, j, + ff_vlc_init_from_lengths(&dv_vlc, TEX_VLC_BITS, j, &tmp[0].len, sizeof(tmp[0]), - NULL, 0, 0, 0, INIT_VLC_USE_NEW_STATIC, NULL); + NULL, 0, 0, 0, VLC_INIT_USE_STATIC, NULL); av_assert1(dv_vlc.table_size == 1664); for (int i = 0; i < dv_vlc.table_size; i++) { @@ -345,7 +346,7 @@ static inline void bit_copy(PutBitContext *pb, GetBitContext *gb) put_bits(pb, bits_left, get_bits(gb, bits_left)); } -static av_always_inline void put_block_8x4(int16_t *block, uint8_t *av_restrict p, int stride) +static av_always_inline void put_block_8x4(int16_t *block, uint8_t *restrict p, int stride) { int i, j; @@ -646,10 +647,13 @@ static int dvvideo_decode_frame(AVCodecContext *avctx, AVFrame *frame, } s->frame = frame; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; avctx->pix_fmt = s->sys->pix_fmt; avctx->framerate = av_inv_q(s->sys->time_base); + avctx->bit_rate = av_rescale_q(s->sys->frame_size, + (AVRational) { 8, 1 }, + s->sys->time_base); ret = ff_set_dimensions(avctx, s->sys->width, s->sys->height); if (ret < 0) @@ -670,14 +674,14 @@ static int dvvideo_decode_frame(AVCodecContext *avctx, AVFrame *frame, /* Determine the codec's field order from the packet */ if ( *vsc_pack == DV_VIDEO_CONTROL ) { if (avctx->height == 720) { - frame->interlaced_frame = 0; - frame->top_field_first = 0; + frame->flags &= ~AV_FRAME_FLAG_INTERLACED; + frame->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; } else if (avctx->height == 1080) { - frame->interlaced_frame = 1; - frame->top_field_first = (vsc_pack[3] & 0x40) == 0x40; + frame->flags |= AV_FRAME_FLAG_INTERLACED; + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * ((vsc_pack[3] & 0x40) == 0x40); } else { - frame->interlaced_frame = (vsc_pack[3] & 0x10) == 0x10; - frame->top_field_first = !(vsc_pack[3] & 0x40); + frame->flags |= AV_FRAME_FLAG_INTERLACED * ((vsc_pack[3] & 0x10) == 0x10); + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * !(vsc_pack[3] & 0x40); } } diff --git a/libavcodec/dvdsubenc.c b/libavcodec/dvdsubenc.c index d272b576757..06c2cf5e5af 100644 --- a/libavcodec/dvdsubenc.c +++ b/libavcodec/dvdsubenc.c @@ -376,7 +376,8 @@ static int encode_dvd_subtitles(AVCodecContext *avctx, x2 = vrect.x + vrect.w - 1; y2 = vrect.y + vrect.h - 1; - if (x2 > avctx->width || y2 > avctx->height) { + if ((avctx->width > 0 && x2 > avctx->width) || + (avctx->height > 0 && y2 > avctx->height)) { av_log(avctx, AV_LOG_ERROR, "canvas_size(%d:%d) is too small(%d:%d) for render\n", avctx->width, avctx->height, x2, y2); ret = AVERROR(EINVAL); diff --git a/libavcodec/dvenc.c b/libavcodec/dvenc.c index cd442b524df..ce21247081d 100644 --- a/libavcodec/dvenc.c +++ b/libavcodec/dvenc.c @@ -29,6 +29,7 @@ #include "config.h" #include "libavutil/attributes.h" +#include "libavutil/emms.h" #include "libavutil/internal.h" #include "libavutil/mem_internal.h" #include "libavutil/opt.h" @@ -1048,9 +1049,9 @@ static inline int dv_write_pack(enum DVPackType pack_id, DVEncContext *c, int fs; if (c->avctx->height >= 720) - fs = c->avctx->height == 720 || c->frame->top_field_first ? 0x40 : 0x00; + fs = c->avctx->height == 720 || (c->frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 0x40 : 0x00; else - fs = c->frame->top_field_first ? 0x00 : 0x40; + fs = (c->frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 0x00 : 0x40; if (DV_PROFILE_IS_HD(c->sys) || (int)(av_q2d(c->avctx->sample_aspect_ratio) * diff --git a/libavcodec/dxa.c b/libavcodec/dxa.c index 8d2d2d771ba..d33ac3c8b0e 100644 --- a/libavcodec/dxa.c +++ b/libavcodec/dxa.c @@ -45,8 +45,8 @@ typedef struct DxaDecContext { uint32_t pal[256]; } DxaDecContext; -static const int shift1[6] = { 0, 8, 8, 8, 4, 4 }; -static const int shift2[6] = { 0, 0, 8, 4, 0, 4 }; +static const uint8_t shift1[6] = { 0, 8, 8, 8, 4, 4 }; +static const uint8_t shift2[6] = { 0, 0, 8, 4, 0, 4 }; static int decode_13(AVCodecContext *avctx, DxaDecContext *c, uint8_t* dst, int stride, uint8_t *src, int srcsize, uint8_t *ref) @@ -230,7 +230,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0) return ret; memcpy(frame->data[1], c->pal, AVPALETTE_SIZE); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = pc; +FF_ENABLE_DEPRECATION_WARNINGS +#endif outptr = frame->data[0]; srcptr = c->decomp_buf; @@ -258,19 +262,19 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, switch(compr){ case -1: - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; if (c->prev->data[0]) memcpy(frame->data[0], c->prev->data[0], frame->linesize[0] * avctx->height); else{ // Should happen only when first frame is 'NULL' memset(frame->data[0], 0, frame->linesize[0] * avctx->height); - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; } break; case 2: case 4: - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; for (j = 0; j < avctx->height; j++) { memcpy(outptr, srcptr, avctx->width); @@ -285,7 +289,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (!(avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL)) return AVERROR_INVALIDDATA; } - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; for (j = 0; j < avctx->height; j++) { if(tmpptr){ @@ -300,7 +304,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, break; case 12: // ScummVM coding case 13: - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; if (!c->prev->data[0]) { av_log(avctx, AV_LOG_ERROR, "Missing reference frame\n"); @@ -313,8 +317,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return AVERROR_INVALIDDATA; } - av_frame_unref(c->prev); - if ((ret = av_frame_ref(c->prev, frame)) < 0) + if ((ret = av_frame_replace(c->prev, frame)) < 0) return ret; *got_frame = 1; diff --git a/libavcodec/dxtory.c b/libavcodec/dxtory.c index e13d274862e..f36420cdd9c 100644 --- a/libavcodec/dxtory.c +++ b/libavcodec/dxtory.c @@ -864,7 +864,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; pic->pict_type = AV_PICTURE_TYPE_I; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/dxv.c b/libavcodec/dxv.c index 7c84874229b..e3107414c8e 100644 --- a/libavcodec/dxv.c +++ b/libavcodec/dxv.c @@ -28,6 +28,7 @@ #include "avcodec.h" #include "bytestream.h" #include "codec_internal.h" +#include "dxv.h" #include "lzf.h" #include "texturedsp.h" #include "thread.h" @@ -37,216 +38,15 @@ typedef struct DXVContext { GetByteContext gbc; uint8_t *tex_data; // Compressed texture - uint8_t *ctex_data; // Compressed texture - int tex_rat; // Compression ratio - int tex_step; // Distance between blocks - int ctex_step; // Distance between blocks - int64_t tex_size; // Texture size - int64_t ctex_size; // Texture size + uint8_t *ctex_data; // Compressed chroma texture - /* Optimal number of slices for parallel decoding */ - int slice_count; + int64_t tex_size; // Texture size + int64_t ctex_size; // Chroma texture size uint8_t *op_data[4]; // Opcodes int64_t op_size[4]; // Opcodes size - - int texture_block_w; - int texture_block_h; - - int ctexture_block_w; - int ctexture_block_h; - - /* Pointer to the selected decompression function */ - int (*tex_funct)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block); - int (*tex_funct_planar[2])(uint8_t *plane0, ptrdiff_t stride0, - uint8_t *plane1, ptrdiff_t stride1, - const uint8_t *block); } DXVContext; -static void decompress_indices(uint8_t *dst, const uint8_t *src) -{ - int block, i; - - for (block = 0; block < 2; block++) { - int tmp = AV_RL24(src); - - /* Unpack 8x3 bit from last 3 byte block */ - for (i = 0; i < 8; i++) - dst[i] = (tmp >> (i * 3)) & 0x7; - - src += 3; - dst += 8; - } -} - -static int extract_component(int yo0, int yo1, int code) -{ - int yo; - - if (yo0 == yo1) { - yo = yo0; - } else if (code == 0) { - yo = yo0; - } else if (code == 1) { - yo = yo1; - } else { - if (yo0 > yo1) { - yo = (uint8_t) (((8 - code) * yo0 + - (code - 1) * yo1) / 7); - } else { - if (code == 6) { - yo = 0; - } else if (code == 7) { - yo = 255; - } else { - yo = (uint8_t) (((6 - code) * yo0 + - (code - 1) * yo1) / 5); - } - } - } - - return yo; -} - -static int cocg_block(uint8_t *plane0, ptrdiff_t stride0, - uint8_t *plane1, ptrdiff_t stride1, - const uint8_t *block) -{ - uint8_t co_indices[16]; - uint8_t cg_indices[16]; - uint8_t co0 = *(block); - uint8_t co1 = *(block + 1); - uint8_t cg0 = *(block + 8); - uint8_t cg1 = *(block + 9); - int x, y; - - decompress_indices(co_indices, block + 2); - decompress_indices(cg_indices, block + 10); - - for (y = 0; y < 4; y++) { - for (x = 0; x < 4; x++) { - int co_code = co_indices[x + y * 4]; - int cg_code = cg_indices[x + y * 4]; - - plane0[x] = extract_component(cg0, cg1, cg_code); - plane1[x] = extract_component(co0, co1, co_code); - } - plane0 += stride0; - plane1 += stride1; - } - - return 16; -} - -static void yao_subblock(uint8_t *dst, uint8_t *yo_indices, - ptrdiff_t stride, const uint8_t *block) -{ - uint8_t yo0 = *(block); - uint8_t yo1 = *(block + 1); - int x, y; - - decompress_indices(yo_indices, block + 2); - - for (y = 0; y < 4; y++) { - for (x = 0; x < 4; x++) { - int yo_code = yo_indices[x + y * 4]; - - dst[x] = extract_component(yo0, yo1, yo_code); - } - dst += stride; - } -} - -static int yo_block(uint8_t *dst, ptrdiff_t stride, - uint8_t *unused0, ptrdiff_t unused1, - const uint8_t *block) -{ - uint8_t yo_indices[16]; - - yao_subblock(dst, yo_indices, stride, block); - yao_subblock(dst + 4, yo_indices, stride, block + 8); - yao_subblock(dst + 8, yo_indices, stride, block + 16); - yao_subblock(dst + 12, yo_indices, stride, block + 24); - - return 32; -} - -static int yao_block(uint8_t *plane0, ptrdiff_t stride0, - uint8_t *plane3, ptrdiff_t stride1, - const uint8_t *block) -{ - uint8_t yo_indices[16]; - uint8_t a_indices[16]; - - yao_subblock(plane0, yo_indices, stride0, block); - yao_subblock(plane3, a_indices, stride1, block + 8); - yao_subblock(plane0 + 4, yo_indices, stride0, block + 16); - yao_subblock(plane3 + 4, a_indices, stride1, block + 24); - yao_subblock(plane0 + 8, yo_indices, stride0, block + 32); - yao_subblock(plane3 + 8, a_indices, stride1, block + 40); - yao_subblock(plane0 + 12, yo_indices, stride0, block + 48); - yao_subblock(plane3 + 12, a_indices, stride1, block + 56); - - return 64; -} - -static int decompress_texture_thread(AVCodecContext *avctx, void *arg, - int slice, int thread_nb) -{ - const DXVContext *ctx = avctx->priv_data; - AVFrame *frame = arg; - const uint8_t *d = ctx->tex_data; - int w_block = avctx->coded_width / ctx->texture_block_w; - int h_block = avctx->coded_height / ctx->texture_block_h; - int x, y; - int start_slice, end_slice; - - start_slice = h_block * slice / ctx->slice_count; - end_slice = h_block * (slice + 1) / ctx->slice_count; - - if (ctx->tex_funct) { - for (y = start_slice; y < end_slice; y++) { - uint8_t *p = frame->data[0] + y * frame->linesize[0] * ctx->texture_block_h; - int off = y * w_block; - for (x = 0; x < w_block; x++) { - ctx->tex_funct(p + x * 4 * ctx->texture_block_w, frame->linesize[0], - d + (off + x) * ctx->tex_step); - } - } - } else { - const uint8_t *c = ctx->ctex_data; - - for (y = start_slice; y < end_slice; y++) { - uint8_t *p0 = frame->data[0] + y * frame->linesize[0] * ctx->texture_block_h; - uint8_t *p3 = ctx->tex_step != 64 ? NULL : frame->data[3] + y * frame->linesize[3] * ctx->texture_block_h; - int off = y * w_block; - for (x = 0; x < w_block; x++) { - ctx->tex_funct_planar[0](p0 + x * ctx->texture_block_w, frame->linesize[0], - p3 != NULL ? p3 + x * ctx->texture_block_w : NULL, frame->linesize[3], - d + (off + x) * ctx->tex_step); - } - } - - w_block = (avctx->coded_width / 2) / ctx->ctexture_block_w; - h_block = (avctx->coded_height / 2) / ctx->ctexture_block_h; - start_slice = h_block * slice / ctx->slice_count; - end_slice = h_block * (slice + 1) / ctx->slice_count; - - for (y = start_slice; y < end_slice; y++) { - uint8_t *p0 = frame->data[1] + y * frame->linesize[1] * ctx->ctexture_block_h; - uint8_t *p1 = frame->data[2] + y * frame->linesize[2] * ctx->ctexture_block_h; - int off = y * w_block; - for (x = 0; x < w_block; x++) { - ctx->tex_funct_planar[1](p0 + x * ctx->ctexture_block_w, frame->linesize[1], - p1 + x * ctx->ctexture_block_w, frame->linesize[2], - c + (off + x) * ctx->ctex_step); - } - } - } - - return 0; -} - /* This scheme addresses already decoded elements depending on 2-bit status: * 0 -> copy new element * 1 -> copy one element from position -x @@ -440,7 +240,7 @@ static int get_opcodes(GetByteContext *gb, uint32_t *table, uint8_t *dst, int op size_in_bits = bytestream2_get_le32(gb); endoffset = ((size_in_bits + 7) >> 3) - 4; - if (endoffset <= 0 || bytestream2_get_bytes_left(gb) < endoffset) + if ((int)endoffset <= 0 || bytestream2_get_bytes_left(gb) < endoffset) return AVERROR_INVALIDDATA; offset = endoffset; @@ -955,7 +755,7 @@ static int dxv_decompress_dxt5(AVCodecContext *avctx) break; case 2: /* Copy two dwords from a previous index */ - idx = 8 + bytestream2_get_le16(gbc); + idx = 8 + 4 * bytestream2_get_le16(gbc); if (idx > pos || (unsigned int)(pos - idx) + 2 > ctx->tex_size / 4) return AVERROR_INVALIDDATA; prev = AV_RL32(ctx->tex_data + 4 * (pos - idx)); @@ -1043,6 +843,7 @@ static int dxv_decode(AVCodecContext *avctx, AVFrame *frame, { DXVContext *ctx = avctx->priv_data; GetByteContext *gbc = &ctx->gbc; + TextureDSPThreadContext texdsp_ctx, ctexdsp_ctx; int (*decompress_tex)(AVCodecContext *avctx); const char *msgcomp, *msgtext; uint32_t tag; @@ -1052,65 +853,51 @@ static int dxv_decode(AVCodecContext *avctx, AVFrame *frame, bytestream2_init(gbc, avpkt->data, avpkt->size); - ctx->texture_block_h = 4; - ctx->texture_block_w = 4; - avctx->pix_fmt = AV_PIX_FMT_RGBA; avctx->colorspace = AVCOL_SPC_RGB; - ctx->tex_funct = NULL; - ctx->tex_funct_planar[0] = NULL; - ctx->tex_funct_planar[1] = NULL; - tag = bytestream2_get_le32(gbc); switch (tag) { - case MKBETAG('D', 'X', 'T', '1'): + case DXV_FMT_DXT1: decompress_tex = dxv_decompress_dxt1; - ctx->tex_funct = ctx->texdsp.dxt1_block; - ctx->tex_rat = 8; - ctx->tex_step = 8; + texdsp_ctx.tex_funct = ctx->texdsp.dxt1_block; + texdsp_ctx.tex_ratio = 8; + texdsp_ctx.raw_ratio = 16; msgcomp = "DXTR1"; msgtext = "DXT1"; break; - case MKBETAG('D', 'X', 'T', '5'): + case DXV_FMT_DXT5: decompress_tex = dxv_decompress_dxt5; - ctx->tex_funct = ctx->texdsp.dxt5_block; - ctx->tex_rat = 4; - ctx->tex_step = 16; + /* DXV misnomers DXT5, alpha is premultiplied so use DXT4 instead */ + texdsp_ctx.tex_funct = ctx->texdsp.dxt4_block; + texdsp_ctx.tex_ratio = 16; + texdsp_ctx.raw_ratio = 16; msgcomp = "DXTR5"; msgtext = "DXT5"; break; - case MKBETAG('Y', 'C', 'G', '6'): + case DXV_FMT_YCG6: decompress_tex = dxv_decompress_ycg6; - ctx->tex_funct_planar[0] = yo_block; - ctx->tex_funct_planar[1] = cocg_block; - ctx->tex_rat = 8; - ctx->tex_step = 32; - ctx->ctex_step = 16; + texdsp_ctx.tex_funct = ctx->texdsp.rgtc1u_gray_block; + texdsp_ctx.tex_ratio = 8; + texdsp_ctx.raw_ratio = 4; + ctexdsp_ctx.tex_funct = ctx->texdsp.rgtc1u_gray_block; + ctexdsp_ctx.tex_ratio = 16; + ctexdsp_ctx.raw_ratio = 4; msgcomp = "YOCOCG6"; msgtext = "YCG6"; - ctx->ctex_size = avctx->coded_width * avctx->coded_height / 4; - ctx->texture_block_h = 4; - ctx->texture_block_w = 16; - ctx->ctexture_block_h = 4; - ctx->ctexture_block_w = 4; avctx->pix_fmt = AV_PIX_FMT_YUV420P; avctx->colorspace = AVCOL_SPC_YCOCG; break; - case MKBETAG('Y', 'G', '1', '0'): + case DXV_FMT_YG10: decompress_tex = dxv_decompress_yg10; - ctx->tex_funct_planar[0] = yao_block; - ctx->tex_funct_planar[1] = cocg_block; - ctx->tex_rat = 4; - ctx->tex_step = 64; - ctx->ctex_step = 16; + texdsp_ctx.tex_funct = ctx->texdsp.rgtc1u_gray_block; + texdsp_ctx.tex_ratio = 16; + texdsp_ctx.raw_ratio = 4; + ctexdsp_ctx.tex_funct = ctx->texdsp.rgtc1u_gray_block; + ctexdsp_ctx.tex_ratio = 16; + ctexdsp_ctx.raw_ratio = 4; msgcomp = "YAOCOCG10"; msgtext = "YG10"; - ctx->ctex_size = avctx->coded_width * avctx->coded_height / 4; - ctx->texture_block_h = 4; - ctx->texture_block_w = 16; - ctx->ctexture_block_h = 4; - ctx->ctexture_block_w = 4; avctx->pix_fmt = AV_PIX_FMT_YUVA420P; avctx->colorspace = AVCOL_SPC_YCOCG; break; @@ -1129,26 +916,30 @@ static int dxv_decode(AVCodecContext *avctx, AVFrame *frame, } if (old_type & 0x40) { + tag = DXV_FMT_DXT5; msgtext = "DXT5"; - ctx->tex_funct = ctx->texdsp.dxt5_block; - ctx->tex_step = 16; + texdsp_ctx.tex_funct = ctx->texdsp.dxt4_block; + texdsp_ctx.tex_ratio = 16; + texdsp_ctx.raw_ratio = 16; } else if (old_type & 0x20 || version_major == 1) { + tag = DXV_FMT_DXT1; msgtext = "DXT1"; - ctx->tex_funct = ctx->texdsp.dxt1_block; - ctx->tex_step = 8; + texdsp_ctx.tex_funct = ctx->texdsp.dxt1_block; + texdsp_ctx.tex_ratio = 8; + texdsp_ctx.raw_ratio = 16; } else { av_log(avctx, AV_LOG_ERROR, "Unsupported header (0x%08"PRIX32")\n.", tag); return AVERROR_INVALIDDATA; } - ctx->tex_rat = 1; break; } - ctx->slice_count = av_clip(avctx->thread_count, 1, - avctx->coded_height / FFMAX(ctx->texture_block_h, - ctx->ctexture_block_h)); + texdsp_ctx.slice_count = av_clip(avctx->thread_count, 1, + avctx->coded_height / TEXTURE_BLOCK_H); + ctexdsp_ctx.slice_count = av_clip(avctx->thread_count, 1, + avctx->coded_height / 2 / TEXTURE_BLOCK_H); /* New header is 12 bytes long. */ if (!old_type) { @@ -1158,7 +949,6 @@ static int dxv_decode(AVCodecContext *avctx, AVFrame *frame, /* Encoder copies texture data when compression is not advantageous. */ if (bytestream2_get_byte(gbc)) { msgcomp = "RAW"; - ctx->tex_rat = 1; decompress_tex = dxv_decompress_raw; } @@ -1176,14 +966,20 @@ static int dxv_decode(AVCodecContext *avctx, AVFrame *frame, return AVERROR_INVALIDDATA; } - ctx->tex_size = avctx->coded_width * avctx->coded_height * 4 / ctx->tex_rat; + ctx->tex_size = avctx->coded_width / (texdsp_ctx.raw_ratio / (avctx->pix_fmt == AV_PIX_FMT_RGBA ? 4 : 1)) * + avctx->coded_height / TEXTURE_BLOCK_H * + texdsp_ctx.tex_ratio; ret = av_reallocp(&ctx->tex_data, ctx->tex_size + AV_INPUT_BUFFER_PADDING_SIZE); if (ret < 0) return ret; - if (ctx->ctex_size) { + if (avctx->pix_fmt != AV_PIX_FMT_RGBA) { int i; + ctx->ctex_size = avctx->coded_width / 2 / ctexdsp_ctx.raw_ratio * + avctx->coded_height / 2 / TEXTURE_BLOCK_H * + ctexdsp_ctx.tex_ratio; + ctx->op_size[0] = avctx->coded_width * avctx->coded_height / 16; ctx->op_size[1] = avctx->coded_width * avctx->coded_height / 32; ctx->op_size[2] = avctx->coded_width * avctx->coded_height / 32; @@ -1203,24 +999,57 @@ static int dxv_decode(AVCodecContext *avctx, AVFrame *frame, ret = decompress_tex(avctx); if (ret < 0) return ret; - { - int w_block = avctx->coded_width / ctx->texture_block_w; - int h_block = avctx->coded_height / ctx->texture_block_h; - if (w_block * h_block * ctx->tex_step > ctx->tex_size * 8LL) - return AVERROR_INVALIDDATA; - } ret = ff_thread_get_buffer(avctx, frame, 0); if (ret < 0) return ret; - /* Now decompress the texture with the standard functions. */ - avctx->execute2(avctx, decompress_texture_thread, - frame, NULL, ctx->slice_count); + texdsp_ctx.width = avctx->coded_width; + texdsp_ctx.height = avctx->coded_height; + ctexdsp_ctx.width = avctx->coded_width / 2; + ctexdsp_ctx.height = avctx->coded_height / 2; + switch (tag) { + case DXV_FMT_YG10: + /* BC5 texture with alpha in the second half of each block */ + texdsp_ctx.tex_data.in = ctx->tex_data + texdsp_ctx.tex_ratio / 2; + texdsp_ctx.frame_data.out = frame->data[3]; + texdsp_ctx.stride = frame->linesize[3]; + ret = ff_texturedsp_exec_decompress_threads(avctx, &texdsp_ctx); + if (ret < 0) + return ret; + /* fallthrough */ + case DXV_FMT_YCG6: + /* BC5 texture with Co in the first half of each block and Cg in the second */ + ctexdsp_ctx.tex_data.in = ctx->ctex_data; + ctexdsp_ctx.frame_data.out = frame->data[2]; + ctexdsp_ctx.stride = frame->linesize[2]; + ret = ff_texturedsp_exec_decompress_threads(avctx, &ctexdsp_ctx); + if (ret < 0) + return ret; + ctexdsp_ctx.tex_data.in = ctx->ctex_data + ctexdsp_ctx.tex_ratio / 2; + ctexdsp_ctx.frame_data.out = frame->data[1]; + ctexdsp_ctx.stride = frame->linesize[1]; + ret = ff_texturedsp_exec_decompress_threads(avctx, &ctexdsp_ctx); + if (ret < 0) + return ret; + /* fallthrough */ + case DXV_FMT_DXT1: + case DXV_FMT_DXT5: + /* For DXT1 and DXT5, self explanatory + * For YCG6, BC4 texture for Y + * For YG10, BC5 texture with Y in the first half of each block */ + texdsp_ctx.tex_data.in = ctx->tex_data; + texdsp_ctx.frame_data.out = frame->data[0]; + texdsp_ctx.stride = frame->linesize[0]; + ret = ff_texturedsp_exec_decompress_threads(avctx, &texdsp_ctx); + if (ret < 0) + return ret; + break; + } /* Frame is ready to be output. */ frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; @@ -1237,9 +1066,9 @@ static int dxv_init(AVCodecContext *avctx) return ret; } - /* Codec requires 16x16 alignment. */ - avctx->coded_width = FFALIGN(avctx->width, 16); - avctx->coded_height = FFALIGN(avctx->height, 16); + /* Since codec is based on 4x4 blocks, size is aligned to 4 */ + avctx->coded_width = FFALIGN(avctx->width, TEXTURE_BLOCK_W); + avctx->coded_height = FFALIGN(avctx->height, TEXTURE_BLOCK_H); ff_texturedsp_init(&ctx->texdsp); diff --git a/libavcodec/dxv.h b/libavcodec/dxv.h new file mode 100644 index 00000000000..71cfddec858 --- /dev/null +++ b/libavcodec/dxv.h @@ -0,0 +1,34 @@ +/* + * Resolume DXV common + * Copyright (C) 2024 Connor Worley + * + * 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 + */ + +#ifndef AVCODEC_DXV_H +#define AVCODEC_DXV_H + +#include "libavutil/macros.h" + +typedef enum DXVTextureFormat { + DXV_FMT_DXT1 = MKBETAG('D', 'X', 'T', '1'), + DXV_FMT_DXT5 = MKBETAG('D', 'X', 'T', '5'), + DXV_FMT_YCG6 = MKBETAG('Y', 'C', 'G', '6'), + DXV_FMT_YG10 = MKBETAG('Y', 'G', '1', '0'), +} DXVTextureFormat; + +#endif /* AVCODEC_DXV_H */ diff --git a/libavcodec/dxva2.c b/libavcodec/dxva2.c index 568d686f39a..59025633f7e 100644 --- a/libavcodec/dxva2.c +++ b/libavcodec/dxva2.c @@ -20,10 +20,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include #include #include +#include "libavutil/avassert.h" #include "libavutil/common.h" #include "libavutil/log.h" #include "libavutil/time.h" @@ -53,28 +53,28 @@ DEFINE_GUID(ff_IID_IDirectXVideoDecoderService, 0xfc51a551,0xd5e7,0x11d9,0xaf,0x typedef struct dxva_mode { const GUID *guid; enum AVCodecID codec; - // List of supported profiles, terminated by a FF_PROFILE_UNKNOWN entry. + // List of supported profiles, terminated by a AV_PROFILE_UNKNOWN entry. // If NULL, don't check profile. const int *profiles; } dxva_mode; -static const int prof_mpeg2_main[] = {FF_PROFILE_MPEG2_SIMPLE, - FF_PROFILE_MPEG2_MAIN, - FF_PROFILE_UNKNOWN}; -static const int prof_h264_high[] = {FF_PROFILE_H264_CONSTRAINED_BASELINE, - FF_PROFILE_H264_MAIN, - FF_PROFILE_H264_HIGH, - FF_PROFILE_UNKNOWN}; -static const int prof_hevc_main[] = {FF_PROFILE_HEVC_MAIN, - FF_PROFILE_UNKNOWN}; -static const int prof_hevc_main10[] = {FF_PROFILE_HEVC_MAIN_10, - FF_PROFILE_UNKNOWN}; -static const int prof_vp9_profile0[] = {FF_PROFILE_VP9_0, - FF_PROFILE_UNKNOWN}; -static const int prof_vp9_profile2[] = {FF_PROFILE_VP9_2, - FF_PROFILE_UNKNOWN}; -static const int prof_av1_profile0[] = {FF_PROFILE_AV1_MAIN, - FF_PROFILE_UNKNOWN}; +static const int prof_mpeg2_main[] = {AV_PROFILE_MPEG2_SIMPLE, + AV_PROFILE_MPEG2_MAIN, + AV_PROFILE_UNKNOWN}; +static const int prof_h264_high[] = {AV_PROFILE_H264_CONSTRAINED_BASELINE, + AV_PROFILE_H264_MAIN, + AV_PROFILE_H264_HIGH, + AV_PROFILE_UNKNOWN}; +static const int prof_hevc_main[] = {AV_PROFILE_HEVC_MAIN, + AV_PROFILE_UNKNOWN}; +static const int prof_hevc_main10[] = {AV_PROFILE_HEVC_MAIN_10, + AV_PROFILE_UNKNOWN}; +static const int prof_vp9_profile0[] = {AV_PROFILE_VP9_0, + AV_PROFILE_UNKNOWN}; +static const int prof_vp9_profile2[] = {AV_PROFILE_VP9_2, + AV_PROFILE_UNKNOWN}; +static const int prof_av1_profile0[] = {AV_PROFILE_AV1_MAIN, + AV_PROFILE_UNKNOWN}; static const dxva_mode dxva_modes[] = { /* MPEG-2 */ @@ -199,7 +199,7 @@ static int dxva_check_codec_compatibility(AVCodecContext *avctx, const dxva_mode if (mode->profiles && !(avctx->hwaccel_flags & AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH)) { int i, found = 0; - for (i = 0; mode->profiles[i] != FF_PROFILE_UNKNOWN; i++) { + for (i = 0; mode->profiles[i] != AV_PROFILE_UNKNOWN; i++) { if (avctx->profile == mode->profiles[i]) { found = 1; break; @@ -768,12 +768,17 @@ static void *get_surface(const AVCodecContext *avctx, const AVFrame *frame) } unsigned ff_dxva2_get_surface_index(const AVCodecContext *avctx, - const AVDXVAContext *ctx, - const AVFrame *frame) + AVDXVAContext *ctx, const AVFrame *frame, + int curr) { void *surface = get_surface(avctx, frame); unsigned i; +#if CONFIG_D3D12VA + if (avctx->pix_fmt == AV_PIX_FMT_D3D12) { + return ff_d3d12va_get_surface_index(avctx, (D3D12VADecodeContext *)ctx, frame, curr); + } +#endif #if CONFIG_D3D11VA if (avctx->pix_fmt == AV_PIX_FMT_D3D11) return (intptr_t)frame->data[1]; @@ -790,7 +795,7 @@ unsigned ff_dxva2_get_surface_index(const AVCodecContext *avctx, } #endif - assert(0); + av_log((AVCodecContext *)avctx, AV_LOG_WARNING, "Could not get surface index. Using 0 instead.\n"); return 0; } @@ -1007,7 +1012,7 @@ int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, /* TODO Film Grain when possible */ - assert(buffer_count == 1 + (qm_size > 0) + 2); + av_assert0(buffer_count == 1 + (qm_size > 0) + 2); #if CONFIG_D3D11VA if (ff_dxva2_is_d3d11(avctx)) @@ -1056,3 +1061,23 @@ int ff_dxva2_is_d3d11(const AVCodecContext *avctx) else return 0; } + +unsigned *ff_dxva2_get_report_id(const AVCodecContext *avctx, AVDXVAContext *ctx) +{ + unsigned *report_id = NULL; + +#if CONFIG_D3D12VA + if (avctx->pix_fmt == AV_PIX_FMT_D3D12) + report_id = &ctx->d3d12va.report_id; +#endif +#if CONFIG_D3D11VA + if (ff_dxva2_is_d3d11(avctx)) + report_id = &ctx->d3d11va.report_id; +#endif +#if CONFIG_DXVA2 + if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) + report_id = &ctx->dxva2.report_id; +#endif + + return report_id; +} diff --git a/libavcodec/dxva2.h b/libavcodec/dxva2.h index 22c93992f22..bdec6112e9d 100644 --- a/libavcodec/dxva2.h +++ b/libavcodec/dxva2.h @@ -45,9 +45,6 @@ * @{ */ -#define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for DXVA2 and old UVD/UVD+ ATI video cards -#define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO 2 ///< Work around for DXVA2 and old Intel GPUs with ClearVideo interface - /** * This structure is used to provides the necessary configurations and data * to the DXVA2 FFmpeg HWAccel implementation. diff --git a/libavcodec/dxva2_av1.c b/libavcodec/dxva2_av1.c index 228f72ba18e..85d747bca8f 100644 --- a/libavcodec/dxva2_av1.c +++ b/libavcodec/dxva2_av1.c @@ -27,6 +27,7 @@ #include "dxva2_internal.h" #include "av1dec.h" +#include "hwaccel_internal.h" #define MAX_TILES 256 @@ -55,10 +56,11 @@ static int get_bit_depth_from_seq(const AV1RawSequenceHeader *seq) return 8; } -static int fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, const AV1DecContext *h, +int ff_dxva2_av1_fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_PicParams_AV1 *pp) { int i,j, uses_lr; + const AV1DecContext *h = avctx->priv_data; const AV1RawSequenceHeader *seq = h->raw_seq; const AV1RawFrameHeader *frame_header = h->raw_frame_header; const AV1RawFilmGrainParams *film_grain = &h->cur_frame.film_grain; @@ -74,7 +76,6 @@ static int fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *c pp->max_width = seq->max_frame_width_minus_1 + 1; pp->max_height = seq->max_frame_height_minus_1 + 1; - pp->CurrPicTextureIndex = ff_dxva2_get_surface_index(avctx, ctx, h->cur_frame.f); pp->superres_denom = frame_header->use_superres ? frame_header->coded_denom + AV1_SUPERRES_DENOM_MIN : AV1_SUPERRES_NUM; pp->bitdepth = get_bit_depth_from_seq(seq); pp->seq_profile = seq->seq_profile; @@ -150,9 +151,11 @@ static int fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *c for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { AVFrame *ref_frame = h->ref[i].f; if (ref_frame->buf[0]) - pp->RefFrameMapTextureIndex[i] = ff_dxva2_get_surface_index(avctx, ctx, ref_frame); + pp->RefFrameMapTextureIndex[i] = ff_dxva2_get_surface_index(avctx, ctx, ref_frame, 0); } + pp->CurrPicTextureIndex = ff_dxva2_get_surface_index(avctx, ctx, h->cur_frame.f, 1); + /* Loop filter parameters */ pp->loop_filter.filter_level[0] = frame_header->loop_filter_level[0]; pp->loop_filter.filter_level[1] = frame_header->loop_filter_level[1]; @@ -280,7 +283,7 @@ static int dxva2_av1_start_frame(AVCodecContext *avctx, av_assert0(ctx_pic); /* Fill up DXVA_PicParams_AV1 */ - if (fill_picture_parameters(avctx, ctx, h, &ctx_pic->pp) < 0) + if (ff_dxva2_av1_fill_picture_parameters(avctx, ctx, &ctx_pic->pp) < 0) return -1; ctx_pic->bitstream_size = 0; @@ -457,11 +460,11 @@ static int dxva2_av1_uninit(AVCodecContext *avctx) } #if CONFIG_AV1_DXVA2_HWACCEL -const AVHWAccel ff_av1_dxva2_hwaccel = { - .name = "av1_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_AV1, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_av1_dxva2_hwaccel = { + .p.name = "av1_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = dxva2_av1_uninit, .start_frame = dxva2_av1_start_frame, @@ -474,11 +477,11 @@ const AVHWAccel ff_av1_dxva2_hwaccel = { #endif #if CONFIG_AV1_D3D11VA_HWACCEL -const AVHWAccel ff_av1_d3d11va_hwaccel = { - .name = "av1_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_AV1, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_av1_d3d11va_hwaccel = { + .p.name = "av1_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = dxva2_av1_uninit, .start_frame = dxva2_av1_start_frame, @@ -491,11 +494,11 @@ const AVHWAccel ff_av1_d3d11va_hwaccel = { #endif #if CONFIG_AV1_D3D11VA2_HWACCEL -const AVHWAccel ff_av1_d3d11va2_hwaccel = { - .name = "av1_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_AV1, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_av1_d3d11va2_hwaccel = { + .p.name = "av1_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = dxva2_av1_uninit, .start_frame = dxva2_av1_start_frame, diff --git a/libavcodec/dxva2_h264.c b/libavcodec/dxva2_h264.c index 6300b1418df..0fe41526251 100644 --- a/libavcodec/dxva2_h264.c +++ b/libavcodec/dxva2_h264.c @@ -28,6 +28,7 @@ #include "h264dec.h" #include "h264data.h" #include "h264_ps.h" +#include "hwaccel_internal.h" #include "mpegutils.h" struct dxva2_picture_context { @@ -43,23 +44,20 @@ struct dxva2_picture_context { static void fill_picture_entry(DXVA_PicEntry_H264 *pic, unsigned index, unsigned flag) { - assert((index&0x7f) == index && (flag&0x01) == flag); + av_assert0((index&0x7f) == index && (flag&0x01) == flag); pic->bPicEntry = index | (flag << 7); } -static void fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, const H264Context *h, +void ff_dxva2_h264_fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_PicParams_H264 *pp) { + const H264Context *h = avctx->priv_data; const H264Picture *current_picture = h->cur_pic_ptr; const SPS *sps = h->ps.sps; const PPS *pps = h->ps.pps; int i, j; memset(pp, 0, sizeof(*pp)); - /* Configure current picture */ - fill_picture_entry(&pp->CurrPic, - ff_dxva2_get_surface_index(avctx, ctx, current_picture->f), - h->picture_structure == PICT_BOTTOM_FIELD); /* Configure the set of references */ pp->UsedForReferenceFlags = 0; pp->NonExistingFrameFlags = 0; @@ -74,7 +72,7 @@ static void fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext * } if (r) { fill_picture_entry(&pp->RefFrameList[i], - ff_dxva2_get_surface_index(avctx, ctx, r->f), + ff_dxva2_get_surface_index(avctx, ctx, r->f, 0), r->long_ref != 0); if ((r->reference & PICT_TOP_FIELD) && r->field_poc[0] != INT_MAX) @@ -94,6 +92,10 @@ static void fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext * pp->FrameNumList[i] = 0; } } + /* Configure current picture */ + fill_picture_entry(&pp->CurrPic, + ff_dxva2_get_surface_index(avctx, ctx, current_picture->f, 1), + h->picture_structure == PICT_BOTTOM_FIELD); pp->wFrameWidthInMbsMinus1 = h->mb_width - 1; pp->wFrameHeightInMbsMinus1 = h->mb_height - 1; @@ -163,9 +165,10 @@ static void fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext * //pp->SliceGroupMap[810]; /* XXX not implemented by FFmpeg */ } -static void fill_scaling_lists(const AVCodecContext *avctx, AVDXVAContext *ctx, const H264Context *h, DXVA_Qmatrix_H264 *qm) +void ff_dxva2_h264_fill_scaling_lists(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_Qmatrix_H264 *qm) { - const PPS *pps = h->ps.pps; + const H264Context *h = avctx->priv_data; + const PPS *pps = h->ps.pps; unsigned i, j; memset(qm, 0, sizeof(*qm)); if (DXVA_CONTEXT_WORKAROUND(avctx, ctx) & FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG) { @@ -191,8 +194,8 @@ static void fill_scaling_lists(const AVCodecContext *avctx, AVDXVAContext *ctx, static int is_slice_short(const AVCodecContext *avctx, AVDXVAContext *ctx) { - assert(DXVA_CONTEXT_CFG_BITSTREAM(avctx, ctx) == 1 || - DXVA_CONTEXT_CFG_BITSTREAM(avctx, ctx) == 2); + av_assert0(DXVA_CONTEXT_CFG_BITSTREAM(avctx, ctx) == 1 || + DXVA_CONTEXT_CFG_BITSTREAM(avctx, ctx) == 2); return DXVA_CONTEXT_CFG_BITSTREAM(avctx, ctx) == 2; } @@ -252,9 +255,9 @@ static void fill_slice_long(AVCodecContext *avctx, DXVA_Slice_H264_Long *slice, unsigned plane; unsigned index; if (DXVA_CONTEXT_WORKAROUND(avctx, ctx) & FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO) - index = ff_dxva2_get_surface_index(avctx, ctx, r->f); + index = ff_dxva2_get_surface_index(avctx, ctx, r->f, 0); else - index = get_refpic_index(pp, ff_dxva2_get_surface_index(avctx, ctx, r->f)); + index = get_refpic_index(pp, ff_dxva2_get_surface_index(avctx, ctx, r->f, 0)); fill_picture_entry(&slice->RefPicList[list][i], index, sl->ref_list[list][i].reference == PICT_BOTTOM_FIELD); for (plane = 0; plane < 3; plane++) { @@ -345,10 +348,10 @@ static int commit_bitstream_and_slice_buffer(AVCodecContext *avctx, static const unsigned start_code_size = sizeof(start_code); unsigned position, size; - assert(offsetof(DXVA_Slice_H264_Short, BSNALunitDataLocation) == - offsetof(DXVA_Slice_H264_Long, BSNALunitDataLocation)); - assert(offsetof(DXVA_Slice_H264_Short, SliceBytesInBuffer) == - offsetof(DXVA_Slice_H264_Long, SliceBytesInBuffer)); + av_assert0(offsetof(DXVA_Slice_H264_Short, BSNALunitDataLocation) == + offsetof(DXVA_Slice_H264_Long, BSNALunitDataLocation)); + av_assert0(offsetof(DXVA_Slice_H264_Short, SliceBytesInBuffer) == + offsetof(DXVA_Slice_H264_Long, SliceBytesInBuffer)); if (is_slice_short(avctx, ctx)) slice = &ctx_pic->slice_short[i]; @@ -450,13 +453,13 @@ static int dxva2_h264_start_frame(AVCodecContext *avctx, if (!DXVA_CONTEXT_VALID(avctx, ctx)) return -1; - assert(ctx_pic); + av_assert0(ctx_pic); /* Fill up DXVA_PicParams_H264 */ - fill_picture_parameters(avctx, ctx, h, &ctx_pic->pp); + ff_dxva2_h264_fill_picture_parameters(avctx, ctx, &ctx_pic->pp); /* Fill up DXVA_Qmatrix_H264 */ - fill_scaling_lists(avctx, ctx, h, &ctx_pic->qm); + ff_dxva2_h264_fill_scaling_lists(avctx, ctx, &ctx_pic->qm); ctx_pic->slice_count = 0; ctx_pic->bitstream_size = 0; @@ -516,11 +519,11 @@ static int dxva2_h264_end_frame(AVCodecContext *avctx) } #if CONFIG_H264_DXVA2_HWACCEL -const AVHWAccel ff_h264_dxva2_hwaccel = { - .name = "h264_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_h264_dxva2_hwaccel = { + .p.name = "h264_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_h264_start_frame, @@ -533,11 +536,11 @@ const AVHWAccel ff_h264_dxva2_hwaccel = { #endif #if CONFIG_H264_D3D11VA_HWACCEL -const AVHWAccel ff_h264_d3d11va_hwaccel = { - .name = "h264_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_h264_d3d11va_hwaccel = { + .p.name = "h264_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_h264_start_frame, @@ -550,11 +553,11 @@ const AVHWAccel ff_h264_d3d11va_hwaccel = { #endif #if CONFIG_H264_D3D11VA2_HWACCEL -const AVHWAccel ff_h264_d3d11va2_hwaccel = { - .name = "h264_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_h264_d3d11va2_hwaccel = { + .p.name = "h264_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_h264_start_frame, diff --git a/libavcodec/dxva2_hevc.c b/libavcodec/dxva2_hevc.c index 6b239d9917c..31d74a7164d 100644 --- a/libavcodec/dxva2_hevc.c +++ b/libavcodec/dxva2_hevc.c @@ -27,6 +27,7 @@ #include "dxva2_internal.h" #include "hevc_data.h" #include "hevcdec.h" +#include "hwaccel_internal.h" #define MAX_SLICES 256 @@ -56,9 +57,10 @@ static int get_refpic_index(const DXVA_PicParams_HEVC *pp, int surface_index) return 0xff; } -static void fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, const HEVCContext *h, +void ff_dxva2_hevc_fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_PicParams_HEVC *pp) { + const HEVCContext *h = avctx->priv_data; const HEVCFrame *current_picture = h->ref; const HEVCSPS *sps = h->ps.sps; const HEVCPPS *pps = h->ps.pps; @@ -78,8 +80,6 @@ static void fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext * (0 << 14) | (0 << 15); - fill_picture_entry(&pp->CurrPic, ff_dxva2_get_surface_index(avctx, ctx, current_picture->frame), 0); - pp->sps_max_dec_pic_buffering_minus1 = sps->temporal_layer[sps->max_sub_layers - 1].max_dec_pic_buffering - 1; pp->log2_min_luma_coding_block_size_minus3 = sps->log2_min_cb_size - 3; pp->log2_diff_max_min_luma_coding_block_size = sps->log2_diff_max_min_coding_block_size; @@ -170,7 +170,7 @@ static void fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext * } if (frame) { - fill_picture_entry(&pp->RefPicList[i], ff_dxva2_get_surface_index(avctx, ctx, frame->frame), !!(frame->flags & HEVC_FRAME_FLAG_LONG_REF)); + fill_picture_entry(&pp->RefPicList[i], ff_dxva2_get_surface_index(avctx, ctx, frame->frame, 0), !!(frame->flags & HEVC_FRAME_FLAG_LONG_REF)); pp->PicOrderCntValList[i] = frame->poc; } else { pp->RefPicList[i].bPicEntry = 0xff; @@ -178,6 +178,8 @@ static void fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext * } } + fill_picture_entry(&pp->CurrPic, ff_dxva2_get_surface_index(avctx, ctx, current_picture->frame, 1), 0); + #define DO_REF_LIST(ref_idx, ref_list) { \ const RefPicList *rpl = &h->rps[ref_idx]; \ for (i = 0, j = 0; i < FF_ARRAY_ELEMS(pp->ref_list); i++) { \ @@ -185,7 +187,7 @@ static void fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext * while (!frame && j < rpl->nb_refs) \ frame = rpl->ref[j++]; \ if (frame && frame->flags & (HEVC_FRAME_FLAG_LONG_REF | HEVC_FRAME_FLAG_SHORT_REF)) \ - pp->ref_list[i] = get_refpic_index(pp, ff_dxva2_get_surface_index(avctx, ctx, frame->frame)); \ + pp->ref_list[i] = get_refpic_index(pp, ff_dxva2_get_surface_index(avctx, ctx, frame->frame, 0)); \ else \ pp->ref_list[i] = 0xff; \ } \ @@ -199,8 +201,9 @@ static void fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext * pp->StatusReportFeedbackNumber = 1 + DXVA_CONTEXT_REPORT_ID(avctx, ctx)++; } -static void fill_scaling_lists(AVDXVAContext *ctx, const HEVCContext *h, DXVA_Qmatrix_HEVC *qm) +void ff_dxva2_hevc_fill_scaling_lists(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_Qmatrix_HEVC *qm) { + const HEVCContext *h = avctx->priv_data; unsigned i, j, pos; const ScalingList *sl = h->ps.pps->scaling_list_data_present_flag ? &h->ps.pps->scaling_list : &h->ps.sps->scaling_list; @@ -368,10 +371,10 @@ static int dxva2_hevc_start_frame(AVCodecContext *avctx, av_assert0(ctx_pic); /* Fill up DXVA_PicParams_HEVC */ - fill_picture_parameters(avctx, ctx, h, &ctx_pic->pp); + ff_dxva2_hevc_fill_picture_parameters(avctx, ctx, &ctx_pic->pp); /* Fill up DXVA_Qmatrix_HEVC */ - fill_scaling_lists(ctx, h, &ctx_pic->qm); + ff_dxva2_hevc_fill_scaling_lists(avctx, ctx, &ctx_pic->qm); ctx_pic->slice_count = 0; ctx_pic->bitstream_size = 0; @@ -420,11 +423,11 @@ static int dxva2_hevc_end_frame(AVCodecContext *avctx) } #if CONFIG_HEVC_DXVA2_HWACCEL -const AVHWAccel ff_hevc_dxva2_hwaccel = { - .name = "hevc_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_hevc_dxva2_hwaccel = { + .p.name = "hevc_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_hevc_start_frame, @@ -437,11 +440,11 @@ const AVHWAccel ff_hevc_dxva2_hwaccel = { #endif #if CONFIG_HEVC_D3D11VA_HWACCEL -const AVHWAccel ff_hevc_d3d11va_hwaccel = { - .name = "hevc_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_hevc_d3d11va_hwaccel = { + .p.name = "hevc_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_hevc_start_frame, @@ -454,11 +457,11 @@ const AVHWAccel ff_hevc_d3d11va_hwaccel = { #endif #if CONFIG_HEVC_D3D11VA2_HWACCEL -const AVHWAccel ff_hevc_d3d11va2_hwaccel = { - .name = "hevc_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_hevc_d3d11va2_hwaccel = { + .p.name = "hevc_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_hevc_start_frame, diff --git a/libavcodec/dxva2_internal.h b/libavcodec/dxva2_internal.h index b822af59cdc..224a867ebc5 100644 --- a/libavcodec/dxva2_internal.h +++ b/libavcodec/dxva2_internal.h @@ -26,18 +26,34 @@ #define COBJMACROS #include "config.h" +#include "config_components.h" /* define the proper COM entries before forcing desktop APIs */ #include +#define FF_DXVA2_WORKAROUND_SCALING_LIST_ZIGZAG 1 ///< Work around for DXVA2/Direct3D11 and old UVD/UVD+ ATI video cards +#define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO 2 ///< Work around for DXVA2/Direct3D11 and old Intel GPUs with ClearVideo interface + #if CONFIG_DXVA2 #include "dxva2.h" #include "libavutil/hwcontext_dxva2.h" +#define DXVA2_VAR(ctx, var) ctx->dxva2.var +#else +#define DXVA2_VAR(ctx, var) 0 #endif + #if CONFIG_D3D11VA #include "d3d11va.h" #include "libavutil/hwcontext_d3d11va.h" +#define D3D11VA_VAR(ctx, var) ctx->d3d11va.var +#else +#define D3D11VA_VAR(ctx, var) 0 +#endif + +#if CONFIG_D3D12VA +#include "d3d12va_decode.h" #endif + #if HAVE_DXVA_H /* When targeting WINAPI_FAMILY_PHONE_APP or WINAPI_FAMILY_APP, dxva.h * defines nothing. Force the struct definitions to be visible. */ @@ -62,6 +78,9 @@ typedef union { #if CONFIG_DXVA2 struct dxva_context dxva2; #endif +#if CONFIG_D3D12VA + struct D3D12VADecodeContext d3d12va; +#endif } AVDXVAContext; typedef struct FFDXVASharedContext { @@ -101,43 +120,28 @@ typedef struct FFDXVASharedContext { #define D3D11VA_CONTEXT(ctx) (&ctx->d3d11va) #define DXVA2_CONTEXT(ctx) (&ctx->dxva2) -#if CONFIG_D3D11VA && CONFIG_DXVA2 -#define DXVA_CONTEXT_WORKAROUND(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? ctx->d3d11va.workaround : ctx->dxva2.workaround) -#define DXVA_CONTEXT_COUNT(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? ctx->d3d11va.surface_count : ctx->dxva2.surface_count) -#define DXVA_CONTEXT_DECODER(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? (void *)ctx->d3d11va.decoder : (void *)ctx->dxva2.decoder) -#define DXVA_CONTEXT_REPORT_ID(avctx, ctx) (*(ff_dxva2_is_d3d11(avctx) ? &ctx->d3d11va.report_id : &ctx->dxva2.report_id)) -#define DXVA_CONTEXT_CFG(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? (void *)ctx->d3d11va.cfg : (void *)ctx->dxva2.cfg) -#define DXVA_CONTEXT_CFG_BITSTREAM(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? ctx->d3d11va.cfg->ConfigBitstreamRaw : ctx->dxva2.cfg->ConfigBitstreamRaw) -#define DXVA_CONTEXT_CFG_INTRARESID(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? ctx->d3d11va.cfg->ConfigIntraResidUnsigned : ctx->dxva2.cfg->ConfigIntraResidUnsigned) -#define DXVA_CONTEXT_CFG_RESIDACCEL(avctx, ctx) (ff_dxva2_is_d3d11(avctx) ? ctx->d3d11va.cfg->ConfigResidDiffAccelerator : ctx->dxva2.cfg->ConfigResidDiffAccelerator) +#define DXVA2_CONTEXT_VAR(avctx, ctx, var) (avctx->pix_fmt == AV_PIX_FMT_D3D12 ? 0 : (ff_dxva2_is_d3d11(avctx) ? D3D11VA_VAR(ctx, var) : DXVA2_VAR(ctx, var))) + +#define DXVA_CONTEXT_REPORT_ID(avctx, ctx) (*ff_dxva2_get_report_id(avctx, ctx)) +#define DXVA_CONTEXT_WORKAROUND(avctx, ctx) DXVA2_CONTEXT_VAR(avctx, ctx, workaround) +#define DXVA_CONTEXT_COUNT(avctx, ctx) DXVA2_CONTEXT_VAR(avctx, ctx, surface_count) +#define DXVA_CONTEXT_DECODER(avctx, ctx) (avctx->pix_fmt == AV_PIX_FMT_D3D12 ? 0 : (ff_dxva2_is_d3d11(avctx) ? (void *)D3D11VA_VAR(ctx, decoder) : (void *)DXVA2_VAR(ctx, decoder))) +#define DXVA_CONTEXT_CFG(avctx, ctx) (avctx->pix_fmt == AV_PIX_FMT_D3D12 ? 0 : (ff_dxva2_is_d3d11(avctx) ? (void *)D3D11VA_VAR(ctx, cfg) : (void *)DXVA2_VAR(ctx, cfg))) +#define DXVA_CONTEXT_CFG_BITSTREAM(avctx, ctx) DXVA2_CONTEXT_VAR(avctx, ctx, cfg->ConfigBitstreamRaw) +#define DXVA_CONTEXT_CFG_INTRARESID(avctx, ctx) DXVA2_CONTEXT_VAR(avctx, ctx, cfg->ConfigIntraResidUnsigned) +#define DXVA_CONTEXT_CFG_RESIDACCEL(avctx, ctx) DXVA2_CONTEXT_VAR(avctx, ctx, cfg->ConfigResidDiffAccelerator) #define DXVA_CONTEXT_VALID(avctx, ctx) (DXVA_CONTEXT_DECODER(avctx, ctx) && \ DXVA_CONTEXT_CFG(avctx, ctx) && \ - (ff_dxva2_is_d3d11(avctx) || ctx->dxva2.surface_count)) -#elif CONFIG_DXVA2 -#define DXVA_CONTEXT_WORKAROUND(avctx, ctx) (ctx->dxva2.workaround) -#define DXVA_CONTEXT_COUNT(avctx, ctx) (ctx->dxva2.surface_count) -#define DXVA_CONTEXT_DECODER(avctx, ctx) (ctx->dxva2.decoder) -#define DXVA_CONTEXT_REPORT_ID(avctx, ctx) (*(&ctx->dxva2.report_id)) -#define DXVA_CONTEXT_CFG(avctx, ctx) (ctx->dxva2.cfg) -#define DXVA_CONTEXT_CFG_BITSTREAM(avctx, ctx) (ctx->dxva2.cfg->ConfigBitstreamRaw) -#define DXVA_CONTEXT_CFG_INTRARESID(avctx, ctx) (ctx->dxva2.cfg->ConfigIntraResidUnsigned) -#define DXVA_CONTEXT_CFG_RESIDACCEL(avctx, ctx) (ctx->dxva2.cfg->ConfigResidDiffAccelerator) -#define DXVA_CONTEXT_VALID(avctx, ctx) (ctx->dxva2.decoder && ctx->dxva2.cfg && ctx->dxva2.surface_count) -#elif CONFIG_D3D11VA -#define DXVA_CONTEXT_WORKAROUND(avctx, ctx) (ctx->d3d11va.workaround) -#define DXVA_CONTEXT_COUNT(avctx, ctx) (ctx->d3d11va.surface_count) -#define DXVA_CONTEXT_DECODER(avctx, ctx) (ctx->d3d11va.decoder) -#define DXVA_CONTEXT_REPORT_ID(avctx, ctx) (*(&ctx->d3d11va.report_id)) -#define DXVA_CONTEXT_CFG(avctx, ctx) (ctx->d3d11va.cfg) -#define DXVA_CONTEXT_CFG_BITSTREAM(avctx, ctx) (ctx->d3d11va.cfg->ConfigBitstreamRaw) -#define DXVA_CONTEXT_CFG_INTRARESID(avctx, ctx) (ctx->d3d11va.cfg->ConfigIntraResidUnsigned) -#define DXVA_CONTEXT_CFG_RESIDACCEL(avctx, ctx) (ctx->d3d11va.cfg->ConfigResidDiffAccelerator) -#define DXVA_CONTEXT_VALID(avctx, ctx) (ctx->d3d11va.decoder && ctx->d3d11va.cfg) + (ff_dxva2_is_d3d11(avctx) || DXVA2_VAR(ctx, surface_count))) + +#if CONFIG_D3D12VA +unsigned ff_d3d12va_get_surface_index(const AVCodecContext *avctx, + D3D12VADecodeContext *ctx, const AVFrame *frame, + int curr); #endif unsigned ff_dxva2_get_surface_index(const AVCodecContext *avctx, - const AVDXVAContext *, - const AVFrame *frame); + AVDXVAContext *, const AVFrame *frame, int curr); int ff_dxva2_commit_buffer(AVCodecContext *, AVDXVAContext *, DECODER_BUFFER_DESC *, @@ -161,4 +165,30 @@ int ff_dxva2_common_frame_params(AVCodecContext *avctx, int ff_dxva2_is_d3d11(const AVCodecContext *avctx); +unsigned *ff_dxva2_get_report_id(const AVCodecContext *avctx, AVDXVAContext *ctx); + +void ff_dxva2_h264_fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_PicParams_H264 *pp); + +void ff_dxva2_h264_fill_scaling_lists(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_Qmatrix_H264 *qm); + +void ff_dxva2_hevc_fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_PicParams_HEVC *pp); + +void ff_dxva2_hevc_fill_scaling_lists(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_Qmatrix_HEVC *qm); + +int ff_dxva2_vp9_fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_PicParams_VP9 *pp); + +#if CONFIG_AV1_D3D12VA_HWACCEL || CONFIG_AV1_D3D11VA_HWACCEL || CONFIG_AV1_D3D11VA2_HWACCEL || CONFIG_AV1_DXVA2_HWACCEL +int ff_dxva2_av1_fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_PicParams_AV1 *pp); +#endif + +void ff_dxva2_mpeg2_fill_picture_parameters(AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_PictureParameters *pp); + +void ff_dxva2_mpeg2_fill_quantization_matrices(AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_QmatrixData *qm); + +void ff_dxva2_mpeg2_fill_slice(AVCodecContext *avctx, DXVA_SliceInfo *slice, unsigned position, const uint8_t *buffer, unsigned size); + +void ff_dxva2_vc1_fill_picture_parameters(AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_PictureParameters *pp); + +void ff_dxva2_vc1_fill_slice(AVCodecContext *avctx, DXVA_SliceInfo *slice, unsigned position, unsigned size); + #endif /* AVCODEC_DXVA2_INTERNAL_H */ diff --git a/libavcodec/dxva2_mpeg2.c b/libavcodec/dxva2_mpeg2.c index 1989c588dc4..d31a8bb8724 100644 --- a/libavcodec/dxva2_mpeg2.c +++ b/libavcodec/dxva2_mpeg2.c @@ -25,6 +25,7 @@ #include "libavutil/log.h" #include "dxva2_internal.h" +#include "hwaccel_internal.h" #include "mpegutils.h" #include "mpegvideodec.h" @@ -39,25 +40,25 @@ struct dxva2_picture_context { unsigned bitstream_size; }; -static void fill_picture_parameters(AVCodecContext *avctx, +void ff_dxva2_mpeg2_fill_picture_parameters(AVCodecContext *avctx, AVDXVAContext *ctx, - const struct MpegEncContext *s, DXVA_PictureParameters *pp) { + const struct MpegEncContext *s = avctx->priv_data; const Picture *current_picture = s->current_picture_ptr; int is_field = s->picture_structure != PICT_FRAME; memset(pp, 0, sizeof(*pp)); - pp->wDecodedPictureIndex = ff_dxva2_get_surface_index(avctx, ctx, current_picture->f); pp->wDeblockedPictureIndex = 0; if (s->pict_type != AV_PICTURE_TYPE_I) - pp->wForwardRefPictureIndex = ff_dxva2_get_surface_index(avctx, ctx, s->last_picture.f); + pp->wForwardRefPictureIndex = ff_dxva2_get_surface_index(avctx, ctx, s->last_picture.f, 0); else pp->wForwardRefPictureIndex = 0xffff; if (s->pict_type == AV_PICTURE_TYPE_B) - pp->wBackwardRefPictureIndex = ff_dxva2_get_surface_index(avctx, ctx, s->next_picture.f); + pp->wBackwardRefPictureIndex = ff_dxva2_get_surface_index(avctx, ctx, s->next_picture.f, 0); else pp->wBackwardRefPictureIndex = 0xffff; + pp->wDecodedPictureIndex = ff_dxva2_get_surface_index(avctx, ctx, current_picture->f, 1); pp->wPicWidthInMBminus1 = s->mb_width - 1; pp->wPicHeightInMBminus1 = (s->mb_height >> is_field) - 1; pp->bMacroblockWidthMinus1 = 15; @@ -105,11 +106,11 @@ static void fill_picture_parameters(AVCodecContext *avctx, pp->bBitstreamConcealmentMethod = 0; } -static void fill_quantization_matrices(AVCodecContext *avctx, +void ff_dxva2_mpeg2_fill_quantization_matrices(AVCodecContext *avctx, AVDXVAContext *ctx, - const struct MpegEncContext *s, DXVA_QmatrixData *qm) { + const struct MpegEncContext *s = avctx->priv_data; int i; for (i = 0; i < 4; i++) qm->bNewQmatrix[i] = 1; @@ -122,12 +123,12 @@ static void fill_quantization_matrices(AVCodecContext *avctx, } } -static void fill_slice(AVCodecContext *avctx, - const struct MpegEncContext *s, +void ff_dxva2_mpeg2_fill_slice(AVCodecContext *avctx, DXVA_SliceInfo *slice, unsigned position, const uint8_t *buffer, unsigned size) { + const struct MpegEncContext *s = avctx->priv_data; int is_field = s->picture_structure != PICT_FRAME; GetBitContext gb; @@ -263,10 +264,10 @@ static int dxva2_mpeg2_start_frame(AVCodecContext *avctx, if (!DXVA_CONTEXT_VALID(avctx, ctx)) return -1; - assert(ctx_pic); + av_assert0(ctx_pic); - fill_picture_parameters(avctx, ctx, s, &ctx_pic->pp); - fill_quantization_matrices(avctx, ctx, s, &ctx_pic->qm); + ff_dxva2_mpeg2_fill_picture_parameters(avctx, ctx, &ctx_pic->pp); + ff_dxva2_mpeg2_fill_quantization_matrices(avctx, ctx, &ctx_pic->qm); ctx_pic->slice_count = 0; ctx_pic->bitstream_size = 0; @@ -292,7 +293,7 @@ static int dxva2_mpeg2_decode_slice(AVCodecContext *avctx, ctx_pic->bitstream_size += size; position = buffer - ctx_pic->bitstream; - fill_slice(avctx, s, &ctx_pic->slice[ctx_pic->slice_count++], position, + ff_dxva2_mpeg2_fill_slice(avctx, &ctx_pic->slice[ctx_pic->slice_count++], position, buffer, size); return 0; } @@ -316,11 +317,11 @@ static int dxva2_mpeg2_end_frame(AVCodecContext *avctx) } #if CONFIG_MPEG2_DXVA2_HWACCEL -const AVHWAccel ff_mpeg2_dxva2_hwaccel = { - .name = "mpeg2_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_mpeg2_dxva2_hwaccel = { + .p.name = "mpeg2_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_mpeg2_start_frame, @@ -333,11 +334,11 @@ const AVHWAccel ff_mpeg2_dxva2_hwaccel = { #endif #if CONFIG_MPEG2_D3D11VA_HWACCEL -const AVHWAccel ff_mpeg2_d3d11va_hwaccel = { - .name = "mpeg2_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_mpeg2_d3d11va_hwaccel = { + .p.name = "mpeg2_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_mpeg2_start_frame, @@ -350,11 +351,11 @@ const AVHWAccel ff_mpeg2_d3d11va_hwaccel = { #endif #if CONFIG_MPEG2_D3D11VA2_HWACCEL -const AVHWAccel ff_mpeg2_d3d11va2_hwaccel = { - .name = "mpeg2_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_mpeg2_d3d11va2_hwaccel = { + .p.name = "mpeg2_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_mpeg2_start_frame, diff --git a/libavcodec/dxva2_vc1.c b/libavcodec/dxva2_vc1.c index 12e3de59ec2..f7513b2b153 100644 --- a/libavcodec/dxva2_vc1.c +++ b/libavcodec/dxva2_vc1.c @@ -23,6 +23,7 @@ #include "config_components.h" #include "dxva2_internal.h" +#include "hwaccel_internal.h" #include "mpegutils.h" #include "mpegvideodec.h" #include "vc1.h" @@ -39,10 +40,11 @@ struct dxva2_picture_context { unsigned bitstream_size; }; -static void fill_picture_parameters(AVCodecContext *avctx, - AVDXVAContext *ctx, const VC1Context *v, +void ff_dxva2_vc1_fill_picture_parameters(AVCodecContext *avctx, + AVDXVAContext *ctx, DXVA_PictureParameters *pp) { + const VC1Context *v = avctx->priv_data; const MpegEncContext *s = &v->s; const Picture *current_picture = s->current_picture_ptr; int intcomp = 0; @@ -56,16 +58,16 @@ static void fill_picture_parameters(AVCodecContext *avctx, } memset(pp, 0, sizeof(*pp)); - pp->wDecodedPictureIndex = - pp->wDeblockedPictureIndex = ff_dxva2_get_surface_index(avctx, ctx, current_picture->f); if (s->pict_type != AV_PICTURE_TYPE_I && !v->bi_type) - pp->wForwardRefPictureIndex = ff_dxva2_get_surface_index(avctx, ctx, s->last_picture.f); + pp->wForwardRefPictureIndex = ff_dxva2_get_surface_index(avctx, ctx, s->last_picture.f, 0); else pp->wForwardRefPictureIndex = 0xffff; if (s->pict_type == AV_PICTURE_TYPE_B && !v->bi_type) - pp->wBackwardRefPictureIndex = ff_dxva2_get_surface_index(avctx, ctx, s->next_picture.f); + pp->wBackwardRefPictureIndex = ff_dxva2_get_surface_index(avctx, ctx, s->next_picture.f, 0); else pp->wBackwardRefPictureIndex = 0xffff; + pp->wDecodedPictureIndex = + pp->wDeblockedPictureIndex = ff_dxva2_get_surface_index(avctx, ctx, current_picture->f, 1); if (v->profile == PROFILE_ADVANCED) { /* It is the cropped width/height -1 of the frame */ pp->wPicWidthInMBminus1 = avctx->width - 1; @@ -162,7 +164,7 @@ static void fill_picture_parameters(AVCodecContext *avctx, pp->bBitstreamConcealmentMethod = 0; } -static void fill_slice(AVCodecContext *avctx, DXVA_SliceInfo *slice, +void ff_dxva2_vc1_fill_slice(AVCodecContext *avctx, DXVA_SliceInfo *slice, unsigned position, unsigned size) { const VC1Context *v = avctx->priv_data; @@ -319,9 +321,9 @@ static int dxva2_vc1_start_frame(AVCodecContext *avctx, if (!DXVA_CONTEXT_VALID(avctx, ctx)) return -1; - assert(ctx_pic); + av_assert0(ctx_pic); - fill_picture_parameters(avctx, ctx, v, &ctx_pic->pp); + ff_dxva2_vc1_fill_picture_parameters(avctx, ctx, &ctx_pic->pp); ctx_pic->slice_count = 0; ctx_pic->bitstream_size = 0; @@ -355,7 +357,7 @@ static int dxva2_vc1_decode_slice(AVCodecContext *avctx, ctx_pic->bitstream_size += size; position = buffer - ctx_pic->bitstream; - fill_slice(avctx, &ctx_pic->slice[ctx_pic->slice_count++], position, size); + ff_dxva2_vc1_fill_slice(avctx, &ctx_pic->slice[ctx_pic->slice_count++], position, size); return 0; } @@ -376,11 +378,11 @@ static int dxva2_vc1_end_frame(AVCodecContext *avctx) } #if CONFIG_WMV3_DXVA2_HWACCEL -const AVHWAccel ff_wmv3_dxva2_hwaccel = { - .name = "wmv3_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_WMV3, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_wmv3_dxva2_hwaccel = { + .p.name = "wmv3_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_WMV3, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, @@ -393,11 +395,11 @@ const AVHWAccel ff_wmv3_dxva2_hwaccel = { #endif #if CONFIG_VC1_DXVA2_HWACCEL -const AVHWAccel ff_vc1_dxva2_hwaccel = { - .name = "vc1_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VC1, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_vc1_dxva2_hwaccel = { + .p.name = "vc1_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VC1, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, @@ -410,11 +412,11 @@ const AVHWAccel ff_vc1_dxva2_hwaccel = { #endif #if CONFIG_WMV3_D3D11VA_HWACCEL -const AVHWAccel ff_wmv3_d3d11va_hwaccel = { - .name = "wmv3_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_WMV3, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_wmv3_d3d11va_hwaccel = { + .p.name = "wmv3_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_WMV3, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, @@ -427,11 +429,11 @@ const AVHWAccel ff_wmv3_d3d11va_hwaccel = { #endif #if CONFIG_WMV3_D3D11VA2_HWACCEL -const AVHWAccel ff_wmv3_d3d11va2_hwaccel = { - .name = "wmv3_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_WMV3, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_wmv3_d3d11va2_hwaccel = { + .p.name = "wmv3_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_WMV3, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, @@ -444,11 +446,11 @@ const AVHWAccel ff_wmv3_d3d11va2_hwaccel = { #endif #if CONFIG_VC1_D3D11VA_HWACCEL -const AVHWAccel ff_vc1_d3d11va_hwaccel = { - .name = "vc1_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VC1, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_vc1_d3d11va_hwaccel = { + .p.name = "vc1_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VC1, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, @@ -461,11 +463,11 @@ const AVHWAccel ff_vc1_d3d11va_hwaccel = { #endif #if CONFIG_VC1_D3D11VA2_HWACCEL -const AVHWAccel ff_vc1_d3d11va2_hwaccel = { - .name = "vc1_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VC1, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_vc1_d3d11va2_hwaccel = { + .p.name = "vc1_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VC1, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vc1_start_frame, diff --git a/libavcodec/dxva2_vp9.c b/libavcodec/dxva2_vp9.c index dbe6c08ad19..1498deb3c82 100644 --- a/libavcodec/dxva2_vp9.c +++ b/libavcodec/dxva2_vp9.c @@ -26,6 +26,7 @@ #include "libavutil/pixdesc.h" #include "dxva2_internal.h" +#include "hwaccel_internal.h" #include "vp9shared.h" struct vp9_dxva2_picture_context { @@ -42,19 +43,18 @@ static void fill_picture_entry(DXVA_PicEntry_VPx *pic, pic->bPicEntry = index | (flag << 7); } -static int fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, const VP9SharedContext *h, +int ff_dxva2_vp9_fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *ctx, DXVA_PicParams_VP9 *pp) { + const VP9SharedContext *h = avctx->priv_data; + const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(avctx->sw_pix_fmt); int i; - const AVPixFmtDescriptor * pixdesc = av_pix_fmt_desc_get(avctx->sw_pix_fmt); if (!pixdesc) return -1; memset(pp, 0, sizeof(*pp)); - fill_picture_entry(&pp->CurrPic, ff_dxva2_get_surface_index(avctx, ctx, h->frames[CUR_FRAME].tf.f), 0); - pp->profile = h->h.profile; pp->wFormatAndPictureInfoFlags = ((h->h.keyframe == 0) << 0) | ((h->h.invisible == 0) << 1) | @@ -80,7 +80,7 @@ static int fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *c for (i = 0; i < 8; i++) { if (h->refs[i].f->buf[0]) { - fill_picture_entry(&pp->ref_frame_map[i], ff_dxva2_get_surface_index(avctx, ctx, h->refs[i].f), 0); + fill_picture_entry(&pp->ref_frame_map[i], ff_dxva2_get_surface_index(avctx, ctx, h->refs[i].f, 0), 0); pp->ref_frame_coded_width[i] = h->refs[i].f->width; pp->ref_frame_coded_height[i] = h->refs[i].f->height; } else @@ -90,13 +90,15 @@ static int fill_picture_parameters(const AVCodecContext *avctx, AVDXVAContext *c for (i = 0; i < 3; i++) { uint8_t refidx = h->h.refidx[i]; if (h->refs[refidx].f->buf[0]) - fill_picture_entry(&pp->frame_refs[i], ff_dxva2_get_surface_index(avctx, ctx, h->refs[refidx].f), 0); + fill_picture_entry(&pp->frame_refs[i], ff_dxva2_get_surface_index(avctx, ctx, h->refs[refidx].f, 0), 0); else pp->frame_refs[i].bPicEntry = 0xFF; pp->ref_frame_sign_bias[i + 1] = h->h.signbias[i]; } + fill_picture_entry(&pp->CurrPic, ff_dxva2_get_surface_index(avctx, ctx, h->frames[CUR_FRAME].tf.f, 1), 0); + pp->filter_level = h->h.filter.level; pp->sharpness_level = h->h.filter.sharpness; @@ -264,7 +266,7 @@ static int dxva2_vp9_start_frame(AVCodecContext *avctx, av_assert0(ctx_pic); /* Fill up DXVA_PicParams_VP9 */ - if (fill_picture_parameters(avctx, ctx, h, &ctx_pic->pp) < 0) + if (ff_dxva2_vp9_fill_picture_parameters(avctx, ctx, &ctx_pic->pp) < 0) return -1; ctx_pic->bitstream_size = 0; @@ -307,11 +309,11 @@ static int dxva2_vp9_end_frame(AVCodecContext *avctx) } #if CONFIG_VP9_DXVA2_HWACCEL -const AVHWAccel ff_vp9_dxva2_hwaccel = { - .name = "vp9_dxva2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_DXVA2_VLD, +const FFHWAccel ff_vp9_dxva2_hwaccel = { + .p.name = "vp9_dxva2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_DXVA2_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vp9_start_frame, @@ -324,11 +326,11 @@ const AVHWAccel ff_vp9_dxva2_hwaccel = { #endif #if CONFIG_VP9_D3D11VA_HWACCEL -const AVHWAccel ff_vp9_d3d11va_hwaccel = { - .name = "vp9_d3d11va", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_D3D11VA_VLD, +const FFHWAccel ff_vp9_d3d11va_hwaccel = { + .p.name = "vp9_d3d11va", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_D3D11VA_VLD, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vp9_start_frame, @@ -341,11 +343,11 @@ const AVHWAccel ff_vp9_d3d11va_hwaccel = { #endif #if CONFIG_VP9_D3D11VA2_HWACCEL -const AVHWAccel ff_vp9_d3d11va2_hwaccel = { - .name = "vp9_d3d11va2", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_D3D11, +const FFHWAccel ff_vp9_d3d11va2_hwaccel = { + .p.name = "vp9_d3d11va2", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_D3D11, .init = ff_dxva2_decode_init, .uninit = ff_dxva2_decode_uninit, .start_frame = dxva2_vp9_start_frame, diff --git a/libavcodec/dxvenc.c b/libavcodec/dxvenc.c new file mode 100644 index 00000000000..91f4ba7619a --- /dev/null +++ b/libavcodec/dxvenc.c @@ -0,0 +1,357 @@ +/* + * Resolume DXV encoder + * Copyright (C) 2024 Connor Worley + * + * 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 + +#include "libavutil/crc.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" + +#include "bytestream.h" +#include "codec_internal.h" +#include "dxv.h" +#include "encode.h" +#include "texturedsp.h" + +#define DXV_HEADER_LENGTH 12 + +/* + * DXV uses LZ-like back-references to avoid copying words that have already + * appeared in the decompressed stream. Using a simple hash table (HT) + * significantly speeds up the lookback process while encoding. + */ +#define LOOKBACK_HT_ELEMS 0x40000 +#define LOOKBACK_WORDS 0x20202 + +typedef struct HTEntry { + uint32_t key; + uint32_t pos; +} HTEntry; + +static void ht_init(HTEntry *ht) +{ + for (size_t i = 0; i < LOOKBACK_HT_ELEMS; i++) { + ht[i].pos = -1; + } +} + +static uint32_t ht_lookup_and_upsert(HTEntry *ht, const AVCRC *hash_ctx, + uint32_t key, uint32_t pos) +{ + uint32_t ret = -1; + size_t hash = av_crc(hash_ctx, 0, (uint8_t*)&key, 4) % LOOKBACK_HT_ELEMS; + for (size_t i = hash; i < hash + LOOKBACK_HT_ELEMS; i++) { + size_t wrapped_index = i % LOOKBACK_HT_ELEMS; + HTEntry *entry = &ht[wrapped_index]; + if (entry->key == key || entry->pos == -1) { + ret = entry->pos; + entry->key = key; + entry->pos = pos; + break; + } + } + return ret; +} + +static void ht_delete(HTEntry *ht, const AVCRC *hash_ctx, + uint32_t key, uint32_t pos) +{ + HTEntry *removed_entry = NULL; + size_t removed_hash; + size_t hash = av_crc(hash_ctx, 0, (uint8_t*)&key, 4) % LOOKBACK_HT_ELEMS; + + for (size_t i = hash; i < hash + LOOKBACK_HT_ELEMS; i++) { + size_t wrapped_index = i % LOOKBACK_HT_ELEMS; + HTEntry *entry = &ht[wrapped_index]; + if (entry->pos == -1) + return; + if (removed_entry) { + size_t candidate_hash = av_crc(hash_ctx, 0, (uint8_t*)&entry->key, 4) % LOOKBACK_HT_ELEMS; + if ((wrapped_index > removed_hash && (candidate_hash <= removed_hash || candidate_hash > wrapped_index)) || + (wrapped_index < removed_hash && (candidate_hash <= removed_hash && candidate_hash > wrapped_index))) { + *removed_entry = *entry; + entry->pos = -1; + removed_entry = entry; + removed_hash = wrapped_index; + } + } else if (entry->key == key) { + if (entry->pos <= pos) { + entry->pos = -1; + removed_entry = entry; + removed_hash = wrapped_index; + } else { + return; + } + } + } +} + +typedef struct DXVEncContext { + AVClass *class; + + PutByteContext pbc; + + uint8_t *tex_data; // Compressed texture + int64_t tex_size; // Texture size + + /* Optimal number of slices for parallel decoding */ + int slice_count; + + TextureDSPThreadContext enc; + + DXVTextureFormat tex_fmt; + int (*compress_tex)(AVCodecContext *avctx); + + const AVCRC *crc_ctx; + + HTEntry color_lookback_ht[LOOKBACK_HT_ELEMS]; + HTEntry lut_lookback_ht[LOOKBACK_HT_ELEMS]; +} DXVEncContext; + +/* Converts an index offset value to a 2-bit opcode and pushes it to a stream. + * Inverse of CHECKPOINT in dxv.c. */ +#define PUSH_OP(x) \ + do { \ + if (state == 16) { \ + if (bytestream2_get_bytes_left_p(pbc) < 4) { \ + return AVERROR_INVALIDDATA; \ + } \ + value = pbc->buffer; \ + bytestream2_put_le32(pbc, 0); \ + state = 0; \ + } \ + if (idx >= 0x102 * x) { \ + op = 3; \ + bytestream2_put_le16(pbc, (idx / x) - 0x102); \ + } else if (idx >= 2 * x) { \ + op = 2; \ + bytestream2_put_byte(pbc, (idx / x) - 2); \ + } else if (idx == x) { \ + op = 1; \ + } else { \ + op = 0; \ + } \ + AV_WL32(value, AV_RL32(value) | (op << (state * 2))); \ + state++; \ + } while (0) + +static int dxv_compress_dxt1(AVCodecContext *avctx) +{ + DXVEncContext *ctx = avctx->priv_data; + PutByteContext *pbc = &ctx->pbc; + void *value; + uint32_t color, lut, idx, color_idx, lut_idx, prev_pos, state = 16, pos = 2, op = 0; + + ht_init(ctx->color_lookback_ht); + ht_init(ctx->lut_lookback_ht); + + bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data)); + bytestream2_put_le32(pbc, AV_RL32(ctx->tex_data + 4)); + + ht_lookup_and_upsert(ctx->color_lookback_ht, ctx->crc_ctx, AV_RL32(ctx->tex_data), 0); + ht_lookup_and_upsert(ctx->lut_lookback_ht, ctx->crc_ctx, AV_RL32(ctx->tex_data + 4), 1); + + while (pos + 2 <= ctx->tex_size / 4) { + idx = 0; + + color = AV_RL32(ctx->tex_data + pos * 4); + prev_pos = ht_lookup_and_upsert(ctx->color_lookback_ht, ctx->crc_ctx, color, pos); + color_idx = prev_pos != -1 ? pos - prev_pos : 0; + if (pos >= LOOKBACK_WORDS) { + uint32_t old_pos = pos - LOOKBACK_WORDS; + uint32_t old_color = AV_RL32(ctx->tex_data + old_pos * 4); + ht_delete(ctx->color_lookback_ht, ctx->crc_ctx, old_color, old_pos); + } + pos++; + + lut = AV_RL32(ctx->tex_data + pos * 4); + if (color_idx && lut == AV_RL32(ctx->tex_data + (pos - color_idx) * 4)) { + idx = color_idx; + } else { + idx = 0; + prev_pos = ht_lookup_and_upsert(ctx->lut_lookback_ht, ctx->crc_ctx, lut, pos); + lut_idx = prev_pos != -1 ? pos - prev_pos : 0; + } + if (pos >= LOOKBACK_WORDS) { + uint32_t old_pos = pos - LOOKBACK_WORDS; + uint32_t old_lut = AV_RL32(ctx->tex_data + old_pos * 4); + ht_delete(ctx->lut_lookback_ht, ctx->crc_ctx, old_lut, old_pos); + } + pos++; + + PUSH_OP(2); + + if (!idx) { + idx = color_idx; + PUSH_OP(2); + if (!idx) + bytestream2_put_le32(pbc, color); + + idx = lut_idx; + PUSH_OP(2); + if (!idx) + bytestream2_put_le32(pbc, lut); + } + } + + return 0; +} + +static int dxv_encode(AVCodecContext *avctx, AVPacket *pkt, + const AVFrame *frame, int *got_packet) +{ + DXVEncContext *ctx = avctx->priv_data; + PutByteContext *pbc = &ctx->pbc; + int ret; + + /* unimplemented: needs to depend on compression ratio of tex format */ + /* under DXT1, we need 3 words to encode load ops for 32 words. + * the first 2 words of the texture do not need load ops. */ + ret = ff_alloc_packet(avctx, pkt, DXV_HEADER_LENGTH + ctx->tex_size + AV_CEIL_RSHIFT(ctx->tex_size - 8, 7) * 12); + if (ret < 0) + return ret; + + if (ctx->enc.tex_funct) { + ctx->enc.tex_data.out = ctx->tex_data; + ctx->enc.frame_data.in = frame->data[0]; + ctx->enc.stride = frame->linesize[0]; + ctx->enc.width = avctx->width; + ctx->enc.height = avctx->height; + ff_texturedsp_exec_compress_threads(avctx, &ctx->enc); + } else { + /* unimplemented: YCoCg formats */ + return AVERROR_INVALIDDATA; + } + + bytestream2_init_writer(pbc, pkt->data, pkt->size); + + bytestream2_put_le32(pbc, ctx->tex_fmt); + bytestream2_put_byte(pbc, 4); + bytestream2_put_byte(pbc, 0); + bytestream2_put_byte(pbc, 0); + bytestream2_put_byte(pbc, 0); + /* Fill in compressed size later */ + bytestream2_skip_p(pbc, 4); + + ret = ctx->compress_tex(avctx); + if (ret < 0) + return ret; + + AV_WL32(pkt->data + 8, bytestream2_tell_p(pbc) - DXV_HEADER_LENGTH); + av_shrink_packet(pkt, bytestream2_tell_p(pbc)); + + *got_packet = 1; + return 0; +} + +static av_cold int dxv_init(AVCodecContext *avctx) +{ + DXVEncContext *ctx = avctx->priv_data; + TextureDSPEncContext texdsp; + int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx); + + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid image size %dx%d.\n", + avctx->width, avctx->height); + return ret; + } + + if (avctx->width % TEXTURE_BLOCK_W || avctx->height % TEXTURE_BLOCK_H) { + av_log(avctx, + AV_LOG_ERROR, + "Video size %dx%d is not multiple of "AV_STRINGIFY(TEXTURE_BLOCK_W)"x"AV_STRINGIFY(TEXTURE_BLOCK_H)".\n", + avctx->width, avctx->height); + return AVERROR_INVALIDDATA; + } + + ff_texturedspenc_init(&texdsp); + + switch (ctx->tex_fmt) { + case DXV_FMT_DXT1: + ctx->compress_tex = dxv_compress_dxt1; + ctx->enc.tex_funct = texdsp.dxt1_block; + ctx->enc.tex_ratio = 8; + break; + default: + av_log(avctx, AV_LOG_ERROR, "Invalid format %08X\n", ctx->tex_fmt); + return AVERROR_INVALIDDATA; + } + ctx->enc.raw_ratio = 16; + ctx->tex_size = avctx->width / TEXTURE_BLOCK_W * + avctx->height / TEXTURE_BLOCK_H * + ctx->enc.tex_ratio; + ctx->enc.slice_count = av_clip(avctx->thread_count, 1, avctx->height / TEXTURE_BLOCK_H); + + ctx->tex_data = av_malloc(ctx->tex_size); + if (!ctx->tex_data) { + return AVERROR(ENOMEM); + } + + ctx->crc_ctx = av_crc_get_table(AV_CRC_32_IEEE); + if (!ctx->crc_ctx) { + av_log(avctx, AV_LOG_ERROR, "Could not initialize CRC table.\n"); + return AVERROR_BUG; + } + + return 0; +} + +static av_cold int dxv_close(AVCodecContext *avctx) +{ + DXVEncContext *ctx = avctx->priv_data; + + av_freep(&ctx->tex_data); + + return 0; +} + +#define OFFSET(x) offsetof(DXVEncContext, x) +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM +static const AVOption options[] = { + { "format", NULL, OFFSET(tex_fmt), AV_OPT_TYPE_INT, { .i64 = DXV_FMT_DXT1 }, DXV_FMT_DXT1, DXV_FMT_DXT1, FLAGS, .unit = "format" }, + { "dxt1", "DXT1 (Normal Quality, No Alpha)", 0, AV_OPT_TYPE_CONST, { .i64 = DXV_FMT_DXT1 }, 0, 0, FLAGS, .unit = "format" }, + { NULL }, +}; + +static const AVClass dxvenc_class = { + .class_name = "DXV encoder", + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_dxv_encoder = { + .p.name = "dxv", + CODEC_LONG_NAME("Resolume DXV"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_DXV, + .init = dxv_init, + FF_CODEC_ENCODE_CB(dxv_encode), + .close = dxv_close, + .priv_data_size = sizeof(DXVEncContext), + .p.capabilities = AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_FRAME_THREADS, + .p.priv_class = &dxvenc_class, + .p.pix_fmts = (const enum AVPixelFormat[]) { + AV_PIX_FMT_RGBA, AV_PIX_FMT_NONE, + }, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/dynamic_hdr10_plus.c b/libavcodec/dynamic_hdr10_plus.c deleted file mode 100644 index 34a44aac655..00000000000 --- a/libavcodec/dynamic_hdr10_plus.c +++ /dev/null @@ -1,198 +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 - */ - -#include "dynamic_hdr10_plus.h" -#include "get_bits.h" - -static const int64_t luminance_den = 1; -static const int32_t peak_luminance_den = 15; -static const int64_t rgb_den = 100000; -static const int32_t fraction_pixel_den = 1000; -static const int32_t knee_point_den = 4095; -static const int32_t bezier_anchor_den = 1023; -static const int32_t saturation_weight_den = 8; - -int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t *data, - int size) -{ - GetBitContext gbc, *gb = &gbc; - int ret; - - if (!s) - return AVERROR(ENOMEM); - - ret = init_get_bits8(gb, data, size); - if (ret < 0) - return ret; - - if (get_bits_left(gb) < 10) - return AVERROR_INVALIDDATA; - - s->application_version = get_bits(gb, 8); - s->num_windows = get_bits(gb, 2); - - if (s->num_windows < 1 || s->num_windows > 3) { - return AVERROR_INVALIDDATA; - } - - if (get_bits_left(gb) < ((19 * 8 + 1) * (s->num_windows - 1))) - return AVERROR_INVALIDDATA; - - for (int w = 1; w < s->num_windows; w++) { - // The corners are set to absolute coordinates here. They should be - // converted to the relative coordinates (in [0, 1]) in the decoder. - AVHDRPlusColorTransformParams *params = &s->params[w]; - params->window_upper_left_corner_x = - (AVRational){get_bits(gb, 16), 1}; - params->window_upper_left_corner_y = - (AVRational){get_bits(gb, 16), 1}; - params->window_lower_right_corner_x = - (AVRational){get_bits(gb, 16), 1}; - params->window_lower_right_corner_y = - (AVRational){get_bits(gb, 16), 1}; - - params->center_of_ellipse_x = get_bits(gb, 16); - params->center_of_ellipse_y = get_bits(gb, 16); - params->rotation_angle = get_bits(gb, 8); - params->semimajor_axis_internal_ellipse = get_bits(gb, 16); - params->semimajor_axis_external_ellipse = get_bits(gb, 16); - params->semiminor_axis_external_ellipse = get_bits(gb, 16); - params->overlap_process_option = get_bits1(gb); - } - - if (get_bits_left(gb) < 28) - return AVERROR_INVALIDDATA; - - s->targeted_system_display_maximum_luminance = - (AVRational){get_bits_long(gb, 27), luminance_den}; - s->targeted_system_display_actual_peak_luminance_flag = get_bits1(gb); - - if (s->targeted_system_display_actual_peak_luminance_flag) { - int rows, cols; - if (get_bits_left(gb) < 10) - return AVERROR_INVALIDDATA; - rows = get_bits(gb, 5); - cols = get_bits(gb, 5); - if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) { - return AVERROR_INVALIDDATA; - } - s->num_rows_targeted_system_display_actual_peak_luminance = rows; - s->num_cols_targeted_system_display_actual_peak_luminance = cols; - - if (get_bits_left(gb) < (rows * cols * 4)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < rows; i++) { - for (int j = 0; j < cols; j++) { - s->targeted_system_display_actual_peak_luminance[i][j] = - (AVRational){get_bits(gb, 4), peak_luminance_den}; - } - } - } - for (int w = 0; w < s->num_windows; w++) { - AVHDRPlusColorTransformParams *params = &s->params[w]; - if (get_bits_left(gb) < (3 * 17 + 17 + 4)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < 3; i++) { - params->maxscl[i] = - (AVRational){get_bits(gb, 17), rgb_den}; - } - params->average_maxrgb = - (AVRational){get_bits(gb, 17), rgb_den}; - params->num_distribution_maxrgb_percentiles = get_bits(gb, 4); - - if (get_bits_left(gb) < - (params->num_distribution_maxrgb_percentiles * 24)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < params->num_distribution_maxrgb_percentiles; i++) { - params->distribution_maxrgb[i].percentage = get_bits(gb, 7); - params->distribution_maxrgb[i].percentile = - (AVRational){get_bits(gb, 17), rgb_den}; - } - - if (get_bits_left(gb) < 10) - return AVERROR_INVALIDDATA; - - params->fraction_bright_pixels = (AVRational){get_bits(gb, 10), fraction_pixel_den}; - } - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - s->mastering_display_actual_peak_luminance_flag = get_bits1(gb); - if (s->mastering_display_actual_peak_luminance_flag) { - int rows, cols; - if (get_bits_left(gb) < 10) - return AVERROR_INVALIDDATA; - rows = get_bits(gb, 5); - cols = get_bits(gb, 5); - if (((rows < 2) || (rows > 25)) || ((cols < 2) || (cols > 25))) { - return AVERROR_INVALIDDATA; - } - s->num_rows_mastering_display_actual_peak_luminance = rows; - s->num_cols_mastering_display_actual_peak_luminance = cols; - - if (get_bits_left(gb) < (rows * cols * 4)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < rows; i++) { - for (int j = 0; j < cols; j++) { - s->mastering_display_actual_peak_luminance[i][j] = - (AVRational){get_bits(gb, 4), peak_luminance_den}; - } - } - } - - for (int w = 0; w < s->num_windows; w++) { - AVHDRPlusColorTransformParams *params = &s->params[w]; - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - - params->tone_mapping_flag = get_bits1(gb); - if (params->tone_mapping_flag) { - if (get_bits_left(gb) < 28) - return AVERROR_INVALIDDATA; - - params->knee_point_x = - (AVRational){get_bits(gb, 12), knee_point_den}; - params->knee_point_y = - (AVRational){get_bits(gb, 12), knee_point_den}; - params->num_bezier_curve_anchors = get_bits(gb, 4); - - if (get_bits_left(gb) < (params->num_bezier_curve_anchors * 10)) - return AVERROR_INVALIDDATA; - - for (int i = 0; i < params->num_bezier_curve_anchors; i++) { - params->bezier_curve_anchors[i] = - (AVRational){get_bits(gb, 10), bezier_anchor_den}; - } - } - - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - params->color_saturation_mapping_flag = get_bits1(gb); - if (params->color_saturation_mapping_flag) { - if (get_bits_left(gb) < 6) - return AVERROR_INVALIDDATA; - params->color_saturation_weight = - (AVRational){get_bits(gb, 6), saturation_weight_den}; - } - } - - return 0; -} diff --git a/libavcodec/dynamic_hdr_vivid.c b/libavcodec/dynamic_hdr_vivid.c index d689669dec0..a9b69107982 100644 --- a/libavcodec/dynamic_hdr_vivid.c +++ b/libavcodec/dynamic_hdr_vivid.c @@ -46,7 +46,8 @@ int ff_parse_itu_t_t35_to_dynamic_hdr_vivid(AVDynamicHDRVivid *s, const uint8_t return AVERROR_INVALIDDATA; s->system_start_code = get_bits(gb, 8); - if (s->system_start_code == 0x01) { + // T/UWA 005.1-2022, table 11 + if (s->system_start_code >= 0x01 && s->system_start_code <= 0x07) { s->num_windows = 1; if (get_bits_left(gb) < 12 * 4 * s->num_windows) @@ -89,35 +90,43 @@ int ff_parse_itu_t_t35_to_dynamic_hdr_vivid(AVDynamicHDRVivid *s, const uint8_t tm_params->base_param_k2 = get_bits(gb, 2); tm_params->base_param_k3 = get_bits(gb, 4); tm_params->base_param_Delta_enable_mode = get_bits(gb, 3); - if (tm_params->base_param_Delta_enable_mode == 2 || tm_params->base_param_Delta_enable_mode == 6) - tm_params->base_param_Delta = (AVRational){get_bits(gb, 7) * -1, base_param_Delta_den}; - else - tm_params->base_param_Delta = (AVRational){get_bits(gb, 7), base_param_Delta_den}; + tm_params->base_param_Delta = (AVRational){get_bits(gb, 7), base_param_Delta_den}; + } + if (get_bits_left(gb) < 1) + return AVERROR_INVALIDDATA; + tm_params->three_Spline_enable_flag = get_bits(gb, 1); + if (tm_params->three_Spline_enable_flag) { + AVHDRVivid3SplineParams *three_spline; - if (get_bits_left(gb) < 1) + if (get_bits_left(gb) < 1 + tm_params->three_Spline_num * (2 + 12 + 28 + 1)) return AVERROR_INVALIDDATA; - tm_params->three_Spline_enable_flag = get_bits(gb, 1); - if (tm_params->three_Spline_enable_flag) { - if (get_bits_left(gb) < 1 + tm_params->three_Spline_num * (2 + 12 + 28 + 1)) - return AVERROR_INVALIDDATA; - tm_params->three_Spline_num = get_bits(gb, 1) + 1; - for (int j = 0; j < tm_params->three_Spline_num; j++) { - tm_params->three_Spline_TH_mode = get_bits(gb, 2); - if (tm_params->three_Spline_TH_mode == 0 || tm_params->three_Spline_TH_mode == 2) { - if (get_bits_left(gb) < 8) - return AVERROR_INVALIDDATA; - tm_params->three_Spline_TH_enable_MB = (AVRational){get_bits(gb, 8), 255}; - } - tm_params->three_Spline_TH_enable = (AVRational){get_bits(gb, 12), 4095}; - tm_params->three_Spline_TH_Delta1 = (AVRational){get_bits(gb, 10), 1023}; - tm_params->three_Spline_TH_Delta2 = (AVRational){get_bits(gb, 10), 1023}; - tm_params->three_Spline_enable_Strength = (AVRational){get_bits(gb, 8), 255}; + tm_params->three_Spline_num = get_bits(gb, 1) + 1; + if (tm_params->three_Spline_num > FF_ARRAY_ELEMS(tm_params->three_spline)) + return AVERROR_INVALIDDATA; + for (int j = 0; j < tm_params->three_Spline_num; j++) { + three_spline = &tm_params->three_spline[j]; + three_spline->th_mode = get_bits(gb, 2); + if (three_spline->th_mode == 0 || three_spline->th_mode == 2) { + if (get_bits_left(gb) < 8) + return AVERROR_INVALIDDATA; + three_spline->th_enable_mb = (AVRational){get_bits(gb, 8), 255}; } - } else { - tm_params->three_Spline_num = 1; - tm_params->three_Spline_TH_mode = 0; + three_spline->th_enable = (AVRational){get_bits(gb, 12), 4095}; + three_spline->th_delta1 = (AVRational){get_bits(gb, 10), 1023}; + three_spline->th_delta2 = (AVRational){get_bits(gb, 10), 1023}; + three_spline->enable_strength = (AVRational){get_bits(gb, 8), 255}; } - +#if FF_API_HDR_VIVID_THREE_SPLINE + three_spline = &tm_params->three_spline[0]; +FF_DISABLE_DEPRECATION_WARNINGS + tm_params->three_Spline_TH_mode = three_spline->th_mode; + tm_params->three_Spline_TH_enable_MB = three_spline->th_enable_mb; + tm_params->three_Spline_TH_enable = three_spline->th_enable; + tm_params->three_Spline_TH_Delta1 = three_spline->th_delta1; + tm_params->three_Spline_TH_Delta2 = three_spline->th_delta2; + tm_params->three_Spline_enable_Strength = three_spline->enable_strength; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } } } diff --git a/libavcodec/eac3dec.c b/libavcodec/eac3dec.c index deca51dd3da..5c71751a0c8 100644 --- a/libavcodec/eac3dec.c +++ b/libavcodec/eac3dec.c @@ -464,7 +464,16 @@ static int ff_eac3_parse_header(AC3DecodeContext *s) if (get_bits1(gbc)) { int addbsil = get_bits(gbc, 6); for (i = 0; i < addbsil + 1; i++) { - skip_bits(gbc, 8); // skip additional bit stream info + if (i == 0) { + /* In this 8 bit chunk, the LSB is equal to flag_ec3_extension_type_a + which can be used to detect Atmos presence */ + skip_bits(gbc, 7); + if (get_bits1(gbc)) { + s->eac3_extension_type_a = 1; + } + } else { + skip_bits(gbc, 8); // skip additional bit stream info + } } } diff --git a/libavcodec/eac3enc.c b/libavcodec/eac3enc.c index 4b3236d4e5e..527f77e33aa 100644 --- a/libavcodec/eac3enc.c +++ b/libavcodec/eac3enc.c @@ -263,7 +263,6 @@ const FFCodec ff_eac3_encoder = { AV_SAMPLE_FMT_NONE }, .p.priv_class = &eac3enc_class, .p.supported_samplerates = ff_ac3_sample_rate_tab, - CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(ff_ac3_channel_layouts) .p.ch_layouts = ff_ac3_ch_layouts, .defaults = ff_ac3_enc_defaults, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, diff --git a/libavcodec/eacmv.c b/libavcodec/eacmv.c index 18f27dfdf07..43dba20faec 100644 --- a/libavcodec/eacmv.c +++ b/libavcodec/eacmv.c @@ -202,17 +202,16 @@ static int cmv_decode_frame(AVCodecContext *avctx, AVFrame *frame, buf += EA_PREAMBLE_SIZE; if ((buf[0]&1)) { // subtype cmv_decode_inter(s, frame, buf+2, buf_end); - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; }else{ - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; cmv_decode_intra(s, frame, buf+2, buf_end); } - av_frame_unref(s->last2_frame); - av_frame_move_ref(s->last2_frame, s->last_frame); - if ((ret = av_frame_ref(s->last_frame, frame)) < 0) + FFSWAP(AVFrame*, s->last2_frame, s->last_frame); + if ((ret = av_frame_replace(s->last_frame, frame)) < 0) return ret; *got_frame = 1; diff --git a/libavcodec/eamad.c b/libavcodec/eamad.c index 45012c62b8a..287575e073d 100644 --- a/libavcodec/eamad.c +++ b/libavcodec/eamad.c @@ -317,8 +317,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, *got_frame = 1; if (chunk_type != MADe_TAG) { - av_frame_unref(s->last_frame); - if ((ret = av_frame_ref(s->last_frame, frame)) < 0) + if ((ret = av_frame_replace(s->last_frame, frame)) < 0) return ret; } diff --git a/libavcodec/eatgq.c b/libavcodec/eatgq.c index 01e1acd4e42..0f0ed3585fa 100644 --- a/libavcodec/eatgq.c +++ b/libavcodec/eatgq.c @@ -237,7 +237,7 @@ static int tgq_decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) return ret; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; for (y = 0; y < FFALIGN(avctx->height, 16) >> 4; y++) diff --git a/libavcodec/eatgv.c b/libavcodec/eatgv.c index 29f7ee12f5b..35bd88b7403 100644 --- a/libavcodec/eatgv.c +++ b/libavcodec/eatgv.c @@ -310,7 +310,7 @@ static int tgv_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (chunk_type == kVGT_TAG) { int y; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; if (!s->frame_buffer && @@ -330,7 +330,7 @@ static int tgv_decode_frame(AVCodecContext *avctx, AVFrame *frame, av_log(avctx, AV_LOG_WARNING, "inter frame without corresponding intra frame\n"); return buf_size; } - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; if (tgv_decode_inter(s, frame, buf, buf_end) < 0) { av_log(avctx, AV_LOG_WARNING, "truncated inter frame\n"); @@ -338,8 +338,7 @@ static int tgv_decode_frame(AVCodecContext *avctx, AVFrame *frame, } } - av_frame_unref(s->last_frame); - if ((ret = av_frame_ref(s->last_frame, frame)) < 0) + if ((ret = av_frame_replace(s->last_frame, frame)) < 0) return ret; *got_frame = 1; diff --git a/libavcodec/elbg.c b/libavcodec/elbg.c index d97a7bc3f99..a52ee1e3d89 100644 --- a/libavcodec/elbg.c +++ b/libavcodec/elbg.c @@ -44,13 +44,13 @@ typedef struct cell_s { * ELBG internal data */ typedef struct ELBGContext { - int64_t error; + int error; int dim; int num_cb; int *codebook; cell **cells; - int64_t *utility; - int64_t *utility_inc; + int *utility; + int *utility_inc; int *nearest_cb; int *points; int *temp_points; @@ -75,9 +75,12 @@ static inline int distance_limited(int *a, int *b, int dim, int limit) { int i, dist=0; for (i=0; i limit) - return INT_MAX; + int64_t distance = a[i] - b[i]; + + distance *= distance; + if (dist >= limit - distance) + return limit; + dist += distance; } return dist; @@ -97,8 +100,12 @@ static inline void vect_division(int *res, int *vect, int div, int dim) static int eval_error_cell(ELBGContext *elbg, int *centroid, cell *cells) { int error=0; - for (; cells; cells=cells->next) - error += distance_limited(centroid, elbg->points + cells->index*elbg->dim, elbg->dim, INT_MAX); + for (; cells; cells=cells->next) { + int distance = distance_limited(centroid, elbg->points + cells->index*elbg->dim, elbg->dim, INT_MAX); + if (error >= INT_MAX - distance) + return INT_MAX; + error += distance; + } return error; } @@ -178,10 +185,13 @@ static int simple_lbg(ELBGContext *elbg, int dist[2] = {distance_limited(centroid[0], points + tempcell->index*dim, dim, INT_MAX), distance_limited(centroid[1], points + tempcell->index*dim, dim, INT_MAX)}; int idx = dist[0] > dist[1]; - newutility[idx] += dist[idx]; + if (newutility[idx] >= INT_MAX - dist[idx]) + newutility[idx] = INT_MAX; + else + newutility[idx] += dist[idx]; } - return newutility[0] + newutility[1]; + return (newutility[0] >= INT_MAX - newutility[1]) ? INT_MAX : newutility[0] + newutility[1]; } static void get_new_centroids(ELBGContext *elbg, int huc, int *newcentroid_i, @@ -253,9 +263,9 @@ static void evaluate_utility_inc(ELBGContext *elbg) int64_t inc=0; for (int i = 0; i < elbg->num_cb; i++) { - if (elbg->num_cb * elbg->utility[i] > elbg->error) + if (elbg->num_cb * (int64_t)elbg->utility[i] > elbg->error) inc += elbg->utility[i]; - elbg->utility_inc[i] = inc; + elbg->utility_inc[i] = FFMIN(inc, INT_MAX); } } @@ -278,7 +288,7 @@ static void update_utility_and_n_cb(ELBGContext *elbg, int idx, int newutility) */ static void try_shift_candidate(ELBGContext *elbg, int idx[3]) { - int j, k, cont=0; + int j, k, cont=0, tmp; int64_t olderror=0, newerror; int newutility[3]; int *newcentroid[3] = { @@ -305,12 +315,17 @@ static void try_shift_candidate(ELBGContext *elbg, int idx[3]) get_new_centroids(elbg, idx[1], newcentroid[0], newcentroid[1]); newutility[2] = eval_error_cell(elbg, newcentroid[2], elbg->cells[idx[0]]); - newutility[2] += eval_error_cell(elbg, newcentroid[2], elbg->cells[idx[2]]); + tmp = eval_error_cell(elbg, newcentroid[2], elbg->cells[idx[2]]); + newutility[2] = (tmp >= INT_MAX - newutility[2]) ? INT_MAX : newutility[2] + tmp; newerror = newutility[2]; - newerror += simple_lbg(elbg, elbg->dim, newcentroid, newutility, elbg->points, + tmp = simple_lbg(elbg, elbg->dim, newcentroid, newutility, elbg->points, elbg->cells[idx[1]]); + if (tmp >= INT_MAX - newerror) + newerror = INT_MAX; + else + newerror += tmp; if (olderror > newerror) { shift_codebook(elbg, idx, newcentroid); @@ -334,7 +349,7 @@ static void do_shiftings(ELBGContext *elbg) evaluate_utility_inc(elbg); for (idx[0]=0; idx[0] < elbg->num_cb; idx[0]++) - if (elbg->num_cb * elbg->utility[idx[0]] < elbg->error) { + if (elbg->num_cb * (int64_t)elbg->utility[idx[0]] < elbg->error) { if (elbg->utility_inc[elbg->num_cb - 1] == 0) return; @@ -346,15 +361,15 @@ static void do_shiftings(ELBGContext *elbg) } } -static void do_elbg(ELBGContext *av_restrict elbg, int *points, int numpoints, +static void do_elbg(ELBGContext *restrict elbg, int *points, int numpoints, int max_steps) { int *const size_part = elbg->size_part; int i, j, steps = 0; int best_idx = 0; - int64_t last_error; + int last_error; - elbg->error = INT64_MAX; + elbg->error = INT_MAX; elbg->points = points; do { @@ -382,8 +397,9 @@ static void do_elbg(ELBGContext *av_restrict elbg, int *points, int numpoints, } } elbg->nearest_cb[i] = best_idx; - elbg->error += best_dist; - elbg->utility[elbg->nearest_cb[i]] += best_dist; + elbg->error = (elbg->error >= INT_MAX - best_dist) ? INT_MAX : elbg->error + best_dist; + elbg->utility[elbg->nearest_cb[i]] = (elbg->utility[elbg->nearest_cb[i]] >= INT_MAX - best_dist) ? + INT_MAX : elbg->utility[elbg->nearest_cb[i]] + best_dist; free_cells->index = i; free_cells->next = elbg->cells[elbg->nearest_cb[i]]; elbg->cells[elbg->nearest_cb[i]] = free_cells; @@ -419,7 +435,7 @@ static void do_elbg(ELBGContext *av_restrict elbg, int *points, int numpoints, * If not, it calls do_elbg for a (smaller) random sample of the points in * points. */ -static void init_elbg(ELBGContext *av_restrict elbg, int *points, int *temp_points, +static void init_elbg(ELBGContext *restrict elbg, int *points, int *temp_points, int numpoints, int max_steps) { int dim = elbg->dim; @@ -447,7 +463,7 @@ int avpriv_elbg_do(ELBGContext **elbgp, int *points, int dim, int numpoints, int *codebook, int num_cb, int max_steps, int *closest_cb, AVLFG *rand_state, uintptr_t flags) { - ELBGContext *const av_restrict elbg = *elbgp ? *elbgp : av_mallocz(sizeof(*elbg)); + ELBGContext *const restrict elbg = *elbgp ? *elbgp : av_mallocz(sizeof(*elbg)); if (!elbg) return AVERROR(ENOMEM); diff --git a/libavcodec/encode.c b/libavcodec/encode.c index 041fc7670e5..7fc9737e938 100644 --- a/libavcodec/encode.c +++ b/libavcodec/encode.c @@ -21,17 +21,43 @@ #include "libavutil/attributes.h" #include "libavutil/avassert.h" #include "libavutil/channel_layout.h" +#include "libavutil/emms.h" #include "libavutil/frame.h" #include "libavutil/imgutils.h" #include "libavutil/internal.h" +#include "libavutil/pixdesc.h" #include "libavutil/samplefmt.h" #include "avcodec.h" +#include "avcodec_internal.h" +#include "codec_desc.h" #include "codec_internal.h" #include "encode.h" #include "frame_thread_encoder.h" #include "internal.h" +typedef struct EncodeContext { + AVCodecInternal avci; + + /** + * This is set to AV_PKT_FLAG_KEY for encoders that encode intra-only + * formats (i.e. whose codec descriptor has AV_CODEC_PROP_INTRA_ONLY set). + * This is used to set said flag generically for said encoders. + */ + int intra_only_flag; + + /** + * An audio frame with less than required samples has been submitted (and + * potentially padded with silence). Reject all subsequent frames. + */ + int last_audio_frame; +} EncodeContext; + +static EncodeContext *encode_ctx(AVCodecInternal *avci) +{ + return (EncodeContext*)avci; +} + int ff_alloc_packet(AVCodecContext *avctx, AVPacket *avpkt, int64_t size) { if (size < 0 || size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) { @@ -157,7 +183,7 @@ static int pad_last_frame(AVCodecContext *s, AVFrame *frame, const AVFrame *src, fail: av_frame_unref(frame); - s->internal->last_audio_frame = 0; + encode_ctx(s->internal)->last_audio_frame = 0; return ret; } @@ -172,11 +198,6 @@ int avcodec_encode_subtitle(AVCodecContext *avctx, uint8_t *buf, int buf_size, ret = ffcodec(avctx->codec)->cb.encode_sub(avctx, buf, buf_size, sub); avctx->frame_num++; -#if FF_API_AVCTX_FRAME_NUMBER -FF_DISABLE_DEPRECATION_WARNINGS - avctx->frame_number = avctx->frame_num; -FF_ENABLE_DEPRECATION_WARNINGS -#endif return ret; } @@ -192,18 +213,27 @@ int ff_encode_get_frame(AVCodecContext *avctx, AVFrame *frame) av_frame_move_ref(frame, avci->buffer_frame); +#if FF_API_FRAME_KEY +FF_DISABLE_DEPRECATION_WARNINGS + if (frame->key_frame) + frame->flags |= AV_FRAME_FLAG_KEY; +FF_ENABLE_DEPRECATION_WARNINGS +#endif +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + if (frame->interlaced_frame) + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (frame->top_field_first) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + return 0; } int ff_encode_reordered_opaque(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame) { -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - avctx->reordered_opaque = frame->reordered_opaque; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { int ret = av_buffer_replace(&pkt->opaque_ref, frame->opaque_ref); if (ret < 0) @@ -356,7 +386,7 @@ static int encode_receive_packet_internal(AVCodecContext *avctx, AVPacket *avpkt } else ret = encode_simple_receive_packet(avctx, avpkt); if (ret >= 0) - avpkt->flags |= avci->intra_only_flag; + avpkt->flags |= encode_ctx(avci)->intra_only_flag; if (ret == AVERROR_EOF) avci->draining_done = 1; @@ -414,6 +444,7 @@ static int encode_generate_icc_profile(av_unused AVCodecContext *c, av_unused AV static int encode_send_frame_internal(AVCodecContext *avctx, const AVFrame *src) { AVCodecInternal *avci = avctx->internal; + EncodeContext *ec = encode_ctx(avci); AVFrame *dst = avci->buffer_frame; int ret; @@ -426,7 +457,7 @@ static int encode_send_frame_internal(AVCodecContext *avctx, const AVFrame *src) /* check for valid frame size */ if (!(avctx->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)) { /* if we already got an undersized frame, that must have been the last */ - if (avctx->internal->last_audio_frame) { + if (ec->last_audio_frame) { av_log(avctx, AV_LOG_ERROR, "frame_size (%d) was not respected for a non-last frame\n", avctx->frame_size); return AVERROR(EINVAL); } @@ -435,7 +466,7 @@ static int encode_send_frame_internal(AVCodecContext *avctx, const AVFrame *src) return AVERROR(EINVAL); } if (src->nb_samples < avctx->frame_size) { - avctx->internal->last_audio_frame = 1; + ec->last_audio_frame = 1; if (!(avctx->codec->capabilities & AV_CODEC_CAP_SMALL_LAST_FRAME)) { int pad_samples = avci->pad_samples ? avci->pad_samples : avctx->frame_size; int out_samples = (src->nb_samples + pad_samples - 1) / pad_samples * pad_samples; @@ -457,13 +488,6 @@ static int encode_send_frame_internal(AVCodecContext *avctx, const AVFrame *src) finish: -#if FF_API_PKT_DURATION -FF_DISABLE_DEPRECATION_WARNINGS - if (dst->pkt_duration && dst->pkt_duration != dst->duration) - dst->duration = dst->pkt_duration; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) { ret = encode_generate_icc_profile(avctx, dst); if (ret < 0) @@ -509,11 +533,6 @@ int attribute_align_arg avcodec_send_frame(AVCodecContext *avctx, const AVFrame } avctx->frame_num++; -#if FF_API_AVCTX_FRAME_NUMBER -FF_DISABLE_DEPRECATION_WARNINGS - avctx->frame_number = avctx->frame_num; -FF_ENABLE_DEPRECATION_WARNINGS -#endif return 0; } @@ -541,25 +560,38 @@ int attribute_align_arg avcodec_receive_packet(AVCodecContext *avctx, AVPacket * static int encode_preinit_video(AVCodecContext *avctx) { + const AVCodec *c = avctx->codec; const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(avctx->pix_fmt); int i; - if (avctx->codec->pix_fmts) { - for (i = 0; avctx->codec->pix_fmts[i] != AV_PIX_FMT_NONE; i++) - if (avctx->pix_fmt == avctx->codec->pix_fmts[i]) + if (!av_get_pix_fmt_name(avctx->pix_fmt)) { + av_log(avctx, AV_LOG_ERROR, "Invalid video pixel format: %d\n", + avctx->pix_fmt); + return AVERROR(EINVAL); + } + + if (c->pix_fmts) { + for (i = 0; c->pix_fmts[i] != AV_PIX_FMT_NONE; i++) + if (avctx->pix_fmt == c->pix_fmts[i]) break; - if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_NONE) { - char buf[128]; - snprintf(buf, sizeof(buf), "%d", avctx->pix_fmt); - av_log(avctx, AV_LOG_ERROR, "Specified pixel format %s is invalid or not supported\n", - (char *)av_x_if_null(av_get_pix_fmt_name(avctx->pix_fmt), buf)); + if (c->pix_fmts[i] == AV_PIX_FMT_NONE) { + av_log(avctx, AV_LOG_ERROR, + "Specified pixel format %s is not supported by the %s encoder.\n", + av_get_pix_fmt_name(avctx->pix_fmt), c->name); + + av_log(avctx, AV_LOG_ERROR, "Supported pixel formats:\n"); + for (int p = 0; c->pix_fmts[p] != AV_PIX_FMT_NONE; p++) { + av_log(avctx, AV_LOG_ERROR, " %s\n", + av_get_pix_fmt_name(c->pix_fmts[p])); + } + return AVERROR(EINVAL); } - if (avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ420P || - avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ411P || - avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ422P || - avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ440P || - avctx->codec->pix_fmts[i] == AV_PIX_FMT_YUVJ444P) + if (c->pix_fmts[i] == AV_PIX_FMT_YUVJ420P || + c->pix_fmts[i] == AV_PIX_FMT_YUVJ411P || + c->pix_fmts[i] == AV_PIX_FMT_YUVJ422P || + c->pix_fmts[i] == AV_PIX_FMT_YUVJ440P || + c->pix_fmts[i] == AV_PIX_FMT_YUVJ444P) avctx->color_range = AVCOL_RANGE_JPEG; } @@ -574,6 +606,8 @@ static int encode_preinit_video(AVCodecContext *avctx) return AVERROR(EINVAL); } +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS if (avctx->ticks_per_frame && avctx->time_base.num && avctx->ticks_per_frame > INT_MAX / avctx->time_base.num) { av_log(avctx, AV_LOG_ERROR, @@ -583,6 +617,8 @@ static int encode_preinit_video(AVCodecContext *avctx) avctx->time_base.den); return AVERROR(EINVAL); } +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (avctx->hw_frames_ctx) { AVHWFramesContext *frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data; @@ -608,56 +644,84 @@ static int encode_preinit_video(AVCodecContext *avctx) static int encode_preinit_audio(AVCodecContext *avctx) { + const AVCodec *c = avctx->codec; int i; - if (avctx->codec->sample_fmts) { - for (i = 0; avctx->codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; i++) { - if (avctx->sample_fmt == avctx->codec->sample_fmts[i]) + if (!av_get_sample_fmt_name(avctx->sample_fmt)) { + av_log(avctx, AV_LOG_ERROR, "Invalid audio sample format: %d\n", + avctx->sample_fmt); + return AVERROR(EINVAL); + } + if (avctx->sample_rate <= 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid audio sample rate: %d\n", + avctx->sample_rate); + return AVERROR(EINVAL); + } + + if (c->sample_fmts) { + for (i = 0; c->sample_fmts[i] != AV_SAMPLE_FMT_NONE; i++) { + if (avctx->sample_fmt == c->sample_fmts[i]) break; if (avctx->ch_layout.nb_channels == 1 && av_get_planar_sample_fmt(avctx->sample_fmt) == - av_get_planar_sample_fmt(avctx->codec->sample_fmts[i])) { - avctx->sample_fmt = avctx->codec->sample_fmts[i]; + av_get_planar_sample_fmt(c->sample_fmts[i])) { + avctx->sample_fmt = c->sample_fmts[i]; break; } } - if (avctx->codec->sample_fmts[i] == AV_SAMPLE_FMT_NONE) { - char buf[128]; - snprintf(buf, sizeof(buf), "%d", avctx->sample_fmt); - av_log(avctx, AV_LOG_ERROR, "Specified sample format %s is invalid or not supported\n", - (char *)av_x_if_null(av_get_sample_fmt_name(avctx->sample_fmt), buf)); + if (c->sample_fmts[i] == AV_SAMPLE_FMT_NONE) { + av_log(avctx, AV_LOG_ERROR, + "Specified sample format %s is not supported by the %s encoder\n", + av_get_sample_fmt_name(avctx->sample_fmt), c->name); + + av_log(avctx, AV_LOG_ERROR, "Supported sample formats:\n"); + for (int p = 0; c->sample_fmts[p] != AV_SAMPLE_FMT_NONE; p++) { + av_log(avctx, AV_LOG_ERROR, " %s\n", + av_get_sample_fmt_name(c->sample_fmts[p])); + } + return AVERROR(EINVAL); } } - if (avctx->codec->supported_samplerates) { - for (i = 0; avctx->codec->supported_samplerates[i] != 0; i++) - if (avctx->sample_rate == avctx->codec->supported_samplerates[i]) + if (c->supported_samplerates) { + for (i = 0; c->supported_samplerates[i] != 0; i++) + if (avctx->sample_rate == c->supported_samplerates[i]) break; - if (avctx->codec->supported_samplerates[i] == 0) { - av_log(avctx, AV_LOG_ERROR, "Specified sample rate %d is not supported\n", - avctx->sample_rate); + if (c->supported_samplerates[i] == 0) { + av_log(avctx, AV_LOG_ERROR, + "Specified sample rate %d is not supported by the %s encoder\n", + avctx->sample_rate, c->name); + + av_log(avctx, AV_LOG_ERROR, "Supported sample rates:\n"); + for (int p = 0; c->supported_samplerates[p]; p++) + av_log(avctx, AV_LOG_ERROR, " %d\n", c->supported_samplerates[p]); + return AVERROR(EINVAL); } } - if (avctx->sample_rate < 0) { - av_log(avctx, AV_LOG_ERROR, "Specified sample rate %d is not supported\n", - avctx->sample_rate); - return AVERROR(EINVAL); - } - if (avctx->codec->ch_layouts) { - for (i = 0; avctx->codec->ch_layouts[i].nb_channels; i++) { - if (!av_channel_layout_compare(&avctx->ch_layout, &avctx->codec->ch_layouts[i])) + if (c->ch_layouts) { + for (i = 0; c->ch_layouts[i].nb_channels; i++) { + if (!av_channel_layout_compare(&avctx->ch_layout, &c->ch_layouts[i])) break; } - if (!avctx->codec->ch_layouts[i].nb_channels) { + if (!c->ch_layouts[i].nb_channels) { char buf[512]; int ret = av_channel_layout_describe(&avctx->ch_layout, buf, sizeof(buf)); - if (ret > 0) - av_log(avctx, AV_LOG_ERROR, "Specified channel layout '%s' is not supported\n", buf); + av_log(avctx, AV_LOG_ERROR, + "Specified channel layout '%s' is not supported by the %s encoder\n", + ret > 0 ? buf : "?", c->name); + + av_log(avctx, AV_LOG_ERROR, "Supported channel layouts:\n"); + for (int p = 0; c->ch_layouts[p].nb_channels; p++) { + ret = av_channel_layout_describe(&c->ch_layouts[p], buf, sizeof(buf)); + av_log(avctx, AV_LOG_ERROR, " %s\n", ret > 0 ? buf : "?"); + } return AVERROR(EINVAL); } } + if (!avctx->bits_per_raw_sample) + avctx->bits_per_raw_sample = av_get_exact_bits_per_sample(avctx->codec_id); if (!avctx->bits_per_raw_sample) avctx->bits_per_raw_sample = 8 * av_get_bytes_per_sample(avctx->sample_fmt); @@ -667,6 +731,7 @@ static int encode_preinit_audio(AVCodecContext *avctx) int ff_encode_preinit(AVCodecContext *avctx) { AVCodecInternal *avci = avctx->internal; + EncodeContext *ec = encode_ctx(avci); int ret = 0; if (avctx->time_base.num <= 0 || avctx->time_base.den <= 0) { @@ -697,7 +762,7 @@ int ff_encode_preinit(AVCodecContext *avctx) avctx->rc_initial_buffer_occupancy = avctx->rc_buffer_size * 3LL / 4; if (avctx->codec_descriptor->props & AV_CODEC_PROP_INTRA_ONLY) - avctx->internal->intra_only_flag = AV_PKT_FLAG_KEY; + ec->intra_only_flag = AV_PKT_FLAG_KEY; if (ffcodec(avctx->codec)->cb_type == FF_CODEC_CB_TYPE_ENCODE) { avci->in_frame = av_frame_alloc(); @@ -772,3 +837,49 @@ int ff_encode_receive_frame(AVCodecContext *avctx, AVFrame *frame) av_frame_move_ref(frame, avci->recon_frame); return 0; } + +void ff_encode_flush_buffers(AVCodecContext *avctx) +{ + AVCodecInternal *avci = avctx->internal; + + if (avci->in_frame) + av_frame_unref(avci->in_frame); + if (avci->recon_frame) + av_frame_unref(avci->recon_frame); +} + +AVCodecInternal *ff_encode_internal_alloc(void) +{ + return av_mallocz(sizeof(EncodeContext)); +} + +AVCPBProperties *ff_encode_add_cpb_side_data(AVCodecContext *avctx) +{ + AVPacketSideData *tmp; + AVCPBProperties *props; + size_t size; + int i; + + for (i = 0; i < avctx->nb_coded_side_data; i++) + if (avctx->coded_side_data[i].type == AV_PKT_DATA_CPB_PROPERTIES) + return (AVCPBProperties *)avctx->coded_side_data[i].data; + + props = av_cpb_properties_alloc(&size); + if (!props) + return NULL; + + tmp = av_realloc_array(avctx->coded_side_data, avctx->nb_coded_side_data + 1, sizeof(*tmp)); + if (!tmp) { + av_freep(&props); + return NULL; + } + + avctx->coded_side_data = tmp; + avctx->nb_coded_side_data++; + + avctx->coded_side_data[avctx->nb_coded_side_data - 1].type = AV_PKT_DATA_CPB_PROPERTIES; + avctx->coded_side_data[avctx->nb_coded_side_data - 1].data = (uint8_t*)props; + avctx->coded_side_data[avctx->nb_coded_side_data - 1].size = size; + + return props; +} diff --git a/libavcodec/encode.h b/libavcodec/encode.h index 26a3304045b..85331e04b7d 100644 --- a/libavcodec/encode.h +++ b/libavcodec/encode.h @@ -27,9 +27,10 @@ #include "packet.h" /** - * avcodec_receive_frame() implementation for encoders. + * Used by some encoders as upper bound for the length of headers. + * TODO: Use proper codec-specific upper bounds. */ -int ff_encode_receive_frame(AVCodecContext *avctx, AVFrame *frame); +#define FF_INPUT_BUFFER_MIN_SIZE 16384 /** * Called by encoders to get the next frame for encoding. @@ -75,15 +76,14 @@ int ff_alloc_packet(AVCodecContext *avctx, AVPacket *avpkt, int64_t size); int ff_encode_reordered_opaque(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame); -/* - * Perform encoder initialization and validation. - * Called when opening the encoder, before the FFCodec.init() call. - */ -int ff_encode_preinit(AVCodecContext *avctx); - int ff_encode_encode_cb(AVCodecContext *avctx, AVPacket *avpkt, AVFrame *frame, int *got_packet); +/** + * Add a CPB properties side data to an encoding context. + */ +AVCPBProperties *ff_encode_add_cpb_side_data(AVCodecContext *avctx); + /** * Rescale from sample rate to AVCodecContext.time_base. */ diff --git a/libavcodec/error_resilience.c b/libavcodec/error_resilience.c index 2aa6f1d8640..9d03e53237c 100644 --- a/libavcodec/error_resilience.c +++ b/libavcodec/error_resilience.c @@ -393,7 +393,7 @@ static void guess_mv(ERContext *s) const ptrdiff_t mb_stride = s->mb_stride; const int mb_width = s->mb_width; int mb_height = s->mb_height; - int i, depth, num_avail; + int i, num_avail; int mb_x, mb_y; ptrdiff_t mot_step, mot_stride; int blocklist_length, next_blocklist_length; @@ -469,7 +469,7 @@ static void guess_mv(ERContext *s) } } - for (depth = 0; ; depth++) { + for (;;) { int changed, pass, none_left; int blocklist_index; @@ -804,7 +804,7 @@ void ff_er_frame_start(ERContext *s) static int er_supported(ERContext *s) { - if(s->avctx->hwaccel && s->avctx->hwaccel->decode_slice || + if (s->avctx->hwaccel || !s->cur_pic.f || s->cur_pic.field_picture ) @@ -828,7 +828,7 @@ void ff_er_add_slice(ERContext *s, int startx, int starty, const int end_xy = s->mb_index2xy[end_i]; int mask = -1; - if (s->avctx->hwaccel && s->avctx->hwaccel->decode_slice) + if (s->avctx->hwaccel) return; if (start_i > end_i || start_xy > end_xy) { @@ -889,7 +889,7 @@ void ff_er_add_slice(ERContext *s, int startx, int starty, } } -void ff_er_frame_end(ERContext *s) +void ff_er_frame_end(ERContext *s, int *decode_error_flags) { int *linesize = NULL; int i, mb_x, mb_y, error, error_type, dc_error, mv_error, ac_error; @@ -1114,7 +1114,10 @@ void ff_er_frame_end(ERContext *s) av_log(s->avctx, AV_LOG_INFO, "concealing %d DC, %d AC, %d MV errors in %c frame\n", dc_error, ac_error, mv_error, av_get_picture_type_char(s->cur_pic.f->pict_type)); - s->cur_pic.f->decode_error_flags |= FF_DECODE_ERROR_CONCEALMENT_ACTIVE; + if (decode_error_flags) + *decode_error_flags |= FF_DECODE_ERROR_CONCEALMENT_ACTIVE; + else + s->cur_pic.f->decode_error_flags |= FF_DECODE_ERROR_CONCEALMENT_ACTIVE; is_intra_likely = is_intra_more_likely(s); diff --git a/libavcodec/error_resilience.h b/libavcodec/error_resilience.h index 47cc8a4fc67..1346639c3ce 100644 --- a/libavcodec/error_resilience.h +++ b/libavcodec/error_resilience.h @@ -24,7 +24,6 @@ #include "avcodec.h" #include "me_cmp.h" -#include "threadframe.h" ///< current MB is the first after a resync marker #define VP_START 1 @@ -40,7 +39,7 @@ typedef struct ERPicture { AVFrame *f; - ThreadFrame *tf; + const struct ThreadFrame *tf; // it is the caller's responsibility to allocate these buffers int16_t (*motion_val[2])[2]; @@ -90,7 +89,17 @@ typedef struct ERContext { } ERContext; void ff_er_frame_start(ERContext *s); -void ff_er_frame_end(ERContext *s); + +/** + * Indicate that a frame has finished decoding and perform error concealment + * in case it has been enabled and is necessary and supported. + * + * @param s ERContext in use + * @param decode_error_flags pointer where updated decode_error_flags are written + * if supplied; if not, the new flags are directly + * applied to the AVFrame whose errors are concealed + */ +void ff_er_frame_end(ERContext *s, int *decode_error_flags); void ff_er_add_slice(ERContext *s, int startx, int starty, int endx, int endy, int status); diff --git a/libavcodec/escape124.c b/libavcodec/escape124.c index 4c19ab9c75d..357320ef94f 100644 --- a/libavcodec/escape124.c +++ b/libavcodec/escape124.c @@ -97,15 +97,12 @@ static CodeBook unpack_codebook(GetBitContext* gb, unsigned depth, cb.size = size; for (i = 0; i < size; i++) { unsigned mask_bits = get_bits(gb, 4); - unsigned color0 = get_bits(gb, 15); - unsigned color1 = get_bits(gb, 15); - - for (j = 0; j < 4; j++) { - if (mask_bits & (1 << j)) - cb.blocks[i].pixels[j] = color1; - else - cb.blocks[i].pixels[j] = color0; - } + unsigned color[2]; + color[0] = get_bits(gb, 15); + color[1] = get_bits(gb, 15); + + for (j = 0; j < 4; j++) + cb.blocks[i].pixels[j] = color[(mask_bits>>j) & 1]; } return cb; } @@ -365,8 +362,7 @@ static int escape124_decode_frame(AVCodecContext *avctx, AVFrame *frame, "Escape sizes: %i, %i, %i\n", frame_size, buf_size, get_bits_count(&gb) / 8); - av_frame_unref(s->frame); - if ((ret = av_frame_ref(s->frame, frame)) < 0) + if ((ret = av_frame_replace(s->frame, frame)) < 0) return ret; *got_frame = 1; diff --git a/libavcodec/evc.h b/libavcodec/evc.h new file mode 100644 index 00000000000..e493455a422 --- /dev/null +++ b/libavcodec/evc.h @@ -0,0 +1,146 @@ +/* + * EVC definitions and enums + * Copyright (c) 2022 Dawid Kozinski + * + * 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 + */ + +#ifndef AVCODEC_EVC_H +#define AVCODEC_EVC_H + +// The length field that indicates the length in bytes of the following NAL unit is configured to be of 4 bytes +#define EVC_NALU_LENGTH_PREFIX_SIZE (4) /* byte */ +#define EVC_NALU_HEADER_SIZE (2) /* byte */ + +/** + * @see ISO_IEC_23094-1_2020, 7.4.2.2 NAL unit header semantic + * Table 4 - NAL unit type codes and NAL unit type classes + */ +enum EVCNALUnitType { + EVC_NOIDR_NUT = 0, /* Coded slice of a non-IDR picture */ + EVC_IDR_NUT = 1, /* Coded slice of an IDR picture */ + EVC_RSV_VCL_NUT02 = 2, + EVC_RSV_VCL_NUT03 = 3, + EVC_RSV_VCL_NUT04 = 4, + EVC_RSV_VCL_NUT05 = 5, + EVC_RSV_VCL_NUT06 = 6, + EVC_RSV_VCL_NUT07 = 7, + EVC_RSV_VCL_NUT08 = 8, + EVC_RSV_VCL_NUT09 = 9, + EVC_RSV_VCL_NUT10 = 10, + EVC_RSV_VCL_NUT11 = 11, + EVC_RSV_VCL_NUT12 = 12, + EVC_RSV_VCL_NUT13 = 13, + EVC_RSV_VCL_NUT14 = 14, + EVC_RSV_VCL_NUT15 = 15, + EVC_RSV_VCL_NUT16 = 16, + EVC_RSV_VCL_NUT17 = 17, + EVC_RSV_VCL_NUT18 = 18, + EVC_RSV_VCL_NUT19 = 19, + EVC_RSV_VCL_NUT20 = 20, + EVC_RSV_VCL_NUT21 = 21, + EVC_RSV_VCL_NUT22 = 22, + EVC_RSV_VCL_NUT23 = 23, + EVC_SPS_NUT = 24, /* Sequence parameter set */ + EVC_PPS_NUT = 25, /* Picture paremeter set */ + EVC_APS_NUT = 26, /* Adaptation parameter set */ + EVC_FD_NUT = 27, /* Filler data */ + EVC_SEI_NUT = 28, /* Supplemental enhancement information */ + EVC_RSV_NONVCL29 = 29, + EVC_RSV_NONVCL30 = 30, + EVC_RSV_NONVCL31 = 31, + EVC_RSV_NONVCL32 = 32, + EVC_RSV_NONVCL33 = 33, + EVC_RSV_NONVCL34 = 34, + EVC_RSV_NONVCL35 = 35, + EVC_RSV_NONVCL36 = 36, + EVC_RSV_NONVCL37 = 37, + EVC_RSV_NONVCL38 = 38, + EVC_RSV_NONVCL39 = 39, + EVC_RSV_NONVCL40 = 40, + EVC_RSV_NONVCL41 = 41, + EVC_RSV_NONVCL42 = 42, + EVC_RSV_NONVCL43 = 43, + EVC_RSV_NONVCL44 = 44, + EVC_RSV_NONVCL45 = 45, + EVC_RSV_NONVCL46 = 46, + EVC_RSV_NONVCL47 = 47, + EVC_RSV_NONVCL48 = 48, + EVC_RSV_NONVCL49 = 49, + EVC_RSV_NONVCL50 = 50, + EVC_RSV_NONVCL51 = 51, + EVC_RSV_NONVCL52 = 52, + EVC_RSV_NONVCL53 = 53, + EVC_RSV_NONVCL54 = 54, + EVC_RSV_NONVCL55 = 55, + EVC_UNSPEC_NUT56 = 56, + EVC_UNSPEC_NUT57 = 57, + EVC_UNSPEC_NUT58 = 58, + EVC_UNSPEC_NUT59 = 59, + EVC_UNSPEC_NUT60 = 60, + EVC_UNSPEC_NUT61 = 61, + EVC_UNSPEC_NUT62 = 62 +}; + +// slice type +// @see ISO_IEC_23094-1_2020 7.4.5 Slice header semantics +// +enum EVCSliceType { + EVC_SLICE_TYPE_B = 0, + EVC_SLICE_TYPE_P = 1, + EVC_SLICE_TYPE_I = 2 +}; + +enum { + // 7.4.3.2: aps_video_parameter_set_id is u(4). + EVC_MAX_APS_COUNT = 32, + + // 7.4.3.1: sps_seq_parameter_set_id is in [0, 15]. + EVC_MAX_SPS_COUNT = 16, + + // 7.4.3.2: pps_pic_parameter_set_id is in [0, 63]. + EVC_MAX_PPS_COUNT = 64, + + // 7.4.5: slice header slice_pic_parameter_set_id in [0, 63] + EVC_MAX_SH_COUNT = 64, + + // E.3.2: cpb_cnt_minus1[i] is in [0, 31]. + EVC_MAX_CPB_CNT = 32, + + // A.4.1: in table A.1 the highest level allows a MaxLumaPs of 35 651 584. + EVC_MAX_LUMA_PS = 35651584, + + EVC_MAX_NUM_REF_PICS = 21, + + EVC_MAX_NUM_RPLS = 64, + + // A.4.1: pic_width_in_luma_samples and pic_height_in_luma_samples are + // constrained to be not greater than sqrt(MaxLumaPs * 8). Hence height/ + // width are bounded above by sqrt(8 * 35651584) = 16888.2 samples. + EVC_MAX_WIDTH = 16888, + EVC_MAX_HEIGHT = 16888, + + // A.4.1: table A.1 allows at most 22 tile rows for any level. + EVC_MAX_TILE_ROWS = 22, + // A.4.1: table A.1 allows at most 20 tile columns for any level. + EVC_MAX_TILE_COLUMNS = 20, + + // A.4.1: table A.1 allows at most 600 slice segments for any level. + EVC_MAX_SLICE_SEGMENTS = 600, +}; + +#endif // AVCODEC_EVC_H diff --git a/libavcodec/evc_parse.c b/libavcodec/evc_parse.c new file mode 100644 index 00000000000..cc9aa106fbc --- /dev/null +++ b/libavcodec/evc_parse.c @@ -0,0 +1,214 @@ +/* + * 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 "golomb.h" +#include "evc.h" +#include "evc_parse.h" + +// @see ISO_IEC_23094-1 (7.3.2.6 Slice layer RBSP syntax) +int ff_evc_parse_slice_header(GetBitContext *gb, EVCParserSliceHeader *sh, + const EVCParamSets *ps, enum EVCNALUnitType nalu_type) +{ + const EVCParserPPS *pps; + const EVCParserSPS *sps; + int num_tiles_in_slice = 0; + unsigned slice_pic_parameter_set_id; + + slice_pic_parameter_set_id = get_ue_golomb_31(gb); + + if (slice_pic_parameter_set_id >= EVC_MAX_PPS_COUNT) + return AVERROR_INVALIDDATA; + + pps = ps->pps[slice_pic_parameter_set_id]; + if(!pps) + return AVERROR_INVALIDDATA; + + sps = ps->sps[pps->pps_seq_parameter_set_id]; + if(!sps) + return AVERROR_INVALIDDATA; + + memset(sh, 0, sizeof(*sh)); + sh->slice_pic_parameter_set_id = slice_pic_parameter_set_id; + + if (!pps->single_tile_in_pic_flag) { + sh->single_tile_in_slice_flag = get_bits1(gb); + sh->first_tile_id = get_bits(gb, pps->tile_id_len_minus1 + 1); + } else + sh->single_tile_in_slice_flag = 1; + + if (!sh->single_tile_in_slice_flag) { + if (pps->arbitrary_slice_present_flag) + sh->arbitrary_slice_flag = get_bits1(gb); + + if (!sh->arbitrary_slice_flag) + sh->last_tile_id = get_bits(gb, pps->tile_id_len_minus1 + 1); + else { + unsigned num_remaining_tiles_in_slice_minus1 = get_ue_golomb_long(gb); + if (num_remaining_tiles_in_slice_minus1 > EVC_MAX_TILE_ROWS * EVC_MAX_TILE_COLUMNS - 2) + return AVERROR_INVALIDDATA; + + num_tiles_in_slice = num_remaining_tiles_in_slice_minus1 + 2; + sh->num_remaining_tiles_in_slice_minus1 = num_remaining_tiles_in_slice_minus1; + for (int i = 0; i < num_tiles_in_slice - 1; ++i) + sh->delta_tile_id_minus1[i] = get_ue_golomb_long(gb); + } + } + + sh->slice_type = get_ue_golomb_31(gb); + + if (nalu_type == EVC_IDR_NUT) + sh->no_output_of_prior_pics_flag = get_bits1(gb); + + if (sps->sps_mmvd_flag && ((sh->slice_type == EVC_SLICE_TYPE_B) || (sh->slice_type == EVC_SLICE_TYPE_P))) + sh->mmvd_group_enable_flag = get_bits1(gb); + else + sh->mmvd_group_enable_flag = 0; + + if (sps->sps_alf_flag) { + int ChromaArrayType = sps->chroma_format_idc; + + sh->slice_alf_enabled_flag = get_bits1(gb); + + if (sh->slice_alf_enabled_flag) { + sh->slice_alf_luma_aps_id = get_bits(gb, 5); + sh->slice_alf_map_flag = get_bits1(gb); + sh->slice_alf_chroma_idc = get_bits(gb, 2); + + if ((ChromaArrayType == 1 || ChromaArrayType == 2) && sh->slice_alf_chroma_idc > 0) + sh->slice_alf_chroma_aps_id = get_bits(gb, 5); + } + if (ChromaArrayType == 3) { + int sliceChromaAlfEnabledFlag = 0; + int sliceChroma2AlfEnabledFlag = 0; + + if (sh->slice_alf_chroma_idc == 1) { // @see ISO_IEC_23094-1 (7.4.5) + sliceChromaAlfEnabledFlag = 1; + sliceChroma2AlfEnabledFlag = 0; + } else if (sh->slice_alf_chroma_idc == 2) { + sliceChromaAlfEnabledFlag = 0; + sliceChroma2AlfEnabledFlag = 1; + } else if (sh->slice_alf_chroma_idc == 3) { + sliceChromaAlfEnabledFlag = 1; + sliceChroma2AlfEnabledFlag = 1; + } else { + sliceChromaAlfEnabledFlag = 0; + sliceChroma2AlfEnabledFlag = 0; + } + + if (!sh->slice_alf_enabled_flag) + sh->slice_alf_chroma_idc = get_bits(gb, 2); + + if (sliceChromaAlfEnabledFlag) { + sh->slice_alf_chroma_aps_id = get_bits(gb, 5); + sh->slice_alf_chroma_map_flag = get_bits1(gb); + } + + if (sliceChroma2AlfEnabledFlag) { + sh->slice_alf_chroma2_aps_id = get_bits(gb, 5); + sh->slice_alf_chroma2_map_flag = get_bits1(gb); + } + } + } + + if (nalu_type != EVC_IDR_NUT) { + if (sps->sps_pocs_flag) + sh->slice_pic_order_cnt_lsb = get_bits(gb, sps->log2_max_pic_order_cnt_lsb_minus4 + 4); + } + + // @note + // If necessary, add the missing fields to the EVCParserSliceHeader structure + // and then extend parser implementation + + return 0; +} + +int ff_evc_derive_poc(const EVCParamSets *ps, const EVCParserSliceHeader *sh, + EVCParserPoc *poc, enum EVCNALUnitType nalu_type, int tid) +{ + const EVCParserPPS *pps = ps->pps[sh->slice_pic_parameter_set_id]; + const EVCParserSPS *sps; + + if (!pps) + return AVERROR_INVALIDDATA; + + sps = ps->sps[pps->pps_seq_parameter_set_id]; + if (!sps) + return AVERROR_INVALIDDATA; + + if (sps->sps_pocs_flag) { + int PicOrderCntMsb = 0; + poc->prevPicOrderCntVal = poc->PicOrderCntVal; + + if (nalu_type == EVC_IDR_NUT) + PicOrderCntMsb = 0; + else { + int MaxPicOrderCntLsb = 1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4); + int prevPicOrderCntLsb = poc->PicOrderCntVal & (MaxPicOrderCntLsb - 1); + int prevPicOrderCntMsb = poc->PicOrderCntVal - prevPicOrderCntLsb; + + if ((sh->slice_pic_order_cnt_lsb < prevPicOrderCntLsb) && + ((prevPicOrderCntLsb - sh->slice_pic_order_cnt_lsb) >= (MaxPicOrderCntLsb / 2))) + PicOrderCntMsb = prevPicOrderCntMsb + MaxPicOrderCntLsb; + else if ((sh->slice_pic_order_cnt_lsb > prevPicOrderCntLsb) && + ((sh->slice_pic_order_cnt_lsb - prevPicOrderCntLsb) > (MaxPicOrderCntLsb / 2))) + PicOrderCntMsb = prevPicOrderCntMsb - MaxPicOrderCntLsb; + else + PicOrderCntMsb = prevPicOrderCntMsb; + } + poc->PicOrderCntVal = PicOrderCntMsb + sh->slice_pic_order_cnt_lsb; + } else { + if (nalu_type == EVC_IDR_NUT) { + poc->PicOrderCntVal = 0; + poc->DocOffset = -1; + } else { + int SubGopLength = 1 << sps->log2_sub_gop_length; + + if (tid > (SubGopLength > 1 ? 1 + av_log2(SubGopLength - 1) : 0)) + return AVERROR_INVALIDDATA; + + if (tid == 0) { + poc->PicOrderCntVal = poc->prevPicOrderCntVal + SubGopLength; + poc->DocOffset = 0; + poc->prevPicOrderCntVal = poc->PicOrderCntVal; + } else { + int ExpectedTemporalId; + int PocOffset; + int prevDocOffset = poc->DocOffset; + + poc->DocOffset = (prevDocOffset + 1) % SubGopLength; + if (poc->DocOffset == 0) { + poc->prevPicOrderCntVal += SubGopLength; + ExpectedTemporalId = 0; + } else + ExpectedTemporalId = 1 + av_log2(poc->DocOffset); + + while (tid != ExpectedTemporalId) { + poc->DocOffset = (poc->DocOffset + 1) % SubGopLength; + if (poc->DocOffset == 0) + ExpectedTemporalId = 0; + else + ExpectedTemporalId = 1 + av_log2(poc->DocOffset); + } + PocOffset = (int)(SubGopLength * ((2.0 * poc->DocOffset + 1) / (1 << tid) - 2)); + poc->PicOrderCntVal = poc->prevPicOrderCntVal + PocOffset; + } + } + } + + return 0; +} diff --git a/libavcodec/evc_parse.h b/libavcodec/evc_parse.h new file mode 100644 index 00000000000..4712310826b --- /dev/null +++ b/libavcodec/evc_parse.h @@ -0,0 +1,105 @@ +/* + * 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 + * EVC decoder/parser shared code + */ + +#ifndef AVCODEC_EVC_PARSE_H +#define AVCODEC_EVC_PARSE_H + +#include + +#include "libavutil/intreadwrite.h" +#include "libavutil/log.h" +#include "evc.h" +#include "evc_ps.h" + +// The sturcture reflects Slice Header RBSP(raw byte sequence payload) layout +// @see ISO_IEC_23094-1 section 7.3.2.6 +// +// The following descriptors specify the parsing process of each element +// u(n) - unsigned integer using n bits +// ue(v) - unsigned integer 0-th order Exp_Golomb-coded syntax element with the left bit first +// u(n) - unsigned integer using n bits. +// When n is "v" in the syntax table, the number of bits varies in a manner dependent on the value of other syntax elements. +typedef struct EVCParserSliceHeader { + uint8_t slice_pic_parameter_set_id; // ue(v) + uint8_t single_tile_in_slice_flag; // u(1) + uint8_t first_tile_id; // u(v) + uint8_t arbitrary_slice_flag; // u(1) + uint8_t last_tile_id; // u(v) + uint32_t num_remaining_tiles_in_slice_minus1; // ue(v) + uint16_t delta_tile_id_minus1[EVC_MAX_TILE_ROWS * EVC_MAX_TILE_COLUMNS]; // ue(v) + + uint8_t slice_type; // ue(v) + uint8_t no_output_of_prior_pics_flag; // u(1) + uint8_t mmvd_group_enable_flag; // u(1) + uint8_t slice_alf_enabled_flag; // u(1) + + uint8_t slice_alf_luma_aps_id; // u(5) + uint8_t slice_alf_map_flag; // u(1) + uint8_t slice_alf_chroma_idc; // u(2) + uint8_t slice_alf_chroma_aps_id; // u(5) + uint8_t slice_alf_chroma_map_flag; // u(1) + uint8_t slice_alf_chroma2_aps_id; // u(5) + uint8_t slice_alf_chroma2_map_flag; // u(1) + uint16_t slice_pic_order_cnt_lsb; // u(v) + + // @note + // Currently the structure does not reflect the entire Slice Header RBSP layout. + // It contains only the fields that are necessary to read from the NAL unit all the values + // necessary for the correct initialization of the AVCodecContext structure. + + // @note + // If necessary, add the missing fields to the structure to reflect + // the contents of the entire NAL unit of the SPS type + +} EVCParserSliceHeader; + +// picture order count of the current picture +typedef struct EVCParserPoc { + int PicOrderCntVal; // current picture order count value + int prevPicOrderCntVal; // the picture order count of the previous Tid0 picture + int DocOffset; // the decoding order count of the previous picture +} EVCParserPoc; + +static inline uint32_t evc_read_nal_unit_length(const uint8_t *bits, int bits_size, void *logctx) +{ + uint32_t nalu_len = 0; + + if (bits_size < EVC_NALU_LENGTH_PREFIX_SIZE) { + av_log(logctx, AV_LOG_ERROR, "Can't read NAL unit length\n"); + return 0; + } + + nalu_len = AV_RB32(bits); + + return nalu_len; +} + +int ff_evc_parse_slice_header(GetBitContext *gb, EVCParserSliceHeader *sh, + const EVCParamSets *ps, enum EVCNALUnitType nalu_type); + +// POC (picture order count of the current picture) derivation +// @see ISO/IEC 23094-1:2020(E) 8.3.1 Decoding process for picture order count +int ff_evc_derive_poc(const EVCParamSets *ps, const EVCParserSliceHeader *sh, + EVCParserPoc *poc, enum EVCNALUnitType nalu_type, int tid); + +#endif /* AVCODEC_EVC_PARSE_H */ diff --git a/libavcodec/evc_parser.c b/libavcodec/evc_parser.c new file mode 100644 index 00000000000..8590ebcdafc --- /dev/null +++ b/libavcodec/evc_parser.c @@ -0,0 +1,377 @@ +/* + * EVC format parser + * + * Copyright (C) 2021 Dawid Kozinski + * + * 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 "avcodec.h" +#include "bytestream.h" +#include "evc.h" +#include "evc_parse.h" + +typedef struct EVCParserContext { + EVCParamSets ps; + EVCParserPoc poc; + + int parsed_extradata; +} EVCParserContext; + +#define NUM_CHROMA_FORMATS 4 // @see ISO_IEC_23094-1 section 6.2 table 2 + +static const enum AVPixelFormat pix_fmts_8bit[NUM_CHROMA_FORMATS] = { + AV_PIX_FMT_GRAY8, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P +}; + +static const enum AVPixelFormat pix_fmts_9bit[NUM_CHROMA_FORMATS] = { + AV_PIX_FMT_GRAY9, AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9 +}; + +static const enum AVPixelFormat pix_fmts_10bit[NUM_CHROMA_FORMATS] = { + AV_PIX_FMT_GRAY10, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10 +}; + +static const enum AVPixelFormat pix_fmts_12bit[NUM_CHROMA_FORMATS] = { + AV_PIX_FMT_GRAY12, AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12 +}; + +static const enum AVPixelFormat pix_fmts_14bit[NUM_CHROMA_FORMATS] = { + AV_PIX_FMT_GRAY14, AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14 +}; + +static const enum AVPixelFormat pix_fmts_16bit[NUM_CHROMA_FORMATS] = { + AV_PIX_FMT_GRAY16, AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16 +}; + +static int parse_nal_unit(AVCodecParserContext *s, AVCodecContext *avctx, + const uint8_t *buf, int buf_size) +{ + EVCParserContext *ctx = s->priv_data; + GetBitContext gb; + int nalu_type, tid; + int ret; + + if (buf_size <= 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit size: (%d)\n", buf_size); + return AVERROR_INVALIDDATA; + } + + ret = init_get_bits8(&gb, buf, buf_size); + if (ret < 0) + return ret; + + // @see ISO_IEC_23094-1_2020, 7.4.2.2 NAL unit header semantic (Table 4 - NAL unit type codes and NAL unit type classes) + // @see enum EVCNALUnitType in evc.h + if (get_bits1(&gb)) {// forbidden_zero_bit + av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit header\n"); + return AVERROR_INVALIDDATA; + } + + nalu_type = get_bits(&gb, 6) - 1; + if (nalu_type < EVC_NOIDR_NUT || nalu_type > EVC_UNSPEC_NUT62) { + av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit type: (%d)\n", nalu_type); + return AVERROR_INVALIDDATA; + } + + tid = get_bits(&gb, 3); + skip_bits(&gb, 5); // nuh_reserved_zero_5bits + skip_bits1(&gb); // nuh_extension_flag + + switch (nalu_type) { + case EVC_SPS_NUT: + ret = ff_evc_parse_sps(&gb, &ctx->ps); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "SPS parsing error\n"); + return ret; + } + break; + case EVC_PPS_NUT: + ret = ff_evc_parse_pps(&gb, &ctx->ps); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "PPS parsing error\n"); + return ret; + } + break; + case EVC_IDR_NUT: // Coded slice of a IDR or non-IDR picture + case EVC_NOIDR_NUT: { + const EVCParserPPS *pps; + const EVCParserSPS *sps; + EVCParserSliceHeader sh; + int bit_depth; + + ret = ff_evc_parse_slice_header(&gb, &sh, &ctx->ps, nalu_type); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Slice header parsing error\n"); + return ret; + } + + pps = ctx->ps.pps[sh.slice_pic_parameter_set_id]; + sps = ctx->ps.sps[pps->pps_seq_parameter_set_id]; + av_assert0(sps && pps); + + s->coded_width = sps->pic_width_in_luma_samples; + s->coded_height = sps->pic_height_in_luma_samples; + + if (sps->picture_cropping_flag) { + s->width = sps->pic_width_in_luma_samples - sps->picture_crop_left_offset - sps->picture_crop_right_offset; + s->height = sps->pic_height_in_luma_samples - sps->picture_crop_top_offset - sps->picture_crop_bottom_offset; + } else { + s->width = sps->pic_width_in_luma_samples; + s->height = sps->pic_height_in_luma_samples; + } + + switch (sh.slice_type) { + case EVC_SLICE_TYPE_B: { + s->pict_type = AV_PICTURE_TYPE_B; + break; + } + case EVC_SLICE_TYPE_P: { + s->pict_type = AV_PICTURE_TYPE_P; + break; + } + case EVC_SLICE_TYPE_I: { + s->pict_type = AV_PICTURE_TYPE_I; + break; + } + default: { + s->pict_type = AV_PICTURE_TYPE_NONE; + } + } + + avctx->profile = sps->profile_idc; + + if (sps->vui_parameters_present_flag && sps->vui_parameters.timing_info_present_flag) { + int64_t num = sps->vui_parameters.num_units_in_tick; + int64_t den = sps->vui_parameters.time_scale; + if (num != 0 && den != 0) + av_reduce(&avctx->framerate.den, &avctx->framerate.num, num, den, 1 << 30); + } else + avctx->framerate = (AVRational) { 0, 1 }; + + bit_depth = sps->bit_depth_chroma_minus8 + 8; + s->format = AV_PIX_FMT_NONE; + + switch (bit_depth) { + case 8: + s->format = pix_fmts_8bit[sps->chroma_format_idc]; + break; + case 9: + s->format = pix_fmts_9bit[sps->chroma_format_idc]; + break; + case 10: + s->format = pix_fmts_10bit[sps->chroma_format_idc]; + break; + case 12: + s->format = pix_fmts_12bit[sps->chroma_format_idc]; + break; + case 14: + s->format = pix_fmts_14bit[sps->chroma_format_idc]; + break; + case 16: + s->format = pix_fmts_16bit[sps->chroma_format_idc]; + break; + } + + s->key_frame = (nalu_type == EVC_IDR_NUT) ? 1 : 0; + + // POC (picture order count of the current picture) derivation + // @see ISO/IEC 23094-1:2020(E) 8.3.1 Decoding process for picture order count + ret = ff_evc_derive_poc(&ctx->ps, &sh, &ctx->poc, nalu_type, tid); + if (ret < 0) + return ret; + + s->output_picture_number = ctx->poc.PicOrderCntVal; + + break; + } + case EVC_SEI_NUT: // Supplemental Enhancement Information + case EVC_APS_NUT: // Adaptation parameter set + case EVC_FD_NUT: // Filler data + default: + break; + } + + return 0; +} + +/** + * Parse NAL units of found picture and decode some basic information. + * + * @param s codec parser context + * @param avctx codec context + * @param buf buffer with field/frame data + * @param buf_size size of the buffer + */ +static int parse_nal_units(AVCodecParserContext *s, AVCodecContext *avctx, const uint8_t *buf, int buf_size) +{ + const uint8_t *data = buf; + int data_size = buf_size; + + while (data_size > 0) { + int nalu_size = 0; + int ret; + + // Buffer size is not enough for buffer to store NAL unit 4-bytes prefix (length) + if (data_size < EVC_NALU_LENGTH_PREFIX_SIZE) + return AVERROR_INVALIDDATA; + + nalu_size = evc_read_nal_unit_length(data, data_size, avctx); + + + data += EVC_NALU_LENGTH_PREFIX_SIZE; + data_size -= EVC_NALU_LENGTH_PREFIX_SIZE; + + if (data_size < nalu_size) + return AVERROR_INVALIDDATA; + + ret = parse_nal_unit(s, avctx, data, nalu_size); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Parsing of NAL unit failed\n"); + return AVERROR_INVALIDDATA; + } + + data += nalu_size; + data_size -= nalu_size; + } + return 0; +} + +// Decoding nal units from evcC (EVCDecoderConfigurationRecord) +// @see @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.2 +static int decode_extradata(AVCodecParserContext *s, AVCodecContext *avctx) +{ + const uint8_t *data = avctx->extradata; + int size = avctx->extradata_size; + int ret = 0; + GetByteContext gb; + + bytestream2_init(&gb, data, size); + + if (!data || size <= 0) + return -1; + + // extradata is encoded as evcC format. + if (data[0] == 1) { + int num_of_arrays; // indicates the number of arrays of NAL units of the indicated type(s) + + int nalu_length_field_size; // indicates the length in bytes of the NALUnitLenght field in EVC video stream sample in the stream + // The value of this field shall be one of 0, 1, or 3 corresponding to a length encoded with 1, 2, or 4 bytes, respectively. + + if (bytestream2_get_bytes_left(&gb) < 18) { + av_log(avctx, AV_LOG_ERROR, "evcC %d too short\n", size); + return AVERROR_INVALIDDATA; + } + + bytestream2_skip(&gb, 16); + + // @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.3 + // LengthSizeMinusOne plus 1 indicates the length in bytes of the NALUnitLength field in a EVC video stream sample in the stream to which this configuration record applies. For example, a size of one byte is indicated with a value of 0. + // The value of this field shall be one of 0, 1, or 3 corresponding to a length encoded with 1, 2, or 4 bytes, respectively. + nalu_length_field_size = (bytestream2_get_byte(&gb) & 3) + 1; + if( nalu_length_field_size != 1 && + nalu_length_field_size != 2 && + nalu_length_field_size != 4 ) { + av_log(avctx, AV_LOG_ERROR, "The length in bytes of the NALUnitLenght field in a EVC video stream has unsupported value of %d\n", nalu_length_field_size); + return AVERROR_INVALIDDATA; + } + + num_of_arrays = bytestream2_get_byte(&gb); + + /* Decode nal units from evcC. */ + for (int i = 0; i < num_of_arrays; i++) { + + // @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.3 + // NAL_unit_type indicates the type of the NAL units in the following array (which shall be all of that type); + // - it takes a value as defined in ISO/IEC 23094-1; + // - it is restricted to take one of the values indicating a SPS, PPS, APS, or SEI NAL unit. + int nal_unit_type = bytestream2_get_byte(&gb) & 0x3f; + int num_nalus = bytestream2_get_be16(&gb); + + for (int j = 0; j < num_nalus; j++) { + + int nal_unit_length = bytestream2_get_be16(&gb); + + if (bytestream2_get_bytes_left(&gb) < nal_unit_length) { + av_log(avctx, AV_LOG_ERROR, "Invalid NAL unit size in extradata.\n"); + return AVERROR_INVALIDDATA; + } + + if( nal_unit_type == EVC_SPS_NUT || + nal_unit_type == EVC_PPS_NUT || + nal_unit_type == EVC_APS_NUT || + nal_unit_type == EVC_SEI_NUT ) { + if (parse_nal_unit(s, avctx, gb.buffer, nal_unit_length) != 0) { + av_log(avctx, AV_LOG_ERROR, "Parsing of NAL unit failed\n"); + return AVERROR_INVALIDDATA; + } + } + + bytestream2_skip(&gb, nal_unit_length); + } + } + } else + return -1; + + return ret; +} + +static int evc_parse(AVCodecParserContext *s, AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + int next; + int ret; + EVCParserContext *ctx = s->priv_data; + + s->picture_structure = AV_PICTURE_STRUCTURE_FRAME; + s->key_frame = 0; + + if (avctx->extradata && !ctx->parsed_extradata) { + decode_extradata(s, avctx); + ctx->parsed_extradata = 1; + } + + next = buf_size; + + ret = parse_nal_units(s, avctx, buf, buf_size); + if(ret < 0) { + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } + + // poutbuf contains just one Access Unit + *poutbuf = buf; + *poutbuf_size = buf_size; + + return next; +} + +static void evc_parser_close(AVCodecParserContext *s) +{ + EVCParserContext *ctx = s->priv_data; + + ff_evc_ps_free(&ctx->ps); +} + +const AVCodecParser ff_evc_parser = { + .codec_ids = { AV_CODEC_ID_EVC }, + .priv_data_size = sizeof(EVCParserContext), + .parser_parse = evc_parse, + .parser_close = evc_parser_close, +}; diff --git a/libavcodec/evc_ps.c b/libavcodec/evc_ps.c new file mode 100644 index 00000000000..19895d404d0 --- /dev/null +++ b/libavcodec/evc_ps.c @@ -0,0 +1,442 @@ +/* + * 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 "get_bits.h" +#include "golomb.h" +#include "evc.h" +#include "evc_ps.h" + +#define EXTENDED_SAR 255 + +// @see ISO_IEC_23094-1 (7.3.7 Reference picture list structure syntax) +static int ref_pic_list_struct(const EVCParserSPS *sps, GetBitContext *gb, RefPicListStruct *rpl) +{ + uint32_t delta_poc_st, strp_entry_sign_flag = 0; + rpl->ref_pic_num = get_ue_golomb_long(gb); + + if ((unsigned)rpl->ref_pic_num > sps->sps_max_dec_pic_buffering_minus1) + return AVERROR_INVALIDDATA; + + if (rpl->ref_pic_num > 0) { + delta_poc_st = get_ue_golomb_long(gb); + + rpl->ref_pics[0] = delta_poc_st; + if (rpl->ref_pics[0] != 0) { + strp_entry_sign_flag = get_bits(gb, 1); + + rpl->ref_pics[0] *= 1 - (strp_entry_sign_flag << 1); + } + } + + for (int i = 1; i < rpl->ref_pic_num; ++i) { + delta_poc_st = get_ue_golomb_long(gb); + if (delta_poc_st != 0) + strp_entry_sign_flag = get_bits(gb, 1); + rpl->ref_pics[i] = rpl->ref_pics[i - 1] + delta_poc_st * (1 - (strp_entry_sign_flag << 1)); + } + + return 0; +} + +// @see ISO_IEC_23094-1 (E.2.2 HRD parameters syntax) +static int hrd_parameters(GetBitContext *gb, HRDParameters *hrd) +{ + hrd->cpb_cnt_minus1 = get_ue_golomb_31(gb); + if (hrd->cpb_cnt_minus1 >= FF_ARRAY_ELEMS(hrd->cpb_size_value_minus1)) + return AVERROR_INVALIDDATA; + + hrd->bit_rate_scale = get_bits(gb, 4); + hrd->cpb_size_scale = get_bits(gb, 4); + for (int SchedSelIdx = 0; SchedSelIdx <= hrd->cpb_cnt_minus1; SchedSelIdx++) { + hrd->bit_rate_value_minus1[SchedSelIdx] = get_ue_golomb_long(gb); + hrd->cpb_size_value_minus1[SchedSelIdx] = get_ue_golomb_long(gb); + hrd->cbr_flag[SchedSelIdx] = get_bits(gb, 1); + } + hrd->initial_cpb_removal_delay_length_minus1 = get_bits(gb, 5); + hrd->cpb_removal_delay_length_minus1 = get_bits(gb, 5); + hrd->cpb_removal_delay_length_minus1 = get_bits(gb, 5); + hrd->time_offset_length = get_bits(gb, 5); + + return 0; +} + +// @see ISO_IEC_23094-1 (E.2.1 VUI parameters syntax) +static int vui_parameters(GetBitContext *gb, VUIParameters *vui) +{ + int ret; + + vui->aspect_ratio_info_present_flag = get_bits(gb, 1); + if (vui->aspect_ratio_info_present_flag) { + vui->aspect_ratio_idc = get_bits(gb, 8); + if (vui->aspect_ratio_idc == EXTENDED_SAR) { + vui->sar_width = get_bits(gb, 16); + vui->sar_height = get_bits(gb, 16); + } + } + vui->overscan_info_present_flag = get_bits(gb, 1); + if (vui->overscan_info_present_flag) + vui->overscan_appropriate_flag = get_bits(gb, 1); + vui->video_signal_type_present_flag = get_bits(gb, 1); + if (vui->video_signal_type_present_flag) { + vui->video_format = get_bits(gb, 3); + vui->video_full_range_flag = get_bits(gb, 1); + vui->colour_description_present_flag = get_bits(gb, 1); + if (vui->colour_description_present_flag) { + vui->colour_primaries = get_bits(gb, 8); + vui->transfer_characteristics = get_bits(gb, 8); + vui->matrix_coefficients = get_bits(gb, 8); + } + } + vui->chroma_loc_info_present_flag = get_bits(gb, 1); + if (vui->chroma_loc_info_present_flag) { + vui->chroma_sample_loc_type_top_field = get_ue_golomb_31(gb); + vui->chroma_sample_loc_type_bottom_field = get_ue_golomb_31(gb); + } + vui->neutral_chroma_indication_flag = get_bits(gb, 1); + + vui->field_seq_flag = get_bits(gb, 1); + + vui->timing_info_present_flag = get_bits(gb, 1); + if (vui->timing_info_present_flag) { + vui->num_units_in_tick = get_bits_long(gb, 32); + vui->time_scale = get_bits_long(gb, 32); + vui->fixed_pic_rate_flag = get_bits(gb, 1); + } + vui->nal_hrd_parameters_present_flag = get_bits(gb, 1); + if (vui->nal_hrd_parameters_present_flag) { + ret = hrd_parameters(gb, &vui->hrd_parameters); + if (ret < 0) + return ret; + } + + vui->vcl_hrd_parameters_present_flag = get_bits(gb, 1); + if (vui->vcl_hrd_parameters_present_flag) { + ret = hrd_parameters(gb, &vui->hrd_parameters); + if (ret < 0) + return ret; + } + if (vui->nal_hrd_parameters_present_flag || vui->vcl_hrd_parameters_present_flag) + vui->low_delay_hrd_flag = get_bits(gb, 1); + vui->pic_struct_present_flag = get_bits(gb, 1); + vui->bitstream_restriction_flag = get_bits(gb, 1); + if (vui->bitstream_restriction_flag) { + vui->motion_vectors_over_pic_boundaries_flag = get_bits(gb, 1); + vui->max_bytes_per_pic_denom = get_ue_golomb_31(gb); + vui->max_bits_per_mb_denom = get_ue_golomb_31(gb); + vui->log2_max_mv_length_horizontal = get_ue_golomb_31(gb); + vui->log2_max_mv_length_vertical = get_ue_golomb_31(gb); + vui->num_reorder_pics = get_ue_golomb_long(gb); + vui->max_dec_pic_buffering = get_ue_golomb_long(gb); + } + + return 0; +} + +// @see ISO_IEC_23094-1 (7.3.2.1 SPS RBSP syntax) +int ff_evc_parse_sps(GetBitContext *gb, EVCParamSets *ps) +{ + EVCParserSPS *sps; + unsigned sps_seq_parameter_set_id; + int ret; + + sps_seq_parameter_set_id = get_ue_golomb(gb); + + if (sps_seq_parameter_set_id >= EVC_MAX_SPS_COUNT) + return AVERROR_INVALIDDATA; + + sps = av_mallocz(sizeof(*sps)); + if (!sps) + return AVERROR(ENOMEM); + + sps->sps_seq_parameter_set_id = sps_seq_parameter_set_id; + + // the Baseline profile is indicated by profile_idc eqal to 0 + // the Main profile is indicated by profile_idc eqal to 1 + sps->profile_idc = get_bits(gb, 8); + + sps->level_idc = get_bits(gb, 8); + + skip_bits_long(gb, 32); /* skip toolset_idc_h */ + skip_bits_long(gb, 32); /* skip toolset_idc_l */ + + // 0 - monochrome + // 1 - 4:2:0 + // 2 - 4:2:2 + // 3 - 4:4:4 + sps->chroma_format_idc = get_ue_golomb_31(gb); + if (sps->chroma_format_idc > 3) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + sps->pic_width_in_luma_samples = get_ue_golomb_long(gb); + sps->pic_height_in_luma_samples = get_ue_golomb_long(gb); + + sps->bit_depth_luma_minus8 = get_ue_golomb_31(gb); + sps->bit_depth_chroma_minus8 = get_ue_golomb_31(gb); + + sps->sps_btt_flag = get_bits1(gb); + if (sps->sps_btt_flag) { + sps->log2_ctu_size_minus2 = get_ue_golomb_long(gb); + sps->log2_min_cb_size_minus2 = get_ue_golomb_long(gb); + sps->log2_diff_ctu_max_14_cb_size = get_ue_golomb_long(gb); + sps->log2_diff_ctu_max_tt_cb_size = get_ue_golomb_long(gb); + sps->log2_diff_min_cb_min_tt_cb_size_minus2 = get_ue_golomb_long(gb); + } + + sps->sps_suco_flag = get_bits1(gb); + if (sps->sps_suco_flag) { + sps->log2_diff_ctu_size_max_suco_cb_size = get_ue_golomb_long(gb); + sps->log2_diff_max_suco_min_suco_cb_size = get_ue_golomb_long(gb); + } + + sps->sps_admvp_flag = get_bits1(gb); + if (sps->sps_admvp_flag) { + sps->sps_affine_flag = get_bits1(gb); + sps->sps_amvr_flag = get_bits1(gb); + sps->sps_dmvr_flag = get_bits1(gb); + sps->sps_mmvd_flag = get_bits1(gb); + sps->sps_hmvp_flag = get_bits1(gb); + } + + sps->sps_eipd_flag = get_bits1(gb); + if (sps->sps_eipd_flag) { + sps->sps_ibc_flag = get_bits1(gb); + if (sps->sps_ibc_flag) + sps->log2_max_ibc_cand_size_minus2 = get_ue_golomb(gb); + } + + sps->sps_cm_init_flag = get_bits1(gb); + if (sps->sps_cm_init_flag) + sps->sps_adcc_flag = get_bits1(gb); + + sps->sps_iqt_flag = get_bits1(gb); + if (sps->sps_iqt_flag) + sps->sps_ats_flag = get_bits1(gb); + + sps->sps_addb_flag = get_bits1(gb); + sps->sps_alf_flag = get_bits1(gb); + sps->sps_htdf_flag = get_bits1(gb); + sps->sps_rpl_flag = get_bits1(gb); + sps->sps_pocs_flag = get_bits1(gb); + sps->sps_dquant_flag = get_bits1(gb); + sps->sps_dra_flag = get_bits1(gb); + + if (sps->sps_pocs_flag) { + sps->log2_max_pic_order_cnt_lsb_minus4 = get_ue_golomb(gb); + if (sps->log2_max_pic_order_cnt_lsb_minus4 > 12U) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + } + + if (!sps->sps_pocs_flag || !sps->sps_rpl_flag) { + sps->log2_sub_gop_length = get_ue_golomb(gb); + if (sps->log2_sub_gop_length > 5U) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + if (sps->log2_sub_gop_length == 0) + sps->log2_ref_pic_gap_length = get_ue_golomb(gb); + } + + if (!sps->sps_rpl_flag) + sps->max_num_tid0_ref_pics = get_ue_golomb_31(gb); + else { + sps->sps_max_dec_pic_buffering_minus1 = get_ue_golomb_long(gb); + if ((unsigned)sps->sps_max_dec_pic_buffering_minus1 > 16 - 1) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + sps->long_term_ref_pic_flag = get_bits1(gb); + sps->rpl1_same_as_rpl0_flag = get_bits1(gb); + sps->num_ref_pic_list_in_sps[0] = get_ue_golomb(gb); + + if ((unsigned)sps->num_ref_pic_list_in_sps[0] >= EVC_MAX_NUM_RPLS) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + for (int i = 0; i < sps->num_ref_pic_list_in_sps[0]; ++i) { + ret = ref_pic_list_struct(sps, gb, &sps->rpls[0][i]); + if (ret < 0) + goto fail; + } + + if (!sps->rpl1_same_as_rpl0_flag) { + sps->num_ref_pic_list_in_sps[1] = get_ue_golomb(gb); + if ((unsigned)sps->num_ref_pic_list_in_sps[1] >= EVC_MAX_NUM_RPLS) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + for (int i = 0; i < sps->num_ref_pic_list_in_sps[1]; ++i) { + ret = ref_pic_list_struct(sps, gb, &sps->rpls[1][i]); + if (ret < 0) + goto fail; + } + } + } + + sps->picture_cropping_flag = get_bits1(gb); + + if (sps->picture_cropping_flag) { + sps->picture_crop_left_offset = get_ue_golomb_long(gb); + sps->picture_crop_right_offset = get_ue_golomb_long(gb); + sps->picture_crop_top_offset = get_ue_golomb_long(gb); + sps->picture_crop_bottom_offset = get_ue_golomb_long(gb); + } + + if (sps->chroma_format_idc != 0) { + sps->chroma_qp_table_struct.chroma_qp_table_present_flag = get_bits1(gb); + + if (sps->chroma_qp_table_struct.chroma_qp_table_present_flag) { + sps->chroma_qp_table_struct.same_qp_table_for_chroma = get_bits1(gb); + sps->chroma_qp_table_struct.global_offset_flag = get_bits1(gb); + for (int i = 0; i < (sps->chroma_qp_table_struct.same_qp_table_for_chroma ? 1 : 2); i++) { + sps->chroma_qp_table_struct.num_points_in_qp_table_minus1[i] = get_ue_golomb(gb); + if (sps->chroma_qp_table_struct.num_points_in_qp_table_minus1[i] >= EVC_MAX_QP_TABLE_SIZE) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + for (int j = 0; j <= sps->chroma_qp_table_struct.num_points_in_qp_table_minus1[i]; j++) { + sps->chroma_qp_table_struct.delta_qp_in_val_minus1[i][j] = get_bits(gb, 6); + sps->chroma_qp_table_struct.delta_qp_out_val[i][j] = get_se_golomb_long(gb); + } + } + } + } + + sps->vui_parameters_present_flag = get_bits1(gb); + if (sps->vui_parameters_present_flag) { + ret = vui_parameters(gb, &(sps->vui_parameters)); + if (ret < 0) + goto fail; + } + + // @note + // If necessary, add the missing fields to the EVCParserSPS structure + // and then extend parser implementation + + av_freep(&ps->sps[sps_seq_parameter_set_id]); + ps->sps[sps_seq_parameter_set_id] = sps; + + return 0; +fail: + av_free(sps); + return ret; +} + +// @see ISO_IEC_23094-1 (7.3.2.2 SPS RBSP syntax) +// +// @note +// The current implementation of parse_sps function doesn't handle VUI parameters parsing. +// If it will be needed, parse_sps function could be extended to handle VUI parameters parsing +// to initialize fields of the AVCodecContex i.e. color_primaries, color_trc,color_range +// +int ff_evc_parse_pps(GetBitContext *gb, EVCParamSets *ps) +{ + EVCParserPPS *pps; + unsigned pps_pic_parameter_set_id; + int ret; + + pps_pic_parameter_set_id = get_ue_golomb(gb); + if (pps_pic_parameter_set_id >= EVC_MAX_PPS_COUNT) + return AVERROR_INVALIDDATA; + + pps = av_mallocz(sizeof(*pps)); + if (!pps) + return AVERROR(ENOMEM); + + pps->pps_pic_parameter_set_id = pps_pic_parameter_set_id; + + pps->pps_seq_parameter_set_id = get_ue_golomb(gb); + if (pps->pps_seq_parameter_set_id >= EVC_MAX_SPS_COUNT) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + pps->num_ref_idx_default_active_minus1[0] = get_ue_golomb(gb); + pps->num_ref_idx_default_active_minus1[1] = get_ue_golomb(gb); + pps->additional_lt_poc_lsb_len = get_ue_golomb(gb); + pps->rpl1_idx_present_flag = get_bits1(gb); + pps->single_tile_in_pic_flag = get_bits1(gb); + + if (!pps->single_tile_in_pic_flag) { + pps->num_tile_columns_minus1 = get_ue_golomb(gb); + pps->num_tile_rows_minus1 = get_ue_golomb(gb); + if (pps->num_tile_columns_minus1 >= EVC_MAX_TILE_COLUMNS || + pps->num_tile_rows_minus1 >= EVC_MAX_TILE_ROWS) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + pps->uniform_tile_spacing_flag = get_bits1(gb); + + if (!pps->uniform_tile_spacing_flag) { + for (int i = 0; i < pps->num_tile_columns_minus1; i++) + pps->tile_column_width_minus1[i] = get_ue_golomb(gb); + + for (int i = 0; i < pps->num_tile_rows_minus1; i++) + pps->tile_row_height_minus1[i] = get_ue_golomb(gb); + } + pps->loop_filter_across_tiles_enabled_flag = get_bits1(gb); + pps->tile_offset_len_minus1 = get_ue_golomb(gb); + } + + pps->tile_id_len_minus1 = get_ue_golomb(gb); + if (pps->tile_id_len_minus1 > 15U) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + pps->explicit_tile_id_flag = get_bits1(gb); + + if (pps->explicit_tile_id_flag) { + for (int i = 0; i <= pps->num_tile_rows_minus1; i++) { + for (int j = 0; j <= pps->num_tile_columns_minus1; j++) + pps->tile_id_val[i][j] = get_bits(gb, pps->tile_id_len_minus1 + 1); + } + } + + pps->pic_dra_enabled_flag = 0; + pps->pic_dra_enabled_flag = get_bits1(gb); + + if (pps->pic_dra_enabled_flag) + pps->pic_dra_aps_id = get_bits(gb, 5); + + pps->arbitrary_slice_present_flag = get_bits1(gb); + pps->constrained_intra_pred_flag = get_bits1(gb); + pps->cu_qp_delta_enabled_flag = get_bits1(gb); + + if (pps->cu_qp_delta_enabled_flag) + pps->log2_cu_qp_delta_area_minus6 = get_ue_golomb(gb); + + av_freep(&ps->pps[pps_pic_parameter_set_id]); + ps->pps[pps_pic_parameter_set_id] = pps; + + return 0; +fail: + av_free(pps); + return ret; +} + +void ff_evc_ps_free(EVCParamSets *ps) { + for (int i = 0; i < EVC_MAX_SPS_COUNT; i++) + av_freep(&ps->sps[i]); + + for (int i = 0; i < EVC_MAX_PPS_COUNT; i++) + av_freep(&ps->pps[i]); +} diff --git a/libavcodec/evc_ps.h b/libavcodec/evc_ps.h new file mode 100644 index 00000000000..336953b1769 --- /dev/null +++ b/libavcodec/evc_ps.h @@ -0,0 +1,224 @@ +/* + * 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 + * EVC decoder/parser shared code + */ + +#ifndef AVCODEC_EVC_PS_H +#define AVCODEC_EVC_PS_H + +#include + +#include "evc.h" +#include "get_bits.h" + +#define EVC_MAX_QP_TABLE_SIZE 58 +#define NUM_CPB 32 + +// rpl structure +typedef struct RefPicListStruct { + uint32_t ref_pic_num; + uint32_t ref_pics[EVC_MAX_NUM_REF_PICS]; +} RefPicListStruct; + +// chromaQP table structure to be signalled in SPS +typedef struct ChromaQpTable { + uint8_t chroma_qp_table_present_flag; // u(1) + uint8_t same_qp_table_for_chroma; // u(1) + uint8_t global_offset_flag; // u(1) + uint8_t num_points_in_qp_table_minus1[2]; // ue(v) + uint8_t delta_qp_in_val_minus1[2][EVC_MAX_QP_TABLE_SIZE]; // u(6) + int delta_qp_out_val[2][EVC_MAX_QP_TABLE_SIZE]; // se(v) +} ChromaQpTable; + +// Hypothetical Reference Decoder (HRD) parameters, part of VUI +typedef struct HRDParameters { + uint8_t cpb_cnt_minus1; // ue(v) + uint8_t bit_rate_scale; // u(4) + uint8_t cpb_size_scale; // u(4) + uint32_t bit_rate_value_minus1[NUM_CPB]; // ue(v) + uint32_t cpb_size_value_minus1[NUM_CPB]; // ue(v) + uint8_t cbr_flag[NUM_CPB]; // u(1) + uint8_t initial_cpb_removal_delay_length_minus1; // u(5) + uint8_t cpb_removal_delay_length_minus1; // u(5) + uint8_t dpb_output_delay_length_minus1; // u(5) + uint8_t time_offset_length; // u(5) +} HRDParameters; + +// video usability information (VUI) part of SPS +typedef struct VUIParameters { + uint8_t aspect_ratio_info_present_flag; // u(1) + uint8_t aspect_ratio_idc; // u(8) + uint16_t sar_width; // u(16) + uint16_t sar_height; // u(16) + uint8_t overscan_info_present_flag; // u(1) + uint8_t overscan_appropriate_flag; // u(1) + uint8_t video_signal_type_present_flag; // u(1) + uint8_t video_format; // u(3) + uint8_t video_full_range_flag; // u(1) + uint8_t colour_description_present_flag; // u(1) + uint8_t colour_primaries; // u(8) + uint8_t transfer_characteristics; // u(8) + uint8_t matrix_coefficients; // u(8) + uint8_t chroma_loc_info_present_flag; // u(1) + uint8_t chroma_sample_loc_type_top_field; // ue(v) + uint8_t chroma_sample_loc_type_bottom_field; // ue(v) + uint8_t neutral_chroma_indication_flag; // u(1) + uint8_t field_seq_flag; // u(1) + uint8_t timing_info_present_flag; // u(1) + uint32_t num_units_in_tick; // u(32) + uint32_t time_scale; // u(32) + uint8_t fixed_pic_rate_flag; // u(1) + uint8_t nal_hrd_parameters_present_flag; // u(1) + uint8_t vcl_hrd_parameters_present_flag; // u(1) + uint8_t low_delay_hrd_flag; // u(1) + uint8_t pic_struct_present_flag; // u(1) + uint8_t bitstream_restriction_flag; // u(1) + uint8_t motion_vectors_over_pic_boundaries_flag; // u(1) + uint8_t max_bytes_per_pic_denom; // ue(v) + uint8_t max_bits_per_mb_denom; // ue(v) + uint8_t log2_max_mv_length_horizontal; // ue(v) + uint8_t log2_max_mv_length_vertical; // ue(v) + uint32_t num_reorder_pics; // ue(v) + uint32_t max_dec_pic_buffering; // ue(v) + + HRDParameters hrd_parameters; +} VUIParameters; + +// The sturcture reflects SPS RBSP(raw byte sequence payload) layout +// @see ISO_IEC_23094-1 section 7.3.2.1 +// +// The following descriptors specify the parsing process of each element +// u(n) - unsigned integer using n bits +// ue(v) - unsigned integer 0-th order Exp_Golomb-coded syntax element with the left bit first +typedef struct EVCParserSPS { + uint8_t sps_seq_parameter_set_id; // ue(v) + uint8_t profile_idc; // u(8) + uint8_t level_idc; // u(8) + uint32_t toolset_idc_h; // u(32) + uint32_t toolset_idc_l; // u(32) + uint8_t chroma_format_idc; // ue(v) + uint32_t pic_width_in_luma_samples; // ue(v) + uint32_t pic_height_in_luma_samples; // ue(v) + uint8_t bit_depth_luma_minus8; // ue(v) + uint8_t bit_depth_chroma_minus8; // ue(v) + + uint8_t sps_btt_flag; // u(1) + uint32_t log2_ctu_size_minus2; // ue(v) + uint32_t log2_min_cb_size_minus2; // ue(v) + uint32_t log2_diff_ctu_max_14_cb_size; // ue(v) + uint32_t log2_diff_ctu_max_tt_cb_size; // ue(v) + uint32_t log2_diff_min_cb_min_tt_cb_size_minus2; // ue(v) + + uint8_t sps_suco_flag; // u(1) + uint32_t log2_diff_ctu_size_max_suco_cb_size; // ue(v) + uint32_t log2_diff_max_suco_min_suco_cb_size; // ue(v) + + uint8_t sps_admvp_flag; // u(1) + uint8_t sps_affine_flag; // u(1) + uint8_t sps_amvr_flag; // u(1) + uint8_t sps_dmvr_flag; // u(1) + uint8_t sps_mmvd_flag; // u(1) + uint8_t sps_hmvp_flag; // u(1) + + uint8_t sps_eipd_flag; // u(1) + uint8_t sps_ibc_flag; // u(1) + uint32_t log2_max_ibc_cand_size_minus2; // ue(v) + + uint8_t sps_cm_init_flag; // u(1) + uint8_t sps_adcc_flag; // u(1) + + uint8_t sps_iqt_flag; // u(1) + uint8_t sps_ats_flag; // u(1) + + uint8_t sps_addb_flag; // u(1) + uint8_t sps_alf_flag; // u(1) + uint8_t sps_htdf_flag; // u(1) + uint8_t sps_rpl_flag; // u(1) + uint8_t sps_pocs_flag; // u(1) + uint8_t sps_dquant_flag; // u(1) + uint8_t sps_dra_flag; // u(1) + + uint32_t log2_max_pic_order_cnt_lsb_minus4; // ue(v) + uint32_t log2_sub_gop_length; // ue(v) + uint32_t log2_ref_pic_gap_length; // ue(v) + + uint8_t max_num_tid0_ref_pics; // ue(v) + + uint32_t sps_max_dec_pic_buffering_minus1; // ue(v) + uint8_t long_term_ref_pic_flag; // u(1) + uint8_t rpl1_same_as_rpl0_flag; // u(1) + uint8_t num_ref_pic_list_in_sps[2]; // ue(v) + struct RefPicListStruct rpls[2][EVC_MAX_NUM_RPLS]; + + uint8_t picture_cropping_flag; // u(1) + uint32_t picture_crop_left_offset; // ue(v) + uint32_t picture_crop_right_offset; // ue(v) + uint32_t picture_crop_top_offset; // ue(v) + uint32_t picture_crop_bottom_offset; // ue(v) + + struct ChromaQpTable chroma_qp_table_struct; + + uint8_t vui_parameters_present_flag; // u(1) + + struct VUIParameters vui_parameters; + +} EVCParserSPS; + +typedef struct EVCParserPPS { + uint8_t pps_pic_parameter_set_id; // ue(v) + uint8_t pps_seq_parameter_set_id; // ue(v) + uint8_t num_ref_idx_default_active_minus1[2]; // ue(v) + uint8_t additional_lt_poc_lsb_len; // ue(v) + uint8_t rpl1_idx_present_flag; // u(1) + uint8_t single_tile_in_pic_flag; // u(1) + uint32_t num_tile_columns_minus1; // ue(v) + uint32_t num_tile_rows_minus1; // ue(v) + uint8_t uniform_tile_spacing_flag; // u(1) + uint32_t tile_column_width_minus1[EVC_MAX_TILE_COLUMNS]; // ue(v) + uint32_t tile_row_height_minus1[EVC_MAX_TILE_ROWS]; // ue(v) + uint8_t loop_filter_across_tiles_enabled_flag; // u(1) + uint32_t tile_offset_len_minus1; // ue(v) + uint8_t tile_id_len_minus1; // ue(v) + uint8_t explicit_tile_id_flag; // u(1) + uint32_t tile_id_val[EVC_MAX_TILE_ROWS][EVC_MAX_TILE_COLUMNS]; // u(v) + uint8_t pic_dra_enabled_flag; // u(1) + uint8_t pic_dra_aps_id; // u(5) + uint8_t arbitrary_slice_present_flag; // u(1) + uint8_t constrained_intra_pred_flag; // u(1) + uint8_t cu_qp_delta_enabled_flag; // u(1) + uint32_t log2_cu_qp_delta_area_minus6; // ue(v) + +} EVCParserPPS; + +typedef struct EVCParamSets { + EVCParserSPS *sps[EVC_MAX_SPS_COUNT]; + EVCParserPPS *pps[EVC_MAX_PPS_COUNT]; +} EVCParamSets; + +// @see ISO_IEC_23094-1 (7.3.2.1 SPS RBSP syntax) +int ff_evc_parse_sps(GetBitContext *gb, EVCParamSets *ps); + +// @see ISO_IEC_23094-1 (7.3.2.2 SPS RBSP syntax) +int ff_evc_parse_pps(GetBitContext *gb, EVCParamSets *ps); + +void ff_evc_ps_free(EVCParamSets *ps); + +#endif /* AVCODEC_EVC_PS_H */ diff --git a/libavcodec/evrcdec.c b/libavcodec/evrcdec.c index af7640d7e15..44d5bee7229 100644 --- a/libavcodec/evrcdec.c +++ b/libavcodec/evrcdec.c @@ -495,11 +495,11 @@ static void fcb_excitation(EVRCContext *e, const uint16_t *codebook, /** * Synthesis of the decoder output signal. * - * param[in] in input signal - * param[in] filter_coeffs LPC coefficients - * param[in/out] memory synthesis filter memory - * param buffer_length amount of data to process - * param[out] samples output samples + * @param[in] in input signal + * @param[in] filter_coeffs LPC coefficients + * @param[in/out] memory synthesis filter memory + * @param buffer_length amount of data to process + * @param[out] samples output samples * * TIA/IS-127 5.2.3.15, 5.7.3.4 */ diff --git a/libavcodec/exr.c b/libavcodec/exr.c index 8cc6b056b29..cf629534363 100644 --- a/libavcodec/exr.c +++ b/libavcodec/exr.c @@ -334,7 +334,10 @@ static int huf_unpack_enc_table(GetByteContext *gb, return ret; for (; im <= iM; im++) { - uint64_t l = freq[im] = get_bits(&gbit, 6); + int l; + if (get_bits_left(&gbit) < 6) + return AVERROR_INVALIDDATA; + l = freq[im] = get_bits(&gbit, 6); if (l == LONG_ZEROCODE_RUN) { int zerun = get_bits(&gbit, 8) + SHORTEST_LONG_RUN; @@ -404,8 +407,8 @@ static int huf_build_dec_table(const EXRContext *s, td->he[j].code = td->freq[iM] >> 6; j++; - ff_free_vlc(&td->vlc); - return ff_init_vlc_sparse(&td->vlc, 12, j, + ff_vlc_free(&td->vlc); + return ff_vlc_init_sparse(&td->vlc, 12, j, &td->he[0].len, sizeof(td->he[0]), sizeof(td->he[0].len), &td->he[0].code, sizeof(td->he[0]), sizeof(td->he[0].code), &td->he[0].sym, sizeof(td->he[0]), sizeof(td->he[0].sym), 0); @@ -760,8 +763,8 @@ static int pxr24_uncompress(const EXRContext *s, const uint8_t *src, static void unpack_14(const uint8_t b[14], uint16_t s[16]) { - unsigned short shift = (b[ 2] >> 2) & 15; - unsigned short bias = (0x20 << shift); + uint16_t shift = (b[ 2] >> 2) & 15; + uint16_t bias = (0x20 << shift); int i; s[ 0] = (b[0] << 8) | b[1]; @@ -1119,7 +1122,6 @@ static int dwa_uncompress(const EXRContext *s, const uint8_t *src, int compresse } { - const float scale = s->pixel_type == EXR_FLOAT ? 2.f : 1.f; const int o = s->nb_channels == 4; float *bo = ((float *)td->uncompressed_data) + y * td->xsize * s->nb_channels + td->xsize * (o + 0) + x; @@ -1137,9 +1139,9 @@ static int dwa_uncompress(const EXRContext *s, const uint8_t *src, int compresse convert(yb[idx], ub[idx], vb[idx], &bo[xx], &go[xx], &ro[xx]); - bo[xx] = to_linear(bo[xx], scale); - go[xx] = to_linear(go[xx], scale); - ro[xx] = to_linear(ro[xx], scale); + bo[xx] = to_linear(bo[xx], 1.f); + go[xx] = to_linear(go[xx], 1.f); + ro[xx] = to_linear(ro[xx], 1.f); } bo += td->xsize * s->nb_channels; @@ -1871,7 +1873,7 @@ static int decode_header(EXRContext *s, AVFrame *frame) continue; } else if ((var_size = check_header_variable(s, "tiles", "tiledesc", 22)) >= 0) { - char tileLevel; + uint8_t tileLevel; if (!s->is_tile) av_log(s->avctx, AV_LOG_WARNING, @@ -1940,7 +1942,7 @@ static int decode_header(EXRContext *s, AVFrame *frame) "preview", 16)) >= 0) { uint32_t pw = bytestream2_get_le32(gb); uint32_t ph = bytestream2_get_le32(gb); - uint64_t psize = pw * ph; + uint64_t psize = pw * (uint64_t)ph; if (psize > INT64_MAX / 4) { ret = AVERROR_INVALIDDATA; goto fail; @@ -2089,6 +2091,8 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, if (s->apply_trc_type != AVCOL_TRC_UNSPECIFIED) avctx->color_trc = s->apply_trc_type; + else if (s->gamma > 0.9999f && s->gamma < 1.0001f) + avctx->color_trc = AVCOL_TRC_LINEAR; switch (s->compression) { case EXR_RAW: @@ -2283,7 +2287,7 @@ static av_cold int decode_end(AVCodecContext *avctx) av_freep(&td->dc_data); av_freep(&td->rle_data); av_freep(&td->rle_raw_data); - ff_free_vlc(&td->vlc); + ff_vlc_free(&td->vlc); } av_freep(&s->thread_data); @@ -2305,39 +2309,39 @@ static const AVOption options[] = { // XXX: Note the abuse of the enum using AVCOL_TRC_UNSPECIFIED to subsume the existing gamma option { "apply_trc", "color transfer characteristics to apply to EXR linear input", OFFSET(apply_trc_type), - AV_OPT_TYPE_INT, {.i64 = AVCOL_TRC_UNSPECIFIED }, 1, AVCOL_TRC_NB-1, VD, "apply_trc_type"}, + AV_OPT_TYPE_INT, {.i64 = AVCOL_TRC_UNSPECIFIED }, 1, AVCOL_TRC_NB-1, VD, .unit = "apply_trc_type"}, { "bt709", "BT.709", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT709 }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT709 }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "gamma", "gamma", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_UNSPECIFIED }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_UNSPECIFIED }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "gamma22", "BT.470 M", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_GAMMA22 }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_GAMMA22 }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "gamma28", "BT.470 BG", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_GAMMA28 }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_GAMMA28 }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "smpte170m", "SMPTE 170 M", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE170M }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE170M }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "smpte240m", "SMPTE 240 M", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE240M }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE240M }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "linear", "Linear", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LINEAR }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LINEAR }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "log", "Log", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LOG }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LOG }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "log_sqrt", "Log square root", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LOG_SQRT }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LOG_SQRT }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "iec61966_2_4", "IEC 61966-2-4", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_IEC61966_2_4 }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_IEC61966_2_4 }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "bt1361", "BT.1361", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT1361_ECG }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT1361_ECG }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "iec61966_2_1", "IEC 61966-2-1", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_IEC61966_2_1 }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_IEC61966_2_1 }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "bt2020_10bit", "BT.2020 - 10 bit", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_10 }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_10 }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "bt2020_12bit", "BT.2020 - 12 bit", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_12 }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_12 }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "smpte2084", "SMPTE ST 2084", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTEST2084 }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTEST2084 }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { "smpte428_1", "SMPTE ST 428-1", 0, - AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTEST428_1 }, INT_MIN, INT_MAX, VD, "apply_trc_type"}, + AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTEST428_1 }, INT_MIN, INT_MAX, VD, .unit = "apply_trc_type"}, { NULL }, }; diff --git a/libavcodec/exrdsp.c b/libavcodec/exrdsp.c index 8259da4841d..248cb93c5ae 100644 --- a/libavcodec/exrdsp.c +++ b/libavcodec/exrdsp.c @@ -40,10 +40,20 @@ static void reorder_pixels_scalar(uint8_t *dst, const uint8_t *src, ptrdiff_t si static void predictor_scalar(uint8_t *src, ptrdiff_t size) { - ptrdiff_t i; + /* Unrolled: `src[i + 1] += src[i] - 128;` */ + if ((size & 1) == 0) { + src[1] += src[0] ^ 0x80; + src++; + size--; + } + + for (ptrdiff_t i = 1; i < size; i += 2) { + uint8_t a = src[i] + src[i - 1]; - for (i = 1; i < size; i++) - src[i] += src[i-1] - 128; + src[i] = a; + src[i + 1] += a; + src[i] ^= 0x80; + } } av_cold void ff_exrdsp_init(ExrDSPContext *c) @@ -51,7 +61,9 @@ av_cold void ff_exrdsp_init(ExrDSPContext *c) c->reorder_pixels = reorder_pixels_scalar; c->predictor = predictor_scalar; -#if ARCH_X86 +#if ARCH_RISCV + ff_exrdsp_init_riscv(c); +#elif ARCH_X86 ff_exrdsp_init_x86(c); #endif } diff --git a/libavcodec/exrdsp.h b/libavcodec/exrdsp.h index aba3d3cf51c..ea37c8b08ea 100644 --- a/libavcodec/exrdsp.h +++ b/libavcodec/exrdsp.h @@ -28,6 +28,7 @@ typedef struct ExrDSPContext { } ExrDSPContext; void ff_exrdsp_init(ExrDSPContext *c); +void ff_exrdsp_init_riscv(ExrDSPContext *c); void ff_exrdsp_init_x86(ExrDSPContext *c); #endif /* AVCODEC_EXRDSP_H */ diff --git a/libavcodec/exrenc.c b/libavcodec/exrenc.c index 36327f498cb..1b710fc0882 100644 --- a/libavcodec/exrenc.c +++ b/libavcodec/exrenc.c @@ -521,14 +521,14 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, #define OFFSET(x) offsetof(EXRContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "compression", "set compression type", OFFSET(compression), AV_OPT_TYPE_INT, {.i64=0}, 0, EXR_NBCOMPR-1, VE, "compr" }, - { "none", "none", 0, AV_OPT_TYPE_CONST, {.i64=EXR_RAW}, 0, 0, VE, "compr" }, - { "rle" , "RLE", 0, AV_OPT_TYPE_CONST, {.i64=EXR_RLE}, 0, 0, VE, "compr" }, - { "zip1", "ZIP1", 0, AV_OPT_TYPE_CONST, {.i64=EXR_ZIP1}, 0, 0, VE, "compr" }, - { "zip16", "ZIP16", 0, AV_OPT_TYPE_CONST, {.i64=EXR_ZIP16}, 0, 0, VE, "compr" }, - { "format", "set pixel type", OFFSET(pixel_type), AV_OPT_TYPE_INT, {.i64=EXR_FLOAT}, EXR_HALF, EXR_UNKNOWN-1, VE, "pixel" }, - { "half" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=EXR_HALF}, 0, 0, VE, "pixel" }, - { "float", NULL, 0, AV_OPT_TYPE_CONST, {.i64=EXR_FLOAT}, 0, 0, VE, "pixel" }, + { "compression", "set compression type", OFFSET(compression), AV_OPT_TYPE_INT, {.i64=0}, 0, EXR_NBCOMPR-1, VE, .unit = "compr" }, + { "none", "none", 0, AV_OPT_TYPE_CONST, {.i64=EXR_RAW}, 0, 0, VE, .unit = "compr" }, + { "rle" , "RLE", 0, AV_OPT_TYPE_CONST, {.i64=EXR_RLE}, 0, 0, VE, .unit = "compr" }, + { "zip1", "ZIP1", 0, AV_OPT_TYPE_CONST, {.i64=EXR_ZIP1}, 0, 0, VE, .unit = "compr" }, + { "zip16", "ZIP16", 0, AV_OPT_TYPE_CONST, {.i64=EXR_ZIP16}, 0, 0, VE, .unit = "compr" }, + { "format", "set pixel type", OFFSET(pixel_type), AV_OPT_TYPE_INT, {.i64=EXR_FLOAT}, EXR_HALF, EXR_UNKNOWN-1, VE, .unit = "pixel" }, + { "half" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=EXR_HALF}, 0, 0, VE, .unit = "pixel" }, + { "float", NULL, 0, AV_OPT_TYPE_CONST, {.i64=EXR_FLOAT}, 0, 0, VE, .unit = "pixel" }, { "gamma", "set gamma", OFFSET(gamma), AV_OPT_TYPE_FLOAT, {.dbl=1.f}, 0.001, FLT_MAX, VE }, { NULL}, }; diff --git a/libavcodec/faandct.c b/libavcodec/faandct.c index 38c392bbae0..b0b11859a77 100644 --- a/libavcodec/faandct.c +++ b/libavcodec/faandct.c @@ -26,6 +26,7 @@ */ #include "faandct.h" +#include "libavutil/emms.h" #include "libavutil/internal.h" #include "libavutil/libm.h" diff --git a/libavcodec/faanidct.c b/libavcodec/faanidct.c index 3921f82dae4..f2667fb7168 100644 --- a/libavcodec/faanidct.c +++ b/libavcodec/faanidct.c @@ -20,6 +20,7 @@ */ #include "faanidct.h" #include "libavutil/common.h" +#include "libavutil/emms.h" /* To allow switching to double. */ typedef float FLOAT; diff --git a/libavcodec/faxcompr.c b/libavcodec/faxcompr.c index d9dec3fcb83..41a1eec0815 100644 --- a/libavcodec/faxcompr.c +++ b/libavcodec/faxcompr.c @@ -95,28 +95,24 @@ static const uint8_t ccitt_group3_2d_lens[11] = { 4, 3, 7, 6, 3, 1, 3, 6, 7, 7, 9 }; -static VLC ccitt_vlc[2], ccitt_group3_2d_vlc; +// Also contains the other VLC tables pointed to by ccitt_vlc +static VLCElem ccitt_group3_2d_vlc[512 + 528 + 648]; +static const VLCElem *ccitt_vlc[2]; static av_cold void ccitt_unpack_init(void) { - static VLCElem code_table1[528]; - static VLCElem code_table2[648]; + VLCInitState state = VLC_INIT_STATE(ccitt_group3_2d_vlc); int i; - ccitt_vlc[0].table = code_table1; - ccitt_vlc[0].table_allocated = 528; - ccitt_vlc[1].table = code_table2; - ccitt_vlc[1].table_allocated = 648; + ff_vlc_init_tables(&state, 9, 11, + ccitt_group3_2d_lens, 1, 1, + ccitt_group3_2d_bits, 1, 1, 0); for (i = 0; i < 2; i++) { - ff_init_vlc_sparse(&ccitt_vlc[i], 9, CCITT_SYMS, - ccitt_codes_lens[i], 1, 1, - ccitt_codes_bits[i], 1, 1, - ccitt_syms, 2, 2, - INIT_VLC_USE_NEW_STATIC); + ccitt_vlc[i] = ff_vlc_init_tables_sparse(&state, 9, CCITT_SYMS, + ccitt_codes_lens[i], 1, 1, + ccitt_codes_bits[i], 1, 1, + ccitt_syms, 2, 2, 0); } - INIT_VLC_STATIC(&ccitt_group3_2d_vlc, 9, 11, - ccitt_group3_2d_lens, 1, 1, - ccitt_group3_2d_bits, 1, 1, 512); } av_cold void ff_ccitt_unpack_init(void) @@ -213,7 +209,7 @@ static int decode_group3_1d_line(AVCodecContext *avctx, GetBitContext *gb, for (;;) { if (get_bits_left(gb) <= 0) return AVERROR_INVALIDDATA; - t = get_vlc2(gb, ccitt_vlc[mode].table, 9, 2); + t = get_vlc2(gb, ccitt_vlc[mode], 9, 2); run += t; if (t < 64) { *runs++ = run; @@ -261,7 +257,7 @@ static int decode_group3_2d_line(AVCodecContext *avctx, GetBitContext *gb, int cmode; if (get_bits_left(gb) <= 0) return AVERROR_INVALIDDATA; - cmode = get_vlc2(gb, ccitt_group3_2d_vlc.table, 9, 1); + cmode = get_vlc2(gb, ccitt_group3_2d_vlc, 9, 1); if (cmode == -1) { av_log(avctx, AV_LOG_ERROR, "Incorrect mode VLC\n"); return AVERROR_INVALIDDATA; @@ -285,7 +281,7 @@ static int decode_group3_2d_line(AVCodecContext *avctx, GetBitContext *gb, for (;;) { if (get_bits_left(gb) <= 0) return AVERROR_INVALIDDATA; - t = get_vlc2(gb, ccitt_vlc[mode].table, 9, 2); + t = get_vlc2(gb, ccitt_vlc[mode], 9, 2); if (t == -1) { av_log(avctx, AV_LOG_ERROR, "Incorrect code\n"); return AVERROR_INVALIDDATA; diff --git a/libavcodec/fdctdsp.c b/libavcodec/fdctdsp.c index 5306c9d047e..f8ba17426c1 100644 --- a/libavcodec/fdctdsp.c +++ b/libavcodec/fdctdsp.c @@ -18,7 +18,6 @@ #include "libavutil/attributes.h" #include "avcodec.h" -#include "dct.h" #include "faandct.h" #include "fdctdsp.h" #include "config.h" diff --git a/libavcodec/fdctdsp.h b/libavcodec/fdctdsp.h index 3e1f683b9ea..7378eab870c 100644 --- a/libavcodec/fdctdsp.h +++ b/libavcodec/fdctdsp.h @@ -21,17 +21,28 @@ #include -#include "avcodec.h" +#include "libavutil/attributes_internal.h" + +struct AVCodecContext; typedef struct FDCTDSPContext { void (*fdct)(int16_t *block /* align 16 */); void (*fdct248)(int16_t *block /* align 16 */); } FDCTDSPContext; -void ff_fdctdsp_init(FDCTDSPContext *c, AVCodecContext *avctx); -void ff_fdctdsp_init_ppc(FDCTDSPContext *c, AVCodecContext *avctx, +FF_VISIBILITY_PUSH_HIDDEN +void ff_fdctdsp_init(FDCTDSPContext *c, struct AVCodecContext *avctx); +void ff_fdctdsp_init_ppc(FDCTDSPContext *c, struct AVCodecContext *avctx, unsigned high_bit_depth); -void ff_fdctdsp_init_x86(FDCTDSPContext *c, AVCodecContext *avctx, +void ff_fdctdsp_init_x86(FDCTDSPContext *c, struct AVCodecContext *avctx, unsigned high_bit_depth); +void ff_fdct_ifast(int16_t *data); +void ff_fdct_ifast248(int16_t *data); +void ff_jpeg_fdct_islow_8(int16_t *data); +void ff_jpeg_fdct_islow_10(int16_t *data); +void ff_fdct248_islow_8(int16_t *data); +void ff_fdct248_islow_10(int16_t *data); +FF_VISIBILITY_POP_HIDDEN + #endif /* AVCODEC_FDCTDSP_H */ diff --git a/libavcodec/ffjni.c b/libavcodec/ffjni.c index 154be9ae993..69d9a9faa35 100644 --- a/libavcodec/ffjni.c +++ b/libavcodec/ffjni.c @@ -25,6 +25,7 @@ #include #include "libavutil/bprint.h" +#include "libavutil/error.h" #include "libavutil/log.h" #include "libavutil/mem.h" @@ -235,17 +236,9 @@ int ff_jni_exception_get_summary(JNIEnv *env, jthrowable exception, char **error av_free(name); av_free(message); - if (class_class) { - (*env)->DeleteLocalRef(env, class_class); - } - - if (exception_class) { - (*env)->DeleteLocalRef(env, exception_class); - } - - if (string) { - (*env)->DeleteLocalRef(env, string); - } + (*env)->DeleteLocalRef(env, class_class); + (*env)->DeleteLocalRef(env, exception_class); + (*env)->DeleteLocalRef(env, string); return ret; } diff --git a/libavcodec/ffjni.h b/libavcodec/ffjni.h index 6027bac0ab1..d1e86f83299 100644 --- a/libavcodec/ffjni.h +++ b/libavcodec/ffjni.h @@ -24,6 +24,7 @@ #define AVCODEC_FFJNI_H #include +#include /* * Attach permanently a JNI environment to the current thread and retrieve it. @@ -105,7 +106,7 @@ struct FFJniField { const char *method; const char *signature; enum FFJniFieldType type; - int offset; + size_t offset; int mandatory; }; diff --git a/libavcodec/fflcms2.c b/libavcodec/fflcms2.c index 5443f178bc9..3b67e62d3af 100644 --- a/libavcodec/fflcms2.c +++ b/libavcodec/fflcms2.c @@ -201,6 +201,57 @@ static av_always_inline void XYZ_xy(cmsCIEXYZ XYZ, AVCIExy *xy) xy->y = av_d2q(k * XYZ.Y, 100000); } +static av_always_inline AVRational abs_sub_q(AVRational r1, AVRational r2) +{ + AVRational diff = av_sub_q(r1, r2); + /* denominator assumed to be positive */ + return av_make_q(abs(diff.num), diff.den); +} + +static const AVCIExy wp_d50 = { {3457, 10000}, {3585, 10000} }; /* CIE D50 */ + +int ff_icc_profile_sanitize(FFIccContext *s, cmsHPROFILE profile) +{ + cmsCIEXYZ *white, fixed; + AVCIExy wpxy; + AVRational diff, z; + if (!profile) + return 0; + + if (cmsGetEncodedICCversion(profile) >= 0x4000000) { // ICC v4 + switch (cmsGetHeaderRenderingIntent(profile)) { + case INTENT_RELATIVE_COLORIMETRIC: + case INTENT_ABSOLUTE_COLORIMETRIC: ; + /* ICC v4 colorimetric profiles are specified to always use D50 + * media white point, anything else is a violation of the spec. + * Sadly, such profiles are incredibly common (Apple...), so make + * an effort to fix them. */ + if (!(white = cmsReadTag(profile, cmsSigMediaWhitePointTag))) + return AVERROR_INVALIDDATA; + XYZ_xy(*white, &wpxy); + diff = av_add_q(abs_sub_q(wpxy.x, wp_d50.x), abs_sub_q(wpxy.y, wp_d50.y)); + if (av_cmp_q(diff, av_make_q(1, 1000)) > 0) { + av_log(s->avctx, AV_LOG_WARNING, "Invalid colorimetric ICCv4 " + "profile media white point tag (expected %.4f %.4f, " + "got %.4f %.4f)\n", + av_q2d(wp_d50.x), av_q2d(wp_d50.y), + av_q2d(wpxy.x), av_q2d(wpxy.y)); + /* x+y+z = 1 */ + z = av_sub_q(av_sub_q(av_make_q(1, 1), wp_d50.x), wp_d50.y); + fixed.X = av_q2d(av_div_q(wp_d50.x, wp_d50.y)) * white->Y; + fixed.Y = white->Y; + fixed.Z = av_q2d(av_div_q(z, wp_d50.y)) * white->Y; + if (!cmsWriteTag(profile, cmsSigMediaWhitePointTag, &fixed)) + return AVERROR_EXTERNAL; + } + break; + default: break; + } + } + + return 0; +} + int ff_icc_profile_read_primaries(FFIccContext *s, cmsHPROFILE profile, AVColorPrimariesDesc *out_primaries) { diff --git a/libavcodec/fflcms2.h b/libavcodec/fflcms2.h index af63c9a13c8..b54173e50e2 100644 --- a/libavcodec/fflcms2.h +++ b/libavcodec/fflcms2.h @@ -65,6 +65,13 @@ int ff_icc_profile_generate(FFIccContext *s, */ int ff_icc_profile_attach(FFIccContext *s, cmsHPROFILE profile, AVFrame *frame); +/** + * Sanitize an ICC profile to try and fix badly broken values. + * + * Returns 0 on success, or a negative error code. + */ +int ff_icc_profile_sanitize(FFIccContext *s, cmsHPROFILE profile); + /** * Read the color primaries and white point coefficients encoded by an ICC * profile, and return the raw values in `out_primaries`. diff --git a/libavcodec/fft-internal.h b/libavcodec/fft-internal.h deleted file mode 100644 index d89a3e38ca6..00000000000 --- a/libavcodec/fft-internal.h +++ /dev/null @@ -1,62 +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 - */ - -#ifndef AVCODEC_FFT_INTERNAL_H -#define AVCODEC_FFT_INTERNAL_H - -#include "libavutil/mathematics.h" -#include "fft.h" - -#if FFT_FLOAT - -#define FIX15(v) (v) -#define sqrthalf (float)M_SQRT1_2 - -#define BF(x, y, a, b) do { \ - x = a - b; \ - y = a + b; \ - } while (0) - -#define CMUL(dre, dim, are, aim, bre, bim) do { \ - (dre) = (are) * (bre) - (aim) * (bim); \ - (dim) = (are) * (bim) + (aim) * (bre); \ - } while (0) - -#else /* FFT_FLOAT */ - -#define CMUL(dre, dim, are, aim, bre, bim) do { \ - int64_t accu; \ - (accu) = (int64_t)(bre) * (are); \ - (accu) -= (int64_t)(bim) * (aim); \ - (dre) = (int)(((accu) + 0x40000000) >> 31); \ - (accu) = (int64_t)(bre) * (aim); \ - (accu) += (int64_t)(bim) * (are); \ - (dim) = (int)(((accu) + 0x40000000) >> 31); \ - } while (0) - -#endif /* FFT_FLOAT */ - -#define ff_imdct_calc_c FFT_NAME(ff_imdct_calc_c) -#define ff_imdct_half_c FFT_NAME(ff_imdct_half_c) -#define ff_mdct_calc_c FFT_NAME(ff_mdct_calc_c) - -void ff_imdct_calc_c(FFTContext *s, FFTSample *output, const FFTSample *input); -void ff_imdct_half_c(FFTContext *s, FFTSample *output, const FFTSample *input); -void ff_mdct_calc_c(FFTContext *s, FFTSample *output, const FFTSample *input); - -#endif /* AVCODEC_FFT_INTERNAL_H */ diff --git a/libavcodec/fft.h b/libavcodec/fft.h deleted file mode 100644 index d46e5a3f0be..00000000000 --- a/libavcodec/fft.h +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2000, 2001, 2002 Fabrice Bellard - * Copyright (c) 2002-2004 Michael Niedermayer - * - * 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 - */ - -#ifndef AVCODEC_FFT_H -#define AVCODEC_FFT_H - -#ifndef FFT_FLOAT -#define FFT_FLOAT 1 -#endif - -#include -#include "config.h" - -#include "libavutil/attributes_internal.h" -#include "libavutil/mem_internal.h" - -#if FFT_FLOAT - -#include "avfft.h" - -#define FFT_NAME(x) x - -typedef float FFTDouble; - -#else - -#define Q31(x) (int)((x)*2147483648.0 + 0.5) -#define FFT_NAME(x) x ## _fixed_32 - -typedef int32_t FFTSample; - -typedef struct FFTComplex { - FFTSample re, im; -} FFTComplex; - -typedef int FFTDouble; -typedef struct FFTContext FFTContext; - -#endif /* FFT_FLOAT */ - -typedef struct FFTDComplex { - FFTDouble re, im; -} FFTDComplex; - -/* FFT computation */ - -enum fft_permutation_type { - FF_FFT_PERM_DEFAULT, - FF_FFT_PERM_SWAP_LSBS, - FF_FFT_PERM_AVX, -}; - -enum mdct_permutation_type { - FF_MDCT_PERM_NONE, - FF_MDCT_PERM_INTERLEAVE, -}; - -struct FFTContext { - int nbits; - int inverse; - uint16_t *revtab; - FFTComplex *tmp_buf; - int mdct_size; /* size of MDCT (i.e. number of input data * 2) */ - int mdct_bits; /* n = 2^nbits */ - /* pre/post rotation tables */ - FFTSample *tcos; - FFTSample *tsin; - /** - * Do the permutation needed BEFORE calling fft_calc(). - */ - void (*fft_permute)(struct FFTContext *s, FFTComplex *z); - /** - * Do a complex FFT with the parameters defined in ff_fft_init(). The - * input data must be permuted before. No 1.0/sqrt(n) normalization is done. - */ - void (*fft_calc)(struct FFTContext *s, FFTComplex *z); - void (*imdct_calc)(struct FFTContext *s, FFTSample *output, const FFTSample *input); - void (*imdct_half)(struct FFTContext *s, FFTSample *output, const FFTSample *input); - void (*mdct_calc)(struct FFTContext *s, FFTSample *output, const FFTSample *input); - enum fft_permutation_type fft_permutation; - enum mdct_permutation_type mdct_permutation; - uint32_t *revtab32; -}; - -#if CONFIG_HARDCODED_TABLES -#define COSTABLE_CONST const -#define ff_init_ff_cos_tabs(index) -#else -#define COSTABLE_CONST -#define ff_init_ff_cos_tabs FFT_NAME(ff_init_ff_cos_tabs) - -/** - * Initialize the cosine table in ff_cos_tabs[index] - * @param index index in ff_cos_tabs array of the table to initialize - */ -void ff_init_ff_cos_tabs(int index); -#endif - -#define COSTABLE(size) \ - COSTABLE_CONST attribute_visibility_hidden DECLARE_ALIGNED(32, FFTSample, FFT_NAME(ff_cos_##size))[size/2] - -extern COSTABLE(16); -extern COSTABLE(32); -extern COSTABLE(64); -extern COSTABLE(128); -extern COSTABLE(256); -extern COSTABLE(512); -extern COSTABLE(1024); -extern COSTABLE(2048); -extern COSTABLE(4096); -extern COSTABLE(8192); -extern COSTABLE(16384); -extern COSTABLE(32768); -extern COSTABLE(65536); -extern COSTABLE(131072); -extern COSTABLE_CONST FFTSample* const FFT_NAME(ff_cos_tabs)[18]; - -#define ff_fft_init FFT_NAME(ff_fft_init) -#define ff_fft_end FFT_NAME(ff_fft_end) - -/** - * Set up a complex FFT. - * @param nbits log2 of the length of the input array - * @param inverse if 0 perform the forward transform, if 1 perform the inverse - */ -int ff_fft_init(FFTContext *s, int nbits, int inverse); - -void ff_fft_init_aarch64(FFTContext *s); -void ff_fft_init_x86(FFTContext *s); -void ff_fft_init_arm(FFTContext *s); -void ff_fft_init_mips(FFTContext *s); -void ff_fft_init_ppc(FFTContext *s); - -void ff_fft_end(FFTContext *s); - -#define ff_mdct_init FFT_NAME(ff_mdct_init) -#define ff_mdct_end FFT_NAME(ff_mdct_end) - -int ff_mdct_init(FFTContext *s, int nbits, int inverse, double scale); -void ff_mdct_end(FFTContext *s); - -#endif /* AVCODEC_FFT_H */ diff --git a/libavcodec/fft_fixed_32.c b/libavcodec/fft_fixed_32.c deleted file mode 100644 index e18dc83891e..00000000000 --- a/libavcodec/fft_fixed_32.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2012 - * MIPS Technologies, Inc., California. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Authors: Stanislav Ocovaj (socovaj@mips.com) - * Goran Cordasic (goran@mips.com) - * Djordje Pesut (djordje@mips.com) - * - * 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 - */ - -#define FFT_FLOAT 0 -#include "fft_template.c" diff --git a/libavcodec/fft_init_table.c b/libavcodec/fft_init_table.c deleted file mode 100644 index 83e35ffb7c8..00000000000 --- a/libavcodec/fft_init_table.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright (c) 2012 - * MIPS Technologies, Inc., California. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Authors: Stanislav Ocovaj (socovaj@mips.com) - * Goran Cordasic (goran@mips.com) - * Djordje Pesut (djordje@mips.com) - * - * 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 - * definitions and initialization of LUT table for FFT - */ -#include "libavutil/thread.h" - -#include "libavcodec/fft_table.h" - -const int32_t ff_w_tab_sr[MAX_FFT_SIZE/(4*16)] = { -2147483647, 2147483016, 2147481121, 2147477963, 2147473542, 2147467857, 2147460908, 2147452697, -2147443222, 2147432484, 2147420483, 2147407218, 2147392690, 2147376899, 2147359845, 2147341527, -2147321946, 2147301102, 2147278995, 2147255625, 2147230991, 2147205094, 2147177934, 2147149511, -2147119825, 2147088876, 2147056664, 2147023188, 2146988450, 2146952448, 2146915184, 2146876656, -2146836866, 2146795813, 2146753497, 2146709917, 2146665076, 2146618971, 2146571603, 2146522973, -2146473080, 2146421924, 2146369505, 2146315824, 2146260881, 2146204674, 2146147205, 2146088474, -2146028480, 2145967224, 2145904705, 2145840924, 2145775880, 2145709574, 2145642006, 2145573176, -2145503083, 2145431729, 2145359112, 2145285233, 2145210092, 2145133690, 2145056025, 2144977098, -2144896910, 2144815460, 2144732748, 2144648774, 2144563539, 2144477042, 2144389283, 2144300264, -2144209982, 2144118439, 2144025635, 2143931570, 2143836244, 2143739656, 2143641807, 2143542697, -2143442326, 2143340694, 2143237802, 2143133648, 2143028234, 2142921559, 2142813624, 2142704427, -2142593971, 2142482254, 2142369276, 2142255039, 2142139541, 2142022783, 2141904764, 2141785486, -2141664948, 2141543150, 2141420092, 2141295774, 2141170197, 2141043360, 2140915264, 2140785908, -2140655293, 2140523418, 2140390284, 2140255892, 2140120240, 2139983329, 2139845159, 2139705730, -2139565043, 2139423097, 2139279892, 2139135429, 2138989708, 2138842728, 2138694490, 2138544994, -2138394240, 2138242228, 2138088958, 2137934430, 2137778644, 2137621601, 2137463301, 2137303743, -2137142927, 2136980855, 2136817525, 2136652938, 2136487095, 2136319994, 2136151637, 2135982023, -2135811153, 2135639026, 2135465642, 2135291003, 2135115107, 2134937956, 2134759548, 2134579885, -2134398966, 2134216791, 2134033361, 2133848675, 2133662734, 2133475538, 2133287087, 2133097381, -2132906420, 2132714204, 2132520734, 2132326009, 2132130030, 2131932796, 2131734309, 2131534567, -2131333572, 2131131322, 2130927819, 2130723062, 2130517052, 2130309789, 2130101272, 2129891502, -2129680480, 2129468204, 2129254676, 2129039895, 2128823862, 2128606576, 2128388038, 2128168248, -2127947206, 2127724913, 2127501367, 2127276570, 2127050522, 2126823222, 2126594672, 2126364870, -2126133817, 2125901514, 2125667960, 2125433155, 2125197100, 2124959795, 2124721240, 2124481435, -2124240380, 2123998076, 2123754522, 2123509718, 2123263666, 2123016364, 2122767814, 2122518015, -2122266967, 2122014670, 2121761126, 2121506333, 2121250292, 2120993003, 2120734467, 2120474683, -2120213651, 2119951372, 2119687847, 2119423074, 2119157054, 2118889788, 2118621275, 2118351516, -2118080511, 2117808259, 2117534762, 2117260020, 2116984031, 2116706797, 2116428319, 2116148595, -2115867626, 2115585412, 2115301954, 2115017252, 2114731305, 2114444114, 2114155680, 2113866001, -2113575080, 2113282914, 2112989506, 2112694855, 2112398960, 2112101824, 2111803444, 2111503822, -2111202959, 2110900853, 2110597505, 2110292916, 2109987085, 2109680013, 2109371700, 2109062146, -2108751352, 2108439317, 2108126041, 2107811526, 2107495770, 2107178775, 2106860540, 2106541065, -2106220352, 2105898399, 2105575208, 2105250778, 2104925109, 2104598202, 2104270057, 2103940674, -2103610054, 2103278196, 2102945101, 2102610768, 2102275199, 2101938393, 2101600350, 2101261071, -2100920556, 2100578805, 2100235819, 2099891596, 2099546139, 2099199446, 2098851519, 2098502357, -2098151960, 2097800329, 2097447464, 2097093365, 2096738032, 2096381466, 2096023667, 2095664635, -2095304370, 2094942872, 2094580142, 2094216179, 2093850985, 2093484559, 2093116901, 2092748012, -2092377892, 2092006541, 2091633960, 2091260147, 2090885105, 2090508833, 2090131331, 2089752599, -2089372638, 2088991448, 2088609029, 2088225381, 2087840505, 2087454400, 2087067068, 2086678508, -2086288720, 2085897705, 2085505463, 2085111994, 2084717298, 2084321376, 2083924228, 2083525854, -2083126254, 2082725429, 2082323379, 2081920103, 2081515603, 2081109879, 2080702930, 2080294757, -2079885360, 2079474740, 2079062896, 2078649830, 2078235540, 2077820028, 2077403294, 2076985338, -2076566160, 2076145760, 2075724139, 2075301296, 2074877233, 2074451950, 2074025446, 2073597721, -2073168777, 2072738614, 2072307231, 2071874629, 2071440808, 2071005769, 2070569511, 2070132035, -2069693342, 2069253430, 2068812302, 2068369957, 2067926394, 2067481616, 2067035621, 2066588410, -2066139983, 2065690341, 2065239484, 2064787411, 2064334124, 2063879623, 2063423908, 2062966978, -2062508835, 2062049479, 2061588910, 2061127128, 2060664133, 2060199927, 2059734508, 2059267877, -2058800036, 2058330983, 2057860719, 2057389244, 2056916560, 2056442665, 2055967560, 2055491246, -2055013723, 2054534991, 2054055050, 2053573901, 2053091544, 2052607979, 2052123207, 2051637227, -2051150040, 2050661647, 2050172048, 2049681242, 2049189231, 2048696014, 2048201592, 2047705965, -2047209133, 2046711097, 2046211857, 2045711414, 2045209767, 2044706916, 2044202863, 2043697608, -2043191150, 2042683490, 2042174628, 2041664565, 2041153301, 2040640837, 2040127172, 2039612306, -2039096241, 2038578976, 2038060512, 2037540850, 2037019988, 2036497928, 2035974670, 2035450215, -2034924562, 2034397712, 2033869665, 2033340422, 2032809982, 2032278347, 2031745516, 2031211490, -2030676269, 2030139853, 2029602243, 2029063439, 2028523442, 2027982251, 2027439867, 2026896291, -2026351522, 2025805561, 2025258408, 2024710064, 2024160529, 2023609803, 2023057887, 2022504780, -2021950484, 2021394998, 2020838323, 2020280460, 2019721407, 2019161167, 2018599739, 2018037123, -2017473321, 2016908331, 2016342155, 2015774793, 2015206245, 2014636511, 2014065592, 2013493489, -2012920201, 2012345729, 2011770073, 2011193233, 2010615210, 2010036005, 2009455617, 2008874047, -2008291295, 2007707362, 2007122248, 2006535953, 2005948478, 2005359822, 2004769987, 2004178973, -2003586779, 2002993407, 2002398857, 2001803128, 2001206222, 2000608139, 2000008879, 1999408442, -1998806829, 1998204040, 1997600076, 1996994937, 1996388622, 1995781134, 1995172471, 1994562635, -1993951625, 1993339442, 1992726087, 1992111559, 1991495860, 1990878989, 1990260946, 1989641733, -1989021350, 1988399796, 1987777073, 1987153180, 1986528118, 1985901888, 1985274489, 1984645923, -1984016189, 1983385288, 1982753220, 1982119985, 1981485585, 1980850019, 1980213288, 1979575392, -1978936331, 1978296106, 1977654717, 1977012165, 1976368450, 1975723572, 1975077532, 1974430331, -1973781967, 1973132443, 1972481757, 1971829912, 1971176906, 1970522741, 1969867417, 1969210933, -1968553292, 1967894492, 1967234535, 1966573420, 1965911148, 1965247720, 1964583136, 1963917396, -1963250501, 1962582451, 1961913246, 1961242888, 1960571375, 1959898709, 1959224890, 1958549919, -1957873796, 1957196520, 1956518093, 1955838516, 1955157788, 1954475909, 1953792881, 1953108703, -1952423377, 1951736902, 1951049279, 1950360508, 1949670589, 1948979524, 1948287312, 1947593954, -1946899451, 1946203802, 1945507008, 1944809070, 1944109987, 1943409761, 1942708392, 1942005880, -1941302225, 1940597428, 1939891490, 1939184411, 1938476190, 1937766830, 1937056329, 1936344689, -1935631910, 1934917992, 1934202936, 1933486742, 1932769411, 1932050943, 1931331338, 1930610597, -1929888720, 1929165708, 1928441561, 1927716279, 1926989864, 1926262315, 1925533633, 1924803818, -1924072871, 1923340791, 1922607581, 1921873239, 1921137767, 1920401165, 1919663432, 1918924571, -1918184581, 1917443462, 1916701216, 1915957841, 1915213340, 1914467712, 1913720958, 1912973078, -1912224073, 1911473942, 1910722688, 1909970309, 1909216806, 1908462181, 1907706433, 1906949562, -1906191570, 1905432457, 1904672222, 1903910867, 1903148392, 1902384797, 1901620084, 1900854251, -1900087301, 1899319232, 1898550047, 1897779744, 1897008325, 1896235790, 1895462140, 1894687374, -1893911494, 1893134500, 1892356392, 1891577171, 1890796837, 1890015391, 1889232832, 1888449163, -1887664383, 1886878492, 1886091491, 1885303381, 1884514161, 1883723833, 1882932397, 1882139853, -1881346202, 1880551444, 1879755580, 1878958610, 1878160535, 1877361354, 1876561070, 1875759681, -1874957189, 1874153594, 1873348897, 1872543097, 1871736196, 1870928194, 1870119091, 1869308888, -1868497586, 1867685184, 1866871683, 1866057085, 1865241388, 1864424594, 1863606704, 1862787717, -1861967634, 1861146456, 1860324183, 1859500816, 1858676355, 1857850800, 1857024153, 1856196413, -1855367581, 1854537657, 1853706643, 1852874538, 1852041343, 1851207059, 1850371686, 1849535224, -1848697674, 1847859036, 1847019312, 1846178501, 1845336604, 1844493621, 1843649553, 1842804401, -1841958164, 1841110844, 1840262441, 1839412956, 1838562388, 1837710739, 1836858008, 1836004197, -1835149306, 1834293336, 1833436286, 1832578158, 1831718951, 1830858668, 1829997307, 1829134869, -1828271356, 1827406767, 1826541103, 1825674364, 1824806552, 1823937666, 1823067707, 1822196675, -1821324572, 1820451397, 1819577151, 1818701835, 1817825449, 1816947994, 1816069469, 1815189877, -1814309216, 1813427489, 1812544694, 1811660833, 1810775906, 1809889915, 1809002858, 1808114737, -1807225553, 1806335305, 1805443995, 1804551623, 1803658189, 1802763694, 1801868139, 1800971523, -1800073849, 1799175115, 1798275323, 1797374472, 1796472565, 1795569601, 1794665580, 1793760504, -1792854372, 1791947186, 1791038946, 1790129652, 1789219305, 1788307905, 1787395453, 1786481950, -1785567396, 1784651792, 1783735137, 1782817434, 1781898681, 1780978881, 1780058032, 1779136137, -1778213194, 1777289206, 1776364172, 1775438094, 1774510970, 1773582803, 1772653593, 1771723340, -1770792044, 1769859707, 1768926328, 1767991909, 1767056450, 1766119952, 1765182414, 1764243838, -1763304224, 1762363573, 1761421885, 1760479161, 1759535401, 1758590607, 1757644777, 1756697914, -1755750017, 1754801087, 1753851126, 1752900132, 1751948107, 1750995052, 1750040966, 1749085851, -1748129707, 1747172535, 1746214334, 1745255107, 1744294853, 1743333573, 1742371267, 1741407936, -1740443581, 1739478202, 1738511799, 1737544374, 1736575927, 1735606458, 1734635968, 1733664458, -1732691928, 1731718378, 1730743810, 1729768224, 1728791620, 1727813999, 1726835361, 1725855708, -1724875040, 1723893357, 1722910659, 1721926948, 1720942225, 1719956488, 1718969740, 1717981981, -1716993211, 1716003431, 1715012642, 1714020844, 1713028037, 1712034223, 1711039401, 1710043573, -1709046739, 1708048900, 1707050055, 1706050207, 1705049355, 1704047500, 1703044642, 1702040783, -1701035922, 1700030061, 1699023199, 1698015339, 1697006479, 1695996621, 1694985765, 1693973912, -1692961062, 1691947217, 1690932376, 1689916541, 1688899711, 1687881888, 1686863072, 1685843263, -1684822463, 1683800672, 1682777890, 1681754118, 1680729357, 1679703608, 1678676870, 1677649144, -1676620432, 1675590733, 1674560049, 1673528379, 1672495725, 1671462087, 1670427466, 1669391862, -1668355276, 1667317709, 1666279161, 1665239632, 1664199124, 1663157637, 1662115172, 1661071729, -1660027308, 1658981911, 1657935539, 1656888190, 1655839867, 1654790570, 1653740300, 1652689057, -1651636841, 1650583654, 1649529496, 1648474367, 1647418269, 1646361202, 1645303166, 1644244162, -1643184191, 1642123253, 1641061349, 1639998480, 1638934646, 1637869848, 1636804087, 1635737362, -1634669676, 1633601027, 1632531418, 1631460848, 1630389319, 1629316830, 1628243383, 1627168978, -1626093616, 1625017297, 1623940023, 1622861793, 1621782608, 1620702469, 1619621377, 1618539332, -1617456335, 1616372386, 1615287487, 1614201637, 1613114838, 1612027089, 1610938393, 1609848749, -1608758157, 1607666620, 1606574136, 1605480708, 1604386335, 1603291018, 1602194758, 1601097555, -1599999411, 1598900325, 1597800299, 1596699333, 1595597428, 1594494583, 1593390801, 1592286082, -1591180426, 1590073833, 1588966306, 1587857843, 1586748447, 1585638117, 1584526854, 1583414660, -1582301533, 1581187476, 1580072489, 1578956572, 1577839726, 1576721952, 1575603251, 1574483623, -1573363068, 1572241588, 1571119183, 1569995854, 1568871601, 1567746425, 1566620327, 1565493307, -1564365367, 1563236506, 1562106725, 1560976026, 1559844408, 1558711873, 1557578421, 1556444052, -1555308768, 1554172569, 1553035455, 1551897428, 1550758488, 1549618636, 1548477872, 1547336197, -1546193612, 1545050118, 1543905714, 1542760402, 1541614183, 1540467057, 1539319024, 1538170087, -1537020244, 1535869497, 1534717846, 1533565293, 1532411837, 1531257480, 1530102222, 1528946064, -1527789007, 1526631051, 1525472197, 1524312445, 1523151797, 1521990252, 1520827813, 1519664478, -1518500250, 1517335128, 1516169114, 1515002208, 1513834411, 1512665723, 1511496145, 1510325678, -1509154322, 1507982079, 1506808949, 1505634932, 1504460029, 1503284242, 1502107570, 1500930014, -1499751576, 1498572255, 1497392053, 1496210969, 1495029006, 1493846163, 1492662441, 1491477842, -1490292364, 1489106011, 1487918781, 1486730675, 1485541696, 1484351842, 1483161115, 1481969516, -1480777044, 1479583702, 1478389489, 1477194407, 1475998456, 1474801636, 1473603949, 1472405394, -1471205974, 1470005688, 1468804538, 1467602523, 1466399645, 1465195904, 1463991302, 1462785838, -1461579514, 1460372329, 1459164286, 1457955385, 1456745625, 1455535009, 1454323536, 1453111208, -1451898025, 1450683988, 1449469098, 1448253355, 1447036760, 1445819314, 1444601017, 1443381870, -1442161874, 1440941030, 1439719338, 1438496799, 1437273414, 1436049184, 1434824109, 1433598189, -1432371426, 1431143821, 1429915374, 1428686085, 1427455956, 1426224988, 1424993180, 1423760534, -1422527051, 1421292730, 1420057574, 1418821582, 1417584755, 1416347095, 1415108601, 1413869275, -1412629117, 1411388129, 1410146309, 1408903661, 1407660183, 1406415878, 1405170745, 1403924785, -1402678000, 1401430389, 1400181954, 1398932695, 1397682613, 1396431709, 1395179984, 1393927438, -1392674072, 1391419886, 1390164882, 1388909060, 1387652422, 1386394966, 1385136696, 1383877610, -1382617710, 1381356997, 1380095472, 1378833134, 1377569986, 1376306026, 1375041258, 1373775680, -1372509294, 1371242101, 1369974101, 1368705296, 1367435685, 1366165269, 1364894050, 1363622028, -1362349204, 1361075579, 1359801152, 1358525926, 1357249901, 1355973077, 1354695455, 1353417037, -1352137822, 1350857812, 1349577007, 1348295409, 1347013017, 1345729833, 1344445857, 1343161090, -1341875533, 1340589187, 1339302052, 1338014129, 1336725419, 1335435923, 1334145641, 1332854574, -1331562723, 1330270089, 1328976672, 1327682474, 1326387494, 1325091734, 1323795195, 1322497877, -1321199781, 1319900907, 1318601257, 1317300832, 1315999631, 1314697657, 1313394909, 1312091388, -1310787095, 1309482032, 1308176198, 1306869594, 1305562222, 1304254082, 1302945174, 1301635500, -1300325060, 1299013855, 1297701886, 1296389154, 1295075659, 1293761402, 1292446384, 1291130606, -1289814068, 1288496772, 1287178717, 1285859905, 1284540337, 1283220013, 1281898935, 1280577102, -1279254516, 1277931177, 1276607086, 1275282245, 1273956653, 1272630312, 1271303222, 1269975384, -1268646800, 1267317469, 1265987392, 1264656571, 1263325005, 1261992697, 1260659646, 1259325853, -1257991320, 1256656047, 1255320034, 1253983283, 1252645794, 1251307568, 1249968606, 1248628909, -1247288478, 1245947312, 1244605414, 1243262783, 1241919421, 1240575329, 1239230506, 1237884955, -1236538675, 1235191668, 1233843935, 1232495475, 1231146291, 1229796382, 1228445750, 1227094395, -1225742318, 1224389521, 1223036002, 1221681765, 1220326809, 1218971135, 1217614743, 1216257636, -1214899813, 1213541275, 1212182024, 1210822059, 1209461382, 1208099993, 1206737894, 1205375085, -1204011567, 1202647340, 1201282407, 1199916766, 1198550419, 1197183368, 1195815612, 1194447153, -1193077991, 1191708127, 1190337562, 1188966297, 1187594332, 1186221669, 1184848308, 1183474250, -1182099496, 1180724046, 1179347902, 1177971064, 1176593533, 1175215310, 1173836395, 1172456790, -1171076495, 1169695512, 1168313840, 1166931481, 1165548435, 1164164704, 1162780288, 1161395188, -1160009405, 1158622939, 1157235792, 1155847964, 1154459456, 1153070269, 1151680403, 1150289860, -1148898640, 1147506745, 1146114174, 1144720929, 1143327011, 1141932420, 1140537158, 1139141224, -1137744621, 1136347348, 1134949406, 1133550797, 1132151521, 1130751579, 1129350972, 1127949701, -1126547765, 1125145168, 1123741908, 1122337987, 1120933406, 1119528166, 1118122267, 1116715710, -1115308496, 1113900627, 1112492101, 1111082922, 1109673089, 1108262603, 1106851465, 1105439676, -1104027237, 1102614148, 1101200410, 1099786025, 1098370993, 1096955314, 1095538991, 1094122023, -1092704411, 1091286156, 1089867259, 1088447722, 1087027544, 1085606726, 1084185270, 1082763176, -1081340445, 1079917078, 1078493076, 1077068439, 1075643169, 1074217266, 1072790730, 1071363564, -1069935768, 1068507342, 1067078288, 1065648605, 1064218296, 1062787361, 1061355801, 1059923616, -1058490808, 1057057377, 1055623324, 1054188651, 1052753357, 1051317443, 1049880912, 1048443763, -1047005996, 1045567615, 1044128617, 1042689006, 1041248781, 1039807944, 1038366495, 1036924436, -1035481766, 1034038487, 1032594600, 1031150105, 1029705004, 1028259297, 1026812985, 1025366069, -1023918550, 1022470428, 1021021705, 1019572382, 1018122458, 1016671936, 1015220816, 1013769098, -1012316784, 1010863875, 1009410370, 1007956272, 1006501581, 1005046298, 1003590424, 1002133959, -1000676905, 999219262, 997761031, 996302214, 994842810, 993382821, 991922248, 990461091, -988999351, 987537030, 986074127, 984610645, 983146583, 981681943, 980216726, 978750932, -977284562, 975817617, 974350098, 972882006, 971413342, 969944106, 968474300, 967003923, -965532978, 964061465, 962589385, 961116739, 959643527, 958169751, 956695411, 955220508, -953745043, 952269017, 950792431, 949315286, 947837582, 946359321, 944880503, 943401129, -941921200, 940440717, 938959681, 937478092, 935995952, 934513261, 933030021, 931546231, -930061894, 928577010, 927091579, 925605603, 924119082, 922632018, 921144411, 919656262, -918167572, 916678342, 915188572, 913698265, 912207419, 910716038, 909224120, 907731667, -906238681, 904745161, 903251110, 901756526, 900261413, 898765769, 897269597, 895772898, -894275671, 892777918, 891279640, 889780838, 888281512, 886781663, 885281293, 883780402, -882278992, 880777062, 879274614, 877771649, 876268167, 874764170, 873259659, 871754633, -870249095, 868743045, 867236484, 865729413, 864221832, 862713743, 861205147, 859696043, -858186435, 856676321, 855165703, 853654582, 852142959, 850630835, 849118210, 847605086, -846091463, 844577343, 843062726, 841547612, 840032004, 838515901, 836999305, 835482217, -833964638, 832446567, 830928007, 829408958, 827889422, 826369398, 824848888, 823327893, -821806413, 820284450, 818762005, 817239078, 815715670, 814191782, 812667415, 811142571, -809617249, 808091450, 806565177, 805038429, 803511207, 801983513, 800455346, 798926709, -797397602, 795868026, 794337982, 792807470, 791276492, 789745049, 788213141, 786680769, -785147934, 783614638, 782080880, 780546663, 779011986, 777476851, 775941259, 774405210, -772868706, 771331747, 769794334, 768256469, 766718151, 765179382, 763640164, 762100496, -760560380, 759019816, 757478806, 755937350, 754395449, 752853105, 751310318, 749767089, -748223418, 746679308, 745134758, 743589770, 742044345, 740498483, 738952186, 737405453, -735858287, 734310688, 732762657, 731214195, 729665303, 728115982, 726566232, 725016055, -723465451, 721914422, 720362968, 718811090, 717258790, 715706067, 714152924, 712599360, -711045377, 709490976, 707936158, 706380923, 704825272, 703269207, 701712728, 700155836, -698598533, 697040818, 695482694, 693924160, 692365218, 690805869, 689246113, 687685952, -686125387, 684564417, 683003045, 681441272, 679879097, 678316522, 676753549, 675190177, -673626408, 672062243, 670497682, 668932727, 667367379, 665801638, 664235505, 662668981, -661102068, 659534766, 657967075, 656398998, 654830535, 653261686, 651692453, 650122837, -648552838, 646982457, 645411696, 643840556, 642269036, 640697139, 639124865, 637552215, -635979190, 634405791, 632832018, 631257873, 629683357, 628108471, 626533215, 624957590, -623381598, 621805239, 620228514, 618651424, 617073971, 615496154, 613917975, 612339436, -610760536, 609181276, 607601658, 606021683, 604441352, 602860664, 601279623, 599698227, -598116479, 596534378, 594951927, 593369126, 591785976, 590202477, 588618632, 587034440, -585449903, 583865021, 582279796, 580694229, 579108320, 577522070, 575935480, 574348552, -572761285, 571173682, 569585743, 567997469, 566408860, 564819919, 563230645, 561641039, -560051104, 558460839, 556870245, 555279324, 553688076, 552096502, 550504604, 548912382, -547319836, 545726969, 544133781, 542540273, 540946445, 539352300, 537757837, 536163058, -534567963, 532972554, 531376831, 529780796, 528184449, 526587791, 524990824, 523393547, -521795963, 520198072, 518599875, 517001373, 515402566, 513803457, 512204045, 510604332, -509004318, 507404005, 505803394, 504202485, 502601279, 500999778, 499397982, 497795892, -496193509, 494590835, 492987869, 491384614, 489781069, 488177236, 486573117, 484968710, -483364019, 481759043, 480153784, 478548243, 476942419, 475336316, 473729932, 472123270, -470516330, 468909114, 467301622, 465693854, 464085813, 462477499, 460868912, 459260055, -457650927, 456041530, 454431865, 452821933, 451211734, 449601270, 447990541, 446379549, -444768294, 443156777, 441545000, 439932963, 438320667, 436708113, 435095303, 433482236, -431868915, 430255339, 428641511, 427027430, 425413098, 423798515, 422183684, 420568604, -418953276, 417337703, 415721883, 414105819, 412489512, 410872962, 409256170, 407639137, -406021865, 404404353, 402786604, 401168618, 399550396, 397931939, 396313247, 394694323, -393075166, 391455778, 389836160, 388216313, 386596237, 384975934, 383355404, 381734649, -380113669, 378492466, 376871039, 375249392, 373627523, 372005435, 370383128, 368760603, -367137861, 365514903, 363891730, 362268343, 360644742, 359020930, 357396906, 355772673, -354148230, 352523578, 350898719, 349273654, 347648383, 346022908, 344397230, 342771348, -341145265, 339518981, 337892498, 336265816, 334638936, 333011859, 331384586, 329757119, -328129457, 326501602, 324873555, 323245317, 321616889, 319988272, 318359466, 316730474, -315101295, 313471930, 311842381, 310212649, 308582734, 306952638, 305322361, 303691904, -302061269, 300430456, 298799466, 297168301, 295536961, 293905447, 292273760, 290641901, -289009871, 287377671, 285745302, 284112765, 282480061, 280847190, 279214155, 277580955, -275947592, 274314066, 272680379, 271046532, 269412525, 267778360, 266144038, 264509558, -262874923, 261240134, 259605191, 257970095, 256334847, 254699448, 253063900, 251428203, -249792358, 248156366, 246520228, 244883945, 243247518, 241610947, 239974235, 238337382, -236700388, 235063255, 233425984, 231788575, 230151030, 228513350, 226875535, 225237587, -223599506, 221961294, 220322951, 218684479, 217045878, 215407149, 213768293, 212129312, -210490206, 208850976, 207211624, 205572149, 203932553, 202292838, 200653003, 199013051, -197372981, 195732795, 194092495, 192452080, 190811551, 189170911, 187530159, 185889297, -184248325, 182607245, 180966058, 179324764, 177683365, 176041861, 174400254, 172758544, -171116733, 169474820, 167832808, 166190698, 164548489, 162906184, 161263783, 159621287, -157978697, 156336015, 154693240, 153050374, 151407418, 149764374, 148121241, 146478021, -144834714, 143191323, 141547847, 139904288, 138260647, 136616925, 134973122, 133329239, -131685278, 130041240, 128397125, 126752935, 125108670, 123464332, 121819921, 120175438, -118530885, 116886262, 115241570, 113596810, 111951983, 110307091, 108662134, 107017112, -105372028, 103726882, 102081675, 100436408, 98791081, 97145697, 95500255, 93854758, - 92209205, 90563597, 88917937, 87272224, 85626460, 83980645, 82334782, 80688869, - 79042909, 77396903, 75750851, 74104755, 72458615, 70812432, 69166208, 67519943, - 65873638, 64227295, 62580914, 60934496, 59288042, 57641553, 55995030, 54348475, - 52701887, 51055268, 49408620, 47761942, 46115236, 44468503, 42821744, 41174960, - 39528151, 37881320, 36234466, 34587590, 32940695, 31293780, 29646846, 27999895, - 26352928, 24705945, 23058947, 21411936, 19764913, 18117878, 16470832, 14823776, - 13176712, 11529640, 9882561, 8235476, 6588387, 4941294, 3294197, 1647099 -}; - -uint16_t ff_fft_offsets_lut[21845]; - -static void fft_lut_init(uint16_t *table, int off, int size, int *index) -{ - if (size < 16) { - table[*index] = off >> 2; - (*index)++; - } - else { - fft_lut_init(table, off, size >> 1, index); - fft_lut_init(table, off + (size >> 1), size >> 2, index); - fft_lut_init(table, off + 3 * (size >> 2), size >> 2, index); - } -} - -static void fft_lut_init_start(void) -{ - int n = 0; - - fft_lut_init(ff_fft_offsets_lut, 0, 1 << 17, &n); -} - -void ff_fft_lut_init(void) -{ - static AVOnce init_once = AV_ONCE_INIT; - - ff_thread_once(&init_once, fft_lut_init_start); -} diff --git a/libavcodec/fft_table.h b/libavcodec/fft_table.h deleted file mode 100644 index 09df49f2b8e..00000000000 --- a/libavcodec/fft_table.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2012 - * MIPS Technologies, Inc., California. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Authors: Stanislav Ocovaj (socovaj@mips.com) - * Goran Cordasic (goran@mips.com) - * Djordje Pesut (djordje@mips.com) - * - * 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 - * definitions and tables for FFT - */ -#ifndef AVCODEC_FFT_TABLE_H -#define AVCODEC_FFT_TABLE_H - -#include "libavcodec/fft.h" - -#define MAX_LOG2_NFFT 17 //!< Specifies maximum allowed fft size -#define MAX_FFT_SIZE (1 << MAX_LOG2_NFFT) - -extern const int32_t ff_w_tab_sr[]; -extern uint16_t ff_fft_offsets_lut[]; -void ff_fft_lut_init(void); - -#endif /* AVCODEC_FFT_TABLE_H */ diff --git a/libavcodec/fft_template.c b/libavcodec/fft_template.c deleted file mode 100644 index f2742a3ae8d..00000000000 --- a/libavcodec/fft_template.c +++ /dev/null @@ -1,628 +0,0 @@ -/* - * FFT/IFFT transforms - * Copyright (c) 2008 Loren Merritt - * Copyright (c) 2002 Fabrice Bellard - * Partly based on libdjbfft by D. J. Bernstein - * - * 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 - * FFT/IFFT transforms. - */ - -#include -#include -#include "libavutil/mathematics.h" -#include "libavutil/thread.h" -#include "fft.h" -#include "fft-internal.h" - -#if !FFT_FLOAT -#include "fft_table.h" -#else /* !FFT_FLOAT */ - -/* cos(2*pi*x/n) for 0<=x<=n/4, followed by its reverse */ -#if !CONFIG_HARDCODED_TABLES -COSTABLE(16); -COSTABLE(32); -COSTABLE(64); -COSTABLE(128); -COSTABLE(256); -COSTABLE(512); -COSTABLE(1024); -COSTABLE(2048); -COSTABLE(4096); -COSTABLE(8192); -COSTABLE(16384); -COSTABLE(32768); -COSTABLE(65536); -COSTABLE(131072); - -static av_cold void init_ff_cos_tabs(int index) -{ - int i; - int m = 1<> 1; - if(!(i&m)) return split_radix_permutation(i, m, inverse)*2; - m >>= 1; - if(inverse == !(i&m)) return split_radix_permutation(i, m, inverse)*4 + 1; - else return split_radix_permutation(i, m, inverse)*4 - 1; -} - - -static const int avx_tab[] = { - 0, 4, 1, 5, 8, 12, 9, 13, 2, 6, 3, 7, 10, 14, 11, 15 -}; - -static int is_second_half_of_fft32(int i, int n) -{ - if (n <= 32) - return i >= 16; - else if (i < n/2) - return is_second_half_of_fft32(i, n/2); - else if (i < 3*n/4) - return is_second_half_of_fft32(i - n/2, n/4); - else - return is_second_half_of_fft32(i - 3*n/4, n/4); -} - -static av_cold void fft_perm_avx(FFTContext *s) -{ - int i; - int n = 1 << s->nbits; - - for (i = 0; i < n; i += 16) { - int k; - if (is_second_half_of_fft32(i, n)) { - for (k = 0; k < 16; k++) - s->revtab[-split_radix_permutation(i + k, n, s->inverse) & (n - 1)] = - i + avx_tab[k]; - - } else { - for (k = 0; k < 16; k++) { - int j = i + k; - j = (j & ~7) | ((j >> 1) & 3) | ((j << 2) & 4); - s->revtab[-split_radix_permutation(i + k, n, s->inverse) & (n - 1)] = j; - } - } - } -} - -av_cold int ff_fft_init(FFTContext *s, int nbits, int inverse) -{ - int i, j, n; - - s->revtab = NULL; - s->revtab32 = NULL; - - if (nbits < 2 || nbits > 17) - goto fail; - s->nbits = nbits; - n = 1 << nbits; - - if (nbits <= 16) { - s->revtab = av_malloc(n * sizeof(uint16_t)); - if (!s->revtab) - goto fail; - } else { - s->revtab32 = av_malloc(n * sizeof(uint32_t)); - if (!s->revtab32) - goto fail; - } - s->tmp_buf = av_malloc(n * sizeof(FFTComplex)); - if (!s->tmp_buf) - goto fail; - s->inverse = inverse; - s->fft_permutation = FF_FFT_PERM_DEFAULT; - - s->fft_permute = fft_permute_c; - s->fft_calc = fft_calc_c; -#if CONFIG_MDCT - s->imdct_calc = ff_imdct_calc_c; - s->imdct_half = ff_imdct_half_c; - s->mdct_calc = ff_mdct_calc_c; -#endif - -#if FFT_FLOAT -#if ARCH_AARCH64 - ff_fft_init_aarch64(s); -#elif ARCH_ARM - ff_fft_init_arm(s); -#elif ARCH_PPC - ff_fft_init_ppc(s); -#elif ARCH_X86 - ff_fft_init_x86(s); -#endif -#if HAVE_MIPSFPU - ff_fft_init_mips(s); -#endif - for(j=4; j<=nbits; j++) { - ff_init_ff_cos_tabs(j); - } -#else /* FFT_FLOAT */ - ff_fft_lut_init(); -#endif - - - if (ARCH_X86 && FFT_FLOAT && s->fft_permutation == FF_FFT_PERM_AVX) { - fft_perm_avx(s); - } else { -#define PROCESS_FFT_PERM_SWAP_LSBS(num) do {\ - for(i = 0; i < n; i++) {\ - int k;\ - j = i;\ - j = (j & ~3) | ((j >> 1) & 1) | ((j << 1) & 2);\ - k = -split_radix_permutation(i, n, s->inverse) & (n - 1);\ - s->revtab##num[k] = j;\ - } \ -} while(0); - -#define PROCESS_FFT_PERM_DEFAULT(num) do {\ - for(i = 0; i < n; i++) {\ - int k;\ - j = i;\ - k = -split_radix_permutation(i, n, s->inverse) & (n - 1);\ - s->revtab##num[k] = j;\ - } \ -} while(0); - -#define SPLIT_RADIX_PERMUTATION(num) do { \ - if (s->fft_permutation == FF_FFT_PERM_SWAP_LSBS) {\ - PROCESS_FFT_PERM_SWAP_LSBS(num) \ - } else {\ - PROCESS_FFT_PERM_DEFAULT(num) \ - }\ -} while(0); - - if (s->revtab) - SPLIT_RADIX_PERMUTATION() - if (s->revtab32) - SPLIT_RADIX_PERMUTATION(32) - -#undef PROCESS_FFT_PERM_DEFAULT -#undef PROCESS_FFT_PERM_SWAP_LSBS -#undef SPLIT_RADIX_PERMUTATION - } - - return 0; - fail: - av_freep(&s->revtab); - av_freep(&s->revtab32); - av_freep(&s->tmp_buf); - return -1; -} - -static void fft_permute_c(FFTContext *s, FFTComplex *z) -{ - int j, np; - const uint16_t *revtab = s->revtab; - const uint32_t *revtab32 = s->revtab32; - np = 1 << s->nbits; - /* TODO: handle split-radix permute in a more optimal way, probably in-place */ - if (revtab) { - for(j=0;jtmp_buf[revtab[j]] = z[j]; - } else - for(j=0;jtmp_buf[revtab32[j]] = z[j]; - - memcpy(z, s->tmp_buf, np * sizeof(FFTComplex)); -} - -av_cold void ff_fft_end(FFTContext *s) -{ - av_freep(&s->revtab); - av_freep(&s->revtab32); - av_freep(&s->tmp_buf); -} - -#if !FFT_FLOAT - -static void fft_calc_c(FFTContext *s, FFTComplex *z) { - - int nbits, i, n, num_transforms, offset, step; - int n4, n2, n34; - unsigned tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8; - FFTComplex *tmpz; - const int fft_size = (1 << s->nbits); - int64_t accu; - - num_transforms = (0x2aab >> (16 - s->nbits)) | 1; - - for (n=0; n> 1) | 1; - - for (n=0; n> 31); - accu = (int64_t)Q31(M_SQRT1_2)*(int)(tmp3 - tmp4); - tmp7 = (int32_t)((accu + 0x40000000) >> 31); - accu = (int64_t)Q31(M_SQRT1_2)*(int)(tmp2 - tmp1); - tmp6 = (int32_t)((accu + 0x40000000) >> 31); - accu = (int64_t)Q31(M_SQRT1_2)*(int)(tmp3 + tmp4); - tmp8 = (int32_t)((accu + 0x40000000) >> 31); - tmp1 = tmp5 + tmp7; - tmp3 = tmp5 - tmp7; - tmp2 = tmp6 + tmp8; - tmp4 = tmp6 - tmp8; - - tmpz[5].re = tmpz[1].re - tmp1; - tmpz[1].re = tmpz[1].re + tmp1; - tmpz[5].im = tmpz[1].im - tmp2; - tmpz[1].im = tmpz[1].im + tmp2; - tmpz[7].re = tmpz[3].re - tmp4; - tmpz[3].re = tmpz[3].re + tmp4; - tmpz[7].im = tmpz[3].im + tmp3; - tmpz[3].im = tmpz[3].im - tmp3; - } - - step = 1 << ((MAX_LOG2_NFFT-4) - 4); - n4 = 4; - - for (nbits=4; nbits<=s->nbits; nbits++){ - n2 = 2*n4; - n34 = 3*n4; - num_transforms = (num_transforms >> 1) | 1; - - for (n=0; n> 31); - accu = (int64_t)w_re*tmpz[ n2+i].im; - accu -= (int64_t)w_im*tmpz[ n2+i].re; - tmp2 = (int32_t)((accu + 0x40000000) >> 31); - accu = (int64_t)w_re*tmpz[n34+i].re; - accu -= (int64_t)w_im*tmpz[n34+i].im; - tmp3 = (int32_t)((accu + 0x40000000) >> 31); - accu = (int64_t)w_re*tmpz[n34+i].im; - accu += (int64_t)w_im*tmpz[n34+i].re; - tmp4 = (int32_t)((accu + 0x40000000) >> 31); - - tmp5 = tmp1 + tmp3; - tmp1 = tmp1 - tmp3; - tmp6 = tmp2 + tmp4; - tmp2 = tmp2 - tmp4; - - tmpz[ n2+i].re = tmpz[ i].re - tmp5; - tmpz[ i].re = tmpz[ i].re + tmp5; - tmpz[ n2+i].im = tmpz[ i].im - tmp6; - tmpz[ i].im = tmpz[ i].im + tmp6; - tmpz[n34+i].re = tmpz[n4+i].re - tmp2; - tmpz[ n4+i].re = tmpz[n4+i].re + tmp2; - tmpz[n34+i].im = tmpz[n4+i].im + tmp1; - tmpz[ n4+i].im = tmpz[n4+i].im - tmp1; - - w_re_ptr += step; - w_im_ptr -= step; - } - } - step >>= 1; - n4 <<= 1; - } -} - -#else /* !FFT_FLOAT */ - -#define BUTTERFLIES(a0,a1,a2,a3) {\ - BF(t3, t5, t5, t1);\ - BF(a2.re, a0.re, a0.re, t5);\ - BF(a3.im, a1.im, a1.im, t3);\ - BF(t4, t6, t2, t6);\ - BF(a3.re, a1.re, a1.re, t4);\ - BF(a2.im, a0.im, a0.im, t6);\ -} - -// force loading all the inputs before storing any. -// this is slightly slower for small data, but avoids store->load aliasing -// for addresses separated by large powers of 2. -#define BUTTERFLIES_BIG(a0,a1,a2,a3) {\ - FFTSample r0=a0.re, i0=a0.im, r1=a1.re, i1=a1.im;\ - BF(t3, t5, t5, t1);\ - BF(a2.re, a0.re, r0, t5);\ - BF(a3.im, a1.im, i1, t3);\ - BF(t4, t6, t2, t6);\ - BF(a3.re, a1.re, r1, t4);\ - BF(a2.im, a0.im, i0, t6);\ -} - -#define TRANSFORM(a0,a1,a2,a3,wre,wim) {\ - CMUL(t1, t2, a2.re, a2.im, wre, -wim);\ - CMUL(t5, t6, a3.re, a3.im, wre, wim);\ - BUTTERFLIES(a0,a1,a2,a3)\ -} - -#define TRANSFORM_ZERO(a0,a1,a2,a3) {\ - t1 = a2.re;\ - t2 = a2.im;\ - t5 = a3.re;\ - t6 = a3.im;\ - BUTTERFLIES(a0,a1,a2,a3)\ -} - -/* z[0...8n-1], w[1...2n-1] */ -#define PASS(name)\ -static void name(FFTComplex *z, const FFTSample *wre, unsigned int n)\ -{\ - FFTDouble t1, t2, t3, t4, t5, t6;\ - int o1 = 2*n;\ - int o2 = 4*n;\ - int o3 = 6*n;\ - const FFTSample *wim = wre+o1;\ - n--;\ -\ - TRANSFORM_ZERO(z[0],z[o1],z[o2],z[o3]);\ - TRANSFORM(z[1],z[o1+1],z[o2+1],z[o3+1],wre[1],wim[-1]);\ - do {\ - z += 2;\ - wre += 2;\ - wim -= 2;\ - TRANSFORM(z[0],z[o1],z[o2],z[o3],wre[0],wim[0]);\ - TRANSFORM(z[1],z[o1+1],z[o2+1],z[o3+1],wre[1],wim[-1]);\ - } while(--n);\ -} - -PASS(pass) -#if !CONFIG_SMALL -#undef BUTTERFLIES -#define BUTTERFLIES BUTTERFLIES_BIG -PASS(pass_big) -#endif - -#define DECL_FFT(n,n2,n4)\ -static void fft##n(FFTComplex *z)\ -{\ - fft##n2(z);\ - fft##n4(z+n4*2);\ - fft##n4(z+n4*3);\ - pass(z,FFT_NAME(ff_cos_##n),n4/2);\ -} - -static void fft4(FFTComplex *z) -{ - FFTDouble t1, t2, t3, t4, t5, t6, t7, t8; - - BF(t3, t1, z[0].re, z[1].re); - BF(t8, t6, z[3].re, z[2].re); - BF(z[2].re, z[0].re, t1, t6); - BF(t4, t2, z[0].im, z[1].im); - BF(t7, t5, z[2].im, z[3].im); - BF(z[3].im, z[1].im, t4, t8); - BF(z[3].re, z[1].re, t3, t7); - BF(z[2].im, z[0].im, t2, t5); -} - -static void fft8(FFTComplex *z) -{ - FFTDouble t1, t2, t3, t4, t5, t6; - - fft4(z); - - BF(t1, z[5].re, z[4].re, -z[5].re); - BF(t2, z[5].im, z[4].im, -z[5].im); - BF(t5, z[7].re, z[6].re, -z[7].re); - BF(t6, z[7].im, z[6].im, -z[7].im); - - BUTTERFLIES(z[0],z[2],z[4],z[6]); - TRANSFORM(z[1],z[3],z[5],z[7],sqrthalf,sqrthalf); -} - -#if !CONFIG_SMALL -static void fft16(FFTComplex *z) -{ - FFTDouble t1, t2, t3, t4, t5, t6; - FFTSample cos_16_1 = FFT_NAME(ff_cos_16)[1]; - FFTSample cos_16_3 = FFT_NAME(ff_cos_16)[3]; - - fft8(z); - fft4(z+8); - fft4(z+12); - - TRANSFORM_ZERO(z[0],z[4],z[8],z[12]); - TRANSFORM(z[2],z[6],z[10],z[14],sqrthalf,sqrthalf); - TRANSFORM(z[1],z[5],z[9],z[13],cos_16_1,cos_16_3); - TRANSFORM(z[3],z[7],z[11],z[15],cos_16_3,cos_16_1); -} -#else -DECL_FFT(16,8,4) -#endif -DECL_FFT(32,16,8) -DECL_FFT(64,32,16) -DECL_FFT(128,64,32) -DECL_FFT(256,128,64) -DECL_FFT(512,256,128) -#if !CONFIG_SMALL -#define pass pass_big -#endif -DECL_FFT(1024,512,256) -DECL_FFT(2048,1024,512) -DECL_FFT(4096,2048,1024) -DECL_FFT(8192,4096,2048) -DECL_FFT(16384,8192,4096) -DECL_FFT(32768,16384,8192) -DECL_FFT(65536,32768,16384) -DECL_FFT(131072,65536,32768) - -static void (* const fft_dispatch[])(FFTComplex*) = { - fft4, fft8, fft16, fft32, fft64, fft128, fft256, fft512, fft1024, - fft2048, fft4096, fft8192, fft16384, fft32768, fft65536, fft131072 -}; - -static void fft_calc_c(FFTContext *s, FFTComplex *z) -{ - fft_dispatch[s->nbits-2](z); -} -#endif /* !FFT_FLOAT */ diff --git a/libavcodec/ffv1dec.c b/libavcodec/ffv1dec.c index 180d24e6953..97877b91064 100644 --- a/libavcodec/ffv1dec.c +++ b/libavcodec/ffv1dec.c @@ -217,13 +217,13 @@ static int decode_slice_header(const FFV1Context *f, FFV1Context *fs) ps = get_symbol(c, state, 0); if (ps == 1) { - f->cur->interlaced_frame = 1; - f->cur->top_field_first = 1; + f->cur->flags |= AV_FRAME_FLAG_INTERLACED; + f->cur->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } else if (ps == 2) { - f->cur->interlaced_frame = 1; - f->cur->top_field_first = 0; + f->cur->flags |= AV_FRAME_FLAG_INTERLACED; + f->cur->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; } else if (ps == 3) { - f->cur->interlaced_frame = 0; + f->cur->flags &= ~AV_FRAME_FLAG_INTERLACED; } f->cur->sample_aspect_ratio.num = get_symbol(c, state, 0); f->cur->sample_aspect_ratio.den = get_symbol(c, state, 0); @@ -264,16 +264,16 @@ static int decode_slice(AVCodecContext *c, void *arg) for( si=0; fs != f->slice_context[si]; si ++) ; - if(f->fsrc && !p->key_frame) + if(f->fsrc && !(p->flags & AV_FRAME_FLAG_KEY)) ff_thread_await_progress(&f->last_picture, si, 0); - if(f->fsrc && !p->key_frame) { + if(f->fsrc && !(p->flags & AV_FRAME_FLAG_KEY)) { FFV1Context *fssrc = f->fsrc->slice_context[si]; FFV1Context *fsdst = f->slice_context[si]; av_assert1(fsdst->plane_count == fssrc->plane_count); av_assert1(fsdst == fs); - if (!p->key_frame) + if (!(p->flags & AV_FRAME_FLAG_KEY)) fsdst->slice_damaged |= fssrc->slice_damaged; for (i = 0; i < f->plane_count; i++) { @@ -310,7 +310,7 @@ static int decode_slice(AVCodecContext *c, void *arg) } if ((ret = ff_ffv1_init_slice_state(f, fs)) < 0) return ret; - if (f->cur->key_frame || fs->slice_reset_contexts) { + if ((f->cur->flags & AV_FRAME_FLAG_KEY) || fs->slice_reset_contexts) { ff_ffv1_clear_slice_state(f, fs); } else if (fs->slice_damaged) { return AVERROR_INVALIDDATA; @@ -439,6 +439,11 @@ static int read_extra_header(FFV1Context *f) av_log(f->avctx, AV_LOG_ERROR, "Invalid version in global header\n"); return AVERROR_INVALIDDATA; } + if (f->version > 4) { + av_log(f->avctx, AV_LOG_ERROR, "unsupported version %d\n", + f->version); + return AVERROR_PATCHWELCOME; + } if (f->version > 2) { c->bytestream_end -= 4; f->micro_version = get_symbol(c, state, 0); @@ -621,6 +626,9 @@ static int read_header(FFV1Context *f) } else if (f->avctx->bits_per_raw_sample == 12) { f->packed_at_lsb = 1; f->avctx->pix_fmt = AV_PIX_FMT_GRAY12; + } else if (f->avctx->bits_per_raw_sample == 14) { + f->packed_at_lsb = 1; + f->avctx->pix_fmt = AV_PIX_FMT_GRAY14; } else if (f->avctx->bits_per_raw_sample == 16) { f->packed_at_lsb = 1; f->avctx->pix_fmt = AV_PIX_FMT_GRAY16; @@ -685,6 +693,12 @@ static int read_header(FFV1Context *f) case 0x10: f->avctx->pix_fmt = AV_PIX_FMT_YUV422P12; break; case 0x11: f->avctx->pix_fmt = AV_PIX_FMT_YUV420P12; break; } + } else if (f->avctx->bits_per_raw_sample == 12 && f->transparency) { + f->packed_at_lsb = 1; + switch(16 * f->chroma_h_shift + f->chroma_v_shift) { + case 0x00: f->avctx->pix_fmt = AV_PIX_FMT_YUVA444P12; break; + case 0x10: f->avctx->pix_fmt = AV_PIX_FMT_YUVA422P12; break; + } } else if (f->avctx->bits_per_raw_sample == 14 && !f->transparency) { f->packed_at_lsb = 1; switch(16 * f->chroma_h_shift + f->chroma_v_shift) { @@ -729,6 +743,8 @@ static int read_header(FFV1Context *f) f->avctx->pix_fmt = AV_PIX_FMT_GBRAP12; else if (f->avctx->bits_per_raw_sample == 14 && !f->transparency) f->avctx->pix_fmt = AV_PIX_FMT_GBRP14; + else if (f->avctx->bits_per_raw_sample == 14 && f->transparency) + f->avctx->pix_fmt = AV_PIX_FMT_GBRAP14; else if (f->avctx->bits_per_raw_sample == 16 && !f->transparency) { f->avctx->pix_fmt = AV_PIX_FMT_GBRP16; f->use32bit = 1; @@ -869,16 +885,16 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, AVFrame *p; if (f->last_picture.f) - ff_thread_release_ext_buffer(avctx, &f->last_picture); + ff_thread_release_ext_buffer(&f->last_picture); FFSWAP(ThreadFrame, f->picture, f->last_picture); f->cur = p = f->picture.f; if (f->version < 3 && avctx->field_order > AV_FIELD_PROGRESSIVE) { /* we have interlaced material flagged in container */ - p->interlaced_frame = 1; + p->flags |= AV_FRAME_FLAG_INTERLACED; if (avctx->field_order == AV_FIELD_TT || avctx->field_order == AV_FIELD_TB) - p->top_field_first = 1; + p->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } f->avctx = avctx; @@ -887,7 +903,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, p->pict_type = AV_PICTURE_TYPE_I; //FIXME I vs. P if (get_rac(c, &keystate)) { - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; f->key_frame_ok = 0; if ((ret = read_header(f)) < 0) return ret; @@ -898,7 +914,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, "Cannot decode non-keyframe without valid keyframe\n"); return AVERROR_INVALIDDATA; } - p->key_frame = 0; + p->flags &= ~AV_FRAME_FLAG_KEY; } if (f->ac != AC_GOLOMB_RICE) { @@ -922,7 +938,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, if (avctx->debug & FF_DEBUG_PICT_INFO) av_log(avctx, AV_LOG_DEBUG, "ver:%d keyframe:%d coder:%d ec:%d slices:%d bps:%d\n", - f->version, p->key_frame, f->ac, f->ec, f->slice_count, f->avctx->bits_per_raw_sample); + f->version, !!(p->flags & AV_FRAME_FLAG_KEY), f->ac, f->ec, f->slice_count, f->avctx->bits_per_raw_sample); ff_thread_finish_setup(avctx); @@ -1009,7 +1025,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, ff_thread_report_progress(&f->picture, INT_MAX, 0); if (f->last_picture.f) - ff_thread_release_ext_buffer(avctx, &f->last_picture); + ff_thread_release_ext_buffer(&f->last_picture); if ((ret = av_frame_ref(rframe, f->picture.f)) < 0) return ret; @@ -1056,31 +1072,24 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src) if (dst == src) return 0; - { - ThreadFrame picture = fdst->picture, last_picture = fdst->last_picture; - uint8_t (*initial_states[MAX_QUANT_TABLES])[32]; - struct FFV1Context *slice_context[MAX_SLICES]; - memcpy(initial_states, fdst->initial_states, sizeof(fdst->initial_states)); - memcpy(slice_context, fdst->slice_context , sizeof(fdst->slice_context)); - - memcpy(fdst, fsrc, sizeof(*fdst)); - memcpy(fdst->initial_states, initial_states, sizeof(fdst->initial_states)); - memcpy(fdst->slice_context, slice_context , sizeof(fdst->slice_context)); - fdst->picture = picture; - fdst->last_picture = last_picture; - for (i = 0; inum_h_slices * fdst->num_v_slices; i++) { - FFV1Context *fssrc = fsrc->slice_context[i]; - FFV1Context *fsdst = fdst->slice_context[i]; - copy_fields(fsdst, fssrc, fsrc); - } - av_assert0(!fdst->plane[0].state); - av_assert0(!fdst->sample_buffer); + copy_fields(fdst, fsrc, fsrc); + fdst->use32bit = fsrc->use32bit; + memcpy(fdst->state_transition, fsrc->state_transition, + sizeof(fdst->state_transition)); + memcpy(fdst->quant_table, fsrc->quant_table, sizeof(fsrc->quant_table)); + + for (i = 0; i < fdst->num_h_slices * fdst->num_v_slices; i++) { + FFV1Context *fssrc = fsrc->slice_context[i]; + FFV1Context *fsdst = fdst->slice_context[i]; + copy_fields(fsdst, fssrc, fsrc); } + av_assert0(!fdst->plane[0].state); + av_assert0(!fdst->sample_buffer); av_assert1(fdst->max_slice_count == fsrc->max_slice_count); - ff_thread_release_ext_buffer(dst, &fdst->picture); + ff_thread_release_ext_buffer(&fdst->picture); if (fsrc->picture.f->data[0]) { if ((ret = ff_thread_ref_frame(&fdst->picture, &fsrc->picture)) < 0) return ret; @@ -1097,12 +1106,12 @@ static av_cold int ffv1_decode_close(AVCodecContext *avctx) FFV1Context *const s = avctx->priv_data; if (s->picture.f) { - ff_thread_release_ext_buffer(avctx, &s->picture); + ff_thread_release_ext_buffer(&s->picture); av_frame_free(&s->picture.f); } if (s->last_picture.f) { - ff_thread_release_ext_buffer(avctx, &s->last_picture); + ff_thread_release_ext_buffer(&s->last_picture); av_frame_free(&s->last_picture.f); } return ff_ffv1_close(avctx); diff --git a/libavcodec/ffv1enc.c b/libavcodec/ffv1enc.c index fb12776cc29..e510abf6ff9 100644 --- a/libavcodec/ffv1enc.c +++ b/libavcodec/ffv1enc.c @@ -585,8 +585,11 @@ static av_cold int encode_init(AVCodecContext *avctx) case AV_PIX_FMT_YUV440P12: case AV_PIX_FMT_YUV420P12: case AV_PIX_FMT_YUV422P12: + case AV_PIX_FMT_YUVA444P12: + case AV_PIX_FMT_YUVA422P12: if (!avctx->bits_per_raw_sample && !s->bits_per_raw_sample) s->bits_per_raw_sample = 12; + case AV_PIX_FMT_GRAY14: case AV_PIX_FMT_YUV444P14: case AV_PIX_FMT_YUV420P14: case AV_PIX_FMT_YUV422P14: @@ -667,6 +670,7 @@ static av_cold int encode_init(AVCodecContext *avctx) if (!avctx->bits_per_raw_sample && !s->bits_per_raw_sample) s->bits_per_raw_sample = 12; case AV_PIX_FMT_GBRP14: + case AV_PIX_FMT_GBRAP14: if (!avctx->bits_per_raw_sample && !s->bits_per_raw_sample) s->bits_per_raw_sample = 14; case AV_PIX_FMT_GBRP16: @@ -916,10 +920,10 @@ static void encode_slice_header(FFV1Context *f, FFV1Context *fs) put_symbol(c, state, f->plane[j].quant_table_index, 0); av_assert0(f->plane[j].quant_table_index == f->context_model); } - if (!f->cur_enc_frame->interlaced_frame) + if (!(f->cur_enc_frame->flags & AV_FRAME_FLAG_INTERLACED)) put_symbol(c, state, 3, 0); else - put_symbol(c, state, 1 + !f->cur_enc_frame->top_field_first, 0); + put_symbol(c, state, 1 + !(f->cur_enc_frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST), 0); put_symbol(c, state, f->cur_enc_frame->sample_aspect_ratio.num, 0); put_symbol(c, state, f->cur_enc_frame->sample_aspect_ratio.den, 0); if (f->version > 3) { @@ -1100,7 +1104,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, uint8_t keystate = 128; uint8_t *buf_p; int i, ret; - int64_t maxsize = AV_INPUT_BUFFER_MIN_SIZE + int64_t maxsize = FF_INPUT_BUFFER_MIN_SIZE + avctx->width*avctx->height*37LL*4; if(!pict) { @@ -1150,7 +1154,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, } if (f->version > 3) - maxsize = AV_INPUT_BUFFER_MIN_SIZE + avctx->width*avctx->height*3LL*4; + maxsize = FF_INPUT_BUFFER_MIN_SIZE + avctx->width*avctx->height*3LL*4; if (maxsize > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE - 32) { av_log(avctx, AV_LOG_WARNING, "Cannot allocate worst case packet size, the encoding could fail\n"); @@ -1242,15 +1246,15 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, static const AVOption options[] = { { "slicecrc", "Protect slices with CRCs", OFFSET(ec), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE }, { "coder", "Coder type", OFFSET(ac), AV_OPT_TYPE_INT, - { .i64 = 0 }, -2, 2, VE, "coder" }, + { .i64 = 0 }, -2, 2, VE, .unit = "coder" }, { "rice", "Golomb rice", 0, AV_OPT_TYPE_CONST, - { .i64 = AC_GOLOMB_RICE }, INT_MIN, INT_MAX, VE, "coder" }, + { .i64 = AC_GOLOMB_RICE }, INT_MIN, INT_MAX, VE, .unit = "coder" }, { "range_def", "Range with default table", 0, AV_OPT_TYPE_CONST, - { .i64 = AC_RANGE_DEFAULT_TAB_FORCE }, INT_MIN, INT_MAX, VE, "coder" }, + { .i64 = AC_RANGE_DEFAULT_TAB_FORCE }, INT_MIN, INT_MAX, VE, .unit = "coder" }, { "range_tab", "Range with custom table", 0, AV_OPT_TYPE_CONST, - { .i64 = AC_RANGE_CUSTOM_TAB }, INT_MIN, INT_MAX, VE, "coder" }, + { .i64 = AC_RANGE_CUSTOM_TAB }, INT_MIN, INT_MAX, VE, .unit = "coder" }, { "ac", "Range with custom table (the ac option exists for compatibility and is deprecated)", 0, AV_OPT_TYPE_CONST, - { .i64 = 1 }, INT_MIN, INT_MAX, VE, "coder" }, + { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "coder" }, { "context", "Context model", OFFSET(context_model), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, @@ -1284,13 +1288,14 @@ const FFCodec ff_ffv1_encoder = { AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA420P16, + AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_GRAY16, AV_PIX_FMT_GRAY8, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, - AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, + AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRAP14, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_YA8, - AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, + AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GBRP16, AV_PIX_FMT_RGB48, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_RGBA64, AV_PIX_FMT_GRAY9, diff --git a/libavcodec/fic.c b/libavcodec/fic.c index 94cf42887f3..fb998021371 100644 --- a/libavcodec/fic.c +++ b/libavcodec/fic.c @@ -406,11 +406,11 @@ static int fic_decode_frame(AVCodecContext *avctx, AVFrame *rframe, NULL, nslices, sizeof(ctx->slice_data[0]))) < 0) return ret; - ctx->frame->key_frame = 1; + ctx->frame->flags |= AV_FRAME_FLAG_KEY; ctx->frame->pict_type = AV_PICTURE_TYPE_I; for (slice = 0; slice < nslices; slice++) { if (ctx->slice_data[slice].p_frame) { - ctx->frame->key_frame = 0; + ctx->frame->flags &= ~AV_FRAME_FLAG_KEY; ctx->frame->pict_type = AV_PICTURE_TYPE_P; break; } diff --git a/libavcodec/fitsdec.c b/libavcodec/fitsdec.c index b9c51e70c31..284e945ba5b 100644 --- a/libavcodec/fitsdec.c +++ b/libavcodec/fitsdec.c @@ -301,7 +301,7 @@ static int fits_decode_frame(AVCodecContext *avctx, AVFrame *p, } } - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; @@ -319,6 +319,7 @@ static const AVClass fits_decoder_class = { .item_name = av_default_item_name, .option = fits_options, .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DECODER, }; const FFCodec ff_fits_decoder = { diff --git a/libavcodec/fitsenc.c b/libavcodec/fitsenc.c index 12952278ac4..86ea11f0c08 100644 --- a/libavcodec/fitsenc.c +++ b/libavcodec/fitsenc.c @@ -104,6 +104,7 @@ static int fits_encode_frame(AVCodecContext *avctx, AVPacket *pkt, bytes_left = padded_data_size - data_size; memset(bytestream, 0, bytes_left); + pkt->flags |= AV_PKT_FLAG_KEY; *got_packet = 1; return 0; diff --git a/libavcodec/flac.c b/libavcodec/flac.c index 174b4801bea..fac4cff9e7c 100644 --- a/libavcodec/flac.c +++ b/libavcodec/flac.c @@ -48,14 +48,14 @@ static int64_t get_utf8(GetBitContext *gb) return val; } -int ff_flac_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, +int ff_flac_decode_frame_header(void *logctx, GetBitContext *gb, FLACFrameInfo *fi, int log_level_offset) { int bs_code, sr_code, bps_code; /* frame sync code */ if ((get_bits(gb, 15) & 0x7FFF) != 0x7FFC) { - av_log(avctx, AV_LOG_ERROR + log_level_offset, "invalid sync code\n"); + av_log(logctx, AV_LOG_ERROR + log_level_offset, "invalid sync code\n"); return AVERROR_INVALIDDATA; } @@ -75,7 +75,7 @@ int ff_flac_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, fi->channels = 2; fi->ch_mode -= FLAC_MAX_CHANNELS - 1; } else { - av_log(avctx, AV_LOG_ERROR + log_level_offset, + av_log(logctx, AV_LOG_ERROR + log_level_offset, "invalid channel mode: %d\n", fi->ch_mode); return AVERROR_INVALIDDATA; } @@ -83,7 +83,7 @@ int ff_flac_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, /* bits per sample */ bps_code = get_bits(gb, 3); if (bps_code == 3) { - av_log(avctx, AV_LOG_ERROR + log_level_offset, + av_log(logctx, AV_LOG_ERROR + log_level_offset, "invalid sample size code (%d)\n", bps_code); return AVERROR_INVALIDDATA; @@ -92,7 +92,7 @@ int ff_flac_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, /* reserved bit */ if (get_bits1(gb)) { - av_log(avctx, AV_LOG_ERROR + log_level_offset, + av_log(logctx, AV_LOG_ERROR + log_level_offset, "broken stream, invalid padding\n"); return AVERROR_INVALIDDATA; } @@ -100,14 +100,14 @@ int ff_flac_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, /* sample or frame count */ fi->frame_or_sample_num = get_utf8(gb); if (fi->frame_or_sample_num < 0) { - av_log(avctx, AV_LOG_ERROR + log_level_offset, + av_log(logctx, AV_LOG_ERROR + log_level_offset, "sample/frame number invalid; utf8 fscked\n"); return AVERROR_INVALIDDATA; } /* blocksize */ if (bs_code == 0) { - av_log(avctx, AV_LOG_ERROR + log_level_offset, + av_log(logctx, AV_LOG_ERROR + log_level_offset, "reserved blocksize code: 0\n"); return AVERROR_INVALIDDATA; } else if (bs_code == 6) { @@ -128,7 +128,7 @@ int ff_flac_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, } else if (sr_code == 14) { fi->samplerate = get_bits(gb, 16) * 10; } else { - av_log(avctx, AV_LOG_ERROR + log_level_offset, + av_log(logctx, AV_LOG_ERROR + log_level_offset, "illegal sample rate code %d\n", sr_code); return AVERROR_INVALIDDATA; @@ -138,7 +138,7 @@ int ff_flac_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, skip_bits(gb, 8); if (av_crc(av_crc_get_table(AV_CRC_8_ATM), 0, gb->buffer, get_bits_count(gb)/8)) { - av_log(avctx, AV_LOG_ERROR + log_level_offset, + av_log(logctx, AV_LOG_ERROR + log_level_offset, "header crc mismatch\n"); return AVERROR_INVALIDDATA; } diff --git a/libavcodec/flac_parse.h b/libavcodec/flac_parse.h index 67a7320bea6..b0cbad825e1 100644 --- a/libavcodec/flac_parse.h +++ b/libavcodec/flac_parse.h @@ -75,13 +75,13 @@ int ff_flac_is_extradata_valid(AVCodecContext *avctx, /** * Validate and decode a frame header. - * @param avctx AVCodecContext to use as av_log() context + * @param logctx context for logging * @param gb GetBitContext from which to read frame header * @param[out] fi frame information * @param log_level_offset log level offset. can be used to silence error messages. * @return non-zero on error, 0 if ok */ -int ff_flac_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, +int ff_flac_decode_frame_header(void *logctx, GetBitContext *gb, FLACFrameInfo *fi, int log_level_offset); void ff_flac_set_channel_layout(AVCodecContext *avctx, int channels); diff --git a/libavcodec/flac_parser.c b/libavcodec/flac_parser.c index bd91cc1a058..99460e7ea60 100644 --- a/libavcodec/flac_parser.c +++ b/libavcodec/flac_parser.c @@ -519,6 +519,8 @@ static int check_header_mismatch(FLACParseContext *fpc, for (i = 0; i < FLAC_MAX_SEQUENTIAL_HEADERS && curr != child; i++) curr = curr->next; + av_assert0(i < FLAC_MAX_SEQUENTIAL_HEADERS); + if (header->link_penalty[i] < FLAC_HEADER_CRC_FAIL_PENALTY || header->link_penalty[i] == FLAC_HEADER_NOT_PENALIZED_YET) { FLACHeaderMarker *start, *end; diff --git a/libavcodec/flacdsp.c b/libavcodec/flacdsp.c index 42e231db53c..71b4ac44aa4 100644 --- a/libavcodec/flacdsp.c +++ b/libavcodec/flacdsp.c @@ -121,6 +121,8 @@ av_cold void ff_flacdsp_init(FLACDSPContext *c, enum AVSampleFormat fmt, int cha #if ARCH_ARM ff_flacdsp_init_arm(c, fmt, channels); +#elif ARCH_RISCV + ff_flacdsp_init_riscv(c, fmt, channels); #elif ARCH_X86 ff_flacdsp_init_x86(c, fmt, channels); #endif diff --git a/libavcodec/flacdsp.h b/libavcodec/flacdsp.h index 9f8ed38b66e..15149c026eb 100644 --- a/libavcodec/flacdsp.h +++ b/libavcodec/flacdsp.h @@ -38,6 +38,7 @@ typedef struct FLACDSPContext { void ff_flacdsp_init(FLACDSPContext *c, enum AVSampleFormat fmt, int channels); void ff_flacdsp_init_arm(FLACDSPContext *c, enum AVSampleFormat fmt, int channels); +void ff_flacdsp_init_riscv(FLACDSPContext *c, enum AVSampleFormat fmt, int channels); void ff_flacdsp_init_x86(FLACDSPContext *c, enum AVSampleFormat fmt, int channels); #endif /* AVCODEC_FLACDSP_H */ diff --git a/libavcodec/flacenc.c b/libavcodec/flacenc.c index a449b732353..3dc4dc2e4f1 100644 --- a/libavcodec/flacenc.c +++ b/libavcodec/flacenc.c @@ -1712,27 +1712,27 @@ static av_cold int flac_encode_close(AVCodecContext *avctx) #define FLAGS AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM static const AVOption options[] = { { "lpc_coeff_precision", "LPC coefficient precision", offsetof(FlacEncodeContext, options.lpc_coeff_precision), AV_OPT_TYPE_INT, {.i64 = 15 }, 0, MAX_LPC_PRECISION, FLAGS }, -{ "lpc_type", "LPC algorithm", offsetof(FlacEncodeContext, options.lpc_type), AV_OPT_TYPE_INT, {.i64 = FF_LPC_TYPE_DEFAULT }, FF_LPC_TYPE_DEFAULT, FF_LPC_TYPE_NB-1, FLAGS, "lpc_type" }, -{ "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LPC_TYPE_NONE }, INT_MIN, INT_MAX, FLAGS, "lpc_type" }, -{ "fixed", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LPC_TYPE_FIXED }, INT_MIN, INT_MAX, FLAGS, "lpc_type" }, -{ "levinson", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LPC_TYPE_LEVINSON }, INT_MIN, INT_MAX, FLAGS, "lpc_type" }, -{ "cholesky", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LPC_TYPE_CHOLESKY }, INT_MIN, INT_MAX, FLAGS, "lpc_type" }, +{ "lpc_type", "LPC algorithm", offsetof(FlacEncodeContext, options.lpc_type), AV_OPT_TYPE_INT, {.i64 = FF_LPC_TYPE_DEFAULT }, FF_LPC_TYPE_DEFAULT, FF_LPC_TYPE_NB-1, FLAGS, .unit = "lpc_type" }, +{ "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LPC_TYPE_NONE }, INT_MIN, INT_MAX, FLAGS, .unit = "lpc_type" }, +{ "fixed", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LPC_TYPE_FIXED }, INT_MIN, INT_MAX, FLAGS, .unit = "lpc_type" }, +{ "levinson", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LPC_TYPE_LEVINSON }, INT_MIN, INT_MAX, FLAGS, .unit = "lpc_type" }, +{ "cholesky", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LPC_TYPE_CHOLESKY }, INT_MIN, INT_MAX, FLAGS, .unit = "lpc_type" }, { "lpc_passes", "Number of passes to use for Cholesky factorization during LPC analysis", offsetof(FlacEncodeContext, options.lpc_passes), AV_OPT_TYPE_INT, {.i64 = 2 }, 1, INT_MAX, FLAGS }, { "min_partition_order", NULL, offsetof(FlacEncodeContext, options.min_partition_order), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, MAX_PARTITION_ORDER, FLAGS }, { "max_partition_order", NULL, offsetof(FlacEncodeContext, options.max_partition_order), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, MAX_PARTITION_ORDER, FLAGS }, -{ "prediction_order_method", "Search method for selecting prediction order", offsetof(FlacEncodeContext, options.prediction_order_method), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, ORDER_METHOD_LOG, FLAGS, "predm" }, -{ "estimation", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_EST }, INT_MIN, INT_MAX, FLAGS, "predm" }, -{ "2level", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_2LEVEL }, INT_MIN, INT_MAX, FLAGS, "predm" }, -{ "4level", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_4LEVEL }, INT_MIN, INT_MAX, FLAGS, "predm" }, -{ "8level", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_8LEVEL }, INT_MIN, INT_MAX, FLAGS, "predm" }, -{ "search", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_SEARCH }, INT_MIN, INT_MAX, FLAGS, "predm" }, -{ "log", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_LOG }, INT_MIN, INT_MAX, FLAGS, "predm" }, -{ "ch_mode", "Stereo decorrelation mode", offsetof(FlacEncodeContext, options.ch_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, FLAC_CHMODE_MID_SIDE, FLAGS, "ch_mode" }, -{ "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX, FLAGS, "ch_mode" }, -{ "indep", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FLAC_CHMODE_INDEPENDENT }, INT_MIN, INT_MAX, FLAGS, "ch_mode" }, -{ "left_side", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FLAC_CHMODE_LEFT_SIDE }, INT_MIN, INT_MAX, FLAGS, "ch_mode" }, -{ "right_side", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FLAC_CHMODE_RIGHT_SIDE }, INT_MIN, INT_MAX, FLAGS, "ch_mode" }, -{ "mid_side", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FLAC_CHMODE_MID_SIDE }, INT_MIN, INT_MAX, FLAGS, "ch_mode" }, +{ "prediction_order_method", "Search method for selecting prediction order", offsetof(FlacEncodeContext, options.prediction_order_method), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, ORDER_METHOD_LOG, FLAGS, .unit = "predm" }, +{ "estimation", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_EST }, INT_MIN, INT_MAX, FLAGS, .unit = "predm" }, +{ "2level", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_2LEVEL }, INT_MIN, INT_MAX, FLAGS, .unit = "predm" }, +{ "4level", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_4LEVEL }, INT_MIN, INT_MAX, FLAGS, .unit = "predm" }, +{ "8level", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_8LEVEL }, INT_MIN, INT_MAX, FLAGS, .unit = "predm" }, +{ "search", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_SEARCH }, INT_MIN, INT_MAX, FLAGS, .unit = "predm" }, +{ "log", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_LOG }, INT_MIN, INT_MAX, FLAGS, .unit = "predm" }, +{ "ch_mode", "Stereo decorrelation mode", offsetof(FlacEncodeContext, options.ch_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, FLAC_CHMODE_MID_SIDE, FLAGS, .unit = "ch_mode" }, +{ "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX, FLAGS, .unit = "ch_mode" }, +{ "indep", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FLAC_CHMODE_INDEPENDENT }, INT_MIN, INT_MAX, FLAGS, .unit = "ch_mode" }, +{ "left_side", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FLAC_CHMODE_LEFT_SIDE }, INT_MIN, INT_MAX, FLAGS, .unit = "ch_mode" }, +{ "right_side", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FLAC_CHMODE_RIGHT_SIDE }, INT_MIN, INT_MAX, FLAGS, .unit = "ch_mode" }, +{ "mid_side", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FLAC_CHMODE_MID_SIDE }, INT_MIN, INT_MAX, FLAGS, .unit = "ch_mode" }, { "exact_rice_parameters", "Calculate rice parameters exactly", offsetof(FlacEncodeContext, options.exact_rice_parameters), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "multi_dim_quant", "Multi-dimensional quantization", offsetof(FlacEncodeContext, options.multi_dim_quant), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "min_prediction_order", NULL, offsetof(FlacEncodeContext, options.min_prediction_order), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, MAX_LPC_ORDER, FLAGS }, diff --git a/libavcodec/flashsv.c b/libavcodec/flashsv.c index 8a01e3a4b66..fe00e529a5f 100644 --- a/libavcodec/flashsv.c +++ b/libavcodec/flashsv.c @@ -511,7 +511,6 @@ const FFCodec ff_flashsv_decoder = { FF_CODEC_DECODE_CB(flashsv_decode_frame), .p.capabilities = AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, - .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_BGR24, AV_PIX_FMT_NONE }, }; #endif /* CONFIG_FLASHSV_DECODER */ @@ -579,6 +578,5 @@ const FFCodec ff_flashsv2_decoder = { FF_CODEC_DECODE_CB(flashsv_decode_frame), .p.capabilities = AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, - .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_BGR24, AV_PIX_FMT_NONE }, }; #endif /* CONFIG_FLASHSV2_DECODER */ diff --git a/libavcodec/flashsv2enc.c b/libavcodec/flashsv2enc.c index 75b48eb1fd3..6dcb9102a8a 100644 --- a/libavcodec/flashsv2enc.c +++ b/libavcodec/flashsv2enc.c @@ -857,7 +857,7 @@ static int flashsv2_encode_frame(AVCodecContext *avctx, AVPacket *pkt, int res; int keyframe = 0; - if ((res = ff_alloc_packet(avctx, pkt, s->frame_size + AV_INPUT_BUFFER_MIN_SIZE)) < 0) + if ((res = ff_alloc_packet(avctx, pkt, s->frame_size + FF_INPUT_BUFFER_MIN_SIZE)) < 0) return res; /* First frame needs to be a keyframe */ diff --git a/libavcodec/flicvideo.c b/libavcodec/flicvideo.c index 228f6527752..43f3f83bf65 100644 --- a/libavcodec/flicvideo.c +++ b/libavcodec/flicvideo.c @@ -60,12 +60,28 @@ #define FLC_DTA_TYPE_CODE (0xAF44) /* Marks an "Extended FLC" comes from Dave's Targa Animator (DTA) */ #define FLC_MAGIC_CARPET_SYNTHETIC_TYPE_CODE (0xAF13) +static inline int check_pixel_ptr(ptrdiff_t ptr, int n, + ptrdiff_t limit, int direction) +{ + if (( direction && ptr + n > limit) || + (!direction && ptr + n < limit)) + return AVERROR_INVALIDDATA; + return 0; +} + #define CHECK_PIXEL_PTR(n) \ - if (pixel_ptr + n > pixel_limit) { \ - av_log (s->avctx, AV_LOG_ERROR, "Invalid pixel_ptr = %d > pixel_limit = %d\n", \ - pixel_ptr + n, pixel_limit); \ - return AVERROR_INVALIDDATA; \ - } \ +{ \ + ret = check_pixel_ptr(pixel_ptr, (n), pixel_limit, direction); \ + if (ret < 0) \ + return ret; \ +} + +#define CHECK_Y_PTR() \ +{ \ + ret = check_pixel_ptr(y_ptr, 0, pixel_limit, direction); \ + if (ret < 0) \ + return ret; \ +} typedef struct FlicDecodeContext { AVCodecContext *avctx; @@ -79,7 +95,7 @@ typedef struct FlicDecodeContext { static av_cold int flic_decode_init(AVCodecContext *avctx) { FlicDecodeContext *s = avctx->priv_data; - unsigned char *fli_header = (unsigned char *)avctx->extradata; + uint8_t *fli_header = avctx->extradata; int depth; if (avctx->extradata_size != 0 && @@ -128,6 +144,7 @@ static av_cold int flic_decode_init(AVCodecContext *avctx) } switch (depth) { + case 1 : avctx->pix_fmt = AV_PIX_FMT_MONOBLACK; break; case 8 : avctx->pix_fmt = AV_PIX_FMT_PAL8; break; case 15 : avctx->pix_fmt = AV_PIX_FMT_RGB555; break; case 16 : avctx->pix_fmt = AV_PIX_FMT_RGB565; break; @@ -146,6 +163,198 @@ static av_cold int flic_decode_init(AVCodecContext *avctx) return 0; } +static int flic_decode_frame_1BPP(AVCodecContext *avctx, + AVFrame *rframe, int *got_frame, + const uint8_t *buf, int buf_size) +{ + FlicDecodeContext *s = avctx->priv_data; + + GetByteContext g2; + ptrdiff_t pixel_ptr; + + unsigned int frame_size; + int num_chunks; + + unsigned int chunk_size; + int chunk_type; + + int i, j, ret, direction; + + int lines; + int compressed_lines; + int starting_line; + int line_packets; + ptrdiff_t y_ptr; + int byte_run; + int pixel_skip; + int pixel_countdown; + unsigned char *pixels; + ptrdiff_t pixel_limit; + + bytestream2_init(&g2, buf, buf_size); + + if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) + return ret; + + direction = s->frame->linesize[0] > 0; + pixels = s->frame->data[0]; + pixel_limit = s->avctx->height * s->frame->linesize[0]; + if (buf_size < 16 || buf_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) + return AVERROR_INVALIDDATA; + frame_size = bytestream2_get_le32(&g2); + if (frame_size > buf_size) + frame_size = buf_size; + bytestream2_skip(&g2, 2); /* skip the magic number */ + num_chunks = bytestream2_get_le16(&g2); + bytestream2_skip(&g2, 8); /* skip padding */ + + if (frame_size < 16) + return AVERROR_INVALIDDATA; + + frame_size -= 16; + + /* iterate through the chunks */ + while ((frame_size >= 6) && (num_chunks > 0) && + bytestream2_get_bytes_left(&g2) >= 4) { + int stream_ptr_after_chunk; + chunk_size = bytestream2_get_le32(&g2); + if (chunk_size > frame_size) { + av_log(avctx, AV_LOG_WARNING, + "Invalid chunk_size = %u > frame_size = %u\n", chunk_size, frame_size); + chunk_size = frame_size; + } + stream_ptr_after_chunk = bytestream2_tell(&g2) - 4 + chunk_size; + + chunk_type = bytestream2_get_le16(&g2); + + switch (chunk_type) { + case FLI_BRUN: + /* Byte run compression: This chunk type only occurs in the first + * FLI frame and it will update the entire frame. */ + y_ptr = 0; + for (lines = 0; lines < s->avctx->height; lines++) { + pixel_ptr = y_ptr; + /* disregard the line packets; instead, iterate through all + * pixels on a row */ + bytestream2_skip(&g2, 1); + pixel_countdown = (s->avctx->width + 7) >> 3; + while (pixel_countdown > 0) { + if (bytestream2_tell(&g2) + 1 > stream_ptr_after_chunk) + break; + byte_run = sign_extend(bytestream2_get_byte(&g2), 8); + if (!byte_run) { + av_log(avctx, AV_LOG_ERROR, "Invalid byte run value.\n"); + return AVERROR_INVALIDDATA; + } + + if (byte_run > 0) { + int value = bytestream2_get_byte(&g2); + CHECK_PIXEL_PTR(byte_run); + for (j = 0; j < byte_run; j++) { + pixels[pixel_ptr++] = value; + pixel_countdown--; + if (pixel_countdown < 0) + av_log(avctx, AV_LOG_ERROR, "pixel_countdown < 0 (%d) at line %d\n", + pixel_countdown, lines); + } + } else { /* copy bytes if byte_run < 0 */ + byte_run = -byte_run; + CHECK_PIXEL_PTR(byte_run); + if (bytestream2_tell(&g2) + byte_run > stream_ptr_after_chunk) + break; + for (j = 0; j < byte_run; j++) { + pixels[pixel_ptr++] = bytestream2_get_byte(&g2); + pixel_countdown--; + if (pixel_countdown < 0) + av_log(avctx, AV_LOG_ERROR, "pixel_countdown < 0 (%d) at line %d\n", + pixel_countdown, lines); + } + } + } + + y_ptr += s->frame->linesize[0]; + } + break; + + case FLI_LC: + /* line compressed */ + starting_line = bytestream2_get_le16(&g2); + if (starting_line >= s->avctx->height) + return AVERROR_INVALIDDATA; + y_ptr = 0; + y_ptr += starting_line * s->frame->linesize[0]; + + compressed_lines = bytestream2_get_le16(&g2); + while (compressed_lines > 0) { + pixel_ptr = y_ptr; + CHECK_PIXEL_PTR(0); + pixel_countdown = (s->avctx->width + 7) >> 3; + if (bytestream2_tell(&g2) + 1 > stream_ptr_after_chunk) + break; + line_packets = bytestream2_get_byte(&g2); + if (line_packets > 0) { + for (i = 0; i < line_packets; i++) { + /* account for the skip bytes */ + if (bytestream2_tell(&g2) + 1 > stream_ptr_after_chunk) + break; + pixel_skip = bytestream2_get_byte(&g2); + pixel_ptr += pixel_skip; + pixel_countdown -= pixel_skip; + byte_run = sign_extend(bytestream2_get_byte(&g2),8); + if (byte_run > 0) { + CHECK_PIXEL_PTR(byte_run); + if (bytestream2_tell(&g2) + byte_run > stream_ptr_after_chunk) + break; + for (j = 0; j < byte_run; j++, pixel_countdown--) { + pixels[pixel_ptr++] = bytestream2_get_byte(&g2); + } + } else if (byte_run < 0) { + int value = bytestream2_get_byte(&g2); + byte_run = -byte_run; + CHECK_PIXEL_PTR(byte_run); + for (j = 0; j < byte_run; j++, pixel_countdown--) { + pixels[pixel_ptr++] = value; + } + } + } + } + + y_ptr += s->frame->linesize[0]; + compressed_lines--; + } + break; + + default: + av_log(avctx, AV_LOG_ERROR, "Unrecognized chunk type: %d\n", chunk_type); + break; + } + + if (stream_ptr_after_chunk - bytestream2_tell(&g2) >= 0) { + bytestream2_skip(&g2, stream_ptr_after_chunk - bytestream2_tell(&g2)); + } else { + av_log(avctx, AV_LOG_ERROR, "Chunk overread\n"); + break; + } + + frame_size -= chunk_size; + num_chunks--; + } + + /* by the end of the chunk, the stream ptr should equal the frame + * size (minus 1 or 2, possibly); if it doesn't, issue a warning */ + if (bytestream2_get_bytes_left(&g2) > 2) + av_log(avctx, AV_LOG_ERROR, "Processed FLI chunk where chunk size = %d " \ + "and final chunk ptr = %d\n", buf_size, + buf_size - bytestream2_get_bytes_left(&g2)); + + if ((ret = av_frame_ref(rframe, s->frame)) < 0) + return ret; + + *got_frame = 1; + + return buf_size; +} + static int flic_decode_frame_8BPP(AVCodecContext *avctx, AVFrame *rframe, int *got_frame, const uint8_t *buf, int buf_size) @@ -153,7 +362,7 @@ static int flic_decode_frame_8BPP(AVCodecContext *avctx, FlicDecodeContext *s = avctx->priv_data; GetByteContext g2; - int pixel_ptr; + ptrdiff_t pixel_ptr; int palette_ptr; unsigned char palette_idx1; unsigned char palette_idx2; @@ -164,7 +373,7 @@ static int flic_decode_frame_8BPP(AVCodecContext *avctx, unsigned int chunk_size; int chunk_type; - int i, j, ret; + int i, j, ret, direction; int color_packets; int color_changes; @@ -175,18 +384,19 @@ static int flic_decode_frame_8BPP(AVCodecContext *avctx, int compressed_lines; int starting_line; int line_packets; - int y_ptr; + ptrdiff_t y_ptr; int byte_run; int pixel_skip; int pixel_countdown; unsigned char *pixels; - unsigned int pixel_limit; + ptrdiff_t pixel_limit; bytestream2_init(&g2, buf, buf_size); if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; + direction = s->frame->linesize[0] > 0; pixels = s->frame->data[0]; pixel_limit = s->avctx->height * s->frame->linesize[0]; if (buf_size < 16 || buf_size > INT_MAX - (3 * 256 + AV_INPUT_BUFFER_PADDING_SIZE)) @@ -271,8 +481,7 @@ static int flic_decode_frame_8BPP(AVCodecContext *avctx, while (compressed_lines > 0) { if (bytestream2_tell(&g2) + 2 > stream_ptr_after_chunk) break; - if (y_ptr > pixel_limit) - return AVERROR_INVALIDDATA; + CHECK_Y_PTR() line_packets = sign_extend(bytestream2_get_le16(&g2), 16); if ((line_packets & 0xC000) == 0xC000) { // line skip opcode @@ -374,8 +583,8 @@ static int flic_decode_frame_8BPP(AVCodecContext *avctx, case FLI_BLACK: /* set the whole frame to color 0 (which is usually black) */ - memset(pixels, 0, - s->frame->linesize[0] * s->avctx->height); + for (int y = 0; y < s->avctx->height; y++) + memset(pixels + y * s->frame->linesize[0], 0, s->avctx->width); break; case FLI_BRUN: @@ -433,7 +642,7 @@ static int flic_decode_frame_8BPP(AVCodecContext *avctx, "has incorrect size, skipping chunk\n", chunk_size - 6); bytestream2_skip(&g2, chunk_size - 6); } else { - for (y_ptr = 0; y_ptr < s->frame->linesize[0] * s->avctx->height; + for (y_ptr = 0; check_pixel_ptr(y_ptr, s->avctx->width, pixel_limit, direction) == 0; y_ptr += s->frame->linesize[0]) { bytestream2_get_buffer(&g2, &pixels[y_ptr], s->avctx->width); @@ -473,7 +682,11 @@ static int flic_decode_frame_8BPP(AVCodecContext *avctx, /* make the palette available on the way out */ memcpy(s->frame->data[1], s->palette, AVPALETTE_SIZE); if (s->new_palette) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS s->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif s->new_palette = 0; } @@ -494,7 +707,7 @@ static int flic_decode_frame_15_16BPP(AVCodecContext *avctx, FlicDecodeContext *s = avctx->priv_data; GetByteContext g2; - int pixel_ptr; + ptrdiff_t pixel_ptr; unsigned char palette_idx1; unsigned int frame_size; @@ -503,24 +716,25 @@ static int flic_decode_frame_15_16BPP(AVCodecContext *avctx, unsigned int chunk_size; int chunk_type; - int i, j, ret; + int i, j, ret, direction; int lines; int compressed_lines; int line_packets; - int y_ptr; + ptrdiff_t y_ptr; int byte_run; int pixel_skip; int pixel_countdown; unsigned char *pixels; int pixel; - unsigned int pixel_limit; + ptrdiff_t pixel_limit; bytestream2_init(&g2, buf, buf_size); if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; + direction = s->frame->linesize[0] > 0; pixels = s->frame->data[0]; pixel_limit = s->avctx->height * s->frame->linesize[0]; @@ -569,8 +783,7 @@ static int flic_decode_frame_15_16BPP(AVCodecContext *avctx, while (compressed_lines > 0) { if (bytestream2_tell(&g2) + 2 > stream_ptr_after_chunk) break; - if (y_ptr > pixel_limit) - return AVERROR_INVALIDDATA; + CHECK_Y_PTR() line_packets = sign_extend(bytestream2_get_le16(&g2), 16); if (line_packets < 0) { line_packets = -line_packets; @@ -621,8 +834,8 @@ static int flic_decode_frame_15_16BPP(AVCodecContext *avctx, case FLI_BLACK: /* set the whole frame to 0x0000 which is black in both 15Bpp and 16Bpp modes. */ - memset(pixels, 0x0000, - s->frame->linesize[0] * s->avctx->height); + for (int y = 0; y < s->avctx->height; y++) + memset(pixels + y * s->frame->linesize[0], 0, s->avctx->width * 2); break; case FLI_BRUN: @@ -736,7 +949,7 @@ static int flic_decode_frame_15_16BPP(AVCodecContext *avctx, if (bytestream2_get_bytes_left(&g2) < 2 * s->avctx->width * s->avctx->height ) return AVERROR_INVALIDDATA; - for (y_ptr = 0; y_ptr < s->frame->linesize[0] * s->avctx->height; + for (y_ptr = 0; check_pixel_ptr(y_ptr, 2*s->avctx->width, pixel_limit, direction) == 0; y_ptr += s->frame->linesize[0]) { pixel_countdown = s->avctx->width; @@ -794,7 +1007,7 @@ static int flic_decode_frame_24BPP(AVCodecContext *avctx, FlicDecodeContext *s = avctx->priv_data; GetByteContext g2; - int pixel_ptr; + ptrdiff_t pixel_ptr; unsigned char palette_idx1; unsigned int frame_size; @@ -803,24 +1016,25 @@ static int flic_decode_frame_24BPP(AVCodecContext *avctx, unsigned int chunk_size; int chunk_type; - int i, j, ret; + int i, j, ret, direction; int lines; int compressed_lines; int line_packets; - int y_ptr; + ptrdiff_t y_ptr; int byte_run; int pixel_skip; int pixel_countdown; unsigned char *pixels; int pixel; - unsigned int pixel_limit; + ptrdiff_t pixel_limit; bytestream2_init(&g2, buf, buf_size); if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; + direction = s->frame->linesize[0] > 0; pixels = s->frame->data[0]; pixel_limit = s->avctx->height * s->frame->linesize[0]; @@ -869,8 +1083,7 @@ static int flic_decode_frame_24BPP(AVCodecContext *avctx, while (compressed_lines > 0) { if (bytestream2_tell(&g2) + 2 > stream_ptr_after_chunk) break; - if (y_ptr > pixel_limit) - return AVERROR_INVALIDDATA; + CHECK_Y_PTR() line_packets = sign_extend(bytestream2_get_le16(&g2), 16); if (line_packets < 0) { line_packets = -line_packets; @@ -922,8 +1135,8 @@ static int flic_decode_frame_24BPP(AVCodecContext *avctx, case FLI_BLACK: /* set the whole frame to 0x00 which is black for 24 bit mode. */ - memset(pixels, 0x00, - s->frame->linesize[0] * s->avctx->height); + for (int y = 0; y < s->avctx->height; y++) + memset(pixels + y * s->frame->linesize[0], 0, s->avctx->width * 3); break; case FLI_BRUN: @@ -1022,7 +1235,7 @@ static int flic_decode_frame_24BPP(AVCodecContext *avctx, "bigger than image, skipping chunk\n", chunk_size - 6); bytestream2_skip(&g2, chunk_size - 6); } else { - for (y_ptr = 0; y_ptr < s->frame->linesize[0] * s->avctx->height; + for (y_ptr = 0; check_pixel_ptr(y_ptr, 3*s->avctx->width, pixel_limit, direction) == 0; y_ptr += s->frame->linesize[0]) { bytestream2_get_buffer(&g2, pixels + y_ptr, 3*s->avctx->width); @@ -1072,7 +1285,10 @@ static int flic_decode_frame(AVCodecContext *avctx, AVFrame *frame, { const uint8_t *buf = avpkt->data; int buf_size = avpkt->size; - if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { + if (avctx->pix_fmt == AV_PIX_FMT_MONOBLACK) { + return flic_decode_frame_1BPP(avctx, frame, got_frame, + buf, buf_size); + } else if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { return flic_decode_frame_8BPP(avctx, frame, got_frame, buf, buf_size); } else if ((avctx->pix_fmt == AV_PIX_FMT_RGB555) || diff --git a/libavcodec/flvdec.c b/libavcodec/flvdec.c index 09fefd3d1c0..8baaed06a8f 100644 --- a/libavcodec/flvdec.c +++ b/libavcodec/flvdec.c @@ -123,6 +123,4 @@ const FFCodec ff_flv_decoder = { .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, .p.max_lowres = 3, - .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE }, }; diff --git a/libavcodec/fmvc.c b/libavcodec/fmvc.c index 3ee915cc4cb..a9e5afd17b8 100644 --- a/libavcodec/fmvc.c +++ b/libavcodec/fmvc.c @@ -100,7 +100,6 @@ static int decode_type2(GetByteContext *gb, PutByteContext *pb) continue; } } - repeat = 0; } repeat = 1; } @@ -433,7 +432,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) return ret; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; src = s->buffer; @@ -519,7 +518,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) return ret; - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; ssrc = s->buffer; diff --git a/libavcodec/frame_thread_encoder.c b/libavcodec/frame_thread_encoder.c index 62d9580ad4c..cda51581178 100644 --- a/libavcodec/frame_thread_encoder.c +++ b/libavcodec/frame_thread_encoder.c @@ -27,6 +27,7 @@ #include "libavutil/opt.h" #include "libavutil/thread.h" #include "avcodec.h" +#include "avcodec_internal.h" #include "encode.h" #include "internal.h" #include "pthread_internal.h" @@ -110,7 +111,7 @@ static void * attribute_align_arg worker(void *v){ pthread_mutex_unlock(&c->finished_task_mutex); } end: - avcodec_close(avctx); + ff_codec_close(avctx); av_freep(&avctx); return NULL; } @@ -230,7 +231,7 @@ av_cold int ff_frame_thread_encoder_init(AVCodecContext *avctx) return 0; fail: - avcodec_close(thread_avctx); + ff_codec_close(thread_avctx); av_freep(&thread_avctx); avctx->thread_count = i; av_log(avctx, AV_LOG_ERROR, "ff_frame_thread_encoder_init failed\n"); diff --git a/libavcodec/fraps.c b/libavcodec/fraps.c index 4c4c46b6027..bed244e4e96 100644 --- a/libavcodec/fraps.c +++ b/libavcodec/fraps.c @@ -56,7 +56,6 @@ typedef struct FrapsContext { int tmpbuf_size; } FrapsContext; - /** * initializes decoder * @param avctx codec context @@ -123,13 +122,13 @@ static int fraps2_decode_plane(FrapsContext *s, uint8_t *dst, int stride, int w, else if (Uoff) dst[i] += 0x80; if (get_bits_left(&gb) < 0) { - ff_free_vlc(&vlc); + ff_vlc_free(&vlc); return AVERROR_INVALIDDATA; } } dst += stride; } - ff_free_vlc(&vlc); + ff_vlc_free(&vlc); return 0; } @@ -216,7 +215,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f, } f->pict_type = AV_PICTURE_TYPE_I; - f->key_frame = 1; + f->flags |= AV_FRAME_FLAG_KEY; avctx->pix_fmt = version & 1 ? is_pal ? AV_PIX_FMT_PAL8 : AV_PIX_FMT_BGR24 : AV_PIX_FMT_YUVJ420P; avctx->color_range = version & 1 ? AVCOL_RANGE_UNSPECIFIED @@ -323,7 +322,6 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f, return buf_size; } - /** * closes decoder * @param avctx codec context @@ -331,13 +329,12 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *f, */ static av_cold int decode_end(AVCodecContext *avctx) { - FrapsContext *s = (FrapsContext*)avctx->priv_data; + FrapsContext *s = avctx->priv_data; av_freep(&s->tmpbuf); return 0; } - const FFCodec ff_fraps_decoder = { .p.name = "fraps", CODEC_LONG_NAME("Fraps"), diff --git a/libavcodec/frwu.c b/libavcodec/frwu.c index cf183f84107..70bc136765b 100644 --- a/libavcodec/frwu.c +++ b/libavcodec/frwu.c @@ -63,7 +63,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; pic->pict_type = AV_PICTURE_TYPE_I; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; for (field = 0; field < 2; field++) { int i; diff --git a/libavcodec/ftr.c b/libavcodec/ftr.c index 74a2c10b5c8..7cf1b1586fb 100644 --- a/libavcodec/ftr.c +++ b/libavcodec/ftr.c @@ -203,6 +203,10 @@ const FFCodec ff_ftr_decoder = { .close = ftr_close, .flush = ftr_flush, .priv_data_size = sizeof(FTRContext), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/g2meet.c b/libavcodec/g2meet.c index 05b5a5124fb..34ff60a3cf9 100644 --- a/libavcodec/g2meet.c +++ b/libavcodec/g2meet.c @@ -195,8 +195,8 @@ static av_cold void jpg_free_context(JPGContext *ctx) int i; for (i = 0; i < 2; i++) { - ff_free_vlc(&ctx->dc_vlc[i]); - ff_free_vlc(&ctx->ac_vlc[i]); + ff_vlc_free(&ctx->dc_vlc[i]); + ff_vlc_free(&ctx->ac_vlc[i]); } av_freep(&ctx->buf); @@ -1560,7 +1560,10 @@ static int g2m_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = got_header; + if (got_header) + pic->flags |= AV_FRAME_FLAG_KEY; + else + pic->flags &= ~AV_FRAME_FLAG_KEY; pic->pict_type = got_header ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; for (i = 0; i < avctx->height; i++) diff --git a/libavcodec/g722dsp.c b/libavcodec/g722dsp.c index c770bfbdff0..302283688fd 100644 --- a/libavcodec/g722dsp.c +++ b/libavcodec/g722dsp.c @@ -71,6 +71,8 @@ av_cold void ff_g722dsp_init(G722DSPContext *c) #if ARCH_ARM ff_g722dsp_init_arm(c); +#elif ARCH_RISCV + ff_g722dsp_init_riscv(c); #elif ARCH_X86 ff_g722dsp_init_x86(c); #endif diff --git a/libavcodec/g722dsp.h b/libavcodec/g722dsp.h index c956a1e161a..1aa7078a295 100644 --- a/libavcodec/g722dsp.h +++ b/libavcodec/g722dsp.h @@ -29,6 +29,7 @@ typedef struct G722DSPContext { void ff_g722dsp_init(G722DSPContext *c); void ff_g722dsp_init_arm(G722DSPContext *c); +void ff_g722dsp_init_riscv(G722DSPContext *c); void ff_g722dsp_init_x86(G722DSPContext *c); #endif /* AVCODEC_G722DSP_H */ diff --git a/libavcodec/g722enc.c b/libavcodec/g722enc.c index 47811cee4d6..2f19a1b29a0 100644 --- a/libavcodec/g722enc.c +++ b/libavcodec/g722enc.c @@ -382,7 +382,6 @@ const FFCodec ff_adpcm_g722_encoder = { .close = g722_encode_close, FF_CODEC_ENCODE_CB(g722_encode_frame), .p.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO) .p.ch_layouts = (const AVChannelLayout[]){ AV_CHANNEL_LAYOUT_MONO, { 0 } }, diff --git a/libavcodec/g723_1dec.c b/libavcodec/g723_1dec.c index 55e20de5b5f..5fe4a21d9b2 100644 --- a/libavcodec/g723_1dec.c +++ b/libavcodec/g723_1dec.c @@ -1118,6 +1118,10 @@ const FFCodec ff_g723_1_decoder = { .priv_data_size = sizeof(G723_1_Context), .init = g723_1_decode_init, FF_CODEC_DECODE_CB(g723_1_decode_frame), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .p.priv_class = &g723_1dec_class, }; diff --git a/libavcodec/g729dec.c b/libavcodec/g729dec.c index f783812cc7c..33e1fb9c29d 100644 --- a/libavcodec/g729dec.c +++ b/libavcodec/g729dec.c @@ -760,7 +760,11 @@ const FFCodec ff_g729_decoder = { .init = decoder_init, FF_CODEC_DECODE_CB(decode_frame), .close = decode_close, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, }; const FFCodec ff_acelp_kelvin_decoder = { @@ -772,5 +776,9 @@ const FFCodec ff_acelp_kelvin_decoder = { .init = decoder_init, FF_CODEC_DECODE_CB(decode_frame), .close = decode_close, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, }; diff --git a/libavcodec/g729postfilter.c b/libavcodec/g729postfilter.c index 382db924321..b1880b2fe13 100644 --- a/libavcodec/g729postfilter.c +++ b/libavcodec/g729postfilter.c @@ -78,7 +78,7 @@ static const int16_t formant_pp_factor_den_pow[10] = { /** * \brief Residual signal calculation (4.2.1 if G.729) - * \param out [out] output data filtered through A(z/FORMANT_PP_FACTOR_NUM) + * \param[out] out output data filtered through A(z/FORMANT_PP_FACTOR_NUM) * \param filter_coeffs (3.12) A(z/FORMANT_PP_FACTOR_NUM) filter coefficients * \param in input speech data to process * \param subframe_size size of one subframe @@ -105,7 +105,7 @@ static void residual_filter(int16_t* out, const int16_t* filter_coeffs, const in * \param dsp initialized DSP context * \param pitch_delay_int integer part of the pitch delay in the first subframe * \param residual filtering input data - * \param residual_filt [out] speech signal with applied A(z/FORMANT_PP_FACTOR_NUM) filter + * \param[out] residual_filt speech signal with applied A(z/FORMANT_PP_FACTOR_NUM) filter * \param subframe_size size of subframe * * \return 0 if long-term prediction gain is less than 3dB, 1 - otherwise @@ -472,7 +472,7 @@ static int16_t get_tilt_comp(AudioDSPContext *adsp, int16_t *lp_gn, /** * \brief Apply tilt compensation filter (4.2.3). - * \param res_pst [in/out] residual signal (partially filtered) + * \param[in,out] res_pst residual signal (partially filtered) * \param k1 (3.12) reflection coefficient * \param subframe_size size of subframe * \param ht_prev_data previous data for 4.2.3, equation 86 @@ -572,7 +572,7 @@ void ff_g729_postfilter(AudioDSPContext *adsp, int16_t* ht_prev_data, int* voici * \brief Adaptive gain control (4.2.4) * \param gain_before gain of speech before applying postfilters * \param gain_after gain of speech after applying postfilters - * \param speech [in/out] signal buffer + * \param[in,out] speech signal buffer * \param subframe_size length of subframe * \param gain_prev (3.12) previous value of gain coefficient * diff --git a/libavcodec/g729postfilter.h b/libavcodec/g729postfilter.h index 69815341ed3..0421ed87209 100644 --- a/libavcodec/g729postfilter.h +++ b/libavcodec/g729postfilter.h @@ -79,15 +79,15 @@ /** * \brief Signal postfiltering (4.2) * \param dsp initialized DSP context - * \param ht_prev_data [in/out] (Q12) pointer to variable receiving tilt + * \param[in,out] ht_prev_data (Q12) pointer to variable receiving tilt * compensation filter data from previous subframe - * \param voicing [in/out] (Q0) pointer to variable receiving voicing decision + * \param[in,out] voicing (Q0) pointer to variable receiving voicing decision * \param lp_filter_coeffs (Q12) LP filter coefficients * \param pitch_delay_int integer part of the pitch delay - * \param residual [in/out] (Q0) residual signal buffer (used in long-term postfilter) - * \param res_filter_data [in/out] (Q0) speech data of previous subframe - * \param pos_filter_data [in/out] (Q0) previous speech data for short-term postfilter - * \param speech [in/out] (Q0) signal buffer + * \param[in,out] residual (Q0) residual signal buffer (used in long-term postfilter) + * \param[in,out] res_filter_data (Q0) speech data of previous subframe + * \param[in,out] pos_filter_data (Q0) previous speech data for short-term postfilter + * \param[in,out] speech (Q0) signal buffer * \param subframe_size size of subframe * * Filtering has the following stages: @@ -105,7 +105,7 @@ void ff_g729_postfilter(AudioDSPContext *adsp, int16_t* ht_prev_data, int* voici * \brief Adaptive gain control (4.2.4) * \param gain_before (Q0) gain of speech before applying postfilters * \param gain_after (Q0) gain of speech after applying postfilters - * \param speech [in/out] (Q0) signal buffer + * \param[in,out] speech (Q0) signal buffer * \param subframe_size length of subframe * \param gain_prev (Q12) previous value of gain coefficient * diff --git a/libavcodec/gemdec.c b/libavcodec/gemdec.c index c8fd8dcdcd2..9e2a50c7661 100644 --- a/libavcodec/gemdec.c +++ b/libavcodec/gemdec.c @@ -180,8 +180,12 @@ static int gem_decode_frame(AVCodecContext *avctx, AVFrame *p, return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS p->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif palette = (uint32_t *)p->data[1]; if (tag == AV_RB32("STTT")) { diff --git a/libavcodec/get_bits.h b/libavcodec/get_bits.h index 65dc080ddb3..fe2f6378b45 100644 --- a/libavcodec/get_bits.h +++ b/libavcodec/get_bits.h @@ -93,6 +93,7 @@ typedef BitstreamContext GetBitContext; #define init_get_bits8 bits_init8 #define align_get_bits bits_align #define get_vlc2 bits_read_vlc +#define get_vlc_multi bits_read_vlc_multi #define init_get_bits8_le(s, buffer, byte_size) bits_init8_le((BitstreamContextLE*)s, buffer, byte_size) #define get_bits_le(s, n) bits_read_le((BitstreamContextLE*)s, n) @@ -186,21 +187,28 @@ static inline unsigned int show_bits(GetBitContext *s, int n); #define CLOSE_READER(name, gb) (gb)->index = name ## _index +#define UPDATE_CACHE_BE_EXT(name, gb, bits, dst_bits) name ## _cache = \ + AV_RB ## bits((gb)->buffer + (name ## _index >> 3)) << (name ## _index & 7) >> (bits - dst_bits) + +#define UPDATE_CACHE_LE_EXT(name, gb, bits, dst_bits) name ## _cache = \ + (uint ## dst_bits ## _t)(AV_RL ## bits((gb)->buffer + (name ## _index >> 3)) >> (name ## _index & 7)) + +/* Using these two macros ensures that 32 bits are available. */ +# define UPDATE_CACHE_LE_32(name, gb) UPDATE_CACHE_LE_EXT(name, (gb), 64, 32) + +# define UPDATE_CACHE_BE_32(name, gb) UPDATE_CACHE_BE_EXT(name, (gb), 64, 32) + # ifdef LONG_BITSTREAM_READER -# define UPDATE_CACHE_LE(name, gb) name ## _cache = \ - AV_RL64((gb)->buffer + (name ## _index >> 3)) >> (name ## _index & 7) +# define UPDATE_CACHE_LE(name, gb) UPDATE_CACHE_LE_32(name, (gb)) -# define UPDATE_CACHE_BE(name, gb) name ## _cache = \ - AV_RB64((gb)->buffer + (name ## _index >> 3)) >> (32 - (name ## _index & 7)) +# define UPDATE_CACHE_BE(name, gb) UPDATE_CACHE_BE_32(name, (gb)) #else -# define UPDATE_CACHE_LE(name, gb) name ## _cache = \ - AV_RL32((gb)->buffer + (name ## _index >> 3)) >> (name ## _index & 7) +# define UPDATE_CACHE_LE(name, gb) UPDATE_CACHE_LE_EXT(name, (gb), 32, 32) -# define UPDATE_CACHE_BE(name, gb) name ## _cache = \ - AV_RB32((gb)->buffer + (name ## _index >> 3)) << (name ## _index & 7) +# define UPDATE_CACHE_BE(name, gb) UPDATE_CACHE_BE_EXT(name, (gb), 32, 32) #endif @@ -208,12 +216,14 @@ static inline unsigned int show_bits(GetBitContext *s, int n); #ifdef BITSTREAM_READER_LE # define UPDATE_CACHE(name, gb) UPDATE_CACHE_LE(name, gb) +# define UPDATE_CACHE_32(name, gb) UPDATE_CACHE_LE_32(name, (gb)) # define SKIP_CACHE(name, gb, num) name ## _cache >>= (num) #else # define UPDATE_CACHE(name, gb) UPDATE_CACHE_BE(name, gb) +# define UPDATE_CACHE_32(name, gb) UPDATE_CACHE_BE_32(name, (gb)) # define SKIP_CACHE(name, gb, num) name ## _cache <<= (num) @@ -413,15 +423,26 @@ static inline unsigned int get_bits_long(GetBitContext *s, int n) av_assert2(n>=0 && n<=32); if (!n) { return 0; - } else if (n <= MIN_CACHE_BITS) { + } else if ((!HAVE_FAST_64BIT || av_builtin_constant_p(n <= MIN_CACHE_BITS)) + && n <= MIN_CACHE_BITS) { return get_bits(s, n); } else { +#if HAVE_FAST_64BIT + unsigned tmp; + OPEN_READER(re, s); + UPDATE_CACHE_32(re, s); + tmp = SHOW_UBITS(re, s, n); + LAST_SKIP_BITS(re, s, n); + CLOSE_READER(re, s); + return tmp; +#else #ifdef BITSTREAM_READER_LE unsigned ret = get_bits(s, 16); return ret | (get_bits(s, n - 16) << 16); #else unsigned ret = get_bits(s, 16) << (n - 16); return ret | get_bits(s, n - 16); +#endif #endif } } @@ -622,7 +643,7 @@ static inline const uint8_t *align_get_bits(GetBitContext *s) /** * Parse a vlc code. * @param bits is the number of bits which will be read at once, must be - * identical to nb_bits in init_vlc() + * identical to nb_bits in vlc_init() * @param max_depth is the number of times bits bits must be read to completely * read the longest vlc code * = (max_vlc_length + bits - 1) / bits @@ -643,6 +664,16 @@ static av_always_inline int get_vlc2(GetBitContext *s, const VLCElem *table, return code; } +static inline int get_vlc_multi(GetBitContext *s, uint8_t *dst, + const VLC_MULTI_ELEM *const Jtable, + const VLCElem *const table, + const int bits, const int max_depth, + const int symbols_size) +{ + dst[0] = get_vlc2(s, table, bits, max_depth); + return 1; +} + static inline int decode012(GetBitContext *gb) { int n; diff --git a/libavcodec/get_buffer.c b/libavcodec/get_buffer.c index a04fd878de6..9b35fde7c68 100644 --- a/libavcodec/get_buffer.c +++ b/libavcodec/get_buffer.c @@ -32,6 +32,7 @@ #include "avcodec.h" #include "internal.h" +#include "refstruct.h" typedef struct FramePool { /** @@ -52,51 +53,23 @@ typedef struct FramePool { int samples; } FramePool; -static void frame_pool_free(void *opaque, uint8_t *data) +static void frame_pool_free(FFRefStructOpaque unused, void *obj) { - FramePool *pool = (FramePool*)data; + FramePool *pool = obj; int i; for (i = 0; i < FF_ARRAY_ELEMS(pool->pools); i++) av_buffer_pool_uninit(&pool->pools[i]); - - av_freep(&data); -} - -static AVBufferRef *frame_pool_alloc(void) -{ - FramePool *pool = av_mallocz(sizeof(*pool)); - AVBufferRef *buf; - - if (!pool) - return NULL; - - buf = av_buffer_create((uint8_t*)pool, sizeof(*pool), - frame_pool_free, NULL, 0); - if (!buf) { - av_freep(&pool); - return NULL; - } - - return buf; } static int update_frame_pool(AVCodecContext *avctx, AVFrame *frame) { - FramePool *pool = avctx->internal->pool ? - (FramePool*)avctx->internal->pool->data : NULL; - AVBufferRef *pool_buf; + FramePool *pool = avctx->internal->pool; int i, ret, ch, planes; if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) { int planar = av_sample_fmt_is_planar(frame->format); ch = frame->ch_layout.nb_channels; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (!ch) - ch = frame->channels; -FF_ENABLE_DEPRECATION_WARNINGS -#endif planes = planar ? ch : 1; } @@ -109,10 +82,9 @@ FF_ENABLE_DEPRECATION_WARNINGS return 0; } - pool_buf = frame_pool_alloc(); - if (!pool_buf) + pool = ff_refstruct_alloc_ext(sizeof(*pool), 0, NULL, frame_pool_free); + if (!pool) return AVERROR(ENOMEM); - pool = (FramePool*)pool_buf->data; switch (avctx->codec_type) { case AVMEDIA_TYPE_VIDEO: { @@ -189,18 +161,18 @@ FF_ENABLE_DEPRECATION_WARNINGS default: av_assert0(0); } - av_buffer_unref(&avctx->internal->pool); - avctx->internal->pool = pool_buf; + ff_refstruct_unref(&avctx->internal->pool); + avctx->internal->pool = pool; return 0; fail: - av_buffer_unref(&pool_buf); + ff_refstruct_unref(&pool); return ret; } static int audio_get_buffer(AVCodecContext *avctx, AVFrame *frame) { - FramePool *pool = (FramePool*)avctx->internal->pool->data; + FramePool *pool = avctx->internal->pool; int planes = pool->planes; int i; @@ -245,7 +217,7 @@ static int audio_get_buffer(AVCodecContext *avctx, AVFrame *frame) static int video_get_buffer(AVCodecContext *s, AVFrame *pic) { - FramePool *pool = (FramePool*)s->internal->pool->data; + FramePool *pool = s->internal->pool; int i; if (pic->data[0] || pic->data[1] || pic->data[2] || pic->data[3]) { @@ -285,6 +257,22 @@ int avcodec_default_get_buffer2(AVCodecContext *avctx, AVFrame *frame, int flags if (avctx->hw_frames_ctx) { ret = av_hwframe_get_buffer(avctx->hw_frames_ctx, frame, 0); + if (ret == AVERROR(ENOMEM)) { + AVHWFramesContext *frames_ctx = + (AVHWFramesContext*)avctx->hw_frames_ctx->data; + if (frames_ctx->initial_pool_size > 0 && + !avctx->internal->warned_on_failed_allocation_from_fixed_pool) { + av_log(avctx, AV_LOG_WARNING, "Failed to allocate a %s/%s " + "frame from a fixed pool of hardware frames.\n", + av_get_pix_fmt_name(frames_ctx->format), + av_get_pix_fmt_name(frames_ctx->sw_format)); + av_log(avctx, AV_LOG_WARNING, "Consider setting " + "extra_hw_frames to a larger value " + "(currently set to %d, giving a pool size of %d).\n", + avctx->extra_hw_frames, frames_ctx->initial_pool_size); + avctx->internal->warned_on_failed_allocation_from_fixed_pool = 1; + } + } frame->width = avctx->coded_width; frame->height = avctx->coded_height; return ret; diff --git a/libavcodec/gif.c b/libavcodec/gif.c index 131af6198ad..49356236e72 100644 --- a/libavcodec/gif.c +++ b/libavcodec/gif.c @@ -477,7 +477,7 @@ static int gif_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const uint32_t *palette = NULL; int ret; - if ((ret = ff_alloc_packet(avctx, pkt, avctx->width*avctx->height*7/5 + AV_INPUT_BUFFER_MIN_SIZE)) < 0) + if ((ret = ff_alloc_packet(avctx, pkt, avctx->width*avctx->height*7/5 + FF_INPUT_BUFFER_MIN_SIZE)) < 0) return ret; outbuf_ptr = pkt->data; end = pkt->data + pkt->size; @@ -503,8 +503,7 @@ static int gif_encode_frame(AVCodecContext *avctx, AVPacket *pkt, } if (!s->image) { - av_frame_unref(s->last_frame); - ret = av_frame_ref(s->last_frame, pict); + ret = av_frame_replace(s->last_frame, pict); if (ret < 0) return ret; } @@ -533,9 +532,9 @@ static int gif_encode_close(AVCodecContext *avctx) #define OFFSET(x) offsetof(GIFContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption gif_options[] = { - { "gifflags", "set GIF flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = GF_OFFSETTING|GF_TRANSDIFF}, 0, INT_MAX, FLAGS, "flags" }, - { "offsetting", "enable picture offsetting", 0, AV_OPT_TYPE_CONST, {.i64=GF_OFFSETTING}, INT_MIN, INT_MAX, FLAGS, "flags" }, - { "transdiff", "enable transparency detection between frames", 0, AV_OPT_TYPE_CONST, {.i64=GF_TRANSDIFF}, INT_MIN, INT_MAX, FLAGS, "flags" }, + { "gifflags", "set GIF flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = GF_OFFSETTING|GF_TRANSDIFF}, 0, INT_MAX, FLAGS, .unit = "flags" }, + { "offsetting", "enable picture offsetting", 0, AV_OPT_TYPE_CONST, {.i64=GF_OFFSETTING}, INT_MIN, INT_MAX, FLAGS, .unit = "flags" }, + { "transdiff", "enable transparency detection between frames", 0, AV_OPT_TYPE_CONST, {.i64=GF_TRANSDIFF}, INT_MIN, INT_MAX, FLAGS, .unit = "flags" }, { "gifimage", "enable encoding only images per frame", OFFSET(image), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { "global_palette", "write a palette to the global gif header where feasible", OFFSET(use_global_palette), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, { NULL } diff --git a/libavcodec/gif_parser.c b/libavcodec/gif_parser.c index 4c18c00d7d8..f5903585faf 100644 --- a/libavcodec/gif_parser.c +++ b/libavcodec/gif_parser.c @@ -24,9 +24,6 @@ * GIF parser */ -#include "libavutil/bswap.h" -#include "libavutil/common.h" - #include "gif.h" #include "parser.h" @@ -50,11 +47,13 @@ typedef struct GIFParseContext { int block_size; int etype; int delay; + int keyframe; } GIFParseContext; static int gif_find_frame_end(GIFParseContext *g, const uint8_t *buf, int buf_size, void *logctx) { + ParseContext *pc = &g->pc; int index, next = END_NOT_FOUND; for (index = 0; index < buf_size; index++) { @@ -63,10 +62,22 @@ static int gif_find_frame_end(GIFParseContext *g, const uint8_t *buf, !memcmp(buf + index, gif89a_sig, 6)) { g->state = GIF_HEADER; g->found_sig++; + g->keyframe = 1; } else if (buf[index] == GIF_EXTENSION_INTRODUCER) { g->state = GIF_EXTENSION; - g->found_start = 1; + g->found_start = pc->frame_start_found = 1; } else if (buf[index] == GIF_IMAGE_SEPARATOR) { + if (g->state != GIF_EXTENSION_BLOCK && g->found_start && + g->found_end && g->found_sig) { + next = index; + g->found_start = pc->frame_start_found = 1; + g->found_end = 0; + g->index = 0; + g->gct_flag = 0; + g->gct_size = 0; + g->state = GIF_IMAGE; + break; + } g->state = GIF_IMAGE; } else if (buf[index] == GIF_TRAILER) { g->state = 0; @@ -93,7 +104,7 @@ static int gif_find_frame_end(GIFParseContext *g, const uint8_t *buf, } else if (g->state == GIF_EXTENSION) { if (g->found_start && g->found_end && g->found_sig) { next = index; - g->found_start = 0; + g->found_start = pc->frame_start_found = 0; g->found_end = 0; g->index = 0; g->gct_flag = 0; @@ -140,7 +151,7 @@ static int gif_find_frame_end(GIFParseContext *g, const uint8_t *buf, } g->index++; } else if (g->state == GIF_IMAGE) { - if (g->index == 8) { + if (g->index == 9) { g->gct_flag = !!(buf[index] & 0x80); g->gct_size = 3 * (1 << ((buf[index] & 0x07) + 1)); } @@ -165,14 +176,24 @@ static int gif_parse(AVCodecParserContext *s, AVCodecContext *avctx, GIFParseContext *g = s->priv_data; int next; - next = gif_find_frame_end(g, buf, buf_size, avctx); - if (ff_combine_frame(&g->pc, next, &buf, &buf_size) < 0) { - *poutbuf = NULL; - *poutbuf_size = 0; - return buf_size; + *poutbuf_size = 0; + *poutbuf = NULL; + + if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) { + next = buf_size; + } else { + next = gif_find_frame_end(g, buf, buf_size, avctx); + if (ff_combine_frame(&g->pc, next, &buf, &buf_size) < 0) { + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } } - s->duration = g->delay; + s->duration = g->delay ? g->delay : 10; + s->key_frame = g->keyframe; + s->pict_type = g->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + g->keyframe = 0; *poutbuf = buf; *poutbuf_size = buf_size; diff --git a/libavcodec/gifdec.c b/libavcodec/gifdec.c index f47390c3bd9..a97a6d15fc3 100644 --- a/libavcodec/gifdec.c +++ b/libavcodec/gifdec.c @@ -59,7 +59,7 @@ typedef struct GifState { /* depending on disposal method we store either part of the image * drawn on the canvas or background color that * should be used upon disposal */ - uint32_t * stored_img; + uint8_t *stored_img; int stored_img_size; int stored_bg_color; @@ -86,52 +86,55 @@ static void gif_read_palette(GifState *s, uint32_t *pal, int nb) static void gif_fill(AVFrame *picture, uint32_t color) { - uint32_t *p = (uint32_t *)picture->data[0]; - uint32_t *p_end = p + (picture->linesize[0] / sizeof(uint32_t)) * picture->height; - - for (; p < p_end; p++) - *p = color; + const ptrdiff_t linesize = picture->linesize[0]; + uint8_t *py = picture->data[0]; + const int w = picture->width; + const int h = picture->height; + + for (int y = 0; y < h; y++) { + uint32_t *px = (uint32_t *)py; + for (int x = 0; x < w; x++) + px[x] = color; + py += linesize; + } } static void gif_fill_rect(AVFrame *picture, uint32_t color, int l, int t, int w, int h) { - const int linesize = picture->linesize[0] / sizeof(uint32_t); - const uint32_t *py = (uint32_t *)picture->data[0] + t * linesize; - const uint32_t *pr, *pb = py + h * linesize; - uint32_t *px; - - for (; py < pb; py += linesize) { - px = (uint32_t *)py + l; - pr = px + w; - - for (; px < pr; px++) - *px = color; + const ptrdiff_t linesize = picture->linesize[0]; + uint8_t *py = picture->data[0] + t * linesize; + + for (int y = 0; y < h; y++) { + uint32_t *px = ((uint32_t *)py) + l; + for (int x = 0; x < w; x++) + px[x] = color; + py += linesize; } } -static void gif_copy_img_rect(const uint32_t *src, uint32_t *dst, - int linesize, int l, int t, int w, int h) +static void gif_copy_img_rect(const uint8_t *src, uint8_t *dst, + ptrdiff_t src_linesize, + ptrdiff_t dst_linesize, + int l, int t, int w, int h) { - const int y_start = t * linesize; - const uint32_t *src_px, - *src_py = src + y_start, - *dst_py = dst + y_start; - const uint32_t *src_pb = src_py + h * linesize; - uint32_t *dst_px; - - for (; src_py < src_pb; src_py += linesize, dst_py += linesize) { - src_px = src_py + l; - dst_px = (uint32_t *)dst_py + l; - - memcpy(dst_px, src_px, w * sizeof(uint32_t)); + const uint8_t *src_py = src; + uint8_t *dst_py = dst; + + src_py += t * src_linesize; + dst_py += t * dst_linesize; + for (int y = 0; y < h; y++) { + memcpy(dst_py + l * 4, src_py + l * 4, w * 4); + src_py += src_linesize; + dst_py += dst_linesize; } } static int gif_read_image(GifState *s, AVFrame *frame) { int left, top, width, height, bits_per_pixel, code_size, flags, pw; - int is_interleaved, has_local_palette, y, pass, y1, linesize, pal_size, lzwed_len; + int is_interleaved, has_local_palette, y, pass, y1, pal_size, lzwed_len; uint32_t *ptr, *pal, *px, *pr, *ptr1; + ptrdiff_t linesize; int ret; uint8_t *idx; @@ -214,8 +217,8 @@ static int gif_read_image(GifState *s, AVFrame *frame) if (s->gce_prev_disposal == GCE_DISPOSAL_BACKGROUND) { gif_fill_rect(frame, s->stored_bg_color, s->gce_l, s->gce_t, s->gce_w, s->gce_h); } else if (s->gce_prev_disposal == GCE_DISPOSAL_RESTORE) { - gif_copy_img_rect(s->stored_img, (uint32_t *)frame->data[0], - frame->linesize[0] / sizeof(uint32_t), s->gce_l, s->gce_t, s->gce_w, s->gce_h); + gif_copy_img_rect(s->stored_img, frame->data[0], + FFABS(frame->linesize[0]), frame->linesize[0], s->gce_l, s->gce_t, s->gce_w, s->gce_h); } s->gce_prev_disposal = s->gce_disposal; @@ -230,12 +233,12 @@ static int gif_read_image(GifState *s, AVFrame *frame) else s->stored_bg_color = s->bg_color; } else if (s->gce_disposal == GCE_DISPOSAL_RESTORE) { - av_fast_malloc(&s->stored_img, &s->stored_img_size, frame->linesize[0] * frame->height); + av_fast_malloc(&s->stored_img, &s->stored_img_size, FFABS(frame->linesize[0]) * frame->height); if (!s->stored_img) return AVERROR(ENOMEM); - gif_copy_img_rect((uint32_t *)frame->data[0], s->stored_img, - frame->linesize[0] / sizeof(uint32_t), left, top, pw, height); + gif_copy_img_rect(frame->data[0], s->stored_img, + frame->linesize[0], FFABS(frame->linesize[0]), left, top, pw, height); } } @@ -252,8 +255,8 @@ static int gif_read_image(GifState *s, AVFrame *frame) } /* read all the image */ - linesize = frame->linesize[0] / sizeof(uint32_t); - ptr1 = (uint32_t *)frame->data[0] + top * linesize + left; + linesize = frame->linesize[0]; + ptr1 = (uint32_t *)(frame->data[0] + top * linesize) + left; ptr = ptr1; pass = 0; y1 = 0; @@ -278,24 +281,24 @@ static int gif_read_image(GifState *s, AVFrame *frame) case 0: case 1: y1 += 8; - ptr += linesize * 8; + ptr += linesize * 2; break; case 2: y1 += 4; - ptr += linesize * 4; + ptr += linesize * 1; break; case 3: y1 += 2; - ptr += linesize * 2; + ptr += linesize / 2; break; } while (y1 >= height) { y1 = 4 >> pass; - ptr = ptr1 + linesize * y1; + ptr = ptr1 + linesize / 4 * y1; pass++; } } else { - ptr += linesize; + ptr += linesize / 4; } } @@ -472,10 +475,6 @@ static int gif_decode_frame(AVCodecContext *avctx, AVFrame *rframe, bytestream2_init(&s->gb, avpkt->data, avpkt->size); - s->frame->pts = avpkt->pts; - s->frame->pkt_dts = avpkt->dts; - s->frame->duration = avpkt->duration; - if (avpkt->size >= 6) { s->keyframe = memcmp(avpkt->data, gif87a_sig, 6) == 0 || memcmp(avpkt->data, gif89a_sig, 6) == 0; @@ -493,35 +492,29 @@ static int gif_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; av_frame_unref(s->frame); - if ((ret = ff_get_buffer(avctx, s->frame, 0)) < 0) - return ret; - av_fast_malloc(&s->idx_line, &s->idx_line_size, s->screen_width); if (!s->idx_line) return AVERROR(ENOMEM); - - s->frame->pict_type = AV_PICTURE_TYPE_I; - s->frame->key_frame = 1; - s->keyframe_ok = 1; - } else { - if (!s->keyframe_ok) { - av_log(avctx, AV_LOG_ERROR, "cannot decode frame without keyframe\n"); - return AVERROR_INVALIDDATA; - } - - if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) - return ret; - - s->frame->pict_type = AV_PICTURE_TYPE_P; - s->frame->key_frame = 0; + } else if (!s->keyframe_ok) { + av_log(avctx, AV_LOG_ERROR, "cannot decode frame without keyframe\n"); + return AVERROR_INVALIDDATA; } + ret = ff_reget_buffer(avctx, s->frame, 0); + if (ret < 0) + return ret; + ret = gif_parse_next_image(s, s->frame); if (ret < 0) return ret; if ((ret = av_frame_ref(rframe, s->frame)) < 0) return ret; + + rframe->pict_type = s->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + rframe->flags = AV_FRAME_FLAG_KEY * s->keyframe; + s->keyframe_ok |= !!s->keyframe; + *got_frame = 1; return bytestream2_tell(&s->gb); diff --git a/libavcodec/golomb.h b/libavcodec/golomb.h index 164c2583b6c..742334978d5 100644 --- a/libavcodec/golomb.h +++ b/libavcodec/golomb.h @@ -402,6 +402,7 @@ static inline int get_ur_golomb(GetBitContext *gb, int k, int limit, log = av_log2(buf); if (log > 31 - limit) { + av_assert2(log >= k); buf >>= log - k; buf += (30U - log) << k; LAST_SKIP_BITS(re, gb, 32 + k - log); @@ -424,6 +425,8 @@ static inline int get_ur_golomb(GetBitContext *gb, int k, int limit, /** * read unsigned golomb rice code (jpegls). + * + * @returns -1 on error */ static inline int get_ur_golomb_jpegls(GetBitContext *gb, int k, int limit, int esc_len) @@ -535,6 +538,8 @@ static inline int get_sr_golomb(GetBitContext *gb, int k, int limit, /** * read signed golomb rice code (flac). + * + * @returns INT_MIN on error */ static inline int get_sr_golomb_flac(GetBitContext *gb, int k, int limit, int esc_len) diff --git a/libavcodec/h261dec.c b/libavcodec/h261dec.c index 84962939643..4fbd5985b36 100644 --- a/libavcodec/h261dec.c +++ b/libavcodec/h261dec.c @@ -44,10 +44,10 @@ #define MBA_STUFFING 33 #define MBA_STARTCODE 34 -static VLC h261_mba_vlc; -static VLC h261_mtype_vlc; -static VLC h261_mv_vlc; -static VLC h261_cbp_vlc; +static VLCElem h261_mba_vlc[540]; +static VLCElem h261_mtype_vlc[80]; +static VLCElem h261_mv_vlc[144]; +static VLCElem h261_cbp_vlc[512]; typedef struct H261DecContext { MpegEncContext s; @@ -64,18 +64,18 @@ typedef struct H261DecContext { static av_cold void h261_decode_init_static(void) { - INIT_VLC_STATIC(&h261_mba_vlc, H261_MBA_VLC_BITS, 35, - ff_h261_mba_bits, 1, 1, - ff_h261_mba_code, 1, 1, 540); - INIT_VLC_STATIC(&h261_mtype_vlc, H261_MTYPE_VLC_BITS, 10, - ff_h261_mtype_bits, 1, 1, - ff_h261_mtype_code, 1, 1, 80); - INIT_VLC_STATIC(&h261_mv_vlc, H261_MV_VLC_BITS, 17, - &ff_h261_mv_tab[0][1], 2, 1, - &ff_h261_mv_tab[0][0], 2, 1, 144); - INIT_VLC_STATIC(&h261_cbp_vlc, H261_CBP_VLC_BITS, 63, - &ff_h261_cbp_tab[0][1], 2, 1, - &ff_h261_cbp_tab[0][0], 2, 1, 512); + VLC_INIT_STATIC_TABLE(h261_mba_vlc, H261_MBA_VLC_BITS, 35, + ff_h261_mba_bits, 1, 1, + ff_h261_mba_code, 1, 1, 0); + VLC_INIT_STATIC_TABLE(h261_mtype_vlc, H261_MTYPE_VLC_BITS, 10, + ff_h261_mtype_bits, 1, 1, + ff_h261_mtype_code, 1, 1, 0); + VLC_INIT_STATIC_TABLE(h261_mv_vlc, H261_MV_VLC_BITS, 17, + &ff_h261_mv_tab[0][1], 2, 1, + &ff_h261_mv_tab[0][0], 2, 1, 0); + VLC_INIT_STATIC_TABLE(h261_cbp_vlc, H261_CBP_VLC_BITS, 63, + &ff_h261_cbp_tab[0][1], 2, 1, + &ff_h261_cbp_tab[0][0], 2, 1, 0); INIT_FIRST_VLC_RL(ff_h261_rl_tcoeff, 552); } @@ -94,7 +94,6 @@ static av_cold int h261_decode_init(AVCodecContext *avctx) avctx->pix_fmt = AV_PIX_FMT_YUV420P; h->gob_start_code_skipped = 0; - ff_mpv_idct_init(s); ff_thread_once(&init_static_once, h261_decode_init_static); @@ -254,7 +253,7 @@ static const int mvmap[17] = { static int decode_mv_component(GetBitContext *gb, int v) { - int mv_diff = get_vlc2(gb, h261_mv_vlc.table, H261_MV_VLC_BITS, 2); + int mv_diff = get_vlc2(gb, h261_mv_vlc, H261_MV_VLC_BITS, 2); /* check if mv_diff is valid */ if (mv_diff < 0) @@ -379,7 +378,7 @@ static int h261_decode_mb(H261DecContext *h) cbp = 63; // Read mba do { - h->mba_diff = get_vlc2(&s->gb, h261_mba_vlc.table, + h->mba_diff = get_vlc2(&s->gb, h261_mba_vlc, H261_MBA_VLC_BITS, 2); /* Check for slice end */ @@ -410,7 +409,7 @@ static int h261_decode_mb(H261DecContext *h) h261_init_dest(s); // Read mtype - com->mtype = get_vlc2(&s->gb, h261_mtype_vlc.table, H261_MTYPE_VLC_BITS, 2); + com->mtype = get_vlc2(&s->gb, h261_mtype_vlc, H261_MTYPE_VLC_BITS, 2); if (com->mtype < 0) { av_log(s->avctx, AV_LOG_ERROR, "Invalid mtype index %d\n", com->mtype); @@ -450,7 +449,7 @@ static int h261_decode_mb(H261DecContext *h) // Read cbp if (HAS_CBP(com->mtype)) - cbp = get_vlc2(&s->gb, h261_cbp_vlc.table, H261_CBP_VLC_BITS, 1) + 1; + cbp = get_vlc2(&s->gb, h261_cbp_vlc, H261_CBP_VLC_BITS, 1) + 1; if (s->mb_intra) { s->current_picture.mb_type[xy] = MB_TYPE_INTRA; @@ -513,10 +512,7 @@ static int h261_decode_picture_header(H261DecContext *h) } /* temporal reference */ - i = get_bits(&s->gb, 5); /* picture timestamp */ - if (i < (s->picture_number & 31)) - i += 32; - s->picture_number = (s->picture_number & ~31) + i; + skip_bits(&s->gb, 5); /* picture timestamp */ s->avctx->framerate = (AVRational) { 30000, 1001 }; @@ -531,17 +527,11 @@ static int h261_decode_picture_header(H261DecContext *h) if (format == 0) { // QCIF s->width = 176; s->height = 144; - s->mb_width = 11; - s->mb_height = 9; } else { // CIF s->width = 352; s->height = 288; - s->mb_width = 22; - s->mb_height = 18; } - s->mb_num = s->mb_width * s->mb_height; - skip_bits1(&s->gb); /* still image mode off */ skip_bits1(&s->gb); /* Reserved */ @@ -615,7 +605,6 @@ static int h261_decode_frame(AVCodecContext *avctx, AVFrame *pict, h->gob_start_code_skipped = 0; -retry: init_get_bits(&s->gb, buf, buf_size * 8); ret = h261_decode_picture_header(h); @@ -637,18 +626,12 @@ static int h261_decode_frame(AVCodecContext *avctx, AVFrame *pict, ret = ff_set_dimensions(avctx, s->width, s->height); if (ret < 0) return ret; - - goto retry; } - // for skipping the frame - s->current_picture.f->pict_type = s->pict_type; - s->current_picture.f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; - if ((avctx->skip_frame >= AVDISCARD_NONREF && s->pict_type == AV_PICTURE_TYPE_B) || (avctx->skip_frame >= AVDISCARD_NONKEY && s->pict_type != AV_PICTURE_TYPE_I) || avctx->skip_frame >= AVDISCARD_ALL) - return get_consumed_bytes(s, buf_size); + return buf_size; if (ff_mpv_frame_start(s, avctx) < 0) return -1; diff --git a/libavcodec/h263dec.c b/libavcodec/h263dec.c index 68a618a7ed1..910df7585f7 100644 --- a/libavcodec/h263dec.c +++ b/libavcodec/h263dec.c @@ -36,6 +36,7 @@ #include "flvdec.h" #include "h263.h" #include "h263dec.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "mpeg_er.h" #include "mpeg4video.h" @@ -48,6 +49,23 @@ #include "thread.h" #include "wmv2dec.h" +static const enum AVPixelFormat h263_hwaccel_pixfmt_list_420[] = { +#if CONFIG_H263_VAAPI_HWACCEL || CONFIG_MPEG4_VAAPI_HWACCEL + AV_PIX_FMT_VAAPI, +#endif +#if CONFIG_MPEG4_NVDEC_HWACCEL + AV_PIX_FMT_CUDA, +#endif +#if CONFIG_MPEG4_VDPAU_HWACCEL + AV_PIX_FMT_VDPAU, +#endif +#if CONFIG_H263_VIDEOTOOLBOX_HWACCEL || CONFIG_MPEG4_VIDEOTOOLBOX_HWACCEL + AV_PIX_FMT_VIDEOTOOLBOX, +#endif + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_NONE +}; + static enum AVPixelFormat h263_get_format(AVCodecContext *avctx) { /* MPEG-4 Studio Profile only, not supported by hardware */ @@ -62,7 +80,12 @@ static enum AVPixelFormat h263_get_format(AVCodecContext *avctx) return AV_PIX_FMT_GRAY8; } - return avctx->pix_fmt = ff_get_format(avctx, avctx->codec->pix_fmts); + if (avctx->codec_id == AV_CODEC_ID_H263 || + avctx->codec_id == AV_CODEC_ID_H263P || + avctx->codec_id == AV_CODEC_ID_MPEG4) + return avctx->pix_fmt = ff_get_format(avctx, h263_hwaccel_pixfmt_list_420); + + return AV_PIX_FMT_YUV420P; } av_cold int ff_h263_decode_init(AVCodecContext *avctx) @@ -127,7 +150,6 @@ av_cold int ff_h263_decode_init(AVCodecContext *avctx) avctx->codec->id != AV_CODEC_ID_H263P && avctx->codec->id != AV_CODEC_ID_MPEG4) { avctx->pix_fmt = h263_get_format(avctx); - ff_mpv_idct_init(s); if ((ret = ff_mpv_common_init(s)) < 0) return ret; } @@ -190,7 +212,7 @@ static int decode_slice(MpegEncContext *s) if (s->avctx->hwaccel) { const uint8_t *start = s->gb.buffer + get_bits_count(&s->gb) / 8; - ret = s->avctx->hwaccel->decode_slice(s->avctx, start, s->gb.buffer_end - start); + ret = FF_HW_CALL(s->avctx, decode_slice, start, s->gb.buffer_end - start); // ensure we exit decode loop s->mb_y = s->mb_height; return ret; @@ -458,23 +480,12 @@ int ff_h263_decode_frame(AVCodecContext *avctx, AVFrame *pict, if (ret < 0) return ret; - if (!s->context_initialized) - // we need the idct permutation for reading a custom matrix - ff_mpv_idct_init(s); - /* let's go :-) */ if (CONFIG_WMV2_DECODER && s->msmpeg4_version == 5) { ret = ff_wmv2_decode_picture_header(s); } else if (CONFIG_MSMPEG4DEC && s->msmpeg4_version) { ret = ff_msmpeg4_decode_picture_header(s); } else if (CONFIG_MPEG4_DECODER && avctx->codec_id == AV_CODEC_ID_MPEG4) { - if (s->avctx->extradata_size && !s->extradata_parsed) { - GetBitContext gb; - - if (init_get_bits8(&gb, s->avctx->extradata, s->avctx->extradata_size) >= 0 ) - ff_mpeg4_decode_picture_header(avctx->priv_data, &gb, 1, 0); - s->extradata_parsed = 1; - } ret = ff_mpeg4_decode_picture_header(avctx->priv_data, &s->gb, 0, 0); s->skipped_last_frame = (ret == FRAME_SKIPPED); } else if (CONFIG_H263I_DECODER && s->codec_id == AV_CODEC_ID_H263I) { @@ -568,8 +579,8 @@ int ff_h263_decode_frame(AVCodecContext *avctx, AVFrame *pict, ff_thread_finish_setup(avctx); if (avctx->hwaccel) { - ret = avctx->hwaccel->start_frame(avctx, s->gb.buffer, - s->gb.buffer_end - s->gb.buffer); + ret = FF_HW_CALL(avctx, start_frame, + s->gb.buffer, s->gb.buffer_end - s->gb.buffer); if (ret < 0 ) return ret; } @@ -621,10 +632,10 @@ int ff_h263_decode_frame(AVCodecContext *avctx, AVFrame *pict, av_assert1(s->bitstream_buffer_size == 0); frame_end: if (!s->studio_profile) - ff_er_frame_end(&s->er); + ff_er_frame_end(&s->er, NULL); if (avctx->hwaccel) { - ret = avctx->hwaccel->end_frame(avctx); + ret = FF_HW_SIMPLE_CALL(avctx, end_frame); if (ret < 0) return ret; } @@ -670,23 +681,6 @@ int ff_h263_decode_frame(AVCodecContext *avctx, AVFrame *pict, return get_consumed_bytes(s, buf_size); } -const enum AVPixelFormat ff_h263_hwaccel_pixfmt_list_420[] = { -#if CONFIG_H263_VAAPI_HWACCEL || CONFIG_MPEG4_VAAPI_HWACCEL - AV_PIX_FMT_VAAPI, -#endif -#if CONFIG_MPEG4_NVDEC_HWACCEL - AV_PIX_FMT_CUDA, -#endif -#if CONFIG_MPEG4_VDPAU_HWACCEL - AV_PIX_FMT_VDPAU, -#endif -#if CONFIG_H263_VIDEOTOOLBOX_HWACCEL || CONFIG_MPEG4_VIDEOTOOLBOX_HWACCEL - AV_PIX_FMT_VIDEOTOOLBOX, -#endif - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE -}; - static const AVCodecHWConfigInternal *const h263_hw_config_list[] = { #if CONFIG_H263_VAAPI_HWACCEL HWACCEL_VAAPI(h263), @@ -717,7 +711,6 @@ const FFCodec ff_h263_decoder = { .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, .flush = ff_mpeg_flush, .p.max_lowres = 3, - .p.pix_fmts = ff_h263_hwaccel_pixfmt_list_420, .hw_configs = h263_hw_config_list, }; @@ -735,6 +728,5 @@ const FFCodec ff_h263p_decoder = { .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, .flush = ff_mpeg_flush, .p.max_lowres = 3, - .p.pix_fmts = ff_h263_hwaccel_pixfmt_list_420, .hw_configs = h263_hw_config_list, }; diff --git a/libavcodec/h263dec.h b/libavcodec/h263dec.h index 9f1db729037..89c5fcf58fb 100644 --- a/libavcodec/h263dec.h +++ b/libavcodec/h263dec.h @@ -33,12 +33,10 @@ #define CBPY_VLC_BITS 6 #define TEX_VLC_BITS 9 -extern VLC ff_h263_intra_MCBPC_vlc; -extern VLC ff_h263_inter_MCBPC_vlc; -extern VLC ff_h263_cbpy_vlc; -extern VLC ff_h263_mv_vlc; - -extern const enum AVPixelFormat ff_h263_hwaccel_pixfmt_list_420[]; +extern VLCElem ff_h263_intra_MCBPC_vlc[]; +extern VLCElem ff_h263_inter_MCBPC_vlc[]; +extern VLCElem ff_h263_cbpy_vlc[]; +extern VLCElem ff_h263_mv_vlc[]; int ff_h263_decode_motion(MpegEncContext * s, int pred, int f_code); int ff_h263_decode_init(AVCodecContext *avctx); diff --git a/libavcodec/h2645_parse.c b/libavcodec/h2645_parse.c index 90944177c72..9f66f079c29 100644 --- a/libavcodec/h2645_parse.c +++ b/libavcodec/h2645_parse.c @@ -30,6 +30,7 @@ #include "hevc.h" #include "h264.h" #include "h2645_parse.h" +#include "vvc.h" int ff_h2645_extract_rbsp(const uint8_t *src, int length, H2645RBSP *rbsp, H2645NAL *nal, int small_padding) @@ -39,8 +40,9 @@ int ff_h2645_extract_rbsp(const uint8_t *src, int length, nal->skipped_bytes = 0; #define STARTCODE_TEST \ - if (i + 2 < length && src[i + 1] == 0 && src[i + 2] <= 3) { \ - if (src[i + 2] != 3 && src[i + 2] != 0) { \ + if (i + 2 < length && src[i + 1] == 0 && \ + (src[i + 2] == 3 || src[i + 2] == 1)) { \ + if (src[i + 2] == 1) { \ /* startcode, so we must be past the end */ \ length = i; \ } \ @@ -145,6 +147,47 @@ int ff_h2645_extract_rbsp(const uint8_t *src, int length, return si; } +static const char *const vvc_nal_type_name[32] = { + "TRAIL_NUT", // VVC_TRAIL_NUT + "STSA_NUT", // VVC_STSA_NUT + "RADL_NUT", // VVC_RADL_NUT + "RASL_NUT", // VVC_RASL_NUT + "RSV_VCL4", // VVC_RSV_VCL_4 + "RSV_VCL5", // VVC_RSV_VCL_5 + "RSV_VCL6", // VVC_RSV_VCL_6 + "IDR_W_RADL", // VVC_IDR_W_RADL + "IDR_N_LP", // VVC_IDR_N_LP + "CRA_NUT", // VVC_CRA_NUT + "GDR_NUT", // VVC_GDR_NUT + "RSV_IRAP_11", // VVC_RSV_IRAP_11 + "OPI_NUT", // VVC_OPI_NUT + "DCI_NUT", // VVC_DCI_NUT + "VPS_NUT", // VVC_VPS_NUT + "SPS_NUT", // VVC_SPS_NUT + "PPS_NUT", // VVC_PPS_NUT + "APS_PREFIX", // VVC_PREFIX_APS_NUT + "APS_SUFFIX", // VVC_SUFFIX_APS_NUT + "PH_NUT", // VVC_PH_NUT + "AUD_NUT", // VVC_AUD_NUT + "EOS_NUT", // VVC_EOS_NUT + "EOB_NUT", // VVC_EOB_NUT + "SEI_PREFIX", // VVC_PREFIX_SEI_NUT + "SEI_SUFFIX", // VVC_SUFFIX_SEI_NUT + "FD_NUT", // VVC_FD_NUT + "RSV_NVCL26", // VVC_RSV_NVCL_26 + "RSV_NVCL27", // VVC_RSV_NVCL_27 + "UNSPEC28", // VVC_UNSPEC_28 + "UNSPEC29", // VVC_UNSPEC_29 + "UNSPEC30", // VVC_UNSPEC_30 + "UNSPEC31", // VVC_UNSPEC_31 +}; + +static const char *vvc_nal_unit_name(int nal_type) +{ + av_assert0(nal_type >= 0 && nal_type < 32); + return vvc_nal_type_name[nal_type]; +} + static const char *const hevc_nal_type_name[64] = { "TRAIL_N", // HEVC_NAL_TRAIL_N "TRAIL_R", // HEVC_NAL_TRAIL_R @@ -293,6 +336,31 @@ static int get_bit_length(H2645NAL *nal, int min_size, int skip_trailing_zeros) * @return AVERROR_INVALIDDATA if the packet is not a valid NAL unit, * 0 otherwise */ +static int vvc_parse_nal_header(H2645NAL *nal, void *logctx) +{ + GetBitContext *gb = &nal->gb; + + if (get_bits1(gb) != 0) //forbidden_zero_bit + return AVERROR_INVALIDDATA; + + skip_bits1(gb); //nuh_reserved_zero_bit + + nal->nuh_layer_id = get_bits(gb, 6); + nal->type = get_bits(gb, 5); + nal->temporal_id = get_bits(gb, 3) - 1; + if (nal->temporal_id < 0) + return AVERROR_INVALIDDATA; + + if ((nal->type >= VVC_IDR_W_RADL && nal->type <= VVC_RSV_IRAP_11) && nal->temporal_id) + return AVERROR_INVALIDDATA; + + av_log(logctx, AV_LOG_DEBUG, + "nal_unit_type: %d(%s), nuh_layer_id: %d, temporal_id: %d\n", + nal->type, vvc_nal_unit_name(nal->type), nal->nuh_layer_id, nal->temporal_id); + + return 0; +} + static int hevc_parse_nal_header(H2645NAL *nal, void *logctx) { GetBitContext *gb = &nal->gb; @@ -509,7 +577,9 @@ int ff_h2645_packet_split(H2645Packet *pkt, const uint8_t *buf, int length, /* Reset type in case it contains a stale value from a previously parsed NAL */ nal->type = 0; - if (codec_id == AV_CODEC_ID_HEVC) + if (codec_id == AV_CODEC_ID_VVC) + ret = vvc_parse_nal_header(nal, logctx); + else if (codec_id == AV_CODEC_ID_HEVC) ret = hevc_parse_nal_header(nal, logctx); else ret = h264_parse_nal_header(nal, logctx); diff --git a/libavcodec/h2645_sei.c b/libavcodec/h2645_sei.c index 6e4a9a1af25..3b58d22d6fc 100644 --- a/libavcodec/h2645_sei.c +++ b/libavcodec/h2645_sei.c @@ -27,17 +27,20 @@ #include "libavutil/ambient_viewing_environment.h" #include "libavutil/display.h" +#include "libavutil/hdr_dynamic_metadata.h" #include "libavutil/film_grain_params.h" +#include "libavutil/mastering_display_metadata.h" #include "libavutil/pixdesc.h" #include "libavutil/stereo3d.h" #include "atsc_a53.h" #include "avcodec.h" -#include "dynamic_hdr10_plus.h" +#include "decode.h" #include "dynamic_hdr_vivid.h" #include "get_bits.h" #include "golomb.h" #include "h2645_sei.h" +#include "itut35.h" #define IS_H264(codec_id) (CONFIG_H264_SEI && CONFIG_HEVC_SEI ? codec_id == AV_CODEC_ID_H264 : CONFIG_H264_SEI) #define IS_HEVC(codec_id) (CONFIG_H264_SEI && CONFIG_HEVC_SEI ? codec_id == AV_CODEC_ID_HEVC : CONFIG_HEVC_SEI) @@ -52,8 +55,8 @@ static int decode_registered_user_data_dynamic_hdr_plus(HEVCSEIDynamicHDRPlus *s if (!metadata) return AVERROR(ENOMEM); - err = ff_parse_itu_t_t35_to_dynamic_hdr10_plus(metadata, gb->buffer, - bytestream2_get_bytes_left(gb)); + err = av_dynamic_hdr_plus_from_t35(metadata, gb->buffer, + bytestream2_get_bytes_left(gb)); if (err < 0) { av_free(metadata); return err; @@ -138,7 +141,8 @@ static int decode_registered_user_data(H2645SEI *h, GetByteContext *gb, bytestream2_skipu(gb, 1); // itu_t_t35_country_code_extension_byte } - if (country_code != 0xB5 && country_code != 0x26) { // usa_country_code and cn_country_code + if (country_code != ITU_T_T35_COUNTRY_CODE_US && + country_code != ITU_T_T35_COUNTRY_CODE_CN) { av_log(logctx, AV_LOG_VERBOSE, "Unsupported User Data Registered ITU-T T35 SEI message (country_code = %d)\n", country_code); @@ -149,7 +153,7 @@ static int decode_registered_user_data(H2645SEI *h, GetByteContext *gb, provider_code = bytestream2_get_be16u(gb); switch (provider_code) { - case 0x31: { // atsc_provider_code + case ITU_T_T35_PROVIDER_CODE_ATSC: { uint32_t user_identifier; if (bytestream2_get_bytes_left(gb) < 4) @@ -170,7 +174,7 @@ static int decode_registered_user_data(H2645SEI *h, GetByteContext *gb, break; } #if CONFIG_HEVC_SEI - case 0x04: { // cuva_provider_code + case ITU_T_T35_PROVIDER_CODE_CUVA: { const uint16_t cuva_provider_oriented_code = 0x0005; uint16_t provider_oriented_code; @@ -186,7 +190,7 @@ static int decode_registered_user_data(H2645SEI *h, GetByteContext *gb, } break; } - case 0x3C: { // smpte_provider_code + case ITU_T_T35_PROVIDER_CODE_SMTPE: { // A/341 Amendment - 2094-40 const uint16_t smpte2094_40_provider_oriented_code = 0x0001; const uint8_t smpte2094_40_application_identifier = 0x04; @@ -207,6 +211,24 @@ static int decode_registered_user_data(H2645SEI *h, GetByteContext *gb, } break; } + case 0x5890: { // aom_provider_code + const uint16_t aom_grain_provider_oriented_code = 0x0001; + uint16_t provider_oriented_code; + + if (!IS_HEVC(codec_id)) + goto unsupported_provider_code; + + if (bytestream2_get_bytes_left(gb) < 2) + return AVERROR_INVALIDDATA; + + provider_oriented_code = bytestream2_get_byteu(gb); + if (provider_oriented_code == aom_grain_provider_oriented_code) { + return ff_aom_parse_film_grain_sets(&h->aom_film_grain, + gb->buffer, + bytestream2_get_bytes_left(gb)); + } + break; + } unsupported_provider_code: #endif default: @@ -392,6 +414,52 @@ static int decode_film_grain_characteristics(H2645SEIFilmGrainCharacteristics *h return 0; } +static int decode_nal_sei_mastering_display_info(H2645SEIMasteringDisplay *s, + GetByteContext *gb) +{ + int i; + + if (bytestream2_get_bytes_left(gb) < 24) + return AVERROR_INVALIDDATA; + + // Mastering primaries + for (i = 0; i < 3; i++) { + s->display_primaries[i][0] = bytestream2_get_be16u(gb); + s->display_primaries[i][1] = bytestream2_get_be16u(gb); + } + // White point (x, y) + s->white_point[0] = bytestream2_get_be16u(gb); + s->white_point[1] = bytestream2_get_be16u(gb); + + // Max and min luminance of mastering display + s->max_luminance = bytestream2_get_be32u(gb); + s->min_luminance = bytestream2_get_be32u(gb); + + // As this SEI message comes before the first frame that references it, + // initialize the flag to 2 and decrement on IRAP access unit so it + // persists for the coded video sequence (e.g., between two IRAPs) + s->present = 2; + + return 0; +} + +static int decode_nal_sei_content_light_info(H2645SEIContentLight *s, + GetByteContext *gb) +{ + if (bytestream2_get_bytes_left(gb) < 4) + return AVERROR_INVALIDDATA; + + // Max and average light levels + s->max_content_light_level = bytestream2_get_be16u(gb); + s->max_pic_average_light_level = bytestream2_get_be16u(gb); + // As this SEI message comes before the first frame that references it, + // initialize the flag to 2 and decrement on IRAP access unit so it + // persists for the coded video sequence (e.g., between two IRAPs) + s->present = 2; + + return 0; +} + int ff_h2645_sei_message_decode(H2645SEI *h, enum SEIType type, enum AVCodecID codec_id, GetBitContext *gb, GetByteContext *gbyte, void *logctx) @@ -412,6 +480,11 @@ int ff_h2645_sei_message_decode(H2645SEI *h, enum SEIType type, case SEI_TYPE_AMBIENT_VIEWING_ENVIRONMENT: return decode_ambient_viewing_environment(&h->ambient_viewing_environment, gbyte); + case SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME: + return decode_nal_sei_mastering_display_info(&h->mastering_display, + gbyte); + case SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO: + return decode_nal_sei_content_light_info(&h->content_light, gbyte); default: return FF_H2645_SEI_MESSAGE_UNHANDLED; } @@ -463,6 +536,7 @@ int ff_h2645_sei_to_frame(AVFrame *frame, H2645SEI *sei, int seed) { H2645SEIFramePacking *fp = &sei->frame_packing; + int ret; if (fp->present && is_frame_packing_type_valid(fp->arrangement_type, codec_id) && @@ -547,8 +621,7 @@ int ff_h2645_sei_to_frame(AVFrame *frame, H2645SEI *sei, if (!sd) av_buffer_unref(&a53->buf_ref); a53->buf_ref = NULL; - if (avctx) - avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; + avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; } for (unsigned i = 0; i < sei->unregistered.nb_buf_ref; i++) { @@ -587,35 +660,45 @@ int ff_h2645_sei_to_frame(AVFrame *frame, H2645SEI *sei, h274 = &fgp->codec.h274; fgp->seed = seed; + fgp->width = frame->width; + fgp->height = frame->height; + + /* H.274 mandates film grain be applied to 4:4:4 frames */ + fgp->subsampling_x = fgp->subsampling_y = 0; h274->model_id = fgc->model_id; if (fgc->separate_colour_description_present_flag) { - h274->bit_depth_luma = fgc->bit_depth_luma; - h274->bit_depth_chroma = fgc->bit_depth_chroma; - h274->color_range = fgc->full_range + 1; - h274->color_primaries = fgc->color_primaries; - h274->color_trc = fgc->transfer_characteristics; - h274->color_space = fgc->matrix_coeffs; + fgp->bit_depth_luma = fgc->bit_depth_luma; + fgp->bit_depth_chroma = fgc->bit_depth_chroma; + fgp->color_range = fgc->full_range + 1; + fgp->color_primaries = fgc->color_primaries; + fgp->color_trc = fgc->transfer_characteristics; + fgp->color_space = fgc->matrix_coeffs; } else { - h274->bit_depth_luma = bit_depth_luma; - h274->bit_depth_chroma = bit_depth_chroma; + fgp->bit_depth_luma = bit_depth_luma; + fgp->bit_depth_chroma = bit_depth_chroma; if (vui->video_signal_type_present_flag) - h274->color_range = vui->video_full_range_flag + 1; - else - h274->color_range = AVCOL_RANGE_UNSPECIFIED; + fgp->color_range = vui->video_full_range_flag + 1; if (vui->colour_description_present_flag) { - h274->color_primaries = vui->colour_primaries; - h274->color_trc = vui->transfer_characteristics; - h274->color_space = vui->matrix_coeffs; - } else { - h274->color_primaries = AVCOL_PRI_UNSPECIFIED; - h274->color_trc = AVCOL_TRC_UNSPECIFIED; - h274->color_space = AVCOL_SPC_UNSPECIFIED; + fgp->color_primaries = vui->colour_primaries; + fgp->color_trc = vui->transfer_characteristics; + fgp->color_space = vui->matrix_coeffs; } } h274->blending_mode_id = fgc->blending_mode_id; h274->log2_scale_factor = fgc->log2_scale_factor; +#if FF_API_H274_FILM_GRAIN_VCS +FF_DISABLE_DEPRECATION_WARNINGS + h274->bit_depth_luma = fgp->bit_depth_luma; + h274->bit_depth_chroma = fgp->bit_depth_chroma; + h274->color_range = fgp->color_range; + h274->color_primaries = fgp->color_primaries; + h274->color_trc = fgp->color_trc; + h274->color_space = fgp->color_space; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + memcpy(&h274->component_model_present, &fgc->comp_model_present_flag, sizeof(h274->component_model_present)); memcpy(&h274->num_intensity_intervals, &fgc->num_intensity_intervals, @@ -634,10 +717,15 @@ int ff_h2645_sei_to_frame(AVFrame *frame, H2645SEI *sei, else fgc->present = fgc->persistence_flag; - if (avctx) - avctx->properties |= FF_CODEC_PROPERTY_FILM_GRAIN; + avctx->properties |= FF_CODEC_PROPERTY_FILM_GRAIN; } +#if CONFIG_HEVC_SEI + ret = ff_aom_attach_film_grain_sets(&sei->aom_film_grain, frame); + if (ret < 0) + return ret; +#endif + if (sei->ambient_viewing_environment.present) { H2645SEIAmbientViewingEnvironment *env = &sei->ambient_viewing_environment; @@ -652,6 +740,71 @@ int ff_h2645_sei_to_frame(AVFrame *frame, H2645SEI *sei, dst_env->ambient_light_y = av_make_q(env->ambient_light_y, 50000); } + if (sei->mastering_display.present) { + // HEVC uses a g,b,r ordering, which we convert to a more natural r,g,b + const int mapping[3] = {2, 0, 1}; + const int chroma_den = 50000; + const int luma_den = 10000; + int i; + AVMasteringDisplayMetadata *metadata; + + ret = ff_decode_mastering_display_new(avctx, frame, &metadata); + if (ret < 0) + return ret; + + if (metadata) { + for (i = 0; i < 3; i++) { + const int j = mapping[i]; + metadata->display_primaries[i][0].num = sei->mastering_display.display_primaries[j][0]; + metadata->display_primaries[i][0].den = chroma_den; + metadata->display_primaries[i][1].num = sei->mastering_display.display_primaries[j][1]; + metadata->display_primaries[i][1].den = chroma_den; + } + metadata->white_point[0].num = sei->mastering_display.white_point[0]; + metadata->white_point[0].den = chroma_den; + metadata->white_point[1].num = sei->mastering_display.white_point[1]; + metadata->white_point[1].den = chroma_den; + + metadata->max_luminance.num = sei->mastering_display.max_luminance; + metadata->max_luminance.den = luma_den; + metadata->min_luminance.num = sei->mastering_display.min_luminance; + metadata->min_luminance.den = luma_den; + metadata->has_luminance = 1; + metadata->has_primaries = 1; + + av_log(avctx, AV_LOG_DEBUG, "Mastering Display Metadata:\n"); + av_log(avctx, AV_LOG_DEBUG, + "r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f)\n", + av_q2d(metadata->display_primaries[0][0]), + av_q2d(metadata->display_primaries[0][1]), + av_q2d(metadata->display_primaries[1][0]), + av_q2d(metadata->display_primaries[1][1]), + av_q2d(metadata->display_primaries[2][0]), + av_q2d(metadata->display_primaries[2][1]), + av_q2d(metadata->white_point[0]), av_q2d(metadata->white_point[1])); + av_log(avctx, AV_LOG_DEBUG, + "min_luminance=%f, max_luminance=%f\n", + av_q2d(metadata->min_luminance), av_q2d(metadata->max_luminance)); + } + } + + if (sei->content_light.present) { + AVContentLightMetadata *metadata; + + ret = ff_decode_content_light_new(avctx, frame, &metadata); + if (ret < 0) + return ret; + + if (metadata) { + metadata->MaxCLL = sei->content_light.max_content_light_level; + metadata->MaxFALL = sei->content_light.max_pic_average_light_level; + + av_log(avctx, AV_LOG_DEBUG, "Content Light Level Metadata:\n"); + av_log(avctx, AV_LOG_DEBUG, "MaxCLL=%d, MaxFALL=%d\n", + metadata->MaxCLL, metadata->MaxFALL); + } + } + return 0; } @@ -667,4 +820,7 @@ void ff_h2645_sei_reset(H2645SEI *s) av_buffer_unref(&s->dynamic_hdr_vivid.info); s->ambient_viewing_environment.present = 0; + s->mastering_display.present = 0; + s->content_light.present = 0; + s->aom_film_grain.enable = 0; } diff --git a/libavcodec/h2645_sei.h b/libavcodec/h2645_sei.h index e07ae103761..b9a6c7587b1 100644 --- a/libavcodec/h2645_sei.h +++ b/libavcodec/h2645_sei.h @@ -23,7 +23,9 @@ #include "libavutil/buffer.h" #include "libavutil/frame.h" +#include "libavutil/film_grain_params.h" +#include "aom_film_grain.h" #include "avcodec.h" #include "bytestream.h" #include "codec_id.h" @@ -105,6 +107,20 @@ typedef struct H2645SEIFilmGrainCharacteristics { int persistence_flag; //< HEVC only } H2645SEIFilmGrainCharacteristics; +typedef struct H2645SEIMasteringDisplay { + int present; + uint16_t display_primaries[3][2]; + uint16_t white_point[2]; + uint32_t max_luminance; + uint32_t min_luminance; +} H2645SEIMasteringDisplay; + +typedef struct H2645SEIContentLight { + int present; + uint16_t max_content_light_level; + uint16_t max_pic_average_light_level; +} H2645SEIContentLight; + typedef struct H2645SEI { H2645SEIA53Caption a53_caption; H2645SEIAFD afd; @@ -116,6 +132,9 @@ typedef struct H2645SEI { H2645SEIAlternativeTransfer alternative_transfer; H2645SEIFilmGrainCharacteristics film_grain_characteristics; H2645SEIAmbientViewingEnvironment ambient_viewing_environment; + H2645SEIMasteringDisplay mastering_display; + H2645SEIContentLight content_light; + AVFilmGrainAFGS1Params aom_film_grain; } H2645SEI; enum { diff --git a/libavcodec/h2645_vui.c b/libavcodec/h2645_vui.c index 0633fcbddd2..e5c7bf46f9b 100644 --- a/libavcodec/h2645_vui.c +++ b/libavcodec/h2645_vui.c @@ -36,21 +36,19 @@ void ff_h2645_decode_common_vui_params(GetBitContext *gb, H2645VUI *vui, void *logctx) { - int aspect_ratio_info_present_flag; - av_log(logctx, AV_LOG_DEBUG, "Decoding VUI\n"); - aspect_ratio_info_present_flag = get_bits1(gb); - if (aspect_ratio_info_present_flag) { - uint8_t aspect_ratio_idc = get_bits(gb, 8); - if (aspect_ratio_idc < FF_ARRAY_ELEMS(ff_h2645_pixel_aspect)) - vui->sar = ff_h2645_pixel_aspect[aspect_ratio_idc]; - else if (aspect_ratio_idc == EXTENDED_SAR) { + vui->aspect_ratio_info_present_flag = get_bits1(gb); + if (vui->aspect_ratio_info_present_flag) { + vui->aspect_ratio_idc = get_bits(gb, 8); + if (vui->aspect_ratio_idc < FF_ARRAY_ELEMS(ff_h2645_pixel_aspect)) + vui->sar = ff_h2645_pixel_aspect[vui->aspect_ratio_idc]; + else if (vui->aspect_ratio_idc == EXTENDED_SAR) { vui->sar.num = get_bits(gb, 16); vui->sar.den = get_bits(gb, 16); } else av_log(logctx, AV_LOG_WARNING, - "Unknown SAR index: %u.\n", aspect_ratio_idc); + "Unknown SAR index: %u.\n", vui->aspect_ratio_idc); } else vui->sar = (AVRational){ 0, 1 }; diff --git a/libavcodec/h2645_vui.h b/libavcodec/h2645_vui.h index 638da7c3667..2c839f4b015 100644 --- a/libavcodec/h2645_vui.h +++ b/libavcodec/h2645_vui.h @@ -26,6 +26,8 @@ typedef struct H2645VUI { AVRational sar; + int aspect_ratio_idc; + int aspect_ratio_info_present_flag; int overscan_info_present_flag; int overscan_appropriate_flag; diff --git a/libavcodec/h264_cavlc.c b/libavcodec/h264_cavlc.c index d061a5953bb..75e5fb74836 100644 --- a/libavcodec/h264_cavlc.c +++ b/libavcodec/h264_cavlc.c @@ -234,38 +234,6 @@ static const uint8_t run_bits[7][16]={ {7,6,5,4,3,2,1,1,1,1,1,1,1,1,1}, }; -static VLC coeff_token_vlc[4]; -static VLCElem coeff_token_vlc_tables[520+332+280+256]; -static const int coeff_token_vlc_tables_size[4]={520,332,280,256}; - -static VLC chroma_dc_coeff_token_vlc; -static VLCElem chroma_dc_coeff_token_vlc_table[256]; -static const int chroma_dc_coeff_token_vlc_table_size = 256; - -static VLC chroma422_dc_coeff_token_vlc; -static VLCElem chroma422_dc_coeff_token_vlc_table[8192]; -static const int chroma422_dc_coeff_token_vlc_table_size = 8192; - -static VLC total_zeros_vlc[15+1]; -static VLCElem total_zeros_vlc_tables[15][512]; -static const int total_zeros_vlc_tables_size = 512; - -static VLC chroma_dc_total_zeros_vlc[3+1]; -static VLCElem chroma_dc_total_zeros_vlc_tables[3][8]; -static const int chroma_dc_total_zeros_vlc_tables_size = 8; - -static VLC chroma422_dc_total_zeros_vlc[7+1]; -static VLCElem chroma422_dc_total_zeros_vlc_tables[7][32]; -static const int chroma422_dc_total_zeros_vlc_tables_size = 32; - -static VLC run_vlc[6+1]; -static VLCElem run_vlc_tables[6][8]; -static const int run_vlc_tables_size = 8; - -static VLC run7_vlc; -static VLCElem run7_vlc_table[96]; -static const int run7_vlc_table_size = 96; - #define LEVEL_TAB_BITS 8 static int8_t cavlc_level_tab[7][1<non_zero_count_cache[index8 - 1]; @@ -324,84 +314,67 @@ static av_cold void init_cavlc_level_tab(void){ av_cold void ff_h264_decode_init_vlc(void) { - int offset; - - chroma_dc_coeff_token_vlc.table = chroma_dc_coeff_token_vlc_table; - chroma_dc_coeff_token_vlc.table_allocated = chroma_dc_coeff_token_vlc_table_size; - init_vlc(&chroma_dc_coeff_token_vlc, CHROMA_DC_COEFF_TOKEN_VLC_BITS, 4*5, - &chroma_dc_coeff_token_len [0], 1, 1, - &chroma_dc_coeff_token_bits[0], 1, 1, - INIT_VLC_USE_NEW_STATIC); - - chroma422_dc_coeff_token_vlc.table = chroma422_dc_coeff_token_vlc_table; - chroma422_dc_coeff_token_vlc.table_allocated = chroma422_dc_coeff_token_vlc_table_size; - init_vlc(&chroma422_dc_coeff_token_vlc, CHROMA422_DC_COEFF_TOKEN_VLC_BITS, 4*9, - &chroma422_dc_coeff_token_len [0], 1, 1, - &chroma422_dc_coeff_token_bits[0], 1, 1, - INIT_VLC_USE_NEW_STATIC); - - offset = 0; + const VLCElem *coeff_token_vlc_original[4]; + VLCInitState state = VLC_INIT_STATE(run7_vlc_table); + + VLC_INIT_STATIC_TABLE(chroma_dc_coeff_token_vlc_table, + CHROMA_DC_COEFF_TOKEN_VLC_BITS, 4 * 5, + &chroma_dc_coeff_token_len [0], 1, 1, + &chroma_dc_coeff_token_bits[0], 1, 1, 0); + + VLC_INIT_STATIC_TABLE(chroma422_dc_coeff_token_vlc_table, + CHROMA422_DC_COEFF_TOKEN_VLC_BITS, 4 * 9, + &chroma422_dc_coeff_token_len [0], 1, 1, + &chroma422_dc_coeff_token_bits[0], 1, 1, 0); + + ff_vlc_init_tables(&state, RUN7_VLC_BITS, 16, + &run_len [6][0], 1, 1, + &run_bits[6][0], 1, 1, 0); + + for (int i = 0; i < 6; i++) { + run_vlc[i + 1] = ff_vlc_init_tables(&state, RUN_VLC_BITS, 7, + &run_len [i][0], 1, 1, + &run_bits[i][0], 1, 1, 0); + } + for (int i = 0; i < 4; i++) { - coeff_token_vlc[i].table = coeff_token_vlc_tables + offset; - coeff_token_vlc[i].table_allocated = coeff_token_vlc_tables_size[i]; - init_vlc(&coeff_token_vlc[i], COEFF_TOKEN_VLC_BITS, 4*17, - &coeff_token_len [i][0], 1, 1, - &coeff_token_bits[i][0], 1, 1, - INIT_VLC_USE_NEW_STATIC); - offset += coeff_token_vlc_tables_size[i]; + coeff_token_vlc_original[i] = + ff_vlc_init_tables(&state, COEFF_TOKEN_VLC_BITS, 4*17, + &coeff_token_len [i][0], 1, 1, + &coeff_token_bits[i][0], 1, 1, 0); + } + for (int i = 0; i < FF_ARRAY_ELEMS(coeff_token_vlc); i++) { + static const uint8_t coeff_token_table_index[17] = { + 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3 + }; + coeff_token_vlc[i] = coeff_token_vlc_original[coeff_token_table_index[i]]; } - /* - * This is a one time safety check to make sure that - * the packed static coeff_token_vlc table sizes - * were initialized correctly. - */ - av_assert0(offset == FF_ARRAY_ELEMS(coeff_token_vlc_tables)); for (int i = 0; i < 3; i++) { - chroma_dc_total_zeros_vlc[i + 1].table = chroma_dc_total_zeros_vlc_tables[i]; - chroma_dc_total_zeros_vlc[i + 1].table_allocated = chroma_dc_total_zeros_vlc_tables_size; - init_vlc(&chroma_dc_total_zeros_vlc[i + 1], - CHROMA_DC_TOTAL_ZEROS_VLC_BITS, 4, - &chroma_dc_total_zeros_len [i][0], 1, 1, - &chroma_dc_total_zeros_bits[i][0], 1, 1, - INIT_VLC_USE_NEW_STATIC); + chroma_dc_total_zeros_vlc[i + 1] = + ff_vlc_init_tables(&state, CHROMA_DC_TOTAL_ZEROS_VLC_BITS, 4, + &chroma_dc_total_zeros_len [i][0], 1, 1, + &chroma_dc_total_zeros_bits[i][0], 1, 1, 0); } for (int i = 0; i < 7; i++) { - chroma422_dc_total_zeros_vlc[i + 1].table = chroma422_dc_total_zeros_vlc_tables[i]; - chroma422_dc_total_zeros_vlc[i + 1].table_allocated = chroma422_dc_total_zeros_vlc_tables_size; - init_vlc(&chroma422_dc_total_zeros_vlc[i + 1], - CHROMA422_DC_TOTAL_ZEROS_VLC_BITS, 8, - &chroma422_dc_total_zeros_len [i][0], 1, 1, - &chroma422_dc_total_zeros_bits[i][0], 1, 1, - INIT_VLC_USE_NEW_STATIC); + chroma422_dc_total_zeros_vlc[i + 1] = + ff_vlc_init_tables(&state, CHROMA422_DC_TOTAL_ZEROS_VLC_BITS, 8, + &chroma422_dc_total_zeros_len [i][0], 1, 1, + &chroma422_dc_total_zeros_bits[i][0], 1, 1, 0); } for (int i = 0; i < 15; i++) { - total_zeros_vlc[i + 1].table = total_zeros_vlc_tables[i]; - total_zeros_vlc[i + 1].table_allocated = total_zeros_vlc_tables_size; - init_vlc(&total_zeros_vlc[i + 1], - TOTAL_ZEROS_VLC_BITS, 16, - &total_zeros_len [i][0], 1, 1, - &total_zeros_bits[i][0], 1, 1, - INIT_VLC_USE_NEW_STATIC); + total_zeros_vlc[i + 1] = + ff_vlc_init_tables(&state, TOTAL_ZEROS_VLC_BITS, 16, + &total_zeros_len [i][0], 1, 1, + &total_zeros_bits[i][0], 1, 1, 0); } - - for (int i = 0; i < 6; i++) { - run_vlc[i + 1].table = run_vlc_tables[i]; - run_vlc[i + 1].table_allocated = run_vlc_tables_size; - init_vlc(&run_vlc[i + 1], - RUN_VLC_BITS, 7, - &run_len [i][0], 1, 1, - &run_bits[i][0], 1, 1, - INIT_VLC_USE_NEW_STATIC); - } - run7_vlc.table = run7_vlc_table; - run7_vlc.table_allocated = run7_vlc_table_size; - init_vlc(&run7_vlc, RUN7_VLC_BITS, 16, - &run_len [6][0], 1, 1, - &run_bits[6][0], 1, 1, - INIT_VLC_USE_NEW_STATIC); + /* + * This is a one time safety check to make sure that + * the vlc table sizes were initialized correctly. + */ + av_assert1(state.size == 0); init_cavlc_level_tab(); } @@ -434,7 +407,6 @@ static int decode_residual(const H264Context *h, H264SliceContext *sl, const uint8_t *scantable, const uint32_t *qmul, int max_coeff) { - static const int coeff_token_table_index[17]= {0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3}; int level[16]; int zeros_left, coeff_token, total_coeff, i, trailing_ones, run_before; @@ -442,21 +414,18 @@ static int decode_residual(const H264Context *h, H264SliceContext *sl, if(max_coeff <= 8){ if (max_coeff == 4) - coeff_token = get_vlc2(gb, chroma_dc_coeff_token_vlc.table, CHROMA_DC_COEFF_TOKEN_VLC_BITS, 1); + coeff_token = get_vlc2(gb, chroma_dc_coeff_token_vlc_table, + CHROMA_DC_COEFF_TOKEN_VLC_BITS, 1); else - coeff_token = get_vlc2(gb, chroma422_dc_coeff_token_vlc.table, CHROMA422_DC_COEFF_TOKEN_VLC_BITS, 1); - total_coeff= coeff_token>>2; + coeff_token = get_vlc2(gb, chroma422_dc_coeff_token_vlc_table, + CHROMA422_DC_COEFF_TOKEN_VLC_BITS, 1); }else{ - if(n >= LUMA_DC_BLOCK_INDEX){ - total_coeff= pred_non_zero_count(h, sl, (n - LUMA_DC_BLOCK_INDEX)*16); - coeff_token= get_vlc2(gb, coeff_token_vlc[ coeff_token_table_index[total_coeff] ].table, COEFF_TOKEN_VLC_BITS, 2); - total_coeff= coeff_token>>2; - }else{ - total_coeff= pred_non_zero_count(h, sl, n); - coeff_token= get_vlc2(gb, coeff_token_vlc[ coeff_token_table_index[total_coeff] ].table, COEFF_TOKEN_VLC_BITS, 2); - total_coeff= coeff_token>>2; - } + total_coeff = pred_non_zero_count(h, sl, n >= LUMA_DC_BLOCK_INDEX ? + (n - LUMA_DC_BLOCK_INDEX) * 16 : n); + coeff_token = get_vlc2(gb, coeff_token_vlc[total_coeff], + COEFF_TOKEN_VLC_BITS, 2); } + total_coeff = coeff_token >> 2; sl->non_zero_count_cache[scan8[n]] = total_coeff; //FIXME set last_non_zero? @@ -563,13 +532,14 @@ static int decode_residual(const H264Context *h, H264SliceContext *sl, else{ if (max_coeff <= 8) { if (max_coeff == 4) - zeros_left = get_vlc2(gb, chroma_dc_total_zeros_vlc[total_coeff].table, + zeros_left = get_vlc2(gb, chroma_dc_total_zeros_vlc[total_coeff], CHROMA_DC_TOTAL_ZEROS_VLC_BITS, 1); else - zeros_left = get_vlc2(gb, chroma422_dc_total_zeros_vlc[total_coeff].table, + zeros_left = get_vlc2(gb, chroma422_dc_total_zeros_vlc[total_coeff], CHROMA422_DC_TOTAL_ZEROS_VLC_BITS, 1); } else { - zeros_left= get_vlc2(gb, total_zeros_vlc[ total_coeff ].table, TOTAL_ZEROS_VLC_BITS, 1); + zeros_left = get_vlc2(gb, total_zeros_vlc[total_coeff], + TOTAL_ZEROS_VLC_BITS, 1); } } @@ -579,9 +549,9 @@ static int decode_residual(const H264Context *h, H264SliceContext *sl, ((type*)block)[*scantable] = level[0]; \ for(i=1;i 0;i++) { \ if(zeros_left < 7) \ - run_before= get_vlc2(gb, run_vlc[zeros_left].table, RUN_VLC_BITS, 1); \ + run_before = get_vlc2(gb, run_vlc[zeros_left], RUN_VLC_BITS, 1); \ else \ - run_before= get_vlc2(gb, run7_vlc.table, RUN7_VLC_BITS, 2); \ + run_before = get_vlc2(gb, run7_vlc_table, RUN7_VLC_BITS, 2); \ zeros_left -= run_before; \ scantable -= 1 + run_before; \ ((type*)block)[*scantable]= level[i]; \ @@ -594,9 +564,9 @@ static int decode_residual(const H264Context *h, H264SliceContext *sl, ((type*)block)[*scantable] = ((int)(level[0] * qmul[*scantable] + 32))>>6; \ for(i=1;i 0;i++) { \ if(zeros_left < 7) \ - run_before= get_vlc2(gb, run_vlc[zeros_left].table, RUN_VLC_BITS, 1); \ + run_before = get_vlc2(gb, run_vlc[zeros_left], RUN_VLC_BITS, 1); \ else \ - run_before= get_vlc2(gb, run7_vlc.table, RUN7_VLC_BITS, 2); \ + run_before = get_vlc2(gb, run7_vlc_table, RUN7_VLC_BITS, 2); \ zeros_left -= run_before; \ scantable -= 1 + run_before; \ ((type*)block)[*scantable]= ((int)(level[i] * qmul[*scantable] + 32))>>6; \ diff --git a/libavcodec/h264_direct.c b/libavcodec/h264_direct.c index 014491e29be..587274aa6de 100644 --- a/libavcodec/h264_direct.c +++ b/libavcodec/h264_direct.c @@ -34,7 +34,7 @@ #include -static int get_scale_factor(H264SliceContext *sl, +static int get_scale_factor(const H264SliceContext *sl, int poc, int poc1, int i) { int poc0 = sl->ref_list[0][i].poc; @@ -83,7 +83,7 @@ static void fill_colmap(const H264Context *h, H264SliceContext *sl, int map[2][16 + 32], int list, int field, int colfield, int mbafi) { - H264Picture *const ref1 = sl->ref_list[1][0].parent; + const H264Picture *const ref1 = sl->ref_list[1][0].parent; int j, old_ref, rfield; int start = mbafi ? 16 : 0; int end = mbafi ? 16 + 2 * sl->ref_count[0] : sl->ref_count[0]; @@ -150,7 +150,7 @@ void ff_h264_direct_ref_list_init(const H264Context *const h, H264SliceContext * if (h->picture_structure == PICT_FRAME) { int cur_poc = h->cur_pic_ptr->poc; - int *col_poc = sl->ref_list[1][0].parent->field_poc; + const int *col_poc = sl->ref_list[1][0].parent->field_poc; if (col_poc[0] == INT_MAX && col_poc[1] == INT_MAX) { av_log(h->avctx, AV_LOG_ERROR, "co located POCs unavailable\n"); sl->col_parity = 1; diff --git a/libavcodec/h264_mb.c b/libavcodec/h264_mb.c index 0b317745566..4e941363139 100644 --- a/libavcodec/h264_mb.c +++ b/libavcodec/h264_mb.c @@ -34,6 +34,7 @@ #include "h264dec.h" #include "h264_ps.h" #include "qpeldsp.h" +#include "rectangle.h" #include "threadframe.h" static inline int get_lowest_part_list_y(H264SliceContext *sl, @@ -65,7 +66,7 @@ static inline void get_lowest_part_y(const H264Context *h, H264SliceContext *sl, // Error resilience puts the current picture in the ref list. // Don't try to wait on these as it will cause a deadlock. // Fields can wait on each other, though. - if (ref->parent->tf.progress->data != h->cur_pic.tf.progress->data || + if (ref->parent->tf.progress != h->cur_pic.tf.progress || (ref->reference & 3) != h->picture_structure) { my = get_lowest_part_list_y(sl, n, height, y_offset, 0); if (refs[0][ref_n] < 0) @@ -78,7 +79,7 @@ static inline void get_lowest_part_y(const H264Context *h, H264SliceContext *sl, int ref_n = sl->ref_cache[1][scan8[n]]; H264Ref *ref = &sl->ref_list[1][ref_n]; - if (ref->parent->tf.progress->data != h->cur_pic.tf.progress->data || + if (ref->parent->tf.progress != h->cur_pic.tf.progress || (ref->reference & 3) != h->picture_structure) { my = get_lowest_part_list_y(sl, n, height, y_offset, 1); if (refs[1][ref_n] < 0) diff --git a/libavcodec/h264_mvpred.h b/libavcodec/h264_mvpred.h index 46ae2738f92..bc9fef50e4e 100644 --- a/libavcodec/h264_mvpred.h +++ b/libavcodec/h264_mvpred.h @@ -30,10 +30,142 @@ #include "h264dec.h" #include "mpegutils.h" +#include "rectangle.h" + #include "libavutil/avassert.h" #include "libavutil/mem_internal.h" +/** + * Get the predicted intra4x4 prediction mode. + */ +static av_always_inline int pred_intra_mode(const H264Context *h, + H264SliceContext *sl, int n) +{ + const int index8 = scan8[n]; + const int left = sl->intra4x4_pred_mode_cache[index8 - 1]; + const int top = sl->intra4x4_pred_mode_cache[index8 - 8]; + const int min = FFMIN(left, top); + + ff_tlog(h->avctx, "mode:%d %d min:%d\n", left, top, min); + + if (min < 0) + return DC_PRED; + else + return min; +} + +static av_always_inline void write_back_intra_pred_mode(const H264Context *h, + H264SliceContext *sl) +{ + int8_t *i4x4 = sl->intra4x4_pred_mode + h->mb2br_xy[sl->mb_xy]; + int8_t *i4x4_cache = sl->intra4x4_pred_mode_cache; + + AV_COPY32(i4x4, i4x4_cache + 4 + 8 * 4); + i4x4[4] = i4x4_cache[7 + 8 * 3]; + i4x4[5] = i4x4_cache[7 + 8 * 2]; + i4x4[6] = i4x4_cache[7 + 8 * 1]; +} + +static av_always_inline void write_back_non_zero_count(const H264Context *h, + H264SliceContext *sl) +{ + const int mb_xy = sl->mb_xy; + uint8_t *nnz = h->non_zero_count[mb_xy]; + uint8_t *nnz_cache = sl->non_zero_count_cache; + + AV_COPY32(&nnz[ 0], &nnz_cache[4 + 8 * 1]); + AV_COPY32(&nnz[ 4], &nnz_cache[4 + 8 * 2]); + AV_COPY32(&nnz[ 8], &nnz_cache[4 + 8 * 3]); + AV_COPY32(&nnz[12], &nnz_cache[4 + 8 * 4]); + AV_COPY32(&nnz[16], &nnz_cache[4 + 8 * 6]); + AV_COPY32(&nnz[20], &nnz_cache[4 + 8 * 7]); + AV_COPY32(&nnz[32], &nnz_cache[4 + 8 * 11]); + AV_COPY32(&nnz[36], &nnz_cache[4 + 8 * 12]); + + if (!h->chroma_y_shift) { + AV_COPY32(&nnz[24], &nnz_cache[4 + 8 * 8]); + AV_COPY32(&nnz[28], &nnz_cache[4 + 8 * 9]); + AV_COPY32(&nnz[40], &nnz_cache[4 + 8 * 13]); + AV_COPY32(&nnz[44], &nnz_cache[4 + 8 * 14]); + } +} + +static av_always_inline void write_back_motion_list(const H264Context *h, + H264SliceContext *sl, + int b_stride, + int b_xy, int b8_xy, + int mb_type, int list) +{ + int16_t(*mv_dst)[2] = &h->cur_pic.motion_val[list][b_xy]; + int16_t(*mv_src)[2] = &sl->mv_cache[list][scan8[0]]; + AV_COPY128(mv_dst + 0 * b_stride, mv_src + 8 * 0); + AV_COPY128(mv_dst + 1 * b_stride, mv_src + 8 * 1); + AV_COPY128(mv_dst + 2 * b_stride, mv_src + 8 * 2); + AV_COPY128(mv_dst + 3 * b_stride, mv_src + 8 * 3); + if (CABAC(h)) { + uint8_t (*mvd_dst)[2] = &sl->mvd_table[list][FMO ? 8 * sl->mb_xy + : h->mb2br_xy[sl->mb_xy]]; + uint8_t(*mvd_src)[2] = &sl->mvd_cache[list][scan8[0]]; + if (IS_SKIP(mb_type)) { + AV_ZERO128(mvd_dst); + } else { + AV_COPY64(mvd_dst, mvd_src + 8 * 3); + AV_COPY16(mvd_dst + 3 + 3, mvd_src + 3 + 8 * 0); + AV_COPY16(mvd_dst + 3 + 2, mvd_src + 3 + 8 * 1); + AV_COPY16(mvd_dst + 3 + 1, mvd_src + 3 + 8 * 2); + } + } + + { + int8_t *ref_index = &h->cur_pic.ref_index[list][b8_xy]; + int8_t *ref_cache = sl->ref_cache[list]; + ref_index[0 + 0 * 2] = ref_cache[scan8[0]]; + ref_index[1 + 0 * 2] = ref_cache[scan8[4]]; + ref_index[0 + 1 * 2] = ref_cache[scan8[8]]; + ref_index[1 + 1 * 2] = ref_cache[scan8[12]]; + } +} + +static av_always_inline void write_back_motion(const H264Context *h, + H264SliceContext *sl, + int mb_type) +{ + const int b_stride = h->b_stride; + const int b_xy = 4 * sl->mb_x + 4 * sl->mb_y * h->b_stride; // try mb2b(8)_xy + const int b8_xy = 4 * sl->mb_xy; + + if (USES_LIST(mb_type, 0)) { + write_back_motion_list(h, sl, b_stride, b_xy, b8_xy, mb_type, 0); + } else { + fill_rectangle(&h->cur_pic.ref_index[0][b8_xy], + 2, 2, 2, (uint8_t)LIST_NOT_USED, 1); + } + if (USES_LIST(mb_type, 1)) + write_back_motion_list(h, sl, b_stride, b_xy, b8_xy, mb_type, 1); + + if (sl->slice_type_nos == AV_PICTURE_TYPE_B && CABAC(h)) { + if (IS_8X8(mb_type)) { + uint8_t *direct_table = &h->direct_table[4 * sl->mb_xy]; + direct_table[1] = sl->sub_mb_type[1] >> 1; + direct_table[2] = sl->sub_mb_type[2] >> 1; + direct_table[3] = sl->sub_mb_type[3] >> 1; + } + } +} + +static av_always_inline int get_dct8x8_allowed(const H264Context *h, H264SliceContext *sl) +{ + if (h->ps.sps->direct_8x8_inference_flag) + return !(AV_RN64A(sl->sub_mb_type) & + ((MB_TYPE_16x8 | MB_TYPE_8x16 | MB_TYPE_8x8) * + 0x0001000100010001ULL)); + else + return !(AV_RN64A(sl->sub_mb_type) & + ((MB_TYPE_16x8 | MB_TYPE_8x16 | MB_TYPE_8x8 | MB_TYPE_DIRECT2) * + 0x0001000100010001ULL)); +} + static av_always_inline int fetch_diagonal_mv(const H264Context *h, H264SliceContext *sl, const int16_t **C, int i, int list, int part_width) diff --git a/libavcodec/h264_parse.c b/libavcodec/h264_parse.c index 97ddf0e437b..3378650fd59 100644 --- a/libavcodec/h264_parse.c +++ b/libavcodec/h264_parse.c @@ -526,22 +526,22 @@ int ff_h264_decode_extradata(const uint8_t *data, int size, H264ParamSets *ps, * * @param sps SPS * - * @return profile as defined by FF_PROFILE_H264_* + * @return profile as defined by AV_PROFILE_H264_* */ int ff_h264_get_profile(const SPS *sps) { int profile = sps->profile_idc; switch (sps->profile_idc) { - case FF_PROFILE_H264_BASELINE: + case AV_PROFILE_H264_BASELINE: // constraint_set1_flag set to 1 - profile |= (sps->constraint_set_flags & 1 << 1) ? FF_PROFILE_H264_CONSTRAINED : 0; + profile |= (sps->constraint_set_flags & 1 << 1) ? AV_PROFILE_H264_CONSTRAINED : 0; break; - case FF_PROFILE_H264_HIGH_10: - case FF_PROFILE_H264_HIGH_422: - case FF_PROFILE_H264_HIGH_444_PREDICTIVE: + case AV_PROFILE_H264_HIGH_10: + case AV_PROFILE_H264_HIGH_422: + case AV_PROFILE_H264_HIGH_444_PREDICTIVE: // constraint_set3_flag set to 1 - profile |= (sps->constraint_set_flags & 1 << 3) ? FF_PROFILE_H264_INTRA : 0; + profile |= (sps->constraint_set_flags & 1 << 3) ? AV_PROFILE_H264_INTRA : 0; break; } diff --git a/libavcodec/h264_parser.c b/libavcodec/h264_parser.c index fae76992cd5..94cfbc481ea 100644 --- a/libavcodec/h264_parser.c +++ b/libavcodec/h264_parser.c @@ -47,6 +47,7 @@ #include "h264data.h" #include "mpegutils.h" #include "parser.h" +#include "refstruct.h" #include "startcode.h" typedef struct H264ParseContext { @@ -373,13 +374,7 @@ static inline int parse_nal_units(AVCodecParserContext *s, goto fail; } - av_buffer_unref(&p->ps.pps_ref); - p->ps.pps = NULL; - p->ps.sps = NULL; - p->ps.pps_ref = av_buffer_ref(p->ps.pps_list[pps_id]); - if (!p->ps.pps_ref) - goto fail; - p->ps.pps = (const PPS*)p->ps.pps_ref->data; + ff_refstruct_replace(&p->ps.pps, p->ps.pps_list[pps_id]); p->ps.sps = p->ps.pps->sps; sps = p->ps.sps; @@ -568,7 +563,7 @@ static inline int parse_nal_units(AVCodecParserContext *s, if (p->sei.common.unregistered.x264_build < 44U) den *= 2; av_reduce(&avctx->framerate.den, &avctx->framerate.num, - sps->num_units_in_tick * avctx->ticks_per_frame, den, 1 << 30); + sps->num_units_in_tick * 2, den, 1 << 30); } av_freep(&rbsp.rbsp_buffer); @@ -625,7 +620,7 @@ static int h264_parse(AVCodecParserContext *s, parse_nal_units(s, avctx, buf, buf_size); if (avctx->framerate.num) - time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){avctx->ticks_per_frame, 1})); + time_base = av_inv_q(av_mul_q(avctx->framerate, (AVRational){2, 1})); if (p->sei.picture_timing.cpb_removal_delay >= 0) { s->dts_sync_point = p->sei.buffering_period.present; s->dts_ref_dts_delta = p->sei.picture_timing.cpb_removal_delay; diff --git a/libavcodec/h264_picture.c b/libavcodec/h264_picture.c index 2661ff4698e..3234141dbd6 100644 --- a/libavcodec/h264_picture.c +++ b/libavcodec/h264_picture.c @@ -26,14 +26,17 @@ */ #include "libavutil/avassert.h" +#include "libavutil/emms.h" #include "error_resilience.h" #include "avcodec.h" #include "h264dec.h" +#include "hwaccel_internal.h" #include "mpegutils.h" +#include "refstruct.h" #include "thread.h" #include "threadframe.h" -void ff_h264_unref_picture(H264Context *h, H264Picture *pic) +void ff_h264_unref_picture(H264Picture *pic) { int off = offsetof(H264Picture, f_grain) + sizeof(pic->f_grain); int i; @@ -41,31 +44,43 @@ void ff_h264_unref_picture(H264Context *h, H264Picture *pic) if (!pic->f || !pic->f->buf[0]) return; - ff_thread_release_ext_buffer(h->avctx, &pic->tf); - ff_thread_release_buffer(h->avctx, pic->f_grain); - av_buffer_unref(&pic->hwaccel_priv_buf); + ff_thread_release_ext_buffer(&pic->tf); + av_frame_unref(pic->f_grain); + ff_refstruct_unref(&pic->hwaccel_picture_private); - av_buffer_unref(&pic->qscale_table_buf); - av_buffer_unref(&pic->mb_type_buf); - av_buffer_unref(&pic->pps_buf); + ff_refstruct_unref(&pic->qscale_table_base); + ff_refstruct_unref(&pic->mb_type_base); + ff_refstruct_unref(&pic->pps); for (i = 0; i < 2; i++) { - av_buffer_unref(&pic->motion_val_buf[i]); - av_buffer_unref(&pic->ref_index_buf[i]); + ff_refstruct_unref(&pic->motion_val_base[i]); + ff_refstruct_unref(&pic->ref_index[i]); } + ff_refstruct_unref(&pic->decode_error_flags); memset((uint8_t*)pic + off, 0, sizeof(*pic) - off); } static void h264_copy_picture_params(H264Picture *dst, const H264Picture *src) { + ff_refstruct_replace(&dst->qscale_table_base, src->qscale_table_base); + ff_refstruct_replace(&dst->mb_type_base, src->mb_type_base); + ff_refstruct_replace(&dst->pps, src->pps); + + for (int i = 0; i < 2; i++) { + ff_refstruct_replace(&dst->motion_val_base[i], src->motion_val_base[i]); + ff_refstruct_replace(&dst->ref_index[i], src->ref_index[i]); + } + + ff_refstruct_replace(&dst->hwaccel_picture_private, + src->hwaccel_picture_private); + + ff_refstruct_replace(&dst->decode_error_flags, src->decode_error_flags); + dst->qscale_table = src->qscale_table; dst->mb_type = src->mb_type; - dst->pps = src->pps; - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 2; i++) dst->motion_val[i] = src->motion_val[i]; - dst->ref_index[i] = src->ref_index[i]; - } for (int i = 0; i < 2; i++) dst->field_poc[i] = src->field_poc[i]; @@ -81,6 +96,7 @@ static void h264_copy_picture_params(H264Picture *dst, const H264Picture *src) dst->field_picture = src->field_picture; dst->reference = src->reference; dst->recovered = src->recovered; + dst->gray = src->gray; dst->invalid_gap = src->invalid_gap; dst->sei_recovery_frame_cnt = src->sei_recovery_frame_cnt; dst->mb_width = src->mb_width; @@ -89,9 +105,9 @@ static void h264_copy_picture_params(H264Picture *dst, const H264Picture *src) dst->needs_fg = src->needs_fg; } -int ff_h264_ref_picture(H264Context *h, H264Picture *dst, H264Picture *src) +int ff_h264_ref_picture(H264Picture *dst, const H264Picture *src) { - int ret, i; + int ret; av_assert0(!dst->f->buf[0]); av_assert0(src->f->buf[0]); @@ -108,92 +124,46 @@ int ff_h264_ref_picture(H264Context *h, H264Picture *dst, H264Picture *src) goto fail; } - dst->qscale_table_buf = av_buffer_ref(src->qscale_table_buf); - dst->mb_type_buf = av_buffer_ref(src->mb_type_buf); - dst->pps_buf = av_buffer_ref(src->pps_buf); - if (!dst->qscale_table_buf || !dst->mb_type_buf || !dst->pps_buf) { - ret = AVERROR(ENOMEM); - goto fail; - } - - for (i = 0; i < 2; i++) { - dst->motion_val_buf[i] = av_buffer_ref(src->motion_val_buf[i]); - dst->ref_index_buf[i] = av_buffer_ref(src->ref_index_buf[i]); - if (!dst->motion_val_buf[i] || !dst->ref_index_buf[i]) { - ret = AVERROR(ENOMEM); - goto fail; - } - } - - if (src->hwaccel_picture_private) { - dst->hwaccel_priv_buf = av_buffer_ref(src->hwaccel_priv_buf); - if (!dst->hwaccel_priv_buf) { - ret = AVERROR(ENOMEM); - goto fail; - } - dst->hwaccel_picture_private = dst->hwaccel_priv_buf->data; - } - h264_copy_picture_params(dst, src); return 0; fail: - ff_h264_unref_picture(h, dst); + ff_h264_unref_picture(dst); return ret; } -int ff_h264_replace_picture(H264Context *h, H264Picture *dst, const H264Picture *src) +int ff_h264_replace_picture(H264Picture *dst, const H264Picture *src) { - int ret, i; + int ret; if (!src->f || !src->f->buf[0]) { - ff_h264_unref_picture(h, dst); + ff_h264_unref_picture(dst); return 0; } av_assert0(src->tf.f == src->f); dst->tf.f = dst->f; - ff_thread_release_ext_buffer(h->avctx, &dst->tf); - ret = ff_thread_ref_frame(&dst->tf, &src->tf); + ret = ff_thread_replace_frame(&dst->tf, &src->tf); if (ret < 0) goto fail; if (src->needs_fg) { - ff_thread_release_buffer(h->avctx, dst->f_grain); + av_frame_unref(dst->f_grain); ret = av_frame_ref(dst->f_grain, src->f_grain); if (ret < 0) goto fail; } - ret = av_buffer_replace(&dst->qscale_table_buf, src->qscale_table_buf); - ret |= av_buffer_replace(&dst->mb_type_buf, src->mb_type_buf); - ret |= av_buffer_replace(&dst->pps_buf, src->pps_buf); - if (ret < 0) - goto fail; - - for (i = 0; i < 2; i++) { - ret = av_buffer_replace(&dst->motion_val_buf[i], src->motion_val_buf[i]); - ret |= av_buffer_replace(&dst->ref_index_buf[i], src->ref_index_buf[i]); - if (ret < 0) - goto fail; - } - - ret = av_buffer_replace(&dst->hwaccel_priv_buf, src->hwaccel_priv_buf); - if (ret < 0) - goto fail; - - dst->hwaccel_picture_private = src->hwaccel_picture_private; - h264_copy_picture_params(dst, src); return 0; fail: - ff_h264_unref_picture(h, dst); + ff_h264_unref_picture(dst); return ret; } -void ff_h264_set_erpic(ERPicture *dst, H264Picture *src) +void ff_h264_set_erpic(ERPicture *dst, const H264Picture *src) { #if CONFIG_ERROR_RESILIENCE int i; @@ -234,12 +204,12 @@ int ff_h264_field_end(H264Context *h, H264SliceContext *sl, int in_setup) } if (avctx->hwaccel) { - err = avctx->hwaccel->end_frame(avctx); + err = FF_HW_SIMPLE_CALL(avctx, end_frame); if (err < 0) av_log(avctx, AV_LOG_ERROR, "hardware accelerator failed to decode picture\n"); } else if (!in_setup && cur->needs_fg && (!FIELD_PICTURE(h) || !h->first_field)) { - AVFrameSideData *sd = av_frame_get_side_data(cur->f, AV_FRAME_DATA_FILM_GRAIN_PARAMS); + const AVFrameSideData *sd = av_frame_get_side_data(cur->f, AV_FRAME_DATA_FILM_GRAIN_PARAMS); err = AVERROR_INVALIDDATA; if (sd) // a decoding error may have happened before the side data could be allocated diff --git a/libavcodec/h264_ps.c b/libavcodec/h264_ps.c index d0d1e659039..3c8fc33c7f6 100644 --- a/libavcodec/h264_ps.c +++ b/libavcodec/h264_ps.c @@ -34,6 +34,7 @@ #include "h2645_vui.h" #include "h264_ps.h" #include "golomb.h" +#include "refstruct.h" #define MIN_LOG2_MAX_FRAME_NUM 4 @@ -85,7 +86,7 @@ static const int level_max_dpb_mbs[][2] = { static void remove_pps(H264ParamSets *s, int id) { - av_buffer_unref(&s->pps_list[id]); + ff_refstruct_unref(&s->pps_list[id]); } static void remove_sps(H264ParamSets *s, int id) @@ -95,11 +96,11 @@ static void remove_sps(H264ParamSets *s, int id) if (s->sps_list[id]) { /* drop all PPS that depend on this SPS */ for (i = 0; i < FF_ARRAY_ELEMS(s->pps_list); i++) - if (s->pps_list[i] && ((PPS*)s->pps_list[i]->data)->sps_id == id) + if (s->pps_list[i] && s->pps_list[i]->sps_id == id) remove_pps(s, i); } #endif - av_buffer_unref(&s->sps_list[id]); + ff_refstruct_unref(&s->sps_list[id]); } static inline int decode_hrd_parameters(GetBitContext *gb, void *logctx, @@ -113,12 +114,13 @@ static inline int decode_hrd_parameters(GetBitContext *gb, void *logctx, return AVERROR_INVALIDDATA; } - get_bits(gb, 4); /* bit_rate_scale */ + sps->cpr_flag = 0x0; + sps->bit_rate_scale = get_bits(gb, 4); get_bits(gb, 4); /* cpb_size_scale */ for (i = 0; i < cpb_count; i++) { - get_ue_golomb_long(gb); /* bit_rate_value_minus1 */ - get_ue_golomb_long(gb); /* cpb_size_value_minus1 */ - get_bits1(gb); /* cbr_flag */ + sps->bit_rate_value[i] = get_ue_golomb_long(gb) + 1; /* bit_rate_value_minus1 + 1 */ + sps->cpb_size_value[i] = get_ue_golomb_long(gb) + 1; /* cpb_size_value_minus1 + 1 */ + sps->cpr_flag |= get_bits1(gb) << i; } sps->initial_cpb_removal_delay_length = get_bits(gb, 5) + 1; sps->cpb_removal_delay_length = get_bits(gb, 5) + 1; @@ -176,7 +178,7 @@ static inline int decode_vui_parameters(GetBitContext *gb, void *logctx, get_ue_golomb_31(gb); /* log2_max_mv_length_horizontal */ get_ue_golomb_31(gb); /* log2_max_mv_length_vertical */ sps->num_reorder_frames = get_ue_golomb_31(gb); - get_ue_golomb_31(gb); /*max_dec_frame_buffering*/ + sps->max_dec_frame_buffering = get_ue_golomb_31(gb); if (get_bits_left(gb) < 0) { sps->num_reorder_frames = 0; @@ -197,12 +199,14 @@ static inline int decode_vui_parameters(GetBitContext *gb, void *logctx, } static int decode_scaling_list(GetBitContext *gb, uint8_t *factors, int size, - const uint8_t *jvt_list, - const uint8_t *fallback_list) + const uint8_t *jvt_list, const uint8_t *fallback_list, + uint16_t *mask, int pos) { int i, last = 8, next = 8; const uint8_t *scan = size == 16 ? ff_zigzag_scan : ff_zigzag_direct; - if (!get_bits1(gb)) /* matrix not written, we use the predicted one */ + uint16_t seq_scaling_list_present_flag = get_bits1(gb); + *mask |= (seq_scaling_list_present_flag << pos); + if (!seq_scaling_list_present_flag) /* matrix not written, we use the predicted one */ memcpy(factors, fallback_list, size * sizeof(uint8_t)); else for (i = 0; i < size; i++) { @@ -226,6 +230,7 @@ static int decode_scaling_list(GetBitContext *gb, uint8_t *factors, int size, /* returns non zero if the provided SPS scaling matrix has been filled */ static int decode_scaling_matrices(GetBitContext *gb, const SPS *sps, const PPS *pps, int is_sps, + int present_flag, uint16_t *mask, uint8_t(*scaling_matrix4)[16], uint8_t(*scaling_matrix8)[64]) { @@ -237,21 +242,22 @@ static int decode_scaling_matrices(GetBitContext *gb, const SPS *sps, fallback_sps ? sps->scaling_matrix8[3] : default_scaling8[1] }; int ret = 0; - if (get_bits1(gb)) { - ret |= decode_scaling_list(gb, scaling_matrix4[0], 16, default_scaling4[0], fallback[0]); // Intra, Y - ret |= decode_scaling_list(gb, scaling_matrix4[1], 16, default_scaling4[0], scaling_matrix4[0]); // Intra, Cr - ret |= decode_scaling_list(gb, scaling_matrix4[2], 16, default_scaling4[0], scaling_matrix4[1]); // Intra, Cb - ret |= decode_scaling_list(gb, scaling_matrix4[3], 16, default_scaling4[1], fallback[1]); // Inter, Y - ret |= decode_scaling_list(gb, scaling_matrix4[4], 16, default_scaling4[1], scaling_matrix4[3]); // Inter, Cr - ret |= decode_scaling_list(gb, scaling_matrix4[5], 16, default_scaling4[1], scaling_matrix4[4]); // Inter, Cb + *mask = 0x0; + if (present_flag) { + ret |= decode_scaling_list(gb, scaling_matrix4[0], 16, default_scaling4[0], fallback[0], mask, 0); // Intra, Y + ret |= decode_scaling_list(gb, scaling_matrix4[1], 16, default_scaling4[0], scaling_matrix4[0], mask, 1); // Intra, Cr + ret |= decode_scaling_list(gb, scaling_matrix4[2], 16, default_scaling4[0], scaling_matrix4[1], mask, 2); // Intra, Cb + ret |= decode_scaling_list(gb, scaling_matrix4[3], 16, default_scaling4[1], fallback[1], mask, 3); // Inter, Y + ret |= decode_scaling_list(gb, scaling_matrix4[4], 16, default_scaling4[1], scaling_matrix4[3], mask, 4); // Inter, Cr + ret |= decode_scaling_list(gb, scaling_matrix4[5], 16, default_scaling4[1], scaling_matrix4[4], mask, 5); // Inter, Cb if (is_sps || pps->transform_8x8_mode) { - ret |= decode_scaling_list(gb, scaling_matrix8[0], 64, default_scaling8[0], fallback[2]); // Intra, Y - ret |= decode_scaling_list(gb, scaling_matrix8[3], 64, default_scaling8[1], fallback[3]); // Inter, Y + ret |= decode_scaling_list(gb, scaling_matrix8[0], 64, default_scaling8[0], fallback[2], mask, 6); // Intra, Y + ret |= decode_scaling_list(gb, scaling_matrix8[3], 64, default_scaling8[1], fallback[3], mask, 7); // Inter, Y if (sps->chroma_format_idc == 3) { - ret |= decode_scaling_list(gb, scaling_matrix8[1], 64, default_scaling8[0], scaling_matrix8[0]); // Intra, Cr - ret |= decode_scaling_list(gb, scaling_matrix8[4], 64, default_scaling8[1], scaling_matrix8[3]); // Inter, Cr - ret |= decode_scaling_list(gb, scaling_matrix8[2], 64, default_scaling8[0], scaling_matrix8[1]); // Intra, Cb - ret |= decode_scaling_list(gb, scaling_matrix8[5], 64, default_scaling8[1], scaling_matrix8[4]); // Inter, Cb + ret |= decode_scaling_list(gb, scaling_matrix8[1], 64, default_scaling8[0], scaling_matrix8[0], mask, 8); // Intra, Cr + ret |= decode_scaling_list(gb, scaling_matrix8[4], 64, default_scaling8[1], scaling_matrix8[3], mask, 9); // Inter, Cr + ret |= decode_scaling_list(gb, scaling_matrix8[2], 64, default_scaling8[0], scaling_matrix8[1], mask, 10); // Intra, Cb + ret |= decode_scaling_list(gb, scaling_matrix8[5], 64, default_scaling8[1], scaling_matrix8[4], mask, 11); // Inter, Cb } } if (!ret) @@ -266,31 +272,27 @@ void ff_h264_ps_uninit(H264ParamSets *ps) int i; for (i = 0; i < MAX_SPS_COUNT; i++) - av_buffer_unref(&ps->sps_list[i]); + ff_refstruct_unref(&ps->sps_list[i]); for (i = 0; i < MAX_PPS_COUNT; i++) - av_buffer_unref(&ps->pps_list[i]); + ff_refstruct_unref(&ps->pps_list[i]); - av_buffer_unref(&ps->pps_ref); - - ps->pps = NULL; + ff_refstruct_unref(&ps->pps); ps->sps = NULL; } int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, H264ParamSets *ps, int ignore_truncation) { - AVBufferRef *sps_buf; int profile_idc, level_idc, constraint_set_flags = 0; unsigned int sps_id; int i, log2_max_frame_num_minus4; SPS *sps; int ret; - sps_buf = av_buffer_allocz(sizeof(*sps)); - if (!sps_buf) + sps = ff_refstruct_allocz(sizeof(*sps)); + if (!sps) return AVERROR(ENOMEM); - sps = (SPS*)sps_buf->data; sps->data_size = gb->buffer_end - gb->buffer; if (sps->data_size > sizeof(sps->data)) { @@ -368,7 +370,8 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, goto fail; } sps->transform_bypass = get_bits1(gb); - ret = decode_scaling_matrices(gb, sps, NULL, 1, + ret = decode_scaling_matrices(gb, sps, NULL, 1, get_bits1(gb), + &sps->scaling_matrix_present_mask, sps->scaling_matrix4, sps->scaling_matrix8); if (ret < 0) goto fail; @@ -519,7 +522,7 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, sps->vui_parameters_present_flag = get_bits1(gb); if (sps->vui_parameters_present_flag) { - int ret = decode_vui_parameters(gb, avctx, sps); + ret = decode_vui_parameters(gb, avctx, sps); if (ret < 0) goto fail; } @@ -574,17 +577,17 @@ int ff_h264_decode_seq_parameter_set(GetBitContext *gb, AVCodecContext *avctx, * original one. * otherwise drop all PPSes that depend on it */ if (ps->sps_list[sps_id] && - !memcmp(ps->sps_list[sps_id]->data, sps_buf->data, sps_buf->size)) { - av_buffer_unref(&sps_buf); + !memcmp(ps->sps_list[sps_id], sps, sizeof(*sps))) { + ff_refstruct_unref(&sps); } else { remove_sps(ps, sps_id); - ps->sps_list[sps_id] = sps_buf; + ps->sps_list[sps_id] = sps; } return 0; fail: - av_buffer_unref(&sps_buf); + ff_refstruct_unref(&sps); return AVERROR_INVALIDDATA; } @@ -683,19 +686,16 @@ static int more_rbsp_data_in_pps(const SPS *sps, void *logctx) return 1; } -static void pps_free(void *opaque, uint8_t *data) +static void pps_free(FFRefStructOpaque unused, void *obj) { - PPS *pps = (PPS*)data; - - av_buffer_unref(&pps->sps_ref); + PPS *pps = obj; - av_freep(&data); + ff_refstruct_unref(&pps->sps); } int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avctx, H264ParamSets *ps, int bit_length) { - AVBufferRef *pps_buf; const SPS *sps; unsigned int pps_id = get_ue_golomb(gb); PPS *pps; @@ -708,15 +708,9 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct return AVERROR_INVALIDDATA; } - pps = av_mallocz(sizeof(*pps)); + pps = ff_refstruct_alloc_ext(sizeof(*pps), 0, NULL, pps_free); if (!pps) return AVERROR(ENOMEM); - pps_buf = av_buffer_create((uint8_t*)pps, sizeof(*pps), - pps_free, NULL, 0); - if (!pps_buf) { - av_freep(&pps); - return AVERROR(ENOMEM); - } pps->data_size = gb->buffer_end - gb->buffer; if (pps->data_size > sizeof(pps->data)) { @@ -731,6 +725,7 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct if (!(bit_length & 7) && pps->data_size < sizeof(pps->data)) pps->data[pps->data_size++] = 0x80; + pps->pps_id = pps_id; pps->sps_id = get_ue_golomb_31(gb); if ((unsigned)pps->sps_id >= MAX_SPS_COUNT || !ps->sps_list[pps->sps_id]) { @@ -738,12 +733,7 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct ret = AVERROR_INVALIDDATA; goto fail; } - pps->sps_ref = av_buffer_ref(ps->sps_list[pps->sps_id]); - if (!pps->sps_ref) { - ret = AVERROR(ENOMEM); - goto fail; - } - pps->sps = (const SPS*)pps->sps_ref->data; + pps->sps = ff_refstruct_ref_c(ps->sps_list[pps->sps_id]); sps = pps->sps; if (sps->bit_depth_luma > 14) { @@ -802,7 +792,10 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct bits_left = bit_length - get_bits_count(gb); if (bits_left > 0 && more_rbsp_data_in_pps(sps, avctx)) { pps->transform_8x8_mode = get_bits1(gb); + pps->pic_scaling_matrix_present_flag = get_bits1(gb); ret = decode_scaling_matrices(gb, sps, pps, 0, + pps->pic_scaling_matrix_present_flag, + &pps->pic_scaling_matrix_present_mask, pps->scaling_matrix4, pps->scaling_matrix8); if (ret < 0) goto fail; @@ -842,11 +835,11 @@ int ff_h264_decode_picture_parameter_set(GetBitContext *gb, AVCodecContext *avct } remove_pps(ps, pps_id); - ps->pps_list[pps_id] = pps_buf; + ps->pps_list[pps_id] = pps; return 0; fail: - av_buffer_unref(&pps_buf); + ff_refstruct_unref(&pps); return ret; } diff --git a/libavcodec/h264_ps.h b/libavcodec/h264_ps.h index 5c35761fbc8..80af4832fe0 100644 --- a/libavcodec/h264_ps.h +++ b/libavcodec/h264_ps.h @@ -26,7 +26,6 @@ #include -#include "libavutil/buffer.h" #include "libavutil/pixfmt.h" #include "libavutil/rational.h" @@ -80,7 +79,9 @@ typedef struct SPS { int32_t offset_for_ref_frame[256]; int bitstream_restriction_flag; int num_reorder_frames; + int max_dec_frame_buffering; int scaling_matrix_present; + uint16_t scaling_matrix_present_mask; uint8_t scaling_matrix4[6][16]; uint8_t scaling_matrix8[6][64]; int nal_hrd_parameters_present_flag; @@ -88,6 +89,10 @@ typedef struct SPS { int pic_struct_present_flag; int time_offset_length; int cpb_cnt; ///< See H.264 E.1.2 + int bit_rate_scale; + uint32_t bit_rate_value[32]; ///< bit_rate_value_minus1 + 1 + uint32_t cpb_size_value[32]; ///< cpb_size_value_minus1 + 1 + uint32_t cpr_flag; int initial_cpb_removal_delay_length; ///< initial_cpb_removal_delay_length_minus1 + 1 int cpb_removal_delay_length; ///< cpb_removal_delay_length_minus1 + 1 int dpb_output_delay_length; ///< dpb_output_delay_length_minus1 + 1 @@ -103,9 +108,10 @@ typedef struct SPS { * Picture parameter set */ typedef struct PPS { + unsigned int pps_id; unsigned int sps_id; int cabac; ///< entropy_coding_mode_flag - int pic_order_present; ///< pic_order_present_flag + int pic_order_present; ///< bottom_field_pic_order_in_frame_present_flag int slice_group_count; ///< num_slice_groups_minus1 + 1 int mb_slice_group_map_type; unsigned int ref_count[2]; ///< num_ref_idx_l0/1_active_minus1 + 1 @@ -118,6 +124,8 @@ typedef struct PPS { int constrained_intra_pred; ///< constrained_intra_pred_flag int redundant_pic_cnt_present; ///< redundant_pic_cnt_present_flag int transform_8x8_mode; ///< transform_8x8_mode_flag + int pic_scaling_matrix_present_flag; + uint16_t pic_scaling_matrix_present_mask; uint8_t scaling_matrix4[6][16]; uint8_t scaling_matrix8[6][64]; uint8_t chroma_qp_table[2][QP_MAX_NUM+1]; ///< pre-scaled (with chroma_qp_index_offset) version of qp_table @@ -130,18 +138,16 @@ typedef struct PPS { uint32_t(*dequant4_coeff[6])[16]; uint32_t(*dequant8_coeff[6])[64]; - AVBufferRef *sps_ref; - const SPS *sps; + const SPS *sps; ///< RefStruct reference } PPS; typedef struct H264ParamSets { - AVBufferRef *sps_list[MAX_SPS_COUNT]; - AVBufferRef *pps_list[MAX_PPS_COUNT]; + const SPS *sps_list[MAX_SPS_COUNT]; ///< RefStruct references + const PPS *pps_list[MAX_PPS_COUNT]; ///< RefStruct references - AVBufferRef *pps_ref; /* currently active parameters sets */ - const PPS *pps; - const SPS *sps; + const PPS *pps; ///< RefStruct reference + const SPS *sps; ///< ordinary pointer, no RefStruct reference int overread_warning_printed[2]; } H264ParamSets; diff --git a/libavcodec/h264_refs.c b/libavcodec/h264_refs.c index 50bbe949176..9bc7b20988f 100644 --- a/libavcodec/h264_refs.c +++ b/libavcodec/h264_refs.c @@ -48,7 +48,7 @@ static void pic_as_field(H264Ref *pic, const int parity) pic->poc = pic->parent->field_poc[parity == PICT_BOTTOM_FIELD]; } -static void ref_from_h264pic(H264Ref *dst, H264Picture *src) +static void ref_from_h264pic(H264Ref *dst, const H264Picture *src) { memcpy(dst->data, src->f->data, sizeof(dst->data)); memcpy(dst->linesize, src->f->linesize, sizeof(dst->linesize)); @@ -58,7 +58,8 @@ static void ref_from_h264pic(H264Ref *dst, H264Picture *src) dst->parent = src; } -static int split_field_copy(H264Ref *dest, H264Picture *src, int parity, int id_add) +static int split_field_copy(H264Ref *dest, const H264Picture *src, + int parity, int id_add) { int match = !!(src->reference & parity); @@ -275,7 +276,7 @@ static void h264_fill_mbaff_ref_list(H264SliceContext *sl) int list, i, j; for (list = 0; list < sl->list_count; list++) { for (i = 0; i < sl->ref_count[list]; i++) { - H264Ref *frame = &sl->ref_list[list][i]; + const H264Ref *frame = &sl->ref_list[list][i]; H264Ref *field = &sl->ref_list[list][16 + 2 * i]; field[0] = *frame; @@ -409,6 +410,17 @@ int ff_h264_build_ref_list(H264Context *h, H264SliceContext *sl) else return -1; } + if (h->noref_gray>0 && sl->ref_list[list][index].parent->gray && h->non_gray) { + for (int j=0; jlist_count; j++) { + int list2 = (list+j)&1; + if (h->default_ref[list2].parent && !h->default_ref[list2].parent->gray + && !(!FIELD_PICTURE(h) && (h->default_ref[list2].reference&3) != 3)) { + sl->ref_list[list][index] = h->default_ref[list2]; + av_log(h, AV_LOG_DEBUG, "replacement of gray gap frame\n"); + break; + } + } + } av_assert0(av_buffer_get_ref_count(sl->ref_list[list][index].parent->f->buf[0]) > 0); } } @@ -571,8 +583,8 @@ void ff_h264_remove_all_refs(H264Context *h) assert(h->long_ref_count == 0); if (h->short_ref_count && !h->last_pic_for_ec.f->data[0]) { - ff_h264_unref_picture(h, &h->last_pic_for_ec); - ff_h264_ref_picture(h, &h->last_pic_for_ec, h->short_ref[0]); + ff_h264_unref_picture(&h->last_pic_for_ec); + ff_h264_ref_picture(&h->last_pic_for_ec, h->short_ref[0]); } for (i = 0; i < h->short_ref_count; i++) { @@ -807,7 +819,7 @@ int ff_h264_execute_ref_pic_marking(H264Context *h) for (i = 0; i < FF_ARRAY_ELEMS(h->ps.pps_list); i++) { if (h->ps.pps_list[i]) { - const PPS *pps = (const PPS *)h->ps.pps_list[i]->data; + const PPS *pps = h->ps.pps_list[i]; pps_ref_count[0] = FFMAX(pps_ref_count[0], pps->ref_count[0]); pps_ref_count[1] = FFMAX(pps_ref_count[1], pps->ref_count[1]); } @@ -821,9 +833,9 @@ int ff_h264_execute_ref_pic_marking(H264Context *h) || pps_ref_count[0] <= 1 + (h->picture_structure != PICT_FRAME) && pps_ref_count[1] <= 1) && pps_ref_count[0]<=2 + (h->picture_structure != PICT_FRAME) + (2*!h->has_recovery_point) && h->cur_pic_ptr->f->pict_type == AV_PICTURE_TYPE_I){ - h->cur_pic_ptr->recovered |= 1; + h->cur_pic_ptr->recovered |= FRAME_RECOVERED_HEURISTIC; if(!h->avctx->has_b_frames) - h->frame_recovered |= FRAME_RECOVERED_SEI; + h->frame_recovered |= FRAME_RECOVERED_HEURISTIC; } out: diff --git a/libavcodec/h264_sei.c b/libavcodec/h264_sei.c index 668204959f6..8d6dc779431 100644 --- a/libavcodec/h264_sei.c +++ b/libavcodec/h264_sei.c @@ -179,7 +179,7 @@ static int decode_buffering_period(H264SEIBufferingPeriod *h, GetBitContext *gb, "non-existing SPS %d referenced in buffering period\n", sps_id); return sps_id > 31 ? AVERROR_INVALIDDATA : AVERROR_PS_NOT_FOUND; } - sps = (const SPS*)ps->sps_list[sps_id]->data; + sps = ps->sps_list[sps_id]; // NOTE: This is really so duplicated in the standard... See H.264, D.1.1 if (sps->nal_hrd_parameters_present_flag) { diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c index 7767e16cf15..752735cc549 100644 --- a/libavcodec/h264_slice.c +++ b/libavcodec/h264_slice.c @@ -28,14 +28,11 @@ #include "config_components.h" #include "libavutil/avassert.h" -#include "libavutil/display.h" -#include "libavutil/film_grain_params.h" #include "libavutil/pixdesc.h" #include "libavutil/timecode.h" -#include "internal.h" +#include "decode.h" #include "cabac.h" #include "cabac_functions.h" -#include "decode.h" #include "error_resilience.h" #include "avcodec.h" #include "h264.h" @@ -47,6 +44,7 @@ #include "mathops.h" #include "mpegutils.h" #include "rectangle.h" +#include "refstruct.h" #include "thread.h" #include "threadframe.h" @@ -123,7 +121,7 @@ static void release_unused_pictures(H264Context *h, int remove_current) for (i = 0; i < H264_MAX_PICTURE_COUNT; i++) { if (h->DPB[i].f->buf[0] && !h->DPB[i].reference && (remove_current || &h->DPB[i] != h->cur_pic_ptr)) { - ff_h264_unref_picture(h, &h->DPB[i]); + ff_h264_unref_picture(&h->DPB[i]); } } } @@ -167,20 +165,19 @@ static int init_table_pools(H264Context *h) const int b4_stride = h->mb_width * 4 + 1; const int b4_array_size = b4_stride * h->mb_height * 4; - h->qscale_table_pool = av_buffer_pool_init(big_mb_num + h->mb_stride, - av_buffer_allocz); - h->mb_type_pool = av_buffer_pool_init((big_mb_num + h->mb_stride) * - sizeof(uint32_t), av_buffer_allocz); - h->motion_val_pool = av_buffer_pool_init(2 * (b4_array_size + 4) * - sizeof(int16_t), av_buffer_allocz); - h->ref_index_pool = av_buffer_pool_init(4 * mb_array_size, av_buffer_allocz); + h->qscale_table_pool = ff_refstruct_pool_alloc(big_mb_num + h->mb_stride, 0); + h->mb_type_pool = ff_refstruct_pool_alloc((big_mb_num + h->mb_stride) * + sizeof(uint32_t), 0); + h->motion_val_pool = ff_refstruct_pool_alloc(2 * (b4_array_size + 4) * + sizeof(int16_t), 0); + h->ref_index_pool = ff_refstruct_pool_alloc(4 * mb_array_size, 0); if (!h->qscale_table_pool || !h->mb_type_pool || !h->motion_val_pool || !h->ref_index_pool) { - av_buffer_pool_uninit(&h->qscale_table_pool); - av_buffer_pool_uninit(&h->mb_type_pool); - av_buffer_pool_uninit(&h->motion_val_pool); - av_buffer_pool_uninit(&h->ref_index_pool); + ff_refstruct_pool_uninit(&h->qscale_table_pool); + ff_refstruct_pool_uninit(&h->mb_type_pool); + ff_refstruct_pool_uninit(&h->motion_val_pool); + ff_refstruct_pool_uninit(&h->ref_index_pool); return AVERROR(ENOMEM); } @@ -208,16 +205,17 @@ static int alloc_picture(H264Context *h, H264Picture *pic) goto fail; } - if (h->avctx->hwaccel) { - const AVHWAccel *hwaccel = h->avctx->hwaccel; - av_assert0(!pic->hwaccel_picture_private); - if (hwaccel->frame_priv_data_size) { - pic->hwaccel_priv_buf = av_buffer_allocz(hwaccel->frame_priv_data_size); - if (!pic->hwaccel_priv_buf) - return AVERROR(ENOMEM); - pic->hwaccel_picture_private = pic->hwaccel_priv_buf->data; - } + ret = ff_hwaccel_frame_priv_alloc(h->avctx, &pic->hwaccel_picture_private); + if (ret < 0) + goto fail; + + if (h->decode_error_flags_pool) { + pic->decode_error_flags = ff_refstruct_pool_get(h->decode_error_flags_pool); + if (!pic->decode_error_flags) + goto fail; + atomic_init(pic->decode_error_flags, 0); } + if (CONFIG_GRAY && !h->avctx->hwaccel && h->flags & AV_CODEC_FLAG_GRAY && pic->f->data[2]) { int h_chroma_shift, v_chroma_shift; av_pix_fmt_get_chroma_sub_sample(pic->f->format, @@ -237,28 +235,24 @@ static int alloc_picture(H264Context *h, H264Picture *pic) goto fail; } - pic->qscale_table_buf = av_buffer_pool_get(h->qscale_table_pool); - pic->mb_type_buf = av_buffer_pool_get(h->mb_type_pool); - if (!pic->qscale_table_buf || !pic->mb_type_buf) + pic->qscale_table_base = ff_refstruct_pool_get(h->qscale_table_pool); + pic->mb_type_base = ff_refstruct_pool_get(h->mb_type_pool); + if (!pic->qscale_table_base || !pic->mb_type_base) goto fail; - pic->mb_type = (uint32_t*)pic->mb_type_buf->data + 2 * h->mb_stride + 1; - pic->qscale_table = pic->qscale_table_buf->data + 2 * h->mb_stride + 1; + pic->mb_type = pic->mb_type_base + 2 * h->mb_stride + 1; + pic->qscale_table = pic->qscale_table_base + 2 * h->mb_stride + 1; for (i = 0; i < 2; i++) { - pic->motion_val_buf[i] = av_buffer_pool_get(h->motion_val_pool); - pic->ref_index_buf[i] = av_buffer_pool_get(h->ref_index_pool); - if (!pic->motion_val_buf[i] || !pic->ref_index_buf[i]) + pic->motion_val_base[i] = ff_refstruct_pool_get(h->motion_val_pool); + pic->ref_index[i] = ff_refstruct_pool_get(h->ref_index_pool); + if (!pic->motion_val_base[i] || !pic->ref_index[i]) goto fail; - pic->motion_val[i] = (int16_t (*)[2])pic->motion_val_buf[i]->data + 4; - pic->ref_index[i] = pic->ref_index_buf[i]->data; + pic->motion_val[i] = pic->motion_val_base[i] + 4; } - pic->pps_buf = av_buffer_ref(h->ps.pps_ref); - if (!pic->pps_buf) - goto fail; - pic->pps = (const PPS*)pic->pps_buf->data; + pic->pps = ff_refstruct_ref_c(h->ps.pps); pic->mb_width = h->mb_width; pic->mb_height = h->mb_height; @@ -266,11 +260,11 @@ static int alloc_picture(H264Context *h, H264Picture *pic) return 0; fail: - ff_h264_unref_picture(h, pic); + ff_h264_unref_picture(pic); return (ret < 0) ? ret : AVERROR(ENOMEM); } -static int find_unused_picture(H264Context *h) +static int find_unused_picture(const H264Context *h) { int i; @@ -289,9 +283,8 @@ static int find_unused_picture(H264Context *h) (pic) < (old_ctx)->DPB + H264_MAX_PICTURE_COUNT) ? \ &(new_ctx)->DPB[(pic) - (old_ctx)->DPB] : NULL) -static void copy_picture_range(H264Picture **to, H264Picture **from, int count, - H264Context *new_base, - H264Context *old_base) +static void copy_picture_range(H264Picture **to, H264Picture *const *from, int count, + H264Context *new_base, const H264Context *old_base) { int i; @@ -303,6 +296,34 @@ static void copy_picture_range(H264Picture **to, H264Picture **from, int count, } } +static void color_frame(AVFrame *frame, const int c[4]) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); + + av_assert0(desc->flags & AV_PIX_FMT_FLAG_PLANAR); + + for (int p = 0; p < desc->nb_components; p++) { + uint8_t *dst = frame->data[p]; + int is_chroma = p == 1 || p == 2; + int bytes = is_chroma ? AV_CEIL_RSHIFT(frame->width, desc->log2_chroma_w) : frame->width; + int height = is_chroma ? AV_CEIL_RSHIFT(frame->height, desc->log2_chroma_h) : frame->height; + if (desc->comp[0].depth >= 9) { + ((uint16_t*)dst)[0] = c[p]; + av_memcpy_backptr(dst + 2, 2, bytes - 2); + dst += frame->linesize[p]; + for (int y = 1; y < height; y++) { + memcpy(dst, frame->data[p], 2*bytes); + dst += frame->linesize[p]; + } + } else { + for (int y = 0; y < height; y++) { + memset(dst, c[p], bytes); + dst += frame->linesize[p]; + } + } + } +} + static int h264_slice_header_init(H264Context *h); int ff_h264_update_thread_context(AVCodecContext *dst, @@ -335,26 +356,13 @@ int ff_h264_update_thread_context(AVCodecContext *dst, memcpy(h->block_offset, h1->block_offset, sizeof(h->block_offset)); // SPS/PPS - for (i = 0; i < FF_ARRAY_ELEMS(h->ps.sps_list); i++) { - ret = av_buffer_replace(&h->ps.sps_list[i], h1->ps.sps_list[i]); - if (ret < 0) - return ret; - } - for (i = 0; i < FF_ARRAY_ELEMS(h->ps.pps_list); i++) { - ret = av_buffer_replace(&h->ps.pps_list[i], h1->ps.pps_list[i]); - if (ret < 0) - return ret; - } + for (int i = 0; i < FF_ARRAY_ELEMS(h->ps.sps_list); i++) + ff_refstruct_replace(&h->ps.sps_list[i], h1->ps.sps_list[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(h->ps.pps_list); i++) + ff_refstruct_replace(&h->ps.pps_list[i], h1->ps.pps_list[i]); - ret = av_buffer_replace(&h->ps.pps_ref, h1->ps.pps_ref); - if (ret < 0) - return ret; - h->ps.pps = NULL; - h->ps.sps = NULL; - if (h1->ps.pps_ref) { - h->ps.pps = (const PPS*)h->ps.pps_ref->data; - h->ps.sps = h->ps.pps->sps; - } + ff_refstruct_replace(&h->ps.pps, h1->ps.pps); + h->ps.sps = h1->ps.sps; if (need_reinit || !inited) { h->width = h1->width; @@ -377,10 +385,6 @@ int ff_h264_update_thread_context(AVCodecContext *dst, memcpy(h->block_offset, h1->block_offset, sizeof(h->block_offset)); } - h->avctx->coded_height = h1->avctx->coded_height; - h->avctx->coded_width = h1->avctx->coded_width; - h->avctx->width = h1->avctx->width; - h->avctx->height = h1->avctx->height; h->width_from_caller = h1->width_from_caller; h->height_from_caller = h1->height_from_caller; h->coded_picture_number = h1->coded_picture_number; @@ -390,13 +394,13 @@ int ff_h264_update_thread_context(AVCodecContext *dst, h->droppable = h1->droppable; for (i = 0; i < H264_MAX_PICTURE_COUNT; i++) { - ret = ff_h264_replace_picture(h, &h->DPB[i], &h1->DPB[i]); + ret = ff_h264_replace_picture(&h->DPB[i], &h1->DPB[i]); if (ret < 0) return ret; } h->cur_pic_ptr = REBASE_PICTURE(h1->cur_pic_ptr, h, h1); - ret = ff_h264_replace_picture(h, &h->cur_pic, &h1->cur_pic); + ret = ff_h264_replace_picture(&h->cur_pic, &h1->cur_pic); if (ret < 0) return ret; @@ -438,6 +442,8 @@ int ff_h264_update_thread_context(AVCodecContext *dst, return ret; h->sei.common.unregistered.x264_build = h1->sei.common.unregistered.x264_build; + h->sei.common.mastering_display = h1->sei.common.mastering_display; + h->sei.common.content_light = h1->sei.common.content_light; if (!h->cur_pic_ptr) return 0; @@ -451,6 +457,7 @@ int ff_h264_update_thread_context(AVCodecContext *dst, h->poc.prev_frame_num = h->poc.frame_num; h->recovery_frame = h1->recovery_frame; + h->non_gray = h1->non_gray; return err; } @@ -489,11 +496,6 @@ static int h264_frame_start(H264Context *h) pic = &h->DPB[i]; pic->reference = h->droppable ? 0 : h->picture_structure; -#if FF_API_FRAME_PICTURE_NUMBER -FF_DISABLE_DEPRECATION_WARNINGS - pic->f->coded_picture_number = h->coded_picture_number++; -FF_ENABLE_DEPRECATION_WARNINGS -#endif pic->field_picture = h->picture_structure != PICT_FRAME; pic->frame_num = h->poc.frame_num; /* @@ -501,7 +503,7 @@ FF_ENABLE_DEPRECATION_WARNINGS * in later. * See decode_nal_units(). */ - pic->f->key_frame = 0; + pic->f->flags &= ~AV_FRAME_FLAG_KEY; pic->mmco_reset = 0; pic->recovered = 0; pic->invalid_gap = 0; @@ -521,12 +523,12 @@ FF_ENABLE_DEPRECATION_WARNINGS return ret; h->cur_pic_ptr = pic; - ff_h264_unref_picture(h, &h->cur_pic); + ff_h264_unref_picture(&h->cur_pic); if (CONFIG_ERROR_RESILIENCE) { ff_h264_set_erpic(&h->er.cur_pic, NULL); } - if ((ret = ff_h264_ref_picture(h, &h->cur_pic, h->cur_pic_ptr)) < 0) + if ((ret = ff_h264_ref_picture(&h->cur_pic, h->cur_pic_ptr)) < 0) return ret; for (i = 0; i < h->nb_slice_ctx; i++) { @@ -574,8 +576,8 @@ FF_ENABLE_DEPRECATION_WARNINGS } static av_always_inline void backup_mb_border(const H264Context *h, H264SliceContext *sl, - uint8_t *src_y, - uint8_t *src_cb, uint8_t *src_cr, + const uint8_t *src_y, + const uint8_t *src_cb, const uint8_t *src_cr, int linesize, int uvlinesize, int simple) { @@ -777,13 +779,13 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) { #define HWACCEL_MAX (CONFIG_H264_DXVA2_HWACCEL + \ (CONFIG_H264_D3D11VA_HWACCEL * 2) + \ + CONFIG_H264_D3D12VA_HWACCEL + \ CONFIG_H264_NVDEC_HWACCEL + \ CONFIG_H264_VAAPI_HWACCEL + \ CONFIG_H264_VIDEOTOOLBOX_HWACCEL + \ - CONFIG_H264_VDPAU_HWACCEL) + CONFIG_H264_VDPAU_HWACCEL + \ + CONFIG_H264_VULKAN_HWACCEL) enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmt = pix_fmts; - const enum AVPixelFormat *choices = pix_fmts; - int i; switch (h->ps.sps->bit_depth_luma) { case 9: @@ -801,6 +803,9 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) #if CONFIG_H264_VIDEOTOOLBOX_HWACCEL if (h->avctx->colorspace != AVCOL_SPC_RGB) *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_H264_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; #endif if (CHROMA444(h)) { if (h->avctx->colorspace == AVCOL_SPC_RGB) { @@ -809,10 +814,20 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) *fmt++ = AV_PIX_FMT_YUV444P10; } else if (CHROMA422(h)) *fmt++ = AV_PIX_FMT_YUV422P10; - else + else { +#if CONFIG_H264_VAAPI_HWACCEL + // Just add as candidate. Whether VAProfileH264High10 usable or + // not is decided by vaapi_decode_make_config() defined in FFmpeg + // and vaQueryCodingProfile() defined in libva. + *fmt++ = AV_PIX_FMT_VAAPI; +#endif *fmt++ = AV_PIX_FMT_YUV420P10; + } break; case 12: +#if CONFIG_H264_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; +#endif if (CHROMA444(h)) { if (h->avctx->colorspace == AVCOL_SPC_RGB) { *fmt++ = AV_PIX_FMT_GBRP12; @@ -838,6 +853,9 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) #if CONFIG_H264_VDPAU_HWACCEL *fmt++ = AV_PIX_FMT_VDPAU; #endif +#if CONFIG_H264_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; +#endif #if CONFIG_H264_NVDEC_HWACCEL *fmt++ = AV_PIX_FMT_CUDA; #endif @@ -865,12 +883,13 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) *fmt++ = AV_PIX_FMT_D3D11VA_VLD; *fmt++ = AV_PIX_FMT_D3D11; #endif +#if CONFIG_H264_D3D12VA_HWACCEL + *fmt++ = AV_PIX_FMT_D3D12; +#endif #if CONFIG_H264_VAAPI_HWACCEL *fmt++ = AV_PIX_FMT_VAAPI; #endif - if (h->avctx->codec->pix_fmts) - choices = h->avctx->codec->pix_fmts; - else if (h->avctx->color_range == AVCOL_RANGE_JPEG) + if (h->avctx->color_range == AVCOL_RANGE_JPEG) *fmt++ = AV_PIX_FMT_YUVJ420P; else *fmt++ = AV_PIX_FMT_YUV420P; @@ -884,16 +903,16 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) *fmt = AV_PIX_FMT_NONE; - for (i=0; choices[i] != AV_PIX_FMT_NONE; i++) - if (choices[i] == h->avctx->pix_fmt && !force_callback) - return choices[i]; - return ff_thread_get_format(h->avctx, choices); + for (int i = 0; pix_fmts[i] != AV_PIX_FMT_NONE; i++) + if (pix_fmts[i] == h->avctx->pix_fmt && !force_callback) + return pix_fmts[i]; + return ff_get_format(h->avctx, pix_fmts); } /* export coded and cropped frame dimensions to AVCodecContext */ static void init_dimensions(H264Context *h) { - const SPS *sps = (const SPS*)h->ps.sps; + const SPS *sps = h->ps.sps; int cr = sps->crop_right; int cl = sps->crop_left; int ct = sps->crop_top; @@ -950,7 +969,7 @@ static int h264_slice_header_init(H264Context *h) if (h->x264_build < 44U) den *= 2; av_reduce(&h->avctx->framerate.den, &h->avctx->framerate.num, - sps->num_units_in_tick * h->avctx->ticks_per_frame, den, 1 << 30); + sps->num_units_in_tick * 2, den, 1 << 30); } ff_h264_free_tables(h); @@ -1029,17 +1048,11 @@ static int h264_init_ps(H264Context *h, const H264SliceContext *sl, int first_sl const SPS *sps; int needs_reinit = 0, must_reinit, ret; - if (first_slice) { - av_buffer_unref(&h->ps.pps_ref); - h->ps.pps = NULL; - h->ps.pps_ref = av_buffer_ref(h->ps.pps_list[sl->pps_id]); - if (!h->ps.pps_ref) - return AVERROR(ENOMEM); - h->ps.pps = (const PPS*)h->ps.pps_ref->data; - } + if (first_slice) + ff_refstruct_replace(&h->ps.pps, h->ps.pps_list[sl->pps_id]); if (h->ps.sps != h->ps.pps->sps) { - h->ps.sps = (const SPS*)h->ps.pps->sps; + h->ps.sps = h->ps.pps->sps; if (h->mb_width != h->ps.sps->mb_width || h->mb_height != h->ps.sps->mb_height || @@ -1148,9 +1161,10 @@ static int h264_export_frame_props(H264Context *h) const SPS *sps = h->ps.sps; H264Picture *cur = h->cur_pic_ptr; AVFrame *out = cur->f; + int interlaced_frame = 0, top_field_first = 0; int ret; - out->interlaced_frame = 0; + out->flags &= ~AV_FRAME_FLAG_INTERLACED; out->repeat_pict = 0; /* Signal interlacing information externally. */ @@ -1168,21 +1182,21 @@ static int h264_export_frame_props(H264Context *h) } if (sps->pic_struct_present_flag && h->sei.picture_timing.present) { - H264SEIPictureTiming *pt = &h->sei.picture_timing; + const H264SEIPictureTiming *pt = &h->sei.picture_timing; switch (pt->pic_struct) { case H264_SEI_PIC_STRUCT_FRAME: break; case H264_SEI_PIC_STRUCT_TOP_FIELD: case H264_SEI_PIC_STRUCT_BOTTOM_FIELD: - out->interlaced_frame = 1; + interlaced_frame = 1; break; case H264_SEI_PIC_STRUCT_TOP_BOTTOM: case H264_SEI_PIC_STRUCT_BOTTOM_TOP: if (FIELD_OR_MBAFF_PICTURE(h)) - out->interlaced_frame = 1; + interlaced_frame = 1; else // try to flag soft telecine progressive - out->interlaced_frame = h->prev_interlaced_frame; + interlaced_frame = !!h->prev_interlaced_frame; break; case H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP: case H264_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM: @@ -1201,35 +1215,34 @@ static int h264_export_frame_props(H264Context *h) if ((pt->ct_type & 3) && pt->pic_struct <= H264_SEI_PIC_STRUCT_BOTTOM_TOP) - out->interlaced_frame = (pt->ct_type & (1 << 1)) != 0; + interlaced_frame = ((pt->ct_type & (1 << 1)) != 0); } else { /* Derive interlacing flag from used decoding process. */ - out->interlaced_frame = FIELD_OR_MBAFF_PICTURE(h); + interlaced_frame = !!FIELD_OR_MBAFF_PICTURE(h); } - h->prev_interlaced_frame = out->interlaced_frame; + h->prev_interlaced_frame = interlaced_frame; if (cur->field_poc[0] != cur->field_poc[1]) { /* Derive top_field_first from field pocs. */ - out->top_field_first = cur->field_poc[0] < cur->field_poc[1]; + top_field_first = (cur->field_poc[0] < cur->field_poc[1]); } else { if (sps->pic_struct_present_flag && h->sei.picture_timing.present) { /* Use picture timing SEI information. Even if it is a * information of a past frame, better than nothing. */ if (h->sei.picture_timing.pic_struct == H264_SEI_PIC_STRUCT_TOP_BOTTOM || h->sei.picture_timing.pic_struct == H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP) - out->top_field_first = 1; - else - out->top_field_first = 0; - } else if (out->interlaced_frame) { + top_field_first = 1; + } else if (interlaced_frame) { /* Default to top field first when pic_struct_present_flag * is not set but interlaced frame detected */ - out->top_field_first = 1; - } else { + top_field_first = 1; + } // else /* Most likely progressive */ - out->top_field_first = 0; - } } + out->flags |= (AV_FRAME_FLAG_INTERLACED * interlaced_frame) | + (AV_FRAME_FLAG_TOP_FIELD_FIRST * top_field_first); + ret = ff_h2645_sei_to_frame(out, &h->sei.common, AV_CODEC_ID_H264, h->avctx, &sps->vui, sps->bit_depth_luma, sps->bit_depth_chroma, cur->poc + (unsigned)(h->poc_offset << 5)); @@ -1239,26 +1252,27 @@ static int h264_export_frame_props(H264Context *h) if (h->sei.picture_timing.timecode_cnt > 0) { uint32_t *tc_sd; char tcbuf[AV_TIMECODE_STR_SIZE]; + AVFrameSideData *tcside; + ret = ff_frame_new_side_data(h->avctx, out, AV_FRAME_DATA_S12M_TIMECODE, + sizeof(uint32_t)*4, &tcside); + if (ret < 0) + return ret; + + if (tcside) { + tc_sd = (uint32_t*)tcside->data; + tc_sd[0] = h->sei.picture_timing.timecode_cnt; - AVFrameSideData *tcside = av_frame_new_side_data(out, - AV_FRAME_DATA_S12M_TIMECODE, - sizeof(uint32_t)*4); - if (!tcside) - return AVERROR(ENOMEM); - - tc_sd = (uint32_t*)tcside->data; - tc_sd[0] = h->sei.picture_timing.timecode_cnt; - - for (int i = 0; i < tc_sd[0]; i++) { - int drop = h->sei.picture_timing.timecode[i].dropframe; - int hh = h->sei.picture_timing.timecode[i].hours; - int mm = h->sei.picture_timing.timecode[i].minutes; - int ss = h->sei.picture_timing.timecode[i].seconds; - int ff = h->sei.picture_timing.timecode[i].frame; - - tc_sd[i + 1] = av_timecode_get_smpte(h->avctx->framerate, drop, hh, mm, ss, ff); - av_timecode_make_smpte_tc_string2(tcbuf, h->avctx->framerate, tc_sd[i + 1], 0, 0); - av_dict_set(&out->metadata, "timecode", tcbuf, 0); + for (int i = 0; i < tc_sd[0]; i++) { + int drop = h->sei.picture_timing.timecode[i].dropframe; + int hh = h->sei.picture_timing.timecode[i].hours; + int mm = h->sei.picture_timing.timecode[i].minutes; + int ss = h->sei.picture_timing.timecode[i].seconds; + int ff = h->sei.picture_timing.timecode[i].frame; + + tc_sd[i + 1] = av_timecode_get_smpte(h->avctx->framerate, drop, hh, mm, ss, ff); + av_timecode_make_smpte_tc_string2(tcbuf, h->avctx->framerate, tc_sd[i + 1], 0, 0); + av_dict_set(&out->metadata, "timecode", tcbuf, 0); + } } h->sei.picture_timing.timecode_cnt = 0; } @@ -1319,7 +1333,7 @@ static int h264_select_output_frame(H264Context *h) out = h->delayed_pic[0]; out_idx = 0; for (i = 1; h->delayed_pic[i] && - !h->delayed_pic[i]->f->key_frame && + !(h->delayed_pic[i]->f->flags & AV_FRAME_FLAG_KEY) && !h->delayed_pic[i]->mmco_reset; i++) if (h->delayed_pic[i]->poc < out->poc) { @@ -1327,7 +1341,7 @@ static int h264_select_output_frame(H264Context *h) out_idx = i; } if (h->avctx->has_b_frames == 0 && - (h->delayed_pic[0]->f->key_frame || h->delayed_pic[0]->mmco_reset)) + ((h->delayed_pic[0]->f->flags & AV_FRAME_FLAG_KEY) || h->delayed_pic[0]->mmco_reset)) h->next_outputed_poc = INT_MIN; out_of_order = out->poc < h->next_outputed_poc; @@ -1338,17 +1352,16 @@ static int h264_select_output_frame(H264Context *h) } if (!out_of_order && pics > h->avctx->has_b_frames) { h->next_output_pic = out; - if (out_idx == 0 && h->delayed_pic[0] && (h->delayed_pic[0]->f->key_frame || h->delayed_pic[0]->mmco_reset)) { + if (out_idx == 0 && h->delayed_pic[0] && ((h->delayed_pic[0]->f->flags & AV_FRAME_FLAG_KEY) || h->delayed_pic[0]->mmco_reset)) { h->next_outputed_poc = INT_MIN; } else h->next_outputed_poc = out->poc; - if (out->recovered) { - // We have reached an recovery point and all frames after it in - // display order are "recovered". - h->frame_recovered |= FRAME_RECOVERED_SEI; - } - out->recovered |= !!(h->frame_recovered & FRAME_RECOVERED_SEI); + // We have reached an recovery point and all frames after it in + // display order are "recovered". + h->frame_recovered |= out->recovered; + + out->recovered |= h->frame_recovered & FRAME_RECOVERED_SEI; if (!out->recovered) { if (!(h->avctx->flags & AV_CODEC_FLAG_OUTPUT_CORRUPT) && @@ -1383,7 +1396,7 @@ static int h264_field_start(H264Context *h, const H264SliceContext *sl, sps = h->ps.sps; - if (sps && sps->bitstream_restriction_flag && + if (sps->bitstream_restriction_flag && h->avctx->has_b_frames < sps->num_reorder_frames) { h->avctx->has_b_frames = sps->num_reorder_frames; } @@ -1526,17 +1539,21 @@ static int h264_field_start(H264Context *h, const H264SliceContext *sl, ff_thread_await_progress(&prev->tf, INT_MAX, 0); if (prev->field_picture) ff_thread_await_progress(&prev->tf, INT_MAX, 1); - ff_thread_release_ext_buffer(h->avctx, &h->short_ref[0]->tf); + ff_thread_release_ext_buffer(&h->short_ref[0]->tf); h->short_ref[0]->tf.f = h->short_ref[0]->f; ret = ff_thread_ref_frame(&h->short_ref[0]->tf, &prev->tf); if (ret < 0) return ret; h->short_ref[0]->poc = prev->poc + 2U; + h->short_ref[0]->gray = prev->gray; ff_thread_report_progress(&h->short_ref[0]->tf, INT_MAX, 0); if (h->short_ref[0]->field_picture) ff_thread_report_progress(&h->short_ref[0]->tf, INT_MAX, 1); - } else if (!h->frame_recovered && !h->avctx->hwaccel) - ff_color_frame(h->short_ref[0]->f, c); + } else if (!h->frame_recovered) { + if (!h->avctx->hwaccel) + color_frame(h->short_ref[0]->f, c); + h->short_ref[0]->gray = 1; + } h->short_ref[0]->frame_num = h->poc.prev_frame_num; } } @@ -1628,17 +1645,20 @@ static int h264_field_start(H264Context *h, const H264SliceContext *sl, } } - h->cur_pic_ptr->f->key_frame |= (nal->type == H264_NAL_IDR_SLICE); + h->cur_pic_ptr->f->flags |= AV_FRAME_FLAG_KEY * !!(nal->type == H264_NAL_IDR_SLICE); - if (nal->type == H264_NAL_IDR_SLICE || - (h->recovery_frame == h->poc.frame_num && nal->ref_idc)) { - h->recovery_frame = -1; - h->cur_pic_ptr->recovered = 1; - } - // If we have an IDR, all frames after it in decoded order are - // "recovered". - if (nal->type == H264_NAL_IDR_SLICE) + if (nal->type == H264_NAL_IDR_SLICE) { + h->cur_pic_ptr->recovered |= FRAME_RECOVERED_IDR; + // If we have an IDR, all frames after it in decoded order are + // "recovered". h->frame_recovered |= FRAME_RECOVERED_IDR; + } + + if (h->recovery_frame == h->poc.frame_num && nal->ref_idc) { + h->recovery_frame = -1; + h->cur_pic_ptr->recovered |= FRAME_RECOVERED_SEI; + } + #if 1 h->cur_pic_ptr->recovered |= h->frame_recovered; #else @@ -1711,7 +1731,7 @@ static int h264_slice_header_parse(const H264Context *h, H264SliceContext *sl, sl->pps_id); return AVERROR_INVALIDDATA; } - pps = (const PPS*)h->ps.pps_list[sl->pps_id]->data; + pps = h->ps.pps_list[sl->pps_id]; sps = pps->sps; sl->frame_num = get_bits(&sl->gb, sps->log2_max_frame_num); @@ -1967,7 +1987,7 @@ static int h264_slice_init(H264Context *h, H264SliceContext *sl, if (j < sl->list_count && i < sl->ref_count[j] && sl->ref_list[j][i].parent->f->buf[0]) { int k; - AVBuffer *buf = sl->ref_list[j][i].parent->f->buf[0]->buffer; + const AVBuffer *buf = sl->ref_list[j][i].parent->f->buf[0]->buffer; for (k = 0; k < h->short_ref_count; k++) if (h->short_ref[k]->f->buf[0]->buffer == buf) { id_list[i] = k; @@ -1992,6 +2012,19 @@ static int h264_slice_init(H264Context *h, H264SliceContext *sl, (sl->ref_list[j][i].reference & 3); } + if (sl->slice_type_nos == AV_PICTURE_TYPE_I) { + h->cur_pic_ptr->gray = 0; + h->non_gray = 1; + } else { + int gray = 0; + for (j = 0; j < sl->list_count; j++) { + for (i = 0; i < sl->ref_count[j]; i++) { + gray |= sl->ref_list[j][i].parent->gray; + } + } + h->cur_pic_ptr->gray = gray; + } + if (h->avctx->debug & FF_DEBUG_PICT_INFO) { av_log(h->avctx, AV_LOG_DEBUG, "slice:%d %c mb:%d %c%s%s frame:%d poc:%d/%d ref:%d/%d qp:%d loop:%d:%d:%d weight:%d%s %s\n", @@ -2098,7 +2131,7 @@ int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal) } if (!first_slice) { - const PPS *pps = (const PPS*)h->ps.pps_list[sl->pps_id]->data; + const PPS *pps = h->ps.pps_list[sl->pps_id]; if (h->ps.pps->sps_id != pps->sps_id || h->ps.pps->transform_8x8_mode != pps->transform_8x8_mode /*|| @@ -2162,9 +2195,9 @@ int ff_h264_get_slice_type(const H264SliceContext *sl) static av_always_inline void fill_filter_caches_inter(const H264Context *h, H264SliceContext *sl, int mb_type, int top_xy, - int left_xy[LEFT_MBS], + const int left_xy[LEFT_MBS], int top_type, - int left_type[LEFT_MBS], + const int left_type[LEFT_MBS], int mb_xy, int list) { int b_stride = h->b_stride; @@ -2221,7 +2254,7 @@ static av_always_inline void fill_filter_caches_inter(const H264Context *h, } { - int8_t *ref = &h->cur_pic.ref_index[list][4 * mb_xy]; + const int8_t *ref = &h->cur_pic.ref_index[list][4 * mb_xy]; const int *ref2frm = &h->ref2frm[sl->slice_num & (MAX_SLICES - 1)][list][(MB_MBAFF(sl) ? 20 : 2)]; uint32_t ref01 = (pack16to32(ref2frm[ref[0]], ref2frm[ref[1]]) & 0x00FF00FF) * 0x0101; uint32_t ref23 = (pack16to32(ref2frm[ref[2]], ref2frm[ref[3]]) & 0x00FF00FF) * 0x0101; @@ -2248,7 +2281,7 @@ static int fill_filter_caches(const H264Context *h, H264SliceContext *sl, int mb const int mb_xy = sl->mb_xy; int top_xy, left_xy[LEFT_MBS]; int top_type, left_type[LEFT_MBS]; - uint8_t *nnz; + const uint8_t *nnz; uint8_t *nnz_cache; top_xy = mb_xy - (h->mb_stride << MB_FIELD(sl)); diff --git a/libavcodec/h264chroma.c b/libavcodec/h264chroma.c index 60b86b6fba0..1eeab7bc40c 100644 --- a/libavcodec/h264chroma.c +++ b/libavcodec/h264chroma.c @@ -58,5 +58,7 @@ av_cold void ff_h264chroma_init(H264ChromaContext *c, int bit_depth) ff_h264chroma_init_mips(c, bit_depth); #elif ARCH_LOONGARCH64 ff_h264chroma_init_loongarch(c, bit_depth); +#elif ARCH_RISCV + ff_h264chroma_init_riscv(c, bit_depth); #endif } diff --git a/libavcodec/h264chroma.h b/libavcodec/h264chroma.h index b8f9c8f4fcc..9c81c18a76f 100644 --- a/libavcodec/h264chroma.h +++ b/libavcodec/h264chroma.h @@ -37,5 +37,6 @@ void ff_h264chroma_init_ppc(H264ChromaContext *c, int bit_depth); void ff_h264chroma_init_x86(H264ChromaContext *c, int bit_depth); void ff_h264chroma_init_mips(H264ChromaContext *c, int bit_depth); void ff_h264chroma_init_loongarch(H264ChromaContext *c, int bit_depth); +void ff_h264chroma_init_riscv(H264ChromaContext *c, int bit_depth); #endif /* AVCODEC_H264CHROMA_H */ diff --git a/libavcodec/h264dec.c b/libavcodec/h264dec.c index 2d691731c5d..b4973fce296 100644 --- a/libavcodec/h264dec.c +++ b/libavcodec/h264dec.c @@ -30,6 +30,7 @@ #include "config_components.h" #include "libavutil/avassert.h" +#include "libavutil/emms.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/thread.h" @@ -45,10 +46,12 @@ #include "h264data.h" #include "h264_ps.h" #include "golomb.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "mpegutils.h" #include "profiles.h" #include "rectangle.h" +#include "refstruct.h" #include "thread.h" #include "threadframe.h" @@ -64,7 +67,7 @@ static void h264_er_decode_mb(void *opaque, int ref, int mv_dir, int mv_type, int (*mv)[2][4][2], int mb_x, int mb_y, int mb_intra, int mb_skipped) { - H264Context *h = opaque; + const H264Context *h = opaque; H264SliceContext *sl = &h->slice_ctx[0]; sl->mb_x = mb_x; @@ -101,9 +104,17 @@ void ff_h264_draw_horiz_band(const H264Context *h, H264SliceContext *sl, { AVCodecContext *avctx = h->avctx; const AVFrame *src = h->cur_pic.f; - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt); - int vshift = desc->log2_chroma_h; + const AVPixFmtDescriptor *desc; + int offset[AV_NUM_DATA_POINTERS]; + int vshift; const int field_pic = h->picture_structure != PICT_FRAME; + + if (!avctx->draw_horiz_band) + return; + + if (field_pic && h->first_field && !(avctx->slice_flags & SLICE_FLAG_ALLOW_FIELD)) + return; + if (field_pic) { height <<= 1; y <<= 1; @@ -111,24 +122,19 @@ void ff_h264_draw_horiz_band(const H264Context *h, H264SliceContext *sl, height = FFMIN(height, avctx->height - y); - if (field_pic && h->first_field && !(avctx->slice_flags & SLICE_FLAG_ALLOW_FIELD)) - return; - - if (avctx->draw_horiz_band) { - int offset[AV_NUM_DATA_POINTERS]; - int i; + desc = av_pix_fmt_desc_get(avctx->pix_fmt); + vshift = desc->log2_chroma_h; - offset[0] = y * src->linesize[0]; - offset[1] = - offset[2] = (y >> vshift) * src->linesize[1]; - for (i = 3; i < AV_NUM_DATA_POINTERS; i++) - offset[i] = 0; + offset[0] = y * src->linesize[0]; + offset[1] = + offset[2] = (y >> vshift) * src->linesize[1]; + for (int i = 3; i < AV_NUM_DATA_POINTERS; i++) + offset[i] = 0; - emms_c(); + emms_c(); - avctx->draw_horiz_band(avctx, src, offset, - y, h->picture_structure, height); - } + avctx->draw_horiz_band(avctx, src, offset, + y, h->picture_structure, height); } void ff_h264_free_tables(H264Context *h) @@ -149,10 +155,10 @@ void ff_h264_free_tables(H264Context *h) av_freep(&h->mb2b_xy); av_freep(&h->mb2br_xy); - av_buffer_pool_uninit(&h->qscale_table_pool); - av_buffer_pool_uninit(&h->mb_type_pool); - av_buffer_pool_uninit(&h->motion_val_pool); - av_buffer_pool_uninit(&h->ref_index_pool); + ff_refstruct_pool_uninit(&h->qscale_table_pool); + ff_refstruct_pool_uninit(&h->mb_type_pool); + ff_refstruct_pool_uninit(&h->motion_val_pool); + ff_refstruct_pool_uninit(&h->ref_index_pool); #if CONFIG_ERROR_RESILIENCE av_freep(&h->er.mb_index2xy); @@ -305,6 +311,12 @@ static int h264_init_context(AVCodecContext *avctx, H264Context *h) ff_h264_sei_uninit(&h->sei); + if (avctx->active_thread_type & FF_THREAD_FRAME) { + h->decode_error_flags_pool = ff_refstruct_pool_alloc(sizeof(atomic_int), 0); + if (!h->decode_error_flags_pool) + return AVERROR(ENOMEM); + } + h->nb_slice_ctx = (avctx->active_thread_type & FF_THREAD_SLICE) ? avctx->thread_count : 1; h->slice_ctx = av_calloc(h->nb_slice_ctx, sizeof(*h->slice_ctx)); if (!h->slice_ctx) { @@ -331,7 +343,7 @@ static int h264_init_context(AVCodecContext *avctx, H264Context *h) static void h264_free_pic(H264Context *h, H264Picture *pic) { - ff_h264_unref_picture(h, pic); + ff_h264_unref_picture(pic); av_frame_free(&pic->f); av_frame_free(&pic->f_grain); } @@ -351,6 +363,8 @@ static av_cold int h264_decode_end(AVCodecContext *avctx) h->cur_pic_ptr = NULL; + ff_refstruct_pool_uninit(&h->decode_error_flags_pool); + av_freep(&h->slice_ctx); h->nb_slice_ctx = 0; @@ -382,7 +396,11 @@ static av_cold int h264_decode_init(AVCodecContext *avctx) return AVERROR_UNKNOWN; } +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS avctx->ticks_per_frame = 2; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (!avctx->internal->is_copy) { if (avctx->extradata_size > 0 && avctx->extradata) { @@ -452,7 +470,7 @@ void ff_h264_flush_change(H264Context *h) h->delayed_pic[j++] = h->delayed_pic[i]; h->delayed_pic[j] = NULL; } - ff_h264_unref_picture(h, &h->last_pic_for_ec); + ff_h264_unref_picture(&h->last_pic_for_ec); h->first_field = 0; h->recovery_frame = -1; @@ -472,14 +490,18 @@ static void h264_decode_flush(AVCodecContext *avctx) ff_h264_sei_uninit(&h->sei); for (i = 0; i < H264_MAX_PICTURE_COUNT; i++) - ff_h264_unref_picture(h, &h->DPB[i]); + ff_h264_unref_picture(&h->DPB[i]); h->cur_pic_ptr = NULL; - ff_h264_unref_picture(h, &h->cur_pic); + ff_h264_unref_picture(&h->cur_pic); h->mb_y = 0; + h->non_gray = 0; ff_h264_free_tables(h); h->context_initialized = 0; + + if (FF_HW_HAS_CB(avctx, flush)) + FF_HW_SIMPLE_CALL(avctx, flush); } static int get_last_needed_nal(H264Context *h) @@ -645,14 +667,14 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size) } if (h->avctx->hwaccel && - (ret = h->avctx->hwaccel->start_frame(h->avctx, buf, buf_size)) < 0) + (ret = FF_HW_CALL(h->avctx, start_frame, buf, buf_size)) < 0) goto end; } max_slice_ctx = avctx->hwaccel ? 1 : h->nb_slice_ctx; if (h->nb_slice_ctx_queued == max_slice_ctx) { if (h->avctx->hwaccel) { - ret = avctx->hwaccel->decode_slice(avctx, nal->raw_data, nal->raw_size); + ret = FF_HW_CALL(avctx, decode_slice, nal->raw_data, nal->raw_size); h->nb_slice_ctx_queued = 0; } else ret = ff_h264_execute_decode_slices(h); @@ -679,11 +701,9 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size) break; case H264_NAL_SPS: { GetBitContext tmp_gb = nal->gb; - if (avctx->hwaccel && avctx->hwaccel->decode_params) { - ret = avctx->hwaccel->decode_params(avctx, - nal->type, - nal->raw_data, - nal->raw_size); + if (FF_HW_HAS_CB(avctx, decode_params)) { + ret = FF_HW_CALL(avctx, decode_params, + nal->type, nal->raw_data, nal->raw_size); if (ret < 0) goto end; } @@ -698,11 +718,9 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size) break; } case H264_NAL_PPS: - if (avctx->hwaccel && avctx->hwaccel->decode_params) { - ret = avctx->hwaccel->decode_params(avctx, - nal->type, - nal->raw_data, - nal->raw_size); + if (FF_HW_HAS_CB(avctx, decode_params)) { + ret = FF_HW_CALL(avctx, decode_params, + nal->type, nal->raw_data, nal->raw_size); if (ret < 0) goto end; } @@ -734,7 +752,16 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size) // set decode_error_flags to allow users to detect concealed decoding errors if ((ret < 0 || h->er.error_occurred) && h->cur_pic_ptr) { - h->cur_pic_ptr->f->decode_error_flags |= FF_DECODE_ERROR_DECODE_SLICES; + if (h->cur_pic_ptr->decode_error_flags) { + /* Frame-threading in use */ + atomic_int *decode_error = h->cur_pic_ptr->decode_error_flags; + /* Using atomics here is not supposed to provide syncronisation; + * they are merely used to allow to set decode_error from both + * decoding threads in case of coded slices. */ + atomic_fetch_or_explicit(decode_error, FF_DECODE_ERROR_DECODE_SLICES, + memory_order_relaxed); + } else + h->cur_pic_ptr->f->decode_error_flags |= FF_DECODE_ERROR_DECODE_SLICES; } ret = 0; @@ -757,6 +784,7 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size) H264SliceContext *sl = h->slice_ctx; int use_last_pic = h->last_pic_for_ec.f->buf[0] && !sl->ref_count[0]; + int decode_error_flags = 0; ff_h264_set_erpic(&h->er.cur_pic, h->cur_pic_ptr); @@ -774,7 +802,15 @@ static int decode_nal_units(H264Context *h, const uint8_t *buf, int buf_size) if (sl->ref_count[1]) ff_h264_set_erpic(&h->er.next_pic, sl->ref_list[1][0].parent); - ff_er_frame_end(&h->er); + ff_er_frame_end(&h->er, &decode_error_flags); + if (decode_error_flags) { + if (h->cur_pic_ptr->decode_error_flags) { + atomic_int *decode_error = h->cur_pic_ptr->decode_error_flags; + atomic_fetch_or_explicit(decode_error, decode_error_flags, + memory_order_relaxed); + } else + h->cur_pic_ptr->f->decode_error_flags |= decode_error_flags; + } if (use_last_pic) memset(&sl->ref_list[0][0], 0, sizeof(sl->ref_list[0][0])); } @@ -801,7 +837,7 @@ static int get_consumed_bytes(int pos, int buf_size) return pos; } -static int h264_export_enc_params(AVFrame *f, H264Picture *p) +static int h264_export_enc_params(AVFrame *f, const H264Picture *p) { AVVideoEncParams *par; unsigned int nb_mb = p->mb_height * p->mb_width; @@ -846,10 +882,18 @@ static int output_frame(H264Context *h, AVFrame *dst, H264Picture *srcp) if (srcp->needs_fg && (ret = av_frame_copy_props(dst, srcp->f)) < 0) return ret; + if (srcp->decode_error_flags) { + atomic_int *decode_error = srcp->decode_error_flags; + /* The following is not supposed to provide synchronisation at all: + * given that srcp has already finished decoding, decode_error + * has already been set to its final value. */ + dst->decode_error_flags |= atomic_load_explicit(decode_error, memory_order_relaxed); + } + av_dict_set(&dst->metadata, "stereo_mode", ff_h264_sei_stereo_mode(&h->sei.common.frame_packing), 0); if (srcp->sei_recovery_frame_cnt == 0) - dst->key_frame = 1; + dst->flags |= AV_FRAME_FLAG_KEY; if (h->avctx->export_side_data & AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS) { ret = h264_export_enc_params(dst, srcp); @@ -898,6 +942,12 @@ static int finalize_frame(H264Context *h, AVFrame *dst, H264Picture *out, int *g (h->avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL) || out->recovered)) { + if (h->skip_gray > 0 && + h->non_gray && out->gray && + !(h->avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL) + ) + return 0; + if (!h->avctx->hwaccel && (out->field_poc[0] == INT_MAX || out->field_poc[1] == INT_MAX) @@ -951,7 +1001,7 @@ static int send_next_delayed_frame(H264Context *h, AVFrame *dst_frame, out_idx = 0; for (i = 1; h->delayed_pic[i] && - !h->delayed_pic[i]->f->key_frame && + !(h->delayed_pic[i]->f->flags & AV_FRAME_FLAG_KEY) && !h->delayed_pic[i]->mmco_reset; i++) if (h->delayed_pic[i]->poc < out->poc) { @@ -985,7 +1035,7 @@ static int h264_decode_frame(AVCodecContext *avctx, AVFrame *pict, h->setup_finished = 0; h->nb_slice_ctx_queued = 0; - ff_h264_unref_picture(h, &h->last_pic_for_ec); + ff_h264_unref_picture(&h->last_pic_for_ec); /* end of stream, output what is still in the buffers */ if (buf_size == 0) @@ -1037,7 +1087,7 @@ static int h264_decode_frame(AVCodecContext *avctx, AVFrame *pict, av_assert0(pict->buf[0] || !*got_frame); - ff_h264_unref_picture(h, &h->last_pic_for_ec); + ff_h264_unref_picture(&h->last_pic_for_ec); return get_consumed_bytes(buf_index, buf_size); } @@ -1050,6 +1100,8 @@ static const AVOption h264_options[] = { { "nal_length_size", "nal_length_size", OFFSET(nal_length_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 4, VDX }, { "enable_er", "Enable error resilience on damaged frames (unsafe)", OFFSET(enable_er), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VD }, { "x264_build", "Assume this x264 version if no x264 version found in any SEI", OFFSET(x264_build), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VD }, + { "skip_gray", "Do not return gray gap frames", OFFSET(skip_gray), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, VD }, + { "noref_gray", "Avoid using gray gap frames as references", OFFSET(noref_gray), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, VD }, { NULL }, }; @@ -1082,6 +1134,9 @@ const FFCodec ff_h264_decoder = { #if CONFIG_H264_D3D11VA2_HWACCEL HWACCEL_D3D11VA2(h264), #endif +#if CONFIG_H264_D3D12VA_HWACCEL + HWACCEL_D3D12VA(h264), +#endif #if CONFIG_H264_NVDEC_HWACCEL HWACCEL_NVDEC(h264), #endif @@ -1093,6 +1148,9 @@ const FFCodec ff_h264_decoder = { #endif #if CONFIG_H264_VIDEOTOOLBOX_HWACCEL HWACCEL_VIDEOTOOLBOX(h264), +#endif +#if CONFIG_H264_VULKAN_HWACCEL + HWACCEL_VULKAN(h264), #endif NULL }, diff --git a/libavcodec/h264dec.h b/libavcodec/h264dec.h index 9a1ec1bacec..447c2499d97 100644 --- a/libavcodec/h264dec.h +++ b/libavcodec/h264dec.h @@ -29,7 +29,6 @@ #define AVCODEC_H264DEC_H #include "libavutil/buffer.h" -#include "libavutil/intreadwrite.h" #include "libavutil/mem_internal.h" #include "cabac.h" @@ -44,7 +43,7 @@ #include "h264qpel.h" #include "h274.h" #include "mpegutils.h" -#include "rectangle.h" +#include "threadframe.h" #include "videodsp.h" #define H264_MAX_PICTURE_COUNT 36 @@ -110,20 +109,19 @@ typedef struct H264Picture { AVFrame *f_grain; - AVBufferRef *qscale_table_buf; + int8_t *qscale_table_base; ///< RefStruct reference int8_t *qscale_table; - AVBufferRef *motion_val_buf[2]; + int16_t (*motion_val_base[2])[2]; ///< RefStruct reference int16_t (*motion_val[2])[2]; - AVBufferRef *mb_type_buf; + uint32_t *mb_type_base; ///< RefStruct reference uint32_t *mb_type; - AVBufferRef *hwaccel_priv_buf; - void *hwaccel_picture_private; ///< hardware accelerator private data + /// RefStruct reference for hardware accelerator private data + void *hwaccel_picture_private; - AVBufferRef *ref_index_buf[2]; - int8_t *ref_index[2]; + int8_t *ref_index[2]; ///< RefStruct reference int field_poc[2]; ///< top/bottom POC int poc; ///< frame POC @@ -149,11 +147,15 @@ typedef struct H264Picture { int sei_recovery_frame_cnt; int needs_fg; ///< whether picture needs film grain synthesis (see `f_grain`) - AVBufferRef *pps_buf; const PPS *pps; int mb_width, mb_height; int mb_stride; + + /// RefStruct reference; its pointee is shared between decoding threads. + atomic_int *decode_error_flags; + + int gray; } H264Picture; typedef struct H264Ref { @@ -164,7 +166,7 @@ typedef struct H264Ref { int poc; int pic_id; - H264Picture *parent; + const H264Picture *parent; } H264Ref; typedef struct H264SliceContext { @@ -519,8 +521,22 @@ typedef struct H264Context { * so all the following frames in presentation order are correct. */ #define FRAME_RECOVERED_SEI (1 << 1) +/** + * Recovery point detected by heuristic + */ +#define FRAME_RECOVERED_HEURISTIC (1 << 2) - int frame_recovered; ///< Initial frame has been completely recovered + /** + * Initial frame has been completely recovered. + * + * Once this is set, all following decoded as well as displayed frames will be marked as recovered + * If a frame is marked as recovered frame_recovered will be set once this frame is output and thus + * all subsequently output fraames are also marked as recovered + * + * In effect, if you want all subsequent DECODED frames marked as recovered, set frame_recovered + * If you want all subsequent DISPAYED frames marked as recovered, set the frame->recovered + */ + int frame_recovered; int has_recovery_point; @@ -547,11 +563,16 @@ typedef struct H264Context { H264SEIContext sei; - AVBufferPool *qscale_table_pool; - AVBufferPool *mb_type_pool; - AVBufferPool *motion_val_pool; - AVBufferPool *ref_index_pool; + struct FFRefStructPool *qscale_table_pool; + struct FFRefStructPool *mb_type_pool; + struct FFRefStructPool *motion_val_pool; + struct FFRefStructPool *ref_index_pool; + struct FFRefStructPool *decode_error_flags_pool; int ref2frm[MAX_SLICES][2][64]; ///< reference to frame number lists, used in the loop filter, the first 2 are for -2,-1 + + int non_gray; ///< Did we encounter a intra frame after a gray gap frame + int noref_gray; + int skip_gray; } H264Context; extern const uint16_t ff_h264_mb_sizes[4]; @@ -649,141 +670,11 @@ static av_always_inline int get_chroma_qp(const PPS *pps, int t, int qscale) return pps->chroma_qp_table[t][qscale]; } -/** - * Get the predicted intra4x4 prediction mode. - */ -static av_always_inline int pred_intra_mode(const H264Context *h, - H264SliceContext *sl, int n) -{ - const int index8 = scan8[n]; - const int left = sl->intra4x4_pred_mode_cache[index8 - 1]; - const int top = sl->intra4x4_pred_mode_cache[index8 - 8]; - const int min = FFMIN(left, top); - - ff_tlog(h->avctx, "mode:%d %d min:%d\n", left, top, min); - - if (min < 0) - return DC_PRED; - else - return min; -} - -static av_always_inline void write_back_intra_pred_mode(const H264Context *h, - H264SliceContext *sl) -{ - int8_t *i4x4 = sl->intra4x4_pred_mode + h->mb2br_xy[sl->mb_xy]; - int8_t *i4x4_cache = sl->intra4x4_pred_mode_cache; - - AV_COPY32(i4x4, i4x4_cache + 4 + 8 * 4); - i4x4[4] = i4x4_cache[7 + 8 * 3]; - i4x4[5] = i4x4_cache[7 + 8 * 2]; - i4x4[6] = i4x4_cache[7 + 8 * 1]; -} - -static av_always_inline void write_back_non_zero_count(const H264Context *h, - H264SliceContext *sl) -{ - const int mb_xy = sl->mb_xy; - uint8_t *nnz = h->non_zero_count[mb_xy]; - uint8_t *nnz_cache = sl->non_zero_count_cache; - - AV_COPY32(&nnz[ 0], &nnz_cache[4 + 8 * 1]); - AV_COPY32(&nnz[ 4], &nnz_cache[4 + 8 * 2]); - AV_COPY32(&nnz[ 8], &nnz_cache[4 + 8 * 3]); - AV_COPY32(&nnz[12], &nnz_cache[4 + 8 * 4]); - AV_COPY32(&nnz[16], &nnz_cache[4 + 8 * 6]); - AV_COPY32(&nnz[20], &nnz_cache[4 + 8 * 7]); - AV_COPY32(&nnz[32], &nnz_cache[4 + 8 * 11]); - AV_COPY32(&nnz[36], &nnz_cache[4 + 8 * 12]); - - if (!h->chroma_y_shift) { - AV_COPY32(&nnz[24], &nnz_cache[4 + 8 * 8]); - AV_COPY32(&nnz[28], &nnz_cache[4 + 8 * 9]); - AV_COPY32(&nnz[40], &nnz_cache[4 + 8 * 13]); - AV_COPY32(&nnz[44], &nnz_cache[4 + 8 * 14]); - } -} - -static av_always_inline void write_back_motion_list(const H264Context *h, - H264SliceContext *sl, - int b_stride, - int b_xy, int b8_xy, - int mb_type, int list) -{ - int16_t(*mv_dst)[2] = &h->cur_pic.motion_val[list][b_xy]; - int16_t(*mv_src)[2] = &sl->mv_cache[list][scan8[0]]; - AV_COPY128(mv_dst + 0 * b_stride, mv_src + 8 * 0); - AV_COPY128(mv_dst + 1 * b_stride, mv_src + 8 * 1); - AV_COPY128(mv_dst + 2 * b_stride, mv_src + 8 * 2); - AV_COPY128(mv_dst + 3 * b_stride, mv_src + 8 * 3); - if (CABAC(h)) { - uint8_t (*mvd_dst)[2] = &sl->mvd_table[list][FMO ? 8 * sl->mb_xy - : h->mb2br_xy[sl->mb_xy]]; - uint8_t(*mvd_src)[2] = &sl->mvd_cache[list][scan8[0]]; - if (IS_SKIP(mb_type)) { - AV_ZERO128(mvd_dst); - } else { - AV_COPY64(mvd_dst, mvd_src + 8 * 3); - AV_COPY16(mvd_dst + 3 + 3, mvd_src + 3 + 8 * 0); - AV_COPY16(mvd_dst + 3 + 2, mvd_src + 3 + 8 * 1); - AV_COPY16(mvd_dst + 3 + 1, mvd_src + 3 + 8 * 2); - } - } - - { - int8_t *ref_index = &h->cur_pic.ref_index[list][b8_xy]; - int8_t *ref_cache = sl->ref_cache[list]; - ref_index[0 + 0 * 2] = ref_cache[scan8[0]]; - ref_index[1 + 0 * 2] = ref_cache[scan8[4]]; - ref_index[0 + 1 * 2] = ref_cache[scan8[8]]; - ref_index[1 + 1 * 2] = ref_cache[scan8[12]]; - } -} - -static av_always_inline void write_back_motion(const H264Context *h, - H264SliceContext *sl, - int mb_type) -{ - const int b_stride = h->b_stride; - const int b_xy = 4 * sl->mb_x + 4 * sl->mb_y * h->b_stride; // try mb2b(8)_xy - const int b8_xy = 4 * sl->mb_xy; - - if (USES_LIST(mb_type, 0)) { - write_back_motion_list(h, sl, b_stride, b_xy, b8_xy, mb_type, 0); - } else { - fill_rectangle(&h->cur_pic.ref_index[0][b8_xy], - 2, 2, 2, (uint8_t)LIST_NOT_USED, 1); - } - if (USES_LIST(mb_type, 1)) - write_back_motion_list(h, sl, b_stride, b_xy, b8_xy, mb_type, 1); - - if (sl->slice_type_nos == AV_PICTURE_TYPE_B && CABAC(h)) { - if (IS_8X8(mb_type)) { - uint8_t *direct_table = &h->direct_table[4 * sl->mb_xy]; - direct_table[1] = sl->sub_mb_type[1] >> 1; - direct_table[2] = sl->sub_mb_type[2] >> 1; - direct_table[3] = sl->sub_mb_type[3] >> 1; - } - } -} - -static av_always_inline int get_dct8x8_allowed(const H264Context *h, H264SliceContext *sl) -{ - if (h->ps.sps->direct_8x8_inference_flag) - return !(AV_RN64A(sl->sub_mb_type) & - ((MB_TYPE_16x8 | MB_TYPE_8x16 | MB_TYPE_8x8) * - 0x0001000100010001ULL)); - else - return !(AV_RN64A(sl->sub_mb_type) & - ((MB_TYPE_16x8 | MB_TYPE_8x16 | MB_TYPE_8x8 | MB_TYPE_DIRECT2) * - 0x0001000100010001ULL)); -} - int ff_h264_field_end(H264Context *h, H264SliceContext *sl, int in_setup); -int ff_h264_ref_picture(H264Context *h, H264Picture *dst, H264Picture *src); -int ff_h264_replace_picture(H264Context *h, H264Picture *dst, const H264Picture *src); -void ff_h264_unref_picture(H264Context *h, H264Picture *pic); +int ff_h264_ref_picture(H264Picture *dst, const H264Picture *src); +int ff_h264_replace_picture(H264Picture *dst, const H264Picture *src); +void ff_h264_unref_picture(H264Picture *pic); void ff_h264_slice_context_init(H264Context *h, H264SliceContext *sl); @@ -806,6 +697,6 @@ void ff_h264_flush_change(H264Context *h); void ff_h264_free_tables(H264Context *h); -void ff_h264_set_erpic(ERPicture *dst, H264Picture *src); +void ff_h264_set_erpic(ERPicture *dst, const H264Picture *src); #endif /* AVCODEC_H264DEC_H */ diff --git a/libavcodec/h264idct_template.c b/libavcodec/h264idct_template.c index ec0b428c275..db19b5f9fb0 100644 --- a/libavcodec/h264idct_template.c +++ b/libavcodec/h264idct_template.c @@ -27,7 +27,7 @@ #include "bit_depth_template.c" #include "libavutil/common.h" -#include "h264dec.h" +#include "h264_parse.h" #include "h264idct.h" void FUNCC(ff_h264_idct_add)(uint8_t *_dst, int16_t *_block, int stride) diff --git a/libavcodec/h264qpel_template.c b/libavcodec/h264qpel_template.c index 27c5b8f17fe..f7fabe4aaa7 100644 --- a/libavcodec/h264qpel_template.c +++ b/libavcodec/h264qpel_template.c @@ -26,7 +26,7 @@ #include "hpel_template.c" #include "pel_template.c" -static inline void FUNC(copy_block2)(uint8_t *dst, const uint8_t *src, int dstStride, int srcStride, int h) +static inline void FUNC(copy_block2)(uint8_t *dst, const uint8_t *restrict src, int dstStride, int srcStride, int h) { int i; for(i=0; i>= sizeof(pixel)-1;\ srcStride >>= sizeof(pixel)-1;\ for(i=0; i>= sizeof(pixel)-1;\ srcStride >>= sizeof(pixel)-1;\ for(i=0; i>= sizeof(pixel)-1;\ srcStride >>= sizeof(pixel)-1;\ src -= 2*srcStride;\ @@ -150,12 +153,13 @@ static av_unused void FUNC(OPNAME ## h264_qpel2_hv_lowpass)(uint8_t *_dst, pixel tmp++;\ }\ }\ -static void FUNC(OPNAME ## h264_qpel4_h_lowpass)(uint8_t *_dst, const uint8_t *_src, int dstStride, int srcStride){\ +static void FUNC(OPNAME ## h264_qpel4_h_lowpass)(uint8_t *_dst, const uint8_t *restrict _src, int dstStride, int srcStride)\ +{\ const int h=4;\ INIT_CLIP\ int i;\ pixel *dst = (pixel*)_dst;\ - const pixel *src = (const pixel*)_src;\ + const pixel *restrict src = (const pixel*)_src;\ dstStride >>= sizeof(pixel)-1;\ srcStride >>= sizeof(pixel)-1;\ for(i=0; i>= sizeof(pixel)-1;\ srcStride >>= sizeof(pixel)-1;\ for(i=0; i>= sizeof(pixel)-1;\ srcStride >>= sizeof(pixel)-1;\ src -= 2*srcStride;\ @@ -238,12 +244,13 @@ static void FUNC(OPNAME ## h264_qpel4_hv_lowpass)(uint8_t *_dst, pixeltmp *tmp, }\ }\ \ -static void FUNC(OPNAME ## h264_qpel8_h_lowpass)(uint8_t *_dst, const uint8_t *_src, int dstStride, int srcStride){\ +static void FUNC(OPNAME ## h264_qpel8_h_lowpass)(uint8_t *_dst, const uint8_t *restrict _src, int dstStride, int srcStride)\ +{\ const int h=8;\ INIT_CLIP\ int i;\ pixel *dst = (pixel*)_dst;\ - const pixel *src = (const pixel*)_src;\ + const pixel *restrict src = (const pixel*)_src;\ dstStride >>= sizeof(pixel)-1;\ srcStride >>= sizeof(pixel)-1;\ for(i=0; i>= sizeof(pixel)-1;\ srcStride >>= sizeof(pixel)-1;\ for(i=0; i>= sizeof(pixel)-1;\ srcStride >>= sizeof(pixel)-1;\ src -= 2*srcStride;\ @@ -350,7 +359,8 @@ static void FUNC(OPNAME ## h264_qpel8_hv_lowpass)(uint8_t *_dst, pixeltmp *tmp, }\ }\ \ -static void FUNC(OPNAME ## h264_qpel16_v_lowpass)(uint8_t *dst, const uint8_t *src, int dstStride, int srcStride){\ +static void FUNC(OPNAME ## h264_qpel16_v_lowpass)(uint8_t *dst, const uint8_t *restrict src, int dstStride, int srcStride)\ +{\ FUNC(OPNAME ## h264_qpel8_v_lowpass)(dst , src , dstStride, srcStride);\ FUNC(OPNAME ## h264_qpel8_v_lowpass)(dst+8*sizeof(pixel), src+8*sizeof(pixel), dstStride, srcStride);\ src += 8*srcStride;\ @@ -359,7 +369,8 @@ static void FUNC(OPNAME ## h264_qpel16_v_lowpass)(uint8_t *dst, const uint8_t *s FUNC(OPNAME ## h264_qpel8_v_lowpass)(dst+8*sizeof(pixel), src+8*sizeof(pixel), dstStride, srcStride);\ }\ \ -static void FUNC(OPNAME ## h264_qpel16_h_lowpass)(uint8_t *dst, const uint8_t *src, int dstStride, int srcStride){\ +static void FUNC(OPNAME ## h264_qpel16_h_lowpass)(uint8_t *dst, const uint8_t *restrict src, int dstStride, int srcStride)\ +{\ FUNC(OPNAME ## h264_qpel8_h_lowpass)(dst , src , dstStride, srcStride);\ FUNC(OPNAME ## h264_qpel8_h_lowpass)(dst+8*sizeof(pixel), src+8*sizeof(pixel), dstStride, srcStride);\ src += 8*srcStride;\ @@ -368,7 +379,7 @@ static void FUNC(OPNAME ## h264_qpel16_h_lowpass)(uint8_t *dst, const uint8_t *s FUNC(OPNAME ## h264_qpel8_h_lowpass)(dst+8*sizeof(pixel), src+8*sizeof(pixel), dstStride, srcStride);\ }\ \ -static void FUNC(OPNAME ## h264_qpel16_hv_lowpass)(uint8_t *dst, pixeltmp *tmp, const uint8_t *src, int dstStride, int tmpStride, int srcStride){\ +static void FUNC(OPNAME ## h264_qpel16_hv_lowpass)(uint8_t *dst, pixeltmp *tmp, const uint8_t *restrict src, int dstStride, int tmpStride, int srcStride){\ FUNC(OPNAME ## h264_qpel8_hv_lowpass)(dst , tmp , src , dstStride, tmpStride, srcStride);\ FUNC(OPNAME ## h264_qpel8_hv_lowpass)(dst+8*sizeof(pixel), tmp+8, src+8*sizeof(pixel), dstStride, tmpStride, srcStride);\ src += 8*srcStride;\ @@ -378,31 +389,31 @@ static void FUNC(OPNAME ## h264_qpel16_hv_lowpass)(uint8_t *dst, pixeltmp *tmp, }\ #define H264_MC(OPNAME, SIZE) \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc00)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc00)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ FUNCC(OPNAME ## pixels ## SIZE)(dst, src, stride, SIZE);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc10)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc10)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ uint8_t half[SIZE*SIZE*sizeof(pixel)];\ FUNC(put_h264_qpel ## SIZE ## _h_lowpass)(half, src, SIZE*sizeof(pixel), stride);\ FUNC(OPNAME ## pixels ## SIZE ## _l2)(dst, src, half, stride, stride, SIZE*sizeof(pixel), SIZE);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc20)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc20)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ FUNC(OPNAME ## h264_qpel ## SIZE ## _h_lowpass)(dst, src, stride, stride);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc30)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc30)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ uint8_t half[SIZE*SIZE*sizeof(pixel)];\ FUNC(put_h264_qpel ## SIZE ## _h_lowpass)(half, src, SIZE*sizeof(pixel), stride);\ FUNC(OPNAME ## pixels ## SIZE ## _l2)(dst, src+sizeof(pixel), half, stride, stride, SIZE*sizeof(pixel), SIZE);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc01)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc01)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ uint8_t full[SIZE*(SIZE+5)*sizeof(pixel)];\ uint8_t * const full_mid= full + SIZE*2*sizeof(pixel);\ @@ -412,7 +423,7 @@ static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc01)(uint8_t *dst, const uint FUNC(OPNAME ## pixels ## SIZE ## _l2)(dst, full_mid, half, stride, SIZE*sizeof(pixel), SIZE*sizeof(pixel), SIZE);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc02)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc02)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ uint8_t full[SIZE*(SIZE+5)*sizeof(pixel)];\ uint8_t * const full_mid= full + SIZE*2*sizeof(pixel);\ @@ -420,7 +431,7 @@ static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc02)(uint8_t *dst, const uint FUNC(OPNAME ## h264_qpel ## SIZE ## _v_lowpass)(dst, full_mid, stride, SIZE*sizeof(pixel));\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc03)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc03)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ uint8_t full[SIZE*(SIZE+5)*sizeof(pixel)];\ uint8_t * const full_mid= full + SIZE*2*sizeof(pixel);\ @@ -430,7 +441,7 @@ static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc03)(uint8_t *dst, const uint FUNC(OPNAME ## pixels ## SIZE ## _l2)(dst, full_mid+SIZE*sizeof(pixel), half, stride, SIZE*sizeof(pixel), SIZE*sizeof(pixel), SIZE);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc11)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc11)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ uint8_t full[SIZE*(SIZE+5)*sizeof(pixel)];\ uint8_t * const full_mid= full + SIZE*2*sizeof(pixel);\ @@ -442,7 +453,7 @@ static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc11)(uint8_t *dst, const uint FUNC(OPNAME ## pixels ## SIZE ## _l2)(dst, halfH, halfV, stride, SIZE*sizeof(pixel), SIZE*sizeof(pixel), SIZE);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc31)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc31)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ uint8_t full[SIZE*(SIZE+5)*sizeof(pixel)];\ uint8_t * const full_mid= full + SIZE*2*sizeof(pixel);\ @@ -454,7 +465,7 @@ static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc31)(uint8_t *dst, const uint FUNC(OPNAME ## pixels ## SIZE ## _l2)(dst, halfH, halfV, stride, SIZE*sizeof(pixel), SIZE*sizeof(pixel), SIZE);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc13)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc13)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ uint8_t full[SIZE*(SIZE+5)*sizeof(pixel)];\ uint8_t * const full_mid= full + SIZE*2*sizeof(pixel);\ @@ -466,7 +477,7 @@ static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc13)(uint8_t *dst, const uint FUNC(OPNAME ## pixels ## SIZE ## _l2)(dst, halfH, halfV, stride, SIZE*sizeof(pixel), SIZE*sizeof(pixel), SIZE);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc33)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc33)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ uint8_t full[SIZE*(SIZE+5)*sizeof(pixel)];\ uint8_t * const full_mid= full + SIZE*2*sizeof(pixel);\ @@ -478,13 +489,13 @@ static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc33)(uint8_t *dst, const uint FUNC(OPNAME ## pixels ## SIZE ## _l2)(dst, halfH, halfV, stride, SIZE*sizeof(pixel), SIZE*sizeof(pixel), SIZE);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc22)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc22)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ pixeltmp tmp[SIZE*(SIZE+5)*sizeof(pixel)];\ FUNC(OPNAME ## h264_qpel ## SIZE ## _hv_lowpass)(dst, tmp, src, stride, SIZE*sizeof(pixel), stride);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc21)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc21)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ pixeltmp tmp[SIZE*(SIZE+5)*sizeof(pixel)];\ uint8_t halfH[SIZE*SIZE*sizeof(pixel)];\ @@ -494,7 +505,7 @@ static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc21)(uint8_t *dst, const uint FUNC(OPNAME ## pixels ## SIZE ## _l2)(dst, halfH, halfHV, stride, SIZE*sizeof(pixel), SIZE*sizeof(pixel), SIZE);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc23)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc23)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ pixeltmp tmp[SIZE*(SIZE+5)*sizeof(pixel)];\ uint8_t halfH[SIZE*SIZE*sizeof(pixel)];\ @@ -504,7 +515,7 @@ static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc23)(uint8_t *dst, const uint FUNC(OPNAME ## pixels ## SIZE ## _l2)(dst, halfH, halfHV, stride, SIZE*sizeof(pixel), SIZE*sizeof(pixel), SIZE);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc12)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc12)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ uint8_t full[SIZE*(SIZE+5)*sizeof(pixel)];\ uint8_t * const full_mid= full + SIZE*2*sizeof(pixel);\ @@ -517,7 +528,7 @@ static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc12)(uint8_t *dst, const uint FUNC(OPNAME ## pixels ## SIZE ## _l2)(dst, halfV, halfHV, stride, SIZE*sizeof(pixel), SIZE*sizeof(pixel), SIZE);\ }\ \ -static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc32)(uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void FUNCC(OPNAME ## h264_qpel ## SIZE ## _mc32)(uint8_t *dst, const uint8_t *restrict src, ptrdiff_t stride)\ {\ uint8_t full[SIZE*(SIZE+5)*sizeof(pixel)];\ uint8_t * const full_mid= full + SIZE*2*sizeof(pixel);\ diff --git a/libavcodec/h26x/h2656_deblock_template.c b/libavcodec/h26x/h2656_deblock_template.c new file mode 100644 index 00000000000..8ed95e754d1 --- /dev/null +++ b/libavcodec/h26x/h2656_deblock_template.c @@ -0,0 +1,99 @@ + +/* + * HEVC/VVC deblocking dsp template + * + * Copyright (C) 2024 Nuo Mi + * Copyright (C) 2012 - 2013 Guillaume Martres + * + * 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 + */ + +static void FUNC(loop_filter_luma_strong)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, + const int32_t tc, const int32_t tc2, const int tc3, + const uint8_t no_p, const uint8_t no_q) +{ + for (int d = 0; d < 4; d++) { + const int p3 = P3; + const int p2 = P2; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + const int q3 = Q3; + if (!no_p) { + P0 = p0 + av_clip(((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3) - p0, -tc3, tc3); + P1 = p1 + av_clip(((p2 + p1 + p0 + q0 + 2) >> 2) - p1, -tc2, tc2); + P2 = p2 + av_clip(((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3) - p2, -tc, tc); + } + if (!no_q) { + Q0 = q0 + av_clip(((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3) - q0, -tc3, tc3); + Q1 = q1 + av_clip(((p0 + q0 + q1 + q2 + 2) >> 2) - q1, -tc2, tc2); + Q2 = q2 + av_clip(((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3) - q2, -tc, tc); + } + pix += ystride; + } +} + +static void FUNC(loop_filter_luma_weak)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, + const int32_t tc, const int32_t beta, const uint8_t no_p, const uint8_t no_q, const int nd_p, const int nd_q) +{ + const int tc_2 = tc >> 1; + for (int d = 0; d < 4; d++) { + const int p2 = P2; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + int delta0 = (9 * (q0 - p0) - 3 * (q1 - p1) + 8) >> 4; + if (abs(delta0) < 10 * tc) { + delta0 = av_clip(delta0, -tc, tc); + if (!no_p) + P0 = av_clip_pixel(p0 + delta0); + if (!no_q) + Q0 = av_clip_pixel(q0 - delta0); + if (!no_p && nd_p > 1) { + const int deltap1 = av_clip((((p2 + p0 + 1) >> 1) - p1 + delta0) >> 1, -tc_2, tc_2); + P1 = av_clip_pixel(p1 + deltap1); + } + if (!no_q && nd_q > 1) { + const int deltaq1 = av_clip((((q2 + q0 + 1) >> 1) - q1 - delta0) >> 1, -tc_2, tc_2); + Q1 = av_clip_pixel(q1 + deltaq1); + } + } + pix += ystride; + } +} + +static void FUNC(loop_filter_chroma_weak)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, + const int size, const int32_t tc, const uint8_t no_p, const uint8_t no_q) +{ + for (int d = 0; d < size; d++) { + int delta0; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + delta0 = av_clip((((q0 - p0) * 4) + p1 - q1 + 4) >> 3, -tc, tc); + if (!no_p) + P0 = av_clip_pixel(p0 + delta0); + if (!no_q) + Q0 = av_clip_pixel(q0 - delta0); + pix += ystride; + } +} diff --git a/libavcodec/h26x/h2656_inter_template.c b/libavcodec/h26x/h2656_inter_template.c new file mode 100644 index 00000000000..864f6c7e7dc --- /dev/null +++ b/libavcodec/h26x/h2656_inter_template.c @@ -0,0 +1,577 @@ +/* + * inter prediction template for HEVC/VVC + * + * Copyright (C) 2022 Nuo Mi + * Copyright (C) 2024 Wu Jianhua + * + * 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 + */ + +#define CHROMA_EXTRA_BEFORE 1 +#define CHROMA_EXTRA 3 +#define LUMA_EXTRA_BEFORE 3 +#define LUMA_EXTRA 7 + +static void FUNC(put_pixels)(int16_t *dst, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = src[x] << (14 - BIT_DEPTH); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_uni_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, + const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + + for (int y = 0; y < height; y++) { + memcpy(dst, src, width * sizeof(pixel)); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_w_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, + const int denom, const int wx, const int _ox, const int8_t *hf, const int8_t *vf, + const int width) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int v = (src[x] << (14 - BIT_DEPTH)); + dst[x] = av_clip_pixel(((v * wx + offset) >> shift) + ox); + } + src += src_stride; + dst += dst_stride; + } +} + +#define LUMA_FILTER(src, stride) \ + (filter[0] * src[x - 3 * stride] + \ + filter[1] * src[x - 2 * stride] + \ + filter[2] * src[x - stride] + \ + filter[3] * src[x ] + \ + filter[4] * src[x + stride] + \ + filter[5] * src[x + 2 * stride] + \ + filter[6] * src[x + 3 * stride] + \ + filter[7] * src[x + 4 * stride]) + +static void FUNC(put_luma_h)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = hf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_luma_v)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = vf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_luma_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = hf; + + src -= LUMA_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + LUMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = vf; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6; + tmp += MAX_PB_SIZE; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_uni_luma_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int val = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + dst[x] = av_clip_pixel((val + offset) >> shift); + } + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_luma_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = vf; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int val = LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8); + dst[x] = av_clip_pixel((val + offset) >> shift); + } + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_luma_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = hf; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= LUMA_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + LUMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = vf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int val = LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6; + dst[x] = av_clip_pixel((val + offset) >> shift); + } + tmp += MAX_PB_SIZE; + dst += dst_stride; + } + +} + +static void FUNC(put_uni_luma_w_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, int height, + const int denom, const int wx, const int _ox, const int8_t *hf, const int8_t *vf, + const int width) +{ + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((((LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_luma_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, + const int denom, const int wx, const int _ox, const int8_t *hf, const int8_t *vf, + const int width) +{ + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = vf; + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((((LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_luma_w_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, const int denom, + const int wx, const int _ox, const int8_t *hf, const int8_t *vf, const int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= LUMA_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + LUMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = vf; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((((LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); + tmp += MAX_PB_SIZE; + dst += dst_stride; + } +} + +#define CHROMA_FILTER(src, stride) \ + (filter[0] * src[x - stride] + \ + filter[1] * src[x] + \ + filter[2] * src[x + stride] + \ + filter[3] * src[x + 2 * stride]) + +static void FUNC(put_chroma_h)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = hf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_chroma_v)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = vf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8); + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_chroma_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = hf; + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (int y = 0; y < height + CHROMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = vf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6; + tmp += MAX_PB_SIZE; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(put_uni_chroma_h)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_chroma_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = vf; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) + offset) >> shift); + src += src_stride; + dst += dst_stride; + } +} + +static void FUNC(put_uni_chroma_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const int8_t *hf, const int8_t *vf, const int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (int y = 0; y < height + CHROMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = vf; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel(((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift); + tmp += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(put_uni_chroma_w_h)(uint8_t *_dst, ptrdiff_t _dst_stride, + const uint8_t *_src, ptrdiff_t _src_stride, int height, int denom, int wx, int ox, + const int8_t *hf, const int8_t *vf, int width) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + ox = ox * (1 << (BIT_DEPTH - 8)); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + dst[x] = av_clip_pixel((((CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); + } + dst += dst_stride; + src += src_stride; + } +} + +static void FUNC(put_uni_chroma_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const uint8_t *_src, const ptrdiff_t _src_stride, const int height, + const int denom, const int wx, const int _ox, const int8_t *hf, const int8_t *vf, + const int width) +{ + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = vf; + const int shift = denom + 14 - BIT_DEPTH; + const int ox = _ox * (1 << (BIT_DEPTH - 8)); +#if BIT_DEPTH < 14 + int offset = 1 << (shift - 1); +#else + int offset = 0; +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + dst[x] = av_clip_pixel((((CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); + } + dst += dst_stride; + src += src_stride; + } +} + +static void FUNC(put_uni_chroma_w_hv)(uint8_t *_dst, ptrdiff_t _dst_stride, + const uint8_t *_src, ptrdiff_t _src_stride, int height, int denom, int wx, int ox, + const int8_t *hf, const int8_t *vf, int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel *)_src; + pixel *dst = (pixel *)_dst; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int8_t *filter = hf; + const int shift = denom + 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + + src -= CHROMA_EXTRA_BEFORE * src_stride; + + for (int y = 0; y < height + CHROMA_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8); + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE; + filter = vf; + + ox = ox * (1 << (BIT_DEPTH - 8)); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); + tmp += MAX_PB_SIZE; + dst += dst_stride; + } +} diff --git a/libavcodec/h26x/h2656_sao_template.c b/libavcodec/h26x/h2656_sao_template.c new file mode 100644 index 00000000000..b3eb8a3deb5 --- /dev/null +++ b/libavcodec/h26x/h2656_sao_template.c @@ -0,0 +1,217 @@ +/* + * HEVC/VVC SAO template + * + * Copyright (C) 2024 Nuo Mi + * Copyright (C) 2012 - 2013 Guillaume Martres + * + * 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 + */ + +static void FUNC(sao_band_filter)(uint8_t *_dst, const uint8_t *_src, + ptrdiff_t stride_dst, ptrdiff_t stride_src, + const int16_t *sao_offset_val, int sao_left_class, + int width, int height) +{ + pixel *dst = (pixel *)_dst; + const pixel *src = (const pixel *)_src; + int offset_table[32] = { 0 }; + int k, y, x; + int shift = BIT_DEPTH - 5; + + stride_dst /= sizeof(pixel); + stride_src /= sizeof(pixel); + + for (k = 0; k < 4; k++) + offset_table[(k + sao_left_class) & 31] = sao_offset_val[k + 1]; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) + dst[x] = av_clip_pixel(src[x] + offset_table[(src[x] >> shift) & 31]); + dst += stride_dst; + src += stride_src; + } +} + +#define CMP(a, b) (((a) > (b)) - ((a) < (b))) + +static void FUNC(sao_edge_filter)(uint8_t *_dst, const uint8_t *_src, ptrdiff_t stride_dst, const int16_t *sao_offset_val, + int eo, int width, int height) { + + static const uint8_t edge_idx[] = { 1, 2, 0, 3, 4 }; + static const int8_t pos[4][2][2] = { + { { -1, 0 }, { 1, 0 } }, // horizontal + { { 0, -1 }, { 0, 1 } }, // vertical + { { -1, -1 }, { 1, 1 } }, // 45 degree + { { 1, -1 }, { -1, 1 } }, // 135 degree + }; + pixel *dst = (pixel *)_dst; + const pixel *src = (const pixel *)_src; + int a_stride, b_stride; + int x, y; + ptrdiff_t stride_src = (2*MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE) / sizeof(pixel); + stride_dst /= sizeof(pixel); + + a_stride = pos[eo][0][0] + pos[eo][0][1] * stride_src; + b_stride = pos[eo][1][0] + pos[eo][1][1] * stride_src; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + int diff0 = CMP(src[x], src[x + a_stride]); + int diff1 = CMP(src[x], src[x + b_stride]); + int offset_val = edge_idx[2 + diff0 + diff1]; + dst[x] = av_clip_pixel(src[x] + sao_offset_val[offset_val]); + } + src += stride_src; + dst += stride_dst; + } +} + +static void FUNC(sao_edge_restore_0)(uint8_t *_dst, const uint8_t *_src, + ptrdiff_t stride_dst, ptrdiff_t stride_src, const SAOParams *sao, + const int *borders, int _width, int _height, + int c_idx, const uint8_t *vert_edge, + const uint8_t *horiz_edge, const uint8_t *diag_edge) +{ + int x, y; + pixel *dst = (pixel *)_dst; + const pixel *src = (const pixel *)_src; + const int16_t *sao_offset_val = sao->offset_val[c_idx]; + int sao_eo_class = sao->eo_class[c_idx]; + int init_x = 0, width = _width, height = _height; + + stride_dst /= sizeof(pixel); + stride_src /= sizeof(pixel); + + if (sao_eo_class != SAO_EO_VERT) { + if (borders[0]) { + int offset_val = sao_offset_val[0]; + for (y = 0; y < height; y++) { + dst[y * stride_dst] = av_clip_pixel(src[y * stride_src] + offset_val); + } + init_x = 1; + } + if (borders[2]) { + int offset_val = sao_offset_val[0]; + int offset = width - 1; + for (x = 0; x < height; x++) { + dst[x * stride_dst + offset] = av_clip_pixel(src[x * stride_src + offset] + offset_val); + } + width--; + } + } + if (sao_eo_class != SAO_EO_HORIZ) { + if (borders[1]) { + int offset_val = sao_offset_val[0]; + for (x = init_x; x < width; x++) + dst[x] = av_clip_pixel(src[x] + offset_val); + } + if (borders[3]) { + int offset_val = sao_offset_val[0]; + ptrdiff_t y_stride_dst = stride_dst * (height - 1); + ptrdiff_t y_stride_src = stride_src * (height - 1); + for (x = init_x; x < width; x++) + dst[x + y_stride_dst] = av_clip_pixel(src[x + y_stride_src] + offset_val); + height--; + } + } +} + +static void FUNC(sao_edge_restore_1)(uint8_t *_dst, const uint8_t *_src, + ptrdiff_t stride_dst, ptrdiff_t stride_src, const SAOParams *sao, + const int *borders, int _width, int _height, + int c_idx, const uint8_t *vert_edge, + const uint8_t *horiz_edge, const uint8_t *diag_edge) +{ + int x, y; + pixel *dst = (pixel *)_dst; + const pixel *src = (const pixel *)_src; + const int16_t *sao_offset_val = sao->offset_val[c_idx]; + int sao_eo_class = sao->eo_class[c_idx]; + int init_x = 0, init_y = 0, width = _width, height = _height; + + stride_dst /= sizeof(pixel); + stride_src /= sizeof(pixel); + + if (sao_eo_class != SAO_EO_VERT) { + if (borders[0]) { + int offset_val = sao_offset_val[0]; + for (y = 0; y < height; y++) { + dst[y * stride_dst] = av_clip_pixel(src[y * stride_src] + offset_val); + } + init_x = 1; + } + if (borders[2]) { + int offset_val = sao_offset_val[0]; + int offset = width - 1; + for (x = 0; x < height; x++) { + dst[x * stride_dst + offset] = av_clip_pixel(src[x * stride_src + offset] + offset_val); + } + width--; + } + } + if (sao_eo_class != SAO_EO_HORIZ) { + if (borders[1]) { + int offset_val = sao_offset_val[0]; + for (x = init_x; x < width; x++) + dst[x] = av_clip_pixel(src[x] + offset_val); + init_y = 1; + } + if (borders[3]) { + int offset_val = sao_offset_val[0]; + ptrdiff_t y_stride_dst = stride_dst * (height - 1); + ptrdiff_t y_stride_src = stride_src * (height - 1); + for (x = init_x; x < width; x++) + dst[x + y_stride_dst] = av_clip_pixel(src[x + y_stride_src] + offset_val); + height--; + } + } + + { + int save_upper_left = !diag_edge[0] && sao_eo_class == SAO_EO_135D && !borders[0] && !borders[1]; + int save_upper_right = !diag_edge[1] && sao_eo_class == SAO_EO_45D && !borders[1] && !borders[2]; + int save_lower_right = !diag_edge[2] && sao_eo_class == SAO_EO_135D && !borders[2] && !borders[3]; + int save_lower_left = !diag_edge[3] && sao_eo_class == SAO_EO_45D && !borders[0] && !borders[3]; + + // Restore pixels that can't be modified + if(vert_edge[0] && sao_eo_class != SAO_EO_VERT) { + for(y = init_y+save_upper_left; y< height-save_lower_left; y++) + dst[y*stride_dst] = src[y*stride_src]; + } + if(vert_edge[1] && sao_eo_class != SAO_EO_VERT) { + for(y = init_y+save_upper_right; y< height-save_lower_right; y++) + dst[y*stride_dst+width-1] = src[y*stride_src+width-1]; + } + + if(horiz_edge[0] && sao_eo_class != SAO_EO_HORIZ) { + for(x = init_x+save_upper_left; x < width-save_upper_right; x++) + dst[x] = src[x]; + } + if(horiz_edge[1] && sao_eo_class != SAO_EO_HORIZ) { + for(x = init_x+save_lower_left; x < width-save_lower_right; x++) + dst[(height-1)*stride_dst+x] = src[(height-1)*stride_src+x]; + } + if(diag_edge[0] && sao_eo_class == SAO_EO_135D) + dst[0] = src[0]; + if(diag_edge[1] && sao_eo_class == SAO_EO_45D) + dst[width-1] = src[width-1]; + if(diag_edge[2] && sao_eo_class == SAO_EO_135D) + dst[stride_dst*(height-1)+width-1] = src[stride_src*(height-1)+width-1]; + if(diag_edge[3] && sao_eo_class == SAO_EO_45D) + dst[stride_dst*(height-1)] = src[stride_src*(height-1)]; + + } +} + +#undef CMP diff --git a/libavcodec/h274.c b/libavcodec/h274.c index a69f9411429..5709200322e 100644 --- a/libavcodec/h274.c +++ b/libavcodec/h274.c @@ -38,7 +38,7 @@ static void prng_shift(uint32_t *state) { // Primitive polynomial x^31 + x^3 + 1 (modulo 2) uint32_t x = *state; - uint8_t feedback = (x >> 2) ^ (x >> 30); + uint8_t feedback = 1u ^ (x >> 2) ^ (x >> 30); *state = (x << 1) | (feedback & 1u); } @@ -59,13 +59,13 @@ static void init_slice_c(int8_t out[64][64], uint8_t h, uint8_t v, // // Note: To make the subsequent matrix multiplication cache friendlier, we // store each *column* of the starting image in a *row* of `out` - for (int y = 0; y <= freq_v; y++) { - for (int x = 0; x <= freq_h; x += 4) { + for (int l = 0; l <= freq_v; l++) { + for (int k = 0; k <= freq_h; k += 4) { uint16_t offset = seed % 2048; - out[x + 0][y] = Gaussian_LUT[offset + 0]; - out[x + 1][y] = Gaussian_LUT[offset + 1]; - out[x + 2][y] = Gaussian_LUT[offset + 2]; - out[x + 3][y] = Gaussian_LUT[offset + 3]; + out[l][k + 0] = Gaussian_LUT[offset + 0]; + out[l][k + 1] = Gaussian_LUT[offset + 1]; + out[l][k + 2] = Gaussian_LUT[offset + 2]; + out[l][k + 3] = Gaussian_LUT[offset + 3]; prng_shift(&seed); } } @@ -74,9 +74,9 @@ static void init_slice_c(int8_t out[64][64], uint8_t h, uint8_t v, // 64x64 inverse integer transform for (int y = 0; y < 64; y++) { - for (int x = 0; x <= freq_h; x++) { + for (int x = 0; x <= freq_v; x++) { int32_t sum = 0; - for (int p = 0; p <= freq_v; p++) + for (int p = 0; p <= freq_h; p++) sum += R64T[y][p] * out[x][p]; tmp[y][x] = (sum + 128) >> 8; } @@ -85,8 +85,8 @@ static void init_slice_c(int8_t out[64][64], uint8_t h, uint8_t v, for (int y = 0; y < 64; y++) { for (int x = 0; x < 64; x++) { int32_t sum = 0; - for (int p = 0; p <= freq_h; p++) - sum += tmp[y][p] * R64T[x][p]; // R64T^T = R64 + for (int p = 0; p <= freq_v; p++) + sum += tmp[x][p] * R64T[y][p]; // R64T^T = R64 // Renormalize and clip to [-127, 127] out[y][x] = av_clip((sum + 128) >> 8, -127, 127); } @@ -110,7 +110,7 @@ static void init_slice(H274FilmGrainDatabase *database, uint8_t h, uint8_t v) init_slice_c(database->db[h][v], h, v, database->slice_tmp); } -// Computes the average of an 8x8 block, right-shifted by 6 +// Computes the average of an 8x8 block static uint16_t avg_8x8_c(const uint8_t *in, int in_stride) { uint16_t avg[8] = {0}; // summing over an array vectorizes better @@ -259,11 +259,11 @@ int ff_h274_apply_film_grain(AVFrame *out_frame, const AVFrame *in_frame, // only advanced in 16x16 blocks, so use a nested loop for (int y = 0; y < height; y += 16) { for (int x = 0; x < width; x += 16) { - uint16_t y_offset = (seed >> 16) % 52; - uint16_t x_offset = (seed & 0xFFFF) % 56; + uint16_t x_offset = (seed >> 16) % 52; + uint16_t y_offset = (seed & 0xFFFF) % 56; const int invert = (seed & 0x1); - y_offset &= 0xFFFC; - x_offset &= 0xFFF8; + x_offset &= 0xFFFC; + y_offset &= 0xFFF8; prng_shift(&seed); for (int yy = 0; yy < 16 && y+yy < height; yy += 8) { diff --git a/libavcodec/h274.h b/libavcodec/h274.h index 920f6991fb5..cebc8becb37 100644 --- a/libavcodec/h274.h +++ b/libavcodec/h274.h @@ -40,11 +40,26 @@ typedef struct H274FilmGrainDatabase { int16_t slice_tmp[64][64]; } H274FilmGrainDatabase; +/** + * Check whether ff_h274_apply_film_grain() supports the given parameter combination. + * + * @param model_id model_id from AVFilmGrainParams to be supplied + * @param pix_fmt pixel format of the frames to be supplied + */ +static inline int ff_h274_film_grain_params_supported(int model_id, enum AVPixelFormat pix_fmt) +{ + return model_id == 0 && pix_fmt == AV_PIX_FMT_YUV420P; +} + // Synthesizes film grain on top of `in` and stores the result to `out`. `out` // must already have been allocated and set to the same size and format as // `in`. // // Returns a negative error code on error, such as invalid params. +// If ff_h274_film_grain_params_supported() indicated that the parameters +// are supported, no error will be returned if the arguments given to +// ff_h274_film_grain_params_supported() coincide with actual values +// from the frames and params. int ff_h274_apply_film_grain(AVFrame *out, const AVFrame *in, H274FilmGrainDatabase *db, const AVFilmGrainParams *params); diff --git a/libavcodec/hap.h b/libavcodec/hap.h index fb5a4c41238..1de6d454288 100644 --- a/libavcodec/hap.h +++ b/libavcodec/hap.h @@ -23,10 +23,9 @@ #ifndef AVCODEC_HAP_H #define AVCODEC_HAP_H +#include #include -#include "libavutil/opt.h" - #include "bytestream.h" #include "texturedsp.h" @@ -59,9 +58,8 @@ typedef struct HapChunk { } HapChunk; typedef struct HapContext { - AVClass *class; + const struct AVClass *class; - TextureDSPContext dxtc; GetByteContext gbc; enum HapTextureFormat opt_tex_fmt; /* Texture type (encoder only) */ diff --git a/libavcodec/hapdec.c b/libavcodec/hapdec.c index 3df69e63357..6066cb814cd 100644 --- a/libavcodec/hapdec.c +++ b/libavcodec/hapdec.c @@ -323,12 +323,14 @@ static int hap_decode(AVCodecContext *avctx, AVFrame *frame, ctx->dec[t].frame_data.out = frame->data[0]; ctx->dec[t].stride = frame->linesize[0]; - avctx->execute2(avctx, ff_texturedsp_decompress_thread, &ctx->dec[t], NULL, ctx->dec[t].slice_count); + ctx->dec[t].width = avctx->coded_width; + ctx->dec[t].height = avctx->coded_height; + ff_texturedsp_exec_decompress_threads(avctx, &ctx->dec[t]); } /* Frame is ready to be output */ frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; @@ -337,6 +339,7 @@ static int hap_decode(AVCodecContext *avctx, AVFrame *frame, static av_cold int hap_init(AVCodecContext *avctx) { HapContext *ctx = avctx->priv_data; + TextureDSPContext dxtc; const char *texture_name; int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx); @@ -350,7 +353,7 @@ static av_cold int hap_init(AVCodecContext *avctx) avctx->coded_width = FFALIGN(avctx->width, TEXTURE_BLOCK_W); avctx->coded_height = FFALIGN(avctx->height, TEXTURE_BLOCK_H); - ff_texturedsp_init(&ctx->dxtc); + ff_texturedsp_init(&dxtc); ctx->texture_count = 1; ctx->dec[0].raw_ratio = 16; @@ -361,25 +364,25 @@ static av_cold int hap_init(AVCodecContext *avctx) case MKTAG('H','a','p','1'): texture_name = "DXT1"; ctx->dec[0].tex_ratio = 8; - ctx->dec[0].tex_funct = ctx->dxtc.dxt1_block; + ctx->dec[0].tex_funct = dxtc.dxt1_block; avctx->pix_fmt = AV_PIX_FMT_RGB0; break; case MKTAG('H','a','p','5'): texture_name = "DXT5"; ctx->dec[0].tex_ratio = 16; - ctx->dec[0].tex_funct = ctx->dxtc.dxt5_block; + ctx->dec[0].tex_funct = dxtc.dxt5_block; avctx->pix_fmt = AV_PIX_FMT_RGBA; break; case MKTAG('H','a','p','Y'): texture_name = "DXT5-YCoCg-scaled"; ctx->dec[0].tex_ratio = 16; - ctx->dec[0].tex_funct = ctx->dxtc.dxt5ys_block; + ctx->dec[0].tex_funct = dxtc.dxt5ys_block; avctx->pix_fmt = AV_PIX_FMT_RGB0; break; case MKTAG('H','a','p','A'): texture_name = "RGTC1"; ctx->dec[0].tex_ratio = 8; - ctx->dec[0].tex_funct = ctx->dxtc.rgtc1u_gray_block; + ctx->dec[0].tex_funct = dxtc.rgtc1u_gray_block; ctx->dec[0].raw_ratio = 4; avctx->pix_fmt = AV_PIX_FMT_GRAY8; break; @@ -387,8 +390,8 @@ static av_cold int hap_init(AVCodecContext *avctx) texture_name = "DXT5-YCoCg-scaled / RGTC1"; ctx->dec[0].tex_ratio = 16; ctx->dec[1].tex_ratio = 8; - ctx->dec[0].tex_funct = ctx->dxtc.dxt5ys_block; - ctx->dec[1].tex_funct = ctx->dxtc.rgtc1u_alpha_block; + ctx->dec[0].tex_funct = dxtc.dxt5ys_block; + ctx->dec[1].tex_funct = dxtc.rgtc1u_alpha_block; ctx->dec[1].raw_ratio = 16; ctx->dec[1].slice_count = ctx->dec[0].slice_count; avctx->pix_fmt = AV_PIX_FMT_RGBA; diff --git a/libavcodec/hapenc.c b/libavcodec/hapenc.c index 7de7358e3d9..3f74098d130 100644 --- a/libavcodec/hapenc.c +++ b/libavcodec/hapenc.c @@ -63,7 +63,9 @@ static int compress_texture(AVCodecContext *avctx, uint8_t *out, int out_length, ctx->enc.tex_data.out = out; ctx->enc.frame_data.in = f->data[0]; ctx->enc.stride = f->linesize[0]; - avctx->execute2(avctx, ff_texturedsp_compress_thread, &ctx->enc, NULL, ctx->enc.slice_count); + ctx->enc.width = avctx->width; + ctx->enc.height = avctx->height; + ff_texturedsp_exec_compress_threads(avctx, &ctx->enc); return 0; } @@ -232,6 +234,7 @@ static int hap_encode(AVCodecContext *avctx, AVPacket *pkt, static av_cold int hap_init(AVCodecContext *avctx) { HapContext *ctx = avctx->priv_data; + TextureDSPEncContext dxtc; int corrected_chunk_count; int ret = av_image_check_size(avctx->width, avctx->height, 0, avctx); @@ -247,26 +250,26 @@ static av_cold int hap_init(AVCodecContext *avctx) return AVERROR_INVALIDDATA; } - ff_texturedspenc_init(&ctx->dxtc); + ff_texturedspenc_init(&dxtc); switch (ctx->opt_tex_fmt) { case HAP_FMT_RGBDXT1: ctx->enc.tex_ratio = 8; avctx->codec_tag = MKTAG('H', 'a', 'p', '1'); avctx->bits_per_coded_sample = 24; - ctx->enc.tex_funct = ctx->dxtc.dxt1_block; + ctx->enc.tex_funct = dxtc.dxt1_block; break; case HAP_FMT_RGBADXT5: ctx->enc.tex_ratio = 16; avctx->codec_tag = MKTAG('H', 'a', 'p', '5'); avctx->bits_per_coded_sample = 32; - ctx->enc.tex_funct = ctx->dxtc.dxt5_block; + ctx->enc.tex_funct = dxtc.dxt5_block; break; case HAP_FMT_YCOCGDXT5: ctx->enc.tex_ratio = 16; avctx->codec_tag = MKTAG('H', 'a', 'p', 'Y'); avctx->bits_per_coded_sample = 24; - ctx->enc.tex_funct = ctx->dxtc.dxt5ys_block; + ctx->enc.tex_funct = dxtc.dxt5ys_block; break; default: av_log(avctx, AV_LOG_ERROR, "Invalid format %02X\n", ctx->opt_tex_fmt); @@ -328,14 +331,14 @@ static av_cold int hap_close(AVCodecContext *avctx) #define OFFSET(x) offsetof(HapContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "format", NULL, OFFSET(opt_tex_fmt), AV_OPT_TYPE_INT, { .i64 = HAP_FMT_RGBDXT1 }, HAP_FMT_RGBDXT1, HAP_FMT_YCOCGDXT5, FLAGS, "format" }, - { "hap", "Hap 1 (DXT1 textures)", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_FMT_RGBDXT1 }, 0, 0, FLAGS, "format" }, - { "hap_alpha", "Hap Alpha (DXT5 textures)", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_FMT_RGBADXT5 }, 0, 0, FLAGS, "format" }, - { "hap_q", "Hap Q (DXT5-YCoCg textures)", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_FMT_YCOCGDXT5 }, 0, 0, FLAGS, "format" }, + { "format", NULL, OFFSET(opt_tex_fmt), AV_OPT_TYPE_INT, { .i64 = HAP_FMT_RGBDXT1 }, HAP_FMT_RGBDXT1, HAP_FMT_YCOCGDXT5, FLAGS, .unit = "format" }, + { "hap", "Hap 1 (DXT1 textures)", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_FMT_RGBDXT1 }, 0, 0, FLAGS, .unit = "format" }, + { "hap_alpha", "Hap Alpha (DXT5 textures)", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_FMT_RGBADXT5 }, 0, 0, FLAGS, .unit = "format" }, + { "hap_q", "Hap Q (DXT5-YCoCg textures)", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_FMT_YCOCGDXT5 }, 0, 0, FLAGS, .unit = "format" }, { "chunks", "chunk count", OFFSET(opt_chunk_count), AV_OPT_TYPE_INT, {.i64 = 1 }, 1, HAP_MAX_CHUNKS, FLAGS, }, - { "compressor", "second-stage compressor", OFFSET(opt_compressor), AV_OPT_TYPE_INT, { .i64 = HAP_COMP_SNAPPY }, HAP_COMP_NONE, HAP_COMP_SNAPPY, FLAGS, "compressor" }, - { "none", "None", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_COMP_NONE }, 0, 0, FLAGS, "compressor" }, - { "snappy", "Snappy", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_COMP_SNAPPY }, 0, 0, FLAGS, "compressor" }, + { "compressor", "second-stage compressor", OFFSET(opt_compressor), AV_OPT_TYPE_INT, { .i64 = HAP_COMP_SNAPPY }, HAP_COMP_NONE, HAP_COMP_SNAPPY, FLAGS, .unit = "compressor" }, + { "none", "None", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_COMP_NONE }, 0, 0, FLAGS, .unit = "compressor" }, + { "snappy", "Snappy", 0, AV_OPT_TYPE_CONST, { .i64 = HAP_COMP_SNAPPY }, 0, 0, FLAGS, .unit = "compressor" }, { NULL }, }; diff --git a/libavcodec/hcadec.c b/libavcodec/hcadec.c index 2f019a82146..88146c7cdd3 100644 --- a/libavcodec/hcadec.c +++ b/libavcodec/hcadec.c @@ -28,8 +28,12 @@ #include "get_bits.h" #include "hca_data.h" +#define HCA_MASK 0x7f7f7f7f +#define MAX_CHANNELS 16 + typedef struct ChannelContext { - float base[128]; + DECLARE_ALIGNED(32, float, base)[128]; + DECLARE_ALIGNED(32, float, factors)[128]; DECLARE_ALIGNED(32, float, imdct_in)[128]; DECLARE_ALIGNED(32, float, imdct_out)[128]; DECLARE_ALIGNED(32, float, imdct_prev)[128]; @@ -44,11 +48,15 @@ typedef struct ChannelContext { typedef struct HCAContext { const AVCRC *crc_table; - ChannelContext ch[16]; + ChannelContext ch[MAX_CHANNELS]; uint8_t ath[128]; + uint8_t cipher[256]; + uint64_t key; + uint16_t subkey; int ath_type; + int ciph_type; unsigned hfr_group_count; uint8_t track_count; uint8_t channel_config; @@ -57,11 +65,99 @@ typedef struct HCAContext { uint8_t stereo_band_count; uint8_t bands_per_hfr_group; + // Set during init() and freed on close(). Untouched on init_flush() av_tx_fn tx_fn; AVTXContext *tx_ctx; AVFloatDSPContext *fdsp; } HCAContext; +static void cipher_init56_create_table(uint8_t *r, uint8_t key) +{ + const int mul = ((key & 1) << 3) | 5; + const int add = (key & 0xE) | 1; + + key >>= 4; + for (int i = 0; i < 16; i++) { + key = (key * mul + add) & 0xF; + r[i] = key; + } +} + +static void cipher_init56(uint8_t *cipher, uint64_t keycode) +{ + uint8_t base[256], base_r[16], base_c[16], kc[8], seed[16]; + + /* 56bit keycode encryption (given as a uint64_t number, but upper 8b aren't used) */ + /* keycode = keycode - 1 */ + if (keycode != 0) + keycode--; + + /* init keycode table */ + for (int r = 0; r < (8-1); r++) { + kc[r] = keycode & 0xFF; + keycode = keycode >> 8; + } + + /* init seed table */ + seed[ 0] = kc[1]; + seed[ 1] = kc[1] ^ kc[6]; + seed[ 2] = kc[2] ^ kc[3]; + seed[ 3] = kc[2]; + seed[ 4] = kc[2] ^ kc[1]; + seed[ 5] = kc[3] ^ kc[4]; + seed[ 6] = kc[3]; + seed[ 7] = kc[3] ^ kc[2]; + seed[ 8] = kc[4] ^ kc[5]; + seed[ 9] = kc[4]; + seed[10] = kc[4] ^ kc[3]; + seed[11] = kc[5] ^ kc[6]; + seed[12] = kc[5]; + seed[13] = kc[5] ^ kc[4]; + seed[14] = kc[6] ^ kc[1]; + seed[15] = kc[6]; + + /* init base table */ + cipher_init56_create_table(base_r, kc[0]); + for (int r = 0; r < 16; r++) { + uint8_t nb; + cipher_init56_create_table(base_c, seed[r]); + nb = base_r[r] << 4; + for (int c = 0; c < 16; c++) + base[r*16 + c] = nb | base_c[c]; /* combine nibbles */ + } + + /* final shuffle table */ + { + unsigned x = 0; + unsigned pos = 1; + + for (int i = 0; i < 256; i++) { + x = (x + 17) & 0xFF; + if (base[x] != 0 && base[x] != 0xFF) + cipher[pos++] = base[x]; + } + cipher[0] = 0; + cipher[0xFF] = 0xFF; + } +} + +static void cipher_init(uint8_t *cipher, int type, uint64_t keycode, uint16_t subkey) +{ + switch (type) { + case 56: + if (keycode) { + if (subkey) + keycode = keycode * (((uint64_t)subkey<<16u)|((uint16_t)~subkey+2u)); + cipher_init56(cipher, keycode); + } + break; + case 0: + for (int i = 0; i < 256; i++) + cipher[i] = i; + break; + } +} + static void ath_init1(uint8_t *ath, int sample_rate) { unsigned int index; @@ -101,24 +197,29 @@ static inline unsigned ceil2(unsigned a, unsigned b) return (b > 0) ? (a / b + ((a % b) ? 1 : 0)) : 0; } -static av_cold int decode_init(AVCodecContext *avctx) +static av_cold void init_flush(AVCodecContext *avctx) +{ + HCAContext *c = avctx->priv_data; + + memset(c, 0, offsetof(HCAContext, tx_fn)); +} + +static int init_hca(AVCodecContext *avctx, const uint8_t *extradata, + const int extradata_size) { HCAContext *c = avctx->priv_data; GetByteContext gb0, *const gb = &gb0; int8_t r[16] = { 0 }; - float scale = 1.f / 8.f; unsigned b, chunk; int version, ret; + unsigned hfr_group_count; - avctx->sample_fmt = AV_SAMPLE_FMT_FLTP; - c->crc_table = av_crc_get_table(AV_CRC_16_ANSI); - - if (avctx->ch_layout.nb_channels <= 0 || avctx->ch_layout.nb_channels > 16) - return AVERROR(EINVAL); + init_flush(avctx); - if (avctx->extradata_size < 36) + if (extradata_size < 36) return AVERROR_INVALIDDATA; - bytestream2_init(gb, avctx->extradata, avctx->extradata_size); + + bytestream2_init(gb, extradata, extradata_size); bytestream2_skipu(gb, 4); version = bytestream2_get_be16(gb); @@ -126,13 +227,13 @@ static av_cold int decode_init(AVCodecContext *avctx) c->ath_type = version >= 0x200 ? 0 : 1; - if (bytestream2_get_be32u(gb) != MKBETAG('f', 'm', 't', 0)) + if ((bytestream2_get_be32u(gb) & HCA_MASK) != MKBETAG('f', 'm', 't', 0)) return AVERROR_INVALIDDATA; bytestream2_skipu(gb, 4); bytestream2_skipu(gb, 4); bytestream2_skipu(gb, 4); - chunk = bytestream2_get_be32u(gb); + chunk = bytestream2_get_be32u(gb) & HCA_MASK; if (chunk == MKBETAG('c', 'o', 'm', 'p')) { bytestream2_skipu(gb, 2); bytestream2_skipu(gb, 1); @@ -161,9 +262,8 @@ static av_cold int decode_init(AVCodecContext *avctx) if (c->total_band_count > FF_ARRAY_ELEMS(c->ch->imdct_in)) return AVERROR_INVALIDDATA; - while (bytestream2_get_bytes_left(gb) >= 4) { - chunk = bytestream2_get_be32u(gb); + chunk = bytestream2_get_be32u(gb) & HCA_MASK; if (chunk == MKBETAG('v', 'b', 'r', 0)) { bytestream2_skip(gb, 2 + 2); } else if (chunk == MKBETAG('a', 't', 'h', 0)) { @@ -173,7 +273,7 @@ static av_cold int decode_init(AVCodecContext *avctx) } else if (chunk == MKBETAG('c', 'o', 'm', 'm')) { bytestream2_skip(gb, bytestream2_get_byte(gb) * 8); } else if (chunk == MKBETAG('c', 'i', 'p', 'h')) { - bytestream2_skip(gb, 2); + c->ciph_type = bytestream2_get_be16(gb); } else if (chunk == MKBETAG('l', 'o', 'o', 'p')) { bytestream2_skip(gb, 4 + 4 + 2 + 2); } else if (chunk == MKBETAG('p', 'a', 'd', 0)) { @@ -183,6 +283,14 @@ static av_cold int decode_init(AVCodecContext *avctx) } } + if (bytestream2_get_bytes_left(gb) >= 10) { + bytestream2_skip(gb, bytestream2_get_bytes_left(gb) - 10); + c->key = bytestream2_get_be64u(gb); + c->subkey = bytestream2_get_be16u(gb); + } + + cipher_init(c->cipher, c->ciph_type, c->key, c->subkey); + ret = ath_init(c->ath, c->ath_type, avctx->sample_rate); if (ret < 0) return ret; @@ -229,11 +337,12 @@ static av_cold int decode_init(AVCodecContext *avctx) if (c->total_band_count < c->base_band_count) return AVERROR_INVALIDDATA; - c->hfr_group_count = ceil2(c->total_band_count - (c->base_band_count + c->stereo_band_count), + hfr_group_count = ceil2(c->total_band_count - (c->base_band_count + c->stereo_band_count), c->bands_per_hfr_group); - if (c->base_band_count + c->stereo_band_count + (unsigned long)c->hfr_group_count > 128ULL) + if (c->base_band_count + c->stereo_band_count + (uint64_t)hfr_group_count > 128ULL) return AVERROR_INVALIDDATA; + c->hfr_group_count = hfr_group_count; for (int i = 0; i < avctx->ch_layout.nb_channels; i++) { c->ch[i].chan_type = r[i]; @@ -243,11 +352,38 @@ static av_cold int decode_init(AVCodecContext *avctx) return AVERROR_INVALIDDATA; } + // Done last to signal init() finished + c->crc_table = av_crc_get_table(AV_CRC_16_ANSI); + + return 0; +} + +static av_cold int decode_init(AVCodecContext *avctx) +{ + HCAContext *c = avctx->priv_data; + float scale = 1.f / 8.f; + int ret; + + avctx->sample_fmt = AV_SAMPLE_FMT_FLTP; + + if (avctx->ch_layout.nb_channels <= 0 || avctx->ch_layout.nb_channels > FF_ARRAY_ELEMS(c->ch)) + return AVERROR(EINVAL); + c->fdsp = avpriv_float_dsp_alloc(avctx->flags & AV_CODEC_FLAG_BITEXACT); if (!c->fdsp) return AVERROR(ENOMEM); - return av_tx_init(&c->tx_ctx, &c->tx_fn, AV_TX_FLOAT_MDCT, 1, 128, &scale, 0); + ret = av_tx_init(&c->tx_ctx, &c->tx_fn, AV_TX_FLOAT_MDCT, 1, 128, &scale, 0); + if (ret < 0) + return ret; + + if (avctx->extradata_size != 0 && avctx->extradata_size < 36) + return AVERROR_INVALIDDATA; + + if (!avctx->extradata_size) + return 0; + + return init_hca(avctx, avctx->extradata, avctx->extradata_size); } static void run_imdct(HCAContext *c, ChannelContext *ch, int index, float *out) @@ -273,8 +409,8 @@ static void apply_intensity_stereo(HCAContext *s, ChannelContext *ch1, ChannelCo return; for (int i = 0; i < band_count; i++) { - *(c2++) = *c1 * ratio_r; - *(c1++) *= ratio_l; + c2[i] = c1[i] * ratio_r; + c1[i] *= ratio_l; } } @@ -299,6 +435,10 @@ static void reconstruct_hfr(HCAContext *s, ChannelContext *ch, static void dequantize_coefficients(HCAContext *c, ChannelContext *ch, GetBitContext *gb) { + const float *base = ch->base; + float *factors = ch->factors; + float *out = ch->imdct_in; + for (int i = 0; i < ch->count; i++) { unsigned scale = ch->scale[i]; int nb_bits = max_bits_table[scale]; @@ -315,10 +455,11 @@ static void dequantize_coefficients(HCAContext *c, ChannelContext *ch, skip_bits_long(gb, quant_spectrum_bits[value] - nb_bits); factor = quant_spectrum_value[value]; } - ch->imdct_in[i] = factor * ch->base[i]; + factors[i] = factor; } - memset(ch->imdct_in + ch->count, 0, sizeof(ch->imdct_in) - ch->count * sizeof(ch->imdct_in[0])); + memset(factors + ch->count, 0, 512 - ch->count * sizeof(*factors)); + c->fdsp->vector_fmul(out, factors, base, 128); } static void unpack(HCAContext *c, ChannelContext *ch, @@ -385,16 +526,49 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, AVPacket *avpkt) { HCAContext *c = avctx->priv_data; - int ch, ret, packed_noise_level; + int ch, offset = 0, ret, packed_noise_level; GetBitContext gb0, *const gb = &gb0; float **samples; + if (avpkt->size <= 8) + return AVERROR_INVALIDDATA; + + if (AV_RN16(avpkt->data) != 0xFFFF) { + if ((AV_RL32(avpkt->data)) != MKTAG('H','C','A',0)) { + return AVERROR_INVALIDDATA; + } else if (AV_RB16(avpkt->data + 6) <= avpkt->size) { + ret = init_hca(avctx, avpkt->data, AV_RB16(avpkt->data + 6)); + if (ret < 0) { + c->crc_table = NULL; // signal that init has not finished + return ret; + } + offset = AV_RB16(avpkt->data + 6); + if (offset == avpkt->size) + return avpkt->size; + } else { + return AVERROR_INVALIDDATA; + } + } + + if (!c->crc_table) + return AVERROR_INVALIDDATA; + + if (c->key || c->subkey) { + uint8_t *data, *cipher = c->cipher; + + if ((ret = av_packet_make_writable(avpkt)) < 0) + return ret; + data = avpkt->data; + for (int n = 0; n < avpkt->size; n++) + data[n] = cipher[data[n]]; + } + if (avctx->err_recognition & AV_EF_CRCCHECK) { - if (av_crc(c->crc_table, 0, avpkt->data, avpkt->size)) + if (av_crc(c->crc_table, 0, avpkt->data + offset, avpkt->size - offset)) return AVERROR_INVALIDDATA; } - if ((ret = init_get_bits8(gb, avpkt->data, avpkt->size)) < 0) + if ((ret = init_get_bits8(gb, avpkt->data + offset, avpkt->size - offset)) < 0) return ret; if (get_bits(gb, 16) != 0xFFFF) @@ -439,6 +613,14 @@ static av_cold int decode_close(AVCodecContext *avctx) return 0; } +static av_cold void decode_flush(AVCodecContext *avctx) +{ + HCAContext *c = avctx->priv_data; + + for (int ch = 0; ch < MAX_CHANNELS; ch++) + memset(c->ch[ch].imdct_prev, 0, sizeof(c->ch[ch].imdct_prev)); +} + const FFCodec ff_hca_decoder = { .p.name = "hca", CODEC_LONG_NAME("CRI HCA"), @@ -447,6 +629,7 @@ const FFCodec ff_hca_decoder = { .priv_data_size = sizeof(HCAContext), .init = decode_init, FF_CODEC_DECODE_CB(decode_frame), + .flush = decode_flush, .close = decode_close, .p.capabilities = AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, diff --git a/libavcodec/hdrdec.c b/libavcodec/hdrdec.c index 998227744b3..9b6395bb6d3 100644 --- a/libavcodec/hdrdec.c +++ b/libavcodec/hdrdec.c @@ -212,7 +212,7 @@ static int hdr_decode_frame(AVCodecContext *avctx, AVFrame *p, } } - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/hdrenc.c b/libavcodec/hdrenc.c index 40d283ee610..54682d0a773 100644 --- a/libavcodec/hdrenc.c +++ b/libavcodec/hdrenc.c @@ -124,7 +124,7 @@ static int hdr_encode_frame(AVCodecContext *avctx, AVPacket *pkt, uint8_t *buf; int ret; - packet_size = avctx->width * avctx->height * 4LL + 1024LL; + packet_size = avctx->height * 4LL + avctx->width * avctx->height * 8LL + 1024LL; if ((ret = ff_get_encode_buffer(avctx, pkt, packet_size, 0)) < 0) return ret; diff --git a/libavcodec/hevc.h b/libavcodec/hevc.h index 1804755327e..6b454a75c18 100644 --- a/libavcodec/hevc.h +++ b/libavcodec/hevc.h @@ -154,6 +154,9 @@ enum { // get near that, though, so set a lower limit here with the maximum // possible value for 4K video (at most 135 16x16 Ctb rows). HEVC_MAX_ENTRY_POINT_OFFSETS = HEVC_MAX_TILE_COLUMNS * 135, + + // A.3.7: Screen content coding extensions + HEVC_MAX_PALETTE_PREDICTOR_SIZE = 128, }; diff --git a/libavcodec/hevc_cabac.c b/libavcodec/hevc_cabac.c index 6b38da84bdc..63ffb3d37ca 100644 --- a/libavcodec/hevc_cabac.c +++ b/libavcodec/hevc_cabac.c @@ -31,114 +31,66 @@ #define CABAC_MAX_BIN 31 -/** - * number of bin by SyntaxElement. - */ -static const int8_t num_bins_in_se[] = { - 1, // sao_merge_flag - 1, // sao_type_idx - 0, // sao_eo_class - 0, // sao_band_position - 0, // sao_offset_abs - 0, // sao_offset_sign - 0, // end_of_slice_flag - 3, // split_coding_unit_flag - 1, // cu_transquant_bypass_flag - 3, // skip_flag - 3, // cu_qp_delta - 1, // pred_mode - 4, // part_mode - 0, // pcm_flag - 1, // prev_intra_luma_pred_mode - 0, // mpm_idx - 0, // rem_intra_luma_pred_mode - 2, // intra_chroma_pred_mode - 1, // merge_flag - 1, // merge_idx - 5, // inter_pred_idc - 2, // ref_idx_l0 - 2, // ref_idx_l1 - 2, // abs_mvd_greater0_flag - 2, // abs_mvd_greater1_flag - 0, // abs_mvd_minus2 - 0, // mvd_sign_flag - 1, // mvp_lx_flag - 1, // no_residual_data_flag - 3, // split_transform_flag - 2, // cbf_luma - 5, // cbf_cb, cbf_cr - 2, // transform_skip_flag[][] - 2, // explicit_rdpcm_flag[][] - 2, // explicit_rdpcm_dir_flag[][] - 18, // last_significant_coeff_x_prefix - 18, // last_significant_coeff_y_prefix - 0, // last_significant_coeff_x_suffix - 0, // last_significant_coeff_y_suffix - 4, // significant_coeff_group_flag - 44, // significant_coeff_flag - 24, // coeff_abs_level_greater1_flag - 6, // coeff_abs_level_greater2_flag - 0, // coeff_abs_level_remaining - 0, // coeff_sign_flag - 8, // log2_res_scale_abs - 2, // res_scale_sign_flag - 1, // cu_chroma_qp_offset_flag - 1, // cu_chroma_qp_offset_idx -}; +// ELEM(NAME, NUM_BINS) +#define CABAC_ELEMS(ELEM) \ + ELEM(SAO_MERGE_FLAG, 1) \ + ELEM(SAO_TYPE_IDX, 1) \ + ELEM(SAO_EO_CLASS, 0) \ + ELEM(SAO_BAND_POSITION, 0) \ + ELEM(SAO_OFFSET_ABS, 0) \ + ELEM(SAO_OFFSET_SIGN, 0) \ + ELEM(END_OF_SLICE_FLAG, 0) \ + ELEM(SPLIT_CODING_UNIT_FLAG, 3) \ + ELEM(CU_TRANSQUANT_BYPASS_FLAG, 1) \ + ELEM(SKIP_FLAG, 3) \ + ELEM(CU_QP_DELTA, 3) \ + ELEM(PRED_MODE_FLAG, 1) \ + ELEM(PART_MODE, 4) \ + ELEM(PCM_FLAG, 0) \ + ELEM(PREV_INTRA_LUMA_PRED_FLAG, 1) \ + ELEM(MPM_IDX, 0) \ + ELEM(REM_INTRA_LUMA_PRED_MODE, 0) \ + ELEM(INTRA_CHROMA_PRED_MODE, 2) \ + ELEM(MERGE_FLAG, 1) \ + ELEM(MERGE_IDX, 1) \ + ELEM(INTER_PRED_IDC, 5) \ + ELEM(REF_IDX_L0, 2) \ + ELEM(REF_IDX_L1, 2) \ + ELEM(ABS_MVD_GREATER0_FLAG, 2) \ + ELEM(ABS_MVD_GREATER1_FLAG, 2) \ + ELEM(ABS_MVD_MINUS2, 0) \ + ELEM(MVD_SIGN_FLAG, 0) \ + ELEM(MVP_LX_FLAG, 1) \ + ELEM(NO_RESIDUAL_DATA_FLAG, 1) \ + ELEM(SPLIT_TRANSFORM_FLAG, 3) \ + ELEM(CBF_LUMA, 2) \ + ELEM(CBF_CB_CR, 5) \ + ELEM(TRANSFORM_SKIP_FLAG, 2) \ + ELEM(EXPLICIT_RDPCM_FLAG, 2) \ + ELEM(EXPLICIT_RDPCM_DIR_FLAG, 2) \ + ELEM(LAST_SIGNIFICANT_COEFF_X_PREFIX, 18) \ + ELEM(LAST_SIGNIFICANT_COEFF_Y_PREFIX, 18) \ + ELEM(LAST_SIGNIFICANT_COEFF_X_SUFFIX, 0) \ + ELEM(LAST_SIGNIFICANT_COEFF_Y_SUFFIX, 0) \ + ELEM(SIGNIFICANT_COEFF_GROUP_FLAG, 4) \ + ELEM(SIGNIFICANT_COEFF_FLAG, 44) \ + ELEM(COEFF_ABS_LEVEL_GREATER1_FLAG, 24) \ + ELEM(COEFF_ABS_LEVEL_GREATER2_FLAG, 6) \ + ELEM(COEFF_ABS_LEVEL_REMAINING, 0) \ + ELEM(COEFF_SIGN_FLAG, 0) \ + ELEM(LOG2_RES_SCALE_ABS, 8) \ + ELEM(RES_SCALE_SIGN_FLAG, 2) \ + ELEM(CU_CHROMA_QP_OFFSET_FLAG, 1) \ + ELEM(CU_CHROMA_QP_OFFSET_IDX, 1) \ /** - * Offset to ctxIdx 0 in init_values and states, indexed by SyntaxElement. + * Offset to ctxIdx 0 in init_values and states. */ -static const int elem_offset[sizeof(num_bins_in_se)] = { - 0, // sao_merge_flag - 1, // sao_type_idx - 2, // sao_eo_class - 2, // sao_band_position - 2, // sao_offset_abs - 2, // sao_offset_sign - 2, // end_of_slice_flag - 2, // split_coding_unit_flag - 5, // cu_transquant_bypass_flag - 6, // skip_flag - 9, // cu_qp_delta - 12, // pred_mode - 13, // part_mode - 17, // pcm_flag - 17, // prev_intra_luma_pred_mode - 18, // mpm_idx - 18, // rem_intra_luma_pred_mode - 18, // intra_chroma_pred_mode - 20, // merge_flag - 21, // merge_idx - 22, // inter_pred_idc - 27, // ref_idx_l0 - 29, // ref_idx_l1 - 31, // abs_mvd_greater0_flag - 33, // abs_mvd_greater1_flag - 35, // abs_mvd_minus2 - 35, // mvd_sign_flag - 35, // mvp_lx_flag - 36, // no_residual_data_flag - 37, // split_transform_flag - 40, // cbf_luma - 42, // cbf_cb, cbf_cr - 47, // transform_skip_flag[][] - 49, // explicit_rdpcm_flag[][] - 51, // explicit_rdpcm_dir_flag[][] - 53, // last_significant_coeff_x_prefix - 71, // last_significant_coeff_y_prefix - 89, // last_significant_coeff_x_suffix - 89, // last_significant_coeff_y_suffix - 89, // significant_coeff_group_flag - 93, // significant_coeff_flag - 137, // coeff_abs_level_greater1_flag - 161, // coeff_abs_level_greater2_flag - 167, // coeff_abs_level_remaining - 167, // coeff_sign_flag - 167, // log2_res_scale_abs - 175, // res_scale_sign_flag - 177, // cu_chroma_qp_offset_flag - 178, // cu_chroma_qp_offset_idx +enum { +#define OFFSET(NAME, NUM_BINS) \ + NAME ## _OFFSET, \ + NAME ## _END = NAME ## _OFFSET + NUM_BINS - 1, +CABAC_ELEMS(OFFSET) }; #define CNU 154 @@ -570,12 +522,12 @@ int ff_hevc_cabac_init(HEVCLocalContext *lc, int ctb_addr_ts) int ff_hevc_sao_merge_flag_decode(HEVCLocalContext *lc) { - return GET_CABAC(elem_offset[SAO_MERGE_FLAG]); + return GET_CABAC(SAO_MERGE_FLAG_OFFSET); } int ff_hevc_sao_type_idx_decode(HEVCLocalContext *lc) { - if (!GET_CABAC(elem_offset[SAO_TYPE_IDX])) + if (!GET_CABAC(SAO_TYPE_IDX_OFFSET)) return 0; if (!get_cabac_bypass(&lc->cc)) @@ -622,7 +574,7 @@ int ff_hevc_end_of_slice_flag_decode(HEVCLocalContext *lc) int ff_hevc_cu_transquant_bypass_flag_decode(HEVCLocalContext *lc) { - return GET_CABAC(elem_offset[CU_TRANSQUANT_BYPASS_FLAG]); + return GET_CABAC(CU_TRANSQUANT_BYPASS_FLAG_OFFSET); } int ff_hevc_skip_flag_decode(HEVCLocalContext *lc, int x0, int y0, int x_cb, int y_cb) @@ -638,7 +590,7 @@ int ff_hevc_skip_flag_decode(HEVCLocalContext *lc, int x0, int y0, int x_cb, int if (lc->ctb_up_flag || y0b) inc += !!SAMPLE_CTB(s->skip_flag, x_cb, y_cb - 1); - return GET_CABAC(elem_offset[SKIP_FLAG] + inc); + return GET_CABAC(SKIP_FLAG_OFFSET + inc); } int ff_hevc_cu_qp_delta_abs(HEVCLocalContext *lc) @@ -647,7 +599,7 @@ int ff_hevc_cu_qp_delta_abs(HEVCLocalContext *lc) int suffix_val = 0; int inc = 0; - while (prefix_val < 5 && GET_CABAC(elem_offset[CU_QP_DELTA] + inc)) { + while (prefix_val < 5 && GET_CABAC(CU_QP_DELTA_OFFSET + inc)) { prefix_val++; inc = 1; } @@ -675,7 +627,7 @@ int ff_hevc_cu_qp_delta_sign_flag(HEVCLocalContext *lc) int ff_hevc_cu_chroma_qp_offset_flag(HEVCLocalContext *lc) { - return GET_CABAC(elem_offset[CU_CHROMA_QP_OFFSET_FLAG]); + return GET_CABAC(CU_CHROMA_QP_OFFSET_FLAG_OFFSET); } int ff_hevc_cu_chroma_qp_offset_idx(HEVCLocalContext *lc) @@ -683,7 +635,7 @@ int ff_hevc_cu_chroma_qp_offset_idx(HEVCLocalContext *lc) int c_max= FFMAX(5, lc->parent->ps.pps->chroma_qp_offset_list_len_minus1); int i = 0; - while (i < c_max && GET_CABAC(elem_offset[CU_CHROMA_QP_OFFSET_IDX])) + while (i < c_max && GET_CABAC(CU_CHROMA_QP_OFFSET_IDX_OFFSET)) i++; return i; @@ -691,7 +643,7 @@ int ff_hevc_cu_chroma_qp_offset_idx(HEVCLocalContext *lc) int ff_hevc_pred_mode_decode(HEVCLocalContext *lc) { - return GET_CABAC(elem_offset[PRED_MODE_FLAG]); + return GET_CABAC(PRED_MODE_FLAG_OFFSET); } int ff_hevc_split_coding_unit_flag_decode(HEVCLocalContext *lc, int ct_depth, int x0, int y0) @@ -712,40 +664,40 @@ int ff_hevc_split_coding_unit_flag_decode(HEVCLocalContext *lc, int ct_depth, in inc += (depth_left > ct_depth); inc += (depth_top > ct_depth); - return GET_CABAC(elem_offset[SPLIT_CODING_UNIT_FLAG] + inc); + return GET_CABAC(SPLIT_CODING_UNIT_FLAG_OFFSET + inc); } int ff_hevc_part_mode_decode(HEVCLocalContext *lc, int log2_cb_size) { - if (GET_CABAC(elem_offset[PART_MODE])) // 1 + if (GET_CABAC(PART_MODE_OFFSET)) // 1 return PART_2Nx2N; if (log2_cb_size == lc->parent->ps.sps->log2_min_cb_size) { if (lc->cu.pred_mode == MODE_INTRA) // 0 return PART_NxN; - if (GET_CABAC(elem_offset[PART_MODE] + 1)) // 01 + if (GET_CABAC(PART_MODE_OFFSET + 1)) // 01 return PART_2NxN; if (log2_cb_size == 3) // 00 return PART_Nx2N; - if (GET_CABAC(elem_offset[PART_MODE] + 2)) // 001 + if (GET_CABAC(PART_MODE_OFFSET + 2)) // 001 return PART_Nx2N; return PART_NxN; // 000 } if (!lc->parent->ps.sps->amp_enabled_flag) { - if (GET_CABAC(elem_offset[PART_MODE] + 1)) // 01 + if (GET_CABAC(PART_MODE_OFFSET + 1)) // 01 return PART_2NxN; return PART_Nx2N; } - if (GET_CABAC(elem_offset[PART_MODE] + 1)) { // 01X, 01XX - if (GET_CABAC(elem_offset[PART_MODE] + 3)) // 011 + if (GET_CABAC(PART_MODE_OFFSET + 1)) { // 01X, 01XX + if (GET_CABAC(PART_MODE_OFFSET + 3)) // 011 return PART_2NxN; if (get_cabac_bypass(&lc->cc)) // 0101 return PART_2NxnD; return PART_2NxnU; // 0100 } - if (GET_CABAC(elem_offset[PART_MODE] + 3)) // 001 + if (GET_CABAC(PART_MODE_OFFSET + 3)) // 001 return PART_Nx2N; if (get_cabac_bypass(&lc->cc)) // 0001 return PART_nRx2N; @@ -759,7 +711,7 @@ int ff_hevc_pcm_flag_decode(HEVCLocalContext *lc) int ff_hevc_prev_intra_luma_pred_flag_decode(HEVCLocalContext *lc) { - return GET_CABAC(elem_offset[PREV_INTRA_LUMA_PRED_FLAG]); + return GET_CABAC(PREV_INTRA_LUMA_PRED_FLAG_OFFSET); } int ff_hevc_mpm_idx_decode(HEVCLocalContext *lc) @@ -783,7 +735,7 @@ int ff_hevc_rem_intra_luma_pred_mode_decode(HEVCLocalContext *lc) int ff_hevc_intra_chroma_pred_mode_decode(HEVCLocalContext *lc) { int ret; - if (!GET_CABAC(elem_offset[INTRA_CHROMA_PRED_MODE])) + if (!GET_CABAC(INTRA_CHROMA_PRED_MODE_OFFSET)) return 4; ret = get_cabac_bypass(&lc->cc) << 1; @@ -793,7 +745,7 @@ int ff_hevc_intra_chroma_pred_mode_decode(HEVCLocalContext *lc) int ff_hevc_merge_idx_decode(HEVCLocalContext *lc) { - int i = GET_CABAC(elem_offset[MERGE_IDX]); + int i = GET_CABAC(MERGE_IDX_OFFSET); if (i != 0) { while (i < lc->parent->sh.max_num_merge_cand-1 && get_cabac_bypass(&lc->cc)) @@ -804,17 +756,17 @@ int ff_hevc_merge_idx_decode(HEVCLocalContext *lc) int ff_hevc_merge_flag_decode(HEVCLocalContext *lc) { - return GET_CABAC(elem_offset[MERGE_FLAG]); + return GET_CABAC(MERGE_FLAG_OFFSET); } int ff_hevc_inter_pred_idc_decode(HEVCLocalContext *lc, int nPbW, int nPbH) { if (nPbW + nPbH == 12) - return GET_CABAC(elem_offset[INTER_PRED_IDC] + 4); - if (GET_CABAC(elem_offset[INTER_PRED_IDC] + lc->ct_depth)) + return GET_CABAC(INTER_PRED_IDC_OFFSET + 4); + if (GET_CABAC(INTER_PRED_IDC_OFFSET + lc->ct_depth)) return PRED_BI; - return GET_CABAC(elem_offset[INTER_PRED_IDC] + 4); + return GET_CABAC(INTER_PRED_IDC_OFFSET + 4); } int ff_hevc_ref_idx_lx_decode(HEVCLocalContext *lc, int num_ref_idx_lx) @@ -823,7 +775,7 @@ int ff_hevc_ref_idx_lx_decode(HEVCLocalContext *lc, int num_ref_idx_lx) int max = num_ref_idx_lx - 1; int max_ctx = FFMIN(max, 2); - while (i < max_ctx && GET_CABAC(elem_offset[REF_IDX_L0] + i)) + while (i < max_ctx && GET_CABAC(REF_IDX_L0_OFFSET + i)) i++; if (i == 2) { while (i < max && get_cabac_bypass(&lc->cc)) @@ -835,22 +787,22 @@ int ff_hevc_ref_idx_lx_decode(HEVCLocalContext *lc, int num_ref_idx_lx) int ff_hevc_mvp_lx_flag_decode(HEVCLocalContext *lc) { - return GET_CABAC(elem_offset[MVP_LX_FLAG]); + return GET_CABAC(MVP_LX_FLAG_OFFSET); } int ff_hevc_no_residual_syntax_flag_decode(HEVCLocalContext *lc) { - return GET_CABAC(elem_offset[NO_RESIDUAL_DATA_FLAG]); + return GET_CABAC(NO_RESIDUAL_DATA_FLAG_OFFSET); } static av_always_inline int abs_mvd_greater0_flag_decode(HEVCLocalContext *lc) { - return GET_CABAC(elem_offset[ABS_MVD_GREATER0_FLAG]); + return GET_CABAC(ABS_MVD_GREATER0_FLAG_OFFSET); } static av_always_inline int abs_mvd_greater1_flag_decode(HEVCLocalContext *lc) { - return GET_CABAC(elem_offset[ABS_MVD_GREATER1_FLAG] + 1); + return GET_CABAC(ABS_MVD_GREATER1_FLAG_OFFSET + 1); } static av_always_inline int mvd_decode(HEVCLocalContext *lc) @@ -878,39 +830,39 @@ static av_always_inline int mvd_sign_flag_decode(HEVCLocalContext *lc) int ff_hevc_split_transform_flag_decode(HEVCLocalContext *lc, int log2_trafo_size) { - return GET_CABAC(elem_offset[SPLIT_TRANSFORM_FLAG] + 5 - log2_trafo_size); + return GET_CABAC(SPLIT_TRANSFORM_FLAG_OFFSET + 5 - log2_trafo_size); } int ff_hevc_cbf_cb_cr_decode(HEVCLocalContext *lc, int trafo_depth) { - return GET_CABAC(elem_offset[CBF_CB_CR] + trafo_depth); + return GET_CABAC(CBF_CB_CR_OFFSET + trafo_depth); } int ff_hevc_cbf_luma_decode(HEVCLocalContext *lc, int trafo_depth) { - return GET_CABAC(elem_offset[CBF_LUMA] + !trafo_depth); + return GET_CABAC(CBF_LUMA_OFFSET + !trafo_depth); } static int hevc_transform_skip_flag_decode(HEVCLocalContext *lc, int c_idx) { - return GET_CABAC(elem_offset[TRANSFORM_SKIP_FLAG] + !!c_idx); + return GET_CABAC(TRANSFORM_SKIP_FLAG_OFFSET + !!c_idx); } static int explicit_rdpcm_flag_decode(HEVCLocalContext *lc, int c_idx) { - return GET_CABAC(elem_offset[EXPLICIT_RDPCM_FLAG] + !!c_idx); + return GET_CABAC(EXPLICIT_RDPCM_FLAG_OFFSET + !!c_idx); } static int explicit_rdpcm_dir_flag_decode(HEVCLocalContext *lc, int c_idx) { - return GET_CABAC(elem_offset[EXPLICIT_RDPCM_DIR_FLAG] + !!c_idx); + return GET_CABAC(EXPLICIT_RDPCM_DIR_FLAG_OFFSET + !!c_idx); } int ff_hevc_log2_res_scale_abs(HEVCLocalContext *lc, int idx) { int i =0; - while (i < 4 && GET_CABAC(elem_offset[LOG2_RES_SCALE_ABS] + 4 * idx + i)) + while (i < 4 && GET_CABAC(LOG2_RES_SCALE_ABS_OFFSET + 4 * idx + i)) i++; return i; @@ -918,7 +870,7 @@ int ff_hevc_log2_res_scale_abs(HEVCLocalContext *lc, int idx) int ff_hevc_res_scale_sign_flag(HEVCLocalContext *lc, int idx) { - return GET_CABAC(elem_offset[RES_SCALE_SIGN_FLAG] + idx); + return GET_CABAC(RES_SCALE_SIGN_FLAG_OFFSET + idx); } static av_always_inline void last_significant_coeff_xy_prefix_decode(HEVCLocalContext *lc, int c_idx, @@ -936,13 +888,13 @@ static av_always_inline void last_significant_coeff_xy_prefix_decode(HEVCLocalCo ctx_shift = log2_size - 2; } while (i < max && - GET_CABAC(elem_offset[LAST_SIGNIFICANT_COEFF_X_PREFIX] + (i >> ctx_shift) + ctx_offset)) + GET_CABAC(LAST_SIGNIFICANT_COEFF_X_PREFIX_OFFSET + (i >> ctx_shift) + ctx_offset)) i++; *last_scx_prefix = i; i = 0; while (i < max && - GET_CABAC(elem_offset[LAST_SIGNIFICANT_COEFF_Y_PREFIX] + (i >> ctx_shift) + ctx_offset)) + GET_CABAC(LAST_SIGNIFICANT_COEFF_Y_PREFIX_OFFSET + (i >> ctx_shift) + ctx_offset)) i++; *last_scy_prefix = i; } @@ -965,18 +917,18 @@ static av_always_inline int significant_coeff_group_flag_decode(HEVCLocalContext inc = FFMIN(ctx_cg, 1) + (c_idx>0 ? 2 : 0); - return GET_CABAC(elem_offset[SIGNIFICANT_COEFF_GROUP_FLAG] + inc); + return GET_CABAC(SIGNIFICANT_COEFF_GROUP_FLAG_OFFSET + inc); } static av_always_inline int significant_coeff_flag_decode(HEVCLocalContext *lc, int x_c, int y_c, int offset, const uint8_t *ctx_idx_map) { int inc = ctx_idx_map[(y_c << 2) + x_c] + offset; - return GET_CABAC(elem_offset[SIGNIFICANT_COEFF_FLAG] + inc); + return GET_CABAC(SIGNIFICANT_COEFF_FLAG_OFFSET + inc); } static av_always_inline int significant_coeff_flag_decode_0(HEVCLocalContext *lc, int c_idx, int offset) { - return GET_CABAC(elem_offset[SIGNIFICANT_COEFF_FLAG] + offset); + return GET_CABAC(SIGNIFICANT_COEFF_FLAG_OFFSET + offset); } static av_always_inline int coeff_abs_level_greater1_flag_decode(HEVCLocalContext *lc, int c_idx, int inc) @@ -985,7 +937,7 @@ static av_always_inline int coeff_abs_level_greater1_flag_decode(HEVCLocalContex if (c_idx > 0) inc += 16; - return GET_CABAC(elem_offset[COEFF_ABS_LEVEL_GREATER1_FLAG] + inc); + return GET_CABAC(COEFF_ABS_LEVEL_GREATER1_FLAG_OFFSET + inc); } static av_always_inline int coeff_abs_level_greater2_flag_decode(HEVCLocalContext *lc, int c_idx, int inc) @@ -993,7 +945,7 @@ static av_always_inline int coeff_abs_level_greater2_flag_decode(HEVCLocalContex if (c_idx > 0) inc += 4; - return GET_CABAC(elem_offset[COEFF_ABS_LEVEL_GREATER2_FLAG] + inc); + return GET_CABAC(COEFF_ABS_LEVEL_GREATER2_FLAG_OFFSET + inc); } static av_always_inline int coeff_abs_level_remaining_decode(HEVCLocalContext *lc, int rc_rice_param) diff --git a/libavcodec/hevc_mvs.c b/libavcodec/hevc_mvs.c index c231797a57d..0a8cc2c43db 100644 --- a/libavcodec/hevc_mvs.c +++ b/libavcodec/hevc_mvs.c @@ -227,7 +227,7 @@ static int temporal_luma_motion_vector(const HEVCContext *s, int x0, int y0, int availableFlagLXCol = 0; int colPic; - const HEVCFrame *ref = s->ref->collocated_ref; + const HEVCFrame *ref = s->collocated_ref; if (!ref) { memset(mvLXCol, 0, sizeof(*mvLXCol)); diff --git a/libavcodec/hevc_parse.c b/libavcodec/hevc_parse.c index 29dfd479f38..7bc28fd081c 100644 --- a/libavcodec/hevc_parse.c +++ b/libavcodec/hevc_parse.c @@ -86,11 +86,13 @@ int ff_hevc_decode_extradata(const uint8_t *data, int size, HEVCParamSets *ps, bytestream2_init(&gb, data, size); - if (size > 3 && (data[0] || data[1] || data[2] > 1)) { - /* It seems the extradata is encoded as hvcC format. - * Temporarily, we support configurationVersion==0 until 14496-15 3rd - * is finalized. When finalized, configurationVersion will be 1 and we - * can recognize hvcC by checking if avctx->extradata[0]==1 or not. */ + /* data[0] == 1 is configurationVersion from 14496-15. + * data[0] == 0 is for backward compatibility predates the standard. + * + * Minimum number of bytes of hvcC with 0 numOfArrays is 23. + */ + if (size >= 23 && ((data[0] == 1) || (data[0] == 0 && (data[1] || data[2] > 1)))) { + /* It seems the extradata is encoded as hvcC format. */ int i, j, num_arrays, nal_len_size; *is_nalff = 1; diff --git a/libavcodec/hevc_parser.c b/libavcodec/hevc_parser.c index 59f9a0ff3e5..99a4272fad4 100644 --- a/libavcodec/hevc_parser.c +++ b/libavcodec/hevc_parser.c @@ -77,15 +77,15 @@ static int hevc_parse_slice_header(AVCodecParserContext *s, H2645NAL *nal, av_log(avctx, AV_LOG_ERROR, "PPS id out of range: %d\n", pps_id); return AVERROR_INVALIDDATA; } - ps->pps = (HEVCPPS*)ps->pps_list[pps_id]->data; + ps->pps = ps->pps_list[pps_id]; if (ps->pps->sps_id >= HEVC_MAX_SPS_COUNT || !ps->sps_list[ps->pps->sps_id]) { av_log(avctx, AV_LOG_ERROR, "SPS id out of range: %d\n", ps->pps->sps_id); return AVERROR_INVALIDDATA; } - if (ps->sps != (HEVCSPS*)ps->sps_list[ps->pps->sps_id]->data) { - ps->sps = (HEVCSPS*)ps->sps_list[ps->pps->sps_id]->data; - ps->vps = (HEVCVPS*)ps->vps_list[ps->sps->vps_id]->data; + if (ps->sps != ps->sps_list[ps->pps->sps_id]) { + ps->sps = ps->sps_list[ps->pps->sps_id]; + ps->vps = ps->vps_list[ps->sps->vps_id]; } ow = &ps->sps->output_window; @@ -278,6 +278,8 @@ static int hevc_find_frame_end(AVCodecParserContext *s, const uint8_t *buf, (nut >= 41 && nut <= 44) || (nut >= 48 && nut <= 55)) { if (pc->frame_start_found) { pc->frame_start_found = 0; + if (!((pc->state64 >> 6 * 8) & 0xFF)) + return i - 6; return i - 5; } } else if (nut <= HEVC_NAL_RASL_R || @@ -288,6 +290,8 @@ static int hevc_find_frame_end(AVCodecParserContext *s, const uint8_t *buf, pc->frame_start_found = 1; } else { // First slice of next frame found pc->frame_start_found = 0; + if (!((pc->state64 >> 6 * 8) & 0xFF)) + return i - 6; return i - 5; } } diff --git a/libavcodec/hevc_ps.c b/libavcodec/hevc_ps.c index 5fe62ec35b7..6475d86d7da 100644 --- a/libavcodec/hevc_ps.c +++ b/libavcodec/hevc_ps.c @@ -28,6 +28,7 @@ #include "h2645_vui.h" #include "hevc_data.h" #include "hevc_ps.h" +#include "refstruct.h" static const uint8_t default_scaling_list_intra[] = { 16, 16, 16, 16, 17, 18, 21, 24, @@ -61,90 +62,89 @@ static const uint8_t hevc_sub_height_c[] = { static void remove_pps(HEVCParamSets *s, int id) { - if (s->pps_list[id] && s->pps == (const HEVCPPS*)s->pps_list[id]->data) + if (s->pps == s->pps_list[id]) s->pps = NULL; - av_buffer_unref(&s->pps_list[id]); + ff_refstruct_unref(&s->pps_list[id]); } static void remove_sps(HEVCParamSets *s, int id) { int i; if (s->sps_list[id]) { - if (s->sps == (const HEVCSPS*)s->sps_list[id]->data) + if (s->sps == s->sps_list[id]) s->sps = NULL; /* drop all PPS that depend on this SPS */ for (i = 0; i < FF_ARRAY_ELEMS(s->pps_list); i++) - if (s->pps_list[i] && ((HEVCPPS*)s->pps_list[i]->data)->sps_id == id) + if (s->pps_list[i] && s->pps_list[i]->sps_id == id) remove_pps(s, i); - av_assert0(!(s->sps_list[id] && s->sps == (HEVCSPS*)s->sps_list[id]->data)); + av_assert0(!(s->sps_list[id] && s->sps == s->sps_list[id])); + ff_refstruct_unref(&s->sps_list[id]); } - av_buffer_unref(&s->sps_list[id]); } static void remove_vps(HEVCParamSets *s, int id) { int i; if (s->vps_list[id]) { - if (s->vps == (const HEVCVPS*)s->vps_list[id]->data) + if (s->vps == s->vps_list[id]) s->vps = NULL; for (i = 0; i < FF_ARRAY_ELEMS(s->sps_list); i++) - if (s->sps_list[i] && ((HEVCSPS*)s->sps_list[i]->data)->vps_id == id) + if (s->sps_list[i] && s->sps_list[i]->vps_id == id) remove_sps(s, i); + ff_refstruct_unref(&s->vps_list[id]); } - av_buffer_unref(&s->vps_list[id]); } int ff_hevc_decode_short_term_rps(GetBitContext *gb, AVCodecContext *avctx, ShortTermRPS *rps, const HEVCSPS *sps, int is_slice_header) { - uint8_t rps_predict = 0; int delta_poc; int k0 = 0; int k = 0; int i; + rps->rps_predict = 0; + if (rps != sps->st_rps && sps->nb_st_rps) - rps_predict = get_bits1(gb); + rps->rps_predict = get_bits1(gb); - if (rps_predict) { + if (rps->rps_predict) { const ShortTermRPS *rps_ridx; int delta_rps; - unsigned abs_delta_rps; - uint8_t use_delta_flag = 0; - uint8_t delta_rps_sign; if (is_slice_header) { - unsigned int delta_idx = get_ue_golomb_long(gb) + 1; - if (delta_idx > sps->nb_st_rps) { + rps->delta_idx = get_ue_golomb_long(gb) + 1; + if (rps->delta_idx > sps->nb_st_rps) { av_log(avctx, AV_LOG_ERROR, "Invalid value of delta_idx in slice header RPS: %d > %d.\n", - delta_idx, sps->nb_st_rps); + rps->delta_idx, sps->nb_st_rps); return AVERROR_INVALIDDATA; } - rps_ridx = &sps->st_rps[sps->nb_st_rps - delta_idx]; + rps_ridx = &sps->st_rps[sps->nb_st_rps - rps->delta_idx]; rps->rps_idx_num_delta_pocs = rps_ridx->num_delta_pocs; } else rps_ridx = &sps->st_rps[rps - sps->st_rps - 1]; - delta_rps_sign = get_bits1(gb); - abs_delta_rps = get_ue_golomb_long(gb) + 1; - if (abs_delta_rps < 1 || abs_delta_rps > 32768) { + rps->delta_rps_sign = get_bits1(gb); + rps->abs_delta_rps = get_ue_golomb_long(gb) + 1; + if (rps->abs_delta_rps > 32768) { av_log(avctx, AV_LOG_ERROR, "Invalid value of abs_delta_rps: %d\n", - abs_delta_rps); + rps->abs_delta_rps); return AVERROR_INVALIDDATA; } - delta_rps = (1 - (delta_rps_sign << 1)) * abs_delta_rps; + delta_rps = (1 - (rps->delta_rps_sign << 1)) * rps->abs_delta_rps; for (i = 0; i <= rps_ridx->num_delta_pocs; i++) { int used = rps->used[k] = get_bits1(gb); + rps->use_delta_flag = 0; if (!used) - use_delta_flag = get_bits1(gb); + rps->use_delta_flag = get_bits1(gb); - if (used || use_delta_flag) { + if (used || rps->use_delta_flag) { if (i < rps_ridx->num_delta_pocs) delta_poc = delta_rps + rps_ridx->delta_poc[i]; else @@ -210,7 +210,7 @@ int ff_hevc_decode_short_term_rps(GetBitContext *gb, AVCodecContext *avctx, if (rps->num_delta_pocs) { prev = 0; for (i = 0; i < rps->num_negative_pics; i++) { - delta_poc = get_ue_golomb_long(gb) + 1; + delta_poc = rps->delta_poc_s0[i] = get_ue_golomb_long(gb) + 1; if (delta_poc < 1 || delta_poc > 32768) { av_log(avctx, AV_LOG_ERROR, "Invalid value of delta_poc: %d\n", @@ -223,7 +223,7 @@ int ff_hevc_decode_short_term_rps(GetBitContext *gb, AVCodecContext *avctx, } prev = 0; for (i = 0; i < nb_positive_pics; i++) { - delta_poc = get_ue_golomb_long(gb) + 1; + delta_poc = rps->delta_poc_s1[i] = get_ue_golomb_long(gb) + 1; if (delta_poc < 1 || delta_poc > 32768) { av_log(avctx, AV_LOG_ERROR, "Invalid value of delta_poc: %d\n", @@ -251,14 +251,16 @@ static int decode_profile_tier_level(GetBitContext *gb, AVCodecContext *avctx, ptl->profile_space = get_bits(gb, 2); ptl->tier_flag = get_bits1(gb); ptl->profile_idc = get_bits(gb, 5); - if (ptl->profile_idc == FF_PROFILE_HEVC_MAIN) + if (ptl->profile_idc == AV_PROFILE_HEVC_MAIN) av_log(avctx, AV_LOG_DEBUG, "Main profile bitstream\n"); - else if (ptl->profile_idc == FF_PROFILE_HEVC_MAIN_10) + else if (ptl->profile_idc == AV_PROFILE_HEVC_MAIN_10) av_log(avctx, AV_LOG_DEBUG, "Main 10 profile bitstream\n"); - else if (ptl->profile_idc == FF_PROFILE_HEVC_MAIN_STILL_PICTURE) + else if (ptl->profile_idc == AV_PROFILE_HEVC_MAIN_STILL_PICTURE) av_log(avctx, AV_LOG_DEBUG, "Main Still Picture profile bitstream\n"); - else if (ptl->profile_idc == FF_PROFILE_HEVC_REXT) + else if (ptl->profile_idc == AV_PROFILE_HEVC_REXT) av_log(avctx, AV_LOG_DEBUG, "Range Extension profile bitstream\n"); + else if (ptl->profile_idc == AV_PROFILE_HEVC_SCC) + av_log(avctx, AV_LOG_DEBUG, "Screen Content Coding Extension profile bitstream\n"); else av_log(avctx, AV_LOG_WARNING, "Unknown HEVC profile: %d\n", ptl->profile_idc); @@ -355,81 +357,104 @@ static int parse_ptl(GetBitContext *gb, AVCodecContext *avctx, } static void decode_sublayer_hrd(GetBitContext *gb, unsigned int nb_cpb, - int subpic_params_present) + HEVCSublayerHdrParams *par, int subpic_params_present) { int i; for (i = 0; i < nb_cpb; i++) { - get_ue_golomb_long(gb); // bit_rate_value_minus1 - get_ue_golomb_long(gb); // cpb_size_value_minus1 + par->bit_rate_value_minus1[i] = get_ue_golomb_long(gb); + par->cpb_size_value_minus1[i] = get_ue_golomb_long(gb); if (subpic_params_present) { - get_ue_golomb_long(gb); // cpb_size_du_value_minus1 - get_ue_golomb_long(gb); // bit_rate_du_value_minus1 + par->cpb_size_du_value_minus1[i] = get_ue_golomb_long(gb); + par->bit_rate_du_value_minus1[i] = get_ue_golomb_long(gb); } - skip_bits1(gb); // cbr_flag + + par->cbr_flag |= get_bits1(gb) << i; } } static int decode_hrd(GetBitContext *gb, int common_inf_present, - int max_sublayers) + HEVCHdrParams *hdr, int max_sublayers) { - int nal_params_present = 0, vcl_params_present = 0; - int subpic_params_present = 0; - int i; - if (common_inf_present) { - nal_params_present = get_bits1(gb); - vcl_params_present = get_bits1(gb); - - if (nal_params_present || vcl_params_present) { - subpic_params_present = get_bits1(gb); - - if (subpic_params_present) { - skip_bits(gb, 8); // tick_divisor_minus2 - skip_bits(gb, 5); // du_cpb_removal_delay_increment_length_minus1 - skip_bits(gb, 1); // sub_pic_cpb_params_in_pic_timing_sei_flag - skip_bits(gb, 5); // dpb_output_delay_du_length_minus1 + hdr->nal_hrd_parameters_present_flag = get_bits1(gb); + hdr->vcl_hrd_parameters_present_flag = get_bits1(gb); + + if (hdr->nal_hrd_parameters_present_flag || + hdr->vcl_hrd_parameters_present_flag) { + hdr->sub_pic_hrd_params_present_flag = get_bits1(gb); + + if (hdr->sub_pic_hrd_params_present_flag) { + hdr->tick_divisor_minus2 = get_bits(gb, 8); + hdr->du_cpb_removal_delay_increment_length_minus1 = get_bits(gb, 5); + hdr->sub_pic_cpb_params_in_pic_timing_sei_flag = get_bits1(gb); + hdr->dpb_output_delay_du_length_minus1 = get_bits(gb, 5); } - skip_bits(gb, 4); // bit_rate_scale - skip_bits(gb, 4); // cpb_size_scale + hdr->bit_rate_scale = get_bits(gb, 4); + hdr->cpb_size_scale = get_bits(gb, 4); - if (subpic_params_present) - skip_bits(gb, 4); // cpb_size_du_scale + if (hdr->sub_pic_hrd_params_present_flag) + hdr->cpb_size_du_scale = get_bits(gb, 4); - skip_bits(gb, 5); // initial_cpb_removal_delay_length_minus1 - skip_bits(gb, 5); // au_cpb_removal_delay_length_minus1 - skip_bits(gb, 5); // dpb_output_delay_length_minus1 + hdr->initial_cpb_removal_delay_length_minus1 = get_bits(gb, 5); + hdr->au_cpb_removal_delay_length_minus1 = get_bits(gb, 5); + hdr->dpb_output_delay_length_minus1 = get_bits(gb, 5); } } - for (i = 0; i < max_sublayers; i++) { - int low_delay = 0; - unsigned int nb_cpb = 1; - int fixed_rate = get_bits1(gb); + for (int i = 0; i < max_sublayers; i++) { + unsigned fixed_pic_rate_general_flag = get_bits1(gb); + unsigned fixed_pic_rate_within_cvs_flag = 0; + unsigned low_delay_hrd_flag = 0; + hdr->flags.fixed_pic_rate_general_flag |= fixed_pic_rate_general_flag << i; - if (!fixed_rate) - fixed_rate = get_bits1(gb); + if (!fixed_pic_rate_general_flag) + fixed_pic_rate_within_cvs_flag = get_bits1(gb); + hdr->flags.fixed_pic_rate_within_cvs_flag |= fixed_pic_rate_within_cvs_flag << i; - if (fixed_rate) - get_ue_golomb_long(gb); // elemental_duration_in_tc_minus1 + if (fixed_pic_rate_within_cvs_flag || fixed_pic_rate_general_flag) + hdr->elemental_duration_in_tc_minus1[i] = get_ue_golomb_long(gb); else - low_delay = get_bits1(gb); - - if (!low_delay) { - nb_cpb = get_ue_golomb_long(gb) + 1; - if (nb_cpb < 1 || nb_cpb > 32) { - av_log(NULL, AV_LOG_ERROR, "nb_cpb %d invalid\n", nb_cpb); + low_delay_hrd_flag = get_bits1(gb); + hdr->flags.low_delay_hrd_flag |= low_delay_hrd_flag << i; + + if (!low_delay_hrd_flag) { + unsigned cpb_cnt_minus1 = get_ue_golomb_long(gb); + if (cpb_cnt_minus1 > 31) { + av_log(NULL, AV_LOG_ERROR, "nb_cpb %d invalid\n", + cpb_cnt_minus1); return AVERROR_INVALIDDATA; } + hdr->cpb_cnt_minus1[i] = cpb_cnt_minus1; } - if (nal_params_present) - decode_sublayer_hrd(gb, nb_cpb, subpic_params_present); - if (vcl_params_present) - decode_sublayer_hrd(gb, nb_cpb, subpic_params_present); + if (hdr->nal_hrd_parameters_present_flag) + decode_sublayer_hrd(gb, hdr->cpb_cnt_minus1[i]+1, &hdr->nal_params[i], + hdr->sub_pic_hrd_params_present_flag); + + if (hdr->vcl_hrd_parameters_present_flag) + decode_sublayer_hrd(gb, hdr->cpb_cnt_minus1[i]+1, &hdr->vcl_params[i], + hdr->sub_pic_hrd_params_present_flag); } + + return 0; +} + +static void uninit_vps(FFRefStructOpaque opaque, void *obj) +{ + HEVCVPS *vps = obj; + + av_freep(&vps->hdr); +} + +static int compare_vps(const HEVCVPS *vps1, const HEVCVPS *vps2) +{ + if (!memcmp(vps1, vps2, offsetof(HEVCVPS, hdr))) + return !vps1->vps_num_hrd_parameters || + !memcmp(vps1->hdr, vps2->hdr, vps1->vps_num_hrd_parameters * sizeof(*vps1->hdr)); + return 0; } @@ -439,12 +464,10 @@ int ff_hevc_decode_nal_vps(GetBitContext *gb, AVCodecContext *avctx, int i,j; int vps_id = 0; ptrdiff_t nal_size; - HEVCVPS *vps; - AVBufferRef *vps_buf = av_buffer_allocz(sizeof(*vps)); + HEVCVPS *vps = ff_refstruct_alloc_ext(sizeof(*vps), 0, NULL, uninit_vps); - if (!vps_buf) + if (!vps) return AVERROR(ENOMEM); - vps = (HEVCVPS*)vps_buf->data; av_log(avctx, AV_LOG_DEBUG, "Decoding VPS\n"); @@ -459,7 +482,7 @@ int ff_hevc_decode_nal_vps(GetBitContext *gb, AVCodecContext *avctx, } memcpy(vps->data, gb->buffer, vps->data_size); - vps_id = get_bits(gb, 4); + vps_id = vps->vps_id = get_bits(gb, 4); if (get_bits(gb, 2) != 3) { // vps_reserved_three_2bits av_log(avctx, AV_LOG_ERROR, "vps_reserved_three_2bits is not three\n"); @@ -530,13 +553,21 @@ int ff_hevc_decode_nal_vps(GetBitContext *gb, AVCodecContext *avctx, "vps_num_hrd_parameters %d is invalid\n", vps->vps_num_hrd_parameters); goto err; } + + if (vps->vps_num_hrd_parameters) { + vps->hdr = av_calloc(vps->vps_num_hrd_parameters, sizeof(*vps->hdr)); + if (!vps->hdr) + goto err; + } + for (i = 0; i < vps->vps_num_hrd_parameters; i++) { int common_inf_present = 1; get_ue_golomb_long(gb); // hrd_layer_set_idx if (i) common_inf_present = get_bits1(gb); - decode_hrd(gb, common_inf_present, vps->vps_max_sub_layers); + decode_hrd(gb, common_inf_present, &vps->hdr[i], + vps->vps_max_sub_layers); } } get_bits1(gb); /* vps_extension_flag */ @@ -549,17 +580,17 @@ int ff_hevc_decode_nal_vps(GetBitContext *gb, AVCodecContext *avctx, } if (ps->vps_list[vps_id] && - !memcmp(ps->vps_list[vps_id]->data, vps_buf->data, vps_buf->size)) { - av_buffer_unref(&vps_buf); + compare_vps(ps->vps_list[vps_id], vps)) { + ff_refstruct_unref(&vps); } else { remove_vps(ps, vps_id); - ps->vps_list[vps_id] = vps_buf; + ps->vps_list[vps_id] = vps; } return 0; err: - av_buffer_unref(&vps_buf); + ff_refstruct_unref(&vps); return AVERROR_INVALIDDATA; } @@ -655,7 +686,7 @@ static void decode_vui(GetBitContext *gb, AVCodecContext *avctx, vui->vui_num_ticks_poc_diff_one_minus1 = get_ue_golomb_long(gb); vui->vui_hrd_parameters_present_flag = get_bits1(gb); if (vui->vui_hrd_parameters_present_flag) - decode_hrd(gb, 1, sps->max_sub_layers); + decode_hrd(gb, 1, &sps->hdr, sps->max_sub_layers); } vui->bitstream_restriction_flag = get_bits1(gb); @@ -720,7 +751,8 @@ static void set_default_scaling_list_data(ScalingList *sl) memcpy(sl->sl[3][5], default_scaling_list_inter, 64); } -static int scaling_list_data(GetBitContext *gb, AVCodecContext *avctx, ScalingList *sl, HEVCSPS *sps) +static int scaling_list_data(GetBitContext *gb, AVCodecContext *avctx, + ScalingList *sl, const HEVCSPS *sps) { uint8_t scaling_list_pred_mode_flag; uint8_t scaling_list_dc_coef[2][6]; @@ -846,12 +878,12 @@ static int map_pixel_format(AVCodecContext *avctx, HEVCSPS *sps) } int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, - int apply_defdispwin, AVBufferRef **vps_list, AVCodecContext *avctx) + int apply_defdispwin, const HEVCVPS * const *vps_list, + AVCodecContext *avctx) { HEVCWindow *ow; int ret = 0; - int log2_diff_max_min_transform_block_size; - int bit_depth_chroma, start, vui_present, sublayer_ordering_info; + int bit_depth_chroma, start, num_comps; int i; // Coded parameters @@ -900,7 +932,8 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, sps->height, 0, avctx)) < 0) return ret; - if (get_bits1(gb)) { // pic_conformance_flag + sps->conformance_window_flag = get_bits1(gb); + if (sps->conformance_window_flag) { int vert_mult = hevc_sub_height_c[sps->chroma_format_idc]; int horiz_mult = hevc_sub_width_c[sps->chroma_format_idc]; sps->pic_conf_win.left_offset = get_ue_golomb_long(gb) * horiz_mult; @@ -925,8 +958,18 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, sps->output_window = sps->pic_conf_win; } - sps->bit_depth = get_ue_golomb_long(gb) + 8; - bit_depth_chroma = get_ue_golomb_long(gb) + 8; + sps->bit_depth = get_ue_golomb_31(gb) + 8; + if (sps->bit_depth > 16) { + av_log(avctx, AV_LOG_ERROR, "Luma bit depth (%d) is out of range\n", + sps->bit_depth); + return AVERROR_INVALIDDATA; + } + bit_depth_chroma = get_ue_golomb_31(gb) + 8; + if (bit_depth_chroma > 16) { + av_log(avctx, AV_LOG_ERROR, "Chroma bit depth (%d) is out of range\n", + bit_depth_chroma); + return AVERROR_INVALIDDATA; + } if (sps->chroma_format_idc && bit_depth_chroma != sps->bit_depth) { av_log(avctx, AV_LOG_ERROR, "Luma bit depth (%d) is different from chroma bit depth (%d), " @@ -947,8 +990,8 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, return AVERROR_INVALIDDATA; } - sublayer_ordering_info = get_bits1(gb); - start = sublayer_ordering_info ? 0 : sps->max_sub_layers - 1; + sps->sublayer_ordering_info_flag = get_bits1(gb); + start = sps->sublayer_ordering_info_flag ? 0 : sps->max_sub_layers - 1; for (i = start; i < sps->max_sub_layers; i++) { sps->temporal_layer[i].max_dec_pic_buffering = get_ue_golomb_long(gb) + 1; sps->temporal_layer[i].num_reorder_pics = get_ue_golomb_long(gb); @@ -969,7 +1012,7 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, } } - if (!sublayer_ordering_info) { + if (!sps->sublayer_ordering_info_flag) { for (i = 0; i < start; i++) { sps->temporal_layer[i].max_dec_pic_buffering = sps->temporal_layer[start].max_dec_pic_buffering; sps->temporal_layer[i].num_reorder_pics = sps->temporal_layer[start].num_reorder_pics; @@ -977,12 +1020,12 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, } } - sps->log2_min_cb_size = get_ue_golomb_long(gb) + 3; - sps->log2_diff_max_min_coding_block_size = get_ue_golomb_long(gb); - sps->log2_min_tb_size = get_ue_golomb_long(gb) + 2; - log2_diff_max_min_transform_block_size = get_ue_golomb_long(gb); - sps->log2_max_trafo_size = log2_diff_max_min_transform_block_size + - sps->log2_min_tb_size; + sps->log2_min_cb_size = get_ue_golomb_long(gb) + 3; + sps->log2_diff_max_min_coding_block_size = get_ue_golomb_long(gb); + sps->log2_min_tb_size = get_ue_golomb_long(gb) + 2; + sps->log2_diff_max_min_transform_block_size = get_ue_golomb_long(gb); + sps->log2_max_trafo_size = sps->log2_diff_max_min_transform_block_size + + sps->log2_min_tb_size; if (sps->log2_min_cb_size < 3 || sps->log2_min_cb_size > 30) { av_log(avctx, AV_LOG_ERROR, "Invalid value %d for log2_min_cb_size", sps->log2_min_cb_size); @@ -999,8 +1042,9 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, return AVERROR_INVALIDDATA; } - if (log2_diff_max_min_transform_block_size < 0 || log2_diff_max_min_transform_block_size > 30) { - av_log(avctx, AV_LOG_ERROR, "Invalid value %d for log2_diff_max_min_transform_block_size", log2_diff_max_min_transform_block_size); + if (sps->log2_diff_max_min_transform_block_size > 30) { + av_log(avctx, AV_LOG_ERROR, "Invalid value %d for log2_diff_max_min_transform_block_size", + sps->log2_diff_max_min_transform_block_size); return AVERROR_INVALIDDATA; } @@ -1067,13 +1111,18 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, sps->sps_temporal_mvp_enabled_flag = get_bits1(gb); sps->sps_strong_intra_smoothing_enable_flag = get_bits1(gb); sps->vui.common.sar = (AVRational){0, 1}; - vui_present = get_bits1(gb); - if (vui_present) + sps->vui_present = get_bits1(gb); + if (sps->vui_present) decode_vui(gb, avctx, apply_defdispwin, sps); - if (get_bits1(gb)) { // sps_extension_flag - sps->sps_range_extension_flag = get_bits1(gb); - skip_bits(gb, 7); //sps_extension_7bits = get_bits(gb, 7); + sps->sps_extension_present_flag = get_bits1(gb); + if (sps->sps_extension_present_flag) { + sps->sps_range_extension_flag = get_bits1(gb); + sps->sps_multilayer_extension_flag = get_bits1(gb); + sps->sps_3d_extension_flag = get_bits1(gb); + sps->sps_scc_extension_flag = get_bits1(gb); + skip_bits(gb, 4); // sps_extension_4bits + if (sps->sps_range_extension_flag) { sps->transform_skip_rotation_enabled_flag = get_bits1(gb); sps->transform_skip_context_enabled_flag = get_bits1(gb); @@ -1099,6 +1148,64 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, av_log(avctx, AV_LOG_WARNING, "cabac_bypass_alignment_enabled_flag not yet implemented\n"); } + + if (sps->sps_multilayer_extension_flag) { + skip_bits1(gb); // inter_view_mv_vert_constraint_flag + av_log(avctx, AV_LOG_WARNING, + "sps_multilayer_extension_flag not yet implemented\n"); + } + + if (sps->sps_3d_extension_flag) { + for (i = 0; i <= 1; i++) { + skip_bits1(gb); // iv_di_mc_enabled_flag + skip_bits1(gb); // iv_mv_scal_enabled_flag + if (i == 0) { + get_ue_golomb_long(gb); // log2_ivmc_sub_pb_size_minus3 + skip_bits1(gb); // iv_res_pred_enabled_flag + skip_bits1(gb); // depth_ref_enabled_flag + skip_bits1(gb); // vsp_mc_enabled_flag + skip_bits1(gb); // dbbp_enabled_flag + } else { + skip_bits1(gb); // tex_mc_enabled_flag + get_ue_golomb_long(gb); // log2_ivmc_sub_pb_size_minus3 + skip_bits1(gb); // intra_contour_enabled_flag + skip_bits1(gb); // intra_dc_only_wedge_enabled_flag + skip_bits1(gb); // cqt_cu_part_pred_enabled_flag + skip_bits1(gb); // inter_dc_only_enabled_flag + skip_bits1(gb); // skip_intra_enabled_flag + } + } + av_log(avctx, AV_LOG_WARNING, + "sps_3d_extension_flag not yet implemented\n"); + } + + if (sps->sps_scc_extension_flag) { + sps->sps_curr_pic_ref_enabled_flag = get_bits1(gb); + sps->palette_mode_enabled_flag = get_bits1(gb); + if (sps->palette_mode_enabled_flag) { + sps->palette_max_size = get_ue_golomb(gb); + sps->delta_palette_max_predictor_size = get_ue_golomb(gb); + sps->sps_palette_predictor_initializers_present_flag = get_bits1(gb); + + if (sps->sps_palette_predictor_initializers_present_flag) { + sps->sps_num_palette_predictor_initializers = get_ue_golomb(gb) + 1; + if (sps->sps_num_palette_predictor_initializers > HEVC_MAX_PALETTE_PREDICTOR_SIZE) { + av_log(avctx, AV_LOG_ERROR, + "sps_num_palette_predictor_initializers out of range: %u\n", + sps->sps_num_palette_predictor_initializers); + return AVERROR_INVALIDDATA; + } + num_comps = !sps->chroma_format_idc ? 1 : 3; + for (int comp = 0; comp < num_comps; comp++) { + int bit_depth = !comp ? sps->bit_depth : sps->bit_depth_chroma; + for (i = 0; i < sps->sps_num_palette_predictor_initializers; i++) + sps->sps_palette_predictor_initializer[comp][i] = get_bits(gb, bit_depth); + } + } + } + sps->motion_vector_resolution_control_idc = get_bits(gb, 2); + sps->intra_boundary_filtering_disabled_flag = get_bits1(gb); + } } if (apply_defdispwin) { sps->output_window.left_offset += sps->vui.def_disp_win.left_offset; @@ -1190,15 +1297,13 @@ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, int ff_hevc_decode_nal_sps(GetBitContext *gb, AVCodecContext *avctx, HEVCParamSets *ps, int apply_defdispwin) { - HEVCSPS *sps; - AVBufferRef *sps_buf = av_buffer_allocz(sizeof(*sps)); + HEVCSPS *sps = ff_refstruct_allocz(sizeof(*sps)); unsigned int sps_id; int ret; ptrdiff_t nal_size; - if (!sps_buf) + if (!sps) return AVERROR(ENOMEM); - sps = (HEVCSPS*)sps_buf->data; av_log(avctx, AV_LOG_DEBUG, "Decoding SPS\n"); @@ -1217,7 +1322,7 @@ int ff_hevc_decode_nal_sps(GetBitContext *gb, AVCodecContext *avctx, apply_defdispwin, ps->vps_list, avctx); if (ret < 0) { - av_buffer_unref(&sps_buf); + ff_refstruct_unref(&sps); return ret; } @@ -1235,19 +1340,19 @@ int ff_hevc_decode_nal_sps(GetBitContext *gb, AVCodecContext *avctx, * original one. * otherwise drop all PPSes that depend on it */ if (ps->sps_list[sps_id] && - !memcmp(ps->sps_list[sps_id]->data, sps_buf->data, sps_buf->size)) { - av_buffer_unref(&sps_buf); + !memcmp(ps->sps_list[sps_id], sps, sizeof(*sps))) { + ff_refstruct_unref(&sps); } else { remove_sps(ps, sps_id); - ps->sps_list[sps_id] = sps_buf; + ps->sps_list[sps_id] = sps; } return 0; } -static void hevc_pps_free(void *opaque, uint8_t *data) +static void hevc_pps_free(FFRefStructOpaque unused, void *obj) { - HEVCPPS *pps = (HEVCPPS*)data; + HEVCPPS *pps = obj; av_freep(&pps->column_width); av_freep(&pps->row_height); @@ -1259,42 +1364,203 @@ static void hevc_pps_free(void *opaque, uint8_t *data) av_freep(&pps->tile_pos_rs); av_freep(&pps->tile_id); av_freep(&pps->min_tb_addr_zs_tab); +} - av_freep(&pps); +static void colour_mapping_octants(GetBitContext *gb, HEVCPPS *pps, int inp_depth, + int idx_y, int idx_cb, int idx_cr, int inp_length) +{ + unsigned int split_octant_flag, part_num_y, coded_res_flag, res_coeff_q, res_coeff_r; + int cm_res_bits; + + part_num_y = 1 << pps->cm_y_part_num_log2; + + split_octant_flag = inp_depth < pps->cm_octant_depth ? get_bits1(gb) : 0; + + if (split_octant_flag) + for (int k = 0; k < 2; k++) + for (int m = 0; m < 2; m++) + for (int n = 0; n < 2; n++) + colour_mapping_octants(gb, pps, inp_depth + 1, + idx_y + part_num_y * k * inp_length / 2, + idx_cb + m * inp_length / 2, + idx_cr + n * inp_length / 2, + inp_length / 2); + else + for (int i = 0; i < part_num_y; i++) { + for (int j = 0; j < 4; j++) { + coded_res_flag = get_bits1(gb); + if (coded_res_flag) + for (int c = 0; c < 3; c++) { + res_coeff_q = get_ue_golomb_long(gb); + cm_res_bits = FFMAX(0, 10 + pps->luma_bit_depth_cm_input - + pps->luma_bit_depth_cm_output - + pps->cm_res_quant_bits - pps->cm_delta_flc_bits); + res_coeff_r = cm_res_bits ? get_bits(gb, cm_res_bits) : 0; + if (res_coeff_q || res_coeff_r) + skip_bits1(gb); + } + } + } } -static int pps_range_extensions(GetBitContext *gb, AVCodecContext *avctx, - HEVCPPS *pps, HEVCSPS *sps) { - int i; +static int colour_mapping_table(GetBitContext *gb, AVCodecContext *avctx, HEVCPPS *pps) +{ + pps->num_cm_ref_layers = get_ue_golomb(gb) + 1; + if (pps->num_cm_ref_layers > 62) { + av_log(avctx, AV_LOG_ERROR, + "num_cm_ref_layers_minus1 shall be in the range [0, 61].\n"); + return AVERROR_INVALIDDATA; + } + for (int i = 0; i < pps->num_cm_ref_layers; i++) + pps->cm_ref_layer_id[i] = get_bits(gb, 6); + + pps->cm_octant_depth = get_bits(gb, 2); + pps->cm_y_part_num_log2 = get_bits(gb, 2); + + pps->luma_bit_depth_cm_input = get_ue_golomb(gb) + 8; + pps->chroma_bit_depth_cm_input = get_ue_golomb(gb) + 8; + pps->luma_bit_depth_cm_output = get_ue_golomb(gb) + 8; + pps->chroma_bit_depth_cm_output = get_ue_golomb(gb) + 8; + + pps->cm_res_quant_bits = get_bits(gb, 2); + pps->cm_delta_flc_bits = get_bits(gb, 2) + 1; + + if (pps->cm_octant_depth == 1) { + pps->cm_adapt_threshold_u_delta = get_se_golomb_long(gb); + pps->cm_adapt_threshold_v_delta = get_se_golomb_long(gb); + } + + colour_mapping_octants(gb, pps, 0, 0, 0, 0, 1 << pps->cm_octant_depth); + return 0; +} + +static int pps_multilayer_extension(GetBitContext *gb, AVCodecContext *avctx, + HEVCPPS *pps, const HEVCSPS *sps, const HEVCVPS *vps) +{ + pps->poc_reset_info_present_flag = get_bits1(gb); + pps->pps_infer_scaling_list_flag = get_bits1(gb); + if (pps->pps_infer_scaling_list_flag) + pps->pps_scaling_list_ref_layer_id = get_bits(gb, 6); + + pps->num_ref_loc_offsets = get_ue_golomb(gb); + if (pps->num_ref_loc_offsets > vps->vps_max_layers - 1) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < pps->num_ref_loc_offsets; i++) { + pps->ref_loc_offset_layer_id[i] = get_bits(gb, 6); + pps->scaled_ref_layer_offset_present_flag[i] = get_bits1(gb); + if (pps->scaled_ref_layer_offset_present_flag[i]) { + pps->scaled_ref_layer_left_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + pps->scaled_ref_layer_top_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + pps->scaled_ref_layer_right_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + pps->scaled_ref_layer_bottom_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + } + + pps->ref_region_offset_present_flag[i] = get_bits1(gb); + if (pps->ref_region_offset_present_flag[i]) { + pps->ref_region_left_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + pps->ref_region_top_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + pps->ref_region_right_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + pps->ref_region_bottom_offset[pps->ref_loc_offset_layer_id[i]] = get_se_golomb_long(gb); + } + + pps->resample_phase_set_present_flag[i] = get_bits1(gb); + if (pps->resample_phase_set_present_flag[i]) { + pps->phase_hor_luma[pps->ref_loc_offset_layer_id[i]] = get_ue_golomb_31(gb); + pps->phase_ver_luma[pps->ref_loc_offset_layer_id[i]] = get_ue_golomb_31(gb); + pps->phase_hor_chroma[pps->ref_loc_offset_layer_id[i]] = get_ue_golomb(gb) - 8; + pps->phase_ver_chroma[pps->ref_loc_offset_layer_id[i]] = get_ue_golomb(gb) - 8; + } + } + + pps->colour_mapping_enabled_flag = get_bits1(gb); + if (pps->colour_mapping_enabled_flag) { + int ret = colour_mapping_table(gb, avctx, pps); + if (ret < 0) + return ret; + } + + return 0; +} + +static void delta_dlt(GetBitContext *gb, HEVCPPS *pps) +{ + unsigned int num_val_delta_dlt, max_diff = 0; + int min_diff_minus1 = -1; + unsigned int len; + + num_val_delta_dlt = get_bits(gb, pps->pps_bit_depth_for_depth_layers_minus8 + 8); + if (num_val_delta_dlt) { + if (num_val_delta_dlt > 1) + max_diff = get_bits(gb, pps->pps_bit_depth_for_depth_layers_minus8 + 8); + if (num_val_delta_dlt > 2 && max_diff) { + len = av_log2(max_diff) + 1; + min_diff_minus1 = get_bits(gb, len); + } + if (max_diff > (min_diff_minus1 + 1)) + for (int k = 1; k < num_val_delta_dlt; k++) { + len = av_log2(max_diff - (min_diff_minus1 + 1)) + 1; + skip_bits(gb, len); // delta_val_diff_minus_min + } + } +} + +static int pps_3d_extension(GetBitContext *gb, AVCodecContext *avctx, + HEVCPPS *pps, const HEVCSPS *sps) +{ + unsigned int pps_depth_layers_minus1; + + if (get_bits1(gb)) { // dlts_present_flag + pps_depth_layers_minus1 = get_bits(gb, 6); + pps->pps_bit_depth_for_depth_layers_minus8 = get_bits(gb, 4); + for (int i = 0; i <= pps_depth_layers_minus1; i++) { + if (get_bits1(gb)) { // dlt_flag[i] + if (!get_bits1(gb)) { // dlt_pred_flag[i] + if (get_bits1(gb)) { // dlt_val_flags_present_flag[i] + for (int j = 0; j <= ((1 << (pps->pps_bit_depth_for_depth_layers_minus8 + 8)) - 1); j++) + skip_bits1(gb); // dlt_value_flag[i][j] + } else + delta_dlt(gb, pps); + } + } + } + } + + return 0; +} + +static int pps_range_extensions(GetBitContext *gb, AVCodecContext *avctx, + HEVCPPS *pps, const HEVCSPS *sps) +{ if (pps->transform_skip_enabled_flag) { - pps->log2_max_transform_skip_block_size = get_ue_golomb_long(gb) + 2; + pps->log2_max_transform_skip_block_size = get_ue_golomb_31(gb) + 2; } pps->cross_component_prediction_enabled_flag = get_bits1(gb); pps->chroma_qp_offset_list_enabled_flag = get_bits1(gb); if (pps->chroma_qp_offset_list_enabled_flag) { - pps->diff_cu_chroma_qp_offset_depth = get_ue_golomb_long(gb); - pps->chroma_qp_offset_list_len_minus1 = get_ue_golomb_long(gb); + pps->diff_cu_chroma_qp_offset_depth = get_ue_golomb_31(gb); + pps->chroma_qp_offset_list_len_minus1 = get_ue_golomb_31(gb); if (pps->chroma_qp_offset_list_len_minus1 > 5) { av_log(avctx, AV_LOG_ERROR, "chroma_qp_offset_list_len_minus1 shall be in the range [0, 5].\n"); return AVERROR_INVALIDDATA; } - for (i = 0; i <= pps->chroma_qp_offset_list_len_minus1; i++) { - pps->cb_qp_offset_list[i] = get_se_golomb_long(gb); + for (int i = 0; i <= pps->chroma_qp_offset_list_len_minus1; i++) { + pps->cb_qp_offset_list[i] = get_se_golomb(gb); if (pps->cb_qp_offset_list[i]) { av_log(avctx, AV_LOG_WARNING, "cb_qp_offset_list not tested yet.\n"); } - pps->cr_qp_offset_list[i] = get_se_golomb_long(gb); + pps->cr_qp_offset_list[i] = get_se_golomb(gb); if (pps->cr_qp_offset_list[i]) { av_log(avctx, AV_LOG_WARNING, "cb_qp_offset_list not tested yet.\n"); } } } - pps->log2_sao_offset_scale_luma = get_ue_golomb_long(gb); - pps->log2_sao_offset_scale_chroma = get_ue_golomb_long(gb); + pps->log2_sao_offset_scale_luma = get_ue_golomb_31(gb); + pps->log2_sao_offset_scale_chroma = get_ue_golomb_31(gb); if ( pps->log2_sao_offset_scale_luma > FFMAX(sps->bit_depth - 10, 0) || pps->log2_sao_offset_scale_chroma > FFMAX(sps->bit_depth_chroma - 10, 0) @@ -1304,8 +1570,62 @@ static int pps_range_extensions(GetBitContext *gb, AVCodecContext *avctx, return(0); } +static int pps_scc_extension(GetBitContext *gb, AVCodecContext *avctx, + HEVCPPS *pps, const HEVCSPS *sps) +{ + int num_comps, ret; + + pps->pps_curr_pic_ref_enabled_flag = get_bits1(gb); + if (pps->residual_adaptive_colour_transform_enabled_flag = get_bits1(gb)) { + pps->pps_slice_act_qp_offsets_present_flag = get_bits1(gb); + pps->pps_act_y_qp_offset = get_se_golomb(gb) - 5; + pps->pps_act_cb_qp_offset = get_se_golomb(gb) - 5; + pps->pps_act_cr_qp_offset = get_se_golomb(gb) - 3; + +#define CHECK_QP_OFFSET(name) (pps->pps_act_ ## name ## _qp_offset <= -12 || \ + pps->pps_act_ ## name ## _qp_offset >= 12) + ret = CHECK_QP_OFFSET(y) || CHECK_QP_OFFSET(cb) || CHECK_QP_OFFSET(cr); +#undef CHECK_QP_OFFSET + if (ret) { + av_log(avctx, AV_LOG_ERROR, + "PpsActQpOffsetY/Cb/Cr shall be in the range of [-12, 12].\n"); + return AVERROR_INVALIDDATA; + } + } + + if (pps->pps_palette_predictor_initializers_present_flag = get_bits1(gb)) { + pps->pps_num_palette_predictor_initializers = get_ue_golomb(gb); + if (pps->pps_num_palette_predictor_initializers > 0) { + if (pps->pps_num_palette_predictor_initializers > HEVC_MAX_PALETTE_PREDICTOR_SIZE) { + av_log(avctx, AV_LOG_ERROR, + "pps_num_palette_predictor_initializers out of range: %u\n", + pps->pps_num_palette_predictor_initializers); + return AVERROR_INVALIDDATA; + } + pps->monochrome_palette_flag = get_bits1(gb); + pps->luma_bit_depth_entry = get_ue_golomb_31(gb) + 8; + if (pps->luma_bit_depth_entry != sps->bit_depth) + return AVERROR_INVALIDDATA; + if (!pps->monochrome_palette_flag) { + pps->chroma_bit_depth_entry = get_ue_golomb_31(gb) + 8; + if (pps->chroma_bit_depth_entry != sps->bit_depth_chroma) + return AVERROR_INVALIDDATA; + } + + num_comps = pps->monochrome_palette_flag ? 1 : 3; + for (int comp = 0; comp < num_comps; comp++) { + int bit_depth = !comp ? pps->luma_bit_depth_entry : pps->chroma_bit_depth_entry; + for (int i = 0; i < pps->pps_num_palette_predictor_initializers; i++) + pps->pps_palette_predictor_initializer[comp][i] = get_bits(gb, bit_depth); + } + } + } + + return 0; +} + static inline int setup_pps(AVCodecContext *avctx, GetBitContext *gb, - HEVCPPS *pps, HEVCSPS *sps) + HEVCPPS *pps, const HEVCSPS *sps) { int log2_diff; int pic_area_in_ctbs; @@ -1439,25 +1759,18 @@ static inline int setup_pps(AVCodecContext *avctx, GetBitContext *gb, int ff_hevc_decode_nal_pps(GetBitContext *gb, AVCodecContext *avctx, HEVCParamSets *ps) { - HEVCSPS *sps = NULL; + const HEVCSPS *sps = NULL; + const HEVCVPS *vps = NULL; int i, ret = 0; unsigned int pps_id = 0; ptrdiff_t nal_size; unsigned log2_parallel_merge_level_minus2; - AVBufferRef *pps_buf; - HEVCPPS *pps = av_mallocz(sizeof(*pps)); + HEVCPPS *pps = ff_refstruct_alloc_ext(sizeof(*pps), 0, NULL, hevc_pps_free); if (!pps) return AVERROR(ENOMEM); - pps_buf = av_buffer_create((uint8_t *)pps, sizeof(*pps), - hevc_pps_free, NULL, 0); - if (!pps_buf) { - av_freep(&pps); - return AVERROR(ENOMEM); - } - av_log(avctx, AV_LOG_DEBUG, "Decoding PPS\n"); nal_size = gb->buffer_end - gb->buffer; @@ -1482,7 +1795,7 @@ int ff_hevc_decode_nal_pps(GetBitContext *gb, AVCodecContext *avctx, pps->log2_max_transform_skip_block_size = 2; // Coded parameters - pps_id = get_ue_golomb_long(gb); + pps_id = pps->pps_id = get_ue_golomb_long(gb); if (pps_id >= HEVC_MAX_PPS_COUNT) { av_log(avctx, AV_LOG_ERROR, "PPS id out of range: %d\n", pps_id); ret = AVERROR_INVALIDDATA; @@ -1499,7 +1812,8 @@ int ff_hevc_decode_nal_pps(GetBitContext *gb, AVCodecContext *avctx, ret = AVERROR_INVALIDDATA; goto err; } - sps = (HEVCSPS *)ps->sps_list[pps->sps_id]->data; + sps = ps->sps_list[pps->sps_id]; + vps = ps->vps_list[sps->vps_id]; pps->dependent_slice_segments_enabled_flag = get_bits1(gb); pps->output_flag_present_flag = get_bits1(gb); @@ -1509,8 +1823,14 @@ int ff_hevc_decode_nal_pps(GetBitContext *gb, AVCodecContext *avctx, pps->cabac_init_present_flag = get_bits1(gb); - pps->num_ref_idx_l0_default_active = get_ue_golomb_long(gb) + 1; - pps->num_ref_idx_l1_default_active = get_ue_golomb_long(gb) + 1; + pps->num_ref_idx_l0_default_active = get_ue_golomb_31(gb) + 1; + pps->num_ref_idx_l1_default_active = get_ue_golomb_31(gb) + 1; + if (pps->num_ref_idx_l0_default_active >= HEVC_MAX_REFS || + pps->num_ref_idx_l1_default_active >= HEVC_MAX_REFS) { + av_log(avctx, AV_LOG_ERROR, "Too many default refs in PPS: %d/%d.\n", + pps->num_ref_idx_l0_default_active, pps->num_ref_idx_l1_default_active); + goto err; + } pps->pic_init_qp_minus26 = get_se_golomb(gb); @@ -1655,13 +1975,33 @@ int ff_hevc_decode_nal_pps(GetBitContext *gb, AVCodecContext *avctx, pps->slice_header_extension_present_flag = get_bits1(gb); - if (get_bits1(gb)) { // pps_extension_present_flag - pps->pps_range_extensions_flag = get_bits1(gb); - skip_bits(gb, 7); // pps_extension_7bits - if (sps->ptl.general_ptl.profile_idc == FF_PROFILE_HEVC_REXT && pps->pps_range_extensions_flag) { + pps->pps_extension_present_flag = get_bits1(gb); + if (pps->pps_extension_present_flag) { + pps->pps_range_extensions_flag = get_bits1(gb); + pps->pps_multilayer_extension_flag = get_bits1(gb); + pps->pps_3d_extension_flag = get_bits1(gb); + pps->pps_scc_extension_flag = get_bits1(gb); + skip_bits(gb, 4); // pps_extension_4bits + + if (sps->ptl.general_ptl.profile_idc >= AV_PROFILE_HEVC_REXT && pps->pps_range_extensions_flag) { if ((ret = pps_range_extensions(gb, avctx, pps, sps)) < 0) goto err; } + + if (pps->pps_multilayer_extension_flag) { + if ((ret = pps_multilayer_extension(gb, avctx, pps, sps, vps)) < 0) + goto err; + } + + if (pps->pps_3d_extension_flag) { + if ((ret = pps_3d_extension(gb, avctx, pps, sps)) < 0) + goto err; + } + + if (pps->pps_scc_extension_flag) { + if ((ret = pps_scc_extension(gb, avctx, pps, sps)) < 0) + goto err; + } } ret = setup_pps(avctx, gb, pps, sps); @@ -1669,18 +2009,17 @@ int ff_hevc_decode_nal_pps(GetBitContext *gb, AVCodecContext *avctx, goto err; if (get_bits_left(gb) < 0) { - av_log(avctx, AV_LOG_ERROR, + av_log(avctx, AV_LOG_WARNING, "Overread PPS by %d bits\n", -get_bits_left(gb)); - goto err; } remove_pps(ps, pps_id); - ps->pps_list[pps_id] = pps_buf; + ps->pps_list[pps_id] = pps; return 0; err: - av_buffer_unref(&pps_buf); + ff_refstruct_unref(&pps); return ret; } @@ -1689,11 +2028,11 @@ void ff_hevc_ps_uninit(HEVCParamSets *ps) int i; for (i = 0; i < FF_ARRAY_ELEMS(ps->vps_list); i++) - av_buffer_unref(&ps->vps_list[i]); + ff_refstruct_unref(&ps->vps_list[i]); for (i = 0; i < FF_ARRAY_ELEMS(ps->sps_list); i++) - av_buffer_unref(&ps->sps_list[i]); + ff_refstruct_unref(&ps->sps_list[i]); for (i = 0; i < FF_ARRAY_ELEMS(ps->pps_list); i++) - av_buffer_unref(&ps->pps_list[i]); + ff_refstruct_unref(&ps->pps_list[i]); ps->sps = NULL; ps->pps = NULL; diff --git a/libavcodec/hevc_ps.h b/libavcodec/hevc_ps.h index 18894cfed18..0d8eaf2b3ea 100644 --- a/libavcodec/hevc_ps.h +++ b/libavcodec/hevc_ps.h @@ -23,7 +23,6 @@ #include -#include "libavutil/buffer.h" #include "libavutil/pixfmt.h" #include "libavutil/rational.h" @@ -32,10 +31,55 @@ #include "h2645_vui.h" #include "hevc.h" +typedef struct HEVCSublayerHdrParams { + uint32_t bit_rate_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t cpb_size_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t cpb_size_du_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t bit_rate_du_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t cbr_flag; +} HEVCSublayerHdrParams; + +// flags in bitmask form +typedef struct HEVCHdrFlagParams { + uint8_t fixed_pic_rate_general_flag; + uint8_t fixed_pic_rate_within_cvs_flag; + uint8_t low_delay_hrd_flag; +} HEVCHdrFlagParams; + +typedef struct HEVCHdrParams { + HEVCHdrFlagParams flags; + uint8_t nal_hrd_parameters_present_flag; + uint8_t vcl_hrd_parameters_present_flag; + uint8_t sub_pic_hrd_params_present_flag; + uint8_t sub_pic_cpb_params_in_pic_timing_sei_flag; + + uint8_t tick_divisor_minus2; + uint8_t du_cpb_removal_delay_increment_length_minus1; + uint8_t dpb_output_delay_du_length_minus1; + uint8_t bit_rate_scale; + uint8_t cpb_size_scale; + uint8_t cpb_size_du_scale; + uint8_t initial_cpb_removal_delay_length_minus1; + uint8_t au_cpb_removal_delay_length_minus1; + uint8_t dpb_output_delay_length_minus1; + uint8_t cpb_cnt_minus1[HEVC_MAX_SUB_LAYERS]; + uint16_t elemental_duration_in_tc_minus1[HEVC_MAX_SUB_LAYERS]; + + HEVCSublayerHdrParams nal_params[HEVC_MAX_SUB_LAYERS]; + HEVCSublayerHdrParams vcl_params[HEVC_MAX_SUB_LAYERS]; +} HEVCHdrParams; + typedef struct ShortTermRPS { + uint8_t rps_predict; + unsigned int delta_idx; + uint8_t use_delta_flag; + uint8_t delta_rps_sign; + unsigned int abs_delta_rps; unsigned int num_negative_pics; int num_delta_pocs; int rps_idx_num_delta_pocs; + int32_t delta_poc_s0[32]; + int32_t delta_poc_s1[32]; int32_t delta_poc[32]; uint8_t used[32]; } ShortTermRPS; @@ -108,6 +152,8 @@ typedef struct PTL { } PTL; typedef struct HEVCVPS { + unsigned int vps_id; + uint8_t vps_temporal_id_nesting_flag; int vps_max_layers; int vps_max_sub_layers; ///< vps_max_temporal_layers_minus1 + 1 @@ -128,6 +174,9 @@ typedef struct HEVCVPS { uint8_t data[4096]; int data_size; + /* Put this at the end of the structure to make it easier to calculate the + * size before this pointer, which is used for memcmp */ + HEVCHdrParams *hdr; } HEVCVPS; typedef struct ScalingList { @@ -144,8 +193,11 @@ typedef struct HEVCSPS { HEVCWindow output_window; + uint8_t conformance_window_flag; HEVCWindow pic_conf_win; + HEVCHdrParams hdr; + int bit_depth; int bit_depth_chroma; int pixel_shift; @@ -154,6 +206,7 @@ typedef struct HEVCSPS { unsigned int log2_max_poc_lsb; int pcm_enabled_flag; + uint8_t sublayer_ordering_info_flag; int max_sub_layers; struct { int max_dec_pic_buffering; @@ -162,9 +215,11 @@ typedef struct HEVCSPS { } temporal_layer[HEVC_MAX_SUB_LAYERS]; uint8_t temporal_id_nesting_flag; + int vui_present; VUI vui; PTL ptl; + uint8_t sps_extension_present_flag; uint8_t scaling_list_enable_flag; ScalingList scaling_list; @@ -195,6 +250,7 @@ typedef struct HEVCSPS { unsigned int log2_max_trafo_size; unsigned int log2_ctb_size; unsigned int log2_min_pu_size; + unsigned int log2_diff_max_min_transform_block_size; int max_transform_hierarchy_depth_inter; int max_transform_hierarchy_depth_intra; @@ -210,6 +266,20 @@ typedef struct HEVCSPS { int persistent_rice_adaptation_enabled_flag; int cabac_bypass_alignment_enabled_flag; + int sps_multilayer_extension_flag; + int sps_3d_extension_flag; + + int sps_scc_extension_flag; + int sps_curr_pic_ref_enabled_flag; + int palette_mode_enabled_flag; + int palette_max_size; + int delta_palette_max_predictor_size; + int sps_palette_predictor_initializers_present_flag; + int sps_num_palette_predictor_initializers; + int sps_palette_predictor_initializer[3][HEVC_MAX_PALETTE_PREDICTOR_SIZE]; + int motion_vector_resolution_control_idc; + int intra_boundary_filtering_disabled_flag; + ///< coded frame dimension in various units int width; int height; @@ -234,6 +304,7 @@ typedef struct HEVCSPS { } HEVCSPS; typedef struct HEVCPPS { + unsigned int pps_id; unsigned int sps_id; ///< seq_parameter_set_id uint8_t sign_data_hiding_flag; @@ -283,7 +354,11 @@ typedef struct HEVCPPS { int num_extra_slice_header_bits; uint8_t slice_header_extension_present_flag; uint8_t log2_max_transform_skip_block_size; + uint8_t pps_extension_present_flag; uint8_t pps_range_extensions_flag; + uint8_t pps_multilayer_extension_flag; + uint8_t pps_3d_extension_flag; + uint8_t pps_scc_extension_flag; uint8_t cross_component_prediction_enabled_flag; uint8_t chroma_qp_offset_list_enabled_flag; uint8_t diff_cu_chroma_qp_offset_depth; @@ -293,6 +368,58 @@ typedef struct HEVCPPS { uint8_t log2_sao_offset_scale_luma; uint8_t log2_sao_offset_scale_chroma; + // Multilayer extension parameters + uint8_t poc_reset_info_present_flag; + uint8_t pps_infer_scaling_list_flag; + uint8_t pps_scaling_list_ref_layer_id; + uint8_t num_ref_loc_offsets; + uint8_t ref_loc_offset_layer_id[64]; + uint8_t scaled_ref_layer_offset_present_flag[64]; + int16_t scaled_ref_layer_left_offset[64]; + int16_t scaled_ref_layer_top_offset[64]; + int16_t scaled_ref_layer_right_offset[64]; + int16_t scaled_ref_layer_bottom_offset[64]; + uint8_t ref_region_offset_present_flag[64]; + int16_t ref_region_left_offset[64]; + int16_t ref_region_top_offset[64]; + int16_t ref_region_right_offset[64]; + int16_t ref_region_bottom_offset[64]; + uint8_t resample_phase_set_present_flag[64]; + uint8_t phase_hor_luma[64]; + uint8_t phase_ver_luma[64]; + int8_t phase_hor_chroma[64]; + int8_t phase_ver_chroma[64]; + uint8_t colour_mapping_enabled_flag; + uint8_t num_cm_ref_layers; + uint8_t cm_ref_layer_id[62]; + uint8_t cm_octant_depth; + uint8_t cm_y_part_num_log2; + uint8_t luma_bit_depth_cm_input; + uint8_t chroma_bit_depth_cm_input; + uint8_t luma_bit_depth_cm_output; + uint8_t chroma_bit_depth_cm_output; + uint8_t cm_res_quant_bits; + uint8_t cm_delta_flc_bits; + int8_t cm_adapt_threshold_u_delta; + int8_t cm_adapt_threshold_v_delta; + + // 3D extension parameters + uint8_t pps_bit_depth_for_depth_layers_minus8; + + // SCC extension parameters + uint8_t pps_curr_pic_ref_enabled_flag; + uint8_t residual_adaptive_colour_transform_enabled_flag; + uint8_t pps_slice_act_qp_offsets_present_flag; + int8_t pps_act_y_qp_offset; // _plus5 + int8_t pps_act_cb_qp_offset; // _plus5 + int8_t pps_act_cr_qp_offset; // _plus3 + uint8_t pps_palette_predictor_initializers_present_flag; + uint8_t pps_num_palette_predictor_initializers; + uint8_t monochrome_palette_flag; + uint8_t luma_bit_depth_entry; + uint8_t chroma_bit_depth_entry; + uint16_t pps_palette_predictor_initializer[3][HEVC_MAX_PALETTE_PREDICTOR_SIZE]; + // Inferred parameters unsigned int *column_width; ///< ColumnWidth unsigned int *row_height; ///< RowHeight @@ -312,9 +439,9 @@ typedef struct HEVCPPS { } HEVCPPS; typedef struct HEVCParamSets { - AVBufferRef *vps_list[HEVC_MAX_VPS_COUNT]; - AVBufferRef *sps_list[HEVC_MAX_SPS_COUNT]; - AVBufferRef *pps_list[HEVC_MAX_PPS_COUNT]; + const HEVCVPS *vps_list[HEVC_MAX_VPS_COUNT]; ///< RefStruct references + const HEVCSPS *sps_list[HEVC_MAX_SPS_COUNT]; ///< RefStruct references + const HEVCPPS *pps_list[HEVC_MAX_PPS_COUNT]; ///< RefStruct references /* currently active parameter sets */ const HEVCVPS *vps; @@ -332,7 +459,8 @@ typedef struct HEVCParamSets { * to an existing VPS */ int ff_hevc_parse_sps(HEVCSPS *sps, GetBitContext *gb, unsigned int *sps_id, - int apply_defdispwin, AVBufferRef **vps_list, AVCodecContext *avctx); + int apply_defdispwin, const HEVCVPS * const *vps_list, + AVCodecContext *avctx); int ff_hevc_decode_nal_vps(GetBitContext *gb, AVCodecContext *avctx, HEVCParamSets *ps); diff --git a/libavcodec/hevc_refs.c b/libavcodec/hevc_refs.c index 811e8feff8a..54e3d40e1cf 100644 --- a/libavcodec/hevc_refs.c +++ b/libavcodec/hevc_refs.c @@ -23,12 +23,14 @@ #include "libavutil/avassert.h" +#include "decode.h" #include "thread.h" #include "hevc.h" #include "hevcdec.h" +#include "refstruct.h" #include "threadframe.h" -void ff_hevc_unref_frame(HEVCContext *s, HEVCFrame *frame, int flags) +void ff_hevc_unref_frame(HEVCFrame *frame, int flags) { /* frame->frame can be NULL if context init failed */ if (!frame->frame || !frame->frame->buf[0]) @@ -36,22 +38,18 @@ void ff_hevc_unref_frame(HEVCContext *s, HEVCFrame *frame, int flags) frame->flags &= ~flags; if (!frame->flags) { - ff_thread_release_ext_buffer(s->avctx, &frame->tf); - ff_thread_release_buffer(s->avctx, frame->frame_grain); + ff_thread_release_ext_buffer(&frame->tf); + av_frame_unref(frame->frame_grain); frame->needs_fg = 0; - av_buffer_unref(&frame->tab_mvf_buf); - frame->tab_mvf = NULL; + ff_refstruct_unref(&frame->tab_mvf); - av_buffer_unref(&frame->rpl_buf); - av_buffer_unref(&frame->rpl_tab_buf); - frame->rpl_tab = NULL; + ff_refstruct_unref(&frame->rpl); + frame->nb_rpl_elems = 0; + ff_refstruct_unref(&frame->rpl_tab); frame->refPicList = NULL; - frame->collocated_ref = NULL; - - av_buffer_unref(&frame->hwaccel_priv_buf); - frame->hwaccel_picture_private = NULL; + ff_refstruct_unref(&frame->hwaccel_picture_private); } } @@ -69,7 +67,7 @@ void ff_hevc_clear_refs(HEVCContext *s) { int i; for (i = 0; i < FF_ARRAY_ELEMS(s->DPB); i++) - ff_hevc_unref_frame(s, &s->DPB[i], + ff_hevc_unref_frame(&s->DPB[i], HEVC_FRAME_FLAG_SHORT_REF | HEVC_FRAME_FLAG_LONG_REF); } @@ -78,7 +76,7 @@ void ff_hevc_flush_dpb(HEVCContext *s) { int i; for (i = 0; i < FF_ARRAY_ELEMS(s->DPB); i++) - ff_hevc_unref_frame(s, &s->DPB[i], ~0); + ff_hevc_unref_frame(&s->DPB[i], ~0); } static HEVCFrame *alloc_frame(HEVCContext *s) @@ -94,40 +92,35 @@ static HEVCFrame *alloc_frame(HEVCContext *s) if (ret < 0) return NULL; - frame->rpl_buf = av_buffer_allocz(s->pkt.nb_nals * sizeof(RefPicListTab)); - if (!frame->rpl_buf) + frame->rpl = ff_refstruct_allocz(s->pkt.nb_nals * sizeof(*frame->rpl)); + if (!frame->rpl) goto fail; + frame->nb_rpl_elems = s->pkt.nb_nals; - frame->tab_mvf_buf = av_buffer_pool_get(s->tab_mvf_pool); - if (!frame->tab_mvf_buf) + frame->tab_mvf = ff_refstruct_pool_get(s->tab_mvf_pool); + if (!frame->tab_mvf) goto fail; - frame->tab_mvf = (MvField *)frame->tab_mvf_buf->data; - frame->rpl_tab_buf = av_buffer_pool_get(s->rpl_tab_pool); - if (!frame->rpl_tab_buf) + frame->rpl_tab = ff_refstruct_pool_get(s->rpl_tab_pool); + if (!frame->rpl_tab) goto fail; - frame->rpl_tab = (RefPicListTab **)frame->rpl_tab_buf->data; frame->ctb_count = s->ps.sps->ctb_width * s->ps.sps->ctb_height; for (j = 0; j < frame->ctb_count; j++) - frame->rpl_tab[j] = (RefPicListTab *)frame->rpl_buf->data; - - frame->frame->top_field_first = s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD; - frame->frame->interlaced_frame = (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD) || (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_BOTTOM_FIELD); - - if (s->avctx->hwaccel) { - const AVHWAccel *hwaccel = s->avctx->hwaccel; - av_assert0(!frame->hwaccel_picture_private); - if (hwaccel->frame_priv_data_size) { - frame->hwaccel_priv_buf = av_buffer_allocz(hwaccel->frame_priv_data_size); - if (!frame->hwaccel_priv_buf) - goto fail; - frame->hwaccel_picture_private = frame->hwaccel_priv_buf->data; - } - } + frame->rpl_tab[j] = frame->rpl; + + if (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD) + frame->frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + if ((s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD) || + (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_BOTTOM_FIELD)) + frame->frame->flags |= AV_FRAME_FLAG_INTERLACED; + + ret = ff_hwaccel_frame_priv_alloc(s->avctx, &frame->hwaccel_picture_private); + if (ret < 0) + goto fail; return frame; fail: - ff_hevc_unref_frame(s, frame, ~0); + ff_hevc_unref_frame(frame, ~0); return NULL; } av_log(s->avctx, AV_LOG_ERROR, "Error allocating frame, DPB full.\n"); @@ -157,6 +150,7 @@ int ff_hevc_set_new_ref(HEVCContext *s, AVFrame **frame, int poc) *frame = ref->frame; s->ref = ref; + s->collocated_ref = NULL; if (s->sh.pic_output_flag) ref->flags = HEVC_FRAME_FLAG_OUTPUT | HEVC_FRAME_FLAG_SHORT_REF; @@ -178,7 +172,7 @@ static void unref_missing_refs(HEVCContext *s) for (int i = 0; i < FF_ARRAY_ELEMS(s->DPB); i++) { HEVCFrame *frame = &s->DPB[i]; if (frame->sequence == HEVC_SEQUENCE_COUNTER_INVALID) { - ff_hevc_unref_frame(s, frame, ~0); + ff_hevc_unref_frame(frame, ~0); } } } @@ -192,7 +186,7 @@ int ff_hevc_output_frame(HEVCContext *s, AVFrame *out, int flush) if ((frame->flags & mask) == HEVC_FRAME_FLAG_OUTPUT && frame->sequence != s->seq_decode) { if (s->sh.no_output_of_prior_pics_flag == 1) - ff_hevc_unref_frame(s, frame, HEVC_FRAME_FLAG_OUTPUT); + ff_hevc_unref_frame(frame, HEVC_FRAME_FLAG_OUTPUT); else frame->flags |= HEVC_FRAME_FLAG_BUMPING; } @@ -225,9 +219,9 @@ int ff_hevc_output_frame(HEVCContext *s, AVFrame *out, int flush) ret = av_frame_ref(out, frame->needs_fg ? frame->frame_grain : frame->frame); if (frame->flags & HEVC_FRAME_FLAG_BUMPING) - ff_hevc_unref_frame(s, frame, HEVC_FRAME_FLAG_OUTPUT | HEVC_FRAME_FLAG_BUMPING); + ff_hevc_unref_frame(frame, HEVC_FRAME_FLAG_OUTPUT | HEVC_FRAME_FLAG_BUMPING); else - ff_hevc_unref_frame(s, frame, HEVC_FRAME_FLAG_OUTPUT); + ff_hevc_unref_frame(frame, HEVC_FRAME_FLAG_OUTPUT); if (ret < 0) return ret; @@ -298,11 +292,11 @@ static int init_slice_rpl(HEVCContext *s) int ctb_addr_ts = s->ps.pps->ctb_addr_rs_to_ts[s->sh.slice_segment_addr]; int i; - if (s->slice_idx >= frame->rpl_buf->size / sizeof(RefPicListTab)) + if (s->slice_idx >= frame->nb_rpl_elems) return AVERROR_INVALIDDATA; for (i = ctb_addr_ts; i < ctb_count; i++) - frame->rpl_tab[i] = (RefPicListTab *)frame->rpl_buf->data + s->slice_idx; + frame->rpl_tab[i] = frame->rpl + s->slice_idx; frame->refPicList = (RefPicList *)frame->rpl_tab[ctb_addr_ts]; @@ -322,7 +316,7 @@ int ff_hevc_slice_rpl(HEVCContext *s) return ret; if (!(s->rps[ST_CURR_BEF].nb_refs + s->rps[ST_CURR_AFT].nb_refs + - s->rps[LT_CURR].nb_refs)) { + s->rps[LT_CURR].nb_refs) && !s->ps.pps->pps_curr_pic_ref_enabled_flag) { av_log(s->avctx, AV_LOG_ERROR, "Zero refs in the frame RPS.\n"); return AVERROR_INVALIDDATA; } @@ -349,6 +343,13 @@ int ff_hevc_slice_rpl(HEVCContext *s) rpl_tmp.nb_refs++; } } + // Construct RefPicList0, RefPicList1 (8-8, 8-10) + if (s->ps.pps->pps_curr_pic_ref_enabled_flag && rpl_tmp.nb_refs < HEVC_MAX_REFS) { + rpl_tmp.list[rpl_tmp.nb_refs] = s->ref->poc; + rpl_tmp.ref[rpl_tmp.nb_refs] = s->ref; + rpl_tmp.isLongTerm[rpl_tmp.nb_refs] = 1; + rpl_tmp.nb_refs++; + } } /* reorder the references if necessary */ @@ -371,9 +372,17 @@ int ff_hevc_slice_rpl(HEVCContext *s) rpl->nb_refs = FFMIN(rpl->nb_refs, sh->nb_refs[list_idx]); } + // 8-9 + if (s->ps.pps->pps_curr_pic_ref_enabled_flag && + !sh->rpl_modification_flag[list_idx] && + rpl_tmp.nb_refs > sh->nb_refs[L0]) { + rpl->list[sh->nb_refs[L0] - 1] = s->ref->poc; + rpl->ref[sh->nb_refs[L0] - 1] = s->ref; + } + if (sh->collocated_list == list_idx && sh->collocated_ref_idx < rpl->nb_refs) - s->ref->collocated_ref = rpl->ref[sh->collocated_ref_idx]; + s->collocated_ref = rpl->ref[sh->collocated_ref_idx]; } return 0; @@ -518,7 +527,7 @@ int ff_hevc_frame_rps(HEVCContext *s) fail: /* release any frames that are now unused */ for (i = 0; i < FF_ARRAY_ELEMS(s->DPB); i++) - ff_hevc_unref_frame(s, &s->DPB[i], 0); + ff_hevc_unref_frame(&s->DPB[i], 0); return ret; } @@ -541,5 +550,9 @@ int ff_hevc_frame_nb_refs(const HEVCContext *s) for (i = 0; i < long_rps->nb_refs; i++) ret += !!long_rps->used[i]; } + + if (s->ps.pps->pps_curr_pic_ref_enabled_flag) + ret++; + return ret; } diff --git a/libavcodec/hevc_sei.c b/libavcodec/hevc_sei.c index 3c6bde1b62f..abdb52acd3b 100644 --- a/libavcodec/hevc_sei.c +++ b/libavcodec/hevc_sei.c @@ -49,61 +49,14 @@ static int decode_nal_sei_decoded_picture_hash(HEVCSEIPictureHash *s, return 0; } -static int decode_nal_sei_mastering_display_info(HEVCSEIMasteringDisplay *s, - GetByteContext *gb) -{ - int i; - - if (bytestream2_get_bytes_left(gb) < 24) - return AVERROR_INVALIDDATA; - - // Mastering primaries - for (i = 0; i < 3; i++) { - s->display_primaries[i][0] = bytestream2_get_be16u(gb); - s->display_primaries[i][1] = bytestream2_get_be16u(gb); - } - // White point (x, y) - s->white_point[0] = bytestream2_get_be16u(gb); - s->white_point[1] = bytestream2_get_be16u(gb); - - // Max and min luminance of mastering display - s->max_luminance = bytestream2_get_be32u(gb); - s->min_luminance = bytestream2_get_be32u(gb); - - // As this SEI message comes before the first frame that references it, - // initialize the flag to 2 and decrement on IRAP access unit so it - // persists for the coded video sequence (e.g., between two IRAPs) - s->present = 2; - - return 0; -} - -static int decode_nal_sei_content_light_info(HEVCSEIContentLight *s, - GetByteContext *gb) -{ - if (bytestream2_get_bytes_left(gb) < 4) - return AVERROR_INVALIDDATA; - - // Max and average light levels - s->max_content_light_level = bytestream2_get_be16u(gb); - s->max_pic_average_light_level = bytestream2_get_be16u(gb); - // As this SEI message comes before the first frame that references it, - // initialize the flag to 2 and decrement on IRAP access unit so it - // persists for the coded video sequence (e.g., between two IRAPs) - s->present = 2; - - return 0; -} - static int decode_nal_sei_pic_timing(HEVCSEI *s, GetBitContext *gb, const HEVCParamSets *ps, void *logctx) { HEVCSEIPictureTiming *h = &s->picture_timing; - HEVCSPS *sps; + const HEVCSPS *sps = ps->sps_list[s->active_seq_parameter_set_id]; - if (!ps->sps_list[s->active_seq_parameter_set_id]) + if (!sps) return(AVERROR(ENOMEM)); - sps = (HEVCSPS*)ps->sps_list[s->active_seq_parameter_set_id]->data; if (sps->vui.frame_field_info_present_flag) { int pic_struct = get_bits(gb, 4); @@ -206,10 +159,6 @@ static int decode_nal_sei_prefix(GetBitContext *gb, GetByteContext *gbyte, return decode_nal_sei_decoded_picture_hash(&s->picture_hash, gbyte); case SEI_TYPE_PIC_TIMING: return decode_nal_sei_pic_timing(s, gb, ps, logctx); - case SEI_TYPE_MASTERING_DISPLAY_COLOUR_VOLUME: - return decode_nal_sei_mastering_display_info(&s->mastering_display, gbyte); - case SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO: - return decode_nal_sei_content_light_info(&s->content_light, gbyte); case SEI_TYPE_ACTIVE_PARAMETER_SETS: return decode_nal_sei_active_parameter_sets(s, gb, logctx); case SEI_TYPE_TIME_CODE: diff --git a/libavcodec/hevc_sei.h b/libavcodec/hevc_sei.h index 4189f5e6f74..a23a64ec4f8 100644 --- a/libavcodec/hevc_sei.h +++ b/libavcodec/hevc_sei.h @@ -53,20 +53,6 @@ typedef struct HEVCSEIPictureTiming { int picture_struct; } HEVCSEIPictureTiming; -typedef struct HEVCSEIMasteringDisplay { - int present; - uint16_t display_primaries[3][2]; - uint16_t white_point[2]; - uint32_t max_luminance; - uint32_t min_luminance; -} HEVCSEIMasteringDisplay; - -typedef struct HEVCSEIContentLight { - int present; - uint16_t max_content_light_level; - uint16_t max_pic_average_light_level; -} HEVCSEIContentLight; - typedef struct HEVCSEIAlternativeTransfer { int present; int preferred_transfer_characteristics; @@ -96,8 +82,6 @@ typedef struct HEVCSEI { H2645SEI common; HEVCSEIPictureHash picture_hash; HEVCSEIPictureTiming picture_timing; - HEVCSEIMasteringDisplay mastering_display; - HEVCSEIContentLight content_light; int active_seq_parameter_set_id; HEVCSEITimeCode timecode; } HEVCSEI; diff --git a/libavcodec/hevcdec.c b/libavcodec/hevcdec.c index 0e2844f47ce..425020436d2 100644 --- a/libavcodec/hevcdec.c +++ b/libavcodec/hevcdec.c @@ -28,28 +28,27 @@ #include "libavutil/attributes.h" #include "libavutil/avstring.h" #include "libavutil/common.h" -#include "libavutil/display.h" #include "libavutil/film_grain_params.h" #include "libavutil/internal.h" -#include "libavutil/mastering_display_metadata.h" #include "libavutil/md5.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/timecode.h" +#include "aom_film_grain.h" #include "bswapdsp.h" -#include "bytestream.h" #include "cabac_functions.h" #include "codec_internal.h" #include "decode.h" #include "golomb.h" #include "hevc.h" -#include "hevc_data.h" #include "hevc_parse.h" #include "hevcdec.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "internal.h" #include "profiles.h" +#include "refstruct.h" #include "thread.h" #include "threadframe.h" @@ -88,8 +87,8 @@ static void pic_arrays_free(HEVCContext *s) av_freep(&s->sh.size); av_freep(&s->sh.offset); - av_buffer_pool_uninit(&s->tab_mvf_pool); - av_buffer_pool_uninit(&s->rpl_tab_pool); + ff_refstruct_pool_uninit(&s->tab_mvf_pool); + ff_refstruct_pool_uninit(&s->rpl_tab_pool); } /* allocate arrays that depend on frame dimensions */ @@ -135,10 +134,8 @@ static int pic_arrays_init(HEVCContext *s, const HEVCSPS *sps) if (!s->horizontal_bs || !s->vertical_bs) goto fail; - s->tab_mvf_pool = av_buffer_pool_init(min_pu_size * sizeof(MvField), - av_buffer_allocz); - s->rpl_tab_pool = av_buffer_pool_init(ctb_count * sizeof(RefPicListTab), - av_buffer_allocz); + s->tab_mvf_pool = ff_refstruct_pool_alloc(min_pu_size * sizeof(MvField), 0); + s->rpl_tab_pool = ff_refstruct_pool_alloc(ctb_count * sizeof(RefPicListTab), 0); if (!s->tab_mvf_pool || !s->rpl_tab_pool) goto fail; @@ -326,7 +323,7 @@ static void export_stream_params(HEVCContext *s, const HEVCSPS *sps) { AVCodecContext *avctx = s->avctx; const HEVCParamSets *ps = &s->ps; - const HEVCVPS *vps = (const HEVCVPS*)ps->vps_list[sps->vps_id]->data; + const HEVCVPS *vps = ps->vps_list[sps->vps_id]; const HEVCWindow *ow = &sps->output_window; unsigned int num = 0, den = 0; @@ -392,7 +389,8 @@ static int export_stream_params_from_sei(HEVCContext *s) avctx->color_trc = s->sei.common.alternative_transfer.preferred_transfer_characteristics; } - if (s->sei.common.film_grain_characteristics.present) + if (s->sei.common.film_grain_characteristics.present || + s->sei.common.aom_film_grain.enable) avctx->properties |= FF_CODEC_PROPERTY_FILM_GRAIN; return 0; @@ -402,10 +400,12 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) { #define HWACCEL_MAX (CONFIG_HEVC_DXVA2_HWACCEL + \ CONFIG_HEVC_D3D11VA_HWACCEL * 2 + \ + CONFIG_HEVC_D3D12VA_HWACCEL + \ CONFIG_HEVC_NVDEC_HWACCEL + \ CONFIG_HEVC_VAAPI_HWACCEL + \ CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL + \ - CONFIG_HEVC_VDPAU_HWACCEL) + CONFIG_HEVC_VDPAU_HWACCEL + \ + CONFIG_HEVC_VULKAN_HWACCEL) enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmt = pix_fmts; switch (sps->pix_fmt) { @@ -418,6 +418,9 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) *fmt++ = AV_PIX_FMT_D3D11VA_VLD; *fmt++ = AV_PIX_FMT_D3D11; #endif +#if CONFIG_HEVC_D3D12VA_HWACCEL + *fmt++ = AV_PIX_FMT_D3D12; +#endif #if CONFIG_HEVC_VAAPI_HWACCEL *fmt++ = AV_PIX_FMT_VAAPI; #endif @@ -429,6 +432,9 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) #endif #if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; #endif break; case AV_PIX_FMT_YUV420P10: @@ -439,12 +445,18 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) *fmt++ = AV_PIX_FMT_D3D11VA_VLD; *fmt++ = AV_PIX_FMT_D3D11; #endif +#if CONFIG_HEVC_D3D12VA_HWACCEL + *fmt++ = AV_PIX_FMT_D3D12; +#endif #if CONFIG_HEVC_VAAPI_HWACCEL *fmt++ = AV_PIX_FMT_VAAPI; #endif #if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; #endif +#if CONFIG_HEVC_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; +#endif #if CONFIG_HEVC_VDPAU_HWACCEL *fmt++ = AV_PIX_FMT_VDPAU; #endif @@ -464,6 +476,9 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) #endif #if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; #endif break; case AV_PIX_FMT_YUV422P: @@ -473,12 +488,16 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) #endif #if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; #endif break; case AV_PIX_FMT_YUV444P10: #if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; #endif + /* NOTE: fallthrough */ case AV_PIX_FMT_YUV420P12: case AV_PIX_FMT_YUV444P12: #if CONFIG_HEVC_VAAPI_HWACCEL @@ -487,6 +506,9 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) #if CONFIG_HEVC_VDPAU_HWACCEL *fmt++ = AV_PIX_FMT_VDPAU; #endif +#if CONFIG_HEVC_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; +#endif #if CONFIG_HEVC_NVDEC_HWACCEL *fmt++ = AV_PIX_FMT_CUDA; #endif @@ -494,6 +516,9 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) case AV_PIX_FMT_YUV422P12: #if CONFIG_HEVC_VAAPI_HWACCEL *fmt++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; #endif break; } @@ -501,7 +526,7 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) *fmt++ = sps->pix_fmt; *fmt = AV_PIX_FMT_NONE; - return ff_thread_get_format(s->avctx, pix_fmts); + return ff_get_format(s->avctx, pix_fmts); } static int set_sps(HEVCContext *s, const HEVCSPS *sps, @@ -553,7 +578,7 @@ static int set_sps(HEVCContext *s, const HEVCSPS *sps, } s->ps.sps = sps; - s->ps.vps = (HEVCVPS*) s->ps.vps_list[s->ps.sps->vps_id]->data; + s->ps.vps = s->ps.vps_list[s->ps.sps->vps_id]; return 0; @@ -596,16 +621,16 @@ static int hls_slice_header(HEVCContext *s) return AVERROR_INVALIDDATA; } if (!sh->first_slice_in_pic_flag && - s->ps.pps != (HEVCPPS*)s->ps.pps_list[sh->pps_id]->data) { + s->ps.pps != s->ps.pps_list[sh->pps_id]) { av_log(s->avctx, AV_LOG_ERROR, "PPS changed between slices.\n"); return AVERROR_INVALIDDATA; } - s->ps.pps = (HEVCPPS*)s->ps.pps_list[sh->pps_id]->data; + s->ps.pps = s->ps.pps_list[sh->pps_id]; if (s->nal_unit_type == HEVC_NAL_CRA_NUT && s->last_eos == 1) sh->no_output_of_prior_pics_flag = 1; - if (s->ps.sps != (HEVCSPS*)s->ps.sps_list[s->ps.pps->sps_id]->data) { - const HEVCSPS *sps = (HEVCSPS*)s->ps.sps_list[s->ps.pps->sps_id]->data; + if (s->ps.sps != s->ps.sps_list[s->ps.pps->sps_id]) { + const HEVCSPS *sps = s->ps.sps_list[s->ps.pps->sps_id]; enum AVPixelFormat pix_fmt; ff_hevc_clear_refs(s); @@ -633,6 +658,10 @@ static int hls_slice_header(HEVCContext *s) if (s->ps.pps->dependent_slice_segments_enabled_flag) sh->dependent_slice_segment_flag = get_bits1(gb); + if (sh->dependent_slice_segment_flag && !s->slice_initialized) { + av_log(s->avctx, AV_LOG_ERROR, "Independent slice segment missing.\n"); + return AVERROR_INVALIDDATA; + } slice_address_length = av_ceil_log2(s->ps.sps->ctb_width * s->ps.sps->ctb_height); @@ -668,7 +697,8 @@ static int hls_slice_header(HEVCContext *s) sh->slice_type); return AVERROR_INVALIDDATA; } - if (IS_IRAP(s) && sh->slice_type != HEVC_SLICE_I) { + if (IS_IRAP(s) && sh->slice_type != HEVC_SLICE_I && + !s->ps.pps->pps_curr_pic_ref_enabled_flag) { av_log(s->avctx, AV_LOG_ERROR, "Inter slices in an IRAP frame.\n"); return AVERROR_INVALIDDATA; } @@ -731,8 +761,13 @@ static int hls_slice_header(HEVCContext *s) else sh->slice_temporal_mvp_enabled_flag = 0; } else { - s->sh.short_term_rps = NULL; - s->poc = 0; + s->poc = 0; + sh->pic_order_cnt_lsb = 0; + sh->short_term_ref_pic_set_sps_flag = 0; + sh->short_term_ref_pic_set_size = 0; + sh->short_term_rps = NULL; + sh->long_term_ref_pic_set_size = 0; + sh->slice_temporal_mvp_enabled_flag = 0; } /* 8.3.1 */ @@ -767,11 +802,11 @@ static int hls_slice_header(HEVCContext *s) sh->nb_refs[L1] = s->ps.pps->num_ref_idx_l1_default_active; if (get_bits1(gb)) { // num_ref_idx_active_override_flag - sh->nb_refs[L0] = get_ue_golomb_long(gb) + 1; + sh->nb_refs[L0] = get_ue_golomb_31(gb) + 1; if (sh->slice_type == HEVC_SLICE_B) - sh->nb_refs[L1] = get_ue_golomb_long(gb) + 1; + sh->nb_refs[L1] = get_ue_golomb_31(gb) + 1; } - if (sh->nb_refs[L0] > HEVC_MAX_REFS || sh->nb_refs[L1] > HEVC_MAX_REFS) { + if (sh->nb_refs[L0] >= HEVC_MAX_REFS || sh->nb_refs[L1] >= HEVC_MAX_REFS) { av_log(s->avctx, AV_LOG_ERROR, "Too many refs: %d/%d.\n", sh->nb_refs[L0], sh->nb_refs[L1]); return AVERROR_INVALIDDATA; @@ -839,6 +874,14 @@ static int hls_slice_header(HEVCContext *s) sh->max_num_merge_cand); return AVERROR_INVALIDDATA; } + + // Syntax in 7.3.6.1 + if (s->ps.sps->motion_vector_resolution_control_idc == 2) + sh->use_integer_mv_flag = get_bits1(gb); + else + // Inferred to be equal to motion_vector_resolution_control_idc if not present + sh->use_integer_mv_flag = s->ps.sps->motion_vector_resolution_control_idc; + } sh->slice_qp_delta = get_se_golomb(gb); @@ -856,6 +899,12 @@ static int hls_slice_header(HEVCContext *s) sh->slice_cr_qp_offset = 0; } + if (s->ps.pps->pps_slice_act_qp_offsets_present_flag) { + sh->slice_act_y_qp_offset = get_se_golomb(gb); + sh->slice_act_cb_qp_offset = get_se_golomb(gb); + sh->slice_act_cr_qp_offset = get_se_golomb(gb); + } + if (s->ps.pps->chroma_qp_offset_list_enabled_flag) sh->cu_chroma_qp_offset_enabled_flag = get_bits1(gb); else @@ -901,9 +950,6 @@ static int hls_slice_header(HEVCContext *s) } else { sh->slice_loop_filter_across_slices_enabled_flag = s->ps.pps->seq_loop_filter_across_slices_enabled_flag; } - } else if (!s->slice_initialized) { - av_log(s->avctx, AV_LOG_ERROR, "Independent slice segment missing.\n"); - return AVERROR_INVALIDDATA; } sh->num_entry_point_offsets = 0; @@ -1923,13 +1969,13 @@ static void hls_prediction_unit(HEVCLocalContext *lc, int x0, int y0, if (current_mv.pred_flag & PF_L0) { ref0 = refPicList[0].ref[current_mv.ref_idx[0]]; - if (!ref0 || !ref0->frame->data[0]) + if (!ref0 || !ref0->frame) return; hevc_await_progress(s, ref0, ¤t_mv.mv[0], y0, nPbH); } if (current_mv.pred_flag & PF_L1) { ref1 = refPicList[1].ref[current_mv.ref_idx[1]]; - if (!ref1 || !ref1->frame->data[0]) + if (!ref1 || !ref1->frame) return; hevc_await_progress(s, ref1, ¤t_mv.mv[1], y0, nPbH); } @@ -2480,7 +2526,7 @@ static void hls_decode_neighbour(HEVCLocalContext *lc, int x_ctb, int y_ctb, lc->ctb_up_left_flag = ((x_ctb > 0) && (y_ctb > 0) && (ctb_addr_in_slice-1 >= s->ps.sps->ctb_width) && (s->ps.pps->tile_id[ctb_addr_ts] == s->ps.pps->tile_id[s->ps.pps->ctb_addr_rs_to_ts[ctb_addr_rs-1 - s->ps.sps->ctb_width]])); } -static int hls_decode_entry(AVCodecContext *avctxt, void *isFilterThread) +static int hls_decode_entry(AVCodecContext *avctxt, void *arg) { HEVCContext *s = avctxt->priv_data; HEVCLocalContext *const lc = s->HEVClc; @@ -2544,14 +2590,10 @@ static int hls_decode_entry(AVCodecContext *avctxt, void *isFilterThread) static int hls_slice_data(HEVCContext *s) { - int arg[2]; - int ret[2]; + int ret = 0; - arg[0] = 0; - arg[1] = 1; - - s->avctx->execute(s->avctx, hls_decode_entry, arg, ret , 1, sizeof(int)); - return ret[0]; + s->avctx->execute(s->avctx, hls_decode_entry, NULL, &ret , 1, 0); + return ret; } static int hls_decode_entry_wpp(AVCodecContext *avctxt, void *hevc_lclist, int job, int self_id) @@ -2728,76 +2770,18 @@ static int set_side_data(HEVCContext *s) AVFrame *out = s->ref->frame; int ret; - // Decrement the mastering display flag when IRAP frame has no_rasl_output_flag=1 - // so the side data persists for the entire coded video sequence. - if (s->sei.mastering_display.present > 0 && - IS_IRAP(s) && s->no_rasl_output_flag) { - s->sei.mastering_display.present--; - } - if (s->sei.mastering_display.present) { - // HEVC uses a g,b,r ordering, which we convert to a more natural r,g,b - const int mapping[3] = {2, 0, 1}; - const int chroma_den = 50000; - const int luma_den = 10000; - int i; - AVMasteringDisplayMetadata *metadata = - av_mastering_display_metadata_create_side_data(out); - if (!metadata) - return AVERROR(ENOMEM); - - for (i = 0; i < 3; i++) { - const int j = mapping[i]; - metadata->display_primaries[i][0].num = s->sei.mastering_display.display_primaries[j][0]; - metadata->display_primaries[i][0].den = chroma_den; - metadata->display_primaries[i][1].num = s->sei.mastering_display.display_primaries[j][1]; - metadata->display_primaries[i][1].den = chroma_den; - } - metadata->white_point[0].num = s->sei.mastering_display.white_point[0]; - metadata->white_point[0].den = chroma_den; - metadata->white_point[1].num = s->sei.mastering_display.white_point[1]; - metadata->white_point[1].den = chroma_den; - - metadata->max_luminance.num = s->sei.mastering_display.max_luminance; - metadata->max_luminance.den = luma_den; - metadata->min_luminance.num = s->sei.mastering_display.min_luminance; - metadata->min_luminance.den = luma_den; - metadata->has_luminance = 1; - metadata->has_primaries = 1; - - av_log(s->avctx, AV_LOG_DEBUG, "Mastering Display Metadata:\n"); - av_log(s->avctx, AV_LOG_DEBUG, - "r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f)\n", - av_q2d(metadata->display_primaries[0][0]), - av_q2d(metadata->display_primaries[0][1]), - av_q2d(metadata->display_primaries[1][0]), - av_q2d(metadata->display_primaries[1][1]), - av_q2d(metadata->display_primaries[2][0]), - av_q2d(metadata->display_primaries[2][1]), - av_q2d(metadata->white_point[0]), av_q2d(metadata->white_point[1])); - av_log(s->avctx, AV_LOG_DEBUG, - "min_luminance=%f, max_luminance=%f\n", - av_q2d(metadata->min_luminance), av_q2d(metadata->max_luminance)); - } - // Decrement the mastering display flag when IRAP frame has no_rasl_output_flag=1 - // so the side data persists for the entire coded video sequence. - if (s->sei.content_light.present > 0 && - IS_IRAP(s) && s->no_rasl_output_flag) { - s->sei.content_light.present--; - } - if (s->sei.content_light.present) { - AVContentLightMetadata *metadata = - av_content_light_metadata_create_side_data(out); - if (!metadata) - return AVERROR(ENOMEM); - metadata->MaxCLL = s->sei.content_light.max_content_light_level; - metadata->MaxFALL = s->sei.content_light.max_pic_average_light_level; + // Decrement the mastering display and content light level flag when IRAP + // frame has no_rasl_output_flag=1 so the side data persists for the entire + // coded video sequence. + if (IS_IRAP(s) && s->no_rasl_output_flag) { + if (s->sei.common.mastering_display.present > 0) + s->sei.common.mastering_display.present--; - av_log(s->avctx, AV_LOG_DEBUG, "Content Light Level Metadata:\n"); - av_log(s->avctx, AV_LOG_DEBUG, "MaxCLL=%d, MaxFALL=%d\n", - metadata->MaxCLL, metadata->MaxFALL); + if (s->sei.common.content_light.present > 0) + s->sei.common.content_light.present--; } - ret = ff_h2645_sei_to_frame(out, &s->sei.common, AV_CODEC_ID_HEVC, NULL, + ret = ff_h2645_sei_to_frame(out, &s->sei.common, AV_CODEC_ID_HEVC, s->avctx, &s->ps.sps->vui.common, s->ps.sps->bit_depth, s->ps.sps->bit_depth_chroma, s->ref->poc /* no poc_offset in HEVC */); @@ -2807,24 +2791,27 @@ static int set_side_data(HEVCContext *s) if (s->sei.timecode.present) { uint32_t *tc_sd; char tcbuf[AV_TIMECODE_STR_SIZE]; - AVFrameSideData *tcside = av_frame_new_side_data(out, AV_FRAME_DATA_S12M_TIMECODE, - sizeof(uint32_t) * 4); - if (!tcside) - return AVERROR(ENOMEM); + AVFrameSideData *tcside; + ret = ff_frame_new_side_data(s->avctx, out, AV_FRAME_DATA_S12M_TIMECODE, + sizeof(uint32_t) * 4, &tcside); + if (ret < 0) + return ret; - tc_sd = (uint32_t*)tcside->data; - tc_sd[0] = s->sei.timecode.num_clock_ts; + if (tcside) { + tc_sd = (uint32_t*)tcside->data; + tc_sd[0] = s->sei.timecode.num_clock_ts; - for (int i = 0; i < tc_sd[0]; i++) { - int drop = s->sei.timecode.cnt_dropped_flag[i]; - int hh = s->sei.timecode.hours_value[i]; - int mm = s->sei.timecode.minutes_value[i]; - int ss = s->sei.timecode.seconds_value[i]; - int ff = s->sei.timecode.n_frames[i]; + for (int i = 0; i < tc_sd[0]; i++) { + int drop = s->sei.timecode.cnt_dropped_flag[i]; + int hh = s->sei.timecode.hours_value[i]; + int mm = s->sei.timecode.minutes_value[i]; + int ss = s->sei.timecode.seconds_value[i]; + int ff = s->sei.timecode.n_frames[i]; - tc_sd[i + 1] = av_timecode_get_smpte(s->avctx->framerate, drop, hh, mm, ss, ff); - av_timecode_make_smpte_tc_string2(tcbuf, s->avctx->framerate, tc_sd[i + 1], 0, 0); - av_dict_set(&out->metadata, "timecode", tcbuf, 0); + tc_sd[i + 1] = av_timecode_get_smpte(s->avctx->framerate, drop, hh, mm, ss, ff); + av_timecode_make_smpte_tc_string2(tcbuf, s->avctx->framerate, tc_sd[i + 1], 0, 0); + av_dict_set(&out->metadata, "timecode", tcbuf, 0); + } } s->sei.timecode.num_clock_ts = 0; @@ -2835,10 +2822,9 @@ static int set_side_data(HEVCContext *s) if (!info_ref) return AVERROR(ENOMEM); - if (!av_frame_new_side_data_from_buf(out, AV_FRAME_DATA_DYNAMIC_HDR_PLUS, info_ref)) { - av_buffer_unref(&info_ref); - return AVERROR(ENOMEM); - } + ret = ff_frame_new_side_data_from_buf(s->avctx, out, AV_FRAME_DATA_DYNAMIC_HDR_PLUS, &info_ref, NULL); + if (ret < 0) + return ret; } if (s->rpu_buf) { @@ -2897,12 +2883,30 @@ static int hevc_frame_start(HEVCContext *s) goto fail; } - s->ref->frame->key_frame = IS_IRAP(s); + if (IS_IRAP(s)) + s->ref->frame->flags |= AV_FRAME_FLAG_KEY; + else + s->ref->frame->flags &= ~AV_FRAME_FLAG_KEY; - s->ref->needs_fg = s->sei.common.film_grain_characteristics.present && + s->ref->needs_fg = (s->sei.common.film_grain_characteristics.present || + s->sei.common.aom_film_grain.enable) && !(s->avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN) && !s->avctx->hwaccel; + ret = set_side_data(s); + if (ret < 0) + goto fail; + + if (s->ref->needs_fg && + (s->sei.common.film_grain_characteristics.present && + !ff_h274_film_grain_params_supported(s->sei.common.film_grain_characteristics.model_id, + s->ref->frame->format) + || !av_film_grain_params_select(s->ref->frame))) { + av_log_once(s->avctx, AV_LOG_WARNING, AV_LOG_DEBUG, &s->film_grain_warning_shown, + "Unsupported film grain parameters. Ignoring film grain.\n"); + s->ref->needs_fg = 0; + } + if (s->ref->needs_fg) { s->ref->frame_grain->format = s->ref->frame->format; s->ref->frame_grain->width = s->ref->frame->width; @@ -2911,10 +2915,6 @@ static int hevc_frame_start(HEVCContext *s) goto fail; } - ret = set_side_data(s); - if (ret < 0) - goto fail; - s->frame->pict_type = 3 - s->sh.slice_type; if (!IS_IRAP(s)) @@ -2932,28 +2932,33 @@ static int hevc_frame_start(HEVCContext *s) fail: if (s->ref) - ff_hevc_unref_frame(s, s->ref, ~0); - s->ref = NULL; + ff_hevc_unref_frame(s->ref, ~0); + s->ref = s->collocated_ref = NULL; return ret; } static int hevc_frame_end(HEVCContext *s) { HEVCFrame *out = s->ref; - const AVFrameSideData *sd; - int ret; + const AVFilmGrainParams *fgp; + av_unused int ret; if (out->needs_fg) { - sd = av_frame_get_side_data(out->frame, AV_FRAME_DATA_FILM_GRAIN_PARAMS); - av_assert0(out->frame_grain->buf[0] && sd); - ret = ff_h274_apply_film_grain(out->frame_grain, out->frame, &s->h274db, - (AVFilmGrainParams *) sd->data); - - if (ret < 0) { - av_log(s->avctx, AV_LOG_WARNING, "Failed synthesizing film " - "grain, ignoring: %s\n", av_err2str(ret)); - out->needs_fg = 0; + av_assert0(out->frame_grain->buf[0]); + fgp = av_film_grain_params_select(out->frame); + switch (fgp->type) { + case AV_FILM_GRAIN_PARAMS_NONE: + av_assert0(0); + return AVERROR_BUG; + case AV_FILM_GRAIN_PARAMS_H274: + ret = ff_h274_apply_film_grain(out->frame_grain, out->frame, + &s->h274db, fgp); + break; + case AV_FILM_GRAIN_PARAMS_AV1: + ret = ff_aom_apply_film_grain(out->frame_grain, out->frame, fgp); + break; } + av_assert1(ret >= 0); } return 0; @@ -2971,11 +2976,9 @@ static int decode_nal_unit(HEVCContext *s, const H2645NAL *nal) switch (s->nal_unit_type) { case HEVC_NAL_VPS: - if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) { - ret = s->avctx->hwaccel->decode_params(s->avctx, - nal->type, - nal->raw_data, - nal->raw_size); + if (FF_HW_HAS_CB(s->avctx, decode_params)) { + ret = FF_HW_CALL(s->avctx, decode_params, + nal->type, nal->raw_data, nal->raw_size); if (ret < 0) goto fail; } @@ -2984,11 +2987,9 @@ static int decode_nal_unit(HEVCContext *s, const H2645NAL *nal) goto fail; break; case HEVC_NAL_SPS: - if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) { - ret = s->avctx->hwaccel->decode_params(s->avctx, - nal->type, - nal->raw_data, - nal->raw_size); + if (FF_HW_HAS_CB(s->avctx, decode_params)) { + ret = FF_HW_CALL(s->avctx, decode_params, + nal->type, nal->raw_data, nal->raw_size); if (ret < 0) goto fail; } @@ -2998,11 +2999,9 @@ static int decode_nal_unit(HEVCContext *s, const H2645NAL *nal) goto fail; break; case HEVC_NAL_PPS: - if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) { - ret = s->avctx->hwaccel->decode_params(s->avctx, - nal->type, - nal->raw_data, - nal->raw_size); + if (FF_HW_HAS_CB(s->avctx, decode_params)) { + ret = FF_HW_CALL(s->avctx, decode_params, + nal->type, nal->raw_data, nal->raw_size); if (ret < 0) goto fail; } @@ -3012,11 +3011,9 @@ static int decode_nal_unit(HEVCContext *s, const H2645NAL *nal) break; case HEVC_NAL_SEI_PREFIX: case HEVC_NAL_SEI_SUFFIX: - if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) { - ret = s->avctx->hwaccel->decode_params(s->avctx, - nal->type, - nal->raw_data, - nal->raw_size); + if (FF_HW_HAS_CB(s->avctx, decode_params)) { + ret = FF_HW_CALL(s->avctx, decode_params, + nal->type, nal->raw_data, nal->raw_size); if (ret < 0) goto fail; } @@ -3041,8 +3038,11 @@ static int decode_nal_unit(HEVCContext *s, const H2645NAL *nal) case HEVC_NAL_RASL_N: case HEVC_NAL_RASL_R: ret = hls_slice_header(s); - if (ret < 0) + if (ret < 0) { + // hls_slice_header() does not cleanup on failure thus the state now is inconsistant so we cannot use it on depandant slices + s->slice_initialized = 0; return ret; + } if (ret == 1) { ret = AVERROR_INVALIDDATA; goto fail; @@ -3102,16 +3102,23 @@ static int decode_nal_unit(HEVCContext *s, const H2645NAL *nal) } if (s->sh.first_slice_in_pic_flag && s->avctx->hwaccel) { - ret = s->avctx->hwaccel->start_frame(s->avctx, NULL, 0); + ret = FF_HW_CALL(s->avctx, start_frame, NULL, 0); if (ret < 0) goto fail; } if (s->avctx->hwaccel) { - ret = s->avctx->hwaccel->decode_slice(s->avctx, nal->raw_data, nal->raw_size); + ret = FF_HW_CALL(s->avctx, decode_slice, nal->raw_data, nal->raw_size); if (ret < 0) goto fail; } else { + if (s->avctx->profile == AV_PROFILE_HEVC_SCC) { + av_log(s->avctx, AV_LOG_ERROR, + "SCC profile is not yet implemented in hevc native decoder.\n"); + ret = AVERROR_PATCHWELCOME; + goto fail; + } + if (s->threads_number > 1 && s->sh.num_entry_point_offsets > 0) ctb_addr_ts = hls_slice_data_wpp(s, nal); else @@ -3155,7 +3162,7 @@ static int decode_nal_units(HEVCContext *s, const uint8_t *buf, int length) int i, ret = 0; int eos_at_start = 1; - s->ref = NULL; + s->ref = s->collocated_ref = NULL; s->last_eos = s->eos; s->eos = 0; s->overlap = 0; @@ -3321,7 +3328,7 @@ static int hevc_decode_extradata(HEVCContext *s, uint8_t *buf, int length, int f /* export stream parameters from the first SPS */ for (i = 0; i < FF_ARRAY_ELEMS(s->ps.sps_list); i++) { if (first && s->ps.sps_list[i]) { - const HEVCSPS *sps = (const HEVCSPS*)s->ps.sps_list[i]->data; + const HEVCSPS *sps = s->ps.sps_list[i]; export_stream_params(s, sps); break; } @@ -3360,19 +3367,26 @@ static int hevc_decode_frame(AVCodecContext *avctx, AVFrame *rframe, } sd = av_packet_get_side_data(avpkt, AV_PKT_DATA_DOVI_CONF, &sd_size); - if (sd && sd_size > 0) + if (sd && sd_size > 0) { + int old = s->dovi_ctx.dv_profile; + ff_dovi_update_cfg(&s->dovi_ctx, (AVDOVIDecoderConfigurationRecord *) sd); + if (old) + av_log(avctx, AV_LOG_DEBUG, + "New DOVI configuration record from input packet (profile %d -> %u).\n", + old, s->dovi_ctx.dv_profile); + } - s->ref = NULL; + s->ref = s->collocated_ref = NULL; ret = decode_nal_units(s, avpkt->data, avpkt->size); if (ret < 0) return ret; if (avctx->hwaccel) { - if (s->ref && (ret = avctx->hwaccel->end_frame(avctx)) < 0) { + if (s->ref && (ret = FF_HW_SIMPLE_CALL(avctx, end_frame)) < 0) { av_log(avctx, AV_LOG_ERROR, "hardware accelerator failed to decode picture\n"); - ff_hevc_unref_frame(s, s->ref, ~0); + ff_hevc_unref_frame(s->ref, ~0); return ret; } } else { @@ -3381,7 +3395,7 @@ static int hevc_decode_frame(AVCodecContext *avctx, AVFrame *rframe, s->sei.picture_hash.is_md5) { ret = verify_md5(s, s->ref->frame); if (ret < 0 && avctx->err_recognition & AV_EF_EXPLODE) { - ff_hevc_unref_frame(s, s->ref, ~0); + ff_hevc_unref_frame(s->ref, ~0); return ret; } } @@ -3401,7 +3415,7 @@ static int hevc_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return avpkt->size; } -static int hevc_ref_frame(HEVCContext *s, HEVCFrame *dst, HEVCFrame *src) +static int hevc_ref_frame(HEVCFrame *dst, HEVCFrame *src) { int ret; @@ -3416,36 +3430,20 @@ static int hevc_ref_frame(HEVCContext *s, HEVCFrame *dst, HEVCFrame *src) dst->needs_fg = 1; } - dst->tab_mvf_buf = av_buffer_ref(src->tab_mvf_buf); - if (!dst->tab_mvf_buf) - goto fail; - dst->tab_mvf = src->tab_mvf; - - dst->rpl_tab_buf = av_buffer_ref(src->rpl_tab_buf); - if (!dst->rpl_tab_buf) - goto fail; - dst->rpl_tab = src->rpl_tab; - - dst->rpl_buf = av_buffer_ref(src->rpl_buf); - if (!dst->rpl_buf) - goto fail; + dst->tab_mvf = ff_refstruct_ref(src->tab_mvf); + dst->rpl_tab = ff_refstruct_ref(src->rpl_tab); + dst->rpl = ff_refstruct_ref(src->rpl); + dst->nb_rpl_elems = src->nb_rpl_elems; dst->poc = src->poc; dst->ctb_count = src->ctb_count; dst->flags = src->flags; dst->sequence = src->sequence; - if (src->hwaccel_picture_private) { - dst->hwaccel_priv_buf = av_buffer_ref(src->hwaccel_priv_buf); - if (!dst->hwaccel_priv_buf) - goto fail; - dst->hwaccel_picture_private = dst->hwaccel_priv_buf->data; - } + ff_refstruct_replace(&dst->hwaccel_picture_private, + src->hwaccel_picture_private); return 0; -fail: - ff_hevc_unref_frame(s, dst, ~0); - return AVERROR(ENOMEM); } static av_cold int hevc_decode_free(AVCodecContext *avctx) @@ -3467,7 +3465,7 @@ static av_cold int hevc_decode_free(AVCodecContext *avctx) av_frame_free(&s->output_frame); for (i = 0; i < FF_ARRAY_ELEMS(s->DPB); i++) { - ff_hevc_unref_frame(s, &s->DPB[i], ~0); + ff_hevc_unref_frame(&s->DPB[i], ~0); av_frame_free(&s->DPB[i].frame); av_frame_free(&s->DPB[i].frame_grain); } @@ -3549,9 +3547,9 @@ static int hevc_update_thread_context(AVCodecContext *dst, int i, ret; for (i = 0; i < FF_ARRAY_ELEMS(s->DPB); i++) { - ff_hevc_unref_frame(s, &s->DPB[i], ~0); + ff_hevc_unref_frame(&s->DPB[i], ~0); if (s0->DPB[i].frame->buf[0]) { - ret = hevc_ref_frame(s, &s->DPB[i], &s0->DPB[i]); + ret = hevc_ref_frame(&s->DPB[i], &s0->DPB[i]); if (ret < 0) return ret; } @@ -3559,23 +3557,14 @@ static int hevc_update_thread_context(AVCodecContext *dst, if (s->ps.sps != s0->ps.sps) s->ps.sps = NULL; - for (i = 0; i < FF_ARRAY_ELEMS(s->ps.vps_list); i++) { - ret = av_buffer_replace(&s->ps.vps_list[i], s0->ps.vps_list[i]); - if (ret < 0) - return ret; - } + for (int i = 0; i < FF_ARRAY_ELEMS(s->ps.vps_list); i++) + ff_refstruct_replace(&s->ps.vps_list[i], s0->ps.vps_list[i]); - for (i = 0; i < FF_ARRAY_ELEMS(s->ps.sps_list); i++) { - ret = av_buffer_replace(&s->ps.sps_list[i], s0->ps.sps_list[i]); - if (ret < 0) - return ret; - } + for (int i = 0; i < FF_ARRAY_ELEMS(s->ps.sps_list); i++) + ff_refstruct_replace(&s->ps.sps_list[i], s0->ps.sps_list[i]); - for (i = 0; i < FF_ARRAY_ELEMS(s->ps.pps_list); i++) { - ret = av_buffer_replace(&s->ps.pps_list[i], s0->ps.pps_list[i]); - if (ret < 0) - return ret; - } + for (int i = 0; i < FF_ARRAY_ELEMS(s->ps.pps_list); i++) + ff_refstruct_replace(&s->ps.pps_list[i], s0->ps.pps_list[i]); if (s->ps.sps != s0->ps.sps) if ((ret = set_sps(s, s0->ps.sps, src->pix_fmt)) < 0) @@ -3594,6 +3583,8 @@ static int hevc_update_thread_context(AVCodecContext *dst, s->threads_number = s0->threads_number; s->threads_type = s0->threads_type; + s->film_grain_warning_shown = s0->film_grain_warning_shown; + if (s0->eos) { s->seq_decode = (s->seq_decode + 1) & HEVC_SEQUENCE_COUNTER_MASK; s->max_ra = INT_MAX; @@ -3612,9 +3603,7 @@ static int hevc_update_thread_context(AVCodecContext *dst, if (ret < 0) return ret; - ret = ff_dovi_ctx_replace(&s->dovi_ctx, &s0->dovi_ctx); - if (ret < 0) - return ret; + ff_dovi_ctx_replace(&s->dovi_ctx, &s0->dovi_ctx); ret = av_buffer_replace(&s->sei.common.dynamic_hdr_vivid.info, s0->sei.common.dynamic_hdr_vivid.info); @@ -3624,8 +3613,9 @@ static int hevc_update_thread_context(AVCodecContext *dst, s->sei.common.frame_packing = s0->sei.common.frame_packing; s->sei.common.display_orientation = s0->sei.common.display_orientation; s->sei.common.alternative_transfer = s0->sei.common.alternative_transfer; - s->sei.mastering_display = s0->sei.mastering_display; - s->sei.content_light = s0->sei.content_light; + s->sei.common.mastering_display = s0->sei.common.mastering_display; + s->sei.common.content_light = s0->sei.common.content_light; + s->sei.common.aom_film_grain = s0->sei.common.aom_film_grain; ret = export_stream_params_from_sei(s); if (ret < 0) @@ -3664,12 +3654,18 @@ static av_cold int hevc_decode_init(AVCodecContext *avctx) atomic_init(&s->wpp_err, 0); if (!avctx->internal->is_copy) { + const AVPacketSideData *sd; + if (avctx->extradata_size > 0 && avctx->extradata) { ret = hevc_decode_extradata(s, avctx->extradata, avctx->extradata_size, 1); if (ret < 0) { return ret; } } + + sd = ff_get_coded_side_data(avctx, AV_PKT_DATA_DOVI_CONF); + if (sd && sd->size > 0) + ff_dovi_update_cfg(&s->dovi_ctx, (AVDOVIDecoderConfigurationRecord *) sd->data); } return 0; @@ -3684,6 +3680,9 @@ static void hevc_decode_flush(AVCodecContext *avctx) av_buffer_unref(&s->rpu_buf); s->max_ra = INT_MAX; s->eos = 1; + + if (FF_HW_HAS_CB(avctx, flush)) + FF_HW_SIMPLE_CALL(avctx, flush); } #define OFFSET(x) offsetof(HEVCContext, x) @@ -3731,6 +3730,9 @@ const FFCodec ff_hevc_decoder = { #if CONFIG_HEVC_D3D11VA2_HWACCEL HWACCEL_D3D11VA2(hevc), #endif +#if CONFIG_HEVC_D3D12VA_HWACCEL + HWACCEL_D3D12VA(hevc), +#endif #if CONFIG_HEVC_NVDEC_HWACCEL HWACCEL_NVDEC(hevc), #endif @@ -3742,6 +3744,9 @@ const FFCodec ff_hevc_decoder = { #endif #if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL HWACCEL_VIDEOTOOLBOX(hevc), +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + HWACCEL_VULKAN(hevc), #endif NULL }, diff --git a/libavcodec/hevcdec.h b/libavcodec/hevcdec.h index 9d3f4adbb3c..e65a6180ca2 100644 --- a/libavcodec/hevcdec.h +++ b/libavcodec/hevcdec.h @@ -75,7 +75,7 @@ #define IS_IDR(s) ((s)->nal_unit_type == HEVC_NAL_IDR_W_RADL || (s)->nal_unit_type == HEVC_NAL_IDR_N_LP) #define IS_BLA(s) ((s)->nal_unit_type == HEVC_NAL_BLA_W_RADL || (s)->nal_unit_type == HEVC_NAL_BLA_W_LP || \ (s)->nal_unit_type == HEVC_NAL_BLA_N_LP) -#define IS_IRAP(s) ((s)->nal_unit_type >= 16 && (s)->nal_unit_type <= 23) +#define IS_IRAP(s) ((s)->nal_unit_type >= HEVC_NAL_BLA_W_LP && (s)->nal_unit_type <= HEVC_NAL_RSV_IRAP_VCL23) enum RPSType { ST_CURR_BEF = 0, @@ -86,58 +86,6 @@ enum RPSType { NB_RPS_TYPE, }; -enum SyntaxElement { - SAO_MERGE_FLAG = 0, - SAO_TYPE_IDX, - SAO_EO_CLASS, - SAO_BAND_POSITION, - SAO_OFFSET_ABS, - SAO_OFFSET_SIGN, - END_OF_SLICE_FLAG, - SPLIT_CODING_UNIT_FLAG, - CU_TRANSQUANT_BYPASS_FLAG, - SKIP_FLAG, - CU_QP_DELTA, - PRED_MODE_FLAG, - PART_MODE, - PCM_FLAG, - PREV_INTRA_LUMA_PRED_FLAG, - MPM_IDX, - REM_INTRA_LUMA_PRED_MODE, - INTRA_CHROMA_PRED_MODE, - MERGE_FLAG, - MERGE_IDX, - INTER_PRED_IDC, - REF_IDX_L0, - REF_IDX_L1, - ABS_MVD_GREATER0_FLAG, - ABS_MVD_GREATER1_FLAG, - ABS_MVD_MINUS2, - MVD_SIGN_FLAG, - MVP_LX_FLAG, - NO_RESIDUAL_DATA_FLAG, - SPLIT_TRANSFORM_FLAG, - CBF_LUMA, - CBF_CB_CR, - TRANSFORM_SKIP_FLAG, - EXPLICIT_RDPCM_FLAG, - EXPLICIT_RDPCM_DIR_FLAG, - LAST_SIGNIFICANT_COEFF_X_PREFIX, - LAST_SIGNIFICANT_COEFF_Y_PREFIX, - LAST_SIGNIFICANT_COEFF_X_SUFFIX, - LAST_SIGNIFICANT_COEFF_Y_SUFFIX, - SIGNIFICANT_COEFF_GROUP_FLAG, - SIGNIFICANT_COEFF_FLAG, - COEFF_ABS_LEVEL_GREATER1_FLAG, - COEFF_ABS_LEVEL_GREATER2_FLAG, - COEFF_ABS_LEVEL_REMAINING, - COEFF_SIGN_FLAG, - LOG2_RES_SCALE_ABS, - RES_SCALE_SIGN_FLAG, - CU_CHROMA_QP_OFFSET_FLAG, - CU_CHROMA_QP_OFFSET_IDX, -}; - enum PartMode { PART_2Nx2N = 0, PART_2NxN = 1, @@ -295,12 +243,17 @@ typedef struct SliceHeader { int slice_cb_qp_offset; int slice_cr_qp_offset; + int slice_act_y_qp_offset; + int slice_act_cb_qp_offset; + int slice_act_cr_qp_offset; + uint8_t cu_chroma_qp_offset_enabled_flag; int beta_offset; ///< beta_offset_div2 * 2 int tc_offset; ///< tc_offset_div2 * 2 - unsigned int max_num_merge_cand; ///< 5 - 5_minus_max_num_merge_cand + uint8_t max_num_merge_cand; ///< 5 - 5_minus_max_num_merge_cand + uint8_t use_integer_mv_flag; unsigned *entry_point_offset; int * offset; @@ -403,19 +356,16 @@ typedef struct HEVCFrame { AVFrame *frame_grain; ThreadFrame tf; int needs_fg; /* 1 if grain needs to be applied by the decoder */ - MvField *tab_mvf; + MvField *tab_mvf; ///< RefStruct reference RefPicList *refPicList; - RefPicListTab **rpl_tab; + RefPicListTab **rpl_tab; ///< RefStruct reference int ctb_count; int poc; - struct HEVCFrame *collocated_ref; - AVBufferRef *tab_mvf_buf; - AVBufferRef *rpl_tab_buf; - AVBufferRef *rpl_buf; + RefPicListTab *rpl; ///< RefStruct reference + int nb_rpl_elems; - AVBufferRef *hwaccel_priv_buf; - void *hwaccel_picture_private; + void *hwaccel_picture_private; ///< RefStruct reference /** * A sequence counter, so that old frames are output first @@ -512,8 +462,8 @@ typedef struct HEVCContext { HEVCSEI sei; struct AVMD5 *md5_ctx; - AVBufferPool *tab_mvf_pool; - AVBufferPool *rpl_tab_pool; + struct FFRefStructPool *tab_mvf_pool; + struct FFRefStructPool *rpl_tab_pool; ///< candidate references for the current frame RefPicList rps[5]; @@ -524,6 +474,7 @@ typedef struct HEVCContext { enum HEVCNALUnitType nal_unit_type; int temporal_id; ///< temporal_id_plus1 - 1 HEVCFrame *ref; + HEVCFrame *collocated_ref; HEVCFrame DPB[32]; int poc; int pocTid0; @@ -591,6 +542,8 @@ typedef struct HEVCContext { int nal_length_size; ///< Number of bytes used for nal length (1, 2 or 4) int nuh_layer_id; + int film_grain_warning_shown; + AVBufferRef *rpu_buf; ///< 0 or 1 Dolby Vision RPUs. DOVIContext dovi_ctx; ///< Dolby Vision decoding context } HEVCContext; @@ -683,7 +636,7 @@ int ff_hevc_output_frame(HEVCContext *s, AVFrame *frame, int flush); void ff_hevc_bump_frame(HEVCContext *s); -void ff_hevc_unref_frame(HEVCContext *s, HEVCFrame *frame, int flags); +void ff_hevc_unref_frame(HEVCFrame *frame, int flags); void ff_hevc_set_neighbour_available(HEVCLocalContext *lc, int x0, int y0, int nPbW, int nPbH); diff --git a/libavcodec/hevcdsp.c b/libavcodec/hevcdsp.c index 2ca551df1d6..630fdc012ec 100644 --- a/libavcodec/hevcdsp.c +++ b/libavcodec/hevcdsp.c @@ -91,7 +91,8 @@ static const int8_t transform[32][32] = { 90, -90, 88, -85, 82, -78, 73, -67, 61, -54, 46, -38, 31, -22, 13, -4 }, }; -DECLARE_ALIGNED(16, const int8_t, ff_hevc_epel_filters)[7][4] = { +DECLARE_ALIGNED(16, const int8_t, ff_hevc_epel_filters)[8][4] = { + { 0 }, { -2, 58, 10, -2}, { -4, 54, 16, -2}, { -6, 46, 28, -4}, @@ -101,7 +102,8 @@ DECLARE_ALIGNED(16, const int8_t, ff_hevc_epel_filters)[7][4] = { { -2, 10, 58, -2}, }; -DECLARE_ALIGNED(16, const int8_t, ff_hevc_qpel_filters)[3][16] = { +DECLARE_ALIGNED(16, const int8_t, ff_hevc_qpel_filters)[4][16] = { + { 0 }, { -1, 4,-10, 58, 17, -5, 1, 0, -1, 4,-10, 58, 17, -5, 1, 0}, { -1, 4,-11, 40, 40,-11, 4, -1, -1, 4,-11, 40, 40,-11, 4, -1}, { 0, 1, -5, 17, 58,-10, 4, -1, 0, 1, -5, 17, 58,-10, 4, -1} diff --git a/libavcodec/hevcdsp.h b/libavcodec/hevcdsp.h index 1b9c5bb6bcd..a5933dcac48 100644 --- a/libavcodec/hevcdsp.h +++ b/libavcodec/hevcdsp.h @@ -126,8 +126,9 @@ typedef struct HEVCDSPContext { void ff_hevc_dsp_init(HEVCDSPContext *hpc, int bit_depth); -extern const int8_t ff_hevc_epel_filters[7][4]; -extern const int8_t ff_hevc_qpel_filters[3][16]; +/** ff_hevc_.pel_filters[0] are dummies to simplify array addressing */ +extern const int8_t ff_hevc_epel_filters[8][4]; +extern const int8_t ff_hevc_qpel_filters[4][16]; void ff_hevc_dsp_init_aarch64(HEVCDSPContext *c, const int bit_depth); void ff_hevc_dsp_init_arm(HEVCDSPContext *c, const int bit_depth); diff --git a/libavcodec/hevcdsp_template.c b/libavcodec/hevcdsp_template.c index 725fab99edc..121c44c401c 100644 --- a/libavcodec/hevcdsp_template.c +++ b/libavcodec/hevcdsp_template.c @@ -25,6 +25,8 @@ #include "bit_depth_template.c" #include "hevcdsp.h" +#include "h26x/h2656_sao_template.c" +#include "h26x/h2656_inter_template.c" static void FUNC(put_pcm)(uint8_t *_dst, ptrdiff_t stride, int width, int height, GetBitContext *gb, int pcm_bit_depth) @@ -295,235 +297,54 @@ IDCT_DC(32) #undef SET #undef SCALE -static void FUNC(sao_band_filter)(uint8_t *_dst, const uint8_t *_src, - ptrdiff_t stride_dst, ptrdiff_t stride_src, - const int16_t *sao_offset_val, int sao_left_class, - int width, int height) -{ - pixel *dst = (pixel *)_dst; - const pixel *src = (const pixel *)_src; - int offset_table[32] = { 0 }; - int k, y, x; - int shift = BIT_DEPTH - 5; - - stride_dst /= sizeof(pixel); - stride_src /= sizeof(pixel); - - for (k = 0; k < 4; k++) - offset_table[(k + sao_left_class) & 31] = sao_offset_val[k + 1]; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = av_clip_pixel(src[x] + offset_table[(src[x] >> shift) & 31]); - dst += stride_dst; - src += stride_src; - } -} - -#define CMP(a, b) (((a) > (b)) - ((a) < (b))) - -static void FUNC(sao_edge_filter)(uint8_t *_dst, const uint8_t *_src, ptrdiff_t stride_dst, const int16_t *sao_offset_val, - int eo, int width, int height) { - - static const uint8_t edge_idx[] = { 1, 2, 0, 3, 4 }; - static const int8_t pos[4][2][2] = { - { { -1, 0 }, { 1, 0 } }, // horizontal - { { 0, -1 }, { 0, 1 } }, // vertical - { { -1, -1 }, { 1, 1 } }, // 45 degree - { { 1, -1 }, { -1, 1 } }, // 135 degree - }; - pixel *dst = (pixel *)_dst; - const pixel *src = (const pixel *)_src; - int a_stride, b_stride; - int x, y; - ptrdiff_t stride_src = (2*MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE) / sizeof(pixel); - stride_dst /= sizeof(pixel); - - a_stride = pos[eo][0][0] + pos[eo][0][1] * stride_src; - b_stride = pos[eo][1][0] + pos[eo][1][1] * stride_src; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - int diff0 = CMP(src[x], src[x + a_stride]); - int diff1 = CMP(src[x], src[x + b_stride]); - int offset_val = edge_idx[2 + diff0 + diff1]; - dst[x] = av_clip_pixel(src[x] + sao_offset_val[offset_val]); - } - src += stride_src; - dst += stride_dst; - } -} - -static void FUNC(sao_edge_restore_0)(uint8_t *_dst, const uint8_t *_src, - ptrdiff_t stride_dst, ptrdiff_t stride_src, const SAOParams *sao, - const int *borders, int _width, int _height, - int c_idx, const uint8_t *vert_edge, - const uint8_t *horiz_edge, const uint8_t *diag_edge) -{ - int x, y; - pixel *dst = (pixel *)_dst; - const pixel *src = (const pixel *)_src; - const int16_t *sao_offset_val = sao->offset_val[c_idx]; - int sao_eo_class = sao->eo_class[c_idx]; - int init_x = 0, width = _width, height = _height; - - stride_dst /= sizeof(pixel); - stride_src /= sizeof(pixel); - - if (sao_eo_class != SAO_EO_VERT) { - if (borders[0]) { - int offset_val = sao_offset_val[0]; - for (y = 0; y < height; y++) { - dst[y * stride_dst] = av_clip_pixel(src[y * stride_src] + offset_val); - } - init_x = 1; - } - if (borders[2]) { - int offset_val = sao_offset_val[0]; - int offset = width - 1; - for (x = 0; x < height; x++) { - dst[x * stride_dst + offset] = av_clip_pixel(src[x * stride_src + offset] + offset_val); - } - width--; - } - } - if (sao_eo_class != SAO_EO_HORIZ) { - if (borders[1]) { - int offset_val = sao_offset_val[0]; - for (x = init_x; x < width; x++) - dst[x] = av_clip_pixel(src[x] + offset_val); - } - if (borders[3]) { - int offset_val = sao_offset_val[0]; - ptrdiff_t y_stride_dst = stride_dst * (height - 1); - ptrdiff_t y_stride_src = stride_src * (height - 1); - for (x = init_x; x < width; x++) - dst[x + y_stride_dst] = av_clip_pixel(src[x + y_stride_src] + offset_val); - height--; - } - } -} - -static void FUNC(sao_edge_restore_1)(uint8_t *_dst, const uint8_t *_src, - ptrdiff_t stride_dst, ptrdiff_t stride_src, const SAOParams *sao, - const int *borders, int _width, int _height, - int c_idx, const uint8_t *vert_edge, - const uint8_t *horiz_edge, const uint8_t *diag_edge) -{ - int x, y; - pixel *dst = (pixel *)_dst; - const pixel *src = (const pixel *)_src; - const int16_t *sao_offset_val = sao->offset_val[c_idx]; - int sao_eo_class = sao->eo_class[c_idx]; - int init_x = 0, init_y = 0, width = _width, height = _height; - - stride_dst /= sizeof(pixel); - stride_src /= sizeof(pixel); - - if (sao_eo_class != SAO_EO_VERT) { - if (borders[0]) { - int offset_val = sao_offset_val[0]; - for (y = 0; y < height; y++) { - dst[y * stride_dst] = av_clip_pixel(src[y * stride_src] + offset_val); - } - init_x = 1; - } - if (borders[2]) { - int offset_val = sao_offset_val[0]; - int offset = width - 1; - for (x = 0; x < height; x++) { - dst[x * stride_dst + offset] = av_clip_pixel(src[x * stride_src + offset] + offset_val); - } - width--; - } - } - if (sao_eo_class != SAO_EO_HORIZ) { - if (borders[1]) { - int offset_val = sao_offset_val[0]; - for (x = init_x; x < width; x++) - dst[x] = av_clip_pixel(src[x] + offset_val); - init_y = 1; - } - if (borders[3]) { - int offset_val = sao_offset_val[0]; - ptrdiff_t y_stride_dst = stride_dst * (height - 1); - ptrdiff_t y_stride_src = stride_src * (height - 1); - for (x = init_x; x < width; x++) - dst[x + y_stride_dst] = av_clip_pixel(src[x + y_stride_src] + offset_val); - height--; - } - } - - { - int save_upper_left = !diag_edge[0] && sao_eo_class == SAO_EO_135D && !borders[0] && !borders[1]; - int save_upper_right = !diag_edge[1] && sao_eo_class == SAO_EO_45D && !borders[1] && !borders[2]; - int save_lower_right = !diag_edge[2] && sao_eo_class == SAO_EO_135D && !borders[2] && !borders[3]; - int save_lower_left = !diag_edge[3] && sao_eo_class == SAO_EO_45D && !borders[0] && !borders[3]; - - // Restore pixels that can't be modified - if(vert_edge[0] && sao_eo_class != SAO_EO_VERT) { - for(y = init_y+save_upper_left; y< height-save_lower_left; y++) - dst[y*stride_dst] = src[y*stride_src]; - } - if(vert_edge[1] && sao_eo_class != SAO_EO_VERT) { - for(y = init_y+save_upper_right; y< height-save_lower_right; y++) - dst[y*stride_dst+width-1] = src[y*stride_src+width-1]; - } - - if(horiz_edge[0] && sao_eo_class != SAO_EO_HORIZ) { - for(x = init_x+save_upper_left; x < width-save_upper_right; x++) - dst[x] = src[x]; - } - if(horiz_edge[1] && sao_eo_class != SAO_EO_HORIZ) { - for(x = init_x+save_lower_left; x < width-save_lower_right; x++) - dst[(height-1)*stride_dst+x] = src[(height-1)*stride_src+x]; - } - if(diag_edge[0] && sao_eo_class == SAO_EO_135D) - dst[0] = src[0]; - if(diag_edge[1] && sao_eo_class == SAO_EO_45D) - dst[width-1] = src[width-1]; - if(diag_edge[2] && sao_eo_class == SAO_EO_135D) - dst[stride_dst*(height-1)+width-1] = src[stride_src*(height-1)+width-1]; - if(diag_edge[3] && sao_eo_class == SAO_EO_45D) - dst[stride_dst*(height-1)] = src[stride_src*(height-1)]; - - } -} - -#undef CMP - //////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////// -static void FUNC(put_hevc_pel_pixels)(int16_t *dst, - const uint8_t *_src, ptrdiff_t _srcstride, - int height, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel *)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = src[x] << (14 - BIT_DEPTH); - src += srcstride; - dst += MAX_PB_SIZE; - } -} - -static void FUNC(put_hevc_pel_uni_pixels)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, - int height, intptr_t mx, intptr_t my, int width) -{ - int y; - const pixel *src = (const pixel *)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - - for (y = 0; y < height; y++) { - memcpy(dst, src, width * sizeof(pixel)); - src += srcstride; - dst += dststride; - } -} +#define ff_hevc_pel_filters ff_hevc_qpel_filters +#define DECL_HV_FILTER(f) \ + const uint8_t *hf = ff_hevc_ ## f ## _filters[mx]; \ + const uint8_t *vf = ff_hevc_ ## f ## _filters[my]; + +#define FW_PUT(p, f, t) \ +static void FUNC(put_hevc_## f)(int16_t *dst, const uint8_t *src, ptrdiff_t srcstride, int height, \ + intptr_t mx, intptr_t my, int width) \ +{ \ + DECL_HV_FILTER(p) \ + FUNC(put_ ## t)(dst, src, srcstride, height, hf, vf, width); \ +} + +#define FW_PUT_UNI(p, f, t) \ +static void FUNC(put_hevc_ ## f)(uint8_t *dst, ptrdiff_t dststride, const uint8_t *src, \ + ptrdiff_t srcstride, int height, intptr_t mx, intptr_t my, int width) \ +{ \ + DECL_HV_FILTER(p) \ + FUNC(put_ ## t)(dst, dststride, src, srcstride, height, hf, vf, width); \ +} + +#define FW_PUT_UNI_W(p, f, t) \ +static void FUNC(put_hevc_ ## f)(uint8_t *dst, ptrdiff_t dststride, const uint8_t *src, \ + ptrdiff_t srcstride,int height, int denom, int wx, int ox, \ + intptr_t mx, intptr_t my, int width) \ +{ \ + DECL_HV_FILTER(p) \ + FUNC(put_ ## t)(dst, dststride, src, srcstride, height, denom, wx, ox, hf, vf, width); \ +} + +#define FW_PUT_FUNCS(f, t, dir) \ + FW_PUT(f, f ## _ ## dir, t ## _ ## dir) \ + FW_PUT_UNI(f, f ## _uni_ ## dir, uni_ ## t ## _ ## dir) \ + FW_PUT_UNI_W(f, f ## _uni_w_ ## dir, uni_## t ## _w_ ## dir) + +FW_PUT(pel, pel_pixels, pixels) +FW_PUT_UNI(pel, pel_uni_pixels, uni_pixels) +FW_PUT_UNI_W(pel, pel_uni_w_pixels, uni_w_pixels) + +FW_PUT_FUNCS(qpel, luma, h ) +FW_PUT_FUNCS(qpel, luma, v ) +FW_PUT_FUNCS(qpel, luma, hv ) +FW_PUT_FUNCS(epel, chroma, h ) +FW_PUT_FUNCS(epel, chroma, v ) +FW_PUT_FUNCS(epel, chroma, hv ) static void FUNC(put_hevc_pel_bi_pixels)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, @@ -551,30 +372,6 @@ static void FUNC(put_hevc_pel_bi_pixels)(uint8_t *_dst, ptrdiff_t _dststride, co } } -static void FUNC(put_hevc_pel_uni_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, - int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel *)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - int shift = denom + 14 - BIT_DEPTH; -#if BIT_DEPTH < 14 - int offset = 1 << (shift - 1); -#else - int offset = 0; -#endif - - ox = ox * (1 << (BIT_DEPTH - 8)); - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = av_clip_pixel((((src[x] << (14 - BIT_DEPTH)) * wx + offset) >> shift) + ox); - src += srcstride; - dst += dststride; - } -} - static void FUNC(put_hevc_pel_bi_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, int denom, int wx0, int wx1, @@ -614,96 +411,6 @@ static void FUNC(put_hevc_pel_bi_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride, filter[6] * src[x + 3 * stride] + \ filter[7] * src[x + 4 * stride]) -static void FUNC(put_hevc_qpel_h)(int16_t *dst, - const uint8_t *_src, ptrdiff_t _srcstride, - int height, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel*)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); - src += srcstride; - dst += MAX_PB_SIZE; - } -} - -static void FUNC(put_hevc_qpel_v)(int16_t *dst, - const uint8_t *_src, ptrdiff_t _srcstride, - int height, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel*)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - const int8_t *filter = ff_hevc_qpel_filters[my - 1]; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8); - src += srcstride; - dst += MAX_PB_SIZE; - } -} - -static void FUNC(put_hevc_qpel_hv)(int16_t *dst, - const uint8_t *_src, - ptrdiff_t _srcstride, - int height, intptr_t mx, - intptr_t my, int width) -{ - int x, y; - const int8_t *filter; - const pixel *src = (const pixel*)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; - int16_t *tmp = tmp_array; - - src -= QPEL_EXTRA_BEFORE * srcstride; - filter = ff_hevc_qpel_filters[mx - 1]; - for (y = 0; y < height + QPEL_EXTRA; y++) { - for (x = 0; x < width; x++) - tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); - src += srcstride; - tmp += MAX_PB_SIZE; - } - - tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; - filter = ff_hevc_qpel_filters[my - 1]; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6; - tmp += MAX_PB_SIZE; - dst += MAX_PB_SIZE; - } -} - -static void FUNC(put_hevc_qpel_uni_h)(uint8_t *_dst, ptrdiff_t _dststride, - const uint8_t *_src, ptrdiff_t _srcstride, - int height, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel*)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; - int shift = 14 - BIT_DEPTH; - -#if BIT_DEPTH < 14 - int offset = 1 << (shift - 1); -#else - int offset = 0; -#endif - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = av_clip_pixel(((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift); - src += srcstride; - dst += dststride; - } -} - static void FUNC(put_hevc_qpel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, intptr_t mx, intptr_t my, int width) @@ -714,7 +421,7 @@ static void FUNC(put_hevc_qpel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, const pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; + const int8_t *filter = ff_hevc_qpel_filters[mx]; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 @@ -732,33 +439,6 @@ static void FUNC(put_hevc_qpel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, const } } -static void FUNC(put_hevc_qpel_uni_v)(uint8_t *_dst, ptrdiff_t _dststride, - const uint8_t *_src, ptrdiff_t _srcstride, - int height, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel*)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_qpel_filters[my - 1]; - int shift = 14 - BIT_DEPTH; - -#if BIT_DEPTH < 14 - int offset = 1 << (shift - 1); -#else - int offset = 0; -#endif - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = av_clip_pixel(((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + offset) >> shift); - src += srcstride; - dst += dststride; - } -} - - static void FUNC(put_hevc_qpel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, intptr_t mx, intptr_t my, int width) @@ -769,7 +449,7 @@ static void FUNC(put_hevc_qpel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_qpel_filters[my - 1]; + const int8_t *filter = ff_hevc_qpel_filters[my]; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 @@ -787,46 +467,6 @@ static void FUNC(put_hevc_qpel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, } } -static void FUNC(put_hevc_qpel_uni_hv)(uint8_t *_dst, ptrdiff_t _dststride, - const uint8_t *_src, ptrdiff_t _srcstride, - int height, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const int8_t *filter; - const pixel *src = (const pixel*)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; - int16_t *tmp = tmp_array; - int shift = 14 - BIT_DEPTH; - -#if BIT_DEPTH < 14 - int offset = 1 << (shift - 1); -#else - int offset = 0; -#endif - - src -= QPEL_EXTRA_BEFORE * srcstride; - filter = ff_hevc_qpel_filters[mx - 1]; - for (y = 0; y < height + QPEL_EXTRA; y++) { - for (x = 0; x < width; x++) - tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); - src += srcstride; - tmp += MAX_PB_SIZE; - } - - tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; - filter = ff_hevc_qpel_filters[my - 1]; - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = av_clip_pixel(((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift); - tmp += MAX_PB_SIZE; - dst += dststride; - } -} - static void FUNC(put_hevc_qpel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, intptr_t mx, intptr_t my, int width) @@ -847,7 +487,7 @@ static void FUNC(put_hevc_qpel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, #endif src -= QPEL_EXTRA_BEFORE * srcstride; - filter = ff_hevc_qpel_filters[mx - 1]; + filter = ff_hevc_qpel_filters[mx]; for (y = 0; y < height + QPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); @@ -856,7 +496,7 @@ static void FUNC(put_hevc_qpel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, } tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; - filter = ff_hevc_qpel_filters[my - 1]; + filter = ff_hevc_qpel_filters[my]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) @@ -867,33 +507,6 @@ static void FUNC(put_hevc_qpel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, } } -static void FUNC(put_hevc_qpel_uni_w_h)(uint8_t *_dst, ptrdiff_t _dststride, - const uint8_t *_src, ptrdiff_t _srcstride, - int height, int denom, int wx, int ox, - intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel*)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; - int shift = denom + 14 - BIT_DEPTH; -#if BIT_DEPTH < 14 - int offset = 1 << (shift - 1); -#else - int offset = 0; -#endif - - ox = ox * (1 << (BIT_DEPTH - 8)); - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = av_clip_pixel((((QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); - src += srcstride; - dst += dststride; - } -} - static void FUNC(put_hevc_qpel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, int denom, int wx0, int wx1, @@ -905,7 +518,7 @@ static void FUNC(put_hevc_qpel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; + const int8_t *filter = ff_hevc_qpel_filters[mx]; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; @@ -922,33 +535,6 @@ static void FUNC(put_hevc_qpel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, } } -static void FUNC(put_hevc_qpel_uni_w_v)(uint8_t *_dst, ptrdiff_t _dststride, - const uint8_t *_src, ptrdiff_t _srcstride, - int height, int denom, int wx, int ox, - intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel*)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_qpel_filters[my - 1]; - int shift = denom + 14 - BIT_DEPTH; -#if BIT_DEPTH < 14 - int offset = 1 << (shift - 1); -#else - int offset = 0; -#endif - - ox = ox * (1 << (BIT_DEPTH - 8)); - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = av_clip_pixel((((QPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); - src += srcstride; - dst += dststride; - } -} - static void FUNC(put_hevc_qpel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, int denom, int wx0, int wx1, @@ -960,7 +546,7 @@ static void FUNC(put_hevc_qpel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_qpel_filters[my - 1]; + const int8_t *filter = ff_hevc_qpel_filters[my]; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; @@ -977,47 +563,6 @@ static void FUNC(put_hevc_qpel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, } } -static void FUNC(put_hevc_qpel_uni_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, - const uint8_t *_src, ptrdiff_t _srcstride, - int height, int denom, int wx, int ox, - intptr_t mx, intptr_t my, int width) -{ - int x, y; - const int8_t *filter; - const pixel *src = (const pixel*)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - int16_t tmp_array[(MAX_PB_SIZE + QPEL_EXTRA) * MAX_PB_SIZE]; - int16_t *tmp = tmp_array; - int shift = denom + 14 - BIT_DEPTH; -#if BIT_DEPTH < 14 - int offset = 1 << (shift - 1); -#else - int offset = 0; -#endif - - src -= QPEL_EXTRA_BEFORE * srcstride; - filter = ff_hevc_qpel_filters[mx - 1]; - for (y = 0; y < height + QPEL_EXTRA; y++) { - for (x = 0; x < width; x++) - tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); - src += srcstride; - tmp += MAX_PB_SIZE; - } - - tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; - filter = ff_hevc_qpel_filters[my - 1]; - - ox = ox * (1 << (BIT_DEPTH - 8)); - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = av_clip_pixel((((QPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); - tmp += MAX_PB_SIZE; - dst += dststride; - } -} - static void FUNC(put_hevc_qpel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, int denom, int wx0, int wx1, @@ -1035,7 +580,7 @@ static void FUNC(put_hevc_qpel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, int log2Wd = denom + shift - 1; src -= QPEL_EXTRA_BEFORE * srcstride; - filter = ff_hevc_qpel_filters[mx - 1]; + filter = ff_hevc_qpel_filters[mx]; for (y = 0; y < height + QPEL_EXTRA; y++) { for (x = 0; x < width; x++) tmp[x] = QPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); @@ -1044,7 +589,7 @@ static void FUNC(put_hevc_qpel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, } tmp = tmp_array + QPEL_EXTRA_BEFORE * MAX_PB_SIZE; - filter = ff_hevc_qpel_filters[my - 1]; + filter = ff_hevc_qpel_filters[my]; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); @@ -1067,94 +612,6 @@ static void FUNC(put_hevc_qpel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, filter[2] * src[x + stride] + \ filter[3] * src[x + 2 * stride]) -static void FUNC(put_hevc_epel_h)(int16_t *dst, - const uint8_t *_src, ptrdiff_t _srcstride, - int height, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel *)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[mx - 1]; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); - src += srcstride; - dst += MAX_PB_SIZE; - } -} - -static void FUNC(put_hevc_epel_v)(int16_t *dst, - const uint8_t *_src, ptrdiff_t _srcstride, - int height, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel *)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[my - 1]; - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8); - src += srcstride; - dst += MAX_PB_SIZE; - } -} - -static void FUNC(put_hevc_epel_hv)(int16_t *dst, - const uint8_t *_src, ptrdiff_t _srcstride, - int height, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel *)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[mx - 1]; - int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; - int16_t *tmp = tmp_array; - - src -= EPEL_EXTRA_BEFORE * srcstride; - - for (y = 0; y < height + EPEL_EXTRA; y++) { - for (x = 0; x < width; x++) - tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); - src += srcstride; - tmp += MAX_PB_SIZE; - } - - tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; - filter = ff_hevc_epel_filters[my - 1]; - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6; - tmp += MAX_PB_SIZE; - dst += MAX_PB_SIZE; - } -} - -static void FUNC(put_hevc_epel_uni_h)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, - int height, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel *)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[mx - 1]; - int shift = 14 - BIT_DEPTH; -#if BIT_DEPTH < 14 - int offset = 1 << (shift - 1); -#else - int offset = 0; -#endif - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = av_clip_pixel(((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift); - src += srcstride; - dst += dststride; - } -} - static void FUNC(put_hevc_epel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, intptr_t mx, intptr_t my, int width) @@ -1164,7 +621,7 @@ static void FUNC(put_hevc_epel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[mx - 1]; + const int8_t *filter = ff_hevc_epel_filters[mx]; int shift = 14 + 1 - BIT_DEPTH; #if BIT_DEPTH < 14 int offset = 1 << (shift - 1); @@ -1182,30 +639,6 @@ static void FUNC(put_hevc_epel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, } } -static void FUNC(put_hevc_epel_uni_v)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, - int height, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel *)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[my - 1]; - int shift = 14 - BIT_DEPTH; -#if BIT_DEPTH < 14 - int offset = 1 << (shift - 1); -#else - int offset = 0; -#endif - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = av_clip_pixel(((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) + offset) >> shift); - src += srcstride; - dst += dststride; - } -} - static void FUNC(put_hevc_epel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, intptr_t mx, intptr_t my, int width) @@ -1213,7 +646,7 @@ static void FUNC(put_hevc_epel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, int x, y; const pixel *src = (const pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[my - 1]; + const int8_t *filter = ff_hevc_epel_filters[my]; pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; @@ -1232,44 +665,6 @@ static void FUNC(put_hevc_epel_bi_v)(uint8_t *_dst, ptrdiff_t _dststride, } } -static void FUNC(put_hevc_epel_uni_hv)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, - int height, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel *)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[mx - 1]; - int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; - int16_t *tmp = tmp_array; - int shift = 14 - BIT_DEPTH; -#if BIT_DEPTH < 14 - int offset = 1 << (shift - 1); -#else - int offset = 0; -#endif - - src -= EPEL_EXTRA_BEFORE * srcstride; - - for (y = 0; y < height + EPEL_EXTRA; y++) { - for (x = 0; x < width; x++) - tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); - src += srcstride; - tmp += MAX_PB_SIZE; - } - - tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; - filter = ff_hevc_epel_filters[my - 1]; - - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = av_clip_pixel(((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift); - tmp += MAX_PB_SIZE; - dst += dststride; - } -} - static void FUNC(put_hevc_epel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, intptr_t mx, intptr_t my, int width) @@ -1279,7 +674,7 @@ static void FUNC(put_hevc_epel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[mx - 1]; + const int8_t *filter = ff_hevc_epel_filters[mx]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; @@ -1299,7 +694,7 @@ static void FUNC(put_hevc_epel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; - filter = ff_hevc_epel_filters[my - 1]; + filter = ff_hevc_epel_filters[my]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) @@ -1310,32 +705,6 @@ static void FUNC(put_hevc_epel_bi_hv)(uint8_t *_dst, ptrdiff_t _dststride, } } -static void FUNC(put_hevc_epel_uni_w_h)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, - int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel *)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[mx - 1]; - int shift = denom + 14 - BIT_DEPTH; -#if BIT_DEPTH < 14 - int offset = 1 << (shift - 1); -#else - int offset = 0; -#endif - - ox = ox * (1 << (BIT_DEPTH - 8)); - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - dst[x] = av_clip_pixel((((EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); - } - dst += dststride; - src += srcstride; - } -} - static void FUNC(put_hevc_epel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, int denom, int wx0, int wx1, @@ -1346,7 +715,7 @@ static void FUNC(put_hevc_epel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[mx - 1]; + const int8_t *filter = ff_hevc_epel_filters[mx]; int shift = 14 + 1 - BIT_DEPTH; int log2Wd = denom + shift - 1; @@ -1362,32 +731,6 @@ static void FUNC(put_hevc_epel_bi_w_h)(uint8_t *_dst, ptrdiff_t _dststride, } } -static void FUNC(put_hevc_epel_uni_w_v)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, - int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel *)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[my - 1]; - int shift = denom + 14 - BIT_DEPTH; -#if BIT_DEPTH < 14 - int offset = 1 << (shift - 1); -#else - int offset = 0; -#endif - - ox = ox * (1 << (BIT_DEPTH - 8)); - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - dst[x] = av_clip_pixel((((EPEL_FILTER(src, srcstride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox); - } - dst += dststride; - src += srcstride; - } -} - static void FUNC(put_hevc_epel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, int denom, int wx0, int wx1, @@ -1396,7 +739,7 @@ static void FUNC(put_hevc_epel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, int x, y; const pixel *src = (const pixel *)_src; ptrdiff_t srcstride = _srcstride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[my - 1]; + const int8_t *filter = ff_hevc_epel_filters[my]; pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); int shift = 14 + 1 - BIT_DEPTH; @@ -1414,45 +757,6 @@ static void FUNC(put_hevc_epel_bi_w_v)(uint8_t *_dst, ptrdiff_t _dststride, } } -static void FUNC(put_hevc_epel_uni_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, - int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width) -{ - int x, y; - const pixel *src = (const pixel *)_src; - ptrdiff_t srcstride = _srcstride / sizeof(pixel); - pixel *dst = (pixel *)_dst; - ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[mx - 1]; - int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; - int16_t *tmp = tmp_array; - int shift = denom + 14 - BIT_DEPTH; -#if BIT_DEPTH < 14 - int offset = 1 << (shift - 1); -#else - int offset = 0; -#endif - - src -= EPEL_EXTRA_BEFORE * srcstride; - - for (y = 0; y < height + EPEL_EXTRA; y++) { - for (x = 0; x < width; x++) - tmp[x] = EPEL_FILTER(src, 1) >> (BIT_DEPTH - 8); - src += srcstride; - tmp += MAX_PB_SIZE; - } - - tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; - filter = ff_hevc_epel_filters[my - 1]; - - ox = ox * (1 << (BIT_DEPTH - 8)); - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) - dst[x] = av_clip_pixel((((EPEL_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox); - tmp += MAX_PB_SIZE; - dst += dststride; - } -} - static void FUNC(put_hevc_epel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, const int16_t *src2, int height, int denom, int wx0, int wx1, @@ -1463,7 +767,7 @@ static void FUNC(put_hevc_epel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, ptrdiff_t srcstride = _srcstride / sizeof(pixel); pixel *dst = (pixel *)_dst; ptrdiff_t dststride = _dststride / sizeof(pixel); - const int8_t *filter = ff_hevc_epel_filters[mx - 1]; + const int8_t *filter = ff_hevc_epel_filters[mx]; int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; int16_t *tmp = tmp_array; int shift = 14 + 1 - BIT_DEPTH; @@ -1479,7 +783,7 @@ static void FUNC(put_hevc_epel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, } tmp = tmp_array + EPEL_EXTRA_BEFORE * MAX_PB_SIZE; - filter = ff_hevc_epel_filters[my - 1]; + filter = ff_hevc_epel_filters[my]; ox0 = ox0 * (1 << (BIT_DEPTH - 8)); ox1 = ox1 * (1 << (BIT_DEPTH - 8)); @@ -1513,19 +817,20 @@ static void FUNC(put_hevc_epel_bi_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, #define TQ2 pix[2 * xstride + 3 * ystride] #define TQ3 pix[3 * xstride + 3 * ystride] +#include "h26x/h2656_deblock_template.c" + static void FUNC(hevc_loop_filter_luma)(uint8_t *_pix, ptrdiff_t _xstride, ptrdiff_t _ystride, int beta, const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q) { - int d, j; - pixel *pix = (pixel *)_pix; ptrdiff_t xstride = _xstride / sizeof(pixel); ptrdiff_t ystride = _ystride / sizeof(pixel); beta <<= BIT_DEPTH - 8; - for (j = 0; j < 2; j++) { + for (int j = 0; j < 2; j++) { + pixel* pix = (pixel*)_pix + j * 4 * ystride; const int dp0 = abs(P2 - 2 * P1 + P0); const int dq0 = abs(Q2 - 2 * Q1 + Q0); const int dp3 = abs(TP2 - 2 * TP1 + TP0); @@ -1536,10 +841,7 @@ static void FUNC(hevc_loop_filter_luma)(uint8_t *_pix, const int no_p = _no_p[j]; const int no_q = _no_q[j]; - if (d0 + d3 >= beta) { - pix += 4 * ystride; - continue; - } else { + if (d0 + d3 < beta) { const int beta_3 = beta >> 3; const int beta_2 = beta >> 2; const int tc25 = ((tc * 5 + 1) >> 1); @@ -1547,63 +849,16 @@ static void FUNC(hevc_loop_filter_luma)(uint8_t *_pix, if (abs(P3 - P0) + abs(Q3 - Q0) < beta_3 && abs(P0 - Q0) < tc25 && abs(TP3 - TP0) + abs(TQ3 - TQ0) < beta_3 && abs(TP0 - TQ0) < tc25 && (d0 << 1) < beta_2 && (d3 << 1) < beta_2) { - // strong filtering const int tc2 = tc << 1; - for (d = 0; d < 4; d++) { - const int p3 = P3; - const int p2 = P2; - const int p1 = P1; - const int p0 = P0; - const int q0 = Q0; - const int q1 = Q1; - const int q2 = Q2; - const int q3 = Q3; - if (!no_p) { - P0 = p0 + av_clip(((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3) - p0, -tc2, tc2); - P1 = p1 + av_clip(((p2 + p1 + p0 + q0 + 2) >> 2) - p1, -tc2, tc2); - P2 = p2 + av_clip(((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3) - p2, -tc2, tc2); - } - if (!no_q) { - Q0 = q0 + av_clip(((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3) - q0, -tc2, tc2); - Q1 = q1 + av_clip(((p0 + q0 + q1 + q2 + 2) >> 2) - q1, -tc2, tc2); - Q2 = q2 + av_clip(((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3) - q2, -tc2, tc2); - } - pix += ystride; - } - } else { // normal filtering + FUNC(loop_filter_luma_strong)(pix, xstride, ystride, tc2, tc2, tc2, no_p, no_q); + } else { int nd_p = 1; int nd_q = 1; - const int tc_2 = tc >> 1; if (dp0 + dp3 < ((beta + (beta >> 1)) >> 3)) nd_p = 2; if (dq0 + dq3 < ((beta + (beta >> 1)) >> 3)) nd_q = 2; - - for (d = 0; d < 4; d++) { - const int p2 = P2; - const int p1 = P1; - const int p0 = P0; - const int q0 = Q0; - const int q1 = Q1; - const int q2 = Q2; - int delta0 = (9 * (q0 - p0) - 3 * (q1 - p1) + 8) >> 4; - if (abs(delta0) < 10 * tc) { - delta0 = av_clip(delta0, -tc, tc); - if (!no_p) - P0 = av_clip_pixel(p0 + delta0); - if (!no_q) - Q0 = av_clip_pixel(q0 - delta0); - if (!no_p && nd_p > 1) { - const int deltap1 = av_clip((((p2 + p0 + 1) >> 1) - p1 + delta0) >> 1, -tc_2, tc_2); - P1 = av_clip_pixel(p1 + deltap1); - } - if (!no_q && nd_q > 1) { - const int deltaq1 = av_clip((((q2 + q0 + 1) >> 1) - q1 - delta0) >> 1, -tc_2, tc_2); - Q1 = av_clip_pixel(q1 + deltaq1); - } - } - pix += ystride; - } + FUNC(loop_filter_luma_weak)(pix, xstride, ystride, tc, beta, no_p, no_q, nd_p, nd_q); } } } @@ -1613,32 +868,19 @@ static void FUNC(hevc_loop_filter_chroma)(uint8_t *_pix, ptrdiff_t _xstride, ptrdiff_t _ystride, const int *_tc, const uint8_t *_no_p, const uint8_t *_no_q) { - int d, j, no_p, no_q; - pixel *pix = (pixel *)_pix; + int no_p, no_q; ptrdiff_t xstride = _xstride / sizeof(pixel); ptrdiff_t ystride = _ystride / sizeof(pixel); + const int size = 4; - for (j = 0; j < 2; j++) { + for (int j = 0; j < 2; j++) { + pixel *pix = (pixel *)_pix + j * size * ystride; const int tc = _tc[j] << (BIT_DEPTH - 8); - if (tc <= 0) { - pix += 4 * ystride; - continue; - } - no_p = _no_p[j]; - no_q = _no_q[j]; - - for (d = 0; d < 4; d++) { - int delta0; - const int p1 = P1; - const int p0 = P0; - const int q0 = Q0; - const int q1 = Q1; - delta0 = av_clip((((q0 - p0) * 4) + p1 - q1 + 4) >> 3, -tc, tc); - if (!no_p) - P0 = av_clip_pixel(p0 + delta0); - if (!no_q) - Q0 = av_clip_pixel(q0 - delta0); - pix += ystride; + if (tc > 0) { + no_p = _no_p[j]; + no_q = _no_q[j]; + + FUNC(loop_filter_chroma_weak)(pix, xstride, ystride, size, tc, no_p, no_q); } } } diff --git a/libavcodec/hevcpred_template.c b/libavcodec/hevcpred_template.c index 16d1c7f35ff..46bd8065233 100644 --- a/libavcodec/hevcpred_template.c +++ b/libavcodec/hevcpred_template.c @@ -213,13 +213,10 @@ do { \ j = 0; while (j < size_max_x && !IS_INTRA(j, -1)) j++; - if (j > 0) - if (cand_up_left) { - EXTEND_LEFT_CIP(top, j, j + 1); - } else { - EXTEND_LEFT_CIP(top, j, j); - top[-1] = top[0]; - } + if (j > 0) { + EXTEND_LEFT_CIP(top, j, j); + top[-1] = top[0]; + } left[-1] = top[-1]; } left[-1] = top[-1]; diff --git a/libavcodec/hnm4video.c b/libavcodec/hnm4video.c index f223bb82fc7..51e75789d2a 100644 --- a/libavcodec/hnm4video.c +++ b/libavcodec/hnm4video.c @@ -420,7 +420,7 @@ static int hnm_decode_frame(AVCodecContext *avctx, AVFrame *frame, postprocess_current_frame(avctx); copy_processed_frame(avctx, frame); frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; memcpy(frame->data[1], hnm->palette, 256 * 4); *got_frame = 1; } else if (chunk_id == HNM4_CHUNK_ID_IU) { @@ -438,7 +438,7 @@ static int hnm_decode_frame(AVCodecContext *avctx, AVFrame *frame, } copy_processed_frame(avctx, frame); frame->pict_type = AV_PICTURE_TYPE_P; - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; memcpy(frame->data[1], hnm->palette, 256 * 4); *got_frame = 1; FFSWAP(uint8_t *, hnm->current, hnm->previous); diff --git a/libavcodec/hq_hqa.c b/libavcodec/hq_hqa.c index 6ce73b7ae49..096fb65dc7a 100644 --- a/libavcodec/hq_hqa.c +++ b/libavcodec/hq_hqa.c @@ -21,6 +21,8 @@ #include #include "libavutil/attributes.h" +#include "libavutil/mem_internal.h" +#include "libavutil/thread.h" #include "avcodec.h" #include "bytestream.h" @@ -28,9 +30,9 @@ #include "codec_internal.h" #include "decode.h" #include "get_bits.h" - -#include "hq_hqa.h" +#include "hq_hqadata.h" #include "hq_hqadsp.h" +#include "vlc.h" /* HQ/HQA slices are a set of macroblocks belonging to a frame, and * they usually form a pseudorandom pattern (probably because it is @@ -48,6 +50,16 @@ * while lavc simply aligns coded_width and coded_height. */ +typedef struct HQContext { + AVCodecContext *avctx; + HQDSPContext hqhqadsp; + + DECLARE_ALIGNED(16, int16_t, block)[12][64]; +} HQContext; + +static VLCElem hq_ac_vlc[1184]; +static VLCElem hqa_cbp_vlc[32]; + static inline void put_blocks(HQContext *c, AVFrame *pic, int plane, int x, int y, int ilace, int16_t *block0, int16_t *block1) @@ -70,21 +82,21 @@ static int hq_decode_block(HQContext *c, GetBitContext *gb, int16_t block[64], if (!is_hqa) { block[0] = get_sbits(gb, 9) * 64; - q = ff_hq_quants[qsel][is_chroma][get_bits(gb, 2)]; + q = hq_quants[qsel][is_chroma][get_bits(gb, 2)]; } else { - q = ff_hq_quants[qsel][is_chroma][get_bits(gb, 2)]; + q = hq_quants[qsel][is_chroma][get_bits(gb, 2)]; block[0] = get_sbits(gb, 9) * 64; } for (;;) { - val = get_vlc2(gb, c->hq_ac_vlc.table, 9, 2); + val = get_vlc2(gb, hq_ac_vlc, 9, 2); if (val < 0) return AVERROR_INVALIDDATA; - pos += ff_hq_ac_skips[val]; + pos += hq_ac_skips[val]; if (pos >= 64) break; - block[ff_zigzag_direct[pos]] = (int)(ff_hq_ac_syms[val] * (unsigned)q[pos]) >> 12; + block[ff_zigzag_direct[pos]] = (int)(hq_ac_syms[val] * (unsigned)q[pos]) >> 12; pos++; } @@ -124,10 +136,10 @@ static int hq_decode_frame(HQContext *ctx, AVFrame *pic, GetByteContext *gbc, int slice, start_off, next_off, i, ret; if ((unsigned)prof_num >= NUM_HQ_PROFILES) { - profile = &ff_hq_profile[0]; + profile = &hq_profile[0]; avpriv_request_sample(ctx->avctx, "HQ Profile %d", prof_num); } else { - profile = &ff_hq_profile[prof_num]; + profile = &hq_profile[prof_num]; av_log(ctx->avctx, AV_LOG_VERBOSE, "HQ Profile %d\n", prof_num); } @@ -185,7 +197,7 @@ static int hqa_decode_mb(HQContext *c, AVFrame *pic, int qgroup, if (get_bits_left(gb) < 1) return AVERROR_INVALIDDATA; - cbp = get_vlc2(gb, c->hqa_cbp_vlc.table, 5, 1); + cbp = get_vlc2(gb, hqa_cbp_vlc, 5, 1); for (i = 0; i < 12; i++) memset(c->block[i], 0, sizeof(*c->block)); @@ -354,7 +366,7 @@ static int hq_hqa_decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; } - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; @@ -362,22 +374,24 @@ static int hq_hqa_decode_frame(AVCodecContext *avctx, AVFrame *pic, return avpkt->size; } -static av_cold int hq_hqa_decode_init(AVCodecContext *avctx) +static av_cold void hq_init_vlcs(void) { - HQContext *ctx = avctx->priv_data; - ctx->avctx = avctx; + VLC_INIT_STATIC_TABLE(hqa_cbp_vlc, 5, FF_ARRAY_ELEMS(cbp_vlc_lens), + cbp_vlc_lens, 1, 1, cbp_vlc_bits, 1, 1, 0); - ff_hqdsp_init(&ctx->hqhqadsp); - - return ff_hq_init_vlcs(ctx); + VLC_INIT_STATIC_TABLE(hq_ac_vlc, 9, NUM_HQ_AC_ENTRIES, + hq_ac_bits, 1, 1, hq_ac_codes, 2, 2, 0); } -static av_cold int hq_hqa_decode_close(AVCodecContext *avctx) +static av_cold int hq_hqa_decode_init(AVCodecContext *avctx) { + static AVOnce init_static_once = AV_ONCE_INIT; HQContext *ctx = avctx->priv_data; + ctx->avctx = avctx; + + ff_hqdsp_init(&ctx->hqhqadsp); - ff_free_vlc(&ctx->hq_ac_vlc); - ff_free_vlc(&ctx->hqa_cbp_vlc); + ff_thread_once(&init_static_once, hq_init_vlcs); return 0; } @@ -390,7 +404,5 @@ const FFCodec ff_hq_hqa_decoder = { .priv_data_size = sizeof(HQContext), .init = hq_hqa_decode_init, FF_CODEC_DECODE_CB(hq_hqa_decode_frame), - .close = hq_hqa_decode_close, .p.capabilities = AV_CODEC_CAP_DR1, - .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/hq_hqa.h b/libavcodec/hq_hqa.h deleted file mode 100644 index 71aa36706c0..00000000000 --- a/libavcodec/hq_hqa.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Canopus HQ/HQA decoder - * - * 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 - */ - -#ifndef AVCODEC_HQ_HQA_H -#define AVCODEC_HQ_HQA_H - -#include - -#include "libavutil/mem_internal.h" - -#include "avcodec.h" -#include "hq_hqadsp.h" -#include "vlc.h" - -#define NUM_HQ_AC_ENTRIES 746 -#define NUM_HQ_PROFILES 22 -#define NUM_HQ_QUANTS 16 - -typedef struct HQContext { - AVCodecContext *avctx; - HQDSPContext hqhqadsp; - - VLC hq_ac_vlc; - VLC hqa_cbp_vlc; - DECLARE_ALIGNED(16, int16_t, block)[12][64]; -} HQContext; - -typedef struct HQProfile { - const uint8_t *perm_tab; - int width, height; - int num_slices; - int tab_w, tab_h; -} HQProfile; - -extern const int32_t * const ff_hq_quants[16][2][4]; -extern const HQProfile ff_hq_profile[NUM_HQ_PROFILES]; - -extern const uint8_t ff_hq_ac_skips[NUM_HQ_AC_ENTRIES]; -extern const int16_t ff_hq_ac_syms [NUM_HQ_AC_ENTRIES]; - -int ff_hq_init_vlcs(HQContext *c); - -#endif /* AVCODEC_HQ_HQA_H */ diff --git a/libavcodec/hq_hqadata.c b/libavcodec/hq_hqadata.h similarity index 99% rename from libavcodec/hq_hqadata.c rename to libavcodec/hq_hqadata.h index 56470eadc19..2faf47f0033 100644 --- a/libavcodec/hq_hqadata.c +++ b/libavcodec/hq_hqadata.h @@ -1,5 +1,5 @@ /* - * Canopus HQ/HQA decoder + * Canopus HQ/HQA data * * This file is part of FFmpeg. * @@ -18,7 +18,21 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "hq_hqa.h" +#ifndef AVCODEC_HQ_HQADATA_H +#define AVCODEC_HQ_HQADATA_H + +#include + +#define NUM_HQ_AC_ENTRIES 746 +#define NUM_HQ_PROFILES 22 +#define NUM_HQ_QUANTS 16 + +typedef struct HQProfile { + const uint8_t *perm_tab; + int width, height; + int num_slices; + int tab_w, tab_h; +} HQProfile; #define MAT_SIZE 64 @@ -1123,7 +1137,7 @@ static const int32_t qmat4D[MAT_SIZE] = { 0x24CF8B9, 0x384AC0F, 0x709581F, 0x3CDBBA7, }; -const int32_t *const ff_hq_quants[NUM_HQ_QUANTS][2][4] = { +static const int32_t *const hq_quants[NUM_HQ_QUANTS][2][4] = { { { qmat00, qmat02, qmat06, qmat0E }, { qmat01, qmat03, qmat07, qmat0F } }, { { qmat02, qmat06, qmat0E, qmat16 }, { qmat03, qmat07, qmat0F, qmat17 } }, { { qmat04, qmat0A, qmat12, qmat1E }, { qmat05, qmat0B, qmat13, qmat1F } }, @@ -1289,7 +1303,7 @@ static const uint16_t hq_ac_codes[NUM_HQ_AC_ENTRIES] = { 0xFFFE, 0xFFFF, }; -const uint8_t ff_hq_ac_skips[NUM_HQ_AC_ENTRIES] = { +static const uint8_t hq_ac_skips[NUM_HQ_AC_ENTRIES] = { 0, 0, 0, 0, 64, 1, 1, 0, 0, 0, 0, 2, 2, 1, 1, 0, 0, 0, 0, 3, 3, 4, 4, 0, 0, 0, 0, 5, 5, 6, 6, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 7, 7, 8, 8, 9, @@ -1339,7 +1353,7 @@ const uint8_t ff_hq_ac_skips[NUM_HQ_AC_ENTRIES] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -const int16_t ff_hq_ac_syms[NUM_HQ_AC_ENTRIES] = { +static const int16_t hq_ac_syms[NUM_HQ_AC_ENTRIES] = { 1, -1, 2, -2, 0, 1, -1, 3, -3, 4, -4, 1, -1, 2, -2, 5, -5, 6, -6, 1, -1, 1, -1, 7, @@ -8340,7 +8354,7 @@ static const uint8_t hq_tab_21[] = { }; /* List of profiles, order is important */ -const HQProfile ff_hq_profile[NUM_HQ_PROFILES] = { +static const HQProfile hq_profile[NUM_HQ_PROFILES] = { { hq_tab_11, 160, 120, 8, 10, 8 }, // case 0 (default) = case 11 { hq_tab_01, 720, 480, 8, 25, 54 }, { hq_tab_02, 720, 486, 8, 15, 93 }, @@ -8365,13 +8379,4 @@ const HQProfile ff_hq_profile[NUM_HQ_PROFILES] = { { hq_tab_21, 704, 576, 8, 24, 66 }, }; -av_cold int ff_hq_init_vlcs(HQContext *c) -{ - int ret = init_vlc(&c->hqa_cbp_vlc, 5, FF_ARRAY_ELEMS(cbp_vlc_lens), - cbp_vlc_lens, 1, 1, cbp_vlc_bits, 1, 1, 0); - if (ret < 0) - return ret; - - return init_vlc(&c->hq_ac_vlc, 9, NUM_HQ_AC_ENTRIES, - hq_ac_bits, 1, 1, hq_ac_codes, 2, 2, 0); -} +#endif /* AVCODEC_HQ_HQADATA_H */ diff --git a/libavcodec/hqx.c b/libavcodec/hqx.c index 60839465502..51099aa6843 100644 --- a/libavcodec/hqx.c +++ b/libavcodec/hqx.c @@ -504,7 +504,7 @@ static int hqx_decode_frame(AVCodecContext *avctx, AVFrame *frame, avctx->execute2(avctx, decode_slice_thread, NULL, NULL, 16); - ctx->pic->key_frame = 1; + ctx->pic->flags |= AV_FRAME_FLAG_KEY; ctx->pic->pict_type = AV_PICTURE_TYPE_I; *got_picture_ptr = 1; @@ -517,9 +517,9 @@ static av_cold int hqx_decode_close(AVCodecContext *avctx) int i; HQXContext *ctx = avctx->priv_data; - ff_free_vlc(&ctx->cbp_vlc); + ff_vlc_free(&ctx->cbp_vlc); for (i = 0; i < 3; i++) { - ff_free_vlc(&ctx->dc_vlc[i]); + ff_vlc_free(&ctx->dc_vlc[i]); } return 0; diff --git a/libavcodec/hqxvlc.c b/libavcodec/hqxvlc.c index 7ab24276601..1eeda4fccee 100644 --- a/libavcodec/hqxvlc.c +++ b/libavcodec/hqxvlc.c @@ -2140,7 +2140,7 @@ const HQXAC ff_hqx_ac[NUM_HQX_AC] = { #define INIT_DC_TABLE(idx, name) \ do { \ - ret = init_vlc(&ctx->dc_vlc[idx], HQX_DC_VLC_BITS, \ + ret = vlc_init(&ctx->dc_vlc[idx], HQX_DC_VLC_BITS, \ FF_ARRAY_ELEMS(name ## _vlc_lens), \ name ## _vlc_lens, 1, 1, \ name ## _vlc_bits, 2, 2, 0); \ @@ -2150,7 +2150,7 @@ const HQXAC ff_hqx_ac[NUM_HQX_AC] = { av_cold int ff_hqx_init_vlcs(HQXContext *ctx) { - int ret = init_vlc(&ctx->cbp_vlc, HQX_CBP_VLC_BITS, FF_ARRAY_ELEMS(cbp_vlc_lens), + int ret = vlc_init(&ctx->cbp_vlc, HQX_CBP_VLC_BITS, FF_ARRAY_ELEMS(cbp_vlc_lens), cbp_vlc_lens, 1, 1, cbp_vlc_bits, 1, 1, 0); if (ret < 0) return ret; diff --git a/libavcodec/huffman.c b/libavcodec/huffman.c index 99aa39c565d..d47fe100879 100644 --- a/libavcodec/huffman.c +++ b/libavcodec/huffman.c @@ -148,7 +148,7 @@ static int build_huff_tree(VLC *vlc, Node *nodes, int head, int flags, int nb_bi get_tree_codes(bits, lens, xlat, nodes, head, 0, 0, &pos, no_zero_count); - return ff_init_vlc_sparse(vlc, nb_bits, pos, lens, 2, 2, bits, 4, 4, xlat, 1, 1, 0); + return ff_vlc_init_sparse(vlc, nb_bits, pos, lens, 2, 2, bits, 4, 4, xlat, 1, 1, 0); } diff --git a/libavcodec/huffyuvdec.c b/libavcodec/huffyuvdec.c index 1a7690da941..ce6d4d4c59c 100644 --- a/libavcodec/huffyuvdec.c +++ b/libavcodec/huffyuvdec.c @@ -42,6 +42,7 @@ #include "huffyuvdsp.h" #include "lossless_videodsp.h" #include "thread.h" +#include "libavutil/emms.h" #include "libavutil/imgutils.h" #include "libavutil/pixdesc.h" @@ -168,8 +169,9 @@ static int generate_joint_tables(HYuvDecContext *s) len = (uint8_t *)(bits + (1 << VLC_BITS)); if (s->bitstream_bpp < 24 || s->version > 2) { + int count = 1 + s->alpha + 2 * s->chroma; int p, i, y, u; - for (p = 0; p < 4; p++) { + for (p = 0; p < count; p++) { int p0 = s->version > 2 ? p : 0; for (i = y = 0; y < s->vlc_n; y++) { int len0 = s->len[p0][y]; @@ -191,8 +193,8 @@ static int generate_joint_tables(HYuvDecContext *s) i++; } } - ff_free_vlc(&s->vlc[4 + p]); - if ((ret = ff_init_vlc_sparse(&s->vlc[4 + p], VLC_BITS, i, len, 1, 1, + ff_vlc_free(&s->vlc[4 + p]); + if ((ret = ff_vlc_init_sparse(&s->vlc[4 + p], VLC_BITS, i, len, 1, 1, bits, 2, 2, symbols, 2, 2, 0)) < 0) goto out; } @@ -235,8 +237,8 @@ static int generate_joint_tables(HYuvDecContext *s) } } } - ff_free_vlc(&s->vlc[4]); - if ((ret = init_vlc(&s->vlc[4], VLC_BITS, i, len, 1, 1, + ff_vlc_free(&s->vlc[4]); + if ((ret = vlc_init(&s->vlc[4], VLC_BITS, i, len, 1, 1, bits, 2, 2, 0)) < 0) goto out; } @@ -263,8 +265,8 @@ static int read_huffman_tables(HYuvDecContext *s, const uint8_t *src, int length return ret; if ((ret = ff_huffyuv_generate_bits_table(s->bits[i], s->len[i], s->vlc_n)) < 0) return ret; - ff_free_vlc(&s->vlc[i]); - if ((ret = init_vlc(&s->vlc[i], VLC_BITS, s->vlc_n, s->len[i], 1, 1, + ff_vlc_free(&s->vlc[i]); + if ((ret = vlc_init(&s->vlc[i], VLC_BITS, s->vlc_n, s->len[i], 1, 1, s->bits[i], 4, 4, 0)) < 0) return ret; } @@ -303,8 +305,8 @@ static int read_old_huffman_tables(HYuvDecContext *s) memcpy(s->len[2], s->len[1], 256 * sizeof(uint8_t)); for (i = 0; i < 4; i++) { - ff_free_vlc(&s->vlc[i]); - if ((ret = init_vlc(&s->vlc[i], VLC_BITS, 256, s->len[i], 1, 1, + ff_vlc_free(&s->vlc[i]); + if ((ret = vlc_init(&s->vlc[i], VLC_BITS, 256, s->len[i], 1, 1, s->bits[i], 4, 4, 0)) < 0) return ret; } @@ -324,7 +326,7 @@ static av_cold int decode_end(AVCodecContext *avctx) av_freep(&s->bitstream_buffer); for (i = 0; i < 8; i++) - ff_free_vlc(&s->vlc[i]); + ff_vlc_free(&s->vlc[i]); return 0; } diff --git a/libavcodec/huffyuvdsp.c b/libavcodec/huffyuvdsp.c index fb98836cc47..80587dac85f 100644 --- a/libavcodec/huffyuvdsp.c +++ b/libavcodec/huffyuvdsp.c @@ -87,7 +87,9 @@ av_cold void ff_huffyuvdsp_init(HuffYUVDSPContext *c, enum AVPixelFormat pix_fmt c->add_hfyu_median_pred_int16 = add_hfyu_median_pred_int16_c; c->add_hfyu_left_pred_bgr32 = add_hfyu_left_pred_bgr32_c; -#if ARCH_X86 +#if ARCH_RISCV + ff_huffyuvdsp_init_riscv(c, pix_fmt); +#elif ARCH_X86 ff_huffyuvdsp_init_x86(c, pix_fmt); #endif } diff --git a/libavcodec/huffyuvdsp.h b/libavcodec/huffyuvdsp.h index 90e50b54291..34bed58ef22 100644 --- a/libavcodec/huffyuvdsp.h +++ b/libavcodec/huffyuvdsp.h @@ -34,6 +34,8 @@ typedef struct HuffYUVDSPContext { } HuffYUVDSPContext; void ff_huffyuvdsp_init(HuffYUVDSPContext *c, enum AVPixelFormat pix_fmt); +void ff_huffyuvdsp_init_riscv(HuffYUVDSPContext *c, + enum AVPixelFormat pix_fmt); void ff_huffyuvdsp_init_x86(HuffYUVDSPContext *c, enum AVPixelFormat pix_fmt); #endif /* AVCODEC_HUFFYUVDSP_H */ diff --git a/libavcodec/huffyuvenc.c b/libavcodec/huffyuvenc.c index 72d6246ebe0..494ca7e6032 100644 --- a/libavcodec/huffyuvenc.c +++ b/libavcodec/huffyuvenc.c @@ -39,6 +39,7 @@ #include "huffyuvencdsp.h" #include "lossless_videoencdsp.h" #include "put_bits.h" +#include "libavutil/emms.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" @@ -761,7 +762,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame * const p = pict; int i, j, size = 0, ret; - if ((ret = ff_alloc_packet(avctx, pkt, width * height * 3 * 4 + AV_INPUT_BUFFER_MIN_SIZE)) < 0) + if ((ret = ff_alloc_packet(avctx, pkt, width * height * 3 * 4 + FF_INPUT_BUFFER_MIN_SIZE)) < 0) return ret; if (s->context) { @@ -1047,10 +1048,10 @@ static av_cold int encode_end(AVCodecContext *avctx) { "non_deterministic", "Allow multithreading for e.g. context=1 at the expense of determinism", \ OFFSET(non_determ), AV_OPT_TYPE_BOOL, { .i64 = 0 }, \ 0, 1, VE }, \ - { "pred", "Prediction method", OFFSET(predictor), AV_OPT_TYPE_INT, { .i64 = LEFT }, LEFT, MEDIAN, VE, "pred" }, \ - { "left", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LEFT }, INT_MIN, INT_MAX, VE, "pred" }, \ - { "plane", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PLANE }, INT_MIN, INT_MAX, VE, "pred" }, \ - { "median", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MEDIAN }, INT_MIN, INT_MAX, VE, "pred" }, \ + { "pred", "Prediction method", OFFSET(predictor), AV_OPT_TYPE_INT, { .i64 = LEFT }, LEFT, MEDIAN, VE, .unit = "pred" }, \ + { "left", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LEFT }, INT_MIN, INT_MAX, VE, .unit = "pred" }, \ + { "plane", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PLANE }, INT_MIN, INT_MAX, VE, .unit = "pred" }, \ + { "median", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MEDIAN }, INT_MIN, INT_MAX, VE, .unit = "pred" }, \ static const AVOption normal_options[] = { COMMON_OPTIONS diff --git a/libavcodec/hwaccel_internal.h b/libavcodec/hwaccel_internal.h new file mode 100644 index 00000000000..b0cc22bb689 --- /dev/null +++ b/libavcodec/hwaccel_internal.h @@ -0,0 +1,180 @@ +/* + * 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 + */ + +/** + * Header providing the internals of AVHWAccel. + */ + +#ifndef AVCODEC_HWACCEL_INTERNAL_H +#define AVCODEC_HWACCEL_INTERNAL_H + +#include + +#include "avcodec.h" +#include "refstruct.h" + +#define HWACCEL_CAP_ASYNC_SAFE (1 << 0) +#define HWACCEL_CAP_THREAD_SAFE (1 << 1) + +typedef struct FFHWAccel { + /** + * The public AVHWAccel. See avcodec.h for it. + */ + AVHWAccel p; + + /** + * Allocate a custom buffer + */ + int (*alloc_frame)(AVCodecContext *avctx, AVFrame *frame); + + /** + * Called at the beginning of each frame or field picture. + * + * Meaningful frame information (codec specific) is guaranteed to + * be parsed at this point. This function is mandatory. + * + * Note that buf can be NULL along with buf_size set to 0. + * Otherwise, this means the whole frame is available at this point. + * + * @param avctx the codec context + * @param buf the frame data buffer base + * @param buf_size the size of the frame in bytes + * @return zero if successful, a negative value otherwise + */ + int (*start_frame)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size); + + /** + * Callback for parameter data (SPS/PPS/VPS etc). + * + * Useful for hardware decoders which keep persistent state about the + * video parameters, and need to receive any changes to update that state. + * + * @param avctx the codec context + * @param type the nal unit type + * @param buf the nal unit data buffer + * @param buf_size the size of the nal unit in bytes + * @return zero if successful, a negative value otherwise + */ + int (*decode_params)(AVCodecContext *avctx, int type, const uint8_t *buf, uint32_t buf_size); + + /** + * Callback for each slice. + * + * Meaningful slice information (codec specific) is guaranteed to + * be parsed at this point. This function is mandatory. + * + * @param avctx the codec context + * @param buf the slice data buffer base + * @param buf_size the size of the slice in bytes + * @return zero if successful, a negative value otherwise + */ + int (*decode_slice)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size); + + /** + * Called at the end of each frame or field picture. + * + * The whole picture is parsed at this point and can now be sent + * to the hardware accelerator. This function is mandatory. + * + * @param avctx the codec context + * @return zero if successful, a negative value otherwise + */ + int (*end_frame)(AVCodecContext *avctx); + + /** + * Size of per-frame hardware accelerator private data. + * + * Private data is allocated with av_mallocz() before + * AVCodecContext.get_buffer() and deallocated after + * AVCodecContext.release_buffer(). + */ + int frame_priv_data_size; + + /** + * Size of the private data to allocate in + * AVCodecInternal.hwaccel_priv_data. + */ + int priv_data_size; + + /** + * Internal hwaccel capabilities. + */ + int caps_internal; + + /** + * Initialize the hwaccel private data. + * + * This will be called from ff_get_format(), after hwaccel and + * hwaccel_context are set and the hwaccel private data in AVCodecInternal + * is allocated. + */ + int (*init)(AVCodecContext *avctx); + + /** + * Uninitialize the hwaccel private data. + * + * This will be called from get_format() or ff_codec_close(), after hwaccel + * and hwaccel_context are already uninitialized. + */ + int (*uninit)(AVCodecContext *avctx); + + /** + * Fill the given hw_frames context with current codec parameters. Called + * from get_format. Refer to avcodec_get_hw_frames_parameters() for + * details. + * + * This CAN be called before AVHWAccel.init is called, and you must assume + * that avctx->hwaccel_priv_data is invalid. + */ + int (*frame_params)(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx); + + /** + * Copy necessary context variables from a previous thread context to the current one. + * For thread-safe hwaccels only. + */ + int (*update_thread_context)(AVCodecContext *dst, const AVCodecContext *src); + + /** + * Callback to free the hwaccel-specific frame data. + * + * @param hwctx a pointer to an AVHWDeviceContext. + * @param data the per-frame hardware accelerator private data to be freed. + */ + void (*free_frame_priv)(FFRefStructOpaque hwctx, void *data); + + /** + * Callback to flush the hwaccel state. + */ + void (*flush)(AVCodecContext *avctx); +} FFHWAccel; + +static inline const FFHWAccel *ffhwaccel(const AVHWAccel *codec) +{ + return (const FFHWAccel*)codec; +} + +#define FF_HW_CALL(avctx, function, ...) \ + (ffhwaccel((avctx)->hwaccel)->function((avctx), __VA_ARGS__)) + +#define FF_HW_SIMPLE_CALL(avctx, function) \ + (ffhwaccel((avctx)->hwaccel)->function(avctx)) + +#define FF_HW_HAS_CB(avctx, function) \ + ((avctx)->hwaccel && ffhwaccel((avctx)->hwaccel)->function) + +#endif /* AVCODEC_HWACCEL_INTERNAL */ diff --git a/libavcodec/hwaccels.h b/libavcodec/hwaccels.h index aca55831f32..5171e4c7d73 100644 --- a/libavcodec/hwaccels.h +++ b/libavcodec/hwaccels.h @@ -19,67 +19,75 @@ #ifndef AVCODEC_HWACCELS_H #define AVCODEC_HWACCELS_H -#include "avcodec.h" - -extern const AVHWAccel ff_av1_d3d11va_hwaccel; -extern const AVHWAccel ff_av1_d3d11va2_hwaccel; -extern const AVHWAccel ff_av1_dxva2_hwaccel; -extern const AVHWAccel ff_av1_nvdec_hwaccel; -extern const AVHWAccel ff_av1_vaapi_hwaccel; -extern const AVHWAccel ff_av1_vdpau_hwaccel; -extern const AVHWAccel ff_h263_vaapi_hwaccel; -extern const AVHWAccel ff_h263_videotoolbox_hwaccel; -extern const AVHWAccel ff_h264_d3d11va_hwaccel; -extern const AVHWAccel ff_h264_d3d11va2_hwaccel; -extern const AVHWAccel ff_h264_dxva2_hwaccel; -extern const AVHWAccel ff_h264_nvdec_hwaccel; -extern const AVHWAccel ff_h264_vaapi_hwaccel; -extern const AVHWAccel ff_h264_vdpau_hwaccel; -extern const AVHWAccel ff_h264_videotoolbox_hwaccel; -extern const AVHWAccel ff_hevc_d3d11va_hwaccel; -extern const AVHWAccel ff_hevc_d3d11va2_hwaccel; -extern const AVHWAccel ff_hevc_dxva2_hwaccel; -extern const AVHWAccel ff_hevc_nvdec_hwaccel; -extern const AVHWAccel ff_hevc_vaapi_hwaccel; -extern const AVHWAccel ff_hevc_vdpau_hwaccel; -extern const AVHWAccel ff_hevc_videotoolbox_hwaccel; -extern const AVHWAccel ff_mjpeg_nvdec_hwaccel; -extern const AVHWAccel ff_mjpeg_vaapi_hwaccel; -extern const AVHWAccel ff_mpeg1_nvdec_hwaccel; -extern const AVHWAccel ff_mpeg1_vdpau_hwaccel; -extern const AVHWAccel ff_mpeg1_videotoolbox_hwaccel; -extern const AVHWAccel ff_mpeg2_d3d11va_hwaccel; -extern const AVHWAccel ff_mpeg2_d3d11va2_hwaccel; -extern const AVHWAccel ff_mpeg2_nvdec_hwaccel; -extern const AVHWAccel ff_mpeg2_dxva2_hwaccel; -extern const AVHWAccel ff_mpeg2_vaapi_hwaccel; -extern const AVHWAccel ff_mpeg2_vdpau_hwaccel; -extern const AVHWAccel ff_mpeg2_videotoolbox_hwaccel; -extern const AVHWAccel ff_mpeg4_nvdec_hwaccel; -extern const AVHWAccel ff_mpeg4_vaapi_hwaccel; -extern const AVHWAccel ff_mpeg4_vdpau_hwaccel; -extern const AVHWAccel ff_mpeg4_videotoolbox_hwaccel; -extern const AVHWAccel ff_prores_videotoolbox_hwaccel; -extern const AVHWAccel ff_vc1_d3d11va_hwaccel; -extern const AVHWAccel ff_vc1_d3d11va2_hwaccel; -extern const AVHWAccel ff_vc1_dxva2_hwaccel; -extern const AVHWAccel ff_vc1_nvdec_hwaccel; -extern const AVHWAccel ff_vc1_vaapi_hwaccel; -extern const AVHWAccel ff_vc1_vdpau_hwaccel; -extern const AVHWAccel ff_vp8_nvdec_hwaccel; -extern const AVHWAccel ff_vp8_vaapi_hwaccel; -extern const AVHWAccel ff_vp9_d3d11va_hwaccel; -extern const AVHWAccel ff_vp9_d3d11va2_hwaccel; -extern const AVHWAccel ff_vp9_dxva2_hwaccel; -extern const AVHWAccel ff_vp9_nvdec_hwaccel; -extern const AVHWAccel ff_vp9_vaapi_hwaccel; -extern const AVHWAccel ff_vp9_vdpau_hwaccel; -extern const AVHWAccel ff_vp9_videotoolbox_hwaccel; -extern const AVHWAccel ff_wmv3_d3d11va_hwaccel; -extern const AVHWAccel ff_wmv3_d3d11va2_hwaccel; -extern const AVHWAccel ff_wmv3_dxva2_hwaccel; -extern const AVHWAccel ff_wmv3_nvdec_hwaccel; -extern const AVHWAccel ff_wmv3_vaapi_hwaccel; -extern const AVHWAccel ff_wmv3_vdpau_hwaccel; +extern const struct FFHWAccel ff_av1_d3d11va_hwaccel; +extern const struct FFHWAccel ff_av1_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_av1_d3d12va_hwaccel; +extern const struct FFHWAccel ff_av1_dxva2_hwaccel; +extern const struct FFHWAccel ff_av1_nvdec_hwaccel; +extern const struct FFHWAccel ff_av1_vaapi_hwaccel; +extern const struct FFHWAccel ff_av1_vdpau_hwaccel; +extern const struct FFHWAccel ff_av1_vulkan_hwaccel; +extern const struct FFHWAccel ff_h263_vaapi_hwaccel; +extern const struct FFHWAccel ff_h263_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_h264_d3d11va_hwaccel; +extern const struct FFHWAccel ff_h264_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_h264_d3d12va_hwaccel; +extern const struct FFHWAccel ff_h264_dxva2_hwaccel; +extern const struct FFHWAccel ff_h264_nvdec_hwaccel; +extern const struct FFHWAccel ff_h264_vaapi_hwaccel; +extern const struct FFHWAccel ff_h264_vdpau_hwaccel; +extern const struct FFHWAccel ff_h264_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_h264_vulkan_hwaccel; +extern const struct FFHWAccel ff_hevc_d3d11va_hwaccel; +extern const struct FFHWAccel ff_hevc_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_hevc_d3d12va_hwaccel; +extern const struct FFHWAccel ff_hevc_dxva2_hwaccel; +extern const struct FFHWAccel ff_hevc_nvdec_hwaccel; +extern const struct FFHWAccel ff_hevc_vaapi_hwaccel; +extern const struct FFHWAccel ff_hevc_vdpau_hwaccel; +extern const struct FFHWAccel ff_hevc_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_hevc_vulkan_hwaccel; +extern const struct FFHWAccel ff_mjpeg_nvdec_hwaccel; +extern const struct FFHWAccel ff_mjpeg_vaapi_hwaccel; +extern const struct FFHWAccel ff_mpeg1_nvdec_hwaccel; +extern const struct FFHWAccel ff_mpeg1_vdpau_hwaccel; +extern const struct FFHWAccel ff_mpeg1_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_mpeg2_d3d11va_hwaccel; +extern const struct FFHWAccel ff_mpeg2_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_mpeg2_d3d12va_hwaccel; +extern const struct FFHWAccel ff_mpeg2_dxva2_hwaccel; +extern const struct FFHWAccel ff_mpeg2_nvdec_hwaccel; +extern const struct FFHWAccel ff_mpeg2_vaapi_hwaccel; +extern const struct FFHWAccel ff_mpeg2_vdpau_hwaccel; +extern const struct FFHWAccel ff_mpeg2_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_mpeg4_nvdec_hwaccel; +extern const struct FFHWAccel ff_mpeg4_vaapi_hwaccel; +extern const struct FFHWAccel ff_mpeg4_vdpau_hwaccel; +extern const struct FFHWAccel ff_mpeg4_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_prores_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_vc1_d3d11va_hwaccel; +extern const struct FFHWAccel ff_vc1_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_vc1_d3d12va_hwaccel; +extern const struct FFHWAccel ff_vc1_dxva2_hwaccel; +extern const struct FFHWAccel ff_vc1_nvdec_hwaccel; +extern const struct FFHWAccel ff_vc1_vaapi_hwaccel; +extern const struct FFHWAccel ff_vc1_vdpau_hwaccel; +extern const struct FFHWAccel ff_vp8_nvdec_hwaccel; +extern const struct FFHWAccel ff_vp8_vaapi_hwaccel; +extern const struct FFHWAccel ff_vp9_d3d11va_hwaccel; +extern const struct FFHWAccel ff_vp9_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_vp9_d3d12va_hwaccel; +extern const struct FFHWAccel ff_vp9_dxva2_hwaccel; +extern const struct FFHWAccel ff_vp9_nvdec_hwaccel; +extern const struct FFHWAccel ff_vp9_vaapi_hwaccel; +extern const struct FFHWAccel ff_vp9_vdpau_hwaccel; +extern const struct FFHWAccel ff_vp9_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_wmv3_d3d11va_hwaccel; +extern const struct FFHWAccel ff_wmv3_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_wmv3_d3d12va_hwaccel; +extern const struct FFHWAccel ff_wmv3_dxva2_hwaccel; +extern const struct FFHWAccel ff_wmv3_nvdec_hwaccel; +extern const struct FFHWAccel ff_wmv3_vaapi_hwaccel; +extern const struct FFHWAccel ff_wmv3_vdpau_hwaccel; #endif /* AVCODEC_HWACCELS_H */ diff --git a/libavcodec/hwconfig.h b/libavcodec/hwconfig.h index 721424912c4..ee29ca631df 100644 --- a/libavcodec/hwconfig.h +++ b/libavcodec/hwconfig.h @@ -22,10 +22,6 @@ #include "avcodec.h" #include "hwaccels.h" - -#define HWACCEL_CAP_ASYNC_SAFE (1 << 0) - - typedef struct AVCodecHWConfigInternal { /** * This is the structure which will be returned to the user by @@ -36,9 +32,10 @@ typedef struct AVCodecHWConfigInternal { * If this configuration uses a hwaccel, a pointer to it. * If not, NULL. */ - const AVHWAccel *hwaccel; + const struct FFHWAccel *hwaccel; } AVCodecHWConfigInternal; +void ff_hwaccel_uninit(AVCodecContext *avctx); // These macros are used to simplify AVCodecHWConfigInternal definitions. @@ -76,8 +73,12 @@ typedef struct AVCodecHWConfigInternal { HW_CONFIG_HWACCEL(1, 1, 1, VDPAU, VDPAU, ff_ ## codec ## _vdpau_hwaccel) #define HWACCEL_VIDEOTOOLBOX(codec) \ HW_CONFIG_HWACCEL(1, 1, 1, VIDEOTOOLBOX, VIDEOTOOLBOX, ff_ ## codec ## _videotoolbox_hwaccel) +#define HWACCEL_VULKAN(codec) \ + HW_CONFIG_HWACCEL(1, 1, 1, VULKAN, VULKAN, ff_ ## codec ## _vulkan_hwaccel) #define HWACCEL_D3D11VA(codec) \ HW_CONFIG_HWACCEL(0, 0, 1, D3D11VA_VLD, NONE, ff_ ## codec ## _d3d11va_hwaccel) +#define HWACCEL_D3D12VA(codec) \ + HW_CONFIG_HWACCEL(1, 1, 0, D3D12, D3D12VA, ff_ ## codec ## _d3d12va_hwaccel) #define HW_CONFIG_ENCODER(device, frames, ad_hoc, format, device_type_) \ &(const AVCodecHWConfigInternal) { \ diff --git a/libavcodec/idcinvideo.c b/libavcodec/idcinvideo.c index f6b8b3cd697..d9e46335480 100644 --- a/libavcodec/idcinvideo.c +++ b/libavcodec/idcinvideo.c @@ -224,7 +224,14 @@ static int idcin_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (idcin_decode_vlcs(s, frame)) return AVERROR_INVALIDDATA; - frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif /* make the palette available on the way out */ memcpy(frame->data[1], s->pal, AVPALETTE_SIZE); diff --git a/libavcodec/idctdsp.c b/libavcodec/idctdsp.c index 7216afb094f..de879f73020 100644 --- a/libavcodec/idctdsp.c +++ b/libavcodec/idctdsp.c @@ -70,7 +70,7 @@ av_cold void ff_init_scantable_permutation(uint8_t *idct_permutation, } } -void ff_put_pixels_clamped_c(const int16_t *block, uint8_t *av_restrict pixels, +void ff_put_pixels_clamped_c(const int16_t *block, uint8_t *restrict pixels, ptrdiff_t line_size) { int i; @@ -91,7 +91,7 @@ void ff_put_pixels_clamped_c(const int16_t *block, uint8_t *av_restrict pixels, } } -static void put_pixels_clamped4_c(const int16_t *block, uint8_t *av_restrict pixels, +static void put_pixels_clamped4_c(const int16_t *block, uint8_t *restrict pixels, int line_size) { int i; @@ -108,7 +108,7 @@ static void put_pixels_clamped4_c(const int16_t *block, uint8_t *av_restrict pix } } -static void put_pixels_clamped2_c(const int16_t *block, uint8_t *av_restrict pixels, +static void put_pixels_clamped2_c(const int16_t *block, uint8_t *restrict pixels, int line_size) { int i; @@ -124,7 +124,7 @@ static void put_pixels_clamped2_c(const int16_t *block, uint8_t *av_restrict pix } static void put_signed_pixels_clamped_c(const int16_t *block, - uint8_t *av_restrict pixels, + uint8_t *restrict pixels, ptrdiff_t line_size) { int i, j; @@ -144,7 +144,7 @@ static void put_signed_pixels_clamped_c(const int16_t *block, } } -void ff_add_pixels_clamped_c(const int16_t *block, uint8_t *av_restrict pixels, +void ff_add_pixels_clamped_c(const int16_t *block, uint8_t *restrict pixels, ptrdiff_t line_size) { int i; @@ -164,7 +164,7 @@ void ff_add_pixels_clamped_c(const int16_t *block, uint8_t *av_restrict pixels, } } -static void add_pixels_clamped4_c(const int16_t *block, uint8_t *av_restrict pixels, +static void add_pixels_clamped4_c(const int16_t *block, uint8_t *restrict pixels, int line_size) { int i; @@ -180,7 +180,7 @@ static void add_pixels_clamped4_c(const int16_t *block, uint8_t *av_restrict pix } } -static void add_pixels_clamped2_c(const int16_t *block, uint8_t *av_restrict pixels, +static void add_pixels_clamped2_c(const int16_t *block, uint8_t *restrict pixels, int line_size) { int i; diff --git a/libavcodec/idctdsp.h b/libavcodec/idctdsp.h index 7224463349e..c08242881c7 100644 --- a/libavcodec/idctdsp.h +++ b/libavcodec/idctdsp.h @@ -19,11 +19,10 @@ #ifndef AVCODEC_IDCTDSP_H #define AVCODEC_IDCTDSP_H +#include #include -#include "config.h" - -#include "avcodec.h" +struct AVCodecContext; enum idct_permutation_type { FF_IDCT_PERM_NONE, @@ -44,13 +43,13 @@ int ff_init_scantable_permutation_x86(uint8_t *idct_permutation, typedef struct IDCTDSPContext { /* pixel ops : interface with DCT */ void (*put_pixels_clamped)(const int16_t *block /* align 16 */, - uint8_t *av_restrict pixels /* align 8 */, + uint8_t *restrict pixels /* align 8 */, ptrdiff_t line_size); void (*put_signed_pixels_clamped)(const int16_t *block /* align 16 */, - uint8_t *av_restrict pixels /* align 8 */, + uint8_t *restrict pixels /* align 8 */, ptrdiff_t line_size); void (*add_pixels_clamped)(const int16_t *block /* align 16 */, - uint8_t *av_restrict pixels /* align 8 */, + uint8_t *restrict pixels /* align 8 */, ptrdiff_t line_size); void (*idct)(int16_t *block /* align 16 */); @@ -90,28 +89,28 @@ typedef struct IDCTDSPContext { int mpeg4_studio_profile; } IDCTDSPContext; -void ff_put_pixels_clamped_c(const int16_t *block, uint8_t *av_restrict pixels, +void ff_put_pixels_clamped_c(const int16_t *block, uint8_t *restrict pixels, ptrdiff_t line_size); -void ff_add_pixels_clamped_c(const int16_t *block, uint8_t *av_restrict pixels, +void ff_add_pixels_clamped_c(const int16_t *block, uint8_t *restrict pixels, ptrdiff_t line_size); -void ff_idctdsp_init(IDCTDSPContext *c, AVCodecContext *avctx); +void ff_idctdsp_init(IDCTDSPContext *c, struct AVCodecContext *avctx); -void ff_idctdsp_init_aarch64(IDCTDSPContext *c, AVCodecContext *avctx, +void ff_idctdsp_init_aarch64(IDCTDSPContext *c, struct AVCodecContext *avctx, unsigned high_bit_depth); -void ff_idctdsp_init_alpha(IDCTDSPContext *c, AVCodecContext *avctx, +void ff_idctdsp_init_alpha(IDCTDSPContext *c, struct AVCodecContext *avctx, unsigned high_bit_depth); -void ff_idctdsp_init_arm(IDCTDSPContext *c, AVCodecContext *avctx, +void ff_idctdsp_init_arm(IDCTDSPContext *c, struct AVCodecContext *avctx, unsigned high_bit_depth); -void ff_idctdsp_init_ppc(IDCTDSPContext *c, AVCodecContext *avctx, +void ff_idctdsp_init_ppc(IDCTDSPContext *c, struct AVCodecContext *avctx, unsigned high_bit_depth); -void ff_idctdsp_init_riscv(IDCTDSPContext *c, AVCodecContext *avctx, +void ff_idctdsp_init_riscv(IDCTDSPContext *c, struct AVCodecContext *avctx, unsigned high_bit_depth); -void ff_idctdsp_init_x86(IDCTDSPContext *c, AVCodecContext *avctx, +void ff_idctdsp_init_x86(IDCTDSPContext *c, struct AVCodecContext *avctx, unsigned high_bit_depth); -void ff_idctdsp_init_mips(IDCTDSPContext *c, AVCodecContext *avctx, +void ff_idctdsp_init_mips(IDCTDSPContext *c, struct AVCodecContext *avctx, unsigned high_bit_depth); -void ff_idctdsp_init_loongarch(IDCTDSPContext *c, AVCodecContext *avctx, +void ff_idctdsp_init_loongarch(IDCTDSPContext *c, struct AVCodecContext *avctx, unsigned high_bit_depth); #endif /* AVCODEC_IDCTDSP_H */ diff --git a/libavcodec/iff.c b/libavcodec/iff.c index e02d2e77e53..32d771b8872 100644 --- a/libavcodec/iff.c +++ b/libavcodec/iff.c @@ -522,7 +522,7 @@ static int decode_byterun2(uint8_t *dst, int height, int line_size, GetByteContext *gb) { GetByteContext cmds; - unsigned count; + int count; int i, y_pos = 0, x_pos = 0; if (bytestream2_get_be32(gb) != MKBETAG('V', 'D', 'A', 'T')) @@ -530,7 +530,7 @@ static int decode_byterun2(uint8_t *dst, int height, int line_size, bytestream2_skip(gb, 4); count = bytestream2_get_be16(gb) - 2; - if (bytestream2_get_bytes_left(gb) < count) + if (count < 0 || bytestream2_get_bytes_left(gb) < count) return 0; bytestream2_init(&cmds, gb->buffer, count); @@ -1887,10 +1887,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } if (avpkt->flags & AV_PKT_FLAG_KEY) { - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; } else { - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; } diff --git a/libavcodec/iirfilter.c b/libavcodec/iirfilter.c index 903d64e8d46..727a3704447 100644 --- a/libavcodec/iirfilter.c +++ b/libavcodec/iirfilter.c @@ -26,8 +26,12 @@ #include +#include "config.h" + #include "libavutil/attributes.h" #include "libavutil/common.h" +#include "libavutil/log.h" +#include "libavutil/mem.h" #include "iirfilter.h" diff --git a/libavcodec/ilbcdec.c b/libavcodec/ilbcdec.c index 4ecdff41831..ba1da168bc0 100644 --- a/libavcodec/ilbcdec.c +++ b/libavcodec/ilbcdec.c @@ -1095,12 +1095,6 @@ static void do_plc(int16_t *plc_residual, /* (o) concealed residual */ if (s->consPLICount * s->block_samples > 320) { use_gain = 29491; /* 0.9 in Q15 */ - } else if (s->consPLICount * s->block_samples > 640) { - use_gain = 22938; /* 0.7 in Q15 */ - } else if (s->consPLICount * s->block_samples > 960) { - use_gain = 16384; /* 0.5 in Q15 */ - } else if (s->consPLICount * s->block_samples > 1280) { - use_gain = 0; /* 0.0 in Q15 */ } /* Compute mixing factor of picth repeatition and noise: diff --git a/libavcodec/imc.c b/libavcodec/imc.c index 174332de4da..99eb0892366 100644 --- a/libavcodec/imc.c +++ b/libavcodec/imc.c @@ -106,7 +106,7 @@ typedef struct IMCContext { AVCodecContext *avctx; } IMCContext; -static VLC huffman_vlc[4][4]; +static const VLCElem *huffman_vlc[4][4]; #define IMC_VLC_BITS 9 #define VLC_TABLES_SIZE 9512 @@ -171,16 +171,15 @@ static av_cold void iac_generate_tabs(IMCContext *q, int sampling_rate) static av_cold void imc_init_static(void) { + VLCInitState state = VLC_INIT_STATE(vlc_tables); /* initialize the VLC tables */ - for (int i = 0, offset = 0; i < 4 ; i++) { + for (int i = 0; i < 4 ; i++) { for (int j = 0; j < 4; j++) { - huffman_vlc[i][j].table = &vlc_tables[offset]; - huffman_vlc[i][j].table_allocated = VLC_TABLES_SIZE - offset; - ff_init_vlc_from_lengths(&huffman_vlc[i][j], IMC_VLC_BITS, imc_huffman_sizes[i], - imc_huffman_lens[i][j], 1, - imc_huffman_syms[i][j], 1, 1, - 0, INIT_VLC_STATIC_OVERLONG, NULL); - offset += huffman_vlc[i][j].table_size; + huffman_vlc[i][j] = + ff_vlc_init_tables_from_lengths(&state, IMC_VLC_BITS, imc_huffman_sizes[i], + imc_huffman_lens[i][j], 1, + imc_huffman_syms[i][j], 1, 1, + 0, 0); } } } @@ -311,16 +310,11 @@ static void imc_read_level_coeffs(IMCContext *q, int stream_format_code, int *levlCoeffs) { int i; - VLC *hufftab[4]; int start = 0; const uint8_t *cb_sel; - int s; + int s = stream_format_code >> 1; + const VLCElem * const *const hufftab = huffman_vlc[s]; - s = stream_format_code >> 1; - hufftab[0] = &huffman_vlc[s][0]; - hufftab[1] = &huffman_vlc[s][1]; - hufftab[2] = &huffman_vlc[s][2]; - hufftab[3] = &huffman_vlc[s][3]; cb_sel = imc_cb_select[s]; if (stream_format_code & 4) @@ -328,7 +322,7 @@ static void imc_read_level_coeffs(IMCContext *q, int stream_format_code, if (start) levlCoeffs[0] = get_bits(&q->gb, 7); for (i = start; i < BANDS; i++) { - levlCoeffs[i] = get_vlc2(&q->gb, hufftab[cb_sel[i]]->table, + levlCoeffs[i] = get_vlc2(&q->gb, hufftab[cb_sel[i]], IMC_VLC_BITS, 2); if (levlCoeffs[i] == 17) levlCoeffs[i] += get_bits(&q->gb, 4); @@ -1038,6 +1032,7 @@ const FFCodec ff_imc_decoder = { .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; #endif #if CONFIG_IAC_DECODER @@ -1054,5 +1049,6 @@ const FFCodec ff_iac_decoder = { .p.capabilities = AV_CODEC_CAP_DR1, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; #endif diff --git a/libavcodec/imm4.c b/libavcodec/imm4.c index ccec5dff43a..ef7e692b843 100644 --- a/libavcodec/imm4.c +++ b/libavcodec/imm4.c @@ -108,16 +108,16 @@ static const uint8_t block_bits[] = { 6, 5, 5, 5, 4, 2, 3, 4, 4, }; -static VLC cbplo_tab; -static VLC cbphi_tab; -static VLC blktype_tab; -static VLC block_tab; +static VLCElem cbplo_tab[1 << CBPLO_VLC_BITS]; +static VLCElem cbphi_tab[1 << CBPHI_VLC_BITS]; +static VLCElem blktype_tab[1 << BLKTYPE_VLC_BITS]; +static VLCElem block_tab[1 << BLOCK_VLC_BITS]; static int get_cbphi(GetBitContext *gb, int x) { int value; - value = get_vlc2(gb, cbphi_tab.table, CBPHI_VLC_BITS, 1); + value = get_vlc2(gb, cbphi_tab, CBPHI_VLC_BITS, 1); if (value < 0) return AVERROR_INVALIDDATA; @@ -134,7 +134,7 @@ static int decode_block(AVCodecContext *avctx, GetBitContext *gb, for (i = !flag; i < 64; i++) { int value; - value = get_vlc2(gb, block_tab.table, BLOCK_VLC_BITS, 1); + value = get_vlc2(gb, block_tab, BLOCK_VLC_BITS, 1); if (value < 0) return AVERROR_INVALIDDATA; if (value == 0) { @@ -219,12 +219,15 @@ static int decode_intra(AVCodecContext *avctx, GetBitContext *gb, AVFrame *frame for (y = 0; y < avctx->height; y += 16) { for (x = 0; x < avctx->width; x += 16) { - unsigned flag, cbphi, cbplo; + unsigned flag, cbplo; + int cbphi; - cbplo = get_vlc2(gb, cbplo_tab.table, CBPLO_VLC_BITS, 1); + cbplo = get_vlc2(gb, cbplo_tab, CBPLO_VLC_BITS, 1); flag = get_bits1(gb); cbphi = get_cbphi(gb, 1); + if (cbphi < 0) + return cbphi; ret = decode_blocks(avctx, gb, cbplo | (cbphi << 2), 0, offset, flag); if (ret < 0) @@ -272,7 +275,8 @@ static int decode_inter(AVCodecContext *avctx, GetBitContext *gb, for (y = 0; y < avctx->height; y += 16) { for (x = 0; x < avctx->width; x += 16) { int reverse, intra_block, value; - unsigned cbphi, cbplo, flag2 = 0; + unsigned cbplo, flag2 = 0; + int cbphi; if (get_bits1(gb)) { copy_block16(frame->data[0] + y * frame->linesize[0] + x, @@ -287,7 +291,7 @@ static int decode_inter(AVCodecContext *avctx, GetBitContext *gb, continue; } - value = get_vlc2(gb, blktype_tab.table, BLKTYPE_VLC_BITS, 1); + value = get_vlc2(gb, blktype_tab, BLKTYPE_VLC_BITS, 1); if (value < 0) return AVERROR_INVALIDDATA; @@ -298,6 +302,9 @@ static int decode_inter(AVCodecContext *avctx, GetBitContext *gb, cbplo = value >> 4; cbphi = get_cbphi(gb, reverse); + if (cbphi < 0) + return cbphi; + if (intra_block) { ret = decode_blocks(avctx, gb, cbplo | (cbphi << 2), 0, offset, flag2); if (ret < 0) @@ -420,11 +427,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, switch (type) { case 0x19781977: - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; break; case 0x12250926: - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; break; default: @@ -434,7 +441,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (avctx->width != width || avctx->height != height) { - if (!frame->key_frame) { + if (!(frame->flags & AV_FRAME_FLAG_KEY)) { av_log(avctx, AV_LOG_ERROR, "Frame size change is unsupported.\n"); return AVERROR_INVALIDDATA; } @@ -445,16 +452,15 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (ret < 0) return ret; - if ((ret = ff_get_buffer(avctx, frame, frame->key_frame ? AV_GET_BUFFER_FLAG_REF : 0)) < 0) + if ((ret = ff_get_buffer(avctx, frame, (frame->flags & AV_FRAME_FLAG_KEY) ? AV_GET_BUFFER_FLAG_REF : 0)) < 0) return ret; - if (frame->key_frame) { + if (frame->flags & AV_FRAME_FLAG_KEY) { ret = decode_intra(avctx, gb, frame); if (ret < 0) return ret; - av_frame_unref(s->prev_frame); - if ((ret = av_frame_ref(s->prev_frame, frame)) < 0) + if ((ret = av_frame_replace(s->prev_frame, frame)) < 0) return ret; } else { if (!s->prev_frame->data[0]) { @@ -474,20 +480,20 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, static av_cold void imm4_init_static_data(void) { - INIT_VLC_STATIC_FROM_LENGTHS(&cbplo_tab, CBPLO_VLC_BITS, FF_ARRAY_ELEMS(cbplo), - &cbplo[0][1], 2, &cbplo[0][0], 2, 1, - 0, 0, 1 << CBPLO_VLC_BITS); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(cbplo_tab, CBPLO_VLC_BITS, FF_ARRAY_ELEMS(cbplo), + &cbplo[0][1], 2, &cbplo[0][0], 2, 1, + 0, 0); - INIT_VLC_SPARSE_STATIC(&cbphi_tab, CBPHI_VLC_BITS, FF_ARRAY_ELEMS(cbphi_bits), - cbphi_bits, 1, 1, cbphi_codes, 1, 1, NULL, 0, 0, 64); + VLC_INIT_STATIC_TABLE(cbphi_tab, CBPHI_VLC_BITS, FF_ARRAY_ELEMS(cbphi_bits), + cbphi_bits, 1, 1, cbphi_codes, 1, 1, 0); - INIT_VLC_STATIC_FROM_LENGTHS(&blktype_tab, BLKTYPE_VLC_BITS, FF_ARRAY_ELEMS(blktype), - &blktype[0][1], 2, &blktype[0][0], 2, 1, - 0, 0, 1 << BLKTYPE_VLC_BITS); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(blktype_tab, BLKTYPE_VLC_BITS, FF_ARRAY_ELEMS(blktype), + &blktype[0][1], 2, &blktype[0][0], 2, 1, + 0, 0); - INIT_VLC_STATIC_FROM_LENGTHS(&block_tab, BLOCK_VLC_BITS, FF_ARRAY_ELEMS(block_bits), - block_bits, 1, block_symbols, 2, 2, - 0, 0, 1 << BLOCK_VLC_BITS); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(block_tab, BLOCK_VLC_BITS, FF_ARRAY_ELEMS(block_bits), + block_bits, 1, block_symbols, 2, 2, + 0, 0); } static av_cold int decode_init(AVCodecContext *avctx) diff --git a/libavcodec/imx.c b/libavcodec/imx.c index 44bab23c270..0d9d9b5bb95 100644 --- a/libavcodec/imx.c +++ b/libavcodec/imx.c @@ -58,11 +58,19 @@ static int imx_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; if (ff_copy_palette(imx->pal, avpkt, avctx)) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; - frame->key_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + frame->flags |= AV_FRAME_FLAG_KEY; } else { - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } bytestream2_init(&gb, avpkt->data, avpkt->size); @@ -92,7 +100,7 @@ static int imx_decode_frame(AVCodecContext *avctx, AVFrame *rframe, break; } - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; break; case 1: if (len == 0) { @@ -114,7 +122,7 @@ static int imx_decode_frame(AVCodecContext *avctx, AVFrame *rframe, break; } - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; } else { while (len > 0) { fill = bytestream2_get_byte(&gb); @@ -150,7 +158,7 @@ static int imx_decode_frame(AVCodecContext *avctx, AVFrame *rframe, } } - frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + frame->pict_type = (frame->flags & AV_FRAME_FLAG_KEY) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if ((ret = av_frame_ref(rframe, frame)) < 0) return ret; diff --git a/libavcodec/indeo2.c b/libavcodec/indeo2.c index dd88ebf7c58..6878ab7cb66 100644 --- a/libavcodec/indeo2.c +++ b/libavcodec/indeo2.c @@ -42,12 +42,12 @@ typedef struct Ir2Context{ } Ir2Context; #define CODE_VLC_BITS 14 -static VLC ir2_vlc; +static VLCElem ir2_vlc[1 << CODE_VLC_BITS]; /* Indeo 2 codes are in range 0x01..0x7F and 0x81..0x90 */ static inline int ir2_get_code(GetBitContext *gb) { - return get_vlc2(gb, ir2_vlc.table, CODE_VLC_BITS, 1); + return get_vlc2(gb, ir2_vlc, CODE_VLC_BITS, 1); } static int ir2_decode_plane(Ir2Context *ctx, int width, int height, uint8_t *dst, @@ -226,9 +226,9 @@ static int ir2_decode_frame(AVCodecContext *avctx, AVFrame *picture, static av_cold void ir2_init_static(void) { - INIT_VLC_STATIC_FROM_LENGTHS(&ir2_vlc, CODE_VLC_BITS, IR2_CODES, - &ir2_tab[0][1], 2, &ir2_tab[0][0], 2, 1, - 0, INIT_VLC_OUTPUT_LE, 1 << CODE_VLC_BITS); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(ir2_vlc, CODE_VLC_BITS, IR2_CODES, + &ir2_tab[0][1], 2, &ir2_tab[0][0], 2, 1, + 0, VLC_INIT_OUTPUT_LE); } static av_cold int ir2_decode_init(AVCodecContext *avctx) diff --git a/libavcodec/indeo3.c b/libavcodec/indeo3.c index 5f1014f0d49..7bb0235bdb5 100644 --- a/libavcodec/indeo3.c +++ b/libavcodec/indeo3.c @@ -171,6 +171,9 @@ static av_cold int allocate_frame_buffers(Indeo3DecodeContext *ctx, int luma_size, chroma_size; ptrdiff_t luma_pitch, chroma_pitch; + luma_width = FFALIGN(luma_width , 2); + luma_height = FFALIGN(luma_height, 2); + if (luma_width < 16 || luma_width > 640 || luma_height < 16 || luma_height > 480 || luma_width & 1 || luma_height & 1) { diff --git a/libavcodec/intelh263dec.c b/libavcodec/intelh263dec.c index 2c216b00a62..f8eeb6b44ea 100644 --- a/libavcodec/intelh263dec.c +++ b/libavcodec/intelh263dec.c @@ -138,8 +138,4 @@ const FFCodec ff_h263i_decoder = { FF_CODEC_DECODE_CB(ff_h263_decode_frame), .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, - .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE - }, }; diff --git a/libavcodec/internal.h b/libavcodec/internal.h index a283c52e012..64fe0122c87 100644 --- a/libavcodec/internal.h +++ b/libavcodec/internal.h @@ -26,10 +26,7 @@ #include -#include "libavutil/buffer.h" #include "libavutil/channel_layout.h" -#include "libavutil/mathematics.h" -#include "libavutil/pixfmt.h" #include "avcodec.h" #include "config.h" @@ -56,19 +53,13 @@ typedef struct AVCodecInternal { */ int is_copy; - /** - * An audio frame with less than required samples has been submitted (and - * potentially padded with silence). Reject all subsequent frames. - */ - int last_audio_frame; - /** * Audio encoders can set this flag during init to indicate that they * want the small last frame to be padded to a multiple of pad_samples. */ int pad_samples; - AVBufferRef *pool; + struct FramePool *pool; void *thread_ctx; @@ -95,13 +86,6 @@ typedef struct AVCodecInternal { uint8_t *byte_buffer; unsigned int byte_buffer_size; - /** - * This is set to AV_PKT_FLAG_KEY for encoders that encode intra-only - * formats (i.e. whose codec descriptor has AV_CODEC_PROP_INTRA_ONLY set). - * This is used to set said flag generically for said encoders. - */ - int intra_only_flag; - void *frame_thread_encoder; /** @@ -148,21 +132,24 @@ typedef struct AVCodecInternal { AVFrame *buffer_frame; int draining_done; - int showed_multi_packet_warning; - - /* to prevent infinite loop on errors when draining */ - int nb_draining_errors; - +#if FF_API_DROPCHANGED /* used when avctx flag AV_CODEC_FLAG_DROPCHANGED is set */ int changed_frames_dropped; int initial_format; int initial_width, initial_height; int initial_sample_rate; AVChannelLayout initial_ch_layout; +#endif #if CONFIG_LCMS2 FFIccContext icc; /* used to read and write embedded ICC profiles */ #endif + + /** + * Set when the user has been warned about a failed allocation from + * a fixed frame pool. + */ + int warned_on_failed_allocation_from_fixed_pool; } AVCodecInternal; /** @@ -173,43 +160,10 @@ int ff_match_2uint16(const uint16_t (*tab)[2], int size, int a, int b); unsigned int ff_toupper4(unsigned int x); -void ff_color_frame(AVFrame *frame, const int color[4]); - -/** - * Maximum size in bytes of extradata. - * This value was chosen such that every bit of the buffer is - * addressable by a 32-bit signed integer as used by get_bits. - */ -#define FF_MAX_EXTRADATA_SIZE ((1 << 28) - AV_INPUT_BUFFER_PADDING_SIZE) - -/** - * 2^(x) for integer x - * @return correctly rounded float - */ -static av_always_inline float ff_exp2fi(int x) { - /* Normal range */ - if (-126 <= x && x <= 128) - return av_int2float((x+127) << 23); - /* Too large */ - else if (x > 128) - return INFINITY; - /* Subnormal numbers */ - else if (x > -150) - return av_int2float(1 << (x+149)); - /* Negligibly small */ - else - return 0; -} - int avpriv_h264_has_num_reorder_frames(AVCodecContext *avctx); int avpriv_codec_get_cap_skip_frame_fill_param(const AVCodec *codec); -/** - * Add a CPB properties side data to an encoding context. - */ -AVCPBProperties *ff_add_cpb_side_data(AVCodecContext *avctx); - /** * Check AVFrame for S12M timecode side data and allocate and fill TC SEI message with timecode info * @@ -232,16 +186,4 @@ int ff_alloc_timecode_sei(const AVFrame *frame, AVRational rate, size_t prefix_l */ int64_t ff_guess_coded_bitrate(AVCodecContext *avctx); -/** - * Check if a value is in the list. If not, return the default value - * - * @param ctx Context for the log msg - * @param val_name Name of the checked value, for log msg - * @param array_valid_values Array of valid int, ended with INT_MAX - * @param default_value Value return if checked value is not in the array - * @return Value or default_value. - */ -int ff_int_from_list_or_default(void *ctx, const char * val_name, int val, - const int * array_valid_values, int default_value); - #endif /* AVCODEC_INTERNAL_H */ diff --git a/libavcodec/interplayacm.c b/libavcodec/interplayacm.c index a0b9655e7a1..057ab16e85a 100644 --- a/libavcodec/interplayacm.c +++ b/libavcodec/interplayacm.c @@ -37,6 +37,7 @@ static int mul_3x5 [5 * 5 * 5]; static int mul_2x11[11 * 11]; typedef struct InterplayACMContext { + AVCodecContext *avctx; GetBitContext gb; uint8_t *bitstream; int max_framesize; @@ -77,6 +78,7 @@ static av_cold int decode_init(AVCodecContext *avctx) static AVOnce init_static_once = AV_ONCE_INIT; InterplayACMContext *s = avctx->priv_data; + s->avctx = avctx; if (avctx->extradata_size < 14) return AVERROR_INVALIDDATA; @@ -343,7 +345,7 @@ static int t15(InterplayACMContext *s, unsigned ind, unsigned col) /* b = (x1) + (x2 * 3) + (x3 * 9) */ b = get_bits(gb, 5); if (b > 26) { - av_log(NULL, AV_LOG_ERROR, "Too large b = %d > 26\n", b); + av_log(s->avctx, AV_LOG_ERROR, "Too large b = %d > 26\n", b); return AVERROR_INVALIDDATA; } @@ -372,7 +374,7 @@ static int t27(InterplayACMContext *s, unsigned ind, unsigned col) /* b = (x1) + (x2 * 5) + (x3 * 25) */ b = get_bits(gb, 7); if (b > 124) { - av_log(NULL, AV_LOG_ERROR, "Too large b = %d > 124\n", b); + av_log(s->avctx, AV_LOG_ERROR, "Too large b = %d > 124\n", b); return AVERROR_INVALIDDATA; } @@ -400,7 +402,7 @@ static int t37(InterplayACMContext *s, unsigned ind, unsigned col) /* b = (x1) + (x2 * 11) */ b = get_bits(gb, 7); if (b > 120) { - av_log(NULL, AV_LOG_ERROR, "Too large b = %d > 120\n", b); + av_log(s->avctx, AV_LOG_ERROR, "Too large b = %d > 120\n", b); return AVERROR_INVALIDDATA; } diff --git a/libavcodec/interplayvideo.c b/libavcodec/interplayvideo.c index 1a3461bf109..9c13707f876 100644 --- a/libavcodec/interplayvideo.c +++ b/libavcodec/interplayvideo.c @@ -1315,7 +1315,14 @@ static int ipvideo_decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; if (!s->is_16bpp) { - frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif } switch (frame_format) { @@ -1333,9 +1340,8 @@ static int ipvideo_decode_frame(AVCodecContext *avctx, AVFrame *frame, *got_frame = send_buffer; /* shuffle frames */ - av_frame_unref(s->second_last_frame); FFSWAP(AVFrame*, s->second_last_frame, s->last_frame); - if ((ret = av_frame_ref(s->last_frame, frame)) < 0) + if ((ret = av_frame_replace(s->last_frame, frame)) < 0) return ret; /* report that the buffer was completely consumed */ diff --git a/libavcodec/intrax8.c b/libavcodec/intrax8.c index e4c8b96c9c0..948391599f4 100644 --- a/libavcodec/intrax8.c +++ b/libavcodec/intrax8.c @@ -45,49 +45,43 @@ #define AC_VLC_MTD MAX_TABLE_DEPTH(AC_VLC_BITS, MAX_AC_VLC_BITS) #define OR_VLC_MTD MAX_TABLE_DEPTH(OR_VLC_BITS, MAX_OR_VLC_BITS) -static VLC j_ac_vlc[2][2][8]; // [quant < 13], [intra / inter], [select] -static VLC j_dc_vlc[2][8]; // [quant], [select] -static VLC j_orient_vlc[2][4]; // [quant], [select] +static const VLCElem *j_ac_vlc[2][2][8]; // [quant < 13], [intra / inter], [select] +static const VLCElem *j_dc_vlc[2][8]; // [quant], [select] +static const VLCElem *j_orient_vlc[2][4]; // [quant], [select] -static av_cold void x8_init_vlc(VLC *vlc, int nb_bits, int nb_codes, - int *offset, const uint8_t table[][2]) +static av_cold const VLCElem *x8_init_vlc(VLCInitState *state, int nb_bits, + int nb_codes, const uint8_t table[][2]) { - static VLCElem vlc_buf[VLC_BUFFER_SIZE]; - - vlc->table = &vlc_buf[*offset]; - vlc->table_allocated = VLC_BUFFER_SIZE - *offset; - ff_init_vlc_from_lengths(vlc, nb_bits, nb_codes, &table[0][1], 2, - &table[0][0], 2, 1, 0, INIT_VLC_STATIC_OVERLONG, NULL); - *offset += vlc->table_size; + return ff_vlc_init_tables_from_lengths(state, nb_bits, nb_codes, &table[0][1], 2, + &table[0][0], 2, 1, 0, 0); } static av_cold void x8_vlc_init(void) { + static VLCElem vlc_buf[VLC_BUFFER_SIZE]; + VLCInitState state = VLC_INIT_STATE(vlc_buf); int i; - int offset = 0; // set ac tables for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++) for (int k = 0; k < 8; k++) - x8_init_vlc(&j_ac_vlc[i][j][k], AC_VLC_BITS, 77, - &offset, x8_ac_quant_table[i][j][k]); + j_ac_vlc[i][j][k] = x8_init_vlc(&state, AC_VLC_BITS, 77, + x8_ac_quant_table[i][j][k]); // set dc tables for (int i = 0; i < 2; i++) for (int j = 0; j < 8; j++) - x8_init_vlc(&j_dc_vlc[i][j], DC_VLC_BITS, 34, &offset, - x8_dc_quant_table[i][j]); + j_dc_vlc[i][j] = x8_init_vlc(&state, DC_VLC_BITS, 34, + x8_dc_quant_table[i][j]); // set orient tables for (i = 0; i < 2; i++) - x8_init_vlc(&j_orient_vlc[0][i], OR_VLC_BITS, 12, - &offset, x8_orient_highquant_table[i]); + j_orient_vlc[0][i] = x8_init_vlc(&state, OR_VLC_BITS, 12, + x8_orient_highquant_table[i]); for (i = 0; i < 4; i++) - x8_init_vlc(&j_orient_vlc[1][i], OR_VLC_BITS, 12, - &offset, x8_orient_lowquant_table[i]); - - av_assert2(offset == VLC_BUFFER_SIZE); + j_orient_vlc[1][i] = x8_init_vlc(&state, OR_VLC_BITS, 12, + x8_orient_lowquant_table[i]); } static void x8_reset_vlc_tables(IntraX8Context *w) @@ -108,15 +102,15 @@ static inline void x8_select_ac_table(IntraX8Context *const w, int mode) table_index = get_bits(w->gb, 3); // 2 modes use same tables - w->j_ac_vlc_table[mode] = j_ac_vlc[w->quant < 13][mode >> 1][table_index].table; - av_assert2(w->j_ac_vlc[mode]); + w->j_ac_vlc_table[mode] = j_ac_vlc[w->quant < 13][mode >> 1][table_index]; + av_assert2(w->j_ac_vlc_table[mode]); } static inline int x8_get_orient_vlc(IntraX8Context *w) { if (!w->j_orient_vlc_table) { int table_index = get_bits(w->gb, 1 + (w->quant < 13)); - w->j_orient_vlc_table = j_orient_vlc[w->quant < 13][table_index].table; + w->j_orient_vlc_table = j_orient_vlc[w->quant < 13][table_index]; } return get_vlc2(w->gb, w->j_orient_vlc_table, OR_VLC_BITS, OR_VLC_MTD); @@ -258,7 +252,7 @@ static int x8_get_dc_rlf(IntraX8Context *const w, const int mode, if (!w->j_dc_vlc_table[mode]) { int table_index = get_bits(w->gb, 3); // 4 modes, same table - w->j_dc_vlc_table[mode] = j_dc_vlc[w->quant < 13][table_index].table; + w->j_dc_vlc_table[mode] = j_dc_vlc[w->quant < 13][table_index]; } i = get_vlc2(w->gb, w->j_dc_vlc_table[mode], DC_VLC_BITS, DC_VLC_MTD); diff --git a/libavcodec/ituh263dec.c b/libavcodec/ituh263dec.c index d4a07071ec1..aeeda1cc42b 100644 --- a/libavcodec/ituh263dec.c +++ b/libavcodec/ituh263dec.c @@ -99,38 +99,38 @@ void ff_h263_show_pict_info(MpegEncContext *s){ /***********************************************/ /* decoding */ -VLC ff_h263_intra_MCBPC_vlc; -VLC ff_h263_inter_MCBPC_vlc; -VLC ff_h263_cbpy_vlc; -VLC ff_h263_mv_vlc; -static VLC h263_mbtype_b_vlc; -static VLC cbpc_b_vlc; +VLCElem ff_h263_intra_MCBPC_vlc[72]; +VLCElem ff_h263_inter_MCBPC_vlc[198]; +VLCElem ff_h263_cbpy_vlc[64]; +VLCElem ff_h263_mv_vlc[538]; +static VLCElem h263_mbtype_b_vlc[80]; +static VLCElem cbpc_b_vlc[8]; /* init vlcs */ static av_cold void h263_decode_init_vlc(void) { - INIT_VLC_STATIC(&ff_h263_intra_MCBPC_vlc, INTRA_MCBPC_VLC_BITS, 9, - ff_h263_intra_MCBPC_bits, 1, 1, - ff_h263_intra_MCBPC_code, 1, 1, 72); - INIT_VLC_STATIC(&ff_h263_inter_MCBPC_vlc, INTER_MCBPC_VLC_BITS, 28, - ff_h263_inter_MCBPC_bits, 1, 1, - ff_h263_inter_MCBPC_code, 1, 1, 198); - INIT_VLC_STATIC(&ff_h263_cbpy_vlc, CBPY_VLC_BITS, 16, - &ff_h263_cbpy_tab[0][1], 2, 1, - &ff_h263_cbpy_tab[0][0], 2, 1, 64); - INIT_VLC_STATIC(&ff_h263_mv_vlc, H263_MV_VLC_BITS, 33, - &ff_mvtab[0][1], 2, 1, - &ff_mvtab[0][0], 2, 1, 538); + VLC_INIT_STATIC_TABLE(ff_h263_intra_MCBPC_vlc, INTRA_MCBPC_VLC_BITS, 9, + ff_h263_intra_MCBPC_bits, 1, 1, + ff_h263_intra_MCBPC_code, 1, 1, 0); + VLC_INIT_STATIC_TABLE(ff_h263_inter_MCBPC_vlc, INTER_MCBPC_VLC_BITS, 28, + ff_h263_inter_MCBPC_bits, 1, 1, + ff_h263_inter_MCBPC_code, 1, 1, 0); + VLC_INIT_STATIC_TABLE(ff_h263_cbpy_vlc, CBPY_VLC_BITS, 16, + &ff_h263_cbpy_tab[0][1], 2, 1, + &ff_h263_cbpy_tab[0][0], 2, 1, 0); + VLC_INIT_STATIC_TABLE(ff_h263_mv_vlc, H263_MV_VLC_BITS, 33, + &ff_mvtab[0][1], 2, 1, + &ff_mvtab[0][0], 2, 1, 0); ff_h263_init_rl_inter(); - INIT_VLC_RL(ff_h263_rl_inter, 554); + VLC_INIT_RL(ff_h263_rl_inter, 554); INIT_FIRST_VLC_RL(ff_rl_intra_aic, 554); - INIT_VLC_STATIC(&h263_mbtype_b_vlc, H263_MBTYPE_B_VLC_BITS, 15, - &ff_h263_mbtype_b_tab[0][1], 2, 1, - &ff_h263_mbtype_b_tab[0][0], 2, 1, 80); - INIT_VLC_STATIC(&cbpc_b_vlc, CBPC_B_VLC_BITS, 4, - &ff_cbpc_b_tab[0][1], 2, 1, - &ff_cbpc_b_tab[0][0], 2, 1, 8); + VLC_INIT_STATIC_TABLE(h263_mbtype_b_vlc, H263_MBTYPE_B_VLC_BITS, 15, + &ff_h263_mbtype_b_tab[0][1], 2, 1, + &ff_h263_mbtype_b_tab[0][0], 2, 1, 0); + VLC_INIT_STATIC_TABLE(cbpc_b_vlc, CBPC_B_VLC_BITS, 4, + &ff_cbpc_b_tab[0][1], 2, 1, + &ff_cbpc_b_tab[0][0], 2, 1, 0); } av_cold void ff_h263_decode_init_vlc(void) @@ -273,7 +273,7 @@ int ff_h263_resync(MpegEncContext *s){ int ff_h263_decode_motion(MpegEncContext * s, int pred, int f_code) { int code, val, sign, shift; - code = get_vlc2(&s->gb, ff_h263_mv_vlc.table, H263_MV_VLC_BITS, 2); + code = get_vlc2(&s->gb, ff_h263_mv_vlc, H263_MV_VLC_BITS, 2); if (code == 0) return pred; @@ -366,13 +366,13 @@ static void preview_obmc(MpegEncContext *s){ s->current_picture.mb_type[xy] = MB_TYPE_SKIP | MB_TYPE_16x16 | MB_TYPE_L0; goto end; } - cbpc = get_vlc2(&s->gb, ff_h263_inter_MCBPC_vlc.table, INTER_MCBPC_VLC_BITS, 2); + cbpc = get_vlc2(&s->gb, ff_h263_inter_MCBPC_vlc, INTER_MCBPC_VLC_BITS, 2); }while(cbpc == 20); if(cbpc & 4){ s->current_picture.mb_type[xy] = MB_TYPE_INTRA; }else{ - get_vlc2(&s->gb, ff_h263_cbpy_vlc.table, CBPY_VLC_BITS, 1); + get_vlc2(&s->gb, ff_h263_cbpy_vlc, CBPY_VLC_BITS, 1); if (cbpc & 8) { if(s->modified_quant){ if(get_bits1(&s->gb)) skip_bits(&s->gb, 1); @@ -809,7 +809,7 @@ int ff_h263_decode_mb(MpegEncContext *s, s->mb_skipped = !(s->obmc | s->loop_filter); goto end; } - cbpc = get_vlc2(&s->gb, ff_h263_inter_MCBPC_vlc.table, INTER_MCBPC_VLC_BITS, 2); + cbpc = get_vlc2(&s->gb, ff_h263_inter_MCBPC_vlc, INTER_MCBPC_VLC_BITS, 2); if (cbpc < 0){ av_log(s->avctx, AV_LOG_ERROR, "cbpc damaged at %d %d\n", s->mb_x, s->mb_y); return SLICE_ERROR; @@ -824,7 +824,7 @@ int ff_h263_decode_mb(MpegEncContext *s, if(s->pb_frame && get_bits1(&s->gb)) pb_mv_count = h263_get_modb(&s->gb, s->pb_frame, &cbpb); - cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc.table, CBPY_VLC_BITS, 1); + cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc, CBPY_VLC_BITS, 1); if (cbpy < 0) { av_log(s->avctx, AV_LOG_ERROR, "cbpy damaged at %d %d\n", s->mb_x, s->mb_y); @@ -905,7 +905,8 @@ int ff_h263_decode_mb(MpegEncContext *s, mot_val1[1 ]= mot_val1[3 ]= mot_val1[1+2*stride]= mot_val1[3+2*stride]= 0; do{ - mb_type= get_vlc2(&s->gb, h263_mbtype_b_vlc.table, H263_MBTYPE_B_VLC_BITS, 2); + mb_type = get_vlc2(&s->gb, h263_mbtype_b_vlc, + H263_MBTYPE_B_VLC_BITS, 2); if (mb_type < 0){ av_log(s->avctx, AV_LOG_ERROR, "b mb_type damaged at %d %d\n", s->mb_x, s->mb_y); return SLICE_ERROR; @@ -917,13 +918,13 @@ int ff_h263_decode_mb(MpegEncContext *s, s->mb_intra = IS_INTRA(mb_type); if(HAS_CBP(mb_type)){ s->bdsp.clear_blocks(s->block[0]); - cbpc = get_vlc2(&s->gb, cbpc_b_vlc.table, CBPC_B_VLC_BITS, 1); + cbpc = get_vlc2(&s->gb, cbpc_b_vlc, CBPC_B_VLC_BITS, 1); if(s->mb_intra){ dquant = IS_QUANT(mb_type); goto intra; } - cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc.table, CBPY_VLC_BITS, 1); + cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc, CBPY_VLC_BITS, 1); if (cbpy < 0){ av_log(s->avctx, AV_LOG_ERROR, "b cbpy damaged at %d %d\n", s->mb_x, s->mb_y); @@ -1009,7 +1010,7 @@ int ff_h263_decode_mb(MpegEncContext *s, s->current_picture.mb_type[xy] = mb_type; } else { /* I-Frame */ do{ - cbpc = get_vlc2(&s->gb, ff_h263_intra_MCBPC_vlc.table, INTRA_MCBPC_VLC_BITS, 2); + cbpc = get_vlc2(&s->gb, ff_h263_intra_MCBPC_vlc, INTRA_MCBPC_VLC_BITS, 2); if (cbpc < 0){ av_log(s->avctx, AV_LOG_ERROR, "I cbpc damaged at %d %d\n", s->mb_x, s->mb_y); return SLICE_ERROR; @@ -1034,7 +1035,7 @@ int ff_h263_decode_mb(MpegEncContext *s, if(s->pb_frame && get_bits1(&s->gb)) pb_mv_count = h263_get_modb(&s->gb, s->pb_frame, &cbpb); - cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc.table, CBPY_VLC_BITS, 1); + cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc, CBPY_VLC_BITS, 1); if(cbpy<0){ av_log(s->avctx, AV_LOG_ERROR, "I cbpy damaged at %d %d\n", s->mb_x, s->mb_y); return SLICE_ERROR; diff --git a/libavcodec/ituh263enc.c b/libavcodec/ituh263enc.c index c30ecad4382..97abfb3f456 100644 --- a/libavcodec/ituh263enc.c +++ b/libavcodec/ituh263enc.c @@ -865,6 +865,11 @@ av_cold void ff_h263_encode_init(MpegEncContext *s) s->c_dc_scale_table= ff_mpeg1_dc_scale_table; } + if (s->lmin > s->lmax) { + av_log(s->avctx, AV_LOG_WARNING, "Clipping lmin value to %d\n", s->lmax); + s->lmin = s->lmax; + } + ff_thread_once(&init_static_once, h263_encode_init_static); } diff --git a/libavcodec/aarch64/asm-offsets.h b/libavcodec/itut35.h similarity index 69% rename from libavcodec/aarch64/asm-offsets.h rename to libavcodec/itut35.h index fc38eed2985..ffa70249812 100644 --- a/libavcodec/aarch64/asm-offsets.h +++ b/libavcodec/itut35.h @@ -16,10 +16,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVCODEC_AARCH64_ASM_OFFSETS_H -#define AVCODEC_AARCH64_ASM_OFFSETS_H +#ifndef AVCODEC_ITUT35_H +#define AVCODEC_ITUT35_H -/* FFTContext */ -#define IMDCT_HALF 0x48 +#define ITU_T_T35_COUNTRY_CODE_CN 0x26 +#define ITU_T_T35_COUNTRY_CODE_US 0xB5 -#endif /* AVCODEC_AARCH64_ASM_OFFSETS_H */ +#define ITU_T_T35_PROVIDER_CODE_ATSC 0x31 +#define ITU_T_T35_PROVIDER_CODE_CUVA 0x04 +#define ITU_T_T35_PROVIDER_CODE_DOLBY 0x3B +#define ITU_T_T35_PROVIDER_CODE_SMTPE 0x3C + +#endif /* AVCODEC_ITUT35_H */ diff --git a/libavcodec/ivi.c b/libavcodec/ivi.c index 43f3cb1da36..60769664a12 100644 --- a/libavcodec/ivi.c +++ b/libavcodec/ivi.c @@ -154,8 +154,8 @@ static int ivi_create_huff_from_desc(const IVIHuffDesc *cb, VLC *vlc, int flag) }//for i /* number of codewords = pos */ - return init_vlc(vlc, IVI_VLC_BITS, pos, bits, 1, 1, codewords, 2, 2, - (flag ? INIT_VLC_USE_NEW_STATIC : 0) | INIT_VLC_OUTPUT_LE); + return vlc_init(vlc, IVI_VLC_BITS, pos, bits, 1, 1, codewords, 2, 2, + (flag ? VLC_INIT_USE_STATIC : 0) | VLC_INIT_OUTPUT_LE); } static av_cold void ivi_init_static_vlc(void) @@ -237,7 +237,7 @@ int ff_ivi_dec_huff_desc(GetBitContext *gb, int desc_coded, int which_tab, ivi_huff_desc_copy(&huff_tab->cust_desc, &new_huff); if (huff_tab->cust_tab.table) - ff_free_vlc(&huff_tab->cust_tab); + ff_vlc_free(&huff_tab->cust_tab); result = ivi_create_huff_from_desc(&huff_tab->cust_desc, &huff_tab->cust_tab, 0); if (result) { @@ -277,7 +277,7 @@ static av_cold void ivi_free_buffers(IVIPlaneDesc *planes) av_freep(&band->bufs[3]); if (band->blk_vlc.cust_tab.table) - ff_free_vlc(&band->blk_vlc.cust_tab); + ff_vlc_free(&band->blk_vlc.cust_tab); for (t = 0; t < band->num_tiles; t++) av_freep(&band->tiles[t].mbs); av_freep(&band->tiles); @@ -1215,10 +1215,10 @@ av_cold int ff_ivi_decode_close(AVCodecContext *avctx) ivi_free_buffers(&ctx->planes[0]); if (ctx->mb_vlc.cust_tab.table) - ff_free_vlc(&ctx->mb_vlc.cust_tab); + ff_vlc_free(&ctx->mb_vlc.cust_tab); if (ctx->blk_vlc.cust_tab.table) - ff_free_vlc(&ctx->blk_vlc.cust_tab); + ff_vlc_free(&ctx->blk_vlc.cust_tab); av_frame_free(&ctx->p_frame); diff --git a/libavcodec/ivi.h b/libavcodec/ivi.h index 06cd4d95ff2..afc3ec2a75b 100644 --- a/libavcodec/ivi.h +++ b/libavcodec/ivi.h @@ -61,18 +61,17 @@ typedef struct IVIHuffDesc { * macroblock/block huffman table descriptor */ typedef struct IVIHuffTab { - int32_t tab_sel; /// index of one of the predefined tables - /// or "7" for custom one - VLC *tab; /// pointer to the table associated with tab_sel + int32_t tab_sel; ///< index of one of the predefined tables, or "7" for custom one + VLC *tab; ///< pointer to the table associated with tab_sel - /// the following are used only when tab_sel == 7 - IVIHuffDesc cust_desc; /// custom Huffman codebook descriptor - VLC cust_tab; /// vlc table for custom codebook + // the following are used only when tab_sel == 7 + IVIHuffDesc cust_desc; ///< custom Huffman codebook descriptor + VLC cust_tab; ///< vlc table for custom codebook } IVIHuffTab; enum { - IVI_MB_HUFF = 0, /// Huffman table is used for coding macroblocks - IVI_BLK_HUFF = 1 /// Huffman table is used for coding blocks + IVI_MB_HUFF = 0, ///< Huffman table is used for coding macroblocks + IVI_BLK_HUFF = 1 ///< Huffman table is used for coding blocks }; diff --git a/libavcodec/j2kenc.c b/libavcodec/j2kenc.c index e4cc34956cd..6f827be05b2 100644 --- a/libavcodec/j2kenc.c +++ b/libavcodec/j2kenc.c @@ -115,6 +115,7 @@ typedef struct { int width, height; ///< image width and height uint8_t cbps[4]; ///< bits per sample in particular components + uint8_t comp_remap[4]; int chroma_shift[2]; uint8_t planar; int ncomponents; @@ -126,7 +127,7 @@ typedef struct { uint8_t *buf_end; int bit_index; - int64_t lambda; + uint64_t lambda; Jpeg2000CodingStyle codsty; Jpeg2000QuantStyle qntsty; @@ -319,8 +320,8 @@ static int put_siz(Jpeg2000EncoderContext *s) for (i = 0; i < s->ncomponents; i++){ // Ssiz_i XRsiz_i, YRsiz_i bytestream_put_byte(&s->buf, s->cbps[i] - 1); - bytestream_put_byte(&s->buf, i?1<chroma_shift[0]:1); - bytestream_put_byte(&s->buf, i?1<chroma_shift[1]:1); + bytestream_put_byte(&s->buf, (i+1&2)?1<chroma_shift[0]:1); + bytestream_put_byte(&s->buf, (i+1&2)?1<chroma_shift[1]:1); } return 0; } @@ -431,7 +432,7 @@ static void compute_rates(Jpeg2000EncoderContext* s) for (compno = 0; compno < s->ncomponents; compno++) { int tilew = tile->comp[compno].coord[0][1] - tile->comp[compno].coord[0][0]; int tileh = tile->comp[compno].coord[1][1] - tile->comp[compno].coord[1][0]; - int scale = (compno?1 << s->chroma_shift[0]:1) * (compno?1 << s->chroma_shift[1]:1); + int scale = ((compno+1&2)?1 << s->chroma_shift[0]:1) * ((compno+1&2)?1 << s->chroma_shift[1]:1); for (layno = 0; layno < s->nlayers; layno++) { if (s->layer_rates[layno] > 0) { tile->layer_rates[layno] += (double)(tilew * tileh) * s->ncomponents * s->cbps[compno] / @@ -483,7 +484,7 @@ static int init_tiles(Jpeg2000EncoderContext *s) comp->coord[0][1] = comp->coord_o[0][1] = FFMIN((tilex+1)*s->tile_width, s->width); comp->coord[1][0] = comp->coord_o[1][0] = tiley * s->tile_height; comp->coord[1][1] = comp->coord_o[1][1] = FFMIN((tiley+1)*s->tile_height, s->height); - if (compno > 0) + if (compno + 1 & 2) for (i = 0; i < 2; i++) for (j = 0; j < 2; j++) comp->coord[i][j] = comp->coord_o[i][j] = ff_jpeg2000_ceildivpow2(comp->coord[i][j], s->chroma_shift[i]); @@ -492,8 +493,8 @@ static int init_tiles(Jpeg2000EncoderContext *s) codsty, qntsty, s->cbps[compno], - compno?1<chroma_shift[0]:1, - compno?1<chroma_shift[1]:1, + (compno+1&2)?1<chroma_shift[0]:1, + (compno+1&2)?1<chroma_shift[1]:1, s->avctx )) < 0) return ret; @@ -512,17 +513,18 @@ static int init_tiles(Jpeg2000EncoderContext *s) Jpeg2000Tile *tile = s->tile + tileno; \ if (s->planar){ \ for (compno = 0; compno < s->ncomponents; compno++){ \ + int icompno = s->comp_remap[compno]; \ Jpeg2000Component *comp = tile->comp + compno; \ int *dst = comp->i_data; \ int cbps = s->cbps[compno]; \ - line = (const PIXEL*)s->picture->data[compno] \ - + comp->coord[1][0] * (s->picture->linesize[compno] / sizeof(PIXEL)) \ + line = (const PIXEL*)s->picture->data[icompno] \ + + comp->coord[1][0] * (s->picture->linesize[icompno] / sizeof(PIXEL)) \ + comp->coord[0][0]; \ for (y = comp->coord[1][0]; y < comp->coord[1][1]; y++){ \ const PIXEL *ptr = line; \ for (x = comp->coord[0][0]; x < comp->coord[0][1]; x++) \ *dst++ = *ptr++ - (1 << (cbps - 1)); \ - line += s->picture->linesize[compno] / sizeof(PIXEL); \ + line += s->picture->linesize[icompno] / sizeof(PIXEL); \ } \ } \ } else{ \ @@ -779,7 +781,7 @@ static void putnumpasses(Jpeg2000EncoderContext *s, int n) static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, int layno, - int precno, uint8_t *expn, int numgbits, int packetno, + int precno, const uint8_t *expn, int numgbits, int packetno, int nlayers) { int bandno, empty = 1; @@ -1007,7 +1009,7 @@ static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til Jpeg2000Component *comp = tile->comp + compno; uint8_t reducedresno = codsty->nreslevels - 1 -reslevelno; // ==> N_L - r Jpeg2000ResLevel *reslevel = comp->reslevel + reslevelno; - int log_subsampling[2] = { compno?s->chroma_shift[0]:0, compno?s->chroma_shift[1]:0}; + int log_subsampling[2] = { (compno+1&2)?s->chroma_shift[0]:0, (compno+1&2)?s->chroma_shift[1]:0}; unsigned prcx, prcy; int trx0, try0; @@ -1068,7 +1070,7 @@ static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til for (x = tile_coord[0][0]; x < tile_coord[0][1]; x = (x/step_x + 1)*step_x) { for (compno = 0; compno < s->ncomponents; compno++) { Jpeg2000Component *comp = tile->comp + compno; - int log_subsampling[2] = { compno?s->chroma_shift[0]:0, compno?s->chroma_shift[1]:0}; + int log_subsampling[2] = { (compno+1&2)?s->chroma_shift[0]:0, (compno+1&2)?s->chroma_shift[1]:0}; for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++) { unsigned prcx, prcy; @@ -1114,7 +1116,7 @@ static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til case JPEG2000_PGOD_CPRL: for (compno = 0; compno < s->ncomponents; compno++) { Jpeg2000Component *comp = tile->comp + compno; - int log_subsampling[2] = { compno?s->chroma_shift[0]:0, compno?s->chroma_shift[1]:0}; + int log_subsampling[2] = { (compno+1&2)?s->chroma_shift[0]:0, (compno+1&2)?s->chroma_shift[1]:0}; step_x = 32; step_y = 32; @@ -1346,7 +1348,7 @@ static void makelayers(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile) } } -static int getcut(Jpeg2000Cblk *cblk, int64_t lambda, int dwt_norm) +static int getcut(Jpeg2000Cblk *cblk, uint64_t lambda) { int passno, res = 0; for (passno = 0; passno < cblk->npasses; passno++){ @@ -1358,7 +1360,7 @@ static int getcut(Jpeg2000Cblk *cblk, int64_t lambda, int dwt_norm) dd = cblk->passes[passno].disto - (res ? cblk->passes[res-1].disto : 0); - if (((dd * dwt_norm) >> WMSEDEC_SHIFT) * dwt_norm >= dr * lambda) + if (dd >= dr * lambda) res = passno+1; } return res; @@ -1381,11 +1383,12 @@ static void truncpasses(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile) Jpeg2000Band *band = reslevel->band + bandno; Jpeg2000Prec *prec = band->prec + precno; + int64_t dwt_norm = dwt_norms[codsty->transform == FF_DWT53][bandpos][lev] * (int64_t)band->i_stepsize >> 15; + int64_t lambda_prime = av_rescale(s->lambda, 1 << WMSEDEC_SHIFT, dwt_norm * dwt_norm); for (cblkno = 0; cblkno < prec->nb_codeblocks_height * prec->nb_codeblocks_width; cblkno++){ Jpeg2000Cblk *cblk = prec->cblk + cblkno; - cblk->ninclpasses = getcut(cblk, s->lambda, - (int64_t)dwt_norms[codsty->transform == FF_DWT53][bandpos][lev] * (int64_t)band->i_stepsize >> 15); + cblk->ninclpasses = getcut(cblk, lambda_prime); cblk->layers[0].data_start = cblk->data; cblk->layers[0].cum_passes = cblk->ninclpasses; cblk->layers[0].npasses = cblk->ninclpasses; @@ -1532,7 +1535,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, uint8_t *chunkstart, *jp2cstart, *jp2hstart; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt); - if ((ret = ff_alloc_packet(avctx, pkt, avctx->width*avctx->height*9 + AV_INPUT_BUFFER_MIN_SIZE)) < 0) + if ((ret = ff_alloc_packet(avctx, pkt, avctx->width*avctx->height*9 + FF_INPUT_BUFFER_MIN_SIZE)) < 0) return ret; // init: @@ -1761,8 +1764,9 @@ static av_cold int j2kenc_init(AVCodecContext *avctx) s->height = avctx->height; s->ncomponents = desc->nb_components; - for (i = 0; i < 3; i++) { + for (i = 0; i < 4; i++) { s->cbps[i] = desc->comp[i].depth; + s->comp_remap[i] = i; //default } if ((desc->flags & AV_PIX_FMT_FLAG_PLANAR) && s->ncomponents > 1) { @@ -1771,6 +1775,11 @@ static av_cold int j2kenc_init(AVCodecContext *avctx) s->chroma_shift, s->chroma_shift + 1); if (ret) return ret; + if (desc->flags & AV_PIX_FMT_FLAG_RGB) { + s->comp_remap[0] = 2; + s->comp_remap[1] = 0; + s->comp_remap[2] = 1; + } } ff_thread_once(&init_static_once, init_luts); @@ -1797,22 +1806,22 @@ static int j2kenc_destroy(AVCodecContext *avctx) #define OFFSET(x) offsetof(Jpeg2000EncoderContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "format", "Codec Format", OFFSET(format), AV_OPT_TYPE_INT, { .i64 = CODEC_JP2 }, CODEC_J2K, CODEC_JP2, VE, "format" }, - { "j2k", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = CODEC_J2K }, 0, 0, VE, "format" }, - { "jp2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = CODEC_JP2 }, 0, 0, VE, "format" }, + { "format", "Codec Format", OFFSET(format), AV_OPT_TYPE_INT, { .i64 = CODEC_JP2 }, CODEC_J2K, CODEC_JP2, VE, .unit = "format" }, + { "j2k", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = CODEC_J2K }, 0, 0, VE, .unit = "format" }, + { "jp2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = CODEC_JP2 }, 0, 0, VE, .unit = "format" }, { "tile_width", "Tile Width", OFFSET(tile_width), AV_OPT_TYPE_INT, { .i64 = 256 }, 1, 1<<30, VE, }, { "tile_height", "Tile Height", OFFSET(tile_height), AV_OPT_TYPE_INT, { .i64 = 256 }, 1, 1<<30, VE, }, - { "pred", "DWT Type", OFFSET(pred), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE, "pred" }, - { "dwt97int", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "pred" }, - { "dwt53", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "pred" }, + { "pred", "DWT Type", OFFSET(pred), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE, .unit = "pred" }, + { "dwt97int", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "dwt53", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "pred" }, { "sop", "SOP marker", OFFSET(sop), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE, }, { "eph", "EPH marker", OFFSET(eph), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE, }, - { "prog", "Progression Order", OFFSET(prog), AV_OPT_TYPE_INT, { .i64 = 0 }, JPEG2000_PGOD_LRCP, JPEG2000_PGOD_CPRL, VE, "prog" }, - { "lrcp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = JPEG2000_PGOD_LRCP }, 0, 0, VE, "prog" }, - { "rlcp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = JPEG2000_PGOD_RLCP }, 0, 0, VE, "prog" }, - { "rpcl", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = JPEG2000_PGOD_RPCL }, 0, 0, VE, "prog" }, - { "pcrl", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = JPEG2000_PGOD_PCRL }, 0, 0, VE, "prog" }, - { "cprl", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = JPEG2000_PGOD_CPRL }, 0, 0, VE, "prog" }, + { "prog", "Progression Order", OFFSET(prog), AV_OPT_TYPE_INT, { .i64 = 0 }, JPEG2000_PGOD_LRCP, JPEG2000_PGOD_CPRL, VE, .unit = "prog" }, + { "lrcp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = JPEG2000_PGOD_LRCP }, 0, 0, VE, .unit = "prog" }, + { "rlcp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = JPEG2000_PGOD_RLCP }, 0, 0, VE, .unit = "prog" }, + { "rpcl", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = JPEG2000_PGOD_RPCL }, 0, 0, VE, .unit = "prog" }, + { "pcrl", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = JPEG2000_PGOD_PCRL }, 0, 0, VE, .unit = "prog" }, + { "cprl", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = JPEG2000_PGOD_CPRL }, 0, 0, VE, .unit = "prog" }, { "layer_rates", "Layer Rates", OFFSET(lr_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE }, { NULL } }; @@ -1829,17 +1838,30 @@ const FFCodec ff_jpeg2000_encoder = { CODEC_LONG_NAME("JPEG 2000"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_JPEG2000, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE | + AV_CODEC_CAP_FRAME_THREADS, .priv_data_size = sizeof(Jpeg2000EncoderContext), .init = j2kenc_init, FF_CODEC_ENCODE_CB(encode_frame), .close = j2kenc_destroy, .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_RGB24, AV_PIX_FMT_YUV444P, AV_PIX_FMT_GRAY8, - AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, - AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, + AV_PIX_FMT_RGB24, AV_PIX_FMT_RGB48, + AV_PIX_FMT_GBR24P,AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16, + AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16, + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV420P16, + AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV422P16, + AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P9, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV444P16, + AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV440P10, AV_PIX_FMT_YUV440P12, + AV_PIX_FMT_YUV411P, + AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YA8, AV_PIX_FMT_YA16, + AV_PIX_FMT_RGBA, AV_PIX_FMT_RGBA64, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, + AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA420P16, + AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA422P16, + AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA444P16, + AV_PIX_FMT_PAL8, - AV_PIX_FMT_RGB48, AV_PIX_FMT_GRAY16, AV_PIX_FMT_NONE }, .p.priv_class = &j2k_class, diff --git a/libavcodec/jfdctfst.c b/libavcodec/jfdctfst.c index 805e05808c9..946b12f379e 100644 --- a/libavcodec/jfdctfst.c +++ b/libavcodec/jfdctfst.c @@ -68,7 +68,7 @@ #include #include "libavutil/attributes.h" -#include "dct.h" +#include "fdctdsp.h" #define DCTSIZE 8 #define GLOBAL(x) x diff --git a/libavcodec/jfdctint_template.c b/libavcodec/jfdctint_template.c index 67fb77b5e12..aa2680132ee 100644 --- a/libavcodec/jfdctint_template.c +++ b/libavcodec/jfdctint_template.c @@ -60,7 +60,7 @@ */ #include "libavutil/common.h" -#include "dct.h" +#include "fdctdsp.h" #include "bit_depth_template.c" @@ -69,7 +69,7 @@ #define GLOBAL(x) x #define RIGHT_SHIFT(x, n) ((x) >> (n)) #define MULTIPLY16C16(var,const) ((var)*(const)) -#define DESCALE(x,n) RIGHT_SHIFT((x) + (1 << ((n) - 1)), n) +#define DESCALE(x,n) RIGHT_SHIFT((int)(x) + (1 << ((n) - 1)), n) /* @@ -175,7 +175,7 @@ #if BITS_IN_JSAMPLE == 8 && CONST_BITS<=13 && PASS1_BITS<=2 #define MULTIPLY(var,const) MULTIPLY16C16(var,const) #else -#define MULTIPLY(var,const) ((var) * (const)) +#define MULTIPLY(var,const) (int)((var) * (unsigned)(const)) #endif @@ -261,7 +261,7 @@ FUNC(ff_jpeg_fdct_islow)(int16_t *data) { int tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; int tmp10, tmp11, tmp12, tmp13; - int z1, z2, z3, z4, z5; + unsigned z1, z2, z3, z4, z5; int16_t *dataptr; int ctr; diff --git a/libavcodec/jni.c b/libavcodec/jni.c index 85dcf2abafd..1193c608c3f 100644 --- a/libavcodec/jni.c +++ b/libavcodec/jni.c @@ -34,8 +34,9 @@ #include "libavutil/log.h" #include "ffjni.h" -void *java_vm; -pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; +static void *java_vm; +static void *android_app_ctx; +static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; int av_jni_set_java_vm(void *vm, void *log_ctx) { @@ -77,3 +78,45 @@ void *av_jni_get_java_vm(void *log_ctx) } #endif + +#if defined(__ANDROID__) + +int av_jni_set_android_app_ctx(void *app_ctx, void *log_ctx) +{ +#if CONFIG_JNI + JNIEnv *env = ff_jni_get_env(log_ctx); + if (!env) + return AVERROR(EINVAL); + + jobjectRefType type = (*env)->GetObjectRefType(env, app_ctx); + if (type != JNIGlobalRefType) { + av_log(log_ctx, AV_LOG_ERROR, "Application context must be passed as a global reference"); + return AVERROR(EINVAL); + } + + pthread_mutex_lock(&lock); + android_app_ctx = app_ctx; + pthread_mutex_unlock(&lock); + + return 0; +#else + return AVERROR(ENOSYS); +#endif +} + +void *av_jni_get_android_app_ctx(void) +{ +#if CONFIG_JNI + void *ctx; + + pthread_mutex_lock(&lock); + ctx = android_app_ctx; + pthread_mutex_unlock(&lock); + + return ctx; +#else + return NULL; +#endif +} + +#endif diff --git a/libavcodec/jni.h b/libavcodec/jni.h index dd99e926113..955cd28096b 100644 --- a/libavcodec/jni.h +++ b/libavcodec/jni.h @@ -43,4 +43,25 @@ int av_jni_set_java_vm(void *vm, void *log_ctx); */ void *av_jni_get_java_vm(void *log_ctx); +/* + * Set the Android application context which will be used to retrieve the Android + * content resolver to handle content uris. + * + * This function is only available on Android. + * + * @param app_ctx global JNI reference to the Android application context + * @return 0 on success, < 0 otherwise + */ +int av_jni_set_android_app_ctx(void *app_ctx, void *log_ctx); + +/* + * Get the Android application context that has been set with + * av_jni_set_android_app_ctx. + * + * This function is only available on Android. + * + * @return a pointer the the Android application context + */ +void *av_jni_get_android_app_ctx(void); + #endif /* AVCODEC_JNI_H */ diff --git a/libavcodec/jpeg2000.c b/libavcodec/jpeg2000.c index 0aa984bc53f..d6ffb023191 100644 --- a/libavcodec/jpeg2000.c +++ b/libavcodec/jpeg2000.c @@ -32,7 +32,6 @@ #include "libavutil/mem.h" #include "libavutil/thread.h" #include "avcodec.h" -#include "internal.h" #include "jpeg2000.h" #define SHL(a, n) ((n) >= 0 ? (a) << (n) : (a) >> -(n)) @@ -201,6 +200,17 @@ void ff_jpeg2000_set_significance(Jpeg2000T1Context *t1, int x, int y, // static const uint8_t lut_gain[2][4] = { { 0, 0, 0, 0 }, { 0, 1, 1, 2 } }; (unused) +/** + * 2^(x) for integer x in the range -126..128. + * @return correctly rounded float + */ +static av_always_inline float exp2fi(int x) +{ + av_assert2(-126 <= x && x <= 128); + /* Normal range */ + return av_int2float((x+127) << 23); +} + static void init_band_stepsize(AVCodecContext *avctx, Jpeg2000Band *band, Jpeg2000CodingStyle *codsty, @@ -230,7 +240,7 @@ static void init_band_stepsize(AVCodecContext *avctx, * R_b = R_I + log2 (gain_b ) * see ISO/IEC 15444-1:2002 E.1.1 eqn. E-3 and E-4 */ gain = cbps; - band->f_stepsize = ff_exp2fi(gain - qntsty->expn[gbandno]); + band->f_stepsize = exp2fi(gain - qntsty->expn[gbandno]); band->f_stepsize *= qntsty->mant[gbandno] / 2048.0 + 1.0; break; default: @@ -391,7 +401,7 @@ static int init_band(AVCodecContext *avctx, Jpeg2000CodingStyle *codsty, Jpeg2000QuantStyle *qntsty, int bandno, int gbandno, int reslevelno, - int cbps, int dx, int dy) + const int cbps, int dx, int dy) { Jpeg2000Band *band = reslevel->band + bandno; uint8_t log2_band_prec_width, log2_band_prec_height; @@ -466,7 +476,7 @@ static int init_band(AVCodecContext *avctx, int ff_jpeg2000_init_component(Jpeg2000Component *comp, Jpeg2000CodingStyle *codsty, Jpeg2000QuantStyle *qntsty, - int cbps, int dx, int dy, + const int cbps, int dx, int dy, AVCodecContext *avctx) { int reslevelno, bandno, gbandno = 0, ret, i, j; diff --git a/libavcodec/jpeg2000.h b/libavcodec/jpeg2000.h index e5ecb4cbf91..d004c08f104 100644 --- a/libavcodec/jpeg2000.h +++ b/libavcodec/jpeg2000.h @@ -111,7 +111,7 @@ enum Jpeg2000Quantsty { // quantization style #define JPEG2000_CSTY_SOP 0x02 // SOP marker present #define JPEG2000_CSTY_EPH 0x04 // EPH marker present #define JPEG2000_CTSY_HTJ2K_F 0x40 // Only HT code-blocks (Rec. ITU-T T.814 | ISO/IEC 15444-15) are present -#define JPEG2000_CTSY_HTJ2K_M 0xC0 // HT code blocks (Rec. ITU-T T.814 | ISO/IEC 15444-15) can be present +#define JPEG2000_CTSY_HTJ2K_M 0xC0 // HT code-blocks (Rec. ITU-T T.814 | ISO/IEC 15444-15) can be present // Progression orders #define JPEG2000_PGOD_LRCP 0x00 // Layer-resolution level-component-position progression @@ -189,6 +189,9 @@ typedef struct Jpeg2000Cblk { Jpeg2000Pass *passes; Jpeg2000Layer *layers; int coord[2][2]; // border coordinates {{x0, x1}, {y0, y1}} + /* specific to HT code-blocks */ + int zbp; + int pass_lengths[2]; } Jpeg2000Cblk; // code block typedef struct Jpeg2000Prec { diff --git a/libavcodec/jpeg2000dec.c b/libavcodec/jpeg2000dec.c index e8b6a5900f4..72aaefbdb3e 100644 --- a/libavcodec/jpeg2000dec.c +++ b/libavcodec/jpeg2000dec.c @@ -42,6 +42,8 @@ #include "jpeg2000.h" #include "jpeg2000dsp.h" #include "profiles.h" +#include "jpeg2000dec.h" +#include "jpeg2000htdec.h" #define JP2_SIG_TYPE 0x6A502020 #define JP2_SIG_VALUE 0x0D0A870A @@ -51,93 +53,6 @@ #define HAD_COC 0x01 #define HAD_QCC 0x02 -#define MAX_POCS 32 - -typedef struct Jpeg2000POCEntry { - uint16_t LYEpoc; - uint16_t CSpoc; - uint16_t CEpoc; - uint8_t RSpoc; - uint8_t REpoc; - uint8_t Ppoc; -} Jpeg2000POCEntry; - -typedef struct Jpeg2000POC { - Jpeg2000POCEntry poc[MAX_POCS]; - int nb_poc; - int is_default; -} Jpeg2000POC; - -typedef struct Jpeg2000TilePart { - uint8_t tile_index; // Tile index who refers the tile-part - const uint8_t *tp_end; - GetByteContext header_tpg; // bit stream of header if PPM header is used - GetByteContext tpg; // bit stream in tile-part -} Jpeg2000TilePart; - -/* RMK: For JPEG2000 DCINEMA 3 tile-parts in a tile - * one per component, so tile_part elements have a size of 3 */ -typedef struct Jpeg2000Tile { - Jpeg2000Component *comp; - uint8_t properties[4]; - Jpeg2000CodingStyle codsty[4]; - Jpeg2000QuantStyle qntsty[4]; - Jpeg2000POC poc; - Jpeg2000TilePart tile_part[32]; - uint8_t has_ppt; // whether this tile has a ppt marker - uint8_t *packed_headers; // contains packed headers. Used only along with PPT marker - int packed_headers_size; // size in bytes of the packed headers - GetByteContext packed_headers_stream; // byte context corresponding to packed headers - uint16_t tp_idx; // Tile-part index - int coord[2][2]; // border coordinates {{x0, x1}, {y0, y1}} -} Jpeg2000Tile; - -typedef struct Jpeg2000DecoderContext { - AVClass *class; - AVCodecContext *avctx; - GetByteContext g; - - int width, height; - int image_offset_x, image_offset_y; - int tile_offset_x, tile_offset_y; - uint8_t cbps[4]; // bits per sample in particular components - uint8_t sgnd[4]; // if a component is signed - uint8_t properties[4]; - - uint8_t has_ppm; - uint8_t *packed_headers; // contains packed headers. Used only along with PPM marker - int packed_headers_size; - GetByteContext packed_headers_stream; - uint8_t in_tile_headers; - - int cdx[4], cdy[4]; - int precision; - int ncomponents; - int colour_space; - uint32_t palette[256]; - int8_t pal8; - int cdef[4]; - int tile_width, tile_height; - unsigned numXtiles, numYtiles; - int maxtilelen; - AVRational sar; - - Jpeg2000CodingStyle codsty[4]; - Jpeg2000QuantStyle qntsty[4]; - Jpeg2000POC poc; - uint8_t roi_shift[4]; - - int bit_index; - - int curtileno; - - Jpeg2000Tile *tile; - Jpeg2000DSPContext dsp; - - /*options parameters*/ - int reduction_factor; -} Jpeg2000DecoderContext; - /* get_bits functions for JPEG2000 packet bitstream * It is a get_bit function with a bit-stuffing routine. If the value of the * byte is 0xFF, the next byte includes an extra zero bit stuffed into the MSB. @@ -402,8 +317,8 @@ static int get_siz(Jpeg2000DecoderContext *s) if (ret < 0) return ret; - if (s->avctx->profile == FF_PROFILE_JPEG2000_DCINEMA_2K || - s->avctx->profile == FF_PROFILE_JPEG2000_DCINEMA_4K) { + if (s->avctx->profile == AV_PROFILE_JPEG2000_DCINEMA_2K || + s->avctx->profile == AV_PROFILE_JPEG2000_DCINEMA_4K) { possible_fmts = xyz_pix_fmts; possible_fmts_nb = FF_ARRAY_ELEMS(xyz_pix_fmts); } else { @@ -459,6 +374,10 @@ static int get_siz(Jpeg2000DecoderContext *s) s->cdx[0] == s->cdx[1] && s->cdy[0] == s->cdy[1]) { s->avctx->pix_fmt = AV_PIX_FMT_YA8; i = 0; + } else if (ncomponents == 2 && s->precision == 16 && + s->cdx[0] == s->cdx[1] && s->cdy[0] == s->cdy[1]) { + s->avctx->pix_fmt = AV_PIX_FMT_YA16; + i = 0; } else if (ncomponents == 1 && s->precision == 8) { s->avctx->pix_fmt = AV_PIX_FMT_GRAY8; i = 0; @@ -532,12 +451,12 @@ static int get_cox(Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c) c->cblk_style = bytestream2_get_byteu(&s->g); if (c->cblk_style != 0) { // cblk style if (c->cblk_style & JPEG2000_CTSY_HTJ2K_M || c->cblk_style & JPEG2000_CTSY_HTJ2K_F) { - av_log(s->avctx, AV_LOG_ERROR, "Support for High throughput JPEG 2000 is not yet available\n"); - return AVERROR_PATCHWELCOME; + av_log(s->avctx,AV_LOG_TRACE,"High Throughput jpeg 2000 codestream.\n"); + } else { + av_log(s->avctx, AV_LOG_WARNING, "extra cblk styles %X\n", c->cblk_style); + if (c->cblk_style & JPEG2000_CBLK_BYPASS) + av_log(s->avctx, AV_LOG_WARNING, "Selective arithmetic coding bypass\n"); } - av_log(s->avctx, AV_LOG_WARNING, "extra cblk styles %X\n", c->cblk_style); - if (c->cblk_style & JPEG2000_CBLK_BYPASS) - av_log(s->avctx, AV_LOG_WARNING, "Selective arithmetic coding bypass\n"); } c->transform = bytestream2_get_byteu(&s->g); // DWT transformation type /* set integer 9/7 DWT in case of BITEXACT flag */ @@ -570,7 +489,7 @@ static int get_cox(Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c) /* get coding parameters for a particular tile or whole image*/ static int get_cod(Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c, - uint8_t *properties) + const uint8_t *properties) { Jpeg2000CodingStyle tmp; int compno, ret; @@ -720,7 +639,7 @@ static int get_qcx(Jpeg2000DecoderContext *s, int n, Jpeg2000QuantStyle *q) /* Get quantization parameters for a particular tile or a whole image. */ static int get_qcd(Jpeg2000DecoderContext *s, int n, Jpeg2000QuantStyle *q, - uint8_t *properties) + const uint8_t *properties) { Jpeg2000QuantStyle tmp; int compno, ret; @@ -915,9 +834,6 @@ static int get_tlm(Jpeg2000DecoderContext *s, int n) case 2: bytestream2_get_be16(&s->g); break; - case 3: - bytestream2_get_be32(&s->g); - break; } if (SP == 0) { bytestream2_get_be16(&s->g); @@ -967,8 +883,8 @@ static int get_ppm(Jpeg2000DecoderContext *s, int n) return AVERROR(ENOMEM); s->has_ppm = 1; memset(&s->packed_headers_stream, 0, sizeof(s->packed_headers_stream)); - bytestream_get_buffer(&s->g.buffer, s->packed_headers + s->packed_headers_size, - n - 3); + bytestream2_get_bufferu(&s->g, s->packed_headers + s->packed_headers_size, + n - 3); s->packed_headers_size += n - 3; return 0; @@ -1002,10 +918,8 @@ static int get_ppt(Jpeg2000DecoderContext *s, int n) } else return AVERROR(ENOMEM); memset(&tile->packed_headers_stream, 0, sizeof(tile->packed_headers_stream)); - memcpy(tile->packed_headers + tile->packed_headers_size, - s->g.buffer, n - 3); + bytestream2_get_bufferu(&s->g, tile->packed_headers + tile->packed_headers_size, n - 3); tile->packed_headers_size += n - 3; - bytestream2_skip(&s->g, n - 3); return 0; } @@ -1085,7 +999,7 @@ static int getlblockinc(Jpeg2000DecoderContext *s) return res; } -static inline void select_header(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, +static inline void select_header(Jpeg2000DecoderContext *s, const Jpeg2000Tile *tile, int *tp_index) { s->g = tile->tile_part[*tp_index].header_tpg; @@ -1096,8 +1010,8 @@ static inline void select_header(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, } } -static inline void select_stream(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, - int *tp_index, Jpeg2000CodingStyle *codsty) +static inline void select_stream(Jpeg2000DecoderContext *s, const Jpeg2000Tile *tile, + int *tp_index, const Jpeg2000CodingStyle *codsty) { s->g = tile->tile_part[*tp_index].tpg; if (bytestream2_get_bytes_left(&s->g) == 0 && s->bit_index == 8) { @@ -1114,9 +1028,9 @@ static inline void select_stream(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, } static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, int *tp_index, - Jpeg2000CodingStyle *codsty, + const Jpeg2000CodingStyle *codsty, Jpeg2000ResLevel *rlevel, int precno, - int layno, uint8_t *expn, int numgbits) + int layno, const uint8_t *expn, int numgbits) { int bandno, cblkno, ret, nb_code_blocks; int cwsno; @@ -1162,13 +1076,15 @@ static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, return incl; if (!cblk->npasses) { - int v = expn[bandno] + numgbits - 1 - - tag_tree_decode(s, prec->zerobits + cblkno, 100); + int zbp = tag_tree_decode(s, prec->zerobits + cblkno, 100); + int v = expn[bandno] + numgbits - 1 - zbp; + if (v < 0 || v > 30) { av_log(s->avctx, AV_LOG_ERROR, "nonzerobits %d invalid or unsupported\n", v); return AVERROR_INVALIDDATA; } + cblk->zbp = zbp; cblk->nonzerobits = v; } if ((newpasses = getnpasses(s)) < 0) @@ -1209,8 +1125,23 @@ static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, } } - if ((ret = get_bits(s, av_log2(newpasses1) + cblk->lblock)) < 0) - return ret; + if (newpasses > 1 && (codsty->cblk_style & JPEG2000_CTSY_HTJ2K_F)) { + // Retrieve pass lengths for each pass + int href_passes = (cblk->npasses + newpasses - 1) % 3; + int eb = av_log2(newpasses - href_passes); + int extra_bit = newpasses > 2 ? 1 : 0; + if ((ret = get_bits(s, llen + eb + 3)) < 0) + return ret; + cblk->pass_lengths[0] = ret; + if ((ret = get_bits(s, llen + 3 + extra_bit)) < 0) + return ret; + cblk->pass_lengths[1] = ret; + ret = cblk->pass_lengths[0] + cblk->pass_lengths[1]; + } else { + if ((ret = get_bits(s, av_log2(newpasses1) + cblk->lblock)) < 0) + return ret; + cblk->pass_lengths[0] = ret; + } if (ret > cblk->data_allocated) { size_t new_size = FFMAX(2*cblk->data_allocated, ret); void *new = av_realloc(cblk->data, new_size); @@ -1951,7 +1882,7 @@ static inline void roi_scale_cblk(Jpeg2000Cblk *cblk, } } -static inline void tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile *tile) +static inline int tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile *tile) { Jpeg2000T1Context t1; @@ -1959,9 +1890,12 @@ static inline void tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile /* Loop on tile components */ for (compno = 0; compno < s->ncomponents; compno++) { - Jpeg2000Component *comp = tile->comp + compno; - Jpeg2000CodingStyle *codsty = tile->codsty + compno; + Jpeg2000Component *comp = tile->comp + compno; + Jpeg2000CodingStyle *codsty = tile->codsty + compno; + Jpeg2000QuantStyle *quantsty = tile->qntsty + compno; + int coded = 0; + int subbandno = 0; t1.stride = (1<log2_cblk_width) + 2; @@ -1969,10 +1903,12 @@ static inline void tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile for (reslevelno = 0; reslevelno < codsty->nreslevels2decode; reslevelno++) { Jpeg2000ResLevel *rlevel = comp->reslevel + reslevelno; /* Loop on bands */ - for (bandno = 0; bandno < rlevel->nbands; bandno++) { + for (bandno = 0; bandno < rlevel->nbands; bandno++, subbandno++) { int nb_precincts, precno; Jpeg2000Band *band = rlevel->band + bandno; int cblkno = 0, bandpos; + /* See Rec. ITU-T T.800, Equation E-2 */ + int magp = quantsty->expn[subbandno] + quantsty->nguardbits - 1; bandpos = bandno + (reslevelno > 0); @@ -1980,6 +1916,11 @@ static inline void tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile band->coord[1][0] == band->coord[1][1]) continue; + if ((codsty->cblk_style & JPEG2000_CTSY_HTJ2K_F) && magp >= 31) { + avpriv_request_sample(s->avctx, "JPEG2000_CTSY_HTJ2K_F and magp >= 31"); + return AVERROR_PATCHWELCOME; + } + nb_precincts = rlevel->num_precincts_x * rlevel->num_precincts_y; /* Loop on precincts */ for (precno = 0; precno < nb_precincts; precno++) { @@ -1989,12 +1930,21 @@ static inline void tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile for (cblkno = 0; cblkno < prec->nb_codeblocks_width * prec->nb_codeblocks_height; cblkno++) { - int x, y; + int x, y, ret; + Jpeg2000Cblk *cblk = prec->cblk + cblkno; - int ret = decode_cblk(s, codsty, &t1, cblk, - cblk->coord[0][1] - cblk->coord[0][0], - cblk->coord[1][1] - cblk->coord[1][0], - bandpos, comp->roi_shift); + + if (codsty->cblk_style & JPEG2000_CTSY_HTJ2K_F) + ret = ff_jpeg2000_decode_htj2k(s, codsty, &t1, cblk, + cblk->coord[0][1] - cblk->coord[0][0], + cblk->coord[1][1] - cblk->coord[1][0], + magp, comp->roi_shift); + else + ret = decode_cblk(s, codsty, &t1, cblk, + cblk->coord[0][1] - cblk->coord[0][0], + cblk->coord[1][1] - cblk->coord[1][0], + bandpos, comp->roi_shift); + if (ret) coded = 1; else @@ -2020,6 +1970,7 @@ static inline void tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile ff_dwt_decode(&comp->dwt, codsty->transform == FF_DWT97 ? (void*)comp->f_data : (void*)comp->i_data); } /*end comp */ + return 0; } #define WRITE_FRAME(D, PIXEL) \ @@ -2096,7 +2047,9 @@ static int jpeg2000_decode_tile(AVCodecContext *avctx, void *td, AVFrame *picture = td; Jpeg2000Tile *tile = s->tile + jobnr; - tile_codeblocks(s, tile); + int ret = tile_codeblocks(s, tile); + if (ret < 0) + return ret; /* inverse MCT transformation */ if (tile->codsty[0].mct) @@ -2550,7 +2503,7 @@ static int jpeg2000_decode_frame(AVCodecContext *avctx, AVFrame *picture, if ((ret = ff_thread_get_buffer(avctx, picture, 0)) < 0) goto end; picture->pict_type = AV_PICTURE_TYPE_I; - picture->key_frame = 1; + picture->flags |= AV_FRAME_FLAG_KEY; if (ret = jpeg2000_read_bitstream_packets(s)) goto end; diff --git a/libavcodec/jpeg2000dec.h b/libavcodec/jpeg2000dec.h new file mode 100644 index 00000000000..d0ca6e7a794 --- /dev/null +++ b/libavcodec/jpeg2000dec.h @@ -0,0 +1,119 @@ +/* + * JPEG 2000 image decoder + * Copyright (c) 2007 Kamil Nowosad + * Copyright (c) 2013 Nicolas Bertrand + * Copyright (c) 2022 Caleb Etemesi + * + * 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 + */ + +#ifndef AVCODEC_JPEG2000DEC_H +#define AVCODEC_JPEG2000DEC_H + +#include "bytestream.h" +#include "jpeg2000.h" +#include "jpeg2000dsp.h" + + +#define MAX_POCS 32 + +typedef struct Jpeg2000POCEntry { + uint16_t LYEpoc; + uint16_t CSpoc; + uint16_t CEpoc; + uint8_t RSpoc; + uint8_t REpoc; + uint8_t Ppoc; +} Jpeg2000POCEntry; + +typedef struct Jpeg2000POC { + Jpeg2000POCEntry poc[MAX_POCS]; + int nb_poc; + int is_default; +} Jpeg2000POC; + +typedef struct Jpeg2000TilePart { + uint8_t tile_index; // Tile index who refers the tile-part + const uint8_t *tp_end; + GetByteContext header_tpg; // bit stream of header if PPM header is used + GetByteContext tpg; // bit stream in tile-part +} Jpeg2000TilePart; + +/* RMK: For JPEG2000 DCINEMA 3 tile-parts in a tile + * one per component, so tile_part elements have a size of 3 */ +typedef struct Jpeg2000Tile { + Jpeg2000Component *comp; + uint8_t properties[4]; + Jpeg2000CodingStyle codsty[4]; + Jpeg2000QuantStyle qntsty[4]; + Jpeg2000POC poc; + Jpeg2000TilePart tile_part[32]; + uint8_t has_ppt; // whether this tile has a ppt marker + uint8_t *packed_headers; // contains packed headers. Used only along with PPT marker + int packed_headers_size; // size in bytes of the packed headers + GetByteContext packed_headers_stream; // byte context corresponding to packed headers + uint16_t tp_idx; // Tile-part index + int coord[2][2]; // border coordinates {{x0, x1}, {y0, y1}} +} Jpeg2000Tile; + +typedef struct Jpeg2000DecoderContext { + AVClass *class; + AVCodecContext *avctx; + GetByteContext g; + + int width, height; + int image_offset_x, image_offset_y; + int tile_offset_x, tile_offset_y; + uint8_t cbps[4]; // bits per sample in particular components + uint8_t sgnd[4]; // if a component is signed + uint8_t properties[4]; + + uint8_t has_ppm; + uint8_t *packed_headers; // contains packed headers. Used only along with PPM marker + int packed_headers_size; + GetByteContext packed_headers_stream; + uint8_t in_tile_headers; + + int cdx[4], cdy[4]; + int precision; + int ncomponents; + int colour_space; + uint32_t palette[256]; + int8_t pal8; + int cdef[4]; + int tile_width, tile_height; + unsigned numXtiles, numYtiles; + int maxtilelen; + AVRational sar; + + Jpeg2000CodingStyle codsty[4]; + Jpeg2000QuantStyle qntsty[4]; + Jpeg2000POC poc; + uint8_t roi_shift[4]; + + int bit_index; + + int curtileno; + + Jpeg2000Tile *tile; + Jpeg2000DSPContext dsp; + + /*options parameters*/ + int reduction_factor; +} Jpeg2000DecoderContext; + +#endif //AVCODEC_JPEG2000DEC_H diff --git a/libavcodec/jpeg2000dsp.c b/libavcodec/jpeg2000dsp.c index b1bff6d5b17..7840fdc3577 100644 --- a/libavcodec/jpeg2000dsp.c +++ b/libavcodec/jpeg2000dsp.c @@ -26,7 +26,7 @@ /* Inverse ICT parameters in float and integer. * int value = (float value) * (1<<16) */ -static const float f_ict_params[4] = { +const float ff_jpeg2000_f_ict_params[4] = { 1.402f, 0.34413f, 0.71414f, @@ -42,6 +42,7 @@ static const int i_ict_params[4] = { static void ict_float(void *_src0, void *_src1, void *_src2, int csize) { + const float *const f_ict_params = ff_jpeg2000_f_ict_params; float *src0 = _src0, *src1 = _src1, *src2 = _src2; float i0f, i1f, i2f; int i; @@ -95,7 +96,9 @@ av_cold void ff_jpeg2000dsp_init(Jpeg2000DSPContext *c) c->mct_decode[FF_DWT53] = rct_int; c->mct_decode[FF_DWT97_INT] = ict_int; -#if ARCH_X86 +#if ARCH_RISCV + ff_jpeg2000dsp_init_riscv(c); +#elif ARCH_X86 ff_jpeg2000dsp_init_x86(c); #endif } diff --git a/libavcodec/jpeg2000dsp.h b/libavcodec/jpeg2000dsp.h index 1ae5b95d9a9..6854c7762db 100644 --- a/libavcodec/jpeg2000dsp.h +++ b/libavcodec/jpeg2000dsp.h @@ -30,7 +30,10 @@ typedef struct Jpeg2000DSPContext { void (*mct_decode[FF_DWT_NB])(void *src0, void *src1, void *src2, int csize); } Jpeg2000DSPContext; +extern const float ff_jpeg2000_f_ict_params[4]; + void ff_jpeg2000dsp_init(Jpeg2000DSPContext *c); +void ff_jpeg2000dsp_init_riscv(Jpeg2000DSPContext *c); void ff_jpeg2000dsp_init_x86(Jpeg2000DSPContext *c); #endif /* AVCODEC_JPEG2000DSP_H */ diff --git a/libavcodec/jpeg2000htdec.c b/libavcodec/jpeg2000htdec.c new file mode 100644 index 00000000000..4f0b10b4293 --- /dev/null +++ b/libavcodec/jpeg2000htdec.c @@ -0,0 +1,1476 @@ +/* + * Copyright (c) 2022 Caleb Etemesi + * + * 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 + */ + +/* + * Copyright 2019 - 2021, Osamu Watanabe + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "libavutil/attributes.h" +#include "libavutil/common.h" +#include "libavutil/avassert.h" +#include "jpeg2000htdec.h" +#include "jpeg2000.h" +#include "jpeg2000dec.h" + +#define J2K_Q1 0 +#define J2K_Q2 1 + +#define HT_SHIFT_SIGMA 0 +#define HT_SHIFT_SCAN 4 +#define HT_SHIFT_REF 3 +#define HT_SHIFT_REF_IND 2 + +/* See Rec. ITU-T T.800, Table 2 */ +const static uint8_t mel_e[13] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5 }; + +static const uint16_t dec_cxt_vlc_table1[1024]; +static const uint16_t dec_cxt_vlc_table0[1024]; + +typedef struct StateVars { + int32_t pos; + uint32_t bits; + uint32_t tmp; + uint32_t last; + uint8_t bits_left; + uint64_t bit_buf; +} StateVars; + +typedef struct MelDecoderState { + uint8_t k; + uint8_t run; + uint8_t one; +} MelDecoderState; + +/** + * Given a precomputed c, checks whether n % d == 0. c is precomputed from d + * using precompute_c(). + */ +av_always_inline +static uint32_t is_divisible(uint32_t n, uint64_t c) +{ + return n * c <= c - 1; +} + +/** + * Precompute the number c used by is_divisible(). + */ +av_always_inline +static uint64_t precompute_c(uint32_t d) +{ + return 1 + (0xffffffffffffffffull / d); +} + +static void jpeg2000_init_zero(StateVars *s) +{ + s->bits_left = 0; + s->bit_buf = 0; + s->tmp = 0; + s->bits = 0; + s->pos = 0; + s->last = 0; +} + +static void jpeg2000_init_mel(StateVars *s, uint32_t Pcup) +{ + jpeg2000_init_zero(s); + s->pos = Pcup; +} + +static void jpeg2000_init_mag_ref(StateVars *s, uint32_t Lref) +{ + s->pos = Lref - 2; + s->bits = 0; + s->last = 0xFF; + s->tmp = 0; + s->bits_left = 0; + s->bit_buf = 0; +} + +static void jpeg2000_init_mel_decoder(MelDecoderState *mel_state) +{ + mel_state->k = 0; + mel_state->run = 0; + mel_state->one = 0; +} + +/** + * Refill the buffer backwards in little endian while skipping over stuffing + * bits. Stuffing bits are those that appear in the position of any byte whose + * LSBs are all 1's if the last consumed byte was larger than 0x8F. + */ +static int jpeg2000_bitbuf_refill_backwards(StateVars *buffer, const uint8_t *array) +{ + uint64_t tmp = 0; + int32_t position = buffer->pos - 4; + uint32_t new_bits = 32; + + if (buffer->bits_left >= 32) + return 0; // enough data, no need to pull in more bits + + /** + * Unstuff bits. Load a temporary byte, which precedes the position we + * currently at, to ensure that we can also un-stuff if the stuffed bit is + * the bottom most bits. + */ + + for(int i = FFMAX(0, position + 1); i <= buffer->pos + 1; i++) + tmp = 256*tmp + array[i]; + + if ((tmp & 0x7FFF000000) > 0x7F8F000000) { + tmp &= 0x7FFFFFFFFF; + new_bits--; + } + if ((tmp & 0x007FFF0000) > 0x007F8F0000) { + tmp = (tmp & 0x007FFFFFFF) + ((tmp & 0xFF00000000) >> 1); + new_bits--; + } + if ((tmp & 0x00007FFF00) > 0x00007F8F00) { + tmp = (tmp & 0x00007FFFFF) + ((tmp & 0xFFFF000000) >> 1); + new_bits--; + } + if ((tmp & 0x0000007FFF) > 0x0000007F8F) { + tmp = (tmp & 0x0000007FFF) + ((tmp & 0xFFFFFF0000) >> 1); + new_bits--; + } + + tmp >>= 8; // Remove temporary byte loaded + + /* Add bits to the MSB of the bit buffer */ + buffer->bit_buf |= tmp << buffer->bits_left; + buffer->bits_left += new_bits; + buffer->pos = FFMAX(0, position); + return 0; +} + +/** + * Refill the bit-buffer reading new bits going forward + * in the stream while skipping over stuffed bits. + */ +static void jpeg2000_bitbuf_refill_forward(StateVars *buffer, const uint8_t *array, + uint32_t length) +{ + while (buffer->bits_left < 32) { + buffer->tmp = 0xFF; + buffer->bits = (buffer->last == 0xFF) ? 7 : 8; + if (buffer->pos <= length) { + buffer->tmp = array[buffer->pos]; + buffer->pos += 1; + buffer->last = buffer->tmp; + } + buffer->bit_buf |= ((uint64_t) buffer->tmp) << buffer->bits_left; + buffer->bits_left += buffer->bits; + } +} + +/** + * Drops bits from lower bits in the bit buffer. buf contains the bit buffers. + * nbits is the number of bits to remove. + */ +av_always_inline +static void jpeg2000_bitbuf_drop_bits_lsb(StateVars *buf, uint8_t nbits) +{ + av_assert2(buf->bits_left >= nbits); // cannot read more bits than available + buf->bit_buf >>= nbits; + buf->bits_left -= nbits; +} + +/** + * Get bits from the bit buffer reading them from the least significant bits + * moving to the most significant bits. In case there are fewer bits, refill + * from buf moving backwards. + */ +av_always_inline +static uint64_t jpeg2000_bitbuf_get_bits_lsb(StateVars *bit_stream, uint8_t nbits, + const uint8_t *buf) +{ + uint64_t bits; + uint64_t mask = (1ull << nbits) - 1; + if (bit_stream->bits_left < nbits) + jpeg2000_bitbuf_refill_backwards(bit_stream, buf); + bits = bit_stream->bit_buf & mask; + jpeg2000_bitbuf_drop_bits_lsb(bit_stream, nbits); + return bits; +} + +/** + * Get bits from the bit buffer reading them from the least significant bits + * moving to the most significant bits. In case there are fewer bits, refill from + * buf moving forward. + */ +av_always_inline +static uint64_t jpeg2000_bitbuf_get_bits_lsb_forward(StateVars *bit_stream, + uint8_t nbits, const uint8_t *buf, + uint32_t length) +{ + uint64_t bits; + uint64_t mask = (1ull << nbits) - 1; + + if (bit_stream->bits_left <= nbits) + jpeg2000_bitbuf_refill_forward(bit_stream, buf, length); + bits = bit_stream->bit_buf & mask; + jpeg2000_bitbuf_drop_bits_lsb(bit_stream, nbits); + return bits; +} + +/** + * Look ahead bit buffer without discarding bits. + */ +av_always_inline +static uint64_t jpeg2000_bitbuf_peek_bits_lsb(StateVars *stream, uint8_t nbits) +{ + uint64_t mask = (1ull << nbits) - 1; + return stream->bit_buf & mask; +} + +static void jpeg2000_init_vlc(StateVars *s, uint32_t Lcup, uint32_t Pcup, + const uint8_t *Dcup) +{ + s->bits_left = 0; + s->bit_buf = 0; + s->pos = Lcup - 2 - Pcup; + s->last = Dcup[Lcup - 2]; + s->tmp = (s->last) >> 4; + s->bits = ((s->tmp & 7) < 7) ? 4 : 3; + + jpeg2000_bitbuf_refill_backwards(s, Dcup + Pcup); + jpeg2000_bitbuf_drop_bits_lsb(s, 4); +} + +/** + * Decode prefix codes for VLC segment. See Rec. ITU-T T.814, 7.3.5. + */ +av_always_inline +static int jpeg2000_decode_ctx_vlc(const Jpeg2000DecoderContext *s, + StateVars *vlc_stream, const uint16_t *table, + const uint8_t *Dcup, uint8_t *sig_pat, + uint8_t *res_off, uint8_t *emb_pat_k, + uint8_t *emb_pat_1, uint8_t pos, + uint32_t Pcup, uint16_t context) +{ + uint32_t value; + uint8_t len; + uint64_t index; + uint64_t code_word; + + jpeg2000_bitbuf_refill_backwards(vlc_stream, Dcup + Pcup); + + code_word = vlc_stream->bit_buf & 0x7f; + index = code_word + (context << 7); + + av_assert0(index < 1024); // The CxtVLC table has 1024 entries. + + value = table[index]; + + len = (value & 0x000F) >> 1; + + res_off[pos] = (uint8_t) (value & 1); + sig_pat[pos] = (uint8_t) ((value & 0x00F0) >> 4); + emb_pat_k[pos] = (uint8_t) ((value & 0x0F00) >> 8); + emb_pat_1[pos] = (uint8_t) ((value & 0xF000) >> 12); + + jpeg2000_bitbuf_drop_bits_lsb(vlc_stream, len); + return 0; +} + +/** + * Decode variable length u-vlc prefix. See decodeUPrefix procedure at Rec. + * ITU-T T.814, 7.3.6. + */ +av_always_inline +static uint8_t vlc_decode_u_prefix(StateVars *vlc_stream, const uint8_t *refill_array) +{ + static const uint8_t return_value[8] = { 5, 1, 2, 1, 3, 1, 2, 1 }; + static const uint8_t drop_bits[8] = { 3, 1, 2, 1, 3, 1, 2, 1 }; + + uint8_t bits; + + if (vlc_stream->bits_left < 3) + jpeg2000_bitbuf_refill_backwards(vlc_stream, refill_array); + + bits = jpeg2000_bitbuf_peek_bits_lsb(vlc_stream, 3); + + jpeg2000_bitbuf_drop_bits_lsb(vlc_stream, drop_bits[bits]); + return return_value[bits]; +} + +/** + * Decode variable length u-vlc suffix. See decodeUSuffix procedure at Rec. + * ITU-T T.814, 7.3.6. + */ +av_always_inline +static uint8_t vlc_decode_u_suffix(StateVars *vlc_stream, uint8_t suffix, + const uint8_t *refill_array) +{ + static const int mask[] = { 1, 31 }; + static const int drop_bits[] = { 1, 5 }; + + uint8_t bits; + int cond = suffix != 3; + if (suffix < 3) + return 0; + + if (vlc_stream->bits_left < 5) + jpeg2000_bitbuf_refill_backwards(vlc_stream, refill_array); + + bits = jpeg2000_bitbuf_peek_bits_lsb(vlc_stream, 5); + + jpeg2000_bitbuf_drop_bits_lsb(vlc_stream, drop_bits[cond]); + return bits & mask[cond]; +} + +/** + * Decode u-vlc extension values. See decodeUExtension procedure at Rec. ITU-T + * T.814, 7.3.6. + */ +av_always_inline +static uint8_t vlc_decode_u_extension(StateVars *vlc_stream, uint8_t suffix, + const uint8_t *refill_array) +{ + return jpeg2000_bitbuf_get_bits_lsb(vlc_stream, 4 * (suffix >= 28), refill_array); +} + +/** + * Magnitude and Sign decode procedures. See decodeMagSgnValue procedure at Rec. + * ITU-T T.814, 7.3.8. + */ +av_always_inline +static int32_t jpeg2000_decode_mag_sgn(StateVars *mag_sgn_stream, int32_t m_n, + int32_t i_n, const uint8_t *buf, uint32_t length) +{ + int32_t val = 0; + if (m_n > 0) { + val = jpeg2000_bitbuf_get_bits_lsb_forward(mag_sgn_stream,m_n,buf,length); + val += (i_n << m_n); + } + return val; +} + +av_always_inline +static void recover_mag_sgn(StateVars *mag_sgn, uint8_t pos, uint16_t q, int32_t m_n[2], + int32_t known_1[2], const uint8_t emb_pat_1[2], + int32_t v[2][4], int32_t m[2][4], uint8_t *E, + uint32_t *mu_n, const uint8_t *Dcup, uint32_t Pcup, + uint32_t pLSB) +{ + for (int i = 0; i < 4; i++) { + int32_t n = 4 * q + i; + m_n[pos] = m[pos][i]; + known_1[pos] = (emb_pat_1[pos] >> i) & 1; + v[pos][i] = jpeg2000_decode_mag_sgn(mag_sgn, m_n[pos], known_1[pos], Dcup, Pcup); + + if (m_n[pos] != 0) { + E[n] = 32 - ff_clz(v[pos][i] | 1); + mu_n[n] = (v[pos][i] >> 1) + 1; + mu_n[n] <<= pLSB; + mu_n[n] |= ((uint32_t) (v[pos][i] & 1)) << 31; // sign bit. + } + } +} + +static int jpeg2000_import_bit(StateVars *stream, const uint8_t *array, uint32_t length) +{ + int cond = stream->pos <= length; + int pos = FFMIN(stream->pos, length); + if (stream->bits == 0) { + stream->bits = (stream->tmp == 0xFF) ? 7 : 8; + stream->pos += cond; + stream->tmp = cond ? array[pos] : 0xFF; + } + stream->bits -= 1; + return (stream->tmp >> stream->bits) & 1; +} + +static int jpeg2000_peek_bit(StateVars *stream, const uint8_t *array, uint32_t length) +{ + if (stream->bits == 0) { + int cond = stream->pos <= length; + int pos = FFMIN(stream->pos, length); + stream->bits = (stream->tmp == 0xFF) ? 7 : 8; + stream->pos += cond; + stream->tmp = cond ? array[pos] : 0xFF; + } + return (stream->tmp >> stream->bits) & 1; +} + +static int jpeg2000_decode_mel_sym(MelDecoderState *mel_state, + StateVars *mel_stream, + const uint8_t *Dcup, + uint32_t Lcup) +{ + + if (mel_state->run == 0 && mel_state->one == 0) { + uint8_t eval; + uint8_t bit; + + eval = mel_e[mel_state->k]; + bit = jpeg2000_import_bit(mel_stream, Dcup, Lcup); + if (bit == 1) { + mel_state->run = 1 << eval; + mel_state->k = FFMIN(12, mel_state->k + 1); + } else { + mel_state->run = 0; + while (eval > 0) { + bit = jpeg2000_import_bit(mel_stream, Dcup, Lcup); + mel_state->run = (2 * (mel_state->run)) + bit; + eval -= 1; + } + mel_state->k = FFMAX(0, mel_state->k - 1); + mel_state->one = 1; + } + } + if (mel_state->run > 0) { + mel_state->run -= 1; + return 0; + } else { + mel_state->one = 0; + return 1; + } +} + +/** + * Magref decoding procedures. + */ +av_always_inline +static int jpeg2000_import_magref_bit(StateVars *stream, const uint8_t *array, + uint32_t length) +{ + return jpeg2000_bitbuf_get_bits_lsb(stream, 1, array); +} + +/** + * Signal EMB decode. + */ +static int jpeg2000_decode_sig_emb(const Jpeg2000DecoderContext *s, MelDecoderState *mel_state, + StateVars *mel_stream, StateVars *vlc_stream, + const uint16_t *vlc_table, const uint8_t *Dcup, + uint8_t *sig_pat, uint8_t *res_off, uint8_t *emb_pat_k, + uint8_t *emb_pat_1, uint8_t pos, uint16_t context, + uint32_t Lcup, uint32_t Pcup) +{ + if (context == 0) { + uint8_t sym; + sym = jpeg2000_decode_mel_sym(mel_state, mel_stream, Dcup, Lcup); + if (sym == 0) { + sig_pat[pos] = 0; + res_off[pos] = 0; + emb_pat_k[pos] = 0; + emb_pat_1[pos] = 0; + return 0; + } + } + return jpeg2000_decode_ctx_vlc(s, vlc_stream, vlc_table, Dcup, sig_pat, + res_off, emb_pat_k, emb_pat_1, pos, Pcup, + context); +} + +av_always_inline +static int jpeg2000_get_state(int x1, int x2, int width, int shift_by, + const uint8_t *block_states) +{ + return (block_states[(x1 + 1) * (width + 2) + (x2 + 1)] >> shift_by) & 1; +} + +av_always_inline +static void jpeg2000_modify_state(int x1, int x2, int width, + int value, uint8_t *block_states) +{ + block_states[(x1 + 1) * (width + 2) + (x2 + 1)] |= value; +} + +av_always_inline +static int jpeg2000_decode_ht_cleanup_segment(const Jpeg2000DecoderContext *s, + Jpeg2000Cblk *cblk, Jpeg2000T1Context *t1, + MelDecoderState *mel_state, + StateVars *mel_stream, StateVars *vlc_stream, + StateVars *mag_sgn_stream, const uint8_t *Dcup, + uint32_t Lcup, uint32_t Pcup, uint8_t pLSB, + int width, int height, int32_t *sample_buf, + uint8_t *block_states) +{ + uint16_t q = 0; // Represents current quad position + uint16_t q1, q2; + uint16_t context1, context2; + uint16_t context = 0; + + uint8_t sig_pat[2] = { 0 }; // significance pattern + uint8_t res_off[2] = { 0 }; // residual offset + uint8_t emb_pat_k[2] = { 0 }; // exponent Max Bound pattern K + uint8_t emb_pat_1[2] = { 0 }; // exponent Max Bound pattern 1 + uint8_t gamma[2] = { 0 }; + + uint8_t E_n[2] = { 0 }; + uint8_t E_ne[2] = { 0 }; + uint8_t E_nw[2] = { 0 }; + uint8_t E_nf[2] = { 0 }; + + uint8_t max_e[2] = { 0 }; + uint8_t u_pfx[2] = { 0 }; + uint8_t u_sfx[2] = { 0 }; + uint8_t u_ext[2] = { 0 }; + + int32_t u[2] = { 0 }; + int32_t U[2] = { 0 }; // exponent bound + int32_t m_n[2] = { 0 }; + int32_t known_1[2] = { 0 }; + + int32_t m[2][4] = { 0 }; + int32_t v[2][4] = { 0 }; + + uint8_t kappa[2] = { 1, 1 }; + + int ret = 0; + + int sp; + + uint64_t c; + + uint8_t *sigma, *sigma_n, *E; + uint32_t *mu, *mu_n; + + const uint8_t *vlc_buf = Dcup + Pcup; + + /* + * Bound on the precision needed to process the codeblock. The number of + * decoded bit planes is equal to at most cblk->zbp + 2 since S_blk = P if + * there are no placeholder passes or HT Sets and P = cblk->zbp. See Rec. + * ITU-T T.814, 7.6. + */ + int maxbp = cblk->zbp + 2; + + /* convert to raster-scan */ + const uint16_t is_border_x = width % 2; + const uint16_t is_border_y = height % 2; + + const uint16_t quad_width = ff_jpeg2000_ceildivpow2(width, 1); + const uint16_t quad_height = ff_jpeg2000_ceildivpow2(height, 1); + + size_t buf_size = 4 * quad_width * quad_height; + + /* do we have enough precision, assuming a 32-bit decoding path */ + if (maxbp >= 32) + return AVERROR_INVALIDDATA; + + sigma_n = av_calloc(buf_size, sizeof(uint8_t)); + E = av_calloc(buf_size, sizeof(uint8_t)); + mu_n = av_calloc(buf_size, sizeof(uint32_t)); + + if (!sigma_n || !E || !mu_n) { + ret = AVERROR(ENOMEM); + goto free; + } + + sigma = sigma_n; + mu = mu_n; + + while (q < quad_width - 1) { + q1 = q; + q2 = q1 + 1; + + if ((ret = jpeg2000_decode_sig_emb(s, mel_state, mel_stream, vlc_stream, + dec_cxt_vlc_table0, Dcup, sig_pat, res_off, + emb_pat_k, emb_pat_1, J2K_Q1, context, Lcup, + Pcup)) < 0) + goto free; + + for (int i = 0; i < 4; i++) + sigma_n[4 * q1 + i] = (sig_pat[J2K_Q1] >> i) & 1; + + /* calculate context */ + context = sigma_n[4 * q1]; // f + context |= sigma_n[4 * q1 + 1]; // sf + context += sigma_n[4 * q1 + 2] << 1; // w << 1 + context += sigma_n[4 * q1 + 3] << 2; + + if ((ret = jpeg2000_decode_sig_emb(s, mel_state, mel_stream, vlc_stream, + dec_cxt_vlc_table0, Dcup, sig_pat, res_off, + emb_pat_k, emb_pat_1, J2K_Q2, context, Lcup, + Pcup)) < 0) + goto free; + + for (int i = 0; i < 4; i++) + sigma_n[4 * q2 + i] = (sig_pat[J2K_Q2] >> i) & 1; + + /* calculate context for the next quad */ + context = sigma_n[4 * q2]; // f + context |= sigma_n[4 * q2 + 1]; // sf + context += sigma_n[4 * q2 + 2] << 1; // w << 1 + context += sigma_n[4 * q2 + 3] << 2; // sw << 2 + + u[0] = 0; + u[1] = 0; + + jpeg2000_bitbuf_refill_backwards(vlc_stream, vlc_buf); + + if (res_off[J2K_Q1] == 1 && res_off[J2K_Q2] == 1) { + + if (jpeg2000_decode_mel_sym(mel_state, mel_stream, Dcup, Lcup) == 1) { + + u_pfx[J2K_Q1] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_pfx[J2K_Q2] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + + u_sfx[J2K_Q1] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q1], vlc_buf); + u_sfx[J2K_Q2] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q2], vlc_buf); + + u_ext[J2K_Q1] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q1], vlc_buf); + u_ext[J2K_Q2] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q2], vlc_buf); + + u[J2K_Q1] = 2 + u_pfx[J2K_Q1] + u_sfx[J2K_Q1] + (u_ext[J2K_Q1] * 4); + u[J2K_Q2] = 2 + u_pfx[J2K_Q2] + u_sfx[J2K_Q2] + (u_ext[J2K_Q2] * 4); + + } else { + u_pfx[J2K_Q1] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + + if (u_pfx[J2K_Q1] > 2) { + u[J2K_Q2] = jpeg2000_bitbuf_get_bits_lsb(vlc_stream, 1, vlc_buf) + 1; + u_sfx[J2K_Q1] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q1], vlc_buf); + u_ext[J2K_Q1] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q1], vlc_buf); + } else { + u_pfx[J2K_Q2] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_sfx[J2K_Q1] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q1], vlc_buf); + u_sfx[J2K_Q2] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q2], vlc_buf); + u_ext[J2K_Q1] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q1], vlc_buf); + u_ext[J2K_Q2] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q2], vlc_buf); + u[J2K_Q2] = u_pfx[J2K_Q2] + u_sfx[J2K_Q2] + (u_ext[J2K_Q2] * 4); + } + /* See Rec. ITU-T T.814, 7.3.6(3) */ + u[J2K_Q1] = u_pfx[J2K_Q1] + u_sfx[J2K_Q1] + (u_ext[J2K_Q1] * 4); + } + + } else if (res_off[J2K_Q1] == 1 || res_off[J2K_Q2] == 1) { + uint8_t pos = res_off[J2K_Q1] == 1 ? 0 : 1; + u_pfx[pos] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_sfx[pos] = vlc_decode_u_suffix(vlc_stream, u_pfx[pos], vlc_buf); + u_ext[pos] = vlc_decode_u_extension(vlc_stream, u_sfx[pos], vlc_buf); + u[pos] = u_pfx[pos] + u_sfx[pos] + (u_ext[pos] * 4); + } + U[J2K_Q1] = kappa[J2K_Q1] + u[J2K_Q1]; + U[J2K_Q2] = kappa[J2K_Q2] + u[J2K_Q2]; + if (U[J2K_Q1] > maxbp || U[J2K_Q2] > maxbp) { + ret = AVERROR_INVALIDDATA; + goto free; + } + + for (int i = 0; i < 4; i++) { + m[J2K_Q1][i] = sigma_n[4 * q1 + i] * U[J2K_Q1] - ((emb_pat_k[J2K_Q1] >> i) & 1); + m[J2K_Q2][i] = sigma_n[4 * q2 + i] * U[J2K_Q2] - ((emb_pat_k[J2K_Q2] >> i) & 1); + } + + recover_mag_sgn(mag_sgn_stream, J2K_Q1, q1, m_n, known_1, emb_pat_1, v, m, + E, mu_n, Dcup, Pcup, pLSB); + + recover_mag_sgn(mag_sgn_stream, J2K_Q2, q2, m_n, known_1, emb_pat_1, v, m, + E, mu_n, Dcup, Pcup, pLSB); + + q += 2; // Move to the next quad pair + } + + if (quad_width % 2 == 1) { + q1 = q; + + if ((ret = jpeg2000_decode_sig_emb(s, mel_state, mel_stream, vlc_stream, + dec_cxt_vlc_table0, Dcup, sig_pat, res_off, + emb_pat_k, emb_pat_1, J2K_Q1, context, Lcup, + Pcup)) < 0) + goto free; + + for (int i = 0; i < 4; i++) + sigma_n[4 * q1 + i] = (sig_pat[J2K_Q1] >> i) & 1; + + u[J2K_Q1] = 0; + + if (res_off[J2K_Q1] == 1) { + u_pfx[J2K_Q1] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_sfx[J2K_Q1] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q1], vlc_buf); + u_ext[J2K_Q1] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q1], vlc_buf); + u[J2K_Q1] = u_pfx[J2K_Q1] + u_sfx[J2K_Q1] + (u_ext[J2K_Q1] * 4); + } + + U[J2K_Q1] = kappa[J2K_Q1] + u[J2K_Q1]; + if (U[J2K_Q1] > maxbp) { + ret = AVERROR_INVALIDDATA; + goto free; + } + + for (int i = 0; i < 4; i++) + m[J2K_Q1][i] = sigma_n[4 * q1 + i] * U[J2K_Q1] - ((emb_pat_k[J2K_Q1] >> i) & 1); + + recover_mag_sgn(mag_sgn_stream, J2K_Q1, q1, m_n, known_1, emb_pat_1, v, m, + E, mu_n, Dcup, Pcup, pLSB); + + q++; // move to next quad pair + } + + /** + * Initial line pair end. As an optimization, we can replace modulo + * operations with checking if a number is divisible , since that's the only + * thing we need. This is paired with is_divisible. Credits to Daniel Lemire + * blog post [1]. + * + * [1] + * https://lemire.me/blog/2019/02/08/faster-remainders-when-the-divisor-is-a-constant-beating-compilers-and-libdivide/ + * + * It's UB on zero, but the spec doesn't allow a quad being zero, so we + * error out early in case that's the case. + */ + c = precompute_c(quad_width); + + for (int row = 1; row < quad_height; row++) { + while ((q - (row * quad_width)) < quad_width - 1 && q < (quad_height * quad_width)) { + q1 = q; + q2 = q + 1; + context1 = sigma_n[4 * (q1 - quad_width) + 1]; + context1 += sigma_n[4 * (q1 - quad_width) + 3] << 2; // ne + + if (!is_divisible(q1, c)) { + context1 |= sigma_n[4 * (q1 - quad_width) - 1]; // nw + context1 += (sigma_n[4 * q1 - 1] | sigma_n[4 * q1 - 2]) << 1; // sw | q + } + if (!is_divisible(q1 + 1, c)) + context1 |= sigma_n[4 * (q1 - quad_width) + 5] << 2; + + if ((ret = jpeg2000_decode_sig_emb(s, mel_state, mel_stream, vlc_stream, + dec_cxt_vlc_table1, Dcup, sig_pat, res_off, + emb_pat_k, emb_pat_1, J2K_Q1, context1, Lcup, + Pcup)) + < 0) + goto free; + + for (int i = 0; i < 4; i++) + sigma_n[4 * q1 + i] = (sig_pat[J2K_Q1] >> i) & 1; + + context2 = sigma_n[4 * (q2 - quad_width) + 1]; + context2 += sigma_n[4 * (q2 - quad_width) + 3] << 2; + + if (!is_divisible(q2, c)) { + context2 |= sigma_n[4 * (q2 - quad_width) - 1]; + context2 += (sigma_n[4 * q2 - 1] | sigma_n[4 * q2 - 2]) << 1; + } + if (!is_divisible(q2 + 1, c)) + context2 |= sigma_n[4 * (q2 - quad_width) + 5] << 2; + + if ((ret = jpeg2000_decode_sig_emb(s, mel_state, mel_stream, vlc_stream, + dec_cxt_vlc_table1, Dcup, sig_pat, res_off, + emb_pat_k, emb_pat_1, J2K_Q2, context2, Lcup, + Pcup)) + < 0) + goto free; + + for (int i = 0; i < 4; i++) + sigma_n[4 * q2 + i] = (sig_pat[J2K_Q2] >> i) & 1; + + u[J2K_Q1] = 0; + u[J2K_Q2] = 0; + + jpeg2000_bitbuf_refill_backwards(vlc_stream, vlc_buf); + + if (res_off[J2K_Q1] == 1 && res_off[J2K_Q2] == 1) { + u_pfx[J2K_Q1] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_pfx[J2K_Q2] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + + u_sfx[J2K_Q1] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q1], vlc_buf); + u_sfx[J2K_Q2] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q2], vlc_buf); + + u_ext[J2K_Q1] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q1], vlc_buf); + u_ext[J2K_Q2] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q2], vlc_buf); + + u[J2K_Q1] = u_pfx[J2K_Q1] + u_sfx[J2K_Q1] + (u_ext[J2K_Q1] << 2); + u[J2K_Q2] = u_pfx[J2K_Q2] + u_sfx[J2K_Q2] + (u_ext[J2K_Q2] << 2); + + } else if (res_off[J2K_Q1] == 1 || res_off[J2K_Q2] == 1) { + uint8_t pos = res_off[J2K_Q1] == 1 ? 0 : 1; + + u_pfx[pos] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_sfx[pos] = vlc_decode_u_suffix(vlc_stream, u_pfx[pos], vlc_buf); + u_ext[pos] = vlc_decode_u_extension(vlc_stream, u_sfx[pos], vlc_buf); + + u[pos] = u_pfx[pos] + u_sfx[pos] + (u_ext[pos] << 2); + } + sp = sig_pat[J2K_Q1]; + + gamma[J2K_Q1] = 1; + + if (sp == 0 || sp == 1 || sp == 2 || sp == 4 || sp == 8) + gamma[J2K_Q1] = 0; + + sp = sig_pat[J2K_Q2]; + + gamma[J2K_Q2] = 1; + + if (sp == 0 || sp == 1 || sp == 2 || sp == 4 || sp == 8) + gamma[J2K_Q2] = 0; + + E_n[J2K_Q1] = E[4 * (q1 - quad_width) + 1]; + E_n[J2K_Q2] = E[4 * (q2 - quad_width) + 1]; + + E_ne[J2K_Q1] = E[4 * (q1 - quad_width) + 3]; + E_ne[J2K_Q2] = E[4 * (q2 - quad_width) + 3]; + + E_nw[J2K_Q1] = (!is_divisible(q1, c)) * E[FFMAX((4 * (q1 - quad_width) - 1), 0)]; + E_nw[J2K_Q2] = (!is_divisible(q2, c)) * E[FFMAX((4 * (q2 - quad_width) - 1), 0)]; + + E_nf[J2K_Q1] = (!is_divisible(q1 + 1, c)) * E[4 * (q1 - quad_width) + 5]; + E_nf[J2K_Q2] = (!is_divisible(q2 + 1, c)) * E[4 * (q2 - quad_width) + 5]; + + max_e[J2K_Q1] = FFMAX(E_nw[J2K_Q1], FFMAX3(E_n[J2K_Q1], E_ne[J2K_Q1], E_nf[J2K_Q1])); + max_e[J2K_Q2] = FFMAX(E_nw[J2K_Q2], FFMAX3(E_n[J2K_Q2], E_ne[J2K_Q2], E_nf[J2K_Q2])); + + kappa[J2K_Q1] = FFMAX(1, gamma[J2K_Q1] * (max_e[J2K_Q1] - 1)); + kappa[J2K_Q2] = FFMAX(1, gamma[J2K_Q2] * (max_e[J2K_Q2] - 1)); + + U[J2K_Q1] = kappa[J2K_Q1] + u[J2K_Q1]; + U[J2K_Q2] = kappa[J2K_Q2] + u[J2K_Q2]; + if (U[J2K_Q1] > maxbp || U[J2K_Q2] > maxbp) { + ret = AVERROR_INVALIDDATA; + goto free; + } + + for (int i = 0; i < 4; i++) { + m[J2K_Q1][i] = sigma_n[4 * q1 + i] * U[J2K_Q1] - ((emb_pat_k[J2K_Q1] >> i) & 1); + m[J2K_Q2][i] = sigma_n[4 * q2 + i] * U[J2K_Q2] - ((emb_pat_k[J2K_Q2] >> i) & 1); + } + recover_mag_sgn(mag_sgn_stream, J2K_Q1, q1, m_n, known_1, emb_pat_1, v, m, + E, mu_n, Dcup, Pcup, pLSB); + + recover_mag_sgn(mag_sgn_stream, J2K_Q2, q2, m_n, known_1, emb_pat_1, v, m, + E, mu_n, Dcup, Pcup, pLSB); + + q += 2; // Move to the next quad pair + } + + if (quad_width % 2 == 1) { + q1 = q; + + /* calculate context for current quad */ + context1 = sigma_n[4 * (q1 - quad_width) + 1]; + context1 += (sigma_n[4 * (q1 - quad_width) + 3] << 2); + + if (!is_divisible(q1, c)) { + context1 |= sigma_n[4 * (q1 - quad_width) - 1]; + context1 += (sigma_n[4 * q1 - 1] | sigma_n[4 * q1 - 2]) << 1; + } + if (!is_divisible(q1 + 1, c)) + context1 |= sigma_n[4 * (q1 - quad_width) + 5] << 2; + + if ((ret = jpeg2000_decode_sig_emb(s, mel_state, mel_stream, vlc_stream, + dec_cxt_vlc_table1, Dcup, sig_pat, res_off, + emb_pat_k, emb_pat_1, J2K_Q1, context1, Lcup, + Pcup)) < 0) + goto free; + + for (int i = 0; i < 4; i++) + sigma_n[4 * q1 + i] = (sig_pat[J2K_Q1] >> i) & 1; + + u[J2K_Q1] = 0; + + /* Recover mag_sgn value */ + if (res_off[J2K_Q1] == 1) { + u_pfx[J2K_Q1] = vlc_decode_u_prefix(vlc_stream, vlc_buf); + u_sfx[J2K_Q1] = vlc_decode_u_suffix(vlc_stream, u_pfx[J2K_Q1], vlc_buf); + u_ext[J2K_Q1] = vlc_decode_u_extension(vlc_stream, u_sfx[J2K_Q1], vlc_buf); + + u[J2K_Q1] = u_pfx[J2K_Q1] + u_sfx[J2K_Q1] + (u_ext[J2K_Q1] << 2); + } + + sp = sig_pat[J2K_Q1]; + + gamma[J2K_Q1] = 1; + + if (sp == 0 || sp == 1 || sp == 2 || sp == 4 || sp == 8) + gamma[J2K_Q1] = 0; + + E_n[J2K_Q1] = E[4 * (q1 - quad_width) + 1]; + + E_ne[J2K_Q1] = E[4 * (q1 - quad_width) + 3]; + + E_nw[J2K_Q1] = (!is_divisible(q1, c)) * E[FFMAX((4 * (q1 - quad_width) - 1), 0)]; + + E_nf[J2K_Q1] = (!is_divisible(q1 + 1, c)) * E[4 * (q1 - quad_width) + 5]; + + max_e[J2K_Q1] = FFMAX(E_nw[J2K_Q1], FFMAX3(E_n[J2K_Q1], E_ne[J2K_Q1], E_nf[J2K_Q1])); + + kappa[J2K_Q1] = FFMAX(1, gamma[J2K_Q1] * (max_e[J2K_Q1] - 1)); + + U[J2K_Q1] = kappa[J2K_Q1] + u[J2K_Q1]; + if (U[J2K_Q1] > maxbp) { + ret = AVERROR_INVALIDDATA; + goto free; + } + + for (int i = 0; i < 4; i++) + m[J2K_Q1][i] = sigma_n[4 * q1 + i] * U[J2K_Q1] - ((emb_pat_k[J2K_Q1] >> i) & 1); + + recover_mag_sgn(mag_sgn_stream, J2K_Q1, q1, m_n, known_1, emb_pat_1, v, m, + E, mu_n, Dcup, Pcup, pLSB); + q += 1; + } + } + + // convert to raster-scan + for (int y = 0; y < quad_height; y++) { + for (int x = 0; x < quad_width; x++) { + int j1, j2; + int x1, x2 , x3; + + j1 = 2 * y; + j2 = 2 * x; + + sample_buf[j2 + (j1 * width)] = (int32_t)*mu; + jpeg2000_modify_state(j1, j2, width, *sigma, block_states); + sigma += 1; + mu += 1; + + x1 = y != quad_height - 1 || is_border_y == 0; + sample_buf[j2 + ((j1 + 1) * width)] = ((int32_t)*mu) * x1; + jpeg2000_modify_state(j1 + 1, j2, width, (*sigma) * x1, block_states); + sigma += 1; + mu += 1; + + x2 = x != quad_width - 1 || is_border_x == 0; + sample_buf[(j2 + 1) + (j1 * width)] = ((int32_t)*mu) * x2; + jpeg2000_modify_state(j1, j2 + 1, width, (*sigma) * x2, block_states); + sigma += 1; + mu += 1; + + x3 = x1 | x2; + sample_buf[(j2 + 1) + (j1 + 1) * width] = ((int32_t)*mu) * x3; + jpeg2000_modify_state(j1 + 1, j2 + 1, width, (*sigma) * x3, block_states); + sigma += 1; + mu += 1; + } + } + ret = 1; +free: + av_freep(&sigma_n); + av_freep(&E); + av_freep(&mu_n); + return ret; +} + +static void jpeg2000_calc_mbr(uint8_t *mbr, const uint16_t i, const uint16_t j, + const uint32_t mbr_info, uint8_t causal_cond, + uint8_t *block_states, int width) +{ + int local_mbr = 0; + + local_mbr |= jpeg2000_get_state(i - 1, j - 1, width, HT_SHIFT_SIGMA, block_states); + local_mbr |= jpeg2000_get_state(i - 1, j + 0, width, HT_SHIFT_SIGMA, block_states); + local_mbr |= jpeg2000_get_state(i - 1, j + 1, width, HT_SHIFT_SIGMA, block_states); + + local_mbr |= jpeg2000_get_state(i + 0, j - 1, width, HT_SHIFT_SIGMA, block_states); + local_mbr |= jpeg2000_get_state(i + 0, j + 1, width, HT_SHIFT_SIGMA, block_states); + + local_mbr |= jpeg2000_get_state(i + 1, j - 1, width, HT_SHIFT_SIGMA, block_states) * causal_cond; + local_mbr |= jpeg2000_get_state(i + 1, j + 0, width, HT_SHIFT_SIGMA, block_states) * causal_cond; + local_mbr |= jpeg2000_get_state(i + 1, j + 1, width, HT_SHIFT_SIGMA, block_states) * causal_cond; + + local_mbr |= jpeg2000_get_state(i - 1, j - 1, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i - 1, j - 1, width, HT_SHIFT_SCAN, block_states); + local_mbr |= jpeg2000_get_state(i - 1, j + 0, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i - 1, j - 1, width, HT_SHIFT_SCAN, block_states); + local_mbr |= jpeg2000_get_state(i - 1, j + 1, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i - 1, j + 1, width, HT_SHIFT_SCAN, block_states); + + local_mbr |= jpeg2000_get_state(i + 0, j - 1, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i + 0, j - 1, width, HT_SHIFT_SCAN, block_states); + local_mbr |= jpeg2000_get_state(i + 0, j + 1, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i + 0, j + 1, width, HT_SHIFT_SCAN, block_states); + + local_mbr |= jpeg2000_get_state(i + 1, j - 1, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i + 1, j - 1, width, HT_SHIFT_SCAN, block_states) * causal_cond; + local_mbr |= jpeg2000_get_state(i + 1, j + 0, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i + 1, j + 0, width, HT_SHIFT_SCAN, block_states) * causal_cond; + local_mbr |= jpeg2000_get_state(i + 1, j + 1, width, HT_SHIFT_REF, block_states) * + jpeg2000_get_state(i + 1, j + 1, width, HT_SHIFT_SCAN, block_states) * causal_cond; + + *mbr |= local_mbr; +} + +static void jpeg2000_process_stripes_block(StateVars *sig_prop, int i_s, int j_s, + int width, int height, int stride, int pLSB, + int32_t *sample_buf, uint8_t *block_states, + uint8_t *magref_segment, uint32_t magref_length) +{ + for (int j = j_s; j < j_s + width; j++) { + uint32_t mbr_info = 0; + for (int i = i_s; i < i_s + height; i++) { + int modify_state, cond; + uint8_t bit; + uint8_t causal_cond = i != (i_s + height - 1); + int32_t *sp = &sample_buf[j + (i * (stride - 2))]; + uint8_t mbr = 0; + + if (jpeg2000_get_state(i, j, stride - 2, HT_SHIFT_SIGMA, block_states) == 0) + jpeg2000_calc_mbr(&mbr, i, j, mbr_info & 0x1EF, causal_cond, block_states, stride - 2); + mbr_info >>= 3; + cond = mbr != 0; + bit = jpeg2000_peek_bit(sig_prop, magref_segment, magref_length); + *sp |= (bit * cond) << pLSB; + sig_prop->bits -= cond; + modify_state = (((1 << HT_SHIFT_REF_IND) | (1 << HT_SHIFT_REF)) * cond) | 1 << HT_SHIFT_SCAN; + jpeg2000_modify_state(i, j, stride - 2, modify_state, block_states); + } + } +} + +/** + * See procedure decodeSigPropMag at Rec. ITU-T T.814, 7.4. +*/ +av_noinline +static void jpeg2000_decode_sigprop_segment(Jpeg2000Cblk *cblk, uint16_t width, + uint16_t height, uint8_t *magref_segment, + uint32_t magref_length, uint8_t pLSB, + int32_t *sample_buf, uint8_t *block_states) +{ + StateVars sp_dec; + + const uint16_t num_v_stripe = height / 4; + const uint16_t num_h_stripe = width / 4; + int b_width = 4; + int b_height = 4; + int stride = width + 2; + + int last_width; + uint16_t i = 0, j = 0; + + jpeg2000_init_zero(&sp_dec); + + for (int n1 = 0; n1 < num_v_stripe; n1++) { + j = 0; + for (int n2 = 0; n2 < num_h_stripe; n2++) { + jpeg2000_process_stripes_block(&sp_dec, i, j, b_width, b_height, stride, + pLSB, sample_buf, block_states, magref_segment, + magref_length); + j += 4; + } + last_width = width % 4; + if (last_width) + jpeg2000_process_stripes_block(&sp_dec, i, j, last_width, b_height, stride, + pLSB, sample_buf, block_states, magref_segment, + magref_length); + i += 4; + } + + /* Decode remaining height stripes */ + b_height = height % 4; + j = 0; + for (int n2 = 0; n2 < num_h_stripe; n2++) { + jpeg2000_process_stripes_block(&sp_dec, i, j, b_width, b_height, stride, + pLSB, sample_buf, block_states, magref_segment, + magref_length); + j += 4; + } + last_width = width % 4; + if (last_width) + jpeg2000_process_stripes_block(&sp_dec, i, j, last_width, b_height, stride, + pLSB, sample_buf, block_states, magref_segment, + magref_length); +} + +/** + * See procedure decodeSigPropMag at Rec. ITU-T T.814, 7.5. +*/ +static int +jpeg2000_decode_magref_segment( uint16_t width, uint16_t block_height, + uint8_t *magref_segment,uint32_t magref_length, + uint8_t pLSB, int32_t *sample_buf, uint8_t *block_states) +{ + + StateVars mag_ref = { 0 }; + const uint16_t num_v_stripe = block_height / 4; + uint16_t height = 4; + uint16_t i_start = 0; + int32_t *sp; + + jpeg2000_init_mag_ref(&mag_ref, magref_length); + + for (int n1 = 0; n1 < num_v_stripe; n1++) { + for (int j = 0; j < width; j++) { + for (int i = i_start; i < i_start + height; i++) { + /** + * We move column wise, going from one quad to another. See + * Rec. ITU-T T.814, Figure 7. + */ + sp = &sample_buf[j + i * width]; + if (jpeg2000_get_state(i, j, width, HT_SHIFT_SIGMA, block_states) != 0) { + jpeg2000_modify_state(i, j, width, 1 << HT_SHIFT_REF_IND, block_states); + *sp |= jpeg2000_import_magref_bit(&mag_ref, magref_segment, magref_length) << pLSB; + } + } + } + i_start += 4; + } + height = block_height % 4; + for (int j = 0; j < width; j++) { + for (int i = i_start; i < i_start + height; i++) { + sp = &sample_buf[j + i * width]; + if (jpeg2000_get_state(i, j, width, HT_SHIFT_SIGMA, block_states) != 0) { + jpeg2000_modify_state(i, j, width, 1 << HT_SHIFT_REF_IND, block_states); + *sp |= jpeg2000_import_magref_bit(&mag_ref, magref_segment, magref_length) << pLSB; + } + } + } + return 1; +} + + +int +ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *codsty, Jpeg2000T1Context *t1, Jpeg2000Cblk *cblk, + int width, int height, int magp, uint8_t roi_shift) +{ + uint8_t p0 = 0; // Number of placeholder passes + uint32_t Lcup; // Length of HT cleanup segment + uint32_t Lref; // Length of Refinement segment + uint32_t Scup; // HT cleanup segment suffix length + uint32_t Pcup; // HT cleanup segment prefix length + + uint8_t S_blk; // Number of skipped magnitude bitplanes + uint8_t pLSB; + + uint8_t *Dcup; // Byte of an HT cleanup segment + uint8_t *Dref; // Byte of an HT refinement segment + + int z_blk; // Number of ht coding pass + + uint8_t empty_passes; + + StateVars mag_sgn; // Magnitude and Sign + StateVars mel; // Adaptive run-length coding + StateVars vlc; // Variable Length coding + StateVars sig_prop; // Significance propagation + + MelDecoderState mel_state; + + int ret; + + /* Temporary buffers */ + int32_t *sample_buf = NULL; + uint8_t *block_states = NULL; + + int32_t n, val; // Post-processing + + int32_t M_b = magp; + + /* codeblock size as constrained by Rec. ITU-T T.800, Table A.18 */ + av_assert0(width <= 1024U && height <= 1024U); + av_assert0(width * height <= 4096); + av_assert0(width * height > 0); + + if (roi_shift) + avpriv_report_missing_feature(s->avctx, "ROI shift"); + + memset(t1->data, 0, t1->stride * height * sizeof(*t1->data)); + memset(t1->flags, 0, t1->stride * (height + 2) * sizeof(*t1->flags)); + + if (cblk->npasses == 0) + return 0; + + if (cblk->npasses > 3) + p0 = 0; + else if (cblk->length == 0) + p0 = 1; + + empty_passes = p0 * 3; + z_blk = cblk->npasses - empty_passes; + + if (z_blk <= 0) + return 0; // No passes within this set, continue + + Lcup = cblk->pass_lengths[0]; + Lref = cblk->pass_lengths[1]; + + if (Lcup < 2) { + av_log(s->avctx, AV_LOG_ERROR, + "Cleanup pass length must be at least 2 bytes in length\n"); + return AVERROR_INVALIDDATA; + } + Dcup = cblk->data; + Dref = cblk->data + Lcup; // Dref comes after the refinement segment + S_blk = p0 + cblk->zbp; + pLSB = 30 - S_blk; + + Scup = (Dcup[Lcup - 1] << 4) + (Dcup[Lcup - 2] & 0x0F); + + if (Scup < 2 || Scup > Lcup || Scup > 4079) { + av_log(s->avctx, AV_LOG_ERROR, "Cleanup pass suffix length is invalid %d\n", + Scup); + ret = AVERROR_INVALIDDATA; + goto free; + } + Pcup = Lcup - Scup; + + /* modDcup shall be done before the creation of vlc instance. */ + Dcup[Lcup - 1] = 0xFF; + Dcup[Lcup - 2] |= 0x0F; + + /* Magnitude and refinement */ + jpeg2000_init_zero(&mag_sgn); + jpeg2000_bitbuf_refill_forward(&mag_sgn, Dcup, Pcup); + + /* Significance propagation */ + jpeg2000_init_zero(&sig_prop); + + /* Adaptive run length */ + jpeg2000_init_mel(&mel, Pcup); + + /* Variable Length coding */ + jpeg2000_init_vlc(&vlc, Lcup, Pcup, Dcup); + + jpeg2000_init_mel_decoder(&mel_state); + + sample_buf = av_calloc((width + 4) * (height + 4), sizeof(int32_t)); + block_states = av_calloc((width + 4) * (height + 4), sizeof(uint8_t)); + + if (!sample_buf || !block_states) { + ret = AVERROR(ENOMEM); + goto free; + } + if ((ret = jpeg2000_decode_ht_cleanup_segment(s, cblk, t1, &mel_state, &mel, &vlc, + &mag_sgn, Dcup, Lcup, Pcup, pLSB, width, + height, sample_buf, block_states)) < 0) { + av_log(s->avctx, AV_LOG_ERROR, "Bad HT cleanup segment\n"); + goto free; + } + + if (cblk->npasses > 1) + jpeg2000_decode_sigprop_segment(cblk, width, height, Dref, Lref, + pLSB - 1, sample_buf, block_states); + + if (cblk->npasses > 2) { + + if (Lref < 2){ + av_log(s->avctx,AV_LOG_ERROR,"Invalid magnitude refinement length\n"); + ret = AVERROR_INVALIDDATA; + goto free; + } + if ((ret = jpeg2000_decode_magref_segment(width, height, Dref, Lref, + pLSB - 1, sample_buf, block_states)) < 0) + goto free; + } + + pLSB = 31 - M_b; + + /* Reconstruct the sample values */ + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + n = x + (y * t1->stride); + val = sample_buf[x + (y * width)]; + /* Convert sign-magnitude to two's complement. */ + val = val >> 31 ? 0x80000000 - val : val; + val >>= (pLSB - 1); + t1->data[n] = val; + } + } +free: + av_freep(&sample_buf); + av_freep(&block_states); + return ret; +} + +/** + * CtxVLC tables (see Rec. ITU-T T.800, Annex C) as found at + * https://github.com/osamu620/OpenHTJ2K (author: Osamu Watanabe) + */ +static const uint16_t dec_cxt_vlc_table1[1024] = { + 0x0016, 0x006A, 0x0046, 0x00DD, 0x0086, 0x888B, 0x0026, 0x444D, 0x0016, 0x00AA, 0x0046, 0x88AD, 0x0086, + 0x003A, 0x0026, 0x00DE, 0x0016, 0x00CA, 0x0046, 0x009D, 0x0086, 0x005A, 0x0026, 0x222D, 0x0016, 0x009A, + 0x0046, 0x007D, 0x0086, 0x01FD, 0x0026, 0x007E, 0x0016, 0x006A, 0x0046, 0x88CD, 0x0086, 0x888B, 0x0026, + 0x111D, 0x0016, 0x00AA, 0x0046, 0x005D, 0x0086, 0x003A, 0x0026, 0x00EE, 0x0016, 0x00CA, 0x0046, 0x00BD, + 0x0086, 0x005A, 0x0026, 0x11FF, 0x0016, 0x009A, 0x0046, 0x003D, 0x0086, 0x04ED, 0x0026, 0x2AAF, 0x0016, + 0x006A, 0x0046, 0x00DD, 0x0086, 0x888B, 0x0026, 0x444D, 0x0016, 0x00AA, 0x0046, 0x88AD, 0x0086, 0x003A, + 0x0026, 0x44EF, 0x0016, 0x00CA, 0x0046, 0x009D, 0x0086, 0x005A, 0x0026, 0x222D, 0x0016, 0x009A, 0x0046, + 0x007D, 0x0086, 0x01FD, 0x0026, 0x00BE, 0x0016, 0x006A, 0x0046, 0x88CD, 0x0086, 0x888B, 0x0026, 0x111D, + 0x0016, 0x00AA, 0x0046, 0x005D, 0x0086, 0x003A, 0x0026, 0x4CCF, 0x0016, 0x00CA, 0x0046, 0x00BD, 0x0086, + 0x005A, 0x0026, 0x00FE, 0x0016, 0x009A, 0x0046, 0x003D, 0x0086, 0x04ED, 0x0026, 0x006F, 0x0002, 0x0088, + 0x0002, 0x005C, 0x0002, 0x0018, 0x0002, 0x00DE, 0x0002, 0x0028, 0x0002, 0x009C, 0x0002, 0x004A, 0x0002, + 0x007E, 0x0002, 0x0088, 0x0002, 0x00CC, 0x0002, 0x0018, 0x0002, 0x888F, 0x0002, 0x0028, 0x0002, 0x00FE, + 0x0002, 0x003A, 0x0002, 0x222F, 0x0002, 0x0088, 0x0002, 0x04FD, 0x0002, 0x0018, 0x0002, 0x00BE, 0x0002, + 0x0028, 0x0002, 0x00BF, 0x0002, 0x004A, 0x0002, 0x006E, 0x0002, 0x0088, 0x0002, 0x00AC, 0x0002, 0x0018, + 0x0002, 0x444F, 0x0002, 0x0028, 0x0002, 0x00EE, 0x0002, 0x003A, 0x0002, 0x113F, 0x0002, 0x0088, 0x0002, + 0x005C, 0x0002, 0x0018, 0x0002, 0x00CF, 0x0002, 0x0028, 0x0002, 0x009C, 0x0002, 0x004A, 0x0002, 0x006F, + 0x0002, 0x0088, 0x0002, 0x00CC, 0x0002, 0x0018, 0x0002, 0x009F, 0x0002, 0x0028, 0x0002, 0x00EF, 0x0002, + 0x003A, 0x0002, 0x233F, 0x0002, 0x0088, 0x0002, 0x04FD, 0x0002, 0x0018, 0x0002, 0x00AF, 0x0002, 0x0028, + 0x0002, 0x44FF, 0x0002, 0x004A, 0x0002, 0x005F, 0x0002, 0x0088, 0x0002, 0x00AC, 0x0002, 0x0018, 0x0002, + 0x007F, 0x0002, 0x0028, 0x0002, 0x00DF, 0x0002, 0x003A, 0x0002, 0x111F, 0x0002, 0x0028, 0x0002, 0x005C, + 0x0002, 0x008A, 0x0002, 0x00BF, 0x0002, 0x0018, 0x0002, 0x00FE, 0x0002, 0x00CC, 0x0002, 0x007E, 0x0002, + 0x0028, 0x0002, 0x8FFF, 0x0002, 0x004A, 0x0002, 0x007F, 0x0002, 0x0018, 0x0002, 0x00DF, 0x0002, 0x00AC, + 0x0002, 0x133F, 0x0002, 0x0028, 0x0002, 0x222D, 0x0002, 0x008A, 0x0002, 0x00BE, 0x0002, 0x0018, 0x0002, + 0x44EF, 0x0002, 0x2AAD, 0x0002, 0x006E, 0x0002, 0x0028, 0x0002, 0x15FF, 0x0002, 0x004A, 0x0002, 0x009E, + 0x0002, 0x0018, 0x0002, 0x00CF, 0x0002, 0x003C, 0x0002, 0x223F, 0x0002, 0x0028, 0x0002, 0x005C, 0x0002, + 0x008A, 0x0002, 0x2BBF, 0x0002, 0x0018, 0x0002, 0x04EF, 0x0002, 0x00CC, 0x0002, 0x006F, 0x0002, 0x0028, + 0x0002, 0x27FF, 0x0002, 0x004A, 0x0002, 0x009F, 0x0002, 0x0018, 0x0002, 0x00DE, 0x0002, 0x00AC, 0x0002, + 0x444F, 0x0002, 0x0028, 0x0002, 0x222D, 0x0002, 0x008A, 0x0002, 0x8AAF, 0x0002, 0x0018, 0x0002, 0x00EE, + 0x0002, 0x2AAD, 0x0002, 0x005F, 0x0002, 0x0028, 0x0002, 0x44FF, 0x0002, 0x004A, 0x0002, 0x888F, 0x0002, + 0x0018, 0x0002, 0xAAAF, 0x0002, 0x003C, 0x0002, 0x111F, 0x0004, 0x8FFD, 0x0028, 0x005C, 0x0004, 0x00BC, + 0x008A, 0x66FF, 0x0004, 0x00CD, 0x0018, 0x111D, 0x0004, 0x009C, 0x003A, 0x8AAF, 0x0004, 0x00FC, 0x0028, + 0x133D, 0x0004, 0x00AC, 0x004A, 0x3BBF, 0x0004, 0x2BBD, 0x0018, 0x5FFF, 0x0004, 0x006C, 0x157D, 0x455F, + 0x0004, 0x2FFD, 0x0028, 0x222D, 0x0004, 0x22AD, 0x008A, 0x44EF, 0x0004, 0x00CC, 0x0018, 0x4FFF, 0x0004, + 0x007C, 0x003A, 0x447F, 0x0004, 0x04DD, 0x0028, 0x233D, 0x0004, 0x009D, 0x004A, 0x00DE, 0x0004, 0x88BD, + 0x0018, 0xAFFF, 0x0004, 0x115D, 0x1FFD, 0x444F, 0x0004, 0x8FFD, 0x0028, 0x005C, 0x0004, 0x00BC, 0x008A, + 0x8CEF, 0x0004, 0x00CD, 0x0018, 0x111D, 0x0004, 0x009C, 0x003A, 0x888F, 0x0004, 0x00FC, 0x0028, 0x133D, + 0x0004, 0x00AC, 0x004A, 0x44DF, 0x0004, 0x2BBD, 0x0018, 0x8AFF, 0x0004, 0x006C, 0x157D, 0x006F, 0x0004, + 0x2FFD, 0x0028, 0x222D, 0x0004, 0x22AD, 0x008A, 0x00EE, 0x0004, 0x00CC, 0x0018, 0x2EEF, 0x0004, 0x007C, + 0x003A, 0x277F, 0x0004, 0x04DD, 0x0028, 0x233D, 0x0004, 0x009D, 0x004A, 0x1BBF, 0x0004, 0x88BD, 0x0018, + 0x37FF, 0x0004, 0x115D, 0x1FFD, 0x333F, 0x0002, 0x0088, 0x0002, 0x02ED, 0x0002, 0x00CA, 0x0002, 0x4CCF, + 0x0002, 0x0048, 0x0002, 0x23FF, 0x0002, 0x001A, 0x0002, 0x888F, 0x0002, 0x0088, 0x0002, 0x006C, 0x0002, + 0x002A, 0x0002, 0x00AF, 0x0002, 0x0048, 0x0002, 0x22EF, 0x0002, 0x00AC, 0x0002, 0x005F, 0x0002, 0x0088, + 0x0002, 0x444D, 0x0002, 0x00CA, 0x0002, 0xCCCF, 0x0002, 0x0048, 0x0002, 0x00FE, 0x0002, 0x001A, 0x0002, + 0x006F, 0x0002, 0x0088, 0x0002, 0x005C, 0x0002, 0x002A, 0x0002, 0x009F, 0x0002, 0x0048, 0x0002, 0x00DF, + 0x0002, 0x03FD, 0x0002, 0x222F, 0x0002, 0x0088, 0x0002, 0x02ED, 0x0002, 0x00CA, 0x0002, 0x8CCF, 0x0002, + 0x0048, 0x0002, 0x11FF, 0x0002, 0x001A, 0x0002, 0x007E, 0x0002, 0x0088, 0x0002, 0x006C, 0x0002, 0x002A, + 0x0002, 0x007F, 0x0002, 0x0048, 0x0002, 0x00EE, 0x0002, 0x00AC, 0x0002, 0x003E, 0x0002, 0x0088, 0x0002, + 0x444D, 0x0002, 0x00CA, 0x0002, 0x00BE, 0x0002, 0x0048, 0x0002, 0x00BF, 0x0002, 0x001A, 0x0002, 0x003F, + 0x0002, 0x0088, 0x0002, 0x005C, 0x0002, 0x002A, 0x0002, 0x009E, 0x0002, 0x0048, 0x0002, 0x00DE, 0x0002, + 0x03FD, 0x0002, 0x111F, 0x0004, 0x8AED, 0x0048, 0x888D, 0x0004, 0x00DC, 0x00CA, 0x3FFF, 0x0004, 0xCFFD, + 0x002A, 0x003D, 0x0004, 0x00BC, 0x005A, 0x8DDF, 0x0004, 0x8FFD, 0x0048, 0x006C, 0x0004, 0x027D, 0x008A, + 0x99FF, 0x0004, 0x00EC, 0x00FA, 0x003C, 0x0004, 0x00AC, 0x001A, 0x009F, 0x0004, 0x2FFD, 0x0048, 0x007C, + 0x0004, 0x44CD, 0x00CA, 0x67FF, 0x0004, 0x1FFD, 0x002A, 0x444D, 0x0004, 0x00AD, 0x005A, 0x8CCF, 0x0004, + 0x4FFD, 0x0048, 0x445D, 0x0004, 0x01BD, 0x008A, 0x4EEF, 0x0004, 0x45DD, 0x00FA, 0x111D, 0x0004, 0x009C, + 0x001A, 0x222F, 0x0004, 0x8AED, 0x0048, 0x888D, 0x0004, 0x00DC, 0x00CA, 0xAFFF, 0x0004, 0xCFFD, 0x002A, + 0x003D, 0x0004, 0x00BC, 0x005A, 0x11BF, 0x0004, 0x8FFD, 0x0048, 0x006C, 0x0004, 0x027D, 0x008A, 0x22EF, + 0x0004, 0x00EC, 0x00FA, 0x003C, 0x0004, 0x00AC, 0x001A, 0x227F, 0x0004, 0x2FFD, 0x0048, 0x007C, 0x0004, + 0x44CD, 0x00CA, 0x5DFF, 0x0004, 0x1FFD, 0x002A, 0x444D, 0x0004, 0x00AD, 0x005A, 0x006F, 0x0004, 0x4FFD, + 0x0048, 0x445D, 0x0004, 0x01BD, 0x008A, 0x11DF, 0x0004, 0x45DD, 0x00FA, 0x111D, 0x0004, 0x009C, 0x001A, + 0x155F, 0x0006, 0x00FC, 0x0018, 0x111D, 0x0048, 0x888D, 0x00AA, 0x4DDF, 0x0006, 0x2AAD, 0x005A, 0x67FF, + 0x0028, 0x223D, 0x00BC, 0xAAAF, 0x0006, 0x00EC, 0x0018, 0x5FFF, 0x0048, 0x006C, 0x008A, 0xCCCF, 0x0006, + 0x009D, 0x00CA, 0x44EF, 0x0028, 0x003C, 0x8FFD, 0x137F, 0x0006, 0x8EED, 0x0018, 0x1FFF, 0x0048, 0x007C, + 0x00AA, 0x4CCF, 0x0006, 0x227D, 0x005A, 0x1DDF, 0x0028, 0x444D, 0x4FFD, 0x155F, 0x0006, 0x00DC, 0x0018, + 0x2EEF, 0x0048, 0x445D, 0x008A, 0x22BF, 0x0006, 0x009C, 0x00CA, 0x8CDF, 0x0028, 0x222D, 0x2FFD, 0x226F, + 0x0006, 0x00FC, 0x0018, 0x111D, 0x0048, 0x888D, 0x00AA, 0x1BBF, 0x0006, 0x2AAD, 0x005A, 0x33FF, 0x0028, + 0x223D, 0x00BC, 0x8AAF, 0x0006, 0x00EC, 0x0018, 0x9BFF, 0x0048, 0x006C, 0x008A, 0x8ABF, 0x0006, 0x009D, + 0x00CA, 0x4EEF, 0x0028, 0x003C, 0x8FFD, 0x466F, 0x0006, 0x8EED, 0x0018, 0xCFFF, 0x0048, 0x007C, 0x00AA, + 0x8CCF, 0x0006, 0x227D, 0x005A, 0xAEEF, 0x0028, 0x444D, 0x4FFD, 0x477F, 0x0006, 0x00DC, 0x0018, 0xAFFF, + 0x0048, 0x445D, 0x008A, 0x2BBF, 0x0006, 0x009C, 0x00CA, 0x44DF, 0x0028, 0x222D, 0x2FFD, 0x133F, 0x00F6, + 0xAFFD, 0x1FFB, 0x003C, 0x0008, 0x23BD, 0x007A, 0x11DF, 0x00F6, 0x45DD, 0x2FFB, 0x4EEF, 0x00DA, 0x177D, + 0xCFFD, 0x377F, 0x00F6, 0x3FFD, 0x8FFB, 0x111D, 0x0008, 0x009C, 0x005A, 0x1BBF, 0x00F6, 0x00CD, 0x00BA, + 0x8DDF, 0x4FFB, 0x006C, 0x9BFD, 0x455F, 0x00F6, 0x67FD, 0x1FFB, 0x002C, 0x0008, 0x00AC, 0x007A, 0x009F, + 0x00F6, 0x00AD, 0x2FFB, 0x7FFF, 0x00DA, 0x004C, 0x5FFD, 0x477F, 0x00F6, 0x00EC, 0x8FFB, 0x001C, 0x0008, + 0x008C, 0x005A, 0x888F, 0x00F6, 0x00CC, 0x00BA, 0x2EEF, 0x4FFB, 0x115D, 0x8AED, 0x113F, 0x00F6, 0xAFFD, + 0x1FFB, 0x003C, 0x0008, 0x23BD, 0x007A, 0x1DDF, 0x00F6, 0x45DD, 0x2FFB, 0xBFFF, 0x00DA, 0x177D, 0xCFFD, + 0x447F, 0x00F6, 0x3FFD, 0x8FFB, 0x111D, 0x0008, 0x009C, 0x005A, 0x277F, 0x00F6, 0x00CD, 0x00BA, 0x22EF, + 0x4FFB, 0x006C, 0x9BFD, 0x444F, 0x00F6, 0x67FD, 0x1FFB, 0x002C, 0x0008, 0x00AC, 0x007A, 0x11BF, 0x00F6, + 0x00AD, 0x2FFB, 0xFFFF, 0x00DA, 0x004C, 0x5FFD, 0x233F, 0x00F6, 0x00EC, 0x8FFB, 0x001C, 0x0008, 0x008C, + 0x005A, 0x006F, 0x00F6, 0x00CC, 0x00BA, 0x8BBF, 0x4FFB, 0x115D, 0x8AED, 0x222F}; + +static const uint16_t dec_cxt_vlc_table0[1024] = { + 0x0026, 0x00AA, 0x0046, 0x006C, 0x0086, 0x8AED, 0x0018, 0x8DDF, 0x0026, 0x01BD, 0x0046, 0x5FFF, 0x0086, + 0x027D, 0x005A, 0x155F, 0x0026, 0x003A, 0x0046, 0x444D, 0x0086, 0x4CCD, 0x0018, 0xCCCF, 0x0026, 0x2EFD, + 0x0046, 0x99FF, 0x0086, 0x009C, 0x00CA, 0x133F, 0x0026, 0x00AA, 0x0046, 0x445D, 0x0086, 0x8CCD, 0x0018, + 0x11DF, 0x0026, 0x4FFD, 0x0046, 0xCFFF, 0x0086, 0x009D, 0x005A, 0x007E, 0x0026, 0x003A, 0x0046, 0x1FFF, + 0x0086, 0x88AD, 0x0018, 0x00BE, 0x0026, 0x8FFD, 0x0046, 0x4EEF, 0x0086, 0x888D, 0x00CA, 0x111F, 0x0026, + 0x00AA, 0x0046, 0x006C, 0x0086, 0x8AED, 0x0018, 0x45DF, 0x0026, 0x01BD, 0x0046, 0x22EF, 0x0086, 0x027D, + 0x005A, 0x227F, 0x0026, 0x003A, 0x0046, 0x444D, 0x0086, 0x4CCD, 0x0018, 0x11BF, 0x0026, 0x2EFD, 0x0046, + 0x00FE, 0x0086, 0x009C, 0x00CA, 0x223F, 0x0026, 0x00AA, 0x0046, 0x445D, 0x0086, 0x8CCD, 0x0018, 0x00DE, + 0x0026, 0x4FFD, 0x0046, 0xABFF, 0x0086, 0x009D, 0x005A, 0x006F, 0x0026, 0x003A, 0x0046, 0x6EFF, 0x0086, + 0x88AD, 0x0018, 0x2AAF, 0x0026, 0x8FFD, 0x0046, 0x00EE, 0x0086, 0x888D, 0x00CA, 0x222F, 0x0004, 0x00CA, + 0x0088, 0x027D, 0x0004, 0x4CCD, 0x0028, 0x00FE, 0x0004, 0x2AFD, 0x0048, 0x005C, 0x0004, 0x009D, 0x0018, + 0x00DE, 0x0004, 0x01BD, 0x0088, 0x006C, 0x0004, 0x88AD, 0x0028, 0x11DF, 0x0004, 0x8AED, 0x0048, 0x003C, + 0x0004, 0x888D, 0x0018, 0x111F, 0x0004, 0x00CA, 0x0088, 0x006D, 0x0004, 0x88CD, 0x0028, 0x88FF, 0x0004, + 0x8BFD, 0x0048, 0x444D, 0x0004, 0x009C, 0x0018, 0x00BE, 0x0004, 0x4EFD, 0x0088, 0x445D, 0x0004, 0x00AC, + 0x0028, 0x00EE, 0x0004, 0x45DD, 0x0048, 0x222D, 0x0004, 0x003D, 0x0018, 0x007E, 0x0004, 0x00CA, 0x0088, + 0x027D, 0x0004, 0x4CCD, 0x0028, 0x1FFF, 0x0004, 0x2AFD, 0x0048, 0x005C, 0x0004, 0x009D, 0x0018, 0x11BF, + 0x0004, 0x01BD, 0x0088, 0x006C, 0x0004, 0x88AD, 0x0028, 0x22EF, 0x0004, 0x8AED, 0x0048, 0x003C, 0x0004, + 0x888D, 0x0018, 0x227F, 0x0004, 0x00CA, 0x0088, 0x006D, 0x0004, 0x88CD, 0x0028, 0x4EEF, 0x0004, 0x8BFD, + 0x0048, 0x444D, 0x0004, 0x009C, 0x0018, 0x2AAF, 0x0004, 0x4EFD, 0x0088, 0x445D, 0x0004, 0x00AC, 0x0028, + 0x8DDF, 0x0004, 0x45DD, 0x0048, 0x222D, 0x0004, 0x003D, 0x0018, 0x155F, 0x0004, 0x005A, 0x0088, 0x006C, + 0x0004, 0x88DD, 0x0028, 0x23FF, 0x0004, 0x11FD, 0x0048, 0x444D, 0x0004, 0x00AD, 0x0018, 0x00BE, 0x0004, + 0x137D, 0x0088, 0x155D, 0x0004, 0x00CC, 0x0028, 0x00DE, 0x0004, 0x02ED, 0x0048, 0x111D, 0x0004, 0x009D, + 0x0018, 0x007E, 0x0004, 0x005A, 0x0088, 0x455D, 0x0004, 0x44CD, 0x0028, 0x00EE, 0x0004, 0x1FFD, 0x0048, + 0x003C, 0x0004, 0x00AC, 0x0018, 0x555F, 0x0004, 0x47FD, 0x0088, 0x113D, 0x0004, 0x02BD, 0x0028, 0x477F, + 0x0004, 0x4CDD, 0x0048, 0x8FFF, 0x0004, 0x009C, 0x0018, 0x222F, 0x0004, 0x005A, 0x0088, 0x006C, 0x0004, + 0x88DD, 0x0028, 0x00FE, 0x0004, 0x11FD, 0x0048, 0x444D, 0x0004, 0x00AD, 0x0018, 0x888F, 0x0004, 0x137D, + 0x0088, 0x155D, 0x0004, 0x00CC, 0x0028, 0x8CCF, 0x0004, 0x02ED, 0x0048, 0x111D, 0x0004, 0x009D, 0x0018, + 0x006F, 0x0004, 0x005A, 0x0088, 0x455D, 0x0004, 0x44CD, 0x0028, 0x1DDF, 0x0004, 0x1FFD, 0x0048, 0x003C, + 0x0004, 0x00AC, 0x0018, 0x227F, 0x0004, 0x47FD, 0x0088, 0x113D, 0x0004, 0x02BD, 0x0028, 0x22BF, 0x0004, + 0x4CDD, 0x0048, 0x22EF, 0x0004, 0x009C, 0x0018, 0x233F, 0x0006, 0x4DDD, 0x4FFB, 0xCFFF, 0x0018, 0x113D, + 0x005A, 0x888F, 0x0006, 0x23BD, 0x008A, 0x00EE, 0x002A, 0x155D, 0xAAFD, 0x277F, 0x0006, 0x44CD, 0x8FFB, + 0x44EF, 0x0018, 0x467D, 0x004A, 0x2AAF, 0x0006, 0x00AC, 0x555B, 0x99DF, 0x1FFB, 0x003C, 0x5FFD, 0x266F, + 0x0006, 0x1DDD, 0x4FFB, 0x6EFF, 0x0018, 0x177D, 0x005A, 0x1BBF, 0x0006, 0x88AD, 0x008A, 0x5DDF, 0x002A, + 0x444D, 0x2FFD, 0x667F, 0x0006, 0x00CC, 0x8FFB, 0x2EEF, 0x0018, 0x455D, 0x004A, 0x119F, 0x0006, 0x009C, + 0x555B, 0x8CCF, 0x1FFB, 0x111D, 0x8CED, 0x006E, 0x0006, 0x4DDD, 0x4FFB, 0x3FFF, 0x0018, 0x113D, 0x005A, + 0x11BF, 0x0006, 0x23BD, 0x008A, 0x8DDF, 0x002A, 0x155D, 0xAAFD, 0x222F, 0x0006, 0x44CD, 0x8FFB, 0x00FE, + 0x0018, 0x467D, 0x004A, 0x899F, 0x0006, 0x00AC, 0x555B, 0x00DE, 0x1FFB, 0x003C, 0x5FFD, 0x446F, 0x0006, + 0x1DDD, 0x4FFB, 0x9BFF, 0x0018, 0x177D, 0x005A, 0x00BE, 0x0006, 0x88AD, 0x008A, 0xCDDF, 0x002A, 0x444D, + 0x2FFD, 0x007E, 0x0006, 0x00CC, 0x8FFB, 0x4EEF, 0x0018, 0x455D, 0x004A, 0x377F, 0x0006, 0x009C, 0x555B, + 0x8BBF, 0x1FFB, 0x111D, 0x8CED, 0x233F, 0x0004, 0x00AA, 0x0088, 0x047D, 0x0004, 0x01DD, 0x0028, 0x11DF, + 0x0004, 0x27FD, 0x0048, 0x005C, 0x0004, 0x8AAD, 0x0018, 0x2BBF, 0x0004, 0x009C, 0x0088, 0x006C, 0x0004, + 0x00CC, 0x0028, 0x00EE, 0x0004, 0x8CED, 0x0048, 0x222D, 0x0004, 0x888D, 0x0018, 0x007E, 0x0004, 0x00AA, + 0x0088, 0x006D, 0x0004, 0x88CD, 0x0028, 0x00FE, 0x0004, 0x19FD, 0x0048, 0x003C, 0x0004, 0x2AAD, 0x0018, + 0xAAAF, 0x0004, 0x8BFD, 0x0088, 0x005D, 0x0004, 0x00BD, 0x0028, 0x4CCF, 0x0004, 0x44ED, 0x0048, 0x4FFF, + 0x0004, 0x223D, 0x0018, 0x111F, 0x0004, 0x00AA, 0x0088, 0x047D, 0x0004, 0x01DD, 0x0028, 0x99FF, 0x0004, + 0x27FD, 0x0048, 0x005C, 0x0004, 0x8AAD, 0x0018, 0x00BE, 0x0004, 0x009C, 0x0088, 0x006C, 0x0004, 0x00CC, + 0x0028, 0x00DE, 0x0004, 0x8CED, 0x0048, 0x222D, 0x0004, 0x888D, 0x0018, 0x444F, 0x0004, 0x00AA, 0x0088, + 0x006D, 0x0004, 0x88CD, 0x0028, 0x2EEF, 0x0004, 0x19FD, 0x0048, 0x003C, 0x0004, 0x2AAD, 0x0018, 0x447F, + 0x0004, 0x8BFD, 0x0088, 0x005D, 0x0004, 0x00BD, 0x0028, 0x009F, 0x0004, 0x44ED, 0x0048, 0x67FF, 0x0004, + 0x223D, 0x0018, 0x133F, 0x0006, 0x00CC, 0x008A, 0x9DFF, 0x2FFB, 0x467D, 0x1FFD, 0x99BF, 0x0006, 0x2AAD, + 0x002A, 0x66EF, 0x4FFB, 0x005C, 0x2EED, 0x377F, 0x0006, 0x89BD, 0x004A, 0x00FE, 0x8FFB, 0x006C, 0x67FD, + 0x889F, 0x0006, 0x888D, 0x001A, 0x5DDF, 0x00AA, 0x222D, 0x89DD, 0x444F, 0x0006, 0x2BBD, 0x008A, 0xCFFF, + 0x2FFB, 0x226D, 0x009C, 0x00BE, 0x0006, 0xAAAD, 0x002A, 0x1DDF, 0x4FFB, 0x003C, 0x4DDD, 0x466F, 0x0006, + 0x8AAD, 0x004A, 0xAEEF, 0x8FFB, 0x445D, 0x8EED, 0x177F, 0x0006, 0x233D, 0x001A, 0x4CCF, 0x00AA, 0xAFFF, + 0x88CD, 0x133F, 0x0006, 0x00CC, 0x008A, 0x77FF, 0x2FFB, 0x467D, 0x1FFD, 0x3BBF, 0x0006, 0x2AAD, 0x002A, + 0x00EE, 0x4FFB, 0x005C, 0x2EED, 0x007E, 0x0006, 0x89BD, 0x004A, 0x4EEF, 0x8FFB, 0x006C, 0x67FD, 0x667F, + 0x0006, 0x888D, 0x001A, 0x00DE, 0x00AA, 0x222D, 0x89DD, 0x333F, 0x0006, 0x2BBD, 0x008A, 0x57FF, 0x2FFB, + 0x226D, 0x009C, 0x199F, 0x0006, 0xAAAD, 0x002A, 0x99DF, 0x4FFB, 0x003C, 0x4DDD, 0x155F, 0x0006, 0x8AAD, + 0x004A, 0xCEEF, 0x8FFB, 0x445D, 0x8EED, 0x277F, 0x0006, 0x233D, 0x001A, 0x1BBF, 0x00AA, 0x3FFF, 0x88CD, + 0x111F, 0x0006, 0x45DD, 0x2FFB, 0x111D, 0x0018, 0x467D, 0x8FFD, 0xCCCF, 0x0006, 0x19BD, 0x004A, 0x22EF, + 0x002A, 0x222D, 0x3FFD, 0x888F, 0x0006, 0x00CC, 0x008A, 0x00FE, 0x0018, 0x115D, 0xCFFD, 0x8AAF, 0x0006, + 0x00AC, 0x003A, 0x8CDF, 0x1FFB, 0x133D, 0x66FD, 0x466F, 0x0006, 0x8CCD, 0x2FFB, 0x5FFF, 0x0018, 0x006C, + 0x4FFD, 0xABBF, 0x0006, 0x22AD, 0x004A, 0x00EE, 0x002A, 0x233D, 0xAEFD, 0x377F, 0x0006, 0x2BBD, 0x008A, + 0x55DF, 0x0018, 0x005C, 0x177D, 0x119F, 0x0006, 0x009C, 0x003A, 0x4CCF, 0x1FFB, 0x333D, 0x8EED, 0x444F, + 0x0006, 0x45DD, 0x2FFB, 0x111D, 0x0018, 0x467D, 0x8FFD, 0x99BF, 0x0006, 0x19BD, 0x004A, 0x2EEF, 0x002A, + 0x222D, 0x3FFD, 0x667F, 0x0006, 0x00CC, 0x008A, 0x4EEF, 0x0018, 0x115D, 0xCFFD, 0x899F, 0x0006, 0x00AC, + 0x003A, 0x00DE, 0x1FFB, 0x133D, 0x66FD, 0x226F, 0x0006, 0x8CCD, 0x2FFB, 0x9BFF, 0x0018, 0x006C, 0x4FFD, + 0x00BE, 0x0006, 0x22AD, 0x004A, 0x1DDF, 0x002A, 0x233D, 0xAEFD, 0x007E, 0x0006, 0x2BBD, 0x008A, 0xCEEF, + 0x0018, 0x005C, 0x177D, 0x277F, 0x0006, 0x009C, 0x003A, 0x8BBF, 0x1FFB, 0x333D, 0x8EED, 0x455F, 0x1FF9, + 0x1DDD, 0xAFFB, 0x00DE, 0x8FF9, 0x001C, 0xFFFB, 0x477F, 0x4FF9, 0x177D, 0x3FFB, 0x3BBF, 0x2FF9, 0xAEEF, + 0x8EED, 0x444F, 0x1FF9, 0x22AD, 0x000A, 0x8BBF, 0x8FF9, 0x00FE, 0xCFFD, 0x007E, 0x4FF9, 0x115D, 0x5FFB, + 0x577F, 0x2FF9, 0x8DDF, 0x2EED, 0x333F, 0x1FF9, 0x2BBD, 0xAFFB, 0x88CF, 0x8FF9, 0xBFFF, 0xFFFB, 0x377F, + 0x4FF9, 0x006D, 0x3FFB, 0x00BE, 0x2FF9, 0x66EF, 0x9FFD, 0x133F, 0x1FF9, 0x009D, 0x000A, 0xABBF, 0x8FF9, + 0xDFFF, 0x6FFD, 0x006E, 0x4FF9, 0x002C, 0x5FFB, 0x888F, 0x2FF9, 0xCDDF, 0x4DDD, 0x222F, 0x1FF9, 0x1DDD, + 0xAFFB, 0x4CCF, 0x8FF9, 0x001C, 0xFFFB, 0x277F, 0x4FF9, 0x177D, 0x3FFB, 0x99BF, 0x2FF9, 0xCEEF, 0x8EED, + 0x004E, 0x1FF9, 0x22AD, 0x000A, 0x00AE, 0x8FF9, 0x7FFF, 0xCFFD, 0x005E, 0x4FF9, 0x115D, 0x5FFB, 0x009E, + 0x2FF9, 0x5DDF, 0x2EED, 0x003E, 0x1FF9, 0x2BBD, 0xAFFB, 0x00CE, 0x8FF9, 0xEFFF, 0xFFFB, 0x667F, 0x4FF9, + 0x006D, 0x3FFB, 0x8AAF, 0x2FF9, 0x00EE, 0x9FFD, 0x233F, 0x1FF9, 0x009D, 0x000A, 0x1BBF, 0x8FF9, 0x4EEF, + 0x6FFD, 0x455F, 0x4FF9, 0x002C, 0x5FFB, 0x008E, 0x2FF9, 0x99DF, 0x4DDD, 0x111F}; diff --git a/libavcodec/jpeg2000htdec.h b/libavcodec/jpeg2000htdec.h new file mode 100644 index 00000000000..572d095c927 --- /dev/null +++ b/libavcodec/jpeg2000htdec.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 Caleb Etemesi + * + * 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 + */ + +#ifndef AVCODEC_JPEG2000HTDEC_H +#define AVCODEC_JPEG2000HTDEC_H + +#include "jpeg2000dec.h" + +/** + * HT Block decoder as specified in Rec. ITU-T T.814 | ISO/IEC 15444-15 + */ + +int ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *codsty, + Jpeg2000T1Context *t1, Jpeg2000Cblk *cblk, int width, + int height, int magp, uint8_t roi_shift); + +#endif /* AVCODEC_JPEG2000HTDEC_H */ diff --git a/libavcodec/jpegls.c b/libavcodec/jpegls.c index cc598f3c177..7b4bc30e466 100644 --- a/libavcodec/jpegls.c +++ b/libavcodec/jpegls.c @@ -28,6 +28,7 @@ #include #include "libavutil/internal.h" #include "libavutil/intmath.h" +#include "libavutil/log.h" #include "jpegls.h" void ff_jpegls_init_state(JLSState *state) diff --git a/libavcodec/jpeglsdec.c b/libavcodec/jpeglsdec.c index ec163b8964d..c245cf0279c 100644 --- a/libavcodec/jpeglsdec.c +++ b/libavcodec/jpeglsdec.c @@ -382,6 +382,19 @@ int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near, state->T3 = s->t3; state->reset = s->reset; ff_jpegls_reset_coding_parameters(state, 0); + + /* Testing parameters here, we cannot test in LSE or SOF because + * these interdepend and are allowed in either order + */ + if (state->maxval >= (1<bpp) || + state->T1 > state->T2 || + state->T2 > state->T3 || + state->T3 > state->maxval || + state->reset > FFMAX(255, state->maxval)) { + ret = AVERROR_INVALIDDATA; + goto end; + } + ff_jpegls_init_state(state); if (s->bits <= 8) diff --git a/libavcodec/jpeglsenc.c b/libavcodec/jpeglsenc.c index 53394102df3..3481c61ee31 100644 --- a/libavcodec/jpeglsenc.c +++ b/libavcodec/jpeglsenc.c @@ -432,7 +432,7 @@ static av_cold int encode_jpegls_init(AVCodecContext *avctx) ctx->comps = 1; else ctx->comps = 3; - size = AV_INPUT_BUFFER_MIN_SIZE; + size = FF_INPUT_BUFFER_MIN_SIZE; /* INT_MAX due to PutBit-API. */ if (avctx->width * (unsigned)avctx->height > (INT_MAX - size) / 4 / ctx->comps) return AVERROR(ERANGE); @@ -456,10 +456,10 @@ static av_cold int encode_jpegls_close(AVCodecContext *avctx) #define OFFSET(x) offsetof(JPEGLSContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { -{ "pred", "Prediction method", OFFSET(pred), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, VE, "pred" }, - { "left", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "pred" }, - { "plane", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "pred" }, - { "median", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, INT_MIN, INT_MAX, VE, "pred" }, +{ "pred", "Prediction method", OFFSET(pred), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, VE, .unit = "pred" }, + { "left", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "plane", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "median", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, INT_MIN, INT_MAX, VE, .unit = "pred" }, { NULL}, }; diff --git a/libavcodec/jpegxl.h b/libavcodec/jpegxl.h new file mode 100644 index 00000000000..66a6be35556 --- /dev/null +++ b/libavcodec/jpegxl.h @@ -0,0 +1,94 @@ +/* + * JPEG XL Common Header Definitions + * Copyright (c) 2023 Leo Izen + * + * 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 + */ + +#ifndef AVCODEC_JPEGXL_H +#define AVCODEC_JPEGXL_H + +#define FF_JPEGXL_CODESTREAM_SIGNATURE_LE 0x0aff +#define FF_JPEGXL_CONTAINER_SIGNATURE_LE 0x204c584a0c000000 +#define FF_JPEGXL_CODESTREAM_SIGNATURE_BE 0xff0a +#define FF_JPEGXL_CONTAINER_SIGNATURE_BE 0x0000000c4a584c20 + +typedef enum FFJXLFrameEncoding { + JPEGXL_ENC_VARDCT, + JPEGXL_ENC_MODULAR +} FFJXLFrameEncoding; + +typedef enum FFJXLFrameType { + JPEGXL_FRAME_REGULAR, + JPEGXL_FRAME_LF, + JPEGXL_FRAME_REFERENCE_ONLY, + JPEGXL_FRAME_SKIP_PROGRESSIVE +} FFJXLFrameType; + +typedef enum FFJXLBlendMode { + JPEGXL_BM_REPLACE, + JPEGXL_BM_ADD, + JPEGXL_BM_BLEND, + JPEGXL_BM_MULADD, + JPEGXL_BM_MUL +} FFJXLBlendMode; + +typedef enum FFJXLExtraChannelType { + JPEGXL_CT_ALPHA = 0, + JPEGXL_CT_DEPTH, + JPEGXL_CT_SPOT_COLOR, + JPEGXL_CT_SELECTION_MASK, + JPEGXL_CT_BLACK, + JPEGXL_CT_CFA, + JPEGXL_CT_THERMAL, + JPEGXL_CT_NON_OPTIONAL = 15, + JPEGXL_CT_OPTIONAL +} FFJXLExtraChannelType; + +typedef enum FFJXLColorSpace { + JPEGXL_CS_RGB = 0, + JPEGXL_CS_GRAY, + JPEGXL_CS_XYB, + JPEGXL_CS_UNKNOWN +} FFJXLColorSpace; + +typedef enum FFJXLWhitePoint { + JPEGXL_WP_D65 = 1, + JPEGXL_WP_CUSTOM, + JPEGXL_WP_E = 10, + JPEGXL_WP_DCI = 11 +} FFJXLWhitePoint; + +typedef enum FFJXLPrimaries { + JPEGXL_PR_SRGB = 1, + JPEGXL_PR_CUSTOM, + JPEGXL_PR_2100 = 9, + JPEGXL_PR_P3 = 11, +} FFJXLPrimaries; + +typedef enum FFJXLTransferCharacteristic { + JPEGXL_TR_BT709 = 1, + JPEGXL_TR_UNKNOWN, + JPEGXL_TR_LINEAR = 8, + JPEGXL_TR_SRGB = 13, + JPEGXL_TR_PQ = 16, + JPEGXL_TR_DCI, + JPEGXL_TR_HLG, + JPEGXL_TR_GAMMA = 1 << 24, +} FFJXLTransferCharacteristic; + +#endif /* AVCODEC_JPEGXL_H */ diff --git a/libavcodec/jpegxl_parse.c b/libavcodec/jpegxl_parse.c new file mode 100644 index 00000000000..7cfdd3e7d59 --- /dev/null +++ b/libavcodec/jpegxl_parse.c @@ -0,0 +1,520 @@ +/* + * JPEG XL Header Parser + * Copyright (c) 2023 Leo Izen + * + * 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 + +#include "bytestream.h" +#define UNCHECKED_BITSTREAM_READER 0 +#define BITSTREAM_READER_LE +#include "get_bits.h" +#include "jpegxl.h" +#include "jpegxl_parse.h" + +/* read a U32(c_i + u(u_i)) */ +static av_always_inline uint32_t jxl_u32(GetBitContext *gb, + uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3, + uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) +{ + const uint32_t constants[4] = {c0, c1, c2, c3}; + const uint32_t ubits [4] = {u0, u1, u2, u3}; + uint32_t ret, choice = get_bits(gb, 2); + + ret = constants[choice]; + if (ubits[choice]) + ret += get_bits_long(gb, ubits[choice]); + + return ret; +} + +static av_always_inline uint32_t jxl_enum(GetBitContext *gb) +{ + return jxl_u32(gb, 0, 1, 2, 18, 0, 0, 4, 6); +} + +/* read a U64() */ +static uint64_t jxl_u64(GetBitContext *gb) +{ + uint64_t shift = 12, ret; + + switch (get_bits(gb, 2)) { + case 1: + ret = 1 + get_bits(gb, 4); + break; + case 2: + ret = 17 + get_bits(gb, 8); + break; + case 3: + ret = get_bits(gb, 12); + while (get_bits1(gb)) { + if (shift < 60) { + ret |= (uint64_t)get_bits(gb, 8) << shift; + shift += 8; + } else { + ret |= (uint64_t)get_bits(gb, 4) << shift; + break; + } + } + break; + default: + ret = 0; + } + + return ret; +} + +static uint32_t jpegxl_width_from_ratio(uint32_t height, int ratio) +{ + uint64_t height64 = height; /* avoid integer overflow */ + switch (ratio) { + case 1: + return height; + case 2: + return (uint32_t)((height64 * 12) / 10); + case 3: + return (uint32_t)((height64 * 4) / 3); + case 4: + return (uint32_t)((height64 * 3) / 2); + case 5: + return (uint32_t)((height64 * 16) / 9); + case 6: + return (uint32_t)((height64 * 5) / 4); + case 7: + return (uint32_t)(height64 * 2); + default: + break; + } + + return 0; /* manual width */ +} + +/** + * validate a Jpeg XL Size Header + * @return >= 0 upon valid size, < 0 upon invalid size found + */ +static int jpegxl_read_size_header(GetBitContext *gb, FFJXLMetadata *meta, int validate) +{ + uint32_t width, height; + + if (get_bits1(gb)) { + /* small size header */ + height = (get_bits(gb, 5) + 1) << 3; + width = jpegxl_width_from_ratio(height, get_bits(gb, 3)); + if (!width) + width = (get_bits(gb, 5) + 1) << 3; + } else { + /* large size header */ + height = 1 + jxl_u32(gb, 0, 0, 0, 0, 9, 13, 18, 30); + width = jpegxl_width_from_ratio(height, get_bits(gb, 3)); + if (!width) + width = 1 + jxl_u32(gb, 0, 0, 0, 0, 9, 13, 18, 30); + } + if (validate && (width > (1 << 18) || height > (1 << 18) + || (width >> 4) * (height >> 4) > (1 << 20))) + return AVERROR_INVALIDDATA; + + if (meta) { + meta->width = meta->coded_width = width; + meta->height = meta->coded_height = height; + } + + return 0; +} + +/** + * validate a Jpeg XL Preview Header + * @return >= 0 upon valid size, < 0 upon invalid size found + */ +static int jpegxl_read_preview_header(GetBitContext *gb, int validate) +{ + uint32_t width, height; + + if (get_bits1(gb)) { + /* coded height and width divided by eight */ + height = jxl_u32(gb, 16, 32, 1, 33, 0, 0, 5, 9) << 3; + width = jpegxl_width_from_ratio(height, get_bits(gb, 3)); + if (!width) + width = jxl_u32(gb, 16, 32, 1, 33, 0, 0, 5, 9) << 3; + } else { + /* full height and width coded */ + height = jxl_u32(gb, 1, 65, 321, 1345, 6, 8, 10, 12); + width = jpegxl_width_from_ratio(height, get_bits(gb, 3)); + if (!width) + width = jxl_u32(gb, 1, 65, 321, 1345, 6, 8, 10, 12); + } + if (validate && (width > 4096 || height > 4096)) + return AVERROR_INVALIDDATA; + + return 0; +} + +/** + * get a Jpeg XL BitDepth Header. These cannot be invalid. + */ +static void jpegxl_get_bit_depth(GetBitContext *gb, FFJXLMetadata *meta) +{ + int bit_depth; + if (get_bits1(gb)) { + /* float samples */ + bit_depth = jxl_u32(gb, 32, 16, 24, 1, 0, 0, 0, 6); /* mantissa */ + skip_bits_long(gb, 4); /* exponent */ + } else { + /* integer samples */ + bit_depth = jxl_u32(gb, 8, 10, 12, 1, 0, 0, 0, 6); + } + if (meta) + meta->bit_depth = bit_depth; +} + +/** + * validate a Jpeg XL Extra Channel Info bundle + * @return >= 0 upon valid, < 0 upon invalid + */ +static int jpegxl_read_extra_channel_info(GetBitContext *gb, FFJXLMetadata *meta, int validate) +{ + int default_alpha = get_bits1(gb); + uint32_t type, name_len = 0; + + if (!default_alpha) { + type = jxl_enum(gb); + if (validate && type > 63) + return AVERROR_INVALIDDATA; /* enum types cannot be 64+ */ + if (validate && validate < 10 && type == JPEGXL_CT_BLACK) + return AVERROR_INVALIDDATA; + jpegxl_get_bit_depth(gb, NULL); + jxl_u32(gb, 0, 3, 4, 1, 0, 0, 0, 3); /* dim-shift */ + /* max of name_len is 1071 = 48 + 2^10 - 1 */ + name_len = 8 * jxl_u32(gb, 0, 0, 16, 48, 0, 4, 5, 10); + } else { + type = JPEGXL_CT_ALPHA; + } + + if (get_bits_left(gb) < name_len) + return AVERROR_BUFFER_TOO_SMALL; + + /* skip over the name */ + skip_bits_long(gb, name_len); + + if (!default_alpha && type == JPEGXL_CT_ALPHA) + skip_bits1(gb); + + if (type == JPEGXL_CT_SPOT_COLOR) + skip_bits_long(gb, 16 * 4); + + if (type == JPEGXL_CT_CFA) + jxl_u32(gb, 1, 0, 3, 19, 0, 2, 4, 8); + + if (meta && type == JPEGXL_CT_ALPHA) + meta->have_alpha = 1; + + return 0; +} + +static int jpegxl_skip_extensions(GetBitContext *gb) +{ + uint64_t extensions = jxl_u64(gb), extensions_len = 0; + + if (get_bits_left(gb) <= 0) + return AVERROR_BUFFER_TOO_SMALL; + + if (!extensions) + return 0; + + for (int i = 0; i < 64; i++) { + if (extensions & (UINT64_C(1) << i)) + extensions_len += jxl_u64(gb); + if (get_bits_left(gb) <= 0) + return AVERROR_BUFFER_TOO_SMALL; + } + + if (extensions_len > INT_MAX || get_bits_left(gb) <= extensions_len) + return AVERROR_BUFFER_TOO_SMALL; + + skip_bits_long(gb, extensions_len); + + return 0; +} + +int ff_jpegxl_parse_codestream_header(const uint8_t *buf, int buflen, FFJXLMetadata *meta, int validate) +{ + GetBitContext gbi, *gb = &gbi; + + int all_default, extra_fields = 0; + int xyb_encoded = 1, have_icc_profile = 0; + int animation_offset = 0, have_timecodes = 0; + + FFJXLPrimaries primaries = JPEGXL_PR_SRGB; + FFJXLTransferCharacteristic trc = JPEGXL_TR_SRGB + (1U << 24); + FFJXLWhitePoint white_point = JPEGXL_WP_D65; + FFJXLColorSpace color_space = JPEGXL_CS_RGB; + + AVRational tb; + uint32_t num_extra_channels = 0; + int ret; + + ret = init_get_bits8(gb, buf, buflen); + if (ret < 0) + return ret; + + if (get_bits(gb, 16) != FF_JPEGXL_CODESTREAM_SIGNATURE_LE && validate) + return AVERROR_INVALIDDATA; + + ret = jpegxl_read_size_header(gb, meta, validate); + if (ret < 0) + return ret; + + all_default = get_bits1(gb); + if (!all_default) + extra_fields = get_bits1(gb); + + if (extra_fields) { + int orientation = get_bits(gb, 3); + if (orientation > 3 && meta) + FFSWAP(uint32_t, meta->width, meta->height); + + /* + * intrinstic size + * any size header here is valid, but as it + * is variable length we have to read it + */ + if (get_bits1(gb)) + jpegxl_read_size_header(gb, NULL, 0); + + /* preview header */ + if (get_bits1(gb)) { + ret = jpegxl_read_preview_header(gb, 0); + if (ret < 0) + return ret; + } + + /* animation header */ + if (get_bits1(gb)) { + animation_offset = get_bits_count(gb); + tb.den = jxl_u32(gb, 100, 1000, 1, 1, 0, 0, 10, 30); + tb.num = jxl_u32(gb, 1, 1001, 1, 1, 0, 0, 8, 10); + jxl_u32(gb, 0, 0, 0, 0, 0, 3, 16, 32); + have_timecodes = get_bits1(gb); + } + } + + if (animation_offset && meta) { + meta->animation_offset = animation_offset; + meta->timebase = tb; + meta->have_timecodes = have_timecodes; + } + + if (get_bits_left(gb) <= 0) + return AVERROR_BUFFER_TOO_SMALL; + + if (!all_default) { + jpegxl_get_bit_depth(gb, meta); + + /* modular_16bit_buffers must equal 1 */ + if (!get_bits1(gb) && validate && validate < 10) + return AVERROR_INVALIDDATA; + + num_extra_channels = jxl_u32(gb, 0, 1, 2, 1, 0, 0, 4, 12); + if (num_extra_channels > 4 && validate && validate < 10) + return AVERROR_INVALIDDATA; + for (uint32_t i = 0; i < num_extra_channels; i++) { + ret = jpegxl_read_extra_channel_info(gb, meta, validate); + if (ret < 0) + return ret; + if (get_bits_left(gb) <= 0) + return AVERROR_BUFFER_TOO_SMALL; + } + + xyb_encoded = get_bits1(gb); + + /* color encoding bundle */ + if (!get_bits1(gb)) { + have_icc_profile = get_bits1(gb); + color_space = jxl_enum(gb); + if (color_space > 63 && validate) + return AVERROR_INVALIDDATA; + if (!have_icc_profile) { + if (color_space != JPEGXL_CS_XYB) { + white_point = jxl_enum(gb); + if (white_point > 63 && validate) + return AVERROR_INVALIDDATA; + if (white_point == JPEGXL_WP_CUSTOM) { + /* ux and uy values */ + jxl_u32(gb, 0, 524288, 1048576, 2097152, 19, 19, 20, 21); + jxl_u32(gb, 0, 524288, 1048576, 2097152, 19, 19, 20, 21); + } + if (color_space != JPEGXL_CS_GRAY) { + /* primaries */ + primaries = jxl_enum(gb); + if (primaries > 63 && validate) + return AVERROR_INVALIDDATA; + if (primaries == JPEGXL_PR_CUSTOM) { + /* ux/uy values for r,g,b */ + for (int i = 0; i < 6; i++) { + jxl_u32(gb, 0, 524288, 1048576, 2097152, 19, 19, 20, 21); + if (get_bits_left(gb) <= 0) + return AVERROR_BUFFER_TOO_SMALL; + } + } + } + } + + /* transfer characteristics */ + if (get_bits1(gb)) { + /* gamma */ + trc = get_bits(gb, 24); + } else { + /* transfer function */ + trc = jxl_enum(gb); + if (trc > 63 && validate) + return AVERROR_INVALIDDATA; + trc += (1U << 24); + } + + /* rendering intent */ + if (jxl_enum(gb) > 63 && validate) + return AVERROR_INVALIDDATA; + } + } + + /* tone mapping bundle */ + if (extra_fields && !get_bits1(gb)) + skip_bits_long(gb, 16 + 16 + 1 + 16); + + ret = jpegxl_skip_extensions(gb); + if (ret < 0) + return ret; + } + + if (meta) { + meta->xyb_encoded = xyb_encoded; + meta->have_icc_profile = have_icc_profile; + meta->csp = color_space; + meta->primaries = primaries; + meta->wp = white_point; + meta->trc = trc; + if (!meta->bit_depth) + meta->bit_depth = 8; + meta->num_extra_channels = num_extra_channels; + } + + /* default transform */ + if (!get_bits1(gb)) { + /* opsin inverse matrix */ + if (xyb_encoded && !get_bits1(gb)) + skip_bits_long(gb, 16 * 16); + /* cw_mask and default weights */ + if (get_bits1(gb)) + skip_bits_long(gb, 16 * 15); + if (get_bits1(gb)) + skip_bits_long(gb, 16 * 55); + if (get_bits1(gb)) + skip_bits_long(gb, 16 * 210); + } + + if (!have_icc_profile) { + int bits_remaining = 7 - ((get_bits_count(gb) - 1) & 0x7); + if (bits_remaining && get_bits(gb, bits_remaining)) + return AVERROR_INVALIDDATA; + } + + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + + return get_bits_count(gb); +} + +/* + * copies as much of the codestream into the buffer as possible + * pass a shorter buflen to request less + * returns the number of bytes consumed from input, may be greater than input_len + * if the input doesn't end on an ISOBMFF-box boundary + */ +int ff_jpegxl_collect_codestream_header(const uint8_t *input_buffer, int input_len, + uint8_t *buffer, int buflen, int *copied) +{ + GetByteContext gb; + int pos = 0, last_box = 0; + bytestream2_init(&gb, input_buffer, input_len); + + while (1) { + uint64_t size; + uint32_t tag; + int head_size = 8; + + if (bytestream2_get_bytes_left(&gb) < 8) + return AVERROR_BUFFER_TOO_SMALL; + + size = bytestream2_get_be32(&gb); + tag = bytestream2_get_le32(&gb); + + if (size == 1) { + if (bytestream2_get_bytes_left(&gb) < 8) + return AVERROR_BUFFER_TOO_SMALL; + size = bytestream2_get_be64(&gb); + head_size = 16; + } + /* invalid ISOBMFF size */ + if (size && size <= head_size) + return AVERROR_INVALIDDATA; + if (size) + size -= head_size; + + if (tag == MKTAG('j','x','l','p')) { + uint32_t idx; + if (bytestream2_get_bytes_left(&gb) < 4) + return AVERROR_BUFFER_TOO_SMALL; + idx = bytestream2_get_be32(&gb); + if (idx >= UINT32_C(0x80000000)) + last_box = 1; + if (size) { + if (size <= 4) + return AVERROR_INVALIDDATA; + size -= 4; + } + } + if (tag == MKTAG('j','x','l','c')) + last_box = 1; + + /* + * size = 0 means "until EOF". this is legal but uncommon + * here we just set it to the remaining size of the probe buffer + */ + if (!size) + size = bytestream2_get_bytes_left(&gb); + else + pos += size + head_size; + + if (tag == MKTAG('j','x','l','c') || tag == MKTAG('j','x','l','p')) { + if (size > buflen - *copied) + size = buflen - *copied; + /* + * arbitrary chunking of the payload makes this memcpy hard to avoid + * in practice this will only be performed one or two times at most + */ + *copied += bytestream2_get_buffer(&gb, buffer + *copied, size); + } else { + bytestream2_skip(&gb, size); + } + if (last_box || bytestream2_get_bytes_left(&gb) <= 0 || *copied >= buflen) + break; + } + + return pos; +} diff --git a/libavcodec/jpegxl_parse.h b/libavcodec/jpegxl_parse.h new file mode 100644 index 00000000000..0602f4d4099 --- /dev/null +++ b/libavcodec/jpegxl_parse.h @@ -0,0 +1,72 @@ +/* + * JPEG XL Header Parser + * Copyright (c) 2023 Leo Izen + * + * 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 + */ + +#ifndef AVCODEC_JPEGXL_PARSE_H +#define AVCODEC_JPEGXL_PARSE_H + +#include + +#include "libavutil/rational.h" + +#include "jpegxl.h" + +typedef struct FFJXLMetadata { + uint32_t width; + uint32_t height; + uint32_t coded_width; + uint32_t coded_height; + int bit_depth; + int have_alpha; + /* + * offset, in bits, of the animation header + * zero if not animated + */ + int animation_offset; + AVRational timebase; + FFJXLColorSpace csp; + FFJXLWhitePoint wp; + FFJXLPrimaries primaries; + FFJXLTransferCharacteristic trc; + + /* used by the parser */ + int xyb_encoded; + int have_icc_profile; + int have_timecodes; + uint32_t num_extra_channels; +} FFJXLMetadata; + +/* + * copies as much of the codestream into the buffer as possible + * pass a shorter buflen to request less + * returns the number of bytes consumed from input, may be greater than input_len + * if the input doesn't end on an ISOBMFF-box boundary + */ +int ff_jpegxl_collect_codestream_header(const uint8_t *input_buffer, int input_len, + uint8_t *buffer, int buflen, int *copied); + +/* + * Parse the codestream header with the provided buffer. Returns negative upon failure, + * or the number of bits consumed upon success. + * The FFJXLMetadata parameter may be NULL, in which case it's ignored. + */ +int ff_jpegxl_parse_codestream_header(const uint8_t *buf, int buflen, FFJXLMetadata *meta, int validate); + +#endif /* AVCODEC_JPEGXL_PARSE_H */ diff --git a/libavcodec/jpegxl_parser.c b/libavcodec/jpegxl_parser.c new file mode 100644 index 00000000000..8c45e1a1b73 --- /dev/null +++ b/libavcodec/jpegxl_parser.c @@ -0,0 +1,1538 @@ +/** + * JPEG XL parser + * Copyright (c) 2023 Leo Izen + * + * 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 +#include +#include + +#include "libavutil/attributes.h" +#include "libavutil/error.h" +#include "libavutil/intmath.h" +#include "libavutil/macros.h" +#include "libavutil/mem.h" +#include "libavutil/pixfmt.h" + +#include "bytestream.h" +#include "codec_id.h" +#define UNCHECKED_BITSTREAM_READER 0 +#define BITSTREAM_READER_LE +#include "get_bits.h" +#include "jpegxl.h" +#include "jpegxl_parse.h" +#include "parser.h" +#include "vlc.h" + +#define JXL_FLAG_NOISE 1 +#define JXL_FLAG_PATCHES 2 +#define JXL_FLAG_SPLINES 16 +#define JXL_FLAG_USE_LF_FRAME 32 +#define JXL_FLAG_SKIP_ADAPTIVE_LF_SMOOTH 128 + +#define MAX_PREFIX_ALPHABET_SIZE (1u << 15) + +#define clog1p(x) (ff_log2(x) + !!(x)) +#define unpack_signed(x) (((x) & 1 ? -(x)-1 : (x))/2) +#define div_ceil(x, y) (((x) - 1) / (y) + 1) +#define vlm(a,b) {.sym = (a), .len = (b)} + +typedef struct JXLHybridUintConf { + int split_exponent; + uint32_t msb_in_token; + uint32_t lsb_in_token; +} JXLHybridUintConf; + +typedef struct JXLSymbolDistribution { + JXLHybridUintConf config; + int log_bucket_size; + /* this is the actual size of the alphabet */ + int alphabet_size; + /* ceil(log(alphabet_size)) */ + int log_alphabet_size; + + /* for prefix code distributions */ + VLC vlc; + /* in case bits == 0 */ + uint32_t default_symbol; + + /* + * each (1 << log_alphabet_size) length + * with log_alphabet_size <= 8 + */ + /* frequencies associated with this Distribution */ + uint32_t freq[258]; + /* cutoffs for using the symbol table */ + uint16_t cutoffs[258]; + /* the symbol table for this distribution */ + uint16_t symbols[258]; + /* the offset for symbols */ + uint16_t offsets[258]; + + /* if this distribution contains only one symbol this is its index */ + int uniq_pos; +} JXLSymbolDistribution; + +typedef struct JXLDistributionBundle { + /* lz77 flags */ + int lz77_enabled; + uint32_t lz77_min_symbol; + uint32_t lz77_min_length; + JXLHybridUintConf lz_len_conf; + + /* one entry for each distribution */ + uint8_t *cluster_map; + /* length of cluster_map */ + int num_dist; + + /* one for each cluster */ + JXLSymbolDistribution *dists; + int num_clusters; + + /* whether to use brotli prefixes or ans */ + int use_prefix_code; + /* bundle log alphabet size, dist ones may be smaller */ + int log_alphabet_size; +} JXLDistributionBundle; + +typedef struct JXLEntropyDecoder { + + /* state is a positive 32-bit integer, or -1 if unset */ + int64_t state; + + /* lz77 values */ + uint32_t num_to_copy; + uint32_t copy_pos; + uint32_t num_decoded; + + /* length is (1 << 20) */ + /* if lz77 is enabled for this bundle */ + /* if lz77 is disabled it's NULL */ + uint32_t *window; + + /* primary bundle associated with this distribution */ + JXLDistributionBundle bundle; + + /* for av_log */ + void *logctx; +} JXLEntropyDecoder; + +typedef struct JXLFrame { + FFJXLFrameType type; + FFJXLFrameEncoding encoding; + + int is_last; + int full_frame; + + uint32_t total_length; + uint32_t body_length; +} JXLFrame; + +typedef struct JXLCodestream { + FFJXLMetadata meta; + JXLFrame frame; +} JXLCodestream; + +typedef struct JXLParseContext { + ParseContext pc; + JXLCodestream codestream; + + /* using ISOBMFF-based container */ + int container; + int skip; + int copied; + int collected_size; + int codestream_length; + int skipped_icc; + int next; + + uint8_t cs_buffer[4096 + AV_INPUT_BUFFER_PADDING_SIZE]; +} JXLParseContext; + +/* used for reading brotli prefixes */ +static const VLCElem level0_table[16] = { + vlm(0, 2), vlm(4, 2), vlm(3, 2), vlm(2, 3), vlm(0, 2), vlm(4, 2), vlm(3, 2), vlm(1, 4), + vlm(0, 2), vlm(4, 2), vlm(3, 2), vlm(2, 3), vlm(0, 2), vlm(4, 2), vlm(3, 2), vlm(5, 4), +}; + +/* prefix table for populating ANS distribution */ +static const VLCElem dist_prefix_table[128] = { + vlm(10, 3), vlm(12, 7), vlm(7, 3), vlm(3, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(5, 4), + vlm(10, 3), vlm(4, 4), vlm(7, 3), vlm(1, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(2, 4), + vlm(10, 3), vlm(0, 5), vlm(7, 3), vlm(3, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(5, 4), + vlm(10, 3), vlm(4, 4), vlm(7, 3), vlm(1, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(2, 4), + vlm(10, 3), vlm(11, 6), vlm(7, 3), vlm(3, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(5, 4), + vlm(10, 3), vlm(4, 4), vlm(7, 3), vlm(1, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(2, 4), + vlm(10, 3), vlm(0, 5), vlm(7, 3), vlm(3, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(5, 4), + vlm(10, 3), vlm(4, 4), vlm(7, 3), vlm(1, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(2, 4), + vlm(10, 3), vlm(13, 7), vlm(7, 3), vlm(3, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(5, 4), + vlm(10, 3), vlm(4, 4), vlm(7, 3), vlm(1, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(2, 4), + vlm(10, 3), vlm(0, 5), vlm(7, 3), vlm(3, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(5, 4), + vlm(10, 3), vlm(4, 4), vlm(7, 3), vlm(1, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(2, 4), + vlm(10, 3), vlm(11, 6), vlm(7, 3), vlm(3, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(5, 4), + vlm(10, 3), vlm(4, 4), vlm(7, 3), vlm(1, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(2, 4), + vlm(10, 3), vlm(0, 5), vlm(7, 3), vlm(3, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(5, 4), + vlm(10, 3), vlm(4, 4), vlm(7, 3), vlm(1, 4), vlm(6, 3), vlm(8, 3), vlm(9, 3), vlm(2, 4), +}; + +static const uint8_t prefix_codelen_map[18] = { + 1, 2, 3, 4, 0, 5, 17, 6, 16, 7, 8, 9, 10, 11, 12, 13, 14, 15, +}; + +/** + * Read a variable-length 8-bit integer. + * Used when populating the ANS frequency tables. + */ +static av_always_inline uint8_t jxl_u8(GetBitContext *gb) +{ + int n; + if (!get_bits1(gb)) + return 0; + n = get_bits(gb, 3); + + return get_bitsz(gb, n) | (1 << n); +} + +/* read a U32(c_i + u(u_i)) */ +static av_always_inline uint32_t jxl_u32(GetBitContext *gb, + uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3, + uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) +{ + const uint32_t constants[4] = {c0, c1, c2, c3}; + const uint32_t ubits [4] = {u0, u1, u2, u3}; + uint32_t ret, choice = get_bits(gb, 2); + + ret = constants[choice]; + if (ubits[choice]) + ret += get_bits_long(gb, ubits[choice]); + + return ret; +} + +/* read a U64() */ +static uint64_t jxl_u64(GetBitContext *gb) +{ + uint64_t shift = 12, ret; + + switch (get_bits(gb, 2)) { + case 1: + ret = 1 + get_bits(gb, 4); + break; + case 2: + ret = 17 + get_bits(gb, 8); + break; + case 3: + ret = get_bits(gb, 12); + while (get_bits1(gb)) { + if (shift < 60) { + ret |= (uint64_t)get_bits(gb, 8) << shift; + shift += 8; + } else { + ret |= (uint64_t)get_bits(gb, 4) << shift; + break; + } + } + break; + default: + ret = 0; + } + + return ret; +} + +static int read_hybrid_uint_conf(GetBitContext *gb, JXLHybridUintConf *conf, int log_alphabet_size) +{ + conf->split_exponent = get_bitsz(gb, clog1p(log_alphabet_size)); + if (conf->split_exponent == log_alphabet_size) { + conf->msb_in_token = conf->lsb_in_token = 0; + return 0; + } + + conf->msb_in_token = get_bitsz(gb, clog1p(conf->split_exponent)); + if (conf->msb_in_token > conf->split_exponent) + return AVERROR_INVALIDDATA; + conf->lsb_in_token = get_bitsz(gb, clog1p(conf->split_exponent - conf->msb_in_token)); + if (conf->msb_in_token + conf->lsb_in_token > conf->split_exponent) + return AVERROR_INVALIDDATA; + + return 0; +} + +static int read_hybrid_uint(GetBitContext *gb, const JXLHybridUintConf *conf, uint32_t token, uint32_t *hybrid_uint) +{ + uint32_t n, low, split = 1 << conf->split_exponent; + + if (token < split) { + *hybrid_uint = token; + return 0; + } + + n = conf->split_exponent - conf->lsb_in_token - conf->msb_in_token + + ((token - split) >> (conf->msb_in_token + conf->lsb_in_token)); + if (n >= 32) + return AVERROR_INVALIDDATA; + low = token & ((1 << conf->lsb_in_token) - 1); + token >>= conf->lsb_in_token; + token &= (1 << conf->msb_in_token) - 1; + token |= 1 << conf->msb_in_token; + *hybrid_uint = (((token << n) | get_bits_long(gb, n)) << conf->lsb_in_token ) | low; + + return 0; +} + +static inline uint32_t read_prefix_symbol(GetBitContext *gb, const JXLSymbolDistribution *dist) +{ + if (!dist->vlc.bits) + return dist->default_symbol; + + return get_vlc2(gb, dist->vlc.table, dist->vlc.bits, 1); +} + +static uint32_t read_ans_symbol(GetBitContext *gb, JXLEntropyDecoder *dec, const JXLSymbolDistribution *dist) +{ + uint32_t index, i, pos, symbol, offset; + + if (dec->state < 0) + dec->state = get_bits_long(gb, 32); + + index = dec->state & 0xFFF; + i = index >> dist->log_bucket_size; + pos = index & ((1 << dist->log_bucket_size) - 1); + symbol = pos >= dist->cutoffs[i] ? dist->symbols[i] : i; + offset = pos >= dist->cutoffs[i] ? dist->offsets[i] + pos : pos; + dec->state = dist->freq[symbol] * (dec->state >> 12) + offset; + if (dec->state < (1 << 16)) + dec->state = (dec->state << 16) | get_bits(gb, 16); + dec->state &= 0xFFFFFFFF; + + return symbol; +} + +static int decode_hybrid_varlen_uint(GetBitContext *gb, JXLEntropyDecoder *dec, + const JXLDistributionBundle *bundle, + uint32_t context, uint32_t *hybrid_uint) +{ + int ret; + uint32_t token, distance; + const JXLSymbolDistribution *dist; + + if (dec->num_to_copy > 0) { + *hybrid_uint = dec->window[dec->copy_pos++ & 0xFFFFF]; + dec->num_to_copy--; + dec->window[dec->num_decoded++ & 0xFFFFF] = *hybrid_uint; + return 0; + } + + if (context >= bundle->num_dist) + return AVERROR(EINVAL); + if (bundle->cluster_map[context] >= bundle->num_clusters) + return AVERROR_INVALIDDATA; + + dist = &bundle->dists[bundle->cluster_map[context]]; + if (bundle->use_prefix_code) + token = read_prefix_symbol(gb, dist); + else + token = read_ans_symbol(gb, dec, dist); + + if (bundle->lz77_enabled && token >= bundle->lz77_min_symbol) { + const JXLSymbolDistribution *lz77dist = &bundle->dists[bundle->cluster_map[bundle->num_dist - 1]]; + ret = read_hybrid_uint(gb, &bundle->lz_len_conf, token - bundle->lz77_min_symbol, &dec->num_to_copy); + if (ret < 0) + return ret; + dec->num_to_copy += bundle->lz77_min_length; + if (bundle->use_prefix_code) + token = read_prefix_symbol(gb, lz77dist); + else + token = read_ans_symbol(gb, dec, lz77dist); + ret = read_hybrid_uint(gb, &lz77dist->config, token, &distance); + if (ret < 0) + return ret; + distance++; + distance = FFMIN3(distance, dec->num_decoded, 1 << 20); + dec->copy_pos = dec->num_decoded - distance; + return decode_hybrid_varlen_uint(gb, dec, bundle, context, hybrid_uint); + } + ret = read_hybrid_uint(gb, &dist->config, token, hybrid_uint); + if (ret < 0) + return ret; + if (bundle->lz77_enabled) + dec->window[dec->num_decoded++ & 0xFFFFF] = *hybrid_uint; + + return 0; +} + +static int populate_distribution(GetBitContext *gb, JXLSymbolDistribution *dist, int log_alphabet_size) +{ + int len = 0, shift, omit_log = -1, omit_pos = -1; + int prev = 0, num_same = 0; + uint32_t total_count = 0; + uint8_t logcounts[258] = { 0 }; + uint8_t same[258] = { 0 }; + const int table_size = 1 << log_alphabet_size; + dist->uniq_pos = -1; + + if (get_bits1(gb)) { + /* simple code */ + if (get_bits1(gb)) { + uint8_t v1 = jxl_u8(gb); + uint8_t v2 = jxl_u8(gb); + if (v1 == v2) + return AVERROR_INVALIDDATA; + dist->freq[v1] = get_bits(gb, 12); + dist->freq[v2] = (1 << 12) - dist->freq[v1]; + if (!dist->freq[v1]) + dist->uniq_pos = v2; + dist->alphabet_size = 1 + FFMAX(v1, v2); + } else { + uint8_t x = jxl_u8(gb); + dist->freq[x] = 1 << 12; + dist->uniq_pos = x; + dist->alphabet_size = 1 + x; + } + if (dist->alphabet_size > table_size) + return AVERROR_INVALIDDATA; + + return 0; + } + + if (get_bits1(gb)) { + /* flat code */ + dist->alphabet_size = jxl_u8(gb) + 1; + if (dist->alphabet_size > table_size) + return AVERROR_INVALIDDATA; + for (int i = 0; i < dist->alphabet_size; i++) + dist->freq[i] = (1 << 12) / dist->alphabet_size; + for (int i = 0; i < (1 << 12) % dist->alphabet_size; i++) + dist->freq[i]++; + return 0; + } + + do { + if (!get_bits1(gb)) + break; + } while (++len < 3); + + shift = (get_bitsz(gb, len) | (1 << len)) - 1; + if (shift > 13) + return AVERROR_INVALIDDATA; + + dist->alphabet_size = jxl_u8(gb) + 3; + if (dist->alphabet_size > table_size) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < dist->alphabet_size; i++) { + logcounts[i] = get_vlc2(gb, dist_prefix_table, 7, 1); + if (logcounts[i] == 13) { + int rle = jxl_u8(gb); + same[i] = rle + 5; + i += rle + 3; + continue; + } + if (logcounts[i] > omit_log) { + omit_log = logcounts[i]; + omit_pos = i; + } + } + if (omit_pos < 0 || omit_pos + 1 < dist->alphabet_size && logcounts[omit_pos + 1] == 13) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < dist->alphabet_size; i++) { + if (same[i]) { + num_same = same[i] - 1; + prev = i > 0 ? dist->freq[i - 1] : 0; + } + if (num_same) { + dist->freq[i] = prev; + num_same--; + } else { + if (i == omit_pos || !logcounts[i]) + continue; + if (logcounts[i] == 1) { + dist->freq[i] = 1; + } else { + int bitcount = FFMIN(FFMAX(0, shift - ((12 - logcounts[i] + 1) >> 1)), logcounts[i] - 1); + dist->freq[i] = (1 << (logcounts[i] - 1)) + (get_bitsz(gb, bitcount) << (logcounts[i] - 1 - bitcount)); + } + } + total_count += dist->freq[i]; + } + dist->freq[omit_pos] = (1 << 12) - total_count; + + return 0; +} + +static void dist_bundle_close(JXLDistributionBundle *bundle) +{ + if (bundle->use_prefix_code && bundle->dists) + for (int i = 0; i < bundle->num_clusters; i++) + ff_vlc_free(&bundle->dists[i].vlc); + av_freep(&bundle->dists); + av_freep(&bundle->cluster_map); +} + + +static int read_distribution_bundle(GetBitContext *gb, JXLEntropyDecoder *dec, + JXLDistributionBundle *bundle, int num_dist, int disallow_lz77); + +static int read_dist_clustering(GetBitContext *gb, JXLEntropyDecoder *dec, JXLDistributionBundle *bundle) +{ + int ret; + + bundle->cluster_map = av_malloc(bundle->num_dist); + if (!bundle->cluster_map) + return AVERROR(ENOMEM); + + if (bundle->num_dist == 1) { + bundle->cluster_map[0] = 0; + bundle->num_clusters = 1; + return 0; + } + + if (get_bits1(gb)) { + /* simple clustering */ + uint32_t nbits = get_bits(gb, 2); + for (int i = 0; i < bundle->num_dist; i++) + bundle->cluster_map[i] = get_bitsz(gb, nbits); + } else { + /* complex clustering */ + int use_mtf = get_bits1(gb); + JXLDistributionBundle nested = { 0 }; + /* num_dist == 1 prevents this from recursing again */ + ret = read_distribution_bundle(gb, dec, &nested, 1, bundle->num_dist <= 2); + if (ret < 0) { + dist_bundle_close(&nested); + return ret; + } + for (int i = 0; i < bundle->num_dist; i++) { + uint32_t clust; + ret = decode_hybrid_varlen_uint(gb, dec, &nested, 0, &clust); + if (ret < 0) { + dist_bundle_close(&nested); + return ret; + } + bundle->cluster_map[i] = clust; + } + dec->state = -1; + /* it's not going to necessarily be zero after reading */ + dec->num_to_copy = 0; + dist_bundle_close(&nested); + if (use_mtf) { + uint8_t mtf[256]; + for (int i = 0; i < 256; i++) + mtf[i] = i; + for (int i = 0; i < bundle->num_dist; i++) { + int index = bundle->cluster_map[i]; + bundle->cluster_map[i] = mtf[index]; + if (index) { + int value = mtf[index]; + for (int j = index; j > 0; j--) + mtf[j] = mtf[j - 1]; + mtf[0] = value; + } + } + } + } + for (int i = 0; i < bundle->num_dist; i++) { + if (bundle->cluster_map[i] >= bundle->num_clusters) + bundle->num_clusters = bundle->cluster_map[i] + 1; + } + + if (bundle->num_clusters > bundle->num_dist) + return AVERROR_INVALIDDATA; + + return 0; +} + +static int gen_alias_map(JXLEntropyDecoder *dec, JXLSymbolDistribution *dist, int log_alphabet_size) +{ + uint32_t bucket_size, table_size; + uint8_t overfull[256], underfull[256]; + int overfull_pos = 0, underfull_pos = 0; + dist->log_bucket_size = 12 - log_alphabet_size; + bucket_size = 1 << dist->log_bucket_size; + table_size = 1 << log_alphabet_size; + + if (dist->uniq_pos >= 0) { + for (int i = 0; i < table_size; i++) { + dist->symbols[i] = dist->uniq_pos; + dist->offsets[i] = bucket_size * i; + dist->cutoffs[i] = 0; + } + return 0; + } + + for (int i = 0; i < dist->alphabet_size; i++) { + dist->cutoffs[i] = dist->freq[i]; + dist->symbols[i] = i; + if (dist->cutoffs[i] > bucket_size) + overfull[overfull_pos++] = i; + else if (dist->cutoffs[i] < bucket_size) + underfull[underfull_pos++] = i; + } + + for (int i = dist->alphabet_size; i < table_size; i++) { + dist->cutoffs[i] = 0; + underfull[underfull_pos++] = i; + } + + while (overfull_pos) { + int o, u, by; + /* this should be impossible */ + if (!underfull_pos) + return AVERROR_INVALIDDATA; + u = underfull[--underfull_pos]; + o = overfull[--overfull_pos]; + by = bucket_size - dist->cutoffs[u]; + dist->cutoffs[o] -= by; + dist->symbols[u] = o; + dist->offsets[u] = dist->cutoffs[o]; + if (dist->cutoffs[o] < bucket_size) + underfull[underfull_pos++] = o; + else if (dist->cutoffs[o] > bucket_size) + overfull[overfull_pos++] = o; + } + + for (int i = 0; i < table_size; i++) { + if (dist->cutoffs[i] == bucket_size) { + dist->symbols[i] = i; + dist->offsets[i] = 0; + dist->cutoffs[i] = 0; + } else { + dist->offsets[i] -= dist->cutoffs[i]; + } + } + + return 0; +} + +static int read_simple_vlc_prefix(GetBitContext *gb, JXLEntropyDecoder *dec, JXLSymbolDistribution *dist) +{ + int nsym, tree_select, bits; + + int8_t lens[4]; + int16_t symbols[4]; + + nsym = 1 + get_bits(gb, 2); + for (int i = 0; i < nsym; i++) + symbols[i] = get_bitsz(gb, dist->log_alphabet_size); + if (nsym == 4) + tree_select = get_bits1(gb); + switch (nsym) { + case 1: + dist->vlc.bits = 0; + dist->default_symbol = symbols[0]; + return 0; + case 2: + bits = 1; + lens[0] = 1, lens[1] = 1, lens[2] = 0, lens[3] = 0; + if (symbols[1] < symbols[0]) + FFSWAP(int16_t, symbols[0], symbols[1]); + break; + case 3: + bits = 2; + lens[0] = 1, lens[1] = 2, lens[2] = 2, lens[3] = 0; + if (symbols[2] < symbols[1]) + FFSWAP(int16_t, symbols[1], symbols[2]); + break; + case 4: + if (tree_select) { + bits = 3; + lens[0] = 1, lens[1] = 2, lens[2] = 3, lens[3] = 3; + if (symbols[3] < symbols[2]) + FFSWAP(int16_t, symbols[2], symbols[3]); + } else { + bits = 2; + lens[0] = 2, lens[1] = 2, lens[2] = 2, lens[3] = 2; + while (1) { + if (symbols[1] < symbols[0]) + FFSWAP(int16_t, symbols[0], symbols[1]); + if (symbols[3] < symbols[2]) + FFSWAP(int16_t, symbols[2], symbols[3]); + if (symbols[1] <= symbols[2]) + break; + FFSWAP(int16_t, symbols[1], symbols[2]); + } + } + break; + default: + // Challenge Complete! How did we get here? + return AVERROR_BUG; + } + + return ff_vlc_init_from_lengths(&dist->vlc, bits, nsym, lens, 1, symbols, + 2, 2, 0, VLC_INIT_LE, dec->logctx); +} + +static int read_vlc_prefix(GetBitContext *gb, JXLEntropyDecoder *dec, JXLSymbolDistribution *dist) +{ + int8_t level1_lens[18] = { 0 }; + int8_t level1_lens_s[18] = { 0 }; + int16_t level1_syms[18] = { 0 }; + uint32_t level1_codecounts[19] = { 0 }; + uint8_t *buf = NULL; + int8_t *level2_lens, *level2_lens_s; + int16_t *level2_syms; + uint32_t *level2_codecounts; + + int repeat_count_prev = 0, repeat_count_zero = 0, prev = 8; + int total_code = 0, len, hskip, num_codes = 0, ret; + + VLC level1_vlc = { 0 }; + + if (dist->alphabet_size == 1) { + dist->vlc.bits = 0; + dist->default_symbol = 0; + return 0; + } + + hskip = get_bits(gb, 2); + if (hskip == 1) + return read_simple_vlc_prefix(gb, dec, dist); + + level1_codecounts[0] = hskip; + for (int i = hskip; i < 18; i++) { + len = level1_lens[prefix_codelen_map[i]] = get_vlc2(gb, level0_table, 4, 1); + if (len < 0) { + ret = AVERROR_INVALIDDATA; + goto end; + } + level1_codecounts[len]++; + if (len) { + total_code += (32 >> len); + num_codes++; + } + if (total_code >= 32) { + level1_codecounts[0] += 18 - i - 1; + break; + } + } + + if (total_code != 32 && num_codes >= 2 || num_codes < 1) { + ret = AVERROR_INVALIDDATA; + goto end; + } + + for (int i = 1; i < 19; i++) + level1_codecounts[i] += level1_codecounts[i - 1]; + + for (int i = 17; i >= 0; i--) { + int idx = --level1_codecounts[level1_lens[i]]; + level1_lens_s[idx] = level1_lens[i]; + level1_syms[idx] = i; + } + + ret = ff_vlc_init_from_lengths(&level1_vlc, 5, 18, level1_lens_s, 1, level1_syms, 2, 2, + 0, VLC_INIT_LE, dec->logctx); + if (ret < 0) + goto end; + + buf = av_mallocz(MAX_PREFIX_ALPHABET_SIZE * (2 * sizeof(int8_t) + sizeof(int16_t) + sizeof(uint32_t)) + + sizeof(uint32_t)); + if (!buf) { + ret = AVERROR(ENOMEM); + goto end; + } + + level2_lens = (int8_t *)buf; + level2_lens_s = (int8_t *)(buf + MAX_PREFIX_ALPHABET_SIZE * sizeof(int8_t)); + level2_syms = (int16_t *)(buf + MAX_PREFIX_ALPHABET_SIZE * (2 * sizeof(int8_t))); + level2_codecounts = (uint32_t *)(buf + MAX_PREFIX_ALPHABET_SIZE * (2 * sizeof(int8_t) + sizeof(int16_t))); + + total_code = 0; + for (int i = 0; i < dist->alphabet_size; i++) { + len = get_vlc2(gb, level1_vlc.table, 5, 1); + if (len < 0) { + ret = AVERROR_INVALIDDATA; + goto end; + } + if (get_bits_left(gb) < 0) { + ret = AVERROR_BUFFER_TOO_SMALL; + goto end; + } + if (len == 16) { + int extra = 3 + get_bits(gb, 2); + if (repeat_count_prev) + extra += 4 * (repeat_count_prev - 2) - repeat_count_prev; + extra = FFMIN(extra, dist->alphabet_size - i); + for (int j = 0; j < extra; j++) + level2_lens[i + j] = prev; + total_code += (32768 >> prev) * extra; + i += extra - 1; + repeat_count_prev += extra; + repeat_count_zero = 0; + level2_codecounts[prev] += extra; + } else if (len == 17) { + int extra = 3 + get_bits(gb, 3); + if (repeat_count_zero > 0) + extra += 8 * (repeat_count_zero - 2) - repeat_count_zero; + extra = FFMIN(extra, dist->alphabet_size - i); + i += extra - 1; + repeat_count_prev = 0; + repeat_count_zero += extra; + level2_codecounts[0] += extra; + } else { + level2_lens[i] = len; + repeat_count_prev = repeat_count_zero = 0; + if (len) { + total_code += (32768 >> len); + prev = len; + } + level2_codecounts[len]++; + } + if (total_code >= 32768) { + level2_codecounts[0] += dist->alphabet_size - i - 1; + break; + } + } + + if (total_code != 32768 && level2_codecounts[0] < dist->alphabet_size - 1) { + ret = AVERROR_INVALIDDATA; + goto end; + } + + for (int i = 1; i < dist->alphabet_size + 1; i++) + level2_codecounts[i] += level2_codecounts[i - 1]; + + for (int i = dist->alphabet_size - 1; i >= 0; i--) { + int idx = --level2_codecounts[level2_lens[i]]; + level2_lens_s[idx] = level2_lens[i]; + level2_syms[idx] = i; + } + + ret = ff_vlc_init_from_lengths(&dist->vlc, 15, dist->alphabet_size, level2_lens_s, + 1, level2_syms, 2, 2, 0, VLC_INIT_LE, dec->logctx); + +end: + av_freep(&buf); + ff_vlc_free(&level1_vlc); + + return ret; +} + +static int read_distribution_bundle(GetBitContext *gb, JXLEntropyDecoder *dec, + JXLDistributionBundle *bundle, int num_dist, int disallow_lz77) +{ + int ret; + + if (num_dist <= 0) + return AVERROR(EINVAL); + + bundle->num_dist = num_dist; + bundle->lz77_enabled = get_bits1(gb); + if (bundle->lz77_enabled) { + if (disallow_lz77) + return AVERROR_INVALIDDATA; + bundle->lz77_min_symbol = jxl_u32(gb, 224, 512, 4096, 8, 0, 0, 0, 15); + bundle->lz77_min_length = jxl_u32(gb, 3, 4, 5, 9, 0, 0, 2, 8); + bundle->num_dist++; + ret = read_hybrid_uint_conf(gb, &bundle->lz_len_conf, 8); + if (ret < 0) + return ret; + } + + if (bundle->lz77_enabled && !dec->window) { + dec->window = av_malloc_array(1 << 20, sizeof(uint32_t)); + if (!dec->window) + return AVERROR(ENOMEM); + } + + ret = read_dist_clustering(gb, dec, bundle); + if (ret < 0) + return ret; + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + + bundle->dists = av_calloc(bundle->num_clusters, sizeof(JXLSymbolDistribution)); + if (!bundle->dists) + return AVERROR(ENOMEM); + + bundle->use_prefix_code = get_bits1(gb); + bundle->log_alphabet_size = bundle->use_prefix_code ? 15 : 5 + get_bits(gb, 2); + + for (int i = 0; i < bundle->num_clusters; i++) { + ret = read_hybrid_uint_conf(gb, &bundle->dists[i].config, bundle->log_alphabet_size); + if (ret < 0) + return ret; + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + } + + if (bundle->use_prefix_code) { + for (int i = 0; i < bundle->num_clusters; i++) { + JXLSymbolDistribution *dist = &bundle->dists[i]; + if (get_bits1(gb)) { + int n = get_bits(gb, 4); + dist->alphabet_size = 1 + (1 << n) + get_bitsz(gb, n); + if (dist->alphabet_size > MAX_PREFIX_ALPHABET_SIZE) + return AVERROR_INVALIDDATA; + } else { + dist->alphabet_size = 1; + } + dist->log_alphabet_size = clog1p(dist->alphabet_size - 1); + } + for (int i = 0; i < bundle->num_clusters; i++) { + ret = read_vlc_prefix(gb, dec, &bundle->dists[i]); + if (ret < 0) + return ret; + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + } + } else { + for (int i = 0; i < bundle->num_clusters; i++) { + ret = populate_distribution(gb, &bundle->dists[i], bundle->log_alphabet_size); + if (ret < 0) + return ret; + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + } + for (int i = 0; i < bundle->num_clusters; i++) { + ret = gen_alias_map(dec, &bundle->dists[i], bundle->log_alphabet_size); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static void entropy_decoder_close(JXLEntropyDecoder *dec) +{ + if (!dec) + return; + av_freep(&dec->window); + dist_bundle_close(&dec->bundle); +} + +static int entropy_decoder_init(void *avctx, GetBitContext *gb, JXLEntropyDecoder *dec, int num_dist) +{ + int ret; + + memset(dec, 0, sizeof(*dec)); + dec->logctx = avctx; + dec->state = -1; + + ret = read_distribution_bundle(gb, dec, &dec->bundle, num_dist, 0); + if (ret < 0) { + entropy_decoder_close(dec); + return ret; + } + + return 0; +} + +static int64_t entropy_decoder_read_symbol(GetBitContext *gb, JXLEntropyDecoder *dec, uint32_t context) +{ + int ret; + uint32_t hybrid_uint; + + ret = decode_hybrid_varlen_uint(gb, dec, &dec->bundle, context, &hybrid_uint); + if (ret < 0) + return ret; + + return hybrid_uint; +} + +static inline uint32_t icc_context(uint64_t i, uint32_t b1, uint32_t b2) +{ + uint32_t p1, p2; + if (i <= 128) + return 0; + if (b1 >= 'a' && b1 <= 'z' || b1 >= 'A' && b1 <= 'Z') + p1 = 0; + else if (b1 >= '0' && b1 <= '9' || b1 == '.' || b1 == ',') + p1 = 1; + else if (b1 <= 1) + p1 = b1 + 2; + else if (b1 > 1 && b1 < 16) + p1 = 4; + else if (b1 > 240 && b1 < 255) + p1 = 5; + else if (b1 == 255) + p1 = 6; + else + p1 = 7; + + if (b2 >= 'a' && b2 <= 'z' || b2 >= 'A' && b2 <= 'Z') + p2 = 0; + else if (b2 >= '0' && b2 <= '9' || b2 == '.' || b2 == ',') + p2 = 1; + else if (b2 < 16) + p2 = 2; + else if (b2 > 240) + p2 = 3; + else + p2 = 4; + + return 1 + p1 + p2 * 8; +} + +static inline uint32_t toc_context(uint32_t x) +{ + return FFMIN(7, clog1p(x)); +} + +static void populate_fields(AVCodecParserContext *s, AVCodecContext *avctx, const FFJXLMetadata *meta) +{ + s->width = meta->width; + s->height = meta->height; + + switch (meta->csp) { + case JPEGXL_CS_RGB: + case JPEGXL_CS_XYB: + avctx->colorspace = AVCOL_SPC_RGB; + break; + default: + avctx->colorspace = AVCOL_SPC_UNSPECIFIED; + } + + if (meta->wp == JPEGXL_WP_D65) { + switch (meta->primaries) { + case JPEGXL_PR_SRGB: + avctx->color_primaries = AVCOL_PRI_BT709; + break; + case JPEGXL_PR_P3: + avctx->color_primaries = AVCOL_PRI_SMPTE432; + break; + case JPEGXL_PR_2100: + avctx->color_primaries = AVCOL_PRI_BT2020; + break; + default: + avctx->color_primaries = AVCOL_PRI_UNSPECIFIED; + } + } else if (meta->wp == JPEGXL_WP_DCI && meta->primaries == JPEGXL_PR_P3) { + avctx->color_primaries = AVCOL_PRI_SMPTE431; + } else { + avctx->color_primaries = AVCOL_PRI_UNSPECIFIED; + } + + if (meta->trc > JPEGXL_TR_GAMMA) { + FFJXLTransferCharacteristic trc = meta->trc - JPEGXL_TR_GAMMA; + switch (trc) { + case JPEGXL_TR_BT709: + avctx->color_trc = AVCOL_TRC_BT709; + break; + case JPEGXL_TR_LINEAR: + avctx->color_trc = AVCOL_TRC_LINEAR; + break; + case JPEGXL_TR_SRGB: + avctx->color_trc = AVCOL_TRC_IEC61966_2_1; + break; + case JPEGXL_TR_PQ: + avctx->color_trc = AVCOL_TRC_SMPTEST2084; + break; + case JPEGXL_TR_DCI: + avctx->color_trc = AVCOL_TRC_SMPTE428; + break; + case JPEGXL_TR_HLG: + avctx->color_trc = AVCOL_TRC_ARIB_STD_B67; + break; + default: + avctx->color_trc = AVCOL_TRC_UNSPECIFIED; + } + } else if (meta->trc > 0) { + if (meta->trc > 45355 && meta->trc < 45555) + avctx->color_trc = AVCOL_TRC_GAMMA22; + else if (meta->trc > 35614 && meta->trc < 35814) + avctx->color_trc = AVCOL_TRC_GAMMA28; + else + avctx->color_trc = AVCOL_TRC_UNSPECIFIED; + } else { + avctx->color_trc = AVCOL_TRC_UNSPECIFIED; + } + + if (meta->csp == JPEGXL_CS_GRAY) { + if (meta->bit_depth <= 8) + s->format = meta->have_alpha ? AV_PIX_FMT_YA8 : AV_PIX_FMT_GRAY8; + else if (meta->bit_depth <= 16) + s->format = meta->have_alpha ? AV_PIX_FMT_YA16 : AV_PIX_FMT_GRAY16; + else + s->format = meta->have_alpha ? AV_PIX_FMT_NONE : AV_PIX_FMT_GRAYF32; + } else { + if (meta->bit_depth <= 8) + s->format = meta->have_alpha ? AV_PIX_FMT_RGBA : AV_PIX_FMT_RGB24; + else if (meta->bit_depth <= 16) + s->format = meta->have_alpha ? AV_PIX_FMT_RGBA64 : AV_PIX_FMT_RGB48; + else + s->format = meta->have_alpha ? AV_PIX_FMT_RGBAF32 : AV_PIX_FMT_RGBF32; + } +} + +static int skip_icc_profile(void *avctx, JXLParseContext *ctx, GetBitContext *gb) +{ + int64_t ret; + uint32_t last = 0, last2 = 0; + JXLEntropyDecoder dec = { 0 }; + uint64_t enc_size = jxl_u64(gb); + uint64_t output_size = 0; + int out_size_shift = 0; + + if (!enc_size || enc_size > (1 << 22)) + return AVERROR_INVALIDDATA; + + ret = entropy_decoder_init(avctx, gb, &dec, 41); + if (ret < 0) + goto end; + + if (get_bits_left(gb) < 0) { + ret = AVERROR_BUFFER_TOO_SMALL; + goto end; + } + + for (uint64_t read = 0; read < enc_size; read++) { + ret = entropy_decoder_read_symbol(gb, &dec, icc_context(read, last, last2)); + if (ret < 0) + goto end; + if (ret > 255) { + ret = AVERROR_INVALIDDATA; + goto end; + } + if (get_bits_left(gb) < 0) { + ret = AVERROR_BUFFER_TOO_SMALL; + goto end; + } + last2 = last; + last = ret; + if (out_size_shift < 63) { + output_size += (ret & UINT64_C(0x7F)) << out_size_shift; + if (!(ret & 0x80)) { + out_size_shift = 63; + } else { + out_size_shift += 7; + if (out_size_shift > 56) { + ret = AVERROR_INVALIDDATA; + goto end; + } + } + } else if (output_size < 132) { + ret = AVERROR_INVALIDDATA; + goto end; + } + } + + ret = 0; + +end: + entropy_decoder_close(&dec); + + return ret; +} + +static int skip_extensions(GetBitContext *gb) +{ + uint64_t extensions = jxl_u64(gb), extensions_len = 0; + + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + + if (!extensions) + return 0; + + for (int i = 0; i < 64; i++) { + if (extensions & (UINT64_C(1) << i)) + extensions_len += jxl_u64(gb); + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + } + + if (extensions_len > INT_MAX || get_bits_left(gb) < extensions_len) + return AVERROR_BUFFER_TOO_SMALL; + + skip_bits_long(gb, extensions_len); + + return 0; +} + +static int parse_frame_header(void *avctx, JXLParseContext *ctx, GetBitContext *gb) +{ + int all_default, do_yCbCr = 0, num_passes = 1, ret; + int group_size_shift = 1, lf_level = 0, save_as_ref = 0; + int have_crop = 0, full_frame = 1, resets_canvas = 1, upsampling = 1; + JXLFrame *frame = &ctx->codestream.frame; + const FFJXLMetadata *meta = &ctx->codestream.meta; + int32_t x0 = 0, y0 = 0; + uint32_t duration = 0, width = meta->coded_width, height = meta->coded_height; + uint32_t name_len, num_groups, num_lf_groups, group_dim, lf_group_dim, toc_count; + uint64_t flags = 0; + int start_len = get_bits_count(gb); + + memset(frame, 0, sizeof(*frame)); + frame->is_last = 1; + + all_default = get_bits1(gb); + if (!all_default) { + frame->type = get_bits(gb, 2); + frame->encoding = get_bits1(gb); + flags = jxl_u64(gb); + if (!meta->xyb_encoded) + do_yCbCr = get_bits1(gb); + if (!(flags & JXL_FLAG_USE_LF_FRAME)) { + if (do_yCbCr) + skip_bits(gb, 6); // jpeg upsampling + upsampling = jxl_u32(gb, 1, 2, 4, 8, 0, 0, 0, 0); + skip_bits_long(gb, 2 * meta->num_extra_channels); + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + } + if (frame->encoding == JPEGXL_ENC_MODULAR) + group_size_shift = get_bits(gb, 2); + else if (meta->xyb_encoded) + skip_bits(gb, 6); // xqm and bqm scales + if (frame->type != JPEGXL_FRAME_REFERENCE_ONLY) { + num_passes = jxl_u32(gb, 1, 2, 3, 4, 0, 0, 0, 3); + if (num_passes != 1) { + int num_ds = jxl_u32(gb, 0, 1, 2, 3, 0, 0, 0, 1); + skip_bits(gb, 2 * (num_passes - 1)); // shift + skip_bits(gb, 2 * num_ds); // downsample + for (int i = 0; i < num_ds; i++) + jxl_u32(gb, 0, 1, 2, 0, 0, 0, 0, 3); + } + } + if (frame->type == JPEGXL_FRAME_LF) + lf_level = 1 + get_bits(gb, 2); + else + have_crop = get_bits1(gb); + if (have_crop) { + if (frame->type != JPEGXL_FRAME_REFERENCE_ONLY) { + uint32_t ux0 = jxl_u32(gb, 0, 256, 2304, 18688, 8, 11, 14, 30); + uint32_t uy0 = jxl_u32(gb, 0, 256, 2304, 18688, 8, 11, 14, 30); + x0 = unpack_signed(ux0); + y0 = unpack_signed(uy0); + } + width = jxl_u32(gb, 0, 256, 2304, 18688, 8, 11, 14, 30); + height = jxl_u32(gb, 0, 256, 2304, 18688, 8, 11, 14, 30); + full_frame = x0 <= 0 && y0 <= 0 && width + x0 >= meta->coded_width + && height + y0 >= meta->coded_height; + } + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + if (frame->type == JPEGXL_FRAME_REGULAR || frame->type == JPEGXL_FRAME_SKIP_PROGRESSIVE) { + for (int i = 0; i <= meta->num_extra_channels; i++) { + int mode = jxl_u32(gb, 0, 1, 2, 3, 0, 0, 0, 2); + if (meta->num_extra_channels && (mode == JPEGXL_BM_BLEND || mode == JPEGXL_BM_MULADD)) + jxl_u32(gb, 0, 1, 2, 3, 0, 0, 0, 2); + if (meta->num_extra_channels && (mode == JPEGXL_BM_BLEND || mode == JPEGXL_BM_MULADD + || mode == JPEGXL_BM_MUL)) + skip_bits1(gb); + if (!i) + resets_canvas = mode == JPEGXL_BM_REPLACE && full_frame; + if (!resets_canvas) + skip_bits(gb, 2); + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + } + if (meta->animation_offset) + duration = jxl_u32(gb, 0, 1, 0, 0, 0, 0, 8, 32); + if (meta->have_timecodes) + skip_bits_long(gb, 32); + frame->is_last = get_bits1(gb); + } else { + frame->is_last = 0; + } + if (frame->type != JPEGXL_FRAME_LF && !frame->is_last) + save_as_ref = get_bits(gb, 2); + if (frame->type == JPEGXL_FRAME_REFERENCE_ONLY || + (resets_canvas && !frame->is_last && (!duration || save_as_ref) + && frame->type != JPEGXL_FRAME_LF)) + skip_bits1(gb); // save before color transform + name_len = 8 * jxl_u32(gb, 0, 0, 16, 48, 0, 4, 5, 10); + if (get_bits_left(gb) < name_len) + return AVERROR_BUFFER_TOO_SMALL; + skip_bits_long(gb, name_len); + } + + if (!all_default) { + int restd = get_bits1(gb), gab = 1; + if (!restd) + gab = get_bits1(gb); + if (gab && !restd && get_bits1(gb)) + // gab custom + skip_bits_long(gb, 16 * 6); + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + if (!restd) { + int epf = get_bits(gb, 2); + if (epf) { + if (frame->encoding == JPEGXL_ENC_VARDCT && get_bits1(gb)) { + skip_bits_long(gb, 16 * 8); // custom epf sharpness + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + } + if (get_bits1(gb)) { + skip_bits_long(gb, 3 * 16 + 32); // custom epf weight + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + } + if (get_bits1(gb)) { // custom epf sigma + if (frame->encoding == JPEGXL_ENC_VARDCT) + skip_bits(gb, 16); + skip_bits_long(gb, 16 * 3); + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + } + if (frame->encoding == JPEGXL_ENC_MODULAR) + skip_bits(gb, 16); + } + ret = skip_extensions(gb); + if (ret < 0) + return ret; + } + ret = skip_extensions(gb); + if (ret < 0) + return ret; + } + + width = div_ceil(div_ceil(width, upsampling), 1 << (3 * lf_level)); + height = div_ceil(div_ceil(height, upsampling), 1 << (3 * lf_level)); + group_dim = 128 << group_size_shift; + lf_group_dim = group_dim << 3; + num_groups = div_ceil(width, group_dim) * div_ceil(height, group_dim); + num_lf_groups = div_ceil(width, lf_group_dim) * div_ceil(height, lf_group_dim); + if (num_groups == 1 && num_passes == 1) + toc_count = 1; + else + toc_count = 2 + num_lf_groups + num_groups * num_passes; + + // permuted toc + if (get_bits1(gb)) { + JXLEntropyDecoder dec; + uint32_t end, lehmer = 0; + ret = entropy_decoder_init(avctx, gb, &dec, 8); + if (ret < 0) + return ret; + if (get_bits_left(gb) < 0) { + entropy_decoder_close(&dec); + return AVERROR_BUFFER_TOO_SMALL; + } + end = entropy_decoder_read_symbol(gb, &dec, toc_context(toc_count)); + if (end > toc_count) { + entropy_decoder_close(&dec); + return AVERROR_INVALIDDATA; + } + for (uint32_t i = 0; i < end; i++) { + lehmer = entropy_decoder_read_symbol(gb, &dec, toc_context(lehmer)); + if (get_bits_left(gb) < 0) { + entropy_decoder_close(&dec); + return AVERROR_BUFFER_TOO_SMALL; + } + } + entropy_decoder_close(&dec); + } + align_get_bits(gb); + + for (uint32_t i = 0; i < toc_count; i++) { + frame->body_length += 8 * jxl_u32(gb, 0, 1024, 17408, 4211712, 10, 14, 22, 30); + if (get_bits_left(gb) < 0) + return AVERROR_BUFFER_TOO_SMALL; + } + align_get_bits(gb); + + frame->total_length = frame->body_length + get_bits_count(gb) - start_len; + + return 0; +} + +static int skip_boxes(JXLParseContext *ctx, const uint8_t *buf, int buf_size) +{ + GetByteContext gb; + + if (ctx->skip > buf_size) + return AVERROR_BUFFER_TOO_SMALL; + + buf += ctx->skip; + buf_size -= ctx->skip; + bytestream2_init(&gb, buf, buf_size); + + while (1) { + uint64_t size; + int head_size = 8; + + if (bytestream2_peek_le16(&gb) == FF_JPEGXL_CODESTREAM_SIGNATURE_LE) + break; + if (bytestream2_peek_le64(&gb) == FF_JPEGXL_CONTAINER_SIGNATURE_LE) + break; + + if (bytestream2_get_bytes_left(&gb) < 8) + return AVERROR_BUFFER_TOO_SMALL; + + size = bytestream2_get_be32(&gb); + bytestream2_skip(&gb, 4); // tag + if (size == 1) { + if (bytestream2_get_bytes_left(&gb) < 8) + return AVERROR_BUFFER_TOO_SMALL; + size = bytestream2_get_be64(&gb); + head_size = 16; + } + if (!size) + return AVERROR_INVALIDDATA; + /* invalid ISOBMFF size */ + if (size <= head_size || size > INT_MAX - ctx->skip) + return AVERROR_INVALIDDATA; + + ctx->skip += size; + bytestream2_skip(&gb, size - head_size); + if (bytestream2_get_bytes_left(&gb) <= 0) + return AVERROR_BUFFER_TOO_SMALL; + } + + return 0; +} + +static int try_parse(AVCodecParserContext *s, AVCodecContext *avctx, JXLParseContext *ctx, + const uint8_t *buf, int buf_size) +{ + int ret, cs_buflen, header_skip; + const uint8_t *cs_buffer; + GetBitContext gb; + + if (ctx->skip > buf_size) + return AVERROR_BUFFER_TOO_SMALL; + + buf += ctx->skip; + buf_size -= ctx->skip; + + if (ctx->container || AV_RL64(buf) == FF_JPEGXL_CONTAINER_SIGNATURE_LE) { + ctx->container = 1; + ret = ff_jpegxl_collect_codestream_header(buf, buf_size, ctx->cs_buffer, + sizeof(ctx->cs_buffer) - AV_INPUT_BUFFER_PADDING_SIZE, &ctx->copied); + if (ret < 0) + return ret; + ctx->collected_size = ret; + if (!ctx->copied) { + ctx->skip += ret; + return AVERROR_BUFFER_TOO_SMALL; + } + cs_buffer = ctx->cs_buffer; + cs_buflen = FFMIN(sizeof(ctx->cs_buffer) - AV_INPUT_BUFFER_PADDING_SIZE, ctx->copied); + } else { + cs_buffer = buf; + cs_buflen = buf_size; + } + + if (!ctx->codestream_length) { + header_skip = ff_jpegxl_parse_codestream_header(cs_buffer, cs_buflen, &ctx->codestream.meta, 0); + if (header_skip < 0) + return header_skip; + ctx->codestream_length = header_skip; + populate_fields(s, avctx, &ctx->codestream.meta); + } + + if (ctx->container) + return ctx->collected_size; + + ret = init_get_bits8(&gb, cs_buffer, cs_buflen); + if (ret < 0) + return ret; + + skip_bits_long(&gb, ctx->codestream_length); + + if (!ctx->skipped_icc && ctx->codestream.meta.have_icc_profile) { + ret = skip_icc_profile(avctx, ctx, &gb); + if (ret < 0) + return ret; + ctx->skipped_icc = 1; + align_get_bits(&gb); + ctx->codestream_length = get_bits_count(&gb); + } + + if (get_bits_left(&gb) <= 0) + return AVERROR_BUFFER_TOO_SMALL; + + while (1) { + ret = parse_frame_header(avctx, ctx, &gb); + if (ret < 0) + return ret; + ctx->codestream_length += ctx->codestream.frame.total_length; + if (ctx->codestream.frame.is_last) + return ctx->codestream_length / 8; + if (get_bits_left(&gb) <= ctx->codestream.frame.body_length) + return AVERROR_BUFFER_TOO_SMALL; + skip_bits_long(&gb, ctx->codestream.frame.body_length); + } +} + +static int jpegxl_parse(AVCodecParserContext *s, AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + JXLParseContext *ctx = s->priv_data; + int next = END_NOT_FOUND, ret; + const uint8_t *pbuf = ctx->pc.buffer; + int pindex = ctx->pc.index; + + *poutbuf_size = 0; + *poutbuf = NULL; + + if (!ctx->pc.index) { + if (ctx->pc.overread) + goto flush; + pbuf = buf; + pindex = buf_size; + } + + if ((!ctx->container || !ctx->codestream_length) && !ctx->next) { + ret = try_parse(s, avctx, ctx, pbuf, pindex); + if (ret < 0) + goto flush; + ctx->next = ret; + if (ctx->container) + ctx->skip += ctx->next; + } + + if (ctx->container && ctx->next >= 0) { + ret = skip_boxes(ctx, pbuf, pindex); + if (ret < 0) { + if (ret == AVERROR_INVALIDDATA) + ctx->next = -1; + goto flush; + } + ctx->next = ret + ctx->skip; + } + + if (ctx->next >= 0) + next = ctx->next - ctx->pc.index; + +flush: + if (next > buf_size) + next = END_NOT_FOUND; + + ret = ff_combine_frame(&ctx->pc, next, &buf, &buf_size); + if (ret < 0) + return buf_size; + + *poutbuf = buf; + *poutbuf_size = buf_size; + + ctx->codestream_length = 0; + ctx->collected_size = 0; + ctx->container = 0; + ctx->copied = 0; + ctx->skip = 0; + ctx->skipped_icc = 0; + ctx->next = 0; + memset(&ctx->codestream, 0, sizeof(ctx->codestream)); + + return next; +} + +const AVCodecParser ff_jpegxl_parser = { + .codec_ids = { AV_CODEC_ID_JPEGXL }, + .priv_data_size = sizeof(JXLParseContext), + .parser_parse = jpegxl_parse, + .parser_close = ff_parse_close, +}; diff --git a/libavcodec/jvdec.c b/libavcodec/jvdec.c index e0287a9cb9d..13ede9068a8 100644 --- a/libavcodec/jvdec.c +++ b/libavcodec/jvdec.c @@ -37,7 +37,9 @@ typedef struct JvContext { BlockDSPContext bdsp; AVFrame *frame; uint32_t palette[AVPALETTE_COUNT]; +#if FF_API_PALETTE_HAS_CHANGED int palette_has_changed; +#endif } JvContext; static av_cold int decode_init(AVCodecContext *avctx) @@ -207,14 +209,20 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, s->palette[i] = 0xFFU << 24 | pal << 2 | ((pal >> 4) & 0x30303); buf += 3; } +#if FF_API_PALETTE_HAS_CHANGED s->palette_has_changed = 1; +#endif } if (video_size) { - s->frame->key_frame = 1; + s->frame->flags |= AV_FRAME_FLAG_KEY; s->frame->pict_type = AV_PICTURE_TYPE_I; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS s->frame->palette_has_changed = s->palette_has_changed; s->palette_has_changed = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(s->frame->data[1], s->palette, AVPALETTE_SIZE); if ((ret = av_frame_ref(rframe, s->frame)) < 0) diff --git a/libavcodec/kbdwin.c b/libavcodec/kbdwin.c index bf32aeb3176..cb73ceb1604 100644 --- a/libavcodec/kbdwin.c +++ b/libavcodec/kbdwin.c @@ -17,41 +17,46 @@ */ #include "libavutil/avassert.h" +#include "libavutil/libm.h" #include "libavutil/mathematics.h" #include "libavutil/attributes.h" #include "kbdwin.h" -#define BESSEL_I0_ITER 50 // default: 50 iterations of Bessel I0 approximation - -av_cold void ff_kbd_window_init(float *window, float alpha, int n) +av_cold static void kbd_window_init(float *float_window, int *int_window, float alpha, int n) { - int i, j; - double sum = 0.0, bessel, tmp; - double local_window[FF_KBD_WINDOW_MAX]; - double alpha2 = (alpha * M_PI / n) * (alpha * M_PI / n); + int i; + double sum = 0.0, tmp; + double scale = 0.0; + double temp[FF_KBD_WINDOW_MAX / 2 + 1]; + double alpha2 = 4 * (alpha * M_PI / n) * (alpha * M_PI / n); av_assert0(n <= FF_KBD_WINDOW_MAX); - for (i = 0; i < n; i++) { + for (i = 0; i <= n / 2; i++) { tmp = i * (n - i) * alpha2; - bessel = 1.0; - for (j = BESSEL_I0_ITER; j > 0; j--) - bessel = bessel * tmp / (j * j) + 1; - sum += bessel; - local_window[i] = sum; + temp[i] = av_bessel_i0(sqrt(tmp)); + scale += temp[i] * (1 + (i && ipalette_has_changed = ff_copy_palette(ctx->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + frame->palette_has_changed = +#endif + ff_copy_palette(ctx->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif header = bytestream2_get_byte(&ctx->g); @@ -288,15 +295,19 @@ static int decode_frame(AVCodecContext * avctx, AVFrame *frame, } if (header & KMVC_KEYFRAME) { - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; } else { - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; } if (header & KMVC_PALETTE) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif // palette starts from index 1 and has 127 entries for (i = 1; i <= ctx->palsize; i++) { ctx->pal[i] = 0xFFU << 24 | bytestream2_get_be24(&ctx->g); @@ -305,7 +316,11 @@ static int decode_frame(AVCodecContext * avctx, AVFrame *frame, if (ctx->setpal) { ctx->setpal = 0; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } /* make the palette available on the way out */ diff --git a/libavcodec/lagarith.c b/libavcodec/lagarith.c index 78ccbc15b9c..75b557e518f 100644 --- a/libavcodec/lagarith.c +++ b/libavcodec/lagarith.c @@ -27,6 +27,8 @@ #include +#include "libavutil/thread.h" + #include "avcodec.h" #include "codec_internal.h" #include "get_bits.h" @@ -35,6 +37,8 @@ #include "lossless_videodsp.h" #include "thread.h" +#define VLC_BITS 7 + enum LagarithFrameType { FRAME_RAW = 1, /**< uncompressed */ FRAME_U_RGB24 = 2, /**< unaligned RGB24 */ @@ -56,6 +60,35 @@ typedef struct LagarithContext { int zeros_rem; /**< number of zero bytes remaining to output */ } LagarithContext; +static VLCElem lag_tab[1 << VLC_BITS]; + +static const uint8_t lag_bits[] = { + 7, 7, 2, 7, 3, 4, 5, 6, 7, 7, 7, 7, 7, 6, 7, 4, 5, 7, 7, 7, 7, + 5, 6, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 6, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +}; + +static const uint8_t lag_codes[] = { + 0x01, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x05, + 0x08, 0x09, 0x0A, 0x0B, 0x0B, 0x0B, 0x0B, 0x10, 0x11, 0x12, 0x13, + 0x13, 0x13, 0x14, 0x15, 0x20, 0x21, 0x22, 0x23, 0x23, 0x24, 0x25, + 0x28, 0x29, 0x2A, 0x2B, 0x2B, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, + 0x48, 0x49, 0x4A, 0x4B, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, +}; + +static const uint8_t lag_symbols[] = { + 20, 12, 0, 12, 1, 2, 4, 7, 7, 28, 4, 25, 17, + 10, 17, 3, 6, 2, 23, 15, 15, 5, 9, 10, 31, 1, 22, + 14, 14, 8, 9, 30, 6, 27, 19, 11, 19, 0, 21, 13, 13, + 8, 29, 5, 26, 18, 18, 3, 24, 16, 16, 11, 32, +}; + +static av_cold void lag_init_static_data(void) +{ + VLC_INIT_STATIC_SPARSE_TABLE(lag_tab, VLC_BITS, FF_ARRAY_ELEMS(lag_bits), + lag_bits, 1, 1, lag_codes, 1, 1, lag_symbols, 1, 1, 0); +} + /** * Compute the 52-bit mantissa of 1/(double)denom. * This crazy format uses floats in an entropy coder and we have to match x86 @@ -101,23 +134,10 @@ static uint8_t lag_calc_zero_run(int8_t x) static int lag_decode_prob(GetBitContext *gb, uint32_t *value) { - static const uint8_t series[] = { 1, 2, 3, 5, 8, 13, 21 }; - int i; - int bit = 0; - int bits = 0; - int prevbit = 0; - unsigned val; - - for (i = 0; i < 7; i++) { - if (prevbit && bit) - break; - prevbit = bit; - bit = get_bits1(gb); - if (bit && !prevbit) - bits += series[i]; - } - bits--; - if (bits < 0 || bits > 31) { + unsigned val, bits; + + bits = get_vlc2(gb, lag_tab, VLC_BITS, 1); + if (bits > 31) { *value = 0; return AVERROR_INVALIDDATA; } else if (bits == 0) { @@ -146,17 +166,17 @@ static int lag_read_prob_header(lag_rac *rac, GetBitContext *gb) /* Read probabilities from bitstream */ for (i = 1; i < 257; i++) { if (lag_decode_prob(gb, &rac->prob[i]) < 0) { - av_log(rac->avctx, AV_LOG_ERROR, "Invalid probability encountered.\n"); + av_log(rac->logctx, AV_LOG_ERROR, "Invalid probability encountered.\n"); return AVERROR_INVALIDDATA; } if ((uint64_t)cumul_prob + rac->prob[i] > UINT_MAX) { - av_log(rac->avctx, AV_LOG_ERROR, "Integer overflow encountered in cumulative probability calculation.\n"); + av_log(rac->logctx, AV_LOG_ERROR, "Integer overflow encountered in cumulative probability calculation.\n"); return AVERROR_INVALIDDATA; } cumul_prob += rac->prob[i]; if (!rac->prob[i]) { if (lag_decode_prob(gb, &prob)) { - av_log(rac->avctx, AV_LOG_ERROR, "Invalid probability run encountered.\n"); + av_log(rac->logctx, AV_LOG_ERROR, "Invalid probability run encountered.\n"); return AVERROR_INVALIDDATA; } if (prob > 256 - i) @@ -169,7 +189,7 @@ static int lag_read_prob_header(lag_rac *rac, GetBitContext *gb) } if (!cumul_prob) { - av_log(rac->avctx, AV_LOG_ERROR, "All probabilities are 0!\n"); + av_log(rac->logctx, AV_LOG_ERROR, "All probabilities are 0!\n"); return AVERROR_INVALIDDATA; } @@ -187,7 +207,7 @@ static int lag_read_prob_header(lag_rac *rac, GetBitContext *gb) scaled_cumul_prob += rac->prob[i]; } if (scaled_cumul_prob <= 0) { - av_log(rac->avctx, AV_LOG_ERROR, "Scaled probabilities invalid\n"); + av_log(rac->logctx, AV_LOG_ERROR, "Scaled probabilities invalid\n"); return AVERROR_INVALIDDATA; } for (; i < 257; i++) { @@ -201,7 +221,7 @@ static int lag_read_prob_header(lag_rac *rac, GetBitContext *gb) cumulative_target = 1U << scale_factor; if (scaled_cumul_prob > cumulative_target) { - av_log(rac->avctx, AV_LOG_ERROR, + av_log(rac->logctx, AV_LOG_ERROR, "Scaled probabilities are larger than target!\n"); return AVERROR_INVALIDDATA; } @@ -443,7 +463,7 @@ static int lag_decode_arith_plane(LagarithContext *l, uint8_t *dst, const uint8_t *src_end = src + src_size; int ret; - rac.avctx = l->avctx; + rac.logctx = l->avctx; l->zeros = 0; if(src_size < 2) @@ -550,7 +570,7 @@ static int lag_decode_frame(AVCodecContext *avctx, AVFrame *p, int i, j, planes = 3; int ret = 0; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; frametype = buf[0]; @@ -720,10 +740,12 @@ static int lag_decode_frame(AVCodecContext *avctx, AVFrame *p, static av_cold int lag_decode_init(AVCodecContext *avctx) { + static AVOnce init_static_once = AV_ONCE_INIT; LagarithContext *l = avctx->priv_data; l->avctx = avctx; ff_llviddsp_init(&l->llviddsp); + ff_thread_once(&init_static_once, lag_init_static_data); return 0; } diff --git a/libavcodec/lagarithrac.h b/libavcodec/lagarithrac.h index a31b054dbba..2c8cb7385a1 100644 --- a/libavcodec/lagarithrac.h +++ b/libavcodec/lagarithrac.h @@ -32,11 +32,10 @@ #include #include "libavutil/intreadwrite.h" -#include "avcodec.h" #include "get_bits.h" typedef struct lag_rac { - AVCodecContext *avctx; + void *logctx; unsigned low; unsigned range; unsigned scale; /**< Number of bits of precision in range. */ diff --git a/libavcodec/lcldec.c b/libavcodec/lcldec.c index 5bdb43286c4..b4304618e44 100644 --- a/libavcodec/lcldec.c +++ b/libavcodec/lcldec.c @@ -167,7 +167,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, const uint8_t *buf = avpkt->data; int buf_size = avpkt->size; LclDecContext * const c = avctx->priv_data; - unsigned int pixel_ptr; + ptrdiff_t pixel_ptr; int row, col; unsigned char *encoded = avpkt->data, *outptr; uint8_t *y_out, *u_out, *v_out; @@ -221,7 +221,9 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (c->decomp_size != mszh_dlen) { av_log(avctx, AV_LOG_ERROR, "Decoded size differs (%d != %d)\n", c->decomp_size, mszh_dlen); - return AVERROR_INVALIDDATA; + if (c->decomp_size != mszh_dlen && + c->decomp_size != mszh_dlen + 2) // YUV420 306x306 is missing 2 bytes + return AVERROR_INVALIDDATA; } encoded = c->decomp_buf; len = mszh_dlen; @@ -479,7 +481,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return AVERROR_INVALIDDATA; } - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/leaddata.h b/libavcodec/leaddata.h new file mode 100644 index 00000000000..525b582cf72 --- /dev/null +++ b/libavcodec/leaddata.h @@ -0,0 +1,62 @@ +/* + * LEAD MCMP decoder tables + * + * 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 + */ + +#ifndef AVCODEC_LEADDATA_H +#define AVCODEC_LEADDATA_H + +#include + +static const uint8_t luma_dc_len[]={ + 2, 3, 3, 3, 3, 3, 4, 5, 6, 7, 8, 9 +}; + +static const uint8_t chroma_dc_len[]={ + 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +static const uint8_t luma_ac_len[]={ + 2, 2, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 7, 7, 8, + 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, + 12, 12, 12, 12, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16 +}; + +static const uint8_t chroma_ac_len[]={ + 2, 2, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 12, 12, 12, 12, 14, 15, 15, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16 +}; + +#endif /* AVCODEC_LEADDATA_H */ diff --git a/libavcodec/leaddec.c b/libavcodec/leaddec.c new file mode 100644 index 00000000000..a6f6c3cc873 --- /dev/null +++ b/libavcodec/leaddec.c @@ -0,0 +1,316 @@ +/* + * LEAD MCMP decoder + * + * Copyright (c) 2023 Peter Ross + * + * 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 "avcodec.h" +#include "blockdsp.h" +#include "codec_internal.h" +#include "copy_block.h" +#include "decode.h" +#include "get_bits.h" +#include "idctdsp.h" +#include "jpegquanttables.h" +#include "jpegtables.h" +#include "leaddata.h" +#include "libavutil/mem_internal.h" +#include "libavutil/thread.h" + +#define LUMA_DC_BITS 9 +#define CHROMA_DC_BITS 11 +#define LUMA_AC_BITS 10 +#define CHROMA_AC_BITS 10 + +static VLCElem luma_dc_vlc[1 << LUMA_DC_BITS]; +static VLCElem chroma_dc_vlc[1 << CHROMA_DC_BITS]; +static VLCElem luma_ac_vlc[1160]; +static VLCElem chroma_ac_vlc[1160]; + +static av_cold void lead_init_static_data(void) +{ + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(luma_dc_vlc, LUMA_DC_BITS, FF_ARRAY_ELEMS(luma_dc_len), + luma_dc_len, 1, + NULL, 0, 0, + 0, 0); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(chroma_dc_vlc, CHROMA_DC_BITS, FF_ARRAY_ELEMS(chroma_dc_len), + chroma_dc_len, 1, + NULL, 0, 0, + 0, 0); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(luma_ac_vlc, LUMA_AC_BITS, FF_ARRAY_ELEMS(luma_ac_len), + luma_ac_len, 1, + ff_mjpeg_val_ac_luminance, 1, 1, + 0, 0); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(chroma_ac_vlc, CHROMA_AC_BITS, FF_ARRAY_ELEMS(chroma_ac_len), + chroma_ac_len, 1, + ff_mjpeg_val_ac_chrominance, 1, 1, + 0, 0); +} + +typedef struct LeadContext { + uint8_t *bitstream_buf; + unsigned int bitstream_buf_size; + BlockDSPContext bdsp; + IDCTDSPContext idsp; + uint8_t permutated_scantable[64]; +} LeadContext; + +static av_cold int lead_decode_init(AVCodecContext * avctx) +{ + static AVOnce init_static_once = AV_ONCE_INIT; + LeadContext *s = avctx->priv_data; + + if (avctx->extradata_size < 20) + return AVERROR_INVALIDDATA; + + ff_blockdsp_init(&s->bdsp); + ff_idctdsp_init(&s->idsp, avctx); + ff_permute_scantable(s->permutated_scantable, ff_zigzag_direct, s->idsp.idct_permutation); + + ff_thread_once(&init_static_once, lead_init_static_data); + + return 0; +} + +static void calc_dequant(uint16_t * dequant, const uint8_t * quant_tbl, int q) +{ + for (int i = 0; i < 64; i++) + dequant[i] = av_clip(q * quant_tbl[ff_zigzag_direct[i]] / 50, 2, 32767); +} + +static int decode_block(LeadContext * s, GetBitContext * gb, + const VLCElem * dc_table, int dc_bits, const VLCElem * ac_table, int ac_bits, + int16_t * dc_pred, const uint16_t * dequant, + uint8_t * dst, int stride) +{ + DECLARE_ALIGNED(32, int16_t, block)[64]; + int size; + + s->bdsp.clear_block(block); + + if (get_bits_left(gb) <= 0) + return AVERROR_INVALIDDATA; + + size = get_vlc2(gb, dc_table, dc_bits, 1); + if (size < 0) + return AVERROR_INVALIDDATA; + + if (size) + *dc_pred += get_xbits(gb, size); + + block[0] = (1 << 10) + *dc_pred * dequant[0]; + + for (int i = 1; i < 64; i++) { + int symbol = get_vlc2(gb, ac_table, ac_bits, 2); + if (symbol < 0) + return AVERROR_INVALIDDATA; + + if (!symbol) + break; + + i += symbol >> 4; + if (i >= 64) + return AVERROR_INVALIDDATA; + + size = symbol & 0xF; + if (size) + block[s->permutated_scantable[i]] = get_xbits(gb, size) * dequant[i]; + } + + s->idsp.idct_put(dst, stride, block); + return 0; +} + +static int lead_decode_frame(AVCodecContext *avctx, AVFrame * frame, + int * got_frame, AVPacket * avpkt) +{ + LeadContext *s = avctx->priv_data; + const uint8_t * buf = avpkt->data; + int ret, format, zero = 0, yuv20p_half = 0, fields = 1, q, size; + GetBitContext gb; + int16_t dc_pred[3] = {0, 0, 0}; + uint16_t dequant[2][64]; + + if (avpkt->size < 8) + return AVERROR_INVALIDDATA; + + format = AV_RL16(buf + 4); + switch(format) { + case 0x0: + zero = 1; + avctx->pix_fmt = AV_PIX_FMT_YUV420P; + break; + case 0x8000: + yuv20p_half = 1; + // fall-through + case 0x1000: + avctx->pix_fmt = AV_PIX_FMT_YUV420P; + break; + case 0x2000: + avctx->pix_fmt = AV_PIX_FMT_YUV444P; + break; + case 0x2006: + avctx->pix_fmt = AV_PIX_FMT_YUV444P; + fields = 2; + break; + default: + avpriv_request_sample(avctx, "unsupported format 0x%x", format); + return AVERROR_PATCHWELCOME; + } + + q = AV_RL16(buf + 6); + calc_dequant(dequant[0], ff_mjpeg_std_luminance_quant_tbl, q); + calc_dequant(dequant[1], ff_mjpeg_std_chrominance_quant_tbl, q); + + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + return ret; + + frame->flags |= AV_FRAME_FLAG_KEY; + frame->pict_type = AV_PICTURE_TYPE_I; + + av_fast_padded_malloc(&s->bitstream_buf, &s->bitstream_buf_size, avpkt->size - 8); + if (!s->bitstream_buf) + return AVERROR(ENOMEM); + + size = 0; + for (int i = 8; i < avpkt->size; i++) { + int src = buf[i] ^ 0x80; + s->bitstream_buf[size++] = src; + if (src == 0xFF && i + 1 < avpkt->size && (buf[i + 1] ^ 0x80) == 0x00) + i++; + } + + ret = init_get_bits8(&gb, s->bitstream_buf, size); + if (ret < 0) + return ret; + + if (avctx->pix_fmt == AV_PIX_FMT_YUV420P && zero) { + for (int mb_y = 0; mb_y < avctx->height / 8; mb_y++) + for (int mb_x = 0; mb_x < avctx->width / 16; mb_x++) + for (int b = 0; b < 4; b++) { + int luma_block = 2; + const VLCElem * dc_vlc = b < luma_block ? luma_dc_vlc : chroma_dc_vlc; + int dc_bits = b < luma_block ? LUMA_DC_BITS : CHROMA_DC_BITS; + const VLCElem * ac_vlc = b < luma_block ? luma_ac_vlc : chroma_ac_vlc; + int ac_bits = b < luma_block ? LUMA_AC_BITS : CHROMA_AC_BITS; + int plane = b < luma_block ? 0 : b - 1; + int x, y, yclip; + + if (b < luma_block) { + y = 8*mb_y + 8*(b >> 1); + x = 16*mb_x + 8*(b & 1); + yclip = 0; + } else { + y = 4*mb_y; + x = 8*mb_x; + yclip = y + 8 >= avctx->height / 2; + } + + if (yclip) { + uint8_t tmp[64]; + ret = decode_block(s, &gb, dc_vlc, dc_bits, ac_vlc, ac_bits, + dc_pred + plane, dequant[!(b < 4)], tmp, 8); + for (int yy = 0; yy < 8 && y + yy < avctx->height / 2; yy++) + memcpy(frame->data[plane] + (y+yy)*frame->linesize[plane] + x, tmp + yy, 8); + } else { + ret = decode_block(s, &gb, dc_vlc, dc_bits, ac_vlc, ac_bits, + dc_pred + plane, dequant[!(b < 4)], + frame->data[plane] + y*frame->linesize[plane] + x, + frame->linesize[plane]); + } + if (ret < 0) + return ret; + } + } else if (avctx->pix_fmt == AV_PIX_FMT_YUV420P) { + for (int mb_y = 0; mb_y < (avctx->height + 15) / 16; mb_y++) + for (int mb_x = 0; mb_x < (avctx->width + 15) / 16; mb_x++) + for (int b = 0; b < (yuv20p_half ? 4 : 6); b++) { + int luma_block = yuv20p_half ? 2 : 4; + const VLCElem * dc_vlc = b < luma_block ? luma_dc_vlc : chroma_dc_vlc; + int dc_bits = b < luma_block ? LUMA_DC_BITS : CHROMA_DC_BITS; + const VLCElem * ac_vlc = b < luma_block ? luma_ac_vlc : chroma_ac_vlc; + int ac_bits = b < luma_block ? LUMA_AC_BITS : CHROMA_AC_BITS; + int plane = b < luma_block ? 0 : b - (yuv20p_half ? 1 : 3); + int x, y; + + if (b < luma_block) { + y = 16*mb_y + 8*(b >> 1); + x = 16*mb_x + 8*(b & 1); + } else { + y = 8*mb_y; + x = 8*mb_x; + } + + ret = decode_block(s, &gb, dc_vlc, dc_bits, ac_vlc, ac_bits, + dc_pred + plane, dequant[!(b < 4)], + frame->data[plane] + y*frame->linesize[plane] + x, + (yuv20p_half && b < 2 ? 2 : 1) * frame->linesize[plane]); + if (ret < 0) + return ret; + + if (yuv20p_half && b < 2) + copy_block8(frame->data[plane] + (y + 1)*frame->linesize[plane] + x, + frame->data[plane] + y*frame->linesize[plane] + x, + 2*frame->linesize[plane], 2*frame->linesize[plane], 8); + } + } else { + for (int f = 0; f < fields; f++) + for (int j = 0; j < (avctx->height + 7) / fields / 8; j++) + for (int i = 0; i < (avctx->width + 7) / 8; i++) + for (int plane = 0; plane < 3; plane++) { + const VLCElem * dc_vlc = !plane ? luma_dc_vlc : chroma_dc_vlc; + int dc_bits = !plane ? LUMA_DC_BITS : CHROMA_DC_BITS; + const VLCElem * ac_vlc = !plane ? luma_ac_vlc : chroma_ac_vlc; + int ac_bits = !plane ? LUMA_AC_BITS : CHROMA_AC_BITS; + + ret = decode_block(s, &gb, dc_vlc, dc_bits, ac_vlc, ac_bits, + dc_pred + plane, dequant[!!plane], + frame->data[plane] + (f + 8*j*fields)*frame->linesize[plane] + 8*i, + fields * frame->linesize[plane]); + if (ret < 0) + return ret; + } + } + + *got_frame = 1; + + return avpkt->size; +} + +static av_cold int lead_decode_end(AVCodecContext * avctx) +{ + LeadContext *s = avctx->priv_data; + + av_freep(&s->bitstream_buf); + + return 0; +} + +const FFCodec ff_lead_decoder = { + .p.name = "lead", + CODEC_LONG_NAME("LEAD MCMP"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_LEAD, + .priv_data_size = sizeof(LeadContext), + .init = lead_decode_init, + .close = lead_decode_end, + FF_CODEC_DECODE_CB(lead_decode_frame), + .p.capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/leb.h b/libavcodec/leb.h new file mode 100644 index 00000000000..5159c434b1e --- /dev/null +++ b/libavcodec/leb.h @@ -0,0 +1,70 @@ +/* + * 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 + * leb128 handling implementations + */ + +#ifndef AVCODEC_LEB_H +#define AVCODEC_LEB_H + +#include "get_bits.h" + +/** + * Read a unsigned integer coded as a variable number of up to eight + * little-endian bytes, where the MSB in a byte signals another byte + * must be read. + * All coded bits are read, but values > UINT_MAX are truncated. + */ +static inline unsigned get_leb(GetBitContext *s) { + int more, i = 0; + unsigned leb = 0; + + do { + int byte = get_bits(s, 8); + unsigned bits = byte & 0x7f; + more = byte & 0x80; + if (i <= 4) + leb |= bits << (i * 7); + if (++i == 8) + break; + } while (more); + + return leb; +} + +/** + * Read a unsigned integer coded as a variable number of up to eight + * little-endian bytes, where the MSB in a byte signals another byte + * must be read. + */ +static inline int64_t get_leb128(GetBitContext *gb) { + int64_t ret = 0; + + for (int i = 0; i < 8; i++) { + int byte = get_bits(gb, 8); + ret |= (int64_t)(byte & 0x7f) << (i * 7); + if (!(byte & 0x80)) + break; + } + + return ret; +} + +#endif /* AVCODEC_LEB_H */ diff --git a/libavcodec/libaomdec.c b/libavcodec/libaomdec.c index 53982559d93..69eec8b089d 100644 --- a/libavcodec/libaomdec.c +++ b/libavcodec/libaomdec.c @@ -48,7 +48,7 @@ static av_cold int aom_init(AVCodecContext *avctx, .threads = FFMIN(avctx->thread_count ? avctx->thread_count : av_cpu_count(), 16) }; - av_log(avctx, AV_LOG_INFO, "%s\n", aom_codec_version_str()); + av_log(avctx, AV_LOG_VERBOSE, "%s\n", aom_codec_version_str()); av_log(avctx, AV_LOG_VERBOSE, "%s\n", aom_codec_build_config()); if (aom_codec_dec_init(&ctx->decoder, iface, &deccfg, 0) != AOM_CODEC_OK) { @@ -78,17 +78,17 @@ static int set_pix_fmt(AVCodecContext *avctx, struct aom_image *img) if (img->bit_depth == 8) { avctx->pix_fmt = img->monochrome ? AV_PIX_FMT_GRAY8 : AV_PIX_FMT_YUV420P; - avctx->profile = FF_PROFILE_AV1_MAIN; + avctx->profile = AV_PROFILE_AV1_MAIN; return 0; } else if (img->bit_depth == 10) { avctx->pix_fmt = img->monochrome ? AV_PIX_FMT_GRAY10 : AV_PIX_FMT_YUV420P10; - avctx->profile = FF_PROFILE_AV1_MAIN; + avctx->profile = AV_PROFILE_AV1_MAIN; return 0; } else if (img->bit_depth == 12) { avctx->pix_fmt = img->monochrome ? AV_PIX_FMT_GRAY12 : AV_PIX_FMT_YUV420P12; - avctx->profile = FF_PROFILE_AV1_PROFESSIONAL; + avctx->profile = AV_PROFILE_AV1_PROFESSIONAL; return 0; } else { return AVERROR_INVALIDDATA; @@ -97,15 +97,15 @@ static int set_pix_fmt(AVCodecContext *avctx, struct aom_image *img) case AOM_IMG_FMT_I42216: if (img->bit_depth == 8) { avctx->pix_fmt = AV_PIX_FMT_YUV422P; - avctx->profile = FF_PROFILE_AV1_PROFESSIONAL; + avctx->profile = AV_PROFILE_AV1_PROFESSIONAL; return 0; } else if (img->bit_depth == 10) { avctx->pix_fmt = AV_PIX_FMT_YUV422P10; - avctx->profile = FF_PROFILE_AV1_PROFESSIONAL; + avctx->profile = AV_PROFILE_AV1_PROFESSIONAL; return 0; } else if (img->bit_depth == 12) { avctx->pix_fmt = AV_PIX_FMT_YUV422P12; - avctx->profile = FF_PROFILE_AV1_PROFESSIONAL; + avctx->profile = AV_PROFILE_AV1_PROFESSIONAL; return 0; } else { return AVERROR_INVALIDDATA; @@ -115,18 +115,18 @@ static int set_pix_fmt(AVCodecContext *avctx, struct aom_image *img) if (img->bit_depth == 8) { avctx->pix_fmt = avctx->colorspace == AVCOL_SPC_RGB ? AV_PIX_FMT_GBRP : AV_PIX_FMT_YUV444P; - avctx->profile = FF_PROFILE_AV1_HIGH; + avctx->profile = AV_PROFILE_AV1_HIGH; return 0; } else if (img->bit_depth == 10) { avctx->pix_fmt = AV_PIX_FMT_YUV444P10; avctx->pix_fmt = avctx->colorspace == AVCOL_SPC_RGB ? AV_PIX_FMT_GBRP10 : AV_PIX_FMT_YUV444P10; - avctx->profile = FF_PROFILE_AV1_HIGH; + avctx->profile = AV_PROFILE_AV1_HIGH; return 0; } else if (img->bit_depth == 12) { avctx->pix_fmt = avctx->colorspace == AVCOL_SPC_RGB ? AV_PIX_FMT_GBRP12 : AV_PIX_FMT_YUV444P12; - avctx->profile = FF_PROFILE_AV1_PROFESSIONAL; + avctx->profile = AV_PROFILE_AV1_PROFESSIONAL; return 0; } else { return AVERROR_INVALIDDATA; @@ -185,7 +185,10 @@ static int aom_decode(AVCodecContext *avctx, AVFrame *picture, aom_codec_frame_flags_t flags; ret = aom_codec_control(&ctx->decoder, AOMD_GET_FRAME_FLAGS, &flags); if (ret == AOM_CODEC_OK) { - picture->key_frame = !!(flags & AOM_FRAME_IS_KEY); + if (flags & AOM_FRAME_IS_KEY) + picture->flags |= AV_FRAME_FLAG_KEY; + else + picture->flags &= ~AV_FRAME_FLAG_KEY; if (flags & (AOM_FRAME_IS_KEY | AOM_FRAME_IS_INTRAONLY)) picture->pict_type = AV_PICTURE_TYPE_I; else if (flags & AOM_FRAME_IS_SWITCH) diff --git a/libavcodec/libaomenc.c b/libavcodec/libaomenc.c index 0b88102c775..8c1f84cc9fb 100644 --- a/libavcodec/libaomenc.c +++ b/libavcodec/libaomenc.c @@ -451,16 +451,16 @@ static int set_pix_fmt(AVCodecContext *avctx, aom_codec_caps_t codec_caps, enccfg->monochrome = 1; /* Fall-through */ case AV_PIX_FMT_YUV420P: - enccfg->g_profile = FF_PROFILE_AV1_MAIN; + enccfg->g_profile = AV_PROFILE_AV1_MAIN; *img_fmt = AOM_IMG_FMT_I420; return 0; case AV_PIX_FMT_YUV422P: - enccfg->g_profile = FF_PROFILE_AV1_PROFESSIONAL; + enccfg->g_profile = AV_PROFILE_AV1_PROFESSIONAL; *img_fmt = AOM_IMG_FMT_I422; return 0; case AV_PIX_FMT_YUV444P: case AV_PIX_FMT_GBRP: - enccfg->g_profile = FF_PROFILE_AV1_HIGH; + enccfg->g_profile = AV_PROFILE_AV1_HIGH; *img_fmt = AOM_IMG_FMT_I444; return 0; case AV_PIX_FMT_GRAY10: @@ -471,7 +471,7 @@ static int set_pix_fmt(AVCodecContext *avctx, aom_codec_caps_t codec_caps, case AV_PIX_FMT_YUV420P12: if (codec_caps & AOM_CODEC_CAP_HIGHBITDEPTH) { enccfg->g_profile = - enccfg->g_bit_depth == 10 ? FF_PROFILE_AV1_MAIN : FF_PROFILE_AV1_PROFESSIONAL; + enccfg->g_bit_depth == 10 ? AV_PROFILE_AV1_MAIN : AV_PROFILE_AV1_PROFESSIONAL; *img_fmt = AOM_IMG_FMT_I42016; *flags |= AOM_CODEC_USE_HIGHBITDEPTH; return 0; @@ -480,7 +480,7 @@ static int set_pix_fmt(AVCodecContext *avctx, aom_codec_caps_t codec_caps, case AV_PIX_FMT_YUV422P10: case AV_PIX_FMT_YUV422P12: if (codec_caps & AOM_CODEC_CAP_HIGHBITDEPTH) { - enccfg->g_profile = FF_PROFILE_AV1_PROFESSIONAL; + enccfg->g_profile = AV_PROFILE_AV1_PROFESSIONAL; *img_fmt = AOM_IMG_FMT_I42216; *flags |= AOM_CODEC_USE_HIGHBITDEPTH; return 0; @@ -492,7 +492,7 @@ static int set_pix_fmt(AVCodecContext *avctx, aom_codec_caps_t codec_caps, case AV_PIX_FMT_GBRP12: if (codec_caps & AOM_CODEC_CAP_HIGHBITDEPTH) { enccfg->g_profile = - enccfg->g_bit_depth == 10 ? FF_PROFILE_AV1_HIGH : FF_PROFILE_AV1_PROFESSIONAL; + enccfg->g_bit_depth == 10 ? AV_PROFILE_AV1_HIGH : AV_PROFILE_AV1_PROFESSIONAL; *img_fmt = AOM_IMG_FMT_I44416; *flags |= AOM_CODEC_USE_HIGHBITDEPTH; return 0; @@ -842,7 +842,7 @@ static av_cold int aom_init(AVCodecContext *avctx, /* 0-3: For non-zero values the encoder increasingly optimizes for reduced * complexity playback on low powered devices at the expense of encode * quality. */ - if (avctx->profile != FF_PROFILE_UNKNOWN) + if (avctx->profile != AV_PROFILE_UNKNOWN) enccfg.g_profile = avctx->profile; enccfg.g_error_resilient = ctx->error_resilient; @@ -1018,7 +1018,7 @@ static av_cold int aom_init(AVCodecContext *avctx, if (codec_caps & AOM_CODEC_CAP_HIGHBITDEPTH) ctx->rawimg.bit_depth = enccfg.g_bit_depth; - cpb_props = ff_add_cpb_side_data(avctx); + cpb_props = ff_encode_add_cpb_side_data(avctx); if (!cpb_props) return AVERROR(ENOMEM); @@ -1295,8 +1295,19 @@ static int aom_encode(AVCodecContext *avctx, AVPacket *pkt, if (frame->duration > ULONG_MAX) { av_log(avctx, AV_LOG_WARNING, "Frame duration too large: %"PRId64"\n", frame->duration); - } else - duration = frame->duration ? frame->duration : avctx->ticks_per_frame; + } else if (frame->duration) + duration = frame->duration; + else if (avctx->framerate.num > 0 && avctx->framerate.den > 0) + duration = av_rescale_q(1, av_inv_q(avctx->framerate), avctx->time_base); + else { +FF_DISABLE_DEPRECATION_WARNINGS + duration = +#if FF_API_TICKS_PER_FRAME + avctx->ticks_per_frame ? avctx->ticks_per_frame : +#endif + 1; +FF_ENABLE_DEPRECATION_WARNINGS + } switch (frame->color_range) { case AVCOL_RANGE_MPEG: @@ -1456,13 +1467,13 @@ static const AVOption options[] = { "alternate reference frame selection", OFFSET(lag_in_frames), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE}, { "arnr-max-frames", "altref noise reduction max frame count", OFFSET(arnr_max_frames), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE}, { "arnr-strength", "altref noise reduction filter strength", OFFSET(arnr_strength), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 6, VE}, - { "aq-mode", "adaptive quantization mode", OFFSET(aq_mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 4, VE, "aq_mode"}, - { "none", "Aq not used", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, VE, "aq_mode"}, - { "variance", "Variance based Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 0, VE, "aq_mode"}, - { "complexity", "Complexity based Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 0, VE, "aq_mode"}, - { "cyclic", "Cyclic Refresh Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 3}, 0, 0, VE, "aq_mode"}, - { "error-resilience", "Error resilience configuration", OFFSET(error_resilient), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, VE, "er"}, - { "default", "Improve resiliency against losses of whole frames", 0, AV_OPT_TYPE_CONST, {.i64 = AOM_ERROR_RESILIENT_DEFAULT}, 0, 0, VE, "er"}, + { "aq-mode", "adaptive quantization mode", OFFSET(aq_mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 4, VE, .unit = "aq_mode"}, + { "none", "Aq not used", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, VE, .unit = "aq_mode"}, + { "variance", "Variance based Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 0, VE, .unit = "aq_mode"}, + { "complexity", "Complexity based Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 0, VE, .unit = "aq_mode"}, + { "cyclic", "Cyclic Refresh Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 3}, 0, 0, VE, .unit = "aq_mode"}, + { "error-resilience", "Error resilience configuration", OFFSET(error_resilient), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, VE, .unit = "er"}, + { "default", "Improve resiliency against losses of whole frames", 0, AV_OPT_TYPE_CONST, {.i64 = AOM_ERROR_RESILIENT_DEFAULT}, 0, 0, VE, .unit = "er"}, { "crf", "Select the quality for constant quality mode", offsetof(AOMContext, crf), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 63, VE }, { "static-thresh", "A change threshold on blocks below which they will be skipped by the encoder", OFFSET(static_thresh), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, { "drop-threshold", "Frame drop threshold", offsetof(AOMContext, drop_threshold), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, VE }, @@ -1481,13 +1492,13 @@ static const AVOption options[] = { { "enable-global-motion", "Enable global motion", OFFSET(enable_global_motion), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE}, { "enable-intrabc", "Enable intra block copy prediction mode", OFFSET(enable_intrabc), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE}, { "enable-restoration", "Enable Loop Restoration filtering", OFFSET(enable_restoration), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE}, - { "usage", "Quality and compression efficiency vs speed trade-off", OFFSET(usage), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, "usage"}, - { "good", "Good quality", 0, AV_OPT_TYPE_CONST, {.i64 = 0 /* AOM_USAGE_GOOD_QUALITY */}, 0, 0, VE, "usage"}, - { "realtime", "Realtime encoding", 0, AV_OPT_TYPE_CONST, {.i64 = 1 /* AOM_USAGE_REALTIME */}, 0, 0, VE, "usage"}, - { "allintra", "All Intra encoding", 0, AV_OPT_TYPE_CONST, {.i64 = 2 /* AOM_USAGE_ALL_INTRA */}, 0, 0, VE, "usage"}, - { "tune", "The metric that the encoder tunes for. Automatically chosen by the encoder by default", OFFSET(tune), AV_OPT_TYPE_INT, {.i64 = -1}, -1, AOM_TUNE_SSIM, VE, "tune"}, - { "psnr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AOM_TUNE_PSNR}, 0, 0, VE, "tune"}, - { "ssim", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AOM_TUNE_SSIM}, 0, 0, VE, "tune"}, + { "usage", "Quality and compression efficiency vs speed trade-off", OFFSET(usage), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, .unit = "usage"}, + { "good", "Good quality", 0, AV_OPT_TYPE_CONST, {.i64 = 0 /* AOM_USAGE_GOOD_QUALITY */}, 0, 0, VE, .unit = "usage"}, + { "realtime", "Realtime encoding", 0, AV_OPT_TYPE_CONST, {.i64 = 1 /* AOM_USAGE_REALTIME */}, 0, 0, VE, .unit = "usage"}, + { "allintra", "All Intra encoding", 0, AV_OPT_TYPE_CONST, {.i64 = 2 /* AOM_USAGE_ALL_INTRA */}, 0, 0, VE, .unit = "usage"}, + { "tune", "The metric that the encoder tunes for. Automatically chosen by the encoder by default", OFFSET(tune), AV_OPT_TYPE_INT, {.i64 = -1}, -1, AOM_TUNE_SSIM, VE, .unit = "tune"}, + { "psnr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AOM_TUNE_PSNR}, 0, 0, VE, .unit = "tune"}, + { "ssim", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AOM_TUNE_SSIM}, 0, 0, VE, .unit = "tune"}, FF_AV1_PROFILE_OPTS { "still-picture", "Encode in single frame mode (typically used for still AVIF images).", OFFSET(still_picture), AV_OPT_TYPE_BOOL, {.i64 = 0}, -1, 1, VE }, { "enable-rect-partitions", "Enable rectangular partitions", OFFSET(enable_rect_partitions), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE}, diff --git a/libavcodec/libaribb24.c b/libavcodec/libaribb24.c index 8ccf3c4b5d8..507a11511f1 100644 --- a/libavcodec/libaribb24.c +++ b/libavcodec/libaribb24.c @@ -49,13 +49,13 @@ static unsigned int get_profile_font_size(AVCodecContext *avctx) Libaribb24Context *b24 = avctx->priv_data; int profile = avctx->profile; - if (profile == FF_PROFILE_UNKNOWN) + if (profile == AV_PROFILE_UNKNOWN) profile = b24->default_profile; switch (profile) { - case FF_PROFILE_ARIB_PROFILE_A: + case AV_PROFILE_ARIB_PROFILE_A: return 36; - case FF_PROFILE_ARIB_PROFILE_C: + case AV_PROFILE_ARIB_PROFILE_C: return 18; default: return 0; @@ -75,15 +75,15 @@ static int libaribb24_generate_ass_header(AVCodecContext *avctx) unsigned int font_size = 0; int profile = avctx->profile; - if (profile == FF_PROFILE_UNKNOWN) + if (profile == AV_PROFILE_UNKNOWN) profile = b24->default_profile; switch (profile) { - case FF_PROFILE_ARIB_PROFILE_A: + case AV_PROFILE_ARIB_PROFILE_A: plane_width = 960; plane_height = 540; break; - case FF_PROFILE_ARIB_PROFILE_C: + case AV_PROFILE_ARIB_PROFILE_C: plane_width = 320; plane_height = 180; break; @@ -147,12 +147,12 @@ static int libaribb24_init(AVCodecContext *avctx) { Libaribb24Context *b24 = avctx->priv_data; void(* arib_dec_init)(arib_decoder_t* decoder) = NULL; - int ret_code = AVERROR_EXTERNAL; + int ret; int profile = avctx->profile; if (!(b24->lib_instance = arib_instance_new(avctx))) { av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribb24!\n"); - goto init_fail; + return AVERROR_EXTERNAL; } if (b24->aribb24_base_path) { @@ -165,46 +165,35 @@ static int libaribb24_init(AVCodecContext *avctx) if (!(b24->parser = arib_get_parser(b24->lib_instance))) { av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribb24 PES parser!\n"); - goto init_fail; + return AVERROR_EXTERNAL; } if (!(b24->decoder = arib_get_decoder(b24->lib_instance))) { av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribb24 decoder!\n"); - goto init_fail; + return AVERROR_EXTERNAL; } - if (profile == FF_PROFILE_UNKNOWN) + if (profile == AV_PROFILE_UNKNOWN) profile = b24->default_profile; switch (profile) { - case FF_PROFILE_ARIB_PROFILE_A: + case AV_PROFILE_ARIB_PROFILE_A: arib_dec_init = arib_initialize_decoder_a_profile; break; - case FF_PROFILE_ARIB_PROFILE_C: + case AV_PROFILE_ARIB_PROFILE_C: arib_dec_init = arib_initialize_decoder_c_profile; break; default: av_log(avctx, AV_LOG_ERROR, "Unknown or unsupported profile set!\n"); - ret_code = AVERROR(EINVAL); - goto init_fail; + return AVERROR(EINVAL); } arib_dec_init(b24->decoder); - if (libaribb24_generate_ass_header(avctx) < 0) { - ret_code = AVERROR(ENOMEM); - goto init_fail; - } + ret = libaribb24_generate_ass_header(avctx); + if (ret < 0) + return ret; return 0; - -init_fail: - if (b24->decoder) - arib_finalize_decoder(b24->decoder); - - if (b24->lib_instance) - arib_instance_destroy(b24->lib_instance); - - return ret_code; } static int libaribb24_close(AVCodecContext *avctx) @@ -227,7 +216,7 @@ static int libaribb24_handle_regions(AVCodecContext *avctx, AVSubtitle *sub) Libaribb24Context *b24 = avctx->priv_data; const arib_buf_region_t *region = arib_decoder_get_regions(b24->decoder); unsigned int profile_font_size = get_profile_font_size(avctx); - AVBPrint buf = { 0 }; + AVBPrint buf; int ret = 0; av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED); @@ -291,6 +280,7 @@ static int libaribb24_handle_regions(AVCodecContext *avctx, AVSubtitle *sub) av_log(avctx, AV_LOG_DEBUG, "Styled ASS line: %s\n", buf.str); + sub->format = 1; /* text */ ret = ff_ass_add_rect(sub, buf.str, b24->read_order++, 0, NULL, NULL); } @@ -389,9 +379,9 @@ static const AVOption options[] = { { "aribb24-skip-ruby-text", "skip ruby text blocks during decoding", OFFSET(aribb24_skip_ruby), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD }, { "default_profile", "default profile to use if not specified in the stream parameters", - OFFSET(default_profile), AV_OPT_TYPE_INT, { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, FF_PROFILE_ARIB_PROFILE_C, SD, "profile" }, - {"a", "Profile A", 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_ARIB_PROFILE_A}, INT_MIN, INT_MAX, SD, "profile"}, - {"c", "Profile C", 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_ARIB_PROFILE_C}, INT_MIN, INT_MAX, SD, "profile"}, + OFFSET(default_profile), AV_OPT_TYPE_INT, { .i64 = AV_PROFILE_UNKNOWN }, AV_PROFILE_UNKNOWN, AV_PROFILE_ARIB_PROFILE_C, SD, .unit = "profile" }, + {"a", "Profile A", 0, AV_OPT_TYPE_CONST, {.i64 = AV_PROFILE_ARIB_PROFILE_A}, INT_MIN, INT_MAX, SD, .unit = "profile"}, + {"c", "Profile C", 0, AV_OPT_TYPE_CONST, {.i64 = AV_PROFILE_ARIB_PROFILE_C}, INT_MIN, INT_MAX, SD, .unit = "profile"}, { NULL } }; @@ -409,7 +399,7 @@ const FFCodec ff_libaribb24_decoder = { .p.id = AV_CODEC_ID_ARIB_CAPTION, .p.priv_class = &aribb24_class, .p.wrapper_name = "libaribb24", - .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_NOT_INIT_THREADSAFE, .priv_data_size = sizeof(Libaribb24Context), .init = libaribb24_init, .close = libaribb24_close, diff --git a/libavcodec/libaribcaption.c b/libavcodec/libaribcaption.c new file mode 100644 index 00000000000..fba68273750 --- /dev/null +++ b/libavcodec/libaribcaption.c @@ -0,0 +1,1181 @@ +/* + * ARIB STD-B24 caption decoder using the libaribcaption library + * Copyright (c) 2022 TADANO Tokumei + * + * 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 "avcodec.h" +#include "codec_internal.h" +#include "internal.h" +#include "libavcodec/ass.h" +#include "libavutil/avstring.h" +#include "libavutil/avutil.h" +#include "libavutil/thread.h" +#include "libavutil/log.h" +#include "libavutil/opt.h" + +#include + +#if !defined(DEFAULT_FONT_ASS) +# define DEFAULT_FONT_ASS "sans-serif" +#endif + +#define ARIBC_BPRINT_SIZE_INIT 64 +#define ARIBC_BPRINT_SIZE_MAX (8 * 1024) +#define ARIBC_ALPHA_MAX_NUM 4 +#define ARIBC_ALPHA_DEFAULT_FRONT 0xFF +#define ARIBC_ALPHA_DEFAULT_BACK 0x80 + +#define ARIBCC_COLOR_RGB(c) ((c) & 0xFFFFFF) +#define ARIBCC_COLOR_DIFF_RGB(c1,c2) (((c1) ^ (c2)) & 0x00FFFFFF) +#define ARIBCC_COLOR_DIFF_A(c1,c2) (((c1) ^ (c2)) & 0xFF000000) + +#define CLUT_RGBA(r,g,b,a) (((unsigned)(a) << 24) | ((r) << 16) | ((g) << 8) | (b)) +#define CLUT_A(c) (((c) >> 24) & 0xFF) +#define CLUT_R(c) (((c) >> 16) & 0xFF) +#define CLUT_G(c) (((c) >> 8) & 0xFF) +#define CLUT_B(c) ( (c) & 0xFF) + +#define ARIBCC_COLOR_TO_CLUT_RGBA(c,a) (((ARIBCC_COLOR_A(c) ? ARIBCC_COLOR_A(c) : (a)) << 24) | \ + (ARIBCC_COLOR_R(c) << 16) | \ + (ARIBCC_COLOR_G(c) << 8) | \ + (ARIBCC_COLOR_B(c))) + +typedef struct ARIBCaptionContext { + AVClass *class; + AVCodecContext *avctx; + const AVPacket *avpkt; + AVSubtitle *sub; + + aribcc_context_t *context; + aribcc_decoder_t *decoder; + aribcc_renderer_t *renderer; + + int subtitle_type; + int encoding_scheme; + int ass_single_rect; + char *font; + int force_stroke_text; + int ignore_background; + int ignore_ruby; + float stroke_width; + int replace_drcs; + int replace_msz_ascii; + int replace_msz_japanese; + int replace_msz_glyph; + + int64_t pts; + AVRational time_base; + int canvas_width; + int canvas_height; + int plane_width; + int plane_height; + int frame_width; + int frame_height; + int bitmap_plane_width; + int bitmap_plane_height; + int font_size; + int charstyle; + int border_style; + int readorder; + + aribcc_caption_t caption; + aribcc_render_result_t render_result; + uint32_t *clut; + int clut_idx; + int clut_overflow; + uint8_t clut_alpha[ARIBC_ALPHA_MAX_NUM]; +} ARIBCaptionContext; + +static void hex_dump_debug(void *ctx, const char *buf, int buf_size) +{ + int i; + + for (i = 0; i < buf_size; i++) { + ff_dlog(ctx, "%02hhx ", buf[i]); + if (i % 16 == 15) + ff_dlog(ctx, "\n"); + } + if (i % 16) + ff_dlog(ctx, "\n"); +} + +static void logcat_callback(aribcc_loglevel_t level, const char* message, void* userdata) +{ + ARIBCaptionContext *ctx = userdata; + int lvl; + + if (ctx->decoder != NULL) { + switch (level) { + case ARIBCC_LOGLEVEL_ERROR: + lvl = AV_LOG_ERROR; + break; + case ARIBCC_LOGLEVEL_WARNING: + lvl = AV_LOG_WARNING; + break; + default: + lvl = AV_LOG_INFO; + } + + av_log(ctx, lvl, "%s\n", message); + } +} + +static void estimate_video_frame_size(ARIBCaptionContext *ctx) +{ + if (ctx->avctx->width > 0 && ctx->avctx->height > 0) { + /* input video size specified by -canvas_size option */ + ctx->bitmap_plane_width = ctx->avctx->width; + ctx->bitmap_plane_height = ctx->avctx->height; + } else if (ctx->plane_width == 960) { + /* ARIB TR-B14 Fascicle 2 Volume 3 [Section 2] 4.3.1 */ + /* ARIB TR-B14 Fascicle 2 Volume 3 [Section 2] Appendix-4 */ + ctx->bitmap_plane_width = 1440; + ctx->bitmap_plane_height = 1080; + } else { + ctx->bitmap_plane_width = ctx->plane_width; + ctx->bitmap_plane_height = ctx->plane_height; + } + /* Expand either width or height */ + if (ctx->bitmap_plane_height * ctx->plane_width > ctx->bitmap_plane_width * ctx->plane_height) { + ctx->frame_height = ctx->bitmap_plane_height; + ctx->frame_width = ctx->frame_height * ctx->plane_width / ctx->plane_height; + } else { + ctx->frame_width = ctx->bitmap_plane_width; + ctx->frame_height = ctx->frame_width * ctx->plane_height / ctx->plane_width; + } +} + +static void clut_set_alpha(ARIBCaptionContext *ctx, uint8_t a) +{ + int i; + + for (i = 0; i < ARIBC_ALPHA_MAX_NUM; i++) { + if (ctx->clut_alpha[i] == 0) { + ctx->clut_alpha[i] = a; + return; + } + if (ctx->clut_alpha[i] == a) + return; + } + return; +} + +static uint8_t clut_find_nearlest_alpha(ARIBCaptionContext *ctx, uint8_t a) +{ + int i, j, d; + + if (a == 0) + return a; + d = 256; + j = 0; + for (i = 0; i < ARIBC_ALPHA_MAX_NUM; i++) { + if (ctx->clut_alpha[i] == a) + return a; + if (ctx->clut_alpha[i] == 0) + break; + if (abs((int)a - (int)ctx->clut_alpha[i]) < d) { + d = abs((int)a - (int)ctx->clut_alpha[i]); + j = i; + } + } + return ctx->clut_alpha[j]; +} + +static int clut_find(ARIBCaptionContext *ctx, uint32_t rgba) +{ + int i; + + for (i = 0; i < ctx->clut_idx; i++) { + if (ctx->clut[i] == rgba) + return i; + } + return -1; +} + +static inline int clut_color_distance(uint32_t rgba1, uint32_t rgba2) +{ + return abs((int)CLUT_R(rgba1) - (int)CLUT_R(rgba2)) + + abs((int)CLUT_G(rgba1) - (int)CLUT_G(rgba2)) + + abs((int)CLUT_B(rgba1) - (int)CLUT_B(rgba2)); +} + +static uint8_t clut_pick_or_set(ARIBCaptionContext *ctx, int r, int g, int b, int a) +{ + int c, i, d, d_min; + uint32_t rgba; + + a = clut_find_nearlest_alpha(ctx, a); + if (a == 0) + return 0; /* transparent */ + rgba = CLUT_RGBA(r,g,b,a); + + d_min = 256 * 3; + c = 0; + for (i = 0; i < ctx->clut_idx; i++) { + if (ctx->clut[i] == rgba) + return i; + if (CLUT_A(ctx->clut[i]) != a) + continue; + d = clut_color_distance(ctx->clut[i], rgba); + if (d < d_min) { + d_min = d; + c = i; + } + } + if (d_min > 3) { + if (ctx->clut_idx >= AVPALETTE_COUNT) + ctx->clut_overflow++; + else { + c = ctx->clut_idx; + ctx->clut[ctx->clut_idx++] = rgba; + } + } + return c; +} + +/* initialiaze CLUT with each character colors */ +static void clut_init(ARIBCaptionContext *ctx, aribcc_caption_region_t *region) +{ + aribcc_color_t text_color, back_color, stroke_color; + uint32_t rgba; + + ctx->clut[0] = CLUT_RGBA(0,0,0,0); /* transparent */ + ctx->clut_alpha[0] = 0xFF; + ctx->clut_idx = 1; + ctx->clut_overflow = 0; + text_color = region->chars[0].text_color; + back_color = region->chars[0].back_color; + stroke_color = region->chars[0].stroke_color; + rgba = ARIBCC_COLOR_TO_CLUT_RGBA(text_color, ARIBC_ALPHA_DEFAULT_FRONT); + ctx->clut[ctx->clut_idx++] = rgba; + clut_set_alpha(ctx, CLUT_A(rgba)); + rgba = ARIBCC_COLOR_TO_CLUT_RGBA(back_color, ARIBC_ALPHA_DEFAULT_BACK); + ctx->clut[ctx->clut_idx++] = rgba; + clut_set_alpha(ctx, CLUT_A(rgba)); + rgba = ARIBCC_COLOR_TO_CLUT_RGBA(stroke_color, ARIBC_ALPHA_DEFAULT_FRONT); + if (clut_find(ctx, rgba) < 0) { + ctx->clut[ctx->clut_idx++] = rgba; + clut_set_alpha(ctx, CLUT_A(rgba)); + } + + for (int i = 1; i < region->char_count; i++) { + if (region->chars[i].text_color != text_color) { + rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].text_color, + ARIBC_ALPHA_DEFAULT_FRONT); + if (clut_find(ctx, rgba) < 0) { + ctx->clut[ctx->clut_idx++] = rgba; + clut_set_alpha(ctx, CLUT_A(rgba)); + } + } + if (region->chars[i].back_color != back_color) { + rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].back_color, + ARIBC_ALPHA_DEFAULT_BACK); + if (clut_find(ctx, rgba) < 0) { + ctx->clut[ctx->clut_idx++] = rgba; + clut_set_alpha(ctx, CLUT_A(rgba)); + } + } + if (region->chars[i].stroke_color != stroke_color) { + rgba = ARIBCC_COLOR_TO_CLUT_RGBA(region->chars[i].stroke_color, + ARIBC_ALPHA_DEFAULT_FRONT); + if (clut_find(ctx, rgba) < 0) { + if (ctx->clut_idx < AVPALETTE_COUNT) + ctx->clut[ctx->clut_idx++] = rgba; + clut_set_alpha(ctx, CLUT_A(rgba)); + } + } + } +} + +/** + * aribcaption_trans_{bitmap|ass|text}_subtitle() + * + * Transfer decoded subtitle to AVSubtitle with corresponding subtitle type. + * + * @param ctx pointer to the ARIBCaptionContext + * @return > 0 number of rectangles to be displayed + * = 0 no subtitle + * < 0 error code + */ +static int aribcaption_trans_bitmap_subtitle(ARIBCaptionContext *ctx) +{ + int ret = 0; + AVSubtitle *sub = ctx->sub; + int status, rect_idx; + int old_width = ctx->frame_width; + int old_height = ctx->frame_height; + + if (ctx->caption.plane_width > 0 && ctx->caption.plane_height > 0) { + ctx->plane_width = ctx->caption.plane_width; + ctx->plane_height = ctx->caption.plane_height; + } + estimate_video_frame_size(ctx); + if (ctx->frame_width != old_width || ctx->frame_height != old_height) { + ff_dlog(ctx, "canvas: %dx%d plane: %dx%d bitmap: %dx%d frame: %dx%d\n", + ctx->avctx->width, ctx->avctx->height, + ctx->plane_width, ctx->plane_height, + ctx->bitmap_plane_width, ctx->bitmap_plane_height, + ctx->frame_width, ctx->frame_height); + if (!aribcc_renderer_set_frame_size(ctx->renderer, + ctx->frame_width, ctx->frame_height)) { + av_log(ctx, AV_LOG_ERROR, + "aribcc_renderer_set_frame_size() returned with error.\n"); + return AVERROR_EXTERNAL; + } + } + + status = aribcc_renderer_append_caption(ctx->renderer, &ctx->caption); + if (!status) { + av_log(ctx, AV_LOG_ERROR, + "aribcc_renderer_append_caption() returned with error.\n"); + return AVERROR_EXTERNAL; + } + + status = aribcc_renderer_render(ctx->renderer, ctx->pts, &ctx->render_result); + switch (status) { + case ARIBCC_RENDER_STATUS_GOT_IMAGE: + break; + + case ARIBCC_RENDER_STATUS_GOT_IMAGE_UNCHANGED: + aribcc_render_result_cleanup(&ctx->render_result); + ff_dlog(ctx, "got image unchanged\n"); + return 0; + + case ARIBCC_RENDER_STATUS_NO_IMAGE: + ff_dlog(ctx, "no image\n"); + return 0; + + case ARIBCC_RENDER_STATUS_ERROR: + av_log(ctx, AV_LOG_ERROR, + "aribcc_renderer_render() returned with error.\n"); + return AVERROR_EXTERNAL; + + default: + aribcc_render_result_cleanup(&ctx->render_result); + av_log(ctx, AV_LOG_ERROR, + "aribcc_renderer_render() returned unknown status: %d\n", status); + return AVERROR_EXTERNAL; + } + + if (!ctx->render_result.image_count || ctx->render_result.images == NULL) { + aribcc_render_result_cleanup(&ctx->render_result); + ff_dlog(ctx, "no image (%d)\n", ctx->render_result.image_count); + return 0; + } + + sub->format = 0; /* graphic */ + sub->rects = av_calloc(ctx->render_result.image_count, sizeof(*sub->rects)); + if (!sub->rects) { + ret = AVERROR(ENOMEM); + goto fail; + } + for (int i = 0; i < ctx->render_result.image_count; i++) { + sub->rects[i] = av_mallocz(sizeof(*sub->rects[i])); + if (!sub->rects[i]) { + ret = AVERROR(ENOMEM); + goto fail; + } + } + + for (rect_idx = 0; rect_idx < ctx->caption.region_count; rect_idx++) { + AVSubtitleRect *rect = sub->rects[rect_idx]; + aribcc_image_t *image = &ctx->render_result.images[rect_idx]; + int w, h, shrink_height, dst_idx; + + clut_init(ctx, &ctx->caption.regions[rect_idx]); + + rect->w = image->width * ctx->bitmap_plane_width / ctx->frame_width; + rect->h = image->height * ctx->bitmap_plane_height / ctx->frame_height; + rect->data[0] = av_mallocz(rect->w * rect->h); + if (!rect->data[0]) { + ret = AVERROR(ENOMEM); + goto fail; + } + if ((image->height != rect->h && image->width != rect->w) || + image->stride < image->width * 4 || + image->stride * image->height > image->bitmap_size) { + av_log(ctx, AV_LOG_ERROR, "Bug: unexpected rendered image: %d(%d)x%d -> %dx%d\n", + image->width, image->stride / 4, image->height, rect->w, rect->h); + ret = AVERROR_EXTERNAL; + goto fail; + } + + shrink_height = image->height != rect->h; + dst_idx = 0; + for (h = 0; h < rect->h; h++) { + for (w = 0; w < rect->w; w++) { + /* Bi-linear interpolation */ + int n, m, idx0, idx1, r, g, b, a; + if (shrink_height) { + int div_a, y0, y1; + div_a = h * ctx->frame_height; + n = ctx->bitmap_plane_height; + y0 = div_a / n; + y1 = FFMIN(y0 + 1, image->height - 1); + m = div_a - n * y0; + idx0 = image->stride * y0 + w * 4; + idx1 = image->stride * y1 + w * 4; + } else { + int div_a, x0, x1; + div_a = w * ctx->frame_width; + n = ctx->bitmap_plane_width; + x0 = div_a / n; + x1 = FFMIN(x0 + 1, image->width - 1); + m = div_a - n * x0; + idx0 = image->stride * h + x0 * 4; + idx1 = image->stride * h + x1 * 4; + } + r = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n; + g = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n; + b = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n; + a = (image->bitmap[idx0++] * (n - m) + image->bitmap[idx1++] * m) / n; + rect->data[0][dst_idx++] = clut_pick_or_set(ctx, r, g, b, a); + } + } + rect->data[1] = av_memdup(ctx->clut, AVPALETTE_SIZE); + if (!rect->data[1]) { + ret = AVERROR(ENOMEM); + goto fail; + } + + if (ctx->avctx->profile == AV_PROFILE_ARIB_PROFILE_C) { + /* ARIB TR-B14 version 3.8 Fascicle 1-(2/2) Volume 3 [Section 4] */ + /* No position information is provided for profile C */ + rect->x = (ctx->frame_width - rect->w) / 2; + rect->y = ctx->frame_height - rect->h * (ctx->caption.region_count - rect_idx); + } else { + rect->x = image->dst_x * ctx->bitmap_plane_width / ctx->frame_width; + rect->y = image->dst_y * ctx->bitmap_plane_height / ctx->frame_height; + } + rect->type = SUBTITLE_BITMAP; + rect->linesize[0] = rect->w; + rect->nb_colors = 256; + + ff_dlog(ctx, "BITMAP subtitle%s (%d,%d) %dx%d -> (%d,%d) %dx%d [%d]: %d colors\n", + (ctx->caption.regions[rect_idx].is_ruby) ? " (ruby)" : "", + image->dst_x, image->dst_y, image->width, image->height, + rect->x, rect->y, rect->w, rect->h, + rect_idx, ctx->clut_idx); + if (ctx->clut_overflow) + av_log(ctx, AV_LOG_WARNING, "CLUT overflow (%d).\n", ctx->clut_overflow); + } + sub->num_rects = rect_idx; + + return rect_idx; + +fail: + if (sub->rects) { + for (int i = 0; i < ctx->caption.region_count; i++) { + if (sub->rects[i]) { + av_freep(&sub->rects[i]->data[0]); + av_freep(&sub->rects[i]->data[1]); + av_freep(&sub->rects[i]); + } + } + av_freep(&sub->rects); + } + sub->num_rects = 0; + + return ret; +} + +static int set_ass_header(ARIBCaptionContext *ctx) +{ + AVCodecContext *avctx = ctx->avctx; + int outline, shadow; + const char *font_name; + const char *fonts = ctx->font; + + if (ctx->border_style == 4) { + outline = 0; + shadow = 4; + } else { + outline = 1; + shadow = 0; + } + if (ctx->force_stroke_text) + outline = (int)(ctx->stroke_width * 4.0 / 3.0); + + if (fonts && *fonts) + font_name = av_get_token(&fonts, ","); + else + font_name = av_strdup(DEFAULT_FONT_ASS); + if (!font_name) + return AVERROR(ENOMEM); + + av_freep(&avctx->subtitle_header); + avctx->subtitle_header = av_asprintf( + "[Script Info]\r\n" + "ScriptType: v4.00+\r\n" + "PlayResX: %d\r\n" + "PlayResY: %d\r\n" + "WrapStyle: 2\r\n" /* 2: no word wrapping */ + "\r\n" + + "[V4+ Styles]\r\n" + "Format: Name, " + "Fontname, Fontsize, " + "PrimaryColour, SecondaryColour, OutlineColour, BackColour, " + "Bold, Italic, Underline, StrikeOut, " + "ScaleX, ScaleY, " + "Spacing, Angle, " + "BorderStyle, Outline, Shadow, " + "Alignment, MarginL, MarginR, MarginV, " + "Encoding\r\n" + + "Style: " + "Default," /* Name */ + "%s,%d," /* Font{name,size} */ + "&H%x,&H%x,&H%x,&H%x," /* {Primary,Secondary,Outline,Back}Colour */ + "%d,%d,%d,0," /* Bold, Italic, Underline, StrikeOut */ + "100,100," /* Scale{X,Y} */ + "0,0," /* Spacing, Angle */ + "%d,%d,%d," /* BorderStyle, Outline, Shadow */ + "%d,10,10,10," /* Alignment, Margin[LRV] */ + "0\r\n" /* Encoding */ + "\r\n" + + "[Events]\r\n" + "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n", + ctx->plane_width, ctx->plane_height, + font_name, ctx->font_size, + ASS_DEFAULT_COLOR, ASS_DEFAULT_COLOR, + ASS_DEFAULT_BACK_COLOR, ASS_DEFAULT_BACK_COLOR, + -ASS_DEFAULT_BOLD, -ASS_DEFAULT_ITALIC, -ASS_DEFAULT_UNDERLINE, + ctx->border_style, outline, shadow, ASS_DEFAULT_ALIGNMENT); + + av_freep(&font_name); + if (!avctx->subtitle_header) + return AVERROR(ENOMEM); + avctx->subtitle_header_size = strlen(avctx->subtitle_header); + return 0; +} + +static void set_ass_color(AVBPrint *buf, int color_num, + aribcc_color_t new_color, aribcc_color_t old_color) +{ + if (ARIBCC_COLOR_DIFF_RGB(new_color, old_color)) + av_bprintf(buf, "{\\%dc&H%06x&}", color_num, + ARIBCC_COLOR_RGB(new_color)); + if (ARIBCC_COLOR_DIFF_A(new_color, old_color)) + av_bprintf(buf, "{\\%da&H%02x&}", color_num, + 0xFF - ARIBCC_COLOR_A(new_color)); +} + +static int aribcaption_trans_ass_subtitle(ARIBCaptionContext *ctx) +{ + AVSubtitle *sub = ctx->sub; + AVBPrint buf; + bool single_rect = ctx->ass_single_rect; + int ret = 0, rect_idx; + + if (ctx->caption.plane_width > 0 && ctx->caption.plane_height > 0 && + (ctx->caption.plane_width != ctx->plane_width || + ctx->caption.plane_height != ctx->plane_height)) { + ctx->plane_width = ctx->caption.plane_width; + ctx->plane_height = ctx->caption.plane_height; + if ((ret = set_ass_header(ctx)) < 0) + return ret; + } + + /* ARIB TR-B14 version 3.8 Fascicle 1-(2/2) Volume 3 [Section 4] */ + /* No position information is provided for profile C */ + if (ctx->avctx->profile == AV_PROFILE_ARIB_PROFILE_C) + single_rect = true; + + sub->format = 1; /* text */ + if (ctx->caption.region_count == 0) { + /* clear previous caption for indefinite duration */ + ff_ass_add_rect(sub, "", ctx->readorder++, 0, NULL, NULL); + return 1; + } + + av_bprint_init(&buf, ARIBC_BPRINT_SIZE_INIT, ARIBC_BPRINT_SIZE_MAX); + + if (single_rect && ctx->avctx->profile != AV_PROFILE_ARIB_PROFILE_C) { + int x, y, rx, ry; + x = ctx->plane_width; + y = ctx->plane_height; + for (int i = 0; i < ctx->caption.region_count; i++) { + rx = ctx->caption.regions[i].x; + ry = ctx->caption.regions[i].y; + if (rx < x) + x = rx; + if (ry < y) + y = ry; + } + av_bprintf(&buf, "{\\an7}"); + if (y < 0) + y += ctx->plane_height; + if (x > 0 || y > 0) + av_bprintf(&buf, "{\\pos(%d,%d)}", x, y); + } + + rect_idx = 0; + for (int i = 0; i < ctx->caption.region_count; i++) { + aribcc_caption_region_t *region = &ctx->caption.regions[i]; + aribcc_color_t text_color = ARIBCC_MAKE_RGBA(0xFF, 0xFF, 0xFF, + ARIBC_ALPHA_DEFAULT_FRONT); + aribcc_color_t stroke_color = ARIBCC_MAKE_RGBA(0, 0, 0, + ARIBC_ALPHA_DEFAULT_FRONT); + aribcc_color_t back_color = ARIBCC_MAKE_RGBA(0, 0, 0, + ARIBC_ALPHA_DEFAULT_BACK); + aribcc_charstyle_t charstyle = ctx->charstyle; + int char_width = ctx->font_size; + int char_height = ctx->font_size; + int char_horizontal_spacing = 0; + + if (region->is_ruby && ctx->ignore_ruby) + continue; + + if (!single_rect) { + int x = region->x; + int y = region->y; + if (x < 0) + x += ctx->plane_width; + if (y < 0) + y += ctx->plane_height; + av_bprint_clear(&buf); + av_bprintf(&buf, "{\\an7}"); + if (x > 0 || y > 0) + av_bprintf(&buf, "{\\pos(%d,%d)}", x, y); + } + if (region->is_ruby) + av_bprintf(&buf, "{\\fs%d}", char_height / 2); + + for (int j = 0; j < region->char_count; j++) { + aribcc_caption_char_t *ch = ®ion->chars[j]; + + if (ctx->avctx->profile != AV_PROFILE_ARIB_PROFILE_C) { + if (ch->char_horizontal_spacing != char_horizontal_spacing) { + av_bprintf(&buf, "{\\fsp%d}", (region->is_ruby) ? + ch->char_horizontal_spacing / 2 : + ch->char_horizontal_spacing); + char_horizontal_spacing = ch->char_horizontal_spacing; + } + if (ch->char_width != char_width) { + av_bprintf(&buf, "{\\fscx%"PRId64"}", + av_rescale(ch->char_width, 100, ctx->font_size)); + char_width = ch->char_width; + } + if (ch->char_height != char_height) { + av_bprintf(&buf, "{\\fscy%"PRId64"}", + av_rescale(ch->char_height, 100, ctx->font_size)); + char_height = ch->char_height; + } + } + if (ch->style != charstyle) { + aribcc_charstyle_t diff = ch->style ^ charstyle; + if (diff & ARIBCC_CHARSTYLE_STROKE) { + if (charstyle & ARIBCC_CHARSTYLE_STROKE) { + if (ctx->force_stroke_text) + av_bprintf(&buf, "{\\bord%d}", + (int)(ctx->stroke_width * 4.0 / 3.0)); + else + av_bprintf(&buf, "{\\bord0}"); + } else + av_bprintf(&buf, "{\\bord3}"); + } + if (diff & ARIBCC_CHARSTYLE_BOLD) { + if (charstyle & ARIBCC_CHARSTYLE_BOLD) + av_bprintf(&buf, "{\\b0}"); + else + av_bprintf(&buf, "{\\b1}"); + } + if (diff & ARIBCC_CHARSTYLE_ITALIC) { + if (charstyle & ARIBCC_CHARSTYLE_ITALIC) + av_bprintf(&buf, "{\\i0}"); + else + av_bprintf(&buf, "{\\i1}"); + } + if (diff & ARIBCC_CHARSTYLE_UNDERLINE) { + if (charstyle & ARIBCC_CHARSTYLE_UNDERLINE) + av_bprintf(&buf, "{\\u0}"); + else + av_bprintf(&buf, "{\\u1}"); + } + charstyle = ch->style; + } + if (ch->text_color != text_color) { + set_ass_color(&buf, 1, ch->text_color, text_color); + text_color = ch->text_color; + } + if (ch->stroke_color != stroke_color) { + set_ass_color(&buf, 3, ch->stroke_color, stroke_color); + stroke_color = ch->stroke_color; + } + if (ch->back_color != back_color) { + if (ctx->border_style == 4) + set_ass_color(&buf, 4, ch->back_color, back_color); + else + set_ass_color(&buf, 3, ch->back_color, back_color); + back_color = ch->back_color; + } + if (region->chars[j].type == ARIBCC_CHARTYPE_DRCS) + av_bprintf(&buf, "\xe3\x80\x93"); /* Geta Mark */ + else + ff_ass_bprint_text_event(&buf, ch->u8str, strlen(ch->u8str), "", 0); + } + + if (single_rect) { + if (i + 1 < ctx->caption.region_count) + av_bprintf(&buf, "{\\r}\\N"); + ff_dlog(ctx, "ASS subtitle%s (%d,%d) %dx%d [%d]\n", + (region->is_ruby) ? " (ruby)" : "", + region->x, region->y, region->width, region->height, + rect_idx); + } else { + if (!av_bprint_is_complete(&buf)) { + ret = AVERROR(ENOMEM); + goto fail; + } + ff_dlog(ctx, "ASS subtitle%s (%d,%d) %dx%d [%d]: %s\n", + (region->is_ruby) ? " (ruby)" : "", + region->x, region->y, region->width, region->height, + rect_idx, buf.str); + + ret = ff_ass_add_rect(sub, buf.str, ctx->readorder++, 0 , NULL, NULL); + if (ret != 0) + goto fail; + rect_idx++; + } + } + if (single_rect) { + if (!av_bprint_is_complete(&buf)) { + ret = AVERROR(ENOMEM); + goto fail; + } + ff_dlog(ctx, "ASS subtitle: %s\n", buf.str); + + ret = ff_ass_add_rect(sub, buf.str, ctx->readorder++, 0 , NULL, NULL); + if (ret != 0) + goto fail; + rect_idx++; + } + + av_bprint_finalize(&buf, NULL); + return rect_idx; + +fail: + if (sub->rects) { + for (int i = 0; i < ctx->caption.region_count; i++) { + if (sub->rects[i]) { + av_freep(&sub->rects[i]->ass); + av_freep(&sub->rects[i]); + } + } + av_freep(&sub->rects); + } + sub->num_rects = 0; + av_bprint_finalize(&buf, NULL); + + return ret; +} + +static int aribcaption_trans_text_subtitle(ARIBCaptionContext *ctx) +{ + AVSubtitle *sub = ctx->sub; + AVSubtitleRect *rect; + int ret = 0; + const char *text; + + sub->rects = av_calloc(ctx->caption.region_count, sizeof(*sub->rects)); + if (!sub->rects) { + ret = AVERROR(ENOMEM); + goto fail; + } + sub->num_rects = 1; + + sub->rects[0] = av_mallocz(sizeof(*sub->rects[0])); + if (!sub->rects[0]) { + ret = AVERROR(ENOMEM); + goto fail; + } + rect = sub->rects[0]; + + if (ctx->caption.region_count == 0) + text = ""; /* clear previous caption */ + else { + text = ctx->caption.text; + ff_dlog(ctx, "TEXT subtitle: %s\n", text); + } + rect->text = av_strdup(text); + if (!rect->text) { + ret = AVERROR(ENOMEM); + goto fail; + } + + sub->format = 1; /* text */ + rect->type = SUBTITLE_TEXT; + + return 1; + +fail: + if (sub->rects) { + rect = sub->rects[0]; + if (rect) { + av_freep(&rect->text); + av_freep(&rect); + } + av_freep(&sub->rects); + } + sub->num_rects = 0; + + return ret; +} + +static int aribcaption_decode(AVCodecContext *avctx, AVSubtitle *sub, + int *got_sub_ptr, const AVPacket *avpkt) +{ + ARIBCaptionContext *ctx = avctx->priv_data; + int status; + + ff_dlog(ctx, "ARIB caption packet pts=%"PRIx64":\n", avpkt->pts); + if (sub->num_rects) { + avpriv_request_sample(ctx, "Different Version of Segment asked Twice"); + return AVERROR_PATCHWELCOME; + } + hex_dump_debug(ctx, avpkt->data, avpkt->size); + + ctx->sub = sub; + ctx->avpkt = avpkt; + ctx->time_base = avctx->pkt_timebase; + if (ctx->time_base.num <= 0 || ctx->time_base.den <= 0) { + av_log(ctx, AV_LOG_VERBOSE, "No timebase set. assuming 90kHz.\n"); + ctx->time_base = av_make_q(1, 90000); + } + if (avpkt->pts == AV_NOPTS_VALUE) + ctx->pts = ARIBCC_PTS_NOPTS; + else + ctx->pts = av_rescale_q(avpkt->pts, ctx->time_base, (AVRational){1, 1000}); + + status = aribcc_decoder_decode(ctx->decoder, avpkt->data, avpkt->size, + ctx->pts, &ctx->caption); + if (status == ARIBCC_DECODE_STATUS_ERROR) { + av_log(ctx, AV_LOG_ERROR, + "aribcc_decoder_decode() returned with error.\n"); + return AVERROR(EAGAIN); + } + if (status == ARIBCC_DECODE_STATUS_NO_CAPTION) { + ff_dlog(ctx, "No caption.\n"); + return avpkt->size; + } else { + ff_dlog(ctx, "type=%02x, flags=%x, lang=%03x\n", + ctx->caption.type, ctx->caption.flags, ctx->caption.iso6392_language_code); + ff_dlog(ctx, "region count = %d, start=%d.%d, duration=%d.%d\n", + ctx->caption.region_count, + (int)(ctx->caption.pts / 1000), (int)(ctx->caption.pts % 1000), + (int)((ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) ? + -1 : ctx->caption.wait_duration / 1000), + (int)((ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) ? + 0 : ctx->caption.wait_duration % 1000)); + } + + switch ((enum AVSubtitleType) ctx->subtitle_type) { + case SUBTITLE_TEXT: + status = aribcaption_trans_text_subtitle(ctx); + break; + + case SUBTITLE_ASS: + status = aribcaption_trans_ass_subtitle(ctx); + break; + + case SUBTITLE_BITMAP: + status = aribcaption_trans_bitmap_subtitle(ctx); + break; + + case SUBTITLE_NONE: + default: + status = 0; + } + + if (status < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to set Subtitle: %s\n", + av_err2str(status)); + aribcc_caption_cleanup(&ctx->caption); + return status; + } + if (status > 0) { + *got_sub_ptr = 1; + if (ctx->avpkt->pts != AV_NOPTS_VALUE) + sub->pts = av_rescale_q(ctx->avpkt->pts, + ctx->time_base, AV_TIME_BASE_Q); + if (ctx->caption.wait_duration == ARIBCC_DURATION_INDEFINITE) + sub->end_display_time = UINT32_MAX; + else + sub->end_display_time = (uint32_t)ctx->caption.wait_duration; + } + + aribcc_caption_cleanup(&ctx->caption); + return avpkt->size; +} + +static void aribcaption_flush(AVCodecContext *avctx) +{ + ARIBCaptionContext *ctx = avctx->priv_data; + + if (ctx->decoder) + aribcc_decoder_flush(ctx->decoder); + if (ctx->renderer) + aribcc_renderer_flush(ctx->renderer); + if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP)) + ctx->readorder = 0; +} + +static int aribcaption_close(AVCodecContext *avctx) +{ + ARIBCaptionContext *ctx = avctx->priv_data; + + av_freep(&ctx->clut); + if (ctx->renderer) + aribcc_renderer_free(ctx->renderer); + if (ctx->decoder) + aribcc_decoder_free(ctx->decoder); + if (ctx->context) + aribcc_context_free(ctx->context); + + return 0; +} + +static int aribcaption_init(AVCodecContext *avctx) +{ + ARIBCaptionContext *ctx = avctx->priv_data; + aribcc_profile_t profile; + int ret = 0; + + ctx->avctx = avctx; + + switch (avctx->profile) { + case AV_PROFILE_ARIB_PROFILE_A: + profile = ARIBCC_PROFILE_A; + /* assume 960x540 at initial state */ + ctx->plane_width = 960; + ctx->plane_height = 540; + ctx->font_size = 36; + break; + case AV_PROFILE_ARIB_PROFILE_C: + profile = ARIBCC_PROFILE_C; + ctx->plane_width = 320; + ctx->plane_height = 180; + ctx->font_size = 16; + break; + default: + av_log(avctx, AV_LOG_ERROR, "Unknown or unsupported profile set.\n"); + return AVERROR(EINVAL); + } + /* determine BorderStyle of ASS header */ + if (ctx->ignore_background) + ctx->border_style = 1; + else + ctx->border_style = 4; + ctx->charstyle = ARIBCC_CHARSTYLE_DEFAULT; + if (ctx->force_stroke_text || ctx->ignore_background) + ctx->charstyle |= ARIBCC_CHARSTYLE_STROKE; + + if (!(ctx->context = aribcc_context_alloc())) { + av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption context.\n"); + return AVERROR_EXTERNAL; + } + aribcc_context_set_logcat_callback(ctx->context, logcat_callback, avctx); + if (!(ctx->decoder = aribcc_decoder_alloc(ctx->context))) { + av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption decoder.\n"); + return AVERROR_EXTERNAL; + } + if (!aribcc_decoder_initialize(ctx->decoder, + (enum aribcc_encoding_scheme_t) ctx->encoding_scheme, + ARIBCC_CAPTIONTYPE_CAPTION, + profile, + ARIBCC_LANGUAGEID_FIRST)) { + av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribcaption decoder.\n"); + return AVERROR_EXTERNAL; + } + aribcc_decoder_set_replace_msz_fullwidth_ascii(ctx->decoder, + ctx->replace_msz_ascii); + aribcc_decoder_set_replace_msz_fullwidth_japanese(ctx->decoder, + ctx->replace_msz_japanese); + + /* Similar behavior as ffmpeg tool to set canvas size */ + if (ctx->canvas_width > 0 && ctx->canvas_height > 0 && + (ctx->avctx->width == 0 || ctx->avctx->height == 0)) { + ctx->avctx->width = ctx->canvas_width; + ctx->avctx->height = ctx->canvas_height; + } + + switch ((enum AVSubtitleType) ctx->subtitle_type) { + case SUBTITLE_ASS: + ret = set_ass_header(ctx); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to set ASS header: %s\n", + av_err2str(ret)); + return ret; + } + break; + + case SUBTITLE_BITMAP: + if(!(ctx->renderer = aribcc_renderer_alloc(ctx->context))) { + av_log(avctx, AV_LOG_ERROR, "Failed to alloc libaribcaption renderer.\n"); + return AVERROR_EXTERNAL; + } + if(!aribcc_renderer_initialize(ctx->renderer, + ARIBCC_CAPTIONTYPE_CAPTION, + ARIBCC_FONTPROVIDER_TYPE_AUTO, + ARIBCC_TEXTRENDERER_TYPE_AUTO)) { + av_log(avctx, AV_LOG_ERROR, "Failed to initialize libaribcaption renderer.\n"); + return AVERROR_EXTERNAL; + } + estimate_video_frame_size(ctx); + ff_dlog(ctx, "canvas: %dx%d plane: %dx%d bitmap: %dx%d frame: %dx%d\n", + ctx->avctx->width, ctx->avctx->height, + ctx->plane_width, ctx->plane_height, + ctx->bitmap_plane_width, ctx->bitmap_plane_height, + ctx->frame_width, ctx->frame_height); + if (!aribcc_renderer_set_frame_size(ctx->renderer, + ctx->frame_width, ctx->frame_height)) { + av_log(ctx, AV_LOG_ERROR, + "aribcc_renderer_set_frame_size() returned with error.\n"); + return AVERROR_EXTERNAL; + } + + if (!(ctx->clut = av_mallocz(AVPALETTE_SIZE))) + return AVERROR(ENOMEM); + + aribcc_renderer_set_storage_policy(ctx->renderer, ARIBCC_CAPTION_STORAGE_POLICY_MINIMUM, 0); + aribcc_renderer_set_replace_drcs(ctx->renderer, ctx->replace_drcs); + aribcc_renderer_set_force_stroke_text(ctx->renderer, ctx->force_stroke_text); + aribcc_renderer_set_force_no_background(ctx->renderer, ctx->ignore_background); + aribcc_renderer_set_force_no_ruby(ctx->renderer, ctx->ignore_ruby); + aribcc_renderer_set_stroke_width(ctx->renderer, ctx->stroke_width); + aribcc_renderer_set_replace_msz_halfwidth_glyph(ctx->renderer, + ctx->replace_msz_glyph); + if (ctx->font) { + int is_nomem = 0; + size_t count = 0; + const char **font_families = NULL; + const char *fonts = ctx->font; + + while (*fonts) { + const char **ff = av_realloc_array(font_families, count + 1, sizeof(*font_families)); + if (!ff) { + is_nomem = 1; + break; + } else { + font_families = ff; + ff[count++] = av_get_token(&fonts, ","); + if (!ff[count - 1]) { + is_nomem = 1; + break; + } else if (*fonts) + fonts++; + } + } + if (!is_nomem && count) + aribcc_renderer_set_default_font_family(ctx->renderer, font_families, count, true); + while (count) + av_freep(&font_families[--count]); + av_freep(&font_families); + if (is_nomem) + return AVERROR(ENOMEM); + } + break; + + case SUBTITLE_TEXT: + case SUBTITLE_NONE: + default: + /* do nothing */ ; + } + + ctx->readorder = 0; + + return 0; +} + +#if !defined(ASS_SINGLE_RECT) +# define ASS_SINGLE_RECT 0 +#endif + +#define OFFSET(x) offsetof(ARIBCaptionContext, x) +#define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + { "sub_type", "subtitle rendering type", + OFFSET(subtitle_type), AV_OPT_TYPE_INT, + { .i64 = SUBTITLE_ASS }, SUBTITLE_NONE, SUBTITLE_ASS, SD, .unit = "type" }, + { "none", "do nothing", 0, AV_OPT_TYPE_CONST, + { .i64 = SUBTITLE_NONE }, .flags = SD, .unit = "type" }, + { "bitmap", "bitmap rendering", 0, AV_OPT_TYPE_CONST, + { .i64 = SUBTITLE_BITMAP }, .flags = SD, .unit = "type" }, + { "text", "plain text", 0, AV_OPT_TYPE_CONST, + { .i64 = SUBTITLE_TEXT }, .flags = SD, .unit = "type" }, + { "ass", "formatted text", 0, AV_OPT_TYPE_CONST, + { .i64 = SUBTITLE_ASS }, .flags = SD, .unit = "type" }, + { "caption_encoding", "encoding scheme of subtitle text", + OFFSET(encoding_scheme), AV_OPT_TYPE_INT, { .i64 = ARIBCC_ENCODING_SCHEME_AUTO }, + ARIBCC_ENCODING_SCHEME_AUTO, ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN, SD, .unit = "encoding" }, + { "auto", "automatically detect encoding scheme", 0, AV_OPT_TYPE_CONST, + { .i64 = ARIBCC_ENCODING_SCHEME_AUTO }, .flags = SD, .unit = "encoding" }, + { "jis", "8bit-char JIS encoding (Japanese ISDB captions)", 0, AV_OPT_TYPE_CONST, + { .i64 = ARIBCC_ENCODING_SCHEME_ARIB_STD_B24_JIS }, .flags = SD, .unit = "encoding" }, + { "utf8", "UTF-8 encoding (Philippines ISDB-T captions)", 0, AV_OPT_TYPE_CONST, + { .i64 = ARIBCC_ENCODING_SCHEME_ARIB_STD_B24_UTF8 }, .flags = SD, .unit = "encoding" }, + { "latin", "latin characters (SBTVD / ISDB-Tb captions used in South America)", 0, AV_OPT_TYPE_CONST, + { .i64 = ARIBCC_ENCODING_SCHEME_ABNT_NBR_15606_1_LATIN }, .flags = SD, .unit = "encoding" }, + { "ass_single_rect", "workaround of ASS subtitle for players which can't handle multi-rectangle [ass]", + OFFSET(ass_single_rect), AV_OPT_TYPE_BOOL, { .i64 = ASS_SINGLE_RECT }, 0, 1, SD }, + { "font", "comma-separated font family [ass, bitmap]", + OFFSET(font), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, SD }, + { "force_outline_text", "always render characters with outline [(ass), bitmap]", + OFFSET(force_stroke_text), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD }, + { "ignore_background", "ignore rendering caption background [(ass), bitmap]", + OFFSET(ignore_background), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD }, + { "ignore_ruby", "ignore ruby-like characters [ass, bitmap]", + OFFSET(ignore_ruby), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, SD }, + { "outline_width", "outline width of text [(ass), bitmap]", + OFFSET(stroke_width), AV_OPT_TYPE_FLOAT, { .dbl = 1.5 }, 0.0, 3.0, SD }, + { "replace_drcs", "replace known DRCS [bitmap]", + OFFSET(replace_drcs), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD }, + { "replace_msz_ascii", "replace MSZ fullwidth alphanumerics with halfwidth alphanumerics [ass, bitmap]", + OFFSET(replace_msz_ascii), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD }, + { "replace_msz_japanese", "replace MSZ fullwidth Japanese with halfwidth [ass, bitmap]", + OFFSET(replace_msz_japanese), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD }, + { "replace_msz_glyph", "replace MSZ characters with halfwidth glyphs [bitmap]", + OFFSET(replace_msz_glyph), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, SD }, + {"canvas_size", "set input video size (WxH or abbreviation) [bitmap]", + OFFSET(canvas_width), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, INT_MAX, SD }, + { NULL } +}; + +static const AVClass aribcaption_class = { + .class_name = "aribcaption decoder", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_libaribcaption_decoder = { + .p.name = "libaribcaption", + .p.long_name = NULL_IF_CONFIG_SMALL("ARIB STD-B24 caption decoder"), + .p.type = AVMEDIA_TYPE_SUBTITLE, + .p.id = AV_CODEC_ID_ARIB_CAPTION, + .priv_data_size = sizeof(ARIBCaptionContext), + .init = aribcaption_init, + .close = aribcaption_close, + FF_CODEC_DECODE_SUB_CB(aribcaption_decode), + .flush = aribcaption_flush, + .p.priv_class = &aribcaption_class, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/libcodec2.c b/libavcodec/libcodec2.c index 83f68e85c79..b8fa396facd 100644 --- a/libavcodec/libcodec2.c +++ b/libavcodec/libcodec2.c @@ -189,7 +189,6 @@ const FFCodec ff_libcodec2_decoder = { .init = libcodec2_init_decoder, .close = libcodec2_close, FF_CODEC_DECODE_CB(libcodec2_decode), - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO) }; const FFCodec ff_libcodec2_encoder = { @@ -208,5 +207,4 @@ const FFCodec ff_libcodec2_encoder = { .init = libcodec2_init_encoder, .close = libcodec2_close, FF_CODEC_ENCODE_CB(libcodec2_encode), - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO) }; diff --git a/libavcodec/libdav1d.c b/libavcodec/libdav1d.c index 2488a709c72..ddcd0708b4f 100644 --- a/libavcodec/libdav1d.c +++ b/libavcodec/libdav1d.c @@ -24,16 +24,20 @@ #include "libavutil/avassert.h" #include "libavutil/cpu.h" #include "libavutil/film_grain_params.h" +#include "libavutil/hdr_dynamic_metadata.h" #include "libavutil/mastering_display_metadata.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "atsc_a53.h" +#include "av1_parse.h" #include "avcodec.h" #include "bytestream.h" #include "codec_internal.h" #include "decode.h" +#include "dovi_rpu.h" #include "internal.h" +#include "itut35.h" #define FF_DAV1D_VERSION_AT_LEAST(x,y) \ (DAV1D_API_VERSION_MAJOR > (x) || DAV1D_API_VERSION_MAJOR == (x) && DAV1D_API_VERSION_MINOR >= (y)) @@ -42,6 +46,7 @@ typedef struct Libdav1dContext { AVClass *class; Dav1dContext *c; AVBufferPool *pool; + DOVIContext dovi; int pool_size; Dav1dData data; @@ -153,12 +158,9 @@ static void libdav1d_init_params(AVCodecContext *c, const Dav1dSequenceHeader *s else c->pix_fmt = pix_fmt[seq->layout][seq->hbd]; - if (seq->num_units_in_tick && seq->time_scale) { - av_reduce(&c->framerate.den, &c->framerate.num, - seq->num_units_in_tick, seq->time_scale, INT_MAX); - if (seq->equal_picture_interval) - c->ticks_per_frame = seq->num_ticks_per_picture; - } + c->framerate = ff_av1_framerate(seq->num_ticks_per_picture, + (unsigned)seq->num_units_in_tick, + (unsigned)seq->time_scale); if (seq->film_grain_present) c->properties |= FF_CODEC_PROPERTY_FILM_GRAIN; @@ -214,9 +216,10 @@ static av_cold int libdav1d_init(AVCodecContext *c) #else int threads = (c->thread_count ? c->thread_count : av_cpu_count()) * 3 / 2; #endif + const AVPacketSideData *sd; int res; - av_log(c, AV_LOG_INFO, "libdav1d %s\n", dav1d_version()); + av_log(c, AV_LOG_VERBOSE, "libdav1d %s\n", dav1d_version()); dav1d_default_settings(&s); s.logger.cookie = c; @@ -277,6 +280,20 @@ static av_cold int libdav1d_init(AVCodecContext *c) if (res < 0) return AVERROR(ENOMEM); +#if FF_DAV1D_VERSION_AT_LEAST(6,7) + res = dav1d_get_frame_delay(&s); + if (res < 0) // Should not happen + return AVERROR_EXTERNAL; + + // When dav1d_get_frame_delay() returns 1, there's no delay whatsoever + c->delay = res > 1 ? res : 0; +#endif + + dav1d->dovi.logctx = c; + dav1d->dovi.dv_profile = 10; // default for AV1 + sd = ff_get_coded_side_data(c, AV_PKT_DATA_DOVI_CONF); + if (sd && sd->size > 0) + ff_dovi_update_cfg(&dav1d->dovi, (AVDOVIDecoderConfigurationRecord *) sd->data); return 0; } @@ -288,13 +305,6 @@ static void libdav1d_flush(AVCodecContext *c) dav1d_flush(dav1d->c); } -typedef struct OpaqueData { - void *pkt_orig_opaque; -#if FF_API_REORDERED_OPAQUE - int64_t reordered_opaque; -#endif -} OpaqueData; - static void libdav1d_data_free(const uint8_t *data, void *opaque) { AVBufferRef *buf = opaque; @@ -304,24 +314,17 @@ static void libdav1d_data_free(const uint8_t *data, void *opaque) { static void libdav1d_user_data_free(const uint8_t *data, void *opaque) { AVPacket *pkt = opaque; av_assert0(data == opaque); - av_free(pkt->opaque); av_packet_free(&pkt); } -static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) +static int libdav1d_receive_frame_internal(AVCodecContext *c, Dav1dPicture *p) { Libdav1dContext *dav1d = c->priv_data; Dav1dData *data = &dav1d->data; - Dav1dPicture pic = { 0 }, *p = &pic; - AVPacket *pkt; - OpaqueData *od = NULL; -#if FF_DAV1D_VERSION_AT_LEAST(5,1) - enum Dav1dEventFlags event_flags = 0; -#endif int res; if (!data->sz) { - pkt = av_packet_alloc(); + AVPacket *pkt = av_packet_alloc(); if (!pkt) return AVERROR(ENOMEM); @@ -342,30 +345,9 @@ static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) pkt->buf = NULL; -FF_DISABLE_DEPRECATION_WARNINGS - if ( -#if FF_API_REORDERED_OPAQUE - c->reordered_opaque != AV_NOPTS_VALUE || -#endif - (pkt->opaque && (c->flags & AV_CODEC_FLAG_COPY_OPAQUE))) { - od = av_mallocz(sizeof(*od)); - if (!od) { - av_packet_free(&pkt); - dav1d_data_unref(data); - return AVERROR(ENOMEM); - } - od->pkt_orig_opaque = pkt->opaque; -#if FF_API_REORDERED_OPAQUE - od->reordered_opaque = c->reordered_opaque; -#endif -FF_ENABLE_DEPRECATION_WARNINGS - } - pkt->opaque = od; - res = dav1d_data_wrap_user_data(data, (const uint8_t *)pkt, libdav1d_user_data_free, pkt); if (res < 0) { - av_free(pkt->opaque); av_packet_free(&pkt); dav1d_data_unref(data); return res; @@ -392,11 +374,29 @@ FF_ENABLE_DEPRECATION_WARNINGS if (res < 0) { if (res == AVERROR(EINVAL)) res = AVERROR_INVALIDDATA; - else if (res == AVERROR(EAGAIN) && c->internal->draining) - res = AVERROR_EOF; + else if (res == AVERROR(EAGAIN)) + res = c->internal->draining ? AVERROR_EOF : 1; + } + return res; +} + +static int libdav1d_receive_frame(AVCodecContext *c, AVFrame *frame) +{ + Libdav1dContext *dav1d = c->priv_data; + Dav1dPicture pic = { 0 }, *p = &pic; + AVPacket *pkt; +#if FF_DAV1D_VERSION_AT_LEAST(5,1) + enum Dav1dEventFlags event_flags = 0; +#endif + int res; + + do { + res = libdav1d_receive_frame_internal(c, p); + } while (res > 0); + + if (res < 0) return res; - } av_assert0(p->data[0] && p->allocator_data); @@ -440,29 +440,17 @@ FF_ENABLE_DEPRECATION_WARNINGS ff_set_sar(c, frame->sample_aspect_ratio); pkt = (AVPacket *)p->m.user_data.data; - od = pkt->opaque; -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - if (od && od->reordered_opaque != AV_NOPTS_VALUE) - frame->reordered_opaque = od->reordered_opaque; - else - frame->reordered_opaque = AV_NOPTS_VALUE; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - - // restore the original user opaque value for - // ff_decode_frame_props_from_pkt() - pkt->opaque = od ? od->pkt_orig_opaque : NULL; - av_freep(&od); // match timestamps and packet size res = ff_decode_frame_props_from_pkt(c, frame, pkt); - pkt->opaque = NULL; if (res < 0) goto fail; frame->pkt_dts = pkt->pts; - frame->key_frame = p->frame_hdr->frame_type == DAV1D_FRAME_TYPE_KEY; + if (p->frame_hdr->frame_type == DAV1D_FRAME_TYPE_KEY) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; switch (p->frame_hdr->frame_type) { case DAV1D_FRAME_TYPE_KEY: @@ -481,65 +469,127 @@ FF_ENABLE_DEPRECATION_WARNINGS } if (p->mastering_display) { - AVMasteringDisplayMetadata *mastering = av_mastering_display_metadata_create_side_data(frame); - if (!mastering) { - res = AVERROR(ENOMEM); + AVMasteringDisplayMetadata *mastering; + + res = ff_decode_mastering_display_new(c, frame, &mastering); + if (res < 0) goto fail; - } - for (int i = 0; i < 3; i++) { - mastering->display_primaries[i][0] = av_make_q(p->mastering_display->primaries[i][0], 1 << 16); - mastering->display_primaries[i][1] = av_make_q(p->mastering_display->primaries[i][1], 1 << 16); - } - mastering->white_point[0] = av_make_q(p->mastering_display->white_point[0], 1 << 16); - mastering->white_point[1] = av_make_q(p->mastering_display->white_point[1], 1 << 16); + if (mastering) { + for (int i = 0; i < 3; i++) { + mastering->display_primaries[i][0] = av_make_q(p->mastering_display->primaries[i][0], 1 << 16); + mastering->display_primaries[i][1] = av_make_q(p->mastering_display->primaries[i][1], 1 << 16); + } + mastering->white_point[0] = av_make_q(p->mastering_display->white_point[0], 1 << 16); + mastering->white_point[1] = av_make_q(p->mastering_display->white_point[1], 1 << 16); - mastering->max_luminance = av_make_q(p->mastering_display->max_luminance, 1 << 8); - mastering->min_luminance = av_make_q(p->mastering_display->min_luminance, 1 << 14); + mastering->max_luminance = av_make_q(p->mastering_display->max_luminance, 1 << 8); + mastering->min_luminance = av_make_q(p->mastering_display->min_luminance, 1 << 14); - mastering->has_primaries = 1; - mastering->has_luminance = 1; + mastering->has_primaries = 1; + mastering->has_luminance = 1; + } } if (p->content_light) { - AVContentLightMetadata *light = av_content_light_metadata_create_side_data(frame); - if (!light) { - res = AVERROR(ENOMEM); + AVContentLightMetadata *light; + + res = ff_decode_content_light_new(c, frame, &light); + if (res < 0) goto fail; + + if (light) { + light->MaxCLL = p->content_light->max_content_light_level; + light->MaxFALL = p->content_light->max_frame_average_light_level; } - light->MaxCLL = p->content_light->max_content_light_level; - light->MaxFALL = p->content_light->max_frame_average_light_level; } if (p->itut_t35) { +#if FF_DAV1D_VERSION_AT_LEAST(6,9) + for (size_t i = 0; i < p->n_itut_t35; i++) { + const Dav1dITUTT35 *itut_t35 = &p->itut_t35[i]; +#else + const Dav1dITUTT35 *itut_t35 = p->itut_t35; +#endif GetByteContext gb; - unsigned int user_identifier; + int provider_code; + + bytestream2_init(&gb, itut_t35->payload, itut_t35->payload_size); + + provider_code = bytestream2_get_be16(&gb); + switch (provider_code) { + case ITU_T_T35_PROVIDER_CODE_ATSC: { + uint32_t user_identifier = bytestream2_get_be32(&gb); + switch (user_identifier) { + case MKBETAG('G', 'A', '9', '4'): { // closed captions + AVBufferRef *buf = NULL; + + res = ff_parse_a53_cc(&buf, gb.buffer, bytestream2_get_bytes_left(&gb)); + if (res < 0) + goto fail; + if (!res) + break; + + res = ff_frame_new_side_data_from_buf(c, frame, AV_FRAME_DATA_A53_CC, &buf, NULL); + if (res < 0) + goto fail; + + c->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; + break; + } + default: // ignore unsupported identifiers + break; + } + break; + } + case ITU_T_T35_PROVIDER_CODE_SMTPE: { + AVDynamicHDRPlus *hdrplus; + int provider_oriented_code = bytestream2_get_be16(&gb); + int application_identifier = bytestream2_get_byte(&gb); + + if (itut_t35->country_code != ITU_T_T35_COUNTRY_CODE_US || + provider_oriented_code != 1 || application_identifier != 4) + break; - bytestream2_init(&gb, p->itut_t35->payload, p->itut_t35->payload_size); - bytestream2_skip(&gb, 1); // terminal provider code - bytestream2_skip(&gb, 1); // terminal provider oriented code - user_identifier = bytestream2_get_be32(&gb); - switch (user_identifier) { - case MKBETAG('G', 'A', '9', '4'): { // closed captions - AVBufferRef *buf = NULL; + hdrplus = av_dynamic_hdr_plus_create_side_data(frame); + if (!hdrplus) { + res = AVERROR(ENOMEM); + goto fail; + } - res = ff_parse_a53_cc(&buf, gb.buffer, bytestream2_get_bytes_left(&gb)); + res = av_dynamic_hdr_plus_from_t35(hdrplus, gb.buffer, + bytestream2_get_bytes_left(&gb)); if (res < 0) goto fail; - if (!res) + break; + } + case ITU_T_T35_PROVIDER_CODE_DOLBY: { + int provider_oriented_code = bytestream2_get_be32(&gb); + if (itut_t35->country_code != ITU_T_T35_COUNTRY_CODE_US || + provider_oriented_code != 0x800) break; - if (!av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_A53_CC, buf)) - av_buffer_unref(&buf); + res = ff_dovi_rpu_parse(&dav1d->dovi, gb.buffer, gb.buffer_end - gb.buffer); + if (res < 0) { + av_log(c, AV_LOG_WARNING, "Error parsing DOVI OBU.\n"); + break; // ignore + } - c->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; + res = ff_dovi_attach_side_data(&dav1d->dovi, frame); + if (res < 0) + goto fail; break; } - default: // ignore unsupported identifiers + default: // ignore unsupported provider codes break; } +#if FF_DAV1D_VERSION_AT_LEAST(6,9) + } +#endif } if (p->frame_hdr->film_grain.present && (!dav1d->apply_grain || (c->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN))) { AVFilmGrainParams *fgp = av_film_grain_params_create_side_data(frame); + const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(frame->format); + av_assert0(pixdesc); if (!fgp) { res = AVERROR(ENOMEM); goto fail; @@ -547,6 +597,14 @@ FF_ENABLE_DEPRECATION_WARNINGS fgp->type = AV_FILM_GRAIN_PARAMS_AV1; fgp->seed = p->frame_hdr->film_grain.data.seed; + fgp->width = frame->width; + fgp->height = frame->height; + fgp->color_range = frame->color_range; + fgp->color_primaries = frame->color_primaries; + fgp->color_trc = frame->color_trc; + fgp->color_space = frame->colorspace; + fgp->subsampling_x = pixdesc->log2_chroma_w; + fgp->subsampling_y = pixdesc->log2_chroma_h; fgp->codec.aom.num_y_points = p->frame_hdr->film_grain.data.num_y_points; fgp->codec.aom.chroma_scaling_from_luma = p->frame_hdr->film_grain.data.chroma_scaling_from_luma; fgp->codec.aom.scaling_shift = p->frame_hdr->film_grain.data.scaling_shift; @@ -589,6 +647,7 @@ static av_cold int libdav1d_close(AVCodecContext *c) Libdav1dContext *dav1d = c->priv_data; av_buffer_pool_uninit(&dav1d->pool); + ff_dovi_ctx_unref(&dav1d->dovi); dav1d_data_unref(&dav1d->data); dav1d_close(&dav1d->c); @@ -635,7 +694,7 @@ const FFCodec ff_libdav1d_decoder = { .flush = libdav1d_flush, FF_CODEC_RECEIVE_FRAME_CB(libdav1d_receive_frame), .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS, - .caps_internal = FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_SETS_FRAME_PROPS | + .caps_internal = FF_CODEC_CAP_SETS_FRAME_PROPS | FF_CODEC_CAP_AUTO_THREADS, .p.priv_class = &libdav1d_class, .p.wrapper_name = "libdav1d", diff --git a/libavcodec/libdavs2.c b/libavcodec/libdavs2.c index 179d2f4e4b6..5a605c36f61 100644 --- a/libavcodec/libdavs2.c +++ b/libavcodec/libdavs2.c @@ -234,7 +234,5 @@ const FFCodec ff_libdavs2_decoder = { .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_AUTO_THREADS, - .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE }, .p.wrapper_name = "libdavs2", }; diff --git a/libavcodec/libfdk-aacdec.c b/libavcodec/libfdk-aacdec.c index 8c1586e25ef..fa9d771c4e4 100644 --- a/libavcodec/libfdk-aacdec.c +++ b/libavcodec/libfdk-aacdec.c @@ -75,29 +75,29 @@ typedef struct FDKAACDecContext { #define OFFSET(x) offsetof(FDKAACDecContext, x) #define AD AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_DECODING_PARAM static const AVOption fdk_aac_dec_options[] = { - { "conceal", "Error concealment method", OFFSET(conceal_method), AV_OPT_TYPE_INT, { .i64 = CONCEAL_METHOD_NOISE_SUBSTITUTION }, CONCEAL_METHOD_SPECTRAL_MUTING, CONCEAL_METHOD_NB - 1, AD, "conceal" }, - { "spectral", "Spectral muting", 0, AV_OPT_TYPE_CONST, { .i64 = CONCEAL_METHOD_SPECTRAL_MUTING }, INT_MIN, INT_MAX, AD, "conceal" }, - { "noise", "Noise Substitution", 0, AV_OPT_TYPE_CONST, { .i64 = CONCEAL_METHOD_NOISE_SUBSTITUTION }, INT_MIN, INT_MAX, AD, "conceal" }, - { "energy", "Energy Interpolation", 0, AV_OPT_TYPE_CONST, { .i64 = CONCEAL_METHOD_ENERGY_INTERPOLATION }, INT_MIN, INT_MAX, AD, "conceal" }, + { "conceal", "Error concealment method", OFFSET(conceal_method), AV_OPT_TYPE_INT, { .i64 = CONCEAL_METHOD_NOISE_SUBSTITUTION }, CONCEAL_METHOD_SPECTRAL_MUTING, CONCEAL_METHOD_NB - 1, AD, .unit = "conceal" }, + { "spectral", "Spectral muting", 0, AV_OPT_TYPE_CONST, { .i64 = CONCEAL_METHOD_SPECTRAL_MUTING }, INT_MIN, INT_MAX, AD, .unit = "conceal" }, + { "noise", "Noise Substitution", 0, AV_OPT_TYPE_CONST, { .i64 = CONCEAL_METHOD_NOISE_SUBSTITUTION }, INT_MIN, INT_MAX, AD, .unit = "conceal" }, + { "energy", "Energy Interpolation", 0, AV_OPT_TYPE_CONST, { .i64 = CONCEAL_METHOD_ENERGY_INTERPOLATION }, INT_MIN, INT_MAX, AD, .unit = "conceal" }, { "drc_boost", "Dynamic Range Control: boost, where [0] is none and [127] is max boost", - OFFSET(drc_boost), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 127, AD, NULL }, + OFFSET(drc_boost), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 127, AD, .unit = NULL }, { "drc_cut", "Dynamic Range Control: attenuation factor, where [0] is none and [127] is max compression", - OFFSET(drc_cut), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 127, AD, NULL }, + OFFSET(drc_cut), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 127, AD, .unit = NULL }, { "drc_level", "Dynamic Range Control: reference level, quantized to 0.25dB steps where [0] is 0dB and [127] is -31.75dB, -1 for auto, and -2 for disabled", - OFFSET(drc_level), AV_OPT_TYPE_INT, { .i64 = -1}, -2, 127, AD, NULL }, + OFFSET(drc_level), AV_OPT_TYPE_INT, { .i64 = -1}, -2, 127, AD, .unit = NULL }, { "drc_heavy", "Dynamic Range Control: heavy compression, where [1] is on (RF mode) and [0] is off", - OFFSET(drc_heavy), AV_OPT_TYPE_INT, { .i64 = -1}, -1, 1, AD, NULL }, + OFFSET(drc_heavy), AV_OPT_TYPE_INT, { .i64 = -1}, -1, 1, AD, .unit = NULL }, #if FDKDEC_VER_AT_LEAST(2, 5) // 2.5.10 { "level_limit", "Signal level limiting", OFFSET(level_limit), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, AD }, #endif #if FDKDEC_VER_AT_LEAST(3, 0) // 3.0.0 { "drc_effect","Dynamic Range Control: effect type, where e.g. [0] is none and [6] is general", - OFFSET(drc_effect), AV_OPT_TYPE_INT, { .i64 = -1}, -1, 8, AD, NULL }, + OFFSET(drc_effect), AV_OPT_TYPE_INT, { .i64 = -1}, -1, 8, AD, .unit = NULL }, #endif #if FDKDEC_VER_AT_LEAST(3, 1) // 3.1.0 { "album_mode","Dynamic Range Control: album mode, where [0] is off and [1] is on", - OFFSET(album_mode), AV_OPT_TYPE_INT, { .i64 = -1}, -1, 1, AD, NULL }, + OFFSET(album_mode), AV_OPT_TYPE_INT, { .i64 = -1}, -1, 1, AD, .unit = NULL }, #endif { "downmix", "Request a specific channel layout from the decoder", OFFSET(downmix_layout), AV_OPT_TYPE_CHLAYOUT, {.str = NULL}, .flags = AD }, { NULL } @@ -264,14 +264,6 @@ static av_cold int fdk_aac_decode_init(AVCodecContext *avctx) return AVERROR_UNKNOWN; } -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (avctx->request_channel_layout) { - av_channel_layout_uninit(&s->downmix_layout); - av_channel_layout_from_mask(&s->downmix_layout, avctx->request_channel_layout); - } -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (s->downmix_layout.nb_channels > 0 && s->downmix_layout.order != AV_CHANNEL_ORDER_NATIVE) { int downmix_channels = -1; diff --git a/libavcodec/libfdk-aacenc.c b/libavcodec/libfdk-aacenc.c index 54549de4735..fe3ba14ee89 100644 --- a/libavcodec/libfdk-aacenc.c +++ b/libavcodec/libfdk-aacenc.c @@ -21,6 +21,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/common.h" +#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "avcodec.h" #include "audio_frame_queue.h" @@ -46,6 +47,15 @@ typedef struct AACContext { int latm; int header_period; int vbr; + int drc_profile; + int drc_target_ref; + int comp_profile; + int comp_target_ref; + int prog_ref; + int metadata_mode; + AACENC_MetaData metaDataSetup; + int delay_sent; + int frame_length; AudioFrameQueue afq; } AACContext; @@ -56,14 +66,20 @@ static const AVOption aac_enc_options[] = { #if FDKENC_VER_AT_LEAST(4, 0) // 4.0.0 { "eld_v2", "Enable ELDv2 (LD-MPS extension for ELD stereo signals)", offsetof(AACContext, eld_v2), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, #endif - { "signaling", "SBR/PS signaling style", offsetof(AACContext, signaling), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 2, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, "signaling" }, - { "default", "Choose signaling implicitly (explicit hierarchical by default, implicit if global header is disabled)", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, "signaling" }, - { "implicit", "Implicit backwards compatible signaling", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, "signaling" }, - { "explicit_sbr", "Explicit SBR, implicit PS signaling", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, "signaling" }, - { "explicit_hierarchical", "Explicit hierarchical signaling", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, "signaling" }, + { "signaling", "SBR/PS signaling style", offsetof(AACContext, signaling), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 2, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, .unit = "signaling" }, + { "default", "Choose signaling implicitly (explicit hierarchical by default, implicit if global header is disabled)", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, .unit = "signaling" }, + { "implicit", "Implicit backwards compatible signaling", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, .unit = "signaling" }, + { "explicit_sbr", "Explicit SBR, implicit PS signaling", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, .unit = "signaling" }, + { "explicit_hierarchical", "Explicit hierarchical signaling", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, .unit = "signaling" }, { "latm", "Output LATM/LOAS encapsulated data", offsetof(AACContext, latm), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, { "header_period", "StreamMuxConfig and PCE repetition period (in frames)", offsetof(AACContext, header_period), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 0xffff, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, { "vbr", "VBR mode (1-5)", offsetof(AACContext, vbr), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 5, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "drc_profile", "The desired compression profile for AAC DRC", offsetof(AACContext, drc_profile), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 256, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "drc_target_ref", "Expected target reference level at decoder side in dB (for clipping prevention/limiter)", offsetof(AACContext, drc_target_ref), AV_OPT_TYPE_INT, { .i64 = 0.0 }, -31.75, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "comp_profile", "The desired compression profile for AAC DRC", offsetof(AACContext, comp_profile), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 256, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "comp_target_ref", "Expected target reference level at decoder side in dB (for clipping prevention/limiter)", offsetof(AACContext, comp_target_ref), AV_OPT_TYPE_INT, { .i64 = 0.0 }, -31.75, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "prog_ref", "The program reference level or dialog level in dB", offsetof(AACContext, prog_ref), AV_OPT_TYPE_INT, { .i64 = 0.0 }, -31.75, 0, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, + { "frame_length", "The desired frame length", offsetof(AACContext, frame_length), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1024, AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }, FF_AAC_PROFILE_OPTS { NULL } }; @@ -118,6 +134,44 @@ static int aac_encode_close(AVCodecContext *avctx) return 0; } +static void aac_encode_flush(AVCodecContext *avctx) +{ + AACContext *s = avctx->priv_data; + AACENC_BufDesc in_buf = { 0 }, out_buf = { 0 }; + AACENC_InArgs in_args = { 0 }; + AACENC_OutArgs out_args; + int64_t pts, duration; + uint8_t dummy_in[1], dummy_out[1]; + int in_buffer_identifiers[] = { IN_AUDIO_DATA, IN_METADATA_SETUP }; + int in_buffer_element_sizes[] = { 2, sizeof(AACENC_MetaData) }; + int in_buffer_sizes[] = { 0, sizeof(s->metaDataSetup) }; + int out_buffer_identifier = OUT_BITSTREAM_DATA; + int out_buffer_size = sizeof(dummy_out), out_buffer_element_size = 1; + void* inBuffer[] = { dummy_in, &s->metaDataSetup }; + void *out_ptr = dummy_out; + AACENC_ERROR err; + + ff_af_queue_remove(&s->afq, s->afq.frame_count, &pts, &duration); + + in_buf.bufs = (void **)inBuffer; + in_buf.numBufs = s->metadata_mode == 0 ? 1 : 2; + in_buf.bufferIdentifiers = in_buffer_identifiers; + in_buf.bufSizes = in_buffer_sizes; + in_buf.bufElSizes = in_buffer_element_sizes; + + out_buf.numBufs = 1; + out_buf.bufs = &out_ptr; + out_buf.bufferIdentifiers = &out_buffer_identifier; + out_buf.bufSizes = &out_buffer_size; + out_buf.bufElSizes = &out_buffer_element_size; + + err = aacEncEncode(s->handle, &in_buf, &out_buf, &in_args, &out_args); + if (err != AACENC_OK) { + av_log(avctx, AV_LOG_ERROR, "Unexpected error while flushing: %s\n", + aac_get_error(err)); + } +} + static av_cold int aac_encode_init(AVCodecContext *avctx) { AACContext *s = avctx->priv_data; @@ -125,7 +179,7 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) AACENC_InfoStruct info = { 0 }; CHANNEL_MODE mode; AACENC_ERROR err; - int aot = FF_PROFILE_AAC_LOW + 1; + int aot = AV_PROFILE_AAC_LOW + 1; int sce = 0, cpe = 0; if ((err = aacEncOpen(&s->handle, 0, avctx->ch_layout.nb_channels)) != AACENC_OK) { @@ -134,7 +188,7 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) goto error; } - if (avctx->profile != FF_PROFILE_UNKNOWN) + if (avctx->profile != AV_PROFILE_UNKNOWN) aot = avctx->profile + 1; if ((err = aacEncoder_SetParam(s->handle, AACENC_AOT, aot)) != AACENC_OK) { @@ -143,7 +197,7 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) goto error; } - if (aot == FF_PROFILE_AAC_ELD + 1 && s->eld_sbr) { + if (aot == AV_PROFILE_AAC_ELD + 1 && s->eld_sbr) { if ((err = aacEncoder_SetParam(s->handle, AACENC_SBR_MODE, 1)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to enable SBR for ELD: %s\n", @@ -152,6 +206,15 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) } } + if (s->frame_length >= 0) { + if ((err = aacEncoder_SetParam(s->handle, AACENC_GRANULE_LENGTH, + s->frame_length)) != AACENC_OK) { + av_log(avctx, AV_LOG_ERROR, "Unable to set granule length: %s\n", + aac_get_error(err)); + goto error; + } + } + if ((err = aacEncoder_SetParam(s->handle, AACENC_SAMPLERATE, avctx->sample_rate)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to set the sample rate %d: %s\n", @@ -164,7 +227,7 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) case 2: #if FDKENC_VER_AT_LEAST(4, 0) // 4.0.0 // (profile + 1) to map from profile range to AOT range - if (aot == FF_PROFILE_AAC_ELD + 1 && s->eld_v2) { + if (aot == AV_PROFILE_AAC_ELD + 1 && s->eld_v2) { if ((err = aacEncoder_SetParam(s->handle, AACENC_CHANNELMODE, 128)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to enable ELDv2: %s\n", @@ -247,14 +310,14 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) } } else { if (avctx->bit_rate <= 0) { - if (avctx->profile == FF_PROFILE_AAC_HE_V2) { + if (avctx->profile == AV_PROFILE_AAC_HE_V2) { sce = 1; cpe = 0; } avctx->bit_rate = (96*sce + 128*cpe) * avctx->sample_rate / 44; - if (avctx->profile == FF_PROFILE_AAC_HE || - avctx->profile == FF_PROFILE_AAC_HE_V2 || - avctx->profile == FF_PROFILE_MPEG2_AAC_HE || + if (avctx->profile == AV_PROFILE_AAC_HE || + avctx->profile == AV_PROFILE_AAC_HE_V2 || + avctx->profile == AV_PROFILE_MPEG2_AAC_HE || s->eld_sbr) avctx->bit_rate /= 2; } @@ -319,6 +382,30 @@ static av_cold int aac_encode_init(AVCodecContext *avctx) } } + s->metadata_mode = 0; + if (s->prog_ref) { + s->metadata_mode = 1; + s->metaDataSetup.prog_ref_level_present = 1; + s->metaDataSetup.prog_ref_level = s->prog_ref << 16; + } + if (s->drc_profile) { + s->metadata_mode = 1; + s->metaDataSetup.drc_profile = s->drc_profile; + s->metaDataSetup.drc_TargetRefLevel = s->drc_target_ref << 16; + if (s->comp_profile) { + /* Including the comp_profile means that we need to set the mode to ETSI */ + s->metadata_mode = 2; + s->metaDataSetup.comp_profile = s->comp_profile; + s->metaDataSetup.comp_TargetRefLevel = s->comp_target_ref << 16; + } + } + + if ((err = aacEncoder_SetParam(s->handle, AACENC_METADATA_MODE, s->metadata_mode)) != AACENC_OK) { + av_log(avctx, AV_LOG_ERROR, "Unable to set metadata mode to %d: %s\n", + s->metadata_mode, aac_get_error(err)); + goto error; + } + if ((err = aacEncEncode(s->handle, NULL, NULL, NULL, NULL)) != AACENC_OK) { av_log(avctx, AV_LOG_ERROR, "Unable to initialize the encoder: %s\n", aac_get_error(err)); @@ -363,12 +450,14 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, AACENC_BufDesc in_buf = { 0 }, out_buf = { 0 }; AACENC_InArgs in_args = { 0 }; AACENC_OutArgs out_args = { 0 }; - int in_buffer_identifier = IN_AUDIO_DATA; - int in_buffer_size, in_buffer_element_size; + void* inBuffer[] = { 0, &s->metaDataSetup }; + int in_buffer_identifiers[] = { IN_AUDIO_DATA, IN_METADATA_SETUP }; + int in_buffer_element_sizes[] = { 2, sizeof(AACENC_MetaData) }; + int in_buffer_sizes[] = { 0, sizeof(s->metaDataSetup) }; int out_buffer_identifier = OUT_BITSTREAM_DATA; int out_buffer_size, out_buffer_element_size; - void *in_ptr, *out_ptr; - int ret; + void *out_ptr; + int ret, discard_padding; uint8_t dummy_buf[1]; AACENC_ERROR err; @@ -376,13 +465,12 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, if (!frame) { /* Must be a non-null pointer, even if it's a dummy. We could use * the address of anything else on the stack as well. */ - in_ptr = dummy_buf; - in_buffer_size = 0; + inBuffer[0] = dummy_buf; in_args.numInSamples = -1; } else { - in_ptr = frame->data[0]; - in_buffer_size = 2 * avctx->ch_layout.nb_channels * frame->nb_samples; + inBuffer[0] = frame->data[0]; + in_buffer_sizes[0] = 2 * avctx->ch_layout.nb_channels * frame->nb_samples; in_args.numInSamples = avctx->ch_layout.nb_channels * frame->nb_samples; @@ -391,12 +479,16 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, return ret; } - in_buffer_element_size = 2; - in_buf.numBufs = 1; - in_buf.bufs = &in_ptr; - in_buf.bufferIdentifiers = &in_buffer_identifier; - in_buf.bufSizes = &in_buffer_size; - in_buf.bufElSizes = &in_buffer_element_size; + if (s->metadata_mode == 0) { + in_buf.numBufs = 1; + } else { + in_buf.numBufs = 2; + } + + in_buf.bufs = (void**)inBuffer; + in_buf.bufferIdentifiers = in_buffer_identifiers; + in_buf.bufSizes = in_buffer_sizes; + in_buf.bufElSizes = in_buffer_element_sizes; /* The maximum packet size is 6144 bits aka 768 bytes per channel. */ ret = ff_alloc_packet(avctx, avpkt, FFMAX(8192, 768 * avctx->ch_layout.nb_channels)); @@ -428,18 +520,36 @@ static int aac_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, ff_af_queue_remove(&s->afq, avctx->frame_size, &avpkt->pts, &avpkt->duration); + discard_padding = avctx->frame_size - avpkt->duration; + // Check if subtraction resulted in an overflow + if ((discard_padding < avctx->frame_size) != (avpkt->duration > 0)) { + av_log(avctx, AV_LOG_ERROR, "discard padding overflow\n"); + return AVERROR(EINVAL); + } + if ((!s->delay_sent && avctx->initial_padding > 0) || discard_padding > 0) { + uint8_t *side_data = + av_packet_new_side_data(avpkt, AV_PKT_DATA_SKIP_SAMPLES, 10); + if (!side_data) + return AVERROR(ENOMEM); + if (!s->delay_sent) { + AV_WL32(side_data, avctx->initial_padding); + s->delay_sent = 1; + } + AV_WL32(side_data + 4, discard_padding); + } + avpkt->size = out_args.numOutBytes; *got_packet_ptr = 1; return 0; } static const AVProfile profiles[] = { - { FF_PROFILE_AAC_LOW, "LC" }, - { FF_PROFILE_AAC_HE, "HE-AAC" }, - { FF_PROFILE_AAC_HE_V2, "HE-AACv2" }, - { FF_PROFILE_AAC_LD, "LD" }, - { FF_PROFILE_AAC_ELD, "ELD" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_AAC_LOW, "LC" }, + { AV_PROFILE_AAC_HE, "HE-AAC" }, + { AV_PROFILE_AAC_HE_V2, "HE-AACv2" }, + { AV_PROFILE_AAC_LD, "LD" }, + { AV_PROFILE_AAC_ELD, "ELD" }, + { AV_PROFILE_UNKNOWN }, }; static const FFCodecDefault aac_encode_defaults[] = { @@ -447,28 +557,6 @@ static const FFCodecDefault aac_encode_defaults[] = { { NULL } }; -#if FF_API_OLD_CHANNEL_LAYOUT -static const uint64_t aac_channel_layout[] = { - AV_CH_LAYOUT_MONO, - AV_CH_LAYOUT_STEREO, - AV_CH_LAYOUT_SURROUND, - AV_CH_LAYOUT_4POINT0, - AV_CH_LAYOUT_5POINT0_BACK, - AV_CH_LAYOUT_5POINT1_BACK, -#if FDKENC_VER_AT_LEAST(4, 0) // 4.0.0 - AV_CH_LAYOUT_6POINT1_BACK, -#endif -#if FDKENC_VER_AT_LEAST(3, 4) // 3.4.12 - AV_CH_LAYOUT_7POINT1_WIDE_BACK, - AV_CH_LAYOUT_7POINT1, -#endif -#if FDKENC_VER_AT_LEAST(4, 0) // 4.0.0 - AV_CH_LAYOUT_7POINT1_TOP_BACK, -#endif - 0, -}; -#endif /* FF_API_OLD_CHANNEL_LAYOUT */ - static const AVChannelLayout aac_ch_layouts[16] = { AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, @@ -500,11 +588,13 @@ const FFCodec ff_libfdk_aac_encoder = { .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_AAC, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | + AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_SMALL_LAST_FRAME, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, .priv_data_size = sizeof(AACContext), .init = aac_encode_init, FF_CODEC_ENCODE_CB(aac_encode_frame), + .flush = aac_encode_flush, .close = aac_encode_close, .p.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, @@ -513,6 +603,5 @@ const FFCodec ff_libfdk_aac_encoder = { .p.profiles = profiles, .p.supported_samplerates = aac_sample_rates, .p.wrapper_name = "libfdk", - CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(aac_channel_layout) .p.ch_layouts = aac_ch_layouts, }; diff --git a/libavcodec/libgsmenc.c b/libavcodec/libgsmenc.c index 640954491a7..505b6afb07f 100644 --- a/libavcodec/libgsmenc.c +++ b/libavcodec/libgsmenc.c @@ -127,7 +127,6 @@ const FFCodec ff_libgsm_encoder = { FF_CODEC_ENCODE_CB(libgsm_encode_frame), .close = libgsm_encode_close, .defaults = libgsm_defaults, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO, { 0 } }, .p.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, @@ -146,7 +145,6 @@ const FFCodec ff_libgsm_ms_encoder = { FF_CODEC_ENCODE_CB(libgsm_encode_frame), .close = libgsm_encode_close, .defaults = libgsm_defaults, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO, { 0 } }, .p.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, diff --git a/libavcodec/libjxl.h b/libavcodec/libjxl.h index e305b6e7588..0b983a122f8 100644 --- a/libavcodec/libjxl.h +++ b/libavcodec/libjxl.h @@ -27,19 +27,8 @@ #ifndef AVCODEC_LIBJXL_H #define AVCODEC_LIBJXL_H -#include #include - -/* - * libjxl version 0.7.0 and earlier doesn't contain these macros at all - * so to detect version 0.7.0 versus 0.8.0 we need to define them ourselves - */ -#ifndef JPEGXL_COMPUTE_NUMERIC_VERSION - #define JPEGXL_COMPUTE_NUMERIC_VERSION(major,minor,patch) ((major<<24) | (minor<<16) | (patch<<8) | 0) -#endif -#ifndef JPEGXL_NUMERIC_VERSION - #define JPEGXL_NUMERIC_VERSION JPEGXL_COMPUTE_NUMERIC_VERSION(0, 7, 0) -#endif +#include /** * Transform threadcount in ffmpeg to one used by libjxl. diff --git a/libavcodec/libjxldec.c b/libavcodec/libjxldec.c index 045a1535f93..d57a27418f7 100644 --- a/libavcodec/libjxldec.c +++ b/libavcodec/libjxldec.c @@ -37,6 +37,7 @@ #include "avcodec.h" #include "codec_internal.h" #include "decode.h" +#include "internal.h" #include #include @@ -52,13 +53,20 @@ typedef struct LibJxlDecodeContext { #endif JxlDecoderStatus events; AVBufferRef *iccp; + AVPacket *avpkt; + int64_t accumulated_pts; + int64_t frame_duration; + int prev_is_last; + AVRational anim_timebase; + AVFrame *frame; } LibJxlDecodeContext; static int libjxl_init_jxl_decoder(AVCodecContext *avctx) { LibJxlDecodeContext *ctx = avctx->priv_data; - ctx->events = JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_COLOR_ENCODING; + ctx->events = JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE + | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME; if (JxlDecoderSubscribeEvents(ctx->decoder, ctx->events) != JXL_DEC_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Error subscribing to JXL events\n"); return AVERROR_EXTERNAL; @@ -71,6 +79,7 @@ static int libjxl_init_jxl_decoder(AVCodecContext *avctx) memset(&ctx->basic_info, 0, sizeof(JxlBasicInfo)); memset(&ctx->jxl_pixfmt, 0, sizeof(JxlPixelFormat)); + ctx->prev_is_last = 1; return 0; } @@ -93,6 +102,11 @@ static av_cold int libjxl_decode_init(AVCodecContext *avctx) return AVERROR_EXTERNAL; } + ctx->avpkt = avctx->internal->in_pkt; + ctx->frame = av_frame_alloc(); + if (!ctx->frame) + return AVERROR(ENOMEM); + return libjxl_init_jxl_decoder(avctx); } @@ -114,10 +128,11 @@ static enum AVPixelFormat libjxl_get_pix_fmt(AVCodecContext *avctx, LibJxlDecode return basic_info->alpha_bits ? AV_PIX_FMT_YA8 : AV_PIX_FMT_GRAY8; } if (basic_info->exponent_bits_per_sample || basic_info->bits_per_sample > 16) { - if (basic_info->alpha_bits) - return AV_PIX_FMT_NONE; - format->data_type = JXL_TYPE_FLOAT; - return AV_PIX_FMT_GRAYF32; + if (!basic_info->alpha_bits) { + format->data_type = JXL_TYPE_FLOAT; + return AV_PIX_FMT_GRAYF32; + } + av_log(avctx, AV_LOG_WARNING, "Downsampling gray+alpha float to 16-bit integer via libjxl\n"); } format->data_type = JXL_TYPE_UINT16; return basic_info->alpha_bits ? AV_PIX_FMT_YA16 : AV_PIX_FMT_GRAY16; @@ -129,10 +144,10 @@ static enum AVPixelFormat libjxl_get_pix_fmt(AVCodecContext *avctx, LibJxlDecode format->data_type = JXL_TYPE_UINT8; return basic_info->alpha_bits ? AV_PIX_FMT_RGBA : AV_PIX_FMT_RGB24; } - if (basic_info->exponent_bits_per_sample) - av_log(avctx, AV_LOG_WARNING, "Downsampling float to 16-bit integer via libjxl\n"); - else if (basic_info->bits_per_sample > 16) - av_log(avctx, AV_LOG_WARNING, "Downsampling larger integer to 16-bit via libjxl\n"); + if (basic_info->exponent_bits_per_sample || basic_info->bits_per_sample > 16) { + format->data_type = JXL_TYPE_FLOAT; + return basic_info->alpha_bits ? AV_PIX_FMT_RGBAF32 : AV_PIX_FMT_RGBF32; + } format->data_type = JXL_TYPE_UINT16; return basic_info->alpha_bits ? AV_PIX_FMT_RGBA64 : AV_PIX_FMT_RGB48; } @@ -198,14 +213,22 @@ static int libjxl_get_icc(AVCodecContext *avctx) JxlDecoderStatus jret; /* an ICC profile is present, and we can meaningfully get it, * because the pixel data is not XYB-encoded */ +#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0) jret = JxlDecoderGetICCProfileSize(ctx->decoder, &ctx->jxl_pixfmt, JXL_COLOR_PROFILE_TARGET_DATA, &icc_len); +#else + jret = JxlDecoderGetICCProfileSize(ctx->decoder, JXL_COLOR_PROFILE_TARGET_DATA, &icc_len); +#endif if (jret == JXL_DEC_SUCCESS && icc_len > 0) { av_buffer_unref(&ctx->iccp); ctx->iccp = av_buffer_alloc(icc_len); if (!ctx->iccp) return AVERROR(ENOMEM); +#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0) jret = JxlDecoderGetColorAsICCProfile(ctx->decoder, &ctx->jxl_pixfmt, JXL_COLOR_PROFILE_TARGET_DATA, - ctx->iccp->data, icc_len); + ctx->iccp->data, icc_len); +#else + jret = JxlDecoderGetColorAsICCProfile(ctx->decoder, JXL_COLOR_PROFILE_TARGET_DATA, ctx->iccp->data, icc_len); +#endif if (jret != JXL_DEC_SUCCESS) { av_log(avctx, AV_LOG_WARNING, "Unable to obtain ICC Profile\n"); av_buffer_unref(&ctx->iccp); @@ -241,12 +264,21 @@ static int libjxl_color_encoding_event(AVCodecContext *avctx, AVFrame *frame) /* set this flag if we need to fall back on wide gamut */ int fallback = 0; +#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0) jret = JxlDecoderGetColorAsEncodedProfile(ctx->decoder, NULL, JXL_COLOR_PROFILE_TARGET_ORIGINAL, &jxl_color); +#else + jret = JxlDecoderGetColorAsEncodedProfile(ctx->decoder, JXL_COLOR_PROFILE_TARGET_ORIGINAL, &jxl_color); +#endif if (jret == JXL_DEC_SUCCESS) { /* enum values describe the colors of this image */ jret = JxlDecoderSetPreferredColorProfile(ctx->decoder, &jxl_color); if (jret == JXL_DEC_SUCCESS) - jret = JxlDecoderGetColorAsEncodedProfile(ctx->decoder, &ctx->jxl_pixfmt, JXL_COLOR_PROFILE_TARGET_DATA, &jxl_color); +#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0) + jret = JxlDecoderGetColorAsEncodedProfile(ctx->decoder, &ctx->jxl_pixfmt, + JXL_COLOR_PROFILE_TARGET_DATA, &jxl_color); +#else + jret = JxlDecoderGetColorAsEncodedProfile(ctx->decoder, JXL_COLOR_PROFILE_TARGET_DATA, &jxl_color); +#endif /* if we couldn't successfully request the pixel data space, we fall back on wide gamut */ /* this code path is very unlikely to happen in practice */ if (jret != JXL_DEC_SUCCESS) @@ -269,7 +301,7 @@ static int libjxl_color_encoding_event(AVCodecContext *avctx, AVFrame *frame) } avctx->color_range = frame->color_range = AVCOL_RANGE_JPEG; - if (ctx->jxl_pixfmt.num_channels >= 3) + if (ctx->basic_info.num_color_channels > 1) avctx->colorspace = AVCOL_SPC_RGB; avctx->color_primaries = AVCOL_PRI_UNSPECIFIED; avctx->color_trc = AVCOL_TRC_UNSPECIFIED; @@ -305,7 +337,7 @@ static int libjxl_color_encoding_event(AVCodecContext *avctx, AVFrame *frame) } /* all colors will be in-gamut so we want accurate colors */ jxl_color.rendering_intent = JXL_RENDERING_INTENT_RELATIVE; - jxl_color.color_space = avctx->colorspace == AVCOL_SPC_RGB ? JXL_COLOR_SPACE_RGB : JXL_COLOR_SPACE_GRAY; + jxl_color.color_space = ctx->basic_info.num_color_channels > 1 ? JXL_COLOR_SPACE_RGB : JXL_COLOR_SPACE_GRAY; jret = JxlDecoderSetPreferredColorProfile(ctx->decoder, &jxl_color); if (jret != JXL_DEC_SUCCESS) { av_log(avctx, AV_LOG_WARNING, "Unable to set fallback color encoding\n"); @@ -328,19 +360,36 @@ static int libjxl_color_encoding_event(AVCodecContext *avctx, AVFrame *frame) return 0; } -static int libjxl_decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_frame, AVPacket *avpkt) +static int libjxl_receive_frame(AVCodecContext *avctx, AVFrame *frame) { LibJxlDecodeContext *ctx = avctx->priv_data; - const uint8_t *buf = avpkt->data; - size_t remaining = avpkt->size; - JxlDecoderStatus jret; + JxlDecoderStatus jret = JXL_DEC_SUCCESS; int ret; - *got_frame = 0; + AVPacket *pkt = ctx->avpkt; while (1) { + size_t remaining; + JxlFrameHeader header; - jret = JxlDecoderSetInput(ctx->decoder, buf, remaining); + if (!pkt->size) { + av_packet_unref(pkt); + ret = ff_decode_get_packet(avctx, pkt); + if (ret < 0 && ret != AVERROR_EOF) + return ret; + ctx->accumulated_pts = 0; + ctx->frame_duration = 0; + if (!pkt->size) { + /* jret set by the last iteration of the loop */ + if (jret == JXL_DEC_NEED_MORE_INPUT) { + av_log(avctx, AV_LOG_ERROR, "Unexpected end of JXL codestream\n"); + return AVERROR_INVALIDDATA; + } else { + return AVERROR_EOF; + } + } + } + jret = JxlDecoderSetInput(ctx->decoder, pkt->data, pkt->size); if (jret == JXL_DEC_ERROR) { /* this should never happen here unless there's a bug in libjxl */ av_log(avctx, AV_LOG_ERROR, "Unknown libjxl decode error\n"); @@ -354,17 +403,14 @@ static int libjxl_decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_f * the number of bytes that it did read */ remaining = JxlDecoderReleaseInput(ctx->decoder); - buf = avpkt->data + avpkt->size - remaining; + pkt->data += pkt->size - remaining; + pkt->size = remaining; switch(jret) { case JXL_DEC_ERROR: av_log(avctx, AV_LOG_ERROR, "Unknown libjxl decode error\n"); return AVERROR_INVALIDDATA; case JXL_DEC_NEED_MORE_INPUT: - if (remaining == 0) { - av_log(avctx, AV_LOG_ERROR, "Unexpected end of JXL codestream\n"); - return AVERROR_INVALIDDATA; - } av_log(avctx, AV_LOG_DEBUG, "NEED_MORE_INPUT event emitted\n"); continue; case JXL_DEC_BASIC_INFO: @@ -384,19 +430,25 @@ static int libjxl_decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_f } if ((ret = ff_set_dimensions(avctx, ctx->basic_info.xsize, ctx->basic_info.ysize)) < 0) return ret; + if (ctx->basic_info.have_animation) + ctx->anim_timebase = av_make_q(ctx->basic_info.animation.tps_denominator, + ctx->basic_info.animation.tps_numerator); continue; case JXL_DEC_COLOR_ENCODING: av_log(avctx, AV_LOG_DEBUG, "COLOR_ENCODING event emitted\n"); - if ((ret = libjxl_color_encoding_event(avctx, frame)) < 0) + ret = libjxl_color_encoding_event(avctx, ctx->frame); + if (ret < 0) return ret; continue; case JXL_DEC_NEED_IMAGE_OUT_BUFFER: av_log(avctx, AV_LOG_DEBUG, "NEED_IMAGE_OUT_BUFFER event emitted\n"); - if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + ret = ff_get_buffer(avctx, ctx->frame, 0); + if (ret < 0) return ret; - ctx->jxl_pixfmt.align = frame->linesize[0]; - if (JxlDecoderSetImageOutBuffer(ctx->decoder, &ctx->jxl_pixfmt, frame->data[0], frame->buf[0]->size) - != JXL_DEC_SUCCESS) { + ctx->jxl_pixfmt.align = ctx->frame->linesize[0]; + if (JxlDecoderSetImageOutBuffer(ctx->decoder, &ctx->jxl_pixfmt, + ctx->frame->data[0], ctx->frame->buf[0]->size) + != JXL_DEC_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Bad libjxl dec need image out buffer event\n"); return AVERROR_EXTERNAL; } @@ -407,37 +459,57 @@ static int libjxl_decode_frame(AVCodecContext *avctx, AVFrame *frame, int *got_f } #endif continue; + case JXL_DEC_FRAME: + /* Frame here refers to the Frame bundle, not a decoded picture */ + av_log(avctx, AV_LOG_DEBUG, "FRAME event emitted\n"); + if (ctx->prev_is_last) { + /* + * The last frame sent was tagged as "is_last" which + * means this is a new image file altogether. + */ + ctx->frame->pict_type = AV_PICTURE_TYPE_I; + ctx->frame->flags |= AV_FRAME_FLAG_KEY; + } + if (JxlDecoderGetFrameHeader(ctx->decoder, &header) != JXL_DEC_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Bad libjxl dec frame event\n"); + return AVERROR_EXTERNAL; + } + ctx->prev_is_last = header.is_last; + /* zero duration for animation means the frame is not presented */ + if (ctx->basic_info.have_animation && header.duration) + ctx->frame_duration = header.duration; + continue; case JXL_DEC_FULL_IMAGE: /* full image is one frame, even if animated */ av_log(avctx, AV_LOG_DEBUG, "FULL_IMAGE event emitted\n"); - frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; if (ctx->iccp) { - AVFrameSideData *sd = av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_ICC_PROFILE, ctx->iccp); - if (!sd) - return AVERROR(ENOMEM); - /* ownership is transfered, and it is not ref-ed */ - ctx->iccp = NULL; + ret = ff_frame_new_side_data_from_buf(avctx, ctx->frame, AV_FRAME_DATA_ICC_PROFILE, &ctx->iccp, NULL); + if (ret < 0) + return ret; + } + if (ctx->basic_info.have_animation) { + ctx->frame->pts = av_rescale_q(ctx->accumulated_pts, ctx->anim_timebase, avctx->pkt_timebase); + ctx->frame->duration = av_rescale_q(ctx->frame_duration, ctx->anim_timebase, avctx->pkt_timebase); + } else { + ctx->frame->pts = 0; + ctx->frame->duration = pkt->duration; } - *got_frame = 1; - return avpkt->size - remaining; + if (pkt->pts != AV_NOPTS_VALUE) + ctx->frame->pts += pkt->pts; + ctx->accumulated_pts += ctx->frame_duration; + ctx->frame->pkt_dts = pkt->dts; + av_frame_move_ref(frame, ctx->frame); + return 0; case JXL_DEC_SUCCESS: av_log(avctx, AV_LOG_DEBUG, "SUCCESS event emitted\n"); /* - * The SUCCESS event isn't fired until after JXL_DEC_FULL_IMAGE. If this - * stream only contains one JXL image then JXL_DEC_SUCCESS will never fire. - * If the image2 sequence being decoded contains several JXL files, then - * libjxl will fire this event after the next AVPacket has been passed, - * which means the current packet is actually the next image in the sequence. - * This is why we reset the decoder and populate the packet data now, since - * this is the next packet and it has not been decoded yet. The decoder does - * have to be reset to allow us to use it for the next image, or libjxl - * will become very confused if the header information is not identical. + * this event will be fired when the zero-length EOF + * packet is sent to the decoder by the client, + * but it will also be fired when the next image of + * an image2pipe sequence is loaded up */ JxlDecoderReset(ctx->decoder); libjxl_init_jxl_decoder(avctx); - buf = avpkt->data; - remaining = avpkt->size; continue; default: av_log(avctx, AV_LOG_ERROR, "Bad libjxl event: %d\n", jret); @@ -457,6 +529,7 @@ static av_cold int libjxl_decode_close(AVCodecContext *avctx) JxlDecoderDestroy(ctx->decoder); ctx->decoder = NULL; av_buffer_unref(&ctx->iccp); + av_frame_free(&ctx->frame); return 0; } @@ -468,7 +541,7 @@ const FFCodec ff_libjxl_decoder = { .p.id = AV_CODEC_ID_JPEGXL, .priv_data_size = sizeof(LibJxlDecodeContext), .init = libjxl_decode_init, - FF_CODEC_DECODE_CB(libjxl_decode_frame), + FF_CODEC_RECEIVE_FRAME_CB(libjxl_receive_frame), .close = libjxl_decode_close, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_OTHER_THREADS, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | diff --git a/libavcodec/libjxlenc.c b/libavcodec/libjxlenc.c index 897452f5750..3f2c74097db 100644 --- a/libavcodec/libjxlenc.c +++ b/libavcodec/libjxlenc.c @@ -52,6 +52,7 @@ typedef struct LibJxlEncodeContext { int effort; float distance; int modular; + int xyb; uint8_t *buffer; size_t buffer_size; } LibJxlEncodeContext; @@ -85,8 +86,8 @@ static float quality_to_distance(float quality) } /** - * Initalize the decoder on a per-frame basis. All of these need to be set - * once each time the decoder is reset, which it must be each frame to make + * Initalize the encoder on a per-frame basis. All of these need to be set + * once each time the encoder is reset, which it must be each frame to make * the image2 muxer work. * * @return 0 upon success, negative on failure. @@ -104,7 +105,7 @@ static int libjxl_init_jxl_encoder(AVCodecContext *avctx) return AVERROR_EXTERNAL; } - /* This needs to be set each time the decoder is reset */ + /* This needs to be set each time the encoder is reset */ if (JxlEncoderSetParallelRunner(ctx->encoder, JxlThreadParallelRunner, ctx->runner) != JXL_ENC_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to set JxlThreadParallelRunner\n"); @@ -259,6 +260,7 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra size_t available = ctx->buffer_size; size_t bytes_written = 0; uint8_t *next_out = ctx->buffer; + const uint8_t *data; ret = libjxl_init_jxl_encoder(avctx); if (ret) { @@ -302,7 +304,8 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra av_log(avctx, AV_LOG_WARNING, "Unknown color range, assuming full (pc)\n"); /* bitexact lossless requires there to be no XYB transform */ - info.uses_original_profile = ctx->distance == 0.0; + info.uses_original_profile = ctx->distance == 0.0 || !ctx->xyb; + info.orientation = frame->linesize[0] >= 0 ? JXL_ORIENT_IDENTITY : JXL_ORIENT_FLIP_VERTICAL; if (JxlEncoderSetBasicInfo(ctx->encoder, &info) != JXL_ENC_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to set JxlBasicInfo\n"); @@ -335,11 +338,11 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra break; case AVCOL_TRC_GAMMA22: jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; - jxl_color.gamma = 2.2; + jxl_color.gamma = 1/2.2f; break; case AVCOL_TRC_GAMMA28: jxl_color.transfer_function = JXL_TRANSFER_FUNCTION_GAMMA; - jxl_color.gamma = 2.8; + jxl_color.gamma = 1/2.8f; break; default: if (pix_desc->flags & AV_PIX_FMT_FLAG_FLOAT) { @@ -383,9 +386,15 @@ static int libjxl_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFra } jxl_fmt.endianness = JXL_NATIVE_ENDIAN; - jxl_fmt.align = frame->linesize[0]; + if (frame->linesize[0] >= 0) { + jxl_fmt.align = frame->linesize[0]; + data = frame->data[0]; + } else { + jxl_fmt.align = -frame->linesize[0]; + data = frame->data[0] + frame->linesize[0] * (info.ysize - 1); + } - if (JxlEncoderAddImageFrame(ctx->options, &jxl_fmt, frame->data[0], jxl_fmt.align * info.ysize) != JXL_ENC_SUCCESS) { + if (JxlEncoderAddImageFrame(ctx->options, &jxl_fmt, data, jxl_fmt.align * info.ysize) != JXL_ENC_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to add Image Frame\n"); return AVERROR_EXTERNAL; } @@ -446,7 +455,7 @@ static av_cold int libjxl_encode_close(AVCodecContext *avctx) ctx->runner = NULL; /* - * destroying the decoder also frees + * destroying the encoder also frees * ctx->options so we don't need to */ if (ctx->encoder) @@ -466,6 +475,8 @@ static const AVOption libjxl_encode_options[] = { { "distance", "Maximum Butteraugli distance (quality setting, " "lower = better, zero = lossless, default 1.0)", OFFSET(distance), AV_OPT_TYPE_FLOAT, { .dbl = -1.0 }, -1.0, 15.0, VE }, { "modular", "Force modular mode", OFFSET(modular), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, + { "xyb", "Use XYB-encoding for lossy images", OFFSET(xyb), + AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE }, { NULL }, }; @@ -486,6 +497,7 @@ const FFCodec ff_libjxl_encoder = { FF_CODEC_ENCODE_CB(libjxl_encode_frame), .close = libjxl_encode_close, .p.capabilities = AV_CODEC_CAP_OTHER_THREADS | + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP | @@ -493,6 +505,7 @@ const FFCodec ff_libjxl_encoder = { .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, AV_PIX_FMT_RGB48, AV_PIX_FMT_RGBA64, + AV_PIX_FMT_RGBF32, AV_PIX_FMT_RGBAF32, AV_PIX_FMT_GRAY8, AV_PIX_FMT_YA8, AV_PIX_FMT_GRAY16, AV_PIX_FMT_YA16, AV_PIX_FMT_GRAYF32, diff --git a/libavcodec/libkvazaar.c b/libavcodec/libkvazaar.c index 168486f4ecb..0711d9ab387 100644 --- a/libavcodec/libkvazaar.c +++ b/libavcodec/libkvazaar.c @@ -85,13 +85,14 @@ static av_cold int libkvazaar_init(AVCodecContext *avctx) cfg->framerate_num = avctx->framerate.num; cfg->framerate_denom = avctx->framerate.den; } else { - if (avctx->ticks_per_frame > INT_MAX / avctx->time_base.num) { - av_log(avctx, AV_LOG_ERROR, - "Could not set framerate for kvazaar: integer overflow\n"); - return AVERROR(EINVAL); - } cfg->framerate_num = avctx->time_base.den; - cfg->framerate_denom = avctx->time_base.num * avctx->ticks_per_frame; +FF_DISABLE_DEPRECATION_WARNINGS + cfg->framerate_denom = avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } cfg->target_bitrate = avctx->bit_rate; cfg->vui.sar_width = avctx->sample_aspect_ratio.num; @@ -100,6 +101,13 @@ static av_cold int libkvazaar_init(AVCodecContext *avctx) cfg->rc_algorithm = KVZ_LAMBDA; } + cfg->vui.fullrange = avctx->color_range == AVCOL_RANGE_JPEG; + cfg->vui.colorprim = avctx->color_primaries; + cfg->vui.transfer = avctx->color_trc; + cfg->vui.colormatrix = avctx->colorspace; + if (avctx->chroma_sample_location != AVCHROMA_LOC_UNSPECIFIED) + cfg->vui.chroma_loc = avctx->chroma_sample_location - 1; + if (ctx->kvz_params) { AVDictionary *dict = NULL; if (!av_dict_parse_string(&dict, ctx->kvz_params, "=", ",", 0)) { @@ -220,9 +228,9 @@ static int libkvazaar_encode(AVCodecContext *avctx, frame->width / 2, 0 }; - av_image_copy(dst, dst_linesizes, - (const uint8_t **)frame->data, frame->linesize, - frame->format, frame->width, frame->height); + av_image_copy2(dst, dst_linesizes, + frame->data, frame->linesize, + frame->format, frame->width, frame->height); } input_pic->pts = frame->pts; diff --git a/libavcodec/libmp3lame.c b/libavcodec/libmp3lame.c index 26e58baa3d0..d3810eb2ab0 100644 --- a/libavcodec/libmp3lame.c +++ b/libavcodec/libmp3lame.c @@ -55,6 +55,8 @@ typedef struct LAMEContext { float *samples_flt[2]; AudioFrameQueue afq; AVFloatDSPContext *fdsp; + int copyright; + int original; } LAMEContext; @@ -137,6 +139,12 @@ static av_cold int mp3lame_encode_init(AVCodecContext *avctx) /* bit reservoir usage */ lame_set_disable_reservoir(s->gfp, !s->reservoir); + /* copyright flag */ + lame_set_copyright(s->gfp, s->copyright); + + /* original flag */ + lame_set_original(s->gfp, s->original); + /* set specified parameters */ if (lame_init_params(s->gfp) < 0) { ret = AVERROR_EXTERNAL; @@ -280,17 +288,14 @@ static int mp3lame_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, // Check if subtraction resulted in an overflow if ((discard_padding < avctx->frame_size) != (avpkt->duration > 0)) { av_log(avctx, AV_LOG_ERROR, "discard padding overflow\n"); - av_packet_unref(avpkt); return AVERROR(EINVAL); } if ((!s->delay_sent && avctx->initial_padding > 0) || discard_padding > 0) { uint8_t* side_data = av_packet_new_side_data(avpkt, AV_PKT_DATA_SKIP_SAMPLES, 10); - if(!side_data) { - av_packet_unref(avpkt); + if (!side_data) return AVERROR(ENOMEM); - } if (!s->delay_sent) { AV_WL32(side_data, avctx->initial_padding); s->delay_sent = 1; @@ -306,9 +311,11 @@ static int mp3lame_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, #define OFFSET(x) offsetof(LAMEContext, x) #define AE AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "reservoir", "use bit reservoir", OFFSET(reservoir), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AE }, - { "joint_stereo", "use joint stereo", OFFSET(joint_stereo), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AE }, - { "abr", "use ABR", OFFSET(abr), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AE }, + { "reservoir", "use bit reservoir", OFFSET(reservoir), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AE }, + { "joint_stereo", "use joint stereo", OFFSET(joint_stereo), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AE }, + { "abr", "use ABR", OFFSET(abr), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AE }, + { "copyright", "set copyright flag", OFFSET(copyright), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AE}, + { "original", "set original flag", OFFSET(original), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, AE}, { NULL }, }; @@ -345,7 +352,6 @@ const FFCodec ff_libmp3lame_encoder = { AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_NONE }, .p.supported_samplerates = libmp3lame_sample_rates, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, { 0 }, diff --git a/libavcodec/libopencore-amr.c b/libavcodec/libopencore-amr.c index fd9e6e63430..641a1561298 100644 --- a/libavcodec/libopencore-amr.c +++ b/libavcodec/libopencore-amr.c @@ -106,8 +106,8 @@ static int amr_nb_decode_frame(AVCodecContext *avctx, AVFrame *frame, enum Mode dec_mode; int packet_size, ret; - ff_dlog(avctx, "amr_decode_frame buf=%p buf_size=%d frame_count=%d!!\n", - buf, buf_size, avctx->frame_number); + ff_dlog(avctx, "amr_decode_frame buf=%p buf_size=%d frame_count=%"PRId64"!!\n", + buf, buf_size, avctx->frame_num); /* get output buffer */ frame->nb_samples = 160; diff --git a/libavcodec/libopenh264.c b/libavcodec/libopenh264.c index 0f6d28ed88c..c80c85ea8b2 100644 --- a/libavcodec/libopenh264.c +++ b/libavcodec/libopenh264.c @@ -46,18 +46,3 @@ void ff_libopenh264_trace_callback(void *ctx, int level, const char *msg) int equiv_ffmpeg_log_level = libopenh264_to_ffmpeg_log_level(level); av_log(ctx, equiv_ffmpeg_log_level, "%s\n", msg); } - -int ff_libopenh264_check_version(void *logctx) -{ - // Mingw GCC < 4.7 on x86_32 uses an incorrect/buggy ABI for the WelsGetCodecVersion - // function (for functions returning larger structs), thus skip the check in those - // configurations. -#if !defined(_WIN32) || !defined(__GNUC__) || !ARCH_X86_32 || AV_GCC_VERSION_AT_LEAST(4, 7) - OpenH264Version libver = WelsGetCodecVersion(); - if (memcmp(&libver, &g_stCodecVersion, sizeof(libver))) { - av_log(logctx, AV_LOG_ERROR, "Incorrect library version loaded\n"); - return AVERROR(EINVAL); - } -#endif - return 0; -} diff --git a/libavcodec/libopenh264.h b/libavcodec/libopenh264.h index dbb9c5d429e..0b462d6fdcd 100644 --- a/libavcodec/libopenh264.h +++ b/libavcodec/libopenh264.h @@ -34,6 +34,4 @@ void ff_libopenh264_trace_callback(void *ctx, int level, const char *msg); -int ff_libopenh264_check_version(void *logctx); - #endif /* AVCODEC_LIBOPENH264_H */ diff --git a/libavcodec/libopenh264dec.c b/libavcodec/libopenh264dec.c index af53219b410..b6a9bba2dc7 100644 --- a/libavcodec/libopenh264dec.c +++ b/libavcodec/libopenh264dec.c @@ -52,13 +52,9 @@ static av_cold int svc_decode_init(AVCodecContext *avctx) { SVCContext *s = avctx->priv_data; SDecodingParam param = { 0 }; - int err; int log_level; WelsTraceCallback callback_function; - if ((err = ff_libopenh264_check_version(avctx)) < 0) - return AVERROR_DECODER_NOT_FOUND; - if (WelsCreateDecoder(&s->decoder)) { av_log(avctx, AV_LOG_ERROR, "Unable to create decoder\n"); return AVERROR_UNKNOWN; @@ -141,7 +137,8 @@ static int svc_decode_frame(AVCodecContext *avctx, AVFrame *avframe, linesize[0] = info.UsrData.sSystemBuffer.iStride[0]; linesize[1] = linesize[2] = info.UsrData.sSystemBuffer.iStride[1]; linesize[3] = 0; - av_image_copy(avframe->data, avframe->linesize, (const uint8_t **) ptrs, linesize, avctx->pix_fmt, avctx->width, avctx->height); + av_image_copy2(avframe->data, avframe->linesize, ptrs, linesize, + avctx->pix_fmt, avctx->width, avctx->height); avframe->pts = info.uiOutYuvTimeStamp; avframe->pkt_dts = AV_NOPTS_VALUE; diff --git a/libavcodec/libopenh264enc.c b/libavcodec/libopenh264enc.c index 8b4755f5ba4..eef769eed0d 100644 --- a/libavcodec/libopenh264enc.c +++ b/libavcodec/libopenh264enc.c @@ -61,28 +61,28 @@ typedef struct SVCContext { #define DEPRECATED AV_OPT_FLAG_DEPRECATED static const AVOption options[] = { { "loopfilter", "enable loop filter", OFFSET(loopfilter), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE }, - { "profile", "set profile restrictions", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xffff, VE, "profile" }, -#define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, { .i64 = value }, 0, 0, VE, "profile" - { PROFILE("constrained_baseline", FF_PROFILE_H264_CONSTRAINED_BASELINE) }, - { PROFILE("main", FF_PROFILE_H264_MAIN) }, - { PROFILE("high", FF_PROFILE_H264_HIGH) }, + { "profile", "set profile restrictions", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = AV_PROFILE_UNKNOWN }, AV_PROFILE_UNKNOWN, 0xffff, VE, .unit = "profile" }, +#define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, { .i64 = value }, 0, 0, VE, .unit = "profile" + { PROFILE("constrained_baseline", AV_PROFILE_H264_CONSTRAINED_BASELINE) }, + { PROFILE("main", AV_PROFILE_H264_MAIN) }, + { PROFILE("high", AV_PROFILE_H264_HIGH) }, #undef PROFILE { "max_nal_size", "set maximum NAL size in bytes", OFFSET(max_nal_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, { "allow_skip_frames", "allow skipping frames to hit the target bitrate", OFFSET(skip_frames), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, - { "coder", "Coder type", OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE, "coder" }, - { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX, VE, "coder" }, - { "cavlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "coder" }, - { "cabac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "coder" }, - { "vlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "coder" }, - { "ac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "coder" }, - - { "rc_mode", "Select rate control mode", OFFSET(rc_mode), AV_OPT_TYPE_INT, { .i64 = RC_QUALITY_MODE }, RC_OFF_MODE, RC_TIMESTAMP_MODE, VE, "rc_mode" }, - { "off", "bit rate control off", 0, AV_OPT_TYPE_CONST, { .i64 = RC_OFF_MODE }, 0, 0, VE, "rc_mode" }, - { "quality", "quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RC_QUALITY_MODE }, 0, 0, VE, "rc_mode" }, - { "bitrate", "bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = RC_BITRATE_MODE }, 0, 0, VE, "rc_mode" }, - { "buffer", "using buffer status to adjust the video quality (no bitrate control)", 0, AV_OPT_TYPE_CONST, { .i64 = RC_BUFFERBASED_MODE }, 0, 0, VE, "rc_mode" }, + { "coder", "Coder type", OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE, .unit = "coder" }, + { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX, VE, .unit = "coder" }, + { "cavlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, .unit = "coder" }, + { "cabac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "coder" }, + { "vlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, .unit = "coder" }, + { "ac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "coder" }, + + { "rc_mode", "Select rate control mode", OFFSET(rc_mode), AV_OPT_TYPE_INT, { .i64 = RC_QUALITY_MODE }, RC_OFF_MODE, RC_TIMESTAMP_MODE, VE, .unit = "rc_mode" }, + { "off", "bit rate control off", 0, AV_OPT_TYPE_CONST, { .i64 = RC_OFF_MODE }, 0, 0, VE, .unit = "rc_mode" }, + { "quality", "quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RC_QUALITY_MODE }, 0, 0, VE, .unit = "rc_mode" }, + { "bitrate", "bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = RC_BITRATE_MODE }, 0, 0, VE, .unit = "rc_mode" }, + { "buffer", "using buffer status to adjust the video quality (no bitrate control)", 0, AV_OPT_TYPE_CONST, { .i64 = RC_BUFFERBASED_MODE }, 0, 0, VE, .unit = "rc_mode" }, #if OPENH264_VER_AT_LEAST(1, 4) - { "timestamp", "bit rate control based on timestamp", 0, AV_OPT_TYPE_CONST, { .i64 = RC_TIMESTAMP_MODE }, 0, 0, VE, "rc_mode" }, + { "timestamp", "bit rate control based on timestamp", 0, AV_OPT_TYPE_CONST, { .i64 = RC_TIMESTAMP_MODE }, 0, 0, VE, .unit = "rc_mode" }, #endif { NULL } @@ -110,14 +110,10 @@ static av_cold int svc_encode_init(AVCodecContext *avctx) { SVCContext *s = avctx->priv_data; SEncParamExt param = { 0 }; - int err; int log_level; WelsTraceCallback callback_function; AVCPBProperties *props; - if ((err = ff_libopenh264_check_version(avctx)) < 0) - return AVERROR_ENCODER_NOT_FOUND; - if (WelsCreateSVCEncoder(&s->encoder)) { av_log(avctx, AV_LOG_ERROR, "Unable to create encoder\n"); return AVERROR_UNKNOWN; @@ -139,12 +135,13 @@ static av_cold int svc_encode_init(AVCodecContext *avctx) if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { param.fMaxFrameRate = av_q2d(avctx->framerate); } else { - if (avctx->ticks_per_frame > INT_MAX / avctx->time_base.num) { - av_log(avctx, AV_LOG_ERROR, - "Could not set framerate for libopenh264enc: integer overflow\n"); - return AVERROR(EINVAL); - } - param.fMaxFrameRate = 1.0 / av_q2d(avctx->time_base) / FFMAX(avctx->ticks_per_frame, 1); +FF_DISABLE_DEPRECATION_WARNINGS + param.fMaxFrameRate = 1.0 / av_q2d(avctx->time_base) +#if FF_API_TICKS_PER_FRAME + / FFMAX(avctx->ticks_per_frame, 1) +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } param.iPicWidth = avctx->width; param.iPicHeight = avctx->height; @@ -176,12 +173,12 @@ static av_cold int svc_encode_init(AVCodecContext *avctx) param.iMultipleThreadIdc = avctx->thread_count; /* Allow specifying the libopenh264 profile through AVCodecContext. */ - if (FF_PROFILE_UNKNOWN == s->profile && - FF_PROFILE_UNKNOWN != avctx->profile) + if (AV_PROFILE_UNKNOWN == s->profile && + AV_PROFILE_UNKNOWN != avctx->profile) switch (avctx->profile) { - case FF_PROFILE_H264_HIGH: - case FF_PROFILE_H264_MAIN: - case FF_PROFILE_H264_CONSTRAINED_BASELINE: + case AV_PROFILE_H264_HIGH: + case AV_PROFILE_H264_MAIN: + case AV_PROFILE_H264_CONSTRAINED_BASELINE: s->profile = avctx->profile; break; default: @@ -190,34 +187,34 @@ static av_cold int svc_encode_init(AVCodecContext *avctx) break; } - if (s->profile == FF_PROFILE_UNKNOWN && s->coder >= 0) - s->profile = s->coder == 0 ? FF_PROFILE_H264_CONSTRAINED_BASELINE : + if (s->profile == AV_PROFILE_UNKNOWN && s->coder >= 0) + s->profile = s->coder == 0 ? AV_PROFILE_H264_CONSTRAINED_BASELINE : #if OPENH264_VER_AT_LEAST(1, 8) - FF_PROFILE_H264_HIGH; + AV_PROFILE_H264_HIGH; #else - FF_PROFILE_H264_MAIN; + AV_PROFILE_H264_MAIN; #endif switch (s->profile) { - case FF_PROFILE_H264_HIGH: + case AV_PROFILE_H264_HIGH: av_log(avctx, AV_LOG_VERBOSE, "Using %s, " "select EProfileIdc PRO_HIGH in libopenh264.\n", param.iEntropyCodingModeFlag ? "CABAC" : "CAVLC"); break; - case FF_PROFILE_H264_MAIN: + case AV_PROFILE_H264_MAIN: av_log(avctx, AV_LOG_VERBOSE, "Using %s, " "select EProfileIdc PRO_MAIN in libopenh264.\n", param.iEntropyCodingModeFlag ? "CABAC" : "CAVLC"); break; - case FF_PROFILE_H264_CONSTRAINED_BASELINE: - case FF_PROFILE_UNKNOWN: - s->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE; + case AV_PROFILE_H264_CONSTRAINED_BASELINE: + case AV_PROFILE_UNKNOWN: + s->profile = AV_PROFILE_H264_CONSTRAINED_BASELINE; param.iEntropyCodingModeFlag = 0; av_log(avctx, AV_LOG_VERBOSE, "Using CAVLC, " "select EProfileIdc PRO_BASELINE in libopenh264.\n"); break; default: - s->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE; + s->profile = AV_PROFILE_H264_CONSTRAINED_BASELINE; param.iEntropyCodingModeFlag = 0; av_log(avctx, AV_LOG_WARNING, "Unsupported profile, " "select EProfileIdc PRO_BASELINE in libopenh264.\n"); @@ -311,15 +308,15 @@ static av_cold int svc_encode_init(AVCodecContext *avctx) #if OPENH264_VER_AT_LEAST(1, 6) param.sSpatialLayers[0].uiVideoFormat = VF_UNDEF; + if (avctx->color_range != AVCOL_RANGE_UNSPECIFIED) { - param.sSpatialLayers[0].bVideoSignalTypePresent = true; param.sSpatialLayers[0].bFullRange = (avctx->color_range == AVCOL_RANGE_JPEG); - } + } else if (avctx->pix_fmt == AV_PIX_FMT_YUVJ420P) + param.sSpatialLayers[0].bFullRange = 1; if (avctx->colorspace != AVCOL_SPC_UNSPECIFIED || avctx->color_primaries != AVCOL_PRI_UNSPECIFIED || avctx->color_trc != AVCOL_TRC_UNSPECIFIED) { - param.sSpatialLayers[0].bVideoSignalTypePresent = true; param.sSpatialLayers[0].bColorDescriptionPresent = true; } @@ -329,6 +326,9 @@ static av_cold int svc_encode_init(AVCodecContext *avctx) param.sSpatialLayers[0].uiColorPrimaries = avctx->color_primaries; if (avctx->color_trc != AVCOL_TRC_UNSPECIFIED) param.sSpatialLayers[0].uiTransferCharacteristics = avctx->color_trc; + + param.sSpatialLayers[0].bVideoSignalTypePresent = + (param.sSpatialLayers[0].bFullRange || param.sSpatialLayers[0].bColorDescriptionPresent); #endif if ((*s->encoder)->InitializeExt(s->encoder, ¶m) != cmResultSuccess) { @@ -349,7 +349,7 @@ static av_cold int svc_encode_init(AVCodecContext *avctx) memcpy(avctx->extradata, fbi.sLayerInfo[0].pBsBuf, size); } - props = ff_add_cpb_side_data(avctx); + props = ff_encode_add_cpb_side_data(avctx); if (!props) return AVERROR(ENOMEM); props->max_bitrate = param.iMaxBitrate; @@ -443,6 +443,7 @@ const FFCodec ff_libopenh264_encoder = { .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_AUTO_THREADS, .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_NONE }, .defaults = svc_enc_defaults, .p.priv_class = &class, diff --git a/libavcodec/libopenjpegdec.c b/libavcodec/libopenjpegdec.c deleted file mode 100644 index 206db07ec70..00000000000 --- a/libavcodec/libopenjpegdec.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * JPEG 2000 decoding support via OpenJPEG - * Copyright (c) 2009 Jaikrishnan Menon - * - * 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 - * JPEG 2000 decoder using libopenjpeg - */ - -#include "libavutil/common.h" -#include "libavutil/imgutils.h" -#include "libavutil/intreadwrite.h" -#include "libavutil/opt.h" -#include "libavutil/pixfmt.h" - -#include "avcodec.h" -#include "codec_internal.h" -#include "decode.h" -#include "thread.h" - -#include - -#define JP2_SIG_TYPE 0x6A502020 -#define JP2_SIG_VALUE 0x0D0A870A - -// pix_fmts with lower bpp have to be listed before -// similar pix_fmts with higher bpp. -#define RGB_PIXEL_FORMATS AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, \ - AV_PIX_FMT_RGB48, AV_PIX_FMT_RGBA64 - -#define GRAY_PIXEL_FORMATS AV_PIX_FMT_GRAY8, AV_PIX_FMT_YA8, \ - AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, \ - AV_PIX_FMT_GRAY16, AV_PIX_FMT_YA16 - -#define YUV_PIXEL_FORMATS AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUVA420P, \ - AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUVA422P, \ - AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVA444P, \ - AV_PIX_FMT_YUV420P9, AV_PIX_FMT_YUV422P9, AV_PIX_FMT_YUV444P9, \ - AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, \ - AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, \ - AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, \ - AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV444P12, \ - AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14, \ - AV_PIX_FMT_YUV420P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV444P16, \ - AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16 - -#define XYZ_PIXEL_FORMATS AV_PIX_FMT_XYZ12 - -static const enum AVPixelFormat libopenjpeg_rgb_pix_fmts[] = { - RGB_PIXEL_FORMATS -}; -static const enum AVPixelFormat libopenjpeg_gray_pix_fmts[] = { - GRAY_PIXEL_FORMATS -}; -static const enum AVPixelFormat libopenjpeg_yuv_pix_fmts[] = { - YUV_PIXEL_FORMATS -}; -static const enum AVPixelFormat libopenjpeg_all_pix_fmts[] = { - RGB_PIXEL_FORMATS, GRAY_PIXEL_FORMATS, YUV_PIXEL_FORMATS, XYZ_PIXEL_FORMATS -}; - -typedef struct LibOpenJPEGContext { - AVClass *class; - opj_dparameters_t dec_params; - int lowqual; -} LibOpenJPEGContext; - -static void error_callback(const char *msg, void *data) -{ - av_log(data, AV_LOG_ERROR, "%s", msg); -} - -static void warning_callback(const char *msg, void *data) -{ - av_log(data, AV_LOG_WARNING, "%s", msg); -} - -static void info_callback(const char *msg, void *data) -{ - av_log(data, AV_LOG_DEBUG, "%s", msg); -} - -typedef struct BufferReader { - int pos; - int size; - const uint8_t *buffer; -} BufferReader; - -static OPJ_SIZE_T stream_read(void *out_buffer, OPJ_SIZE_T nb_bytes, void *user_data) -{ - BufferReader *reader = user_data; - int remaining; - - if (reader->pos == reader->size) { - return (OPJ_SIZE_T)-1; - } - remaining = reader->size - reader->pos; - if (nb_bytes > remaining) { - nb_bytes = remaining; - } - memcpy(out_buffer, reader->buffer + reader->pos, nb_bytes); - reader->pos += (int)nb_bytes; - return nb_bytes; -} - -static OPJ_OFF_T stream_skip(OPJ_OFF_T nb_bytes, void *user_data) -{ - BufferReader *reader = user_data; - if (nb_bytes < 0) { - if (reader->pos == 0) { - return (OPJ_SIZE_T)-1; - } - if (nb_bytes + reader->pos < 0) { - nb_bytes = -reader->pos; - } - } else { - int remaining; - - if (reader->pos == reader->size) { - return (OPJ_SIZE_T)-1; - } - remaining = reader->size - reader->pos; - if (nb_bytes > remaining) { - nb_bytes = remaining; - } - } - reader->pos += (int)nb_bytes; - return nb_bytes; -} - -static OPJ_BOOL stream_seek(OPJ_OFF_T nb_bytes, void *user_data) -{ - BufferReader *reader = user_data; - if (nb_bytes < 0 || nb_bytes > reader->size) { - return OPJ_FALSE; - } - reader->pos = (int)nb_bytes; - return OPJ_TRUE; -} - -static inline int libopenjpeg_matches_pix_fmt(const opj_image_t *image, enum AVPixelFormat pix_fmt) -{ - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); - int match = 1; - - if (desc->nb_components != image->numcomps) { - return 0; - } - - switch (desc->nb_components) { - case 4: - match = match && - desc->comp[3].depth >= image->comps[3].prec && - 1 == image->comps[3].dx && - 1 == image->comps[3].dy; - case 3: - match = match && - desc->comp[2].depth >= image->comps[2].prec && - 1 << desc->log2_chroma_w == image->comps[2].dx && - 1 << desc->log2_chroma_h == image->comps[2].dy; - case 2: - match = match && - desc->comp[1].depth >= image->comps[1].prec && - 1 << desc->log2_chroma_w == image->comps[1].dx && - 1 << desc->log2_chroma_h == image->comps[1].dy; - case 1: - match = match && - desc->comp[0].depth >= image->comps[0].prec && - 1 == image->comps[0].dx && - 1 == image->comps[0].dy; - default: - break; - } - - return match; -} - -static inline enum AVPixelFormat libopenjpeg_guess_pix_fmt(const opj_image_t *image) { - int index; - const enum AVPixelFormat *possible_fmts = NULL; - int possible_fmts_nb = 0; - - switch (image->color_space) { - case OPJ_CLRSPC_SRGB: - possible_fmts = libopenjpeg_rgb_pix_fmts; - possible_fmts_nb = FF_ARRAY_ELEMS(libopenjpeg_rgb_pix_fmts); - break; - case OPJ_CLRSPC_GRAY: - possible_fmts = libopenjpeg_gray_pix_fmts; - possible_fmts_nb = FF_ARRAY_ELEMS(libopenjpeg_gray_pix_fmts); - break; - case OPJ_CLRSPC_SYCC: - possible_fmts = libopenjpeg_yuv_pix_fmts; - possible_fmts_nb = FF_ARRAY_ELEMS(libopenjpeg_yuv_pix_fmts); - break; - default: - possible_fmts = libopenjpeg_all_pix_fmts; - possible_fmts_nb = FF_ARRAY_ELEMS(libopenjpeg_all_pix_fmts); - break; - } - - for (index = 0; index < possible_fmts_nb; ++index) - if (libopenjpeg_matches_pix_fmt(image, possible_fmts[index])) { - return possible_fmts[index]; - } - - return AV_PIX_FMT_NONE; -} - -static inline int libopenjpeg_ispacked(enum AVPixelFormat pix_fmt) -{ - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); - int i, component_plane; - - if (pix_fmt == AV_PIX_FMT_GRAY16) - return 0; - - component_plane = desc->comp[0].plane; - for (i = 1; i < desc->nb_components; i++) - if (component_plane != desc->comp[i].plane) - return 0; - return 1; -} - -static inline void libopenjpeg_copy_to_packed8(AVFrame *picture, opj_image_t *image) { - uint8_t *img_ptr; - int index, x, y, c; - for (y = 0; y < picture->height; y++) { - index = y * picture->width; - img_ptr = picture->data[0] + y * picture->linesize[0]; - for (x = 0; x < picture->width; x++, index++) - for (c = 0; c < image->numcomps; c++) - *img_ptr++ = 0x80 * image->comps[c].sgnd + image->comps[c].data[index]; - } -} - -static inline void libopenjpeg_copy_to_packed16(AVFrame *picture, opj_image_t *image) { - uint16_t *img_ptr; - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(picture->format); - int index, x, y, c; - int adjust[4]; - for (x = 0; x < image->numcomps; x++) - adjust[x] = FFMAX(FFMIN(desc->comp[x].depth - image->comps[x].prec, 8), 0) + desc->comp[x].shift; - - for (y = 0; y < picture->height; y++) { - index = y * picture->width; - img_ptr = (uint16_t *) (picture->data[0] + y * picture->linesize[0]); - for (x = 0; x < picture->width; x++, index++) - for (c = 0; c < image->numcomps; c++) - *img_ptr++ = (1 << image->comps[c].prec - 1) * image->comps[c].sgnd + - (unsigned)image->comps[c].data[index] << adjust[c]; - } -} - -static inline void libopenjpeg_copyto8(AVFrame *picture, opj_image_t *image) { - int *comp_data; - uint8_t *img_ptr; - int index, x, y; - - for (index = 0; index < image->numcomps; index++) { - comp_data = image->comps[index].data; - for (y = 0; y < image->comps[index].h; y++) { - img_ptr = picture->data[index] + y * picture->linesize[index]; - for (x = 0; x < image->comps[index].w; x++) { - *img_ptr = 0x80 * image->comps[index].sgnd + *comp_data; - img_ptr++; - comp_data++; - } - } - } -} - -static inline void libopenjpeg_copyto16(AVFrame *picture, opj_image_t *image) { - int *comp_data; - uint16_t *img_ptr; - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(picture->format); - int index, x, y; - int adjust[4]; - for (x = 0; x < image->numcomps; x++) - adjust[x] = FFMAX(FFMIN(desc->comp[x].depth - image->comps[x].prec, 8), 0) + desc->comp[x].shift; - - for (index = 0; index < image->numcomps; index++) { - comp_data = image->comps[index].data; - for (y = 0; y < image->comps[index].h; y++) { - img_ptr = (uint16_t *)(picture->data[index] + y * picture->linesize[index]); - for (x = 0; x < image->comps[index].w; x++) { - *img_ptr = (1 << image->comps[index].prec - 1) * image->comps[index].sgnd + - (unsigned)*comp_data << adjust[index]; - img_ptr++; - comp_data++; - } - } - } -} - -static av_cold int libopenjpeg_decode_init(AVCodecContext *avctx) -{ - LibOpenJPEGContext *ctx = avctx->priv_data; - - opj_set_default_decoder_parameters(&ctx->dec_params); - return 0; -} - -static int libopenjpeg_decode_frame(AVCodecContext *avctx, AVFrame *picture, - int *got_frame, AVPacket *avpkt) -{ - const uint8_t *buf = avpkt->data; - int buf_size = avpkt->size; - LibOpenJPEGContext *ctx = avctx->priv_data; - const AVPixFmtDescriptor *desc; - int width, height, ret; - int pixel_size = 0; - int ispacked = 0; - int i; - opj_image_t *image = NULL; - BufferReader reader = {0, avpkt->size, avpkt->data}; - opj_codec_t *dec = NULL; - opj_stream_t *stream = NULL; - - *got_frame = 0; - - // Check if input is a raw jpeg2k codestream or in jp2 wrapping - if ((AV_RB32(buf) == 12) && - (AV_RB32(buf + 4) == JP2_SIG_TYPE) && - (AV_RB32(buf + 8) == JP2_SIG_VALUE)) { - dec = opj_create_decompress(OPJ_CODEC_JP2); - } else { - /* If the AVPacket contains a jp2c box, then skip to - * the starting byte of the codestream. */ - if (AV_RB32(buf + 4) == AV_RB32("jp2c")) - buf += 8; - dec = opj_create_decompress(OPJ_CODEC_J2K); - } - - if (!dec) { - av_log(avctx, AV_LOG_ERROR, "Error initializing decoder.\n"); - ret = AVERROR_EXTERNAL; - goto done; - } - - if (!opj_set_error_handler(dec, error_callback, avctx) || - !opj_set_warning_handler(dec, warning_callback, avctx) || - !opj_set_info_handler(dec, info_callback, avctx)) { - av_log(avctx, AV_LOG_ERROR, "Error setting decoder handlers.\n"); - ret = AVERROR_EXTERNAL; - goto done; - } - - ctx->dec_params.cp_layer = ctx->lowqual; - ctx->dec_params.cp_reduce = avctx->lowres; - - // Tie decoder with decoding parameters - opj_setup_decoder(dec, &ctx->dec_params); - - stream = opj_stream_default_create(OPJ_STREAM_READ); - - if (!stream) { - av_log(avctx, AV_LOG_ERROR, - "Codestream could not be opened for reading.\n"); - ret = AVERROR_EXTERNAL; - goto done; - } - - opj_stream_set_read_function(stream, stream_read); - opj_stream_set_skip_function(stream, stream_skip); - opj_stream_set_seek_function(stream, stream_seek); - opj_stream_set_user_data(stream, &reader, NULL); - opj_stream_set_user_data_length(stream, avpkt->size); - // Decode the header only. - ret = !opj_read_header(stream, dec, &image); - - if (ret) { - av_log(avctx, AV_LOG_ERROR, "Error decoding codestream header.\n"); - ret = AVERROR_EXTERNAL; - goto done; - } - - width = image->x1 - image->x0; - height = image->y1 - image->y0; - - ret = ff_set_dimensions(avctx, width, height); - if (ret < 0) - goto done; - - if (avctx->pix_fmt != AV_PIX_FMT_NONE) - if (!libopenjpeg_matches_pix_fmt(image, avctx->pix_fmt)) - avctx->pix_fmt = AV_PIX_FMT_NONE; - - if (avctx->pix_fmt == AV_PIX_FMT_NONE) - avctx->pix_fmt = libopenjpeg_guess_pix_fmt(image); - - if (avctx->pix_fmt == AV_PIX_FMT_NONE) { - av_log(avctx, AV_LOG_ERROR, "Unable to determine pixel format.\n"); - ret = AVERROR_UNKNOWN; - goto done; - } - for (i = 0; i < image->numcomps; i++) - if (image->comps[i].prec > avctx->bits_per_raw_sample) - avctx->bits_per_raw_sample = image->comps[i].prec; - - if ((ret = ff_thread_get_buffer(avctx, picture, 0)) < 0) - goto done; - - ret = !opj_decode(dec, stream, image); - - if (ret) { - av_log(avctx, AV_LOG_ERROR, "Error decoding codestream.\n"); - ret = AVERROR_EXTERNAL; - goto done; - } - - for (i = 0; i < image->numcomps; i++) { - if (!image->comps[i].data) { - av_log(avctx, AV_LOG_ERROR, - "Image component %d contains no data.\n", i); - ret = AVERROR_INVALIDDATA; - goto done; - } - } - - desc = av_pix_fmt_desc_get(avctx->pix_fmt); - pixel_size = desc->comp[0].step; - ispacked = libopenjpeg_ispacked(avctx->pix_fmt); - - switch (pixel_size) { - case 1: - if (ispacked) { - libopenjpeg_copy_to_packed8(picture, image); - } else { - libopenjpeg_copyto8(picture, image); - } - break; - case 2: - if (ispacked) { - libopenjpeg_copy_to_packed8(picture, image); - } else { - libopenjpeg_copyto16(picture, image); - } - break; - case 3: - case 4: - if (ispacked) { - libopenjpeg_copy_to_packed8(picture, image); - } - break; - case 6: - case 8: - if (ispacked) { - libopenjpeg_copy_to_packed16(picture, image); - } - break; - default: - avpriv_report_missing_feature(avctx, "Pixel size %d", pixel_size); - ret = AVERROR_PATCHWELCOME; - goto done; - } - - *got_frame = 1; - picture->pict_type = AV_PICTURE_TYPE_I; - picture->key_frame = 1; - ret = buf_size; - -done: - opj_image_destroy(image); - opj_stream_destroy(stream); - opj_destroy_codec(dec); - return ret; -} - -#define OFFSET(x) offsetof(LibOpenJPEGContext, x) -#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM - -static const AVOption options[] = { - { "lowqual", "Limit the number of layers used for decoding", - OFFSET(lowqual), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VD }, - { NULL }, -}; - -static const AVClass openjpeg_class = { - .class_name = "libopenjpeg", - .item_name = av_default_item_name, - .option = options, - .version = LIBAVUTIL_VERSION_INT, -}; - -const FFCodec ff_libopenjpeg_decoder = { - .p.name = "libopenjpeg", - CODEC_LONG_NAME("OpenJPEG JPEG 2000"), - .p.type = AVMEDIA_TYPE_VIDEO, - .p.id = AV_CODEC_ID_JPEG2000, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, - .p.max_lowres = 31, - .p.priv_class = &openjpeg_class, - .p.wrapper_name = "libopenjpeg", - .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, - .priv_data_size = sizeof(LibOpenJPEGContext), - .init = libopenjpeg_decode_init, - FF_CODEC_DECODE_CB(libopenjpeg_decode_frame), -}; diff --git a/libavcodec/libopenjpegenc.c b/libavcodec/libopenjpegenc.c index 009c7a43774..6a4364b1fee 100644 --- a/libavcodec/libopenjpegenc.c +++ b/libavcodec/libopenjpegenc.c @@ -722,24 +722,24 @@ static int libopenjpeg_encode_frame(AVCodecContext *avctx, AVPacket *pkt, #define OFFSET(x) offsetof(LibOpenJPEGContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "format", "Codec Format", OFFSET(format), AV_OPT_TYPE_INT, { .i64 = OPJ_CODEC_JP2 }, OPJ_CODEC_J2K, OPJ_CODEC_JP2, VE, "format" }, - { "j2k", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CODEC_J2K }, 0, 0, VE, "format" }, - { "jp2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CODEC_JP2 }, 0, 0, VE, "format" }, - { "profile", NULL, OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = OPJ_STD_RSIZ }, OPJ_STD_RSIZ, OPJ_CINEMA4K, VE, "profile" }, - { "jpeg2000", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_STD_RSIZ }, 0, 0, VE, "profile" }, - { "cinema2k", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CINEMA2K }, 0, 0, VE, "profile" }, - { "cinema4k", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CINEMA4K }, 0, 0, VE, "profile" }, - { "cinema_mode", "Digital Cinema", OFFSET(cinema_mode), AV_OPT_TYPE_INT, { .i64 = OPJ_OFF }, OPJ_OFF, OPJ_CINEMA4K_24, VE, "cinema_mode" }, - { "off", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_OFF }, 0, 0, VE, "cinema_mode" }, - { "2k_24", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CINEMA2K_24 }, 0, 0, VE, "cinema_mode" }, - { "2k_48", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CINEMA2K_48 }, 0, 0, VE, "cinema_mode" }, - { "4k_24", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CINEMA4K_24 }, 0, 0, VE, "cinema_mode" }, - { "prog_order", "Progression Order", OFFSET(prog_order), AV_OPT_TYPE_INT, { .i64 = OPJ_LRCP }, OPJ_LRCP, OPJ_CPRL, VE, "prog_order" }, - { "lrcp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_LRCP }, 0, 0, VE, "prog_order" }, - { "rlcp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_RLCP }, 0, 0, VE, "prog_order" }, - { "rpcl", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_RPCL }, 0, 0, VE, "prog_order" }, - { "pcrl", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_PCRL }, 0, 0, VE, "prog_order" }, - { "cprl", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CPRL }, 0, 0, VE, "prog_order" }, + { "format", "Codec Format", OFFSET(format), AV_OPT_TYPE_INT, { .i64 = OPJ_CODEC_JP2 }, OPJ_CODEC_J2K, OPJ_CODEC_JP2, VE, .unit = "format" }, + { "j2k", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CODEC_J2K }, 0, 0, VE, .unit = "format" }, + { "jp2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CODEC_JP2 }, 0, 0, VE, .unit = "format" }, + { "profile", NULL, OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = OPJ_STD_RSIZ }, OPJ_STD_RSIZ, OPJ_CINEMA4K, VE, .unit = "profile" }, + { "jpeg2000", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_STD_RSIZ }, 0, 0, VE, .unit = "profile" }, + { "cinema2k", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CINEMA2K }, 0, 0, VE, .unit = "profile" }, + { "cinema4k", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CINEMA4K }, 0, 0, VE, .unit = "profile" }, + { "cinema_mode", "Digital Cinema", OFFSET(cinema_mode), AV_OPT_TYPE_INT, { .i64 = OPJ_OFF }, OPJ_OFF, OPJ_CINEMA4K_24, VE, .unit = "cinema_mode" }, + { "off", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_OFF }, 0, 0, VE, .unit = "cinema_mode" }, + { "2k_24", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CINEMA2K_24 }, 0, 0, VE, .unit = "cinema_mode" }, + { "2k_48", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CINEMA2K_48 }, 0, 0, VE, .unit = "cinema_mode" }, + { "4k_24", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CINEMA4K_24 }, 0, 0, VE, .unit = "cinema_mode" }, + { "prog_order", "Progression Order", OFFSET(prog_order), AV_OPT_TYPE_INT, { .i64 = OPJ_LRCP }, OPJ_LRCP, OPJ_CPRL, VE, .unit = "prog_order" }, + { "lrcp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_LRCP }, 0, 0, VE, .unit = "prog_order" }, + { "rlcp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_RLCP }, 0, 0, VE, .unit = "prog_order" }, + { "rpcl", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_RPCL }, 0, 0, VE, .unit = "prog_order" }, + { "pcrl", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_PCRL }, 0, 0, VE, .unit = "prog_order" }, + { "cprl", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = OPJ_CPRL }, 0, 0, VE, .unit = "prog_order" }, { "numresolution", NULL, OFFSET(numresolution), AV_OPT_TYPE_INT, { .i64 = 6 }, 0, 33, VE }, { "irreversible", NULL, OFFSET(irreversible), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, { "disto_alloc", NULL, OFFSET(disto_alloc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, VE }, diff --git a/libavcodec/libopusenc.c b/libavcodec/libopusenc.c index 75bc491c9ef..af25f27f74d 100644 --- a/libavcodec/libopusenc.c +++ b/libavcodec/libopusenc.c @@ -512,18 +512,14 @@ static int libopus_encode(AVCodecContext *avctx, AVPacket *avpkt, discard_padding = opus->opts.packet_size - avpkt->duration; // Check if subtraction resulted in an overflow - if ((discard_padding < opus->opts.packet_size) != (avpkt->duration > 0)) { - av_packet_unref(avpkt); + if ((discard_padding < opus->opts.packet_size) != (avpkt->duration > 0)) return AVERROR(EINVAL); - } if (discard_padding > 0) { uint8_t* side_data = av_packet_new_side_data(avpkt, AV_PKT_DATA_SKIP_SAMPLES, 10); - if(!side_data) { - av_packet_unref(avpkt); + if (!side_data) return AVERROR(ENOMEM); - } AV_WL32(side_data + 4, discard_padding); } @@ -548,18 +544,18 @@ static av_cold int libopus_encode_close(AVCodecContext *avctx) #define OFFSET(x) offsetof(LibopusEncContext, opts.x) #define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption libopus_options[] = { - { "application", "Intended application type", OFFSET(application), AV_OPT_TYPE_INT, { .i64 = OPUS_APPLICATION_AUDIO }, OPUS_APPLICATION_VOIP, OPUS_APPLICATION_RESTRICTED_LOWDELAY, FLAGS, "application" }, - { "voip", "Favor improved speech intelligibility", 0, AV_OPT_TYPE_CONST, { .i64 = OPUS_APPLICATION_VOIP }, 0, 0, FLAGS, "application" }, - { "audio", "Favor faithfulness to the input", 0, AV_OPT_TYPE_CONST, { .i64 = OPUS_APPLICATION_AUDIO }, 0, 0, FLAGS, "application" }, - { "lowdelay", "Restrict to only the lowest delay modes", 0, AV_OPT_TYPE_CONST, { .i64 = OPUS_APPLICATION_RESTRICTED_LOWDELAY }, 0, 0, FLAGS, "application" }, + { "application", "Intended application type", OFFSET(application), AV_OPT_TYPE_INT, { .i64 = OPUS_APPLICATION_AUDIO }, OPUS_APPLICATION_VOIP, OPUS_APPLICATION_RESTRICTED_LOWDELAY, FLAGS, .unit = "application" }, + { "voip", "Favor improved speech intelligibility", 0, AV_OPT_TYPE_CONST, { .i64 = OPUS_APPLICATION_VOIP }, 0, 0, FLAGS, .unit = "application" }, + { "audio", "Favor faithfulness to the input", 0, AV_OPT_TYPE_CONST, { .i64 = OPUS_APPLICATION_AUDIO }, 0, 0, FLAGS, .unit = "application" }, + { "lowdelay", "Restrict to only the lowest delay modes, disable voice-optimized modes", 0, AV_OPT_TYPE_CONST, { .i64 = OPUS_APPLICATION_RESTRICTED_LOWDELAY }, 0, 0, FLAGS, .unit = "application" }, { "frame_duration", "Duration of a frame in milliseconds", OFFSET(frame_duration), AV_OPT_TYPE_FLOAT, { .dbl = 20.0 }, 2.5, 120.0, FLAGS }, { "packet_loss", "Expected packet loss percentage", OFFSET(packet_loss), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, FLAGS }, { "fec", "Enable inband FEC. Expected packet loss must be non-zero", OFFSET(fec), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, - { "vbr", "Variable bit rate mode", OFFSET(vbr), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 2, FLAGS, "vbr" }, - { "off", "Use constant bit rate", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "vbr" }, - { "on", "Use variable bit rate", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "vbr" }, - { "constrained", "Use constrained VBR", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, "vbr" }, - { "mapping_family", "Channel Mapping Family", OFFSET(mapping_family), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 255, FLAGS, "mapping_family" }, + { "vbr", "Variable bit rate mode", OFFSET(vbr), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 2, FLAGS, .unit = "vbr" }, + { "off", "Use constant bit rate", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, .unit = "vbr" }, + { "on", "Use variable bit rate", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, .unit = "vbr" }, + { "constrained", "Use constrained VBR", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, .unit = "vbr" }, + { "mapping_family", "Channel Mapping Family", OFFSET(mapping_family), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 255, FLAGS, .unit = "mapping_family" }, #ifdef OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST { "apply_phase_inv", "Apply intensity stereo phase inversion", OFFSET(apply_phase_inv), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS }, #endif diff --git a/libavcodec/librav1e.c b/libavcodec/librav1e.c index 08affabe3e0..2a6d8bfbed0 100644 --- a/libavcodec/librav1e.c +++ b/libavcodec/librav1e.c @@ -57,9 +57,6 @@ typedef struct librav1eContext { typedef struct FrameData { int64_t pts; int64_t duration; -#if FF_API_REORDERED_OPAQUE - int64_t reordered_opaque; -#endif void *frame_opaque; AVBufferRef *frame_opaque_ref; @@ -219,10 +216,15 @@ static av_cold int librav1e_encode_init(AVCodecContext *avctx) avctx->framerate.den, avctx->framerate.num }); } else { +FF_DISABLE_DEPRECATION_WARNINGS rav1e_config_set_time_base(cfg, (RaRational) { - avctx->time_base.num * avctx->ticks_per_frame, - avctx->time_base.den + avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + , avctx->time_base.den }); +FF_ENABLE_DEPRECATION_WARNINGS } if ((avctx->flags & AV_CODEC_FLAG_PASS1 || avctx->flags & AV_CODEC_FLAG_PASS2) && !avctx->bit_rate) { @@ -467,20 +469,11 @@ static int librav1e_receive_packet(AVCodecContext *avctx, AVPacket *pkt) } fd->pts = frame->pts; fd->duration = frame->duration; -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - fd->reordered_opaque = frame->reordered_opaque; -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { fd->frame_opaque = frame->opaque; - ret = av_buffer_replace(&fd->frame_opaque_ref, frame->opaque_ref); - if (ret < 0) { - frame_data_free(fd); - av_frame_unref(frame); - return ret; - } + fd->frame_opaque_ref = frame->opaque_ref; + frame->opaque_ref = NULL; } rframe = rav1e_frame_new(ctx->ctx); @@ -578,11 +571,6 @@ FF_ENABLE_DEPRECATION_WARNINGS fd = rpkt->opaque; pkt->pts = pkt->dts = fd->pts; pkt->duration = fd->duration; -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - avctx->reordered_opaque = fd->reordered_opaque; -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { pkt->opaque = fd->frame_opaque; diff --git a/libavcodec/librsvgdec.c b/libavcodec/librsvgdec.c index 9c8aa2dedc8..c328fbc7742 100644 --- a/libavcodec/librsvgdec.c +++ b/libavcodec/librsvgdec.c @@ -38,48 +38,75 @@ static int librsvg_decode_frame(AVCodecContext *avctx, AVFrame *frame, { int ret; LibRSVGContext *s = avctx->priv_data; - - RsvgHandle *handle; - RsvgDimensionData unscaled_dimensions, dimensions; - cairo_surface_t *image; + RsvgHandle *handle = NULL; + RsvgDimensionData dimensions; +#if LIBRSVG_MAJOR_VERSION > 2 || LIBRSVG_MAJOR_VERSION == 2 && LIBRSVG_MINOR_VERSION >= 52 + RsvgRectangle viewport = { 0 }; +#else + RsvgDimensionData unscaled_dimensions; +#endif + cairo_surface_t *image = NULL; cairo_t *crender = NULL; GError *error = NULL; + gboolean gret; *got_frame = 0; handle = rsvg_handle_new_from_data(pkt->data, pkt->size, &error); if (error) { - av_log(avctx, AV_LOG_ERROR, "Error parsing svg!\n"); - g_error_free(error); - return AVERROR_INVALIDDATA; + av_log(avctx, AV_LOG_ERROR, "Error parsing svg: %s\n", error->message); + ret = AVERROR_INVALIDDATA; + goto end; } +#if LIBRSVG_MAJOR_VERSION > 2 || LIBRSVG_MAJOR_VERSION == 2 && LIBRSVG_MINOR_VERSION >= 52 + gret = rsvg_handle_get_intrinsic_size_in_pixels(handle, &viewport.width, &viewport.height); + if (!gret) { + viewport.width = s->width ? s->width : 100; + viewport.height = s->height ? s->height : 100; + } + dimensions.width = (int)viewport.width; + dimensions.height = (int)viewport.height; +#else rsvg_handle_get_dimensions(handle, &dimensions); rsvg_handle_get_dimensions(handle, &unscaled_dimensions); +#endif dimensions.width = s->width ? s->width : dimensions.width; dimensions.height = s->height ? s->height : dimensions.height; if (s->keep_ar && (s->width || s->height)) { +#if LIBRSVG_MAJOR_VERSION > 2 || LIBRSVG_MAJOR_VERSION == 2 && LIBRSVG_MINOR_VERSION >= 52 + double default_ar = viewport.width / viewport.height; +#else double default_ar = unscaled_dimensions.width/(double)unscaled_dimensions.height; +#endif if (!s->width) dimensions.width = lrintf(dimensions.height * default_ar); else dimensions.height = lrintf(dimensions.width / default_ar); } - if ((ret = ff_set_dimensions(avctx, dimensions.width, dimensions.height))) - return ret; + ret = ff_set_dimensions(avctx, dimensions.width, dimensions.height); + if (ret < 0) + goto end; + avctx->pix_fmt = AV_PIX_FMT_RGB32; + viewport.width = dimensions.width; + viewport.height = dimensions.height; + + ret = ff_get_buffer(avctx, frame, 0); + if (ret < 0) + goto end; - if ((ret = ff_get_buffer(avctx, frame, 0))) - return ret; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; image = cairo_image_surface_create_for_data(frame->data[0], CAIRO_FORMAT_ARGB32, frame->width, frame->height, frame->linesize[0]); - if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) - return AVERROR_INVALIDDATA; + if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) { + ret = AVERROR_EXTERNAL; + goto end; + } crender = cairo_create(image); @@ -88,18 +115,34 @@ static int librsvg_decode_frame(AVCodecContext *avctx, AVFrame *frame, cairo_paint(crender); cairo_restore(crender); +#if LIBRSVG_MAJOR_VERSION > 2 || LIBRSVG_MAJOR_VERSION == 2 && LIBRSVG_MINOR_VERSION >= 52 + gret = rsvg_handle_render_document(handle, crender, &viewport, &error); +#else cairo_scale(crender, dimensions.width / (double)unscaled_dimensions.width, dimensions.height / (double)unscaled_dimensions.height); + gret = rsvg_handle_render_cairo(handle, crender); +#endif - rsvg_handle_render_cairo(handle, crender); - - cairo_destroy(crender); - cairo_surface_destroy(image); - g_object_unref(handle); + if (!gret) { + av_log(avctx, AV_LOG_ERROR, "Error rendering svg: %s\n", error ? error->message : "unknown error"); + ret = AVERROR_EXTERNAL; + goto end; + } *got_frame = 1; + ret = 0; - return 0; +end: + if (error) + g_error_free(error); + if (handle) + g_object_unref(handle); + if (crender) + cairo_destroy(crender); + if (image) + cairo_surface_destroy(image); + + return ret; } #define OFFSET(x) offsetof(LibRSVGContext, x) diff --git a/libavcodec/libshine.c b/libavcodec/libshine.c index 2f6a9233e02..333d86f7742 100644 --- a/libavcodec/libshine.c +++ b/libavcodec/libshine.c @@ -140,7 +140,6 @@ const FFCodec ff_libshine_encoder = { .p.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_NONE }, .p.supported_samplerates = libshine_sample_rates, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, { 0 }, diff --git a/libavcodec/libspeexdec.c b/libavcodec/libspeexdec.c index 47fc5d6a4b2..84b308490a5 100644 --- a/libavcodec/libspeexdec.c +++ b/libavcodec/libspeexdec.c @@ -195,7 +195,11 @@ const FFCodec ff_libspeex_decoder = { CODEC_LONG_NAME("libspeex Speex"), .p.type = AVMEDIA_TYPE_AUDIO, .p.id = AV_CODEC_ID_SPEEX, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, .p.wrapper_name = "libspeex", .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, .priv_data_size = sizeof(LibSpeexContext), diff --git a/libavcodec/libspeexenc.c b/libavcodec/libspeexenc.c index 9fdb247863b..4c12cef2552 100644 --- a/libavcodec/libspeexenc.c +++ b/libavcodec/libspeexenc.c @@ -354,7 +354,6 @@ const FFCodec ff_libspeex_encoder = { .close = encode_close, .p.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, { 0 }, diff --git a/libavcodec/libsvtav1.c b/libavcodec/libsvtav1.c index 56e1e22b512..8fa42d590be 100644 --- a/libavcodec/libsvtav1.c +++ b/libavcodec/libsvtav1.c @@ -27,6 +27,8 @@ #include "libavutil/common.h" #include "libavutil/frame.h" #include "libavutil/imgutils.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/mastering_display_metadata.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/avassert.h" @@ -62,16 +64,6 @@ typedef struct SvtContext { // User options. AVDictionary *svtav1_opts; -#if FF_API_SVTAV1_OPTS - int hierarchical_level; - int la_depth; - int scd; - - int tier; - - int tile_columns; - int tile_rows; -#endif int enc_mode; int crf; int qp; @@ -146,6 +138,69 @@ static int alloc_buffer(EbSvtAv1EncConfiguration *config, SvtContext *svt_enc) } +static void handle_mdcv(struct EbSvtAv1MasteringDisplayInfo *dst, + const AVMasteringDisplayMetadata *mdcv) +{ + if (mdcv->has_primaries) { + const struct EbSvtAv1ChromaPoints *const points[] = { + &dst->r, + &dst->g, + &dst->b, + }; + + for (int i = 0; i < 3; i++) { + const struct EbSvtAv1ChromaPoints *dst = points[i]; + const AVRational *src = mdcv->display_primaries[i]; + + AV_WB16(&dst->x, + av_rescale_q(1, src[0], (AVRational){ 1, (1 << 16) })); + AV_WB16(&dst->y, + av_rescale_q(1, src[1], (AVRational){ 1, (1 << 16) })); + } + + AV_WB16(&dst->white_point.x, + av_rescale_q(1, mdcv->white_point[0], + (AVRational){ 1, (1 << 16) })); + AV_WB16(&dst->white_point.y, + av_rescale_q(1, mdcv->white_point[1], + (AVRational){ 1, (1 << 16) })); + } + + if (mdcv->has_luminance) { + AV_WB32(&dst->max_luma, + av_rescale_q(1, mdcv->max_luminance, + (AVRational){ 1, (1 << 8) })); + AV_WB32(&dst->min_luma, + av_rescale_q(1, mdcv->min_luminance, + (AVRational){ 1, (1 << 14) })); + } +} + +static void handle_side_data(AVCodecContext *avctx, + EbSvtAv1EncConfiguration *param) +{ + const AVFrameSideData *cll_sd = + av_frame_side_data_get(avctx->decoded_side_data, + avctx->nb_decoded_side_data, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + const AVFrameSideData *mdcv_sd = + av_frame_side_data_get(avctx->decoded_side_data, + avctx->nb_decoded_side_data, + AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + + if (cll_sd) { + const AVContentLightMetadata *cll = + (AVContentLightMetadata *)cll_sd->data; + + AV_WB16(¶m->content_light_level.max_cll, cll->MaxCLL); + AV_WB16(¶m->content_light_level.max_fall, cll->MaxFALL); + } + + if (mdcv_sd) { + handle_mdcv(¶m->mastering_display, + (AVMasteringDisplayMetadata *)mdcv_sd->data); + } +} + static int config_enc_params(EbSvtAv1EncConfiguration *param, AVCodecContext *avctx) { @@ -154,23 +209,7 @@ static int config_enc_params(EbSvtAv1EncConfiguration *param, AVDictionaryEntry *en = NULL; // Update param from options -#if FF_API_SVTAV1_OPTS - if (svt_enc->hierarchical_level >= 0) - param->hierarchical_levels = svt_enc->hierarchical_level; - if (svt_enc->tier >= 0) - param->tier = svt_enc->tier; - if (svt_enc->scd >= 0) - param->scene_change_detection = svt_enc->scd; - if (svt_enc->tile_columns >= 0) - param->tile_columns = svt_enc->tile_columns; - if (svt_enc->tile_rows >= 0) - param->tile_rows = svt_enc->tile_rows; - - if (svt_enc->la_depth >= 0) - param->look_ahead_distance = svt_enc->la_depth; -#endif - - if (svt_enc->enc_mode >= 0) + if (svt_enc->enc_mode >= -1) param->enc_mode = svt_enc->enc_mode; if (avctx->bit_rate) { @@ -184,8 +223,10 @@ static int config_enc_params(EbSvtAv1EncConfiguration *param, param->min_qp_allowed = avctx->qmin; } param->max_bit_rate = avctx->rc_max_rate; - if (avctx->bit_rate && avctx->rc_buffer_size) - param->maximum_buffer_size_ms = avctx->rc_buffer_size * 1000LL / avctx->bit_rate; + if ((avctx->bit_rate > 0 || avctx->rc_max_rate > 0) && avctx->rc_buffer_size) + param->maximum_buffer_size_ms = + avctx->rc_buffer_size * 1000LL / + FFMAX(avctx->bit_rate, avctx->rc_max_rate); if (svt_enc->crf > 0) { param->qp = svt_enc->crf; @@ -234,26 +275,52 @@ static int config_enc_params(EbSvtAv1EncConfiguration *param, } #endif - if (avctx->profile != FF_PROFILE_UNKNOWN) + if (avctx->profile != AV_PROFILE_UNKNOWN) param->profile = avctx->profile; - if (avctx->level != FF_LEVEL_UNKNOWN) + if (avctx->level != AV_LEVEL_UNKNOWN) param->level = avctx->level; - if (avctx->gop_size > 0) + // gop_size == 1 case is handled when encoding each frame by setting + // pic_type to EB_AV1_KEY_PICTURE. For gop_size > 1, set the + // intra_period_length. Even though setting intra_period_length to 0 should + // work in this case, it does not. + // See: https://gitlab.com/AOMediaCodec/SVT-AV1/-/issues/2076 + if (avctx->gop_size > 1) param->intra_period_length = avctx->gop_size - 1; +#if SVT_AV1_CHECK_VERSION(1, 1, 0) + // In order for SVT-AV1 to force keyframes by setting pic_type to + // EB_AV1_KEY_PICTURE on any frame, force_key_frames has to be set. Note + // that this does not force all frames to be keyframes (it only forces a + // keyframe with pic_type is set to EB_AV1_KEY_PICTURE). As of now, SVT-AV1 + // does not support arbitrary keyframe requests by setting pic_type to + // EB_AV1_KEY_PICTURE, so it is done only when gop_size == 1. + // FIXME: When SVT-AV1 supports arbitrary keyframe requests, this code needs + // to be updated to set force_key_frames accordingly. + if (avctx->gop_size == 1) + param->force_key_frames = 1; +#endif + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { param->frame_rate_numerator = avctx->framerate.num; param->frame_rate_denominator = avctx->framerate.den; } else { param->frame_rate_numerator = avctx->time_base.den; - param->frame_rate_denominator = avctx->time_base.num * avctx->ticks_per_frame; +FF_DISABLE_DEPRECATION_WARNINGS + param->frame_rate_denominator = avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } /* 2 = IDR, closed GOP, 1 = CRA, open GOP */ param->intra_refresh_type = avctx->flags & AV_CODEC_FLAG_CLOSED_GOP ? 2 : 1; + handle_side_data(avctx, param); + #if SVT_AV1_CHECK_VERSION(0, 9, 1) while ((en = av_dict_get(svt_enc->svtav1_opts, "", en, AV_DICT_IGNORE_SUFFIX))) { EbErrorType ret = svt_av1_enc_parse_parameter(param, en->key, en->value); @@ -291,21 +358,22 @@ static int config_enc_params(EbSvtAv1EncConfiguration *param, } if ((param->encoder_color_format == EB_YUV422 || param->encoder_bit_depth > 10) - && param->profile != FF_PROFILE_AV1_PROFESSIONAL ) { + && param->profile != AV_PROFILE_AV1_PROFESSIONAL ) { av_log(avctx, AV_LOG_WARNING, "Forcing Professional profile\n"); - param->profile = FF_PROFILE_AV1_PROFESSIONAL; - } else if (param->encoder_color_format == EB_YUV444 && param->profile != FF_PROFILE_AV1_HIGH) { + param->profile = AV_PROFILE_AV1_PROFESSIONAL; + } else if (param->encoder_color_format == EB_YUV444 && param->profile != AV_PROFILE_AV1_HIGH) { av_log(avctx, AV_LOG_WARNING, "Forcing High profile\n"); - param->profile = FF_PROFILE_AV1_HIGH; + param->profile = AV_PROFILE_AV1_HIGH; } avctx->bit_rate = param->rate_control_mode > 0 ? param->target_bit_rate : 0; avctx->rc_max_rate = param->max_bit_rate; - avctx->rc_buffer_size = param->maximum_buffer_size_ms * avctx->bit_rate / 1000LL; + avctx->rc_buffer_size = param->maximum_buffer_size_ms * + FFMAX(avctx->bit_rate, avctx->rc_max_rate) / 1000LL; if (avctx->bit_rate || avctx->rc_max_rate || avctx->rc_buffer_size) { - AVCPBProperties *cpb_props = ff_add_cpb_side_data(avctx); + AVCPBProperties *cpb_props = ff_encode_add_cpb_side_data(avctx); if (!cpb_props) return AVERROR(ENOMEM); @@ -453,6 +521,9 @@ static int eb_send_frame(AVCodecContext *avctx, const AVFrame *frame) break; } + if (avctx->gop_size == 1) + headerPtr->pic_type = EB_AV1_KEY_PICTURE; + svt_av1_enc_send_picture(svt_enc->svt_handle, headerPtr); return 0; @@ -509,6 +580,14 @@ static int eb_receive_packet(AVCodecContext *avctx, AVPacket *pkt) if (svt_ret == EB_NoErrorEmptyQueue) return AVERROR(EAGAIN); +#if SVT_AV1_CHECK_VERSION(2, 0, 0) + if (headerPtr->flags & EB_BUFFERFLAG_EOS) { + svt_enc->eos_flag = EOS_RECEIVED; + svt_av1_enc_release_out_buffer(&headerPtr); + return AVERROR_EOF; + } +#endif + ref = get_output_ref(avctx, svt_enc, headerPtr->n_filled_len); if (!ref) { av_log(avctx, AV_LOG_ERROR, "Failed to allocate output packet.\n"); @@ -543,8 +622,10 @@ static int eb_receive_packet(AVCodecContext *avctx, AVPacket *pkt) if (headerPtr->pic_type == EB_AV1_NON_REF_PICTURE) pkt->flags |= AV_PKT_FLAG_DISPOSABLE; +#if !(SVT_AV1_CHECK_VERSION(2, 0, 0)) if (headerPtr->flags & EB_BUFFERFLAG_EOS) svt_enc->eos_flag = EOS_RECEIVED; +#endif ff_side_data_set_encoder_stats(pkt, headerPtr->qp * FF_QP2LAMBDA, NULL, 0, pict_type); @@ -575,27 +656,13 @@ static av_cold int eb_enc_close(AVCodecContext *avctx) #define OFFSET(x) offsetof(SvtContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { -#if FF_API_SVTAV1_OPTS - { "hielevel", "Hierarchical prediction levels setting (Deprecated, use svtav1-params)", OFFSET(hierarchical_level), - AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 4, VE | AV_OPT_FLAG_DEPRECATED , "hielevel"}, - { "3level", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3 }, INT_MIN, INT_MAX, VE, "hielevel" }, - { "4level", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 4 }, INT_MIN, INT_MAX, VE, "hielevel" }, - - { "la_depth", "Look ahead distance [0, 120] (Deprecated, use svtav1-params)", OFFSET(la_depth), - AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 120, VE | AV_OPT_FLAG_DEPRECATED }, - - { "tier", "Set operating point tier (Deprecated, use svtav1-params)", OFFSET(tier), - AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE | AV_OPT_FLAG_DEPRECATED, "tier" }, - { "main", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, VE, "tier" }, - { "high", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, VE, "tier" }, -#endif { "preset", "Encoding preset", - OFFSET(enc_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, MAX_ENC_PRESET, VE }, + OFFSET(enc_mode), AV_OPT_TYPE_INT, { .i64 = -2 }, -2, MAX_ENC_PRESET, VE }, FF_AV1_PROFILE_OPTS #define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ - { .i64 = value }, 0, 0, VE, "avctx.level" + { .i64 = value }, 0, 0, VE, .unit = "avctx.level" { LEVEL("2.0", 20) }, { LEVEL("2.1", 21) }, { LEVEL("2.2", 22) }, @@ -626,14 +693,6 @@ static const AVOption options[] = { AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 63, VE }, { "qp", "Initial Quantizer level value", OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 63, VE }, -#if FF_API_SVTAV1_OPTS - { "sc_detection", "Scene change detection (Deprecated, use svtav1-params)", OFFSET(scd), - AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE | AV_OPT_FLAG_DEPRECATED }, - - { "tile_columns", "Log2 of number of tile columns to use (Deprecated, use svtav1-params)", OFFSET(tile_columns), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 4, VE | AV_OPT_FLAG_DEPRECATED }, - { "tile_rows", "Log2 of number of tile rows to use (Deprecated, use svtav1-params)", OFFSET(tile_rows), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 6, VE | AV_OPT_FLAG_DEPRECATED }, -#endif - { "svtav1-params", "Set the SVT-AV1 configuration using a :-separated list of key=value parameters", OFFSET(svtav1_opts), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE }, {NULL}, diff --git a/libavcodec/libtwolame.c b/libavcodec/libtwolame.c index 9c0156aa257..f3bd4771d71 100644 --- a/libavcodec/libtwolame.c +++ b/libavcodec/libtwolame.c @@ -177,12 +177,12 @@ static int twolame_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, #define OFFSET(x) offsetof(TWOLAMEContext, x) #define AE AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "mode", "Mpeg Mode", OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = TWOLAME_AUTO_MODE }, TWOLAME_AUTO_MODE, TWOLAME_MONO, AE, "mode"}, - { "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TWOLAME_AUTO_MODE }, 0, 0, AE, "mode" }, - { "stereo", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TWOLAME_STEREO }, 0, 0, AE, "mode" }, - { "joint_stereo", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TWOLAME_JOINT_STEREO }, 0, 0, AE, "mode" }, - { "dual_channel", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TWOLAME_DUAL_CHANNEL }, 0, 0, AE, "mode" }, - { "mono", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TWOLAME_MONO }, 0, 0, AE, "mode" }, + { "mode", "Mpeg Mode", OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = TWOLAME_AUTO_MODE }, TWOLAME_AUTO_MODE, TWOLAME_MONO, AE, .unit = "mode"}, + { "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TWOLAME_AUTO_MODE }, 0, 0, AE, .unit = "mode" }, + { "stereo", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TWOLAME_STEREO }, 0, 0, AE, .unit = "mode" }, + { "joint_stereo", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TWOLAME_JOINT_STEREO }, 0, 0, AE, .unit = "mode" }, + { "dual_channel", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TWOLAME_DUAL_CHANNEL }, 0, 0, AE, .unit = "mode" }, + { "mono", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TWOLAME_MONO }, 0, 0, AE, .unit = "mode" }, { "psymodel", "Psychoacoustic Model", OFFSET(psymodel), AV_OPT_TYPE_INT, { .i64 = 3 }, -1, 4, AE}, { "energy_levels","enable energy levels", OFFSET(energy), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AE}, { "error_protection","enable CRC error protection", OFFSET(error_protection), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, AE}, @@ -228,7 +228,6 @@ const FFCodec ff_libtwolame_encoder = { AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_NONE }, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, diff --git a/libavcodec/libuavs3d.c b/libavcodec/libuavs3d.c index b1ccb151e68..53b5faafc53 100644 --- a/libavcodec/libuavs3d.c +++ b/libavcodec/libuavs3d.c @@ -79,12 +79,10 @@ static void uavs3d_output_callback(uavs3d_io_frm_t *dec_frame) { frm->pts = dec_frame->pts; frm->pkt_dts = dec_frame->dts; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS frm->pkt_pos = dec_frame->pkt_pos; frm->pkt_size = dec_frame->pkt_size; -#if FF_API_FRAME_PICTURE_NUMBER -FF_DISABLE_DEPRECATION_WARNINGS - frm->coded_picture_number = dec_frame->dtr; - frm->display_picture_number = dec_frame->ptr; FF_ENABLE_DEPRECATION_WARNINGS #endif @@ -92,7 +90,10 @@ FF_ENABLE_DEPRECATION_WARNINGS av_log(NULL, AV_LOG_WARNING, "Error frame type in uavs3d: %d.\n", dec_frame->type); } else { frm->pict_type = ff_avs3_image_type[dec_frame->type]; - frm->key_frame = (frm->pict_type == AV_PICTURE_TYPE_I); + if (frm->pict_type == AV_PICTURE_TYPE_I) + frm->flags |= AV_FRAME_FLAG_KEY; + else + frm->flags &= ~AV_FRAME_FLAG_KEY; } for (i = 0; i < 3; i++) { @@ -175,8 +176,12 @@ static int libuavs3d_decode_frame(AVCodecContext *avctx, AVFrame *frm, uavs3d_io_frm_t *frm_dec = &h->dec_frame; buf_end = buf + buf_size; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS frm_dec->pkt_pos = avpkt->pos; frm_dec->pkt_size = avpkt->size; +FF_ENABLE_DEPRECATION_WARNINGS +#endif while (!finish) { int bs_len; @@ -263,8 +268,5 @@ const FFCodec ff_libuavs3d_decoder = { .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_AUTO_THREADS, .flush = libuavs3d_flush, - .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, - AV_PIX_FMT_YUV420P10LE, - AV_PIX_FMT_NONE }, .p.wrapper_name = "libuavs3d", }; diff --git a/libavcodec/libvpx.c b/libavcodec/libvpx.c deleted file mode 100644 index 8601f82bd24..00000000000 --- a/libavcodec/libvpx.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2013 Guillaume Martres - * - * 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 -#include "libvpx.h" -#include "config.h" -#include "config_components.h" - -#if CONFIG_LIBVPX_VP9_ENCODER -#include -#include -#endif - -static const enum AVPixelFormat vp9_pix_fmts_def[] = { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_YUVA420P, - AV_PIX_FMT_NONE -}; - -#if CONFIG_LIBVPX_VP9_ENCODER -static const enum AVPixelFormat vp9_pix_fmts_highcol[] = { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_YUVA420P, - AV_PIX_FMT_YUV422P, - AV_PIX_FMT_YUV440P, - AV_PIX_FMT_YUV444P, - AV_PIX_FMT_GBRP, - AV_PIX_FMT_NONE -}; - -static const enum AVPixelFormat vp9_pix_fmts_highbd[] = { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_YUVA420P, - AV_PIX_FMT_YUV422P, - AV_PIX_FMT_YUV440P, - AV_PIX_FMT_YUV444P, - AV_PIX_FMT_YUV420P10, - AV_PIX_FMT_YUV422P10, - AV_PIX_FMT_YUV440P10, - AV_PIX_FMT_YUV444P10, - AV_PIX_FMT_YUV420P12, - AV_PIX_FMT_YUV422P12, - AV_PIX_FMT_YUV440P12, - AV_PIX_FMT_YUV444P12, - AV_PIX_FMT_GBRP, - AV_PIX_FMT_GBRP10, - AV_PIX_FMT_GBRP12, - AV_PIX_FMT_NONE -}; -#endif - -av_cold void ff_vp9_init_static(FFCodec *codec) -{ - codec->p.pix_fmts = vp9_pix_fmts_def; -#if CONFIG_LIBVPX_VP9_ENCODER - { - vpx_codec_caps_t codec_caps = vpx_codec_get_caps(vpx_codec_vp9_cx()); - if (codec_caps & VPX_CODEC_CAP_HIGHBITDEPTH) - codec->p.pix_fmts = vp9_pix_fmts_highbd; - else - codec->p.pix_fmts = vp9_pix_fmts_highcol; - } -#endif -} diff --git a/libavcodec/libvpx.h b/libavcodec/libvpx.h index 331feb8745a..4671e0edef3 100644 --- a/libavcodec/libvpx.h +++ b/libavcodec/libvpx.h @@ -21,16 +21,6 @@ #ifndef AVCODEC_LIBVPX_H #define AVCODEC_LIBVPX_H -#include - -#include "codec_internal.h" - #define MAX_VPX_THREADS 64 -void ff_vp9_init_static(FFCodec *codec); -#if 0 -enum AVPixelFormat ff_vpx_imgfmt_to_pixfmt(vpx_img_fmt_t img); -vpx_img_fmt_t ff_vpx_pixfmt_to_imgfmt(enum AVPixelFormat pix); -#endif - #endif /* AVCODEC_LIBVPX_H */ diff --git a/libavcodec/libvpxdec.c b/libavcodec/libvpxdec.c index 0ae19c3f72a..c6187fd5a14 100644 --- a/libavcodec/libvpxdec.c +++ b/libavcodec/libvpxdec.c @@ -127,26 +127,26 @@ static int set_pix_fmt(AVCodecContext *avctx, struct vpx_image *img, switch (img->fmt) { case VPX_IMG_FMT_I420: if (avctx->codec_id == AV_CODEC_ID_VP9) - avctx->profile = FF_PROFILE_VP9_0; + avctx->profile = AV_PROFILE_VP9_0; avctx->pix_fmt = has_alpha_channel ? AV_PIX_FMT_YUVA420P : AV_PIX_FMT_YUV420P; return 0; #if CONFIG_LIBVPX_VP9_DECODER case VPX_IMG_FMT_I422: - avctx->profile = FF_PROFILE_VP9_1; + avctx->profile = AV_PROFILE_VP9_1; avctx->pix_fmt = AV_PIX_FMT_YUV422P; return 0; case VPX_IMG_FMT_I440: - avctx->profile = FF_PROFILE_VP9_1; + avctx->profile = AV_PROFILE_VP9_1; avctx->pix_fmt = AV_PIX_FMT_YUV440P; return 0; case VPX_IMG_FMT_I444: - avctx->profile = FF_PROFILE_VP9_1; + avctx->profile = AV_PROFILE_VP9_1; avctx->pix_fmt = avctx->colorspace == AVCOL_SPC_RGB ? AV_PIX_FMT_GBRP : AV_PIX_FMT_YUV444P; return 0; case VPX_IMG_FMT_I42016: - avctx->profile = FF_PROFILE_VP9_2; + avctx->profile = AV_PROFILE_VP9_2; if (img->bit_depth == 10) { avctx->pix_fmt = AV_PIX_FMT_YUV420P10; return 0; @@ -157,7 +157,7 @@ static int set_pix_fmt(AVCodecContext *avctx, struct vpx_image *img, return AVERROR_INVALIDDATA; } case VPX_IMG_FMT_I42216: - avctx->profile = FF_PROFILE_VP9_3; + avctx->profile = AV_PROFILE_VP9_3; if (img->bit_depth == 10) { avctx->pix_fmt = AV_PIX_FMT_YUV422P10; return 0; @@ -168,7 +168,7 @@ static int set_pix_fmt(AVCodecContext *avctx, struct vpx_image *img, return AVERROR_INVALIDDATA; } case VPX_IMG_FMT_I44016: - avctx->profile = FF_PROFILE_VP9_3; + avctx->profile = AV_PROFILE_VP9_3; if (img->bit_depth == 10) { avctx->pix_fmt = AV_PIX_FMT_YUV440P10; return 0; @@ -179,7 +179,7 @@ static int set_pix_fmt(AVCodecContext *avctx, struct vpx_image *img, return AVERROR_INVALIDDATA; } case VPX_IMG_FMT_I44416: - avctx->profile = FF_PROFILE_VP9_3; + avctx->profile = AV_PROFILE_VP9_3; if (img->bit_depth == 10) { avctx->pix_fmt = avctx->colorspace == AVCOL_SPC_RGB ? AV_PIX_FMT_GBRP10 : AV_PIX_FMT_YUV444P10; @@ -317,10 +317,8 @@ static int vpx_decode(AVCodecContext *avctx, AVFrame *picture, return AVERROR(ENOMEM); if (ctx->has_alpha_channel) { picture->buf[1] = av_buffer_ref(img_alpha->fb_priv); - if (!picture->buf[1]) { - av_frame_unref(picture); + if (!picture->buf[1]) return AVERROR(ENOMEM); - } } for (int i = 0; i < 4; i++) { picture->data[i] = planes[i]; @@ -329,8 +327,8 @@ static int vpx_decode(AVCodecContext *avctx, AVFrame *picture, } else { if ((ret = ff_get_buffer(avctx, picture, 0)) < 0) return ret; - av_image_copy(picture->data, picture->linesize, (const uint8_t**)planes, - linesizes, avctx->pix_fmt, img->d_w, img->d_h); + av_image_copy2(picture->data, picture->linesize, planes, + linesizes, avctx->pix_fmt, img->d_w, img->d_h); } *got_frame = 1; } @@ -377,7 +375,7 @@ static av_cold int vp9_init(AVCodecContext *avctx) return vpx_init(avctx, &ctx->decoder, vpx_codec_vp9_dx()); } -FFCodec ff_libvpx_vp9_decoder = { +const FFCodec ff_libvpx_vp9_decoder = { .p.name = "libvpx-vp9", CODEC_LONG_NAME("libvpx VP9"), .p.type = AVMEDIA_TYPE_VIDEO, @@ -391,6 +389,5 @@ FFCodec ff_libvpx_vp9_decoder = { FF_CODEC_DECODE_CB(vpx_decode), .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | FF_CODEC_CAP_AUTO_THREADS, - .init_static_data = ff_vp9_init_static, }; #endif /* CONFIG_LIBVPX_VP9_DECODER */ diff --git a/libavcodec/libvpxenc.c b/libavcodec/libvpxenc.c index 339d4d81469..b1aa1c85add 100644 --- a/libavcodec/libvpxenc.c +++ b/libavcodec/libvpxenc.c @@ -58,19 +58,21 @@ struct FrameListData { size_t sz; /**< length of compressed data */ int64_t pts; /**< time stamp to show frame (in timebase units) */ - unsigned long duration; /**< duration to show frame - (in timebase units) */ uint32_t flags; /**< flags for this frame */ uint64_t sse[4]; int have_sse; /**< true if we have pending sse[] */ - uint64_t frame_number; struct FrameListData *next; }; -typedef struct FrameHDR10Plus { +typedef struct FrameData { int64_t pts; + int64_t duration; + + void *frame_opaque; + AVBufferRef *frame_opaque_ref; + AVBufferRef *hdr10_plus; -} FrameHDR10Plus; +} FrameData; typedef struct VPxEncoderContext { AVClass *class; @@ -84,7 +86,6 @@ typedef struct VPxEncoderContext { int deadline; //i.e., RT/GOOD/BEST uint64_t sse[4]; int have_sse; /**< true if we have pending sse[] */ - uint64_t frame_number; struct FrameListData *coded_frame_list; struct FrameListData *alpha_coded_frame_list; @@ -117,6 +118,9 @@ typedef struct VPxEncoderContext { int *ts_layer_flags; int current_temporal_idx; + // VP8-only + int screen_content_mode; + // VP9-only int lossless; int tile_columns; @@ -132,7 +136,9 @@ typedef struct VPxEncoderContext { int corpus_complexity; int tpl_model; int min_gf_interval; - AVFifo *hdr10_plus_fifo; + + // This FIFO is used to propagate various properties from frames to packets. + AVFifo *fifo; /** * If the driver does not support ROI then warn the first time we * encounter a frame with ROI side data. @@ -158,6 +164,7 @@ static const char *const ctlidstr[] = { [VP8E_SET_MAX_INTRA_BITRATE_PCT] = "VP8E_SET_MAX_INTRA_BITRATE_PCT", [VP8E_SET_SHARPNESS] = "VP8E_SET_SHARPNESS", [VP8E_SET_TEMPORAL_LAYER_ID] = "VP8E_SET_TEMPORAL_LAYER_ID", + [VP8E_SET_SCREEN_CONTENT_MODE] = "VP8E_SET_SCREEN_CONTENT_MODE", #if CONFIG_LIBVPX_VP9_ENCODER [VP9E_SET_LOSSLESS] = "VP9E_SET_LOSSLESS", [VP9E_SET_TILE_COLUMNS] = "VP9E_SET_TILE_COLUMNS", @@ -329,33 +336,98 @@ static av_cold void free_frame_list(struct FrameListData *list) } } -static av_cold void free_hdr10_plus_fifo(AVFifo **fifo) +static void frame_data_uninit(FrameData *fd) { - FrameHDR10Plus frame_hdr10_plus; - while (av_fifo_read(*fifo, &frame_hdr10_plus, 1) >= 0) - av_buffer_unref(&frame_hdr10_plus.hdr10_plus); + av_buffer_unref(&fd->frame_opaque_ref); + av_buffer_unref(&fd->hdr10_plus); +} + +static av_cold void fifo_free(AVFifo **fifo) +{ + FrameData fd; + while (av_fifo_read(*fifo, &fd, 1) >= 0) + frame_data_uninit(&fd); av_fifo_freep2(fifo); } -static int copy_hdr10_plus_to_pkt(AVFifo *fifo, AVPacket *pkt) +static int frame_data_submit(AVCodecContext *avctx, AVFifo *fifo, + const AVFrame *frame) { - FrameHDR10Plus frame_hdr10_plus; + VPxContext *ctx = avctx->priv_data; + const struct vpx_codec_enc_cfg *enccfg = ctx->encoder.config.enc; + + FrameData fd = { .pts = frame->pts }; + + AVFrameSideData *av_uninit(sd); + int ret; + +#if CONFIG_LIBVPX_VP9_ENCODER + // Keep HDR10+ if it has bit depth higher than 8 and + // it has PQ trc (SMPTE2084). + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DYNAMIC_HDR_PLUS); + if (avctx->codec_id == AV_CODEC_ID_VP9 && sd && + enccfg->g_bit_depth > 8 && avctx->color_trc == AVCOL_TRC_SMPTE2084) { + fd.hdr10_plus = av_buffer_ref(sd->buf); + if (!fd.hdr10_plus) + return AVERROR(ENOMEM); + } +#endif + + fd.duration = frame->duration; + fd.frame_opaque = frame->opaque; + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE && frame->opaque_ref) { + ret = av_buffer_replace(&fd.frame_opaque_ref, frame->opaque_ref); + if (ret < 0) + goto fail; + } + + ret = av_fifo_write(fifo, &fd, 1); + if (ret < 0) + goto fail; + + return 0; +fail: + frame_data_uninit(&fd); + return ret; +} + +static int frame_data_apply(AVCodecContext *avctx, AVFifo *fifo, AVPacket *pkt) +{ + FrameData fd; uint8_t *data; - if (!pkt || av_fifo_peek(fifo, &frame_hdr10_plus, 1, 0) < 0) - return 0; - if (!frame_hdr10_plus.hdr10_plus || frame_hdr10_plus.pts != pkt->pts) + int ret = 0; + + if (av_fifo_peek(fifo, &fd, 1, 0) < 0) return 0; - av_fifo_drain2(fifo, 1); + if (fd.pts != pkt->pts) { + av_log(avctx, AV_LOG_WARNING, + "Mismatching timestamps: libvpx %"PRId64" queued %"PRId64"; " + "this is a bug, please report it\n", pkt->pts, fd.pts); + goto skip; + } - data = av_packet_new_side_data(pkt, AV_PKT_DATA_DYNAMIC_HDR10_PLUS, frame_hdr10_plus.hdr10_plus->size); - if (!data) { - av_buffer_unref(&frame_hdr10_plus.hdr10_plus); - return AVERROR(ENOMEM); + pkt->duration = fd.duration; + if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + pkt->opaque = fd.frame_opaque; + pkt->opaque_ref = fd.frame_opaque_ref; + fd.frame_opaque_ref = NULL; } - memcpy(data, frame_hdr10_plus.hdr10_plus->data, frame_hdr10_plus.hdr10_plus->size); - av_buffer_unref(&frame_hdr10_plus.hdr10_plus); - return 0; + if (fd.hdr10_plus) { + data = av_packet_new_side_data(pkt, AV_PKT_DATA_DYNAMIC_HDR10_PLUS, fd.hdr10_plus->size); + if (!data) { + ret = AVERROR(ENOMEM); + goto skip; + } + + memcpy(data, fd.hdr10_plus->data, fd.hdr10_plus->size); + } + +skip: + av_fifo_drain2(fifo, 1); + frame_data_uninit(&fd); + + return ret; } static av_cold int codecctl_int(AVCodecContext *avctx, @@ -449,8 +521,8 @@ static av_cold int vpx_free(AVCodecContext *avctx) av_freep(&avctx->stats_out); free_frame_list(ctx->coded_frame_list); free_frame_list(ctx->alpha_coded_frame_list); - if (ctx->hdr10_plus_fifo) - free_hdr10_plus_fifo(&ctx->hdr10_plus_fifo); + if (ctx->fifo) + fifo_free(&ctx->fifo); return 0; } @@ -914,18 +986,14 @@ static av_cold int vpx_init(AVCodecContext *avctx, return AVERROR(EINVAL); } + ctx->fifo = av_fifo_alloc2(1, sizeof(FrameData), AV_FIFO_FLAG_AUTO_GROW); + if (!ctx->fifo) + return AVERROR(ENOMEM); + #if CONFIG_LIBVPX_VP9_ENCODER if (avctx->codec_id == AV_CODEC_ID_VP9) { if (set_pix_fmt(avctx, codec_caps, &enccfg, &flags, &img_fmt)) return AVERROR(EINVAL); - // Keep HDR10+ if it has bit depth higher than 8 and - // it has PQ trc (SMPTE2084). - if (enccfg.g_bit_depth > 8 && avctx->color_trc == AVCOL_TRC_SMPTE2084) { - ctx->hdr10_plus_fifo = av_fifo_alloc2(1, sizeof(FrameHDR10Plus), - AV_FIFO_FLAG_AUTO_GROW); - if (!ctx->hdr10_plus_fifo) - return AVERROR(ENOMEM); - } } #endif @@ -1067,7 +1135,7 @@ static av_cold int vpx_init(AVCodecContext *avctx, /* 0-3: For non-zero values the encoder increasingly optimizes for reduced complexity playback on low powered devices at the expense of encode quality. */ - if (avctx->profile != FF_PROFILE_UNKNOWN) + if (avctx->profile != AV_PROFILE_UNKNOWN) enccfg.g_profile = avctx->profile; enccfg.g_error_resilient = ctx->error_resilient || ctx->flags & VP8F_ERROR_RESILIENT; @@ -1184,6 +1252,14 @@ static av_cold int vpx_init(AVCodecContext *avctx, #endif } #endif + if (avctx->codec_id == AV_CODEC_ID_VP8 && ctx->screen_content_mode >= 0) { + if (ctx->screen_content_mode == 2 && ctx->is_alpha) { + av_log(avctx, AV_LOG_ERROR, + "Transparency encoding with screen mode with aggressive rate control not supported\n"); + return AVERROR(EINVAL); + } + codecctl_int(avctx, VP8E_SET_SCREEN_CONTENT_MODE, ctx->screen_content_mode); + } av_log(avctx, AV_LOG_DEBUG, "Using deadline: %d\n", ctx->deadline); @@ -1195,7 +1271,7 @@ static av_cold int vpx_init(AVCodecContext *avctx, ctx->rawimg.bit_depth = enccfg.g_bit_depth; #endif - cpb_props = ff_add_cpb_side_data(avctx); + cpb_props = ff_encode_add_cpb_side_data(avctx); if (!cpb_props) return AVERROR(ENOMEM); @@ -1215,14 +1291,12 @@ static inline void cx_pktcpy(struct FrameListData *dst, VPxContext *ctx) { dst->pts = src->data.frame.pts; - dst->duration = src->data.frame.duration; dst->flags = src->data.frame.flags; dst->sz = src->data.frame.sz; dst->buf = src->data.frame.buf; dst->have_sse = 0; - /* For alt-ref frame, don't store PSNR or increment frame_number */ + /* For alt-ref frame, don't store PSNR */ if (!(dst->flags & VPX_FRAME_IS_INVISIBLE)) { - dst->frame_number = ++ctx->frame_number; dst->have_sse = ctx->have_sse; if (ctx->have_sse) { /* associate last-seen SSE to the frame. */ @@ -1232,8 +1306,6 @@ static inline void cx_pktcpy(struct FrameListData *dst, memcpy(dst->sse, ctx->sse, sizeof(dst->sse)); ctx->have_sse = 0; } - } else { - dst->frame_number = -1; /* sanity marker */ } } @@ -1289,13 +1361,9 @@ static int storeframe(AVCodecContext *avctx, struct FrameListData *cx_frame, AV_WB64(side_data, 1); memcpy(side_data + 8, alpha_cx_frame->buf, alpha_cx_frame->sz); } - if (cx_frame->frame_number != -1) { - if (ctx->hdr10_plus_fifo) { - int err = copy_hdr10_plus_to_pkt(ctx->hdr10_plus_fifo, pkt); - if (err < 0) - return err; - } - } + ret = frame_data_apply(avctx, ctx->fifo, pkt); + if (ret < 0) + return ret; return pkt->size; } @@ -1622,6 +1690,7 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, const struct vpx_codec_enc_cfg *enccfg = ctx->encoder.config.enc; vpx_svc_layer_id_t layer_id; int layer_id_valid = 0; + unsigned long duration = 0; if (avctx->qmax >= 0 && enccfg->rc_max_quantizer != avctx->qmax) { struct vpx_codec_enc_cfg cfg = *enccfg; @@ -1709,23 +1778,10 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, } } - if (ctx->hdr10_plus_fifo) { - AVFrameSideData *hdr10_plus_metadata; - // Add HDR10+ metadata to queue. - hdr10_plus_metadata = av_frame_get_side_data(frame, AV_FRAME_DATA_DYNAMIC_HDR_PLUS); - if (hdr10_plus_metadata) { - int err; - struct FrameHDR10Plus data; - data.pts = frame->pts; - data.hdr10_plus = av_buffer_ref(hdr10_plus_metadata->buf); - if (!data.hdr10_plus) - return AVERROR(ENOMEM); - err = av_fifo_write(ctx->hdr10_plus_fifo, &data, 1); - if (err < 0) { - av_buffer_unref(&data.hdr10_plus); - return err; - } - } + if (!(avctx->flags & AV_CODEC_FLAG_PASS1)) { + res = frame_data_submit(avctx, ctx->fifo, frame); + if (res < 0) + return res; } } @@ -1765,8 +1821,25 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, #endif } + if (frame && frame->duration > ULONG_MAX) { + av_log(avctx, AV_LOG_WARNING, + "Frame duration too large: %"PRId64"\n", frame->duration); + } else if (frame && frame->duration) + duration = frame->duration; + else if (avctx->framerate.num > 0 && avctx->framerate.den > 0) + duration = av_rescale_q(1, av_inv_q(avctx->framerate), avctx->time_base); + else { +FF_DISABLE_DEPRECATION_WARNINGS + duration = +#if FF_API_TICKS_PER_FRAME + avctx->ticks_per_frame ? avctx->ticks_per_frame : +#endif + 1; +FF_ENABLE_DEPRECATION_WARNINGS + } + res = vpx_codec_encode(&ctx->encoder, rawimg, timestamp, - avctx->ticks_per_frame, flags, ctx->deadline); + duration, flags, ctx->deadline); if (res != VPX_CODEC_OK) { log_encoder_error(avctx, "Error encoding frame"); return AVERROR_INVALIDDATA; @@ -1774,7 +1847,7 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, if (ctx->is_alpha) { res = vpx_codec_encode(&ctx->encoder_alpha, rawimg_alpha, timestamp, - avctx->ticks_per_frame, flags, ctx->deadline); + duration, flags, ctx->deadline); if (res != VPX_CODEC_OK) { log_encoder_error(avctx, "Error encoding alpha frame"); return AVERROR_INVALIDDATA; @@ -1827,24 +1900,24 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, "alternate reference frame selection", OFFSET(lag_in_frames), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE}, \ { "arnr-maxframes", "altref noise reduction max frame count", OFFSET(arnr_max_frames), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE}, \ { "arnr-strength", "altref noise reduction filter strength", OFFSET(arnr_strength), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE}, \ - { "arnr-type", "altref noise reduction filter type", OFFSET(arnr_type), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE, "arnr_type"}, \ - { "backward", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 0, VE, "arnr_type" }, \ - { "forward", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 0, VE, "arnr_type" }, \ - { "centered", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 3}, 0, 0, VE, "arnr_type" }, \ - { "tune", "Tune the encoding to a specific scenario", OFFSET(tune), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE, "tune"}, \ - { "psnr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VP8_TUNE_PSNR}, 0, 0, VE, "tune"}, \ - { "ssim", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VP8_TUNE_SSIM}, 0, 0, VE, "tune"}, \ - { "deadline", "Time to spend encoding, in microseconds.", OFFSET(deadline), AV_OPT_TYPE_INT, {.i64 = VPX_DL_GOOD_QUALITY}, INT_MIN, INT_MAX, VE, "quality"}, \ - { "best", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VPX_DL_BEST_QUALITY}, 0, 0, VE, "quality"}, \ - { "good", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VPX_DL_GOOD_QUALITY}, 0, 0, VE, "quality"}, \ - { "realtime", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VPX_DL_REALTIME}, 0, 0, VE, "quality"}, \ - { "error-resilient", "Error resilience configuration", OFFSET(error_resilient), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, VE, "er"}, \ + { "arnr-type", "altref noise reduction filter type", OFFSET(arnr_type), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE, .unit = "arnr_type"}, \ + { "backward", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 0, VE, .unit = "arnr_type" }, \ + { "forward", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 0, VE, .unit = "arnr_type" }, \ + { "centered", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 3}, 0, 0, VE, .unit = "arnr_type" }, \ + { "tune", "Tune the encoding to a specific scenario", OFFSET(tune), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE, .unit = "tune"}, \ + { "psnr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VP8_TUNE_PSNR}, 0, 0, VE, .unit = "tune"}, \ + { "ssim", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VP8_TUNE_SSIM}, 0, 0, VE, .unit = "tune"}, \ + { "deadline", "Time to spend encoding, in microseconds.", OFFSET(deadline), AV_OPT_TYPE_INT, {.i64 = VPX_DL_GOOD_QUALITY}, INT_MIN, INT_MAX, VE, .unit = "quality"}, \ + { "best", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VPX_DL_BEST_QUALITY}, 0, 0, VE, .unit = "quality"}, \ + { "good", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VPX_DL_GOOD_QUALITY}, 0, 0, VE, .unit = "quality"}, \ + { "realtime", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VPX_DL_REALTIME}, 0, 0, VE, .unit = "quality"}, \ + { "error-resilient", "Error resilience configuration", OFFSET(error_resilient), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, VE, .unit = "er"}, \ { "max-intra-rate", "Maximum I-frame bitrate (pct) 0=unlimited", OFFSET(max_intra_rate), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE}, \ - { "default", "Improve resiliency against losses of whole frames", 0, AV_OPT_TYPE_CONST, {.i64 = VPX_ERROR_RESILIENT_DEFAULT}, 0, 0, VE, "er"}, \ + { "default", "Improve resiliency against losses of whole frames", 0, AV_OPT_TYPE_CONST, {.i64 = VPX_ERROR_RESILIENT_DEFAULT}, 0, 0, VE, .unit = "er"}, \ { "partitions", "The frame partitions are independently decodable " \ "by the bool decoder, meaning that partitions can be decoded even " \ "though earlier partitions have been lost. Note that intra prediction" \ - " is still done over the partition boundary.", 0, AV_OPT_TYPE_CONST, {.i64 = VPX_ERROR_RESILIENT_PARTITIONS}, 0, 0, VE, "er"}, \ + " is still done over the partition boundary.", 0, AV_OPT_TYPE_CONST, {.i64 = VPX_ERROR_RESILIENT_PARTITIONS}, 0, 0, VE, .unit = "er"}, \ { "crf", "Select the quality for constant quality mode", offsetof(VPxContext, crf), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 63, VE }, \ { "static-thresh", "A change threshold on blocks below which they will be skipped by the encoder", OFFSET(static_thresh), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, \ { "drop-threshold", "Frame drop threshold", offsetof(VPxContext, drop_threshold), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, VE }, \ @@ -1855,10 +1928,10 @@ static int vpx_encode(AVCodecContext *avctx, AVPacket *pkt, #define LEGACY_OPTIONS \ {"speed", "", offsetof(VPxContext, cpu_used), AV_OPT_TYPE_INT, {.i64 = 1}, -16, 16, VE}, \ - {"quality", "", offsetof(VPxContext, deadline), AV_OPT_TYPE_INT, {.i64 = VPX_DL_GOOD_QUALITY}, INT_MIN, INT_MAX, VE, "quality"}, \ - {"vp8flags", "", offsetof(VPxContext, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, UINT_MAX, VE, "flags"}, \ - {"error_resilient", "enable error resilience", 0, AV_OPT_TYPE_CONST, {.i64 = VP8F_ERROR_RESILIENT}, INT_MIN, INT_MAX, VE, "flags"}, \ - {"altref", "enable use of alternate reference frames (VP8/2-pass only)", 0, AV_OPT_TYPE_CONST, {.i64 = VP8F_AUTO_ALT_REF}, INT_MIN, INT_MAX, VE, "flags"}, \ + {"quality", "", offsetof(VPxContext, deadline), AV_OPT_TYPE_INT, {.i64 = VPX_DL_GOOD_QUALITY}, INT_MIN, INT_MAX, VE, .unit = "quality"}, \ + {"vp8flags", "", offsetof(VPxContext, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, UINT_MAX, VE, .unit = "flags"}, \ + {"error_resilient", "enable error resilience", 0, AV_OPT_TYPE_CONST, {.i64 = VP8F_ERROR_RESILIENT}, INT_MIN, INT_MAX, VE, .unit = "flags"}, \ + {"altref", "enable use of alternate reference frames (VP8/2-pass only)", 0, AV_OPT_TYPE_CONST, {.i64 = VP8F_AUTO_ALT_REF}, INT_MIN, INT_MAX, VE, .unit = "flags"}, \ {"arnr_max_frames", "altref noise reduction max frame count", offsetof(VPxContext, arnr_max_frames), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 15, VE}, \ {"arnr_strength", "altref noise reduction filter strength", offsetof(VPxContext, arnr_strength), AV_OPT_TYPE_INT, {.i64 = 3}, 0, 6, VE}, \ {"arnr_type", "altref noise reduction filter type", offsetof(VPxContext, arnr_type), AV_OPT_TYPE_INT, {.i64 = 3}, 1, 3, VE}, \ @@ -1871,6 +1944,7 @@ static const AVOption vp8_options[] = { { "auto-alt-ref", "Enable use of alternate reference " "frames (2-pass only)", OFFSET(auto_alt_ref), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 2, VE}, { "cpu-used", "Quality/Speed ratio modifier", OFFSET(cpu_used), AV_OPT_TYPE_INT, {.i64 = 1}, -16, 16, VE}, + { "screen-content-mode", "Encoder screen content mode", OFFSET(screen_content_mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 2, VE}, LEGACY_OPTIONS { NULL } }; @@ -1887,16 +1961,16 @@ static const AVOption vp9_options[] = { { "tile-rows", "Number of tile rows to use, log2", OFFSET(tile_rows), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 2, VE}, { "frame-parallel", "Enable frame parallel decodability features", OFFSET(frame_parallel), AV_OPT_TYPE_BOOL,{.i64 = -1}, -1, 1, VE}, #if VPX_ENCODER_ABI_VERSION >= 12 - { "aq-mode", "adaptive quantization mode", OFFSET(aq_mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 4, VE, "aq_mode"}, + { "aq-mode", "adaptive quantization mode", OFFSET(aq_mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 4, VE, .unit = "aq_mode"}, #else - { "aq-mode", "adaptive quantization mode", OFFSET(aq_mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 3, VE, "aq_mode"}, + { "aq-mode", "adaptive quantization mode", OFFSET(aq_mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 3, VE, .unit = "aq_mode"}, #endif - { "none", "Aq not used", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, VE, "aq_mode" }, - { "variance", "Variance based Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 0, VE, "aq_mode" }, - { "complexity", "Complexity based Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 0, VE, "aq_mode" }, - { "cyclic", "Cyclic Refresh Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 3}, 0, 0, VE, "aq_mode" }, + { "none", "Aq not used", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, VE, .unit = "aq_mode" }, + { "variance", "Variance based Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 0, VE, .unit = "aq_mode" }, + { "complexity", "Complexity based Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 0, VE, .unit = "aq_mode" }, + { "cyclic", "Cyclic Refresh Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 3}, 0, 0, VE, .unit = "aq_mode" }, #if VPX_ENCODER_ABI_VERSION >= 12 - { "equator360", "360 video Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 4}, 0, 0, VE, "aq_mode" }, + { "equator360", "360 video Aq", 0, AV_OPT_TYPE_CONST, {.i64 = 4}, 0, 0, VE, .unit = "aq_mode" }, {"level", "Specify level", OFFSET(level), AV_OPT_TYPE_FLOAT, {.dbl=-1}, -1, 6.2, VE}, #endif #ifdef VPX_CTRL_VP9E_SET_ROW_MT @@ -1904,14 +1978,14 @@ static const AVOption vp9_options[] = { #endif #ifdef VPX_CTRL_VP9E_SET_TUNE_CONTENT #if VPX_ENCODER_ABI_VERSION >= 14 - { "tune-content", "Tune content type", OFFSET(tune_content), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 2, VE, "tune_content" }, + { "tune-content", "Tune content type", OFFSET(tune_content), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 2, VE, .unit = "tune_content" }, #else - { "tune-content", "Tune content type", OFFSET(tune_content), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, VE, "tune_content" }, + { "tune-content", "Tune content type", OFFSET(tune_content), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 1, VE, .unit = "tune_content" }, #endif - { "default", "Regular video content", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, VE, "tune_content" }, - { "screen", "Screen capture content", 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 0, VE, "tune_content" }, + { "default", "Regular video content", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, VE, .unit = "tune_content" }, + { "screen", "Screen capture content", 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 0, VE, .unit = "tune_content" }, #if VPX_ENCODER_ABI_VERSION >= 14 - { "film", "Film content; improves grain retention", 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 0, VE, "tune_content" }, + { "film", "Film content; improves grain retention", 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 0, VE, .unit = "tune_content" }, #endif #endif #if VPX_ENCODER_ABI_VERSION >= 14 @@ -1959,12 +2033,14 @@ const FFCodec ff_libvpx_vp8_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_VP8, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | - AV_CODEC_CAP_OTHER_THREADS, + AV_CODEC_CAP_OTHER_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(VPxContext), .init = vp8_init, FF_CODEC_ENCODE_CB(vpx_encode), .close = vpx_free, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | + FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_AUTO_THREADS, .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_NONE }, .p.priv_class = &class_vp8, @@ -1979,6 +2055,45 @@ static av_cold int vp9_init(AVCodecContext *avctx) return vpx_init(avctx, vpx_codec_vp9_cx()); } +static const enum AVPixelFormat vp9_pix_fmts_highcol[] = { + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVA420P, + AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUV444P, + AV_PIX_FMT_GBRP, + AV_PIX_FMT_NONE +}; + +static const enum AVPixelFormat vp9_pix_fmts_highbd[] = { + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUVA420P, + AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV440P, + AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUV420P10, + AV_PIX_FMT_YUV422P10, + AV_PIX_FMT_YUV440P10, + AV_PIX_FMT_YUV444P10, + AV_PIX_FMT_YUV420P12, + AV_PIX_FMT_YUV422P12, + AV_PIX_FMT_YUV440P12, + AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_GBRP, + AV_PIX_FMT_GBRP10, + AV_PIX_FMT_GBRP12, + AV_PIX_FMT_NONE +}; + +static av_cold void vp9_init_static(FFCodec *codec) +{ + vpx_codec_caps_t codec_caps = vpx_codec_get_caps(vpx_codec_vp9_cx()); + if (codec_caps & VPX_CODEC_CAP_HIGHBITDEPTH) + codec->p.pix_fmts = vp9_pix_fmts_highbd; + else + codec->p.pix_fmts = vp9_pix_fmts_highcol; +} + static const AVClass class_vp9 = { .class_name = "libvpx-vp9 encoder", .item_name = av_default_item_name, @@ -1992,7 +2107,8 @@ FFCodec ff_libvpx_vp9_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_VP9, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | - AV_CODEC_CAP_OTHER_THREADS, + AV_CODEC_CAP_OTHER_THREADS | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .p.profiles = NULL_IF_CONFIG_SMALL(ff_vp9_profiles), .p.priv_class = &class_vp9, .p.wrapper_name = "libvpx", @@ -2001,8 +2117,9 @@ FFCodec ff_libvpx_vp9_encoder = { FF_CODEC_ENCODE_CB(vpx_encode), .close = vpx_free, .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | + FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_AUTO_THREADS, .defaults = defaults, - .init_static_data = ff_vp9_init_static, + .init_static_data = vp9_init_static, }; #endif /* CONFIG_LIBVPX_VP9_ENCODER */ diff --git a/libavcodec/libwebpenc_animencoder.c b/libavcodec/libwebpenc_animencoder.c index 8756231f23c..02f0b6f64d3 100644 --- a/libavcodec/libwebpenc_animencoder.c +++ b/libavcodec/libwebpenc_animencoder.c @@ -39,9 +39,6 @@ typedef struct LibWebPAnimContext { int64_t first_frame_pts; // pts of the first encoded frame. int64_t end_pts; // pts + duration of the last frame -#if FF_API_REORDERED_OPAQUE - int64_t reordered_opaque; -#endif void *first_frame_opaque; AVBufferRef *first_frame_opaque_ref; @@ -92,11 +89,6 @@ static int libwebp_anim_encode_frame(AVCodecContext *avctx, AVPacket *pkt, if (pkt->pts != AV_NOPTS_VALUE && s->end_pts > pkt->pts) pkt->duration = s->end_pts - pkt->pts; -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - avctx->reordered_opaque = s->reordered_opaque; -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { pkt->opaque = s->first_frame_opaque; pkt->opaque_ref = s->first_frame_opaque_ref; @@ -134,11 +126,6 @@ FF_ENABLE_DEPRECATION_WARNINGS if (!avctx->frame_num) { s->first_frame_pts = frame->pts; -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - s->reordered_opaque = frame->reordered_opaque; -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { s->first_frame_opaque = frame->opaque; diff --git a/libavcodec/libwebpenc_common.c b/libavcodec/libwebpenc_common.c index 761219e50e7..5645d117279 100644 --- a/libavcodec/libwebpenc_common.c +++ b/libavcodec/libwebpenc_common.c @@ -37,14 +37,14 @@ const FFCodecDefault ff_libwebp_defaults[] = { #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { { "lossless", "Use lossless mode", OFFSET(lossless), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, - { "preset", "Configuration preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, WEBP_PRESET_TEXT, VE, "preset" }, - { "none", "do not use a preset", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0, VE, "preset" }, - { "default", "default preset", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_DEFAULT }, 0, 0, VE, "preset" }, - { "picture", "digital picture, like portrait, inner shot", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_PICTURE }, 0, 0, VE, "preset" }, - { "photo", "outdoor photograph, with natural lighting", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_PHOTO }, 0, 0, VE, "preset" }, - { "drawing", "hand or line drawing, with high-contrast details", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_DRAWING }, 0, 0, VE, "preset" }, - { "icon", "small-sized colorful images", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_ICON }, 0, 0, VE, "preset" }, - { "text", "text-like", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_TEXT }, 0, 0, VE, "preset" }, + { "preset", "Configuration preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, WEBP_PRESET_TEXT, VE, .unit = "preset" }, + { "none", "do not use a preset", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0, VE, .unit = "preset" }, + { "default", "default preset", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_DEFAULT }, 0, 0, VE, .unit = "preset" }, + { "picture", "digital picture, like portrait, inner shot", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_PICTURE }, 0, 0, VE, .unit = "preset" }, + { "photo", "outdoor photograph, with natural lighting", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_PHOTO }, 0, 0, VE, .unit = "preset" }, + { "drawing", "hand or line drawing, with high-contrast details", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_DRAWING }, 0, 0, VE, .unit = "preset" }, + { "icon", "small-sized colorful images", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_ICON }, 0, 0, VE, .unit = "preset" }, + { "text", "text-like", 0, AV_OPT_TYPE_CONST, { .i64 = WEBP_PRESET_TEXT }, 0, 0, VE, .unit = "preset" }, { "cr_threshold","Conditional replenishment threshold", OFFSET(cr_threshold), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, { "cr_size" ,"Conditional replenishment block size", OFFSET(cr_size) , AV_OPT_TYPE_INT, { .i64 = 16 }, 0, 256, VE }, { "quality" ,"Quality", OFFSET(quality), AV_OPT_TYPE_FLOAT, { .dbl = 75 }, 0, 100, VE }, diff --git a/libavcodec/libx264.c b/libavcodec/libx264.c index f65ac5dacc2..813ccbbdb32 100644 --- a/libavcodec/libx264.c +++ b/libavcodec/libx264.c @@ -25,11 +25,13 @@ #include "libavutil/eval.h" #include "libavutil/internal.h" #include "libavutil/opt.h" +#include "libavutil/mastering_display_metadata.h" #include "libavutil/mem.h" #include "libavutil/pixdesc.h" #include "libavutil/stereo3d.h" #include "libavutil/time.h" #include "libavutil/intreadwrite.h" +#include "libavutil/video_hint.h" #include "avcodec.h" #include "codec_internal.h" #include "encode.h" @@ -37,6 +39,7 @@ #include "packet_internal.h" #include "atsc_a53.h" #include "sei.h" +#include "golomb.h" #include #include @@ -48,11 +51,11 @@ // from x264.h, for quant_offsets, Macroblocks are 16x16 // blocks of pixels (with respect to the luma plane) #define MB_SIZE 16 +#define MB_LSIZE 4 +#define MB_FLOOR(x) ((x) >> (MB_LSIZE)) +#define MB_CEIL(x) MB_FLOOR((x) + (MB_SIZE - 1)) typedef struct X264Opaque { -#if FF_API_REORDERED_OPAQUE - int64_t reordered_opaque; -#endif int64_t wallclock; int64_t duration; @@ -123,6 +126,8 @@ typedef struct X264Context { * encounter a frame with ROI side data. */ int roi_warned; + + int mb_info; } X264Context; static void X264_log(void *p, int level, const char *fmt, va_list args) @@ -151,7 +156,7 @@ static int encode_nals(AVCodecContext *ctx, AVPacket *pkt, { X264Context *x4 = ctx->priv_data; uint8_t *p; - uint64_t size = x4->sei_size; + uint64_t size = FFMAX(x4->sei_size, 0); int ret; if (!nnal) @@ -178,8 +183,8 @@ static int encode_nals(AVCodecContext *ctx, AVPacket *pkt, memcpy(p, x4->sei, x4->sei_size); p += x4->sei_size; size -= x4->sei_size; - x4->sei_size = 0; - av_freep(&x4->sei); + /* Keep the value around in case of flush */ + x4->sei_size = -x4->sei_size; } /* x264 guarantees the payloads of the NALs @@ -189,28 +194,6 @@ static int encode_nals(AVCodecContext *ctx, AVPacket *pkt, return 1; } -static int avfmt2_num_planes(int avfmt) -{ - switch (avfmt) { - case AV_PIX_FMT_YUV420P: - case AV_PIX_FMT_YUVJ420P: - case AV_PIX_FMT_YUV420P9: - case AV_PIX_FMT_YUV420P10: - case AV_PIX_FMT_YUV444P: - return 3; - - case AV_PIX_FMT_BGR0: - case AV_PIX_FMT_BGR24: - case AV_PIX_FMT_RGB24: - case AV_PIX_FMT_GRAY8: - case AV_PIX_FMT_GRAY10: - return 1; - - default: - return 3; - } -} - static void reconfig_encoder(AVCodecContext *ctx, const AVFrame *frame) { X264Context *x4 = ctx->priv_data; @@ -218,9 +201,9 @@ static void reconfig_encoder(AVCodecContext *ctx, const AVFrame *frame) if (x4->avcintra_class < 0) { - if (x4->params.b_interlaced && x4->params.b_tff != frame->top_field_first) { + if (x4->params.b_interlaced && x4->params.b_tff != !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)) { - x4->params.b_tff = frame->top_field_first; + x4->params.b_tff = !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); x264_encoder_reconfig(x4->enc, &x4->params); } if (x4->params.vui.i_sar_height*ctx->sample_aspect_ratio.num != ctx->sample_aspect_ratio.den * x4->params.vui.i_sar_width) { @@ -311,15 +294,13 @@ static void reconfig_encoder(AVCodecContext *ctx, const AVFrame *frame) } } -static void free_picture(AVCodecContext *ctx) +static void free_picture(x264_picture_t *pic) { - X264Context *x4 = ctx->priv_data; - x264_picture_t *pic = &x4->pic; - for (int i = 0; i < pic->extra_sei.num_payloads; i++) av_free(pic->extra_sei.payloads[i].payload); av_freep(&pic->extra_sei.payloads); av_freep(&pic->prop.quant_offsets); + av_freep(&pic->prop.mb_info); pic->extra_sei.num_payloads = 0; } @@ -345,6 +326,74 @@ static enum AVPixelFormat csp_to_pixfmt(int csp) return AV_PIX_FMT_NONE; } +static void av_always_inline mbinfo_compute_changed_coords(const AVVideoRect *rect, + int *min_x, + int *max_x, + int *min_y, + int *max_y) +{ + *min_y = MB_FLOOR(rect->y); + *max_y = MB_CEIL(rect->y + rect->height); + *min_x = MB_FLOOR(rect->x); + *max_x = MB_CEIL(rect->x + rect->width); +} + +static void av_always_inline mbinfo_compute_constant_coords(const AVVideoRect *rect, + int *min_x, + int *max_x, + int *min_y, + int *max_y) +{ + *min_y = MB_CEIL(rect->y); + *max_y = MB_FLOOR(rect->y + rect->height); + *min_x = MB_CEIL(rect->x); + *max_x = MB_FLOOR(rect->x + rect->width); +} + +static int setup_mb_info(AVCodecContext *ctx, x264_picture_t *pic, + const AVFrame *frame, + const AVVideoHint *info) +{ + int mb_width = (frame->width + MB_SIZE - 1) / MB_SIZE; + int mb_height = (frame->height + MB_SIZE - 1) / MB_SIZE; + + const AVVideoRect *mbinfo_rects; + int nb_rects; + uint8_t *mbinfo; + + mbinfo_rects = (const AVVideoRect *)av_video_hint_rects(info); + nb_rects = info->nb_rects; + + mbinfo = av_calloc(mb_width * mb_height, sizeof(*mbinfo)); + if (!mbinfo) + return AVERROR(ENOMEM); + +#define COMPUTE_MBINFO(mbinfo_filler_, mbinfo_marker_, compute_coords_fn_) \ + memset(mbinfo, mbinfo_filler_, sizeof(*mbinfo) * mb_width * mb_height); \ + \ + for (int i = 0; i < nb_rects; i++) { \ + int min_x, max_x, min_y, max_y; \ + \ + compute_coords_fn_(mbinfo_rects, &min_x, &max_x, &min_y, &max_y); \ + for (int mb_y = min_y; mb_y < max_y; ++mb_y) { \ + memset(mbinfo + mb_y * mb_width + min_x, mbinfo_marker_, max_x - min_x); \ + } \ + \ + mbinfo_rects++; \ + } \ + + if (info->type == AV_VIDEO_HINT_TYPE_CHANGED) { + COMPUTE_MBINFO(X264_MBINFO_CONSTANT, 0, mbinfo_compute_changed_coords); + } else /* if (info->type == AV_VIDEO_HINT_TYPE_CHANGED) */ { + COMPUTE_MBINFO(0, X264_MBINFO_CONSTANT, mbinfo_compute_constant_coords); + } + + pic->prop.mb_info = mbinfo; + pic->prop.mb_info_free = av_free; + + return 0; +} + static int setup_roi(AVCodecContext *ctx, x264_picture_t *pic, int bit_depth, const AVFrame *frame, const uint8_t *data, size_t size) { @@ -364,7 +413,7 @@ static int setup_roi(AVCodecContext *ctx, x264_picture_t *pic, int bit_depth, av_log(ctx, AV_LOG_WARNING, "Adaptive quantization must be enabled to use ROI encoding, skipping ROI.\n"); } return 0; - } else if (frame->interlaced_frame) { + } else if (frame->flags & AV_FRAME_FLAG_INTERLACED) { if (!x4->roi_warned) { x4->roi_warned = 1; av_log(ctx, AV_LOG_WARNING, "interlaced_frame not supported for ROI encoding yet, skipping ROI.\n"); @@ -429,6 +478,7 @@ static int setup_frame(AVCodecContext *ctx, const AVFrame *frame, int64_t wallclock = 0; int bit_depth, ret; AVFrameSideData *sd; + AVFrameSideData *mbinfo_sd; *ppic = NULL; if (!frame) @@ -443,7 +493,7 @@ static int setup_frame(AVCodecContext *ctx, const AVFrame *frame, #endif if (bit_depth > 8) pic->img.i_csp |= X264_CSP_HIGH_DEPTH; - pic->img.i_plane = avfmt2_num_planes(ctx->pix_fmt); + pic->img.i_plane = av_pix_fmt_count_planes(ctx->pix_fmt); for (int i = 0; i < pic->img.i_plane; i++) { pic->img.plane[i] = frame->data[i]; @@ -461,11 +511,6 @@ static int setup_frame(AVCodecContext *ctx, const AVFrame *frame, goto fail; } -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - opaque->reordered_opaque = frame->reordered_opaque; -FF_ENABLE_DEPRECATION_WARNINGS -#endif opaque->duration = frame->duration; opaque->wallclock = wallclock; if (ctx->export_side_data & AV_CODEC_EXPORT_DATA_PRFT) @@ -501,18 +546,19 @@ FF_ENABLE_DEPRECATION_WARNINGS goto fail; if (sei_data) { - pic->extra_sei.payloads = av_mallocz(sizeof(pic->extra_sei.payloads[0])); - if (pic->extra_sei.payloads == NULL) { + sei->payloads = av_mallocz(sizeof(sei->payloads[0])); + if (!sei->payloads) { + av_free(sei_data); ret = AVERROR(ENOMEM); goto fail; } - pic->extra_sei.sei_free = av_free; + sei->sei_free = av_free; - pic->extra_sei.payloads[0].payload_size = sei_size; - pic->extra_sei.payloads[0].payload = sei_data; - pic->extra_sei.num_payloads = 1; - pic->extra_sei.payloads[0].payload_type = 4; + sei->payloads[0].payload_size = sei_size; + sei->payloads[0].payload = sei_data; + sei->payloads[0].payload_type = SEI_TYPE_USER_DATA_REGISTERED_ITU_T_T35; + sei->num_payloads = 1; } } @@ -523,6 +569,17 @@ FF_ENABLE_DEPRECATION_WARNINGS goto fail; } + mbinfo_sd = av_frame_get_side_data(frame, AV_FRAME_DATA_VIDEO_HINT); + if (mbinfo_sd) { + int ret = setup_mb_info(ctx, pic, frame, (const AVVideoHint *)mbinfo_sd->data); + if (ret < 0) { + /* No need to fail here, this is not fatal. We just proceed with no + * mb_info and log a message */ + + av_log(ctx, AV_LOG_WARNING, "setup_mb_info failed with error: %s\n", av_err2str(ret)); + } + } + if (x4->udu_sei) { for (int j = 0; j < frame->nb_side_data; j++) { AVFrameSideData *side_data = frame->side_data[j]; @@ -553,7 +610,7 @@ FF_ENABLE_DEPRECATION_WARNINGS return 0; fail: - free_picture(ctx); + free_picture(pic); *ppic = NULL; return ret; } @@ -618,11 +675,6 @@ static int X264_frame(AVCodecContext *ctx, AVPacket *pkt, const AVFrame *frame, out_opaque = pic_out.opaque; if (out_opaque >= x4->reordered_opaque && out_opaque < &x4->reordered_opaque[x4->nb_reordered_opaque]) { -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - ctx->reordered_opaque = out_opaque->reordered_opaque; -FF_ENABLE_DEPRECATION_WARNINGS -#endif wallclock = out_opaque->wallclock; pkt->duration = out_opaque->duration; @@ -637,11 +689,6 @@ FF_ENABLE_DEPRECATION_WARNINGS // Unexpected opaque pointer on picture output av_log(ctx, AV_LOG_ERROR, "Unexpected opaque pointer; " "this is a bug, please report it.\n"); -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - ctx->reordered_opaque = 0; -FF_ENABLE_DEPRECATION_WARNINGS -#endif } switch (pic_out.i_type) { @@ -663,7 +710,36 @@ FF_ENABLE_DEPRECATION_WARNINGS pkt->flags |= AV_PKT_FLAG_KEY*pic_out.b_keyframe; if (ret) { - ff_side_data_set_encoder_stats(pkt, (pic_out.i_qpplus1 - 1) * FF_QP2LAMBDA, NULL, 0, pict_type); + int error_count = 0; + int64_t *errors = NULL; + int64_t sse[3] = {0}; + + if (ctx->flags & AV_CODEC_FLAG_PSNR) { + const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(ctx->pix_fmt); + double scale[3] = { 1, + (double)(1 << pix_desc->log2_chroma_h) * (1 << pix_desc->log2_chroma_w), + (double)(1 << pix_desc->log2_chroma_h) * (1 << pix_desc->log2_chroma_w), + }; + + error_count = pix_desc->nb_components; + + for (int i = 0; i < pix_desc->nb_components; ++i) { + double max_value = (double)(1 << pix_desc->comp[i].depth) - 1.0; + double plane_size = ctx->width * (double)ctx->height / scale[i]; + + /* psnr = 10 * log10(max_value * max_value / mse) */ + double mse = (max_value * max_value) / pow(10, pic_out.prop.f_psnr[i] / 10.0); + + /* SSE = MSE * width * height / scale -> because of possible chroma downsampling */ + sse[i] = (int64_t)floor(mse * plane_size + .5); + }; + + errors = sse; + } + + ff_side_data_set_encoder_stats(pkt, (pic_out.i_qpplus1 - 1) * FF_QP2LAMBDA, + errors, error_count, pict_type); + if (wallclock) ff_side_data_set_prft(pkt, wallclock); } @@ -672,6 +748,24 @@ FF_ENABLE_DEPRECATION_WARNINGS return 0; } +static void X264_flush(AVCodecContext *avctx) +{ + X264Context *x4 = avctx->priv_data; + x264_nal_t *nal; + int nnal, ret; + x264_picture_t pic_out = {0}; + + do { + ret = x264_encoder_encode(x4->enc, &nal, &nnal, NULL, &pic_out); + } while (ret > 0 && x264_encoder_delayed_frames(x4->enc)); + + for (int i = 0; i < x4->nb_reordered_opaque; i++) + opaque_uninit(&x4->reordered_opaque[i]); + + if (x4->sei_size < 0) + x4->sei_size = -x4->sei_size; +} + static av_cold int X264_close(AVCodecContext *avctx) { X264Context *x4 = avctx->priv_data; @@ -755,12 +849,226 @@ static int convert_pix_fmt(enum AVPixelFormat pix_fmt) return 0; } +static int save_sei(AVCodecContext *avctx, x264_nal_t *nal) +{ + X264Context *x4 = avctx->priv_data; + + av_log(avctx, AV_LOG_INFO, "%s\n", nal->p_payload + 25); + x4->sei_size = nal->i_payload; + x4->sei = av_malloc(x4->sei_size); + if (!x4->sei) + return AVERROR(ENOMEM); + + memcpy(x4->sei, nal->p_payload, nal->i_payload); + + return 0; +} + +#if CONFIG_LIBX264_ENCODER +static int set_avcc_extradata(AVCodecContext *avctx, x264_nal_t *nal, int nnal) +{ + x264_nal_t *sps_nal = NULL; + x264_nal_t *pps_nal = NULL; + uint8_t *p, *sps; + int ret; + + /* We know it's in the order of SPS/PPS/SEI, but it's not documented in x264 API. + * The x264 param i_sps_id implies there is a single pair of SPS/PPS. + */ + for (int i = 0; i < nnal; i++) { + switch (nal[i].i_type) { + case NAL_SPS: + sps_nal = &nal[i]; + break; + case NAL_PPS: + pps_nal = &nal[i]; + break; + case NAL_SEI: + ret = save_sei(avctx, &nal[i]); + if (ret < 0) + return ret; + break; + } + } + if (!sps_nal || !pps_nal) + return AVERROR_EXTERNAL; + + avctx->extradata_size = sps_nal->i_payload + pps_nal->i_payload + 7; + avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!avctx->extradata) + return AVERROR(ENOMEM); + + // Now create AVCDecoderConfigurationRecord + p = avctx->extradata; + // Skip size part + sps = sps_nal->p_payload + 4; + *p++ = 1; // version + *p++ = sps[1]; // AVCProfileIndication + *p++ = sps[2]; // profile_compatibility + *p++ = sps[3]; // AVCLevelIndication + *p++ = 0xFF; + *p++ = 0xE0 | 0x01; // 3 bits reserved (111) + 5 bits number of sps + memcpy(p, sps_nal->p_payload + 2, sps_nal->i_payload - 2); + // Make sps has AV_INPUT_BUFFER_PADDING_SIZE padding, so it can be used + // with GetBitContext + sps = p + 2; + p += sps_nal->i_payload - 2; + *p++ = 1; + memcpy(p, pps_nal->p_payload + 2, pps_nal->i_payload - 2); + p += pps_nal->i_payload - 2; + + if (sps[3] != 66 && sps[3] != 77 && sps[3] != 88) { + GetBitContext gbc; + int chroma_format_idc; + int bit_depth_luma_minus8, bit_depth_chroma_minus8; + + /* It's not possible to have emulation prevention byte before + * bit_depth_chroma_minus8 due to the range of sps id, chroma_format_idc + * and so on. So we can read directly without need to escape emulation + * prevention byte. + * + * +4 to skip until sps id. + */ + ret = init_get_bits8(&gbc, sps + 4, sps_nal->i_payload - 4 - 4); + if (ret < 0) + return ret; + // Skip sps id + get_ue_golomb_31(&gbc); + chroma_format_idc = get_ue_golomb_31(&gbc); + if (chroma_format_idc == 3) + skip_bits1(&gbc); + bit_depth_luma_minus8 = get_ue_golomb_31(&gbc); + bit_depth_chroma_minus8 = get_ue_golomb_31(&gbc); + + *p++ = 0xFC | chroma_format_idc; + *p++ = 0xF8 | bit_depth_luma_minus8; + *p++ = 0xF8 | bit_depth_chroma_minus8; + *p++ = 0; + } + av_assert2(avctx->extradata + avctx->extradata_size >= p); + avctx->extradata_size = p - avctx->extradata; + + return 0; +} +#endif + +static int set_extradata(AVCodecContext *avctx) +{ + X264Context *x4 = avctx->priv_data; + x264_nal_t *nal; + uint8_t *p; + int nnal, s; + + s = x264_encoder_headers(x4->enc, &nal, &nnal); + if (s < 0) + return AVERROR_EXTERNAL; + +#if CONFIG_LIBX264_ENCODER + if (!x4->params.b_annexb) + return set_avcc_extradata(avctx, nal, nnal); +#endif + + avctx->extradata = p = av_mallocz(s + AV_INPUT_BUFFER_PADDING_SIZE); + if (!p) + return AVERROR(ENOMEM); + + for (int i = 0; i < nnal; i++) { + /* Don't put the SEI in extradata. */ + if (nal[i].i_type == NAL_SEI) { + s = save_sei(avctx, &nal[i]); + if (s < 0) + return s; + continue; + } + memcpy(p, nal[i].p_payload, nal[i].i_payload); + p += nal[i].i_payload; + } + avctx->extradata_size = p - avctx->extradata; + + return 0; +} + #define PARSE_X264_OPT(name, var)\ if (x4->var && x264_param_parse(&x4->params, name, x4->var) < 0) {\ av_log(avctx, AV_LOG_ERROR, "Error parsing option '%s' with value '%s'.\n", name, x4->var);\ return AVERROR(EINVAL);\ } +#if CONFIG_LIBX264_HDR10 +static void handle_mdcv(x264_param_t *params, + const AVMasteringDisplayMetadata *mdcv) +{ + if (!mdcv->has_primaries && !mdcv->has_luminance) + return; + + params->mastering_display.b_mastering_display = 1; + + if (mdcv->has_primaries) { + int *const points[][2] = { + { + ¶ms->mastering_display.i_red_x, + ¶ms->mastering_display.i_red_y + }, + { + ¶ms->mastering_display.i_green_x, + ¶ms->mastering_display.i_green_y + }, + { + ¶ms->mastering_display.i_blue_x, + ¶ms->mastering_display.i_blue_y + }, + }; + + for (int i = 0; i < 3; i++) { + const AVRational *src = mdcv->display_primaries[i]; + int *dst[2] = { points[i][0], points[i][1] }; + + *dst[0] = av_rescale_q(1, src[0], (AVRational){ 1, 50000 }); + *dst[1] = av_rescale_q(1, src[1], (AVRational){ 1, 50000 }); + } + + params->mastering_display.i_white_x = + av_rescale_q(1, mdcv->white_point[0], (AVRational){ 1, 50000 }); + params->mastering_display.i_white_y = + av_rescale_q(1, mdcv->white_point[1], (AVRational){ 1, 50000 }); + } + + if (mdcv->has_luminance) { + params->mastering_display.i_display_max = + av_rescale_q(1, mdcv->max_luminance, (AVRational){ 1, 10000 }); + params->mastering_display.i_display_min = + av_rescale_q(1, mdcv->min_luminance, (AVRational){ 1, 10000 }); + } +} +#endif // CONFIG_LIBX264_HDR10 + +static void handle_side_data(AVCodecContext *avctx, x264_param_t *params) +{ +#if CONFIG_LIBX264_HDR10 + const AVFrameSideData *cll_sd = + av_frame_side_data_get(avctx->decoded_side_data, + avctx->nb_decoded_side_data, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + const AVFrameSideData *mdcv_sd = + av_frame_side_data_get(avctx->decoded_side_data, + avctx->nb_decoded_side_data, + AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + + if (cll_sd) { + const AVContentLightMetadata *cll = + (AVContentLightMetadata *)cll_sd->data; + + params->content_light_level.i_max_cll = cll->MaxCLL; + params->content_light_level.i_max_fall = cll->MaxFALL; + + params->content_light_level.b_cll = 1; + } + + if (mdcv_sd) { + handle_mdcv(params, (AVMasteringDisplayMetadata *)mdcv_sd->data); + } +#endif // CONFIG_LIBX264_HDR10 +} + static av_cold int X264_init(AVCodecContext *avctx) { X264Context *x4 = avctx->priv_data; @@ -967,22 +1275,22 @@ static av_cold int X264_init(AVCodecContext *avctx) /* Allow specifying the x264 profile through AVCodecContext. */ if (!x4->profile) switch (avctx->profile) { - case FF_PROFILE_H264_BASELINE: + case AV_PROFILE_H264_BASELINE: x4->profile = "baseline"; break; - case FF_PROFILE_H264_HIGH: + case AV_PROFILE_H264_HIGH: x4->profile = "high"; break; - case FF_PROFILE_H264_HIGH_10: + case AV_PROFILE_H264_HIGH_10: x4->profile = "high10"; break; - case FF_PROFILE_H264_HIGH_422: + case AV_PROFILE_H264_HIGH_422: x4->profile = "high422"; break; - case FF_PROFILE_H264_HIGH_444: + case AV_PROFILE_H264_HIGH_444: x4->profile = "high444"; break; - case FF_PROFILE_H264_MAIN: + case AV_PROFILE_H264_MAIN: x4->profile = "main"; break; default: @@ -1024,7 +1332,13 @@ static av_cold int X264_init(AVCodecContext *avctx) x4->params.i_fps_den = avctx->framerate.den; } else { x4->params.i_fps_num = avctx->time_base.den; - x4->params.i_fps_den = avctx->time_base.num * avctx->ticks_per_frame; +FF_DISABLE_DEPRECATION_WARNINGS + x4->params.i_fps_den = avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } x4->params.analyse.b_psnr = avctx->flags & AV_CODEC_FLAG_PSNR; @@ -1055,6 +1369,8 @@ static av_cold int X264_init(AVCodecContext *avctx) if (avctx->chroma_sample_location != AVCHROMA_LOC_UNSPECIFIED) x4->params.vui.i_chroma_loc = avctx->chroma_sample_location - 1; + handle_side_data(avctx, &x4->params); + if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) x4->params.b_repeat_headers = 0; @@ -1102,6 +1418,8 @@ static av_cold int X264_init(AVCodecContext *avctx) } } + x4->params.analyse.b_mb_info = x4->mb_info; + // update AVCodecContext with x264 parameters avctx->has_b_frames = x4->params.i_bframe ? x4->params.i_bframe_pyramid ? 2 : 1 : 0; @@ -1115,33 +1433,12 @@ static av_cold int X264_init(AVCodecContext *avctx) return AVERROR_EXTERNAL; if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { - x264_nal_t *nal; - uint8_t *p; - int nnal, s, i; - - s = x264_encoder_headers(x4->enc, &nal, &nnal); - avctx->extradata = p = av_mallocz(s + AV_INPUT_BUFFER_PADDING_SIZE); - if (!p) - return AVERROR(ENOMEM); - - for (i = 0; i < nnal; i++) { - /* Don't put the SEI in extradata. */ - if (nal[i].i_type == NAL_SEI) { - av_log(avctx, AV_LOG_INFO, "%s\n", nal[i].p_payload+25); - x4->sei_size = nal[i].i_payload; - x4->sei = av_malloc(x4->sei_size); - if (!x4->sei) - return AVERROR(ENOMEM); - memcpy(x4->sei, nal[i].p_payload, nal[i].i_payload); - continue; - } - memcpy(p, nal[i].p_payload, nal[i].i_payload); - p += nal[i].i_payload; - } - avctx->extradata_size = p - avctx->extradata; + ret = set_extradata(avctx); + if (ret < 0) + return ret; } - cpb_props = ff_add_cpb_side_data(avctx); + cpb_props = ff_encode_add_cpb_side_data(avctx); if (!cpb_props) return AVERROR(ENOMEM); cpb_props->buffer_size = x4->params.rc.i_vbv_buffer_size * 1000; @@ -1245,30 +1542,30 @@ static const AVOption options[] = { { "crf", "Select the quality for constant quality mode", OFFSET(crf), AV_OPT_TYPE_FLOAT, {.dbl = -1 }, -1, FLT_MAX, VE }, { "crf_max", "In CRF mode, prevents VBV from lowering quality beyond this point.",OFFSET(crf_max), AV_OPT_TYPE_FLOAT, {.dbl = -1 }, -1, FLT_MAX, VE }, { "qp", "Constant quantization parameter rate control method",OFFSET(cqp), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE }, - { "aq-mode", "AQ method", OFFSET(aq_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, "aq_mode"}, - { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_AQ_NONE}, INT_MIN, INT_MAX, VE, "aq_mode" }, - { "variance", "Variance AQ (complexity mask)", 0, AV_OPT_TYPE_CONST, {.i64 = X264_AQ_VARIANCE}, INT_MIN, INT_MAX, VE, "aq_mode" }, - { "autovariance", "Auto-variance AQ", 0, AV_OPT_TYPE_CONST, {.i64 = X264_AQ_AUTOVARIANCE}, INT_MIN, INT_MAX, VE, "aq_mode" }, + { "aq-mode", "AQ method", OFFSET(aq_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, .unit = "aq_mode"}, + { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_AQ_NONE}, INT_MIN, INT_MAX, VE, .unit = "aq_mode" }, + { "variance", "Variance AQ (complexity mask)", 0, AV_OPT_TYPE_CONST, {.i64 = X264_AQ_VARIANCE}, INT_MIN, INT_MAX, VE, .unit = "aq_mode" }, + { "autovariance", "Auto-variance AQ", 0, AV_OPT_TYPE_CONST, {.i64 = X264_AQ_AUTOVARIANCE}, INT_MIN, INT_MAX, VE, .unit = "aq_mode" }, #if X264_BUILD >= 144 - { "autovariance-biased", "Auto-variance AQ with bias to dark scenes", 0, AV_OPT_TYPE_CONST, {.i64 = X264_AQ_AUTOVARIANCE_BIASED}, INT_MIN, INT_MAX, VE, "aq_mode" }, + { "autovariance-biased", "Auto-variance AQ with bias to dark scenes", 0, AV_OPT_TYPE_CONST, {.i64 = X264_AQ_AUTOVARIANCE_BIASED}, INT_MIN, INT_MAX, VE, .unit = "aq_mode" }, #endif { "aq-strength", "AQ strength. Reduces blocking and blurring in flat and textured areas.", OFFSET(aq_strength), AV_OPT_TYPE_FLOAT, {.dbl = -1}, -1, FLT_MAX, VE}, { "psy", "Use psychovisual optimizations.", OFFSET(psy), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE }, { "psy-rd", "Strength of psychovisual optimization, in : format.", OFFSET(psy_rd), AV_OPT_TYPE_STRING, {0 }, 0, 0, VE}, { "rc-lookahead", "Number of frames to look ahead for frametype and ratecontrol", OFFSET(rc_lookahead), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE }, { "weightb", "Weighted prediction for B-frames.", OFFSET(weightb), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE }, - { "weightp", "Weighted prediction analysis method.", OFFSET(weightp), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, "weightp" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_WEIGHTP_NONE}, INT_MIN, INT_MAX, VE, "weightp" }, - { "simple", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_WEIGHTP_SIMPLE}, INT_MIN, INT_MAX, VE, "weightp" }, - { "smart", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_WEIGHTP_SMART}, INT_MIN, INT_MAX, VE, "weightp" }, + { "weightp", "Weighted prediction analysis method.", OFFSET(weightp), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, .unit = "weightp" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_WEIGHTP_NONE}, INT_MIN, INT_MAX, VE, .unit = "weightp" }, + { "simple", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_WEIGHTP_SIMPLE}, INT_MIN, INT_MAX, VE, .unit = "weightp" }, + { "smart", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_WEIGHTP_SMART}, INT_MIN, INT_MAX, VE, .unit = "weightp" }, { "ssim", "Calculate and print SSIM stats.", OFFSET(ssim), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE }, { "intra-refresh", "Use Periodic Intra Refresh instead of IDR frames.",OFFSET(intra_refresh),AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE }, { "bluray-compat", "Bluray compatibility workarounds.", OFFSET(bluray_compat) ,AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE }, { "b-bias", "Influences how often B-frames are used", OFFSET(b_bias), AV_OPT_TYPE_INT, { .i64 = INT_MIN}, INT_MIN, INT_MAX, VE }, - { "b-pyramid", "Keep some B-frames as references.", OFFSET(b_pyramid), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, "b_pyramid" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_B_PYRAMID_NONE}, INT_MIN, INT_MAX, VE, "b_pyramid" }, - { "strict", "Strictly hierarchical pyramid", 0, AV_OPT_TYPE_CONST, {.i64 = X264_B_PYRAMID_STRICT}, INT_MIN, INT_MAX, VE, "b_pyramid" }, - { "normal", "Non-strict (not Blu-ray compatible)", 0, AV_OPT_TYPE_CONST, {.i64 = X264_B_PYRAMID_NORMAL}, INT_MIN, INT_MAX, VE, "b_pyramid" }, + { "b-pyramid", "Keep some B-frames as references.", OFFSET(b_pyramid), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, .unit = "b_pyramid" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_B_PYRAMID_NONE}, INT_MIN, INT_MAX, VE, .unit = "b_pyramid" }, + { "strict", "Strictly hierarchical pyramid", 0, AV_OPT_TYPE_CONST, {.i64 = X264_B_PYRAMID_STRICT}, INT_MIN, INT_MAX, VE, .unit = "b_pyramid" }, + { "normal", "Non-strict (not Blu-ray compatible)", 0, AV_OPT_TYPE_CONST, {.i64 = X264_B_PYRAMID_NORMAL}, INT_MIN, INT_MAX, VE, .unit = "b_pyramid" }, { "mixed-refs", "One reference per partition, as opposed to one reference per macroblock", OFFSET(mixed_refs), AV_OPT_TYPE_BOOL, { .i64 = -1}, -1, 1, VE }, { "8x8dct", "High profile 8x8 transform.", OFFSET(dct8x8), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE}, { "fast-pskip", NULL, OFFSET(fast_pskip), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE}, @@ -1278,39 +1575,40 @@ static const AVOption options[] = { { "cplxblur", "Reduce fluctuations in QP (before curve compression)", OFFSET(cplxblur), AV_OPT_TYPE_FLOAT, {.dbl = -1 }, -1, FLT_MAX, VE}, { "partitions", "A comma-separated list of partitions to consider. " "Possible values: p8x8, p4x4, b8x8, i8x8, i4x4, none, all", OFFSET(partitions), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE}, - { "direct-pred", "Direct MV prediction mode", OFFSET(direct_pred), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, "direct-pred" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_DIRECT_PRED_NONE }, 0, 0, VE, "direct-pred" }, - { "spatial", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_DIRECT_PRED_SPATIAL }, 0, 0, VE, "direct-pred" }, - { "temporal", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_DIRECT_PRED_TEMPORAL }, 0, 0, VE, "direct-pred" }, - { "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_DIRECT_PRED_AUTO }, 0, 0, VE, "direct-pred" }, + { "direct-pred", "Direct MV prediction mode", OFFSET(direct_pred), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, .unit = "direct-pred" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_DIRECT_PRED_NONE }, 0, 0, VE, .unit = "direct-pred" }, + { "spatial", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_DIRECT_PRED_SPATIAL }, 0, 0, VE, .unit = "direct-pred" }, + { "temporal", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_DIRECT_PRED_TEMPORAL }, 0, 0, VE, .unit = "direct-pred" }, + { "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_DIRECT_PRED_AUTO }, 0, 0, VE, .unit = "direct-pred" }, { "slice-max-size","Limit the size of each slice in bytes", OFFSET(slice_max_size),AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE }, { "stats", "Filename for 2 pass stats", OFFSET(stats), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE }, { "nal-hrd", "Signal HRD information (requires vbv-bufsize; " - "cbr not allowed in .mp4)", OFFSET(nal_hrd), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, "nal-hrd" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_NAL_HRD_NONE}, INT_MIN, INT_MAX, VE, "nal-hrd" }, - { "vbr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_NAL_HRD_VBR}, INT_MIN, INT_MAX, VE, "nal-hrd" }, - { "cbr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_NAL_HRD_CBR}, INT_MIN, INT_MAX, VE, "nal-hrd" }, + "cbr not allowed in .mp4)", OFFSET(nal_hrd), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, .unit = "nal-hrd" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_NAL_HRD_NONE}, INT_MIN, INT_MAX, VE, .unit = "nal-hrd" }, + { "vbr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_NAL_HRD_VBR}, INT_MIN, INT_MAX, VE, .unit = "nal-hrd" }, + { "cbr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = X264_NAL_HRD_CBR}, INT_MIN, INT_MAX, VE, .unit = "nal-hrd" }, { "avcintra-class","AVC-Intra class 50/100/200/300/480", OFFSET(avcintra_class),AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 480 , VE}, - { "me_method", "Set motion estimation method", OFFSET(motion_est), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, X264_ME_TESA, VE, "motion-est"}, - { "motion-est", "Set motion estimation method", OFFSET(motion_est), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, X264_ME_TESA, VE, "motion-est"}, - { "dia", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_ME_DIA }, INT_MIN, INT_MAX, VE, "motion-est" }, - { "hex", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_ME_HEX }, INT_MIN, INT_MAX, VE, "motion-est" }, - { "umh", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_ME_UMH }, INT_MIN, INT_MAX, VE, "motion-est" }, - { "esa", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_ME_ESA }, INT_MIN, INT_MAX, VE, "motion-est" }, - { "tesa", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_ME_TESA }, INT_MIN, INT_MAX, VE, "motion-est" }, + { "me_method", "Set motion estimation method", OFFSET(motion_est), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, X264_ME_TESA, VE, .unit = "motion-est"}, + { "motion-est", "Set motion estimation method", OFFSET(motion_est), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, X264_ME_TESA, VE, .unit = "motion-est"}, + { "dia", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_ME_DIA }, INT_MIN, INT_MAX, VE, .unit = "motion-est" }, + { "hex", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_ME_HEX }, INT_MIN, INT_MAX, VE, .unit = "motion-est" }, + { "umh", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_ME_UMH }, INT_MIN, INT_MAX, VE, .unit = "motion-est" }, + { "esa", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_ME_ESA }, INT_MIN, INT_MAX, VE, .unit = "motion-est" }, + { "tesa", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = X264_ME_TESA }, INT_MIN, INT_MAX, VE, .unit = "motion-est" }, { "forced-idr", "If forcing keyframes, force them as IDR frames.", OFFSET(forced_idr), AV_OPT_TYPE_BOOL, { .i64 = 0 }, -1, 1, VE }, - { "coder", "Coder type", OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE, "coder" }, - { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX, VE, "coder" }, - { "cavlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "coder" }, - { "cabac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "coder" }, - { "vlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "coder" }, - { "ac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "coder" }, + { "coder", "Coder type", OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE, .unit = "coder" }, + { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX, VE, .unit = "coder" }, + { "cavlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, .unit = "coder" }, + { "cabac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "coder" }, + { "vlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, .unit = "coder" }, + { "ac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "coder" }, { "b_strategy", "Strategy to choose between I/P/B-frames", OFFSET(b_frame_strategy), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 2, VE }, { "chromaoffset", "QP difference between chroma and luma", OFFSET(chroma_offset), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, VE }, { "sc_threshold", "Scene change threshold", OFFSET(scenechange_threshold), AV_OPT_TYPE_INT, { .i64 = -1 }, INT_MIN, INT_MAX, VE }, { "noise_reduction", "Noise reduction", OFFSET(noise_reduction), AV_OPT_TYPE_INT, { .i64 = -1 }, INT_MIN, INT_MAX, VE }, { "udu_sei", "Use user data unregistered SEI if available", OFFSET(udu_sei), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "x264-params", "Override the x264 configuration using a :-separated list of key=value parameters", OFFSET(x264_params), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE }, + { "mb_info", "Set mb_info data through AVSideData, only useful when used from the API", OFFSET(mb_info), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { NULL }, }; @@ -1359,12 +1657,14 @@ FFCodec ff_libx264_encoder = { .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE | + AV_CODEC_CAP_ENCODER_FLUSH | AV_CODEC_CAP_ENCODER_RECON_FRAME, .p.priv_class = &x264_class, .p.wrapper_name = "libx264", .priv_data_size = sizeof(X264Context), .init = X264_init, FF_CODEC_ENCODE_CB(X264_frame), + .flush = X264_flush, .close = X264_close, .defaults = x264_defaults, #if X264_BUILD < 153 diff --git a/libavcodec/libx265.c b/libavcodec/libx265.c index 420d0953af1..d3e74eaacfb 100644 --- a/libavcodec/libx265.c +++ b/libavcodec/libx265.c @@ -30,21 +30,17 @@ #include "libavutil/avassert.h" #include "libavutil/buffer.h" #include "libavutil/internal.h" -#include "libavutil/common.h" +#include "libavutil/mastering_display_metadata.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avcodec.h" #include "codec_internal.h" #include "encode.h" -#include "internal.h" #include "packet_internal.h" #include "atsc_a53.h" #include "sei.h" typedef struct ReorderedData { -#if FF_API_REORDERED_OPAQUE - int64_t reordered_opaque; -#endif int64_t duration; void *frame_opaque; @@ -179,6 +175,68 @@ static av_cold int libx265_param_parse_int(AVCodecContext *avctx, return 0; } +static int handle_mdcv(void *logctx, const x265_api *api, + x265_param *params, + const AVMasteringDisplayMetadata *mdcv) +{ + char buf[10 /* # of PRId64s */ * 20 /* max strlen for %PRId64 */ + sizeof("G(,)B(,)R(,)WP(,)L(,)")]; + + // G(%hu,%hu)B(%hu,%hu)R(%hu,%hu)WP(%hu,%hu)L(%u,%u) + snprintf(buf, sizeof(buf), + "G(%"PRId64",%"PRId64")B(%"PRId64",%"PRId64")R(%"PRId64",%"PRId64")" + "WP(%"PRId64",%"PRId64")L(%"PRId64",%"PRId64")", + av_rescale_q(1, mdcv->display_primaries[1][0], (AVRational){ 1, 50000 }), + av_rescale_q(1, mdcv->display_primaries[1][1], (AVRational){ 1, 50000 }), + av_rescale_q(1, mdcv->display_primaries[2][0], (AVRational){ 1, 50000 }), + av_rescale_q(1, mdcv->display_primaries[2][1], (AVRational){ 1, 50000 }), + av_rescale_q(1, mdcv->display_primaries[0][0], (AVRational){ 1, 50000 }), + av_rescale_q(1, mdcv->display_primaries[0][1], (AVRational){ 1, 50000 }), + av_rescale_q(1, mdcv->white_point[0], (AVRational){ 1, 50000 }), + av_rescale_q(1, mdcv->white_point[1], (AVRational){ 1, 50000 }), + av_rescale_q(1, mdcv->max_luminance, (AVRational){ 1, 10000 }), + av_rescale_q(1, mdcv->min_luminance, (AVRational){ 1, 10000 })); + + if (api->param_parse(params, "master-display", buf) == + X265_PARAM_BAD_VALUE) { + av_log(logctx, AV_LOG_ERROR, + "Invalid value \"%s\" for param \"master-display\".\n", + buf); + return AVERROR(EINVAL); + } + + return 0; +} + +static int handle_side_data(AVCodecContext *avctx, const x265_api *api, + x265_param *params) +{ + const AVFrameSideData *cll_sd = + av_frame_side_data_get(avctx->decoded_side_data, + avctx->nb_decoded_side_data, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + const AVFrameSideData *mdcv_sd = + av_frame_side_data_get(avctx->decoded_side_data, + avctx->nb_decoded_side_data, + AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + + if (cll_sd) { + const AVContentLightMetadata *cll = + (AVContentLightMetadata *)cll_sd->data; + + params->maxCLL = cll->MaxCLL; + params->maxFALL = cll->MaxFALL; + } + + if (mdcv_sd) { + int ret = handle_mdcv( + avctx, api, params, + (AVMasteringDisplayMetadata *)mdcv_sd->data); + if (ret < 0) + return ret; + } + + return 0; +} + static av_cold int libx265_encode_init(AVCodecContext *avctx) { libx265Context *ctx = avctx->priv_data; @@ -220,7 +278,13 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) ctx->params->fpsDenom = avctx->framerate.den; } else { ctx->params->fpsNum = avctx->time_base.den; - ctx->params->fpsDenom = avctx->time_base.num * avctx->ticks_per_frame; +FF_DISABLE_DEPRECATION_WARNINGS + ctx->params->fpsDenom = avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } ctx->params->sourceWidth = avctx->width; ctx->params->sourceHeight = avctx->height; @@ -333,6 +397,13 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) return AVERROR_BUG; } + ret = handle_side_data(avctx, ctx->api, ctx->params); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed handling side data! (%s)\n", + av_err2str(ret)); + return ret; + } + if (ctx->crf >= 0) { char crf[6]; @@ -389,7 +460,7 @@ static av_cold int libx265_encode_init(AVCodecContext *avctx) ctx->params->rc.vbvBufferSize = avctx->rc_buffer_size / 1000; ctx->params->rc.vbvMaxBitrate = avctx->rc_max_rate / 1000; - cpb_props = ff_add_cpb_side_data(avctx); + cpb_props = ff_encode_add_cpb_side_data(avctx); if (!cpb_props) return AVERROR(ENOMEM); cpb_props->buffer_size = ctx->params->rc.vbvBufferSize * 1000; @@ -620,11 +691,6 @@ static int libx265_encode_frame(AVCodecContext *avctx, AVPacket *pkt, rd = &ctx->rd[rd_idx]; rd->duration = pic->duration; -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - rd->reordered_opaque = pic->reordered_opaque; -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { rd->frame_opaque = pic->opaque; ret = av_buffer_replace(&rd->frame_opaque_ref, pic->opaque_ref); @@ -762,11 +828,6 @@ FF_ENABLE_DEPRECATION_WARNINGS int idx = (int)(intptr_t)x265pic_out.userData - 1; ReorderedData *rd = &ctx->rd[idx]; -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - avctx->reordered_opaque = rd->reordered_opaque; -FF_ENABLE_DEPRECATION_WARNINGS -#endif pkt->duration = rd->duration; if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { @@ -777,13 +838,6 @@ FF_ENABLE_DEPRECATION_WARNINGS rd_release(ctx, idx); } -#if FF_API_REORDERED_OPAQUE - else { -FF_DISABLE_DEPRECATION_WARNINGS - avctx->reordered_opaque = 0; -FF_ENABLE_DEPRECATION_WARNINGS - } -#endif *got_packet = 1; return 0; diff --git a/libavcodec/libxavs.c b/libavcodec/libxavs.c index 9ed73d1042a..4c7b1908cc4 100644 --- a/libavcodec/libxavs.c +++ b/libavcodec/libxavs.c @@ -141,7 +141,7 @@ static int XAVS_frame(AVCodecContext *avctx, AVPacket *pkt, x4->pic.i_pts = frame->pts; x4->pic.i_type = XAVS_TYPE_AUTO; - x4->pts_buffer[avctx->frame_number % (avctx->max_b_frames+1)] = frame->pts; + x4->pts_buffer[avctx->frame_num % (avctx->max_b_frames+1)] = frame->pts; } if (xavs_encoder_encode(x4->enc, &nal, &nnal, @@ -386,21 +386,21 @@ static const AVOption options[] = { { "qp", "Constant quantization parameter rate control method",OFFSET(cqp), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, VE }, { "b-bias", "Influences how often B-frames are used", OFFSET(b_bias), AV_OPT_TYPE_INT, {.i64 = INT_MIN}, INT_MIN, INT_MAX, VE }, { "cplxblur", "Reduce fluctuations in QP (before curve compression)", OFFSET(cplxblur), AV_OPT_TYPE_FLOAT, {.dbl = -1 }, -1, FLT_MAX, VE}, - { "direct-pred", "Direct MV prediction mode", OFFSET(direct_pred), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, VE, "direct-pred" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_DIRECT_PRED_NONE }, 0, 0, VE, "direct-pred" }, - { "spatial", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_DIRECT_PRED_SPATIAL }, 0, 0, VE, "direct-pred" }, - { "temporal", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_DIRECT_PRED_TEMPORAL }, 0, 0, VE, "direct-pred" }, - { "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_DIRECT_PRED_AUTO }, 0, 0, VE, "direct-pred" }, + { "direct-pred", "Direct MV prediction mode", OFFSET(direct_pred), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, VE, .unit = "direct-pred" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_DIRECT_PRED_NONE }, 0, 0, VE, .unit = "direct-pred" }, + { "spatial", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_DIRECT_PRED_SPATIAL }, 0, 0, VE, .unit = "direct-pred" }, + { "temporal", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_DIRECT_PRED_TEMPORAL }, 0, 0, VE, .unit = "direct-pred" }, + { "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_DIRECT_PRED_AUTO }, 0, 0, VE, .unit = "direct-pred" }, { "aud", "Use access unit delimiters.", OFFSET(aud), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE}, { "mbtree", "Use macroblock tree ratecontrol.", OFFSET(mbtree), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE}, { "mixed-refs", "One reference per partition, as opposed to one reference per macroblock", OFFSET(mixed_refs), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE }, { "fast-pskip", NULL, OFFSET(fast_pskip), AV_OPT_TYPE_BOOL, {.i64 = -1 }, -1, 1, VE}, - { "motion-est", "Set motion estimation method", OFFSET(motion_est), AV_OPT_TYPE_INT, { .i64 = XAVS_ME_DIA }, -1, XAVS_ME_TESA, VE, "motion-est"}, - { "dia", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_ME_DIA }, INT_MIN, INT_MAX, VE, "motion-est" }, - { "hex", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_ME_HEX }, INT_MIN, INT_MAX, VE, "motion-est" }, - { "umh", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_ME_UMH }, INT_MIN, INT_MAX, VE, "motion-est" }, - { "esa", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_ME_ESA }, INT_MIN, INT_MAX, VE, "motion-est" }, - { "tesa", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_ME_TESA }, INT_MIN, INT_MAX, VE, "motion-est" }, + { "motion-est", "Set motion estimation method", OFFSET(motion_est), AV_OPT_TYPE_INT, { .i64 = XAVS_ME_DIA }, -1, XAVS_ME_TESA, VE, .unit = "motion-est"}, + { "dia", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_ME_DIA }, INT_MIN, INT_MAX, VE, .unit = "motion-est" }, + { "hex", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_ME_HEX }, INT_MIN, INT_MAX, VE, .unit = "motion-est" }, + { "umh", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_ME_UMH }, INT_MIN, INT_MAX, VE, .unit = "motion-est" }, + { "esa", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_ME_ESA }, INT_MIN, INT_MAX, VE, .unit = "motion-est" }, + { "tesa", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XAVS_ME_TESA }, INT_MIN, INT_MAX, VE, .unit = "motion-est" }, { "b_strategy", "Strategy to choose between I/P/B-frames", OFFSET(b_frame_strategy), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 2, VE}, { "chromaoffset", "QP difference between chroma and luma", OFFSET(chroma_offset), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, VE}, { "sc_threshold", "Scene change threshold", OFFSET(scenechange_threshold), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, VE}, diff --git a/libavcodec/libxevd.c b/libavcodec/libxevd.c new file mode 100644 index 00000000000..c6c7327e654 --- /dev/null +++ b/libavcodec/libxevd.c @@ -0,0 +1,484 @@ +/* + * libxevd decoder + * EVC (MPEG-5 Essential Video Coding) decoding using XEVD MPEG-5 EVC decoder library + * + * Copyright (C) 2021 Dawid Kozinski + * + * 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 + +#include + +#include "libavutil/internal.h" +#include "libavutil/common.h" +#include "libavutil/pixdesc.h" +#include "libavutil/pixfmt.h" +#include "libavutil/imgutils.h" +#include "libavutil/cpu.h" + +#include "avcodec.h" +#include "codec_internal.h" +#include "profiles.h" +#include "decode.h" + +#define XEVD_PARAM_BAD_NAME -1 +#define XEVD_PARAM_BAD_VALUE -2 + +#define EVC_NAL_HEADER_SIZE 2 /* byte */ + +/** + * The structure stores all the states associated with the instance of Xeve MPEG-5 EVC decoder + */ +typedef struct XevdContext { + XEVD id; // XEVD instance identifier @see xevd.h + XEVD_CDSC cdsc; // decoding parameters @see xevd.h + + // If end of stream occurs it is required "flushing" (aka draining) the codec, + // as the codec might buffer multiple frames or packets internally. + int draining_mode; // The flag is set if codec enters draining mode. + + AVPacket *pkt; // access unit (a set of NAL units that are consecutive in decoding order and containing exactly one encoded image) +} XevdContext; + +/** + * The function populates the XEVD_CDSC structure. + * XEVD_CDSC contains all decoder parameters that should be initialized before its use. + * + * @param[in] avctx codec context + * @param[out] cdsc contains all decoder parameters that should be initialized before its use + * + */ +static void get_conf(AVCodecContext *avctx, XEVD_CDSC *cdsc) +{ + int cpu_count = av_cpu_count(); + + /* clear XEVS_CDSC structure */ + memset(cdsc, 0, sizeof(XEVD_CDSC)); + + /* init XEVD_CDSC */ + if (avctx->thread_count <= 0) + cdsc->threads = (cpu_count < XEVD_MAX_TASK_CNT) ? cpu_count : XEVD_MAX_TASK_CNT; + else if (avctx->thread_count > XEVD_MAX_TASK_CNT) + cdsc->threads = XEVD_MAX_TASK_CNT; + else + cdsc->threads = avctx->thread_count; +} + +/** + * Read NAL unit length + * @param bs input data (bitstream) + * @return the length of NAL unit on success, 0 value on failure + */ +static uint32_t read_nal_unit_length(const uint8_t *bs, int bs_size, AVCodecContext *avctx) +{ + uint32_t len = 0; + XEVD_INFO info; + int ret; + + if (bs_size == XEVD_NAL_UNIT_LENGTH_BYTE) { + ret = xevd_info((void *)bs, XEVD_NAL_UNIT_LENGTH_BYTE, 1, &info); + if (XEVD_FAILED(ret)) { + av_log(avctx, AV_LOG_ERROR, "Cannot get bitstream information\n"); + return 0; + } + len = info.nalu_len; + if (len == 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid bitstream size! [%d]\n", bs_size); + return 0; + } + } + + return len; +} + +/** + * @param[in] xectx the structure that stores all the state associated with the instance of Xeve MPEG-5 EVC decoder + * @param[out] avctx codec context + * @return 0 on success, negative value on failure + */ +static int export_stream_params(const XevdContext *xectx, AVCodecContext *avctx) +{ + int ret; + int size; + int color_space; + + avctx->pix_fmt = AV_PIX_FMT_YUV420P10; + + size = 4; + ret = xevd_config(xectx->id, XEVD_CFG_GET_CODED_WIDTH, &avctx->coded_width, &size); + if (XEVD_FAILED(ret)) { + av_log(avctx, AV_LOG_ERROR, "Failed to get coded_width\n"); + return AVERROR_EXTERNAL; + } + + ret = xevd_config(xectx->id, XEVD_CFG_GET_CODED_HEIGHT, &avctx->coded_height, &size); + if (XEVD_FAILED(ret)) { + av_log(avctx, AV_LOG_ERROR, "Failed to get coded_height\n"); + return AVERROR_EXTERNAL; + } + + ret = xevd_config(xectx->id, XEVD_CFG_GET_WIDTH, &avctx->width, &size); + if (XEVD_FAILED(ret)) { + av_log(avctx, AV_LOG_ERROR, "Failed to get width\n"); + return AVERROR_EXTERNAL; + } + + ret = xevd_config(xectx->id, XEVD_CFG_GET_HEIGHT, &avctx->height, &size); + if (XEVD_FAILED(ret)) { + av_log(avctx, AV_LOG_ERROR, "Failed to get height\n"); + return AVERROR_EXTERNAL; + } + + ret = xevd_config(xectx->id, XEVD_CFG_GET_COLOR_SPACE, &color_space, &size); + if (XEVD_FAILED(ret)) { + av_log(avctx, AV_LOG_ERROR, "Failed to get color_space\n"); + return AVERROR_EXTERNAL; + } + switch(color_space) { + case XEVD_CS_YCBCR400_10LE: + avctx->pix_fmt = AV_PIX_FMT_GRAY10LE; + break; + case XEVD_CS_YCBCR420_10LE: + avctx->pix_fmt = AV_PIX_FMT_YUV420P10LE; + break; + case XEVD_CS_YCBCR422_10LE: + avctx->pix_fmt = AV_PIX_FMT_YUV422P10LE; + break; + case XEVD_CS_YCBCR444_10LE: + avctx->pix_fmt = AV_PIX_FMT_YUV444P10LE; + break; + default: + av_log(avctx, AV_LOG_ERROR, "Unknown color space\n"); + avctx->pix_fmt = AV_PIX_FMT_NONE; + return AVERROR_INVALIDDATA; + } + + // the function returns sps->num_reorder_pics + ret = xevd_config(xectx->id, XEVD_CFG_GET_MAX_CODING_DELAY, &avctx->max_b_frames, &size); + if (XEVD_FAILED(ret)) { + av_log(avctx, AV_LOG_ERROR, "Failed to get max_coding_delay\n"); + return AVERROR_EXTERNAL; + } + + avctx->has_b_frames = (avctx->max_b_frames) ? 1 : 0; + + return 0; +} + +/** + * @brief Copy image in imgb to frame. + * + * @param avctx codec context + * @param[in] imgb + * @param[out] frame + * @return 0 on success, negative value on failure + */ +static int libxevd_image_copy(struct AVCodecContext *avctx, XEVD_IMGB *imgb, struct AVFrame *frame) +{ + int ret; + if (imgb->cs != XEVD_CS_YCBCR420_10LE) { + av_log(avctx, AV_LOG_ERROR, "Not supported pixel format: %s\n", av_get_pix_fmt_name(avctx->pix_fmt)); + return AVERROR_INVALIDDATA; + } + + if (imgb->w[0] != avctx->width || imgb->h[0] != avctx->height) { // stream resolution changed + if (ff_set_dimensions(avctx, imgb->w[0], imgb->h[0]) < 0) { + av_log(avctx, AV_LOG_ERROR, "Cannot set new dimension\n"); + return AVERROR_INVALIDDATA; + } + } + + ret = ff_get_buffer(avctx, frame, 0); + if (ret < 0) + return ret; + + av_image_copy(frame->data, frame->linesize, (const uint8_t **)imgb->a, + imgb->s, avctx->pix_fmt, + imgb->w[0], imgb->h[0]); + + return 0; +} + +/** + * Initialize decoder + * Create a decoder instance and allocate all the needed resources + * + * @param avctx codec context + * @return 0 on success, negative error code on failure + */ +static av_cold int libxevd_init(AVCodecContext *avctx) +{ + XevdContext *xectx = avctx->priv_data; + XEVD_CDSC *cdsc = &(xectx->cdsc); + + /* read configurations and set values for created descriptor (XEVD_CDSC) */ + get_conf(avctx, cdsc); + + /* create decoder */ + xectx->id = xevd_create(&(xectx->cdsc), NULL); + if (xectx->id == NULL) { + av_log(avctx, AV_LOG_ERROR, "Cannot create XEVD encoder\n"); + return AVERROR_EXTERNAL; + } + + xectx->draining_mode = 0; + xectx->pkt = av_packet_alloc(); + if (!xectx->pkt) { + av_log(avctx, AV_LOG_ERROR, "Cannot allocate memory for AVPacket\n"); + return AVERROR(ENOMEM); + } + + return 0; +} + +static int libxevd_return_frame(AVCodecContext *avctx, AVFrame *frame, + XEVD_IMGB *imgb, AVPacket **pkt_au) +{ + AVPacket *pkt_au_imgb = (AVPacket*)imgb->pdata[0]; + int ret; + + if (!pkt_au_imgb) { + av_log(avctx, AV_LOG_ERROR, "Invalid data needed to fill frame properties\n"); + + if (pkt_au) + av_packet_free(pkt_au); + av_frame_unref(frame); + + imgb->release(imgb); + imgb = NULL; + + return AVERROR_INVALIDDATA; + } + + // got frame + ret = libxevd_image_copy(avctx, imgb, frame); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Image copying error\n"); + + av_packet_free(&pkt_au_imgb); + av_frame_unref(frame); + + imgb->release(imgb); + imgb = NULL; + + return ret; + } + + // use ff_decode_frame_props_from_pkt() to fill frame properties + ret = ff_decode_frame_props_from_pkt(avctx, frame, pkt_au_imgb); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "ff_decode_frame_props_from_pkt error\n"); + + av_packet_free(&pkt_au_imgb); + av_frame_unref(frame); + + imgb->release(imgb); + imgb = NULL; + + return ret; + } + + frame->pkt_dts = imgb->ts[XEVD_TS_DTS]; + frame->pts = imgb->ts[XEVD_TS_PTS]; + + av_packet_free(&pkt_au_imgb); + + // xevd_pull uses pool of objects of type XEVD_IMGB. + // The pool size is equal MAX_PB_SIZE (26), so release object when it is no more needed + imgb->release(imgb); + imgb = NULL; + + return 0; +} +/** + * Decode frame with decoupled packet/frame dataflow + * + * @param avctx codec context + * @param[out] frame decoded frame + * + * @return 0 on success, negative error code on failure + */ +static int libxevd_receive_frame(AVCodecContext *avctx, AVFrame *frame) +{ + XevdContext *xectx = avctx->priv_data; + AVPacket *pkt = xectx->pkt; + XEVD_IMGB *imgb = NULL; + + int xevd_ret = 0; + int ret = 0; + + // obtain access unit (input data) - a set of NAL units that are consecutive in decoding order and containing exactly one encoded image + ret = ff_decode_get_packet(avctx, pkt); + if (ret < 0 && ret != AVERROR_EOF) { + av_packet_unref(pkt); + + return ret; + } else if(ret == AVERROR_EOF && xectx->draining_mode == 0) { // End of stream situations. Enter draining mode + + xectx->draining_mode = 1; + av_packet_unref(pkt); + } + + if (pkt->size > 0) { + int bs_read_pos = 0; + XEVD_STAT stat; + XEVD_BITB bitb; + int nalu_size; + AVPacket *pkt_au = av_packet_alloc(); + imgb = NULL; + + if (!pkt_au) { + av_packet_unref(pkt); + return AVERROR(ENOMEM); + } + FFSWAP(AVPacket*, pkt_au, xectx->pkt); + + // get all nal units from AU + while(pkt_au->size > (bs_read_pos + XEVD_NAL_UNIT_LENGTH_BYTE)) { + memset(&stat, 0, sizeof(XEVD_STAT)); + + nalu_size = read_nal_unit_length(pkt_au->data + bs_read_pos, XEVD_NAL_UNIT_LENGTH_BYTE, avctx); + if (nalu_size == 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid bitstream\n"); + av_packet_free(&pkt_au); + ret = AVERROR_INVALIDDATA; + + return ret; + } + bs_read_pos += XEVD_NAL_UNIT_LENGTH_BYTE; + + bitb.addr = pkt_au->data + bs_read_pos; + bitb.ssize = nalu_size; + bitb.pdata[0] = pkt_au; + bitb.ts[XEVD_TS_DTS] = pkt_au->dts; + + /* main decoding block */ + xevd_ret = xevd_decode(xectx->id, &bitb, &stat); + if (XEVD_FAILED(xevd_ret)) { + av_log(avctx, AV_LOG_ERROR, "Failed to decode bitstream\n"); + av_packet_free(&pkt_au); + + return AVERROR_EXTERNAL; + } + + bs_read_pos += nalu_size; + + if (stat.nalu_type == XEVD_NUT_SPS) { // EVC stream parameters changed + if ((ret = export_stream_params(xectx, avctx)) != 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to export stream params\n"); + av_packet_free(&pkt_au); + + return ret; + } + } + + if (stat.read != nalu_size) + av_log(avctx, AV_LOG_INFO, "Different reading of bitstream (in:%d, read:%d)\n,", nalu_size, stat.read); + + // stat.fnum - has negative value if the decoded data is not frame + if (stat.fnum >= 0) { + + xevd_ret = xevd_pull(xectx->id, &imgb); // The function returns a valid image only if the return code is XEVD_OK + + if (XEVD_FAILED(xevd_ret)) { + av_log(avctx, AV_LOG_ERROR, "Failed to pull the decoded image (xevd error code: %d, frame#=%d)\n", xevd_ret, stat.fnum); + + av_packet_free(&pkt_au); + + return AVERROR_EXTERNAL; + } else if (xevd_ret == XEVD_OK_FRM_DELAYED) { + if(bs_read_pos == pkt_au->size) { + return AVERROR(EAGAIN); + } + } else { // XEVD_OK + if (!imgb) { + if(bs_read_pos == pkt_au->size) { + av_log(avctx, AV_LOG_ERROR, "Invalid decoded image data\n"); + + av_packet_free(&pkt_au); + return AVERROR(EAGAIN); + } + } else { + return libxevd_return_frame(avctx, frame, imgb, &pkt_au); + } + } + } + } + } else { // decoder draining mode handling + + xevd_ret = xevd_pull(xectx->id, &imgb); + + if (xevd_ret == XEVD_ERR_UNEXPECTED) { // draining process completed + av_log(avctx, AV_LOG_DEBUG, "Draining process completed\n"); + + return AVERROR_EOF; + } else if (XEVD_FAILED(xevd_ret)) { // handle all other errors + av_log(avctx, AV_LOG_ERROR, "Failed to pull the decoded image (xevd error code: %d)\n", xevd_ret); + + return AVERROR_EXTERNAL; + } else { // XEVD_OK + if (!imgb) { + av_log(avctx, AV_LOG_ERROR, "Invalid decoded image data\n"); + + return AVERROR_EXTERNAL; + } + + return libxevd_return_frame(avctx, frame, imgb, NULL); + } + } + + return ret; +} + +/** + * Destroy decoder + * + * @param avctx codec context + * @return 0 on success + */ +static av_cold int libxevd_close(AVCodecContext *avctx) +{ + XevdContext *xectx = avctx->priv_data; + if (xectx->id) { + xevd_delete(xectx->id); + xectx->id = NULL; + } + + xectx->draining_mode = 0; + av_packet_free(&xectx->pkt); + + return 0; +} + +const FFCodec ff_libxevd_decoder = { + .p.name = "evc", + CODEC_LONG_NAME("EVC / MPEG-5 Essential Video Coding (EVC)"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_EVC, + .init = libxevd_init, + FF_CODEC_RECEIVE_FRAME_CB(libxevd_receive_frame), + .close = libxevd_close, + .priv_data_size = sizeof(XevdContext), + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | + AV_CODEC_CAP_OTHER_THREADS | AV_CODEC_CAP_AVOID_PROBING, + .p.profiles = NULL_IF_CONFIG_SMALL(ff_evc_profiles), + .p.wrapper_name = "libxevd", + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_NOT_INIT_THREADSAFE | + FF_CODEC_CAP_SETS_FRAME_PROPS, +}; diff --git a/libavcodec/libxeve.c b/libavcodec/libxeve.c new file mode 100644 index 00000000000..c923ac1cac9 --- /dev/null +++ b/libavcodec/libxeve.c @@ -0,0 +1,616 @@ +/* + * libxeve encoder + * EVC (MPEG-5 Essential Video Coding) encoding using XEVE MPEG-5 EVC encoder library + * + * Copyright (C) 2021 Dawid Kozinski + * + * 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 +#include + +#include + +#include "libavutil/internal.h" +#include "libavutil/common.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "libavutil/pixfmt.h" +#include "libavutil/time.h" +#include "libavutil/cpu.h" +#include "libavutil/avstring.h" + +#include "avcodec.h" +#include "internal.h" +#include "packet_internal.h" +#include "codec_internal.h" +#include "profiles.h" +#include "encode.h" + +#define MAX_BS_BUF (16*1024*1024) + +/** + * Error codes + */ +#define XEVE_PARAM_BAD_NAME -100 +#define XEVE_PARAM_BAD_VALUE -200 + +/** + * Encoder states + * + * STATE_ENCODING - the encoder receives and processes input frames + * STATE_BUMPING - there are no more input frames, however the encoder still processes previously received data + */ +typedef enum State { + STATE_ENCODING, + STATE_BUMPING, +} State; + +/** + * The structure stores all the states associated with the instance of Xeve MPEG-5 EVC encoder + */ +typedef struct XeveContext { + const AVClass *class; + + XEVE id; // XEVE instance identifier + XEVE_CDSC cdsc; // coding parameters i.e profile, width & height of input frame, num of therads, frame rate ... + XEVE_BITB bitb; // bitstream buffer (output) + XEVE_STAT stat; // encoding status (output) + XEVE_IMGB imgb; // image buffer (input) + + State state; // encoder state (skipping, encoding, bumping) + + int profile_id; // encoder profile (main, baseline) + int preset_id; // preset of xeve ( fast, medium, slow, placebo) + int tune_id; // tune of xeve (psnr, zerolatency) + + // variables for rate control modes + int rc_mode; // Rate control mode [ 0(CQP) / 1(ABR) / 2(CRF) ] + int qp; // quantization parameter (QP) [0,51] + int crf; // constant rate factor (CRF) [10,49] + + int hash; // embed picture signature (HASH) for conformance checking in decoding + int sei_info; // embed Supplemental enhancement information while encoding + + int color_format; // input data color format: currently only XEVE_CF_YCBCR420 is supported + + AVDictionary *xeve_params; +} XeveContext; + +/** + * Convert FFmpeg pixel format (AVPixelFormat) to XEVE pre-defined color format + * + * @param[in] av_pix_fmt pixel format (@see https://ffmpeg.org/doxygen/trunk/pixfmt_8h.html#a9a8e335cf3be472042bc9f0cf80cd4c5) + * @param[out] xeve_col_fmt XEVE pre-defined color format (@see xeve.h) + * + * @return 0 on success, negative value on failure + */ +static int libxeve_color_fmt(enum AVPixelFormat av_pix_fmt, int *xeve_col_fmt) +{ + switch (av_pix_fmt) { + case AV_PIX_FMT_YUV420P: + *xeve_col_fmt = XEVE_CF_YCBCR420; + break; + case AV_PIX_FMT_YUV420P10: + *xeve_col_fmt = XEVE_CF_YCBCR420; + break; + default: + *xeve_col_fmt = XEVE_CF_UNKNOWN; + return AVERROR_INVALIDDATA; + } + + return 0; +} + +/** + * Convert FFmpeg pixel format (AVPixelFormat) into XEVE pre-defined color space + * + * @param[in] px_fmt pixel format (@see https://ffmpeg.org/doxygen/trunk/pixfmt_8h.html#a9a8e335cf3be472042bc9f0cf80cd4c5) + * + * @return XEVE pre-defined color space (@see xeve.h) on success, XEVE_CF_UNKNOWN on failure + */ +static int libxeve_color_space(enum AVPixelFormat av_pix_fmt) +{ + /* color space of input image */ + int cs = XEVE_CF_UNKNOWN; + + switch (av_pix_fmt) { + case AV_PIX_FMT_YUV420P: + cs = XEVE_CS_YCBCR420; + break; + case AV_PIX_FMT_YUV420P10: +#if AV_HAVE_BIGENDIAN + cs = XEVE_CS_SET(XEVE_CF_YCBCR420, 10, 1); +#else + cs = XEVE_CS_YCBCR420_10LE; +#endif + + break; + default: + cs = XEVE_CF_UNKNOWN; + break; + } + + return cs; +} + +/** + * The function returns a pointer to the object of the XEVE_CDSC type. + * XEVE_CDSC contains all encoder parameters that should be initialized before the encoder is used. + * + * The field values of the XEVE_CDSC structure are populated based on: + * - the corresponding field values of the AvCodecConetxt structure, + * - the xeve encoder specific option values, + * (the full list of options available for xeve encoder is displayed after executing the command ./ffmpeg --help encoder = libxeve) + * + * The order of processing input data and populating the XEVE_CDSC structure + * 1) first, the fields of the AVCodecContext structure corresponding to the provided input options are processed, + * (i.e -pix_fmt yuv420p -s:v 1920x1080 -r 30 -profile:v 0) + * 2) then xeve-specific options added as AVOption to the xeve AVCodec implementation + * (i.e -preset 0) + * + * Keep in mind that, there are options that can be set in different ways. + * In this case, please follow the above-mentioned order of processing. + * The most recent assignments overwrite the previous values. + * + * @param[in] avctx codec context (AVCodecContext) + * @param[out] cdsc contains all Xeve MPEG-5 EVC encoder encoder parameters that should be initialized before the encoder is use + * + * @return 0 on success, negative error code on failure + */ +static int get_conf(AVCodecContext *avctx, XEVE_CDSC *cdsc) +{ + XeveContext *xectx = NULL; + int ret; + + xectx = avctx->priv_data; + + /* initialize xeve_param struct with default values */ + ret = xeve_param_default(&cdsc->param); + if (XEVE_FAILED(ret)) { + av_log(avctx, AV_LOG_ERROR, "Cannot set_default parameter\n"); + return AVERROR_EXTERNAL; + } + + /* read options from AVCodecContext */ + if (avctx->width > 0) + cdsc->param.w = avctx->width; + + if (avctx->height > 0) + cdsc->param.h = avctx->height; + + if (avctx->framerate.num > 0) { + // fps can be float number, but xeve API doesn't support it + cdsc->param.fps = lrintf(av_q2d(avctx->framerate)); + } + + // GOP size (key-frame interval, I-picture period) + cdsc->param.keyint = avctx->gop_size; // 0: only one I-frame at the first time; 1: every frame is coded in I-frame + + if (avctx->max_b_frames == 0 || avctx->max_b_frames == 1 || avctx->max_b_frames == 3 || + avctx->max_b_frames == 7 || avctx->max_b_frames == 15) // number of b-frames + cdsc->param.bframes = avctx->max_b_frames; + else { + av_log(avctx, AV_LOG_ERROR, "Incorrect value for maximum number of B frames: (%d) \n" + "Acceptable values for bf option (maximum number of B frames) are 0,1,3,7 or 15\n", avctx->max_b_frames); + return AVERROR_INVALIDDATA; + } + + cdsc->param.level_idc = avctx->level; + + if (avctx->rc_buffer_size) // VBV buf size + cdsc->param.vbv_bufsize = (int)(avctx->rc_buffer_size / 1000); + + cdsc->param.rc_type = xectx->rc_mode; + + if (xectx->rc_mode == XEVE_RC_CQP) + cdsc->param.qp = xectx->qp; + else if (xectx->rc_mode == XEVE_RC_ABR) { + if (avctx->bit_rate / 1000 > INT_MAX || avctx->rc_max_rate / 1000 > INT_MAX) { + av_log(avctx, AV_LOG_ERROR, "Not supported bitrate bit_rate and rc_max_rate > %d000\n", INT_MAX); + return AVERROR_INVALIDDATA; + } + cdsc->param.bitrate = (int)(avctx->bit_rate / 1000); + } else if (xectx->rc_mode == XEVE_RC_CRF) + cdsc->param.crf = xectx->crf; + else { + av_log(avctx, AV_LOG_ERROR, "Not supported rate control type: %d\n", xectx->rc_mode); + return AVERROR_INVALIDDATA; + } + + if (avctx->thread_count <= 0) { + int cpu_count = av_cpu_count(); + cdsc->param.threads = (cpu_count < XEVE_MAX_THREADS) ? cpu_count : XEVE_MAX_THREADS; + } else if (avctx->thread_count > XEVE_MAX_THREADS) + cdsc->param.threads = XEVE_MAX_THREADS; + else + cdsc->param.threads = avctx->thread_count; + + + libxeve_color_fmt(avctx->pix_fmt, &xectx->color_format); + + cdsc->param.cs = XEVE_CS_SET(xectx->color_format, cdsc->param.codec_bit_depth, AV_HAVE_BIGENDIAN); + + cdsc->max_bs_buf_size = MAX_BS_BUF; + + ret = xeve_param_ppt(&cdsc->param, xectx->profile_id, xectx->preset_id, xectx->tune_id); + if (XEVE_FAILED(ret)) { + av_log(avctx, AV_LOG_ERROR, "Cannot set profile(%d), preset(%d), tune(%d)\n", xectx->profile_id, xectx->preset_id, xectx->tune_id); + return AVERROR_EXTERNAL; + } + + return 0; +} + +/** + * Set XEVE_CFG_SET_USE_PIC_SIGNATURE for encoder + * + * @param[in] logger context + * @param[in] id XEVE encodec instance identifier + * @param[in] ctx the structure stores all the states associated with the instance of Xeve MPEG-5 EVC encoder + * + * @return 0 on success, negative error code on failure + */ +static int set_extra_config(AVCodecContext *avctx, XEVE id, XeveContext *ctx) +{ + int ret, size; + size = 4; + + // embed SEI messages identifying encoder parameters and command line arguments + // - 0: off\n" + // - 1: emit sei info" + // + // SEI - Supplemental enhancement information contains information + // that is not necessary to decode the samples of coded pictures from VCL NAL units. + // Some SEI message information is required to check bitstream conformance + // and for output timing decoder conformance. + // @see ISO_IEC_23094-1_2020 7.4.3.5 + // @see ISO_IEC_23094-1_2020 Annex D + ret = xeve_config(id, XEVE_CFG_SET_SEI_CMD, &ctx->sei_info, &size); // sei_cmd_info + if (XEVE_FAILED(ret)) { + av_log(avctx, AV_LOG_ERROR, "Failed to set config for sei command info messages\n"); + return AVERROR_EXTERNAL; + } + + ret = xeve_config(id, XEVE_CFG_SET_USE_PIC_SIGNATURE, &ctx->hash, &size); + if (XEVE_FAILED(ret)) { + av_log(avctx, AV_LOG_ERROR, "Failed to set config for picture signature\n"); + return AVERROR_EXTERNAL; + } + + return 0; +} + +/** + * @brief Switch encoder to bumping mode + * + * @param id XEVE encodec instance identifier + * @return 0 on success, negative error code on failure + */ +static int setup_bumping(XEVE id) +{ + int val = 1; + int size = sizeof(int); + if (XEVE_FAILED(xeve_config(id, XEVE_CFG_SET_FORCE_OUT, (void *)(&val), &size))) + return AVERROR_EXTERNAL; + + return 0; +} + +/** + * @brief Initialize eXtra-fast Essential Video Encoder codec + * Create an encoder instance and allocate all the needed resources + * + * @param avctx codec context + * @return 0 on success, negative error code on failure + */ +static av_cold int libxeve_init(AVCodecContext *avctx) +{ + XeveContext *xectx = avctx->priv_data; + unsigned char *bs_buf = NULL; + int i; + int shift_h = 0; + int shift_v = 0; + int width_chroma = 0; + int height_chroma = 0; + XEVE_IMGB *imgb = NULL; + int ret = 0; + + XEVE_CDSC *cdsc = &(xectx->cdsc); + + /* allocate bitstream buffer */ + bs_buf = av_malloc(MAX_BS_BUF); + if (bs_buf == NULL) { + av_log(avctx, AV_LOG_ERROR, "Cannot allocate bitstream buffer\n"); + return AVERROR(ENOMEM); + } + xectx->bitb.addr = bs_buf; + xectx->bitb.bsize = MAX_BS_BUF; + + /* read configurations and set values for created descriptor (XEVE_CDSC) */ + if ((ret = get_conf(avctx, cdsc)) != 0) { + av_log(avctx, AV_LOG_ERROR, "Cannot get configuration\n"); + return AVERROR(EINVAL); + } + + if ((ret = xeve_param_check(&cdsc->param)) != 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid configuration\n"); + return AVERROR(EINVAL); + } + + { + const AVDictionaryEntry *en = NULL; + while (en = av_dict_iterate(xectx->xeve_params, en)) { + if ((ret = xeve_param_parse(&cdsc->param, en->key, en->value)) < 0) { + av_log(avctx, AV_LOG_WARNING, + "Error parsing option '%s = %s'.\n", + en->key, en->value); + } + } + } + + /* create encoder */ + xectx->id = xeve_create(cdsc, NULL); + if (xectx->id == NULL) { + av_log(avctx, AV_LOG_ERROR, "Cannot create XEVE encoder\n"); + return AVERROR_EXTERNAL; + } + + if ((ret = set_extra_config(avctx, xectx->id, xectx)) != 0) { + av_log(avctx, AV_LOG_ERROR, "Cannot set extra configuration\n"); + return AVERROR(EINVAL); + } + + if ((ret = av_pix_fmt_get_chroma_sub_sample(avctx->pix_fmt, &shift_h, &shift_v)) != 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to get chroma shift\n"); + return AVERROR(EINVAL); + } + + // Chroma subsampling + // + // YUV format explanation + // shift_h == 1 && shift_v == 1 : YUV420 + // shift_h == 1 && shift_v == 0 : YUV422 + // shift_h == 0 && shift_v == 0 : YUV444 + // + width_chroma = AV_CEIL_RSHIFT(avctx->width, shift_h); + height_chroma = AV_CEIL_RSHIFT(avctx->height, shift_v); + + /* set default values for input image buffer */ + imgb = &xectx->imgb; + imgb->cs = libxeve_color_space(avctx->pix_fmt); + imgb->np = 3; /* only for yuv420p, yuv420ple */ + + for (i = 0; i < imgb->np; i++) + imgb->x[i] = imgb->y[i] = 0; + + imgb->w[0] = imgb->aw[0] = avctx->width; // width luma + imgb->w[1] = imgb->w[2] = imgb->aw[1] = imgb->aw[2] = width_chroma; + imgb->h[0] = imgb->ah[0] = avctx->height; // height luma + imgb->h[1] = imgb->h[2] = imgb->ah[1] = imgb->ah[2] = height_chroma; + + xectx->state = STATE_ENCODING; + + return 0; +} + +/** + * Encode raw data frame into EVC packet + * + * @param[in] avctx codec context + * @param[out] avpkt output AVPacket containing encoded data + * @param[in] frame AVFrame containing the raw data to be encoded + * @param[out] got_packet encoder sets to 0 or 1 to indicate that a + * non-empty packet was returned in pkt + * + * @return 0 on success, negative error code on failure + */ +static int libxeve_encode(AVCodecContext *avctx, AVPacket *avpkt, + const AVFrame *frame, int *got_packet) +{ + XeveContext *xectx = avctx->priv_data; + int ret = -1; + + // No more input frames are available but encoder still can have some data in its internal buffer to process + // and some frames to dump. + if (xectx->state == STATE_ENCODING && frame == NULL) { + if (setup_bumping(xectx->id) == 0) + xectx->state = STATE_BUMPING; // Entering bumping process + else { + av_log(avctx, AV_LOG_ERROR, "Failed to setup bumping\n"); + return 0; + } + } + + if (xectx->state == STATE_ENCODING) { + int i; + XEVE_IMGB *imgb = NULL; + + imgb = &xectx->imgb; + + for (i = 0; i < imgb->np; i++) { + imgb->a[i] = frame->data[i]; + imgb->s[i] = frame->linesize[i]; + } + + imgb->ts[XEVE_TS_PTS] = frame->pts; + + /* push image to encoder */ + ret = xeve_push(xectx->id, imgb); + if (XEVE_FAILED(ret)) { + av_log(avctx, AV_LOG_ERROR, "xeve_push() failed\n"); + return AVERROR_EXTERNAL; + } + } + if (xectx->state == STATE_ENCODING || xectx->state == STATE_BUMPING) { + /* encoding */ + ret = xeve_encode(xectx->id, &(xectx->bitb), &(xectx->stat)); + if (XEVE_FAILED(ret)) { + av_log(avctx, AV_LOG_ERROR, "xeve_encode() failed\n"); + return AVERROR_EXTERNAL; + } + + /* store bitstream */ + if (ret == XEVE_OK_OUT_NOT_AVAILABLE) { // Return OK but picture is not available yet + *got_packet = 0; + return 0; + } else if (ret == XEVE_OK) { + int av_pic_type; + + if (xectx->stat.write > 0) { + + ret = ff_get_encode_buffer(avctx, avpkt, xectx->stat.write, 0); + if (ret < 0) + return ret; + + memcpy(avpkt->data, xectx->bitb.addr, xectx->stat.write); + + avpkt->time_base.num = 1; + avpkt->time_base.den = xectx->cdsc.param.fps; + + avpkt->pts = xectx->bitb.ts[XEVE_TS_PTS]; + avpkt->dts = xectx->bitb.ts[XEVE_TS_DTS]; + + switch(xectx->stat.stype) { + case XEVE_ST_I: + av_pic_type = AV_PICTURE_TYPE_I; + avpkt->flags |= AV_PKT_FLAG_KEY; + break; + case XEVE_ST_P: + av_pic_type = AV_PICTURE_TYPE_P; + break; + case XEVE_ST_B: + av_pic_type = AV_PICTURE_TYPE_B; + break; + case XEVE_ST_UNKNOWN: + av_log(avctx, AV_LOG_ERROR, "Unknown slice type\n"); + return AVERROR_INVALIDDATA; + } + + ff_side_data_set_encoder_stats(avpkt, xectx->stat.qp * FF_QP2LAMBDA, NULL, 0, av_pic_type); + + *got_packet = 1; + } + } else if (ret == XEVE_OK_NO_MORE_FRM) { + // Return OK but no more frames + return 0; + } else { + av_log(avctx, AV_LOG_ERROR, "Invalid return value: %d\n", ret); + return AVERROR_EXTERNAL; + } + } else { + av_log(avctx, AV_LOG_ERROR, "Udefined encoder state\n"); + return AVERROR_INVALIDDATA; + } + + return 0; +} + +/** + * Destroy the encoder and release all the allocated resources + * + * @param avctx codec context + * @return 0 on success, negative error code on failure + */ +static av_cold int libxeve_close(AVCodecContext *avctx) +{ + XeveContext *xectx = avctx->priv_data; + + if (xectx->id) { + xeve_delete(xectx->id); + xectx->id = NULL; + } + + av_free(xectx->bitb.addr); /* release bitstream buffer */ + + return 0; +} + +#define OFFSET(x) offsetof(XeveContext, x) +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM + +static const enum AVPixelFormat supported_pixel_formats[] = { + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUV420P10, + AV_PIX_FMT_NONE +}; + +// Consider using following options (./ffmpeg --help encoder=libxeve) +// +static const AVOption libxeve_options[] = { + { "preset", "Encoding preset for setting encoding speed", OFFSET(preset_id), AV_OPT_TYPE_INT, { .i64 = XEVE_PRESET_MEDIUM }, XEVE_PRESET_DEFAULT, XEVE_PRESET_PLACEBO, VE, .unit = "preset" }, + { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PRESET_DEFAULT }, INT_MIN, INT_MAX, VE, .unit = "preset" }, + { "fast", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PRESET_FAST }, INT_MIN, INT_MAX, VE, .unit = "preset" }, + { "medium", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PRESET_MEDIUM }, INT_MIN, INT_MAX, VE, .unit = "preset" }, + { "slow", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PRESET_SLOW }, INT_MIN, INT_MAX, VE, .unit = "preset" }, + { "placebo", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PRESET_PLACEBO }, INT_MIN, INT_MAX, VE, .unit = "preset" }, + { "tune", "Tuning parameter for special purpose operation", OFFSET(tune_id), AV_OPT_TYPE_INT, { .i64 = XEVE_TUNE_NONE }, XEVE_TUNE_NONE, XEVE_TUNE_PSNR, VE, .unit = "tune"}, + { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_TUNE_NONE }, INT_MIN, INT_MAX, VE, .unit = "tune" }, + { "zerolatency", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_TUNE_ZEROLATENCY }, INT_MIN, INT_MAX, VE, .unit = "tune" }, + { "psnr", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_TUNE_PSNR }, INT_MIN, INT_MAX, VE, .unit = "tune" }, + { "profile", "Encoding profile", OFFSET(profile_id), AV_OPT_TYPE_INT, { .i64 = XEVE_PROFILE_BASELINE }, XEVE_PROFILE_BASELINE, XEVE_PROFILE_MAIN, VE, .unit = "profile" }, + { "baseline", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PROFILE_BASELINE }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "main", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_PROFILE_MAIN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "rc_mode", "Rate control mode", OFFSET(rc_mode), AV_OPT_TYPE_INT, { .i64 = XEVE_RC_CQP }, XEVE_RC_CQP, XEVE_RC_CRF, VE, .unit = "rc_mode" }, + { "CQP", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_RC_CQP }, INT_MIN, INT_MAX, VE, .unit = "rc_mode" }, + { "ABR", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_RC_ABR }, INT_MIN, INT_MAX, VE, .unit = "rc_mode" }, + { "CRF", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = XEVE_RC_CRF }, INT_MIN, INT_MAX, VE, .unit = "rc_mode" }, + { "qp", "Quantization parameter value for CQP rate control mode", OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 32 }, 0, 51, VE }, + { "crf", "Constant rate factor value for CRF rate control mode", OFFSET(crf), AV_OPT_TYPE_INT, { .i64 = 32 }, 10, 49, VE }, + { "hash", "Embed picture signature (HASH) for conformance checking in decoding", OFFSET(hash), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, + { "sei_info", "Embed SEI messages identifying encoder parameters and command line arguments", OFFSET(sei_info), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, + { "xeve-params", "Override the xeve configuration using a :-separated list of key=value parameters", OFFSET(xeve_params), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE }, + { NULL } +}; + +static const AVClass libxeve_class = { + .class_name = "libxeve", + .item_name = av_default_item_name, + .option = libxeve_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +/** + * libavcodec generic global options, which can be set on all the encoders and decoders + * @see https://www.ffmpeg.org/ffmpeg-codecs.html#Codec-Options + */ +static const FFCodecDefault libxeve_defaults[] = { + { "b", "0" }, // bitrate in terms of kilo-bits per second + { "g", "0" }, // gop_size (key-frame interval 0: only one I-frame at the first time; 1: every frame is coded in I-frame) + { "bf", "15"}, // maximum number of B frames (0: no B-frames, 1,3,7,15) + { "threads", "0"}, // number of threads to be used (0: automatically select the number of threads to set) + { NULL }, +}; + +const FFCodec ff_libxeve_encoder = { + .p.name = "libxeve", + .p.long_name = NULL_IF_CONFIG_SMALL("libxeve MPEG-5 EVC"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_EVC, + .init = libxeve_init, + FF_CODEC_ENCODE_CB(libxeve_encode), + .close = libxeve_close, + .priv_data_size = sizeof(XeveContext), + .p.priv_class = &libxeve_class, + .defaults = libxeve_defaults, + .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS | AV_CODEC_CAP_DR1, + .p.profiles = NULL_IF_CONFIG_SMALL(ff_evc_profiles), + .p.wrapper_name = "libxeve", + .p.pix_fmts = supported_pixel_formats, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | FF_CODEC_CAP_NOT_INIT_THREADSAFE, +}; diff --git a/libavcodec/libxvid.c b/libavcodec/libxvid.c index aba875b6b85..b9ac39429de 100644 --- a/libavcodec/libxvid.c +++ b/libavcodec/libxvid.c @@ -739,7 +739,7 @@ static int xvid_encode_frame(AVCodecContext *avctx, AVPacket *pkt, xvid_enc_frame_t xvid_enc_frame = { 0 }; xvid_enc_stats_t xvid_enc_stats = { 0 }; - if ((ret = ff_alloc_packet(avctx, pkt, mb_width*(int64_t)mb_height*MAX_MB_BYTES + AV_INPUT_BUFFER_MIN_SIZE)) < 0) + if ((ret = ff_alloc_packet(avctx, pkt, mb_width*(int64_t)mb_height*MAX_MB_BYTES + FF_INPUT_BUFFER_MIN_SIZE)) < 0) return ret; /* Start setting up the frame */ @@ -879,10 +879,10 @@ static av_cold int xvid_encode_close(AVCodecContext *avctx) static const AVOption options[] = { { "lumi_aq", "Luminance masking AQ", OFFSET(lumi_aq), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, { "variance_aq", "Variance AQ", OFFSET(variance_aq), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, - { "ssim", "Show SSIM information to stdout", OFFSET(ssim), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, VE, "ssim" }, - { "off", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "ssim" }, - { "avg", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "ssim" }, - { "frame", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, INT_MIN, INT_MAX, VE, "ssim" }, + { "ssim", "Show SSIM information to stdout", OFFSET(ssim), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, VE, .unit = "ssim" }, + { "off", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, .unit = "ssim" }, + { "avg", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "ssim" }, + { "frame", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, INT_MIN, INT_MAX, VE, .unit = "ssim" }, { "ssim_acc", "SSIM accuracy", OFFSET(ssim_acc), AV_OPT_TYPE_INT, { .i64 = 2 }, 0, 4, VE }, { "gmc", "use GMC", OFFSET(gmc), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, { "me_quality", "Motion estimation quality", OFFSET(me_quality), AV_OPT_TYPE_INT, { .i64 = 4 }, 0, 6, VE }, diff --git a/libavcodec/libzvbi-teletextdec.c b/libavcodec/libzvbi-teletextdec.c index 45e30eb01c9..c2cbb0a4241 100644 --- a/libavcodec/libzvbi-teletextdec.c +++ b/libavcodec/libzvbi-teletextdec.c @@ -791,10 +791,10 @@ static const AVOption options[] = { {"txt_page", "page numbers to decode, subtitle for subtitles, * for all", OFFSET(pgno), AV_OPT_TYPE_STRING, {.str = "*"}, 0, 0, SD}, {"txt_default_region", "default G0 character set used for decoding", OFFSET(default_region), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 87, SD}, {"txt_chop_top", "discards the top teletext line", OFFSET(chop_top), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, SD}, - {"txt_format", "format of the subtitles (bitmap or text or ass)", OFFSET(format_id), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 2, SD, "txt_format"}, - {"bitmap", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, SD, "txt_format"}, - {"text", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 0, SD, "txt_format"}, - {"ass", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 0, SD, "txt_format"}, + {"txt_format", "format of the subtitles (bitmap or text or ass)", OFFSET(format_id), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 2, SD, .unit = "txt_format"}, + {"bitmap", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, SD, .unit = "txt_format"}, + {"text", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 0, SD, .unit = "txt_format"}, + {"ass", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 0, SD, .unit = "txt_format"}, {"txt_left", "x offset of generated bitmaps", OFFSET(x_offset), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 65535, SD}, {"txt_top", "y offset of generated bitmaps", OFFSET(y_offset), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 65535, SD}, {"txt_chop_spaces", "chops leading and trailing spaces from text", OFFSET(chop_spaces), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, SD}, diff --git a/libavcodec/ljpegenc.c b/libavcodec/ljpegenc.c index aa62beac71e..46546e21603 100644 --- a/libavcodec/ljpegenc.c +++ b/libavcodec/ljpegenc.c @@ -214,7 +214,7 @@ static int ljpeg_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const int height = avctx->height; const int mb_width = (width + s->hsample[0] - 1) / s->hsample[0]; const int mb_height = (height + s->vsample[0] - 1) / s->vsample[0]; - size_t max_pkt_size = AV_INPUT_BUFFER_MIN_SIZE; + size_t max_pkt_size = FF_INPUT_BUFFER_MIN_SIZE; int ret, header_bits; if( avctx->pix_fmt == AV_PIX_FMT_BGR0 @@ -296,10 +296,10 @@ static av_cold int ljpeg_encode_init(AVCodecContext *avctx) #define OFFSET(x) offsetof(LJpegEncContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { -{ "pred", "Prediction method", OFFSET(pred), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 3, VE, "pred" }, - { "left", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "pred" }, - { "plane", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, INT_MIN, INT_MAX, VE, "pred" }, - { "median", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3 }, INT_MIN, INT_MAX, VE, "pred" }, +{ "pred", "Prediction method", OFFSET(pred), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 3, VE, .unit = "pred" }, + { "left", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "plane", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "median", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3 }, INT_MIN, INT_MAX, VE, .unit = "pred" }, { NULL}, }; diff --git a/libavcodec/loco.c b/libavcodec/loco.c index d57a67317a0..8cc270acbb6 100644 --- a/libavcodec/loco.c +++ b/libavcodec/loco.c @@ -92,10 +92,15 @@ static inline int loco_get_rice(RICEContext *r) if (get_bits_left(&r->gb) < 1) return INT_MIN; v = get_ur_golomb_jpegls(&r->gb, loco_get_rice_param(r), INT_MAX, 0); + if (v == -1) + return INT_MIN; loco_update_rice_param(r, (v + 1) >> 1); if (!v) { if (r->save >= 0) { - r->run = get_ur_golomb_jpegls(&r->gb, 2, INT_MAX, 0); + int run = get_ur_golomb_jpegls(&r->gb, 2, INT_MAX, 0); + if (run == -1) + return INT_MIN; + r->run = run; if (r->run > 1) r->save += r->run + 1; else @@ -152,6 +157,8 @@ static int loco_decode_plane(LOCOContext *l, uint8_t *data, int width, int heigh /* restore top left pixel */ val = loco_get_rice(&rc); + if (val == INT_MIN) + return AVERROR_INVALIDDATA; data[0] = 128 + val; /* restore top line */ for (i = 1; i < width; i++) { @@ -206,7 +213,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; #define ADVANCE_BY_DECODED do { \ if (decoded < 0 || decoded >= buf_size) goto buf_too_small; \ diff --git a/libavcodec/loongarch/Makefile b/libavcodec/loongarch/Makefile index c1b5de5c443..07da2964e4e 100644 --- a/libavcodec/loongarch/Makefile +++ b/libavcodec/loongarch/Makefile @@ -9,12 +9,9 @@ OBJS-$(CONFIG_HPELDSP) += loongarch/hpeldsp_init_loongarch.o OBJS-$(CONFIG_IDCTDSP) += loongarch/idctdsp_init_loongarch.o OBJS-$(CONFIG_VIDEODSP) += loongarch/videodsp_init.o OBJS-$(CONFIG_HEVC_DECODER) += loongarch/hevcdsp_init_loongarch.o -LASX-OBJS-$(CONFIG_H264CHROMA) += loongarch/h264chroma_lasx.o LASX-OBJS-$(CONFIG_H264QPEL) += loongarch/h264qpel_lasx.o LASX-OBJS-$(CONFIG_H264DSP) += loongarch/h264dsp_lasx.o \ - loongarch/h264idct_lasx.o \ loongarch/h264_deblock_lasx.o -LASX-OBJS-$(CONFIG_H264PRED) += loongarch/h264_intrapred_lasx.o LASX-OBJS-$(CONFIG_VC1_DECODER) += loongarch/vc1dsp_lasx.o LASX-OBJS-$(CONFIG_HPELDSP) += loongarch/hpeldsp_lasx.o LASX-OBJS-$(CONFIG_IDCTDSP) += loongarch/simple_idct_lasx.o \ @@ -30,4 +27,14 @@ LSX-OBJS-$(CONFIG_HEVC_DECODER) += loongarch/hevcdsp_lsx.o \ loongarch/hevc_lpf_sao_lsx.o \ loongarch/hevc_mc_bi_lsx.o \ loongarch/hevc_mc_uni_lsx.o \ - loongarch/hevc_mc_uniw_lsx.o + loongarch/hevc_mc_uniw_lsx.o \ + loongarch/hevc_add_res.o \ + loongarch/hevc_mc.o \ + loongarch/hevc_idct.o +LSX-OBJS-$(CONFIG_H264DSP) += loongarch/h264idct.o \ + loongarch/h264idct_loongarch.o \ + loongarch/h264dsp.o +LSX-OBJS-$(CONFIG_H264QPEL) += loongarch/h264qpel.o \ + loongarch/h264qpel_lsx.o +LSX-OBJS-$(CONFIG_H264CHROMA) += loongarch/h264chroma.o +LSX-OBJS-$(CONFIG_H264PRED) += loongarch/h264intrapred.o diff --git a/libavcodec/loongarch/h264_deblock_lasx.c b/libavcodec/loongarch/h264_deblock_lasx.c index c89bea9a846..eead931dcf5 100644 --- a/libavcodec/loongarch/h264_deblock_lasx.c +++ b/libavcodec/loongarch/h264_deblock_lasx.c @@ -20,7 +20,7 @@ */ #include "libavcodec/bit_depth_template.c" -#include "h264dsp_lasx.h" +#include "h264dsp_loongarch.h" #include "libavutil/loongarch/loongson_intrinsics.h" #define H264_LOOP_FILTER_STRENGTH_ITERATION_LASX(edges, step, mask_mv, dir, \ diff --git a/libavcodec/loongarch/h264_intrapred_init_loongarch.c b/libavcodec/loongarch/h264_intrapred_init_loongarch.c index 12620bd842b..c415fa30dab 100644 --- a/libavcodec/loongarch/h264_intrapred_init_loongarch.c +++ b/libavcodec/loongarch/h264_intrapred_init_loongarch.c @@ -21,7 +21,7 @@ #include "libavutil/loongarch/cpu.h" #include "libavcodec/h264pred.h" -#include "h264_intrapred_lasx.h" +#include "h264_intrapred_loongarch.h" av_cold void ff_h264_pred_init_loongarch(H264PredContext *h, int codec_id, const int bit_depth, @@ -30,6 +30,22 @@ av_cold void ff_h264_pred_init_loongarch(H264PredContext *h, int codec_id, int cpu_flags = av_get_cpu_flags(); if (bit_depth == 8) { + if (have_lsx(cpu_flags)) { + if (chroma_format_idc <= 1) { + } + if (codec_id == AV_CODEC_ID_VP7 || codec_id == AV_CODEC_ID_VP8) { + } else { + if (chroma_format_idc <= 1) { + } + if (codec_id == AV_CODEC_ID_SVQ3) { + h->pred16x16[PLANE_PRED8x8] = ff_h264_pred16x16_plane_svq3_8_lsx; + } else if (codec_id == AV_CODEC_ID_RV40) { + h->pred16x16[PLANE_PRED8x8] = ff_h264_pred16x16_plane_rv40_8_lsx; + } else { + h->pred16x16[PLANE_PRED8x8] = ff_h264_pred16x16_plane_h264_8_lsx; + } + } + } if (have_lasx(cpu_flags)) { if (chroma_format_idc <= 1) { } diff --git a/libavcodec/loongarch/h264_intrapred_lasx.c b/libavcodec/loongarch/h264_intrapred_lasx.c deleted file mode 100644 index c38cd611b84..00000000000 --- a/libavcodec/loongarch/h264_intrapred_lasx.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - * Contributed by Hao Chen - * - * 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/loongarch/loongson_intrinsics.h" -#include "h264_intrapred_lasx.h" - -#define PRED16X16_PLANE \ - ptrdiff_t stride_1, stride_2, stride_3, stride_4, stride_5, stride_6; \ - ptrdiff_t stride_8, stride_15; \ - int32_t res0, res1, res2, res3, cnt; \ - uint8_t *src0, *src1; \ - __m256i reg0, reg1, reg2, reg3, reg4; \ - __m256i tmp0, tmp1, tmp2, tmp3; \ - __m256i shuff = {0x0B040A0509060807, 0x0F000E010D020C03, 0, 0}; \ - __m256i mult = {0x0004000300020001, 0x0008000700060005, 0, 0}; \ - __m256i int_mult1 = {0x0000000100000000, 0x0000000300000002, \ - 0x0000000500000004, 0x0000000700000006}; \ - \ - stride_1 = -stride; \ - stride_2 = stride << 1; \ - stride_3 = stride_2 + stride; \ - stride_4 = stride_2 << 1; \ - stride_5 = stride_4 + stride; \ - stride_6 = stride_3 << 1; \ - stride_8 = stride_4 << 1; \ - stride_15 = (stride_8 << 1) - stride; \ - src0 = src - 1; \ - src1 = src0 + stride_8; \ - \ - reg0 = __lasx_xvldx(src0, -stride); \ - reg1 = __lasx_xvldx(src, (8 - stride)); \ - reg0 = __lasx_xvilvl_d(reg1, reg0); \ - reg0 = __lasx_xvshuf_b(reg0, reg0, shuff); \ - reg0 = __lasx_xvhsubw_hu_bu(reg0, reg0); \ - reg0 = __lasx_xvmul_h(reg0, mult); \ - res1 = (src1[0] - src0[stride_6]) + \ - 2 * (src1[stride] - src0[stride_5]) + \ - 3 * (src1[stride_2] - src0[stride_4]) + \ - 4 * (src1[stride_3] - src0[stride_3]) + \ - 5 * (src1[stride_4] - src0[stride_2]) + \ - 6 * (src1[stride_5] - src0[stride]) + \ - 7 * (src1[stride_6] - src0[0]) + \ - 8 * (src0[stride_15] - src0[stride_1]); \ - reg0 = __lasx_xvhaddw_w_h(reg0, reg0); \ - reg0 = __lasx_xvhaddw_d_w(reg0, reg0); \ - reg0 = __lasx_xvhaddw_q_d(reg0, reg0); \ - res0 = __lasx_xvpickve2gr_w(reg0, 0); \ - -#define PRED16X16_PLANE_END \ - res2 = (src0[stride_15] + src[15 - stride] + 1) << 4; \ - res3 = 7 * (res0 + res1); \ - res2 -= res3; \ - reg0 = __lasx_xvreplgr2vr_w(res0); \ - reg1 = __lasx_xvreplgr2vr_w(res1); \ - reg2 = __lasx_xvreplgr2vr_w(res2); \ - reg3 = __lasx_xvmul_w(reg0, int_mult1); \ - reg4 = __lasx_xvslli_w(reg0, 3); \ - reg4 = __lasx_xvadd_w(reg4, reg3); \ - for (cnt = 8; cnt--;) { \ - tmp0 = __lasx_xvadd_w(reg2, reg3); \ - tmp1 = __lasx_xvadd_w(reg2, reg4); \ - tmp0 = __lasx_xvssrani_hu_w(tmp1, tmp0, 5); \ - tmp0 = __lasx_xvpermi_d(tmp0, 0xD8); \ - reg2 = __lasx_xvadd_w(reg2, reg1); \ - tmp2 = __lasx_xvadd_w(reg2, reg3); \ - tmp3 = __lasx_xvadd_w(reg2, reg4); \ - tmp1 = __lasx_xvssrani_hu_w(tmp3, tmp2, 5); \ - tmp1 = __lasx_xvpermi_d(tmp1, 0xD8); \ - tmp0 = __lasx_xvssrani_bu_h(tmp1, tmp0, 0); \ - reg2 = __lasx_xvadd_w(reg2, reg1); \ - __lasx_xvstelm_d(tmp0, src, 0, 0); \ - __lasx_xvstelm_d(tmp0, src, 8, 2); \ - src += stride; \ - __lasx_xvstelm_d(tmp0, src, 0, 1); \ - __lasx_xvstelm_d(tmp0, src, 8, 3); \ - src += stride; \ - } - - -void ff_h264_pred16x16_plane_h264_8_lasx(uint8_t *src, ptrdiff_t stride) -{ - PRED16X16_PLANE - res0 = (5 * res0 + 32) >> 6; - res1 = (5 * res1 + 32) >> 6; - PRED16X16_PLANE_END -} - -void ff_h264_pred16x16_plane_rv40_8_lasx(uint8_t *src, ptrdiff_t stride) -{ - PRED16X16_PLANE - res0 = (res0 + (res0 >> 2)) >> 4; - res1 = (res1 + (res1 >> 2)) >> 4; - PRED16X16_PLANE_END -} - -void ff_h264_pred16x16_plane_svq3_8_lasx(uint8_t *src, ptrdiff_t stride) -{ - PRED16X16_PLANE - cnt = (5 * (res0/4)) / 16; - res0 = (5 * (res1/4)) / 16; - res1 = cnt; - PRED16X16_PLANE_END -} diff --git a/libavcodec/loongarch/h264_intrapred_lasx.h b/libavcodec/loongarch/h264_intrapred_loongarch.h similarity index 70% rename from libavcodec/loongarch/h264_intrapred_lasx.h rename to libavcodec/loongarch/h264_intrapred_loongarch.h index 0c2653300c6..39be87ee9fb 100644 --- a/libavcodec/loongarch/h264_intrapred_lasx.h +++ b/libavcodec/loongarch/h264_intrapred_loongarch.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Loongson Technology Corporation Limited + * Copyright (c) 2023 Loongson Technology Corporation Limited * Contributed by Hao Chen * * This file is part of FFmpeg. @@ -19,13 +19,17 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVCODEC_LOONGARCH_H264_INTRAPRED_LASX_H -#define AVCODEC_LOONGARCH_H264_INTRAPRED_LASX_H +#ifndef AVCODEC_LOONGARCH_H264_INTRAPRED_LOONGARCH_H +#define AVCODEC_LOONGARCH_H264_INTRAPRED_LOONGARCH_H #include "libavcodec/avcodec.h" +void ff_h264_pred16x16_plane_h264_8_lsx(uint8_t *src, ptrdiff_t stride); +void ff_h264_pred16x16_plane_rv40_8_lsx(uint8_t *src, ptrdiff_t stride); +void ff_h264_pred16x16_plane_svq3_8_lsx(uint8_t *src, ptrdiff_t stride); + void ff_h264_pred16x16_plane_h264_8_lasx(uint8_t *src, ptrdiff_t stride); void ff_h264_pred16x16_plane_rv40_8_lasx(uint8_t *src, ptrdiff_t stride); void ff_h264_pred16x16_plane_svq3_8_lasx(uint8_t *src, ptrdiff_t stride); -#endif // #ifndef AVCODEC_LOONGARCH_H264_INTRAPRED_LASX_H +#endif // #ifndef AVCODEC_LOONGARCH_H264_INTRAPRED_LOONGARCH_H diff --git a/libavcodec/loongarch/h264chroma.S b/libavcodec/loongarch/h264chroma.S new file mode 100644 index 00000000000..353b8d004bf --- /dev/null +++ b/libavcodec/loongarch/h264chroma.S @@ -0,0 +1,966 @@ +/* + * Loongson LSX/LASX optimized h264chroma + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Lu Wang + * + * 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 "loongson_asm.S" + +/* void ff_put_h264_chroma_mc8_lsx(uint8_t *dst, uint8_t *src, ptrdiff_t stride, + int h, int x, int y) */ +function ff_put_h264_chroma_mc8_lsx + li.d t8, 8 + sub.d t1, t8, a4 // 8-x + sub.d t2, t8, a5 // 8-y + mul.d t3, t1, t2 // A + mul.d t4, a4, t2 // B + mul.d t5, t1, a5 // C + mul.d t6, a4, a5 // D + add.d t0, t4, t5 // E + vreplgr2vr.b vr0, t3 + vreplgr2vr.b vr1, t4 + vreplgr2vr.b vr2, t5 + vreplgr2vr.b vr3, t6 + vreplgr2vr.b vr4, t0 + slli.d t2, a2, 1 + add.d t3, t2, a2 + slli.d t4, a2, 2 + + bge zero, t6, .ENDLOOP_D + move t1, a3 + vilvl.b vr9, vr1, vr0 + vilvl.b vr10, vr3, vr2 +.LOOP_D: + vld vr5, a1, 0 + vld vr6, a1, 1 + add.d a1, a1, a2 + vld vr7, a1, 0 + vld vr8, a1, 1 + vilvl.b vr11, vr6, vr5 + vilvl.b vr12, vr8, vr7 + vmulwev.h.bu vr13, vr9, vr11 + vmaddwod.h.bu vr13, vr9, vr11 + vmulwev.h.bu vr14, vr10, vr12 + vmaddwod.h.bu vr14, vr10, vr12 + vadd.h vr13, vr13, vr14 + vsrarni.b.h vr13, vr13, 6 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vld vr6, a1, 1 + vilvl.b vr11, vr8, vr7 + vilvl.b vr12, vr6, vr5 + vmulwev.h.bu vr13, vr9, vr11 + vmaddwod.h.bu vr13, vr9, vr11 + vmulwev.h.bu vr14, vr10, vr12 + vmaddwod.h.bu vr14, vr10, vr12 + vadd.h vr13, vr13, vr14 + vsrarni.b.h vr13, vr13, 6 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr7, a1, 0 + vld vr8, a1, 1 + vilvl.b vr11, vr6, vr5 + vilvl.b vr12, vr8, vr7 + vmulwev.h.bu vr13, vr9, vr11 + vmaddwod.h.bu vr13, vr9, vr11 + vmulwev.h.bu vr14, vr10, vr12 + vmaddwod.h.bu vr14, vr10, vr12 + vadd.h vr13, vr13, vr14 + vsrarni.b.h vr13, vr13, 6 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vld vr6, a1, 1 + vilvl.b vr11, vr8, vr7 + vilvl.b vr12, vr6, vr5 + vmulwev.h.bu vr13, vr9, vr11 + vmaddwod.h.bu vr13, vr9, vr11 + vmulwev.h.bu vr14, vr10, vr12 + vmaddwod.h.bu vr14, vr10, vr12 + vadd.h vr13, vr13, vr14 + vsrarni.b.h vr13, vr13, 6 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOP_D + b .ENDLOOP +.ENDLOOP_D: + + bge zero, t0, .ENDLOOP_E + move t1, a3 + li.d t7, 1 + slt t8, zero, t5 + maskeqz t5, a2, t8 + masknez t7, t7, t8 + or t7, t7, t5 + vilvl.b vr7, vr4, vr0 +.LOOP_E: + vld vr5, a1, 0 + vldx vr6, a1, t7 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrarni.b.h vr6, vr6, 6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vldx vr6, a1, t7 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrarni.b.h vr6, vr6, 6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vldx vr6, a1, t7 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrarni.b.h vr6, vr6, 6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vldx vr6, a1, t7 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrarni.b.h vr6, vr6, 6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOP_E + b .ENDLOOP +.ENDLOOP_E: + + move t1, a3 +.LOOP: + vld vr5, a1, 0 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vsrarni.b.h vr6, vr6, 6 + vsrarni.b.h vr7, vr7, 6 + vilvl.b vr6, vr7, vr6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + vldx vr5, a1, a2 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vsrarni.b.h vr6, vr6, 6 + vsrarni.b.h vr7, vr7, 6 + vilvl.b vr6, vr7, vr6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + vldx vr5, a1, t2 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vsrarni.b.h vr6, vr6, 6 + vsrarni.b.h vr7, vr7, 6 + vilvl.b vr6, vr7, vr6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + vldx vr5, a1, t3 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vsrarni.b.h vr6, vr6, 6 + vsrarni.b.h vr7, vr7, 6 + vilvl.b vr6, vr7, vr6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, t4 + + addi.d t1, t1, -4 + blt zero, t1, .LOOP +.ENDLOOP: +endfunc + +/* void ff_avg_h264_chroma_mc8_lsx(uint8_t *dst, uint8_t *src, ptrdiff_t stride, + int h, int x, int y) */ +function ff_avg_h264_chroma_mc8_lsx + li.d t8, 8 + sub.d t1, t8, a4 // 8-x + sub.d t2, t8, a5 // 8-y + mul.d t3, t1, t2 // A + mul.d t4, a4, t2 // B + mul.d t5, t1, a5 // C + mul.d t6, a4, a5 // D + add.d t0, t4, t5 // E + vreplgr2vr.b vr0, t3 + vreplgr2vr.b vr1, t4 + vreplgr2vr.b vr2, t5 + vreplgr2vr.b vr3, t6 + vreplgr2vr.b vr4, t0 + slli.d t2, a2, 1 + add.d t3, t2, a2 + slli.d t4, a2, 2 + + bge zero, t6, .ENDLOOPD + move t1, a3 + vilvl.b vr9, vr1, vr0 + vilvl.b vr10, vr3, vr2 +.LOOPD: + vld vr5, a1, 0 + vld vr6, a1, 1 + add.d a1, a1, a2 + vld vr7, a1, 0 + vld vr8, a1, 1 + vld vr11, a0, 0 + vilvl.b vr12, vr6, vr5 + vilvl.b vr13, vr8, vr7 + vmulwev.h.bu vr14, vr9, vr12 + vmaddwod.h.bu vr14, vr9, vr12 + vmulwev.h.bu vr15, vr10, vr13 + vmaddwod.h.bu vr15, vr10, vr13 + vadd.h vr14, vr14, vr15 + vsrari.h vr14, vr14, 6 + vsllwil.hu.bu vr11, vr11, 0 + vadd.h vr11, vr14, vr11 + vsrarni.b.h vr11, vr11, 1 + vstelm.d vr11, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vld vr6, a1, 1 + vld vr11, a0, 0 + vilvl.b vr12, vr8, vr7 + vilvl.b vr13, vr6, vr5 + vmulwev.h.bu vr14, vr9, vr12 + vmaddwod.h.bu vr14, vr9, vr12 + vmulwev.h.bu vr15, vr10, vr13 + vmaddwod.h.bu vr15, vr10, vr13 + vadd.h vr14, vr14, vr15 + vsrari.h vr14, vr14, 6 + vsllwil.hu.bu vr11, vr11, 0 + vadd.h vr11, vr14, vr11 + vsrarni.b.h vr11, vr11, 1 + vstelm.d vr11, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr7, a1, 0 + vld vr8, a1, 1 + vld vr11, a0, 0 + vilvl.b vr12, vr6, vr5 + vilvl.b vr13, vr8, vr7 + vmulwev.h.bu vr14, vr9, vr12 + vmaddwod.h.bu vr14, vr9, vr12 + vmulwev.h.bu vr15, vr10, vr13 + vmaddwod.h.bu vr15, vr10, vr13 + vadd.h vr14, vr14, vr15 + vsrari.h vr14, vr14, 6 + vsllwil.hu.bu vr11, vr11, 0 + vadd.h vr11, vr14, vr11 + vsrarni.b.h vr11, vr11, 1 + vstelm.d vr11, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vld vr6, a1, 1 + vld vr11, a0, 0 + vilvl.b vr12, vr8, vr7 + vilvl.b vr13, vr6, vr5 + vmulwev.h.bu vr14, vr9, vr12 + vmaddwod.h.bu vr14, vr9, vr12 + vmulwev.h.bu vr15, vr10, vr13 + vmaddwod.h.bu vr15, vr10, vr13 + vadd.h vr14, vr14, vr15 + vsrari.h vr14, vr14, 6 + vsllwil.hu.bu vr11, vr11, 0 + vadd.h vr11, vr14, vr11 + vsrarni.b.h vr11, vr11, 1 + vstelm.d vr11, a0, 0, 0 + add.d a0, a0, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPD + b .ENDLOOPELSE +.ENDLOOPD: + + bge zero, t0, .ENDLOOPE + move t1, a3 + li.d t7, 1 + slt t8, zero, t5 + maskeqz t5, a2, t8 + masknez t7, t7, t8 + or t7, t7, t5 + vilvl.b vr7, vr4, vr0 +.LOOPE: + vld vr5, a1, 0 + vldx vr6, a1, t7 + vld vr8, a0, 0 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vldx vr6, a1, t7 + vld vr8, a0, 0 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vldx vr6, a1, t7 + vld vr8, a0, 0 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + vld vr5, a1, 0 + vldx vr6, a1, t7 + vld vr8, a0, 0 + vilvl.b vr5, vr6, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPE + b .ENDLOOPELSE +.ENDLOOPE: + + move t1, a3 +.LOOPELSE: + vld vr5, a1, 0 + vld vr8, a0, 0 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vilvl.h vr6, vr7, vr6 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vldx vr5, a1, a2 + vld vr8, a0, 0 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vilvl.h vr6, vr7, vr6 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vldx vr5, a1, t2 + vld vr8, a0, 0 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vilvl.h vr6, vr7, vr6 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vldx vr5, a1, t3 + vld vr8, a0, 0 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vilvl.h vr6, vr7, vr6 + vsrari.h vr6, vr6, 6 + vsllwil.hu.bu vr8, vr8, 0 + vadd.h vr8, vr6, vr8 + vsrarni.b.h vr8, vr8, 1 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + add.d a1, a1, t4 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPELSE +.ENDLOOPELSE: +endfunc + +/* void ff_put_h264_chroma_mc4_lsx(uint8_t *dst, uint8_t *src, ptrdiff_t stride, + int h, int x, int y) */ +function ff_put_h264_chroma_mc4_lsx + li.d t8, 8 + sub.d t1, t8, a4 // 8-x + sub.d t2, t8, a5 // 8-y + mul.d t3, t1, t2 // A + mul.d t4, a4, t2 // B + mul.d t5, t1, a5 // C + mul.d t6, a4, a5 // D + add.d t0, t4, t5 // E + slli.d t8, a2, 1 + vreplgr2vr.b vr0, t3 + vreplgr2vr.b vr1, t4 + vreplgr2vr.b vr2, t5 + vreplgr2vr.b vr3, t6 + vreplgr2vr.b vr4, t0 + + bge zero, t6, .ENDPUT_D + move t1, a3 + vilvl.b vr9, vr1, vr0 + vilvl.b vr10, vr3, vr2 +.PUT_D: + vld vr5, a1, 0 + vld vr6, a1, 1 + add.d a1, a1, a2 + vld vr7, a1, 0 + vld vr8, a1, 1 + add.d a1, a1, a2 + vld vr11, a1, 0 + vld vr12, a1, 1 + vilvl.b vr5, vr6, vr5 + vilvl.b vr7, vr8, vr7 + vilvl.b vr13, vr12, vr11 + vilvl.d vr5, vr7, vr5 + vilvl.d vr13, vr13, vr7 + vmulwev.h.bu vr14, vr9, vr5 + vmaddwod.h.bu vr14, vr9, vr5 + vmulwev.h.bu vr15, vr10, vr13 + vmaddwod.h.bu vr15, vr10, vr13 + vadd.h vr14, vr14, vr15 + vsrarni.b.h vr14, vr14, 6 + vstelm.w vr14, a0, 0, 0 + add.d a0, a0, a2 + vstelm.w vr14, a0, 0, 1 + add.d a0, a0, a2 + addi.d t1, t1, -2 + blt zero, t1, .PUT_D + b .ENDPUT +.ENDPUT_D: + + bge zero, t0, .ENDPUT_E + move t1, a3 + li.d t7, 1 + slt t8, zero, t5 + maskeqz t5, a2, t8 + masknez t7, t7, t8 + or t7, t7, t5 + vilvl.b vr7, vr4, vr0 +.PUT_E: + vld vr5, a1, 0 + vldx vr6, a1, t7 + vilvl.b vr5, vr6, vr5 + add.d a1, a1, a2 + vld vr8, a1, 0 + vldx vr9, a1, t7 + vilvl.b vr8, vr9, vr8 + vilvl.d vr5, vr8, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrarni.b.h vr6, vr6, 6 + vstelm.w vr6, a0, 0, 0 + add.d a0, a0, a2 + vstelm.w vr6, a0, 0, 1 + add.d a0, a0, a2 + add.d a1, a1, a2 + addi.d t1, t1, -2 + blt zero, t1, .PUT_E + b .ENDPUT +.ENDPUT_E: + + move t1, a3 +.PUT: + vld vr5, a1, 0 + vldx vr8, a1, a2 + vilvl.w vr5, vr8, vr5 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vsrarni.b.h vr6, vr6, 6 + vsrarni.b.h vr7, vr7, 6 + vilvl.b vr6, vr7, vr6 + vstelm.w vr6, a0, 0, 0 + add.d a0, a0, a2 + vstelm.w vr6, a0, 0, 1 + add.d a0, a0, a2 + add.d a1, a1, t8 + addi.d t1, t1, -2 + blt zero, t1, .PUT +.ENDPUT: +endfunc + +/* void ff_put_h264_chroma_mc8_lasx(uint8_t *dst, uint8_t *src, ptrdiff_t stride, + int h, int x, int y) */ +function ff_put_h264_chroma_mc8_lasx + li.d t8, 8 + sub.d t1, t8, a4 // 8-x + sub.d t2, t8, a5 // 8-y + mul.d t3, t1, t2 // A + mul.d t4, a4, t2 // B + mul.d t5, t1, a5 // C + mul.d t6, a4, a5 // D + add.d t0, t4, t5 // E + xvreplgr2vr.b xr0, t3 + xvreplgr2vr.b xr1, t4 + xvreplgr2vr.b xr2, t5 + xvreplgr2vr.b xr3, t6 + xvreplgr2vr.b xr4, t0 + slli.d t2, a2, 1 + add.d t3, t2, a2 + slli.d t4, a2, 2 + + bge zero, t6, .ENDLOOP_DA + move t1, a3 + xvilvl.b xr9, xr1, xr0 + xvilvl.b xr10, xr3, xr2 +.LOOP_DA: + fld.d f5, a1, 0 + fld.d f6, a1, 1 + add.d a1, a1, a2 + fld.d f7, a1, 0 + fld.d f8, a1, 1 + add.d a1, a1, a2 + fld.d f13, a1, 0 + fld.d f14, a1, 1 + add.d a1, a1, a2 + fld.d f15, a1, 0 + fld.d f16, a1, 1 + add.d a1, a1, a2 + fld.d f17, a1, 0 + fld.d f18, a1, 1 + vilvl.b vr11, vr6, vr5 + vilvl.b vr12, vr8, vr7 + vilvl.b vr14, vr14, vr13 + vilvl.b vr15, vr16, vr15 + vilvl.b vr16, vr18, vr17 + xvpermi.q xr11, xr12, 0x02 + xvpermi.q xr12, xr14, 0x02 + xvpermi.q xr14, xr15, 0x02 + xvpermi.q xr15, xr16, 0x02 + + xvmulwev.h.bu xr19, xr9, xr11 + xvmaddwod.h.bu xr19, xr9, xr11 + xvmulwev.h.bu xr20, xr10, xr12 + xvmaddwod.h.bu xr20, xr10, xr12 + xvadd.h xr21, xr19, xr20 + xvsrarni.b.h xr21, xr21, 6 + vstelm.d vr21, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr21, a0, 0, 2 + add.d a0, a0, a2 + xvmulwev.h.bu xr13, xr9, xr14 + xvmaddwod.h.bu xr13, xr9, xr14 + xvmulwev.h.bu xr14, xr10, xr15 + xvmaddwod.h.bu xr14, xr10, xr15 + xvadd.h xr13, xr13, xr14 + xvsrarni.b.h xr13, xr13, 6 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr13, a0, 0, 2 + add.d a0, a0, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOP_DA + b .ENDLOOPA +.ENDLOOP_DA: + + bge zero, t0, .ENDLOOP_EA + move t1, a3 + li.d t7, 1 + slt t8, zero, t5 + maskeqz t5, a2, t8 + masknez t7, t7, t8 + or t7, t7, t5 + xvilvl.b xr7, xr4, xr0 +.LOOP_EA: + fld.d f5, a1, 0 + fldx.d f6, a1, t7 + add.d a1, a1, a2 + fld.d f9, a1, 0 + fldx.d f10, a1, t7 + add.d a1, a1, a2 + fld.d f11, a1, 0 + fldx.d f12, a1, t7 + add.d a1, a1, a2 + fld.d f13, a1, 0 + fldx.d f14, a1, t7 + vilvl.b vr5, vr6, vr5 + vilvl.b vr9, vr10, vr9 + vilvl.b vr11, vr12, vr11 + vilvl.b vr13, vr14, vr13 + xvpermi.q xr5, xr9, 0x02 + xvpermi.q xr11, xr13, 0x02 + + xvmulwev.h.bu xr8, xr7, xr5 + xvmaddwod.h.bu xr8, xr7, xr5 + xvmulwev.h.bu xr6, xr7, xr11 + xvmaddwod.h.bu xr6, xr7, xr11 + xvsrarni.b.h xr8, xr8, 6 + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr8, a0, 0, 2 + add.d a0, a0, a2 + xvsrarni.b.h xr6, xr6, 6 + vstelm.d vr6, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr6, a0, 0, 2 + add.d a0, a0, a2 + add.d a1, a1, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOP_EA + b .ENDLOOPA +.ENDLOOP_EA: + + move t1, a3 +.LOOPA: + fld.d f5, a1, 0 + fldx.d f6, a1, a2 + fldx.d f7, a1, t2 + fldx.d f8, a1, t3 + vilvl.d vr5, vr6, vr5 + vilvl.d vr7, vr8, vr7 + xvpermi.q xr5, xr7, 0x02 + xvmulwev.h.bu xr6, xr0, xr5 + xvmulwod.h.bu xr7, xr0, xr5 + xvilvl.h xr8, xr7, xr6 + xvilvh.h xr9, xr7, xr6 + xvsrarni.b.h xr9, xr8, 6 + vstelm.d vr9, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr9, a0, 0, 1 + add.d a0, a0, a2 + xvstelm.d xr9, a0, 0, 2 + add.d a0, a0, a2 + xvstelm.d xr9, a0, 0, 3 + add.d a0, a0, a2 + add.d a1, a1, t4 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPA +.ENDLOOPA: +endfunc + +/* void ff_avg_h264_chroma_mc8_lasx(uint8_t *dst, uint8_t *src, ptrdiff_t stride, + int h, int x, int y) */ +function ff_avg_h264_chroma_mc8_lasx + li.d t8, 8 + sub.d t1, t8, a4 // 8-x + sub.d t2, t8, a5 // 8-y + mul.d t3, t1, t2 // A + mul.d t4, a4, t2 // B + mul.d t5, t1, a5 // C + mul.d t6, a4, a5 // D + add.d t0, t4, t5 // E + xvreplgr2vr.b xr0, t3 + xvreplgr2vr.b xr1, t4 + xvreplgr2vr.b xr2, t5 + xvreplgr2vr.b xr3, t6 + xvreplgr2vr.b xr4, t0 + slli.d t2, a2, 1 + add.d t3, t2, a2 + slli.d t4, a2, 2 + + bge zero, t6, .ENDLOOPDA + move t1, a3 + xvilvl.b xr9, xr1, xr0 + xvilvl.b xr10, xr3, xr2 +.LOOPDA: + fld.d f5, a1, 0 + fld.d f6, a1, 1 + add.d a1, a1, a2 + fld.d f7, a1, 0 + fld.d f8, a1, 1 + add.d a1, a1, a2 + fld.d f11, a1, 0 + fld.d f12, a1, 1 + add.d a1, a1, a2 + fld.d f13, a1, 0 + fld.d f14, a1, 1 + add.d a1, a1, a2 + fld.d f15, a1, 0 + fld.d f16, a1, 1 + fld.d f17, a0, 0 + fldx.d f18, a0, a2 + fldx.d f19, a0, t2 + fldx.d f20, a0, t3 + vilvl.b vr5, vr6, vr5 + vilvl.b vr7, vr8, vr7 + vilvl.b vr11, vr12, vr11 + vilvl.b vr13, vr14, vr13 + vilvl.b vr16, vr16, vr15 + xvpermi.q xr5, xr7, 0x02 + xvpermi.q xr7, xr11, 0x02 + xvpermi.q xr11, xr13, 0x02 + xvpermi.q xr13, xr16, 0x02 + xvpermi.q xr17, xr18, 0x02 + xvpermi.q xr19, xr20, 0x02 + + xvmulwev.h.bu xr14, xr9, xr5 + xvmaddwod.h.bu xr14, xr9, xr5 + xvmulwev.h.bu xr15, xr10, xr7 + xvmaddwod.h.bu xr15, xr10, xr7 + xvadd.h xr14, xr14, xr15 + xvsrari.h xr14, xr14, 6 + xvsllwil.hu.bu xr17, xr17, 0 + xvadd.h xr20, xr14, xr17 + xvsrarni.b.h xr20, xr20, 1 + xvstelm.d xr20, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr20, a0, 0, 2 + add.d a0, a0, a2 + xvmulwev.h.bu xr14, xr9, xr11 + xvmaddwod.h.bu xr14, xr9, xr11 + xvmulwev.h.bu xr15, xr10, xr13 + xvmaddwod.h.bu xr15, xr10, xr13 + xvadd.h xr14, xr14, xr15 + xvsrari.h xr14, xr14, 6 + xvsllwil.hu.bu xr19, xr19, 0 + xvadd.h xr21, xr14, xr19 + xvsrarni.b.h xr21, xr21, 1 + xvstelm.d xr21, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr21, a0, 0, 2 + add.d a0, a0, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPDA + b .ENDLOOPELSEA +.ENDLOOPDA: + + bge zero, t0, .ENDLOOPEA + move t1, a3 + li.d t7, 1 + slt t8, zero, t5 + maskeqz t5, a2, t8 + masknez t7, t7, t8 + or t7, t7, t5 + xvilvl.b xr7, xr4, xr0 +.LOOPEA: + fld.d f5, a1, 0 + fldx.d f6, a1, t7 + add.d a1, a1, a2 + fld.d f8, a1, 0 + fldx.d f9, a1, t7 + add.d a1, a1, a2 + fld.d f10, a1, 0 + fldx.d f11, a1, t7 + add.d a1, a1, a2 + fld.d f12, a1, 0 + fldx.d f13, a1, t7 + add.d a1, a1, a2 + fld.d f14, a0, 0 + fldx.d f15, a0, a2 + fldx.d f16, a0, t2 + fldx.d f17, a0, t3 + vilvl.b vr5, vr6, vr5 + vilvl.b vr8, vr9, vr8 + vilvl.b vr10, vr11, vr10 + vilvl.b vr12, vr13, vr12 + xvpermi.q xr5, xr8, 0x02 + xvpermi.q xr10, xr12, 0x02 + xvpermi.q xr14, xr15, 0x02 + xvpermi.q xr16, xr17, 0x02 + + xvmulwev.h.bu xr6, xr7, xr5 + xvmaddwod.h.bu xr6, xr7, xr5 + xvsrari.h xr6, xr6, 6 + xvsllwil.hu.bu xr14, xr14, 0 + xvadd.h xr8, xr6, xr14 + xvsrarni.b.h xr8, xr8, 1 + xvstelm.d xr8, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr8, a0, 0, 2 + add.d a0, a0, a2 + xvmulwev.h.bu xr6, xr7, xr10 + xvmaddwod.h.bu xr6, xr7, xr10 + xvsrari.h xr6, xr6, 6 + xvsllwil.hu.bu xr16, xr16, 0 + xvadd.h xr8, xr6, xr16 + xvsrarni.b.h xr8, xr8, 1 + xvstelm.d xr8, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr8, a0, 0, 2 + add.d a0, a0, a2 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPEA + b .ENDLOOPELSEA +.ENDLOOPEA: + + move t1, a3 +.LOOPELSEA: + fld.d f5, a1, 0 + fldx.d f6, a1, a2 + fldx.d f7, a1, t2 + fldx.d f8, a1, t3 + fld.d f9, a0, 0 + fldx.d f10, a0, a2 + fldx.d f11, a0, t2 + fldx.d f12, a0, t3 + xvpermi.q xr5, xr6, 0x02 + xvpermi.q xr7, xr8, 0x02 + xvpermi.q xr9, xr10, 0x02 + xvpermi.q xr11, xr12, 0x02 + + xvmulwev.h.bu xr12, xr0, xr5 + xvmulwod.h.bu xr13, xr0, xr5 + xvilvl.h xr12, xr13, xr12 + xvsrari.h xr12, xr12, 6 + xvsllwil.hu.bu xr9, xr9, 0 + xvadd.h xr9, xr12, xr9 + xvsrarni.b.h xr9, xr9, 1 + xvstelm.d xr9, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr9, a0, 0, 2 + add.d a0, a0, a2 + xvmulwev.h.bu xr12, xr0, xr7 + xvmulwod.h.bu xr13, xr0, xr7 + xvilvl.h xr12, xr13, xr12 + xvsrari.h xr12, xr12, 6 + xvsllwil.hu.bu xr11, xr11, 0 + xvadd.h xr13, xr12, xr11 + xvsrarni.b.h xr13, xr13, 1 + xvstelm.d xr13, a0, 0, 0 + add.d a0, a0, a2 + xvstelm.d xr13, a0, 0, 2 + add.d a0, a0, a2 + add.d a1, a1, t4 + + addi.d t1, t1, -4 + blt zero, t1, .LOOPELSEA +.ENDLOOPELSEA: +endfunc + +/* void ff_put_h264_chroma_mc4_lasx(uint8_t *dst, uint8_t *src, ptrdiff_t stride, + int h, int x, int y) */ +function ff_put_h264_chroma_mc4_lasx + li.d t8, 8 + sub.d t1, t8, a4 // 8-x + sub.d t2, t8, a5 // 8-y + mul.d t3, t1, t2 // A + mul.d t4, a4, t2 // B + mul.d t5, t1, a5 // C + mul.d t6, a4, a5 // D + add.d t0, t4, t5 // E + slli.d t8, a2, 1 + vreplgr2vr.b vr0, t3 + vreplgr2vr.b vr1, t4 + vreplgr2vr.b vr2, t5 + vreplgr2vr.b vr3, t6 + vreplgr2vr.b vr4, t0 + + bge zero, t6, .ENDPUT_DA + move t1, a3 + vilvl.b vr9, vr1, vr0 + vilvl.b vr10, vr3, vr2 +.PUT_DA: + fld.d f5, a1, 0 + fld.d f6, a1, 1 + add.d a1, a1, a2 + fld.d f7, a1, 0 + fld.d f8, a1, 1 + add.d a1, a1, a2 + fld.d f11, a1, 0 + fld.d f12, a1, 1 + vilvl.b vr5, vr6, vr5 + vilvl.b vr7, vr8, vr7 + vilvl.b vr13, vr12, vr11 + vilvl.d vr5, vr7, vr5 + vilvl.d vr13, vr13, vr7 + vmulwev.h.bu vr14, vr9, vr5 + vmaddwod.h.bu vr14, vr9, vr5 + vmulwev.h.bu vr15, vr10, vr13 + vmaddwod.h.bu vr15, vr10, vr13 + xvadd.h xr14, xr14, xr15 + vsrarni.b.h vr16, vr14, 6 + vstelm.w vr16, a0, 0, 0 + add.d a0, a0, a2 + vstelm.w vr16, a0, 0, 1 + add.d a0, a0, a2 + addi.d t1, t1, -2 + blt zero, t1, .PUT_DA + b .ENDPUTA +.ENDPUT_DA: + + bge zero, t0, .ENDPUT_EA + move t1, a3 + li.d t7, 1 + slt t8, zero, t5 + maskeqz t5, a2, t8 + masknez t7, t7, t8 + or t7, t7, t5 + vilvl.b vr7, vr4, vr0 +.PUT_EA: + fld.d f5, a1, 0 + fldx.d f6, a1, t7 + vilvl.b vr5, vr6, vr5 + add.d a1, a1, a2 + fld.d f8, a1, 0 + fldx.d f9, a1, t7 + vilvl.b vr8, vr9, vr8 + vilvl.d vr5, vr8, vr5 + vmulwev.h.bu vr6, vr7, vr5 + vmaddwod.h.bu vr6, vr7, vr5 + vsrarni.b.h vr6, vr6, 6 + vstelm.w vr6, a0, 0, 0 + add.d a0, a0, a2 + vstelm.w vr6, a0, 0, 1 + add.d a0, a0, a2 + add.d a1, a1, a2 + addi.d t1, t1, -2 + blt zero, t1, .PUT_EA + b .ENDPUTA +.ENDPUT_EA: + + move t1, a3 +.PUTA: + fld.d f5, a1, 0 + fldx.d f8, a1, a2 + vilvl.w vr5, vr8, vr5 + vmulwev.h.bu vr6, vr0, vr5 + vmulwod.h.bu vr7, vr0, vr5 + vilvl.h vr6, vr7, vr6 + vsrarni.b.h vr6, vr6, 6 + vstelm.w vr6, a0, 0, 0 + add.d a0, a0, a2 + vstelm.w vr6, a0, 0, 1 + add.d a0, a0, a2 + add.d a1, a1, t8 + addi.d t1, t1, -2 + blt zero, t1, .PUTA +.ENDPUTA: +endfunc diff --git a/libavcodec/loongarch/h264chroma_init_loongarch.c b/libavcodec/loongarch/h264chroma_init_loongarch.c index 0ca24ecc477..40a957aad3e 100644 --- a/libavcodec/loongarch/h264chroma_init_loongarch.c +++ b/libavcodec/loongarch/h264chroma_init_loongarch.c @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "h264chroma_lasx.h" +#include "h264chroma_loongarch.h" #include "libavutil/attributes.h" #include "libavutil/loongarch/cpu.h" #include "libavcodec/h264chroma.h" @@ -27,6 +27,14 @@ av_cold void ff_h264chroma_init_loongarch(H264ChromaContext *c, int bit_depth) { int cpu_flags = av_get_cpu_flags(); + if (have_lsx(cpu_flags)) { + if (bit_depth <= 8) { + c->put_h264_chroma_pixels_tab[0] = ff_put_h264_chroma_mc8_lsx; + c->avg_h264_chroma_pixels_tab[0] = ff_avg_h264_chroma_mc8_lsx; + c->put_h264_chroma_pixels_tab[1] = ff_put_h264_chroma_mc4_lsx; + } + } + if (have_lasx(cpu_flags)) { if (bit_depth <= 8) { c->put_h264_chroma_pixels_tab[0] = ff_put_h264_chroma_mc8_lasx; diff --git a/libavcodec/loongarch/h264chroma_lasx.c b/libavcodec/loongarch/h264chroma_lasx.c deleted file mode 100644 index 1c0e002bdf6..00000000000 --- a/libavcodec/loongarch/h264chroma_lasx.c +++ /dev/null @@ -1,1280 +0,0 @@ -/* - * Loongson LASX optimized h264chroma - * - * Copyright (c) 2020 Loongson Technology Corporation Limited - * Contributed by Shiyou Yin - * - * 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 "h264chroma_lasx.h" -#include "libavutil/attributes.h" -#include "libavutil/avassert.h" -#include "libavutil/loongarch/loongson_intrinsics.h" - -static const uint8_t chroma_mask_arr[64] = { - 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, - 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, - 0, 1, 1, 2, 2, 3, 3, 4, 16, 17, 17, 18, 18, 19, 19, 20, - 0, 1, 1, 2, 2, 3, 3, 4, 16, 17, 17, 18, 18, 19, 19, 20 -}; - -static av_always_inline void avc_chroma_hv_8x4_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coef_hor0, - uint32_t coef_hor1, uint32_t coef_ver0, - uint32_t coef_ver1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride_2x << 1; - __m256i src0, src1, src2, src3, src4, out; - __m256i res_hz0, res_hz1, res_hz2, res_vt0, res_vt1; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 0, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src, stride_3x, src, stride_4x, - src1, src2, src3, src4); - DUP2_ARG3(__lasx_xvpermi_q, src2, src1, 0x20, src4, src3, 0x20, src1, src3); - src0 = __lasx_xvshuf_b(src0, src0, mask); - DUP2_ARG3(__lasx_xvshuf_b, src1, src1, mask, src3, src3, mask, src1, src3); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_hz_vec, src1, coeff_hz_vec, res_hz0, res_hz1); - res_hz2 = __lasx_xvdp2_h_bu(src3, coeff_hz_vec); - res_vt0 = __lasx_xvmul_h(res_hz1, coeff_vt_vec0); - res_vt1 = __lasx_xvmul_h(res_hz2, coeff_vt_vec0); - res_hz0 = __lasx_xvpermi_q(res_hz1, res_hz0, 0x20); - res_hz1 = __lasx_xvpermi_q(res_hz1, res_hz2, 0x3); - res_vt0 = __lasx_xvmadd_h(res_vt0, res_hz0, coeff_vt_vec1); - res_vt1 = __lasx_xvmadd_h(res_vt1, res_hz1, coeff_vt_vec1); - out = __lasx_xvssrarni_bu_h(res_vt1, res_vt0, 6); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_hv_8x8_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coef_hor0, - uint32_t coef_hor1, uint32_t coef_ver0, - uint32_t coef_ver1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i src0, src1, src2, src3, src4, src5, src6, src7, src8; - __m256i out0, out1; - __m256i res_hz0, res_hz1, res_hz2, res_hz3, res_hz4; - __m256i res_vt0, res_vt1, res_vt2, res_vt3; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 0, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src, stride_3x, src, stride_4x, - src1, src2, src3, src4); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src, stride_3x, src, stride_4x, - src5, src6, src7, src8); - DUP4_ARG3(__lasx_xvpermi_q, src2, src1, 0x20, src4, src3, 0x20, src6, src5, 0x20, - src8, src7, 0x20, src1, src3, src5, src7); - src0 = __lasx_xvshuf_b(src0, src0, mask); - DUP4_ARG3(__lasx_xvshuf_b, src1, src1, mask, src3, src3, mask, src5, src5, mask, src7, - src7, mask, src1, src3, src5, src7); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_hz_vec, src1, coeff_hz_vec, src3, - coeff_hz_vec, src5, coeff_hz_vec, res_hz0, res_hz1, res_hz2, res_hz3); - res_hz4 = __lasx_xvdp2_h_bu(src7, coeff_hz_vec); - res_vt0 = __lasx_xvmul_h(res_hz1, coeff_vt_vec0); - res_vt1 = __lasx_xvmul_h(res_hz2, coeff_vt_vec0); - res_vt2 = __lasx_xvmul_h(res_hz3, coeff_vt_vec0); - res_vt3 = __lasx_xvmul_h(res_hz4, coeff_vt_vec0); - res_hz0 = __lasx_xvpermi_q(res_hz1, res_hz0, 0x20); - res_hz1 = __lasx_xvpermi_q(res_hz1, res_hz2, 0x3); - res_hz2 = __lasx_xvpermi_q(res_hz2, res_hz3, 0x3); - res_hz3 = __lasx_xvpermi_q(res_hz3, res_hz4, 0x3); - DUP4_ARG3(__lasx_xvmadd_h, res_vt0, res_hz0, coeff_vt_vec1, res_vt1, res_hz1, coeff_vt_vec1, - res_vt2, res_hz2, coeff_vt_vec1, res_vt3, res_hz3, coeff_vt_vec1, - res_vt0, res_vt1, res_vt2, res_vt3); - DUP2_ARG3(__lasx_xvssrarni_bu_h, res_vt1, res_vt0, 6, res_vt3, res_vt2, 6, out0, out1); - __lasx_xvstelm_d(out0, dst, 0, 0); - __lasx_xvstelm_d(out0, dst + stride, 0, 2); - __lasx_xvstelm_d(out0, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(out1, dst, 0, 0); - __lasx_xvstelm_d(out1, dst + stride, 0, 2); - __lasx_xvstelm_d(out1, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out1, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_hz_8x4_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - __m256i src0, src1, src2, src3, out; - __m256i res0, res1; - __m256i mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 0, src, 0, mask, src0); - DUP2_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src1, src2); - src3 = __lasx_xvldx(src, stride_3x); - DUP2_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src3, src2, 0x20, src0, src2); - DUP2_ARG3(__lasx_xvshuf_b, src0, src0, mask, src2, src2, mask, src0, src2); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, res0, res1); - out = __lasx_xvssrarni_bu_h(res1, res0, 6); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); - -} - -static av_always_inline void avc_chroma_hz_8x8_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i src0, src1, src2, src3, src4, src5, src6, src7; - __m256i out0, out1; - __m256i res0, res1, res2, res3; - __m256i mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 0, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src, stride_3x, src, stride_4x, - src1, src2, src3, src4); - src += stride_4x; - DUP2_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src5, src6); - src7 = __lasx_xvldx(src, stride_3x); - DUP4_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src3, src2, 0x20, src5, src4, 0x20, - src7, src6, 0x20, src0, src2, src4, src6); - DUP4_ARG3(__lasx_xvshuf_b, src0, src0, mask, src2, src2, mask, src4, src4, mask, - src6, src6, mask, src0, src2, src4, src6); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, src4, coeff_vec, src6, - coeff_vec, res0, res1, res2, res3); - DUP2_ARG3(__lasx_xvssrarni_bu_h, res1, res0, 6, res3, res2, 6, out0, out1); - __lasx_xvstelm_d(out0, dst, 0, 0); - __lasx_xvstelm_d(out0, dst + stride, 0, 2); - __lasx_xvstelm_d(out0, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(out1, dst, 0, 0); - __lasx_xvstelm_d(out1, dst + stride, 0, 2); - __lasx_xvstelm_d(out1, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out1, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_hz_nonmult_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1, int32_t height) -{ - uint32_t row; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i src0, src1, src2, src3, out; - __m256i res0, res1; - __m256i mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - mask = __lasx_xvld(chroma_mask_arr, 0); - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - - for (row = height >> 2; row--;) { - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src0, src1, src2, src3); - src += stride_4x; - DUP2_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src3, src2, 0x20, src0, src2); - DUP2_ARG3(__lasx_xvshuf_b, src0, src0, mask, src2, src2, mask, src0, src2); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, res0, res1); - out = __lasx_xvssrarni_bu_h(res1, res0, 6); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); - dst += stride_4x; - } - - if ((height & 3)) { - src0 = __lasx_xvld(src, 0); - src1 = __lasx_xvldx(src, stride); - src1 = __lasx_xvpermi_q(src1, src0, 0x20); - src0 = __lasx_xvshuf_b(src1, src1, mask); - res0 = __lasx_xvdp2_h_bu(src0, coeff_vec); - out = __lasx_xvssrarni_bu_h(res0, res0, 6); - __lasx_xvstelm_d(out, dst, 0, 0); - dst += stride; - __lasx_xvstelm_d(out, dst, 0, 2); - } -} - -static av_always_inline void avc_chroma_vt_8x4_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - __m256i src0, src1, src2, src3, src4, out; - __m256i res0, res1; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - src0 = __lasx_xvld(src, 0); - src += stride; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src1, src2, src3, src4); - DUP4_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src2, src1, 0x20, src3, src2, 0x20, - src4, src3, 0x20, src0, src1, src2, src3); - DUP2_ARG2(__lasx_xvilvl_b, src1, src0, src3, src2, src0, src2); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, res0, res1); - out = __lasx_xvssrarni_bu_h(res1, res0, 6); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_vt_8x8_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i src0, src1, src2, src3, src4, src5, src6, src7, src8; - __m256i out0, out1; - __m256i res0, res1, res2, res3; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - src0 = __lasx_xvld(src, 0); - src += stride; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src1, src2, src3, src4); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src5, src6, src7, src8); - DUP4_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src2, src1, 0x20, src3, src2, 0x20, - src4, src3, 0x20, src0, src1, src2, src3); - DUP4_ARG3(__lasx_xvpermi_q, src5, src4, 0x20, src6, src5, 0x20, src7, src6, 0x20, - src8, src7, 0x20, src4, src5, src6, src7); - DUP4_ARG2(__lasx_xvilvl_b, src1, src0, src3, src2, src5, src4, src7, src6, - src0, src2, src4, src6); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, src4, coeff_vec, - src6, coeff_vec, res0, res1, res2, res3); - DUP2_ARG3(__lasx_xvssrarni_bu_h, res1, res0, 6, res3, res2, 6, out0, out1); - __lasx_xvstelm_d(out0, dst, 0, 0); - __lasx_xvstelm_d(out0, dst + stride, 0, 2); - __lasx_xvstelm_d(out0, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(out1, dst, 0, 0); - __lasx_xvstelm_d(out1, dst + stride, 0, 2); - __lasx_xvstelm_d(out1, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out1, dst + stride_3x, 0, 3); -} - -static av_always_inline void copy_width8x8_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride) -{ - uint64_t tmp[8]; - ptrdiff_t stride_2, stride_3, stride_4; - __asm__ volatile ( - "slli.d %[stride_2], %[stride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[stride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "ld.d %[tmp0], %[src], 0x0 \n\t" - "ldx.d %[tmp1], %[src], %[stride] \n\t" - "ldx.d %[tmp2], %[src], %[stride_2] \n\t" - "ldx.d %[tmp3], %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "ld.d %[tmp4], %[src], 0x0 \n\t" - "ldx.d %[tmp5], %[src], %[stride] \n\t" - "ldx.d %[tmp6], %[src], %[stride_2] \n\t" - "ldx.d %[tmp7], %[src], %[stride_3] \n\t" - - "st.d %[tmp0], %[dst], 0x0 \n\t" - "stx.d %[tmp1], %[dst], %[stride] \n\t" - "stx.d %[tmp2], %[dst], %[stride_2] \n\t" - "stx.d %[tmp3], %[dst], %[stride_3] \n\t" - "add.d %[dst], %[dst], %[stride_4] \n\t" - "st.d %[tmp4], %[dst], 0x0 \n\t" - "stx.d %[tmp5], %[dst], %[stride] \n\t" - "stx.d %[tmp6], %[dst], %[stride_2] \n\t" - "stx.d %[tmp7], %[dst], %[stride_3] \n\t" - : [tmp0]"=&r"(tmp[0]), [tmp1]"=&r"(tmp[1]), - [tmp2]"=&r"(tmp[2]), [tmp3]"=&r"(tmp[3]), - [tmp4]"=&r"(tmp[4]), [tmp5]"=&r"(tmp[5]), - [tmp6]"=&r"(tmp[6]), [tmp7]"=&r"(tmp[7]), - [dst]"+&r"(dst), [src]"+&r"(src), - [stride_2]"=&r"(stride_2), [stride_3]"=&r"(stride_3), - [stride_4]"=&r"(stride_4) - : [stride]"r"(stride) - : "memory" - ); -} - -static av_always_inline void copy_width8x4_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride) -{ - uint64_t tmp[4]; - ptrdiff_t stride_2, stride_3; - __asm__ volatile ( - "slli.d %[stride_2], %[stride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[stride] \n\t" - "ld.d %[tmp0], %[src], 0x0 \n\t" - "ldx.d %[tmp1], %[src], %[stride] \n\t" - "ldx.d %[tmp2], %[src], %[stride_2] \n\t" - "ldx.d %[tmp3], %[src], %[stride_3] \n\t" - - "st.d %[tmp0], %[dst], 0x0 \n\t" - "stx.d %[tmp1], %[dst], %[stride] \n\t" - "stx.d %[tmp2], %[dst], %[stride_2] \n\t" - "stx.d %[tmp3], %[dst], %[stride_3] \n\t" - : [tmp0]"=&r"(tmp[0]), [tmp1]"=&r"(tmp[1]), - [tmp2]"=&r"(tmp[2]), [tmp3]"=&r"(tmp[3]), - [stride_2]"=&r"(stride_2), [stride_3]"=&r"(stride_3) - : [stride]"r"(stride), [dst]"r"(dst), [src]"r"(src) - : "memory" - ); -} - -static void avc_chroma_hv_8w_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coef_hor0, uint32_t coef_hor1, - uint32_t coef_ver0, uint32_t coef_ver1, - int32_t height) -{ - if (4 == height) { - avc_chroma_hv_8x4_lasx(src, dst, stride, coef_hor0, coef_hor1, coef_ver0, - coef_ver1); - } else if (8 == height) { - avc_chroma_hv_8x8_lasx(src, dst, stride, coef_hor0, coef_hor1, coef_ver0, - coef_ver1); - } -} - -static void avc_chroma_hv_4x2_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coef_hor0, uint32_t coef_hor1, - uint32_t coef_ver0, uint32_t coef_ver1) -{ - ptrdiff_t stride_2 = stride << 1; - __m256i src0, src1, src2; - __m256i res_hz, res_vt; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - __m256i coeff_vt_vec = __lasx_xvpermi_q(coeff_vt_vec1, coeff_vt_vec0, 0x02); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 32, src, 0, mask, src0); - DUP2_ARG2(__lasx_xvldx, src, stride, src, stride_2, src1, src2); - DUP2_ARG3(__lasx_xvshuf_b, src1, src0, mask, src2, src1, mask, src0, src1); - src0 = __lasx_xvpermi_q(src0, src1, 0x02); - res_hz = __lasx_xvdp2_h_bu(src0, coeff_hz_vec); - res_vt = __lasx_xvmul_h(res_hz, coeff_vt_vec); - res_hz = __lasx_xvpermi_q(res_hz, res_vt, 0x01); - res_vt = __lasx_xvadd_h(res_hz, res_vt); - res_vt = __lasx_xvssrarni_bu_h(res_vt, res_vt, 6); - __lasx_xvstelm_w(res_vt, dst, 0, 0); - __lasx_xvstelm_w(res_vt, dst + stride, 0, 1); -} - -static void avc_chroma_hv_4x4_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coef_hor0, uint32_t coef_hor1, - uint32_t coef_ver0, uint32_t coef_ver1) -{ - ptrdiff_t stride_2 = stride << 1; - ptrdiff_t stride_3 = stride_2 + stride; - ptrdiff_t stride_4 = stride_2 << 1; - __m256i src0, src1, src2, src3, src4; - __m256i res_hz0, res_hz1, res_vt0, res_vt1; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 32, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src1, src2, src3, src4); - DUP4_ARG3(__lasx_xvshuf_b, src1, src0, mask, src2, src1, mask, src3, src2, mask, - src4, src3, mask, src0, src1, src2, src3); - DUP2_ARG3(__lasx_xvpermi_q, src0, src2, 0x02, src1, src3, 0x02, src0, src1); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_hz_vec, src1, coeff_hz_vec, res_hz0, res_hz1); - DUP2_ARG2(__lasx_xvmul_h, res_hz0, coeff_vt_vec1, res_hz1, coeff_vt_vec0, res_vt0, res_vt1); - res_hz0 = __lasx_xvadd_h(res_vt0, res_vt1); - res_hz0 = __lasx_xvssrarni_bu_h(res_hz0, res_hz0, 6); - __lasx_xvstelm_w(res_hz0, dst, 0, 0); - __lasx_xvstelm_w(res_hz0, dst + stride, 0, 1); - __lasx_xvstelm_w(res_hz0, dst + stride_2, 0, 4); - __lasx_xvstelm_w(res_hz0, dst + stride_3, 0, 5); -} - -static void avc_chroma_hv_4x8_lasx(const uint8_t *src, uint8_t * dst, ptrdiff_t stride, - uint32_t coef_hor0, uint32_t coef_hor1, - uint32_t coef_ver0, uint32_t coef_ver1) -{ - ptrdiff_t stride_2 = stride << 1; - ptrdiff_t stride_3 = stride_2 + stride; - ptrdiff_t stride_4 = stride_2 << 1; - __m256i src0, src1, src2, src3, src4, src5, src6, src7, src8; - __m256i res_hz0, res_hz1, res_hz2, res_hz3; - __m256i res_vt0, res_vt1, res_vt2, res_vt3; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 32, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src1, src2, src3, src4); - src += stride_4; - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src5, src6, src7, src8); - DUP4_ARG3(__lasx_xvshuf_b, src1, src0, mask, src2, src1, mask, src3, src2, mask, - src4, src3, mask, src0, src1, src2, src3); - DUP4_ARG3(__lasx_xvshuf_b, src5, src4, mask, src6, src5, mask, src7, src6, mask, - src8, src7, mask, src4, src5, src6, src7); - DUP4_ARG3(__lasx_xvpermi_q, src0, src2, 0x02, src1, src3, 0x02, src4, src6, 0x02, - src5, src7, 0x02, src0, src1, src4, src5); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_hz_vec, src1, coeff_hz_vec, src4, coeff_hz_vec, - src5, coeff_hz_vec, res_hz0, res_hz1, res_hz2, res_hz3); - DUP4_ARG2(__lasx_xvmul_h, res_hz0, coeff_vt_vec1, res_hz1, coeff_vt_vec0, res_hz2, - coeff_vt_vec1, res_hz3, coeff_vt_vec0, res_vt0, res_vt1, res_vt2, res_vt3); - DUP2_ARG2(__lasx_xvadd_h, res_vt0, res_vt1, res_vt2, res_vt3, res_vt0, res_vt2); - res_hz0 = __lasx_xvssrarni_bu_h(res_vt2, res_vt0, 6); - __lasx_xvstelm_w(res_hz0, dst, 0, 0); - __lasx_xvstelm_w(res_hz0, dst + stride, 0, 1); - __lasx_xvstelm_w(res_hz0, dst + stride_2, 0, 4); - __lasx_xvstelm_w(res_hz0, dst + stride_3, 0, 5); - dst += stride_4; - __lasx_xvstelm_w(res_hz0, dst, 0, 2); - __lasx_xvstelm_w(res_hz0, dst + stride, 0, 3); - __lasx_xvstelm_w(res_hz0, dst + stride_2, 0, 6); - __lasx_xvstelm_w(res_hz0, dst + stride_3, 0, 7); -} - -static void avc_chroma_hv_4w_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coef_hor0, uint32_t coef_hor1, - uint32_t coef_ver0, uint32_t coef_ver1, - int32_t height) -{ - if (8 == height) { - avc_chroma_hv_4x8_lasx(src, dst, stride, coef_hor0, coef_hor1, coef_ver0, - coef_ver1); - } else if (4 == height) { - avc_chroma_hv_4x4_lasx(src, dst, stride, coef_hor0, coef_hor1, coef_ver0, - coef_ver1); - } else if (2 == height) { - avc_chroma_hv_4x2_lasx(src, dst, stride, coef_hor0, coef_hor1, coef_ver0, - coef_ver1); - } -} - -static void avc_chroma_hz_4x2_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1) -{ - __m256i src0, src1; - __m256i res, mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 32, src, 0, mask, src0); - src1 = __lasx_xvldx(src, stride); - src0 = __lasx_xvshuf_b(src1, src0, mask); - res = __lasx_xvdp2_h_bu(src0, coeff_vec); - res = __lasx_xvslli_h(res, 3); - res = __lasx_xvssrarni_bu_h(res, res, 6); - __lasx_xvstelm_w(res, dst, 0, 0); - __lasx_xvstelm_w(res, dst + stride, 0, 1); -} - -static void avc_chroma_hz_4x4_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2 = stride << 1; - ptrdiff_t stride_3 = stride_2 + stride; - __m256i src0, src1, src2, src3; - __m256i res, mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 32, src, 0, mask, src0); - DUP2_ARG2(__lasx_xvldx, src, stride, src, stride_2, src1, src2); - src3 = __lasx_xvldx(src, stride_3); - DUP2_ARG3(__lasx_xvshuf_b, src1, src0, mask, src3, src2, mask, src0, src2); - src0 = __lasx_xvpermi_q(src0, src2, 0x02); - res = __lasx_xvdp2_h_bu(src0, coeff_vec); - res = __lasx_xvslli_h(res, 3); - res = __lasx_xvssrarni_bu_h(res, res, 6); - __lasx_xvstelm_w(res, dst, 0, 0); - __lasx_xvstelm_w(res, dst + stride, 0, 1); - __lasx_xvstelm_w(res, dst + stride_2, 0, 4); - __lasx_xvstelm_w(res, dst + stride_3, 0, 5); -} - -static void avc_chroma_hz_4x8_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2 = stride << 1; - ptrdiff_t stride_3 = stride_2 + stride; - ptrdiff_t stride_4 = stride_2 << 1; - __m256i src0, src1, src2, src3, src4, src5, src6, src7; - __m256i res0, res1, mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 32, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src1, src2, src3, src4); - src += stride_4; - DUP2_ARG2(__lasx_xvldx, src, stride, src, stride_2, src5, src6); - src7 = __lasx_xvldx(src, stride_3); - DUP4_ARG3(__lasx_xvshuf_b, src1, src0, mask, src3, src2, mask, src5, src4, mask, - src7, src6, mask, src0, src2, src4, src6); - DUP2_ARG3(__lasx_xvpermi_q, src0, src2, 0x02, src4, src6, 0x02, src0, src4); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src4, coeff_vec, res0, res1); - res0 = __lasx_xvssrarni_bu_h(res1, res0, 6); - __lasx_xvstelm_w(res0, dst, 0, 0); - __lasx_xvstelm_w(res0, dst + stride, 0, 1); - __lasx_xvstelm_w(res0, dst + stride_2, 0, 4); - __lasx_xvstelm_w(res0, dst + stride_3, 0, 5); - dst += stride_4; - __lasx_xvstelm_w(res0, dst, 0, 2); - __lasx_xvstelm_w(res0, dst + stride, 0, 3); - __lasx_xvstelm_w(res0, dst + stride_2, 0, 6); - __lasx_xvstelm_w(res0, dst + stride_3, 0, 7); -} - -static void avc_chroma_hz_4w_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1, - int32_t height) -{ - if (8 == height) { - avc_chroma_hz_4x8_lasx(src, dst, stride, coeff0, coeff1); - } else if (4 == height) { - avc_chroma_hz_4x4_lasx(src, dst, stride, coeff0, coeff1); - } else if (2 == height) { - avc_chroma_hz_4x2_lasx(src, dst, stride, coeff0, coeff1); - } -} - -static void avc_chroma_hz_8w_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1, - int32_t height) -{ - if (4 == height) { - avc_chroma_hz_8x4_lasx(src, dst, stride, coeff0, coeff1); - } else if (8 == height) { - avc_chroma_hz_8x8_lasx(src, dst, stride, coeff0, coeff1); - } else { - avc_chroma_hz_nonmult_lasx(src, dst, stride, coeff0, coeff1, height); - } -} - -static void avc_chroma_vt_4x2_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1) -{ - __m256i src0, src1, src2; - __m256i tmp0, tmp1; - __m256i res; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - src0 = __lasx_xvld(src, 0); - DUP2_ARG2(__lasx_xvldx, src, stride, src, stride << 1, src1, src2); - DUP2_ARG2(__lasx_xvilvl_b, src1, src0, src2, src1, tmp0, tmp1); - tmp0 = __lasx_xvilvl_d(tmp1, tmp0); - res = __lasx_xvdp2_h_bu(tmp0, coeff_vec); - res = __lasx_xvslli_h(res, 3); - res = __lasx_xvssrarni_bu_h(res, res, 6); - __lasx_xvstelm_w(res, dst, 0, 0); - __lasx_xvstelm_w(res, dst + stride, 0, 1); -} - -static void avc_chroma_vt_4x4_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2 = stride << 1; - ptrdiff_t stride_3 = stride_2 + stride; - ptrdiff_t stride_4 = stride_2 << 1; - __m256i src0, src1, src2, src3, src4; - __m256i tmp0, tmp1, tmp2, tmp3; - __m256i res; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - src0 = __lasx_xvld(src, 0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src1, src2, src3, src4); - DUP4_ARG2(__lasx_xvilvl_b, src1, src0, src2, src1, src3, src2, src4, src3, - tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp2); - tmp0 = __lasx_xvpermi_q(tmp0, tmp2, 0x02); - res = __lasx_xvdp2_h_bu(tmp0, coeff_vec); - res = __lasx_xvslli_h(res, 3); - res = __lasx_xvssrarni_bu_h(res, res, 6); - __lasx_xvstelm_w(res, dst, 0, 0); - __lasx_xvstelm_w(res, dst + stride, 0, 1); - __lasx_xvstelm_w(res, dst + stride_2, 0, 4); - __lasx_xvstelm_w(res, dst + stride_3, 0, 5); -} - -static void avc_chroma_vt_4x8_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1) -{ - ptrdiff_t stride_2 = stride << 1; - ptrdiff_t stride_3 = stride_2 + stride; - ptrdiff_t stride_4 = stride_2 << 1; - __m256i src0, src1, src2, src3, src4, src5, src6, src7, src8; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; - __m256i res0, res1; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - src0 = __lasx_xvld(src, 0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src1, src2, src3, src4); - src += stride_4; - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2, src, stride_3, - src, stride_4, src5, src6, src7, src8); - DUP4_ARG2(__lasx_xvilvl_b, src1, src0, src2, src1, src3, src2, src4, src3, - tmp0, tmp1, tmp2, tmp3); - DUP4_ARG2(__lasx_xvilvl_b, src5, src4, src6, src5, src7, src6, src8, src7, - tmp4, tmp5, tmp6, tmp7); - DUP4_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, tmp6, - tmp0, tmp2, tmp4, tmp6); - tmp0 = __lasx_xvpermi_q(tmp0, tmp2, 0x02); - tmp4 = __lasx_xvpermi_q(tmp4, tmp6, 0x02); - DUP2_ARG2(__lasx_xvdp2_h_bu, tmp0, coeff_vec, tmp4, coeff_vec, res0, res1); - res0 = __lasx_xvssrarni_bu_h(res1, res0, 6); - __lasx_xvstelm_w(res0, dst, 0, 0); - __lasx_xvstelm_w(res0, dst + stride, 0, 1); - __lasx_xvstelm_w(res0, dst + stride_2, 0, 4); - __lasx_xvstelm_w(res0, dst + stride_3, 0, 5); - dst += stride_4; - __lasx_xvstelm_w(res0, dst, 0, 2); - __lasx_xvstelm_w(res0, dst + stride, 0, 3); - __lasx_xvstelm_w(res0, dst + stride_2, 0, 6); - __lasx_xvstelm_w(res0, dst + stride_3, 0, 7); -} - -static void avc_chroma_vt_4w_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1, - int32_t height) -{ - if (8 == height) { - avc_chroma_vt_4x8_lasx(src, dst, stride, coeff0, coeff1); - } else if (4 == height) { - avc_chroma_vt_4x4_lasx(src, dst, stride, coeff0, coeff1); - } else if (2 == height) { - avc_chroma_vt_4x2_lasx(src, dst, stride, coeff0, coeff1); - } -} - -static void avc_chroma_vt_8w_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - uint32_t coeff0, uint32_t coeff1, - int32_t height) -{ - if (4 == height) { - avc_chroma_vt_8x4_lasx(src, dst, stride, coeff0, coeff1); - } else if (8 == height) { - avc_chroma_vt_8x8_lasx(src, dst, stride, coeff0, coeff1); - } -} - -static void copy_width4_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t height) -{ - uint32_t tp0, tp1, tp2, tp3, tp4, tp5, tp6, tp7; - - if (8 == height) { - ptrdiff_t stride_2, stride_3, stride_4; - - __asm__ volatile ( - "slli.d %[stride_2], %[stride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[stride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "ld.wu %[tp0], %[src], 0 \n\t" - "ldx.wu %[tp1], %[src], %[stride] \n\t" - "ldx.wu %[tp2], %[src], %[stride_2] \n\t" - "ldx.wu %[tp3], %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "ld.wu %[tp4], %[src], 0 \n\t" - "ldx.wu %[tp5], %[src], %[stride] \n\t" - "ldx.wu %[tp6], %[src], %[stride_2] \n\t" - "ldx.wu %[tp7], %[src], %[stride_3] \n\t" - "st.w %[tp0], %[dst], 0 \n\t" - "stx.w %[tp1], %[dst], %[stride] \n\t" - "stx.w %[tp2], %[dst], %[stride_2] \n\t" - "stx.w %[tp3], %[dst], %[stride_3] \n\t" - "add.d %[dst], %[dst], %[stride_4] \n\t" - "st.w %[tp4], %[dst], 0 \n\t" - "stx.w %[tp5], %[dst], %[stride] \n\t" - "stx.w %[tp6], %[dst], %[stride_2] \n\t" - "stx.w %[tp7], %[dst], %[stride_3] \n\t" - : [stride_2]"+&r"(stride_2), [stride_3]"+&r"(stride_3), [stride_4]"+&r"(stride_4), - [src]"+&r"(src), [dst]"+&r"(dst), [tp0]"+&r"(tp0), [tp1]"+&r"(tp1), - [tp2]"+&r"(tp2), [tp3]"+&r"(tp3), [tp4]"+&r"(tp4), [tp5]"+&r"(tp5), - [tp6]"+&r"(tp6), [tp7]"+&r"(tp7) - : [stride]"r"(stride) - : "memory" - ); - } else if (4 == height) { - ptrdiff_t stride_2, stride_3; - - __asm__ volatile ( - "slli.d %[stride_2], %[stride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[stride] \n\t" - "ld.wu %[tp0], %[src], 0 \n\t" - "ldx.wu %[tp1], %[src], %[stride] \n\t" - "ldx.wu %[tp2], %[src], %[stride_2] \n\t" - "ldx.wu %[tp3], %[src], %[stride_3] \n\t" - "st.w %[tp0], %[dst], 0 \n\t" - "stx.w %[tp1], %[dst], %[stride] \n\t" - "stx.w %[tp2], %[dst], %[stride_2] \n\t" - "stx.w %[tp3], %[dst], %[stride_3] \n\t" - : [stride_2]"+&r"(stride_2), [stride_3]"+&r"(stride_3), - [src]"+&r"(src), [dst]"+&r"(dst), [tp0]"+&r"(tp0), [tp1]"+&r"(tp1), - [tp2]"+&r"(tp2), [tp3]"+&r"(tp3) - : [stride]"r"(stride) - : "memory" - ); - } else if (2 == height) { - __asm__ volatile ( - "ld.wu %[tp0], %[src], 0 \n\t" - "ldx.wu %[tp1], %[src], %[stride] \n\t" - "st.w %[tp0], %[dst], 0 \n\t" - "stx.w %[tp1], %[dst], %[stride] \n\t" - : [tp0]"+&r"(tp0), [tp1]"+&r"(tp1) - : [src]"r"(src), [dst]"r"(dst), [stride]"r"(stride) - : "memory" - ); - } -} - -static void copy_width8_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t height) -{ - if (8 == height) { - copy_width8x8_lasx(src, dst, stride); - } else if (4 == height) { - copy_width8x4_lasx(src, dst, stride); - } -} - -void ff_put_h264_chroma_mc4_lasx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, - int height, int x, int y) -{ - av_assert2(x < 8 && y < 8 && x >= 0 && y >= 0); - - if(x && y) { - avc_chroma_hv_4w_lasx(src, dst, stride, x, (8 - x), y, (8 - y), height); - } else if (x) { - avc_chroma_hz_4w_lasx(src, dst, stride, x, (8 - x), height); - } else if (y) { - avc_chroma_vt_4w_lasx(src, dst, stride, y, (8 - y), height); - } else { - copy_width4_lasx(src, dst, stride, height); - } -} - -void ff_put_h264_chroma_mc8_lasx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, - int height, int x, int y) -{ - av_assert2(x < 8 && y < 8 && x >= 0 && y >= 0); - - if (!(x || y)) { - copy_width8_lasx(src, dst, stride, height); - } else if (x && y) { - avc_chroma_hv_8w_lasx(src, dst, stride, x, (8 - x), y, (8 - y), height); - } else if (x) { - avc_chroma_hz_8w_lasx(src, dst, stride, x, (8 - x), height); - } else { - avc_chroma_vt_8w_lasx(src, dst, stride, y, (8 - y), height); - } -} - -static av_always_inline void avc_chroma_hv_and_aver_dst_8x4_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coef_hor0, - uint32_t coef_hor1, uint32_t coef_ver0, - uint32_t coef_ver1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i tp0, tp1, tp2, tp3; - __m256i src0, src1, src2, src3, src4, out; - __m256i res_hz0, res_hz1, res_hz2, res_vt0, res_vt1; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 0, src, 0, mask, src0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src, stride_3x, src, stride_4x, - src1, src2, src3, src4); - DUP2_ARG3(__lasx_xvpermi_q, src2, src1, 0x20, src4, src3, 0x20, src1, src3); - src0 = __lasx_xvshuf_b(src0, src0, mask); - DUP2_ARG3(__lasx_xvshuf_b, src1, src1, mask, src3, src3, mask, src1, src3); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_hz_vec, src1, coeff_hz_vec, res_hz0, res_hz1); - res_hz2 = __lasx_xvdp2_h_bu(src3, coeff_hz_vec); - res_vt0 = __lasx_xvmul_h(res_hz1, coeff_vt_vec0); - res_vt1 = __lasx_xvmul_h(res_hz2, coeff_vt_vec0); - res_hz0 = __lasx_xvpermi_q(res_hz1, res_hz0, 0x20); - res_hz1 = __lasx_xvpermi_q(res_hz1, res_hz2, 0x3); - res_vt0 = __lasx_xvmadd_h(res_vt0, res_hz0, coeff_vt_vec1); - res_vt1 = __lasx_xvmadd_h(res_vt1, res_hz1, coeff_vt_vec1); - out = __lasx_xvssrarni_bu_h(res_vt1, res_vt0, 6); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - tp0 = __lasx_xvpermi_q(tp2, tp0, 0x20); - out = __lasx_xvavgr_bu(out, tp0); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_hv_and_aver_dst_8x8_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coef_hor0, - uint32_t coef_hor1, uint32_t coef_ver0, - uint32_t coef_ver1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i tp0, tp1, tp2, tp3, dst0, dst1; - __m256i src0, src1, src2, src3, src4, src5, src6, src7, src8; - __m256i out0, out1; - __m256i res_hz0, res_hz1, res_hz2, res_hz3, res_hz4; - __m256i res_vt0, res_vt1, res_vt2, res_vt3; - __m256i mask; - __m256i coeff_hz_vec0 = __lasx_xvreplgr2vr_b(coef_hor0); - __m256i coeff_hz_vec1 = __lasx_xvreplgr2vr_b(coef_hor1); - __m256i coeff_vt_vec0 = __lasx_xvreplgr2vr_h(coef_ver0); - __m256i coeff_vt_vec1 = __lasx_xvreplgr2vr_h(coef_ver1); - __m256i coeff_hz_vec = __lasx_xvilvl_b(coeff_hz_vec0, coeff_hz_vec1); - - DUP2_ARG2(__lasx_xvld, chroma_mask_arr, 0, src, 0, mask, src0); - src += stride; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src1, src2, src3, src4); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src5, src6, src7, src8); - DUP4_ARG3(__lasx_xvpermi_q, src2, src1, 0x20, src4, src3, 0x20, src6, src5, 0x20, - src8, src7, 0x20, src1, src3, src5, src7); - src0 = __lasx_xvshuf_b(src0, src0, mask); - DUP4_ARG3(__lasx_xvshuf_b, src1, src1, mask, src3, src3, mask, src5, src5, mask, src7, - src7, mask, src1, src3, src5, src7); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_hz_vec, src1, coeff_hz_vec, src3, - coeff_hz_vec, src5, coeff_hz_vec, res_hz0, res_hz1, res_hz2, res_hz3); - res_hz4 = __lasx_xvdp2_h_bu(src7, coeff_hz_vec); - res_vt0 = __lasx_xvmul_h(res_hz1, coeff_vt_vec0); - res_vt1 = __lasx_xvmul_h(res_hz2, coeff_vt_vec0); - res_vt2 = __lasx_xvmul_h(res_hz3, coeff_vt_vec0); - res_vt3 = __lasx_xvmul_h(res_hz4, coeff_vt_vec0); - res_hz0 = __lasx_xvpermi_q(res_hz1, res_hz0, 0x20); - res_hz1 = __lasx_xvpermi_q(res_hz1, res_hz2, 0x3); - res_hz2 = __lasx_xvpermi_q(res_hz2, res_hz3, 0x3); - res_hz3 = __lasx_xvpermi_q(res_hz3, res_hz4, 0x3); - res_vt0 = __lasx_xvmadd_h(res_vt0, res_hz0, coeff_vt_vec1); - res_vt1 = __lasx_xvmadd_h(res_vt1, res_hz1, coeff_vt_vec1); - res_vt2 = __lasx_xvmadd_h(res_vt2, res_hz2, coeff_vt_vec1); - res_vt3 = __lasx_xvmadd_h(res_vt3, res_hz3, coeff_vt_vec1); - DUP2_ARG3(__lasx_xvssrarni_bu_h, res_vt1, res_vt0, 6, res_vt3, res_vt2, 6, - out0, out1); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - dst0 = __lasx_xvpermi_q(tp2, tp0, 0x20); - dst += stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - dst -= stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - dst1 = __lasx_xvpermi_q(tp2, tp0, 0x20); - out0 = __lasx_xvavgr_bu(out0, dst0); - out1 = __lasx_xvavgr_bu(out1, dst1); - __lasx_xvstelm_d(out0, dst, 0, 0); - __lasx_xvstelm_d(out0, dst + stride, 0, 2); - __lasx_xvstelm_d(out0, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(out1, dst, 0, 0); - __lasx_xvstelm_d(out1, dst + stride, 0, 2); - __lasx_xvstelm_d(out1, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out1, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_hz_and_aver_dst_8x4_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - __m256i tp0, tp1, tp2, tp3; - __m256i src0, src1, src2, src3, out; - __m256i res0, res1; - __m256i mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - mask = __lasx_xvld(chroma_mask_arr, 0); - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src0, src1, src2, src3); - DUP2_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src3, src2, 0x20, src0, src2); - DUP2_ARG3(__lasx_xvshuf_b, src0, src0, mask, src2, src2, mask, src0, src2); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, res0, res1); - out = __lasx_xvssrarni_bu_h(res1, res0, 6); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - tp0 = __lasx_xvpermi_q(tp2, tp0, 0x20); - out = __lasx_xvavgr_bu(out, tp0); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_hz_and_aver_dst_8x8_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i tp0, tp1, tp2, tp3, dst0, dst1; - __m256i src0, src1, src2, src3, src4, src5, src6, src7; - __m256i out0, out1; - __m256i res0, res1, res2, res3; - __m256i mask; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - mask = __lasx_xvld(chroma_mask_arr, 0); - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src0, src1, src2, src3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src4, src5, src6, src7); - DUP4_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src3, src2, 0x20, src5, src4, 0x20, - src7, src6, 0x20, src0, src2, src4, src6); - DUP4_ARG3(__lasx_xvshuf_b, src0, src0, mask, src2, src2, mask, src4, src4, - mask, src6, src6, mask, src0, src2, src4, src6); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, src4, coeff_vec, src6, - coeff_vec, res0, res1, res2, res3); - DUP2_ARG3(__lasx_xvssrarni_bu_h, res1, res0, 6, res3, res2, 6, out0, out1); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - dst0 = __lasx_xvpermi_q(tp2, tp0, 0x20); - dst += stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - dst -= stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - dst1 = __lasx_xvpermi_q(tp2, tp0, 0x20); - out0 = __lasx_xvavgr_bu(out0, dst0); - out1 = __lasx_xvavgr_bu(out1, dst1); - __lasx_xvstelm_d(out0, dst, 0, 0); - __lasx_xvstelm_d(out0, dst + stride, 0, 2); - __lasx_xvstelm_d(out0, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(out1, dst, 0, 0); - __lasx_xvstelm_d(out1, dst + stride, 0, 2); - __lasx_xvstelm_d(out1, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out1, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_vt_and_aver_dst_8x4_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i tp0, tp1, tp2, tp3; - __m256i src0, src1, src2, src3, src4, out; - __m256i res0, res1; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - src0 = __lasx_xvld(src, 0); - DUP4_ARG2(__lasx_xvldx, src, stride, src, stride_2x, src, stride_3x, src, stride_4x, - src1, src2, src3, src4); - DUP4_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src2, src1, 0x20, src3, src2, 0x20, - src4, src3, 0x20, src0, src1, src2, src3); - DUP2_ARG2(__lasx_xvilvl_b, src1, src0, src3, src2, src0, src2); - DUP2_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, res0, res1); - out = __lasx_xvssrarni_bu_h(res1, res0, 6); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - tp0 = __lasx_xvpermi_q(tp2, tp0, 0x20); - out = __lasx_xvavgr_bu(out, tp0); - __lasx_xvstelm_d(out, dst, 0, 0); - __lasx_xvstelm_d(out, dst + stride, 0, 2); - __lasx_xvstelm_d(out, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out, dst + stride_3x, 0, 3); -} - -static av_always_inline void avc_chroma_vt_and_aver_dst_8x8_lasx(const uint8_t *src, - uint8_t *dst, ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1) -{ - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - __m256i tp0, tp1, tp2, tp3, dst0, dst1; - __m256i src0, src1, src2, src3, src4, src5, src6, src7, src8; - __m256i out0, out1; - __m256i res0, res1, res2, res3; - __m256i coeff_vec0 = __lasx_xvreplgr2vr_b(coeff0); - __m256i coeff_vec1 = __lasx_xvreplgr2vr_b(coeff1); - __m256i coeff_vec = __lasx_xvilvl_b(coeff_vec0, coeff_vec1); - - coeff_vec = __lasx_xvslli_b(coeff_vec, 3); - src0 = __lasx_xvld(src, 0); - src += stride; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src1, src2, src3, src4); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, src, stride_3x, - src5, src6, src7, src8); - DUP4_ARG3(__lasx_xvpermi_q, src1, src0, 0x20, src2, src1, 0x20, src3, src2, 0x20, - src4, src3, 0x20, src0, src1, src2, src3); - DUP4_ARG3(__lasx_xvpermi_q, src5, src4, 0x20, src6, src5, 0x20, src7, src6, 0x20, - src8, src7, 0x20, src4, src5, src6, src7); - DUP4_ARG2(__lasx_xvilvl_b, src1, src0, src3, src2, src5, src4, src7, src6, - src0, src2, src4, src6); - DUP4_ARG2(__lasx_xvdp2_h_bu, src0, coeff_vec, src2, coeff_vec, src4, coeff_vec, src6, - coeff_vec, res0, res1, res2, res3); - DUP2_ARG3(__lasx_xvssrarni_bu_h, res1, res0, 6, res3, res2, 6, out0, out1); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - dst0 = __lasx_xvpermi_q(tp2, tp0, 0x20); - dst += stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, dst, stride_3x, - tp0, tp1, tp2, tp3); - dst -= stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tp2, tp0, tp3, tp1, tp0, tp2); - dst1 = __lasx_xvpermi_q(tp2, tp0, 0x20); - out0 = __lasx_xvavgr_bu(out0, dst0); - out1 = __lasx_xvavgr_bu(out1, dst1); - __lasx_xvstelm_d(out0, dst, 0, 0); - __lasx_xvstelm_d(out0, dst + stride, 0, 2); - __lasx_xvstelm_d(out0, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(out1, dst, 0, 0); - __lasx_xvstelm_d(out1, dst + stride, 0, 2); - __lasx_xvstelm_d(out1, dst + stride_2x, 0, 1); - __lasx_xvstelm_d(out1, dst + stride_3x, 0, 3); -} - -static av_always_inline void avg_width8x8_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride) -{ - __m256i src0, src1, src2, src3; - __m256i dst0, dst1, dst2, dst3; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - ptrdiff_t stride_4x = stride << 2; - - src0 = __lasx_xvldrepl_d(src, 0); - src1 = __lasx_xvldrepl_d(src + stride, 0); - src2 = __lasx_xvldrepl_d(src + stride_2x, 0); - src3 = __lasx_xvldrepl_d(src + stride_3x, 0); - dst0 = __lasx_xvldrepl_d(dst, 0); - dst1 = __lasx_xvldrepl_d(dst + stride, 0); - dst2 = __lasx_xvldrepl_d(dst + stride_2x, 0); - dst3 = __lasx_xvldrepl_d(dst + stride_3x, 0); - src0 = __lasx_xvpackev_d(src1,src0); - src2 = __lasx_xvpackev_d(src3,src2); - src0 = __lasx_xvpermi_q(src0, src2, 0x02); - dst0 = __lasx_xvpackev_d(dst1,dst0); - dst2 = __lasx_xvpackev_d(dst3,dst2); - dst0 = __lasx_xvpermi_q(dst0, dst2, 0x02); - dst0 = __lasx_xvavgr_bu(src0, dst0); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + stride, 0, 1); - __lasx_xvstelm_d(dst0, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst0, dst + stride_3x, 0, 3); - - src += stride_4x; - dst += stride_4x; - src0 = __lasx_xvldrepl_d(src, 0); - src1 = __lasx_xvldrepl_d(src + stride, 0); - src2 = __lasx_xvldrepl_d(src + stride_2x, 0); - src3 = __lasx_xvldrepl_d(src + stride_3x, 0); - dst0 = __lasx_xvldrepl_d(dst, 0); - dst1 = __lasx_xvldrepl_d(dst + stride, 0); - dst2 = __lasx_xvldrepl_d(dst + stride_2x, 0); - dst3 = __lasx_xvldrepl_d(dst + stride_3x, 0); - src0 = __lasx_xvpackev_d(src1,src0); - src2 = __lasx_xvpackev_d(src3,src2); - src0 = __lasx_xvpermi_q(src0, src2, 0x02); - dst0 = __lasx_xvpackev_d(dst1,dst0); - dst2 = __lasx_xvpackev_d(dst3,dst2); - dst0 = __lasx_xvpermi_q(dst0, dst2, 0x02); - dst0 = __lasx_xvavgr_bu(src0, dst0); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + stride, 0, 1); - __lasx_xvstelm_d(dst0, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst0, dst + stride_3x, 0, 3); -} - -static av_always_inline void avg_width8x4_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride) -{ - __m256i src0, src1, src2, src3; - __m256i dst0, dst1, dst2, dst3; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - - src0 = __lasx_xvldrepl_d(src, 0); - src1 = __lasx_xvldrepl_d(src + stride, 0); - src2 = __lasx_xvldrepl_d(src + stride_2x, 0); - src3 = __lasx_xvldrepl_d(src + stride_3x, 0); - dst0 = __lasx_xvldrepl_d(dst, 0); - dst1 = __lasx_xvldrepl_d(dst + stride, 0); - dst2 = __lasx_xvldrepl_d(dst + stride_2x, 0); - dst3 = __lasx_xvldrepl_d(dst + stride_3x, 0); - src0 = __lasx_xvpackev_d(src1,src0); - src2 = __lasx_xvpackev_d(src3,src2); - src0 = __lasx_xvpermi_q(src0, src2, 0x02); - dst0 = __lasx_xvpackev_d(dst1,dst0); - dst2 = __lasx_xvpackev_d(dst3,dst2); - dst0 = __lasx_xvpermi_q(dst0, dst2, 0x02); - dst0 = __lasx_xvavgr_bu(src0, dst0); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + stride, 0, 1); - __lasx_xvstelm_d(dst0, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst0, dst + stride_3x, 0, 3); -} - -static void avc_chroma_hv_and_aver_dst_8w_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, - uint32_t coef_hor0, - uint32_t coef_hor1, - uint32_t coef_ver0, - uint32_t coef_ver1, - int32_t height) -{ - if (4 == height) { - avc_chroma_hv_and_aver_dst_8x4_lasx(src, dst, stride, coef_hor0, - coef_hor1, coef_ver0, coef_ver1); - } else if (8 == height) { - avc_chroma_hv_and_aver_dst_8x8_lasx(src, dst, stride, coef_hor0, - coef_hor1, coef_ver0, coef_ver1); - } -} - -static void avc_chroma_hz_and_aver_dst_8w_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1, int32_t height) -{ - if (4 == height) { - avc_chroma_hz_and_aver_dst_8x4_lasx(src, dst, stride, coeff0, coeff1); - } else if (8 == height) { - avc_chroma_hz_and_aver_dst_8x8_lasx(src, dst, stride, coeff0, coeff1); - } -} - -static void avc_chroma_vt_and_aver_dst_8w_lasx(const uint8_t *src, uint8_t *dst, - ptrdiff_t stride, uint32_t coeff0, - uint32_t coeff1, int32_t height) -{ - if (4 == height) { - avc_chroma_vt_and_aver_dst_8x4_lasx(src, dst, stride, coeff0, coeff1); - } else if (8 == height) { - avc_chroma_vt_and_aver_dst_8x8_lasx(src, dst, stride, coeff0, coeff1); - } -} - -static void avg_width8_lasx(const uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t height) -{ - if (8 == height) { - avg_width8x8_lasx(src, dst, stride); - } else if (4 == height) { - avg_width8x4_lasx(src, dst, stride); - } -} - -void ff_avg_h264_chroma_mc8_lasx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, - int height, int x, int y) -{ - av_assert2(x < 8 && y < 8 && x >= 0 && y >= 0); - - if (!(x || y)) { - avg_width8_lasx(src, dst, stride, height); - } else if (x && y) { - avc_chroma_hv_and_aver_dst_8w_lasx(src, dst, stride, x, (8 - x), y, - (8 - y), height); - } else if (x) { - avc_chroma_hz_and_aver_dst_8w_lasx(src, dst, stride, x, (8 - x), height); - } else { - avc_chroma_vt_and_aver_dst_8w_lasx(src, dst, stride, y, (8 - y), height); - } -} diff --git a/libavcodec/loongarch/h264chroma_lasx.h b/libavcodec/loongarch/h264chroma_lasx.h deleted file mode 100644 index 633752035e0..00000000000 --- a/libavcodec/loongarch/h264chroma_lasx.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2020 Loongson Technology Corporation Limited - * Contributed by Shiyou Yin - * - * 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 - */ - -#ifndef AVCODEC_LOONGARCH_H264CHROMA_LASX_H -#define AVCODEC_LOONGARCH_H264CHROMA_LASX_H - -#include -#include -#include "libavcodec/h264.h" - -void ff_put_h264_chroma_mc4_lasx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, - int h, int x, int y); -void ff_put_h264_chroma_mc8_lasx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, - int h, int x, int y); -void ff_avg_h264_chroma_mc8_lasx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride, - int h, int x, int y); - -#endif /* AVCODEC_LOONGARCH_H264CHROMA_LASX_H */ diff --git a/libavcodec/loongarch/h264chroma_loongarch.h b/libavcodec/loongarch/h264chroma_loongarch.h new file mode 100644 index 00000000000..e65fcfe9f38 --- /dev/null +++ b/libavcodec/loongarch/h264chroma_loongarch.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Shiyou Yin + * + * 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 + */ + +#ifndef AVCODEC_LOONGARCH_H264CHROMA_LOONGARCH_H +#define AVCODEC_LOONGARCH_H264CHROMA_LOONGARCH_H + +#include "libavcodec/h264.h" + +void ff_put_h264_chroma_mc8_lsx(unsigned char *dst, const unsigned char *src, + long int stride, int h, int x, int y); +void ff_avg_h264_chroma_mc8_lsx(unsigned char *dst, const unsigned char *src, + long int stride, int h, int x, int y); +void ff_put_h264_chroma_mc4_lsx(unsigned char *dst, const unsigned char *src, + long int stride, int h, int x, int y); + +void ff_put_h264_chroma_mc4_lasx(unsigned char *dst, const unsigned char *src, + long int stride, int h, int x, int y); +void ff_put_h264_chroma_mc8_lasx(unsigned char *dst, const unsigned char *src, + long int stride, int h, int x, int y); +void ff_avg_h264_chroma_mc8_lasx(unsigned char *dst, const unsigned char *src, + long int stride, int h, int x, int y); + +#endif /* AVCODEC_LOONGARCH_H264CHROMA_LOONGARCH_H */ diff --git a/libavcodec/loongarch/h264dsp.S b/libavcodec/loongarch/h264dsp.S new file mode 100644 index 00000000000..750fe49143c --- /dev/null +++ b/libavcodec/loongarch/h264dsp.S @@ -0,0 +1,1977 @@ +/* + * Loongson LSX/LASX optimized h264dsp + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Hao Chen + * + * 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 "loongson_asm.S" + +const vec_shuf +.rept 2 +.byte 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3 +.endr +endconst + +.macro AVC_LPF_P1_OR_Q1 _in0, _in1, _in2, _in3, _in4, _in5, _out, _tmp0, _tmp1 + vavgr.hu \_tmp0, \_in0, \_in1 + vslli.h \_tmp1, \_in2, 1 + vsub.h \_tmp0, \_tmp0, \_tmp1 + vavg.h \_tmp0, \_in3, \_tmp0 + vclip.h \_tmp0, \_tmp0, \_in4, \_in5 + vadd.h \_out, \_in2, \_tmp0 +.endm + +.macro AVC_LPF_P0Q0 _in0, _in1, _in2, _in3, _in4, _in5, _out0, \ + _out1, _tmp0, _tmp1 + vsub.h \_tmp0, \_in0, \_in1 + vsub.h \_tmp1, \_in2, \_in3 + vslli.h \_tmp0, \_tmp0, 2 + vaddi.hu \_tmp1, \_tmp1, 4 + vadd.h \_tmp0, \_tmp0, \_tmp1 + vsrai.h \_tmp0, \_tmp0, 3 + vclip.h \_tmp0, \_tmp0, \_in4, \_in5 + vadd.h \_out0, \_in1, \_tmp0 + vsub.h \_out1, \_in0, \_tmp0 + vclip255.h \_out0, \_out0 + vclip255.h \_out1, \_out1 +.endm + +.macro SAVE_REG + addi.d sp, sp, -64 + fst.d f24, sp, 0 + fst.d f25, sp, 8 + fst.d f26, sp, 16 + fst.d f27, sp, 24 + fst.d f28, sp, 32 + fst.d f29, sp, 40 + fst.d f30, sp, 48 + fst.d f31, sp, 56 +.endm + +.macro RESTORE_REG + fld.d f24, sp, 0 + fld.d f25, sp, 8 + fld.d f26, sp, 16 + fld.d f27, sp, 24 + fld.d f28, sp, 32 + fld.d f29, sp, 40 + fld.d f30, sp, 48 + fld.d f31, sp, 56 + addi.d sp, sp, 64 +.endm + +.macro load_double _in0, _in1, _in2, _in3, _src, _str0, _str1, _str2 + fld.d \_in0, \_src, 0 + fldx.d \_in1, \_src, \_str0 + fldx.d \_in2, \_src, \_str1 + fldx.d \_in3, \_src, \_str2 +.endm + +.macro store_double _in0, _in1, _in2, _in3, _dst, _str0, _str1, _str2 + fst.d \_in0, \_dst, 0 + fstx.d \_in1, \_dst, \_str0 + fstx.d \_in2, \_dst, \_str1 + fstx.d \_in3, \_dst, \_str2 +.endm + +function ff_h264_h_lpf_luma_8_lsx + slli.d t0, a1, 1 //img_width_2x + slli.d t1, a1, 2 //img_width_4x + slli.d t2, a1, 3 //img_width_8x + SAVE_REG + la.local t4, vec_shuf + add.d t3, t0, a1 //img_width_3x + vldrepl.w vr0, a4, 0 //tmp_vec0 + vld vr1, t4, 0 //tc_vec + vshuf.b vr1, vr0, vr0, vr1 //tc_vec + vslti.b vr2, vr1, 0 + vxori.b vr2, vr2, 255 + vandi.b vr2, vr2, 1 //bs_vec + vsetnez.v $fcc0, vr2 + bceqz $fcc0, .END_LUMA_8 + vldi vr0, 0 //zero + addi.d t4, a0, -4 //src + vslt.bu vr3, vr0, vr2 //is_bs_greater_than0 + add.d t5, t4, t2 //src_tmp + vld vr4, t4, 0 //row0 + vldx vr5, t4, a1 //row1 + vldx vr6, t4, t0 //row2 + vldx vr7, t4, t3 //row3 + add.d t6, t4, t1 // src += img_width_4x + vld vr8, t6, 0 //row4 + vldx vr9, t6, a1 //row5 + vldx vr10, t6, t0 //row6 + vldx vr11, t6, t3 //row7 + vld vr12, t5, 0 //row8 + vldx vr13, t5, a1 //row9 + vldx vr14, t5, t0 //row10 + vldx vr15, t5, t3 //row11 + add.d t6, t5, t1 // src_tmp += img_width_4x + vld vr16, t6, 0 //row12 + vldx vr17, t6, a1 //row13 + vldx vr18, t6, t0 //row14 + vldx vr19, t6, t3 //row15 + LSX_TRANSPOSE16X8_B vr4, vr5, vr6, vr7, vr8, vr9, vr10, vr11, \ + vr12, vr13, vr14, vr15, vr16, vr17, vr18, vr19, \ + vr10, vr11, vr12, vr13, vr14, vr15, vr16, vr17, \ + vr20, vr21, vr22, vr23, vr24, vr25, vr26, vr27 + //vr10: p3_org, vr11: p2_org, vr12: p1_org, vr13: p0_org + //vr14: q0_org, vr15: q1_org, vr16: q2_org, vr17: q3_org + vabsd.bu vr20, vr13, vr14 //p0_asub_q0 + vabsd.bu vr21, vr12, vr13 //p1_asub_p0 + vabsd.bu vr22, vr15, vr14 //q1_asub_q0 + + vreplgr2vr.b vr4, a2 //alpha + vreplgr2vr.b vr5, a3 //beta + + vslt.bu vr6, vr20, vr4 //is_less_than_alpha + vslt.bu vr7, vr21, vr5 //is_less_than_beta + vand.v vr8, vr6, vr7 //is_less_than + vslt.bu vr7, vr22, vr5 //is_less_than_beta + vand.v vr8, vr7, vr8 //is_less_than + vand.v vr8, vr8, vr3 //is_less_than + vsetnez.v $fcc0, vr8 + bceqz $fcc0, .END_LUMA_8 + vneg.b vr9, vr1 //neg_tc_h + vsllwil.hu.bu vr18, vr1, 0 //tc_h.0 + vexth.hu.bu vr19, vr1 //tc_h.1 + vexth.h.b vr2, vr9 //neg_tc_h.1 + vsllwil.h.b vr9, vr9, 0 //neg_tc_h.0 + + vsllwil.hu.bu vr23, vr12, 0 //p1_org_h.0 + vexth.hu.bu vr3, vr12 //p1_org_h.1 + vsllwil.hu.bu vr24, vr13, 0 //p0_org_h.0 + vexth.hu.bu vr4, vr13 //p0_org_h.1 + vsllwil.hu.bu vr25, vr14, 0 //q0_org_h.0 + vexth.hu.bu vr6, vr14 //q0_org_h.1 + + vabsd.bu vr0, vr11, vr13 //p2_asub_p0 + vslt.bu vr7, vr0, vr5 + vand.v vr7, vr8, vr7 //is_less_than_beta + vsetnez.v $fcc0, vr7 + bceqz $fcc0, .END_LUMA_BETA + vsllwil.hu.bu vr26, vr11, 0 //p2_org_h.0 + vexth.hu.bu vr0, vr11 //p2_org_h.1 + AVC_LPF_P1_OR_Q1 vr24, vr25, vr23, vr26, vr9, vr18, vr27, vr28, vr29 + AVC_LPF_P1_OR_Q1 vr4, vr6, vr3, vr0, vr2, vr19, vr28, vr29, vr30 + vpickev.b vr27, vr28, vr27 + vbitsel.v vr12, vr12, vr27, vr7 + vandi.b vr7, vr7, 1 + vadd.b vr1, vr1, vr7 +.END_LUMA_BETA: + vabsd.bu vr26, vr16, vr14 //q2_asub_q0 + vslt.bu vr7, vr26, vr5 + vand.v vr7, vr7, vr8 + vsllwil.hu.bu vr27, vr15, 0 //q1_org_h.0 + vexth.hu.bu vr26, vr15 //q1_org_h.1 + vsetnez.v $fcc0, vr7 + bceqz $fcc0, .END_LUMA_BETA_SEC + vsllwil.hu.bu vr28, vr16, 0 //q2_org_h.0 + vexth.hu.bu vr0, vr16 //q2_org_h.1 + AVC_LPF_P1_OR_Q1 vr24, vr25, vr27, vr28, vr9, vr18, vr29, vr30, vr31 + AVC_LPF_P1_OR_Q1 vr4, vr6, vr26, vr0, vr2, vr19, vr22, vr30, vr31 + vpickev.b vr29, vr22, vr29 + vbitsel.v vr15, vr15, vr29, vr7 + vandi.b vr7, vr7, 1 + vadd.b vr1, vr1, vr7 +.END_LUMA_BETA_SEC: + vneg.b vr22, vr1 //neg_thresh_h + vsllwil.h.b vr28, vr22, 0 //neg_thresh_h.0 + vexth.h.b vr29, vr22 //neg_thresh_h.1 + vsllwil.hu.bu vr18, vr1, 0 //tc_h.0 + vexth.hu.bu vr1, vr1 //tc_h.1 + AVC_LPF_P0Q0 vr25, vr24, vr23, vr27, vr28, vr18, vr30, vr31, vr0, vr2 + AVC_LPF_P0Q0 vr6, vr4, vr3, vr26, vr29, vr1, vr20, vr21, vr0, vr2 + vpickev.b vr30, vr20, vr30 //p0_h + vpickev.b vr31, vr21, vr31 //q0_h + vbitsel.v vr13, vr13, vr30, vr8 //p0_org + vbitsel.v vr14, vr14, vr31, vr8 //q0_org + + vilvl.b vr4, vr12, vr10 // row0.0 + vilvl.b vr5, vr16, vr14 // row0.1 + vilvl.b vr6, vr13, vr11 // row2.0 + vilvl.b vr7, vr17, vr15 // row2.1 + + vilvh.b vr8, vr12, vr10 // row1.0 + vilvh.b vr9, vr16, vr14 // row1.1 + vilvh.b vr10, vr13, vr11 // row3.0 + vilvh.b vr11, vr17, vr15 // row3.1 + + vilvl.b vr12, vr6, vr4 // row4.0 + vilvl.b vr13, vr7, vr5 // row4.1 + vilvl.b vr14, vr10, vr8 // row6.0 + vilvl.b vr15, vr11, vr9 // row6.1 + + vilvh.b vr16, vr6, vr4 // row5.0 + vilvh.b vr17, vr7, vr5 // row5.1 + vilvh.b vr18, vr10, vr8 // row7.0 + vilvh.b vr19, vr11, vr9 // row7.1 + + vilvl.w vr4, vr13, vr12 // row4: 0, 4, 1, 5 + vilvh.w vr5, vr13, vr12 // row4: 2, 6, 3, 7 + vilvl.w vr6, vr17, vr16 // row5: 0, 4, 1, 5 + vilvh.w vr7, vr17, vr16 // row5: 2, 6, 3, 7 + + vilvl.w vr8, vr15, vr14 // row6: 0, 4, 1, 5 + vilvh.w vr9, vr15, vr14 // row6: 2, 6, 3, 7 + vilvl.w vr10, vr19, vr18 // row7: 0, 4, 1, 5 + vilvh.w vr11, vr19, vr18 // row7: 2, 6, 3, 7 + + vbsrl.v vr20, vr4, 8 + vbsrl.v vr21, vr5, 8 + vbsrl.v vr22, vr6, 8 + vbsrl.v vr23, vr7, 8 + + vbsrl.v vr24, vr8, 8 + vbsrl.v vr25, vr9, 8 + vbsrl.v vr26, vr10, 8 + vbsrl.v vr27, vr11, 8 + + store_double f4, f20, f5, f21, t4, a1, t0, t3 + add.d t4, t4, t1 + store_double f6, f22, f7, f23, t4, a1, t0, t3 + add.d t4, t4, t1 + store_double f8, f24, f9, f25, t4, a1, t0, t3 + add.d t4, t4, t1 + store_double f10, f26, f11, f27, t4, a1, t0, t3 +.END_LUMA_8: + RESTORE_REG +endfunc + +function ff_h264_v_lpf_luma_8_lsx + slli.d t0, a1, 1 //img_width_2x + la.local t4, vec_shuf + vldrepl.w vr0, a4, 0 //tmp_vec0 + vld vr1, t4, 0 //tc_vec + add.d t1, t0, a1 //img_width_3x + vshuf.b vr1, vr0, vr0, vr1 //tc_vec + addi.d sp, sp, -24 + fst.d f24, sp, 0 + fst.d f25, sp, 8 + fst.d f26, sp, 16 + vslti.b vr2, vr1, 0 + vxori.b vr2, vr2, 255 + vandi.b vr2, vr2, 1 //bs_vec + vsetnez.v $fcc0, vr2 + bceqz $fcc0, .END_V_LUMA_8 + sub.d t2, a0, t1 //data - img_width_3x + vreplgr2vr.b vr4, a2 //alpha + vreplgr2vr.b vr5, a3 //beta + vldi vr0, 0 //zero + vld vr10, t2, 0 //p2_org + vldx vr11, t2, a1 //p1_org + vldx vr12, t2, t0 //p0_org + vld vr13, a0, 0 //q0_org + vldx vr14, a0, a1 //q1_org + + vslt.bu vr0, vr0, vr2 //is_bs_greater_than0 + vabsd.bu vr16, vr11, vr12 //p1_asub_p0 + vabsd.bu vr15, vr12, vr13 //p0_asub_q0 + vabsd.bu vr17, vr14, vr13 //q1_asub_q0 + + vslt.bu vr6, vr15, vr4 //is_less_than_alpha + vslt.bu vr7, vr16, vr5 //is_less_than_beta + vand.v vr8, vr6, vr7 //is_less_than + vslt.bu vr7, vr17, vr5 //is_less_than_beta + vand.v vr8, vr7, vr8 + vand.v vr8, vr8, vr0 //is_less_than + + vsetnez.v $fcc0, vr8 + bceqz $fcc0, .END_V_LUMA_8 + vldx vr15, a0, t0 //q2_org + vneg.b vr0, vr1 //neg_tc_h + vsllwil.h.b vr18, vr1, 0 //tc_h.0 + vexth.h.b vr19, vr1 //tc_h.1 + vsllwil.h.b vr9, vr0, 0 //neg_tc_h.0 + vexth.h.b vr2, vr0 //neg_tc_h.1 + + vsllwil.hu.bu vr16, vr11, 0 //p1_org_h.0 + vexth.hu.bu vr17, vr11 //p1_org_h.1 + vsllwil.hu.bu vr20, vr12, 0 //p0_org_h.0 + vexth.hu.bu vr21, vr12 //p0_org_h.1 + vsllwil.hu.bu vr22, vr13, 0 //q0_org_h.0 + vexth.hu.bu vr23, vr13 //q0_org_h.1 + + vabsd.bu vr0, vr10, vr12 //p2_asub_p0 + vslt.bu vr7, vr0, vr5 //is_less_than_beta + vand.v vr7, vr7, vr8 //is_less_than_beta + + vsetnez.v $fcc0, vr8 + bceqz $fcc0, .END_V_LESS_BETA + vsllwil.hu.bu vr3, vr10, 0 //p2_org_h.0 + vexth.hu.bu vr4, vr10 //p2_org_h.1 + AVC_LPF_P1_OR_Q1 vr20, vr22, vr16, vr3, vr9, vr18, vr24, vr0, vr26 + AVC_LPF_P1_OR_Q1 vr21, vr23, vr17, vr4, vr2, vr19, vr25, vr0, vr26 + vpickev.b vr24, vr25, vr24 + vbitsel.v vr24, vr11, vr24, vr7 + addi.d t3, t2, 16 + vstx vr24, t2, a1 + vandi.b vr7, vr7, 1 + vadd.b vr1, vr7, vr1 +.END_V_LESS_BETA: + vabsd.bu vr0, vr15, vr13 //q2_asub_q0 + vslt.bu vr7, vr0, vr5 //is_less_than_beta + vand.v vr7, vr7, vr8 //is_less_than_beta + vsllwil.hu.bu vr3, vr14, 0 //q1_org_h.0 + vexth.hu.bu vr4, vr14 //q1_org_h.1 + + vsetnez.v $fcc0, vr7 + bceqz $fcc0, .END_V_LESS_BETA_SEC + vsllwil.hu.bu vr11, vr15, 0 //q2_org_h.0 + vexth.hu.bu vr15, vr15 //q2_org_h.1 + AVC_LPF_P1_OR_Q1 vr20, vr22, vr3, vr11, vr9, vr18, vr24, vr0, vr26 + AVC_LPF_P1_OR_Q1 vr21, vr23, vr4, vr15, vr2, vr19, vr25, vr0, vr26 + vpickev.b vr24, vr25, vr24 + vbitsel.v vr24, vr14, vr24, vr7 + vstx vr24, a0, a1 + vandi.b vr7, vr7, 1 + vadd.b vr1, vr1, vr7 +.END_V_LESS_BETA_SEC: + vneg.b vr0, vr1 + vsllwil.h.b vr9, vr0, 0 //neg_thresh_h.0 + vexth.h.b vr2, vr0 //neg_thresh_h.1 + vsllwil.hu.bu vr18, vr1, 0 //tc_h.0 + vexth.hu.bu vr19, vr1 //tc_h.1 + AVC_LPF_P0Q0 vr22, vr20, vr16, vr3, vr9, vr18, vr11, vr15, vr0, vr26 + AVC_LPF_P0Q0 vr23, vr21, vr17, vr4, vr2, vr19, vr10, vr14, vr0, vr26 + vpickev.b vr11, vr10, vr11 //p0_h + vpickev.b vr15, vr14, vr15 //q0_h + vbitsel.v vr11, vr12, vr11, vr8 //p0_h + vbitsel.v vr15, vr13, vr15, vr8 //q0_h + vstx vr11, t2, t0 + vst vr15, a0, 0 +.END_V_LUMA_8: + fld.d f24, sp, 0 + fld.d f25, sp, 8 + fld.d f26, sp, 16 + addi.d sp, sp, 24 +endfunc + +const chroma_shuf +.byte 0, 0, 1, 1, 2, 2, 3, 3, 0, 0, 1, 1, 2, 2, 3, 3 +endconst + +function ff_h264_h_lpf_chroma_8_lsx + slli.d t0, a1, 1 //img_width_2x + slli.d t1, a1, 2 //img_width_4x + la.local t4, chroma_shuf + add.d t2, t0, a1 //img_width_3x + vldrepl.w vr0, a4, 0 //tmp_vec0 + vld vr1, t4, 0 //tc_vec + vshuf.b vr1, vr0, vr0, vr1 //tc_vec + vslti.b vr2, vr1, 0 + vxori.b vr2, vr2, 255 + vandi.b vr2, vr2, 1 //bs_vec + vsetnez.v $fcc0, vr2 + bceqz $fcc0, .END_CHROMA_8 + vldi vr0, 0 + addi.d t4, a0, -2 + vslt.bu vr3, vr0, vr2 //is_bs_greater_than0 + add.d t5, t4, t1 + vld vr4, t4, 0 //row0 + vldx vr5, t4, a1 //row1 + vldx vr6, t4, t0 //row2 + vldx vr7, t4, t2 //row3 + vld vr8, t5, 0 //row4 + vldx vr9, t5, a1 //row5 + vldx vr10, t5, t0 //row6 + vldx vr11, t5, t2 //row7 + vilvl.b vr12, vr6, vr4 //p1_org + vilvl.b vr13, vr7, vr5 //p0_org + vilvl.b vr14, vr10, vr8 //q0_org + vilvl.b vr15, vr11, vr9 //q1_org + vilvl.b vr4, vr13, vr12 //row0 + vilvl.b vr5, vr15, vr14 //row1 + vilvl.w vr6, vr5, vr4 //row2 + vilvh.w vr7, vr5, vr4 //row3 + vilvl.d vr12, vr6, vr6 //p1_org + vilvh.d vr13, vr6, vr6 //p0_org + vilvl.d vr14, vr7, vr7 //q0_org + vilvh.d vr15, vr7, vr7 //q1_org + + vabsd.bu vr20, vr13, vr14 //p0_asub_q0 + vabsd.bu vr21, vr12, vr13 //p1_asub_p0 + vabsd.bu vr22, vr15, vr14 //q1_asub_q0 + + vreplgr2vr.b vr4, a2 //alpha + vreplgr2vr.b vr5, a3 //beta + + vslt.bu vr6, vr20, vr4 //is_less_than_alpha + vslt.bu vr7, vr21, vr5 //is_less_than_beta + vand.v vr8, vr6, vr7 //is_less_than + vslt.bu vr7, vr22, vr5 //is_less_than_beta + vand.v vr8, vr7, vr8 //is_less_than + vand.v vr8, vr8, vr3 //is_less_than + vsetnez.v $fcc0, vr8 + bceqz $fcc0, .END_CHROMA_8 + + vneg.b vr9, vr1 //neg_tc_h + vexth.hu.bu vr3, vr12 //p1_org_h + vexth.hu.bu vr4, vr13 //p0_org_h.1 + vexth.hu.bu vr5, vr14 //q0_org_h.1 + vexth.hu.bu vr6, vr15 //q1_org_h.1 + + vexth.hu.bu vr18, vr1 //tc_h.1 + vexth.h.b vr2, vr9 //neg_tc_h.1 + + AVC_LPF_P0Q0 vr5, vr4, vr3, vr6, vr2, vr18, vr10, vr11, vr16, vr17 + vpickev.b vr10, vr10, vr10 //p0_h + vpickev.b vr11, vr11, vr11 //q0_h + vbitsel.v vr13, vr13, vr10, vr8 + vbitsel.v vr14, vr14, vr11, vr8 + vilvl.b vr15, vr14, vr13 + addi.d t4, t4, 1 + add.d t5, t4, a1 + add.d t6, t4, t0 + add.d t7, t4, t2 + vstelm.h vr15, t4, 0, 0 + vstelm.h vr15, t5, 0, 1 + vstelm.h vr15, t6, 0, 2 + vstelm.h vr15, t7, 0, 3 + add.d t4, t4, t1 + add.d t5, t4, a1 + add.d t6, t4, t0 + add.d t7, t4, t2 + vstelm.h vr15, t4, 0, 4 + vstelm.h vr15, t5, 0, 5 + vstelm.h vr15, t6, 0, 6 + vstelm.h vr15, t7, 0, 7 +.END_CHROMA_8: +endfunc + +function ff_h264_v_lpf_chroma_8_lsx + slli.d t0, a1, 1 //img_width_2x + la.local t4, chroma_shuf + vldrepl.w vr0, a4, 0 //tmp_vec0 + vld vr1, t4, 0 //tc_vec + vshuf.b vr1, vr0, vr0, vr1 //tc_vec + vslti.b vr2, vr1, 0 + vxori.b vr2, vr2, 255 + vandi.b vr2, vr2, 1 //bs_vec + vsetnez.v $fcc0, vr2 + bceqz $fcc0, .END_CHROMA_V_8 + vldi vr0, 0 + sub.d t4, a0, t0 + vslt.bu vr3, vr0, vr2 //is_bs_greater_than0 + vld vr12, t4, 0 //p1_org + vldx vr13, t4, a1 //p0_org + vld vr14, a0, 0 //q0_org + vldx vr15, a0, a1 //q1_org + + vabsd.bu vr20, vr13, vr14 //p0_asub_q0 + vabsd.bu vr21, vr12, vr13 //p1_asub_p0 + vabsd.bu vr22, vr15, vr14 //q1_asub_q0 + + vreplgr2vr.b vr4, a2 //alpha + vreplgr2vr.b vr5, a3 //beta + + vslt.bu vr6, vr20, vr4 //is_less_than_alpha + vslt.bu vr7, vr21, vr5 //is_less_than_beta + vand.v vr8, vr6, vr7 //is_less_than + vslt.bu vr7, vr22, vr5 //is_less_than_beta + vand.v vr8, vr7, vr8 //is_less_than + vand.v vr8, vr8, vr3 //is_less_than + vsetnez.v $fcc0, vr8 + bceqz $fcc0, .END_CHROMA_V_8 + + vneg.b vr9, vr1 //neg_tc_h + vsllwil.hu.bu vr3, vr12, 0 //p1_org_h + vsllwil.hu.bu vr4, vr13, 0 //p0_org_h.1 + vsllwil.hu.bu vr5, vr14, 0 //q0_org_h.1 + vsllwil.hu.bu vr6, vr15, 0 //q1_org_h.1 + + vexth.hu.bu vr18, vr1 //tc_h.1 + vexth.h.b vr2, vr9 //neg_tc_h.1 + + AVC_LPF_P0Q0 vr5, vr4, vr3, vr6, vr2, vr18, vr10, vr11, vr16, vr17 + vpickev.b vr10, vr10, vr10 //p0_h + vpickev.b vr11, vr11, vr11 //q0_h + vbitsel.v vr10, vr13, vr10, vr8 + vbitsel.v vr11, vr14, vr11, vr8 + fstx.d f10, t4, a1 + fst.d f11, a0, 0 +.END_CHROMA_V_8: +endfunc + +.macro AVC_LPF_P0P1P2_OR_Q0Q1Q2 _in0, _in1, _in2, _in3, _in4, _in5 \ + _out0, _out1, _out2, _tmp0, _const3 + vadd.h \_tmp0, \_in1, \_in2 + vadd.h \_tmp0, \_tmp0, \_in3 + vslli.h \_out2, \_in0, 1 + vslli.h \_out0, \_tmp0, 1 + vadd.h \_out0, \_out0, \_in4 + vadd.h \_out1, \_in4, \_tmp0 + vadd.h \_out0, \_out0, \_in5 + vmadd.h \_out2, \_in4, \_const3 + vsrar.h \_out0, \_out0, \_const3 + vadd.h \_out2, \_out2, \_tmp0 + vsrari.h \_out1, \_out1, 2 + vsrar.h \_out2, \_out2, \_const3 +.endm + +.macro AVC_LPF_P0_OR_Q0 _in0, _in1, _in2, _out0, _tmp0 + vslli.h \_tmp0, \_in2, 1 + vadd.h \_out0, \_in0, \_in1 + vadd.h \_out0, \_out0, \_tmp0 + vsrari.h \_out0, \_out0, 2 +.endm + +////LSX optimization is sufficient for this function. +function ff_h264_h_lpf_luma_intra_8_lsx + slli.d t0, a1, 1 //img_width_2x + slli.d t1, a1, 2 //img_width_4x + addi.d t4, a0, -4 //src + SAVE_REG + add.d t2, t0, a1 //img_width_3x + add.d t5, t4, t1 + vld vr0, t4, 0 //row0 + vldx vr1, t4, a1 //row1 + vldx vr2, t4, t0 //row2 + vldx vr3, t4, t2 //row3 + add.d t6, t5, t1 + vld vr4, t5, 0 //row4 + vldx vr5, t5, a1 //row5 + vldx vr6, t5, t0 //row6 + vldx vr7, t5, t2 //row7 + add.d t7, t6, t1 + vld vr8, t6, 0 //row8 + vldx vr9, t6, a1 //row9 + vldx vr10, t6, t0 //row10 + vldx vr11, t6, t2 //row11 + vld vr12, t7, 0 //row12 + vldx vr13, t7, a1 //row13 + vldx vr14, t7, t0 //row14 + vldx vr15, t7, t2 //row15 + LSX_TRANSPOSE16X8_B vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr8, vr9, vr10, vr11, vr12, vr13, vr14, vr15, \ + vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + // vr0: p3_org, vr1: p2_org, vr2: p1_org, vr3: p0_org + // vr4: q0_org, vr5: q1_org, vr6: q2_org, vr7: q3_org + + vreplgr2vr.b vr16, a2 //alpha_in + vreplgr2vr.b vr17, a3 //beta_in + vabsd.bu vr10, vr3, vr4 //p0_asub_q0 + vabsd.bu vr11, vr2, vr3 //p1_asub_p0 + vabsd.bu vr12, vr5, vr4 //q1_asub_q0 + + vslt.bu vr8, vr10, vr16 //is_less_than_alpha + vslt.bu vr9, vr11, vr17 //is_less_than_beta + vand.v vr18, vr8, vr9 //is_less_than + vslt.bu vr9, vr12, vr17 //is_less_than_beta + vand.v vr18, vr18, vr9 //is_less_than + + vsetnez.v $fcc0, vr18 + bceqz $fcc0, .END_H_INTRA_8 + vsrli.b vr16, vr16, 2 //less_alpha_shift2_add2 + vaddi.bu vr16, vr16, 2 + vslt.bu vr16, vr10, vr16 + vsllwil.hu.bu vr10, vr2, 0 //p1_org_h.0 + vexth.hu.bu vr11, vr2 //p1_org_h.1 + vsllwil.hu.bu vr12, vr3, 0 //p0_org_h.0 + vexth.hu.bu vr13, vr3 //p0_org_h.1 + + vsllwil.hu.bu vr14, vr4, 0 //q0_org_h.0 + vexth.hu.bu vr15, vr4 //q0_org_h.1 + vsllwil.hu.bu vr19, vr5, 0 //q1_org_h.0 + vexth.hu.bu vr20, vr5 //q1_org_h.1 + + vabsd.bu vr21, vr1, vr3 //p2_asub_p0 + vslt.bu vr9, vr21, vr17 //is_less_than_beta + vand.v vr9, vr9, vr16 + vxori.b vr22, vr9, 0xff //negate_is_less_than_beta + vand.v vr9, vr9, vr18 + vand.v vr22, vr22, vr18 + + vsetnez.v $fcc0, vr9 + bceqz $fcc0, .END_H_INTRA_LESS_BETA + vsllwil.hu.bu vr23, vr1, 0 //p2_org_h.0 + vexth.hu.bu vr24, vr1 //p2_org_h.1 + vsllwil.hu.bu vr25, vr0, 0 //p3_org_h.0 + vexth.hu.bu vr26, vr0 //p3_org_h.1 + vldi vr27, 0x403 + + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr25, vr12, vr14, vr10, vr23, vr19, vr28, vr29, vr30, vr31, vr27 + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr26, vr13, vr15, vr11, vr24, vr20, vr23, vr25, vr21, vr31, vr27 + vpickev.b vr28, vr23, vr28 //p0_h + vpickev.b vr29, vr25, vr29 //p1_h + vpickev.b vr30, vr21, vr30 //p2_h + vbitsel.v vr3, vr3, vr28, vr9 + vbitsel.v vr2, vr2, vr29, vr9 + vbitsel.v vr1, vr1, vr30, vr9 +.END_H_INTRA_LESS_BETA: + AVC_LPF_P0_OR_Q0 vr12, vr19, vr10, vr23, vr25 + AVC_LPF_P0_OR_Q0 vr13, vr20, vr11, vr24, vr25 + //vr23: p0_h.0 vr24: p0_h.1 + vpickev.b vr23, vr24, vr23 + vbitsel.v vr3, vr3, vr23, vr22 + + vabsd.bu vr21, vr6, vr4 //q2_asub_q0 + vslt.bu vr9, vr21, vr17 //is_less_than_beta + vand.v vr9, vr9, vr16 + vxori.b vr22, vr9, 0xff //negate_is_less_than_beta + vand.v vr9, vr9, vr18 + vand.v vr22, vr22, vr18 + + vsetnez.v $fcc0, vr9 + bceqz $fcc0, .END_H_INTRA_LESS_BETA_SEC + vsllwil.hu.bu vr23, vr6, 0 //q2_org_h.0 + vexth.hu.bu vr24, vr6 //q2_org_h.1 + vsllwil.hu.bu vr25, vr7, 0 //q3_org_h.0 + vexth.hu.bu vr26, vr7 //q3_org_h.1 + vldi vr27, 0x403 + + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr25, vr14, vr12, vr19, vr23, vr10, vr28, vr29, vr30, vr31, vr27 + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr26, vr15, vr13, vr20, vr24, vr11, vr23, vr25, vr21, vr31, vr27 + vpickev.b vr28, vr23, vr28 //q0_h + vpickev.b vr29, vr25, vr29 //q1_h + vpickev.b vr30, vr21, vr30 //q2_h + vbitsel.v vr4, vr4, vr28, vr9 + vbitsel.v vr5, vr5, vr29, vr9 + vbitsel.v vr6, vr6, vr30, vr9 +.END_H_INTRA_LESS_BETA_SEC: + AVC_LPF_P0_OR_Q0 vr14, vr10, vr19, vr23, vr25 + AVC_LPF_P0_OR_Q0 vr15, vr11, vr20, vr24, vr25 + vpickev.b vr23, vr24, vr23 + vbitsel.v vr4, vr4, vr23, vr22 + + vilvl.b vr14, vr2, vr0 // row0.0 + vilvl.b vr15, vr6, vr4 // row0.1 + vilvl.b vr16, vr3, vr1 // row2.0 + vilvl.b vr17, vr7, vr5 // row2.1 + + vilvh.b vr18, vr2, vr0 // row1.0 + vilvh.b vr19, vr6, vr4 // row1.1 + vilvh.b vr20, vr3, vr1 // row3.0 + vilvh.b vr21, vr7, vr5 // row3.1 + + vilvl.b vr2, vr16, vr14 // row4.0 + vilvl.b vr3, vr17, vr15 // row4.1 + vilvl.b vr4, vr20, vr18 // row6.0 + vilvl.b vr5, vr21, vr19 // row6.1 + + vilvh.b vr6, vr16, vr14 // row5.0 + vilvh.b vr7, vr17, vr15 // row5.1 + vilvh.b vr8, vr20, vr18 // row7.0 + vilvh.b vr9, vr21, vr19 // row7.1 + + vilvl.w vr14, vr3, vr2 // row4: 0, 4, 1, 5 + vilvh.w vr15, vr3, vr2 // row4: 2, 6, 3, 7 + vilvl.w vr16, vr7, vr6 // row5: 0, 4, 1, 5 + vilvh.w vr17, vr7, vr6 // row5: 2, 6, 3, 7 + + vilvl.w vr18, vr5, vr4 // row6: 0, 4, 1, 5 + vilvh.w vr19, vr5, vr4 // row6: 2, 6, 3, 7 + vilvl.w vr20, vr9, vr8 // row7: 0, 4, 1, 5 + vilvh.w vr21, vr9, vr8 // row7: 2, 6, 3, 7 + + vbsrl.v vr0, vr14, 8 + vbsrl.v vr1, vr15, 8 + vbsrl.v vr2, vr16, 8 + vbsrl.v vr3, vr17, 8 + + vbsrl.v vr4, vr18, 8 + vbsrl.v vr5, vr19, 8 + vbsrl.v vr6, vr20, 8 + vbsrl.v vr7, vr21, 8 + + store_double f14, f0, f15, f1, t4, a1, t0, t2 + store_double f16, f2, f17, f3, t5, a1, t0, t2 + store_double f18, f4, f19, f5, t6, a1, t0, t2 + store_double f20, f6, f21, f7, t7, a1, t0, t2 +.END_H_INTRA_8: + RESTORE_REG +endfunc + +//LSX optimization is sufficient for this function. +function ff_h264_v_lpf_luma_intra_8_lsx + slli.d t0, a1, 1 //img_width_2x + add.d t1, t0, a1 //img_width_3x + SAVE_REG + sub.d t4, a0, t1 //src - img_width_3x + + vld vr0, a0, 0 //q0_org + vldx vr1, a0, a1 //q1_org + vldx vr2, t4, a1 //p1_org + vldx vr3, t4, t0 //p0_org + + vreplgr2vr.b vr4, a2 //alpha + vreplgr2vr.b vr5, a3 //beta + + vabsd.bu vr6, vr3, vr0 //p0_asub_q0 + vabsd.bu vr7, vr2, vr3 //p1_asub_p0 + vabsd.bu vr8, vr1, vr0 //q1_asub_q0 + + vslt.bu vr9, vr6, vr4 //is_less_than_alpha + vslt.bu vr10, vr7, vr5 //is_less_than_beta + vand.v vr11, vr9, vr10 //is_less_than + vslt.bu vr10, vr8, vr5 + vand.v vr11, vr10, vr11 + + vsetnez.v $fcc0, vr11 + bceqz $fcc0, .END_V_INTRA_8 + + vld vr12, t4, 0 //p2_org + vldx vr13, a0, t0 //q2_org + vsrli.b vr14, vr4, 2 //is_alpha_shift2_add2 + vsllwil.hu.bu vr15, vr2, 0 //p1_org_h.0 + vexth.hu.bu vr16, vr2 //p1_org_h.1 + vaddi.bu vr14, vr14, 2 + vsllwil.hu.bu vr17, vr3, 0 //p0_org_h.0 + vexth.hu.bu vr18, vr3 //p0_org_h.1 + vslt.bu vr14, vr6, vr14 + vsllwil.hu.bu vr19, vr0, 0 //q0_org_h.0 + vexth.hu.bu vr20, vr0 //q0_org_h.1 + vsllwil.hu.bu vr21, vr1, 0 //q1_org_h.0 + vexth.hu.bu vr22, vr1 //q1_org_h.1 + + vabsd.bu vr23, vr12, vr3 //p2_asub_p0 + vslt.bu vr10, vr23, vr5 //is_less_than_beta + vand.v vr10, vr10, vr14 + vxori.b vr23, vr10, 0xff //negate_is_less_than_beta + vand.v vr10, vr10, vr11 + vand.v vr23, vr23, vr11 + + vsetnez.v $fcc0, vr10 + bceqz $fcc0, .END_V_INTRA_LESS_BETA + sub.d t5, t4, a1 + vld vr24, t5, 0 //p3_org + vsllwil.hu.bu vr26, vr12, 0 //p2_org_h.0 + vexth.hu.bu vr27, vr12 //p2_org_h.1 + vsllwil.hu.bu vr28, vr24, 0 //p3_org_h.0 + vexth.hu.bu vr29, vr24 //p3_org_h.1 + vldi vr4, 0x403 + + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr28, vr17, vr19, vr15, vr26, vr21, vr25, vr30, vr31, vr24, vr4 + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr29, vr18, vr20, vr16, vr27, vr22, vr6, vr7, vr8, vr24, vr4 + + vpickev.b vr25, vr6, vr25 //p0_h + vpickev.b vr30, vr7, vr30 //p1_h + vpickev.b vr31, vr8, vr31 //p2_h + + vbitsel.v vr3, vr3, vr25, vr10 + vbitsel.v vr2, vr2, vr30, vr10 + vbitsel.v vr12, vr12, vr31, vr10 + + vstx vr2, t4, a1 + vst vr12, t4, 0 +.END_V_INTRA_LESS_BETA: + AVC_LPF_P0_OR_Q0 vr17, vr21, vr15, vr24, vr30 + AVC_LPF_P0_OR_Q0 vr18, vr22, vr16, vr25, vr30 + vpickev.b vr24, vr25, vr24 + vbitsel.v vr3, vr3, vr24, vr23 + vstx vr3, t4, t0 + + vabsd.bu vr23, vr13, vr0 //q2_asub_q0 + vslt.bu vr10, vr23, vr5 //is_less_than_beta + vand.v vr10, vr10, vr14 + vxori.b vr23, vr10, 0xff //negate_is_less_than_beta + vand.v vr10, vr10, vr11 + vand.v vr23, vr23, vr11 + + vsetnez.v $fcc0, vr10 + bceqz $fcc0, .END_V_INTRA_LESS_BETA_SEC + vldx vr24, a0, t1 //q3_org + + vsllwil.hu.bu vr26, vr13, 0 //q2_org_h.0 + vexth.hu.bu vr27, vr13 //q2_org_h.1 + vsllwil.hu.bu vr28, vr24, 0 //q3_org_h.0 + vexth.hu.bu vr29, vr24 //q3_org_h.1 + vldi vr4, 0x403 + + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr28, vr19, vr17, vr21, vr26, vr15, vr25, vr30, vr31, vr24, vr4 + AVC_LPF_P0P1P2_OR_Q0Q1Q2 vr29, vr20, vr18, vr22, vr27, vr16, vr6, vr7, vr8, vr24, vr4 + + vpickev.b vr25, vr6, vr25 + vpickev.b vr30, vr7, vr30 + vpickev.b vr31, vr8, vr31 + + vbitsel.v vr0, vr0, vr25, vr10 + vbitsel.v vr1, vr1, vr30, vr10 + vbitsel.v vr13, vr13, vr31, vr10 + vstx vr1, a0, a1 + vstx vr13, a0, t0 +.END_V_INTRA_LESS_BETA_SEC: + AVC_LPF_P0_OR_Q0 vr19, vr15, vr21, vr24, vr30 + AVC_LPF_P0_OR_Q0 vr20, vr16, vr22, vr25, vr30 + vpickev.b vr24, vr25, vr24 + vbitsel.v vr0, vr0, vr24, vr23 + vst vr0, a0, 0 +.END_V_INTRA_8: + RESTORE_REG +endfunc + +function ff_h264_h_lpf_chroma_intra_8_lsx + addi.d t4, a0, -2 + slli.d t0, a1, 1 //img_2x + slli.d t2, a1, 2 //img_4x + add.d t1, t0, a1 //img_3x + + add.d t5, t4, t2 + fld.s f0, t4, 0 //row0 + fldx.s f1, t4, a1 //row1 + fldx.s f2, t4, t0 //row2 + fldx.s f3, t4, t1 //row3 + fld.s f4, t5, 0 //row4 + fldx.s f5, t5, a1 //row5 + fldx.s f6, t5, t0 //row6 + fldx.s f7, t5, t1 //row7 + + vilvl.b vr8, vr2, vr0 //p1_org + vilvl.b vr9, vr3, vr1 //p0_org + vilvl.b vr10, vr6, vr4 //q0_org + vilvl.b vr11, vr7, vr5 //q1_org + + vilvl.b vr0, vr9, vr8 + vilvl.b vr1, vr11, vr10 + vilvl.w vr2, vr1, vr0 + vilvh.w vr3, vr1, vr0 + + vilvl.d vr8, vr2, vr2 //p1_org + vilvh.d vr9, vr2, vr2 //p0_org + vilvl.d vr10, vr3, vr3 //q0_org + vilvh.d vr11, vr3, vr3 //q1_org + + vreplgr2vr.b vr0, a2 //alpha + vreplgr2vr.b vr1, a3 //beta + + vabsd.bu vr2, vr9, vr10 //p0_asub_q0 + vabsd.bu vr3, vr8, vr9 //p1_asub_p0 + vabsd.bu vr4, vr11, vr10 //q1_asub_q0 + + vslt.bu vr5, vr2, vr0 //is_less_than_alpha + vslt.bu vr6, vr3, vr1 //is_less_than_beta + vand.v vr7, vr5, vr6 //is_less_than + vslt.bu vr6, vr4, vr1 + vand.v vr7, vr7, vr6 + + vsetnez.v $fcc0, vr7 + bceqz $fcc0, .END_H_CHROMA_INTRA_8 + + vexth.hu.bu vr12, vr8 //p1_org_h + vexth.hu.bu vr13, vr9 //p0_org_h + vexth.hu.bu vr14, vr10 //q0_org_h + vexth.hu.bu vr15, vr11 //q1_org_h + + AVC_LPF_P0_OR_Q0 vr13, vr15, vr12, vr16, vr18 + AVC_LPF_P0_OR_Q0 vr14, vr12, vr15, vr17, vr18 + + vpickev.b vr18, vr16, vr16 + vpickev.b vr19, vr17, vr17 + vbitsel.v vr9, vr9, vr18, vr7 + vbitsel.v vr10, vr10, vr19, vr7 +.END_H_CHROMA_INTRA_8: + vilvl.b vr11, vr10, vr9 + addi.d t4, t4, 1 + vstelm.h vr11, t4, 0, 0 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 1 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 2 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 3 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 4 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 5 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 6 + add.d t4, t4, a1 + vstelm.h vr11, t4, 0, 7 +endfunc + +function ff_h264_v_lpf_chroma_intra_8_lsx + slli.d t0, a1, 1 //img_width_2x + sub.d t2, a0, a1 + sub.d t1, a0, t0 //data - img_width_2x + + vreplgr2vr.b vr0, a2 + vreplgr2vr.b vr1, a3 + + vld vr2, t1, 0 //p1_org + vldx vr3, t1, a1 //p0_org + vld vr4, a0, 0 //q0_org + vldx vr5, a0, a1 //q1_org + + vabsd.bu vr6, vr3, vr4 //p0_asub_q0 + vabsd.bu vr7, vr2, vr3 //p1_asub_p0 + vabsd.bu vr8, vr5, vr4 //q1_asub_q0 + + vslt.bu vr9, vr6, vr0 //is_less_than_alpha + vslt.bu vr10, vr7, vr1 //is_less_than_beta + vand.v vr11, vr9, vr10 //is_less_than + vslt.bu vr10, vr8, vr1 + vand.v vr11, vr10, vr11 + + vsetnez.v $fcc0, vr11 + bceqz $fcc0, .END_V_CHROMA_INTRA_8 + + vsllwil.hu.bu vr6, vr2, 0 //p1_org_h.0 + vsllwil.hu.bu vr8, vr3, 0 //p0_org_h.0 + vsllwil.hu.bu vr13, vr4, 0 //q0_org_h.0 + vsllwil.hu.bu vr15, vr5, 0 //q1_org_h.0 + + AVC_LPF_P0_OR_Q0 vr8, vr15, vr6, vr17, vr23 + AVC_LPF_P0_OR_Q0 vr13, vr6, vr15, vr18, vr23 + + vpickev.b vr19, vr17, vr17 + vpickev.b vr20, vr18, vr18 + vbitsel.v vr3, vr3, vr19, vr11 + vbitsel.v vr4, vr4, vr20, vr11 + + vstelm.d vr3, t2, 0, 0 + vstelm.d vr4, a0, 0, 0 +.END_V_CHROMA_INTRA_8: +endfunc + +.macro biweight_calc _in0, _in1, _in2, _in3, _reg0, _reg1, _reg2,\ + _out0, _out1, _out2, _out3 + vmov \_out0, \_reg0 + vmov \_out1, \_reg0 + vmov \_out2, \_reg0 + vmov \_out3, \_reg0 + vmaddwev.h.bu.b \_out0, \_in0, \_reg1 + vmaddwev.h.bu.b \_out1, \_in1, \_reg1 + vmaddwev.h.bu.b \_out2, \_in2, \_reg1 + vmaddwev.h.bu.b \_out3, \_in3, \_reg1 + vmaddwod.h.bu.b \_out0, \_in0, \_reg1 + vmaddwod.h.bu.b \_out1, \_in1, \_reg1 + vmaddwod.h.bu.b \_out2, \_in2, \_reg1 + vmaddwod.h.bu.b \_out3, \_in3, \_reg1 + + vssran.bu.h \_out0, \_out0, \_reg2 + vssran.bu.h \_out1, \_out1, \_reg2 + vssran.bu.h \_out2, \_out2, \_reg2 + vssran.bu.h \_out3, \_out3, \_reg2 +.endm + +.macro biweight_load_8 + load_double f0, f1, f2, f3, a1, a2, t0, t1 + load_double f10, f11, f12, f13, a0, a2, t0, t1 + + vilvl.d vr0, vr1, vr0 //src0 + vilvl.d vr2, vr3, vr2 //src2 + vilvl.d vr10, vr11, vr10 //dst0 + vilvl.d vr12, vr13, vr12 //dst2 + + vilvl.b vr1, vr10, vr0 //vec0.0 + vilvh.b vr3, vr10, vr0 //vec0.1 + vilvl.b vr5, vr12, vr2 //vec1.0 + vilvh.b vr7, vr12, vr2 //vec1.1 +.endm + +.macro biweight_8 + biweight_calc vr1, vr3, vr5, vr7, vr8, vr20, vr9, vr0, vr2, vr4, vr6 + vilvl.d vr0, vr2, vr0 + vilvl.d vr2, vr6, vr4 + + vbsrl.v vr1, vr0, 8 + vbsrl.v vr3, vr2, 8 + + store_double f0, f1, f2, f3, a0, a2, t0, t1 +.endm + +.macro biweight_load2_8 + biweight_load_8 + load_double f0, f2, f4, f6, t4, a2, t0, t1 + load_double f14, f15, f16, f17, t5, a2, t0, t1 + + vilvl.d vr0, vr2, vr0 //src4 + vilvl.d vr4, vr6, vr4 //src6 + vilvl.d vr14, vr15, vr14 //dst4 + vilvl.d vr16, vr17, vr16 //dst6 + + vilvl.b vr11, vr14, vr0 //vec4.0 + vilvh.b vr13, vr14, vr0 //vec4.1 + vilvl.b vr15, vr16, vr4 //vec6.0 + vilvh.b vr17, vr16, vr4 //vec6.1 +.endm + +.macro biweight2_8 + biweight_8 + biweight_calc vr11, vr13, vr15, vr17, vr8, vr20, vr9, \ + vr10, vr12, vr14, vr16 + vilvl.d vr10, vr12, vr10 + vilvl.d vr12, vr16, vr14 + + vbsrl.v vr11, vr10, 8 + vbsrl.v vr13, vr12, 8 + + store_double f10, f11, f12, f13, t5, a2, t0, t1 +.endm + +.macro biweight_load_16 + add.d t4, a1, t2 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + vld vr4, t4, 0 + vldx vr5, t4, a2 + vldx vr6, t4, t0 + vldx vr7, t4, t1 + + add.d t5, a0, t2 + vld vr10, a0, 0 + vldx vr11, a0, a2 + vldx vr12, a0, t0 + vldx vr13, a0, t1 + vld vr14, t5, 0 + vldx vr15, t5, a2 + vldx vr16, t5, t0 + vldx vr17, t5, t1 + + vilvl.b vr18, vr10, vr0 + vilvl.b vr19, vr11, vr1 + vilvl.b vr21, vr12, vr2 + vilvl.b vr22, vr13, vr3 + vilvh.b vr0, vr10, vr0 + vilvh.b vr1, vr11, vr1 + vilvh.b vr2, vr12, vr2 + vilvh.b vr3, vr13, vr3 + + vilvl.b vr10, vr14, vr4 + vilvl.b vr11, vr15, vr5 + vilvl.b vr12, vr16, vr6 + vilvl.b vr13, vr17, vr7 + vilvh.b vr14, vr14, vr4 + vilvh.b vr15, vr15, vr5 + vilvh.b vr16, vr16, vr6 + vilvh.b vr17, vr17, vr7 +.endm + +.macro biweight_16 + biweight_calc vr18, vr19, vr21, vr22, vr8, vr20, vr9, vr4, vr5, vr6, vr7 + biweight_calc vr0, vr1, vr2, vr3, vr8, vr20, vr9, vr18, vr19, vr21, vr22 + biweight_calc vr10, vr11, vr12, vr13, vr8, vr20, vr9, vr0, vr1, vr2, vr3 + biweight_calc vr14, vr15, vr16, vr17, vr8, vr20, vr9, vr10, vr11, vr12, vr13 + + vilvl.d vr4, vr18, vr4 + vilvl.d vr5, vr19, vr5 + vilvl.d vr6, vr21, vr6 + vilvl.d vr7, vr22, vr7 + vilvl.d vr0, vr10, vr0 + vilvl.d vr1, vr11, vr1 + vilvl.d vr2, vr12, vr2 + vilvl.d vr3, vr13, vr3 + + vst vr4, a0, 0 + vstx vr5, a0, a2 + vstx vr6, a0, t0 + vstx vr7, a0, t1 + vst vr0, t5, 0 + vstx vr1, t5, a2 + vstx vr2, t5, t0 + vstx vr3, t5, t1 +.endm + +.macro biweight_func w +function ff_biweight_h264_pixels\w\()_8_lsx + slli.d t0, a2, 1 + slli.d t2, a2, 2 + add.d t1, t0, a2 + addi.d a7, a7, 1 + ori a7, a7, 1 + sll.d a7, a7, a4 + addi.d a4, a4, 1 + + vreplgr2vr.b vr0, a6 //tmp0 + vreplgr2vr.b vr1, a5 //tmp1 + vreplgr2vr.h vr8, a7 //offset + vreplgr2vr.h vr9, a4 //denom + vilvh.b vr20, vr1, vr0 //wgt +.endm + +biweight_func 8 + addi.d t3, zero, 8 + biweight_load_8 + biweight_8 + blt a3, t3, .END_BIWEIGHT_H264_PIXELS8 + addi.d t3, zero, 16 + add.d a1, a1, t2 + add.d a0, a0, t2 + biweight_load_8 + biweight_8 + blt a3, t3, .END_BIWEIGHT_H264_PIXELS8 + add.d a1, a1, t2 + add.d a0, a0, t2 + add.d t4, a1, t2 + add.d t5, a0, t2 + biweight_load2_8 + biweight2_8 +.END_BIWEIGHT_H264_PIXELS8: +endfunc + +biweight_func 16 + addi.d t6, zero, 16 + biweight_load_16 + biweight_16 + + bne a3, t6, .END_BIWEIGHT_PIXELS16 + add.d a1, t4, t2 + add.d a0, t5, t2 + biweight_load_16 + biweight_16 +.END_BIWEIGHT_PIXELS16: +endfunc + +.macro biweight_calc_4 _in0, _out0 + vmov \_out0, vr8 + vmaddwev.h.bu.b \_out0, \_in0, vr20 + vmaddwod.h.bu.b \_out0, \_in0, vr20 + vssran.bu.h \_out0, \_out0, vr9 +.endm + +//LSX optimization is sufficient for this function. +biweight_func 4 + addi.d t3, zero, 4 + fld.s f0, a1, 0 + fldx.s f1, a1, a2 + fld.s f10, a0, 0 + fldx.s f11, a0, a2 + vilvl.w vr2, vr1, vr0 + vilvl.w vr12, vr11, vr10 + vilvl.b vr0, vr12, vr2 + + biweight_calc_4 vr0, vr1 + vbsrl.v vr2, vr1, 4 + fst.s f1, a0, 0 + fstx.s f2, a0, a2 + + blt a3, t3, .END_BIWEIGHT_H264_PIXELS4 + addi.d t3, zero, 8 + fldx.s f0, a1, t0 + fldx.s f1, a1, t1 + fldx.s f10, a0, t0 + fldx.s f11, a0, t1 + vilvl.w vr2, vr1, vr0 + vilvl.w vr12, vr11, vr10 + vilvl.b vr0, vr12, vr2 + + biweight_calc_4 vr0, vr1 + vbsrl.v vr2, vr1, 4 + fstx.s f1, a0, t0 + fstx.s f2, a0, t1 + blt a3, t3, .END_BIWEIGHT_H264_PIXELS4 + add.d a1, a1, t2 + add.d a0, a0, t2 + fld.s f0, a1, 0 + fldx.s f1, a1, a2 + fldx.s f2, a1, t0 + fldx.s f3, a1, t1 + fld.s f10, a0, 0 + fldx.s f11, a0, a2 + fldx.s f12, a0, t0 + fldx.s f13, a0, t1 + vilvl.w vr4, vr1, vr0 + vilvl.w vr5, vr3, vr2 + vilvl.w vr14, vr11, vr10 + vilvl.w vr15, vr13, vr12 + + vilvl.b vr0, vr14, vr4 + vilvl.b vr10, vr15, vr5 + + vmov vr1, vr8 + vmov vr11, vr8 + vmaddwev.h.bu.b vr1, vr0, vr20 + vmaddwev.h.bu.b vr11, vr10, vr20 + vmaddwod.h.bu.b vr1, vr0, vr20 + vmaddwod.h.bu.b vr11, vr10, vr20 + + vssran.bu.h vr0, vr1, vr9 //vec0 + vssran.bu.h vr10, vr11, vr9 //vec0 + vbsrl.v vr2, vr0, 4 + vbsrl.v vr12, vr10, 4 + + fst.s f0, a0, 0 + fstx.s f2, a0, a2 + fstx.s f10, a0, t0 + fstx.s f12, a0, t1 +.END_BIWEIGHT_H264_PIXELS4: +endfunc + +.macro biweight_func_lasx w +function ff_biweight_h264_pixels\w\()_8_lasx + slli.d t0, a2, 1 + slli.d t2, a2, 2 + add.d t1, t0, a2 + addi.d a7, a7, 1 + ori a7, a7, 1 + sll.d a7, a7, a4 + addi.d a4, a4, 1 + + xvreplgr2vr.b xr0, a6 //tmp0 + xvreplgr2vr.b xr1, a5 //tmp1 + xvreplgr2vr.h xr8, a7 //offset + xvreplgr2vr.h xr9, a4 //denom + xvilvh.b xr20, xr1, xr0 //wgt +.endm + +.macro biweight_calc_lasx _in0, _in1, _reg0, _reg1, _reg2, _out0, _out1 + xmov \_out0, \_reg0 + xmov \_out1, \_reg0 + xvmaddwev.h.bu.b \_out0, \_in0, \_reg1 + xvmaddwev.h.bu.b \_out1, \_in1, \_reg1 + xvmaddwod.h.bu.b \_out0, \_in0, \_reg1 + xvmaddwod.h.bu.b \_out1, \_in1, \_reg1 + + xvssran.bu.h \_out0, \_out0, \_reg2 + xvssran.bu.h \_out1, \_out1, \_reg2 +.endm + +.macro biweight_load_lasx_8 + load_double f0, f1, f2, f3, a1, a2, t0, t1 + load_double f10, f11, f12, f13, a0, a2, t0, t1 + + vilvl.d vr0, vr1, vr0 //src0 + vilvl.d vr2, vr3, vr2 //src2 + vilvl.d vr10, vr11, vr10 //dst0 + vilvl.d vr12, vr13, vr12 //dst2 + + xvpermi.q xr2, xr0, 0x20 + xvpermi.q xr12, xr10, 0x20 + + xvilvl.b xr0, xr12, xr2 + xvilvh.b xr1, xr12, xr2 +.endm + +.macro biweight_lasx_8 + biweight_calc_lasx xr0, xr1, xr8, xr20, xr9, xr2, xr3 + xvilvl.d xr0, xr3, xr2 + xvpermi.d xr2, xr0, 0x4E + vbsrl.v vr1, vr0, 8 + vbsrl.v vr3, vr2, 8 + + store_double f0, f1, f2, f3, a0, a2, t0, t1 +.endm + +biweight_func_lasx 8 + addi.d t3, zero, 8 + biweight_load_lasx_8 + biweight_lasx_8 + blt a3, t3, .END_BIWEIGHT_H264_PIXELS8_LASX + addi.d t3, zero, 16 + add.d a1, a1, t2 + add.d a0, a0, t2 + biweight_load_lasx_8 + biweight_lasx_8 + blt a3, t3, .END_BIWEIGHT_H264_PIXELS8_LASX + add.d a1, a1, t2 + add.d a0, a0, t2 + add.d t4, a1, t2 + add.d t5, a0, t2 + biweight_load_lasx_8 + load_double f4, f5, f6, f7, t4, a2, t0, t1 + load_double f14, f15, f16, f17, t5, a2, t0, t1 + vilvl.d vr4, vr5, vr4 //src4 + vilvl.d vr6, vr7, vr6 //src6 + vilvl.d vr14, vr15, vr14 //dst4 + vilvl.d vr16, vr17, vr16 //dst6 + xvpermi.q xr6, xr4, 0x20 + xvpermi.q xr16, xr14, 0x20 + xvilvl.b xr10, xr16, xr6 + xvilvh.b xr11, xr16, xr6 + biweight_lasx_8 + biweight_calc_lasx xr10, xr11, xr8, xr20, xr9, xr12, xr13 + xvilvl.d xr10, xr13, xr12 + xvpermi.d xr12, xr10, 0x4E + vbsrl.v vr11, vr10, 8 + vbsrl.v vr13, vr12, 8 + store_double f10, f11, f12, f13, t5, a2, t0, t1 +.END_BIWEIGHT_H264_PIXELS8_LASX: +endfunc + +.macro biweight_load_lasx_16 + add.d t4, a1, t2 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + vld vr4, t4, 0 + vldx vr5, t4, a2 + vldx vr6, t4, t0 + vldx vr7, t4, t1 + + add.d t5, a0, t2 + vld vr10, a0, 0 + vldx vr11, a0, a2 + vldx vr12, a0, t0 + vldx vr13, a0, t1 + vld vr14, t5, 0 + vldx vr15, t5, a2 + vldx vr16, t5, t0 + vldx vr17, t5, t1 + + xvpermi.q xr1, xr0, 0x20 + xvpermi.q xr3, xr2, 0x20 + xvpermi.q xr5, xr4, 0x20 + xvpermi.q xr7, xr6, 0x20 + + xvpermi.q xr11, xr10, 0x20 + xvpermi.q xr13, xr12, 0x20 + xvpermi.q xr15, xr14, 0x20 + xvpermi.q xr17, xr16, 0x20 + + xvilvl.b xr0, xr11, xr1 //vec0 + xvilvl.b xr2, xr13, xr3 //vec2 + xvilvl.b xr4, xr15, xr5 //vec4 + xvilvl.b xr6, xr17, xr7 //vec6 + + xvilvh.b xr10, xr11, xr1 //vec1 + xvilvh.b xr12, xr13, xr3 //vec2 + xvilvh.b xr14, xr15, xr5 //vec5 + xvilvh.b xr16, xr17, xr7 //vec7 +.endm + +.macro biweight_lasx_16 + biweight_calc_lasx xr0, xr2, xr8, xr20, xr9, xr1, xr3 + biweight_calc_lasx xr4, xr6, xr8, xr20, xr9, xr5, xr7 + biweight_calc_lasx xr10, xr12, xr8, xr20, xr9, xr11, xr13 + biweight_calc_lasx xr14, xr16, xr8, xr20, xr9, xr15, xr17 + xvilvl.d xr0, xr11, xr1 + xvilvl.d xr2, xr13, xr3 + xvilvl.d xr4, xr15, xr5 + xvilvl.d xr6, xr17, xr7 + + xvpermi.d xr1, xr0, 0x4E + xvpermi.d xr3, xr2, 0x4E + xvpermi.d xr5, xr4, 0x4E + xvpermi.d xr7, xr6, 0x4E + vst vr0, a0, 0 + vstx vr1, a0, a2 + vstx vr2, a0, t0 + vstx vr3, a0, t1 + vst vr4, t5, 0 + vstx vr5, t5, a2 + vstx vr6, t5, t0 + vstx vr7, t5, t1 +.endm + +biweight_func_lasx 16 + addi.d t6, zero, 16 + biweight_load_lasx_16 + biweight_lasx_16 + bne a3, t6, .END_BIWEIGHT_PIXELS16_LASX + add.d a1, t4, t2 + add.d a0, t5, t2 + biweight_load_lasx_16 + biweight_lasx_16 +.END_BIWEIGHT_PIXELS16_LASX: +endfunc + +.macro weight_func w +function ff_weight_h264_pixels\w\()_8_lsx + slli.d t0, a1, 1 + slli.d t2, a1, 2 + add.d t1, t0, a1 + + sll.d a5, a5, a3 + vreplgr2vr.h vr20, a4 //weight + vreplgr2vr.h vr8, a5 //offset + vreplgr2vr.h vr9, a3 //log2_denom +.endm + +.macro weight_load_16 + add.d t4, a0, t2 + vld vr0, a0, 0 + vldx vr1, a0, a1 + vldx vr2, a0, t0 + vldx vr3, a0, t1 + vld vr4, t4, 0 + vldx vr5, t4, a1 + vldx vr6, t4, t0 + vldx vr7, t4, t1 + + vilvl.b vr10, vr23, vr0 + vilvl.b vr11, vr23, vr1 + vilvl.b vr12, vr23, vr2 + vilvl.b vr13, vr23, vr3 + vilvl.b vr14, vr23, vr4 + vilvl.b vr15, vr23, vr5 + vilvl.b vr16, vr23, vr6 + vilvl.b vr17, vr23, vr7 +.endm + +.macro weight_extend_16 + vilvl.b vr10, vr23, vr0 + vilvl.b vr11, vr23, vr1 + vilvl.b vr12, vr23, vr2 + vilvl.b vr13, vr23, vr3 + vilvl.b vr14, vr23, vr4 + vilvl.b vr15, vr23, vr5 + vilvl.b vr16, vr23, vr6 + vilvl.b vr17, vr23, vr7 + + vilvh.b vr18, vr23, vr0 + vilvh.b vr19, vr23, vr1 + vilvh.b vr21, vr23, vr2 + vilvh.b vr22, vr23, vr3 + vilvh.b vr0, vr23, vr4 + vilvh.b vr1, vr23, vr5 + vilvh.b vr2, vr23, vr6 + vilvh.b vr3, vr23, vr7 +.endm + +.macro weight_calc _in0, _in1, _in2, _in3, _reg0, _reg1, _reg2, \ + _out0, _out1, _out2, _out3 + vmul.h \_in0, \_in0, \_reg1 + vmul.h \_in1, \_in1, \_reg1 + vmul.h \_in2, \_in2, \_reg1 + vmul.h \_in3, \_in3, \_reg1 + vsadd.h \_out0, \_reg0, \_in0 + vsadd.h \_out1, \_reg0, \_in1 + vsadd.h \_out2, \_reg0, \_in2 + vsadd.h \_out3, \_reg0, \_in3 + vssrarn.bu.h \_out0, \_out0, \_reg2 + vssrarn.bu.h \_out1, \_out1, \_reg2 + vssrarn.bu.h \_out2, \_out2, \_reg2 + vssrarn.bu.h \_out3, \_out3, \_reg2 +.endm + +.macro weight_16 + weight_calc vr10, vr11, vr12, vr13, vr8, vr20, vr9, vr10, vr11, vr12, vr13 + weight_calc vr14, vr15, vr16, vr17, vr8, vr20, vr9, vr14, vr15, vr16, vr17 + weight_calc vr18, vr19, vr21, vr22, vr8, vr20, vr9, vr4, vr5, vr6, vr7 + weight_calc vr0, vr1, vr2, vr3, vr8, vr20, vr9, vr0, vr1, vr2, vr3 + + vilvl.d vr10, vr4, vr10 + vilvl.d vr11, vr5, vr11 + vilvl.d vr12, vr6, vr12 + vilvl.d vr13, vr7, vr13 + vilvl.d vr14, vr0, vr14 + vilvl.d vr15, vr1, vr15 + vilvl.d vr16, vr2, vr16 + vilvl.d vr17, vr3, vr17 + + vst vr10, a0, 0 + vstx vr11, a0, a1 + vstx vr12, a0, t0 + vstx vr13, a0, t1 + vst vr14, t4, 0 + vstx vr15, t4, a1 + vstx vr16, t4, t0 + vstx vr17, t4, t1 +.endm + +weight_func 16 + vldi vr23, 0 + addi.d t3, zero, 16 + weight_load_16 + weight_extend_16 + weight_16 + bne a2, t3, .END_WEIGHT_H264_PIXELS16_8 + add.d a0, t4, t2 + weight_load_16 + weight_extend_16 + weight_16 +.END_WEIGHT_H264_PIXELS16_8: +endfunc + +.macro weight_load_8 + load_double f0, f1, f2, f3, a0, a1, t0, t1 +.endm + +.macro weight_extend_8 + vilvl.b vr10, vr21, vr0 + vilvl.b vr11, vr21, vr1 + vilvl.b vr12, vr21, vr2 + vilvl.b vr13, vr21, vr3 +.endm + +.macro weight_8 + weight_calc vr10, vr11, vr12, vr13, vr8, vr20, vr9, vr0, vr1, vr2, vr3 + store_double f0, f1, f2, f3, a0, a1, t0, t1 +.endm + +weight_func 8 + vldi vr21, 0 + addi.d t3, zero, 8 + weight_load_8 + weight_extend_8 + weight_8 + blt a2, t3, .END_WEIGHT_H264_PIXELS8 + add.d a0, a0, t2 + addi.d t3, zero, 16 + weight_load_8 + weight_extend_8 + weight_8 + blt a2, t3, .END_WEIGHT_H264_PIXELS8 + add.d a0, a0, t2 + add.d t4, a0, t2 + weight_load_8 + load_double f4, f5, f6, f7, t4, a1, t0, t1 + weight_extend_8 + vilvl.b vr14, vr21, vr4 + vilvl.b vr15, vr21, vr5 + vilvl.b vr16, vr21, vr6 + vilvl.b vr17, vr21, vr7 + weight_8 + weight_calc vr14, vr15, vr16, vr17, vr8, vr20, vr9, vr4, vr5, vr6, vr7 + store_double f4, f5, f6, f7, t4, a1, t0, t1 +.END_WEIGHT_H264_PIXELS8: +endfunc + +.macro weight_func_lasx w +function ff_weight_h264_pixels\w\()_8_lasx + slli.d t0, a1, 1 + slli.d t2, a1, 2 + add.d t1, t0, a1 + + sll.d a5, a5, a3 + xvreplgr2vr.h xr20, a4 //weight + xvreplgr2vr.h xr8, a5 //offset + xvreplgr2vr.h xr9, a3 //log2_denom +.endm + +.macro weight_calc_lasx _in0, _in1, _reg0, _reg1, _reg2, _out0, _out1 + xvmul.h \_out0, \_in0, \_reg1 + xvmul.h \_out1, \_in1, \_reg1 + xvsadd.h \_out0, \_reg0, \_out0 + xvsadd.h \_out1, \_reg0, \_out1 + xvssrarn.bu.h \_out0, \_out0, \_reg2 + xvssrarn.bu.h \_out1, \_out1, \_reg2 +.endm + +.macro weight_load_lasx_8 + load_double f0, f1, f2, f3, a0, a1, t0, t1 + vilvl.d vr4, vr1, vr0 + vilvl.d vr5, vr3, vr2 + vext2xv.hu.bu xr6, xr4 + vext2xv.hu.bu xr7, xr5 +.endm + +.macro weight_lasx_8 + weight_calc_lasx xr6, xr7, xr8, xr20, xr9, xr1, xr3 + xvpermi.d xr2, xr1, 0x2 + xvpermi.d xr4, xr3, 0x2 + store_double f1, f2, f3, f4, a0, a1, t0, t1 +.endm + +weight_func_lasx 8 + addi.d t3, zero, 8 + weight_load_lasx_8 + weight_lasx_8 + blt a2, t3, .END_WEIGHT_H264_PIXELS8_LASX + add.d a0, a0, t2 + addi.d t3, zero, 16 + weight_load_lasx_8 + weight_lasx_8 + blt a2, t3, .END_WEIGHT_H264_PIXELS8_LASX + add.d a0, a0, t2 + add.d t4, a0, t2 + weight_load_lasx_8 + load_double f14, f15, f16, f17, t4, a1, t0, t1 + vilvl.d vr4, vr15, vr14 + vilvl.d vr5, vr17, vr16 + vext2xv.hu.bu xr10, xr4 + vext2xv.hu.bu xr11, xr5 + weight_lasx_8 + weight_calc_lasx xr10, xr11, xr8, xr20, xr9, xr4, xr6 + xvpermi.d xr5, xr4, 0x2 + xvpermi.d xr7, xr6, 0x2 + store_double f4, f5, f6, f7, t4, a1, t0, t1 +.END_WEIGHT_H264_PIXELS8_LASX: +endfunc + +.macro weight_load_lasx_16 + add.d t4, a0, t2 + vld vr0, a0, 0 + vldx vr1, a0, a1 + vldx vr2, a0, t0 + vldx vr3, a0, t1 + vld vr4, t4, 0 + vldx vr5, t4, a1 + vldx vr6, t4, t0 + vldx vr7, t4, t1 + + vext2xv.hu.bu xr0, xr0 + vext2xv.hu.bu xr1, xr1 + vext2xv.hu.bu xr2, xr2 + vext2xv.hu.bu xr3, xr3 + vext2xv.hu.bu xr4, xr4 + vext2xv.hu.bu xr5, xr5 + vext2xv.hu.bu xr6, xr6 + vext2xv.hu.bu xr7, xr7 +.endm + +.macro weight_lasx_16 + weight_calc_lasx xr0, xr1, xr8, xr20, xr9, xr10, xr11 + weight_calc_lasx xr2, xr3, xr8, xr20, xr9, xr12, xr13 + weight_calc_lasx xr4, xr5, xr8, xr20, xr9, xr14, xr15 + weight_calc_lasx xr6, xr7, xr8, xr20, xr9, xr16, xr17 + xvpermi.d xr10, xr10, 0xD8 + xvpermi.d xr11, xr11, 0xD8 + xvpermi.d xr12, xr12, 0xD8 + xvpermi.d xr13, xr13, 0xD8 + xvpermi.d xr14, xr14, 0xD8 + xvpermi.d xr15, xr15, 0xD8 + xvpermi.d xr16, xr16, 0xD8 + xvpermi.d xr17, xr17, 0xD8 + + vst vr10, a0, 0 + vstx vr11, a0, a1 + vstx vr12, a0, t0 + vstx vr13, a0, t1 + vst vr14, t4, 0 + vstx vr15, t4, a1 + vstx vr16, t4, t0 + vstx vr17, t4, t1 +.endm + +weight_func_lasx 16 + addi.d t3, zero, 16 + weight_load_lasx_16 + weight_lasx_16 + bne a2, t3, .END_WEIGHT_H264_PIXELS16_8_LASX + add.d a0, t4, t2 + weight_load_lasx_16 + weight_lasx_16 +.END_WEIGHT_H264_PIXELS16_8_LASX: +endfunc + +//LSX optimization is sufficient for this function. +function ff_weight_h264_pixels4_8_lsx + add.d t0, a0, a1 + addi.d t3, zero, 4 + + sll.d a5, a5, a3 + vreplgr2vr.h vr20, a4 //weight + vreplgr2vr.h vr8, a5 //offset + vreplgr2vr.h vr9, a3 //log2_denom + vldi vr21, 0 + + fld.s f0, a0, 0 + fldx.s f1, a0, a1 + vilvl.w vr4, vr1, vr0 + vilvl.b vr5, vr21, vr4 + vmul.h vr10, vr5, vr20 + vsadd.h vr0, vr8, vr10 + vssrarn.bu.h vr0, vr0, vr9 + + fst.s f0, a0, 0 + vstelm.w vr0, t0, 0, 1 + blt a2, t3, .END_WEIGHT_H264_PIXELS4 + add.d a0, t0, a1 + addi.d t3, zero, 8 + fld.s f0, a0, 0 + fldx.s f1, a0, a1 + add.d t0, a0, a1 + vilvl.w vr4, vr1, vr0 + vilvl.b vr5, vr21, vr4 + + vmul.h vr10, vr5, vr20 + vsadd.h vr0, vr8, vr10 + vssrarn.bu.h vr0, vr0, vr9 + + fst.s f0, a0, 0 + vstelm.w vr0, t0, 0, 1 + blt a2, t3, .END_WEIGHT_H264_PIXELS4 + add.d a0, t0, a1 + add.d t0, a0, a1 + add.d t1, t0, a1 + add.d t2, t1, a1 + + fld.s f0, a0, 0 + fld.s f1, t0, 0 + fld.s f2, t1, 0 + fld.s f3, t2, 0 + + vilvl.w vr4, vr1, vr0 + vilvl.w vr5, vr3, vr2 + vilvl.b vr6, vr21, vr4 + vilvl.b vr7, vr21, vr5 + + vmul.h vr10, vr6, vr20 + vmul.h vr11, vr7, vr20 + vsadd.h vr0, vr8, vr10 + vsadd.h vr1, vr8, vr11 + vssrarn.bu.h vr10, vr0, vr9 + vssrarn.bu.h vr11, vr1, vr9 + + fst.s f10, a0, 0 + vstelm.w vr10, t0, 0, 1 + fst.s f11, t1, 0 + vstelm.w vr11, t2, 0, 1 +.END_WEIGHT_H264_PIXELS4: +endfunc + +function ff_h264_add_pixels4_8_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + vld vr0, a1, 0 + vld vr1, a1, 16 + vldi vr2, 0 + fld.s f3, a0, 0 + fldx.s f4, a0, a2 + fldx.s f5, a0, t0 + fldx.s f6, a0, t1 + vilvl.w vr7, vr4, vr3 + vilvl.w vr8, vr6, vr5 + vilvl.b vr9, vr2, vr7 + vilvl.b vr10, vr2, vr8 + vadd.h vr11, vr0, vr9 + vadd.h vr12, vr1, vr10 + vpickev.b vr0, vr12, vr11 + vbsrl.v vr3, vr0, 4 + vbsrl.v vr4, vr0, 8 + vbsrl.v vr5, vr0, 12 + fst.s f0, a0, 0 + fstx.s f3, a0, a2 + fstx.s f4, a0, t0 + fstx.s f5, a0, t1 + vst vr2, a1, 0 + vst vr2, a1, 16 +endfunc + +function ff_h264_add_pixels8_8_lsx + slli.d t0, a2, 1 + slli.d t2, a2, 2 + add.d t1, t0, a2 + add.d t3, a0, t2 + vldi vr0, 0 + vld vr1, a1, 0 + vld vr2, a1, 16 + vld vr3, a1, 32 + vld vr4, a1, 48 + vld vr5, a1, 64 + vld vr6, a1, 80 + vld vr7, a1, 96 + vld vr8, a1, 112 + load_double f10, f11, f12, f13, a0, a2, t0, t1 + load_double f14, f15, f16, f17, t3, a2, t0, t1 + vilvl.b vr10, vr0, vr10 + vilvl.b vr11, vr0, vr11 + vilvl.b vr12, vr0, vr12 + vilvl.b vr13, vr0, vr13 + vilvl.b vr14, vr0, vr14 + vilvl.b vr15, vr0, vr15 + vilvl.b vr16, vr0, vr16 + vilvl.b vr17, vr0, vr17 + vadd.h vr1, vr1, vr10 + vadd.h vr2, vr2, vr11 + vadd.h vr3, vr3, vr12 + vadd.h vr4, vr4, vr13 + vadd.h vr5, vr5, vr14 + vadd.h vr6, vr6, vr15 + vadd.h vr7, vr7, vr16 + vadd.h vr8, vr8, vr17 + vpickev.b vr10, vr2, vr1 + vpickev.b vr12, vr4, vr3 + vpickev.b vr14, vr6, vr5 + vpickev.b vr16, vr8, vr7 + vbsrl.v vr11, vr10, 8 + vbsrl.v vr13, vr12, 8 + vbsrl.v vr15, vr14, 8 + vbsrl.v vr17, vr16, 8 + vst vr0, a1, 0 + vst vr0, a1, 16 + vst vr0, a1, 32 + vst vr0, a1, 48 + vst vr0, a1, 64 + vst vr0, a1, 80 + vst vr0, a1, 96 + vst vr0, a1, 112 + store_double f10, f11, f12, f13, a0, a2, t0, t1 + store_double f14, f15, f16, f17, t3, a2, t0, t1 +endfunc + +const cnst_value +.byte 6, 2, 6, 2, 6, 2, 6, 2, 6, 2, 6, 2, 6, 2, 6, 2 +.byte 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1 +endconst + +function ff_h264_loop_filter_strength_lsx + vldi vr0, 0 + ldptr.w t0, sp, 0 //mask_mv1 + ldptr.w t1, sp, 8 //field + beqz t1, .FIELD + la.local t2, cnst_value + vld vr1, t2, 0 + vld vr2, t2, 16 + b .END_FIELD +.FIELD: + vldi vr1, 0x06 + vldi vr2, 0x03 +.END_FIELD: + vldi vr3, 0x01 + slli.d a6, a6, 3 //step <<= 3 + slli.d a5, a5, 3 //edges <<= 3 + move t3, zero + slli.d t4, a6, 2 + move t5, a2 + move t6, a3 + move t7, a1 + move t8, a0 + slli.d t0, t0, 3 +.ITERATION_FIR: + bge t3, a5, .END_ITERATION_FIR + vand.v vr20, vr20, vr0 + and t2, t0, t3 + bnez t2, .MASK_MV_FIR + beqz a4, .BIDIR_FIR + vld vr4, t5, 4 + vld vr5, t5, 44 + vld vr6, t5, 12 + vld vr7, t5, 52 + vilvl.w vr4, vr5, vr4 + vilvl.w vr6, vr6, vr6 + vilvl.w vr7, vr7, vr7 + vshuf4i.h vr5, vr4, 0x4e + vsub.b vr6, vr6, vr4 + vsub.b vr7, vr7, vr5 + vor.v vr6, vr6, vr7 + vld vr10, t6, 16 + vld vr11, t6, 48 + vld vr12, t6, 208 + vld vr8, t6, 176 + vsub.h vr13, vr10, vr11 + vsub.h vr14, vr10, vr12 + vsub.h vr15, vr8, vr11 + vsub.h vr16, vr8, vr12 + vssrarni.b.h vr14, vr13, 0 + vssrarni.b.h vr16, vr15, 0 + vadd.b vr14, vr2, vr14 + vadd.b vr16, vr2, vr16 + vssub.bu vr14, vr14, vr1 + vssub.bu vr16, vr16, vr1 + vssrarni.b.h vr14, vr14, 0 + vssrarni.b.h vr16, vr16, 0 + vor.v vr20, vr6, vr14 + vshuf4i.h vr16, vr16, 0x4e + vor.v vr20, vr20, vr16 + vshuf4i.h vr21, vr20, 0x4e + vmin.bu vr20, vr20, vr21 + b .MASK_MV_FIR +.BIDIR_FIR: + vld vr4, t5, 4 + vld vr5, t5, 12 + vld vr10, t6, 16 + vld vr11, t6, 48 + vsub.h vr12, vr11, vr10 + vssrarni.b.h vr12, vr12, 0 + vadd.b vr13, vr12, vr2 + vssub.bu vr14, vr13, vr1 + vsat.h vr15, vr14, 7 + vpickev.b vr20, vr15, vr15 + vsub.b vr6, vr5, vr4 + vor.v vr20, vr20, vr6 +.MASK_MV_FIR: + vld vr4, t7, 12 + vld vr5, t7, 4 + vor.v vr6, vr4, vr5 + vmin.bu vr6, vr6, vr3 + vmin.bu vr20, vr20, vr3 + vslli.h vr6, vr6, 1 + vmax.bu vr6, vr20, vr6 + vilvl.b vr7, vr0, vr6 + add.d t3, t3, a6 + fst.d f7, t8, 32 + add.d t5, t5, a6 + add.d t6, t6, t4 + add.d t7, t7, a6 + add.d t8, t8, a6 + b .ITERATION_FIR +.END_ITERATION_FIR: + move t3, zero + addi.d a5, zero, 32 + vldi vr21, 0xff + move t5, a2 + move t6, a3 + move t7, a1 + move t8, a0 + slli.d a7, a7, 3 +.ITERATION_SEC: + bge t3, a5, .END_ITERATION_SEC + vand.v vr20, vr20, vr21 + and t2, a7, t3 + bnez t2, .MASK_MV_SEC + beqz a4, .BIDIR_SEC + vld vr4, t5, 11 + vld vr5, t5, 51 + vld vr6, t5, 12 + vld vr7, t5, 52 + vilvl.w vr4, vr5, vr4 + vilvl.w vr6, vr6, vr6 + vilvl.w vr7, vr7, vr7 + vshuf4i.h vr5, vr4, 0x4e + vsub.b vr6, vr6, vr4 + vsub.b vr7, vr7, vr5 + vor.v vr6, vr6, vr7 + vld vr10, t6, 44 + vld vr11, t6, 48 + vld vr12, t6, 208 + vld vr8, t6, 204 + vsub.h vr13, vr10, vr11 + vsub.h vr14, vr10, vr12 + vsub.h vr15, vr8, vr11 + vsub.h vr16, vr8, vr12 + vssrarni.b.h vr14, vr13, 0 + vssrarni.b.h vr16, vr15, 0 + vadd.b vr14, vr2, vr14 + vadd.b vr16, vr2, vr16 + vssub.bu vr14, vr14, vr1 + vssub.bu vr16, vr16, vr1 + vssrarni.b.h vr14, vr14, 0 + vssrarni.b.h vr16, vr16, 0 + vor.v vr20, vr6, vr14 + vshuf4i.h vr16, vr16, 0x4e + vor.v vr20, vr20, vr16 + vshuf4i.h vr22, vr20, 0x4e + vmin.bu vr20, vr20, vr22 + b .MASK_MV_SEC +.BIDIR_SEC: + vld vr4, t5, 11 + vld vr5, t5, 12 + vld vr10, t6, 44 + vld vr11, t6, 48 + vsub.h vr12, vr11, vr10 + vssrarni.b.h vr12, vr12, 0 + vadd.b vr13, vr12, vr2 + vssub.bu vr14, vr13, vr1 + vssrarni.b.h vr14, vr14, 0 + vsub.b vr6, vr5, vr4 + vor.v vr20, vr14, vr6 +.MASK_MV_SEC: + vld vr4, t7, 12 + vld vr5, t7, 11 + vor.v vr6, vr4, vr5 + vmin.bu vr6, vr6, vr3 + vmin.bu vr20, vr20, vr3 + vslli.h vr6, vr6, 1 + vmax.bu vr6, vr20, vr6 + vilvl.b vr7, vr0, vr6 + addi.d t3, t3, 8 + fst.d f7, t8, 0 + addi.d t5, t5, 8 + addi.d t6, t6, 32 + addi.d t7, t7, 8 + addi.d t8, t8, 8 + b .ITERATION_SEC +.END_ITERATION_SEC: + vld vr4, a0, 0 + vld vr5, a0, 16 + vilvh.d vr6, vr4, vr4 + vilvh.d vr7, vr5, vr5 + LSX_TRANSPOSE4x4_H vr4, vr6, vr5, vr7, vr6, vr7, vr8, vr9, vr10, vr11 + vilvl.d vr4, vr7, vr6 + vilvl.d vr5, vr9, vr8 + vst vr4, a0, 0 + vst vr5, a0, 16 +endfunc diff --git a/libavcodec/loongarch/h264dsp_init_loongarch.c b/libavcodec/loongarch/h264dsp_init_loongarch.c index 37633c3e51c..b70fe696d29 100644 --- a/libavcodec/loongarch/h264dsp_init_loongarch.c +++ b/libavcodec/loongarch/h264dsp_init_loongarch.c @@ -21,13 +21,55 @@ */ #include "libavutil/loongarch/cpu.h" -#include "h264dsp_lasx.h" +#include "h264dsp_loongarch.h" av_cold void ff_h264dsp_init_loongarch(H264DSPContext *c, const int bit_depth, const int chroma_format_idc) { int cpu_flags = av_get_cpu_flags(); + if (have_lsx(cpu_flags)) { + if (chroma_format_idc <= 1) + c->h264_loop_filter_strength = ff_h264_loop_filter_strength_lsx; + if (bit_depth == 8) { + c->h264_idct_add = ff_h264_idct_add_8_lsx; + c->h264_idct8_add = ff_h264_idct8_add_8_lsx; + c->h264_idct_dc_add = ff_h264_idct_dc_add_8_lsx; + c->h264_idct8_dc_add = ff_h264_idct8_dc_add_8_lsx; + + if (chroma_format_idc <= 1) { + c->h264_idct_add8 = ff_h264_idct_add8_8_lsx; + c->h264_h_loop_filter_chroma = ff_h264_h_lpf_chroma_8_lsx; + c->h264_h_loop_filter_chroma_intra = ff_h264_h_lpf_chroma_intra_8_lsx; + } else + c->h264_idct_add8 = ff_h264_idct_add8_422_8_lsx; + + c->h264_idct_add16 = ff_h264_idct_add16_8_lsx; + c->h264_idct8_add4 = ff_h264_idct8_add4_8_lsx; + c->h264_luma_dc_dequant_idct = ff_h264_luma_dc_dequant_idct_8_lsx; + c->h264_idct_add16intra = ff_h264_idct_add16_intra_8_lsx; + + c->h264_add_pixels4_clear = ff_h264_add_pixels4_8_lsx; + c->h264_add_pixels8_clear = ff_h264_add_pixels8_8_lsx; + c->h264_v_loop_filter_luma = ff_h264_v_lpf_luma_8_lsx; + c->h264_h_loop_filter_luma = ff_h264_h_lpf_luma_8_lsx; + c->h264_v_loop_filter_luma_intra = ff_h264_v_lpf_luma_intra_8_lsx; + c->h264_h_loop_filter_luma_intra = ff_h264_h_lpf_luma_intra_8_lsx; + c->h264_v_loop_filter_chroma = ff_h264_v_lpf_chroma_8_lsx; + + c->h264_v_loop_filter_chroma_intra = ff_h264_v_lpf_chroma_intra_8_lsx; + + c->biweight_h264_pixels_tab[0] = ff_biweight_h264_pixels16_8_lsx; + c->biweight_h264_pixels_tab[1] = ff_biweight_h264_pixels8_8_lsx; + c->biweight_h264_pixels_tab[2] = ff_biweight_h264_pixels4_8_lsx; + c->weight_h264_pixels_tab[0] = ff_weight_h264_pixels16_8_lsx; + c->weight_h264_pixels_tab[1] = ff_weight_h264_pixels8_8_lsx; + c->weight_h264_pixels_tab[2] = ff_weight_h264_pixels4_8_lsx; + c->h264_idct8_add = ff_h264_idct8_add_8_lsx; + c->h264_idct8_dc_add = ff_h264_idct8_dc_add_8_lsx; + } + } +#if HAVE_LASX if (have_lasx(cpu_flags)) { if (chroma_format_idc <= 1) c->h264_loop_filter_strength = ff_h264_loop_filter_strength_lasx; @@ -38,38 +80,18 @@ av_cold void ff_h264dsp_init_loongarch(H264DSPContext *c, const int bit_depth, c->h264_h_loop_filter_luma = ff_h264_h_lpf_luma_8_lasx; c->h264_v_loop_filter_luma_intra = ff_h264_v_lpf_luma_intra_8_lasx; c->h264_h_loop_filter_luma_intra = ff_h264_h_lpf_luma_intra_8_lasx; - c->h264_v_loop_filter_chroma = ff_h264_v_lpf_chroma_8_lasx; - - if (chroma_format_idc <= 1) - c->h264_h_loop_filter_chroma = ff_h264_h_lpf_chroma_8_lasx; - c->h264_v_loop_filter_chroma_intra = ff_h264_v_lpf_chroma_intra_8_lasx; - - if (chroma_format_idc <= 1) - c->h264_h_loop_filter_chroma_intra = ff_h264_h_lpf_chroma_intra_8_lasx; /* Weighted MC */ c->weight_h264_pixels_tab[0] = ff_weight_h264_pixels16_8_lasx; c->weight_h264_pixels_tab[1] = ff_weight_h264_pixels8_8_lasx; - c->weight_h264_pixels_tab[2] = ff_weight_h264_pixels4_8_lasx; c->biweight_h264_pixels_tab[0] = ff_biweight_h264_pixels16_8_lasx; c->biweight_h264_pixels_tab[1] = ff_biweight_h264_pixels8_8_lasx; - c->biweight_h264_pixels_tab[2] = ff_biweight_h264_pixels4_8_lasx; - - c->h264_idct_add = ff_h264_idct_add_lasx; - c->h264_idct8_add = ff_h264_idct8_addblk_lasx; - c->h264_idct_dc_add = ff_h264_idct4x4_addblk_dc_lasx; - c->h264_idct8_dc_add = ff_h264_idct8_dc_addblk_lasx; - c->h264_idct_add16 = ff_h264_idct_add16_lasx; - c->h264_idct8_add4 = ff_h264_idct8_add4_lasx; - - if (chroma_format_idc <= 1) - c->h264_idct_add8 = ff_h264_idct_add8_lasx; - else - c->h264_idct_add8 = ff_h264_idct_add8_422_lasx; - c->h264_idct_add16intra = ff_h264_idct_add16_intra_lasx; - c->h264_luma_dc_dequant_idct = ff_h264_deq_idct_luma_dc_lasx; + c->h264_idct8_add = ff_h264_idct8_add_8_lasx; + c->h264_idct8_dc_add = ff_h264_idct8_dc_add_8_lasx; + c->h264_idct8_add4 = ff_h264_idct8_add4_8_lasx; } } +#endif // #if HAVE_LASX } diff --git a/libavcodec/loongarch/h264dsp_lasx.c b/libavcodec/loongarch/h264dsp_lasx.c index 7fd4cedf7ef..5205cc849f1 100644 --- a/libavcodec/loongarch/h264dsp_lasx.c +++ b/libavcodec/loongarch/h264dsp_lasx.c @@ -23,7 +23,7 @@ */ #include "libavutil/loongarch/loongson_intrinsics.h" -#include "h264dsp_lasx.h" +#include "h264dsp_loongarch.h" #define AVC_LPF_P1_OR_Q1(p0_or_q0_org_in, q0_or_p0_org_in, \ p1_or_q1_org_in, p2_or_q2_org_in, \ @@ -67,10 +67,10 @@ void ff_h264_h_lpf_luma_8_lasx(uint8_t *data, ptrdiff_t img_width, int alpha_in, int beta_in, int8_t *tc) { - ptrdiff_t img_width_2x = img_width << 1; - ptrdiff_t img_width_4x = img_width << 2; - ptrdiff_t img_width_8x = img_width << 3; - ptrdiff_t img_width_3x = img_width_2x + img_width; + int img_width_2x = img_width << 1; + int img_width_4x = img_width << 2; + int img_width_8x = img_width << 3; + int img_width_3x = img_width_2x + img_width; __m256i tmp_vec0, bs_vec; __m256i tc_vec = {0x0101010100000000, 0x0303030302020202, 0x0101010100000000, 0x0303030302020202}; @@ -244,8 +244,8 @@ void ff_h264_h_lpf_luma_8_lasx(uint8_t *data, ptrdiff_t img_width, void ff_h264_v_lpf_luma_8_lasx(uint8_t *data, ptrdiff_t img_width, int alpha_in, int beta_in, int8_t *tc) { - ptrdiff_t img_width_2x = img_width << 1; - ptrdiff_t img_width_3x = img_width + img_width_2x; + int img_width_2x = img_width << 1; + int img_width_3x = img_width + img_width_2x; __m256i tmp_vec0, bs_vec; __m256i tc_vec = {0x0101010100000000, 0x0303030302020202, 0x0101010100000000, 0x0303030302020202}; @@ -363,184 +363,6 @@ void ff_h264_v_lpf_luma_8_lasx(uint8_t *data, ptrdiff_t img_width, } } -void ff_h264_h_lpf_chroma_8_lasx(uint8_t *data, ptrdiff_t img_width, - int alpha_in, int beta_in, int8_t *tc) -{ - __m256i tmp_vec0, bs_vec; - __m256i tc_vec = {0x0303020201010000, 0x0303020201010000, 0x0, 0x0}; - __m256i zero = __lasx_xvldi(0); - ptrdiff_t img_width_2x = img_width << 1; - ptrdiff_t img_width_4x = img_width << 2; - ptrdiff_t img_width_3x = img_width_2x + img_width; - - tmp_vec0 = __lasx_xvldrepl_w((uint32_t*)tc, 0); - tc_vec = __lasx_xvshuf_b(tmp_vec0, tmp_vec0, tc_vec); - bs_vec = __lasx_xvslti_b(tc_vec, 0); - bs_vec = __lasx_xvxori_b(bs_vec, 255); - bs_vec = __lasx_xvandi_b(bs_vec, 1); - bs_vec = __lasx_xvpermi_q(zero, bs_vec, 0x30); - - if (__lasx_xbnz_v(bs_vec)) { - uint8_t *src = data - 2; - __m256i p1_org, p0_org, q0_org, q1_org; - __m256i p0_asub_q0, p1_asub_p0, q1_asub_q0, alpha, beta; - __m256i is_less_than, is_less_than_beta, is_less_than_alpha; - __m256i is_bs_greater_than0; - - is_bs_greater_than0 = __lasx_xvslt_bu(zero, bs_vec); - - { - __m256i row0, row1, row2, row3, row4, row5, row6, row7; - - DUP4_ARG2(__lasx_xvldx, src, 0, src, img_width, src, img_width_2x, - src, img_width_3x, row0, row1, row2, row3); - src += img_width_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, img_width, src, img_width_2x, - src, img_width_3x, row4, row5, row6, row7); - src -= img_width_4x; - /* LASX_TRANSPOSE8x4_B */ - DUP4_ARG2(__lasx_xvilvl_b, row2, row0, row3, row1, row6, row4, - row7, row5, p1_org, p0_org, q0_org, q1_org); - row0 = __lasx_xvilvl_b(p0_org, p1_org); - row1 = __lasx_xvilvl_b(q1_org, q0_org); - row3 = __lasx_xvilvh_w(row1, row0); - row2 = __lasx_xvilvl_w(row1, row0); - p1_org = __lasx_xvpermi_d(row2, 0x00); - p0_org = __lasx_xvpermi_d(row2, 0x55); - q0_org = __lasx_xvpermi_d(row3, 0x00); - q1_org = __lasx_xvpermi_d(row3, 0x55); - } - - p0_asub_q0 = __lasx_xvabsd_bu(p0_org, q0_org); - p1_asub_p0 = __lasx_xvabsd_bu(p1_org, p0_org); - q1_asub_q0 = __lasx_xvabsd_bu(q1_org, q0_org); - - alpha = __lasx_xvreplgr2vr_b(alpha_in); - beta = __lasx_xvreplgr2vr_b(beta_in); - - is_less_than_alpha = __lasx_xvslt_bu(p0_asub_q0, alpha); - is_less_than_beta = __lasx_xvslt_bu(p1_asub_p0, beta); - is_less_than = is_less_than_alpha & is_less_than_beta; - is_less_than_beta = __lasx_xvslt_bu(q1_asub_q0, beta); - is_less_than = is_less_than_beta & is_less_than; - is_less_than = is_less_than & is_bs_greater_than0; - - if (__lasx_xbnz_v(is_less_than)) { - __m256i p1_org_h, p0_org_h, q0_org_h, q1_org_h; - - p1_org_h = __lasx_vext2xv_hu_bu(p1_org); - p0_org_h = __lasx_vext2xv_hu_bu(p0_org); - q0_org_h = __lasx_vext2xv_hu_bu(q0_org); - q1_org_h = __lasx_vext2xv_hu_bu(q1_org); - - { - __m256i tc_h, neg_thresh_h, p0_h, q0_h; - - neg_thresh_h = __lasx_xvneg_b(tc_vec); - neg_thresh_h = __lasx_vext2xv_h_b(neg_thresh_h); - tc_h = __lasx_vext2xv_hu_bu(tc_vec); - - AVC_LPF_P0Q0(q0_org_h, p0_org_h, p1_org_h, q1_org_h, - neg_thresh_h, tc_h, p0_h, q0_h); - DUP2_ARG2(__lasx_xvpickev_b, p0_h, p0_h, q0_h, q0_h, - p0_h, q0_h); - DUP2_ARG2(__lasx_xvpermi_d, p0_h, 0xd8, q0_h, 0xd8, - p0_h, q0_h); - p0_org = __lasx_xvbitsel_v(p0_org, p0_h, is_less_than); - q0_org = __lasx_xvbitsel_v(q0_org, q0_h, is_less_than); - } - - p0_org = __lasx_xvilvl_b(q0_org, p0_org); - src = data - 1; - __lasx_xvstelm_h(p0_org, src, 0, 0); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 1); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 2); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 3); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 4); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 5); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 6); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 7); - } - } -} - -void ff_h264_v_lpf_chroma_8_lasx(uint8_t *data, ptrdiff_t img_width, - int alpha_in, int beta_in, int8_t *tc) -{ - int img_width_2x = img_width << 1; - __m256i tmp_vec0, bs_vec; - __m256i tc_vec = {0x0303020201010000, 0x0303020201010000, 0x0, 0x0}; - __m256i zero = __lasx_xvldi(0); - - tmp_vec0 = __lasx_xvldrepl_w((uint32_t*)tc, 0); - tc_vec = __lasx_xvshuf_b(tmp_vec0, tmp_vec0, tc_vec); - bs_vec = __lasx_xvslti_b(tc_vec, 0); - bs_vec = __lasx_xvxori_b(bs_vec, 255); - bs_vec = __lasx_xvandi_b(bs_vec, 1); - bs_vec = __lasx_xvpermi_q(zero, bs_vec, 0x30); - - if (__lasx_xbnz_v(bs_vec)) { - __m256i p1_org, p0_org, q0_org, q1_org; - __m256i p0_asub_q0, p1_asub_p0, q1_asub_q0, alpha, beta; - __m256i is_less_than, is_less_than_beta, is_less_than_alpha; - __m256i is_bs_greater_than0; - - alpha = __lasx_xvreplgr2vr_b(alpha_in); - beta = __lasx_xvreplgr2vr_b(beta_in); - - DUP2_ARG2(__lasx_xvldx, data, -img_width_2x, data, -img_width, - p1_org, p0_org); - DUP2_ARG2(__lasx_xvldx, data, 0, data, img_width, q0_org, q1_org); - - is_bs_greater_than0 = __lasx_xvslt_bu(zero, bs_vec); - p0_asub_q0 = __lasx_xvabsd_bu(p0_org, q0_org); - p1_asub_p0 = __lasx_xvabsd_bu(p1_org, p0_org); - q1_asub_q0 = __lasx_xvabsd_bu(q1_org, q0_org); - - is_less_than_alpha = __lasx_xvslt_bu(p0_asub_q0, alpha); - is_less_than_beta = __lasx_xvslt_bu(p1_asub_p0, beta); - is_less_than = is_less_than_alpha & is_less_than_beta; - is_less_than_beta = __lasx_xvslt_bu(q1_asub_q0, beta); - is_less_than = is_less_than_beta & is_less_than; - is_less_than = is_less_than & is_bs_greater_than0; - - if (__lasx_xbnz_v(is_less_than)) { - __m256i p1_org_h, p0_org_h, q0_org_h, q1_org_h; - - p1_org_h = __lasx_vext2xv_hu_bu(p1_org); - p0_org_h = __lasx_vext2xv_hu_bu(p0_org); - q0_org_h = __lasx_vext2xv_hu_bu(q0_org); - q1_org_h = __lasx_vext2xv_hu_bu(q1_org); - - { - __m256i neg_thresh_h, tc_h, p0_h, q0_h; - - neg_thresh_h = __lasx_xvneg_b(tc_vec); - neg_thresh_h = __lasx_vext2xv_h_b(neg_thresh_h); - tc_h = __lasx_vext2xv_hu_bu(tc_vec); - - AVC_LPF_P0Q0(q0_org_h, p0_org_h, p1_org_h, q1_org_h, - neg_thresh_h, tc_h, p0_h, q0_h); - DUP2_ARG2(__lasx_xvpickev_b, p0_h, p0_h, q0_h, q0_h, - p0_h, q0_h); - DUP2_ARG2(__lasx_xvpermi_d, p0_h, 0xd8, q0_h, 0xd8, - p0_h, q0_h); - p0_h = __lasx_xvbitsel_v(p0_org, p0_h, is_less_than); - q0_h = __lasx_xvbitsel_v(q0_org, q0_h, is_less_than); - __lasx_xvstelm_d(p0_h, data - img_width, 0, 0); - __lasx_xvstelm_d(q0_h, data, 0, 0); - } - } - } -} - #define AVC_LPF_P0P1P2_OR_Q0Q1Q2(p3_or_q3_org_in, p0_or_q0_org_in, \ q3_or_p3_org_in, p1_or_q1_org_in, \ p2_or_q2_org_in, q1_or_p1_org_in, \ @@ -584,9 +406,9 @@ void ff_h264_v_lpf_chroma_8_lasx(uint8_t *data, ptrdiff_t img_width, void ff_h264_h_lpf_luma_intra_8_lasx(uint8_t *data, ptrdiff_t img_width, int alpha_in, int beta_in) { - ptrdiff_t img_width_2x = img_width << 1; - ptrdiff_t img_width_4x = img_width << 2; - ptrdiff_t img_width_3x = img_width_2x + img_width; + int img_width_2x = img_width << 1; + int img_width_4x = img_width << 2; + int img_width_3x = img_width_2x + img_width; uint8_t *src = data - 4; __m256i p0_asub_q0, p1_asub_p0, q1_asub_q0, alpha, beta; __m256i is_less_than, is_less_than_beta, is_less_than_alpha; @@ -760,8 +582,8 @@ void ff_h264_h_lpf_luma_intra_8_lasx(uint8_t *data, ptrdiff_t img_width, void ff_h264_v_lpf_luma_intra_8_lasx(uint8_t *data, ptrdiff_t img_width, int alpha_in, int beta_in) { - ptrdiff_t img_width_2x = img_width << 1; - ptrdiff_t img_width_3x = img_width_2x + img_width; + int img_width_2x = img_width << 1; + int img_width_3x = img_width_2x + img_width; uint8_t *src = data - img_width_2x; __m256i p0_asub_q0, p1_asub_p0, q1_asub_q0, alpha, beta; __m256i is_less_than, is_less_than_beta, is_less_than_alpha; @@ -877,1160 +699,6 @@ void ff_h264_v_lpf_luma_intra_8_lasx(uint8_t *data, ptrdiff_t img_width, } } -void ff_h264_h_lpf_chroma_intra_8_lasx(uint8_t *data, ptrdiff_t img_width, - int alpha_in, int beta_in) -{ - uint8_t *src = data - 2; - ptrdiff_t img_width_2x = img_width << 1; - ptrdiff_t img_width_4x = img_width << 2; - ptrdiff_t img_width_3x = img_width_2x + img_width; - __m256i p1_org, p0_org, q0_org, q1_org; - __m256i p0_asub_q0, p1_asub_p0, q1_asub_q0, alpha, beta; - __m256i is_less_than, is_less_than_beta, is_less_than_alpha; - - { - __m256i row0, row1, row2, row3, row4, row5, row6, row7; - - DUP4_ARG2(__lasx_xvldx, src, 0, src, img_width, src, img_width_2x, src, - img_width_3x, row0, row1, row2, row3); - src += img_width_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, img_width, src, img_width_2x, src, - img_width_3x, row4, row5, row6, row7); - - /* LASX_TRANSPOSE8x4_B */ - DUP4_ARG2(__lasx_xvilvl_b, row2, row0, row3, row1, row6, row4, row7, row5, - p1_org, p0_org, q0_org, q1_org); - row0 = __lasx_xvilvl_b(p0_org, p1_org); - row1 = __lasx_xvilvl_b(q1_org, q0_org); - row3 = __lasx_xvilvh_w(row1, row0); - row2 = __lasx_xvilvl_w(row1, row0); - p1_org = __lasx_xvpermi_d(row2, 0x00); - p0_org = __lasx_xvpermi_d(row2, 0x55); - q0_org = __lasx_xvpermi_d(row3, 0x00); - q1_org = __lasx_xvpermi_d(row3, 0x55); - } - - alpha = __lasx_xvreplgr2vr_b(alpha_in); - beta = __lasx_xvreplgr2vr_b(beta_in); - - p0_asub_q0 = __lasx_xvabsd_bu(p0_org, q0_org); - p1_asub_p0 = __lasx_xvabsd_bu(p1_org, p0_org); - q1_asub_q0 = __lasx_xvabsd_bu(q1_org, q0_org); - - is_less_than_alpha = __lasx_xvslt_bu(p0_asub_q0, alpha); - is_less_than_beta = __lasx_xvslt_bu(p1_asub_p0, beta); - is_less_than = is_less_than_alpha & is_less_than_beta; - is_less_than_beta = __lasx_xvslt_bu(q1_asub_q0, beta); - is_less_than = is_less_than_beta & is_less_than; - - if (__lasx_xbnz_v(is_less_than)) { - __m256i p0_h, q0_h, p1_org_h, p0_org_h, q0_org_h, q1_org_h; - - p1_org_h = __lasx_vext2xv_hu_bu(p1_org); - p0_org_h = __lasx_vext2xv_hu_bu(p0_org); - q0_org_h = __lasx_vext2xv_hu_bu(q0_org); - q1_org_h = __lasx_vext2xv_hu_bu(q1_org); - - AVC_LPF_P0_OR_Q0(p0_org_h, q1_org_h, p1_org_h, p0_h); - AVC_LPF_P0_OR_Q0(q0_org_h, p1_org_h, q1_org_h, q0_h); - DUP2_ARG2(__lasx_xvpickev_b, p0_h, p0_h, q0_h, q0_h, p0_h, q0_h); - DUP2_ARG2(__lasx_xvpermi_d, p0_h, 0xd8, q0_h, 0xd8, p0_h, q0_h); - p0_org = __lasx_xvbitsel_v(p0_org, p0_h, is_less_than); - q0_org = __lasx_xvbitsel_v(q0_org, q0_h, is_less_than); - } - p0_org = __lasx_xvilvl_b(q0_org, p0_org); - src = data - 1; - __lasx_xvstelm_h(p0_org, src, 0, 0); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 1); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 2); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 3); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 4); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 5); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 6); - src += img_width; - __lasx_xvstelm_h(p0_org, src, 0, 7); -} - -void ff_h264_v_lpf_chroma_intra_8_lasx(uint8_t *data, ptrdiff_t img_width, - int alpha_in, int beta_in) -{ - ptrdiff_t img_width_2x = img_width << 1; - __m256i p1_org, p0_org, q0_org, q1_org; - __m256i p0_asub_q0, p1_asub_p0, q1_asub_q0, alpha, beta; - __m256i is_less_than, is_less_than_beta, is_less_than_alpha; - - alpha = __lasx_xvreplgr2vr_b(alpha_in); - beta = __lasx_xvreplgr2vr_b(beta_in); - - p1_org = __lasx_xvldx(data, -img_width_2x); - p0_org = __lasx_xvldx(data, -img_width); - DUP2_ARG2(__lasx_xvldx, data, 0, data, img_width, q0_org, q1_org); - - p0_asub_q0 = __lasx_xvabsd_bu(p0_org, q0_org); - p1_asub_p0 = __lasx_xvabsd_bu(p1_org, p0_org); - q1_asub_q0 = __lasx_xvabsd_bu(q1_org, q0_org); - - is_less_than_alpha = __lasx_xvslt_bu(p0_asub_q0, alpha); - is_less_than_beta = __lasx_xvslt_bu(p1_asub_p0, beta); - is_less_than = is_less_than_alpha & is_less_than_beta; - is_less_than_beta = __lasx_xvslt_bu(q1_asub_q0, beta); - is_less_than = is_less_than_beta & is_less_than; - - if (__lasx_xbnz_v(is_less_than)) { - __m256i p0_h, q0_h, p1_org_h, p0_org_h, q0_org_h, q1_org_h; - - p1_org_h = __lasx_vext2xv_hu_bu(p1_org); - p0_org_h = __lasx_vext2xv_hu_bu(p0_org); - q0_org_h = __lasx_vext2xv_hu_bu(q0_org); - q1_org_h = __lasx_vext2xv_hu_bu(q1_org); - - AVC_LPF_P0_OR_Q0(p0_org_h, q1_org_h, p1_org_h, p0_h); - AVC_LPF_P0_OR_Q0(q0_org_h, p1_org_h, q1_org_h, q0_h); - DUP2_ARG2(__lasx_xvpickev_b, p0_h, p0_h, q0_h, q0_h, p0_h, q0_h); - DUP2_ARG2(__lasx_xvpermi_d, p0_h, 0xd8, q0_h, 0xd8, p0_h, q0_h); - p0_h = __lasx_xvbitsel_v(p0_org, p0_h, is_less_than); - q0_h = __lasx_xvbitsel_v(q0_org, q0_h, is_less_than); - __lasx_xvstelm_d(p0_h, data - img_width, 0, 0); - __lasx_xvstelm_d(q0_h, data, 0, 0); - } -} - -void ff_biweight_h264_pixels16_8_lasx(uint8_t *dst, uint8_t *src, - ptrdiff_t stride, int height, - int log2_denom, int weight_dst, - int weight_src, int offset_in) -{ - __m256i wgt; - __m256i src0, src1, src2, src3; - __m256i dst0, dst1, dst2, dst3; - __m256i vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; - __m256i denom, offset; - int stride_2x = stride << 1; - int stride_4x = stride << 2; - int stride_3x = stride_2x + stride; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp4, tmp5, tmp6, tmp7); - src += stride_4x; - DUP4_ARG3(__lasx_xvpermi_q, tmp1, tmp0, 0x20, tmp3, tmp2, 0x20, tmp5, tmp4, - 0x20, tmp7, tmp6, 0x20, src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp0, tmp1, tmp2, tmp3); - dst += stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp4, tmp5, tmp6, tmp7); - dst -= stride_4x; - DUP4_ARG3(__lasx_xvpermi_q, tmp1, tmp0, 0x20, tmp3, tmp2, 0x20, tmp5, tmp4, - 0x20, tmp7, tmp6, 0x20, dst0, dst1, dst2, dst3); - - DUP4_ARG2(__lasx_xvxori_b, src0, 128, src1, 128, src2, 128, src3, 128, - src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvxori_b, dst0, 128, dst1, 128, dst2, 128, dst3, 128, - dst0, dst1, dst2, dst3); - DUP4_ARG2(__lasx_xvilvl_b, dst0, src0, dst1, src1, dst2, src2, - dst3, src3, vec0, vec2, vec4, vec6); - DUP4_ARG2(__lasx_xvilvh_b, dst0, src0, dst1, src1, dst2, src2, - dst3, src3, vec1, vec3, vec5, vec7); - - DUP4_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec0, offset, wgt, vec1, - offset, wgt, vec2, offset, wgt, vec3, tmp0, tmp1, tmp2, tmp3); - DUP4_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec4, offset, wgt, vec5, - offset, wgt, vec6, offset, wgt, vec7, tmp4, tmp5, tmp6, tmp7); - - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp1 = __lasx_xvsra_h(tmp1, denom); - tmp2 = __lasx_xvsra_h(tmp2, denom); - tmp3 = __lasx_xvsra_h(tmp3, denom); - tmp4 = __lasx_xvsra_h(tmp4, denom); - tmp5 = __lasx_xvsra_h(tmp5, denom); - tmp6 = __lasx_xvsra_h(tmp6, denom); - tmp7 = __lasx_xvsra_h(tmp7, denom); - - DUP4_ARG1(__lasx_xvclip255_h, tmp0, tmp1, tmp2, tmp3, - tmp0, tmp1, tmp2, tmp3); - DUP4_ARG1(__lasx_xvclip255_h, tmp4, tmp5, tmp6, tmp7, - tmp4, tmp5, tmp6, tmp7); - DUP4_ARG2(__lasx_xvpickev_b, tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, tmp6, - dst0, dst1, dst2, dst3); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst0, dst, 0, 2); - __lasx_xvstelm_d(dst0, dst, 8, 3); - dst += stride; - __lasx_xvstelm_d(dst1, dst, 0, 0); - __lasx_xvstelm_d(dst1, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst1, dst, 0, 2); - __lasx_xvstelm_d(dst1, dst, 8, 3); - dst += stride; - __lasx_xvstelm_d(dst2, dst, 0, 0); - __lasx_xvstelm_d(dst2, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst2, dst, 0, 2); - __lasx_xvstelm_d(dst2, dst, 8, 3); - dst += stride; - __lasx_xvstelm_d(dst3, dst, 0, 0); - __lasx_xvstelm_d(dst3, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst3, dst, 0, 2); - __lasx_xvstelm_d(dst3, dst, 8, 3); - dst += stride; - - if (16 == height) { - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp4, tmp5, tmp6, tmp7); - src += stride_4x; - DUP4_ARG3(__lasx_xvpermi_q, tmp1, tmp0, 0x20, tmp3, tmp2, 0x20, tmp5, - tmp4, 0x20, tmp7, tmp6, 0x20, src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp0, tmp1, tmp2, tmp3); - dst += stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp4, tmp5, tmp6, tmp7); - dst -= stride_4x; - DUP4_ARG3(__lasx_xvpermi_q, tmp1, tmp0, 0x20, tmp3, tmp2, 0x20, tmp5, - tmp4, 0x20, tmp7, tmp6, 0x20, dst0, dst1, dst2, dst3); - - DUP4_ARG2(__lasx_xvxori_b, src0, 128, src1, 128, src2, 128, src3, 128, - src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvxori_b, dst0, 128, dst1, 128, dst2, 128, dst3, 128, - dst0, dst1, dst2, dst3); - DUP4_ARG2(__lasx_xvilvl_b, dst0, src0, dst1, src1, dst2, src2, - dst3, src3, vec0, vec2, vec4, vec6); - DUP4_ARG2(__lasx_xvilvh_b, dst0, src0, dst1, src1, dst2, src2, - dst3, src3, vec1, vec3, vec5, vec7); - - DUP4_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec0, offset, wgt, vec1, - offset, wgt, vec2, offset, wgt, vec3, tmp0, tmp1, tmp2, tmp3); - DUP4_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec4, offset, wgt, vec5, - offset, wgt, vec6, offset, wgt, vec7, tmp4, tmp5, tmp6, tmp7); - - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp1 = __lasx_xvsra_h(tmp1, denom); - tmp2 = __lasx_xvsra_h(tmp2, denom); - tmp3 = __lasx_xvsra_h(tmp3, denom); - tmp4 = __lasx_xvsra_h(tmp4, denom); - tmp5 = __lasx_xvsra_h(tmp5, denom); - tmp6 = __lasx_xvsra_h(tmp6, denom); - tmp7 = __lasx_xvsra_h(tmp7, denom); - - DUP4_ARG1(__lasx_xvclip255_h, tmp0, tmp1, tmp2, tmp3, - tmp0, tmp1, tmp2, tmp3); - DUP4_ARG1(__lasx_xvclip255_h, tmp4, tmp5, tmp6, tmp7, - tmp4, tmp5, tmp6, tmp7); - DUP4_ARG2(__lasx_xvpickev_b, tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, - tmp6, dst0, dst1, dst2, dst3); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst0, dst, 0, 2); - __lasx_xvstelm_d(dst0, dst, 8, 3); - dst += stride; - __lasx_xvstelm_d(dst1, dst, 0, 0); - __lasx_xvstelm_d(dst1, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst1, dst, 0, 2); - __lasx_xvstelm_d(dst1, dst, 8, 3); - dst += stride; - __lasx_xvstelm_d(dst2, dst, 0, 0); - __lasx_xvstelm_d(dst2, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst2, dst, 0, 2); - __lasx_xvstelm_d(dst2, dst, 8, 3); - dst += stride; - __lasx_xvstelm_d(dst3, dst, 0, 0); - __lasx_xvstelm_d(dst3, dst, 8, 1); - dst += stride; - __lasx_xvstelm_d(dst3, dst, 0, 2); - __lasx_xvstelm_d(dst3, dst, 8, 3); - } -} - -static void avc_biwgt_8x4_lasx(uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t weight_dst, int32_t offset_in) -{ - __m256i wgt, vec0, vec1; - __m256i src0, dst0; - __m256i tmp0, tmp1, tmp2, tmp3, denom, offset; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP2_ARG2(__lasx_xvxori_b, src0, 128, dst0, 128, src0, dst0); - vec0 = __lasx_xvilvl_b(dst0, src0); - vec1 = __lasx_xvilvh_b(dst0, src0); - DUP2_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec0, offset, wgt, vec1, - tmp0, tmp1); - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp1 = __lasx_xvsra_h(tmp1, denom); - DUP2_ARG1(__lasx_xvclip255_h, tmp0, tmp1, tmp0, tmp1); - dst0 = __lasx_xvpickev_b(tmp1, tmp0); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + stride, 0, 1); - __lasx_xvstelm_d(dst0, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst0, dst + stride_3x, 0, 3); -} - -static void avc_biwgt_8x8_lasx(uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t weight_dst, int32_t offset_in) -{ - __m256i wgt, vec0, vec1, vec2, vec3; - __m256i src0, src1, dst0, dst1; - __m256i tmp0, tmp1, tmp2, tmp3, denom, offset; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - uint8_t* dst_tmp = dst; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src1 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - tmp0 = __lasx_xvld(dst_tmp, 0); - DUP2_ARG2(__lasx_xvldx, dst_tmp, stride, dst_tmp, stride_2x, tmp1, tmp2); - tmp3 = __lasx_xvldx(dst_tmp, stride_3x); - dst_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, dst_tmp, 0, dst_tmp, stride, dst_tmp, stride_2x, - dst_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst1 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - - DUP4_ARG2(__lasx_xvxori_b, src0, 128, src1, 128, dst0, 128, dst1, 128, - src0, src1, dst0, dst1); - DUP2_ARG2(__lasx_xvilvl_b, dst0, src0, dst1, src1, vec0, vec2); - DUP2_ARG2(__lasx_xvilvh_b, dst0, src0, dst1, src1, vec1, vec3); - DUP4_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec0, offset, wgt, vec1, - offset, wgt, vec2, offset, wgt, vec3, tmp0, tmp1, tmp2, tmp3); - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp1 = __lasx_xvsra_h(tmp1, denom); - tmp2 = __lasx_xvsra_h(tmp2, denom); - tmp3 = __lasx_xvsra_h(tmp3, denom); - DUP4_ARG1(__lasx_xvclip255_h, tmp0, tmp1, tmp2, tmp3, - tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvpickev_b, tmp1, tmp0, tmp3, tmp2, dst0, dst1); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + stride, 0, 1); - __lasx_xvstelm_d(dst0, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(dst1, dst, 0, 0); - __lasx_xvstelm_d(dst1, dst + stride, 0, 1); - __lasx_xvstelm_d(dst1, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst1, dst + stride_3x, 0, 3); -} - -static void avc_biwgt_8x16_lasx(uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t weight_dst, int32_t offset_in) -{ - __m256i wgt, vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7; - __m256i src0, src1, src2, src3, dst0, dst1, dst2, dst3; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, denom, offset; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - uint8_t* dst_tmp = dst; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src1 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src2 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src3 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - - DUP4_ARG2(__lasx_xvldx, dst_tmp, 0, dst_tmp, stride, dst_tmp, stride_2x, - dst_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - dst_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, dst_tmp, 0, dst_tmp, stride, dst_tmp, stride_2x, - dst_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - dst_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst1 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, dst_tmp, 0, dst_tmp, stride, dst_tmp, stride_2x, - dst_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - dst_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst2 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, dst_tmp, 0, dst_tmp, stride, dst_tmp, stride_2x, - dst_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst3 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - - DUP4_ARG2(__lasx_xvxori_b, src0, 128, src1, 128, src2, 128, src3, 128, - src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvxori_b, dst0, 128, dst1, 128, dst2, 128, dst3, 128, - dst0, dst1, dst2, dst3); - DUP4_ARG2(__lasx_xvilvl_b, dst0, src0, dst1, src1, dst2, src2, - dst3, src3, vec0, vec2, vec4, vec6); - DUP4_ARG2(__lasx_xvilvh_b, dst0, src0, dst1, src1, dst2, src2, - dst3, src3, vec1, vec3, vec5, vec7); - DUP4_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec0, offset, wgt, vec1, - offset, wgt, vec2, offset, wgt, vec3, tmp0, tmp1, tmp2, tmp3); - DUP4_ARG3(__lasx_xvdp2add_h_b,offset, wgt, vec4, offset, wgt, vec5, - offset, wgt, vec6, offset, wgt, vec7, tmp4, tmp5, tmp6, tmp7); - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp1 = __lasx_xvsra_h(tmp1, denom); - tmp2 = __lasx_xvsra_h(tmp2, denom); - tmp3 = __lasx_xvsra_h(tmp3, denom); - tmp4 = __lasx_xvsra_h(tmp4, denom); - tmp5 = __lasx_xvsra_h(tmp5, denom); - tmp6 = __lasx_xvsra_h(tmp6, denom); - tmp7 = __lasx_xvsra_h(tmp7, denom); - DUP4_ARG1(__lasx_xvclip255_h, tmp0, tmp1, tmp2, tmp3, - tmp0, tmp1, tmp2, tmp3); - DUP4_ARG1(__lasx_xvclip255_h, tmp4, tmp5, tmp6, tmp7, - tmp4, tmp5, tmp6, tmp7); - DUP4_ARG2(__lasx_xvpickev_b, tmp1, tmp0, tmp3, tmp2, tmp5, tmp4, tmp7, tmp6, - dst0, dst1, dst2, dst3) - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + stride, 0, 1); - __lasx_xvstelm_d(dst0, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(dst1, dst, 0, 0); - __lasx_xvstelm_d(dst1, dst + stride, 0, 1); - __lasx_xvstelm_d(dst1, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst1, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(dst2, dst, 0, 0); - __lasx_xvstelm_d(dst2, dst + stride, 0, 1); - __lasx_xvstelm_d(dst2, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst2, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_d(dst3, dst, 0, 0); - __lasx_xvstelm_d(dst3, dst + stride, 0, 1); - __lasx_xvstelm_d(dst3, dst + stride_2x, 0, 2); - __lasx_xvstelm_d(dst3, dst + stride_3x, 0, 3); -} - -void ff_biweight_h264_pixels8_8_lasx(uint8_t *dst, uint8_t *src, - ptrdiff_t stride, int height, - int log2_denom, int weight_dst, - int weight_src, int offset) -{ - if (4 == height) { - avc_biwgt_8x4_lasx(src, dst, stride, log2_denom, weight_src, weight_dst, - offset); - } else if (8 == height) { - avc_biwgt_8x8_lasx(src, dst, stride, log2_denom, weight_src, weight_dst, - offset); - } else { - avc_biwgt_8x16_lasx(src, dst, stride, log2_denom, weight_src, weight_dst, - offset); - } -} - -static void avc_biwgt_4x2_lasx(uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t weight_dst, int32_t offset_in) -{ - __m256i wgt, vec0; - __m256i src0, dst0; - __m256i tmp0, tmp1, denom, offset; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP2_ARG2(__lasx_xvldx, src, 0, src, stride, tmp0, tmp1); - src0 = __lasx_xvilvl_w(tmp1, tmp0); - DUP2_ARG2(__lasx_xvldx, dst, 0, dst, stride, tmp0, tmp1); - dst0 = __lasx_xvilvl_w(tmp1, tmp0); - DUP2_ARG2(__lasx_xvxori_b, src0, 128, dst0, 128, src0, dst0); - vec0 = __lasx_xvilvl_b(dst0, src0); - tmp0 = __lasx_xvdp2add_h_b(offset, wgt, vec0); - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp0 = __lasx_xvclip255_h(tmp0); - tmp0 = __lasx_xvpickev_b(tmp0, tmp0); - __lasx_xvstelm_w(tmp0, dst, 0, 0); - __lasx_xvstelm_w(tmp0, dst + stride, 0, 1); -} - -static void avc_biwgt_4x4_lasx(uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t weight_dst, int32_t offset_in) -{ - __m256i wgt, vec0; - __m256i src0, dst0; - __m256i tmp0, tmp1, tmp2, tmp3, denom, offset; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_w, tmp2, tmp0, tmp3, tmp1, tmp0, tmp1); - src0 = __lasx_xvilvl_w(tmp1, tmp0); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_w, tmp2, tmp0, tmp3, tmp1, tmp0, tmp1); - dst0 = __lasx_xvilvl_w(tmp1, tmp0); - DUP2_ARG2(__lasx_xvxori_b, src0, 128, dst0, 128, src0, dst0); - vec0 = __lasx_xvilvl_b(dst0, src0); - dst0 = __lasx_xvilvh_b(dst0, src0); - vec0 = __lasx_xvpermi_q(vec0, dst0, 0x02); - tmp0 = __lasx_xvdp2add_h_b(offset, wgt, vec0); - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp0 = __lasx_xvclip255_h(tmp0); - tmp0 = __lasx_xvpickev_b(tmp0, tmp0); - __lasx_xvstelm_w(tmp0, dst, 0, 0); - __lasx_xvstelm_w(tmp0, dst + stride, 0, 1); - __lasx_xvstelm_w(tmp0, dst + stride_2x, 0, 4); - __lasx_xvstelm_w(tmp0, dst + stride_3x, 0, 5); -} - -static void avc_biwgt_4x8_lasx(uint8_t *src, uint8_t *dst, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t weight_dst, int32_t offset_in) -{ - __m256i wgt, vec0, vec1; - __m256i src0, dst0; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, denom, offset; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_in = (unsigned) ((offset_in + 1) | 1) << log2_denom; - offset_in += ((weight_src + weight_dst) << 7); - log2_denom += 1; - - tmp0 = __lasx_xvreplgr2vr_b(weight_src); - tmp1 = __lasx_xvreplgr2vr_b(weight_dst); - wgt = __lasx_xvilvh_b(tmp1, tmp0); - offset = __lasx_xvreplgr2vr_h(offset_in); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp4, tmp5, tmp6, tmp7); - DUP4_ARG2(__lasx_xvilvl_w, tmp2, tmp0, tmp3, tmp1, tmp6, tmp4, tmp7, tmp5, - tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_w, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp0, tmp1, tmp2, tmp3); - dst += stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, stride, dst, stride_2x, - dst, stride_3x, tmp4, tmp5, tmp6, tmp7); - dst -= stride_4x; - DUP4_ARG2(__lasx_xvilvl_w, tmp2, tmp0, tmp3, tmp1, tmp6, tmp4, tmp7, tmp5, - tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_w, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - dst0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP2_ARG2(__lasx_xvxori_b, src0, 128, dst0, 128, src0, dst0); - vec0 = __lasx_xvilvl_b(dst0, src0); - vec1 = __lasx_xvilvh_b(dst0, src0); - DUP2_ARG3(__lasx_xvdp2add_h_b, offset, wgt, vec0, offset, wgt, vec1, - tmp0, tmp1); - tmp0 = __lasx_xvsra_h(tmp0, denom); - tmp1 = __lasx_xvsra_h(tmp1, denom); - DUP2_ARG1(__lasx_xvclip255_h, tmp0, tmp1, tmp0, tmp1); - tmp0 = __lasx_xvpickev_b(tmp1, tmp0); - __lasx_xvstelm_w(tmp0, dst, 0, 0); - __lasx_xvstelm_w(tmp0, dst + stride, 0, 1); - __lasx_xvstelm_w(tmp0, dst + stride_2x, 0, 2); - __lasx_xvstelm_w(tmp0, dst + stride_3x, 0, 3); - dst += stride_4x; - __lasx_xvstelm_w(tmp0, dst, 0, 4); - __lasx_xvstelm_w(tmp0, dst + stride, 0, 5); - __lasx_xvstelm_w(tmp0, dst + stride_2x, 0, 6); - __lasx_xvstelm_w(tmp0, dst + stride_3x, 0, 7); -} - -void ff_biweight_h264_pixels4_8_lasx(uint8_t *dst, uint8_t *src, - ptrdiff_t stride, int height, - int log2_denom, int weight_dst, - int weight_src, int offset) -{ - if (2 == height) { - avc_biwgt_4x2_lasx(src, dst, stride, log2_denom, weight_src, - weight_dst, offset); - } else if (4 == height) { - avc_biwgt_4x4_lasx(src, dst, stride, log2_denom, weight_src, - weight_dst, offset); - } else { - avc_biwgt_4x8_lasx(src, dst, stride, log2_denom, weight_src, - weight_dst, offset); - } -} - -void ff_weight_h264_pixels16_8_lasx(uint8_t *src, ptrdiff_t stride, - int height, int log2_denom, - int weight_src, int offset_in) -{ - uint32_t offset_val; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - __m256i zero = __lasx_xvldi(0); - __m256i src0, src1, src2, src3; - __m256i src0_l, src1_l, src2_l, src3_l, src0_h, src1_h, src2_h, src3_h; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; - __m256i wgt, denom, offset; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(weight_src); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp4, tmp5, tmp6, tmp7); - src -= stride_4x; - DUP4_ARG3(__lasx_xvpermi_q, tmp1, tmp0, 0x20, tmp3, tmp2, 0x20, tmp5, tmp4, - 0x20, tmp7, tmp6, 0x20, src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvilvl_b, zero, src0, zero, src1, zero, src2, - zero, src3, src0_l, src1_l, src2_l, src3_l); - DUP4_ARG2(__lasx_xvilvh_b, zero, src0, zero, src1, zero, src2, - zero, src3, src0_h, src1_h, src2_h, src3_h); - src0_l = __lasx_xvmul_h(wgt, src0_l); - src0_h = __lasx_xvmul_h(wgt, src0_h); - src1_l = __lasx_xvmul_h(wgt, src1_l); - src1_h = __lasx_xvmul_h(wgt, src1_h); - src2_l = __lasx_xvmul_h(wgt, src2_l); - src2_h = __lasx_xvmul_h(wgt, src2_h); - src3_l = __lasx_xvmul_h(wgt, src3_l); - src3_h = __lasx_xvmul_h(wgt, src3_h); - DUP4_ARG2(__lasx_xvsadd_h, src0_l, offset, src0_h, offset, src1_l, offset, - src1_h, offset, src0_l, src0_h, src1_l, src1_h); - DUP4_ARG2(__lasx_xvsadd_h, src2_l, offset, src2_h, offset, src3_l, offset, - src3_h, offset, src2_l, src2_h, src3_l, src3_h); - src0_l = __lasx_xvmaxi_h(src0_l, 0); - src0_h = __lasx_xvmaxi_h(src0_h, 0); - src1_l = __lasx_xvmaxi_h(src1_l, 0); - src1_h = __lasx_xvmaxi_h(src1_h, 0); - src2_l = __lasx_xvmaxi_h(src2_l, 0); - src2_h = __lasx_xvmaxi_h(src2_h, 0); - src3_l = __lasx_xvmaxi_h(src3_l, 0); - src3_h = __lasx_xvmaxi_h(src3_h, 0); - src0_l = __lasx_xvssrlrn_bu_h(src0_l, denom); - src0_h = __lasx_xvssrlrn_bu_h(src0_h, denom); - src1_l = __lasx_xvssrlrn_bu_h(src1_l, denom); - src1_h = __lasx_xvssrlrn_bu_h(src1_h, denom); - src2_l = __lasx_xvssrlrn_bu_h(src2_l, denom); - src2_h = __lasx_xvssrlrn_bu_h(src2_h, denom); - src3_l = __lasx_xvssrlrn_bu_h(src3_l, denom); - src3_h = __lasx_xvssrlrn_bu_h(src3_h, denom); - __lasx_xvstelm_d(src0_l, src, 0, 0); - __lasx_xvstelm_d(src0_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src0_l, src, 0, 2); - __lasx_xvstelm_d(src0_h, src, 8, 2); - src += stride; - __lasx_xvstelm_d(src1_l, src, 0, 0); - __lasx_xvstelm_d(src1_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src1_l, src, 0, 2); - __lasx_xvstelm_d(src1_h, src, 8, 2); - src += stride; - __lasx_xvstelm_d(src2_l, src, 0, 0); - __lasx_xvstelm_d(src2_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src2_l, src, 0, 2); - __lasx_xvstelm_d(src2_h, src, 8, 2); - src += stride; - __lasx_xvstelm_d(src3_l, src, 0, 0); - __lasx_xvstelm_d(src3_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src3_l, src, 0, 2); - __lasx_xvstelm_d(src3_h, src, 8, 2); - src += stride; - - if (16 == height) { - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp4, tmp5, tmp6, tmp7); - src -= stride_4x; - DUP4_ARG3(__lasx_xvpermi_q, tmp1, tmp0, 0x20, tmp3, tmp2, 0x20, tmp5, - tmp4, 0x20, tmp7, tmp6, 0x20, src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvilvl_b, zero, src0, zero, src1, zero, src2, - zero, src3, src0_l, src1_l, src2_l, src3_l); - DUP4_ARG2(__lasx_xvilvh_b, zero, src0, zero, src1, zero, src2, - zero, src3, src0_h, src1_h, src2_h, src3_h); - src0_l = __lasx_xvmul_h(wgt, src0_l); - src0_h = __lasx_xvmul_h(wgt, src0_h); - src1_l = __lasx_xvmul_h(wgt, src1_l); - src1_h = __lasx_xvmul_h(wgt, src1_h); - src2_l = __lasx_xvmul_h(wgt, src2_l); - src2_h = __lasx_xvmul_h(wgt, src2_h); - src3_l = __lasx_xvmul_h(wgt, src3_l); - src3_h = __lasx_xvmul_h(wgt, src3_h); - DUP4_ARG2(__lasx_xvsadd_h, src0_l, offset, src0_h, offset, src1_l, - offset, src1_h, offset, src0_l, src0_h, src1_l, src1_h); - DUP4_ARG2(__lasx_xvsadd_h, src2_l, offset, src2_h, offset, src3_l, - offset, src3_h, offset, src2_l, src2_h, src3_l, src3_h); - src0_l = __lasx_xvmaxi_h(src0_l, 0); - src0_h = __lasx_xvmaxi_h(src0_h, 0); - src1_l = __lasx_xvmaxi_h(src1_l, 0); - src1_h = __lasx_xvmaxi_h(src1_h, 0); - src2_l = __lasx_xvmaxi_h(src2_l, 0); - src2_h = __lasx_xvmaxi_h(src2_h, 0); - src3_l = __lasx_xvmaxi_h(src3_l, 0); - src3_h = __lasx_xvmaxi_h(src3_h, 0); - src0_l = __lasx_xvssrlrn_bu_h(src0_l, denom); - src0_h = __lasx_xvssrlrn_bu_h(src0_h, denom); - src1_l = __lasx_xvssrlrn_bu_h(src1_l, denom); - src1_h = __lasx_xvssrlrn_bu_h(src1_h, denom); - src2_l = __lasx_xvssrlrn_bu_h(src2_l, denom); - src2_h = __lasx_xvssrlrn_bu_h(src2_h, denom); - src3_l = __lasx_xvssrlrn_bu_h(src3_l, denom); - src3_h = __lasx_xvssrlrn_bu_h(src3_h, denom); - __lasx_xvstelm_d(src0_l, src, 0, 0); - __lasx_xvstelm_d(src0_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src0_l, src, 0, 2); - __lasx_xvstelm_d(src0_h, src, 8, 2); - src += stride; - __lasx_xvstelm_d(src1_l, src, 0, 0); - __lasx_xvstelm_d(src1_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src1_l, src, 0, 2); - __lasx_xvstelm_d(src1_h, src, 8, 2); - src += stride; - __lasx_xvstelm_d(src2_l, src, 0, 0); - __lasx_xvstelm_d(src2_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src2_l, src, 0, 2); - __lasx_xvstelm_d(src2_h, src, 8, 2); - src += stride; - __lasx_xvstelm_d(src3_l, src, 0, 0); - __lasx_xvstelm_d(src3_h, src, 8, 0); - src += stride; - __lasx_xvstelm_d(src3_l, src, 0, 2); - __lasx_xvstelm_d(src3_h, src, 8, 2); - } -} - -static void avc_wgt_8x4_lasx(uint8_t *src, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t offset_in) -{ - uint32_t offset_val; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - __m256i wgt, zero = __lasx_xvldi(0); - __m256i src0, src0_h, src0_l; - __m256i tmp0, tmp1, tmp2, tmp3, denom, offset; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(weight_src); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - src0_l = __lasx_xvilvl_b(zero, src0); - src0_h = __lasx_xvilvh_b(zero, src0); - src0_l = __lasx_xvmul_h(wgt, src0_l); - src0_h = __lasx_xvmul_h(wgt, src0_h); - src0_l = __lasx_xvsadd_h(src0_l, offset); - src0_h = __lasx_xvsadd_h(src0_h, offset); - src0_l = __lasx_xvmaxi_h(src0_l, 0); - src0_h = __lasx_xvmaxi_h(src0_h, 0); - src0_l = __lasx_xvssrlrn_bu_h(src0_l, denom); - src0_h = __lasx_xvssrlrn_bu_h(src0_h, denom); - - src0 = __lasx_xvpickev_d(src0_h, src0_l); - __lasx_xvstelm_d(src0, src, 0, 0); - __lasx_xvstelm_d(src0, src + stride, 0, 1); - __lasx_xvstelm_d(src0, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src0, src + stride_3x, 0, 3); -} - -static void avc_wgt_8x8_lasx(uint8_t *src, ptrdiff_t stride, int32_t log2_denom, - int32_t src_weight, int32_t offset_in) -{ - __m256i src0, src1, src0_h, src0_l, src1_h, src1_l, zero = __lasx_xvldi(0); - __m256i tmp0, tmp1, tmp2, tmp3, denom, offset, wgt; - uint32_t offset_val; - uint8_t* src_tmp = src; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(src_weight); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src_tmp, 0, src_tmp, stride, src_tmp, stride_2x, - src_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - src_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src_tmp, 0, src_tmp, stride, src_tmp, stride_2x, - src_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src1 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP2_ARG2(__lasx_xvilvl_b, zero, src0, zero, src1, src0_l, src1_l); - DUP2_ARG2(__lasx_xvilvh_b, zero, src0, zero, src1, src0_h, src1_h); - src0_l = __lasx_xvmul_h(wgt, src0_l); - src0_h = __lasx_xvmul_h(wgt, src0_h); - src1_l = __lasx_xvmul_h(wgt, src1_l); - src1_h = __lasx_xvmul_h(wgt, src1_h); - DUP4_ARG2(__lasx_xvsadd_h, src0_l, offset, src0_h, offset, src1_l, offset, - src1_h, offset, src0_l, src0_h, src1_l, src1_h); - src0_l = __lasx_xvmaxi_h(src0_l, 0); - src0_h = __lasx_xvmaxi_h(src0_h, 0); - src1_l = __lasx_xvmaxi_h(src1_l, 0); - src1_h = __lasx_xvmaxi_h(src1_h, 0); - src0_l = __lasx_xvssrlrn_bu_h(src0_l, denom); - src0_h = __lasx_xvssrlrn_bu_h(src0_h, denom); - src1_l = __lasx_xvssrlrn_bu_h(src1_l, denom); - src1_h = __lasx_xvssrlrn_bu_h(src1_h, denom); - - DUP2_ARG2(__lasx_xvpickev_d, src0_h, src0_l, src1_h, src1_l, src0, src1); - __lasx_xvstelm_d(src0, src, 0, 0); - __lasx_xvstelm_d(src0, src + stride, 0, 1); - __lasx_xvstelm_d(src0, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src0, src + stride_3x, 0, 3); - src += stride_4x; - __lasx_xvstelm_d(src1, src, 0, 0); - __lasx_xvstelm_d(src1, src + stride, 0, 1); - __lasx_xvstelm_d(src1, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src1, src + stride_3x, 0, 3); -} - -static void avc_wgt_8x16_lasx(uint8_t *src, ptrdiff_t stride, - int32_t log2_denom, int32_t src_weight, - int32_t offset_in) -{ - __m256i src0, src1, src2, src3; - __m256i src0_h, src0_l, src1_h, src1_l, src2_h, src2_l, src3_h, src3_l; - __m256i tmp0, tmp1, tmp2, tmp3, denom, offset, wgt; - __m256i zero = __lasx_xvldi(0); - uint32_t offset_val; - uint8_t* src_tmp = src; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(src_weight); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src_tmp, 0, src_tmp, stride, src_tmp, stride_2x, - src_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - src_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src_tmp, 0, src_tmp, stride, src_tmp, stride_2x, - src_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - src_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src1 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src_tmp, 0, src_tmp, stride, src_tmp, stride_2x, - src_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - src_tmp += stride_4x; - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src2 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - DUP4_ARG2(__lasx_xvldx, src_tmp, 0, src_tmp, stride, src_tmp, stride_2x, - src_tmp, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_d, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src3 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - - DUP4_ARG2(__lasx_xvilvl_b, zero, src0, zero, src1, zero, src2, zero, src3, - src0_l, src1_l, src2_l, src3_l); - DUP4_ARG2(__lasx_xvilvh_b, zero, src0, zero, src1, zero, src2, zero, src3, - src0_h, src1_h, src2_h, src3_h); - src0_l = __lasx_xvmul_h(wgt, src0_l); - src0_h = __lasx_xvmul_h(wgt, src0_h); - src1_l = __lasx_xvmul_h(wgt, src1_l); - src1_h = __lasx_xvmul_h(wgt, src1_h); - src2_l = __lasx_xvmul_h(wgt, src2_l); - src2_h = __lasx_xvmul_h(wgt, src2_h); - src3_l = __lasx_xvmul_h(wgt, src3_l); - src3_h = __lasx_xvmul_h(wgt, src3_h); - - DUP4_ARG2(__lasx_xvsadd_h, src0_l, offset, src0_h, offset, src1_l, offset, - src1_h, offset, src0_l, src0_h, src1_l, src1_h); - DUP4_ARG2(__lasx_xvsadd_h, src2_l, offset, src2_h, offset, src3_l, offset, - src3_h, offset, src2_l, src2_h, src3_l, src3_h); - - src0_l = __lasx_xvmaxi_h(src0_l, 0); - src0_h = __lasx_xvmaxi_h(src0_h, 0); - src1_l = __lasx_xvmaxi_h(src1_l, 0); - src1_h = __lasx_xvmaxi_h(src1_h, 0); - src2_l = __lasx_xvmaxi_h(src2_l, 0); - src2_h = __lasx_xvmaxi_h(src2_h, 0); - src3_l = __lasx_xvmaxi_h(src3_l, 0); - src3_h = __lasx_xvmaxi_h(src3_h, 0); - src0_l = __lasx_xvssrlrn_bu_h(src0_l, denom); - src0_h = __lasx_xvssrlrn_bu_h(src0_h, denom); - src1_l = __lasx_xvssrlrn_bu_h(src1_l, denom); - src1_h = __lasx_xvssrlrn_bu_h(src1_h, denom); - src2_l = __lasx_xvssrlrn_bu_h(src2_l, denom); - src2_h = __lasx_xvssrlrn_bu_h(src2_h, denom); - src3_l = __lasx_xvssrlrn_bu_h(src3_l, denom); - src3_h = __lasx_xvssrlrn_bu_h(src3_h, denom); - DUP4_ARG2(__lasx_xvpickev_d, src0_h, src0_l, src1_h, src1_l, src2_h, src2_l, - src3_h, src3_l, src0, src1, src2, src3); - - __lasx_xvstelm_d(src0, src, 0, 0); - __lasx_xvstelm_d(src0, src + stride, 0, 1); - __lasx_xvstelm_d(src0, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src0, src + stride_3x, 0, 3); - src += stride_4x; - __lasx_xvstelm_d(src1, src, 0, 0); - __lasx_xvstelm_d(src1, src + stride, 0, 1); - __lasx_xvstelm_d(src1, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src1, src + stride_3x, 0, 3); - src += stride_4x; - __lasx_xvstelm_d(src2, src, 0, 0); - __lasx_xvstelm_d(src2, src + stride, 0, 1); - __lasx_xvstelm_d(src2, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src2, src + stride_3x, 0, 3); - src += stride_4x; - __lasx_xvstelm_d(src3, src, 0, 0); - __lasx_xvstelm_d(src3, src + stride, 0, 1); - __lasx_xvstelm_d(src3, src + stride_2x, 0, 2); - __lasx_xvstelm_d(src3, src + stride_3x, 0, 3); -} - -void ff_weight_h264_pixels8_8_lasx(uint8_t *src, ptrdiff_t stride, - int height, int log2_denom, - int weight_src, int offset) -{ - if (4 == height) { - avc_wgt_8x4_lasx(src, stride, log2_denom, weight_src, offset); - } else if (8 == height) { - avc_wgt_8x8_lasx(src, stride, log2_denom, weight_src, offset); - } else { - avc_wgt_8x16_lasx(src, stride, log2_denom, weight_src, offset); - } -} - -static void avc_wgt_4x2_lasx(uint8_t *src, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t offset_in) -{ - uint32_t offset_val; - __m256i wgt, zero = __lasx_xvldi(0); - __m256i src0, tmp0, tmp1, denom, offset; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(weight_src); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP2_ARG2(__lasx_xvldx, src, 0, src, stride, tmp0, tmp1); - src0 = __lasx_xvilvl_w(tmp1, tmp0); - src0 = __lasx_xvilvl_b(zero, src0); - src0 = __lasx_xvmul_h(wgt, src0); - src0 = __lasx_xvsadd_h(src0, offset); - src0 = __lasx_xvmaxi_h(src0, 0); - src0 = __lasx_xvssrlrn_bu_h(src0, denom); - __lasx_xvstelm_w(src0, src, 0, 0); - __lasx_xvstelm_w(src0, src + stride, 0, 1); -} - -static void avc_wgt_4x4_lasx(uint8_t *src, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t offset_in) -{ - __m256i wgt; - __m256i src0, tmp0, tmp1, tmp2, tmp3, denom, offset; - uint32_t offset_val; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(weight_src); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_w, tmp2, tmp0, tmp3, tmp1, tmp0, tmp1); - src0 = __lasx_xvilvl_w(tmp1, tmp0); - src0 = __lasx_vext2xv_hu_bu(src0); - src0 = __lasx_xvmul_h(wgt, src0); - src0 = __lasx_xvsadd_h(src0, offset); - src0 = __lasx_xvmaxi_h(src0, 0); - src0 = __lasx_xvssrlrn_bu_h(src0, denom); - __lasx_xvstelm_w(src0, src, 0, 0); - __lasx_xvstelm_w(src0, src + stride, 0, 1); - __lasx_xvstelm_w(src0, src + stride_2x, 0, 4); - __lasx_xvstelm_w(src0, src + stride_3x, 0, 5); -} - -static void avc_wgt_4x8_lasx(uint8_t *src, ptrdiff_t stride, - int32_t log2_denom, int32_t weight_src, - int32_t offset_in) -{ - __m256i src0, src0_h, src0_l; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, denom, offset; - __m256i wgt, zero = __lasx_xvldi(0); - uint32_t offset_val; - ptrdiff_t stride_2x = stride << 1; - ptrdiff_t stride_4x = stride << 2; - ptrdiff_t stride_3x = stride_2x + stride; - - offset_val = (unsigned) offset_in << log2_denom; - - wgt = __lasx_xvreplgr2vr_h(weight_src); - offset = __lasx_xvreplgr2vr_h(offset_val); - denom = __lasx_xvreplgr2vr_h(log2_denom); - - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp0, tmp1, tmp2, tmp3); - src += stride_4x; - DUP4_ARG2(__lasx_xvldx, src, 0, src, stride, src, stride_2x, - src, stride_3x, tmp4, tmp5, tmp6, tmp7); - src -= stride_4x; - DUP4_ARG2(__lasx_xvilvl_w, tmp2, tmp0, tmp3, tmp1, tmp6, tmp4, tmp7, - tmp5, tmp0, tmp1, tmp2, tmp3); - DUP2_ARG2(__lasx_xvilvl_w, tmp1, tmp0, tmp3, tmp2, tmp0, tmp1); - src0 = __lasx_xvpermi_q(tmp1, tmp0, 0x20); - src0_l = __lasx_xvilvl_b(zero, src0); - src0_h = __lasx_xvilvh_b(zero, src0); - src0_l = __lasx_xvmul_h(wgt, src0_l); - src0_h = __lasx_xvmul_h(wgt, src0_h); - src0_l = __lasx_xvsadd_h(src0_l, offset); - src0_h = __lasx_xvsadd_h(src0_h, offset); - src0_l = __lasx_xvmaxi_h(src0_l, 0); - src0_h = __lasx_xvmaxi_h(src0_h, 0); - src0_l = __lasx_xvssrlrn_bu_h(src0_l, denom); - src0_h = __lasx_xvssrlrn_bu_h(src0_h, denom); - __lasx_xvstelm_w(src0_l, src, 0, 0); - __lasx_xvstelm_w(src0_l, src + stride, 0, 1); - __lasx_xvstelm_w(src0_h, src + stride_2x, 0, 0); - __lasx_xvstelm_w(src0_h, src + stride_3x, 0, 1); - src += stride_4x; - __lasx_xvstelm_w(src0_l, src, 0, 4); - __lasx_xvstelm_w(src0_l, src + stride, 0, 5); - __lasx_xvstelm_w(src0_h, src + stride_2x, 0, 4); - __lasx_xvstelm_w(src0_h, src + stride_3x, 0, 5); -} - -void ff_weight_h264_pixels4_8_lasx(uint8_t *src, ptrdiff_t stride, - int height, int log2_denom, - int weight_src, int offset) -{ - if (2 == height) { - avc_wgt_4x2_lasx(src, stride, log2_denom, weight_src, offset); - } else if (4 == height) { - avc_wgt_4x4_lasx(src, stride, log2_denom, weight_src, offset); - } else { - avc_wgt_4x8_lasx(src, stride, log2_denom, weight_src, offset); - } -} - void ff_h264_add_pixels4_8_lasx(uint8_t *_dst, int16_t *_src, int stride) { __m256i src0, dst0, dst1, dst2, dst3, zero; diff --git a/libavcodec/loongarch/h264dsp_lasx.h b/libavcodec/loongarch/h264dsp_lasx.h deleted file mode 100644 index 4cf813750bb..00000000000 --- a/libavcodec/loongarch/h264dsp_lasx.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2021 Loongson Technology Corporation Limited - * Contributed by Shiyou Yin - * Xiwei Gu - * - * 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 - */ - -#ifndef AVCODEC_LOONGARCH_H264DSP_LASX_H -#define AVCODEC_LOONGARCH_H264DSP_LASX_H - -#include "libavcodec/h264dec.h" - -void ff_h264_h_lpf_luma_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta, int8_t *tc0); -void ff_h264_v_lpf_luma_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta, int8_t *tc0); -void ff_h264_h_lpf_luma_intra_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta); -void ff_h264_v_lpf_luma_intra_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta); -void ff_h264_h_lpf_chroma_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta, int8_t *tc0); -void ff_h264_v_lpf_chroma_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta, int8_t *tc0); -void ff_h264_h_lpf_chroma_intra_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta); -void ff_h264_v_lpf_chroma_intra_8_lasx(uint8_t *src, ptrdiff_t stride, - int alpha, int beta); -void ff_biweight_h264_pixels16_8_lasx(uint8_t *dst, uint8_t *src, - ptrdiff_t stride, int height, - int log2_denom, int weight_dst, - int weight_src, int offset_in); -void ff_biweight_h264_pixels8_8_lasx(uint8_t *dst, uint8_t *src, - ptrdiff_t stride, int height, - int log2_denom, int weight_dst, - int weight_src, int offset); -void ff_biweight_h264_pixels4_8_lasx(uint8_t *dst, uint8_t *src, - ptrdiff_t stride, int height, - int log2_denom, int weight_dst, - int weight_src, int offset); -void ff_weight_h264_pixels16_8_lasx(uint8_t *src, ptrdiff_t stride, - int height, int log2_denom, - int weight_src, int offset_in); -void ff_weight_h264_pixels8_8_lasx(uint8_t *src, ptrdiff_t stride, - int height, int log2_denom, - int weight_src, int offset); -void ff_weight_h264_pixels4_8_lasx(uint8_t *src, ptrdiff_t stride, - int height, int log2_denom, - int weight_src, int offset); -void ff_h264_add_pixels4_8_lasx(uint8_t *_dst, int16_t *_src, int stride); - -void ff_h264_add_pixels8_8_lasx(uint8_t *_dst, int16_t *_src, int stride); -void ff_h264_idct_add_lasx(uint8_t *dst, int16_t *src, int32_t dst_stride); -void ff_h264_idct8_addblk_lasx(uint8_t *dst, int16_t *src, int32_t dst_stride); -void ff_h264_idct4x4_addblk_dc_lasx(uint8_t *dst, int16_t *src, - int32_t dst_stride); -void ff_h264_idct8_dc_addblk_lasx(uint8_t *dst, int16_t *src, - int32_t dst_stride); -void ff_h264_idct_add16_lasx(uint8_t *dst, const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]); -void ff_h264_idct8_add4_lasx(uint8_t *dst, const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]); -void ff_h264_idct_add8_lasx(uint8_t **dst, const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]); -void ff_h264_idct_add8_422_lasx(uint8_t **dst, const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]); -void ff_h264_idct_add16_intra_lasx(uint8_t *dst, const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]); -void ff_h264_deq_idct_luma_dc_lasx(int16_t *dst, int16_t *src, - int32_t de_qval); - -void ff_h264_loop_filter_strength_lasx(int16_t bS[2][4][4], uint8_t nnz[40], - int8_t ref[2][40], int16_t mv[2][40][2], - int bidir, int edges, int step, - int mask_mv0, int mask_mv1, int field); - -#endif // #ifndef AVCODEC_LOONGARCH_H264DSP_LASX_H diff --git a/libavcodec/loongarch/h264dsp_loongarch.h b/libavcodec/loongarch/h264dsp_loongarch.h new file mode 100644 index 00000000000..e17522dfe04 --- /dev/null +++ b/libavcodec/loongarch/h264dsp_loongarch.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Shiyou Yin + * Xiwei Gu + * + * 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 + */ + +#ifndef AVCODEC_LOONGARCH_H264DSP_LOONGARCH_H +#define AVCODEC_LOONGARCH_H264DSP_LOONGARCH_H + +#include "libavcodec/h264dec.h" +#include "config.h" + +void ff_h264_idct_add_8_lsx(uint8_t *dst, int16_t *src, int dst_stride); +void ff_h264_idct8_add_8_lsx(uint8_t *dst, int16_t *src, int dst_stride); +void ff_h264_idct_dc_add_8_lsx(uint8_t *dst, int16_t *src, int dst_stride); +void ff_h264_idct8_dc_add_8_lsx(uint8_t *dst, int16_t *src, int dst_stride); +void ff_h264_luma_dc_dequant_idct_8_lsx(int16_t *_output, int16_t *_input, int qmul); +void ff_h264_idct_add16_8_lsx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]); +void ff_h264_idct8_add4_8_lsx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]); +void ff_h264_idct_add8_8_lsx(uint8_t **dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]); +void ff_h264_idct_add8_422_8_lsx(uint8_t **dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]); +void ff_h264_idct_add16_intra_8_lsx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]); + +void ff_h264_h_lpf_luma_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_v_lpf_luma_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_h_lpf_luma_intra_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta); +void ff_h264_v_lpf_luma_intra_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta); +void ff_h264_h_lpf_chroma_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_v_lpf_chroma_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_h_lpf_chroma_intra_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta); +void ff_h264_v_lpf_chroma_intra_8_lsx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta); +void ff_biweight_h264_pixels16_8_lsx(uint8_t *dst, uint8_t *src, + ptrdiff_t stride, int height, + int log2_denom, int weight_dst, + int weight_src, int offset_in); +void ff_biweight_h264_pixels8_8_lsx(uint8_t *dst, uint8_t *src, + ptrdiff_t stride, int height, + int log2_denom, int weight_dst, + int weight_src, int offset); +void ff_biweight_h264_pixels4_8_lsx(uint8_t *dst, uint8_t *src, + ptrdiff_t stride, int height, + int log2_denom, int weight_dst, + int weight_src, int offset); +void ff_weight_h264_pixels16_8_lsx(uint8_t *src, ptrdiff_t stride, + int height, int log2_denom, + int weight_src, int offset_in); +void ff_weight_h264_pixels8_8_lsx(uint8_t *src, ptrdiff_t stride, + int height, int log2_denom, + int weight_src, int offset); +void ff_weight_h264_pixels4_8_lsx(uint8_t *src, ptrdiff_t stride, + int height, int log2_denom, + int weight_src, int offset); +void ff_h264_add_pixels4_8_lsx(uint8_t *_dst, int16_t *_src, int stride); +void ff_h264_add_pixels8_8_lsx(uint8_t *_dst, int16_t *_src, int stride); +void ff_h264_loop_filter_strength_lsx(int16_t bS[2][4][4], uint8_t nnz[40], + int8_t ref[2][40], int16_t mv[2][40][2], + int bidir, int edges, int step, + int mask_mv0, int mask_mv1, int field); + +#if HAVE_LASX +void ff_h264_h_lpf_luma_8_lasx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_v_lpf_luma_8_lasx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_h_lpf_luma_intra_8_lasx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta); +void ff_h264_v_lpf_luma_intra_8_lasx(uint8_t *src, ptrdiff_t stride, + int alpha, int beta); +void ff_biweight_h264_pixels16_8_lasx(unsigned char *dst, unsigned char *src, + long int stride, int height, + int log2_denom, int weight_dst, + int weight_src, int offset_in); +void ff_biweight_h264_pixels8_8_lasx(unsigned char *dst, unsigned char *src, + long int stride, int height, + int log2_denom, int weight_dst, + int weight_src, int offset); +void ff_weight_h264_pixels16_8_lasx(uint8_t *src, ptrdiff_t stride, + int height, int log2_denom, + int weight_src, int offset_in); +void ff_weight_h264_pixels8_8_lasx(uint8_t *src, ptrdiff_t stride, + int height, int log2_denom, + int weight_src, int offset); +void ff_h264_add_pixels4_8_lasx(uint8_t *_dst, int16_t *_src, int stride); + +void ff_h264_add_pixels8_8_lasx(uint8_t *_dst, int16_t *_src, int stride); +void ff_h264_idct8_add_8_lasx(uint8_t *dst, int16_t *src, int32_t dst_stride); +void ff_h264_idct8_dc_add_8_lasx(uint8_t *dst, int16_t *src, + int32_t dst_stride); +void ff_h264_idct8_add4_8_lasx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]); +void ff_h264_loop_filter_strength_lasx(int16_t bS[2][4][4], uint8_t nnz[40], + int8_t ref[2][40], int16_t mv[2][40][2], + int bidir, int edges, int step, + int mask_mv0, int mask_mv1, int field); +#endif // #if HAVE_LASX + +#endif // #ifndef AVCODEC_LOONGARCH_H264DSP_LOONGARCH_H diff --git a/libavcodec/loongarch/h264idct.S b/libavcodec/loongarch/h264idct.S new file mode 100644 index 00000000000..f504cfb7148 --- /dev/null +++ b/libavcodec/loongarch/h264idct.S @@ -0,0 +1,658 @@ +/* + * Loongson LASX optimized h264idct + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Shiyou Yin + * + * 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 "loongson_asm.S" + +/* + * #define FUNC2(a, b, c) FUNC3(a, b, c) + * #define FUNCC(a) FUNC2(a, BIT_DEPTH, _c) + * void FUNCC(ff_h264_idct_add)(uint8_t *_dst, int16_t *_block, int stride) + * LSX optimization is enough for this function. + */ +function ff_h264_idct_add_8_lsx + fld.d f0, a1, 0 + fld.d f1, a1, 8 + fld.d f2, a1, 16 + fld.d f3, a1, 24 + vxor.v vr7, vr7, vr7 + add.d t2, a2, a2 + add.d t3, t2, a2 + vst vr7, a1, 0 + vst vr7, a1, 16 + + vadd.h vr4, vr0, vr2 + vsub.h vr5, vr0, vr2 + vsrai.h vr6, vr1, 1 + vsrai.h vr7, vr3, 1 + vsub.h vr6, vr6, vr3 + vadd.h vr7, vr1, vr7 + LSX_BUTTERFLY_4_H vr4, vr5, vr6, vr7, vr0, vr1, vr2, vr3 + LSX_TRANSPOSE4x4_H vr0, vr1, vr2, vr3, vr0, vr1, vr2, vr3, vr4, vr5 + vadd.h vr4, vr0, vr2 + vsub.h vr5, vr0, vr2 + vsrai.h vr6, vr1, 1 + vsrai.h vr7, vr3, 1 + vsub.h vr6, vr6, vr3 + vadd.h vr7, vr1, vr7 + LSX_BUTTERFLY_4_H vr4, vr5, vr6, vr7, vr0, vr1, vr2, vr3 + + fld.s f4, a0, 0 + fldx.s f5, a0, a2 + fldx.s f6, a0, t2 + fldx.s f7, a0, t3 + + vsrari.h vr0, vr0, 6 + vsrari.h vr1, vr1, 6 + vsrari.h vr2, vr2, 6 + vsrari.h vr3, vr3, 6 + + vsllwil.hu.bu vr4, vr4, 0 + vsllwil.hu.bu vr5, vr5, 0 + vsllwil.hu.bu vr6, vr6, 0 + vsllwil.hu.bu vr7, vr7, 0 + vadd.h vr0, vr0, vr4 + vadd.h vr1, vr1, vr5 + vadd.h vr2, vr2, vr6 + vadd.h vr3, vr3, vr7 + vssrarni.bu.h vr1, vr0, 0 + vssrarni.bu.h vr3, vr2, 0 + + vbsrl.v vr0, vr1, 8 + vbsrl.v vr2, vr3, 8 + fst.s f1, a0, 0 + fstx.s f0, a0, a2 + fstx.s f3, a0, t2 + fstx.s f2, a0, t3 +endfunc + +/* + * #define FUNC2(a, b, c) FUNC3(a, b, c) + * #define FUNCC(a) FUNC2(a, BIT_DEPTH, _c) + * void FUNCC(ff_h264_idct8_add)(uint8_t *_dst, int16_t *_block, int stride) + */ +function ff_h264_idct8_add_8_lsx + ld.h t0, a1, 0 + add.d t2, a2, a2 + add.d t3, t2, a2 + add.d t4, t3, a2 + add.d t5, t4, a2 + add.d t6, t5, a2 + add.d t7, t6, a2 + addi.w t0, t0, 32 + st.h t0, a1, 0 + + vld vr0, a1, 0 + vld vr1, a1, 16 + vld vr2, a1, 32 + vld vr3, a1, 48 + vld vr4, a1, 64 + vld vr5, a1, 80 + vld vr6, a1, 96 + vld vr7, a1, 112 + vxor.v vr8, vr8, vr8 + vst vr8, a1, 0 + vst vr8, a1, 16 + vst vr8, a1, 32 + vst vr8, a1, 48 + vst vr8, a1, 64 + vst vr8, a1, 80 + vst vr8, a1, 96 + vst vr8, a1, 112 + + vadd.h vr18, vr0, vr4 + vsub.h vr19, vr0, vr4 + vsrai.h vr20, vr2, 1 + vsrai.h vr21, vr6, 1 + vsub.h vr20, vr20, vr6 + vadd.h vr21, vr21, vr2 + LSX_BUTTERFLY_4_H vr18, vr19, vr20, vr21, vr10, vr12, vr14, vr16 + vsrai.h vr11, vr7, 1 + vsrai.h vr13, vr3, 1 + vsrai.h vr15, vr5, 1 + vsrai.h vr17, vr1, 1 + vsub.h vr11, vr5, vr11 + vsub.h vr13, vr7, vr13 + vadd.h vr15, vr7, vr15 + vadd.h vr17, vr5, vr17 + vsub.h vr11, vr11, vr7 + vsub.h vr13, vr13, vr3 + vadd.h vr15, vr15, vr5 + vadd.h vr17, vr17, vr1 + vsub.h vr11, vr11, vr3 + vadd.h vr13, vr13, vr1 + vsub.h vr15, vr15, vr1 + vadd.h vr17, vr17, vr3 + vsrai.h vr18, vr11, 2 + vsrai.h vr19, vr13, 2 + vsrai.h vr20, vr15, 2 + vsrai.h vr21, vr17, 2 + vadd.h vr11, vr11, vr21 + vadd.h vr13, vr13, vr20 + vsub.h vr15, vr19, vr15 + vsub.h vr17, vr17, vr18 + LSX_BUTTERFLY_8_H vr10, vr16, vr12, vr14, vr13, vr15, vr11, vr17, \ + vr0, vr3, vr1, vr2, vr5, vr6, vr4, vr7 + + LSX_TRANSPOSE8x8_H vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr10, vr11, vr12, vr13, vr14, vr15, vr16, vr17 + vexth.w.h vr20, vr0 + vexth.w.h vr21, vr1 + vexth.w.h vr22, vr2 + vexth.w.h vr23, vr3 + vexth.w.h vr8, vr4 + vexth.w.h vr9, vr5 + vexth.w.h vr18, vr6 + vexth.w.h vr19, vr7 + vsllwil.w.h vr0, vr0, 0 + vsllwil.w.h vr1, vr1, 0 + vsllwil.w.h vr2, vr2, 0 + vsllwil.w.h vr3, vr3, 0 + vsllwil.w.h vr4, vr4, 0 + vsllwil.w.h vr5, vr5, 0 + vsllwil.w.h vr6, vr6, 0 + vsllwil.w.h vr7, vr7, 0 + + vadd.w vr11, vr0, vr4 + vsub.w vr13, vr0, vr4 + vsrai.w vr15, vr2, 1 + vsrai.w vr17, vr6, 1 + vsub.w vr15, vr15, vr6 + vadd.w vr17, vr17, vr2 + LSX_BUTTERFLY_4_W vr11, vr13, vr15, vr17, vr10, vr12, vr14, vr16 + vsrai.w vr11, vr7, 1 + vsrai.w vr13, vr3, 1 + vsrai.w vr15, vr5, 1 + vsrai.w vr17, vr1, 1 + vsub.w vr11, vr5, vr11 + vsub.w vr13, vr7, vr13 + vadd.w vr15, vr7, vr15 + vadd.w vr17, vr5, vr17 + vsub.w vr11, vr11, vr7 + vsub.w vr13, vr13, vr3 + vadd.w vr15, vr15, vr5 + vadd.w vr17, vr17, vr1 + vsub.w vr11, vr11, vr3 + vadd.w vr13, vr13, vr1 + vsub.w vr15, vr15, vr1 + vadd.w vr17, vr17, vr3 + vsrai.w vr0, vr11, 2 + vsrai.w vr1, vr13, 2 + vsrai.w vr2, vr15, 2 + vsrai.w vr3, vr17, 2 + vadd.w vr11, vr11, vr3 + vadd.w vr13, vr13, vr2 + vsub.w vr15, vr1, vr15 + vsub.w vr17, vr17, vr0 + LSX_BUTTERFLY_8_W vr10, vr12, vr14, vr16, vr11, vr13, vr15, vr17, \ + vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7 + + vadd.w vr11, vr20, vr8 + vsub.w vr13, vr20, vr8 + vsrai.w vr15, vr22, 1 + vsrai.w vr17, vr18, 1 + vsub.w vr15, vr15, vr18 + vadd.w vr17, vr17, vr22 + LSX_BUTTERFLY_4_W vr11, vr13, vr15, vr17, vr10, vr12, vr14, vr16 + vsrai.w vr11, vr19, 1 + vsrai.w vr13, vr23, 1 + vsrai.w vr15, vr9, 1 + vsrai.w vr17, vr21, 1 + vsub.w vr11, vr9, vr11 + vsub.w vr13, vr19, vr13 + vadd.w vr15, vr19, vr15 + vadd.w vr17, vr9, vr17 + vsub.w vr11, vr11, vr19 + vsub.w vr13, vr13, vr23 + vadd.w vr15, vr15, vr9 + vadd.w vr17, vr17, vr21 + vsub.w vr11, vr11, vr23 + vadd.w vr13, vr13, vr21 + vsub.w vr15, vr15, vr21 + vadd.w vr17, vr17, vr23 + vsrai.w vr20, vr11, 2 + vsrai.w vr21, vr13, 2 + vsrai.w vr22, vr15, 2 + vsrai.w vr23, vr17, 2 + vadd.w vr11, vr11, vr23 + vadd.w vr13, vr13, vr22 + vsub.w vr15, vr21, vr15 + vsub.w vr17, vr17, vr20 + LSX_BUTTERFLY_8_W vr10, vr12, vr14, vr16, vr11, vr13, vr15, vr17, \ + vr20, vr21, vr22, vr23, vr8, vr9, vr18, vr19 + + vld vr10, a0, 0 + vldx vr11, a0, a2 + vldx vr12, a0, t2 + vldx vr13, a0, t3 + vldx vr14, a0, t4 + vldx vr15, a0, t5 + vldx vr16, a0, t6 + vldx vr17, a0, t7 + vsrani.h.w vr20, vr0, 6 + vsrani.h.w vr21, vr1, 6 + vsrani.h.w vr22, vr2, 6 + vsrani.h.w vr23, vr3, 6 + vsrani.h.w vr8, vr4, 6 + vsrani.h.w vr9, vr5, 6 + vsrani.h.w vr18, vr6, 6 + vsrani.h.w vr19, vr7, 6 + vsllwil.hu.bu vr10, vr10, 0 + vsllwil.hu.bu vr11, vr11, 0 + vsllwil.hu.bu vr12, vr12, 0 + vsllwil.hu.bu vr13, vr13, 0 + vsllwil.hu.bu vr14, vr14, 0 + vsllwil.hu.bu vr15, vr15, 0 + vsllwil.hu.bu vr16, vr16, 0 + vsllwil.hu.bu vr17, vr17, 0 + + vadd.h vr0, vr20, vr10 + vadd.h vr1, vr21, vr11 + vadd.h vr2, vr22, vr12 + vadd.h vr3, vr23, vr13 + vadd.h vr4, vr8, vr14 + vadd.h vr5, vr9, vr15 + vadd.h vr6, vr18, vr16 + vadd.h vr7, vr19, vr17 + vssrarni.bu.h vr1, vr0, 0 + vssrarni.bu.h vr3, vr2, 0 + vssrarni.bu.h vr5, vr4, 0 + vssrarni.bu.h vr7, vr6, 0 + vbsrl.v vr0, vr1, 8 + vbsrl.v vr2, vr3, 8 + vbsrl.v vr4, vr5, 8 + vbsrl.v vr6, vr7, 8 + fst.d f1, a0, 0 + fstx.d f0, a0, a2 + fstx.d f3, a0, t2 + fstx.d f2, a0, t3 + fstx.d f5, a0, t4 + fstx.d f4, a0, t5 + fstx.d f7, a0, t6 + fstx.d f6, a0, t7 +endfunc + +/* + * #define FUNC2(a, b, c) FUNC3(a, b, c) + * #define FUNCC(a) FUNC2(a, BIT_DEPTH, _c) + * void FUNCC(ff_h264_idct8_add)(uint8_t *_dst, int16_t *_block, int stride) + */ +function ff_h264_idct8_add_8_lasx + ld.h t0, a1, 0 + add.d t2, a2, a2 + add.d t3, t2, a2 + add.d t4, t3, a2 + add.d t5, t4, a2 + add.d t6, t5, a2 + add.d t7, t6, a2 + addi.w t0, t0, 32 + st.h t0, a1, 0 + + vld vr0, a1, 0 + vld vr1, a1, 16 + vld vr2, a1, 32 + vld vr3, a1, 48 + vld vr4, a1, 64 + vld vr5, a1, 80 + vld vr6, a1, 96 + vld vr7, a1, 112 + xvxor.v xr8, xr8, xr8 + xvst xr8, a1, 0 + xvst xr8, a1, 32 + xvst xr8, a1, 64 + xvst xr8, a1, 96 + + vadd.h vr18, vr0, vr4 + vsub.h vr19, vr0, vr4 + vsrai.h vr20, vr2, 1 + vsrai.h vr21, vr6, 1 + vsub.h vr20, vr20, vr6 + vadd.h vr21, vr21, vr2 + LSX_BUTTERFLY_4_H vr18, vr19, vr20, vr21, vr10, vr12, vr14, vr16 + vsrai.h vr11, vr7, 1 + vsrai.h vr13, vr3, 1 + vsrai.h vr15, vr5, 1 + vsrai.h vr17, vr1, 1 + vsub.h vr11, vr5, vr11 + vsub.h vr13, vr7, vr13 + vadd.h vr15, vr7, vr15 + vadd.h vr17, vr5, vr17 + vsub.h vr11, vr11, vr7 + vsub.h vr13, vr13, vr3 + vadd.h vr15, vr15, vr5 + vadd.h vr17, vr17, vr1 + vsub.h vr11, vr11, vr3 + vadd.h vr13, vr13, vr1 + vsub.h vr15, vr15, vr1 + vadd.h vr17, vr17, vr3 + vsrai.h vr18, vr11, 2 + vsrai.h vr19, vr13, 2 + vsrai.h vr20, vr15, 2 + vsrai.h vr21, vr17, 2 + vadd.h vr11, vr11, vr21 + vadd.h vr13, vr13, vr20 + vsub.h vr15, vr19, vr15 + vsub.h vr17, vr17, vr18 + LSX_BUTTERFLY_8_H vr10, vr16, vr12, vr14, vr13, vr15, vr11, vr17, \ + vr0, vr3, vr1, vr2, vr5, vr6, vr4, vr7 + + LSX_TRANSPOSE8x8_H vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr10, vr11, vr12, vr13, vr14, vr15, vr16, vr17 + vext2xv.w.h xr0, xr0 + vext2xv.w.h xr1, xr1 + vext2xv.w.h xr2, xr2 + vext2xv.w.h xr3, xr3 + vext2xv.w.h xr4, xr4 + vext2xv.w.h xr5, xr5 + vext2xv.w.h xr6, xr6 + vext2xv.w.h xr7, xr7 + + xvadd.w xr11, xr0, xr4 + xvsub.w xr13, xr0, xr4 + xvsrai.w xr15, xr2, 1 + xvsrai.w xr17, xr6, 1 + xvsub.w xr15, xr15, xr6 + xvadd.w xr17, xr17, xr2 + LASX_BUTTERFLY_4_W xr11, xr13, xr15, xr17, xr10, xr12, xr14, xr16 + xvsrai.w xr11, xr7, 1 + xvsrai.w xr13, xr3, 1 + xvsrai.w xr15, xr5, 1 + xvsrai.w xr17, xr1, 1 + xvsub.w xr11, xr5, xr11 + xvsub.w xr13, xr7, xr13 + xvadd.w xr15, xr7, xr15 + xvadd.w xr17, xr5, xr17 + xvsub.w xr11, xr11, xr7 + xvsub.w xr13, xr13, xr3 + xvadd.w xr15, xr15, xr5 + xvadd.w xr17, xr17, xr1 + xvsub.w xr11, xr11, xr3 + xvadd.w xr13, xr13, xr1 + xvsub.w xr15, xr15, xr1 + xvadd.w xr17, xr17, xr3 + xvsrai.w xr0, xr11, 2 + xvsrai.w xr1, xr13, 2 + xvsrai.w xr2, xr15, 2 + xvsrai.w xr3, xr17, 2 + xvadd.w xr11, xr11, xr3 + xvadd.w xr13, xr13, xr2 + xvsub.w xr15, xr1, xr15 + xvsub.w xr17, xr17, xr0 + LASX_BUTTERFLY_8_W xr10, xr12, xr14, xr16, xr11, xr13, xr15, xr17, \ + xr0, xr1, xr2, xr3, xr4, xr5, xr6, xr7 + + vld vr10, a0, 0 + vldx vr11, a0, a2 + vldx vr12, a0, t2 + vldx vr13, a0, t3 + vldx vr14, a0, t4 + vldx vr15, a0, t5 + vldx vr16, a0, t6 + vldx vr17, a0, t7 + xvldi xr8, 0x806 //"xvldi.w xr8 6" + xvsran.h.w xr0, xr0, xr8 + xvsran.h.w xr1, xr1, xr8 + xvsran.h.w xr2, xr2, xr8 + xvsran.h.w xr3, xr3, xr8 + xvsran.h.w xr4, xr4, xr8 + xvsran.h.w xr5, xr5, xr8 + xvsran.h.w xr6, xr6, xr8 + xvsran.h.w xr7, xr7, xr8 + xvpermi.d xr0, xr0, 0x08 + xvpermi.d xr1, xr1, 0x08 + xvpermi.d xr2, xr2, 0x08 + xvpermi.d xr3, xr3, 0x08 + xvpermi.d xr4, xr4, 0x08 + xvpermi.d xr5, xr5, 0x08 + xvpermi.d xr6, xr6, 0x08 + xvpermi.d xr7, xr7, 0x08 + + vsllwil.hu.bu vr10, vr10, 0 + vsllwil.hu.bu vr11, vr11, 0 + vsllwil.hu.bu vr12, vr12, 0 + vsllwil.hu.bu vr13, vr13, 0 + vsllwil.hu.bu vr14, vr14, 0 + vsllwil.hu.bu vr15, vr15, 0 + vsllwil.hu.bu vr16, vr16, 0 + vsllwil.hu.bu vr17, vr17, 0 + + vadd.h vr0, vr0, vr10 + vadd.h vr1, vr1, vr11 + vadd.h vr2, vr2, vr12 + vadd.h vr3, vr3, vr13 + vadd.h vr4, vr4, vr14 + vadd.h vr5, vr5, vr15 + vadd.h vr6, vr6, vr16 + vadd.h vr7, vr7, vr17 + vssrarni.bu.h vr1, vr0, 0 + vssrarni.bu.h vr3, vr2, 0 + vssrarni.bu.h vr5, vr4, 0 + vssrarni.bu.h vr7, vr6, 0 + vbsrl.v vr0, vr1, 8 + vbsrl.v vr2, vr3, 8 + vbsrl.v vr4, vr5, 8 + vbsrl.v vr6, vr7, 8 + fst.d f1, a0, 0 + fstx.d f0, a0, a2 + fstx.d f3, a0, t2 + fstx.d f2, a0, t3 + fstx.d f5, a0, t4 + fstx.d f4, a0, t5 + fstx.d f7, a0, t6 + fstx.d f6, a0, t7 +endfunc + +/* + * #define FUNC2(a, b, c) FUNC3(a, b, c) + * #define FUNCC(a) FUNC2(a, BIT_DEPTH, _c) + * void FUNCC(ff_h264_idct_dc_add)(uint8_t *_dst, int16_t *_block, int stride) + * LSX optimization is enough for this function. + */ +function ff_h264_idct_dc_add_8_lsx + vldrepl.h vr4, a1, 0 + add.d t2, a2, a2 + add.d t3, t2, a2 + fld.s f0, a0, 0 + fldx.s f1, a0, a2 + fldx.s f2, a0, t2 + fldx.s f3, a0, t3 + st.h zero, a1, 0 + + vsrari.h vr4, vr4, 6 + vilvl.w vr0, vr1, vr0 + vilvl.w vr1, vr3, vr2 + vsllwil.hu.bu vr0, vr0, 0 + vsllwil.hu.bu vr1, vr1, 0 + vadd.h vr0, vr0, vr4 + vadd.h vr1, vr1, vr4 + vssrarni.bu.h vr1, vr0, 0 + + vbsrl.v vr2, vr1, 4 + vbsrl.v vr3, vr1, 8 + vbsrl.v vr4, vr1, 12 + fst.s f1, a0, 0 + fstx.s f2, a0, a2 + fstx.s f3, a0, t2 + fstx.s f4, a0, t3 +endfunc + +/* + * #define FUNC2(a, b, c) FUNC3(a, b, c) + * #define FUNCC(a) FUNC2(a, BIT_DEPTH, _c) + * void FUNCC(ff_h264_idct8_dc_add)(uint8_t *_dst, int16_t *_block, int stride) + */ +function ff_h264_idct8_dc_add_8_lsx + vldrepl.h vr8, a1, 0 + add.d t2, a2, a2 + add.d t3, t2, a2 + add.d t4, t3, a2 + add.d t5, t4, a2 + add.d t6, t5, a2 + add.d t7, t6, a2 + + fld.d f0, a0, 0 + fldx.d f1, a0, a2 + fldx.d f2, a0, t2 + fldx.d f3, a0, t3 + fldx.d f4, a0, t4 + fldx.d f5, a0, t5 + fldx.d f6, a0, t6 + fldx.d f7, a0, t7 + st.h zero, a1, 0 + + vsrari.h vr8, vr8, 6 + vsllwil.hu.bu vr0, vr0, 0 + vsllwil.hu.bu vr1, vr1, 0 + vsllwil.hu.bu vr2, vr2, 0 + vsllwil.hu.bu vr3, vr3, 0 + vsllwil.hu.bu vr4, vr4, 0 + vsllwil.hu.bu vr5, vr5, 0 + vsllwil.hu.bu vr6, vr6, 0 + vsllwil.hu.bu vr7, vr7, 0 + vadd.h vr0, vr0, vr8 + vadd.h vr1, vr1, vr8 + vadd.h vr2, vr2, vr8 + vadd.h vr3, vr3, vr8 + vadd.h vr4, vr4, vr8 + vadd.h vr5, vr5, vr8 + vadd.h vr6, vr6, vr8 + vadd.h vr7, vr7, vr8 + vssrarni.bu.h vr1, vr0, 0 + vssrarni.bu.h vr3, vr2, 0 + vssrarni.bu.h vr5, vr4, 0 + vssrarni.bu.h vr7, vr6, 0 + + vbsrl.v vr0, vr1, 8 + vbsrl.v vr2, vr3, 8 + vbsrl.v vr4, vr5, 8 + vbsrl.v vr6, vr7, 8 + fst.d f1, a0, 0 + fstx.d f0, a0, a2 + fstx.d f3, a0, t2 + fstx.d f2, a0, t3 + fstx.d f5, a0, t4 + fstx.d f4, a0, t5 + fstx.d f7, a0, t6 + fstx.d f6, a0, t7 +endfunc +function ff_h264_idct8_dc_add_8_lasx + xvldrepl.h xr8, a1, 0 + add.d t2, a2, a2 + add.d t3, t2, a2 + add.d t4, t3, a2 + add.d t5, t4, a2 + add.d t6, t5, a2 + add.d t7, t6, a2 + + fld.d f0, a0, 0 + fldx.d f1, a0, a2 + fldx.d f2, a0, t2 + fldx.d f3, a0, t3 + fldx.d f4, a0, t4 + fldx.d f5, a0, t5 + fldx.d f6, a0, t6 + fldx.d f7, a0, t7 + st.h zero, a1, 0 + + xvsrari.h xr8, xr8, 6 + xvpermi.q xr1, xr0, 0x20 + xvpermi.q xr3, xr2, 0x20 + xvpermi.q xr5, xr4, 0x20 + xvpermi.q xr7, xr6, 0x20 + xvsllwil.hu.bu xr1, xr1, 0 + xvsllwil.hu.bu xr3, xr3, 0 + xvsllwil.hu.bu xr5, xr5, 0 + xvsllwil.hu.bu xr7, xr7, 0 + xvadd.h xr1, xr1, xr8 + xvadd.h xr3, xr3, xr8 + xvadd.h xr5, xr5, xr8 + xvadd.h xr7, xr7, xr8 + + xvssrarni.bu.h xr3, xr1, 0 + xvssrarni.bu.h xr7, xr5, 0 + + xvpermi.q xr1, xr3, 0x11 + xvpermi.q xr5, xr7, 0x11 + xvbsrl.v xr0, xr1, 8 + xvbsrl.v xr2, xr3, 8 + xvbsrl.v xr4, xr5, 8 + xvbsrl.v xr6, xr7, 8 + + fst.d f3, a0, 0 + fstx.d f1, a0, a2 + fstx.d f2, a0, t2 + fstx.d f0, a0, t3 + fstx.d f7, a0, t4 + fstx.d f5, a0, t5 + fstx.d f6, a0, t6 + fstx.d f4, a0, t7 +endfunc + +/** + * IDCT transforms the 16 dc values and dequantizes them. + * @param qmul quantization parameter + * void FUNCC(ff_h264_luma_dc_dequant_idct)(int16_t *_output, int16_t *_input, int qmul){ + * LSX optimization is enough for this function. + */ +function ff_h264_luma_dc_dequant_idct_8_lsx + vld vr0, a1, 0 + vld vr1, a1, 8 + vld vr2, a1, 16 + vld vr3, a1, 24 + vreplgr2vr.w vr8, a2 + LSX_TRANSPOSE4x4_H vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, vr9, vr10 + LSX_BUTTERFLY_4_H vr4, vr6, vr7, vr5, vr0, vr3, vr2, vr1 + LSX_BUTTERFLY_4_H vr0, vr1, vr2, vr3, vr4, vr7, vr6, vr5 + LSX_TRANSPOSE4x4_H vr4, vr5, vr6, vr7, vr0, vr1, vr2, vr3, vr9, vr10 + LSX_BUTTERFLY_4_H vr0, vr1, vr3, vr2, vr4, vr7, vr6, vr5 + LSX_BUTTERFLY_4_H vr4, vr5, vr6, vr7, vr0, vr1, vr2, vr3 + vsllwil.w.h vr0, vr0, 0 + vsllwil.w.h vr1, vr1, 0 + vsllwil.w.h vr2, vr2, 0 + vsllwil.w.h vr3, vr3, 0 + vmul.w vr0, vr0, vr8 + vmul.w vr1, vr1, vr8 + vmul.w vr2, vr2, vr8 + vmul.w vr3, vr3, vr8 + vsrarni.h.w vr1, vr0, 8 + vsrarni.h.w vr3, vr2, 8 + + vstelm.h vr1, a0, 0, 0 + vstelm.h vr1, a0, 32, 4 + vstelm.h vr1, a0, 64, 1 + vstelm.h vr1, a0, 96, 5 + vstelm.h vr3, a0, 128, 0 + vstelm.h vr3, a0, 160, 4 + vstelm.h vr3, a0, 192, 1 + vstelm.h vr3, a0, 224, 5 + addi.d a0, a0, 256 + vstelm.h vr1, a0, 0, 2 + vstelm.h vr1, a0, 32, 6 + vstelm.h vr1, a0, 64, 3 + vstelm.h vr1, a0, 96, 7 + vstelm.h vr3, a0, 128, 2 + vstelm.h vr3, a0, 160, 6 + vstelm.h vr3, a0, 192, 3 + vstelm.h vr3, a0, 224, 7 +endfunc diff --git a/libavcodec/loongarch/h264idct_lasx.c b/libavcodec/loongarch/h264idct_lasx.c deleted file mode 100644 index 46bd3b74d5f..00000000000 --- a/libavcodec/loongarch/h264idct_lasx.c +++ /dev/null @@ -1,498 +0,0 @@ -/* - * Loongson LASX optimized h264dsp - * - * Copyright (c) 2021 Loongson Technology Corporation Limited - * Contributed by Shiyou Yin - * Xiwei Gu - * - * 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/loongarch/loongson_intrinsics.h" -#include "h264dsp_lasx.h" -#include "libavcodec/bit_depth_template.c" - -#define AVC_ITRANS_H(in0, in1, in2, in3, out0, out1, out2, out3) \ -{ \ - __m256i tmp0_m, tmp1_m, tmp2_m, tmp3_m; \ - \ - tmp0_m = __lasx_xvadd_h(in0, in2); \ - tmp1_m = __lasx_xvsub_h(in0, in2); \ - tmp2_m = __lasx_xvsrai_h(in1, 1); \ - tmp2_m = __lasx_xvsub_h(tmp2_m, in3); \ - tmp3_m = __lasx_xvsrai_h(in3, 1); \ - tmp3_m = __lasx_xvadd_h(in1, tmp3_m); \ - \ - LASX_BUTTERFLY_4_H(tmp0_m, tmp1_m, tmp2_m, tmp3_m, \ - out0, out1, out2, out3); \ -} - -void ff_h264_idct_add_lasx(uint8_t *dst, int16_t *src, int32_t dst_stride) -{ - __m256i src0_m, src1_m, src2_m, src3_m; - __m256i dst0_m, dst1_m; - __m256i hres0, hres1, hres2, hres3, vres0, vres1, vres2, vres3; - __m256i inp0_m, inp1_m, res0_m, src1, src3; - __m256i src0 = __lasx_xvld(src, 0); - __m256i src2 = __lasx_xvld(src, 16); - __m256i zero = __lasx_xvldi(0); - int32_t dst_stride_2x = dst_stride << 1; - int32_t dst_stride_3x = dst_stride_2x + dst_stride; - - __lasx_xvst(zero, src, 0); - DUP2_ARG2(__lasx_xvilvh_d, src0, src0, src2, src2, src1, src3); - AVC_ITRANS_H(src0, src1, src2, src3, hres0, hres1, hres2, hres3); - LASX_TRANSPOSE4x4_H(hres0, hres1, hres2, hres3, hres0, hres1, hres2, hres3); - AVC_ITRANS_H(hres0, hres1, hres2, hres3, vres0, vres1, vres2, vres3); - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, dst_stride, dst, dst_stride_2x, - dst, dst_stride_3x, src0_m, src1_m, src2_m, src3_m); - DUP4_ARG2(__lasx_xvld, dst, 0, dst + dst_stride, 0, dst + dst_stride_2x, - 0, dst + dst_stride_3x, 0, src0_m, src1_m, src2_m, src3_m); - DUP2_ARG2(__lasx_xvilvl_d, vres1, vres0, vres3, vres2, inp0_m, inp1_m); - inp0_m = __lasx_xvpermi_q(inp1_m, inp0_m, 0x20); - inp0_m = __lasx_xvsrari_h(inp0_m, 6); - DUP2_ARG2(__lasx_xvilvl_w, src1_m, src0_m, src3_m, src2_m, dst0_m, dst1_m); - dst0_m = __lasx_xvilvl_d(dst1_m, dst0_m); - res0_m = __lasx_vext2xv_hu_bu(dst0_m); - res0_m = __lasx_xvadd_h(res0_m, inp0_m); - res0_m = __lasx_xvclip255_h(res0_m); - dst0_m = __lasx_xvpickev_b(res0_m, res0_m); - __lasx_xvstelm_w(dst0_m, dst, 0, 0); - __lasx_xvstelm_w(dst0_m, dst + dst_stride, 0, 1); - __lasx_xvstelm_w(dst0_m, dst + dst_stride_2x, 0, 4); - __lasx_xvstelm_w(dst0_m, dst + dst_stride_3x, 0, 5); -} - -void ff_h264_idct8_addblk_lasx(uint8_t *dst, int16_t *src, - int32_t dst_stride) -{ - __m256i src0, src1, src2, src3, src4, src5, src6, src7; - __m256i vec0, vec1, vec2, vec3; - __m256i tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; - __m256i res0, res1, res2, res3, res4, res5, res6, res7; - __m256i dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7; - __m256i zero = __lasx_xvldi(0); - int32_t dst_stride_2x = dst_stride << 1; - int32_t dst_stride_4x = dst_stride << 2; - int32_t dst_stride_3x = dst_stride_2x + dst_stride; - - src[0] += 32; - DUP4_ARG2(__lasx_xvld, src, 0, src, 16, src, 32, src, 48, - src0, src1, src2, src3); - DUP4_ARG2(__lasx_xvld, src, 64, src, 80, src, 96, src, 112, - src4, src5, src6, src7); - __lasx_xvst(zero, src, 0); - __lasx_xvst(zero, src, 32); - __lasx_xvst(zero, src, 64); - __lasx_xvst(zero, src, 96); - - vec0 = __lasx_xvadd_h(src0, src4); - vec1 = __lasx_xvsub_h(src0, src4); - vec2 = __lasx_xvsrai_h(src2, 1); - vec2 = __lasx_xvsub_h(vec2, src6); - vec3 = __lasx_xvsrai_h(src6, 1); - vec3 = __lasx_xvadd_h(src2, vec3); - - LASX_BUTTERFLY_4_H(vec0, vec1, vec2, vec3, tmp0, tmp1, tmp2, tmp3); - - vec0 = __lasx_xvsrai_h(src7, 1); - vec0 = __lasx_xvsub_h(src5, vec0); - vec0 = __lasx_xvsub_h(vec0, src3); - vec0 = __lasx_xvsub_h(vec0, src7); - - vec1 = __lasx_xvsrai_h(src3, 1); - vec1 = __lasx_xvsub_h(src1, vec1); - vec1 = __lasx_xvadd_h(vec1, src7); - vec1 = __lasx_xvsub_h(vec1, src3); - - vec2 = __lasx_xvsrai_h(src5, 1); - vec2 = __lasx_xvsub_h(vec2, src1); - vec2 = __lasx_xvadd_h(vec2, src7); - vec2 = __lasx_xvadd_h(vec2, src5); - - vec3 = __lasx_xvsrai_h(src1, 1); - vec3 = __lasx_xvadd_h(src3, vec3); - vec3 = __lasx_xvadd_h(vec3, src5); - vec3 = __lasx_xvadd_h(vec3, src1); - - tmp4 = __lasx_xvsrai_h(vec3, 2); - tmp4 = __lasx_xvadd_h(tmp4, vec0); - tmp5 = __lasx_xvsrai_h(vec2, 2); - tmp5 = __lasx_xvadd_h(tmp5, vec1); - tmp6 = __lasx_xvsrai_h(vec1, 2); - tmp6 = __lasx_xvsub_h(tmp6, vec2); - tmp7 = __lasx_xvsrai_h(vec0, 2); - tmp7 = __lasx_xvsub_h(vec3, tmp7); - - LASX_BUTTERFLY_8_H(tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, - res0, res1, res2, res3, res4, res5, res6, res7); - LASX_TRANSPOSE8x8_H(res0, res1, res2, res3, res4, res5, res6, res7, - res0, res1, res2, res3, res4, res5, res6, res7); - - DUP4_ARG1(__lasx_vext2xv_w_h, res0, res1, res2, res3, - tmp0, tmp1, tmp2, tmp3); - DUP4_ARG1(__lasx_vext2xv_w_h, res4, res5, res6, res7, - tmp4, tmp5, tmp6, tmp7); - vec0 = __lasx_xvadd_w(tmp0, tmp4); - vec1 = __lasx_xvsub_w(tmp0, tmp4); - - vec2 = __lasx_xvsrai_w(tmp2, 1); - vec2 = __lasx_xvsub_w(vec2, tmp6); - vec3 = __lasx_xvsrai_w(tmp6, 1); - vec3 = __lasx_xvadd_w(vec3, tmp2); - - tmp0 = __lasx_xvadd_w(vec0, vec3); - tmp2 = __lasx_xvadd_w(vec1, vec2); - tmp4 = __lasx_xvsub_w(vec1, vec2); - tmp6 = __lasx_xvsub_w(vec0, vec3); - - vec0 = __lasx_xvsrai_w(tmp7, 1); - vec0 = __lasx_xvsub_w(tmp5, vec0); - vec0 = __lasx_xvsub_w(vec0, tmp3); - vec0 = __lasx_xvsub_w(vec0, tmp7); - - vec1 = __lasx_xvsrai_w(tmp3, 1); - vec1 = __lasx_xvsub_w(tmp1, vec1); - vec1 = __lasx_xvadd_w(vec1, tmp7); - vec1 = __lasx_xvsub_w(vec1, tmp3); - - vec2 = __lasx_xvsrai_w(tmp5, 1); - vec2 = __lasx_xvsub_w(vec2, tmp1); - vec2 = __lasx_xvadd_w(vec2, tmp7); - vec2 = __lasx_xvadd_w(vec2, tmp5); - - vec3 = __lasx_xvsrai_w(tmp1, 1); - vec3 = __lasx_xvadd_w(tmp3, vec3); - vec3 = __lasx_xvadd_w(vec3, tmp5); - vec3 = __lasx_xvadd_w(vec3, tmp1); - - tmp1 = __lasx_xvsrai_w(vec3, 2); - tmp1 = __lasx_xvadd_w(tmp1, vec0); - tmp3 = __lasx_xvsrai_w(vec2, 2); - tmp3 = __lasx_xvadd_w(tmp3, vec1); - tmp5 = __lasx_xvsrai_w(vec1, 2); - tmp5 = __lasx_xvsub_w(tmp5, vec2); - tmp7 = __lasx_xvsrai_w(vec0, 2); - tmp7 = __lasx_xvsub_w(vec3, tmp7); - - LASX_BUTTERFLY_4_W(tmp0, tmp2, tmp5, tmp7, res0, res1, res6, res7); - LASX_BUTTERFLY_4_W(tmp4, tmp6, tmp1, tmp3, res2, res3, res4, res5); - - DUP4_ARG2(__lasx_xvsrai_w, res0, 6, res1, 6, res2, 6, res3, 6, - res0, res1, res2, res3); - DUP4_ARG2(__lasx_xvsrai_w, res4, 6, res5, 6, res6, 6, res7, 6, - res4, res5, res6, res7); - DUP4_ARG2(__lasx_xvpickev_h, res1, res0, res3, res2, res5, res4, res7, - res6, res0, res1, res2, res3); - DUP4_ARG2(__lasx_xvpermi_d, res0, 0xd8, res1, 0xd8, res2, 0xd8, res3, 0xd8, - res0, res1, res2, res3); - - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, dst_stride, dst, dst_stride_2x, - dst, dst_stride_3x, dst0, dst1, dst2, dst3); - dst += dst_stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, dst_stride, dst, dst_stride_2x, - dst, dst_stride_3x, dst4, dst5, dst6, dst7); - dst -= dst_stride_4x; - DUP4_ARG2(__lasx_xvilvl_b, zero, dst0, zero, dst1, zero, dst2, zero, dst3, - dst0, dst1, dst2, dst3); - DUP4_ARG2(__lasx_xvilvl_b, zero, dst4, zero, dst5, zero, dst6, zero, dst7, - dst4, dst5, dst6, dst7); - DUP4_ARG3(__lasx_xvpermi_q, dst1, dst0, 0x20, dst3, dst2, 0x20, dst5, - dst4, 0x20, dst7, dst6, 0x20, dst0, dst1, dst2, dst3); - res0 = __lasx_xvadd_h(res0, dst0); - res1 = __lasx_xvadd_h(res1, dst1); - res2 = __lasx_xvadd_h(res2, dst2); - res3 = __lasx_xvadd_h(res3, dst3); - DUP4_ARG1(__lasx_xvclip255_h, res0, res1, res2, res3, res0, res1, - res2, res3); - DUP2_ARG2(__lasx_xvpickev_b, res1, res0, res3, res2, res0, res1); - __lasx_xvstelm_d(res0, dst, 0, 0); - __lasx_xvstelm_d(res0, dst + dst_stride, 0, 2); - __lasx_xvstelm_d(res0, dst + dst_stride_2x, 0, 1); - __lasx_xvstelm_d(res0, dst + dst_stride_3x, 0, 3); - dst += dst_stride_4x; - __lasx_xvstelm_d(res1, dst, 0, 0); - __lasx_xvstelm_d(res1, dst + dst_stride, 0, 2); - __lasx_xvstelm_d(res1, dst + dst_stride_2x, 0, 1); - __lasx_xvstelm_d(res1, dst + dst_stride_3x, 0, 3); -} - -void ff_h264_idct4x4_addblk_dc_lasx(uint8_t *dst, int16_t *src, - int32_t dst_stride) -{ - const int16_t dc = (src[0] + 32) >> 6; - int32_t dst_stride_2x = dst_stride << 1; - int32_t dst_stride_3x = dst_stride_2x + dst_stride; - __m256i pred, out; - __m256i src0, src1, src2, src3; - __m256i input_dc = __lasx_xvreplgr2vr_h(dc); - - src[0] = 0; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, dst_stride, dst, dst_stride_2x, - dst, dst_stride_3x, src0, src1, src2, src3); - DUP2_ARG2(__lasx_xvilvl_w, src1, src0, src3, src2, src0, src1); - - pred = __lasx_xvpermi_q(src0, src1, 0x02); - pred = __lasx_xvaddw_h_h_bu(input_dc, pred); - pred = __lasx_xvclip255_h(pred); - out = __lasx_xvpickev_b(pred, pred); - __lasx_xvstelm_w(out, dst, 0, 0); - __lasx_xvstelm_w(out, dst + dst_stride, 0, 1); - __lasx_xvstelm_w(out, dst + dst_stride_2x, 0, 4); - __lasx_xvstelm_w(out, dst + dst_stride_3x, 0, 5); -} - -void ff_h264_idct8_dc_addblk_lasx(uint8_t *dst, int16_t *src, - int32_t dst_stride) -{ - int32_t dc_val; - int32_t dst_stride_2x = dst_stride << 1; - int32_t dst_stride_4x = dst_stride << 2; - int32_t dst_stride_3x = dst_stride_2x + dst_stride; - __m256i dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7; - __m256i dc; - - dc_val = (src[0] + 32) >> 6; - dc = __lasx_xvreplgr2vr_h(dc_val); - - src[0] = 0; - - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, dst_stride, dst, dst_stride_2x, - dst, dst_stride_3x, dst0, dst1, dst2, dst3); - dst += dst_stride_4x; - DUP4_ARG2(__lasx_xvldx, dst, 0, dst, dst_stride, dst, dst_stride_2x, - dst, dst_stride_3x, dst4, dst5, dst6, dst7); - dst -= dst_stride_4x; - DUP4_ARG1(__lasx_vext2xv_hu_bu, dst0, dst1, dst2, dst3, - dst0, dst1, dst2, dst3); - DUP4_ARG1(__lasx_vext2xv_hu_bu, dst4, dst5, dst6, dst7, - dst4, dst5, dst6, dst7); - DUP4_ARG3(__lasx_xvpermi_q, dst1, dst0, 0x20, dst3, dst2, 0x20, dst5, - dst4, 0x20, dst7, dst6, 0x20, dst0, dst1, dst2, dst3); - dst0 = __lasx_xvadd_h(dst0, dc); - dst1 = __lasx_xvadd_h(dst1, dc); - dst2 = __lasx_xvadd_h(dst2, dc); - dst3 = __lasx_xvadd_h(dst3, dc); - DUP4_ARG1(__lasx_xvclip255_h, dst0, dst1, dst2, dst3, - dst0, dst1, dst2, dst3); - DUP2_ARG2(__lasx_xvpickev_b, dst1, dst0, dst3, dst2, dst0, dst1); - __lasx_xvstelm_d(dst0, dst, 0, 0); - __lasx_xvstelm_d(dst0, dst + dst_stride, 0, 2); - __lasx_xvstelm_d(dst0, dst + dst_stride_2x, 0, 1); - __lasx_xvstelm_d(dst0, dst + dst_stride_3x, 0, 3); - dst += dst_stride_4x; - __lasx_xvstelm_d(dst1, dst, 0, 0); - __lasx_xvstelm_d(dst1, dst + dst_stride, 0, 2); - __lasx_xvstelm_d(dst1, dst + dst_stride_2x, 0, 1); - __lasx_xvstelm_d(dst1, dst + dst_stride_3x, 0, 3); -} - -void ff_h264_idct_add16_lasx(uint8_t *dst, - const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]) -{ - int32_t i; - - for (i = 0; i < 16; i++) { - int32_t nnz = nzc[scan8[i]]; - - if (nnz) { - if (nnz == 1 && ((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - else - ff_h264_idct_add_lasx(dst + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - } - } -} - -void ff_h264_idct8_add4_lasx(uint8_t *dst, const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]) -{ - int32_t cnt; - - for (cnt = 0; cnt < 16; cnt += 4) { - int32_t nnz = nzc[scan8[cnt]]; - - if (nnz) { - if (nnz == 1 && ((dctcoef *) block)[cnt * 16]) - ff_h264_idct8_dc_addblk_lasx(dst + blk_offset[cnt], - block + cnt * 16 * sizeof(pixel), - dst_stride); - else - ff_h264_idct8_addblk_lasx(dst + blk_offset[cnt], - block + cnt * 16 * sizeof(pixel), - dst_stride); - } - } -} - - -void ff_h264_idct_add8_lasx(uint8_t **dst, - const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]) -{ - int32_t i; - - for (i = 16; i < 20; i++) { - if (nzc[scan8[i]]) - ff_h264_idct_add_lasx(dst[0] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst[0] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - } - for (i = 32; i < 36; i++) { - if (nzc[scan8[i]]) - ff_h264_idct_add_lasx(dst[1] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst[1] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - } -} - -void ff_h264_idct_add8_422_lasx(uint8_t **dst, - const int32_t *blk_offset, - int16_t *block, int32_t dst_stride, - const uint8_t nzc[15 * 8]) -{ - int32_t i; - - for (i = 16; i < 20; i++) { - if (nzc[scan8[i]]) - ff_h264_idct_add_lasx(dst[0] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst[0] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - } - for (i = 32; i < 36; i++) { - if (nzc[scan8[i]]) - ff_h264_idct_add_lasx(dst[1] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst[1] + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - } - for (i = 20; i < 24; i++) { - if (nzc[scan8[i + 4]]) - ff_h264_idct_add_lasx(dst[0] + blk_offset[i + 4], - block + i * 16 * sizeof(pixel), - dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst[0] + blk_offset[i + 4], - block + i * 16 * sizeof(pixel), - dst_stride); - } - for (i = 36; i < 40; i++) { - if (nzc[scan8[i + 4]]) - ff_h264_idct_add_lasx(dst[1] + blk_offset[i + 4], - block + i * 16 * sizeof(pixel), - dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst[1] + blk_offset[i + 4], - block + i * 16 * sizeof(pixel), - dst_stride); - } -} - -void ff_h264_idct_add16_intra_lasx(uint8_t *dst, - const int32_t *blk_offset, - int16_t *block, - int32_t dst_stride, - const uint8_t nzc[15 * 8]) -{ - int32_t i; - - for (i = 0; i < 16; i++) { - if (nzc[scan8[i]]) - ff_h264_idct_add_lasx(dst + blk_offset[i], - block + i * 16 * sizeof(pixel), dst_stride); - else if (((dctcoef *) block)[i * 16]) - ff_h264_idct4x4_addblk_dc_lasx(dst + blk_offset[i], - block + i * 16 * sizeof(pixel), - dst_stride); - } -} - -void ff_h264_deq_idct_luma_dc_lasx(int16_t *dst, int16_t *src, - int32_t de_qval) -{ -#define DC_DEST_STRIDE 16 - - __m256i src0, src1, src2, src3; - __m256i vec0, vec1, vec2, vec3; - __m256i tmp0, tmp1, tmp2, tmp3; - __m256i hres0, hres1, hres2, hres3; - __m256i vres0, vres1, vres2, vres3; - __m256i de_q_vec = __lasx_xvreplgr2vr_w(de_qval); - - DUP4_ARG2(__lasx_xvld, src, 0, src, 8, src, 16, src, 24, - src0, src1, src2, src3); - LASX_TRANSPOSE4x4_H(src0, src1, src2, src3, tmp0, tmp1, tmp2, tmp3); - LASX_BUTTERFLY_4_H(tmp0, tmp2, tmp3, tmp1, vec0, vec3, vec2, vec1); - LASX_BUTTERFLY_4_H(vec0, vec1, vec2, vec3, hres0, hres3, hres2, hres1); - LASX_TRANSPOSE4x4_H(hres0, hres1, hres2, hres3, - hres0, hres1, hres2, hres3); - LASX_BUTTERFLY_4_H(hres0, hres1, hres3, hres2, vec0, vec3, vec2, vec1); - LASX_BUTTERFLY_4_H(vec0, vec1, vec2, vec3, vres0, vres1, vres2, vres3); - DUP4_ARG1(__lasx_vext2xv_w_h, vres0, vres1, vres2, vres3, - vres0, vres1, vres2, vres3); - DUP2_ARG3(__lasx_xvpermi_q, vres1, vres0, 0x20, vres3, vres2, 0x20, - vres0, vres1); - - vres0 = __lasx_xvmul_w(vres0, de_q_vec); - vres1 = __lasx_xvmul_w(vres1, de_q_vec); - - vres0 = __lasx_xvsrari_w(vres0, 8); - vres1 = __lasx_xvsrari_w(vres1, 8); - vec0 = __lasx_xvpickev_h(vres1, vres0); - vec0 = __lasx_xvpermi_d(vec0, 0xd8); - __lasx_xvstelm_h(vec0, dst + 0 * DC_DEST_STRIDE, 0, 0); - __lasx_xvstelm_h(vec0, dst + 2 * DC_DEST_STRIDE, 0, 1); - __lasx_xvstelm_h(vec0, dst + 8 * DC_DEST_STRIDE, 0, 2); - __lasx_xvstelm_h(vec0, dst + 10 * DC_DEST_STRIDE, 0, 3); - __lasx_xvstelm_h(vec0, dst + 1 * DC_DEST_STRIDE, 0, 4); - __lasx_xvstelm_h(vec0, dst + 3 * DC_DEST_STRIDE, 0, 5); - __lasx_xvstelm_h(vec0, dst + 9 * DC_DEST_STRIDE, 0, 6); - __lasx_xvstelm_h(vec0, dst + 11 * DC_DEST_STRIDE, 0, 7); - __lasx_xvstelm_h(vec0, dst + 4 * DC_DEST_STRIDE, 0, 8); - __lasx_xvstelm_h(vec0, dst + 6 * DC_DEST_STRIDE, 0, 9); - __lasx_xvstelm_h(vec0, dst + 12 * DC_DEST_STRIDE, 0, 10); - __lasx_xvstelm_h(vec0, dst + 14 * DC_DEST_STRIDE, 0, 11); - __lasx_xvstelm_h(vec0, dst + 5 * DC_DEST_STRIDE, 0, 12); - __lasx_xvstelm_h(vec0, dst + 7 * DC_DEST_STRIDE, 0, 13); - __lasx_xvstelm_h(vec0, dst + 13 * DC_DEST_STRIDE, 0, 14); - __lasx_xvstelm_h(vec0, dst + 15 * DC_DEST_STRIDE, 0, 15); - -#undef DC_DEST_STRIDE -} diff --git a/libavcodec/loongarch/h264idct_loongarch.c b/libavcodec/loongarch/h264idct_loongarch.c new file mode 100644 index 00000000000..26af45503f1 --- /dev/null +++ b/libavcodec/loongarch/h264idct_loongarch.c @@ -0,0 +1,184 @@ +/* + * Loongson LSX/LASX optimized h264idct + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Shiyou Yin + * Xiwei Gu + * + * 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 "h264dsp_loongarch.h" +#include "libavcodec/bit_depth_template.c" + +void ff_h264_idct_add16_8_lsx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]) +{ + int32_t i; + + for (i = 0; i < 16; i++) { + int32_t nnz = nzc[scan8[i]]; + + if (nnz == 1 && ((dctcoef *) block)[i * 16]) { + ff_h264_idct_dc_add_8_lsx(dst + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } else if (nnz) { + ff_h264_idct_add_8_lsx(dst + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } + } +} + +void ff_h264_idct8_add4_8_lsx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]) +{ + int32_t cnt; + + for (cnt = 0; cnt < 16; cnt += 4) { + int32_t nnz = nzc[scan8[cnt]]; + + if (nnz == 1 && ((dctcoef *) block)[cnt * 16]) { + ff_h264_idct8_dc_add_8_lsx(dst + blk_offset[cnt], + block + cnt * 16 * sizeof(pixel), + dst_stride); + } else if (nnz) { + ff_h264_idct8_add_8_lsx(dst + blk_offset[cnt], + block + cnt * 16 * sizeof(pixel), + dst_stride); + } + } +} + +#if HAVE_LASX +void ff_h264_idct8_add4_8_lasx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]) +{ + int32_t cnt; + + for (cnt = 0; cnt < 16; cnt += 4) { + int32_t nnz = nzc[scan8[cnt]]; + + if (nnz == 1 && ((dctcoef *) block)[cnt * 16]) { + ff_h264_idct8_dc_add_8_lasx(dst + blk_offset[cnt], + block + cnt * 16 * sizeof(pixel), + dst_stride); + } else if (nnz) { + ff_h264_idct8_add_8_lasx(dst + blk_offset[cnt], + block + cnt * 16 * sizeof(pixel), + dst_stride); + } + } +} +#endif // #if HAVE_LASX + +void ff_h264_idct_add8_8_lsx(uint8_t **dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]) +{ + int32_t i; + + for (i = 16; i < 20; i++) { + if (nzc[scan8[i]]) + ff_h264_idct_add_8_lsx(dst[0] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst[0] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } + for (i = 32; i < 36; i++) { + if (nzc[scan8[i]]) + ff_h264_idct_add_8_lsx(dst[1] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst[1] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } +} + +void ff_h264_idct_add8_422_8_lsx(uint8_t **dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]) +{ + int32_t i; + + for (i = 16; i < 20; i++) { + if (nzc[scan8[i]]) + ff_h264_idct_add_8_lsx(dst[0] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst[0] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } + for (i = 20; i < 24; i++) { + if (nzc[scan8[i + 4]]) + ff_h264_idct_add_8_lsx(dst[0] + blk_offset[i + 4], + block + i * 16 * sizeof(pixel), + dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst[0] + blk_offset[i + 4], + block + i * 16 * sizeof(pixel), + dst_stride); + } + for (i = 32; i < 36; i++) { + if (nzc[scan8[i]]) + ff_h264_idct_add_8_lsx(dst[1] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst[1] + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } + for (i = 36; i < 40; i++) { + if (nzc[scan8[i + 4]]) + ff_h264_idct_add_8_lsx(dst[1] + blk_offset[i + 4], + block + i * 16 * sizeof(pixel), + dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst[1] + blk_offset[i + 4], + block + i * 16 * sizeof(pixel), + dst_stride); + } +} + +void ff_h264_idct_add16_intra_8_lsx(uint8_t *dst, const int32_t *blk_offset, + int16_t *block, int32_t dst_stride, + const uint8_t nzc[15 * 8]) +{ + int32_t i; + + for (i = 0; i < 16; i++) { + if (nzc[scan8[i]]) + ff_h264_idct_add_8_lsx(dst + blk_offset[i], + block + i * 16 * sizeof(pixel), dst_stride); + else if (((dctcoef *) block)[i * 16]) + ff_h264_idct_dc_add_8_lsx(dst + blk_offset[i], + block + i * 16 * sizeof(pixel), + dst_stride); + } +} diff --git a/libavcodec/loongarch/h264intrapred.S b/libavcodec/loongarch/h264intrapred.S new file mode 100644 index 00000000000..a03f467b6ef --- /dev/null +++ b/libavcodec/loongarch/h264intrapred.S @@ -0,0 +1,299 @@ +/* + * Loongson LSX optimized h264intrapred + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Lu Wang + * + * 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 "loongson_asm.S" + +const shufa +.byte 6, 5, 4, 3, 2, 1, 0 +endconst + +const mulk +.byte 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0 +endconst + +const mulh +.byte 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0 +.byte 8, 0, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0 +endconst + +.macro PRED16X16_PLANE + slli.d t6, a1, 1 + slli.d t4, a1, 3 + addi.d t0, a0, 7 + sub.d t0, t0, a1 + add.d t1, a0, t4 + addi.d t1, t1, -1 + sub.d t2, t1, t6 + + ld.bu t3, t0, 1 + ld.bu t4, t0, -1 + ld.bu t5, t1, 0 + ld.bu t7, t2, 0 + sub.d t3, t3, t4 + sub.d t4, t5, t7 + + la.local t5, mulk + vld vr0, t5, 0 + fld.d f1, t0, 2 + fld.d f2, t0, -8 + la.local t5, shufa + fld.d f3, t5, 0 + vshuf.b vr2, vr2, vr2, vr3 + vilvl.b vr1, vr1, vr2 + vhsubw.hu.bu vr1, vr1, vr1 + vmul.h vr0, vr0, vr1 + vhaddw.w.h vr1, vr0, vr0 + vhaddw.d.w vr0, vr1, vr1 + vhaddw.q.d vr1, vr0, vr0 + vpickve2gr.w t5, vr1, 0 + add.d t3, t3, t5 +//2 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ldx.bu t7, t1, a1 + sub.d t5, t7, t8 + slli.d t5, t5, 1 + +//3&4 + add.d t1, t1, t6 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ld.bu t7, t1, 0 + sub.d t7, t7, t8 + slli.d t8, t7, 1 + add.d t7, t7, t8 + add.d t5, t5, t7 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ldx.bu t7, t1, a1 + sub.d t7, t7, t8 + slli.d t7, t7, 2 + add.d t5, t5, t7 + +//5&6 + add.d t1, t1, t6 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ld.bu t7, t1, 0 + sub.d t7, t7, t8 + slli.d t8, t7, 2 + add.d t7, t7, t8 + add.d t5, t5, t7 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ldx.bu t7, t1, a1 + sub.d t7, t7, t8 + slli.d t8, t7, 1 + slli.d t7, t7, 2 + add.d t7, t7, t8 + add.d t5, t5, t7 + +//7&8 + add.d t1, t1, t6 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ld.bu t7, t1, 0 + sub.d t7, t7, t8 + slli.d t8, t7, 3 + sub.d t7, t8, t7 + add.d t5, t5, t7 + sub.d t2, t2, a1 + ld.bu t8, t2, 0 + ldx.bu t7, t1, a1 + sub.d t7, t7, t8 + slli.d t7, t7, 3 + add.d t5, t5, t7 + add.d t4, t4, t5 + add.d t1, t1, a1 +.endm + +.macro PRED16X16_PLANE_END + ld.bu t7, t1, 0 + ld.bu t8, t2, 16 + add.d t5, t7, t8 + addi.d t5, t5, 1 + slli.d t5, t5, 4 + add.d t7, t3, t4 + slli.d t8, t7, 3 + sub.d t7, t8, t7 + sub.d t5, t5, t7 + + la.local t8, mulh + vld vr3, t8, 0 + slli.d t8, t3, 3 + vreplgr2vr.h vr4, t3 + vreplgr2vr.h vr9, t8 + vmul.h vr5, vr3, vr4 + +.rept 16 + move t7, t5 + add.d t5, t5, t4 + vreplgr2vr.h vr6, t7 + vadd.h vr7, vr6, vr5 + vadd.h vr8, vr9, vr7 + vssrani.bu.h vr8, vr7, 5 + vst vr8, a0, 0 + add.d a0, a0, a1 +.endr +.endm + +.macro PRED16X16_PLANE_END_LASX + ld.bu t7, t1, 0 + ld.bu t8, t2, 16 + add.d t5, t7, t8 + addi.d t5, t5, 1 + slli.d t5, t5, 4 + add.d t7, t3, t4 + slli.d t8, t7, 3 + sub.d t7, t8, t7 + sub.d t5, t5, t7 + + la.local t8, mulh + xvld xr3, t8, 0 + xvreplgr2vr.h xr4, t3 + xvmul.h xr5, xr3, xr4 + +.rept 8 + move t7, t5 + add.d t5, t5, t4 + xvreplgr2vr.h xr6, t7 + xvreplgr2vr.h xr8, t5 + add.d t5, t5, t4 + xvadd.h xr7, xr6, xr5 + xvadd.h xr9, xr8, xr5 + + xvssrani.bu.h xr9, xr7, 5 + vstelm.d vr9, a0, 0, 0 + xvstelm.d xr9, a0, 8, 2 + add.d a0, a0, a1 + vstelm.d vr9, a0, 0, 1 + xvstelm.d xr9, a0, 8, 3 + add.d a0, a0, a1 +.endr +.endm + +/* void ff_h264_pred16x16_plane_h264_8_lsx(uint8_t *src, ptrdiff_t stride) + */ +function ff_h264_pred16x16_plane_h264_8_lsx + PRED16X16_PLANE + + slli.d t7, t3, 2 + add.d t3, t3, t7 + addi.d t3, t3, 32 + srai.d t3, t3, 6 + slli.d t7, t4, 2 + add.d t4, t4, t7 + addi.d t4, t4, 32 + srai.d t4, t4, 6 + + PRED16X16_PLANE_END +endfunc + +/* void ff_h264_pred16x16_plane_rv40_8_lsx(uint8_t *src, ptrdiff_t stride) + */ +function ff_h264_pred16x16_plane_rv40_8_lsx + PRED16X16_PLANE + + srai.d t7, t3, 2 + add.d t3, t3, t7 + srai.d t3, t3, 4 + srai.d t7, t4, 2 + add.d t4, t4, t7 + srai.d t4, t4, 4 + + PRED16X16_PLANE_END +endfunc + +/* void ff_h264_pred16x16_plane_svq3_8_lsx(uint8_t *src, ptrdiff_t stride) + */ +function ff_h264_pred16x16_plane_svq3_8_lsx + PRED16X16_PLANE + + li.d t6, 4 + li.d t7, 5 + li.d t8, 16 + div.d t3, t3, t6 + mul.d t3, t3, t7 + div.d t3, t3, t8 + div.d t4, t4, t6 + mul.d t4, t4, t7 + div.d t4, t4, t8 + move t7, t3 + move t3, t4 + move t4, t7 + + PRED16X16_PLANE_END +endfunc + +/* void ff_h264_pred16x16_plane_h264_8_lasx(uint8_t *src, ptrdiff_t stride) + */ +function ff_h264_pred16x16_plane_h264_8_lasx + PRED16X16_PLANE + + slli.d t7, t3, 2 + add.d t3, t3, t7 + addi.d t3, t3, 32 + srai.d t3, t3, 6 + slli.d t7, t4, 2 + add.d t4, t4, t7 + addi.d t4, t4, 32 + srai.d t4, t4, 6 + + PRED16X16_PLANE_END_LASX +endfunc + +/* void ff_h264_pred16x16_plane_rv40_8_lasx(uint8_t *src, ptrdiff_t stride) + */ +function ff_h264_pred16x16_plane_rv40_8_lasx + PRED16X16_PLANE + + srai.d t7, t3, 2 + add.d t3, t3, t7 + srai.d t3, t3, 4 + srai.d t7, t4, 2 + add.d t4, t4, t7 + srai.d t4, t4, 4 + + PRED16X16_PLANE_END_LASX +endfunc + +/* void ff_h264_pred16x16_plane_svq3_8_lasx(uint8_t *src, ptrdiff_t stride) + */ +function ff_h264_pred16x16_plane_svq3_8_lasx + PRED16X16_PLANE + + li.d t5, 4 + li.d t7, 5 + li.d t8, 16 + div.d t3, t3, t5 + mul.d t3, t3, t7 + div.d t3, t3, t8 + div.d t4, t4, t5 + mul.d t4, t4, t7 + div.d t4, t4, t8 + move t7, t3 + move t3, t4 + move t4, t7 + + PRED16X16_PLANE_END_LASX +endfunc diff --git a/libavcodec/loongarch/h264qpel.S b/libavcodec/loongarch/h264qpel.S new file mode 100644 index 00000000000..3f885b6ce23 --- /dev/null +++ b/libavcodec/loongarch/h264qpel.S @@ -0,0 +1,1686 @@ +/* + * Loongson LSX optimized h264qpel + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Hecai Yuan + * + * 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 "loongson_asm.S" + +.macro VLD_QPEL8_H_SSRANI_LSX in0, in1, in2, in3, in4 + vld vr0, \in4, 0 + vldx vr1, \in4, a2 + QPEL8_H_LSX \in0, \in1 + vssrani.bu.h \in0, \in2, 5 + vssrani.bu.h \in1, \in3, 5 +.endm + +.macro VLDX_QPEL8_H_SSRANI_LSX in0, in1, in2, in3, in4 + vldx vr0, \in4, t1 + vldx vr1, \in4, t2 + QPEL8_H_LSX \in0, \in1 + vssrani.bu.h \in0, \in2, 5 + vssrani.bu.h \in1, \in3, 5 +.endm + +.macro VLD_DOUBLE_QPEL8_H_SSRANI_LSX in0, in1, in2, in3, in4, in5, in6, in7, in8 + vld vr0, \in8, 0 + vldx vr1, \in8, a2 + QPEL8_H_LSX \in0, \in1 + vssrani.bu.h \in0, \in4, 5 + vssrani.bu.h \in1, \in5, 5 + vldx vr0, \in8, t1 + vldx vr1, \in8, t2 + QPEL8_H_LSX \in2, \in3 + vssrani.bu.h \in2, \in6, 5 + vssrani.bu.h \in3, \in7, 5 +.endm + +function ff_put_h264_qpel16_mc00_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + slli.d t2, t0, 1 +.rept 4 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vst vr0, a0, 0 + vstx vr1, a0, a2 + vstx vr2, a0, t0 + vstx vr3, a0, t1 + add.d a0, a0, t2 +.endr +endfunc + +.macro QPEL8_H_LSX out0, out1 + vbsrl.v vr2, vr0, 1 + vbsrl.v vr3, vr1, 1 + vbsrl.v vr4, vr0, 2 + vbsrl.v vr5, vr1, 2 + vbsrl.v vr6, vr0, 3 + vbsrl.v vr7, vr1, 3 + vbsrl.v vr8, vr0, 4 + vbsrl.v vr9, vr1, 4 + vbsrl.v vr10, vr0, 5 + vbsrl.v vr11, vr1, 5 + + vilvl.b vr6, vr4, vr6 + vilvl.b vr7, vr5, vr7 + vilvl.b vr8, vr2, vr8 + vilvl.b vr9, vr3, vr9 + vilvl.b vr10, vr0, vr10 + vilvl.b vr11, vr1, vr11 + vhaddw.hu.bu vr6, vr6, vr6 + vhaddw.hu.bu vr7, vr7, vr7 + vhaddw.hu.bu vr8, vr8, vr8 + vhaddw.hu.bu vr9, vr9, vr9 + vhaddw.hu.bu vr10, vr10, vr10 + vhaddw.hu.bu vr11, vr11, vr11 + vmul.h vr2, vr6, vr20 + vmul.h vr3, vr7, vr20 + vmul.h vr4, vr8, vr21 + vmul.h vr5, vr9, vr21 + vssub.h vr2, vr2, vr4 + vssub.h vr3, vr3, vr5 + vsadd.h vr2, vr2, vr10 + vsadd.h vr3, vr3, vr11 + vsadd.h \out0, vr2, vr22 + vsadd.h \out1, vr3, vr22 +.endm + +.macro VLD_DOUBLE_QPEL8_H_LSX in0, in1, in2, in3, in4 + vld vr0, \in4, 0 + vldx vr1, \in4, a2 + QPEL8_H_LSX \in0, \in1 + vldx vr0, \in4, t1 + vldx vr1, \in4, t2 + QPEL8_H_LSX \in2, \in3 +.endm + +.macro put_h264_qpel16 in0 +function ff_put_h264_qpel16_mc\in0\()_lsx +.ifc \in0, 10 + addi.d t8, a1, 0 +.else + addi.d t8, a1, 1 +.endif + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + slli.d t1, a2, 1 + add.d t2, t1, a2 + addi.d t0, a1, -2 // t0 = src - 2 + addi.d a1, t0, 8 // a1 = t0 + 8 +.rept 4 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr12, vr13, a1 + vld vr10, t8, 0 + vldx vr11, t8, a2 + vavgr.bu vr0, vr2, vr10 + vavgr.bu vr1, vr3, vr11 + vst vr0, a0, 0 + vstx vr1, a0, a2 + VLDX_QPEL8_H_SSRANI_LSX vr4, vr5, vr14, vr15, a1 + vldx vr12, t8, t1 + vldx vr13, t8, t2 + vavgr.bu vr2, vr4, vr12 + vavgr.bu vr3, vr5, vr13 + vstx vr2, a0, t1 + vstx vr3, a0, t2 + alsl.d a0, a2, a0, 2 + alsl.d t8, a2, t8, 2 + alsl.d a1, a2, a1, 2 + alsl.d t0, a2, t0, 2 +.endr +endfunc +.endm + +put_h264_qpel16 10 +put_h264_qpel16 30 + +function ff_put_h264_qpel16_mc20_lsx + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + slli.d t1, a2, 1 + add.d t2, t1, a2 + addi.d t0, a1, -2 // t0 = src - 2 + addi.d a1, t0, 8 // a1 = t0 + 8 +.rept 4 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr12, vr13, a1 + vst vr2, a0, 0 + vstx vr3, a0, a2 + VLDX_QPEL8_H_SSRANI_LSX vr4, vr5, vr14, vr15, a1 + vstx vr4, a0, t1 + vstx vr5, a0, t2 + alsl.d a0, a2, a0, 2 + alsl.d a1, a2, a1, 2 + alsl.d t0, a2, t0, 2 +.endr +endfunc + +.macro QPEL8_V_LSX in0, in1, in2, in3, in4, in5, in6 + vilvl.b vr7, \in3, \in2 + vilvl.b vr8, \in4, \in3 + vilvl.b vr9, \in4, \in1 + vilvl.b vr10, \in5, \in2 + vilvl.b vr11, \in5, \in0 + vilvl.b vr12, \in6, \in1 + vhaddw.hu.bu vr7, vr7, vr7 + vhaddw.hu.bu vr8, vr8, vr8 + vhaddw.hu.bu vr9, vr9, vr9 + vhaddw.hu.bu vr10, vr10, vr10 + vhaddw.hu.bu vr11, vr11, vr11 + vhaddw.hu.bu vr12, vr12, vr12 + vmul.h vr7, vr7, vr20 + vmul.h vr8, vr8, vr20 + vmul.h vr9, vr9, vr21 + vmul.h vr10, vr10, vr21 + vssub.h vr7, vr7, vr9 + vssub.h vr8, vr8, vr10 + vsadd.h vr7, vr7, vr11 + vsadd.h vr8, vr8, vr12 + vsadd.h vr7, vr7, vr22 + vsadd.h vr8, vr8, vr22 + + vilvh.b vr13, \in3, \in2 + vilvh.b vr14, \in4, \in3 + vilvh.b vr15, \in4, \in1 + vilvh.b vr16, \in5, \in2 + vilvh.b vr17, \in5, \in0 + vilvh.b vr18, \in6, \in1 + vhaddw.hu.bu vr13, vr13, vr13 + vhaddw.hu.bu vr14, vr14, vr14 + vhaddw.hu.bu vr15, vr15, vr15 + vhaddw.hu.bu vr16, vr16, vr16 + vhaddw.hu.bu vr17, vr17, vr17 + vhaddw.hu.bu vr18, vr18, vr18 + vmul.h vr13, vr13, vr20 + vmul.h vr14, vr14, vr20 + vmul.h vr15, vr15, vr21 + vmul.h vr16, vr16, vr21 + vssub.h vr13, vr13, vr15 + vssub.h vr14, vr14, vr16 + vsadd.h vr13, vr13, vr17 + vsadd.h vr14, vr14, vr18 + vsadd.h vr13, vr13, vr22 + vsadd.h vr14, vr14, vr22 + vssrani.bu.h vr13, vr7, 5 + vssrani.bu.h vr14, vr8, 5 +.endm + +.macro put_h264_qpel16_mc1 in0 +function ff_put_h264_qpel16_mc\in0\()_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + sub.d t2, a1, t0 // t2 = src - 2 * stride + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + + vld vr0, t2, 0 + vldx vr1, t2, a2 + vldx vr2, t2, t0 + vldx vr3, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr4, t2, 0 + vldx vr5, t2, a2 + vldx vr6, t2, t0 + QPEL8_V_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 +.ifc \in0, 01 + vavgr.bu vr13, vr2, vr13 + vavgr.bu vr14, vr3, vr14 +.else + vavgr.bu vr13, vr3, vr13 + vavgr.bu vr14, vr4, vr14 +.endif + vst vr13, a0, 0 + vstx vr14, a0, a2 + + vldx vr0, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 *stride + vld vr1, t2, 0 + QPEL8_V_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1 +.ifc \in0, 01 + vavgr.bu vr13, vr4, vr13 + vavgr.bu vr14, vr5, vr14 +.else + vavgr.bu vr13, vr5, vr13 + vavgr.bu vr14, vr6, vr14 +.endif + vstx vr13, a0, t0 + vstx vr14, a0, t1 + + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + + vldx vr2, t2, a2 + vldx vr3, t2, t0 + QPEL8_V_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3 +.ifc \in0, 01 + vavgr.bu vr13, vr6, vr13 + vavgr.bu vr14, vr0, vr14 +.else + vavgr.bu vr13, vr0, vr13 + vavgr.bu vr14, vr1, vr14 +.endif + vst vr13, a0, 0 + vstx vr14, a0, a2 + + vldx vr4, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr5, t2, 0 + QPEL8_V_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5 +.ifc \in0, 01 + vavgr.bu vr13, vr1, vr13 + vavgr.bu vr14, vr2, vr14 +.else + vavgr.bu vr13, vr2, vr13 + vavgr.bu vr14, vr3, vr14 +.endif + vstx vr13, a0, t0 + vstx vr14, a0, t1 + + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + + vldx vr6, t2, a2 + vldx vr0, t2, t0 + QPEL8_V_LSX vr1, vr2, vr3, vr4, vr5, vr6, vr0 +.ifc \in0, 01 + vavgr.bu vr13, vr3, vr13 + vavgr.bu vr14, vr4, vr14 +.else + vavgr.bu vr13, vr4, vr13 + vavgr.bu vr14, vr5, vr14 +.endif + vst vr13, a0, 0 + vstx vr14, a0, a2 + + vldx vr1, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr2, t2, 0 + QPEL8_V_LSX vr3, vr4, vr5, vr6, vr0, vr1, vr2 +.ifc \in0, 01 + vavgr.bu vr13, vr5, vr13 + vavgr.bu vr14, vr6, vr14 +.else + vavgr.bu vr13, vr6, vr13 + vavgr.bu vr14, vr0, vr14 +.endif + vstx vr13, a0, t0 + vstx vr14, a0, t1 + + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + + vldx vr3, t2, a2 + vldx vr4, t2, t0 + QPEL8_V_LSX vr5, vr6, vr0, vr1, vr2, vr3, vr4 +.ifc \in0, 01 + vavgr.bu vr13, vr0, vr13 + vavgr.bu vr14, vr1, vr14 +.else + vavgr.bu vr13, vr1, vr13 + vavgr.bu vr14, vr2, vr14 +.endif + vst vr13, a0, 0 + vstx vr14, a0, a2 + + vldx vr5, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr6, t2, 0 + QPEL8_V_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 +.ifc \in0, 01 + vavgr.bu vr13, vr2, vr13 + vavgr.bu vr14, vr3, vr14 +.else + vavgr.bu vr13, vr3, vr13 + vavgr.bu vr14, vr4, vr14 +.endif + vstx vr13, a0, t0 + vstx vr14, a0, t1 +endfunc +.endm + +put_h264_qpel16_mc1 01 +put_h264_qpel16_mc1 03 + +.macro VST_QPEL8_V_LOWPASS_LSX in0, in1, in2, in3, in4, in5, in6, in7, in8 + QPEL8_V_LSX \in0, \in1, \in2, \in3, \in4, \in5, \in6 + vavgr.bu vr13, \in7, vr13 + vavgr.bu vr14, \in8, vr14 + vst vr13, a0, 0 + vstx vr14, a0, a2 +.endm + +.macro VSTX_QPEL8_V_LOWPASS_LSX in0, in1, in2, in3, in4, in5, in6, in7, in8 + QPEL8_V_LSX \in0, \in1, \in2, \in3, \in4, \in5, \in6 + vavgr.bu vr13, \in7, vr13 + vavgr.bu vr14, \in8, vr14 + vstx vr13, a0, t1 + vstx vr14, a0, t2 +.endm + +function ff_put_h264_qpel16_mc11_lsx + addi.d sp, sp, -64 + fst.d f24, sp, 0 + fst.d f25, sp, 8 + fst.d f26, sp, 16 + fst.d f27, sp, 24 + fst.d f28, sp, 32 + fst.d f29, sp, 40 + fst.d f30, sp, 48 + fst.d f31, sp, 56 + slli.d t1, a2, 1 + add.d t2, t1, a2 + slli.d t6, t1, 1 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + sub.d t4, a1, t1 // t4 = src - 2 * stride + addi.d t0, a1, -2 // t0 = src - 2 + addi.d a1, t0, 8 // a1 = t0 + 8 +.rept 2 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + alsl.d t0, a2, t0, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, t0 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, a1 + + vld vr0, t4, 0 // t4 = src - 2 * stride + vldx vr1, t4, a2 + vldx vr2, t4, t1 + vldx vr3, t4, t2 + alsl.d t4, a2, t4, 2 // src + 2 *stride + vld vr4, t4, 0 + vldx vr5, t4, a2 + vldx vr6, t4, t1 + VST_QPEL8_V_LOWPASS_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr23, vr24 + vldx vr0, t4, t2 + alsl.d t4, a2, t4, 2 // src + 6 *stride + vld vr1, t4, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1, vr25, vr26 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + vldx vr2, t4, a2 + vldx vr3, t4, t1 + VST_QPEL8_V_LOWPASS_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3, vr27, vr28 + vldx vr4, t4, t2 + alsl.d t4, a2, t4, 2 // src + 10 *stride + vld vr5, t4, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5, vr29, vr30 + alsl.d t0, a2, t0, 2 + alsl.d a1, a2, a1, 2 // a1 = src + 8 * stride + alsl.d a0, a2, a0, 2 // dst = dst + 8 * stride + sub.d t4, t4, t6 +.endr + fld.d f24, sp, 0 + fld.d f25, sp, 8 + fld.d f26, sp, 16 + fld.d f27, sp, 24 + fld.d f28, sp, 32 + fld.d f29, sp, 40 + fld.d f30, sp, 48 + fld.d f31, sp, 56 + addi.d sp, sp, 64 +endfunc + +function ff_avg_h264_qpel16_mc00_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + slli.d t2, t0, 1 + addi.d t3, a0, 0 +.rept 4 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vld vr8, t3, 0 + vldx vr9, t3, a2 + vldx vr10, t3, t0 + vldx vr11, t3, t1 + add.d t3, t3, t2 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr1, vr9, vr1 + vavgr.bu vr2, vr10, vr2 + vavgr.bu vr3, vr11, vr3 + vst vr0, a0, 0 + vstx vr1, a0, a2 + vstx vr2, a0, t0 + vstx vr3, a0, t1 + add.d a0, a0, t2 +.endr +endfunc + +.macro put_h264_qpel16_mc in0 +function ff_put_h264_qpel16_mc\in0\()_lsx + addi.d sp, sp, -64 + fst.d f24, sp, 0 + fst.d f25, sp, 8 + fst.d f26, sp, 16 + fst.d f27, sp, 24 + fst.d f28, sp, 32 + fst.d f29, sp, 40 + fst.d f30, sp, 48 + fst.d f31, sp, 56 + slli.d t1, a2, 1 + add.d t2, t1, a2 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + addi.d t0, a1, -2 // t0 = src - 2 + +.ifc \in0, 33 + add.d t0, t0, a2 +.endif + add.d t3, a1, zero // t3 = src + sub.d t4, a1, t1 // t4 = src - 2 * stride + addi.d t4, t4, 1 + + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + alsl.d a1, a2, t0, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, a1 + addi.d a1, t0, 8 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, a1 + vld vr0, t4, 0 // t4 = src - 2 * stride + 1 + vldx vr1, t4, a2 + vldx vr2, t4, t1 + vldx vr3, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr4, t4, 0 + vldx vr5, t4, a2 + vldx vr6, t4, t1 + VST_QPEL8_V_LOWPASS_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr23, vr24 + vldx vr0, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr1, t4, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1, vr25, vr26 + add.d t6, t4, zero // t6 = src + 6 * stride + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + vldx vr2, t4, a2 + vldx vr3, t4, t1 + VST_QPEL8_V_LOWPASS_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3, vr27, vr28 + vldx vr4, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr5, t4, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5, vr29, vr30 + alsl.d a1, a2, t0, 3 // a1 = src + 8 * stride + addi.d t5, a1, 8 // a1 = src + 8 * stride + 8 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, a1 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, t5 + alsl.d t5, a2, t5, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, t5 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + + // t6 = src + 6 * stride + 1 + vld vr0, t6, 0 + vldx vr1, t6, a2 + vldx vr2, t6, t1 + vldx vr3, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr4, t6, 0 + vldx vr5, t6, a2 + vldx vr6, t6, t1 + VST_QPEL8_V_LOWPASS_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr23, vr24 + vldx vr0, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr1, t6, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr2, vr3, vr4, vr5 ,vr6, vr0, vr1, vr25, vr26 + alsl.d a0, a2, a0, 2 // dst = dst + 4 *stride + vldx vr2, t6, a2 + vldx vr3, t6, t1 + VST_QPEL8_V_LOWPASS_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3, vr27, vr28 + vldx vr4, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr5, t6, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5, vr29, vr30 + + fld.d f24, sp, 0 + fld.d f25, sp, 8 + fld.d f26, sp, 16 + fld.d f27, sp, 24 + fld.d f28, sp, 32 + fld.d f29, sp, 40 + fld.d f30, sp, 48 + fld.d f31, sp, 56 + addi.d sp, sp, 64 +endfunc +.endm + +put_h264_qpel16_mc 33 +put_h264_qpel16_mc 31 + +function ff_put_h264_qpel16_mc13_lsx + slli.d t1, a2, 1 + add.d t2, t1, a2 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + addi.d sp, sp, -64 + fst.d f24, sp, 0 + fst.d f25, sp, 8 + fst.d f26, sp, 16 + fst.d f27, sp, 24 + fst.d f28, sp, 32 + fst.d f29, sp, 40 + fst.d f30, sp, 48 + fst.d f31, sp, 56 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t0, t0, a2 + add.d t3, a1, zero // t3 = src + sub.d t4, a1, t1 // t4 = src - 2 * stride + + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + alsl.d a1, a2, t0, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, a1 + addi.d a1, t0, 8 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, a1 + vld vr0, t4, 0 // t4 = src - 2 * stride + 1 + vldx vr1, t4, a2 + vldx vr2, t4, t1 + vldx vr3, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr4, t4, 0 + vldx vr5, t4, a2 + vldx vr6, t4, t1 + VST_QPEL8_V_LOWPASS_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr23, vr24 + vldx vr0, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr1, t4, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1, vr25, vr26 + add.d t6, t4, zero + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + vldx vr2, t4, a2 + vldx vr3, t4, t1 + VST_QPEL8_V_LOWPASS_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3, vr27, vr28 + vldx vr4, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr5, t4, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5, vr29, vr30 + alsl.d a1, a2, t0, 3 // a1 = src + 8 * stride + addi.d t5, a1, 8 // a1 = src + 8 * stride + 8 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, a1 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, t5 + alsl.d t5, a2, t5, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, t5 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + + vld vr0, t6, 0 // // t6 = src + 6 * stride + 1 + vldx vr1, t6, a2 + vldx vr2, t6, t1 + vldx vr3, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr4, t6, 0 + vldx vr5, t6, a2 + vldx vr6, t6, t1 + VST_QPEL8_V_LOWPASS_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr23, vr24 + vldx vr0, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr1, t6, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1, vr25, vr26 + alsl.d a0, a2, a0, 2 // dst = dst + 4 *stride + vldx vr2, t6, a2 + vldx vr3, t6, t1 + VST_QPEL8_V_LOWPASS_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3, vr27, vr28 + vldx vr4, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr5, t6, 0 + VSTX_QPEL8_V_LOWPASS_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5, vr29, vr30 + fld.d f24, sp, 0 + fld.d f25, sp, 8 + fld.d f26, sp, 16 + fld.d f27, sp, 24 + fld.d f28, sp, 32 + fld.d f29, sp, 40 + fld.d f30, sp, 48 + fld.d f31, sp, 56 + addi.d sp, sp, 64 +endfunc + +function ff_avg_h264_qpel16_mc10_lsx + addi.d t0, a0, 0 // t0 = dst + addi.d t4, a1, -2 // t1 = src - 2 + addi.d t5, t4, 8 + slli.d t1, a2, 1 + add.d t2, a2, t1 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 +.rept 2 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t4 + alsl.d t4, a2, t4, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, t4 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr12, vr13, t5 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vld vr12, t0, 0 + vldx vr13, t0, a2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vst vr0, a0, 0 + vstx vr1, a0, a2 + VLDX_QPEL8_H_SSRANI_LSX vr2, vr3, vr14, vr15, t5 + vldx vr0, a1, t1 + vldx vr1, a1, t2 + vldx vr12, t0, t1 + vldx vr13, t0, t2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vstx vr0, a0, t1 + vstx vr1, a0, t2 + alsl.d t5, a2, t5, 2 + alsl.d a1, a2, a1, 2 + alsl.d t0, a2, t0, 2 + alsl.d a0, a2, a0, 2 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr16, vr17, t5 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vld vr12, t0, 0 + vldx vr13, t0, a2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vst vr0, a0, 0 + vstx vr1, a0, a2 + VLDX_QPEL8_H_SSRANI_LSX vr2, vr3, vr18, vr19, t5 + vldx vr0, a1, t1 + vldx vr1, a1, t2 + vldx vr12, t0, t1 + vldx vr13, t0, t2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vstx vr0, a0, t1 + vstx vr1, a0, t2 + alsl.d t5, a2, t5, 2 + alsl.d a1, a2, a1, 2 + alsl.d t0, a2, t0, 2 + alsl.d a0, a2, a0, 2 + alsl.d t4, a2, t4, 2 // src + 8 * stride -2 +.endr +endfunc + +function ff_avg_h264_qpel16_mc30_lsx + addi.d t0, a0, 0 // t0 = dst + addi.d t4, a1, -2 // t1 = src - 2 + addi.d t5, t4, 8 + addi.d a1, a1, 1 // a1 = a1 + 1 + slli.d t1, a2, 1 + add.d t2, a2, t1 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 +.rept 2 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t4 + alsl.d t4, a2, t4, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, t4 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr12, vr13, t5 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vld vr12, t0, 0 + vldx vr13, t0, a2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vst vr0, a0, 0 + vstx vr1, a0, a2 + VLDX_QPEL8_H_SSRANI_LSX vr2, vr3, vr14, vr15, t5 + vldx vr0, a1, t1 + vldx vr1, a1, t2 + vldx vr12, t0, t1 + vldx vr13, t0, t2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vstx vr0, a0, t1 + vstx vr1, a0, t2 + alsl.d t5, a2, t5, 2 + alsl.d a1, a2, a1, 2 + alsl.d t0, a2, t0, 2 + alsl.d a0, a2, a0, 2 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr16, vr17, t5 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vld vr12, t0, 0 + vldx vr13, t0, a2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vst vr0, a0, 0 + vstx vr1, a0, a2 + VLDX_QPEL8_H_SSRANI_LSX vr2, vr3, vr18, vr19, t5 + vldx vr0, a1, t1 + vldx vr1, a1, t2 + vldx vr12, t0, t1 + vldx vr13, t0, t2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vavgr.bu vr0, vr0, vr12 + vavgr.bu vr1, vr1, vr13 + vstx vr0, a0, t1 + vstx vr1, a0, t2 + alsl.d t5, a2, t5, 2 + alsl.d a1, a2, a1, 2 + alsl.d t0, a2, t0, 2 + alsl.d a0, a2, a0, 2 + alsl.d t4, a2, t4, 2 // t1 = src + 8 * stride -2 +.endr +endfunc + +function ff_put_h264_qpel16_mc02_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + sub.d t2, a1, t0 // t2 = src - 2 * stride + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + + vld vr0, t2, 0 + vldx vr1, t2, a2 + vldx vr2, t2, t0 + vldx vr3, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr4, t2, 0 + vldx vr5, t2, a2 + vldx vr6, t2, t0 + QPEL8_V_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr0, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 *stride + vld vr1, t2, 0 + QPEL8_V_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1 + vstx vr13, a0, t0 + vstx vr14, a0, t1 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + vldx vr2, t2, a2 + vldx vr3, t2, t0 + QPEL8_V_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr4, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr5, t2, 0 + QPEL8_V_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5 + vstx vr13, a0, t0 + vstx vr14, a0, t1 + + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + + vldx vr6, t2, a2 + vldx vr0, t2, t0 + QPEL8_V_LSX vr1, vr2, vr3, vr4, vr5, vr6, vr0 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr1, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr2, t2, 0 + QPEL8_V_LSX vr3, vr4, vr5, vr6, vr0, vr1, vr2 + vstx vr13, a0, t0 + vstx vr14, a0, t1 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + vldx vr3, t2, a2 + vldx vr4, t2, t0 + QPEL8_V_LSX vr5, vr6, vr0, vr1, vr2, vr3, vr4 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr5, t2, t1 + alsl.d t2, a2, t2, 2 // t2 = t2 + 4 * stride + vld vr6, t2, 0 + QPEL8_V_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 + vstx vr13, a0, t0 + vstx vr14, a0, t1 +endfunc + +.macro avc_luma_hv_qrt_and_aver_dst_16x16_lsx + addi.d sp, sp, -64 + fst.d f24, sp, 0 + fst.d f25, sp, 8 + fst.d f26, sp, 16 + fst.d f27, sp, 24 + fst.d f28, sp, 32 + fst.d f29, sp, 40 + fst.d f30, sp, 48 + fst.d f31, sp, 56 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + alsl.d a1, a2, t0, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, a1 + addi.d a1, t0, 8 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, a1 + vld vr0, t4, 0 // t4 = src - 2 * stride + 1 + vldx vr1, t4, a2 + vldx vr2, t4, t1 + vldx vr3, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr4, t4, 0 + vldx vr5, t4, a2 + vldx vr6, t4, t1 + QPEL8_V_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 + vld vr0, t8, 0 + vldx vr1, t8, a2 + vavgr.bu vr13, vr23, vr13 + vavgr.bu vr14, vr24, vr14 + vavgr.bu vr13, vr13, vr0 + vavgr.bu vr14, vr14, vr1 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr0, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr1, t4, 0 + QPEL8_V_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1 + vldx vr2, t8, t1 + vldx vr3, t8, t2 + vavgr.bu vr13, vr25, vr13 + vavgr.bu vr14, vr26, vr14 + vavgr.bu vr13, vr13, vr2 + vavgr.bu vr14, vr14, vr3 + add.d t6, t4, zero // t6 = src + 6 * stride + vstx vr13, a0, t1 + vstx vr14, a0, t2 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + alsl.d t8, a2, t8, 2 + vldx vr2, t4, a2 + vldx vr3, t4, t1 + QPEL8_V_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3 + vld vr4, t8, 0 + vldx vr5, t8, a2 + vavgr.bu vr13, vr27, vr13 + vavgr.bu vr14, vr28, vr14 + vavgr.bu vr13, vr13, vr4 + vavgr.bu vr14, vr14, vr5 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr4, t4, t2 + alsl.d t4, a2, t4, 2 + vld vr5, t4, 0 + QPEL8_V_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5 + vldx vr6, t8, t1 + vldx vr0, t8, t2 + vavgr.bu vr13, vr29, vr13 + vavgr.bu vr14, vr30, vr14 + vavgr.bu vr13, vr13, vr6 + vavgr.bu vr14, vr14, vr0 + vstx vr13, a0, t1 + vstx vr14, a0, t2 + alsl.d a1, a2, t0, 3 // a1 = src + 8 * stride + addi.d t5, a1, 8 // a1 = src + 8 * stride + 8 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, a1 + alsl.d a1, a2, a1, 2 + VLD_DOUBLE_QPEL8_H_LSX vr16, vr17, vr18, vr19, a1 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr23, vr24, vr25, vr26, vr12, vr13, \ + vr14, vr15, t5 + alsl.d t5, a2, t5, 2 + VLD_DOUBLE_QPEL8_H_SSRANI_LSX vr27, vr28, vr29, vr30, vr16, vr17, \ + vr18, vr19, t5 + alsl.d a0, a2, a0, 2 // dst = dst + 4 * stride + alsl.d t8, a2, t8, 2 + // t6 = src + 6 * stride + 1 + vld vr0, t6, 0 + vldx vr1, t6, a2 + vldx vr2, t6, t1 + vldx vr3, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr4, t6, 0 + vldx vr5, t6, a2 + vldx vr6, t6, t1 + QPEL8_V_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 + vld vr0, t8, 0 + vldx vr1, t8, a2 + vavgr.bu vr13, vr23, vr13 + vavgr.bu vr14, vr24, vr14 + vavgr.bu vr13, vr13, vr0 + vavgr.bu vr14, vr14, vr1 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr0, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr1, t6, 0 + QPEL8_V_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1 + vldx vr2, t8, t1 + vldx vr3, t8, t2 + vavgr.bu vr13, vr25, vr13 + vavgr.bu vr14, vr26, vr14 + vavgr.bu vr13, vr13, vr2 + vavgr.bu vr14, vr14, vr3 + vstx vr13, a0, t1 + vstx vr14, a0, t2 + alsl.d a0, a2, a0, 2 // dst = dst + 4 *stride + alsl.d t8, a2, t8, 2 + vldx vr2, t6, a2 + vldx vr3, t6, t1 + QPEL8_V_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3 + vld vr4, t8, 0 + vldx vr5, t8, a2 + vavgr.bu vr13, vr27, vr13 + vavgr.bu vr14, vr28, vr14 + vavgr.bu vr13, vr13, vr4 + vavgr.bu vr14, vr14, vr5 + vst vr13, a0, 0 + vstx vr14, a0, a2 + vldx vr4, t6, t2 + alsl.d t6, a2, t6, 2 + vld vr5, t6, 0 + QPEL8_V_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5 + vldx vr6, t8, t1 + vldx vr0, t8, t2 + vavgr.bu vr13, vr29, vr13 + vavgr.bu vr14, vr30, vr14 + vavgr.bu vr13, vr13, vr6 + vavgr.bu vr14, vr14, vr0 + vstx vr13, a0, t1 + vstx vr14, a0, t2 + fld.d f24, sp, 0 + fld.d f25, sp, 8 + fld.d f26, sp, 16 + fld.d f27, sp, 24 + fld.d f28, sp, 32 + fld.d f29, sp, 40 + fld.d f30, sp, 48 + fld.d f31, sp, 56 + addi.d sp, sp, 64 +.endm + +function ff_avg_h264_qpel16_mc33_lsx + slli.d t1, a2, 1 + add.d t2, t1, a2 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t0, t0, a2 // t0 = src + stride - 2 + add.d t3, a1, zero // t3 = src + sub.d t4, a1, t1 // t4 = src - 2 * stride + addi.d t4, t4, 1 + addi.d t8, a0, 0 + avc_luma_hv_qrt_and_aver_dst_16x16_lsx +endfunc + +function ff_avg_h264_qpel16_mc11_lsx + slli.d t1, a2, 1 + add.d t2, t1, a2 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t3, a1, zero // t3 = src + sub.d t4, a1, t1 // t4 = src - 2 * stride + addi.d t8, a0, 0 + avc_luma_hv_qrt_and_aver_dst_16x16_lsx +endfunc + +function ff_avg_h264_qpel16_mc31_lsx + slli.d t1, a2, 1 + add.d t2, t1, a2 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t3, a1, zero // t3 = src + sub.d t4, a1, t1 // t4 = src - 2 * stride + addi.d t4, t4, 1 + addi.d t8, a0, 0 + avc_luma_hv_qrt_and_aver_dst_16x16_lsx +endfunc + +function ff_avg_h264_qpel16_mc13_lsx + slli.d t1, a2, 1 + add.d t2, t1, a2 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t0, t0, a2 + add.d t3, a1, zero // t3 = src + sub.d t4, a1, t1 // t4 = src - 2 * stride + addi.d t8, a0, 0 + avc_luma_hv_qrt_and_aver_dst_16x16_lsx +endfunc + +function ff_avg_h264_qpel16_mc20_lsx + slli.d t1, a2, 1 + add.d t2, t1, a2 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + addi.d t0, a1, -2 // t0 = src - 2 + addi.d t5, a0, 0 + addi.d a1, t0, 8 +.rept 4 + VLD_DOUBLE_QPEL8_H_LSX vr12, vr13, vr14, vr15, t0 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr12, vr13, a1 + vld vr0, t5, 0 + vldx vr1, t5, a2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vst vr0, a0, 0 + vstx vr1, a0, a2 + add.d a1, a1, t1 + VLD_QPEL8_H_SSRANI_LSX vr2, vr3, vr14, vr15, a1 + vldx vr0, t5, t1 + vldx vr1, t5, t2 + vavgr.bu vr0, vr0, vr2 + vavgr.bu vr1, vr1, vr3 + vstx vr0, a0, t1 + vstx vr1, a0, t2 + alsl.d t0, a2, t0, 2 + alsl.d t5, a2, t5, 2 + alsl.d a0, a2, a0, 2 + alsl.d a1, a2, a1, 1 +.endr +endfunc + +.macro QPEL8_HV_H_LSX out0, out1 + vbsrl.v vr2, vr0, 1 + vbsrl.v vr3, vr1, 1 + vbsrl.v vr4, vr0, 2 + vbsrl.v vr5, vr1, 2 + vbsrl.v vr6, vr0, 3 + vbsrl.v vr7, vr1, 3 + vbsrl.v vr8, vr0, 4 + vbsrl.v vr9, vr1, 4 + vbsrl.v vr10, vr0, 5 + vbsrl.v vr11, vr1, 5 + vilvl.b vr6, vr4, vr6 + vilvl.b vr7, vr5, vr7 + vilvl.b vr8, vr2, vr8 + vilvl.b vr9, vr3, vr9 + vilvl.b vr10, vr0, vr10 + vilvl.b vr11, vr1, vr11 + vhaddw.hu.bu vr6, vr6, vr6 + vhaddw.hu.bu vr7, vr7, vr7 + vhaddw.hu.bu vr8, vr8, vr8 + vhaddw.hu.bu vr9, vr9, vr9 + vhaddw.hu.bu vr10, vr10, vr10 + vhaddw.hu.bu vr11, vr11, vr11 + vmul.h vr2, vr6, vr20 + vmul.h vr3, vr7, vr20 + vmul.h vr4, vr8, vr21 + vmul.h vr5, vr9, vr21 + vssub.h vr2, vr2, vr4 + vssub.h vr3, vr3, vr5 + vsadd.h \out0, vr2, vr10 + vsadd.h \out1, vr3, vr11 +.endm + +.macro QPEL8_HV_V_LSX in0, in1, in2, in3, in4, in5, in6, out0, out1, out2, out3 + vilvl.h vr0, \in2, \in3 + vilvl.h vr1, \in3, \in4 // tmp0 + vilvl.h vr2, \in1, \in4 + vilvl.h vr3, \in2, \in5 // tmp2 + vilvl.h vr4, \in0, \in5 + vilvl.h vr5, \in1, \in6 // tmp4 + vhaddw.w.h vr0, vr0, vr0 + vhaddw.w.h vr1, vr1, vr1 + vhaddw.w.h vr2, vr2, vr2 + vhaddw.w.h vr3, vr3, vr3 + vhaddw.w.h vr4, vr4, vr4 + vhaddw.w.h vr5, vr5, vr5 + vmul.w vr0, vr0, vr22 + vmul.w vr1, vr1, vr22 + vmul.w vr2, vr2, vr23 + vmul.w vr3, vr3, vr23 + vssub.w vr0, vr0, vr2 + vssub.w vr1, vr1, vr3 + vsadd.w vr0, vr0, vr4 + vsadd.w vr1, vr1, vr5 + vsadd.w \out0, vr0, vr24 + vsadd.w \out1, vr1, vr24 + vilvh.h vr0, \in2, \in3 + vilvh.h vr1, \in3, \in4 // tmp0 + vilvh.h vr2, \in1, \in4 + vilvh.h vr3, \in2, \in5 // tmp2 + vilvh.h vr4, \in0, \in5 + vilvh.h vr5, \in1, \in6 // tmp4 + vhaddw.w.h vr0, vr0, vr0 + vhaddw.w.h vr1, vr1, vr1 + vhaddw.w.h vr2, vr2, vr2 + vhaddw.w.h vr3, vr3, vr3 + vhaddw.w.h vr4, vr4, vr4 + vhaddw.w.h vr5, vr5, vr5 + vmul.w vr0, vr0, vr22 + vmul.w vr1, vr1, vr22 + vmul.w vr2, vr2, vr23 + vmul.w vr3, vr3, vr23 + vssub.w vr0, vr0, vr2 + vssub.w vr1, vr1, vr3 + vsadd.w vr0, vr0, vr4 + vsadd.w vr1, vr1, vr5 + vsadd.w \out2, vr0, vr24 + vsadd.w \out3, vr1, vr24 + vssrani.hu.w \out2, \out0, 10 + vssrani.hu.w \out3, \out1, 10 + vssrani.bu.h \out3, \out2, 0 +.endm + +.macro h264_qpel8_hv_lowpass_core_lsx in0, in1, type + vld vr0, \in0, 0 + vldx vr1, \in0, a3 + QPEL8_HV_H_LSX vr12, vr13 // a b$ + vldx vr0, \in0, t1 + vldx vr1, \in0, t2 + QPEL8_HV_H_LSX vr14, vr15 // c d$ + + alsl.d \in0, a3, \in0, 2 + + vld vr0, \in0, 0 + vldx vr1, \in0, a3 + QPEL8_HV_H_LSX vr16, vr17 // e f$ + vldx vr0, \in0, t1 + vldx vr1, \in0, t2 + QPEL8_HV_H_LSX vr18, vr19 // g h$ + QPEL8_HV_V_LSX vr12, vr13, vr14, vr15, vr16, vr17, vr18, vr6, vr7, vr0, vr1 +.ifc \type, avg + fld.d f2, t3, 0 + fldx.d f3, t3, a2 + vilvl.d vr2, vr3, vr2 + vavgr.bu vr1, vr2, vr1 +.endif + vstelm.d vr1, \in1, 0, 0 + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 1 + + alsl.d \in0, a3, \in0, 2 + + // tmp8 + vld vr0, \in0, 0 + vldx vr1, \in0, a3 + QPEL8_HV_H_LSX vr12, vr13 + QPEL8_HV_V_LSX vr14, vr15, vr16, vr17, vr18, vr19, vr12, vr6, vr7, vr0, vr1 +.ifc \type, avg + fldx.d f2, t3, t5 + fldx.d f3, t3, t6 + vilvl.d vr2, vr3, vr2 + vavgr.bu vr1, vr2, vr1 +.endif + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 0 + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 1 + + // tmp10 + vldx vr0, \in0, t1 + vldx vr1, \in0, t2 + QPEL8_HV_H_LSX vr14, vr15 + QPEL8_HV_V_LSX vr16, vr17, vr18, vr19, vr12, vr13, vr14, vr6, vr7, vr0, vr1 +.ifc \type, avg + alsl.d t3, a2, t3, 2 + fld.d f2, t3, 0 + fldx.d f3, t3, a2 + vilvl.d vr2, vr3, vr2 + vavgr.bu vr1, vr2, vr1 +.endif + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 0 + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 1 + + // tmp12 + alsl.d \in0, a3, \in0, 2 + + vld vr0, \in0, 0 + vldx vr1, \in0, a3 + QPEL8_HV_H_LSX vr16, vr17 + QPEL8_HV_V_LSX vr18, vr19, vr12, vr13, vr14, vr15, vr16, vr6, vr7, vr0, vr1 +.ifc \type, avg + fldx.d f2, t3, t5 + fldx.d f3, t3, t6 + vilvl.d vr2, vr3, vr2 + vavgr.bu vr1, vr2, vr1 +.endif + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 0 + add.d \in1, \in1, a2 + vstelm.d vr1, \in1, 0, 1 +.endm + +function put_h264_qpel8_hv_lowpass_lsx + slli.d t1, a3, 1 + add.d t2, t1, a3 + addi.d sp, sp, -8 + fst.d f24, sp, 0 + addi.d t0, a1, -2 // t0 = src - 2 + sub.d t0, t0, t1 // t0 = t0 - 2 * stride + vldi vr20, 0x414 // h_20 + vldi vr21, 0x405 // h_5 + vldi vr22, 0x814 // w_20 + vldi vr23, 0x805 // w_5 + addi.d t4, zero, 512 + vreplgr2vr.w vr24, t4 // w_512 + h264_qpel8_hv_lowpass_core_lsx t0, a0, put + fld.d f24, sp, 0 + addi.d sp, sp, 8 +endfunc + +function put_h264_qpel8_h_lowpass_lsx + slli.d t1, a3, 1 + add.d t2, t1, a3 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t3, a1, zero // t3 = src +.rept 2 + vld vr0, t0, 0 + vldx vr1, t0, a3 + QPEL8_H_LSX vr12, vr13 + vssrani.bu.h vr13, vr12, 5 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr13, a0, 0, 1 + add.d a0, a0, a2 + vldx vr0, t0, t1 + vldx vr1, t0, t2 + QPEL8_H_LSX vr12, vr13 + vssrani.bu.h vr13, vr12, 5 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr13, a0, 0, 1 + add.d a0, a0, a2 + alsl.d t0, a3, t0, 2 +.endr +endfunc + +function put_pixels16_l2_8_lsx + slli.d t0, a4, 1 + add.d t1, t0, a4 + slli.d t2, t0, 1 + slli.d t3, a3, 1 + add.d t4, t3, a3 + slli.d t5, t3, 1 +.rept 4 + vld vr0, a1, 0 + vldx vr1, a1, a4 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vld vr8, a2, 0x00 + vld vr9, a2, 0x10 + vld vr10, a2, 0x20 + vld vr11, a2, 0x30 + addi.d a2, a2, 0x40 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr1, vr9, vr1 + vavgr.bu vr2, vr10, vr2 + vavgr.bu vr3, vr11, vr3 + vst vr0, a0, 0 + vstx vr1, a0, a3 + vstx vr2, a0, t3 + vstx vr3, a0, t4 + add.d a0, a0, t5 +.endr +endfunc + +.macro QPEL8_V1_LSX in0, in1, in2, in3, in4, in5, in6 + vilvl.b vr7, \in3, \in2 + vilvl.b vr8, \in4, \in3 + vilvl.b vr9, \in4, \in1 + vilvl.b vr10, \in5, \in2 + vilvl.b vr11, \in5, \in0 + vilvl.b vr12, \in6, \in1 + vhaddw.hu.bu vr7, vr7, vr7 + vhaddw.hu.bu vr8, vr8, vr8 + vhaddw.hu.bu vr9, vr9, vr9 + vhaddw.hu.bu vr10, vr10, vr10 + vhaddw.hu.bu vr11, vr11, vr11 + vhaddw.hu.bu vr12, vr12, vr12 + vmul.h vr7, vr7, vr20 + vmul.h vr8, vr8, vr20 + vmul.h vr9, vr9, vr21 + vmul.h vr10, vr10, vr21 + vssub.h vr7, vr7, vr9 + vssub.h vr8, vr8, vr10 + vsadd.h vr7, vr7, vr11 + vsadd.h vr8, vr8, vr12 + vsadd.h vr7, vr7, vr22 + vsadd.h vr8, vr8, vr22 + vssrani.bu.h vr8, vr7, 5 +.endm + +.macro h264_qpel8_v_lowpass_lsx type +function \type\()_h264_qpel8_v_lowpass_lsx + slli.d t0, a3, 1 + add.d t1, t0, a3 + sub.d t2, a1, t0 // t2 = src - 2 * stride +.ifc \type, avg + addi.d t3, a0, 0 + slli.d t4, a2, 1 + add.d t5, t4, a2 +.endif + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + + fld.d f0, t2, 0 + fldx.d f1, t2, a3 + fldx.d f2, t2, t0 + fldx.d f3, t2, t1 + alsl.d t2, a3, t2, 2 // t2 = t2 + 4 * stride + fld.d f4, t2, 0 + fldx.d f5, t2, a3 + fldx.d f6, t2, t0 + QPEL8_V1_LSX vr0, vr1, vr2, vr3, vr4, vr5, vr6 +.ifc \type, avg + fld.d f0, t3, 0 + fldx.d f1, t3, a2 + vilvl.d vr0, vr1, vr0 + vavgr.bu vr8, vr8, vr0 +.endif + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr8, a0, 0, 1 + add.d a0, a0, a2 + + fldx.d f0, t2, t1 + alsl.d t2, a3, t2, 2 // t2 = t2 + 4 *stride + fld.d f1, t2, 0 + QPEL8_V1_LSX vr2, vr3, vr4, vr5, vr6, vr0, vr1 +.ifc \type, avg + fldx.d f2, t3, t4 + fldx.d f3, t3, t5 + vilvl.d vr2, vr3, vr2 + vavgr.bu vr8, vr8, vr2 +.endif + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr8, a0, 0, 1 + add.d a0, a0, a2 + + alsl.d t3, a2, t3, 2 + + fldx.d f2, t2, a3 + fldx.d f3, t2, t0 + QPEL8_V1_LSX vr4, vr5, vr6, vr0, vr1, vr2, vr3 +.ifc \type, avg + fld.d f4, t3, 0 + fldx.d f5, t3, a2 + vilvl.d vr4, vr5, vr4 + vavgr.bu vr8, vr8, vr4 +.endif + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr8, a0, 0, 1 + add.d a0, a0, a2 + + fldx.d f4, t2, t1 + alsl.d t2, a3, t2, 2 // t2 = t2 + 4 * stride + fld.d f5, t2, 0 + QPEL8_V1_LSX vr6, vr0, vr1, vr2, vr3, vr4, vr5 +.ifc \type, avg + fldx.d f6, t3, t4 + fldx.d f0, t3, t5 + vilvl.d vr6, vr0, vr6 + vavgr.bu vr8, vr8, vr6 +.endif + vstelm.d vr8, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr8, a0, 0, 1 +endfunc +.endm + +h264_qpel8_v_lowpass_lsx put +h264_qpel8_v_lowpass_lsx avg + +function avg_pixels16_l2_8_lsx + slli.d t0, a4, 1 + add.d t1, t0, a4 + slli.d t2, t0, 1 + slli.d t3, a3, 1 + add.d t4, t3, a3 + slli.d t5, t3, 1 + addi.d t6, a0, 0 +.rept 4 + vld vr0, a1, 0 + vldx vr1, a1, a4 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vld vr8, a2, 0x00 + vld vr9, a2, 0x10 + vld vr10, a2, 0x20 + vld vr11, a2, 0x30 + addi.d a2, a2, 0x40 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr1, vr9, vr1 + vavgr.bu vr2, vr10, vr2 + vavgr.bu vr3, vr11, vr3 + vld vr8, t6, 0 + vldx vr9, t6, a3 + vldx vr10, t6, t3 + vldx vr11, t6, t4 + add.d t6, t6, t5 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr1, vr9, vr1 + vavgr.bu vr2, vr10, vr2 + vavgr.bu vr3, vr11, vr3 + vst vr0, a0, 0 + vstx vr1, a0, a3 + vstx vr2, a0, t3 + vstx vr3, a0, t4 + add.d a0, a0, t5 +.endr +endfunc + +function avg_h264_qpel8_hv_lowpass_lsx + slli.d t1, a3, 1 + add.d t2, t1, a3 + slli.d t5, a2, 1 + add.d t6, a2, t5 + addi.d sp, sp, -8 + fst.d f24, sp, 0 + vldi vr20, 0x414 // h_20 + vldi vr21, 0x405 // h_5 + vldi vr22, 0x814 // w_20 + vldi vr23, 0x805 // w_5 + addi.d t4, zero, 512 + vreplgr2vr.w vr24, t4 // w_512 + addi.d t0, a1, -2 // t0 = src - 2 + sub.d t0, t0, t1 // t0 = t0 - 2 * stride + addi.d t3, a0, 0 // t3 = dst + h264_qpel8_hv_lowpass_core_lsx t0, a0, avg + fld.d f24, sp, 0 + addi.d sp, sp, 8 +endfunc + +function put_pixels8_l2_8_lsx + slli.d t0, a4, 1 + add.d t1, t0, a4 + slli.d t2, t0, 1 +.rept 2 + vld vr0, a1, 0 + vldx vr1, a1, a4 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vilvl.d vr0, vr1, vr0 + vilvl.d vr2, vr3, vr2 + vld vr8, a2, 0x00 + vld vr9, a2, 0x08 + vld vr10, a2, 0x10 + vld vr11, a2, 0x18 + vilvl.d vr8, vr9, vr8 + vilvl.d vr10, vr11, vr10 + addi.d a2, a2, 32 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr2, vr10, vr2 + vstelm.d vr0, a0, 0, 0 + add.d a0, a0, a3 + vstelm.d vr0, a0, 0, 1 + add.d a0, a0, a3 + vstelm.d vr2, a0, 0, 0 + add.d a0, a0, a3 + vstelm.d vr2, a0, 0, 1 + add.d a0, a0, a3 +.endr +endfunc + +function ff_put_h264_qpel8_mc00_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + slli.d t2, t0, 1 + ld.d t3, a1, 0x0 + ldx.d t4, a1, a2 + ldx.d t5, a1, t0 + ldx.d t6, a1, t1 + st.d t3, a0, 0x0 + stx.d t4, a0, a2 + stx.d t5, a0, t0 + stx.d t6, a0, t1 + add.d a1, a1, t2 + add.d a0, a0, t2 + ld.d t3, a1, 0x0 + ldx.d t4, a1, a2 + ldx.d t5, a1, t0 + ldx.d t6, a1, t1 + st.d t3, a0, 0x0 + stx.d t4, a0, a2 + stx.d t5, a0, t0 + stx.d t6, a0, t1 +endfunc + +function ff_avg_h264_qpel8_mc00_lsx + slli.d t0, a2, 1 + add.d t1, t0, a2 + slli.d t2, t0, 1 + addi.d t3, a0, 0 +.rept 2 + vld vr0, a1, 0 + vldx vr1, a1, a2 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vilvl.d vr0, vr1, vr0 + vilvl.d vr2, vr3, vr2 + vld vr8, t3, 0 + vldx vr9, t3, a2 + vldx vr10, t3, t0 + vldx vr11, t3, t1 + add.d t3, t3, t2 + vilvl.d vr8, vr9, vr8 + vilvl.d vr10, vr11, vr10 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr2, vr10, vr2 + vstelm.d vr0, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr0, a0, 0, 1 + add.d a0, a0, a2 + vstelm.d vr2, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr2, a0, 0, 1 + add.d a0, a0, a2 +.endr +endfunc + +function avg_pixels8_l2_8_lsx + slli.d t0, a4, 1 + add.d t1, t0, a4 + slli.d t2, t0, 1 + addi.d t3, a0, 0 + slli.d t4, a3, 1 + add.d t5, t4, a3 + slli.d t6, t4, 1 +.rept 2 + vld vr0, a1, 0 + vldx vr1, a1, a4 + vldx vr2, a1, t0 + vldx vr3, a1, t1 + add.d a1, a1, t2 + vilvl.d vr0, vr1, vr0 + vilvl.d vr2, vr3, vr2 + vld vr8, a2, 0x00 + vld vr9, a2, 0x08 + vld vr10, a2, 0x10 + vld vr11, a2, 0x18 + addi.d a2, a2, 0x20 + vilvl.d vr8, vr9, vr8 + vilvl.d vr10, vr11, vr10 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr2, vr10, vr2 + vld vr8, t3, 0 + vldx vr9, t3, a3 + vldx vr10, t3, t4 + vldx vr11, t3, t5 + add.d t3, t3, t6 + vilvl.d vr8, vr9, vr8 + vilvl.d vr10, vr11, vr10 + vavgr.bu vr0, vr8, vr0 + vavgr.bu vr2, vr10, vr2 + vstelm.d vr0, a0, 0, 0 + add.d a0, a0, a3 + vstelm.d vr0, a0, 0, 1 + add.d a0, a0, a3 + vstelm.d vr2, a0, 0, 0 + add.d a0, a0, a3 + vstelm.d vr2, a0, 0, 1 + add.d a0, a0, a3 +.endr +endfunc + +function avg_h264_qpel8_h_lowpass_lsx + slli.d t1, a3, 1 + add.d t2, t1, a3 + slli.d t5, a2, 1 + add.d t6, t5, a2 + vldi vr20, 0x414 + vldi vr21, 0x405 + vldi vr22, 0x410 + addi.d t0, a1, -2 // t0 = src - 2 + add.d t3, a1, zero // t3 = src + addi.d t4, a0, 0 // t4 = dst +.rept 4 + vld vr0, t0, 0 + vldx vr1, t0, a3 + QPEL8_H_LSX vr12, vr13 + vssrani.bu.h vr13, vr12, 5 + fld.d f0, t4, 0 + fldx.d f1, t4, a2 + vilvl.d vr0, vr1, vr0 + vavgr.bu vr13, vr13, vr0 + vstelm.d vr13, a0, 0, 0 + add.d a0, a0, a2 + vstelm.d vr13, a0, 0, 1 + add.d a0, a0, a2 + add.d t0, t0, t1 + add.d t4, t4, t1 +.endr +endfunc diff --git a/libavcodec/loongarch/h264qpel_init_loongarch.c b/libavcodec/loongarch/h264qpel_init_loongarch.c index 969c9c376c6..9d3a5cb1643 100644 --- a/libavcodec/loongarch/h264qpel_init_loongarch.c +++ b/libavcodec/loongarch/h264qpel_init_loongarch.c @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "h264qpel_lasx.h" +#include "h264qpel_loongarch.h" #include "libavutil/attributes.h" #include "libavutil/loongarch/cpu.h" #include "libavcodec/h264qpel.h" @@ -27,6 +27,77 @@ av_cold void ff_h264qpel_init_loongarch(H264QpelContext *c, int bit_depth) { int cpu_flags = av_get_cpu_flags(); + + if (have_lsx(cpu_flags)) { + if (8 == bit_depth) { + c->put_h264_qpel_pixels_tab[0][0] = ff_put_h264_qpel16_mc00_lsx; + c->put_h264_qpel_pixels_tab[0][1] = ff_put_h264_qpel16_mc10_lsx; + c->put_h264_qpel_pixels_tab[0][2] = ff_put_h264_qpel16_mc20_lsx; + c->put_h264_qpel_pixels_tab[0][3] = ff_put_h264_qpel16_mc30_lsx; + c->put_h264_qpel_pixels_tab[0][4] = ff_put_h264_qpel16_mc01_lsx; + c->put_h264_qpel_pixels_tab[0][5] = ff_put_h264_qpel16_mc11_lsx; + c->put_h264_qpel_pixels_tab[0][6] = ff_put_h264_qpel16_mc21_lsx; + c->put_h264_qpel_pixels_tab[0][7] = ff_put_h264_qpel16_mc31_lsx; + c->put_h264_qpel_pixels_tab[0][8] = ff_put_h264_qpel16_mc02_lsx; + c->put_h264_qpel_pixels_tab[0][9] = ff_put_h264_qpel16_mc12_lsx; + c->put_h264_qpel_pixels_tab[0][10] = ff_put_h264_qpel16_mc22_lsx; + c->put_h264_qpel_pixels_tab[0][11] = ff_put_h264_qpel16_mc32_lsx; + c->put_h264_qpel_pixels_tab[0][12] = ff_put_h264_qpel16_mc03_lsx; + c->put_h264_qpel_pixels_tab[0][13] = ff_put_h264_qpel16_mc13_lsx; + c->put_h264_qpel_pixels_tab[0][14] = ff_put_h264_qpel16_mc23_lsx; + c->put_h264_qpel_pixels_tab[0][15] = ff_put_h264_qpel16_mc33_lsx; + + c->avg_h264_qpel_pixels_tab[0][0] = ff_avg_h264_qpel16_mc00_lsx; + c->avg_h264_qpel_pixels_tab[0][1] = ff_avg_h264_qpel16_mc10_lsx; + c->avg_h264_qpel_pixels_tab[0][2] = ff_avg_h264_qpel16_mc20_lsx; + c->avg_h264_qpel_pixels_tab[0][3] = ff_avg_h264_qpel16_mc30_lsx; + c->avg_h264_qpel_pixels_tab[0][4] = ff_avg_h264_qpel16_mc01_lsx; + c->avg_h264_qpel_pixels_tab[0][5] = ff_avg_h264_qpel16_mc11_lsx; + c->avg_h264_qpel_pixels_tab[0][6] = ff_avg_h264_qpel16_mc21_lsx; + c->avg_h264_qpel_pixels_tab[0][7] = ff_avg_h264_qpel16_mc31_lsx; + c->avg_h264_qpel_pixels_tab[0][8] = ff_avg_h264_qpel16_mc02_lsx; + c->avg_h264_qpel_pixels_tab[0][9] = ff_avg_h264_qpel16_mc12_lsx; + c->avg_h264_qpel_pixels_tab[0][10] = ff_avg_h264_qpel16_mc22_lsx; + c->avg_h264_qpel_pixels_tab[0][11] = ff_avg_h264_qpel16_mc32_lsx; + c->avg_h264_qpel_pixels_tab[0][12] = ff_avg_h264_qpel16_mc03_lsx; + c->avg_h264_qpel_pixels_tab[0][13] = ff_avg_h264_qpel16_mc13_lsx; + c->avg_h264_qpel_pixels_tab[0][14] = ff_avg_h264_qpel16_mc23_lsx; + c->avg_h264_qpel_pixels_tab[0][15] = ff_avg_h264_qpel16_mc33_lsx; + + c->put_h264_qpel_pixels_tab[1][0] = ff_put_h264_qpel8_mc00_lsx; + c->put_h264_qpel_pixels_tab[1][1] = ff_put_h264_qpel8_mc10_lsx; + c->put_h264_qpel_pixels_tab[1][2] = ff_put_h264_qpel8_mc20_lsx; + c->put_h264_qpel_pixels_tab[1][3] = ff_put_h264_qpel8_mc30_lsx; + c->put_h264_qpel_pixels_tab[1][4] = ff_put_h264_qpel8_mc01_lsx; + c->put_h264_qpel_pixels_tab[1][5] = ff_put_h264_qpel8_mc11_lsx; + c->put_h264_qpel_pixels_tab[1][6] = ff_put_h264_qpel8_mc21_lsx; + c->put_h264_qpel_pixels_tab[1][7] = ff_put_h264_qpel8_mc31_lsx; + c->put_h264_qpel_pixels_tab[1][8] = ff_put_h264_qpel8_mc02_lsx; + c->put_h264_qpel_pixels_tab[1][9] = ff_put_h264_qpel8_mc12_lsx; + c->put_h264_qpel_pixels_tab[1][10] = ff_put_h264_qpel8_mc22_lsx; + c->put_h264_qpel_pixels_tab[1][11] = ff_put_h264_qpel8_mc32_lsx; + c->put_h264_qpel_pixels_tab[1][12] = ff_put_h264_qpel8_mc03_lsx; + c->put_h264_qpel_pixels_tab[1][13] = ff_put_h264_qpel8_mc13_lsx; + c->put_h264_qpel_pixels_tab[1][14] = ff_put_h264_qpel8_mc23_lsx; + c->put_h264_qpel_pixels_tab[1][15] = ff_put_h264_qpel8_mc33_lsx; + + c->avg_h264_qpel_pixels_tab[1][0] = ff_avg_h264_qpel8_mc00_lsx; + c->avg_h264_qpel_pixels_tab[1][1] = ff_avg_h264_qpel8_mc10_lsx; + c->avg_h264_qpel_pixels_tab[1][2] = ff_avg_h264_qpel8_mc20_lsx; + c->avg_h264_qpel_pixels_tab[1][3] = ff_avg_h264_qpel8_mc30_lsx; + c->avg_h264_qpel_pixels_tab[1][5] = ff_avg_h264_qpel8_mc11_lsx; + c->avg_h264_qpel_pixels_tab[1][6] = ff_avg_h264_qpel8_mc21_lsx; + c->avg_h264_qpel_pixels_tab[1][7] = ff_avg_h264_qpel8_mc31_lsx; + c->avg_h264_qpel_pixels_tab[1][8] = ff_avg_h264_qpel8_mc02_lsx; + c->avg_h264_qpel_pixels_tab[1][9] = ff_avg_h264_qpel8_mc12_lsx; + c->avg_h264_qpel_pixels_tab[1][10] = ff_avg_h264_qpel8_mc22_lsx; + c->avg_h264_qpel_pixels_tab[1][11] = ff_avg_h264_qpel8_mc32_lsx; + c->avg_h264_qpel_pixels_tab[1][13] = ff_avg_h264_qpel8_mc13_lsx; + c->avg_h264_qpel_pixels_tab[1][14] = ff_avg_h264_qpel8_mc23_lsx; + c->avg_h264_qpel_pixels_tab[1][15] = ff_avg_h264_qpel8_mc33_lsx; + } + } +#if HAVE_LASX if (have_lasx(cpu_flags)) { if (8 == bit_depth) { c->put_h264_qpel_pixels_tab[0][0] = ff_put_h264_qpel16_mc00_lasx; @@ -95,4 +166,5 @@ av_cold void ff_h264qpel_init_loongarch(H264QpelContext *c, int bit_depth) c->avg_h264_qpel_pixels_tab[1][15] = ff_avg_h264_qpel8_mc33_lasx; } } +#endif } diff --git a/libavcodec/loongarch/h264qpel_lasx.c b/libavcodec/loongarch/h264qpel_lasx.c index 1c142e510eb..519bb03fe64 100644 --- a/libavcodec/loongarch/h264qpel_lasx.c +++ b/libavcodec/loongarch/h264qpel_lasx.c @@ -21,7 +21,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "h264qpel_lasx.h" +#include "h264qpel_loongarch.h" #include "libavutil/loongarch/loongson_intrinsics.h" #include "libavutil/attributes.h" @@ -418,157 +418,6 @@ avg_pixels8_8_lsx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride) ); } -/* avg_pixels8_8_lsx : dst = avg(src, dst) - * put_pixels8_l2_8_lsx: dst = avg(src, half) , half stride is 8. - * avg_pixels8_l2_8_lsx: dst = avg(avg(src, half), dst) , half stride is 8.*/ -static av_always_inline void -put_pixels8_l2_8_lsx(uint8_t *dst, const uint8_t *src, const uint8_t *half, - ptrdiff_t dstStride, ptrdiff_t srcStride) -{ - ptrdiff_t stride_2, stride_3, stride_4; - __asm__ volatile ( - /* h0~h7 */ - "slli.d %[stride_2], %[srcStride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[srcStride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "vld $vr0, %[src], 0 \n\t" - "vldx $vr1, %[src], %[srcStride] \n\t" - "vldx $vr2, %[src], %[stride_2] \n\t" - "vldx $vr3, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "vld $vr4, %[src], 0 \n\t" - "vldx $vr5, %[src], %[srcStride] \n\t" - "vldx $vr6, %[src], %[stride_2] \n\t" - "vldx $vr7, %[src], %[stride_3] \n\t" - - "vld $vr8, %[half], 0x00 \n\t" - "vld $vr9, %[half], 0x08 \n\t" - "vld $vr10, %[half], 0x10 \n\t" - "vld $vr11, %[half], 0x18 \n\t" - "vld $vr12, %[half], 0x20 \n\t" - "vld $vr13, %[half], 0x28 \n\t" - "vld $vr14, %[half], 0x30 \n\t" - "vld $vr15, %[half], 0x38 \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vstelm.d $vr0, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr1, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr2, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr3, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr4, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr5, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr6, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr7, %[dst], 0, 0 \n\t" - : [dst]"+&r"(dst), [half]"+&r"(half), [src]"+&r"(src), - [stride_2]"=&r"(stride_2), [stride_3]"=&r"(stride_3), - [stride_4]"=&r"(stride_4) - : [srcStride]"r"(srcStride), [dstStride]"r"(dstStride) - : "memory" - ); -} - -/* avg_pixels8_8_lsx : dst = avg(src, dst) - * put_pixels8_l2_8_lsx: dst = avg(src, half) , half stride is 8. - * avg_pixels8_l2_8_lsx: dst = avg(avg(src, half), dst) , half stride is 8.*/ -static av_always_inline void -avg_pixels8_l2_8_lsx(uint8_t *dst, const uint8_t *src, const uint8_t *half, - ptrdiff_t dstStride, ptrdiff_t srcStride) -{ - uint8_t *tmp = dst; - ptrdiff_t stride_2, stride_3, stride_4; - __asm__ volatile ( - /* h0~h7 */ - "slli.d %[stride_2], %[srcStride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[srcStride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "vld $vr0, %[src], 0 \n\t" - "vldx $vr1, %[src], %[srcStride] \n\t" - "vldx $vr2, %[src], %[stride_2] \n\t" - "vldx $vr3, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "vld $vr4, %[src], 0 \n\t" - "vldx $vr5, %[src], %[srcStride] \n\t" - "vldx $vr6, %[src], %[stride_2] \n\t" - "vldx $vr7, %[src], %[stride_3] \n\t" - - "vld $vr8, %[half], 0x00 \n\t" - "vld $vr9, %[half], 0x08 \n\t" - "vld $vr10, %[half], 0x10 \n\t" - "vld $vr11, %[half], 0x18 \n\t" - "vld $vr12, %[half], 0x20 \n\t" - "vld $vr13, %[half], 0x28 \n\t" - "vld $vr14, %[half], 0x30 \n\t" - "vld $vr15, %[half], 0x38 \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "slli.d %[stride_2], %[dstStride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[dstStride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "vld $vr8, %[tmp], 0 \n\t" - "vldx $vr9, %[tmp], %[dstStride] \n\t" - "vldx $vr10, %[tmp], %[stride_2] \n\t" - "vldx $vr11, %[tmp], %[stride_3] \n\t" - "add.d %[tmp], %[tmp], %[stride_4] \n\t" - "vld $vr12, %[tmp], 0 \n\t" - "vldx $vr13, %[tmp], %[dstStride] \n\t" - "vldx $vr14, %[tmp], %[stride_2] \n\t" - "vldx $vr15, %[tmp], %[stride_3] \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vstelm.d $vr0, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr1, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr2, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr3, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr4, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr5, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr6, %[dst], 0, 0 \n\t" - "add.d %[dst], %[dst], %[dstStride] \n\t" - "vstelm.d $vr7, %[dst], 0, 0 \n\t" - : [dst]"+&r"(dst), [tmp]"+&r"(tmp), [half]"+&r"(half), - [src]"+&r"(src), [stride_2]"=&r"(stride_2), - [stride_3]"=&r"(stride_3), [stride_4]"=&r"(stride_4) - : [dstStride]"r"(dstStride), [srcStride]"r"(srcStride) - : "memory" - ); -} - /* put_pixels16_8_lsx: dst = src */ static av_always_inline void put_pixels16_8_lsx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride) @@ -729,254 +578,6 @@ avg_pixels16_8_lsx(uint8_t *dst, const uint8_t *src, ptrdiff_t stride) ); } -/* avg_pixels16_8_lsx : dst = avg(src, dst) - * put_pixels16_l2_8_lsx: dst = avg(src, half) , half stride is 8. - * avg_pixels16_l2_8_lsx: dst = avg(avg(src, half), dst) , half stride is 8.*/ -static av_always_inline void -put_pixels16_l2_8_lsx(uint8_t *dst, const uint8_t *src, uint8_t *half, - ptrdiff_t dstStride, ptrdiff_t srcStride) -{ - ptrdiff_t stride_2, stride_3, stride_4; - ptrdiff_t dstride_2, dstride_3, dstride_4; - __asm__ volatile ( - "slli.d %[stride_2], %[srcStride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[srcStride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "slli.d %[dstride_2], %[dstStride], 1 \n\t" - "add.d %[dstride_3], %[dstride_2], %[dstStride] \n\t" - "slli.d %[dstride_4], %[dstride_2], 1 \n\t" - /* h0~h7 */ - "vld $vr0, %[src], 0 \n\t" - "vldx $vr1, %[src], %[srcStride] \n\t" - "vldx $vr2, %[src], %[stride_2] \n\t" - "vldx $vr3, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "vld $vr4, %[src], 0 \n\t" - "vldx $vr5, %[src], %[srcStride] \n\t" - "vldx $vr6, %[src], %[stride_2] \n\t" - "vldx $vr7, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - - "vld $vr8, %[half], 0x00 \n\t" - "vld $vr9, %[half], 0x10 \n\t" - "vld $vr10, %[half], 0x20 \n\t" - "vld $vr11, %[half], 0x30 \n\t" - "vld $vr12, %[half], 0x40 \n\t" - "vld $vr13, %[half], 0x50 \n\t" - "vld $vr14, %[half], 0x60 \n\t" - "vld $vr15, %[half], 0x70 \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vst $vr0, %[dst], 0 \n\t" - "vstx $vr1, %[dst], %[dstStride] \n\t" - "vstx $vr2, %[dst], %[dstride_2] \n\t" - "vstx $vr3, %[dst], %[dstride_3] \n\t" - "add.d %[dst], %[dst], %[dstride_4] \n\t" - "vst $vr4, %[dst], 0 \n\t" - "vstx $vr5, %[dst], %[dstStride] \n\t" - "vstx $vr6, %[dst], %[dstride_2] \n\t" - "vstx $vr7, %[dst], %[dstride_3] \n\t" - "add.d %[dst], %[dst], %[dstride_4] \n\t" - - /* h8~h15 */ - "vld $vr0, %[src], 0 \n\t" - "vldx $vr1, %[src], %[srcStride] \n\t" - "vldx $vr2, %[src], %[stride_2] \n\t" - "vldx $vr3, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "vld $vr4, %[src], 0 \n\t" - "vldx $vr5, %[src], %[srcStride] \n\t" - "vldx $vr6, %[src], %[stride_2] \n\t" - "vldx $vr7, %[src], %[stride_3] \n\t" - - "vld $vr8, %[half], 0x80 \n\t" - "vld $vr9, %[half], 0x90 \n\t" - "vld $vr10, %[half], 0xa0 \n\t" - "vld $vr11, %[half], 0xb0 \n\t" - "vld $vr12, %[half], 0xc0 \n\t" - "vld $vr13, %[half], 0xd0 \n\t" - "vld $vr14, %[half], 0xe0 \n\t" - "vld $vr15, %[half], 0xf0 \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vst $vr0, %[dst], 0 \n\t" - "vstx $vr1, %[dst], %[dstStride] \n\t" - "vstx $vr2, %[dst], %[dstride_2] \n\t" - "vstx $vr3, %[dst], %[dstride_3] \n\t" - "add.d %[dst], %[dst], %[dstride_4] \n\t" - "vst $vr4, %[dst], 0 \n\t" - "vstx $vr5, %[dst], %[dstStride] \n\t" - "vstx $vr6, %[dst], %[dstride_2] \n\t" - "vstx $vr7, %[dst], %[dstride_3] \n\t" - : [dst]"+&r"(dst), [half]"+&r"(half), [src]"+&r"(src), - [stride_2]"=&r"(stride_2), [stride_3]"=&r"(stride_3), - [stride_4]"=&r"(stride_4), [dstride_2]"=&r"(dstride_2), - [dstride_3]"=&r"(dstride_3), [dstride_4]"=&r"(dstride_4) - : [dstStride]"r"(dstStride), [srcStride]"r"(srcStride) - : "memory" - ); -} - -/* avg_pixels16_8_lsx : dst = avg(src, dst) - * put_pixels16_l2_8_lsx: dst = avg(src, half) , half stride is 8. - * avg_pixels16_l2_8_lsx: dst = avg(avg(src, half), dst) , half stride is 8.*/ -static av_always_inline void -avg_pixels16_l2_8_lsx(uint8_t *dst, const uint8_t *src, uint8_t *half, - ptrdiff_t dstStride, ptrdiff_t srcStride) -{ - uint8_t *tmp = dst; - ptrdiff_t stride_2, stride_3, stride_4; - ptrdiff_t dstride_2, dstride_3, dstride_4; - __asm__ volatile ( - "slli.d %[stride_2], %[srcStride], 1 \n\t" - "add.d %[stride_3], %[stride_2], %[srcStride] \n\t" - "slli.d %[stride_4], %[stride_2], 1 \n\t" - "slli.d %[dstride_2], %[dstStride], 1 \n\t" - "add.d %[dstride_3], %[dstride_2], %[dstStride] \n\t" - "slli.d %[dstride_4], %[dstride_2], 1 \n\t" - /* h0~h7 */ - "vld $vr0, %[src], 0 \n\t" - "vldx $vr1, %[src], %[srcStride] \n\t" - "vldx $vr2, %[src], %[stride_2] \n\t" - "vldx $vr3, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "vld $vr4, %[src], 0 \n\t" - "vldx $vr5, %[src], %[srcStride] \n\t" - "vldx $vr6, %[src], %[stride_2] \n\t" - "vldx $vr7, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - - "vld $vr8, %[half], 0x00 \n\t" - "vld $vr9, %[half], 0x10 \n\t" - "vld $vr10, %[half], 0x20 \n\t" - "vld $vr11, %[half], 0x30 \n\t" - "vld $vr12, %[half], 0x40 \n\t" - "vld $vr13, %[half], 0x50 \n\t" - "vld $vr14, %[half], 0x60 \n\t" - "vld $vr15, %[half], 0x70 \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vld $vr8, %[tmp], 0 \n\t" - "vldx $vr9, %[tmp], %[dstStride] \n\t" - "vldx $vr10, %[tmp], %[dstride_2] \n\t" - "vldx $vr11, %[tmp], %[dstride_3] \n\t" - "add.d %[tmp], %[tmp], %[dstride_4] \n\t" - "vld $vr12, %[tmp], 0 \n\t" - "vldx $vr13, %[tmp], %[dstStride] \n\t" - "vldx $vr14, %[tmp], %[dstride_2] \n\t" - "vldx $vr15, %[tmp], %[dstride_3] \n\t" - "add.d %[tmp], %[tmp], %[dstride_4] \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vst $vr0, %[dst], 0 \n\t" - "vstx $vr1, %[dst], %[dstStride] \n\t" - "vstx $vr2, %[dst], %[dstride_2] \n\t" - "vstx $vr3, %[dst], %[dstride_3] \n\t" - "add.d %[dst], %[dst], %[dstride_4] \n\t" - "vst $vr4, %[dst], 0 \n\t" - "vstx $vr5, %[dst], %[dstStride] \n\t" - "vstx $vr6, %[dst], %[dstride_2] \n\t" - "vstx $vr7, %[dst], %[dstride_3] \n\t" - "add.d %[dst], %[dst], %[dstride_4] \n\t" - - /* h8~h15 */ - "vld $vr0, %[src], 0 \n\t" - "vldx $vr1, %[src], %[srcStride] \n\t" - "vldx $vr2, %[src], %[stride_2] \n\t" - "vldx $vr3, %[src], %[stride_3] \n\t" - "add.d %[src], %[src], %[stride_4] \n\t" - "vld $vr4, %[src], 0 \n\t" - "vldx $vr5, %[src], %[srcStride] \n\t" - "vldx $vr6, %[src], %[stride_2] \n\t" - "vldx $vr7, %[src], %[stride_3] \n\t" - - "vld $vr8, %[half], 0x80 \n\t" - "vld $vr9, %[half], 0x90 \n\t" - "vld $vr10, %[half], 0xa0 \n\t" - "vld $vr11, %[half], 0xb0 \n\t" - "vld $vr12, %[half], 0xc0 \n\t" - "vld $vr13, %[half], 0xd0 \n\t" - "vld $vr14, %[half], 0xe0 \n\t" - "vld $vr15, %[half], 0xf0 \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vld $vr8, %[tmp], 0 \n\t" - "vldx $vr9, %[tmp], %[dstStride] \n\t" - "vldx $vr10, %[tmp], %[dstride_2] \n\t" - "vldx $vr11, %[tmp], %[dstride_3] \n\t" - "add.d %[tmp], %[tmp], %[dstride_4] \n\t" - "vld $vr12, %[tmp], 0 \n\t" - "vldx $vr13, %[tmp], %[dstStride] \n\t" - "vldx $vr14, %[tmp], %[dstride_2] \n\t" - "vldx $vr15, %[tmp], %[dstride_3] \n\t" - - "vavgr.bu $vr0, $vr8, $vr0 \n\t" - "vavgr.bu $vr1, $vr9, $vr1 \n\t" - "vavgr.bu $vr2, $vr10, $vr2 \n\t" - "vavgr.bu $vr3, $vr11, $vr3 \n\t" - "vavgr.bu $vr4, $vr12, $vr4 \n\t" - "vavgr.bu $vr5, $vr13, $vr5 \n\t" - "vavgr.bu $vr6, $vr14, $vr6 \n\t" - "vavgr.bu $vr7, $vr15, $vr7 \n\t" - - "vst $vr0, %[dst], 0 \n\t" - "vstx $vr1, %[dst], %[dstStride] \n\t" - "vstx $vr2, %[dst], %[dstride_2] \n\t" - "vstx $vr3, %[dst], %[dstride_3] \n\t" - "add.d %[dst], %[dst], %[dstride_4] \n\t" - "vst $vr4, %[dst], 0 \n\t" - "vstx $vr5, %[dst], %[dstStride] \n\t" - "vstx $vr6, %[dst], %[dstride_2] \n\t" - "vstx $vr7, %[dst], %[dstride_3] \n\t" - : [dst]"+&r"(dst), [tmp]"+&r"(tmp), [half]"+&r"(half), [src]"+&r"(src), - [stride_2]"=&r"(stride_2), [stride_3]"=&r"(stride_3), - [stride_4]"=&r"(stride_4), [dstride_2]"=&r"(dstride_2), - [dstride_3]"=&r"(dstride_3), [dstride_4]"=&r"(dstride_4) - : [dstStride]"r"(dstStride), [srcStride]"r"(srcStride) - : "memory" - ); -} - #define QPEL8_H_LOWPASS(out_v) \ src00 = __lasx_xvld(src, - 2); \ src += srcStride; \ diff --git a/libavcodec/loongarch/h264qpel_lasx.h b/libavcodec/loongarch/h264qpel_lasx.h deleted file mode 100644 index 32b6b509170..00000000000 --- a/libavcodec/loongarch/h264qpel_lasx.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2020 Loongson Technology Corporation Limited - * Contributed by Shiyou Yin - * - * 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 - */ - -#ifndef AVCODEC_LOONGARCH_H264QPEL_LASX_H -#define AVCODEC_LOONGARCH_H264QPEL_LASX_H - -#include -#include -#include "libavcodec/h264.h" - -void ff_h264_h_lpf_luma_inter_lasx(uint8_t *src, int stride, - int alpha, int beta, int8_t *tc0); -void ff_h264_v_lpf_luma_inter_lasx(uint8_t *src, int stride, - int alpha, int beta, int8_t *tc0); -void ff_put_h264_qpel16_mc00_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc10_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc20_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc30_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc01_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc11_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc21_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc31_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc02_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc12_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc32_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc22_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc03_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc13_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc23_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_put_h264_qpel16_mc33_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc00_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc10_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc20_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc30_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc01_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc11_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc21_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc31_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc02_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc12_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc22_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc32_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc03_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc13_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc23_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel16_mc33_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); - -void ff_put_h264_qpel8_mc00_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc10_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc20_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc30_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc01_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc11_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc21_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc31_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc02_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc12_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc22_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc32_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc03_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc13_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc23_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_put_h264_qpel8_mc33_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t stride); -void ff_avg_h264_qpel8_mc00_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc10_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc20_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc30_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc11_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc21_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc31_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc02_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc12_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc22_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc32_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc13_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc23_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -void ff_avg_h264_qpel8_mc33_lasx(uint8_t *dst, const uint8_t *src, - ptrdiff_t dst_stride); -#endif // #ifndef AVCODEC_LOONGARCH_H264QPEL_LASX_H diff --git a/libavcodec/loongarch/h264qpel_loongarch.h b/libavcodec/loongarch/h264qpel_loongarch.h new file mode 100644 index 00000000000..68232730dac --- /dev/null +++ b/libavcodec/loongarch/h264qpel_loongarch.h @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Shiyou Yin + * + * 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 + */ + +#ifndef AVCODEC_LOONGARCH_H264QPEL_LOONGARCH_H +#define AVCODEC_LOONGARCH_H264QPEL_LOONGARCH_H + +#include +#include +#include "libavcodec/h264.h" +#include "config.h" + +void put_h264_qpel8_hv_lowpass_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dstStride, ptrdiff_t srcStride); +void put_h264_qpel8_h_lowpass_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dstStride, ptrdiff_t srcStride); +void put_h264_qpel8_v_lowpass_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dstStride, ptrdiff_t srcStride); +void put_pixels16_l2_8_lsx(uint8_t *dst, const uint8_t *src, uint8_t *half, + ptrdiff_t dstStride, ptrdiff_t srcStride); +void put_pixels8_l2_8_lsx(uint8_t *dst, const uint8_t *src, const uint8_t *half, + ptrdiff_t dstStride, ptrdiff_t srcStride); + +void avg_h264_qpel8_h_lowpass_lsx(uint8_t *dst, const uint8_t *src, int dstStride, + int srcStride); +void avg_h264_qpel8_v_lowpass_lsx(uint8_t *dst, uint8_t *src, int dstStride, + int srcStride); +void avg_pixels16_l2_8_lsx(uint8_t *dst, const uint8_t *src, uint8_t *half, + ptrdiff_t dstStride, ptrdiff_t srcStride); +void avg_h264_qpel8_hv_lowpass_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dstStride, ptrdiff_t srcStride); +void avg_pixels8_l2_8_lsx(uint8_t *dst, const uint8_t *src, const uint8_t *half, + ptrdiff_t dstStride, ptrdiff_t srcStride); + +void ff_put_h264_qpel16_mc00_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc10_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc20_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc30_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc01_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc11_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc13_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc31_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc33_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc03_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel16_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel16_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel16_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); + +void ff_avg_h264_qpel16_mc00_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc10_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc30_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc33_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc11_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc31_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc13_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc20_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc03_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc01_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel16_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); + +void ff_put_h264_qpel8_mc03_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc00_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc01_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc30_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc10_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc33_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc13_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc31_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc11_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc20_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); + +void ff_avg_h264_qpel8_mc00_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc10_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc20_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc30_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc11_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc31_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc13_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc33_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); + +#if HAVE_LASX +void ff_h264_h_lpf_luma_inter_lasx(uint8_t *src, int stride, + int alpha, int beta, int8_t *tc0); +void ff_h264_v_lpf_luma_inter_lasx(uint8_t *src, int stride, + int alpha, int beta, int8_t *tc0); +void ff_put_h264_qpel16_mc00_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc10_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc20_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc30_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc01_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc11_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc21_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc31_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc02_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc12_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc32_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc22_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc03_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc13_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc23_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_put_h264_qpel16_mc33_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc00_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc10_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc20_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc30_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc01_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc11_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc21_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc31_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc02_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc12_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc22_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc32_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc03_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc13_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc23_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel16_mc33_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); + +void ff_put_h264_qpel8_mc00_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc10_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc20_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc30_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc01_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc11_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc21_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc31_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc02_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc12_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc22_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc32_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc03_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc13_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc23_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_put_h264_qpel8_mc33_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride); +void ff_avg_h264_qpel8_mc00_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc10_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc20_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc30_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc11_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc21_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc31_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc02_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc12_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc22_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc32_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc13_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc23_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +void ff_avg_h264_qpel8_mc33_lasx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dst_stride); +#endif + +#endif // #ifndef AVCODEC_LOONGARCH_H264QPEL_LOONGARCH_H diff --git a/libavcodec/loongarch/h264qpel_lsx.c b/libavcodec/loongarch/h264qpel_lsx.c new file mode 100644 index 00000000000..12b3bae6d1c --- /dev/null +++ b/libavcodec/loongarch/h264qpel_lsx.c @@ -0,0 +1,487 @@ +/* + * Loongson LSX optimized h264qpel + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Hecai Yuan + * + * 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 "h264qpel_loongarch.h" +#include "libavutil/loongarch/loongson_intrinsics.h" +#include "libavutil/attributes.h" + +static void put_h264_qpel16_hv_lowpass_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dstStride, ptrdiff_t srcStride) +{ + put_h264_qpel8_hv_lowpass_lsx(dst, src, dstStride, srcStride); + put_h264_qpel8_hv_lowpass_lsx(dst + 8, src + 8, dstStride, srcStride); + src += srcStride << 3; + dst += dstStride << 3; + put_h264_qpel8_hv_lowpass_lsx(dst, src, dstStride, srcStride); + put_h264_qpel8_hv_lowpass_lsx(dst + 8, src + 8, dstStride, srcStride); +} + +void ff_put_h264_qpel16_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + put_h264_qpel16_hv_lowpass_lsx(dst, src, stride, stride); +} + +static void put_h264_qpel16_h_lowpass_lsx(uint8_t *dst, const uint8_t *src, + int dstStride, int srcStride) +{ + put_h264_qpel8_h_lowpass_lsx(dst, src, dstStride, srcStride); + put_h264_qpel8_h_lowpass_lsx(dst+8, src+8, dstStride, srcStride); + src += srcStride << 3; + dst += dstStride << 3; + put_h264_qpel8_h_lowpass_lsx(dst, src, dstStride, srcStride); + put_h264_qpel8_h_lowpass_lsx(dst+8, src+8, dstStride, srcStride); +} + +static void put_h264_qpel16_v_lowpass_lsx(uint8_t *dst, const uint8_t *src, + int dstStride, int srcStride) +{ + put_h264_qpel8_v_lowpass_lsx(dst, (uint8_t*)src, dstStride, srcStride); + put_h264_qpel8_v_lowpass_lsx(dst+8, (uint8_t*)src+8, dstStride, srcStride); + src += 8*srcStride; + dst += 8*dstStride; + put_h264_qpel8_v_lowpass_lsx(dst, (uint8_t*)src, dstStride, srcStride); + put_h264_qpel8_v_lowpass_lsx(dst+8, (uint8_t*)src+8, dstStride, srcStride); +} + +void ff_put_h264_qpel16_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 256; + + put_h264_qpel16_h_lowpass_lsx(halfH, src, 16, stride); + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + put_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +void ff_put_h264_qpel16_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 256; + + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + put_h264_qpel16_v_lowpass_lsx(halfH, src, 16, stride); + put_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +void ff_put_h264_qpel16_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 256; + + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + put_h264_qpel16_v_lowpass_lsx(halfH, src + 1, 16, stride); + put_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +void ff_put_h264_qpel16_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 256; + + put_h264_qpel16_h_lowpass_lsx(halfH, src + stride, 16, stride); + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + put_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +static void avg_h264_qpel16_v_lowpass_lsx(uint8_t *dst, const uint8_t *src, + int dstStride, int srcStride) +{ + avg_h264_qpel8_v_lowpass_lsx(dst, (uint8_t*)src, dstStride, srcStride); + avg_h264_qpel8_v_lowpass_lsx(dst+8, (uint8_t*)src+8, dstStride, srcStride); + src += 8*srcStride; + dst += 8*dstStride; + avg_h264_qpel8_v_lowpass_lsx(dst, (uint8_t*)src, dstStride, srcStride); + avg_h264_qpel8_v_lowpass_lsx(dst+8, (uint8_t*)src+8, dstStride, srcStride); +} + +void ff_avg_h264_qpel16_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + avg_h264_qpel16_v_lowpass_lsx(dst, src, stride, stride); +} + +void ff_avg_h264_qpel16_mc03_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[256]; + + put_h264_qpel16_v_lowpass_lsx(half, src, 16, stride); + avg_pixels16_l2_8_lsx(dst, src + stride, half, stride, stride); +} + +void ff_avg_h264_qpel16_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 256; + + put_h264_qpel16_h_lowpass_lsx(halfH, src + stride, 16, stride); + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + avg_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +void ff_avg_h264_qpel16_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 256; + + put_h264_qpel16_h_lowpass_lsx(halfH, src, 16, stride); + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + avg_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +void ff_avg_h264_qpel16_mc01_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[256]; + + put_h264_qpel16_v_lowpass_lsx(half, src, 16, stride); + avg_pixels16_l2_8_lsx(dst, src, half, stride, stride); +} + +void ff_avg_h264_qpel16_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 256; + + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + put_h264_qpel16_v_lowpass_lsx(halfH, src + 1, 16, stride); + avg_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +void ff_avg_h264_qpel16_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[512]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 256; + + put_h264_qpel16_hv_lowpass_lsx(halfHV, src, 16, stride); + put_h264_qpel16_v_lowpass_lsx(halfH, src, 16, stride); + avg_pixels16_l2_8_lsx(dst, halfH, halfHV, stride, 16); +} + +static void avg_h264_qpel16_hv_lowpass_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t dstStride, ptrdiff_t srcStride) +{ + avg_h264_qpel8_hv_lowpass_lsx(dst, src, dstStride, srcStride); + avg_h264_qpel8_hv_lowpass_lsx(dst + 8, src + 8, dstStride, srcStride); + src += srcStride << 3; + dst += dstStride << 3; + avg_h264_qpel8_hv_lowpass_lsx(dst, src, dstStride, srcStride); + avg_h264_qpel8_hv_lowpass_lsx(dst + 8, src + 8, dstStride, srcStride); +} + +void ff_avg_h264_qpel16_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + avg_h264_qpel16_hv_lowpass_lsx(dst, src, stride, stride); +} + +void ff_put_h264_qpel8_mc03_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[64]; + + put_h264_qpel8_v_lowpass_lsx(half, (uint8_t*)src, 8, stride); + put_pixels8_l2_8_lsx(dst, src + stride, half, stride, stride); +} + +void ff_put_h264_qpel8_mc01_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[64]; + + put_h264_qpel8_v_lowpass_lsx(half, (uint8_t*)src, 8, stride); + put_pixels8_l2_8_lsx(dst, src, half, stride, stride); +} + +void ff_put_h264_qpel8_mc30_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[64]; + + put_h264_qpel8_h_lowpass_lsx(half, src, 8, stride); + put_pixels8_l2_8_lsx(dst, src+1, half, stride, stride); +} + +void ff_put_h264_qpel8_mc10_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[64]; + + put_h264_qpel8_h_lowpass_lsx(half, src, 8, stride); + put_pixels8_l2_8_lsx(dst, src, half, stride, stride); +} + +void ff_put_h264_qpel8_mc33_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src + stride, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src + 1, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_put_h264_qpel8_mc13_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src + stride, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_put_h264_qpel8_mc31_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src + 1, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_put_h264_qpel8_mc11_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_put_h264_qpel8_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 64; + + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfH, (uint8_t*)src + 1, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_put_h264_qpel8_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 64; + + put_h264_qpel8_h_lowpass_lsx(halfH, src, 8, stride); + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_put_h264_qpel8_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 64; + + put_h264_qpel8_h_lowpass_lsx(halfH, src + stride, 8, stride); + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_put_h264_qpel8_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 64; + + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfH, (uint8_t*)src, 8, stride); + put_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_put_h264_qpel8_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + put_h264_qpel8_v_lowpass_lsx(dst, (uint8_t*)src, stride, stride); +} + +void ff_put_h264_qpel8_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + put_h264_qpel8_hv_lowpass_lsx(dst, src, stride, stride); +} + +void ff_put_h264_qpel8_mc20_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + put_h264_qpel8_h_lowpass_lsx(dst, src, stride, stride); +} + +void ff_avg_h264_qpel8_mc10_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[64]; + + put_h264_qpel8_h_lowpass_lsx(half, src, 8, stride); + avg_pixels8_l2_8_lsx(dst, src, half, stride, stride); +} + +void ff_avg_h264_qpel8_mc20_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + avg_h264_qpel8_h_lowpass_lsx(dst, src, stride, stride); +} + +void ff_avg_h264_qpel8_mc30_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t half[64]; + + put_h264_qpel8_h_lowpass_lsx(half, src, 8, stride); + avg_pixels8_l2_8_lsx(dst, src+1, half, stride, stride); +} + +void ff_avg_h264_qpel8_mc11_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_avg_h264_qpel8_mc21_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 64; + + put_h264_qpel8_h_lowpass_lsx(halfH, src, 8, stride); + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_avg_h264_qpel8_mc31_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src + 1, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_avg_h264_qpel8_mc02_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + avg_h264_qpel8_v_lowpass_lsx(dst, (uint8_t*)src, stride, stride); +} + +void ff_avg_h264_qpel8_mc12_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 64; + + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfH, (uint8_t*)src, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_avg_h264_qpel8_mc22_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + avg_h264_qpel8_hv_lowpass_lsx(dst, src, stride, stride); +} + +void ff_avg_h264_qpel8_mc32_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfHV = temp; + uint8_t *const halfH = temp + 64; + + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfH, (uint8_t*)src + 1, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_avg_h264_qpel8_mc13_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src + stride, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} + +void ff_avg_h264_qpel8_mc23_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t temp[128]; + uint8_t *const halfH = temp; + uint8_t *const halfHV = temp + 64; + + put_h264_qpel8_h_lowpass_lsx(halfH, src + stride, 8, stride); + put_h264_qpel8_hv_lowpass_lsx(halfHV, src, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfHV, stride, 8); +} + +void ff_avg_h264_qpel8_mc33_lsx(uint8_t *dst, const uint8_t *src, + ptrdiff_t stride) +{ + uint8_t halfH[64]; + uint8_t halfV[64]; + + put_h264_qpel8_h_lowpass_lsx(halfH, src + stride, 8, stride); + put_h264_qpel8_v_lowpass_lsx(halfV, (uint8_t*)src + 1, 8, stride); + avg_pixels8_l2_8_lsx(dst, halfH, halfV, stride, 8); +} diff --git a/libavcodec/loongarch/hevc_add_res.S b/libavcodec/loongarch/hevc_add_res.S new file mode 100644 index 00000000000..dd2d820af8e --- /dev/null +++ b/libavcodec/loongarch/hevc_add_res.S @@ -0,0 +1,162 @@ +/* + * Loongson LSX optimized add_residual functions for HEVC decoding + * + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by jinbo + * + * 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 "loongson_asm.S" + +/* + * void ff_hevc_add_residual4x4_lsx(uint8_t *dst, const int16_t *res, ptrdiff_t stride) + */ +.macro ADD_RES_LSX_4x4_8 + vldrepl.w vr0, a0, 0 + add.d t0, a0, a2 + vldrepl.w vr1, t0, 0 + vld vr2, a1, 0 + + vilvl.w vr1, vr1, vr0 + vsllwil.hu.bu vr1, vr1, 0 + vadd.h vr1, vr1, vr2 + vssrani.bu.h vr1, vr1, 0 + + vstelm.w vr1, a0, 0, 0 + vstelm.w vr1, t0, 0, 1 +.endm + +function ff_hevc_add_residual4x4_8_lsx + ADD_RES_LSX_4x4_8 + alsl.d a0, a2, a0, 1 + addi.d a1, a1, 16 + ADD_RES_LSX_4x4_8 +endfunc + +/* + * void ff_hevc_add_residual8x8_8_lsx(uint8_t *dst, const int16_t *res, ptrdiff_t stride) + */ +.macro ADD_RES_LSX_8x8_8 + vldrepl.d vr0, a0, 0 + add.d t0, a0, a2 + vldrepl.d vr1, t0, 0 + add.d t1, t0, a2 + vldrepl.d vr2, t1, 0 + add.d t2, t1, a2 + vldrepl.d vr3, t2, 0 + + vld vr4, a1, 0 + addi.d t3, zero, 16 + vldx vr5, a1, t3 + addi.d t4, a1, 32 + vld vr6, t4, 0 + vldx vr7, t4, t3 + + vsllwil.hu.bu vr0, vr0, 0 + vsllwil.hu.bu vr1, vr1, 0 + vsllwil.hu.bu vr2, vr2, 0 + vsllwil.hu.bu vr3, vr3, 0 + vadd.h vr0, vr0, vr4 + vadd.h vr1, vr1, vr5 + vadd.h vr2, vr2, vr6 + vadd.h vr3, vr3, vr7 + vssrani.bu.h vr1, vr0, 0 + vssrani.bu.h vr3, vr2, 0 + + vstelm.d vr1, a0, 0, 0 + vstelm.d vr1, t0, 0, 1 + vstelm.d vr3, t1, 0, 0 + vstelm.d vr3, t2, 0, 1 +.endm + +function ff_hevc_add_residual8x8_8_lsx + ADD_RES_LSX_8x8_8 + alsl.d a0, a2, a0, 2 + addi.d a1, a1, 64 + ADD_RES_LSX_8x8_8 +endfunc + +/* + * void ff_hevc_add_residual16x16_8_lsx(uint8_t *dst, const int16_t *res, ptrdiff_t stride) + */ +function ff_hevc_add_residual16x16_8_lsx +.rept 8 + vld vr0, a0, 0 + vldx vr2, a0, a2 + + vld vr4, a1, 0 + addi.d t0, zero, 16 + vldx vr5, a1, t0 + addi.d t1, a1, 32 + vld vr6, t1, 0 + vldx vr7, t1, t0 + + vexth.hu.bu vr1, vr0 + vsllwil.hu.bu vr0, vr0, 0 + vexth.hu.bu vr3, vr2 + vsllwil.hu.bu vr2, vr2, 0 + vadd.h vr0, vr0, vr4 + vadd.h vr1, vr1, vr5 + vadd.h vr2, vr2, vr6 + vadd.h vr3, vr3, vr7 + + vssrani.bu.h vr1, vr0, 0 + vssrani.bu.h vr3, vr2, 0 + + vst vr1, a0, 0 + vstx vr3, a0, a2 + + alsl.d a0, a2, a0, 1 + addi.d a1, a1, 64 +.endr +endfunc + +/* + * void ff_hevc_add_residual32x32_8_lsx(uint8_t *dst, const int16_t *res, ptrdiff_t stride) + */ +function ff_hevc_add_residual32x32_8_lsx +.rept 32 + vld vr0, a0, 0 + addi.w t0, zero, 16 + vldx vr2, a0, t0 + + vld vr4, a1, 0 + vldx vr5, a1, t0 + addi.d t1, a1, 32 + vld vr6, t1, 0 + vldx vr7, t1, t0 + + vexth.hu.bu vr1, vr0 + vsllwil.hu.bu vr0, vr0, 0 + vexth.hu.bu vr3, vr2 + vsllwil.hu.bu vr2, vr2, 0 + vadd.h vr0, vr0, vr4 + vadd.h vr1, vr1, vr5 + vadd.h vr2, vr2, vr6 + vadd.h vr3, vr3, vr7 + + vssrani.bu.h vr1, vr0, 0 + vssrani.bu.h vr3, vr2, 0 + + vst vr1, a0, 0 + vstx vr3, a0, t0 + + add.d a0, a0, a2 + addi.d a1, a1, 64 +.endr +endfunc diff --git a/libavcodec/loongarch/hevc_idct.S b/libavcodec/loongarch/hevc_idct.S new file mode 100644 index 00000000000..83c46e17d7e --- /dev/null +++ b/libavcodec/loongarch/hevc_idct.S @@ -0,0 +1,857 @@ +/* + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by Hecai Yuan + * + * 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 "loongson_asm.S" + +.macro fr_store + addi.d sp, sp, -64 + fst.d f24, sp, 0 + fst.d f25, sp, 8 + fst.d f26, sp, 16 + fst.d f27, sp, 24 + fst.d f28, sp, 32 + fst.d f29, sp, 40 + fst.d f30, sp, 48 + fst.d f31, sp, 56 +.endm + +.macro fr_recover + fld.d f24, sp, 0 + fld.d f25, sp, 8 + fld.d f26, sp, 16 + fld.d f27, sp, 24 + fld.d f28, sp, 32 + fld.d f29, sp, 40 + fld.d f30, sp, 48 + fld.d f31, sp, 56 + addi.d sp, sp, 64 +.endm + +.extern gt32x32_cnst1 + +.extern gt32x32_cnst2 + +.extern gt8x8_cnst + +.extern gt32x32_cnst0 + +.macro idct_16x32_step1_lasx + xvldrepl.w xr20, t1, 0 + xvldrepl.w xr21, t1, 4 + xvldrepl.w xr22, t1, 8 + xvldrepl.w xr23, t1, 12 + + xvmulwev.w.h xr16, xr8, xr20 + xvmaddwod.w.h xr16, xr8, xr20 + xvmulwev.w.h xr17, xr9, xr20 + xvmaddwod.w.h xr17, xr9, xr20 + + xvmaddwev.w.h xr16, xr10, xr21 + xvmaddwod.w.h xr16, xr10, xr21 + xvmaddwev.w.h xr17, xr11, xr21 + xvmaddwod.w.h xr17, xr11, xr21 + + xvmaddwev.w.h xr16, xr12, xr22 + xvmaddwod.w.h xr16, xr12, xr22 + xvmaddwev.w.h xr17, xr13, xr22 + xvmaddwod.w.h xr17, xr13, xr22 + + xvmaddwev.w.h xr16, xr14, xr23 + xvmaddwod.w.h xr16, xr14, xr23 + xvmaddwev.w.h xr17, xr15, xr23 + xvmaddwod.w.h xr17, xr15, xr23 + + xvld xr0, t2, 0 + xvld xr1, t2, 32 + + xvadd.w xr18, xr0, xr16 + xvadd.w xr19, xr1, xr17 + xvsub.w xr0, xr0, xr16 + xvsub.w xr1, xr1, xr17 + + xvst xr18, t2, 0 + xvst xr19, t2, 32 + xvst xr0, t3, 0 + xvst xr1, t3, 32 +.endm + +.macro idct_16x32_step2_lasx in0, in1, in2, in3, in4, in5, in6, in7, out0, out1 + + xvldrepl.w xr20, t1, 0 + xvldrepl.w xr21, t1, 4 + xvldrepl.w xr22, t1, 8 + xvldrepl.w xr23, t1, 12 + + xvmulwev.w.h \out0, \in0, xr20 + xvmaddwod.w.h \out0, \in0, xr20 + xvmulwev.w.h \out1, \in1, xr20 + xvmaddwod.w.h \out1, \in1, xr20 + xvmaddwev.w.h \out0, \in2, xr21 + xvmaddwod.w.h \out0, \in2, xr21 + xvmaddwev.w.h \out1, \in3, xr21 + xvmaddwod.w.h \out1, \in3, xr21 + xvmaddwev.w.h \out0, \in4, xr22 + xvmaddwod.w.h \out0, \in4, xr22 + xvmaddwev.w.h \out1, \in5, xr22 + xvmaddwod.w.h \out1, \in5, xr22 + xvmaddwev.w.h \out0, \in6, xr23 + xvmaddwod.w.h \out0, \in6, xr23 + xvmaddwev.w.h \out1, \in7, xr23 // sum0_r + xvmaddwod.w.h \out1, \in7, xr23 // sum0_l +.endm + + /* loop for all columns of filter constants */ +.macro idct_16x32_step3_lasx round + xvadd.w xr16, xr16, xr30 + xvadd.w xr17, xr17, xr31 + + xvld xr0, t2, 0 + xvld xr1, t2, 32 + + xvadd.w xr30, xr0, xr16 + xvadd.w xr31, xr1, xr17 + xvsub.w xr16, xr0, xr16 + xvsub.w xr17, xr1, xr17 + xvssrarni.h.w xr31, xr30, \round + xvssrarni.h.w xr17, xr16, \round + xvst xr31, t4, 0 + xvst xr17, t5, 0 +.endm + +.macro idct_16x32_lasx buf_pitch, round + addi.d t2, sp, 64 + + addi.d t0, a0, \buf_pitch*4*2 + + // 4 12 20 28 + xvld xr0, t0, 0 + xvld xr1, t0, \buf_pitch*8*2 + xvld xr2, t0, \buf_pitch*16*2 + xvld xr3, t0, \buf_pitch*24*2 + + xvilvl.h xr10, xr1, xr0 + xvilvh.h xr11, xr1, xr0 + xvilvl.h xr12, xr3, xr2 + xvilvh.h xr13, xr3, xr2 + + la.local t1, gt32x32_cnst2 + + xvldrepl.w xr20, t1, 0 + xvldrepl.w xr21, t1, 4 + xvmulwev.w.h xr14, xr10, xr20 + xvmaddwod.w.h xr14, xr10, xr20 + xvmulwev.w.h xr15, xr11, xr20 + xvmaddwod.w.h xr15, xr11, xr20 + xvmaddwev.w.h xr14, xr12, xr21 + xvmaddwod.w.h xr14, xr12, xr21 + xvmaddwev.w.h xr15, xr13, xr21 + xvmaddwod.w.h xr15, xr13, xr21 + + xvldrepl.w xr20, t1, 8 + xvldrepl.w xr21, t1, 12 + xvmulwev.w.h xr16, xr10, xr20 + xvmaddwod.w.h xr16, xr10, xr20 + xvmulwev.w.h xr17, xr11, xr20 + xvmaddwod.w.h xr17, xr11, xr20 + xvmaddwev.w.h xr16, xr12, xr21 + xvmaddwod.w.h xr16, xr12, xr21 + xvmaddwev.w.h xr17, xr13, xr21 + xvmaddwod.w.h xr17, xr13, xr21 + + xvldrepl.w xr20, t1, 16 + xvldrepl.w xr21, t1, 20 + xvmulwev.w.h xr18, xr10, xr20 + xvmaddwod.w.h xr18, xr10, xr20 + xvmulwev.w.h xr19, xr11, xr20 + xvmaddwod.w.h xr19, xr11, xr20 + xvmaddwev.w.h xr18, xr12, xr21 + xvmaddwod.w.h xr18, xr12, xr21 + xvmaddwev.w.h xr19, xr13, xr21 + xvmaddwod.w.h xr19, xr13, xr21 + + xvldrepl.w xr20, t1, 24 + xvldrepl.w xr21, t1, 28 + xvmulwev.w.h xr22, xr10, xr20 + xvmaddwod.w.h xr22, xr10, xr20 + xvmulwev.w.h xr23, xr11, xr20 + xvmaddwod.w.h xr23, xr11, xr20 + xvmaddwev.w.h xr22, xr12, xr21 + xvmaddwod.w.h xr22, xr12, xr21 + xvmaddwev.w.h xr23, xr13, xr21 + xvmaddwod.w.h xr23, xr13, xr21 + + /* process coeff 0, 8, 16, 24 */ + la.local t1, gt8x8_cnst + + xvld xr0, a0, 0 + xvld xr1, a0, \buf_pitch*8*2 + xvld xr2, a0, \buf_pitch*16*2 + xvld xr3, a0, \buf_pitch*24*2 + + xvldrepl.w xr20, t1, 0 + xvldrepl.w xr21, t1, 4 + + xvilvl.h xr10, xr2, xr0 + xvilvh.h xr11, xr2, xr0 + xvilvl.h xr12, xr3, xr1 + xvilvh.h xr13, xr3, xr1 + + xvmulwev.w.h xr4, xr10, xr20 + xvmaddwod.w.h xr4, xr10, xr20 // sum0_r + xvmulwev.w.h xr5, xr11, xr20 + xvmaddwod.w.h xr5, xr11, xr20 // sum0_l + xvmulwev.w.h xr6, xr12, xr21 + xvmaddwod.w.h xr6, xr12, xr21 // tmp1_r + xvmulwev.w.h xr7, xr13, xr21 + xvmaddwod.w.h xr7, xr13, xr21 // tmp1_l + + xvsub.w xr0, xr4, xr6 // sum1_r + xvadd.w xr1, xr4, xr6 // sum0_r + xvsub.w xr2, xr5, xr7 // sum1_l + xvadd.w xr3, xr5, xr7 // sum0_l + + // HEVC_EVEN16_CALC + xvsub.w xr24, xr1, xr14 // 7 + xvsub.w xr25, xr3, xr15 + xvadd.w xr14, xr1, xr14 // 0 + xvadd.w xr15, xr3, xr15 + xvst xr24, t2, 7*16*4 // 448=16*28=7*16*4 + xvst xr25, t2, 7*16*4+32 // 480 + xvst xr14, t2, 0 + xvst xr15, t2, 32 + + xvsub.w xr26, xr0, xr22 // 4 + xvsub.w xr27, xr2, xr23 + xvadd.w xr22, xr0, xr22 // 3 + xvadd.w xr23, xr2, xr23 + xvst xr26, t2, 4*16*4 // 256=4*16*4 + xvst xr27, t2, 4*16*4+32 // 288 + xvst xr22, t2, 3*16*4 // 192=3*16*4 + xvst xr23, t2, 3*16*4+32 // 224 + + xvldrepl.w xr20, t1, 16 + xvldrepl.w xr21, t1, 20 + + xvmulwev.w.h xr4, xr10, xr20 + xvmaddwod.w.h xr4, xr10, xr20 + xvmulwev.w.h xr5, xr11, xr20 + xvmaddwod.w.h xr5, xr11, xr20 + xvmulwev.w.h xr6, xr12, xr21 + xvmaddwod.w.h xr6, xr12, xr21 + xvmulwev.w.h xr7, xr13, xr21 + xvmaddwod.w.h xr7, xr13, xr21 + + xvsub.w xr0, xr4, xr6 // sum1_r + xvadd.w xr1, xr4, xr6 // sum0_r + xvsub.w xr2, xr5, xr7 // sum1_l + xvadd.w xr3, xr5, xr7 // sum0_l + + // HEVC_EVEN16_CALC + xvsub.w xr24, xr1, xr16 // 6 + xvsub.w xr25, xr3, xr17 + xvadd.w xr16, xr1, xr16 // 1 + xvadd.w xr17, xr3, xr17 + xvst xr24, t2, 6*16*4 // 384=6*16*4 + xvst xr25, t2, 6*16*4+32 // 416 + xvst xr16, t2, 1*16*4 // 64=1*16*4 + xvst xr17, t2, 1*16*4+32 // 96 + + xvsub.w xr26, xr0, xr18 // 5 + xvsub.w xr27, xr2, xr19 + xvadd.w xr18, xr0, xr18 // 2 + xvadd.w xr19, xr2, xr19 + xvst xr26, t2, 5*16*4 // 320=5*16*4 + xvst xr27, t2, 5*16*4+32 // 352 + xvst xr18, t2, 2*16*4 // 128=2*16*4 + xvst xr19, t2, 2*16*4+32 // 160 + + /* process coeff 2 6 10 14 18 22 26 30 */ + addi.d t0, a0, \buf_pitch*2*2 + + xvld xr0, t0, 0 + xvld xr1, t0, \buf_pitch*4*2 + xvld xr2, t0, \buf_pitch*8*2 + xvld xr3, t0, \buf_pitch*12*2 + + xvld xr4, t0, \buf_pitch*16*2 + xvld xr5, t0, \buf_pitch*20*2 + xvld xr6, t0, \buf_pitch*24*2 + xvld xr7, t0, \buf_pitch*28*2 + + xvilvl.h xr8, xr1, xr0 + xvilvh.h xr9, xr1, xr0 + xvilvl.h xr10, xr3, xr2 + xvilvh.h xr11, xr3, xr2 + xvilvl.h xr12, xr5, xr4 + xvilvh.h xr13, xr5, xr4 + xvilvl.h xr14, xr7, xr6 + xvilvh.h xr15, xr7, xr6 + + la.local t1, gt32x32_cnst1 + + addi.d t2, sp, 64 + addi.d t3, sp, 64+960 // 30*32 + + idct_16x32_step1_lasx + +.rept 7 + addi.d t1, t1, 16 + addi.d t2, t2, 64 + addi.d t3, t3, -64 + idct_16x32_step1_lasx +.endr + + addi.d t0, a0, \buf_pitch*2 + + xvld xr0, t0, 0 + xvld xr1, t0, \buf_pitch*2*2 + xvld xr2, t0, \buf_pitch*4*2 + xvld xr3, t0, \buf_pitch*6*2 + xvld xr4, t0, \buf_pitch*8*2 + xvld xr5, t0, \buf_pitch*10*2 + xvld xr6, t0, \buf_pitch*12*2 + xvld xr7, t0, \buf_pitch*14*2 + + xvilvl.h xr8, xr1, xr0 + xvilvh.h xr9, xr1, xr0 + xvilvl.h xr10, xr3, xr2 + xvilvh.h xr11, xr3, xr2 + xvilvl.h xr12, xr5, xr4 + xvilvh.h xr13, xr5, xr4 + xvilvl.h xr14, xr7, xr6 + xvilvh.h xr15, xr7, xr6 + + la.local t1, gt32x32_cnst0 + + idct_16x32_step2_lasx xr8, xr9, xr10, xr11, xr12, xr13, \ + xr14, xr15, xr16, xr17 + + addi.d t0, a0, \buf_pitch*16*2+\buf_pitch*2 + + xvld xr0, t0, 0 + xvld xr1, t0, \buf_pitch*2*2 + xvld xr2, t0, \buf_pitch*4*2 + xvld xr3, t0, \buf_pitch*6*2 + xvld xr4, t0, \buf_pitch*8*2 + xvld xr5, t0, \buf_pitch*10*2 + xvld xr6, t0, \buf_pitch*12*2 + xvld xr7, t0, \buf_pitch*14*2 + + xvilvl.h xr18, xr1, xr0 + xvilvh.h xr19, xr1, xr0 + xvilvl.h xr24, xr3, xr2 + xvilvh.h xr25, xr3, xr2 + xvilvl.h xr26, xr5, xr4 + xvilvh.h xr27, xr5, xr4 + xvilvl.h xr28, xr7, xr6 + xvilvh.h xr29, xr7, xr6 + + addi.d t1, t1, 16 + idct_16x32_step2_lasx xr18, xr19, xr24, xr25, xr26, xr27, \ + xr28, xr29, xr30, xr31 + + addi.d t4, a0, 0 + addi.d t5, a0, \buf_pitch*31*2 + addi.d t2, sp, 64 + + idct_16x32_step3_lasx \round + +.rept 15 + + addi.d t1, t1, 16 + idct_16x32_step2_lasx xr8, xr9, xr10, xr11, xr12, xr13, \ + xr14, xr15, xr16, xr17 + + addi.d t1, t1, 16 + idct_16x32_step2_lasx xr18, xr19, xr24, xr25, xr26, xr27, \ + xr28, xr29, xr30, xr31 + + addi.d t2, t2, 64 + addi.d t4, t4, \buf_pitch*2 + addi.d t5, t5, -\buf_pitch*2 + + idct_16x32_step3_lasx \round +.endr + +.endm + +function hevc_idct_16x32_column_step1_lasx + addi.d sp, sp, -1600 // 64+512*3 + fr_store + + idct_16x32_lasx 32, 7 + + fr_recover + addi.d sp, sp, 1600 +endfunc + +function hevc_idct_16x32_column_step2_lasx + addi.d sp, sp, -1600 // 64+512*3 + fr_store + + idct_16x32_lasx 16, 12 + + fr_recover + addi.d sp, sp, 1600 +endfunc + +function hevc_idct_transpose_32x16_to_16x32_lasx + fr_store + + xvld xr0, a0, 0 + xvld xr1, a0, 64 + xvld xr2, a0, 128 + xvld xr3, a0, 192 + xvld xr4, a0, 256 + xvld xr5, a0, 320 + xvld xr6, a0, 384 + xvld xr7, a0, 448 + + xvpermi.q xr8, xr0, 0x01 + xvpermi.q xr9, xr1, 0x01 + xvpermi.q xr10, xr2, 0x01 + xvpermi.q xr11, xr3, 0x01 + xvpermi.q xr12, xr4, 0x01 + xvpermi.q xr13, xr5, 0x01 + xvpermi.q xr14, xr6, 0x01 + xvpermi.q xr15, xr7, 0x01 + + LSX_TRANSPOSE8x8_H vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + LSX_TRANSPOSE8x8_H vr8, vr9, vr10, vr11, vr12, vr13, vr14, vr15, \ + vr8, vr9, vr10, vr11, vr12, vr13, vr14, vr15, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + addi.d a0, a0, 512 + + vld vr24, a0, 0 + vld vr25, a0, 64 + vld vr26, a0, 128 + vld vr27, a0, 192 + vld vr28, a0, 256 + vld vr29, a0, 320 + vld vr30, a0, 384 + vld vr31, a0, 448 + + LSX_TRANSPOSE8x8_H vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + xvpermi.q xr0, xr24, 0x02 + xvpermi.q xr1, xr25, 0x02 + xvpermi.q xr2, xr26, 0x02 + xvpermi.q xr3, xr27, 0x02 + xvpermi.q xr4, xr28, 0x02 + xvpermi.q xr5, xr29, 0x02 + xvpermi.q xr6, xr30, 0x02 + xvpermi.q xr7, xr31, 0x02 + + xvst xr0, a1, 0 + xvst xr1, a1, 32 + xvst xr2, a1, 64 + xvst xr3, a1, 96 + xvst xr4, a1, 128 + xvst xr5, a1, 160 + xvst xr6, a1, 192 + xvst xr7, a1, 224 + + addi.d a1, a1, 256 + addi.d a0, a0, 16 + + vld vr24, a0, 0 + vld vr25, a0, 64 + vld vr26, a0, 128 + vld vr27, a0, 192 + vld vr28, a0, 256 + vld vr29, a0, 320 + vld vr30, a0, 384 + vld vr31, a0, 448 + + LSX_TRANSPOSE8x8_H vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + xvpermi.q xr8, xr24, 0x02 + xvpermi.q xr9, xr25, 0x02 + xvpermi.q xr10, xr26, 0x02 + xvpermi.q xr11, xr27, 0x02 + xvpermi.q xr12, xr28, 0x02 + xvpermi.q xr13, xr29, 0x02 + xvpermi.q xr14, xr30, 0x02 + xvpermi.q xr15, xr31, 0x02 + + xvst xr8, a1, 0 + xvst xr9, a1, 32 + xvst xr10, a1, 64 + xvst xr11, a1, 96 + xvst xr12, a1, 128 + xvst xr13, a1, 160 + xvst xr14, a1, 192 + xvst xr15, a1, 224 + + // second + addi.d a0, a0, 32-512-16 + + xvld xr0, a0, 0 + xvld xr1, a0, 64 + xvld xr2, a0, 128 + xvld xr3, a0, 192 + xvld xr4, a0, 256 + xvld xr5, a0, 320 + xvld xr6, a0, 384 + xvld xr7, a0, 448 + + xvpermi.q xr8, xr0, 0x01 + xvpermi.q xr9, xr1, 0x01 + xvpermi.q xr10, xr2, 0x01 + xvpermi.q xr11, xr3, 0x01 + xvpermi.q xr12, xr4, 0x01 + xvpermi.q xr13, xr5, 0x01 + xvpermi.q xr14, xr6, 0x01 + xvpermi.q xr15, xr7, 0x01 + + LSX_TRANSPOSE8x8_H vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + LSX_TRANSPOSE8x8_H vr8, vr9, vr10, vr11, vr12, vr13, vr14, vr15, \ + vr8, vr9, vr10, vr11, vr12, vr13, vr14, vr15, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + addi.d a0, a0, 512 + + vld vr24, a0, 0 + vld vr25, a0, 64 + vld vr26, a0, 128 + vld vr27, a0, 192 + vld vr28, a0, 256 + vld vr29, a0, 320 + vld vr30, a0, 384 + vld vr31, a0, 448 + + LSX_TRANSPOSE8x8_H vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + xvpermi.q xr0, xr24, 0x02 + xvpermi.q xr1, xr25, 0x02 + xvpermi.q xr2, xr26, 0x02 + xvpermi.q xr3, xr27, 0x02 + xvpermi.q xr4, xr28, 0x02 + xvpermi.q xr5, xr29, 0x02 + xvpermi.q xr6, xr30, 0x02 + xvpermi.q xr7, xr31, 0x02 + + addi.d a1, a1, 256 + xvst xr0, a1, 0 + xvst xr1, a1, 32 + xvst xr2, a1, 64 + xvst xr3, a1, 96 + xvst xr4, a1, 128 + xvst xr5, a1, 160 + xvst xr6, a1, 192 + xvst xr7, a1, 224 + + addi.d a1, a1, 256 + addi.d a0, a0, 16 + + vld vr24, a0, 0 + vld vr25, a0, 64 + vld vr26, a0, 128 + vld vr27, a0, 192 + vld vr28, a0, 256 + vld vr29, a0, 320 + vld vr30, a0, 384 + vld vr31, a0, 448 + + LSX_TRANSPOSE8x8_H vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + xvpermi.q xr8, xr24, 0x02 + xvpermi.q xr9, xr25, 0x02 + xvpermi.q xr10, xr26, 0x02 + xvpermi.q xr11, xr27, 0x02 + xvpermi.q xr12, xr28, 0x02 + xvpermi.q xr13, xr29, 0x02 + xvpermi.q xr14, xr30, 0x02 + xvpermi.q xr15, xr31, 0x02 + + xvst xr8, a1, 0 + xvst xr9, a1, 32 + xvst xr10, a1, 64 + xvst xr11, a1, 96 + xvst xr12, a1, 128 + xvst xr13, a1, 160 + xvst xr14, a1, 192 + xvst xr15, a1, 224 + + fr_recover +endfunc + +function hevc_idct_transpose_16x32_to_32x16_lasx + fr_store + + xvld xr0, a0, 0 + xvld xr1, a0, 32 + xvld xr2, a0, 64 + xvld xr3, a0, 96 + xvld xr4, a0, 128 + xvld xr5, a0, 160 + xvld xr6, a0, 192 + xvld xr7, a0, 224 + + xvpermi.q xr8, xr0, 0x01 + xvpermi.q xr9, xr1, 0x01 + xvpermi.q xr10, xr2, 0x01 + xvpermi.q xr11, xr3, 0x01 + xvpermi.q xr12, xr4, 0x01 + xvpermi.q xr13, xr5, 0x01 + xvpermi.q xr14, xr6, 0x01 + xvpermi.q xr15, xr7, 0x01 + + LSX_TRANSPOSE8x8_H vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + LSX_TRANSPOSE8x8_H vr8, vr9, vr10, vr11, vr12, vr13, vr14, vr15, \ + vr8, vr9, vr10, vr11, vr12, vr13, vr14, vr15, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + addi.d a0, a0, 256 + + vld vr24, a0, 0 + vld vr25, a0, 32 + vld vr26, a0, 64 + vld vr27, a0, 96 + vld vr28, a0, 128 + vld vr29, a0, 160 + vld vr30, a0, 192 + vld vr31, a0, 224 + + LSX_TRANSPOSE8x8_H vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + xvpermi.q xr0, xr24, 0x02 + xvpermi.q xr1, xr25, 0x02 + xvpermi.q xr2, xr26, 0x02 + xvpermi.q xr3, xr27, 0x02 + xvpermi.q xr4, xr28, 0x02 + xvpermi.q xr5, xr29, 0x02 + xvpermi.q xr6, xr30, 0x02 + xvpermi.q xr7, xr31, 0x02 + + xvst xr0, a1, 0 + xvst xr1, a1, 64 + xvst xr2, a1, 128 + xvst xr3, a1, 192 + xvst xr4, a1, 256 + xvst xr5, a1, 320 + xvst xr6, a1, 384 + xvst xr7, a1, 448 + + addi.d a1, a1, 512 + addi.d a0, a0, 16 + + vld vr24, a0, 0 + vld vr25, a0, 32 + vld vr26, a0, 64 + vld vr27, a0, 96 + vld vr28, a0, 128 + vld vr29, a0, 160 + vld vr30, a0, 192 + vld vr31, a0, 224 + + LSX_TRANSPOSE8x8_H vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + xvpermi.q xr8, xr24, 0x02 + xvpermi.q xr9, xr25, 0x02 + xvpermi.q xr10, xr26, 0x02 + xvpermi.q xr11, xr27, 0x02 + xvpermi.q xr12, xr28, 0x02 + xvpermi.q xr13, xr29, 0x02 + xvpermi.q xr14, xr30, 0x02 + xvpermi.q xr15, xr31, 0x02 + + xvst xr8, a1, 0 + xvst xr9, a1, 64 + xvst xr10, a1, 128 + xvst xr11, a1, 192 + xvst xr12, a1, 256 + xvst xr13, a1, 320 + xvst xr14, a1, 384 + xvst xr15, a1, 448 + + // second + addi.d a0, a0, 256-16 + + xvld xr0, a0, 0 + xvld xr1, a0, 32 + xvld xr2, a0, 64 + xvld xr3, a0, 96 + xvld xr4, a0, 128 + xvld xr5, a0, 160 + xvld xr6, a0, 192 + xvld xr7, a0, 224 + + xvpermi.q xr8, xr0, 0x01 + xvpermi.q xr9, xr1, 0x01 + xvpermi.q xr10, xr2, 0x01 + xvpermi.q xr11, xr3, 0x01 + xvpermi.q xr12, xr4, 0x01 + xvpermi.q xr13, xr5, 0x01 + xvpermi.q xr14, xr6, 0x01 + xvpermi.q xr15, xr7, 0x01 + + LSX_TRANSPOSE8x8_H vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr0, vr1, vr2, vr3, vr4, vr5, vr6, vr7, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + LSX_TRANSPOSE8x8_H vr8, vr9, vr10, vr11, vr12, vr13, vr14, vr15, \ + vr8, vr9, vr10, vr11, vr12, vr13, vr14, vr15, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + addi.d a0, a0, 256 + + vld vr24, a0, 0 + vld vr25, a0, 32 + vld vr26, a0, 64 + vld vr27, a0, 96 + vld vr28, a0, 128 + vld vr29, a0, 160 + vld vr30, a0, 192 + vld vr31, a0, 224 + + LSX_TRANSPOSE8x8_H vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + xvpermi.q xr0, xr24, 0x02 + xvpermi.q xr1, xr25, 0x02 + xvpermi.q xr2, xr26, 0x02 + xvpermi.q xr3, xr27, 0x02 + xvpermi.q xr4, xr28, 0x02 + xvpermi.q xr5, xr29, 0x02 + xvpermi.q xr6, xr30, 0x02 + xvpermi.q xr7, xr31, 0x02 + + addi.d a1, a1, -512+32 + + xvst xr0, a1, 0 + xvst xr1, a1, 64 + xvst xr2, a1, 128 + xvst xr3, a1, 192 + xvst xr4, a1, 256 + xvst xr5, a1, 320 + xvst xr6, a1, 384 + xvst xr7, a1, 448 + + addi.d a1, a1, 512 + addi.d a0, a0, 16 + + vld vr24, a0, 0 + vld vr25, a0, 32 + vld vr26, a0, 64 + vld vr27, a0, 96 + vld vr28, a0, 128 + vld vr29, a0, 160 + vld vr30, a0, 192 + vld vr31, a0, 224 + + LSX_TRANSPOSE8x8_H vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr24, vr25, vr26, vr27, vr28, vr29, vr30, vr31, \ + vr16, vr17, vr18, vr19, vr20, vr21, vr22, vr23 + + xvpermi.q xr8, xr24, 0x02 + xvpermi.q xr9, xr25, 0x02 + xvpermi.q xr10, xr26, 0x02 + xvpermi.q xr11, xr27, 0x02 + xvpermi.q xr12, xr28, 0x02 + xvpermi.q xr13, xr29, 0x02 + xvpermi.q xr14, xr30, 0x02 + xvpermi.q xr15, xr31, 0x02 + + xvst xr8, a1, 0 + xvst xr9, a1, 64 + xvst xr10, a1, 128 + xvst xr11, a1, 192 + xvst xr12, a1, 256 + xvst xr13, a1, 320 + xvst xr14, a1, 384 + xvst xr15, a1, 448 + + fr_recover +endfunc + +function ff_hevc_idct_32x32_lasx + + addi.d t7, a0, 0 + addi.d t6, a1, 0 + + addi.d sp, sp, -8 + st.d ra, sp, 0 + + bl hevc_idct_16x32_column_step1_lasx + + addi.d a0, a0, 32 + + bl hevc_idct_16x32_column_step1_lasx + + addi.d sp, sp, -1086 // (16*32+31)*2 + fr_store + + addi.d t8, sp, 64+31*2 // tmp_buf_ptr + + addi.d a0, t7, 0 + addi.d a1, t8, 0 + bl hevc_idct_transpose_32x16_to_16x32_lasx + + addi.d a0, t8, 0 + bl hevc_idct_16x32_column_step2_lasx + + addi.d a0, t8, 0 + addi.d a1, t7, 0 + bl hevc_idct_transpose_16x32_to_32x16_lasx + + // second + addi.d a0, t7, 32*8*2*2 + addi.d a1, t8, 0 + bl hevc_idct_transpose_32x16_to_16x32_lasx + + addi.d a0, t8, 0 + bl hevc_idct_16x32_column_step2_lasx + + addi.d a0, t8, 0 + addi.d a1, t7, 32*8*2*2 + bl hevc_idct_transpose_16x32_to_32x16_lasx + + fr_recover + addi.d sp, sp, 1086 // (16*32+31)*2 + + ld.d ra, sp, 0 + addi.d sp, sp, 8 + +endfunc diff --git a/libavcodec/loongarch/hevc_idct_lsx.c b/libavcodec/loongarch/hevc_idct_lsx.c index 2193b27546b..527279d85db 100644 --- a/libavcodec/loongarch/hevc_idct_lsx.c +++ b/libavcodec/loongarch/hevc_idct_lsx.c @@ -23,18 +23,18 @@ #include "libavutil/loongarch/loongson_intrinsics.h" #include "hevcdsp_lsx.h" -static const int16_t gt8x8_cnst[16] __attribute__ ((aligned (64))) = { +const int16_t gt8x8_cnst[16] __attribute__ ((aligned (64))) = { 64, 64, 83, 36, 89, 50, 18, 75, 64, -64, 36, -83, 75, -89, -50, -18 }; -static const int16_t gt16x16_cnst[64] __attribute__ ((aligned (64))) = { +const int16_t gt16x16_cnst[64] __attribute__ ((aligned (64))) = { 64, 83, 64, 36, 89, 75, 50, 18, 90, 80, 57, 25, 70, 87, 9, 43, 64, 36, -64, -83, 75, -18, -89, -50, 87, 9, -80, -70, -43, 57, -25, -90, 64, -36, -64, 83, 50, -89, 18, 75, 80, -70, -25, 90, -87, 9, 43, 57, 64, -83, 64, -36, 18, -50, 75, -89, 70, -87, 90, -80, 9, -43, -57, 25 }; -static const int16_t gt32x32_cnst0[256] __attribute__ ((aligned (64))) = { +const int16_t gt32x32_cnst0[256] __attribute__ ((aligned (64))) = { 90, 90, 88, 85, 82, 78, 73, 67, 61, 54, 46, 38, 31, 22, 13, 4, 90, 82, 67, 46, 22, -4, -31, -54, -73, -85, -90, -88, -78, -61, -38, -13, 88, 67, 31, -13, -54, -82, -90, -78, -46, -4, 38, 73, 90, 85, 61, 22, @@ -53,14 +53,14 @@ static const int16_t gt32x32_cnst0[256] __attribute__ ((aligned (64))) = { 4, -13, 22, -31, 38, -46, 54, -61, 67, -73, 78, -82, 85, -88, 90, -90 }; -static const int16_t gt32x32_cnst1[64] __attribute__ ((aligned (64))) = { +const int16_t gt32x32_cnst1[64] __attribute__ ((aligned (64))) = { 90, 87, 80, 70, 57, 43, 25, 9, 87, 57, 9, -43, -80, -90, -70, -25, 80, 9, -70, -87, -25, 57, 90, 43, 70, -43, -87, 9, 90, 25, -80, -57, 57, -80, -25, 90, -9, -87, 43, 70, 43, -90, 57, 25, -87, 70, 9, -80, 25, -70, 90, -80, 43, 9, -57, 87, 9, -25, 43, -57, 70, -80, 87, -90 }; -static const int16_t gt32x32_cnst2[16] __attribute__ ((aligned (64))) = { +const int16_t gt32x32_cnst2[16] __attribute__ ((aligned (64))) = { 89, 75, 50, 18, 75, -18, -89, -50, 50, -89, 18, 75, 18, -50, 75, -89 }; diff --git a/libavcodec/loongarch/hevc_mc.S b/libavcodec/loongarch/hevc_mc.S new file mode 100644 index 00000000000..12d92e32e93 --- /dev/null +++ b/libavcodec/loongarch/hevc_mc.S @@ -0,0 +1,4445 @@ +/* + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by jinbo + * + * 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 "loongson_asm.S" + +.extern ff_hevc_qpel_filters +.extern ff_hevc_epel_filters + +.macro LOAD_VAR bit + addi.w t1, a5, 6 //shift + addi.w t3, zero, 1 //one + sub.w t4, t1, t3 + sll.w t3, t3, t4 //offset +.if \bit == 128 + vreplgr2vr.w vr1, a6 //wx + vreplgr2vr.w vr2, t3 //offset + vreplgr2vr.w vr3, t1 //shift + vreplgr2vr.w vr4, a7 //ox +.else + xvreplgr2vr.w xr1, a6 + xvreplgr2vr.w xr2, t3 + xvreplgr2vr.w xr3, t1 + xvreplgr2vr.w xr4, a7 +.endif +.endm + +.macro HEVC_PEL_UNI_W_PIXELS8_LSX src0, dst0, w + vldrepl.d vr0, \src0, 0 + vsllwil.hu.bu vr0, vr0, 0 + vexth.wu.hu vr5, vr0 + vsllwil.wu.hu vr0, vr0, 0 + vslli.w vr0, vr0, 6 + vslli.w vr5, vr5, 6 + vmul.w vr0, vr0, vr1 + vmul.w vr5, vr5, vr1 + vadd.w vr0, vr0, vr2 + vadd.w vr5, vr5, vr2 + vsra.w vr0, vr0, vr3 + vsra.w vr5, vr5, vr3 + vadd.w vr0, vr0, vr4 + vadd.w vr5, vr5, vr4 + vssrani.h.w vr5, vr0, 0 + vssrani.bu.h vr5, vr5, 0 +.if \w == 6 + fst.s f5, \dst0, 0 + vstelm.h vr5, \dst0, 4, 2 +.else + fst.d f5, \dst0, 0 +.endif +.endm + +.macro HEVC_PEL_UNI_W_PIXELS8x2_LASX src0, dst0, w + vldrepl.d vr0, \src0, 0 + add.d t2, \src0, a3 + vldrepl.d vr5, t2, 0 + xvpermi.q xr0, xr5, 0x02 + xvsllwil.hu.bu xr0, xr0, 0 + xvexth.wu.hu xr5, xr0 + xvsllwil.wu.hu xr0, xr0, 0 + xvslli.w xr0, xr0, 6 + xvslli.w xr5, xr5, 6 + xvmul.w xr0, xr0, xr1 + xvmul.w xr5, xr5, xr1 + xvadd.w xr0, xr0, xr2 + xvadd.w xr5, xr5, xr2 + xvsra.w xr0, xr0, xr3 + xvsra.w xr5, xr5, xr3 + xvadd.w xr0, xr0, xr4 + xvadd.w xr5, xr5, xr4 + xvssrani.h.w xr5, xr0, 0 + xvpermi.q xr0, xr5, 0x01 + xvssrani.bu.h xr0, xr5, 0 + add.d t3, \dst0, a1 +.if \w == 6 + vstelm.w vr0, \dst0, 0, 0 + vstelm.h vr0, \dst0, 4, 2 + vstelm.w vr0, t3, 0, 2 + vstelm.h vr0, t3, 4, 6 +.else + vstelm.d vr0, \dst0, 0, 0 + vstelm.d vr0, t3, 0, 1 +.endif +.endm + +.macro HEVC_PEL_UNI_W_PIXELS16_LSX src0, dst0 + vld vr0, \src0, 0 + vexth.hu.bu vr7, vr0 + vexth.wu.hu vr8, vr7 + vsllwil.wu.hu vr7, vr7, 0 + vsllwil.hu.bu vr5, vr0, 0 + vexth.wu.hu vr6, vr5 + vsllwil.wu.hu vr5, vr5, 0 + vslli.w vr5, vr5, 6 + vslli.w vr6, vr6, 6 + vslli.w vr7, vr7, 6 + vslli.w vr8, vr8, 6 + vmul.w vr5, vr5, vr1 + vmul.w vr6, vr6, vr1 + vmul.w vr7, vr7, vr1 + vmul.w vr8, vr8, vr1 + vadd.w vr5, vr5, vr2 + vadd.w vr6, vr6, vr2 + vadd.w vr7, vr7, vr2 + vadd.w vr8, vr8, vr2 + vsra.w vr5, vr5, vr3 + vsra.w vr6, vr6, vr3 + vsra.w vr7, vr7, vr3 + vsra.w vr8, vr8, vr3 + vadd.w vr5, vr5, vr4 + vadd.w vr6, vr6, vr4 + vadd.w vr7, vr7, vr4 + vadd.w vr8, vr8, vr4 + vssrani.h.w vr6, vr5, 0 + vssrani.h.w vr8, vr7, 0 + vssrani.bu.h vr8, vr6, 0 + vst vr8, \dst0, 0 +.endm + +.macro HEVC_PEL_UNI_W_PIXELS16_LASX src0, dst0 + vld vr0, \src0, 0 + xvpermi.d xr0, xr0, 0xd8 + xvsllwil.hu.bu xr0, xr0, 0 + xvexth.wu.hu xr6, xr0 + xvsllwil.wu.hu xr5, xr0, 0 + xvslli.w xr5, xr5, 6 + xvslli.w xr6, xr6, 6 + xvmul.w xr5, xr5, xr1 + xvmul.w xr6, xr6, xr1 + xvadd.w xr5, xr5, xr2 + xvadd.w xr6, xr6, xr2 + xvsra.w xr5, xr5, xr3 + xvsra.w xr6, xr6, xr3 + xvadd.w xr5, xr5, xr4 + xvadd.w xr6, xr6, xr4 + xvssrani.h.w xr6, xr5, 0 + xvpermi.q xr7, xr6, 0x01 + xvssrani.bu.h xr7, xr6, 0 + vst vr7, \dst0, 0 +.endm + +.macro HEVC_PEL_UNI_W_PIXELS32_LASX src0, dst0, w +.if \w == 16 + vld vr0, \src0, 0 + add.d t2, \src0, a3 + vld vr5, t2, 0 + xvpermi.q xr0, xr5, 0x02 +.else //w=24/32 + xvld xr0, \src0, 0 +.endif + xvexth.hu.bu xr7, xr0 + xvexth.wu.hu xr8, xr7 + xvsllwil.wu.hu xr7, xr7, 0 + xvsllwil.hu.bu xr5, xr0, 0 + xvexth.wu.hu xr6, xr5 + xvsllwil.wu.hu xr5, xr5, 0 + xvslli.w xr5, xr5, 6 + xvslli.w xr6, xr6, 6 + xvslli.w xr7, xr7, 6 + xvslli.w xr8, xr8, 6 + xvmul.w xr5, xr5, xr1 + xvmul.w xr6, xr6, xr1 + xvmul.w xr7, xr7, xr1 + xvmul.w xr8, xr8, xr1 + xvadd.w xr5, xr5, xr2 + xvadd.w xr6, xr6, xr2 + xvadd.w xr7, xr7, xr2 + xvadd.w xr8, xr8, xr2 + xvsra.w xr5, xr5, xr3 + xvsra.w xr6, xr6, xr3 + xvsra.w xr7, xr7, xr3 + xvsra.w xr8, xr8, xr3 + xvadd.w xr5, xr5, xr4 + xvadd.w xr6, xr6, xr4 + xvadd.w xr7, xr7, xr4 + xvadd.w xr8, xr8, xr4 + xvssrani.h.w xr6, xr5, 0 + xvssrani.h.w xr8, xr7, 0 + xvssrani.bu.h xr8, xr6, 0 +.if \w == 16 + vst vr8, \dst0, 0 + add.d t2, \dst0, a1 + xvpermi.q xr8, xr8, 0x01 + vst vr8, t2, 0 +.elseif \w == 24 + vst vr8, \dst0, 0 + xvstelm.d xr8, \dst0, 16, 2 +.else + xvst xr8, \dst0, 0 +.endif +.endm + +/* + * void FUNC(put_hevc_pel_uni_w_pixels)(uint8_t *_dst, ptrdiff_t _dststride, + * const uint8_t *_src, ptrdiff_t _srcstride, + * int height, int denom, int wx, int ox, + * intptr_t mx, intptr_t my, int width) + */ +function ff_hevc_put_hevc_pel_uni_w_pixels4_8_lsx + LOAD_VAR 128 + srli.w t0, a4, 1 +.LOOP_PIXELS4: + vldrepl.w vr0, a2, 0 + add.d t1, a2, a3 + vldrepl.w vr5, t1, 0 + vsllwil.hu.bu vr0, vr0, 0 + vsllwil.wu.hu vr0, vr0, 0 + vsllwil.hu.bu vr5, vr5, 0 + vsllwil.wu.hu vr5, vr5, 0 + vslli.w vr0, vr0, 6 + vslli.w vr5, vr5, 6 + vmul.w vr0, vr0, vr1 + vmul.w vr5, vr5, vr1 + vadd.w vr0, vr0, vr2 + vadd.w vr5, vr5, vr2 + vsra.w vr0, vr0, vr3 + vsra.w vr5, vr5, vr3 + vadd.w vr0, vr0, vr4 + vadd.w vr5, vr5, vr4 + vssrani.h.w vr5, vr0, 0 + vssrani.bu.h vr5, vr5, 0 + fst.s f5, a0, 0 + add.d t2, a0, a1 + vstelm.w vr5, t2, 0, 1 + alsl.d a2, a3, a2, 1 + alsl.d a0, a1, a0, 1 + addi.w t0, t0, -1 + bnez t0, .LOOP_PIXELS4 +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels6_8_lsx + LOAD_VAR 128 +.LOOP_PIXELS6: + HEVC_PEL_UNI_W_PIXELS8_LSX a2, a0, 6 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.w a4, a4, -1 + bnez a4, .LOOP_PIXELS6 +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels6_8_lasx + LOAD_VAR 256 + srli.w t0, a4, 1 +.LOOP_PIXELS6_LASX: + HEVC_PEL_UNI_W_PIXELS8x2_LASX a2, a0, 6 + alsl.d a2, a3, a2, 1 + alsl.d a0, a1, a0, 1 + addi.w t0, t0, -1 + bnez t0, .LOOP_PIXELS6_LASX +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels8_8_lsx + LOAD_VAR 128 +.LOOP_PIXELS8: + HEVC_PEL_UNI_W_PIXELS8_LSX a2, a0, 8 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.w a4, a4, -1 + bnez a4, .LOOP_PIXELS8 +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels8_8_lasx + LOAD_VAR 256 + srli.w t0, a4, 1 +.LOOP_PIXELS8_LASX: + HEVC_PEL_UNI_W_PIXELS8x2_LASX a2, a0, 8 + alsl.d a2, a3, a2, 1 + alsl.d a0, a1, a0, 1 + addi.w t0, t0, -1 + bnez t0, .LOOP_PIXELS8_LASX +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels12_8_lsx + LOAD_VAR 128 +.LOOP_PIXELS12: + vld vr0, a2, 0 + vexth.hu.bu vr7, vr0 + vsllwil.wu.hu vr7, vr7, 0 + vsllwil.hu.bu vr5, vr0, 0 + vexth.wu.hu vr6, vr5 + vsllwil.wu.hu vr5, vr5, 0 + vslli.w vr5, vr5, 6 + vslli.w vr6, vr6, 6 + vslli.w vr7, vr7, 6 + vmul.w vr5, vr5, vr1 + vmul.w vr6, vr6, vr1 + vmul.w vr7, vr7, vr1 + vadd.w vr5, vr5, vr2 + vadd.w vr6, vr6, vr2 + vadd.w vr7, vr7, vr2 + vsra.w vr5, vr5, vr3 + vsra.w vr6, vr6, vr3 + vsra.w vr7, vr7, vr3 + vadd.w vr5, vr5, vr4 + vadd.w vr6, vr6, vr4 + vadd.w vr7, vr7, vr4 + vssrani.h.w vr6, vr5, 0 + vssrani.h.w vr7, vr7, 0 + vssrani.bu.h vr7, vr6, 0 + fst.d f7, a0, 0 + vstelm.w vr7, a0, 8, 2 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.w a4, a4, -1 + bnez a4, .LOOP_PIXELS12 +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels12_8_lasx + LOAD_VAR 256 +.LOOP_PIXELS12_LASX: + vld vr0, a2, 0 + xvpermi.d xr0, xr0, 0xd8 + xvsllwil.hu.bu xr0, xr0, 0 + xvexth.wu.hu xr6, xr0 + xvsllwil.wu.hu xr5, xr0, 0 + xvslli.w xr5, xr5, 6 + xvslli.w xr6, xr6, 6 + xvmul.w xr5, xr5, xr1 + xvmul.w xr6, xr6, xr1 + xvadd.w xr5, xr5, xr2 + xvadd.w xr6, xr6, xr2 + xvsra.w xr5, xr5, xr3 + xvsra.w xr6, xr6, xr3 + xvadd.w xr5, xr5, xr4 + xvadd.w xr6, xr6, xr4 + xvssrani.h.w xr6, xr5, 0 + xvpermi.q xr7, xr6, 0x01 + xvssrani.bu.h xr7, xr6, 0 + fst.d f7, a0, 0 + vstelm.w vr7, a0, 8, 2 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.w a4, a4, -1 + bnez a4, .LOOP_PIXELS12_LASX +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels16_8_lsx + LOAD_VAR 128 +.LOOP_PIXELS16: + HEVC_PEL_UNI_W_PIXELS16_LSX a2, a0 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.w a4, a4, -1 + bnez a4, .LOOP_PIXELS16 +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels16_8_lasx + LOAD_VAR 256 + srli.w t0, a4, 1 +.LOOP_PIXELS16_LASX: + HEVC_PEL_UNI_W_PIXELS32_LASX a2, a0, 16 + alsl.d a2, a3, a2, 1 + alsl.d a0, a1, a0, 1 + addi.w t0, t0, -1 + bnez t0, .LOOP_PIXELS16_LASX +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels24_8_lsx + LOAD_VAR 128 +.LOOP_PIXELS24: + HEVC_PEL_UNI_W_PIXELS16_LSX a2, a0 + addi.d t0, a2, 16 + addi.d t1, a0, 16 + HEVC_PEL_UNI_W_PIXELS8_LSX t0, t1, 8 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.w a4, a4, -1 + bnez a4, .LOOP_PIXELS24 +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels24_8_lasx + LOAD_VAR 256 +.LOOP_PIXELS24_LASX: + HEVC_PEL_UNI_W_PIXELS32_LASX a2, a0, 24 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.w a4, a4, -1 + bnez a4, .LOOP_PIXELS24_LASX +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels32_8_lsx + LOAD_VAR 128 +.LOOP_PIXELS32: + HEVC_PEL_UNI_W_PIXELS16_LSX a2, a0 + addi.d t0, a2, 16 + addi.d t1, a0, 16 + HEVC_PEL_UNI_W_PIXELS16_LSX t0, t1 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.w a4, a4, -1 + bnez a4, .LOOP_PIXELS32 +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels32_8_lasx + LOAD_VAR 256 +.LOOP_PIXELS32_LASX: + HEVC_PEL_UNI_W_PIXELS32_LASX a2, a0, 32 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.w a4, a4, -1 + bnez a4, .LOOP_PIXELS32_LASX +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels48_8_lsx + LOAD_VAR 128 +.LOOP_PIXELS48: + HEVC_PEL_UNI_W_PIXELS16_LSX a2, a0 + addi.d t0, a2, 16 + addi.d t1, a0, 16 + HEVC_PEL_UNI_W_PIXELS16_LSX t0, t1 + addi.d t0, a2, 32 + addi.d t1, a0, 32 + HEVC_PEL_UNI_W_PIXELS16_LSX t0, t1 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.w a4, a4, -1 + bnez a4, .LOOP_PIXELS48 +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels48_8_lasx + LOAD_VAR 256 +.LOOP_PIXELS48_LASX: + HEVC_PEL_UNI_W_PIXELS32_LASX a2, a0, 32 + addi.d t0, a2, 32 + addi.d t1, a0, 32 + HEVC_PEL_UNI_W_PIXELS16_LASX t0, t1 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.w a4, a4, -1 + bnez a4, .LOOP_PIXELS48_LASX +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels64_8_lsx + LOAD_VAR 128 +.LOOP_PIXELS64: + HEVC_PEL_UNI_W_PIXELS16_LSX a2, a0 + addi.d t0, a2, 16 + addi.d t1, a0, 16 + HEVC_PEL_UNI_W_PIXELS16_LSX t0, t1 + addi.d t0, a2, 32 + addi.d t1, a0, 32 + HEVC_PEL_UNI_W_PIXELS16_LSX t0, t1 + addi.d t0, a2, 48 + addi.d t1, a0, 48 + HEVC_PEL_UNI_W_PIXELS16_LSX t0, t1 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.w a4, a4, -1 + bnez a4, .LOOP_PIXELS64 +endfunc + +function ff_hevc_put_hevc_pel_uni_w_pixels64_8_lasx + LOAD_VAR 256 +.LOOP_PIXELS64_LASX: + HEVC_PEL_UNI_W_PIXELS32_LASX a2, a0, 32 + addi.d t0, a2, 32 + addi.d t1, a0, 32 + HEVC_PEL_UNI_W_PIXELS32_LASX t0, t1, 32 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.w a4, a4, -1 + bnez a4, .LOOP_PIXELS64_LASX +endfunc + +.macro vhaddw.d.h in0 + vhaddw.w.h \in0, \in0, \in0 + vhaddw.d.w \in0, \in0, \in0 +.endm + +.macro xvhaddw.d.h in0 + xvhaddw.w.h \in0, \in0, \in0 + xvhaddw.d.w \in0, \in0, \in0 +.endm + +/* + * void FUNC(put_hevc_qpel_uni_w_v)(uint8_t *_dst, ptrdiff_t _dststride, + * const uint8_t *_src, ptrdiff_t _srcstride, + * int height, int denom, int wx, int ox, + * intptr_t mx, intptr_t my, int width) + */ +function ff_hevc_put_hevc_qpel_uni_w_v4_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + fld.s f6, a2, 0 //0 + fldx.s f7, a2, a3 //1 + fldx.s f8, a2, t0 //2 + add.d a2, a2, t1 + fld.s f9, a2, 0 //3 + fldx.s f10, a2, a3 //4 + fldx.s f11, a2, t0 //5 + fldx.s f12, a2, t1 //6 + add.d a2, a2, t2 + vilvl.b vr6, vr7, vr6 + vilvl.b vr7, vr9, vr8 + vilvl.b vr8, vr11, vr10 + vilvl.b vr9, vr13, vr12 + vilvl.h vr6, vr7, vr6 + vilvl.h vr7, vr9, vr8 + vilvl.w vr8, vr7, vr6 + vilvh.w vr9, vr7, vr6 +.LOOP_V4: + fld.s f13, a2, 0 //7 + fldx.s f14, a2, a3 //8 next loop + add.d a2, a2, t0 + vextrins.b vr8, vr13, 0x70 + vextrins.b vr8, vr13, 0xf1 + vextrins.b vr9, vr13, 0x72 + vextrins.b vr9, vr13, 0xf3 + vbsrl.v vr10, vr8, 1 + vbsrl.v vr11, vr9, 1 + vextrins.b vr10, vr14, 0x70 + vextrins.b vr10, vr14, 0xf1 + vextrins.b vr11, vr14, 0x72 + vextrins.b vr11, vr14, 0xf3 + vdp2.h.bu.b vr6, vr8, vr5 //QPEL_FILTER(src, stride) + vdp2.h.bu.b vr7, vr9, vr5 + vdp2.h.bu.b vr12, vr10, vr5 + vdp2.h.bu.b vr13, vr11, vr5 + vbsrl.v vr8, vr10, 1 + vbsrl.v vr9, vr11, 1 + vhaddw.d.h vr6 + vhaddw.d.h vr7 + vhaddw.d.h vr12 + vhaddw.d.h vr13 + vpickev.w vr6, vr7, vr6 + vpickev.w vr12, vr13, vr12 + vmulwev.w.h vr6, vr6, vr1 //QPEL_FILTER(src, stride) * wx + vmulwev.w.h vr12, vr12, vr1 + vadd.w vr6, vr6, vr2 + vsra.w vr6, vr6, vr3 + vadd.w vr6, vr6, vr4 + vadd.w vr12, vr12, vr2 + vsra.w vr12, vr12, vr3 + vadd.w vr12, vr12, vr4 + vssrani.h.w vr12, vr6, 0 + vssrani.bu.h vr12, vr12, 0 + fst.s f12, a0, 0 + add.d a0, a0, a1 + vstelm.w vr12, a0, 0, 1 + add.d a0, a0, a1 + addi.d a4, a4, -2 + bnez a4, .LOOP_V4 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_v6_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + fld.d f6, a2, 0 + fldx.d f7, a2, a3 + fldx.d f8, a2, t0 + add.d a2, a2, t1 + fld.d f9, a2, 0 + fldx.d f10, a2, a3 + fldx.d f11, a2, t0 + fldx.d f12, a2, t1 + add.d a2, a2, t2 + vilvl.b vr6, vr7, vr6 //transpose 8x6 to 3x16 + vilvl.b vr7, vr9, vr8 + vilvl.b vr8, vr11, vr10 + vilvl.b vr9, vr13, vr12 + vilvl.h vr10, vr7, vr6 + vilvh.h vr11, vr7, vr6 + vilvl.h vr12, vr9, vr8 + vilvh.h vr13, vr9, vr8 + vilvl.w vr6, vr12, vr10 + vilvh.w vr7, vr12, vr10 + vilvl.w vr8, vr13, vr11 +.LOOP_V6: + fld.d f13, a2, 0 + add.d a2, a2, a3 + vextrins.b vr6, vr13, 0x70 + vextrins.b vr6, vr13, 0xf1 + vextrins.b vr7, vr13, 0x72 + vextrins.b vr7, vr13, 0xf3 + vextrins.b vr8, vr13, 0x74 + vextrins.b vr8, vr13, 0xf5 + vdp2.h.bu.b vr10, vr6, vr5 //QPEL_FILTER(src, stride) + vdp2.h.bu.b vr11, vr7, vr5 + vdp2.h.bu.b vr12, vr8, vr5 + vbsrl.v vr6, vr6, 1 + vbsrl.v vr7, vr7, 1 + vbsrl.v vr8, vr8, 1 + vhaddw.d.h vr10 + vhaddw.d.h vr11 + vhaddw.d.h vr12 + vpickev.w vr10, vr11, vr10 + vpickev.w vr11, vr13, vr12 + vmulwev.w.h vr10, vr10, vr1 //QPEL_FILTER(src, stride) * wx + vmulwev.w.h vr11, vr11, vr1 + vadd.w vr10, vr10, vr2 + vadd.w vr11, vr11, vr2 + vsra.w vr10, vr10, vr3 + vsra.w vr11, vr11, vr3 + vadd.w vr10, vr10, vr4 + vadd.w vr11, vr11, vr4 + vssrani.h.w vr11, vr10, 0 + vssrani.bu.h vr11, vr11, 0 + fst.s f11, a0, 0 + vstelm.h vr11, a0, 4, 2 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_V6 +endfunc + +// transpose 8x8b to 4x16b +.macro TRANSPOSE8X8B_LSX in0, in1, in2, in3, in4, in5, in6, in7, \ + out0, out1, out2, out3 + vilvl.b \in0, \in1, \in0 + vilvl.b \in1, \in3, \in2 + vilvl.b \in2, \in5, \in4 + vilvl.b \in3, \in7, \in6 + vilvl.h \in4, \in1, \in0 + vilvh.h \in5, \in1, \in0 + vilvl.h \in6, \in3, \in2 + vilvh.h \in7, \in3, \in2 + vilvl.w \out0, \in6, \in4 + vilvh.w \out1, \in6, \in4 + vilvl.w \out2, \in7, \in5 + vilvh.w \out3, \in7, \in5 +.endm + +.macro PUT_HEVC_QPEL_UNI_W_V8_LSX in0, in1, in2, in3, out0, out1, pos +.if \pos == 0 + vextrins.b \in0, vr13, 0x70 //insert the 8th load + vextrins.b \in0, vr13, 0xf1 + vextrins.b \in1, vr13, 0x72 + vextrins.b \in1, vr13, 0xf3 + vextrins.b \in2, vr13, 0x74 + vextrins.b \in2, vr13, 0xf5 + vextrins.b \in3, vr13, 0x76 + vextrins.b \in3, vr13, 0xf7 +.else// \pos == 8 + vextrins.b \in0, vr13, 0x78 + vextrins.b \in0, vr13, 0xf9 + vextrins.b \in1, vr13, 0x7a + vextrins.b \in1, vr13, 0xfb + vextrins.b \in2, vr13, 0x7c + vextrins.b \in2, vr13, 0xfd + vextrins.b \in3, vr13, 0x7e + vextrins.b \in3, vr13, 0xff +.endif + vdp2.h.bu.b \out0, \in0, vr5 //QPEL_FILTER(src, stride) + vdp2.h.bu.b \out1, \in1, vr5 + vdp2.h.bu.b vr12, \in2, vr5 + vdp2.h.bu.b vr20, \in3, vr5 + vbsrl.v \in0, \in0, 1 //Back up previous 7 loaded datas, + vbsrl.v \in1, \in1, 1 //so just need to insert the 8th + vbsrl.v \in2, \in2, 1 //load in the next loop. + vbsrl.v \in3, \in3, 1 + vhaddw.d.h \out0 + vhaddw.d.h \out1 + vhaddw.d.h vr12 + vhaddw.d.h vr20 + vpickev.w \out0, \out1, \out0 + vpickev.w \out1, vr20, vr12 + vmulwev.w.h \out0, \out0, vr1 //QPEL_FILTER(src, stride) * wx + vmulwev.w.h \out1, \out1, vr1 + vadd.w \out0, \out0, vr2 + vadd.w \out1, \out1, vr2 + vsra.w \out0, \out0, vr3 + vsra.w \out1, \out1, vr3 + vadd.w \out0, \out0, vr4 + vadd.w \out1, \out1, vr4 +.endm + +function ff_hevc_put_hevc_qpel_uni_w_v8_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + fld.d f6, a2, 0 + fldx.d f7, a2, a3 + fldx.d f8, a2, t0 + add.d a2, a2, t1 + fld.d f9, a2, 0 + fldx.d f10, a2, a3 + fldx.d f11, a2, t0 + fldx.d f12, a2, t1 + add.d a2, a2, t2 + TRANSPOSE8X8B_LSX vr6, vr7, vr8, vr9, vr10, vr11, vr12, vr13, \ + vr6, vr7, vr8, vr9 +.LOOP_V8: + fld.d f13, a2, 0 //the 8th load + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_V8_LSX vr6, vr7, vr8, vr9, vr10, vr11, 0 + vssrani.h.w vr11, vr10, 0 + vssrani.bu.h vr11, vr11, 0 + fst.d f11, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_V8 +endfunc + +.macro PUT_HEVC_UNI_W_V8_LASX w + fld.d f6, a2, 0 + fldx.d f7, a2, a3 + fldx.d f8, a2, t0 + add.d a2, a2, t1 + fld.d f9, a2, 0 + fldx.d f10, a2, a3 + fldx.d f11, a2, t0 + fldx.d f12, a2, t1 + add.d a2, a2, t2 + TRANSPOSE8X8B_LSX vr6, vr7, vr8, vr9, vr10, vr11, vr12, vr13, \ + vr6, vr7, vr8, vr9 + xvpermi.q xr6, xr7, 0x02 + xvpermi.q xr8, xr9, 0x02 +.LOOP_V8_LASX_\w: + fld.d f13, a2, 0 // 0 1 2 3 4 5 6 7 the 8th load + add.d a2, a2, a3 + vshuf4i.h vr13, vr13, 0xd8 + vbsrl.v vr14, vr13, 4 + xvpermi.q xr13, xr14, 0x02 //0 1 4 5 * * * * 2 3 6 7 * * * * + xvextrins.b xr6, xr13, 0x70 //begin to insert the 8th load + xvextrins.b xr6, xr13, 0xf1 + xvextrins.b xr8, xr13, 0x72 + xvextrins.b xr8, xr13, 0xf3 + xvdp2.h.bu.b xr20, xr6, xr5 //QPEL_FILTER(src, stride) + xvdp2.h.bu.b xr21, xr8, xr5 + xvbsrl.v xr6, xr6, 1 + xvbsrl.v xr8, xr8, 1 + xvhaddw.d.h xr20 + xvhaddw.d.h xr21 + xvpickev.w xr20, xr21, xr20 + xvpermi.d xr20, xr20, 0xd8 + xvmulwev.w.h xr20, xr20, xr1 //QPEL_FILTER(src, stride) * wx + xvadd.w xr20, xr20, xr2 + xvsra.w xr20, xr20, xr3 + xvadd.w xr10, xr20, xr4 + xvpermi.q xr11, xr10, 0x01 + vssrani.h.w vr11, vr10, 0 + vssrani.bu.h vr11, vr11, 0 + fst.d f11, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_V8_LASX_\w +.endm + +function ff_hevc_put_hevc_qpel_uni_w_v8_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + PUT_HEVC_UNI_W_V8_LASX 8 +endfunc + +.macro PUT_HEVC_QPEL_UNI_W_V16_LSX w + vld vr6, a2, 0 + vldx vr7, a2, a3 + vldx vr8, a2, t0 + add.d a2, a2, t1 + vld vr9, a2, 0 + vldx vr10, a2, a3 + vldx vr11, a2, t0 + vldx vr12, a2, t1 + add.d a2, a2, t2 +.if \w > 8 + vilvh.d vr14, vr14, vr6 + vilvh.d vr15, vr15, vr7 + vilvh.d vr16, vr16, vr8 + vilvh.d vr17, vr17, vr9 + vilvh.d vr18, vr18, vr10 + vilvh.d vr19, vr19, vr11 + vilvh.d vr20, vr20, vr12 +.endif + TRANSPOSE8X8B_LSX vr6, vr7, vr8, vr9, vr10, vr11, vr12, vr13, \ + vr6, vr7, vr8, vr9 +.if \w > 8 + TRANSPOSE8X8B_LSX vr14, vr15, vr16, vr17, vr18, vr19, vr20, vr21, \ + vr14, vr15, vr16, vr17 +.endif +.LOOP_HORI_16_\w: + vld vr13, a2, 0 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_V8_LSX vr6, vr7, vr8, vr9, vr10, vr11, 0 +.if \w > 8 + PUT_HEVC_QPEL_UNI_W_V8_LSX vr14, vr15, vr16, vr17, vr18, vr19, 8 +.endif + vssrani.h.w vr11, vr10, 0 +.if \w > 8 + vssrani.h.w vr19, vr18, 0 + vssrani.bu.h vr19, vr11, 0 +.else + vssrani.bu.h vr11, vr11, 0 +.endif +.if \w == 8 + fst.d f11, a0, 0 +.elseif \w == 12 + fst.d f19, a0, 0 + vstelm.w vr19, a0, 8, 2 +.else + vst vr19, a0, 0 +.endif + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_HORI_16_\w +.endm + +function ff_hevc_put_hevc_qpel_uni_w_v16_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + PUT_HEVC_QPEL_UNI_W_V16_LSX 16 +endfunc + +.macro PUT_HEVC_QPEL_UNI_W_V16_LASX w + vld vr6, a2, 0 + vldx vr7, a2, a3 + vldx vr8, a2, t0 + add.d a2, a2, t1 + vld vr9, a2, 0 + vldx vr10, a2, a3 + vldx vr11, a2, t0 + vldx vr12, a2, t1 + add.d a2, a2, t2 + xvpermi.q xr6, xr10, 0x02 //pack and transpose the 8x16 to 4x32 begin + xvpermi.q xr7, xr11, 0x02 + xvpermi.q xr8, xr12, 0x02 + xvpermi.q xr9, xr13, 0x02 + xvilvl.b xr14, xr7, xr6 //0 2 + xvilvh.b xr15, xr7, xr6 //1 3 + xvilvl.b xr16, xr9, xr8 //0 2 + xvilvh.b xr17, xr9, xr8 //1 3 + xvpermi.d xr14, xr14, 0xd8 + xvpermi.d xr15, xr15, 0xd8 + xvpermi.d xr16, xr16, 0xd8 + xvpermi.d xr17, xr17, 0xd8 + xvilvl.h xr6, xr16, xr14 + xvilvh.h xr7, xr16, xr14 + xvilvl.h xr8, xr17, xr15 + xvilvh.h xr9, xr17, xr15 + xvilvl.w xr14, xr7, xr6 //0 1 4 5 + xvilvh.w xr15, xr7, xr6 //2 3 6 7 + xvilvl.w xr16, xr9, xr8 //8 9 12 13 + xvilvh.w xr17, xr9, xr8 //10 11 14 15 end +.LOOP_HORI_16_LASX_\w: + vld vr13, a2, 0 //the 8th load + add.d a2, a2, a3 + vshuf4i.w vr13, vr13, 0xd8 + vbsrl.v vr12, vr13, 8 + xvpermi.q xr13, xr12, 0x02 + xvextrins.b xr14, xr13, 0x70 //inset the 8th load + xvextrins.b xr14, xr13, 0xf1 + xvextrins.b xr15, xr13, 0x72 + xvextrins.b xr15, xr13, 0xf3 + xvextrins.b xr16, xr13, 0x74 + xvextrins.b xr16, xr13, 0xf5 + xvextrins.b xr17, xr13, 0x76 + xvextrins.b xr17, xr13, 0xf7 + xvdp2.h.bu.b xr6, xr14, xr5 //QPEL_FILTER(src, stride) + xvdp2.h.bu.b xr7, xr15, xr5 + xvdp2.h.bu.b xr8, xr16, xr5 + xvdp2.h.bu.b xr9, xr17, xr5 + xvhaddw.d.h xr6 + xvhaddw.d.h xr7 + xvhaddw.d.h xr8 + xvhaddw.d.h xr9 + xvbsrl.v xr14, xr14, 1 //Back up previous 7 loaded datas, + xvbsrl.v xr15, xr15, 1 //so just need to insert the 8th + xvbsrl.v xr16, xr16, 1 //load in next loop. + xvbsrl.v xr17, xr17, 1 + xvpickev.w xr6, xr7, xr6 //0 1 2 3 4 5 6 7 + xvpickev.w xr7, xr9, xr8 //8 9 10 11 12 13 14 15 + xvmulwev.w.h xr6, xr6, xr1 //QPEL_FILTER(src, stride) * wx + xvmulwev.w.h xr7, xr7, xr1 + xvadd.w xr6, xr6, xr2 + xvadd.w xr7, xr7, xr2 + xvsra.w xr6, xr6, xr3 + xvsra.w xr7, xr7, xr3 + xvadd.w xr6, xr6, xr4 + xvadd.w xr7, xr7, xr4 + xvssrani.h.w xr7, xr6, 0 //0 1 2 3 8 9 10 11 4 5 6 7 12 13 14 15 + xvpermi.q xr6, xr7, 0x01 + vssrani.bu.h vr6, vr7, 0 + vshuf4i.w vr6, vr6, 0xd8 +.if \w == 12 + fst.d f6, a0, 0 + vstelm.w vr6, a0, 8, 2 +.else + vst vr6, a0, 0 +.endif + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_HORI_16_LASX_\w +.endm + +function ff_hevc_put_hevc_qpel_uni_w_v16_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + PUT_HEVC_QPEL_UNI_W_V16_LASX 16 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_v12_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + PUT_HEVC_QPEL_UNI_W_V16_LSX 12 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_v12_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + PUT_HEVC_QPEL_UNI_W_V16_LASX 12 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_v24_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + addi.d t4, a0, 0 //save dst + addi.d t5, a2, 0 //save src + addi.d t6, a4, 0 + PUT_HEVC_QPEL_UNI_W_V16_LSX 24 + addi.d a0, t4, 16 + addi.d a2, t5, 16 + addi.d a4, t6, 0 + PUT_HEVC_QPEL_UNI_W_V16_LSX 8 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_v24_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + addi.d t4, a0, 0 //save dst + addi.d t5, a2, 0 //save src + addi.d t6, a4, 0 + PUT_HEVC_QPEL_UNI_W_V16_LASX 24 + addi.d a0, t4, 16 + addi.d a2, t5, 16 + addi.d a4, t6, 0 + PUT_HEVC_UNI_W_V8_LASX 24 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_v32_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + addi.d t3, zero, 2 + addi.d t4, a0, 0 //save dst + addi.d t5, a2, 0 //save src + addi.d t6, a4, 0 +.LOOP_V32: + PUT_HEVC_QPEL_UNI_W_V16_LSX 32 + addi.d t3, t3, -1 + addi.d a0, t4, 16 + addi.d a2, t5, 16 + addi.d a4, t6, 0 + bnez t3, .LOOP_V32 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_v32_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + addi.d t3, zero, 2 + addi.d t4, a0, 0 //save dst + addi.d t5, a2, 0 //save src + addi.d t6, a4, 0 +.LOOP_V32_LASX: + PUT_HEVC_QPEL_UNI_W_V16_LASX 32 + addi.d t3, t3, -1 + addi.d a0, t4, 16 + addi.d a2, t5, 16 + addi.d a4, t6, 0 + bnez t3, .LOOP_V32_LASX +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_v48_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + addi.d t3, zero, 3 + addi.d t4, a0, 0 //save dst + addi.d t5, a2, 0 //save src + addi.d t6, a4, 0 +.LOOP_V48: + PUT_HEVC_QPEL_UNI_W_V16_LSX 48 + addi.d t3, t3, -1 + addi.d a0, t4, 16 + addi.d t4, t4, 16 + addi.d a2, t5, 16 + addi.d t5, t5, 16 + addi.d a4, t6, 0 + bnez t3, .LOOP_V48 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_v48_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + addi.d t3, zero, 3 + addi.d t4, a0, 0 //save dst + addi.d t5, a2, 0 //save src + addi.d t6, a4, 0 +.LOOP_V48_LASX: + PUT_HEVC_QPEL_UNI_W_V16_LASX 48 + addi.d t3, t3, -1 + addi.d a0, t4, 16 + addi.d t4, t4, 16 + addi.d a2, t5, 16 + addi.d t5, t5, 16 + addi.d a4, t6, 0 + bnez t3, .LOOP_V48_LASX +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_v64_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + addi.d t3, zero, 4 + addi.d t4, a0, 0 //save dst + addi.d t5, a2, 0 //save src + addi.d t6, a4, 0 +.LOOP_V64: + PUT_HEVC_QPEL_UNI_W_V16_LSX 64 + addi.d t3, t3, -1 + addi.d a0, t4, 16 + addi.d t4, t4, 16 + addi.d a2, t5, 16 + addi.d t5, t5, 16 + addi.d a4, t6, 0 + bnez t3, .LOOP_V64 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_v64_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + add.d t2, t1, a3 //stride * 4 + sub.d a2, a2, t1 //src -= stride*3 + addi.d t3, zero, 4 + addi.d t4, a0, 0 //save dst + addi.d t5, a2, 0 //save src + addi.d t6, a4, 0 +.LOOP_V64_LASX: + PUT_HEVC_QPEL_UNI_W_V16_LASX 64 + addi.d t3, t3, -1 + addi.d a0, t4, 16 + addi.d t4, t4, 16 + addi.d a2, t5, 16 + addi.d t5, t5, 16 + addi.d a4, t6, 0 + bnez t3, .LOOP_V64_LASX +endfunc + +.macro PUT_HEVC_QPEL_UNI_W_H8_LSX in0, out0, out1 + vbsrl.v vr7, \in0, 1 + vbsrl.v vr8, \in0, 2 + vbsrl.v vr9, \in0, 3 + vbsrl.v vr10, \in0, 4 + vbsrl.v vr11, \in0, 5 + vbsrl.v vr12, \in0, 6 + vbsrl.v vr13, \in0, 7 + vilvl.d vr6, vr7, \in0 + vilvl.d vr7, vr9, vr8 + vilvl.d vr8, vr11, vr10 + vilvl.d vr9, vr13, vr12 + vdp2.h.bu.b vr10, vr6, vr5 + vdp2.h.bu.b vr11, vr7, vr5 + vdp2.h.bu.b vr12, vr8, vr5 + vdp2.h.bu.b vr13, vr9, vr5 + vhaddw.d.h vr10 + vhaddw.d.h vr11 + vhaddw.d.h vr12 + vhaddw.d.h vr13 + vpickev.w vr10, vr11, vr10 + vpickev.w vr11, vr13, vr12 + vmulwev.w.h vr10, vr10, vr1 + vmulwev.w.h vr11, vr11, vr1 + vadd.w vr10, vr10, vr2 + vadd.w vr11, vr11, vr2 + vsra.w vr10, vr10, vr3 + vsra.w vr11, vr11, vr3 + vadd.w \out0, vr10, vr4 + vadd.w \out1, vr11, vr4 +.endm + +.macro PUT_HEVC_QPEL_UNI_W_H8_LASX in0, out0 + xvbsrl.v xr7, \in0, 4 + xvpermi.q xr7, \in0, 0x20 + xvbsrl.v xr8, xr7, 1 + xvbsrl.v xr9, xr7, 2 + xvbsrl.v xr10, xr7, 3 + xvpackev.d xr7, xr8, xr7 + xvpackev.d xr8, xr10, xr9 + xvdp2.h.bu.b xr10, xr7, xr5 + xvdp2.h.bu.b xr11, xr8, xr5 + xvhaddw.d.h xr10 + xvhaddw.d.h xr11 + xvpickev.w xr10, xr11, xr10 + xvmulwev.w.h xr10, xr10, xr1 + xvadd.w xr10, xr10, xr2 + xvsra.w xr10, xr10, xr3 + xvadd.w \out0, xr10, xr4 +.endm + +.macro PUT_HEVC_QPEL_UNI_W_H16_LASX in0, out0 + xvpermi.d xr6, \in0, 0x94 + xvbsrl.v xr7, xr6, 1 + xvbsrl.v xr8, xr6, 2 + xvbsrl.v xr9, xr6, 3 + xvbsrl.v xr10, xr6, 4 + xvbsrl.v xr11, xr6, 5 + xvbsrl.v xr12, xr6, 6 + xvbsrl.v xr13, xr6, 7 + xvpackev.d xr6, xr7, xr6 + xvpackev.d xr7, xr9, xr8 + xvpackev.d xr8, xr11, xr10 + xvpackev.d xr9, xr13, xr12 + xvdp2.h.bu.b xr10, xr6, xr5 + xvdp2.h.bu.b xr11, xr7, xr5 + xvdp2.h.bu.b xr12, xr8, xr5 + xvdp2.h.bu.b xr13, xr9, xr5 + xvhaddw.d.h xr10 + xvhaddw.d.h xr11 + xvhaddw.d.h xr12 + xvhaddw.d.h xr13 + xvpickev.w xr10, xr11, xr10 + xvpickev.w xr11, xr13, xr12 + xvmulwev.w.h xr10, xr10, xr1 + xvmulwev.w.h xr11, xr11, xr1 + xvadd.w xr10, xr10, xr2 + xvadd.w xr11, xr11, xr2 + xvsra.w xr10, xr10, xr3 + xvsra.w xr11, xr11, xr3 + xvadd.w xr10, xr10, xr4 + xvadd.w xr11, xr11, xr4 + xvssrani.h.w xr11, xr10, 0 + xvpermi.q \out0, xr11, 0x01 + xvssrani.bu.h \out0, xr11, 0 +.endm + +/* + * void FUNC(put_hevc_qpel_uni_w_h)(uint8_t *_dst, ptrdiff_t _dststride, + * const uint8_t *_src, ptrdiff_t _srcstride, + * int height, int denom, int wx, int ox, + * intptr_t mx, intptr_t my, int width) + */ +function ff_hevc_put_hevc_qpel_uni_w_h4_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + addi.d a2, a2, -3 //src -= 3 +.LOOP_H4: + vld vr18, a2, 0 + vldx vr19, a2, a3 + alsl.d a2, a3, a2, 1 + vbsrl.v vr6, vr18, 1 + vbsrl.v vr7, vr18, 2 + vbsrl.v vr8, vr18, 3 + vbsrl.v vr9, vr19, 1 + vbsrl.v vr10, vr19, 2 + vbsrl.v vr11, vr19, 3 + vilvl.d vr6, vr6, vr18 + vilvl.d vr7, vr8, vr7 + vilvl.d vr8, vr9, vr19 + vilvl.d vr9, vr11, vr10 + vdp2.h.bu.b vr10, vr6, vr5 + vdp2.h.bu.b vr11, vr7, vr5 + vdp2.h.bu.b vr12, vr8, vr5 + vdp2.h.bu.b vr13, vr9, vr5 + vhaddw.d.h vr10 + vhaddw.d.h vr11 + vhaddw.d.h vr12 + vhaddw.d.h vr13 + vpickev.w vr10, vr11, vr10 + vpickev.w vr11, vr13, vr12 + vmulwev.w.h vr10, vr10, vr1 + vmulwev.w.h vr11, vr11, vr1 + vadd.w vr10, vr10, vr2 + vadd.w vr11, vr11, vr2 + vsra.w vr10, vr10, vr3 + vsra.w vr11, vr11, vr3 + vadd.w vr10, vr10, vr4 + vadd.w vr11, vr11, vr4 + vssrani.h.w vr11, vr10, 0 + vssrani.bu.h vr11, vr11, 0 + fst.s f11, a0, 0 + vbsrl.v vr11, vr11, 4 + fstx.s f11, a0, a1 + alsl.d a0, a1, a0, 1 + addi.d a4, a4, -2 + bnez a4, .LOOP_H4 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h4_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + addi.d a2, a2, -3 //src -= 3 +.LOOP_H4_LASX: + vld vr18, a2, 0 + vldx vr19, a2, a3 + alsl.d a2, a3, a2, 1 + xvpermi.q xr18, xr19, 0x02 + xvbsrl.v xr6, xr18, 1 + xvbsrl.v xr7, xr18, 2 + xvbsrl.v xr8, xr18, 3 + xvpackev.d xr6, xr6, xr18 + xvpackev.d xr7, xr8, xr7 + xvdp2.h.bu.b xr10, xr6, xr5 + xvdp2.h.bu.b xr11, xr7, xr5 + xvhaddw.d.h xr10 + xvhaddw.d.h xr11 + xvpickev.w xr10, xr11, xr10 + xvmulwev.w.h xr10, xr10, xr1 + xvadd.w xr10, xr10, xr2 + xvsra.w xr10, xr10, xr3 + xvadd.w xr10, xr10, xr4 + xvpermi.q xr11, xr10, 0x01 + vssrani.h.w vr11, vr10, 0 + vssrani.bu.h vr11, vr11, 0 + fst.s f11, a0, 0 + vbsrl.v vr11, vr11, 4 + fstx.s f11, a0, a1 + alsl.d a0, a1, a0, 1 + addi.d a4, a4, -2 + bnez a4, .LOOP_H4_LASX +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h6_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + addi.d a2, a2, -3 //src -= 3 +.LOOP_H6: + vld vr6, a2, 0 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr6, vr10, vr11 + vssrani.h.w vr11, vr10, 0 + vssrani.bu.h vr11, vr11, 0 + fst.s f11, a0, 0 + vstelm.h vr11, a0, 4, 2 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H6 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h6_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + addi.d a2, a2, -3 //src -= 3 +.LOOP_H6_LASX: + vld vr6, a2, 0 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H8_LASX xr6, xr10 + xvpermi.q xr11, xr10, 0x01 + vssrani.h.w vr11, vr10, 0 + vssrani.bu.h vr11, vr11, 0 + fst.s f11, a0, 0 + vstelm.h vr11, a0, 4, 2 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H6_LASX +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h8_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + addi.d a2, a2, -3 //src -= 3 +.LOOP_H8: + vld vr6, a2, 0 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr6, vr10, vr11 + vssrani.h.w vr11, vr10, 0 + vssrani.bu.h vr11, vr11, 0 + fst.d f11, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H8 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h8_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + addi.d a2, a2, -3 //src -= 3 +.LOOP_H8_LASX: + vld vr6, a2, 0 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H8_LASX xr6, xr10 + xvpermi.q xr11, xr10, 0x01 + vssrani.h.w vr11, vr10, 0 + vssrani.bu.h vr11, vr11, 0 + fst.d f11, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H8_LASX +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h12_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + addi.d a2, a2, -3 //src -= 3 +.LOOP_H12: + vld vr6, a2, 0 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr6, vr14, vr15 + vld vr6, a2, 8 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr6, vr16, vr17 + add.d a2, a2, a3 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + fst.d f17, a0, 0 + vbsrl.v vr17, vr17, 8 + fst.s f17, a0, 8 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H12 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h12_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + addi.d a2, a2, -3 //src -= 3 +.LOOP_H12_LASX: + xvld xr6, a2, 0 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H16_LASX xr6, xr14 + fst.d f14, a0, 0 + vstelm.w vr14, a0, 8, 2 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H12_LASX +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h16_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + addi.d a2, a2, -3 //src -= 3 +.LOOP_H16: + vld vr6, a2, 0 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr6, vr14, vr15 + vld vr6, a2, 8 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr6, vr16, vr17 + add.d a2, a2, a3 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + vst vr17, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H16 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h16_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + addi.d a2, a2, -3 //src -= 3 +.LOOP_H16_LASX: + xvld xr6, a2, 0 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H16_LASX xr6, xr10 + vst vr10, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H16_LASX +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h24_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + addi.d a2, a2, -3 //src -= 3 +.LOOP_H24: + vld vr18, a2, 0 + vld vr19, a2, 16 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr18, vr14, vr15 + vshuf4i.d vr18, vr19, 0x09 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr18, vr16, vr17 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + vst vr17, a0, 0 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr19, vr14, vr15 + vssrani.h.w vr15, vr14, 0 + vssrani.bu.h vr15, vr15, 0 + fst.d f15, a0, 16 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H24 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h24_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + addi.d a2, a2, -3 //src -= 3 +.LOOP_H24_LASX: + xvld xr18, a2, 0 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H16_LASX xr18, xr20 + xvpermi.q xr19, xr18, 0x01 + vst vr20, a0, 0 + PUT_HEVC_QPEL_UNI_W_H8_LASX xr19, xr20 + xvpermi.q xr21, xr20, 0x01 + vssrani.h.w vr21, vr20, 0 + vssrani.bu.h vr21, vr21, 0 + fst.d f21, a0, 16 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H24_LASX +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h32_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + addi.d a2, a2, -3 //src -= 3 +.LOOP_H32: + vld vr18, a2, 0 + vld vr19, a2, 16 + vld vr20, a2, 32 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr18, vr14, vr15 + vshuf4i.d vr18, vr19, 0x09 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr18, vr16, vr17 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + vst vr17, a0, 0 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr19, vr14, vr15 + vshuf4i.d vr19, vr20, 0x09 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr19, vr16, vr17 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + vst vr17, a0, 16 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H32 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h32_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + addi.d a2, a2, -3 //src -= 3 +.LOOP_H32_LASX: + xvld xr18, a2, 0 + xvld xr19, a2, 16 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H16_LASX xr18, xr20 + PUT_HEVC_QPEL_UNI_W_H16_LASX xr19, xr21 + xvpermi.q xr20, xr21, 0x02 + xvst xr20, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H32_LASX +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h48_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + addi.d a2, a2, -3 //src -= 3 +.LOOP_H48: + vld vr18, a2, 0 + vld vr19, a2, 16 + vld vr20, a2, 32 + vld vr21, a2, 48 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr18, vr14, vr15 + vshuf4i.d vr18, vr19, 0x09 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr18, vr16, vr17 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + vst vr17, a0, 0 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr19, vr14, vr15 + vshuf4i.d vr19, vr20, 0x09 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr19, vr16, vr17 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + vst vr17, a0, 16 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr20, vr14, vr15 + vshuf4i.d vr20, vr21, 0x09 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr20, vr16, vr17 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + vst vr17, a0, 32 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H48 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h48_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + addi.d a2, a2, -3 //src -= 3 +.LOOP_H48_LASX: + xvld xr18, a2, 0 + xvld xr19, a2, 32 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H16_LASX xr18, xr20 + xvpermi.q xr18, xr19, 0x03 + PUT_HEVC_QPEL_UNI_W_H16_LASX xr18, xr21 + xvpermi.q xr20, xr21, 0x02 + xvst xr20, a0, 0 + PUT_HEVC_QPEL_UNI_W_H16_LASX xr19, xr20 + vst vr20, a0, 32 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H48_LASX +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h64_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + addi.d a2, a2, -3 //src -= 3 +.LOOP_H64: + vld vr18, a2, 0 + vld vr19, a2, 16 + vld vr20, a2, 32 + vld vr21, a2, 48 + vld vr22, a2, 64 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr18, vr14, vr15 + vshuf4i.d vr18, vr19, 0x09 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr18, vr16, vr17 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + vst vr17, a0, 0 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr19, vr14, vr15 + vshuf4i.d vr19, vr20, 0x09 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr19, vr16, vr17 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + vst vr17, a0, 16 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr20, vr14, vr15 + vshuf4i.d vr20, vr21, 0x09 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr20, vr16, vr17 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + vst vr17, a0, 32 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr21, vr14, vr15 + vshuf4i.d vr21, vr22, 0x09 + PUT_HEVC_QPEL_UNI_W_H8_LSX vr21, vr16, vr17 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + vst vr17, a0, 48 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H64 +endfunc + +function ff_hevc_put_hevc_qpel_uni_w_h64_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + xvreplve0.q xr5, xr5 + addi.d a2, a2, -3 //src -= 3 +.LOOP_H64_LASX: + xvld xr18, a2, 0 + xvld xr19, a2, 32 + xvld xr20, a2, 64 + add.d a2, a2, a3 + PUT_HEVC_QPEL_UNI_W_H16_LASX xr18, xr21 + xvpermi.q xr18, xr19, 0x03 + PUT_HEVC_QPEL_UNI_W_H16_LASX xr18, xr22 + xvpermi.q xr21, xr22, 0x02 + xvst xr21, a0, 0 + PUT_HEVC_QPEL_UNI_W_H16_LASX xr19, xr21 + xvpermi.q xr19, xr20, 0x03 + PUT_HEVC_QPEL_UNI_W_H16_LASX xr19, xr22 + xvpermi.q xr21, xr22, 0x02 + xvst xr21, a0, 32 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_H64_LASX +endfunc + +const shufb + .byte 0,1,2,3, 1,2,3,4 ,2,3,4,5, 3,4,5,6 //mask for epel_uni_w(128-bit) + .byte 4,5,6,7, 5,6,7,8 ,6,7,8,9, 7,8,9,10 //mask for epel_uni_w(256-bit) + .byte 0,1,2,3, 4,5,6,7 ,1,2,3,4, 5,6,7,8 //mask for qpel_uni_h4 + .byte 0,1,1,2, 2,3,3,4 ,4,5,5,6, 6,7,7,8 //mask for qpel_uni_h/v6/8... + .byte 0,1,2,3, 1,2,3,4 ,2,3,4,5, 3,4,5,6, 4,5,6,7, 5,6,7,8, 6,7,8,9, 7,8,9,10 //epel_uni_w_h16/24/32/48/64 + .byte 0,1,1,2, 2,3,3,4 ,4,5,5,6, 6,7,7,8, 0,1,1,2, 2,3,3,4 ,4,5,5,6, 6,7,7,8 //mask for bi_epel_h16/24/32/48/64 +endconst + +.macro PUT_HEVC_EPEL_UNI_W_HV4_LSX w + fld.d f7, a2, 0 // start to load src + fldx.d f8, a2, a3 + alsl.d a2, a3, a2, 1 + fld.d f9, a2, 0 + vshuf.b vr7, vr7, vr7, vr0 // 0123 1234 2345 3456 + vshuf.b vr8, vr8, vr8, vr0 + vshuf.b vr9, vr9, vr9, vr0 + vdp2.h.bu.b vr10, vr7, vr5 // EPEL_FILTER(src, 1) + vdp2.h.bu.b vr11, vr8, vr5 + vdp2.h.bu.b vr12, vr9, vr5 + vhaddw.w.h vr10, vr10, vr10 // tmp[0/1/2/3] + vhaddw.w.h vr11, vr11, vr11 // vr10,vr11,vr12 corresponding to EPEL_EXTRA + vhaddw.w.h vr12, vr12, vr12 +.LOOP_HV4_\w: + add.d a2, a2, a3 + fld.d f14, a2, 0 // height loop begin + vshuf.b vr14, vr14, vr14, vr0 + vdp2.h.bu.b vr13, vr14, vr5 + vhaddw.w.h vr13, vr13, vr13 + vmul.w vr14, vr10, vr16 // EPEL_FILTER(tmp, MAX_PB_SIZE) + vmadd.w vr14, vr11, vr17 + vmadd.w vr14, vr12, vr18 + vmadd.w vr14, vr13, vr19 + vaddi.wu vr10, vr11, 0 //back up previous value + vaddi.wu vr11, vr12, 0 + vaddi.wu vr12, vr13, 0 + vsrai.w vr14, vr14, 6 // >> 6 + vmul.w vr14, vr14, vr1 // * wx + vadd.w vr14, vr14, vr2 // + offset + vsra.w vr14, vr14, vr3 // >> shift + vadd.w vr14, vr14, vr4 // + ox + vssrani.h.w vr14, vr14, 0 + vssrani.bu.h vr14, vr14, 0 // clip + fst.s f14, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_HV4_\w +.endm + +/* + * void FUNC(put_hevc_epel_uni_w_hv)(uint8_t *_dst, ptrdiff_t _dststride, + * const uint8_t *_src, ptrdiff_t _srcstride, + * int height, int denom, int wx, int ox, + * intptr_t mx, intptr_t my, int width) + */ +function ff_hevc_put_hevc_epel_uni_w_hv4_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + vreplvei.w vr5, vr5, 0 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + vreplvei.w vr16, vr6, 0 + vreplvei.w vr17, vr6, 1 + vreplvei.w vr18, vr6, 2 + vreplvei.w vr19, vr6, 3 + la.local t1, shufb + vld vr0, t1, 0 + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + PUT_HEVC_EPEL_UNI_W_HV4_LSX 4 +endfunc + +.macro PUT_HEVC_EPEL_UNI_W_HV8_LSX w + vld vr7, a2, 0 // start to load src + vldx vr8, a2, a3 + alsl.d a2, a3, a2, 1 + vld vr9, a2, 0 + vshuf.b vr10, vr7, vr7, vr0 // 0123 1234 2345 3456 + vshuf.b vr11, vr8, vr8, vr0 + vshuf.b vr12, vr9, vr9, vr0 + vshuf.b vr7, vr7, vr7, vr22// 4567 5678 6789 78910 + vshuf.b vr8, vr8, vr8, vr22 + vshuf.b vr9, vr9, vr9, vr22 + vdp2.h.bu.b vr13, vr10, vr5 // EPEL_FILTER(src, 1) + vdp2.h.bu.b vr14, vr11, vr5 + vdp2.h.bu.b vr15, vr12, vr5 + vdp2.h.bu.b vr23, vr7, vr5 + vdp2.h.bu.b vr20, vr8, vr5 + vdp2.h.bu.b vr21, vr9, vr5 + vhaddw.w.h vr7, vr13, vr13 + vhaddw.w.h vr8, vr14, vr14 + vhaddw.w.h vr9, vr15, vr15 + vhaddw.w.h vr10, vr23, vr23 + vhaddw.w.h vr11, vr20, vr20 + vhaddw.w.h vr12, vr21, vr21 +.LOOP_HV8_HORI_\w: + add.d a2, a2, a3 + vld vr15, a2, 0 + vshuf.b vr23, vr15, vr15, vr0 + vshuf.b vr15, vr15, vr15, vr22 + vdp2.h.bu.b vr13, vr23, vr5 + vdp2.h.bu.b vr14, vr15, vr5 + vhaddw.w.h vr13, vr13, vr13 //789--13 + vhaddw.w.h vr14, vr14, vr14 //101112--14 + vmul.w vr15, vr7, vr16 //EPEL_FILTER(tmp, MAX_PB_SIZE) + vmadd.w vr15, vr8, vr17 + vmadd.w vr15, vr9, vr18 + vmadd.w vr15, vr13, vr19 + vmul.w vr20, vr10, vr16 + vmadd.w vr20, vr11, vr17 + vmadd.w vr20, vr12, vr18 + vmadd.w vr20, vr14, vr19 + vaddi.wu vr7, vr8, 0 //back up previous value + vaddi.wu vr8, vr9, 0 + vaddi.wu vr9, vr13, 0 + vaddi.wu vr10, vr11, 0 + vaddi.wu vr11, vr12, 0 + vaddi.wu vr12, vr14, 0 + vsrai.w vr15, vr15, 6 // >> 6 + vsrai.w vr20, vr20, 6 + vmul.w vr15, vr15, vr1 // * wx + vmul.w vr20, vr20, vr1 + vadd.w vr15, vr15, vr2 // + offset + vadd.w vr20, vr20, vr2 + vsra.w vr15, vr15, vr3 // >> shift + vsra.w vr20, vr20, vr3 + vadd.w vr15, vr15, vr4 // + ox + vadd.w vr20, vr20, vr4 + vssrani.h.w vr20, vr15, 0 + vssrani.bu.h vr20, vr20, 0 +.if \w > 6 + fst.d f20, a0, 0 +.else + fst.s f20, a0, 0 + vstelm.h vr20, a0, 4, 2 +.endif + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_HV8_HORI_\w +.endm + +.macro PUT_HEVC_EPEL_UNI_W_HV8_LASX w + vld vr7, a2, 0 // start to load src + vldx vr8, a2, a3 + alsl.d a2, a3, a2, 1 + vld vr9, a2, 0 + xvreplve0.q xr7, xr7 + xvreplve0.q xr8, xr8 + xvreplve0.q xr9, xr9 + xvshuf.b xr10, xr7, xr7, xr0 // 0123 1234 2345 3456 + xvshuf.b xr11, xr8, xr8, xr0 + xvshuf.b xr12, xr9, xr9, xr0 + xvdp2.h.bu.b xr13, xr10, xr5 // EPEL_FILTER(src, 1) + xvdp2.h.bu.b xr14, xr11, xr5 + xvdp2.h.bu.b xr15, xr12, xr5 + xvhaddw.w.h xr7, xr13, xr13 + xvhaddw.w.h xr8, xr14, xr14 + xvhaddw.w.h xr9, xr15, xr15 +.LOOP_HV8_HORI_LASX_\w: + add.d a2, a2, a3 + vld vr15, a2, 0 + xvreplve0.q xr15, xr15 + xvshuf.b xr23, xr15, xr15, xr0 + xvdp2.h.bu.b xr10, xr23, xr5 + xvhaddw.w.h xr10, xr10, xr10 + xvmul.w xr15, xr7, xr16 //EPEL_FILTER(tmp, MAX_PB_SIZE) + xvmadd.w xr15, xr8, xr17 + xvmadd.w xr15, xr9, xr18 + xvmadd.w xr15, xr10, xr19 + xvaddi.wu xr7, xr8, 0 //back up previous value + xvaddi.wu xr8, xr9, 0 + xvaddi.wu xr9, xr10, 0 + xvsrai.w xr15, xr15, 6 // >> 6 + xvmul.w xr15, xr15, xr1 // * wx + xvadd.w xr15, xr15, xr2 // + offset + xvsra.w xr15, xr15, xr3 // >> shift + xvadd.w xr15, xr15, xr4 // + ox + xvpermi.q xr20, xr15, 0x01 + vssrani.h.w vr20, vr15, 0 + vssrani.bu.h vr20, vr20, 0 +.if \w > 6 + fst.d f20, a0, 0 +.else + fst.s f20, a0, 0 + vstelm.h vr20, a0, 4, 2 +.endif + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_HV8_HORI_LASX_\w +.endm + +.macro PUT_HEVC_EPEL_UNI_W_HV16_LASX w + xvld xr7, a2, 0 // start to load src + xvldx xr8, a2, a3 + alsl.d a2, a3, a2, 1 + xvld xr9, a2, 0 + xvpermi.d xr10, xr7, 0x09 //8..18 + xvpermi.d xr11, xr8, 0x09 + xvpermi.d xr12, xr9, 0x09 + xvreplve0.q xr7, xr7 + xvreplve0.q xr8, xr8 + xvreplve0.q xr9, xr9 + xvshuf.b xr13, xr7, xr7, xr0 // 0123 1234 2345 3456 + xvshuf.b xr14, xr8, xr8, xr0 + xvshuf.b xr15, xr9, xr9, xr0 + xvdp2.h.bu.b xr20, xr13, xr5 // EPEL_FILTER(src, 1) + xvdp2.h.bu.b xr21, xr14, xr5 + xvdp2.h.bu.b xr22, xr15, xr5 + xvhaddw.w.h xr7, xr20, xr20 + xvhaddw.w.h xr8, xr21, xr21 + xvhaddw.w.h xr9, xr22, xr22 + xvreplve0.q xr10, xr10 + xvreplve0.q xr11, xr11 + xvreplve0.q xr12, xr12 + xvshuf.b xr13, xr10, xr10, xr0 + xvshuf.b xr14, xr11, xr11, xr0 + xvshuf.b xr15, xr12, xr12, xr0 + xvdp2.h.bu.b xr20, xr13, xr5 + xvdp2.h.bu.b xr21, xr14, xr5 + xvdp2.h.bu.b xr22, xr15, xr5 + xvhaddw.w.h xr10, xr20, xr20 + xvhaddw.w.h xr11, xr21, xr21 + xvhaddw.w.h xr12, xr22, xr22 +.LOOP_HV16_HORI_LASX_\w: + add.d a2, a2, a3 + xvld xr15, a2, 0 + xvpermi.d xr20, xr15, 0x09 //8...18 + xvreplve0.q xr15, xr15 + xvreplve0.q xr20, xr20 + xvshuf.b xr21, xr15, xr15, xr0 + xvshuf.b xr22, xr20, xr20, xr0 + xvdp2.h.bu.b xr13, xr21, xr5 + xvdp2.h.bu.b xr14, xr22, xr5 + xvhaddw.w.h xr13, xr13, xr13 + xvhaddw.w.h xr14, xr14, xr14 + xvmul.w xr15, xr7, xr16 //EPEL_FILTER(tmp, MAX_PB_SIZE) + xvmadd.w xr15, xr8, xr17 + xvmadd.w xr15, xr9, xr18 + xvmadd.w xr15, xr13, xr19 + xvmul.w xr20, xr10, xr16 + xvmadd.w xr20, xr11, xr17 + xvmadd.w xr20, xr12, xr18 + xvmadd.w xr20, xr14, xr19 + xvaddi.wu xr7, xr8, 0 //back up previous value + xvaddi.wu xr8, xr9, 0 + xvaddi.wu xr9, xr13, 0 + xvaddi.wu xr10, xr11, 0 + xvaddi.wu xr11, xr12, 0 + xvaddi.wu xr12, xr14, 0 + xvsrai.w xr15, xr15, 6 // >> 6 + xvsrai.w xr20, xr20, 6 // >> 6 + xvmul.w xr15, xr15, xr1 // * wx + xvmul.w xr20, xr20, xr1 // * wx + xvadd.w xr15, xr15, xr2 // + offset + xvadd.w xr20, xr20, xr2 // + offset + xvsra.w xr15, xr15, xr3 // >> shift + xvsra.w xr20, xr20, xr3 // >> shift + xvadd.w xr15, xr15, xr4 // + ox + xvadd.w xr20, xr20, xr4 // + ox + xvssrani.h.w xr20, xr15, 0 + xvpermi.q xr21, xr20, 0x01 + vssrani.bu.h vr21, vr20, 0 + vpermi.w vr21, vr21, 0xd8 +.if \w < 16 + fst.d f21, a0, 0 + vstelm.w vr21, a0, 8, 2 +.else + vst vr21, a0, 0 +.endif + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_HV16_HORI_LASX_\w +.endm + +function ff_hevc_put_hevc_epel_uni_w_hv6_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + vreplvei.w vr5, vr5, 0 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + vreplvei.w vr16, vr6, 0 + vreplvei.w vr17, vr6, 1 + vreplvei.w vr18, vr6, 2 + vreplvei.w vr19, vr6, 3 + la.local t1, shufb + vld vr0, t1, 0 + vaddi.bu vr22, vr0, 4 // update shufb to get high part + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + PUT_HEVC_EPEL_UNI_W_HV8_LSX 6 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv6_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + xvreplve0.w xr5, xr5 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + xvreplve0.q xr6, xr6 + xvrepl128vei.w xr16, xr6, 0 + xvrepl128vei.w xr17, xr6, 1 + xvrepl128vei.w xr18, xr6, 2 + xvrepl128vei.w xr19, xr6, 3 + la.local t1, shufb + xvld xr0, t1, 0 + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + PUT_HEVC_EPEL_UNI_W_HV8_LASX 6 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv8_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + vreplvei.w vr5, vr5, 0 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + vreplvei.w vr16, vr6, 0 + vreplvei.w vr17, vr6, 1 + vreplvei.w vr18, vr6, 2 + vreplvei.w vr19, vr6, 3 + la.local t1, shufb + vld vr0, t1, 0 + vaddi.bu vr22, vr0, 4 // update shufb to get high part + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + PUT_HEVC_EPEL_UNI_W_HV8_LSX 8 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv8_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + xvreplve0.w xr5, xr5 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + xvreplve0.q xr6, xr6 + xvrepl128vei.w xr16, xr6, 0 + xvrepl128vei.w xr17, xr6, 1 + xvrepl128vei.w xr18, xr6, 2 + xvrepl128vei.w xr19, xr6, 3 + la.local t1, shufb + xvld xr0, t1, 0 + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + PUT_HEVC_EPEL_UNI_W_HV8_LASX 8 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv12_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + vreplvei.w vr5, vr5, 0 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + vreplvei.w vr16, vr6, 0 + vreplvei.w vr17, vr6, 1 + vreplvei.w vr18, vr6, 2 + vreplvei.w vr19, vr6, 3 + la.local t1, shufb + vld vr0, t1, 0 + vaddi.bu vr22, vr0, 4 // update shufb to get high part + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + PUT_HEVC_EPEL_UNI_W_HV8_LSX 12 + addi.d a0, t2, 8 + addi.d a2, t3, 8 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_HV4_LSX 12 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv12_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + xvreplve0.w xr5, xr5 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + xvreplve0.q xr6, xr6 + xvrepl128vei.w xr16, xr6, 0 + xvrepl128vei.w xr17, xr6, 1 + xvrepl128vei.w xr18, xr6, 2 + xvrepl128vei.w xr19, xr6, 3 + la.local t1, shufb + xvld xr0, t1, 0 + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + PUT_HEVC_EPEL_UNI_W_HV16_LASX 12 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv16_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + vreplvei.w vr5, vr5, 0 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + vreplvei.w vr16, vr6, 0 + vreplvei.w vr17, vr6, 1 + vreplvei.w vr18, vr6, 2 + vreplvei.w vr19, vr6, 3 + la.local t1, shufb + vld vr0, t1, 0 + vaddi.bu vr22, vr0, 4 // update shufb to get high part + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + addi.d t5, zero, 2 +.LOOP_HV16: + PUT_HEVC_EPEL_UNI_W_HV8_LSX 16 + addi.d a0, t2, 8 + addi.d a2, t3, 8 + addi.d a4, t4, 0 + addi.d t5, t5, -1 + bnez t5, .LOOP_HV16 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv16_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + xvreplve0.w xr5, xr5 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + xvreplve0.q xr6, xr6 + xvrepl128vei.w xr16, xr6, 0 + xvrepl128vei.w xr17, xr6, 1 + xvrepl128vei.w xr18, xr6, 2 + xvrepl128vei.w xr19, xr6, 3 + la.local t1, shufb + xvld xr0, t1, 0 + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + PUT_HEVC_EPEL_UNI_W_HV16_LASX 16 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv24_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + vreplvei.w vr5, vr5, 0 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + vreplvei.w vr16, vr6, 0 + vreplvei.w vr17, vr6, 1 + vreplvei.w vr18, vr6, 2 + vreplvei.w vr19, vr6, 3 + la.local t1, shufb + vld vr0, t1, 0 + vaddi.bu vr22, vr0, 4 // update shufb to get high part + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + addi.d t5, zero, 3 +.LOOP_HV24: + PUT_HEVC_EPEL_UNI_W_HV8_LSX 24 + addi.d a0, t2, 8 + addi.d t2, t2, 8 + addi.d a2, t3, 8 + addi.d t3, t3, 8 + addi.d a4, t4, 0 + addi.d t5, t5, -1 + bnez t5, .LOOP_HV24 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv24_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + xvreplve0.w xr5, xr5 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + xvreplve0.q xr6, xr6 + xvrepl128vei.w xr16, xr6, 0 + xvrepl128vei.w xr17, xr6, 1 + xvrepl128vei.w xr18, xr6, 2 + xvrepl128vei.w xr19, xr6, 3 + la.local t1, shufb + xvld xr0, t1, 0 + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + PUT_HEVC_EPEL_UNI_W_HV16_LASX 24 + addi.d a0, t2, 16 + addi.d a2, t3, 16 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_HV8_LASX 24 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv32_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + vreplvei.w vr5, vr5, 0 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + vreplvei.w vr16, vr6, 0 + vreplvei.w vr17, vr6, 1 + vreplvei.w vr18, vr6, 2 + vreplvei.w vr19, vr6, 3 + la.local t1, shufb + vld vr0, t1, 0 + vaddi.bu vr22, vr0, 4 // update shufb to get high part + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + addi.d t5, zero, 4 +.LOOP_HV32: + PUT_HEVC_EPEL_UNI_W_HV8_LSX 32 + addi.d a0, t2, 8 + addi.d t2, t2, 8 + addi.d a2, t3, 8 + addi.d t3, t3, 8 + addi.d a4, t4, 0 + addi.d t5, t5, -1 + bnez t5, .LOOP_HV32 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv32_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + xvreplve0.w xr5, xr5 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + xvreplve0.q xr6, xr6 + xvrepl128vei.w xr16, xr6, 0 + xvrepl128vei.w xr17, xr6, 1 + xvrepl128vei.w xr18, xr6, 2 + xvrepl128vei.w xr19, xr6, 3 + la.local t1, shufb + xvld xr0, t1, 0 + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + addi.d t5, zero, 2 +.LOOP_HV32_LASX: + PUT_HEVC_EPEL_UNI_W_HV16_LASX 32 + addi.d a0, t2, 16 + addi.d t2, t2, 16 + addi.d a2, t3, 16 + addi.d t3, t3, 16 + addi.d a4, t4, 0 + addi.d t5, t5, -1 + bnez t5, .LOOP_HV32_LASX +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv48_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + vreplvei.w vr5, vr5, 0 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + vreplvei.w vr16, vr6, 0 + vreplvei.w vr17, vr6, 1 + vreplvei.w vr18, vr6, 2 + vreplvei.w vr19, vr6, 3 + la.local t1, shufb + vld vr0, t1, 0 + vaddi.bu vr22, vr0, 4 // update shufb to get high part + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + addi.d t5, zero, 6 +.LOOP_HV48: + PUT_HEVC_EPEL_UNI_W_HV8_LSX 48 + addi.d a0, t2, 8 + addi.d t2, t2, 8 + addi.d a2, t3, 8 + addi.d t3, t3, 8 + addi.d a4, t4, 0 + addi.d t5, t5, -1 + bnez t5, .LOOP_HV48 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv48_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + xvreplve0.w xr5, xr5 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + xvreplve0.q xr6, xr6 + xvrepl128vei.w xr16, xr6, 0 + xvrepl128vei.w xr17, xr6, 1 + xvrepl128vei.w xr18, xr6, 2 + xvrepl128vei.w xr19, xr6, 3 + la.local t1, shufb + xvld xr0, t1, 0 + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + addi.d t5, zero, 3 +.LOOP_HV48_LASX: + PUT_HEVC_EPEL_UNI_W_HV16_LASX 48 + addi.d a0, t2, 16 + addi.d t2, t2, 16 + addi.d a2, t3, 16 + addi.d t3, t3, 16 + addi.d a4, t4, 0 + addi.d t5, t5, -1 + bnez t5, .LOOP_HV48_LASX +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv64_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + vreplvei.w vr5, vr5, 0 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + vreplvei.w vr16, vr6, 0 + vreplvei.w vr17, vr6, 1 + vreplvei.w vr18, vr6, 2 + vreplvei.w vr19, vr6, 3 + la.local t1, shufb + vld vr0, t1, 0 + vaddi.bu vr22, vr0, 4 // update shufb to get high part + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + addi.d t5, zero, 8 +.LOOP_HV64: + PUT_HEVC_EPEL_UNI_W_HV8_LSX 64 + addi.d a0, t2, 8 + addi.d t2, t2, 8 + addi.d a2, t3, 8 + addi.d t3, t3, 8 + addi.d a4, t4, 0 + addi.d t5, t5, -1 + bnez t5, .LOOP_HV64 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_hv64_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 // mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr5, t1, t0 // ff_hevc_epel_filters[mx]; + xvreplve0.w xr5, xr5 + ld.d t0, sp, 8 // my + slli.w t0, t0, 2 + vldx vr6, t1, t0 // ff_hevc_epel_filters[my]; + vsllwil.h.b vr6, vr6, 0 + vsllwil.w.h vr6, vr6, 0 + xvreplve0.q xr6, xr6 + xvrepl128vei.w xr16, xr6, 0 + xvrepl128vei.w xr17, xr6, 1 + xvrepl128vei.w xr18, xr6, 2 + xvrepl128vei.w xr19, xr6, 3 + la.local t1, shufb + xvld xr0, t1, 0 + sub.d a2, a2, a3 // src -= srcstride + addi.d a2, a2, -1 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + addi.d t5, zero, 4 +.LOOP_HV64_LASX: + PUT_HEVC_EPEL_UNI_W_HV16_LASX 64 + addi.d a0, t2, 16 + addi.d t2, t2, 16 + addi.d a2, t3, 16 + addi.d t3, t3, 16 + addi.d a4, t4, 0 + addi.d t5, t5, -1 + bnez t5, .LOOP_HV64_LASX +endfunc + +/* + * void FUNC(put_hevc_qpel_uni_h)(uint8_t *_dst, ptrdiff_t _dststride, + * const uint8_t *_src, ptrdiff_t _srcstride, + * int height, intptr_t mx, intptr_t my, + * int width) + */ +function ff_hevc_put_hevc_uni_qpel_h4_8_lsx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr5, t1, t0 //filter + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + vreplgr2vr.h vr1, t1 + la.local t1, shufb + vld vr2, t1, 32 //mask0 0 1 + vaddi.bu vr3, vr2, 2 //mask1 2 3 +.LOOP_UNI_H4: + vld vr18, a2, 0 + vldx vr19, a2, a3 + alsl.d a2, a3, a2, 1 + vshuf.b vr6, vr18, vr18, vr2 + vshuf.b vr7, vr18, vr18, vr3 + vshuf.b vr8, vr19, vr19, vr2 + vshuf.b vr9, vr19, vr19, vr3 + vdp2.h.bu.b vr10, vr6, vr5 + vdp2.h.bu.b vr11, vr7, vr5 + vdp2.h.bu.b vr12, vr8, vr5 + vdp2.h.bu.b vr13, vr9, vr5 + vhaddw.d.h vr10 + vhaddw.d.h vr11 + vhaddw.d.h vr12 + vhaddw.d.h vr13 + vpickev.w vr10, vr11, vr10 + vpickev.w vr11, vr13, vr12 + vpickev.h vr10, vr11, vr10 + vadd.h vr10, vr10, vr1 + vsrai.h vr10, vr10, 6 + vssrani.bu.h vr10, vr10, 0 + fst.s f10, a0, 0 + vbsrl.v vr10, vr10, 4 + fstx.s f10, a0, a1 + alsl.d a0, a1, a0, 1 + addi.d a4, a4, -2 + bnez a4, .LOOP_UNI_H4 +endfunc + +.macro HEVC_UNI_QPEL_H8_LSX in0, out0 + vshuf.b vr10, \in0, \in0, vr5 + vshuf.b vr11, \in0, \in0, vr6 + vshuf.b vr12, \in0, \in0, vr7 + vshuf.b vr13, \in0, \in0, vr8 + vdp2.h.bu.b \out0, vr10, vr0 //(QPEL_FILTER(src, 1) + vdp2add.h.bu.b \out0, vr11, vr1 + vdp2add.h.bu.b \out0, vr12, vr2 + vdp2add.h.bu.b \out0, vr13, vr3 + vadd.h \out0, \out0, vr4 + vsrai.h \out0, \out0, 6 +.endm + +.macro HEVC_UNI_QPEL_H16_LASX in0, out0 + xvshuf.b xr10, \in0, \in0, xr5 + xvshuf.b xr11, \in0, \in0, xr6 + xvshuf.b xr12, \in0, \in0, xr7 + xvshuf.b xr13, \in0, \in0, xr8 + xvdp2.h.bu.b \out0, xr10, xr0 //(QPEL_FILTER(src, 1) + xvdp2add.h.bu.b \out0, xr11, xr1 + xvdp2add.h.bu.b \out0, xr12, xr2 + xvdp2add.h.bu.b \out0, xr13, xr3 + xvadd.h \out0, \out0, xr4 + xvsrai.h \out0, \out0, 6 +.endm + +function ff_hevc_put_hevc_uni_qpel_h6_8_lsx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr0, t1, t0 //filter abcdefgh + vreplvei.h vr1, vr0, 1 //cd... + vreplvei.h vr2, vr0, 2 //ef... + vreplvei.h vr3, vr0, 3 //gh... + vreplvei.h vr0, vr0, 0 //ab... + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + vreplgr2vr.h vr4, t1 + la.local t1, shufb + vld vr5, t1, 48 + vaddi.bu vr6, vr5, 2 + vaddi.bu vr7, vr5, 4 + vaddi.bu vr8, vr5, 6 +.LOOP_UNI_H6: + vld vr9, a2, 0 + add.d a2, a2, a3 + HEVC_UNI_QPEL_H8_LSX vr9, vr14 + vssrani.bu.h vr14, vr14, 0 + fst.s f14, a0, 0 + vstelm.h vr14, a0, 4, 2 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_H6 +endfunc + +function ff_hevc_put_hevc_uni_qpel_h8_8_lsx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr0, t1, t0 //filter abcdefgh + vreplvei.h vr1, vr0, 1 //cd... + vreplvei.h vr2, vr0, 2 //ef... + vreplvei.h vr3, vr0, 3 //gh... + vreplvei.h vr0, vr0, 0 //ab... + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + vreplgr2vr.h vr4, t1 + la.local t1, shufb + vld vr5, t1, 48 + vaddi.bu vr6, vr5, 2 + vaddi.bu vr7, vr5, 4 + vaddi.bu vr8, vr5, 6 +.LOOP_UNI_H8: + vld vr9, a2, 0 + add.d a2, a2, a3 + HEVC_UNI_QPEL_H8_LSX vr9, vr14 + vssrani.bu.h vr14, vr14, 0 + fst.d f14, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_H8 +endfunc + +function ff_hevc_put_hevc_uni_qpel_h12_8_lsx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr0, t1, t0 //filter abcdefgh + vreplvei.h vr1, vr0, 1 //cd... + vreplvei.h vr2, vr0, 2 //ef... + vreplvei.h vr3, vr0, 3 //gh... + vreplvei.h vr0, vr0, 0 //ab... + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + vreplgr2vr.h vr4, t1 + la.local t1, shufb + vld vr5, t1, 48 + vaddi.bu vr6, vr5, 2 + vaddi.bu vr7, vr5, 4 + vaddi.bu vr8, vr5, 6 +.LOOP_UNI_H12: + vld vr9, a2, 0 + HEVC_UNI_QPEL_H8_LSX vr9, vr14 + vld vr9, a2, 8 + add.d a2, a2, a3 + HEVC_UNI_QPEL_H8_LSX vr9, vr15 + vssrani.bu.h vr15, vr14, 0 + fst.d f15, a0, 0 + vstelm.w vr15, a0, 8, 2 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_H12 +endfunc + +function ff_hevc_put_hevc_uni_qpel_h12_8_lasx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr0, t1, t0 //filter abcdefgh + xvreplve0.q xr0, xr0 + xvrepl128vei.h xr1, xr0, 1 //cd... + xvrepl128vei.h xr2, xr0, 2 //ef... + xvrepl128vei.h xr3, xr0, 3 //gh... + xvrepl128vei.h xr0, xr0, 0 //ab... + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + xvreplgr2vr.h xr4, t1 + la.local t1, shufb + vld vr5, t1, 48 + xvreplve0.q xr5, xr5 + xvaddi.bu xr6, xr5, 2 + xvaddi.bu xr7, xr5, 4 + xvaddi.bu xr8, xr5, 6 +.LOOP_UNI_H12_LASX: + xvld xr9, a2, 0 + add.d a2, a2, a3 + xvpermi.d xr9, xr9, 0x94 //rearrange data + HEVC_UNI_QPEL_H16_LASX xr9, xr14 + xvpermi.q xr15, xr14, 0x01 + vssrani.bu.h vr15, vr14, 0 + fst.d f15, a0, 0 + vstelm.w vr15, a0, 8, 2 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_H12_LASX +endfunc + +function ff_hevc_put_hevc_uni_qpel_h16_8_lsx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr0, t1, t0 //filter abcdefgh + vreplvei.h vr1, vr0, 1 //cd... + vreplvei.h vr2, vr0, 2 //ef... + vreplvei.h vr3, vr0, 3 //gh... + vreplvei.h vr0, vr0, 0 //ab... + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + vreplgr2vr.h vr4, t1 + la.local t1, shufb + vld vr5, t1, 48 + vaddi.bu vr6, vr5, 2 + vaddi.bu vr7, vr5, 4 + vaddi.bu vr8, vr5, 6 +.LOOP_UNI_H16: + vld vr9, a2, 0 + HEVC_UNI_QPEL_H8_LSX vr9, vr14 + vld vr9, a2, 8 + add.d a2, a2, a3 + HEVC_UNI_QPEL_H8_LSX vr9, vr15 + vssrani.bu.h vr15, vr14, 0 + vst vr15, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_H16 +endfunc + +function ff_hevc_put_hevc_uni_qpel_h16_8_lasx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr0, t1, t0 //filter abcdefgh + xvreplve0.q xr0, xr0 + xvrepl128vei.h xr1, xr0, 1 //cd... + xvrepl128vei.h xr2, xr0, 2 //ef... + xvrepl128vei.h xr3, xr0, 3 //gh... + xvrepl128vei.h xr0, xr0, 0 //ab... + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + xvreplgr2vr.h xr4, t1 + la.local t1, shufb + vld vr5, t1, 48 + xvreplve0.q xr5, xr5 + xvaddi.bu xr6, xr5, 2 + xvaddi.bu xr7, xr5, 4 + xvaddi.bu xr8, xr5, 6 +.LOOP_UNI_H16_LASX: + xvld xr9, a2, 0 + add.d a2, a2, a3 + xvpermi.d xr9, xr9, 0x94 //rearrange data + HEVC_UNI_QPEL_H16_LASX xr9, xr14 + xvpermi.q xr15, xr14, 0x01 + vssrani.bu.h vr15, vr14, 0 + vst vr15, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_H16_LASX +endfunc + +function ff_hevc_put_hevc_uni_qpel_h24_8_lsx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr0, t1, t0 //filter abcdefgh + vreplvei.h vr1, vr0, 1 //cd... + vreplvei.h vr2, vr0, 2 //ef... + vreplvei.h vr3, vr0, 3 //gh... + vreplvei.h vr0, vr0, 0 //ab... + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + vreplgr2vr.h vr4, t1 + la.local t1, shufb + vld vr5, t1, 48 + vaddi.bu vr6, vr5, 2 + vaddi.bu vr7, vr5, 4 + vaddi.bu vr8, vr5, 6 +.LOOP_UNI_H24: + vld vr9, a2, 0 + HEVC_UNI_QPEL_H8_LSX vr9, vr14 + vld vr9, a2, 8 + HEVC_UNI_QPEL_H8_LSX vr9, vr15 + vld vr9, a2, 16 + add.d a2, a2, a3 + HEVC_UNI_QPEL_H8_LSX vr9, vr16 + vssrani.bu.h vr15, vr14, 0 + vssrani.bu.h vr16, vr16, 0 + vst vr15, a0, 0 + fst.d f16, a0, 16 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_H24 +endfunc + +function ff_hevc_put_hevc_uni_qpel_h24_8_lasx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr0, t1, t0 //filter abcdefgh + xvreplve0.q xr0, xr0 + xvrepl128vei.h xr1, xr0, 1 //cd... + xvrepl128vei.h xr2, xr0, 2 //ef... + xvrepl128vei.h xr3, xr0, 3 //gh... + xvrepl128vei.h xr0, xr0, 0 //ab... + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + xvreplgr2vr.h xr4, t1 + la.local t1, shufb + vld vr5, t1, 48 + xvreplve0.q xr5, xr5 + xvaddi.bu xr6, xr5, 2 + xvaddi.bu xr7, xr5, 4 + xvaddi.bu xr8, xr5, 6 +.LOOP_UNI_H24_LASX: + xvld xr9, a2, 0 + xvpermi.q xr19, xr9, 0x01 //16...23 + add.d a2, a2, a3 + xvpermi.d xr9, xr9, 0x94 //rearrange data + HEVC_UNI_QPEL_H16_LASX xr9, xr14 + xvpermi.q xr15, xr14, 0x01 + vssrani.bu.h vr15, vr14, 0 + vst vr15, a0, 0 + HEVC_UNI_QPEL_H8_LSX vr19, vr16 + vssrani.bu.h vr16, vr16, 0 + fst.d f16, a0, 16 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_H24_LASX +endfunc + +function ff_hevc_put_hevc_uni_qpel_h32_8_lsx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr0, t1, t0 //filter abcdefgh + vreplvei.h vr1, vr0, 1 //cd... + vreplvei.h vr2, vr0, 2 //ef... + vreplvei.h vr3, vr0, 3 //gh... + vreplvei.h vr0, vr0, 0 //ab... + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + vreplgr2vr.h vr4, t1 + la.local t1, shufb + vld vr5, t1, 48 + vaddi.bu vr6, vr5, 2 + vaddi.bu vr7, vr5, 4 + vaddi.bu vr8, vr5, 6 +.LOOP_UNI_H32: + vld vr9, a2, 0 + HEVC_UNI_QPEL_H8_LSX vr9, vr14 + vld vr9, a2, 8 + HEVC_UNI_QPEL_H8_LSX vr9, vr15 + vld vr9, a2, 16 + HEVC_UNI_QPEL_H8_LSX vr9, vr16 + vld vr9, a2, 24 + add.d a2, a2, a3 + HEVC_UNI_QPEL_H8_LSX vr9, vr17 + vssrani.bu.h vr15, vr14, 0 + vssrani.bu.h vr17, vr16, 0 + vst vr15, a0, 0 + vst vr17, a0, 16 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_H32 +endfunc + +function ff_hevc_put_hevc_uni_qpel_h32_8_lasx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr0, t1, t0 //filter abcdefgh + xvreplve0.q xr0, xr0 + xvrepl128vei.h xr1, xr0, 1 //cd... + xvrepl128vei.h xr2, xr0, 2 //ef... + xvrepl128vei.h xr3, xr0, 3 //gh... + xvrepl128vei.h xr0, xr0, 0 //ab... + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + xvreplgr2vr.h xr4, t1 + la.local t1, shufb + vld vr5, t1, 48 + xvreplve0.q xr5, xr5 + xvaddi.bu xr6, xr5, 2 + xvaddi.bu xr7, xr5, 4 + xvaddi.bu xr8, xr5, 6 +.LOOP_UNI_H32_LASX: + xvld xr9, a2, 0 + xvpermi.d xr9, xr9, 0x94 + HEVC_UNI_QPEL_H16_LASX xr9, xr14 + xvld xr9, a2, 16 + xvpermi.d xr9, xr9, 0x94 + HEVC_UNI_QPEL_H16_LASX xr9, xr15 + add.d a2, a2, a3 + xvssrani.bu.h xr15, xr14, 0 + xvpermi.d xr15, xr15, 0xd8 + xvst xr15, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_H32_LASX +endfunc + +function ff_hevc_put_hevc_uni_qpel_h48_8_lsx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr0, t1, t0 //filter abcdefgh + vreplvei.h vr1, vr0, 1 //cd... + vreplvei.h vr2, vr0, 2 //ef... + vreplvei.h vr3, vr0, 3 //gh... + vreplvei.h vr0, vr0, 0 //ab... + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + vreplgr2vr.h vr4, t1 + la.local t1, shufb + vld vr5, t1, 48 + vaddi.bu vr6, vr5, 2 + vaddi.bu vr7, vr5, 4 + vaddi.bu vr8, vr5, 6 +.LOOP_UNI_H48: + vld vr9, a2, 0 + HEVC_UNI_QPEL_H8_LSX vr9, vr14 + vld vr9, a2, 8 + HEVC_UNI_QPEL_H8_LSX vr9, vr15 + vld vr9, a2, 16 + HEVC_UNI_QPEL_H8_LSX vr9, vr16 + vld vr9, a2, 24 + HEVC_UNI_QPEL_H8_LSX vr9, vr17 + vld vr9, a2, 32 + HEVC_UNI_QPEL_H8_LSX vr9, vr18 + vld vr9, a2, 40 + add.d a2, a2, a3 + HEVC_UNI_QPEL_H8_LSX vr9, vr19 + vssrani.bu.h vr15, vr14, 0 + vssrani.bu.h vr17, vr16, 0 + vssrani.bu.h vr19, vr18, 0 + vst vr15, a0, 0 + vst vr17, a0, 16 + vst vr19, a0, 32 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_H48 +endfunc + +function ff_hevc_put_hevc_uni_qpel_h48_8_lasx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr0, t1, t0 //filter abcdefgh + xvreplve0.q xr0, xr0 + xvrepl128vei.h xr1, xr0, 1 //cd... + xvrepl128vei.h xr2, xr0, 2 //ef... + xvrepl128vei.h xr3, xr0, 3 //gh... + xvrepl128vei.h xr0, xr0, 0 //ab... + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + xvreplgr2vr.h xr4, t1 + la.local t1, shufb + vld vr5, t1, 48 + xvreplve0.q xr5, xr5 + xvaddi.bu xr6, xr5, 2 + xvaddi.bu xr7, xr5, 4 + xvaddi.bu xr8, xr5, 6 +.LOOP_UNI_H48_LASX: + xvld xr9, a2, 0 + xvpermi.d xr9, xr9, 0x94 + HEVC_UNI_QPEL_H16_LASX xr9, xr14 + xvld xr9, a2, 16 + xvpermi.d xr9, xr9, 0x94 + HEVC_UNI_QPEL_H16_LASX xr9, xr15 + xvld xr9, a2, 32 + xvpermi.d xr9, xr9, 0x94 + HEVC_UNI_QPEL_H16_LASX xr9, xr16 + add.d a2, a2, a3 + xvssrani.bu.h xr15, xr14, 0 + xvpermi.d xr15, xr15, 0xd8 + xvst xr15, a0, 0 + xvpermi.q xr17, xr16, 0x01 + vssrani.bu.h vr17, vr16, 0 + vst vr17, a0, 32 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_H48_LASX +endfunc + +function ff_hevc_put_hevc_uni_qpel_h64_8_lasx + slli.w t0, a5, 4 + la.local t1, ff_hevc_qpel_filters + vldx vr0, t1, t0 //filter abcdefgh + xvreplve0.q xr0, xr0 + xvrepl128vei.h xr1, xr0, 1 //cd... + xvrepl128vei.h xr2, xr0, 2 //ef... + xvrepl128vei.h xr3, xr0, 3 //gh... + xvrepl128vei.h xr0, xr0, 0 //ab... + addi.d a2, a2, -3 //src -= 3 + addi.w t1, zero, 32 + xvreplgr2vr.h xr4, t1 + la.local t1, shufb + vld vr5, t1, 48 + xvreplve0.q xr5, xr5 + xvaddi.bu xr6, xr5, 2 + xvaddi.bu xr7, xr5, 4 + xvaddi.bu xr8, xr5, 6 +.LOOP_UNI_H64_LASX: + xvld xr9, a2, 0 + xvpermi.d xr9, xr9, 0x94 + HEVC_UNI_QPEL_H16_LASX xr9, xr14 + xvld xr9, a2, 16 + xvpermi.d xr9, xr9, 0x94 + HEVC_UNI_QPEL_H16_LASX xr9, xr15 + xvld xr9, a2, 32 + xvpermi.d xr9, xr9, 0x94 + HEVC_UNI_QPEL_H16_LASX xr9, xr16 + xvld xr9, a2, 48 + xvpermi.d xr9, xr9, 0x94 + HEVC_UNI_QPEL_H16_LASX xr9, xr17 + add.d a2, a2, a3 + xvssrani.bu.h xr15, xr14, 0 + xvpermi.d xr15, xr15, 0xd8 + xvst xr15, a0, 0 + xvssrani.bu.h xr17, xr16, 0 + xvpermi.d xr17, xr17, 0xd8 + xvst xr17, a0, 32 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_H64_LASX +endfunc + +/* + * void FUNC(put_hevc_epel_uni_w_v)(uint8_t *_dst, ptrdiff_t _dststride, + * const uint8_t *_src, ptrdiff_t _srcstride, + * int height, int denom, int wx, int ox, + * intptr_t mx, intptr_t my, int width) + */ +function ff_hevc_put_hevc_epel_uni_w_v4_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + fld.s f6, a2, 0 //0 + fldx.s f7, a2, a3 //1 + fldx.s f8, a2, t0 //2 + add.d a2, a2, t1 + vilvl.b vr6, vr7, vr6 + vilvl.b vr7, vr8, vr8 + vilvl.h vr6, vr7, vr6 + vreplvei.w vr0, vr0, 0 +.LOOP_UNI_V4: + fld.s f9, a2, 0 //3 + fldx.s f10, a2, a3 //4 + add.d a2, a2, t0 + vextrins.b vr6, vr9, 0x30 //insert the 3th load + vextrins.b vr6, vr9, 0x71 + vextrins.b vr6, vr9, 0xb2 + vextrins.b vr6, vr9, 0xf3 + vbsrl.v vr7, vr6, 1 + vextrins.b vr7, vr10, 0x30 //insert the 4th load + vextrins.b vr7, vr10, 0x71 + vextrins.b vr7, vr10, 0xb2 + vextrins.b vr7, vr10, 0xf3 + vdp2.h.bu.b vr8, vr6, vr0 //EPEL_FILTER(src, stride) + vdp2.h.bu.b vr9, vr7, vr0 + vhaddw.w.h vr10, vr8, vr8 + vhaddw.w.h vr11, vr9, vr9 + vmulwev.w.h vr10, vr10, vr1 //EPEL_FILTER(src, stride) * wx + vmulwev.w.h vr11, vr11, vr1 + vadd.w vr10, vr10, vr2 // + offset + vadd.w vr11, vr11, vr2 + vsra.w vr10, vr10, vr3 // >> shift + vsra.w vr11, vr11, vr3 + vadd.w vr10, vr10, vr4 // + ox + vadd.w vr11, vr11, vr4 + vssrani.h.w vr11, vr10, 0 + vssrani.bu.h vr10, vr11, 0 + vbsrl.v vr6, vr7, 1 + fst.s f10, a0, 0 + vbsrl.v vr10, vr10, 4 + fstx.s f10, a0, a1 + alsl.d a0, a1, a0, 1 + addi.d a4, a4, -2 + bnez a4, .LOOP_UNI_V4 +endfunc + +.macro CALC_EPEL_FILTER_LSX out0, out1 + vdp2.h.bu.b vr12, vr10, vr0 //EPEL_FILTER(src, stride) + vdp2add.h.bu.b vr12, vr11, vr5 + vexth.w.h vr13, vr12 + vsllwil.w.h vr12, vr12, 0 + vmulwev.w.h vr12, vr12, vr1 //EPEL_FILTER(src, stride) * wx + vmulwev.w.h vr13, vr13, vr1 //EPEL_FILTER(src, stride) * wx + vadd.w vr12, vr12, vr2 // + offset + vadd.w vr13, vr13, vr2 + vsra.w vr12, vr12, vr3 // >> shift + vsra.w vr13, vr13, vr3 + vadd.w \out0, vr12, vr4 // + ox + vadd.w \out1, vr13, vr4 +.endm + +.macro CALC_EPEL_FILTER_LASX out0 + xvdp2.h.bu.b xr11, xr12, xr0 //EPEL_FILTER(src, stride) + xvhaddw.w.h xr12, xr11, xr11 + xvmulwev.w.h xr12, xr12, xr1 //EPEL_FILTER(src, stride) * wx + xvadd.w xr12, xr12, xr2 // + offset + xvsra.w xr12, xr12, xr3 // >> shift + xvadd.w \out0, xr12, xr4 // + ox +.endm + +//w is a label, also can be used as a condition for ".if" statement. +.macro PUT_HEVC_EPEL_UNI_W_V8_LSX w + fld.d f6, a2, 0 //0 + fldx.d f7, a2, a3 //1 + fldx.d f8, a2, t0 //2 + add.d a2, a2, t1 +.LOOP_UNI_V8_\w: + fld.d f9, a2, 0 // 3 + add.d a2, a2, a3 + vilvl.b vr10, vr7, vr6 + vilvl.b vr11, vr9, vr8 + vaddi.bu vr6, vr7, 0 //back up previous value + vaddi.bu vr7, vr8, 0 + vaddi.bu vr8, vr9, 0 + CALC_EPEL_FILTER_LSX vr12, vr13 + vssrani.h.w vr13, vr12, 0 + vssrani.bu.h vr13, vr13, 0 +.if \w < 8 + fst.s f13, a0, 0 + vstelm.h vr13, a0, 4, 2 +.else + fst.d f13, a0, 0 +.endif + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_V8_\w +.endm + +//w is a label, also can be used as a condition for ".if" statement. +.macro PUT_HEVC_EPEL_UNI_W_V8_LASX w + fld.d f6, a2, 0 //0 + fldx.d f7, a2, a3 //1 + fldx.d f8, a2, t0 //2 + add.d a2, a2, t1 +.LOOP_UNI_V8_LASX_\w: + fld.d f9, a2, 0 // 3 + add.d a2, a2, a3 + vilvl.b vr10, vr7, vr6 + vilvl.b vr11, vr9, vr8 + xvilvl.h xr12, xr11, xr10 + xvilvh.h xr13, xr11, xr10 + xvpermi.q xr12, xr13, 0x02 + vaddi.bu vr6, vr7, 0 //back up previous value + vaddi.bu vr7, vr8, 0 + vaddi.bu vr8, vr9, 0 + CALC_EPEL_FILTER_LASX xr12 + xvpermi.q xr13, xr12, 0x01 + vssrani.h.w vr13, vr12, 0 + vssrani.bu.h vr13, vr13, 0 +.if \w < 8 + fst.s f13, a0, 0 + vstelm.h vr13, a0, 4, 2 +.else + fst.d f13, a0, 0 +.endif + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_V8_LASX_\w +.endm + +function ff_hevc_put_hevc_epel_uni_w_v6_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 + PUT_HEVC_EPEL_UNI_W_V8_LSX 6 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v6_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.w xr0, xr0 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + PUT_HEVC_EPEL_UNI_W_V8_LASX 6 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v8_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 + PUT_HEVC_EPEL_UNI_W_V8_LSX 8 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v8_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.w xr0, xr0 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + PUT_HEVC_EPEL_UNI_W_V8_LASX 8 +endfunc + +//w is a label, also can be used as a condition for ".if" statement. +.macro PUT_HEVC_EPEL_UNI_W_V16_LSX w + vld vr6, a2, 0 //0 + vldx vr7, a2, a3 //1 + vldx vr8, a2, t0 //2 + add.d a2, a2, t1 +.LOOP_UNI_V16_\w: + vld vr9, a2, 0 //3 + add.d a2, a2, a3 + vilvl.b vr10, vr7, vr6 + vilvl.b vr11, vr9, vr8 + CALC_EPEL_FILTER_LSX vr14, vr15 + vilvh.b vr10, vr7, vr6 + vilvh.b vr11, vr9, vr8 + CALC_EPEL_FILTER_LSX vr16, vr17 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + vaddi.bu vr6, vr7, 0 //back up previous value + vaddi.bu vr7, vr8, 0 + vaddi.bu vr8, vr9, 0 +.if \w < 16 + fst.d f17, a0, 0 + vstelm.w vr17, a0, 8, 2 +.else + vst vr17, a0, 0 +.endif + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_V16_\w +.endm + +//w is a label, also can be used as a condition for ".if" statement. +.macro PUT_HEVC_EPEL_UNI_W_V16_LASX w + vld vr6, a2, 0 //0 + vldx vr7, a2, a3 //1 + vldx vr8, a2, t0 //2 + add.d a2, a2, t1 +.LOOP_UNI_V16_LASX_\w: + vld vr9, a2, 0 //3 + add.d a2, a2, a3 + xvilvl.b xr10, xr7, xr6 + xvilvh.b xr11, xr7, xr6 + xvpermi.q xr11, xr10, 0x20 + xvilvl.b xr12, xr9, xr8 + xvilvh.b xr13, xr9, xr8 + xvpermi.q xr13, xr12, 0x20 + xvdp2.h.bu.b xr10, xr11, xr0 //EPEL_FILTER(src, stride) + xvdp2add.h.bu.b xr10, xr13, xr5 + xvexth.w.h xr11, xr10 + xvsllwil.w.h xr10, xr10, 0 + xvmulwev.w.h xr10, xr10, xr1 //EPEL_FILTER(src, stride) * wx + xvmulwev.w.h xr11, xr11, xr1 + xvadd.w xr10, xr10, xr2 // + offset + xvadd.w xr11, xr11, xr2 + xvsra.w xr10, xr10, xr3 // >> shift + xvsra.w xr11, xr11, xr3 + xvadd.w xr10, xr10, xr4 // + wx + xvadd.w xr11, xr11, xr4 + xvssrani.h.w xr11, xr10, 0 + xvpermi.q xr10, xr11, 0x01 + vssrani.bu.h vr10, vr11, 0 + vaddi.bu vr6, vr7, 0 //back up previous value + vaddi.bu vr7, vr8, 0 + vaddi.bu vr8, vr9, 0 +.if \w < 16 + fst.d f10, a0, 0 + vstelm.w vr10, a0, 8, 2 +.else + vst vr10, a0, 0 +.endif + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_V16_LASX_\w +.endm + +function ff_hevc_put_hevc_epel_uni_w_v12_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 + PUT_HEVC_EPEL_UNI_W_V16_LSX 12 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v12_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.q xr0, xr0 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + xvrepl128vei.h xr5, xr0, 1 + xvrepl128vei.h xr0, xr0, 0 + PUT_HEVC_EPEL_UNI_W_V16_LASX 12 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v16_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 + PUT_HEVC_EPEL_UNI_W_V16_LSX 16 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v16_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.q xr0, xr0 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + xvrepl128vei.h xr5, xr0, 1 + xvrepl128vei.h xr0, xr0, 0 + PUT_HEVC_EPEL_UNI_W_V16_LASX 16 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v24_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 + addi.d t2, a0, 0 //save init + addi.d t3, a2, 0 + addi.d t4, a4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LSX 24 + addi.d a0, t2, 16 //increase step + addi.d a2, t3, 16 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_V8_LSX 24 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v24_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.w xr20, xr0 //save xr0 + xvreplve0.q xr0, xr0 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + xvrepl128vei.h xr5, xr0, 1 + xvrepl128vei.h xr0, xr0, 0 + addi.d t2, a0, 0 //save init + addi.d t3, a2, 0 + addi.d t4, a4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LASX 24 + addi.d a0, t2, 16 //increase step + addi.d a2, t3, 16 + addi.d a4, t4, 0 + xvaddi.bu xr0, xr20, 0 + PUT_HEVC_EPEL_UNI_W_V8_LASX 24 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v32_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LSX 32 + addi.d a0, t2, 16 + addi.d a2, t3, 16 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LSX 33 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v32_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.q xr0, xr0 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + xvrepl128vei.h xr5, xr0, 1 + xvrepl128vei.h xr0, xr0, 0 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LASX 32 + addi.d a0, t2, 16 + addi.d a2, t3, 16 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LASX 33 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v48_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LSX 48 + addi.d a0, t2, 16 + addi.d a2, t3, 16 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LSX 49 + addi.d a0, t2, 32 + addi.d a2, t3, 32 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LSX 50 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v48_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.q xr0, xr0 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + xvrepl128vei.h xr5, xr0, 1 + xvrepl128vei.h xr0, xr0, 0 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LASX 48 + addi.d a0, t2, 16 + addi.d a2, t3, 16 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LASX 49 + addi.d a0, t2, 32 + addi.d a2, t3, 32 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LASX 50 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v64_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LSX 64 + addi.d a0, t2, 16 + addi.d a2, t3, 16 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LSX 65 + addi.d a0, t2, 32 + addi.d a2, t3, 32 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LSX 66 + addi.d a0, t2, 48 + addi.d a2, t3, 48 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LSX 67 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_v64_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 8 //my + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.q xr0, xr0 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + sub.d a2, a2, a3 //src -= stride + xvrepl128vei.h xr5, xr0, 1 + xvrepl128vei.h xr0, xr0, 0 + addi.d t2, a0, 0 + addi.d t3, a2, 0 + addi.d t4, a4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LASX 64 + addi.d a0, t2, 16 + addi.d a2, t3, 16 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LASX 65 + addi.d a0, t2, 32 + addi.d a2, t3, 32 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LASX 66 + addi.d a0, t2, 48 + addi.d a2, t3, 48 + addi.d a4, t4, 0 + PUT_HEVC_EPEL_UNI_W_V16_LASX 67 +endfunc + +/* + * void FUNC(put_hevc_epel_uni_w_h)(uint8_t *_dst, ptrdiff_t _dststride, + * const uint8_t *_src, ptrdiff_t _srcstride, + * int height, int denom, int wx, int ox, + * intptr_t mx, intptr_t my, int width) + */ +function ff_hevc_put_hevc_epel_uni_w_h4_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + vreplvei.w vr0, vr0, 0 + la.local t1, shufb + vld vr5, t1, 0 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 +.LOOP_UNI_W_H4: + fld.d f6, a2, 0 + add.d a2, a2, a3 + vshuf.b vr6, vr6, vr6, vr5 + vdp2.h.bu.b vr7, vr6, vr0 + vhaddw.w.h vr7, vr7, vr7 + vmulwev.w.h vr7, vr7, vr1 + vadd.w vr7, vr7, vr2 + vsra.w vr7, vr7, vr3 + vadd.w vr7, vr7, vr4 + vssrani.h.w vr7, vr7, 0 + vssrani.bu.h vr7, vr7, 0 + fst.s f7, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H4 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h6_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + vreplvei.w vr0, vr0, 0 + la.local t1, shufb + vld vr6, t1, 48 + vaddi.bu vr7, vr6, 2 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 +.LOOP_UNI_W_H6: + vld vr8, a2, 0 + add.d a2, a2, a3 + vshuf.b vr10, vr8, vr8, vr6 + vshuf.b vr11, vr8, vr8, vr7 + CALC_EPEL_FILTER_LSX vr14, vr15 + vssrani.h.w vr15, vr14, 0 + vssrani.bu.h vr15, vr15, 0 + fst.s f15, a0, 0 + vstelm.h vr15, a0, 4, 2 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H6 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h6_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.w xr0, xr0 + la.local t1, shufb + xvld xr6, t1, 64 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 +.LOOP_UNI_W_H6_LASX: + vld vr8, a2, 0 + xvreplve0.q xr8, xr8 + add.d a2, a2, a3 + xvshuf.b xr12, xr8, xr8, xr6 + CALC_EPEL_FILTER_LASX xr14 + xvpermi.q xr15, xr14, 0x01 + vssrani.h.w vr15, vr14, 0 + vssrani.bu.h vr15, vr15, 0 + fst.s f15, a0, 0 + vstelm.h vr15, a0, 4, 2 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H6_LASX +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h8_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + vreplvei.w vr0, vr0, 0 + la.local t1, shufb + vld vr6, t1, 48 + vaddi.bu vr7, vr6, 2 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 +.LOOP_UNI_W_H8: + vld vr8, a2, 0 + add.d a2, a2, a3 + vshuf.b vr10, vr8, vr8, vr6 + vshuf.b vr11, vr8, vr8, vr7 + CALC_EPEL_FILTER_LSX vr14, vr15 + vssrani.h.w vr15, vr14, 0 + vssrani.bu.h vr15, vr15, 0 + fst.d f15, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H8 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h8_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.w xr0, xr0 + la.local t1, shufb + xvld xr6, t1, 64 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 +.LOOP_UNI_W_H8_LASX: + vld vr8, a2, 0 + xvreplve0.q xr8, xr8 + add.d a2, a2, a3 + xvshuf.b xr12, xr8, xr8, xr6 + CALC_EPEL_FILTER_LASX xr14 + xvpermi.q xr15, xr14, 0x01 + vssrani.h.w vr15, vr14, 0 + vssrani.bu.h vr15, vr15, 0 + fst.d f15, a0, 0 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H8_LASX +endfunc + +.macro EPEL_UNI_W_H16_LOOP_LSX idx0, idx1, idx2 + vld vr8, a2, \idx0 + vshuf.b vr10, vr8, vr8, vr6 + vshuf.b vr11, vr8, vr8, vr7 + CALC_EPEL_FILTER_LSX vr14, vr15 + vld vr8, a2, \idx1 + vshuf.b vr10, vr8, vr8, vr6 + vshuf.b vr11, vr8, vr8, vr7 + CALC_EPEL_FILTER_LSX vr16, vr17 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + vst vr17, a0, \idx2 +.endm + +.macro EPEL_UNI_W_H16_LOOP_LASX idx0, idx2, w + xvld xr8, a2, \idx0 + xvpermi.d xr9, xr8, 0x09 + xvreplve0.q xr8, xr8 + xvshuf.b xr12, xr8, xr8, xr6 + CALC_EPEL_FILTER_LASX xr14 + xvreplve0.q xr8, xr9 + xvshuf.b xr12, xr8, xr8, xr6 + CALC_EPEL_FILTER_LASX xr16 + xvssrani.h.w xr16, xr14, 0 + xvpermi.q xr17, xr16, 0x01 + vssrani.bu.h vr17, vr16, 0 + vpermi.w vr17, vr17, 0xd8 +.if \w == 12 + fst.d f17, a0, 0 + vstelm.w vr17, a0, 8, 2 +.else + vst vr17, a0, \idx2 +.endif +.endm + +function ff_hevc_put_hevc_epel_uni_w_h12_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + vreplvei.w vr0, vr0, 0 + la.local t1, shufb + vld vr6, t1, 48 + vaddi.bu vr7, vr6, 2 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 +.LOOP_UNI_W_H12: + vld vr8, a2, 0 + vshuf.b vr10, vr8, vr8, vr6 + vshuf.b vr11, vr8, vr8, vr7 + CALC_EPEL_FILTER_LSX vr14, vr15 + vld vr8, a2, 8 + vshuf.b vr10, vr8, vr8, vr6 + vshuf.b vr11, vr8, vr8, vr7 + CALC_EPEL_FILTER_LSX vr16, vr17 + vssrani.h.w vr15, vr14, 0 + vssrani.h.w vr17, vr16, 0 + vssrani.bu.h vr17, vr15, 0 + fst.d f17, a0, 0 + vstelm.w vr17, a0, 8, 2 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H12 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h12_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.w xr0, xr0 + la.local t1, shufb + xvld xr6, t1, 64 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 +.LOOP_UNI_W_H12_LASX: + EPEL_UNI_W_H16_LOOP_LASX 0, 0, 12 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H12_LASX +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h16_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + vreplvei.w vr0, vr0, 0 + la.local t1, shufb + vld vr6, t1, 48 + vaddi.bu vr7, vr6, 2 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 +.LOOP_UNI_W_H16: + EPEL_UNI_W_H16_LOOP_LSX 0, 8, 0 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H16 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h16_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.w xr0, xr0 + la.local t1, shufb + xvld xr6, t1, 64 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 +.LOOP_UNI_W_H16_LASX: + EPEL_UNI_W_H16_LOOP_LASX 0, 0, 16 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H16_LASX +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h24_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + vreplvei.w vr0, vr0, 0 + la.local t1, shufb + vld vr6, t1, 48 + vaddi.bu vr7, vr6, 2 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 +.LOOP_UNI_W_H24: + EPEL_UNI_W_H16_LOOP_LSX 0, 8, 0 + vld vr8, a2, 16 + add.d a2, a2, a3 + vshuf.b vr10, vr8, vr8, vr6 + vshuf.b vr11, vr8, vr8, vr7 + CALC_EPEL_FILTER_LSX vr18, vr19 + vssrani.h.w vr19, vr18, 0 + vssrani.bu.h vr19, vr19, 0 + fst.d f19, a0, 16 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H24 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h24_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.w xr0, xr0 + la.local t1, shufb + xvld xr6, t1, 64 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 +.LOOP_UNI_W_H24_LASX: + EPEL_UNI_W_H16_LOOP_LASX 0, 0, 24 + vld vr8, a2, 16 + add.d a2, a2, a3 + xvreplve0.q xr8, xr8 + xvshuf.b xr12, xr8, xr8, xr6 + CALC_EPEL_FILTER_LASX xr14 + xvpermi.q xr15, xr14, 0x01 + vssrani.h.w vr15, vr14, 0 + vssrani.bu.h vr15, vr15, 0 + fst.d f15, a0, 16 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H24_LASX +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h32_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + vreplvei.w vr0, vr0, 0 + la.local t1, shufb + vld vr6, t1, 48 + vaddi.bu vr7, vr6, 2 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 +.LOOP_UNI_W_H32: + EPEL_UNI_W_H16_LOOP_LSX 0, 8, 0 + EPEL_UNI_W_H16_LOOP_LSX 16, 24, 16 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H32 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h32_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.w xr0, xr0 + la.local t1, shufb + xvld xr6, t1, 64 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 +.LOOP_UNI_W_H32_LASX: + EPEL_UNI_W_H16_LOOP_LASX 0, 0, 32 + EPEL_UNI_W_H16_LOOP_LASX 16, 16, 32 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H32_LASX +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h48_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + vreplvei.w vr0, vr0, 0 + la.local t1, shufb + vld vr6, t1, 48 + vaddi.bu vr7, vr6, 2 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 +.LOOP_UNI_W_H48: + EPEL_UNI_W_H16_LOOP_LSX 0, 8, 0 + EPEL_UNI_W_H16_LOOP_LSX 16, 24, 16 + EPEL_UNI_W_H16_LOOP_LSX 32, 40, 32 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H48 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h48_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.w xr0, xr0 + la.local t1, shufb + xvld xr6, t1, 64 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 +.LOOP_UNI_W_H48_LASX: + EPEL_UNI_W_H16_LOOP_LASX 0, 0, 48 + EPEL_UNI_W_H16_LOOP_LASX 16, 16, 48 + EPEL_UNI_W_H16_LOOP_LASX 32, 32, 48 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H48_LASX +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h64_8_lsx + LOAD_VAR 128 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + vreplvei.w vr0, vr0, 0 + la.local t1, shufb + vld vr6, t1, 48 + vaddi.bu vr7, vr6, 2 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 + vreplvei.h vr5, vr0, 1 + vreplvei.h vr0, vr0, 0 +.LOOP_UNI_W_H64: + EPEL_UNI_W_H16_LOOP_LSX 0, 8, 0 + EPEL_UNI_W_H16_LOOP_LSX 16, 24, 16 + EPEL_UNI_W_H16_LOOP_LSX 32, 40, 32 + EPEL_UNI_W_H16_LOOP_LSX 48, 56, 48 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H64 +endfunc + +function ff_hevc_put_hevc_epel_uni_w_h64_8_lasx + LOAD_VAR 256 + ld.d t0, sp, 0 //mx + slli.w t0, t0, 2 + la.local t1, ff_hevc_epel_filters + vldx vr0, t1, t0 //filter + xvreplve0.w xr0, xr0 + la.local t1, shufb + xvld xr6, t1, 64 + slli.d t0, a3, 1 //stride * 2 + add.d t1, t0, a3 //stride * 3 + addi.d a2, a2, -1 //src -= 1 +.LOOP_UNI_W_H64_LASX: + EPEL_UNI_W_H16_LOOP_LASX 0, 0, 64 + EPEL_UNI_W_H16_LOOP_LASX 16, 16, 64 + EPEL_UNI_W_H16_LOOP_LASX 32, 32, 64 + EPEL_UNI_W_H16_LOOP_LASX 48, 48, 64 + add.d a2, a2, a3 + add.d a0, a0, a1 + addi.d a4, a4, -1 + bnez a4, .LOOP_UNI_W_H64_LASX +endfunc + +/* + * void FUNC(put_hevc_epel_bi_h)(uint8_t *_dst, ptrdiff_t _dststride, + * const uint8_t *_src, ptrdiff_t _srcstride, + * const int16_t *src2, int height, intptr_t mx, + * intptr_t my, int width) + */ +function ff_hevc_put_hevc_bi_epel_h4_8_lsx + slli.w a6, a6, 2 + la.local t0, ff_hevc_epel_filters + vldx vr0, t0, a6 // filter + vreplvei.w vr0, vr0, 0 + la.local t0, shufb + vld vr1, t0, 0 // mask + addi.d a2, a2, -1 // src -= 1 +.LOOP_BI_EPEL_H4: + vld vr4, a4, 0 // src2 + vld vr5, a2, 0 + add.d a2, a2, a3 + addi.d a4, a4, 128 + vshuf.b vr5, vr5, vr5, vr1 + vdp2.h.bu.b vr6, vr5, vr0 // EPEL_FILTER(src, 1) + vsllwil.w.h vr4, vr4, 0 + vhaddw.w.h vr6, vr6, vr6 + vadd.w vr6, vr6, vr4 // src2[x] + vssrani.h.w vr6, vr6, 0 + vssrarni.bu.h vr6, vr6, 7 + fst.s f6, a0, 0 + add.d a0, a0, a1 + addi.d a5, a5, -1 + bnez a5, .LOOP_BI_EPEL_H4 +endfunc + +.macro PUT_HEVC_BI_EPEL_H8_LSX in0, in1, in2, in3, out0 + vshuf.b vr6, \in1, \in0, \in2 + vshuf.b vr7, \in1, \in0, \in3 + vdp2.h.bu.b vr8, vr6, vr0 // EPEL_FILTER(src, 1) + vdp2add.h.bu.b vr8, vr7, vr1 // EPEL_FILTER(src, 1) + vsadd.h \out0, vr8, vr4 // src2[x] +.endm + +.macro PUT_HEVC_BI_EPEL_H16_LASX in0, in1, in2, in3, out0 + xvshuf.b xr6, \in1, \in0, \in2 + xvshuf.b xr7, \in1, \in0, \in3 + xvdp2.h.bu.b xr8, xr6, xr0 // EPEL_FILTER(src, 1) + xvdp2add.h.bu.b xr8, xr7, xr1 // EPEL_FILTER(src, 1) + xvsadd.h \out0, xr8, xr4 // src2[x] +.endm + +function ff_hevc_put_hevc_bi_epel_h6_8_lsx + slli.w a6, a6, 2 + la.local t0, ff_hevc_epel_filters + vldx vr0, t0, a6 // filter + vreplvei.h vr1, vr0, 1 + vreplvei.h vr0, vr0, 0 + la.local t0, shufb + vld vr2, t0, 48// mask + vaddi.bu vr3, vr2, 2 + addi.d a2, a2, -1 // src -= 1 +.LOOP_BI_EPEL_H6: + vld vr4, a4, 0 // src2 + vld vr5, a2, 0 + add.d a2, a2, a3 + addi.d a4, a4, 128 + PUT_HEVC_BI_EPEL_H8_LSX vr5, vr5, vr2, vr3, vr7 + vssrarni.bu.h vr7, vr7, 7 + fst.s f7, a0, 0 + vstelm.h vr7, a0, 4, 2 + add.d a0, a0, a1 + addi.d a5, a5, -1 + bnez a5, .LOOP_BI_EPEL_H6 +endfunc + +function ff_hevc_put_hevc_bi_epel_h8_8_lsx + slli.w a6, a6, 2 + la.local t0, ff_hevc_epel_filters + vldx vr0, t0, a6 // filter + vreplvei.h vr1, vr0, 1 + vreplvei.h vr0, vr0, 0 + la.local t0, shufb + vld vr2, t0, 48// mask + vaddi.bu vr3, vr2, 2 + addi.d a2, a2, -1 // src -= 1 +.LOOP_BI_EPEL_H8: + vld vr4, a4, 0 // src2 + vld vr5, a2, 0 + add.d a2, a2, a3 + addi.d a4, a4, 128 + PUT_HEVC_BI_EPEL_H8_LSX vr5, vr5, vr2, vr3, vr7 + vssrarni.bu.h vr7, vr7, 7 + fst.d f7, a0, 0 + add.d a0, a0, a1 + addi.d a5, a5, -1 + bnez a5, .LOOP_BI_EPEL_H8 +endfunc + +function ff_hevc_put_hevc_bi_epel_h12_8_lsx + slli.w a6, a6, 2 + la.local t0, ff_hevc_epel_filters + vldx vr0, t0, a6 // filter + vreplvei.h vr1, vr0, 1 + vreplvei.h vr0, vr0, 0 + la.local t0, shufb + vld vr2, t0, 48// mask + vaddi.bu vr3, vr2, 2 + addi.d a2, a2, -1 // src -= 1 +.LOOP_BI_EPEL_H12: + vld vr4, a4, 0 // src2 + vld vr5, a2, 0 + PUT_HEVC_BI_EPEL_H8_LSX vr5, vr5, vr2, vr3, vr11 + vld vr5, a2, 8 + vld vr4, a4, 16 + PUT_HEVC_BI_EPEL_H8_LSX vr5, vr5, vr2, vr3, vr12 + vssrarni.bu.h vr12, vr11, 7 + fst.d f12, a0, 0 + vstelm.w vr12, a0, 8, 2 + add.d a2, a2, a3 + addi.d a4, a4, 128 + add.d a0, a0, a1 + addi.d a5, a5, -1 + bnez a5, .LOOP_BI_EPEL_H12 +endfunc + +function ff_hevc_put_hevc_bi_epel_h12_8_lasx + slli.w a6, a6, 2 + la.local t0, ff_hevc_epel_filters + vldx vr0, t0, a6 // filter + xvreplve0.q xr0, xr0 + xvrepl128vei.h xr1, xr0, 1 + xvrepl128vei.h xr0, xr0, 0 + la.local t0, shufb + xvld xr2, t0, 96// mask + xvaddi.bu xr3, xr2, 2 + addi.d a2, a2, -1 // src -= 1 +.LOOP_BI_EPEL_H12_LASX: + xvld xr4, a4, 0 // src2 + xvld xr5, a2, 0 + xvpermi.d xr5, xr5, 0x94 + PUT_HEVC_BI_EPEL_H16_LASX xr5, xr5, xr2, xr3, xr9 + xvpermi.q xr10, xr9, 0x01 + vssrarni.bu.h vr10, vr9, 7 + fst.d f10, a0, 0 + vstelm.w vr10, a0, 8, 2 + add.d a2, a2, a3 + addi.d a4, a4, 128 + add.d a0, a0, a1 + addi.d a5, a5, -1 + bnez a5, .LOOP_BI_EPEL_H12_LASX +endfunc + +function ff_hevc_put_hevc_bi_epel_h16_8_lsx + slli.w a6, a6, 2 + la.local t0, ff_hevc_epel_filters + vldx vr0, t0, a6 // filter + vreplvei.h vr1, vr0, 1 + vreplvei.h vr0, vr0, 0 + la.local t0, shufb + vld vr2, t0, 48// mask + vaddi.bu vr3, vr2, 2 + addi.d a2, a2, -1 // src -= 1 +.LOOP_BI_EPEL_H16: + vld vr4, a4, 0 // src2 + vld vr5, a2, 0 + PUT_HEVC_BI_EPEL_H8_LSX vr5, vr5, vr2, vr3, vr11 + vld vr5, a2, 8 + vld vr4, a4, 16 + PUT_HEVC_BI_EPEL_H8_LSX vr5, vr5, vr2, vr3, vr12 + vssrarni.bu.h vr12, vr11, 7 + vst vr12, a0, 0 + add.d a2, a2, a3 + addi.d a4, a4, 128 + add.d a0, a0, a1 + addi.d a5, a5, -1 + bnez a5, .LOOP_BI_EPEL_H16 +endfunc + +function ff_hevc_put_hevc_bi_epel_h16_8_lasx + slli.w a6, a6, 2 + la.local t0, ff_hevc_epel_filters + vldx vr0, t0, a6 // filter + xvreplve0.q xr0, xr0 + xvrepl128vei.h xr1, xr0, 1 + xvrepl128vei.h xr0, xr0, 0 + la.local t0, shufb + xvld xr2, t0, 96// mask + xvaddi.bu xr3, xr2, 2 + addi.d a2, a2, -1 // src -= 1 +.LOOP_BI_EPEL_H16_LASX: + xvld xr4, a4, 0 // src2 + xvld xr5, a2, 0 + xvpermi.d xr5, xr5, 0x94 + PUT_HEVC_BI_EPEL_H16_LASX xr5, xr5, xr2, xr3, xr9 + xvpermi.q xr10, xr9, 0x01 + vssrarni.bu.h vr10, vr9, 7 + vst vr10, a0, 0 + add.d a2, a2, a3 + addi.d a4, a4, 128 + add.d a0, a0, a1 + addi.d a5, a5, -1 + bnez a5, .LOOP_BI_EPEL_H16_LASX +endfunc + +function ff_hevc_put_hevc_bi_epel_h32_8_lasx + slli.w a6, a6, 2 + la.local t0, ff_hevc_epel_filters + vldx vr0, t0, a6 // filter + xvreplve0.q xr0, xr0 + xvrepl128vei.h xr1, xr0, 1 + xvrepl128vei.h xr0, xr0, 0 + la.local t0, shufb + xvld xr2, t0, 96// mask + xvaddi.bu xr3, xr2, 2 + addi.d a2, a2, -1 // src -= 1 +.LOOP_BI_EPEL_H32_LASX: + xvld xr4, a4, 0 // src2 + xvld xr5, a2, 0 + xvpermi.q xr15, xr5, 0x01 + xvpermi.d xr5, xr5, 0x94 + PUT_HEVC_BI_EPEL_H16_LASX xr5, xr5, xr2, xr3, xr9 + xvld xr4, a4, 32 + xvld xr15, a2, 16 + xvpermi.d xr15, xr15, 0x94 + PUT_HEVC_BI_EPEL_H16_LASX xr15, xr15, xr2, xr3, xr11 + xvssrarni.bu.h xr11, xr9, 7 + xvpermi.d xr11, xr11, 0xd8 + xvst xr11, a0, 0 + add.d a2, a2, a3 + addi.d a4, a4, 128 + add.d a0, a0, a1 + addi.d a5, a5, -1 + bnez a5, .LOOP_BI_EPEL_H32_LASX +endfunc + +function ff_hevc_put_hevc_bi_epel_h48_8_lsx + slli.w a6, a6, 2 + la.local t0, ff_hevc_epel_filters + vldx vr0, t0, a6// filter + vreplvei.h vr1, vr0, 1 + vreplvei.h vr0, vr0, 0 + la.local t0, shufb + vld vr2, t0, 48// mask + vaddi.bu vr3, vr2, 2 + vaddi.bu vr21, vr2, 8 + vaddi.bu vr22, vr2, 10 + addi.d a2, a2, -1 // src -= 1 +.LOOP_BI_EPEL_H48: + vld vr4, a4, 0 // src2 + vld vr5, a2, 0 + vld vr9, a2, 16 + vld vr10, a2, 32 + vld vr11, a2, 48 + PUT_HEVC_BI_EPEL_H8_LSX vr5, vr5, vr2, vr3, vr12 + vld vr4, a4, 16 + PUT_HEVC_BI_EPEL_H8_LSX vr5, vr9, vr21, vr22, vr13 + vld vr4, a4, 32 + PUT_HEVC_BI_EPEL_H8_LSX vr9, vr9, vr2, vr3, vr14 + vld vr4, a4, 48 + PUT_HEVC_BI_EPEL_H8_LSX vr9, vr10, vr21, vr22, vr15 + vld vr4, a4, 64 + PUT_HEVC_BI_EPEL_H8_LSX vr10, vr10, vr2, vr3, vr16 + vld vr4, a4, 80 + PUT_HEVC_BI_EPEL_H8_LSX vr10, vr11, vr21, vr22, vr17 + vssrarni.bu.h vr13, vr12, 7 + vssrarni.bu.h vr15, vr14, 7 + vssrarni.bu.h vr17, vr16, 7 + vst vr13, a0, 0 + vst vr15, a0, 16 + vst vr17, a0, 32 + add.d a2, a2, a3 + addi.d a4, a4, 128 + add.d a0, a0, a1 + addi.d a5, a5, -1 + bnez a5, .LOOP_BI_EPEL_H48 +endfunc + +function ff_hevc_put_hevc_bi_epel_h48_8_lasx + slli.w a6, a6, 2 + la.local t0, ff_hevc_epel_filters + vldx vr0, t0, a6 // filter + xvreplve0.q xr0, xr0 + xvrepl128vei.h xr1, xr0, 1 + xvrepl128vei.h xr0, xr0, 0 + la.local t0, shufb + xvld xr2, t0, 96// mask + xvaddi.bu xr3, xr2, 2 + addi.d a2, a2, -1 // src -= 1 +.LOOP_BI_EPEL_H48_LASX: + xvld xr4, a4, 0 // src2 + xvld xr5, a2, 0 + xvld xr9, a2, 32 + xvpermi.d xr10, xr9, 0x94 + xvpermi.q xr9, xr5, 0x21 + xvpermi.d xr9, xr9, 0x94 + xvpermi.d xr5, xr5, 0x94 + PUT_HEVC_BI_EPEL_H16_LASX xr5, xr5, xr2, xr3, xr11 + xvld xr4, a4, 32 + PUT_HEVC_BI_EPEL_H16_LASX xr9, xr9, xr2, xr3, xr12 + xvld xr4, a4, 64 + PUT_HEVC_BI_EPEL_H16_LASX xr10, xr10, xr2, xr3, xr13 + xvssrarni.bu.h xr12, xr11, 7 + xvpermi.d xr12, xr12, 0xd8 + xvpermi.q xr14, xr13, 0x01 + vssrarni.bu.h vr14, vr13, 7 + xvst xr12, a0, 0 + vst vr14, a0, 32 + add.d a2, a2, a3 + addi.d a4, a4, 128 + add.d a0, a0, a1 + addi.d a5, a5, -1 + bnez a5, .LOOP_BI_EPEL_H48_LASX +endfunc + +function ff_hevc_put_hevc_bi_epel_h64_8_lsx + slli.w a6, a6, 2 + la.local t0, ff_hevc_epel_filters + vldx vr0, t0, a6// filter + vreplvei.h vr1, vr0, 1 + vreplvei.h vr0, vr0, 0 + la.local t0, shufb + vld vr2, t0, 48// mask + vaddi.bu vr3, vr2, 2 + vaddi.bu vr21, vr2, 8 + vaddi.bu vr22, vr2, 10 + addi.d a2, a2, -1 // src -= 1 +.LOOP_BI_EPEL_H64: + vld vr4, a4, 0 // src2 + vld vr5, a2, 0 + vld vr9, a2, 16 + vld vr10, a2, 32 + vld vr11, a2, 48 + vld vr12, a2, 64 + PUT_HEVC_BI_EPEL_H8_LSX vr5, vr5, vr2, vr3, vr13 + vld vr4, a4, 16 + PUT_HEVC_BI_EPEL_H8_LSX vr5, vr9, vr21, vr22, vr14 + vld vr4, a4, 32 + PUT_HEVC_BI_EPEL_H8_LSX vr9, vr9, vr2, vr3, vr15 + vld vr4, a4, 48 + PUT_HEVC_BI_EPEL_H8_LSX vr9, vr10, vr21, vr22, vr16 + vld vr4, a4, 64 + PUT_HEVC_BI_EPEL_H8_LSX vr10, vr10, vr2, vr3, vr17 + vld vr4, a4, 80 + PUT_HEVC_BI_EPEL_H8_LSX vr10, vr11, vr21, vr22, vr18 + vld vr4, a4, 96 + PUT_HEVC_BI_EPEL_H8_LSX vr11, vr11, vr2, vr3, vr19 + vld vr4, a4, 112 + PUT_HEVC_BI_EPEL_H8_LSX vr11, vr12, vr21, vr22, vr20 + vssrarni.bu.h vr14, vr13, 7 + vssrarni.bu.h vr16, vr15, 7 + vssrarni.bu.h vr18, vr17, 7 + vssrarni.bu.h vr20, vr19, 7 + vst vr14, a0, 0 + vst vr16, a0, 16 + vst vr18, a0, 32 + vst vr20, a0, 48 + add.d a2, a2, a3 + addi.d a4, a4, 128 + add.d a0, a0, a1 + addi.d a5, a5, -1 + bnez a5, .LOOP_BI_EPEL_H64 +endfunc + +function ff_hevc_put_hevc_bi_epel_h64_8_lasx + slli.w a6, a6, 2 + la.local t0, ff_hevc_epel_filters + vldx vr0, t0, a6 // filter + xvreplve0.q xr0, xr0 + xvrepl128vei.h xr1, xr0, 1 + xvrepl128vei.h xr0, xr0, 0 + la.local t0, shufb + xvld xr2, t0, 96// mask + xvaddi.bu xr3, xr2, 2 + addi.d a2, a2, -1 // src -= 1 +.LOOP_BI_EPEL_H64_LASX: + xvld xr4, a4, 0 // src2 + xvld xr5, a2, 0 + xvld xr9, a2, 32 + xvld xr11, a2, 48 + xvpermi.d xr11, xr11, 0x94 + xvpermi.d xr10, xr9, 0x94 + xvpermi.q xr9, xr5, 0x21 + xvpermi.d xr9, xr9, 0x94 + xvpermi.d xr5, xr5, 0x94 + PUT_HEVC_BI_EPEL_H16_LASX xr5, xr5, xr2, xr3, xr12 + xvld xr4, a4, 32 + PUT_HEVC_BI_EPEL_H16_LASX xr9, xr9, xr2, xr3, xr13 + xvld xr4, a4, 64 + PUT_HEVC_BI_EPEL_H16_LASX xr10, xr10, xr2, xr3, xr14 + xvld xr4, a4, 96 + PUT_HEVC_BI_EPEL_H16_LASX xr11, xr11, xr2, xr3, xr15 + xvssrarni.bu.h xr13, xr12, 7 + xvssrarni.bu.h xr15, xr14, 7 + xvpermi.d xr13, xr13, 0xd8 + xvpermi.d xr15, xr15, 0xd8 + xvst xr13, a0, 0 + xvst xr15, a0, 32 + add.d a2, a2, a3 + addi.d a4, a4, 128 + add.d a0, a0, a1 + addi.d a5, a5, -1 + bnez a5, .LOOP_BI_EPEL_H64_LASX +endfunc diff --git a/libavcodec/loongarch/hevc_mc_bi_lsx.c b/libavcodec/loongarch/hevc_mc_bi_lsx.c index 48441c107b7..d7ddd1c2469 100644 --- a/libavcodec/loongarch/hevc_mc_bi_lsx.c +++ b/libavcodec/loongarch/hevc_mc_bi_lsx.c @@ -2224,7 +2224,7 @@ void ff_hevc_put_hevc_bi_##PEL##_##DIR##WIDTH##_8_lsx(uint8_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR - 1]; \ + const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR]; \ \ hevc_##DIR1##_##TAP##t_##WIDTH##w_lsx(src, src_stride, src_16bit, \ MAX_PB_SIZE, dst, dst_stride, \ @@ -2265,8 +2265,8 @@ void ff_hevc_put_hevc_bi_##PEL##_hv##WIDTH##_8_lsx(uint8_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter_x = ff_hevc_##PEL##_filters[mx - 1]; \ - const int8_t *filter_y = ff_hevc_##PEL##_filters[my - 1]; \ + const int8_t *filter_x = ff_hevc_##PEL##_filters[mx]; \ + const int8_t *filter_y = ff_hevc_##PEL##_filters[my]; \ \ hevc_hv_##TAP##t_##WIDTH##w_lsx(src, src_stride, src_16bit, \ MAX_PB_SIZE, dst, dst_stride, \ diff --git a/libavcodec/loongarch/hevc_mc_uni_lsx.c b/libavcodec/loongarch/hevc_mc_uni_lsx.c index 5437dce0e0d..6bdc27a824c 100644 --- a/libavcodec/loongarch/hevc_mc_uni_lsx.c +++ b/libavcodec/loongarch/hevc_mc_uni_lsx.c @@ -1373,7 +1373,7 @@ void ff_hevc_put_hevc_uni_##PEL##_##DIR##WIDTH##_8_lsx(uint8_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR - 1]; \ + const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR]; \ \ common_##DIR1##_##TAP##t_##WIDTH##w_lsx(src, src_stride, dst, dst_stride, \ filter, height); \ @@ -1401,8 +1401,8 @@ void ff_hevc_put_hevc_uni_##PEL##_hv##WIDTH##_8_lsx(uint8_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter_x = ff_hevc_##PEL##_filters[mx - 1]; \ - const int8_t *filter_y = ff_hevc_##PEL##_filters[my - 1]; \ + const int8_t *filter_x = ff_hevc_##PEL##_filters[mx]; \ + const int8_t *filter_y = ff_hevc_##PEL##_filters[my]; \ \ hevc_hv_##TAP##t_##WIDTH##w_lsx(src, src_stride, dst, dst_stride, \ filter_x, filter_y, height); \ diff --git a/libavcodec/loongarch/hevc_mc_uniw_lsx.c b/libavcodec/loongarch/hevc_mc_uniw_lsx.c index c4e79225d38..0ced8d49206 100644 --- a/libavcodec/loongarch/hevc_mc_uniw_lsx.c +++ b/libavcodec/loongarch/hevc_mc_uniw_lsx.c @@ -280,8 +280,8 @@ void ff_hevc_put_hevc_uni_w_##PEL##_hv##WIDTH##_8_lsx(uint8_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter_x = ff_hevc_##PEL##_filters[mx - 1]; \ - const int8_t *filter_y = ff_hevc_##PEL##_filters[my - 1]; \ + const int8_t *filter_x = ff_hevc_##PEL##_filters[mx]; \ + const int8_t *filter_y = ff_hevc_##PEL##_filters[my]; \ int shift = denom + 14 - 8; \ \ hevc_hv_##TAP##t_##WIDTH##w_lsx(src, src_stride, dst, dst_stride, filter_x,\ diff --git a/libavcodec/loongarch/hevcdsp_init_loongarch.c b/libavcodec/loongarch/hevcdsp_init_loongarch.c index 22739c6f5b5..1585bda276b 100644 --- a/libavcodec/loongarch/hevcdsp_init_loongarch.c +++ b/libavcodec/loongarch/hevcdsp_init_loongarch.c @@ -22,6 +22,7 @@ #include "libavutil/loongarch/cpu.h" #include "hevcdsp_lsx.h" +#include "hevcdsp_lasx.h" void ff_hevc_dsp_init_loongarch(HEVCDSPContext *c, const int bit_depth) { @@ -123,8 +124,15 @@ void ff_hevc_dsp_init_loongarch(HEVCDSPContext *c, const int bit_depth) c->put_hevc_qpel_bi[8][0][1] = ff_hevc_put_hevc_bi_qpel_h48_8_lsx; c->put_hevc_qpel_bi[9][0][1] = ff_hevc_put_hevc_bi_qpel_h64_8_lsx; + c->put_hevc_epel_bi[1][0][1] = ff_hevc_put_hevc_bi_epel_h4_8_lsx; + c->put_hevc_epel_bi[2][0][1] = ff_hevc_put_hevc_bi_epel_h6_8_lsx; + c->put_hevc_epel_bi[3][0][1] = ff_hevc_put_hevc_bi_epel_h8_8_lsx; + c->put_hevc_epel_bi[4][0][1] = ff_hevc_put_hevc_bi_epel_h12_8_lsx; + c->put_hevc_epel_bi[5][0][1] = ff_hevc_put_hevc_bi_epel_h16_8_lsx; c->put_hevc_epel_bi[6][0][1] = ff_hevc_put_hevc_bi_epel_h24_8_lsx; c->put_hevc_epel_bi[7][0][1] = ff_hevc_put_hevc_bi_epel_h32_8_lsx; + c->put_hevc_epel_bi[8][0][1] = ff_hevc_put_hevc_bi_epel_h48_8_lsx; + c->put_hevc_epel_bi[9][0][1] = ff_hevc_put_hevc_bi_epel_h64_8_lsx; c->put_hevc_epel_bi[4][1][0] = ff_hevc_put_hevc_bi_epel_v12_8_lsx; c->put_hevc_epel_bi[5][1][0] = ff_hevc_put_hevc_bi_epel_v16_8_lsx; @@ -137,6 +145,14 @@ void ff_hevc_dsp_init_loongarch(HEVCDSPContext *c, const int bit_depth) c->put_hevc_epel_bi[6][1][1] = ff_hevc_put_hevc_bi_epel_hv24_8_lsx; c->put_hevc_epel_bi[7][1][1] = ff_hevc_put_hevc_bi_epel_hv32_8_lsx; + c->put_hevc_qpel_uni[1][0][1] = ff_hevc_put_hevc_uni_qpel_h4_8_lsx; + c->put_hevc_qpel_uni[2][0][1] = ff_hevc_put_hevc_uni_qpel_h6_8_lsx; + c->put_hevc_qpel_uni[3][0][1] = ff_hevc_put_hevc_uni_qpel_h8_8_lsx; + c->put_hevc_qpel_uni[4][0][1] = ff_hevc_put_hevc_uni_qpel_h12_8_lsx; + c->put_hevc_qpel_uni[5][0][1] = ff_hevc_put_hevc_uni_qpel_h16_8_lsx; + c->put_hevc_qpel_uni[6][0][1] = ff_hevc_put_hevc_uni_qpel_h24_8_lsx; + c->put_hevc_qpel_uni[7][0][1] = ff_hevc_put_hevc_uni_qpel_h32_8_lsx; + c->put_hevc_qpel_uni[8][0][1] = ff_hevc_put_hevc_uni_qpel_h48_8_lsx; c->put_hevc_qpel_uni[9][0][1] = ff_hevc_put_hevc_uni_qpel_h64_8_lsx; c->put_hevc_qpel_uni[6][1][0] = ff_hevc_put_hevc_uni_qpel_v24_8_lsx; @@ -160,6 +176,56 @@ void ff_hevc_dsp_init_loongarch(HEVCDSPContext *c, const int bit_depth) c->put_hevc_epel_uni[6][1][1] = ff_hevc_put_hevc_uni_epel_hv24_8_lsx; c->put_hevc_epel_uni[7][1][1] = ff_hevc_put_hevc_uni_epel_hv32_8_lsx; + c->put_hevc_qpel_uni_w[1][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels4_8_lsx; + c->put_hevc_qpel_uni_w[2][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels6_8_lsx; + c->put_hevc_qpel_uni_w[3][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels8_8_lsx; + c->put_hevc_qpel_uni_w[4][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels12_8_lsx; + c->put_hevc_qpel_uni_w[5][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels16_8_lsx; + c->put_hevc_qpel_uni_w[6][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels24_8_lsx; + c->put_hevc_qpel_uni_w[7][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels32_8_lsx; + c->put_hevc_qpel_uni_w[8][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels48_8_lsx; + c->put_hevc_qpel_uni_w[9][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels64_8_lsx; + + c->put_hevc_epel_uni_w[1][1][1] = ff_hevc_put_hevc_epel_uni_w_hv4_8_lsx; + c->put_hevc_epel_uni_w[2][1][1] = ff_hevc_put_hevc_epel_uni_w_hv6_8_lsx; + c->put_hevc_epel_uni_w[3][1][1] = ff_hevc_put_hevc_epel_uni_w_hv8_8_lsx; + c->put_hevc_epel_uni_w[4][1][1] = ff_hevc_put_hevc_epel_uni_w_hv12_8_lsx; + c->put_hevc_epel_uni_w[5][1][1] = ff_hevc_put_hevc_epel_uni_w_hv16_8_lsx; + c->put_hevc_epel_uni_w[6][1][1] = ff_hevc_put_hevc_epel_uni_w_hv24_8_lsx; + c->put_hevc_epel_uni_w[7][1][1] = ff_hevc_put_hevc_epel_uni_w_hv32_8_lsx; + c->put_hevc_epel_uni_w[8][1][1] = ff_hevc_put_hevc_epel_uni_w_hv48_8_lsx; + c->put_hevc_epel_uni_w[9][1][1] = ff_hevc_put_hevc_epel_uni_w_hv64_8_lsx; + + c->put_hevc_epel_uni_w[1][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels4_8_lsx; + c->put_hevc_epel_uni_w[2][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels6_8_lsx; + c->put_hevc_epel_uni_w[3][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels8_8_lsx; + c->put_hevc_epel_uni_w[4][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels12_8_lsx; + c->put_hevc_epel_uni_w[5][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels16_8_lsx; + c->put_hevc_epel_uni_w[6][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels24_8_lsx; + c->put_hevc_epel_uni_w[7][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels32_8_lsx; + c->put_hevc_epel_uni_w[8][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels48_8_lsx; + c->put_hevc_epel_uni_w[9][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels64_8_lsx; + + c->put_hevc_epel_uni_w[1][0][1] = ff_hevc_put_hevc_epel_uni_w_h4_8_lsx; + c->put_hevc_epel_uni_w[2][0][1] = ff_hevc_put_hevc_epel_uni_w_h6_8_lsx; + c->put_hevc_epel_uni_w[3][0][1] = ff_hevc_put_hevc_epel_uni_w_h8_8_lsx; + c->put_hevc_epel_uni_w[4][0][1] = ff_hevc_put_hevc_epel_uni_w_h12_8_lsx; + c->put_hevc_epel_uni_w[5][0][1] = ff_hevc_put_hevc_epel_uni_w_h16_8_lsx; + c->put_hevc_epel_uni_w[6][0][1] = ff_hevc_put_hevc_epel_uni_w_h24_8_lsx; + c->put_hevc_epel_uni_w[7][0][1] = ff_hevc_put_hevc_epel_uni_w_h32_8_lsx; + c->put_hevc_epel_uni_w[8][0][1] = ff_hevc_put_hevc_epel_uni_w_h48_8_lsx; + c->put_hevc_epel_uni_w[9][0][1] = ff_hevc_put_hevc_epel_uni_w_h64_8_lsx; + + c->put_hevc_epel_uni_w[1][1][0] = ff_hevc_put_hevc_epel_uni_w_v4_8_lsx; + c->put_hevc_epel_uni_w[2][1][0] = ff_hevc_put_hevc_epel_uni_w_v6_8_lsx; + c->put_hevc_epel_uni_w[3][1][0] = ff_hevc_put_hevc_epel_uni_w_v8_8_lsx; + c->put_hevc_epel_uni_w[4][1][0] = ff_hevc_put_hevc_epel_uni_w_v12_8_lsx; + c->put_hevc_epel_uni_w[5][1][0] = ff_hevc_put_hevc_epel_uni_w_v16_8_lsx; + c->put_hevc_epel_uni_w[6][1][0] = ff_hevc_put_hevc_epel_uni_w_v24_8_lsx; + c->put_hevc_epel_uni_w[7][1][0] = ff_hevc_put_hevc_epel_uni_w_v32_8_lsx; + c->put_hevc_epel_uni_w[8][1][0] = ff_hevc_put_hevc_epel_uni_w_v48_8_lsx; + c->put_hevc_epel_uni_w[9][1][0] = ff_hevc_put_hevc_epel_uni_w_v64_8_lsx; + c->put_hevc_qpel_uni_w[3][1][1] = ff_hevc_put_hevc_uni_w_qpel_hv8_8_lsx; c->put_hevc_qpel_uni_w[5][1][1] = ff_hevc_put_hevc_uni_w_qpel_hv16_8_lsx; c->put_hevc_qpel_uni_w[6][1][1] = ff_hevc_put_hevc_uni_w_qpel_hv24_8_lsx; @@ -167,6 +233,30 @@ void ff_hevc_dsp_init_loongarch(HEVCDSPContext *c, const int bit_depth) c->put_hevc_qpel_uni_w[8][1][1] = ff_hevc_put_hevc_uni_w_qpel_hv48_8_lsx; c->put_hevc_qpel_uni_w[9][1][1] = ff_hevc_put_hevc_uni_w_qpel_hv64_8_lsx; + c->put_hevc_qpel_uni_w[1][1][0] = ff_hevc_put_hevc_qpel_uni_w_v4_8_lsx; + c->put_hevc_qpel_uni_w[2][1][0] = ff_hevc_put_hevc_qpel_uni_w_v6_8_lsx; + c->put_hevc_qpel_uni_w[3][1][0] = ff_hevc_put_hevc_qpel_uni_w_v8_8_lsx; + c->put_hevc_qpel_uni_w[4][1][0] = ff_hevc_put_hevc_qpel_uni_w_v12_8_lsx; + c->put_hevc_qpel_uni_w[5][1][0] = ff_hevc_put_hevc_qpel_uni_w_v16_8_lsx; + c->put_hevc_qpel_uni_w[6][1][0] = ff_hevc_put_hevc_qpel_uni_w_v24_8_lsx; + c->put_hevc_qpel_uni_w[7][1][0] = ff_hevc_put_hevc_qpel_uni_w_v32_8_lsx; + c->put_hevc_qpel_uni_w[8][1][0] = ff_hevc_put_hevc_qpel_uni_w_v48_8_lsx; + c->put_hevc_qpel_uni_w[9][1][0] = ff_hevc_put_hevc_qpel_uni_w_v64_8_lsx; + + c->put_hevc_qpel_uni_w[1][0][1] = ff_hevc_put_hevc_qpel_uni_w_h4_8_lsx; + c->put_hevc_qpel_uni_w[2][0][1] = ff_hevc_put_hevc_qpel_uni_w_h6_8_lsx; + c->put_hevc_qpel_uni_w[3][0][1] = ff_hevc_put_hevc_qpel_uni_w_h8_8_lsx; + c->put_hevc_qpel_uni_w[4][0][1] = ff_hevc_put_hevc_qpel_uni_w_h12_8_lsx; + c->put_hevc_qpel_uni_w[5][0][1] = ff_hevc_put_hevc_qpel_uni_w_h16_8_lsx; + c->put_hevc_qpel_uni_w[6][0][1] = ff_hevc_put_hevc_qpel_uni_w_h24_8_lsx; + c->put_hevc_qpel_uni_w[7][0][1] = ff_hevc_put_hevc_qpel_uni_w_h32_8_lsx; + c->put_hevc_qpel_uni_w[8][0][1] = ff_hevc_put_hevc_qpel_uni_w_h48_8_lsx; + c->put_hevc_qpel_uni_w[9][0][1] = ff_hevc_put_hevc_qpel_uni_w_h64_8_lsx; + + c->sao_edge_filter[0] = ff_hevc_sao_edge_filter_8_lsx; + c->sao_edge_filter[1] = ff_hevc_sao_edge_filter_8_lsx; + c->sao_edge_filter[2] = ff_hevc_sao_edge_filter_8_lsx; + c->sao_edge_filter[3] = ff_hevc_sao_edge_filter_8_lsx; c->sao_edge_filter[4] = ff_hevc_sao_edge_filter_8_lsx; c->hevc_h_loop_filter_luma = ff_hevc_loop_filter_luma_h_8_lsx; @@ -185,6 +275,93 @@ void ff_hevc_dsp_init_loongarch(HEVCDSPContext *c, const int bit_depth) c->idct[1] = ff_hevc_idct_8x8_lsx; c->idct[2] = ff_hevc_idct_16x16_lsx; c->idct[3] = ff_hevc_idct_32x32_lsx; + + c->add_residual[0] = ff_hevc_add_residual4x4_8_lsx; + c->add_residual[1] = ff_hevc_add_residual8x8_8_lsx; + c->add_residual[2] = ff_hevc_add_residual16x16_8_lsx; + c->add_residual[3] = ff_hevc_add_residual32x32_8_lsx; + } + } + + if (have_lasx(cpu_flags)) { + if (bit_depth == 8) { + c->put_hevc_qpel_uni_w[2][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels6_8_lasx; + c->put_hevc_qpel_uni_w[3][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels8_8_lasx; + c->put_hevc_qpel_uni_w[4][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels12_8_lasx; + c->put_hevc_qpel_uni_w[5][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels16_8_lasx; + c->put_hevc_qpel_uni_w[6][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels24_8_lasx; + c->put_hevc_qpel_uni_w[7][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels32_8_lasx; + c->put_hevc_qpel_uni_w[8][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels48_8_lasx; + c->put_hevc_qpel_uni_w[9][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels64_8_lasx; + + c->put_hevc_epel_uni_w[2][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels6_8_lasx; + c->put_hevc_epel_uni_w[3][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels8_8_lasx; + c->put_hevc_epel_uni_w[4][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels12_8_lasx; + c->put_hevc_epel_uni_w[5][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels16_8_lasx; + c->put_hevc_epel_uni_w[6][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels24_8_lasx; + c->put_hevc_epel_uni_w[7][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels32_8_lasx; + c->put_hevc_epel_uni_w[8][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels48_8_lasx; + c->put_hevc_epel_uni_w[9][0][0] = ff_hevc_put_hevc_pel_uni_w_pixels64_8_lasx; + + c->put_hevc_epel_uni_w[2][1][1] = ff_hevc_put_hevc_epel_uni_w_hv6_8_lasx; + c->put_hevc_epel_uni_w[3][1][1] = ff_hevc_put_hevc_epel_uni_w_hv8_8_lasx; + c->put_hevc_epel_uni_w[4][1][1] = ff_hevc_put_hevc_epel_uni_w_hv12_8_lasx; + c->put_hevc_epel_uni_w[5][1][1] = ff_hevc_put_hevc_epel_uni_w_hv16_8_lasx; + c->put_hevc_epel_uni_w[6][1][1] = ff_hevc_put_hevc_epel_uni_w_hv24_8_lasx; + c->put_hevc_epel_uni_w[7][1][1] = ff_hevc_put_hevc_epel_uni_w_hv32_8_lasx; + c->put_hevc_epel_uni_w[8][1][1] = ff_hevc_put_hevc_epel_uni_w_hv48_8_lasx; + c->put_hevc_epel_uni_w[9][1][1] = ff_hevc_put_hevc_epel_uni_w_hv64_8_lasx; + + c->put_hevc_epel_uni_w[2][0][1] = ff_hevc_put_hevc_epel_uni_w_h6_8_lasx; + c->put_hevc_epel_uni_w[3][0][1] = ff_hevc_put_hevc_epel_uni_w_h8_8_lasx; + c->put_hevc_epel_uni_w[4][0][1] = ff_hevc_put_hevc_epel_uni_w_h12_8_lasx; + c->put_hevc_epel_uni_w[5][0][1] = ff_hevc_put_hevc_epel_uni_w_h16_8_lasx; + c->put_hevc_epel_uni_w[6][0][1] = ff_hevc_put_hevc_epel_uni_w_h24_8_lasx; + c->put_hevc_epel_uni_w[7][0][1] = ff_hevc_put_hevc_epel_uni_w_h32_8_lasx; + c->put_hevc_epel_uni_w[8][0][1] = ff_hevc_put_hevc_epel_uni_w_h48_8_lasx; + c->put_hevc_epel_uni_w[9][0][1] = ff_hevc_put_hevc_epel_uni_w_h64_8_lasx; + + c->put_hevc_qpel_uni_w[3][1][0] = ff_hevc_put_hevc_qpel_uni_w_v8_8_lasx; + c->put_hevc_qpel_uni_w[4][1][0] = ff_hevc_put_hevc_qpel_uni_w_v12_8_lasx; + c->put_hevc_qpel_uni_w[5][1][0] = ff_hevc_put_hevc_qpel_uni_w_v16_8_lasx; + c->put_hevc_qpel_uni_w[6][1][0] = ff_hevc_put_hevc_qpel_uni_w_v24_8_lasx; + c->put_hevc_qpel_uni_w[7][1][0] = ff_hevc_put_hevc_qpel_uni_w_v32_8_lasx; + c->put_hevc_qpel_uni_w[8][1][0] = ff_hevc_put_hevc_qpel_uni_w_v48_8_lasx; + c->put_hevc_qpel_uni_w[9][1][0] = ff_hevc_put_hevc_qpel_uni_w_v64_8_lasx; + + c->put_hevc_epel_uni_w[2][1][0] = ff_hevc_put_hevc_epel_uni_w_v6_8_lasx; + c->put_hevc_epel_uni_w[3][1][0] = ff_hevc_put_hevc_epel_uni_w_v8_8_lasx; + c->put_hevc_epel_uni_w[4][1][0] = ff_hevc_put_hevc_epel_uni_w_v12_8_lasx; + c->put_hevc_epel_uni_w[5][1][0] = ff_hevc_put_hevc_epel_uni_w_v16_8_lasx; + c->put_hevc_epel_uni_w[6][1][0] = ff_hevc_put_hevc_epel_uni_w_v24_8_lasx; + c->put_hevc_epel_uni_w[7][1][0] = ff_hevc_put_hevc_epel_uni_w_v32_8_lasx; + c->put_hevc_epel_uni_w[8][1][0] = ff_hevc_put_hevc_epel_uni_w_v48_8_lasx; + c->put_hevc_epel_uni_w[9][1][0] = ff_hevc_put_hevc_epel_uni_w_v64_8_lasx; + + c->put_hevc_qpel_uni_w[1][0][1] = ff_hevc_put_hevc_qpel_uni_w_h4_8_lasx; + c->put_hevc_qpel_uni_w[2][0][1] = ff_hevc_put_hevc_qpel_uni_w_h6_8_lasx; + c->put_hevc_qpel_uni_w[3][0][1] = ff_hevc_put_hevc_qpel_uni_w_h8_8_lasx; + c->put_hevc_qpel_uni_w[4][0][1] = ff_hevc_put_hevc_qpel_uni_w_h12_8_lasx; + c->put_hevc_qpel_uni_w[5][0][1] = ff_hevc_put_hevc_qpel_uni_w_h16_8_lasx; + c->put_hevc_qpel_uni_w[6][0][1] = ff_hevc_put_hevc_qpel_uni_w_h24_8_lasx; + c->put_hevc_qpel_uni_w[7][0][1] = ff_hevc_put_hevc_qpel_uni_w_h32_8_lasx; + c->put_hevc_qpel_uni_w[8][0][1] = ff_hevc_put_hevc_qpel_uni_w_h48_8_lasx; + c->put_hevc_qpel_uni_w[9][0][1] = ff_hevc_put_hevc_qpel_uni_w_h64_8_lasx; + + c->put_hevc_qpel_uni[4][0][1] = ff_hevc_put_hevc_uni_qpel_h12_8_lasx; + c->put_hevc_qpel_uni[5][0][1] = ff_hevc_put_hevc_uni_qpel_h16_8_lasx; + c->put_hevc_qpel_uni[6][0][1] = ff_hevc_put_hevc_uni_qpel_h24_8_lasx; + c->put_hevc_qpel_uni[7][0][1] = ff_hevc_put_hevc_uni_qpel_h32_8_lasx; + c->put_hevc_qpel_uni[8][0][1] = ff_hevc_put_hevc_uni_qpel_h48_8_lasx; + c->put_hevc_qpel_uni[9][0][1] = ff_hevc_put_hevc_uni_qpel_h64_8_lasx; + + c->put_hevc_epel_bi[4][0][1] = ff_hevc_put_hevc_bi_epel_h12_8_lasx; + c->put_hevc_epel_bi[5][0][1] = ff_hevc_put_hevc_bi_epel_h16_8_lasx; + c->put_hevc_epel_bi[7][0][1] = ff_hevc_put_hevc_bi_epel_h32_8_lasx; + c->put_hevc_epel_bi[8][0][1] = ff_hevc_put_hevc_bi_epel_h48_8_lasx; + c->put_hevc_epel_bi[9][0][1] = ff_hevc_put_hevc_bi_epel_h64_8_lasx; + + c->idct[3] = ff_hevc_idct_32x32_lasx; } } } diff --git a/libavcodec/loongarch/hevcdsp_lasx.h b/libavcodec/loongarch/hevcdsp_lasx.h new file mode 100644 index 00000000000..714cbf58806 --- /dev/null +++ b/libavcodec/loongarch/hevcdsp_lasx.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2023 Loongson Technology Corporation Limited + * Contributed by jinbo + * + * 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 + */ + +#ifndef AVCODEC_LOONGARCH_HEVCDSP_LASX_H +#define AVCODEC_LOONGARCH_HEVCDSP_LASX_H + +#include "libavcodec/hevcdsp.h" + +#define PEL_UNI_W(PEL, DIR, WIDTH) \ +void ff_hevc_put_hevc_##PEL##_uni_w_##DIR##WIDTH##_8_lasx(uint8_t *dst, \ + ptrdiff_t \ + dst_stride, \ + const uint8_t *src, \ + ptrdiff_t \ + src_stride, \ + int height, \ + int denom, \ + int wx, \ + int ox, \ + intptr_t mx, \ + intptr_t my, \ + int width) + +PEL_UNI_W(pel, pixels, 6); +PEL_UNI_W(pel, pixels, 8); +PEL_UNI_W(pel, pixels, 12); +PEL_UNI_W(pel, pixels, 16); +PEL_UNI_W(pel, pixels, 24); +PEL_UNI_W(pel, pixels, 32); +PEL_UNI_W(pel, pixels, 48); +PEL_UNI_W(pel, pixels, 64); + +PEL_UNI_W(qpel, v, 8); +PEL_UNI_W(qpel, v, 12); +PEL_UNI_W(qpel, v, 16); +PEL_UNI_W(qpel, v, 24); +PEL_UNI_W(qpel, v, 32); +PEL_UNI_W(qpel, v, 48); +PEL_UNI_W(qpel, v, 64); + +PEL_UNI_W(qpel, h, 4); +PEL_UNI_W(qpel, h, 6); +PEL_UNI_W(qpel, h, 8); +PEL_UNI_W(qpel, h, 12); +PEL_UNI_W(qpel, h, 16); +PEL_UNI_W(qpel, h, 24); +PEL_UNI_W(qpel, h, 32); +PEL_UNI_W(qpel, h, 48); +PEL_UNI_W(qpel, h, 64); + +PEL_UNI_W(epel, hv, 6); +PEL_UNI_W(epel, hv, 8); +PEL_UNI_W(epel, hv, 12); +PEL_UNI_W(epel, hv, 16); +PEL_UNI_W(epel, hv, 24); +PEL_UNI_W(epel, hv, 32); +PEL_UNI_W(epel, hv, 48); +PEL_UNI_W(epel, hv, 64); + +PEL_UNI_W(epel, v, 6); +PEL_UNI_W(epel, v, 8); +PEL_UNI_W(epel, v, 12); +PEL_UNI_W(epel, v, 16); +PEL_UNI_W(epel, v, 24); +PEL_UNI_W(epel, v, 32); +PEL_UNI_W(epel, v, 48); +PEL_UNI_W(epel, v, 64); + +PEL_UNI_W(epel, h, 6); +PEL_UNI_W(epel, h, 8); +PEL_UNI_W(epel, h, 12); +PEL_UNI_W(epel, h, 16); +PEL_UNI_W(epel, h, 24); +PEL_UNI_W(epel, h, 32); +PEL_UNI_W(epel, h, 48); +PEL_UNI_W(epel, h, 64); + +#undef PEL_UNI_W + +#define UNI_MC(PEL, DIR, WIDTH) \ +void ff_hevc_put_hevc_uni_##PEL##_##DIR##WIDTH##_8_lasx(uint8_t *dst, \ + ptrdiff_t dst_stride, \ + const uint8_t *src, \ + ptrdiff_t src_stride, \ + int height, \ + intptr_t mx, \ + intptr_t my, \ + int width) +UNI_MC(qpel, h, 12); +UNI_MC(qpel, h, 16); +UNI_MC(qpel, h, 24); +UNI_MC(qpel, h, 32); +UNI_MC(qpel, h, 48); +UNI_MC(qpel, h, 64); + +#undef UNI_MC + +#define BI_MC(PEL, DIR, WIDTH) \ +void ff_hevc_put_hevc_bi_##PEL##_##DIR##WIDTH##_8_lasx(uint8_t *dst, \ + ptrdiff_t dst_stride, \ + const uint8_t *src, \ + ptrdiff_t src_stride, \ + const int16_t *src_16bit, \ + int height, \ + intptr_t mx, \ + intptr_t my, \ + int width) +BI_MC(epel, h, 12); +BI_MC(epel, h, 16); +BI_MC(epel, h, 32); +BI_MC(epel, h, 48); +BI_MC(epel, h, 64); + +#undef BI_MC + +void ff_hevc_idct_32x32_lasx(int16_t *coeffs, int col_limit); + +#endif // #ifndef AVCODEC_LOONGARCH_HEVCDSP_LASX_H diff --git a/libavcodec/loongarch/hevcdsp_lsx.c b/libavcodec/loongarch/hevcdsp_lsx.c index 85843dd111c..57479255253 100644 --- a/libavcodec/loongarch/hevcdsp_lsx.c +++ b/libavcodec/loongarch/hevcdsp_lsx.c @@ -3233,7 +3233,7 @@ void ff_hevc_put_hevc_##PEL##_##DIR##WIDTH##_8_lsx(int16_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR - 1]; \ + const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR]; \ \ hevc_##DIR1##_##TAP##t_##WIDTH##w_lsx(src, src_stride, dst, \ MAX_PB_SIZE, filter, height); \ @@ -3274,8 +3274,8 @@ void ff_hevc_put_hevc_##PEL##_hv##WIDTH##_8_lsx(int16_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter_x = ff_hevc_##PEL##_filters[mx - 1]; \ - const int8_t *filter_y = ff_hevc_##PEL##_filters[my - 1]; \ + const int8_t *filter_x = ff_hevc_##PEL##_filters[mx]; \ + const int8_t *filter_y = ff_hevc_##PEL##_filters[my]; \ \ hevc_hv_##TAP##t_##WIDTH##w_lsx(src, src_stride, dst, MAX_PB_SIZE, \ filter_x, filter_y, height); \ diff --git a/libavcodec/loongarch/hevcdsp_lsx.h b/libavcodec/loongarch/hevcdsp_lsx.h index 0d54196cafe..a5ef237b5d5 100644 --- a/libavcodec/loongarch/hevcdsp_lsx.h +++ b/libavcodec/loongarch/hevcdsp_lsx.h @@ -126,8 +126,15 @@ BI_MC(qpel, hv, 32); BI_MC(qpel, hv, 48); BI_MC(qpel, hv, 64); +BI_MC(epel, h, 4); +BI_MC(epel, h, 6); +BI_MC(epel, h, 8); +BI_MC(epel, h, 12); +BI_MC(epel, h, 16); BI_MC(epel, h, 24); BI_MC(epel, h, 32); +BI_MC(epel, h, 48); +BI_MC(epel, h, 64); BI_MC(epel, v, 12); BI_MC(epel, v, 16); @@ -151,7 +158,14 @@ void ff_hevc_put_hevc_uni_##PEL##_##DIR##WIDTH##_8_lsx(uint8_t *dst, \ intptr_t mx, \ intptr_t my, \ int width) - +UNI_MC(qpel, h, 4); +UNI_MC(qpel, h, 6); +UNI_MC(qpel, h, 8); +UNI_MC(qpel, h, 12); +UNI_MC(qpel, h, 16); +UNI_MC(qpel, h, 24); +UNI_MC(qpel, h, 32); +UNI_MC(qpel, h, 48); UNI_MC(qpel, h, 64); UNI_MC(qpel, v, 24); @@ -227,4 +241,86 @@ void ff_hevc_idct_8x8_lsx(int16_t *coeffs, int col_limit); void ff_hevc_idct_16x16_lsx(int16_t *coeffs, int col_limit); void ff_hevc_idct_32x32_lsx(int16_t *coeffs, int col_limit); +void ff_hevc_add_residual4x4_8_lsx(uint8_t *dst, const int16_t *res, ptrdiff_t stride); +void ff_hevc_add_residual8x8_8_lsx(uint8_t *dst, const int16_t *res, ptrdiff_t stride); +void ff_hevc_add_residual16x16_8_lsx(uint8_t *dst, const int16_t *res, ptrdiff_t stride); +void ff_hevc_add_residual32x32_8_lsx(uint8_t *dst, const int16_t *res, ptrdiff_t stride); + +#define PEL_UNI_W(PEL, DIR, WIDTH) \ +void ff_hevc_put_hevc_##PEL##_uni_w_##DIR##WIDTH##_8_lsx(uint8_t *dst, \ + ptrdiff_t \ + dst_stride, \ + const uint8_t *src, \ + ptrdiff_t \ + src_stride, \ + int height, \ + int denom, \ + int wx, \ + int ox, \ + intptr_t mx, \ + intptr_t my, \ + int width) + +PEL_UNI_W(pel, pixels, 4); +PEL_UNI_W(pel, pixels, 6); +PEL_UNI_W(pel, pixels, 8); +PEL_UNI_W(pel, pixels, 12); +PEL_UNI_W(pel, pixels, 16); +PEL_UNI_W(pel, pixels, 24); +PEL_UNI_W(pel, pixels, 32); +PEL_UNI_W(pel, pixels, 48); +PEL_UNI_W(pel, pixels, 64); + +PEL_UNI_W(qpel, v, 4); +PEL_UNI_W(qpel, v, 6); +PEL_UNI_W(qpel, v, 8); +PEL_UNI_W(qpel, v, 12); +PEL_UNI_W(qpel, v, 16); +PEL_UNI_W(qpel, v, 24); +PEL_UNI_W(qpel, v, 32); +PEL_UNI_W(qpel, v, 48); +PEL_UNI_W(qpel, v, 64); + +PEL_UNI_W(qpel, h, 4); +PEL_UNI_W(qpel, h, 6); +PEL_UNI_W(qpel, h, 8); +PEL_UNI_W(qpel, h, 12); +PEL_UNI_W(qpel, h, 16); +PEL_UNI_W(qpel, h, 24); +PEL_UNI_W(qpel, h, 32); +PEL_UNI_W(qpel, h, 48); +PEL_UNI_W(qpel, h, 64); + +PEL_UNI_W(epel, hv, 4); +PEL_UNI_W(epel, hv, 6); +PEL_UNI_W(epel, hv, 8); +PEL_UNI_W(epel, hv, 12); +PEL_UNI_W(epel, hv, 16); +PEL_UNI_W(epel, hv, 24); +PEL_UNI_W(epel, hv, 32); +PEL_UNI_W(epel, hv, 48); +PEL_UNI_W(epel, hv, 64); + +PEL_UNI_W(epel, h, 4); +PEL_UNI_W(epel, h, 6); +PEL_UNI_W(epel, h, 8); +PEL_UNI_W(epel, h, 12); +PEL_UNI_W(epel, h, 16); +PEL_UNI_W(epel, h, 24); +PEL_UNI_W(epel, h, 32); +PEL_UNI_W(epel, h, 48); +PEL_UNI_W(epel, h, 64); + +PEL_UNI_W(epel, v, 4); +PEL_UNI_W(epel, v, 6); +PEL_UNI_W(epel, v, 8); +PEL_UNI_W(epel, v, 12); +PEL_UNI_W(epel, v, 16); +PEL_UNI_W(epel, v, 24); +PEL_UNI_W(epel, v, 32); +PEL_UNI_W(epel, v, 48); +PEL_UNI_W(epel, v, 64); + +#undef PEL_UNI_W + #endif // #ifndef AVCODEC_LOONGARCH_HEVCDSP_LSX_H diff --git a/libavcodec/loongarch/idctdsp_lasx.c b/libavcodec/loongarch/idctdsp_lasx.c index 1cfab0e0282..ff1fbf72bbf 100644 --- a/libavcodec/loongarch/idctdsp_lasx.c +++ b/libavcodec/loongarch/idctdsp_lasx.c @@ -23,7 +23,7 @@ #include "libavutil/loongarch/loongson_intrinsics.h" void ff_put_pixels_clamped_lasx(const int16_t *block, - uint8_t *av_restrict pixels, + uint8_t *restrict pixels, ptrdiff_t stride) { __m256i b0, b1, b2, b3; @@ -48,7 +48,7 @@ void ff_put_pixels_clamped_lasx(const int16_t *block, } void ff_put_signed_pixels_clamped_lasx(const int16_t *block, - uint8_t *av_restrict pixels, + uint8_t *restrict pixels, ptrdiff_t stride) { __m256i b0, b1, b2, b3; @@ -77,7 +77,7 @@ void ff_put_signed_pixels_clamped_lasx(const int16_t *block, } void ff_add_pixels_clamped_lasx(const int16_t *block, - uint8_t *av_restrict pixels, + uint8_t *restrict pixels, ptrdiff_t stride) { __m256i b0, b1, b2, b3; diff --git a/libavcodec/loongarch/idctdsp_loongarch.h b/libavcodec/loongarch/idctdsp_loongarch.h index cae8e7af584..c139179515c 100644 --- a/libavcodec/loongarch/idctdsp_loongarch.h +++ b/libavcodec/loongarch/idctdsp_loongarch.h @@ -29,13 +29,13 @@ void ff_simple_idct_lasx(int16_t *block); void ff_simple_idct_put_lasx(uint8_t *dest, ptrdiff_t stride_dst, int16_t *block); void ff_simple_idct_add_lasx(uint8_t *dest, ptrdiff_t stride_dst, int16_t *block); void ff_put_pixels_clamped_lasx(const int16_t *block, - uint8_t *av_restrict pixels, + uint8_t *restrict pixels, ptrdiff_t line_size); void ff_put_signed_pixels_clamped_lasx(const int16_t *block, - uint8_t *av_restrict pixels, + uint8_t *restrict pixels, ptrdiff_t line_size); void ff_add_pixels_clamped_lasx(const int16_t *block, - uint8_t *av_restrict pixels, + uint8_t *restrict pixels, ptrdiff_t line_size); #endif /* AVCODEC_LOONGARCH_IDCTDSP_LOONGARCH_H */ diff --git a/libavcodec/loongarch/loongson_asm.S b/libavcodec/loongarch/loongson_asm.S new file mode 100644 index 00000000000..0a649f51c7f --- /dev/null +++ b/libavcodec/loongarch/loongson_asm.S @@ -0,0 +1,945 @@ +/* + * Loongson asm helper. + * + * Copyright (c) 2022 Loongson Technology Corporation Limited + * Contributed by Gu Xiwei(guxiwei-hf@loongson.cn) + * Shiyou Yin(yinshiyou-hf@loongson.cn) + * + * 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 + */ + +/** + * MAJOR version: Macro usage changes. + * MINOR version: Add new functions, or bug fixes. + * MICRO version: Comment changes or implementation changes. + */ +#define LML_VERSION_MAJOR 0 +#define LML_VERSION_MINOR 2 +#define LML_VERSION_MICRO 0 + +/* + *============================================================================ + * macros for specific projetc, set them as needed. + * Following LoongML macros for your reference. + *============================================================================ + */ +#define ASM_PREF +#define DEFAULT_ALIGN 5 + +.macro function name, align=DEFAULT_ALIGN +.macro endfunc + jirl $r0, $r1, 0x0 + .size ASM_PREF\name, . - ASM_PREF\name + .purgem endfunc +.endm +.text ; +.align \align ; +.globl ASM_PREF\name ; +.type ASM_PREF\name, @function ; +ASM_PREF\name: ; +.endm + +/** + * Attention: If align is not zero, the macro will use + * t7 until the end of function + */ +.macro alloc_stack size, align=0 +.if \align + .macro clean_stack + add.d sp, sp, t7 + .endm + addi.d sp, sp, - \size + andi.d t7, sp, \align - 1 + sub.d sp, sp, t7 + addi.d t7, t7, \size +.else + .macro clean_stack + addi.d sp, sp, \size + .endm + addi.d sp, sp, - \size +.endif +.endm + +.macro const name, align=DEFAULT_ALIGN + .macro endconst + .size \name, . - \name + .purgem endconst + .endm +.section .rodata +.align \align +\name: +.endm + +/* + *============================================================================ + * LoongArch register alias + *============================================================================ + */ + +#define a0 $a0 +#define a1 $a1 +#define a2 $a2 +#define a3 $a3 +#define a4 $a4 +#define a5 $a5 +#define a6 $a6 +#define a7 $a7 + +#define t0 $t0 +#define t1 $t1 +#define t2 $t2 +#define t3 $t3 +#define t4 $t4 +#define t5 $t5 +#define t6 $t6 +#define t7 $t7 +#define t8 $t8 + +#define s0 $s0 +#define s1 $s1 +#define s2 $s2 +#define s3 $s3 +#define s4 $s4 +#define s5 $s5 +#define s6 $s6 +#define s7 $s7 +#define s8 $s8 + +#define zero $zero +#define sp $sp +#define ra $ra + +#define f0 $f0 +#define f1 $f1 +#define f2 $f2 +#define f3 $f3 +#define f4 $f4 +#define f5 $f5 +#define f6 $f6 +#define f7 $f7 +#define f8 $f8 +#define f9 $f9 +#define f10 $f10 +#define f11 $f11 +#define f12 $f12 +#define f13 $f13 +#define f14 $f14 +#define f15 $f15 +#define f16 $f16 +#define f17 $f17 +#define f18 $f18 +#define f19 $f19 +#define f20 $f20 +#define f21 $f21 +#define f22 $f22 +#define f23 $f23 +#define f24 $f24 +#define f25 $f25 +#define f26 $f26 +#define f27 $f27 +#define f28 $f28 +#define f29 $f29 +#define f30 $f30 +#define f31 $f31 + +#define vr0 $vr0 +#define vr1 $vr1 +#define vr2 $vr2 +#define vr3 $vr3 +#define vr4 $vr4 +#define vr5 $vr5 +#define vr6 $vr6 +#define vr7 $vr7 +#define vr8 $vr8 +#define vr9 $vr9 +#define vr10 $vr10 +#define vr11 $vr11 +#define vr12 $vr12 +#define vr13 $vr13 +#define vr14 $vr14 +#define vr15 $vr15 +#define vr16 $vr16 +#define vr17 $vr17 +#define vr18 $vr18 +#define vr19 $vr19 +#define vr20 $vr20 +#define vr21 $vr21 +#define vr22 $vr22 +#define vr23 $vr23 +#define vr24 $vr24 +#define vr25 $vr25 +#define vr26 $vr26 +#define vr27 $vr27 +#define vr28 $vr28 +#define vr29 $vr29 +#define vr30 $vr30 +#define vr31 $vr31 + +#define xr0 $xr0 +#define xr1 $xr1 +#define xr2 $xr2 +#define xr3 $xr3 +#define xr4 $xr4 +#define xr5 $xr5 +#define xr6 $xr6 +#define xr7 $xr7 +#define xr8 $xr8 +#define xr9 $xr9 +#define xr10 $xr10 +#define xr11 $xr11 +#define xr12 $xr12 +#define xr13 $xr13 +#define xr14 $xr14 +#define xr15 $xr15 +#define xr16 $xr16 +#define xr17 $xr17 +#define xr18 $xr18 +#define xr19 $xr19 +#define xr20 $xr20 +#define xr21 $xr21 +#define xr22 $xr22 +#define xr23 $xr23 +#define xr24 $xr24 +#define xr25 $xr25 +#define xr26 $xr26 +#define xr27 $xr27 +#define xr28 $xr28 +#define xr29 $xr29 +#define xr30 $xr30 +#define xr31 $xr31 + +/* + *============================================================================ + * LSX/LASX synthesize instructions + *============================================================================ + */ + +/* + * Description : Dot product of byte vector elements + * Arguments : Inputs - vj, vk + * Outputs - vd + * Return Type - halfword + */ +.macro vdp2.h.bu vd, vj, vk + vmulwev.h.bu \vd, \vj, \vk + vmaddwod.h.bu \vd, \vj, \vk +.endm + +.macro vdp2.h.bu.b vd, vj, vk + vmulwev.h.bu.b \vd, \vj, \vk + vmaddwod.h.bu.b \vd, \vj, \vk +.endm + +.macro vdp2.w.h vd, vj, vk + vmulwev.w.h \vd, \vj, \vk + vmaddwod.w.h \vd, \vj, \vk +.endm + +.macro xvdp2.h.bu xd, xj, xk + xvmulwev.h.bu \xd, \xj, \xk + xvmaddwod.h.bu \xd, \xj, \xk +.endm + +.macro xvdp2.h.bu.b xd, xj, xk + xvmulwev.h.bu.b \xd, \xj, \xk + xvmaddwod.h.bu.b \xd, \xj, \xk +.endm + +.macro xvdp2.w.h xd, xj, xk + xvmulwev.w.h \xd, \xj, \xk + xvmaddwod.w.h \xd, \xj, \xk +.endm + +/* + * Description : Dot product & addition of halfword vector elements + * Arguments : Inputs - vj, vk + * Outputs - vd + * Return Type - twice size of input + */ +.macro vdp2add.h.bu vd, vj, vk + vmaddwev.h.bu \vd, \vj, \vk + vmaddwod.h.bu \vd, \vj, \vk +.endm + +.macro vdp2add.h.bu.b vd, vj, vk + vmaddwev.h.bu.b \vd, \vj, \vk + vmaddwod.h.bu.b \vd, \vj, \vk +.endm + +.macro vdp2add.w.h vd, vj, vk + vmaddwev.w.h \vd, \vj, \vk + vmaddwod.w.h \vd, \vj, \vk +.endm + +.macro xvdp2add.h.bu.b xd, xj, xk + xvmaddwev.h.bu.b \xd, \xj, \xk + xvmaddwod.h.bu.b \xd, \xj, \xk +.endm + +.macro xvdp2add.w.h xd, xj, xk + xvmaddwev.w.h \xd, \xj, \xk + xvmaddwod.w.h \xd, \xj, \xk +.endm + +/* + * Description : Range each element of vector + * clip: vj > vk ? vj : vk && vj < va ? vj : va + * clip255: vj < 255 ? vj : 255 && vj > 0 ? vj : 0 + */ +.macro vclip.h vd, vj, vk, va + vmax.h \vd, \vj, \vk + vmin.h \vd, \vd, \va +.endm + +.macro vclip255.w vd, vj + vmaxi.w \vd, \vj, 0 + vsat.wu \vd, \vd, 7 +.endm + +.macro vclip255.h vd, vj + vmaxi.h \vd, \vj, 0 + vsat.hu \vd, \vd, 7 +.endm + +.macro xvclip.h xd, xj, xk, xa + xvmax.h \xd, \xj, \xk + xvmin.h \xd, \xd, \xa +.endm + +.macro xvclip255.h xd, xj + xvmaxi.h \xd, \xj, 0 + xvsat.hu \xd, \xd, 7 +.endm + +.macro xvclip255.w xd, xj + xvmaxi.w \xd, \xj, 0 + xvsat.wu \xd, \xd, 7 +.endm + +/* + * Description : Store elements of vector + * vd : Data vector to be stroed + * rk : Address of data storage + * ra : Offset of address + * si : Index of data in vd + */ +.macro vstelmx.b vd, rk, ra, si + add.d \rk, \rk, \ra + vstelm.b \vd, \rk, 0, \si +.endm + +.macro vstelmx.h vd, rk, ra, si + add.d \rk, \rk, \ra + vstelm.h \vd, \rk, 0, \si +.endm + +.macro vstelmx.w vd, rk, ra, si + add.d \rk, \rk, \ra + vstelm.w \vd, \rk, 0, \si +.endm + +.macro vstelmx.d vd, rk, ra, si + add.d \rk, \rk, \ra + vstelm.d \vd, \rk, 0, \si +.endm + +.macro vmov xd, xj + vor.v \xd, \xj, \xj +.endm + +.macro xmov xd, xj + xvor.v \xd, \xj, \xj +.endm + +.macro xvstelmx.d xd, rk, ra, si + add.d \rk, \rk, \ra + xvstelm.d \xd, \rk, 0, \si +.endm + +/* + *============================================================================ + * LSX/LASX custom macros + *============================================================================ + */ + +/* + * Load 4 float, double, V128, v256 elements with stride. + */ +.macro FLDS_LOADX_4 src, stride, stride2, stride3, out0, out1, out2, out3 + fld.s \out0, \src, 0 + fldx.s \out1, \src, \stride + fldx.s \out2, \src, \stride2 + fldx.s \out3, \src, \stride3 +.endm + +.macro FLDD_LOADX_4 src, stride, stride2, stride3, out0, out1, out2, out3 + fld.d \out0, \src, 0 + fldx.d \out1, \src, \stride + fldx.d \out2, \src, \stride2 + fldx.d \out3, \src, \stride3 +.endm + +.macro LSX_LOADX_4 src, stride, stride2, stride3, out0, out1, out2, out3 + vld \out0, \src, 0 + vldx \out1, \src, \stride + vldx \out2, \src, \stride2 + vldx \out3, \src, \stride3 +.endm + +.macro LASX_LOADX_4 src, stride, stride2, stride3, out0, out1, out2, out3 + xvld \out0, \src, 0 + xvldx \out1, \src, \stride + xvldx \out2, \src, \stride2 + xvldx \out3, \src, \stride3 +.endm + +/* + * Description : Transpose 4x4 block with half-word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + */ +.macro LSX_TRANSPOSE4x4_H in0, in1, in2, in3, out0, out1, out2, out3, \ + tmp0, tmp1 + vilvl.h \tmp0, \in1, \in0 + vilvl.h \tmp1, \in3, \in2 + vilvl.w \out0, \tmp1, \tmp0 + vilvh.w \out2, \tmp1, \tmp0 + vilvh.d \out1, \out0, \out0 + vilvh.d \out3, \out0, \out2 +.endm + +/* + * Description : Transpose 4x4 block with word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + * Details : + * Example : + * 1, 2, 3, 4 1, 5, 9,13 + * 5, 6, 7, 8 to 2, 6,10,14 + * 9,10,11,12 =====> 3, 7,11,15 + * 13,14,15,16 4, 8,12,16 + */ +.macro LSX_TRANSPOSE4x4_W _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3, \ + _tmp0, _tmp1 + + vilvl.w \_tmp0, \_in1, \_in0 + vilvh.w \_out1, \_in1, \_in0 + vilvl.w \_tmp1, \_in3, \_in2 + vilvh.w \_out3, \_in3, \_in2 + + vilvl.d \_out0, \_tmp1, \_tmp0 + vilvl.d \_out2, \_out3, \_out1 + vilvh.d \_out3, \_out3, \_out1 + vilvh.d \_out1, \_tmp1, \_tmp0 +.endm + +/* + * Description : Transpose 8x8 block with half-word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7 + * Outputs - out0, out1, out2, out3, out4, out5, out6, out7 + */ +.macro LSX_TRANSPOSE8x8_H in0, in1, in2, in3, in4, in5, in6, in7, out0, out1, \ + out2, out3, out4, out5, out6, out7, tmp0, tmp1, tmp2, \ + tmp3, tmp4, tmp5, tmp6, tmp7 + vilvl.h \tmp0, \in6, \in4 + vilvl.h \tmp1, \in7, \in5 + vilvl.h \tmp2, \in2, \in0 + vilvl.h \tmp3, \in3, \in1 + + vilvl.h \tmp4, \tmp1, \tmp0 + vilvh.h \tmp5, \tmp1, \tmp0 + vilvl.h \tmp6, \tmp3, \tmp2 + vilvh.h \tmp7, \tmp3, \tmp2 + + vilvh.h \tmp0, \in6, \in4 + vilvh.h \tmp1, \in7, \in5 + vilvh.h \tmp2, \in2, \in0 + vilvh.h \tmp3, \in3, \in1 + + vpickev.d \out0, \tmp4, \tmp6 + vpickod.d \out1, \tmp4, \tmp6 + vpickev.d \out2, \tmp5, \tmp7 + vpickod.d \out3, \tmp5, \tmp7 + + vilvl.h \tmp4, \tmp1, \tmp0 + vilvh.h \tmp5, \tmp1, \tmp0 + vilvl.h \tmp6, \tmp3, \tmp2 + vilvh.h \tmp7, \tmp3, \tmp2 + + vpickev.d \out4, \tmp4, \tmp6 + vpickod.d \out5, \tmp4, \tmp6 + vpickev.d \out6, \tmp5, \tmp7 + vpickod.d \out7, \tmp5, \tmp7 +.endm + +/* + * Description : Transpose 16x8 block with byte elements in vectors + * Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7 + * Outputs - out0, out1, out2, out3, out4, out5, out6, out7 + */ +.macro LASX_TRANSPOSE16X8_B in0, in1, in2, in3, in4, in5, in6, in7, \ + in8, in9, in10, in11, in12, in13, in14, in15, \ + out0, out1, out2, out3, out4, out5, out6, out7,\ + tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 + xvilvl.b \tmp0, \in2, \in0 + xvilvl.b \tmp1, \in3, \in1 + xvilvl.b \tmp2, \in6, \in4 + xvilvl.b \tmp3, \in7, \in5 + xvilvl.b \tmp4, \in10, \in8 + xvilvl.b \tmp5, \in11, \in9 + xvilvl.b \tmp6, \in14, \in12 + xvilvl.b \tmp7, \in15, \in13 + xvilvl.b \out0, \tmp1, \tmp0 + xvilvh.b \out1, \tmp1, \tmp0 + xvilvl.b \out2, \tmp3, \tmp2 + xvilvh.b \out3, \tmp3, \tmp2 + xvilvl.b \out4, \tmp5, \tmp4 + xvilvh.b \out5, \tmp5, \tmp4 + xvilvl.b \out6, \tmp7, \tmp6 + xvilvh.b \out7, \tmp7, \tmp6 + xvilvl.w \tmp0, \out2, \out0 + xvilvh.w \tmp2, \out2, \out0 + xvilvl.w \tmp4, \out3, \out1 + xvilvh.w \tmp6, \out3, \out1 + xvilvl.w \tmp1, \out6, \out4 + xvilvh.w \tmp3, \out6, \out4 + xvilvl.w \tmp5, \out7, \out5 + xvilvh.w \tmp7, \out7, \out5 + xvilvl.d \out0, \tmp1, \tmp0 + xvilvh.d \out1, \tmp1, \tmp0 + xvilvl.d \out2, \tmp3, \tmp2 + xvilvh.d \out3, \tmp3, \tmp2 + xvilvl.d \out4, \tmp5, \tmp4 + xvilvh.d \out5, \tmp5, \tmp4 + xvilvl.d \out6, \tmp7, \tmp6 + xvilvh.d \out7, \tmp7, \tmp6 +.endm + +/* + * Description : Transpose 16x8 block with byte elements in vectors + * Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7 + * Outputs - out0, out1, out2, out3, out4, out5, out6, out7 + */ +.macro LSX_TRANSPOSE16X8_B in0, in1, in2, in3, in4, in5, in6, in7, \ + in8, in9, in10, in11, in12, in13, in14, in15, \ + out0, out1, out2, out3, out4, out5, out6, out7,\ + tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 + vilvl.b \tmp0, \in2, \in0 + vilvl.b \tmp1, \in3, \in1 + vilvl.b \tmp2, \in6, \in4 + vilvl.b \tmp3, \in7, \in5 + vilvl.b \tmp4, \in10, \in8 + vilvl.b \tmp5, \in11, \in9 + vilvl.b \tmp6, \in14, \in12 + vilvl.b \tmp7, \in15, \in13 + + vilvl.b \out0, \tmp1, \tmp0 + vilvh.b \out1, \tmp1, \tmp0 + vilvl.b \out2, \tmp3, \tmp2 + vilvh.b \out3, \tmp3, \tmp2 + vilvl.b \out4, \tmp5, \tmp4 + vilvh.b \out5, \tmp5, \tmp4 + vilvl.b \out6, \tmp7, \tmp6 + vilvh.b \out7, \tmp7, \tmp6 + vilvl.w \tmp0, \out2, \out0 + vilvh.w \tmp2, \out2, \out0 + vilvl.w \tmp4, \out3, \out1 + vilvh.w \tmp6, \out3, \out1 + vilvl.w \tmp1, \out6, \out4 + vilvh.w \tmp3, \out6, \out4 + vilvl.w \tmp5, \out7, \out5 + vilvh.w \tmp7, \out7, \out5 + vilvl.d \out0, \tmp1, \tmp0 + vilvh.d \out1, \tmp1, \tmp0 + vilvl.d \out2, \tmp3, \tmp2 + vilvh.d \out3, \tmp3, \tmp2 + vilvl.d \out4, \tmp5, \tmp4 + vilvh.d \out5, \tmp5, \tmp4 + vilvl.d \out6, \tmp7, \tmp6 + vilvh.d \out7, \tmp7, \tmp6 +.endm + +/* + * Description : Transpose 4x4 block with half-word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + */ +.macro LASX_TRANSPOSE4x4_H in0, in1, in2, in3, out0, out1, out2, out3, \ + tmp0, tmp1 + xvilvl.h \tmp0, \in1, \in0 + xvilvl.h \tmp1, \in3, \in2 + xvilvl.w \out0, \tmp1, \tmp0 + xvilvh.w \out2, \tmp1, \tmp0 + xvilvh.d \out1, \out0, \out0 + xvilvh.d \out3, \out0, \out2 +.endm + +/* + * Description : Transpose 4x8 block with half-word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + */ +.macro LASX_TRANSPOSE4x8_H in0, in1, in2, in3, out0, out1, out2, out3, \ + tmp0, tmp1 + xvilvl.h \tmp0, \in2, \in0 + xvilvl.h \tmp1, \in3, \in1 + xvilvl.h \out2, \tmp1, \tmp0 + xvilvh.h \out3, \tmp1, \tmp0 + + xvilvl.d \out0, \out2, \out2 + xvilvh.d \out1, \out2, \out2 + xvilvl.d \out2, \out3, \out3 + xvilvh.d \out3, \out3, \out3 +.endm + +/* + * Description : Transpose 8x8 block with half-word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3, in4, in5, in6, in7 + * Outputs - out0, out1, out2, out3, out4, out5, out6, out7 + */ +.macro LASX_TRANSPOSE8x8_H in0, in1, in2, in3, in4, in5, in6, in7, \ + out0, out1, out2, out3, out4, out5, out6, out7, \ + tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7 + xvilvl.h \tmp0, \in6, \in4 + xvilvl.h \tmp1, \in7, \in5 + xvilvl.h \tmp2, \in2, \in0 + xvilvl.h \tmp3, \in3, \in1 + + xvilvl.h \tmp4, \tmp1, \tmp0 + xvilvh.h \tmp5, \tmp1, \tmp0 + xvilvl.h \tmp6, \tmp3, \tmp2 + xvilvh.h \tmp7, \tmp3, \tmp2 + + xvilvh.h \tmp0, \in6, \in4 + xvilvh.h \tmp1, \in7, \in5 + xvilvh.h \tmp2, \in2, \in0 + xvilvh.h \tmp3, \in3, \in1 + + xvpickev.d \out0, \tmp4, \tmp6 + xvpickod.d \out1, \tmp4, \tmp6 + xvpickev.d \out2, \tmp5, \tmp7 + xvpickod.d \out3, \tmp5, \tmp7 + + xvilvl.h \tmp4, \tmp1, \tmp0 + xvilvh.h \tmp5, \tmp1, \tmp0 + xvilvl.h \tmp6, \tmp3, \tmp2 + xvilvh.h \tmp7, \tmp3, \tmp2 + + xvpickev.d \out4, \tmp4, \tmp6 + xvpickod.d \out5, \tmp4, \tmp6 + xvpickev.d \out6, \tmp5, \tmp7 + xvpickod.d \out7, \tmp5, \tmp7 +.endm + +/* + * Description : Transpose 2x4x4 block with half-word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + */ +.macro LASX_TRANSPOSE2x4x4_H in0, in1, in2, in3, out0, out1, out2, out3, \ + tmp0, tmp1, tmp2 + xvilvh.h \tmp1, \in0, \in1 + xvilvl.h \out1, \in0, \in1 + xvilvh.h \tmp0, \in2, \in3 + xvilvl.h \out3, \in2, \in3 + + xvilvh.w \tmp2, \out3, \out1 + xvilvl.w \out3, \out3, \out1 + + xvilvl.w \out2, \tmp0, \tmp1 + xvilvh.w \tmp1, \tmp0, \tmp1 + + xvilvh.d \out0, \out2, \out3 + xvilvl.d \out2, \out2, \out3 + xvilvh.d \out1, \tmp1, \tmp2 + xvilvl.d \out3, \tmp1, \tmp2 +.endm + +/* + * Description : Transpose 4x4 block with word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + * Details : + * Example : + * 1, 2, 3, 4, 1, 2, 3, 4 1,5, 9,13, 1,5, 9,13 + * 5, 6, 7, 8, 5, 6, 7, 8 to 2,6,10,14, 2,6,10,14 + * 9,10,11,12, 9,10,11,12 =====> 3,7,11,15, 3,7,11,15 + * 13,14,15,16, 13,14,15,16 4,8,12,16, 4,8,12,16 + */ +.macro LASX_TRANSPOSE4x4_W _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3, \ + _tmp0, _tmp1 + + xvilvl.w \_tmp0, \_in1, \_in0 + xvilvh.w \_out1, \_in1, \_in0 + xvilvl.w \_tmp1, \_in3, \_in2 + xvilvh.w \_out3, \_in3, \_in2 + + xvilvl.d \_out0, \_tmp1, \_tmp0 + xvilvl.d \_out2, \_out3, \_out1 + xvilvh.d \_out3, \_out3, \_out1 + xvilvh.d \_out1, \_tmp1, \_tmp0 +.endm + +/* + * Description : Transpose 8x8 block with word elements in vectors + * Arguments : Inputs - _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7 + * Outputs - _out0, _out1, _out2, _out3, _out4, _out5, _out6, + * _out7 + * Example : LASX_TRANSPOSE8x8_W + * _in0 : 1,2,3,4,5,6,7,8 + * _in1 : 2,2,3,4,5,6,7,8 + * _in2 : 3,2,3,4,5,6,7,8 + * _in3 : 4,2,3,4,5,6,7,8 + * _in4 : 5,2,3,4,5,6,7,8 + * _in5 : 6,2,3,4,5,6,7,8 + * _in6 : 7,2,3,4,5,6,7,8 + * _in7 : 8,2,3,4,5,6,7,8 + * + * _out0 : 1,2,3,4,5,6,7,8 + * _out1 : 2,2,2,2,2,2,2,2 + * _out2 : 3,3,3,3,3,3,3,3 + * _out3 : 4,4,4,4,4,4,4,4 + * _out4 : 5,5,5,5,5,5,5,5 + * _out5 : 6,6,6,6,6,6,6,6 + * _out6 : 7,7,7,7,7,7,7,7 + * _out7 : 8,8,8,8,8,8,8,8 + */ +.macro LASX_TRANSPOSE8x8_W _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7,\ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7,\ + _tmp0, _tmp1, _tmp2, _tmp3 + xvilvl.w \_tmp0, \_in2, \_in0 + xvilvl.w \_tmp1, \_in3, \_in1 + xvilvh.w \_tmp2, \_in2, \_in0 + xvilvh.w \_tmp3, \_in3, \_in1 + xvilvl.w \_out0, \_tmp1, \_tmp0 + xvilvh.w \_out1, \_tmp1, \_tmp0 + xvilvl.w \_out2, \_tmp3, \_tmp2 + xvilvh.w \_out3, \_tmp3, \_tmp2 + + xvilvl.w \_tmp0, \_in6, \_in4 + xvilvl.w \_tmp1, \_in7, \_in5 + xvilvh.w \_tmp2, \_in6, \_in4 + xvilvh.w \_tmp3, \_in7, \_in5 + xvilvl.w \_out4, \_tmp1, \_tmp0 + xvilvh.w \_out5, \_tmp1, \_tmp0 + xvilvl.w \_out6, \_tmp3, \_tmp2 + xvilvh.w \_out7, \_tmp3, \_tmp2 + + xmov \_tmp0, \_out0 + xmov \_tmp1, \_out1 + xmov \_tmp2, \_out2 + xmov \_tmp3, \_out3 + xvpermi.q \_out0, \_out4, 0x02 + xvpermi.q \_out1, \_out5, 0x02 + xvpermi.q \_out2, \_out6, 0x02 + xvpermi.q \_out3, \_out7, 0x02 + xvpermi.q \_out4, \_tmp0, 0x31 + xvpermi.q \_out5, \_tmp1, 0x31 + xvpermi.q \_out6, \_tmp2, 0x31 + xvpermi.q \_out7, \_tmp3, 0x31 +.endm + +/* + * Description : Transpose 4x4 block with double-word elements in vectors + * Arguments : Inputs - _in0, _in1, _in2, _in3 + * Outputs - _out0, _out1, _out2, _out3 + * Example : LASX_TRANSPOSE4x4_D + * _in0 : 1,2,3,4 + * _in1 : 1,2,3,4 + * _in2 : 1,2,3,4 + * _in3 : 1,2,3,4 + * + * _out0 : 1,1,1,1 + * _out1 : 2,2,2,2 + * _out2 : 3,3,3,3 + * _out3 : 4,4,4,4 + */ +.macro LASX_TRANSPOSE4x4_D _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3, \ + _tmp0, _tmp1 + xvilvl.d \_tmp0, \_in1, \_in0 + xvilvh.d \_out1, \_in1, \_in0 + xvilvh.d \_tmp1, \_in3, \_in2 + xvilvl.d \_out2, \_in3, \_in2 + + xvor.v \_out0, \_tmp0, \_tmp0 + xvor.v \_out3, \_tmp1, \_tmp1 + + xvpermi.q \_out0, \_out2, 0x02 + xvpermi.q \_out2, \_tmp0, 0x31 + xvpermi.q \_out3, \_out1, 0x31 + xvpermi.q \_out1, \_tmp1, 0x02 +.endm + +/* + * Description : Butterfly of 4 input vectors + * Arguments : Inputs - _in0, _in1, _in2, _in3 + * Outputs - _out0, _out1, _out2, _out3 + * Details : Butterfly operation + * Example : LSX_BUTTERFLY_4 + * _out0 = _in0 + _in3; + * _out1 = _in1 + _in2; + * _out2 = _in1 - _in2; + * _out3 = _in0 - _in3; + */ +.macro LSX_BUTTERFLY_4_B _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + vadd.b \_out0, \_in0, \_in3 + vadd.b \_out1, \_in1, \_in2 + vsub.b \_out2, \_in1, \_in2 + vsub.b \_out3, \_in0, \_in3 +.endm +.macro LSX_BUTTERFLY_4_H _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + vadd.h \_out0, \_in0, \_in3 + vadd.h \_out1, \_in1, \_in2 + vsub.h \_out2, \_in1, \_in2 + vsub.h \_out3, \_in0, \_in3 +.endm +.macro LSX_BUTTERFLY_4_W _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + vadd.w \_out0, \_in0, \_in3 + vadd.w \_out1, \_in1, \_in2 + vsub.w \_out2, \_in1, \_in2 + vsub.w \_out3, \_in0, \_in3 +.endm +.macro LSX_BUTTERFLY_4_D _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + vadd.d \_out0, \_in0, \_in3 + vadd.d \_out1, \_in1, \_in2 + vsub.d \_out2, \_in1, \_in2 + vsub.d \_out3, \_in0, \_in3 +.endm + +.macro LASX_BUTTERFLY_4_B _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + xvadd.b \_out0, \_in0, \_in3 + xvadd.b \_out1, \_in1, \_in2 + xvsub.b \_out2, \_in1, \_in2 + xvsub.b \_out3, \_in0, \_in3 +.endm +.macro LASX_BUTTERFLY_4_H _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + xvadd.h \_out0, \_in0, \_in3 + xvadd.h \_out1, \_in1, \_in2 + xvsub.h \_out2, \_in1, \_in2 + xvsub.h \_out3, \_in0, \_in3 +.endm +.macro LASX_BUTTERFLY_4_W _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + xvadd.w \_out0, \_in0, \_in3 + xvadd.w \_out1, \_in1, \_in2 + xvsub.w \_out2, \_in1, \_in2 + xvsub.w \_out3, \_in0, \_in3 +.endm +.macro LASX_BUTTERFLY_4_D _in0, _in1, _in2, _in3, _out0, _out1, _out2, _out3 + xvadd.d \_out0, \_in0, \_in3 + xvadd.d \_out1, \_in1, \_in2 + xvsub.d \_out2, \_in1, \_in2 + xvsub.d \_out3, \_in0, \_in3 +.endm + +/* + * Description : Butterfly of 8 input vectors + * Arguments : Inputs - _in0, _in1, _in2, _in3, ~ + * Outputs - _out0, _out1, _out2, _out3, ~ + * Details : Butterfly operation + * Example : LASX_BUTTERFLY_8 + * _out0 = _in0 + _in7; + * _out1 = _in1 + _in6; + * _out2 = _in2 + _in5; + * _out3 = _in3 + _in4; + * _out4 = _in3 - _in4; + * _out5 = _in2 - _in5; + * _out6 = _in1 - _in6; + * _out7 = _in0 - _in7; + */ +.macro LSX_BUTTERFLY_8_B _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + vadd.b \_out0, \_in0, \_in7 + vadd.b \_out1, \_in1, \_in6 + vadd.b \_out2, \_in2, \_in5 + vadd.b \_out3, \_in3, \_in4 + vsub.b \_out4, \_in3, \_in4 + vsub.b \_out5, \_in2, \_in5 + vsub.b \_out6, \_in1, \_in6 + vsub.b \_out7, \_in0, \_in7 +.endm + +.macro LSX_BUTTERFLY_8_H _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + vadd.h \_out0, \_in0, \_in7 + vadd.h \_out1, \_in1, \_in6 + vadd.h \_out2, \_in2, \_in5 + vadd.h \_out3, \_in3, \_in4 + vsub.h \_out4, \_in3, \_in4 + vsub.h \_out5, \_in2, \_in5 + vsub.h \_out6, \_in1, \_in6 + vsub.h \_out7, \_in0, \_in7 +.endm + +.macro LSX_BUTTERFLY_8_W _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + vadd.w \_out0, \_in0, \_in7 + vadd.w \_out1, \_in1, \_in6 + vadd.w \_out2, \_in2, \_in5 + vadd.w \_out3, \_in3, \_in4 + vsub.w \_out4, \_in3, \_in4 + vsub.w \_out5, \_in2, \_in5 + vsub.w \_out6, \_in1, \_in6 + vsub.w \_out7, \_in0, \_in7 +.endm + +.macro LSX_BUTTERFLY_8_D _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + vadd.d \_out0, \_in0, \_in7 + vadd.d \_out1, \_in1, \_in6 + vadd.d \_out2, \_in2, \_in5 + vadd.d \_out3, \_in3, \_in4 + vsub.d \_out4, \_in3, \_in4 + vsub.d \_out5, \_in2, \_in5 + vsub.d \_out6, \_in1, \_in6 + vsub.d \_out7, \_in0, \_in7 +.endm + +.macro LASX_BUTTERFLY_8_B _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + xvadd.b \_out0, \_in0, \_in7 + xvadd.b \_out1, \_in1, \_in6 + xvadd.b \_out2, \_in2, \_in5 + xvadd.b \_out3, \_in3, \_in4 + xvsub.b \_out4, \_in3, \_in4 + xvsub.b \_out5, \_in2, \_in5 + xvsub.b \_out6, \_in1, \_in6 + xvsub.b \_out7, \_in0, \_in7 +.endm + +.macro LASX_BUTTERFLY_8_H _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + xvadd.h \_out0, \_in0, \_in7 + xvadd.h \_out1, \_in1, \_in6 + xvadd.h \_out2, \_in2, \_in5 + xvadd.h \_out3, \_in3, \_in4 + xvsub.h \_out4, \_in3, \_in4 + xvsub.h \_out5, \_in2, \_in5 + xvsub.h \_out6, \_in1, \_in6 + xvsub.h \_out7, \_in0, \_in7 +.endm + +.macro LASX_BUTTERFLY_8_W _in0, _in1, _in2, _in3, _in4, _in5, _in6, _in7, \ + _out0, _out1, _out2, _out3, _out4, _out5, _out6, _out7 + xvadd.w \_out0, \_in0, \_in7 + xvadd.w \_out1, \_in1, \_in6 + xvadd.w \_out2, \_in2, \_in5 + xvadd.w \_out3, \_in3, \_in4 + xvsub.w \_out4, \_in3, \_in4 + xvsub.w \_out5, \_in2, \_in5 + xvsub.w \_out6, \_in1, \_in6 + xvsub.w \_out7, \_in0, \_in7 +.endm diff --git a/libavcodec/lossless_audiodsp.c b/libavcodec/lossless_audiodsp.c index 1daf2e4c123..b0d64cf5b3f 100644 --- a/libavcodec/lossless_audiodsp.c +++ b/libavcodec/lossless_audiodsp.c @@ -63,6 +63,8 @@ av_cold void ff_llauddsp_init(LLAudDSPContext *c) ff_llauddsp_init_arm(c); #elif ARCH_PPC ff_llauddsp_init_ppc(c); +#elif ARCH_RISCV + ff_llauddsp_init_riscv(c); #elif ARCH_X86 ff_llauddsp_init_x86(c); #endif diff --git a/libavcodec/lossless_audiodsp.h b/libavcodec/lossless_audiodsp.h index eea5d49fa9b..cf2d43d7c9e 100644 --- a/libavcodec/lossless_audiodsp.h +++ b/libavcodec/lossless_audiodsp.h @@ -46,6 +46,7 @@ typedef struct LLAudDSPContext { void ff_llauddsp_init(LLAudDSPContext *c); void ff_llauddsp_init_arm(LLAudDSPContext *c); void ff_llauddsp_init_ppc(LLAudDSPContext *c); +void ff_llauddsp_init_riscv(LLAudDSPContext *c); void ff_llauddsp_init_x86(LLAudDSPContext *c); #endif /* AVCODEC_LOSSLESS_AUDIODSP_H */ diff --git a/libavcodec/lossless_videodsp.c b/libavcodec/lossless_videodsp.c index 359606981c0..876decb1e6b 100644 --- a/libavcodec/lossless_videodsp.c +++ b/libavcodec/lossless_videodsp.c @@ -121,6 +121,8 @@ void ff_llviddsp_init(LLVidDSPContext *c) #if ARCH_PPC ff_llviddsp_init_ppc(c); +#elif ARCH_RISCV + ff_llviddsp_init_riscv(c); #elif ARCH_X86 ff_llviddsp_init_x86(c); #endif diff --git a/libavcodec/lossless_videodsp.h b/libavcodec/lossless_videodsp.h index da4baa1414b..5309ce4be77 100644 --- a/libavcodec/lossless_videodsp.h +++ b/libavcodec/lossless_videodsp.h @@ -40,6 +40,7 @@ typedef struct LLVidDSPContext { } LLVidDSPContext; void ff_llviddsp_init(LLVidDSPContext *llviddsp); +void ff_llviddsp_init_riscv(LLVidDSPContext *llviddsp); void ff_llviddsp_init_x86(LLVidDSPContext *llviddsp); void ff_llviddsp_init_ppc(LLVidDSPContext *llviddsp); diff --git a/libavcodec/lossless_videoencdsp.c b/libavcodec/lossless_videoencdsp.c index b4130ebc7b1..0a3a4ec5096 100644 --- a/libavcodec/lossless_videoencdsp.c +++ b/libavcodec/lossless_videoencdsp.c @@ -18,19 +18,29 @@ #include "config.h" #include "libavutil/attributes.h" +#include "libavutil/intreadwrite.h" #include "lossless_videoencdsp.h" #include "mathops.h" +#if HAVE_FAST_64BIT +typedef uint64_t uint_native; +#define READ AV_RN64 +#define WRITE AV_WN64 +#else +typedef uint32_t uint_native; +#define READ AV_RN32 +#define WRITE AV_WN32 +#endif // 0x7f7f7f7f or 0x7f7f7f7f7f7f7f7f or whatever, depending on the cpu's native arithmetic size -#define pb_7f (~0UL / 255 * 0x7f) -#define pb_80 (~0UL / 255 * 0x80) +#define pb_7f (~(uint_native)0 / 255 * 0x7f) +#define pb_80 (~(uint_native)0 / 255 * 0x80) static void diff_bytes_c(uint8_t *dst, const uint8_t *src1, const uint8_t *src2, intptr_t w) { long i; #if !HAVE_FAST_UNALIGNED - if (((long)src1 | (long)src2) & (sizeof(long) - 1)) { + if (((uintptr_t)src1 | (uintptr_t)src2) & (sizeof(uint_native) - 1)) { for (i = 0; i + 7 < w; i += 8) { dst[i + 0] = src1[i + 0] - src2[i + 0]; dst[i + 1] = src1[i + 1] - src2[i + 1]; @@ -43,11 +53,10 @@ static void diff_bytes_c(uint8_t *dst, const uint8_t *src1, const uint8_t *src2, } } else #endif - for (i = 0; i <= w - (int) sizeof(long); i += sizeof(long)) { - long a = *(long *) (src1 + i); - long b = *(long *) (src2 + i); - *(long *) (dst + i) = ((a | pb_80) - (b & pb_7f)) ^ - ((a ^ b ^ pb_80) & pb_80); + for (i = 0; i <= w - (int) sizeof(uint_native); i += sizeof(uint_native)) { + uint_native a = READ(src1 + i); + uint_native b = READ(src2 + i); + WRITE(dst + i, ((a | pb_80) - (b & pb_7f)) ^ ((a ^ b ^ pb_80) & pb_80)); } for (; i < w; i++) dst[i + 0] = src1[i + 0] - src2[i + 0]; @@ -94,7 +103,9 @@ av_cold void ff_llvidencdsp_init(LLVidEncDSPContext *c) c->sub_median_pred = sub_median_pred_c; c->sub_left_predict = sub_left_predict_c; -#if ARCH_X86 +#if ARCH_RISCV + ff_llvidencdsp_init_riscv(c); +#elif ARCH_X86 ff_llvidencdsp_init_x86(c); #endif } diff --git a/libavcodec/lossless_videoencdsp.h b/libavcodec/lossless_videoencdsp.h index f2c28784856..7fd0ad32c7f 100644 --- a/libavcodec/lossless_videoencdsp.h +++ b/libavcodec/lossless_videoencdsp.h @@ -23,8 +23,8 @@ #include typedef struct LLVidEncDSPContext { - void (*diff_bytes)(uint8_t *dst /* align 16 */, - const uint8_t *src1 /* align 16 */, + void (*diff_bytes)(uint8_t *dst /* align 1 */, + const uint8_t *src1 /* align 1 */, const uint8_t *src2 /* align 1 */, intptr_t w); /** @@ -40,6 +40,7 @@ typedef struct LLVidEncDSPContext { } LLVidEncDSPContext; void ff_llvidencdsp_init(LLVidEncDSPContext *c); +void ff_llvidencdsp_init_riscv(LLVidEncDSPContext *c); void ff_llvidencdsp_init_x86(LLVidEncDSPContext *c); #endif /* AVCODEC_LOSSLESS_VIDEOENCDSP_H */ diff --git a/libavcodec/lpc.c b/libavcodec/lpc.c index dc6a3060cea..88ab37e761a 100644 --- a/libavcodec/lpc.c +++ b/libavcodec/lpc.c @@ -25,8 +25,39 @@ #define LPC_USE_DOUBLE #include "lpc.h" +#include "lpc_functions.h" #include "libavutil/avassert.h" +/** + * Schur recursion. + * Produces reflection coefficients from autocorrelation data. + */ +static inline void compute_ref_coefs(const LPC_TYPE *autoc, int max_order, + LPC_TYPE *ref, LPC_TYPE *error) +{ + LPC_TYPE err; + LPC_TYPE gen0[MAX_LPC_ORDER], gen1[MAX_LPC_ORDER]; + + for (int i = 0; i < max_order; i++) + gen0[i] = gen1[i] = autoc[i + 1]; + + err = autoc[0]; + ref[0] = -gen1[0] / ((LPC_USE_FIXED || err) ? err : 1); + err += gen1[0] * ref[0]; + if (error) + error[0] = err; + for (int i = 1; i < max_order; i++) { + for (int j = 0; j < max_order - i; j++) { + gen1[j] = gen1[j + 1] + ref[i - 1] * gen0[j]; + gen0[j] = gen1[j + 1] * ref[i - 1] + gen0[j]; + } + ref[i] = -gen1[0] / ((LPC_USE_FIXED || err) ? err : 1); + err += gen1[0] * ref[i]; + if (error) + error[i] = err; + } +} + /** * Apply Welch window function to audio block @@ -250,8 +281,10 @@ int ff_lpc_calc_coefs(LPCContext *s, double av_uninit(weight); memset(var, 0, FFALIGN(MAX_LPC_ORDER+1,4)*sizeof(*var)); - for(j=0; j 1) + for(j=0; jlpc_apply_welch_window = lpc_apply_welch_window_c; s->lpc_compute_autocorr = lpc_compute_autocorr_c; -#if ARCH_X86 +#if ARCH_RISCV + ff_lpc_init_riscv(s); +#elif ARCH_X86 ff_lpc_init_x86(s); #endif diff --git a/libavcodec/lpc.h b/libavcodec/lpc.h index 467d0b2830c..6d62707a597 100644 --- a/libavcodec/lpc.h +++ b/libavcodec/lpc.h @@ -24,9 +24,7 @@ #include #include -#include "libavutil/avassert.h" #include "libavutil/lls.h" -#include "aac_defines.h" #define ORDER_METHOD_EST 0 #define ORDER_METHOD_2LEVEL 1 @@ -109,6 +107,7 @@ double ff_lpc_calc_ref_coefs_f(LPCContext *s, const float *samples, int len, */ int ff_lpc_init(LPCContext *s, int blocksize, int max_order, enum FFLPCType lpc_type); +void ff_lpc_init_riscv(LPCContext *s); void ff_lpc_init_x86(LPCContext *s); /** @@ -116,99 +115,4 @@ void ff_lpc_init_x86(LPCContext *s); */ void ff_lpc_end(LPCContext *s); -#if USE_FIXED -typedef int LPC_TYPE; -typedef unsigned LPC_TYPE_U; -#else -#ifdef LPC_USE_DOUBLE -typedef double LPC_TYPE; -typedef double LPC_TYPE_U; -#else -typedef float LPC_TYPE; -typedef float LPC_TYPE_U; -#endif -#endif // USE_FIXED - -/** - * Schur recursion. - * Produces reflection coefficients from autocorrelation data. - */ -static inline void compute_ref_coefs(const LPC_TYPE *autoc, int max_order, - LPC_TYPE *ref, LPC_TYPE *error) -{ - int i, j; - LPC_TYPE err; - LPC_TYPE gen0[MAX_LPC_ORDER], gen1[MAX_LPC_ORDER]; - - for (i = 0; i < max_order; i++) - gen0[i] = gen1[i] = autoc[i + 1]; - - err = autoc[0]; - ref[0] = -gen1[0] / ((USE_FIXED || err) ? err : 1); - err += gen1[0] * ref[0]; - if (error) - error[0] = err; - for (i = 1; i < max_order; i++) { - for (j = 0; j < max_order - i; j++) { - gen1[j] = gen1[j + 1] + ref[i - 1] * gen0[j]; - gen0[j] = gen1[j + 1] * ref[i - 1] + gen0[j]; - } - ref[i] = -gen1[0] / ((USE_FIXED || err) ? err : 1); - err += gen1[0] * ref[i]; - if (error) - error[i] = err; - } -} - -/** - * Levinson-Durbin recursion. - * Produce LPC coefficients from autocorrelation data. - */ -static inline int AAC_RENAME(compute_lpc_coefs)(const LPC_TYPE *autoc, int max_order, - LPC_TYPE *lpc, int lpc_stride, int fail, - int normalize) -{ - int i, j; - LPC_TYPE err = 0; - LPC_TYPE *lpc_last = lpc; - - av_assert2(normalize || !fail); - - if (normalize) - err = *autoc++; - - if (fail && (autoc[max_order - 1] == 0 || err <= 0)) - return -1; - - for(i=0; i>1; j++) { - LPC_TYPE f = lpc_last[ j]; - LPC_TYPE b = lpc_last[i-1-j]; - lpc[ j] = f + (LPC_TYPE_U)AAC_MUL26(r, b); - lpc[i-1-j] = b + (LPC_TYPE_U)AAC_MUL26(r, f); - } - - if (fail && err < 0) - return -1; - - lpc_last = lpc; - lpc += lpc_stride; - } - - return 0; -} - #endif /* AVCODEC_LPC_H */ diff --git a/libavcodec/lpc_functions.h b/libavcodec/lpc_functions.h new file mode 100644 index 00000000000..57bcfab900d --- /dev/null +++ b/libavcodec/lpc_functions.h @@ -0,0 +1,100 @@ +/* + * LPC utility functions + * Copyright (c) 2006 Justin Ruggles + * + * 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 + */ + +#ifndef AVCODEC_LPC_FUNCTIONS_H +#define AVCODEC_LPC_FUNCTIONS_H + +#include "libavutil/avassert.h" + +#ifndef LPC_USE_FIXED +#define LPC_USE_FIXED 0 +#endif + +#if LPC_USE_FIXED +typedef int LPC_TYPE; +typedef unsigned LPC_TYPE_U; +#else +#ifndef LPC_SRA_R +#define LPC_SRA_R(x, y) (x) +#define LPC_MUL26(x, y) ((x) * (y)) +#define LPC_FIXR(x) ((float)(x)) +#endif + +#ifdef LPC_USE_DOUBLE +typedef double LPC_TYPE; +typedef double LPC_TYPE_U; +#else +typedef float LPC_TYPE; +typedef float LPC_TYPE_U; +#endif +#endif // USE_FIXED + +/** + * Levinson-Durbin recursion. + * Produce LPC coefficients from autocorrelation data. + */ +static inline int compute_lpc_coefs(const LPC_TYPE *autoc, int max_order, + LPC_TYPE *lpc, int lpc_stride, int fail, + int normalize) +{ + LPC_TYPE err = 0; + LPC_TYPE *lpc_last = lpc; + + av_assert2(normalize || !fail); + + if (normalize) + err = *autoc++; + + if (fail && (autoc[max_order - 1] == 0 || err <= 0)) + return -1; + + for(int i = 0; i < max_order; i++) { + LPC_TYPE r = LPC_SRA_R(-autoc[i], 5); + + if (normalize) { + for(int j = 0; j < i; j++) + r -= lpc_last[j] * autoc[i-j-1]; + + if (err) + r /= err; + err *= LPC_FIXR(1.0) - (r * r); + } + + lpc[i] = r; + + for(int j = 0; j < (i + 1) >> 1; j++) { + LPC_TYPE f = lpc_last[ j]; + LPC_TYPE b = lpc_last[i-1-j]; + lpc[ j] = f + (LPC_TYPE_U)LPC_MUL26(r, b); + lpc[i-1-j] = b + (LPC_TYPE_U)LPC_MUL26(r, f); + } + + if (fail && err < 0) + return -1; + + lpc_last = lpc; + lpc += lpc_stride; + } + + return 0; +} + +#endif /* AVCODEC_LPC_FUNCTIONS_H */ diff --git a/libavcodec/lscrdec.c b/libavcodec/lscrdec.c index 76a46751f07..415914bf0ac 100644 --- a/libavcodec/lscrdec.c +++ b/libavcodec/lscrdec.c @@ -154,10 +154,13 @@ static int decode_frame_lscr(AVCodecContext *avctx, AVFrame *rframe, size = bytestream2_get_le32(gb); - frame->key_frame = (nb_blocks == 1) && - (w == avctx->width) && - (h == avctx->height) && - (x == 0) && (y == 0); + if ((nb_blocks == 1) && + (w == avctx->width) && + (h == avctx->height) && + (x == 0) && (y == 0)) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; bytestream2_seek(gb, 2 + nb_blocks * 12 + offset, SEEK_SET); csize = bytestream2_get_be32(gb); @@ -199,7 +202,7 @@ static int decode_frame_lscr(AVCodecContext *avctx, AVFrame *rframe, } } - frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + frame->pict_type = (frame->flags & AV_FRAME_FLAG_KEY) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if ((ret = av_frame_ref(rframe, frame)) < 0) return ret; diff --git a/libavcodec/m101.c b/libavcodec/m101.c index 3def577b746..43a3c7bbe5b 100644 --- a/libavcodec/m101.c +++ b/libavcodec/m101.c @@ -67,15 +67,17 @@ static int m101_decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) return ret; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; - frame->interlaced_frame = ((avctx->extradata[3*4] & 3) != 3); - if (frame->interlaced_frame) - frame->top_field_first = avctx->extradata[3*4] & 1; + frame->flags |= AV_FRAME_FLAG_KEY; + if ((avctx->extradata[3*4] & 3) != 3) { + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (avctx->extradata[3*4] & 1) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + } for (y = 0; y < avctx->height; y++) { int src_y = y; - if (frame->interlaced_frame) - src_y = ((y&1)^frame->top_field_first) ? y/2 : (y/2 + avctx->height/2); + if (frame->flags & AV_FRAME_FLAG_INTERLACED) + src_y = ((y&1) ^ !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)) ? y/2 : (y/2 + avctx->height/2); if (bits == 8) { uint8_t *line = frame->data[0] + y*frame->linesize[0]; memcpy(line, buf + src_y*stride, 2*avctx->width); diff --git a/libavcodec/magicyuv.c b/libavcodec/magicyuv.c index 62263409b1e..4f304936269 100644 --- a/libavcodec/magicyuv.c +++ b/libavcodec/magicyuv.c @@ -34,6 +34,8 @@ #include "lossless_videodsp.h" #include "thread.h" +#define VLC_BITS 12 + typedef struct Slice { uint32_t start; uint32_t size; @@ -67,15 +69,20 @@ typedef struct MagicYUVContext { Slice *slices[4]; // slice bitstream positions for each plane unsigned int slices_size[4]; // slice sizes for each plane VLC vlc[4]; // VLC for each plane + VLC_MULTI multi[4]; // Buffer for joint VLC data int (*magy_decode_slice)(AVCodecContext *avctx, void *tdata, int j, int threadnr); LLVidDSPContext llviddsp; + HuffEntry he[1 << 14]; + uint8_t len[1 << 14]; } MagicYUVContext; -static int huff_build(const uint8_t len[], uint16_t codes_pos[33], - VLC *vlc, int nb_elems, void *logctx) +static int huff_build(AVCodecContext *avctx, + const uint8_t len[], uint16_t codes_pos[33], + VLC *vlc, VLC_MULTI *multi, int nb_elems, void *logctx) { - HuffEntry he[4096]; + MagicYUVContext *s = avctx->priv_data; + HuffEntry *he = s->he; for (int i = 31; i > 0; i--) codes_pos[i] += codes_pos[i + 1]; @@ -83,8 +90,9 @@ static int huff_build(const uint8_t len[], uint16_t codes_pos[33], for (unsigned i = nb_elems; i-- > 0;) he[--codes_pos[len[i]]] = (HuffEntry){ len[i], i }; - ff_free_vlc(vlc); - return ff_init_vlc_from_lengths(vlc, FFMIN(he[0].len, 12), nb_elems, + ff_vlc_free(vlc); + ff_vlc_free_multi(multi); + return ff_vlc_init_multi_from_lengths(vlc, multi, FFMIN(he[0].len, VLC_BITS), nb_elems, nb_elems, &he[0].len, sizeof(he[0]), &he[0].sym, sizeof(he[0]), sizeof(he[0].sym), 0, 0, logctx); @@ -111,6 +119,21 @@ static void magicyuv_median_pred16(uint16_t *dst, const uint16_t *src1, *left_top = lt; } +#define READ_PLANE(dst, plane, b, c) \ +{ \ + x = 0; \ + for (; CACHED_BITSTREAM_READER && x < width-c && get_bits_left(&gb) > 0;) {\ + ret = get_vlc_multi(&gb, (uint8_t *)dst + x * b, multi, \ + vlc, vlc_bits, 3, b); \ + if (ret <= 0) \ + return AVERROR_INVALIDDATA; \ + x += ret; \ + } \ + for (; x < width && get_bits_left(&gb) > 0; x++) \ + dst[x] = get_vlc2(&gb, vlc, vlc_bits, 3); \ + dst += stride; \ +} + static int magy_decode_slice10(AVCodecContext *avctx, void *tdata, int j, int threadnr) { @@ -130,6 +153,9 @@ static int magy_decode_slice10(AVCodecContext *avctx, void *tdata, int sheight = AV_CEIL_RSHIFT(s->slice_height, s->vshift[i]); ptrdiff_t fake_stride = (p->linesize[i] / 2) * (1 + interlaced); ptrdiff_t stride = p->linesize[i] / 2; + const VLC_MULTI_ELEM *const multi = s->multi[i].table; + const VLCElem *const vlc = s->vlc[i].table; + const int vlc_bits = s->vlc[i].bits; int flags, pred; int ret = init_get_bits8(&gb, s->buf + s->slices[i][j].start, s->slices[i][j].size); @@ -151,20 +177,8 @@ static int magy_decode_slice10(AVCodecContext *avctx, void *tdata, dst += stride; } } else { - for (k = 0; k < height; k++) { - for (x = 0; x < width; x++) { - int pix; - if (get_bits_left(&gb) <= 0) - return AVERROR_INVALIDDATA; - - pix = get_vlc2(&gb, s->vlc[i].table, s->vlc[i].bits, 3); - if (pix < 0) - return AVERROR_INVALIDDATA; - - dst[x] = pix; - } - dst += stride; - } + for (k = 0; k < height; k++) + READ_PLANE(dst, i, 2, 3) } switch (pred) { @@ -261,6 +275,9 @@ static int magy_decode_slice(AVCodecContext *avctx, void *tdata, ptrdiff_t fake_stride = p->linesize[i] * (1 + interlaced); ptrdiff_t stride = p->linesize[i]; const uint8_t *slice = s->buf + s->slices[i][j].start; + const VLC_MULTI_ELEM *const multi = s->multi[i].table; + const VLCElem *const vlc = s->vlc[i].table; + const int vlc_bits = s->vlc[i].bits; int flags, pred; flags = bytestream_get_byte(&slice); @@ -280,20 +297,8 @@ static int magy_decode_slice(AVCodecContext *avctx, void *tdata, if (ret < 0) return ret; - for (k = 0; k < height; k++) { - for (x = 0; x < width; x++) { - int pix; - if (get_bits_left(&gb) <= 0) - return AVERROR_INVALIDDATA; - - pix = get_vlc2(&gb, s->vlc[i].table, s->vlc[i].bits, 3); - if (pix < 0) - return AVERROR_INVALIDDATA; - - dst[x] = pix; - } - dst += stride; - } + for (k = 0; k < height; k++) + READ_PLANE(dst, i, 1, 7) } switch (pred) { @@ -379,7 +384,7 @@ static int build_huffman(AVCodecContext *avctx, const uint8_t *table, { MagicYUVContext *s = avctx->priv_data; GetByteContext gb; - uint8_t len[4096]; + uint8_t *len = s->len; uint16_t length_count[33] = { 0 }; int i = 0, j = 0, k; @@ -407,7 +412,7 @@ static int build_huffman(AVCodecContext *avctx, const uint8_t *table, if (j == max) { j = 0; - if (huff_build(len, length_count, &s->vlc[i], max, avctx)) { + if (huff_build(avctx, len, length_count, &s->vlc[i], &s->multi[i], max, avctx)) { av_log(avctx, AV_LOG_ERROR, "Cannot build Huffman codes\n"); return AVERROR_INVALIDDATA; } @@ -524,6 +529,16 @@ static int magy_decode_frame(AVCodecContext *avctx, AVFrame *p, s->decorrelate = 1; s->bps = 12; break; + case 0x71: + avctx->pix_fmt = AV_PIX_FMT_GBRP14; + s->decorrelate = 1; + s->bps = 14; + break; + case 0x72: + avctx->pix_fmt = AV_PIX_FMT_GBRAP14; + s->decorrelate = 1; + s->bps = 14; + break; case 0x73: avctx->pix_fmt = AV_PIX_FMT_GRAY10; s->bps = 10; @@ -637,7 +652,7 @@ static int magy_decode_frame(AVCodecContext *avctx, AVFrame *p, return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; if ((ret = ff_thread_get_buffer(avctx, p, 0)) < 0) return ret; @@ -651,7 +666,9 @@ static int magy_decode_frame(AVCodecContext *avctx, AVFrame *p, avctx->pix_fmt == AV_PIX_FMT_GBRP10 || avctx->pix_fmt == AV_PIX_FMT_GBRAP10|| avctx->pix_fmt == AV_PIX_FMT_GBRAP12|| - avctx->pix_fmt == AV_PIX_FMT_GBRP12) { + avctx->pix_fmt == AV_PIX_FMT_GBRAP14|| + avctx->pix_fmt == AV_PIX_FMT_GBRP12|| + avctx->pix_fmt == AV_PIX_FMT_GBRP14) { FFSWAP(uint8_t*, p->data[0], p->data[1]); FFSWAP(int, p->linesize[0], p->linesize[1]); } else { @@ -686,7 +703,8 @@ static av_cold int magy_decode_end(AVCodecContext *avctx) for (i = 0; i < FF_ARRAY_ELEMS(s->slices); i++) { av_freep(&s->slices[i]); s->slices_size[i] = 0; - ff_free_vlc(&s->vlc[i]); + ff_vlc_free(&s->vlc[i]); + ff_vlc_free_multi(&s->multi[i]); } return 0; diff --git a/libavcodec/magicyuvenc.c b/libavcodec/magicyuvenc.c index 9e41c1b0fe7..1c3ab564603 100644 --- a/libavcodec/magicyuvenc.c +++ b/libavcodec/magicyuvenc.c @@ -22,6 +22,7 @@ #include #include +#include "libavutil/cpu.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/qsort.h" @@ -52,10 +53,17 @@ typedef struct PTable { int64_t prob; ///< number of occurences of this value in input } PTable; +typedef struct Slice { + unsigned pos; + unsigned size; + uint8_t *slice; + uint8_t *bitslice; + PTable counts[256]; +} Slice; + typedef struct MagicYUVContext { const AVClass *class; int frame_pred; - PutBitContext pb; int planes; uint8_t format; int slice_height; @@ -63,10 +71,9 @@ typedef struct MagicYUVContext { int correlate; int hshift[4]; int vshift[4]; - uint8_t *slices[4]; - unsigned slice_pos[4]; - unsigned tables_size; + unsigned bitslice_size; uint8_t *decorrelate_buf[2]; + Slice *slices; HuffEntry he[4][256]; LLVidEncDSPContext llvidencdsp; void (*predict)(struct MagicYUVContext *s, const uint8_t *src, uint8_t *dst, @@ -150,7 +157,6 @@ static av_cold int magy_encode_init(AVCodecContext *avctx) { MagicYUVContext *s = avctx->priv_data; PutByteContext pb; - int i; switch (avctx->pix_fmt) { case AV_PIX_FMT_GBRP: @@ -190,25 +196,39 @@ static av_cold int magy_encode_init(AVCodecContext *avctx) s->format = 0x6b; break; } - if (s->correlate) { - s->decorrelate_buf[0] = av_calloc(2U * avctx->height, FFALIGN(avctx->width, 16)); - if (!s->decorrelate_buf[0]) - return AVERROR(ENOMEM); - s->decorrelate_buf[1] = s->decorrelate_buf[0] + avctx->height * FFALIGN(avctx->width, 16); - } ff_llvidencdsp_init(&s->llvidencdsp); s->planes = av_pix_fmt_count_planes(avctx->pix_fmt); - s->nb_slices = 1; + s->nb_slices = (avctx->slices <= 0) ? av_cpu_count() : avctx->slices; + s->nb_slices = FFMIN(s->nb_slices, avctx->height >> s->vshift[1]); + s->nb_slices = FFMAX(1, s->nb_slices); + s->slice_height = FFALIGN((avctx->height + s->nb_slices - 1) / s->nb_slices, 1 << s->vshift[1]); + s->nb_slices = (avctx->height + s->slice_height - 1) / s->slice_height; + s->slices = av_calloc(s->nb_slices * s->planes, sizeof(*s->slices)); + if (!s->slices) + return AVERROR(ENOMEM); - for (i = 0; i < s->planes; i++) { - s->slices[i] = av_malloc(avctx->width * (avctx->height + 2) + - AV_INPUT_BUFFER_PADDING_SIZE); - if (!s->slices[i]) { - av_log(avctx, AV_LOG_ERROR, "Cannot allocate temporary buffer.\n"); + if (s->correlate) { + s->decorrelate_buf[0] = av_calloc(2U * (s->nb_slices * s->slice_height), FFALIGN(avctx->width, av_cpu_max_align())); + if (!s->decorrelate_buf[0]) return AVERROR(ENOMEM); + s->decorrelate_buf[1] = s->decorrelate_buf[0] + (s->nb_slices * s->slice_height) * FFALIGN(avctx->width, av_cpu_max_align()); + } + + s->bitslice_size = avctx->width * s->slice_height + 2; + for (int n = 0; n < s->nb_slices; n++) { + for (int i = 0; i < s->planes; i++) { + Slice *sl = &s->slices[n * s->planes + i]; + + sl->bitslice = av_malloc(s->bitslice_size + AV_INPUT_BUFFER_PADDING_SIZE); + sl->slice = av_malloc(avctx->width * (s->slice_height + 2) + + AV_INPUT_BUFFER_PADDING_SIZE); + if (!sl->slice || !sl->bitslice) { + av_log(avctx, AV_LOG_ERROR, "Cannot allocate temporary buffer.\n"); + return AVERROR(ENOMEM); + } } } @@ -263,15 +283,12 @@ static void calculate_codes(HuffEntry *he, uint16_t codes_count[33]) } } -static void count_usage(uint8_t *src, int width, +static void count_usage(const uint8_t *src, int width, int height, PTable *counts) { - int i, j; - - for (j = 0; j < height; j++) { - for (i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) counts[src[i]].prob++; - } src += width; } } @@ -352,17 +369,37 @@ static void magy_huffman_compute_bits(PTable *prob_table, HuffEntry *distincts, } } -static int encode_table(AVCodecContext *avctx, uint8_t *dst, - int width, int height, - PutBitContext *pb, HuffEntry *he) +static int count_plane_slice(AVCodecContext *avctx, int n, int plane) +{ + MagicYUVContext *s = avctx->priv_data; + Slice *sl = &s->slices[n * s->planes + plane]; + const uint8_t *dst = sl->slice; + PTable *counts = sl->counts; + + memset(counts, 0, sizeof(sl->counts)); + + count_usage(dst, AV_CEIL_RSHIFT(avctx->width, s->hshift[plane]), + AV_CEIL_RSHIFT(s->slice_height, s->vshift[plane]), counts); + + return 0; +} + +static int encode_table(AVCodecContext *avctx, + PutBitContext *pb, HuffEntry *he, int plane) { + MagicYUVContext *s = avctx->priv_data; PTable counts[256] = { {0} }; uint16_t codes_counts[33] = { 0 }; - int i; - count_usage(dst, width, height, counts); + for (int n = 0; n < s->nb_slices; n++) { + Slice *sl = &s->slices[n * s->planes + plane]; + PTable *slice_counts = sl->counts; + + for (int i = 0; i < 256; i++) + counts[i].prob = slice_counts[i].prob; + } - for (i = 0; i < 256; i++) { + for (int i = 0; i < 256; i++) { counts[i].prob++; counts[i].value = i; } @@ -371,7 +408,7 @@ static int encode_table(AVCodecContext *avctx, uint8_t *dst, calculate_codes(he, codes_counts); - for (i = 0; i < 256; i++) { + for (int i = 0; i < 256; i++) { put_bits(pb, 1, 0); put_bits(pb, 7, he[i].len); } @@ -379,11 +416,28 @@ static int encode_table(AVCodecContext *avctx, uint8_t *dst, return 0; } -static int encode_slice(uint8_t *src, uint8_t *dst, int dst_size, - int width, int height, HuffEntry *he, int prediction) +static int encode_plane_slice_raw(const uint8_t *src, uint8_t *dst, unsigned dst_size, + int width, int height, int prediction) +{ + unsigned count = width * height; + + dst[0] = 1; + dst[1] = prediction; + + memcpy(dst + 2, src, count); + count += 2; + AV_WN32(dst + count, 0); + if (count & 3) + count += 4 - (count & 3); + + return count; +} + +static int encode_plane_slice(const uint8_t *src, uint8_t *dst, unsigned dst_size, + int width, int height, HuffEntry *he, int prediction) { + const uint8_t *osrc = src; PutBitContext pb; - int i, j; int count; init_put_bits(&pb, dst, dst_size); @@ -391,10 +445,13 @@ static int encode_slice(uint8_t *src, uint8_t *dst, int dst_size, put_bits(&pb, 8, 0); put_bits(&pb, 8, prediction); - for (j = 0; j < height; j++) { - for (i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { const int idx = src[i]; - put_bits(&pb, he[idx].len, he[idx].code); + const int len = he[idx].len; + if (put_bits_left(&pb) < len + 32) + return encode_plane_slice_raw(osrc, dst, dst_size, width, height, prediction); + put_bits(&pb, len, he[idx].code); } src += width; @@ -410,13 +467,98 @@ static int encode_slice(uint8_t *src, uint8_t *dst, int dst_size, return put_bytes_output(&pb); } +static int encode_slice(AVCodecContext *avctx, void *tdata, + int n, int threadnr) +{ + MagicYUVContext *s = avctx->priv_data; + const int slice_height = s->slice_height; + const int last_height = FFMIN(slice_height, avctx->height - n * slice_height); + const int height = (n < (s->nb_slices - 1)) ? slice_height : last_height; + + for (int i = 0; i < s->planes; i++) { + Slice *sl = &s->slices[n * s->planes + i]; + + sl->size = + encode_plane_slice(sl->slice, + sl->bitslice, + s->bitslice_size, + AV_CEIL_RSHIFT(avctx->width, s->hshift[i]), + AV_CEIL_RSHIFT(height, s->vshift[i]), + s->he[i], s->frame_pred); + } + + return 0; +} + +static int predict_slice(AVCodecContext *avctx, void *tdata, + int n, int threadnr) +{ + const int aligned_width = FFALIGN(avctx->width, av_cpu_max_align()); + MagicYUVContext *s = avctx->priv_data; + const int slice_height = s->slice_height; + const int last_height = FFMIN(slice_height, avctx->height - n * slice_height); + const int height = (n < (s->nb_slices - 1)) ? slice_height : last_height; + const int width = avctx->width; + AVFrame *frame = tdata; + + if (s->correlate) { + uint8_t *decorrelated[2] = { s->decorrelate_buf[0] + n * slice_height * aligned_width, + s->decorrelate_buf[1] + n * slice_height * aligned_width }; + const int decorrelate_linesize = aligned_width; + const uint8_t *const data[4] = { decorrelated[0], frame->data[0] + n * slice_height * frame->linesize[0], + decorrelated[1], s->planes == 4 ? frame->data[3] + n * slice_height * frame->linesize[3] : NULL }; + const uint8_t *r, *g, *b; + const int linesize[4] = { decorrelate_linesize, frame->linesize[0], + decorrelate_linesize, frame->linesize[3] }; + + g = frame->data[0] + n * slice_height * frame->linesize[0]; + b = frame->data[1] + n * slice_height * frame->linesize[1]; + r = frame->data[2] + n * slice_height * frame->linesize[2]; + + for (int i = 0; i < height; i++) { + s->llvidencdsp.diff_bytes(decorrelated[0], b, g, width); + s->llvidencdsp.diff_bytes(decorrelated[1], r, g, width); + g += frame->linesize[0]; + b += frame->linesize[1]; + r += frame->linesize[2]; + decorrelated[0] += decorrelate_linesize; + decorrelated[1] += decorrelate_linesize; + } + + for (int i = 0; i < s->planes; i++) { + Slice *sl = &s->slices[n * s->planes + i]; + + s->predict(s, data[i], sl->slice, linesize[i], + frame->width, height); + } + } else { + for (int i = 0; i < s->planes; i++) { + Slice *sl = &s->slices[n * s->planes + i]; + + s->predict(s, frame->data[i] + n * (slice_height >> s->vshift[i]) * frame->linesize[i], + sl->slice, + frame->linesize[i], + AV_CEIL_RSHIFT(frame->width, s->hshift[i]), + AV_CEIL_RSHIFT(height, s->vshift[i])); + } + } + + for (int p = 0; p < s->planes; p++) + count_plane_slice(avctx, n, p); + + return 0; +} + static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_packet) { MagicYUVContext *s = avctx->priv_data; - PutByteContext pb; const int width = avctx->width, height = avctx->height; - int pos, slice, i, j, ret = 0; + const int slice_height = s->slice_height; + unsigned tables_size; + PutBitContext pbit; + PutByteContext pb; + int pos, ret = 0; ret = ff_alloc_packet(avctx, pkt, (256 + 4 * s->nb_slices + width * height) * s->planes + 256); @@ -439,92 +581,53 @@ static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, bytestream2_put_le32(&pb, avctx->width); bytestream2_put_le32(&pb, avctx->height); bytestream2_put_le32(&pb, avctx->width); - bytestream2_put_le32(&pb, avctx->height); + bytestream2_put_le32(&pb, slice_height); bytestream2_put_le32(&pb, 0); - for (i = 0; i < s->planes; i++) { + for (int i = 0; i < s->planes; i++) { bytestream2_put_le32(&pb, 0); - for (j = 1; j < s->nb_slices; j++) { + for (int j = 1; j < s->nb_slices; j++) bytestream2_put_le32(&pb, 0); - } } bytestream2_put_byte(&pb, s->planes); - for (i = 0; i < s->planes; i++) { - for (slice = 0; slice < s->nb_slices; slice++) { - bytestream2_put_byte(&pb, i); - } + for (int i = 0; i < s->planes; i++) { + for (int n = 0; n < s->nb_slices; n++) + bytestream2_put_byte(&pb, n * s->planes + i); } - if (s->correlate) { - uint8_t *decorrelated[2] = { s->decorrelate_buf[0], - s->decorrelate_buf[1] }; - const int decorrelate_linesize = FFALIGN(width, 16); - const uint8_t *const data[4] = { decorrelated[0], frame->data[0], - decorrelated[1], frame->data[3] }; - const uint8_t *r, *g, *b; - const int linesize[4] = { decorrelate_linesize, frame->linesize[0], - decorrelate_linesize, frame->linesize[3] }; + avctx->execute2(avctx, predict_slice, (void *)frame, NULL, s->nb_slices); - g = frame->data[0]; - b = frame->data[1]; - r = frame->data[2]; + init_put_bits(&pbit, pkt->data + bytestream2_tell_p(&pb), bytestream2_get_bytes_left_p(&pb)); - for (i = 0; i < height; i++) { - s->llvidencdsp.diff_bytes(decorrelated[0], b, g, width); - s->llvidencdsp.diff_bytes(decorrelated[1], r, g, width); - g += frame->linesize[0]; - b += frame->linesize[1]; - r += frame->linesize[2]; - decorrelated[0] += decorrelate_linesize; - decorrelated[1] += decorrelate_linesize; - } + for (int i = 0; i < s->planes; i++) + encode_table(avctx, &pbit, s->he[i], i); - for (i = 0; i < s->planes; i++) { - for (slice = 0; slice < s->nb_slices; slice++) { - s->predict(s, data[i], s->slices[i], linesize[i], - frame->width, frame->height); - } - } - } else { - for (i = 0; i < s->planes; i++) { - for (slice = 0; slice < s->nb_slices; slice++) { - s->predict(s, frame->data[i], s->slices[i], frame->linesize[i], - AV_CEIL_RSHIFT(frame->width, s->hshift[i]), - AV_CEIL_RSHIFT(frame->height, s->vshift[i])); - } - } - } + tables_size = put_bytes_count(&pbit, 1); + bytestream2_skip_p(&pb, tables_size); - init_put_bits(&s->pb, pkt->data + bytestream2_tell_p(&pb), bytestream2_get_bytes_left_p(&pb)); + avctx->execute2(avctx, encode_slice, NULL, NULL, s->nb_slices); - for (i = 0; i < s->planes; i++) { - encode_table(avctx, s->slices[i], - AV_CEIL_RSHIFT(frame->width, s->hshift[i]), - AV_CEIL_RSHIFT(frame->height, s->vshift[i]), - &s->pb, s->he[i]); - } - s->tables_size = put_bytes_count(&s->pb, 1); - bytestream2_skip_p(&pb, s->tables_size); - - for (i = 0; i < s->planes; i++) { - unsigned slice_size; - - s->slice_pos[i] = bytestream2_tell_p(&pb); - slice_size = encode_slice(s->slices[i], pkt->data + bytestream2_tell_p(&pb), - bytestream2_get_bytes_left_p(&pb), - AV_CEIL_RSHIFT(frame->width, s->hshift[i]), - AV_CEIL_RSHIFT(frame->height, s->vshift[i]), - s->he[i], s->frame_pred); - bytestream2_skip_p(&pb, slice_size); + for (int n = 0; n < s->nb_slices; n++) { + for (int i = 0; i < s->planes; i++) { + Slice *sl = &s->slices[n * s->planes + i]; + + sl->pos = bytestream2_tell_p(&pb); + + bytestream2_put_buffer(&pb, sl->bitslice, sl->size); + } } pos = bytestream2_tell_p(&pb); bytestream2_seek_p(&pb, 32, SEEK_SET); - bytestream2_put_le32(&pb, s->slice_pos[0] - 32); - for (i = 0; i < s->planes; i++) { - bytestream2_put_le32(&pb, s->slice_pos[i] - 32); + bytestream2_put_le32(&pb, s->slices[0].pos - 32); + for (int i = 0; i < s->planes; i++) { + for (int n = 0; n < s->nb_slices; n++) { + Slice *sl = &s->slices[n * s->planes + i]; + + bytestream2_put_le32(&pb, sl->pos - 32); + } } bytestream2_seek_p(&pb, pos, SEEK_SET); @@ -538,10 +641,14 @@ static int magy_encode_frame(AVCodecContext *avctx, AVPacket *pkt, static av_cold int magy_encode_close(AVCodecContext *avctx) { MagicYUVContext *s = avctx->priv_data; - int i; - for (i = 0; i < s->planes; i++) - av_freep(&s->slices[i]); + for (int i = 0; i < s->planes * s->nb_slices && s->slices; i++) { + Slice *sl = &s->slices[i]; + + av_freep(&sl->slice); + av_freep(&sl->bitslice); + } + av_freep(&s->slices); av_freep(&s->decorrelate_buf); return 0; @@ -550,10 +657,10 @@ static av_cold int magy_encode_close(AVCodecContext *avctx) #define OFFSET(x) offsetof(MagicYUVContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "pred", "Prediction method", OFFSET(frame_pred), AV_OPT_TYPE_INT, {.i64=LEFT}, LEFT, MEDIAN, VE, "pred" }, - { "left", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LEFT }, 0, 0, VE, "pred" }, - { "gradient", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = GRADIENT }, 0, 0, VE, "pred" }, - { "median", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MEDIAN }, 0, 0, VE, "pred" }, + { "pred", "Prediction method", OFFSET(frame_pred), AV_OPT_TYPE_INT, {.i64=LEFT}, LEFT, MEDIAN, VE, .unit = "pred" }, + { "left", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LEFT }, 0, 0, VE, .unit = "pred" }, + { "gradient", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = GRADIENT }, 0, 0, VE, .unit = "pred" }, + { "median", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MEDIAN }, 0, 0, VE, .unit = "pred" }, { NULL}, }; @@ -570,6 +677,7 @@ const FFCodec ff_magicyuv_encoder = { .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_MAGICYUV, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, .priv_data_size = sizeof(MagicYUVContext), .p.priv_class = &magicyuv_class, diff --git a/libavcodec/mdct_fixed_32.c b/libavcodec/mdct_fixed_32.c deleted file mode 100644 index eaa6355e679..00000000000 --- a/libavcodec/mdct_fixed_32.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2012 - * MIPS Technologies, Inc., California. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Authors: Stanislav Ocovaj (socovaj@mips.com) - * Goran Cordasic (goran@mips.com) - * Djordje Pesut (djordje@mips.com) - * - * 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 - */ - -#define FFT_FLOAT 0 -#include "mdct_template.c" diff --git a/libavcodec/mdct_template.c b/libavcodec/mdct_template.c deleted file mode 100644 index a854ad27008..00000000000 --- a/libavcodec/mdct_template.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * MDCT/IMDCT transforms - * Copyright (c) 2002 Fabrice Bellard - * - * 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 -#include -#include "libavutil/common.h" -#include "libavutil/libm.h" -#include "libavutil/mathematics.h" -#include "fft.h" -#include "fft-internal.h" - -/** - * @file - * MDCT/IMDCT transforms. - */ - -#if FFT_FLOAT -# define RSCALE(x, y) ((x) + (y)) -#else -# define RSCALE(x, y) ((int)((x) + (unsigned)(y) + 32) >> 6) -#endif - -/** - * init MDCT or IMDCT computation. - */ -av_cold int ff_mdct_init(FFTContext *s, int nbits, int inverse, double scale) -{ - int n, n4, i; - double alpha, theta; - int tstep; - - memset(s, 0, sizeof(*s)); - n = 1 << nbits; - s->mdct_bits = nbits; - s->mdct_size = n; - n4 = n >> 2; - s->mdct_permutation = FF_MDCT_PERM_NONE; - - if (ff_fft_init(s, s->mdct_bits - 2, inverse) < 0) - goto fail; - - s->tcos = av_malloc_array(n/2, sizeof(FFTSample)); - if (!s->tcos) - goto fail; - - switch (s->mdct_permutation) { - case FF_MDCT_PERM_NONE: - s->tsin = s->tcos + n4; - tstep = 1; - break; - case FF_MDCT_PERM_INTERLEAVE: - s->tsin = s->tcos + 1; - tstep = 2; - break; - default: - goto fail; - } - - theta = 1.0 / 8.0 + (scale < 0 ? n4 : 0); - scale = sqrt(fabs(scale)); - for(i=0;itcos[i*tstep] = lrint(-cos(alpha) * 2147483648.0); - s->tsin[i*tstep] = lrint(-sin(alpha) * 2147483648.0); -#else - s->tcos[i*tstep] = FIX15(-cos(alpha) * scale); - s->tsin[i*tstep] = FIX15(-sin(alpha) * scale); -#endif - } - return 0; - fail: - ff_mdct_end(s); - return -1; -} - -/** - * Compute the middle half of the inverse MDCT of size N = 2^nbits, - * thus excluding the parts that can be derived by symmetry - * @param output N/2 samples - * @param input N/2 samples - */ -void ff_imdct_half_c(FFTContext *s, FFTSample *output, const FFTSample *input) -{ - int k, n8, n4, n2, n, j; - const uint16_t *revtab = s->revtab; - const FFTSample *tcos = s->tcos; - const FFTSample *tsin = s->tsin; - const FFTSample *in1, *in2; - FFTComplex *z = (FFTComplex *)output; - - n = 1 << s->mdct_bits; - n2 = n >> 1; - n4 = n >> 2; - n8 = n >> 3; - - /* pre rotation */ - in1 = input; - in2 = input + n2 - 1; - for(k = 0; k < n4; k++) { - j=revtab[k]; - CMUL(z[j].re, z[j].im, *in2, *in1, tcos[k], tsin[k]); - in1 += 2; - in2 -= 2; - } - s->fft_calc(s, z); - - /* post rotation + reordering */ - for(k = 0; k < n8; k++) { - FFTSample r0, i0, r1, i1; - CMUL(r0, i1, z[n8-k-1].im, z[n8-k-1].re, tsin[n8-k-1], tcos[n8-k-1]); - CMUL(r1, i0, z[n8+k ].im, z[n8+k ].re, tsin[n8+k ], tcos[n8+k ]); - z[n8-k-1].re = r0; - z[n8-k-1].im = i0; - z[n8+k ].re = r1; - z[n8+k ].im = i1; - } -} - -/** - * Compute inverse MDCT of size N = 2^nbits - * @param output N samples - * @param input N/2 samples - */ -void ff_imdct_calc_c(FFTContext *s, FFTSample *output, const FFTSample *input) -{ - int k; - int n = 1 << s->mdct_bits; - int n2 = n >> 1; - int n4 = n >> 2; - - ff_imdct_half_c(s, output+n4, input); - - for(k = 0; k < n4; k++) { - output[k] = -output[n2-k-1]; - output[n-k-1] = output[n2+k]; - } -} - -/** - * Compute MDCT of size N = 2^nbits - * @param input N samples - * @param out N/2 samples - */ -void ff_mdct_calc_c(FFTContext *s, FFTSample *out, const FFTSample *input) -{ - int i, j, n, n8, n4, n2, n3; - FFTDouble re, im; - const uint16_t *revtab = s->revtab; - const FFTSample *tcos = s->tcos; - const FFTSample *tsin = s->tsin; - FFTComplex *x = (FFTComplex *)out; - - n = 1 << s->mdct_bits; - n2 = n >> 1; - n4 = n >> 2; - n8 = n >> 3; - n3 = 3 * n4; - - /* pre rotation */ - for(i=0;ifft_calc(s, x); - - /* post rotation */ - for(i=0;itcos); - ff_fft_end(s); -} diff --git a/libavcodec/mdec.c b/libavcodec/mdec.c index 640b671a0fe..c4904216b81 100644 --- a/libavcodec/mdec.c +++ b/libavcodec/mdec.c @@ -68,7 +68,7 @@ static inline int mdec_decode_block_intra(MDECContext *a, int16_t *block, int n) const int qscale = a->qscale; /* DC coefficient */ - if (a->version == 2) { + if (a->version <= 2) { block[0] = 2 * get_sbits(&a->gb, 10) + 1024; } else { component = (n <= 3 ? 0 : n - 4 + 1); @@ -177,7 +177,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_thread_get_buffer(avctx, frame, 0)) < 0) return ret; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; av_fast_padded_malloc(&a->bitstream_buffer, &a->bitstream_buffer_size, buf_size); if (!a->bitstream_buffer) diff --git a/libavcodec/me_cmp.c b/libavcodec/me_cmp.c index cd05e63ffd6..db3fb1260cd 100644 --- a/libavcodec/me_cmp.c +++ b/libavcodec/me_cmp.c @@ -1064,6 +1064,8 @@ av_cold void ff_me_cmp_init(MECmpContext *c, AVCodecContext *avctx) ff_me_cmp_init_arm(c, avctx); #elif ARCH_PPC ff_me_cmp_init_ppc(c, avctx); +#elif ARCH_RISCV + ff_me_cmp_init_riscv(c, avctx); #elif ARCH_X86 ff_me_cmp_init_x86(c, avctx); #elif ARCH_MIPS diff --git a/libavcodec/me_cmp.h b/libavcodec/me_cmp.h index aefd32a7dc9..fee0ecb28e7 100644 --- a/libavcodec/me_cmp.h +++ b/libavcodec/me_cmp.h @@ -86,6 +86,7 @@ void ff_me_cmp_init_aarch64(MECmpContext *c, AVCodecContext *avctx); void ff_me_cmp_init_alpha(MECmpContext *c, AVCodecContext *avctx); void ff_me_cmp_init_arm(MECmpContext *c, AVCodecContext *avctx); void ff_me_cmp_init_ppc(MECmpContext *c, AVCodecContext *avctx); +void ff_me_cmp_init_riscv(MECmpContext *c, AVCodecContext *avctx); void ff_me_cmp_init_x86(MECmpContext *c, AVCodecContext *avctx); void ff_me_cmp_init_mips(MECmpContext *c, AVCodecContext *avctx); diff --git a/libavcodec/mediacodec_wrapper.c b/libavcodec/mediacodec_wrapper.c index 34ec2134aa1..306359071e3 100644 --- a/libavcodec/mediacodec_wrapper.c +++ b/libavcodec/mediacodec_wrapper.c @@ -60,31 +60,33 @@ struct JNIAMediaCodecListFields { jfieldID level_id; }; +#define OFFSET(x) offsetof(struct JNIAMediaCodecListFields, x) static const struct FFJniField jni_amediacodeclist_mapping[] = { - { "android/media/MediaCodecList", NULL, NULL, FF_JNI_CLASS, offsetof(struct JNIAMediaCodecListFields, mediacodec_list_class), 1 }, - { "android/media/MediaCodecList", "", "(I)V", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecListFields, init_id), 0 }, - { "android/media/MediaCodecList", "findDecoderForFormat", "(Landroid/media/MediaFormat;)Ljava/lang/String;", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecListFields, find_decoder_for_format_id), 0 }, + { "android/media/MediaCodecList", NULL, NULL, FF_JNI_CLASS, OFFSET(mediacodec_list_class), 1 }, + { "android/media/MediaCodecList", "", "(I)V", FF_JNI_METHOD, OFFSET(init_id), 0 }, + { "android/media/MediaCodecList", "findDecoderForFormat", "(Landroid/media/MediaFormat;)Ljava/lang/String;", FF_JNI_METHOD, OFFSET(find_decoder_for_format_id), 0 }, - { "android/media/MediaCodecList", "getCodecCount", "()I", FF_JNI_STATIC_METHOD, offsetof(struct JNIAMediaCodecListFields, get_codec_count_id), 1 }, - { "android/media/MediaCodecList", "getCodecInfoAt", "(I)Landroid/media/MediaCodecInfo;", FF_JNI_STATIC_METHOD, offsetof(struct JNIAMediaCodecListFields, get_codec_info_at_id), 1 }, + { "android/media/MediaCodecList", "getCodecCount", "()I", FF_JNI_STATIC_METHOD, OFFSET(get_codec_count_id), 1 }, + { "android/media/MediaCodecList", "getCodecInfoAt", "(I)Landroid/media/MediaCodecInfo;", FF_JNI_STATIC_METHOD, OFFSET(get_codec_info_at_id), 1 }, - { "android/media/MediaCodecInfo", NULL, NULL, FF_JNI_CLASS, offsetof(struct JNIAMediaCodecListFields, mediacodec_info_class), 1 }, - { "android/media/MediaCodecInfo", "getName", "()Ljava/lang/String;", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecListFields, get_name_id), 1 }, - { "android/media/MediaCodecInfo", "getCapabilitiesForType", "(Ljava/lang/String;)Landroid/media/MediaCodecInfo$CodecCapabilities;", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecListFields, get_codec_capabilities_id), 1 }, - { "android/media/MediaCodecInfo", "getSupportedTypes", "()[Ljava/lang/String;", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecListFields, get_supported_types_id), 1 }, - { "android/media/MediaCodecInfo", "isEncoder", "()Z", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecListFields, is_encoder_id), 1 }, - { "android/media/MediaCodecInfo", "isSoftwareOnly", "()Z", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecListFields, is_software_only_id), 0 }, + { "android/media/MediaCodecInfo", NULL, NULL, FF_JNI_CLASS, OFFSET(mediacodec_info_class), 1 }, + { "android/media/MediaCodecInfo", "getName", "()Ljava/lang/String;", FF_JNI_METHOD, OFFSET(get_name_id), 1 }, + { "android/media/MediaCodecInfo", "getCapabilitiesForType", "(Ljava/lang/String;)Landroid/media/MediaCodecInfo$CodecCapabilities;", FF_JNI_METHOD, OFFSET(get_codec_capabilities_id), 1 }, + { "android/media/MediaCodecInfo", "getSupportedTypes", "()[Ljava/lang/String;", FF_JNI_METHOD, OFFSET(get_supported_types_id), 1 }, + { "android/media/MediaCodecInfo", "isEncoder", "()Z", FF_JNI_METHOD, OFFSET(is_encoder_id), 1 }, + { "android/media/MediaCodecInfo", "isSoftwareOnly", "()Z", FF_JNI_METHOD, OFFSET(is_software_only_id), 0 }, - { "android/media/MediaCodecInfo$CodecCapabilities", NULL, NULL, FF_JNI_CLASS, offsetof(struct JNIAMediaCodecListFields, codec_capabilities_class), 1 }, - { "android/media/MediaCodecInfo$CodecCapabilities", "colorFormats", "[I", FF_JNI_FIELD, offsetof(struct JNIAMediaCodecListFields, color_formats_id), 1 }, - { "android/media/MediaCodecInfo$CodecCapabilities", "profileLevels", "[Landroid/media/MediaCodecInfo$CodecProfileLevel;", FF_JNI_FIELD, offsetof(struct JNIAMediaCodecListFields, profile_levels_id), 1 }, + { "android/media/MediaCodecInfo$CodecCapabilities", NULL, NULL, FF_JNI_CLASS, OFFSET(codec_capabilities_class), 1 }, + { "android/media/MediaCodecInfo$CodecCapabilities", "colorFormats", "[I", FF_JNI_FIELD, OFFSET(color_formats_id), 1 }, + { "android/media/MediaCodecInfo$CodecCapabilities", "profileLevels", "[Landroid/media/MediaCodecInfo$CodecProfileLevel;", FF_JNI_FIELD, OFFSET(profile_levels_id), 1 }, - { "android/media/MediaCodecInfo$CodecProfileLevel", NULL, NULL, FF_JNI_CLASS, offsetof(struct JNIAMediaCodecListFields, codec_profile_level_class), 1 }, - { "android/media/MediaCodecInfo$CodecProfileLevel", "profile", "I", FF_JNI_FIELD, offsetof(struct JNIAMediaCodecListFields, profile_id), 1 }, - { "android/media/MediaCodecInfo$CodecProfileLevel", "level", "I", FF_JNI_FIELD, offsetof(struct JNIAMediaCodecListFields, level_id), 1 }, + { "android/media/MediaCodecInfo$CodecProfileLevel", NULL, NULL, FF_JNI_CLASS, OFFSET(codec_profile_level_class), 1 }, + { "android/media/MediaCodecInfo$CodecProfileLevel", "profile", "I", FF_JNI_FIELD, OFFSET(profile_id), 1 }, + { "android/media/MediaCodecInfo$CodecProfileLevel", "level", "I", FF_JNI_FIELD, OFFSET(level_id), 1 }, { NULL } }; +#undef OFFSET struct JNIAMediaFormatFields { @@ -110,29 +112,31 @@ struct JNIAMediaFormatFields { }; +#define OFFSET(x) offsetof(struct JNIAMediaFormatFields, x) static const struct FFJniField jni_amediaformat_mapping[] = { - { "android/media/MediaFormat", NULL, NULL, FF_JNI_CLASS, offsetof(struct JNIAMediaFormatFields, mediaformat_class), 1 }, + { "android/media/MediaFormat", NULL, NULL, FF_JNI_CLASS, OFFSET(mediaformat_class), 1 }, - { "android/media/MediaFormat", "", "()V", FF_JNI_METHOD, offsetof(struct JNIAMediaFormatFields, init_id), 1 }, + { "android/media/MediaFormat", "", "()V", FF_JNI_METHOD, OFFSET(init_id), 1 }, - { "android/media/MediaFormat", "containsKey", "(Ljava/lang/String;)Z", FF_JNI_METHOD,offsetof(struct JNIAMediaFormatFields, contains_key_id), 1 }, + { "android/media/MediaFormat", "containsKey", "(Ljava/lang/String;)Z", FF_JNI_METHOD, OFFSET(contains_key_id), 1 }, - { "android/media/MediaFormat", "getInteger", "(Ljava/lang/String;)I", FF_JNI_METHOD, offsetof(struct JNIAMediaFormatFields, get_integer_id), 1 }, - { "android/media/MediaFormat", "getLong", "(Ljava/lang/String;)J", FF_JNI_METHOD, offsetof(struct JNIAMediaFormatFields, get_long_id), 1 }, - { "android/media/MediaFormat", "getFloat", "(Ljava/lang/String;)F", FF_JNI_METHOD, offsetof(struct JNIAMediaFormatFields, get_float_id), 1 }, - { "android/media/MediaFormat", "getByteBuffer", "(Ljava/lang/String;)Ljava/nio/ByteBuffer;", FF_JNI_METHOD, offsetof(struct JNIAMediaFormatFields, get_bytebuffer_id), 1 }, - { "android/media/MediaFormat", "getString", "(Ljava/lang/String;)Ljava/lang/String;", FF_JNI_METHOD, offsetof(struct JNIAMediaFormatFields, get_string_id), 1 }, + { "android/media/MediaFormat", "getInteger", "(Ljava/lang/String;)I", FF_JNI_METHOD, OFFSET(get_integer_id), 1 }, + { "android/media/MediaFormat", "getLong", "(Ljava/lang/String;)J", FF_JNI_METHOD, OFFSET(get_long_id), 1 }, + { "android/media/MediaFormat", "getFloat", "(Ljava/lang/String;)F", FF_JNI_METHOD, OFFSET(get_float_id), 1 }, + { "android/media/MediaFormat", "getByteBuffer", "(Ljava/lang/String;)Ljava/nio/ByteBuffer;", FF_JNI_METHOD, OFFSET(get_bytebuffer_id), 1 }, + { "android/media/MediaFormat", "getString", "(Ljava/lang/String;)Ljava/lang/String;", FF_JNI_METHOD, OFFSET(get_string_id), 1 }, - { "android/media/MediaFormat", "setInteger", "(Ljava/lang/String;I)V", FF_JNI_METHOD, offsetof(struct JNIAMediaFormatFields, set_integer_id), 1 }, - { "android/media/MediaFormat", "setLong", "(Ljava/lang/String;J)V", FF_JNI_METHOD, offsetof(struct JNIAMediaFormatFields, set_long_id), 1 }, - { "android/media/MediaFormat", "setFloat", "(Ljava/lang/String;F)V", FF_JNI_METHOD, offsetof(struct JNIAMediaFormatFields, set_float_id), 1 }, - { "android/media/MediaFormat", "setByteBuffer", "(Ljava/lang/String;Ljava/nio/ByteBuffer;)V", FF_JNI_METHOD, offsetof(struct JNIAMediaFormatFields, set_bytebuffer_id), 1 }, - { "android/media/MediaFormat", "setString", "(Ljava/lang/String;Ljava/lang/String;)V", FF_JNI_METHOD, offsetof(struct JNIAMediaFormatFields, set_string_id), 1 }, + { "android/media/MediaFormat", "setInteger", "(Ljava/lang/String;I)V", FF_JNI_METHOD, OFFSET(set_integer_id), 1 }, + { "android/media/MediaFormat", "setLong", "(Ljava/lang/String;J)V", FF_JNI_METHOD, OFFSET(set_long_id), 1 }, + { "android/media/MediaFormat", "setFloat", "(Ljava/lang/String;F)V", FF_JNI_METHOD, OFFSET(set_float_id), 1 }, + { "android/media/MediaFormat", "setByteBuffer", "(Ljava/lang/String;Ljava/nio/ByteBuffer;)V", FF_JNI_METHOD, OFFSET(set_bytebuffer_id), 1 }, + { "android/media/MediaFormat", "setString", "(Ljava/lang/String;Ljava/lang/String;)V", FF_JNI_METHOD, OFFSET(set_string_id), 1 }, - { "android/media/MediaFormat", "toString", "()Ljava/lang/String;", FF_JNI_METHOD, offsetof(struct JNIAMediaFormatFields, to_string_id), 1 }, + { "android/media/MediaFormat", "toString", "()Ljava/lang/String;", FF_JNI_METHOD, OFFSET(to_string_id), 1 }, { NULL } }; +#undef OFFSET static const AVClass amediaformat_class = { .class_name = "amediaformat", @@ -202,57 +206,59 @@ struct JNIAMediaCodecFields { }; +#define OFFSET(x) offsetof(struct JNIAMediaCodecFields, x) static const struct FFJniField jni_amediacodec_mapping[] = { - { "android/media/MediaCodec", NULL, NULL, FF_JNI_CLASS, offsetof(struct JNIAMediaCodecFields, mediacodec_class), 1 }, + { "android/media/MediaCodec", NULL, NULL, FF_JNI_CLASS, OFFSET(mediacodec_class), 1 }, - { "android/media/MediaCodec", "INFO_TRY_AGAIN_LATER", "I", FF_JNI_STATIC_FIELD, offsetof(struct JNIAMediaCodecFields, info_try_again_later_id), 1 }, - { "android/media/MediaCodec", "INFO_OUTPUT_BUFFERS_CHANGED", "I", FF_JNI_STATIC_FIELD, offsetof(struct JNIAMediaCodecFields, info_output_buffers_changed_id), 1 }, - { "android/media/MediaCodec", "INFO_OUTPUT_FORMAT_CHANGED", "I", FF_JNI_STATIC_FIELD, offsetof(struct JNIAMediaCodecFields, info_output_format_changed_id), 1 }, + { "android/media/MediaCodec", "INFO_TRY_AGAIN_LATER", "I", FF_JNI_STATIC_FIELD, OFFSET(info_try_again_later_id), 1 }, + { "android/media/MediaCodec", "INFO_OUTPUT_BUFFERS_CHANGED", "I", FF_JNI_STATIC_FIELD, OFFSET(info_output_buffers_changed_id), 1 }, + { "android/media/MediaCodec", "INFO_OUTPUT_FORMAT_CHANGED", "I", FF_JNI_STATIC_FIELD, OFFSET(info_output_format_changed_id), 1 }, - { "android/media/MediaCodec", "BUFFER_FLAG_CODEC_CONFIG", "I", FF_JNI_STATIC_FIELD, offsetof(struct JNIAMediaCodecFields, buffer_flag_codec_config_id), 1 }, - { "android/media/MediaCodec", "BUFFER_FLAG_END_OF_STREAM", "I", FF_JNI_STATIC_FIELD, offsetof(struct JNIAMediaCodecFields, buffer_flag_end_of_stream_id), 1 }, - { "android/media/MediaCodec", "BUFFER_FLAG_KEY_FRAME", "I", FF_JNI_STATIC_FIELD, offsetof(struct JNIAMediaCodecFields, buffer_flag_key_frame_id), 0 }, + { "android/media/MediaCodec", "BUFFER_FLAG_CODEC_CONFIG", "I", FF_JNI_STATIC_FIELD, OFFSET(buffer_flag_codec_config_id), 1 }, + { "android/media/MediaCodec", "BUFFER_FLAG_END_OF_STREAM", "I", FF_JNI_STATIC_FIELD, OFFSET(buffer_flag_end_of_stream_id), 1 }, + { "android/media/MediaCodec", "BUFFER_FLAG_KEY_FRAME", "I", FF_JNI_STATIC_FIELD, OFFSET(buffer_flag_key_frame_id), 0 }, - { "android/media/MediaCodec", "CONFIGURE_FLAG_ENCODE", "I", FF_JNI_STATIC_FIELD, offsetof(struct JNIAMediaCodecFields, configure_flag_encode_id), 1 }, + { "android/media/MediaCodec", "CONFIGURE_FLAG_ENCODE", "I", FF_JNI_STATIC_FIELD, OFFSET(configure_flag_encode_id), 1 }, - { "android/media/MediaCodec", "createByCodecName", "(Ljava/lang/String;)Landroid/media/MediaCodec;", FF_JNI_STATIC_METHOD, offsetof(struct JNIAMediaCodecFields, create_by_codec_name_id), 1 }, - { "android/media/MediaCodec", "createDecoderByType", "(Ljava/lang/String;)Landroid/media/MediaCodec;", FF_JNI_STATIC_METHOD, offsetof(struct JNIAMediaCodecFields, create_decoder_by_type_id), 1 }, - { "android/media/MediaCodec", "createEncoderByType", "(Ljava/lang/String;)Landroid/media/MediaCodec;", FF_JNI_STATIC_METHOD, offsetof(struct JNIAMediaCodecFields, create_encoder_by_type_id), 1 }, + { "android/media/MediaCodec", "createByCodecName", "(Ljava/lang/String;)Landroid/media/MediaCodec;", FF_JNI_STATIC_METHOD, OFFSET(create_by_codec_name_id), 1 }, + { "android/media/MediaCodec", "createDecoderByType", "(Ljava/lang/String;)Landroid/media/MediaCodec;", FF_JNI_STATIC_METHOD, OFFSET(create_decoder_by_type_id), 1 }, + { "android/media/MediaCodec", "createEncoderByType", "(Ljava/lang/String;)Landroid/media/MediaCodec;", FF_JNI_STATIC_METHOD, OFFSET(create_encoder_by_type_id), 1 }, - { "android/media/MediaCodec", "getName", "()Ljava/lang/String;", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, get_name_id), 1 }, + { "android/media/MediaCodec", "getName", "()Ljava/lang/String;", FF_JNI_METHOD, OFFSET(get_name_id), 1 }, - { "android/media/MediaCodec", "configure", "(Landroid/media/MediaFormat;Landroid/view/Surface;Landroid/media/MediaCrypto;I)V", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, configure_id), 1 }, - { "android/media/MediaCodec", "start", "()V", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, start_id), 1 }, - { "android/media/MediaCodec", "flush", "()V", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, flush_id), 1 }, - { "android/media/MediaCodec", "stop", "()V", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, stop_id), 1 }, - { "android/media/MediaCodec", "release", "()V", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, release_id), 1 }, + { "android/media/MediaCodec", "configure", "(Landroid/media/MediaFormat;Landroid/view/Surface;Landroid/media/MediaCrypto;I)V", FF_JNI_METHOD, OFFSET(configure_id), 1 }, + { "android/media/MediaCodec", "start", "()V", FF_JNI_METHOD, OFFSET(start_id), 1 }, + { "android/media/MediaCodec", "flush", "()V", FF_JNI_METHOD, OFFSET(flush_id), 1 }, + { "android/media/MediaCodec", "stop", "()V", FF_JNI_METHOD, OFFSET(stop_id), 1 }, + { "android/media/MediaCodec", "release", "()V", FF_JNI_METHOD, OFFSET(release_id), 1 }, - { "android/media/MediaCodec", "getOutputFormat", "()Landroid/media/MediaFormat;", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, get_output_format_id), 1 }, + { "android/media/MediaCodec", "getOutputFormat", "()Landroid/media/MediaFormat;", FF_JNI_METHOD, OFFSET(get_output_format_id), 1 }, - { "android/media/MediaCodec", "dequeueInputBuffer", "(J)I", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, dequeue_input_buffer_id), 1 }, - { "android/media/MediaCodec", "queueInputBuffer", "(IIIJI)V", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, queue_input_buffer_id), 1 }, - { "android/media/MediaCodec", "getInputBuffer", "(I)Ljava/nio/ByteBuffer;", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, get_input_buffer_id), 0 }, - { "android/media/MediaCodec", "getInputBuffers", "()[Ljava/nio/ByteBuffer;", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, get_input_buffers_id), 1 }, + { "android/media/MediaCodec", "dequeueInputBuffer", "(J)I", FF_JNI_METHOD, OFFSET(dequeue_input_buffer_id), 1 }, + { "android/media/MediaCodec", "queueInputBuffer", "(IIIJI)V", FF_JNI_METHOD, OFFSET(queue_input_buffer_id), 1 }, + { "android/media/MediaCodec", "getInputBuffer", "(I)Ljava/nio/ByteBuffer;", FF_JNI_METHOD, OFFSET(get_input_buffer_id), 0 }, + { "android/media/MediaCodec", "getInputBuffers", "()[Ljava/nio/ByteBuffer;", FF_JNI_METHOD, OFFSET(get_input_buffers_id), 1 }, - { "android/media/MediaCodec", "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, dequeue_output_buffer_id), 1 }, - { "android/media/MediaCodec", "getOutputBuffer", "(I)Ljava/nio/ByteBuffer;", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, get_output_buffer_id), 0 }, - { "android/media/MediaCodec", "getOutputBuffers", "()[Ljava/nio/ByteBuffer;", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, get_output_buffers_id), 1 }, - { "android/media/MediaCodec", "releaseOutputBuffer", "(IZ)V", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, release_output_buffer_id), 1 }, - { "android/media/MediaCodec", "releaseOutputBuffer", "(IJ)V", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, release_output_buffer_at_time_id), 0 }, + { "android/media/MediaCodec", "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I", FF_JNI_METHOD, OFFSET(dequeue_output_buffer_id), 1 }, + { "android/media/MediaCodec", "getOutputBuffer", "(I)Ljava/nio/ByteBuffer;", FF_JNI_METHOD, OFFSET(get_output_buffer_id), 0 }, + { "android/media/MediaCodec", "getOutputBuffers", "()[Ljava/nio/ByteBuffer;", FF_JNI_METHOD, OFFSET(get_output_buffers_id), 1 }, + { "android/media/MediaCodec", "releaseOutputBuffer", "(IZ)V", FF_JNI_METHOD, OFFSET(release_output_buffer_id), 1 }, + { "android/media/MediaCodec", "releaseOutputBuffer", "(IJ)V", FF_JNI_METHOD, OFFSET(release_output_buffer_at_time_id), 0 }, - { "android/media/MediaCodec", "setInputSurface", "(Landroid/view/Surface;)V", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, set_input_surface_id), 0 }, - { "android/media/MediaCodec", "signalEndOfInputStream", "()V", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, signal_end_of_input_stream_id), 0 }, + { "android/media/MediaCodec", "setInputSurface", "(Landroid/view/Surface;)V", FF_JNI_METHOD, OFFSET(set_input_surface_id), 0 }, + { "android/media/MediaCodec", "signalEndOfInputStream", "()V", FF_JNI_METHOD, OFFSET(signal_end_of_input_stream_id), 0 }, - { "android/media/MediaCodec$BufferInfo", NULL, NULL, FF_JNI_CLASS, offsetof(struct JNIAMediaCodecFields, mediainfo_class), 1 }, + { "android/media/MediaCodec$BufferInfo", NULL, NULL, FF_JNI_CLASS, OFFSET(mediainfo_class), 1 }, - { "android/media/MediaCodec.BufferInfo", "", "()V", FF_JNI_METHOD, offsetof(struct JNIAMediaCodecFields, init_id), 1 }, - { "android/media/MediaCodec.BufferInfo", "flags", "I", FF_JNI_FIELD, offsetof(struct JNIAMediaCodecFields, flags_id), 1 }, - { "android/media/MediaCodec.BufferInfo", "offset", "I", FF_JNI_FIELD, offsetof(struct JNIAMediaCodecFields, offset_id), 1 }, - { "android/media/MediaCodec.BufferInfo", "presentationTimeUs", "J", FF_JNI_FIELD, offsetof(struct JNIAMediaCodecFields, presentation_time_us_id), 1 }, - { "android/media/MediaCodec.BufferInfo", "size", "I", FF_JNI_FIELD, offsetof(struct JNIAMediaCodecFields, size_id), 1 }, + { "android/media/MediaCodec.BufferInfo", "", "()V", FF_JNI_METHOD, OFFSET(init_id), 1 }, + { "android/media/MediaCodec.BufferInfo", "flags", "I", FF_JNI_FIELD, OFFSET(flags_id), 1 }, + { "android/media/MediaCodec.BufferInfo", "offset", "I", FF_JNI_FIELD, OFFSET(offset_id), 1 }, + { "android/media/MediaCodec.BufferInfo", "presentationTimeUs", "J", FF_JNI_FIELD, OFFSET(presentation_time_us_id), 1 }, + { "android/media/MediaCodec.BufferInfo", "size", "I", FF_JNI_FIELD, OFFSET(size_id), 1 }, { NULL } }; +#undef OFFSET static const AVClass amediacodec_class = { .class_name = "amediacodec", @@ -319,44 +325,142 @@ int ff_AMediaCodecProfile_getProfileFromAVCodecContext(AVCodecContext *avctx) static const int HEVCProfileMain10HDR10 = 0x1000; static const int HEVCProfileMain10HDR10Plus = 0x2000; + static const int VP9Profile0 = 0x01; + static const int VP9Profile1 = 0x02; + static const int VP9Profile2 = 0x04; + static const int VP9Profile3 = 0x08; + static const int VP9Profile2HDR = 0x1000; + static const int VP9Profile3HDR = 0x2000; + static const int VP9Profile2HDR10Plus = 0x4000; + static const int VP9Profile3HDR10Plus = 0x8000; + + static const int MPEG4ProfileSimple = 0x01; + static const int MPEG4ProfileSimpleScalable = 0x02; + static const int MPEG4ProfileCore = 0x04; + static const int MPEG4ProfileMain = 0x08; + static const int MPEG4ProfileNbit = 0x10; + static const int MPEG4ProfileScalableTexture = 0x20; + static const int MPEG4ProfileSimpleFBA = 0x80; + static const int MPEG4ProfileSimpleFace = 0x40; + static const int MPEG4ProfileBasicAnimated = 0x100; + static const int MPEG4ProfileHybrid = 0x200; + static const int MPEG4ProfileAdvancedRealTime = 0x400; + static const int MPEG4ProfileCoreScalable = 0x800; + static const int MPEG4ProfileAdvancedCoding = 0x1000; + static const int MPEG4ProfileAdvancedCore = 0x2000; + static const int MPEG4ProfileAdvancedScalable = 0x4000; + static const int MPEG4ProfileAdvancedSimple = 0x8000; + + + static const int AV1ProfileMain8 = 0x1; + static const int AV1ProfileMain10 = 0x2; + static const int AV1ProfileMain10HDR10 = 0x1000; + static const int AV1ProfileMain10HDR10Plus = 0x2000; + // Unused yet. (void)AVCProfileConstrainedHigh; (void)HEVCProfileMain10HDR10; (void)HEVCProfileMain10HDR10Plus; + (void)VP9Profile2HDR; + (void)VP9Profile3HDR; + (void)VP9Profile2HDR10Plus; + (void)VP9Profile3HDR10Plus; + (void)AV1ProfileMain10; + (void)AV1ProfileMain10HDR10; + (void)AV1ProfileMain10HDR10Plus; if (avctx->codec_id == AV_CODEC_ID_H264) { switch(avctx->profile) { - case FF_PROFILE_H264_BASELINE: + case AV_PROFILE_H264_BASELINE: return AVCProfileBaseline; - case FF_PROFILE_H264_CONSTRAINED_BASELINE: + case AV_PROFILE_H264_CONSTRAINED_BASELINE: return AVCProfileConstrainedBaseline; - case FF_PROFILE_H264_MAIN: + case AV_PROFILE_H264_MAIN: return AVCProfileMain; break; - case FF_PROFILE_H264_EXTENDED: + case AV_PROFILE_H264_EXTENDED: return AVCProfileExtended; - case FF_PROFILE_H264_HIGH: + case AV_PROFILE_H264_HIGH: return AVCProfileHigh; - case FF_PROFILE_H264_HIGH_10: - case FF_PROFILE_H264_HIGH_10_INTRA: + case AV_PROFILE_H264_HIGH_10: + case AV_PROFILE_H264_HIGH_10_INTRA: return AVCProfileHigh10; - case FF_PROFILE_H264_HIGH_422: - case FF_PROFILE_H264_HIGH_422_INTRA: + case AV_PROFILE_H264_HIGH_422: + case AV_PROFILE_H264_HIGH_422_INTRA: return AVCProfileHigh422; - case FF_PROFILE_H264_HIGH_444: - case FF_PROFILE_H264_HIGH_444_INTRA: - case FF_PROFILE_H264_HIGH_444_PREDICTIVE: + case AV_PROFILE_H264_HIGH_444: + case AV_PROFILE_H264_HIGH_444_INTRA: + case AV_PROFILE_H264_HIGH_444_PREDICTIVE: return AVCProfileHigh444; } } else if (avctx->codec_id == AV_CODEC_ID_HEVC) { switch (avctx->profile) { - case FF_PROFILE_HEVC_MAIN: + case AV_PROFILE_HEVC_MAIN: return HEVCProfileMain; - case FF_PROFILE_HEVC_MAIN_STILL_PICTURE: + case AV_PROFILE_HEVC_MAIN_STILL_PICTURE: return HEVCProfileMainStill; - case FF_PROFILE_HEVC_MAIN_10: + case AV_PROFILE_HEVC_MAIN_10: return HEVCProfileMain10; } + } else if (avctx->codec_id == AV_CODEC_ID_VP9) { + switch (avctx->profile) { + case AV_PROFILE_VP9_0: + return VP9Profile0; + case AV_PROFILE_VP9_1: + return VP9Profile1; + case AV_PROFILE_VP9_2: + return VP9Profile2; + case AV_PROFILE_VP9_3: + return VP9Profile3; + } + } else if(avctx->codec_id == AV_CODEC_ID_MPEG4) { + switch (avctx->profile) + { + case AV_PROFILE_MPEG4_SIMPLE: + return MPEG4ProfileSimple; + case AV_PROFILE_MPEG4_SIMPLE_SCALABLE: + return MPEG4ProfileSimpleScalable; + case AV_PROFILE_MPEG4_CORE: + return MPEG4ProfileCore; + case AV_PROFILE_MPEG4_MAIN: + return MPEG4ProfileMain; + case AV_PROFILE_MPEG4_N_BIT: + return MPEG4ProfileNbit; + case AV_PROFILE_MPEG4_SCALABLE_TEXTURE: + return MPEG4ProfileScalableTexture; + case AV_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION: + return MPEG4ProfileSimpleFBA; + case AV_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE: + return MPEG4ProfileBasicAnimated; + case AV_PROFILE_MPEG4_HYBRID: + return MPEG4ProfileHybrid; + case AV_PROFILE_MPEG4_ADVANCED_REAL_TIME: + return MPEG4ProfileAdvancedRealTime; + case AV_PROFILE_MPEG4_CORE_SCALABLE: + return MPEG4ProfileCoreScalable; + case AV_PROFILE_MPEG4_ADVANCED_CODING: + return MPEG4ProfileAdvancedCoding; + case AV_PROFILE_MPEG4_ADVANCED_CORE: + return MPEG4ProfileAdvancedCore; + case AV_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE: + return MPEG4ProfileAdvancedScalable; + case AV_PROFILE_MPEG4_ADVANCED_SIMPLE: + return MPEG4ProfileAdvancedSimple; + case AV_PROFILE_MPEG4_SIMPLE_STUDIO: + // Studio profiles are not supported by mediacodec. + default: + break; + } + } else if(avctx->codec_id == AV_CODEC_ID_AV1) { + switch (avctx->profile) + { + case AV_PROFILE_AV1_MAIN: + return AV1ProfileMain8; + case AV_PROFILE_AV1_HIGH: + case AV_PROFILE_AV1_PROFESSIONAL: + default: + break; + } } return -1; @@ -445,10 +549,8 @@ char *ff_AMediaCodecList_getCodecNameByType(const char *mime, int profile, int e goto done; } - if (codec_name) { - (*env)->DeleteLocalRef(env, codec_name); - codec_name = NULL; - } + (*env)->DeleteLocalRef(env, codec_name); + codec_name = NULL; /* Skip software decoders */ if ( @@ -512,10 +614,8 @@ char *ff_AMediaCodecList_getCodecNameByType(const char *mime, int profile, int e found_codec = profile == supported_profile; - if (profile_level) { - (*env)->DeleteLocalRef(env, profile_level); - profile_level = NULL; - } + (*env)->DeleteLocalRef(env, profile_level); + profile_level = NULL; if (found_codec) { break; @@ -523,20 +623,14 @@ char *ff_AMediaCodecList_getCodecNameByType(const char *mime, int profile, int e } done_with_type: - if (profile_levels) { - (*env)->DeleteLocalRef(env, profile_levels); - profile_levels = NULL; - } + (*env)->DeleteLocalRef(env, profile_levels); + profile_levels = NULL; - if (capabilities) { - (*env)->DeleteLocalRef(env, capabilities); - capabilities = NULL; - } + (*env)->DeleteLocalRef(env, capabilities); + capabilities = NULL; - if (type) { - (*env)->DeleteLocalRef(env, type); - type = NULL; - } + (*env)->DeleteLocalRef(env, type); + type = NULL; av_freep(&supported_type); @@ -546,15 +640,11 @@ char *ff_AMediaCodecList_getCodecNameByType(const char *mime, int profile, int e } done_with_info: - if (info) { - (*env)->DeleteLocalRef(env, info); - info = NULL; - } + (*env)->DeleteLocalRef(env, info); + info = NULL; - if (types) { - (*env)->DeleteLocalRef(env, types); - types = NULL; - } + (*env)->DeleteLocalRef(env, types); + types = NULL; if (found_codec) { break; @@ -564,33 +654,13 @@ char *ff_AMediaCodecList_getCodecNameByType(const char *mime, int profile, int e } done: - if (codec_name) { - (*env)->DeleteLocalRef(env, codec_name); - } - - if (info) { - (*env)->DeleteLocalRef(env, info); - } - - if (type) { - (*env)->DeleteLocalRef(env, type); - } - - if (types) { - (*env)->DeleteLocalRef(env, types); - } - - if (capabilities) { - (*env)->DeleteLocalRef(env, capabilities); - } - - if (profile_level) { - (*env)->DeleteLocalRef(env, profile_level); - } - - if (profile_levels) { - (*env)->DeleteLocalRef(env, profile_levels); - } + (*env)->DeleteLocalRef(env, codec_name); + (*env)->DeleteLocalRef(env, info); + (*env)->DeleteLocalRef(env, type); + (*env)->DeleteLocalRef(env, types); + (*env)->DeleteLocalRef(env, capabilities); + (*env)->DeleteLocalRef(env, profile_level); + (*env)->DeleteLocalRef(env, profile_levels); av_freep(&supported_type); @@ -637,9 +707,7 @@ static FFAMediaFormat *mediaformat_jni_new(void) } fail: - if (object) { - (*env)->DeleteLocalRef(env, object); - } + (*env)->DeleteLocalRef(env, object); if (!format->object) { ff_jni_reset_jfields(env, &format->jfields, jni_amediaformat_mapping, 1, format); @@ -724,9 +792,7 @@ static char* mediaformat_jni_toString(FFAMediaFormat* ctx) ret = ff_jni_jstring_to_utf_chars(env, description, format); fail: - if (description) { - (*env)->DeleteLocalRef(env, description); - } + (*env)->DeleteLocalRef(env, description); return ret; } @@ -763,9 +829,7 @@ static int mediaformat_jni_getInt32(FFAMediaFormat* ctx, const char *name, int32 ret = 1; fail: - if (key) { - (*env)->DeleteLocalRef(env, key); - } + (*env)->DeleteLocalRef(env, key); return ret; } @@ -802,9 +866,7 @@ static int mediaformat_jni_getInt64(FFAMediaFormat* ctx, const char *name, int64 ret = 1; fail: - if (key) { - (*env)->DeleteLocalRef(env, key); - } + (*env)->DeleteLocalRef(env, key); return ret; } @@ -841,9 +903,7 @@ static int mediaformat_jni_getFloat(FFAMediaFormat* ctx, const char *name, float ret = 1; fail: - if (key) { - (*env)->DeleteLocalRef(env, key); - } + (*env)->DeleteLocalRef(env, key); return ret; } @@ -895,13 +955,8 @@ static int mediaformat_jni_getBuffer(FFAMediaFormat* ctx, const char *name, void ret = 1; fail: - if (key) { - (*env)->DeleteLocalRef(env, key); - } - - if (result) { - (*env)->DeleteLocalRef(env, result); - } + (*env)->DeleteLocalRef(env, key); + (*env)->DeleteLocalRef(env, result); return ret; } @@ -945,13 +1000,8 @@ static int mediaformat_jni_getString(FFAMediaFormat* ctx, const char *name, cons ret = 1; fail: - if (key) { - (*env)->DeleteLocalRef(env, key); - } - - if (result) { - (*env)->DeleteLocalRef(env, result); - } + (*env)->DeleteLocalRef(env, key); + (*env)->DeleteLocalRef(env, result); return ret; } @@ -977,9 +1027,7 @@ static void mediaformat_jni_setInt32(FFAMediaFormat* ctx, const char* name, int3 } fail: - if (key) { - (*env)->DeleteLocalRef(env, key); - } + (*env)->DeleteLocalRef(env, key); } static void mediaformat_jni_setInt64(FFAMediaFormat* ctx, const char* name, int64_t value) @@ -1003,9 +1051,7 @@ static void mediaformat_jni_setInt64(FFAMediaFormat* ctx, const char* name, int6 } fail: - if (key) { - (*env)->DeleteLocalRef(env, key); - } + (*env)->DeleteLocalRef(env, key); } static void mediaformat_jni_setFloat(FFAMediaFormat* ctx, const char* name, float value) @@ -1029,9 +1075,7 @@ static void mediaformat_jni_setFloat(FFAMediaFormat* ctx, const char* name, floa } fail: - if (key) { - (*env)->DeleteLocalRef(env, key); - } + (*env)->DeleteLocalRef(env, key); } static void mediaformat_jni_setString(FFAMediaFormat* ctx, const char* name, const char* value) @@ -1061,13 +1105,8 @@ static void mediaformat_jni_setString(FFAMediaFormat* ctx, const char* name, con } fail: - if (key) { - (*env)->DeleteLocalRef(env, key); - } - - if (string) { - (*env)->DeleteLocalRef(env, string); - } + (*env)->DeleteLocalRef(env, key); + (*env)->DeleteLocalRef(env, string); } static void mediaformat_jni_setBuffer(FFAMediaFormat* ctx, const char* name, void* data, size_t size) @@ -1109,13 +1148,8 @@ static void mediaformat_jni_setBuffer(FFAMediaFormat* ctx, const char* name, voi } fail: - if (key) { - (*env)->DeleteLocalRef(env, key); - } - - if (buffer) { - (*env)->DeleteLocalRef(env, buffer); - } + (*env)->DeleteLocalRef(env, key); + (*env)->DeleteLocalRef(env, buffer); } static int codec_init_static_fields(FFAMediaCodecJni *codec) @@ -1248,26 +1282,13 @@ static inline FFAMediaCodec *codec_create(int method, const char *arg) ret = 0; fail: - if (jarg) { - (*env)->DeleteLocalRef(env, jarg); - } - - if (object) { - (*env)->DeleteLocalRef(env, object); - } - - if (buffer_info) { - (*env)->DeleteLocalRef(env, buffer_info); - } + (*env)->DeleteLocalRef(env, jarg); + (*env)->DeleteLocalRef(env, object); + (*env)->DeleteLocalRef(env, buffer_info); if (ret < 0) { - if (codec->object) { - (*env)->DeleteGlobalRef(env, codec->object); - } - - if (codec->buffer_info) { - (*env)->DeleteGlobalRef(env, codec->buffer_info); - } + (*env)->DeleteGlobalRef(env, codec->object); + (*env)->DeleteGlobalRef(env, codec->buffer_info); ff_jni_reset_jfields(env, &codec->jfields, jni_amediacodec_mapping, 1, codec); av_freep(&codec); @@ -1588,13 +1609,8 @@ static uint8_t* mediacodec_jni_getInputBuffer(FFAMediaCodec* ctx, size_t idx, si ret = (*env)->GetDirectBufferAddress(env, buffer); *out_size = (*env)->GetDirectBufferCapacity(env, buffer); fail: - if (buffer) { - (*env)->DeleteLocalRef(env, buffer); - } - - if (input_buffers) { - (*env)->DeleteLocalRef(env, input_buffers); - } + (*env)->DeleteLocalRef(env, buffer); + (*env)->DeleteLocalRef(env, input_buffers); return ret; } @@ -1636,13 +1652,8 @@ static uint8_t* mediacodec_jni_getOutputBuffer(FFAMediaCodec* ctx, size_t idx, s ret = (*env)->GetDirectBufferAddress(env, buffer); *out_size = (*env)->GetDirectBufferCapacity(env, buffer); fail: - if (buffer) { - (*env)->DeleteLocalRef(env, buffer); - } - - if (output_buffers) { - (*env)->DeleteLocalRef(env, output_buffers); - } + (*env)->DeleteLocalRef(env, buffer); + (*env)->DeleteLocalRef(env, output_buffers); return ret; } @@ -1664,9 +1675,7 @@ static FFAMediaFormat* mediacodec_jni_getOutputFormat(FFAMediaCodec* ctx) ret = mediaformat_jni_newFromObject(mediaformat); fail: - if (mediaformat) { - (*env)->DeleteLocalRef(env, mediaformat); - } + (*env)->DeleteLocalRef(env, mediaformat); return ret; } @@ -2542,3 +2551,105 @@ int ff_Build_SDK_INT(AVCodecContext *avctx) return ret; } + +static struct { + enum FFAMediaFormatColorRange mf_range; + enum AVColorRange range; +} color_range_map[] = { + { COLOR_RANGE_FULL, AVCOL_RANGE_JPEG }, + { COLOR_RANGE_LIMITED, AVCOL_RANGE_MPEG }, +}; + +static struct { + enum FFAMediaFormatColorStandard mf_standard; + enum AVColorSpace space; +} color_space_map[] = { + { COLOR_STANDARD_BT709, AVCOL_SPC_BT709 }, + { COLOR_STANDARD_BT601_PAL, AVCOL_SPC_BT470BG }, + { COLOR_STANDARD_BT601_NTSC, AVCOL_SPC_SMPTE170M }, + { COLOR_STANDARD_BT2020, AVCOL_SPC_BT2020_NCL }, +}; + +static struct { + enum FFAMediaFormatColorStandard mf_standard; + enum AVColorPrimaries primaries; +} color_primaries_map[] = { + { COLOR_STANDARD_BT709, AVCOL_PRI_BT709 }, + { COLOR_STANDARD_BT601_PAL, AVCOL_PRI_BT470BG }, + { COLOR_STANDARD_BT601_NTSC, AVCOL_PRI_SMPTE170M }, + { COLOR_STANDARD_BT2020, AVCOL_PRI_BT2020 }, +}; + +static struct { + enum FFAMediaFormatColorTransfer mf_transfer; + enum AVColorTransferCharacteristic transfer; +} color_transfer_map[] = { + { COLOR_TRANSFER_LINEAR, AVCOL_TRC_LINEAR }, + { COLOR_TRANSFER_SDR_VIDEO, AVCOL_TRC_SMPTE170M }, + { COLOR_TRANSFER_ST2084, AVCOL_TRC_SMPTEST2084 }, + { COLOR_TRANSFER_HLG, AVCOL_TRC_ARIB_STD_B67 }, +}; + +enum AVColorRange ff_AMediaFormatColorRange_to_AVColorRange(int color_range) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_range_map); i++) + if (color_range_map[i].mf_range == color_range) + return color_range_map[i].range; + + return AVCOL_RANGE_UNSPECIFIED; +} + +int ff_AMediaFormatColorRange_from_AVColorRange(enum AVColorRange color_range) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_range_map); i++) + if (color_range_map[i].range == color_range) + return color_range_map[i].mf_range; + return COLOR_RANGE_UNSPECIFIED; +} + +enum AVColorSpace ff_AMediaFormatColorStandard_to_AVColorSpace(int color_standard) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_space_map); i++) + if (color_space_map[i].mf_standard == color_standard) + return color_space_map[i].space; + + return AVCOL_SPC_UNSPECIFIED; +} + +int ff_AMediaFormatColorStandard_from_AVColorSpace(enum AVColorSpace color_space) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_space_map); i++) + if (color_space_map[i].space == color_space) + return color_space_map[i].mf_standard; + + return COLOR_STANDARD_UNSPECIFIED; +} + +enum AVColorPrimaries ff_AMediaFormatColorStandard_to_AVColorPrimaries(int color_standard) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_primaries_map); i++) + if (color_primaries_map[i].mf_standard == color_standard) + return color_primaries_map[i].primaries; + + return AVCOL_PRI_UNSPECIFIED; +} + +enum AVColorTransferCharacteristic +ff_AMediaFormatColorTransfer_to_AVColorTransfer(int color_transfer) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_transfer_map); i++) + if (color_transfer_map[i].mf_transfer == color_transfer) + return color_transfer_map[i].transfer; + + return AVCOL_TRC_UNSPECIFIED; +} + +int ff_AMediaFormatColorTransfer_from_AVColorTransfer( + enum AVColorTransferCharacteristic color_transfer) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(color_transfer_map); i++) + if (color_transfer_map[i].transfer == color_transfer) + return color_transfer_map[i].mf_transfer; + + return COLOR_TRANSFER_UNSPECIFIED; +} diff --git a/libavcodec/mediacodec_wrapper.h b/libavcodec/mediacodec_wrapper.h index 1b81e6db843..11a42604979 100644 --- a/libavcodec/mediacodec_wrapper.h +++ b/libavcodec/mediacodec_wrapper.h @@ -345,4 +345,77 @@ static inline int ff_AMediaCodec_signalEndOfInputStream(FFAMediaCodec *codec) int ff_Build_SDK_INT(AVCodecContext *avctx); +enum FFAMediaFormatColorRange { + COLOR_RANGE_UNSPECIFIED = 0x0, + COLOR_RANGE_FULL = 0x1, + COLOR_RANGE_LIMITED = 0x2, +}; + +enum FFAMediaFormatColorStandard { + COLOR_STANDARD_UNSPECIFIED = 0x0, + COLOR_STANDARD_BT709 = 0x1, + COLOR_STANDARD_BT601_PAL = 0x2, + COLOR_STANDARD_BT601_NTSC = 0x4, + COLOR_STANDARD_BT2020 = 0x6, +}; + +enum FFAMediaFormatColorTransfer { + COLOR_TRANSFER_UNSPECIFIED = 0x0, + COLOR_TRANSFER_LINEAR = 0x1, + COLOR_TRANSFER_SDR_VIDEO = 0x3, + COLOR_TRANSFER_ST2084 = 0x6, + COLOR_TRANSFER_HLG = 0x7, +}; + +/** + * Map MediaFormat color range to AVColorRange. + * + * return AVCOL_RANGE_UNSPECIFIED when failed. + */ +enum AVColorRange ff_AMediaFormatColorRange_to_AVColorRange(int color_range); + +/** + * Map AVColorRange to MediaFormat color range. + * + * return COLOR_RANGE_UNSPECIFIED when failed. + */ +int ff_AMediaFormatColorRange_from_AVColorRange(enum AVColorRange color_range); + +/** + * Map MediaFormat color standard to AVColorSpace. + * + * return AVCOL_SPC_UNSPECIFIED when failed. + */ +enum AVColorSpace ff_AMediaFormatColorStandard_to_AVColorSpace(int color_standard); + +/** + * Map AVColorSpace to MediaFormat color standard. + * + * return COLOR_STANDARD_UNSPECIFIED when failed. + */ +int ff_AMediaFormatColorStandard_from_AVColorSpace(enum AVColorSpace color_space); + +/** + * Map MediaFormat color standard to AVColorPrimaries. + * + * return AVCOL_PRI_UNSPECIFIED when failed. + */ +enum AVColorPrimaries ff_AMediaFormatColorStandard_to_AVColorPrimaries(int color_standard); + +/** + * Map MediaFormat color transfer to AVColorTransferCharacteristic. + * + * return AVCOL_TRC_UNSPECIFIED when failed. + */ +enum AVColorTransferCharacteristic +ff_AMediaFormatColorTransfer_to_AVColorTransfer(int color_transfer); + +/** + * Map AVColorTransferCharacteristic to MediaFormat color transfer. + * + * return COLOR_TRANSFER_UNSPECIFIED when failed. + */ +int ff_AMediaFormatColorTransfer_from_AVColorTransfer( + enum AVColorTransferCharacteristic color_transfer); + #endif /* AVCODEC_MEDIACODEC_WRAPPER_H */ diff --git a/libavcodec/mediacodecdec.c b/libavcodec/mediacodecdec.c index 21464900d18..b8587289a23 100644 --- a/libavcodec/mediacodecdec.c +++ b/libavcodec/mediacodecdec.c @@ -146,14 +146,14 @@ static int h264_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format) for (i = 0; i < MAX_PPS_COUNT; i++) { if (ps.pps_list[i]) { - pps = (const PPS*)ps.pps_list[i]->data; + pps = ps.pps_list[i]; break; } } if (pps) { if (ps.sps_list[pps->sps_id]) { - sps = (const SPS*)ps.sps_list[pps->sps_id]->data; + sps = ps.sps_list[pps->sps_id]; } } @@ -223,21 +223,21 @@ static int hevc_set_extradata(AVCodecContext *avctx, FFAMediaFormat *format) for (i = 0; i < HEVC_MAX_VPS_COUNT; i++) { if (ps.vps_list[i]) { - vps = (const HEVCVPS*)ps.vps_list[i]->data; + vps = ps.vps_list[i]; break; } } for (i = 0; i < HEVC_MAX_PPS_COUNT; i++) { if (ps.pps_list[i]) { - pps = (const HEVCPPS*)ps.pps_list[i]->data; + pps = ps.pps_list[i]; break; } } if (pps) { if (ps.sps_list[pps->sps_id]) { - sps = (const HEVCSPS*)ps.sps_list[pps->sps_id]->data; + sps = ps.sps_list[pps->sps_id]; } } @@ -577,8 +577,7 @@ const FFCodec ff_ ## short_name ## _mediacodec_decoder = { .flush = mediacodec_decode_flush, \ .close = mediacodec_decode_close, \ .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, \ - .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | \ - FF_CODEC_CAP_SETS_PKT_DTS, \ + .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, \ .bsfs = bsf, \ .hw_configs = mediacodec_hw_configs, \ .p.wrapper_name = "mediacodec", \ diff --git a/libavcodec/mediacodecdec_common.c b/libavcodec/mediacodecdec_common.c index 03bee119188..d6f91e6e89e 100644 --- a/libavcodec/mediacodecdec_common.c +++ b/libavcodec/mediacodecdec_common.c @@ -85,85 +85,6 @@ #define OUTPUT_DEQUEUE_TIMEOUT_US 8000 #define OUTPUT_DEQUEUE_BLOCK_TIMEOUT_US 1000000 -enum { - COLOR_RANGE_FULL = 0x1, - COLOR_RANGE_LIMITED = 0x2, -}; - -static enum AVColorRange mcdec_get_color_range(int color_range) -{ - switch (color_range) { - case COLOR_RANGE_FULL: - return AVCOL_RANGE_JPEG; - case COLOR_RANGE_LIMITED: - return AVCOL_RANGE_MPEG; - default: - return AVCOL_RANGE_UNSPECIFIED; - } -} - -enum { - COLOR_STANDARD_BT709 = 0x1, - COLOR_STANDARD_BT601_PAL = 0x2, - COLOR_STANDARD_BT601_NTSC = 0x4, - COLOR_STANDARD_BT2020 = 0x6, -}; - -static enum AVColorSpace mcdec_get_color_space(int color_standard) -{ - switch (color_standard) { - case COLOR_STANDARD_BT709: - return AVCOL_SPC_BT709; - case COLOR_STANDARD_BT601_PAL: - return AVCOL_SPC_BT470BG; - case COLOR_STANDARD_BT601_NTSC: - return AVCOL_SPC_SMPTE170M; - case COLOR_STANDARD_BT2020: - return AVCOL_SPC_BT2020_NCL; - default: - return AVCOL_SPC_UNSPECIFIED; - } -} - -static enum AVColorPrimaries mcdec_get_color_pri(int color_standard) -{ - switch (color_standard) { - case COLOR_STANDARD_BT709: - return AVCOL_PRI_BT709; - case COLOR_STANDARD_BT601_PAL: - return AVCOL_PRI_BT470BG; - case COLOR_STANDARD_BT601_NTSC: - return AVCOL_PRI_SMPTE170M; - case COLOR_STANDARD_BT2020: - return AVCOL_PRI_BT2020; - default: - return AVCOL_PRI_UNSPECIFIED; - } -} - -enum { - COLOR_TRANSFER_LINEAR = 0x1, - COLOR_TRANSFER_SDR_VIDEO = 0x3, - COLOR_TRANSFER_ST2084 = 0x6, - COLOR_TRANSFER_HLG = 0x7, -}; - -static enum AVColorTransferCharacteristic mcdec_get_color_trc(int color_transfer) -{ - switch (color_transfer) { - case COLOR_TRANSFER_LINEAR: - return AVCOL_TRC_LINEAR; - case COLOR_TRANSFER_SDR_VIDEO: - return AVCOL_TRC_SMPTE170M; - case COLOR_TRANSFER_ST2084: - return AVCOL_TRC_SMPTEST2084; - case COLOR_TRANSFER_HLG: - return AVCOL_TRC_ARIB_STD_B67; - default: - return AVCOL_TRC_UNSPECIFIED; - } -} - enum { COLOR_FormatYUV420Planar = 0x13, COLOR_FormatYUV420SemiPlanar = 0x15, @@ -517,17 +438,17 @@ static int mediacodec_dec_parse_format(AVCodecContext *avctx, MediaCodecDecConte AMEDIAFORMAT_GET_INT32(color_range, "color-range", 0); if (color_range) - avctx->color_range = mcdec_get_color_range(color_range); + avctx->color_range = ff_AMediaFormatColorRange_to_AVColorRange(color_range); AMEDIAFORMAT_GET_INT32(color_standard, "color-standard", 0); if (color_standard) { - avctx->colorspace = mcdec_get_color_space(color_standard); - avctx->color_primaries = mcdec_get_color_pri(color_standard); + avctx->colorspace = ff_AMediaFormatColorStandard_to_AVColorSpace(color_standard); + avctx->color_primaries = ff_AMediaFormatColorStandard_to_AVColorPrimaries(color_standard); } AMEDIAFORMAT_GET_INT32(color_transfer, "color-transfer", 0); if (color_transfer) - avctx->color_trc = mcdec_get_color_trc(color_transfer); + avctx->color_trc = ff_AMediaFormatColorTransfer_to_AVColorTransfer(color_transfer); av_log(avctx, AV_LOG_INFO, "Output crop parameters top=%d bottom=%d left=%d right=%d, " @@ -883,6 +804,8 @@ int ff_mediacodec_dec_receive(AVCodecContext *avctx, MediaCodecDecContext *s, return AVERROR_EXTERNAL; } + if (s->draining && s->eos) + return AVERROR_EOF; return AVERROR(EAGAIN); } diff --git a/libavcodec/mediacodecenc.c b/libavcodec/mediacodecenc.c index a92a8dc5a9c..d3bf27cb7f6 100644 --- a/libavcodec/mediacodecenc.c +++ b/libavcodec/mediacodecenc.c @@ -36,6 +36,7 @@ #include "mediacodec.h" #include "mediacodec_wrapper.h" #include "mediacodecdec_common.h" +#include "profiles.h" #define INPUT_DEQUEUE_TIMEOUT_US 8000 #define OUTPUT_DEQUEUE_TIMEOUT_US 8000 @@ -164,6 +165,18 @@ static av_cold int mediacodec_init(AVCodecContext *avctx) case AV_CODEC_ID_HEVC: codec_mime = "video/hevc"; break; + case AV_CODEC_ID_VP8: + codec_mime = "video/x-vnd.on2.vp8"; + break; + case AV_CODEC_ID_VP9: + codec_mime = "video/x-vnd.on2.vp9"; + break; + case AV_CODEC_ID_MPEG4: + codec_mime = "video/mp4v-es"; + break; + case AV_CODEC_ID_AV1: + codec_mime = "video/av01"; + break; default: av_assert0(0); } @@ -187,9 +200,19 @@ static av_cold int mediacodec_init(AVCodecContext *avctx) ff_AMediaFormat_setString(format, "mime", codec_mime); // Workaround the alignment requirement of mediacodec. We can't do it // silently for AV_PIX_FMT_MEDIACODEC. - if (avctx->pix_fmt != AV_PIX_FMT_MEDIACODEC) { + if (avctx->pix_fmt != AV_PIX_FMT_MEDIACODEC && + (avctx->codec_id == AV_CODEC_ID_H264 || + avctx->codec_id == AV_CODEC_ID_HEVC)) { s->width = FFALIGN(avctx->width, 16); s->height = FFALIGN(avctx->height, 16); + // If avctx video size is aligned to 16 already, we don't need to do + // anything. If align is needed for HEVC, we should use the maximum CTU + // size. + if (avctx->codec_id == AV_CODEC_ID_HEVC && + (s->width != avctx->width || s->height != avctx->height)) { + s->width = FFALIGN(avctx->width, 64); + s->height = FFALIGN(avctx->height, 64); + } } else { s->width = avctx->width; s->height = avctx->height; @@ -243,10 +266,23 @@ static av_cold int mediacodec_init(AVCodecContext *avctx) } } + ret = ff_AMediaFormatColorRange_from_AVColorRange(avctx->color_range); + if (ret != COLOR_RANGE_UNSPECIFIED) + ff_AMediaFormat_setInt32(format, "color-range", ret); + ret = ff_AMediaFormatColorStandard_from_AVColorSpace(avctx->colorspace); + if (ret != COLOR_STANDARD_UNSPECIFIED) + ff_AMediaFormat_setInt32(format, "color-standard", ret); + ret = ff_AMediaFormatColorTransfer_from_AVColorTransfer(avctx->color_trc); + if (ret != COLOR_TRANSFER_UNSPECIFIED) + ff_AMediaFormat_setInt32(format, "color-transfer", ret); + if (avctx->bit_rate) ff_AMediaFormat_setInt32(format, "bitrate", avctx->bit_rate); - if (s->bitrate_mode >= 0) + if (s->bitrate_mode >= 0) { ff_AMediaFormat_setInt32(format, "bitrate-mode", s->bitrate_mode); + if (s->bitrate_mode == BITRATE_MODE_CQ && avctx->global_quality > 0) + ff_AMediaFormat_setInt32(format, "quality", avctx->global_quality); + } // frame-rate and i-frame-interval are required to configure codec if (avctx->framerate.num >= avctx->framerate.den && avctx->framerate.den > 0) { s->fps = avctx->framerate.num / avctx->framerate.den; @@ -293,6 +329,9 @@ static av_cold int mediacodec_init(AVCodecContext *avctx) ret = ff_AMediaCodec_configure(s->codec, format, s->window, NULL, ret); if (ret) { av_log(avctx, AV_LOG_ERROR, "MediaCodec configure failed, %s\n", av_err2str(ret)); + if (avctx->pix_fmt == AV_PIX_FMT_YUV420P) + av_log(avctx, AV_LOG_ERROR, "Please try -pix_fmt nv12, some devices don't " + "support yuv420p as encoder input format.\n"); goto bailout; } @@ -307,6 +346,10 @@ static av_cold int mediacodec_init(AVCodecContext *avctx) goto bailout; mediacodec_output_format(avctx); + if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) + av_log(avctx, AV_LOG_WARNING, + "Mediacodec encoder doesn't support AV_CODEC_FLAG_GLOBAL_HEADER. " + "Use extract_extradata bsf when necessary.\n"); s->frame = av_frame_alloc(); if (!s->frame) @@ -401,9 +444,6 @@ static void copy_frame_to_buffer(AVCodecContext *avctx, const AVFrame *frame, ui MediaCodecEncContext *s = avctx->priv_data; uint8_t *dst_data[4] = {}; int dst_linesize[4] = {}; - const uint8_t *src_data[4] = { - frame->data[0], frame->data[1], frame->data[2], frame->data[3] - }; if (avctx->pix_fmt == AV_PIX_FMT_YUV420P) { dst_data[0] = dst; @@ -422,8 +462,8 @@ static void copy_frame_to_buffer(AVCodecContext *avctx, const AVFrame *frame, ui av_assert0(0); } - av_image_copy(dst_data, dst_linesize, src_data, frame->linesize, - avctx->pix_fmt, avctx->width, avctx->height); + av_image_copy2(dst_data, dst_linesize, frame->data, frame->linesize, + avctx->pix_fmt, avctx->width, avctx->height); } static int mediacodec_send(AVCodecContext *avctx, @@ -503,7 +543,7 @@ static int mediacodec_encode(AVCodecContext *avctx, AVPacket *pkt) return 0; } - if (ret != AVERROR(EAGAIN)) + if (ret < 0 && ret != AVERROR(EAGAIN)) return ret; if (!s->frame->buf[0]) { @@ -563,15 +603,15 @@ static const AVCodecHWConfigInternal *const mediacodec_hw_configs[] = { { "codec_name", "Select codec by name", \ OFFSET(name), AV_OPT_TYPE_STRING, {0}, 0, 0, VE }, \ { "bitrate_mode", "Bitrate control method", \ - OFFSET(bitrate_mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE, "bitrate_mode" }, \ - { "cq", "Constant quality mode", \ - 0, AV_OPT_TYPE_CONST, {.i64 = BITRATE_MODE_CQ}, 0, 0, VE, "bitrate_mode" }, \ - { "vbr", "Variable bitrate mode", \ - 0, AV_OPT_TYPE_CONST, {.i64 = BITRATE_MODE_VBR}, 0, 0, VE, "bitrate_mode" }, \ - { "cbr", "Constant bitrate mode", \ - 0, AV_OPT_TYPE_CONST, {.i64 = BITRATE_MODE_CBR}, 0, 0, VE, "bitrate_mode" }, \ - { "cbr_fd", "Constant bitrate mode with frame drops", \ - 0, AV_OPT_TYPE_CONST, {.i64 = BITRATE_MODE_CBR_FD}, 0, 0, VE, "bitrate_mode" }, \ + OFFSET(bitrate_mode), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE, .unit = "bitrate_mode" }, \ + { "cq", "Constant quality mode", \ + 0, AV_OPT_TYPE_CONST, {.i64 = BITRATE_MODE_CQ}, 0, 0, VE, .unit = "bitrate_mode" }, \ + { "vbr", "Variable bitrate mode", \ + 0, AV_OPT_TYPE_CONST, {.i64 = BITRATE_MODE_VBR}, 0, 0, VE, .unit = "bitrate_mode" }, \ + { "cbr", "Constant bitrate mode", \ + 0, AV_OPT_TYPE_CONST, {.i64 = BITRATE_MODE_CBR}, 0, 0, VE, .unit = "bitrate_mode" }, \ + { "cbr_fd", "Constant bitrate mode with frame drops", \ + 0, AV_OPT_TYPE_CONST, {.i64 = BITRATE_MODE_CBR_FD}, 0, 0, VE, .unit = "bitrate_mode" }, \ { "pts_as_dts", "Use PTS as DTS. It is enabled automatically if avctx max_b_frames <= 0, " \ "since most of Android devices don't output B frames by default.", \ OFFSET(pts_as_dts), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, VE }, \ @@ -632,28 +672,38 @@ enum MediaCodecAvcLevel { static const AVOption h264_options[] = { COMMON_OPTION + + FF_AVCTX_PROFILE_OPTION("baseline", NULL, VIDEO, AV_PROFILE_H264_BASELINE) + FF_AVCTX_PROFILE_OPTION("constrained_baseline", NULL, VIDEO, AV_PROFILE_H264_CONSTRAINED_BASELINE) + FF_AVCTX_PROFILE_OPTION("main", NULL, VIDEO, AV_PROFILE_H264_MAIN) + FF_AVCTX_PROFILE_OPTION("extended", NULL, VIDEO, AV_PROFILE_H264_EXTENDED) + FF_AVCTX_PROFILE_OPTION("high", NULL, VIDEO, AV_PROFILE_H264_HIGH) + FF_AVCTX_PROFILE_OPTION("high10", NULL, VIDEO, AV_PROFILE_H264_HIGH_10) + FF_AVCTX_PROFILE_OPTION("high422", NULL, VIDEO, AV_PROFILE_H264_HIGH_422) + FF_AVCTX_PROFILE_OPTION("high444", NULL, VIDEO, AV_PROFILE_H264_HIGH_444) + { "level", "Specify level", - OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, "level" }, - { "1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel1 }, 0, 0, VE, "level" }, - { "1b", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel1b }, 0, 0, VE, "level" }, - { "1.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel11 }, 0, 0, VE, "level" }, - { "1.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel12 }, 0, 0, VE, "level" }, - { "1.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel13 }, 0, 0, VE, "level" }, - { "2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel2 }, 0, 0, VE, "level" }, - { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel21 }, 0, 0, VE, "level" }, - { "2.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel22 }, 0, 0, VE, "level" }, - { "3", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel3 }, 0, 0, VE, "level" }, - { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel31 }, 0, 0, VE, "level" }, - { "3.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel32 }, 0, 0, VE, "level" }, - { "4", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel4 }, 0, 0, VE, "level" }, - { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel41 }, 0, 0, VE, "level" }, - { "4.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel42 }, 0, 0, VE, "level" }, - { "5", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel5 }, 0, 0, VE, "level" }, - { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel51 }, 0, 0, VE, "level" }, - { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel52 }, 0, 0, VE, "level" }, - { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel6 }, 0, 0, VE, "level" }, - { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel61 }, 0, 0, VE, "level" }, - { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel62 }, 0, 0, VE, "level" }, + OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, .unit = "level" }, + { "1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel1 }, 0, 0, VE, .unit = "level" }, + { "1b", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel1b }, 0, 0, VE, .unit = "level" }, + { "1.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel11 }, 0, 0, VE, .unit = "level" }, + { "1.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel12 }, 0, 0, VE, .unit = "level" }, + { "1.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel13 }, 0, 0, VE, .unit = "level" }, + { "2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel2 }, 0, 0, VE, .unit = "level" }, + { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel21 }, 0, 0, VE, .unit = "level" }, + { "2.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel22 }, 0, 0, VE, .unit = "level" }, + { "3", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel3 }, 0, 0, VE, .unit = "level" }, + { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel31 }, 0, 0, VE, .unit = "level" }, + { "3.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel32 }, 0, 0, VE, .unit = "level" }, + { "4", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel4 }, 0, 0, VE, .unit = "level" }, + { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel41 }, 0, 0, VE, .unit = "level" }, + { "4.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel42 }, 0, 0, VE, .unit = "level" }, + { "5", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel5 }, 0, 0, VE, .unit = "level" }, + { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel51 }, 0, 0, VE, .unit = "level" }, + { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel52 }, 0, 0, VE, .unit = "level" }, + { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel6 }, 0, 0, VE, .unit = "level" }, + { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel61 }, 0, 0, VE, .unit = "level" }, + { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = AVCLevel62 }, 0, 0, VE, .unit = "level" }, { NULL, } }; @@ -694,63 +744,299 @@ enum MediaCodecHevcLevel { static const AVOption hevc_options[] = { COMMON_OPTION + + FF_AVCTX_PROFILE_OPTION("main", NULL, VIDEO, AV_PROFILE_HEVC_MAIN) + FF_AVCTX_PROFILE_OPTION("main10", NULL, VIDEO, AV_PROFILE_HEVC_MAIN_10) + { "level", "Specify tier and level", - OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, "level" }, + OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, .unit = "level" }, { "m1", "Main tier level 1", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel1 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel1 }, 0, 0, VE, .unit = "level" }, { "h1", "High tier level 1", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel1 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel1 }, 0, 0, VE, .unit = "level" }, { "m2", "Main tier level 2", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel2 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel2 }, 0, 0, VE, .unit = "level" }, { "h2", "High tier level 2", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel2 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel2 }, 0, 0, VE, .unit = "level" }, { "m2.1", "Main tier level 2.1", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel21 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel21 }, 0, 0, VE, .unit = "level" }, { "h2.1", "High tier level 2.1", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel21 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel21 }, 0, 0, VE, .unit = "level" }, { "m3", "Main tier level 3", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel3 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel3 }, 0, 0, VE, .unit = "level" }, { "h3", "High tier level 3", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel3 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel3 }, 0, 0, VE, .unit = "level" }, { "m3.1", "Main tier level 3.1", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel31 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel31 }, 0, 0, VE, .unit = "level" }, { "h3.1", "High tier level 3.1", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel31 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel31 }, 0, 0, VE, .unit = "level" }, { "m4", "Main tier level 4", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel4 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel4 }, 0, 0, VE, .unit = "level" }, { "h4", "High tier level 4", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel4 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel4 }, 0, 0, VE, .unit = "level" }, { "m4.1", "Main tier level 4.1", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel41 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel41 }, 0, 0, VE, .unit = "level" }, { "h4.1", "High tier level 4.1", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel41 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel41 }, 0, 0, VE, .unit = "level" }, { "m5", "Main tier level 5", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel5 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel5 }, 0, 0, VE, .unit = "level" }, { "h5", "High tier level 5", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel5 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel5 }, 0, 0, VE, .unit = "level" }, { "m5.1", "Main tier level 5.1", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel51 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel51 }, 0, 0, VE, .unit = "level" }, { "h5.1", "High tier level 5.1", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel51 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel51 }, 0, 0, VE, .unit = "level" }, { "m5.2", "Main tier level 5.2", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel52 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel52 }, 0, 0, VE, .unit = "level" }, { "h5.2", "High tier level 5.2", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel52 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel52 }, 0, 0, VE, .unit = "level" }, { "m6", "Main tier level 6", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel6 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel6 }, 0, 0, VE, .unit = "level" }, { "h6", "High tier level 6", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel6 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel6 }, 0, 0, VE, .unit = "level" }, { "m6.1", "Main tier level 6.1", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel61 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel61 }, 0, 0, VE, .unit = "level" }, { "h6.1", "High tier level 6.1", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel61 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel61 }, 0, 0, VE, .unit = "level" }, { "m6.2", "Main tier level 6.2", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel62 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCMainTierLevel62 }, 0, 0, VE, .unit = "level" }, { "h6.2", "High tier level 6.2", - 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel62 }, 0, 0, VE, "level" }, + 0, AV_OPT_TYPE_CONST, { .i64 = HEVCHighTierLevel62 }, 0, 0, VE, .unit = "level" }, { NULL, } }; DECLARE_MEDIACODEC_ENCODER(hevc, "H.265", AV_CODEC_ID_HEVC) #endif // CONFIG_HEVC_MEDIACODEC_ENCODER + +#if CONFIG_VP8_MEDIACODEC_ENCODER + +enum MediaCodecVP8Level { + VP8Level_Version0 = 0x01, + VP8Level_Version1 = 0x02, + VP8Level_Version2 = 0x04, + VP8Level_Version3 = 0x08, +}; + +static const AVOption vp8_options[] = { + COMMON_OPTION + { "level", "Specify tier and level", + OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, .unit = "level" }, + { "V0", "Level Version 0", + 0, AV_OPT_TYPE_CONST, { .i64 = VP8Level_Version0 }, 0, 0, VE, .unit = "level" }, + { "V1", "Level Version 1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP8Level_Version1 }, 0, 0, VE, .unit = "level" }, + { "V2", "Level Version 2", + 0, AV_OPT_TYPE_CONST, { .i64 = VP8Level_Version2 }, 0, 0, VE, .unit = "level" }, + { "V3", "Level Version 3", + 0, AV_OPT_TYPE_CONST, { .i64 = VP8Level_Version3 }, 0, 0, VE, .unit = "level" }, + { NULL, } +}; + +DECLARE_MEDIACODEC_ENCODER(vp8, "VP8", AV_CODEC_ID_VP8) + +#endif // CONFIG_VP8_MEDIACODEC_ENCODER + +#if CONFIG_VP9_MEDIACODEC_ENCODER + +enum MediaCodecVP9Level { + VP9Level1 = 0x1, + VP9Level11 = 0x2, + VP9Level2 = 0x4, + VP9Level21 = 0x8, + VP9Level3 = 0x10, + VP9Level31 = 0x20, + VP9Level4 = 0x40, + VP9Level41 = 0x80, + VP9Level5 = 0x100, + VP9Level51 = 0x200, + VP9Level52 = 0x400, + VP9Level6 = 0x800, + VP9Level61 = 0x1000, + VP9Level62 = 0x2000, +}; + +static const AVOption vp9_options[] = { + COMMON_OPTION + + FF_AVCTX_PROFILE_OPTION("profile0", NULL, VIDEO, AV_PROFILE_VP9_0) + FF_AVCTX_PROFILE_OPTION("profile1", NULL, VIDEO, AV_PROFILE_VP9_1) + FF_AVCTX_PROFILE_OPTION("profile2", NULL, VIDEO, AV_PROFILE_VP9_2) + FF_AVCTX_PROFILE_OPTION("profile3", NULL, VIDEO, AV_PROFILE_VP9_3) + + { "level", "Specify tier and level", + OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, .unit = "level" }, + { "1", "Level 1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level1 }, 0, 0, VE, .unit = "level" }, + { "1.1", "Level 1.1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level11 }, 0, 0, VE, .unit = "level" }, + { "2", "Level 2", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level2 }, 0, 0, VE, .unit = "level" }, + { "2.1", "Level 2.1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level21 }, 0, 0, VE, .unit = "level" }, + { "3", "Level 3", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level3 }, 0, 0, VE, .unit = "level" }, + { "3.1", "Level 3.1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level31 }, 0, 0, VE, .unit = "level" }, + { "4", "Level 4", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level4 }, 0, 0, VE, .unit = "level" }, + { "4.1", "Level 4.1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level41 }, 0, 0, VE, .unit = "level" }, + { "5", "Level 5", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level5 }, 0, 0, VE, .unit = "level" }, + { "5.1", "Level 5.1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level51 }, 0, 0, VE, .unit = "level" }, + { "5.2", "Level 5.2", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level52 }, 0, 0, VE, .unit = "level" }, + { "6", "Level 6", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level6 }, 0, 0, VE, .unit = "level" }, + { "6.1", "Level 4.1", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level61 }, 0, 0, VE, .unit = "level" }, + { "6.2", "Level 6.2", + 0, AV_OPT_TYPE_CONST, { .i64 = VP9Level62 }, 0, 0, VE, .unit = "level" }, + { NULL, } +}; + +DECLARE_MEDIACODEC_ENCODER(vp9, "VP9", AV_CODEC_ID_VP9) + +#endif // CONFIG_VP9_MEDIACODEC_ENCODER + +#if CONFIG_MPEG4_MEDIACODEC_ENCODER + +enum MediaCodecMpeg4Level { + MPEG4Level0 = 0x01, + MPEG4Level0b = 0x02, + MPEG4Level1 = 0x04, + MPEG4Level2 = 0x08, + MPEG4Level3 = 0x10, + MPEG4Level3b = 0x18, + MPEG4Level4 = 0x20, + MPEG4Level4a = 0x40, + MPEG4Level5 = 0x80, + MPEG4Level6 = 0x100, +}; + +static const AVOption mpeg4_options[] = { + COMMON_OPTION + + FF_MPEG4_PROFILE_OPTS + + { "level", "Specify tier and level", + OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, .unit = "level" }, + { "0", "Level 0", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level0 }, 0, 0, VE, .unit = "level" }, + { "0b", "Level 0b", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level0b }, 0, 0, VE, .unit = "level" }, + { "1", "Level 1", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level1 }, 0, 0, VE, .unit = "level" }, + { "2", "Level 2", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level2 }, 0, 0, VE, .unit = "level" }, + { "3", "Level 3", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level3 }, 0, 0, VE, .unit = "level" }, + { "3b", "Level 3b", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level3b }, 0, 0, VE, .unit = "level" }, + { "4", "Level 4", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level4 }, 0, 0, VE, .unit = "level" }, + { "4a", "Level 4a", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level4a }, 0, 0, VE, .unit = "level" }, + { "5", "Level 5", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level5 }, 0, 0, VE, .unit = "level" }, + { "6", "Level 6", + 0, AV_OPT_TYPE_CONST, { .i64 = MPEG4Level6 }, 0, 0, VE, .unit = "level" }, + { NULL, } +}; + +DECLARE_MEDIACODEC_ENCODER(mpeg4, "MPEG-4", AV_CODEC_ID_MPEG4) + +#endif // CONFIG_MPEG4_MEDIACODEC_ENCODER + +#if CONFIG_AV1_MEDIACODEC_ENCODER + +enum MediaCodecAV1Level { + AV1Level2 = 0x1, + AV1Level21 = 0x2, + AV1Level22 = 0x4, + AV1Level23 = 0x8, + AV1Level3 = 0x10, + AV1Level31 = 0x20, + AV1Level32 = 0x40, + AV1Level33 = 0x80, + AV1Level4 = 0x100, + AV1Level41 = 0x200, + AV1Level42 = 0x400, + AV1Level43 = 0x800, + AV1Level5 = 0x1000, + AV1Level51 = 0x2000, + AV1Level52 = 0x4000, + AV1Level53 = 0x8000, + AV1Level6 = 0x10000, + AV1Level61 = 0x20000, + AV1Level62 = 0x40000, + AV1Level63 = 0x80000, + AV1Level7 = 0x100000, + AV1Level71 = 0x200000, + AV1Level72 = 0x400000, + AV1Level73 = 0x800000, +}; + +static const AVOption av1_options[] = { + COMMON_OPTION + + FF_AV1_PROFILE_OPTS + + { "level", "Specify tier and level", + OFFSET(level), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, VE, .unit = "level" }, + { "2", "Level 2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level2 }, 0, 0, VE, .unit = "level" }, + { "2.1", "Level 2.1", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level21 }, 0, 0, VE, .unit = "level" }, + { "2.2", "Level 2.2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level22 }, 0, 0, VE, .unit = "level" }, + { "2.3", "Level 2.3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level23 }, 0, 0, VE, .unit = "level" }, + { "3", "Level 3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level3 }, 0, 0, VE, .unit = "level" }, + { "3.1", "Level 3.1", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level31 }, 0, 0, VE, .unit = "level" }, + { "3.2", "Level 3.2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level32 }, 0, 0, VE, .unit = "level" }, + { "3.3", "Level 3.3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level33 }, 0, 0, VE, .unit = "level" }, + { "4", "Level 4", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level4 }, 0, 0, VE, .unit = "level" }, + { "4.1", "Level 4.1", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level41 }, 0, 0, VE, .unit = "level" }, + { "4.2", "Level 4.2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level42 }, 0, 0, VE, .unit = "level" }, + { "4.3", "Level 4.3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level43 }, 0, 0, VE, .unit = "level" }, + { "5", "Level 5", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level5 }, 0, 0, VE, .unit = "level" }, + { "5.1", "Level 5.1", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level51 }, 0, 0, VE, .unit = "level" }, + { "5.2", "Level 5.2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level52 }, 0, 0, VE, .unit = "level" }, + { "5.3", "Level 5.3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level53 }, 0, 0, VE, .unit = "level" }, + { "6", "Level 6", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level6 }, 0, 0, VE, .unit = "level" }, + { "6.1", "Level 6.1", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level61 }, 0, 0, VE, .unit = "level" }, + { "6.2", "Level 6.2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level62 }, 0, 0, VE, .unit = "level" }, + { "6.3", "Level 6.3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level63 }, 0, 0, VE, .unit = "level" }, + { "7", "Level 7", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level7 }, 0, 0, VE, .unit = "level" }, + { "7.1", "Level 7.1", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level71 }, 0, 0, VE, .unit = "level" }, + { "7.2", "Level 7.2", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level72 }, 0, 0, VE, .unit = "level" }, + { "7.3", "Level 7.3", + 0, AV_OPT_TYPE_CONST, { .i64 = AV1Level73 }, 0, 0, VE, .unit = "level" }, + { NULL, } +}; + +DECLARE_MEDIACODEC_ENCODER(av1, "AV1", AV_CODEC_ID_AV1) + +#endif // CONFIG_AV1_MEDIACODEC_ENCODER diff --git a/libavcodec/mfenc.c b/libavcodec/mfenc.c index f3415df10b0..a674a35aa44 100644 --- a/libavcodec/mfenc.c +++ b/libavcodec/mfenc.c @@ -248,7 +248,7 @@ static int mf_sample_to_avpacket(AVCodecContext *avctx, IMFSample *sample, AVPac if ((ret = ff_get_encode_buffer(avctx, avpkt, len, 0)) < 0) return ret; - IMFSample_ConvertToContiguousBuffer(sample, &buffer); + hr = IMFSample_ConvertToContiguousBuffer(sample, &buffer); if (FAILED(hr)) return AVERROR_EXTERNAL; @@ -659,7 +659,11 @@ static int mf_encv_output_adjust(AVCodecContext *avctx, IMFMediaType *type) framerate = avctx->framerate; } else { framerate = av_inv_q(avctx->time_base); +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS framerate.den *= avctx->ticks_per_frame; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } ff_MFSetAttributeRatio((IMFAttributes *)type, &MF_MT_FRAME_RATE, framerate.num, framerate.den); @@ -668,10 +672,10 @@ static int mf_encv_output_adjust(AVCodecContext *avctx, IMFMediaType *type) if (avctx->codec_id == AV_CODEC_ID_H264) { UINT32 profile = ff_eAVEncH264VProfile_Base; switch (avctx->profile) { - case FF_PROFILE_H264_MAIN: + case AV_PROFILE_H264_MAIN: profile = ff_eAVEncH264VProfile_Main; break; - case FF_PROFILE_H264_HIGH: + case AV_PROFILE_H264_HIGH: profile = ff_eAVEncH264VProfile_High; break; } @@ -1254,25 +1258,25 @@ MF_ENCODER(AUDIO, mp3, MP3, NULL, AFMTS, ACAPS); #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption venc_opts[] = { - {"rate_control", "Select rate control mode", OFFSET(opt_enc_rc), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE, "rate_control"}, - { "default", "Default mode", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, VE, "rate_control"}, - { "cbr", "CBR mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_CBR}, 0, 0, VE, "rate_control"}, - { "pc_vbr", "Peak constrained VBR mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_PeakConstrainedVBR}, 0, 0, VE, "rate_control"}, - { "u_vbr", "Unconstrained VBR mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_UnconstrainedVBR}, 0, 0, VE, "rate_control"}, - { "quality", "Quality mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_Quality}, 0, 0, VE, "rate_control" }, + {"rate_control", "Select rate control mode", OFFSET(opt_enc_rc), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE, .unit = "rate_control"}, + { "default", "Default mode", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, VE, .unit = "rate_control"}, + { "cbr", "CBR mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_CBR}, 0, 0, VE, .unit = "rate_control"}, + { "pc_vbr", "Peak constrained VBR mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_PeakConstrainedVBR}, 0, 0, VE, .unit = "rate_control"}, + { "u_vbr", "Unconstrained VBR mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_UnconstrainedVBR}, 0, 0, VE, .unit = "rate_control"}, + { "quality", "Quality mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_Quality}, 0, 0, VE, .unit = "rate_control" }, // The following rate_control modes require Windows 8. - { "ld_vbr", "Low delay VBR mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_LowDelayVBR}, 0, 0, VE, "rate_control"}, - { "g_vbr", "Global VBR mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_GlobalVBR}, 0, 0, VE, "rate_control" }, - { "gld_vbr", "Global low delay VBR mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_GlobalLowDelayVBR}, 0, 0, VE, "rate_control"}, - - {"scenario", "Select usage scenario", OFFSET(opt_enc_scenario), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE, "scenario"}, - { "default", "Default scenario", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, VE, "scenario"}, - { "display_remoting", "Display remoting", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVScenarioInfo_DisplayRemoting}, 0, 0, VE, "scenario"}, - { "video_conference", "Video conference", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVScenarioInfo_VideoConference}, 0, 0, VE, "scenario"}, - { "archive", "Archive", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVScenarioInfo_Archive}, 0, 0, VE, "scenario"}, - { "live_streaming", "Live streaming", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVScenarioInfo_LiveStreaming}, 0, 0, VE, "scenario"}, - { "camera_record", "Camera record", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVScenarioInfo_CameraRecord}, 0, 0, VE, "scenario"}, - { "display_remoting_with_feature_map", "Display remoting with feature map", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVScenarioInfo_DisplayRemotingWithFeatureMap}, 0, 0, VE, "scenario"}, + { "ld_vbr", "Low delay VBR mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_LowDelayVBR}, 0, 0, VE, .unit = "rate_control"}, + { "g_vbr", "Global VBR mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_GlobalVBR}, 0, 0, VE, .unit = "rate_control" }, + { "gld_vbr", "Global low delay VBR mode", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVEncCommonRateControlMode_GlobalLowDelayVBR}, 0, 0, VE, .unit = "rate_control"}, + + {"scenario", "Select usage scenario", OFFSET(opt_enc_scenario), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VE, .unit = "scenario"}, + { "default", "Default scenario", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, VE, .unit = "scenario"}, + { "display_remoting", "Display remoting", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVScenarioInfo_DisplayRemoting}, 0, 0, VE, .unit = "scenario"}, + { "video_conference", "Video conference", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVScenarioInfo_VideoConference}, 0, 0, VE, .unit = "scenario"}, + { "archive", "Archive", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVScenarioInfo_Archive}, 0, 0, VE, .unit = "scenario"}, + { "live_streaming", "Live streaming", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVScenarioInfo_LiveStreaming}, 0, 0, VE, .unit = "scenario"}, + { "camera_record", "Camera record", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVScenarioInfo_CameraRecord}, 0, 0, VE, .unit = "scenario"}, + { "display_remoting_with_feature_map", "Display remoting with feature map", 0, AV_OPT_TYPE_CONST, {.i64 = ff_eAVScenarioInfo_DisplayRemotingWithFeatureMap}, 0, 0, VE, .unit = "scenario"}, {"quality", "Quality", OFFSET(opt_enc_quality), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 100, VE}, {"hw_encoding", "Force hardware encoding", OFFSET(opt_enc_hw), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, VE}, diff --git a/libavcodec/midivid.c b/libavcodec/midivid.c index 599d5c8f8fa..70730231b5f 100644 --- a/libavcodec/midivid.c +++ b/libavcodec/midivid.c @@ -222,7 +222,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; frame->pict_type = key ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; - frame->key_frame = key; + if (key) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/mimic.c b/libavcodec/mimic.c index 891471b30e1..cd5d0d7e3ed 100644 --- a/libavcodec/mimic.c +++ b/libavcodec/mimic.c @@ -67,7 +67,7 @@ typedef struct MimicContext { int next_prev_index; } MimicContext; -static VLC block_vlc; +static VLCElem block_vlc[4368]; static const uint8_t huffsyms[] = { 0x10, 0x20, 0x30, 0x00, 0x11, 0x40, 0x50, 0x12, 0x13, 0x21, 0x31, 0x60, @@ -111,7 +111,7 @@ static av_cold int mimic_decode_end(AVCodecContext *avctx) for (i = 0; i < FF_ARRAY_ELEMS(ctx->frames); i++) { if (ctx->frames[i].f) - ff_thread_release_ext_buffer(avctx, &ctx->frames[i]); + ff_thread_release_ext_buffer(&ctx->frames[i]); av_frame_free(&ctx->frames[i].f); } @@ -120,8 +120,9 @@ static av_cold int mimic_decode_end(AVCodecContext *avctx) static av_cold void mimic_init_static(void) { - INIT_VLC_STATIC_FROM_LENGTHS(&block_vlc, MIMIC_VLC_BITS, FF_ARRAY_ELEMS(huffbits), - huffbits, 1, huffsyms, 1, 1, 0, 0, 4368); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(block_vlc, MIMIC_VLC_BITS, + FF_ARRAY_ELEMS(huffbits), + huffbits, 1, huffsyms, 1, 1, 0, 0); } static av_cold int mimic_decode_init(AVCodecContext *avctx) @@ -163,7 +164,7 @@ static int mimic_decode_update_thread_context(AVCodecContext *avctx, const AVCod dst->prev_index = src->next_prev_index; for (i = 0; i < FF_ARRAY_ELEMS(dst->frames); i++) { - ff_thread_release_ext_buffer(avctx, &dst->frames[i]); + ff_thread_release_ext_buffer(&dst->frames[i]); if (i != src->next_cur_index && src->frames[i].f->data[0]) { ret = ff_thread_ref_frame(&dst->frames[i], &src->frames[i]); if (ret < 0) @@ -226,7 +227,7 @@ static int vlc_decode_block(MimicContext *ctx, int num_coeffs, int qscale) int value; int coeff; - vlc = get_vlc2(&ctx->gb, block_vlc.table, MIMIC_VLC_BITS, 3); + vlc = get_vlc2(&ctx->gb, block_vlc, MIMIC_VLC_BITS, 3); if (!vlc) /* end-of-block code */ return 0; if (vlc == -1) @@ -395,7 +396,7 @@ static int mimic_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return AVERROR_INVALIDDATA; } - ff_thread_release_ext_buffer(avctx, &ctx->frames[ctx->cur_index]); + ff_thread_release_ext_buffer(&ctx->frames[ctx->cur_index]); ctx->frames[ctx->cur_index].f->pict_type = is_pframe ? AV_PICTURE_TYPE_P : AV_PICTURE_TYPE_I; if ((res = ff_thread_get_ext_buffer(avctx, &ctx->frames[ctx->cur_index], @@ -420,7 +421,7 @@ static int mimic_decode_frame(AVCodecContext *avctx, AVFrame *rframe, ff_thread_report_progress(&ctx->frames[ctx->cur_index], INT_MAX, 0); if (res < 0) { if (!(avctx->active_thread_type & FF_THREAD_FRAME)) - ff_thread_release_ext_buffer(avctx, &ctx->frames[ctx->cur_index]); + ff_thread_release_ext_buffer(&ctx->frames[ctx->cur_index]); return res; } diff --git a/libavcodec/mips/Makefile b/libavcodec/mips/Makefile index 05ed63bf3e4..50fe38a50e4 100644 --- a/libavcodec/mips/Makefile +++ b/libavcodec/mips/Makefile @@ -13,14 +13,12 @@ MIPSFPU-OBJS-$(CONFIG_AMRWB_DECODER) += mips/acelp_filters_mips.o \ mips/acelp_vectors_mips.o MIPSFPU-OBJS-$(CONFIG_MPEGAUDIODSP) += mips/mpegaudiodsp_mips_float.o MIPSDSP-OBJS-$(CONFIG_MPEGAUDIODSP) += mips/mpegaudiodsp_mips_fixed.o -MIPSFPU-OBJS-$(CONFIG_FFT) += mips/fft_mips.o MIPSFPU-OBJS-$(CONFIG_FMTCONVERT) += mips/fmtconvert_mips.o OBJS-$(CONFIG_AC3DSP) += mips/ac3dsp_mips.o OBJS-$(CONFIG_AAC_DECODER) += mips/aacdec_mips.o \ mips/aacsbr_mips.o \ mips/sbrdsp_mips.o \ mips/aacpsdsp_mips.o -MIPSDSP-OBJS-$(CONFIG_AAC_ENCODER) += mips/aaccoder_mips.o MIPSFPU-OBJS-$(CONFIG_AAC_ENCODER) += mips/iirfilter_mips.o OBJS-$(CONFIG_HEVC_DECODER) += mips/hevcdsp_init_mips.o \ mips/hevcpred_init_mips.o diff --git a/libavcodec/mips/aaccoder_mips.c b/libavcodec/mips/aaccoder_mips.c deleted file mode 100644 index bf27a2a5da1..00000000000 --- a/libavcodec/mips/aaccoder_mips.c +++ /dev/null @@ -1,2502 +0,0 @@ -/* - * Copyright (c) 2012 - * MIPS Technologies, Inc., California. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Author: Stanislav Ocovaj (socovaj@mips.com) - * Szabolcs Pal (sabolc@mips.com) - * - * AAC coefficients encoder optimized for MIPS floating-point architecture - * - * 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 - * Reference: libavcodec/aaccoder.c - */ - -#include "libavutil/libm.h" - -#include -#include "libavutil/mathematics.h" -#include "libavcodec/avcodec.h" -#include "libavcodec/put_bits.h" -#include "libavcodec/aac.h" -#include "libavcodec/aacenc.h" -#include "libavcodec/aactab.h" -#include "libavcodec/aacenctab.h" -#include "libavcodec/aacenc_utils.h" - -#if HAVE_INLINE_ASM -#if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 -typedef struct BandCodingPath { - int prev_idx; - float cost; - int run; -} BandCodingPath; - -static const uint8_t uquad_sign_bits[81] = { - 0, 1, 1, 1, 2, 2, 1, 2, 2, - 1, 2, 2, 2, 3, 3, 2, 3, 3, - 1, 2, 2, 2, 3, 3, 2, 3, 3, - 1, 2, 2, 2, 3, 3, 2, 3, 3, - 2, 3, 3, 3, 4, 4, 3, 4, 4, - 2, 3, 3, 3, 4, 4, 3, 4, 4, - 1, 2, 2, 2, 3, 3, 2, 3, 3, - 2, 3, 3, 3, 4, 4, 3, 4, 4, - 2, 3, 3, 3, 4, 4, 3, 4, 4 -}; - -static const uint8_t upair7_sign_bits[64] = { - 0, 1, 1, 1, 1, 1, 1, 1, - 1, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, -}; - -static const uint8_t upair12_sign_bits[169] = { - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 -}; - -static const uint8_t esc_sign_bits[289] = { - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 -}; - -/** - * Functions developed from template function and optimized for quantizing and encoding band - */ -static void quantize_and_encode_band_cost_SQUAD_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, float *out, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy, const float ROUNDING) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - const float IQ = ff_aac_pow2sf_tab [POW_SF2_ZERO + scale_idx - SCALE_ONE_POS + SCALE_DIV_512]; - int i; - int qc1, qc2, qc3, qc4; - float qenergy = 0.0f; - - uint8_t *p_bits = (uint8_t *)ff_aac_spectral_bits[cb-1]; - uint16_t *p_codes = (uint16_t *)ff_aac_spectral_codes[cb-1]; - float *p_vec = (float *)ff_aac_codebook_vectors[cb-1]; - - abs_pow34_v(s->scoefs, in, size); - scaled = s->scoefs; - for (i = 0; i < size; i += 4) { - int curidx; - int *in_int = (int *)&in[i]; - int t0, t1, t2, t3, t4, t5, t6, t7; - const float *vec; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "slt %[qc1], $zero, %[qc1] \n\t" - "slt %[qc2], $zero, %[qc2] \n\t" - "slt %[qc3], $zero, %[qc3] \n\t" - "slt %[qc4], $zero, %[qc4] \n\t" - "lw %[t0], 0(%[in_int]) \n\t" - "lw %[t1], 4(%[in_int]) \n\t" - "lw %[t2], 8(%[in_int]) \n\t" - "lw %[t3], 12(%[in_int]) \n\t" - "srl %[t0], %[t0], 31 \n\t" - "srl %[t1], %[t1], 31 \n\t" - "srl %[t2], %[t2], 31 \n\t" - "srl %[t3], %[t3], 31 \n\t" - "subu %[t4], $zero, %[qc1] \n\t" - "subu %[t5], $zero, %[qc2] \n\t" - "subu %[t6], $zero, %[qc3] \n\t" - "subu %[t7], $zero, %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t5], %[t1] \n\t" - "movn %[qc3], %[t6], %[t2] \n\t" - "movn %[qc4], %[t7], %[t3] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4), [t5]"=&r"(t5), [t6]"=&r"(t6), [t7]"=&r"(t7) - : [in_int]"r"(in_int) - : "memory" - ); - - curidx = qc1; - curidx *= 3; - curidx += qc2; - curidx *= 3; - curidx += qc3; - curidx *= 3; - curidx += qc4; - curidx += 40; - - put_bits(pb, p_bits[curidx], p_codes[curidx]); - - if (out || energy) { - float e1,e2,e3,e4; - vec = &p_vec[curidx*4]; - e1 = vec[0] * IQ; - e2 = vec[1] * IQ; - e3 = vec[2] * IQ; - e4 = vec[3] * IQ; - if (out) { - out[i+0] = e1; - out[i+1] = e2; - out[i+2] = e3; - out[i+3] = e4; - } - if (energy) - qenergy += (e1*e1 + e2*e2) + (e3*e3 + e4*e4); - } - } - if (energy) - *energy = qenergy; -} - -static void quantize_and_encode_band_cost_UQUAD_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, float *out, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy, const float ROUNDING) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - const float IQ = ff_aac_pow2sf_tab [POW_SF2_ZERO + scale_idx - SCALE_ONE_POS + SCALE_DIV_512]; - int i; - int qc1, qc2, qc3, qc4; - float qenergy = 0.0f; - - uint8_t *p_bits = (uint8_t *)ff_aac_spectral_bits[cb-1]; - uint16_t *p_codes = (uint16_t *)ff_aac_spectral_codes[cb-1]; - float *p_vec = (float *)ff_aac_codebook_vectors[cb-1]; - - abs_pow34_v(s->scoefs, in, size); - scaled = s->scoefs; - for (i = 0; i < size; i += 4) { - int curidx, sign, count; - int *in_int = (int *)&in[i]; - uint8_t v_bits; - unsigned int v_codes; - int t0, t1, t2, t3, t4; - const float *vec; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 2 \n\t" - "ori %[sign], $zero, 0 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - "lw %[t0], 0(%[in_int]) \n\t" - "lw %[t1], 4(%[in_int]) \n\t" - "lw %[t2], 8(%[in_int]) \n\t" - "lw %[t3], 12(%[in_int]) \n\t" - "slt %[t0], %[t0], $zero \n\t" - "movn %[sign], %[t0], %[qc1] \n\t" - "slt %[t1], %[t1], $zero \n\t" - "slt %[t2], %[t2], $zero \n\t" - "slt %[t3], %[t3], $zero \n\t" - "sll %[t0], %[sign], 1 \n\t" - "or %[t0], %[t0], %[t1] \n\t" - "movn %[sign], %[t0], %[qc2] \n\t" - "slt %[t4], $zero, %[qc1] \n\t" - "slt %[t1], $zero, %[qc2] \n\t" - "slt %[count], $zero, %[qc3] \n\t" - "sll %[t0], %[sign], 1 \n\t" - "or %[t0], %[t0], %[t2] \n\t" - "movn %[sign], %[t0], %[qc3] \n\t" - "slt %[t2], $zero, %[qc4] \n\t" - "addu %[count], %[count], %[t4] \n\t" - "addu %[count], %[count], %[t1] \n\t" - "sll %[t0], %[sign], 1 \n\t" - "or %[t0], %[t0], %[t3] \n\t" - "movn %[sign], %[t0], %[qc4] \n\t" - "addu %[count], %[count], %[t2] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [sign]"=&r"(sign), [count]"=&r"(count), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4) - : [in_int]"r"(in_int) - : "memory" - ); - - curidx = qc1; - curidx *= 3; - curidx += qc2; - curidx *= 3; - curidx += qc3; - curidx *= 3; - curidx += qc4; - - v_codes = (p_codes[curidx] << count) | (sign & ((1 << count) - 1)); - v_bits = p_bits[curidx] + count; - put_bits(pb, v_bits, v_codes); - - if (out || energy) { - float e1,e2,e3,e4; - vec = &p_vec[curidx*4]; - e1 = copysignf(vec[0] * IQ, in[i+0]); - e2 = copysignf(vec[1] * IQ, in[i+1]); - e3 = copysignf(vec[2] * IQ, in[i+2]); - e4 = copysignf(vec[3] * IQ, in[i+3]); - if (out) { - out[i+0] = e1; - out[i+1] = e2; - out[i+2] = e3; - out[i+3] = e4; - } - if (energy) - qenergy += (e1*e1 + e2*e2) + (e3*e3 + e4*e4); - } - } - if (energy) - *energy = qenergy; -} - -static void quantize_and_encode_band_cost_SPAIR_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, float *out, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy, const float ROUNDING) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - const float IQ = ff_aac_pow2sf_tab [POW_SF2_ZERO + scale_idx - SCALE_ONE_POS + SCALE_DIV_512]; - int i; - int qc1, qc2, qc3, qc4; - float qenergy = 0.0f; - - uint8_t *p_bits = (uint8_t *)ff_aac_spectral_bits[cb-1]; - uint16_t *p_codes = (uint16_t *)ff_aac_spectral_codes[cb-1]; - float *p_vec = (float *)ff_aac_codebook_vectors[cb-1]; - - abs_pow34_v(s->scoefs, in, size); - scaled = s->scoefs; - for (i = 0; i < size; i += 4) { - int curidx, curidx2; - int *in_int = (int *)&in[i]; - uint8_t v_bits; - unsigned int v_codes; - int t0, t1, t2, t3, t4, t5, t6, t7; - const float *vec1, *vec2; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 4 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - "lw %[t0], 0(%[in_int]) \n\t" - "lw %[t1], 4(%[in_int]) \n\t" - "lw %[t2], 8(%[in_int]) \n\t" - "lw %[t3], 12(%[in_int]) \n\t" - "srl %[t0], %[t0], 31 \n\t" - "srl %[t1], %[t1], 31 \n\t" - "srl %[t2], %[t2], 31 \n\t" - "srl %[t3], %[t3], 31 \n\t" - "subu %[t4], $zero, %[qc1] \n\t" - "subu %[t5], $zero, %[qc2] \n\t" - "subu %[t6], $zero, %[qc3] \n\t" - "subu %[t7], $zero, %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t5], %[t1] \n\t" - "movn %[qc3], %[t6], %[t2] \n\t" - "movn %[qc4], %[t7], %[t3] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4), [t5]"=&r"(t5), [t6]"=&r"(t6), [t7]"=&r"(t7) - : [in_int]"r"(in_int) - : "memory" - ); - - curidx = 9 * qc1; - curidx += qc2 + 40; - - curidx2 = 9 * qc3; - curidx2 += qc4 + 40; - - v_codes = (p_codes[curidx] << p_bits[curidx2]) | (p_codes[curidx2]); - v_bits = p_bits[curidx] + p_bits[curidx2]; - put_bits(pb, v_bits, v_codes); - - if (out || energy) { - float e1,e2,e3,e4; - vec1 = &p_vec[curidx*2 ]; - vec2 = &p_vec[curidx2*2]; - e1 = vec1[0] * IQ; - e2 = vec1[1] * IQ; - e3 = vec2[0] * IQ; - e4 = vec2[1] * IQ; - if (out) { - out[i+0] = e1; - out[i+1] = e2; - out[i+2] = e3; - out[i+3] = e4; - } - if (energy) - qenergy += (e1*e1 + e2*e2) + (e3*e3 + e4*e4); - } - } - if (energy) - *energy = qenergy; -} - -static void quantize_and_encode_band_cost_UPAIR7_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, float *out, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy, const float ROUNDING) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - const float IQ = ff_aac_pow2sf_tab [POW_SF2_ZERO + scale_idx - SCALE_ONE_POS + SCALE_DIV_512]; - int i; - int qc1, qc2, qc3, qc4; - float qenergy = 0.0f; - - uint8_t *p_bits = (uint8_t*) ff_aac_spectral_bits[cb-1]; - uint16_t *p_codes = (uint16_t*)ff_aac_spectral_codes[cb-1]; - float *p_vec = (float *)ff_aac_codebook_vectors[cb-1]; - - abs_pow34_v(s->scoefs, in, size); - scaled = s->scoefs; - for (i = 0; i < size; i += 4) { - int curidx1, curidx2, sign1, count1, sign2, count2; - int *in_int = (int *)&in[i]; - uint8_t v_bits; - unsigned int v_codes; - int t0, t1, t2, t3, t4; - const float *vec1, *vec2; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 7 \n\t" - "ori %[sign1], $zero, 0 \n\t" - "ori %[sign2], $zero, 0 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - "lw %[t0], 0(%[in_int]) \n\t" - "lw %[t1], 4(%[in_int]) \n\t" - "lw %[t2], 8(%[in_int]) \n\t" - "lw %[t3], 12(%[in_int]) \n\t" - "slt %[t0], %[t0], $zero \n\t" - "movn %[sign1], %[t0], %[qc1] \n\t" - "slt %[t2], %[t2], $zero \n\t" - "movn %[sign2], %[t2], %[qc3] \n\t" - "slt %[t1], %[t1], $zero \n\t" - "sll %[t0], %[sign1], 1 \n\t" - "or %[t0], %[t0], %[t1] \n\t" - "movn %[sign1], %[t0], %[qc2] \n\t" - "slt %[t3], %[t3], $zero \n\t" - "sll %[t0], %[sign2], 1 \n\t" - "or %[t0], %[t0], %[t3] \n\t" - "movn %[sign2], %[t0], %[qc4] \n\t" - "slt %[count1], $zero, %[qc1] \n\t" - "slt %[t1], $zero, %[qc2] \n\t" - "slt %[count2], $zero, %[qc3] \n\t" - "slt %[t2], $zero, %[qc4] \n\t" - "addu %[count1], %[count1], %[t1] \n\t" - "addu %[count2], %[count2], %[t2] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [sign1]"=&r"(sign1), [count1]"=&r"(count1), - [sign2]"=&r"(sign2), [count2]"=&r"(count2), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4) - : [in_int]"r"(in_int) - : "t0", "t1", "t2", "t3", "t4", - "memory" - ); - - curidx1 = 8 * qc1; - curidx1 += qc2; - - v_codes = (p_codes[curidx1] << count1) | sign1; - v_bits = p_bits[curidx1] + count1; - put_bits(pb, v_bits, v_codes); - - curidx2 = 8 * qc3; - curidx2 += qc4; - - v_codes = (p_codes[curidx2] << count2) | sign2; - v_bits = p_bits[curidx2] + count2; - put_bits(pb, v_bits, v_codes); - - if (out || energy) { - float e1,e2,e3,e4; - vec1 = &p_vec[curidx1*2]; - vec2 = &p_vec[curidx2*2]; - e1 = copysignf(vec1[0] * IQ, in[i+0]); - e2 = copysignf(vec1[1] * IQ, in[i+1]); - e3 = copysignf(vec2[0] * IQ, in[i+2]); - e4 = copysignf(vec2[1] * IQ, in[i+3]); - if (out) { - out[i+0] = e1; - out[i+1] = e2; - out[i+2] = e3; - out[i+3] = e4; - } - if (energy) - qenergy += (e1*e1 + e2*e2) + (e3*e3 + e4*e4); - } - } - if (energy) - *energy = qenergy; -} - -static void quantize_and_encode_band_cost_UPAIR12_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, float *out, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy, const float ROUNDING) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - const float IQ = ff_aac_pow2sf_tab [POW_SF2_ZERO + scale_idx - SCALE_ONE_POS + SCALE_DIV_512]; - int i; - int qc1, qc2, qc3, qc4; - float qenergy = 0.0f; - - uint8_t *p_bits = (uint8_t*) ff_aac_spectral_bits[cb-1]; - uint16_t *p_codes = (uint16_t*)ff_aac_spectral_codes[cb-1]; - float *p_vec = (float *)ff_aac_codebook_vectors[cb-1]; - - abs_pow34_v(s->scoefs, in, size); - scaled = s->scoefs; - for (i = 0; i < size; i += 4) { - int curidx1, curidx2, sign1, count1, sign2, count2; - int *in_int = (int *)&in[i]; - uint8_t v_bits; - unsigned int v_codes; - int t0, t1, t2, t3, t4; - const float *vec1, *vec2; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 12 \n\t" - "ori %[sign1], $zero, 0 \n\t" - "ori %[sign2], $zero, 0 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - "lw %[t0], 0(%[in_int]) \n\t" - "lw %[t1], 4(%[in_int]) \n\t" - "lw %[t2], 8(%[in_int]) \n\t" - "lw %[t3], 12(%[in_int]) \n\t" - "slt %[t0], %[t0], $zero \n\t" - "movn %[sign1], %[t0], %[qc1] \n\t" - "slt %[t2], %[t2], $zero \n\t" - "movn %[sign2], %[t2], %[qc3] \n\t" - "slt %[t1], %[t1], $zero \n\t" - "sll %[t0], %[sign1], 1 \n\t" - "or %[t0], %[t0], %[t1] \n\t" - "movn %[sign1], %[t0], %[qc2] \n\t" - "slt %[t3], %[t3], $zero \n\t" - "sll %[t0], %[sign2], 1 \n\t" - "or %[t0], %[t0], %[t3] \n\t" - "movn %[sign2], %[t0], %[qc4] \n\t" - "slt %[count1], $zero, %[qc1] \n\t" - "slt %[t1], $zero, %[qc2] \n\t" - "slt %[count2], $zero, %[qc3] \n\t" - "slt %[t2], $zero, %[qc4] \n\t" - "addu %[count1], %[count1], %[t1] \n\t" - "addu %[count2], %[count2], %[t2] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [sign1]"=&r"(sign1), [count1]"=&r"(count1), - [sign2]"=&r"(sign2), [count2]"=&r"(count2), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4) - : [in_int]"r"(in_int) - : "memory" - ); - - curidx1 = 13 * qc1; - curidx1 += qc2; - - v_codes = (p_codes[curidx1] << count1) | sign1; - v_bits = p_bits[curidx1] + count1; - put_bits(pb, v_bits, v_codes); - - curidx2 = 13 * qc3; - curidx2 += qc4; - - v_codes = (p_codes[curidx2] << count2) | sign2; - v_bits = p_bits[curidx2] + count2; - put_bits(pb, v_bits, v_codes); - - if (out || energy) { - float e1,e2,e3,e4; - vec1 = &p_vec[curidx1*2]; - vec2 = &p_vec[curidx2*2]; - e1 = copysignf(vec1[0] * IQ, in[i+0]); - e2 = copysignf(vec1[1] * IQ, in[i+1]); - e3 = copysignf(vec2[0] * IQ, in[i+2]); - e4 = copysignf(vec2[1] * IQ, in[i+3]); - if (out) { - out[i+0] = e1; - out[i+1] = e2; - out[i+2] = e3; - out[i+3] = e4; - } - if (energy) - qenergy += (e1*e1 + e2*e2) + (e3*e3 + e4*e4); - } - } - if (energy) - *energy = qenergy; -} - -static void quantize_and_encode_band_cost_ESC_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, float *out, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy, const float ROUNDING) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - const float IQ = ff_aac_pow2sf_tab [POW_SF2_ZERO + scale_idx - SCALE_ONE_POS + SCALE_DIV_512]; - int i; - int qc1, qc2, qc3, qc4; - float qenergy = 0.0f; - - uint8_t *p_bits = (uint8_t* )ff_aac_spectral_bits[cb-1]; - uint16_t *p_codes = (uint16_t*)ff_aac_spectral_codes[cb-1]; - float *p_vectors = (float* )ff_aac_codebook_vectors[cb-1]; - - abs_pow34_v(s->scoefs, in, size); - scaled = s->scoefs; - - if (cb < 11) { - for (i = 0; i < size; i += 4) { - int curidx, curidx2, sign1, count1, sign2, count2; - int *in_int = (int *)&in[i]; - uint8_t v_bits; - unsigned int v_codes; - int t0, t1, t2, t3, t4; - const float *vec1, *vec2; - - qc1 = scaled[i ] * Q34 + ROUNDING; - qc2 = scaled[i+1] * Q34 + ROUNDING; - qc3 = scaled[i+2] * Q34 + ROUNDING; - qc4 = scaled[i+3] * Q34 + ROUNDING; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 16 \n\t" - "ori %[sign1], $zero, 0 \n\t" - "ori %[sign2], $zero, 0 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - "lw %[t0], 0(%[in_int]) \n\t" - "lw %[t1], 4(%[in_int]) \n\t" - "lw %[t2], 8(%[in_int]) \n\t" - "lw %[t3], 12(%[in_int]) \n\t" - "slt %[t0], %[t0], $zero \n\t" - "movn %[sign1], %[t0], %[qc1] \n\t" - "slt %[t2], %[t2], $zero \n\t" - "movn %[sign2], %[t2], %[qc3] \n\t" - "slt %[t1], %[t1], $zero \n\t" - "sll %[t0], %[sign1], 1 \n\t" - "or %[t0], %[t0], %[t1] \n\t" - "movn %[sign1], %[t0], %[qc2] \n\t" - "slt %[t3], %[t3], $zero \n\t" - "sll %[t0], %[sign2], 1 \n\t" - "or %[t0], %[t0], %[t3] \n\t" - "movn %[sign2], %[t0], %[qc4] \n\t" - "slt %[count1], $zero, %[qc1] \n\t" - "slt %[t1], $zero, %[qc2] \n\t" - "slt %[count2], $zero, %[qc3] \n\t" - "slt %[t2], $zero, %[qc4] \n\t" - "addu %[count1], %[count1], %[t1] \n\t" - "addu %[count2], %[count2], %[t2] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [sign1]"=&r"(sign1), [count1]"=&r"(count1), - [sign2]"=&r"(sign2), [count2]"=&r"(count2), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4) - : [in_int]"r"(in_int) - : "memory" - ); - - curidx = 17 * qc1; - curidx += qc2; - curidx2 = 17 * qc3; - curidx2 += qc4; - - v_codes = (p_codes[curidx] << count1) | sign1; - v_bits = p_bits[curidx] + count1; - put_bits(pb, v_bits, v_codes); - - v_codes = (p_codes[curidx2] << count2) | sign2; - v_bits = p_bits[curidx2] + count2; - put_bits(pb, v_bits, v_codes); - - if (out || energy) { - float e1,e2,e3,e4; - vec1 = &p_vectors[curidx*2 ]; - vec2 = &p_vectors[curidx2*2]; - e1 = copysignf(vec1[0] * IQ, in[i+0]); - e2 = copysignf(vec1[1] * IQ, in[i+1]); - e3 = copysignf(vec2[0] * IQ, in[i+2]); - e4 = copysignf(vec2[1] * IQ, in[i+3]); - if (out) { - out[i+0] = e1; - out[i+1] = e2; - out[i+2] = e3; - out[i+3] = e4; - } - if (energy) - qenergy += (e1*e1 + e2*e2) + (e3*e3 + e4*e4); - } - } - } else { - for (i = 0; i < size; i += 4) { - int curidx, curidx2, sign1, count1, sign2, count2; - int *in_int = (int *)&in[i]; - uint8_t v_bits; - unsigned int v_codes; - int c1, c2, c3, c4; - int t0, t1, t2, t3, t4; - - qc1 = scaled[i ] * Q34 + ROUNDING; - qc2 = scaled[i+1] * Q34 + ROUNDING; - qc3 = scaled[i+2] * Q34 + ROUNDING; - qc4 = scaled[i+3] * Q34 + ROUNDING; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 16 \n\t" - "ori %[sign1], $zero, 0 \n\t" - "ori %[sign2], $zero, 0 \n\t" - "shll_s.w %[c1], %[qc1], 18 \n\t" - "shll_s.w %[c2], %[qc2], 18 \n\t" - "shll_s.w %[c3], %[qc3], 18 \n\t" - "shll_s.w %[c4], %[qc4], 18 \n\t" - "srl %[c1], %[c1], 18 \n\t" - "srl %[c2], %[c2], 18 \n\t" - "srl %[c3], %[c3], 18 \n\t" - "srl %[c4], %[c4], 18 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - "lw %[t0], 0(%[in_int]) \n\t" - "lw %[t1], 4(%[in_int]) \n\t" - "lw %[t2], 8(%[in_int]) \n\t" - "lw %[t3], 12(%[in_int]) \n\t" - "slt %[t0], %[t0], $zero \n\t" - "movn %[sign1], %[t0], %[qc1] \n\t" - "slt %[t2], %[t2], $zero \n\t" - "movn %[sign2], %[t2], %[qc3] \n\t" - "slt %[t1], %[t1], $zero \n\t" - "sll %[t0], %[sign1], 1 \n\t" - "or %[t0], %[t0], %[t1] \n\t" - "movn %[sign1], %[t0], %[qc2] \n\t" - "slt %[t3], %[t3], $zero \n\t" - "sll %[t0], %[sign2], 1 \n\t" - "or %[t0], %[t0], %[t3] \n\t" - "movn %[sign2], %[t0], %[qc4] \n\t" - "slt %[count1], $zero, %[qc1] \n\t" - "slt %[t1], $zero, %[qc2] \n\t" - "slt %[count2], $zero, %[qc3] \n\t" - "slt %[t2], $zero, %[qc4] \n\t" - "addu %[count1], %[count1], %[t1] \n\t" - "addu %[count2], %[count2], %[t2] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [sign1]"=&r"(sign1), [count1]"=&r"(count1), - [sign2]"=&r"(sign2), [count2]"=&r"(count2), - [c1]"=&r"(c1), [c2]"=&r"(c2), - [c3]"=&r"(c3), [c4]"=&r"(c4), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4) - : [in_int]"r"(in_int) - : "memory" - ); - - curidx = 17 * qc1; - curidx += qc2; - - curidx2 = 17 * qc3; - curidx2 += qc4; - - v_codes = (p_codes[curidx] << count1) | sign1; - v_bits = p_bits[curidx] + count1; - put_bits(pb, v_bits, v_codes); - - if (p_vectors[curidx*2 ] == 64.0f) { - int len = av_log2(c1); - v_codes = (((1 << (len - 3)) - 2) << len) | (c1 & ((1 << len) - 1)); - put_bits(pb, len * 2 - 3, v_codes); - } - if (p_vectors[curidx*2+1] == 64.0f) { - int len = av_log2(c2); - v_codes = (((1 << (len - 3)) - 2) << len) | (c2 & ((1 << len) - 1)); - put_bits(pb, len*2-3, v_codes); - } - - v_codes = (p_codes[curidx2] << count2) | sign2; - v_bits = p_bits[curidx2] + count2; - put_bits(pb, v_bits, v_codes); - - if (p_vectors[curidx2*2 ] == 64.0f) { - int len = av_log2(c3); - v_codes = (((1 << (len - 3)) - 2) << len) | (c3 & ((1 << len) - 1)); - put_bits(pb, len* 2 - 3, v_codes); - } - if (p_vectors[curidx2*2+1] == 64.0f) { - int len = av_log2(c4); - v_codes = (((1 << (len - 3)) - 2) << len) | (c4 & ((1 << len) - 1)); - put_bits(pb, len * 2 - 3, v_codes); - } - - if (out || energy) { - float e1, e2, e3, e4; - e1 = copysignf(c1 * cbrtf(c1) * IQ, in[i+0]); - e2 = copysignf(c2 * cbrtf(c2) * IQ, in[i+1]); - e3 = copysignf(c3 * cbrtf(c3) * IQ, in[i+2]); - e4 = copysignf(c4 * cbrtf(c4) * IQ, in[i+3]); - if (out) { - out[i+0] = e1; - out[i+1] = e2; - out[i+2] = e3; - out[i+3] = e4; - } - if (energy) - qenergy += (e1*e1 + e2*e2) + (e3*e3 + e4*e4); - } - } - } - if (energy) - *energy = qenergy; -} - -static void quantize_and_encode_band_cost_NONE_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, float *out, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy, const float ROUNDING) { - av_assert0(0); -} - -static void quantize_and_encode_band_cost_ZERO_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, float *out, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy, const float ROUNDING) { - int i; - if (bits) - *bits = 0; - if (out) { - for (i = 0; i < size; i += 4) { - out[i ] = 0.0f; - out[i+1] = 0.0f; - out[i+2] = 0.0f; - out[i+3] = 0.0f; - } - } - if (energy) - *energy = 0.0f; -} - -static void (*const quantize_and_encode_band_cost_arr[])(struct AACEncContext *s, - PutBitContext *pb, const float *in, float *out, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy, const float ROUNDING) = { - quantize_and_encode_band_cost_ZERO_mips, - quantize_and_encode_band_cost_SQUAD_mips, - quantize_and_encode_band_cost_SQUAD_mips, - quantize_and_encode_band_cost_UQUAD_mips, - quantize_and_encode_band_cost_UQUAD_mips, - quantize_and_encode_band_cost_SPAIR_mips, - quantize_and_encode_band_cost_SPAIR_mips, - quantize_and_encode_band_cost_UPAIR7_mips, - quantize_and_encode_band_cost_UPAIR7_mips, - quantize_and_encode_band_cost_UPAIR12_mips, - quantize_and_encode_band_cost_UPAIR12_mips, - quantize_and_encode_band_cost_ESC_mips, - quantize_and_encode_band_cost_NONE_mips, /* cb 12 doesn't exist */ - quantize_and_encode_band_cost_ZERO_mips, - quantize_and_encode_band_cost_ZERO_mips, - quantize_and_encode_band_cost_ZERO_mips, -}; - -#define quantize_and_encode_band_cost( \ - s, pb, in, out, scaled, size, scale_idx, cb, \ - lambda, uplim, bits, energy, ROUNDING) \ - quantize_and_encode_band_cost_arr[cb]( \ - s, pb, in, out, scaled, size, scale_idx, cb, \ - lambda, uplim, bits, energy, ROUNDING) - -static void quantize_and_encode_band_mips(struct AACEncContext *s, PutBitContext *pb, - const float *in, float *out, int size, int scale_idx, - int cb, const float lambda, int rtz) -{ - quantize_and_encode_band_cost(s, pb, in, out, NULL, size, scale_idx, cb, lambda, - INFINITY, NULL, NULL, (rtz) ? ROUND_TO_ZERO : ROUND_STANDARD); -} - -/** - * Functions developed from template function and optimized for getting the number of bits - */ -static float get_band_numbits_ZERO_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits) -{ - return 0; -} - -static float get_band_numbits_NONE_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits) -{ - av_assert0(0); - return 0; -} - -static float get_band_numbits_SQUAD_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - int i; - int qc1, qc2, qc3, qc4; - int curbits = 0; - - uint8_t *p_bits = (uint8_t *)ff_aac_spectral_bits[cb-1]; - - for (i = 0; i < size; i += 4) { - int curidx; - int *in_int = (int *)&in[i]; - int t0, t1, t2, t3, t4, t5, t6, t7; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "slt %[qc1], $zero, %[qc1] \n\t" - "slt %[qc2], $zero, %[qc2] \n\t" - "slt %[qc3], $zero, %[qc3] \n\t" - "slt %[qc4], $zero, %[qc4] \n\t" - "lw %[t0], 0(%[in_int]) \n\t" - "lw %[t1], 4(%[in_int]) \n\t" - "lw %[t2], 8(%[in_int]) \n\t" - "lw %[t3], 12(%[in_int]) \n\t" - "srl %[t0], %[t0], 31 \n\t" - "srl %[t1], %[t1], 31 \n\t" - "srl %[t2], %[t2], 31 \n\t" - "srl %[t3], %[t3], 31 \n\t" - "subu %[t4], $zero, %[qc1] \n\t" - "subu %[t5], $zero, %[qc2] \n\t" - "subu %[t6], $zero, %[qc3] \n\t" - "subu %[t7], $zero, %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t5], %[t1] \n\t" - "movn %[qc3], %[t6], %[t2] \n\t" - "movn %[qc4], %[t7], %[t3] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4), [t5]"=&r"(t5), [t6]"=&r"(t6), [t7]"=&r"(t7) - : [in_int]"r"(in_int) - : "memory" - ); - - curidx = qc1; - curidx *= 3; - curidx += qc2; - curidx *= 3; - curidx += qc3; - curidx *= 3; - curidx += qc4; - curidx += 40; - - curbits += p_bits[curidx]; - } - return curbits; -} - -static float get_band_numbits_UQUAD_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - int i; - int curbits = 0; - int qc1, qc2, qc3, qc4; - - uint8_t *p_bits = (uint8_t *)ff_aac_spectral_bits[cb-1]; - - for (i = 0; i < size; i += 4) { - int curidx; - int t0, t1, t2, t3, t4; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 2 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4) - ); - - curidx = qc1; - curidx *= 3; - curidx += qc2; - curidx *= 3; - curidx += qc3; - curidx *= 3; - curidx += qc4; - - curbits += p_bits[curidx]; - curbits += uquad_sign_bits[curidx]; - } - return curbits; -} - -static float get_band_numbits_SPAIR_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - int i; - int qc1, qc2, qc3, qc4; - int curbits = 0; - - uint8_t *p_bits = (uint8_t*)ff_aac_spectral_bits[cb-1]; - - for (i = 0; i < size; i += 4) { - int curidx, curidx2; - int *in_int = (int *)&in[i]; - int t0, t1, t2, t3, t4, t5, t6, t7; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 4 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - "lw %[t0], 0(%[in_int]) \n\t" - "lw %[t1], 4(%[in_int]) \n\t" - "lw %[t2], 8(%[in_int]) \n\t" - "lw %[t3], 12(%[in_int]) \n\t" - "srl %[t0], %[t0], 31 \n\t" - "srl %[t1], %[t1], 31 \n\t" - "srl %[t2], %[t2], 31 \n\t" - "srl %[t3], %[t3], 31 \n\t" - "subu %[t4], $zero, %[qc1] \n\t" - "subu %[t5], $zero, %[qc2] \n\t" - "subu %[t6], $zero, %[qc3] \n\t" - "subu %[t7], $zero, %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t5], %[t1] \n\t" - "movn %[qc3], %[t6], %[t2] \n\t" - "movn %[qc4], %[t7], %[t3] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4), [t5]"=&r"(t5), [t6]"=&r"(t6), [t7]"=&r"(t7) - : [in_int]"r"(in_int) - : "memory" - ); - - curidx = 9 * qc1; - curidx += qc2 + 40; - - curidx2 = 9 * qc3; - curidx2 += qc4 + 40; - - curbits += p_bits[curidx] + p_bits[curidx2]; - } - return curbits; -} - -static float get_band_numbits_UPAIR7_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - int i; - int qc1, qc2, qc3, qc4; - int curbits = 0; - - uint8_t *p_bits = (uint8_t *)ff_aac_spectral_bits[cb-1]; - - for (i = 0; i < size; i += 4) { - int curidx, curidx2; - int t0, t1, t2, t3, t4; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 7 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4) - ); - - curidx = 8 * qc1; - curidx += qc2; - - curidx2 = 8 * qc3; - curidx2 += qc4; - - curbits += p_bits[curidx] + - upair7_sign_bits[curidx] + - p_bits[curidx2] + - upair7_sign_bits[curidx2]; - } - return curbits; -} - -static float get_band_numbits_UPAIR12_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - int i; - int qc1, qc2, qc3, qc4; - int curbits = 0; - - uint8_t *p_bits = (uint8_t *)ff_aac_spectral_bits[cb-1]; - - for (i = 0; i < size; i += 4) { - int curidx, curidx2; - int t0, t1, t2, t3, t4; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 12 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4) - ); - - curidx = 13 * qc1; - curidx += qc2; - - curidx2 = 13 * qc3; - curidx2 += qc4; - - curbits += p_bits[curidx] + - p_bits[curidx2] + - upair12_sign_bits[curidx] + - upair12_sign_bits[curidx2]; - } - return curbits; -} - -static float get_band_numbits_ESC_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - int i; - int qc1, qc2, qc3, qc4; - int curbits = 0; - - uint8_t *p_bits = (uint8_t*)ff_aac_spectral_bits[cb-1]; - - for (i = 0; i < size; i += 4) { - int curidx, curidx2; - int cond0, cond1, cond2, cond3; - int c1, c2, c3, c4; - int t4, t5; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 15 \n\t" - "ori %[t5], $zero, 16 \n\t" - "shll_s.w %[c1], %[qc1], 18 \n\t" - "shll_s.w %[c2], %[qc2], 18 \n\t" - "shll_s.w %[c3], %[qc3], 18 \n\t" - "shll_s.w %[c4], %[qc4], 18 \n\t" - "srl %[c1], %[c1], 18 \n\t" - "srl %[c2], %[c2], 18 \n\t" - "srl %[c3], %[c3], 18 \n\t" - "srl %[c4], %[c4], 18 \n\t" - "slt %[cond0], %[t4], %[qc1] \n\t" - "slt %[cond1], %[t4], %[qc2] \n\t" - "slt %[cond2], %[t4], %[qc3] \n\t" - "slt %[cond3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t5], %[cond0] \n\t" - "movn %[qc2], %[t5], %[cond1] \n\t" - "movn %[qc3], %[t5], %[cond2] \n\t" - "movn %[qc4], %[t5], %[cond3] \n\t" - "ori %[t5], $zero, 31 \n\t" - "clz %[c1], %[c1] \n\t" - "clz %[c2], %[c2] \n\t" - "clz %[c3], %[c3] \n\t" - "clz %[c4], %[c4] \n\t" - "subu %[c1], %[t5], %[c1] \n\t" - "subu %[c2], %[t5], %[c2] \n\t" - "subu %[c3], %[t5], %[c3] \n\t" - "subu %[c4], %[t5], %[c4] \n\t" - "sll %[c1], %[c1], 1 \n\t" - "sll %[c2], %[c2], 1 \n\t" - "sll %[c3], %[c3], 1 \n\t" - "sll %[c4], %[c4], 1 \n\t" - "addiu %[c1], %[c1], -3 \n\t" - "addiu %[c2], %[c2], -3 \n\t" - "addiu %[c3], %[c3], -3 \n\t" - "addiu %[c4], %[c4], -3 \n\t" - "subu %[cond0], $zero, %[cond0] \n\t" - "subu %[cond1], $zero, %[cond1] \n\t" - "subu %[cond2], $zero, %[cond2] \n\t" - "subu %[cond3], $zero, %[cond3] \n\t" - "and %[c1], %[c1], %[cond0] \n\t" - "and %[c2], %[c2], %[cond1] \n\t" - "and %[c3], %[c3], %[cond2] \n\t" - "and %[c4], %[c4], %[cond3] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [cond0]"=&r"(cond0), [cond1]"=&r"(cond1), - [cond2]"=&r"(cond2), [cond3]"=&r"(cond3), - [c1]"=&r"(c1), [c2]"=&r"(c2), - [c3]"=&r"(c3), [c4]"=&r"(c4), - [t4]"=&r"(t4), [t5]"=&r"(t5) - ); - - curidx = 17 * qc1; - curidx += qc2; - - curidx2 = 17 * qc3; - curidx2 += qc4; - - curbits += p_bits[curidx]; - curbits += esc_sign_bits[curidx]; - curbits += p_bits[curidx2]; - curbits += esc_sign_bits[curidx2]; - - curbits += c1; - curbits += c2; - curbits += c3; - curbits += c4; - } - return curbits; -} - -static float (*const get_band_numbits_arr[])(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits) = { - get_band_numbits_ZERO_mips, - get_band_numbits_SQUAD_mips, - get_band_numbits_SQUAD_mips, - get_band_numbits_UQUAD_mips, - get_band_numbits_UQUAD_mips, - get_band_numbits_SPAIR_mips, - get_band_numbits_SPAIR_mips, - get_band_numbits_UPAIR7_mips, - get_band_numbits_UPAIR7_mips, - get_band_numbits_UPAIR12_mips, - get_band_numbits_UPAIR12_mips, - get_band_numbits_ESC_mips, - get_band_numbits_NONE_mips, /* cb 12 doesn't exist */ - get_band_numbits_ZERO_mips, - get_band_numbits_ZERO_mips, - get_band_numbits_ZERO_mips, -}; - -#define get_band_numbits( \ - s, pb, in, scaled, size, scale_idx, cb, \ - lambda, uplim, bits) \ - get_band_numbits_arr[cb]( \ - s, pb, in, scaled, size, scale_idx, cb, \ - lambda, uplim, bits) - -static float quantize_band_cost_bits(struct AACEncContext *s, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy) -{ - return get_band_numbits(s, NULL, in, scaled, size, scale_idx, cb, lambda, uplim, bits); -} - -/** - * Functions developed from template function and optimized for getting the band cost - */ -#if HAVE_MIPSFPU -static float get_band_cost_ZERO_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy) -{ - int i; - float cost = 0; - - for (i = 0; i < size; i += 4) { - cost += in[i ] * in[i ]; - cost += in[i+1] * in[i+1]; - cost += in[i+2] * in[i+2]; - cost += in[i+3] * in[i+3]; - } - if (bits) - *bits = 0; - if (energy) - *energy = 0.0f; - return cost * lambda; -} - -static float get_band_cost_NONE_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy) -{ - av_assert0(0); - return 0; -} - -static float get_band_cost_SQUAD_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - const float IQ = ff_aac_pow2sf_tab [POW_SF2_ZERO + scale_idx - SCALE_ONE_POS + SCALE_DIV_512]; - int i; - float cost = 0; - float qenergy = 0.0f; - int qc1, qc2, qc3, qc4; - int curbits = 0; - - uint8_t *p_bits = (uint8_t *)ff_aac_spectral_bits[cb-1]; - float *p_codes = (float *)ff_aac_codebook_vectors[cb-1]; - - for (i = 0; i < size; i += 4) { - const float *vec; - int curidx; - int *in_int = (int *)&in[i]; - float *in_pos = (float *)&in[i]; - float di0, di1, di2, di3; - int t0, t1, t2, t3, t4, t5, t6, t7; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "slt %[qc1], $zero, %[qc1] \n\t" - "slt %[qc2], $zero, %[qc2] \n\t" - "slt %[qc3], $zero, %[qc3] \n\t" - "slt %[qc4], $zero, %[qc4] \n\t" - "lw %[t0], 0(%[in_int]) \n\t" - "lw %[t1], 4(%[in_int]) \n\t" - "lw %[t2], 8(%[in_int]) \n\t" - "lw %[t3], 12(%[in_int]) \n\t" - "srl %[t0], %[t0], 31 \n\t" - "srl %[t1], %[t1], 31 \n\t" - "srl %[t2], %[t2], 31 \n\t" - "srl %[t3], %[t3], 31 \n\t" - "subu %[t4], $zero, %[qc1] \n\t" - "subu %[t5], $zero, %[qc2] \n\t" - "subu %[t6], $zero, %[qc3] \n\t" - "subu %[t7], $zero, %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t5], %[t1] \n\t" - "movn %[qc3], %[t6], %[t2] \n\t" - "movn %[qc4], %[t7], %[t3] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4), [t5]"=&r"(t5), [t6]"=&r"(t6), [t7]"=&r"(t7) - : [in_int]"r"(in_int) - : "memory" - ); - - curidx = qc1; - curidx *= 3; - curidx += qc2; - curidx *= 3; - curidx += qc3; - curidx *= 3; - curidx += qc4; - curidx += 40; - - curbits += p_bits[curidx]; - vec = &p_codes[curidx*4]; - - qenergy += vec[0]*vec[0] + vec[1]*vec[1] - + vec[2]*vec[2] + vec[3]*vec[3]; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "lwc1 $f0, 0(%[in_pos]) \n\t" - "lwc1 $f1, 0(%[vec]) \n\t" - "lwc1 $f2, 4(%[in_pos]) \n\t" - "lwc1 $f3, 4(%[vec]) \n\t" - "lwc1 $f4, 8(%[in_pos]) \n\t" - "lwc1 $f5, 8(%[vec]) \n\t" - "lwc1 $f6, 12(%[in_pos]) \n\t" - "lwc1 $f7, 12(%[vec]) \n\t" - "nmsub.s %[di0], $f0, $f1, %[IQ] \n\t" - "nmsub.s %[di1], $f2, $f3, %[IQ] \n\t" - "nmsub.s %[di2], $f4, $f5, %[IQ] \n\t" - "nmsub.s %[di3], $f6, $f7, %[IQ] \n\t" - - ".set pop \n\t" - - : [di0]"=&f"(di0), [di1]"=&f"(di1), - [di2]"=&f"(di2), [di3]"=&f"(di3) - : [in_pos]"r"(in_pos), [vec]"r"(vec), - [IQ]"f"(IQ) - : "$f0", "$f1", "$f2", "$f3", - "$f4", "$f5", "$f6", "$f7", - "memory" - ); - - cost += di0 * di0 + di1 * di1 - + di2 * di2 + di3 * di3; - } - - if (bits) - *bits = curbits; - if (energy) - *energy = qenergy * (IQ*IQ); - return cost * lambda + curbits; -} - -static float get_band_cost_UQUAD_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - const float IQ = ff_aac_pow2sf_tab [POW_SF2_ZERO + scale_idx - SCALE_ONE_POS + SCALE_DIV_512]; - int i; - float cost = 0; - float qenergy = 0.0f; - int curbits = 0; - int qc1, qc2, qc3, qc4; - - uint8_t *p_bits = (uint8_t*)ff_aac_spectral_bits[cb-1]; - float *p_codes = (float *)ff_aac_codebook_vectors[cb-1]; - - for (i = 0; i < size; i += 4) { - const float *vec; - int curidx; - float *in_pos = (float *)&in[i]; - float di0, di1, di2, di3; - int t0, t1, t2, t3, t4; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 2 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4) - ); - - curidx = qc1; - curidx *= 3; - curidx += qc2; - curidx *= 3; - curidx += qc3; - curidx *= 3; - curidx += qc4; - - curbits += p_bits[curidx]; - curbits += uquad_sign_bits[curidx]; - vec = &p_codes[curidx*4]; - - qenergy += vec[0]*vec[0] + vec[1]*vec[1] - + vec[2]*vec[2] + vec[3]*vec[3]; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "lwc1 %[di0], 0(%[in_pos]) \n\t" - "lwc1 %[di1], 4(%[in_pos]) \n\t" - "lwc1 %[di2], 8(%[in_pos]) \n\t" - "lwc1 %[di3], 12(%[in_pos]) \n\t" - "abs.s %[di0], %[di0] \n\t" - "abs.s %[di1], %[di1] \n\t" - "abs.s %[di2], %[di2] \n\t" - "abs.s %[di3], %[di3] \n\t" - "lwc1 $f0, 0(%[vec]) \n\t" - "lwc1 $f1, 4(%[vec]) \n\t" - "lwc1 $f2, 8(%[vec]) \n\t" - "lwc1 $f3, 12(%[vec]) \n\t" - "nmsub.s %[di0], %[di0], $f0, %[IQ] \n\t" - "nmsub.s %[di1], %[di1], $f1, %[IQ] \n\t" - "nmsub.s %[di2], %[di2], $f2, %[IQ] \n\t" - "nmsub.s %[di3], %[di3], $f3, %[IQ] \n\t" - - ".set pop \n\t" - - : [di0]"=&f"(di0), [di1]"=&f"(di1), - [di2]"=&f"(di2), [di3]"=&f"(di3) - : [in_pos]"r"(in_pos), [vec]"r"(vec), - [IQ]"f"(IQ) - : "$f0", "$f1", "$f2", "$f3", - "memory" - ); - - cost += di0 * di0 + di1 * di1 - + di2 * di2 + di3 * di3; - } - - if (bits) - *bits = curbits; - if (energy) - *energy = qenergy * (IQ*IQ); - return cost * lambda + curbits; -} - -static float get_band_cost_SPAIR_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - const float IQ = ff_aac_pow2sf_tab [POW_SF2_ZERO + scale_idx - SCALE_ONE_POS + SCALE_DIV_512]; - int i; - float cost = 0; - float qenergy = 0.0f; - int qc1, qc2, qc3, qc4; - int curbits = 0; - - uint8_t *p_bits = (uint8_t *)ff_aac_spectral_bits[cb-1]; - float *p_codes = (float *)ff_aac_codebook_vectors[cb-1]; - - for (i = 0; i < size; i += 4) { - const float *vec, *vec2; - int curidx, curidx2; - int *in_int = (int *)&in[i]; - float *in_pos = (float *)&in[i]; - float di0, di1, di2, di3; - int t0, t1, t2, t3, t4, t5, t6, t7; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 4 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - "lw %[t0], 0(%[in_int]) \n\t" - "lw %[t1], 4(%[in_int]) \n\t" - "lw %[t2], 8(%[in_int]) \n\t" - "lw %[t3], 12(%[in_int]) \n\t" - "srl %[t0], %[t0], 31 \n\t" - "srl %[t1], %[t1], 31 \n\t" - "srl %[t2], %[t2], 31 \n\t" - "srl %[t3], %[t3], 31 \n\t" - "subu %[t4], $zero, %[qc1] \n\t" - "subu %[t5], $zero, %[qc2] \n\t" - "subu %[t6], $zero, %[qc3] \n\t" - "subu %[t7], $zero, %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t5], %[t1] \n\t" - "movn %[qc3], %[t6], %[t2] \n\t" - "movn %[qc4], %[t7], %[t3] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4), [t5]"=&r"(t5), [t6]"=&r"(t6), [t7]"=&r"(t7) - : [in_int]"r"(in_int) - : "memory" - ); - - curidx = 9 * qc1; - curidx += qc2 + 40; - - curidx2 = 9 * qc3; - curidx2 += qc4 + 40; - - curbits += p_bits[curidx]; - curbits += p_bits[curidx2]; - - vec = &p_codes[curidx*2]; - vec2 = &p_codes[curidx2*2]; - - qenergy += vec[0]*vec[0] + vec[1]*vec[1] - + vec2[0]*vec2[0] + vec2[1]*vec2[1]; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "lwc1 $f0, 0(%[in_pos]) \n\t" - "lwc1 $f1, 0(%[vec]) \n\t" - "lwc1 $f2, 4(%[in_pos]) \n\t" - "lwc1 $f3, 4(%[vec]) \n\t" - "lwc1 $f4, 8(%[in_pos]) \n\t" - "lwc1 $f5, 0(%[vec2]) \n\t" - "lwc1 $f6, 12(%[in_pos]) \n\t" - "lwc1 $f7, 4(%[vec2]) \n\t" - "nmsub.s %[di0], $f0, $f1, %[IQ] \n\t" - "nmsub.s %[di1], $f2, $f3, %[IQ] \n\t" - "nmsub.s %[di2], $f4, $f5, %[IQ] \n\t" - "nmsub.s %[di3], $f6, $f7, %[IQ] \n\t" - - ".set pop \n\t" - - : [di0]"=&f"(di0), [di1]"=&f"(di1), - [di2]"=&f"(di2), [di3]"=&f"(di3) - : [in_pos]"r"(in_pos), [vec]"r"(vec), - [vec2]"r"(vec2), [IQ]"f"(IQ) - : "$f0", "$f1", "$f2", "$f3", - "$f4", "$f5", "$f6", "$f7", - "memory" - ); - - cost += di0 * di0 + di1 * di1 - + di2 * di2 + di3 * di3; - } - - if (bits) - *bits = curbits; - if (energy) - *energy = qenergy * (IQ*IQ); - return cost * lambda + curbits; -} - -static float get_band_cost_UPAIR7_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - const float IQ = ff_aac_pow2sf_tab [POW_SF2_ZERO + scale_idx - SCALE_ONE_POS + SCALE_DIV_512]; - int i; - float cost = 0; - float qenergy = 0.0f; - int qc1, qc2, qc3, qc4; - int curbits = 0; - - uint8_t *p_bits = (uint8_t *)ff_aac_spectral_bits[cb-1]; - float *p_codes = (float *)ff_aac_codebook_vectors[cb-1]; - - for (i = 0; i < size; i += 4) { - const float *vec, *vec2; - int curidx, curidx2, sign1, count1, sign2, count2; - int *in_int = (int *)&in[i]; - float *in_pos = (float *)&in[i]; - float di0, di1, di2, di3; - int t0, t1, t2, t3, t4; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 7 \n\t" - "ori %[sign1], $zero, 0 \n\t" - "ori %[sign2], $zero, 0 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - "lw %[t0], 0(%[in_int]) \n\t" - "lw %[t1], 4(%[in_int]) \n\t" - "lw %[t2], 8(%[in_int]) \n\t" - "lw %[t3], 12(%[in_int]) \n\t" - "slt %[t0], %[t0], $zero \n\t" - "movn %[sign1], %[t0], %[qc1] \n\t" - "slt %[t2], %[t2], $zero \n\t" - "movn %[sign2], %[t2], %[qc3] \n\t" - "slt %[t1], %[t1], $zero \n\t" - "sll %[t0], %[sign1], 1 \n\t" - "or %[t0], %[t0], %[t1] \n\t" - "movn %[sign1], %[t0], %[qc2] \n\t" - "slt %[t3], %[t3], $zero \n\t" - "sll %[t0], %[sign2], 1 \n\t" - "or %[t0], %[t0], %[t3] \n\t" - "movn %[sign2], %[t0], %[qc4] \n\t" - "slt %[count1], $zero, %[qc1] \n\t" - "slt %[t1], $zero, %[qc2] \n\t" - "slt %[count2], $zero, %[qc3] \n\t" - "slt %[t2], $zero, %[qc4] \n\t" - "addu %[count1], %[count1], %[t1] \n\t" - "addu %[count2], %[count2], %[t2] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [sign1]"=&r"(sign1), [count1]"=&r"(count1), - [sign2]"=&r"(sign2), [count2]"=&r"(count2), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4) - : [in_int]"r"(in_int) - : "memory" - ); - - curidx = 8 * qc1; - curidx += qc2; - - curidx2 = 8 * qc3; - curidx2 += qc4; - - curbits += p_bits[curidx]; - curbits += upair7_sign_bits[curidx]; - vec = &p_codes[curidx*2]; - - curbits += p_bits[curidx2]; - curbits += upair7_sign_bits[curidx2]; - vec2 = &p_codes[curidx2*2]; - - qenergy += vec[0]*vec[0] + vec[1]*vec[1] - + vec2[0]*vec2[0] + vec2[1]*vec2[1]; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "lwc1 %[di0], 0(%[in_pos]) \n\t" - "lwc1 %[di1], 4(%[in_pos]) \n\t" - "lwc1 %[di2], 8(%[in_pos]) \n\t" - "lwc1 %[di3], 12(%[in_pos]) \n\t" - "abs.s %[di0], %[di0] \n\t" - "abs.s %[di1], %[di1] \n\t" - "abs.s %[di2], %[di2] \n\t" - "abs.s %[di3], %[di3] \n\t" - "lwc1 $f0, 0(%[vec]) \n\t" - "lwc1 $f1, 4(%[vec]) \n\t" - "lwc1 $f2, 0(%[vec2]) \n\t" - "lwc1 $f3, 4(%[vec2]) \n\t" - "nmsub.s %[di0], %[di0], $f0, %[IQ] \n\t" - "nmsub.s %[di1], %[di1], $f1, %[IQ] \n\t" - "nmsub.s %[di2], %[di2], $f2, %[IQ] \n\t" - "nmsub.s %[di3], %[di3], $f3, %[IQ] \n\t" - - ".set pop \n\t" - - : [di0]"=&f"(di0), [di1]"=&f"(di1), - [di2]"=&f"(di2), [di3]"=&f"(di3) - : [in_pos]"r"(in_pos), [vec]"r"(vec), - [vec2]"r"(vec2), [IQ]"f"(IQ) - : "$f0", "$f1", "$f2", "$f3", - "memory" - ); - - cost += di0 * di0 + di1 * di1 - + di2 * di2 + di3 * di3; - } - - if (bits) - *bits = curbits; - if (energy) - *energy = qenergy * (IQ*IQ); - return cost * lambda + curbits; -} - -static float get_band_cost_UPAIR12_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - const float IQ = ff_aac_pow2sf_tab [POW_SF2_ZERO + scale_idx - SCALE_ONE_POS + SCALE_DIV_512]; - int i; - float cost = 0; - float qenergy = 0.0f; - int qc1, qc2, qc3, qc4; - int curbits = 0; - - uint8_t *p_bits = (uint8_t *)ff_aac_spectral_bits[cb-1]; - float *p_codes = (float *)ff_aac_codebook_vectors[cb-1]; - - for (i = 0; i < size; i += 4) { - const float *vec, *vec2; - int curidx, curidx2; - int sign1, count1, sign2, count2; - int *in_int = (int *)&in[i]; - float *in_pos = (float *)&in[i]; - float di0, di1, di2, di3; - int t0, t1, t2, t3, t4; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t4], $zero, 12 \n\t" - "ori %[sign1], $zero, 0 \n\t" - "ori %[sign2], $zero, 0 \n\t" - "slt %[t0], %[t4], %[qc1] \n\t" - "slt %[t1], %[t4], %[qc2] \n\t" - "slt %[t2], %[t4], %[qc3] \n\t" - "slt %[t3], %[t4], %[qc4] \n\t" - "movn %[qc1], %[t4], %[t0] \n\t" - "movn %[qc2], %[t4], %[t1] \n\t" - "movn %[qc3], %[t4], %[t2] \n\t" - "movn %[qc4], %[t4], %[t3] \n\t" - "lw %[t0], 0(%[in_int]) \n\t" - "lw %[t1], 4(%[in_int]) \n\t" - "lw %[t2], 8(%[in_int]) \n\t" - "lw %[t3], 12(%[in_int]) \n\t" - "slt %[t0], %[t0], $zero \n\t" - "movn %[sign1], %[t0], %[qc1] \n\t" - "slt %[t2], %[t2], $zero \n\t" - "movn %[sign2], %[t2], %[qc3] \n\t" - "slt %[t1], %[t1], $zero \n\t" - "sll %[t0], %[sign1], 1 \n\t" - "or %[t0], %[t0], %[t1] \n\t" - "movn %[sign1], %[t0], %[qc2] \n\t" - "slt %[t3], %[t3], $zero \n\t" - "sll %[t0], %[sign2], 1 \n\t" - "or %[t0], %[t0], %[t3] \n\t" - "movn %[sign2], %[t0], %[qc4] \n\t" - "slt %[count1], $zero, %[qc1] \n\t" - "slt %[t1], $zero, %[qc2] \n\t" - "slt %[count2], $zero, %[qc3] \n\t" - "slt %[t2], $zero, %[qc4] \n\t" - "addu %[count1], %[count1], %[t1] \n\t" - "addu %[count2], %[count2], %[t2] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [sign1]"=&r"(sign1), [count1]"=&r"(count1), - [sign2]"=&r"(sign2), [count2]"=&r"(count2), - [t0]"=&r"(t0), [t1]"=&r"(t1), [t2]"=&r"(t2), [t3]"=&r"(t3), - [t4]"=&r"(t4) - : [in_int]"r"(in_int) - : "memory" - ); - - curidx = 13 * qc1; - curidx += qc2; - - curidx2 = 13 * qc3; - curidx2 += qc4; - - curbits += p_bits[curidx]; - curbits += p_bits[curidx2]; - curbits += upair12_sign_bits[curidx]; - curbits += upair12_sign_bits[curidx2]; - vec = &p_codes[curidx*2]; - vec2 = &p_codes[curidx2*2]; - - qenergy += vec[0]*vec[0] + vec[1]*vec[1] - + vec2[0]*vec2[0] + vec2[1]*vec2[1]; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "lwc1 %[di0], 0(%[in_pos]) \n\t" - "lwc1 %[di1], 4(%[in_pos]) \n\t" - "lwc1 %[di2], 8(%[in_pos]) \n\t" - "lwc1 %[di3], 12(%[in_pos]) \n\t" - "abs.s %[di0], %[di0] \n\t" - "abs.s %[di1], %[di1] \n\t" - "abs.s %[di2], %[di2] \n\t" - "abs.s %[di3], %[di3] \n\t" - "lwc1 $f0, 0(%[vec]) \n\t" - "lwc1 $f1, 4(%[vec]) \n\t" - "lwc1 $f2, 0(%[vec2]) \n\t" - "lwc1 $f3, 4(%[vec2]) \n\t" - "nmsub.s %[di0], %[di0], $f0, %[IQ] \n\t" - "nmsub.s %[di1], %[di1], $f1, %[IQ] \n\t" - "nmsub.s %[di2], %[di2], $f2, %[IQ] \n\t" - "nmsub.s %[di3], %[di3], $f3, %[IQ] \n\t" - - ".set pop \n\t" - - : [di0]"=&f"(di0), [di1]"=&f"(di1), - [di2]"=&f"(di2), [di3]"=&f"(di3) - : [in_pos]"r"(in_pos), [vec]"r"(vec), - [vec2]"r"(vec2), [IQ]"f"(IQ) - : "$f0", "$f1", "$f2", "$f3", - "memory" - ); - - cost += di0 * di0 + di1 * di1 - + di2 * di2 + di3 * di3; - } - - if (bits) - *bits = curbits; - if (energy) - *energy = qenergy * (IQ*IQ); - return cost * lambda + curbits; -} - -static float get_band_cost_ESC_mips(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy) -{ - const float Q34 = ff_aac_pow34sf_tab[POW_SF2_ZERO - scale_idx + SCALE_ONE_POS - SCALE_DIV_512]; - const float IQ = ff_aac_pow2sf_tab [POW_SF2_ZERO + scale_idx - SCALE_ONE_POS + SCALE_DIV_512]; - const float CLIPPED_ESCAPE = 165140.0f * IQ; - int i; - float cost = 0; - float qenergy = 0.0f; - int qc1, qc2, qc3, qc4; - int curbits = 0; - - uint8_t *p_bits = (uint8_t*)ff_aac_spectral_bits[cb-1]; - float *p_codes = (float* )ff_aac_codebook_vectors[cb-1]; - - for (i = 0; i < size; i += 4) { - const float *vec, *vec2; - int curidx, curidx2; - float t1, t2, t3, t4, V; - float di1, di2, di3, di4; - int cond0, cond1, cond2, cond3; - int c1, c2, c3, c4; - int t6, t7; - - qc1 = scaled[i ] * Q34 + ROUND_STANDARD; - qc2 = scaled[i+1] * Q34 + ROUND_STANDARD; - qc3 = scaled[i+2] * Q34 + ROUND_STANDARD; - qc4 = scaled[i+3] * Q34 + ROUND_STANDARD; - - __asm__ volatile ( - ".set push \n\t" - ".set noreorder \n\t" - - "ori %[t6], $zero, 15 \n\t" - "ori %[t7], $zero, 16 \n\t" - "shll_s.w %[c1], %[qc1], 18 \n\t" - "shll_s.w %[c2], %[qc2], 18 \n\t" - "shll_s.w %[c3], %[qc3], 18 \n\t" - "shll_s.w %[c4], %[qc4], 18 \n\t" - "srl %[c1], %[c1], 18 \n\t" - "srl %[c2], %[c2], 18 \n\t" - "srl %[c3], %[c3], 18 \n\t" - "srl %[c4], %[c4], 18 \n\t" - "slt %[cond0], %[t6], %[qc1] \n\t" - "slt %[cond1], %[t6], %[qc2] \n\t" - "slt %[cond2], %[t6], %[qc3] \n\t" - "slt %[cond3], %[t6], %[qc4] \n\t" - "movn %[qc1], %[t7], %[cond0] \n\t" - "movn %[qc2], %[t7], %[cond1] \n\t" - "movn %[qc3], %[t7], %[cond2] \n\t" - "movn %[qc4], %[t7], %[cond3] \n\t" - - ".set pop \n\t" - - : [qc1]"+r"(qc1), [qc2]"+r"(qc2), - [qc3]"+r"(qc3), [qc4]"+r"(qc4), - [cond0]"=&r"(cond0), [cond1]"=&r"(cond1), - [cond2]"=&r"(cond2), [cond3]"=&r"(cond3), - [c1]"=&r"(c1), [c2]"=&r"(c2), - [c3]"=&r"(c3), [c4]"=&r"(c4), - [t6]"=&r"(t6), [t7]"=&r"(t7) - ); - - curidx = 17 * qc1; - curidx += qc2; - - curidx2 = 17 * qc3; - curidx2 += qc4; - - curbits += p_bits[curidx]; - curbits += esc_sign_bits[curidx]; - vec = &p_codes[curidx*2]; - - curbits += p_bits[curidx2]; - curbits += esc_sign_bits[curidx2]; - vec2 = &p_codes[curidx2*2]; - - curbits += (av_log2(c1) * 2 - 3) & (-cond0); - curbits += (av_log2(c2) * 2 - 3) & (-cond1); - curbits += (av_log2(c3) * 2 - 3) & (-cond2); - curbits += (av_log2(c4) * 2 - 3) & (-cond3); - - t1 = fabsf(in[i ]); - t2 = fabsf(in[i+1]); - t3 = fabsf(in[i+2]); - t4 = fabsf(in[i+3]); - - if (cond0) { - if (t1 >= CLIPPED_ESCAPE) { - di1 = t1 - CLIPPED_ESCAPE; - qenergy += CLIPPED_ESCAPE*CLIPPED_ESCAPE; - } else { - di1 = t1 - (V = c1 * cbrtf(c1) * IQ); - qenergy += V*V; - } - } else { - di1 = t1 - (V = vec[0] * IQ); - qenergy += V*V; - } - - if (cond1) { - if (t2 >= CLIPPED_ESCAPE) { - di2 = t2 - CLIPPED_ESCAPE; - qenergy += CLIPPED_ESCAPE*CLIPPED_ESCAPE; - } else { - di2 = t2 - (V = c2 * cbrtf(c2) * IQ); - qenergy += V*V; - } - } else { - di2 = t2 - (V = vec[1] * IQ); - qenergy += V*V; - } - - if (cond2) { - if (t3 >= CLIPPED_ESCAPE) { - di3 = t3 - CLIPPED_ESCAPE; - qenergy += CLIPPED_ESCAPE*CLIPPED_ESCAPE; - } else { - di3 = t3 - (V = c3 * cbrtf(c3) * IQ); - qenergy += V*V; - } - } else { - di3 = t3 - (V = vec2[0] * IQ); - qenergy += V*V; - } - - if (cond3) { - if (t4 >= CLIPPED_ESCAPE) { - di4 = t4 - CLIPPED_ESCAPE; - qenergy += CLIPPED_ESCAPE*CLIPPED_ESCAPE; - } else { - di4 = t4 - (V = c4 * cbrtf(c4) * IQ); - qenergy += V*V; - } - } else { - di4 = t4 - (V = vec2[1]*IQ); - qenergy += V*V; - } - - cost += di1 * di1 + di2 * di2 - + di3 * di3 + di4 * di4; - } - - if (bits) - *bits = curbits; - return cost * lambda + curbits; -} - -static float (*const get_band_cost_arr[])(struct AACEncContext *s, - PutBitContext *pb, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy) = { - get_band_cost_ZERO_mips, - get_band_cost_SQUAD_mips, - get_band_cost_SQUAD_mips, - get_band_cost_UQUAD_mips, - get_band_cost_UQUAD_mips, - get_band_cost_SPAIR_mips, - get_band_cost_SPAIR_mips, - get_band_cost_UPAIR7_mips, - get_band_cost_UPAIR7_mips, - get_band_cost_UPAIR12_mips, - get_band_cost_UPAIR12_mips, - get_band_cost_ESC_mips, - get_band_cost_NONE_mips, /* cb 12 doesn't exist */ - get_band_cost_ZERO_mips, - get_band_cost_ZERO_mips, - get_band_cost_ZERO_mips, -}; - -#define get_band_cost( \ - s, pb, in, scaled, size, scale_idx, cb, \ - lambda, uplim, bits, energy) \ - get_band_cost_arr[cb]( \ - s, pb, in, scaled, size, scale_idx, cb, \ - lambda, uplim, bits, energy) - -static float quantize_band_cost(struct AACEncContext *s, const float *in, - const float *scaled, int size, int scale_idx, - int cb, const float lambda, const float uplim, - int *bits, float *energy) -{ - return get_band_cost(s, NULL, in, scaled, size, scale_idx, cb, lambda, uplim, bits, energy); -} - -#include "libavcodec/aacenc_quantization_misc.h" - -#include "libavcodec/aaccoder_twoloop.h" - -static void search_for_ms_mips(AACEncContext *s, ChannelElement *cpe) -{ - int start = 0, i, w, w2, g, sid_sf_boost, prev_mid, prev_side; - uint8_t nextband0[128], nextband1[128]; - float M[128], S[128]; - float *L34 = s->scoefs, *R34 = s->scoefs + 128, *M34 = s->scoefs + 128*2, *S34 = s->scoefs + 128*3; - const float lambda = s->lambda; - const float mslambda = FFMIN(1.0f, lambda / 120.f); - SingleChannelElement *sce0 = &cpe->ch[0]; - SingleChannelElement *sce1 = &cpe->ch[1]; - if (!cpe->common_window) - return; - - /** Scout out next nonzero bands */ - ff_init_nextband_map(sce0, nextband0); - ff_init_nextband_map(sce1, nextband1); - - prev_mid = sce0->sf_idx[0]; - prev_side = sce1->sf_idx[0]; - for (w = 0; w < sce0->ics.num_windows; w += sce0->ics.group_len[w]) { - start = 0; - for (g = 0; g < sce0->ics.num_swb; g++) { - float bmax = bval2bmax(g * 17.0f / sce0->ics.num_swb) / 0.0045f; - if (!cpe->is_mask[w*16+g]) - cpe->ms_mask[w*16+g] = 0; - if (!sce0->zeroes[w*16+g] && !sce1->zeroes[w*16+g] && !cpe->is_mask[w*16+g]) { - float Mmax = 0.0f, Smax = 0.0f; - - /* Must compute mid/side SF and book for the whole window group */ - for (w2 = 0; w2 < sce0->ics.group_len[w]; w2++) { - for (i = 0; i < sce0->ics.swb_sizes[g]; i++) { - M[i] = (sce0->coeffs[start+(w+w2)*128+i] - + sce1->coeffs[start+(w+w2)*128+i]) * 0.5; - S[i] = M[i] - - sce1->coeffs[start+(w+w2)*128+i]; - } - abs_pow34_v(M34, M, sce0->ics.swb_sizes[g]); - abs_pow34_v(S34, S, sce0->ics.swb_sizes[g]); - for (i = 0; i < sce0->ics.swb_sizes[g]; i++ ) { - Mmax = FFMAX(Mmax, M34[i]); - Smax = FFMAX(Smax, S34[i]); - } - } - - for (sid_sf_boost = 0; sid_sf_boost < 4; sid_sf_boost++) { - float dist1 = 0.0f, dist2 = 0.0f; - int B0 = 0, B1 = 0; - int minidx; - int mididx, sididx; - int midcb, sidcb; - - minidx = FFMIN(sce0->sf_idx[w*16+g], sce1->sf_idx[w*16+g]); - mididx = av_clip(minidx, 0, SCALE_MAX_POS - SCALE_DIV_512); - sididx = av_clip(minidx - sid_sf_boost * 3, 0, SCALE_MAX_POS - SCALE_DIV_512); - if (sce0->band_type[w*16+g] != NOISE_BT && sce1->band_type[w*16+g] != NOISE_BT - && ( !ff_sfdelta_can_replace(sce0, nextband0, prev_mid, mididx, w*16+g) - || !ff_sfdelta_can_replace(sce1, nextband1, prev_side, sididx, w*16+g))) { - /* scalefactor range violation, bad stuff, will decrease quality unacceptably */ - continue; - } - - midcb = find_min_book(Mmax, mididx); - sidcb = find_min_book(Smax, sididx); - - /* No CB can be zero */ - midcb = FFMAX(1,midcb); - sidcb = FFMAX(1,sidcb); - - for (w2 = 0; w2 < sce0->ics.group_len[w]; w2++) { - FFPsyBand *band0 = &s->psy.ch[s->cur_channel+0].psy_bands[(w+w2)*16+g]; - FFPsyBand *band1 = &s->psy.ch[s->cur_channel+1].psy_bands[(w+w2)*16+g]; - float minthr = FFMIN(band0->threshold, band1->threshold); - int b1,b2,b3,b4; - for (i = 0; i < sce0->ics.swb_sizes[g]; i++) { - M[i] = (sce0->coeffs[start+(w+w2)*128+i] - + sce1->coeffs[start+(w+w2)*128+i]) * 0.5; - S[i] = M[i] - - sce1->coeffs[start+(w+w2)*128+i]; - } - - abs_pow34_v(L34, sce0->coeffs+start+(w+w2)*128, sce0->ics.swb_sizes[g]); - abs_pow34_v(R34, sce1->coeffs+start+(w+w2)*128, sce0->ics.swb_sizes[g]); - abs_pow34_v(M34, M, sce0->ics.swb_sizes[g]); - abs_pow34_v(S34, S, sce0->ics.swb_sizes[g]); - dist1 += quantize_band_cost(s, &sce0->coeffs[start + (w+w2)*128], - L34, - sce0->ics.swb_sizes[g], - sce0->sf_idx[w*16+g], - sce0->band_type[w*16+g], - lambda / band0->threshold, INFINITY, &b1, NULL); - dist1 += quantize_band_cost(s, &sce1->coeffs[start + (w+w2)*128], - R34, - sce1->ics.swb_sizes[g], - sce1->sf_idx[w*16+g], - sce1->band_type[w*16+g], - lambda / band1->threshold, INFINITY, &b2, NULL); - dist2 += quantize_band_cost(s, M, - M34, - sce0->ics.swb_sizes[g], - mididx, - midcb, - lambda / minthr, INFINITY, &b3, NULL); - dist2 += quantize_band_cost(s, S, - S34, - sce1->ics.swb_sizes[g], - sididx, - sidcb, - mslambda / (minthr * bmax), INFINITY, &b4, NULL); - B0 += b1+b2; - B1 += b3+b4; - dist1 -= b1+b2; - dist2 -= b3+b4; - } - cpe->ms_mask[w*16+g] = dist2 <= dist1 && B1 < B0; - if (cpe->ms_mask[w*16+g]) { - if (sce0->band_type[w*16+g] != NOISE_BT && sce1->band_type[w*16+g] != NOISE_BT) { - sce0->sf_idx[w*16+g] = mididx; - sce1->sf_idx[w*16+g] = sididx; - sce0->band_type[w*16+g] = midcb; - sce1->band_type[w*16+g] = sidcb; - } else if ((sce0->band_type[w*16+g] != NOISE_BT) ^ (sce1->band_type[w*16+g] != NOISE_BT)) { - /* ms_mask unneeded, and it confuses some decoders */ - cpe->ms_mask[w*16+g] = 0; - } - break; - } else if (B1 > B0) { - /* More boost won't fix this */ - break; - } - } - } - if (!sce0->zeroes[w*16+g] && sce0->band_type[w*16+g] < RESERVED_BT) - prev_mid = sce0->sf_idx[w*16+g]; - if (!sce1->zeroes[w*16+g] && !cpe->is_mask[w*16+g] && sce1->band_type[w*16+g] < RESERVED_BT) - prev_side = sce1->sf_idx[w*16+g]; - start += sce0->ics.swb_sizes[g]; - } - } -} -#endif /*HAVE_MIPSFPU */ - -#include "libavcodec/aaccoder_trellis.h" - -#endif /* !HAVE_MIPS32R6 && !HAVE_MIPS64R6 */ -#endif /* HAVE_INLINE_ASM */ - -void ff_aac_coder_init_mips(AACEncContext *c) { -#if HAVE_INLINE_ASM -#if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 - AACCoefficientsEncoder *e = c->coder; - int option = c->options.coder; - - if (option == 2) { - e->quantize_and_encode_band = quantize_and_encode_band_mips; - e->encode_window_bands_info = codebook_trellis_rate; -#if HAVE_MIPSFPU - e->search_for_quantizers = search_for_quantizers_twoloop; -#endif /* HAVE_MIPSFPU */ - } -#if HAVE_MIPSFPU - e->search_for_ms = search_for_ms_mips; -#endif /* HAVE_MIPSFPU */ -#endif /* !HAVE_MIPS32R6 && !HAVE_MIPS64R6 */ -#endif /* HAVE_INLINE_ASM */ -} diff --git a/libavcodec/mips/aacdec_mips.c b/libavcodec/mips/aacdec_mips.c index cd357cedbc7..456e2709150 100644 --- a/libavcodec/mips/aacdec_mips.c +++ b/libavcodec/mips/aacdec_mips.c @@ -53,7 +53,7 @@ */ #include "libavutil/attributes.h" -#include "libavcodec/aac.h" +#include "libavcodec/aacdec.h" #include "aacdec_mips.h" #include "libavcodec/aactab.h" #include "libavcodec/sinewin.h" @@ -112,7 +112,7 @@ static av_always_inline int lcg_random(unsigned previous_val) return v.s; } -static void imdct_and_windowing_mips(AACContext *ac, SingleChannelElement *sce) +static void imdct_and_windowing_mips(AACDecContext *ac, SingleChannelElement *sce) { IndividualChannelStream *ics = &sce->ics; float *in = sce->coeffs; @@ -224,7 +224,7 @@ static void imdct_and_windowing_mips(AACContext *ac, SingleChannelElement *sce) } } -static void apply_ltp_mips(AACContext *ac, SingleChannelElement *sce) +static void apply_ltp_mips(AACDecContext *ac, SingleChannelElement *sce) { const LongTermPrediction *ltp = &sce->ics.ltp; const uint16_t *offsets = sce->ics.swb_offset; @@ -334,7 +334,7 @@ static av_always_inline void fmul_and_reverse(float *dst, const float *src0, con } } -static void update_ltp_mips(AACContext *ac, SingleChannelElement *sce) +static void update_ltp_mips(AACDecContext *ac, SingleChannelElement *sce) { IndividualChannelStream *ics = &sce->ics; float *saved = sce->saved; @@ -431,7 +431,7 @@ static void update_ltp_mips(AACContext *ac, SingleChannelElement *sce) #endif /* HAVE_MIPSFPU */ #endif /* HAVE_INLINE_ASM */ -void ff_aacdec_init_mips(AACContext *c) +void ff_aacdec_init_mips(AACDecContext *c) { #if HAVE_INLINE_ASM #if HAVE_MIPSFPU diff --git a/libavcodec/mips/aacdec_mips.h b/libavcodec/mips/aacdec_mips.h index 758266fc169..71581986dc3 100644 --- a/libavcodec/mips/aacdec_mips.h +++ b/libavcodec/mips/aacdec_mips.h @@ -57,7 +57,7 @@ #ifndef AVCODEC_MIPS_AACDEC_MIPS_H #define AVCODEC_MIPS_AACDEC_MIPS_H -#include "libavcodec/aac.h" +#include "libavcodec/aacdec.h" #include "libavutil/mips/asmdefs.h" #if HAVE_INLINE_ASM && HAVE_MIPSFPU diff --git a/libavcodec/mips/aacsbr_mips.c b/libavcodec/mips/aacsbr_mips.c index 33fd9b229ef..e0715491e6f 100644 --- a/libavcodec/mips/aacsbr_mips.c +++ b/libavcodec/mips/aacsbr_mips.c @@ -51,7 +51,7 @@ * Reference: libavcodec/aacsbr.c */ -#include "libavcodec/aac.h" +#include "libavcodec/aacdec.h" #include "libavcodec/aacsbr.h" #include "libavutil/mem_internal.h" #include "libavutil/mips/asmdefs.h" @@ -60,7 +60,7 @@ #if HAVE_INLINE_ASM #if HAVE_MIPSFPU -static int sbr_lf_gen_mips(AACContext *ac, SpectralBandReplication *sbr, +static int sbr_lf_gen_mips(SpectralBandReplication *sbr, float X_low[32][40][2], const float W[2][32][32][2], int buf_idx) { diff --git a/libavcodec/mips/aacsbr_mips.h b/libavcodec/mips/aacsbr_mips.h index 4750c94024d..447393164a0 100644 --- a/libavcodec/mips/aacsbr_mips.h +++ b/libavcodec/mips/aacsbr_mips.h @@ -54,7 +54,7 @@ #ifndef AVCODEC_MIPS_AACSBR_MIPS_H #define AVCODEC_MIPS_AACSBR_MIPS_H -#include "libavcodec/aac.h" +#include "libavcodec/aacdec.h" #include "libavcodec/sbr.h" #include "libavutil/mips/asmdefs.h" diff --git a/libavcodec/mips/ac3dsp_mips.c b/libavcodec/mips/ac3dsp_mips.c index a5eaaf8eb2b..e97a4129228 100644 --- a/libavcodec/mips/ac3dsp_mips.c +++ b/libavcodec/mips/ac3dsp_mips.c @@ -53,6 +53,8 @@ * Reference: libavcodec/ac3dsp.c */ +#include + #include "config.h" #include "libavcodec/ac3dsp.h" #include "libavcodec/ac3.h" @@ -203,7 +205,7 @@ static void ac3_update_bap_counts_mips(uint16_t mant_cnt[16], uint8_t *bap, #if HAVE_MIPSFPU #if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 -static void float_to_fixed24_mips(int32_t *dst, const float *src, unsigned int len) +static void float_to_fixed24_mips(int32_t *dst, const float *src, size_t len) { const float scale = 1 << 24; float src0, src1, src2, src3, src4, src5, src6, src7; diff --git a/libavcodec/mips/cabac.h b/libavcodec/mips/cabac.h index 39c308c7e0d..20ecab43207 100644 --- a/libavcodec/mips/cabac.h +++ b/libavcodec/mips/cabac.h @@ -30,6 +30,7 @@ #include "libavutil/mips/mmiutils.h" #include "config.h" +#if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 #define get_cabac_inline get_cabac_inline_mips static av_always_inline int get_cabac_inline_mips(CABACContext *c, uint8_t * const state){ @@ -225,4 +226,5 @@ static av_always_inline int get_cabac_bypass_sign_mips(CABACContext *c, int val) return res; } +#endif /* !HAVE_MIPS32R6 && !HAVE_MIPS64R6 */ #endif /* AVCODEC_MIPS_CABAC_H */ diff --git a/libavcodec/mips/compute_antialias_fixed.h b/libavcodec/mips/compute_antialias_fixed.h index 1f395d23027..982002ad4cd 100644 --- a/libavcodec/mips/compute_antialias_fixed.h +++ b/libavcodec/mips/compute_antialias_fixed.h @@ -56,6 +56,7 @@ #define AVCODEC_MIPS_COMPUTE_ANTIALIAS_FIXED_H #if HAVE_INLINE_ASM +#if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 static void compute_antialias_mips_fixed(MPADecodeContext *s, GranuleDef *g) { @@ -246,6 +247,7 @@ static void compute_antialias_mips_fixed(MPADecodeContext *s, } } #define compute_antialias compute_antialias_mips_fixed +#endif /* !HAVE_MIPS32R6 && !HAVE_MIPS64R6 */ #endif /* HAVE_INLINE_ASM */ #endif /* AVCODEC_MIPS_COMPUTE_ANTIALIAS_FIXED_H */ diff --git a/libavcodec/mips/fft_mips.c b/libavcodec/mips/fft_mips.c deleted file mode 100644 index bf91fc316ce..00000000000 --- a/libavcodec/mips/fft_mips.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright (c) 2012 - * MIPS Technologies, Inc., California. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the MIPS Technologies, Inc., nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE MIPS TECHNOLOGIES, INC. ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE MIPS TECHNOLOGIES, INC. BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Author: Stanislav Ocovaj (socovaj@mips.com) - * Author: Zoran Lukic (zoranl@mips.com) - * - * Optimized MDCT/IMDCT and FFT transforms - * - * 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 "config.h" -#include "libavutil/attributes.h" -#include "libavcodec/fft.h" -#include "libavcodec/fft_table.h" -#include "libavutil/mips/asmdefs.h" - -/** - * FFT transform - */ - -#if HAVE_INLINE_ASM -#if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 -static void ff_fft_calc_mips(FFTContext *s, FFTComplex *z) -{ - int nbits, i, n, num_transforms, offset, step; - int n4, n2, n34; - FFTSample tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8; - FFTComplex *tmpz; - float w_re, w_im; - float *w_re_ptr, *w_im_ptr; - const int fft_size = (1 << s->nbits); - float pom, pom1, pom2, pom3; - float temp, temp1, temp3, temp4; - FFTComplex * tmpz_n2, * tmpz_n34, * tmpz_n4; - FFTComplex * tmpz_n2_i, * tmpz_n34_i, * tmpz_n4_i, * tmpz_i; - float f1 = 0.7071067812; - - num_transforms = (21845 >> (17 - s->nbits)) | 1; - - for (n=0; n> 1) | 1; - - for (n=0; nnbits; nbits++) { - num_transforms = (num_transforms >> 1) | 1; - n2 = 2 * n4; - n34 = 3 * n4; - - for (n=0; n>= 1; - n4 <<= 1; - } -} - -/** - * MDCT/IMDCT transforms. - */ - -static void ff_imdct_half_mips(FFTContext *s, FFTSample *output, const FFTSample *input) -{ - int k, n8, n4, n2, n, j; - const uint16_t *revtab = s->revtab; - const FFTSample *tcos = s->tcos; - const FFTSample *tsin = s->tsin; - const FFTSample *in1, *in2, *in3, *in4; - FFTComplex *z = (FFTComplex *)output; - - int j1; - const float *tcos1, *tsin1, *tcos2, *tsin2; - float temp1, temp2, temp3, temp4, temp5, temp6, temp7, temp8, - temp9, temp10, temp11, temp12, temp13, temp14, temp15, temp16; - FFTComplex *z1, *z2; - - n = 1 << s->mdct_bits; - n2 = n >> 1; - n4 = n >> 2; - n8 = n >> 3; - - /* pre rotation */ - in1 = input; - in2 = input + n2 - 1; - in3 = input + 2; - in4 = input + n2 - 3; - - tcos1 = tcos; - tsin1 = tsin; - - /* n4 = 64 or 128 */ - for(k = 0; k < n4; k += 2) { - j = revtab[k ]; - j1 = revtab[k + 1]; - - __asm__ volatile ( - "lwc1 %[temp1], 0(%[in2]) \t\n" - "lwc1 %[temp2], 0(%[tcos1]) \t\n" - "lwc1 %[temp3], 0(%[tsin1]) \t\n" - "lwc1 %[temp4], 0(%[in1]) \t\n" - "lwc1 %[temp5], 0(%[in4]) \t\n" - "mul.s %[temp9], %[temp1], %[temp2] \t\n" - "mul.s %[temp10], %[temp1], %[temp3] \t\n" - "lwc1 %[temp6], 4(%[tcos1]) \t\n" - "lwc1 %[temp7], 4(%[tsin1]) \t\n" - "nmsub.s %[temp9], %[temp9], %[temp4], %[temp3] \t\n" - "madd.s %[temp10], %[temp10], %[temp4], %[temp2] \t\n" - "mul.s %[temp11], %[temp5], %[temp6] \t\n" - "mul.s %[temp12], %[temp5], %[temp7] \t\n" - "lwc1 %[temp8], 0(%[in3]) \t\n" - PTR_ADDIU " %[tcos1], %[tcos1], 8 \t\n" - PTR_ADDIU " %[tsin1], %[tsin1], 8 \t\n" - PTR_ADDIU " %[in1], %[in1], 16 \t\n" - "nmsub.s %[temp11], %[temp11], %[temp8], %[temp7] \t\n" - "madd.s %[temp12], %[temp12], %[temp8], %[temp6] \t\n" - PTR_ADDIU " %[in2], %[in2], -16 \t\n" - PTR_ADDIU " %[in3], %[in3], 16 \t\n" - PTR_ADDIU " %[in4], %[in4], -16 \t\n" - - : [temp1]"=&f"(temp1), [temp2]"=&f"(temp2), - [temp3]"=&f"(temp3), [temp4]"=&f"(temp4), - [temp5]"=&f"(temp5), [temp6]"=&f"(temp6), - [temp7]"=&f"(temp7), [temp8]"=&f"(temp8), - [temp9]"=&f"(temp9), [temp10]"=&f"(temp10), - [temp11]"=&f"(temp11), [temp12]"=&f"(temp12), - [tsin1]"+r"(tsin1), [tcos1]"+r"(tcos1), - [in1]"+r"(in1), [in2]"+r"(in2), - [in3]"+r"(in3), [in4]"+r"(in4) - : - : "memory" - ); - - z[j ].re = temp9; - z[j ].im = temp10; - z[j1].re = temp11; - z[j1].im = temp12; - } - - s->fft_calc(s, z); - - /* post rotation + reordering */ - /* n8 = 32 or 64 */ - for(k = 0; k < n8; k += 2) { - tcos1 = &tcos[n8 - k - 2]; - tsin1 = &tsin[n8 - k - 2]; - tcos2 = &tcos[n8 + k]; - tsin2 = &tsin[n8 + k]; - z1 = &z[n8 - k - 2]; - z2 = &z[n8 + k ]; - - __asm__ volatile ( - "lwc1 %[temp1], 12(%[z1]) \t\n" - "lwc1 %[temp2], 4(%[tsin1]) \t\n" - "lwc1 %[temp3], 4(%[tcos1]) \t\n" - "lwc1 %[temp4], 8(%[z1]) \t\n" - "lwc1 %[temp5], 4(%[z1]) \t\n" - "mul.s %[temp9], %[temp1], %[temp2] \t\n" - "mul.s %[temp10], %[temp1], %[temp3] \t\n" - "lwc1 %[temp6], 0(%[tsin1]) \t\n" - "lwc1 %[temp7], 0(%[tcos1]) \t\n" - "nmsub.s %[temp9], %[temp9], %[temp4], %[temp3] \t\n" - "madd.s %[temp10], %[temp10], %[temp4], %[temp2] \t\n" - "mul.s %[temp11], %[temp5], %[temp6] \t\n" - "mul.s %[temp12], %[temp5], %[temp7] \t\n" - "lwc1 %[temp8], 0(%[z1]) \t\n" - "lwc1 %[temp1], 4(%[z2]) \t\n" - "lwc1 %[temp2], 0(%[tsin2]) \t\n" - "lwc1 %[temp3], 0(%[tcos2]) \t\n" - "nmsub.s %[temp11], %[temp11], %[temp8], %[temp7] \t\n" - "madd.s %[temp12], %[temp12], %[temp8], %[temp6] \t\n" - "mul.s %[temp13], %[temp1], %[temp2] \t\n" - "mul.s %[temp14], %[temp1], %[temp3] \t\n" - "lwc1 %[temp4], 0(%[z2]) \t\n" - "lwc1 %[temp5], 12(%[z2]) \t\n" - "lwc1 %[temp6], 4(%[tsin2]) \t\n" - "lwc1 %[temp7], 4(%[tcos2]) \t\n" - "nmsub.s %[temp13], %[temp13], %[temp4], %[temp3] \t\n" - "madd.s %[temp14], %[temp14], %[temp4], %[temp2] \t\n" - "mul.s %[temp15], %[temp5], %[temp6] \t\n" - "mul.s %[temp16], %[temp5], %[temp7] \t\n" - "lwc1 %[temp8], 8(%[z2]) \t\n" - "nmsub.s %[temp15], %[temp15], %[temp8], %[temp7] \t\n" - "madd.s %[temp16], %[temp16], %[temp8], %[temp6] \t\n" - : [temp1]"=&f"(temp1), [temp2]"=&f"(temp2), - [temp3]"=&f"(temp3), [temp4]"=&f"(temp4), - [temp5]"=&f"(temp5), [temp6]"=&f"(temp6), - [temp7]"=&f"(temp7), [temp8]"=&f"(temp8), - [temp9]"=&f"(temp9), [temp10]"=&f"(temp10), - [temp11]"=&f"(temp11), [temp12]"=&f"(temp12), - [temp13]"=&f"(temp13), [temp14]"=&f"(temp14), - [temp15]"=&f"(temp15), [temp16]"=&f"(temp16) - : [z1]"r"(z1), [z2]"r"(z2), - [tsin1]"r"(tsin1), [tcos1]"r"(tcos1), - [tsin2]"r"(tsin2), [tcos2]"r"(tcos2) - : "memory" - ); - - z1[1].re = temp9; - z1[1].im = temp14; - z2[0].re = temp13; - z2[0].im = temp10; - - z1[0].re = temp11; - z1[0].im = temp16; - z2[1].re = temp15; - z2[1].im = temp12; - } -} - -/** - * Compute inverse MDCT of size N = 2^nbits - * @param output N samples - * @param input N/2 samples - */ -static void ff_imdct_calc_mips(FFTContext *s, FFTSample *output, const FFTSample *input) -{ - int k; - int n = 1 << s->mdct_bits; - int n2 = n >> 1; - int n4 = n >> 2; - - ff_imdct_half_mips(s, output+n4, input); - - for(k = 0; k < n4; k+=4) { - output[k] = -output[n2-k-1]; - output[k+1] = -output[n2-k-2]; - output[k+2] = -output[n2-k-3]; - output[k+3] = -output[n2-k-4]; - - output[n-k-1] = output[n2+k]; - output[n-k-2] = output[n2+k+1]; - output[n-k-3] = output[n2+k+2]; - output[n-k-4] = output[n2+k+3]; - } -} -#endif /* !HAVE_MIPS32R6 && !HAVE_MIPS64R6 */ -#endif /* HAVE_INLINE_ASM */ - -av_cold void ff_fft_init_mips(FFTContext *s) -{ - ff_fft_lut_init(); - ff_init_ff_cos_tabs(17); - -#if HAVE_INLINE_ASM -#if !HAVE_MIPS32R6 && !HAVE_MIPS64R6 - s->fft_calc = ff_fft_calc_mips; -#if CONFIG_MDCT - s->imdct_calc = ff_imdct_calc_mips; - s->imdct_half = ff_imdct_half_mips; -#endif -#endif -#endif -} diff --git a/libavcodec/mips/hevc_mc_bi_msa.c b/libavcodec/mips/hevc_mc_bi_msa.c index dac6a32ab42..309ba5025dd 100644 --- a/libavcodec/mips/hevc_mc_bi_msa.c +++ b/libavcodec/mips/hevc_mc_bi_msa.c @@ -5096,7 +5096,7 @@ void ff_hevc_put_hevc_bi_##PEL##_##DIR##WIDTH##_8_msa(uint8_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR - 1]; \ + const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR]; \ \ hevc_##DIR1##_bi_##TAP##t_##WIDTH##w_msa(src, src_stride, src_16bit, \ MAX_PB_SIZE, dst, dst_stride, \ @@ -5150,8 +5150,8 @@ void ff_hevc_put_hevc_bi_##PEL##_hv##WIDTH##_8_msa(uint8_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter_x = ff_hevc_##PEL##_filters[mx - 1]; \ - const int8_t *filter_y = ff_hevc_##PEL##_filters[my - 1]; \ + const int8_t *filter_x = ff_hevc_##PEL##_filters[mx]; \ + const int8_t *filter_y = ff_hevc_##PEL##_filters[my]; \ \ hevc_hv_bi_##TAP##t_##WIDTH##w_msa(src, src_stride, src_16bit, \ MAX_PB_SIZE, dst, dst_stride, \ diff --git a/libavcodec/mips/hevc_mc_biw_msa.c b/libavcodec/mips/hevc_mc_biw_msa.c index 260ec844963..34be61c0dc6 100644 --- a/libavcodec/mips/hevc_mc_biw_msa.c +++ b/libavcodec/mips/hevc_mc_biw_msa.c @@ -6060,7 +6060,7 @@ void ff_hevc_put_hevc_bi_w_##PEL##_##DIR##WIDTH##_8_msa(uint8_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR - 1]; \ + const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR]; \ int log2Wd = denom + 14 - 8; \ \ hevc_##DIR1##_biwgt_##TAP##t_##WIDTH##w_msa(src, src_stride, src_16bit, \ @@ -6122,8 +6122,8 @@ void ff_hevc_put_hevc_bi_w_##PEL##_hv##WIDTH##_8_msa(uint8_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter_x = ff_hevc_##PEL##_filters[mx - 1]; \ - const int8_t *filter_y = ff_hevc_##PEL##_filters[my - 1]; \ + const int8_t *filter_x = ff_hevc_##PEL##_filters[mx]; \ + const int8_t *filter_y = ff_hevc_##PEL##_filters[my]; \ int log2Wd = denom + 14 - 8; \ \ hevc_hv_biwgt_##TAP##t_##WIDTH##w_msa(src, src_stride, src_16bit, \ diff --git a/libavcodec/mips/hevc_mc_uni_msa.c b/libavcodec/mips/hevc_mc_uni_msa.c index e221df7d53f..602b7c614a0 100644 --- a/libavcodec/mips/hevc_mc_uni_msa.c +++ b/libavcodec/mips/hevc_mc_uni_msa.c @@ -4155,7 +4155,7 @@ void ff_hevc_put_hevc_uni_##PEL##_##DIR##WIDTH##_8_msa(uint8_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR - 1]; \ + const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR]; \ \ common_##DIR1##_##TAP##t_##WIDTH##w_msa(src, src_stride, dst, dst_stride, \ filter, height); \ @@ -4207,8 +4207,8 @@ void ff_hevc_put_hevc_uni_##PEL##_hv##WIDTH##_8_msa(uint8_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter_x = ff_hevc_##PEL##_filters[mx - 1]; \ - const int8_t *filter_y = ff_hevc_##PEL##_filters[my - 1]; \ + const int8_t *filter_x = ff_hevc_##PEL##_filters[mx]; \ + const int8_t *filter_y = ff_hevc_##PEL##_filters[my]; \ \ hevc_hv_uni_##TAP##t_##WIDTH##w_msa(src, src_stride, dst, dst_stride, \ filter_x, filter_y, height); \ diff --git a/libavcodec/mips/hevc_mc_uniw_msa.c b/libavcodec/mips/hevc_mc_uniw_msa.c index caf40c34da1..502a5020373 100644 --- a/libavcodec/mips/hevc_mc_uniw_msa.c +++ b/libavcodec/mips/hevc_mc_uniw_msa.c @@ -6263,7 +6263,7 @@ void ff_hevc_put_hevc_uni_w_##PEL##_##DIR##WIDTH##_8_msa(uint8_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR - 1]; \ + const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR]; \ int shift = denom + 14 - 8; \ \ hevc_##DIR1##_uniwgt_##TAP##t_##WIDTH##w_msa(src, src_stride, dst, \ @@ -6320,8 +6320,8 @@ void ff_hevc_put_hevc_uni_w_##PEL##_hv##WIDTH##_8_msa(uint8_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter_x = ff_hevc_##PEL##_filters[mx - 1]; \ - const int8_t *filter_y = ff_hevc_##PEL##_filters[my - 1]; \ + const int8_t *filter_x = ff_hevc_##PEL##_filters[mx]; \ + const int8_t *filter_y = ff_hevc_##PEL##_filters[my]; \ int shift = denom + 14 - 8; \ \ hevc_hv_uniwgt_##TAP##t_##WIDTH##w_msa(src, src_stride, dst, dst_stride, \ diff --git a/libavcodec/mips/hevcdsp_mmi.c b/libavcodec/mips/hevcdsp_mmi.c index 1da56d3d875..7ece7b9be0e 100644 --- a/libavcodec/mips/hevcdsp_mmi.c +++ b/libavcodec/mips/hevcdsp_mmi.c @@ -34,7 +34,7 @@ void ff_hevc_put_hevc_qpel_h##w##_8_mmi(int16_t *dst, const uint8_t *_src, \ ptrdiff_t srcstride = _srcstride / sizeof(pixel); \ double ftmp[15]; \ uint64_t rtmp[1]; \ - const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; \ + const int8_t *filter = ff_hevc_qpel_filters[mx]; \ DECLARE_VAR_ALL64; \ \ x = x_step; \ @@ -134,7 +134,7 @@ void ff_hevc_put_hevc_qpel_hv##w##_8_mmi(int16_t *dst, const uint8_t *_src,\ DECLARE_VAR_ALL64; \ \ src -= (QPEL_EXTRA_BEFORE * srcstride + 3); \ - filter = ff_hevc_qpel_filters[mx - 1]; \ + filter = ff_hevc_qpel_filters[mx]; \ x = x_step; \ y = height + QPEL_EXTRA; \ __asm__ volatile( \ @@ -206,7 +206,7 @@ void ff_hevc_put_hevc_qpel_hv##w##_8_mmi(int16_t *dst, const uint8_t *_src,\ ); \ \ tmp = tmp_array + QPEL_EXTRA_BEFORE * 4 -12; \ - filter = ff_hevc_qpel_filters[my - 1]; \ + filter = ff_hevc_qpel_filters[my]; \ x = x_step; \ y = height; \ __asm__ volatile( \ @@ -314,7 +314,7 @@ void ff_hevc_put_hevc_qpel_bi_h##w##_8_mmi(uint8_t *_dst, \ ptrdiff_t srcstride = _srcstride / sizeof(pixel); \ pixel *dst = (pixel *)_dst; \ ptrdiff_t dststride = _dststride / sizeof(pixel); \ - const int8_t *filter = ff_hevc_qpel_filters[mx - 1]; \ + const int8_t *filter = ff_hevc_qpel_filters[mx]; \ double ftmp[20]; \ uint64_t rtmp[1]; \ union av_intfloat64 shift; \ @@ -458,7 +458,7 @@ void ff_hevc_put_hevc_qpel_bi_hv##w##_8_mmi(uint8_t *_dst, \ offset.i = 64; \ \ src -= (QPEL_EXTRA_BEFORE * srcstride + 3); \ - filter = ff_hevc_qpel_filters[mx - 1]; \ + filter = ff_hevc_qpel_filters[mx]; \ x = width >> 2; \ y = height + QPEL_EXTRA; \ __asm__ volatile( \ @@ -530,7 +530,7 @@ void ff_hevc_put_hevc_qpel_bi_hv##w##_8_mmi(uint8_t *_dst, \ ); \ \ tmp = tmp_array; \ - filter = ff_hevc_qpel_filters[my - 1]; \ + filter = ff_hevc_qpel_filters[my]; \ x = width >> 2; \ y = height; \ __asm__ volatile( \ @@ -665,7 +665,7 @@ void ff_hevc_put_hevc_epel_bi_hv##w##_8_mmi(uint8_t *_dst, \ ptrdiff_t srcstride = _srcstride / sizeof(pixel); \ pixel *dst = (pixel *)_dst; \ ptrdiff_t dststride = _dststride / sizeof(pixel); \ - const int8_t *filter = ff_hevc_epel_filters[mx - 1]; \ + const int8_t *filter = ff_hevc_epel_filters[mx]; \ int16_t tmp_array[(MAX_PB_SIZE + EPEL_EXTRA) * MAX_PB_SIZE]; \ int16_t *tmp = tmp_array; \ double ftmp[12]; \ @@ -735,7 +735,7 @@ void ff_hevc_put_hevc_epel_bi_hv##w##_8_mmi(uint8_t *_dst, \ ); \ \ tmp = tmp_array; \ - filter = ff_hevc_epel_filters[my - 1]; \ + filter = ff_hevc_epel_filters[my]; \ x = width >> 2; \ y = height; \ __asm__ volatile( \ @@ -969,7 +969,7 @@ void ff_hevc_put_hevc_qpel_uni_hv##w##_8_mmi(uint8_t *_dst, \ offset.i = 32; \ \ src -= (QPEL_EXTRA_BEFORE * srcstride + 3); \ - filter = ff_hevc_qpel_filters[mx - 1]; \ + filter = ff_hevc_qpel_filters[mx]; \ x = width >> 2; \ y = height + QPEL_EXTRA; \ __asm__ volatile( \ @@ -1041,7 +1041,7 @@ void ff_hevc_put_hevc_qpel_uni_hv##w##_8_mmi(uint8_t *_dst, \ ); \ \ tmp = tmp_array; \ - filter = ff_hevc_qpel_filters[my - 1]; \ + filter = ff_hevc_qpel_filters[my]; \ x = width >> 2; \ y = height; \ __asm__ volatile( \ diff --git a/libavcodec/mips/hevcdsp_msa.c b/libavcodec/mips/hevcdsp_msa.c index 9c12029c1f5..2b8d2d70ad2 100644 --- a/libavcodec/mips/hevcdsp_msa.c +++ b/libavcodec/mips/hevcdsp_msa.c @@ -4322,7 +4322,7 @@ void ff_hevc_put_hevc_##PEL##_##DIR##WIDTH##_8_msa(int16_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR - 1]; \ + const int8_t *filter = ff_hevc_##PEL##_filters[FILT_DIR]; \ \ hevc_##DIR1##_##TAP##t_##WIDTH##w_msa(src, src_stride, dst, \ MAX_PB_SIZE, filter, height); \ @@ -4373,8 +4373,8 @@ void ff_hevc_put_hevc_##PEL##_hv##WIDTH##_8_msa(int16_t *dst, \ intptr_t my, \ int width) \ { \ - const int8_t *filter_x = ff_hevc_##PEL##_filters[mx - 1]; \ - const int8_t *filter_y = ff_hevc_##PEL##_filters[my - 1]; \ + const int8_t *filter_x = ff_hevc_##PEL##_filters[mx]; \ + const int8_t *filter_y = ff_hevc_##PEL##_filters[my]; \ \ hevc_hv_##TAP##t_##WIDTH##w_msa(src, src_stride, dst, MAX_PB_SIZE, \ filter_x, filter_y, height); \ diff --git a/libavcodec/mips/idctdsp_mips.h b/libavcodec/mips/idctdsp_mips.h index 829efebff96..93a77a6bf39 100644 --- a/libavcodec/mips/idctdsp_mips.h +++ b/libavcodec/mips/idctdsp_mips.h @@ -25,13 +25,13 @@ #include "../mpegvideo.h" void ff_put_pixels_clamped_msa(const int16_t *block, - uint8_t *av_restrict pixels, + uint8_t *restrict pixels, ptrdiff_t line_size); void ff_put_signed_pixels_clamped_msa(const int16_t *block, - uint8_t *av_restrict pixels, + uint8_t *restrict pixels, ptrdiff_t line_size); void ff_add_pixels_clamped_msa(const int16_t *block, - uint8_t *av_restrict pixels, + uint8_t *restrict pixels, ptrdiff_t line_size); void ff_j_rev_dct_msa(int16_t *data); void ff_jref_idct_put_msa(uint8_t *dest, ptrdiff_t stride, int16_t *block); @@ -41,11 +41,11 @@ void ff_simple_idct_put_msa(uint8_t *dest, ptrdiff_t stride_dst, int16_t *block) void ff_simple_idct_add_msa(uint8_t *dest, ptrdiff_t stride_dst, int16_t *block); void ff_put_pixels_clamped_mmi(const int16_t *block, - uint8_t *av_restrict pixels, ptrdiff_t line_size); + uint8_t *restrict pixels, ptrdiff_t line_size); void ff_put_signed_pixels_clamped_mmi(const int16_t *block, - uint8_t *av_restrict pixels, ptrdiff_t line_size); + uint8_t *restrict pixels, ptrdiff_t line_size); void ff_add_pixels_clamped_mmi(const int16_t *block, - uint8_t *av_restrict pixels, ptrdiff_t line_size); + uint8_t *restrict pixels, ptrdiff_t line_size); void ff_simple_idct_8_mmi(int16_t *block); void ff_simple_idct_put_8_mmi(uint8_t *dest, ptrdiff_t line_size, int16_t *block); void ff_simple_idct_add_8_mmi(uint8_t *dest, ptrdiff_t line_size, int16_t *block); diff --git a/libavcodec/mips/idctdsp_mmi.c b/libavcodec/mips/idctdsp_mmi.c index d22e5eedd74..d96b3b1ac62 100644 --- a/libavcodec/mips/idctdsp_mmi.c +++ b/libavcodec/mips/idctdsp_mmi.c @@ -26,7 +26,7 @@ #include "libavutil/mips/mmiutils.h" void ff_put_pixels_clamped_mmi(const int16_t *block, - uint8_t *av_restrict pixels, ptrdiff_t line_size) + uint8_t *restrict pixels, ptrdiff_t line_size) { double ftmp[8]; @@ -83,7 +83,7 @@ void ff_put_pixels_clamped_mmi(const int16_t *block, } void ff_put_signed_pixels_clamped_mmi(const int16_t *block, - uint8_t *av_restrict pixels, ptrdiff_t line_size) + uint8_t *restrict pixels, ptrdiff_t line_size) { double ftmp[5]; @@ -148,7 +148,7 @@ void ff_put_signed_pixels_clamped_mmi(const int16_t *block, } void ff_add_pixels_clamped_mmi(const int16_t *block, - uint8_t *av_restrict pixels, ptrdiff_t line_size) + uint8_t *restrict pixels, ptrdiff_t line_size) { double ftmp[9]; uint64_t tmp[1]; diff --git a/libavcodec/mips/idctdsp_msa.c b/libavcodec/mips/idctdsp_msa.c index b6b98dc7fcc..6fd72337f16 100644 --- a/libavcodec/mips/idctdsp_msa.c +++ b/libavcodec/mips/idctdsp_msa.c @@ -125,21 +125,21 @@ static void add_pixels_clamped_msa(const int16_t *block, uint8_t *pixels, } void ff_put_pixels_clamped_msa(const int16_t *block, - uint8_t *av_restrict pixels, + uint8_t *restrict pixels, ptrdiff_t line_size) { put_pixels_clamped_msa(block, pixels, line_size); } void ff_put_signed_pixels_clamped_msa(const int16_t *block, - uint8_t *av_restrict pixels, + uint8_t *restrict pixels, ptrdiff_t line_size) { put_signed_pixels_clamped_msa(block, pixels, line_size); } void ff_add_pixels_clamped_msa(const int16_t *block, - uint8_t *av_restrict pixels, + uint8_t *restrict pixels, ptrdiff_t line_size) { add_pixels_clamped_msa(block, pixels, line_size); diff --git a/libavcodec/mips/me_cmp_mips.h b/libavcodec/mips/me_cmp_mips.h index 728640102ad..72b7de70b41 100644 --- a/libavcodec/mips/me_cmp_mips.h +++ b/libavcodec/mips/me_cmp_mips.h @@ -54,7 +54,7 @@ int ff_sse8_msa(MpegEncContext *v, const uint8_t *pu8Src, const uint8_t *pu8Ref, ptrdiff_t stride, int i32Height); int ff_sse4_msa(MpegEncContext *v, const uint8_t *pu8Src, const uint8_t *pu8Ref, ptrdiff_t stride, int i32Height); -void ff_add_pixels8_msa(const uint8_t *av_restrict pixels, int16_t *block, +void ff_add_pixels8_msa(const uint8_t *restrict pixels, int16_t *block, ptrdiff_t stride); #endif // #ifndef AVCODEC_MIPS_ME_CMP_MIPS_H diff --git a/libavcodec/mips/pixblockdsp_mips.h b/libavcodec/mips/pixblockdsp_mips.h index a12b1a6949b..7fd137cd09e 100644 --- a/libavcodec/mips/pixblockdsp_mips.h +++ b/libavcodec/mips/pixblockdsp_mips.h @@ -24,16 +24,16 @@ #include "../mpegvideo.h" -void ff_diff_pixels_msa(int16_t *av_restrict block, const uint8_t *src1, +void ff_diff_pixels_msa(int16_t *restrict block, const uint8_t *src1, const uint8_t *src2, ptrdiff_t stride); void ff_get_pixels_16_msa(int16_t *restrict dst, const uint8_t *src, ptrdiff_t stride); void ff_get_pixels_8_msa(int16_t *restrict dst, const uint8_t *src, ptrdiff_t stride); -void ff_get_pixels_8_mmi(int16_t *av_restrict block, const uint8_t *pixels, +void ff_get_pixels_8_mmi(int16_t *restrict block, const uint8_t *pixels, ptrdiff_t stride); -void ff_diff_pixels_mmi(int16_t *av_restrict block, const uint8_t *src1, +void ff_diff_pixels_mmi(int16_t *restrict block, const uint8_t *src1, const uint8_t *src2, ptrdiff_t stride); #endif // #ifndef AVCODEC_MIPS_PIXBLOCKDSP_MIPS_H diff --git a/libavcodec/mips/pixblockdsp_mmi.c b/libavcodec/mips/pixblockdsp_mmi.c index 1230f5de886..dea85bf3fad 100644 --- a/libavcodec/mips/pixblockdsp_mmi.c +++ b/libavcodec/mips/pixblockdsp_mmi.c @@ -25,7 +25,7 @@ #include "libavutil/mips/asmdefs.h" #include "libavutil/mips/mmiutils.h" -void ff_get_pixels_8_mmi(int16_t *av_restrict block, const uint8_t *pixels, +void ff_get_pixels_8_mmi(int16_t *restrict block, const uint8_t *pixels, ptrdiff_t stride) { double ftmp[7]; @@ -94,7 +94,7 @@ void ff_get_pixels_8_mmi(int16_t *av_restrict block, const uint8_t *pixels, ); } -void ff_diff_pixels_mmi(int16_t *av_restrict block, const uint8_t *src1, +void ff_diff_pixels_mmi(int16_t *restrict block, const uint8_t *src1, const uint8_t *src2, ptrdiff_t stride) { double ftmp[5]; diff --git a/libavcodec/mips/pixblockdsp_msa.c b/libavcodec/mips/pixblockdsp_msa.c index 86a4576c1dd..1af4d8da5e4 100644 --- a/libavcodec/mips/pixblockdsp_msa.c +++ b/libavcodec/mips/pixblockdsp_msa.c @@ -124,19 +124,19 @@ static void copy_width16_msa(const uint8_t *src, int32_t src_stride, } } -void ff_get_pixels_16_msa(int16_t *av_restrict dest, const uint8_t *src, +void ff_get_pixels_16_msa(int16_t *restrict dest, const uint8_t *src, ptrdiff_t stride) { copy_width16_msa(src, stride, (uint8_t *) dest, 16, 8); } -void ff_get_pixels_8_msa(int16_t *av_restrict dest, const uint8_t *src, +void ff_get_pixels_8_msa(int16_t *restrict dest, const uint8_t *src, ptrdiff_t stride) { copy_8bit_to_16bit_width8_msa(src, stride, dest, 8, 8); } -void ff_diff_pixels_msa(int16_t *av_restrict block, const uint8_t *src1, +void ff_diff_pixels_msa(int16_t *restrict block, const uint8_t *src1, const uint8_t *src2, ptrdiff_t stride) { diff_pixels_msa(block, src1, src2, stride); diff --git a/libavcodec/misc4.c b/libavcodec/misc4.c index 1bf162e1202..72ac944e54f 100644 --- a/libavcodec/misc4.c +++ b/libavcodec/misc4.c @@ -179,7 +179,10 @@ const FFCodec ff_misc4_decoder = { .priv_data_size = sizeof(MISC4Context), .init = misc4_init, FF_CODEC_DECODE_CB(misc4_decode), - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_SUBFRAMES | + .p.capabilities = AV_CODEC_CAP_DR1 | +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif AV_CODEC_CAP_CHANNEL_CONF, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index c833d66c4db..c9409eac6c3 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -33,6 +33,7 @@ #include "config_components.h" #include "libavutil/display.h" +#include "libavutil/emms.h" #include "libavutil/imgutils.h" #include "libavutil/avassert.h" #include "libavutil/opt.h" @@ -41,6 +42,7 @@ #include "codec_internal.h" #include "copy_block.h" #include "decode.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "idctdsp.h" #include "internal.h" @@ -50,7 +52,6 @@ #include "jpeglsdec.h" #include "profiles.h" #include "put_bits.h" -#include "tiff.h" #include "exif.h" #include "bytestream.h" #include "tiff_common.h" @@ -81,7 +82,7 @@ static int init_default_huffman_tables(MJpegDecodeContext *s) int i, ret; for (i = 0; i < FF_ARRAY_ELEMS(ht); i++) { - ff_free_vlc(&s->vlcs[ht[i].class][ht[i].index]); + ff_vlc_free(&s->vlcs[ht[i].class][ht[i].index]); ret = ff_mjpeg_build_vlc(&s->vlcs[ht[i].class][ht[i].index], ht[i].bits, ht[i].values, ht[i].class == 1, s->avctx); @@ -275,7 +276,7 @@ int ff_mjpeg_decode_dht(MJpegDecodeContext *s) len -= n; /* build VLC and flush previous vlc if present */ - ff_free_vlc(&s->vlcs[class][index]); + ff_vlc_free(&s->vlcs[class][index]); av_log(s->avctx, AV_LOG_DEBUG, "class=%d index=%d nb_codes=%d\n", class, index, n); if ((ret = ff_mjpeg_build_vlc(&s->vlcs[class][index], bits_table, @@ -283,7 +284,7 @@ int ff_mjpeg_decode_dht(MJpegDecodeContext *s) return ret; if (class > 0) { - ff_free_vlc(&s->vlcs[2][index]); + ff_vlc_free(&s->vlcs[2][index]); if ((ret = ff_mjpeg_build_vlc(&s->vlcs[2][index], bits_table, val_table, 0, s->avctx)) < 0) return ret; @@ -441,8 +442,8 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) s->height < ((s->orig_height * 3) / 4)) { s->interlaced = 1; s->bottom_field = s->interlace_polarity; - s->picture_ptr->interlaced_frame = 1; - s->picture_ptr->top_field_first = !s->interlace_polarity; + s->picture_ptr->flags |= AV_FRAME_FLAG_INTERLACED; + s->picture_ptr->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * !s->interlace_polarity; height *= 2; } @@ -545,6 +546,18 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) } av_assert0(s->nb_components == 4); break; + case 0x11412100: + if (s->bits > 8) + goto unk_pixfmt; + if (s->component_id[0] == 'R' && s->component_id[1] == 'G' && s->component_id[2] == 'B') { + s->avctx->pix_fmt = AV_PIX_FMT_GBRP; + s->upscale_h[0] = 4; + s->upscale_h[1] = 0; + s->upscale_h[2] = 1; + } else { + goto unk_pixfmt; + } + break; case 0x22111122: case 0x22111111: if (s->adobe_transform == 0 && s->bits <= 8) { @@ -571,10 +584,15 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) case 0x22221100: case 0x22112200: case 0x11222200: - if (s->bits <= 8) s->avctx->pix_fmt = s->cs_itu601 ? AV_PIX_FMT_YUV444P : AV_PIX_FMT_YUVJ444P; - else + if (s->bits > 8) goto unk_pixfmt; - s->avctx->color_range = s->cs_itu601 ? AVCOL_RANGE_MPEG : AVCOL_RANGE_JPEG; + if (s->adobe_transform == 0 || s->component_id[0] == 'R' && + s->component_id[1] == 'G' && s->component_id[2] == 'B') { + s->avctx->pix_fmt = AV_PIX_FMT_GBRP; + } else { + s->avctx->pix_fmt = s->cs_itu601 ? AV_PIX_FMT_YUV444P : AV_PIX_FMT_YUVJ444P; + s->avctx->color_range = s->cs_itu601 ? AVCOL_RANGE_MPEG : AVCOL_RANGE_JPEG; + } break; case 0x11000000: case 0x13000000: @@ -599,7 +617,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) if (s->bits <= 8) s->avctx->pix_fmt = AV_PIX_FMT_GBRP; else goto unk_pixfmt; - s->upscale_v[0] = s->upscale_v[1] = 1; + s->upscale_v[1] = s->upscale_v[2] = 1; } else { if (pix_fmt_id == 0x14111100) s->upscale_v[1] = s->upscale_v[2] = 1; @@ -614,13 +632,22 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) if (s->bits <= 8) s->avctx->pix_fmt = AV_PIX_FMT_GBRP; else goto unk_pixfmt; - s->upscale_h[0] = s->upscale_h[1] = 1; + s->upscale_h[1] = s->upscale_h[2] = 1; } else { if (s->bits <= 8) s->avctx->pix_fmt = s->cs_itu601 ? AV_PIX_FMT_YUV422P : AV_PIX_FMT_YUVJ422P; else s->avctx->pix_fmt = AV_PIX_FMT_YUV422P16; s->avctx->color_range = s->cs_itu601 ? AVCOL_RANGE_MPEG : AVCOL_RANGE_JPEG; } break; + case 0x11311100: + if (s->bits > 8) + goto unk_pixfmt; + if (s->component_id[0] == 'R' && s->component_id[1] == 'G' && s->component_id[2] == 'B') + s->avctx->pix_fmt = AV_PIX_FMT_GBRP; + else + goto unk_pixfmt; + s->upscale_h[0] = s->upscale_h[2] = 2; + break; case 0x31111100: if (s->bits > 8) goto unk_pixfmt; @@ -630,6 +657,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) break; case 0x22121100: case 0x22111200: + case 0x41211100: if (s->bits <= 8) s->avctx->pix_fmt = s->cs_itu601 ? AV_PIX_FMT_YUV422P : AV_PIX_FMT_YUVJ422P; else goto unk_pixfmt; @@ -673,10 +701,6 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) avpriv_report_missing_feature(s->avctx, "Lowres for weird subsampling"); return AVERROR_PATCHWELCOME; } - if ((AV_RB32(s->upscale_h) || AV_RB32(s->upscale_v)) && s->progressive && s->avctx->pix_fmt == AV_PIX_FMT_GBRP) { - avpriv_report_missing_feature(s->avctx, "progressive for weird subsampling"); - return AVERROR_PATCHWELCOME; - } if (s->ls) { memset(s->upscale_h, 0, sizeof(s->upscale_h)); memset(s->upscale_v, 0, sizeof(s->upscale_v)); @@ -722,7 +746,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) if (s->avctx->skip_frame == AVDISCARD_ALL) { s->picture_ptr->pict_type = AV_PICTURE_TYPE_I; - s->picture_ptr->key_frame = 1; + s->picture_ptr->flags |= AV_FRAME_FLAG_KEY; s->got_picture = 1; return 0; } @@ -731,7 +755,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) if (ff_get_buffer(s->avctx, s->picture_ptr, AV_GET_BUFFER_FLAG_REF) < 0) return -1; s->picture_ptr->pict_type = AV_PICTURE_TYPE_I; - s->picture_ptr->key_frame = 1; + s->picture_ptr->flags |= AV_FRAME_FLAG_KEY; s->got_picture = 1; // Lets clear the palette to avoid leaving uninitialized values in it @@ -773,13 +797,14 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s) } if (s->avctx->hwaccel) { + const FFHWAccel *hwaccel = ffhwaccel(s->avctx->hwaccel); s->hwaccel_picture_private = - av_mallocz(s->avctx->hwaccel->frame_priv_data_size); + av_mallocz(hwaccel->frame_priv_data_size); if (!s->hwaccel_picture_private) return AVERROR(ENOMEM); - ret = s->avctx->hwaccel->start_frame(s->avctx, s->raw_image_buffer, - s->raw_image_buffer_size); + ret = hwaccel->start_frame(s->avctx, s->raw_image_buffer, + s->raw_image_buffer_size); if (ret < 0) return ret; } @@ -1697,9 +1722,6 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, s->h_scount[i] = s->h_count[index]; s->v_scount[i] = s->v_count[index]; - if((nb_components == 1 || nb_components == 3) && s->nb_components == 3 && s->avctx->pix_fmt == AV_PIX_FMT_GBR24P) - index = (index+2)%3; - s->comp_index[i] = index; s->dc_index[i] = get_bits(&s->gb, 4); @@ -1754,9 +1776,9 @@ int ff_mjpeg_decode_sos(MJpegDecodeContext *s, const uint8_t *mb_bitmask, av_assert0(bytes_to_start >= 0 && s->raw_scan_buffer_size >= bytes_to_start); - ret = s->avctx->hwaccel->decode_slice(s->avctx, - s->raw_scan_buffer + bytes_to_start, - s->raw_scan_buffer_size - bytes_to_start); + ret = FF_HW_CALL(s->avctx, decode_slice, + s->raw_scan_buffer + bytes_to_start, + s->raw_scan_buffer_size - bytes_to_start); if (ret < 0) return ret; @@ -2399,7 +2421,7 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, } s->start_code = start_code; - if (s->avctx->debug & FF_DEBUG_STARTCODE) + if (avctx->debug & FF_DEBUG_STARTCODE) av_log(avctx, AV_LOG_DEBUG, "startcode: %X\n", start_code); /* process markers */ @@ -2463,9 +2485,9 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, case SOF0: case SOF1: if (start_code == SOF0) - s->avctx->profile = FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT; + avctx->profile = AV_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT; else - s->avctx->profile = FF_PROFILE_MJPEG_HUFFMAN_EXTENDED_SEQUENTIAL_DCT; + avctx->profile = AV_PROFILE_MJPEG_HUFFMAN_EXTENDED_SEQUENTIAL_DCT; s->lossless = 0; s->ls = 0; s->progressive = 0; @@ -2473,7 +2495,7 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, goto fail; break; case SOF2: - s->avctx->profile = FF_PROFILE_MJPEG_HUFFMAN_PROGRESSIVE_DCT; + avctx->profile = AV_PROFILE_MJPEG_HUFFMAN_PROGRESSIVE_DCT; s->lossless = 0; s->ls = 0; s->progressive = 1; @@ -2481,8 +2503,8 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, goto fail; break; case SOF3: - s->avctx->profile = FF_PROFILE_MJPEG_HUFFMAN_LOSSLESS; - s->avctx->properties |= FF_CODEC_PROPERTY_LOSSLESS; + avctx->profile = AV_PROFILE_MJPEG_HUFFMAN_LOSSLESS; + avctx->properties |= FF_CODEC_PROPERTY_LOSSLESS; s->lossless = 1; s->ls = 0; s->progressive = 0; @@ -2490,8 +2512,8 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, goto fail; break; case SOF48: - s->avctx->profile = FF_PROFILE_MJPEG_JPEG_LS; - s->avctx->properties |= FF_CODEC_PROPERTY_LOSSLESS; + avctx->profile = AV_PROFILE_MJPEG_JPEG_LS; + avctx->properties |= FF_CODEC_PROPERTY_LOSSLESS; s->lossless = 1; s->ls = 1; s->progressive = 0; @@ -2526,8 +2548,8 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, s->got_picture = 0; goto the_end_no_picture; } - if (s->avctx->hwaccel) { - ret = s->avctx->hwaccel->end_frame(s->avctx); + if (avctx->hwaccel) { + ret = FF_HW_SIMPLE_CALL(avctx, end_frame); if (ret < 0) return ret; @@ -2598,7 +2620,7 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, return ret; the_end: - is16bit = av_pix_fmt_desc_get(s->avctx->pix_fmt)->comp[0].step > 1; + is16bit = av_pix_fmt_desc_get(avctx->pix_fmt)->comp[0].step > 1; if (AV_RB32(s->upscale_h)) { int p; @@ -2607,6 +2629,8 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, avctx->pix_fmt == AV_PIX_FMT_YUVJ440P || avctx->pix_fmt == AV_PIX_FMT_YUV440P || avctx->pix_fmt == AV_PIX_FMT_YUVA444P || + avctx->pix_fmt == AV_PIX_FMT_YUVJ422P || + avctx->pix_fmt == AV_PIX_FMT_YUV422P || avctx->pix_fmt == AV_PIX_FMT_YUVJ420P || avctx->pix_fmt == AV_PIX_FMT_YUV420P || avctx->pix_fmt == AV_PIX_FMT_YUV420P16|| @@ -2615,7 +2639,7 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, avctx->pix_fmt == AV_PIX_FMT_GBRP || avctx->pix_fmt == AV_PIX_FMT_GBRAP ); - ret = av_pix_fmt_get_chroma_sub_sample(s->avctx->pix_fmt, &hshift, &vshift); + ret = av_pix_fmt_get_chroma_sub_sample(avctx->pix_fmt, &hshift, &vshift); if (ret) return ret; @@ -2656,6 +2680,24 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, for (index = w - 3; index > 0; index--) { line[index] = (line[index / 3] + line[(index + 1) / 3] + line[(index + 2) / 3] + 1) / 3; } + } else if (s->upscale_h[p] == 4){ + if (is16bit) { + uint16_t *line16 = (uint16_t *) line; + line16[w - 1] = line16[(w - 1) >> 2]; + if (w > 1) + line16[w - 2] = (line16[(w - 1) >> 2] * 3 + line16[(w - 2) >> 2]) >> 2; + if (w > 2) + line16[w - 3] = (line16[(w - 1) >> 2] + line16[(w - 2) >> 2]) >> 1; + } else { + line[w - 1] = line[(w - 1) >> 2]; + if (w > 1) + line[w - 2] = (line[(w - 1) >> 2] * 3 + line[(w - 2) >> 2]) >> 2; + if (w > 2) + line[w - 3] = (line[(w - 1) >> 2] + line[(w - 2) >> 2]) >> 1; + } + for (index = w - 4; index > 0; index--) + line[index] = (line[(index + 3) >> 2] + line[(index + 2) >> 2] + + line[(index + 1) >> 2] + line[index >> 2]) >> 2; } line += s->linesize[p]; } @@ -2677,7 +2719,7 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, avctx->pix_fmt == AV_PIX_FMT_GBRP || avctx->pix_fmt == AV_PIX_FMT_GBRAP ); - ret = av_pix_fmt_get_chroma_sub_sample(s->avctx->pix_fmt, &hshift, &vshift); + ret = av_pix_fmt_get_chroma_sub_sample(avctx->pix_fmt, &hshift, &vshift); if (ret) return ret; @@ -2707,7 +2749,7 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, } } if (s->flipped && !s->rgb) { - ret = av_pix_fmt_get_chroma_sub_sample(s->avctx->pix_fmt, &hshift, &vshift); + ret = av_pix_fmt_get_chroma_sub_sample(avctx->pix_fmt, &hshift, &vshift); if (ret) return ret; @@ -2722,7 +2764,16 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, } } } - if (s->adobe_transform == 0 && s->avctx->pix_fmt == AV_PIX_FMT_GBRAP) { + + if (avctx->pix_fmt == AV_PIX_FMT_GBRP) { + av_assert0(s->nb_components == 3); + FFSWAP(uint8_t *, frame->data[0], frame->data[2]); + FFSWAP(uint8_t *, frame->data[0], frame->data[1]); + FFSWAP(int, frame->linesize[0], frame->linesize[2]); + FFSWAP(int, frame->linesize[0], frame->linesize[1]); + } + + if (s->adobe_transform == 0 && avctx->pix_fmt == AV_PIX_FMT_GBRAP) { int w = s->picture_ptr->width; int h = s->picture_ptr->height; av_assert0(s->nb_components == 4); @@ -2741,11 +2792,11 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, dst[0][j] = g*257 >> 16; dst[1][j] = b*257 >> 16; dst[2][j] = r*257 >> 16; - dst[3][j] = 255; } + memset(dst[3], 255, w); } } - if (s->adobe_transform == 2 && s->avctx->pix_fmt == AV_PIX_FMT_YUVA444P) { + if (s->adobe_transform == 2 && avctx->pix_fmt == AV_PIX_FMT_YUVA444P) { int w = s->picture_ptr->width; int h = s->picture_ptr->height; av_assert0(s->nb_components == 4); @@ -2764,8 +2815,8 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, dst[0][j] = r*257 >> 16; dst[1][j] = (g*257 >> 16) + 128; dst[2][j] = (b*257 >> 16) + 128; - dst[3][j] = 255; } + memset(dst[3], 255, w); } } @@ -2788,16 +2839,18 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, for (i = 0; i < s->iccnum; i++) total_size += s->iccentries[i].length; - sd = av_frame_new_side_data(frame, AV_FRAME_DATA_ICC_PROFILE, total_size); - if (!sd) { - av_log(s->avctx, AV_LOG_ERROR, "Could not allocate frame side data\n"); - return AVERROR(ENOMEM); + ret = ff_frame_new_side_data(avctx, frame, AV_FRAME_DATA_ICC_PROFILE, total_size, &sd); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Could not allocate frame side data\n"); + return ret; } - /* Reassemble the parts, which are now in-order. */ - for (i = 0; i < s->iccnum; i++) { - memcpy(sd->data + offset, s->iccentries[i].data, s->iccentries[i].length); - offset += s->iccentries[i].length; + if (sd) { + /* Reassemble the parts, which are now in-order. */ + for (i = 0; i < s->iccnum; i++) { + memcpy(sd->data + offset, s->iccentries[i].data, s->iccentries[i].length); + offset += s->iccentries[i].length; + } } } @@ -2813,7 +2866,7 @@ int ff_mjpeg_decode_frame_from_buf(AVCodecContext *avctx, AVFrame *frame, sd = av_frame_new_side_data(frame, AV_FRAME_DATA_DISPLAYMATRIX, sizeof(int32_t) * 9); if (!sd) { - av_log(s->avctx, AV_LOG_ERROR, "Could not allocate frame side data\n"); + av_log(avctx, AV_LOG_ERROR, "Could not allocate frame side data\n"); return AVERROR(ENOMEM); } @@ -2903,7 +2956,7 @@ av_cold int ff_mjpeg_decode_end(AVCodecContext *avctx) for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) - ff_free_vlc(&s->vlcs[i][j]); + ff_vlc_free(&s->vlcs[i][j]); } for (i = 0; i < MAX_COMPONENTS; i++) { av_freep(&s->blocks[i]); @@ -3003,6 +3056,8 @@ static void smv_process_frame(AVCodecContext *avctx, AVFrame *frame) frame->crop_top = FFMIN(s->smv_next_frame * avctx->height, frame->height); frame->crop_bottom = frame->height - (s->smv_next_frame + 1) * avctx->height; + if (s->smv_frame->pts != AV_NOPTS_VALUE) + s->smv_frame->pts += s->smv_frame->duration; s->smv_next_frame = (s->smv_next_frame + 1) % s->smv_frames_per_jpeg; if (s->smv_next_frame == 0) @@ -3013,26 +3068,20 @@ static int smvjpeg_receive_frame(AVCodecContext *avctx, AVFrame *frame) { MJpegDecodeContext *s = avctx->priv_data; AVPacket *const pkt = avctx->internal->in_pkt; - int64_t pkt_dts; int got_frame = 0; int ret; - if (s->smv_next_frame > 0) { - av_assert0(s->smv_frame->buf[0]); - ret = av_frame_ref(frame, s->smv_frame); - if (ret < 0) - return ret; - - smv_process_frame(avctx, frame); - return 0; - } + if (s->smv_next_frame > 0) + goto return_frame; ret = ff_decode_get_packet(avctx, pkt); if (ret < 0) return ret; - ret = ff_mjpeg_decode_frame(avctx, frame, &got_frame, pkt); - pkt_dts = pkt->dts; + av_frame_unref(s->smv_frame); + + ret = ff_mjpeg_decode_frame(avctx, s->smv_frame, &got_frame, pkt); + s->smv_frame->pkt_dts = pkt->dts; av_packet_unref(pkt); if (ret < 0) return ret; @@ -3040,11 +3089,12 @@ static int smvjpeg_receive_frame(AVCodecContext *avctx, AVFrame *frame) if (!got_frame) return AVERROR(EAGAIN); - frame->pkt_dts = pkt_dts; + // packet duration covers all the frames in the packet + s->smv_frame->duration /= s->smv_frames_per_jpeg; - av_assert0(frame->buf[0]); - av_frame_unref(s->smv_frame); - ret = av_frame_ref(s->smv_frame, frame); +return_frame: + av_assert0(s->smv_frame->buf[0]); + ret = av_frame_ref(frame, s->smv_frame); if (ret < 0) return ret; @@ -3064,6 +3114,6 @@ const FFCodec ff_smvjpeg_decoder = { .flush = decode_flush, .p.capabilities = AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_EXPORTS_CROPPING | - FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_CLEANUP, + FF_CODEC_CAP_INIT_CLEANUP, }; #endif diff --git a/libavcodec/mjpegdec_common.c b/libavcodec/mjpegdec_common.c index 701ddfec06c..ef2a0ad5085 100644 --- a/libavcodec/mjpegdec_common.c +++ b/libavcodec/mjpegdec_common.c @@ -52,6 +52,6 @@ int ff_mjpeg_build_vlc(VLC *vlc, const uint8_t *bits_table, huff_sym[i] = 16 * 256; } - return ff_init_vlc_from_lengths(vlc, 9, nb_codes, huff_size, 1, + return ff_vlc_init_from_lengths(vlc, 9, nb_codes, huff_size, 1, huff_sym, 2, 2, 0, 0, logctx); } diff --git a/libavcodec/mjpegenc.c b/libavcodec/mjpegenc.c index 508772987f3..521c9e8affd 100644 --- a/libavcodec/mjpegenc.c +++ b/libavcodec/mjpegenc.c @@ -627,9 +627,9 @@ static int amv_encode_picture(AVCodecContext *avctx, AVPacket *pkt, #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { FF_MPV_COMMON_OPTS -{ "huffman", "Huffman table strategy", OFFSET(huffman), AV_OPT_TYPE_INT, { .i64 = HUFFMAN_TABLE_OPTIMAL }, 0, NB_HUFFMAN_TABLE_OPTION - 1, VE, "huffman" }, - { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = HUFFMAN_TABLE_DEFAULT }, INT_MIN, INT_MAX, VE, "huffman" }, - { "optimal", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = HUFFMAN_TABLE_OPTIMAL }, INT_MIN, INT_MAX, VE, "huffman" }, +{ "huffman", "Huffman table strategy", OFFSET(huffman), AV_OPT_TYPE_INT, { .i64 = HUFFMAN_TABLE_OPTIMAL }, 0, NB_HUFFMAN_TABLE_OPTION - 1, VE, .unit = "huffman" }, + { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = HUFFMAN_TABLE_DEFAULT }, INT_MIN, INT_MAX, VE, .unit = "huffman" }, + { "optimal", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = HUFFMAN_TABLE_OPTIMAL }, INT_MIN, INT_MAX, VE, .unit = "huffman" }, { "force_duplicated_matrix", "Always write luma and chroma matrix for mjpeg, useful for rtp streaming.", OFFSET(force_duplicated_matrix), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, VE }, { NULL}, }; diff --git a/libavcodec/mlp.c b/libavcodec/mlp.c index 3f54b2e4480..33045c08c77 100644 --- a/libavcodec/mlp.c +++ b/libavcodec/mlp.c @@ -57,15 +57,6 @@ const ChannelInformation ff_mlp_ch_info[21] = { { 0x3F, 0x04, 0x02, 0x00 }, }; -#if FF_API_OLD_CHANNEL_LAYOUT -const uint64_t ff_mlp_channel_layouts[12] = { - AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO, AV_CH_LAYOUT_2_1, - AV_CH_LAYOUT_QUAD, AV_CH_LAYOUT_2POINT1, AV_CH_LAYOUT_SURROUND, - AV_CH_LAYOUT_4POINT0, AV_CH_LAYOUT_5POINT0_BACK, AV_CH_LAYOUT_3POINT1, - AV_CH_LAYOUT_4POINT1, AV_CH_LAYOUT_5POINT1_BACK, 0, -}; -#endif - const AVChannelLayout ff_mlp_ch_layouts[12] = { AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, AV_CHANNEL_LAYOUT_2_1, AV_CHANNEL_LAYOUT_QUAD, AV_CHANNEL_LAYOUT_2POINT1, AV_CHANNEL_LAYOUT_SURROUND, diff --git a/libavcodec/mlp.h b/libavcodec/mlp.h index 8a3e4566a3c..bec414c680b 100644 --- a/libavcodec/mlp.h +++ b/libavcodec/mlp.h @@ -70,6 +70,15 @@ /** Code that signals end of a stream. */ #define END_OF_STREAM 0xd234d234 +#define PARAM_BLOCKSIZE (1 << 7) +#define PARAM_MATRIX (1 << 6) +#define PARAM_OUTSHIFT (1 << 5) +#define PARAM_QUANTSTEP (1 << 4) +#define PARAM_FIR (1 << 3) +#define PARAM_IIR (1 << 2) +#define PARAM_HUFFOFFSET (1 << 1) +#define PARAM_PRESENCE (1 << 0) + #define FIR 0 #define IIR 1 @@ -137,9 +146,6 @@ typedef struct { */ extern const ChannelInformation ff_mlp_ch_info[21]; -#if FF_API_OLD_CHANNEL_LAYOUT -extern const uint64_t ff_mlp_channel_layouts[12]; -#endif extern const AVChannelLayout ff_mlp_ch_layouts[12]; /** MLP uses checksums that seem to be based on the standard CRC algorithm, but diff --git a/libavcodec/mlp_parse.h b/libavcodec/mlp_parse.h index fa6e3d52dcc..5f1f953cfee 100644 --- a/libavcodec/mlp_parse.h +++ b/libavcodec/mlp_parse.h @@ -113,6 +113,18 @@ static inline uint64_t truehd_layout(int chanmap) return layout; } +static inline int layout_truehd(uint64_t layout) +{ + int chanmap = 0; + + for (int i = 0; i < 13; i++) { + if ((layout & thd_layout[i]) == thd_layout[i]) + chanmap |= 1 << i; + } + + return chanmap; +} + int ff_mlp_read_major_sync(void *log, MLPHeaderInfo *mh, GetBitContext *gb); #endif /* AVCODEC_MLP_PARSE_H */ diff --git a/libavcodec/mlpdec.c b/libavcodec/mlpdec.c index 0ee1f0982c6..305c5d2b368 100644 --- a/libavcodec/mlpdec.c +++ b/libavcodec/mlpdec.c @@ -42,6 +42,7 @@ #include "mlpdsp.h" #include "mlp.h" #include "config.h" +#include "profiles.h" /** number of bits used for VLC lookup - longest Huffman code is 9 */ #if ARCH_ARM @@ -92,14 +93,6 @@ typedef struct SubStream { /// Bitmask of which parameter sets are conveyed in a decoding parameter block. uint8_t param_presence_flags; -#define PARAM_BLOCKSIZE (1 << 7) -#define PARAM_MATRIX (1 << 6) -#define PARAM_OUTSHIFT (1 << 5) -#define PARAM_QUANTSTEP (1 << 4) -#define PARAM_FIR (1 << 3) -#define PARAM_IIR (1 << 2) -#define PARAM_HUFFOFFSET (1 << 1) -#define PARAM_PRESENCE (1 << 0) //@} //@{ @@ -180,6 +173,14 @@ typedef struct MLPDecodeContext { DECLARE_ALIGNED(32, int32_t, sample_buffer)[MAX_BLOCKSIZE][MAX_CHANNELS]; MLPDSPContext dsp; + int32_t (*pack_output)(int32_t lossless_check_data, + uint16_t blockpos, + int32_t (*sample_buffer)[MAX_CHANNELS], + void *data, + uint8_t *ch_assign, + int8_t *output_shift, + uint8_t max_matrix_channel, + int is32); } MLPDecodeContext; static const enum AVChannel thd_channel_order[] = { @@ -229,9 +230,9 @@ static av_cold void init_static(void) static VLCElem vlc_buf[3 * VLC_STATIC_SIZE]; huff_vlc[i].table = &vlc_buf[i * VLC_STATIC_SIZE]; huff_vlc[i].table_allocated = VLC_STATIC_SIZE; - init_vlc(&huff_vlc[i], VLC_BITS, 18, + vlc_init(&huff_vlc[i], VLC_BITS, 18, &ff_mlp_huffman_tables[i][0][1], 2, 1, - &ff_mlp_huffman_tables[i][0][0], 2, 1, INIT_VLC_USE_NEW_STATIC); + &ff_mlp_huffman_tables[i][0][0], 2, 1, VLC_INIT_USE_STATIC); } ff_mlp_init_crc(); @@ -305,14 +306,22 @@ static av_cold int mlp_decode_init(AVCodecContext *avctx) m->substream[substr].lossless_check_data = 0xffffffff; ff_mlpdsp_init(&m->dsp); -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (avctx->request_channel_layout) { - av_channel_layout_uninit(&m->downmix_layout); - av_channel_layout_from_mask(&m->downmix_layout, avctx->request_channel_layout); + if (m->downmix_layout.nb_channels) { + if (!av_channel_layout_compare(&m->downmix_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO) || + !av_channel_layout_compare(&m->downmix_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO_DOWNMIX)) { + av_channel_layout_uninit(&avctx->ch_layout); + avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO; + } else if (!av_channel_layout_compare(&m->downmix_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT0)) { + av_channel_layout_uninit(&avctx->ch_layout); + avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT0; + } else if (!av_channel_layout_compare(&m->downmix_layout, &(AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1)) { + av_channel_layout_uninit(&avctx->ch_layout); + avctx->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_5POINT1; + } + else + av_log(avctx, AV_LOG_WARNING, "Invalid downmix layout\n"); } -FF_ENABLE_DEPRECATION_WARNINGS -#endif + ff_thread_once(&init_static_once, init_static); return 0; @@ -390,8 +399,17 @@ static int read_major_sync(MLPDecodeContext *m, GetBitContext *gb) m->access_unit_size_pow2 = mh.access_unit_size_pow2; m->num_substreams = mh.num_substreams; + m->extended_substream_info = mh.extended_substream_info; m->substream_info = mh.substream_info; + /* If there is a 4th substream and the MSB of substream_info is set, + * there is a 16-channel spatial presentation (Atmos in TrueHD). + */ + if (m->avctx->codec_id == AV_CODEC_ID_TRUEHD + && m->num_substreams == 4 && m->substream_info >> 7 == 1) { + m->avctx->profile = AV_PROFILE_TRUEHD_ATMOS; + } + /* limit to decoding 3 substreams, as the 4th is used by Dolby Atmos for non-audio data */ m->max_decoded_substream = FFMIN(m->num_substreams - 1, 2); @@ -403,10 +421,10 @@ static int read_major_sync(MLPDecodeContext *m, GetBitContext *gb) m->avctx->sample_fmt = AV_SAMPLE_FMT_S32; else m->avctx->sample_fmt = AV_SAMPLE_FMT_S16; - m->dsp.mlp_pack_output = m->dsp.mlp_select_pack_output(m->substream[m->max_decoded_substream].ch_assign, - m->substream[m->max_decoded_substream].output_shift, - m->substream[m->max_decoded_substream].max_matrix_channel, - m->avctx->sample_fmt == AV_SAMPLE_FMT_S32); + m->pack_output = m->dsp.mlp_select_pack_output(m->substream[m->max_decoded_substream].ch_assign, + m->substream[m->max_decoded_substream].output_shift, + m->substream[m->max_decoded_substream].max_matrix_channel, + m->avctx->sample_fmt == AV_SAMPLE_FMT_S32); m->params_valid = 1; for (substr = 0; substr < MAX_SUBSTREAMS; substr++) @@ -450,7 +468,10 @@ static int read_major_sync(MLPDecodeContext *m, GetBitContext *gb) else m->substream[2].mask = mh.channel_layout_thd_stream1; if (m->avctx->ch_layout.nb_channels > 2) - m->substream[mh.num_substreams > 1].mask = mh.channel_layout_thd_stream1; + if (mh.num_substreams > 2) + m->substream[1].mask = mh.channel_layout_thd_stream1; + else + m->substream[mh.num_substreams > 1].mask = mh.channel_layout_thd_stream2; } m->needs_reordering = mh.channel_arrangement >= 18 && mh.channel_arrangement <= 20; @@ -641,10 +662,10 @@ static int read_restart_header(MLPDecodeContext *m, GetBitContext *gbp, if (substr == m->max_decoded_substream) { av_channel_layout_uninit(&m->avctx->ch_layout); av_channel_layout_from_mask(&m->avctx->ch_layout, s->mask); - m->dsp.mlp_pack_output = m->dsp.mlp_select_pack_output(s->ch_assign, - s->output_shift, - s->max_matrix_channel, - m->avctx->sample_fmt == AV_SAMPLE_FMT_S32); + m->pack_output = m->dsp.mlp_select_pack_output(s->ch_assign, + s->output_shift, + s->max_matrix_channel, + m->avctx->sample_fmt == AV_SAMPLE_FMT_S32); if (m->avctx->codec_id == AV_CODEC_ID_MLP && m->needs_reordering) { if (s->mask == (AV_CH_LAYOUT_QUAD|AV_CH_LOW_FREQUENCY) || @@ -903,10 +924,10 @@ static int read_decoding_params(MLPDecodeContext *m, GetBitContext *gbp, } } if (substr == m->max_decoded_substream) - m->dsp.mlp_pack_output = m->dsp.mlp_select_pack_output(s->ch_assign, - s->output_shift, - s->max_matrix_channel, - m->avctx->sample_fmt == AV_SAMPLE_FMT_S32); + m->pack_output = m->dsp.mlp_select_pack_output(s->ch_assign, + s->output_shift, + s->max_matrix_channel, + m->avctx->sample_fmt == AV_SAMPLE_FMT_S32); } if (s->param_presence_flags & PARAM_QUANTSTEP) @@ -1133,14 +1154,14 @@ static int output_data(MLPDecodeContext *m, unsigned int substr, frame->nb_samples = s->blockpos; if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) return ret; - s->lossless_check_data = m->dsp.mlp_pack_output(s->lossless_check_data, - s->blockpos, - m->sample_buffer, - frame->data[0], - s->ch_assign, - s->output_shift, - s->max_matrix_channel, - is32); + s->lossless_check_data = m->pack_output(s->lossless_check_data, + s->blockpos, + m->sample_buffer, + frame->data[0], + s->ch_assign, + s->output_shift, + s->max_matrix_channel, + is32); /* Update matrix encoding side data */ if (s->matrix_encoding != s->prev_matrix_encoding) { @@ -1452,5 +1473,6 @@ const FFCodec ff_truehd_decoder = { FF_CODEC_DECODE_CB(read_access_unit), .flush = mlp_decode_flush, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, + .p.profiles = NULL_IF_CONFIG_SMALL(ff_truehd_profiles), }; #endif /* CONFIG_TRUEHD_DECODER */ diff --git a/libavcodec/mlpdsp.c b/libavcodec/mlpdsp.c index eaf5de8e1a6..cb40160f67c 100644 --- a/libavcodec/mlpdsp.c +++ b/libavcodec/mlpdsp.c @@ -130,7 +130,6 @@ av_cold void ff_mlpdsp_init(MLPDSPContext *c) c->mlp_filter_channel = mlp_filter_channel; c->mlp_rematrix_channel = ff_mlp_rematrix_channel; c->mlp_select_pack_output = mlp_select_pack_output; - c->mlp_pack_output = ff_mlp_pack_output; #if ARCH_ARM ff_mlpdsp_init_arm(c); #elif ARCH_X86 diff --git a/libavcodec/mlpdsp.h b/libavcodec/mlpdsp.h index a0edeb77629..7a9ac228d31 100644 --- a/libavcodec/mlpdsp.h +++ b/libavcodec/mlpdsp.h @@ -66,14 +66,6 @@ typedef struct MLPDSPContext { int8_t *output_shift, uint8_t max_matrix_channel, int is32))(int32_t, uint16_t, int32_t (*)[], void *, uint8_t*, int8_t *, uint8_t, int); - int32_t (*mlp_pack_output)(int32_t lossless_check_data, - uint16_t blockpos, - int32_t (*sample_buffer)[MAX_CHANNELS], - void *data, - uint8_t *ch_assign, - int8_t *output_shift, - uint8_t max_matrix_channel, - int is32); } MLPDSPContext; void ff_mlpdsp_init(MLPDSPContext *c); diff --git a/libavcodec/mlpenc.c b/libavcodec/mlpenc.c index 5995a6b51c6..06670de456e 100644 --- a/libavcodec/mlpenc.c +++ b/libavcodec/mlpenc.c @@ -32,27 +32,33 @@ #include "libavutil/crc.h" #include "libavutil/avstring.h" #include "libavutil/intmath.h" +#include "libavutil/opt.h" #include "libavutil/samplefmt.h" #include "libavutil/thread.h" +#include "mlp_parse.h" #include "mlp.h" #include "lpc.h" -#define MAJOR_HEADER_INTERVAL 16 +#define MAX_NCHANNELS (MAX_CHANNELS + 2) + +#define MIN_HEADER_INTERVAL 8 +#define MAX_HEADER_INTERVAL 128 #define MLP_MIN_LPC_ORDER 1 #define MLP_MAX_LPC_ORDER 8 -#define MLP_MIN_LPC_SHIFT 8 +#define MLP_MIN_LPC_SHIFT 0 #define MLP_MAX_LPC_SHIFT 15 -typedef struct { +typedef struct RestartHeader { uint8_t min_channel; ///< The index of the first channel coded in this substream. uint8_t max_channel; ///< The index of the last channel coded in this substream. uint8_t max_matrix_channel; ///< The number of channels input into the rematrix stage. + int8_t max_shift; uint8_t noise_shift; ///< The left shift applied to random noise in 0x31ea substreams. uint32_t noisegen_seed; ///< The current seed value for the pseudorandom noise generator(s). - int data_check_present; ///< Set if the substream contains extra info to check the size of VLC blocks. + uint8_t data_check_present; ///< Set if the substream contains extra info to check the size of VLC blocks. int32_t lossless_check_data; ///< XOR of all output samples @@ -60,43 +66,38 @@ typedef struct { uint8_t max_output_bits; ///< largest output bit-depth } RestartHeader; -typedef struct { +typedef struct MatrixParams { uint8_t count; ///< number of matrices to apply uint8_t outch[MAX_MATRICES]; ///< output channel for each matrix - int32_t forco[MAX_MATRICES][MAX_CHANNELS+2]; ///< forward coefficients - int32_t coeff[MAX_MATRICES][MAX_CHANNELS+2]; ///< decoding coefficients - uint8_t fbits[MAX_CHANNELS]; ///< fraction bits + int32_t forco[MAX_MATRICES][MAX_NCHANNELS]; ///< forward coefficients + int32_t coeff[MAX_MATRICES][MAX_NCHANNELS]; ///< decoding coefficients + uint8_t fbits[MAX_MATRICES]; ///< fraction bits - int8_t shift[MAX_CHANNELS]; ///< Left shift to apply to decoded PCM values to get final 24-bit output. + int8_t noise_shift[MAX_CHANNELS]; + uint8_t lsb_bypass[MAX_MATRICES]; + int8_t bypassed_lsbs[MAX_MATRICES][MAX_BLOCKSIZE]; } MatrixParams; -enum ParamFlags { - PARAMS_DEFAULT = 0xff, - PARAM_PRESENCE_FLAGS = 1 << 8, - PARAM_BLOCKSIZE = 1 << 7, - PARAM_MATRIX = 1 << 6, - PARAM_OUTSHIFT = 1 << 5, - PARAM_QUANTSTEP = 1 << 4, - PARAM_FIR = 1 << 3, - PARAM_IIR = 1 << 2, - PARAM_HUFFOFFSET = 1 << 1, - PARAM_PRESENT = 1 << 0, -}; +#define PARAMS_DEFAULT (0xff) +#define PARAM_PRESENCE_FLAGS (1 << 8) -typedef struct { +typedef struct DecodingParams { uint16_t blocksize; ///< number of PCM samples in current audio block uint8_t quant_step_size[MAX_CHANNELS]; ///< left shift to apply to Huffman-decoded residuals + int8_t output_shift[MAX_CHANNELS]; ///< Left shift to apply to decoded PCM values to get final 24-bit output. + uint8_t max_order[MAX_CHANNELS]; MatrixParams matrix_params; uint8_t param_presence_flags; ///< Bitmask of which parameter sets are conveyed in a decoding parameter block. + int32_t sample_buffer[MAX_NCHANNELS][MAX_BLOCKSIZE]; } DecodingParams; typedef struct BestOffset { int32_t offset; - int bitcount; - int lsb_bits; + uint32_t bitcount; + uint8_t lsb_bits; int32_t min; int32_t max; } BestOffset; @@ -107,9 +108,42 @@ typedef struct BestOffset { /** Number of possible codebooks (counting "no codebooks") */ #define NUM_CODEBOOKS 4 +typedef struct MLPBlock { + unsigned int seq_size; + ChannelParams channel_params[MAX_CHANNELS]; + DecodingParams decoding_params; + int32_t lossless_check_data; + unsigned int max_output_bits; ///< largest output bit-depth + BestOffset best_offset[MAX_CHANNELS][NUM_CODEBOOKS]; + ChannelParams major_channel_params[MAX_CHANNELS]; ///< ChannelParams to be written to bitstream. + DecodingParams major_decoding_params; ///< DecodingParams to be written to bitstream. + int major_params_changed; ///< params_changed to be written to bitstream. + int32_t inout_buffer[MAX_NCHANNELS][MAX_BLOCKSIZE]; +} MLPBlock; + +typedef struct MLPSubstream { + RestartHeader restart_header; + RestartHeader *cur_restart_header; + MLPBlock b[MAX_HEADER_INTERVAL + 1]; + unsigned int major_cur_subblock_index; + unsigned int major_filter_state_subblock; + int32_t coefs[MAX_CHANNELS][MAX_LPC_ORDER][MAX_LPC_ORDER]; +} MLPSubstream; + typedef struct MLPEncodeContext { + AVClass *class; AVCodecContext *avctx; + int max_restart_interval; ///< Max interval of access units in between two major frames. + int min_restart_interval; ///< Min interval of access units in between two major frames. + int cur_restart_interval; + int lpc_coeff_precision; + int rematrix_precision; + int lpc_type; + int lpc_passes; + int prediction_order; + int max_codebook_search; + int num_substreams; ///< Number of substreams contained within this stream. int num_channels; /**< Number of channels in major_scratch_buffer. @@ -129,82 +163,40 @@ typedef struct MLPEncodeContext { int channel_occupancy; int summary_info; - int32_t *inout_buffer; ///< Pointer to data currently being read from lavc or written to bitstream. - int32_t *major_inout_buffer; ///< Buffer with all in/out data for one entire major frame interval. - int32_t *write_buffer; ///< Pointer to data currently being written to bitstream. - int32_t *sample_buffer; ///< Pointer to current access unit samples. - int32_t *major_scratch_buffer; ///< Scratch buffer big enough to fit all data for one entire major frame interval. int32_t last_frames; ///< Signal last frames. - int32_t *lpc_sample_buffer; - unsigned int major_number_of_frames; unsigned int next_major_number_of_frames; unsigned int major_frame_size; ///< Number of samples in current major frame being encoded. unsigned int next_major_frame_size; ///< Counter of number of samples for next major frame. - int32_t *lossless_check_data; ///< Array with lossless_check_data for each access unit. - - unsigned int *max_output_bits; ///< largest output bit-depth unsigned int frame_index; ///< Index of current frame being encoded. - unsigned int one_sample_buffer_size; ///< Number of samples*channel for one access unit. - - unsigned int max_restart_interval; ///< Max interval of access units in between two major frames. - unsigned int min_restart_interval; ///< Min interval of access units in between two major frames. unsigned int restart_intervals; ///< Number of possible major frame sizes. uint16_t output_timing; ///< Timestamp of current access unit. uint16_t input_timing; ///< Decoding timestamp of current access unit. + uint8_t noise_type; uint8_t channel_arrangement; ///< channel arrangement for MLP streams + uint16_t channel_arrangement8; ///< 8 channel arrangement for THD streams - uint8_t ch_modifier_thd0; ///< channel modifier for TrueHD stream 0 - uint8_t ch_modifier_thd1; ///< channel modifier for TrueHD stream 1 - uint8_t ch_modifier_thd2; ///< channel modifier for TrueHD stream 2 - - unsigned int seq_size [MAJOR_HEADER_INTERVAL]; - unsigned int seq_offset[MAJOR_HEADER_INTERVAL]; - unsigned int sequence_size; - - ChannelParams *channel_params; + uint8_t multichannel_type6ch; ///< channel modifier for TrueHD stream 0 + uint8_t multichannel_type8ch; ///< channel modifier for TrueHD stream 0 + uint8_t ch2_presentation_mod; ///< channel modifier for TrueHD stream 0 + uint8_t ch6_presentation_mod; ///< channel modifier for TrueHD stream 1 + uint8_t ch8_presentation_mod; ///< channel modifier for TrueHD stream 2 - BestOffset best_offset[MAJOR_HEADER_INTERVAL+1][MAX_CHANNELS][NUM_CODEBOOKS]; - - DecodingParams *decoding_params; - RestartHeader restart_header; - - ChannelParams major_channel_params[MAJOR_HEADER_INTERVAL+1][MAX_CHANNELS]; ///< ChannelParams to be written to bitstream. - DecodingParams major_decoding_params[MAJOR_HEADER_INTERVAL+1]; ///< DecodingParams to be written to bitstream. - int major_params_changed[MAJOR_HEADER_INTERVAL+1]; ///< params_changed to be written to bitstream. - - unsigned int major_cur_subblock_index; - unsigned int major_filter_state_subblock; - unsigned int major_number_of_subblocks; - - BestOffset (*cur_best_offset)[NUM_CODEBOOKS]; - ChannelParams *cur_channel_params; - DecodingParams *cur_decoding_params; - RestartHeader *cur_restart_header; + MLPSubstream s[2]; + int32_t filter_state[NUM_FILTERS][MAX_HEADER_INTERVAL * MAX_BLOCKSIZE]; + int32_t lpc_sample_buffer[MAX_HEADER_INTERVAL * MAX_BLOCKSIZE]; AudioFrameQueue afq; /* Analysis stage. */ unsigned int number_of_frames; - unsigned int number_of_samples; unsigned int number_of_subblocks; - unsigned int seq_index; ///< Sequence index for high compression levels. - - const ChannelParams *prev_channel_params; - const DecodingParams *prev_decoding_params; - - ChannelParams *seq_channel_params; - DecodingParams *seq_decoding_params; - - int32_t *filter_state_buffer[NUM_FILTERS]; - - unsigned int max_codebook_search; int shorten_by; @@ -243,7 +235,7 @@ static int compare_filter_params(const ChannelParams *prev_cp, const ChannelPara if (prev->order != fp->order) return 1; - if (!prev->order) + if (!fp->order) return 0; if (prev->shift != fp->shift) @@ -259,26 +251,32 @@ static int compare_filter_params(const ChannelParams *prev_cp, const ChannelPara /** Compare two primitive matrices and returns 1 if anything has changed. * Returns 0 if they are both equal. */ -static int compare_matrix_params(MLPEncodeContext *ctx, const MatrixParams *prev, const MatrixParams *mp) +static int compare_matrix_params(MLPEncodeContext *ctx, MLPSubstream *s, + const MatrixParams *prev, const MatrixParams *mp) { - RestartHeader *rh = ctx->cur_restart_header; + RestartHeader *rh = s->cur_restart_header; if (prev->count != mp->count) return 1; - if (!prev->count) + if (!mp->count) return 0; - for (unsigned int channel = rh->min_channel; channel <= rh->max_channel; channel++) - if (prev->fbits[channel] != mp->fbits[channel]) - return 1; - for (unsigned int mat = 0; mat < mp->count; mat++) { if (prev->outch[mat] != mp->outch[mat]) return 1; - for (unsigned int channel = 0; channel < ctx->num_channels; channel++) - if (prev->coeff[mat][channel] != mp->coeff[mat][channel]) + if (prev->fbits[mat] != mp->fbits[mat]) + return 1; + + if (prev->noise_shift[mat] != mp->noise_shift[mat]) + return 1; + + if (prev->lsb_bypass[mat] != mp->lsb_bypass[mat]) + return 1; + + for (int ch = 0; ch <= rh->max_matrix_channel; ch++) + if (prev->coeff[mat][ch] != mp->coeff[mat][ch]) return 1; } @@ -288,13 +286,15 @@ static int compare_matrix_params(MLPEncodeContext *ctx, const MatrixParams *prev /** Compares two DecodingParams and ChannelParams structures to decide if a * new decoding params header has to be written. */ -static int compare_decoding_params(MLPEncodeContext *ctx) +static int compare_decoding_params(MLPEncodeContext *ctx, + MLPSubstream *s, + unsigned int index) { - const DecodingParams *prev = ctx->prev_decoding_params; - DecodingParams *dp = ctx->cur_decoding_params; + const DecodingParams *prev = index ? &s->b[index-1].major_decoding_params : restart_decoding_params; + DecodingParams *dp = &s->b[index].major_decoding_params; const MatrixParams *prev_mp = &prev->matrix_params; MatrixParams *mp = &dp->matrix_params; - RestartHeader *rh = ctx->cur_restart_header; + RestartHeader *rh = s->cur_restart_header; int retval = 0; if (prev->param_presence_flags != dp->param_presence_flags) @@ -303,24 +303,24 @@ static int compare_decoding_params(MLPEncodeContext *ctx) if (prev->blocksize != dp->blocksize) retval |= PARAM_BLOCKSIZE; - if (compare_matrix_params(ctx, prev_mp, mp)) + if (compare_matrix_params(ctx, s, prev_mp, mp)) retval |= PARAM_MATRIX; - for (unsigned int ch = 0; ch <= rh->max_matrix_channel; ch++) - if (prev_mp->shift[ch] != mp->shift[ch]) { + for (int ch = 0; ch <= rh->max_matrix_channel; ch++) + if (prev->output_shift[ch] != dp->output_shift[ch]) { retval |= PARAM_OUTSHIFT; break; } - for (unsigned int ch = 0; ch <= rh->max_channel; ch++) + for (int ch = 0; ch <= rh->max_channel; ch++) if (prev->quant_step_size[ch] != dp->quant_step_size[ch]) { retval |= PARAM_QUANTSTEP; break; } - for (unsigned int ch = rh->min_channel; ch <= rh->max_channel; ch++) { - const ChannelParams *prev_cp = &ctx->prev_channel_params[ch]; - ChannelParams *cp = &ctx->cur_channel_params[ch]; + for (int ch = rh->min_channel; ch <= rh->max_channel; ch++) { + const ChannelParams *prev_cp = index ? &s->b[index-1].major_channel_params[ch] : &restart_channel_params[ch]; + ChannelParams *cp = &s->b[index].major_channel_params[ch]; if (!(retval & PARAM_FIR) && compare_filter_params(prev_cp, cp, FIR)) @@ -335,7 +335,7 @@ static int compare_decoding_params(MLPEncodeContext *ctx) if (prev_cp->codebook != cp->codebook || prev_cp->huff_lsbs != cp->huff_lsbs ) - retval |= PARAM_PRESENT; + retval |= PARAM_PRESENCE; } return retval; @@ -355,7 +355,7 @@ static void copy_filter_params(ChannelParams *dst_cp, ChannelParams *src_cp, int dst->coeff_bits = src->coeff_bits; } - for (unsigned int order = 0; order < dst->order; order++) + for (int order = 0; order < dst->order; order++) dst_cp->coeff[filter][order] = src_cp->coeff[filter][order]; } @@ -363,37 +363,40 @@ static void copy_matrix_params(MatrixParams *dst, MatrixParams *src) { dst->count = src->count; - if (dst->count) { - for (unsigned int channel = 0; channel < MAX_CHANNELS; channel++) { - - dst->fbits[channel] = src->fbits[channel]; - dst->shift[channel] = src->shift[channel]; + if (!dst->count) + return; - for (unsigned int count = 0; count < MAX_MATRICES; count++) - dst->coeff[count][channel] = src->coeff[count][channel]; - } + for (int count = 0; count < MAX_MATRICES; count++) { + dst->outch[count] = src->outch[count]; + dst->fbits[count] = src->fbits[count]; + dst->noise_shift[count] = src->noise_shift[count]; + dst->lsb_bypass[count] = src->lsb_bypass[count]; - for (unsigned int count = 0; count < MAX_MATRICES; count++) - dst->outch[count] = src->outch[count]; + for (int channel = 0; channel < MAX_NCHANNELS; channel++) + dst->coeff[count][channel] = src->coeff[count][channel]; } } -static void copy_restart_frame_params(MLPEncodeContext *ctx) +static void copy_restart_frame_params(MLPEncodeContext *ctx, MLPSubstream *s) { + RestartHeader *rh = s->cur_restart_header; + for (unsigned int index = 0; index < ctx->number_of_subblocks; index++) { - DecodingParams *dp = ctx->seq_decoding_params + index; + DecodingParams *dp = &s->b[index].decoding_params; - copy_matrix_params(&dp->matrix_params, &ctx->cur_decoding_params->matrix_params); + copy_matrix_params(&dp->matrix_params, &s->b[1].decoding_params.matrix_params); - for (unsigned int channel = 0; channel < ctx->avctx->ch_layout.nb_channels; channel++) { - ChannelParams *cp = ctx->seq_channel_params + index*(ctx->avctx->ch_layout.nb_channels) + channel; + for (int ch = 0; ch <= rh->max_matrix_channel; ch++) + dp->output_shift[ch] = s->b[1].decoding_params.output_shift[ch]; - dp->quant_step_size[channel] = ctx->cur_decoding_params->quant_step_size[channel]; - dp->matrix_params.shift[channel] = ctx->cur_decoding_params->matrix_params.shift[channel]; + for (int ch = 0; ch <= rh->max_channel; ch++) { + ChannelParams *cp = &s->b[index].channel_params[ch]; + + dp->quant_step_size[ch] = s->b[1].decoding_params.quant_step_size[ch]; if (index) for (unsigned int filter = 0; filter < NUM_FILTERS; filter++) - copy_filter_params(cp, &ctx->cur_channel_params[channel], filter); + copy_filter_params(cp, &s->b[1].channel_params[ch], filter); } } } @@ -404,14 +407,17 @@ static void clear_decoding_params(DecodingParams *decoding_params) DecodingParams *dp = decoding_params; dp->param_presence_flags = 0xff; - dp->blocksize = 8; + dp->blocksize = 0; - memset(&dp->matrix_params , 0, sizeof(MatrixParams )); + memset(&dp->matrix_params, 0, sizeof(dp->matrix_params )); memset(dp->quant_step_size, 0, sizeof(dp->quant_step_size)); + memset(dp->sample_buffer, 0, sizeof(dp->sample_buffer )); + memset(dp->output_shift, 0, sizeof(dp->output_shift )); + memset(dp->max_order, MAX_FIR_ORDER, sizeof(dp->max_order)); } /** Clears a ChannelParams struct the way it should be after a restart header. */ -static void clear_channel_params(ChannelParams channel_params[MAX_CHANNELS], int nb_channels) +static void clear_channel_params(ChannelParams *channel_params, int nb_channels) { for (unsigned channel = 0; channel < nb_channels; channel++) { ChannelParams *cp = &channel_params[channel]; @@ -426,21 +432,20 @@ static void clear_channel_params(ChannelParams channel_params[MAX_CHANNELS], int } /** Sets default vales in our encoder for a DecodingParams struct. */ -static void default_decoding_params(MLPEncodeContext *ctx, DecodingParams *decoding_params) +static void default_decoding_params(MLPEncodeContext *ctx, DecodingParams *dp) { - DecodingParams *dp = decoding_params; uint8_t param_presence_flags = 0; - clear_decoding_params(decoding_params); + clear_decoding_params(dp); param_presence_flags |= PARAM_BLOCKSIZE; param_presence_flags |= PARAM_MATRIX; param_presence_flags |= PARAM_OUTSHIFT; param_presence_flags |= PARAM_QUANTSTEP; param_presence_flags |= PARAM_FIR; - /*param_presence_flags |= PARAM_IIR; */ + param_presence_flags |= PARAM_IIR; param_presence_flags |= PARAM_HUFFOFFSET; - param_presence_flags |= PARAM_PRESENT; + param_presence_flags |= PARAM_PRESENCE; dp->param_presence_flags = param_presence_flags; } @@ -450,12 +455,9 @@ static void default_decoding_params(MLPEncodeContext *ctx, DecodingParams *decod /** Calculates the smallest number of bits it takes to encode a given signed * value in two's complement. */ -static int inline number_sbits(int number) +static int inline number_sbits(int32_t n) { - if (number < -1) - number++; - - return av_log2(FFABS(number)) + 1 + !!number; + return 33 - ff_clz(FFABS(n)|1) - !n; } enum InputBitDepth { @@ -480,10 +482,7 @@ static av_cold int mlp_encode_init(AVCodecContext *avctx) { static AVOnce init_static_once = AV_ONCE_INIT; MLPEncodeContext *ctx = avctx->priv_data; - RestartHeader *const rh = &ctx->restart_header; uint64_t channels_present; - unsigned int sum = 0; - size_t size; int ret; ctx->avctx = avctx; @@ -529,27 +528,20 @@ static av_cold int mlp_encode_init(AVCodecContext *avctx) } ctx->coded_sample_rate[1] = -1 & 0xf; - /* TODO Keep count of bitrate and calculate real value. */ ctx->coded_peak_bitrate = mlp_peak_bitrate(9600000, avctx->sample_rate); - /* TODO support more channels. */ - if (avctx->ch_layout.nb_channels > 2) { - av_log(avctx, AV_LOG_WARNING, - "Only mono and stereo are supported at the moment.\n"); - } - ctx->substream_info |= SUBSTREAM_INFO_ALWAYS_SET; if (avctx->ch_layout.nb_channels <= 2) ctx->substream_info |= SUBSTREAM_INFO_MAX_2_CHAN; switch (avctx->sample_fmt) { - case AV_SAMPLE_FMT_S16: + case AV_SAMPLE_FMT_S16P: ctx->coded_sample_fmt[0] = BITS_16; ctx->wordlength = 16; avctx->bits_per_raw_sample = 16; break; /* TODO 20 bits: */ - case AV_SAMPLE_FMT_S32: + case AV_SAMPLE_FMT_S32P: ctx->coded_sample_fmt[0] = BITS_24; ctx->wordlength = 24; avctx->bits_per_raw_sample = 24; @@ -564,32 +556,11 @@ static av_cold int mlp_encode_init(AVCodecContext *avctx) ctx->input_timing = -avctx->frame_size; ctx->num_channels = avctx->ch_layout.nb_channels + 2; /* +2 noise channels */ - ctx->one_sample_buffer_size = avctx->frame_size - * ctx->num_channels; - /* TODO Let user pass major header interval as parameter. */ - ctx->max_restart_interval = MAJOR_HEADER_INTERVAL; - ctx->max_codebook_search = 3; - ctx->min_restart_interval = MAJOR_HEADER_INTERVAL; + ctx->min_restart_interval = ctx->cur_restart_interval = ctx->max_restart_interval; ctx->restart_intervals = ctx->max_restart_interval / ctx->min_restart_interval; - /* TODO Let user pass parameters for LPC filter. */ - - size = avctx->frame_size * ctx->max_restart_interval; - ctx->lpc_sample_buffer = av_calloc(size, sizeof(*ctx->lpc_sample_buffer)); - if (!ctx->lpc_sample_buffer) - return AVERROR(ENOMEM); - - size = ctx->one_sample_buffer_size * ctx->max_restart_interval; - ctx->major_scratch_buffer = av_calloc(size, sizeof(*ctx->major_scratch_buffer)); - if (!ctx->major_scratch_buffer) - return AVERROR(ENOMEM); - - ctx->major_inout_buffer = av_calloc(size, sizeof(*ctx->major_inout_buffer)); - if (!ctx->major_inout_buffer) - return AVERROR(ENOMEM); - - ctx->num_substreams = 1; // TODO: change this after adding multi-channel support for TrueHD + ctx->num_substreams = 1; channels_present = av_channel_layout_subset(&avctx->ch_layout, ~(uint64_t)0); if (ctx->avctx->codec_id == AV_CODEC_ID_MLP) { @@ -615,83 +586,78 @@ static av_cold int mlp_encode_init(AVCodecContext *avctx) ctx->summary_info = ff_mlp_ch_info[ctx->channel_arrangement].summary_info ; } else { /* TrueHD */ - if (channels_present == AV_CH_LAYOUT_MONO) { - ctx->ch_modifier_thd0 = 3; - ctx->ch_modifier_thd1 = 3; - ctx->ch_modifier_thd2 = 3; - ctx->channel_arrangement = 2; + ctx->num_substreams = 1 + (avctx->ch_layout.nb_channels > 2); + switch (channels_present) { + case AV_CH_LAYOUT_MONO: + ctx->ch2_presentation_mod= 3; + ctx->ch6_presentation_mod= 3; + ctx->ch8_presentation_mod= 3; ctx->thd_substream_info = 0x14; - } else if (channels_present == AV_CH_LAYOUT_STEREO) { - ctx->ch_modifier_thd0 = 1; - ctx->ch_modifier_thd1 = 1; - ctx->ch_modifier_thd2 = 1; - ctx->channel_arrangement = 1; + break; + case AV_CH_LAYOUT_STEREO: + ctx->ch2_presentation_mod= 1; + ctx->ch6_presentation_mod= 1; + ctx->ch8_presentation_mod= 1; ctx->thd_substream_info = 0x14; - } else if (channels_present == AV_CH_LAYOUT_5POINT0) { - ctx->ch_modifier_thd0 = 1; - ctx->ch_modifier_thd1 = 1; - ctx->ch_modifier_thd2 = 1; - ctx->channel_arrangement = 11; - ctx->thd_substream_info = 0x104; - } else if (channels_present == AV_CH_LAYOUT_5POINT1) { - ctx->ch_modifier_thd0 = 2; - ctx->ch_modifier_thd1 = 1; - ctx->ch_modifier_thd2 = 2; - ctx->channel_arrangement = 15; - ctx->thd_substream_info = 0x104; - } else { + break; + case AV_CH_LAYOUT_2POINT1: + case AV_CH_LAYOUT_SURROUND: + case AV_CH_LAYOUT_3POINT1: + case AV_CH_LAYOUT_4POINT0: + case AV_CH_LAYOUT_4POINT1: + case AV_CH_LAYOUT_5POINT0: + case AV_CH_LAYOUT_5POINT1: + ctx->ch2_presentation_mod= 0; + ctx->ch6_presentation_mod= 0; + ctx->ch8_presentation_mod= 0; + ctx->thd_substream_info = 0x3C; + break; + default: av_assert1(!"AVCodec.ch_layouts needs to be updated"); } ctx->flags = 0; ctx->channel_occupancy = 0; ctx->summary_info = 0; + ctx->channel_arrangement = + ctx->channel_arrangement8 = layout_truehd(channels_present); } - size = ctx->max_restart_interval; - ctx->max_output_bits = av_calloc(size, sizeof(*ctx->max_output_bits)); - if (!ctx->max_output_bits) - return AVERROR(ENOMEM); - - size = ctx->max_restart_interval; - ctx->lossless_check_data = av_calloc(size, sizeof(*ctx->lossless_check_data)); - if (!ctx->lossless_check_data) - return AVERROR(ENOMEM); - for (unsigned int index = 0; index < ctx->restart_intervals; index++) { - ctx->seq_offset[index] = sum; - ctx->seq_size [index] = ((index + 1) * ctx->min_restart_interval) + 1; - sum += ctx->seq_size[index]; + for (int n = 0; n < ctx->num_substreams; n++) + ctx->s[n].b[index].seq_size = ((index + 1) * ctx->min_restart_interval) + 1; } - ctx->sequence_size = sum; - size = ctx->restart_intervals * ctx->sequence_size * ctx->avctx->ch_layout.nb_channels; - ctx->channel_params = av_calloc(size, sizeof(*ctx->channel_params)); - if (!ctx->channel_params) - return AVERROR(ENOMEM); - size = ctx->restart_intervals * ctx->sequence_size; - ctx->decoding_params = av_calloc(size, sizeof(*ctx->decoding_params)); - if (!ctx->decoding_params) - return AVERROR(ENOMEM); /* TODO see if noisegen_seed is really worth it. */ - rh->noisegen_seed = 0; + if (ctx->avctx->codec_id == AV_CODEC_ID_MLP) { + RestartHeader *const rh = &ctx->s[0].restart_header; - rh->min_channel = 0; - rh->max_channel = avctx->ch_layout.nb_channels - 1; - /* FIXME: this works for 1 and 2 channels, but check for more */ - rh->max_matrix_channel = rh->max_channel; + rh->noisegen_seed = 0; + rh->min_channel = 0; + rh->max_channel = avctx->ch_layout.nb_channels - 1; + rh->max_matrix_channel = rh->max_channel; + } else { + RestartHeader *rh = &ctx->s[0].restart_header; - if ((ret = ff_lpc_init(&ctx->lpc_ctx, ctx->number_of_samples, - MLP_MAX_LPC_ORDER, FF_LPC_TYPE_LEVINSON)) < 0) - return ret; + rh->noisegen_seed = 0; + rh->min_channel = 0; + rh->max_channel = FFMIN(avctx->ch_layout.nb_channels, 2) - 1; + rh->max_matrix_channel = rh->max_channel; - for (int i = 0; i < NUM_FILTERS; i++) { - ctx->filter_state_buffer[i] = av_calloc(avctx->frame_size * ctx->max_restart_interval, - sizeof(*ctx->filter_state_buffer[0])); - if (!ctx->filter_state_buffer[i]) - return AVERROR(ENOMEM); + if (avctx->ch_layout.nb_channels > 2) { + rh = &ctx->s[1].restart_header; + + rh->noisegen_seed = 0; + rh->min_channel = 2; + rh->max_channel = avctx->ch_layout.nb_channels - 1; + rh->max_matrix_channel = rh->max_channel; + } } + if ((ret = ff_lpc_init(&ctx->lpc_ctx, ctx->avctx->frame_size, + MLP_MAX_LPC_ORDER, ctx->lpc_type)) < 0) + return ret; + ff_af_queue_init(avctx, &ctx->afq); ff_thread_once(&init_static_once, mlp_encode_init_static); @@ -725,14 +691,14 @@ static void write_major_sync(MLPEncodeContext *ctx, uint8_t *buf, int buf_size) } else if (ctx->avctx->codec_id == AV_CODEC_ID_TRUEHD) { put_bits(&pb, 8, SYNC_TRUEHD ); put_bits(&pb, 4, ctx->coded_sample_rate[0]); - put_bits(&pb, 1, 0 ); /* 6ch multichannel type */ - put_bits(&pb, 1, 0 ); /* 8ch multichannel type */ + put_bits(&pb, 1, ctx->multichannel_type6ch); + put_bits(&pb, 1, ctx->multichannel_type8ch); put_bits(&pb, 2, 0 ); /* ignored */ - put_bits(&pb, 2, ctx->ch_modifier_thd0 ); - put_bits(&pb, 2, ctx->ch_modifier_thd1 ); + put_bits(&pb, 2, ctx->ch2_presentation_mod); + put_bits(&pb, 2, ctx->ch6_presentation_mod); put_bits(&pb, 5, ctx->channel_arrangement ); - put_bits(&pb, 2, ctx->ch_modifier_thd2 ); - put_bits(&pb, 13, ctx->channel_arrangement ); + put_bits(&pb, 2, ctx->ch8_presentation_mod); + put_bits(&pb, 13, ctx->channel_arrangement8); } put_bits(&pb, 16, MAJOR_SYNC_INFO_SIGNATURE); @@ -740,7 +706,7 @@ static void write_major_sync(MLPEncodeContext *ctx, uint8_t *buf, int buf_size) put_bits(&pb, 16, 0 ); /* ignored */ put_bits(&pb, 1, 1 ); /* is_vbr */ put_bits(&pb, 15, ctx->coded_peak_bitrate ); - put_bits(&pb, 4, 1 ); /* num_substreams */ + put_bits(&pb, 4, ctx->num_substreams ); put_bits(&pb, 2, 0 ); /* ignored */ put_bits(&pb, 2, 0 ); /* extended substream info */ @@ -786,9 +752,10 @@ static void write_major_sync(MLPEncodeContext *ctx, uint8_t *buf, int buf_size) * decoded losslessly again after such a header and the subsequent decoding * params header. */ -static void write_restart_header(MLPEncodeContext *ctx, PutBitContext *pb) +static void write_restart_header(MLPEncodeContext *ctx, MLPSubstream *s, + PutBitContext *pb) { - RestartHeader *rh = ctx->cur_restart_header; + RestartHeader *rh = s->cur_restart_header; uint8_t lossless_check = xor_32_to_8(rh->lossless_check_data); unsigned int start_count = put_bits_count(pb); PutBitContext tmpb; @@ -801,7 +768,7 @@ static void write_restart_header(MLPEncodeContext *ctx, PutBitContext *pb) put_bits(pb, 4, rh->max_matrix_channel); put_bits(pb, 4, rh->noise_shift ); put_bits(pb, 23, rh->noisegen_seed ); - put_bits(pb, 4, 0 ); /* TODO max_shift */ + put_bits(pb, 4, rh->max_shift ); put_bits(pb, 5, rh->max_huff_lsbs ); put_bits(pb, 5, rh->max_output_bits ); put_bits(pb, 5, rh->max_output_bits ); @@ -809,7 +776,7 @@ static void write_restart_header(MLPEncodeContext *ctx, PutBitContext *pb) put_bits(pb, 8, lossless_check ); put_bits(pb, 16, 0 ); /* ignored */ - for (unsigned int ch = 0; ch <= rh->max_matrix_channel; ch++) + for (int ch = 0; ch <= rh->max_matrix_channel; ch++) put_bits(pb, 6, ch); /* Data must be flushed for the checksum to be correct. */ @@ -822,20 +789,27 @@ static void write_restart_header(MLPEncodeContext *ctx, PutBitContext *pb) } /** Writes matrix params for all primitive matrices to the bitstream. */ -static void write_matrix_params(MLPEncodeContext *ctx, PutBitContext *pb) +static void write_matrix_params(MLPEncodeContext *ctx, + MLPSubstream *s, + DecodingParams *dp, + PutBitContext *pb) { - DecodingParams *dp = ctx->cur_decoding_params; + RestartHeader *rh = s->cur_restart_header; MatrixParams *mp = &dp->matrix_params; + int max_channel = rh->max_matrix_channel; put_bits(pb, 4, mp->count); + if (!ctx->noise_type) + max_channel += 2; + for (unsigned int mat = 0; mat < mp->count; mat++) { put_bits(pb, 4, mp->outch[mat]); /* matrix_out_ch */ put_bits(pb, 4, mp->fbits[mat]); - put_bits(pb, 1, 0 ); /* lsb_bypass */ + put_bits(pb, 1, mp->lsb_bypass[mat]); - for (unsigned int channel = 0; channel < ctx->num_channels; channel++) { - int32_t coeff = mp->coeff[mat][channel]; + for (int ch = 0; ch <= max_channel; ch++) { + int32_t coeff = mp->coeff[mat][ch]; if (coeff) { put_bits(pb, 1, 1); @@ -851,15 +825,17 @@ static void write_matrix_params(MLPEncodeContext *ctx, PutBitContext *pb) } /** Writes filter parameters for one filter to the bitstream. */ -static void write_filter_params(MLPEncodeContext *ctx, PutBitContext *pb, - unsigned int channel, unsigned int filter) +static void write_filter_params(MLPEncodeContext *ctx, + ChannelParams *cp, + PutBitContext *pb, + int channel, unsigned int filter) { - FilterParams *fp = &ctx->cur_channel_params[channel].filter_params[filter]; + FilterParams *fp = &cp->filter_params[filter]; put_bits(pb, 4, fp->order); if (fp->order > 0) { - int32_t *fcoeff = ctx->cur_channel_params[channel].coeff[filter]; + int32_t *fcoeff = cp->coeff[filter]; put_bits(pb, 4, fp->shift ); put_bits(pb, 5, fp->coeff_bits ); @@ -877,12 +853,12 @@ static void write_filter_params(MLPEncodeContext *ctx, PutBitContext *pb, /** Writes decoding parameters to the bitstream. These change very often, * usually at almost every frame. */ -static void write_decoding_params(MLPEncodeContext *ctx, PutBitContext *pb, - int params_changed) +static void write_decoding_params(MLPEncodeContext *ctx, MLPSubstream *s, + PutBitContext *pb, int params_changed, + unsigned int subblock_index) { - DecodingParams *dp = ctx->cur_decoding_params; - RestartHeader *rh = ctx->cur_restart_header; - MatrixParams *mp = &dp->matrix_params; + DecodingParams *dp = &s->b[subblock_index].major_decoding_params; + RestartHeader *rh = s->cur_restart_header; if (dp->param_presence_flags != PARAMS_DEFAULT && params_changed & PARAM_PRESENCE_FLAGS) { @@ -904,7 +880,7 @@ static void write_decoding_params(MLPEncodeContext *ctx, PutBitContext *pb, if (dp->param_presence_flags & PARAM_MATRIX) { if (params_changed & PARAM_MATRIX) { put_bits(pb, 1, 1); - write_matrix_params(ctx, pb); + write_matrix_params(ctx, s, dp, pb); } else { put_bits(pb, 1, 0); } @@ -913,8 +889,8 @@ static void write_decoding_params(MLPEncodeContext *ctx, PutBitContext *pb, if (dp->param_presence_flags & PARAM_OUTSHIFT) { if (params_changed & PARAM_OUTSHIFT) { put_bits(pb, 1, 1); - for (unsigned int ch = 0; ch <= rh->max_matrix_channel; ch++) - put_sbits(pb, 4, mp->shift[ch]); + for (int ch = 0; ch <= rh->max_matrix_channel; ch++) + put_sbits(pb, 4, dp->output_shift[ch]); } else { put_bits(pb, 1, 0); } @@ -923,15 +899,15 @@ static void write_decoding_params(MLPEncodeContext *ctx, PutBitContext *pb, if (dp->param_presence_flags & PARAM_QUANTSTEP) { if (params_changed & PARAM_QUANTSTEP) { put_bits(pb, 1, 1); - for (unsigned int ch = 0; ch <= rh->max_channel; ch++) + for (int ch = 0; ch <= rh->max_channel; ch++) put_bits(pb, 4, dp->quant_step_size[ch]); } else { put_bits(pb, 1, 0); } } - for (unsigned int ch = rh->min_channel; ch <= rh->max_channel; ch++) { - ChannelParams *cp = &ctx->cur_channel_params[ch]; + for (int ch = rh->min_channel; ch <= rh->max_channel; ch++) { + ChannelParams *cp = &s->b[subblock_index].major_channel_params[ch]; if (dp->param_presence_flags & 0xF) { put_bits(pb, 1, 1); @@ -939,7 +915,7 @@ static void write_decoding_params(MLPEncodeContext *ctx, PutBitContext *pb, if (dp->param_presence_flags & PARAM_FIR) { if (params_changed & PARAM_FIR) { put_bits(pb, 1, 1); - write_filter_params(ctx, pb, ch, FIR); + write_filter_params(ctx, cp, pb, ch, FIR); } else { put_bits(pb, 1, 0); } @@ -948,7 +924,7 @@ static void write_decoding_params(MLPEncodeContext *ctx, PutBitContext *pb, if (dp->param_presence_flags & PARAM_IIR) { if (params_changed & PARAM_IIR) { put_bits(pb, 1, 1); - write_filter_params(ctx, pb, ch, IIR); + write_filter_params(ctx, cp, pb, ch, IIR); } else { put_bits(pb, 1, 0); } @@ -963,7 +939,7 @@ static void write_decoding_params(MLPEncodeContext *ctx, PutBitContext *pb, } } if (cp->codebook > 0 && cp->huff_lsbs > 24) { - av_log(ctx->avctx, AV_LOG_ERROR, "Invalid Huff LSBs\n"); + av_log(ctx->avctx, AV_LOG_ERROR, "Invalid Huff LSBs %d\n", cp->huff_lsbs); } put_bits(pb, 2, cp->codebook ); @@ -977,17 +953,18 @@ static void write_decoding_params(MLPEncodeContext *ctx, PutBitContext *pb, /** Writes the residuals to the bitstream. That is, the VLC codes from the * codebooks (if any is used), and then the residual. */ -static void write_block_data(MLPEncodeContext *ctx, PutBitContext *pb) +static void write_block_data(MLPEncodeContext *ctx, MLPSubstream *s, + PutBitContext *pb, unsigned int subblock_index) { - DecodingParams *dp = ctx->cur_decoding_params; - RestartHeader *rh = ctx->cur_restart_header; - int32_t *sample_buffer = ctx->write_buffer; + RestartHeader *rh = s->cur_restart_header; + DecodingParams *dp = &s->b[subblock_index].major_decoding_params; + MatrixParams *mp = &dp->matrix_params; int32_t sign_huff_offset[MAX_CHANNELS]; int codebook_index [MAX_CHANNELS]; int lsb_bits [MAX_CHANNELS]; - for (unsigned int ch = rh->min_channel; ch <= rh->max_channel; ch++) { - ChannelParams *cp = &ctx->cur_channel_params[ch]; + for (int ch = rh->min_channel; ch <= rh->max_channel; ch++) { + ChannelParams *cp = &s->b[subblock_index].major_channel_params[ch]; int sign_shift; lsb_bits [ch] = cp->huff_lsbs - dp->quant_step_size[ch]; @@ -1005,53 +982,55 @@ static void write_block_data(MLPEncodeContext *ctx, PutBitContext *pb) } for (unsigned int i = 0; i < dp->blocksize; i++) { - for (unsigned int ch = rh->min_channel; ch <= rh->max_channel; ch++) { - int32_t sample = *sample_buffer++ >> dp->quant_step_size[ch]; + for (unsigned int mat = 0; mat < mp->count; mat++) { + if (mp->lsb_bypass[mat]) { + const int8_t *bypassed_lsbs = mp->bypassed_lsbs[mat]; + + put_bits(pb, 1, bypassed_lsbs[i]); + } + } + + for (int ch = rh->min_channel; ch <= rh->max_channel; ch++) { + int32_t *sample_buffer = dp->sample_buffer[ch]; + int32_t sample = sample_buffer[i] >> dp->quant_step_size[ch]; sample -= sign_huff_offset[ch]; if (codebook_index[ch] >= 0) { int vlc = sample >> lsb_bits[ch]; put_bits(pb, ff_mlp_huffman_tables[codebook_index[ch]][vlc][1], ff_mlp_huffman_tables[codebook_index[ch]][vlc][0]); + sample &= ((1 << lsb_bits[ch]) - 1); } - put_sbits(pb, lsb_bits[ch], sample); + put_bits(pb, lsb_bits[ch], sample); } - sample_buffer += 2; /* noise channels */ } - - ctx->write_buffer = sample_buffer; } /** Writes the substream data to the bitstream. */ -static uint8_t *write_substr(MLPEncodeContext *ctx, uint8_t *buf, int buf_size, +static uint8_t *write_substr(MLPEncodeContext *ctx, + MLPSubstream *s, + uint8_t *buf, int buf_size, int restart_frame, - uint16_t substream_data_len[MAX_SUBSTREAMS]) + uint16_t *substream_data_len) { - int32_t *lossless_check_data = ctx->lossless_check_data; - unsigned int cur_subblock_index = ctx->major_cur_subblock_index; - unsigned int num_subblocks = ctx->major_filter_state_subblock; - RestartHeader *rh = &ctx->restart_header; + int32_t *lossless_check_data = &s->b[ctx->frame_index].lossless_check_data; + unsigned int cur_subblock_index = s->major_cur_subblock_index; + unsigned int num_subblocks = s->major_filter_state_subblock; + RestartHeader *rh = &s->restart_header; int substr_restart_frame = restart_frame; uint8_t parity, checksum; PutBitContext pb; int params_changed; - int end = 0; - lossless_check_data += ctx->frame_index; - ctx->cur_restart_header = rh; + s->cur_restart_header = rh; init_put_bits(&pb, buf, buf_size); for (unsigned int subblock = 0; subblock <= num_subblocks; subblock++) { - unsigned int subblock_index; - - subblock_index = cur_subblock_index++; + unsigned int subblock_index = cur_subblock_index++; - ctx->cur_decoding_params = &ctx->major_decoding_params[subblock_index]; - ctx->cur_channel_params = ctx->major_channel_params[subblock_index]; - - params_changed = ctx->major_params_changed[subblock_index]; + params_changed = s->b[subblock_index].major_params_changed; if (substr_restart_frame || params_changed) { put_bits(&pb, 1, 1); @@ -1059,18 +1038,19 @@ static uint8_t *write_substr(MLPEncodeContext *ctx, uint8_t *buf, int buf_size, if (substr_restart_frame) { put_bits(&pb, 1, 1); - write_restart_header(ctx, &pb); + write_restart_header(ctx, s, &pb); rh->lossless_check_data = 0; } else { put_bits(&pb, 1, 0); } - write_decoding_params(ctx, &pb, params_changed); + write_decoding_params(ctx, s, &pb, params_changed, + subblock_index); } else { put_bits(&pb, 1, 0); } - write_block_data(ctx, &pb); + write_block_data(ctx, s, &pb, subblock_index); put_bits(&pb, 1, !substr_restart_frame); @@ -1079,14 +1059,14 @@ static uint8_t *write_substr(MLPEncodeContext *ctx, uint8_t *buf, int buf_size, put_bits(&pb, (-put_bits_count(&pb)) & 15, 0); - rh->lossless_check_data ^= *lossless_check_data++; + rh->lossless_check_data ^= lossless_check_data[0]; if (ctx->last_frames == 0 && ctx->shorten_by) { if (ctx->avctx->codec_id == AV_CODEC_ID_TRUEHD) { put_bits(&pb, 16, END_OF_STREAM & 0xFFFF); - put_bits(&pb, 16, (ctx->shorten_by & 0x1FFF) | 0x2000); + put_bits(&pb, 16, (ctx->shorten_by & 0x1FFF) | 0xE000); } else { - put_bits(&pb, 32, END_OF_STREAM); + put_bits32(&pb, END_OF_STREAM); } } @@ -1102,13 +1082,12 @@ static uint8_t *write_substr(MLPEncodeContext *ctx, uint8_t *buf, int buf_size, flush_put_bits(&pb); - end += put_bytes_output(&pb); - substream_data_len[0] = end; + substream_data_len[0] = put_bytes_output(&pb); - buf += put_bytes_output(&pb); + buf += substream_data_len[0]; - ctx->major_cur_subblock_index += ctx->major_filter_state_subblock + 1; - ctx->major_filter_state_subblock = 0; + s->major_cur_subblock_index += s->major_filter_state_subblock + 1; + s->major_filter_state_subblock = 0; return buf; } @@ -1117,9 +1096,10 @@ static uint8_t *write_substr(MLPEncodeContext *ctx, uint8_t *buf, int buf_size, static void write_frame_headers(MLPEncodeContext *ctx, uint8_t *frame_header, uint8_t *substream_headers, unsigned int length, int restart_frame, - uint16_t substream_data_len[1]) + uint16_t substream_data_len[MAX_SUBSTREAMS]) { uint16_t access_unit_header = 0; + uint16_t substream_data_end = 0; uint16_t parity_nibble = 0; parity_nibble = ctx->input_timing; @@ -1128,11 +1108,13 @@ static void write_frame_headers(MLPEncodeContext *ctx, uint8_t *frame_header, for (unsigned int substr = 0; substr < ctx->num_substreams; substr++) { uint16_t substr_hdr = 0; + substream_data_end += substream_data_len[substr]; + substr_hdr |= (0 << 15); /* extraword */ substr_hdr |= (!restart_frame << 14); /* !restart_frame */ substr_hdr |= (1 << 13); /* checkdata */ substr_hdr |= (0 << 12); /* ??? */ - substr_hdr |= (substream_data_len[substr] / 2) & 0x0FFF; + substr_hdr |= (substream_data_end / 2) & 0x0FFF; AV_WB16(substream_headers, substr_hdr); @@ -1177,7 +1159,13 @@ static int write_access_unit(MLPEncodeContext *ctx, uint8_t *buf, buf_size -= 2; } - buf = write_substr(ctx, buf, buf_size, restart_frame, &substream_data_len[0]); + for (int substr = 0; substr < ctx->num_substreams; substr++) { + MLPSubstream *s = &ctx->s[substr]; + uint8_t *buf0 = buf; + + buf = write_substr(ctx, s, buf, buf_size, restart_frame, &substream_data_len[substr]); + buf_size -= buf - buf0; + } total_length = buf - buf0; @@ -1194,62 +1182,73 @@ static int write_access_unit(MLPEncodeContext *ctx, uint8_t *buf, * appropriately depending on the bit-depth, and calculates the * lossless_check_data that will be written to the restart header. */ -static void input_data_internal(MLPEncodeContext *ctx, const uint8_t *samples, - int nb_samples, - int is24) +static void input_data_internal(MLPEncodeContext *ctx, MLPSubstream *s, + uint8_t **const samples, + int nb_samples, int is24) { - int32_t *lossless_check_data = ctx->lossless_check_data; - const int32_t *samples_32 = (const int32_t *) samples; - const int16_t *samples_16 = (const int16_t *) samples; - RestartHeader *rh = &ctx->restart_header; - int32_t *sample_buffer = ctx->inout_buffer; + int32_t *lossless_check_data = &s->b[ctx->frame_index].lossless_check_data; + RestartHeader *rh = &s->restart_header; int32_t temp_lossless_check_data = 0; - uint32_t greatest = 0; - - lossless_check_data += ctx->frame_index; + uint32_t bits = 0; for (int i = 0; i < nb_samples; i++) { - for (unsigned int channel = 0; channel <= rh->max_channel; channel++) { - uint32_t abs_sample; + for (int ch = 0; ch <= rh->max_channel; ch++) { + const int32_t *samples_32 = (const int32_t *)samples[ch]; + const int16_t *samples_16 = (const int16_t *)samples[ch]; + int32_t *sample_buffer = s->b[ctx->frame_index].inout_buffer[ch]; int32_t sample; - sample = is24 ? *samples_32++ >> 8 : *samples_16++ * 256; + sample = is24 ? samples_32[i] >> 8 : samples_16[i] * 256; - /* TODO Find out if number_sbits can be used for negative values. */ - abs_sample = FFABS(sample); - greatest = FFMAX(greatest, abs_sample); + bits = FFMAX(number_sbits(sample), bits); - temp_lossless_check_data ^= (sample & 0x00ffffff) << channel; - *sample_buffer++ = sample; + temp_lossless_check_data ^= (sample & 0x00ffffff) << ch; + sample_buffer[i] = sample; } + } + + for (int ch = 0; ch <= rh->max_channel; ch++) { + for (int i = nb_samples; i < ctx->avctx->frame_size; i++) { + int32_t *sample_buffer = s->b[ctx->frame_index].inout_buffer[ch]; - sample_buffer += 2; /* noise channels */ + sample_buffer[i] = 0; + } } - ctx->max_output_bits[ctx->frame_index] = number_sbits(greatest); + s->b[ctx->frame_index].max_output_bits = bits; - *lossless_check_data++ = temp_lossless_check_data; + lossless_check_data[0] = temp_lossless_check_data; } /** Wrapper function for inputting data in two different bit-depths. */ -static void input_data(MLPEncodeContext *ctx, const void *samples, int nb_samples) +static void input_data(MLPEncodeContext *ctx, MLPSubstream *s, uint8_t **const samples, int nb_samples) { - input_data_internal(ctx, samples, nb_samples, ctx->avctx->sample_fmt == AV_SAMPLE_FMT_S32); + input_data_internal(ctx, s, samples, nb_samples, ctx->avctx->sample_fmt == AV_SAMPLE_FMT_S32P); } -static void input_to_sample_buffer(MLPEncodeContext *ctx) +static void input_to_sample_buffer(MLPEncodeContext *ctx, MLPSubstream *s) { - int32_t *sample_buffer = ctx->sample_buffer; + RestartHeader *rh = &s->restart_header; for (unsigned int index = 0; index < ctx->number_of_frames; index++) { - unsigned int cur_index = (ctx->frame_index + index + 1) % ctx->max_restart_interval; - int32_t *input_buffer = ctx->inout_buffer + cur_index * ctx->one_sample_buffer_size; - - for (unsigned int i = 0; i < ctx->avctx->frame_size; i++) { - for (unsigned int channel = 0; channel < ctx->avctx->ch_layout.nb_channels; channel++) - *sample_buffer++ = *input_buffer++; - sample_buffer += 2; /* noise_channels */ - input_buffer += 2; /* noise_channels */ + unsigned int cur_index = (ctx->frame_index + index + 1) % ctx->cur_restart_interval; + DecodingParams *dp = &s->b[index+1].decoding_params; + + for (int ch = 0; ch <= rh->max_channel; ch++) { + const int32_t *input_buffer = s->b[cur_index].inout_buffer[ch]; + int32_t *sample_buffer = dp->sample_buffer[ch]; + int off = 0; + + if (dp->blocksize < ctx->avctx->frame_size) { + DecodingParams *dp = &s->b[index].decoding_params; + int32_t *sample_buffer = dp->sample_buffer[ch]; + for (unsigned int i = 0; i < dp->blocksize; i++) + sample_buffer[i] = input_buffer[i]; + off = dp->blocksize; + } + + for (unsigned int i = 0; i < dp->blocksize; i++) + sample_buffer[i] = input_buffer[i + off]; } } } @@ -1259,113 +1258,138 @@ static void input_to_sample_buffer(MLPEncodeContext *ctx) ****************************************************************************/ /** Counts the number of trailing zeroes in a value */ -static int number_trailing_zeroes(int32_t sample) +static int number_trailing_zeroes(int32_t sample, unsigned int max, unsigned int def) +{ + return sample ? FFMIN(max, ff_ctz(sample)) : def; +} + +static void determine_output_shift(MLPEncodeContext *ctx, MLPSubstream *s) { - int bits = ff_ctz(sample); + RestartHeader *rh = s->cur_restart_header; + DecodingParams *dp1 = &s->b[1].decoding_params; + int32_t sample_mask[MAX_CHANNELS]; - /* All samples are 0. TODO Return previous quant_step_size to avoid - * writing a new header. */ - if (bits >= 24) - return 0; + memset(sample_mask, 0, sizeof(sample_mask)); + + for (int j = 0; j <= ctx->cur_restart_interval; j++) { + DecodingParams *dp = &s->b[j].decoding_params; + + for (int ch = 0; ch <= rh->max_matrix_channel; ch++) { + int32_t *sample_buffer = dp->sample_buffer[ch]; + + for (int i = 0; i < dp->blocksize; i++) + sample_mask[ch] |= sample_buffer[i]; + } + } + + for (int ch = 0; ch <= rh->max_matrix_channel; ch++) + dp1->output_shift[ch] = number_trailing_zeroes(sample_mask[ch], 7, 0); + + for (int j = 0; j <= ctx->cur_restart_interval; j++) { + DecodingParams *dp = &s->b[j].decoding_params; - return bits; + for (int ch = 0; ch <= rh->max_matrix_channel; ch++) { + int32_t *sample_buffer = dp->sample_buffer[ch]; + const int shift = dp1->output_shift[ch]; + + for (int i = 0; i < dp->blocksize; i++) + sample_buffer[i] >>= shift; + } + } } /** Determines how many bits are zero at the end of all samples so they can be * shifted out. */ -static void determine_quant_step_size(MLPEncodeContext *ctx) +static void determine_quant_step_size(MLPEncodeContext *ctx, MLPSubstream *s) { - DecodingParams *dp = ctx->cur_decoding_params; - RestartHeader *rh = ctx->cur_restart_header; - MatrixParams *mp = &dp->matrix_params; - int32_t *sample_buffer = ctx->sample_buffer; + RestartHeader *rh = s->cur_restart_header; + DecodingParams *dp1 = &s->b[1].decoding_params; int32_t sample_mask[MAX_CHANNELS]; - memset(sample_mask, 0x00, sizeof(sample_mask)); + memset(sample_mask, 0, sizeof(sample_mask)); + + for (int j = 0; j <= ctx->cur_restart_interval; j++) { + DecodingParams *dp = &s->b[j].decoding_params; - for (unsigned int i = 0; i < ctx->number_of_samples; i++) { - for (unsigned int channel = 0; channel <= rh->max_channel; channel++) - sample_mask[channel] |= *sample_buffer++; + for (int ch = 0; ch <= rh->max_channel; ch++) { + int32_t *sample_buffer = dp->sample_buffer[ch]; - sample_buffer += 2; /* noise channels */ + for (int i = 0; i < dp->blocksize; i++) + sample_mask[ch] |= sample_buffer[i]; + } } - for (unsigned int channel = 0; channel <= rh->max_channel; channel++) - dp->quant_step_size[channel] = number_trailing_zeroes(sample_mask[channel]) - mp->shift[channel]; + for (int ch = 0; ch <= rh->max_channel; ch++) + dp1->quant_step_size[ch] = number_trailing_zeroes(sample_mask[ch], 15, 0); } /** Determines the smallest number of bits needed to encode the filter * coefficients, and if it's possible to right-shift their values without * losing any precision. */ -static void code_filter_coeffs(MLPEncodeContext *ctx, FilterParams *fp, int32_t *fcoeff) +static void code_filter_coeffs(MLPEncodeContext *ctx, FilterParams *fp, const int32_t *fcoeff) { - int min = INT_MAX, max = INT_MIN; - int bits, shift; - int coeff_mask = 0; + uint32_t coeff_mask = 0; + int bits = 0, shift; for (int order = 0; order < fp->order; order++) { - int coeff = fcoeff[order]; + int32_t coeff = fcoeff[order]; - if (coeff < min) - min = coeff; - if (coeff > max) - max = coeff; + bits = FFMAX(number_sbits(coeff), bits); coeff_mask |= coeff; } - bits = FFMAX(number_sbits(min), number_sbits(max)); - - for (shift = 0; shift < 7 && bits + shift < 16 && !(coeff_mask & (1<coeff_bits = bits; - fp->coeff_shift = shift; + fp->coeff_bits = FFMAX(1, bits - shift); + fp->coeff_shift = FFMIN(shift, 16 - fp->coeff_bits); } /** Determines the best filter parameters for the given data and writes the * necessary information to the context. - * TODO Add IIR filter predictor! */ -static void set_filter_params(MLPEncodeContext *ctx, - unsigned int channel, unsigned int filter, - int clear_filter) +static void set_filter(MLPEncodeContext *ctx, MLPSubstream *s, + int channel, int retry_filter) { - ChannelParams *cp = &ctx->cur_channel_params[channel]; - FilterParams *fp = &cp->filter_params[filter]; + ChannelParams *cp = &s->b[1].channel_params[channel]; + DecodingParams *dp1 = &s->b[1].decoding_params; + FilterParams *fp = &cp->filter_params[FIR]; - if ((filter == IIR && ctx->substream_info & SUBSTREAM_INFO_HIGH_RATE) || - clear_filter) { - fp->order = 0; - } else if (filter == IIR) { + if (retry_filter) + dp1->max_order[channel]--; + + if (dp1->max_order[channel] == 0) { fp->order = 0; - } else if (filter == FIR) { - const int max_order = (ctx->substream_info & SUBSTREAM_INFO_HIGH_RATE) - ? 4 : MLP_MAX_LPC_ORDER; - int32_t *sample_buffer = ctx->sample_buffer + channel; - int32_t coefs[MAX_LPC_ORDER][MAX_LPC_ORDER]; + } else { int32_t *lpc_samples = ctx->lpc_sample_buffer; - int32_t *fcoeff = ctx->cur_channel_params[channel].coeff[filter]; - int shift[MLP_MAX_LPC_ORDER]; + int32_t *fcoeff = cp->coeff[FIR]; + int shift[MAX_LPC_ORDER]; int order; - for (unsigned int i = 0; i < ctx->number_of_samples; i++) { - *lpc_samples++ = *sample_buffer; - sample_buffer += ctx->num_channels; + for (unsigned int j = 0; j <= ctx->cur_restart_interval; j++) { + DecodingParams *dp = &s->b[j].decoding_params; + int32_t *sample_buffer = dp->sample_buffer[channel]; + + for (unsigned int i = 0; i < dp->blocksize; i++) + lpc_samples[i] = sample_buffer[i]; + lpc_samples += dp->blocksize; } order = ff_lpc_calc_coefs(&ctx->lpc_ctx, ctx->lpc_sample_buffer, - ctx->number_of_samples, MLP_MIN_LPC_ORDER, - max_order, 11, coefs, shift, FF_LPC_TYPE_LEVINSON, 0, - ORDER_METHOD_EST, MLP_MIN_LPC_SHIFT, - MLP_MAX_LPC_SHIFT, MLP_MIN_LPC_SHIFT); + lpc_samples - ctx->lpc_sample_buffer, + MLP_MIN_LPC_ORDER, dp1->max_order[channel], + ctx->lpc_coeff_precision, + s->coefs[channel], shift, ctx->lpc_type, ctx->lpc_passes, + ctx->prediction_order, MLP_MIN_LPC_SHIFT, + MLP_MAX_LPC_SHIFT, 0); fp->order = order; - fp->shift = shift[order-1]; + fp->shift = order ? shift[order-1] : 0; for (unsigned int i = 0; i < order; i++) - fcoeff[i] = coefs[order-1][i]; + fcoeff[i] = s->coefs[channel][order-1][i]; code_filter_coeffs(ctx, fp, fcoeff); } @@ -1375,118 +1399,125 @@ static void set_filter_params(MLPEncodeContext *ctx, * buffer if the filter is good enough. Sets the filter data to be cleared if * no good filter was found. */ -static void determine_filters(MLPEncodeContext *ctx) +static void determine_filters(MLPEncodeContext *ctx, MLPSubstream *s) { - RestartHeader *rh = ctx->cur_restart_header; + RestartHeader *rh = s->cur_restart_header; - for (int channel = rh->min_channel; channel <= rh->max_channel; channel++) { - for (int filter = 0; filter < NUM_FILTERS; filter++) - set_filter_params(ctx, channel, filter, 0); - } + for (int ch = rh->min_channel; ch <= rh->max_channel; ch++) + set_filter(ctx, s, ch, 0); } -enum MLPChMode { - MLP_CHMODE_LEFT_RIGHT, - MLP_CHMODE_LEFT_SIDE, - MLP_CHMODE_RIGHT_SIDE, - MLP_CHMODE_MID_SIDE, -}; - -static enum MLPChMode estimate_stereo_mode(MLPEncodeContext *ctx) +static int estimate_coeff(MLPEncodeContext *ctx, MLPSubstream *s, + MatrixParams *mp, + int ch0, int ch1) { - uint64_t score[4], sum[4] = { 0, 0, 0, 0, }; - int32_t *right_ch = ctx->sample_buffer + 1; - int32_t *left_ch = ctx->sample_buffer; - int i; - enum MLPChMode best = 0; - - for(i = 2; i < ctx->number_of_samples; i++) { - int32_t left = left_ch [i * ctx->num_channels] - 2 * left_ch [(i - 1) * ctx->num_channels] + left_ch [(i - 2) * ctx->num_channels]; - int32_t right = right_ch[i * ctx->num_channels] - 2 * right_ch[(i - 1) * ctx->num_channels] + right_ch[(i - 2) * ctx->num_channels]; - - sum[0] += FFABS( left ); - sum[1] += FFABS( right); - sum[2] += FFABS((left + right) >> 1); - sum[3] += FFABS( left - right); + int32_t maxl = INT32_MIN, maxr = INT32_MIN, minl = INT32_MAX, minr = INT32_MAX; + int64_t summ = 0, sums = 0, suml = 0, sumr = 0, enl = 0, enr = 0; + const int shift = 14 - ctx->rematrix_precision; + int32_t cf0, cf1, e[4], d[4]; + int64_t ml, mr; + int i, count = 0; + + for (int j = 0; j <= ctx->cur_restart_interval; j++) { + DecodingParams *dp = &s->b[j].decoding_params; + const int32_t *ch[2]; + + ch[0] = dp->sample_buffer[ch0]; + ch[1] = dp->sample_buffer[ch1]; + + for (int i = 0; i < dp->blocksize; i++) { + int32_t lm = ch[0][i], rm = ch[1][i]; + + enl += FFABS(lm); + enr += FFABS(rm); + + summ += FFABS(lm + rm); + sums += FFABS(lm - rm); + + suml += lm; + sumr += rm; + + maxl = FFMAX(maxl, lm); + maxr = FFMAX(maxr, rm); + + minl = FFMIN(minl, lm); + minr = FFMIN(minr, rm); + } } - score[MLP_CHMODE_LEFT_RIGHT] = sum[0] + sum[1]; - score[MLP_CHMODE_LEFT_SIDE] = sum[0] + sum[3]; - score[MLP_CHMODE_RIGHT_SIDE] = sum[1] + sum[3]; - score[MLP_CHMODE_MID_SIDE] = sum[2] + sum[3]; + summ -= FFABS(suml + sumr); + sums -= FFABS(suml - sumr); + + ml = maxl - (int64_t)minl; + mr = maxr - (int64_t)minr; - for(i = 1; i < 3; i++) - if(score[i] < score[best]) - best = i; + if (!summ && !sums) + return 0; + + if (!ml || !mr) + return 0; - return best; + if ((FFABS(ml) + FFABS(mr)) >= (1 << 24)) + return 0; + + cf0 = (FFMIN(FFABS(mr), FFABS(ml)) * (1LL << 14)) / FFMAX(FFABS(ml), FFABS(mr)); + cf0 = (cf0 >> shift) << shift; + cf1 = -cf0; + + if (sums > summ) + FFSWAP(int32_t, cf0, cf1); + + count = 1; + i = enl < enr; + mp->outch[0] = ch0 + i; + + d[!i] = cf0; + d[ i] = 1 << 14; + e[!i] = cf1; + e[ i] = 1 << 14; + + mp->coeff[0][ch0] = av_clip_intp2(d[0], 15); + mp->coeff[0][ch1] = av_clip_intp2(d[1], 15); + + mp->forco[0][ch0] = av_clip_intp2(e[0], 15); + mp->forco[0][ch1] = av_clip_intp2(e[1], 15); + + return count; } /** Determines how many fractional bits are needed to encode matrix * coefficients. Also shifts the coefficients to fit within 2.14 bits. */ -static void code_matrix_coeffs(MLPEncodeContext *ctx, unsigned int mat) +static void code_matrix_coeffs(MLPEncodeContext *ctx, MLPSubstream *s, + DecodingParams *dp, + unsigned int mat) { - DecodingParams *dp = ctx->cur_decoding_params; + RestartHeader *rh = s->cur_restart_header; MatrixParams *mp = &dp->matrix_params; int32_t coeff_mask = 0; - unsigned int bits; - - for (unsigned int channel = 0; channel < ctx->num_channels; channel++) { - int32_t coeff = mp->coeff[mat][channel]; - coeff_mask |= coeff; - } - for (bits = 0; bits < 14 && !(coeff_mask & (1<max_matrix_channel; ch++) + coeff_mask |= mp->coeff[mat][ch]; - mp->fbits [mat] = 14 - bits; + mp->fbits[mat] = 14 - number_trailing_zeroes(coeff_mask, 14, 14); } /** Determines best coefficients to use for the lossless matrix. */ -static void lossless_matrix_coeffs(MLPEncodeContext *ctx) +static void lossless_matrix_coeffs(MLPEncodeContext *ctx, MLPSubstream *s) { - DecodingParams *dp = ctx->cur_decoding_params; + RestartHeader *rh = s->cur_restart_header; + DecodingParams *dp = &s->b[1].decoding_params; MatrixParams *mp = &dp->matrix_params; - unsigned int shift = 0; - enum MLPChMode mode; - /* No decorrelation for non-stereo. */ - if (ctx->num_channels - 2 != 2) { - mp->count = 0; + mp->count = 0; + if (ctx->num_channels - 2 != 2) return; - } - - mode = estimate_stereo_mode(ctx); - switch (mode) { - /* TODO: add matrix for MID_SIDE */ - case MLP_CHMODE_MID_SIDE: - case MLP_CHMODE_LEFT_RIGHT: - mp->count = 0; - break; - case MLP_CHMODE_LEFT_SIDE: - mp->count = 1; - mp->outch[0] = 1; - mp->coeff[0][0] = 1 << 14; mp->coeff[0][1] = -(1 << 14); - mp->coeff[0][2] = 0 << 14; mp->coeff[0][2] = 0 << 14; - mp->forco[0][0] = 1 << 14; mp->forco[0][1] = -(1 << 14); - mp->forco[0][2] = 0 << 14; mp->forco[0][2] = 0 << 14; - break; - case MLP_CHMODE_RIGHT_SIDE: - mp->count = 1; - mp->outch[0] = 0; - mp->coeff[0][0] = 1 << 14; mp->coeff[0][1] = 1 << 14; - mp->coeff[0][2] = 0 << 14; mp->coeff[0][2] = 0 << 14; - mp->forco[0][0] = 1 << 14; mp->forco[0][1] = -(1 << 14); - mp->forco[0][2] = 0 << 14; mp->forco[0][2] = 0 << 14; - break; - } + mp->count = estimate_coeff(ctx, s, mp, + rh->min_channel, rh->max_channel); for (int mat = 0; mat < mp->count; mat++) - code_matrix_coeffs(ctx, mat); - - for (unsigned int channel = 0; channel < ctx->num_channels; channel++) - mp->shift[channel] = shift; + code_matrix_coeffs(ctx, s, dp, mat); } /** Min and max values that can be encoded with each codebook. The values for @@ -1494,7 +1525,7 @@ static void lossless_matrix_coeffs(MLPEncodeContext *ctx) * codebook is outside the coded value, so it has one more bit of precision. * It should actually be -7 -> 7, shifted down by 0.5. */ -static const int codebook_extremes[3][2] = { +static const int8_t codebook_extremes[3][2] = { {-9, 8}, {-8, 7}, {-15, 14}, }; @@ -1502,11 +1533,11 @@ static const int codebook_extremes[3][2] = { * codebooks and a specified offset. */ static void no_codebook_bits_offset(MLPEncodeContext *ctx, - unsigned int channel, int16_t offset, + DecodingParams *dp, + int channel, int32_t offset, int32_t min, int32_t max, BestOffset *bo) { - DecodingParams *dp = ctx->cur_decoding_params; int32_t unsign = 0; int lsb_bits; @@ -1518,7 +1549,7 @@ static void no_codebook_bits_offset(MLPEncodeContext *ctx, lsb_bits += !!lsb_bits; if (lsb_bits > 0) - unsign = 1 << (lsb_bits - 1); + unsign = 1U << (lsb_bits - 1); bo->offset = offset; bo->lsb_bits = lsb_bits; @@ -1531,15 +1562,13 @@ static void no_codebook_bits_offset(MLPEncodeContext *ctx, * codebooks. */ static void no_codebook_bits(MLPEncodeContext *ctx, - unsigned int channel, + DecodingParams *dp, + int channel, int32_t min, int32_t max, BestOffset *bo) { - DecodingParams *dp = ctx->cur_decoding_params; - int16_t offset; - int32_t unsign = 0; - uint32_t diff; - int lsb_bits; + int32_t offset, unsign = 0; + uint8_t lsb_bits; /* Set offset inside huffoffset's boundaries by adjusting extremes * so that more bits are used, thus shifting the offset. */ @@ -1548,40 +1577,40 @@ static void no_codebook_bits(MLPEncodeContext *ctx, if (max > HUFF_OFFSET_MAX) min = FFMIN(min, 2 * HUFF_OFFSET_MAX - max - 1); - /* Determine offset and minimum number of bits. */ - diff = max - min; - - lsb_bits = number_sbits(diff) - 1; + lsb_bits = FFMAX(number_sbits(min), number_sbits(max)); if (lsb_bits > 0) unsign = 1 << (lsb_bits - 1); /* If all samples are the same (lsb_bits == 0), offset must be * adjusted because of sign_shift. */ - offset = min + diff / 2 + !!lsb_bits; + offset = min + (max - min) / 2 + !!lsb_bits; bo->offset = offset; bo->lsb_bits = lsb_bits; bo->bitcount = lsb_bits * dp->blocksize; bo->min = max - unsign + 1; bo->max = min + unsign; + bo->min = FFMAX(bo->min, HUFF_OFFSET_MIN); + bo->max = FFMIN(bo->max, HUFF_OFFSET_MAX); } /** Determines the least amount of bits needed to encode the samples using a * given codebook and a given offset. */ static inline void codebook_bits_offset(MLPEncodeContext *ctx, - unsigned int channel, int codebook, + DecodingParams *dp, + int channel, int codebook, int32_t sample_min, int32_t sample_max, - int16_t offset, BestOffset *bo) + int32_t offset, BestOffset *bo) { int32_t codebook_min = codebook_extremes[codebook][0]; int32_t codebook_max = codebook_extremes[codebook][1]; - int32_t *sample_buffer = ctx->sample_buffer + channel; - DecodingParams *dp = ctx->cur_decoding_params; + int32_t *sample_buffer = dp->sample_buffer[channel]; int codebook_offset = 7 + (2 - codebook); int32_t unsign_offset = offset; - int lsb_bits = 0, bitcount = 0; + uint32_t bitcount = 0; + int lsb_bits = 0; int offset_min = INT_MAX, offset_max = INT_MAX; int unsign, mask; @@ -1603,7 +1632,7 @@ static inline void codebook_bits_offset(MLPEncodeContext *ctx, } for (int i = 0; i < dp->blocksize; i++) { - int32_t sample = *sample_buffer >> dp->quant_step_size[channel]; + int32_t sample = sample_buffer[i] >> dp->quant_step_size[channel]; int temp_min, temp_max; sample -= unsign_offset; @@ -1619,8 +1648,6 @@ static inline void codebook_bits_offset(MLPEncodeContext *ctx, sample >>= lsb_bits; bitcount += ff_mlp_huffman_tables[codebook][sample + codebook_offset][1]; - - sample_buffer += ctx->num_channels; } bo->offset = offset; @@ -1634,11 +1661,12 @@ static inline void codebook_bits_offset(MLPEncodeContext *ctx, * given codebook. Searches for the best offset to minimize the bits. */ static inline void codebook_bits(MLPEncodeContext *ctx, - unsigned int channel, int codebook, + DecodingParams *dp, + int channel, int codebook, int offset, int32_t min, int32_t max, BestOffset *bo, int direction) { - int previous_count = INT_MAX; + uint32_t previous_count = UINT32_MAX; int offset_min, offset_max; int is_greater = 0; @@ -1648,7 +1676,7 @@ static inline void codebook_bits(MLPEncodeContext *ctx, while (offset <= offset_max && offset >= offset_min) { BestOffset temp_bo; - codebook_bits_offset(ctx, channel, codebook, + codebook_bits_offset(ctx, dp, channel, codebook, min, max, offset, &temp_bo); @@ -1673,58 +1701,59 @@ static inline void codebook_bits(MLPEncodeContext *ctx, /** Determines the least amount of bits needed to encode the samples using * any or no codebook. */ -static void determine_bits(MLPEncodeContext *ctx) +static void determine_bits(MLPEncodeContext *ctx, MLPSubstream *s) { - DecodingParams *dp = ctx->cur_decoding_params; - RestartHeader *rh = ctx->cur_restart_header; - - for (unsigned int channel = 0; channel <= rh->max_channel; channel++) { - ChannelParams *cp = &ctx->cur_channel_params[channel]; - int32_t *sample_buffer = ctx->sample_buffer + channel; - int32_t min = INT32_MAX, max = INT32_MIN; - int no_filters_used = !cp->filter_params[FIR].order; - int average = 0; - int offset = 0; - - /* Determine extremes and average. */ - for (int i = 0; i < dp->blocksize; i++) { - int32_t sample = *sample_buffer >> dp->quant_step_size[channel]; - if (sample < min) - min = sample; - if (sample > max) - max = sample; - average += sample; - sample_buffer += ctx->num_channels; - } - average /= dp->blocksize; + RestartHeader *rh = s->cur_restart_header; + for (unsigned int index = 0; index < ctx->number_of_subblocks; index++) { + DecodingParams *dp = &s->b[index].decoding_params; + + for (int ch = rh->min_channel; ch <= rh->max_channel; ch++) { + ChannelParams *cp = &s->b[index].channel_params[ch]; + int32_t *sample_buffer = dp->sample_buffer[ch]; + int32_t min = INT32_MAX, max = INT32_MIN; + int no_filters_used = !cp->filter_params[FIR].order; + int average = 0; + int offset = 0; + + /* Determine extremes and average. */ + for (int i = 0; i < dp->blocksize; i++) { + int32_t sample = sample_buffer[i] >> dp->quant_step_size[ch]; + if (sample < min) + min = sample; + if (sample > max) + max = sample; + average += sample; + } + average /= dp->blocksize; - /* If filtering is used, we always set the offset to zero, otherwise - * we search for the offset that minimizes the bitcount. */ - if (no_filters_used) { - no_codebook_bits(ctx, channel, min, max, &ctx->cur_best_offset[channel][0]); - offset = av_clip(average, HUFF_OFFSET_MIN, HUFF_OFFSET_MAX); - } else { - no_codebook_bits_offset(ctx, channel, offset, min, max, &ctx->cur_best_offset[channel][0]); - } + /* If filtering is used, we always set the offset to zero, otherwise + * we search for the offset that minimizes the bitcount. */ + if (no_filters_used) { + no_codebook_bits(ctx, dp, ch, min, max, &s->b[index].best_offset[ch][0]); + offset = av_clip(average, HUFF_OFFSET_MIN, HUFF_OFFSET_MAX); + } else { + no_codebook_bits_offset(ctx, dp, ch, offset, min, max, &s->b[index].best_offset[ch][0]); + } - for (int i = 1; i < NUM_CODEBOOKS; i++) { - BestOffset temp_bo = { 0, INT_MAX, 0, 0, 0, }; - int16_t offset_max; + for (int i = 1; i < NUM_CODEBOOKS; i++) { + BestOffset temp_bo = { 0, UINT32_MAX, 0, 0, 0, }; + int32_t offset_max; - codebook_bits_offset(ctx, channel, i - 1, - min, max, offset, - &temp_bo); + codebook_bits_offset(ctx, dp, ch, i - 1, + min, max, offset, + &temp_bo); - if (no_filters_used) { - offset_max = temp_bo.max; + if (no_filters_used) { + offset_max = temp_bo.max; - codebook_bits(ctx, channel, i - 1, temp_bo.min - 1, - min, max, &temp_bo, 0); - codebook_bits(ctx, channel, i - 1, offset_max + 1, - min, max, &temp_bo, 1); - } + codebook_bits(ctx, dp, ch, i - 1, temp_bo.min - 1, + min, max, &temp_bo, 0); + codebook_bits(ctx, dp, ch, i - 1, offset_max + 1, + min, max, &temp_bo, 1); + } - ctx->cur_best_offset[channel][i] = temp_bo; + s->b[index].best_offset[ch][i] = temp_bo; + } } } } @@ -1743,130 +1772,177 @@ static void determine_bits(MLPEncodeContext *ctx) * maximum amount of bits allowed (24), the samples buffer is left as is and * the function returns -1. */ -static int apply_filter(MLPEncodeContext *ctx, unsigned int channel) +static int apply_filter(MLPEncodeContext *ctx, MLPSubstream *s, int channel) { - FilterParams *fp[NUM_FILTERS] = { &ctx->cur_channel_params[channel].filter_params[FIR], - &ctx->cur_channel_params[channel].filter_params[IIR], }; - int32_t mask = MSB_MASK(ctx->cur_decoding_params->quant_step_size[channel]); - int32_t *sample_buffer = ctx->sample_buffer + channel; - unsigned int number_of_samples = ctx->number_of_samples; + DecodingParams *dp = &s->b[1].decoding_params; + ChannelParams *cp = &s->b[1].channel_params[channel]; + FilterParams *fp[NUM_FILTERS] = { &cp->filter_params[FIR], + &cp->filter_params[IIR], }; + const uint8_t codebook = cp->codebook; + int32_t mask = MSB_MASK(dp->quant_step_size[channel]); + int32_t *sample_buffer = s->b[0].decoding_params.sample_buffer[channel]; unsigned int filter_shift = fp[FIR]->shift; - int ret = 0; - - for (int i = 0; i < 8; i++) { - ctx->filter_state_buffer[FIR][i] = *sample_buffer; - ctx->filter_state_buffer[IIR][i] = *sample_buffer; + int32_t *filter_state[NUM_FILTERS] = { ctx->filter_state[FIR], + ctx->filter_state[IIR], }; + int i, j = 1, k = 0; - sample_buffer += ctx->num_channels; + for (i = 0; i < 8; i++) { + filter_state[FIR][i] = sample_buffer[i]; + filter_state[IIR][i] = sample_buffer[i]; } - for (int i = 8; i < number_of_samples; i++) { - int32_t sample = *sample_buffer; + while (1) { + int32_t *sample_buffer = s->b[j].decoding_params.sample_buffer[channel]; + unsigned int blocksize = s->b[j].decoding_params.blocksize; + int32_t sample, residual; int64_t accum = 0; - int64_t residual; + + if (!blocksize) + break; for (int filter = 0; filter < NUM_FILTERS; filter++) { - int32_t *fcoeff = ctx->cur_channel_params[channel].coeff[filter]; + int32_t *fcoeff = cp->coeff[filter]; for (unsigned int order = 0; order < fp[filter]->order; order++) - accum += (int64_t)ctx->filter_state_buffer[filter][i - 1 - order] * - fcoeff[order]; + accum += (int64_t)filter_state[filter][i - 1 - order] * + fcoeff[order]; } + sample = sample_buffer[k]; accum >>= filter_shift; residual = sample - (accum & mask); - if (residual < SAMPLE_MIN(24) || residual > SAMPLE_MAX(24)) { - ret = AVERROR_INVALIDDATA; - return ret; - } + if ((codebook > 0) && + (residual < SAMPLE_MIN(24) || + residual > SAMPLE_MAX(24))) + return -1; - ctx->filter_state_buffer[FIR][i] = sample; - ctx->filter_state_buffer[IIR][i] = (int32_t) residual; + filter_state[FIR][i] = sample; + filter_state[IIR][i] = residual; - sample_buffer += ctx->num_channels; + i++; + k++; + if (k >= blocksize) { + k = 0; + j++; + if (j > ctx->cur_restart_interval) + break; + } } - sample_buffer = ctx->sample_buffer + channel; - for (int i = 0; i < number_of_samples; i++) { - *sample_buffer = ctx->filter_state_buffer[IIR][i]; + for (int l = 0, j = 0; j <= ctx->cur_restart_interval; j++) { + int32_t *sample_buffer = s->b[j].decoding_params.sample_buffer[channel]; + unsigned int blocksize = s->b[j].decoding_params.blocksize; - sample_buffer += ctx->num_channels; + for (int i = 0; i < blocksize; i++, l++) + sample_buffer[i] = filter_state[IIR][l]; } - return ret; + return 0; } -static void apply_filters(MLPEncodeContext *ctx) +static void apply_filters(MLPEncodeContext *ctx, MLPSubstream *s) { - RestartHeader *rh = ctx->cur_restart_header; + RestartHeader *rh = s->cur_restart_header; - for (int channel = rh->min_channel; channel <= rh->max_channel; channel++) { - if (apply_filter(ctx, channel) < 0) { - /* Filter is horribly wrong. - * Clear filter params and update state. */ - set_filter_params(ctx, channel, FIR, 1); - set_filter_params(ctx, channel, IIR, 1); - apply_filter(ctx, channel); + for (int ch = rh->min_channel; ch <= rh->max_channel; ch++) { + while (apply_filter(ctx, s, ch) < 0) { + /* Filter is horribly wrong. Retry. */ + set_filter(ctx, s, ch, 1); } } } /** Generates two noise channels worth of data. */ -static void generate_2_noise_channels(MLPEncodeContext *ctx) +static void generate_2_noise_channels(MLPEncodeContext *ctx, MLPSubstream *s) { - int32_t *sample_buffer = ctx->sample_buffer + ctx->num_channels - 2; - RestartHeader *rh = ctx->cur_restart_header; + RestartHeader *rh = s->cur_restart_header; uint32_t seed = rh->noisegen_seed; - for (unsigned int i = 0; i < ctx->number_of_samples; i++) { - uint16_t seed_shr7 = seed >> 7; - *sample_buffer++ = ((int8_t)(seed >> 15)) * (1 << rh->noise_shift); - *sample_buffer++ = ((int8_t) seed_shr7) * (1 << rh->noise_shift); + for (unsigned int j = 0; j <= ctx->cur_restart_interval; j++) { + DecodingParams *dp = &s->b[j].decoding_params; + int32_t *sample_buffer2 = dp->sample_buffer[ctx->num_channels-2]; + int32_t *sample_buffer1 = dp->sample_buffer[ctx->num_channels-1]; - seed = (seed << 16) ^ seed_shr7 ^ (seed_shr7 << 5); + for (unsigned int i = 0; i < dp->blocksize; i++) { + uint16_t seed_shr7 = seed >> 7; + sample_buffer2[i] = ((int8_t)(seed >> 15)) * (1 << rh->noise_shift); + sample_buffer1[i] = ((int8_t) seed_shr7) * (1 << rh->noise_shift); - sample_buffer += ctx->num_channels - 2; + seed = (seed << 16) ^ seed_shr7 ^ (seed_shr7 << 5); + } } rh->noisegen_seed = seed & ((1 << 24)-1); } /** Rematrixes all channels using chosen coefficients. */ -static void rematrix_channels(MLPEncodeContext *ctx) +static void rematrix_channels(MLPEncodeContext *ctx, MLPSubstream *s) { - DecodingParams *dp = ctx->cur_decoding_params; - MatrixParams *mp = &dp->matrix_params; - int32_t *sample_buffer = ctx->sample_buffer; - unsigned int maxchan = ctx->num_channels; + RestartHeader *rh = s->cur_restart_header; + DecodingParams *dp1 = &s->b[1].decoding_params; + MatrixParams *mp1 = &dp1->matrix_params; + const int maxchan = rh->max_matrix_channel; + int32_t orig_samples[MAX_NCHANNELS]; + int32_t rematrix_samples[MAX_NCHANNELS]; + uint8_t lsb_bypass[MAX_MATRICES] = { 0 }; + + for (unsigned int j = 0; j <= ctx->cur_restart_interval; j++) { + DecodingParams *dp = &s->b[j].decoding_params; + MatrixParams *mp = &dp->matrix_params; + + for (unsigned int i = 0; i < dp->blocksize; i++) { + for (int ch = 0; ch <= maxchan; ch++) + orig_samples[ch] = rematrix_samples[ch] = dp->sample_buffer[ch][i]; + + for (int mat = 0; mat < mp1->count; mat++) { + unsigned int outch = mp1->outch[mat]; + int64_t accum = 0; + + for (int ch = 0; ch <= maxchan; ch++) { + int32_t sample = rematrix_samples[ch]; + + accum += (int64_t)sample * mp1->forco[mat][ch]; + } - for (unsigned int mat = 0; mat < mp->count; mat++) { - unsigned int msb_mask_bits = (ctx->avctx->sample_fmt == AV_SAMPLE_FMT_S16 ? 8 : 0) - mp->shift[mat]; - int32_t mask = MSB_MASK(msb_mask_bits); - unsigned int outch = mp->outch[mat]; + rematrix_samples[outch] = accum >> 14; + } - sample_buffer = ctx->sample_buffer; - for (unsigned int i = 0; i < ctx->number_of_samples; i++) { - int64_t accum = 0; + for (int ch = 0; ch <= maxchan; ch++) + dp->sample_buffer[ch][i] = rematrix_samples[ch]; - for (unsigned int src_ch = 0; src_ch < maxchan; src_ch++) { - int32_t sample = *(sample_buffer + src_ch); - accum += (int64_t) sample * mp->forco[mat][src_ch]; - } - sample_buffer[outch] = (accum >> 14) & mask; + for (unsigned int mat = 0; mat < mp1->count; mat++) { + int8_t *bypassed_lsbs = mp->bypassed_lsbs[mat]; + unsigned int outch = mp1->outch[mat]; + int64_t accum = 0; + int8_t bit; + + for (int ch = 0; ch <= maxchan; ch++) { + int32_t sample = rematrix_samples[ch]; + + accum += (int64_t)sample * mp1->coeff[mat][ch]; + } - sample_buffer += ctx->num_channels; + rematrix_samples[outch] = accum >> 14; + bit = rematrix_samples[outch] != orig_samples[outch]; + + bypassed_lsbs[i] = bit; + lsb_bypass[mat] |= bit; + } } } + + for (unsigned int mat = 0; mat < mp1->count; mat++) + mp1->lsb_bypass[mat] = lsb_bypass[mat]; } /**************************************************************************** **** Functions that deal with determining the best parameters and output *** ****************************************************************************/ -typedef struct { - char path[MAJOR_HEADER_INTERVAL + 2]; +typedef struct PathCounter { + char path[MAX_HEADER_INTERVAL + 2]; int cur_idx; - int bitcount; + uint32_t bitcount; } PathCounter; #define CODEBOOK_CHANGE_BITS 21 @@ -1881,14 +1957,15 @@ static int compare_best_offset(const BestOffset *prev, const BestOffset *cur) return prev->lsb_bits != cur->lsb_bits; } -static int best_codebook_path_cost(MLPEncodeContext *ctx, unsigned int channel, - PathCounter *src, int cur_codebook) +static uint32_t best_codebook_path_cost(MLPEncodeContext *ctx, MLPSubstream *s, + int channel, + PathCounter *src, int cur_codebook) { int idx = src->cur_idx; - const BestOffset *cur_bo = ctx->best_offset[idx][channel], - *prev_bo = idx ? ctx->best_offset[idx - 1][channel] : + const BestOffset *cur_bo = s->b[idx].best_offset[channel], + *prev_bo = idx ? s->b[idx - 1].best_offset[channel] : restart_best_offset; - int bitcount = src->bitcount; + uint32_t bitcount = src->bitcount; int prev_codebook = src->path[idx]; bitcount += cur_bo[cur_codebook].bitcount; @@ -1900,12 +1977,11 @@ static int best_codebook_path_cost(MLPEncodeContext *ctx, unsigned int channel, return bitcount; } -static void set_best_codebook(MLPEncodeContext *ctx) +static void set_best_codebook(MLPEncodeContext *ctx, MLPSubstream *s) { - DecodingParams *dp = ctx->cur_decoding_params; - RestartHeader *rh = ctx->cur_restart_header; + RestartHeader *rh = s->cur_restart_header; - for (unsigned int channel = rh->min_channel; channel <= rh->max_channel; channel++) { + for (int channel = rh->min_channel; channel <= rh->max_channel; channel++) { const BestOffset *prev_bo = restart_best_offset; BestOffset *cur_bo; PathCounter path_counter[NUM_CODEBOOKS + 1]; @@ -1915,17 +1991,17 @@ static void set_best_codebook(MLPEncodeContext *ctx) clear_path_counter(path_counter); for (unsigned int index = 0; index < ctx->number_of_subblocks; index++) { - unsigned int best_bitcount = INT_MAX; + uint32_t best_bitcount = UINT32_MAX; - cur_bo = ctx->best_offset[index][channel]; + cur_bo = s->b[index].best_offset[channel]; for (unsigned int codebook = 0; codebook < NUM_CODEBOOKS; codebook++) { - int prev_best_bitcount = INT_MAX; + uint32_t prev_best_bitcount = UINT32_MAX; for (unsigned int last_best = 0; last_best < 2; last_best++) { PathCounter *dst_path = &path_counter[codebook]; PathCounter *src_path; - int temp_bitcount; + uint32_t temp_bitcount; /* First test last path with same headers, * then with last best. */ @@ -1938,7 +2014,7 @@ static void set_best_codebook(MLPEncodeContext *ctx) src_path = &path_counter[codebook]; } - temp_bitcount = best_codebook_path_cost(ctx, channel, src_path, codebook); + temp_bitcount = best_codebook_path_cost(ctx, s, channel, src_path, codebook); if (temp_bitcount < best_bitcount) { best_bitcount = temp_bitcount; @@ -1965,10 +2041,11 @@ static void set_best_codebook(MLPEncodeContext *ctx) /* Update context. */ for (unsigned int index = 0; index < ctx->number_of_subblocks; index++) { - ChannelParams *cp = ctx->seq_channel_params + index*(ctx->avctx->ch_layout.nb_channels) + channel; + ChannelParams *cp = &s->b[index].channel_params[channel]; + DecodingParams *dp = &s->b[index].decoding_params; best_codebook = *best_path++; - cur_bo = &ctx->best_offset[index][channel][best_codebook]; + cur_bo = &s->b[index].best_offset[channel][best_codebook]; cp->huff_offset = cur_bo->offset; cp->huff_lsbs = cur_bo->lsb_bits + dp->quant_step_size[channel]; @@ -1981,113 +2058,92 @@ static void set_best_codebook(MLPEncodeContext *ctx) * individual access unit. * TODO This is just a stub! */ -static void set_major_params(MLPEncodeContext *ctx) +static void set_major_params(MLPEncodeContext *ctx, MLPSubstream *s) { - RestartHeader *rh = ctx->cur_restart_header; - uint8_t max_huff_lsbs = 0; - uint8_t max_output_bits = 0; - int channels = ctx->avctx->ch_layout.nb_channels; - DecodingParams *seq_dp = ctx->decoding_params + ctx->seq_offset[0] * channels; - ChannelParams *seq_cp = ctx->channel_params + ctx->seq_offset[0] * channels; - - for (unsigned int index = 0; index < ctx->seq_size[ctx->restart_intervals-1]; index++) { - memcpy(&ctx->major_decoding_params[index], seq_dp + index, sizeof(DecodingParams)); - for (unsigned int channel = 0; channel < channels; channel++) { - uint8_t huff_lsbs = (seq_cp + index*(channels) + channel)->huff_lsbs; - if (max_huff_lsbs < huff_lsbs) - max_huff_lsbs = huff_lsbs; - memcpy(&ctx->major_channel_params[index][channel], - (seq_cp + index*(channels) + channel), + RestartHeader *rh = s->cur_restart_header; + uint8_t max_huff_lsbs = 0, max_output_bits = 0; + int8_t max_shift = 0; + + for (int index = 0; index < s->b[ctx->restart_intervals-1].seq_size; index++) { + memcpy(&s->b[index].major_decoding_params, + &s->b[index].decoding_params, sizeof(DecodingParams)); + for (int ch = 0; ch <= rh->max_matrix_channel; ch++) { + int8_t shift = s->b[index].decoding_params.output_shift[ch]; + + max_shift = FFMAX(max_shift, shift); + } + for (int ch = rh->min_channel; ch <= rh->max_channel; ch++) { + uint8_t huff_lsbs = s->b[index].channel_params[ch].huff_lsbs; + + max_huff_lsbs = FFMAX(max_huff_lsbs, huff_lsbs); + + memcpy(&s->b[index].major_channel_params[ch], + &s->b[index].channel_params[ch], sizeof(ChannelParams)); } } rh->max_huff_lsbs = max_huff_lsbs; + rh->max_shift = max_shift; - for (unsigned int index = 0; index < ctx->number_of_frames; index++) - if (max_output_bits < ctx->max_output_bits[index]) - max_output_bits = ctx->max_output_bits[index]; + for (int index = 0; index < ctx->number_of_frames; index++) + if (max_output_bits < s->b[index].max_output_bits) + max_output_bits = s->b[index].max_output_bits; rh->max_output_bits = max_output_bits; - ctx->cur_restart_header = &ctx->restart_header; - - ctx->prev_decoding_params = restart_decoding_params; - ctx->prev_channel_params = restart_channel_params; + s->cur_restart_header = &s->restart_header; - for (unsigned int index = 0; index < MAJOR_HEADER_INTERVAL + 1; index++) { - ctx->cur_decoding_params = &ctx->major_decoding_params[index]; - ctx->cur_channel_params = ctx->major_channel_params[index]; - - ctx->major_params_changed[index] = compare_decoding_params(ctx); - - ctx->prev_decoding_params = ctx->cur_decoding_params; - ctx->prev_channel_params = ctx->cur_channel_params; - } + for (int index = 0; index <= ctx->cur_restart_interval; index++) + s->b[index].major_params_changed = compare_decoding_params(ctx, s, index); - ctx->major_number_of_subblocks = ctx->number_of_subblocks; - ctx->major_filter_state_subblock = 1; - ctx->major_cur_subblock_index = 0; + s->major_filter_state_subblock = 1; + s->major_cur_subblock_index = 0; } -static void analyze_sample_buffer(MLPEncodeContext *ctx) +static void analyze_sample_buffer(MLPEncodeContext *ctx, MLPSubstream *s) { - ChannelParams *seq_cp = ctx->seq_channel_params; - DecodingParams *seq_dp = ctx->seq_decoding_params; - - ctx->cur_restart_header = &ctx->restart_header; - ctx->cur_decoding_params = seq_dp + 1; - ctx->cur_channel_params = seq_cp + ctx->avctx->ch_layout.nb_channels; - - determine_quant_step_size(ctx); - generate_2_noise_channels(ctx); - lossless_matrix_coeffs (ctx); - rematrix_channels (ctx); - determine_filters (ctx); - apply_filters (ctx); - - copy_restart_frame_params(ctx); + s->cur_restart_header = &s->restart_header; /* Copy frame_size from frames 0...max to decoding_params 1...max + 1 * decoding_params[0] is for the filter state subblock. */ for (unsigned int index = 0; index < ctx->number_of_frames; index++) { - DecodingParams *dp = seq_dp + (index + 1); + DecodingParams *dp = &s->b[index+1].decoding_params; dp->blocksize = ctx->avctx->frame_size; } /* The official encoder seems to always encode a filter state subblock * even if there are no filters. TODO check if it is possible to skip * the filter state subblock for no filters. */ - (seq_dp + 0)->blocksize = 8; - (seq_dp + 1)->blocksize -= 8; + s->b[0].decoding_params.blocksize = 8; + s->b[1].decoding_params.blocksize -= 8; - for (unsigned int index = 0; index < ctx->number_of_subblocks; index++) { - ctx->cur_decoding_params = seq_dp + index; - ctx->cur_channel_params = seq_cp + index*(ctx->avctx->ch_layout.nb_channels); - ctx->cur_best_offset = ctx->best_offset[index]; - determine_bits(ctx); - ctx->sample_buffer += ctx->cur_decoding_params->blocksize * ctx->num_channels; - } + input_to_sample_buffer (ctx, s); + determine_output_shift (ctx, s); + generate_2_noise_channels(ctx, s); + lossless_matrix_coeffs (ctx, s); + rematrix_channels (ctx, s); + determine_quant_step_size(ctx, s); + determine_filters (ctx, s); + apply_filters (ctx, s); + + copy_restart_frame_params(ctx, s); + + determine_bits(ctx, s); - set_best_codebook(ctx); + set_best_codebook(ctx, s); } -static void process_major_frame(MLPEncodeContext *ctx) +static void process_major_frame(MLPEncodeContext *ctx, MLPSubstream *s) { - ctx->sample_buffer = ctx->major_inout_buffer; - ctx->number_of_frames = ctx->major_number_of_frames; - ctx->number_of_samples = ctx->major_frame_size; - - ctx->cur_restart_header = &ctx->restart_header; - ctx->cur_decoding_params = &ctx->major_decoding_params[1]; - ctx->cur_channel_params = ctx->major_channel_params[1]; + s->cur_restart_header = &s->restart_header; - generate_2_noise_channels(ctx); - rematrix_channels (ctx); + generate_2_noise_channels(ctx, s); + rematrix_channels (ctx, s); - apply_filters(ctx); + apply_filters(ctx, s); } /****************************************************************************/ @@ -2118,17 +2174,9 @@ static int mlp_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, data = frame ? frame->data[0] : NULL; - ctx->frame_index = avctx->frame_num % ctx->max_restart_interval; - - ctx->inout_buffer = ctx->major_inout_buffer - + ctx->frame_index * ctx->one_sample_buffer_size; - - ctx->sample_buffer = ctx->major_scratch_buffer - + ctx->frame_index * ctx->one_sample_buffer_size; + ctx->frame_index = avctx->frame_num % ctx->cur_restart_interval; - ctx->write_buffer = ctx->inout_buffer; - - if (avctx->frame_num < ctx->max_restart_interval) { + if (avctx->frame_num < ctx->cur_restart_interval) { if (data) goto input_and_return; } @@ -2137,13 +2185,12 @@ static int mlp_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, if (restart_frame) { avpkt->flags |= AV_PKT_FLAG_KEY; - set_major_params(ctx); - if (ctx->min_restart_interval != ctx->max_restart_interval) - process_major_frame(ctx); - } + for (int n = 0; n < ctx->num_substreams; n++) + set_major_params(ctx, &ctx->s[n]); - if (ctx->min_restart_interval == ctx->max_restart_interval) - ctx->write_buffer = ctx->sample_buffer; + if (ctx->min_restart_interval != ctx->cur_restart_interval) + process_major_frame(ctx, &ctx->s[0]); + } bytes_written = write_access_unit(ctx, avpkt->data, avpkt->size, restart_frame); @@ -2158,7 +2205,8 @@ static int mlp_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, ctx->next_major_number_of_frames++; } if (data) - input_data(ctx, data, frame->nb_samples); + for (int n = 0; n < ctx->num_substreams; n++) + input_data(ctx, &ctx->s[n], frame->extended_data, frame->nb_samples); restart_frame = (ctx->frame_index + 1) % ctx->min_restart_interval; @@ -2166,31 +2214,27 @@ static int mlp_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, for (unsigned int seq_index = 0; seq_index < ctx->restart_intervals; seq_index++) { unsigned int number_of_samples; - ctx->sample_buffer = ctx->major_scratch_buffer; - ctx->inout_buffer = ctx->major_inout_buffer; - ctx->number_of_frames = ctx->next_major_number_of_frames; ctx->number_of_subblocks = ctx->next_major_number_of_frames + 1; - ctx->seq_channel_params = ctx->channel_params + ctx->seq_offset[seq_index] * channels; - - ctx->seq_decoding_params = ctx->decoding_params + ctx->seq_offset[seq_index]; - number_of_samples = avctx->frame_size * ctx->number_of_frames; - ctx->number_of_samples = number_of_samples; - for (unsigned int index = 0; index < ctx->seq_size[seq_index]; index++) { - clear_channel_params(ctx->seq_channel_params + index * channels, channels); - default_decoding_params(ctx, ctx->seq_decoding_params + index); - } + for (int n = 0; n < ctx->num_substreams; n++) { + MLPSubstream *s = &ctx->s[n]; - input_to_sample_buffer(ctx); + for (int i = 0; i < s->b[seq_index].seq_size; i++) { + clear_channel_params(s->b[i].channel_params, channels); + default_decoding_params(ctx, &s->b[i].decoding_params); + } + } - if (number_of_samples > 0) - analyze_sample_buffer(ctx); + if (number_of_samples > 0) { + for (int n = 0; n < ctx->num_substreams; n++) + analyze_sample_buffer(ctx, &ctx->s[n]); + } } - if (ctx->frame_index == (ctx->max_restart_interval - 1)) { + if (ctx->frame_index == (ctx->cur_restart_interval - 1)) { ctx->major_frame_size = ctx->next_major_frame_size; ctx->next_major_frame_size = 0; ctx->major_number_of_frames = ctx->next_major_number_of_frames; @@ -2198,7 +2242,7 @@ static int mlp_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, } } - if (!frame && ctx->last_frames < ctx->max_restart_interval - 1) + if (!frame && ctx->last_frames < ctx->cur_restart_interval - 1) avctx->frame_num++; if (bytes_written > 0) { @@ -2222,22 +2266,35 @@ static av_cold int mlp_encode_close(AVCodecContext *avctx) MLPEncodeContext *ctx = avctx->priv_data; ff_lpc_end(&ctx->lpc_ctx); - - av_freep(&ctx->lossless_check_data); - av_freep(&ctx->major_scratch_buffer); - av_freep(&ctx->major_inout_buffer); - av_freep(&ctx->lpc_sample_buffer); - av_freep(&ctx->decoding_params); - av_freep(&ctx->channel_params); - av_freep(&ctx->max_output_bits); ff_af_queue_close(&ctx->afq); - for (int i = 0; i < NUM_FILTERS; i++) - av_freep(&ctx->filter_state_buffer[i]); - return 0; } +#define FLAGS AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM +#define OFFSET(x) offsetof(MLPEncodeContext, x) +static const AVOption mlp_options[] = { +{ "max_interval", "Max number of frames between each new header", OFFSET(max_restart_interval), AV_OPT_TYPE_INT, {.i64 = 16 }, MIN_HEADER_INTERVAL, MAX_HEADER_INTERVAL, FLAGS }, +{ "lpc_coeff_precision", "LPC coefficient precision", OFFSET(lpc_coeff_precision), AV_OPT_TYPE_INT, {.i64 = 15 }, 0, 15, FLAGS }, +{ "lpc_type", "LPC algorithm", OFFSET(lpc_type), AV_OPT_TYPE_INT, {.i64 = FF_LPC_TYPE_LEVINSON }, FF_LPC_TYPE_LEVINSON, FF_LPC_TYPE_CHOLESKY, FLAGS, .unit = "lpc_type" }, +{ "levinson", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LPC_TYPE_LEVINSON }, 0, 0, FLAGS, .unit = "lpc_type" }, +{ "cholesky", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LPC_TYPE_CHOLESKY }, 0, 0, FLAGS, .unit = "lpc_type" }, +{ "lpc_passes", "Number of passes to use for Cholesky factorization during LPC analysis", OFFSET(lpc_passes), AV_OPT_TYPE_INT, {.i64 = 2 }, 1, INT_MAX, FLAGS }, +{ "codebook_search", "Max number of codebook searches", OFFSET(max_codebook_search), AV_OPT_TYPE_INT, {.i64 = 3 }, 1, 100, FLAGS }, +{ "prediction_order", "Search method for selecting prediction order", OFFSET(prediction_order), AV_OPT_TYPE_INT, {.i64 = ORDER_METHOD_EST }, ORDER_METHOD_EST, ORDER_METHOD_SEARCH, FLAGS, .unit = "predm" }, +{ "estimation", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_EST }, 0, 0, FLAGS, .unit = "predm" }, +{ "search", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = ORDER_METHOD_SEARCH }, 0, 0, FLAGS, .unit = "predm" }, +{ "rematrix_precision", "Rematrix coefficient precision", OFFSET(rematrix_precision), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, 14, FLAGS }, +{ NULL }, +}; + +static const AVClass mlp_class = { + .class_name = "mlpenc", + .item_name = av_default_item_name, + .option = mlp_options, + .version = LIBAVUTIL_VERSION_INT, +}; + #if CONFIG_MLP_ENCODER const FFCodec ff_mlp_encoder = { .p.name ="mlp", @@ -2250,9 +2307,9 @@ const FFCodec ff_mlp_encoder = { .init = mlp_encode_init, FF_CODEC_ENCODE_CB(mlp_encode_frame), .close = mlp_encode_close, - .p.sample_fmts = (const enum AVSampleFormat[]) {AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_NONE}, + .p.priv_class = &mlp_class, + .p.sample_fmts = (const enum AVSampleFormat[]) {AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_NONE}, .p.supported_samplerates = (const int[]) {44100, 48000, 88200, 96000, 176400, 192000, 0}, - CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(ff_mlp_channel_layouts) .p.ch_layouts = ff_mlp_ch_layouts, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; @@ -2270,12 +2327,17 @@ const FFCodec ff_truehd_encoder = { .init = mlp_encode_init, FF_CODEC_ENCODE_CB(mlp_encode_frame), .close = mlp_encode_close, - .p.sample_fmts = (const enum AVSampleFormat[]) {AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_NONE}, + .p.priv_class = &mlp_class, + .p.sample_fmts = (const enum AVSampleFormat[]) {AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_NONE}, .p.supported_samplerates = (const int[]) {44100, 48000, 88200, 96000, 176400, 192000, 0}, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO, AV_CH_LAYOUT_5POINT0, AV_CH_LAYOUT_5POINT1) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, + AV_CHANNEL_LAYOUT_2POINT1, + AV_CHANNEL_LAYOUT_SURROUND, + AV_CHANNEL_LAYOUT_3POINT1, + AV_CHANNEL_LAYOUT_4POINT0, + AV_CHANNEL_LAYOUT_4POINT1, AV_CHANNEL_LAYOUT_5POINT0, AV_CHANNEL_LAYOUT_5POINT1, { 0 } diff --git a/libavcodec/mmaldec.c b/libavcodec/mmaldec.c index 3092f585107..a8cda275706 100644 --- a/libavcodec/mmaldec.c +++ b/libavcodec/mmaldec.c @@ -101,6 +101,10 @@ typedef struct MMALDecodeContext { // packets (where each packet contains 1 frame). #define MAX_DELAYED_FRAMES 16 +static const enum AVPixelFormat mmal_pixfmts[] = { + AV_PIX_FMT_MMAL, AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE +}; + static void ffmmal_poolref_unref(FFPoolRef *ref) { if (ref && @@ -367,7 +371,7 @@ static av_cold int ffmmal_init_decoder(AVCodecContext *avctx) return AVERROR(ENOSYS); } - if ((ret = ff_get_format(avctx, avctx->codec->pix_fmts)) < 0) + if ((ret = ff_get_format(avctx, mmal_pixfmts)) < 0) return ret; avctx->pix_fmt = ret; @@ -622,8 +626,10 @@ static int ffmal_copy_frame(AVCodecContext *avctx, AVFrame *frame, MMALDecodeContext *ctx = avctx->priv_data; int ret = 0; - frame->interlaced_frame = ctx->interlaced_frame; - frame->top_field_first = ctx->top_field_first; + if (ctx->interlaced_frame) + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (ctx->top_field_first) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; if (avctx->pix_fmt == AV_PIX_FMT_MMAL) { if (!ctx->pool_out) @@ -646,8 +652,8 @@ static int ffmal_copy_frame(AVCodecContext *avctx, AVFrame *frame, av_image_fill_arrays(src, linesize, buffer->data + buffer->type->video.offset[0], avctx->pix_fmt, w, h, 1); - av_image_copy(frame->data, frame->linesize, (const uint8_t **)src, linesize, - avctx->pix_fmt, avctx->width, avctx->height); + av_image_copy2(frame->data, frame->linesize, src, linesize, + avctx->pix_fmt, avctx->width, avctx->height); } frame->sample_aspect_ratio = avctx->sample_aspect_ratio; @@ -841,11 +847,7 @@ static const AVClass ffmmal_dec_class = { .flush = ffmmal_flush, \ .p.priv_class = &ffmmal_dec_class, \ .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, \ - .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | \ - FF_CODEC_CAP_SETS_PKT_DTS, \ - .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_MMAL, \ - AV_PIX_FMT_YUV420P, \ - AV_PIX_FMT_NONE}, \ + .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, \ .hw_configs = mmal_hw_configs, \ .p.wrapper_name = "mmal", \ }; diff --git a/libavcodec/mobiclip.c b/libavcodec/mobiclip.c index c3b2383dbc4..433de0abb26 100644 --- a/libavcodec/mobiclip.c +++ b/libavcodec/mobiclip.c @@ -274,28 +274,26 @@ typedef struct MobiClipContext { BswapDSPContext bdsp; } MobiClipContext; -static VLC rl_vlc[2]; -static VLC mv_vlc[2][16]; +static const VLCElem *rl_vlc[2]; +static const VLCElem *mv_vlc[2][16]; static av_cold void mobiclip_init_static(void) { - INIT_VLC_STATIC_FROM_LENGTHS(&rl_vlc[0], MOBI_RL_VLC_BITS, 104, - bits0, sizeof(*bits0), - syms0, sizeof(*syms0), sizeof(*syms0), - 0, 0, 1 << MOBI_RL_VLC_BITS); - INIT_VLC_STATIC_FROM_LENGTHS(&rl_vlc[1], MOBI_RL_VLC_BITS, 104, - bits0, sizeof(*bits0), - syms1, sizeof(*syms1), sizeof(*syms1), - 0, 0, 1 << MOBI_RL_VLC_BITS); + static VLCElem vlc_buf[(2 << MOBI_RL_VLC_BITS) + (2 * 16 << MOBI_MV_VLC_BITS)]; + VLCInitState state =VLC_INIT_STATE(vlc_buf); + for (int i = 0; i < 2; i++) { - static VLCElem vlc_buf[2 * 16 << MOBI_MV_VLC_BITS]; + rl_vlc[i] = + ff_vlc_init_tables_from_lengths(&state, MOBI_RL_VLC_BITS, 104, + bits0, sizeof(*bits0), + i ? syms1 : syms0, sizeof(*syms0), sizeof(*syms0), + 0, 0); for (int j = 0; j < 16; j++) { - mv_vlc[i][j].table = &vlc_buf[(16 * i + j) << MOBI_MV_VLC_BITS]; - mv_vlc[i][j].table_allocated = 1 << MOBI_MV_VLC_BITS; - ff_init_vlc_from_lengths(&mv_vlc[i][j], MOBI_MV_VLC_BITS, mv_len[j], - mv_bits[i][j], sizeof(*mv_bits[i][j]), - mv_syms[i][j], sizeof(*mv_syms[i][j]), sizeof(*mv_syms[i][j]), - 0, INIT_VLC_USE_NEW_STATIC, NULL); + mv_vlc[i][j] = + ff_vlc_init_tables_from_lengths(&state, MOBI_MV_VLC_BITS, mv_len[j], + mv_bits[i][j], sizeof(*mv_bits[i][j]), + mv_syms[i][j], sizeof(*mv_syms[i][j]), sizeof(*mv_syms[i][j]), + 0, 0); } } } @@ -410,8 +408,7 @@ static void read_run_encoding(AVCodecContext *avctx, { MobiClipContext *s = avctx->priv_data; GetBitContext *gb = &s->gb; - int n = get_vlc2(gb, rl_vlc[s->dct_tab_idx].table, - MOBI_RL_VLC_BITS, 1); + int n = get_vlc2(gb, rl_vlc[s->dct_tab_idx], MOBI_RL_VLC_BITS, 1); *last = (n >> 11) == 1; *run = (n >> 5) & 0x3F; @@ -1195,8 +1192,7 @@ static int predict_motion(AVCodecContext *avctx, for (int i = 0; i < 2; i++) { int ret, idx2; - idx2 = get_vlc2(gb, mv_vlc[s->moflex][tidx].table, - MOBI_MV_VLC_BITS, 1); + idx2 = get_vlc2(gb, mv_vlc[s->moflex][tidx], MOBI_MV_VLC_BITS, 1); ret = predict_motion(avctx, width, height, idx2, offsetm, offsetx + i * adjx, offsety + i * adjy); @@ -1235,7 +1231,7 @@ static int mobiclip_decode(AVCodecContext *avctx, AVFrame *rframe, if (get_bits1(gb)) { frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; s->moflex = get_bits1(gb); s->dct_tab_idx = get_bits1(gb); @@ -1256,7 +1252,7 @@ static int mobiclip_decode(AVCodecContext *avctx, AVFrame *rframe, memset(motion, 0, s->motion_size); frame->pict_type = AV_PICTURE_TYPE_P; - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; s->dct_tab_idx = 0; ret = setup_qtables(avctx, s->quantizer + (int64_t)get_se_golomb(gb)); @@ -1272,8 +1268,7 @@ static int mobiclip_decode(AVCodecContext *avctx, AVFrame *rframe, motion[x / 16 + 2].x = 0; motion[x / 16 + 2].y = 0; - idx = get_vlc2(gb, mv_vlc[s->moflex][0].table, - MOBI_MV_VLC_BITS, 1); + idx = get_vlc2(gb, mv_vlc[s->moflex][0], MOBI_MV_VLC_BITS, 1); if (idx == 6 || idx == 7) { ret = decode_macroblock(avctx, frame, x, y, idx == 7); diff --git a/libavcodec/motion_est.c b/libavcodec/motion_est.c index df9d1befa83..2091acbbec9 100644 --- a/libavcodec/motion_est.c +++ b/libavcodec/motion_est.c @@ -1446,7 +1446,7 @@ static inline int direct_search(MpegEncContext * s, int mb_x, int mb_y) s->b_direct_mv_table[mot_xy][0]= 0; s->b_direct_mv_table[mot_xy][1]= 0; - return 256*256*256*64; + return 256*256*256*64-1; } c->xmin= xmin; diff --git a/libavcodec/motionpixels.c b/libavcodec/motionpixels.c index a947ca05de7..9df0b206767 100644 --- a/libavcodec/motionpixels.c +++ b/libavcodec/motionpixels.c @@ -328,7 +328,7 @@ static int mp_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if (mp->codes_count > 1) { /* The entries of the mp->codes array are sorted from right to left * in the Huffman tree, hence -(int)sizeof(HuffCode). */ - ret = ff_init_vlc_from_lengths(&mp->vlc, mp->max_codes_bits, mp->codes_count, + ret = ff_vlc_init_from_lengths(&mp->vlc, mp->max_codes_bits, mp->codes_count, &mp->codes[mp->codes_count - 1].size, -(int)sizeof(HuffCode), &mp->codes[mp->codes_count - 1].delta, -(int)sizeof(HuffCode), 1, 0, 0, avctx); @@ -336,7 +336,7 @@ static int mp_decode_frame(AVCodecContext *avctx, AVFrame *rframe, goto end; } mp_decode_frame_helper(mp, &gb); - ff_free_vlc(&mp->vlc); + ff_vlc_free(&mp->vlc); end: if ((ret = av_frame_ref(rframe, mp->frame)) < 0) diff --git a/libavcodec/movtextenc.c b/libavcodec/movtextenc.c index 7aa74d7c9d0..fd8c7dc9f79 100644 --- a/libavcodec/movtextenc.c +++ b/libavcodec/movtextenc.c @@ -22,7 +22,6 @@ #include #include "avcodec.h" #include "libavutil/opt.h" -#include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" #include "libavutil/mem.h" #include "libavutil/common.h" @@ -170,7 +169,6 @@ static int mov_text_encode_close(AVCodecContext *avctx) ff_ass_split_free(s->ass_ctx); av_freep(&s->style_attributes); av_freep(&s->fonts); - av_bprint_finalize(&s->buffer, NULL); return 0; } @@ -183,6 +181,9 @@ static int encode_sample_description(AVCodecContext *avctx) int font_names_total_len = 0; MovTextContext *s = avctx->priv_data; uint8_t buf[30], *p = buf; + int ret; + + av_bprint_init(&s->buffer, 0, INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE + 1); // 0x00, 0x00, 0x00, 0x00, // uint32_t displayFlags // 0x01, // int8_t horizontal-justification @@ -306,19 +307,23 @@ static int encode_sample_description(AVCodecContext *avctx) // }; if (!av_bprint_is_complete(&s->buffer)) { - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail; } avctx->extradata_size = s->buffer.len; avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!avctx->extradata) { - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail; } memcpy(avctx->extradata, s->buffer.str, avctx->extradata_size); - av_bprint_clear(&s->buffer); + ret = 0; +fail: + av_bprint_finalize(&s->buffer, NULL); - return 0; + return ret; } static av_cold int mov_text_encode_init(AVCodecContext *avctx) @@ -327,8 +332,6 @@ static av_cold int mov_text_encode_init(AVCodecContext *avctx) MovTextContext *s = avctx->priv_data; s->avctx = avctx; - av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - s->ass_ctx = ff_ass_split(avctx->subtitle_header); if (!s->ass_ctx) return AVERROR_INVALIDDATA; @@ -640,10 +643,14 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, ASSDialog *dialog; int i, length; + if (bufsize < 3) + goto too_small; + s->text_pos = 0; s->count = 0; s->box_flags = 0; - av_bprint_clear(&s->buffer); + + av_bprint_init_for_buffer(&s->buffer, buf + 2, bufsize - 2); for (i = 0; i < sub->num_rects; i++) { const char *ass = sub->rects[i]->ass; @@ -663,23 +670,19 @@ static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf, if (s->buffer.len > UINT16_MAX) return AVERROR(ERANGE); AV_WB16(buf, s->buffer.len); - buf += 2; for (size_t j = 0; j < box_count; j++) box_types[j].encode(s); - if (!av_bprint_is_complete(&s->buffer)) - return AVERROR(ENOMEM); - if (!s->buffer.len) return 0; - if (s->buffer.len > bufsize - 3) { + if (!av_bprint_is_complete(&s->buffer)) { +too_small: av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); return AVERROR_BUFFER_TOO_SMALL; } - memcpy(buf, s->buffer.str, s->buffer.len); length = s->buffer.len + 2; return length; diff --git a/libavcodec/mp3_header_decompress_bsf.c b/libavcodec/mp3_header_decompress_bsf.c deleted file mode 100644 index a177029fe00..00000000000 --- a/libavcodec/mp3_header_decompress_bsf.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * copyright (c) 2006 Michael Niedermayer - * - * 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/common.h" -#include "libavutil/intreadwrite.h" -#include "bsf.h" -#include "bsf_internal.h" -#include "defs.h" -#include "mpegaudiodecheader.h" -#include "mpegaudiodata.h" - - -static int mp3_header_decompress(AVBSFContext *ctx, AVPacket *out) -{ - AVPacket *in; - uint32_t header; - int sample_rate= ctx->par_in->sample_rate; - int sample_rate_index=0; - int lsf, mpeg25, bitrate_index, frame_size, ret; - uint8_t *buf; - int buf_size; - - ret = ff_bsf_get_packet(ctx, &in); - if (ret < 0) - return ret; - - buf = in->data; - buf_size = in->size; - - header = AV_RB32(buf); - if(ff_mpa_check_header(header) >= 0){ - av_packet_move_ref(out, in); - av_packet_free(&in); - - return 0; - } - - if(ctx->par_in->extradata_size != 15 || strcmp(ctx->par_in->extradata, "FFCMP3 0.0")){ - av_log(ctx, AV_LOG_ERROR, "Extradata invalid %d\n", ctx->par_in->extradata_size); - ret = AVERROR(EINVAL); - goto fail; - } - - header= AV_RB32(ctx->par_in->extradata+11) & MP3_MASK; - - lsf = sample_rate < (24000+32000)/2; - mpeg25 = sample_rate < (12000+16000)/2; - sample_rate_index= (header>>10)&3; - if (sample_rate_index == 3) { - ret = AVERROR_INVALIDDATA; - goto fail; - } - - sample_rate = ff_mpa_freq_tab[sample_rate_index] >> (lsf + mpeg25); //in case sample rate is a little off - - for(bitrate_index=2; bitrate_index<30; bitrate_index++){ - frame_size = ff_mpa_bitrate_tab[lsf][2][bitrate_index>>1]; - frame_size = (frame_size * 144000) / (sample_rate << lsf) + (bitrate_index&1); - if(frame_size == buf_size + 4) - break; - if(frame_size == buf_size + 6) - break; - } - if(bitrate_index == 30){ - av_log(ctx, AV_LOG_ERROR, "Could not find bitrate_index.\n"); - ret = AVERROR(EINVAL); - goto fail; - } - - header |= (bitrate_index&1)<<9; - header |= (bitrate_index>>1)<<12; - header |= (frame_size == buf_size + 4)<<16; //FIXME actually set a correct crc instead of 0 - - ret = av_new_packet(out, frame_size); - if (ret < 0) - goto fail; - ret = av_packet_copy_props(out, in); - if (ret < 0) { - av_packet_unref(out); - goto fail; - } - memcpy(out->data + frame_size - buf_size, buf, buf_size + AV_INPUT_BUFFER_PADDING_SIZE); - - if (ctx->par_in->ch_layout.nb_channels == 2){ - uint8_t *p= out->data + frame_size - buf_size; - if(lsf){ - FFSWAP(int, p[1], p[2]); - header |= (p[1] & 0xC0)>>2; - p[1] &= 0x3F; - }else{ - header |= p[1] & 0x30; - p[1] &= 0xCF; - } - } - - AV_WB32(out->data, header); - - ret = 0; - -fail: - av_packet_free(&in); - return ret; -} - -static const enum AVCodecID codec_ids[] = { - AV_CODEC_ID_MP3, AV_CODEC_ID_NONE, -}; - -const FFBitStreamFilter ff_mp3_header_decompress_bsf = { - .p.name = "mp3decomp", - .p.codec_ids = codec_ids, - .filter = mp3_header_decompress, -}; diff --git a/libavcodec/mpc.c b/libavcodec/mpc.c index 6aa3e80927b..46cb967cf1d 100644 --- a/libavcodec/mpc.c +++ b/libavcodec/mpc.c @@ -27,7 +27,7 @@ */ #include -#include "libavutil/intmath.h" +#include "libavutil/common.h" #include "mpegaudiodsp.h" #include "mpc.h" diff --git a/libavcodec/mpc7.c b/libavcodec/mpc7.c index d2745366c29..b23b4094dbe 100644 --- a/libavcodec/mpc7.c +++ b/libavcodec/mpc7.c @@ -40,33 +40,34 @@ #include "mpc.h" #include "mpc7data.h" -static VLC scfi_vlc, dscf_vlc, hdr_vlc, quant_vlc[MPC7_QUANT_VLC_TABLES][2]; +static VLCElem scfi_vlc[1 << MPC7_SCFI_BITS]; +static VLCElem dscf_vlc[1 << MPC7_DSCF_BITS]; +static VLCElem hdr_vlc [1 << MPC7_HDR_BITS]; +static const VLCElem *quant_vlc[MPC7_QUANT_VLC_TABLES][2]; static av_cold void mpc7_init_static(void) { static VLCElem quant_tables[7224]; + VLCInitState state = VLC_INIT_STATE(quant_tables); const uint8_t *raw_quant_table = mpc7_quant_vlcs; - INIT_VLC_STATIC_FROM_LENGTHS(&scfi_vlc, MPC7_SCFI_BITS, MPC7_SCFI_SIZE, - &mpc7_scfi[1], 2, - &mpc7_scfi[0], 2, 1, 0, 0, 1 << MPC7_SCFI_BITS); - INIT_VLC_STATIC_FROM_LENGTHS(&dscf_vlc, MPC7_DSCF_BITS, MPC7_DSCF_SIZE, - &mpc7_dscf[1], 2, - &mpc7_dscf[0], 2, 1, -7, 0, 1 << MPC7_DSCF_BITS); - INIT_VLC_STATIC_FROM_LENGTHS(&hdr_vlc, MPC7_HDR_BITS, MPC7_HDR_SIZE, - &mpc7_hdr[1], 2, - &mpc7_hdr[0], 2, 1, -5, 0, 1 << MPC7_HDR_BITS); - for (unsigned i = 0, offset = 0; i < MPC7_QUANT_VLC_TABLES; i++){ + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(scfi_vlc, MPC7_SCFI_BITS, MPC7_SCFI_SIZE, + &mpc7_scfi[1], 2, + &mpc7_scfi[0], 2, 1, 0, 0); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(dscf_vlc, MPC7_DSCF_BITS, MPC7_DSCF_SIZE, + &mpc7_dscf[1], 2, + &mpc7_dscf[0], 2, 1, -7, 0); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(hdr_vlc, MPC7_HDR_BITS, MPC7_HDR_SIZE, + &mpc7_hdr[1], 2, + &mpc7_hdr[0], 2, 1, -5, 0); + for (int i = 0; i < MPC7_QUANT_VLC_TABLES; i++) { for (int j = 0; j < 2; j++) { - quant_vlc[i][j].table = &quant_tables[offset]; - quant_vlc[i][j].table_allocated = FF_ARRAY_ELEMS(quant_tables) - offset; - ff_init_vlc_from_lengths(&quant_vlc[i][j], 9, mpc7_quant_vlc_sizes[i], - &raw_quant_table[1], 2, - &raw_quant_table[0], 2, 1, - mpc7_quant_vlc_off[i], - INIT_VLC_STATIC_OVERLONG, NULL); + quant_vlc[i][j] = + ff_vlc_init_tables_from_lengths(&state, 9, mpc7_quant_vlc_sizes[i], + &raw_quant_table[1], 2, + &raw_quant_table[0], 2, 1, + mpc7_quant_vlc_off[i], 0); raw_quant_table += 2 * mpc7_quant_vlc_sizes[i]; - offset += quant_vlc[i][j].table_size; } } ff_mpa_synth_init_fixed(); @@ -134,7 +135,7 @@ static inline void idx_to_quant(MPCContext *c, GetBitContext *gb, int idx, int * case 1: i1 = get_bits1(gb); for(i = 0; i < SAMPLES_PER_BAND/3; i++){ - t = get_vlc2(gb, quant_vlc[0][i1].table, 9, 2); + t = get_vlc2(gb, quant_vlc[0][i1], 9, 2); *dst++ = mpc7_idx30[t]; *dst++ = mpc7_idx31[t]; *dst++ = mpc7_idx32[t]; @@ -143,7 +144,7 @@ static inline void idx_to_quant(MPCContext *c, GetBitContext *gb, int idx, int * case 2: i1 = get_bits1(gb); for(i = 0; i < SAMPLES_PER_BAND/2; i++){ - t = get_vlc2(gb, quant_vlc[1][i1].table, 9, 2); + t = get_vlc2(gb, quant_vlc[1][i1], 9, 2); *dst++ = mpc7_idx50[t]; *dst++ = mpc7_idx51[t]; } @@ -151,7 +152,7 @@ static inline void idx_to_quant(MPCContext *c, GetBitContext *gb, int idx, int * case 3: case 4: case 5: case 6: case 7: i1 = get_bits1(gb); for(i = 0; i < SAMPLES_PER_BAND; i++) - *dst++ = get_vlc2(gb, quant_vlc[idx-1][i1].table, 9, 2); + *dst++ = get_vlc2(gb, quant_vlc[idx-1][i1], 9, 2); break; case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: @@ -166,7 +167,7 @@ static inline void idx_to_quant(MPCContext *c, GetBitContext *gb, int idx, int * static int get_scale_idx(GetBitContext *gb, int ref) { - int t = get_vlc2(gb, dscf_vlc.table, MPC7_DSCF_BITS, 1); + int t = get_vlc2(gb, dscf_vlc, MPC7_DSCF_BITS, 1); if (t == 8) return get_bits(gb, 6); return ref + t; @@ -220,7 +221,7 @@ static int mpc7_decode_frame(AVCodecContext *avctx, AVFrame *frame, /* read subband indexes */ for(i = 0; i <= c->maxbands; i++){ for(ch = 0; ch < 2; ch++){ - int t = i ? get_vlc2(&gb, hdr_vlc.table, MPC7_HDR_BITS, 1) : 4; + int t = i ? get_vlc2(&gb, hdr_vlc, MPC7_HDR_BITS, 1) : 4; if(t == 4) bands[i].res[ch] = get_bits(&gb, 4); else bands[i].res[ch] = bands[i-1].res[ch] + t; if (bands[i].res[ch] < -1 || bands[i].res[ch] > 17) { @@ -237,7 +238,8 @@ static int mpc7_decode_frame(AVCodecContext *avctx, AVFrame *frame, /* get scale indexes coding method */ for(i = 0; i <= mb; i++) for(ch = 0; ch < 2; ch++) - if(bands[i].res[ch]) bands[i].scfi[ch] = get_vlc2(&gb, scfi_vlc.table, MPC7_SCFI_BITS, 1); + if (bands[i].res[ch]) + bands[i].scfi[ch] = get_vlc2(&gb, scfi_vlc, MPC7_SCFI_BITS, 1); /* get scale indexes */ for(i = 0; i <= mb; i++){ for(ch = 0; ch < 2; ch++){ diff --git a/libavcodec/mpc8.c b/libavcodec/mpc8.c index c1b787c33fe..27852591192 100644 --- a/libavcodec/mpc8.c +++ b/libavcodec/mpc8.c @@ -103,8 +103,8 @@ static av_cold void build_vlc(VLC *vlc, unsigned *buf_offset, for (unsigned tmp = num + codes_counts[i - 1]; num < tmp; num++) len[num] = i; - ff_init_vlc_from_lengths(vlc, FFMIN(len[0], 9), num, len, 1, - *syms, 1, 1, offset, INIT_VLC_STATIC_OVERLONG, NULL); + ff_vlc_init_from_lengths(vlc, FFMIN(len[0], 9), num, len, 1, + *syms, 1, 1, offset, VLC_INIT_STATIC_OVERLONG, NULL); *buf_offset += vlc->table_size; *syms += num; } diff --git a/libavcodec/mpeg12.c b/libavcodec/mpeg12.c index a256d45c85b..8d88820c46e 100644 --- a/libavcodec/mpeg12.c +++ b/libavcodec/mpeg12.c @@ -72,7 +72,7 @@ av_cold void ff_init_2d_vlc_rl(const uint16_t table_vlc[][2], RL_VLC_ELEM rl_vlc VLCElem table[680] = { 0 }; VLC vlc = { .table = table, .table_allocated = static_size }; av_assert0(static_size <= FF_ARRAY_ELEMS(table)); - init_vlc(&vlc, TEX_VLC_BITS, n + 2, &table_vlc[0][1], 4, 2, &table_vlc[0][0], 4, 2, INIT_VLC_USE_NEW_STATIC | flags); + vlc_init(&vlc, TEX_VLC_BITS, n + 2, &table_vlc[0][1], 4, 2, &table_vlc[0][0], 4, 2, VLC_INIT_USE_STATIC | flags); for (i = 0; i < vlc.table_size; i++) { int code = vlc.table[i].sym; @@ -115,43 +115,43 @@ void ff_mpeg1_clean_buffers(MpegEncContext *s) /******************************************/ /* decoding */ -VLC ff_mv_vlc; +VLCElem ff_mv_vlc[266]; -VLC ff_dc_lum_vlc; -VLC ff_dc_chroma_vlc; +VLCElem ff_dc_lum_vlc[512]; +VLCElem ff_dc_chroma_vlc[514]; -VLC ff_mbincr_vlc; -VLC ff_mb_ptype_vlc; -VLC ff_mb_btype_vlc; -VLC ff_mb_pat_vlc; +VLCElem ff_mbincr_vlc[538]; +VLCElem ff_mb_ptype_vlc[64]; +VLCElem ff_mb_btype_vlc[64]; +VLCElem ff_mb_pat_vlc[512]; RL_VLC_ELEM ff_mpeg1_rl_vlc[680]; RL_VLC_ELEM ff_mpeg2_rl_vlc[674]; static av_cold void mpeg12_init_vlcs(void) { - INIT_VLC_STATIC(&ff_dc_lum_vlc, DC_VLC_BITS, 12, - ff_mpeg12_vlc_dc_lum_bits, 1, 1, - ff_mpeg12_vlc_dc_lum_code, 2, 2, 512); - INIT_VLC_STATIC(&ff_dc_chroma_vlc, DC_VLC_BITS, 12, - ff_mpeg12_vlc_dc_chroma_bits, 1, 1, - ff_mpeg12_vlc_dc_chroma_code, 2, 2, 514); - INIT_VLC_STATIC(&ff_mv_vlc, MV_VLC_BITS, 17, - &ff_mpeg12_mbMotionVectorTable[0][1], 2, 1, - &ff_mpeg12_mbMotionVectorTable[0][0], 2, 1, 266); - INIT_VLC_STATIC(&ff_mbincr_vlc, MBINCR_VLC_BITS, 36, - &ff_mpeg12_mbAddrIncrTable[0][1], 2, 1, - &ff_mpeg12_mbAddrIncrTable[0][0], 2, 1, 538); - INIT_VLC_STATIC(&ff_mb_pat_vlc, MB_PAT_VLC_BITS, 64, - &ff_mpeg12_mbPatTable[0][1], 2, 1, - &ff_mpeg12_mbPatTable[0][0], 2, 1, 512); - - INIT_VLC_STATIC(&ff_mb_ptype_vlc, MB_PTYPE_VLC_BITS, 7, - &table_mb_ptype[0][1], 2, 1, - &table_mb_ptype[0][0], 2, 1, 64); - INIT_VLC_STATIC(&ff_mb_btype_vlc, MB_BTYPE_VLC_BITS, 11, - &table_mb_btype[0][1], 2, 1, - &table_mb_btype[0][0], 2, 1, 64); + VLC_INIT_STATIC_TABLE(ff_dc_lum_vlc, DC_VLC_BITS, 12, + ff_mpeg12_vlc_dc_lum_bits, 1, 1, + ff_mpeg12_vlc_dc_lum_code, 2, 2, 0); + VLC_INIT_STATIC_TABLE(ff_dc_chroma_vlc, DC_VLC_BITS, 12, + ff_mpeg12_vlc_dc_chroma_bits, 1, 1, + ff_mpeg12_vlc_dc_chroma_code, 2, 2, 0); + VLC_INIT_STATIC_TABLE(ff_mv_vlc, MV_VLC_BITS, 17, + &ff_mpeg12_mbMotionVectorTable[0][1], 2, 1, + &ff_mpeg12_mbMotionVectorTable[0][0], 2, 1, 0); + VLC_INIT_STATIC_TABLE(ff_mbincr_vlc, MBINCR_VLC_BITS, 36, + &ff_mpeg12_mbAddrIncrTable[0][1], 2, 1, + &ff_mpeg12_mbAddrIncrTable[0][0], 2, 1, 0); + VLC_INIT_STATIC_TABLE(ff_mb_pat_vlc, MB_PAT_VLC_BITS, 64, + &ff_mpeg12_mbPatTable[0][1], 2, 1, + &ff_mpeg12_mbPatTable[0][0], 2, 1, 0); + + VLC_INIT_STATIC_TABLE(ff_mb_ptype_vlc, MB_PTYPE_VLC_BITS, 7, + &table_mb_ptype[0][1], 2, 1, + &table_mb_ptype[0][0], 2, 1, 0); + VLC_INIT_STATIC_TABLE(ff_mb_btype_vlc, MB_BTYPE_VLC_BITS, 11, + &table_mb_btype[0][1], 2, 1, + &table_mb_btype[0][0], 2, 1, 0); ff_init_2d_vlc_rl(ff_mpeg1_vlc_table, ff_mpeg1_rl_vlc, ff_mpeg12_run, ff_mpeg12_level, MPEG12_RL_NB_ELEMS, diff --git a/libavcodec/mpeg12dec.c b/libavcodec/mpeg12dec.c index 6c4cbccc597..d05d6355e00 100644 --- a/libavcodec/mpeg12dec.c +++ b/libavcodec/mpeg12dec.c @@ -31,6 +31,7 @@ #include #include "libavutil/attributes.h" +#include "libavutil/emms.h" #include "libavutil/imgutils.h" #include "libavutil/internal.h" #include "libavutil/mem_internal.h" @@ -42,6 +43,7 @@ #include "codec_internal.h" #include "decode.h" #include "error_resilience.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "idctdsp.h" #include "internal.h" @@ -60,6 +62,13 @@ #define A53_MAX_CC_COUNT 2000 +enum Mpeg2ClosedCaptionsFormat { + CC_FORMAT_AUTO, + CC_FORMAT_A53_PART4, + CC_FORMAT_SCTE20, + CC_FORMAT_DVD +}; + typedef struct Mpeg1Context { MpegEncContext mpeg_enc_ctx; int mpeg_enc_ctx_allocated; /* true if decoding context allocated */ @@ -68,13 +77,13 @@ typedef struct Mpeg1Context { AVStereo3D stereo3d; int has_stereo3d; AVBufferRef *a53_buf_ref; + enum Mpeg2ClosedCaptionsFormat cc_format; uint8_t afd; int has_afd; int slice_count; unsigned aspect_ratio_info; AVRational save_aspect; int save_width, save_height, save_progressive_seq; - int rc_buffer_size; AVRational frame_rate_ext; /* MPEG-2 specific framerate modificator */ unsigned frame_rate_index; int sync; /* Did we reach a sync point like a GOP/SEQ/KEYFrame? */ @@ -116,7 +125,7 @@ static int mpeg_decode_motion(MpegEncContext *s, int fcode, int pred) { int code, sign, val, shift; - code = get_vlc2(&s->gb, ff_mv_vlc.table, MV_VLC_BITS, 2); + code = get_vlc2(&s->gb, ff_mv_vlc, MV_VLC_BITS, 2); if (code == 0) return pred; if (code < 0) @@ -232,94 +241,6 @@ static inline int mpeg1_decode_block_inter(MpegEncContext *s, return 0; } -/** - * Changing this would eat up any speed benefits it has. - * Do not use "fast" flag if you need the code to be robust. - */ -static inline int mpeg1_fast_decode_block_inter(MpegEncContext *s, - int16_t *block, int n) -{ - int level, i, j, run; - uint8_t *const scantable = s->intra_scantable.permutated; - const int qscale = s->qscale; - - { - OPEN_READER(re, &s->gb); - i = -1; - // Special case for first coefficient, no need to add second VLC table. - UPDATE_CACHE(re, &s->gb); - if (((int32_t) GET_CACHE(re, &s->gb)) < 0) { - level = (3 * qscale) >> 1; - level = (level - 1) | 1; - if (GET_CACHE(re, &s->gb) & 0x40000000) - level = -level; - block[0] = level; - i++; - SKIP_BITS(re, &s->gb, 2); - if (((int32_t) GET_CACHE(re, &s->gb)) <= (int32_t) 0xBFFFFFFF) - goto end; - } - - /* now quantify & encode AC coefficients */ - for (;;) { - GET_RL_VLC(level, run, re, &s->gb, ff_mpeg1_rl_vlc, - TEX_VLC_BITS, 2, 0); - - if (level != 0) { - i += run; - if (i > MAX_INDEX) - break; - j = scantable[i]; - level = ((level * 2 + 1) * qscale) >> 1; - level = (level - 1) | 1; - level = (level ^ SHOW_SBITS(re, &s->gb, 1)) - - SHOW_SBITS(re, &s->gb, 1); - SKIP_BITS(re, &s->gb, 1); - } else { - /* escape */ - run = SHOW_UBITS(re, &s->gb, 6) + 1; - LAST_SKIP_BITS(re, &s->gb, 6); - UPDATE_CACHE(re, &s->gb); - level = SHOW_SBITS(re, &s->gb, 8); - SKIP_BITS(re, &s->gb, 8); - if (level == -128) { - level = SHOW_UBITS(re, &s->gb, 8) - 256; - SKIP_BITS(re, &s->gb, 8); - } else if (level == 0) { - level = SHOW_UBITS(re, &s->gb, 8); - SKIP_BITS(re, &s->gb, 8); - } - i += run; - if (i > MAX_INDEX) - break; - j = scantable[i]; - if (level < 0) { - level = -level; - level = ((level * 2 + 1) * qscale) >> 1; - level = (level - 1) | 1; - level = -level; - } else { - level = ((level * 2 + 1) * qscale) >> 1; - level = (level - 1) | 1; - } - } - - block[j] = level; - if (((int32_t) GET_CACHE(re, &s->gb)) <= (int32_t) 0xBFFFFFFF) - break; - UPDATE_CACHE(re, &s->gb); - } -end: - LAST_SKIP_BITS(re, &s->gb, 2); - CLOSE_READER(re, &s->gb); - } - - check_scantable_index(s, i); - - s->block_last_index[n] = i; - return 0; -} - static inline int mpeg2_decode_block_non_intra(MpegEncContext *s, int16_t *block, int n) { @@ -405,81 +326,6 @@ static inline int mpeg2_decode_block_non_intra(MpegEncContext *s, return 0; } -/** - * Changing this would eat up any speed benefits it has. - * Do not use "fast" flag if you need the code to be robust. - */ -static inline int mpeg2_fast_decode_block_non_intra(MpegEncContext *s, - int16_t *block, int n) -{ - int level, i, j, run; - uint8_t *const scantable = s->intra_scantable.permutated; - const int qscale = s->qscale; - OPEN_READER(re, &s->gb); - i = -1; - - // special case for first coefficient, no need to add second VLC table - UPDATE_CACHE(re, &s->gb); - if (((int32_t) GET_CACHE(re, &s->gb)) < 0) { - level = (3 * qscale) >> 1; - if (GET_CACHE(re, &s->gb) & 0x40000000) - level = -level; - block[0] = level; - i++; - SKIP_BITS(re, &s->gb, 2); - if (((int32_t) GET_CACHE(re, &s->gb)) <= (int32_t) 0xBFFFFFFF) - goto end; - } - - /* now quantify & encode AC coefficients */ - for (;;) { - GET_RL_VLC(level, run, re, &s->gb, ff_mpeg1_rl_vlc, TEX_VLC_BITS, 2, 0); - - if (level != 0) { - i += run; - if (i > MAX_INDEX) - break; - j = scantable[i]; - level = ((level * 2 + 1) * qscale) >> 1; - level = (level ^ SHOW_SBITS(re, &s->gb, 1)) - - SHOW_SBITS(re, &s->gb, 1); - SKIP_BITS(re, &s->gb, 1); - } else { - /* escape */ - run = SHOW_UBITS(re, &s->gb, 6) + 1; - LAST_SKIP_BITS(re, &s->gb, 6); - UPDATE_CACHE(re, &s->gb); - level = SHOW_SBITS(re, &s->gb, 12); - SKIP_BITS(re, &s->gb, 12); - - i += run; - if (i > MAX_INDEX) - break; - j = scantable[i]; - if (level < 0) { - level = ((-level * 2 + 1) * qscale) >> 1; - level = -level; - } else { - level = ((level * 2 + 1) * qscale) >> 1; - } - } - - block[j] = level; - if (((int32_t) GET_CACHE(re, &s->gb)) <= (int32_t) 0xBFFFFFFF || i > 63) - break; - - UPDATE_CACHE(re, &s->gb); - } -end: - LAST_SKIP_BITS(re, &s->gb, 2); - CLOSE_READER(re, &s->gb); - - check_scantable_index(s, i); - - s->block_last_index[n] = i; - return 0; -} - static inline int mpeg2_decode_block_intra(MpegEncContext *s, int16_t *block, int n) { @@ -562,83 +408,6 @@ static inline int mpeg2_decode_block_intra(MpegEncContext *s, return 0; } -/** - * Changing this would eat up any speed benefits it has. - * Do not use "fast" flag if you need the code to be robust. - */ -static inline int mpeg2_fast_decode_block_intra(MpegEncContext *s, - int16_t *block, int n) -{ - int level, dc, diff, i, j, run; - int component; - const RL_VLC_ELEM *rl_vlc; - uint8_t *const scantable = s->intra_scantable.permutated; - const uint16_t *quant_matrix; - const int qscale = s->qscale; - - /* DC coefficient */ - if (n < 4) { - quant_matrix = s->intra_matrix; - component = 0; - } else { - quant_matrix = s->chroma_intra_matrix; - component = (n & 1) + 1; - } - diff = decode_dc(&s->gb, component); - dc = s->last_dc[component]; - dc += diff; - s->last_dc[component] = dc; - block[0] = dc * (1 << (3 - s->intra_dc_precision)); - i = 0; - if (s->intra_vlc_format) - rl_vlc = ff_mpeg2_rl_vlc; - else - rl_vlc = ff_mpeg1_rl_vlc; - - { - OPEN_READER(re, &s->gb); - /* now quantify & encode AC coefficients */ - for (;;) { - UPDATE_CACHE(re, &s->gb); - GET_RL_VLC(level, run, re, &s->gb, rl_vlc, - TEX_VLC_BITS, 2, 0); - - if (level >= 64 || i > 63) { - break; - } else if (level != 0) { - i += run; - j = scantable[i]; - level = (level * qscale * quant_matrix[j]) >> 4; - level = (level ^ SHOW_SBITS(re, &s->gb, 1)) - - SHOW_SBITS(re, &s->gb, 1); - LAST_SKIP_BITS(re, &s->gb, 1); - } else { - /* escape */ - run = SHOW_UBITS(re, &s->gb, 6) + 1; - SKIP_BITS(re, &s->gb, 6); - level = SHOW_SBITS(re, &s->gb, 12); - LAST_SKIP_BITS(re, &s->gb, 12); - i += run; - j = scantable[i]; - if (level < 0) { - level = (-level * qscale * quant_matrix[j]) >> 4; - level = -level; - } else { - level = (level * qscale * quant_matrix[j]) >> 4; - } - } - - block[j] = level; - } - CLOSE_READER(re, &s->gb); - } - - check_scantable_index(s, i); - - s->block_last_index[n] = i; - return 0; -} - /******************************************/ /* decoding */ @@ -709,7 +478,7 @@ static int mpeg_decode_mb(MpegEncContext *s, int16_t block[12][64]) } break; case AV_PICTURE_TYPE_P: - mb_type = get_vlc2(&s->gb, ff_mb_ptype_vlc.table, MB_PTYPE_VLC_BITS, 1); + mb_type = get_vlc2(&s->gb, ff_mb_ptype_vlc, MB_PTYPE_VLC_BITS, 1); if (mb_type < 0) { av_log(s->avctx, AV_LOG_ERROR, "Invalid mb type in P-frame at %d %d\n", s->mb_x, s->mb_y); @@ -718,7 +487,7 @@ static int mpeg_decode_mb(MpegEncContext *s, int16_t block[12][64]) mb_type = ptype2mb_type[mb_type]; break; case AV_PICTURE_TYPE_B: - mb_type = get_vlc2(&s->gb, ff_mb_btype_vlc.table, MB_BTYPE_VLC_BITS, 1); + mb_type = get_vlc2(&s->gb, ff_mb_btype_vlc, MB_BTYPE_VLC_BITS, 1); if (mb_type < 0) { av_log(s->avctx, AV_LOG_ERROR, "Invalid mb type in B-frame at %d %d\n", s->mb_x, s->mb_y); @@ -766,14 +535,9 @@ static int mpeg_decode_mb(MpegEncContext *s, int16_t block[12][64]) s->mb_intra = 1; if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO) { - if (s->avctx->flags2 & AV_CODEC_FLAG2_FAST) { - for (i = 0; i < 6; i++) - mpeg2_fast_decode_block_intra(s, *s->pblocks[i], i); - } else { - for (i = 0; i < mb_block_count; i++) - if ((ret = mpeg2_decode_block_intra(s, *s->pblocks[i], i)) < 0) - return ret; - } + for (i = 0; i < mb_block_count; i++) + if ((ret = mpeg2_decode_block_intra(s, *s->pblocks[i], i)) < 0) + return ret; } else { for (i = 0; i < 6; i++) { ret = ff_mpeg1_decode_block_intra(&s->gb, @@ -980,7 +744,7 @@ static int mpeg_decode_mb(MpegEncContext *s, int16_t block[12][64]) if (HAS_CBP(mb_type)) { s->bdsp.clear_blocks(s->block[0]); - cbp = get_vlc2(&s->gb, ff_mb_pat_vlc.table, MB_PAT_VLC_BITS, 1); + cbp = get_vlc2(&s->gb, ff_mb_pat_vlc, MB_PAT_VLC_BITS, 1); if (mb_block_count > 6) { cbp *= 1 << mb_block_count - 6; cbp |= get_bits(&s->gb, mb_block_count - 6); @@ -993,46 +757,26 @@ static int mpeg_decode_mb(MpegEncContext *s, int16_t block[12][64]) } if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO) { - if (s->avctx->flags2 & AV_CODEC_FLAG2_FAST) { - for (i = 0; i < 6; i++) { - if (cbp & 32) - mpeg2_fast_decode_block_non_intra(s, *s->pblocks[i], i); - else - s->block_last_index[i] = -1; - cbp += cbp; - } - } else { - cbp <<= 12 - mb_block_count; + cbp <<= 12 - mb_block_count; - for (i = 0; i < mb_block_count; i++) { - if (cbp & (1 << 11)) { - if ((ret = mpeg2_decode_block_non_intra(s, *s->pblocks[i], i)) < 0) - return ret; - } else { - s->block_last_index[i] = -1; - } - cbp += cbp; + for (i = 0; i < mb_block_count; i++) { + if (cbp & (1 << 11)) { + if ((ret = mpeg2_decode_block_non_intra(s, *s->pblocks[i], i)) < 0) + return ret; + } else { + s->block_last_index[i] = -1; } + cbp += cbp; } } else { - if (s->avctx->flags2 & AV_CODEC_FLAG2_FAST) { - for (i = 0; i < 6; i++) { - if (cbp & 32) - mpeg1_fast_decode_block_inter(s, *s->pblocks[i], i); - else - s->block_last_index[i] = -1; - cbp += cbp; - } - } else { - for (i = 0; i < 6; i++) { - if (cbp & 32) { - if ((ret = mpeg1_decode_block_inter(s, *s->pblocks[i], i)) < 0) - return ret; - } else { - s->block_last_index[i] = -1; - } - cbp += cbp; + for (i = 0; i < 6; i++) { + if (cbp & 32) { + if ((ret = mpeg1_decode_block_inter(s, *s->pblocks[i], i)) < 0) + return ret; + } else { + s->block_last_index[i] = -1; } + cbp += cbp; } } } else { @@ -1056,9 +800,6 @@ static av_cold int mpeg_decode_init(AVCodecContext *avctx) avctx->coded_width = avctx->coded_height = 0; // do not trust dimensions from input ff_mpv_decode_init(s2, avctx); - /* we need some permutation to store matrices, - * until the decoder sets the real permutation. */ - ff_mpv_idct_init(s2); ff_mpeg12_init_vlcs(); s2->chroma_format = 1; @@ -1092,18 +833,6 @@ static int mpeg_decode_update_thread_context(AVCodecContext *avctx, } #endif -static void quant_matrix_rebuild(uint16_t *matrix, const uint8_t *old_perm, - const uint8_t *new_perm) -{ - uint16_t temp_matrix[64]; - int i; - - memcpy(temp_matrix, matrix, 64 * sizeof(uint16_t)); - - for (i = 0; i < 64; i++) - matrix[new_perm[i]] = temp_matrix[old_perm[i]]; -} - static const enum AVPixelFormat mpeg1_hwaccel_pixfmt_list_420[] = { #if CONFIG_MPEG1_NVDEC_HWACCEL AV_PIX_FMT_CUDA, @@ -1129,6 +858,9 @@ static const enum AVPixelFormat mpeg2_hwaccel_pixfmt_list_420[] = { AV_PIX_FMT_D3D11VA_VLD, AV_PIX_FMT_D3D11, #endif +#if CONFIG_MPEG2_D3D12VA_HWACCEL + AV_PIX_FMT_D3D12, +#endif #if CONFIG_MPEG2_VAAPI_HWACCEL AV_PIX_FMT_VAAPI, #endif @@ -1167,7 +899,7 @@ static enum AVPixelFormat mpeg_get_pixelformat(AVCodecContext *avctx) else pix_fmts = mpeg12_pixfmt_list_444; - return ff_thread_get_format(avctx, pix_fmts); + return ff_get_format(avctx, pix_fmts); } /* Call this function when we know all parameters. @@ -1176,7 +908,6 @@ static int mpeg_decode_postinit(AVCodecContext *avctx) { Mpeg1Context *s1 = avctx->priv_data; MpegEncContext *s = &s1->mpeg_enc_ctx; - uint8_t old_permutation[64]; int ret; if (avctx->codec_id == AV_CODEC_ID_MPEG1VIDEO) { @@ -1247,7 +978,8 @@ static int mpeg_decode_postinit(AVCodecContext *avctx) if (ret < 0) return ret; - if (avctx->codec_id == AV_CODEC_ID_MPEG2VIDEO && s->bit_rate) { + if (avctx->codec_id == AV_CODEC_ID_MPEG2VIDEO && s->bit_rate && + (s->bit_rate != 0x3FFFF*400)) { avctx->rc_max_rate = s->bit_rate; } else if (avctx->codec_id == AV_CODEC_ID_MPEG1VIDEO && s->bit_rate && (s->bit_rate != 0x3FFFF*400 || s->vbv_delay != 0xFFFF)) { @@ -1265,7 +997,11 @@ static int mpeg_decode_postinit(AVCodecContext *avctx) if (avctx->codec_id == AV_CODEC_ID_MPEG1VIDEO) { // MPEG-1 fps avctx->framerate = ff_mpeg12_frame_rate_tab[s1->frame_rate_index]; +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS avctx->ticks_per_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif avctx->chroma_sample_location = AVCHROMA_LOC_CENTER; } else { // MPEG-2 @@ -1275,7 +1011,11 @@ static int mpeg_decode_postinit(AVCodecContext *avctx) ff_mpeg12_frame_rate_tab[s1->frame_rate_index].num * s1->frame_rate_ext.num, ff_mpeg12_frame_rate_tab[s1->frame_rate_index].den * s1->frame_rate_ext.den, 1 << 30); +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS avctx->ticks_per_frame = 2; +FF_ENABLE_DEPRECATION_WARNINGS +#endif switch (s->chroma_format) { case 1: avctx->chroma_sample_location = AVCHROMA_LOC_LEFT; break; @@ -1287,19 +1027,9 @@ static int mpeg_decode_postinit(AVCodecContext *avctx) avctx->pix_fmt = mpeg_get_pixelformat(avctx); - /* Quantization matrices may need reordering - * if DCT permutation is changed. */ - memcpy(old_permutation, s->idsp.idct_permutation, 64 * sizeof(uint8_t)); - - ff_mpv_idct_init(s); if ((ret = ff_mpv_common_init(s)) < 0) return ret; - quant_matrix_rebuild(s->intra_matrix, old_permutation, s->idsp.idct_permutation); - quant_matrix_rebuild(s->inter_matrix, old_permutation, s->idsp.idct_permutation); - quant_matrix_rebuild(s->chroma_intra_matrix, old_permutation, s->idsp.idct_permutation); - quant_matrix_rebuild(s->chroma_inter_matrix, old_permutation, s->idsp.idct_permutation); - s1->mpeg_enc_ctx_allocated = 1; } return 0; @@ -1342,8 +1072,6 @@ static int mpeg1_decode_picture(AVCodecContext *avctx, const uint8_t *buf, s->mpeg_f_code[1][0] = f_code; s->mpeg_f_code[1][1] = f_code; } - s->current_picture.f->pict_type = s->pict_type; - s->current_picture.f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; if (avctx->debug & FF_DEBUG_PICT_INFO) av_log(avctx, AV_LOG_DEBUG, @@ -1359,7 +1087,6 @@ static void mpeg_decode_sequence_extension(Mpeg1Context *s1) MpegEncContext *s = &s1->mpeg_enc_ctx; int horiz_size_ext, vert_size_ext; int bit_rate_ext; - AVCPBProperties *cpb_props; skip_bits(&s->gb, 1); /* profile and level esc*/ s->avctx->profile = get_bits(&s->gb, 3); @@ -1379,7 +1106,7 @@ static void mpeg_decode_sequence_extension(Mpeg1Context *s1) bit_rate_ext = get_bits(&s->gb, 12); /* XXX: handle it */ s->bit_rate += (bit_rate_ext << 18) * 400LL; check_marker(s->avctx, &s->gb, "after bit rate extension"); - s1->rc_buffer_size += get_bits(&s->gb, 8) * 1024 * 16 << 10; + s->avctx->rc_buffer_size += get_bits(&s->gb, 8) * 1024 * 16 << 10; s->low_delay = get_bits1(&s->gb); if (s->avctx->flags & AV_CODEC_FLAG_LOW_DELAY) @@ -1391,17 +1118,11 @@ static void mpeg_decode_sequence_extension(Mpeg1Context *s1) ff_dlog(s->avctx, "sequence extension\n"); s->codec_id = s->avctx->codec_id = AV_CODEC_ID_MPEG2VIDEO; - if (cpb_props = ff_add_cpb_side_data(s->avctx)) { - cpb_props->buffer_size = s1->rc_buffer_size; - if (s->bit_rate != 0x3FFFF*400) - cpb_props->max_bitrate = s->bit_rate; - } - if (s->avctx->debug & FF_DEBUG_PICT_INFO) av_log(s->avctx, AV_LOG_DEBUG, "profile: %d, level: %d ps: %d cf:%d vbv buffer: %d, bitrate:%"PRId64"\n", s->avctx->profile, s->avctx->level, s->progressive_sequence, s->chroma_format, - s1->rc_buffer_size, s->bit_rate); + s->avctx->rc_buffer_size, s->bit_rate); } static void mpeg_decode_sequence_display_extension(Mpeg1Context *s1) @@ -1524,8 +1245,6 @@ static int mpeg_decode_picture_coding_extension(Mpeg1Context *s1) s->pict_type = AV_PICTURE_TYPE_P; } else s->pict_type = AV_PICTURE_TYPE_B; - s->current_picture.f->pict_type = s->pict_type; - s->current_picture.f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; } s->intra_dc_precision = get_bits(&s->gb, 2); @@ -1595,20 +1314,20 @@ static int mpeg_field_start(MpegEncContext *s, const uint8_t *buf, int buf_size) } } - pan_scan = av_frame_new_side_data(s->current_picture_ptr->f, - AV_FRAME_DATA_PANSCAN, - sizeof(s1->pan_scan)); - if (!pan_scan) - return AVERROR(ENOMEM); - memcpy(pan_scan->data, &s1->pan_scan, sizeof(s1->pan_scan)); + ret = ff_frame_new_side_data(s->avctx, s->current_picture_ptr->f, + AV_FRAME_DATA_PANSCAN, sizeof(s1->pan_scan), + &pan_scan); + if (ret < 0) + return ret; + if (pan_scan) + memcpy(pan_scan->data, &s1->pan_scan, sizeof(s1->pan_scan)); if (s1->a53_buf_ref) { - AVFrameSideData *sd = av_frame_new_side_data_from_buf( - s->current_picture_ptr->f, AV_FRAME_DATA_A53_CC, - s1->a53_buf_ref); - if (!sd) - av_buffer_unref(&s1->a53_buf_ref); - s1->a53_buf_ref = NULL; + ret = ff_frame_new_side_data_from_buf( + s->avctx, s->current_picture_ptr->f, AV_FRAME_DATA_A53_CC, + &s1->a53_buf_ref, NULL); + if (ret < 0) + return ret; } if (s1->has_stereo3d) { @@ -1621,13 +1340,13 @@ static int mpeg_field_start(MpegEncContext *s, const uint8_t *buf, int buf_size) } if (s1->has_afd) { - AVFrameSideData *sd = - av_frame_new_side_data(s->current_picture_ptr->f, - AV_FRAME_DATA_AFD, 1); - if (!sd) - return AVERROR(ENOMEM); - - *sd->data = s1->afd; + AVFrameSideData *sd; + ret = ff_frame_new_side_data(s->avctx, s->current_picture_ptr->f, + AV_FRAME_DATA_AFD, 1, &sd); + if (ret < 0) + return ret; + if (sd) + *sd->data = s1->afd; s1->has_afd = 0; } @@ -1642,7 +1361,7 @@ static int mpeg_field_start(MpegEncContext *s, const uint8_t *buf, int buf_size) } if (s->avctx->hwaccel) { - if ((ret = s->avctx->hwaccel->end_frame(s->avctx)) < 0) { + if ((ret = FF_HW_SIMPLE_CALL(s->avctx, end_frame)) < 0) { av_log(avctx, AV_LOG_ERROR, "hardware accelerator failed to decode first field\n"); return ret; @@ -1658,7 +1377,7 @@ static int mpeg_field_start(MpegEncContext *s, const uint8_t *buf, int buf_size) } if (avctx->hwaccel) { - if ((ret = avctx->hwaccel->start_frame(avctx, buf, buf_size)) < 0) + if ((ret = FF_HW_CALL(avctx, start_frame, buf, buf_size)) < 0) return ret; } @@ -1714,7 +1433,7 @@ static int mpeg_decode_slice(MpegEncContext *s, int mb_y, skip_bits1(&s->gb); } else { while (get_bits_left(&s->gb) > 0) { - int code = get_vlc2(&s->gb, ff_mbincr_vlc.table, + int code = get_vlc2(&s->gb, ff_mbincr_vlc, MBINCR_VLC_BITS, 2); if (code < 0) { av_log(s->avctx, AV_LOG_ERROR, "first mb_incr damaged\n"); @@ -1736,14 +1455,14 @@ static int mpeg_decode_slice(MpegEncContext *s, int mb_y, return AVERROR_INVALIDDATA; } - if (avctx->hwaccel && avctx->hwaccel->decode_slice) { + if (avctx->hwaccel) { const uint8_t *buf_end, *buf_start = *buf - 4; /* include start_code */ int start_code = -1; buf_end = avpriv_find_start_code(buf_start + 2, *buf + buf_size, &start_code); if (buf_end < *buf + buf_size) buf_end -= 4; s->mb_y = mb_y; - if (avctx->hwaccel->decode_slice(avctx, buf_start, buf_end - buf_start) < 0) + if (FF_HW_CALL(avctx, decode_slice, buf_start, buf_end - buf_start) < 0) return DECODE_SLICE_ERROR; *buf = buf_end; return DECODE_SLICE_OK; @@ -1883,7 +1602,7 @@ static int mpeg_decode_slice(MpegEncContext *s, int mb_y, /* read increment again */ s->mb_skip_run = 0; for (;;) { - int code = get_vlc2(&s->gb, ff_mbincr_vlc.table, + int code = get_vlc2(&s->gb, ff_mbincr_vlc, MBINCR_VLC_BITS, 2); if (code < 0) { av_log(s->avctx, AV_LOG_ERROR, "mb incr damaged\n"); @@ -2012,7 +1731,7 @@ static int slice_end(AVCodecContext *avctx, AVFrame *pict) return 0; if (s->avctx->hwaccel) { - int ret = s->avctx->hwaccel->end_frame(s->avctx); + int ret = FF_HW_SIMPLE_CALL(s->avctx, end_frame); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "hardware accelerator failed to decode picture\n"); @@ -2024,7 +1743,7 @@ static int slice_end(AVCodecContext *avctx, AVFrame *pict) if (/* s->mb_y << field_pic == s->mb_height && */ !s->first_field && !s1->first_slice) { /* end of image */ - ff_er_frame_end(&s->er); + ff_er_frame_end(&s->er, NULL); ff_mpv_frame_end(s); @@ -2088,7 +1807,7 @@ static int mpeg1_decode_sequence(AVCodecContext *avctx, return AVERROR_INVALIDDATA; } - s1->rc_buffer_size = get_bits(&s->gb, 10) * 1024 * 16; + s->avctx->rc_buffer_size = get_bits(&s->gb, 10) * 1024 * 16; skip_bits(&s->gb, 1); /* get matrix */ @@ -2136,7 +1855,7 @@ static int mpeg1_decode_sequence(AVCodecContext *avctx, if (s->avctx->debug & FF_DEBUG_PICT_INFO) av_log(s->avctx, AV_LOG_DEBUG, "vbv buffer: %d, bitrate:%"PRId64", aspect_ratio_info: %d \n", - s1->rc_buffer_size, s->bit_rate, s1->aspect_ratio_info); + s->avctx->rc_buffer_size, s->bit_rate, s1->aspect_ratio_info); return 0; } @@ -2160,7 +1879,6 @@ static int vcr2_init_sequence(AVCodecContext *avctx) avctx->pix_fmt = mpeg_get_pixelformat(avctx); - ff_mpv_idct_init(s); if ((ret = ff_mpv_common_init(s)) < 0) return ret; s1->mpeg_enc_ctx_allocated = 1; @@ -2193,12 +1911,27 @@ static int vcr2_init_sequence(AVCodecContext *avctx) return 0; } +static void mpeg_set_cc_format(AVCodecContext *avctx, enum Mpeg2ClosedCaptionsFormat format, + const char *label) +{ + Mpeg1Context *s1 = avctx->priv_data; + + av_assert2(format != CC_FORMAT_AUTO); + + if (!s1->cc_format) { + s1->cc_format = format; + + av_log(avctx, AV_LOG_DEBUG, "CC: first seen substream is %s format\n", label); + } +} + static int mpeg_decode_a53_cc(AVCodecContext *avctx, const uint8_t *p, int buf_size) { Mpeg1Context *s1 = avctx->priv_data; - if (buf_size >= 6 && + if ((!s1->cc_format || s1->cc_format == CC_FORMAT_A53_PART4) && + buf_size >= 6 && p[0] == 'G' && p[1] == 'A' && p[2] == '9' && p[3] == '4' && p[4] == 3 && (p[5] & 0x40)) { /* extract A53 Part 4 CC data */ @@ -2217,9 +1950,11 @@ static int mpeg_decode_a53_cc(AVCodecContext *avctx, memcpy(s1->a53_buf_ref->data + old_size, p + 7, cc_count * UINT64_C(3)); avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; + mpeg_set_cc_format(avctx, CC_FORMAT_A53_PART4, "A/53 Part 4"); } return 1; - } else if (buf_size >= 2 && + } else if ((!s1->cc_format || s1->cc_format == CC_FORMAT_SCTE20) && + buf_size >= 2 && p[0] == 0x03 && (p[1]&0x7f) == 0x01) { /* extract SCTE-20 CC data */ GetBitContext gb; @@ -2263,10 +1998,13 @@ static int mpeg_decode_a53_cc(AVCodecContext *avctx, cap += 3; } } + avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; + mpeg_set_cc_format(avctx, CC_FORMAT_SCTE20, "SCTE-20"); } return 1; - } else if (buf_size >= 11 && + } else if ((!s1->cc_format || s1->cc_format == CC_FORMAT_DVD) && + buf_size >= 11 && p[0] == 'C' && p[1] == 'C' && p[2] == 0x01 && p[3] == 0xf8) { /* extract DVD CC data * @@ -2323,7 +2061,9 @@ static int mpeg_decode_a53_cc(AVCodecContext *avctx, p += 6; } } + avctx->properties |= FF_CODEC_PROPERTY_CLOSED_CAPTIONS; + mpeg_set_cc_format(avctx, CC_FORMAT_DVD, "DVD"); } return 1; } @@ -2508,6 +2248,11 @@ static int decode_chunks(AVCodecContext *avctx, AVFrame *picture, } picture_start_code_seen = 1; + if (buf == avctx->extradata && avctx->codec_tag == AV_RL32("AVmp")) { + av_log(avctx, AV_LOG_WARNING, "ignoring picture start code in AVmp extradata\n"); + break; + } + if (s2->width <= 0 || s2->height <= 0) { av_log(avctx, AV_LOG_ERROR, "Invalid frame dimensions %dx%d.\n", s2->width, s2->height); @@ -2883,11 +2628,39 @@ const FFCodec ff_mpeg1video_decoder = { }, }; +#define M2V_OFFSET(x) offsetof(Mpeg1Context, x) +#define M2V_PARAM AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM + +static const AVOption mpeg2video_options[] = { + { "cc_format", "extract a specific Closed Captions format", + M2V_OFFSET(cc_format), AV_OPT_TYPE_INT, { .i64 = CC_FORMAT_AUTO }, + CC_FORMAT_AUTO, CC_FORMAT_DVD, M2V_PARAM, .unit = "cc_format" }, + + { "auto", "pick first seen CC substream", 0, AV_OPT_TYPE_CONST, + { .i64 = CC_FORMAT_AUTO }, .flags = M2V_PARAM, .unit = "cc_format" }, + { "a53", "pick A/53 Part 4 CC substream", 0, AV_OPT_TYPE_CONST, + { .i64 = CC_FORMAT_A53_PART4 }, .flags = M2V_PARAM, .unit = "cc_format" }, + { "scte20", "pick SCTE-20 CC substream", 0, AV_OPT_TYPE_CONST, + { .i64 = CC_FORMAT_SCTE20 }, .flags = M2V_PARAM, .unit = "cc_format" }, + { "dvd", "pick DVD CC substream", 0, AV_OPT_TYPE_CONST, + { .i64 = CC_FORMAT_DVD }, .flags = M2V_PARAM, .unit = "cc_format" }, + { NULL } +}; + +static const AVClass mpeg2video_class = { + .class_name = "MPEG-2 video", + .item_name = av_default_item_name, + .option = mpeg2video_options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DECODER, +}; + const FFCodec ff_mpeg2video_decoder = { .p.name = "mpeg2video", CODEC_LONG_NAME("MPEG-2 video"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.priv_class = &mpeg2video_class, .priv_data_size = sizeof(Mpeg1Context), .init = mpeg_decode_init, .close = mpeg_decode_end, @@ -2908,6 +2681,9 @@ const FFCodec ff_mpeg2video_decoder = { #if CONFIG_MPEG2_D3D11VA2_HWACCEL HWACCEL_D3D11VA2(mpeg2), #endif +#if CONFIG_MPEG2_D3D12VA_HWACCEL + HWACCEL_D3D12VA(mpeg2), +#endif #if CONFIG_MPEG2_NVDEC_HWACCEL HWACCEL_NVDEC(mpeg2), #endif @@ -2957,7 +2733,7 @@ static int ipu_decode_frame(AVCodecContext *avctx, AVFrame *frame, int ret; // Check for minimal intra MB size (considering mb header, luma & chroma dc VLC, ac EOB VLC) - if (avpkt->size*8LL < (avctx->width+15)/16 * ((avctx->height+15)/16) * (2 + 3*4 + 2*2 + 2*6)) + if (avpkt->size*8LL < (avctx->width+15)/16 * ((avctx->height+15)/16) * (2LL + 3*4 + 2*2 + 2*6)) return AVERROR_INVALIDDATA; ret = ff_get_buffer(avctx, frame, 0); @@ -3046,7 +2822,7 @@ static int ipu_decode_frame(AVCodecContext *avctx, AVFrame *frame, return AVERROR_INVALIDDATA; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; @@ -3060,7 +2836,6 @@ static av_cold int ipu_decode_init(AVCodecContext *avctx) avctx->pix_fmt = AV_PIX_FMT_YUV420P; ff_mpv_decode_init(m, avctx); - ff_mpv_idct_init(m); ff_mpeg12_init_vlcs(); for (int i = 0; i < 64; i++) { diff --git a/libavcodec/mpeg12dec.h b/libavcodec/mpeg12dec.h index 4c015d3096c..46411791490 100644 --- a/libavcodec/mpeg12dec.h +++ b/libavcodec/mpeg12dec.h @@ -30,9 +30,9 @@ static inline int decode_dc(GetBitContext *gb, int component) int code, diff; if (component == 0) { - code = get_vlc2(gb, ff_dc_lum_vlc.table, DC_VLC_BITS, 2); + code = get_vlc2(gb, ff_dc_lum_vlc, DC_VLC_BITS, 2); } else { - code = get_vlc2(gb, ff_dc_chroma_vlc.table, DC_VLC_BITS, 2); + code = get_vlc2(gb, ff_dc_chroma_vlc, DC_VLC_BITS, 2); } if (code == 0) { diff = 0; diff --git a/libavcodec/mpeg12enc.c b/libavcodec/mpeg12enc.c index a932b59678f..76377aea1b6 100644 --- a/libavcodec/mpeg12enc.c +++ b/libavcodec/mpeg12enc.c @@ -203,23 +203,23 @@ static av_cold int encode_init(AVCodecContext *avctx) } } - if (avctx->profile == FF_PROFILE_UNKNOWN) { - if (avctx->level != FF_LEVEL_UNKNOWN) { + if (avctx->profile == AV_PROFILE_UNKNOWN) { + if (avctx->level != AV_LEVEL_UNKNOWN) { av_log(avctx, AV_LOG_ERROR, "Set profile and level\n"); return AVERROR(EINVAL); } /* Main or 4:2:2 */ - avctx->profile = avctx->pix_fmt == AV_PIX_FMT_YUV420P ? FF_PROFILE_MPEG2_MAIN - : FF_PROFILE_MPEG2_422; + avctx->profile = avctx->pix_fmt == AV_PIX_FMT_YUV420P ? AV_PROFILE_MPEG2_MAIN + : AV_PROFILE_MPEG2_422; } - if (avctx->level == FF_LEVEL_UNKNOWN) { - if (avctx->profile == FF_PROFILE_MPEG2_422) { /* 4:2:2 */ + if (avctx->level == AV_LEVEL_UNKNOWN) { + if (avctx->profile == AV_PROFILE_MPEG2_422) { /* 4:2:2 */ if (avctx->width <= 720 && avctx->height <= 608) avctx->level = 5; /* Main */ else avctx->level = 2; /* High */ } else { - if (avctx->profile != FF_PROFILE_MPEG2_HIGH && + if (avctx->profile != AV_PROFILE_MPEG2_HIGH && avctx->pix_fmt != AV_PIX_FMT_YUV420P) { av_log(avctx, AV_LOG_ERROR, "Only High(1) and 4:2:2(0) profiles support 4:2:2 color sampling\n"); @@ -290,7 +290,7 @@ static void mpeg1_encode_sequence_header(MpegEncContext *s) AVRational aspect_ratio = s->avctx->sample_aspect_ratio; int aspect_ratio_info; - if (!s->current_picture.f->key_frame) + if (!(s->current_picture.f->flags & AV_FRAME_FLAG_KEY)) return; if (aspect_ratio.num == 0 || aspect_ratio.den == 0) @@ -333,7 +333,7 @@ static void mpeg1_encode_sequence_header(MpegEncContext *s) else /* VBV calculation: Scaled so that a VCD has the proper * VBV size of 40 kilobytes */ - vbv_buffer_size = ((20 * s->bit_rate) / (1151929 / 2)) * 8 * 1024; + vbv_buffer_size = av_rescale_rnd(s->bit_rate, 20, 1151929 / 2, AV_ROUND_ZERO) * 8 * 1024; vbv_buffer_size = (vbv_buffer_size + 16383) / 16384; put_sbits(&s->pb, 18, v); @@ -366,7 +366,7 @@ static void mpeg1_encode_sequence_header(MpegEncContext *s) put_header(s, EXT_START_CODE); put_bits(&s->pb, 4, 1); // seq ext - put_bits(&s->pb, 1, s->avctx->profile == FF_PROFILE_MPEG2_422); // escx 1 for 4:2:2 profile + put_bits(&s->pb, 1, s->avctx->profile == AV_PROFILE_MPEG2_422); // escx 1 for 4:2:2 profile put_bits(&s->pb, 3, s->avctx->profile); // profile put_bits(&s->pb, 4, s->avctx->level); // level @@ -530,7 +530,7 @@ void ff_mpeg1_encode_picture_header(MpegEncContext *s) if (s->progressive_sequence) put_bits(&s->pb, 1, 0); /* no repeat */ else - put_bits(&s->pb, 1, s->current_picture_ptr->f->top_field_first); + put_bits(&s->pb, 1, !!(s->current_picture_ptr->f->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)); /* XXX: optimize the generation of this flag with entropy measures */ s->frame_pred_frame_dct = s->progressive_sequence; @@ -1199,18 +1199,18 @@ static const AVOption mpeg2_options[] = { { "non_linear_quant", "Use nonlinear quantizer.", FF_MPV_OFFSET(q_scale_type), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "alternate_scan", "Enable alternate scantable.", FF_MPV_OFFSET(alternate_scan), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "a53cc", "Use A53 Closed Captions (if available)", OFFSET(a53_cc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, - { "seq_disp_ext", "Write sequence_display_extension blocks.", OFFSET(seq_disp_ext), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE, "seq_disp_ext" }, - { "auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, VE, "seq_disp_ext" }, - { "never", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, VE, "seq_disp_ext" }, - { "always", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, VE, "seq_disp_ext" }, - { "video_format", "Video_format in the sequence_display_extension indicating the source of the video.", OFFSET(video_format), AV_OPT_TYPE_INT, { .i64 = VIDEO_FORMAT_UNSPECIFIED }, 0, 7, VE, "video_format" }, - { "component", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VIDEO_FORMAT_COMPONENT }, 0, 0, VE, "video_format" }, - { "pal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VIDEO_FORMAT_PAL }, 0, 0, VE, "video_format" }, - { "ntsc", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VIDEO_FORMAT_NTSC }, 0, 0, VE, "video_format" }, - { "secam", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VIDEO_FORMAT_SECAM }, 0, 0, VE, "video_format" }, - { "mac", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VIDEO_FORMAT_MAC }, 0, 0, VE, "video_format" }, - { "unspecified", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VIDEO_FORMAT_UNSPECIFIED}, 0, 0, VE, "video_format" }, -#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, { .i64 = value }, 0, 0, VE, "avctx.level" + { "seq_disp_ext", "Write sequence_display_extension blocks.", OFFSET(seq_disp_ext), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE, .unit = "seq_disp_ext" }, + { "auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, VE, .unit = "seq_disp_ext" }, + { "never", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, VE, .unit = "seq_disp_ext" }, + { "always", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, VE, .unit = "seq_disp_ext" }, + { "video_format", "Video_format in the sequence_display_extension indicating the source of the video.", OFFSET(video_format), AV_OPT_TYPE_INT, { .i64 = VIDEO_FORMAT_UNSPECIFIED }, 0, 7, VE, .unit = "video_format" }, + { "component", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VIDEO_FORMAT_COMPONENT }, 0, 0, VE, .unit = "video_format" }, + { "pal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VIDEO_FORMAT_PAL }, 0, 0, VE, .unit = "video_format" }, + { "ntsc", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VIDEO_FORMAT_NTSC }, 0, 0, VE, .unit = "video_format" }, + { "secam", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VIDEO_FORMAT_SECAM }, 0, 0, VE, .unit = "video_format" }, + { "mac", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VIDEO_FORMAT_MAC }, 0, 0, VE, .unit = "video_format" }, + { "unspecified", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = VIDEO_FORMAT_UNSPECIFIED}, 0, 0, VE, .unit = "video_format" }, +#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, { .i64 = value }, 0, 0, VE, .unit = "avctx.level" { LEVEL("high", 4) }, { LEVEL("high1440", 6) }, { LEVEL("main", 8) }, diff --git a/libavcodec/mpeg12vlc.h b/libavcodec/mpeg12vlc.h index 3ed35968f6f..53f53947c3c 100644 --- a/libavcodec/mpeg12vlc.h +++ b/libavcodec/mpeg12vlc.h @@ -39,13 +39,13 @@ #define MB_PTYPE_VLC_BITS 6 #define MB_BTYPE_VLC_BITS 6 -extern VLC ff_dc_lum_vlc; -extern VLC ff_dc_chroma_vlc; -extern VLC ff_mbincr_vlc; -extern VLC ff_mb_ptype_vlc; -extern VLC ff_mb_btype_vlc; -extern VLC ff_mb_pat_vlc; -extern VLC ff_mv_vlc; +extern VLCElem ff_dc_lum_vlc[]; +extern VLCElem ff_dc_chroma_vlc[]; +extern VLCElem ff_mbincr_vlc[]; +extern VLCElem ff_mb_ptype_vlc[]; +extern VLCElem ff_mb_btype_vlc[]; +extern VLCElem ff_mb_pat_vlc[]; +extern VLCElem ff_mv_vlc[]; void ff_mpeg12_init_vlcs(void); diff --git a/libavcodec/mpeg4video_parser.c b/libavcodec/mpeg4video_parser.c index 28353aa146b..402594e01d8 100644 --- a/libavcodec/mpeg4video_parser.c +++ b/libavcodec/mpeg4video_parser.c @@ -89,7 +89,6 @@ static int mpeg4_decode_header(AVCodecParserContext *s1, AVCodecContext *avctx, int ret; s->avctx = avctx; - s->current_picture_ptr = &s->current_picture; if (avctx->extradata_size && pc->first_picture) { init_get_bits(gb, avctx->extradata, avctx->extradata_size * 8); diff --git a/libavcodec/mpeg4videodec.c b/libavcodec/mpeg4videodec.c index e6317c04de9..04a9ae504e6 100644 --- a/libavcodec/mpeg4videodec.c +++ b/libavcodec/mpeg4videodec.c @@ -42,6 +42,7 @@ #include "h263.h" #include "h263data.h" #include "h263dec.h" +#include "internal.h" #include "profiles.h" #include "qpeldsp.h" #include "threadframe.h" @@ -57,12 +58,12 @@ #define MB_TYPE_B_VLC_BITS 4 #define STUDIO_INTRA_BITS 9 -static VLC dc_lum, dc_chrom; -static VLC sprite_trajectory; -static VLC mb_type_b_vlc; -static VLC studio_intra_tab[12]; -static VLC studio_luma_dc; -static VLC studio_chroma_dc; +static VLCElem dc_lum[512], dc_chrom[512]; +static VLCElem sprite_trajectory[128]; +static VLCElem mb_type_b_vlc[16]; +static const VLCElem *studio_intra_tab[12]; +static VLCElem studio_luma_dc[528]; +static VLCElem studio_chroma_dc[528]; static const uint8_t mpeg4_block_count[4] = { 0, 6, 8, 12 }; @@ -448,14 +449,14 @@ static int mpeg4_decode_sprite_trajectory(Mpeg4DecContext *ctx, GetBitContext *g int length; int x = 0, y = 0; - length = get_vlc2(gb, sprite_trajectory.table, SPRITE_TRAJ_VLC_BITS, 2); + length = get_vlc2(gb, sprite_trajectory, SPRITE_TRAJ_VLC_BITS, 2); if (length > 0) x = get_xbits(gb, length); if (!(ctx->divx_version == 500 && ctx->divx_build == 413)) check_marker(s->avctx, gb, "before sprite_trajectory"); - length = get_vlc2(gb, sprite_trajectory.table, SPRITE_TRAJ_VLC_BITS, 2); + length = get_vlc2(gb, sprite_trajectory, SPRITE_TRAJ_VLC_BITS, 2); if (length > 0) y = get_xbits(gb, length); @@ -596,6 +597,8 @@ static int mpeg4_decode_sprite_trajectory(Mpeg4DecContext *ctx, GetBitContext *g ctx->sprite_shift[0] = alpha + beta + rho - min_ab; ctx->sprite_shift[1] = alpha + beta + rho - min_ab + 2; break; + default: + av_assert0(0); } /* try to simplify the situation */ if (sprite_delta[0][0] == a << ctx->sprite_shift[0] && @@ -734,10 +737,8 @@ int ff_mpeg4_decode_video_packet_header(Mpeg4DecContext *ctx) header_extension = get_bits1(&s->gb); if (header_extension) { - int time_incr = 0; - while (get_bits1(&s->gb) != 0) - time_incr++; + ; check_marker(s->avctx, &s->gb, "before time_increment in video packed header"); skip_bits(&s->gb, ctx->time_increment_bits); /* time_increment */ @@ -890,9 +891,9 @@ static inline int mpeg4_decode_dc(MpegEncContext *s, int n, int *dir_ptr) int level, code; if (n < 4) - code = get_vlc2(&s->gb, dc_lum.table, DC_VLC_BITS, 1); + code = get_vlc2(&s->gb, dc_lum, DC_VLC_BITS, 1); else - code = get_vlc2(&s->gb, dc_chrom.table, DC_VLC_BITS, 1); + code = get_vlc2(&s->gb, dc_chrom, DC_VLC_BITS, 1); if (code < 0 || code > 9 /* && s->nbit < 9 */) { av_log(s->avctx, AV_LOG_ERROR, "illegal dc vlc\n"); @@ -960,7 +961,7 @@ static int mpeg4_decode_partition_a(Mpeg4DecContext *ctx) if (show_bits(&s->gb, 19) == DC_MARKER) return mb_num - 1; - cbpc = get_vlc2(&s->gb, ff_h263_intra_MCBPC_vlc.table, INTRA_MCBPC_VLC_BITS, 2); + cbpc = get_vlc2(&s->gb, ff_h263_intra_MCBPC_vlc, INTRA_MCBPC_VLC_BITS, 2); if (cbpc < 0) { av_log(s->avctx, AV_LOG_ERROR, "mcbpc corrupted at %d %d\n", s->mb_x, s->mb_y); @@ -1032,7 +1033,7 @@ static int mpeg4_decode_partition_a(Mpeg4DecContext *ctx) continue; } - cbpc = get_vlc2(&s->gb, ff_h263_inter_MCBPC_vlc.table, INTER_MCBPC_VLC_BITS, 2); + cbpc = get_vlc2(&s->gb, ff_h263_inter_MCBPC_vlc, INTER_MCBPC_VLC_BITS, 2); if (cbpc < 0) { av_log(s->avctx, AV_LOG_ERROR, "mcbpc corrupted at %d %d\n", s->mb_x, s->mb_y); @@ -1147,7 +1148,7 @@ static int mpeg4_decode_partition_b(MpegEncContext *s, int mb_count) if (s->pict_type == AV_PICTURE_TYPE_I) { int ac_pred = get_bits1(&s->gb); - int cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc.table, CBPY_VLC_BITS, 1); + int cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc, CBPY_VLC_BITS, 1); if (cbpy < 0) { av_log(s->avctx, AV_LOG_ERROR, "cbpy corrupted at %d %d\n", s->mb_x, s->mb_y); @@ -1161,7 +1162,7 @@ static int mpeg4_decode_partition_b(MpegEncContext *s, int mb_count) int i; int dir = 0; int ac_pred = get_bits1(&s->gb); - int cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc.table, CBPY_VLC_BITS, 1); + int cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc, CBPY_VLC_BITS, 1); if (cbpy < 0) { av_log(s->avctx, AV_LOG_ERROR, @@ -1193,7 +1194,7 @@ static int mpeg4_decode_partition_b(MpegEncContext *s, int mb_count) s->current_picture.qscale_table[xy] = s->qscale; s->cbp_table[xy] = 0; } else { - int cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc.table, CBPY_VLC_BITS, 1); + int cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc, CBPY_VLC_BITS, 1); if (cbpy < 0) { av_log(s->avctx, AV_LOG_ERROR, @@ -1689,7 +1690,7 @@ static int mpeg4_decode_mb(MpegEncContext *s, int16_t block[6][64]) } goto end; } - cbpc = get_vlc2(&s->gb, ff_h263_inter_MCBPC_vlc.table, INTER_MCBPC_VLC_BITS, 2); + cbpc = get_vlc2(&s->gb, ff_h263_inter_MCBPC_vlc, INTER_MCBPC_VLC_BITS, 2); if (cbpc < 0) { av_log(s->avctx, AV_LOG_ERROR, "mcbpc damaged at %d %d\n", s->mb_x, s->mb_y); @@ -1708,7 +1709,7 @@ static int mpeg4_decode_mb(MpegEncContext *s, int16_t block[6][64]) s->mcsel = get_bits1(&s->gb); else s->mcsel = 0; - cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc.table, CBPY_VLC_BITS, 1) ^ 0x0F; + cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc, CBPY_VLC_BITS, 1) ^ 0x0F; if (cbpy < 0) { av_log(s->avctx, AV_LOG_ERROR, "P cbpy damaged at %d %d\n", s->mb_x, s->mb_y); @@ -1839,7 +1840,7 @@ static int mpeg4_decode_mb(MpegEncContext *s, int16_t block[6][64]) cbp = 0; } else { modb2 = get_bits1(&s->gb); - mb_type = get_vlc2(&s->gb, mb_type_b_vlc.table, MB_TYPE_B_VLC_BITS, 1); + mb_type = get_vlc2(&s->gb, mb_type_b_vlc, MB_TYPE_B_VLC_BITS, 1); if (mb_type < 0) { av_log(s->avctx, AV_LOG_ERROR, "illegal MB_type\n"); return AVERROR_INVALIDDATA; @@ -1951,7 +1952,7 @@ static int mpeg4_decode_mb(MpegEncContext *s, int16_t block[6][64]) int use_intra_dc_vlc; do { - cbpc = get_vlc2(&s->gb, ff_h263_intra_MCBPC_vlc.table, INTRA_MCBPC_VLC_BITS, 2); + cbpc = get_vlc2(&s->gb, ff_h263_intra_MCBPC_vlc, INTRA_MCBPC_VLC_BITS, 2); if (cbpc < 0) { av_log(s->avctx, AV_LOG_ERROR, "I cbpc damaged at %d %d\n", s->mb_x, s->mb_y); @@ -1969,7 +1970,7 @@ static int mpeg4_decode_mb(MpegEncContext *s, int16_t block[6][64]) else s->current_picture.mb_type[xy] = MB_TYPE_INTRA; - cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc.table, CBPY_VLC_BITS, 1); + cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc, CBPY_VLC_BITS, 1); if (cbpy < 0) { av_log(s->avctx, AV_LOG_ERROR, "I cbpy damaged at %d %d\n", s->mb_x, s->mb_y); @@ -2071,7 +2072,7 @@ static int mpeg4_decode_studio_block(MpegEncContext *s, int32_t block[64], int n int cc, dct_dc_size, dct_diff, code, j, idx = 1, group = 0, run = 0, additional_code_len, sign, mismatch; - const VLC *cur_vlc = &studio_intra_tab[0]; + const VLCElem *cur_vlc = studio_intra_tab[0]; uint8_t *const scantable = s->intra_scantable.permutated; const uint16_t *quant_matrix; uint32_t flc; @@ -2085,14 +2086,14 @@ static int mpeg4_decode_studio_block(MpegEncContext *s, int32_t block[64], int n if (n < 4) { cc = 0; - dct_dc_size = get_vlc2(&s->gb, studio_luma_dc.table, STUDIO_INTRA_BITS, 2); + dct_dc_size = get_vlc2(&s->gb, studio_luma_dc, STUDIO_INTRA_BITS, 2); quant_matrix = s->intra_matrix; } else { cc = (n & 1) + 1; if (ctx->rgb) - dct_dc_size = get_vlc2(&s->gb, studio_luma_dc.table, STUDIO_INTRA_BITS, 2); + dct_dc_size = get_vlc2(&s->gb, studio_luma_dc, STUDIO_INTRA_BITS, 2); else - dct_dc_size = get_vlc2(&s->gb, studio_chroma_dc.table, STUDIO_INTRA_BITS, 2); + dct_dc_size = get_vlc2(&s->gb, studio_chroma_dc, STUDIO_INTRA_BITS, 2); quant_matrix = s->chroma_intra_matrix; } @@ -2121,7 +2122,7 @@ static int mpeg4_decode_studio_block(MpegEncContext *s, int32_t block[64], int n /* AC Coefficients */ while (1) { - group = get_vlc2(&s->gb, cur_vlc->table, STUDIO_INTRA_BITS, 2); + group = get_vlc2(&s->gb, cur_vlc, STUDIO_INTRA_BITS, 2); if (group < 0) { av_log(s->avctx, AV_LOG_ERROR, "illegal ac coefficient group vlc\n"); @@ -2129,7 +2130,7 @@ static int mpeg4_decode_studio_block(MpegEncContext *s, int32_t block[64], int n } additional_code_len = ac_state_tab[group][0]; - cur_vlc = &studio_intra_tab[ac_state_tab[group][1]]; + cur_vlc = studio_intra_tab[ac_state_tab[group][1]]; if (group == 0) { /* End of Block */ @@ -2564,10 +2565,10 @@ static int decode_vol_header(Mpeg4DecContext *ctx, GetBitContext *gb) */ if (ctx->vo_type == CORE_STUDIO_VO_TYPE || ctx->vo_type == SIMPLE_STUDIO_VO_TYPE) { - if (s->avctx->profile != FF_PROFILE_UNKNOWN && s->avctx->profile != FF_PROFILE_MPEG4_SIMPLE_STUDIO) + if (s->avctx->profile != AV_PROFILE_UNKNOWN && s->avctx->profile != AV_PROFILE_MPEG4_SIMPLE_STUDIO) return AVERROR_INVALIDDATA; s->studio_profile = 1; - s->avctx->profile = FF_PROFILE_MPEG4_SIMPLE_STUDIO; + s->avctx->profile = AV_PROFILE_MPEG4_SIMPLE_STUDIO; return decode_studio_vol_header(ctx, gb); } else if (s->studio_profile) { return AVERROR_PATCHWELCOME; @@ -3141,9 +3142,6 @@ static int decode_vop_header(Mpeg4DecContext *ctx, GetBitContext *gb, av_log(s->avctx, AV_LOG_WARNING, "time_increment_bits set to %d bits, based on bitstream analysis\n", ctx->time_increment_bits); - if (s->avctx->framerate.num && 4*s->avctx->framerate.num < 1<time_increment_bits) { - s->avctx->framerate.num = 1<time_increment_bits; - } } if (IS_3IV1) @@ -3605,7 +3603,7 @@ int ff_mpeg4_decode_picture_header(Mpeg4DecContext *ctx, GetBitContext *gb, } else if (startcode == VOS_STARTCODE) { int profile, level; mpeg4_decode_profile_level(s, gb, &profile, &level); - if (profile == FF_PROFILE_MPEG4_SIMPLE_STUDIO && + if (profile == AV_PROFILE_MPEG4_SIMPLE_STUDIO && (level > 0 && level < 9)) { s->studio_profile = 1; next_start_code_studio(gb); @@ -3760,48 +3758,45 @@ static int mpeg4_update_thread_context_for_user(AVCodecContext *dst, static av_cold void mpeg4_init_static(void) { static uint8_t mpeg4_rvlc_rl_tables[2][2][2 * MAX_RUN + MAX_LEVEL + 3]; + static VLCElem vlc_buf[6498]; + VLCInitState state = VLC_INIT_STATE(vlc_buf); - INIT_VLC_STATIC_FROM_LENGTHS(&studio_luma_dc, STUDIO_INTRA_BITS, 19, - &ff_mpeg4_studio_dc_luma[0][1], 2, - &ff_mpeg4_studio_dc_luma[0][0], 2, 1, - 0, 0, 528); - - INIT_VLC_STATIC_FROM_LENGTHS(&studio_chroma_dc, STUDIO_INTRA_BITS, 19, - &ff_mpeg4_studio_dc_chroma[0][1], 2, - &ff_mpeg4_studio_dc_chroma[0][0], 2, 1, - 0, 0, 528); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(studio_luma_dc, STUDIO_INTRA_BITS, 19, + &ff_mpeg4_studio_dc_luma[0][1], 2, + &ff_mpeg4_studio_dc_luma[0][0], 2, 1, + 0, 0); - for (unsigned i = 0, offset = 0; i < 12; i++) { - static VLCElem vlc_buf[6498]; + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(studio_chroma_dc, STUDIO_INTRA_BITS, 19, + &ff_mpeg4_studio_dc_chroma[0][1], 2, + &ff_mpeg4_studio_dc_chroma[0][0], 2, 1, + 0, 0); - studio_intra_tab[i].table = &vlc_buf[offset]; - studio_intra_tab[i].table_allocated = FF_ARRAY_ELEMS(vlc_buf) - offset; - ff_init_vlc_from_lengths(&studio_intra_tab[i], - STUDIO_INTRA_BITS, 24, - &ff_mpeg4_studio_intra[i][0][1], 2, - &ff_mpeg4_studio_intra[i][0][0], 2, 1, - 0, INIT_VLC_STATIC_OVERLONG, NULL); - offset += studio_intra_tab[i].table_size; + for (unsigned i = 0; i < 12; i++) { + studio_intra_tab[i] = + ff_vlc_init_tables_from_lengths(&state, STUDIO_INTRA_BITS, 24, + &ff_mpeg4_studio_intra[i][0][1], 2, + &ff_mpeg4_studio_intra[i][0][0], 2, 1, + 0, 0); } ff_mpeg4_init_rl_intra(); ff_rl_init(&ff_rvlc_rl_inter, mpeg4_rvlc_rl_tables[0]); ff_rl_init(&ff_rvlc_rl_intra, mpeg4_rvlc_rl_tables[1]); INIT_FIRST_VLC_RL(ff_mpeg4_rl_intra, 554); - INIT_VLC_RL(ff_rvlc_rl_inter, 1072); + VLC_INIT_RL(ff_rvlc_rl_inter, 1072); INIT_FIRST_VLC_RL(ff_rvlc_rl_intra, 1072); - INIT_VLC_STATIC(&dc_lum, DC_VLC_BITS, 10 /* 13 */, - &ff_mpeg4_DCtab_lum[0][1], 2, 1, - &ff_mpeg4_DCtab_lum[0][0], 2, 1, 512); - INIT_VLC_STATIC(&dc_chrom, DC_VLC_BITS, 10 /* 13 */, - &ff_mpeg4_DCtab_chrom[0][1], 2, 1, - &ff_mpeg4_DCtab_chrom[0][0], 2, 1, 512); - INIT_VLC_STATIC_FROM_LENGTHS(&sprite_trajectory, SPRITE_TRAJ_VLC_BITS, 15, - ff_sprite_trajectory_lens, 1, - NULL, 0, 0, 0, 0, 128); - INIT_VLC_STATIC(&mb_type_b_vlc, MB_TYPE_B_VLC_BITS, 4, - &ff_mb_type_b_tab[0][1], 2, 1, - &ff_mb_type_b_tab[0][0], 2, 1, 16); + VLC_INIT_STATIC_TABLE(dc_lum, DC_VLC_BITS, 10 /* 13 */, + &ff_mpeg4_DCtab_lum[0][1], 2, 1, + &ff_mpeg4_DCtab_lum[0][0], 2, 1, 0); + VLC_INIT_STATIC_TABLE(dc_chrom, DC_VLC_BITS, 10 /* 13 */, + &ff_mpeg4_DCtab_chrom[0][1], 2, 1, + &ff_mpeg4_DCtab_chrom[0][0], 2, 1, 0); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(sprite_trajectory, SPRITE_TRAJ_VLC_BITS, 15, + ff_sprite_trajectory_lens, 1, + NULL, 0, 0, 0, 0); + VLC_INIT_STATIC_TABLE(mb_type_b_vlc, MB_TYPE_B_VLC_BITS, 4, + &ff_mb_type_b_tab[0][1], 2, 1, + &ff_mb_type_b_tab[0][0], 2, 1, 0); } static av_cold int decode_init(AVCodecContext *avctx) @@ -3831,6 +3826,14 @@ static av_cold int decode_init(AVCodecContext *avctx) ff_thread_once(&init_static_once, mpeg4_init_static); + /* Must be after initializing the MPEG-4 static tables */ + if (avctx->extradata_size && !avctx->internal->is_copy) { + GetBitContext gb; + + if (init_get_bits8(&gb, avctx->extradata, avctx->extradata_size) >= 0) + ff_mpeg4_decode_picture_header(ctx, &gb, 1, 0); + } + return 0; } @@ -3864,7 +3867,6 @@ const FFCodec ff_mpeg4_decoder = { FF_CODEC_CAP_ALLOCATE_PROGRESS, .flush = ff_mpeg_flush, .p.max_lowres = 3, - .p.pix_fmts = ff_h263_hwaccel_pixfmt_list_420, .p.profiles = NULL_IF_CONFIG_SMALL(ff_mpeg4_video_profiles), UPDATE_THREAD_CONTEXT(mpeg4_update_thread_context), UPDATE_THREAD_CONTEXT_FOR_USER(mpeg4_update_thread_context_for_user), diff --git a/libavcodec/mpeg4videodec.h b/libavcodec/mpeg4videodec.h index c0e6ec6592e..4a26d189872 100644 --- a/libavcodec/mpeg4videodec.h +++ b/libavcodec/mpeg4videodec.h @@ -29,6 +29,7 @@ #include "mpegvideo.h" #include "mpeg4videodsp.h" +#include "libavutil/mem_internal.h" typedef struct Mpeg4DecContext { MpegEncContext m; @@ -83,7 +84,7 @@ typedef struct Mpeg4DecContext { Mpeg4VideoDSPContext mdsp; - int32_t block32[12][64]; + DECLARE_ALIGNED(8, int32_t, block32)[12][64]; // 0 = DCT, 1 = DPCM top to bottom scan, -1 = DPCM bottom to top scan int dpcm_direction; int16_t dpcm_macroblock[3][256]; diff --git a/libavcodec/mpeg4videoenc.c b/libavcodec/mpeg4videoenc.c index c3e9ebea452..d1e50612ddf 100644 --- a/libavcodec/mpeg4videoenc.c +++ b/libavcodec/mpeg4videoenc.c @@ -916,7 +916,7 @@ static void mpeg4_encode_visual_object_header(MpegEncContext *s) int profile_and_level_indication; int vo_ver_id; - if (s->avctx->profile != FF_PROFILE_UNKNOWN) { + if (s->avctx->profile != AV_PROFILE_UNKNOWN) { profile_and_level_indication = s->avctx->profile << 4; } else if (s->max_b_frames || s->quarter_sample) { profile_and_level_indication = 0xF0; // adv simple @@ -924,7 +924,7 @@ static void mpeg4_encode_visual_object_header(MpegEncContext *s) profile_and_level_indication = 0x00; // simple } - if (s->avctx->level != FF_LEVEL_UNKNOWN) + if (s->avctx->level != AV_LEVEL_UNKNOWN) profile_and_level_indication |= s->avctx->level; else profile_and_level_indication |= 1; // level 1 @@ -1101,7 +1101,7 @@ int ff_mpeg4_encode_picture_header(MpegEncContext *s) } put_bits(&s->pb, 3, 0); /* intra dc VLC threshold */ if (!s->progressive_sequence) { - put_bits(&s->pb, 1, s->current_picture_ptr->f->top_field_first); + put_bits(&s->pb, 1, !!(s->current_picture_ptr->f->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)); put_bits(&s->pb, 1, s->alternate_scan); } // FIXME sprite stuff diff --git a/libavcodec/mpegaudiodata.h b/libavcodec/mpegaudiodata.h index a4148a1ffec..720c4bee645 100644 --- a/libavcodec/mpegaudiodata.h +++ b/libavcodec/mpegaudiodata.h @@ -31,11 +31,13 @@ #include "config.h" +#include "libavutil/attributes_internal.h" #include "vlc.h" #define MODE_EXT_MS_STEREO 2 #define MODE_EXT_I_STEREO 1 +FF_VISIBILITY_PUSH_HIDDEN extern const uint16_t ff_mpa_bitrate_tab[2][3][15]; extern const uint16_t ff_mpa_freq_tab[3]; extern const int ff_mpa_sblimit_table[5]; @@ -53,7 +55,7 @@ extern uint32_t ff_table_4_3_value[TABLE_4_3_SIZE]; #endif /* VLCs for decoding layer 3 huffman tables */ -extern VLC ff_huff_vlc[16]; +extern const VLCElem *ff_huff_vlc[16]; extern VLC ff_huff_quad_vlc[2]; /* layer3 scale factor size */ @@ -78,5 +80,6 @@ extern const uint8_t ff_mpa_pretab[2][22]; /* Initialize tables shared between the fixed and * floating point MPEG audio decoders. */ void ff_mpegaudiodec_common_init_static(void); +FF_VISIBILITY_POP_HIDDEN #endif /* AVCODEC_MPEGAUDIODATA_H */ diff --git a/libavcodec/mpegaudiodec_common.c b/libavcodec/mpegaudiodec_common.c index 5fcb39b3259..6446d08967d 100644 --- a/libavcodec/mpegaudiodec_common.c +++ b/libavcodec/mpegaudiodec_common.c @@ -64,7 +64,7 @@ const uint8_t ff_lsf_nsf_table[6][3][4] = { }; /* mpegaudio layer 3 huffman tables */ -VLC ff_huff_vlc[16]; +const VLCElem *ff_huff_vlc[16]; static VLCElem huff_vlc_tables[128 + 128 + 128 + 130 + 128 + 154 + 166 + 142 + 204 + 190 + 170 + 542 + 460 + 662 + 414]; VLC ff_huff_quad_vlc[2]; @@ -401,6 +401,7 @@ const uint8_t ff_mpa_pretab[2][22] = { static av_cold void mpegaudiodec_common_init_static(void) { + VLCInitState state = VLC_INIT_STATE(huff_vlc_tables); const uint8_t *huff_sym = mpa_huffsymbols, *huff_lens = mpa_hufflens; int offset; @@ -414,7 +415,6 @@ static av_cold void mpegaudiodec_common_init_static(void) } /* huffman decode tables */ - offset = 0; for (int i = 0; i < 15;) { uint16_t tmp_symbols[256]; int nb_codes_minus_one = mpa_huff_sizes_minus_one[i]; @@ -426,16 +426,14 @@ static av_cold void mpegaudiodec_common_init_static(void) tmp_symbols[j] = high << 1 | ((high && low) << 4) | low; } - ff_huff_vlc[++i].table = huff_vlc_tables + offset; - ff_huff_vlc[i].table_allocated = FF_ARRAY_ELEMS(huff_vlc_tables) - offset; - ff_init_vlc_from_lengths(&ff_huff_vlc[i], 7, j, - huff_lens, 1, tmp_symbols, 2, 2, - 0, INIT_VLC_STATIC_OVERLONG, NULL); - offset += ff_huff_vlc[i].table_size; + ff_huff_vlc[++i] = ff_vlc_init_tables_from_lengths(&state, 7, j, + huff_lens, 1, + tmp_symbols, 2, 2, + 0, 0); huff_lens += j; huff_sym += j; } - av_assert0(offset == FF_ARRAY_ELEMS(huff_vlc_tables)); + av_assert1(state.size == 0); offset = 0; for (int i = 0; i < 2; i++) { @@ -443,9 +441,9 @@ static av_cold void mpegaudiodec_common_init_static(void) ff_huff_quad_vlc[i].table = huff_quad_vlc_tables + offset; ff_huff_quad_vlc[i].table_allocated = 1 << bits; offset += 1 << bits; - init_vlc(&ff_huff_quad_vlc[i], bits, 16, + vlc_init(&ff_huff_quad_vlc[i], bits, 16, mpa_quad_bits[i], 1, 1, mpa_quad_codes[i], 1, 1, - INIT_VLC_USE_NEW_STATIC); + VLC_INIT_USE_STATIC); } av_assert0(offset == FF_ARRAY_ELEMS(huff_quad_vlc_tables)); diff --git a/libavcodec/mpegaudiodec_template.c b/libavcodec/mpegaudiodec_template.c index 3e4ee79be69..2b84e657056 100644 --- a/libavcodec/mpegaudiodec_template.c +++ b/libavcodec/mpegaudiodec_template.c @@ -92,7 +92,7 @@ typedef struct MPADecodeContext { int err_recognition; AVCodecContext* avctx; MPADSPContext mpadsp; - void (*butterflies_float)(float *av_restrict v1, float *av_restrict v2, int len); + void (*butterflies_float)(float *restrict v1, float *restrict v2, int len); AVFrame *frame; uint32_t crc; } MPADecodeContext; @@ -760,6 +760,7 @@ static int huffman_decode(MPADecodeContext *s, GranuleDef *g, /* low frequencies (called big values) */ s_index = 0; for (i = 0; i < 3; i++) { + const VLCElem *vlctab; int j, k, l, linbits; j = g->region_size[i]; if (j == 0) @@ -768,13 +769,13 @@ static int huffman_decode(MPADecodeContext *s, GranuleDef *g, k = g->table_select[i]; l = ff_mpa_huff_data[k][0]; linbits = ff_mpa_huff_data[k][1]; - vlc = &ff_huff_vlc[l]; if (!l) { memset(&g->sb_hybrid[s_index], 0, sizeof(*g->sb_hybrid) * 2 * j); s_index += 2 * j; continue; } + vlctab = ff_huff_vlc[l]; /* read huffcode and compute each couple */ for (; j > 0; j--) { @@ -787,7 +788,7 @@ static int huffman_decode(MPADecodeContext *s, GranuleDef *g, if (pos >= end_pos) break; } - y = get_vlc2(&s->gb, vlc->table, 7, 3); + y = get_vlc2(&s->gb, vlctab, 7, 3); if (!y) { g->sb_hybrid[s_index ] = diff --git a/libavcodec/mpegaudiodsp.c b/libavcodec/mpegaudiodsp.c index 5a5a679d918..0971c287342 100644 --- a/libavcodec/mpegaudiodsp.c +++ b/libavcodec/mpegaudiodsp.c @@ -23,7 +23,6 @@ #include "libavutil/thread.h" #include "mpegaudio.h" #include "mpegaudiodsp.h" -#include "dct.h" #include "dct32.h" static AVOnce mpadsp_table_init = AV_ONCE_INIT; @@ -81,15 +80,12 @@ static av_cold void mpadsp_init_tabs(void) av_cold void ff_mpadsp_init(MPADSPContext *s) { - DCTContext dct; - - ff_dct_init(&dct, 5, DCT_II); ff_thread_once(&mpadsp_table_init, &mpadsp_init_tabs); s->apply_window_float = ff_mpadsp_apply_window_float; s->apply_window_fixed = ff_mpadsp_apply_window_fixed; - s->dct32_float = dct.dct32; + s->dct32_float = ff_dct32_float; s->dct32_fixed = ff_dct32_fixed; s->imdct36_blocks_float = ff_imdct36_blocks_float; diff --git a/libavcodec/mpegaudiodsp.h b/libavcodec/mpegaudiodsp.h index 7bc635191ab..5e47a263bb0 100644 --- a/libavcodec/mpegaudiodsp.h +++ b/libavcodec/mpegaudiodsp.h @@ -22,6 +22,7 @@ #include #include +#include "libavutil/attributes_internal.h" #include "libavutil/macros.h" typedef struct MPADSPContext { @@ -40,6 +41,7 @@ typedef struct MPADSPContext { int count, int switch_point, int block_type); } MPADSPContext; +FF_VISIBILITY_PUSH_HIDDEN void ff_mpadsp_init(MPADSPContext *s); extern int32_t ff_mpa_synth_window_fixed[]; @@ -88,5 +90,6 @@ void ff_imdct36_blocks_fixed(int *out, int *buf, int *in, extern int ff_mdct_win_fixed[8][MDCT_BUF_SIZE]; extern float ff_mdct_win_float[8][MDCT_BUF_SIZE]; +FF_VISIBILITY_POP_HIDDEN #endif /* AVCODEC_MPEGAUDIODSP_H */ diff --git a/libavcodec/mpegaudioenc_fixed.c b/libavcodec/mpegaudioenc_fixed.c index a523b5d5338..cdfc0b99588 100644 --- a/libavcodec/mpegaudioenc_fixed.c +++ b/libavcodec/mpegaudioenc_fixed.c @@ -37,7 +37,6 @@ const FFCodec ff_mp2fixed_encoder = { .p.supported_samplerates = (const int[]){ 44100, 48000, 32000, 22050, 24000, 16000, 0 }, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO) .p.ch_layouts = (const AVChannelLayout[]){ AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, { 0 } }, diff --git a/libavcodec/mpegaudioenc_float.c b/libavcodec/mpegaudioenc_float.c index 6a13d095738..f94ab54e89b 100644 --- a/libavcodec/mpegaudioenc_float.c +++ b/libavcodec/mpegaudioenc_float.c @@ -38,7 +38,6 @@ const FFCodec ff_mp2_encoder = { .p.supported_samplerates = (const int[]){ 44100, 48000, 32000, 22050, 24000, 16000, 0 }, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO) .p.ch_layouts = (const AVChannelLayout[]){ AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, { 0 } }, diff --git a/libavcodec/mpegpicture.c b/libavcodec/mpegpicture.c index 3204a70578c..270630bea6e 100644 --- a/libavcodec/mpegpicture.c +++ b/libavcodec/mpegpicture.c @@ -26,10 +26,10 @@ #include "libavutil/imgutils.h" #include "avcodec.h" -#include "encode.h" #include "motion_est.h" #include "mpegpicture.h" #include "mpegutils.h" +#include "refstruct.h" #include "threadframe.h" static void av_noinline free_picture_tables(Picture *pic) @@ -122,64 +122,13 @@ int ff_mpeg_framesize_alloc(AVCodecContext *avctx, MotionEstContext *me, } /** - * Allocate a frame buffer + * Check the pic's linesize and allocate linesize dependent scratch buffers */ -static int alloc_frame_buffer(AVCodecContext *avctx, Picture *pic, - MotionEstContext *me, ScratchpadContext *sc, - int chroma_x_shift, int chroma_y_shift, - int linesize, int uvlinesize) +static int handle_pic_linesizes(AVCodecContext *avctx, Picture *pic, + MotionEstContext *me, ScratchpadContext *sc, + int linesize, int uvlinesize) { - int edges_needed = av_codec_is_encoder(avctx->codec); - int r, ret; - - pic->tf.f = pic->f; - - if (edges_needed) { - pic->f->width = avctx->width + 2 * EDGE_WIDTH; - pic->f->height = avctx->height + 2 * EDGE_WIDTH; - - r = ff_encode_alloc_frame(avctx, pic->f); - } else if (avctx->codec_id != AV_CODEC_ID_WMV3IMAGE && - avctx->codec_id != AV_CODEC_ID_VC1IMAGE && - avctx->codec_id != AV_CODEC_ID_MSS2) { - r = ff_thread_get_ext_buffer(avctx, &pic->tf, - pic->reference ? AV_GET_BUFFER_FLAG_REF : 0); - } else { - pic->f->width = avctx->width; - pic->f->height = avctx->height; - pic->f->format = avctx->pix_fmt; - r = avcodec_default_get_buffer2(avctx, pic->f, 0); - } - - if (r < 0 || !pic->f->buf[0]) { - av_log(avctx, AV_LOG_ERROR, "get_buffer() failed (%d %p)\n", - r, pic->f->data[0]); - return -1; - } - - if (edges_needed) { - int i; - for (i = 0; pic->f->data[i]; i++) { - int offset = (EDGE_WIDTH >> (i ? chroma_y_shift : 0)) * - pic->f->linesize[i] + - (EDGE_WIDTH >> (i ? chroma_x_shift : 0)); - pic->f->data[i] += offset; - } - pic->f->width = avctx->width; - pic->f->height = avctx->height; - } - - if (avctx->hwaccel) { - assert(!pic->hwaccel_picture_private); - if (avctx->hwaccel->frame_priv_data_size) { - pic->hwaccel_priv_buf = av_buffer_allocz(avctx->hwaccel->frame_priv_data_size); - if (!pic->hwaccel_priv_buf) { - av_log(avctx, AV_LOG_ERROR, "alloc_frame_buffer() failed (hwaccel private data allocation)\n"); - return -1; - } - pic->hwaccel_picture_private = pic->hwaccel_priv_buf->data; - } - } + int ret; if ((linesize && linesize != pic->f->linesize[0]) || (uvlinesize && uvlinesize != pic->f->linesize[1])) { @@ -187,7 +136,7 @@ static int alloc_frame_buffer(AVCodecContext *avctx, Picture *pic, "get_buffer() failed (stride changed: linesize=%d/%d uvlinesize=%d/%d)\n", linesize, pic->f->linesize[0], uvlinesize, pic->f->linesize[1]); - ff_mpeg_unref_picture(avctx, pic); + ff_mpeg_unref_picture(pic); return -1; } @@ -195,7 +144,7 @@ static int alloc_frame_buffer(AVCodecContext *avctx, Picture *pic, pic->f->linesize[1] != pic->f->linesize[2]) { av_log(avctx, AV_LOG_ERROR, "get_buffer() failed (uv stride mismatch)\n"); - ff_mpeg_unref_picture(avctx, pic); + ff_mpeg_unref_picture(pic); return -1; } @@ -204,7 +153,7 @@ static int alloc_frame_buffer(AVCodecContext *avctx, Picture *pic, pic->f->linesize[0])) < 0) { av_log(avctx, AV_LOG_ERROR, "get_buffer() failed to allocate context scratch buffers.\n"); - ff_mpeg_unref_picture(avctx, pic); + ff_mpeg_unref_picture(pic); return ret; } @@ -252,8 +201,7 @@ static int alloc_picture_tables(AVCodecContext *avctx, Picture *pic, int encodin * The pixels are allocated/set by calling get_buffer() if shared = 0 */ int ff_alloc_picture(AVCodecContext *avctx, Picture *pic, MotionEstContext *me, - ScratchpadContext *sc, int shared, int encoding, - int chroma_x_shift, int chroma_y_shift, int out_format, + ScratchpadContext *sc, int encoding, int out_format, int mb_stride, int mb_width, int mb_height, int b8_stride, ptrdiff_t *linesize, ptrdiff_t *uvlinesize) { @@ -264,19 +212,12 @@ int ff_alloc_picture(AVCodecContext *avctx, Picture *pic, MotionEstContext *me, || pic->alloc_mb_height != mb_height) free_picture_tables(pic); - if (shared) { - av_assert0(pic->f->data[0]); - pic->shared = 1; - } else { - av_assert0(!pic->f->buf[0]); - if (alloc_frame_buffer(avctx, pic, me, sc, - chroma_x_shift, chroma_y_shift, - *linesize, *uvlinesize) < 0) - return -1; - - *linesize = pic->f->linesize[0]; - *uvlinesize = pic->f->linesize[1]; - } + if (handle_pic_linesizes(avctx, pic, me, sc, + *linesize, *uvlinesize) < 0) + return -1; + + *linesize = pic->f->linesize[0]; + *uvlinesize = pic->f->linesize[1]; if (!pic->qscale_table_buf) ret = alloc_picture_tables(avctx, pic, encoding, out_format, @@ -300,7 +241,7 @@ int ff_alloc_picture(AVCodecContext *avctx, Picture *pic, MotionEstContext *me, return 0; fail: av_log(avctx, AV_LOG_ERROR, "Error allocating a picture.\n"); - ff_mpeg_unref_picture(avctx, pic); + ff_mpeg_unref_picture(pic); free_picture_tables(pic); return AVERROR(ENOMEM); } @@ -309,24 +250,16 @@ int ff_alloc_picture(AVCodecContext *avctx, Picture *pic, MotionEstContext *me, * Deallocate a picture; frees the picture tables in case they * need to be reallocated anyway. */ -void ff_mpeg_unref_picture(AVCodecContext *avctx, Picture *pic) +void ff_mpeg_unref_picture(Picture *pic) { pic->tf.f = pic->f; - /* WM Image / Screen codecs allocate internal buffers with different - * dimensions / colorspaces; ignore user-defined callbacks for these. */ - if (avctx->codec_id != AV_CODEC_ID_WMV3IMAGE && - avctx->codec_id != AV_CODEC_ID_VC1IMAGE && - avctx->codec_id != AV_CODEC_ID_MSS2) - ff_thread_release_ext_buffer(avctx, &pic->tf); - else if (pic->f) - av_frame_unref(pic->f); + ff_thread_release_ext_buffer(&pic->tf); - av_buffer_unref(&pic->hwaccel_priv_buf); + ff_refstruct_unref(&pic->hwaccel_picture_private); if (pic->needs_realloc) free_picture_tables(pic); - pic->hwaccel_picture_private = NULL; pic->field_picture = 0; pic->b_frame_score = 0; pic->needs_realloc = 0; @@ -368,7 +301,7 @@ int ff_update_picture_tables(Picture *dst, const Picture *src) return 0; } -int ff_mpeg_ref_picture(AVCodecContext *avctx, Picture *dst, Picture *src) +int ff_mpeg_ref_picture(Picture *dst, Picture *src) { int ret; @@ -385,14 +318,8 @@ int ff_mpeg_ref_picture(AVCodecContext *avctx, Picture *dst, Picture *src) if (ret < 0) goto fail; - if (src->hwaccel_picture_private) { - dst->hwaccel_priv_buf = av_buffer_ref(src->hwaccel_priv_buf); - if (!dst->hwaccel_priv_buf) { - ret = AVERROR(ENOMEM); - goto fail; - } - dst->hwaccel_picture_private = dst->hwaccel_priv_buf->data; - } + ff_refstruct_replace(&dst->hwaccel_picture_private, + src->hwaccel_picture_private); dst->field_picture = src->field_picture; dst->b_frame_score = src->b_frame_score; @@ -404,7 +331,7 @@ int ff_mpeg_ref_picture(AVCodecContext *avctx, Picture *dst, Picture *src) return 0; fail: - ff_mpeg_unref_picture(avctx, dst); + ff_mpeg_unref_picture(dst); return ret; } @@ -456,15 +383,15 @@ int ff_find_unused_picture(AVCodecContext *avctx, Picture *picture, int shared) if (ret >= 0 && ret < MAX_PICTURE_COUNT) { if (picture[ret].needs_realloc) { - ff_mpeg_unref_picture(avctx, &picture[ret]); + ff_mpeg_unref_picture(&picture[ret]); } } return ret; } -void av_cold ff_mpv_picture_free(AVCodecContext *avctx, Picture *pic) +void av_cold ff_mpv_picture_free(Picture *pic) { free_picture_tables(pic); - ff_mpeg_unref_picture(avctx, pic); + ff_mpeg_unref_picture(pic); av_frame_free(&pic->f); } diff --git a/libavcodec/mpegpicture.h b/libavcodec/mpegpicture.h index 7919aa402ca..a457586be5c 100644 --- a/libavcodec/mpegpicture.h +++ b/libavcodec/mpegpicture.h @@ -66,8 +66,8 @@ typedef struct Picture { int alloc_mb_height; ///< mb_height used to allocate tables int alloc_mb_stride; ///< mb_stride used to allocate tables - AVBufferRef *hwaccel_priv_buf; - void *hwaccel_picture_private; ///< Hardware accelerator private data + /// RefStruct reference for hardware accelerator private data + void *hwaccel_picture_private; int field_picture; ///< whether or not the picture was encoded in separate fields @@ -82,22 +82,20 @@ typedef struct Picture { } Picture; /** - * Allocate a Picture. - * The pixels are allocated/set by calling get_buffer() if shared = 0. + * Allocate a Picture's accessories, but not the AVFrame's buffer itself. */ int ff_alloc_picture(AVCodecContext *avctx, Picture *pic, MotionEstContext *me, - ScratchpadContext *sc, int shared, int encoding, - int chroma_x_shift, int chroma_y_shift, int out_format, + ScratchpadContext *sc, int encoding, int out_format, int mb_stride, int mb_width, int mb_height, int b8_stride, ptrdiff_t *linesize, ptrdiff_t *uvlinesize); int ff_mpeg_framesize_alloc(AVCodecContext *avctx, MotionEstContext *me, ScratchpadContext *sc, int linesize); -int ff_mpeg_ref_picture(AVCodecContext *avctx, Picture *dst, Picture *src); -void ff_mpeg_unref_picture(AVCodecContext *avctx, Picture *picture); +int ff_mpeg_ref_picture(Picture *dst, Picture *src); +void ff_mpeg_unref_picture(Picture *picture); -void ff_mpv_picture_free(AVCodecContext *avctx, Picture *pic); +void ff_mpv_picture_free(Picture *pic); int ff_update_picture_tables(Picture *dst, const Picture *src); int ff_find_unused_picture(AVCodecContext *avctx, Picture *picture, int shared); diff --git a/libavcodec/mpegutils.c b/libavcodec/mpegutils.c index 2d812a25bec..d94e8f422fb 100644 --- a/libavcodec/mpegutils.c +++ b/libavcodec/mpegutils.c @@ -20,7 +20,9 @@ #include +#include "libavutil/bprint.h" #include "libavutil/common.h" +#include "libavutil/emms.h" #include "libavutil/frame.h" #include "libavutil/pixdesc.h" #include "libavutil/motion_vector.h" @@ -249,31 +251,61 @@ void ff_print_debug_info2(AVCodecContext *avctx, AVFrame *pict, if (avctx->debug & (FF_DEBUG_SKIP | FF_DEBUG_QP | FF_DEBUG_MB_TYPE)) { int x,y; + AVBPrint buf; + int n; + int margin_left; + int x_step; av_log(avctx, AV_LOG_DEBUG, "New frame, type: %c\n", av_get_picture_type_char(pict->pict_type)); + + margin_left = 2; + n = mb_width << 4; + while ((n /= 10)) + margin_left++; + + av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); + av_bprint_chars(&buf, ' ', margin_left); + + n = 0; + if (avctx->debug & FF_DEBUG_SKIP) + n++; + if (avctx->debug & FF_DEBUG_QP) + n += 2; + if (avctx->debug & FF_DEBUG_MB_TYPE) + n += 3; + x_step = (mb_width * 16 > 999) ? 8 : 4; + for (x = 0; x < mb_width; x += x_step) + av_bprintf(&buf, "%-*d", n * x_step, x << 4); + + av_log(avctx, AV_LOG_DEBUG, "%s\n", buf.str); + for (y = 0; y < mb_height; y++) { + av_bprint_clear(&buf); for (x = 0; x < mb_width; x++) { + if (x == 0) + av_bprintf(&buf, "%*d ", margin_left - 1, y << 4); if (avctx->debug & FF_DEBUG_SKIP) { int count = mbskip_table ? mbskip_table[x + y * mb_stride] : 0; if (count > 9) count = 9; - av_log(avctx, AV_LOG_DEBUG, "%1d", count); + av_bprintf(&buf, "%1d", count); } if (avctx->debug & FF_DEBUG_QP) { - av_log(avctx, AV_LOG_DEBUG, "%2d", - qscale_table[x + y * mb_stride]); + av_bprintf(&buf, "%2d", qscale_table[x + y * mb_stride]); } if (avctx->debug & FF_DEBUG_MB_TYPE) { int mb_type = mbtype_table[x + y * mb_stride]; - av_log(avctx, AV_LOG_DEBUG, "%c%c%c", + av_bprintf(&buf, "%c%c%c", get_type_mv_char(mb_type), get_segmentation_char(mb_type), get_interlacement_char(mb_type)); } } - av_log(avctx, AV_LOG_DEBUG, "\n"); + + av_log(avctx, AV_LOG_DEBUG, "%s\n", buf.str); } + av_bprint_finalize(&buf, NULL); } } diff --git a/libavcodec/mpegvideo.c b/libavcodec/mpegvideo.c index fc73abab9cd..81796e42bb7 100644 --- a/libavcodec/mpegvideo.c +++ b/libavcodec/mpegvideo.c @@ -632,7 +632,6 @@ static void clear_context(MpegEncContext *s) memset(&s->next_picture, 0, sizeof(s->next_picture)); memset(&s->last_picture, 0, sizeof(s->last_picture)); memset(&s->current_picture, 0, sizeof(s->current_picture)); - memset(&s->new_picture, 0, sizeof(s->new_picture)); memset(s->thread_context, 0, sizeof(s->thread_context)); @@ -720,8 +719,7 @@ av_cold int ff_mpv_common_init(MpegEncContext *s) if (!(s->next_picture.f = av_frame_alloc()) || !(s->last_picture.f = av_frame_alloc()) || - !(s->current_picture.f = av_frame_alloc()) || - !(s->new_picture = av_frame_alloc())) + !(s->current_picture.f = av_frame_alloc())) goto fail_nomem; if ((ret = ff_mpv_init_context_frame(s))) @@ -795,13 +793,12 @@ void ff_mpv_common_end(MpegEncContext *s) if (s->picture) { for (int i = 0; i < MAX_PICTURE_COUNT; i++) - ff_mpv_picture_free(s->avctx, &s->picture[i]); + ff_mpv_picture_free(&s->picture[i]); } av_freep(&s->picture); - ff_mpv_picture_free(s->avctx, &s->last_picture); - ff_mpv_picture_free(s->avctx, &s->current_picture); - ff_mpv_picture_free(s->avctx, &s->next_picture); - av_frame_free(&s->new_picture); + ff_mpv_picture_free(&s->last_picture); + ff_mpv_picture_free(&s->current_picture); + ff_mpv_picture_free(&s->next_picture); s->context_initialized = 0; s->context_reinit = 0; diff --git a/libavcodec/mpegvideo.h b/libavcodec/mpegvideo.h index 55828e61027..d7c2f57682f 100644 --- a/libavcodec/mpegvideo.h +++ b/libavcodec/mpegvideo.h @@ -114,7 +114,6 @@ typedef struct MpegEncContext { int input_picture_number; ///< used to set pic->display_picture_number, should not be used for/by anything else int coded_picture_number; ///< used to set pic->coded_picture_number, should not be used for/by anything else int picture_number; //FIXME remove, unclear definition - int extradata_parsed; int picture_in_gop_number; ///< 0-> first pic in gop, ... int mb_width, mb_height; ///< number of MBs horizontally & vertically int mb_stride; ///< mb_width+1 used for some arrays to allow simple addressing of left & top MBs without sig11 diff --git a/libavcodec/mpegvideo_dec.c b/libavcodec/mpegvideo_dec.c index 319f6aaf530..88facfc39dd 100644 --- a/libavcodec/mpegvideo_dec.c +++ b/libavcodec/mpegvideo_dec.c @@ -25,17 +25,20 @@ #include "config_components.h" #include "libavutil/avassert.h" +#include "libavutil/emms.h" #include "libavutil/imgutils.h" #include "libavutil/internal.h" #include "libavutil/video_enc_params.h" #include "avcodec.h" +#include "decode.h" #include "h264chroma.h" #include "internal.h" #include "mpegutils.h" #include "mpegvideo.h" #include "mpegvideodec.h" #include "mpeg4videodec.h" +#include "thread.h" #include "threadframe.h" #include "wmv2dec.h" @@ -52,6 +55,7 @@ void ff_mpv_decode_init(MpegEncContext *s, AVCodecContext *avctx) /* convert fourcc to upper case */ s->codec_tag = ff_toupper4(avctx->codec_tag); + ff_mpv_idct_init(s); ff_h264chroma_init(&s->h264chroma, 8); //for lowres } @@ -74,20 +78,16 @@ int ff_mpeg_update_thread_context(AVCodecContext *dst, int err; memcpy(s, s1, sizeof(*s)); + s->context_initialized = 0; + s->context_reinit = 0; s->avctx = dst; s->private_ctx = private_ctx; s->bitstream_buffer = NULL; s->bitstream_buffer_size = s->allocated_bitstream_buffer_size = 0; if (s1->context_initialized) { - ff_mpv_idct_init(s); - if ((err = ff_mpv_common_init(s)) < 0) { - memset(s, 0, sizeof(*s)); - s->avctx = dst; - s->private_ctx = private_ctx; - memcpy(&s->h264chroma, &s1->h264chroma, sizeof(s->h264chroma)); + if ((err = ff_mpv_common_init(s)) < 0) return err; - } } } @@ -106,17 +106,17 @@ int ff_mpeg_update_thread_context(AVCodecContext *dst, av_assert0(!s->picture || s->picture != s1->picture); if (s->picture) for (int i = 0; i < MAX_PICTURE_COUNT; i++) { - ff_mpeg_unref_picture(s->avctx, &s->picture[i]); + ff_mpeg_unref_picture(&s->picture[i]); if (s1->picture && s1->picture[i].f->buf[0] && - (ret = ff_mpeg_ref_picture(s->avctx, &s->picture[i], &s1->picture[i])) < 0) + (ret = ff_mpeg_ref_picture(&s->picture[i], &s1->picture[i])) < 0) return ret; } #define UPDATE_PICTURE(pic)\ do {\ - ff_mpeg_unref_picture(s->avctx, &s->pic);\ + ff_mpeg_unref_picture(&s->pic);\ if (s1->pic.f && s1->pic.f->buf[0])\ - ret = ff_mpeg_ref_picture(s->avctx, &s->pic, &s1->pic);\ + ret = ff_mpeg_ref_picture(&s->pic, &s1->pic);\ else\ ret = ff_update_picture_tables(&s->pic, &s1->pic);\ if (ret < 0)\ @@ -154,21 +154,16 @@ do {\ s->divx_packed = s1->divx_packed; if (s1->bitstream_buffer) { - if (s1->bitstream_buffer_size + - AV_INPUT_BUFFER_PADDING_SIZE > s->allocated_bitstream_buffer_size) { - av_fast_malloc(&s->bitstream_buffer, - &s->allocated_bitstream_buffer_size, - s1->allocated_bitstream_buffer_size); - if (!s->bitstream_buffer) { - s->bitstream_buffer_size = 0; - return AVERROR(ENOMEM); - } + av_fast_padded_malloc(&s->bitstream_buffer, + &s->allocated_bitstream_buffer_size, + s1->bitstream_buffer_size); + if (!s->bitstream_buffer) { + s->bitstream_buffer_size = 0; + return AVERROR(ENOMEM); } s->bitstream_buffer_size = s1->bitstream_buffer_size; memcpy(s->bitstream_buffer, s1->bitstream_buffer, s1->bitstream_buffer_size); - memset(s->bitstream_buffer + s->bitstream_buffer_size, 0, - AV_INPUT_BUFFER_PADDING_SIZE); } // linesize-dependent scratch buffer allocation @@ -242,20 +237,49 @@ int ff_mpv_common_frame_size_change(MpegEncContext *s) static int alloc_picture(MpegEncContext *s, Picture *pic) { - return ff_alloc_picture(s->avctx, pic, &s->me, &s->sc, 0, 0, - s->chroma_x_shift, s->chroma_y_shift, s->out_format, + AVCodecContext *avctx = s->avctx; + int ret; + + pic->tf.f = pic->f; + + /* WM Image / Screen codecs allocate internal buffers with different + * dimensions / colorspaces; ignore user-defined callbacks for these. */ + if (avctx->codec_id != AV_CODEC_ID_WMV3IMAGE && + avctx->codec_id != AV_CODEC_ID_VC1IMAGE && + avctx->codec_id != AV_CODEC_ID_MSS2) { + ret = ff_thread_get_ext_buffer(avctx, &pic->tf, + pic->reference ? AV_GET_BUFFER_FLAG_REF : 0); + } else { + pic->f->width = avctx->width; + pic->f->height = avctx->height; + pic->f->format = avctx->pix_fmt; + ret = avcodec_default_get_buffer2(avctx, pic->f, 0); + } + if (ret < 0) + goto fail; + + ret = ff_hwaccel_frame_priv_alloc(avctx, &pic->hwaccel_picture_private); + if (ret < 0) + goto fail; + + return ff_alloc_picture(s->avctx, pic, &s->me, &s->sc, 0, s->out_format, s->mb_stride, s->mb_width, s->mb_height, s->b8_stride, &s->linesize, &s->uvlinesize); +fail: + ff_mpeg_unref_picture(pic); + return ret; } -static void gray_frame(AVFrame *frame) +static void color_frame(AVFrame *frame, int luma) { int h_chroma_shift, v_chroma_shift; - av_pix_fmt_get_chroma_sub_sample(frame->format, &h_chroma_shift, &v_chroma_shift); - for (int i = 0; i < frame->height; i++) - memset(frame->data[0] + frame->linesize[0] * i, 0x80, frame->width); + memset(frame->data[0] + frame->linesize[0] * i, luma, frame->width); + + if (!frame->data[1]) + return; + av_pix_fmt_get_chroma_sub_sample(frame->format, &h_chroma_shift, &v_chroma_shift); for (int i = 0; i < AV_CEIL_RSHIFT(frame->height, v_chroma_shift); i++) { memset(frame->data[1] + frame->linesize[1] * i, 0x80, AV_CEIL_RSHIFT(frame->width, h_chroma_shift)); @@ -284,7 +308,7 @@ int ff_mpv_frame_start(MpegEncContext *s, AVCodecContext *avctx) if (s->pict_type != AV_PICTURE_TYPE_B && s->last_picture_ptr && s->last_picture_ptr != s->next_picture_ptr && s->last_picture_ptr->f->buf[0]) { - ff_mpeg_unref_picture(s->avctx, s->last_picture_ptr); + ff_mpeg_unref_picture(s->last_picture_ptr); } /* release non reference/forgotten frames */ @@ -293,13 +317,13 @@ int ff_mpv_frame_start(MpegEncContext *s, AVCodecContext *avctx) (&s->picture[i] != s->last_picture_ptr && &s->picture[i] != s->next_picture_ptr && !s->picture[i].needs_realloc)) { - ff_mpeg_unref_picture(s->avctx, &s->picture[i]); + ff_mpeg_unref_picture(&s->picture[i]); } } - ff_mpeg_unref_picture(s->avctx, &s->current_picture); - ff_mpeg_unref_picture(s->avctx, &s->last_picture); - ff_mpeg_unref_picture(s->avctx, &s->next_picture); + ff_mpeg_unref_picture(&s->current_picture); + ff_mpeg_unref_picture(&s->last_picture); + ff_mpeg_unref_picture(&s->next_picture); if (s->current_picture_ptr && !s->current_picture_ptr->f->buf[0]) { // we already have an unused image @@ -320,32 +344,29 @@ int ff_mpv_frame_start(MpegEncContext *s, AVCodecContext *avctx) pic->reference = 3; } -#if FF_API_FRAME_PICTURE_NUMBER -FF_DISABLE_DEPRECATION_WARNINGS - pic->f->coded_picture_number = s->coded_picture_number++; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (alloc_picture(s, pic) < 0) return -1; s->current_picture_ptr = pic; // FIXME use only the vars from current_pic - s->current_picture_ptr->f->top_field_first = s->top_field_first; + s->current_picture_ptr->f->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * !!s->top_field_first; if (s->codec_id == AV_CODEC_ID_MPEG1VIDEO || s->codec_id == AV_CODEC_ID_MPEG2VIDEO) { if (s->picture_structure != PICT_FRAME) - s->current_picture_ptr->f->top_field_first = - (s->picture_structure == PICT_TOP_FIELD) == s->first_field; + s->current_picture_ptr->f->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * + ((s->picture_structure == PICT_TOP_FIELD) == s->first_field); } - s->current_picture_ptr->f->interlaced_frame = !s->progressive_frame && - !s->progressive_sequence; + s->current_picture_ptr->f->flags |= AV_FRAME_FLAG_INTERLACED * (!s->progressive_frame && + !s->progressive_sequence); s->current_picture_ptr->field_picture = s->picture_structure != PICT_FRAME; s->current_picture_ptr->f->pict_type = s->pict_type; - s->current_picture_ptr->f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; + if (s->pict_type == AV_PICTURE_TYPE_I) + s->current_picture_ptr->f->flags |= AV_FRAME_FLAG_KEY; + else + s->current_picture_ptr->f->flags &= ~AV_FRAME_FLAG_KEY; - if ((ret = ff_mpeg_ref_picture(s->avctx, &s->current_picture, + if ((ret = ff_mpeg_ref_picture(&s->current_picture, s->current_picture_ptr)) < 0) return ret; @@ -363,9 +384,6 @@ FF_ENABLE_DEPRECATION_WARNINGS if ((!s->last_picture_ptr || !s->last_picture_ptr->f->buf[0]) && (s->pict_type != AV_PICTURE_TYPE_I)) { - int h_chroma_shift, v_chroma_shift; - av_pix_fmt_get_chroma_sub_sample(s->avctx->pix_fmt, - &h_chroma_shift, &v_chroma_shift); if (s->pict_type == AV_PICTURE_TYPE_B && s->next_picture_ptr && s->next_picture_ptr->f->buf[0]) av_log(avctx, AV_LOG_DEBUG, "allocating dummy last picture for B frame\n"); @@ -382,7 +400,7 @@ FF_ENABLE_DEPRECATION_WARNINGS s->last_picture_ptr = &s->picture[idx]; s->last_picture_ptr->reference = 3; - s->last_picture_ptr->f->key_frame = 0; + s->last_picture_ptr->f->flags &= ~AV_FRAME_FLAG_KEY; s->last_picture_ptr->f->pict_type = AV_PICTURE_TYPE_P; if (alloc_picture(s, s->last_picture_ptr) < 0) { @@ -391,23 +409,8 @@ FF_ENABLE_DEPRECATION_WARNINGS } if (!avctx->hwaccel) { - for (int i = 0; i < avctx->height; i++) - memset(s->last_picture_ptr->f->data[0] + s->last_picture_ptr->f->linesize[0]*i, - 0x80, avctx->width); - if (s->last_picture_ptr->f->data[2]) { - for (int i = 0; i < AV_CEIL_RSHIFT(avctx->height, v_chroma_shift); i++) { - memset(s->last_picture_ptr->f->data[1] + s->last_picture_ptr->f->linesize[1]*i, - 0x80, AV_CEIL_RSHIFT(avctx->width, h_chroma_shift)); - memset(s->last_picture_ptr->f->data[2] + s->last_picture_ptr->f->linesize[2]*i, - 0x80, AV_CEIL_RSHIFT(avctx->width, h_chroma_shift)); - } - } - - if (s->codec_id == AV_CODEC_ID_FLV1 || s->codec_id == AV_CODEC_ID_H263) { - for (int i = 0; i < avctx->height; i++) - memset(s->last_picture_ptr->f->data[0] + s->last_picture_ptr->f->linesize[0] * i, - 16, avctx->width); - } + int luma_val = s->codec_id == AV_CODEC_ID_FLV1 || s->codec_id == AV_CODEC_ID_H263 ? 16 : 0x80; + color_frame(s->last_picture_ptr->f, luma_val); } ff_thread_report_progress(&s->last_picture_ptr->tf, INT_MAX, 0); @@ -424,7 +427,7 @@ FF_ENABLE_DEPRECATION_WARNINGS s->next_picture_ptr = &s->picture[idx]; s->next_picture_ptr->reference = 3; - s->next_picture_ptr->f->key_frame = 0; + s->next_picture_ptr->f->flags &= ~AV_FRAME_FLAG_KEY; s->next_picture_ptr->f->pict_type = AV_PICTURE_TYPE_P; if (alloc_picture(s, s->next_picture_ptr) < 0) { @@ -435,19 +438,15 @@ FF_ENABLE_DEPRECATION_WARNINGS ff_thread_report_progress(&s->next_picture_ptr->tf, INT_MAX, 1); } -#if 0 // BUFREF-FIXME - memset(s->last_picture.f->data, 0, sizeof(s->last_picture.f->data)); - memset(s->next_picture.f->data, 0, sizeof(s->next_picture.f->data)); -#endif if (s->last_picture_ptr) { if (s->last_picture_ptr->f->buf[0] && - (ret = ff_mpeg_ref_picture(s->avctx, &s->last_picture, + (ret = ff_mpeg_ref_picture(&s->last_picture, s->last_picture_ptr)) < 0) return ret; } if (s->next_picture_ptr) { if (s->next_picture_ptr->f->buf[0] && - (ret = ff_mpeg_ref_picture(s->avctx, &s->next_picture, + (ret = ff_mpeg_ref_picture(&s->next_picture, s->next_picture_ptr)) < 0) return ret; } @@ -482,7 +481,7 @@ FF_ENABLE_DEPRECATION_WARNINGS } if (s->avctx->debug & FF_DEBUG_NOMC) - gray_frame(s->current_picture_ptr->f); + color_frame(s->current_picture_ptr->f, 0x80); return 0; } @@ -549,12 +548,12 @@ void ff_mpeg_flush(AVCodecContext *avctx) return; for (int i = 0; i < MAX_PICTURE_COUNT; i++) - ff_mpeg_unref_picture(s->avctx, &s->picture[i]); + ff_mpeg_unref_picture(&s->picture[i]); s->current_picture_ptr = s->last_picture_ptr = s->next_picture_ptr = NULL; - ff_mpeg_unref_picture(s->avctx, &s->current_picture); - ff_mpeg_unref_picture(s->avctx, &s->last_picture); - ff_mpeg_unref_picture(s->avctx, &s->next_picture); + ff_mpeg_unref_picture(&s->current_picture); + ff_mpeg_unref_picture(&s->last_picture); + ff_mpeg_unref_picture(&s->next_picture); s->mb_x = s->mb_y = 0; @@ -882,10 +881,9 @@ static inline void MPV_motion_lowres(MpegEncContext *s, s->mv[dir][1][0], s->mv[dir][1][1], block_s, mb_y); } else { - if (s->picture_structure != s->field_select[dir][0] + 1 && - s->pict_type != AV_PICTURE_TYPE_B && !s->first_field) { + if ( s->picture_structure != s->field_select[dir][0] + 1 && s->pict_type != AV_PICTURE_TYPE_B && !s->first_field + || !ref_picture[0]) { ref_picture = s->current_picture_ptr->f->data; - } mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr, 0, 0, s->field_select[dir][0], @@ -898,8 +896,9 @@ static inline void MPV_motion_lowres(MpegEncContext *s, for (int i = 0; i < 2; i++) { uint8_t *const *ref2picture; - if (s->picture_structure == s->field_select[dir][i] + 1 || - s->pict_type == AV_PICTURE_TYPE_B || s->first_field) { + if ((s->picture_structure == s->field_select[dir][i] + 1 || + s->pict_type == AV_PICTURE_TYPE_B || s->first_field) && + ref_picture[0]) { ref2picture = ref_picture; } else { ref2picture = s->current_picture_ptr->f->data; @@ -930,6 +929,9 @@ static inline void MPV_motion_lowres(MpegEncContext *s, pix_op = s->h264chroma.avg_h264_chroma_pixels_tab; } } else { + if (!ref_picture[0]) { + ref_picture = s->current_picture_ptr->f->data; + } for (int i = 0; i < 2; i++) { mpeg_motion_lowres(s, dest_y, dest_cb, dest_cr, 0, 0, s->picture_structure != i + 1, diff --git a/libavcodec/mpegvideo_enc.c b/libavcodec/mpegvideo_enc.c index bcd2f9ef1d6..5fab302148e 100644 --- a/libavcodec/mpegvideo_enc.c +++ b/libavcodec/mpegvideo_enc.c @@ -35,15 +35,14 @@ #include +#include "libavutil/emms.h" #include "libavutil/internal.h" #include "libavutil/intmath.h" #include "libavutil/mathematics.h" #include "libavutil/mem_internal.h" -#include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "libavutil/thread.h" #include "avcodec.h" -#include "dct.h" #include "encode.h" #include "idctdsp.h" #include "mpeg12codecs.h" @@ -798,6 +797,11 @@ av_cold int ff_mpv_encode_init(AVCodecContext *avctx) AV_CODEC_FLAG_INTERLACED_ME) || s->alternate_scan); + if (s->lmin > s->lmax) { + av_log(avctx, AV_LOG_WARNING, "Clipping lmin value to %d\n", s->lmax); + s->lmin = s->lmax; + } + /* init */ ff_mpv_idct_init(s); if ((ret = ff_mpv_common_init(s)) < 0) @@ -815,8 +819,9 @@ av_cold int ff_mpv_encode_init(AVCodecContext *avctx) !FF_ALLOCZ_TYPED_ARRAY(s->q_intra_matrix16, 32) || !FF_ALLOCZ_TYPED_ARRAY(s->q_chroma_intra_matrix16, 32) || !FF_ALLOCZ_TYPED_ARRAY(s->q_inter_matrix16, 32) || - !FF_ALLOCZ_TYPED_ARRAY(s->input_picture, MAX_PICTURE_COUNT) || - !FF_ALLOCZ_TYPED_ARRAY(s->reordered_input_picture, MAX_PICTURE_COUNT)) + !FF_ALLOCZ_TYPED_ARRAY(s->input_picture, MAX_B_FRAMES + 1) || + !FF_ALLOCZ_TYPED_ARRAY(s->reordered_input_picture, MAX_B_FRAMES + 1) || + !(s->new_picture = av_frame_alloc())) return AVERROR(ENOMEM); /* Allocate MV tables; the MV and MB tables will be copied @@ -968,7 +973,7 @@ av_cold int ff_mpv_encode_init(AVCodecContext *avctx) } } - cpb_props = ff_add_cpb_side_data(avctx); + cpb_props = ff_encode_add_cpb_side_data(avctx); if (!cpb_props) return AVERROR(ENOMEM); cpb_props->max_bitrate = avctx->rc_max_rate; @@ -1086,10 +1091,28 @@ static int get_intra_count(MpegEncContext *s, const uint8_t *src, return acc; } -static int alloc_picture(MpegEncContext *s, Picture *pic, int shared) +static int alloc_picture(MpegEncContext *s, Picture *pic) { - return ff_alloc_picture(s->avctx, pic, &s->me, &s->sc, shared, 1, - s->chroma_x_shift, s->chroma_y_shift, s->out_format, + AVCodecContext *avctx = s->avctx; + int ret; + + pic->f->width = avctx->width + 2 * EDGE_WIDTH; + pic->f->height = avctx->height + 2 * EDGE_WIDTH; + + ret = ff_encode_alloc_frame(avctx, pic->f); + if (ret < 0) + return ret; + + for (int i = 0; pic->f->data[i]; i++) { + int offset = (EDGE_WIDTH >> (i ? s->chroma_y_shift : 0)) * + pic->f->linesize[i] + + (EDGE_WIDTH >> (i ? s->chroma_x_shift : 0)); + pic->f->data[i] += offset; + } + pic->f->width = avctx->width; + pic->f->height = avctx->height; + + return ff_alloc_picture(s->avctx, pic, &s->me, &s->sc, 1, s->out_format, s->mb_stride, s->mb_width, s->mb_height, s->b8_stride, &s->linesize, &s->uvlinesize); } @@ -1135,8 +1158,7 @@ static int load_input_picture(MpegEncContext *s, const AVFrame *pic_arg) } } - if (!pic_arg->buf[0] || - pic_arg->linesize[0] != s->linesize || + if (pic_arg->linesize[0] != s->linesize || pic_arg->linesize[1] != s->uvlinesize || pic_arg->linesize[2] != s->uvlinesize) direct = 0; @@ -1160,66 +1182,57 @@ static int load_input_picture(MpegEncContext *s, const AVFrame *pic_arg) if (direct) { if ((ret = av_frame_ref(pic->f, pic_arg)) < 0) return ret; - } - ret = alloc_picture(s, pic, direct); - if (ret < 0) - return ret; + pic->shared = 1; + } else { + ret = alloc_picture(s, pic); + if (ret < 0) + return ret; + ret = av_frame_copy_props(pic->f, pic_arg); + if (ret < 0) { + ff_mpeg_unref_picture(pic); + return ret; + } - if (!direct) { - if (pic->f->data[0] + INPLACE_OFFSET == pic_arg->data[0] && - pic->f->data[1] + INPLACE_OFFSET == pic_arg->data[1] && - pic->f->data[2] + INPLACE_OFFSET == pic_arg->data[2]) { - // empty - } else { - int h_chroma_shift, v_chroma_shift; - av_pix_fmt_get_chroma_sub_sample(s->avctx->pix_fmt, - &h_chroma_shift, - &v_chroma_shift); - - for (i = 0; i < 3; i++) { - int src_stride = pic_arg->linesize[i]; - int dst_stride = i ? s->uvlinesize : s->linesize; - int h_shift = i ? h_chroma_shift : 0; - int v_shift = i ? v_chroma_shift : 0; - int w = s->width >> h_shift; - int h = s->height >> v_shift; - const uint8_t *src = pic_arg->data[i]; - uint8_t *dst = pic->f->data[i]; - int vpad = 16; - - if ( s->codec_id == AV_CODEC_ID_MPEG2VIDEO - && !s->progressive_sequence - && FFALIGN(s->height, 32) - s->height > 16) - vpad = 32; - - if (!s->avctx->rc_buffer_size) - dst += INPLACE_OFFSET; - - if (src_stride == dst_stride) - memcpy(dst, src, src_stride * h); - else { - int h2 = h; - uint8_t *dst2 = dst; - while (h2--) { - memcpy(dst2, src, w); - dst2 += dst_stride; - src += src_stride; - } - } - if ((s->width & 15) || (s->height & (vpad-1))) { - s->mpvencdsp.draw_edges(dst, dst_stride, - w, h, - 16 >> h_shift, - vpad >> v_shift, - EDGE_BOTTOM); + for (int i = 0; i < 3; i++) { + ptrdiff_t src_stride = pic_arg->linesize[i]; + ptrdiff_t dst_stride = i ? s->uvlinesize : s->linesize; + int h_shift = i ? s->chroma_x_shift : 0; + int v_shift = i ? s->chroma_y_shift : 0; + int w = AV_CEIL_RSHIFT(s->width , h_shift); + int h = AV_CEIL_RSHIFT(s->height, v_shift); + const uint8_t *src = pic_arg->data[i]; + uint8_t *dst = pic->f->data[i]; + int vpad = 16; + + if ( s->codec_id == AV_CODEC_ID_MPEG2VIDEO + && !s->progressive_sequence + && FFALIGN(s->height, 32) - s->height > 16) + vpad = 32; + + if (!s->avctx->rc_buffer_size) + dst += INPLACE_OFFSET; + + if (src_stride == dst_stride) + memcpy(dst, src, src_stride * h - src_stride + w); + else { + int h2 = h; + uint8_t *dst2 = dst; + while (h2--) { + memcpy(dst2, src, w); + dst2 += dst_stride; + src += src_stride; } } - emms_c(); + if ((s->width & 15) || (s->height & (vpad-1))) { + s->mpvencdsp.draw_edges(dst, dst_stride, + w, h, + 16 >> h_shift, + vpad >> v_shift, + EDGE_BOTTOM); + } } + emms_c(); } - ret = av_frame_copy_props(pic->f, pic_arg); - if (ret < 0) - return ret; pic->display_picture_number = display_picture_number; pic->f->pts = pts; // we set this here to avoid modifying pic_arg @@ -1237,8 +1250,10 @@ static int load_input_picture(MpegEncContext *s, const AVFrame *pic_arg) } /* shift buffer entries */ - for (i = flush_offset; i < MAX_PICTURE_COUNT /*s->encoding_delay + 1*/; i++) + for (int i = flush_offset; i <= MAX_B_FRAMES; i++) s->input_picture[i - flush_offset] = s->input_picture[i]; + for (int i = MAX_B_FRAMES + 1 - flush_offset; i <= MAX_B_FRAMES; i++) + s->input_picture[i] = NULL; s->input_picture[encoding_delay] = pic; @@ -1420,7 +1435,7 @@ static int estimate_best_b_count(MpegEncContext *s) goto fail; } - rd += (out_size * lambda2) >> (FF_LAMBDA_SHIFT - 3); + rd += (out_size * (uint64_t)lambda2) >> (FF_LAMBDA_SHIFT - 3); } /* get the delayed frames */ @@ -1429,7 +1444,7 @@ static int estimate_best_b_count(MpegEncContext *s) ret = out_size; goto fail; } - rd += (out_size * lambda2) >> (FF_LAMBDA_SHIFT - 3); + rd += (out_size * (uint64_t)lambda2) >> (FF_LAMBDA_SHIFT - 3); rd += c->error[0] + c->error[1] + c->error[2]; @@ -1456,9 +1471,9 @@ static int select_input_picture(MpegEncContext *s) { int i, ret; - for (i = 1; i < MAX_PICTURE_COUNT; i++) + for (int i = 1; i <= MAX_B_FRAMES; i++) s->reordered_input_picture[i - 1] = s->reordered_input_picture[i]; - s->reordered_input_picture[MAX_PICTURE_COUNT - 1] = NULL; + s->reordered_input_picture[MAX_B_FRAMES] = NULL; /* set next picture type & ordering */ if (!s->reordered_input_picture[0] && s->input_picture[0]) { @@ -1530,8 +1545,10 @@ static int select_input_picture(MpegEncContext *s) } } else if (s->b_frame_strategy == 2) { b_frames = estimate_best_b_count(s); - if (b_frames < 0) + if (b_frames < 0) { + ff_mpeg_unref_picture(s->input_picture[0]); return b_frames; + } } emms_c(); @@ -1586,7 +1603,7 @@ static int select_input_picture(MpegEncContext *s) if ((ret = av_frame_ref(s->new_picture, s->reordered_input_picture[0]->f))) - return ret; + goto fail; if (s->reordered_input_picture[0]->shared || s->avctx->rc_buffer_size) { // input is a shared pix, so we can't modify it -> allocate a new @@ -1599,13 +1616,15 @@ static int select_input_picture(MpegEncContext *s) pic = &s->picture[i]; pic->reference = s->reordered_input_picture[0]->reference; - if (alloc_picture(s, pic, 0) < 0) { - return -1; - } + ret = alloc_picture(s, pic); + if (ret < 0) + goto fail; ret = av_frame_copy_props(pic->f, s->reordered_input_picture[0]->f); - if (ret < 0) - return ret; + if (ret < 0) { + ff_mpeg_unref_picture(pic); + goto fail; + } pic->coded_picture_number = s->reordered_input_picture[0]->coded_picture_number; pic->display_picture_number = s->reordered_input_picture[0]->display_picture_number; @@ -1626,6 +1645,9 @@ static int select_input_picture(MpegEncContext *s) } return 0; +fail: + ff_mpeg_unref_picture(s->reordered_input_picture[0]); + return ret; } static void frame_end(MpegEncContext *s) @@ -1633,9 +1655,8 @@ static void frame_end(MpegEncContext *s) if (s->unrestricted_mv && s->current_picture.reference && !s->intra_only) { - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(s->avctx->pix_fmt); - int hshift = desc->log2_chroma_w; - int vshift = desc->log2_chroma_h; + int hshift = s->chroma_x_shift; + int vshift = s->chroma_y_shift; s->mpvencdsp.draw_edges(s->current_picture.f->data[0], s->current_picture.f->linesize[0], s->h_edge_pos, s->v_edge_pos, @@ -1694,14 +1715,13 @@ static int frame_start(MpegEncContext *s) if (s->pict_type != AV_PICTURE_TYPE_B && s->last_picture_ptr && s->last_picture_ptr != s->next_picture_ptr && s->last_picture_ptr->f->buf[0]) { - ff_mpeg_unref_picture(s->avctx, s->last_picture_ptr); + ff_mpeg_unref_picture(s->last_picture_ptr); } s->current_picture_ptr->f->pict_type = s->pict_type; - s->current_picture_ptr->f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; - ff_mpeg_unref_picture(s->avctx, &s->current_picture); - if ((ret = ff_mpeg_ref_picture(s->avctx, &s->current_picture, + ff_mpeg_unref_picture(&s->current_picture); + if ((ret = ff_mpeg_ref_picture(&s->current_picture, s->current_picture_ptr)) < 0) return ret; @@ -1711,33 +1731,20 @@ static int frame_start(MpegEncContext *s) } if (s->last_picture_ptr) { - ff_mpeg_unref_picture(s->avctx, &s->last_picture); + ff_mpeg_unref_picture(&s->last_picture); if (s->last_picture_ptr->f->buf[0] && - (ret = ff_mpeg_ref_picture(s->avctx, &s->last_picture, + (ret = ff_mpeg_ref_picture(&s->last_picture, s->last_picture_ptr)) < 0) return ret; } if (s->next_picture_ptr) { - ff_mpeg_unref_picture(s->avctx, &s->next_picture); + ff_mpeg_unref_picture(&s->next_picture); if (s->next_picture_ptr->f->buf[0] && - (ret = ff_mpeg_ref_picture(s->avctx, &s->next_picture, + (ret = ff_mpeg_ref_picture(&s->next_picture, s->next_picture_ptr)) < 0) return ret; } - if (s->picture_structure!= PICT_FRAME) { - int i; - for (i = 0; i < 4; i++) { - if (s->picture_structure == PICT_BOTTOM_FIELD) { - s->current_picture.f->data[i] += - s->current_picture.f->linesize[i]; - } - s->current_picture.f->linesize[i] *= 2; - s->last_picture.f->linesize[i] *= 2; - s->next_picture.f->linesize[i] *= 2; - } - } - if (s->dct_error_sum) { av_assert2(s->noise_reduction && s->encoding); update_noise_reduction(s); @@ -1974,7 +1981,7 @@ int ff_mpv_encode_picture(AVCodecContext *avctx, AVPacket *pkt, return ret; } - if (s->current_picture.f->key_frame) + if (s->current_picture.f->flags & AV_FRAME_FLAG_KEY) pkt->flags |= AV_PKT_FLAG_KEY; if (s->mb_info) av_packet_shrink_side_data(pkt, AV_PKT_DATA_H263_MB_INFO, s->mb_info_size); @@ -1985,7 +1992,7 @@ int ff_mpv_encode_picture(AVCodecContext *avctx, AVPacket *pkt, /* release non-reference frames */ for (i = 0; i < MAX_PICTURE_COUNT; i++) { if (!s->picture[i].reference) - ff_mpeg_unref_picture(avctx, &s->picture[i]); + ff_mpeg_unref_picture(&s->picture[i]); } av_assert1((s->frame_bits & 7) == 0); @@ -3778,12 +3785,17 @@ static int encode_picture(MpegEncContext *s) } //FIXME var duplication - s->current_picture_ptr->f->key_frame = - s->current_picture.f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; //FIXME pic_ptr + if (s->pict_type == AV_PICTURE_TYPE_I) { + s->current_picture_ptr->f->flags |= AV_FRAME_FLAG_KEY; //FIXME pic_ptr + s->current_picture.f->flags |= AV_FRAME_FLAG_KEY; + } else { + s->current_picture_ptr->f->flags &= ~AV_FRAME_FLAG_KEY; //FIXME pic_ptr + s->current_picture.f->flags &= ~AV_FRAME_FLAG_KEY; + } s->current_picture_ptr->f->pict_type = s->current_picture.f->pict_type = s->pict_type; - if (s->current_picture.f->key_frame) + if (s->current_picture.f->flags & AV_FRAME_FLAG_KEY) s->picture_in_gop_number=0; s->mb_x = s->mb_y = 0; diff --git a/libavcodec/mpegvideo_parser.c b/libavcodec/mpegvideo_parser.c index 8e7e88ff25e..2cd0348317f 100644 --- a/libavcodec/mpegvideo_parser.c +++ b/libavcodec/mpegvideo_parser.c @@ -105,22 +105,23 @@ static void mpegvideo_extract_headers(AVCodecParserContext *s, { struct MpvParseContext *pc = s->priv_data; const uint8_t *buf_end = buf + buf_size; - uint32_t start_code; - int frame_rate_index, ext_type, bytes_left; - int frame_rate_ext_n, frame_rate_ext_d; - int top_field_first, repeat_first_field, progressive_frame; - int horiz_size_ext, vert_size_ext, bit_rate_ext; + int bytes_left; int did_set_size=0; int set_dim_ret = 0; int bit_rate = 0; int vbv_delay = 0; - int chroma_format; enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE; -//FIXME replace the crap with get_bits() - s->repeat_pict = 0; + // number of picture coding extensions (i.e. MPEG2 pictures) + // in this packet - should be 1 or 2 + int nb_pic_ext = 0; + // when there are two pictures in the packet this indicates + // which field is in the first of them + int first_field = AV_FIELD_UNKNOWN; + +//FIXME replace the crap with get_bits() while (buf < buf_end) { - start_code= -1; + uint32_t start_code = -1; buf= avpriv_find_start_code(buf, buf_end, &start_code); bytes_left = buf_end - buf; switch(start_code) { @@ -133,6 +134,8 @@ static void mpegvideo_extract_headers(AVCodecParserContext *s, break; case SEQ_START_CODE: if (bytes_left >= 7) { + int frame_rate_index; + pc->width = (buf[0] << 4) | (buf[1] >> 4); pc->height = ((buf[1] & 0x0f) << 8) | buf[2]; if(!avctx->width || !avctx->height || !avctx->coded_width || !avctx->coded_height){ @@ -144,25 +147,27 @@ static void mpegvideo_extract_headers(AVCodecParserContext *s, pc->frame_rate = avctx->framerate = ff_mpeg12_frame_rate_tab[frame_rate_index]; bit_rate = (buf[4]<<10) | (buf[5]<<2) | (buf[6]>>6); avctx->codec_id = AV_CODEC_ID_MPEG1VIDEO; +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS avctx->ticks_per_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } break; case EXT_START_CODE: if (bytes_left >= 1) { - ext_type = (buf[0] >> 4); - switch(ext_type) { + switch (buf[0] >> 4) { // ext_type case 0x1: /* sequence extension */ if (bytes_left >= 6) { - horiz_size_ext = ((buf[1] & 1) << 1) | (buf[2] >> 7); - vert_size_ext = (buf[2] >> 5) & 3; - bit_rate_ext = ((buf[2] & 0x1F)<<7) | (buf[3]>>1); - frame_rate_ext_n = (buf[5] >> 5) & 3; - frame_rate_ext_d = (buf[5] & 0x1f); + int horiz_size_ext = ((buf[1] & 1) << 1) | (buf[2] >> 7); + int vert_size_ext = (buf[2] >> 5) & 3; + int bit_rate_ext = ((buf[2] & 0x1F)<<7) | (buf[3]>>1); + int frame_rate_ext_n = (buf[5] >> 5) & 3; + int frame_rate_ext_d = (buf[5] & 0x1f); pc->progressive_sequence = buf[1] & (1 << 3); avctx->has_b_frames= !(buf[5] >> 7); - chroma_format = (buf[1] >> 1) & 3; - switch (chroma_format) { + switch ((buf[1] >> 1) & 3) { // chroma_format case 1: pix_fmt = AV_PIX_FMT_YUV420P; break; case 2: pix_fmt = AV_PIX_FMT_YUV422P; break; case 3: pix_fmt = AV_PIX_FMT_YUV444P; break; @@ -176,14 +181,18 @@ static void mpegvideo_extract_headers(AVCodecParserContext *s, avctx->framerate.num = pc->frame_rate.num * (frame_rate_ext_n + 1); avctx->framerate.den = pc->frame_rate.den * (frame_rate_ext_d + 1); avctx->codec_id = AV_CODEC_ID_MPEG2VIDEO; +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS avctx->ticks_per_frame = 2; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } break; case 0x8: /* picture coding extension */ if (bytes_left >= 5) { - top_field_first = buf[3] & (1 << 7); - repeat_first_field = buf[3] & (1 << 1); - progressive_frame = buf[4] & (1 << 7); + int top_field_first = buf[3] & (1 << 7); + int repeat_first_field = buf[3] & (1 << 1); + int progressive_frame = buf[4] & (1 << 7); /* check if we must repeat the frame */ s->repeat_pict = 1; @@ -205,6 +214,19 @@ static void mpegvideo_extract_headers(AVCodecParserContext *s, s->field_order = AV_FIELD_BB; } else s->field_order = AV_FIELD_PROGRESSIVE; + + s->picture_structure = buf[2] & 3; + + if (!nb_pic_ext) { + // remember parity of the first field for the case + // when there are 2 fields in packet + switch (s->picture_structure) { + case AV_PICTURE_STRUCTURE_BOTTOM_FIELD: first_field = AV_FIELD_BB; break; + case AV_PICTURE_STRUCTURE_TOP_FIELD: first_field = AV_FIELD_TT; break; + } + } + + nb_pic_ext++; } break; } @@ -225,7 +247,7 @@ static void mpegvideo_extract_headers(AVCodecParserContext *s, if (set_dim_ret < 0) av_log(avctx, AV_LOG_ERROR, "Failed to set dimensions\n"); - if (avctx->codec_id == AV_CODEC_ID_MPEG2VIDEO && bit_rate) { + if (avctx->codec_id == AV_CODEC_ID_MPEG2VIDEO && bit_rate && bit_rate != 0x3FFFF) { avctx->rc_max_rate = 400LL*bit_rate; } if (bit_rate && @@ -240,6 +262,12 @@ static void mpegvideo_extract_headers(AVCodecParserContext *s, s->coded_width = FFALIGN(pc->width, 16); s->coded_height = FFALIGN(pc->height, 16); } + + if (avctx->codec_id == AV_CODEC_ID_MPEG1VIDEO || nb_pic_ext > 1) { + s->repeat_pict = 1; + s->picture_structure = AV_PICTURE_STRUCTURE_FRAME; + s->field_order = nb_pic_ext > 1 ? first_field : AV_FIELD_PROGRESSIVE; + } } static int mpegvideo_parse(AVCodecParserContext *s, diff --git a/libavcodec/mpegvideoenc.h b/libavcodec/mpegvideoenc.h index 0e93124cc20..1e297826606 100644 --- a/libavcodec/mpegvideoenc.h +++ b/libavcodec/mpegvideoenc.h @@ -44,33 +44,33 @@ #define FF_MPV_FLAG_MV0 0x0020 #define FF_MPV_OPT_CMP_FUNC \ -{ "sad", "Sum of absolute differences, fast", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_SAD }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "sse", "Sum of squared errors", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_SSE }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "satd", "Sum of absolute Hadamard transformed differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_SATD }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "dct", "Sum of absolute DCT transformed differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_DCT }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "psnr", "Sum of squared quantization errors, low quality", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_PSNR }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "bit", "Number of bits needed for the block", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_BIT }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "rd", "Rate distortion optimal, slow", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_RD }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "zero", "Zero", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_ZERO }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "vsad", "Sum of absolute vertical differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_VSAD }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "vsse", "Sum of squared vertical differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_VSSE }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "nsse", "Noise preserving sum of squared differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_NSSE }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "dct264", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_DCT264 }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "dctmax", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_DCTMAX }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "chroma", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_CHROMA }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ -{ "msad", "Sum of absolute differences, median predicted", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_MEDIAN_SAD }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" } +{ "sad", "Sum of absolute differences, fast", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_SAD }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "sse", "Sum of squared errors", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_SSE }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "satd", "Sum of absolute Hadamard transformed differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_SATD }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "dct", "Sum of absolute DCT transformed differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_DCT }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "psnr", "Sum of squared quantization errors, low quality", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_PSNR }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "bit", "Number of bits needed for the block", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_BIT }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "rd", "Rate distortion optimal, slow", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_RD }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "zero", "Zero", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_ZERO }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "vsad", "Sum of absolute vertical differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_VSAD }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "vsse", "Sum of squared vertical differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_VSSE }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "nsse", "Noise preserving sum of squared differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_NSSE }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "dct264", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_DCT264 }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "dctmax", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_DCTMAX }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "chroma", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_CHROMA }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ +{ "msad", "Sum of absolute differences, median predicted", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_MEDIAN_SAD }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" } #define FF_MPV_OFFSET(x) offsetof(MpegEncContext, x) #define FF_MPV_OPT_FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM) #define FF_MPV_COMMON_OPTS \ FF_MPV_OPT_CMP_FUNC, \ -{ "mpv_flags", "Flags common for all mpegvideo-based encoders.", FF_MPV_OFFSET(mpv_flags), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "mpv_flags" },\ -{ "skip_rd", "RD optimal MB level residual skipping", 0, AV_OPT_TYPE_CONST, { .i64 = FF_MPV_FLAG_SKIP_RD }, 0, 0, FF_MPV_OPT_FLAGS, "mpv_flags" },\ -{ "strict_gop", "Strictly enforce gop size", 0, AV_OPT_TYPE_CONST, { .i64 = FF_MPV_FLAG_STRICT_GOP }, 0, 0, FF_MPV_OPT_FLAGS, "mpv_flags" },\ -{ "qp_rd", "Use rate distortion optimization for qp selection", 0, AV_OPT_TYPE_CONST, { .i64 = FF_MPV_FLAG_QP_RD }, 0, 0, FF_MPV_OPT_FLAGS, "mpv_flags" },\ -{ "cbp_rd", "use rate distortion optimization for CBP", 0, AV_OPT_TYPE_CONST, { .i64 = FF_MPV_FLAG_CBP_RD }, 0, 0, FF_MPV_OPT_FLAGS, "mpv_flags" },\ -{ "naq", "normalize adaptive quantization", 0, AV_OPT_TYPE_CONST, { .i64 = FF_MPV_FLAG_NAQ }, 0, 0, FF_MPV_OPT_FLAGS, "mpv_flags" },\ -{ "mv0", "always try a mb with mv=<0,0>", 0, AV_OPT_TYPE_CONST, { .i64 = FF_MPV_FLAG_MV0 }, 0, 0, FF_MPV_OPT_FLAGS, "mpv_flags" },\ +{ "mpv_flags", "Flags common for all mpegvideo-based encoders.", FF_MPV_OFFSET(mpv_flags), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "mpv_flags" },\ +{ "skip_rd", "RD optimal MB level residual skipping", 0, AV_OPT_TYPE_CONST, { .i64 = FF_MPV_FLAG_SKIP_RD }, 0, 0, FF_MPV_OPT_FLAGS, .unit = "mpv_flags" },\ +{ "strict_gop", "Strictly enforce gop size", 0, AV_OPT_TYPE_CONST, { .i64 = FF_MPV_FLAG_STRICT_GOP }, 0, 0, FF_MPV_OPT_FLAGS, .unit = "mpv_flags" },\ +{ "qp_rd", "Use rate distortion optimization for qp selection", 0, AV_OPT_TYPE_CONST, { .i64 = FF_MPV_FLAG_QP_RD }, 0, 0, FF_MPV_OPT_FLAGS, .unit = "mpv_flags" },\ +{ "cbp_rd", "use rate distortion optimization for CBP", 0, AV_OPT_TYPE_CONST, { .i64 = FF_MPV_FLAG_CBP_RD }, 0, 0, FF_MPV_OPT_FLAGS, .unit = "mpv_flags" },\ +{ "naq", "normalize adaptive quantization", 0, AV_OPT_TYPE_CONST, { .i64 = FF_MPV_FLAG_NAQ }, 0, 0, FF_MPV_OPT_FLAGS, .unit = "mpv_flags" },\ +{ "mv0", "always try a mb with mv=<0,0>", 0, AV_OPT_TYPE_CONST, { .i64 = FF_MPV_FLAG_MV0 }, 0, 0, FF_MPV_OPT_FLAGS, .unit = "mpv_flags" },\ { "luma_elim_threshold", "single coefficient elimination threshold for luminance (negative values also consider dc coefficient)",\ FF_MPV_OFFSET(luma_elim_threshold), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS },\ { "chroma_elim_threshold", "single coefficient elimination threshold for chrominance (negative values also consider dc coefficient)",\ @@ -95,7 +95,7 @@ FF_MPV_OPT_CMP_FUNC, \ {"skip_threshold", "Frame skip threshold", FF_MPV_OFFSET(frame_skip_threshold), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS }, \ {"skip_factor", "Frame skip factor", FF_MPV_OFFSET(frame_skip_factor), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS }, \ {"skip_exp", "Frame skip exponent", FF_MPV_OFFSET(frame_skip_exp), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS }, \ -{"skip_cmp", "Frame skip compare function", FF_MPV_OFFSET(frame_skip_cmp), AV_OPT_TYPE_INT, {.i64 = FF_CMP_DCTMAX }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, "cmp_func" }, \ +{"skip_cmp", "Frame skip compare function", FF_MPV_OFFSET(frame_skip_cmp), AV_OPT_TYPE_INT, {.i64 = FF_CMP_DCTMAX }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS, .unit = "cmp_func" }, \ {"sc_threshold", "Scene change threshold", FF_MPV_OFFSET(scenechange_threshold), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS }, \ {"noise_reduction", "Noise reduction", FF_MPV_OFFSET(noise_reduction), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS }, \ {"ps", "RTP payload size in bytes", FF_MPV_OFFSET(rtp_payload_size), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS }, \ @@ -106,10 +106,10 @@ FF_MPV_OPT_CMP_FUNC, \ {"brd_scale", "Downscale frames for dynamic B-frame decision", FF_MPV_OFFSET(brd_scale), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 3, FF_MPV_OPT_FLAGS }, #define FF_MPV_COMMON_MOTION_EST_OPTS \ -{"motion_est", "motion estimation algorithm", FF_MPV_OFFSET(motion_est), AV_OPT_TYPE_INT, {.i64 = FF_ME_EPZS }, FF_ME_ZERO, FF_ME_XONE, FF_MPV_OPT_FLAGS, "motion_est" }, \ -{ "zero", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_ZERO }, 0, 0, FF_MPV_OPT_FLAGS, "motion_est" }, \ -{ "epzs", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_EPZS }, 0, 0, FF_MPV_OPT_FLAGS, "motion_est" }, \ -{ "xone", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_XONE }, 0, 0, FF_MPV_OPT_FLAGS, "motion_est" }, \ +{"motion_est", "motion estimation algorithm", FF_MPV_OFFSET(motion_est), AV_OPT_TYPE_INT, {.i64 = FF_ME_EPZS }, FF_ME_ZERO, FF_ME_XONE, FF_MPV_OPT_FLAGS, .unit = "motion_est" }, \ +{ "zero", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_ZERO }, 0, 0, FF_MPV_OPT_FLAGS, .unit = "motion_est" }, \ +{ "epzs", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_EPZS }, 0, 0, FF_MPV_OPT_FLAGS, .unit = "motion_est" }, \ +{ "xone", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_XONE }, 0, 0, FF_MPV_OPT_FLAGS, .unit = "motion_est" }, \ {"mepc", "Motion estimation bitrate penalty compensation (1.0 = 256)", FF_MPV_OFFSET(me_penalty_compensation), AV_OPT_TYPE_INT, {.i64 = 256 }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS }, \ {"mepre", "pre motion estimation", FF_MPV_OFFSET(me_pre), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, FF_MPV_OPT_FLAGS }, \ {"intra_penalty", "Penalty for intra blocks in block decision", FF_MPV_OFFSET(intra_penalty), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX/2, FF_MPV_OPT_FLAGS }, \ diff --git a/libavcodec/mscc.c b/libavcodec/mscc.c index e8406aa268c..e467b48baff 100644 --- a/libavcodec/mscc.c +++ b/libavcodec/mscc.c @@ -53,6 +53,9 @@ static int rle_uncompress(AVCodecContext *avctx, GetByteContext *gb, PutByteCont unsigned run = bytestream2_get_byte(gb); if (run) { + if (bytestream2_get_bytes_left_p(pb) < run * s->bpp) + return AVERROR_INVALIDDATA; + switch (avctx->bits_per_coded_sample) { case 8: fill = bytestream2_get_byte(gb); @@ -101,6 +104,9 @@ static int rle_uncompress(AVCodecContext *avctx, GetByteContext *gb, PutByteCont bytestream2_seek_p(pb, y * avctx->width * s->bpp + x * s->bpp, SEEK_SET); } else { + if (bytestream2_get_bytes_left_p(pb) < copy * s->bpp) + return AVERROR_INVALIDDATA; + for (j = 0; j < copy; j++) { switch (avctx->bits_per_coded_sample) { case 8: @@ -150,7 +156,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, &size); if (pal && size == AVPALETTE_SIZE) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif for (j = 0; j < 256; j++) s->pal[j] = 0xFF000000 | AV_RL32(pal + j * 4); } else if (pal) { @@ -200,7 +210,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, s->uncomp_buf + s->bpp * j * avctx->width, s->bpp * avctx->width); } - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/msmpeg4_vc1_data.c b/libavcodec/msmpeg4_vc1_data.c index 059c6f32dc4..a25eb956ff0 100644 --- a/libavcodec/msmpeg4_vc1_data.c +++ b/libavcodec/msmpeg4_vc1_data.c @@ -32,28 +32,25 @@ #include "libavutil/attributes.h" #include "libavutil/thread.h" -VLC ff_msmp4_mb_i_vlc; -VLC ff_msmp4_dc_luma_vlc[2]; -VLC ff_msmp4_dc_chroma_vlc[2]; +VLCElem ff_msmp4_mb_i_vlc[536]; +const VLCElem *ff_msmp4_dc_vlc[2][2]; static av_cold void msmp4_vc1_vlcs_init(void) { - INIT_VLC_STATIC(&ff_msmp4_dc_luma_vlc[0], MSMP4_DC_VLC_BITS, 120, - &ff_table0_dc_lum[0][1], 8, 4, - &ff_table0_dc_lum[0][0], 8, 4, 1158); - INIT_VLC_STATIC(&ff_msmp4_dc_chroma_vlc[0], MSMP4_DC_VLC_BITS, 120, - &ff_table0_dc_chroma[0][1], 8, 4, - &ff_table0_dc_chroma[0][0], 8, 4, 1118); - INIT_VLC_STATIC(&ff_msmp4_dc_luma_vlc[1], MSMP4_DC_VLC_BITS, 120, - &ff_table1_dc_lum[0][1], 8, 4, - &ff_table1_dc_lum[0][0], 8, 4, 1476); - INIT_VLC_STATIC(&ff_msmp4_dc_chroma_vlc[1], MSMP4_DC_VLC_BITS, 120, - &ff_table1_dc_chroma[0][1], 8, 4, - &ff_table1_dc_chroma[0][0], 8, 4, 1216); + static VLCElem vlc_buf[1158 + 1118 + 1476 + 1216]; + VLCInitState state = VLC_INIT_STATE(vlc_buf); - INIT_VLC_STATIC(&ff_msmp4_mb_i_vlc, MSMP4_MB_INTRA_VLC_BITS, 64, - &ff_msmp4_mb_i_table[0][1], 4, 2, - &ff_msmp4_mb_i_table[0][0], 4, 2, 536); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + ff_msmp4_dc_vlc[i][j] = + ff_vlc_init_tables(&state, MSMP4_DC_VLC_BITS, 120, + &ff_msmp4_dc_tables[i][j][0][1], 8, 4, + &ff_msmp4_dc_tables[i][j][0][0], 8, 4, 0); + } + } + VLC_INIT_STATIC_TABLE(ff_msmp4_mb_i_vlc, MSMP4_MB_INTRA_VLC_BITS, 64, + &ff_msmp4_mb_i_table[0][1], 4, 2, + &ff_msmp4_mb_i_table[0][0], 4, 2, 0); } av_cold void ff_msmp4_vc1_vlcs_init_once(void) @@ -82,9 +79,11 @@ const uint16_t ff_msmp4_mb_i_table[64][2] = { { 0xd, 8 }, { 0x713, 13 }, { 0x1da, 10 }, { 0x169, 10 }, }; -/* dc table 0 */ -const uint32_t ff_table0_dc_lum[120][2] = { +const uint32_t ff_msmp4_dc_tables[2][2][120][2] = { +{ + /* dc table 0 */ + { { 0x1, 1 }, { 0x1, 2 }, { 0x1, 4 }, { 0x1, 5 }, { 0x5, 5 }, { 0x7, 5 }, { 0x8, 6 }, { 0xc, 6 }, { 0x0, 7 }, { 0x2, 7 }, { 0x12, 7 }, { 0x1a, 7 }, @@ -115,9 +114,8 @@ const uint32_t ff_table0_dc_lum[120][2] = { { 0x60784, 24 }, { 0x60785, 24 }, { 0x60786, 24 }, { 0x60787, 24 }, { 0x60788, 24 }, { 0x60789, 24 }, { 0x6078a, 24 }, { 0x6078b, 24 }, { 0x6078c, 24 }, { 0x6078d, 24 }, { 0x6078e, 24 }, { 0x6078f, 24 }, -}; - -const uint32_t ff_table0_dc_chroma[120][2] = { + }, + { { 0x0, 2 }, { 0x1, 2 }, { 0x5, 3 }, { 0x9, 4 }, { 0xd, 4 }, { 0x11, 5 }, { 0x1d, 5 }, { 0x1f, 5 }, { 0x21, 6 }, { 0x31, 6 }, { 0x38, 6 }, { 0x33, 6 }, @@ -148,11 +146,11 @@ const uint32_t ff_table0_dc_chroma[120][2] = { { 0x608884, 23 }, { 0x608885, 23 }, { 0x608886, 23 }, { 0x608887, 23 }, { 0x608888, 23 }, { 0x608889, 23 }, { 0x60888a, 23 }, { 0x60888b, 23 }, { 0x60888c, 23 }, { 0x60888d, 23 }, { 0x60888e, 23 }, { 0x60888f, 23 }, -}; - -/* dc table 1 */ - -const uint32_t ff_table1_dc_lum[120][2] = { + } +}, +{ + /* dc table 1 */ + { { 0x2, 2 }, { 0x3, 2 }, { 0x3, 3 }, { 0x2, 4 }, { 0x5, 4 }, { 0x1, 5 }, { 0x3, 5 }, { 0x8, 5 }, { 0x0, 6 }, { 0x5, 6 }, { 0xd, 6 }, { 0xf, 6 }, @@ -183,9 +181,8 @@ const uint32_t ff_table1_dc_lum[120][2] = { { 0x1e695c, 26 }, { 0x1e695d, 26 }, { 0x1e695e, 26 }, { 0x1e695f, 26 }, { 0x1e6960, 26 }, { 0x1e6961, 26 }, { 0x1e6962, 26 }, { 0x1e6963, 26 }, { 0x1e6964, 26 }, { 0x1e6965, 26 }, { 0x1e6966, 26 }, { 0x1e6967, 26 }, -}; - -const uint32_t ff_table1_dc_chroma[120][2] = { + }, + { { 0x0, 2 }, { 0x1, 2 }, { 0x4, 3 }, { 0x7, 3 }, { 0xb, 4 }, { 0xd, 4 }, { 0x15, 5 }, { 0x28, 6 }, { 0x30, 6 }, { 0x32, 6 }, { 0x52, 7 }, { 0x62, 7 }, @@ -216,6 +213,8 @@ const uint32_t ff_table1_dc_chroma[120][2] = { { 0x18f6484, 25 }, { 0x18f6485, 25 }, { 0x18f6486, 25 }, { 0x18f6487, 25 }, { 0x18f6488, 25 }, { 0x18f6489, 25 }, { 0x18f648a, 25 }, { 0x18f648b, 25 }, { 0x18f648c, 25 }, { 0x18f648d, 25 }, { 0x18f648e, 25 }, { 0x18f648f, 25 }, + } +} }; const uint8_t ff_wmv1_scantable[WMV1_SCANTABLE_COUNT][64] = { diff --git a/libavcodec/msmpeg4_vc1_data.h b/libavcodec/msmpeg4_vc1_data.h index d12fcb032f3..a92193a8f5d 100644 --- a/libavcodec/msmpeg4_vc1_data.h +++ b/libavcodec/msmpeg4_vc1_data.h @@ -34,10 +34,9 @@ FF_VISIBILITY_PUSH_HIDDEN void ff_msmp4_vc1_vlcs_init_once(void); #define MSMP4_MB_INTRA_VLC_BITS 9 -extern VLC ff_msmp4_mb_i_vlc; +extern VLCElem ff_msmp4_mb_i_vlc[]; #define MSMP4_DC_VLC_BITS 9 -extern VLC ff_msmp4_dc_luma_vlc[2]; -extern VLC ff_msmp4_dc_chroma_vlc[2]; +extern const VLCElem *ff_msmp4_dc_vlc[2 /* dc_table_index */][2 /* 0: luma, 1: chroma */]; /* intra picture macroblock coded block pattern */ extern const uint16_t ff_msmp4_mb_i_table[64][2]; @@ -46,10 +45,7 @@ extern const uint16_t ff_msmp4_mb_i_table[64][2]; extern const uint8_t ff_wmv1_scantable[WMV1_SCANTABLE_COUNT][64]; -extern const uint32_t ff_table0_dc_lum[120][2]; -extern const uint32_t ff_table1_dc_lum[120][2]; -extern const uint32_t ff_table0_dc_chroma[120][2]; -extern const uint32_t ff_table1_dc_chroma[120][2]; +extern const uint32_t ff_msmp4_dc_tables[2 /* dc_table_index */][2 /* 0: luma, 1: chroma */][120][2]; FF_VISIBILITY_POP_HIDDEN #endif /* AVCODEC_MSMPEG4_VC1_DATA_H */ diff --git a/libavcodec/msmpeg4data.h b/libavcodec/msmpeg4data.h index 26ee960f823..aa4ca86a053 100644 --- a/libavcodec/msmpeg4data.h +++ b/libavcodec/msmpeg4data.h @@ -44,7 +44,7 @@ typedef struct MVTable { const uint8_t *table_mvx; const uint8_t *table_mvy; uint16_t *table_mv_index; /* encoding: convert mv to index in table_mv */ - VLC vlc; /* decoding: vlc */ + const VLCElem *vlc; /* decoding: vlc */ } MVTable; FF_VISIBILITY_PUSH_HIDDEN diff --git a/libavcodec/msmpeg4dec.c b/libavcodec/msmpeg4dec.c index 26a196a38fc..bf1e4877bda 100644 --- a/libavcodec/msmpeg4dec.c +++ b/libavcodec/msmpeg4dec.c @@ -63,19 +63,19 @@ static inline int msmpeg4v1_pred_dc(MpegEncContext * s, int n, /****************************************/ /* decoding stuff */ -VLC ff_mb_non_intra_vlc[4]; -static VLC v2_dc_lum_vlc; -static VLC v2_dc_chroma_vlc; -static VLC v2_intra_cbpc_vlc; -static VLC v2_mb_type_vlc; -VLC ff_inter_intra_vlc; +const VLCElem *ff_mb_non_intra_vlc[4]; +static VLCElem v2_dc_lum_vlc[1472]; +static VLCElem v2_dc_chroma_vlc[1506]; +static VLCElem v2_intra_cbpc_vlc[8]; +static VLCElem v2_mb_type_vlc[128]; +VLCElem ff_inter_intra_vlc[8]; /* This is identical to H.263 except that its range is multiplied by 2. */ static int msmpeg4v2_decode_motion(MpegEncContext * s, int pred, int f_code) { int code, val, sign, shift; - code = get_vlc2(&s->gb, ff_h263_mv_vlc.table, H263_MV_VLC_BITS, 2); + code = get_vlc2(&s->gb, ff_h263_mv_vlc, H263_MV_VLC_BITS, 2); ff_dlog(s, "MV code %d at %d %d pred: %d\n", code, s->mb_x,s->mb_y, pred); if (code < 0) return 0xffff; @@ -125,9 +125,9 @@ static int msmpeg4v12_decode_mb(MpegEncContext *s, int16_t block[6][64]) } if(s->msmpeg4_version==2) - code = get_vlc2(&s->gb, v2_mb_type_vlc.table, V2_MB_TYPE_VLC_BITS, 1); + code = get_vlc2(&s->gb, v2_mb_type_vlc, V2_MB_TYPE_VLC_BITS, 1); else - code = get_vlc2(&s->gb, ff_h263_inter_MCBPC_vlc.table, INTER_MCBPC_VLC_BITS, 2); + code = get_vlc2(&s->gb, ff_h263_inter_MCBPC_vlc, INTER_MCBPC_VLC_BITS, 2); if(code<0 || code>7){ av_log(s->avctx, AV_LOG_ERROR, "cbpc %d invalid at %d %d\n", code, s->mb_x, s->mb_y); return -1; @@ -139,9 +139,9 @@ static int msmpeg4v12_decode_mb(MpegEncContext *s, int16_t block[6][64]) } else { s->mb_intra = 1; if(s->msmpeg4_version==2) - cbp= get_vlc2(&s->gb, v2_intra_cbpc_vlc.table, V2_INTRA_CBPC_VLC_BITS, 1); + cbp = get_vlc2(&s->gb, v2_intra_cbpc_vlc, V2_INTRA_CBPC_VLC_BITS, 1); else - cbp= get_vlc2(&s->gb, ff_h263_intra_MCBPC_vlc.table, INTRA_MCBPC_VLC_BITS, 2); + cbp = get_vlc2(&s->gb, ff_h263_intra_MCBPC_vlc, INTRA_MCBPC_VLC_BITS, 2); if(cbp<0 || cbp>3){ av_log(s->avctx, AV_LOG_ERROR, "cbpc %d invalid at %d %d\n", cbp, s->mb_x, s->mb_y); return -1; @@ -151,7 +151,7 @@ static int msmpeg4v12_decode_mb(MpegEncContext *s, int16_t block[6][64]) if (!s->mb_intra) { int mx, my, cbpy; - cbpy= get_vlc2(&s->gb, ff_h263_cbpy_vlc.table, CBPY_VLC_BITS, 1); + cbpy = get_vlc2(&s->gb, ff_h263_cbpy_vlc, CBPY_VLC_BITS, 1); if(cbpy<0){ av_log(s->avctx, AV_LOG_ERROR, "cbpy %d invalid at %d %d\n", cbp, s->mb_x, s->mb_y); return -1; @@ -173,7 +173,7 @@ static int msmpeg4v12_decode_mb(MpegEncContext *s, int16_t block[6][64]) int v; if(s->msmpeg4_version==2){ s->ac_pred = get_bits1(&s->gb); - v = get_vlc2(&s->gb, ff_h263_cbpy_vlc.table, CBPY_VLC_BITS, 1); + v = get_vlc2(&s->gb, ff_h263_cbpy_vlc, CBPY_VLC_BITS, 1); if (v < 0) { av_log(s->avctx, AV_LOG_ERROR, "cbpy vlc invalid\n"); return -1; @@ -181,7 +181,7 @@ static int msmpeg4v12_decode_mb(MpegEncContext *s, int16_t block[6][64]) cbp|= v<<2; } else{ s->ac_pred = 0; - v = get_vlc2(&s->gb, ff_h263_cbpy_vlc.table, CBPY_VLC_BITS, 1); + v = get_vlc2(&s->gb, ff_h263_cbpy_vlc, CBPY_VLC_BITS, 1); if (v < 0) { av_log(s->avctx, AV_LOG_ERROR, "cbpy vlc invalid\n"); return -1; @@ -230,14 +230,14 @@ static int msmpeg4v34_decode_mb(MpegEncContext *s, int16_t block[6][64]) } } - code = get_vlc2(&s->gb, ff_mb_non_intra_vlc[DEFAULT_INTER_INDEX].table, MB_NON_INTRA_VLC_BITS, 3); + code = get_vlc2(&s->gb, ff_mb_non_intra_vlc[DEFAULT_INTER_INDEX], MB_NON_INTRA_VLC_BITS, 3); //s->mb_intra = (code & 0x40) ? 0 : 1; s->mb_intra = (~code & 0x40) >> 6; cbp = code & 0x3f; } else { s->mb_intra = 1; - code = get_vlc2(&s->gb, ff_msmp4_mb_i_vlc.table, MSMP4_MB_INTRA_VLC_BITS, 2); + code = get_vlc2(&s->gb, ff_msmp4_mb_i_vlc, MSMP4_MB_INTRA_VLC_BITS, 2); /* predict coded block pattern */ cbp = 0; for(i=0;i<6;i++) { @@ -271,7 +271,7 @@ static int msmpeg4v34_decode_mb(MpegEncContext *s, int16_t block[6][64]) s->ac_pred = get_bits1(&s->gb); *mb_type_ptr = MB_TYPE_INTRA; if(s->inter_intra_pred){ - s->h263_aic_dir= get_vlc2(&s->gb, ff_inter_intra_vlc.table, INTER_INTRA_VLC_BITS, 1); + s->h263_aic_dir= get_vlc2(&s->gb, ff_inter_intra_vlc, INTER_INTRA_VLC_BITS, 1); ff_dlog(s, "%d%d %d %d/", s->ac_pred, s->h263_aic_dir, s->mb_x, s->mb_y); } @@ -296,55 +296,58 @@ static int msmpeg4v34_decode_mb(MpegEncContext *s, int16_t block[6][64]) /* init all vlc decoding tables */ static av_cold void msmpeg4_decode_init_static(void) { + static VLCElem vlc_buf[3714 + 2694 + 1636 + 2648 + 1532 + 2488]; + VLCInitState state = VLC_INIT_STATE(vlc_buf); MVTable *mv; INIT_FIRST_VLC_RL(ff_rl_table[0], 642); INIT_FIRST_VLC_RL(ff_rl_table[1], 1104); INIT_FIRST_VLC_RL(ff_rl_table[2], 554); - INIT_VLC_RL(ff_rl_table[3], 940); - INIT_VLC_RL(ff_rl_table[4], 962); + VLC_INIT_RL(ff_rl_table[3], 940); + VLC_INIT_RL(ff_rl_table[4], 962); /* ff_rl_table[5] coincides with ff_h263_rl_inter which has just been * initialized in ff_h263_decode_init() earlier. So just copy the VLCs. */ av_assert1(ff_h263_rl_inter.rl_vlc[0]); memcpy(ff_rl_table[5].rl_vlc, ff_h263_rl_inter.rl_vlc, sizeof(ff_rl_table[5].rl_vlc)); + VLC_INIT_STATIC_TABLE(v2_dc_lum_vlc, MSMP4_DC_VLC_BITS, 512, + &ff_v2_dc_lum_table[0][1], 8, 4, + &ff_v2_dc_lum_table[0][0], 8, 4, 0); + VLC_INIT_STATIC_TABLE(v2_dc_chroma_vlc, MSMP4_DC_VLC_BITS, 512, + &ff_v2_dc_chroma_table[0][1], 8, 4, + &ff_v2_dc_chroma_table[0][0], 8, 4, 0); + + VLC_INIT_STATIC_TABLE(v2_intra_cbpc_vlc, V2_INTRA_CBPC_VLC_BITS, 4, + &ff_v2_intra_cbpc[0][1], 2, 1, + &ff_v2_intra_cbpc[0][0], 2, 1, 0); + VLC_INIT_STATIC_TABLE(v2_mb_type_vlc, V2_MB_TYPE_VLC_BITS, 8, + &ff_v2_mb_type[0][1], 2, 1, + &ff_v2_mb_type[0][0], 2, 1, 0); + mv = &ff_mv_tables[0]; - INIT_VLC_STATIC(&mv->vlc, MV_VLC_BITS, MSMPEG4_MV_TABLES_NB_ELEMS + 1, - mv->table_mv_bits, 1, 1, - mv->table_mv_code, 2, 2, 3714); + mv->vlc = ff_vlc_init_tables_sparse(&state, MV_VLC_BITS, + MSMPEG4_MV_TABLES_NB_ELEMS + 1, + mv->table_mv_bits, 1, 1, + mv->table_mv_code, 2, 2, + NULL, 0, 0, 0); mv = &ff_mv_tables[1]; - INIT_VLC_STATIC(&mv->vlc, MV_VLC_BITS, MSMPEG4_MV_TABLES_NB_ELEMS + 1, - mv->table_mv_bits, 1, 1, - mv->table_mv_code, 2, 2, 2694); - - INIT_VLC_STATIC(&v2_dc_lum_vlc, MSMP4_DC_VLC_BITS, 512, - &ff_v2_dc_lum_table[0][1], 8, 4, - &ff_v2_dc_lum_table[0][0], 8, 4, 1472); - INIT_VLC_STATIC(&v2_dc_chroma_vlc, MSMP4_DC_VLC_BITS, 512, - &ff_v2_dc_chroma_table[0][1], 8, 4, - &ff_v2_dc_chroma_table[0][0], 8, 4, 1506); - - INIT_VLC_STATIC(&v2_intra_cbpc_vlc, V2_INTRA_CBPC_VLC_BITS, 4, - &ff_v2_intra_cbpc[0][1], 2, 1, - &ff_v2_intra_cbpc[0][0], 2, 1, 8); - INIT_VLC_STATIC(&v2_mb_type_vlc, V2_MB_TYPE_VLC_BITS, 8, - &ff_v2_mb_type[0][1], 2, 1, - &ff_v2_mb_type[0][0], 2, 1, 128); - - for (unsigned i = 0, offset = 0; i < 4; i++) { - static VLCElem vlc_buf[1636 + 2648 + 1532 + 2488]; - ff_mb_non_intra_vlc[i].table = &vlc_buf[offset]; - ff_mb_non_intra_vlc[i].table_allocated = FF_ARRAY_ELEMS(vlc_buf) - offset; - init_vlc(&ff_mb_non_intra_vlc[i], MB_NON_INTRA_VLC_BITS, 128, - &ff_wmv2_inter_table[i][0][1], 8, 4, - &ff_wmv2_inter_table[i][0][0], 8, 4, - INIT_VLC_STATIC_OVERLONG); - offset += ff_mb_non_intra_vlc[i].table_size; + mv->vlc = ff_vlc_init_tables_sparse(&state, MV_VLC_BITS, + MSMPEG4_MV_TABLES_NB_ELEMS + 1, + mv->table_mv_bits, 1, 1, + mv->table_mv_code, 2, 2, + NULL, 0, 0, 0); + + for (unsigned i = 0; i < 4; i++) { + ff_mb_non_intra_vlc[i] = + ff_vlc_init_tables_sparse(&state, MB_NON_INTRA_VLC_BITS, 128, + &ff_wmv2_inter_table[i][0][1], 8, 4, + &ff_wmv2_inter_table[i][0][0], 8, 4, + NULL, 0, 0, 0); } - INIT_VLC_STATIC(&ff_inter_intra_vlc, INTER_INTRA_VLC_BITS, 4, - &ff_table_inter_intra[0][1], 2, 1, - &ff_table_inter_intra[0][0], 2, 1, 8); + VLC_INIT_STATIC_TABLE(ff_inter_intra_vlc, INTER_INTRA_VLC_BITS, 4, + &ff_table_inter_intra[0][1], 2, 1, + &ff_table_inter_intra[0][0], 2, 1, 0); ff_msmp4_vc1_vlcs_init_once(); } @@ -572,9 +575,9 @@ static int msmpeg4_decode_dc(MpegEncContext * s, int n, int *dir_ptr) if(s->msmpeg4_version<=2){ if (n < 4) { - level = get_vlc2(&s->gb, v2_dc_lum_vlc.table, MSMP4_DC_VLC_BITS, 3); + level = get_vlc2(&s->gb, v2_dc_lum_vlc, MSMP4_DC_VLC_BITS, 3); } else { - level = get_vlc2(&s->gb, v2_dc_chroma_vlc.table, MSMP4_DC_VLC_BITS, 3); + level = get_vlc2(&s->gb, v2_dc_chroma_vlc, MSMP4_DC_VLC_BITS, 3); } if (level < 0) { av_log(s->avctx, AV_LOG_ERROR, "illegal dc vlc\n"); @@ -582,14 +585,9 @@ static int msmpeg4_decode_dc(MpegEncContext * s, int n, int *dir_ptr) return -1; } level-=256; - }else{ //FIXME optimize use unified tables & index - if (n < 4) { - level = get_vlc2(&s->gb, ff_msmp4_dc_luma_vlc[s->dc_table_index].table, - MSMP4_DC_VLC_BITS, 3); - } else { - level = get_vlc2(&s->gb, ff_msmp4_dc_chroma_vlc[s->dc_table_index].table, - MSMP4_DC_VLC_BITS, 3); - } + } else { + level = get_vlc2(&s->gb, ff_msmp4_dc_vlc[s->dc_table_index][n >= 4], + MSMP4_DC_VLC_BITS, 3); if (level == DC_MAX) { level = get_bits(&s->gb, 8); @@ -818,7 +816,7 @@ void ff_msmpeg4_decode_motion(MpegEncContext *s, int *mx_ptr, int *my_ptr) mv = &ff_mv_tables[s->mv_table_index]; - code = get_vlc2(&s->gb, mv->vlc.table, MV_VLC_BITS, 2); + code = get_vlc2(&s->gb, mv->vlc, MV_VLC_BITS, 2); if (code == MSMPEG4_MV_TABLES_NB_ELEMS) { mx = get_bits(&s->gb, 6); my = get_bits(&s->gb, 6); @@ -855,10 +853,6 @@ const FFCodec ff_msmpeg4v1_decoder = { .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, .p.max_lowres = 3, - .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE - }, }; const FFCodec ff_msmpeg4v2_decoder = { @@ -873,10 +867,6 @@ const FFCodec ff_msmpeg4v2_decoder = { .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, .p.max_lowres = 3, - .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE - }, }; const FFCodec ff_msmpeg4v3_decoder = { @@ -891,10 +881,6 @@ const FFCodec ff_msmpeg4v3_decoder = { .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, .p.max_lowres = 3, - .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE - }, }; const FFCodec ff_wmv1_decoder = { @@ -909,8 +895,4 @@ const FFCodec ff_wmv1_decoder = { .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, .p.max_lowres = 3, - .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE - }, }; diff --git a/libavcodec/msmpeg4dec.h b/libavcodec/msmpeg4dec.h index ad41eea9d45..5daa7c6bc34 100644 --- a/libavcodec/msmpeg4dec.h +++ b/libavcodec/msmpeg4dec.h @@ -28,8 +28,8 @@ #define INTER_INTRA_VLC_BITS 3 #define MB_NON_INTRA_VLC_BITS 9 -extern VLC ff_mb_non_intra_vlc[4]; -extern VLC ff_inter_intra_vlc; +extern const VLCElem *ff_mb_non_intra_vlc[4]; +extern VLCElem ff_inter_intra_vlc[8]; int ff_msmpeg4_decode_init(AVCodecContext *avctx); int ff_msmpeg4_decode_picture_header(MpegEncContext *s); diff --git a/libavcodec/msmpeg4enc.c b/libavcodec/msmpeg4enc.c index 54121438a0c..119ea8f15e3 100644 --- a/libavcodec/msmpeg4enc.c +++ b/libavcodec/msmpeg4enc.c @@ -280,7 +280,20 @@ void ff_msmpeg4_encode_picture_header(MpegEncContext * s) void ff_msmpeg4_encode_ext_header(MpegEncContext * s) { - unsigned fps = s->avctx->time_base.den / s->avctx->time_base.num / FFMAX(s->avctx->ticks_per_frame, 1); + unsigned fps; + + if (s->avctx->framerate.num > 0 && s->avctx->framerate.den > 0) + fps = s->avctx->framerate.num / s->avctx->framerate.den; + else { +FF_DISABLE_DEPRECATION_WARNINGS + fps = s->avctx->time_base.den / s->avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + / FFMAX(s->avctx->ticks_per_frame, 1) +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS + } + put_bits(&s->pb, 5, FFMIN(fps, 31)); //yes 29.97 -> 29 put_bits(&s->pb, 11, FFMIN(s->bit_rate / 1024, 2047)); @@ -531,19 +544,8 @@ static void msmpeg4_encode_dc(MpegEncContext * s, int level, int n, int *dir_ptr if (code > DC_MAX) code = DC_MAX; - if (s->dc_table_index == 0) { - if (n < 4) { - put_bits(&s->pb, ff_table0_dc_lum[code][1], ff_table0_dc_lum[code][0]); - } else { - put_bits(&s->pb, ff_table0_dc_chroma[code][1], ff_table0_dc_chroma[code][0]); - } - } else { - if (n < 4) { - put_bits(&s->pb, ff_table1_dc_lum[code][1], ff_table1_dc_lum[code][0]); - } else { - put_bits(&s->pb, ff_table1_dc_chroma[code][1], ff_table1_dc_chroma[code][0]); - } - } + put_bits(&s->pb, ff_msmp4_dc_tables[s->dc_table_index][n >= 4][code][1], + ff_msmp4_dc_tables[s->dc_table_index][n >= 4][code][0]); if (code == DC_MAX) put_bits(&s->pb, 8, level); diff --git a/libavcodec/msp2dec.c b/libavcodec/msp2dec.c index 9c51c35c61e..30a2825e47b 100644 --- a/libavcodec/msp2dec.c +++ b/libavcodec/msp2dec.c @@ -47,7 +47,7 @@ static int msp2_decode_frame(AVCodecContext *avctx, AVFrame *p, return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; bytestream2_init(&idx, buf, 2 * avctx->height); buf += 2 * avctx->height; diff --git a/libavcodec/msrle.c b/libavcodec/msrle.c index b6fa7f7abb9..51e843e4a69 100644 --- a/libavcodec/msrle.c +++ b/libavcodec/msrle.c @@ -95,7 +95,14 @@ static int msrle_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; if (avctx->bits_per_coded_sample > 1 && avctx->bits_per_coded_sample <= 8) { - s->frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + s->frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif /* make the palette available */ memcpy(s->frame->data[1], s->pal, AVPALETTE_SIZE); diff --git a/libavcodec/msrleenc.c b/libavcodec/msrleenc.c new file mode 100644 index 00000000000..cc39aa308f0 --- /dev/null +++ b/libavcodec/msrleenc.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2023 Tomas Härdin + * + * 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 + * MSRLE encoder + * @see https://wiki.multimedia.cx/index.php?title=Microsoft_RLE + */ + +// TODO: pal4 mode? + +#include "bytestream.h" +#include "codec_internal.h" +#include "encode.h" + +typedef struct MSRLEContext { + int curframe; + AVFrame *last_frame; +} MSRLEContext; + +static av_cold int msrle_encode_init(AVCodecContext *avctx) +{ + MSRLEContext *s = avctx->priv_data; + + avctx->bits_per_coded_sample = 8; + s->last_frame = av_frame_alloc(); + if (!s->last_frame) + return AVERROR(ENOMEM); + + return 0; +} + +static void write_run(AVCodecContext *avctx, uint8_t **data, int len, int value) +{ + // we're allowed to write odd runs + while (len >= 255) { + bytestream_put_byte(data, 255); + bytestream_put_byte(data, value); + len -= 255; + } + if (len >= 1) { + // this is wasteful when len == 1 and sometimes when len == 2 + // but sometimes we have no choice. also write_absolute() + // relies on this + bytestream_put_byte(data, len); + bytestream_put_byte(data, value); + } +} + +static void write_absolute(AVCodecContext *avctx, uint8_t **data, + const uint8_t *line, int len) +{ + // writing 255 would be wasteful here due to the padding requirement + while (len >= 254) { + bytestream_put_byte(data, 0); + bytestream_put_byte(data, 254); + bytestream_put_buffer(data, line, 254); + line += 254; + len -= 254; + } + if (len == 1) { + // it's less wasteful to write single pixels as runs + // not to mention that absolute mode requires >= 3 pixels + write_run(avctx, data, 1, line[0]); + } else if (len == 2) { + write_run(avctx, data, 1, line[0]); + write_run(avctx, data, 1, line[1]); + } else if (len > 0) { + bytestream_put_byte(data, 0); + bytestream_put_byte(data, len); + bytestream_put_buffer(data, line, len); + if (len & 1) + bytestream_put_byte(data, 0); + } +} + +static void write_delta(AVCodecContext *avctx, uint8_t **data, int delta) +{ + // we let the yskip logic handle the case where we want to delta + // to following lines. it's not perfect but it's easier than finding + // the optimal combination of end-of-lines and deltas to reach any + // following position including places where dx < 0 + while (delta >= 255) { + bytestream_put_byte(data, 0); + bytestream_put_byte(data, 2); + bytestream_put_byte(data, 255); + bytestream_put_byte(data, 0); + delta -= 255; + } + if (delta > 0) { + bytestream_put_byte(data, 0); + bytestream_put_byte(data, 2); + bytestream_put_byte(data, delta); + bytestream_put_byte(data, 0); + } +} + +static void write_yskip(AVCodecContext *avctx, uint8_t **data, int yskip) +{ + if (yskip < 4) + return; + // we have yskip*2 nul bytess + *data -= 2*yskip; + // the end-of-line counts as one skip + yskip--; + while (yskip >= 255) { + bytestream_put_byte(data, 0); + bytestream_put_byte(data, 2); + bytestream_put_byte(data, 0); + bytestream_put_byte(data, 255); + yskip -= 255; + } + if (yskip > 0) { + bytestream_put_byte(data, 0); + bytestream_put_byte(data, 2); + bytestream_put_byte(data, 0); + bytestream_put_byte(data, yskip); + } + bytestream_put_be16(data, 0x0000); +} + +// used both to encode lines in keyframes and to encode lines between deltas +static void encode_line(AVCodecContext *avctx, uint8_t **data, + const uint8_t *line, int length) +{ + int run = 0, last = -1, absstart = 0; + if (length == 0) + return; + for (int x = 0; x < length; x++) { + if (last == line[x]) { + run++; + if (run == 3) + write_absolute(avctx, data, &line[absstart], x - absstart - 2); + } else { + if (run >= 3) { + write_run(avctx, data, run, last); + absstart = x; + } + run = 1; + } + last = line[x]; + } + if (run >= 3) + write_run(avctx, data, run, last); + else + write_absolute(avctx, data, &line[absstart], length - absstart); +} + +static int encode(AVCodecContext *avctx, AVPacket *pkt, + const AVFrame *pict, int keyframe, int *got_keyframe) +{ + MSRLEContext *s = avctx->priv_data; + uint8_t *data = pkt->data; + + /* Compare the current frame to the last frame, or code the entire frame + if keyframe != 0. We're continually outputting pairs of bytes: + + 00 00 end of line + 00 01 end of bitmap + 00 02 dx dy delta. move pointer to x+dx, y+dy + 00 ll dd dd .. absolute (verbatim) mode. ll >= 3 + rr dd run. rr >= 1 + + For keyframes we only have absolute mode and runs at our disposal, and + we are not allowed to end a line early. If this happens when keyframe == 0 + then *got_keyframe is set to 1 and s->curframe is reset. + */ + *got_keyframe = 1; // set to zero whenever we use a feature that makes this a not-keyframe + + if (keyframe) { + for (int y = avctx->height-1; y >= 0; y--) { + uint8_t *line = &pict->data[0][y*pict->linesize[0]]; + encode_line(avctx, &data, line, avctx->width); + bytestream_put_be16(&data, 0x0000); // end of line + } + } else { + // compare to previous frame + int yskip = 0; // we can encode large skips using deltas + for (int y = avctx->height-1; y >= 0; y--) { + const uint8_t *line = &pict->data[0][y*pict->linesize[0]]; + const uint8_t *prev = &s->last_frame->data[0][y*s->last_frame->linesize[0]]; + // we need at least 5 pixels in a row for a delta to be worthwhile + int delta = 0, linestart = 0, encoded = 0; + for (int x = 0; x < avctx->width; x++) { + if (line[x] == prev[x]) { + delta++; + if (delta == 5) { + int len = x - linestart - 4; + if (len > 0) { + write_yskip(avctx, &data, yskip); + yskip = 0; + encode_line(avctx, &data, &line[linestart], len); + encoded = 1; + } + linestart = -1; + } + } else { + if (delta >= 5) { + write_yskip(avctx, &data, yskip); + yskip = 0; + write_delta(avctx, &data, delta); + *got_keyframe = 0; + encoded = 1; + } + delta = 0; + if (linestart == -1) + linestart = x; + } + } + if (delta < 5) { + write_yskip(avctx, &data, yskip); + yskip = 0; + encode_line(avctx, &data, &line[linestart], avctx->width - linestart); + encoded = 1; + } else + *got_keyframe = 0; + bytestream_put_be16(&data, 0x0000); // end of line + if (!encoded) + yskip++; + else + yskip = 0; + } + write_yskip(avctx, &data, yskip); + } + bytestream_put_be16(&data, 0x0001); // end of bitmap + pkt->size = data - pkt->data; + return 0; +} + +static int msrle_encode_frame(AVCodecContext *avctx, AVPacket *pkt, + const AVFrame *pict, int *got_packet) +{ + MSRLEContext *s = avctx->priv_data; + int ret, got_keyframe; + + if ((ret = ff_alloc_packet(avctx, pkt, ( + avctx->width*2 /* worst case = rle every pixel */ + 2 /*end of line */ + ) * avctx->height + 2 /* end of bitmap */ + FF_INPUT_BUFFER_MIN_SIZE))) + return ret; + + if (pict->data[1]) { + uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); + if (!side_data) + return AVERROR(ENOMEM); + memcpy(side_data, pict->data[1], AVPALETTE_SIZE); + } + + if ((ret = encode(avctx, pkt, pict, s->curframe == 0, &got_keyframe))) + return ret; + + if (got_keyframe) { + pkt->flags |= AV_PKT_FLAG_KEY; + s->curframe = 0; + } + if (++s->curframe >= avctx->gop_size) + s->curframe = 0; + *got_packet = 1; + + return av_frame_replace(s->last_frame, pict); +} + +static int msrle_encode_close(AVCodecContext *avctx) +{ + MSRLEContext *s = avctx->priv_data; + av_frame_free(&s->last_frame); + return 0; +} + +const FFCodec ff_msrle_encoder = { + .p.name = "msrle", + CODEC_LONG_NAME("Microsoft RLE"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MSRLE, + .p.capabilities = AV_CODEC_CAP_DR1, + .priv_data_size = sizeof(MSRLEContext), + .init = msrle_encode_init, + FF_CODEC_ENCODE_CB(msrle_encode_frame), + .close = msrle_encode_close, + .p.pix_fmts = (const enum AVPixelFormat[]){ + AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE + }, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/mss1.c b/libavcodec/mss1.c index 775852102a9..54606731335 100644 --- a/libavcodec/mss1.c +++ b/libavcodec/mss1.c @@ -165,12 +165,12 @@ static int mss1_decode_frame(AVCodecContext *avctx, AVFrame *rframe, c->corrupted = 0; ff_mss12_slicecontext_reset(&ctx->sc); pal_changed = decode_pal(c, &acoder); - ctx->pic->key_frame = 1; + ctx->pic->flags |= AV_FRAME_FLAG_KEY; ctx->pic->pict_type = AV_PICTURE_TYPE_I; } else { if (c->corrupted) return AVERROR_INVALIDDATA; - ctx->pic->key_frame = 0; + ctx->pic->flags &= ~AV_FRAME_FLAG_KEY; ctx->pic->pict_type = AV_PICTURE_TYPE_P; } c->corrupted = ff_mss12_decode_rect(&ctx->sc, &acoder, 0, 0, @@ -178,7 +178,11 @@ static int mss1_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if (c->corrupted) return AVERROR_INVALIDDATA; memcpy(ctx->pic->data[1], c->pal, AVPALETTE_SIZE); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS ctx->pic->palette_has_changed = pal_changed; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if ((ret = av_frame_ref(rframe, ctx->pic)) < 0) return ret; @@ -202,7 +206,7 @@ static av_cold int mss1_decode_init(AVCodecContext *avctx) ret = ff_mss12_decode_init(&c->ctx, 0, &c->sc, NULL); if (ret < 0) - av_frame_free(&c->pic); + return ret; avctx->pix_fmt = AV_PIX_FMT_PAL8; @@ -229,4 +233,5 @@ const FFCodec ff_mss1_decoder = { .close = mss1_decode_end, FF_CODEC_DECODE_CB(mss1_decode_frame), .p.capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/mss2.c b/libavcodec/mss2.c index 1d1ed11f54e..d54a1220b55 100644 --- a/libavcodec/mss2.c +++ b/libavcodec/mss2.c @@ -324,7 +324,7 @@ static int decode_rle(GetBitContext *gb, uint8_t *pal_dst, ptrdiff_t pal_stride, if (next_code != 1 << current_length) return AVERROR_INVALIDDATA; - if ((i = init_vlc(&vlc, 9, alphabet_size, bits, 1, 1, codes, 4, 4, 0)) < 0) + if ((i = vlc_init(&vlc, 9, alphabet_size, bits, 1, 1, codes, 4, 4, 0)) < 0) return i; /* frame decode */ @@ -371,7 +371,7 @@ static int decode_rle(GetBitContext *gb, uint8_t *pal_dst, ptrdiff_t pal_stride, prev_avail = 1; } while (--h); - ff_free_vlc(&vlc); + ff_vlc_free(&vlc); return 0; } @@ -422,7 +422,7 @@ static int decode_wmv9(AVCodecContext *avctx, const uint8_t *buf, int buf_size, ff_vc1_decode_blocks(v); if (v->end_mb_x == s->mb_width && s->end_mb_y == s->mb_height) { - ff_er_frame_end(&s->er); + ff_er_frame_end(&s->er, NULL); } else { av_log(v->s.avctx, AV_LOG_WARNING, "disabling error correction due to block count mismatch %dx%d != %dx%d\n", @@ -660,7 +660,10 @@ static int mss2_decode_frame(AVCodecContext *avctx, AVFrame *frame, frame->linesize[0] * (avctx->height - 1); c->rgb_stride = -frame->linesize[0]; - frame->key_frame = keyframe; + if (keyframe) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if (is_555) { @@ -794,8 +797,7 @@ static int mss2_decode_frame(AVCodecContext *avctx, AVFrame *frame, av_log(avctx, AV_LOG_WARNING, "buffer not fully consumed\n"); if (c->mvX < 0 || c->mvY < 0) { - av_frame_unref(ctx->last_pic); - ret = av_frame_ref(ctx->last_pic, frame); + ret = av_frame_replace(ctx->last_pic, frame); if (ret < 0) return ret; } @@ -884,14 +886,10 @@ static av_cold int mss2_decode_init(AVCodecContext *avctx) c->pal_stride = c->mask_stride; c->pal_pic = av_mallocz(c->pal_stride * avctx->height); c->last_pal_pic = av_mallocz(c->pal_stride * avctx->height); - if (!c->pal_pic || !c->last_pal_pic || !ctx->last_pic) { - mss2_decode_end(avctx); + if (!c->pal_pic || !c->last_pal_pic || !ctx->last_pic) return AVERROR(ENOMEM); - } - if (ret = wmv9_init(avctx)) { - mss2_decode_end(avctx); + if (ret = wmv9_init(avctx)) return ret; - } ff_mss2dsp_init(&ctx->dsp); avctx->pix_fmt = c->free_colours == 127 ? AV_PIX_FMT_RGB555 @@ -911,4 +909,5 @@ const FFCodec ff_mss2_decoder = { .close = mss2_decode_end, FF_CODEC_DECODE_CB(mss2_decode_frame), .p.capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/mss3.c b/libavcodec/mss3.c index 023f110ec81..2701e9b912a 100644 --- a/libavcodec/mss3.c +++ b/libavcodec/mss3.c @@ -740,7 +740,10 @@ static int mss3_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; - c->pic->key_frame = keyframe; + if (keyframe) + c->pic->flags |= AV_FRAME_FLAG_KEY; + else + c->pic->flags &= ~AV_FRAME_FLAG_KEY; c->pic->pict_type = keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if (!bytestream2_get_bytes_left(&gb)) { if ((ret = av_frame_ref(rframe, c->pic)) < 0) diff --git a/libavcodec/mss4.c b/libavcodec/mss4.c index dceb42da25d..8ae4f152c6d 100644 --- a/libavcodec/mss4.c +++ b/libavcodec/mss4.c @@ -115,9 +115,9 @@ static av_cold void mss4_init_vlc(VLC *vlc, unsigned *offset, vlc->table = &vlc_buf[*offset]; vlc->table_allocated = FF_ARRAY_ELEMS(vlc_buf) - *offset; - ff_init_vlc_from_lengths(vlc, FFMIN(bits[idx - 1], 9), idx, + ff_vlc_init_from_lengths(vlc, FFMIN(bits[idx - 1], 9), idx, bits, 1, syms, 1, 1, - 0, INIT_VLC_STATIC_OVERLONG, NULL); + 0, VLC_INIT_STATIC_OVERLONG, NULL); *offset += vlc->table_size; } @@ -156,9 +156,10 @@ static av_always_inline int get_coeff_bits(GetBitContext *gb, int nbits) return val; } -static inline int get_coeff(GetBitContext *gb, VLC *vlc) +static inline int get_coeff(GetBitContext *gb, const VLC *vlc, + int nb_bits, int max_depth) { - int val = get_vlc2(gb, vlc->table, vlc->bits, 2); + int val = get_vlc2(gb, vlc->table, nb_bits, max_depth); return get_coeff_bits(gb, val); } @@ -171,7 +172,7 @@ static int mss4_decode_dct(GetBitContext *gb, VLC *dc_vlc, VLC *ac_vlc, memset(block, 0, sizeof(*block) * 64); - dc = get_coeff(gb, dc_vlc); + dc = get_coeff(gb, dc_vlc, dc_vlc->bits, 2); // DC prediction is the same as in MSS3 if (by) { if (bx) { @@ -337,7 +338,7 @@ static int mss4_decode_image_block(MSS4Context *ctx, GetBitContext *gb, for (i = 0; i < 3; i++) { vec_len[i] = vec_len_syms[!!i][get_unary(gb, 0, 3)]; for (j = 0; j < vec_len[i]; j++) { - vec[i][j] = get_coeff(gb, &vec_entry_vlc[!!i]); + vec[i][j] = get_coeff(gb, &vec_entry_vlc[!!i], 5, 1); vec[i][j] += ctx->prev_vec[i][j]; ctx->prev_vec[i][j] = vec[i][j]; } @@ -503,7 +504,10 @@ static int mss4_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; - c->pic->key_frame = (frame_type == INTRA_FRAME); + if (frame_type == INTRA_FRAME) + c->pic->flags |= AV_FRAME_FLAG_KEY; + else + c->pic->flags &= ~AV_FRAME_FLAG_KEY; c->pic->pict_type = (frame_type == INTRA_FRAME) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if (frame_type == SKIP_FRAME) { diff --git a/libavcodec/msvideo1.c b/libavcodec/msvideo1.c index 9903ff36a7f..ca4583d8415 100644 --- a/libavcodec/msvideo1.c +++ b/libavcodec/msvideo1.c @@ -312,7 +312,14 @@ static int msvideo1_decode_frame(AVCodecContext *avctx, AVFrame *rframe, return ret; if (s->mode_8bit) { - s->frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + s->frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif } if (s->mode_8bit) diff --git a/libavcodec/msvideo1enc.c b/libavcodec/msvideo1enc.c index 36cfd39a854..3bea3ed2975 100644 --- a/libavcodec/msvideo1enc.c +++ b/libavcodec/msvideo1enc.c @@ -78,12 +78,14 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, int skips = 0; int quality = 24; - if ((ret = ff_alloc_packet(avctx, pkt, avctx->width*avctx->height*9 + AV_INPUT_BUFFER_MIN_SIZE)) < 0) + if ((ret = ff_alloc_packet(avctx, pkt, avctx->width*avctx->height*9 + FF_INPUT_BUFFER_MIN_SIZE)) < 0) return ret; dst= buf= pkt->data; if(!c->prev) c->prev = av_malloc(avctx->width * 3 * (avctx->height + 3)); + if (!c->prev) + return AVERROR(ENOMEM); prevptr = c->prev + avctx->width * 3 * (FFALIGN(avctx->height, 4) - 1); src = (const uint16_t*)(p->data[0] + p->linesize[0]*(FFALIGN(avctx->height, 4) - 1)); if(c->keyint >= avctx->keyint_min) diff --git a/libavcodec/mv30.c b/libavcodec/mv30.c index 24b04400fde..8c45c8304bd 100644 --- a/libavcodec/mv30.c +++ b/libavcodec/mv30.c @@ -59,7 +59,7 @@ typedef struct MV30Context { AVFrame *prev_frame; } MV30Context; -static VLC cbp_tab; +static VLCElem cbp_tab[1 << CBP_VLC_BITS]; static const uint8_t luma_tab[] = { 12, 12, 15, 19, 25, 34, 40, 48, @@ -379,7 +379,7 @@ static int decode_coeffs(GetBitContext *gb, int16_t *coeffs, int nb_codes) memset(coeffs, 0, nb_codes * sizeof(*coeffs)); for (int i = 0; i < nb_codes;) { - int value = get_vlc2(gb, cbp_tab.table, CBP_VLC_BITS, 1); + int value = get_vlc2(gb, cbp_tab, CBP_VLC_BITS, 1); if (value > 0) { int x = get_bits(gb, value); @@ -623,9 +623,8 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, get_qtable(s->intraq_tab[0], s->intra_quant, luma_tab); get_qtable(s->intraq_tab[1], s->intra_quant, chroma_tab); - frame->key_frame = s->is_inter == 0; - - if (frame->key_frame) { + if (s->is_inter == 0) { + frame->flags |= AV_FRAME_FLAG_KEY; ret = decode_intra(avctx, gb, frame); if (ret < 0) return ret; @@ -638,13 +637,13 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return AVERROR_INVALIDDATA; } + frame->flags &= ~AV_FRAME_FLAG_KEY; ret = decode_inter(avctx, gb, frame, s->prev_frame); if (ret < 0) return ret; } - av_frame_unref(s->prev_frame); - if ((ret = av_frame_ref(s->prev_frame, frame)) < 0) + if ((ret = av_frame_replace(s->prev_frame, frame)) < 0) return ret; *got_frame = 1; @@ -658,8 +657,9 @@ static const uint8_t cbp_bits[] = { static av_cold void init_static_data(void) { - INIT_VLC_STATIC_FROM_LENGTHS(&cbp_tab, CBP_VLC_BITS, FF_ARRAY_ELEMS(cbp_bits), - cbp_bits, 1, NULL, 0, 0, 0, 0, 1 << CBP_VLC_BITS); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(cbp_tab, CBP_VLC_BITS, + FF_ARRAY_ELEMS(cbp_bits), + cbp_bits, 1, NULL, 0, 0, 0, 0); } static av_cold int decode_init(AVCodecContext *avctx) diff --git a/libavcodec/mvcdec.c b/libavcodec/mvcdec.c index 1e99f44a7d1..6c971f709ed 100644 --- a/libavcodec/mvcdec.c +++ b/libavcodec/mvcdec.c @@ -247,7 +247,7 @@ static int mvc_decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; diff --git a/libavcodec/mvha.c b/libavcodec/mvha.c index 55056c91cf4..356cebc64e7 100644 --- a/libavcodec/mvha.c +++ b/libavcodec/mvha.c @@ -83,7 +83,7 @@ static int build_vlc(AVCodecContext *avctx, VLC *vlc) uint8_t xlat[256]; int cur_node, i, j, pos = 0; - ff_free_vlc(vlc); + ff_vlc_free(vlc); for (i = 0; i < s->nb_symbols; i++) { nodes[i].count = s->prob[i]; @@ -140,7 +140,7 @@ static int build_vlc(AVCodecContext *avctx, VLC *vlc) get_tree_codes(bits, lens, xlat, nodes, cur_node - 1, 0, 0, &pos); - return ff_init_vlc_sparse(vlc, 12, pos, lens, 2, 2, bits, 4, 4, xlat, 1, 1, 0); + return ff_vlc_init_sparse(vlc, 12, pos, lens, 2, 2, bits, 4, 4, xlat, 1, 1, 0); } static int decode_frame(AVCodecContext *avctx, AVFrame *frame, @@ -272,7 +272,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; @@ -294,7 +294,7 @@ static av_cold int decode_close(AVCodecContext *avctx) MVHAContext *s = avctx->priv_data; ff_inflate_end(&s->zstream); - ff_free_vlc(&s->vlc); + ff_vlc_free(&s->vlc); return 0; } diff --git a/libavcodec/mwsc.c b/libavcodec/mwsc.c index f57648bb15a..a7e8702580a 100644 --- a/libavcodec/mwsc.c +++ b/libavcodec/mwsc.c @@ -50,6 +50,10 @@ static int rle_uncompress(GetByteContext *gb, PutByteContext *pb, GetByteContext if (run == 0) { run = bytestream2_get_le32(gb); + + if (bytestream2_tell_p(pb) + width - w < run) + return AVERROR_INVALIDDATA; + for (int j = 0; j < run; j++, w++) { if (w == width) { w = 0; @@ -61,6 +65,10 @@ static int rle_uncompress(GetByteContext *gb, PutByteContext *pb, GetByteContext int pos = bytestream2_tell_p(pb); bytestream2_seek(gbp, pos, SEEK_SET); + + if (pos + width - w < fill) + return AVERROR_INVALIDDATA; + for (int j = 0; j < fill; j++, w++) { if (w == width) { w = 0; @@ -72,6 +80,9 @@ static int rle_uncompress(GetByteContext *gb, PutByteContext *pb, GetByteContext intra = 0; } else { + if (bytestream2_tell_p(pb) + width - w < run) + return AVERROR_INVALIDDATA; + for (int j = 0; j < run; j++, w++) { if (w == width) { w = 0; @@ -119,13 +130,15 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, bytestream2_init(&gbp, s->prev_frame->data[0], avctx->height * s->prev_frame->linesize[0]); bytestream2_init_writer(&pb, frame->data[0], avctx->height * frame->linesize[0]); - frame->key_frame = rle_uncompress(&gb, &pb, &gbp, avctx->width, avctx->height, avctx->width * 3, - frame->linesize[0], s->prev_frame->linesize[0]); + if (rle_uncompress(&gb, &pb, &gbp, avctx->width, avctx->height, avctx->width * 3, + frame->linesize[0], s->prev_frame->linesize[0])) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; - frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + frame->pict_type = (frame->flags & AV_FRAME_FLAG_KEY) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; - av_frame_unref(s->prev_frame); - if ((ret = av_frame_ref(s->prev_frame, frame)) < 0) + if ((ret = av_frame_replace(s->prev_frame, frame)) < 0) return ret; *got_frame = 1; diff --git a/libavcodec/mxpegdec.c b/libavcodec/mxpegdec.c index 760b12f0cce..73df2ff9ffa 100644 --- a/libavcodec/mxpegdec.c +++ b/libavcodec/mxpegdec.c @@ -286,11 +286,11 @@ static int mxpeg_decode_frame(AVCodecContext *avctx, AVFrame *rframe, AV_GET_BUFFER_FLAG_REF)) < 0) return ret; jpg->picture_ptr->pict_type = AV_PICTURE_TYPE_P; - jpg->picture_ptr->key_frame = 0; + jpg->picture_ptr->flags &= ~AV_FRAME_FLAG_KEY; jpg->got_picture = 1; } else { jpg->picture_ptr->pict_type = AV_PICTURE_TYPE_I; - jpg->picture_ptr->key_frame = 1; + jpg->picture_ptr->flags |= AV_FRAME_FLAG_KEY; } if (s->got_mxm_bitmask) { diff --git a/libavcodec/neon/Makefile b/libavcodec/neon/Makefile index 607f116a772..83c2f0051c9 100644 --- a/libavcodec/neon/Makefile +++ b/libavcodec/neon/Makefile @@ -1 +1,4 @@ +clean:: + $(RM) $(CLEANSUFFIXES:%=libavcodec/neon/%) + OBJS-$(CONFIG_MPEGVIDEO) += neon/mpegvideo.o diff --git a/libavcodec/notchlc.c b/libavcodec/notchlc.c index 90704e1aeba..6351a313f81 100644 --- a/libavcodec/notchlc.c +++ b/libavcodec/notchlc.c @@ -76,12 +76,13 @@ static int lz4_decompress(AVCodecContext *avctx, GetByteContext *gb, PutByteContext *pb) { - unsigned reference_pos, match_length, delta, pos = 0; + unsigned reference_pos, delta, pos = 0; uint8_t history[64 * 1024]; + int match_length; while (bytestream2_get_bytes_left(gb) > 0) { uint8_t token = bytestream2_get_byte(gb); - unsigned num_literals = token >> 4; + int num_literals = token >> 4; if (num_literals == 15) { unsigned char current; @@ -241,7 +242,9 @@ static int decode_blocks(AVCodecContext *avctx, AVFrame *p, bytestream2_seek(&dgb, s->y_data_offset + row_offset, SEEK_SET); - init_get_bits8(&bit, dgb.buffer, bytestream2_get_bytes_left(&dgb)); + ret = init_get_bits8(&bit, dgb.buffer, bytestream2_get_bytes_left(&dgb)); + if (ret < 0) + return ret; for (int x = 0; x < avctx->width; x += 4) { unsigned item = bytestream2_get_le32(gb); unsigned y_min = item & 4095; @@ -514,7 +517,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; diff --git a/libavcodec/nuv.c b/libavcodec/nuv.c index d5391eee54c..1dda770c976 100644 --- a/libavcodec/nuv.c +++ b/libavcodec/nuv.c @@ -56,8 +56,8 @@ static void copy_frame(AVFrame *f, const uint8_t *src, int width, int height) int src_linesize[4]; av_image_fill_arrays(src_data, src_linesize, src, f->format, width, height, 1); - av_image_copy(f->data, f->linesize, (const uint8_t **)src_data, src_linesize, - f->format, width, height); + av_image_copy2(f->data, f->linesize, src_data, src_linesize, + f->format, width, height); } /** @@ -263,7 +263,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, } c->pic->pict_type = keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; - c->pic->key_frame = keyframe; + if (keyframe) + c->pic->flags |= AV_FRAME_FLAG_KEY; + else + c->pic->flags &= ~AV_FRAME_FLAG_KEY; // decompress/copy/whatever data switch (comptype) { case NUV_LZO: diff --git a/libavcodec/nvdec.c b/libavcodec/nvdec.c index a477449d142..1741ee7e470 100644 --- a/libavcodec/nvdec.c +++ b/libavcodec/nvdec.c @@ -35,6 +35,7 @@ #include "decode.h" #include "nvdec.h" #include "internal.h" +#include "refstruct.h" #if !NVDECAPI_CHECK_VERSION(9, 0) #define cudaVideoSurfaceFormat_YUV444 2 @@ -161,9 +162,9 @@ static int nvdec_test_capabilities(NVDECDecoder *decoder, return 0; } -static void nvdec_decoder_free(void *opaque, uint8_t *data) +static void nvdec_decoder_free(FFRefStructOpaque unused, void *obj) { - NVDECDecoder *decoder = (NVDECDecoder*)data; + NVDECDecoder *decoder = obj; if (decoder->decoder) { void *logctx = decoder->hw_device_ref->data; @@ -177,33 +178,24 @@ static void nvdec_decoder_free(void *opaque, uint8_t *data) av_buffer_unref(&decoder->hw_device_ref); cuvid_free_functions(&decoder->cvdl); - - av_freep(&decoder); } -static int nvdec_decoder_create(AVBufferRef **out, AVBufferRef *hw_device_ref, +static int nvdec_decoder_create(NVDECDecoder **out, AVBufferRef *hw_device_ref, CUVIDDECODECREATEINFO *params, void *logctx) { AVHWDeviceContext *hw_device_ctx = (AVHWDeviceContext*)hw_device_ref->data; AVCUDADeviceContext *device_hwctx = hw_device_ctx->hwctx; - AVBufferRef *decoder_ref; NVDECDecoder *decoder; CUcontext dummy; int ret; - decoder = av_mallocz(sizeof(*decoder)); + decoder = ff_refstruct_alloc_ext(sizeof(*decoder), 0, + NULL, nvdec_decoder_free); if (!decoder) return AVERROR(ENOMEM); - decoder_ref = av_buffer_create((uint8_t*)decoder, sizeof(*decoder), - nvdec_decoder_free, NULL, AV_BUFFER_FLAG_READONLY); - if (!decoder_ref) { - av_freep(&decoder); - return AVERROR(ENOMEM); - } - decoder->hw_device_ref = av_buffer_ref(hw_device_ref); if (!decoder->hw_device_ref) { ret = AVERROR(ENOMEM); @@ -237,37 +229,38 @@ static int nvdec_decoder_create(AVBufferRef **out, AVBufferRef *hw_device_ref, goto fail; } - *out = decoder_ref; + *out = decoder; return 0; fail: - av_buffer_unref(&decoder_ref); + ff_refstruct_unref(&decoder); return ret; } -static AVBufferRef *nvdec_decoder_frame_alloc(void *opaque, size_t size) +static int nvdec_decoder_frame_init(FFRefStructOpaque opaque, void *obj) { - NVDECFramePool *pool = opaque; - AVBufferRef *ret; + NVDECFramePool *pool = opaque.nc; + unsigned int *intp = obj; if (pool->nb_allocated >= pool->dpb_size) - return NULL; + return AVERROR(ENOMEM); - ret = av_buffer_alloc(sizeof(unsigned int)); - if (!ret) - return NULL; + *intp = pool->nb_allocated++; - *(unsigned int*)ret->data = pool->nb_allocated++; + return 0; +} - return ret; +static void nvdec_decoder_frame_pool_free(FFRefStructOpaque opaque) +{ + av_free(opaque.nc); } int ff_nvdec_decode_uninit(AVCodecContext *avctx) { NVDECContext *ctx = avctx->internal->hwaccel_priv_data; - av_freep(&ctx->bitstream); av_freep(&ctx->bitstream_internal); + ctx->bitstream = NULL; ctx->bitstream_len = 0; ctx->bitstream_allocated = 0; @@ -275,8 +268,8 @@ int ff_nvdec_decode_uninit(AVCodecContext *avctx) ctx->nb_slices = 0; ctx->slice_offsets_allocated = 0; - av_buffer_unref(&ctx->decoder_ref); - av_buffer_pool_uninit(&ctx->decoder_pool); + ff_refstruct_unref(&ctx->decoder); + ff_refstruct_pool_uninit(&ctx->decoder_pool); return 0; } @@ -408,7 +401,7 @@ int ff_nvdec_decode_init(AVCodecContext *avctx) params.ulNumDecodeSurfaces = frames_ctx->initial_pool_size; params.ulNumOutputSurfaces = unsafe_output ? frames_ctx->initial_pool_size : 1; - ret = nvdec_decoder_create(&ctx->decoder_ref, frames_ctx->device_ref, ¶ms, avctx); + ret = nvdec_decoder_create(&ctx->decoder, frames_ctx->device_ref, ¶ms, avctx); if (ret < 0) { if (params.ulNumDecodeSurfaces > 32) { av_log(avctx, AV_LOG_WARNING, "Using more than 32 (%d) decode surfaces might cause nvdec to fail.\n", @@ -420,7 +413,7 @@ int ff_nvdec_decode_init(AVCodecContext *avctx) return ret; } - decoder = (NVDECDecoder*)ctx->decoder_ref->data; + decoder = ctx->decoder; decoder->unsafe_output = unsafe_output; decoder->real_hw_frames_ref = real_hw_frames_ref; real_hw_frames_ref = NULL; @@ -432,8 +425,9 @@ int ff_nvdec_decode_init(AVCodecContext *avctx) } pool->dpb_size = frames_ctx->initial_pool_size; - ctx->decoder_pool = av_buffer_pool_init2(sizeof(int), pool, - nvdec_decoder_frame_alloc, av_free); + ctx->decoder_pool = ff_refstruct_pool_alloc_ext(sizeof(unsigned int), 0, pool, + nvdec_decoder_frame_init, + NULL, NULL, nvdec_decoder_frame_pool_free); if (!ctx->decoder_pool) { ret = AVERROR(ENOMEM); goto fail; @@ -452,9 +446,9 @@ static void nvdec_fdd_priv_free(void *priv) if (!cf) return; - av_buffer_unref(&cf->idx_ref); - av_buffer_unref(&cf->decoder_ref); - av_buffer_unref(&cf->ref_idx_ref); + ff_refstruct_unref(&cf->idx_ref); + ff_refstruct_unref(&cf->ref_idx_ref); + ff_refstruct_unref(&cf->decoder); av_freep(&priv); } @@ -462,7 +456,7 @@ static void nvdec_fdd_priv_free(void *priv) static void nvdec_unmap_mapped_frame(void *opaque, uint8_t *data) { NVDECFrame *unmap_data = (NVDECFrame*)data; - NVDECDecoder *decoder = (NVDECDecoder*)unmap_data->decoder_ref->data; + NVDECDecoder *decoder = unmap_data->decoder; void *logctx = decoder->hw_device_ref->data; CUdeviceptr devptr = (CUdeviceptr)opaque; int ret; @@ -477,9 +471,9 @@ static void nvdec_unmap_mapped_frame(void *opaque, uint8_t *data) CHECK_CU(decoder->cudl->cuCtxPopCurrent(&dummy)); finish: - av_buffer_unref(&unmap_data->idx_ref); - av_buffer_unref(&unmap_data->decoder_ref); - av_buffer_unref(&unmap_data->ref_idx_ref); + ff_refstruct_unref(&unmap_data->idx_ref); + ff_refstruct_unref(&unmap_data->ref_idx_ref); + ff_refstruct_unref(&unmap_data->decoder); av_free(unmap_data); } @@ -487,7 +481,7 @@ static int nvdec_retrieve_data(void *logctx, AVFrame *frame) { FrameDecodeData *fdd = (FrameDecodeData*)frame->private_ref->data; NVDECFrame *cf = (NVDECFrame*)fdd->hwaccel_priv; - NVDECDecoder *decoder = (NVDECDecoder*)cf->decoder_ref->data; + NVDECDecoder *decoder = cf->decoder; AVHWFramesContext *hwctx = (AVHWFramesContext *)frame->hw_frames_ctx->data; @@ -534,11 +528,8 @@ static int nvdec_retrieve_data(void *logctx, AVFrame *frame) goto copy_fail; unmap_data->idx = cf->idx; - if (!(unmap_data->idx_ref = av_buffer_ref(cf->idx_ref)) || - !(unmap_data->decoder_ref = av_buffer_ref(cf->decoder_ref))) { - ret = AVERROR(ENOMEM); - goto copy_fail; - } + unmap_data->idx_ref = ff_refstruct_ref(cf->idx_ref); + unmap_data->decoder = ff_refstruct_ref(cf->decoder); av_pix_fmt_get_chroma_sub_sample(hwctx->sw_format, &shift_h, &shift_v); for (i = 0; frame->linesize[i]; i++) { @@ -583,19 +574,15 @@ int ff_nvdec_start_frame(AVCodecContext *avctx, AVFrame *frame) if (!cf) return AVERROR(ENOMEM); - cf->decoder_ref = av_buffer_ref(ctx->decoder_ref); - if (!cf->decoder_ref) { - ret = AVERROR(ENOMEM); - goto fail; - } + cf->decoder = ff_refstruct_ref(ctx->decoder); - cf->idx_ref = av_buffer_pool_get(ctx->decoder_pool); + cf->idx_ref = ff_refstruct_pool_get(ctx->decoder_pool); if (!cf->idx_ref) { av_log(avctx, AV_LOG_ERROR, "No decoder surfaces left\n"); ret = AVERROR(ENOMEM); goto fail; } - cf->ref_idx = cf->idx = *(unsigned int*)cf->idx_ref->data; + cf->ref_idx = cf->idx = *cf->idx_ref; fdd->hwaccel_priv = cf; fdd->hwaccel_priv_free = nvdec_fdd_priv_free; @@ -623,16 +610,16 @@ int ff_nvdec_start_frame_sep_ref(AVCodecContext *avctx, AVFrame *frame, int has_ if (has_sep_ref) { if (!cf->ref_idx_ref) { - cf->ref_idx_ref = av_buffer_pool_get(ctx->decoder_pool); + cf->ref_idx_ref = ff_refstruct_pool_get(ctx->decoder_pool); if (!cf->ref_idx_ref) { av_log(avctx, AV_LOG_ERROR, "No decoder surfaces left\n"); ret = AVERROR(ENOMEM); goto fail; } } - cf->ref_idx = *(unsigned int*)cf->ref_idx_ref->data; + cf->ref_idx = *cf->ref_idx_ref; } else { - av_buffer_unref(&cf->ref_idx_ref); + ff_refstruct_unref(&cf->ref_idx_ref); cf->ref_idx = cf->idx; } @@ -645,7 +632,7 @@ int ff_nvdec_start_frame_sep_ref(AVCodecContext *avctx, AVFrame *frame, int has_ int ff_nvdec_end_frame(AVCodecContext *avctx) { NVDECContext *ctx = avctx->internal->hwaccel_priv_data; - NVDECDecoder *decoder = (NVDECDecoder*)ctx->decoder_ref->data; + NVDECDecoder *decoder = ctx->decoder; void *logctx = avctx; CUVIDPICPARAMS *pp = &ctx->pic_params; @@ -677,6 +664,8 @@ int ff_nvdec_simple_end_frame(AVCodecContext *avctx) NVDECContext *ctx = avctx->internal->hwaccel_priv_data; int ret = ff_nvdec_end_frame(avctx); ctx->bitstream = NULL; + ctx->bitstream_len = 0; + ctx->nb_slices = 0; return ret; } @@ -693,7 +682,7 @@ int ff_nvdec_simple_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, ctx->slice_offsets = tmp; if (!ctx->bitstream) - ctx->bitstream = (uint8_t*)buffer; + ctx->bitstream = buffer; ctx->slice_offsets[ctx->nb_slices] = buffer - ctx->bitstream; ctx->bitstream_len += size; diff --git a/libavcodec/nvdec.h b/libavcodec/nvdec.h index 66f3ca59e71..555300d27df 100644 --- a/libavcodec/nvdec.h +++ b/libavcodec/nvdec.h @@ -44,19 +44,19 @@ typedef struct NVDECFrame { unsigned int idx; unsigned int ref_idx; - AVBufferRef *idx_ref; - AVBufferRef *ref_idx_ref; - AVBufferRef *decoder_ref; + unsigned int *idx_ref; ///< RefStruct reference + unsigned int *ref_idx_ref; ///< RefStruct reference + struct NVDECDecoder *decoder; ///< RefStruct reference } NVDECFrame; typedef struct NVDECContext { CUVIDPICPARAMS pic_params; - AVBufferPool *decoder_pool; + struct FFRefStructPool *decoder_pool; - AVBufferRef *decoder_ref; + struct NVDECDecoder *decoder; ///< RefStruct reference - uint8_t *bitstream; + const uint8_t *bitstream; int bitstream_len; unsigned int bitstream_allocated; uint8_t *bitstream_internal; diff --git a/libavcodec/nvdec_av1.c b/libavcodec/nvdec_av1.c index 3bbcd761232..35f22ebf804 100644 --- a/libavcodec/nvdec_av1.c +++ b/libavcodec/nvdec_av1.c @@ -23,6 +23,7 @@ #include "avcodec.h" #include "nvdec.h" #include "decode.h" +#include "hwaccel_internal.h" #include "internal.h" #include "av1dec.h" @@ -302,7 +303,7 @@ static int nvdec_av1_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, /* Shortcut if all tiles are in the same buffer */ if (ctx->nb_slices == s->tg_end - s->tg_start + 1) { - ctx->bitstream = (uint8_t*)buffer; + ctx->bitstream = buffer; ctx->bitstream_len = size; for (int i = 0; i < ctx->nb_slices; ++i) { @@ -320,7 +321,7 @@ static int nvdec_av1_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, } ctx->bitstream = ctx->bitstream_internal = tmp; - memcpy(ctx->bitstream + ctx->bitstream_len, buffer, size); + memcpy(ctx->bitstream_internal + ctx->bitstream_len, buffer, size); for (uint32_t tile_num = s->tg_start; tile_num <= s->tg_end; ++tile_num) { ctx->slice_offsets[tile_num*2 ] = ctx->bitstream_len + s->tile_group_info[tile_num].tile_offset; @@ -337,11 +338,11 @@ static int nvdec_av1_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ return ff_nvdec_frame_params(avctx, hw_frames_ctx, 8 * 2, 0); } -const AVHWAccel ff_av1_nvdec_hwaccel = { - .name = "av1_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_AV1, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_av1_nvdec_hwaccel = { + .p.name = "av1_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_av1_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = nvdec_av1_decode_slice, diff --git a/libavcodec/nvdec_h264.c b/libavcodec/nvdec_h264.c index 116bd4fb5de..ea6c1081eba 100644 --- a/libavcodec/nvdec_h264.c +++ b/libavcodec/nvdec_h264.c @@ -28,6 +28,7 @@ #include "decode.h" #include "internal.h" #include "h264dec.h" +#include "hwaccel_internal.h" static void dpb_add(const H264Context *h, CUVIDH264DPBENTRY *dst, const H264Picture *src, int frame_idx) @@ -137,11 +138,11 @@ static int nvdec_h264_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, const H264SliceContext *sl = &h->slice_ctx[0]; void *tmp; - tmp = av_fast_realloc(ctx->bitstream, &ctx->bitstream_allocated, + tmp = av_fast_realloc(ctx->bitstream_internal, &ctx->bitstream_allocated, ctx->bitstream_len + size + 3); if (!tmp) return AVERROR(ENOMEM); - ctx->bitstream = tmp; + ctx->bitstream = ctx->bitstream_internal = tmp; tmp = av_fast_realloc(ctx->slice_offsets, &ctx->slice_offsets_allocated, (ctx->nb_slices + 1) * sizeof(*ctx->slice_offsets)); @@ -149,8 +150,8 @@ static int nvdec_h264_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, return AVERROR(ENOMEM); ctx->slice_offsets = tmp; - AV_WB24(ctx->bitstream + ctx->bitstream_len, 1); - memcpy(ctx->bitstream + ctx->bitstream_len + 3, buffer, size); + AV_WB24(ctx->bitstream_internal + ctx->bitstream_len, 1); + memcpy(ctx->bitstream_internal + ctx->bitstream_len + 3, buffer, size); ctx->slice_offsets[ctx->nb_slices] = ctx->bitstream_len ; ctx->bitstream_len += size + 3; ctx->nb_slices++; @@ -169,11 +170,11 @@ static int nvdec_h264_frame_params(AVCodecContext *avctx, return ff_nvdec_frame_params(avctx, hw_frames_ctx, sps->ref_frame_count + sps->num_reorder_frames, 0); } -const AVHWAccel ff_h264_nvdec_hwaccel = { - .name = "h264_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_h264_nvdec_hwaccel = { + .p.name = "h264_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_h264_start_frame, .end_frame = ff_nvdec_end_frame, .decode_slice = nvdec_h264_decode_slice, diff --git a/libavcodec/nvdec_hevc.c b/libavcodec/nvdec_hevc.c index 1ab1c39d756..ff118af04bf 100644 --- a/libavcodec/nvdec_hevc.c +++ b/libavcodec/nvdec_hevc.c @@ -29,6 +29,7 @@ #include "internal.h" #include "hevcdec.h" #include "hevc_data.h" +#include "hwaccel_internal.h" static void dpb_add(CUVIDHEVCPICPARAMS *pp, int idx, const HEVCFrame *src) { @@ -273,11 +274,11 @@ static int nvdec_hevc_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, NVDECContext *ctx = avctx->internal->hwaccel_priv_data; void *tmp; - tmp = av_fast_realloc(ctx->bitstream, &ctx->bitstream_allocated, + tmp = av_fast_realloc(ctx->bitstream_internal, &ctx->bitstream_allocated, ctx->bitstream_len + size + 3); if (!tmp) return AVERROR(ENOMEM); - ctx->bitstream = tmp; + ctx->bitstream = ctx->bitstream_internal = tmp; tmp = av_fast_realloc(ctx->slice_offsets, &ctx->slice_offsets_allocated, (ctx->nb_slices + 1) * sizeof(*ctx->slice_offsets)); @@ -285,8 +286,8 @@ static int nvdec_hevc_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, return AVERROR(ENOMEM); ctx->slice_offsets = tmp; - AV_WB24(ctx->bitstream + ctx->bitstream_len, 1); - memcpy(ctx->bitstream + ctx->bitstream_len + 3, buffer, size); + AV_WB24(ctx->bitstream_internal + ctx->bitstream_len, 1); + memcpy(ctx->bitstream_internal + ctx->bitstream_len + 3, buffer, size); ctx->slice_offsets[ctx->nb_slices] = ctx->bitstream_len ; ctx->bitstream_len += size + 3; ctx->nb_slices++; @@ -306,10 +307,10 @@ static int nvdec_hevc_decode_init(AVCodecContext *avctx) { NVDECContext *ctx = avctx->internal->hwaccel_priv_data; ctx->supports_444 = 1; - if (avctx->profile != FF_PROFILE_HEVC_MAIN && - avctx->profile != FF_PROFILE_HEVC_MAIN_10 && - avctx->profile != FF_PROFILE_HEVC_MAIN_STILL_PICTURE && - avctx->profile != FF_PROFILE_HEVC_REXT) { + if (avctx->profile != AV_PROFILE_HEVC_MAIN && + avctx->profile != AV_PROFILE_HEVC_MAIN_10 && + avctx->profile != AV_PROFILE_HEVC_MAIN_STILL_PICTURE && + avctx->profile != AV_PROFILE_HEVC_REXT) { av_log(avctx, AV_LOG_ERROR, "Unsupported HEVC profile: %d\n", avctx->profile); return AVERROR(ENOTSUP); } @@ -317,11 +318,11 @@ static int nvdec_hevc_decode_init(AVCodecContext *avctx) { return ff_nvdec_decode_init(avctx); } -const AVHWAccel ff_hevc_nvdec_hwaccel = { - .name = "hevc_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_hevc_nvdec_hwaccel = { + .p.name = "hevc_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_hevc_start_frame, .end_frame = ff_nvdec_end_frame, .decode_slice = nvdec_hevc_decode_slice, diff --git a/libavcodec/nvdec_mjpeg.c b/libavcodec/nvdec_mjpeg.c index fce464c1f82..850634bf1a1 100644 --- a/libavcodec/nvdec_mjpeg.c +++ b/libavcodec/nvdec_mjpeg.c @@ -25,6 +25,7 @@ #include "mjpegdec.h" #include "nvdec.h" #include "decode.h" +#include "hwaccel_internal.h" static int nvdec_mjpeg_start_frame(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) { @@ -69,11 +70,11 @@ static int nvdec_mjpeg_frame_params(AVCodecContext *avctx, return ff_nvdec_frame_params(avctx, hw_frames_ctx, 1, 0); } -AVHWAccel ff_mjpeg_nvdec_hwaccel = { - .name = "mjpeg_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MJPEG, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_mjpeg_nvdec_hwaccel = { + .p.name = "mjpeg_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MJPEG, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_mjpeg_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = nvdec_mjpeg_decode_slice, diff --git a/libavcodec/nvdec_mpeg12.c b/libavcodec/nvdec_mpeg12.c index 3b9ff60734f..a4603c7ea78 100644 --- a/libavcodec/nvdec_mpeg12.c +++ b/libavcodec/nvdec_mpeg12.c @@ -23,6 +23,7 @@ #include "config_components.h" #include "avcodec.h" +#include "hwaccel_internal.h" #include "internal.h" #include "mpegvideo.h" #include "nvdec.h" @@ -99,11 +100,11 @@ static int nvdec_mpeg12_frame_params(AVCodecContext *avctx, } #if CONFIG_MPEG2_NVDEC_HWACCEL -const AVHWAccel ff_mpeg2_nvdec_hwaccel = { - .name = "mpeg2_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_mpeg2_nvdec_hwaccel = { + .p.name = "mpeg2_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_mpeg12_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = ff_nvdec_simple_decode_slice, @@ -115,11 +116,11 @@ const AVHWAccel ff_mpeg2_nvdec_hwaccel = { #endif #if CONFIG_MPEG1_NVDEC_HWACCEL -const AVHWAccel ff_mpeg1_nvdec_hwaccel = { - .name = "mpeg1_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG1VIDEO, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_mpeg1_nvdec_hwaccel = { + .p.name = "mpeg1_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG1VIDEO, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_mpeg12_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = ff_nvdec_simple_decode_slice, diff --git a/libavcodec/nvdec_mpeg4.c b/libavcodec/nvdec_mpeg4.c index c193f6b6e47..20a0499437b 100644 --- a/libavcodec/nvdec_mpeg4.c +++ b/libavcodec/nvdec_mpeg4.c @@ -26,6 +26,7 @@ #include "mpeg4videodefs.h" #include "nvdec.h" #include "decode.h" +#include "hwaccel_internal.h" static int nvdec_mpeg4_start_frame(AVCodecContext *avctx, const uint8_t *buffer, uint32_t size) { @@ -109,11 +110,11 @@ static int nvdec_mpeg4_frame_params(AVCodecContext *avctx, return ff_nvdec_frame_params(avctx, hw_frames_ctx, 2, 0); } -const AVHWAccel ff_mpeg4_nvdec_hwaccel = { - .name = "mpeg4_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG4, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_mpeg4_nvdec_hwaccel = { + .p.name = "mpeg4_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG4, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_mpeg4_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = nvdec_mpeg4_decode_slice, diff --git a/libavcodec/nvdec_vc1.c b/libavcodec/nvdec_vc1.c index fae1cb0ab23..5096d784dfe 100644 --- a/libavcodec/nvdec_vc1.c +++ b/libavcodec/nvdec_vc1.c @@ -23,6 +23,7 @@ #include "config_components.h" #include "avcodec.h" +#include "hwaccel_internal.h" #include "internal.h" #include "nvdec.h" #include "decode.h" @@ -113,11 +114,11 @@ static int nvdec_vc1_frame_params(AVCodecContext *avctx, return ff_nvdec_frame_params(avctx, hw_frames_ctx, 2, 0); } -const AVHWAccel ff_vc1_nvdec_hwaccel = { - .name = "vc1_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VC1, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_vc1_nvdec_hwaccel = { + .p.name = "vc1_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VC1, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_vc1_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = ff_nvdec_simple_decode_slice, @@ -128,11 +129,11 @@ const AVHWAccel ff_vc1_nvdec_hwaccel = { }; #if CONFIG_WMV3_NVDEC_HWACCEL -const AVHWAccel ff_wmv3_nvdec_hwaccel = { - .name = "wmv3_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_WMV3, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_wmv3_nvdec_hwaccel = { + .p.name = "wmv3_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_WMV3, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_vc1_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = ff_nvdec_simple_decode_slice, diff --git a/libavcodec/nvdec_vp8.c b/libavcodec/nvdec_vp8.c index f174ca430f5..ff3b3f259c0 100644 --- a/libavcodec/nvdec_vp8.c +++ b/libavcodec/nvdec_vp8.c @@ -23,6 +23,7 @@ #include "avcodec.h" #include "nvdec.h" #include "decode.h" +#include "hwaccel_internal.h" #include "internal.h" #include "vp8.h" @@ -90,11 +91,11 @@ static int nvdec_vp8_frame_params(AVCodecContext *avctx, return ff_nvdec_frame_params(avctx, hw_frames_ctx, 3, 0); } -AVHWAccel ff_vp8_nvdec_hwaccel = { - .name = "vp8_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP8, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_vp8_nvdec_hwaccel = { + .p.name = "vp8_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP8, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_vp8_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = ff_nvdec_simple_decode_slice, diff --git a/libavcodec/nvdec_vp9.c b/libavcodec/nvdec_vp9.c index a76bcf99433..e196391c6d4 100644 --- a/libavcodec/nvdec_vp9.c +++ b/libavcodec/nvdec_vp9.c @@ -25,6 +25,7 @@ #include "avcodec.h" #include "nvdec.h" #include "decode.h" +#include "hwaccel_internal.h" #include "internal.h" #include "vp9shared.h" @@ -169,11 +170,11 @@ static int nvdec_vp9_frame_params(AVCodecContext *avctx, return ff_nvdec_frame_params(avctx, hw_frames_ctx, 8, 0); } -const AVHWAccel ff_vp9_nvdec_hwaccel = { - .name = "vp9_nvdec", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_CUDA, +const FFHWAccel ff_vp9_nvdec_hwaccel = { + .p.name = "vp9_nvdec", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_CUDA, .start_frame = nvdec_vp9_start_frame, .end_frame = ff_nvdec_simple_end_frame, .decode_slice = ff_nvdec_simple_decode_slice, diff --git a/libavcodec/nvenc.c b/libavcodec/nvenc.c index 3c68ed39300..a44beb349f3 100644 --- a/libavcodec/nvenc.c +++ b/libavcodec/nvenc.c @@ -36,6 +36,7 @@ #include "libavutil/pixdesc.h" #include "libavutil/mathematics.h" #include "atsc_a53.h" +#include "codec_desc.h" #include "encode.h" #include "internal.h" #include "packet_internal.h" @@ -91,10 +92,18 @@ const AVCodecHWConfigInternal *const ff_nvenc_hw_configs[] = { pix_fmt == AV_PIX_FMT_X2BGR10 || \ pix_fmt == AV_PIX_FMT_GBRP16) +#define IS_RGB(pix_fmt) (pix_fmt == AV_PIX_FMT_0RGB32 || \ + pix_fmt == AV_PIX_FMT_RGB32 || \ + pix_fmt == AV_PIX_FMT_0BGR32 || \ + pix_fmt == AV_PIX_FMT_BGR32 || \ + pix_fmt == AV_PIX_FMT_X2RGB10 || \ + pix_fmt == AV_PIX_FMT_X2BGR10) + #define IS_YUV444(pix_fmt) (pix_fmt == AV_PIX_FMT_YUV444P || \ pix_fmt == AV_PIX_FMT_YUV444P16 || \ pix_fmt == AV_PIX_FMT_GBRP || \ - pix_fmt == AV_PIX_FMT_GBRP16) + pix_fmt == AV_PIX_FMT_GBRP16 || \ + (ctx->rgb_mode == NVENC_RGB_MODE_444 && IS_RGB(pix_fmt))) #define IS_GBRP(pix_fmt) (pix_fmt == AV_PIX_FMT_GBRP || \ pix_fmt == AV_PIX_FMT_GBRP16) @@ -973,7 +982,7 @@ static av_cold int nvenc_recalc_surfaces(AVCodecContext *avctx) // Output in the worst case will only start when the surface buffer is completely full. // Hence we need to keep at least the max amount of surfaces plus the max reorder delay around. - ctx->frame_data_array_nb = ctx->nb_surfaces + ctx->encode_config.frameIntervalP - 1; + ctx->frame_data_array_nb = FFMAX(ctx->nb_surfaces, ctx->nb_surfaces + ctx->encode_config.frameIntervalP - 1); return 0; } @@ -1166,13 +1175,19 @@ static av_cold int nvenc_setup_h264_config(AVCodecContext *avctx) || vui->videoFormat != 5 || vui->videoFullRangeFlag != 0); - h264->sliceMode = 3; - h264->sliceModeData = avctx->slices > 0 ? avctx->slices : 1; + if (ctx->max_slice_size > 0) { + h264->sliceMode = 1; + h264->sliceModeData = ctx->max_slice_size; + } else { + h264->sliceMode = 3; + h264->sliceModeData = avctx->slices > 0 ? avctx->slices : 1; + } if (ctx->intra_refresh) { h264->enableIntraRefresh = 1; - h264->intraRefreshPeriod = avctx->gop_size; - h264->intraRefreshCnt = avctx->gop_size - 1; + h264->intraRefreshPeriod = cc->gopLength; + h264->intraRefreshCnt = cc->gopLength - 1; + cc->gopLength = NVENC_INFINITE_GOPLENGTH; #ifdef NVENC_HAVE_SINGLE_SLICE_INTRA_REFRESH h264->singleSliceIntraRefresh = ctx->single_slice_intra_refresh; #endif @@ -1190,11 +1205,7 @@ static av_cold int nvenc_setup_h264_config(AVCodecContext *avctx) h264->maxNumRefFrames = ctx->dpb_size; } - if (ctx->intra_refresh) { - h264->idrPeriod = NVENC_INFINITE_GOPLENGTH; - } else if (avctx->gop_size >= 0) { - h264->idrPeriod = avctx->gop_size; - } + h264->idrPeriod = cc->gopLength; if (IS_CBR(cc->rcParams.rateControlMode)) { h264->outputBufferingPeriodSEI = 1; @@ -1217,19 +1228,19 @@ static av_cold int nvenc_setup_h264_config(AVCodecContext *avctx) switch(ctx->profile) { case NV_ENC_H264_PROFILE_BASELINE: cc->profileGUID = NV_ENC_H264_PROFILE_BASELINE_GUID; - avctx->profile = FF_PROFILE_H264_BASELINE; + avctx->profile = AV_PROFILE_H264_BASELINE; break; case NV_ENC_H264_PROFILE_MAIN: cc->profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID; - avctx->profile = FF_PROFILE_H264_MAIN; + avctx->profile = AV_PROFILE_H264_MAIN; break; case NV_ENC_H264_PROFILE_HIGH: cc->profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID; - avctx->profile = FF_PROFILE_H264_HIGH; + avctx->profile = AV_PROFILE_H264_HIGH; break; case NV_ENC_H264_PROFILE_HIGH_444P: cc->profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID; - avctx->profile = FF_PROFILE_H264_HIGH_444_PREDICTIVE; + avctx->profile = AV_PROFILE_H264_HIGH_444_PREDICTIVE; break; } } @@ -1237,13 +1248,18 @@ static av_cold int nvenc_setup_h264_config(AVCodecContext *avctx) // force setting profile as high444p if input is AV_PIX_FMT_YUV444P if (IS_YUV444(ctx->data_pix_fmt)) { cc->profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID; - avctx->profile = FF_PROFILE_H264_HIGH_444_PREDICTIVE; + avctx->profile = AV_PROFILE_H264_HIGH_444_PREDICTIVE; } - h264->chromaFormatIDC = avctx->profile == FF_PROFILE_H264_HIGH_444_PREDICTIVE ? 3 : 1; + h264->chromaFormatIDC = avctx->profile == AV_PROFILE_H264_HIGH_444_PREDICTIVE ? 3 : 1; h264->level = ctx->level; +#ifdef NVENC_HAVE_NEW_BIT_DEPTH_API + h264->inputBitDepth = h264->outputBitDepth = + IS_10BIT(ctx->data_pix_fmt) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8; +#endif + if (ctx->coder >= 0) h264->entropyCodingMode = ctx->coder; @@ -1290,13 +1306,19 @@ static av_cold int nvenc_setup_hevc_config(AVCodecContext *avctx) || vui->videoFormat != 5 || vui->videoFullRangeFlag != 0); - hevc->sliceMode = 3; - hevc->sliceModeData = avctx->slices > 0 ? avctx->slices : 1; + if (ctx->max_slice_size > 0) { + hevc->sliceMode = 1; + hevc->sliceModeData = ctx->max_slice_size; + } else { + hevc->sliceMode = 3; + hevc->sliceModeData = avctx->slices > 0 ? avctx->slices : 1; + } if (ctx->intra_refresh) { hevc->enableIntraRefresh = 1; - hevc->intraRefreshPeriod = avctx->gop_size; - hevc->intraRefreshCnt = avctx->gop_size - 1; + hevc->intraRefreshPeriod = cc->gopLength; + hevc->intraRefreshCnt = cc->gopLength - 1; + cc->gopLength = NVENC_INFINITE_GOPLENGTH; #ifdef NVENC_HAVE_SINGLE_SLICE_INTRA_REFRESH hevc->singleSliceIntraRefresh = ctx->single_slice_intra_refresh; #endif @@ -1316,11 +1338,7 @@ static av_cold int nvenc_setup_hevc_config(AVCodecContext *avctx) hevc->maxNumRefFramesInDPB = ctx->dpb_size; } - if (ctx->intra_refresh) { - hevc->idrPeriod = NVENC_INFINITE_GOPLENGTH; - } else if (avctx->gop_size >= 0) { - hevc->idrPeriod = avctx->gop_size; - } + hevc->idrPeriod = cc->gopLength; if (IS_CBR(cc->rcParams.rateControlMode)) { hevc->outputBufferingPeriodSEI = 1; @@ -1331,33 +1349,38 @@ static av_cold int nvenc_setup_hevc_config(AVCodecContext *avctx) switch (ctx->profile) { case NV_ENC_HEVC_PROFILE_MAIN: cc->profileGUID = NV_ENC_HEVC_PROFILE_MAIN_GUID; - avctx->profile = FF_PROFILE_HEVC_MAIN; + avctx->profile = AV_PROFILE_HEVC_MAIN; break; case NV_ENC_HEVC_PROFILE_MAIN_10: cc->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID; - avctx->profile = FF_PROFILE_HEVC_MAIN_10; + avctx->profile = AV_PROFILE_HEVC_MAIN_10; break; case NV_ENC_HEVC_PROFILE_REXT: cc->profileGUID = NV_ENC_HEVC_PROFILE_FREXT_GUID; - avctx->profile = FF_PROFILE_HEVC_REXT; + avctx->profile = AV_PROFILE_HEVC_REXT; break; } // force setting profile as main10 if input is 10 bit if (IS_10BIT(ctx->data_pix_fmt)) { cc->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID; - avctx->profile = FF_PROFILE_HEVC_MAIN_10; + avctx->profile = AV_PROFILE_HEVC_MAIN_10; } // force setting profile as rext if input is yuv444 if (IS_YUV444(ctx->data_pix_fmt)) { cc->profileGUID = NV_ENC_HEVC_PROFILE_FREXT_GUID; - avctx->profile = FF_PROFILE_HEVC_REXT; + avctx->profile = AV_PROFILE_HEVC_REXT; } hevc->chromaFormatIDC = IS_YUV444(ctx->data_pix_fmt) ? 3 : 1; +#ifdef NVENC_HAVE_NEW_BIT_DEPTH_API + hevc->inputBitDepth = hevc->outputBitDepth = + IS_10BIT(ctx->data_pix_fmt) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8; +#else hevc->pixelBitDepthMinus8 = IS_10BIT(ctx->data_pix_fmt) ? 2 : 0; +#endif hevc->level = ctx->level; @@ -1403,7 +1426,7 @@ static av_cold int nvenc_setup_av1_config(AVCodecContext *avctx) return AVERROR(ENOTSUP); } else { cc->profileGUID = NV_ENC_AV1_PROFILE_MAIN_GUID; - avctx->profile = FF_PROFILE_AV1_MAIN; + avctx->profile = AV_PROFILE_AV1_MAIN; } if (ctx->dpb_size >= 0) { @@ -1413,14 +1436,13 @@ static av_cold int nvenc_setup_av1_config(AVCodecContext *avctx) if (ctx->intra_refresh) { av1->enableIntraRefresh = 1; - av1->intraRefreshPeriod = avctx->gop_size; - av1->intraRefreshCnt = avctx->gop_size - 1; - - av1->idrPeriod = NVENC_INFINITE_GOPLENGTH; - } else if (avctx->gop_size >= 0) { - av1->idrPeriod = avctx->gop_size; + av1->intraRefreshPeriod = cc->gopLength; + av1->intraRefreshCnt = cc->gopLength - 1; + cc->gopLength = NVENC_INFINITE_GOPLENGTH; } + av1->idrPeriod = cc->gopLength; + if (IS_CBR(cc->rcParams.rateControlMode)) { av1->enableBitstreamPadding = 1; } @@ -1443,8 +1465,13 @@ static av_cold int nvenc_setup_av1_config(AVCodecContext *avctx) av1->chromaFormatIDC = IS_YUV444(ctx->data_pix_fmt) ? 3 : 1; +#ifdef NVENC_HAVE_NEW_BIT_DEPTH_API + av1->inputBitDepth = IS_10BIT(ctx->data_pix_fmt) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8; + av1->outputBitDepth = (IS_10BIT(ctx->data_pix_fmt) || ctx->highbitdepth) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8; +#else av1->inputPixelBitDepthMinus8 = IS_10BIT(ctx->data_pix_fmt) ? 2 : 0; av1->pixelBitDepthMinus8 = (IS_10BIT(ctx->data_pix_fmt) || ctx->highbitdepth) ? 2 : 0; +#endif if (ctx->b_ref_mode >= 0) av1->useBFramesAsRef = ctx->b_ref_mode; @@ -1564,7 +1591,13 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx) ctx->init_encode_params.frameRateDen = avctx->framerate.den; } else { ctx->init_encode_params.frameRateNum = avctx->time_base.den; - ctx->init_encode_params.frameRateDen = avctx->time_base.num * avctx->ticks_per_frame; +FF_DISABLE_DEPRECATION_WARNINGS + ctx->init_encode_params.frameRateDen = avctx->time_base.num +#if FF_API_TICKS_PER_FRAME + * avctx->ticks_per_frame +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } ctx->init_encode_params.enableEncodeAsync = 0; @@ -1597,24 +1630,22 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx) } if (avctx->gop_size > 0) { - if (avctx->max_b_frames >= 0) { - /* 0 is intra-only, 1 is I/P only, 2 is one B-Frame, 3 two B-frames, and so on. */ - ctx->encode_config.frameIntervalP = avctx->max_b_frames + 1; - } - + // only overwrite preset if a GOP size was selected as input ctx->encode_config.gopLength = avctx->gop_size; } else if (avctx->gop_size == 0) { ctx->encode_config.frameIntervalP = 0; ctx->encode_config.gopLength = 1; } + if (avctx->max_b_frames >= 0 && ctx->encode_config.gopLength > 1) { + /* 0 is intra-only, 1 is I/P only, 2 is one B-Frame, 3 two B-frames, and so on. */ + ctx->encode_config.frameIntervalP = avctx->max_b_frames + 1; + } + /* force to enable intra refresh */ if(ctx->single_slice_intra_refresh) ctx->intra_refresh = 1; - if (ctx->intra_refresh) - ctx->encode_config.gopLength = NVENC_INFINITE_GOPLENGTH; - nvenc_recalc_surfaces(avctx); nvenc_setup_rate_control(avctx); @@ -1659,7 +1690,7 @@ static av_cold int nvenc_setup_encoder(AVCodecContext *avctx) if (ctx->encode_config.rcParams.averageBitRate > 0) avctx->bit_rate = ctx->encode_config.rcParams.averageBitRate; - cpb_props = ff_add_cpb_side_data(avctx); + cpb_props = ff_encode_add_cpb_side_data(avctx); if (!cpb_props) return AVERROR(ENOMEM); cpb_props->max_bitrate = ctx->encode_config.rcParams.maxBitRate; @@ -1673,15 +1704,15 @@ static NV_ENC_BUFFER_FORMAT nvenc_map_buffer_format(enum AVPixelFormat pix_fmt) { switch (pix_fmt) { case AV_PIX_FMT_YUV420P: - return NV_ENC_BUFFER_FORMAT_YV12_PL; + return NV_ENC_BUFFER_FORMAT_YV12; case AV_PIX_FMT_NV12: - return NV_ENC_BUFFER_FORMAT_NV12_PL; + return NV_ENC_BUFFER_FORMAT_NV12; case AV_PIX_FMT_P010: case AV_PIX_FMT_P016: return NV_ENC_BUFFER_FORMAT_YUV420_10BIT; case AV_PIX_FMT_GBRP: case AV_PIX_FMT_YUV444P: - return NV_ENC_BUFFER_FORMAT_YUV444_PL; + return NV_ENC_BUFFER_FORMAT_YUV444; case AV_PIX_FMT_GBRP16: case AV_PIX_FMT_YUV444P16: return NV_ENC_BUFFER_FORMAT_YUV444_10BIT; @@ -1860,7 +1891,7 @@ av_cold int ff_nvenc_encode_close(AVCodecContext *avctx) av_fifo_freep2(&ctx->unused_surface_queue); if (ctx->frame_data_array) { - for (i = 0; i < ctx->nb_surfaces; i++) + for (i = 0; i < ctx->frame_data_array_nb; i++) av_buffer_unref(&ctx->frame_data_array[i].frame_opaque_ref); av_freep(&ctx->frame_data_array); } @@ -1943,6 +1974,11 @@ av_cold int ff_nvenc_encode_init(AVCodecContext *avctx) ctx->data_pix_fmt = avctx->pix_fmt; } + if (ctx->rgb_mode == NVENC_RGB_MODE_DISABLED && IS_RGB(ctx->data_pix_fmt)) { + av_log(avctx, AV_LOG_ERROR, "Packed RGB input, but RGB support is disabled.\n"); + return AVERROR(EINVAL); + } + ctx->frame = av_frame_alloc(); if (!ctx->frame) return AVERROR(ENOMEM); @@ -2001,9 +2037,9 @@ static int nvenc_copy_frame(AVCodecContext *avctx, NvencSurface *nv_surface, if (frame->format == AV_PIX_FMT_YUV420P) FFSWAP(uint8_t*, dst_data[1], dst_data[2]); - av_image_copy(dst_data, dst_linesize, - (const uint8_t**)frame->data, frame->linesize, frame->format, - avctx->width, avctx->height); + av_image_copy2(dst_data, dst_linesize, + frame->data, frame->linesize, frame->format, + avctx->width, avctx->height); return 0; } @@ -2227,8 +2263,13 @@ static int nvenc_set_timestamp(AVCodecContext *avctx, pkt->pts = params->outputTimeStamp; if (avctx->codec_descriptor->props & AV_CODEC_PROP_REORDER) { +FF_DISABLE_DEPRECATION_WARNINGS pkt->dts = timestamp_queue_dequeue(ctx->timestamp_list) - - FFMAX(ctx->encode_config.frameIntervalP - 1, 0) * FFMAX(avctx->ticks_per_frame, 1); +#if FF_API_TICKS_PER_FRAME + FFMAX(avctx->ticks_per_frame, 1) * +#endif + FFMAX(ctx->encode_config.frameIntervalP - 1, 0); +FF_ENABLE_DEPRECATION_WARNINGS } else { pkt->dts = pkt->pts; } @@ -2247,7 +2288,7 @@ static int nvenc_store_frame_data(AVCodecContext *avctx, NV_ENC_PIC_PARAMS *pic_ // in case the encoder got reconfigured, there might be leftovers av_buffer_unref(&frame_data->frame_opaque_ref); - if (frame && frame->opaque_ref && avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + if (frame->opaque_ref && avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { frame_data->frame_opaque_ref = av_buffer_ref(frame->opaque_ref); if (!frame_data->frame_opaque_ref) return AVERROR(ENOMEM); @@ -2256,12 +2297,6 @@ static int nvenc_store_frame_data(AVCodecContext *avctx, NV_ENC_PIC_PARAMS *pic_ frame_data->duration = frame->duration; frame_data->frame_opaque = frame->opaque; -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - frame_data->reordered_opaque = frame->reordered_opaque; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - ctx->frame_data_array_pos = (ctx->frame_data_array_pos + 1) % ctx->frame_data_array_nb; pic_params->inputDuration = idx; @@ -2278,12 +2313,6 @@ static int nvenc_retrieve_frame_data(AVCodecContext *avctx, NV_ENC_LOCK_BITSTREA pkt->duration = frame_data->duration; -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - avctx->reordered_opaque = frame_data->reordered_opaque; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { pkt->opaque = frame_data->frame_opaque; pkt->opaque_ref = frame_data->frame_opaque_ref; @@ -2656,7 +2685,7 @@ static int nvenc_send_frame(AVCodecContext *avctx, const AVFrame *frame) pic_params.outputBitstream = in_surf->output_surface; if (avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT) { - if (frame->top_field_first) + if (frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FIELD_TOP_BOTTOM; else pic_params.pictureStruct = NV_ENC_PIC_STRUCT_FIELD_BOTTOM_TOP; diff --git a/libavcodec/nvenc.h b/libavcodec/nvenc.h index 3a4b456a41d..d99d8a0d760 100644 --- a/libavcodec/nvenc.h +++ b/libavcodec/nvenc.h @@ -83,6 +83,11 @@ typedef void ID3D11Device; #define NVENC_NO_DEPRECATED_RC #endif +// SDK 12.2 compile time feature checks +#if NVENCAPI_CHECK_VERSION(12, 2) +#define NVENC_HAVE_NEW_BIT_DEPTH_API +#endif + typedef struct NvencSurface { NV_ENC_INPUT_PTR input_surface; @@ -100,10 +105,6 @@ typedef struct NvencFrameData { int64_t duration; -#if FF_API_REORDERED_OPAQUE - int64_t reordered_opaque; -#endif - void *frame_opaque; AVBufferRef *frame_opaque_ref; } NvencFrameData; @@ -168,6 +169,12 @@ enum { ANY_DEVICE, }; +enum { + NVENC_RGB_MODE_DISABLED, + NVENC_RGB_MODE_420, + NVENC_RGB_MODE_444, +}; + typedef struct NvencContext { AVClass *avclass; @@ -262,6 +269,8 @@ typedef struct NvencContext int udu_sei; int timing_info; int highbitdepth; + int max_slice_size; + int rgb_mode; } NvencContext; int ff_nvenc_encode_init(AVCodecContext *avctx); diff --git a/libavcodec/nvenc_av1.c b/libavcodec/nvenc_av1.c index 2b349c7b61f..c46cee9fac6 100644 --- a/libavcodec/nvenc_av1.c +++ b/libavcodec/nvenc_av1.c @@ -26,76 +26,81 @@ #define OFFSET(x) offsetof(NvencContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "preset", "Set the encoding preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = PRESET_P4 }, PRESET_DEFAULT, PRESET_P7, VE, "preset" }, - { "default", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_DEFAULT }, 0, 0, VE, "preset" }, - { "slow", "hq 2 passes", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_SLOW }, 0, 0, VE, "preset" }, - { "medium", "hq 1 pass", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_MEDIUM }, 0, 0, VE, "preset" }, - { "fast", "hp 1 pass", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_FAST }, 0, 0, VE, "preset" }, - { "p1", "fastest (lowest quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P1 }, 0, 0, VE, "preset" }, - { "p2", "faster (lower quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P2 }, 0, 0, VE, "preset" }, - { "p3", "fast (low quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P3 }, 0, 0, VE, "preset" }, - { "p4", "medium (default)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P4 }, 0, 0, VE, "preset" }, - { "p5", "slow (good quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P5 }, 0, 0, VE, "preset" }, - { "p6", "slower (better quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P6 }, 0, 0, VE, "preset" }, - { "p7", "slowest (best quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P7 }, 0, 0, VE, "preset" }, - { "tune", "Set the encoding tuning info", OFFSET(tuning_info), AV_OPT_TYPE_INT, { .i64 = NV_ENC_TUNING_INFO_HIGH_QUALITY }, NV_ENC_TUNING_INFO_HIGH_QUALITY, NV_ENC_TUNING_INFO_LOSSLESS, VE, "tune" }, - { "hq", "High quality", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_HIGH_QUALITY }, 0, 0, VE, "tune" }, - { "ll", "Low latency", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_LOW_LATENCY }, 0, 0, VE, "tune" }, - { "ull", "Ultra low latency", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY }, 0, 0, VE, "tune" }, - { "lossless", "Lossless", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_LOSSLESS }, 0, 0, VE, "tune" }, - { "level", "Set the encoding level restriction", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = NV_ENC_LEVEL_AV1_AUTOSELECT }, NV_ENC_LEVEL_AV1_2, NV_ENC_LEVEL_AV1_AUTOSELECT, VE, "level" }, - { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_AUTOSELECT }, 0, 0, VE, "level" }, - { "2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_2 }, 0, 0, VE, "level" }, - { "2.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_2 }, 0, 0, VE, "level" }, - { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_21 }, 0, 0, VE, "level" }, - { "2.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_22 }, 0, 0, VE, "level" }, - { "2.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_23 }, 0, 0, VE, "level" }, - { "3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_3 }, 0, 0, VE, "level" }, - { "3.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_3 }, 0, 0, VE, "level" }, - { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_31 }, 0, 0, VE, "level" }, - { "3.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_32 }, 0, 0, VE, "level" }, - { "3.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_33 }, 0, 0, VE, "level" }, - { "4", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_4 }, 0, 0, VE, "level" }, - { "4.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_4 }, 0, 0, VE, "level" }, - { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_41 }, 0, 0, VE, "level" }, - { "4.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_42 }, 0, 0, VE, "level" }, - { "4.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_43 }, 0, 0, VE, "level" }, - { "5", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_5 }, 0, 0, VE, "level" }, - { "5.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_5 }, 0, 0, VE, "level" }, - { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_51 }, 0, 0, VE, "level" }, - { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_52 }, 0, 0, VE, "level" }, - { "5.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_53 }, 0, 0, VE, "level" }, - { "6", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_6 }, 0, 0, VE, "level" }, - { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_6 }, 0, 0, VE, "level" }, - { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_61 }, 0, 0, VE, "level" }, - { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_62 }, 0, 0, VE, "level" }, - { "6.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_63 }, 0, 0, VE, "level" }, - { "7", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_7 }, 0, 0, VE, "level" }, - { "7.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_7 }, 0, 0, VE, "level" }, - { "7.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_71 }, 0, 0, VE, "level" }, - { "7.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_72 }, 0, 0, VE, "level" }, - { "7.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_73 }, 0, 0, VE, "level" }, - { "tier", "Set the encoding tier", OFFSET(tier), AV_OPT_TYPE_INT, { .i64 = NV_ENC_TIER_AV1_0 }, NV_ENC_TIER_AV1_0, NV_ENC_TIER_AV1_1, VE, "tier"}, - { "0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TIER_AV1_0 }, 0, 0, VE, "tier" }, - { "1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TIER_AV1_1 }, 0, 0, VE, "tier" }, - { "rc", "Override the preset rate-control", OFFSET(rc), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, "rc" }, - { "constqp", "Constant QP mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CONSTQP }, 0, 0, VE, "rc" }, - { "vbr", "Variable bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_VBR }, 0, 0, VE, "rc" }, - { "cbr", "Constant bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CBR }, 0, 0, VE, "rc" }, - { "multipass", "Set the multipass encoding", OFFSET(multipass), AV_OPT_TYPE_INT, { .i64 = NV_ENC_MULTI_PASS_DISABLED }, NV_ENC_MULTI_PASS_DISABLED, NV_ENC_TWO_PASS_FULL_RESOLUTION, VE, "multipass" }, - { "disabled", "Single Pass", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_MULTI_PASS_DISABLED }, 0, 0, VE, "multipass" }, + { "preset", "Set the encoding preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = PRESET_P4 }, PRESET_DEFAULT, PRESET_P7, VE, .unit = "preset" }, + { "default", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_DEFAULT }, 0, 0, VE, .unit = "preset" }, + { "slow", "hq 2 passes", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_SLOW }, 0, 0, VE, .unit = "preset" }, + { "medium", "hq 1 pass", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_MEDIUM }, 0, 0, VE, .unit = "preset" }, + { "fast", "hp 1 pass", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_FAST }, 0, 0, VE, .unit = "preset" }, + { "p1", "fastest (lowest quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P1 }, 0, 0, VE, .unit = "preset" }, + { "p2", "faster (lower quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P2 }, 0, 0, VE, .unit = "preset" }, + { "p3", "fast (low quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P3 }, 0, 0, VE, .unit = "preset" }, + { "p4", "medium (default)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P4 }, 0, 0, VE, .unit = "preset" }, + { "p5", "slow (good quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P5 }, 0, 0, VE, .unit = "preset" }, + { "p6", "slower (better quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P6 }, 0, 0, VE, .unit = "preset" }, + { "p7", "slowest (best quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P7 }, 0, 0, VE, .unit = "preset" }, + { "tune", "Set the encoding tuning info", OFFSET(tuning_info), AV_OPT_TYPE_INT, { .i64 = NV_ENC_TUNING_INFO_HIGH_QUALITY }, NV_ENC_TUNING_INFO_HIGH_QUALITY, NV_ENC_TUNING_INFO_LOSSLESS, VE, .unit = "tune" }, + { "hq", "High quality", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_HIGH_QUALITY }, 0, 0, VE, .unit = "tune" }, + { "ll", "Low latency", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_LOW_LATENCY }, 0, 0, VE, .unit = "tune" }, + { "ull", "Ultra low latency", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY }, 0, 0, VE, .unit = "tune" }, + { "lossless", "Lossless", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_LOSSLESS }, 0, 0, VE, .unit = "tune" }, + { "level", "Set the encoding level restriction", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = NV_ENC_LEVEL_AV1_AUTOSELECT }, NV_ENC_LEVEL_AV1_2, NV_ENC_LEVEL_AV1_AUTOSELECT, VE, .unit = "level" }, + { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_AUTOSELECT }, 0, 0, VE, .unit = "level" }, + { "2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_2 }, 0, 0, VE, .unit = "level" }, + { "2.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_2 }, 0, 0, VE, .unit = "level" }, + { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_21 }, 0, 0, VE, .unit = "level" }, + { "2.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_22 }, 0, 0, VE, .unit = "level" }, + { "2.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_23 }, 0, 0, VE, .unit = "level" }, + { "3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_3 }, 0, 0, VE, .unit = "level" }, + { "3.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_3 }, 0, 0, VE, .unit = "level" }, + { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_31 }, 0, 0, VE, .unit = "level" }, + { "3.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_32 }, 0, 0, VE, .unit = "level" }, + { "3.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_33 }, 0, 0, VE, .unit = "level" }, + { "4", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_4 }, 0, 0, VE, .unit = "level" }, + { "4.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_4 }, 0, 0, VE, .unit = "level" }, + { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_41 }, 0, 0, VE, .unit = "level" }, + { "4.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_42 }, 0, 0, VE, .unit = "level" }, + { "4.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_43 }, 0, 0, VE, .unit = "level" }, + { "5", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_5 }, 0, 0, VE, .unit = "level" }, + { "5.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_5 }, 0, 0, VE, .unit = "level" }, + { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_51 }, 0, 0, VE, .unit = "level" }, + { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_52 }, 0, 0, VE, .unit = "level" }, + { "5.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_53 }, 0, 0, VE, .unit = "level" }, + { "6", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_6 }, 0, 0, VE, .unit = "level" }, + { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_6 }, 0, 0, VE, .unit = "level" }, + { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_61 }, 0, 0, VE, .unit = "level" }, + { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_62 }, 0, 0, VE, .unit = "level" }, + { "6.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_63 }, 0, 0, VE, .unit = "level" }, + { "7", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_7 }, 0, 0, VE, .unit = "level" }, + { "7.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_7 }, 0, 0, VE, .unit = "level" }, + { "7.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_71 }, 0, 0, VE, .unit = "level" }, + { "7.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_72 }, 0, 0, VE, .unit = "level" }, + { "7.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AV1_73 }, 0, 0, VE, .unit = "level" }, + { "tier", "Set the encoding tier", OFFSET(tier), AV_OPT_TYPE_INT, { .i64 = NV_ENC_TIER_AV1_0 }, NV_ENC_TIER_AV1_0, NV_ENC_TIER_AV1_1, VE, .unit = "tier"}, + { "0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TIER_AV1_0 }, 0, 0, VE, .unit = "tier" }, + { "1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TIER_AV1_1 }, 0, 0, VE, .unit = "tier" }, + { "rc", "Override the preset rate-control", OFFSET(rc), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, .unit = "rc" }, + { "constqp", "Constant QP mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CONSTQP }, 0, 0, VE, .unit = "rc" }, + { "vbr", "Variable bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_VBR }, 0, 0, VE, .unit = "rc" }, + { "cbr", "Constant bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CBR }, 0, 0, VE, .unit = "rc" }, + { "multipass", "Set the multipass encoding", OFFSET(multipass), AV_OPT_TYPE_INT, { .i64 = NV_ENC_MULTI_PASS_DISABLED }, NV_ENC_MULTI_PASS_DISABLED, NV_ENC_TWO_PASS_FULL_RESOLUTION, VE, .unit = "multipass" }, + { "disabled", "Single Pass", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_MULTI_PASS_DISABLED }, 0, 0, VE, .unit = "multipass" }, { "qres", "Two Pass encoding is enabled where first Pass is quarter resolution", - 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TWO_PASS_QUARTER_RESOLUTION }, 0, 0, VE, "multipass" }, + 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TWO_PASS_QUARTER_RESOLUTION }, 0, 0, VE, .unit = "multipass" }, { "fullres", "Two Pass encoding is enabled where first Pass is full resolution", - 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TWO_PASS_FULL_RESOLUTION }, 0, 0, VE, "multipass" }, + 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TWO_PASS_FULL_RESOLUTION }, 0, 0, VE, .unit = "multipass" }, { "highbitdepth", "Enable 10 bit encode for 8 bit input",OFFSET(highbitdepth),AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "tile-rows", "Number of tile rows to encode with", OFFSET(tile_rows), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, NV_MAX_TILE_ROWS_AV1, VE }, { "tile-columns", "Number of tile columns to encode with", OFFSET(tile_cols), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, NV_MAX_TILE_COLS_AV1, VE }, { "surfaces", "Number of concurrent surfaces", OFFSET(nb_surfaces), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MAX_REGISTERED_FRAMES, VE }, { "gpu", "Selects which NVENC capable GPU to use. First GPU is 0, second is 1, and so on.", - OFFSET(device), AV_OPT_TYPE_INT, { .i64 = ANY_DEVICE }, -2, INT_MAX, VE, "gpu" }, - { "any", "Pick the first device available", 0, AV_OPT_TYPE_CONST, { .i64 = ANY_DEVICE }, 0, 0, VE, "gpu" }, - { "list", "List the available devices", 0, AV_OPT_TYPE_CONST, { .i64 = LIST_DEVICES }, 0, 0, VE, "gpu" }, + OFFSET(device), AV_OPT_TYPE_INT, { .i64 = ANY_DEVICE }, -2, INT_MAX, VE, .unit = "gpu" }, + { "any", "Pick the first device available", 0, AV_OPT_TYPE_CONST, { .i64 = ANY_DEVICE }, 0, 0, VE, .unit = "gpu" }, + { "list", "List the available devices", 0, AV_OPT_TYPE_CONST, { .i64 = LIST_DEVICES }, 0, 0, VE, .unit = "gpu" }, + { "rgb_mode", "Configure how nvenc handles packed RGB input.", + OFFSET(rgb_mode), AV_OPT_TYPE_INT, { .i64 = NVENC_RGB_MODE_420 }, 0, INT_MAX, VE, .unit = "rgb_mode" }, + { "yuv420", "Convert to yuv420", 0, AV_OPT_TYPE_CONST, { .i64 = NVENC_RGB_MODE_420 }, 0, 0, VE, .unit = "rgb_mode" }, + { "yuv444", "Convert to yuv444", 0, AV_OPT_TYPE_CONST, { .i64 = NVENC_RGB_MODE_444 }, 0, 0, VE, .unit = "rgb_mode" }, + { "disabled", "Disables support, throws an error.", 0, AV_OPT_TYPE_CONST, { .i64 = NVENC_RGB_MODE_DISABLED }, 0, 0, VE, .unit = "rgb_mode" }, { "delay", "Delay frame output by the given amount of frames", OFFSET(async_depth), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 0, INT_MAX, VE }, { "rc-lookahead", "Number of frames to look ahead for rate-control", @@ -128,10 +133,10 @@ static const AVOption options[] = { { "aq-strength", "When Spatial AQ is enabled, this field is used to specify AQ strength. AQ strength scale is from 1 (low) - 15 (aggressive)", OFFSET(aq_strength), AV_OPT_TYPE_INT, { .i64 = 8 }, 1, 15, VE }, { "weighted_pred","Enable weighted prediction", OFFSET(weighted_pred),AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, - { "b_ref_mode", "Use B frames as references", OFFSET(b_ref_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, NV_ENC_BFRAME_REF_MODE_MIDDLE, VE, "b_ref_mode" }, - { "disabled", "B frames will not be used for reference", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_DISABLED }, 0, 0, VE, "b_ref_mode" }, - { "each", "Each B frame will be used for reference", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_EACH }, 0, 0, VE, "b_ref_mode" }, - { "middle", "Only (number of B frames)/2 will be used for reference", 0,AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_MIDDLE }, 0, 0, VE, "b_ref_mode" }, + { "b_ref_mode", "Use B frames as references", OFFSET(b_ref_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, NV_ENC_BFRAME_REF_MODE_MIDDLE, VE, .unit = "b_ref_mode" }, + { "disabled", "B frames will not be used for reference", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_DISABLED }, 0, 0, VE, .unit = "b_ref_mode" }, + { "each", "Each B frame will be used for reference", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_EACH }, 0, 0, VE, .unit = "b_ref_mode" }, + { "middle", "Only (number of B frames)/2 will be used for reference", 0,AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_MIDDLE }, 0, 0, VE, .unit = "b_ref_mode" }, { "dpb_size", "Specifies the DPB size used for encoding (0 means automatic)", OFFSET(dpb_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, { "ldkfs", "Low delay key frame scale; Specifies the Scene Change frame size increase allowed in case of single frame VBV and CBR", @@ -154,7 +159,7 @@ static const FFCodecDefault defaults[] = { { "qdiff", "-1" }, { "qblur", "-1" }, { "qcomp", "-1" }, - { "g", "250" }, + { "g", "-1" }, { "bf", "-1" }, { "refs", "0" }, { NULL }, diff --git a/libavcodec/nvenc_h264.c b/libavcodec/nvenc_h264.c index 698615855bf..a555252a536 100644 --- a/libavcodec/nvenc_h264.c +++ b/libavcodec/nvenc_h264.c @@ -27,99 +27,99 @@ #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { #ifdef NVENC_HAVE_NEW_PRESETS - { "preset", "Set the encoding preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = PRESET_P4 }, PRESET_DEFAULT, PRESET_P7, VE, "preset" }, + { "preset", "Set the encoding preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = PRESET_P4 }, PRESET_DEFAULT, PRESET_P7, VE, .unit = "preset" }, #else - { "preset", "Set the encoding preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = PRESET_MEDIUM }, PRESET_DEFAULT, PRESET_LOSSLESS_HP, VE, "preset" }, + { "preset", "Set the encoding preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = PRESET_MEDIUM }, PRESET_DEFAULT, PRESET_LOSSLESS_HP, VE, .unit = "preset" }, #endif - { "default", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_DEFAULT }, 0, 0, VE, "preset" }, - { "slow", "hq 2 passes", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_SLOW }, 0, 0, VE, "preset" }, - { "medium", "hq 1 pass", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_MEDIUM }, 0, 0, VE, "preset" }, - { "fast", "hp 1 pass", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_FAST }, 0, 0, VE, "preset" }, - { "hp", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_HP }, 0, 0, VE, "preset" }, - { "hq", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_HQ }, 0, 0, VE, "preset" }, - { "bd", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_BD }, 0, 0, VE, "preset" }, - { "ll", "low latency", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOW_LATENCY_DEFAULT }, 0, 0, VE, "preset" }, - { "llhq", "low latency hq", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOW_LATENCY_HQ }, 0, 0, VE, "preset" }, - { "llhp", "low latency hp", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOW_LATENCY_HP }, 0, 0, VE, "preset" }, - { "lossless", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOSSLESS_DEFAULT }, 0, 0, VE, "preset" }, - { "losslesshp", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOSSLESS_HP }, 0, 0, VE, "preset" }, + { "default", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_DEFAULT }, 0, 0, VE, .unit = "preset" }, + { "slow", "hq 2 passes", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_SLOW }, 0, 0, VE, .unit = "preset" }, + { "medium", "hq 1 pass", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_MEDIUM }, 0, 0, VE, .unit = "preset" }, + { "fast", "hp 1 pass", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_FAST }, 0, 0, VE, .unit = "preset" }, + { "hp", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_HP }, 0, 0, VE, .unit = "preset" }, + { "hq", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_HQ }, 0, 0, VE, .unit = "preset" }, + { "bd", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_BD }, 0, 0, VE, .unit = "preset" }, + { "ll", "low latency", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOW_LATENCY_DEFAULT }, 0, 0, VE, .unit = "preset" }, + { "llhq", "low latency hq", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOW_LATENCY_HQ }, 0, 0, VE, .unit = "preset" }, + { "llhp", "low latency hp", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOW_LATENCY_HP }, 0, 0, VE, .unit = "preset" }, + { "lossless", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOSSLESS_DEFAULT }, 0, 0, VE, .unit = "preset" }, + { "losslesshp", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOSSLESS_HP }, 0, 0, VE, .unit = "preset" }, #ifdef NVENC_HAVE_NEW_PRESETS - { "p1", "fastest (lowest quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P1 }, 0, 0, VE, "preset" }, - { "p2", "faster (lower quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P2 }, 0, 0, VE, "preset" }, - { "p3", "fast (low quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P3 }, 0, 0, VE, "preset" }, - { "p4", "medium (default)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P4 }, 0, 0, VE, "preset" }, - { "p5", "slow (good quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P5 }, 0, 0, VE, "preset" }, - { "p6", "slower (better quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P6 }, 0, 0, VE, "preset" }, - { "p7", "slowest (best quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P7 }, 0, 0, VE, "preset" }, - { "tune", "Set the encoding tuning info", OFFSET(tuning_info), AV_OPT_TYPE_INT, { .i64 = NV_ENC_TUNING_INFO_HIGH_QUALITY }, NV_ENC_TUNING_INFO_HIGH_QUALITY, NV_ENC_TUNING_INFO_LOSSLESS, VE, "tune" }, - { "hq", "High quality", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_HIGH_QUALITY }, 0, 0, VE, "tune" }, - { "ll", "Low latency", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_LOW_LATENCY }, 0, 0, VE, "tune" }, - { "ull", "Ultra low latency", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY }, 0, 0, VE, "tune" }, - { "lossless", "Lossless", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_LOSSLESS }, 0, 0, VE, "tune" }, + { "p1", "fastest (lowest quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P1 }, 0, 0, VE, .unit = "preset" }, + { "p2", "faster (lower quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P2 }, 0, 0, VE, .unit = "preset" }, + { "p3", "fast (low quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P3 }, 0, 0, VE, .unit = "preset" }, + { "p4", "medium (default)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P4 }, 0, 0, VE, .unit = "preset" }, + { "p5", "slow (good quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P5 }, 0, 0, VE, .unit = "preset" }, + { "p6", "slower (better quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P6 }, 0, 0, VE, .unit = "preset" }, + { "p7", "slowest (best quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P7 }, 0, 0, VE, .unit = "preset" }, + { "tune", "Set the encoding tuning info", OFFSET(tuning_info), AV_OPT_TYPE_INT, { .i64 = NV_ENC_TUNING_INFO_HIGH_QUALITY }, NV_ENC_TUNING_INFO_HIGH_QUALITY, NV_ENC_TUNING_INFO_LOSSLESS, VE, .unit = "tune" }, + { "hq", "High quality", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_HIGH_QUALITY }, 0, 0, VE, .unit = "tune" }, + { "ll", "Low latency", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_LOW_LATENCY }, 0, 0, VE, .unit = "tune" }, + { "ull", "Ultra low latency", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY }, 0, 0, VE, .unit = "tune" }, + { "lossless", "Lossless", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_LOSSLESS }, 0, 0, VE, .unit = "tune" }, #endif - { "profile", "Set the encoding profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = NV_ENC_H264_PROFILE_MAIN }, NV_ENC_H264_PROFILE_BASELINE, NV_ENC_H264_PROFILE_HIGH_444P, VE, "profile" }, - { "baseline", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_PROFILE_BASELINE }, 0, 0, VE, "profile" }, - { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_PROFILE_MAIN }, 0, 0, VE, "profile" }, - { "high", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_PROFILE_HIGH }, 0, 0, VE, "profile" }, - { "high444p", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_PROFILE_HIGH_444P }, 0, 0, VE, "profile" }, + { "profile", "Set the encoding profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = NV_ENC_H264_PROFILE_MAIN }, NV_ENC_H264_PROFILE_BASELINE, NV_ENC_H264_PROFILE_HIGH_444P, VE, .unit = "profile" }, + { "baseline", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_PROFILE_BASELINE }, 0, 0, VE, .unit = "profile" }, + { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_PROFILE_MAIN }, 0, 0, VE, .unit = "profile" }, + { "high", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_PROFILE_HIGH }, 0, 0, VE, .unit = "profile" }, + { "high444p", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_PROFILE_HIGH_444P }, 0, 0, VE, .unit = "profile" }, #ifdef NVENC_HAVE_H264_LVL6 - { "level", "Set the encoding level restriction", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = NV_ENC_LEVEL_AUTOSELECT }, NV_ENC_LEVEL_AUTOSELECT, NV_ENC_LEVEL_H264_62, VE, "level" }, + { "level", "Set the encoding level restriction", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = NV_ENC_LEVEL_AUTOSELECT }, NV_ENC_LEVEL_AUTOSELECT, NV_ENC_LEVEL_H264_62, VE, .unit = "level" }, #else - { "level", "Set the encoding level restriction", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = NV_ENC_LEVEL_AUTOSELECT }, NV_ENC_LEVEL_AUTOSELECT, NV_ENC_LEVEL_H264_52, VE, "level" }, + { "level", "Set the encoding level restriction", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = NV_ENC_LEVEL_AUTOSELECT }, NV_ENC_LEVEL_AUTOSELECT, NV_ENC_LEVEL_H264_52, VE, .unit = "level" }, #endif - { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AUTOSELECT }, 0, 0, VE, "level" }, - { "1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_1 }, 0, 0, VE, "level" }, - { "1.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_1 }, 0, 0, VE, "level" }, - { "1b", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_1b }, 0, 0, VE, "level" }, - { "1.0b", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_1b }, 0, 0, VE, "level" }, - { "1.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_11 }, 0, 0, VE, "level" }, - { "1.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_12 }, 0, 0, VE, "level" }, - { "1.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_13 }, 0, 0, VE, "level" }, - { "2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_2 }, 0, 0, VE, "level" }, - { "2.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_2 }, 0, 0, VE, "level" }, - { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_21 }, 0, 0, VE, "level" }, - { "2.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_22 }, 0, 0, VE, "level" }, - { "3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_3 }, 0, 0, VE, "level" }, - { "3.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_3 }, 0, 0, VE, "level" }, - { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_31 }, 0, 0, VE, "level" }, - { "3.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_32 }, 0, 0, VE, "level" }, - { "4", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_4 }, 0, 0, VE, "level" }, - { "4.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_4 }, 0, 0, VE, "level" }, - { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_41 }, 0, 0, VE, "level" }, - { "4.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_42 }, 0, 0, VE, "level" }, - { "5", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_5 }, 0, 0, VE, "level" }, - { "5.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_5 }, 0, 0, VE, "level" }, - { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_51 }, 0, 0, VE, "level" }, - { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_52 }, 0, 0, VE, "level" }, + { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AUTOSELECT }, 0, 0, VE, .unit = "level" }, + { "1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_1 }, 0, 0, VE, .unit = "level" }, + { "1.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_1 }, 0, 0, VE, .unit = "level" }, + { "1b", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_1b }, 0, 0, VE, .unit = "level" }, + { "1.0b", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_1b }, 0, 0, VE, .unit = "level" }, + { "1.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_11 }, 0, 0, VE, .unit = "level" }, + { "1.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_12 }, 0, 0, VE, .unit = "level" }, + { "1.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_13 }, 0, 0, VE, .unit = "level" }, + { "2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_2 }, 0, 0, VE, .unit = "level" }, + { "2.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_2 }, 0, 0, VE, .unit = "level" }, + { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_21 }, 0, 0, VE, .unit = "level" }, + { "2.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_22 }, 0, 0, VE, .unit = "level" }, + { "3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_3 }, 0, 0, VE, .unit = "level" }, + { "3.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_3 }, 0, 0, VE, .unit = "level" }, + { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_31 }, 0, 0, VE, .unit = "level" }, + { "3.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_32 }, 0, 0, VE, .unit = "level" }, + { "4", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_4 }, 0, 0, VE, .unit = "level" }, + { "4.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_4 }, 0, 0, VE, .unit = "level" }, + { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_41 }, 0, 0, VE, .unit = "level" }, + { "4.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_42 }, 0, 0, VE, .unit = "level" }, + { "5", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_5 }, 0, 0, VE, .unit = "level" }, + { "5.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_5 }, 0, 0, VE, .unit = "level" }, + { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_51 }, 0, 0, VE, .unit = "level" }, + { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_52 }, 0, 0, VE, .unit = "level" }, #ifdef NVENC_HAVE_H264_LVL6 - { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_60 }, 0, 0, VE, "level" }, - { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_61 }, 0, 0, VE, "level" }, - { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_62 }, 0, 0, VE, "level" }, + { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_60 }, 0, 0, VE, .unit = "level" }, + { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_61 }, 0, 0, VE, .unit = "level" }, + { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_H264_62 }, 0, 0, VE, .unit = "level" }, #endif - { "rc", "Override the preset rate-control", OFFSET(rc), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, "rc" }, - { "constqp", "Constant QP mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CONSTQP }, 0, 0, VE, "rc" }, - { "vbr", "Variable bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_VBR }, 0, 0, VE, "rc" }, - { "cbr", "Constant bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CBR }, 0, 0, VE, "rc" }, + { "rc", "Override the preset rate-control", OFFSET(rc), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, .unit = "rc" }, + { "constqp", "Constant QP mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CONSTQP }, 0, 0, VE, .unit = "rc" }, + { "vbr", "Variable bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_VBR }, 0, 0, VE, .unit = "rc" }, + { "cbr", "Constant bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CBR }, 0, 0, VE, .unit = "rc" }, #ifndef NVENC_NO_DEPRECATED_RC - { "vbr_minqp", "Variable bitrate mode with MinQP (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_MINQP) }, 0, 0, VE, "rc" }, + { "vbr_minqp", "Variable bitrate mode with MinQP (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_MINQP) }, 0, 0, VE, .unit = "rc" }, { "ll_2pass_quality", "Multi-pass optimized for image quality (deprecated)", - 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_QUALITY) }, 0, 0, VE, "rc" }, + 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_QUALITY) }, 0, 0, VE, .unit = "rc" }, { "ll_2pass_size", "Multi-pass optimized for constant frame size (deprecated)", - 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_FRAMESIZE_CAP) }, 0, 0, VE, "rc" }, - { "vbr_2pass", "Multi-pass variable bitrate mode (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_VBR) }, 0, 0, VE, "rc" }, - { "cbr_ld_hq", "Constant bitrate low delay high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ) }, 0, 0, VE, "rc" }, - { "cbr_hq", "Constant bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_HQ) }, 0, 0, VE, "rc" }, - { "vbr_hq", "Variable bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_HQ) }, 0, 0, VE, "rc" }, + 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_FRAMESIZE_CAP) }, 0, 0, VE, .unit = "rc" }, + { "vbr_2pass", "Multi-pass variable bitrate mode (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_VBR) }, 0, 0, VE, .unit = "rc" }, + { "cbr_ld_hq", "Constant bitrate low delay high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ) }, 0, 0, VE, .unit = "rc" }, + { "cbr_hq", "Constant bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_HQ) }, 0, 0, VE, .unit = "rc" }, + { "vbr_hq", "Variable bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_HQ) }, 0, 0, VE, .unit = "rc" }, #else - { "vbr_minqp", "Variable bitrate mode with MinQP (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, + { "vbr_minqp", "Variable bitrate mode with MinQP (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, .unit = "rc" }, { "ll_2pass_quality", "Multi-pass optimized for image quality (deprecated)", - 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, + 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, .unit = "rc" }, { "ll_2pass_size", "Multi-pass optimized for constant frame size (deprecated)", - 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, "rc" }, - { "vbr_2pass", "Multi-pass variable bitrate mode (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, - { "cbr_ld_hq", "Constant bitrate low delay high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, "rc" }, - { "cbr_hq", "Constant bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, "rc" }, - { "vbr_hq", "Variable bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, + 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, .unit = "rc" }, + { "vbr_2pass", "Multi-pass variable bitrate mode (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, .unit = "rc" }, + { "cbr_ld_hq", "Constant bitrate low delay high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, .unit = "rc" }, + { "cbr_hq", "Constant bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, .unit = "rc" }, + { "vbr_hq", "Variable bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, .unit = "rc" }, #endif { "rc-lookahead", "Number of frames to look ahead for rate-control", OFFSET(rc_lookahead), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, @@ -127,9 +127,14 @@ static const AVOption options[] = { { "cbr", "Use cbr encoding mode", OFFSET(cbr), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "2pass", "Use 2pass encoding mode", OFFSET(twopass), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE }, { "gpu", "Selects which NVENC capable GPU to use. First GPU is 0, second is 1, and so on.", - OFFSET(device), AV_OPT_TYPE_INT, { .i64 = ANY_DEVICE }, -2, INT_MAX, VE, "gpu" }, - { "any", "Pick the first device available", 0, AV_OPT_TYPE_CONST, { .i64 = ANY_DEVICE }, 0, 0, VE, "gpu" }, - { "list", "List the available devices", 0, AV_OPT_TYPE_CONST, { .i64 = LIST_DEVICES }, 0, 0, VE, "gpu" }, + OFFSET(device), AV_OPT_TYPE_INT, { .i64 = ANY_DEVICE }, -2, INT_MAX, VE, .unit = "gpu" }, + { "any", "Pick the first device available", 0, AV_OPT_TYPE_CONST, { .i64 = ANY_DEVICE }, 0, 0, VE, .unit = "gpu" }, + { "list", "List the available devices", 0, AV_OPT_TYPE_CONST, { .i64 = LIST_DEVICES }, 0, 0, VE, .unit = "gpu" }, + { "rgb_mode", "Configure how nvenc handles packed RGB input.", + OFFSET(rgb_mode), AV_OPT_TYPE_INT, { .i64 = NVENC_RGB_MODE_420 }, 0, INT_MAX, VE, .unit = "rgb_mode" }, + { "yuv420", "Convert to yuv420", 0, AV_OPT_TYPE_CONST, { .i64 = NVENC_RGB_MODE_420 }, 0, 0, VE, .unit = "rgb_mode" }, + { "yuv444", "Convert to yuv444", 0, AV_OPT_TYPE_CONST, { .i64 = NVENC_RGB_MODE_444 }, 0, 0, VE, .unit = "rgb_mode" }, + { "disabled", "Disables support, throws an error.", 0, AV_OPT_TYPE_CONST, { .i64 = NVENC_RGB_MODE_DISABLED }, 0, 0, VE, .unit = "rgb_mode" }, { "delay", "Delay frame output by the given amount of frames", OFFSET(async_depth), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 0, INT_MAX, VE }, { "no-scenecut", "When lookahead is enabled, set this to 1 to disable adaptive I-frame insertion at scene cuts", @@ -165,34 +170,34 @@ static const AVOption options[] = { OFFSET(qp_cr_offset), AV_OPT_TYPE_INT, { .i64 = 0 }, -12, 12, VE }, { "weighted_pred","Set 1 to enable weighted prediction", OFFSET(weighted_pred),AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, - { "coder", "Coder type", OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = -1 },-1, 2, VE, "coder" }, - { "default", "", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0, VE, "coder" }, - { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_ENTROPY_CODING_MODE_AUTOSELECT }, 0, 0, VE, "coder" }, - { "cabac", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_ENTROPY_CODING_MODE_CABAC }, 0, 0, VE, "coder" }, - { "cavlc", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC }, 0, 0, VE, "coder" }, - { "ac", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_ENTROPY_CODING_MODE_CABAC }, 0, 0, VE, "coder" }, - { "vlc", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC }, 0, 0, VE, "coder" }, + { "coder", "Coder type", OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = -1 },-1, 2, VE, .unit = "coder" }, + { "default", "", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0, VE, .unit = "coder" }, + { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_ENTROPY_CODING_MODE_AUTOSELECT }, 0, 0, VE, .unit = "coder" }, + { "cabac", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_ENTROPY_CODING_MODE_CABAC }, 0, 0, VE, .unit = "coder" }, + { "cavlc", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC }, 0, 0, VE, .unit = "coder" }, + { "ac", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_ENTROPY_CODING_MODE_CABAC }, 0, 0, VE, .unit = "coder" }, + { "vlc", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC }, 0, 0, VE, .unit = "coder" }, #ifdef NVENC_HAVE_BFRAME_REF_MODE - { "b_ref_mode", "Use B frames as references", OFFSET(b_ref_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, NV_ENC_BFRAME_REF_MODE_MIDDLE, VE, "b_ref_mode" }, - { "disabled", "B frames will not be used for reference", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_DISABLED }, 0, 0, VE, "b_ref_mode" }, - { "each", "Each B frame will be used for reference", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_EACH }, 0, 0, VE, "b_ref_mode" }, - { "middle", "Only (number of B frames)/2 will be used for reference", 0,AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_MIDDLE }, 0, 0, VE, "b_ref_mode" }, + { "b_ref_mode", "Use B frames as references", OFFSET(b_ref_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, NV_ENC_BFRAME_REF_MODE_MIDDLE, VE, .unit = "b_ref_mode" }, + { "disabled", "B frames will not be used for reference", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_DISABLED }, 0, 0, VE, .unit = "b_ref_mode" }, + { "each", "Each B frame will be used for reference", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_EACH }, 0, 0, VE, .unit = "b_ref_mode" }, + { "middle", "Only (number of B frames)/2 will be used for reference", 0,AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_MIDDLE }, 0, 0, VE, .unit = "b_ref_mode" }, #else - { "b_ref_mode", "(not supported)", OFFSET(b_ref_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, "b_ref_mode" }, - { "disabled", "", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, VE, "b_ref_mode" }, - { "each", "", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, VE, "b_ref_mode" }, - { "middle", "", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, VE, "b_ref_mode" }, + { "b_ref_mode", "(not supported)", OFFSET(b_ref_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, .unit = "b_ref_mode" }, + { "disabled", "", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, VE, .unit = "b_ref_mode" }, + { "each", "", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, VE, .unit = "b_ref_mode" }, + { "middle", "", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, VE, .unit = "b_ref_mode" }, #endif { "a53cc", "Use A53 Closed Captions (if available)", OFFSET(a53_cc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, { "dpb_size", "Specifies the DPB size used for encoding (0 means automatic)", OFFSET(dpb_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, #ifdef NVENC_HAVE_MULTIPASS - { "multipass", "Set the multipass encoding", OFFSET(multipass), AV_OPT_TYPE_INT, { .i64 = NV_ENC_MULTI_PASS_DISABLED }, NV_ENC_MULTI_PASS_DISABLED, NV_ENC_TWO_PASS_FULL_RESOLUTION, VE, "multipass" }, - { "disabled", "Single Pass", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_MULTI_PASS_DISABLED }, 0, 0, VE, "multipass" }, + { "multipass", "Set the multipass encoding", OFFSET(multipass), AV_OPT_TYPE_INT, { .i64 = NV_ENC_MULTI_PASS_DISABLED }, NV_ENC_MULTI_PASS_DISABLED, NV_ENC_TWO_PASS_FULL_RESOLUTION, VE, .unit = "multipass" }, + { "disabled", "Single Pass", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_MULTI_PASS_DISABLED }, 0, 0, VE, .unit = "multipass" }, { "qres", "Two Pass encoding is enabled where first Pass is quarter resolution", - 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TWO_PASS_QUARTER_RESOLUTION }, 0, 0, VE, "multipass" }, + 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TWO_PASS_QUARTER_RESOLUTION }, 0, 0, VE, .unit = "multipass" }, { "fullres", "Two Pass encoding is enabled where first Pass is full resolution", - 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TWO_PASS_FULL_RESOLUTION }, 0, 0, VE, "multipass" }, + 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TWO_PASS_FULL_RESOLUTION }, 0, 0, VE, .unit = "multipass" }, #endif #ifdef NVENC_HAVE_LDKFS { "ldkfs", "Low delay key frame scale; Specifies the Scene Change frame size increase allowed in case of single frame VBV and CBR", @@ -206,6 +211,8 @@ static const AVOption options[] = { OFFSET(intra_refresh),AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "single-slice-intra-refresh", "Use single slice intra refresh", OFFSET(single_slice_intra_refresh), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, + { "max_slice_size", "Maximum encoded slice size in bytes", + OFFSET(max_slice_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, { "constrained-encoding", "Enable constrainedFrame encoding where each slice in the constrained picture is independent of other slices", OFFSET(constrained_encoding), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { NULL } @@ -218,7 +225,7 @@ static const FFCodecDefault defaults[] = { { "qdiff", "-1" }, { "qblur", "-1" }, { "qcomp", "-1" }, - { "g", "250" }, + { "g", "-1" }, { "bf", "-1" }, { "refs", "0" }, { NULL }, diff --git a/libavcodec/nvenc_hevc.c b/libavcodec/nvenc_hevc.c index d99077f1705..65fcb4efb82 100644 --- a/libavcodec/nvenc_hevc.c +++ b/libavcodec/nvenc_hevc.c @@ -27,88 +27,88 @@ #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { #ifdef NVENC_HAVE_NEW_PRESETS - { "preset", "Set the encoding preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = PRESET_P4 }, PRESET_DEFAULT, PRESET_P7, VE, "preset" }, + { "preset", "Set the encoding preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = PRESET_P4 }, PRESET_DEFAULT, PRESET_P7, VE, .unit = "preset" }, #else - { "preset", "Set the encoding preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = PRESET_MEDIUM }, PRESET_DEFAULT, PRESET_LOSSLESS_HP, VE, "preset" }, + { "preset", "Set the encoding preset", OFFSET(preset), AV_OPT_TYPE_INT, { .i64 = PRESET_MEDIUM }, PRESET_DEFAULT, PRESET_LOSSLESS_HP, VE, .unit = "preset" }, #endif - { "default", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_DEFAULT }, 0, 0, VE, "preset" }, - { "slow", "hq 2 passes", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_SLOW }, 0, 0, VE, "preset" }, - { "medium", "hq 1 pass", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_MEDIUM }, 0, 0, VE, "preset" }, - { "fast", "hp 1 pass", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_FAST }, 0, 0, VE, "preset" }, - { "hp", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_HP }, 0, 0, VE, "preset" }, - { "hq", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_HQ }, 0, 0, VE, "preset" }, - { "bd", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_BD }, 0, 0, VE, "preset" }, - { "ll", "low latency", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOW_LATENCY_DEFAULT }, 0, 0, VE, "preset" }, - { "llhq", "low latency hq", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOW_LATENCY_HQ }, 0, 0, VE, "preset" }, - { "llhp", "low latency hp", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOW_LATENCY_HP }, 0, 0, VE, "preset" }, - { "lossless", "lossless", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOSSLESS_DEFAULT }, 0, 0, VE, "preset" }, - { "losslesshp", "lossless hp", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOSSLESS_HP }, 0, 0, VE, "preset" }, + { "default", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_DEFAULT }, 0, 0, VE, .unit = "preset" }, + { "slow", "hq 2 passes", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_SLOW }, 0, 0, VE, .unit = "preset" }, + { "medium", "hq 1 pass", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_MEDIUM }, 0, 0, VE, .unit = "preset" }, + { "fast", "hp 1 pass", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_FAST }, 0, 0, VE, .unit = "preset" }, + { "hp", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_HP }, 0, 0, VE, .unit = "preset" }, + { "hq", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_HQ }, 0, 0, VE, .unit = "preset" }, + { "bd", "", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_BD }, 0, 0, VE, .unit = "preset" }, + { "ll", "low latency", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOW_LATENCY_DEFAULT }, 0, 0, VE, .unit = "preset" }, + { "llhq", "low latency hq", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOW_LATENCY_HQ }, 0, 0, VE, .unit = "preset" }, + { "llhp", "low latency hp", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOW_LATENCY_HP }, 0, 0, VE, .unit = "preset" }, + { "lossless", "lossless", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOSSLESS_DEFAULT }, 0, 0, VE, .unit = "preset" }, + { "losslesshp", "lossless hp", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_LOSSLESS_HP }, 0, 0, VE, .unit = "preset" }, #ifdef NVENC_HAVE_NEW_PRESETS - { "p1", "fastest (lowest quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P1 }, 0, 0, VE, "preset" }, - { "p2", "faster (lower quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P2 }, 0, 0, VE, "preset" }, - { "p3", "fast (low quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P3 }, 0, 0, VE, "preset" }, - { "p4", "medium (default)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P4 }, 0, 0, VE, "preset" }, - { "p5", "slow (good quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P5 }, 0, 0, VE, "preset" }, - { "p6", "slower (better quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P6 }, 0, 0, VE, "preset" }, - { "p7", "slowest (best quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P7 }, 0, 0, VE, "preset" }, - { "tune", "Set the encoding tuning info", OFFSET(tuning_info), AV_OPT_TYPE_INT, { .i64 = NV_ENC_TUNING_INFO_HIGH_QUALITY }, NV_ENC_TUNING_INFO_HIGH_QUALITY, NV_ENC_TUNING_INFO_LOSSLESS, VE, "tune" }, - { "hq", "High quality", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_HIGH_QUALITY }, 0, 0, VE, "tune" }, - { "ll", "Low latency", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_LOW_LATENCY }, 0, 0, VE, "tune" }, - { "ull", "Ultra low latency", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY }, 0, 0, VE, "tune" }, - { "lossless", "Lossless", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_LOSSLESS }, 0, 0, VE, "tune" }, + { "p1", "fastest (lowest quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P1 }, 0, 0, VE, .unit = "preset" }, + { "p2", "faster (lower quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P2 }, 0, 0, VE, .unit = "preset" }, + { "p3", "fast (low quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P3 }, 0, 0, VE, .unit = "preset" }, + { "p4", "medium (default)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P4 }, 0, 0, VE, .unit = "preset" }, + { "p5", "slow (good quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P5 }, 0, 0, VE, .unit = "preset" }, + { "p6", "slower (better quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P6 }, 0, 0, VE, .unit = "preset" }, + { "p7", "slowest (best quality)", 0, AV_OPT_TYPE_CONST, { .i64 = PRESET_P7 }, 0, 0, VE, .unit = "preset" }, + { "tune", "Set the encoding tuning info", OFFSET(tuning_info), AV_OPT_TYPE_INT, { .i64 = NV_ENC_TUNING_INFO_HIGH_QUALITY }, NV_ENC_TUNING_INFO_HIGH_QUALITY, NV_ENC_TUNING_INFO_LOSSLESS, VE, .unit = "tune" }, + { "hq", "High quality", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_HIGH_QUALITY }, 0, 0, VE, .unit = "tune" }, + { "ll", "Low latency", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_LOW_LATENCY }, 0, 0, VE, .unit = "tune" }, + { "ull", "Ultra low latency", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY }, 0, 0, VE, .unit = "tune" }, + { "lossless", "Lossless", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TUNING_INFO_LOSSLESS }, 0, 0, VE, .unit = "tune" }, #endif - { "profile", "Set the encoding profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = NV_ENC_HEVC_PROFILE_MAIN }, NV_ENC_HEVC_PROFILE_MAIN, FF_PROFILE_HEVC_REXT, VE, "profile" }, - { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_HEVC_PROFILE_MAIN }, 0, 0, VE, "profile" }, - { "main10", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_HEVC_PROFILE_MAIN_10 }, 0, 0, VE, "profile" }, - { "rext", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_HEVC_PROFILE_REXT }, 0, 0, VE, "profile" }, - { "level", "Set the encoding level restriction", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = NV_ENC_LEVEL_AUTOSELECT }, NV_ENC_LEVEL_AUTOSELECT, NV_ENC_LEVEL_HEVC_62, VE, "level" }, - { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AUTOSELECT }, 0, 0, VE, "level" }, - { "1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_1 }, 0, 0, VE, "level" }, - { "1.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_1 }, 0, 0, VE, "level" }, - { "2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_2 }, 0, 0, VE, "level" }, - { "2.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_2 }, 0, 0, VE, "level" }, - { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_21 }, 0, 0, VE, "level" }, - { "3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_3 }, 0, 0, VE, "level" }, - { "3.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_3 }, 0, 0, VE, "level" }, - { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_31 }, 0, 0, VE, "level" }, - { "4", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_4 }, 0, 0, VE, "level" }, - { "4.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_4 }, 0, 0, VE, "level" }, - { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_41 }, 0, 0, VE, "level" }, - { "5", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_5 }, 0, 0, VE, "level" }, - { "5.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_5 }, 0, 0, VE, "level" }, - { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_51 }, 0, 0, VE, "level" }, - { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_52 }, 0, 0, VE, "level" }, - { "6", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_6 }, 0, 0, VE, "level" }, - { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_6 }, 0, 0, VE, "level" }, - { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_61 }, 0, 0, VE, "level" }, - { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_62 }, 0, 0, VE, "level" }, - { "tier", "Set the encoding tier", OFFSET(tier), AV_OPT_TYPE_INT, { .i64 = NV_ENC_TIER_HEVC_MAIN }, NV_ENC_TIER_HEVC_MAIN, NV_ENC_TIER_HEVC_HIGH, VE, "tier"}, - { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TIER_HEVC_MAIN }, 0, 0, VE, "tier" }, - { "high", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TIER_HEVC_HIGH }, 0, 0, VE, "tier" }, - { "rc", "Override the preset rate-control", OFFSET(rc), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, "rc" }, - { "constqp", "Constant QP mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CONSTQP }, 0, 0, VE, "rc" }, - { "vbr", "Variable bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_VBR }, 0, 0, VE, "rc" }, - { "cbr", "Constant bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CBR }, 0, 0, VE, "rc" }, + { "profile", "Set the encoding profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = NV_ENC_HEVC_PROFILE_MAIN }, NV_ENC_HEVC_PROFILE_MAIN, AV_PROFILE_HEVC_REXT, VE, .unit = "profile" }, + { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_HEVC_PROFILE_MAIN }, 0, 0, VE, .unit = "profile" }, + { "main10", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_HEVC_PROFILE_MAIN_10 }, 0, 0, VE, .unit = "profile" }, + { "rext", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_HEVC_PROFILE_REXT }, 0, 0, VE, .unit = "profile" }, + { "level", "Set the encoding level restriction", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = NV_ENC_LEVEL_AUTOSELECT }, NV_ENC_LEVEL_AUTOSELECT, NV_ENC_LEVEL_HEVC_62, VE, .unit = "level" }, + { "auto", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_AUTOSELECT }, 0, 0, VE, .unit = "level" }, + { "1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_1 }, 0, 0, VE, .unit = "level" }, + { "1.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_1 }, 0, 0, VE, .unit = "level" }, + { "2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_2 }, 0, 0, VE, .unit = "level" }, + { "2.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_2 }, 0, 0, VE, .unit = "level" }, + { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_21 }, 0, 0, VE, .unit = "level" }, + { "3", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_3 }, 0, 0, VE, .unit = "level" }, + { "3.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_3 }, 0, 0, VE, .unit = "level" }, + { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_31 }, 0, 0, VE, .unit = "level" }, + { "4", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_4 }, 0, 0, VE, .unit = "level" }, + { "4.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_4 }, 0, 0, VE, .unit = "level" }, + { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_41 }, 0, 0, VE, .unit = "level" }, + { "5", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_5 }, 0, 0, VE, .unit = "level" }, + { "5.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_5 }, 0, 0, VE, .unit = "level" }, + { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_51 }, 0, 0, VE, .unit = "level" }, + { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_52 }, 0, 0, VE, .unit = "level" }, + { "6", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_6 }, 0, 0, VE, .unit = "level" }, + { "6.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_6 }, 0, 0, VE, .unit = "level" }, + { "6.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_61 }, 0, 0, VE, .unit = "level" }, + { "6.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_LEVEL_HEVC_62 }, 0, 0, VE, .unit = "level" }, + { "tier", "Set the encoding tier", OFFSET(tier), AV_OPT_TYPE_INT, { .i64 = NV_ENC_TIER_HEVC_MAIN }, NV_ENC_TIER_HEVC_MAIN, NV_ENC_TIER_HEVC_HIGH, VE, .unit = "tier"}, + { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TIER_HEVC_MAIN }, 0, 0, VE, .unit = "tier" }, + { "high", "", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TIER_HEVC_HIGH }, 0, 0, VE, .unit = "tier" }, + { "rc", "Override the preset rate-control", OFFSET(rc), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, .unit = "rc" }, + { "constqp", "Constant QP mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CONSTQP }, 0, 0, VE, .unit = "rc" }, + { "vbr", "Variable bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_VBR }, 0, 0, VE, .unit = "rc" }, + { "cbr", "Constant bitrate mode", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_PARAMS_RC_CBR }, 0, 0, VE, .unit = "rc" }, #ifndef NVENC_NO_DEPRECATED_RC - { "vbr_minqp", "Variable bitrate mode with MinQP (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_MINQP) }, 0, 0, VE, "rc" }, + { "vbr_minqp", "Variable bitrate mode with MinQP (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_MINQP) }, 0, 0, VE, .unit = "rc" }, { "ll_2pass_quality", "Multi-pass optimized for image quality (deprecated)", - 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_QUALITY) }, 0, 0, VE, "rc" }, + 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_QUALITY) }, 0, 0, VE, .unit = "rc" }, { "ll_2pass_size", "Multi-pass optimized for constant frame size (deprecated)", - 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_FRAMESIZE_CAP) }, 0, 0, VE, "rc" }, - { "vbr_2pass", "Multi-pass variable bitrate mode (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_VBR) }, 0, 0, VE, "rc" }, - { "cbr_ld_hq", "Constant bitrate low delay high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ) }, 0, 0, VE, "rc" }, - { "cbr_hq", "Constant bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_HQ) }, 0, 0, VE, "rc" }, - { "vbr_hq", "Variable bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_HQ) }, 0, 0, VE, "rc" }, + 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_FRAMESIZE_CAP) }, 0, 0, VE, .unit = "rc" }, + { "vbr_2pass", "Multi-pass variable bitrate mode (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_2_PASS_VBR) }, 0, 0, VE, .unit = "rc" }, + { "cbr_ld_hq", "Constant bitrate low delay high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ) }, 0, 0, VE, .unit = "rc" }, + { "cbr_hq", "Constant bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR_HQ) }, 0, 0, VE, .unit = "rc" }, + { "vbr_hq", "Variable bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR_HQ) }, 0, 0, VE, .unit = "rc" }, #else - { "vbr_minqp", "Variable bitrate mode with MinQP (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, + { "vbr_minqp", "Variable bitrate mode with MinQP (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, .unit = "rc" }, { "ll_2pass_quality", "Multi-pass optimized for image quality (deprecated)", - 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, + 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, .unit = "rc" }, { "ll_2pass_size", "Multi-pass optimized for constant frame size (deprecated)", - 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, "rc" }, - { "vbr_2pass", "Multi-pass variable bitrate mode (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, - { "cbr_ld_hq", "Constant bitrate low delay high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, "rc" }, - { "cbr_hq", "Constant bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, "rc" }, - { "vbr_hq", "Variable bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, "rc" }, + 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, .unit = "rc" }, + { "vbr_2pass", "Multi-pass variable bitrate mode (deprecated)", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, .unit = "rc" }, + { "cbr_ld_hq", "Constant bitrate low delay high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, .unit = "rc" }, + { "cbr_hq", "Constant bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_CBR) }, 0, 0, VE, .unit = "rc" }, + { "vbr_hq", "Variable bitrate high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = RCD(NV_ENC_PARAMS_RC_VBR) }, 0, 0, VE, .unit = "rc" }, #endif { "rc-lookahead", "Number of frames to look ahead for rate-control", OFFSET(rc_lookahead), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, @@ -116,9 +116,14 @@ static const AVOption options[] = { { "cbr", "Use cbr encoding mode", OFFSET(cbr), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "2pass", "Use 2pass encoding mode", OFFSET(twopass), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VE }, { "gpu", "Selects which NVENC capable GPU to use. First GPU is 0, second is 1, and so on.", - OFFSET(device), AV_OPT_TYPE_INT, { .i64 = ANY_DEVICE }, -2, INT_MAX, VE, "gpu" }, - { "any", "Pick the first device available", 0, AV_OPT_TYPE_CONST, { .i64 = ANY_DEVICE }, 0, 0, VE, "gpu" }, - { "list", "List the available devices", 0, AV_OPT_TYPE_CONST, { .i64 = LIST_DEVICES }, 0, 0, VE, "gpu" }, + OFFSET(device), AV_OPT_TYPE_INT, { .i64 = ANY_DEVICE }, -2, INT_MAX, VE, .unit = "gpu" }, + { "any", "Pick the first device available", 0, AV_OPT_TYPE_CONST, { .i64 = ANY_DEVICE }, 0, 0, VE, .unit = "gpu" }, + { "list", "List the available devices", 0, AV_OPT_TYPE_CONST, { .i64 = LIST_DEVICES }, 0, 0, VE, .unit = "gpu" }, + { "rgb_mode", "Configure how nvenc handles packed RGB input.", + OFFSET(rgb_mode), AV_OPT_TYPE_INT, { .i64 = NVENC_RGB_MODE_420 }, 0, INT_MAX, VE, .unit = "rgb_mode" }, + { "yuv420", "Convert to yuv420", 0, AV_OPT_TYPE_CONST, { .i64 = NVENC_RGB_MODE_420 }, 0, 0, VE, .unit = "rgb_mode" }, + { "yuv444", "Convert to yuv444", 0, AV_OPT_TYPE_CONST, { .i64 = NVENC_RGB_MODE_444 }, 0, 0, VE, .unit = "rgb_mode" }, + { "disabled", "Disables support, throws an error.", 0, AV_OPT_TYPE_CONST, { .i64 = NVENC_RGB_MODE_DISABLED }, 0, 0, VE, .unit = "rgb_mode" }, { "delay", "Delay frame output by the given amount of frames", OFFSET(async_depth), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 0, INT_MAX, VE }, { "no-scenecut", "When lookahead is enabled, set this to 1 to disable adaptive I-frame insertion at scene cuts", @@ -153,27 +158,27 @@ static const AVOption options[] = { { "weighted_pred","Set 1 to enable weighted prediction", OFFSET(weighted_pred),AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, VE }, #ifdef NVENC_HAVE_HEVC_BFRAME_REF_MODE - { "b_ref_mode", "Use B frames as references", OFFSET(b_ref_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, NV_ENC_BFRAME_REF_MODE_MIDDLE, VE, "b_ref_mode" }, - { "disabled", "B frames will not be used for reference", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_DISABLED }, 0, 0, VE, "b_ref_mode" }, - { "each", "Each B frame will be used for reference", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_EACH }, 0, 0, VE, "b_ref_mode" }, - { "middle", "Only (number of B frames)/2 will be used for reference", 0,AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_MIDDLE }, 0, 0, VE, "b_ref_mode" }, + { "b_ref_mode", "Use B frames as references", OFFSET(b_ref_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, NV_ENC_BFRAME_REF_MODE_MIDDLE, VE, .unit = "b_ref_mode" }, + { "disabled", "B frames will not be used for reference", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_DISABLED }, 0, 0, VE, .unit = "b_ref_mode" }, + { "each", "Each B frame will be used for reference", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_EACH }, 0, 0, VE, .unit = "b_ref_mode" }, + { "middle", "Only (number of B frames)/2 will be used for reference", 0,AV_OPT_TYPE_CONST, { .i64 = NV_ENC_BFRAME_REF_MODE_MIDDLE }, 0, 0, VE, .unit = "b_ref_mode" }, #else - { "b_ref_mode", "(not supported)", OFFSET(b_ref_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, "b_ref_mode" }, - { "disabled", "", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, VE, "b_ref_mode" }, - { "each", "", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, VE, "b_ref_mode" }, - { "middle", "", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, VE, "b_ref_mode" }, + { "b_ref_mode", "(not supported)", OFFSET(b_ref_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE, .unit = "b_ref_mode" }, + { "disabled", "", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, VE, .unit = "b_ref_mode" }, + { "each", "", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, VE, .unit = "b_ref_mode" }, + { "middle", "", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, VE, .unit = "b_ref_mode" }, #endif { "a53cc", "Use A53 Closed Captions (if available)", OFFSET(a53_cc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, { "s12m_tc", "Use timecode (if available)", OFFSET(s12m_tc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, { "dpb_size", "Specifies the DPB size used for encoding (0 means automatic)", OFFSET(dpb_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, #ifdef NVENC_HAVE_MULTIPASS - { "multipass", "Set the multipass encoding", OFFSET(multipass), AV_OPT_TYPE_INT, { .i64 = NV_ENC_MULTI_PASS_DISABLED }, NV_ENC_MULTI_PASS_DISABLED, NV_ENC_TWO_PASS_FULL_RESOLUTION, VE, "multipass" }, - { "disabled", "Single Pass", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_MULTI_PASS_DISABLED }, 0, 0, VE, "multipass" }, + { "multipass", "Set the multipass encoding", OFFSET(multipass), AV_OPT_TYPE_INT, { .i64 = NV_ENC_MULTI_PASS_DISABLED }, NV_ENC_MULTI_PASS_DISABLED, NV_ENC_TWO_PASS_FULL_RESOLUTION, VE, .unit = "multipass" }, + { "disabled", "Single Pass", 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_MULTI_PASS_DISABLED }, 0, 0, VE, .unit = "multipass" }, { "qres", "Two Pass encoding is enabled where first Pass is quarter resolution", - 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TWO_PASS_QUARTER_RESOLUTION }, 0, 0, VE, "multipass" }, + 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TWO_PASS_QUARTER_RESOLUTION }, 0, 0, VE, .unit = "multipass" }, { "fullres", "Two Pass encoding is enabled where first Pass is full resolution", - 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TWO_PASS_FULL_RESOLUTION }, 0, 0, VE, "multipass" }, + 0, AV_OPT_TYPE_CONST, { .i64 = NV_ENC_TWO_PASS_FULL_RESOLUTION }, 0, 0, VE, .unit = "multipass" }, #endif #ifdef NVENC_HAVE_LDKFS { "ldkfs", "Low delay key frame scale; Specifies the Scene Change frame size increase allowed in case of single frame VBV and CBR", @@ -187,6 +192,8 @@ static const AVOption options[] = { OFFSET(intra_refresh),AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "single-slice-intra-refresh", "Use single slice intra refresh", OFFSET(single_slice_intra_refresh), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, + { "max_slice_size", "Maximum encoded slice size in bytes", + OFFSET(max_slice_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, { "constrained-encoding", "Enable constrainedFrame encoding where each slice in the constrained picture is independent of other slices", OFFSET(constrained_encoding), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { NULL } @@ -199,7 +206,7 @@ static const FFCodecDefault defaults[] = { { "qdiff", "-1" }, { "qblur", "-1" }, { "qcomp", "-1" }, - { "g", "250" }, + { "g", "-1" }, { "bf", "-1" }, { "refs", "0" }, { NULL }, diff --git a/libavcodec/omx.c b/libavcodec/omx.c index 6e667971ead..3fdde753b59 100644 --- a/libavcodec/omx.c +++ b/libavcodec/omx.c @@ -531,14 +531,14 @@ static av_cold int omx_component_init(AVCodecContext *avctx, const char *role) CHECK(err); avc.nBFrames = 0; avc.nPFrames = avctx->gop_size - 1; - switch (s->profile == FF_PROFILE_UNKNOWN ? avctx->profile : s->profile) { - case FF_PROFILE_H264_BASELINE: + switch (s->profile == AV_PROFILE_UNKNOWN ? avctx->profile : s->profile) { + case AV_PROFILE_H264_BASELINE: avc.eProfile = OMX_VIDEO_AVCProfileBaseline; break; - case FF_PROFILE_H264_MAIN: + case AV_PROFILE_H264_MAIN: avc.eProfile = OMX_VIDEO_AVCProfileMain; break; - case FF_PROFILE_H264_HIGH: + case AV_PROFILE_H264_HIGH: avc.eProfile = OMX_VIDEO_AVCProfileHigh; break; default: @@ -793,7 +793,8 @@ static int omx_encode_frame(AVCodecContext *avctx, AVPacket *pkt, need_copy = 1; } if (need_copy) - av_image_copy(dst, linesize, (const uint8_t**) frame->data, frame->linesize, avctx->pix_fmt, avctx->width, avctx->height); + av_image_copy2(dst, linesize, frame->data, frame->linesize, + avctx->pix_fmt, avctx->width, avctx->height); buffer->nFlags = OMX_BUFFERFLAG_ENDOFFRAME; buffer->nOffset = 0; // Convert the timestamps to microseconds; some encoders can ignore @@ -919,10 +920,10 @@ static const AVOption options[] = { { "omx_libname", "OpenMAX library name", OFFSET(libname), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VDE }, { "omx_libprefix", "OpenMAX library prefix", OFFSET(libprefix), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VDE }, { "zerocopy", "Try to avoid copying input frames if possible", OFFSET(input_zerocopy), AV_OPT_TYPE_INT, { .i64 = CONFIG_OMX_RPI }, 0, 1, VE }, - { "profile", "Set the encoding profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, FF_PROFILE_H264_HIGH, VE, "profile" }, - { "baseline", "", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_H264_BASELINE }, 0, 0, VE, "profile" }, - { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_H264_MAIN }, 0, 0, VE, "profile" }, - { "high", "", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_H264_HIGH }, 0, 0, VE, "profile" }, + { "profile", "Set the encoding profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = AV_PROFILE_UNKNOWN }, AV_PROFILE_UNKNOWN, AV_PROFILE_H264_HIGH, VE, .unit = "profile" }, + { "baseline", "", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_H264_BASELINE }, 0, 0, VE, .unit = "profile" }, + { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_H264_MAIN }, 0, 0, VE, .unit = "profile" }, + { "high", "", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_H264_HIGH }, 0, 0, VE, .unit = "profile" }, { NULL } }; diff --git a/libavcodec/on2avc.c b/libavcodec/on2avc.c index b190f36e194..8d0c8812488 100644 --- a/libavcodec/on2avc.c +++ b/libavcodec/on2avc.c @@ -892,9 +892,9 @@ static av_cold void on2avc_free_vlcs(On2AVCContext *c) { int i; - ff_free_vlc(&c->scale_diff); + ff_vlc_free(&c->scale_diff); for (i = 1; i < 16; i++) - ff_free_vlc(&c->cb_vlc[i]); + ff_vlc_free(&c->cb_vlc[i]); } static av_cold int on2avc_decode_init(AVCodecContext *avctx) @@ -969,14 +969,14 @@ static av_cold int on2avc_decode_init(AVCodecContext *avctx) if (!c->fdsp) return AVERROR(ENOMEM); - ret = ff_init_vlc_from_lengths(&c->scale_diff, 9, ON2AVC_SCALE_DIFFS, + ret = ff_vlc_init_from_lengths(&c->scale_diff, 9, ON2AVC_SCALE_DIFFS, ff_on2avc_scale_diff_bits, 1, ff_on2avc_scale_diff_syms, 1, 1, -60, 0, avctx); if (ret < 0) goto vlc_fail; for (i = 1; i < 16; i++) { int idx = i - 1; - ret = ff_init_vlc_from_lengths(&c->cb_vlc[i], 9, ff_on2avc_cb_elems[idx], + ret = ff_vlc_init_from_lengths(&c->cb_vlc[i], 9, ff_on2avc_cb_elems[idx], lens, 1, syms, 2, 2, 0, 0, avctx); if (ret < 0) diff --git a/libavcodec/options.c b/libavcodec/options.c index a9b35ee1c34..5169f2e4768 100644 --- a/libavcodec/options.c +++ b/libavcodec/options.c @@ -27,6 +27,7 @@ #include "config_components.h" #include "avcodec.h" +#include "avcodec_internal.h" #include "codec_internal.h" #include "libavutil/avassert.h" #include "libavutil/internal.h" @@ -124,11 +125,6 @@ static int init_context_defaults(AVCodecContext *s, const AVCodec *codec) s->sw_pix_fmt = AV_PIX_FMT_NONE; s->sample_fmt = AV_SAMPLE_FMT_NONE; -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - s->reordered_opaque = AV_NOPTS_VALUE; -FF_ENABLE_DEPRECATION_WARNINGS -#endif if(codec && codec2->priv_data_size){ s->priv_data = av_mallocz(codec2->priv_data_size); if (!s->priv_data) @@ -172,7 +168,7 @@ void avcodec_free_context(AVCodecContext **pavctx) if (!avctx) return; - avcodec_close(avctx); + ff_codec_close(avctx); av_freep(&avctx->extradata); av_freep(&avctx->subtitle_header); @@ -180,6 +176,8 @@ void avcodec_free_context(AVCodecContext **pavctx) av_freep(&avctx->inter_matrix); av_freep(&avctx->rc_override); av_channel_layout_uninit(&avctx->ch_layout); + av_frame_side_data_free( + &avctx->decoded_side_data, &avctx->nb_decoded_side_data); av_freep(pavctx); } @@ -197,7 +195,7 @@ static const AVOption subtitle_rect_options[]={ {"w", "", SROFFSET(w), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, 0}, {"h", "", SROFFSET(h), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, 0}, {"type", "", SROFFSET(type), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, 0}, -{"flags", "", SROFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, 1, 0, "flags"}, +{"flags", "", SROFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, 1, 0, .unit = "flags"}, {"forced", "", SROFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, 1, 0}, {NULL}, }; diff --git a/libavcodec/options_table.h b/libavcodec/options_table.h index 4fea57673ad..7a2ef3474e7 100644 --- a/libavcodec/options_table.h +++ b/libavcodec/options_table.h @@ -42,6 +42,8 @@ #define D AV_OPT_FLAG_DECODING_PARAM #define CC AV_OPT_FLAG_CHILD_CONSTS +#define AR AV_OPT_TYPE_FLAG_ARRAY + #define AV_CODEC_DEFAULT_BITRATE 200*1000 static const AVOption avcodec_options[] = { @@ -50,51 +52,50 @@ static const AVOption avcodec_options[] = { {"bt", "Set video bitrate tolerance (in bits/s). In 1-pass mode, bitrate tolerance specifies how far " "ratecontrol is willing to deviate from the target average bitrate value. This is not related " "to minimum/maximum bitrate. Lowering tolerance too much has an adverse effect on quality.", - OFFSET(bit_rate_tolerance), AV_OPT_TYPE_INT, {.i64 = AV_CODEC_DEFAULT_BITRATE*20 }, 1, INT_MAX, V|E}, -{"flags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, UINT_MAX, V|A|S|E|D, "flags"}, -{"unaligned", "allow decoders to produce unaligned output", 0, AV_OPT_TYPE_CONST, { .i64 = AV_CODEC_FLAG_UNALIGNED }, INT_MIN, INT_MAX, V | D, "flags" }, -{"mv4", "use four motion vectors per macroblock (MPEG-4)", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_4MV }, INT_MIN, INT_MAX, V|E, "flags"}, -{"qpel", "use 1/4-pel motion compensation", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_QPEL }, INT_MIN, INT_MAX, V|E, "flags"}, -{"loop", "use loop filter", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_LOOP_FILTER }, INT_MIN, INT_MAX, V|E, "flags"}, -{"qscale", "use fixed qscale", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_QSCALE }, INT_MIN, INT_MAX, 0, "flags"}, + OFFSET(bit_rate_tolerance), AV_OPT_TYPE_INT, {.i64 = AV_CODEC_DEFAULT_BITRATE*20 }, 0, INT_MAX, A|V|E}, +{"flags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, UINT_MAX, V|A|S|E|D, .unit = "flags"}, +{"unaligned", "allow decoders to produce unaligned output", 0, AV_OPT_TYPE_CONST, { .i64 = AV_CODEC_FLAG_UNALIGNED }, INT_MIN, INT_MAX, V | D, .unit = "flags" }, +{"mv4", "use four motion vectors per macroblock (MPEG-4)", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_4MV }, INT_MIN, INT_MAX, V|E, .unit = "flags"}, +{"qpel", "use 1/4-pel motion compensation", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_QPEL }, INT_MIN, INT_MAX, V|E, .unit = "flags"}, +{"loop", "use loop filter", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_LOOP_FILTER }, INT_MIN, INT_MAX, V|E, .unit = "flags"}, +{"qscale", "use fixed qscale", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_QSCALE }, INT_MIN, INT_MAX, 0, .unit = "flags"}, {"recon_frame", "export reconstructed frames", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_RECON_FRAME}, .unit = "flags"}, {"copy_opaque", "propagate opaque values", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_COPY_OPAQUE}, .unit = "flags"}, {"frame_duration", "use frame durations", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_FRAME_DURATION}, .unit = "flags"}, -{"pass1", "use internal 2-pass ratecontrol in first pass mode", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_PASS1 }, INT_MIN, INT_MAX, 0, "flags"}, -{"pass2", "use internal 2-pass ratecontrol in second pass mode", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_PASS2 }, INT_MIN, INT_MAX, 0, "flags"}, -{"gray", "only decode/encode grayscale", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_GRAY }, INT_MIN, INT_MAX, V|E|D, "flags"}, -{"psnr", "error[?] variables will be set during encoding", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_PSNR }, INT_MIN, INT_MAX, V|E, "flags"}, -{"ildct", "use interlaced DCT", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_INTERLACED_DCT }, INT_MIN, INT_MAX, V|E, "flags"}, -{"low_delay", "force low delay", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_LOW_DELAY }, INT_MIN, INT_MAX, V|D|E, "flags"}, -{"global_header", "place global headers in extradata instead of every keyframe", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_GLOBAL_HEADER }, INT_MIN, INT_MAX, V|A|E, "flags"}, -{"bitexact", "use only bitexact functions (except (I)DCT)", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_BITEXACT }, INT_MIN, INT_MAX, A|V|S|D|E, "flags"}, -{"aic", "H.263 advanced intra coding / MPEG-4 AC prediction", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_AC_PRED }, INT_MIN, INT_MAX, V|E, "flags"}, -{"ilme", "interlaced motion estimation", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_INTERLACED_ME }, INT_MIN, INT_MAX, V|E, "flags"}, -{"cgop", "closed GOP", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_CLOSED_GOP }, INT_MIN, INT_MAX, V|E, "flags"}, -{"output_corrupt", "Output even potentially corrupted frames", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_OUTPUT_CORRUPT }, INT_MIN, INT_MAX, V|D, "flags"}, -{"drop_changed", "Drop frames whose parameters differ from first decoded frame", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_DROPCHANGED }, INT_MIN, INT_MAX, A|V|D, "flags"}, -{"flags2", NULL, OFFSET(flags2), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT}, 0, UINT_MAX, V|A|E|D|S, "flags2"}, -{"fast", "allow non-spec-compliant speedup tricks", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_FAST }, INT_MIN, INT_MAX, V|E, "flags2"}, -{"noout", "skip bitstream encoding", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_NO_OUTPUT }, INT_MIN, INT_MAX, V|E, "flags2"}, -{"ignorecrop", "ignore cropping information from sps", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_IGNORE_CROP }, INT_MIN, INT_MAX, V|D, "flags2"}, -{"local_header", "place global headers at every keyframe instead of in extradata", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_LOCAL_HEADER }, INT_MIN, INT_MAX, V|E, "flags2"}, -{"chunks", "Frame data might be split into multiple chunks", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_CHUNKS }, INT_MIN, INT_MAX, V|D, "flags2"}, -{"showall", "Show all frames before the first keyframe", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_SHOW_ALL }, INT_MIN, INT_MAX, V|D, "flags2"}, -{"export_mvs", "export motion vectors through frame side data", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_EXPORT_MVS}, INT_MIN, INT_MAX, V|D, "flags2"}, -{"skip_manual", "do not skip samples and export skip information as frame side data", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_SKIP_MANUAL}, INT_MIN, INT_MAX, A|D, "flags2"}, -{"ass_ro_flush_noop", "do not reset ASS ReadOrder field on flush", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_RO_FLUSH_NOOP}, INT_MIN, INT_MAX, S|D, "flags2"}, -{"icc_profiles", "generate/parse embedded ICC profiles from/to colorimetry tags", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_ICC_PROFILES}, INT_MIN, INT_MAX, S|D, "flags2"}, -{"export_side_data", "Export metadata as side data", OFFSET(export_side_data), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT}, 0, UINT_MAX, A|V|S|D|E, "export_side_data"}, -{"mvs", "export motion vectors through frame side data", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_EXPORT_DATA_MVS}, INT_MIN, INT_MAX, V|D, "export_side_data"}, -{"prft", "export Producer Reference Time through packet side data", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_EXPORT_DATA_PRFT}, INT_MIN, INT_MAX, A|V|S|E, "export_side_data"}, -{"venc_params", "export video encoding parameters through frame side data", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS}, INT_MIN, INT_MAX, V|D, "export_side_data"}, -{"film_grain", "export film grain parameters through frame side data", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_EXPORT_DATA_FILM_GRAIN}, INT_MIN, INT_MAX, V|D, "export_side_data"}, +{"pass1", "use internal 2-pass ratecontrol in first pass mode", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_PASS1 }, INT_MIN, INT_MAX, 0, .unit = "flags"}, +{"pass2", "use internal 2-pass ratecontrol in second pass mode", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_PASS2 }, INT_MIN, INT_MAX, 0, .unit = "flags"}, +{"gray", "only decode/encode grayscale", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_GRAY }, INT_MIN, INT_MAX, V|E|D, .unit = "flags"}, +{"psnr", "error[?] variables will be set during encoding", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_PSNR }, INT_MIN, INT_MAX, V|E, .unit = "flags"}, +{"ildct", "use interlaced DCT", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_INTERLACED_DCT }, INT_MIN, INT_MAX, V|E, .unit = "flags"}, +{"low_delay", "force low delay", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_LOW_DELAY }, INT_MIN, INT_MAX, V|D|E, .unit = "flags"}, +{"global_header", "place global headers in extradata instead of every keyframe", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_GLOBAL_HEADER }, INT_MIN, INT_MAX, V|A|E, .unit = "flags"}, +{"bitexact", "use only bitexact functions (except (I)DCT)", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_BITEXACT }, INT_MIN, INT_MAX, A|V|S|D|E, .unit = "flags"}, +{"aic", "H.263 advanced intra coding / MPEG-4 AC prediction", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_AC_PRED }, INT_MIN, INT_MAX, V|E, .unit = "flags"}, +{"ilme", "interlaced motion estimation", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_INTERLACED_ME }, INT_MIN, INT_MAX, V|E, .unit = "flags"}, +{"cgop", "closed GOP", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_CLOSED_GOP }, INT_MIN, INT_MAX, V|E, .unit = "flags"}, +{"output_corrupt", "Output even potentially corrupted frames", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_OUTPUT_CORRUPT }, INT_MIN, INT_MAX, V|D, .unit = "flags"}, +#if FF_API_DROPCHANGED +{"drop_changed", "Drop frames whose parameters differ from first decoded frame", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG_DROPCHANGED }, INT_MIN, INT_MAX, A|V|D | AV_OPT_FLAG_DEPRECATED, .unit = "flags"}, +#endif +{"flags2", NULL, OFFSET(flags2), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT}, 0, UINT_MAX, V|A|E|D|S, .unit = "flags2"}, +{"fast", "allow non-spec-compliant speedup tricks", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_FAST }, INT_MIN, INT_MAX, V|E, .unit = "flags2"}, +{"noout", "skip bitstream encoding", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_NO_OUTPUT }, INT_MIN, INT_MAX, V|E, .unit = "flags2"}, +{"ignorecrop", "ignore cropping information from sps", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_IGNORE_CROP }, INT_MIN, INT_MAX, V|D, .unit = "flags2"}, +{"local_header", "place global headers at every keyframe instead of in extradata", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_LOCAL_HEADER }, INT_MIN, INT_MAX, V|E, .unit = "flags2"}, +{"chunks", "Frame data might be split into multiple chunks", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_CHUNKS }, INT_MIN, INT_MAX, V|D, .unit = "flags2"}, +{"showall", "Show all frames before the first keyframe", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_SHOW_ALL }, INT_MIN, INT_MAX, V|D, .unit = "flags2"}, +{"export_mvs", "export motion vectors through frame side data", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_EXPORT_MVS}, INT_MIN, INT_MAX, V|D, .unit = "flags2"}, +{"skip_manual", "do not skip samples and export skip information as frame side data", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_SKIP_MANUAL}, INT_MIN, INT_MAX, A|D, .unit = "flags2"}, +{"ass_ro_flush_noop", "do not reset ASS ReadOrder field on flush", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_RO_FLUSH_NOOP}, INT_MIN, INT_MAX, S|D, .unit = "flags2"}, +{"icc_profiles", "generate/parse embedded ICC profiles from/to colorimetry tags", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_FLAG2_ICC_PROFILES}, INT_MIN, INT_MAX, S|D, .unit = "flags2"}, +{"export_side_data", "Export metadata as side data", OFFSET(export_side_data), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT}, 0, UINT_MAX, A|V|S|D|E, .unit = "export_side_data"}, +{"mvs", "export motion vectors through frame side data", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_EXPORT_DATA_MVS}, INT_MIN, INT_MAX, V|D, .unit = "export_side_data"}, +{"prft", "export Producer Reference Time through packet side data", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_EXPORT_DATA_PRFT}, INT_MIN, INT_MAX, A|V|S|E, .unit = "export_side_data"}, +{"venc_params", "export video encoding parameters through frame side data", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS}, INT_MIN, INT_MAX, V|D, .unit = "export_side_data"}, +{"film_grain", "export film grain parameters through frame side data", 0, AV_OPT_TYPE_CONST, {.i64 = AV_CODEC_EXPORT_DATA_FILM_GRAIN}, INT_MIN, INT_MAX, V|D, .unit = "export_side_data"}, {"time_base", NULL, OFFSET(time_base), AV_OPT_TYPE_RATIONAL, {.dbl = 0}, 0, INT_MAX}, {"g", "set the group of picture (GOP) size", OFFSET(gop_size), AV_OPT_TYPE_INT, {.i64 = 12 }, INT_MIN, INT_MAX, V|E}, {"ar", "set audio sampling rate (in Hz)", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, A|D|E}, -#if FF_API_OLD_CHANNEL_LAYOUT -{"ac", "set number of audio channels", OFFSET(channels), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, A|D|E}, -#endif {"cutoff", "set cutoff bandwidth", OFFSET(cutoff), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, A|E}, {"frame_size", NULL, OFFSET(frame_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, A|E}, {"frame_number", NULL, OFFSET(frame_num), AV_OPT_TYPE_INT64, {.i64 = DEFAULT }, INT_MIN, INT_MAX}, @@ -109,38 +110,38 @@ static const AVOption avcodec_options[] = { {"bf", "set maximum number of B-frames between non-B-frames", OFFSET(max_b_frames), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, -1, INT_MAX, V|E}, {"b_qfactor", "QP factor between P- and B-frames", OFFSET(b_quant_factor), AV_OPT_TYPE_FLOAT, {.dbl = 1.25 }, -FLT_MAX, FLT_MAX, V|E}, {"codec_tag", NULL, OFFSET(codec_tag), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX}, -{"bug", "work around not autodetected encoder bugs", OFFSET(workaround_bugs), AV_OPT_TYPE_FLAGS, {.i64 = FF_BUG_AUTODETECT }, INT_MIN, INT_MAX, V|D, "bug"}, -{"autodetect", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_AUTODETECT }, INT_MIN, INT_MAX, V|D, "bug"}, -{"xvid_ilace", "Xvid interlacing bug (autodetected if FOURCC == XVIX)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_XVID_ILACE }, INT_MIN, INT_MAX, V|D, "bug"}, -{"ump4", "(autodetected if FOURCC == UMP4)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_UMP4 }, INT_MIN, INT_MAX, V|D, "bug"}, -{"no_padding", "padding bug (autodetected)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_NO_PADDING }, INT_MIN, INT_MAX, V|D, "bug"}, -{"amv", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_AMV }, INT_MIN, INT_MAX, V|D, "bug"}, -{"qpel_chroma", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_QPEL_CHROMA }, INT_MIN, INT_MAX, V|D, "bug"}, -{"std_qpel", "old standard qpel (autodetected per FOURCC/version)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_STD_QPEL }, INT_MIN, INT_MAX, V|D, "bug"}, -{"qpel_chroma2", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_QPEL_CHROMA2 }, INT_MIN, INT_MAX, V|D, "bug"}, -{"direct_blocksize", "direct-qpel-blocksize bug (autodetected per FOURCC/version)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_DIRECT_BLOCKSIZE }, INT_MIN, INT_MAX, V|D, "bug"}, -{"edge", "edge padding bug (autodetected per FOURCC/version)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_EDGE }, INT_MIN, INT_MAX, V|D, "bug"}, -{"hpel_chroma", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_HPEL_CHROMA }, INT_MIN, INT_MAX, V|D, "bug"}, -{"dc_clip", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_DC_CLIP }, INT_MIN, INT_MAX, V|D, "bug"}, -{"ms", "work around various bugs in Microsoft's broken decoders", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_MS }, INT_MIN, INT_MAX, V|D, "bug"}, -{"trunc", "truncated frames", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_TRUNCATED}, INT_MIN, INT_MAX, V|D, "bug"}, -{"iedge", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_IEDGE }, INT_MIN, INT_MAX, V|D, "bug"}, -{"strict", "how strictly to follow the standards", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, A|V|D|E, "strict"}, -{"very", "strictly conform to a older more strict version of the spec or reference software", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_VERY_STRICT }, INT_MIN, INT_MAX, A|V|D|E, "strict"}, -{"strict", "strictly conform to all the things in the spec no matter what the consequences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_STRICT }, INT_MIN, INT_MAX, A|V|D|E, "strict"}, -{"normal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_NORMAL }, INT_MIN, INT_MAX, A|V|D|E, "strict"}, -{"unofficial", "allow unofficial extensions", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_UNOFFICIAL }, INT_MIN, INT_MAX, A|V|D|E, "strict"}, -{"experimental", "allow non-standardized experimental things", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_EXPERIMENTAL }, INT_MIN, INT_MAX, A|V|D|E, "strict"}, +{"bug", "work around not autodetected encoder bugs", OFFSET(workaround_bugs), AV_OPT_TYPE_FLAGS, {.i64 = FF_BUG_AUTODETECT }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"autodetect", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_AUTODETECT }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"xvid_ilace", "Xvid interlacing bug (autodetected if FOURCC == XVIX)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_XVID_ILACE }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"ump4", "(autodetected if FOURCC == UMP4)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_UMP4 }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"no_padding", "padding bug (autodetected)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_NO_PADDING }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"amv", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_AMV }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"qpel_chroma", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_QPEL_CHROMA }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"std_qpel", "old standard qpel (autodetected per FOURCC/version)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_STD_QPEL }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"qpel_chroma2", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_QPEL_CHROMA2 }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"direct_blocksize", "direct-qpel-blocksize bug (autodetected per FOURCC/version)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_DIRECT_BLOCKSIZE }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"edge", "edge padding bug (autodetected per FOURCC/version)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_EDGE }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"hpel_chroma", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_HPEL_CHROMA }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"dc_clip", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_DC_CLIP }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"ms", "work around various bugs in Microsoft's broken decoders", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_MS }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"trunc", "truncated frames", 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_TRUNCATED}, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"iedge", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_BUG_IEDGE }, INT_MIN, INT_MAX, V|D, .unit = "bug"}, +{"strict", "how strictly to follow the standards", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, A|V|D|E, .unit = "strict"}, +{"very", "strictly conform to a older more strict version of the spec or reference software", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_VERY_STRICT }, INT_MIN, INT_MAX, A|V|D|E, .unit = "strict"}, +{"strict", "strictly conform to all the things in the spec no matter what the consequences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_STRICT }, INT_MIN, INT_MAX, A|V|D|E, .unit = "strict"}, +{"normal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_NORMAL }, INT_MIN, INT_MAX, A|V|D|E, .unit = "strict"}, +{"unofficial", "allow unofficial extensions", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_UNOFFICIAL }, INT_MIN, INT_MAX, A|V|D|E, .unit = "strict"}, +{"experimental", "allow non-standardized experimental things", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_EXPERIMENTAL }, INT_MIN, INT_MAX, A|V|D|E, .unit = "strict"}, {"b_qoffset", "QP offset between P- and B-frames", OFFSET(b_quant_offset), AV_OPT_TYPE_FLOAT, {.dbl = 1.25 }, -FLT_MAX, FLT_MAX, V|E}, -{"err_detect", "set error detection flags", OFFSET(err_recognition), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, INT_MIN, INT_MAX, A|V|S|D|E, "err_detect"}, -{"crccheck", "verify embedded CRCs", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, A|V|S|D|E, "err_detect"}, -{"bitstream", "detect bitstream specification deviations", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BITSTREAM }, INT_MIN, INT_MAX, A|V|S|D|E, "err_detect"}, -{"buffer", "detect improper bitstream length", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BUFFER }, INT_MIN, INT_MAX, A|V|S|D|E, "err_detect"}, -{"explode", "abort decoding on minor error detection", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_EXPLODE }, INT_MIN, INT_MAX, A|V|S|D|E, "err_detect"}, -{"ignore_err", "ignore errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_IGNORE_ERR }, INT_MIN, INT_MAX, A|V|S|D|E, "err_detect"}, -{"careful", "consider things that violate the spec, are fast to check and have not been seen in the wild as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CAREFUL }, INT_MIN, INT_MAX, A|V|S|D|E, "err_detect"}, -{"compliant", "consider all spec non compliancies as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_COMPLIANT | AV_EF_CAREFUL }, INT_MIN, INT_MAX, A|V|S|D|E, "err_detect"}, -{"aggressive", "consider things that a sane encoder should not do as an error", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_AGGRESSIVE | AV_EF_COMPLIANT | AV_EF_CAREFUL}, INT_MIN, INT_MAX, A|V|S|D|E, "err_detect"}, +{"err_detect", "set error detection flags", OFFSET(err_recognition), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, INT_MIN, INT_MAX, A|V|S|D|E, .unit = "err_detect"}, +{"crccheck", "verify embedded CRCs", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, A|V|S|D|E, .unit = "err_detect"}, +{"bitstream", "detect bitstream specification deviations", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BITSTREAM }, INT_MIN, INT_MAX, A|V|S|D|E, .unit = "err_detect"}, +{"buffer", "detect improper bitstream length", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BUFFER }, INT_MIN, INT_MAX, A|V|S|D|E, .unit = "err_detect"}, +{"explode", "abort decoding on minor error detection", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_EXPLODE }, INT_MIN, INT_MAX, A|V|S|D|E, .unit = "err_detect"}, +{"ignore_err", "ignore errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_IGNORE_ERR }, INT_MIN, INT_MAX, A|V|S|D|E, .unit = "err_detect"}, +{"careful", "consider things that violate the spec, are fast to check and have not been seen in the wild as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CAREFUL }, INT_MIN, INT_MAX, A|V|S|D|E, .unit = "err_detect"}, +{"compliant", "consider all spec non compliancies as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_COMPLIANT | AV_EF_CAREFUL }, INT_MIN, INT_MAX, A|V|S|D|E, .unit = "err_detect"}, +{"aggressive", "consider things that a sane encoder should not do as an error", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_AGGRESSIVE | AV_EF_COMPLIANT | AV_EF_CAREFUL}, INT_MIN, INT_MAX, A|V|S|D|E, .unit = "err_detect"}, {"has_b_frames", NULL, OFFSET(has_b_frames), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX}, {"block_align", NULL, OFFSET(block_align), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX}, {"rc_override_count", NULL, OFFSET(rc_override_count), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX}, @@ -150,57 +151,56 @@ static const AVOption avcodec_options[] = { {"bufsize", "set ratecontrol buffer size (in bits)", OFFSET(rc_buffer_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, A|V|E}, {"i_qfactor", "QP factor between P- and I-frames", OFFSET(i_quant_factor), AV_OPT_TYPE_FLOAT, {.dbl = -0.8 }, -FLT_MAX, FLT_MAX, V|E}, {"i_qoffset", "QP offset between P- and I-frames", OFFSET(i_quant_offset), AV_OPT_TYPE_FLOAT, {.dbl = 0.0 }, -FLT_MAX, FLT_MAX, V|E}, -{"dct", "DCT algorithm", OFFSET(dct_algo), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, V|E, "dct"}, -{"auto", "autoselect a good one", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_AUTO }, INT_MIN, INT_MAX, V|E, "dct"}, -{"fastint", "fast integer", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_FASTINT }, INT_MIN, INT_MAX, V|E, "dct"}, -{"int", "accurate integer", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_INT }, INT_MIN, INT_MAX, V|E, "dct"}, -{"mmx", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_MMX }, INT_MIN, INT_MAX, V|E, "dct"}, -{"altivec", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_ALTIVEC }, INT_MIN, INT_MAX, V|E, "dct"}, -{"faan", "floating point AAN DCT", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_FAAN }, INT_MIN, INT_MAX, V|E, "dct"}, +{"dct", "DCT algorithm", OFFSET(dct_algo), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, V|E, .unit = "dct"}, +{"auto", "autoselect a good one", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_AUTO }, INT_MIN, INT_MAX, V|E, .unit = "dct"}, +{"fastint", "fast integer", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_FASTINT }, INT_MIN, INT_MAX, V|E, .unit = "dct"}, +{"int", "accurate integer", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_INT }, INT_MIN, INT_MAX, V|E, .unit = "dct"}, +{"mmx", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_MMX }, INT_MIN, INT_MAX, V|E, .unit = "dct"}, +{"altivec", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_ALTIVEC }, INT_MIN, INT_MAX, V|E, .unit = "dct"}, +{"faan", "floating point AAN DCT", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DCT_FAAN }, INT_MIN, INT_MAX, V|E, .unit = "dct"}, {"lumi_mask", "compresses bright areas stronger than medium ones", OFFSET(lumi_masking), AV_OPT_TYPE_FLOAT, {.dbl = 0 }, -FLT_MAX, FLT_MAX, V|E}, {"tcplx_mask", "temporal complexity masking", OFFSET(temporal_cplx_masking), AV_OPT_TYPE_FLOAT, {.dbl = 0 }, -FLT_MAX, FLT_MAX, V|E}, {"scplx_mask", "spatial complexity masking", OFFSET(spatial_cplx_masking), AV_OPT_TYPE_FLOAT, {.dbl = 0 }, -FLT_MAX, FLT_MAX, V|E}, {"p_mask", "inter masking", OFFSET(p_masking), AV_OPT_TYPE_FLOAT, {.dbl = 0 }, -FLT_MAX, FLT_MAX, V|E}, {"dark_mask", "compresses dark areas stronger than medium ones", OFFSET(dark_masking), AV_OPT_TYPE_FLOAT, {.dbl = 0 }, -FLT_MAX, FLT_MAX, V|E}, -{"idct", "select IDCT implementation", OFFSET(idct_algo), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, V|E|D, "idct"}, -{"auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_AUTO }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"int", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_INT }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"simple", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLE }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"simplemmx", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEMMX }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"arm", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_ARM }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"altivec", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_ALTIVEC }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"simplearm", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEARM }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"simplearmv5te", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEARMV5TE }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"simplearmv6", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEARMV6 }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"simpleneon", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLENEON }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"xvid", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_XVID }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"xvidmmx", "deprecated, for compatibility only", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_XVID }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"faani", "floating point AAN IDCT", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_FAAN }, INT_MIN, INT_MAX, V|D|E, "idct"}, -{"simpleauto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEAUTO }, INT_MIN, INT_MAX, V|E|D, "idct"}, -{"slice_count", NULL, OFFSET(slice_count), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX}, -{"ec", "set error concealment strategy", OFFSET(error_concealment), AV_OPT_TYPE_FLAGS, {.i64 = 3 }, INT_MIN, INT_MAX, V|D, "ec"}, -{"guess_mvs", "iterative motion vector (MV) search (slow)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_EC_GUESS_MVS }, INT_MIN, INT_MAX, V|D, "ec"}, -{"deblock", "use strong deblock filter for damaged MBs", 0, AV_OPT_TYPE_CONST, {.i64 = FF_EC_DEBLOCK }, INT_MIN, INT_MAX, V|D, "ec"}, -{"favor_inter", "favor predicting from the previous frame", 0, AV_OPT_TYPE_CONST, {.i64 = FF_EC_FAVOR_INTER }, INT_MIN, INT_MAX, V|D, "ec"}, +{"idct", "select IDCT implementation", OFFSET(idct_algo), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, V|E|D, .unit = "idct"}, +{"auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_AUTO }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"int", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_INT }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"simple", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLE }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"simplemmx", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEMMX }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"arm", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_ARM }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"altivec", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_ALTIVEC }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"simplearm", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEARM }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"simplearmv5te", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEARMV5TE }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"simplearmv6", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEARMV6 }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"simpleneon", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLENEON }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"xvid", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_XVID }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"xvidmmx", "deprecated, for compatibility only", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_XVID }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"faani", "floating point AAN IDCT", 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_FAAN }, INT_MIN, INT_MAX, V|D|E, .unit = "idct"}, +{"simpleauto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_IDCT_SIMPLEAUTO }, INT_MIN, INT_MAX, V|E|D, .unit = "idct"}, +{"ec", "set error concealment strategy", OFFSET(error_concealment), AV_OPT_TYPE_FLAGS, {.i64 = 3 }, INT_MIN, INT_MAX, V|D, .unit = "ec"}, +{"guess_mvs", "iterative motion vector (MV) search (slow)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_EC_GUESS_MVS }, INT_MIN, INT_MAX, V|D, .unit = "ec"}, +{"deblock", "use strong deblock filter for damaged MBs", 0, AV_OPT_TYPE_CONST, {.i64 = FF_EC_DEBLOCK }, INT_MIN, INT_MAX, V|D, .unit = "ec"}, +{"favor_inter", "favor predicting from the previous frame", 0, AV_OPT_TYPE_CONST, {.i64 = FF_EC_FAVOR_INTER }, INT_MIN, INT_MAX, V|D, .unit = "ec"}, {"bits_per_coded_sample", NULL, OFFSET(bits_per_coded_sample), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX}, {"aspect", "sample aspect ratio", OFFSET(sample_aspect_ratio), AV_OPT_TYPE_RATIONAL, {.dbl = 0}, 0, 10, V|E}, {"sar", "sample aspect ratio", OFFSET(sample_aspect_ratio), AV_OPT_TYPE_RATIONAL, {.dbl = 0}, 0, 10, V|E}, -{"debug", "print specific debug info", OFFSET(debug), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, INT_MAX, V|A|S|E|D, "debug"}, -{"pict", "picture info", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_PICT_INFO }, INT_MIN, INT_MAX, V|D, "debug"}, -{"rc", "rate control", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_RC }, INT_MIN, INT_MAX, V|E, "debug"}, -{"bitstream", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_BITSTREAM }, INT_MIN, INT_MAX, V|D, "debug"}, -{"mb_type", "macroblock (MB) type", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_MB_TYPE }, INT_MIN, INT_MAX, V|D, "debug"}, -{"qp", "per-block quantization parameter (QP)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_QP }, INT_MIN, INT_MAX, V|D, "debug"}, -{"dct_coeff", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_DCT_COEFF }, INT_MIN, INT_MAX, V|D, "debug"}, -{"green_metadata", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_GREEN_MD }, INT_MIN, INT_MAX, V|D, "debug"}, -{"skip", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_SKIP }, INT_MIN, INT_MAX, V|D, "debug"}, -{"startcode", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_STARTCODE }, INT_MIN, INT_MAX, V|D, "debug"}, -{"er", "error recognition", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_ER }, INT_MIN, INT_MAX, V|D, "debug"}, -{"mmco", "memory management control operations (H.264)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_MMCO }, INT_MIN, INT_MAX, V|D, "debug"}, -{"bugs", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_BUGS }, INT_MIN, INT_MAX, V|D, "debug"}, -{"buffers", "picture buffer allocations", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_BUFFERS }, INT_MIN, INT_MAX, V|D, "debug"}, -{"thread_ops", "threading operations", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_THREADS }, INT_MIN, INT_MAX, V|A|D, "debug"}, -{"nomc", "skip motion compensation", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_NOMC }, INT_MIN, INT_MAX, V|A|D, "debug"}, +{"debug", "print specific debug info", OFFSET(debug), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, INT_MAX, V|A|S|E|D, .unit = "debug"}, +{"pict", "picture info", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_PICT_INFO }, INT_MIN, INT_MAX, V|D, .unit = "debug"}, +{"rc", "rate control", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_RC }, INT_MIN, INT_MAX, V|E, .unit = "debug"}, +{"bitstream", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_BITSTREAM }, INT_MIN, INT_MAX, V|D, .unit = "debug"}, +{"mb_type", "macroblock (MB) type", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_MB_TYPE }, INT_MIN, INT_MAX, V|D, .unit = "debug"}, +{"qp", "per-block quantization parameter (QP)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_QP }, INT_MIN, INT_MAX, V|D, .unit = "debug"}, +{"dct_coeff", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_DCT_COEFF }, INT_MIN, INT_MAX, V|D, .unit = "debug"}, +{"green_metadata", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_GREEN_MD }, INT_MIN, INT_MAX, V|D, .unit = "debug"}, +{"skip", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_SKIP }, INT_MIN, INT_MAX, V|D, .unit = "debug"}, +{"startcode", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_STARTCODE }, INT_MIN, INT_MAX, V|D, .unit = "debug"}, +{"er", "error recognition", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_ER }, INT_MIN, INT_MAX, V|D, .unit = "debug"}, +{"mmco", "memory management control operations (H.264)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_MMCO }, INT_MIN, INT_MAX, V|D, .unit = "debug"}, +{"bugs", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_BUGS }, INT_MIN, INT_MAX, V|D, .unit = "debug"}, +{"buffers", "picture buffer allocations", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_BUFFERS }, INT_MIN, INT_MAX, V|D, .unit = "debug"}, +{"thread_ops", "threading operations", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_THREADS }, INT_MIN, INT_MAX, V|A|D, .unit = "debug"}, +{"nomc", "skip motion compensation", 0, AV_OPT_TYPE_CONST, {.i64 = FF_DEBUG_NOMC }, INT_MIN, INT_MAX, V|A|D, .unit = "debug"}, {"dia_size", "diamond type & size for motion estimation", OFFSET(dia_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E}, {"last_pred", "amount of motion predictors from the previous frame", OFFSET(last_predictor_count), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E}, {"pre_dia_size", "diamond type & size for motion estimation pre-pass", OFFSET(pre_dia_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E}, @@ -208,58 +208,58 @@ static const AVOption avcodec_options[] = { {"me_range", "limit motion vectors range (1023 for DivX player)", OFFSET(me_range), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E}, {"global_quality", NULL, OFFSET(global_quality), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|A|E}, {"slice_flags", NULL, OFFSET(slice_flags), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX}, -{"mbd", "macroblock decision algorithm (high quality mode)", OFFSET(mb_decision), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, 2, V|E, "mbd"}, -{"simple", "use mbcmp", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MB_DECISION_SIMPLE }, INT_MIN, INT_MAX, V|E, "mbd"}, -{"bits", "use fewest bits", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MB_DECISION_BITS }, INT_MIN, INT_MAX, V|E, "mbd"}, -{"rd", "use best rate distortion", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MB_DECISION_RD }, INT_MIN, INT_MAX, V|E, "mbd"}, +{"mbd", "macroblock decision algorithm (high quality mode)", OFFSET(mb_decision), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, 2, V|E, .unit = "mbd"}, +{"simple", "use mbcmp", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MB_DECISION_SIMPLE }, INT_MIN, INT_MAX, V|E, .unit = "mbd"}, +{"bits", "use fewest bits", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MB_DECISION_BITS }, INT_MIN, INT_MAX, V|E, .unit = "mbd"}, +{"rd", "use best rate distortion", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MB_DECISION_RD }, INT_MIN, INT_MAX, V|E, .unit = "mbd"}, {"rc_init_occupancy", "number of bits which should be loaded into the rc buffer before decoding starts", OFFSET(rc_initial_buffer_occupancy), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E}, -{"threads", "set the number of threads", OFFSET(thread_count), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, INT_MAX, V|A|E|D, "threads"}, -{"auto", "autodetect a suitable number of threads to use", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, INT_MIN, INT_MAX, V|E|D, "threads"}, +{"threads", "set the number of threads", OFFSET(thread_count), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, INT_MAX, V|A|E|D, .unit = "threads"}, +{"auto", "autodetect a suitable number of threads to use", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, INT_MIN, INT_MAX, V|E|D, .unit = "threads"}, {"dc", "intra_dc_precision", OFFSET(intra_dc_precision), AV_OPT_TYPE_INT, {.i64 = 0 }, -8, 16, V|E}, {"nssew", "nsse weight", OFFSET(nsse_weight), AV_OPT_TYPE_INT, {.i64 = 8 }, INT_MIN, INT_MAX, V|E}, {"skip_top", "number of macroblock rows at the top which are skipped", OFFSET(skip_top), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|D}, {"skip_bottom", "number of macroblock rows at the bottom which are skipped", OFFSET(skip_bottom), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|D}, -{"profile", NULL, OFFSET(profile), AV_OPT_TYPE_INT, {.i64 = FF_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, V|A|E|CC, "avctx.profile"}, -{"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, V|A|E, "avctx.profile"}, -{"main10", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_PROFILE_HEVC_MAIN_10 }, INT_MIN, INT_MAX, V|E, "avctx.profile"}, -{"level", NULL, OFFSET(level), AV_OPT_TYPE_INT, {.i64 = FF_LEVEL_UNKNOWN }, INT_MIN, INT_MAX, V|A|E|CC, "avctx.level"}, -{"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LEVEL_UNKNOWN }, INT_MIN, INT_MAX, V|A|E, "avctx.level"}, +{"profile", NULL, OFFSET(profile), AV_OPT_TYPE_INT, {.i64 = AV_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, V|A|E|CC, .unit = "avctx.profile"}, +{"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, V|A|E, .unit = "avctx.profile"}, +{"main10", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_PROFILE_HEVC_MAIN_10 }, INT_MIN, INT_MAX, V|E, .unit = "avctx.profile"}, +{"level", "encoding level, usually corresponding to the profile level, codec-specific", OFFSET(level), AV_OPT_TYPE_INT, {.i64 = FF_LEVEL_UNKNOWN }, INT_MIN, INT_MAX, V|A|E|CC, .unit = "avctx.level"}, +{"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_LEVEL_UNKNOWN }, INT_MIN, INT_MAX, V|A|E, .unit = "avctx.level"}, {"lowres", "decode at 1= 1/2, 2=1/4, 3=1/8 resolutions", OFFSET(lowres), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, V|A|D}, -{"cmp", "full-pel ME compare function", OFFSET(me_cmp), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"subcmp", "sub-pel ME compare function", OFFSET(me_sub_cmp), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"mbcmp", "macroblock compare function", OFFSET(mb_cmp), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"ildctcmp", "interlaced DCT compare function", OFFSET(ildct_cmp), AV_OPT_TYPE_INT, {.i64 = FF_CMP_VSAD }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"precmp", "pre motion estimation compare function", OFFSET(me_pre_cmp), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"sad", "sum of absolute differences, fast", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_SAD }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"sse", "sum of squared errors", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_SSE }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"satd", "sum of absolute Hadamard transformed differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_SATD }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"dct", "sum of absolute DCT transformed differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_DCT }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"psnr", "sum of squared quantization errors (avoid, low quality)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_PSNR }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"bit", "number of bits needed for the block", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_BIT }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"rd", "rate distortion optimal, slow", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_RD }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"zero", "0", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_ZERO }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"vsad", "sum of absolute vertical differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_VSAD }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"vsse", "sum of squared vertical differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_VSSE }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"nsse", "noise preserving sum of squared differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_NSSE }, INT_MIN, INT_MAX, V|E, "cmp_func"}, +{"cmp", "full-pel ME compare function", OFFSET(me_cmp), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"subcmp", "sub-pel ME compare function", OFFSET(me_sub_cmp), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"mbcmp", "macroblock compare function", OFFSET(mb_cmp), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"ildctcmp", "interlaced DCT compare function", OFFSET(ildct_cmp), AV_OPT_TYPE_INT, {.i64 = FF_CMP_VSAD }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"precmp", "pre motion estimation compare function", OFFSET(me_pre_cmp), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"sad", "sum of absolute differences, fast", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_SAD }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"sse", "sum of squared errors", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_SSE }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"satd", "sum of absolute Hadamard transformed differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_SATD }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"dct", "sum of absolute DCT transformed differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_DCT }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"psnr", "sum of squared quantization errors (avoid, low quality)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_PSNR }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"bit", "number of bits needed for the block", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_BIT }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"rd", "rate distortion optimal, slow", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_RD }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"zero", "0", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_ZERO }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"vsad", "sum of absolute vertical differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_VSAD }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"vsse", "sum of squared vertical differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_VSSE }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"nsse", "noise preserving sum of squared differences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_NSSE }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, #if CONFIG_SNOW_ENCODER -{"w53", "5/3 wavelet, only used in snow", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_W53 }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"w97", "9/7 wavelet, only used in snow", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_W97 }, INT_MIN, INT_MAX, V|E, "cmp_func"}, +{"w53", "5/3 wavelet, only used in snow", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_W53 }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"w97", "9/7 wavelet, only used in snow", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_W97 }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, #endif -{"dctmax", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_DCTMAX }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"chroma", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_CHROMA }, INT_MIN, INT_MAX, V|E, "cmp_func"}, -{"msad", "sum of absolute differences, median predicted", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_MEDIAN_SAD }, INT_MIN, INT_MAX, V|E, "cmp_func"}, +{"dctmax", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_DCTMAX }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"chroma", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_CHROMA }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, +{"msad", "sum of absolute differences, median predicted", 0, AV_OPT_TYPE_CONST, {.i64 = FF_CMP_MEDIAN_SAD }, INT_MIN, INT_MAX, V|E, .unit = "cmp_func"}, {"mblmin", "minimum macroblock Lagrange factor (VBR)", OFFSET(mb_lmin), AV_OPT_TYPE_INT, {.i64 = FF_QP2LAMBDA * 2 }, 1, FF_LAMBDA_MAX, V|E}, {"mblmax", "maximum macroblock Lagrange factor (VBR)", OFFSET(mb_lmax), AV_OPT_TYPE_INT, {.i64 = FF_QP2LAMBDA * 31 }, 1, FF_LAMBDA_MAX, V|E}, -{"skip_loop_filter", "skip loop filtering process for the selected frames", OFFSET(skip_loop_filter), AV_OPT_TYPE_INT, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, "avdiscard"}, -{"skip_idct" , "skip IDCT/dequantization for the selected frames", OFFSET(skip_idct), AV_OPT_TYPE_INT, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, "avdiscard"}, -{"skip_frame" , "skip decoding for the selected frames", OFFSET(skip_frame), AV_OPT_TYPE_INT, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, "avdiscard"}, -{"none" , "discard no frame", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONE }, INT_MIN, INT_MAX, V|D, "avdiscard"}, -{"default" , "discard useless frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, "avdiscard"}, -{"noref" , "discard all non-reference frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONREF }, INT_MIN, INT_MAX, V|D, "avdiscard"}, -{"bidir" , "discard all bidirectional frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_BIDIR }, INT_MIN, INT_MAX, V|D, "avdiscard"}, -{"nokey" , "discard all frames except keyframes", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONKEY }, INT_MIN, INT_MAX, V|D, "avdiscard"}, -{"nointra" , "discard all frames except I frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONINTRA}, INT_MIN, INT_MAX, V|D, "avdiscard"}, -{"all" , "discard all frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_ALL }, INT_MIN, INT_MAX, V|D, "avdiscard"}, +{"skip_loop_filter", "skip loop filtering process for the selected frames", OFFSET(skip_loop_filter), AV_OPT_TYPE_INT, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, +{"skip_idct" , "skip IDCT/dequantization for the selected frames", OFFSET(skip_idct), AV_OPT_TYPE_INT, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, +{"skip_frame" , "skip decoding for the selected frames", OFFSET(skip_frame), AV_OPT_TYPE_INT, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, +{"none" , "discard no frame", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONE }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, +{"default" , "discard useless frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, +{"noref" , "discard all non-reference frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONREF }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, +{"bidir" , "discard all bidirectional frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_BIDIR }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, +{"nointra" , "discard all frames except I frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONINTRA}, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, +{"nokey" , "discard all frames except keyframes", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONKEY }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, +{"all" , "discard all frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_ALL }, INT_MIN, INT_MAX, V|D, .unit = "avdiscard"}, {"bidir_refine", "refine the two motion vectors used in bidirectional macroblocks", OFFSET(bidir_refine), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, 4, V|E}, {"keyint_min", "minimum interval between IDR-frames", OFFSET(keyint_min), AV_OPT_TYPE_INT, {.i64 = 25 }, INT_MIN, INT_MAX, V|E}, {"refs", "reference frames to consider for motion compensation", OFFSET(refs), AV_OPT_TYPE_INT, {.i64 = 1 }, INT_MIN, INT_MAX, V|E}, @@ -267,136 +267,146 @@ static const AVOption avcodec_options[] = { {"mv0_threshold", NULL, OFFSET(mv0_threshold), AV_OPT_TYPE_INT, {.i64 = 256 }, 0, INT_MAX, V|E}, {"compression_level", NULL, OFFSET(compression_level), AV_OPT_TYPE_INT, {.i64 = FF_COMPRESSION_DEFAULT }, INT_MIN, INT_MAX, V|A|E}, {"bits_per_raw_sample", NULL, OFFSET(bits_per_raw_sample), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX}, -{"ch_layout", NULL, OFFSET(ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str = NULL }, 0, 0, A|E|D, "ch_layout"}, -#if FF_API_OLD_CHANNEL_LAYOUT -{"channel_layout", NULL, OFFSET(channel_layout), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64 = DEFAULT }, 0, UINT64_MAX, A|E|D, "channel_layout"}, -{"request_channel_layout", NULL, OFFSET(request_channel_layout), AV_OPT_TYPE_CHANNEL_LAYOUT, {.i64 = DEFAULT }, 0, UINT64_MAX, A|D, "request_channel_layout"}, -#endif +{"ch_layout", NULL, OFFSET(ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str = NULL }, 0, 0, A|E|D, .unit = "ch_layout"}, {"rc_max_vbv_use", NULL, OFFSET(rc_max_available_vbv_use), AV_OPT_TYPE_FLOAT, {.dbl = 0 }, 0.0, FLT_MAX, V|E}, {"rc_min_vbv_use", NULL, OFFSET(rc_min_vbv_overflow_use), AV_OPT_TYPE_FLOAT, {.dbl = 3 }, 0.0, FLT_MAX, V|E}, +#if FF_API_TICKS_PER_FRAME {"ticks_per_frame", NULL, OFFSET(ticks_per_frame), AV_OPT_TYPE_INT, {.i64 = 1 }, 1, INT_MAX, A|V|E|D}, -{"color_primaries", "color primaries", OFFSET(color_primaries), AV_OPT_TYPE_INT, {.i64 = AVCOL_PRI_UNSPECIFIED }, 1, INT_MAX, V|E|D, "color_primaries_type"}, -{"bt709", "BT.709", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_BT709 }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"unknown", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"bt470m", "BT.470 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_BT470M }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"bt470bg", "BT.470 BG", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_BT470BG }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"smpte170m", "SMPTE 170 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_SMPTE170M }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"smpte240m", "SMPTE 240 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_SMPTE240M }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"film", "Film", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_FILM }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"bt2020", "BT.2020", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_BT2020 }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"smpte428", "SMPTE 428-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_SMPTE428 }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"smpte428_1", "SMPTE 428-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_SMPTE428 }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"smpte431", "SMPTE 431-2", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_SMPTE431 }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"smpte432", "SMPTE 422-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_SMPTE432 }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"jedec-p22", "JEDEC P22", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_JEDEC_P22 }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"ebu3213", "EBU 3213-E", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_EBU3213 }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"unspecified", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, "color_primaries_type"}, -{"color_trc", "color transfer characteristics", OFFSET(color_trc), AV_OPT_TYPE_INT, {.i64 = AVCOL_TRC_UNSPECIFIED }, 1, INT_MAX, V|E|D, "color_trc_type"}, -{"bt709", "BT.709", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT709 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"unknown", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"gamma22", "BT.470 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_GAMMA22 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"gamma28", "BT.470 BG", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_GAMMA28 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"smpte170m", "SMPTE 170 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE170M }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"smpte240m", "SMPTE 240 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE240M }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"linear", "Linear", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LINEAR }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"log100", "Log", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LOG }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"log316", "Log square root", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LOG_SQRT }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"iec61966-2-4", "IEC 61966-2-4", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_IEC61966_2_4 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"bt1361e", "BT.1361", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT1361_ECG }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"iec61966-2-1", "IEC 61966-2-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_IEC61966_2_1 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"bt2020-10", "BT.2020 - 10 bit", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_10 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"bt2020-12", "BT.2020 - 12 bit", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_12 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"smpte2084", "SMPTE 2084", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE2084 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"smpte428", "SMPTE 428-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE428 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"arib-std-b67", "ARIB STD-B67", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_ARIB_STD_B67 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"unspecified", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"log", "Log", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LOG }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"log_sqrt", "Log square root", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LOG_SQRT }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"iec61966_2_4", "IEC 61966-2-4", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_IEC61966_2_4 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"bt1361", "BT.1361", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT1361_ECG }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"iec61966_2_1", "IEC 61966-2-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_IEC61966_2_1 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"bt2020_10bit", "BT.2020 - 10 bit", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_10 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"bt2020_12bit", "BT.2020 - 12 bit", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_12 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"smpte428_1", "SMPTE 428-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE428 }, INT_MIN, INT_MAX, V|E|D, "color_trc_type"}, -{"colorspace", "color space", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64 = AVCOL_SPC_UNSPECIFIED }, 0, INT_MAX, V|E|D, "colorspace_type"}, -{"rgb", "RGB", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_RGB }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"bt709", "BT.709", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT709 }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"unknown", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"fcc", "FCC", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_FCC }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"bt470bg", "BT.470 BG", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT470BG }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"smpte170m", "SMPTE 170 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_SMPTE170M }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"smpte240m", "SMPTE 240 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_SMPTE240M }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"ycgco", "YCGCO", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_YCGCO }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"bt2020nc", "BT.2020 NCL", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT2020_NCL }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"bt2020c", "BT.2020 CL", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT2020_CL }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"smpte2085", "SMPTE 2085", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_SMPTE2085 }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"chroma-derived-nc", "Chroma-derived NCL", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_CHROMA_DERIVED_NCL }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"chroma-derived-c", "Chroma-derived CL", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_CHROMA_DERIVED_CL }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"ictcp", "ICtCp", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_ICTCP }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"unspecified", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"ycocg", "YCGCO", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_YCGCO }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"bt2020_ncl", "BT.2020 NCL", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT2020_NCL }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"bt2020_cl", "BT.2020 CL", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT2020_CL }, INT_MIN, INT_MAX, V|E|D, "colorspace_type"}, -{"color_range", "color range", OFFSET(color_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, INT_MAX, V|E|D, "color_range_type"}, -{"unknown", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, "color_range_type"}, -{"tv", "MPEG (219*2^(n-8))", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG }, INT_MIN, INT_MAX, V|E|D, "color_range_type"}, -{"pc", "JPEG (2^n-1)", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG }, INT_MIN, INT_MAX, V|E|D, "color_range_type"}, -{"unspecified", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, "color_range_type"}, -{"mpeg", "MPEG (219*2^(n-8))", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG }, INT_MIN, INT_MAX, V|E|D, "color_range_type"}, -{"jpeg", "JPEG (2^n-1)", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG }, INT_MIN, INT_MAX, V|E|D, "color_range_type"}, -{"chroma_sample_location", "chroma sample location", OFFSET(chroma_sample_location), AV_OPT_TYPE_INT, {.i64 = AVCHROMA_LOC_UNSPECIFIED }, 0, INT_MAX, V|E|D, "chroma_sample_location_type"}, -{"unknown", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"}, -{"left", "Left", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_LEFT }, INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"}, -{"center", "Center", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_CENTER }, INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"}, -{"topleft", "Top-left", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_TOPLEFT }, INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"}, -{"top", "Top", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_TOP }, INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"}, -{"bottomleft", "Bottom-left", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_BOTTOMLEFT }, INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"}, -{"bottom", "Bottom", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_BOTTOM }, INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"}, -{"unspecified", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, "chroma_sample_location_type"}, +#endif +{"color_primaries", "color primaries", OFFSET(color_primaries), AV_OPT_TYPE_INT, {.i64 = AVCOL_PRI_UNSPECIFIED }, 1, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"bt709", "BT.709", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_BT709 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"unknown", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"bt470m", "BT.470 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_BT470M }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"bt470bg", "BT.470 BG", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_BT470BG }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"smpte170m", "SMPTE 170 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_SMPTE170M }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"smpte240m", "SMPTE 240 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_SMPTE240M }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"film", "Film", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_FILM }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"bt2020", "BT.2020", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_BT2020 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"smpte428", "SMPTE 428-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_SMPTE428 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"smpte428_1", "SMPTE 428-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_SMPTE428 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"smpte431", "SMPTE 431-2", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_SMPTE431 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"smpte432", "SMPTE 422-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_SMPTE432 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"jedec-p22", "JEDEC P22", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_JEDEC_P22 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"ebu3213", "EBU 3213-E", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_EBU3213 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"unspecified", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, .unit = "color_primaries_type"}, +{"color_trc", "color transfer characteristics", OFFSET(color_trc), AV_OPT_TYPE_INT, {.i64 = AVCOL_TRC_UNSPECIFIED }, 1, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"bt709", "BT.709", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT709 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"unknown", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"gamma22", "BT.470 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_GAMMA22 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"gamma28", "BT.470 BG", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_GAMMA28 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"smpte170m", "SMPTE 170 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE170M }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"smpte240m", "SMPTE 240 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE240M }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"linear", "Linear", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LINEAR }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"log100", "Log", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LOG }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"log316", "Log square root", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LOG_SQRT }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"iec61966-2-4", "IEC 61966-2-4", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_IEC61966_2_4 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"bt1361e", "BT.1361", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT1361_ECG }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"iec61966-2-1", "IEC 61966-2-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_IEC61966_2_1 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"bt2020-10", "BT.2020 - 10 bit", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_10 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"bt2020-12", "BT.2020 - 12 bit", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_12 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"smpte2084", "SMPTE 2084", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE2084 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"smpte428", "SMPTE 428-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE428 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"arib-std-b67", "ARIB STD-B67", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_ARIB_STD_B67 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"unspecified", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"log", "Log", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LOG }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"log_sqrt", "Log square root", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_LOG_SQRT }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"iec61966_2_4", "IEC 61966-2-4", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_IEC61966_2_4 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"bt1361", "BT.1361", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT1361_ECG }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"iec61966_2_1", "IEC 61966-2-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_IEC61966_2_1 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"bt2020_10bit", "BT.2020 - 10 bit", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_10 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"bt2020_12bit", "BT.2020 - 12 bit", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_12 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"smpte428_1", "SMPTE 428-1", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_SMPTE428 }, INT_MIN, INT_MAX, V|E|D, .unit = "color_trc_type"}, +{"colorspace", "color space", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64 = AVCOL_SPC_UNSPECIFIED }, 0, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"rgb", "RGB", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_RGB }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"bt709", "BT.709", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT709 }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"unknown", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"fcc", "FCC", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_FCC }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"bt470bg", "BT.470 BG", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT470BG }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"smpte170m", "SMPTE 170 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_SMPTE170M }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"smpte240m", "SMPTE 240 M", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_SMPTE240M }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"ycgco", "YCGCO", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_YCGCO }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"bt2020nc", "BT.2020 NCL", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT2020_NCL }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"bt2020c", "BT.2020 CL", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT2020_CL }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"smpte2085", "SMPTE 2085", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_SMPTE2085 }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"chroma-derived-nc", "Chroma-derived NCL", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_CHROMA_DERIVED_NCL }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"chroma-derived-c", "Chroma-derived CL", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_CHROMA_DERIVED_CL }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"ictcp", "ICtCp", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_ICTCP }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"unspecified", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"ycocg", "YCGCO", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_YCGCO }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"bt2020_ncl", "BT.2020 NCL", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT2020_NCL }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"bt2020_cl", "BT.2020 CL", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT2020_CL }, INT_MIN, INT_MAX, V|E|D, .unit = "colorspace_type"}, +{"color_range", "color range", OFFSET(color_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, INT_MAX, V|E|D, .unit = "color_range_type"}, +{"unknown", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, .unit = "color_range_type"}, +{"tv", "MPEG (219*2^(n-8))", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG }, INT_MIN, INT_MAX, V|E|D, .unit = "color_range_type"}, +{"pc", "JPEG (2^n-1)", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG }, INT_MIN, INT_MAX, V|E|D, .unit = "color_range_type"}, +{"unspecified", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, .unit = "color_range_type"}, +{"mpeg", "MPEG (219*2^(n-8))", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG }, INT_MIN, INT_MAX, V|E|D, .unit = "color_range_type"}, +{"jpeg", "JPEG (2^n-1)", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG }, INT_MIN, INT_MAX, V|E|D, .unit = "color_range_type"}, +{"limited", "MPEG (219*2^(n-8))", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG }, INT_MIN, INT_MAX, V|E|D, .unit = "color_range_type"}, +{"full", "JPEG (2^n-1)", 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG }, INT_MIN, INT_MAX, V|E|D, .unit = "color_range_type"}, +{"chroma_sample_location", "chroma sample location", OFFSET(chroma_sample_location), AV_OPT_TYPE_INT, {.i64 = AVCHROMA_LOC_UNSPECIFIED }, 0, INT_MAX, V|E|D, .unit = "chroma_sample_location_type"}, +{"unknown", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, .unit = "chroma_sample_location_type"}, +{"left", "Left", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_LEFT }, INT_MIN, INT_MAX, V|E|D, .unit = "chroma_sample_location_type"}, +{"center", "Center", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_CENTER }, INT_MIN, INT_MAX, V|E|D, .unit = "chroma_sample_location_type"}, +{"topleft", "Top-left", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_TOPLEFT }, INT_MIN, INT_MAX, V|E|D, .unit = "chroma_sample_location_type"}, +{"top", "Top", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_TOP }, INT_MIN, INT_MAX, V|E|D, .unit = "chroma_sample_location_type"}, +{"bottomleft", "Bottom-left", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_BOTTOMLEFT }, INT_MIN, INT_MAX, V|E|D, .unit = "chroma_sample_location_type"}, +{"bottom", "Bottom", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_BOTTOM }, INT_MIN, INT_MAX, V|E|D, .unit = "chroma_sample_location_type"}, +{"unspecified", "Unspecified", 0, AV_OPT_TYPE_CONST, {.i64 = AVCHROMA_LOC_UNSPECIFIED }, INT_MIN, INT_MAX, V|E|D, .unit = "chroma_sample_location_type"}, {"log_level_offset", "set the log level offset", OFFSET(log_level_offset), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX }, {"slices", "set the number of slices, used in parallelized encoding", OFFSET(slices), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, V|E}, -{"thread_type", "select multithreading type", OFFSET(thread_type), AV_OPT_TYPE_FLAGS, {.i64 = FF_THREAD_SLICE|FF_THREAD_FRAME }, 0, INT_MAX, V|A|E|D, "thread_type"}, -{"slice", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_THREAD_SLICE }, INT_MIN, INT_MAX, V|E|D, "thread_type"}, -{"frame", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_THREAD_FRAME }, INT_MIN, INT_MAX, V|E|D, "thread_type"}, -{"audio_service_type", "audio service type", OFFSET(audio_service_type), AV_OPT_TYPE_INT, {.i64 = AV_AUDIO_SERVICE_TYPE_MAIN }, 0, AV_AUDIO_SERVICE_TYPE_NB-1, A|E, "audio_service_type"}, -{"ma", "Main Audio Service", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_MAIN }, INT_MIN, INT_MAX, A|E, "audio_service_type"}, -{"ef", "Effects", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_EFFECTS }, INT_MIN, INT_MAX, A|E, "audio_service_type"}, -{"vi", "Visually Impaired", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED }, INT_MIN, INT_MAX, A|E, "audio_service_type"}, -{"hi", "Hearing Impaired", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED }, INT_MIN, INT_MAX, A|E, "audio_service_type"}, -{"di", "Dialogue", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_DIALOGUE }, INT_MIN, INT_MAX, A|E, "audio_service_type"}, -{"co", "Commentary", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_COMMENTARY }, INT_MIN, INT_MAX, A|E, "audio_service_type"}, -{"em", "Emergency", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_EMERGENCY }, INT_MIN, INT_MAX, A|E, "audio_service_type"}, -{"vo", "Voice Over", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_VOICE_OVER }, INT_MIN, INT_MAX, A|E, "audio_service_type"}, -{"ka", "Karaoke", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_KARAOKE }, INT_MIN, INT_MAX, A|E, "audio_service_type"}, -{"request_sample_fmt", "sample format audio decoders should prefer", OFFSET(request_sample_fmt), AV_OPT_TYPE_SAMPLE_FMT, {.i64=AV_SAMPLE_FMT_NONE}, -1, INT_MAX, A|D, "request_sample_fmt"}, +{"thread_type", "select multithreading type", OFFSET(thread_type), AV_OPT_TYPE_FLAGS, {.i64 = FF_THREAD_SLICE|FF_THREAD_FRAME }, 0, INT_MAX, V|A|E|D, .unit = "thread_type"}, +{"slice", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_THREAD_SLICE }, INT_MIN, INT_MAX, V|E|D, .unit = "thread_type"}, +{"frame", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_THREAD_FRAME }, INT_MIN, INT_MAX, V|E|D, .unit = "thread_type"}, +{"audio_service_type", "audio service type", OFFSET(audio_service_type), AV_OPT_TYPE_INT, {.i64 = AV_AUDIO_SERVICE_TYPE_MAIN }, 0, AV_AUDIO_SERVICE_TYPE_NB-1, A|E, .unit = "audio_service_type"}, +{"ma", "Main Audio Service", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_MAIN }, INT_MIN, INT_MAX, A|E, .unit = "audio_service_type"}, +{"ef", "Effects", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_EFFECTS }, INT_MIN, INT_MAX, A|E, .unit = "audio_service_type"}, +{"vi", "Visually Impaired", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED }, INT_MIN, INT_MAX, A|E, .unit = "audio_service_type"}, +{"hi", "Hearing Impaired", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED }, INT_MIN, INT_MAX, A|E, .unit = "audio_service_type"}, +{"di", "Dialogue", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_DIALOGUE }, INT_MIN, INT_MAX, A|E, .unit = "audio_service_type"}, +{"co", "Commentary", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_COMMENTARY }, INT_MIN, INT_MAX, A|E, .unit = "audio_service_type"}, +{"em", "Emergency", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_EMERGENCY }, INT_MIN, INT_MAX, A|E, .unit = "audio_service_type"}, +{"vo", "Voice Over", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_VOICE_OVER }, INT_MIN, INT_MAX, A|E, .unit = "audio_service_type"}, +{"ka", "Karaoke", 0, AV_OPT_TYPE_CONST, {.i64 = AV_AUDIO_SERVICE_TYPE_KARAOKE }, INT_MIN, INT_MAX, A|E, .unit = "audio_service_type"}, +{"request_sample_fmt", "sample format audio decoders should prefer", OFFSET(request_sample_fmt), AV_OPT_TYPE_SAMPLE_FMT, {.i64=AV_SAMPLE_FMT_NONE}, -1, INT_MAX, A|D, .unit = "request_sample_fmt"}, {"pkt_timebase", NULL, OFFSET(pkt_timebase), AV_OPT_TYPE_RATIONAL, {.dbl = 0 }, 0, INT_MAX, 0}, {"sub_charenc", "set input text subtitles character encoding", OFFSET(sub_charenc), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, S|D}, -{"sub_charenc_mode", "set input text subtitles character encoding mode", OFFSET(sub_charenc_mode), AV_OPT_TYPE_FLAGS, {.i64 = FF_SUB_CHARENC_MODE_AUTOMATIC}, -1, INT_MAX, S|D, "sub_charenc_mode"}, -{"do_nothing", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_DO_NOTHING}, INT_MIN, INT_MAX, S|D, "sub_charenc_mode"}, -{"auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_AUTOMATIC}, INT_MIN, INT_MAX, S|D, "sub_charenc_mode"}, -{"pre_decoder", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_PRE_DECODER}, INT_MIN, INT_MAX, S|D, "sub_charenc_mode"}, -{"ignore", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_IGNORE}, INT_MIN, INT_MAX, S|D, "sub_charenc_mode"}, +{"sub_charenc_mode", "set input text subtitles character encoding mode", OFFSET(sub_charenc_mode), AV_OPT_TYPE_FLAGS, {.i64 = FF_SUB_CHARENC_MODE_AUTOMATIC}, -1, INT_MAX, S|D, .unit = "sub_charenc_mode"}, +{"do_nothing", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_DO_NOTHING}, INT_MIN, INT_MAX, S|D, .unit = "sub_charenc_mode"}, +{"auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_AUTOMATIC}, INT_MIN, INT_MAX, S|D, .unit = "sub_charenc_mode"}, +{"pre_decoder", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_PRE_DECODER}, INT_MIN, INT_MAX, S|D, .unit = "sub_charenc_mode"}, +{"ignore", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_SUB_CHARENC_MODE_IGNORE}, INT_MIN, INT_MAX, S|D, .unit = "sub_charenc_mode"}, {"apply_cropping", NULL, OFFSET(apply_cropping), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, V | D }, {"skip_alpha", "Skip processing alpha", OFFSET(skip_alpha), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, V|D }, -{"field_order", "Field order", OFFSET(field_order), AV_OPT_TYPE_INT, {.i64 = AV_FIELD_UNKNOWN }, 0, 5, V|D|E, "field_order" }, -{"progressive", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_FIELD_PROGRESSIVE }, 0, 0, V|D|E, "field_order" }, -{"tt", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_FIELD_TT }, 0, 0, V|D|E, "field_order" }, -{"bb", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_FIELD_BB }, 0, 0, V|D|E, "field_order" }, -{"tb", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_FIELD_TB }, 0, 0, V|D|E, "field_order" }, -{"bt", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_FIELD_BT }, 0, 0, V|D|E, "field_order" }, +{"field_order", "Field order", OFFSET(field_order), AV_OPT_TYPE_INT, {.i64 = AV_FIELD_UNKNOWN }, 0, 5, V|D|E, .unit = "field_order" }, +{"progressive", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_FIELD_PROGRESSIVE }, 0, 0, V|D|E, .unit = "field_order" }, +{"tt", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_FIELD_TT }, 0, 0, V|D|E, .unit = "field_order" }, +{"bb", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_FIELD_BB }, 0, 0, V|D|E, .unit = "field_order" }, +{"tb", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_FIELD_TB }, 0, 0, V|D|E, .unit = "field_order" }, +{"bt", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AV_FIELD_BT }, 0, 0, V|D|E, .unit = "field_order" }, {"dump_separator", "set information dump field separator", OFFSET(dump_separator), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, A|V|S|D|E}, {"codec_whitelist", "List of decoders that are allowed to be used", OFFSET(codec_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, A|V|S|D }, {"pixel_format", "set pixel format", OFFSET(pix_fmt), AV_OPT_TYPE_PIXEL_FMT, {.i64=AV_PIX_FMT_NONE}, -1, INT_MAX, 0 }, {"video_size", "set video size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str=NULL}, 0, INT_MAX, 0 }, {"max_pixels", "Maximum number of pixels", OFFSET(max_pixels), AV_OPT_TYPE_INT64, {.i64 = INT_MAX }, 0, INT_MAX, A|V|S|D|E }, {"max_samples", "Maximum number of samples", OFFSET(max_samples), AV_OPT_TYPE_INT64, {.i64 = INT_MAX }, 0, INT_MAX, A|D|E }, -{"hwaccel_flags", NULL, OFFSET(hwaccel_flags), AV_OPT_TYPE_FLAGS, {.i64 = AV_HWACCEL_FLAG_IGNORE_LEVEL }, 0, UINT_MAX, V|D, "hwaccel_flags"}, -{"ignore_level", "ignore level even if the codec level used is unknown or higher than the maximum supported level reported by the hardware driver", 0, AV_OPT_TYPE_CONST, { .i64 = AV_HWACCEL_FLAG_IGNORE_LEVEL }, INT_MIN, INT_MAX, V | D, "hwaccel_flags" }, -{"allow_high_depth", "allow to output YUV pixel formats with a different chroma sampling than 4:2:0 and/or other than 8 bits per component", 0, AV_OPT_TYPE_CONST, {.i64 = AV_HWACCEL_FLAG_ALLOW_HIGH_DEPTH }, INT_MIN, INT_MAX, V | D, "hwaccel_flags"}, -{"allow_profile_mismatch", "attempt to decode anyway if HW accelerated decoder's supported profiles do not exactly match the stream", 0, AV_OPT_TYPE_CONST, {.i64 = AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH }, INT_MIN, INT_MAX, V | D, "hwaccel_flags"}, -{"unsafe_output", "allow potentially unsafe hwaccel frame output that might require special care to process successfully", 0, AV_OPT_TYPE_CONST, {.i64 = AV_HWACCEL_FLAG_UNSAFE_OUTPUT }, INT_MIN, INT_MAX, V | D, "hwaccel_flags"}, +{"hwaccel_flags", NULL, OFFSET(hwaccel_flags), AV_OPT_TYPE_FLAGS, {.i64 = AV_HWACCEL_FLAG_IGNORE_LEVEL }, 0, UINT_MAX, V|D, .unit = "hwaccel_flags"}, +{"ignore_level", "ignore level even if the codec level used is unknown or higher than the maximum supported level reported by the hardware driver", 0, AV_OPT_TYPE_CONST, { .i64 = AV_HWACCEL_FLAG_IGNORE_LEVEL }, INT_MIN, INT_MAX, V | D, .unit = "hwaccel_flags" }, +{"allow_high_depth", "allow to output YUV pixel formats with a different chroma sampling than 4:2:0 and/or other than 8 bits per component", 0, AV_OPT_TYPE_CONST, {.i64 = AV_HWACCEL_FLAG_ALLOW_HIGH_DEPTH }, INT_MIN, INT_MAX, V | D, .unit = "hwaccel_flags"}, +{"allow_profile_mismatch", "attempt to decode anyway if HW accelerated decoder's supported profiles do not exactly match the stream", 0, AV_OPT_TYPE_CONST, {.i64 = AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH }, INT_MIN, INT_MAX, V | D, .unit = "hwaccel_flags"}, +{"unsafe_output", "allow potentially unsafe hwaccel frame output that might require special care to process successfully", 0, AV_OPT_TYPE_CONST, {.i64 = AV_HWACCEL_FLAG_UNSAFE_OUTPUT }, INT_MIN, INT_MAX, V | D, .unit = "hwaccel_flags"}, {"extra_hw_frames", "Number of extra hardware frames to allocate for the user", OFFSET(extra_hw_frames), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, V|D }, {"discard_damaged_percentage", "Percentage of damaged samples to discard a frame", OFFSET(discard_damaged_percentage), AV_OPT_TYPE_INT, {.i64 = 95 }, 0, 100, V|D }, +{"side_data_prefer_packet", "Comma-separated list of side data types for which user-supplied (container) data is preferred over coded bytestream", + OFFSET(side_data_prefer_packet), AV_OPT_TYPE_INT | AR, .min = -1, .max = INT_MAX, .flags = V|A|S|D, .unit = "side_data_pkt" }, + {"replaygain", .default_val.i64 = AV_PKT_DATA_REPLAYGAIN, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" }, + {"displaymatrix", .default_val.i64 = AV_PKT_DATA_DISPLAYMATRIX, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" }, + {"spherical", .default_val.i64 = AV_PKT_DATA_SPHERICAL, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" }, + {"stereo3d", .default_val.i64 = AV_PKT_DATA_STEREO3D, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" }, + {"audio_service_type", .default_val.i64 = AV_PKT_DATA_AUDIO_SERVICE_TYPE, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" }, + {"mastering_display_metadata", .default_val.i64 = AV_PKT_DATA_MASTERING_DISPLAY_METADATA, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" }, + {"content_light_level", .default_val.i64 = AV_PKT_DATA_CONTENT_LIGHT_LEVEL, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" }, + {"icc_profile", .default_val.i64 = AV_PKT_DATA_ICC_PROFILE, .type = AV_OPT_TYPE_CONST, .flags = A|D, .unit = "side_data_pkt" }, {NULL}, }; diff --git a/libavcodec/opus_pvq.c b/libavcodec/opus_pvq.c index d08dcd74134..0a6dc4cdf1a 100644 --- a/libavcodec/opus_pvq.c +++ b/libavcodec/opus_pvq.c @@ -80,7 +80,7 @@ static inline int celt_pulses2bits(const uint8_t *cache, int pulses) return (pulses == 0) ? 0 : cache[pulses] + 1; } -static inline void celt_normalize_residual(const int * av_restrict iy, float * av_restrict X, +static inline void celt_normalize_residual(const int * restrict iy, float * restrict X, int N, float g) { int i; diff --git a/libavcodec/opus_silk.c b/libavcodec/opus_silk.c index cf8b16acffe..27671516a0b 100644 --- a/libavcodec/opus_silk.c +++ b/libavcodec/opus_silk.c @@ -48,7 +48,7 @@ typedef struct SilkFrame { } SilkFrame; struct SilkContext { - AVCodecContext *avctx; + void *logctx; int output_channels; int midonly; @@ -799,7 +799,7 @@ int ff_silk_decode_superframe(SilkContext *s, OpusRangeCoder *rc, if (bandwidth > OPUS_BANDWIDTH_WIDEBAND || coded_channels > 2 || duration_ms > 60) { - av_log(s->avctx, AV_LOG_ERROR, "Invalid parameters passed " + av_log(s->logctx, AV_LOG_ERROR, "Invalid parameters passed " "to the SILK decoder.\n"); return AVERROR(EINVAL); } @@ -879,12 +879,12 @@ void ff_silk_flush(SilkContext *s) memset(s->prev_stereo_weights, 0, sizeof(s->prev_stereo_weights)); } -int ff_silk_init(AVCodecContext *avctx, SilkContext **ps, int output_channels) +int ff_silk_init(void *logctx, SilkContext **ps, int output_channels) { SilkContext *s; if (output_channels != 1 && output_channels != 2) { - av_log(avctx, AV_LOG_ERROR, "Invalid number of output channels: %d\n", + av_log(logctx, AV_LOG_ERROR, "Invalid number of output channels: %d\n", output_channels); return AVERROR(EINVAL); } @@ -893,7 +893,7 @@ int ff_silk_init(AVCodecContext *avctx, SilkContext **ps, int output_channels) if (!s) return AVERROR(ENOMEM); - s->avctx = avctx; + s->logctx = logctx; s->output_channels = output_channels; ff_silk_flush(s); diff --git a/libavcodec/opus_silk.h b/libavcodec/opus_silk.h index 6552c166a4d..4b595da2b98 100644 --- a/libavcodec/opus_silk.h +++ b/libavcodec/opus_silk.h @@ -23,7 +23,6 @@ #ifndef AVCODEC_OPUS_SILK_H #define AVCODEC_OPUS_SILK_H -#include "avcodec.h" #include "opus.h" #include "opus_rc.h" @@ -32,7 +31,7 @@ typedef struct SilkContext SilkContext; -int ff_silk_init(AVCodecContext *avctx, SilkContext **ps, int output_channels); +int ff_silk_init(void *logctx, SilkContext **ps, int output_channels); void ff_silk_free(SilkContext **ps); void ff_silk_flush(SilkContext *s); diff --git a/libavcodec/opusenc.c b/libavcodec/opusenc.c index a2f74a347b4..379d0e0c5d9 100644 --- a/libavcodec/opusenc.c +++ b/libavcodec/opusenc.c @@ -712,8 +712,8 @@ static av_cold int opus_encode_init(AVCodecContext *avctx) #define OPUSENC_FLAGS AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_AUDIO_PARAM static const AVOption opusenc_options[] = { - { "opus_delay", "Maximum delay in milliseconds", offsetof(OpusEncContext, options.max_delay_ms), AV_OPT_TYPE_FLOAT, { .dbl = OPUS_MAX_LOOKAHEAD }, 2.5f, OPUS_MAX_LOOKAHEAD, OPUSENC_FLAGS, "max_delay_ms" }, - { "apply_phase_inv", "Apply intensity stereo phase inversion", offsetof(OpusEncContext, options.apply_phase_inv), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, OPUSENC_FLAGS, "apply_phase_inv" }, + { "opus_delay", "Maximum delay in milliseconds", offsetof(OpusEncContext, options.max_delay_ms), AV_OPT_TYPE_FLOAT, { .dbl = OPUS_MAX_LOOKAHEAD }, 2.5f, OPUS_MAX_LOOKAHEAD, OPUSENC_FLAGS, .unit = "max_delay_ms" }, + { "apply_phase_inv", "Apply intensity stereo phase inversion", offsetof(OpusEncContext, options.apply_phase_inv), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, OPUSENC_FLAGS, .unit = "apply_phase_inv" }, { NULL }, }; @@ -745,7 +745,6 @@ const FFCodec ff_opus_encoder = { .close = opus_encode_end, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.supported_samplerates = (const int []){ 48000, 0 }, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO) .p.ch_layouts = (const AVChannelLayout []){ AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, { 0 } }, .p.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLTP, diff --git a/libavcodec/osq.c b/libavcodec/osq.c new file mode 100644 index 00000000000..1663f0b15fb --- /dev/null +++ b/libavcodec/osq.c @@ -0,0 +1,494 @@ +/* + * OSQ audio decoder + * 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/internal.h" +#include "libavutil/intreadwrite.h" +#include "avcodec.h" +#include "codec_internal.h" +#include "decode.h" +#include "internal.h" +#define BITSTREAM_READER_LE +#include "get_bits.h" +#include "unary.h" + +#define OFFSET 5 + +typedef struct OSQChannel { + unsigned prediction; + unsigned coding_mode; + unsigned residue_parameter; + unsigned residue_bits; + unsigned history[3]; + unsigned pos, count; + double sum; + int32_t prev; +} OSQChannel; + +typedef struct OSQContext { + GetBitContext gb; + OSQChannel ch[2]; + + uint8_t *bitstream; + size_t max_framesize; + size_t bitstream_size; + + int factor; + int decorrelate; + int frame_samples; + uint64_t nb_samples; + + int32_t *decode_buffer[2]; + + AVPacket *pkt; + int pkt_offset; +} OSQContext; + +static void osq_flush(AVCodecContext *avctx) +{ + OSQContext *s = avctx->priv_data; + + s->bitstream_size = 0; + s->pkt_offset = 0; +} + +static av_cold int osq_close(AVCodecContext *avctx) +{ + OSQContext *s = avctx->priv_data; + + av_freep(&s->bitstream); + s->bitstream_size = 0; + + for (int ch = 0; ch < FF_ARRAY_ELEMS(s->decode_buffer); ch++) + av_freep(&s->decode_buffer[ch]); + + return 0; +} + +static av_cold int osq_init(AVCodecContext *avctx) +{ + OSQContext *s = avctx->priv_data; + + if (avctx->extradata_size < 48) + return AVERROR(EINVAL); + + if (avctx->extradata[0] != 1) { + av_log(avctx, AV_LOG_ERROR, "Unsupported version.\n"); + return AVERROR_INVALIDDATA; + } + + avctx->sample_rate = AV_RL32(avctx->extradata + 4); + if (avctx->sample_rate < 1) + return AVERROR_INVALIDDATA; + + av_channel_layout_uninit(&avctx->ch_layout); + avctx->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; + avctx->ch_layout.nb_channels = avctx->extradata[3]; + if (avctx->ch_layout.nb_channels < 1) + return AVERROR_INVALIDDATA; + if (avctx->ch_layout.nb_channels > FF_ARRAY_ELEMS(s->decode_buffer)) + return AVERROR_INVALIDDATA; + + s->factor = 1; + switch (avctx->extradata[2]) { + case 8: avctx->sample_fmt = AV_SAMPLE_FMT_U8P; break; + case 16: avctx->sample_fmt = AV_SAMPLE_FMT_S16P; break; + case 20: + case 24: s->factor = 256; + avctx->sample_fmt = AV_SAMPLE_FMT_S32P; break; + default: return AVERROR_INVALIDDATA; + } + + avctx->bits_per_raw_sample = avctx->extradata[2]; + s->nb_samples = AV_RL64(avctx->extradata + 16); + s->frame_samples = AV_RL16(avctx->extradata + 8); + s->max_framesize = (s->frame_samples * 16 + 1024) * avctx->ch_layout.nb_channels; + + s->bitstream = av_calloc(s->max_framesize + AV_INPUT_BUFFER_PADDING_SIZE, sizeof(*s->bitstream)); + if (!s->bitstream) + return AVERROR(ENOMEM); + + for (int ch = 0; ch < avctx->ch_layout.nb_channels; ch++) { + s->decode_buffer[ch] = av_calloc(s->frame_samples + OFFSET, + sizeof(*s->decode_buffer[ch])); + if (!s->decode_buffer[ch]) + return AVERROR(ENOMEM); + } + + s->pkt = avctx->internal->in_pkt; + + return 0; +} + +static void reset_stats(OSQChannel *cb) +{ + memset(cb->history, 0, sizeof(cb->history)); + cb->pos = cb->count = cb->sum = 0; +} + +static void update_stats(OSQChannel *cb, int val) +{ + cb->sum += FFABS(val) - cb->history[cb->pos]; + cb->history[cb->pos] = FFABS(val); + cb->pos++; + cb->count++; + if (cb->pos >= FF_ARRAY_ELEMS(cb->history)) + cb->pos = 0; +} + +static int update_residue_parameter(OSQChannel *cb) +{ + double sum, x; + int rice_k; + + sum = cb->sum; + x = sum / cb->count; + rice_k = ceil(log2(x)); + if (rice_k >= 30) { + double f = floor(sum / 1.4426952 + 0.5); + if (f <= 1) { + rice_k = 1; + } else if (f >= 31) { + rice_k = 31; + } else + rice_k = f; + } + + return rice_k; +} + +static uint32_t get_urice(GetBitContext *gb, int k) +{ + uint32_t z, x, b; + + x = get_unary(gb, 1, 512); + b = get_bits_long(gb, k); + z = b | x << k; + + return z; +} + +static int32_t get_srice(GetBitContext *gb, int x) +{ + int32_t y = get_urice(gb, x); + return get_bits1(gb) ? -y : y; +} + +static int osq_channel_parameters(AVCodecContext *avctx, int ch) +{ + OSQContext *s = avctx->priv_data; + OSQChannel *cb = &s->ch[ch]; + GetBitContext *gb = &s->gb; + + cb->prev = 0; + cb->prediction = get_urice(gb, 5); + cb->coding_mode = get_urice(gb, 3); + if (cb->prediction >= 15) + return AVERROR_INVALIDDATA; + if (cb->coding_mode > 0 && cb->coding_mode < 3) { + cb->residue_parameter = get_urice(gb, 4); + if (!cb->residue_parameter || cb->residue_parameter >= 31) + return AVERROR_INVALIDDATA; + } else if (cb->coding_mode == 3) { + cb->residue_bits = get_urice(gb, 4); + if (!cb->residue_bits || cb->residue_bits >= 31) + return AVERROR_INVALIDDATA; + } else if (cb->coding_mode) { + return AVERROR_INVALIDDATA; + } + + if (cb->coding_mode == 2) + reset_stats(cb); + + return 0; +} + +#define A (-1) +#define B (-2) +#define C (-3) +#define D (-4) +#define E (-5) +#define P2 (((unsigned)dst[A] + dst[A]) - dst[B]) +#define P3 (((unsigned)dst[A] - dst[B]) * 3 + dst[C]) + +static int do_decode(AVCodecContext *avctx, AVFrame *frame, int decorrelate, int downsample) +{ + OSQContext *s = avctx->priv_data; + const int nb_channels = avctx->ch_layout.nb_channels; + const int nb_samples = frame->nb_samples; + GetBitContext *gb = &s->gb; + + for (int n = 0; n < nb_samples; n++) { + for (int ch = 0; ch < nb_channels; ch++) { + OSQChannel *cb = &s->ch[ch]; + int32_t *dst = s->decode_buffer[ch] + OFFSET; + int32_t p, prev = cb->prev; + + if (nb_channels == 2 && ch == 1 && decorrelate != s->decorrelate) { + if (!decorrelate) { + s->decode_buffer[1][OFFSET+A] += s->decode_buffer[0][OFFSET+B]; + s->decode_buffer[1][OFFSET+B] += s->decode_buffer[0][OFFSET+C]; + s->decode_buffer[1][OFFSET+C] += s->decode_buffer[0][OFFSET+D]; + s->decode_buffer[1][OFFSET+D] += s->decode_buffer[0][OFFSET+E]; + } else { + s->decode_buffer[1][OFFSET+A] -= s->decode_buffer[0][OFFSET+B]; + s->decode_buffer[1][OFFSET+B] -= s->decode_buffer[0][OFFSET+C]; + s->decode_buffer[1][OFFSET+C] -= s->decode_buffer[0][OFFSET+D]; + s->decode_buffer[1][OFFSET+D] -= s->decode_buffer[0][OFFSET+E]; + } + s->decorrelate = decorrelate; + } + + if (!cb->coding_mode) { + dst[n] = 0; + } else if (cb->coding_mode == 3) { + dst[n] = get_sbits_long(gb, cb->residue_bits); + } else { + dst[n] = get_srice(gb, cb->residue_parameter); + } + + if (get_bits_left(gb) < 0) { + av_log(avctx, AV_LOG_ERROR, "overread!\n"); + return AVERROR_INVALIDDATA; + } + + p = prev / 2; + prev = dst[n]; + + switch (cb->prediction) { + case 0: + break; + case 1: + dst[n] += (unsigned)dst[A]; + break; + case 2: + dst[n] += (unsigned)dst[A] + p; + break; + case 3: + dst[n] += P2; + break; + case 4: + dst[n] += P2 + p; + break; + case 5: + dst[n] += P3; + break; + case 6: + dst[n] += P3 + p; + break; + case 7: + dst[n] += (int)(P2 + P3) / 2 + (unsigned)p; + break; + case 8: + dst[n] += (int)(P2 + P3) / 2; + break; + case 9: + dst[n] += (int)(P2 * 2 + P3) / 3 + (unsigned)p; + break; + case 10: + dst[n] += (int)(P2 + P3 * 2) / 3 + (unsigned)p; + break; + case 11: + dst[n] += (int)((unsigned)dst[A] + dst[B]) / 2; + break; + case 12: + dst[n] += (unsigned)dst[B]; + break; + case 13: + dst[n] += (int)(unsigned)(dst[D] + dst[B]) / 2; + break; + case 14: + dst[n] += (int)((unsigned)P2 + dst[A]) / 2 + (unsigned)p; + break; + default: + return AVERROR_INVALIDDATA; + } + + cb->prev = prev; + + if (downsample) + dst[n] *= 256U; + + dst[E] = dst[D]; + dst[D] = dst[C]; + dst[C] = dst[B]; + dst[B] = dst[A]; + dst[A] = dst[n]; + + if (cb->coding_mode == 2) { + update_stats(cb, dst[n]); + cb->residue_parameter = update_residue_parameter(cb); + } + + if (nb_channels == 2 && ch == 1) { + if (decorrelate) + dst[n] += s->decode_buffer[0][OFFSET+n]; + } + + if (downsample) + dst[A] /= 256; + } + } + + return 0; +} + +static int osq_decode_block(AVCodecContext *avctx, AVFrame *frame) +{ + const int nb_channels = avctx->ch_layout.nb_channels; + const int nb_samples = frame->nb_samples; + OSQContext *s = avctx->priv_data; + const unsigned factor = s->factor; + int ret, decorrelate, downsample; + GetBitContext *gb = &s->gb; + + skip_bits1(gb); + decorrelate = get_bits1(gb); + downsample = get_bits1(gb); + + for (int ch = 0; ch < nb_channels; ch++) { + if ((ret = osq_channel_parameters(avctx, ch)) < 0) { + av_log(avctx, AV_LOG_ERROR, "invalid channel parameters\n"); + return ret; + } + } + + if ((ret = do_decode(avctx, frame, decorrelate, downsample)) < 0) + return ret; + + align_get_bits(gb); + + switch (avctx->sample_fmt) { + case AV_SAMPLE_FMT_U8P: + for (int ch = 0; ch < nb_channels; ch++) { + uint8_t *dst = (uint8_t *)frame->extended_data[ch]; + int32_t *src = s->decode_buffer[ch] + OFFSET; + + for (int n = 0; n < nb_samples; n++) + dst[n] = av_clip_uint8(src[n] + 0x80); + } + break; + case AV_SAMPLE_FMT_S16P: + for (int ch = 0; ch < nb_channels; ch++) { + int16_t *dst = (int16_t *)frame->extended_data[ch]; + int32_t *src = s->decode_buffer[ch] + OFFSET; + + for (int n = 0; n < nb_samples; n++) + dst[n] = (int16_t)src[n]; + } + break; + case AV_SAMPLE_FMT_S32P: + for (int ch = 0; ch < nb_channels; ch++) { + int32_t *dst = (int32_t *)frame->extended_data[ch]; + int32_t *src = s->decode_buffer[ch] + OFFSET; + + for (int n = 0; n < nb_samples; n++) + dst[n] = src[n] * factor; + } + break; + default: + return AVERROR_BUG; + } + + return 0; +} + +static int osq_receive_frame(AVCodecContext *avctx, AVFrame *frame) +{ + OSQContext *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + int ret, n; + + while (s->bitstream_size < s->max_framesize) { + int size; + + if (!s->pkt->data) { + ret = ff_decode_get_packet(avctx, s->pkt); + if (ret == AVERROR_EOF && s->bitstream_size > 0) + break; + if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) + return ret; + if (ret < 0) + goto fail; + } + + size = FFMIN(s->pkt->size - s->pkt_offset, s->max_framesize - s->bitstream_size); + memcpy(s->bitstream + s->bitstream_size, s->pkt->data + s->pkt_offset, size); + s->bitstream_size += size; + s->pkt_offset += size; + + if (s->pkt_offset == s->pkt->size) { + av_packet_unref(s->pkt); + s->pkt_offset = 0; + } + } + + frame->nb_samples = FFMIN(s->frame_samples, s->nb_samples); + if (frame->nb_samples <= 0) + return AVERROR_EOF; + + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + goto fail; + + if ((ret = init_get_bits8(gb, s->bitstream, s->bitstream_size)) < 0) + goto fail; + + if ((ret = osq_decode_block(avctx, frame)) < 0) + goto fail; + + s->nb_samples -= frame->nb_samples; + + n = get_bits_count(gb) / 8; + if (n > s->bitstream_size) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + memmove(s->bitstream, &s->bitstream[n], s->bitstream_size - n); + s->bitstream_size -= n; + + return 0; + +fail: + s->bitstream_size = 0; + s->pkt_offset = 0; + av_packet_unref(s->pkt); + + return ret; +} + +const FFCodec ff_osq_decoder = { + .p.name = "osq", + CODEC_LONG_NAME("OSQ (Original Sound Quality)"), + .p.type = AVMEDIA_TYPE_AUDIO, + .p.id = AV_CODEC_ID_OSQ, + .priv_data_size = sizeof(OSQContext), + .init = osq_init, + FF_CODEC_RECEIVE_FRAME_CB(osq_receive_frame), + .close = osq_close, + .p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | + AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_U8P, + AV_SAMPLE_FMT_S16P, + AV_SAMPLE_FMT_S32P, + AV_SAMPLE_FMT_NONE }, + .flush = osq_flush, +}; diff --git a/libavcodec/packet.h b/libavcodec/packet.h index f28e7e70115..b0ba3baea23 100644 --- a/libavcodec/packet.h +++ b/libavcodec/packet.h @@ -33,9 +33,9 @@ #include "libavcodec/version_major.h" /** - * @defgroup lavc_packet AVPacket + * @defgroup lavc_packet_side_data AVPacketSideData * - * Types and functions for working with AVPacket. + * Types and functions for working with AVPacketSideData. * @{ */ enum AVPacketSideDataType { @@ -299,6 +299,37 @@ enum AVPacketSideDataType { */ AV_PKT_DATA_DYNAMIC_HDR10_PLUS, + /** + * IAMF Mix Gain Parameter Data associated with the audio frame. This metadata + * is in the form of the AVIAMFParamDefinition struct and contains information + * defined in sections 3.6.1 and 3.8.1 of the Immersive Audio Model and + * Formats standard. + */ + AV_PKT_DATA_IAMF_MIX_GAIN_PARAM, + + /** + * IAMF Demixing Info Parameter Data associated with the audio frame. This + * metadata is in the form of the AVIAMFParamDefinition struct and contains + * information defined in sections 3.6.1 and 3.8.2 of the Immersive Audio Model + * and Formats standard. + */ + AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM, + + /** + * IAMF Recon Gain Info Parameter Data associated with the audio frame. This + * metadata is in the form of the AVIAMFParamDefinition struct and contains + * information defined in sections 3.6.1 and 3.8.3 of the Immersive Audio Model + * and Formats standard. + */ + AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM, + + /** + * Ambient viewing environment metadata, as defined by H.274. This metadata + * should be associated with a video stream and contains data in the form + * of the AVAmbientViewingEnvironment struct. + */ + AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT, + /** * The number of side data types. * This is not part of the public API/ABI in the sense that it may @@ -312,12 +343,129 @@ enum AVPacketSideDataType { #define AV_PKT_DATA_QUALITY_FACTOR AV_PKT_DATA_QUALITY_STATS //DEPRECATED +/** + * This structure stores auxiliary information for decoding, presenting, or + * otherwise processing the coded stream. It is typically exported by demuxers + * and encoders and can be fed to decoders and muxers either in a per packet + * basis, or as global side data (applying to the entire coded stream). + * + * Global side data is handled as follows: + * - During demuxing, it may be exported through + * @ref AVStream.codecpar.side_data "AVStream's codec parameters", which can + * then be passed as input to decoders through the + * @ref AVCodecContext.coded_side_data "decoder context's side data", for + * initialization. + * - For muxing, it can be fed through @ref AVStream.codecpar.side_data + * "AVStream's codec parameters", typically the output of encoders through + * the @ref AVCodecContext.coded_side_data "encoder context's side data", for + * initialization. + * + * Packet specific side data is handled as follows: + * - During demuxing, it may be exported through @ref AVPacket.side_data + * "AVPacket's side data", which can then be passed as input to decoders. + * - For muxing, it can be fed through @ref AVPacket.side_data "AVPacket's + * side data", typically the output of encoders. + * + * Different modules may accept or export different types of side data + * depending on media type and codec. Refer to @ref AVPacketSideDataType for a + * list of defined types and where they may be found or used. + */ typedef struct AVPacketSideData { uint8_t *data; size_t size; enum AVPacketSideDataType type; } AVPacketSideData; +/** + * Allocate a new packet side data. + * + * @param sd pointer to an array of side data to which the side data should + * be added. *sd may be NULL, in which case the array will be + * initialized. + * @param nb_sd pointer to an integer containing the number of entries in + * the array. The integer value will be increased by 1 on success. + * @param type side data type + * @param size desired side data size + * @param flags currently unused. Must be zero + * + * @return pointer to freshly allocated side data on success, or NULL otherwise. + */ +AVPacketSideData *av_packet_side_data_new(AVPacketSideData **psd, int *pnb_sd, + enum AVPacketSideDataType type, + size_t size, int flags); + +/** + * Wrap existing data as packet side data. + * + * @param sd pointer to an array of side data to which the side data should + * be added. *sd may be NULL, in which case the array will be + * initialized + * @param nb_sd pointer to an integer containing the number of entries in + * the array. The integer value will be increased by 1 on success. + * @param type side data type + * @param data a data array. It must be allocated with the av_malloc() family + * of functions. The ownership of the data is transferred to the + * side data array on success + * @param size size of the data array + * @param flags currently unused. Must be zero + * + * @return pointer to freshly allocated side data on success, or NULL otherwise + * On failure, the side data array is unchanged and the data remains + * owned by the caller. + */ +AVPacketSideData *av_packet_side_data_add(AVPacketSideData **sd, int *nb_sd, + enum AVPacketSideDataType type, + void *data, size_t size, int flags); + +/** + * Get side information from a side data array. + * + * @param sd the array from which the side data should be fetched + * @param nb_sd value containing the number of entries in the array. + * @param type desired side information type + * + * @return pointer to side data if present or NULL otherwise + */ +const AVPacketSideData *av_packet_side_data_get(const AVPacketSideData *sd, + int nb_sd, + enum AVPacketSideDataType type); + +/** + * Remove side data of the given type from a side data array. + * + * @param sd the array from which the side data should be removed + * @param nb_sd pointer to an integer containing the number of entries in + * the array. Will be reduced by the amount of entries removed + * upon return + * @param type side information type + */ +void av_packet_side_data_remove(AVPacketSideData *sd, int *nb_sd, + enum AVPacketSideDataType type); + +/** + * Convenience function to free all the side data stored in an array, and + * the array itself. + * + * @param sd pointer to array of side data to free. Will be set to NULL + * upon return. + * @param nb_sd pointer to an integer containing the number of entries in + * the array. Will be set to 0 upon return. + */ +void av_packet_side_data_free(AVPacketSideData **sd, int *nb_sd); + +const char *av_packet_side_data_name(enum AVPacketSideDataType type); + +/** + * @} + */ + +/** + * @defgroup lavc_packet AVPacket + * + * Types and functions for working with AVPacket. + * @{ + */ + /** * This structure stores compressed data. It is typically exported by demuxers * and then passed as input to decoders, or received as output from encoders and @@ -448,13 +596,6 @@ typedef struct AVPacketList { #define AV_PKT_FLAG_DISPOSABLE 0x0010 enum AVSideDataParamChangeFlags { -#if FF_API_OLD_CHANNEL_LAYOUT - /** - * @deprecated those are not used by any decoder - */ - AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT = 0x0001, - AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT = 0x0002, -#endif AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE = 0x0004, AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS = 0x0008, }; @@ -603,8 +744,6 @@ int av_packet_shrink_side_data(AVPacket *pkt, enum AVPacketSideDataType type, uint8_t* av_packet_get_side_data(const AVPacket *pkt, enum AVPacketSideDataType type, size_t *size); -const char *av_packet_side_data_name(enum AVPacketSideDataType type); - /** * Pack a dictionary for use in side_data. * diff --git a/libavcodec/packet_internal.h b/libavcodec/packet_internal.h index 92a0d4e6d53..52fa6d9be90 100644 --- a/libavcodec/packet_internal.h +++ b/libavcodec/packet_internal.h @@ -23,6 +23,8 @@ #include "packet.h" +#define AVPACKET_IS_EMPTY(pkt) (!(pkt)->data && !(pkt)->side_data_elems) + typedef struct PacketListEntry { struct PacketListEntry *next; AVPacket pkt; diff --git a/libavcodec/pafvideo.c b/libavcodec/pafvideo.c index 458fe9ff47c..14eb42435a3 100644 --- a/libavcodec/pafvideo.c +++ b/libavcodec/pafvideo.c @@ -296,10 +296,10 @@ static int paf_video_decode(AVCodecContext *avctx, AVFrame *rframe, if (code & 0x20) { // frame is keyframe memset(c->pic->data[1], 0, AVPALETTE_SIZE); c->current_frame = 0; - c->pic->key_frame = 1; + c->pic->flags |= AV_FRAME_FLAG_KEY; c->pic->pict_type = AV_PICTURE_TYPE_I; } else { - c->pic->key_frame = 0; + c->pic->flags &= ~AV_FRAME_FLAG_KEY; c->pic->pict_type = AV_PICTURE_TYPE_P; } @@ -327,7 +327,11 @@ static int paf_video_decode(AVCodecContext *avctx, AVFrame *rframe, b = b << 2 | b >> 4; *out++ = (0xFFU << 24) | (r << 16) | (g << 8) | b; } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS c->pic->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } c->dirty[c->current_frame] = 1; diff --git a/libavcodec/parser.c b/libavcodec/parser.c index 49de7e6a57a..af17ee9c156 100644 --- a/libavcodec/parser.c +++ b/libavcodec/parser.c @@ -166,6 +166,10 @@ int av_parser_parse2(AVCodecParserContext *s, AVCodecContext *avctx, #define FILL(name) if(s->name > 0 && avctx->name <= 0) avctx->name = s->name if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) { FILL(field_order); + FILL(coded_width); + FILL(coded_height); + FILL(width); + FILL(height); } /* update the file pointer */ @@ -248,6 +252,7 @@ int ff_combine_frame(ParseContext *pc, int next, AV_INPUT_BUFFER_PADDING_SIZE); if (!new_buffer) { av_log(NULL, AV_LOG_ERROR, "Failed to reallocate parser buffer to %d\n", next + pc->index + AV_INPUT_BUFFER_PADDING_SIZE); + *buf_size = pc->overread_index = pc->index = 0; return AVERROR(ENOMEM); diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c index d3558080181..5128009cd42 100644 --- a/libavcodec/parsers.c +++ b/libavcodec/parsers.c @@ -41,6 +41,7 @@ extern const AVCodecParser ff_dvaudio_parser; extern const AVCodecParser ff_dvbsub_parser; extern const AVCodecParser ff_dvdsub_parser; extern const AVCodecParser ff_dvd_nav_parser; +extern const AVCodecParser ff_evc_parser; extern const AVCodecParser ff_flac_parser; extern const AVCodecParser ff_ftr_parser; extern const AVCodecParser ff_g723_1_parser; @@ -54,6 +55,7 @@ extern const AVCodecParser ff_hevc_parser; extern const AVCodecParser ff_hdr_parser; extern const AVCodecParser ff_ipu_parser; extern const AVCodecParser ff_jpeg2000_parser; +extern const AVCodecParser ff_jpegxl_parser; extern const AVCodecParser ff_misc4_parser; extern const AVCodecParser ff_mjpeg_parser; extern const AVCodecParser ff_mlp_parser; @@ -64,8 +66,7 @@ extern const AVCodecParser ff_opus_parser; extern const AVCodecParser ff_png_parser; extern const AVCodecParser ff_pnm_parser; extern const AVCodecParser ff_qoi_parser; -extern const AVCodecParser ff_rv30_parser; -extern const AVCodecParser ff_rv40_parser; +extern const AVCodecParser ff_rv34_parser; extern const AVCodecParser ff_sbc_parser; extern const AVCodecParser ff_sipr_parser; extern const AVCodecParser ff_tak_parser; @@ -74,6 +75,7 @@ extern const AVCodecParser ff_vorbis_parser; extern const AVCodecParser ff_vp3_parser; extern const AVCodecParser ff_vp8_parser; extern const AVCodecParser ff_vp9_parser; +extern const AVCodecParser ff_vvc_parser; extern const AVCodecParser ff_webp_parser; extern const AVCodecParser ff_xbm_parser; extern const AVCodecParser ff_xma_parser; diff --git a/libavcodec/pcm-bluray.c b/libavcodec/pcm-bluray.c index f65609514a1..235020d78f0 100644 --- a/libavcodec/pcm-bluray.c +++ b/libavcodec/pcm-bluray.c @@ -167,7 +167,7 @@ static int pcm_bluray_decode_frame(AVCodecContext *avctx, AVFrame *frame, samples *= num_source_channels; if (AV_SAMPLE_FMT_S16 == avctx->sample_fmt) { #if HAVE_BIGENDIAN - bytestream2_get_buffer(&gb, dst16, buf_size); + bytestream2_get_buffer(&gb, (uint8_t*)dst16, buf_size); #else do { *dst16++ = bytestream2_get_be16u(&gb); @@ -187,7 +187,8 @@ static int pcm_bluray_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (AV_SAMPLE_FMT_S16 == avctx->sample_fmt) { do { #if HAVE_BIGENDIAN - bytestream2_get_buffer(&gb, dst16, avctx->ch_layout.nb_channels * 2); + bytestream2_get_buffer(&gb, (uint8_t*)dst16, + avctx->ch_layout.nb_channels * 2); dst16 += avctx->ch_layout.nb_channels; #else channel = avctx->ch_layout.nb_channels; diff --git a/libavcodec/pcm-blurayenc.c b/libavcodec/pcm-blurayenc.c index bfbfa91d7ac..7147c804ad2 100644 --- a/libavcodec/pcm-blurayenc.c +++ b/libavcodec/pcm-blurayenc.c @@ -279,17 +279,6 @@ const FFCodec ff_pcm_bluray_encoder = { .init = pcm_bluray_encode_init, FF_CODEC_ENCODE_CB(pcm_bluray_encode_frame), .p.supported_samplerates = (const int[]) { 48000, 96000, 192000, 0 }, - CODEC_OLD_CHANNEL_LAYOUTS( - AV_CH_LAYOUT_MONO, - AV_CH_LAYOUT_STEREO, - AV_CH_LAYOUT_SURROUND, - AV_CH_LAYOUT_2_1, - AV_CH_LAYOUT_4POINT0, - AV_CH_LAYOUT_2_2, - AV_CH_LAYOUT_5POINT0, - AV_CH_LAYOUT_5POINT1, - AV_CH_LAYOUT_7POINT0, - AV_CH_LAYOUT_7POINT1) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, diff --git a/libavcodec/pcm-dvd.c b/libavcodec/pcm-dvd.c index 419b2a138f8..319746c62e2 100644 --- a/libavcodec/pcm-dvd.c +++ b/libavcodec/pcm-dvd.c @@ -157,7 +157,7 @@ static void *pcm_dvd_decode_samples(AVCodecContext *avctx, const uint8_t *src, switch (avctx->bits_per_coded_sample) { case 16: { #if HAVE_BIGENDIAN - bytestream2_get_buffer(&gb, dst16, blocks * s->block_size); + bytestream2_get_buffer(&gb, (uint8_t*)dst16, blocks * s->block_size); dst16 += blocks * s->block_size / 2; #else int samples = blocks * avctx->ch_layout.nb_channels; diff --git a/libavcodec/pcm-dvdenc.c b/libavcodec/pcm-dvdenc.c index a2e5cbdc2e9..71e9b6915ad 100644 --- a/libavcodec/pcm-dvdenc.c +++ b/libavcodec/pcm-dvdenc.c @@ -116,7 +116,7 @@ static int pcm_dvd_encode_frame(AVCodecContext *avctx, AVPacket *avpkt, { PCMDVDContext *s = avctx->priv_data; int samples = frame->nb_samples * avctx->ch_layout.nb_channels; - int64_t pkt_size = (frame->nb_samples / s->samples_per_block) * s->block_size + 3; + int64_t pkt_size = (int64_t)(frame->nb_samples / s->samples_per_block) * s->block_size + 3; int blocks = (pkt_size - 3) / s->block_size; const int16_t *src16; const int32_t *src32; @@ -182,8 +182,6 @@ const FFCodec ff_pcm_dvd_encoder = { .init = pcm_dvd_encode_init, FF_CODEC_ENCODE_CB(pcm_dvd_encode_frame), .p.supported_samplerates = (const int[]) { 48000, 96000, 0}, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO, - AV_CH_LAYOUT_5POINT1, AV_CH_LAYOUT_7POINT1) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, AV_CHANNEL_LAYOUT_5POINT1, diff --git a/libavcodec/pcm.c b/libavcodec/pcm.c index 467ecb4fe09..4abca7cc071 100644 --- a/libavcodec/pcm.c +++ b/libavcodec/pcm.c @@ -538,7 +538,6 @@ static int pcm_decode_frame(AVCodecContext *avctx, AVFrame *frame, s->vector_fmul_scalar((float *)frame->extended_data[0], (const float *)frame->extended_data[0], s->scale, FFALIGN(frame->nb_samples * avctx->ch_layout.nb_channels, 4)); - emms_c(); } *got_frame_ptr = 1; diff --git a/libavcodec/pcx.c b/libavcodec/pcx.c index 4cca497298a..8e2aae7b5e5 100644 --- a/libavcodec/pcx.c +++ b/libavcodec/pcx.c @@ -75,9 +75,10 @@ static int pcx_decode_frame(AVCodecContext *avctx, AVFrame *p, GetByteContext gb; int compressed, xmin, ymin, xmax, ymax; int ret; - unsigned int w, h, bits_per_pixel, bytes_per_line, nplanes, stride, y, x, + unsigned int w, h, bits_per_pixel, bytes_per_line, nplanes, y, x, bytes_per_scanline; uint8_t *ptr, *scanline; + ptrdiff_t stride; if (avpkt->size < PCX_HEADER_SIZE) { av_log(avctx, AV_LOG_ERROR, "Packet too small\n"); diff --git a/libavcodec/pdvdec.c b/libavcodec/pdvdec.c new file mode 100644 index 00000000000..e2c03e7e0dc --- /dev/null +++ b/libavcodec/pdvdec.c @@ -0,0 +1,140 @@ +/* + * PDV video format + * + * 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 "avcodec.h" +#include "codec_internal.h" +#include "decode.h" +#include "zlib_wrapper.h" + +#include + +typedef struct PDVContext { + AVFrame *previous_frame; + FFZStream zstream; +} PDVContext; + +static av_cold int decode_init(AVCodecContext *avctx) +{ + PDVContext *s = avctx->priv_data; + + avctx->pix_fmt = AV_PIX_FMT_MONOBLACK; + + s->previous_frame = av_frame_alloc(); + if (!s->previous_frame) + return AVERROR(ENOMEM); + + return ff_inflate_init(&s->zstream, avctx); +} + +static av_cold int decode_end(AVCodecContext *avctx) +{ + PDVContext *s = avctx->priv_data; + + av_frame_free(&s->previous_frame); + ff_inflate_end(&s->zstream); + + return 0; +} + +static int decode_frame(AVCodecContext *avctx, AVFrame *frame, + int *got_frame, AVPacket *avpkt) +{ + PDVContext *s = avctx->priv_data; + AVFrame *prev_frame = s->previous_frame; + z_stream *const zstream = &s->zstream.zstream; + uint8_t *dst, *prev = prev_frame->data[0]; + int ret, zret; + + if (avctx->skip_frame >= AVDISCARD_ALL || + (avctx->skip_frame >= AVDISCARD_NONINTRA && + !(avpkt->flags & AV_PKT_FLAG_KEY))) + return avpkt->size; + + zret = inflateReset(zstream); + if (zret != Z_OK) { + av_log(avctx, AV_LOG_ERROR, "Could not reset inflate: %d.\n", zret); + return AVERROR_INVALIDDATA; + } + + if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0) + return ret; + + zstream->next_in = avpkt->data; + zstream->avail_in = avpkt->size; + + dst = frame->data[0]; + for (int i = 0; i < avctx->height; i++) { + zstream->next_out = dst; + zstream->avail_out = (avctx->width + 7) >> 3; + + zret = inflate(zstream, Z_SYNC_FLUSH); + if (zret != Z_OK && zret != Z_STREAM_END) { + av_log(avctx, AV_LOG_ERROR, + "Inflate failed with return code: %d.\n", zret); + return AVERROR_INVALIDDATA; + } + + if (prev && !(avpkt->flags & AV_PKT_FLAG_KEY)) { + for (int j = 0; j < (avctx->width + 7) >> 3; j++) + dst[j] ^= prev[j]; + prev += prev_frame->linesize[0]; + } + + dst += frame->linesize[0]; + } + + if ((ret = av_frame_replace(s->previous_frame, frame)) < 0) + return ret; + + if (avpkt->flags & AV_PKT_FLAG_KEY) { + frame->flags |= AV_FRAME_FLAG_KEY; + frame->pict_type = AV_PICTURE_TYPE_I; + } else { + frame->pict_type = AV_PICTURE_TYPE_P; + } + + *got_frame = 1; + + return avpkt->size; +} + +static void decode_flush(AVCodecContext *avctx) +{ + PDVContext *s = avctx->priv_data; + + av_frame_unref(s->previous_frame); +} + +const FFCodec ff_pdv_decoder = { + .p.name = "pdv", + CODEC_LONG_NAME("PDV (PlayDate Video)"), + .priv_data_size = sizeof(PDVContext), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_PDV, + .p.capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | + FF_CODEC_CAP_INIT_CLEANUP, + .init = decode_init, + .close = decode_end, + .flush = decode_flush, + FF_CODEC_DECODE_CB(decode_frame), +}; diff --git a/libavcodec/pgxdec.c b/libavcodec/pgxdec.c index e5d1df784e6..cc7cdb8c9ac 100644 --- a/libavcodec/pgxdec.c +++ b/libavcodec/pgxdec.c @@ -140,7 +140,7 @@ static int pgx_decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; avctx->bits_per_raw_sample = depth; if (bpp == 8) write_frame_8(p, &g, width, height, sign, depth); diff --git a/libavcodec/photocd.c b/libavcodec/photocd.c index 3030a80e0d6..07e8d460bd6 100644 --- a/libavcodec/photocd.c +++ b/libavcodec/photocd.c @@ -210,8 +210,8 @@ static av_noinline int read_hufftable(AVCodecContext *avctx, VLC *vlc) s->syms[j] = sym; } - ff_free_vlc(vlc); - ret = ff_init_vlc_sparse(vlc, 12, count, + ff_vlc_free(vlc); + ret = ff_vlc_init_sparse(vlc, 12, count, s->bits, sizeof(*s->bits), sizeof(*s->bits), s->codes, sizeof(*s->codes), sizeof(*s->codes), s->syms, sizeof(*s->syms), sizeof(*s->syms), 0); @@ -332,7 +332,7 @@ static int photocd_decode_frame(AVCodecContext *avctx, AVFrame *p, return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; bytestream2_init(gb, avpkt->data, avpkt->size); @@ -438,7 +438,7 @@ static av_cold int photocd_decode_close(AVCodecContext *avctx) PhotoCDContext *s = avctx->priv_data; for (int i = 0; i < 3; i++) - ff_free_vlc(&s->vlc[i]); + ff_vlc_free(&s->vlc[i]); return 0; } diff --git a/libavcodec/pictordec.c b/libavcodec/pictordec.c index aef3d3de76f..5aaa725bd3e 100644 --- a/libavcodec/pictordec.c +++ b/libavcodec/pictordec.c @@ -191,7 +191,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; memset(frame->data[0], 0, s->height * frame->linesize[0]); frame->pict_type = AV_PICTURE_TYPE_I; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif pos_after_pal = bytestream2_tell(&s->g) + esize; palette = (uint32_t*)frame->data[1]; diff --git a/libavcodec/pixblockdsp.c b/libavcodec/pixblockdsp.c index 4294075cee3..8703e5aeaf7 100644 --- a/libavcodec/pixblockdsp.c +++ b/libavcodec/pixblockdsp.c @@ -24,7 +24,7 @@ #include "avcodec.h" #include "pixblockdsp.h" -static void get_pixels_16_c(int16_t *av_restrict block, const uint8_t *pixels, +static void get_pixels_16_c(int16_t *restrict block, const uint8_t *pixels, ptrdiff_t stride) { AV_COPY128U(block + 0 * 8, pixels + 0 * stride); @@ -37,7 +37,7 @@ static void get_pixels_16_c(int16_t *av_restrict block, const uint8_t *pixels, AV_COPY128U(block + 7 * 8, pixels + 7 * stride); } -static void get_pixels_8_c(int16_t *av_restrict block, const uint8_t *pixels, +static void get_pixels_8_c(int16_t *restrict block, const uint8_t *pixels, ptrdiff_t stride) { int i; @@ -57,7 +57,7 @@ static void get_pixels_8_c(int16_t *av_restrict block, const uint8_t *pixels, } } -static void diff_pixels_c(int16_t *av_restrict block, const uint8_t *s1, +static void diff_pixels_c(int16_t *restrict block, const uint8_t *s1, const uint8_t *s2, ptrdiff_t stride) { int i; diff --git a/libavcodec/pixblockdsp.h b/libavcodec/pixblockdsp.h index 9b002aa3d68..cac5f3d4a2f 100644 --- a/libavcodec/pixblockdsp.h +++ b/libavcodec/pixblockdsp.h @@ -21,22 +21,20 @@ #include -#include "config.h" - #include "avcodec.h" typedef struct PixblockDSPContext { - void (*get_pixels)(int16_t *av_restrict block /* align 16 */, + void (*get_pixels)(int16_t *restrict block /* align 16 */, const uint8_t *pixels /* align 8 */, ptrdiff_t stride); - void (*get_pixels_unaligned)(int16_t *av_restrict block /* align 16 */, + void (*get_pixels_unaligned)(int16_t *restrict block /* align 16 */, const uint8_t *pixels, ptrdiff_t stride); - void (*diff_pixels)(int16_t *av_restrict block /* align 16 */, + void (*diff_pixels)(int16_t *restrict block /* align 16 */, const uint8_t *s1 /* align 8 */, const uint8_t *s2 /* align 8 */, ptrdiff_t stride); - void (*diff_pixels_unaligned)(int16_t *av_restrict block /* align 16 */, + void (*diff_pixels_unaligned)(int16_t *restrict block /* align 16 */, const uint8_t *s1, const uint8_t *s2, ptrdiff_t stride); diff --git a/libavcodec/pixlet.c b/libavcodec/pixlet.c index b349d397f4c..914f0636bc7 100644 --- a/libavcodec/pixlet.c +++ b/libavcodec/pixlet.c @@ -230,8 +230,8 @@ static int read_high_coeffs(AVCodecContext *avctx, const uint8_t *src, int16_t * if (cnt1 >= length) { cnt1 = get_bits(bc, nbits); } else { - pfx = 14 + ((((uint64_t)(value - 14)) >> 32) & (value - 14)); - if (pfx < 1 || pfx > 25) + pfx = FFMIN(value, 14); + if (pfx < 1) return AVERROR_INVALIDDATA; cnt1 *= (1 << pfx) - 1; shbits = show_bits(bc, pfx); @@ -667,7 +667,7 @@ static int pixlet_decode_frame(AVCodecContext *avctx, AVFrame *p, bytestream2_skip(&ctx->gb, 8); p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->color_range = AVCOL_RANGE_JPEG; ret = ff_thread_get_buffer(avctx, p, 0); diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c index 67bd1ee01ff..ea586332b22 100644 --- a/libavcodec/pngdec.c +++ b/libavcodec/pngdec.c @@ -29,6 +29,7 @@ #include "libavutil/csp.h" #include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" +#include "libavutil/mastering_display_metadata.h" #include "libavutil/pixfmt.h" #include "libavutil/rational.h" #include "libavutil/stereo3d.h" @@ -75,11 +76,21 @@ typedef struct PNGDecContext { int have_chrm; uint32_t white_point[2]; uint32_t display_primaries[3][2]; + int gamma; int have_srgb; int have_cicp; enum AVColorPrimaries cicp_primaries; enum AVColorTransferCharacteristic cicp_trc; enum AVColorRange cicp_range; + int have_clli; + uint32_t clli_max; + uint32_t clli_avg; + /* Mastering Display Color Volume */ + int have_mdcv; + uint16_t mdcv_primaries[3][2]; + uint16_t mdcv_white_point[2]; + uint32_t mdcv_max_lum; + uint32_t mdcv_min_lum; enum PNGHeaderState hdr_state; enum PNGImageState pic_state; @@ -97,6 +108,7 @@ typedef struct PNGDecContext { int bpp; int has_trns; uint8_t transparent_color_be[6]; + int significant_bits; uint32_t palette[256]; uint8_t *crow_buf; @@ -641,6 +653,140 @@ static int decode_phys_chunk(AVCodecContext *avctx, PNGDecContext *s, return 0; } +/* + * This populates AVCodecContext fields so it must be called before + * ff_thread_finish_setup() to avoid a race condition with respect to the + * generic copying of avctx fields. + */ +static int populate_avctx_color_fields(AVCodecContext *avctx, AVFrame *frame) +{ + PNGDecContext *s = avctx->priv_data; + int ret; + + if (s->have_cicp) { + if (s->cicp_primaries >= AVCOL_PRI_NB) + av_log(avctx, AV_LOG_WARNING, "unrecognized cICP primaries\n"); + else + avctx->color_primaries = frame->color_primaries = s->cicp_primaries; + if (s->cicp_trc >= AVCOL_TRC_NB) + av_log(avctx, AV_LOG_WARNING, "unrecognized cICP transfer\n"); + else + avctx->color_trc = frame->color_trc = s->cicp_trc; + if (s->cicp_range == 0) { + av_log(avctx, AV_LOG_WARNING, "tv-range cICP tag found. Colors may be wrong\n"); + avctx->color_range = frame->color_range = AVCOL_RANGE_MPEG; + } else if (s->cicp_range != 1) { + /* we already printed a warning when parsing the cICP chunk */ + avctx->color_range = frame->color_range = AVCOL_RANGE_UNSPECIFIED; + } + } else if (s->iccp_data) { + AVFrameSideData *sd; + ret = ff_frame_new_side_data(avctx, frame, AV_FRAME_DATA_ICC_PROFILE, + s->iccp_data_len, &sd); + if (ret < 0) + return ret; + if (sd) { + memcpy(sd->data, s->iccp_data, s->iccp_data_len); + av_dict_set(&sd->metadata, "name", s->iccp_name, 0); + } + } else if (s->have_srgb) { + avctx->color_primaries = frame->color_primaries = AVCOL_PRI_BT709; + avctx->color_trc = frame->color_trc = AVCOL_TRC_IEC61966_2_1; + } else if (s->have_chrm) { + AVColorPrimariesDesc desc; + enum AVColorPrimaries prim; + desc.wp.x = av_make_q(s->white_point[0], 100000); + desc.wp.y = av_make_q(s->white_point[1], 100000); + desc.prim.r.x = av_make_q(s->display_primaries[0][0], 100000); + desc.prim.r.y = av_make_q(s->display_primaries[0][1], 100000); + desc.prim.g.x = av_make_q(s->display_primaries[1][0], 100000); + desc.prim.g.y = av_make_q(s->display_primaries[1][1], 100000); + desc.prim.b.x = av_make_q(s->display_primaries[2][0], 100000); + desc.prim.b.y = av_make_q(s->display_primaries[2][1], 100000); + prim = av_csp_primaries_id_from_desc(&desc); + if (prim != AVCOL_PRI_UNSPECIFIED) + avctx->color_primaries = frame->color_primaries = prim; + else + av_log(avctx, AV_LOG_WARNING, "unknown cHRM primaries\n"); + } + + /* these chunks override gAMA */ + if (s->iccp_data || s->have_srgb || s->have_cicp) { + av_dict_set(&s->frame_metadata, "gamma", NULL, 0); + } else if (s->gamma) { + /* + * These values are 100000/2.2, 100000/2.8, 100000/2.6, and + * 100000/1.0 respectively. 45455, 35714, and 38462, and 100000. + * There's a 0.001 gamma tolerance here in case of floating + * point issues when the PNG was written. + * + * None of the other enums have a pure gamma curve so it makes + * sense to leave those to sRGB and cICP. + */ + if (s->gamma > 45355 && s->gamma < 45555) + avctx->color_trc = frame->color_trc = AVCOL_TRC_GAMMA22; + else if (s->gamma > 35614 && s->gamma < 35814) + avctx->color_trc = frame->color_trc = AVCOL_TRC_GAMMA28; + else if (s->gamma > 38362 && s->gamma < 38562) + avctx->color_trc = frame->color_trc = AVCOL_TRC_SMPTE428; + else if (s->gamma > 99900 && s->gamma < 100100) + avctx->color_trc = frame->color_trc = AVCOL_TRC_LINEAR; + } + + /* PNG only supports RGB */ + avctx->colorspace = frame->colorspace = AVCOL_SPC_RGB; + if (!s->have_cicp || s->cicp_range == 1) + avctx->color_range = frame->color_range = AVCOL_RANGE_JPEG; + + /* + * tRNS sets alpha depth to full, so we ignore sBIT if set. + * As a result we must wait until now to set + * avctx->bits_per_raw_sample in case tRNS appears after sBIT + */ + if (!s->has_trns && s->significant_bits > 0) + avctx->bits_per_raw_sample = s->significant_bits; + + if (s->have_clli) { + AVContentLightMetadata *clli; + + ret = ff_decode_content_light_new(avctx, frame, &clli); + if (ret < 0) + return ret; + + if (clli) { + /* + * 0.0001 divisor value + * see: https://www.w3.org/TR/png-3/#cLLi-chunk + */ + clli->MaxCLL = s->clli_max / 10000; + clli->MaxFALL = s->clli_avg / 10000; + } + } + + if (s->have_mdcv) { + AVMasteringDisplayMetadata *mdcv; + + ret = ff_decode_mastering_display_new(avctx, frame, &mdcv); + if (ret < 0) + return ret; + + if (mdcv) { + mdcv->has_primaries = 1; + for (int i = 0; i < 3; i++) { + mdcv->display_primaries[i][0] = av_make_q(s->mdcv_primaries[i][0], 50000); + mdcv->display_primaries[i][1] = av_make_q(s->mdcv_primaries[i][1], 50000); + } + mdcv->white_point[0] = av_make_q(s->mdcv_white_point[0], 50000); + mdcv->white_point[1] = av_make_q(s->mdcv_white_point[1], 50000); + mdcv->has_luminance = 1; + mdcv->max_luminance = av_make_q(s->mdcv_max_lum, 10000); + mdcv->min_luminance = av_make_q(s->mdcv_min_lum, 10000); + } + } + + return 0; +} + static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s, GetByteContext *gb, AVFrame *p) { @@ -728,7 +874,7 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s, s->bpp += byte_depth; } - ff_thread_release_ext_buffer(avctx, &s->picture); + ff_thread_release_ext_buffer(&s->picture); if (s->dispose_op == APNG_DISPOSE_OP_PREVIOUS) { /* We only need a buffer for the current picture. */ ret = ff_thread_get_buffer(avctx, p, 0); @@ -755,9 +901,11 @@ static int decode_idat_chunk(AVCodecContext *avctx, PNGDecContext *s, } p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; - p->interlaced_frame = !!s->interlace_type; + p->flags |= AV_FRAME_FLAG_KEY; + p->flags |= AV_FRAME_FLAG_INTERLACED * !!s->interlace_type; + if ((ret = populate_avctx_color_fields(avctx, p)) < 0) + return ret; ff_thread_finish_setup(avctx); /* compute the compressed row size */ @@ -918,6 +1066,44 @@ static int decode_iccp_chunk(PNGDecContext *s, GetByteContext *gb) return ret; } +static int decode_sbit_chunk(AVCodecContext *avctx, PNGDecContext *s, + GetByteContext *gb) +{ + int bits = 0; + int channels; + + if (!(s->hdr_state & PNG_IHDR)) { + av_log(avctx, AV_LOG_ERROR, "sBIT before IHDR\n"); + return AVERROR_INVALIDDATA; + } + + if (s->pic_state & PNG_IDAT) { + av_log(avctx, AV_LOG_ERROR, "sBIT after IDAT\n"); + return AVERROR_INVALIDDATA; + } + + channels = s->color_type & PNG_COLOR_MASK_PALETTE ? 3 : ff_png_get_nb_channels(s->color_type); + + if (bytestream2_get_bytes_left(gb) != channels) { + av_log(avctx, AV_LOG_ERROR, "Invalid sBIT size: %d, expected: %d\n", + bytestream2_get_bytes_left(gb), channels); + return AVERROR_INVALIDDATA; + } + + for (int i = 0; i < channels; i++) { + int b = bytestream2_get_byteu(gb); + bits = FFMAX(b, bits); + } + + if (bits <= 0 || bits > (s->color_type & PNG_COLOR_MASK_PALETTE ? 8 : s->bit_depth)) { + av_log(avctx, AV_LOG_ERROR, "Invalid significant bits: %d\n", bits); + return AVERROR_INVALIDDATA; + } + s->significant_bits = bits; + + return 0; +} + static void handle_small_bpp(PNGDecContext *s, AVFrame *p) { if (s->bits_per_pixel == 1 && s->color_type == PNG_COLOR_TYPE_PALETTE) { @@ -1034,7 +1220,7 @@ static int decode_fctl_chunk(AVCodecContext *avctx, PNGDecContext *s, return AVERROR_INVALIDDATA; } - if ((sequence_number == 0 || !s->last_picture.f->data[0]) && + if ((sequence_number == 0 || !s->last_picture.f) && dispose_op == APNG_DISPOSE_OP_PREVIOUS) { // No previous frame to revert to for the first frame // Spec says to just treat it as a APNG_DISPOSE_OP_BACKGROUND @@ -1259,6 +1445,10 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, case MKTAG('t', 'E', 'X', 't'): case MKTAG('I', 'D', 'A', 'T'): case MKTAG('t', 'R', 'N', 'S'): + case MKTAG('s', 'R', 'G', 'B'): + case MKTAG('c', 'I', 'C', 'P'): + case MKTAG('c', 'H', 'R', 'M'): + case MKTAG('g', 'A', 'M', 'A'): break; default: continue; @@ -1327,11 +1517,8 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, if (bytestream2_get_byte(&gb_chunk) != 0) av_log(avctx, AV_LOG_WARNING, "nonzero cICP matrix\n"); s->cicp_range = bytestream2_get_byte(&gb_chunk); - if (s->cicp_range != 0 && s->cicp_range != 1) { - av_log(avctx, AV_LOG_ERROR, "invalid cICP range: %d\n", s->cicp_range); - ret = AVERROR_INVALIDDATA; - goto fail; - } + if (s->cicp_range != 0 && s->cicp_range != 1) + av_log(avctx, AV_LOG_WARNING, "invalid cICP range: %d\n", s->cicp_range); s->have_cicp = 1; break; case MKTAG('s', 'R', 'G', 'B'): @@ -1358,13 +1545,17 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, break; } + case MKTAG('s', 'B', 'I', 'T'): + if ((ret = decode_sbit_chunk(avctx, s, &gb_chunk)) < 0) + goto fail; + break; case MKTAG('g', 'A', 'M', 'A'): { AVBPrint bp; char *gamma_str; - int num = bytestream2_get_be32(&gb_chunk); + s->gamma = bytestream2_get_be32(&gb_chunk); av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED); - av_bprintf(&bp, "%i/%i", num, 100000); + av_bprintf(&bp, "%i/%i", s->gamma, 100000); ret = av_bprint_finalize(&bp, &gamma_str); if (ret < 0) return ret; @@ -1373,6 +1564,30 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, break; } + case MKTAG('c', 'L', 'L', 'i'): + if (bytestream2_get_bytes_left(&gb_chunk) != 8) { + av_log(avctx, AV_LOG_WARNING, "Invalid cLLi chunk size: %d\n", bytestream2_get_bytes_left(&gb_chunk)); + break; + } + s->have_clli = 1; + s->clli_max = bytestream2_get_be32u(&gb_chunk); + s->clli_avg = bytestream2_get_be32u(&gb_chunk); + break; + case MKTAG('m', 'D', 'C', 'v'): + if (bytestream2_get_bytes_left(&gb_chunk) != 24) { + av_log(avctx, AV_LOG_WARNING, "Invalid mDCv chunk size: %d\n", bytestream2_get_bytes_left(&gb_chunk)); + break; + } + s->have_mdcv = 1; + for (int i = 0; i < 3; i++) { + s->mdcv_primaries[i][0] = bytestream2_get_be16u(&gb_chunk); + s->mdcv_primaries[i][1] = bytestream2_get_be16u(&gb_chunk); + } + s->mdcv_white_point[0] = bytestream2_get_be16u(&gb_chunk); + s->mdcv_white_point[1] = bytestream2_get_be16u(&gb_chunk); + s->mdcv_max_lum = bytestream2_get_be32u(&gb_chunk); + s->mdcv_min_lum = bytestream2_get_be32u(&gb_chunk); + break; case MKTAG('I', 'E', 'N', 'D'): if (!(s->pic_state & PNG_ALLIMAGE)) av_log(avctx, AV_LOG_ERROR, "IEND without all image\n"); @@ -1505,56 +1720,8 @@ static void clear_frame_metadata(PNGDecContext *s) static int output_frame(PNGDecContext *s, AVFrame *f) { - AVCodecContext *avctx = s->avctx; int ret; - if (s->have_cicp) { - if (s->cicp_primaries >= AVCOL_PRI_NB) - av_log(avctx, AV_LOG_WARNING, "unrecognized cICP primaries\n"); - else - avctx->color_primaries = f->color_primaries = s->cicp_primaries; - if (s->cicp_trc >= AVCOL_TRC_NB) - av_log(avctx, AV_LOG_WARNING, "unrecognized cICP transfer\n"); - else - avctx->color_trc = f->color_trc = s->cicp_trc; - avctx->color_range = f->color_range = - s->cicp_range == 0 ? AVCOL_RANGE_MPEG : AVCOL_RANGE_JPEG; - } else if (s->iccp_data) { - AVFrameSideData *sd = av_frame_new_side_data(f, AV_FRAME_DATA_ICC_PROFILE, s->iccp_data_len); - if (!sd) { - ret = AVERROR(ENOMEM); - goto fail; - } - memcpy(sd->data, s->iccp_data, s->iccp_data_len); - - av_dict_set(&sd->metadata, "name", s->iccp_name, 0); - } else if (s->have_srgb) { - avctx->color_primaries = f->color_primaries = AVCOL_PRI_BT709; - avctx->color_trc = f->color_trc = AVCOL_TRC_IEC61966_2_1; - } else if (s->have_chrm) { - AVColorPrimariesDesc desc; - enum AVColorPrimaries prim; - desc.wp.x = av_make_q(s->white_point[0], 100000); - desc.wp.y = av_make_q(s->white_point[1], 100000); - desc.prim.r.x = av_make_q(s->display_primaries[0][0], 100000); - desc.prim.r.y = av_make_q(s->display_primaries[0][1], 100000); - desc.prim.g.x = av_make_q(s->display_primaries[1][0], 100000); - desc.prim.g.y = av_make_q(s->display_primaries[1][1], 100000); - desc.prim.b.x = av_make_q(s->display_primaries[2][0], 100000); - desc.prim.b.y = av_make_q(s->display_primaries[2][1], 100000); - prim = av_csp_primaries_id_from_desc(&desc); - if (prim != AVCOL_PRI_UNSPECIFIED) - avctx->color_primaries = f->color_primaries = prim; - else - av_log(avctx, AV_LOG_WARNING, "unknown cHRM primaries\n"); - } - - /* these chunks override gAMA */ - if (s->iccp_data || s->have_srgb || s->have_cicp) - av_dict_set(&s->frame_metadata, "gamma", NULL, 0); - - avctx->colorspace = f->colorspace = AVCOL_SPC_RGB; - if (s->stereo_mode >= 0) { AVStereo3D *stereo3d = av_stereo3d_create_side_data(f); if (!stereo3d) { @@ -1619,7 +1786,7 @@ static int decode_frame_png(AVCodecContext *avctx, AVFrame *p, goto the_end; if (!(avctx->active_thread_type & FF_THREAD_FRAME)) { - ff_thread_release_ext_buffer(avctx, &s->last_picture); + ff_thread_release_ext_buffer(&s->last_picture); FFSWAP(ThreadFrame, s->picture, s->last_picture); } @@ -1672,9 +1839,9 @@ static int decode_frame_apng(AVCodecContext *avctx, AVFrame *p, if (!(avctx->active_thread_type & FF_THREAD_FRAME)) { if (s->dispose_op == APNG_DISPOSE_OP_PREVIOUS) { - ff_thread_release_ext_buffer(avctx, &s->picture); + ff_thread_release_ext_buffer(&s->picture); } else { - ff_thread_release_ext_buffer(avctx, &s->last_picture); + ff_thread_release_ext_buffer(&s->last_picture); FFSWAP(ThreadFrame, s->picture, s->last_picture); } } @@ -1715,7 +1882,7 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src) src_frame = psrc->dispose_op == APNG_DISPOSE_OP_PREVIOUS ? &psrc->last_picture : &psrc->picture; - ff_thread_release_ext_buffer(dst, &pdst->last_picture); + ff_thread_release_ext_buffer(&pdst->last_picture); if (src_frame && src_frame->f->data[0]) { ret = ff_thread_ref_frame(&pdst->last_picture, src_frame); if (ret < 0) @@ -1730,8 +1897,6 @@ static av_cold int png_dec_init(AVCodecContext *avctx) { PNGDecContext *s = avctx->priv_data; - avctx->color_range = AVCOL_RANGE_JPEG; - s->avctx = avctx; s->last_picture.f = av_frame_alloc(); s->picture.f = av_frame_alloc(); @@ -1747,9 +1912,9 @@ static av_cold int png_dec_end(AVCodecContext *avctx) { PNGDecContext *s = avctx->priv_data; - ff_thread_release_ext_buffer(avctx, &s->last_picture); + ff_thread_release_ext_buffer(&s->last_picture); av_frame_free(&s->last_picture.f); - ff_thread_release_ext_buffer(avctx, &s->picture); + ff_thread_release_ext_buffer(&s->picture); av_frame_free(&s->picture.f); av_freep(&s->buffer); s->buffer_size = 0; diff --git a/libavcodec/pngenc.c b/libavcodec/pngenc.c index 1489256d00d..819cd836594 100644 --- a/libavcodec/pngenc.c +++ b/libavcodec/pngenc.c @@ -32,6 +32,7 @@ #include "libavutil/crc.h" #include "libavutil/csp.h" #include "libavutil/libm.h" +#include "libavutil/mastering_display_metadata.h" #include "libavutil/opt.h" #include "libavutil/rational.h" #include "libavutil/stereo3d.h" @@ -294,8 +295,9 @@ static int png_write_row(AVCodecContext *avctx, const uint8_t *data, int size) return 0; } -#define AV_WB32_PNG(buf, n) AV_WB32(buf, lrint((n) * 100000)) -#define AV_WB32_PNG_D(buf, d) AV_WB32_PNG(buf, av_q2d(d)) +#define PNG_LRINT(d, divisor) lrint((d) * (divisor)) +#define PNG_Q2D(q, divisor) PNG_LRINT(av_q2d(q), (divisor)) +#define AV_WB32_PNG_D(buf, q) AV_WB32(buf, PNG_Q2D(q, 100000)) static int png_get_chrm(enum AVColorPrimaries prim, uint8_t *buf) { const AVColorPrimariesDesc *desc = av_csp_primaries_desc_from_id(prim); @@ -320,7 +322,7 @@ static int png_get_gama(enum AVColorTransferCharacteristic trc, uint8_t *buf) if (gamma <= 1e-6) return 0; - AV_WB32_PNG(buf, 1.0 / gamma); + AV_WB32(buf, PNG_LRINT(1.0 / gamma, 100000)); return 1; } @@ -437,11 +439,42 @@ static int encode_headers(AVCodecContext *avctx, const AVFrame *pict) png_write_chunk(&s->bytestream, MKTAG('c', 'I', 'C', 'P'), s->buf, 4); } + side_data = av_frame_get_side_data(pict, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + if (side_data) { + AVContentLightMetadata *clli = (AVContentLightMetadata *) side_data->data; + AV_WB32(s->buf, clli->MaxCLL * 10000); + AV_WB32(s->buf + 4, clli->MaxFALL * 10000); + png_write_chunk(&s->bytestream, MKTAG('c', 'L', 'L', 'i'), s->buf, 8); + } + + side_data = av_frame_get_side_data(pict, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + if (side_data) { + AVMasteringDisplayMetadata *mdcv = (AVMasteringDisplayMetadata *) side_data->data; + if (mdcv->has_luminance && mdcv->has_primaries) { + for (int i = 0; i < 3; i++) { + AV_WB16(s->buf + 2*i, PNG_Q2D(mdcv->display_primaries[i][0], 50000)); + AV_WB16(s->buf + 2*i + 2, PNG_Q2D(mdcv->display_primaries[i][1], 50000)); + } + AV_WB16(s->buf + 12, PNG_Q2D(mdcv->white_point[0], 50000)); + AV_WB16(s->buf + 14, PNG_Q2D(mdcv->white_point[1], 50000)); + AV_WB32(s->buf + 16, PNG_Q2D(mdcv->max_luminance, 10000)); + AV_WB32(s->buf + 20, PNG_Q2D(mdcv->min_luminance, 10000)); + png_write_chunk(&s->bytestream, MKTAG('m', 'D', 'C', 'v'), s->buf, 24); + } + } + if (png_get_chrm(pict->color_primaries, s->buf)) png_write_chunk(&s->bytestream, MKTAG('c', 'H', 'R', 'M'), s->buf, 32); if (png_get_gama(pict->color_trc, s->buf)) png_write_chunk(&s->bytestream, MKTAG('g', 'A', 'M', 'A'), s->buf, 4); + if (avctx->bits_per_raw_sample > 0 && + avctx->bits_per_raw_sample < (s->color_type & PNG_COLOR_MASK_PALETTE ? 8 : s->bit_depth)) { + int len = s->color_type & PNG_COLOR_MASK_PALETTE ? 3 : ff_png_get_nb_channels(s->color_type); + memset(s->buf, avctx->bits_per_raw_sample, len); + png_write_chunk(&s->bytestream, MKTAG('s', 'B', 'I', 'T'), s->buf, len); + } + /* put the palette if needed, must be after colorspace information */ if (s->color_type == PNG_COLOR_TYPE_PALETTE) { int has_alpha, alpha, i; @@ -605,7 +638,7 @@ static int encode_png(AVCodecContext *avctx, AVPacket *pkt, enc_row_size = deflateBound(&s->zstream.zstream, (avctx->width * s->bits_per_pixel + 7) >> 3); max_packet_size = - AV_INPUT_BUFFER_MIN_SIZE + // headers + FF_INPUT_BUFFER_MIN_SIZE + // headers avctx->height * ( enc_row_size + 12 * (((int64_t)enc_row_size + IOBUF_SIZE - 1) / IOBUF_SIZE) // IDAT * ceil(enc_row_size / IOBUF_SIZE) @@ -936,7 +969,7 @@ static int encode_apng(AVCodecContext *avctx, AVPacket *pkt, enc_row_size = deflateBound(&s->zstream.zstream, (avctx->width * s->bits_per_pixel + 7) >> 3); max_packet_size = - AV_INPUT_BUFFER_MIN_SIZE + // headers + FF_INPUT_BUFFER_MIN_SIZE + // headers avctx->height * ( enc_row_size + (4 + 12) * (((int64_t)enc_row_size + IOBUF_SIZE - 1) / IOBUF_SIZE) // fdAT * ceil(enc_row_size / IOBUF_SIZE) @@ -950,7 +983,7 @@ static int encode_apng(AVCodecContext *avctx, AVPacket *pkt, if (!pict) return AVERROR(EINVAL); - s->bytestream = s->extra_data = av_malloc(AV_INPUT_BUFFER_MIN_SIZE); + s->bytestream = s->extra_data = av_malloc(FF_INPUT_BUFFER_MIN_SIZE); if (!s->extra_data) return AVERROR(ENOMEM); @@ -1053,8 +1086,7 @@ static int encode_apng(AVCodecContext *avctx, AVPacket *pkt, } } - av_frame_unref(s->last_frame); - ret = av_frame_ref(s->last_frame, pict); + ret = av_frame_replace(s->last_frame, pict); if (ret < 0) return ret; @@ -1172,13 +1204,13 @@ static av_cold int png_enc_close(AVCodecContext *avctx) static const AVOption options[] = { {"dpi", "Set image resolution (in dots per inch)", OFFSET(dpi), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 0x10000, VE}, {"dpm", "Set image resolution (in dots per meter)", OFFSET(dpm), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 0x10000, VE}, - { "pred", "Prediction method", OFFSET(filter_type), AV_OPT_TYPE_INT, { .i64 = PNG_FILTER_VALUE_NONE }, PNG_FILTER_VALUE_NONE, PNG_FILTER_VALUE_MIXED, VE, "pred" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_NONE }, INT_MIN, INT_MAX, VE, "pred" }, - { "sub", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_SUB }, INT_MIN, INT_MAX, VE, "pred" }, - { "up", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_UP }, INT_MIN, INT_MAX, VE, "pred" }, - { "avg", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_AVG }, INT_MIN, INT_MAX, VE, "pred" }, - { "paeth", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_PAETH }, INT_MIN, INT_MAX, VE, "pred" }, - { "mixed", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_MIXED }, INT_MIN, INT_MAX, VE, "pred" }, + { "pred", "Prediction method", OFFSET(filter_type), AV_OPT_TYPE_INT, { .i64 = PNG_FILTER_VALUE_NONE }, PNG_FILTER_VALUE_NONE, PNG_FILTER_VALUE_MIXED, VE, .unit = "pred" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_NONE }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "sub", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_SUB }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "up", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_UP }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "avg", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_AVG }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "paeth", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_PAETH }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "mixed", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PNG_FILTER_VALUE_MIXED }, INT_MIN, INT_MAX, VE, .unit = "pred" }, { NULL}, }; diff --git a/libavcodec/pnm.c b/libavcodec/pnm.c index 77d24eeaf7c..9f93c1ae1d4 100644 --- a/libavcodec/pnm.c +++ b/libavcodec/pnm.c @@ -97,10 +97,12 @@ int ff_pnm_decode_header(AVCodecContext *avctx, PNMContext * const s) } else if (s->type==1 || s->type==4) { avctx->pix_fmt = AV_PIX_FMT_MONOWHITE; } else if (s->type==2 || s->type==5) { - if (avctx->codec_id == AV_CODEC_ID_PGMYUV) + if (avctx->codec_id == AV_CODEC_ID_PGMYUV) { avctx->pix_fmt = AV_PIX_FMT_YUV420P; - else + avctx->color_range = AVCOL_RANGE_MPEG; + } else { avctx->pix_fmt = AV_PIX_FMT_GRAY8; + } } else if (s->type==3 || s->type==6) { avctx->pix_fmt = AV_PIX_FMT_RGB24; } else if (s->type==7) { diff --git a/libavcodec/pnmdec.c b/libavcodec/pnmdec.c index 978e4c037ea..40cc2ae868c 100644 --- a/libavcodec/pnmdec.c +++ b/libavcodec/pnmdec.c @@ -65,7 +65,7 @@ static int pnm_decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; avctx->bits_per_raw_sample = av_log2(s->maxval) + 1; switch (avctx->pix_fmt) { @@ -137,7 +137,7 @@ static int pnm_decode_frame(AVCodecContext *avctx, AVFrame *p, if(s->type < 4 || (is_mono && s->type==7)){ for (i=0; iheight; i++) { PutBitContext pb; - init_put_bits(&pb, ptr, linesize); + init_put_bits(&pb, ptr, FFABS(linesize)); for(j=0; jwidth * components; j++){ unsigned int c=0; unsigned v=0; @@ -264,7 +264,7 @@ static int pnm_decode_frame(AVCodecContext *avctx, AVFrame *p, break; case AV_PIX_FMT_GBRPF32: if (!s->half) { - if (avctx->width * avctx->height * 12 > s->bytestream_end - s->bytestream) + if (avctx->width * avctx->height * 12LL > s->bytestream_end - s->bytestream) return AVERROR_INVALIDDATA; scale = 1.f / s->scale; if (s->endian) { diff --git a/libavcodec/ppc/Makefile b/libavcodec/ppc/Makefile index bc13d8a0ce7..10b9ca60daa 100644 --- a/libavcodec/ppc/Makefile +++ b/libavcodec/ppc/Makefile @@ -1,9 +1,6 @@ # subsystems OBJS-$(CONFIG_AUDIODSP) += ppc/audiodsp.o OBJS-$(CONFIG_BLOCKDSP) += ppc/blockdsp.o -OBJS-$(CONFIG_FFT) += ppc/fft_init.o \ - ppc/fft_altivec.o \ - ppc/fft_vsx.o OBJS-$(CONFIG_FDCTDSP) += ppc/fdctdsp.o OBJS-$(CONFIG_FMTCONVERT) += ppc/fmtconvert_altivec.o OBJS-$(CONFIG_H264CHROMA) += ppc/h264chroma_init.o diff --git a/libavcodec/ppc/fdctdsp.c b/libavcodec/ppc/fdctdsp.c index 4ab516c6b35..ae3c1b18ff7 100644 --- a/libavcodec/ppc/fdctdsp.c +++ b/libavcodec/ppc/fdctdsp.c @@ -25,6 +25,7 @@ #include "libavutil/ppc/cpu.h" #include "libavutil/ppc/util_altivec.h" +#include "libavcodec/avcodec.h" #include "libavcodec/fdctdsp.h" #include "fdct.h" diff --git a/libavcodec/ppc/fft_altivec.S b/libavcodec/ppc/fft_altivec.S deleted file mode 100644 index 8cd68d6a90f..00000000000 --- a/libavcodec/ppc/fft_altivec.S +++ /dev/null @@ -1,458 +0,0 @@ -/* - * FFT transform with Altivec optimizations - * Copyright (c) 2009 Loren Merritt - * - * This algorithm (though not any of the implementation details) is - * based on libdjbfft by D. J. Bernstein. - * - * 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 - */ - -/* - * These functions are not individually interchangeable with the C versions. - * While C takes arrays of FFTComplex, Altivec leaves intermediate results - * in blocks as convenient to the vector size. - * i.e. {4x real, 4x imaginary, 4x real, ...} - * - * I ignore standard calling convention. - * Instead, the following registers are treated as global constants: - * v14: zero - * v15..v18: cosines - * v19..v29: permutations - * r9: 16 - * r12: ff_cos_tabs - * and the rest are free for local use. - */ - -#include "config.h" - -#if HAVE_GNU_AS && HAVE_ALTIVEC && HAVE_BIGENDIAN - -#include "asm.S" - -.text - -.macro addi2 ra, imm // add 32-bit immediate -.if \imm & 0xffff - addi \ra, \ra, \imm@l -.endif -.if (\imm+0x8000)>>16 - addis \ra, \ra, \imm@ha -.endif -.endm - -.macro FFT4 a0, a1, a2, a3 // in:0-1 out:2-3 - vperm \a2,\a0,\a1,v20 // vcprm(0,1,s2,s1) // {r0,i0,r3,i2} - vperm \a3,\a0,\a1,v21 // vcprm(2,3,s0,s3) // {r1,i1,r2,i3} - vaddfp \a0,\a2,\a3 // {t1,t2,t6,t5} - vsubfp \a1,\a2,\a3 // {t3,t4,t8,t7} - vmrghw \a2,\a0,\a1 // vcprm(0,s0,1,s1) // {t1,t3,t2,t4} - vperm \a3,\a0,\a1,v22 // vcprm(2,s3,3,s2) // {t6,t7,t5,t8} - vaddfp \a0,\a2,\a3 // {r0,r1,i0,i1} - vsubfp \a1,\a2,\a3 // {r2,r3,i2,i3} - vperm \a2,\a0,\a1,v23 // vcprm(0,1,s0,s1) // {r0,r1,r2,r3} - vperm \a3,\a0,\a1,v24 // vcprm(2,3,s2,s3) // {i0,i1,i2,i3} -.endm - -.macro FFT4x2 a0, a1, b0, b1, a2, a3, b2, b3 - vperm \a2,\a0,\a1,v20 // vcprm(0,1,s2,s1) // {r0,i0,r3,i2} - vperm \a3,\a0,\a1,v21 // vcprm(2,3,s0,s3) // {r1,i1,r2,i3} - vperm \b2,\b0,\b1,v20 - vperm \b3,\b0,\b1,v21 - vaddfp \a0,\a2,\a3 // {t1,t2,t6,t5} - vsubfp \a1,\a2,\a3 // {t3,t4,t8,t7} - vaddfp \b0,\b2,\b3 - vsubfp \b1,\b2,\b3 - vmrghw \a2,\a0,\a1 // vcprm(0,s0,1,s1) // {t1,t3,t2,t4} - vperm \a3,\a0,\a1,v22 // vcprm(2,s3,3,s2) // {t6,t7,t5,t8} - vmrghw \b2,\b0,\b1 - vperm \b3,\b0,\b1,v22 - vaddfp \a0,\a2,\a3 // {r0,r1,i0,i1} - vsubfp \a1,\a2,\a3 // {r2,r3,i2,i3} - vaddfp \b0,\b2,\b3 - vsubfp \b1,\b2,\b3 - vperm \a2,\a0,\a1,v23 // vcprm(0,1,s0,s1) // {r0,r1,r2,r3} - vperm \a3,\a0,\a1,v24 // vcprm(2,3,s2,s3) // {i0,i1,i2,i3} - vperm \b2,\b0,\b1,v23 - vperm \b3,\b0,\b1,v24 -.endm - -.macro FFT8 a0, a1, b0, b1, a2, a3, b2, b3, b4 // in,out:a0-b1 - vmrghw \b2,\b0,\b1 // vcprm(0,s0,1,s1) // {r4,r6,i4,i6} - vmrglw \b3,\b0,\b1 // vcprm(2,s2,3,s3) // {r5,r7,i5,i7} - vperm \a2,\a0,\a1,v20 // FFT4 ... - vperm \a3,\a0,\a1,v21 - vaddfp \b0,\b2,\b3 // {t1,t3,t2,t4} - vsubfp \b1,\b2,\b3 // {r5,r7,i5,i7} - vperm \b4,\b1,\b1,v25 // vcprm(2,3,0,1) // {i5,i7,r5,r7} - vaddfp \a0,\a2,\a3 - vsubfp \a1,\a2,\a3 - vmaddfp \b1,\b1,v17,v14 // * {-1,1,1,-1}/sqrt(2) - vmaddfp \b1,\b4,v18,\b1 // * { 1,1,1,1 }/sqrt(2) // {t8,ta,t7,t9} - vmrghw \a2,\a0,\a1 - vperm \a3,\a0,\a1,v22 - vperm \b2,\b0,\b1,v26 // vcprm(1,2,s3,s0) // {t3,t2,t9,t8} - vperm \b3,\b0,\b1,v27 // vcprm(0,3,s2,s1) // {t1,t4,t7,ta} - vaddfp \a0,\a2,\a3 - vsubfp \a1,\a2,\a3 - vaddfp \b0,\b2,\b3 // {t1,t2,t9,ta} - vsubfp \b1,\b2,\b3 // {t6,t5,tc,tb} - vperm \a2,\a0,\a1,v23 - vperm \a3,\a0,\a1,v24 - vperm \b2,\b0,\b1,v28 // vcprm(0,2,s1,s3) // {t1,t9,t5,tb} - vperm \b3,\b0,\b1,v29 // vcprm(1,3,s0,s2) // {t2,ta,t6,tc} - vsubfp \b0,\a2,\b2 // {r4,r5,r6,r7} - vsubfp \b1,\a3,\b3 // {i4,i5,i6,i7} - vaddfp \a0,\a2,\b2 // {r0,r1,r2,r3} - vaddfp \a1,\a3,\b3 // {i0,i1,i2,i3} -.endm - -.macro BF d0,d1,s0,s1 - vsubfp \d1,\s0,\s1 - vaddfp \d0,\s0,\s1 -.endm - -.macro zip d0,d1,s0,s1 - vmrghw \d0,\s0,\s1 - vmrglw \d1,\s0,\s1 -.endm - -.macro def_fft4 interleave -fft4\interleave\()_altivec: - lvx v0, 0,r3 - lvx v1,r9,r3 - FFT4 v0,v1,v2,v3 -.ifnb \interleave - zip v0,v1,v2,v3 - stvx v0, 0,r3 - stvx v1,r9,r3 -.else - stvx v2, 0,r3 - stvx v3,r9,r3 -.endif - blr -.endm - -.macro def_fft8 interleave -fft8\interleave\()_altivec: - addi r4,r3,32 - lvx v0, 0,r3 - lvx v1,r9,r3 - lvx v2, 0,r4 - lvx v3,r9,r4 - FFT8 v0,v1,v2,v3,v4,v5,v6,v7,v8 -.ifnb \interleave - zip v4,v5,v0,v1 - zip v6,v7,v2,v3 - stvx v4, 0,r3 - stvx v5,r9,r3 - stvx v6, 0,r4 - stvx v7,r9,r4 -.else - stvx v0, 0,r3 - stvx v1,r9,r3 - stvx v2, 0,r4 - stvx v3,r9,r4 -.endif - blr -.endm - -.macro def_fft16 interleave -fft16\interleave\()_altivec: - addi r5,r3,64 - addi r6,r3,96 - addi r4,r3,32 - lvx v0, 0,r5 - lvx v1,r9,r5 - lvx v2, 0,r6 - lvx v3,r9,r6 - FFT4x2 v0,v1,v2,v3,v4,v5,v6,v7 - lvx v0, 0,r3 - lvx v1,r9,r3 - lvx v2, 0,r4 - lvx v3,r9,r4 - FFT8 v0,v1,v2,v3,v8,v9,v10,v11,v12 - vmaddfp v8,v4,v15,v14 // r2*wre - vmaddfp v9,v5,v15,v14 // i2*wre - vmaddfp v10,v6,v15,v14 // r3*wre - vmaddfp v11,v7,v15,v14 // i3*wre - vmaddfp v8,v5,v16,v8 // i2*wim - vnmsubfp v9,v4,v16,v9 // r2*wim - vnmsubfp v10,v7,v16,v10 // i3*wim - vmaddfp v11,v6,v16,v11 // r3*wim - BF v10,v12,v10,v8 - BF v11,v13,v9,v11 - BF v0,v4,v0,v10 - BF v3,v7,v3,v12 - BF v1,v5,v1,v11 - BF v2,v6,v2,v13 -.ifnb \interleave - zip v8, v9,v0,v1 - zip v10,v11,v2,v3 - zip v12,v13,v4,v5 - zip v14,v15,v6,v7 - stvx v8, 0,r3 - stvx v9,r9,r3 - stvx v10, 0,r4 - stvx v11,r9,r4 - stvx v12, 0,r5 - stvx v13,r9,r5 - stvx v14, 0,r6 - stvx v15,r9,r6 -.else - stvx v0, 0,r3 - stvx v4, 0,r5 - stvx v3,r9,r4 - stvx v7,r9,r6 - stvx v1,r9,r3 - stvx v5,r9,r5 - stvx v2, 0,r4 - stvx v6, 0,r6 -.endif - blr -.endm - -// void pass(float *z, float *wre, int n) -.macro PASS interleave, suffix -fft_pass\suffix\()_altivec: - mtctr r5 - slwi r0,r5,4 - slwi r7,r5,6 // o2 - slwi r5,r5,5 // o1 - add r10,r5,r7 // o3 - add r0,r4,r0 // wim - addi r6,r5,16 // o1+16 - addi r8,r7,16 // o2+16 - addi r11,r10,16 // o3+16 -1: - lvx v8, 0,r4 // wre - lvx v10, 0,r0 // wim - sub r0,r0,r9 - lvx v9, 0,r0 - vperm v9,v9,v10,v19 // vcprm(s0,3,2,1) => wim[0 .. -3] - lvx v4,r3,r7 // r2 = z[o2] - lvx v5,r3,r8 // i2 = z[o2+16] - lvx v6,r3,r10 // r3 = z[o3] - lvx v7,r3,r11 // i3 = z[o3+16] - vmaddfp v10,v4,v8,v14 // r2*wre - vmaddfp v11,v5,v8,v14 // i2*wre - vmaddfp v12,v6,v8,v14 // r3*wre - vmaddfp v13,v7,v8,v14 // i3*wre - lvx v0, 0,r3 // r0 = z[0] - lvx v3,r3,r6 // i1 = z[o1+16] - vmaddfp v10,v5,v9,v10 // i2*wim - vnmsubfp v11,v4,v9,v11 // r2*wim - vnmsubfp v12,v7,v9,v12 // i3*wim - vmaddfp v13,v6,v9,v13 // r3*wim - lvx v1,r3,r9 // i0 = z[16] - lvx v2,r3,r5 // r1 = z[o1] - BF v12,v8,v12,v10 - BF v13,v9,v11,v13 - BF v0,v4,v0,v12 - BF v3,v7,v3,v8 -.if !\interleave - stvx v0, 0,r3 - stvx v4,r3,r7 - stvx v3,r3,r6 - stvx v7,r3,r11 -.endif - BF v1,v5,v1,v13 - BF v2,v6,v2,v9 -.if !\interleave - stvx v1,r3,r9 - stvx v2,r3,r5 - stvx v5,r3,r8 - stvx v6,r3,r10 -.else - vmrghw v8,v0,v1 - vmrglw v9,v0,v1 - stvx v8, 0,r3 - stvx v9,r3,r9 - vmrghw v8,v2,v3 - vmrglw v9,v2,v3 - stvx v8,r3,r5 - stvx v9,r3,r6 - vmrghw v8,v4,v5 - vmrglw v9,v4,v5 - stvx v8,r3,r7 - stvx v9,r3,r8 - vmrghw v8,v6,v7 - vmrglw v9,v6,v7 - stvx v8,r3,r10 - stvx v9,r3,r11 -.endif - addi r3,r3,32 - addi r4,r4,16 - bdnz 1b - sub r3,r3,r5 - blr -.endm - -#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ - -#define WORD_0 0x00,0x01,0x02,0x03 -#define WORD_1 0x04,0x05,0x06,0x07 -#define WORD_2 0x08,0x09,0x0a,0x0b -#define WORD_3 0x0c,0x0d,0x0e,0x0f -#define WORD_s0 0x10,0x11,0x12,0x13 -#define WORD_s1 0x14,0x15,0x16,0x17 -#define WORD_s2 0x18,0x19,0x1a,0x1b -#define WORD_s3 0x1c,0x1d,0x1e,0x1f - -#define vcprm(a, b, c, d) .byte WORD_##a, WORD_##b, WORD_##c, WORD_##d - - .rodata - .align 4 -fft_data: - .float 0, 0, 0, 0 - .float 1, 0.92387953, M_SQRT1_2, 0.38268343 - .float 0, 0.38268343, M_SQRT1_2, 0.92387953 - .float -M_SQRT1_2, M_SQRT1_2, M_SQRT1_2,-M_SQRT1_2 - .float M_SQRT1_2, M_SQRT1_2, M_SQRT1_2, M_SQRT1_2 - vcprm(s0,3,2,1) - vcprm(0,1,s2,s1) - vcprm(2,3,s0,s3) - vcprm(2,s3,3,s2) - vcprm(0,1,s0,s1) - vcprm(2,3,s2,s3) - vcprm(2,3,0,1) - vcprm(1,2,s3,s0) - vcprm(0,3,s2,s1) - vcprm(0,2,s1,s3) - vcprm(1,3,s0,s2) - -.macro lvm b, r, regs:vararg - lvx \r, 0, \b - addi \b, \b, 16 - .ifnb \regs - lvm \b, \regs - .endif -.endm - -.macro stvm b, r, regs:vararg - stvx \r, 0, \b - addi \b, \b, 16 - .ifnb \regs - stvm \b, \regs - .endif -.endm - -.macro fft_calc interleave -extfunc ff_fft_calc\interleave\()_altivec - mflr r0 - stp r0, 2*PS(R(1)) - stpu r1, -(160+16*PS)(R(1)) - get_got r11 - addi r6, r1, 16*PS - stvm r6, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29 - mfvrsave r0 - stw r0, 15*PS(R(1)) -#if __APPLE__ - li r6, 0xfffffffc -#else - li r6, -4 -#endif - mtvrsave r6 - - movrel r6, fft_data, r11 - lvm r6, v14, v15, v16, v17, v18, v19, v20, v21 - lvm r6, v22, v23, v24, v25, v26, v27, v28, v29 - - li r9, 16 - movrel r12, X(ff_cos_tabs), r11 - - movrel r6, fft_dispatch_tab\interleave\()_altivec, r11 - lwz r3, 0(R(3)) - subi r3, r3, 2 - slwi r3, r3, 2+ARCH_PPC64 - lpx r3, r3, r6 - mtctr r3 - mr r3, r4 - bctrl - - addi r6, r1, 16*PS - lvm r6, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29 - lwz r6, 15*PS(R(1)) - mtvrsave r6 - lp r1, 0(R(1)) - lp r0, 2*PS(R(1)) - mtlr r0 - blr -.endm - -.macro DECL_FFT suffix, bits, n, n2, n4 -fft\n\suffix\()_altivec: - mflr r0 - stp r0,PS*(\bits-3)(R(1)) - bl fft\n2\()_altivec - addi2 r3,\n*4 - bl fft\n4\()_altivec - addi2 r3,\n*2 - bl fft\n4\()_altivec - addi2 r3,\n*-6 - lp r0,PS*(\bits-3)(R(1)) - lp r4,\bits*PS(R(12)) - mtlr r0 - li r5,\n/16 - b fft_pass\suffix\()_altivec -.endm - -.macro DECL_FFTS interleave, suffix - .text - def_fft4 \suffix - def_fft8 \suffix - def_fft16 \suffix - PASS \interleave, \suffix - DECL_FFT \suffix, 5, 32, 16, 8 - DECL_FFT \suffix, 6, 64, 32, 16 - DECL_FFT \suffix, 7, 128, 64, 32 - DECL_FFT \suffix, 8, 256, 128, 64 - DECL_FFT \suffix, 9, 512, 256, 128 - DECL_FFT \suffix,10, 1024, 512, 256 - DECL_FFT \suffix,11, 2048, 1024, 512 - DECL_FFT \suffix,12, 4096, 2048, 1024 - DECL_FFT \suffix,13, 8192, 4096, 2048 - DECL_FFT \suffix,14,16384, 8192, 4096 - DECL_FFT \suffix,15,32768,16384, 8192 - DECL_FFT \suffix,16,65536,32768,16384 - - fft_calc \suffix - - .rodata - .align 3 -fft_dispatch_tab\suffix\()_altivec: - PTR fft4\suffix\()_altivec - PTR fft8\suffix\()_altivec - PTR fft16\suffix\()_altivec - PTR fft32\suffix\()_altivec - PTR fft64\suffix\()_altivec - PTR fft128\suffix\()_altivec - PTR fft256\suffix\()_altivec - PTR fft512\suffix\()_altivec - PTR fft1024\suffix\()_altivec - PTR fft2048\suffix\()_altivec - PTR fft4096\suffix\()_altivec - PTR fft8192\suffix\()_altivec - PTR fft16384\suffix\()_altivec - PTR fft32768\suffix\()_altivec - PTR fft65536\suffix\()_altivec -.endm - -DECL_FFTS 0 -DECL_FFTS 1, _interleave - -#endif /* HAVE_GNU_AS && HAVE_ALTIVEC && HAVE_BIGENDIAN */ diff --git a/libavcodec/ppc/fft_init.c b/libavcodec/ppc/fft_init.c deleted file mode 100644 index 65ce64f6a17..00000000000 --- a/libavcodec/ppc/fft_init.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * FFT/IFFT transforms - * AltiVec-enabled - * Copyright (c) 2009 Loren Merritt - * - * 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 "config.h" -#include "libavutil/attributes.h" -#include "libavutil/cpu.h" -#include "libavutil/ppc/cpu.h" -#include "libavutil/ppc/util_altivec.h" -#include "libavcodec/fft.h" - -/** - * Do a complex FFT with the parameters defined in ff_fft_init(). - * The input data must be permuted before with s->revtab table. - * No 1.0 / sqrt(n) normalization is done. - * AltiVec-enabled: - * This code assumes that the 'z' pointer is 16 bytes-aligned. - * It also assumes all FFTComplex are 8 bytes-aligned pairs of floats. - */ - -#if HAVE_VSX -#include "fft_vsx.h" -#else -void ff_fft_calc_altivec(FFTContext *s, FFTComplex *z); -void ff_fft_calc_interleave_altivec(FFTContext *s, FFTComplex *z); -#endif - -#if HAVE_GNU_AS && HAVE_ALTIVEC && (HAVE_BIGENDIAN || HAVE_VSX) -static void imdct_half_altivec(FFTContext *s, FFTSample *output, const FFTSample *input) -{ - int j, k; - int n = 1 << s->mdct_bits; - int n4 = n >> 2; - int n8 = n >> 3; - int n32 = n >> 5; - const uint16_t *revtabj = s->revtab; - const uint16_t *revtabk = s->revtab+n4; - const vec_f *tcos = (const vec_f*)(s->tcos+n8); - const vec_f *tsin = (const vec_f*)(s->tsin+n8); - const vec_f *pin = (const vec_f*)(input+n4); - vec_f *pout = (vec_f*)(output+n4); - - /* pre rotation */ - k = n32-1; - do { - vec_f cos,sin,cos0,sin0,cos1,sin1,re,im,r0,i0,r1,i1,a,b,c,d; -#define CMULA(p,o0,o1,o2,o3)\ - a = pin[ k*2+p]; /* { z[k].re, z[k].im, z[k+1].re, z[k+1].im } */\ - b = pin[-k*2-p-1]; /* { z[-k-2].re, z[-k-2].im, z[-k-1].re, z[-k-1].im } */\ - re = vec_perm(a, b, vcprm(0,2,s0,s2)); /* { z[k].re, z[k+1].re, z[-k-2].re, z[-k-1].re } */\ - im = vec_perm(a, b, vcprm(s3,s1,3,1)); /* { z[-k-1].im, z[-k-2].im, z[k+1].im, z[k].im } */\ - cos = vec_perm(cos0, cos1, vcprm(o0,o1,s##o2,s##o3)); /* { cos[k], cos[k+1], cos[-k-2], cos[-k-1] } */\ - sin = vec_perm(sin0, sin1, vcprm(o0,o1,s##o2,s##o3));\ - r##p = im*cos - re*sin;\ - i##p = re*cos + im*sin; -#define STORE2(v,dst)\ - j = dst;\ - vec_ste(v, 0, output+j*2);\ - vec_ste(v, 4, output+j*2); -#define STORE8(p)\ - a = vec_perm(r##p, i##p, vcprm(0,s0,0,s0));\ - b = vec_perm(r##p, i##p, vcprm(1,s1,1,s1));\ - c = vec_perm(r##p, i##p, vcprm(2,s2,2,s2));\ - d = vec_perm(r##p, i##p, vcprm(3,s3,3,s3));\ - STORE2(a, revtabk[ p*2-4]);\ - STORE2(b, revtabk[ p*2-3]);\ - STORE2(c, revtabj[-p*2+2]);\ - STORE2(d, revtabj[-p*2+3]); - - cos0 = tcos[k]; - sin0 = tsin[k]; - cos1 = tcos[-k-1]; - sin1 = tsin[-k-1]; - CMULA(0, 0,1,2,3); - CMULA(1, 2,3,0,1); - STORE8(0); - STORE8(1); - revtabj += 4; - revtabk -= 4; - k--; - } while(k >= 0); - -#if HAVE_VSX - ff_fft_calc_vsx(s, (FFTComplex*)output); -#else - ff_fft_calc_altivec(s, (FFTComplex*)output); -#endif - - /* post rotation + reordering */ - j = -n32; - k = n32-1; - do { - vec_f cos,sin,re,im,a,b,c,d; -#define CMULB(d0,d1,o)\ - re = pout[o*2];\ - im = pout[o*2+1];\ - cos = tcos[o];\ - sin = tsin[o];\ - d0 = im*sin - re*cos;\ - d1 = re*sin + im*cos; - - CMULB(a,b,j); - CMULB(c,d,k); - pout[2*j] = vec_perm(a, d, vcprm(0,s3,1,s2)); - pout[2*j+1] = vec_perm(a, d, vcprm(2,s1,3,s0)); - pout[2*k] = vec_perm(c, b, vcprm(0,s3,1,s2)); - pout[2*k+1] = vec_perm(c, b, vcprm(2,s1,3,s0)); - j++; - k--; - } while(k >= 0); -} - -static void imdct_calc_altivec(FFTContext *s, FFTSample *output, const FFTSample *input) -{ - int k; - int n = 1 << s->mdct_bits; - int n4 = n >> 2; - int n16 = n >> 4; - vec_u32 sign = {1U<<31,1U<<31,1U<<31,1U<<31}; - vec_u32 *p0 = (vec_u32*)(output+n4); - vec_u32 *p1 = (vec_u32*)(output+n4*3); - - imdct_half_altivec(s, output + n4, input); - - for (k = 0; k < n16; k++) { - vec_u32 a = p0[k] ^ sign; - vec_u32 b = p1[-k-1]; - p0[-k-1] = vec_perm(a, a, vcprm(3,2,1,0)); - p1[k] = vec_perm(b, b, vcprm(3,2,1,0)); - } -} -#endif /* HAVE_GNU_AS && HAVE_ALTIVEC && (HAVE_BIGENDIAN || HAVE_VSX) */ - -av_cold void ff_fft_init_ppc(FFTContext *s) -{ -#if HAVE_GNU_AS && HAVE_ALTIVEC && (HAVE_BIGENDIAN || HAVE_VSX) - if (!PPC_ALTIVEC(av_get_cpu_flags())) - return; - -#if HAVE_VSX - s->fft_calc = ff_fft_calc_interleave_vsx; -#else - s->fft_calc = ff_fft_calc_interleave_altivec; -#endif - if (s->mdct_bits >= 5) { - s->imdct_calc = imdct_calc_altivec; - s->imdct_half = imdct_half_altivec; - } -#endif /* HAVE_GNU_AS && HAVE_ALTIVEC && HAVE_BIGENDIAN */ -} diff --git a/libavcodec/ppc/fft_vsx.c b/libavcodec/ppc/fft_vsx.c deleted file mode 100644 index c365fa1380c..00000000000 --- a/libavcodec/ppc/fft_vsx.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * FFT transform, optimized with VSX built-in functions - * Copyright (c) 2014 Rong Yan - * - * This algorithm (though not any of the implementation details) is - * based on libdjbfft by D. J. Bernstein. - * - * 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 "config.h" -#include "libavutil/cpu.h" -#include "libavutil/ppc/util_altivec.h" -#include "libavcodec/fft.h" -#include "libavcodec/fft-internal.h" -#include "fft_vsx.h" - -#if HAVE_VSX - -static void fft32_vsx_interleave(FFTComplex *z) -{ - fft16_vsx_interleave(z); - fft8_vsx_interleave(z+16); - fft8_vsx_interleave(z+24); - pass_vsx_interleave(z,ff_cos_32,4); -} - -static void fft64_vsx_interleave(FFTComplex *z) -{ - fft32_vsx_interleave(z); - fft16_vsx_interleave(z+32); - fft16_vsx_interleave(z+48); - pass_vsx_interleave(z,ff_cos_64, 8); -} -static void fft128_vsx_interleave(FFTComplex *z) -{ - fft64_vsx_interleave(z); - fft32_vsx_interleave(z+64); - fft32_vsx_interleave(z+96); - pass_vsx_interleave(z,ff_cos_128,16); -} -static void fft256_vsx_interleave(FFTComplex *z) -{ - fft128_vsx_interleave(z); - fft64_vsx_interleave(z+128); - fft64_vsx_interleave(z+192); - pass_vsx_interleave(z,ff_cos_256,32); -} -static void fft512_vsx_interleave(FFTComplex *z) -{ - fft256_vsx_interleave(z); - fft128_vsx_interleave(z+256); - fft128_vsx_interleave(z+384); - pass_vsx_interleave(z,ff_cos_512,64); -} -static void fft1024_vsx_interleave(FFTComplex *z) -{ - fft512_vsx_interleave(z); - fft256_vsx_interleave(z+512); - fft256_vsx_interleave(z+768); - pass_vsx_interleave(z,ff_cos_1024,128); - -} -static void fft2048_vsx_interleave(FFTComplex *z) -{ - fft1024_vsx_interleave(z); - fft512_vsx_interleave(z+1024); - fft512_vsx_interleave(z+1536); - pass_vsx_interleave(z,ff_cos_2048,256); -} -static void fft4096_vsx_interleave(FFTComplex *z) -{ - fft2048_vsx_interleave(z); - fft1024_vsx_interleave(z+2048); - fft1024_vsx_interleave(z+3072); - pass_vsx_interleave(z,ff_cos_4096, 512); -} -static void fft8192_vsx_interleave(FFTComplex *z) -{ - fft4096_vsx_interleave(z); - fft2048_vsx_interleave(z+4096); - fft2048_vsx_interleave(z+6144); - pass_vsx_interleave(z,ff_cos_8192,1024); -} -static void fft16384_vsx_interleave(FFTComplex *z) -{ - fft8192_vsx_interleave(z); - fft4096_vsx_interleave(z+8192); - fft4096_vsx_interleave(z+12288); - pass_vsx_interleave(z,ff_cos_16384,2048); -} -static void fft32768_vsx_interleave(FFTComplex *z) -{ - fft16384_vsx_interleave(z); - fft8192_vsx_interleave(z+16384); - fft8192_vsx_interleave(z+24576); - pass_vsx_interleave(z,ff_cos_32768,4096); -} -static void fft65536_vsx_interleave(FFTComplex *z) -{ - fft32768_vsx_interleave(z); - fft16384_vsx_interleave(z+32768); - fft16384_vsx_interleave(z+49152); - pass_vsx_interleave(z,ff_cos_65536,8192); -} - -static void fft32_vsx(FFTComplex *z) -{ - fft16_vsx(z); - fft8_vsx(z+16); - fft8_vsx(z+24); - pass_vsx(z,ff_cos_32,4); -} - -static void fft64_vsx(FFTComplex *z) -{ - fft32_vsx(z); - fft16_vsx(z+32); - fft16_vsx(z+48); - pass_vsx(z,ff_cos_64, 8); -} -static void fft128_vsx(FFTComplex *z) -{ - fft64_vsx(z); - fft32_vsx(z+64); - fft32_vsx(z+96); - pass_vsx(z,ff_cos_128,16); -} -static void fft256_vsx(FFTComplex *z) -{ - fft128_vsx(z); - fft64_vsx(z+128); - fft64_vsx(z+192); - pass_vsx(z,ff_cos_256,32); -} -static void fft512_vsx(FFTComplex *z) -{ - fft256_vsx(z); - fft128_vsx(z+256); - fft128_vsx(z+384); - pass_vsx(z,ff_cos_512,64); -} -static void fft1024_vsx(FFTComplex *z) -{ - fft512_vsx(z); - fft256_vsx(z+512); - fft256_vsx(z+768); - pass_vsx(z,ff_cos_1024,128); - -} -static void fft2048_vsx(FFTComplex *z) -{ - fft1024_vsx(z); - fft512_vsx(z+1024); - fft512_vsx(z+1536); - pass_vsx(z,ff_cos_2048,256); -} -static void fft4096_vsx(FFTComplex *z) -{ - fft2048_vsx(z); - fft1024_vsx(z+2048); - fft1024_vsx(z+3072); - pass_vsx(z,ff_cos_4096, 512); -} -static void fft8192_vsx(FFTComplex *z) -{ - fft4096_vsx(z); - fft2048_vsx(z+4096); - fft2048_vsx(z+6144); - pass_vsx(z,ff_cos_8192,1024); -} -static void fft16384_vsx(FFTComplex *z) -{ - fft8192_vsx(z); - fft4096_vsx(z+8192); - fft4096_vsx(z+12288); - pass_vsx(z,ff_cos_16384,2048); -} -static void fft32768_vsx(FFTComplex *z) -{ - fft16384_vsx(z); - fft8192_vsx(z+16384); - fft8192_vsx(z+24576); - pass_vsx(z,ff_cos_32768,4096); -} -static void fft65536_vsx(FFTComplex *z) -{ - fft32768_vsx(z); - fft16384_vsx(z+32768); - fft16384_vsx(z+49152); - pass_vsx(z,ff_cos_65536,8192); -} - -static void (* const fft_dispatch_vsx[])(FFTComplex*) = { - fft4_vsx, fft8_vsx, fft16_vsx, fft32_vsx, fft64_vsx, fft128_vsx, fft256_vsx, fft512_vsx, fft1024_vsx, - fft2048_vsx, fft4096_vsx, fft8192_vsx, fft16384_vsx, fft32768_vsx, fft65536_vsx, -}; -static void (* const fft_dispatch_vsx_interleave[])(FFTComplex*) = { - fft4_vsx_interleave, fft8_vsx_interleave, fft16_vsx_interleave, fft32_vsx_interleave, fft64_vsx_interleave, - fft128_vsx_interleave, fft256_vsx_interleave, fft512_vsx_interleave, fft1024_vsx_interleave, - fft2048_vsx_interleave, fft4096_vsx_interleave, fft8192_vsx_interleave, fft16384_vsx_interleave, fft32768_vsx_interleave, fft65536_vsx_interleave, -}; -void ff_fft_calc_interleave_vsx(FFTContext *s, FFTComplex *z) -{ - fft_dispatch_vsx_interleave[s->nbits-2](z); -} -void ff_fft_calc_vsx(FFTContext *s, FFTComplex *z) -{ - fft_dispatch_vsx[s->nbits-2](z); -} -#endif /* HAVE_VSX */ diff --git a/libavcodec/ppc/fft_vsx.h b/libavcodec/ppc/fft_vsx.h deleted file mode 100644 index 1e44031aa5e..00000000000 --- a/libavcodec/ppc/fft_vsx.h +++ /dev/null @@ -1,829 +0,0 @@ -#ifndef AVCODEC_PPC_FFT_VSX_H -#define AVCODEC_PPC_FFT_VSX_H -/* - * FFT transform, optimized with VSX built-in functions - * Copyright (c) 2014 Rong Yan Copyright (c) 2009 Loren Merritt - * - * This algorithm (though not any of the implementation details) is - * based on libdjbfft by D. J. Bernstein, and fft_altivec_s.S. - * - * 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 "config.h" -#include "libavutil/cpu.h" -#include "libavutil/ppc/util_altivec.h" -#include "libavcodec/fft.h" -#include "libavcodec/fft-internal.h" - -#if HAVE_VSX - -void ff_fft_calc_interleave_vsx(FFTContext *s, FFTComplex *z); -void ff_fft_calc_vsx(FFTContext *s, FFTComplex *z); - - -#define byte_2complex (2*sizeof(FFTComplex)) -#define byte_4complex (4*sizeof(FFTComplex)) -#define byte_6complex (6*sizeof(FFTComplex)) -#define byte_8complex (8*sizeof(FFTComplex)) -#define byte_10complex (10*sizeof(FFTComplex)) -#define byte_12complex (12*sizeof(FFTComplex)) -#define byte_14complex (14*sizeof(FFTComplex)) - -inline static void pass_vsx_interleave(FFTComplex *z, const FFTSample *wre, unsigned int n) -{ - int o1 = n<<1; - int o2 = n<<2; - int o3 = o1+o2; - int i1, i2, i3; - FFTSample* out = (FFTSample*)z; - const FFTSample *wim = wre+o1; - vec_f vz0, vzo1, vzo2, vzo3; - vec_f x0, x1, x2, x3; - vec_f x4, x5, x6, x7; - vec_f x8, x9, x10, x11; - vec_f x12, x13, x14, x15; - vec_f x16, x17, x18, x19; - vec_f x20, x21, x22, x23; - vec_f vz0plus1, vzo1plus1, vzo2plus1, vzo3plus1; - vec_f y0, y1, y2, y3; - vec_f y4, y5, y8, y9; - vec_f y10, y13, y14, y15; - vec_f y16, y17, y18, y19; - vec_f y20, y21, y22, y23; - vec_f wr1, wi1, wr0, wi0; - vec_f wr2, wi2, wr3, wi3; - vec_f xmulwi0, xmulwi1, ymulwi2, ymulwi3; - - n = n-2; - i1 = o1*sizeof(FFTComplex); - i2 = o2*sizeof(FFTComplex); - i3 = o3*sizeof(FFTComplex); - vzo2 = vec_ld(i2, &(out[0])); // zo2.r zo2.i z(o2+1).r z(o2+1).i - vzo2plus1 = vec_ld(i2+16, &(out[0])); - vzo3 = vec_ld(i3, &(out[0])); // zo3.r zo3.i z(o3+1).r z(o3+1).i - vzo3plus1 = vec_ld(i3+16, &(out[0])); - vz0 = vec_ld(0, &(out[0])); // z0.r z0.i z1.r z1.i - vz0plus1 = vec_ld(16, &(out[0])); - vzo1 = vec_ld(i1, &(out[0])); // zo1.r zo1.i z(o1+1).r z(o1+1).i - vzo1plus1 = vec_ld(i1+16, &(out[0])); - - x0 = vec_add(vzo2, vzo3); - x1 = vec_sub(vzo2, vzo3); - y0 = vec_add(vzo2plus1, vzo3plus1); - y1 = vec_sub(vzo2plus1, vzo3plus1); - - wr1 = vec_splats(wre[1]); - wi1 = vec_splats(wim[-1]); - wi2 = vec_splats(wim[-2]); - wi3 = vec_splats(wim[-3]); - wr2 = vec_splats(wre[2]); - wr3 = vec_splats(wre[3]); - - x2 = vec_perm(x0, x1, vcprm(2,s2,3,s3)); - x3 = vec_perm(x0, x1, vcprm(s3,3,s2,2)); - - y4 = vec_perm(y0, y1, vcprm(s1,1,s0,0)); - y5 = vec_perm(y0, y1, vcprm(s3,3,s2,2)); - y2 = vec_perm(y0, y1, vcprm(0,s0,1,s1)); - y3 = vec_perm(y0, y1, vcprm(2,s2,3,s3)); - - ymulwi2 = vec_mul(y4, wi2); - ymulwi3 = vec_mul(y5, wi3); - x4 = vec_mul(x2, wr1); - x5 = vec_mul(x3, wi1); - y8 = vec_madd(y2, wr2, ymulwi2); - y9 = vec_msub(y2, wr2, ymulwi2); - x6 = vec_add(x4, x5); - x7 = vec_sub(x4, x5); - y13 = vec_madd(y3, wr3, ymulwi3); - y14 = vec_msub(y3, wr3, ymulwi3); - - x8 = vec_perm(x6, x7, vcprm(0,1,s2,s3)); - y10 = vec_perm(y8, y9, vcprm(0,1,s2,s3)); - y15 = vec_perm(y13, y14, vcprm(0,1,s2,s3)); - - x9 = vec_perm(x0, x8, vcprm(0,1,s0,s2)); - x10 = vec_perm(x1, x8, vcprm(1,0,s3,s1)); - - y16 = vec_perm(y10, y15, vcprm(0,2,s0,s2)); - y17 = vec_perm(y10, y15, vcprm(3,1,s3,s1)); - - x11 = vec_add(vz0, x9); - x12 = vec_sub(vz0, x9); - x13 = vec_add(vzo1, x10); - x14 = vec_sub(vzo1, x10); - - y18 = vec_add(vz0plus1, y16); - y19 = vec_sub(vz0plus1, y16); - y20 = vec_add(vzo1plus1, y17); - y21 = vec_sub(vzo1plus1, y17); - - x15 = vec_perm(x13, x14, vcprm(0,s1,2,s3)); - x16 = vec_perm(x13, x14, vcprm(s0,1,s2,3)); - y22 = vec_perm(y20, y21, vcprm(0,s1,2,s3)); - y23 = vec_perm(y20, y21, vcprm(s0,1,s2,3)); - - - vec_st(x11, 0, &(out[0])); - vec_st(y18, 16, &(out[0])); - vec_st(x15, i1, &(out[0])); - vec_st(y22, i1+16, &(out[0])); - vec_st(x12, i2, &(out[0])); - vec_st(y19, i2+16, &(out[0])); - vec_st(x16, i3, &(out[0])); - vec_st(y23, i3+16, &(out[0])); - - do { - out += 8; - wre += 4; - wim -= 4; - wr0 = vec_splats(wre[0]); - wr1 = vec_splats(wre[1]); - wi0 = vec_splats(wim[0]); - wi1 = vec_splats(wim[-1]); - - wr2 = vec_splats(wre[2]); - wr3 = vec_splats(wre[3]); - wi2 = vec_splats(wim[-2]); - wi3 = vec_splats(wim[-3]); - - vzo2 = vec_ld(i2, &(out[0])); // zo2.r zo2.i z(o2+1).r z(o2+1).i - vzo2plus1 = vec_ld(i2+16, &(out[0])); - vzo3 = vec_ld(i3, &(out[0])); // zo3.r zo3.i z(o3+1).r z(o3+1).i - vzo3plus1 = vec_ld(i3+16, &(out[0])); - vz0 = vec_ld(0, &(out[0])); // z0.r z0.i z1.r z1.i - vz0plus1 = vec_ld(16, &(out[0])); - vzo1 = vec_ld(i1, &(out[0])); // zo1.r zo1.i z(o1+1).r z(o1+1).i - vzo1plus1 = vec_ld(i1+16, &(out[0])); - - x0 = vec_add(vzo2, vzo3); - x1 = vec_sub(vzo2, vzo3); - - y0 = vec_add(vzo2plus1, vzo3plus1); - y1 = vec_sub(vzo2plus1, vzo3plus1); - - x4 = vec_perm(x0, x1, vcprm(s1,1,s0,0)); - x5 = vec_perm(x0, x1, vcprm(s3,3,s2,2)); - x2 = vec_perm(x0, x1, vcprm(0,s0,1,s1)); - x3 = vec_perm(x0, x1, vcprm(2,s2,3,s3)); - - y2 = vec_perm(y0, y1, vcprm(0,s0,1,s1)); - y3 = vec_perm(y0, y1, vcprm(2,s2,3,s3)); - xmulwi0 = vec_mul(x4, wi0); - xmulwi1 = vec_mul(x5, wi1); - - y4 = vec_perm(y0, y1, vcprm(s1,1,s0,0)); - y5 = vec_perm(y0, y1, vcprm(s3,3,s2,2)); - - x8 = vec_madd(x2, wr0, xmulwi0); - x9 = vec_msub(x2, wr0, xmulwi0); - ymulwi2 = vec_mul(y4, wi2); - ymulwi3 = vec_mul(y5, wi3); - - x13 = vec_madd(x3, wr1, xmulwi1); - x14 = vec_msub(x3, wr1, xmulwi1); - - y8 = vec_madd(y2, wr2, ymulwi2); - y9 = vec_msub(y2, wr2, ymulwi2); - y13 = vec_madd(y3, wr3, ymulwi3); - y14 = vec_msub(y3, wr3, ymulwi3); - - x10 = vec_perm(x8, x9, vcprm(0,1,s2,s3)); - x15 = vec_perm(x13, x14, vcprm(0,1,s2,s3)); - - y10 = vec_perm(y8, y9, vcprm(0,1,s2,s3)); - y15 = vec_perm(y13, y14, vcprm(0,1,s2,s3)); - - x16 = vec_perm(x10, x15, vcprm(0,2,s0,s2)); - x17 = vec_perm(x10, x15, vcprm(3,1,s3,s1)); - - y16 = vec_perm(y10, y15, vcprm(0,2,s0,s2)); - y17 = vec_perm(y10, y15, vcprm(3,1,s3,s1)); - - x18 = vec_add(vz0, x16); - x19 = vec_sub(vz0, x16); - x20 = vec_add(vzo1, x17); - x21 = vec_sub(vzo1, x17); - - y18 = vec_add(vz0plus1, y16); - y19 = vec_sub(vz0plus1, y16); - y20 = vec_add(vzo1plus1, y17); - y21 = vec_sub(vzo1plus1, y17); - - x22 = vec_perm(x20, x21, vcprm(0,s1,2,s3)); - x23 = vec_perm(x20, x21, vcprm(s0,1,s2,3)); - - y22 = vec_perm(y20, y21, vcprm(0,s1,2,s3)); - y23 = vec_perm(y20, y21, vcprm(s0,1,s2,3)); - - vec_st(x18, 0, &(out[0])); - vec_st(y18, 16, &(out[0])); - vec_st(x22, i1, &(out[0])); - vec_st(y22, i1+16, &(out[0])); - vec_st(x19, i2, &(out[0])); - vec_st(y19, i2+16, &(out[0])); - vec_st(x23, i3, &(out[0])); - vec_st(y23, i3+16, &(out[0])); - } while (n-=2); -} - -inline static void fft2_vsx_interleave(FFTComplex *z) -{ - FFTSample r1, i1; - - r1 = z[0].re - z[1].re; - z[0].re += z[1].re; - z[1].re = r1; - - i1 = z[0].im - z[1].im; - z[0].im += z[1].im; - z[1].im = i1; - } - -inline static void fft4_vsx_interleave(FFTComplex *z) -{ - vec_f a, b, c, d; - float* out= (float*)z; - a = vec_ld(0, &(out[0])); - b = vec_ld(byte_2complex, &(out[0])); - - c = vec_perm(a, b, vcprm(0,1,s2,s1)); - d = vec_perm(a, b, vcprm(2,3,s0,s3)); - a = vec_add(c, d); - b = vec_sub(c, d); - - c = vec_perm(a, b, vcprm(0,1,s0,s1)); - d = vec_perm(a, b, vcprm(2,3,s3,s2)); - - a = vec_add(c, d); - b = vec_sub(c, d); - vec_st(a, 0, &(out[0])); - vec_st(b, byte_2complex, &(out[0])); -} - -inline static void fft8_vsx_interleave(FFTComplex *z) -{ - vec_f vz0, vz1, vz2, vz3; - vec_f x0, x1, x2, x3; - vec_f x4, x5, x6, x7; - vec_f x8, x9, x10, x11; - vec_f x12, x13, x14, x15; - vec_f x16, x17, x18, x19; - vec_f x20, x21, x22, x23; - vec_f x24, x25, x26, x27; - vec_f x28, x29, x30, x31; - vec_f x32, x33, x34; - - float* out= (float*)z; - vec_f vc1 = {sqrthalf, sqrthalf, sqrthalf, sqrthalf}; - - vz0 = vec_ld(0, &(out[0])); - vz1 = vec_ld(byte_2complex, &(out[0])); - vz2 = vec_ld(byte_4complex, &(out[0])); - vz3 = vec_ld(byte_6complex, &(out[0])); - - x0 = vec_perm(vz0, vz1, vcprm(0,1,s2,s1)); - x1 = vec_perm(vz0, vz1, vcprm(2,3,s0,s3)); - x2 = vec_perm(vz2, vz3, vcprm(2,1,s0,s1)); - x3 = vec_perm(vz2, vz3, vcprm(0,3,s2,s3)); - - x4 = vec_add(x0, x1); - x5 = vec_sub(x0, x1); - x6 = vec_add(x2, x3); - x7 = vec_sub(x2, x3); - - x8 = vec_perm(x4, x5, vcprm(0,1,s0,s1)); - x9 = vec_perm(x4, x5, vcprm(2,3,s3,s2)); - x10 = vec_perm(x6, x7, vcprm(2,1,s2,s1)); - x11 = vec_perm(x6, x7, vcprm(0,3,s0,s3)); - - x12 = vec_add(x8, x9); - x13 = vec_sub(x8, x9); - x14 = vec_add(x10, x11); - x15 = vec_sub(x10, x11); - x16 = vec_perm(x12, x13, vcprm(0,s0,1,s1)); - x17 = vec_perm(x14, x15, vcprm(0,s0,1,s1)); - x18 = vec_perm(x16, x17, vcprm(s0,s3,s2,s1)); - x19 = vec_add(x16, x18); // z0.r z2.r z0.i z2.i - x20 = vec_sub(x16, x18); // z4.r z6.r z4.i z6.i - - x21 = vec_perm(x12, x13, vcprm(2,s2,3,s3)); - x22 = vec_perm(x14, x15, vcprm(2,3,s2,s3)); - x23 = vec_perm(x14, x15, vcprm(3,2,s3,s2)); - x24 = vec_add(x22, x23); - x25 = vec_sub(x22, x23); - x26 = vec_mul( vec_perm(x24, x25, vcprm(2,s2,0,s0)), vc1); - - x27 = vec_add(x21, x26); // z1.r z7.r z1.i z3.i - x28 = vec_sub(x21, x26); //z5.r z3.r z5.i z7.i - - x29 = vec_perm(x19, x27, vcprm(0,2,s0,s2)); // z0.r z0.i z1.r z1.i - x30 = vec_perm(x19, x27, vcprm(1,3,s1,s3)); // z2.r z2.i z7.r z3.i - x31 = vec_perm(x20, x28, vcprm(0,2,s0,s2)); // z4.r z4.i z5.r z5.i - x32 = vec_perm(x20, x28, vcprm(1,3,s1,s3)); // z6.r z6.i z3.r z7.i - x33 = vec_perm(x30, x32, vcprm(0,1,s2,3)); // z2.r z2.i z3.r z3.i - x34 = vec_perm(x30, x32, vcprm(s0,s1,2,s3)); // z6.r z6.i z7.r z7.i - - vec_st(x29, 0, &(out[0])); - vec_st(x33, byte_2complex, &(out[0])); - vec_st(x31, byte_4complex, &(out[0])); - vec_st(x34, byte_6complex, &(out[0])); -} - -inline static void fft16_vsx_interleave(FFTComplex *z) -{ - float* out= (float*)z; - vec_f vc0 = {sqrthalf, sqrthalf, sqrthalf, sqrthalf}; - vec_f vc1 = {ff_cos_16[1], ff_cos_16[1], ff_cos_16[1], ff_cos_16[1]}; - vec_f vc2 = {ff_cos_16[3], ff_cos_16[3], ff_cos_16[3], ff_cos_16[3]}; - vec_f vz0, vz1, vz2, vz3; - vec_f vz4, vz5, vz6, vz7; - vec_f x0, x1, x2, x3; - vec_f x4, x5, x6, x7; - vec_f x8, x9, x10, x11; - vec_f x12, x13, x14, x15; - vec_f x16, x17, x18, x19; - vec_f x20, x21, x22, x23; - vec_f x24, x25, x26, x27; - vec_f x28, x29, x30, x31; - vec_f x32, x33, x34, x35; - vec_f x36, x37, x38, x39; - vec_f x40, x41, x42, x43; - vec_f x44, x45, x46, x47; - vec_f x48, x49, x50, x51; - vec_f x52, x53, x54, x55; - vec_f x56, x57, x58, x59; - vec_f x60, x61, x62, x63; - vec_f x64, x65, x66, x67; - vec_f x68, x69, x70, x71; - vec_f x72, x73, x74, x75; - vec_f x76, x77, x78, x79; - vec_f x80, x81, x82, x83; - vec_f x84, x85, x86; - - vz0 = vec_ld(0, &(out[0])); - vz1 = vec_ld(byte_2complex, &(out[0])); - vz2 = vec_ld(byte_4complex, &(out[0])); - vz3 = vec_ld(byte_6complex, &(out[0])); - vz4 = vec_ld(byte_8complex, &(out[0])); - vz5 = vec_ld(byte_10complex, &(out[0])); - vz6 = vec_ld(byte_12complex, &(out[0])); - vz7 = vec_ld(byte_14complex, &(out[0])); - - x0 = vec_perm(vz0, vz1, vcprm(0,1,s2,s1)); - x1 = vec_perm(vz0, vz1, vcprm(2,3,s0,s3)); - x2 = vec_perm(vz2, vz3, vcprm(0,1,s0,s1)); - x3 = vec_perm(vz2, vz3, vcprm(2,3,s2,s3)); - - x4 = vec_perm(vz4, vz5, vcprm(0,1,s2,s1)); - x5 = vec_perm(vz4, vz5, vcprm(2,3,s0,s3)); - x6 = vec_perm(vz6, vz7, vcprm(0,1,s2,s1)); - x7 = vec_perm(vz6, vz7, vcprm(2,3,s0,s3)); - - x8 = vec_add(x0, x1); - x9 = vec_sub(x0, x1); - x10 = vec_add(x2, x3); - x11 = vec_sub(x2, x3); - - x12 = vec_add(x4, x5); - x13 = vec_sub(x4, x5); - x14 = vec_add(x6, x7); - x15 = vec_sub(x6, x7); - - x16 = vec_perm(x8, x9, vcprm(0,1,s0,s1)); - x17 = vec_perm(x8, x9, vcprm(2,3,s3,s2)); - x18 = vec_perm(x10, x11, vcprm(2,1,s1,s2)); - x19 = vec_perm(x10, x11, vcprm(0,3,s0,s3)); - x20 = vec_perm(x12, x14, vcprm(0,1,s0, s1)); - x21 = vec_perm(x12, x14, vcprm(2,3,s2,s3)); - x22 = vec_perm(x13, x15, vcprm(0,1,s0,s1)); - x23 = vec_perm(x13, x15, vcprm(3,2,s3,s2)); - - x24 = vec_add(x16, x17); - x25 = vec_sub(x16, x17); - x26 = vec_add(x18, x19); - x27 = vec_sub(x18, x19); - x28 = vec_add(x20, x21); - x29 = vec_sub(x20, x21); - x30 = vec_add(x22, x23); - x31 = vec_sub(x22, x23); - - x32 = vec_add(x24, x26); - x33 = vec_sub(x24, x26); - x34 = vec_perm(x32, x33, vcprm(0,1,s0,s1)); - - x35 = vec_perm(x28, x29, vcprm(2,1,s1,s2)); - x36 = vec_perm(x28, x29, vcprm(0,3,s0,s3)); - x37 = vec_add(x35, x36); - x38 = vec_sub(x35, x36); - x39 = vec_perm(x37, x38, vcprm(0,1,s1,s0)); - - x40 = vec_perm(x27, x38, vcprm(3,2,s2,s3)); - x41 = vec_perm(x26, x37, vcprm(2,3,s3,s2)); - x42 = vec_add(x40, x41); - x43 = vec_sub(x40, x41); - x44 = vec_mul(x42, vc0); - x45 = vec_mul(x43, vc0); - - x46 = vec_add(x34, x39); // z0.r z0.i z4.r z4.i - x47 = vec_sub(x34, x39); // z8.r z8.i z12.r z12.i - - x48 = vec_perm(x30, x31, vcprm(2,1,s1,s2)); - x49 = vec_perm(x30, x31, vcprm(0,3,s3,s0)); - x50 = vec_add(x48, x49); - x51 = vec_sub(x48, x49); - x52 = vec_mul(x50, vc1); - x53 = vec_mul(x50, vc2); - x54 = vec_mul(x51, vc1); - x55 = vec_mul(x51, vc2); - - x56 = vec_perm(x24, x25, vcprm(2,3,s2,s3)); - x57 = vec_perm(x44, x45, vcprm(0,1,s1,s0)); - x58 = vec_add(x56, x57); - x59 = vec_sub(x56, x57); - - x60 = vec_perm(x54, x55, vcprm(1,0,3,2)); - x61 = vec_perm(x54, x55, vcprm(s1,s0,s3,s2)); - x62 = vec_add(x52, x61); - x63 = vec_sub(x52, x61); - x64 = vec_add(x60, x53); - x65 = vec_sub(x60, x53); - x66 = vec_perm(x62, x64, vcprm(0,1,s3,s2)); - x67 = vec_perm(x63, x65, vcprm(s0,s1,3,2)); - - x68 = vec_add(x58, x66); // z1.r z1.i z3.r z3.i - x69 = vec_sub(x58, x66); // z9.r z9.i z11.r z11.i - x70 = vec_add(x59, x67); // z5.r z5.i z15.r z15.i - x71 = vec_sub(x59, x67); // z13.r z13.i z7.r z7.i - - x72 = vec_perm(x25, x27, vcprm(s1,s0,s2,s3)); - x73 = vec_add(x25, x72); - x74 = vec_sub(x25, x72); - x75 = vec_perm(x73, x74, vcprm(0,1,s0,s1)); - x76 = vec_perm(x44, x45, vcprm(3,2,s2,s3)); - x77 = vec_add(x75, x76); // z2.r z2.i z6.r z6.i - x78 = vec_sub(x75, x76); // z10.r z10.i z14.r z14.i - - x79 = vec_perm(x46, x68, vcprm(0,1,s0,s1)); // z0.r z0.i z1.r z1.i - x80 = vec_perm(x77, x68, vcprm(0,1,s2,s3)); // z2.r z2.i z3.r z3.i - x81 = vec_perm(x46, x70, vcprm(2,3,s0,s1)); // z4.r z4.i z5.r z5.i - x82 = vec_perm(x71, x77, vcprm(s2,s3,2,3)); // z6.r z6.i z7.r z7.i - vec_st(x79, 0, &(out[0])); - vec_st(x80, byte_2complex, &(out[0])); - vec_st(x81, byte_4complex, &(out[0])); - vec_st(x82, byte_6complex, &(out[0])); - x83 = vec_perm(x47, x69, vcprm(0,1,s0,s1)); // z8.r z8.i z9.r z9.i - x84 = vec_perm(x78, x69, vcprm(0,1,s2,s3)); // z10.r z10.i z11.r z11.i - x85 = vec_perm(x47, x71, vcprm(2,3,s0,s1)); // z12.r z12.i z13.r z13.i - x86 = vec_perm(x70, x78, vcprm(s2,s3,2,3)); // z14.r z14.i z15.r z15.i - vec_st(x83, byte_8complex, &(out[0])); - vec_st(x84, byte_10complex, &(out[0])); - vec_st(x85, byte_12complex, &(out[0])); - vec_st(x86, byte_14complex, &(out[0])); -} - -inline static void fft4_vsx(FFTComplex *z) -{ - vec_f a, b, c, d; - float* out= (float*)z; - a = vec_ld(0, &(out[0])); - b = vec_ld(byte_2complex, &(out[0])); - - c = vec_perm(a, b, vcprm(0,1,s2,s1)); - d = vec_perm(a, b, vcprm(2,3,s0,s3)); - a = vec_add(c, d); - b = vec_sub(c, d); - - c = vec_perm(a,b, vcprm(0,s0,1,s1)); - d = vec_perm(a, b, vcprm(2,s3,3,s2)); - - a = vec_add(c, d); - b = vec_sub(c, d); - - c = vec_perm(a, b, vcprm(0,1,s0,s1)); - d = vec_perm(a, b, vcprm(2,3,s2,s3)); - - vec_st(c, 0, &(out[0])); - vec_st(d, byte_2complex, &(out[0])); - return; -} - -inline static void fft8_vsx(FFTComplex *z) -{ - vec_f vz0, vz1, vz2, vz3; - vec_f vz4, vz5, vz6, vz7, vz8; - - float* out= (float*)z; - vec_f vc0 = {0.0, 0.0, 0.0, 0.0}; - vec_f vc1 = {-sqrthalf, sqrthalf, sqrthalf, -sqrthalf}; - vec_f vc2 = {sqrthalf, sqrthalf, sqrthalf, sqrthalf}; - - vz0 = vec_ld(0, &(out[0])); - vz1 = vec_ld(byte_2complex, &(out[0])); - vz2 = vec_ld(byte_4complex, &(out[0])); - vz3 = vec_ld(byte_6complex, &(out[0])); - - vz6 = vec_perm(vz2, vz3, vcprm(0,s0,1,s1)); - vz7 = vec_perm(vz2, vz3, vcprm(2,s2,3,s3)); - vz4 = vec_perm(vz0, vz1, vcprm(0,1,s2,s1)); - vz5 = vec_perm(vz0, vz1, vcprm(2,3,s0,s3)); - - vz2 = vec_add(vz6, vz7); - vz3 = vec_sub(vz6, vz7); - vz8 = vec_perm(vz3, vz3, vcprm(2,3,0,1)); - - vz0 = vec_add(vz4, vz5); - vz1 = vec_sub(vz4, vz5); - - vz3 = vec_madd(vz3, vc1, vc0); - vz3 = vec_madd(vz8, vc2, vz3); - - vz4 = vec_perm(vz0, vz1, vcprm(0,s0,1,s1)); - vz5 = vec_perm(vz0, vz1, vcprm(2,s3,3,s2)); - vz6 = vec_perm(vz2, vz3, vcprm(1,2,s3,s0)); - vz7 = vec_perm(vz2, vz3, vcprm(0,3,s2,s1)); - - vz0 = vec_add(vz4, vz5); - vz1 = vec_sub(vz4, vz5); - vz2 = vec_add(vz6, vz7); - vz3 = vec_sub(vz6, vz7); - - vz4 = vec_perm(vz0, vz1, vcprm(0,1,s0,s1)); - vz5 = vec_perm(vz0, vz1, vcprm(2,3,s2,s3)); - vz6 = vec_perm(vz2, vz3, vcprm(0,2,s1,s3)); - vz7 = vec_perm(vz2, vz3, vcprm(1,3,s0,s2)); - - - vz2 = vec_sub(vz4, vz6); - vz3 = vec_sub(vz5, vz7); - - vz0 = vec_add(vz4, vz6); - vz1 = vec_add(vz5, vz7); - - vec_st(vz0, 0, &(out[0])); - vec_st(vz1, byte_2complex, &(out[0])); - vec_st(vz2, byte_4complex, &(out[0])); - vec_st(vz3, byte_6complex, &(out[0])); - return; -} - -inline static void fft16_vsx(FFTComplex *z) -{ - float* out= (float*)z; - vec_f vc0 = {0.0, 0.0, 0.0, 0.0}; - vec_f vc1 = {-sqrthalf, sqrthalf, sqrthalf, -sqrthalf}; - vec_f vc2 = {sqrthalf, sqrthalf, sqrthalf, sqrthalf}; - vec_f vc3 = {1.0, 0.92387953, sqrthalf, 0.38268343}; - vec_f vc4 = {0.0, 0.38268343, sqrthalf, 0.92387953}; - vec_f vc5 = {-0.0, -0.38268343, -sqrthalf, -0.92387953}; - - vec_f vz0, vz1, vz2, vz3; - vec_f vz4, vz5, vz6, vz7; - vec_f vz8, vz9, vz10, vz11; - vec_f vz12, vz13; - - vz0 = vec_ld(byte_8complex, &(out[0])); - vz1 = vec_ld(byte_10complex, &(out[0])); - vz2 = vec_ld(byte_12complex, &(out[0])); - vz3 = vec_ld(byte_14complex, &(out[0])); - - vz4 = vec_perm(vz0, vz1, vcprm(0,1,s2,s1)); - vz5 = vec_perm(vz0, vz1, vcprm(2,3,s0,s3)); - vz6 = vec_perm(vz2, vz3, vcprm(0,1,s2,s1)); - vz7 = vec_perm(vz2, vz3, vcprm(2,3,s0,s3)); - - vz0 = vec_add(vz4, vz5); - vz1= vec_sub(vz4, vz5); - vz2 = vec_add(vz6, vz7); - vz3 = vec_sub(vz6, vz7); - - vz4 = vec_perm(vz0, vz1, vcprm(0,s0,1,s1)); - vz5 = vec_perm(vz0, vz1, vcprm(2,s3,3,s2)); - vz6 = vec_perm(vz2, vz3, vcprm(0,s0,1,s1)); - vz7 = vec_perm(vz2, vz3, vcprm(2,s3,3,s2)); - - vz0 = vec_add(vz4, vz5); - vz1 = vec_sub(vz4, vz5); - vz2 = vec_add(vz6, vz7); - vz3 = vec_sub(vz6, vz7); - - vz4 = vec_perm(vz0, vz1, vcprm(0,1,s0,s1)); - vz5 = vec_perm(vz0, vz1, vcprm(2,3,s2,s3)); - - vz6 = vec_perm(vz2, vz3, vcprm(0,1,s0,s1)); - vz7 = vec_perm(vz2, vz3, vcprm(2,3,s2,s3)); - - vz0 = vec_ld(0, &(out[0])); - vz1 = vec_ld(byte_2complex, &(out[0])); - vz2 = vec_ld(byte_4complex, &(out[0])); - vz3 = vec_ld(byte_6complex, &(out[0])); - vz10 = vec_perm(vz2, vz3, vcprm(0,s0,1,s1)); - vz11 = vec_perm(vz2, vz3, vcprm(2,s2,3,s3)); - vz8 = vec_perm(vz0, vz1, vcprm(0,1,s2,s1)); - vz9 = vec_perm(vz0, vz1, vcprm(2,3,s0,s3)); - - vz2 = vec_add(vz10, vz11); - vz3 = vec_sub(vz10, vz11); - vz12 = vec_perm(vz3, vz3, vcprm(2,3,0,1)); - vz0 = vec_add(vz8, vz9); - vz1 = vec_sub(vz8, vz9); - - vz3 = vec_madd(vz3, vc1, vc0); - vz3 = vec_madd(vz12, vc2, vz3); - vz8 = vec_perm(vz0, vz1, vcprm(0,s0,1,s1)); - vz9 = vec_perm(vz0, vz1, vcprm(2,s3,3,s2)); - vz10 = vec_perm(vz2, vz3, vcprm(1,2,s3,s0)); - vz11 = vec_perm(vz2, vz3, vcprm(0,3,s2,s1)); - - vz0 = vec_add(vz8, vz9); - vz1 = vec_sub(vz8, vz9); - vz2 = vec_add(vz10, vz11); - vz3 = vec_sub(vz10, vz11); - - vz8 = vec_perm(vz0, vz1, vcprm(0,1,s0,s1)); - vz9 = vec_perm(vz0, vz1, vcprm(2,3,s2,s3)); - vz10 = vec_perm(vz2, vz3, vcprm(0,2,s1,s3)); - vz11 = vec_perm(vz2, vz3, vcprm(1,3,s0,s2)); - - vz2 = vec_sub(vz8, vz10); - vz3 = vec_sub(vz9, vz11); - vz0 = vec_add(vz8, vz10); - vz1 = vec_add(vz9, vz11); - - vz8 = vec_madd(vz4, vc3, vc0); - vz9 = vec_madd(vz5, vc3, vc0); - vz10 = vec_madd(vz6, vc3, vc0); - vz11 = vec_madd(vz7, vc3, vc0); - - vz8 = vec_madd(vz5, vc4, vz8); - vz9 = vec_madd(vz4, vc5, vz9); - vz10 = vec_madd(vz7, vc5, vz10); - vz11 = vec_madd(vz6, vc4, vz11); - - vz12 = vec_sub(vz10, vz8); - vz10 = vec_add(vz10, vz8); - - vz13 = vec_sub(vz9, vz11); - vz11 = vec_add(vz9, vz11); - - vz4 = vec_sub(vz0, vz10); - vz0 = vec_add(vz0, vz10); - - vz7= vec_sub(vz3, vz12); - vz3= vec_add(vz3, vz12); - - vz5 = vec_sub(vz1, vz11); - vz1 = vec_add(vz1, vz11); - - vz6 = vec_sub(vz2, vz13); - vz2 = vec_add(vz2, vz13); - - vec_st(vz0, 0, &(out[0])); - vec_st(vz1, byte_2complex, &(out[0])); - vec_st(vz2, byte_4complex, &(out[0])); - vec_st(vz3, byte_6complex, &(out[0])); - vec_st(vz4, byte_8complex, &(out[0])); - vec_st(vz5, byte_10complex, &(out[0])); - vec_st(vz6, byte_12complex, &(out[0])); - vec_st(vz7, byte_14complex, &(out[0])); - return; - -} -inline static void pass_vsx(FFTComplex * z, const FFTSample * wre, unsigned int n) -{ - int o1 = n<<1; - int o2 = n<<2; - int o3 = o1+o2; - int i1, i2, i3; - FFTSample* out = (FFTSample*)z; - const FFTSample *wim = wre+o1; - vec_f v0, v1, v2, v3; - vec_f v4, v5, v6, v7; - vec_f v8, v9, v10, v11; - vec_f v12, v13; - - n = n-2; - i1 = o1*sizeof(FFTComplex); - i2 = o2*sizeof(FFTComplex); - i3 = o3*sizeof(FFTComplex); - - v8 = vec_ld(0, &(wre[0])); - v10 = vec_ld(0, &(wim[0])); - v9 = vec_ld(0, &(wim[-4])); - v9 = vec_perm(v9, v10, vcprm(s0,3,2,1)); - - v4 = vec_ld(i2, &(out[0])); - v5 = vec_ld(i2+16, &(out[0])); - v6 = vec_ld(i3, &(out[0])); - v7 = vec_ld(i3+16, &(out[0])); - v10 = vec_mul(v4, v8); // r2*wre - v11 = vec_mul(v5, v8); // i2*wre - v12 = vec_mul(v6, v8); // r3*wre - v13 = vec_mul(v7, v8); // i3*wre - - v0 = vec_ld(0, &(out[0])); // r0 - v3 = vec_ld(i1+16, &(out[0])); // i1 - v10 = vec_madd(v5, v9, v10); // r2*wim - v11 = vec_nmsub(v4, v9, v11); // i2*wim - v12 = vec_nmsub(v7, v9, v12); // r3*wim - v13 = vec_madd(v6, v9, v13); // i3*wim - - v1 = vec_ld(16, &(out[0])); // i0 - v2 = vec_ld(i1, &(out[0])); // r1 - v8 = vec_sub(v12, v10); - v12 = vec_add(v12, v10); - v9 = vec_sub(v11, v13); - v13 = vec_add(v11, v13); - v4 = vec_sub(v0, v12); - v0 = vec_add(v0, v12); - v7 = vec_sub(v3, v8); - v3 = vec_add(v3, v8); - - vec_st(v0, 0, &(out[0])); // r0 - vec_st(v3, i1+16, &(out[0])); // i1 - vec_st(v4, i2, &(out[0])); // r2 - vec_st(v7, i3+16, &(out[0]));// i3 - - v5 = vec_sub(v1, v13); - v1 = vec_add(v1, v13); - v6 = vec_sub(v2, v9); - v2 = vec_add(v2, v9); - - vec_st(v1, 16, &(out[0])); // i0 - vec_st(v2, i1, &(out[0])); // r1 - vec_st(v5, i2+16, &(out[0])); // i2 - vec_st(v6, i3, &(out[0])); // r3 - - do { - out += 8; - wre += 4; - wim -= 4; - - v8 = vec_ld(0, &(wre[0])); - v10 = vec_ld(0, &(wim[0])); - v9 = vec_ld(0, &(wim[-4])); - v9 = vec_perm(v9, v10, vcprm(s0,3,2,1)); - - v4 = vec_ld(i2, &(out[0])); // r2 - v5 = vec_ld(i2+16, &(out[0])); // i2 - v6 = vec_ld(i3, &(out[0])); // r3 - v7 = vec_ld(i3+16, &(out[0]));// i3 - v10 = vec_mul(v4, v8); // r2*wre - v11 = vec_mul(v5, v8); // i2*wre - v12 = vec_mul(v6, v8); // r3*wre - v13 = vec_mul(v7, v8); // i3*wre - - v0 = vec_ld(0, &(out[0])); // r0 - v3 = vec_ld(i1+16, &(out[0])); // i1 - v10 = vec_madd(v5, v9, v10); // r2*wim - v11 = vec_nmsub(v4, v9, v11); // i2*wim - v12 = vec_nmsub(v7, v9, v12); // r3*wim - v13 = vec_madd(v6, v9, v13); // i3*wim - - v1 = vec_ld(16, &(out[0])); // i0 - v2 = vec_ld(i1, &(out[0])); // r1 - v8 = vec_sub(v12, v10); - v12 = vec_add(v12, v10); - v9 = vec_sub(v11, v13); - v13 = vec_add(v11, v13); - v4 = vec_sub(v0, v12); - v0 = vec_add(v0, v12); - v7 = vec_sub(v3, v8); - v3 = vec_add(v3, v8); - - vec_st(v0, 0, &(out[0])); // r0 - vec_st(v3, i1+16, &(out[0])); // i1 - vec_st(v4, i2, &(out[0])); // r2 - vec_st(v7, i3+16, &(out[0])); // i3 - - v5 = vec_sub(v1, v13); - v1 = vec_add(v1, v13); - v6 = vec_sub(v2, v9); - v2 = vec_add(v2, v9); - - vec_st(v1, 16, &(out[0])); // i0 - vec_st(v2, i1, &(out[0])); // r1 - vec_st(v5, i2+16, &(out[0])); // i2 - vec_st(v6, i3, &(out[0])); // r3 - } while (n-=2); -} - -#endif - -#endif /* AVCODEC_PPC_FFT_VSX_H */ diff --git a/libavcodec/ppc/h264dsp.c b/libavcodec/ppc/h264dsp.c index c02733dda2a..0650768d7ba 100644 --- a/libavcodec/ppc/h264dsp.c +++ b/libavcodec/ppc/h264dsp.c @@ -401,30 +401,29 @@ static inline void write16x4(uint8_t *dst, int dst_stride, register vec_u8 r0, register vec_u8 r1, register vec_u8 r2, register vec_u8 r3) { DECLARE_ALIGNED(16, unsigned char, result)[64]; - uint32_t *src_int = (uint32_t *)result, *dst_int = (uint32_t *)dst; - int int_dst_stride = dst_stride/4; + uint32_t *src_int = (uint32_t *)result; vec_st(r0, 0, result); vec_st(r1, 16, result); vec_st(r2, 32, result); vec_st(r3, 48, result); /* FIXME: there has to be a better way!!!! */ - *dst_int = *src_int; - *(dst_int+ int_dst_stride) = *(src_int + 1); - *(dst_int+ 2*int_dst_stride) = *(src_int + 2); - *(dst_int+ 3*int_dst_stride) = *(src_int + 3); - *(dst_int+ 4*int_dst_stride) = *(src_int + 4); - *(dst_int+ 5*int_dst_stride) = *(src_int + 5); - *(dst_int+ 6*int_dst_stride) = *(src_int + 6); - *(dst_int+ 7*int_dst_stride) = *(src_int + 7); - *(dst_int+ 8*int_dst_stride) = *(src_int + 8); - *(dst_int+ 9*int_dst_stride) = *(src_int + 9); - *(dst_int+10*int_dst_stride) = *(src_int + 10); - *(dst_int+11*int_dst_stride) = *(src_int + 11); - *(dst_int+12*int_dst_stride) = *(src_int + 12); - *(dst_int+13*int_dst_stride) = *(src_int + 13); - *(dst_int+14*int_dst_stride) = *(src_int + 14); - *(dst_int+15*int_dst_stride) = *(src_int + 15); + AV_WN32(dst, AV_RN32A(src_int + 0)); + AV_WN32(dst + dst_stride, AV_RN32A(src_int + 1)); + AV_WN32(dst + 2 * dst_stride, AV_RN32A(src_int + 2)); + AV_WN32(dst + 3 * dst_stride, AV_RN32A(src_int + 3)); + AV_WN32(dst + 4 * dst_stride, AV_RN32A(src_int + 4)); + AV_WN32(dst + 5 * dst_stride, AV_RN32A(src_int + 5)); + AV_WN32(dst + 6 * dst_stride, AV_RN32A(src_int + 6)); + AV_WN32(dst + 7 * dst_stride, AV_RN32A(src_int + 7)); + AV_WN32(dst + 8 * dst_stride, AV_RN32A(src_int + 8)); + AV_WN32(dst + 9 * dst_stride, AV_RN32A(src_int + 9)); + AV_WN32(dst + 10 * dst_stride, AV_RN32A(src_int + 10)); + AV_WN32(dst + 11 * dst_stride, AV_RN32A(src_int + 11)); + AV_WN32(dst + 12 * dst_stride, AV_RN32A(src_int + 12)); + AV_WN32(dst + 13 * dst_stride, AV_RN32A(src_int + 13)); + AV_WN32(dst + 14 * dst_stride, AV_RN32A(src_int + 14)); + AV_WN32(dst + 15 * dst_stride, AV_RN32A(src_int + 15)); } /** @brief performs a 6x16 transpose of data in src, and stores it to dst @@ -664,7 +663,7 @@ void weight_h264_W_altivec(uint8_t *block, int stride, int height, DECLARE_ALIGNED(16, int32_t, temp)[4]; LOAD_ZERO; - offset <<= log2_denom; + offset *= 1 << log2_denom; if(log2_denom) offset += 1<<(log2_denom-1); temp[0] = log2_denom; temp[1] = weight; @@ -713,7 +712,7 @@ void biweight_h264_W_altivec(uint8_t *dst, uint8_t *src, int stride, int height, DECLARE_ALIGNED(16, int32_t, temp)[4]; LOAD_ZERO; - offset = ((offset + 1) | 1) << log2_denom; + offset = ((offset + 1) | 1) * (1 << log2_denom); temp[0] = log2_denom+1; temp[1] = weights; temp[2] = weightd; diff --git a/libavcodec/ppc/idctdsp.c b/libavcodec/ppc/idctdsp.c index 29f625a01c7..a7acbc5ead3 100644 --- a/libavcodec/ppc/idctdsp.c +++ b/libavcodec/ppc/idctdsp.c @@ -40,6 +40,7 @@ #include "libavutil/ppc/cpu.h" #include "libavutil/ppc/util_altivec.h" +#include "libavcodec/avcodec.h" #include "libavcodec/idctdsp.h" #if HAVE_ALTIVEC diff --git a/libavcodec/ppc/mpegaudiodsp_altivec.c b/libavcodec/ppc/mpegaudiodsp_altivec.c index 6794ed09285..b065203c4e4 100644 --- a/libavcodec/ppc/mpegaudiodsp_altivec.c +++ b/libavcodec/ppc/mpegaudiodsp_altivec.c @@ -19,6 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include "config.h" #include "libavutil/attributes.h" #include "libavutil/cpu.h" diff --git a/libavcodec/ppc/vp8dsp_altivec.c b/libavcodec/ppc/vp8dsp_altivec.c index 12dac8b0a81..061914fc385 100644 --- a/libavcodec/ppc/vp8dsp_altivec.c +++ b/libavcodec/ppc/vp8dsp_altivec.c @@ -50,11 +50,12 @@ static const vec_s8 h_subpel_filters_inner[7] = // for 6tap filters, these are the outer two taps // The zeros mask off pixels 4-7 when filtering 0-3 // and vice-versa -static const vec_s8 h_subpel_filters_outer[3] = +static const vec_s8 h_subpel_filters_outer[4] = { REPT4(0, 0, 2, 1), REPT4(0, 0, 3, 3), REPT4(0, 0, 1, 2), + REPT4(0, 0, 0, 0), }; #define LOAD_H_SUBPEL_FILTER(i) \ diff --git a/libavcodec/profiles.c b/libavcodec/profiles.c index 7af7fbeb130..5bb8f150e65 100644 --- a/libavcodec/profiles.c +++ b/libavcodec/profiles.c @@ -24,161 +24,180 @@ #if !CONFIG_SMALL const AVProfile ff_aac_profiles[] = { - { FF_PROFILE_AAC_LOW, "LC" }, - { FF_PROFILE_AAC_HE, "HE-AAC" }, - { FF_PROFILE_AAC_HE_V2, "HE-AACv2" }, - { FF_PROFILE_AAC_LD, "LD" }, - { FF_PROFILE_AAC_ELD, "ELD" }, - { FF_PROFILE_AAC_MAIN, "Main" }, - { FF_PROFILE_AAC_SSR, "SSR" }, - { FF_PROFILE_AAC_LTP, "LTP" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_AAC_LOW, "LC" }, + { AV_PROFILE_AAC_HE, "HE-AAC" }, + { AV_PROFILE_AAC_HE_V2, "HE-AACv2" }, + { AV_PROFILE_AAC_LD, "LD" }, + { AV_PROFILE_AAC_ELD, "ELD" }, + { AV_PROFILE_AAC_MAIN, "Main" }, + { AV_PROFILE_AAC_SSR, "SSR" }, + { AV_PROFILE_AAC_LTP, "LTP" }, + { AV_PROFILE_UNKNOWN }, }; const AVProfile ff_dca_profiles[] = { - { FF_PROFILE_DTS, "DTS" }, - { FF_PROFILE_DTS_ES, "DTS-ES" }, - { FF_PROFILE_DTS_96_24, "DTS 96/24" }, - { FF_PROFILE_DTS_HD_HRA, "DTS-HD HRA" }, - { FF_PROFILE_DTS_HD_MA, "DTS-HD MA" }, - { FF_PROFILE_DTS_EXPRESS, "DTS Express" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_DTS, "DTS" }, + { AV_PROFILE_DTS_ES, "DTS-ES" }, + { AV_PROFILE_DTS_96_24, "DTS 96/24" }, + { AV_PROFILE_DTS_HD_HRA, "DTS-HD HRA" }, + { AV_PROFILE_DTS_HD_MA, "DTS-HD MA" }, + { AV_PROFILE_DTS_HD_MA_X, "DTS-HD MA + DTS:X" }, + { AV_PROFILE_DTS_HD_MA_X_IMAX, "DTS-HD MA + DTS:X IMAX" }, + { AV_PROFILE_DTS_EXPRESS, "DTS Express" }, + { AV_PROFILE_UNKNOWN }, +}; + +const AVProfile ff_eac3_profiles[] = { + { AV_PROFILE_EAC3_DDP_ATMOS, "Dolby Digital Plus + Dolby Atmos"}, + { AV_PROFILE_UNKNOWN }, +}; + +const AVProfile ff_truehd_profiles[] = { + { AV_PROFILE_TRUEHD_ATMOS, "Dolby TrueHD + Dolby Atmos"}, + { AV_PROFILE_UNKNOWN }, }; const AVProfile ff_dnxhd_profiles[] = { - { FF_PROFILE_DNXHD, "DNXHD"}, - { FF_PROFILE_DNXHR_LB, "DNXHR LB"}, - { FF_PROFILE_DNXHR_SQ, "DNXHR SQ"}, - { FF_PROFILE_DNXHR_HQ, "DNXHR HQ" }, - { FF_PROFILE_DNXHR_HQX, "DNXHR HQX"}, - { FF_PROFILE_DNXHR_444, "DNXHR 444"}, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_DNXHD, "DNXHD"}, + { AV_PROFILE_DNXHR_LB, "DNXHR LB"}, + { AV_PROFILE_DNXHR_SQ, "DNXHR SQ"}, + { AV_PROFILE_DNXHR_HQ, "DNXHR HQ" }, + { AV_PROFILE_DNXHR_HQX, "DNXHR HQX"}, + { AV_PROFILE_DNXHR_444, "DNXHR 444"}, + { AV_PROFILE_UNKNOWN }, }; const AVProfile ff_h264_profiles[] = { - { FF_PROFILE_H264_BASELINE, "Baseline" }, - { FF_PROFILE_H264_CONSTRAINED_BASELINE, "Constrained Baseline" }, - { FF_PROFILE_H264_MAIN, "Main" }, - { FF_PROFILE_H264_EXTENDED, "Extended" }, - { FF_PROFILE_H264_HIGH, "High" }, - { FF_PROFILE_H264_HIGH_10, "High 10" }, - { FF_PROFILE_H264_HIGH_10_INTRA, "High 10 Intra" }, - { FF_PROFILE_H264_HIGH_422, "High 4:2:2" }, - { FF_PROFILE_H264_HIGH_422_INTRA, "High 4:2:2 Intra" }, - { FF_PROFILE_H264_HIGH_444, "High 4:4:4" }, - { FF_PROFILE_H264_HIGH_444_PREDICTIVE, "High 4:4:4 Predictive" }, - { FF_PROFILE_H264_HIGH_444_INTRA, "High 4:4:4 Intra" }, - { FF_PROFILE_H264_CAVLC_444, "CAVLC 4:4:4" }, - { FF_PROFILE_H264_MULTIVIEW_HIGH, "Multiview High" }, - { FF_PROFILE_H264_STEREO_HIGH, "Stereo High" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_H264_BASELINE, "Baseline" }, + { AV_PROFILE_H264_CONSTRAINED_BASELINE, "Constrained Baseline" }, + { AV_PROFILE_H264_MAIN, "Main" }, + { AV_PROFILE_H264_EXTENDED, "Extended" }, + { AV_PROFILE_H264_HIGH, "High" }, + { AV_PROFILE_H264_HIGH_10, "High 10" }, + { AV_PROFILE_H264_HIGH_10_INTRA, "High 10 Intra" }, + { AV_PROFILE_H264_HIGH_422, "High 4:2:2" }, + { AV_PROFILE_H264_HIGH_422_INTRA, "High 4:2:2 Intra" }, + { AV_PROFILE_H264_HIGH_444, "High 4:4:4" }, + { AV_PROFILE_H264_HIGH_444_PREDICTIVE, "High 4:4:4 Predictive" }, + { AV_PROFILE_H264_HIGH_444_INTRA, "High 4:4:4 Intra" }, + { AV_PROFILE_H264_CAVLC_444, "CAVLC 4:4:4" }, + { AV_PROFILE_H264_MULTIVIEW_HIGH, "Multiview High" }, + { AV_PROFILE_H264_STEREO_HIGH, "Stereo High" }, + { AV_PROFILE_UNKNOWN }, }; const AVProfile ff_vvc_profiles[] = { - { FF_PROFILE_VVC_MAIN_10, "Main 10" }, - { FF_PROFILE_VVC_MAIN_10_444, "Main 10 4:4:4" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_VVC_MAIN_10, "Main 10" }, + { AV_PROFILE_VVC_MAIN_10_444, "Main 10 4:4:4" }, + { AV_PROFILE_UNKNOWN }, }; const AVProfile ff_hevc_profiles[] = { - { FF_PROFILE_HEVC_MAIN, "Main" }, - { FF_PROFILE_HEVC_MAIN_10, "Main 10" }, - { FF_PROFILE_HEVC_MAIN_STILL_PICTURE, "Main Still Picture" }, - { FF_PROFILE_HEVC_REXT, "Rext" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_HEVC_MAIN, "Main" }, + { AV_PROFILE_HEVC_MAIN_10, "Main 10" }, + { AV_PROFILE_HEVC_MAIN_STILL_PICTURE, "Main Still Picture" }, + { AV_PROFILE_HEVC_REXT, "Rext" }, + { AV_PROFILE_HEVC_SCC, "Scc" }, + { AV_PROFILE_UNKNOWN }, }; const AVProfile ff_jpeg2000_profiles[] = { - { FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_0, "JPEG 2000 codestream restriction 0" }, - { FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_1, "JPEG 2000 codestream restriction 1" }, - { FF_PROFILE_JPEG2000_CSTREAM_NO_RESTRICTION, "JPEG 2000 no codestream restrictions" }, - { FF_PROFILE_JPEG2000_DCINEMA_2K, "JPEG 2000 digital cinema 2K" }, - { FF_PROFILE_JPEG2000_DCINEMA_4K, "JPEG 2000 digital cinema 4K" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_JPEG2000_CSTREAM_RESTRICTION_0, "JPEG 2000 codestream restriction 0" }, + { AV_PROFILE_JPEG2000_CSTREAM_RESTRICTION_1, "JPEG 2000 codestream restriction 1" }, + { AV_PROFILE_JPEG2000_CSTREAM_NO_RESTRICTION, "JPEG 2000 no codestream restrictions" }, + { AV_PROFILE_JPEG2000_DCINEMA_2K, "JPEG 2000 digital cinema 2K" }, + { AV_PROFILE_JPEG2000_DCINEMA_4K, "JPEG 2000 digital cinema 4K" }, + { AV_PROFILE_UNKNOWN }, }; const AVProfile ff_mpeg2_video_profiles[] = { - { FF_PROFILE_MPEG2_422, "4:2:2" }, - { FF_PROFILE_MPEG2_HIGH, "High" }, - { FF_PROFILE_MPEG2_SS, "Spatially Scalable" }, - { FF_PROFILE_MPEG2_SNR_SCALABLE, "SNR Scalable" }, - { FF_PROFILE_MPEG2_MAIN, "Main" }, - { FF_PROFILE_MPEG2_SIMPLE, "Simple" }, - { FF_PROFILE_RESERVED, "Reserved" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_MPEG2_422, "4:2:2" }, + { AV_PROFILE_MPEG2_HIGH, "High" }, + { AV_PROFILE_MPEG2_SS, "Spatially Scalable" }, + { AV_PROFILE_MPEG2_SNR_SCALABLE, "SNR Scalable" }, + { AV_PROFILE_MPEG2_MAIN, "Main" }, + { AV_PROFILE_MPEG2_SIMPLE, "Simple" }, + { AV_PROFILE_RESERVED, "Reserved" }, + { AV_PROFILE_UNKNOWN }, }; const AVProfile ff_mpeg4_video_profiles[] = { - { FF_PROFILE_MPEG4_SIMPLE, "Simple Profile" }, - { FF_PROFILE_MPEG4_SIMPLE_SCALABLE, "Simple Scalable Profile" }, - { FF_PROFILE_MPEG4_CORE, "Core Profile" }, - { FF_PROFILE_MPEG4_MAIN, "Main Profile" }, - { FF_PROFILE_MPEG4_N_BIT, "N-bit Profile" }, - { FF_PROFILE_MPEG4_SCALABLE_TEXTURE, "Scalable Texture Profile" }, - { FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION, "Simple Face Animation Profile" }, - { FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE, "Basic Animated Texture Profile" }, - { FF_PROFILE_MPEG4_HYBRID, "Hybrid Profile" }, - { FF_PROFILE_MPEG4_ADVANCED_REAL_TIME, "Advanced Real Time Simple Profile" }, - { FF_PROFILE_MPEG4_CORE_SCALABLE, "Code Scalable Profile" }, - { FF_PROFILE_MPEG4_ADVANCED_CODING, "Advanced Coding Profile" }, - { FF_PROFILE_MPEG4_ADVANCED_CORE, "Advanced Core Profile" }, - { FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE, "Advanced Scalable Texture Profile" }, - { FF_PROFILE_MPEG4_SIMPLE_STUDIO, "Simple Studio Profile" }, - { FF_PROFILE_MPEG4_ADVANCED_SIMPLE, "Advanced Simple Profile" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_MPEG4_SIMPLE, "Simple Profile" }, + { AV_PROFILE_MPEG4_SIMPLE_SCALABLE, "Simple Scalable Profile" }, + { AV_PROFILE_MPEG4_CORE, "Core Profile" }, + { AV_PROFILE_MPEG4_MAIN, "Main Profile" }, + { AV_PROFILE_MPEG4_N_BIT, "N-bit Profile" }, + { AV_PROFILE_MPEG4_SCALABLE_TEXTURE, "Scalable Texture Profile" }, + { AV_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION, "Simple Face Animation Profile" }, + { AV_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE, "Basic Animated Texture Profile" }, + { AV_PROFILE_MPEG4_HYBRID, "Hybrid Profile" }, + { AV_PROFILE_MPEG4_ADVANCED_REAL_TIME, "Advanced Real Time Simple Profile" }, + { AV_PROFILE_MPEG4_CORE_SCALABLE, "Code Scalable Profile" }, + { AV_PROFILE_MPEG4_ADVANCED_CODING, "Advanced Coding Profile" }, + { AV_PROFILE_MPEG4_ADVANCED_CORE, "Advanced Core Profile" }, + { AV_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE, "Advanced Scalable Texture Profile" }, + { AV_PROFILE_MPEG4_SIMPLE_STUDIO, "Simple Studio Profile" }, + { AV_PROFILE_MPEG4_ADVANCED_SIMPLE, "Advanced Simple Profile" }, + { AV_PROFILE_UNKNOWN }, }; const AVProfile ff_vc1_profiles[] = { - { FF_PROFILE_VC1_SIMPLE, "Simple" }, - { FF_PROFILE_VC1_MAIN, "Main" }, - { FF_PROFILE_VC1_COMPLEX, "Complex" }, - { FF_PROFILE_VC1_ADVANCED, "Advanced" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_VC1_SIMPLE, "Simple" }, + { AV_PROFILE_VC1_MAIN, "Main" }, + { AV_PROFILE_VC1_COMPLEX, "Complex" }, + { AV_PROFILE_VC1_ADVANCED, "Advanced" }, + { AV_PROFILE_UNKNOWN }, }; const AVProfile ff_vp9_profiles[] = { - { FF_PROFILE_VP9_0, "Profile 0" }, - { FF_PROFILE_VP9_1, "Profile 1" }, - { FF_PROFILE_VP9_2, "Profile 2" }, - { FF_PROFILE_VP9_3, "Profile 3" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_VP9_0, "Profile 0" }, + { AV_PROFILE_VP9_1, "Profile 1" }, + { AV_PROFILE_VP9_2, "Profile 2" }, + { AV_PROFILE_VP9_3, "Profile 3" }, + { AV_PROFILE_UNKNOWN }, }; const AVProfile ff_av1_profiles[] = { - { FF_PROFILE_AV1_MAIN, "Main" }, - { FF_PROFILE_AV1_HIGH, "High" }, - { FF_PROFILE_AV1_PROFESSIONAL, "Professional" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_AV1_MAIN, "Main" }, + { AV_PROFILE_AV1_HIGH, "High" }, + { AV_PROFILE_AV1_PROFESSIONAL, "Professional" }, + { AV_PROFILE_UNKNOWN }, }; const AVProfile ff_sbc_profiles[] = { - { FF_PROFILE_SBC_MSBC, "mSBC" }, - { FF_PROFILE_UNKNOWN }, + { AV_PROFILE_SBC_MSBC, "mSBC" }, + { AV_PROFILE_UNKNOWN }, }; const AVProfile ff_prores_profiles[] = { - { FF_PROFILE_PRORES_PROXY, "Proxy" }, - { FF_PROFILE_PRORES_LT, "LT" }, - { FF_PROFILE_PRORES_STANDARD, "Standard" }, - { FF_PROFILE_PRORES_HQ, "HQ" }, - { FF_PROFILE_PRORES_4444, "4444" }, - { FF_PROFILE_PRORES_XQ, "XQ" }, - { FF_PROFILE_UNKNOWN } + { AV_PROFILE_PRORES_PROXY, "Proxy" }, + { AV_PROFILE_PRORES_LT, "LT" }, + { AV_PROFILE_PRORES_STANDARD, "Standard" }, + { AV_PROFILE_PRORES_HQ, "HQ" }, + { AV_PROFILE_PRORES_4444, "4444" }, + { AV_PROFILE_PRORES_XQ, "XQ" }, + { AV_PROFILE_UNKNOWN } }; const AVProfile ff_mjpeg_profiles[] = { - { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT, "Baseline" }, - { FF_PROFILE_MJPEG_HUFFMAN_EXTENDED_SEQUENTIAL_DCT, "Sequential" }, - { FF_PROFILE_MJPEG_HUFFMAN_PROGRESSIVE_DCT, "Progressive" }, - { FF_PROFILE_MJPEG_HUFFMAN_LOSSLESS, "Lossless" }, - { FF_PROFILE_MJPEG_JPEG_LS, "JPEG LS" }, - { FF_PROFILE_UNKNOWN } + { AV_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT, "Baseline" }, + { AV_PROFILE_MJPEG_HUFFMAN_EXTENDED_SEQUENTIAL_DCT, "Sequential" }, + { AV_PROFILE_MJPEG_HUFFMAN_PROGRESSIVE_DCT, "Progressive" }, + { AV_PROFILE_MJPEG_HUFFMAN_LOSSLESS, "Lossless" }, + { AV_PROFILE_MJPEG_JPEG_LS, "JPEG LS" }, + { AV_PROFILE_UNKNOWN } }; const AVProfile ff_arib_caption_profiles[] = { - { FF_PROFILE_ARIB_PROFILE_A, "Profile A" }, - { FF_PROFILE_ARIB_PROFILE_C, "Profile C" }, - { FF_PROFILE_UNKNOWN } + { AV_PROFILE_ARIB_PROFILE_A, "Profile A" }, + { AV_PROFILE_ARIB_PROFILE_C, "Profile C" }, + { AV_PROFILE_UNKNOWN } +}; + +const AVProfile ff_evc_profiles[] = { + { AV_PROFILE_EVC_BASELINE, "Baseline" }, + { AV_PROFILE_EVC_MAIN, "Main" }, + { AV_PROFILE_UNKNOWN }, }; #endif /* !CONFIG_SMALL */ diff --git a/libavcodec/profiles.h b/libavcodec/profiles.h index 41a19aa9add..ffc8710ef22 100644 --- a/libavcodec/profiles.h +++ b/libavcodec/profiles.h @@ -23,41 +23,43 @@ #include "libavutil/opt.h" #define FF_AVCTX_PROFILE_OPTION(name, description, type, value) \ - {name, description, 0, AV_OPT_TYPE_CONST, {.i64 = value }, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_## type ##_PARAM, "avctx.profile"}, + {name, description, 0, AV_OPT_TYPE_CONST, {.i64 = value }, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_## type ##_PARAM, .unit = "avctx.profile"}, #define FF_AAC_PROFILE_OPTS \ - FF_AVCTX_PROFILE_OPTION("aac_main", NULL, AUDIO, FF_PROFILE_AAC_MAIN)\ - FF_AVCTX_PROFILE_OPTION("aac_low", NULL, AUDIO, FF_PROFILE_AAC_LOW)\ - FF_AVCTX_PROFILE_OPTION("aac_ssr", NULL, AUDIO, FF_PROFILE_AAC_SSR)\ - FF_AVCTX_PROFILE_OPTION("aac_ltp", NULL, AUDIO, FF_PROFILE_AAC_LTP)\ - FF_AVCTX_PROFILE_OPTION("aac_he", NULL, AUDIO, FF_PROFILE_AAC_HE)\ - FF_AVCTX_PROFILE_OPTION("aac_he_v2", NULL, AUDIO, FF_PROFILE_AAC_HE_V2)\ - FF_AVCTX_PROFILE_OPTION("aac_ld", NULL, AUDIO, FF_PROFILE_AAC_LD)\ - FF_AVCTX_PROFILE_OPTION("aac_eld", NULL, AUDIO, FF_PROFILE_AAC_ELD)\ - FF_AVCTX_PROFILE_OPTION("mpeg2_aac_low", NULL, AUDIO, FF_PROFILE_MPEG2_AAC_LOW)\ - FF_AVCTX_PROFILE_OPTION("mpeg2_aac_he", NULL, AUDIO, FF_PROFILE_MPEG2_AAC_HE)\ + FF_AVCTX_PROFILE_OPTION("aac_main", NULL, AUDIO, AV_PROFILE_AAC_MAIN)\ + FF_AVCTX_PROFILE_OPTION("aac_low", NULL, AUDIO, AV_PROFILE_AAC_LOW)\ + FF_AVCTX_PROFILE_OPTION("aac_ssr", NULL, AUDIO, AV_PROFILE_AAC_SSR)\ + FF_AVCTX_PROFILE_OPTION("aac_ltp", NULL, AUDIO, AV_PROFILE_AAC_LTP)\ + FF_AVCTX_PROFILE_OPTION("aac_he", NULL, AUDIO, AV_PROFILE_AAC_HE)\ + FF_AVCTX_PROFILE_OPTION("aac_he_v2", NULL, AUDIO, AV_PROFILE_AAC_HE_V2)\ + FF_AVCTX_PROFILE_OPTION("aac_ld", NULL, AUDIO, AV_PROFILE_AAC_LD)\ + FF_AVCTX_PROFILE_OPTION("aac_eld", NULL, AUDIO, AV_PROFILE_AAC_ELD)\ + FF_AVCTX_PROFILE_OPTION("mpeg2_aac_low", NULL, AUDIO, AV_PROFILE_MPEG2_AAC_LOW)\ + FF_AVCTX_PROFILE_OPTION("mpeg2_aac_he", NULL, AUDIO, AV_PROFILE_MPEG2_AAC_HE)\ #define FF_MPEG4_PROFILE_OPTS \ - FF_AVCTX_PROFILE_OPTION("mpeg4_sp", NULL, VIDEO, FF_PROFILE_MPEG4_SIMPLE)\ - FF_AVCTX_PROFILE_OPTION("mpeg4_core", NULL, VIDEO, FF_PROFILE_MPEG4_CORE)\ - FF_AVCTX_PROFILE_OPTION("mpeg4_main", NULL, VIDEO, FF_PROFILE_MPEG4_MAIN)\ - FF_AVCTX_PROFILE_OPTION("mpeg4_asp", NULL, VIDEO, FF_PROFILE_MPEG4_ADVANCED_SIMPLE)\ + FF_AVCTX_PROFILE_OPTION("mpeg4_sp", NULL, VIDEO, AV_PROFILE_MPEG4_SIMPLE)\ + FF_AVCTX_PROFILE_OPTION("mpeg4_core", NULL, VIDEO, AV_PROFILE_MPEG4_CORE)\ + FF_AVCTX_PROFILE_OPTION("mpeg4_main", NULL, VIDEO, AV_PROFILE_MPEG4_MAIN)\ + FF_AVCTX_PROFILE_OPTION("mpeg4_asp", NULL, VIDEO, AV_PROFILE_MPEG4_ADVANCED_SIMPLE)\ #define FF_MPEG2_PROFILE_OPTS \ - FF_AVCTX_PROFILE_OPTION("422", NULL, VIDEO, FF_PROFILE_MPEG2_422)\ - FF_AVCTX_PROFILE_OPTION("high", NULL, VIDEO, FF_PROFILE_MPEG2_HIGH)\ - FF_AVCTX_PROFILE_OPTION("ss", NULL, VIDEO, FF_PROFILE_MPEG2_SS)\ - FF_AVCTX_PROFILE_OPTION("snr", NULL, VIDEO, FF_PROFILE_MPEG2_SNR_SCALABLE)\ - FF_AVCTX_PROFILE_OPTION("main", NULL, VIDEO, FF_PROFILE_MPEG2_MAIN)\ - FF_AVCTX_PROFILE_OPTION("simple", NULL, VIDEO, FF_PROFILE_MPEG2_SIMPLE)\ + FF_AVCTX_PROFILE_OPTION("422", NULL, VIDEO, AV_PROFILE_MPEG2_422)\ + FF_AVCTX_PROFILE_OPTION("high", NULL, VIDEO, AV_PROFILE_MPEG2_HIGH)\ + FF_AVCTX_PROFILE_OPTION("ss", NULL, VIDEO, AV_PROFILE_MPEG2_SS)\ + FF_AVCTX_PROFILE_OPTION("snr", NULL, VIDEO, AV_PROFILE_MPEG2_SNR_SCALABLE)\ + FF_AVCTX_PROFILE_OPTION("main", NULL, VIDEO, AV_PROFILE_MPEG2_MAIN)\ + FF_AVCTX_PROFILE_OPTION("simple", NULL, VIDEO, AV_PROFILE_MPEG2_SIMPLE)\ #define FF_AV1_PROFILE_OPTS \ - FF_AVCTX_PROFILE_OPTION("main", NULL, VIDEO, FF_PROFILE_AV1_MAIN)\ - FF_AVCTX_PROFILE_OPTION("high", NULL, VIDEO, FF_PROFILE_AV1_HIGH)\ - FF_AVCTX_PROFILE_OPTION("professional", NULL, VIDEO, FF_PROFILE_AV1_PROFESSIONAL)\ + FF_AVCTX_PROFILE_OPTION("main", NULL, VIDEO, AV_PROFILE_AV1_MAIN)\ + FF_AVCTX_PROFILE_OPTION("high", NULL, VIDEO, AV_PROFILE_AV1_HIGH)\ + FF_AVCTX_PROFILE_OPTION("professional", NULL, VIDEO, AV_PROFILE_AV1_PROFESSIONAL)\ extern const AVProfile ff_aac_profiles[]; extern const AVProfile ff_dca_profiles[]; +extern const AVProfile ff_eac3_profiles[]; +extern const AVProfile ff_truehd_profiles[]; extern const AVProfile ff_dnxhd_profiles[]; extern const AVProfile ff_h264_profiles[]; extern const AVProfile ff_hevc_profiles[]; @@ -72,5 +74,6 @@ extern const AVProfile ff_sbc_profiles[]; extern const AVProfile ff_prores_profiles[]; extern const AVProfile ff_mjpeg_profiles[]; extern const AVProfile ff_arib_caption_profiles[]; +extern const AVProfile ff_evc_profiles[]; #endif /* AVCODEC_PROFILES_H */ diff --git a/libavcodec/proresdata.c b/libavcodec/proresdata.c index 4787907c712..00af97d4f17 100644 --- a/libavcodec/proresdata.c +++ b/libavcodec/proresdata.c @@ -43,3 +43,11 @@ const uint8_t ff_prores_interlaced_scan[64] = { 30, 23, 31, 38, 45, 52, 60, 53, 46, 39, 47, 54, 61, 62, 55, 63 }; + +const uint8_t ff_prores_dc_codebook[7] = { 0x04, 0x28, 0x28, 0x4D, 0x4D, 0x70, 0x70 }; + +const uint8_t ff_prores_run_to_cb[16] = { 0x06, 0x06, 0x05, 0x05, 0x04, 0x29, + 0x29, 0x29, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x4C }; + +const uint8_t ff_prores_level_to_cb[10] = { 0x04, 0x0A, 0x05, 0x06, 0x04, 0x28, + 0x28, 0x28, 0x28, 0x4C }; diff --git a/libavcodec/proresdata.h b/libavcodec/proresdata.h index d8c8786689b..d859e93e88b 100644 --- a/libavcodec/proresdata.h +++ b/libavcodec/proresdata.h @@ -29,6 +29,9 @@ extern const uint8_t ff_prores_progressive_scan[64]; extern const uint8_t ff_prores_interlaced_scan[64]; +extern const uint8_t ff_prores_dc_codebook[7]; +extern const uint8_t ff_prores_run_to_cb[16]; +extern const uint8_t ff_prores_level_to_cb[10]; #define FIRST_DC_CB 0xB8 // rice_order = 5, exp_golomb_order = 6, switch_bits = 0 diff --git a/libavcodec/proresdec.h b/libavcodec/proresdec.h index 1e48752e6f5..230fca41f2f 100644 --- a/libavcodec/proresdec.h +++ b/libavcodec/proresdec.h @@ -22,10 +22,15 @@ #ifndef AVCODEC_PRORESDEC_H #define AVCODEC_PRORESDEC_H +#include + #include "get_bits.h" #include "blockdsp.h" #include "proresdsp.h" +#include "libavutil/frame.h" +#include "libavutil/pixfmt.h" + typedef struct { const uint8_t *data; unsigned mb_x; diff --git a/libavcodec/proresdec2.c b/libavcodec/proresdec2.c index c821a078491..faf6dfc9766 100644 --- a/libavcodec/proresdec2.c +++ b/libavcodec/proresdec2.c @@ -37,6 +37,7 @@ #include "codec_internal.h" #include "decode.h" #include "get_bits.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "idctdsp.h" #include "profiles.h" @@ -44,13 +45,6 @@ #include "proresdata.h" #include "thread.h" -static void permute(uint8_t *dst, const uint8_t *src, const uint8_t permutation[64]) -{ - int i; - for (i = 0; i < 64; i++) - dst[i] = permutation[src[i]]; -} - #define ALPHA_SHIFT_16_TO_10(alpha_val) (alpha_val >> 6) #define ALPHA_SHIFT_8_TO_10(alpha_val) ((alpha_val << 2) | (alpha_val >> 6)) #define ALPHA_SHIFT_16_TO_12(alpha_val) (alpha_val >> 4) @@ -147,27 +141,27 @@ static av_cold int decode_init(AVCodecContext *avctx) switch (avctx->codec_tag) { case MKTAG('a','p','c','o'): - avctx->profile = FF_PROFILE_PRORES_PROXY; + avctx->profile = AV_PROFILE_PRORES_PROXY; break; case MKTAG('a','p','c','s'): - avctx->profile = FF_PROFILE_PRORES_LT; + avctx->profile = AV_PROFILE_PRORES_LT; break; case MKTAG('a','p','c','n'): - avctx->profile = FF_PROFILE_PRORES_STANDARD; + avctx->profile = AV_PROFILE_PRORES_STANDARD; break; case MKTAG('a','p','c','h'): - avctx->profile = FF_PROFILE_PRORES_HQ; + avctx->profile = AV_PROFILE_PRORES_HQ; break; case MKTAG('a','p','4','h'): - avctx->profile = FF_PROFILE_PRORES_4444; + avctx->profile = AV_PROFILE_PRORES_4444; avctx->bits_per_raw_sample = 12; break; case MKTAG('a','p','4','x'): - avctx->profile = FF_PROFILE_PRORES_XQ; + avctx->profile = AV_PROFILE_PRORES_XQ; avctx->bits_per_raw_sample = 12; break; default: - avctx->profile = FF_PROFILE_UNKNOWN; + avctx->profile = AV_PROFILE_UNKNOWN; av_log(avctx, AV_LOG_WARNING, "Unknown prores profile %d\n", avctx->codec_tag); } @@ -178,7 +172,7 @@ static av_cold int decode_init(AVCodecContext *avctx) } ff_blockdsp_init(&ctx->bdsp); - ret = ff_proresdsp_init(&ctx->prodsp, avctx); + ret = ff_proresdsp_init(&ctx->prodsp, avctx->bits_per_raw_sample); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "Fail to init proresdsp for bits per raw sample %d\n", avctx->bits_per_raw_sample); return ret; @@ -187,8 +181,8 @@ static av_cold int decode_init(AVCodecContext *avctx) ff_init_scantable_permutation(idct_permutation, ctx->prodsp.idct_permutation_type); - permute(ctx->progressive_scan, ff_prores_progressive_scan, idct_permutation); - permute(ctx->interlaced_scan, ff_prores_interlaced_scan, idct_permutation); + ff_permute_scantable(ctx->progressive_scan, ff_prores_progressive_scan, idct_permutation); + ff_permute_scantable(ctx->interlaced_scan, ff_prores_interlaced_scan, idct_permutation); ctx->pix_fmt = AV_PIX_FMT_NONE; @@ -252,8 +246,9 @@ static int decode_frame_header(ProresContext *ctx, const uint8_t *buf, ctx->scan = ctx->progressive_scan; // permuted } else { ctx->scan = ctx->interlaced_scan; // permuted - ctx->frame->interlaced_frame = 1; - ctx->frame->top_field_first = ctx->frame_type == 1; + ctx->frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (ctx->frame_type == 1) + ctx->frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } if (ctx->alpha_info) { @@ -283,7 +278,7 @@ static int decode_frame_header(ProresContext *ctx, const uint8_t *buf, *fmtp++ = ctx->pix_fmt; *fmtp = AV_PIX_FMT_NONE; - if ((ret = ff_thread_get_format(avctx, pix_fmts)) < 0) + if ((ret = ff_get_format(avctx, pix_fmts)) < 0) return ret; avctx->pix_fmt = ret; @@ -303,7 +298,7 @@ static int decode_frame_header(ProresContext *ctx, const uint8_t *buf, av_log(avctx, AV_LOG_ERROR, "Header truncated\n"); return AVERROR_INVALIDDATA; } - permute(ctx->qmat_luma, ctx->prodsp.idct_permutation, ptr); + ff_permute_scantable(ctx->qmat_luma, ctx->prodsp.idct_permutation, ptr); ptr += 64; } else { memset(ctx->qmat_luma, 4, 64); @@ -314,7 +309,7 @@ static int decode_frame_header(ProresContext *ctx, const uint8_t *buf, av_log(avctx, AV_LOG_ERROR, "Header truncated\n"); return AVERROR_INVALIDDATA; } - permute(ctx->qmat_chroma, ctx->prodsp.idct_permutation, ptr); + ff_permute_scantable(ctx->qmat_chroma, ctx->prodsp.idct_permutation, ptr); } else { memcpy(ctx->qmat_chroma, ctx->qmat_luma, 64); } @@ -515,7 +510,7 @@ static av_always_inline int decode_ac_coeffs(AVCodecContext *avctx, GetBitContex for (pos = block_mask;;) { bits_left = gb->size_in_bits - re_index; - if (!bits_left || (bits_left < 32 && !SHOW_UBITS(re, gb, bits_left))) + if (bits_left <= 0 || (bits_left < 32 && !SHOW_UBITS(re, gb, bits_left))) break; DECODE_CODEWORD(run, run_to_cb[FFMIN(run, 15)], LAST_SKIP_BITS); @@ -706,7 +701,7 @@ static int decode_slice_thread(AVCodecContext *avctx, void *arg, int jobnr, int dest_u = pic->data[1] + (slice->mb_y << 4) * chroma_stride + (slice->mb_x << mb_x_shift); dest_v = pic->data[2] + (slice->mb_y << 4) * chroma_stride + (slice->mb_x << mb_x_shift); - if (ctx->frame_type && ctx->first_field ^ ctx->frame->top_field_first) { + if (ctx->frame_type && ctx->first_field ^ !!(ctx->frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)) { dest_y += pic->linesize[0]; dest_u += pic->linesize[1]; dest_v += pic->linesize[2]; @@ -792,7 +787,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, ctx->frame = frame; ctx->frame->pict_type = AV_PICTURE_TYPE_I; - ctx->frame->key_frame = 1; + ctx->frame->flags |= AV_FRAME_FLAG_KEY; ctx->first_field = 1; buf += 8; @@ -810,13 +805,14 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, ff_thread_finish_setup(avctx); if (avctx->hwaccel) { - ret = avctx->hwaccel->start_frame(avctx, NULL, 0); + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); + ret = hwaccel->start_frame(avctx, NULL, 0); if (ret < 0) return ret; - ret = avctx->hwaccel->decode_slice(avctx, avpkt->data, avpkt->size); + ret = hwaccel->decode_slice(avctx, avpkt->data, avpkt->size); if (ret < 0) return ret; - ret = avctx->hwaccel->end_frame(avctx); + ret = hwaccel->end_frame(avctx); if (ret < 0) return ret; goto finish; diff --git a/libavcodec/proresdsp.c b/libavcodec/proresdsp.c index 6ed01179fee..bc253e55f7d 100644 --- a/libavcodec/proresdsp.c +++ b/libavcodec/proresdsp.c @@ -76,12 +76,12 @@ static void prores_idct_put_12_c(uint16_t *out, ptrdiff_t linesize, int16_t *blo put_pixels_12(out, linesize >> 1, block); } -av_cold int ff_proresdsp_init(ProresDSPContext *dsp, AVCodecContext *avctx) +av_cold int ff_proresdsp_init(ProresDSPContext *dsp, int bits_per_raw_sample) { - if (avctx->bits_per_raw_sample == 10) { + if (bits_per_raw_sample == 10) { dsp->idct_put = prores_idct_put_10_c; dsp->idct_permutation_type = FF_IDCT_PERM_NONE; - } else if (avctx->bits_per_raw_sample == 12) { + } else if (bits_per_raw_sample == 12) { dsp->idct_put = prores_idct_put_12_c; dsp->idct_permutation_type = FF_IDCT_PERM_NONE; } else { @@ -89,7 +89,7 @@ av_cold int ff_proresdsp_init(ProresDSPContext *dsp, AVCodecContext *avctx) } #if ARCH_X86 - ff_proresdsp_init_x86(dsp, avctx); + ff_proresdsp_init_x86(dsp, bits_per_raw_sample); #endif ff_init_scantable_permutation(dsp->idct_permutation, diff --git a/libavcodec/proresdsp.h b/libavcodec/proresdsp.h index 37ba76b8e49..966ba3d797a 100644 --- a/libavcodec/proresdsp.h +++ b/libavcodec/proresdsp.h @@ -25,7 +25,6 @@ #include #include -#include "avcodec.h" typedef struct ProresDSPContext { int idct_permutation_type; @@ -33,8 +32,8 @@ typedef struct ProresDSPContext { void (*idct_put)(uint16_t *out, ptrdiff_t linesize, int16_t *block, const int16_t *qmat); } ProresDSPContext; -int ff_proresdsp_init(ProresDSPContext *dsp, AVCodecContext *avctx); +int ff_proresdsp_init(ProresDSPContext *dsp, int bits_per_raw_sample); -void ff_proresdsp_init_x86(ProresDSPContext *dsp, AVCodecContext *avctx); +void ff_proresdsp_init_x86(ProresDSPContext *dsp, int bits_per_raw_sample); #endif /* AVCODEC_PRORESDSP_H */ diff --git a/libavcodec/proresenc_anatoliy.c b/libavcodec/proresenc_anatoliy.c index 09196c7aa11..b1a173e953b 100644 --- a/libavcodec/proresenc_anatoliy.c +++ b/libavcodec/proresenc_anatoliy.c @@ -32,7 +32,6 @@ #include "avcodec.h" #include "codec_internal.h" #include "encode.h" -#include "internal.h" #include "profiles.h" #include "proresdata.h" #include "put_bits.h" @@ -42,13 +41,13 @@ #define DEFAULT_SLICE_MB_WIDTH 8 static const AVProfile profiles[] = { - { FF_PROFILE_PRORES_PROXY, "apco"}, - { FF_PROFILE_PRORES_LT, "apcs"}, - { FF_PROFILE_PRORES_STANDARD, "apcn"}, - { FF_PROFILE_PRORES_HQ, "apch"}, - { FF_PROFILE_PRORES_4444, "ap4h"}, - { FF_PROFILE_PRORES_XQ, "ap4x"}, - { FF_PROFILE_UNKNOWN } + { AV_PROFILE_PRORES_PROXY, "apco"}, + { AV_PROFILE_PRORES_LT, "apcs"}, + { AV_PROFILE_PRORES_STANDARD, "apcn"}, + { AV_PROFILE_PRORES_HQ, "apch"}, + { AV_PROFILE_PRORES_4444, "ap4h"}, + { AV_PROFILE_PRORES_XQ, "ap4x"}, + { AV_PROFILE_UNKNOWN } }; static const int qp_start_table[] = { 8, 3, 2, 1, 1, 1}; @@ -198,107 +197,117 @@ typedef struct { char *vendor; } ProresContext; -static void encode_codeword(PutBitContext *pb, int val, int codebook) +/** + * Check if a value is in the list. If not, return the default value + * + * @param ctx Context for the log msg + * @param val_name Name of the checked value, for log msg + * @param array_valid_values Array of valid int, ended with INT_MAX + * @param default_value Value return if checked value is not in the array + * @return Value or default_value. + */ +static int int_from_list_or_default(void *ctx, const char *val_name, int val, + const int *array_valid_values, int default_value) { - unsigned int rice_order, exp_order, switch_bits, first_exp, exp, zeros; - - /* number of bits to switch between rice and exp golomb */ - switch_bits = codebook & 3; - rice_order = codebook >> 5; - exp_order = (codebook >> 2) & 7; - - first_exp = ((switch_bits + 1) << rice_order); - - if (val >= first_exp) { /* exp golomb */ - val -= first_exp; - val += (1 << exp_order); - exp = av_log2(val); - zeros = exp - exp_order + switch_bits + 1; - put_bits(pb, zeros, 0); - put_bits(pb, exp + 1, val); - } else if (rice_order) { - put_bits(pb, (val >> rice_order), 0); - put_bits(pb, 1, 1); - put_sbits(pb, rice_order, val); - } else { - put_bits(pb, val, 0); - put_bits(pb, 1, 1); - } + int i = 0; + + while (1) { + int ref_val = array_valid_values[i]; + if (ref_val == INT_MAX) + break; + if (val == ref_val) + return val; + i++; + } + /* val is not a valid value */ + av_log(ctx, AV_LOG_DEBUG, + "%s %d are not supported. Set to default value : %d\n", + val_name, val, default_value); + return default_value; } -#define QSCALE(qmat,ind,val) ((val) / ((qmat)[ind])) -#define TO_GOLOMB(val) (((val) * 2) ^ ((val) >> 31)) -#define DIFF_SIGN(val, sign) (((val) >> 31) ^ (sign)) -#define IS_NEGATIVE(val) ((((val) >> 31) ^ -1) + 1) -#define TO_GOLOMB2(val,sign) ((val)==0 ? 0 : ((val) << 1) + (sign)) - -static av_always_inline int get_level(int val) +static void encode_vlc_codeword(PutBitContext *pb, unsigned codebook, int val) { - int sign = (val >> 31); - return (val ^ sign) - sign; -} + unsigned int rice_order, exp_order, switch_bits, switch_val; + int exponent; -#define FIRST_DC_CB 0xB8 + /* number of prefix bits to switch between Rice and expGolomb */ + switch_bits = (codebook & 3) + 1; + rice_order = codebook >> 5; /* rice code order */ + exp_order = (codebook >> 2) & 7; /* exp golomb code order */ -static const uint8_t dc_codebook[7] = { 0x04, 0x28, 0x28, 0x4D, 0x4D, 0x70, 0x70}; + switch_val = switch_bits << rice_order; -static void encode_dc_coeffs(PutBitContext *pb, int16_t *in, - int blocks_per_slice, int *qmat) -{ - int prev_dc, code; - int i, sign, idx; - int new_dc, delta, diff_sign, new_code; + if (val >= switch_val) { + val -= switch_val - (1 << exp_order); + exponent = av_log2(val); - prev_dc = QSCALE(qmat, 0, in[0] - 16384); - code = TO_GOLOMB(prev_dc); - encode_codeword(pb, code, FIRST_DC_CB); + put_bits(pb, exponent - exp_order + switch_bits, 0); + put_bits(pb, exponent + 1, val); + } else { + exponent = val >> rice_order; - code = 5; sign = 0; idx = 64; - for (i = 1; i < blocks_per_slice; i++, idx += 64) { - new_dc = QSCALE(qmat, 0, in[idx] - 16384); - delta = new_dc - prev_dc; - diff_sign = DIFF_SIGN(delta, sign); - new_code = TO_GOLOMB2(get_level(delta), diff_sign); + if (exponent) + put_bits(pb, exponent, 0); + put_bits(pb, 1, 1); + if (rice_order) + put_sbits(pb, rice_order, val); + } +} - encode_codeword(pb, new_code, dc_codebook[FFMIN(code, 6)]); +#define GET_SIGN(x) ((x) >> 31) +#define MAKE_CODE(x) (((x) * 2) ^ GET_SIGN(x)) - code = new_code; - sign = delta >> 31; - prev_dc = new_dc; +static void encode_dcs(PutBitContext *pb, int16_t *blocks, + int blocks_per_slice, int scale) +{ + int i; + int codebook = 5, code, dc, prev_dc, delta, sign, new_sign; + + prev_dc = (blocks[0] - 0x4000) / scale; + encode_vlc_codeword(pb, FIRST_DC_CB, MAKE_CODE(prev_dc)); + sign = 0; + blocks += 64; + + for (i = 1; i < blocks_per_slice; i++, blocks += 64) { + dc = (blocks[0] - 0x4000) / scale; + delta = dc - prev_dc; + new_sign = GET_SIGN(delta); + delta = (delta ^ sign) - sign; + code = MAKE_CODE(delta); + encode_vlc_codeword(pb, ff_prores_dc_codebook[codebook], code); + codebook = FFMIN(code, 6); + sign = new_sign; + prev_dc = dc; } } -static const uint8_t run_to_cb[16] = { 0x06, 0x06, 0x05, 0x05, 0x04, 0x29, - 0x29, 0x29, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x4C }; -static const uint8_t lev_to_cb[10] = { 0x04, 0x0A, 0x05, 0x06, 0x04, 0x28, - 0x28, 0x28, 0x28, 0x4C }; - -static void encode_ac_coeffs(PutBitContext *pb, - int16_t *in, int blocks_per_slice, int *qmat, const uint8_t ff_prores_scan[64]) +static void encode_acs(PutBitContext *pb, int16_t *blocks, + int blocks_per_slice, + int *qmat, const uint8_t *scan) { + int idx, i; int prev_run = 4; int prev_level = 2; + int run = 0, level; + int max_coeffs, abs_level; - int run = 0, level, code, i, j; - for (i = 1; i < 64; i++) { - int indp = ff_prores_scan[i]; - for (j = 0; j < blocks_per_slice; j++) { - int val = QSCALE(qmat, indp, in[(j << 6) + indp]); - if (val) { - encode_codeword(pb, run, run_to_cb[FFMIN(prev_run, 15)]); + max_coeffs = blocks_per_slice << 6; - prev_run = run; + for (i = 1; i < 64; i++) { + for (idx = scan[i]; idx < max_coeffs; idx += 64) { + level = blocks[idx] / qmat[scan[i]]; + if (level) { + abs_level = FFABS(level); + encode_vlc_codeword(pb, ff_prores_run_to_cb[prev_run], run); + encode_vlc_codeword(pb, ff_prores_level_to_cb[prev_level], abs_level - 1); + put_sbits(pb, 1, GET_SIGN(level)); + + prev_run = FFMIN(run, 15); + prev_level = FFMIN(abs_level, 9); run = 0; - level = get_level(val); - code = level - 1; - - encode_codeword(pb, code, lev_to_cb[FFMIN(prev_level, 9)]); - - prev_level = level; - - put_bits(pb, 1, IS_NEGATIVE(val)); } else { - ++run; + run++; } } } @@ -360,7 +369,7 @@ static void calc_plane_dct(FDCTDSPContext *fdsp, const uint8_t *src, int16_t * b } static int encode_slice_plane(int16_t *blocks, int mb_count, uint8_t *buf, unsigned buf_size, int *qmat, int sub_sample_chroma, - const uint8_t ff_prores_scan[64]) + const uint8_t *scan) { int blocks_per_slice; PutBitContext pb; @@ -368,8 +377,8 @@ static int encode_slice_plane(int16_t *blocks, int mb_count, uint8_t *buf, unsig blocks_per_slice = mb_count << (2 - sub_sample_chroma); init_put_bits(&pb, buf, buf_size); - encode_dc_coeffs(&pb, blocks, blocks_per_slice, qmat); - encode_ac_coeffs(&pb, blocks, blocks_per_slice, qmat, ff_prores_scan); + encode_dcs(&pb, blocks, blocks_per_slice, qmat[0]); + encode_acs(&pb, blocks, blocks_per_slice, qmat, scan); flush_put_bits(&pb); return put_bits_ptr(&pb) - pb.buf; @@ -458,8 +467,7 @@ static av_always_inline int encode_alpha_slice_data(AVCodecContext *avctx, int8_ run++; } } while (idx < num_coeffs); - if (run) - put_alpha_run(&pb, run); + put_alpha_run(&pb, run); flush_put_bits(&pb); *a_data_size = put_bytes_output(&pb); @@ -725,28 +733,29 @@ static int prores_encode_frame(AVCodecContext *avctx, AVPacket *pkt, uint8_t *buf; int compress_frame_size, pic_size, ret, is_top_field_first = 0; uint8_t frame_flags; - int frame_size = FFALIGN(avctx->width, 16) * FFALIGN(avctx->height, 16)*16 + 500 + AV_INPUT_BUFFER_MIN_SIZE; //FIXME choose tighter limit + int frame_size = FFALIGN(avctx->width, 16) * FFALIGN(avctx->height, 16)*16 + 500 + FF_INPUT_BUFFER_MIN_SIZE; //FIXME choose tighter limit - if ((ret = ff_alloc_packet(avctx, pkt, frame_size + AV_INPUT_BUFFER_MIN_SIZE)) < 0) + if ((ret = ff_alloc_packet(avctx, pkt, frame_size + FF_INPUT_BUFFER_MIN_SIZE)) < 0) return ret; buf = pkt->data; compress_frame_size = 8 + header_size; bytestream_put_be32(&buf, compress_frame_size);/* frame size will be update after picture(s) encoding */ - bytestream_put_buffer(&buf, "icpf", 4); + bytestream_put_be32(&buf, FRAME_ID); bytestream_put_be16(&buf, header_size); - bytestream_put_be16(&buf, 0); /* version */ + bytestream_put_be16(&buf, avctx->pix_fmt != AV_PIX_FMT_YUV422P10 || ctx->need_alpha ? 1 : 0); /* version */ bytestream_put_buffer(&buf, ctx->vendor, 4); bytestream_put_be16(&buf, avctx->width); bytestream_put_be16(&buf, avctx->height); - frame_flags = 0x82; /* 422 not interlaced */ - if (avctx->profile >= FF_PROFILE_PRORES_4444) /* 4444 or 4444 Xq */ + frame_flags = 0x80; /* 422 not interlaced */ + if (avctx->profile >= AV_PROFILE_PRORES_4444) /* 4444 or 4444 Xq */ frame_flags |= 0x40; /* 444 chroma */ if (ctx->is_interlaced) { - if (pict->top_field_first || !pict->interlaced_frame) { /* tff frame or progressive frame interpret as tff */ + if ((pict->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) || !(pict->flags & AV_FRAME_FLAG_INTERLACED)) { + /* tff frame or progressive frame interpret as tff */ av_log(avctx, AV_LOG_DEBUG, "use interlaced encoding, top field first\n"); frame_flags |= 0x04; /* interlaced tff */ is_top_field_first = 1; @@ -760,18 +769,13 @@ static int prores_encode_frame(AVCodecContext *avctx, AVPacket *pkt, *buf++ = frame_flags; *buf++ = 0; /* reserved */ /* only write color properties, if valid value. set to unspecified otherwise */ - *buf++ = ff_int_from_list_or_default(avctx, "frame color primaries", pict->color_primaries, valid_primaries, 0); - *buf++ = ff_int_from_list_or_default(avctx, "frame color trc", pict->color_trc, valid_trc, 0); - *buf++ = ff_int_from_list_or_default(avctx, "frame colorspace", pict->colorspace, valid_colorspace, 0); - if (avctx->profile >= FF_PROFILE_PRORES_4444) { - if (avctx->pix_fmt == AV_PIX_FMT_YUV444P10) { - *buf++ = 0xA0;/* src b64a and no alpha */ - } else { - *buf++ = 0xA2;/* src b64a and 16b alpha */ - } - } else { - *buf++ = 32;/* src v210 and no alpha */ - } + *buf++ = int_from_list_or_default(avctx, "frame color primaries", + pict->color_primaries, valid_primaries, 0); + *buf++ = int_from_list_or_default(avctx, "frame color trc", + pict->color_trc, valid_trc, 0); + *buf++ = int_from_list_or_default(avctx, "frame colorspace", + pict->colorspace, valid_colorspace, 0); + *buf++ = ctx->need_alpha ? 0x2 /* 16-bit alpha */ : 0; *buf++ = 0; /* reserved */ *buf++ = 3; /* luma and chroma matrix present */ @@ -839,40 +843,41 @@ static av_cold int prores_encode_init(AVCodecContext *avctx) return AVERROR(EINVAL); } - if (avctx->profile == FF_PROFILE_UNKNOWN) { + if (avctx->profile == AV_PROFILE_UNKNOWN) { if (avctx->pix_fmt == AV_PIX_FMT_YUV422P10) { - avctx->profile = FF_PROFILE_PRORES_STANDARD; + avctx->profile = AV_PROFILE_PRORES_STANDARD; av_log(avctx, AV_LOG_INFO, "encoding with ProRes standard (apcn) profile\n"); } else if (avctx->pix_fmt == AV_PIX_FMT_YUV444P10) { - avctx->profile = FF_PROFILE_PRORES_4444; + avctx->profile = AV_PROFILE_PRORES_4444; av_log(avctx, AV_LOG_INFO, "encoding with ProRes 4444 (ap4h) profile\n"); } else if (avctx->pix_fmt == AV_PIX_FMT_YUVA444P10) { - avctx->profile = FF_PROFILE_PRORES_4444; + avctx->profile = AV_PROFILE_PRORES_4444; av_log(avctx, AV_LOG_INFO, "encoding with ProRes 4444+ (ap4h) profile\n"); - } - } else if (avctx->profile < FF_PROFILE_PRORES_PROXY - || avctx->profile > FF_PROFILE_PRORES_XQ) { + } else + av_assert0(0); + } else if (avctx->profile < AV_PROFILE_PRORES_PROXY + || avctx->profile > AV_PROFILE_PRORES_XQ) { av_log( avctx, AV_LOG_ERROR, "unknown profile %d, use [0 - apco, 1 - apcs, 2 - apcn (default), 3 - apch, 4 - ap4h, 5 - ap4x]\n", avctx->profile); return AVERROR(EINVAL); - } else if ((avctx->pix_fmt == AV_PIX_FMT_YUV422P10) && (avctx->profile > FF_PROFILE_PRORES_HQ)){ + } else if ((avctx->pix_fmt == AV_PIX_FMT_YUV422P10) && (avctx->profile > AV_PROFILE_PRORES_HQ)){ av_log(avctx, AV_LOG_ERROR, "encoding with ProRes 444/Xq (ap4h/ap4x) profile, need YUV444P10 input\n"); return AVERROR(EINVAL); } else if ((avctx->pix_fmt == AV_PIX_FMT_YUV444P10 || avctx->pix_fmt == AV_PIX_FMT_YUVA444P10) - && (avctx->profile < FF_PROFILE_PRORES_4444)){ + && (avctx->profile < AV_PROFILE_PRORES_4444)){ av_log(avctx, AV_LOG_ERROR, "encoding with ProRes Proxy/LT/422/422 HQ (apco, apcs, apcn, ap4h) profile, need YUV422P10 input\n"); return AVERROR(EINVAL); } - if (avctx->profile < FF_PROFILE_PRORES_4444) { /* 422 versions */ + if (avctx->profile < AV_PROFILE_PRORES_4444) { /* 422 versions */ ctx->is_422 = 1; if ((avctx->height & 0xf) || (avctx->width & 0xf)) { ctx->fill_y = av_malloc(4 * (DEFAULT_SLICE_MB_WIDTH << 8)); @@ -898,6 +903,9 @@ static av_cold int prores_encode_init(AVCodecContext *avctx) } } + if (ctx->need_alpha) + avctx->bits_per_coded_sample = 32; + ff_fdctdsp_init(&ctx->fdsp, avctx); avctx->codec_tag = AV_RL32((const uint8_t*)profiles[avctx->profile].name); diff --git a/libavcodec/proresenc_kostya.c b/libavcodec/proresenc_kostya.c index 1940e0378a0..9d9f705ce4b 100644 --- a/libavcodec/proresenc_kostya.c +++ b/libavcodec/proresenc_kostya.c @@ -1,11 +1,9 @@ /* * Apple ProRes encoder * + * Copyright (c) 2011 Anatoliy Wasserman * Copyright (c) 2012 Konstantin Shishkov * - * This encoder appears to be based on Anatoliy Wassermans considering - * similarities in the bugs. - * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -135,32 +133,6 @@ static const uint8_t prores_quant_matrices[][64] = { }, }; -static const uint8_t prores_dc_codebook[4] = { - 0x04, // rice_order = 0, exp_golomb_order = 1, switch_bits = 0 - 0x28, // rice_order = 1, exp_golomb_order = 2, switch_bits = 0 - 0x4D, // rice_order = 2, exp_golomb_order = 3, switch_bits = 1 - 0x70 // rice_order = 3, exp_golomb_order = 4, switch_bits = 0 -}; - -static const uint8_t prores_ac_codebook[7] = { - 0x04, // rice_order = 0, exp_golomb_order = 1, switch_bits = 0 - 0x28, // rice_order = 1, exp_golomb_order = 2, switch_bits = 0 - 0x4C, // rice_order = 2, exp_golomb_order = 3, switch_bits = 0 - 0x05, // rice_order = 0, exp_golomb_order = 1, switch_bits = 1 - 0x29, // rice_order = 1, exp_golomb_order = 2, switch_bits = 1 - 0x06, // rice_order = 0, exp_golomb_order = 1, switch_bits = 2 - 0x0A, // rice_order = 0, exp_golomb_order = 2, switch_bits = 2 -}; - -/** - * Lookup tables for adaptive switching between codebooks - * according with previous run/level value. - */ -static const uint8_t prores_run_to_cb_index[16] = - { 5, 5, 3, 3, 0, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 2 }; - -static const uint8_t prores_lev_to_cb_index[10] = { 0, 6, 3, 5, 0, 1, 1, 1, 1, 2 }; - #define NUM_MB_LIMITS 4 static const int prores_mb_limits[NUM_MB_LIMITS] = { 1620, // up to 720x576 @@ -370,7 +342,7 @@ static void get_slice_data(ProresContext *ctx, const uint16_t *src, static void get_alpha_data(ProresContext *ctx, const uint16_t *src, ptrdiff_t linesize, int x, int y, int w, int h, - int16_t *blocks, int mbs_per_slice, int abits) + uint16_t *blocks, int mbs_per_slice, int abits) { const int slice_width = 16 * mbs_per_slice; int i, j, copy_w, copy_h; @@ -429,18 +401,17 @@ static inline void encode_vlc_codeword(PutBitContext *pb, unsigned codebook, int } #define GET_SIGN(x) ((x) >> 31) -#define MAKE_CODE(x) ((((x)) * 2) ^ GET_SIGN(x)) +#define MAKE_CODE(x) (((x) * 2) ^ GET_SIGN(x)) static void encode_dcs(PutBitContext *pb, int16_t *blocks, int blocks_per_slice, int scale) { int i; - int codebook = 3, code, dc, prev_dc, delta, sign, new_sign; + int codebook = 5, code, dc, prev_dc, delta, sign, new_sign; prev_dc = (blocks[0] - 0x4000) / scale; encode_vlc_codeword(pb, FIRST_DC_CB, MAKE_CODE(prev_dc)); sign = 0; - codebook = 3; blocks += 64; for (i = 1; i < blocks_per_slice; i++, blocks += 64) { @@ -449,9 +420,8 @@ static void encode_dcs(PutBitContext *pb, int16_t *blocks, new_sign = GET_SIGN(delta); delta = (delta ^ sign) - sign; code = MAKE_CODE(delta); - encode_vlc_codeword(pb, prores_dc_codebook[codebook], code); - codebook = (code + (code & 1)) >> 1; - codebook = FFMIN(codebook, 3); + encode_vlc_codeword(pb, ff_prores_dc_codebook[codebook], code); + codebook = FFMIN(code, 6); sign = new_sign; prev_dc = dc; } @@ -459,31 +429,28 @@ static void encode_dcs(PutBitContext *pb, int16_t *blocks, static void encode_acs(PutBitContext *pb, int16_t *blocks, int blocks_per_slice, - int plane_size_factor, const uint8_t *scan, const int16_t *qmat) { int idx, i; - int run, level, run_cb, lev_cb; + int prev_run = 4; + int prev_level = 2; + int run = 0, level; int max_coeffs, abs_level; max_coeffs = blocks_per_slice << 6; - run_cb = prores_run_to_cb_index[4]; - lev_cb = prores_lev_to_cb_index[2]; - run = 0; for (i = 1; i < 64; i++) { for (idx = scan[i]; idx < max_coeffs; idx += 64) { level = blocks[idx] / qmat[scan[i]]; if (level) { abs_level = FFABS(level); - encode_vlc_codeword(pb, prores_ac_codebook[run_cb], run); - encode_vlc_codeword(pb, prores_ac_codebook[lev_cb], - abs_level - 1); + encode_vlc_codeword(pb, ff_prores_run_to_cb[prev_run], run); + encode_vlc_codeword(pb, ff_prores_level_to_cb[prev_level], abs_level - 1); put_sbits(pb, 1, GET_SIGN(level)); - run_cb = prores_run_to_cb_index[FFMIN(run, 15)]; - lev_cb = prores_lev_to_cb_index[FFMIN(abs_level, 9)]; - run = 0; + prev_run = FFMIN(run, 15); + prev_level = FFMIN(abs_level, 9); + run = 0; } else { run++; } @@ -494,14 +461,13 @@ static void encode_acs(PutBitContext *pb, int16_t *blocks, static void encode_slice_plane(ProresContext *ctx, PutBitContext *pb, const uint16_t *src, ptrdiff_t linesize, int mbs_per_slice, int16_t *blocks, - int blocks_per_mb, int plane_size_factor, + int blocks_per_mb, const int16_t *qmat) { int blocks_per_slice = mbs_per_slice * blocks_per_mb; encode_dcs(pb, blocks, blocks_per_slice, qmat[0]); - encode_acs(pb, blocks, blocks_per_slice, plane_size_factor, - ctx->scantable, qmat); + encode_acs(pb, blocks, blocks_per_slice, ctx->scantable, qmat); } static void put_alpha_diff(PutBitContext *pb, int cur, int prev, int abits) @@ -562,8 +528,7 @@ static void encode_alpha_plane(ProresContext *ctx, PutBitContext *pb, run++; } } while (idx < num_coeffs); - if (run) - put_alpha_run(pb, run); + put_alpha_run(pb, run); } static int encode_slice(AVCodecContext *avctx, const AVFrame *pic, @@ -575,17 +540,16 @@ static int encode_slice(AVCodecContext *avctx, const AVFrame *pic, int i, xp, yp; int total_size = 0; const uint16_t *src; - int slice_width_factor = av_log2(mbs_per_slice); int num_cblocks, pwidth, line_add; ptrdiff_t linesize; - int plane_factor, is_chroma; + int is_chroma; uint16_t *qmat; uint16_t *qmat_chroma; if (ctx->pictures_per_frame == 1) line_add = 0; else - line_add = ctx->cur_picture_idx ^ !pic->top_field_first; + line_add = ctx->cur_picture_idx ^ !(pic->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); if (ctx->force_quant) { qmat = ctx->quants[0]; @@ -604,9 +568,6 @@ static int encode_slice(AVCodecContext *avctx, const AVFrame *pic, for (i = 0; i < ctx->num_planes; i++) { is_chroma = (i == 1 || i == 2); - plane_factor = slice_width_factor + 2; - if (is_chroma) - plane_factor += ctx->chroma_factor - 3; if (!is_chroma || ctx->chroma_factor == CFACTOR_Y444) { xp = x << 4; yp = y << 4; @@ -631,11 +592,11 @@ static int encode_slice(AVCodecContext *avctx, const AVFrame *pic, if (!is_chroma) {/* luma quant */ encode_slice_plane(ctx, pb, src, linesize, mbs_per_slice, ctx->blocks[0], - num_cblocks, plane_factor, qmat); + num_cblocks, qmat); } else { /* chroma plane */ encode_slice_plane(ctx, pb, src, linesize, mbs_per_slice, ctx->blocks[0], - num_cblocks, plane_factor, qmat_chroma); + num_cblocks, qmat_chroma); } } else { get_alpha_data(ctx, src, linesize, xp, yp, @@ -676,13 +637,12 @@ static int estimate_dcs(int *error, int16_t *blocks, int blocks_per_slice, int scale) { int i; - int codebook = 3, code, dc, prev_dc, delta, sign, new_sign; + int codebook = 5, code, dc, prev_dc, delta, sign, new_sign; int bits; prev_dc = (blocks[0] - 0x4000) / scale; bits = estimate_vlc(FIRST_DC_CB, MAKE_CODE(prev_dc)); sign = 0; - codebook = 3; blocks += 64; *error += FFABS(blocks[0] - 0x4000) % scale; @@ -693,9 +653,8 @@ static int estimate_dcs(int *error, int16_t *blocks, int blocks_per_slice, new_sign = GET_SIGN(delta); delta = (delta ^ sign) - sign; code = MAKE_CODE(delta); - bits += estimate_vlc(prores_dc_codebook[codebook], code); - codebook = (code + (code & 1)) >> 1; - codebook = FFMIN(codebook, 3); + bits += estimate_vlc(ff_prores_dc_codebook[codebook], code); + codebook = FFMIN(code, 6); sign = new_sign; prev_dc = dc; } @@ -704,17 +663,16 @@ static int estimate_dcs(int *error, int16_t *blocks, int blocks_per_slice, } static int estimate_acs(int *error, int16_t *blocks, int blocks_per_slice, - int plane_size_factor, const uint8_t *scan, const int16_t *qmat) { int idx, i; - int run, level, run_cb, lev_cb; + int prev_run = 4; + int prev_level = 2; + int run, level; int max_coeffs, abs_level; int bits = 0; max_coeffs = blocks_per_slice << 6; - run_cb = prores_run_to_cb_index[4]; - lev_cb = prores_lev_to_cb_index[2]; run = 0; for (i = 1; i < 64; i++) { @@ -723,12 +681,12 @@ static int estimate_acs(int *error, int16_t *blocks, int blocks_per_slice, *error += FFABS(blocks[idx]) % qmat[scan[i]]; if (level) { abs_level = FFABS(level); - bits += estimate_vlc(prores_ac_codebook[run_cb], run); - bits += estimate_vlc(prores_ac_codebook[lev_cb], + bits += estimate_vlc(ff_prores_run_to_cb[prev_run], run); + bits += estimate_vlc(ff_prores_level_to_cb[prev_level], abs_level - 1) + 1; - run_cb = prores_run_to_cb_index[FFMIN(run, 15)]; - lev_cb = prores_lev_to_cb_index[FFMIN(abs_level, 9)]; + prev_run = FFMIN(run, 15); + prev_level = FFMIN(abs_level, 9); run = 0; } else { run++; @@ -742,7 +700,7 @@ static int estimate_acs(int *error, int16_t *blocks, int blocks_per_slice, static int estimate_slice_plane(ProresContext *ctx, int *error, int plane, const uint16_t *src, ptrdiff_t linesize, int mbs_per_slice, - int blocks_per_mb, int plane_size_factor, + int blocks_per_mb, const int16_t *qmat, ProresThreadData *td) { int blocks_per_slice; @@ -751,8 +709,7 @@ static int estimate_slice_plane(ProresContext *ctx, int *error, int plane, blocks_per_slice = mbs_per_slice * blocks_per_mb; bits = estimate_dcs(error, td->blocks[plane], blocks_per_slice, qmat[0]); - bits += estimate_acs(error, td->blocks[plane], blocks_per_slice, - plane_size_factor, ctx->scantable, qmat); + bits += estimate_acs(error, td->blocks[plane], blocks_per_slice, ctx->scantable, qmat); return FFALIGN(bits, 8); } @@ -821,9 +778,8 @@ static int find_slice_quant(AVCodecContext *avctx, ProresContext *ctx = avctx->priv_data; int i, q, pq, xp, yp; const uint16_t *src; - int slice_width_factor = av_log2(mbs_per_slice); int num_cblocks[MAX_PLANES], pwidth; - int plane_factor[MAX_PLANES], is_chroma[MAX_PLANES]; + int is_chroma[MAX_PLANES]; const int min_quant = ctx->profile_info->min_quant; const int max_quant = ctx->profile_info->max_quant; int error, bits, bits_limit; @@ -838,14 +794,11 @@ static int find_slice_quant(AVCodecContext *avctx, if (ctx->pictures_per_frame == 1) line_add = 0; else - line_add = ctx->cur_picture_idx ^ !ctx->pic->top_field_first; + line_add = ctx->cur_picture_idx ^ !(ctx->pic->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); mbs = x + mbs_per_slice; for (i = 0; i < ctx->num_planes; i++) { is_chroma[i] = (i == 1 || i == 2); - plane_factor[i] = slice_width_factor + 2; - if (is_chroma[i]) - plane_factor[i] += ctx->chroma_factor - 3; if (!is_chroma[i] || ctx->chroma_factor == CFACTOR_Y444) { xp = x << 4; yp = y << 4; @@ -889,13 +842,13 @@ static int find_slice_quant(AVCodecContext *avctx, bits += estimate_slice_plane(ctx, &error, 0, src, linesize[0], mbs_per_slice, - num_cblocks[0], plane_factor[0], + num_cblocks[0], ctx->quants[q], td); /* estimate luma plane */ for (i = 1; i < ctx->num_planes - !!ctx->alpha_bits; i++) { /* estimate chroma plane */ bits += estimate_slice_plane(ctx, &error, i, src, linesize[i], mbs_per_slice, - num_cblocks[i], plane_factor[i], + num_cblocks[i], ctx->quants_chroma[q], td); } if (bits > 65000 * 8) @@ -926,13 +879,13 @@ static int find_slice_quant(AVCodecContext *avctx, bits += estimate_slice_plane(ctx, &error, 0, src, linesize[0], mbs_per_slice, - num_cblocks[0], plane_factor[0], + num_cblocks[0], qmat, td);/* estimate luma plane */ for (i = 1; i < ctx->num_planes - !!ctx->alpha_bits; i++) { /* estimate chroma plane */ bits += estimate_slice_plane(ctx, &error, i, src, linesize[i], mbs_per_slice, - num_cblocks[i], plane_factor[i], + num_cblocks[i], qmat_chroma, td); } if (bits <= ctx->bits_per_mb * mbs_per_slice) @@ -1025,7 +978,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, ctx->pic = pic; pkt_size = ctx->frame_size_upper_bound; - if ((ret = ff_alloc_packet(avctx, pkt, pkt_size + AV_INPUT_BUFFER_MIN_SIZE)) < 0) + if ((ret = ff_alloc_packet(avctx, pkt, pkt_size + FF_INPUT_BUFFER_MIN_SIZE)) < 0) return ret; orig_buf = pkt->data; @@ -1038,30 +991,26 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, // frame header tmp = buf; buf += 2; // frame header size will be stored here - bytestream_put_be16 (&buf, 0); // version 1 + bytestream_put_be16 (&buf, ctx->chroma_factor != CFACTOR_Y422 || ctx->alpha_bits ? 1 : 0); bytestream_put_buffer(&buf, ctx->vendor, 4); bytestream_put_be16 (&buf, avctx->width); bytestream_put_be16 (&buf, avctx->height); frame_flags = ctx->chroma_factor << 6; if (avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT) - frame_flags |= pic->top_field_first ? 0x04 : 0x08; + frame_flags |= (pic->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 0x04 : 0x08; bytestream_put_byte (&buf, frame_flags); bytestream_put_byte (&buf, 0); // reserved bytestream_put_byte (&buf, pic->color_primaries); bytestream_put_byte (&buf, pic->color_trc); bytestream_put_byte (&buf, pic->colorspace); - bytestream_put_byte (&buf, 0x40 | (ctx->alpha_bits >> 3)); + bytestream_put_byte (&buf, ctx->alpha_bits >> 3); bytestream_put_byte (&buf, 0); // reserved if (ctx->quant_sel != QUANT_MAT_DEFAULT) { bytestream_put_byte (&buf, 0x03); // matrix flags - both matrices are present - // luma quantisation matrix - for (i = 0; i < 64; i++) - bytestream_put_byte(&buf, ctx->quant_mat[i]); - // chroma quantisation matrix - for (i = 0; i < 64; i++) - bytestream_put_byte(&buf, ctx->quant_mat[i]); + bytestream_put_buffer(&buf, ctx->quant_mat, 64); // luma quantisation matrix + bytestream_put_buffer(&buf, ctx->quant_chroma_mat, 64); // chroma quantisation matrix } else { bytestream_put_byte (&buf, 0x00); // matrix flags - default matrices are used } @@ -1355,6 +1304,7 @@ static av_cold int encode_init(AVCodecContext *avctx) } avctx->codec_tag = ctx->profile_info->tag; + avctx->profile = ctx->profile; av_log(avctx, AV_LOG_DEBUG, "profile %d, %d slices, interlacing: %s, %d bits per MB\n", @@ -1374,39 +1324,39 @@ static const AVOption options[] = { AV_OPT_TYPE_INT, { .i64 = 8 }, 1, MAX_MBS_PER_SLICE, VE }, { "profile", NULL, OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = PRORES_PROFILE_AUTO }, - PRORES_PROFILE_AUTO, PRORES_PROFILE_4444XQ, VE, "profile" }, + PRORES_PROFILE_AUTO, PRORES_PROFILE_4444XQ, VE, .unit = "profile" }, { "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_AUTO }, - 0, 0, VE, "profile" }, + 0, 0, VE, .unit = "profile" }, { "proxy", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_PROXY }, - 0, 0, VE, "profile" }, + 0, 0, VE, .unit = "profile" }, { "lt", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_LT }, - 0, 0, VE, "profile" }, + 0, 0, VE, .unit = "profile" }, { "standard", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_STANDARD }, - 0, 0, VE, "profile" }, + 0, 0, VE, .unit = "profile" }, { "hq", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_HQ }, - 0, 0, VE, "profile" }, + 0, 0, VE, .unit = "profile" }, { "4444", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_4444 }, - 0, 0, VE, "profile" }, + 0, 0, VE, .unit = "profile" }, { "4444xq", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_4444XQ }, - 0, 0, VE, "profile" }, + 0, 0, VE, .unit = "profile" }, { "vendor", "vendor ID", OFFSET(vendor), AV_OPT_TYPE_STRING, { .str = "Lavc" }, 0, 0, VE }, { "bits_per_mb", "desired bits per macroblock", OFFSET(bits_per_mb), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 8192, VE }, { "quant_mat", "quantiser matrix", OFFSET(quant_sel), AV_OPT_TYPE_INT, - { .i64 = -1 }, -1, QUANT_MAT_DEFAULT, VE, "quant_mat" }, + { .i64 = -1 }, -1, QUANT_MAT_DEFAULT, VE, .unit = "quant_mat" }, { "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, - 0, 0, VE, "quant_mat" }, + 0, 0, VE, .unit = "quant_mat" }, { "proxy", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QUANT_MAT_PROXY }, - 0, 0, VE, "quant_mat" }, + 0, 0, VE, .unit = "quant_mat" }, { "lt", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QUANT_MAT_LT }, - 0, 0, VE, "quant_mat" }, + 0, 0, VE, .unit = "quant_mat" }, { "standard", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QUANT_MAT_STANDARD }, - 0, 0, VE, "quant_mat" }, + 0, 0, VE, .unit = "quant_mat" }, { "hq", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QUANT_MAT_HQ }, - 0, 0, VE, "quant_mat" }, + 0, 0, VE, .unit = "quant_mat" }, { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QUANT_MAT_DEFAULT }, - 0, 0, VE, "quant_mat" }, + 0, 0, VE, .unit = "quant_mat" }, { "alpha_bits", "bits for alpha plane", OFFSET(alpha_bits), AV_OPT_TYPE_INT, { .i64 = 16 }, 0, 16, VE }, { NULL } diff --git a/libavcodec/prosumer.c b/libavcodec/prosumer.c index e199d1aaa9e..a1ed6a9e538 100644 --- a/libavcodec/prosumer.c +++ b/libavcodec/prosumer.c @@ -195,7 +195,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/psd.c b/libavcodec/psd.c index ee96bd12376..d3456e6b3dc 100644 --- a/libavcodec/psd.c +++ b/libavcodec/psd.c @@ -532,7 +532,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, } if (s->color_mode == PSD_INDEXED) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS picture->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(picture->data[1], s->palette, AVPALETTE_SIZE); } diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c index d9d5afaa82d..fd356bd1908 100644 --- a/libavcodec/pthread_frame.c +++ b/libavcodec/pthread_frame.c @@ -28,11 +28,14 @@ #include #include "avcodec.h" +#include "avcodec_internal.h" #include "codec_internal.h" #include "decode.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "internal.h" #include "pthread_internal.h" +#include "refstruct.h" #include "thread.h" #include "threadframe.h" #include "version_major.h" @@ -49,21 +52,11 @@ #include "libavutil/thread.h" enum { - ///< Set when the thread is awaiting a packet. + /// Set when the thread is awaiting a packet. STATE_INPUT_READY, - ///< Set before the codec has called ff_thread_finish_setup(). + /// Set before the codec has called ff_thread_finish_setup(). STATE_SETTING_UP, - /** - * Set when the codec calls get_buffer(). - * State is returned to STATE_SETTING_UP afterwards. - */ - STATE_GET_BUFFER, - /** - * Set when the codec calls get_format(). - * State is returned to STATE_SETTING_UP afterwards. - */ - STATE_GET_FORMAT, - ///< Set after the codec has called ff_thread_finish_setup(). + /// Set after the codec has called ff_thread_finish_setup(). STATE_SETUP_FINISHED, }; @@ -73,6 +66,10 @@ enum { INITIALIZED, ///< Thread has been properly set up }; +typedef struct ThreadFrameProgress { + atomic_int progress[2]; +} ThreadFrameProgress; + /** * Context used by codec threads and stored in their AVCodecInternal thread_ctx. */ @@ -104,6 +101,12 @@ typedef struct PerThreadContext { int hwaccel_serializing; int async_serializing; + // set to 1 in ff_thread_finish_setup() when a threadsafe hwaccel is used; + // cannot check hwaccel caps directly, because + // worked threads clear hwaccel state for thread-unsafe hwaccels + // after each decode call + int hwaccel_threadsafe; + atomic_int debug_threads; ///< Set if the FF_DEBUG_THREADS option is set. } PerThreadContext; @@ -117,8 +120,8 @@ typedef struct FrameThreadContext { unsigned pthread_init_cnt; ///< Number of successfully initialized mutexes/conditions pthread_mutex_t buffer_mutex; ///< Mutex used to protect get/release_buffer(). /** - * This lock is used for ensuring threads run in serial when hwaccel - * is used. + * This lock is used for ensuring threads run in serial when thread-unsafe + * hwaccel is used. */ pthread_mutex_t hwaccel_mutex; pthread_mutex_t async_mutex; @@ -133,13 +136,19 @@ typedef struct FrameThreadContext { * While it is set, ff_thread_en/decode_frame won't return any results. */ - /* hwaccel state is temporarily stored here in order to transfer its ownership - * to the next decoding thread without the need for extra synchronization */ + /* hwaccel state for thread-unsafe hwaccels is temporarily stored here in + * order to transfer its ownership to the next decoding thread without the + * need for extra synchronization */ const AVHWAccel *stash_hwaccel; void *stash_hwaccel_context; void *stash_hwaccel_priv; } FrameThreadContext; +static int hwaccel_serial(const AVCodecContext *avctx) +{ + return avctx->hwaccel && !(ffhwaccel(avctx->hwaccel)->caps_internal & HWACCEL_CAP_THREAD_SAFE); +} + static void async_lock(FrameThreadContext *fctx) { pthread_mutex_lock(&fctx->async_mutex); @@ -202,9 +211,9 @@ static attribute_align_arg void *frame_worker_thread(void *arg) * cannot be true here. */ av_assert0(!p->hwaccel_serializing); - /* if the previous thread uses hwaccel then we take the lock to ensure - * the threads don't run concurrently */ - if (avctx->hwaccel) { + /* if the previous thread uses thread-unsafe hwaccel then we take the + * lock to ensure the threads don't run concurrently */ + if (hwaccel_serial(avctx)) { pthread_mutex_lock(&p->parent->hwaccel_mutex); p->hwaccel_serializing = 1; } @@ -214,13 +223,14 @@ static attribute_align_arg void *frame_worker_thread(void *arg) p->result = codec->cb.decode(avctx, p->frame, &p->got_frame, p->avpkt); if ((p->result < 0 || !p->got_frame) && p->frame->buf[0]) - ff_thread_release_buffer(avctx, p->frame); + av_frame_unref(p->frame); if (atomic_load(&p->state) == STATE_SETTING_UP) ff_thread_finish_setup(avctx); if (p->hwaccel_serializing) { - /* wipe hwaccel state to avoid stale pointers lying around; + /* wipe hwaccel state for thread-unsafe hwaccels to avoid stale + * pointers lying around; * the state was transferred to FrameThreadContext in * ff_thread_finish_setup(), so nothing is leaked */ avctx->hwaccel = NULL; @@ -230,7 +240,8 @@ static attribute_align_arg void *frame_worker_thread(void *arg) p->hwaccel_serializing = 0; pthread_mutex_unlock(&p->parent->hwaccel_mutex); } - av_assert0(!avctx->hwaccel); + av_assert0(!avctx->hwaccel || + (ffhwaccel(avctx->hwaccel)->caps_internal & HWACCEL_CAP_THREAD_SAFE)); if (p->async_serializing) { p->async_serializing = 0; @@ -259,7 +270,7 @@ static attribute_align_arg void *frame_worker_thread(void *arg) * @param for_user 0 if the destination is a codec thread, 1 if the destination is the user's thread * @return 0 on success, negative error code on failure */ -static int update_context_from_thread(AVCodecContext *dst, AVCodecContext *src, int for_user) +static int update_context_from_thread(AVCodecContext *dst, const AVCodecContext *src, int for_user) { const FFCodec *const codec = ffcodec(dst->codec); int err = 0; @@ -286,7 +297,11 @@ static int update_context_from_thread(AVCodecContext *dst, AVCodecContext *src, dst->level = src->level; dst->bits_per_raw_sample = src->bits_per_raw_sample; +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS dst->ticks_per_frame = src->ticks_per_frame; +FF_ENABLE_DEPRECATION_WARNINGS +#endif dst->color_primaries = src->color_primaries; dst->color_trc = src->color_trc; @@ -296,12 +311,6 @@ static int update_context_from_thread(AVCodecContext *dst, AVCodecContext *src, dst->sample_rate = src->sample_rate; dst->sample_fmt = src->sample_fmt; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - dst->channels = src->channels; - dst->channel_layout = src->channel_layout; -FF_ENABLE_DEPRECATION_WARNINGS -#endif err = av_channel_layout_copy(&dst->ch_layout, &src->ch_layout); if (err < 0) return err; @@ -319,17 +328,57 @@ FF_ENABLE_DEPRECATION_WARNINGS dst->hwaccel_flags = src->hwaccel_flags; - err = av_buffer_replace(&dst->internal->pool, src->internal->pool); - if (err < 0) - return err; + ff_refstruct_replace(&dst->internal->pool, src->internal->pool); } if (for_user) { if (codec->update_thread_context_for_user) err = codec->update_thread_context_for_user(dst, src); } else { - if (codec->update_thread_context) + const PerThreadContext *p_src = src->internal->thread_ctx; + PerThreadContext *p_dst = dst->internal->thread_ctx; + + if (codec->update_thread_context) { err = codec->update_thread_context(dst, src); + if (err < 0) + return err; + } + + // reset dst hwaccel state if needed + av_assert0(p_dst->hwaccel_threadsafe || + (!dst->hwaccel && !dst->internal->hwaccel_priv_data)); + if (p_dst->hwaccel_threadsafe && + (!p_src->hwaccel_threadsafe || dst->hwaccel != src->hwaccel)) { + ff_hwaccel_uninit(dst); + p_dst->hwaccel_threadsafe = 0; + } + + // propagate hwaccel state for threadsafe hwaccels + if (p_src->hwaccel_threadsafe) { + const FFHWAccel *hwaccel = ffhwaccel(src->hwaccel); + if (!dst->hwaccel) { + if (hwaccel->priv_data_size) { + av_assert0(hwaccel->update_thread_context); + + dst->internal->hwaccel_priv_data = + av_mallocz(hwaccel->priv_data_size); + if (!dst->internal->hwaccel_priv_data) + return AVERROR(ENOMEM); + } + dst->hwaccel = src->hwaccel; + } + av_assert0(dst->hwaccel == src->hwaccel); + + if (hwaccel->update_thread_context) { + err = hwaccel->update_thread_context(dst, src); + if (err < 0) { + av_log(dst, AV_LOG_ERROR, "Error propagating hwaccel state\n"); + ff_hwaccel_uninit(dst); + return err; + } + } + p_dst->hwaccel_threadsafe = 1; + } } return err; @@ -342,7 +391,7 @@ FF_ENABLE_DEPRECATION_WARNINGS * @param src The source context. * @return 0 on success, negative error code on failure */ -static int update_context_from_user(AVCodecContext *dst, AVCodecContext *src) +static int update_context_from_user(AVCodecContext *dst, const AVCodecContext *src) { int err; @@ -363,28 +412,6 @@ static int update_context_from_user(AVCodecContext *dst, AVCodecContext *src) dst->skip_frame = src->skip_frame; dst->frame_num = src->frame_num; -#if FF_API_AVCTX_FRAME_NUMBER -FF_DISABLE_DEPRECATION_WARNINGS - dst->frame_number = src->frame_number; -FF_ENABLE_DEPRECATION_WARNINGS -#endif -#if FF_API_REORDERED_OPAQUE -FF_DISABLE_DEPRECATION_WARNINGS - dst->reordered_opaque = src->reordered_opaque; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - - if (src->slice_count && src->slice_offset) { - if (dst->slice_count < src->slice_count) { - int err = av_reallocp_array(&dst->slice_offset, src->slice_count, - sizeof(*dst->slice_offset)); - if (err < 0) - return err; - } - memcpy(dst->slice_offset, src->slice_offset, - src->slice_count * sizeof(*dst->slice_offset)); - } - dst->slice_count = src->slice_count; av_packet_unref(dst->internal->last_pkt_props); err = av_packet_copy_props(dst->internal->last_pkt_props, src->internal->last_pkt_props); @@ -433,10 +460,12 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx, } /* transfer the stashed hwaccel state, if any */ - av_assert0(!p->avctx->hwaccel); - FFSWAP(const AVHWAccel*, p->avctx->hwaccel, fctx->stash_hwaccel); - FFSWAP(void*, p->avctx->hwaccel_context, fctx->stash_hwaccel_context); - FFSWAP(void*, p->avctx->internal->hwaccel_priv_data, fctx->stash_hwaccel_priv); + av_assert0(!p->avctx->hwaccel || p->hwaccel_threadsafe); + if (!p->hwaccel_threadsafe) { + FFSWAP(const AVHWAccel*, p->avctx->hwaccel, fctx->stash_hwaccel); + FFSWAP(void*, p->avctx->hwaccel_context, fctx->stash_hwaccel_context); + FFSWAP(void*, p->avctx->internal->hwaccel_priv_data, fctx->stash_hwaccel_priv); + } av_packet_unref(p->avpkt); ret = av_packet_ref(p->avpkt, avpkt); @@ -544,7 +573,7 @@ int ff_thread_decode_frame(AVCodecContext *avctx, void ff_thread_report_progress(ThreadFrame *f, int n, int field) { PerThreadContext *p; - atomic_int *progress = f->progress ? (atomic_int*)f->progress->data : NULL; + atomic_int *progress = f->progress ? f->progress->progress : NULL; if (!progress || atomic_load_explicit(&progress[field], memory_order_relaxed) >= n) @@ -567,7 +596,7 @@ void ff_thread_report_progress(ThreadFrame *f, int n, int field) void ff_thread_await_progress(const ThreadFrame *f, int n, int field) { PerThreadContext *p; - atomic_int *progress = f->progress ? (atomic_int*)f->progress->data : NULL; + atomic_int *progress = f->progress ? f->progress->progress : NULL; if (!progress || atomic_load_explicit(&progress[field], memory_order_acquire) >= n) @@ -586,30 +615,38 @@ void ff_thread_await_progress(const ThreadFrame *f, int n, int field) } void ff_thread_finish_setup(AVCodecContext *avctx) { - PerThreadContext *p = avctx->internal->thread_ctx; + PerThreadContext *p; if (!(avctx->active_thread_type&FF_THREAD_FRAME)) return; - if (avctx->hwaccel && !p->hwaccel_serializing) { + p = avctx->internal->thread_ctx; + + p->hwaccel_threadsafe = avctx->hwaccel && + (ffhwaccel(avctx->hwaccel)->caps_internal & HWACCEL_CAP_THREAD_SAFE); + + if (hwaccel_serial(avctx) && !p->hwaccel_serializing) { pthread_mutex_lock(&p->parent->hwaccel_mutex); p->hwaccel_serializing = 1; } /* this assumes that no hwaccel calls happen before ff_thread_finish_setup() */ if (avctx->hwaccel && - !(avctx->hwaccel->caps_internal & HWACCEL_CAP_ASYNC_SAFE)) { + !(ffhwaccel(avctx->hwaccel)->caps_internal & HWACCEL_CAP_ASYNC_SAFE)) { p->async_serializing = 1; async_lock(p->parent); } - /* save hwaccel state for passing to the next thread; + /* thread-unsafe hwaccels share a single private data instance, so we + * save hwaccel state for passing to the next thread; * this is done here so that this worker thread can wipe its own hwaccel * state after decoding, without requiring synchronization */ av_assert0(!p->parent->stash_hwaccel); - p->parent->stash_hwaccel = avctx->hwaccel; - p->parent->stash_hwaccel_context = avctx->hwaccel_context; - p->parent->stash_hwaccel_priv = avctx->internal->hwaccel_priv_data; + if (hwaccel_serial(avctx)) { + p->parent->stash_hwaccel = avctx->hwaccel; + p->parent->stash_hwaccel_context = avctx->hwaccel_context; + p->parent->stash_hwaccel_priv = avctx->internal->hwaccel_priv_data; + } pthread_mutex_lock(&p->progress_mutex); if(atomic_load(&p->state) == STATE_SETUP_FINISHED){ @@ -680,15 +717,17 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) if (codec->close && p->thread_init != UNINITIALIZED) codec->close(ctx); + /* When using a threadsafe hwaccel, this is where + * each thread's context is uninit'd and freed. */ + ff_hwaccel_uninit(ctx); + if (ctx->priv_data) { if (codec->p.priv_class) av_opt_free(ctx->priv_data); av_freep(&ctx->priv_data); } - av_freep(&ctx->slice_offset); - - av_buffer_unref(&ctx->internal->pool); + ff_refstruct_unref(&ctx->internal->pool); av_packet_free(&ctx->internal->last_pkt_props); av_freep(&ctx->internal); av_buffer_unref(&ctx->hw_frames_ctx); @@ -706,7 +745,7 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) ff_pthread_free(fctx, thread_ctx_offsets); /* if we have stashed hwaccel state, move it to the user-facing context, - * so it will be freed in avcodec_close() */ + * so it will be freed in ff_codec_close() */ av_assert0(!avctx->hwaccel); FFSWAP(const AVHWAccel*, avctx->hwaccel, fctx->stash_hwaccel); FFSWAP(void*, avctx->hwaccel_context, fctx->stash_hwaccel_context); @@ -736,7 +775,7 @@ static av_cold int init_thread(PerThreadContext *p, int *threads_to_free, p->parent = fctx; p->avctx = copy; - copy->internal = av_mallocz(sizeof(*copy->internal)); + copy->internal = ff_decode_internal_alloc(); if (!copy->internal) return AVERROR(ENOMEM); copy->internal->thread_ctx = p; @@ -884,11 +923,12 @@ void ff_thread_flush(AVCodecContext *avctx) int ff_thread_can_start_frame(AVCodecContext *avctx) { - PerThreadContext *p = avctx->internal->thread_ctx; - - if ((avctx->active_thread_type&FF_THREAD_FRAME) && atomic_load(&p->state) != STATE_SETTING_UP && + if ((avctx->active_thread_type & FF_THREAD_FRAME) && ffcodec(avctx->codec)->update_thread_context) { - return 0; + PerThreadContext *p = avctx->internal->thread_ctx; + + if (atomic_load(&p->state) != STATE_SETTING_UP) + return 0; } return 1; @@ -903,10 +943,8 @@ static int thread_get_buffer_internal(AVCodecContext *avctx, AVFrame *f, int fla return ff_get_buffer(avctx, f, flags); p = avctx->internal->thread_ctx; -FF_DISABLE_DEPRECATION_WARNINGS if (atomic_load(&p->state) != STATE_SETTING_UP && ffcodec(avctx->codec)->update_thread_context) { -FF_ENABLE_DEPRECATION_WARNINGS av_log(avctx, AV_LOG_ERROR, "get_buffer() cannot be called after ff_thread_finish_setup()\n"); return -1; } @@ -941,37 +979,24 @@ int ff_thread_get_ext_buffer(AVCodecContext *avctx, ThreadFrame *f, int flags) return ff_get_buffer(avctx, f->f, flags); if (ffcodec(avctx->codec)->caps_internal & FF_CODEC_CAP_ALLOCATE_PROGRESS) { - atomic_int *progress; - f->progress = av_buffer_alloc(2 * sizeof(*progress)); - if (!f->progress) { + f->progress = ff_refstruct_allocz(sizeof(*f->progress)); + if (!f->progress) return AVERROR(ENOMEM); - } - progress = (atomic_int*)f->progress->data; - atomic_init(&progress[0], -1); - atomic_init(&progress[1], -1); + atomic_init(&f->progress->progress[0], -1); + atomic_init(&f->progress->progress[1], -1); } ret = ff_thread_get_buffer(avctx, f->f, flags); if (ret) - av_buffer_unref(&f->progress); + ff_refstruct_unref(&f->progress); return ret; } -void ff_thread_release_buffer(AVCodecContext *avctx, AVFrame *f) -{ - if (!f) - return; - - if (avctx->debug & FF_DEBUG_BUFFERS) - av_log(avctx, AV_LOG_DEBUG, "thread_release_buffer called on pic %p\n", f); - - av_frame_unref(f); -} - -void ff_thread_release_ext_buffer(AVCodecContext *avctx, ThreadFrame *f) +void ff_thread_release_ext_buffer(ThreadFrame *f) { - av_buffer_unref(&f->progress); + ff_refstruct_unref(&f->progress); f->owner[0] = f->owner[1] = NULL; - ff_thread_release_buffer(avctx, f->f); + if (f->f) + av_frame_unref(f->f); } diff --git a/libavcodec/qdm2_tablegen.h b/libavcodec/qdm2_tablegen.h index a68ea8599b2..6e174a7801f 100644 --- a/libavcodec/qdm2_tablegen.h +++ b/libavcodec/qdm2_tablegen.h @@ -116,9 +116,9 @@ static av_cold void build_vlc(VLC *vlc, int nb_bits, int nb_codes, { vlc->table = &qdm2_table[*offset]; vlc->table_allocated = FF_ARRAY_ELEMS(qdm2_table) - *offset; - ff_init_vlc_from_lengths(vlc, nb_bits, nb_codes, + ff_vlc_init_from_lengths(vlc, nb_bits, nb_codes, &tab[0][1], 2, &tab[0][0], 2, 1, - -1, INIT_VLC_STATIC_OVERLONG | INIT_VLC_LE, NULL); + -1, VLC_INIT_STATIC_OVERLONG | VLC_INIT_LE, NULL); *offset += vlc->table_size; } diff --git a/libavcodec/qdmc.c b/libavcodec/qdmc.c index 081c4dd46f0..474e5ef8faa 100644 --- a/libavcodec/qdmc.c +++ b/libavcodec/qdmc.c @@ -172,9 +172,9 @@ static av_cold void qdmc_init_static_data(void) static VLCElem vlc_buffer[13698]; vtable[i].table = &vlc_buffer[offset]; vtable[i].table_allocated = FF_ARRAY_ELEMS(vlc_buffer) - offset; - ff_init_vlc_from_lengths(&vtable[i], huff_bits[i], huff_sizes[i], + ff_vlc_init_from_lengths(&vtable[i], huff_bits[i], huff_sizes[i], &hufftab[0][1], 2, &hufftab[0][0], 2, 1, -1, - INIT_VLC_LE | INIT_VLC_STATIC_OVERLONG, NULL); + VLC_INIT_LE | VLC_INIT_STATIC_OVERLONG, NULL); hufftab += huff_sizes[i]; offset += vtable[i].table_size; } diff --git a/libavcodec/qdrw.c b/libavcodec/qdrw.c index e41451e9a77..21a53b8e728 100644 --- a/libavcodec/qdrw.c +++ b/libavcodec/qdrw.c @@ -384,7 +384,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, ret = parse_palette(avctx, &gbc, (uint32_t *)p->data[1], colors, flags & 0x8000); if (ret < 0) return ret; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS p->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif /* jump to image data */ bytestream2_skip(&gbc, 18); @@ -503,7 +507,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if (*got_frame) { p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; return avpkt->size; } else { diff --git a/libavcodec/qoadec.c b/libavcodec/qoadec.c new file mode 100644 index 00000000000..c5cf351f9c1 --- /dev/null +++ b/libavcodec/qoadec.c @@ -0,0 +1,170 @@ +/* + * QOA decoder + * + * 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 "avcodec.h" +#include "codec_internal.h" +#include "decode.h" +#include "get_bits.h" +#include "bytestream.h" +#include "mathops.h" + +#define QOA_SLICE_LEN 20 +#define QOA_LMS_LEN 4 + +typedef struct QOAChannel { + int history[QOA_LMS_LEN]; + int weights[QOA_LMS_LEN]; +} QOAChannel; + +typedef struct QOAContext { + QOAChannel ch[256]; +} QOAContext; + +static const int16_t qoa_dequant_tab[16][8] = { + { 1, -1, 3, -3, 5, -5, 7, -7}, + { 5, -5, 18, -18, 32, -32, 49, -49}, + { 16, -16, 53, -53, 95, -95, 147, -147}, + { 34, -34, 113, -113, 203, -203, 315, -315}, + { 63, -63, 210, -210, 378, -378, 588, -588}, + { 104, -104, 345, -345, 621, -621, 966, -966}, + { 158, -158, 528, -528, 950, -950, 1477, -1477}, + { 228, -228, 760, -760, 1368, -1368, 2128, -2128}, + { 316, -316, 1053, -1053, 1895, -1895, 2947, -2947}, + { 422, -422, 1405, -1405, 2529, -2529, 3934, -3934}, + { 548, -548, 1828, -1828, 3290, -3290, 5117, -5117}, + { 696, -696, 2320, -2320, 4176, -4176, 6496, -6496}, + { 868, -868, 2893, -2893, 5207, -5207, 8099, -8099}, + {1064, -1064, 3548, -3548, 6386, -6386, 9933, -9933}, + {1286, -1286, 4288, -4288, 7718, -7718, 12005, -12005}, + {1536, -1536, 5120, -5120, 9216, -9216, 14336, -14336}, +}; + +static av_cold int qoa_decode_init(AVCodecContext *avctx) +{ + avctx->sample_fmt = AV_SAMPLE_FMT_S16; + + return 0; +} + +static int qoa_lms_predict(QOAChannel *lms) +{ + int prediction = 0; + for (int i = 0; i < QOA_LMS_LEN; i++) + prediction += (unsigned)lms->weights[i] * lms->history[i]; + return prediction >> 13; +} + +static void qoa_lms_update(QOAChannel *lms, int sample, int residual) +{ + int delta = residual >> 4; + for (int i = 0; i < QOA_LMS_LEN; i++) + lms->weights[i] += lms->history[i] < 0 ? -delta : delta; + for (int i = 0; i < QOA_LMS_LEN-1; i++) + lms->history[i] = lms->history[i+1]; + lms->history[QOA_LMS_LEN-1] = sample; +} + +static int qoa_decode_frame(AVCodecContext *avctx, AVFrame *frame, + int *got_frame_ptr, AVPacket *avpkt) +{ + QOAContext *s = avctx->priv_data; + int ret, frame_size, nb_channels, sample_rate; + GetByteContext gb; + int16_t *samples; + + bytestream2_init(&gb, avpkt->data, avpkt->size); + + nb_channels = bytestream2_get_byte(&gb); + sample_rate = bytestream2_get_be24(&gb); + if (!sample_rate || !nb_channels) + return AVERROR_INVALIDDATA; + + if (nb_channels != avctx->ch_layout.nb_channels) { + av_channel_layout_uninit(&avctx->ch_layout); + av_channel_layout_default(&avctx->ch_layout, nb_channels); + if ((ret = av_channel_layout_copy(&frame->ch_layout, &avctx->ch_layout)) < 0) + return ret; + } + + frame->sample_rate = avctx->sample_rate = sample_rate; + + frame->nb_samples = bytestream2_get_be16(&gb); + frame_size = bytestream2_get_be16(&gb); + if (frame_size > avpkt->size) + return AVERROR_INVALIDDATA; + + if (avpkt->size < 8 + QOA_LMS_LEN * 4 * nb_channels + + 8LL * ((frame->nb_samples + QOA_SLICE_LEN - 1) / QOA_SLICE_LEN) * nb_channels) + return AVERROR_INVALIDDATA; + + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + return ret; + samples = (int16_t *)frame->data[0]; + + for (int ch = 0; ch < nb_channels; ch++) { + QOAChannel *qch = &s->ch[ch]; + + for (int n = 0; n < QOA_LMS_LEN; n++) + qch->history[n] = sign_extend(bytestream2_get_be16u(&gb), 16); + for (int n = 0; n < QOA_LMS_LEN; n++) + qch->weights[n] = sign_extend(bytestream2_get_be16u(&gb), 16); + } + + for (int sample_index = 0; sample_index < frame->nb_samples; + sample_index += QOA_SLICE_LEN) { + for (int ch = 0; ch < nb_channels; ch++) { + QOAChannel *lms = &s->ch[ch]; + uint64_t slice = bytestream2_get_be64u(&gb); + int scalefactor = (slice >> 60) & 0xf; + int slice_start = sample_index * nb_channels + ch; + int slice_end = av_clip(sample_index + QOA_SLICE_LEN, 0, frame->nb_samples) * nb_channels + ch; + + for (int si = slice_start; si < slice_end; si += nb_channels) { + int predicted = qoa_lms_predict(lms); + int quantized = (slice >> 57) & 0x7; + int dequantized = qoa_dequant_tab[scalefactor][quantized]; + int reconstructed = av_clip_int16(predicted + dequantized); + + samples[si] = reconstructed; + slice <<= 3; + + qoa_lms_update(lms, reconstructed, dequantized); + } + } + } + + *got_frame_ptr = 1; + + return avpkt->size; +} + +const FFCodec ff_qoa_decoder = { + .p.name = "qoa", + CODEC_LONG_NAME("QOA (Quite OK Audio)"), + .p.type = AVMEDIA_TYPE_AUDIO, + .p.id = AV_CODEC_ID_QOA, + .priv_data_size = sizeof(QOAContext), + .init = qoa_decode_init, + FF_CODEC_DECODE_CB(qoa_decode_frame), + .p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | + AV_CODEC_CAP_DR1, + .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16, + AV_SAMPLE_FMT_NONE }, +}; diff --git a/libavcodec/qoidec.c b/libavcodec/qoidec.c index 9414d2fbe9a..37bc2084c06 100644 --- a/libavcodec/qoidec.c +++ b/libavcodec/qoidec.c @@ -106,7 +106,7 @@ static int qoi_decode_frame(AVCodecContext *avctx, AVFrame *p, memcpy(&dst[off_x * channels], px, channels); } - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/qpeg.c b/libavcodec/qpeg.c index 5bca338acf5..9b4ad8e25d2 100644 --- a/libavcodec/qpeg.c +++ b/libavcodec/qpeg.c @@ -297,14 +297,23 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, } /* make the palette available on the way out */ - p->palette_has_changed = ff_copy_palette(a->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + p->palette_has_changed = +#endif + ff_copy_palette(a->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(p->data[1], a->pal, AVPALETTE_SIZE); - av_frame_unref(ref); - if ((ret = av_frame_ref(ref, p)) < 0) + if ((ret = av_frame_replace(ref, p)) < 0) return ret; - p->key_frame = intra; + if (intra) + p->flags |= AV_FRAME_FLAG_KEY; + else + p->flags &= ~AV_FRAME_FLAG_KEY; p->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; *got_frame = 1; diff --git a/libavcodec/qsv.c b/libavcodec/qsv.c index 7af154202c4..452c0c68560 100644 --- a/libavcodec/qsv.c +++ b/libavcodec/qsv.c @@ -208,7 +208,6 @@ enum AVPixelFormat ff_qsv_map_fourcc(uint32_t fourcc) case MFX_FOURCC_P8: return AV_PIX_FMT_PAL8; case MFX_FOURCC_A2RGB10: return AV_PIX_FMT_X2RGB10; case MFX_FOURCC_RGB4: return AV_PIX_FMT_BGRA; -#if CONFIG_VAAPI case MFX_FOURCC_YUY2: return AV_PIX_FMT_YUYV422; case MFX_FOURCC_Y210: return AV_PIX_FMT_Y210; case MFX_FOURCC_AYUV: return AV_PIX_FMT_VUYX; @@ -217,7 +216,6 @@ enum AVPixelFormat ff_qsv_map_fourcc(uint32_t fourcc) case MFX_FOURCC_P016: return AV_PIX_FMT_P012; case MFX_FOURCC_Y216: return AV_PIX_FMT_Y212; case MFX_FOURCC_Y416: return AV_PIX_FMT_XV36; -#endif #endif } return AV_PIX_FMT_NONE; @@ -245,7 +243,6 @@ int ff_qsv_map_pixfmt(enum AVPixelFormat format, uint32_t *fourcc, uint16_t *shi *fourcc = MFX_FOURCC_RGB4; *shift = 0; return AV_PIX_FMT_BGRA; -#if CONFIG_VAAPI case AV_PIX_FMT_YUV422P: case AV_PIX_FMT_YUYV422: *fourcc = MFX_FOURCC_YUY2; @@ -277,7 +274,6 @@ int ff_qsv_map_pixfmt(enum AVPixelFormat format, uint32_t *fourcc, uint16_t *shi *fourcc = MFX_FOURCC_Y416; *shift = 1; return AV_PIX_FMT_XV36; -#endif #endif default: return AVERROR(ENOSYS); @@ -500,7 +496,7 @@ static int qsv_new_mfx_loader(AVCodecContext *avctx, mfxStatus sts; mfxLoader loader = NULL; mfxConfig cfg; - mfxVariant impl_value; + mfxVariant impl_value = {0}; loader = MFXLoad(); if (!loader) { @@ -681,18 +677,31 @@ static int qsv_create_mfx_session(AVCodecContext *avctx, int ff_qsv_init_internal_session(AVCodecContext *avctx, QSVSession *qs, const char *load_plugins, int gpu_copy) { + mfxIMPL impls[] = { #if CONFIG_D3D11VA - mfxIMPL impl = MFX_IMPL_AUTO_ANY | MFX_IMPL_VIA_D3D11; -#else - mfxIMPL impl = MFX_IMPL_AUTO_ANY; + MFX_IMPL_AUTO_ANY | MFX_IMPL_VIA_D3D11, #endif + MFX_IMPL_AUTO_ANY + }; + mfxIMPL impl; mfxVersion ver = { { QSV_VERSION_MINOR, QSV_VERSION_MAJOR } }; const char *desc; - int ret = qsv_create_mfx_session(avctx, impl, &ver, gpu_copy, &qs->session, + int ret; + + for (int i = 0; i < FF_ARRAY_ELEMS(impls); i++) { + ret = qsv_create_mfx_session(avctx, impls[i], &ver, gpu_copy, &qs->session, &qs->loader); - if (ret) - return ret; + + if (ret == 0) + break; + + if (i == FF_ARRAY_ELEMS(impls) - 1) + return ret; + else + av_log(avctx, AV_LOG_ERROR, "The current mfx implementation is not " + "supported, try next mfx implementation.\n"); + } #ifdef AVCODEC_QSV_LINUX_SESSION_HANDLE ret = ff_qsv_set_display_handle(avctx, qs); diff --git a/libavcodec/qsv_internal.h b/libavcodec/qsv_internal.h index 5119ef4dffa..c2d301b4a22 100644 --- a/libavcodec/qsv_internal.h +++ b/libavcodec/qsv_internal.h @@ -23,9 +23,9 @@ #include "config.h" -#if CONFIG_VAAPI +#if CONFIG_VAAPI && !defined(_WIN32) // Do not enable for libva-win32 on Windows #define AVCODEC_QSV_LINUX_SESSION_HANDLE -#endif //CONFIG_VAAPI +#endif //CONFIG_VAAPI && !defined(_WIN32) #ifdef AVCODEC_QSV_LINUX_SESSION_HANDLE #include @@ -35,7 +35,6 @@ #endif #include #include -#include #include "libavutil/hwcontext_vaapi.h" #endif diff --git a/libavcodec/qsvdec.c b/libavcodec/qsvdec.c index 6bc85116adf..bacb21afdf3 100644 --- a/libavcodec/qsvdec.c +++ b/libavcodec/qsvdec.c @@ -378,9 +378,12 @@ static int qsv_decode_init_context(AVCodecContext *avctx, QSVContext *q, mfxVide q->frame_info = param->mfx.FrameInfo; - if (!avctx->hw_frames_ctx) - q->pool = av_buffer_pool_init(av_image_get_buffer_size(avctx->pix_fmt, - FFALIGN(avctx->width, 128), FFALIGN(avctx->height, 64), 1), av_buffer_allocz); + if (!avctx->hw_frames_ctx) { + ret = av_image_get_buffer_size(avctx->pix_fmt, FFALIGN(avctx->width, 128), FFALIGN(avctx->height, 64), 1); + if (ret < 0) + return ret; + q->pool = av_buffer_pool_init(ret, av_buffer_allocz); + } return 0; } @@ -651,42 +654,50 @@ static int qsv_export_film_grain(AVCodecContext *avctx, mfxExtAV1FilmGrainParam static int qsv_export_hdr_side_data(AVCodecContext *avctx, mfxExtMasteringDisplayColourVolume *mdcv, mfxExtContentLightLevelInfo *clli, AVFrame *frame) { + int ret; + // The SDK re-uses this flag for HDR SEI parsing if (mdcv->InsertPayloadToggle) { - AVMasteringDisplayMetadata *mastering = av_mastering_display_metadata_create_side_data(frame); + AVMasteringDisplayMetadata *mastering; const int mapping[3] = {2, 0, 1}; const int chroma_den = 50000; const int luma_den = 10000; int i; - if (!mastering) - return AVERROR(ENOMEM); + ret = ff_decode_mastering_display_new(avctx, frame, &mastering); + if (ret < 0) + return ret; - for (i = 0; i < 3; i++) { - const int j = mapping[i]; - mastering->display_primaries[i][0] = av_make_q(mdcv->DisplayPrimariesX[j], chroma_den); - mastering->display_primaries[i][1] = av_make_q(mdcv->DisplayPrimariesY[j], chroma_den); - } + if (mastering) { + for (i = 0; i < 3; i++) { + const int j = mapping[i]; + mastering->display_primaries[i][0] = av_make_q(mdcv->DisplayPrimariesX[j], chroma_den); + mastering->display_primaries[i][1] = av_make_q(mdcv->DisplayPrimariesY[j], chroma_den); + } - mastering->white_point[0] = av_make_q(mdcv->WhitePointX, chroma_den); - mastering->white_point[1] = av_make_q(mdcv->WhitePointY, chroma_den); + mastering->white_point[0] = av_make_q(mdcv->WhitePointX, chroma_den); + mastering->white_point[1] = av_make_q(mdcv->WhitePointY, chroma_den); - mastering->max_luminance = av_make_q(mdcv->MaxDisplayMasteringLuminance, luma_den); - mastering->min_luminance = av_make_q(mdcv->MinDisplayMasteringLuminance, luma_den); + mastering->max_luminance = av_make_q(mdcv->MaxDisplayMasteringLuminance, luma_den); + mastering->min_luminance = av_make_q(mdcv->MinDisplayMasteringLuminance, luma_den); - mastering->has_luminance = 1; - mastering->has_primaries = 1; + mastering->has_luminance = 1; + mastering->has_primaries = 1; + } } // The SDK re-uses this flag for HDR SEI parsing if (clli->InsertPayloadToggle) { - AVContentLightMetadata *light = av_content_light_metadata_create_side_data(frame); + AVContentLightMetadata *light; - if (!light) - return AVERROR(ENOMEM); + ret = ff_decode_content_light_new(avctx, frame, &light); + if (ret < 0) + return ret; - light->MaxCLL = clli->MaxContentLightLevel; - light->MaxFALL = clli->MaxPicAverageLightLevel; + if (light) { + light->MaxCLL = clli->MaxContentLightLevel; + light->MaxFALL = clli->MaxPicAverageLightLevel; + } } return 0; @@ -754,7 +765,9 @@ static int qsv_decode(AVCodecContext *avctx, QSVContext *q, if (!*sync && !bs.DataOffset) { bs.DataOffset = avpkt->size; ++q->zero_consume_run; - if (q->zero_consume_run > 1) + if (q->zero_consume_run > 1 && + (avpkt->size || + ret != MFX_ERR_MORE_DATA)) ff_qsv_print_warning(avctx, ret, "A decode call did not consume any data"); } else { q->zero_consume_run = 0; @@ -828,14 +841,18 @@ static int qsv_decode(AVCodecContext *avctx, QSVContext *q, outsurf->Info.PicStruct & MFX_PICSTRUCT_FRAME_TRIPLING ? 4 : outsurf->Info.PicStruct & MFX_PICSTRUCT_FRAME_DOUBLING ? 2 : outsurf->Info.PicStruct & MFX_PICSTRUCT_FIELD_REPEATED ? 1 : 0; - frame->top_field_first = - outsurf->Info.PicStruct & MFX_PICSTRUCT_FIELD_TFF; - frame->interlaced_frame = + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * + !!(outsurf->Info.PicStruct & MFX_PICSTRUCT_FIELD_TFF); + frame->flags |= AV_FRAME_FLAG_INTERLACED * !(outsurf->Info.PicStruct & MFX_PICSTRUCT_PROGRESSIVE); frame->pict_type = ff_qsv_map_pictype(aframe.frame->dec_info.FrameType); //Key frame is IDR frame is only suitable for H264. For HEVC, IRAPs are key frames. - if (avctx->codec_id == AV_CODEC_ID_H264) - frame->key_frame = !!(aframe.frame->dec_info.FrameType & MFX_FRAMETYPE_IDR); + if (avctx->codec_id == AV_CODEC_ID_H264) { + if (aframe.frame->dec_info.FrameType & MFX_FRAMETYPE_IDR) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; + } /* update the surface properties */ if (avctx->pix_fmt == AV_PIX_FMT_QSV) @@ -913,7 +930,7 @@ static int qsv_process_data(AVCodecContext *avctx, QSVContext *q, ret = qsv_decode_header(avctx, q, pkt, pix_fmt, ¶m); if (ret < 0) { if (ret == AVERROR(EAGAIN)) - av_log(avctx, AV_LOG_INFO, "More data is required to decode header\n"); + av_log(avctx, AV_LOG_VERBOSE, "More data is required to decode header\n"); else av_log(avctx, AV_LOG_ERROR, "Error decoding header\n"); goto reinit_fail; @@ -1072,6 +1089,9 @@ static int qsv_decode_frame(AVCodecContext *avctx, AVFrame *frame, ret = qsv_process_data(avctx, &s->qsv, frame, got_frame, &s->buffer_pkt); if (ret < 0){ + if (ret == AVERROR(EAGAIN)) + ret = 0; + /* Drop buffer_pkt when failed to decode the packet. Otherwise, the decoder will keep decoding the failure packet. */ av_packet_unref(&s->buffer_pkt); @@ -1120,17 +1140,6 @@ const FFCodec ff_##x##_qsv_decoder = { \ .bsfs = bsf_name, \ .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HYBRID, \ .p.priv_class = &x##_qsv_class, \ - .p.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_NV12, \ - AV_PIX_FMT_P010, \ - AV_PIX_FMT_P012, \ - AV_PIX_FMT_YUYV422, \ - AV_PIX_FMT_Y210, \ - AV_PIX_FMT_Y212, \ - AV_PIX_FMT_VUYX, \ - AV_PIX_FMT_XV30, \ - AV_PIX_FMT_XV36, \ - AV_PIX_FMT_QSV, \ - AV_PIX_FMT_NONE }, \ .hw_configs = qsv_hw_configs, \ .p.wrapper_name = "qsv", \ .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE, \ @@ -1142,18 +1151,18 @@ const FFCodec ff_##x##_qsv_decoder = { \ static const AVOption hevc_options[] = { { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 1, INT_MAX, VD }, - { "load_plugin", "A user plugin to load in an internal session", OFFSET(load_plugin), AV_OPT_TYPE_INT, { .i64 = LOAD_PLUGIN_HEVC_HW }, LOAD_PLUGIN_NONE, LOAD_PLUGIN_HEVC_HW, VD, "load_plugin" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_NONE }, 0, 0, VD, "load_plugin" }, - { "hevc_sw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_SW }, 0, 0, VD, "load_plugin" }, - { "hevc_hw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_HW }, 0, 0, VD, "load_plugin" }, + { "load_plugin", "A user plugin to load in an internal session", OFFSET(load_plugin), AV_OPT_TYPE_INT, { .i64 = LOAD_PLUGIN_HEVC_HW }, LOAD_PLUGIN_NONE, LOAD_PLUGIN_HEVC_HW, VD, .unit = "load_plugin" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_NONE }, 0, 0, VD, .unit = "load_plugin" }, + { "hevc_sw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_SW }, 0, 0, VD, .unit = "load_plugin" }, + { "hevc_hw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_HW }, 0, 0, VD, .unit = "load_plugin" }, { "load_plugins", "A :-separate list of hexadecimal plugin UIDs to load in an internal session", OFFSET(qsv.load_plugins), AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, VD }, - { "gpu_copy", "A GPU-accelerated copy between video and system memory", OFFSET(qsv.gpu_copy), AV_OPT_TYPE_INT, { .i64 = MFX_GPUCOPY_DEFAULT }, MFX_GPUCOPY_DEFAULT, MFX_GPUCOPY_OFF, VD, "gpu_copy"}, - { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_DEFAULT }, 0, 0, VD, "gpu_copy"}, - { "on", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_ON }, 0, 0, VD, "gpu_copy"}, - { "off", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_OFF }, 0, 0, VD, "gpu_copy"}, + { "gpu_copy", "A GPU-accelerated copy between video and system memory", OFFSET(qsv.gpu_copy), AV_OPT_TYPE_INT, { .i64 = MFX_GPUCOPY_DEFAULT }, MFX_GPUCOPY_DEFAULT, MFX_GPUCOPY_OFF, VD, .unit = "gpu_copy"}, + { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_DEFAULT }, 0, 0, VD, .unit = "gpu_copy"}, + { "on", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_ON }, 0, 0, VD, .unit = "gpu_copy"}, + { "off", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_OFF }, 0, 0, VD, .unit = "gpu_copy"}, { NULL }, }; DEFINE_QSV_DECODER_WITH_OPTION(hevc, HEVC, "hevc_mp4toannexb", hevc_options) @@ -1162,10 +1171,10 @@ DEFINE_QSV_DECODER_WITH_OPTION(hevc, HEVC, "hevc_mp4toannexb", hevc_options) static const AVOption options[] = { { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 1, INT_MAX, VD }, - { "gpu_copy", "A GPU-accelerated copy between video and system memory", OFFSET(qsv.gpu_copy), AV_OPT_TYPE_INT, { .i64 = MFX_GPUCOPY_DEFAULT }, MFX_GPUCOPY_DEFAULT, MFX_GPUCOPY_OFF, VD, "gpu_copy"}, - { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_DEFAULT }, 0, 0, VD, "gpu_copy"}, - { "on", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_ON }, 0, 0, VD, "gpu_copy"}, - { "off", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_OFF }, 0, 0, VD, "gpu_copy"}, + { "gpu_copy", "A GPU-accelerated copy between video and system memory", OFFSET(qsv.gpu_copy), AV_OPT_TYPE_INT, { .i64 = MFX_GPUCOPY_DEFAULT }, MFX_GPUCOPY_DEFAULT, MFX_GPUCOPY_OFF, VD, .unit = "gpu_copy"}, + { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_DEFAULT }, 0, 0, VD, .unit = "gpu_copy"}, + { "on", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_ON }, 0, 0, VD, .unit = "gpu_copy"}, + { "off", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_GPUCOPY_OFF }, 0, 0, VD, .unit = "gpu_copy"}, { NULL }, }; diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c index fc64a086e49..3a8607fca69 100644 --- a/libavcodec/qsvenc.c +++ b/libavcodec/qsvenc.c @@ -21,12 +21,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "config_components.h" - #include #include #include +#include "libavutil/avassert.h" #include "libavutil/common.h" #include "libavutil/hwcontext.h" #include "libavutil/hwcontext_qsv.h" @@ -34,9 +33,9 @@ #include "libavutil/log.h" #include "libavutil/time.h" #include "libavutil/imgutils.h" -#include "libavcodec/bytestream.h" #include "avcodec.h" +#include "encode.h" #include "internal.h" #include "packet_internal.h" #include "qsv.h" @@ -598,6 +597,13 @@ static int select_rc_mode(AVCodecContext *avctx, QSVEncContext *q) else if (want_vcm) { rc_mode = MFX_RATECONTROL_VCM; rc_desc = "video conferencing mode (VCM)"; + + if (!avctx->bit_rate) { + av_log(avctx, AV_LOG_ERROR, "Using the %s ratecontrol method without " + "setting bitrate. Please use the b option to set the desired " + "bitrate.\n", rc_desc); + return AVERROR(EINVAL); + } } #endif else if (want_la) { @@ -607,32 +613,50 @@ static int select_rc_mode(AVCodecContext *avctx, QSVEncContext *q) if (avctx->global_quality > 0) { rc_mode = MFX_RATECONTROL_LA_ICQ; rc_desc = "intelligent constant quality with lookahead (LA_ICQ)"; + } else if (!avctx->bit_rate) { + av_log(avctx, AV_LOG_ERROR, "Using the %s ratecontrol method without " + "setting bitrate. Please use the b option to set the desired " + "bitrate.\n", rc_desc); + return AVERROR(EINVAL); } } else if (avctx->global_quality > 0 && !avctx->rc_max_rate) { rc_mode = MFX_RATECONTROL_ICQ; rc_desc = "intelligent constant quality (ICQ)"; } - else if (avctx->rc_max_rate == avctx->bit_rate) { - rc_mode = MFX_RATECONTROL_CBR; - rc_desc = "constant bitrate (CBR)"; - } + else if (avctx->bit_rate) { + if (avctx->rc_max_rate == avctx->bit_rate) { + rc_mode = MFX_RATECONTROL_CBR; + rc_desc = "constant bitrate (CBR)"; + } #if QSV_HAVE_AVBR - else if (!avctx->rc_max_rate && - (avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_HEVC) && - q->avbr_accuracy && - q->avbr_convergence) { - rc_mode = MFX_RATECONTROL_AVBR; - rc_desc = "average variable bitrate (AVBR)"; - } + else if (!avctx->rc_max_rate && + (avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_HEVC) && + q->avbr_accuracy && + q->avbr_convergence) { + rc_mode = MFX_RATECONTROL_AVBR; + rc_desc = "average variable bitrate (AVBR)"; + } #endif - else if (avctx->global_quality > 0) { - rc_mode = MFX_RATECONTROL_QVBR; - rc_desc = "constant quality with VBR algorithm (QVBR)"; - } - else { - rc_mode = MFX_RATECONTROL_VBR; - rc_desc = "variable bitrate (VBR)"; + else if (avctx->global_quality > 0) { + rc_mode = MFX_RATECONTROL_QVBR; + rc_desc = "constant quality with VBR algorithm (QVBR)"; + } else { + rc_mode = MFX_RATECONTROL_VBR; + rc_desc = "variable bitrate (VBR)"; + } + } else { + rc_mode = MFX_RATECONTROL_CQP; + rc_desc = "constant quantization parameter (CQP)"; + if (avctx->codec_id == AV_CODEC_ID_AV1) + avctx->global_quality = FF_QP2LAMBDA * 128; + else + avctx->global_quality = FF_QP2LAMBDA * 26; + av_log(avctx, AV_LOG_WARNING, "Using the constant quantization " + "parameter (CQP) by default. Please use the global_quality " + "option and other options for a quality-based mode or the b " + "option and other options for a bitrate-based mode if the " + "default is not the desired choice.\n"); } q->param.mfx.RateControlMethod = rc_mode; @@ -834,7 +858,9 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) // for progressive video, the height should be aligned to 16 for // H.264. For HEVC, depending on the version of MFX, it should be // either 32 or 16. The lower number is better if possible. - q->height_align = avctx->codec_id == AV_CODEC_ID_HEVC ? 32 : 16; + // For AV1, it is 32 + q->height_align = (avctx->codec_id == AV_CODEC_ID_HEVC || + avctx->codec_id == AV_CODEC_ID_AV1) ? 32 : 16; } q->param.mfx.FrameInfo.Height = FFALIGN(avctx->height, q->height_align); @@ -1118,11 +1144,16 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q) q->extco3.MaxFrameSizeI = q->max_frame_size_i; if (q->max_frame_size_p >= 0) q->extco3.MaxFrameSizeP = q->max_frame_size_p; + if (sw_format == AV_PIX_FMT_BGRA && + (q->profile == MFX_PROFILE_HEVC_REXT || + q->profile == MFX_PROFILE_UNKNOWN)) + q->extco3.TargetChromaFormatPlus1 = MFX_CHROMAFORMAT_YUV444 + 1; q->extco3.ScenarioInfo = q->scenario; } else if (avctx->codec_id == AV_CODEC_ID_AV1) { if (q->low_delay_brc >= 0) q->extco3.LowDelayBRC = q->low_delay_brc ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF; + q->old_low_delay_brc = q->low_delay_brc; } if (avctx->codec_id == AV_CODEC_ID_HEVC) { @@ -1499,7 +1530,7 @@ static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q) } memset(avctx->extradata + avctx->extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); - cpb_props = ff_add_cpb_side_data(avctx); + cpb_props = ff_encode_add_cpb_side_data(avctx); if (!cpb_props) return AVERROR(ENOMEM); cpb_props->max_bitrate = avctx->rc_max_rate; @@ -1877,6 +1908,62 @@ static int qsvenc_fill_padding_area(AVFrame *frame, int new_w, int new_h) return 0; } +/* frame width / height have been aligned with the alignment */ +static int qsvenc_get_continuous_buffer(AVFrame *frame) +{ + int total_size; + + switch (frame->format) { + case AV_PIX_FMT_NV12: + frame->linesize[0] = frame->width; + frame->linesize[1] = frame->linesize[0]; + total_size = frame->linesize[0] * frame->height + frame->linesize[1] * frame->height / 2; + break; + + case AV_PIX_FMT_P010: + case AV_PIX_FMT_P012: + frame->linesize[0] = 2 * frame->width; + frame->linesize[1] = frame->linesize[0]; + total_size = frame->linesize[0] * frame->height + frame->linesize[1] * frame->height / 2; + break; + + case AV_PIX_FMT_YUYV422: + frame->linesize[0] = 2 * frame->width; + frame->linesize[1] = 0; + total_size = frame->linesize[0] * frame->height; + break; + + case AV_PIX_FMT_Y210: + case AV_PIX_FMT_VUYX: + case AV_PIX_FMT_XV30: + case AV_PIX_FMT_BGRA: + case AV_PIX_FMT_X2RGB10: + frame->linesize[0] = 4 * frame->width; + frame->linesize[1] = 0; + total_size = frame->linesize[0] * frame->height; + break; + + default: + // This should never be reached + av_assert0(0); + return AVERROR(EINVAL); + } + + frame->buf[0] = av_buffer_alloc(total_size); + if (!frame->buf[0]) + return AVERROR(ENOMEM); + + frame->data[0] = frame->buf[0]->data; + frame->extended_data = frame->data; + + if (frame->format == AV_PIX_FMT_NV12 || + frame->format == AV_PIX_FMT_P010 || + frame->format == AV_PIX_FMT_P012) + frame->data[1] = frame->data[0] + frame->linesize[0] * frame->height; + + return 0; +} + static int submit_frame(QSVEncContext *q, const AVFrame *frame, QSVFrame **new_frame) { @@ -1904,8 +1991,9 @@ static int submit_frame(QSVEncContext *q, const AVFrame *frame, } else { /* make a copy if the input is not padded as libmfx requires */ /* and to make allocation continious for data[0]/data[1] */ - if ((frame->height & 31 || frame->linesize[0] & (q->width_align - 1)) || - (frame->data[1] - frame->data[0] != frame->linesize[0] * FFALIGN(qf->frame->height, q->height_align))) { + if ((frame->height & (q->height_align - 1) || frame->linesize[0] & (q->width_align - 1)) || + ((frame->format == AV_PIX_FMT_NV12 || frame->format == AV_PIX_FMT_P010 || frame->format == AV_PIX_FMT_P012) && + (frame->data[1] - frame->data[0] != frame->linesize[0] * FFALIGN(qf->frame->height, q->height_align)))) { int tmp_w, tmp_h; qf->frame->height = tmp_h = FFALIGN(frame->height, q->height_align); qf->frame->width = tmp_w = FFALIGN(frame->width, q->width_align); @@ -1913,7 +2001,7 @@ static int submit_frame(QSVEncContext *q, const AVFrame *frame, qf->frame->format = frame->format; if (!qf->frame->data[0]) { - ret = av_frame_get_buffer(qf->frame, q->width_align); + ret = qsvenc_get_continuous_buffer(qf->frame); if (ret < 0) return ret; } @@ -1933,8 +2021,7 @@ static int submit_frame(QSVEncContext *q, const AVFrame *frame, return ret; } } else { - av_frame_unref(qf->frame); - ret = av_frame_ref(qf->frame, frame); + ret = av_frame_replace(qf->frame, frame); if (ret < 0) return ret; } @@ -1942,8 +2029,8 @@ static int submit_frame(QSVEncContext *q, const AVFrame *frame, qf->surface.Info = q->param.mfx.FrameInfo; qf->surface.Info.PicStruct = - !frame->interlaced_frame ? MFX_PICSTRUCT_PROGRESSIVE : - frame->top_field_first ? MFX_PICSTRUCT_FIELD_TFF : + !(frame->flags & AV_FRAME_FLAG_INTERLACED) ? MFX_PICSTRUCT_PROGRESSIVE : + (frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? MFX_PICSTRUCT_FIELD_TFF : MFX_PICSTRUCT_FIELD_BFF; if (frame->repeat_pict == 1) qf->surface.Info.PicStruct |= MFX_PICSTRUCT_FIELD_REPEATED; @@ -2209,7 +2296,9 @@ static int update_low_delay_brc(AVCodecContext *avctx, QSVEncContext *q) { int updated = 0; - if (avctx->codec_id != AV_CODEC_ID_H264 && avctx->codec_id != AV_CODEC_ID_HEVC) + if (avctx->codec_id != AV_CODEC_ID_H264 && + avctx->codec_id != AV_CODEC_ID_HEVC && + avctx->codec_id != AV_CODEC_ID_AV1) return 0; UPDATE_PARAM(q->old_low_delay_brc, q->low_delay_brc); @@ -2395,7 +2484,7 @@ static int encode_frame(AVCodecContext *avctx, QSVEncContext *q, goto free; } - if (ret == MFX_WRN_INCOMPATIBLE_VIDEO_PARAM && frame && frame->interlaced_frame) + if (ret == MFX_WRN_INCOMPATIBLE_VIDEO_PARAM && frame && (frame->flags & AV_FRAME_FLAG_INTERLACED)) print_interlace_msg(avctx, q); ret = 0; @@ -2514,13 +2603,15 @@ int ff_qsv_encode(AVCodecContext *avctx, QSVEncContext *q, if (qpkt.bs->FrameType & MFX_FRAMETYPE_IDR || qpkt.bs->FrameType & MFX_FRAMETYPE_xIDR) { qpkt.pkt.flags |= AV_PKT_FLAG_KEY; pict_type = AV_PICTURE_TYPE_I; - } else if (qpkt.bs->FrameType & MFX_FRAMETYPE_I || qpkt.bs->FrameType & MFX_FRAMETYPE_xI) + } else if (qpkt.bs->FrameType & MFX_FRAMETYPE_I || qpkt.bs->FrameType & MFX_FRAMETYPE_xI) { + if (avctx->codec_id == AV_CODEC_ID_VP9) + qpkt.pkt.flags |= AV_PKT_FLAG_KEY; pict_type = AV_PICTURE_TYPE_I; - else if (qpkt.bs->FrameType & MFX_FRAMETYPE_P || qpkt.bs->FrameType & MFX_FRAMETYPE_xP) + } else if (qpkt.bs->FrameType & MFX_FRAMETYPE_P || qpkt.bs->FrameType & MFX_FRAMETYPE_xP) pict_type = AV_PICTURE_TYPE_P; else if (qpkt.bs->FrameType & MFX_FRAMETYPE_B || qpkt.bs->FrameType & MFX_FRAMETYPE_xB) pict_type = AV_PICTURE_TYPE_B; - else if (qpkt.bs->FrameType == MFX_FRAMETYPE_UNKNOWN) { + else if (qpkt.bs->FrameType == MFX_FRAMETYPE_UNKNOWN && qpkt.bs->DataLength) { pict_type = AV_PICTURE_TYPE_NONE; av_log(avctx, AV_LOG_WARNING, "Unknown FrameType, set pict_type to AV_PICTURE_TYPE_NONE.\n"); } else { diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h index 4a6fa2caedc..c71bf2ed504 100644 --- a/libavcodec/qsvenc.h +++ b/libavcodec/qsvenc.h @@ -55,23 +55,23 @@ #define QSV_COMMON_OPTS \ { "async_depth", "Maximum processing parallelism", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 1, INT_MAX, VE }, \ -{ "preset", NULL, OFFSET(qsv.preset), AV_OPT_TYPE_INT, { .i64 = MFX_TARGETUSAGE_UNKNOWN }, MFX_TARGETUSAGE_UNKNOWN, MFX_TARGETUSAGE_BEST_SPEED, VE, "preset" }, \ -{ "veryfast", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_BEST_SPEED }, INT_MIN, INT_MAX, VE, "preset" }, \ -{ "faster", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_6 }, INT_MIN, INT_MAX, VE, "preset" }, \ -{ "fast", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_5 }, INT_MIN, INT_MAX, VE, "preset" }, \ -{ "medium", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_BALANCED }, INT_MIN, INT_MAX, VE, "preset" }, \ -{ "slow", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_3 }, INT_MIN, INT_MAX, VE, "preset" }, \ -{ "slower", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_2 }, INT_MIN, INT_MAX, VE, "preset" }, \ -{ "veryslow", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_BEST_QUALITY }, INT_MIN, INT_MAX, VE, "preset" }, \ +{ "preset", NULL, OFFSET(qsv.preset), AV_OPT_TYPE_INT, { .i64 = MFX_TARGETUSAGE_UNKNOWN }, MFX_TARGETUSAGE_UNKNOWN, MFX_TARGETUSAGE_BEST_SPEED, VE, .unit = "preset" }, \ +{ "veryfast", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_BEST_SPEED }, INT_MIN, INT_MAX, VE, .unit = "preset" }, \ +{ "faster", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_6 }, INT_MIN, INT_MAX, VE, .unit = "preset" }, \ +{ "fast", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_5 }, INT_MIN, INT_MAX, VE, .unit = "preset" }, \ +{ "medium", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_BALANCED }, INT_MIN, INT_MAX, VE, .unit = "preset" }, \ +{ "slow", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_3 }, INT_MIN, INT_MAX, VE, .unit = "preset" }, \ +{ "slower", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_2 }, INT_MIN, INT_MAX, VE, .unit = "preset" }, \ +{ "veryslow", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TARGETUSAGE_BEST_QUALITY }, INT_MIN, INT_MAX, VE, .unit = "preset" }, \ { "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" }, +{ "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, .unit = "dual_gfx" }, \ +{ "off", "Disable HyperEncode mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_HYPERMODE_OFF }, INT_MIN, INT_MAX, VE, .unit = "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, .unit = "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, .unit = "dual_gfx" }, #endif #define QSV_OPTION_RDO \ @@ -122,16 +122,16 @@ #define QSV_OPTION_SCENARIO \ { "scenario", "A hint to encoder about the scenario for the encoding session", OFFSET(qsv.scenario), AV_OPT_TYPE_INT, { .i64 = MFX_SCENARIO_UNKNOWN }, \ - MFX_SCENARIO_UNKNOWN, MFX_SCENARIO_REMOTE_GAMING, VE, "scenario" }, \ -{ "unknown", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_UNKNOWN }, .flags = VE, "scenario" }, \ -{ "displayremoting", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_DISPLAY_REMOTING }, .flags = VE, "scenario" }, \ -{ "videoconference", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_VIDEO_CONFERENCE }, .flags = VE, "scenario" }, \ -{ "archive", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_ARCHIVE }, .flags = VE, "scenario" }, \ -{ "livestreaming", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_LIVE_STREAMING }, .flags = VE, "scenario" }, \ -{ "cameracapture", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_CAMERA_CAPTURE }, .flags = VE, "scenario" }, \ -{ "videosurveillance", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_VIDEO_SURVEILLANCE }, .flags = VE, "scenario" }, \ -{ "gamestreaming", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_GAME_STREAMING }, .flags = VE, "scenario" }, \ -{ "remotegaming", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_REMOTE_GAMING }, .flags = VE, "scenario" }, + MFX_SCENARIO_UNKNOWN, MFX_SCENARIO_REMOTE_GAMING, VE, .unit = "scenario" }, \ +{ "unknown", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_UNKNOWN }, .flags = VE, .unit = "scenario" }, \ +{ "displayremoting", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_DISPLAY_REMOTING }, .flags = VE, .unit = "scenario" }, \ +{ "videoconference", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_VIDEO_CONFERENCE }, .flags = VE, .unit = "scenario" }, \ +{ "archive", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_ARCHIVE }, .flags = VE, .unit = "scenario" }, \ +{ "livestreaming", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_LIVE_STREAMING }, .flags = VE, .unit = "scenario" }, \ +{ "cameracapture", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_CAMERA_CAPTURE }, .flags = VE, .unit = "scenario" }, \ +{ "videosurveillance", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_VIDEO_SURVEILLANCE }, .flags = VE, .unit = "scenario" }, \ +{ "gamestreaming", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_GAME_STREAMING }, .flags = VE, .unit = "scenario" }, \ +{ "remotegaming", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCENARIO_REMOTE_GAMING }, .flags = VE, .unit = "scenario" }, #define QSV_OPTION_AVBR \ { "avbr_accuracy", "Accuracy of the AVBR ratecontrol (unit of tenth of percent)", OFFSET(qsv.avbr_accuracy), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, VE }, \ @@ -139,15 +139,15 @@ #define QSV_OPTION_SKIP_FRAME \ { "skip_frame", "Allow frame skipping", OFFSET(qsv.skip_frame), AV_OPT_TYPE_INT, { .i64 = MFX_SKIPFRAME_NO_SKIP }, \ - MFX_SKIPFRAME_NO_SKIP, MFX_SKIPFRAME_BRC_ONLY, VE, "skip_frame" }, \ + MFX_SKIPFRAME_NO_SKIP, MFX_SKIPFRAME_BRC_ONLY, VE, .unit = "skip_frame" }, \ { "no_skip", "Frame skipping is disabled", \ - 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SKIPFRAME_NO_SKIP }, .flags = VE, "skip_frame" }, \ + 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SKIPFRAME_NO_SKIP }, .flags = VE, .unit = "skip_frame" }, \ { "insert_dummy", "Encoder inserts into bitstream frame where all macroblocks are encoded as skipped", \ - 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SKIPFRAME_INSERT_DUMMY }, .flags = VE, "skip_frame" }, \ + 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SKIPFRAME_INSERT_DUMMY }, .flags = VE, .unit = "skip_frame" }, \ { "insert_nothing", "Encoder inserts nothing into bitstream", \ - 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SKIPFRAME_INSERT_NOTHING }, .flags = VE, "skip_frame" }, \ + 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SKIPFRAME_INSERT_NOTHING }, .flags = VE, .unit = "skip_frame" }, \ { "brc_only", "skip_frame metadata indicates the number of missed frames before the current frame", \ - 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SKIPFRAME_BRC_ONLY }, .flags = VE, "skip_frame" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SKIPFRAME_BRC_ONLY }, .flags = VE, .unit = "skip_frame" }, extern const AVCodecHWConfigInternal *const ff_qsv_enc_hw_configs[]; diff --git a/libavcodec/qsvenc_av1.c b/libavcodec/qsvenc_av1.c index c697845d7b5..33727bb07e5 100644 --- a/libavcodec/qsvenc_av1.c +++ b/libavcodec/qsvenc_av1.c @@ -112,9 +112,9 @@ static const AVOption options[] = { QSV_OPTION_EXTBRC QSV_OPTION_LOW_DELAY_BRC QSV_OPTION_MAX_FRAME_SIZE - { "profile", NULL, OFFSET(qsv.profile), AV_OPT_TYPE_INT, { .i64 = MFX_PROFILE_UNKNOWN }, 0, INT_MAX, VE, "profile" }, - { "unknown" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, VE, "profile" }, - { "main" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_AV1_MAIN }, INT_MIN, INT_MAX, VE, "profile" }, + { "profile", NULL, OFFSET(qsv.profile), AV_OPT_TYPE_INT, { .i64 = MFX_PROFILE_UNKNOWN }, 0, INT_MAX, VE, .unit = "profile" }, + { "unknown" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "main" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_AV1_MAIN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, { "tile_cols", "Number of columns for tiled encoding", OFFSET(qsv.tile_cols), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, VE }, { "tile_rows", "Number of rows for tiled encoding", OFFSET(qsv.tile_rows), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, UINT16_MAX, VE }, { "look_ahead_depth", "Depth of look ahead in number frames, available when extbrc option is enabled", OFFSET(qsv.look_ahead_depth), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, VE }, @@ -129,7 +129,7 @@ static const AVClass class = { }; static const FFCodecDefault qsv_enc_defaults[] = { - { "b", "1M" }, + { "b", "0" }, { "g", "-1" }, { "bf", "-1" }, { "refs", "0" }, diff --git a/libavcodec/qsvenc_h264.c b/libavcodec/qsvenc_h264.c index 071a9a79e96..53e4e744b28 100644 --- a/libavcodec/qsvenc_h264.c +++ b/libavcodec/qsvenc_h264.c @@ -133,36 +133,36 @@ static const AVOption options[] = { { "look_ahead", "Use VBR algorithm with look ahead", OFFSET(qsv.look_ahead), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "look_ahead_depth", "Depth of look ahead in number frames", OFFSET(qsv.look_ahead_depth), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, VE }, { "look_ahead_downsampling", "Downscaling factor for the frames saved for the lookahead analysis", OFFSET(qsv.look_ahead_downsampling), - AV_OPT_TYPE_INT, { .i64 = MFX_LOOKAHEAD_DS_UNKNOWN }, MFX_LOOKAHEAD_DS_UNKNOWN, MFX_LOOKAHEAD_DS_4x, VE, "look_ahead_downsampling" }, - { "unknown" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_LOOKAHEAD_DS_UNKNOWN }, INT_MIN, INT_MAX, VE, "look_ahead_downsampling" }, - { "auto" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_LOOKAHEAD_DS_UNKNOWN }, INT_MIN, INT_MAX, VE, "look_ahead_downsampling" }, - { "off" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_LOOKAHEAD_DS_OFF }, INT_MIN, INT_MAX, VE, "look_ahead_downsampling" }, - { "2x" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_LOOKAHEAD_DS_2x }, INT_MIN, INT_MAX, VE, "look_ahead_downsampling" }, - { "4x" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_LOOKAHEAD_DS_4x }, INT_MIN, INT_MAX, VE, "look_ahead_downsampling" }, - - { "int_ref_type", "Intra refresh type. B frames should be set to 0.", OFFSET(qsv.int_ref_type), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, UINT16_MAX, VE, "int_ref_type" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, .flags = VE, "int_ref_type" }, - { "vertical", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, .flags = VE, "int_ref_type" }, - { "horizontal", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, .flags = VE, "int_ref_type" }, - { "slice" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3 }, .flags = VE, "int_ref_type" }, + AV_OPT_TYPE_INT, { .i64 = MFX_LOOKAHEAD_DS_UNKNOWN }, MFX_LOOKAHEAD_DS_UNKNOWN, MFX_LOOKAHEAD_DS_4x, VE, .unit = "look_ahead_downsampling" }, + { "unknown" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_LOOKAHEAD_DS_UNKNOWN }, INT_MIN, INT_MAX, VE, .unit = "look_ahead_downsampling" }, + { "auto" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_LOOKAHEAD_DS_UNKNOWN }, INT_MIN, INT_MAX, VE, .unit = "look_ahead_downsampling" }, + { "off" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_LOOKAHEAD_DS_OFF }, INT_MIN, INT_MAX, VE, .unit = "look_ahead_downsampling" }, + { "2x" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_LOOKAHEAD_DS_2x }, INT_MIN, INT_MAX, VE, .unit = "look_ahead_downsampling" }, + { "4x" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_LOOKAHEAD_DS_4x }, INT_MIN, INT_MAX, VE, .unit = "look_ahead_downsampling" }, + + { "int_ref_type", "Intra refresh type. B frames should be set to 0.", OFFSET(qsv.int_ref_type), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, UINT16_MAX, VE, .unit = "int_ref_type" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, .flags = VE, .unit = "int_ref_type" }, + { "vertical", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, .flags = VE, .unit = "int_ref_type" }, + { "horizontal", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, .flags = VE, .unit = "int_ref_type" }, + { "slice" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3 }, .flags = VE, .unit = "int_ref_type" }, { "int_ref_cycle_size", "Number of frames in the intra refresh cycle", OFFSET(qsv.int_ref_cycle_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, UINT16_MAX, VE }, { "int_ref_qp_delta", "QP difference for the refresh MBs", OFFSET(qsv.int_ref_qp_delta), AV_OPT_TYPE_INT, { .i64 = INT16_MIN }, INT16_MIN, INT16_MAX, VE }, { "recovery_point_sei", "Insert recovery point SEI messages", OFFSET(qsv.recovery_point_sei), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE }, { "int_ref_cycle_dist", "Distance between the beginnings of the intra-refresh cycles in frames", OFFSET(qsv.int_ref_cycle_dist), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT16_MAX, VE }, - { "profile", NULL, OFFSET(qsv.profile), AV_OPT_TYPE_INT, { .i64 = MFX_PROFILE_UNKNOWN }, 0, INT_MAX, VE, "profile" }, - { "unknown" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, VE, "profile" }, - { "baseline", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_AVC_BASELINE }, INT_MIN, INT_MAX, VE, "profile" }, - { "main" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_AVC_MAIN }, INT_MIN, INT_MAX, VE, "profile" }, - { "high" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_AVC_HIGH }, INT_MIN, INT_MAX, VE, "profile" }, + { "profile", NULL, OFFSET(qsv.profile), AV_OPT_TYPE_INT, { .i64 = MFX_PROFILE_UNKNOWN }, 0, INT_MAX, VE, .unit = "profile" }, + { "unknown" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "baseline", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_AVC_BASELINE }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "main" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_AVC_MAIN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "high" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_AVC_HIGH }, INT_MIN, INT_MAX, VE, .unit = "profile" }, { "a53cc" , "Use A53 Closed Captions (if available)", OFFSET(qsv.a53_cc), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, VE}, { "aud", "Insert the Access Unit Delimiter NAL", OFFSET(qsv.aud), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE}, #if QSV_HAVE_MF - { "mfmode", "Multi-Frame Mode", OFFSET(qsv.mfmode), AV_OPT_TYPE_INT, { .i64 = MFX_MF_AUTO }, MFX_MF_DEFAULT, MFX_MF_AUTO, VE, "mfmode"}, - { "off" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_MF_DISABLED }, INT_MIN, INT_MAX, VE, "mfmode" }, - { "auto" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_MF_AUTO }, INT_MIN, INT_MAX, VE, "mfmode" }, + { "mfmode", "Multi-Frame Mode", OFFSET(qsv.mfmode), AV_OPT_TYPE_INT, { .i64 = MFX_MF_AUTO }, MFX_MF_DEFAULT, MFX_MF_AUTO, VE, .unit = "mfmode"}, + { "off" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_MF_DISABLED }, INT_MIN, INT_MAX, VE, .unit = "mfmode" }, + { "auto" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_MF_AUTO }, INT_MIN, INT_MAX, VE, .unit = "mfmode" }, #endif { "repeat_pps", "repeat pps for every frame", OFFSET(qsv.repeat_pps), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, @@ -178,7 +178,7 @@ static const AVClass class = { }; static const FFCodecDefault qsv_enc_defaults[] = { - { "b", "1M" }, + { "b", "0" }, { "refs", "0" }, { "g", "-1" }, { "bf", "-1" }, diff --git a/libavcodec/qsvenc_hevc.c b/libavcodec/qsvenc_hevc.c index 5e23ca9647c..4920b0bca45 100644 --- a/libavcodec/qsvenc_hevc.c +++ b/libavcodec/qsvenc_hevc.c @@ -322,29 +322,29 @@ static const AVOption options[] = { 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" }, - { "load_plugin", "A user plugin to load in an internal session", OFFSET(load_plugin), AV_OPT_TYPE_INT, { .i64 = LOAD_PLUGIN_HEVC_HW }, LOAD_PLUGIN_NONE, LOAD_PLUGIN_HEVC_HW, VE, "load_plugin" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_NONE }, 0, 0, VE, "load_plugin" }, - { "hevc_sw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_SW }, 0, 0, VE, "load_plugin" }, - { "hevc_hw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_HW }, 0, 0, VE, "load_plugin" }, + { "idr_interval", "Distance (in I-frames) between IDR frames", OFFSET(qsv.idr_interval), AV_OPT_TYPE_INT, { .i64 = 0 }, -1, INT_MAX, VE, .unit = "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, .unit = "idr_interval" }, + { "load_plugin", "A user plugin to load in an internal session", OFFSET(load_plugin), AV_OPT_TYPE_INT, { .i64 = LOAD_PLUGIN_HEVC_HW }, LOAD_PLUGIN_NONE, LOAD_PLUGIN_HEVC_HW, VE, .unit = "load_plugin" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_NONE }, 0, 0, VE, .unit = "load_plugin" }, + { "hevc_sw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_SW }, 0, 0, VE, .unit = "load_plugin" }, + { "hevc_hw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = LOAD_PLUGIN_HEVC_HW }, 0, 0, VE, .unit = "load_plugin" }, { "load_plugins", "A :-separate list of hexadecimal plugin UIDs to load in an internal session", OFFSET(qsv.load_plugins), AV_OPT_TYPE_STRING, { .str = "" }, 0, 0, VE }, { "look_ahead_depth", "Depth of look ahead in number frames, available when extbrc option is enabled", OFFSET(qsv.look_ahead_depth), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 100, VE }, - { "profile", NULL, OFFSET(qsv.profile), AV_OPT_TYPE_INT, { .i64 = MFX_PROFILE_UNKNOWN }, 0, INT_MAX, VE, "profile" }, - { "unknown", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, VE, "profile" }, - { "main", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_HEVC_MAIN }, INT_MIN, INT_MAX, VE, "profile" }, - { "main10", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_HEVC_MAIN10 }, INT_MIN, INT_MAX, VE, "profile" }, - { "mainsp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_HEVC_MAINSP }, INT_MIN, INT_MAX, VE, "profile" }, - { "rext", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_HEVC_REXT }, INT_MIN, INT_MAX, VE, "profile" }, + { "profile", NULL, OFFSET(qsv.profile), AV_OPT_TYPE_INT, { .i64 = MFX_PROFILE_UNKNOWN }, 0, INT_MAX, VE, .unit = "profile" }, + { "unknown", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "main", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_HEVC_MAIN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "main10", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_HEVC_MAIN10 }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "mainsp", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_HEVC_MAINSP }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "rext", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_HEVC_REXT }, INT_MIN, INT_MAX, VE, .unit = "profile" }, #if QSV_VERSION_ATLEAST(1, 32) - { "scc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_HEVC_SCC }, INT_MIN, INT_MAX, VE, "profile" }, + { "scc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_HEVC_SCC }, INT_MIN, INT_MAX, VE, .unit = "profile" }, #endif - { "tier", "Set the encoding tier (only level >= 4 can support high tier)", OFFSET(qsv.tier), AV_OPT_TYPE_INT, { .i64 = MFX_TIER_HEVC_HIGH }, MFX_TIER_HEVC_MAIN, MFX_TIER_HEVC_HIGH, VE, "tier" }, - { "main", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TIER_HEVC_MAIN }, INT_MIN, INT_MAX, VE, "tier" }, - { "high", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TIER_HEVC_HIGH }, INT_MIN, INT_MAX, VE, "tier" }, + { "tier", "Set the encoding tier (only level >= 4 can support high tier)", OFFSET(qsv.tier), AV_OPT_TYPE_INT, { .i64 = MFX_TIER_HEVC_HIGH }, MFX_TIER_HEVC_MAIN, MFX_TIER_HEVC_HIGH, VE, .unit = "tier" }, + { "main", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TIER_HEVC_MAIN }, INT_MIN, INT_MAX, VE, .unit = "tier" }, + { "high", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_TIER_HEVC_HIGH }, INT_MIN, INT_MAX, VE, .unit = "tier" }, { "gpb", "1: GPB (generalized P/B frame); 0: regular P frame", OFFSET(qsv.gpb), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE}, @@ -354,11 +354,11 @@ static const AVOption options[] = { { "aud", "Insert the Access Unit Delimiter NAL", OFFSET(qsv.aud), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE}, { "pic_timing_sei", "Insert picture timing SEI with pic_struct_syntax element", OFFSET(qsv.pic_timing_sei), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VE }, { "transform_skip", "Turn this option ON to enable transformskip", OFFSET(qsv.transform_skip), AV_OPT_TYPE_INT, { .i64 = -1}, -1, 1, VE}, - { "int_ref_type", "Intra refresh type. B frames should be set to 0", OFFSET(qsv.int_ref_type), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, UINT16_MAX, VE, "int_ref_type" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, .flags = VE, "int_ref_type" }, - { "vertical", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, .flags = VE, "int_ref_type" }, - { "horizontal", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, .flags = VE, "int_ref_type" }, - { "slice" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3 }, .flags = VE, "int_ref_type" }, + { "int_ref_type", "Intra refresh type. B frames should be set to 0", OFFSET(qsv.int_ref_type), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, UINT16_MAX, VE, .unit = "int_ref_type" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, .flags = VE, .unit = "int_ref_type" }, + { "vertical", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, .flags = VE, .unit = "int_ref_type" }, + { "horizontal", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, .flags = VE, .unit = "int_ref_type" }, + { "slice" , NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3 }, .flags = VE, .unit = "int_ref_type" }, { "int_ref_cycle_size", "Number of frames in the intra refresh cycle", OFFSET(qsv.int_ref_cycle_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, UINT16_MAX, VE }, { "int_ref_qp_delta", "QP difference for the refresh MBs", OFFSET(qsv.int_ref_qp_delta), AV_OPT_TYPE_INT, { .i64 = INT16_MIN }, INT16_MIN, INT16_MAX, VE }, { "int_ref_cycle_dist", "Distance between the beginnings of the intra-refresh cycles in frames", OFFSET(qsv.int_ref_cycle_dist), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT16_MAX, VE }, @@ -374,9 +374,9 @@ static const AVClass class = { }; static const FFCodecDefault qsv_enc_defaults[] = { - { "b", "1M" }, + { "b", "0" }, { "refs", "0" }, - { "g", "-1" }, + { "g", "248" }, { "bf", "-1" }, { "qmin", "-1" }, { "qmax", "-1" }, diff --git a/libavcodec/qsvenc_mpeg2.c b/libavcodec/qsvenc_mpeg2.c index 22f1ff7c0d9..fabf461fe32 100644 --- a/libavcodec/qsvenc_mpeg2.c +++ b/libavcodec/qsvenc_mpeg2.c @@ -65,11 +65,11 @@ static const AVOption options[] = { QSV_COMMON_OPTS QSV_OPTION_RDO - { "profile", NULL, OFFSET(qsv.profile), AV_OPT_TYPE_INT, { .i64 = MFX_PROFILE_UNKNOWN }, 0, INT_MAX, VE, "profile" }, - { "unknown", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, VE, "profile" }, - { "simple", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_MPEG2_SIMPLE }, INT_MIN, INT_MAX, VE, "profile" }, - { "main", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_MPEG2_MAIN }, INT_MIN, INT_MAX, VE, "profile" }, - { "high", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_MPEG2_HIGH }, INT_MIN, INT_MAX, VE, "profile" }, + { "profile", NULL, OFFSET(qsv.profile), AV_OPT_TYPE_INT, { .i64 = MFX_PROFILE_UNKNOWN }, 0, INT_MAX, VE, .unit = "profile" }, + { "unknown", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "simple", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_MPEG2_SIMPLE }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "main", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_MPEG2_MAIN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "high", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_MPEG2_HIGH }, INT_MIN, INT_MAX, VE, .unit = "profile" }, { NULL }, }; @@ -82,7 +82,7 @@ static const AVClass class = { }; static const FFCodecDefault qsv_enc_defaults[] = { - { "b", "1M" }, + { "b", "0" }, { "refs", "0" }, // same as the x264 default { "g", "250" }, diff --git a/libavcodec/qsvenc_vp9.c b/libavcodec/qsvenc_vp9.c index d0340ef94b2..a760e4932e5 100644 --- a/libavcodec/qsvenc_vp9.c +++ b/libavcodec/qsvenc_vp9.c @@ -65,12 +65,12 @@ static av_cold int qsv_enc_close(AVCodecContext *avctx) static const AVOption options[] = { QSV_COMMON_OPTS - { "profile", NULL, OFFSET(qsv.profile), AV_OPT_TYPE_INT, { .i64 = MFX_PROFILE_UNKNOWN }, 0, INT_MAX, VE, "profile" }, - { "unknown", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_UNKNOWN}, INT_MIN, INT_MAX, VE, "profile" }, - { "profile0", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_VP9_0 }, INT_MIN, INT_MAX, VE, "profile" }, - { "profile1", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_VP9_1 }, INT_MIN, INT_MAX, VE, "profile" }, - { "profile2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_VP9_2 }, INT_MIN, INT_MAX, VE, "profile" }, - { "profile3", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_VP9_3 }, INT_MIN, INT_MAX, VE, "profile" }, + { "profile", NULL, OFFSET(qsv.profile), AV_OPT_TYPE_INT, { .i64 = MFX_PROFILE_UNKNOWN }, 0, INT_MAX, VE, .unit = "profile" }, + { "unknown", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_UNKNOWN}, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "profile0", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_VP9_0 }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "profile1", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_VP9_1 }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "profile2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_VP9_2 }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "profile3", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = MFX_PROFILE_VP9_3 }, INT_MIN, INT_MAX, VE, .unit = "profile" }, #if QSV_HAVE_EXT_VP9_TILES /* The minimum tile width in luma pixels is 256, set maximum tile_cols to 32 for 8K video */ @@ -93,7 +93,7 @@ static const AVClass class = { }; static const FFCodecDefault qsv_enc_defaults[] = { - { "b", "1M" }, + { "b", "0" }, { "refs", "0" }, { "g", "250" }, { "trellis", "-1" }, diff --git a/libavcodec/qtrle.c b/libavcodec/qtrle.c index 5cb18c86c2b..9b016d7e83e 100644 --- a/libavcodec/qtrle.c +++ b/libavcodec/qtrle.c @@ -537,7 +537,14 @@ static int qtrle_decode_frame(AVCodecContext *avctx, AVFrame *rframe, } if(has_palette) { - s->frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + s->frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif /* make the palette available on the way out */ memcpy(s->frame->data[1], s->pal, AVPALETTE_SIZE); diff --git a/libavcodec/qtrleenc.c b/libavcodec/qtrleenc.c index 3846762745a..92e6e84380b 100644 --- a/libavcodec/qtrleenc.c +++ b/libavcodec/qtrleenc.c @@ -385,8 +385,7 @@ static int qtrle_encode_frame(AVCodecContext *avctx, AVPacket *pkt, pkt->size = encode_frame(s, pict, pkt->data); /* save the current frame */ - av_frame_unref(s->previous_frame); - ret = av_frame_ref(s->previous_frame, pict); + ret = av_frame_replace(s->previous_frame, pict); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "cannot add reference\n"); return ret; diff --git a/libavcodec/r210dec.c b/libavcodec/r210dec.c index ae80f46eb67..fe6a025988e 100644 --- a/libavcodec/r210dec.c +++ b/libavcodec/r210dec.c @@ -57,7 +57,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; pic->pict_type = AV_PICTURE_TYPE_I; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; g_line = pic->data[0]; b_line = pic->data[1]; r_line = pic->data[2]; diff --git a/libavcodec/r210enc.c b/libavcodec/r210enc.c index 91e34528741..ec1ebc8d609 100644 --- a/libavcodec/r210enc.c +++ b/libavcodec/r210enc.c @@ -35,7 +35,7 @@ static av_cold int encode_init(AVCodecContext *avctx) avctx->bits_per_coded_sample = 32; if (avctx->width > 0) - avctx->bit_rate = ff_guess_coded_bitrate(avctx) * aligned_width / avctx->width; + avctx->bit_rate = av_rescale(ff_guess_coded_bitrate(avctx), aligned_width, avctx->width); return 0; } diff --git a/libavcodec/ra144enc.c b/libavcodec/ra144enc.c index ea537f3f80d..92c35ac12c8 100644 --- a/libavcodec/ra144enc.c +++ b/libavcodec/ra144enc.c @@ -549,6 +549,5 @@ const FFCodec ff_ra_144_encoder = { .p.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }, .p.supported_samplerates = (const int[]){ 8000, 0 }, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO) .p.ch_layouts = (const AVChannelLayout[]){ AV_CHANNEL_LAYOUT_MONO, { 0 } }, }; diff --git a/libavcodec/ra288.c b/libavcodec/ra288.c index c8c20e48848..e4b14ef2a2b 100644 --- a/libavcodec/ra288.c +++ b/libavcodec/ra288.c @@ -30,7 +30,7 @@ #include "codec_internal.h" #include "decode.h" #include "get_bits.h" -#include "lpc.h" +#include "lpc_functions.h" #include "ra288.h" #define MAX_BACKWARD_FILTER_ORDER 36 diff --git a/libavcodec/ralf.c b/libavcodec/ralf.c index 591598d8fcb..8f7fac79359 100644 --- a/libavcodec/ralf.c +++ b/libavcodec/ralf.c @@ -99,7 +99,7 @@ static av_cold int init_ralf_vlc(VLC *vlc, const uint8_t *data, int elems) for (i = 0; i < elems; i++) codes[i] = prefixes[lens[i]]++; - return ff_init_vlc_sparse(vlc, FFMIN(max_bits, 9), elems, + return ff_vlc_init_sparse(vlc, FFMIN(max_bits, 9), elems, lens, 1, 1, codes, 2, 2, NULL, 0, 0, 0); } @@ -109,16 +109,16 @@ static av_cold int decode_close(AVCodecContext *avctx) int i, j, k; for (i = 0; i < 3; i++) { - ff_free_vlc(&ctx->sets[i].filter_params); - ff_free_vlc(&ctx->sets[i].bias); - ff_free_vlc(&ctx->sets[i].coding_mode); + ff_vlc_free(&ctx->sets[i].filter_params); + ff_vlc_free(&ctx->sets[i].bias); + ff_vlc_free(&ctx->sets[i].coding_mode); for (j = 0; j < 10; j++) for (k = 0; k < 11; k++) - ff_free_vlc(&ctx->sets[i].filter_coeffs[j][k]); + ff_vlc_free(&ctx->sets[i].filter_coeffs[j][k]); for (j = 0; j < 15; j++) - ff_free_vlc(&ctx->sets[i].short_codes[j]); + ff_vlc_free(&ctx->sets[i].short_codes[j]); for (j = 0; j < 125; j++) - ff_free_vlc(&ctx->sets[i].long_codes[j]); + ff_vlc_free(&ctx->sets[i].long_codes[j]); } return 0; diff --git a/libavcodec/rasc.c b/libavcodec/rasc.c index cfa3d6b079d..21c1829fc7e 100644 --- a/libavcodec/rasc.c +++ b/libavcodec/rasc.c @@ -379,8 +379,8 @@ static int decode_dlta(AVCodecContext *avctx, if (!s->frame2->data[0] || !s->frame1->data[0]) return AVERROR_INVALIDDATA; - b1 = s->frame1->data[0] + s->frame1->linesize[0] * (y + h - 1) + x * s->bpp; - b2 = s->frame2->data[0] + s->frame2->linesize[0] * (y + h - 1) + x * s->bpp; + b1 = s->frame1->data[0] + s->frame1->linesize[0] * (int)(y + h - 1) + ((int)x) * s->bpp; + b2 = s->frame2->data[0] + s->frame2->linesize[0] * (int)(y + h - 1) + ((int)x) * s->bpp; cx = 0, cy = h; while (bytestream2_get_bytes_left(&dc) > 0) { int type = bytestream2_get_byte(&dc); @@ -620,7 +620,7 @@ static void draw_cursor(AVCodecContext *avctx) if (cr == s->cursor[0] && cg == s->cursor[1] && cb == s->cursor[2]) continue; - dst = s->frame->data[0] + s->frame->linesize[0] * (s->cursor_y + i) + (s->cursor_x + j); + dst = s->frame->data[0] + s->frame->linesize[0] * (int)(s->cursor_y + i) + (int)(s->cursor_x + j); for (int k = 0; k < 256; k++) { int pr = pal[k * 4 + 0]; int pg = pal[k * 4 + 1]; @@ -646,7 +646,7 @@ static void draw_cursor(AVCodecContext *avctx) continue; cr >>= 3; cg >>=3; cb >>= 3; - dst = s->frame->data[0] + s->frame->linesize[0] * (s->cursor_y + i) + 2 * (s->cursor_x + j); + dst = s->frame->data[0] + s->frame->linesize[0] * (int)(s->cursor_y + i) + 2 * (s->cursor_x + j); AV_WL16(dst, cr | cg << 5 | cb << 10); } } @@ -660,7 +660,7 @@ static void draw_cursor(AVCodecContext *avctx) if (cr == s->cursor[0] && cg == s->cursor[1] && cb == s->cursor[2]) continue; - dst = s->frame->data[0] + s->frame->linesize[0] * (s->cursor_y + i) + 4 * (s->cursor_x + j); + dst = s->frame->data[0] + s->frame->linesize[0] * (int)(s->cursor_y + i) + 4 * (s->cursor_x + j); dst[0] = cb; dst[1] = cg; dst[2] = cr; @@ -740,7 +740,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (!s->skip_cursor) draw_cursor(avctx); - s->frame->key_frame = intra; + if (intra) + s->frame->flags |= AV_FRAME_FLAG_KEY; + else + s->frame->flags &= ~AV_FRAME_FLAG_KEY; s->frame->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; *got_frame = 1; diff --git a/libavcodec/ratecontrol.c b/libavcodec/ratecontrol.c index 6a40f9cbdc7..55ce054735b 100644 --- a/libavcodec/ratecontrol.c +++ b/libavcodec/ratecontrol.c @@ -26,6 +26,7 @@ */ #include "libavutil/attributes.h" +#include "libavutil/emms.h" #include "libavutil/internal.h" #include "avcodec.h" @@ -57,7 +58,16 @@ void ff_write_pass1_stats(MpegEncContext *s) static double get_fps(AVCodecContext *avctx) { - return 1.0 / av_q2d(avctx->time_base) / FFMAX(avctx->ticks_per_frame, 1); + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) + return av_q2d(avctx->framerate); + +FF_DISABLE_DEPRECATION_WARNINGS + return 1.0 / av_q2d(avctx->time_base) +#if FF_API_TICKS_PER_FRAME + / FFMAX(avctx->ticks_per_frame, 1) +#endif + ; +FF_ENABLE_DEPRECATION_WARNINGS } static inline double qp2bits(RateControlEntry *rce, double qp) diff --git a/libavcodec/raw.c b/libavcodec/raw.c index 1e5b48d1e06..b73b80e5fdb 100644 --- a/libavcodec/raw.c +++ b/libavcodec/raw.c @@ -206,6 +206,8 @@ static const PixelFormatTag raw_pix_fmt_tags[] = { { AV_PIX_FMT_GBRAP10BE, MKTAG(10 , 00 , '4', 'G') }, { AV_PIX_FMT_GBRAP12LE, MKTAG('G', '4', 00 , 12 ) }, { AV_PIX_FMT_GBRAP12BE, MKTAG(12 , 00 , '4', 'G') }, + { AV_PIX_FMT_GBRAP14LE, MKTAG('G', '4', 00 , 14 ) }, + { AV_PIX_FMT_GBRAP14BE, MKTAG(14 , 00 , '4', 'G') }, { AV_PIX_FMT_GBRAP16LE, MKTAG('G', '4', 00 , 16 ) }, { AV_PIX_FMT_GBRAP16BE, MKTAG(16 , 00 , '4', 'G') }, diff --git a/libavcodec/rawdec.c b/libavcodec/rawdec.c index c20c317fed8..8e9358f95d1 100644 --- a/libavcodec/rawdec.c +++ b/libavcodec/rawdec.c @@ -227,15 +227,16 @@ static int raw_decode(AVCodecContext *avctx, AVFrame *frame, need_copy = !avpkt->buf || context->is_1_2_4_8_bpp || context->is_yuv2 || context->is_lt_16bpp; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; res = ff_decode_frame_props(avctx, frame); if (res < 0) return res; if (context->tff >= 0) { - frame->interlaced_frame = 1; - frame->top_field_first = context->tff; + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (context->tff == 1) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } if ((res = av_image_check_size(avctx->width, avctx->height, 0, avctx)) < 0) @@ -372,7 +373,11 @@ static int raw_decode(AVCodecContext *avctx, AVFrame *frame, } if (ff_copy_palette(context->palette->data, avpkt, avctx)) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else if (context->is_nut_pal8) { int vid_size = avctx->width * avctx->height; int pal_size = avpkt->size - vid_size; @@ -380,7 +385,11 @@ static int raw_decode(AVCodecContext *avctx, AVFrame *frame, if (avpkt->size > vid_size && pal_size <= AVPALETTE_SIZE) { const uint8_t *pal = avpkt->data + vid_size; memcpy(context->palette->data, pal, pal_size); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } } } @@ -459,9 +468,9 @@ static int raw_decode(AVCodecContext *avctx, AVFrame *frame, } if (avctx->field_order > AV_FIELD_PROGRESSIVE) { /* we have interlaced material flagged in container */ - frame->interlaced_frame = 1; + frame->flags |= AV_FRAME_FLAG_INTERLACED; if (avctx->field_order == AV_FIELD_TT || avctx->field_order == AV_FIELD_TB) - frame->top_field_first = 1; + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } *got_frame = 1; diff --git a/libavcodec/rdft.c b/libavcodec/rdft.c deleted file mode 100644 index ac6f5d67811..00000000000 --- a/libavcodec/rdft.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * (I)RDFT transforms - * Copyright (c) 2009 Alex Converse - * - * 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 -#include -#include "libavutil/error.h" -#include "libavutil/mathematics.h" -#include "rdft.h" - -/** - * @file - * (Inverse) Real Discrete Fourier Transforms. - */ - -/** Map one real FFT into two parallel real even and odd FFTs. Then interleave - * the two real FFTs into one complex FFT. Unmangle the results. - * ref: http://www.engineeringproductivitytools.com/stuff/T0001/PT10.HTM - */ -static void rdft_calc_c(RDFTContext *s, FFTSample *data) -{ - int i, i1, i2; - FFTComplex ev, od, odsum; - const int n = 1 << s->nbits; - const float k1 = 0.5; - const float k2 = 0.5 - s->inverse; - const FFTSample *tcos = s->tcos; - const FFTSample *tsin = s->tsin; - - if (!s->inverse) { - s->fft.fft_permute(&s->fft, (FFTComplex*)data); - s->fft.fft_calc(&s->fft, (FFTComplex*)data); - } - /* i=0 is a special case because of packing, the DC term is real, so we - are going to throw the N/2 term (also real) in with it. */ - ev.re = data[0]; - data[0] = ev.re+data[1]; - data[1] = ev.re-data[1]; - -#define RDFT_UNMANGLE(sign0, sign1) \ - for (i = 1; i < (n>>2); i++) { \ - i1 = 2*i; \ - i2 = n-i1; \ - /* Separate even and odd FFTs */ \ - ev.re = k1*(data[i1 ]+data[i2 ]); \ - od.im = k2*(data[i2 ]-data[i1 ]); \ - ev.im = k1*(data[i1+1]-data[i2+1]); \ - od.re = k2*(data[i1+1]+data[i2+1]); \ - /* Apply twiddle factors to the odd FFT and add to the even FFT */ \ - odsum.re = od.re*tcos[i] sign0 od.im*tsin[i]; \ - odsum.im = od.im*tcos[i] sign1 od.re*tsin[i]; \ - data[i1 ] = ev.re + odsum.re; \ - data[i1+1] = ev.im + odsum.im; \ - data[i2 ] = ev.re - odsum.re; \ - data[i2+1] = odsum.im - ev.im; \ - } - - if (s->negative_sin) { - RDFT_UNMANGLE(+,-) - } else { - RDFT_UNMANGLE(-,+) - } - - data[2*i+1]=s->sign_convention*data[2*i+1]; - if (s->inverse) { - data[0] *= k1; - data[1] *= k1; - s->fft.fft_permute(&s->fft, (FFTComplex*)data); - s->fft.fft_calc(&s->fft, (FFTComplex*)data); - } -} - -av_cold int ff_rdft_init(RDFTContext *s, int nbits, enum RDFTransformType trans) -{ - int n = 1 << nbits; - int ret; - - s->nbits = nbits; - s->inverse = trans == IDFT_C2R || trans == DFT_C2R; - s->sign_convention = trans == IDFT_R2C || trans == DFT_C2R ? 1 : -1; - s->negative_sin = trans == DFT_C2R || trans == DFT_R2C; - - if (nbits < 4 || nbits > 16) - return AVERROR(EINVAL); - - if ((ret = ff_fft_init(&s->fft, nbits-1, trans == IDFT_C2R || trans == IDFT_R2C)) < 0) - return ret; - - ff_init_ff_cos_tabs(nbits); - s->tcos = ff_cos_tabs[nbits]; - s->tsin = ff_cos_tabs[nbits] + (n >> 2); - s->rdft_calc = rdft_calc_c; - -#if ARCH_ARM - ff_rdft_init_arm(s); -#endif - - return 0; -} - -av_cold void ff_rdft_end(RDFTContext *s) -{ - ff_fft_end(&s->fft); -} diff --git a/libavcodec/rdft.h b/libavcodec/rdft.h deleted file mode 100644 index ffafca7f242..00000000000 --- a/libavcodec/rdft.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * (I)RDFT transforms - * Copyright (c) 2009 Alex Converse - * - * 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 - */ - -#if !defined(AVCODEC_RDFT_H) && (!defined(FFT_FLOAT) || FFT_FLOAT) -#define AVCODEC_RDFT_H - -#include "config.h" -#include "fft.h" - -struct RDFTContext { - int nbits; - int inverse; - int sign_convention; - - /* pre/post rotation tables */ - const FFTSample *tcos; - const FFTSample *tsin; - int negative_sin; - FFTContext fft; - void (*rdft_calc)(struct RDFTContext *s, FFTSample *z); -}; - -/** - * Set up a real FFT. - * @param nbits log2 of the length of the input array - * @param trans the type of transform - */ -int ff_rdft_init(RDFTContext *s, int nbits, enum RDFTransformType trans); -void ff_rdft_end(RDFTContext *s); - -void ff_rdft_init_arm(RDFTContext *s); - - -#endif /* AVCODEC_RDFT_H */ diff --git a/libavcodec/refstruct.c b/libavcodec/refstruct.c new file mode 100644 index 00000000000..f89af156c2f --- /dev/null +++ b/libavcodec/refstruct.c @@ -0,0 +1,386 @@ +/* + * 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 +#include +#include + +#include "refstruct.h" + +#include "libavutil/avassert.h" +#include "libavutil/error.h" +#include "libavutil/macros.h" +#include "libavutil/mem.h" +#include "libavutil/mem_internal.h" +#include "libavutil/thread.h" + +#ifndef REFSTRUCT_CHECKED +#ifndef ASSERT_LEVEL +#define ASSERT_LEVEL 0 +#endif +#define REFSTRUCT_CHECKED (ASSERT_LEVEL >= 1) +#endif + +#if REFSTRUCT_CHECKED +#define ff_assert(cond) av_assert0(cond) +#else +#define ff_assert(cond) ((void)0) +#endif + +#define REFSTRUCT_COOKIE AV_NE((uint64_t)MKBETAG('R', 'e', 'f', 'S') << 32 | MKBETAG('t', 'r', 'u', 'c'), \ + MKTAG('R', 'e', 'f', 'S') | (uint64_t)MKTAG('t', 'r', 'u', 'c') << 32) + +#if __STDC_VERSION__ >= 201112L && !defined(_MSC_VER) +#define REFCOUNT_OFFSET FFALIGN(sizeof(RefCount), FFMAX(ALIGN_64, _Alignof(max_align_t))) +#else +#define REFCOUNT_OFFSET FFALIGN(sizeof(RefCount), ALIGN_64) +#endif + +typedef struct RefCount { + /** + * An uintptr_t is big enough to hold the address of every reference, + * so no overflow can happen when incrementing the refcount as long as + * the user does not throw away references. + */ + atomic_uintptr_t refcount; + FFRefStructOpaque opaque; + void (*free_cb)(FFRefStructOpaque opaque, void *obj); + void (*free)(void *ref); + +#if REFSTRUCT_CHECKED + uint64_t cookie; +#endif +} RefCount; + +static RefCount *get_refcount(void *obj) +{ + RefCount *ref = (RefCount*)((char*)obj - REFCOUNT_OFFSET); + ff_assert(ref->cookie == REFSTRUCT_COOKIE); + return ref; +} + +static const RefCount *cget_refcount(const void *obj) +{ + const RefCount *ref = (const RefCount*)((const char*)obj - REFCOUNT_OFFSET); + ff_assert(ref->cookie == REFSTRUCT_COOKIE); + return ref; +} + +static void *get_userdata(void *buf) +{ + return (char*)buf + REFCOUNT_OFFSET; +} + +static void refcount_init(RefCount *ref, FFRefStructOpaque opaque, + void (*free_cb)(FFRefStructOpaque opaque, void *obj)) +{ + atomic_init(&ref->refcount, 1); + ref->opaque = opaque; + ref->free_cb = free_cb; + ref->free = av_free; + +#if REFSTRUCT_CHECKED + ref->cookie = REFSTRUCT_COOKIE; +#endif +} + +void *ff_refstruct_alloc_ext_c(size_t size, unsigned flags, FFRefStructOpaque opaque, + void (*free_cb)(FFRefStructOpaque opaque, void *obj)) +{ + void *buf, *obj; + + if (size > SIZE_MAX - REFCOUNT_OFFSET) + return NULL; + buf = av_malloc(size + REFCOUNT_OFFSET); + if (!buf) + return NULL; + refcount_init(buf, opaque, free_cb); + obj = get_userdata(buf); + if (!(flags & FF_REFSTRUCT_FLAG_NO_ZEROING)) + memset(obj, 0, size); + + return obj; +} + +void ff_refstruct_unref(void *objp) +{ + void *obj; + RefCount *ref; + + memcpy(&obj, objp, sizeof(obj)); + if (!obj) + return; + memcpy(objp, &(void *){ NULL }, sizeof(obj)); + + ref = get_refcount(obj); + if (atomic_fetch_sub_explicit(&ref->refcount, 1, memory_order_acq_rel) == 1) { + if (ref->free_cb) + ref->free_cb(ref->opaque, obj); + ref->free(ref); + } + + return; +} + +void *ff_refstruct_ref(void *obj) +{ + RefCount *ref = get_refcount(obj); + + atomic_fetch_add_explicit(&ref->refcount, 1, memory_order_relaxed); + + return obj; +} + +const void *ff_refstruct_ref_c(const void *obj) +{ + /* Casting const away here is fine, as it is only supposed + * to apply to the user's data and not our bookkeeping data. */ + RefCount *ref = get_refcount((void*)obj); + + atomic_fetch_add_explicit(&ref->refcount, 1, memory_order_relaxed); + + return obj; +} + +void ff_refstruct_replace(void *dstp, const void *src) +{ + const void *dst; + memcpy(&dst, dstp, sizeof(dst)); + + if (src == dst) + return; + ff_refstruct_unref(dstp); + if (src) { + dst = ff_refstruct_ref_c(src); + memcpy(dstp, &dst, sizeof(dst)); + } +} + +int ff_refstruct_exclusive(const void *obj) +{ + const RefCount *ref = cget_refcount(obj); + /* Casting const away here is safe, because it is a load. + * It is necessary because atomic_load_explicit() does not + * accept const atomics in C11 (see also N1807). */ + return atomic_load_explicit((atomic_uintptr_t*)&ref->refcount, memory_order_acquire) == 1; +} + +struct FFRefStructPool { + size_t size; + FFRefStructOpaque opaque; + int (*init_cb)(FFRefStructOpaque opaque, void *obj); + void (*reset_cb)(FFRefStructOpaque opaque, void *obj); + void (*free_entry_cb)(FFRefStructOpaque opaque, void *obj); + void (*free_cb)(FFRefStructOpaque opaque); + + int uninited; + unsigned entry_flags; + unsigned pool_flags; + + /** The number of outstanding entries not in available_entries. */ + atomic_uintptr_t refcount; + /** + * This is a linked list of available entries; + * the RefCount's opaque pointer is used as next pointer + * for available entries. + * While the entries are in use, the opaque is a pointer + * to the corresponding FFRefStructPool. + */ + RefCount *available_entries; + AVMutex mutex; +}; + +static void pool_free(FFRefStructPool *pool) +{ + ff_mutex_destroy(&pool->mutex); + if (pool->free_cb) + pool->free_cb(pool->opaque); + av_free(get_refcount(pool)); +} + +static void pool_free_entry(FFRefStructPool *pool, RefCount *ref) +{ + if (pool->free_entry_cb) + pool->free_entry_cb(pool->opaque, get_userdata(ref)); + av_free(ref); +} + +static void pool_return_entry(void *ref_) +{ + RefCount *ref = ref_; + FFRefStructPool *pool = ref->opaque.nc; + + ff_mutex_lock(&pool->mutex); + if (!pool->uninited) { + ref->opaque.nc = pool->available_entries; + pool->available_entries = ref; + ref = NULL; + } + ff_mutex_unlock(&pool->mutex); + + if (ref) + pool_free_entry(pool, ref); + + if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1) + pool_free(pool); +} + +static void pool_reset_entry(FFRefStructOpaque opaque, void *entry) +{ + FFRefStructPool *pool = opaque.nc; + + pool->reset_cb(pool->opaque, entry); +} + +static int refstruct_pool_get_ext(void *datap, FFRefStructPool *pool) +{ + void *ret = NULL; + + memcpy(datap, &(void *){ NULL }, sizeof(void*)); + + ff_mutex_lock(&pool->mutex); + ff_assert(!pool->uninited); + if (pool->available_entries) { + RefCount *ref = pool->available_entries; + ret = get_userdata(ref); + pool->available_entries = ref->opaque.nc; + ref->opaque.nc = pool; + atomic_init(&ref->refcount, 1); + } + ff_mutex_unlock(&pool->mutex); + + if (!ret) { + RefCount *ref; + ret = ff_refstruct_alloc_ext(pool->size, pool->entry_flags, pool, + pool->reset_cb ? pool_reset_entry : NULL); + if (!ret) + return AVERROR(ENOMEM); + ref = get_refcount(ret); + ref->free = pool_return_entry; + if (pool->init_cb) { + int err = pool->init_cb(pool->opaque, ret); + if (err < 0) { + if (pool->pool_flags & FF_REFSTRUCT_POOL_FLAG_RESET_ON_INIT_ERROR) + pool->reset_cb(pool->opaque, ret); + if (pool->pool_flags & FF_REFSTRUCT_POOL_FLAG_FREE_ON_INIT_ERROR) + pool->free_entry_cb(pool->opaque, ret); + av_free(ref); + return err; + } + } + } + atomic_fetch_add_explicit(&pool->refcount, 1, memory_order_relaxed); + + if (pool->pool_flags & FF_REFSTRUCT_POOL_FLAG_ZERO_EVERY_TIME) + memset(ret, 0, pool->size); + + memcpy(datap, &ret, sizeof(ret)); + + return 0; +} + +void *ff_refstruct_pool_get(FFRefStructPool *pool) +{ + void *ret; + refstruct_pool_get_ext(&ret, pool); + return ret; +} + +/** + * Hint: The content of pool_unref() and refstruct_pool_uninit() + * could currently be merged; they are only separate functions + * in case we would ever introduce weak references. + */ +static void pool_unref(void *ref) +{ + FFRefStructPool *pool = get_userdata(ref); + if (atomic_fetch_sub_explicit(&pool->refcount, 1, memory_order_acq_rel) == 1) + pool_free(pool); +} + +static void refstruct_pool_uninit(FFRefStructOpaque unused, void *obj) +{ + FFRefStructPool *pool = obj; + RefCount *entry; + + ff_mutex_lock(&pool->mutex); + ff_assert(!pool->uninited); + pool->uninited = 1; + entry = pool->available_entries; + pool->available_entries = NULL; + ff_mutex_unlock(&pool->mutex); + + while (entry) { + void *next = entry->opaque.nc; + pool_free_entry(pool, entry); + entry = next; + } +} + +FFRefStructPool *ff_refstruct_pool_alloc(size_t size, unsigned flags) +{ + return ff_refstruct_pool_alloc_ext(size, flags, NULL, NULL, NULL, NULL, NULL); +} + +FFRefStructPool *ff_refstruct_pool_alloc_ext_c(size_t size, unsigned flags, + FFRefStructOpaque opaque, + int (*init_cb)(FFRefStructOpaque opaque, void *obj), + void (*reset_cb)(FFRefStructOpaque opaque, void *obj), + void (*free_entry_cb)(FFRefStructOpaque opaque, void *obj), + void (*free_cb)(FFRefStructOpaque opaque)) +{ + FFRefStructPool *pool = ff_refstruct_alloc_ext(sizeof(*pool), 0, NULL, + refstruct_pool_uninit); + int err; + + if (!pool) + return NULL; + get_refcount(pool)->free = pool_unref; + + pool->size = size; + pool->opaque = opaque; + pool->init_cb = init_cb; + pool->reset_cb = reset_cb; + pool->free_entry_cb = free_entry_cb; + pool->free_cb = free_cb; +#define COMMON_FLAGS FF_REFSTRUCT_POOL_FLAG_NO_ZEROING + pool->entry_flags = flags & COMMON_FLAGS; + // Filter out nonsense combinations to avoid checks later. + if (!pool->reset_cb) + flags &= ~FF_REFSTRUCT_POOL_FLAG_RESET_ON_INIT_ERROR; + if (!pool->free_entry_cb) + flags &= ~FF_REFSTRUCT_POOL_FLAG_FREE_ON_INIT_ERROR; + pool->pool_flags = flags; + + if (flags & FF_REFSTRUCT_POOL_FLAG_ZERO_EVERY_TIME) { + // We will zero the buffer before every use, so zeroing + // upon allocating the buffer is unnecessary. + pool->entry_flags |= FF_REFSTRUCT_FLAG_NO_ZEROING; + } + + atomic_init(&pool->refcount, 1); + + err = ff_mutex_init(&pool->mutex, NULL); + if (err) { + // Don't call ff_refstruct_uninit() on pool, as it hasn't been properly + // set up and is just a POD right now. + av_free(get_refcount(pool)); + return NULL; + } + return pool; +} diff --git a/libavcodec/refstruct.h b/libavcodec/refstruct.h new file mode 100644 index 00000000000..c64ad62b6b1 --- /dev/null +++ b/libavcodec/refstruct.h @@ -0,0 +1,297 @@ +/* + * 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 + */ + +#ifndef AVCODEC_REFSTRUCT_H +#define AVCODEC_REFSTRUCT_H + +#include + +/** + * RefStruct is an API for creating reference-counted objects + * with minimal overhead. The API is designed for objects, + * not buffers like the AVBuffer API. The main differences + * to the AVBuffer API are as follows: + * + * - It uses void* instead of uint8_t* as its base type due to + * its focus on objects. + * - There are no equivalents of AVBuffer and AVBufferRef. + * E.g. there is no way to get the usable size of the object: + * The user is supposed to know what is at the other end of + * the pointer. It also avoids one level of indirection. + * - Custom allocators are not supported. This allows to simplify + * the implementation and reduce the amount of allocations. + * - It also has the advantage that the user's free callback need + * only free the resources owned by the object, but not the + * object itself. + * - Because referencing (and replacing) an object managed by the + * RefStruct API does not involve allocations, they can not fail + * and therefore need not be checked. + * + * @note Referencing and unreferencing the buffers is thread-safe and thus + * may be done from multiple threads simultaneously without any need for + * additional locking. + */ + +/** + * This union is used for all opaque parameters in this API to spare the user + * to cast const away in case the opaque to use is const-qualified. + * + * The functions provided by this API with an FFRefStructOpaque come in pairs + * named foo_c and foo. The foo function accepts void* as opaque and is just + * a wrapper around the foo_c function; "_c" means "(potentially) const". + */ +typedef union { + void *nc; + const void *c; +} FFRefStructOpaque; + +/** + * If this flag is set in ff_refstruct_alloc_ext_c(), the object will not + * be initially zeroed. + */ +#define FF_REFSTRUCT_FLAG_NO_ZEROING (1 << 0) + +/** + * Allocate a refcounted object of usable size `size` managed via + * the RefStruct API. + * + * By default (in the absence of flags to the contrary), + * the returned object is initially zeroed. + * + * @param size Desired usable size of the returned object. + * @param flags A bitwise combination of FF_REFSTRUCT_FLAG_* flags. + * @param opaque A pointer that will be passed to the free_cb callback. + * @param free_cb A callback for freeing this object's content + * when its reference count reaches zero; + * it must not free the object itself. + * @return A pointer to an object of the desired size or NULL on failure. + */ +void *ff_refstruct_alloc_ext_c(size_t size, unsigned flags, FFRefStructOpaque opaque, + void (*free_cb)(FFRefStructOpaque opaque, void *obj)); + +/** + * A wrapper around ff_refstruct_alloc_ext_c() for the common case + * of a non-const qualified opaque. + * + * @see ff_refstruct_alloc_ext_c() + */ +static inline +void *ff_refstruct_alloc_ext(size_t size, unsigned flags, void *opaque, + void (*free_cb)(FFRefStructOpaque opaque, void *obj)) +{ + return ff_refstruct_alloc_ext_c(size, flags, (FFRefStructOpaque){.nc = opaque}, + free_cb); +} + +/** + * Equivalent to ff_refstruct_alloc_ext(size, 0, NULL, NULL) + */ +static inline +void *ff_refstruct_allocz(size_t size) +{ + return ff_refstruct_alloc_ext(size, 0, NULL, NULL); +} + +/** + * Decrement the reference count of the underlying object and automatically + * free the object if there are no more references to it. + * + * `*objp == NULL` is legal and a no-op. + * + * @param objp Pointer to a pointer that is either NULL or points to an object + * managed via this API. `*objp` is set to NULL on return. + */ +void ff_refstruct_unref(void *objp); + +/** + * Create a new reference to an object managed via this API, + * i.e. increment the reference count of the underlying object + * and return obj. + * @return a pointer equal to obj. + */ +void *ff_refstruct_ref(void *obj); + +/** + * Analog of ff_refstruct_ref(), but for constant objects. + * @see ff_refstruct_ref() + */ +const void *ff_refstruct_ref_c(const void *obj); + +/** + * Ensure `*dstp` refers to the same object as src. + * + * If `*dstp` is already equal to src, do nothing. Otherwise unreference `*dstp` + * and replace it with a new reference to src in case `src != NULL` (this + * involves incrementing the reference count of src's underlying object) or + * with NULL otherwise. + * + * @param dstp Pointer to a pointer that is either NULL or points to an object + * managed via this API. + * @param src A pointer to an object managed via this API or NULL. + */ +void ff_refstruct_replace(void *dstp, const void *src); + +/** + * Check whether the reference count of an object managed + * via this API is 1. + * + * @param obj A pointer to an object managed via this API. + * @return 1 if the reference count of obj is 1; 0 otherwise. + */ +int ff_refstruct_exclusive(const void *obj); + +/** + * FFRefStructPool is an API for a thread-safe pool of objects managed + * via the RefStruct API. + * + * Frequently allocating and freeing large or complicated objects may be slow + * and wasteful. This API is meant to solve this in cases when the caller + * needs a set of interchangable objects. + * + * At the beginning, the user must call allocate the pool via + * ff_refstruct_pool_alloc() or its analogue ff_refstruct_pool_alloc_ext(). + * Then whenever an object is needed, call ff_refstruct_pool_get() to + * get a new or reused object from the pool. This new object works in all + * aspects the same way as the ones created by ff_refstruct_alloc_ext(). + * However, when the last reference to this object is unreferenced, it is + * (optionally) reset and returned to the pool instead of being freed and + * will be reused for subsequent ff_refstruct_pool_get() calls. + * + * When the caller is done with the pool and no longer needs to create any new + * objects, ff_refstruct_pool_uninit() must be called to mark the pool as + * freeable. Then entries returned to the pool will then be freed. + * Once all the entries are freed, the pool will automatically be freed. + * + * Allocating and releasing objects with this API is thread-safe as long as + * the user-supplied callbacks (if provided) are thread-safe. + */ + +/** + * The buffer pool. This structure is opaque and not meant to be accessed + * directly. It is allocated with the allocators below and freed with + * ff_refstruct_pool_uninit(). + */ +typedef struct FFRefStructPool FFRefStructPool; + +/** + * If this flag is not set, every object in the pool will be zeroed before + * the init callback is called or before it is turned over to the user + * for the first time if no init callback has been provided. + */ +#define FF_REFSTRUCT_POOL_FLAG_NO_ZEROING FF_REFSTRUCT_FLAG_NO_ZEROING +/** + * If this flag is set and both init_cb and reset_cb callbacks are provided, + * then reset_cb will be called if init_cb fails. + * The object passed to reset_cb will be in the state left by init_cb. + */ +#define FF_REFSTRUCT_POOL_FLAG_RESET_ON_INIT_ERROR (1 << 16) +/** + * If this flag is set and both init_cb and free_entry_cb callbacks are + * provided, then free_cb will be called if init_cb fails. + * + * It will be called after reset_cb in case reset_cb and the + * FF_REFSTRUCT_POOL_FLAG_RESET_ON_INIT_ERROR flag are also set. + * + * The object passed to free_cb will be in the state left by + * the callbacks applied earlier (init_cb potentially followed by reset_cb). + */ +#define FF_REFSTRUCT_POOL_FLAG_FREE_ON_INIT_ERROR (1 << 17) +/** + * If this flag is set, the entries will be zeroed before + * being returned to the user (after the init or reset callbacks + * have been called (if provided)). Furthermore, to avoid zeroing twice + * it also makes the pool behave as if the FF_REFSTRUCT_POOL_FLAG_NO_ZEROING + * flag had been provided. + */ +#define FF_REFSTRUCT_POOL_FLAG_ZERO_EVERY_TIME (1 << 18) + +/** + * Equivalent to ff_refstruct_pool_alloc(size, flags, NULL, NULL, NULL, NULL, NULL) + */ +FFRefStructPool *ff_refstruct_pool_alloc(size_t size, unsigned flags); + +/** + * Allocate an FFRefStructPool, potentially using complex callbacks. + * + * @param size size of the entries of the pool + * @param flags a bitwise combination of FF_REFSTRUCT_POOL_FLAG_* flags + * @param opaque A pointer that will be passed to the callbacks below. + * @param init A callback that will be called directly after a new entry + * has been allocated. obj has already been zeroed unless + * the FF_REFSTRUCT_POOL_FLAG_NO_ZEROING flag is in use. + * @param reset A callback that will be called after an entry has been + * returned to the pool and before it is reused. + * @param free_entry A callback that will be called when an entry is freed + * after the pool has been marked as to be uninitialized. + * @param free A callback that will be called when the pool itself is + * freed (after the last entry has been returned and freed). + */ +FFRefStructPool *ff_refstruct_pool_alloc_ext_c(size_t size, unsigned flags, + FFRefStructOpaque opaque, + int (*init_cb)(FFRefStructOpaque opaque, void *obj), + void (*reset_cb)(FFRefStructOpaque opaque, void *obj), + void (*free_entry_cb)(FFRefStructOpaque opaque, void *obj), + void (*free_cb)(FFRefStructOpaque opaque)); + +/** + * A wrapper around ff_refstruct_pool_alloc_ext_c() for the common case + * of a non-const qualified opaque. + * + * @see ff_refstruct_pool_alloc_ext_c() + */ +static inline +FFRefStructPool *ff_refstruct_pool_alloc_ext(size_t size, unsigned flags, + void *opaque, + int (*init_cb)(FFRefStructOpaque opaque, void *obj), + void (*reset_cb)(FFRefStructOpaque opaque, void *obj), + void (*free_entry_cb)(FFRefStructOpaque opaque, void *obj), + void (*free_cb)(FFRefStructOpaque opaque)) +{ + return ff_refstruct_pool_alloc_ext_c(size, flags, (FFRefStructOpaque){.nc = opaque}, + init_cb, reset_cb, free_entry_cb, free_cb); +} + +/** + * Get an object from the pool, reusing an old one from the pool when + * available. + * + * Every call to this function must happen before ff_refstruct_pool_uninit(). + * Otherwise undefined behaviour may occur. + * + * @param pool the pool from which to get the object + * @return a reference to the object on success, NULL on error. + */ +void *ff_refstruct_pool_get(FFRefStructPool *pool); + +/** + * Mark the pool as being available for freeing. It will actually be freed + * only once all the allocated buffers associated with the pool are released. + * Thus it is safe to call this function while some of the allocated buffers + * are still in use. + * + * It is illegal to try to get a new entry after this function has been called. + * + * @param poolp pointer to a pointer to either NULL or a pool to be freed. + * `*poolp` will be set to NULL. + */ +static inline void ff_refstruct_pool_uninit(FFRefStructPool **poolp) +{ + ff_refstruct_unref(poolp); +} + +#endif /* AVCODEC_REFSTRUCT_H */ diff --git a/libavcodec/riscv/Makefile b/libavcodec/riscv/Makefile index 965942f4df8..6c2ce3001a4 100644 --- a/libavcodec/riscv/Makefile +++ b/libavcodec/riscv/Makefile @@ -1,21 +1,62 @@ -OBJS-$(CONFIG_AAC_DECODER) += riscv/aacpsdsp_init.o -RVV-OBJS-$(CONFIG_AAC_DECODER) += riscv/aacpsdsp_rvv.o +OBJS-$(CONFIG_AAC_DECODER) += riscv/aacpsdsp_init.o riscv/sbrdsp_init.o +RVV-OBJS-$(CONFIG_AAC_DECODER) += riscv/aacpsdsp_rvv.o riscv/sbrdsp_rvv.o +OBJS-$(CONFIG_AAC_ENCODER) += riscv/aacencdsp_init.o +RVV-OBJS-$(CONFIG_AAC_ENCODER) += riscv/aacencdsp_rvv.o +OBJS-$(CONFIG_AC3DSP) += riscv/ac3dsp_init.o +RV-OBJS-$(CONFIG_AC3DSP) += riscv/ac3dsp_rvb.o +RVV-OBJS-$(CONFIG_AC3DSP) += riscv/ac3dsp_rvv.o OBJS-$(CONFIG_ALAC_DECODER) += riscv/alacdsp_init.o RVV-OBJS-$(CONFIG_ALAC_DECODER) += riscv/alacdsp_rvv.o -OBJS-$(CONFIG_AUDIODSP) += riscv/audiodsp_init.o \ - riscv/audiodsp_rvf.o +OBJS-$(CONFIG_AUDIODSP) += riscv/audiodsp_init.o +RV-OBJS-$(CONFIG_AUDIODSP) += riscv/audiodsp_rvf.o RVV-OBJS-$(CONFIG_AUDIODSP) += riscv/audiodsp_rvv.o -OBJS-$(CONFIG_BSWAPDSP) += riscv/bswapdsp_init.o \ - riscv/bswapdsp_rvb.o +OBJS-$(CONFIG_BLOCKDSP) += riscv/blockdsp_init.o +RVV-OBJS-$(CONFIG_BLOCKDSP) += riscv/blockdsp_rvv.o +OBJS-$(CONFIG_BSWAPDSP) += riscv/bswapdsp_init.o +RV-OBJS-$(CONFIG_BSWAPDSP) += riscv/bswapdsp_rvb.o RVV-OBJS-$(CONFIG_BSWAPDSP) += riscv/bswapdsp_rvv.o +OBJS-$(CONFIG_EXR_DECODER) += riscv/exrdsp_init.o +RVV-OBJS-$(CONFIG_EXR_DECODER) += riscv/exrdsp_rvv.o +OBJS-$(CONFIG_FLAC_DECODER) += riscv/flacdsp_init.o +RVV-OBJS-$(CONFIG_FLAC_DECODER) += riscv/flacdsp_rvv.o OBJS-$(CONFIG_FMTCONVERT) += riscv/fmtconvert_init.o RVV-OBJS-$(CONFIG_FMTCONVERT) += riscv/fmtconvert_rvv.o +OBJS-$(CONFIG_G722DSP) += riscv/g722dsp_init.o +RVV-OBJS-$(CONFIG_G722DSP) += riscv/g722dsp_rvv.o +OBJS-$(CONFIG_JPEG2000_DECODER) += riscv/jpeg2000dsp_init.o +RVV-OBJS-$(CONFIG_JPEG2000_DECODER) += riscv/jpeg2000dsp_rvv.o +OBJS-$(CONFIG_H264CHROMA) += riscv/h264_chroma_init_riscv.o +RVV-OBJS-$(CONFIG_H264CHROMA) += riscv/h264_mc_chroma.o +OBJS-$(CONFIG_HUFFYUV_DECODER) += riscv/huffyuvdsp_init.o +RVV-OBJS-$(CONFIG_HUFFYUV_DECODER) += riscv/huffyuvdsp_rvv.o OBJS-$(CONFIG_IDCTDSP) += riscv/idctdsp_init.o RVV-OBJS-$(CONFIG_IDCTDSP) += riscv/idctdsp_rvv.o +OBJS-$(CONFIG_LLAUDDSP) += riscv/llauddsp_init.o +RVV-OBJS-$(CONFIG_LLAUDDSP) += riscv/llauddsp_rvv.o +OBJS-$(CONFIG_LLVIDDSP) += riscv/llviddsp_init.o +RVV-OBJS-$(CONFIG_LLVIDDSP) += riscv/llviddsp_rvv.o +OBJS-$(CONFIG_LLVIDENCDSP) += riscv/llvidencdsp_init.o +RVV-OBJS-$(CONFIG_LLVIDENCDSP) += riscv/llvidencdsp_rvv.o +OBJS-$(CONFIG_LPC) += riscv/lpc_init.o +RVV-OBJS-$(CONFIG_LPC) += riscv/lpc_rvv.o +OBJS-$(CONFIG_ME_CMP) += riscv/me_cmp_init.o +RVV-OBJS-$(CONFIG_ME_CMP) += riscv/me_cmp_rvv.o OBJS-$(CONFIG_OPUS_DECODER) += riscv/opusdsp_init.o RVV-OBJS-$(CONFIG_OPUS_DECODER) += riscv/opusdsp_rvv.o -OBJS-$(CONFIG_PIXBLOCKDSP) += riscv/pixblockdsp_init.o \ - riscv/pixblockdsp_rvi.o +OBJS-$(CONFIG_PIXBLOCKDSP) += riscv/pixblockdsp_init.o +RV-OBJS-$(CONFIG_PIXBLOCKDSP) += riscv/pixblockdsp_rvi.o RVV-OBJS-$(CONFIG_PIXBLOCKDSP) += riscv/pixblockdsp_rvv.o +OBJS-$(CONFIG_RV34DSP) += riscv/rv34dsp_init.o +RVV-OBJS-$(CONFIG_RV34DSP) += riscv/rv34dsp_rvv.o +OBJS-$(CONFIG_SVQ1_ENCODER) += riscv/svqenc_init.o +RVV-OBJS-$(CONFIG_SVQ1_ENCODER) += riscv/svqenc_rvv.o +OBJS-$(CONFIG_TAK_DECODER) += riscv/takdsp_init.o +RVV-OBJS-$(CONFIG_TAK_DECODER) += riscv/takdsp_rvv.o +OBJS-$(CONFIG_UTVIDEO_DECODER) += riscv/utvideodsp_init.o +RVV-OBJS-$(CONFIG_UTVIDEO_DECODER) += riscv/utvideodsp_rvv.o +OBJS-$(CONFIG_VC1DSP) += riscv/vc1dsp_init.o +RVV-OBJS-$(CONFIG_VC1DSP) += riscv/vc1dsp_rvv.o +OBJS-$(CONFIG_VP8DSP) += riscv/vp8dsp_init.o +RVV-OBJS-$(CONFIG_VP8DSP) += riscv/vp8dsp_rvv.o OBJS-$(CONFIG_VORBIS_DECODER) += riscv/vorbisdsp_init.o RVV-OBJS-$(CONFIG_VORBIS_DECODER) += riscv/vorbisdsp_rvv.o diff --git a/libavcodec/riscv/aacencdsp_init.c b/libavcodec/riscv/aacencdsp_init.c new file mode 100644 index 00000000000..b27af9d9730 --- /dev/null +++ b/libavcodec/riscv/aacencdsp_init.c @@ -0,0 +1,41 @@ +/* + * AAC encoder assembly optimizations + * Copyright (c) 2023 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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 "config.h" + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavcodec/aacencdsp.h" + +void ff_abs_pow34_rvv(float *out, const float *in, const int size); + +av_cold void ff_aacenc_dsp_init_riscv(AACEncDSPContext *s) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if (flags & AV_CPU_FLAG_RVV_F32) { + if (flags & AV_CPU_FLAG_RVB_ADDR) { + s->abs_pow34 = ff_abs_pow34_rvv; + } + } +#endif +} diff --git a/libavcodec/riscv/aacencdsp_rvv.S b/libavcodec/riscv/aacencdsp_rvv.S new file mode 100644 index 00000000000..4c7a874d779 --- /dev/null +++ b/libavcodec/riscv/aacencdsp_rvv.S @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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/riscv/asm.S" + +func ff_abs_pow34_rvv, zve32f +1: + vsetvli t0, a2, e32, m8, ta, ma + sub a2, a2, t0 + vle32.v v0, (a1) + sh2add a1, t0, a1 + vfabs.v v0, v0 + vfsqrt.v v8, v0 + vfmul.vv v8, v8, v0 + vfsqrt.v v8, v8 + vse32.v v8, (a0) + sh2add a0, t0, a0 + bnez a2, 1b + + ret +endfunc diff --git a/libavcodec/riscv/aacpsdsp_init.c b/libavcodec/riscv/aacpsdsp_init.c index f42baf4251a..e094660cf3f 100644 --- a/libavcodec/riscv/aacpsdsp_init.c +++ b/libavcodec/riscv/aacpsdsp_init.c @@ -43,15 +43,19 @@ av_cold void ff_psdsp_init_riscv(PSDSPContext *c) int flags = av_get_cpu_flags(); if (flags & AV_CPU_FLAG_RVV_F32) { - c->add_squares = ff_ps_add_squares_rvv; - c->mul_pair_single = ff_ps_mul_pair_single_rvv; c->hybrid_analysis = ff_ps_hybrid_analysis_rvv; - c->stereo_interpolate[0] = ff_ps_stereo_interpolate_rvv; + + if (flags & AV_CPU_FLAG_RVB_ADDR) { + if (flags & AV_CPU_FLAG_RVV_I64) { + c->add_squares = ff_ps_add_squares_rvv; + c->hybrid_synthesis_deint = ff_ps_hybrid_synthesis_deint_rvv; + } + c->mul_pair_single = ff_ps_mul_pair_single_rvv; + c->stereo_interpolate[0] = ff_ps_stereo_interpolate_rvv; + } } - if (flags & AV_CPU_FLAG_RVV_I32) { + if ((flags & AV_CPU_FLAG_RVV_I32) && (flags & AV_CPU_FLAG_RVB_ADDR)) c->hybrid_analysis_ileave = ff_ps_hybrid_analysis_ileave_rvv; - c->hybrid_synthesis_deint = ff_ps_hybrid_synthesis_deint_rvv; - } #endif } diff --git a/libavcodec/riscv/aacpsdsp_rvv.S b/libavcodec/riscv/aacpsdsp_rvv.S index 80bd19f6ad2..a79d7d7818f 100644 --- a/libavcodec/riscv/aacpsdsp_rvv.S +++ b/libavcodec/riscv/aacpsdsp_rvv.S @@ -1,5 +1,5 @@ /* - * Copyright © 2022 Rémi Denis-Courmont. + * Copyright © 2022-2023 Rémi Denis-Courmont. * * This file is part of FFmpeg. * @@ -20,15 +20,18 @@ #include "libavutil/riscv/asm.S" -func ff_ps_add_squares_rvv, zve32f +func ff_ps_add_squares_rvv, zve64f + li t1, 32 1: - vsetvli t0, a2, e32, m1, ta, ma - vlseg2e32.v v24, (a1) + vsetvli t0, a2, e32, m4, ta, ma + vle64.v v8, (a1) sub a2, a2, t0 + vnsrl.wx v24, v8, zero vle32.v v16, (a0) sh3add a1, t0, a1 + vnsrl.wx v28, v8, t1 vfmacc.vv v16, v24, v24 - vfmacc.vv v16, v25, v25 + vfmacc.vv v16, v28, v28 vse32.v v16, (a0) sh2add a0, t0, a0 bnez a2, 1b @@ -38,14 +41,14 @@ endfunc func ff_ps_mul_pair_single_rvv, zve32f 1: - vsetvli t0, a3, e32, m1, ta, ma + vsetvli t0, a3, e32, m4, ta, ma vlseg2e32.v v24, (a1) sub a3, a3, t0 vle32.v v16, (a2) sh3add a1, t0, a1 vfmul.vv v24, v24, v16 sh2add a2, t0, a2 - vfmul.vv v25, v25, v16 + vfmul.vv v28, v28, v16 vsseg2e32.v v24, (a0) sh3add a0, t0, a0 bnez a3, 1b @@ -82,63 +85,42 @@ NOHWD fsw fs\n, (4 * \n)(sp) flw fs4, (4 * ((6 * 2) + 0))(a1) flw fs5, (4 * ((6 * 2) + 1))(a1) - add a2, a2, 6 * 2 * 4 // point to filter[i][6][0] + add t2, a2, 6 * 2 * 4 // point to filter[i][6][0] li t4, 8 * 2 * 4 // filter byte stride slli a3, a3, 3 // output byte stride 1: .macro filter, vs0, vs1, fo0, fo1, fo2, fo3 vfmacc.vf v8, \fo0, \vs0 - vfmacc.vf v9, \fo2, \vs0 + vfmacc.vf v10, \fo2, \vs0 vfnmsac.vf v8, \fo1, \vs1 - vfmacc.vf v9, \fo3, \vs1 + vfmacc.vf v10, \fo3, \vs1 .endm - vsetvli t0, a4, e32, m1, ta, ma + vsetvli t0, a4, e32, m2, ta, ma /* * The filter (a2) has 16 segments, of which 13 need to be extracted. * R-V V supports only up to 8 segments, so unrolling is unavoidable. */ - addi t1, a2, -48 - vlse32.v v22, (a2), t4 - addi t2, a2, -44 - vlse32.v v16, (t1), t4 - addi t1, a2, -40 - vfmul.vf v8, v22, fs4 - vlse32.v v24, (t2), t4 - addi t2, a2, -36 - vfmul.vf v9, v22, fs5 - vlse32.v v17, (t1), t4 - addi t1, a2, -32 - vlse32.v v25, (t2), t4 - addi t2, a2, -28 - filter v16, v24, ft0, ft1, ft2, ft3 - vlse32.v v18, (t1), t4 - addi t1, a2, -24 - vlse32.v v26, (t2), t4 - addi t2, a2, -20 - filter v17, v25, ft4, ft5, ft6, ft7 - vlse32.v v19, (t1), t4 - addi t1, a2, -16 - vlse32.v v27, (t2), t4 - addi t2, a2, -12 - filter v18, v26, ft8, ft9, ft10, ft11 - vlse32.v v20, (t1), t4 - addi t1, a2, -8 vlse32.v v28, (t2), t4 - addi t2, a2, -4 - filter v19, v27, fa0, fa1, fa2, fa3 - vlse32.v v21, (t1), t4 + addi t1, a2, 16 + vfmul.vf v8, v28, fs4 + vlsseg4e32.v v16, (a2), t4 + vfmul.vf v10, v28, fs5 + filter v16, v18, ft0, ft1, ft2, ft3 + vlsseg4e32.v v24, (t1), t4 + filter v20, v22, ft4, ft5, ft6, ft7 + addi t1, a2, 32 + filter v24, v26, ft8, ft9, ft10, ft11 + vlsseg4e32.v v16, (t1), t4 sub a4, a4, t0 - vlse32.v v29, (t2), t4 + filter v28, v30, fa0, fa1, fa2, fa3 slli t1, t0, 3 + 1 + 2 // ctz(8 * 2 * 4) - add a2, a2, t1 - filter v20, v28, fa4, fa5, fa6, fa7 - filter v21, v29, fs0, fs1, fs2, fs3 - - add t2, a0, 4 - vsse32.v v8, (a0), a3 + filter v16, v18, fa4, fa5, fa6, fa7 mul t0, t0, a3 - vsse32.v v9, (t2), a3 + filter v20, v22, fs0, fs1, fs2, fs3 + add a2, a2, t1 + add t2, t2, t1 + vssseg2e32.v v8, (a0), a3 add a0, a0, t0 bnez a4, 1b @@ -168,10 +150,10 @@ func ff_ps_hybrid_analysis_ileave_rvv, zve32x /* no needs for zve32f here */ mv t4, a4 addi a2, a2, 1 2: - vsetvli t5, t3, e32, m1, ta, ma + vsetvli t5, t3, e32, m4, ta, ma vlse32.v v16, (t1), t6 sub t3, t3, t5 - vlse32.v v17, (t4), t6 + vlse32.v v20, (t4), t6 mul t2, t5, t6 vsseg2e32.v v16, (t0) sh3add t0, t5, t0 @@ -187,43 +169,46 @@ func ff_ps_hybrid_analysis_ileave_rvv, zve32x /* no needs for zve32f here */ ret endfunc -func ff_ps_hybrid_synthesis_deint_rvv, zve32x - slli t1, a2, 5 + 1 + 2 - sh2add a0, a2, a0 - add a1, a1, t1 - addi a2, a2, -64 - li t1, 38 * 64 * 4 - li t6, 64 * 4 - add a4, a0, t1 - beqz a2, 3f +func ff_ps_hybrid_synthesis_deint_rvv, zve64x + slli t0, a2, 5 + 1 + 2 + sh2add a0, a2, a0 + add a1, a1, t0 + addi t2, a2, -64 + li t0, 38 * 64 + li t1, 32 * 2 * 4 + li t4, 8 - 16384 // offset from in[64][n][0] to in[0][n + 1][0] + slli t5, a2, 5 + 1 + 2 // and from in[0][n+1][0] to in[0][n+1][s] + neg t2, t2 + li t3, 32 + add a4, t4, t5 + sh2add t0, t0, a0 1: - mv t0, a0 - mv t1, a1 - mv t3, a3 - mv t4, a4 - addi a2, a2, 1 + mv t4, t2 + addi a3, a3, -1 2: - vsetvli t5, t3, e32, m1, ta, ma - vlseg2e32.v v16, (t1) - sub t3, t3, t5 - vsse32.v v16, (t0), t6 - mul t2, t5, t6 - vsse32.v v17, (t4), t6 - sh3add t1, t5, t1 - add t0, t0, t2 - add t4, t4, t2 - bnez t3, 2b + vsetvli t5, t4, e32, m4, ta, ma + vlse64.v v16, (a1), t1 /* sizeof (float[32][2]) */ + sub t4, t4, t5 + vnsrl.wx v24, v16, zero + slli t6, t5, 5 + 1 + 2 + vnsrl.wx v28, v16, t3 /* 32 */ + add a1, a1, t6 + vse32.v v24, (a0) + sh2add a0, t5, a0 + vse32.v v28, (t0) + sh2add t0, t5, t0 + bnez t4, 2b + + add a1, a1, a4 + sh2add a0, a2, a0 + sh2add t0, a2, t0 + bnez a3, 1b - add a0, a0, 4 - add a1, a1, 32 * 2 * 4 - add a4, a4, 4 - bnez a2, 1b -3: ret endfunc func ff_ps_stereo_interpolate_rvv, zve32f - vsetvli t0, zero, e32, m1, ta, ma + vsetvli t0, zero, e32, m2, ta, ma vid.v v24 flw ft0, (a2) vadd.vi v24, v24, 1 // v24[i] = i + 1 @@ -232,47 +217,44 @@ func ff_ps_stereo_interpolate_rvv, zve32f flw ft2, 8(a2) vfmv.v.f v16, ft0 flw ft3, 12(a2) - vfmv.v.f v17, ft1 + vfmv.v.f v18, ft1 flw ft0, (a3) - vfmv.v.f v18, ft2 + vfmv.v.f v20, ft2 flw ft1, 4(a3) - vfmv.v.f v19, ft3 + vfmv.v.f v22, ft3 flw ft2, 8(a3) - vfmv.v.f v20, ft0 flw ft3, 12(a3) - vfmv.v.f v21, ft1 fcvt.s.wu ft4, t0 // (float)(vlenb / sizeof (float)) - vfmv.v.f v22, ft2 + vfmacc.vf v16, ft0, v24 // h0 += (i + 1) * h0_step fmul.s ft0, ft0, ft4 - vfmv.v.f v23, ft3 + vfmacc.vf v18, ft1, v24 fmul.s ft1, ft1, ft4 - vfmacc.vv v16, v24, v20 // h0 += (i + 1) * h0_step + vfmacc.vf v20, ft2, v24 fmul.s ft2, ft2, ft4 - vfmacc.vv v17, v24, v21 + vfmacc.vf v22, ft3, v24 fmul.s ft3, ft3, ft4 - vfmacc.vv v18, v24, v22 - vfmacc.vv v19, v24, v23 1: - vsetvli t0, a4, e32, m1, ta, ma - vlseg2e32.v v8, (a0) // v8:l_re, v9:l_im + min t0, t0, a4 + vsetvli zero, t0, e32, m2, ta, ma + vlseg2e32.v v0, (a0) // v0:l_re, v2:l_im sub a4, a4, t0 - vlseg2e32.v v10, (a1) // v10:r_re, v11:r_im - vfmul.vv v12, v8, v16 - vfmul.vv v13, v9, v16 - vfmul.vv v14, v8, v17 - vfmul.vv v15, v9, v17 - vfmacc.vv v12, v10, v18 - vfmacc.vv v13, v11, v18 - vfmacc.vv v14, v10, v19 - vfmacc.vv v15, v11, v19 - vsseg2e32.v v12, (a0) + vlseg2e32.v v4, (a1) // v4:r_re, v6:r_im + vfmul.vv v8, v0, v16 + vfmul.vv v10, v2, v16 + vfmul.vv v12, v0, v18 + vfmul.vv v14, v2, v18 + vfmacc.vv v8, v4, v20 + vfmacc.vv v10, v6, v20 + vfmacc.vv v12, v4, v22 + vfmacc.vv v14, v6, v22 + vsseg2e32.v v8, (a0) sh3add a0, t0, a0 - vsseg2e32.v v14, (a1) + vsseg2e32.v v12, (a1) sh3add a1, t0, a1 vfadd.vf v16, v16, ft0 // h0 += (vlenb / sizeof (float)) * h0_step - vfadd.vf v17, v17, ft1 - vfadd.vf v18, v18, ft2 - vfadd.vf v19, v19, ft3 + vfadd.vf v18, v18, ft1 + vfadd.vf v20, v20, ft2 + vfadd.vf v22, v22, ft3 bnez a4, 1b ret diff --git a/libavcodec/riscv/ac3dsp_init.c b/libavcodec/riscv/ac3dsp_init.c new file mode 100644 index 00000000000..b9e14d56cac --- /dev/null +++ b/libavcodec/riscv/ac3dsp_init.c @@ -0,0 +1,44 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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 +#include + +#include "config.h" +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavcodec/ac3dsp.h" + +void ff_extract_exponents_rvb(uint8_t *exp, int32_t *coef, int nb_coefs); +void ff_float_to_fixed24_rvv(int32_t *dst, const float *src, size_t len); + +av_cold void ff_ac3dsp_init_riscv(AC3DSPContext *c) +{ +#if HAVE_RV + int flags = av_get_cpu_flags(); + + if (flags & AV_CPU_FLAG_RVB_ADDR) { + if (flags & AV_CPU_FLAG_RVB_BASIC) + c->extract_exponents = ff_extract_exponents_rvb; + if (flags & AV_CPU_FLAG_RVV_F32) + c->float_to_fixed24 = ff_float_to_fixed24_rvv; + } +#endif +} diff --git a/libavcodec/riscv/ac3dsp_rvb.S b/libavcodec/riscv/ac3dsp_rvb.S new file mode 100644 index 00000000000..48f8bb101ef --- /dev/null +++ b/libavcodec/riscv/ac3dsp_rvb.S @@ -0,0 +1,38 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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 "config.h" +#include "libavutil/riscv/asm.S" + +func ff_extract_exponents_rvb, zbb +1: + lw t0, (a1) + addi a0, a0, 1 + neg t1, t0 + addi a1, a1, 4 + max t0, t0, t1 + addi a2, a2, -1 + clz t0, t0 + addi t0, t0, 24 - __riscv_xlen + sb t0, -1(a0) + bgtz a2, 1b + + ret +endfunc diff --git a/libavcodec/riscv/ac3dsp_rvv.S b/libavcodec/riscv/ac3dsp_rvv.S new file mode 100644 index 00000000000..b8d32c46779 --- /dev/null +++ b/libavcodec/riscv/ac3dsp_rvv.S @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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 "config.h" +#include "libavutil/riscv/asm.S" + +func ff_float_to_fixed24_rvv, zve32f + li t1, 1 << 24 + fcvt.s.w f0, t1 +1: + vsetvli t0, a2, e32, m8, ta, ma + sub a2, a2, t0 + vle32.v v0, (a1) + vfmul.vf v0, v0, f0 + vfcvt.x.f.v v0, v0 + sh2add a1, t0, a1 + vse32.v v0, (a0) + sh2add a0, t0, a0 + bnez a2, 1b + + ret +endfunc diff --git a/libavcodec/riscv/alacdsp_init.c b/libavcodec/riscv/alacdsp_init.c index fa8a7c81299..cd6dc4f8aea 100644 --- a/libavcodec/riscv/alacdsp_init.c +++ b/libavcodec/riscv/alacdsp_init.c @@ -41,7 +41,7 @@ av_cold void ff_alacdsp_init_riscv(ALACDSPContext *c) #if HAVE_RVV && (__riscv_xlen == 64) int flags = av_get_cpu_flags(); - if (flags & AV_CPU_FLAG_RVV_I32) { + if ((flags & AV_CPU_FLAG_RVV_I32) && (flags & AV_CPU_FLAG_RVB_ADDR)) { c->decorrelate_stereo = ff_alac_decorrelate_stereo_rvv; c->append_extra_bits[0] = ff_alac_append_extra_bits_mono_rvv; c->append_extra_bits[1] = ff_alac_append_extra_bits_stereo_rvv; diff --git a/libavcodec/riscv/alacdsp_rvv.S b/libavcodec/riscv/alacdsp_rvv.S index 8fbe3fbe778..8efb04e0c8d 100644 --- a/libavcodec/riscv/alacdsp_rvv.S +++ b/libavcodec/riscv/alacdsp_rvv.S @@ -25,7 +25,7 @@ func ff_alac_decorrelate_stereo_rvv, zve32x ld a4, 8(a0) ld a0, 0(a0) 1: - vsetvli t0, a1, e32, m1, ta, ma + vsetvli t0, a1, e32, m4, ta, ma vle32.v v24, (a4) sub a1, a1, t0 vle32.v v16, (a0) @@ -47,7 +47,7 @@ func ff_alac_append_extra_bits_mono_rvv, zve32x ld a0, (a0) ld a1, (a1) 1: - vsetvli t0, a4, e32, m1, ta, ma + vsetvli t0, a4, e32, m8, ta, ma vle32.v v16, (a0) sub a4, a4, t0 vle32.v v24, (a1) @@ -67,7 +67,7 @@ func ff_alac_append_extra_bits_stereo_rvv, zve32x ld a7, 8(a1) ld a1, (a1) 1: - vsetvli t0, a4, e32, m1, ta, ma + vsetvli t0, a4, e32, m8, ta, ma vle32.v v16, (a0) sub a4, a4, t0 vle32.v v0, (a6) diff --git a/libavcodec/riscv/audiodsp_init.c b/libavcodec/riscv/audiodsp_init.c index 32c3c6794da..f606406429b 100644 --- a/libavcodec/riscv/audiodsp_init.c +++ b/libavcodec/riscv/audiodsp_init.c @@ -33,16 +33,20 @@ void ff_vector_clipf_rvv(float *dst, const float *src, int len, float min, float av_cold void ff_audiodsp_init_riscv(AudioDSPContext *c) { +#if HAVE_RV int flags = av_get_cpu_flags(); if (flags & AV_CPU_FLAG_RVF) c->vector_clipf = ff_vector_clipf_rvf; #if HAVE_RVV - if (flags & AV_CPU_FLAG_RVV_I32) { - c->scalarproduct_int16 = ff_scalarproduct_int16_rvv; - c->vector_clip_int32 = ff_vector_clip_int32_rvv; + if (flags & AV_CPU_FLAG_RVB_ADDR) { + if (flags & AV_CPU_FLAG_RVV_I32) { + c->scalarproduct_int16 = ff_scalarproduct_int16_rvv; + c->vector_clip_int32 = ff_vector_clip_int32_rvv; + } + if (flags & AV_CPU_FLAG_RVV_F32) + c->vector_clipf = ff_vector_clipf_rvv; } - if (flags & AV_CPU_FLAG_RVV_F32) - c->vector_clipf = ff_vector_clipf_rvv; +#endif #endif } diff --git a/libavcodec/riscv/audiodsp_rvv.S b/libavcodec/riscv/audiodsp_rvv.S index af1e07bef9c..f0b23bab5e7 100644 --- a/libavcodec/riscv/audiodsp_rvv.S +++ b/libavcodec/riscv/audiodsp_rvv.S @@ -21,27 +21,28 @@ #include "libavutil/riscv/asm.S" func ff_scalarproduct_int16_rvv, zve32x - vsetivli zero, 1, e32, m1, ta, ma - vmv.s.x v8, zero + vsetvli t0, zero, e32, m8, ta, ma + vmv.v.x v8, zero + vmv.s.x v0, zero 1: - vsetvli t0, a2, e16, m1, ta, ma + vsetvli t0, a2, e16, m4, tu, ma vle16.v v16, (a0) sub a2, a2, t0 vle16.v v24, (a1) sh1add a0, t0, a0 - vwmul.vv v0, v16, v24 + vwmacc.vv v8, v16, v24 sh1add a1, t0, a1 - vsetvli zero, t0, e32, m2, ta, ma - vredsum.vs v8, v0, v8 bnez a2, 1b - vmv.x.s a0, v8 + vsetvli t0, zero, e32, m8, ta, ma + vredsum.vs v0, v8, v0 + vmv.x.s a0, v0 ret endfunc func ff_vector_clip_int32_rvv, zve32x 1: - vsetvli t0, a4, e32, m1, ta, ma + vsetvli t0, a4, e32, m8, ta, ma vle32.v v8, (a1) sub a4, a4, t0 vmax.vx v8, v8, a2 @@ -58,7 +59,7 @@ func ff_vector_clipf_rvv, zve32f NOHWF fmv.w.x fa0, a3 NOHWF fmv.w.x fa1, a4 1: - vsetvli t0, a2, e32, m1, ta, ma + vsetvli t0, a2, e32, m8, ta, ma vle32.v v8, (a1) sub a2, a2, t0 vfmax.vf v8, v8, fa0 diff --git a/libavcodec/riscv/blockdsp_init.c b/libavcodec/riscv/blockdsp_init.c new file mode 100644 index 00000000000..59b2f9d47bc --- /dev/null +++ b/libavcodec/riscv/blockdsp_init.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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 "config.h" + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/riscv/cpu.h" +#include "libavcodec/blockdsp.h" + +void ff_clear_block_rvv(int16_t *block); +void ff_clear_blocks_rvv(int16_t *block); + +av_cold void ff_blockdsp_init_riscv(BlockDSPContext *c) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if (flags & AV_CPU_FLAG_RVV_I64 && ff_get_rv_vlenb() >= 16) { + c->clear_block = ff_clear_block_rvv; + c->clear_blocks = ff_clear_blocks_rvv; + } +#endif +} diff --git a/libavcodec/riscv/blockdsp_rvv.S b/libavcodec/riscv/blockdsp_rvv.S new file mode 100644 index 00000000000..8bb00bb467a --- /dev/null +++ b/libavcodec/riscv/blockdsp_rvv.S @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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/riscv/asm.S" + +func ff_clear_block_rvv, zve64x + vsetivli zero, 16, e64, m8, ta, ma + vmv.v.i v0, 0 + vse64.v v0, (a0) + + ret +endfunc + +func ff_clear_blocks_rvv, zve64x + vsetivli zero, 16, e64, m8, ta, ma + vmv.v.i v0, 0 + + .rept 5 + vse64.v v0, (a0) + addi a0, a0, 128 + .endr + vse64.v v0, (a0) + + ret +endfunc diff --git a/libavcodec/riscv/bswapdsp_init.c b/libavcodec/riscv/bswapdsp_init.c index abe84ec1f7d..d490c434e78 100644 --- a/libavcodec/riscv/bswapdsp_init.c +++ b/libavcodec/riscv/bswapdsp_init.c @@ -26,21 +26,22 @@ #include "libavcodec/bswapdsp.h" void ff_bswap32_buf_rvb(uint32_t *dst, const uint32_t *src, int len); -void ff_bswap32_buf_rvv(uint32_t *dst, const uint32_t *src, int len); void ff_bswap16_buf_rvv(uint16_t *dst, const uint16_t *src, int len); av_cold void ff_bswapdsp_init_riscv(BswapDSPContext *c) { - int cpu_flags = av_get_cpu_flags(); +#if HAVE_RV + int flags = av_get_cpu_flags(); + if (flags & AV_CPU_FLAG_RVB_ADDR) { #if (__riscv_xlen >= 64) - if (cpu_flags & AV_CPU_FLAG_RVB_BASIC) - c->bswap_buf = ff_bswap32_buf_rvb; + if (flags & AV_CPU_FLAG_RVB_BASIC) + c->bswap_buf = ff_bswap32_buf_rvb; #endif #if HAVE_RVV - if (cpu_flags & AV_CPU_FLAG_RVV_I32) { - c->bswap_buf = ff_bswap32_buf_rvv; - c->bswap16_buf = ff_bswap16_buf_rvv; + if (flags & AV_CPU_FLAG_RVV_I32) + c->bswap16_buf = ff_bswap16_buf_rvv; +#endif } #endif } diff --git a/libavcodec/riscv/bswapdsp_rvb.S b/libavcodec/riscv/bswapdsp_rvb.S index 91b47bf82d4..0786bd3f364 100644 --- a/libavcodec/riscv/bswapdsp_rvb.S +++ b/libavcodec/riscv/bswapdsp_rvb.S @@ -20,49 +20,10 @@ #include "config.h" #include "libavutil/riscv/asm.S" +#include "libavutil/riscv/bswap_rvb.S" #if (__riscv_xlen >= 64) func ff_bswap32_buf_rvb, zbb - andi t0, a1, 4 - beqz t0, 1f - /* Align a1 (input) to 64-bit */ - lwu t0, (a1) - addi a0, a0, 4 - rev8 t0, t0 - addi a2, a2, -1 - srli t0, t0, __riscv_xlen - 32 - addi a1, a1, 4 - sw t0, -4(a0) -1: - andi a3, a2, -2 - sh2add a2, a2, a0 - beqz a3, 3f - sh2add a3, a3, a0 -2: /* 2 elements (64 bits) at a time on a 64-bit boundary */ - ld t0, (a1) - addi a0, a0, 8 - rev8 t0, t0 -#if (__riscv_xlen == 64) - srli t2, t0, 32 - sw t0, -4(a0) -#else - srli t1, t0, __riscv_xlen - 64 - srli t2, t0, __riscv_xlen - 32 - sw t1, -4(a0) -#endif - addi a1, a1, 8 - sw t2, -8(a0) - bne a0, a3, 2b -3: - beq a0, a2, 5f -4: /* Process last element */ - lwu t0, (a1) - addi a0, a0, 4 - rev8 t0, t0 - addi a1, a1, 4 - srli t0, t0, __riscv_xlen - 32 - sw t0, -4(a0) -5: - ret + bswap32_rvb a0, a1, a2 endfunc #endif diff --git a/libavcodec/riscv/bswapdsp_rvv.S b/libavcodec/riscv/bswapdsp_rvv.S index ef2999c1be5..b37fe262559 100644 --- a/libavcodec/riscv/bswapdsp_rvv.S +++ b/libavcodec/riscv/bswapdsp_rvv.S @@ -21,42 +21,18 @@ #include "config.h" #include "libavutil/riscv/asm.S" -func ff_bswap32_buf_rvv, zve32x - li t4, 4 - addi t1, a0, 1 - addi t2, a0, 2 - addi t3, a0, 3 -1: - vsetvli t0, a2, e8, m1, ta, ma - vlseg4e8.v v8, (a1) - sub a2, a2, t0 - sh2add a1, t0, a1 - vsse8.v v8, (t3), t4 - sh2add t3, t0, t3 - vsse8.v v9, (t2), t4 - sh2add t2, t0, t2 - vsse8.v v10, (t1), t4 - sh2add t1, t0, t1 - vsse8.v v11, (a0), t4 - sh2add a0, t0, a0 - bnez a2, 1b - - ret -endfunc - func ff_bswap16_buf_rvv, zve32x - li t2, 2 - addi t1, a0, 1 1: - vsetvli t0, a2, e8, m1, ta, ma - vlseg2e8.v v8, (a1) - sub a2, a2, t0 - sh1add a1, t0, a1 - vsse8.v v8, (t1), t2 - sh1add t1, t0, t1 - vsse8.v v9, (a0), t2 - sh1add a0, t0, a0 - bnez a2, 1b + vsetvli t0, a2, e16, m8, ta, ma + vle16.v v8, (a1) + sub a2, a2, t0 + vsll.vi v16, v8, 8 + sh1add a1, t0, a1 + vsrl.vi v24, v8, 8 + vor.vv v8, v16, v24 + vse16.v v8, (a0) + sh1add a0, t0, a0 + bnez a2, 1b ret endfunc diff --git a/libavutil/bfin/timer.h b/libavcodec/riscv/exrdsp_init.c similarity index 63% rename from libavutil/bfin/timer.h rename to libavcodec/riscv/exrdsp_init.c index 644573daec9..690a2231c5b 100644 --- a/libavutil/bfin/timer.h +++ b/libavcodec/riscv/exrdsp_init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Marc Hoffman + * Copyright © 2023 Rémi Denis-Courmont. * * This file is part of FFmpeg. * @@ -18,24 +18,21 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVUTIL_BFIN_TIMER_H -#define AVUTIL_BFIN_TIMER_H - #include -#define AV_READ_TIME read_time +#include "libavutil/cpu.h" +#include "libavcodec/avcodec.h" +#include "libavcodec/exrdsp.h" + +void ff_reorder_pixels_rvv(uint8_t *dst, const uint8_t *src, ptrdiff_t size); -static inline uint64_t read_time(void) +av_cold void ff_exrdsp_init_riscv(ExrDSPContext *c) { - union { - struct { - unsigned lo; - unsigned hi; - } p; - unsigned long long c; - } t; - __asm__ volatile ("%0=cycles; %1=cycles2;" : "=d" (t.p.lo), "=d" (t.p.hi)); - return t.c; -} +#if HAVE_RVV + int flags = av_get_cpu_flags(); -#endif /* AVUTIL_BFIN_TIMER_H */ + if ((flags & AV_CPU_FLAG_RVV_I32) && (flags & AV_CPU_FLAG_RVB_ADDR)) { + c->reorder_pixels = ff_reorder_pixels_rvv; + } +#endif +} diff --git a/libavcodec/riscv/exrdsp_rvv.S b/libavcodec/riscv/exrdsp_rvv.S new file mode 100644 index 00000000000..f4a35f58ff0 --- /dev/null +++ b/libavcodec/riscv/exrdsp_rvv.S @@ -0,0 +1,38 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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/riscv/asm.S" + +func ff_reorder_pixels_rvv, zve32x + srai a2, a2, 1 + add t1, a1, a2 +1: + vsetvli t0, a2, e8, m4, ta, ma + vle8.v v0, (a1) + sub a2, a2, t0 + vle8.v v4, (t1) + add a1, t0, a1 + vsseg2e8.v v0, (a0) + add t1, t0, t1 + sh1add a0, t0, a0 + bnez a2, 1b + + ret +endfunc diff --git a/libavcodec/riscv/flacdsp_init.c b/libavcodec/riscv/flacdsp_init.c new file mode 100644 index 00000000000..6cfb50ead8e --- /dev/null +++ b/libavcodec/riscv/flacdsp_init.c @@ -0,0 +1,124 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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 "config.h" + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/riscv/cpu.h" +#include "libavcodec/flacdsp.h" + +void ff_flac_lpc16_rvv(int32_t *decoded, const int coeffs[32], + int pred_order, int qlevel, int len); +void ff_flac_lpc32_rvv(int32_t *decoded, const int coeffs[32], + int pred_order, int qlevel, int len); +void ff_flac_lpc32_rvv_simple(int32_t *decoded, const int coeffs[32], + int pred_order, int qlevel, int len); +void ff_flac_decorrelate_indep2_16_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); +void ff_flac_decorrelate_indep4_16_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); +void ff_flac_decorrelate_indep6_16_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); +void ff_flac_decorrelate_indep8_16_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); +void ff_flac_decorrelate_ls_16_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); +void ff_flac_decorrelate_rs_16_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); +void ff_flac_decorrelate_ms_16_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); +void ff_flac_decorrelate_indep2_32_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); +void ff_flac_decorrelate_indep4_32_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); +void ff_flac_decorrelate_indep6_32_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); +void ff_flac_decorrelate_indep8_32_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); +void ff_flac_decorrelate_ls_32_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); +void ff_flac_decorrelate_rs_32_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); +void ff_flac_decorrelate_ms_32_rvv(uint8_t **out, int32_t **in, + int channels, int len, int shift); + +av_cold void ff_flacdsp_init_riscv(FLACDSPContext *c, enum AVSampleFormat fmt, + int channels) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if ((flags & AV_CPU_FLAG_RVV_I32) && (flags & AV_CPU_FLAG_RVB_ADDR)) { + int vlenb = ff_get_rv_vlenb(); + + if (vlenb >= 16) { + c->lpc16 = ff_flac_lpc16_rvv; +# if (__riscv_xlen >= 64) + if (vlenb > 16) + c->lpc32 = ff_flac_lpc32_rvv_simple; + else + c->lpc32 = ff_flac_lpc32_rvv; + } + + switch (fmt) { + case AV_SAMPLE_FMT_S16: + switch (channels) { + case 2: + c->decorrelate[0] = ff_flac_decorrelate_indep2_16_rvv; + break; + case 4: + c->decorrelate[0] = ff_flac_decorrelate_indep4_16_rvv; + break; + case 6: + c->decorrelate[0] = ff_flac_decorrelate_indep6_16_rvv; + break; + case 8: + c->decorrelate[0] = ff_flac_decorrelate_indep8_16_rvv; + break; + } + c->decorrelate[1] = ff_flac_decorrelate_ls_16_rvv; + c->decorrelate[2] = ff_flac_decorrelate_rs_16_rvv; + c->decorrelate[3] = ff_flac_decorrelate_ms_16_rvv; + break; + case AV_SAMPLE_FMT_S32: + switch (channels) { + case 2: + c->decorrelate[0] = ff_flac_decorrelate_indep2_32_rvv; + break; + case 4: + c->decorrelate[0] = ff_flac_decorrelate_indep4_32_rvv; + break; + case 6: + c->decorrelate[0] = ff_flac_decorrelate_indep6_32_rvv; + break; + case 8: + c->decorrelate[0] = ff_flac_decorrelate_indep8_32_rvv; + break; + } + c->decorrelate[1] = ff_flac_decorrelate_ls_32_rvv; + c->decorrelate[2] = ff_flac_decorrelate_rs_32_rvv; + c->decorrelate[3] = ff_flac_decorrelate_ms_32_rvv; + break; +# endif + } + } +#endif +} diff --git a/libavcodec/riscv/flacdsp_rvv.S b/libavcodec/riscv/flacdsp_rvv.S new file mode 100644 index 00000000000..5eb3c5bd550 --- /dev/null +++ b/libavcodec/riscv/flacdsp_rvv.S @@ -0,0 +1,531 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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/riscv/asm.S" + +func ff_flac_lpc16_rvv, zve32x + vsetvli zero, a2, e32, m8, ta, ma + vle32.v v8, (a1) + sub a4, a4, a2 + vle32.v v16, (a0) + sh2add a0, a2, a0 + vmv.s.x v0, zero +1: + vmul.vv v24, v8, v16 + lw t0, (a0) + vredsum.vs v24, v24, v0 + addi a4, a4, -1 + vmv.x.s t1, v24 + sra t1, t1, a3 + add t0, t0, t1 + vslide1down.vx v16, v16, t0 + sw t0, (a0) + addi a0, a0, 4 + bnez a4, 1b + + ret +endfunc + +#if (__riscv_xlen == 64) +func ff_flac_lpc32_rvv, zve32x + addi t2, a2, -16 + ble t2, zero, ff_flac_lpc32_rvv_simple + vsetivli zero, 1, e64, m1, ta, ma + vmv.s.x v0, zero + vsetvli zero, a2, e32, m8, ta, ma + vle32.v v8, (a1) + sub a4, a4, a2 + vle32.v v16, (a0) + sh2add a0, a2, a0 +1: + vsetvli t1, zero, e32, m4, ta, ma + vwmul.vv v24, v8, v16 + vsetvli zero, t2, e32, m4, tu, ma + vwmacc.vv v24, v12, v20 + vsetvli t1, zero, e64, m8, ta, ma + vredsum.vs v24, v24, v0 + lw t0, (a0) + addi a4, a4, -1 + vmv.x.s t1, v24 + vsetvli zero, a2, e32, m8, ta, ma + sra t1, t1, a3 + add t0, t0, t1 + vslide1down.vx v16, v16, t0 + sw t0, (a0) + addi a0, a0, 4 + bnez a4, 1b + + ret +endfunc + +func ff_flac_lpc32_rvv_simple, zve32x + vsetivli zero, 1, e64, m1, ta, ma + vmv.s.x v0, zero + vsetvli zero, a2, e32, m4, ta, ma + vle32.v v8, (a1) + sub a4, a4, a2 + vle32.v v16, (a0) + sh2add a0, a2, a0 +1: + vwmul.vv v24, v8, v16 + vsetvli zero, zero, e64, m8, ta, ma + vredsum.vs v24, v24, v0 + lw t0, (a0) + addi a4, a4, -1 + vmv.x.s t1, v24 + vsetvli zero, zero, e32, m4, ta, ma + sra t1, t1, a3 + add t0, t0, t1 + vslide1down.vx v16, v16, t0 + sw t0, (a0) + addi a0, a0, 4 + bnez a4, 1b + + ret +endfunc + +func ff_flac_decorrelate_indep2_16_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m8, ta, ma + vle32.v v0, (a1) + sub a3, a3, t0 + vle32.v v8, (a2) + sh2add a1, t0, a1 + vsll.vx v0, v0, a4 + sh2add a2, t0, a2 + vsll.vx v8, v8, a4 + vsetvli zero, zero, e16, m4, ta, ma + vncvt.x.x.w v16, v0 + vncvt.x.x.w v20, v8 + vsseg2e16.v v16, (a0) + sh2add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + +func ff_flac_decorrelate_indep4_16_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld t1, 16(a1) + ld t2, 24(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m4, ta, ma + vle32.v v0, (a1) + sub a3, a3, t0 + vle32.v v4, (a2) + sh2add a1, t0, a1 + vsll.vx v0, v0, a4 + sh2add a2, t0, a2 + vle32.v v8, (t1) + sh2add t1, t0, t1 + vsll.vx v4, v4, a4 + vle32.v v12, (t2) + sh2add t2, t0, t2 + vsll.vx v8, v8, a4 + vsll.vx v12, v12, a4 + vsetvli zero, zero, e16, m2, ta, ma + vncvt.x.x.w v16, v0 + vncvt.x.x.w v18, v4 + vncvt.x.x.w v20, v8 + vncvt.x.x.w v22, v12 + vsseg4e16.v v16, (a0) + sh3add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + +func ff_flac_decorrelate_indep6_16_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld t1, 16(a1) + ld t2, 24(a1) + ld t3, 32(a1) + ld t4, 40(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m2, ta, ma + vle32.v v0, (a1) + sub a3, a3, t0 + vle32.v v2, (a2) + sh2add a1, t0, a1 + vsll.vx v0, v0, a4 + sh2add a2, t0, a2 + vle32.v v4, (t1) + sh2add t1, t0, t1 + vsll.vx v2, v2, a4 + vle32.v v6, (t2) + sh2add t2, t0, t2 + vsll.vx v4, v4, a4 + vle32.v v8, (t3) + sh2add t3, t0, t3 + vsll.vx v6, v6, a4 + vle32.v v10, (t4) + sh2add t4, t0, t4 + vsll.vx v8, v8, a4 + slli t0, t0, 2 + vsll.vx v10, v10, a4 + sh1add t0, t0, t0 // t0 *= 3 + vsetvli zero, zero, e16, m1, ta, ma + vncvt.x.x.w v16, v0 + vncvt.x.x.w v17, v2 + vncvt.x.x.w v18, v4 + vncvt.x.x.w v19, v6 + vncvt.x.x.w v20, v8 + vncvt.x.x.w v21, v10 + vsseg6e16.v v16, (a0) + add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + +func ff_flac_decorrelate_indep8_16_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld t1, 16(a1) + ld t2, 24(a1) + ld t3, 32(a1) + ld t4, 40(a1) + ld t5, 48(a1) + ld t6, 56(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m2, ta, ma + vle32.v v0, (a1) + sub a3, a3, t0 + vle32.v v2, (a2) + sh2add a1, t0, a1 + vsll.vx v0, v0, a4 + vle32.v v4, (t1) + sh2add a2, t0, a2 + vsll.vx v2, v2, a4 + sh2add t1, t0, t1 + vle32.v v6, (t2) + vsll.vx v4, v4, a4 + sh2add t2, t0, t2 + vle32.v v8, (t3) + sh2add t3, t0, t3 + vsll.vx v6, v6, a4 + vle32.v v10, (t4) + sh2add t4, t0, t4 + vsll.vx v8, v8, a4 + vle32.v v12, (t5) + sh2add t5, t0, t5 + vsll.vx v10, v10, a4 + vle32.v v14, (t6) + sh2add t6, t0, t6 + vsll.vx v12, v12, a4 + slli t0, t0, 4 + vsll.vx v14, v14, a4 + vsetvli zero, zero, e16, m1, ta, ma + vncvt.x.x.w v16, v0 + vncvt.x.x.w v17, v2 + vncvt.x.x.w v18, v4 + vncvt.x.x.w v19, v6 + vncvt.x.x.w v20, v8 + vncvt.x.x.w v21, v10 + vncvt.x.x.w v22, v12 + vncvt.x.x.w v23, v14 + vsseg8e16.v v16, (a0) + add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + + + +func ff_flac_decorrelate_ls_16_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m8, ta, ma + vle32.v v0, (a1) + sub a3, a3, t0 + vle32.v v8, (a2) + sh2add a1, t0, a1 + vsll.vx v0, v0, a4 + sh2add a2, t0, a2 + vsll.vx v8, v8, a4 + vsub.vv v8, v0, v8 + vsetvli zero, zero, e16, m4, ta, ma + vncvt.x.x.w v16, v0 + vncvt.x.x.w v20, v8 + vsseg2e16.v v16, (a0) + sh2add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + +func ff_flac_decorrelate_rs_16_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m8, ta, ma + vle32.v v0, (a1) + sub a3, a3, t0 + vle32.v v8, (a2) + sh2add a1, t0, a1 + vsll.vx v0, v0, a4 + sh2add a2, t0, a2 + vsll.vx v8, v8, a4 + vadd.vv v0, v0, v8 + vsetvli zero, zero, e16, m4, ta, ma + vncvt.x.x.w v16, v0 + vncvt.x.x.w v20, v8 + vsseg2e16.v v16, (a0) + sh2add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + +func ff_flac_decorrelate_ms_16_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m8, ta, ma + vle32.v v8, (a2) + sub a3, a3, t0 + vle32.v v0, (a1) + sh2add a1, t0, a1 + vsra.vi v16, v8, 1 + sh2add a2, t0, a2 + vsub.vv v24, v0, v16 + vadd.vv v16, v24, v8 + vsll.vx v8, v24, a4 + vsll.vx v0, v16, a4 + vsetvli zero, zero, e16, m4, ta, ma + vncvt.x.x.w v0, v0 + vncvt.x.x.w v4, v8 + vsseg2e16.v v0, (a0) + sh2add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + +func ff_flac_decorrelate_indep2_32_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m4, ta, ma + vle32.v v0, (a1) + sub a3, a3, t0 + vle32.v v4, (a2) + sh2add a1, t0, a1 + vsll.vx v0, v0, a4 + sh2add a2, t0, a2 + vsll.vx v4, v4, a4 + vsseg2e32.v v0, (a0) + sh3add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + +func ff_flac_decorrelate_indep4_32_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld t1, 16(a1) + ld t2, 24(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m2, ta, ma + vle32.v v0, (a1) + sub a3, a3, t0 + vle32.v v2, (a2) + sh2add a1, t0, a1 + vsll.vx v0, v0, a4 + sh2add a2, t0, a2 + vle32.v v4, (t1) + sh2add t1, t0, t1 + vsll.vx v2, v2, a4 + vle32.v v6, (t2) + sh2add t2, t0, t2 + vsll.vx v4, v4, a4 + slli t0, t0, 4 + vsll.vx v6, v6, a4 + vsseg4e32.v v0, (a0) + add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + +func ff_flac_decorrelate_indep6_32_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld t1, 16(a1) + ld t2, 24(a1) + ld t3, 32(a1) + ld t4, 40(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m1, ta, ma + vle32.v v0, (a1) + sub a3, a3, t0 + vle32.v v1, (a2) + sh2add a1, t0, a1 + vsll.vx v0, v0, a4 + sh2add a2, t0, a2 + vle32.v v2, (t1) + sh2add t1, t0, t1 + vsll.vx v1, v1, a4 + vle32.v v3, (t2) + sh2add t2, t0, t2 + vsll.vx v2, v2, a4 + vle32.v v4, (t3) + sh2add t3, t0, t3 + vsll.vx v3, v3, a4 + vle32.v v5, (t4) + sh2add t4, t0, t4 + vsll.vx v4, v4, a4 + slli t0, t0, 3 + vsll.vx v5, v5, a4 + sh1add t0, t0, t0 // t0 *= 3 + vsseg6e32.v v0, (a0) + add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + +func ff_flac_decorrelate_indep8_32_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld t1, 16(a1) + ld t2, 24(a1) + ld t3, 32(a1) + ld t4, 40(a1) + ld t5, 48(a1) + ld t6, 56(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m1, ta, ma + vle32.v v0, (a1) + sub a3, a3, t0 + vle32.v v1, (a2) + sh2add a1, t0, a1 + vsll.vx v0, v0, a4 + vle32.v v2, (t1) + sh2add a2, t0, a2 + vsll.vx v1, v1, a4 + sh2add t1, t0, t1 + vle32.v v3, (t2) + vsll.vx v2, v2, a4 + sh2add t2, t0, t2 + vle32.v v4, (t3) + sh2add t3, t0, t3 + vsll.vx v3, v3, a4 + vle32.v v5, (t4) + sh2add t4, t0, t4 + vsll.vx v4, v4, a4 + vle32.v v6, (t5) + sh2add t5, t0, t5 + vsll.vx v5, v5, a4 + vle32.v v7, (t6) + sh2add t6, t0, t6 + vsll.vx v6, v6, a4 + slli t0, t0, 5 + vsll.vx v7, v7, a4 + vsseg8e32.v v0, (a0) + add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + +func ff_flac_decorrelate_ls_32_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m4, ta, ma + vle32.v v0, (a1) + sub a3, a3, t0 + vle32.v v4, (a2) + sh2add a1, t0, a1 + vsll.vx v0, v0, a4 + sh2add a2, t0, a2 + vsll.vx v4, v4, a4 + vsub.vv v4, v0, v4 + vsseg2e32.v v0, (a0) + sh3add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + +func ff_flac_decorrelate_rs_32_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m4, ta, ma + vle32.v v0, (a1) + sub a3, a3, t0 + vle32.v v4, (a2) + sh2add a1, t0, a1 + vsll.vx v0, v0, a4 + sh2add a2, t0, a2 + vsll.vx v4, v4, a4 + vadd.vv v0, v0, v4 + vsseg2e32.v v0, (a0) + sh3add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + +func ff_flac_decorrelate_ms_32_rvv, zve32x + ld a0, (a0) + ld a2, 8(a1) + ld a1, (a1) +1: + vsetvli t0, a3, e32, m4, ta, ma + vle32.v v4, (a2) + sub a3, a3, t0 + vle32.v v0, (a1) + sh2add a1, t0, a1 + vsra.vi v8, v4, 1 + sh2add a2, t0, a2 + vsub.vv v12, v0, v8 + vadd.vv v8, v12, v4 + vsll.vx v4, v12, a4 + vsll.vx v0, v8, a4 + vsseg2e32.v v0, (a0) + sh3add a0, t0, a0 + bnez a3, 1b + + ret +endfunc +#endif diff --git a/libavcodec/riscv/fmtconvert_init.c b/libavcodec/riscv/fmtconvert_init.c index 2ded9d36153..f5eeafba457 100644 --- a/libavcodec/riscv/fmtconvert_init.c +++ b/libavcodec/riscv/fmtconvert_init.c @@ -36,7 +36,7 @@ av_cold void ff_fmt_convert_init_riscv(FmtConvertContext *c) #if HAVE_RVV int flags = av_get_cpu_flags(); - if (flags & AV_CPU_FLAG_RVV_F32) { + if ((flags & AV_CPU_FLAG_RVV_F32) && (flags & AV_CPU_FLAG_RVB_ADDR)) { c->int32_to_float_fmul_scalar = ff_int32_to_float_fmul_scalar_rvv; c->int32_to_float_fmul_array8 = ff_int32_to_float_fmul_array8_rvv; } diff --git a/libavcodec/riscv/fmtconvert_rvv.S b/libavcodec/riscv/fmtconvert_rvv.S index 51365753d93..d0e2f106d5b 100644 --- a/libavcodec/riscv/fmtconvert_rvv.S +++ b/libavcodec/riscv/fmtconvert_rvv.S @@ -24,7 +24,7 @@ func ff_int32_to_float_fmul_scalar_rvv, zve32f NOHWF fmv.w.x fa0, a2 NOHWF mv a2, a3 1: - vsetvli t0, a2, e32, m1, ta, ma + vsetvli t0, a2, e32, m8, ta, ma vle32.v v24, (a1) sub a2, a2, t0 vfcvt.f.x.v v24, v24 diff --git a/libavcodec/x86/dct_init.c b/libavcodec/riscv/g722dsp_init.c similarity index 66% rename from libavcodec/x86/dct_init.c rename to libavcodec/riscv/g722dsp_init.c index d0e4b34dd32..77e29bfb566 100644 --- a/libavcodec/x86/dct_init.c +++ b/libavcodec/riscv/g722dsp_init.c @@ -1,4 +1,6 @@ /* + * Copyright © 2023 Rémi Denis-Courmont. + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -17,20 +19,22 @@ */ #include "config.h" + +#include + #include "libavutil/attributes.h" #include "libavutil/cpu.h" -#include "libavutil/x86/cpu.h" -#include "libavcodec/dct.h" +#include "libavutil/riscv/cpu.h" +#include "libavcodec/g722dsp.h" -void ff_dct32_float_sse2(FFTSample *out, const FFTSample *in); -void ff_dct32_float_avx(FFTSample *out, const FFTSample *in); +extern void ff_g722_apply_qmf_rvv(const int16_t *prev_samples, int xout[2]); -av_cold void ff_dct_init_x86(DCTContext *s) +av_cold void ff_g722dsp_init_riscv(G722DSPContext *dsp) { - int cpu_flags = av_get_cpu_flags(); +#if HAVE_RVV + int flags = av_get_cpu_flags(); - if (EXTERNAL_SSE2(cpu_flags)) - s->dct32 = ff_dct32_float_sse2; - if (EXTERNAL_AVX_FAST(cpu_flags)) - s->dct32 = ff_dct32_float_avx; + if ((flags & AV_CPU_FLAG_RVV_I32) && ff_get_rv_vlenb() >= 16) + dsp->apply_qmf = ff_g722_apply_qmf_rvv; +#endif } diff --git a/libavcodec/riscv/g722dsp_rvv.S b/libavcodec/riscv/g722dsp_rvv.S new file mode 100644 index 00000000000..981d5cecd8a --- /dev/null +++ b/libavcodec/riscv/g722dsp_rvv.S @@ -0,0 +1,68 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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/riscv/asm.S" + +func ff_g722_apply_qmf_rvv, zve32x + lla t0, qmf_coeffs + vsetivli zero, 12, e16, m2, ta, ma + vlseg2e16.v v28, (a0) + addi t1, t0, 12 * 2 + vle16.v v24, (t0) + vle16.v v26, (t1) + vwmul.vv v16, v28, v24 + vwmul.vv v20, v30, v26 + vsetivli zero, 12, e32, m4, ta, ma + vmv.s.x v10, zero + vredsum.vs v8, v16, v10 + vredsum.vs v9, v20, v10 + vmv.x.s t0, v8 + vmv.x.s t1, v9 + sw t0, 4(a1) + sw t1, 0(a1) + ret +endfunc + +const qmf_coeffs, align=2 + .short 3 + .short -11 + .short 12 + .short 32 + .short -210 + .short 951 + .short 3876 + .short -805 + .short 362 + .short -156 + .short 53 + .short -11 + .short -11 + .short 53 + .short -156 + .short 362 + .short -805 + .short 3876 + .short 951 + .short -210 + .short 32 + .short 12 + .short -11 + .short 3 +endconst diff --git a/libavcodec/riscv/h264_chroma_init_riscv.c b/libavcodec/riscv/h264_chroma_init_riscv.c new file mode 100644 index 00000000000..e6fe5f6ed63 --- /dev/null +++ b/libavcodec/riscv/h264_chroma_init_riscv.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 SiFive, Inc. All rights reserved. + * + * 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 + +#include "libavutil/attributes.h" +#include "libavutil/riscv/cpu.h" +#include "libavcodec/h264chroma.h" +#include "config.h" + +void h264_put_chroma_mc8_rvv(uint8_t *p_dst, const uint8_t *p_src, ptrdiff_t stride, int h, int x, int y); +void h264_avg_chroma_mc8_rvv(uint8_t *p_dst, const uint8_t *p_src, ptrdiff_t stride, int h, int x, int y); +void h264_put_chroma_mc4_rvv(uint8_t *p_dst, const uint8_t *p_src, ptrdiff_t stride, int h, int x, int y); +void h264_avg_chroma_mc4_rvv(uint8_t *p_dst, const uint8_t *p_src, ptrdiff_t stride, int h, int x, int y); +void h264_put_chroma_mc2_rvv(uint8_t *p_dst, const uint8_t *p_src, ptrdiff_t stride, int h, int x, int y); +void h264_avg_chroma_mc2_rvv(uint8_t *p_dst, const uint8_t *p_src, ptrdiff_t stride, int h, int x, int y); + +av_cold void ff_h264chroma_init_riscv(H264ChromaContext *c, int bit_depth) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if (bit_depth == 8 && (flags & AV_CPU_FLAG_RVV_I32) && + (flags & AV_CPU_FLAG_RVB_ADDR) && ff_get_rv_vlenb() >= 16) { + c->put_h264_chroma_pixels_tab[0] = h264_put_chroma_mc8_rvv; + c->avg_h264_chroma_pixels_tab[0] = h264_avg_chroma_mc8_rvv; + c->put_h264_chroma_pixels_tab[1] = h264_put_chroma_mc4_rvv; + c->avg_h264_chroma_pixels_tab[1] = h264_avg_chroma_mc4_rvv; + c->put_h264_chroma_pixels_tab[2] = h264_put_chroma_mc2_rvv; + c->avg_h264_chroma_pixels_tab[2] = h264_avg_chroma_mc2_rvv; + } +#endif +} diff --git a/libavcodec/riscv/h264_mc_chroma.S b/libavcodec/riscv/h264_mc_chroma.S new file mode 100644 index 00000000000..ce99bda44dc --- /dev/null +++ b/libavcodec/riscv/h264_mc_chroma.S @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2023 SiFive, Inc. All rights reserved. + * + * 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/riscv/asm.S" + +.macro do_chroma_mc type unroll + csrw vxrm, zero + slli t2, a5, 3 + mul t1, a5, a4 + sh3add a5, a4, t2 + slli a4, a4, 3 + sub a5, t1, a5 + sub a7, a4, t1 + addi a6, a5, 64 + sub t0, t2, t1 + vsetvli t3, t6, e8, m1, ta, mu + beqz t1, 2f + blez a3, 8f + li t4, 0 + li t2, 0 + li t5, 1 + addi a5, t3, 1 + slli t3, a2, (1 + \unroll) +1: # if (xy != 0) + add a4, a1, t4 + vsetvli zero, a5, e8, m1, ta, ma + .ifc \unroll,1 + addi t2, t2, 4 + .else + addi t2, t2, 2 + .endif + vle8.v v10, (a4) + add a4, a4, a2 + vslide1down.vx v11, v10, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v8, v10, a6 + vwmaccu.vx v8, a7, v11 + vsetvli zero, a5, e8, m1, ta, ma + vle8.v v12, (a4) + vsetvli zero, t6, e8, m1, ta, ma + add a4, a4, a2 + vwmaccu.vx v8, t0, v12 + vsetvli zero, a5, e8, m1, ta, ma + vslide1down.vx v13, v12, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v10, v12, a6 + vwmaccu.vx v8, t1, v13 + vwmaccu.vx v10, a7, v13 + vsetvli zero, a5, e8, m1, ta, ma + vle8.v v14, (a4) + vsetvli zero, t6, e8, m1, ta, ma + add a4, a4, a2 + vwmaccu.vx v10, t0, v14 + vsetvli zero, a5, e8, m1, ta, ma + vslide1down.vx v15, v14, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v12, v14, a6 + vwmaccu.vx v10, t1, v15 + vwmaccu.vx v12, a7, v15 + vnclipu.wi v15, v8, 6 + .ifc \type,avg + vle8.v v9, (a0) + vaaddu.vv v15, v15, v9 + .endif + vse8.v v15, (a0) + add a0, a0, a2 + vnclipu.wi v8, v10, 6 + .ifc \type,avg + vle8.v v9, (a0) + vaaddu.vv v8, v8, v9 + .endif + add t4, t4, t3 + vse8.v v8, (a0) + add a0, a0, a2 + .ifc \unroll,1 + vsetvli zero, a5, e8, m1, ta, ma + vle8.v v14, (a4) + vsetvli zero, t6, e8, m1, ta, ma + add a4, a4, a2 + vwmaccu.vx v12, t0, v14 + vsetvli zero, a5, e8, m1, ta, ma + vslide1down.vx v15, v14, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v16, v14, a6 + vwmaccu.vx v12, t1, v15 + vwmaccu.vx v16, a7, v15 + vsetvli zero, a5, e8, m1, ta, ma + vle8.v v14, (a4) + vsetvli zero, t6, e8, m1, ta, ma + vwmaccu.vx v16, t0, v14 + vsetvli zero, a5, e8, m1, ta, ma + vslide1down.vx v14, v14, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmaccu.vx v16, t1, v14 + vnclipu.wi v8, v12, 6 + .ifc \type,avg + vle8.v v9, (a0) + vaaddu.vv v8, v8, v9 + .endif + vse8.v v8, (a0) + add a0, a0, a2 + vnclipu.wi v8, v16, 6 + .ifc \type,avg + vle8.v v9, (a0) + vaaddu.vv v8, v8, v9 + .endif + vse8.v v8, (a0) + add a0, a0, a2 + .endif + blt t2, a3, 1b + j 8f +2: + bnez a4, 4f + beqz t2, 4f + blez a3, 8f + li a4, 0 + li t1, 0 + slli a7, a2, (1 + \unroll) +3: # if ((x8 - xy) == 0 && (y8 -xy) != 0) + add a5, a1, a4 + vsetvli zero, zero, e8, m1, ta, ma + .ifc \unroll,1 + addi t1, t1, 4 + .else + addi t1, t1, 2 + .endif + vle8.v v8, (a5) + add a5, a5, a2 + add t2, a5, a2 + vwmulu.vx v10, v8, a6 + vle8.v v8, (a5) + vwmulu.vx v12, v8, a6 + vle8.v v9, (t2) + add t2, t2, a2 + add a5, t2, a2 + vwmaccu.vx v10, t0, v8 + add a4, a4, a7 + vwmaccu.vx v12, t0, v9 + vnclipu.wi v15, v10, 6 + vwmulu.vx v10, v9, a6 + vnclipu.wi v9, v12, 6 + .ifc \type,avg + vle8.v v16, (a0) + vaaddu.vv v15, v15, v16 + .endif + vse8.v v15, (a0) + add a0, a0, a2 + .ifc \type,avg + vle8.v v16, (a0) + vaaddu.vv v9, v9, v16 + .endif + vse8.v v9, (a0) + add a0, a0, a2 + .ifc \unroll,1 + vle8.v v8, (t2) + vle8.v v14, (a5) + vwmaccu.vx v10, t0, v8 + vwmulu.vx v12, v8, a6 + vnclipu.wi v8, v10, 6 + vwmaccu.vx v12, t0, v14 + .ifc \type,avg + vle8.v v16, (a0) + vaaddu.vv v8, v8, v16 + .endif + vse8.v v8, (a0) + add a0, a0, a2 + vnclipu.wi v8, v12, 6 + .ifc \type,avg + vle8.v v16, (a0) + vaaddu.vv v8, v8, v16 + .endif + vse8.v v8, (a0) + add a0, a0, a2 + .endif + blt t1, a3, 3b + j 8f +4: + beqz a4, 6f + bnez t2, 6f + blez a3, 8f + li a4, 0 + li t2, 0 + addi t0, t3, 1 + slli t1, a2, (1 + \unroll) +5: # if ((x8 - xy) != 0 && (y8 -xy) == 0) + add a5, a1, a4 + vsetvli zero, t0, e8, m1, ta, ma + .ifc \unroll,1 + addi t2, t2, 4 + .else + addi t2, t2, 2 + .endif + vle8.v v8, (a5) + add a5, a5, a2 + vslide1down.vx v9, v8, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v10, v8, a6 + vwmaccu.vx v10, a7, v9 + vsetvli zero, t0, e8, m1, ta, ma + vle8.v v8, (a5) + add a5, a5, a2 + vslide1down.vx v9, v8, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v12, v8, a6 + vwmaccu.vx v12, a7, v9 + vnclipu.wi v16, v10, 6 + .ifc \type,avg + vle8.v v18, (a0) + vaaddu.vv v16, v16, v18 + .endif + vse8.v v16, (a0) + add a0, a0, a2 + vnclipu.wi v10, v12, 6 + .ifc \type,avg + vle8.v v18, (a0) + vaaddu.vv v10, v10, v18 + .endif + add a4, a4, t1 + vse8.v v10, (a0) + add a0, a0, a2 + .ifc \unroll,1 + vsetvli zero, t0, e8, m1, ta, ma + vle8.v v8, (a5) + add a5, a5, a2 + vslide1down.vx v9, v8, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v14, v8, a6 + vwmaccu.vx v14, a7, v9 + vsetvli zero, t0, e8, m1, ta, ma + vle8.v v8, (a5) + vslide1down.vx v9, v8, t5 + vsetvli zero, t6, e8, m1, ta, ma + vwmulu.vx v12, v8, a6 + vnclipu.wi v8, v14, 6 + vwmaccu.vx v12, a7, v9 + .ifc \type,avg + vle8.v v18, (a0) + vaaddu.vv v8, v8, v18 + .endif + vse8.v v8, (a0) + add a0, a0, a2 + vnclipu.wi v8, v12, 6 + .ifc \type,avg + vle8.v v18, (a0) + vaaddu.vv v8, v8, v18 + .endif + vse8.v v8, (a0) + add a0, a0, a2 + .endif + blt t2, a3, 5b + j 8f +6: + blez a3, 8f + li a4, 0 + li t2, 0 + slli a7, a2, (1 + \unroll) +7: # the final else, none of the above conditions are met + add t0, a1, a4 + vsetvli zero, zero, e8, m1, ta, ma + add a5, a0, a4 + add a4, a4, a7 + .ifc \unroll,1 + addi t2, t2, 4 + .else + addi t2, t2, 2 + .endif + vle8.v v8, (t0) + add t0, t0, a2 + add t1, t0, a2 + vwmulu.vx v10, v8, a6 + vle8.v v8, (t0) + add t0, t1, a2 + vnclipu.wi v13, v10, 6 + vwmulu.vx v10, v8, a6 + .ifc \type,avg + vle8.v v18, (a5) + vaaddu.vv v13, v13, v18 + .endif + vse8.v v13, (a5) + add a5, a5, a2 + vnclipu.wi v8, v10, 6 + .ifc \type,avg + vle8.v v18, (a5) + vaaddu.vv v8, v8, v18 + .endif + vse8.v v8, (a5) + add a5, a5, a2 + .ifc \unroll,1 + vle8.v v9, (t1) + vle8.v v12, (t0) + vwmulu.vx v10, v9, a6 + vnclipu.wi v8, v10, 6 + vwmulu.vx v10, v12, a6 + .ifc \type,avg + vle8.v v18, (a5) + vaaddu.vv v8, v8, v18 + .endif + vse8.v v8, (a5) + add a5, a5, a2 + vnclipu.wi v8, v10, 6 + .ifc \type,avg + vle8.v v18, (a5) + vaaddu.vv v8, v8, v18 + .endif + vse8.v v8, (a5) + .endif + blt t2, a3, 7b +8: + ret +.endm + +func h264_put_chroma_mc_rvv, zve32x +11: + li a7, 3 + blt a3, a7, 12f + do_chroma_mc put 1 +12: + do_chroma_mc put 0 +endfunc + +func h264_avg_chroma_mc_rvv, zve32x +21: + li a7, 3 + blt a3, a7, 22f + do_chroma_mc avg 1 +22: + do_chroma_mc avg 0 +endfunc + +func h264_put_chroma_mc8_rvv, zve32x + li t6, 8 + j 11b +endfunc + +func h264_put_chroma_mc4_rvv, zve32x + li t6, 4 + j 11b +endfunc + +func h264_put_chroma_mc2_rvv, zve32x + li t6, 2 + j 11b +endfunc + +func h264_avg_chroma_mc8_rvv, zve32x + li t6, 8 + j 21b +endfunc + +func h264_avg_chroma_mc4_rvv, zve32x + li t6, 4 + j 21b +endfunc + +func h264_avg_chroma_mc2_rvv, zve32x + li t6, 2 + j 21b +endfunc diff --git a/libavcodec/riscv/huffyuvdsp_init.c b/libavcodec/riscv/huffyuvdsp_init.c new file mode 100644 index 00000000000..b49b3dc097f --- /dev/null +++ b/libavcodec/riscv/huffyuvdsp_init.c @@ -0,0 +1,41 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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 "config.h" +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavcodec/huffyuvdsp.h" + +void ff_add_int16_rvv(uint16_t *dst, const uint16_t *src, unsigned m, int w); +void ff_add_hfyu_left_pred_bgr32_rvv(uint8_t *dst, const uint8_t *src, + intptr_t w, uint8_t *left); + +av_cold void ff_huffyuvdsp_init_riscv(HuffYUVDSPContext *c, + enum AVPixelFormat pix_fmt) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if ((flags & AV_CPU_FLAG_RVV_I32) && (flags & AV_CPU_FLAG_RVB_ADDR)) { + c->add_int16 = ff_add_int16_rvv; + c->add_hfyu_left_pred_bgr32 = ff_add_hfyu_left_pred_bgr32_rvv; + } +#endif +} diff --git a/libavfilter/dnn/dnn_backend_native_layer_maximum.h b/libavcodec/riscv/huffyuvdsp_rvv.S similarity index 50% rename from libavfilter/dnn/dnn_backend_native_layer_maximum.h rename to libavcodec/riscv/huffyuvdsp_rvv.S index 523acbe05fc..9c4434907d9 100644 --- a/libavfilter/dnn/dnn_backend_native_layer_maximum.h +++ b/libavcodec/riscv/huffyuvdsp_rvv.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Guo Yejun + * Copyright © 2023 Rémi Denis-Courmont. * * This file is part of FFmpeg. * @@ -18,27 +18,36 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -/** - * @file - * DNN inference functions interface for native backend. - */ - - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_MAXIMUM_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_MAXIMUM_H +#include "libavutil/riscv/asm.S" -#include "libavformat/avio.h" -#include "dnn_backend_native.h" +func ff_add_int16_rvv, zve32x +1: + vsetvli t0, a3, e16, m8, ta, ma + vle16.v v16, (a0) + sub a3, a3, t0 + vle16.v v24, (a1) + sh1add a1, t0, a1 + vadd.vv v16, v16, v24 + vand.vx v16, v16, a2 + vse16.v v16, (a0) + sh1add a0, t0, a0 + bnez a3, 1b -typedef struct DnnLayerMaximumParams{ - union { - uint32_t u32; - float y; - }val; -} DnnLayerMaximumParams; + ret +endfunc -int ff_dnn_load_layer_maximum(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); -int ff_dnn_execute_layer_maximum(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); +func ff_add_hfyu_left_pred_bgr32_rvv, zve32x + vsetivli zero, 4, e8, m1, ta, ma + vle8.v v8, (a3) + sh2add a2, a2, a1 +1: + vle8.v v0, (a1) + vadd.vv v8, v8, v0 + addi a1, a1, 4 + vse8.v v8, (a0) + addi a0, a0, 4 + bne a2, a1, 1b -#endif + vse8.v v8, (a3) + ret +endfunc diff --git a/libavcodec/riscv/idctdsp_init.c b/libavcodec/riscv/idctdsp_init.c index e6e616a555d..4106d90c550 100644 --- a/libavcodec/riscv/idctdsp_init.c +++ b/libavcodec/riscv/idctdsp_init.c @@ -39,7 +39,7 @@ av_cold void ff_idctdsp_init_riscv(IDCTDSPContext *c, AVCodecContext *avctx, #if HAVE_RVV int flags = av_get_cpu_flags(); - if ((flags & AV_CPU_FLAG_RVV_I32) && ff_get_rv_vlenb() >= 16) { + if ((flags & AV_CPU_FLAG_RVV_I64) && ff_get_rv_vlenb() >= 16) { c->put_pixels_clamped = ff_put_pixels_clamped_rvv; c->put_signed_pixels_clamped = ff_put_signed_pixels_clamped_rvv; c->add_pixels_clamped = ff_add_pixels_clamped_rvv; diff --git a/libavcodec/riscv/idctdsp_rvv.S b/libavcodec/riscv/idctdsp_rvv.S index 06e64e65297..e93e6b5e7aa 100644 --- a/libavcodec/riscv/idctdsp_rvv.S +++ b/libavcodec/riscv/idctdsp_rvv.S @@ -20,60 +20,44 @@ #include "libavutil/riscv/asm.S" -func ff_put_pixels_clamped_rvv, zve32x - vsetivli zero, 8, e16, m1, ta, ma - vlseg8e16.v v24, (a0) -1: +func ff_put_pixels_clamped_rvv, zve64x + li t0, 8 * 8 + vsetvli zero, t0, e16, m8, ta, ma + vle16.v v24, (a0) /* RVV only has signed-signed and unsigned-unsigned clipping. * We need two steps for signed-to-unsigned clipping. */ - vsetvli t0, zero, e16, m8, ta, ma - vmax.vx v24, v24, zero - - vsetivli zero, 8, e8, mf2, ta, ma - vnclipu.wi v16, v24, 0 - vnclipu.wi v17, v25, 0 - vnclipu.wi v18, v26, 0 - vnclipu.wi v19, v27, 0 - vnclipu.wi v20, v28, 0 - vnclipu.wi v21, v29, 0 - vnclipu.wi v22, v30, 0 - vnclipu.wi v23, v31, 0 - vssseg8e8.v v16, (a1), a2 + vmax.vx v24, v24, zero + vsetvli zero, zero, e8, m4, ta, ma + vnclipu.wi v16, v24, 0 + vsetivli zero, 8, e8, mf2, ta, ma + vsse64.v v16, (a1), a2 ret endfunc -func ff_put_signed_pixels_clamped_rvv, zve32x - vsetivli zero, 8, e16, m1, ta, ma - vlseg8e16.v v24, (a0) - - li t1, 128 - vsetivli zero, 8, e8, mf2, ta, ma - vnclip.wi v16, v24, 0 - vnclip.wi v17, v25, 0 - vnclip.wi v18, v26, 0 - vnclip.wi v19, v27, 0 - vnclip.wi v20, v28, 0 - vnclip.wi v21, v29, 0 - vnclip.wi v22, v30, 0 - vnclip.wi v23, v31, 0 - vsetvli t0, zero, e8, m8, ta, ma - vadd.vx v16, v16, t1 - vsetivli zero, 8, e8, mf2, ta, ma - vssseg8e8.v v16, (a1), a2 +func ff_put_signed_pixels_clamped_rvv, zve64x + li t0, 8 * 8 + vsetvli zero, t0, e8, m4, ta, ma + vle16.v v24, (a0) + li t1, 128 + vnclip.wi v16, v24, 0 + vadd.vx v16, v16, t1 + vsetivli zero, 8, e8, mf2, ta, ma + vsse64.v v16, (a1), a2 ret endfunc -func ff_add_pixels_clamped_rvv, zve32x - vsetivli zero, 8, e8, mf2, ta, ma - vlseg8e16.v v24, (a0) - vlsseg8e8.v v16, (a1), a2 - vwaddu.wv v24, v24, v16 - vwaddu.wv v25, v25, v17 - vwaddu.wv v26, v26, v18 - vwaddu.wv v27, v27, v19 - vwaddu.wv v28, v28, v20 - vwaddu.wv v29, v29, v21 - vwaddu.wv v30, v30, v22 - vwaddu.wv v31, v31, v23 - j 1b +func ff_add_pixels_clamped_rvv, zve64x + vsetivli zero, 8, e8, mf2, ta, ma + li t0, 8 * 8 + vlse64.v v16, (a1), a2 + vsetvli zero, t0, e8, m4, ta, ma + vle16.v v24, (a0) + vwaddu.wv v24, v24, v16 + vsetvli zero, zero, e16, m8, ta, ma + vmax.vx v24, v24, zero + vsetvli zero, zero, e8, m4, ta, ma + vnclipu.wi v16, v24, 0 + vsetivli zero, 8, e8, mf2, ta, ma + vsse64.v v16, (a1), a2 + ret endfunc diff --git a/libavcodec/riscv/jpeg2000dsp_init.c b/libavcodec/riscv/jpeg2000dsp_init.c new file mode 100644 index 00000000000..e82ec47bdee --- /dev/null +++ b/libavcodec/riscv/jpeg2000dsp_init.c @@ -0,0 +1,40 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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 "config.h" +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavcodec/jpeg2000dsp.h" + +void ff_ict_float_rvv(void *src0, void *src1, void *src2, int csize); +void ff_rct_int_rvv(void *src0, void *src1, void *src2, int csize); + +av_cold void ff_jpeg2000dsp_init_riscv(Jpeg2000DSPContext *c) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if ((flags & AV_CPU_FLAG_RVV_I32) && (flags & AV_CPU_FLAG_RVB_ADDR)) { + if (flags & AV_CPU_FLAG_RVV_F32) + c->mct_decode[FF_DWT97] = ff_ict_float_rvv; + c->mct_decode[FF_DWT53] = ff_rct_int_rvv; + } +#endif +} diff --git a/libavcodec/riscv/jpeg2000dsp_rvv.S b/libavcodec/riscv/jpeg2000dsp_rvv.S new file mode 100644 index 00000000000..10efe6b0dba --- /dev/null +++ b/libavcodec/riscv/jpeg2000dsp_rvv.S @@ -0,0 +1,72 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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/riscv/asm.S" + +func ff_ict_float_rvv, zve32f + lla t0, ff_jpeg2000_f_ict_params + flw ft0, 0(t0) + flw ft1, 4(t0) + flw ft2, 8(t0) + flw ft3, 12(t0) +1: + vsetvli t0, a3, e32, m8, ta, ma + vle32.v v8, (a0) + sub a3, a3, t0 + vle32.v v16, (a1) + vle32.v v24, (a2) + vfmul.vf v0, v16, ft1 + vfmadd.vf v16, ft3, v8 + vfsub.vv v0, v8, v0 + vfmacc.vf v8, ft0, v24 + vfnmsac.vf v0, ft2, v24 + vse32.v v8, (a0) + sh2add a0, t0, a0 + vse32.v v0, (a1) + sh2add a1, t0, a1 + vse32.v v16, (a2) + sh2add a2, t0, a2 + bnez a3, 1b + + ret +endfunc + +func ff_rct_int_rvv, zve32x +1: + vsetvli t0, a3, e32, m8, ta, ma + vle32.v v16, (a1) + sub a3, a3, t0 + vle32.v v24, (a2) + vle32.v v8, (a0) + vadd.vv v0, v16, v24 + vsra.vi v0, v0, 2 + vsub.vv v0, v8, v0 + vadd.vv v8, v0, v24 + vadd.vv v24, v0, v16 + vse32.v v8, (a0) + sh2add a0, t0, a0 + vse32.v v0, (a1) + sh2add a1, t0, a1 + vse32.v v24, (a2) + sh2add a2, t0, a2 + bnez a3, 1b + + ret +endfunc diff --git a/libavcodec/riscv/llauddsp_init.c b/libavcodec/riscv/llauddsp_init.c new file mode 100644 index 00000000000..1924b368219 --- /dev/null +++ b/libavcodec/riscv/llauddsp_init.c @@ -0,0 +1,44 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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 "config.h" + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavcodec/lossless_audiodsp.h" + +int32_t ff_scalarproduct_and_madd_int16_rvv(int16_t *v1, const int16_t *v2, + const int16_t *v3, int len, + int mul); +int32_t ff_scalarproduct_and_madd_int32_rvv(int16_t *v1, const int32_t *v2, + const int16_t *v3, int len, + int mul); + +av_cold void ff_llauddsp_init_riscv(LLAudDSPContext *c) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if ((flags & AV_CPU_FLAG_RVV_I32) && (flags & AV_CPU_FLAG_RVB_ADDR)) { + c->scalarproduct_and_madd_int16 = ff_scalarproduct_and_madd_int16_rvv; + c->scalarproduct_and_madd_int32 = ff_scalarproduct_and_madd_int32_rvv; + } +#endif +} diff --git a/libavcodec/riscv/llauddsp_rvv.S b/libavcodec/riscv/llauddsp_rvv.S new file mode 100644 index 00000000000..55698648323 --- /dev/null +++ b/libavcodec/riscv/llauddsp_rvv.S @@ -0,0 +1,71 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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/riscv/asm.S" + +func ff_scalarproduct_and_madd_int16_rvv, zve32x + vsetvli t0, zero, e32, m8, ta, ma + vmv.v.x v0, zero +1: + vsetvli t0, a3, e16, m4, tu, ma + vle16.v v8, (a0) + sub a3, a3, t0 + vle16.v v16, (a1) + sh1add a1, t0, a1 + vwmacc.vv v0, v8, v16 + vle16.v v24, (a2) + sh1add a2, t0, a2 + vmacc.vx v8, a4, v24 + vse16.v v8, (a0) + sh1add a0, t0, a0 + bnez a3, 1b + + vsetvli t0, zero, e32, m8, ta, ma + vmv.s.x v8, zero + vredsum.vs v0, v0, v8 + vmv.x.s a0, v0 + ret +endfunc + +func ff_scalarproduct_and_madd_int32_rvv, zve32x + vsetvli t0, zero, e32, m8, ta, ma + vmv.v.x v0, zero +1: + vsetvli t0, a3, e32, m8, tu, ma + vle16.v v8, (a0) + sub a3, a3, t0 + vsext.vf2 v24, v8 + vle32.v v16, (a1) + sh2add a1, t0, a1 + vmacc.vv v0, v16, v24 + vsetvli zero, zero, e16, m4, ta, ma + vle16.v v24, (a2) + sh1add a2, t0, a2 + vmacc.vx v8, a4, v24 + vse16.v v8, (a0) + sh1add a0, t0, a0 + bnez a3, 1b + + vsetvli t0, zero, e32, m8, ta, ma + vmv.s.x v8, zero + vredsum.vs v0, v0, v8 + vmv.x.s a0, v0 + ret +endfunc diff --git a/libavcodec/arm/rdft_init_arm.c b/libavcodec/riscv/llviddsp_init.c similarity index 68% rename from libavcodec/arm/rdft_init_arm.c rename to libavcodec/riscv/llviddsp_init.c index 1c5d8beb61a..f042eeab32f 100644 --- a/libavcodec/arm/rdft_init_arm.c +++ b/libavcodec/riscv/llviddsp_init.c @@ -1,4 +1,6 @@ /* + * Copyright © 2023 Rémi Denis-Courmont. + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -16,18 +18,21 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "config.h" + #include "libavutil/attributes.h" #include "libavutil/cpu.h" -#include "libavutil/arm/cpu.h" - -#include "libavcodec/rdft.h" +#include "libavcodec/lossless_videodsp.h" -void ff_rdft_calc_neon(struct RDFTContext *s, FFTSample *z); +void ff_llvid_add_bytes_rvv(uint8_t *, uint8_t *src, ptrdiff_t w); -av_cold void ff_rdft_init_arm(RDFTContext *s) +av_cold void ff_llviddsp_init_riscv(LLVidDSPContext *c) { - int cpu_flags = av_get_cpu_flags(); +#if HAVE_RVV + int flags = av_get_cpu_flags(); - if (have_neon(cpu_flags)) - s->rdft_calc = ff_rdft_calc_neon; + if (flags & AV_CPU_FLAG_RVV_I32) { + c->add_bytes = ff_llvid_add_bytes_rvv; + } +#endif } diff --git a/libavcodec/riscv/llviddsp_rvv.S b/libavcodec/riscv/llviddsp_rvv.S new file mode 100644 index 00000000000..a4814837b93 --- /dev/null +++ b/libavcodec/riscv/llviddsp_rvv.S @@ -0,0 +1,36 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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/riscv/asm.S" + +func ff_llvid_add_bytes_rvv, zve32x +1: + vsetvli t0, a2, e8, m8, ta, ma + vle8.v v0, (a1) + sub a2, a2, t0 + vle8.v v8, (a0) + add a1, t0, a1 + vadd.vv v8, v0, v8 + vse8.v v8, (a0) + add a0, t0, a0 + bnez a2, 1b + + ret +endfunc diff --git a/libavcodec/riscv/llvidencdsp_init.c b/libavcodec/riscv/llvidencdsp_init.c new file mode 100644 index 00000000000..e35406dc419 --- /dev/null +++ b/libavcodec/riscv/llvidencdsp_init.c @@ -0,0 +1,39 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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 "config.h" + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavcodec/lossless_videoencdsp.h" + +void ff_llvidenc_diff_bytes_rvv(uint8_t *dst, const uint8_t *src1, + const uint8_t *src2, intptr_t w); + +av_cold void ff_llvidencdsp_init_riscv(LLVidEncDSPContext *c) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if (flags & AV_CPU_FLAG_RVV_I32) { + c->diff_bytes = ff_llvidenc_diff_bytes_rvv; + } +#endif +} diff --git a/libavformat/jpegxl_probe.h b/libavcodec/riscv/llvidencdsp_rvv.S similarity index 65% rename from libavformat/jpegxl_probe.h rename to libavcodec/riscv/llvidencdsp_rvv.S index 2960e81e11c..03421651277 100644 --- a/libavformat/jpegxl_probe.h +++ b/libavcodec/riscv/llvidencdsp_rvv.S @@ -1,6 +1,5 @@ /* - * Jpeg XL header verification - * Copyright (c) 2022 Leo Izen + * Copyright © 2023 Rémi Denis-Courmont. * * This file is part of FFmpeg. * @@ -19,14 +18,20 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVFORMAT_JPEGXL_PROBE_H -#define AVFORMAT_JPEGXL_PROBE_H +#include "libavutil/riscv/asm.S" -#include +func ff_llvidenc_diff_bytes_rvv, zve32x +1: + vsetvli t0, a3, e8, m8, ta, ma + vle8.v v0, (a1) + sub a3, a3, t0 + vle8.v v8, (a2) + add a1, t0, a1 + vsub.vv v8, v0, v8 + add a2, t0, a2 + vse8.v v8, (a0) + add a0, t0, a0 + bnez a3, 1b -#define FF_JPEGXL_CODESTREAM_SIGNATURE_LE 0x0aff -#define FF_JPEGXL_CONTAINER_SIGNATURE_LE 0x204c584a0c000000 - -int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen); - -#endif /* AVFORMAT_JPEGXL_PROBE_H */ + ret +endfunc diff --git a/libavcodec/riscv/lpc_init.c b/libavcodec/riscv/lpc_init.c new file mode 100644 index 00000000000..08efae4da7d --- /dev/null +++ b/libavcodec/riscv/lpc_init.c @@ -0,0 +1,43 @@ +/* + * Copyright © 2022 Rémi Denis-Courmont. + * + * 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 "config.h" + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/riscv/cpu.h" +#include "libavcodec/lpc.h" + +void ff_lpc_apply_welch_window_rvv(const int32_t *, ptrdiff_t, double *); +void ff_lpc_compute_autocorr_rvv(const double *, ptrdiff_t, int, double *); + +av_cold void ff_lpc_init_riscv(LPCContext *c) +{ +#if HAVE_RVV && (__riscv_xlen >= 64) + int flags = av_get_cpu_flags(); + + if ((flags & AV_CPU_FLAG_RVV_F64) && (flags & AV_CPU_FLAG_RVB_ADDR)) { + c->lpc_apply_welch_window = ff_lpc_apply_welch_window_rvv; + + if (ff_get_rv_vlenb() > c->max_order) + c->lpc_compute_autocorr = ff_lpc_compute_autocorr_rvv; + } +#endif +} diff --git a/libavcodec/riscv/lpc_rvv.S b/libavcodec/riscv/lpc_rvv.S new file mode 100644 index 00000000000..3fdf80ebed8 --- /dev/null +++ b/libavcodec/riscv/lpc_rvv.S @@ -0,0 +1,118 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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/riscv/asm.S" + +#if __riscv_xlen >= 64 +func ff_lpc_apply_welch_window_rvv, zve64d + vsetvli t0, zero, e64, m8, ta, ma + vid.v v0 + addi t2, a1, -1 + vfcvt.f.xu.v v0, v0 + li t3, 2 + fcvt.d.l ft2, t2 + srai t1, a1, 1 + fcvt.d.l ft3, t3 + li t4, 1 + fdiv.d ft0, ft3, ft2 # ft0 = c = 2. / (len - 1) + fcvt.d.l fa1, t4 # fa1 = 1. + fsub.d ft1, ft0, fa1 + vfrsub.vf v0, v0, ft1 # v0[i] = c - i - 1. +1: + vsetvli t0, t1, e64, m8, ta, ma + vfmul.vv v16, v0, v0 # no fused multipy-add as v0 is reused + sub t1, t1, t0 + vle32.v v8, (a0) + fcvt.d.l ft2, t0 + vfrsub.vf v16, v16, fa1 # v16 = 1. - w * w + sh2add a0, t0, a0 + vsetvli zero, zero, e32, m4, ta, ma + vfwcvt.f.x.v v24, v8 + vsetvli zero, zero, e64, m8, ta, ma + vfsub.vf v0, v0, ft2 # v0 -= vl + vfmul.vv v8, v24, v16 + vse64.v v8, (a2) + sh3add a2, t0, a2 + bnez t1, 1b + + andi t1, a1, 1 + beqz t1, 2f + + sd zero, (a2) + addi a0, a0, 4 + addi a2, a2, 8 +2: + vsetvli t0, zero, e64, m8, ta, ma + vid.v v0 + srai t1, a1, 1 + vfcvt.f.xu.v v0, v0 + fcvt.d.l ft1, t1 + fsub.d ft1, ft0, ft1 # ft1 = c - (len / 2) + vfadd.vf v0, v0, ft1 # v0[i] = c - (len / 2) + i +3: + vsetvli t0, t1, e64, m8, ta, ma + vfmul.vv v16, v0, v0 + sub t1, t1, t0 + vle32.v v8, (a0) + fcvt.d.l ft2, t0 + vfrsub.vf v16, v16, fa1 # v16 = 1. - w * w + sh2add a0, t0, a0 + vsetvli zero, zero, e32, m4, ta, ma + vfwcvt.f.x.v v24, v8 + vsetvli zero, zero, e64, m8, ta, ma + vfadd.vf v0, v0, ft2 # v0 += vl + vfmul.vv v8, v24, v16 + vse64.v v8, (a2) + sh3add a2, t0, a2 + bnez t1, 3b + + ret +endfunc + +func ff_lpc_compute_autocorr_rvv, zve64d + addi a2, a2, 1 + li t0, 1 + vsetvli zero, a2, e64, m8, ta, ma + fcvt.d.l ft0, t0 + vle64.v v0, (a0) + sh3add a0, a2, a0 # data += lag + vfmv.v.f v16, ft0 + bge a2, a1, 2f +1: + vfmv.f.s ft0, v0 + fld ft1, (a0) # ft1 = data[lag + i] + vfmacc.vf v16, ft0, v0 # v16[j] += data[i] * data[i + j] + addi a1, a1, -1 + vfslide1down.vf v0, v0, ft1 + addi a0, a0, 8 + bgt a1, a2, 1b # while (len > lag); +2: + vfmv.f.s ft0, v0 + vsetvli zero, a1, e64, m8, tu, ma + vfmacc.vf v16, ft0, v0 + addi a1, a1, -1 + vslide1down.vx v0, v0, zero + bnez a1, 2b # while (len > 0); + + vsetvli zero, a2, e64, m8, ta, ma + vse64.v v16, (a3) + ret +endfunc +#endif diff --git a/libavcodec/riscv/me_cmp_init.c b/libavcodec/riscv/me_cmp_init.c new file mode 100644 index 00000000000..858e2ccdb86 --- /dev/null +++ b/libavcodec/riscv/me_cmp_init.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2024 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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 "config.h" + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/riscv/cpu.h" +#include "libavcodec/me_cmp.h" +#include "libavcodec/mpegvideo.h" + +int ff_pix_abs16_rvv(MpegEncContext *v, const uint8_t *pix1, const uint8_t *pix2, + ptrdiff_t stride, int h); +int ff_pix_abs8_rvv(MpegEncContext *v, const uint8_t *pix1, const uint8_t *pix2, + ptrdiff_t stride, int h); +int ff_pix_abs16_x2_rvv(MpegEncContext *v, const uint8_t *pix1, const uint8_t *pix2, + ptrdiff_t stride, int h); +int ff_pix_abs8_x2_rvv(MpegEncContext *v, const uint8_t *pix1, const uint8_t *pix2, + ptrdiff_t stride, int h); +int ff_pix_abs16_y2_rvv(MpegEncContext *v, const uint8_t *pix1, const uint8_t *pix2, + ptrdiff_t stride, int h); +int ff_pix_abs8_y2_rvv(MpegEncContext *v, const uint8_t *pix1, const uint8_t *pix2, + ptrdiff_t stride, int h); + +int ff_sse16_rvv(MpegEncContext *v, const uint8_t *pix1, const uint8_t *pix2, + ptrdiff_t stride, int h); +int ff_sse8_rvv(MpegEncContext *v, const uint8_t *pix1, const uint8_t *pix2, + ptrdiff_t stride, int h); +int ff_sse4_rvv(MpegEncContext *v, const uint8_t *pix1, const uint8_t *pix2, + ptrdiff_t stride, int h); + +int ff_vsse16_rvv(MpegEncContext *c, const uint8_t *s1, const uint8_t *s2, ptrdiff_t stride, int h); +int ff_vsse8_rvv(MpegEncContext *c, const uint8_t *s1, const uint8_t *s2, ptrdiff_t stride, int h); +int ff_vsse_intra16_rvv(MpegEncContext *c, const uint8_t *s, const uint8_t *dummy, ptrdiff_t stride, int h); +int ff_vsse_intra8_rvv(MpegEncContext *c, const uint8_t *s, const uint8_t *dummy, ptrdiff_t stride, int h); +int ff_vsad16_rvv(MpegEncContext *c, const uint8_t *s1, const uint8_t *s2, ptrdiff_t stride, int h); +int ff_vsad8_rvv(MpegEncContext *c, const uint8_t *s1, const uint8_t *s2, ptrdiff_t stride, int h); +int ff_vsad_intra16_rvv(MpegEncContext *c, const uint8_t *s, const uint8_t *dummy, ptrdiff_t stride, int h); +int ff_vsad_intra8_rvv(MpegEncContext *c, const uint8_t *s, const uint8_t *dummy, ptrdiff_t stride, int h); +int ff_nsse16_rvv(int multiplier, const uint8_t *s1, const uint8_t *s2, + ptrdiff_t stride, int h); +int ff_nsse8_rvv(int multiplier, const uint8_t *s1, const uint8_t *s2, + ptrdiff_t stride, int h); + +static int nsse16_rvv_wrapper(MpegEncContext *c, const uint8_t *s1, const uint8_t *s2, + ptrdiff_t stride, int h) +{ + if (c) + return ff_nsse16_rvv(c->avctx->nsse_weight, s1, s2, stride, h); + else + return ff_nsse16_rvv(8, s1, s2, stride, h); +} + +static int nsse8_rvv_wrapper(MpegEncContext *c, const uint8_t *s1, const uint8_t *s2, + ptrdiff_t stride, int h) +{ + if (c) + return ff_nsse8_rvv(c->avctx->nsse_weight, s1, s2, stride, h); + else + return ff_nsse8_rvv(8, s1, s2, stride, h); +} + +av_cold void ff_me_cmp_init_riscv(MECmpContext *c, AVCodecContext *avctx) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if (flags & AV_CPU_FLAG_RVV_I32 && ff_get_rv_vlenb() >= 16) { + c->pix_abs[0][0] = ff_pix_abs16_rvv; + c->sad[0] = ff_pix_abs16_rvv; + c->pix_abs[1][0] = ff_pix_abs8_rvv; + c->sad[1] = ff_pix_abs8_rvv; + c->pix_abs[0][1] = ff_pix_abs16_x2_rvv; + c->pix_abs[1][1] = ff_pix_abs8_x2_rvv; + c->pix_abs[0][2] = ff_pix_abs16_y2_rvv; + c->pix_abs[1][2] = ff_pix_abs8_y2_rvv; + + c->sse[0] = ff_sse16_rvv; + c->sse[1] = ff_sse8_rvv; + c->sse[2] = ff_sse4_rvv; + + c->vsse[0] = ff_vsse16_rvv; + c->vsse[1] = ff_vsse8_rvv; + c->vsse[4] = ff_vsse_intra16_rvv; + c->vsse[5] = ff_vsse_intra8_rvv; + c->vsad[0] = ff_vsad16_rvv; + c->vsad[1] = ff_vsad8_rvv; + c->vsad[4] = ff_vsad_intra16_rvv; + c->vsad[5] = ff_vsad_intra8_rvv; + + c->nsse[0] = nsse16_rvv_wrapper; + c->nsse[1] = nsse8_rvv_wrapper; + } +#endif +} diff --git a/libavcodec/riscv/me_cmp_rvv.S b/libavcodec/riscv/me_cmp_rvv.S new file mode 100644 index 00000000000..c9ae5bb6fc5 --- /dev/null +++ b/libavcodec/riscv/me_cmp_rvv.S @@ -0,0 +1,527 @@ +/* + * Copyright (c) 2024 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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/riscv/asm.S" + +.macro pix_abs_ret + vsetivli zero, 1, e32, m1, ta, ma + vmv.x.s a0, v0 + ret +.endm + +func ff_pix_abs16_rvv, zve32x + vsetivli zero, 1, e32, m1, ta, ma + vmv.s.x v0, zero +1: + vsetivli zero, 16, e8, m1, tu, ma + vle8.v v4, (a1) + vle8.v v12, (a2) + addi a4, a4, -1 + vwsubu.vv v16, v4, v12 + add a1, a1, a3 + vwsubu.vv v20, v12, v4 + vsetvli zero, zero, e16, m2, tu, ma + vmax.vv v16, v16, v20 + add a2, a2, a3 + vwredsum.vs v0, v16, v0 + bnez a4, 1b + + pix_abs_ret +endfunc + +func ff_pix_abs8_rvv, zve32x + vsetivli zero, 1, e32, m1, ta, ma + vmv.s.x v0, zero +1: + vsetivli zero, 8, e8, mf2, tu, ma + vle8.v v4, (a1) + vle8.v v12, (a2) + addi a4, a4, -1 + vwsubu.vv v16, v4, v12 + add a1, a1, a3 + vwsubu.vv v20, v12, v4 + vsetvli zero, zero, e16, m1, tu, ma + vmax.vv v16, v16, v20 + add a2, a2, a3 + vwredsum.vs v0, v16, v0 + bnez a4, 1b + + pix_abs_ret +endfunc + +func ff_pix_abs16_x2_rvv, zve32x + csrwi vxrm, 0 + vsetivli zero, 1, e32, m1, ta, ma + li t5, 1 + vmv.s.x v0, zero +1: + vsetivli zero, 17, e8, m2, tu, ma + vle8.v v12, (a2) + addi a4, a4, -1 + vslide1down.vx v24, v12, t5 + vsetivli zero, 16, e8, m1, tu, ma + vle8.v v4, (a1) + vaaddu.vv v12, v12, v24 + vwsubu.vv v16, v4, v12 + add a1, a1, a3 + vwsubu.vv v20, v12, v4 + vsetvli zero, zero, e16, m2, tu, ma + vmax.vv v16, v16, v20 + add a2, a2, a3 + vwredsum.vs v0, v16, v0 + bnez a4, 1b + + pix_abs_ret +endfunc + +func ff_pix_abs8_x2_rvv, zve32x + csrwi vxrm, 0 + vsetivli zero, 1, e32, m1, ta, ma + li t5, 1 + vmv.s.x v0, zero +1: + vsetivli zero, 9, e8, m1, tu, ma + vle8.v v12, (a2) + addi a4, a4, -1 + vslide1down.vx v24, v12, t5 + vsetivli zero, 8, e8, mf2, tu, ma + vle8.v v4, (a1) + vaaddu.vv v12, v12, v24 + vwsubu.vv v16, v4, v12 + add a1, a1, a3 + vwsubu.vv v20, v12, v4 + vsetvli zero, zero, e16, m1, tu, ma + vmax.vv v16, v16, v20 + add a2, a2, a3 + vwredsum.vs v0, v16, v0 + bnez a4, 1b + + pix_abs_ret +endfunc + +func ff_pix_abs16_y2_rvv, zve32x + csrwi vxrm, 0 + vsetivli zero, 1, e32, m1, ta, ma + add t1, a2, a3 + vmv.s.x v0, zero +1: + vsetivli zero, 16, e8, m1, tu, ma + vle8.v v12, (a2) + vle8.v v24, (t1) + addi a4, a4, -1 + vle8.v v4, (a1) + vaaddu.vv v12, v12, v24 + vwsubu.vv v16, v4, v12 + vwsubu.vv v20, v12, v4 + add a1, a1, a3 + vsetvli zero, zero, e16, m2, tu, ma + add a2, a2, a3 + vmax.vv v16, v16, v20 + add t1, t1, a3 + vwredsum.vs v0, v16, v0 + bnez a4, 1b + + pix_abs_ret +endfunc + +func ff_pix_abs8_y2_rvv, zve32x + csrwi vxrm, 0 + vsetivli zero, 1, e32, m1, ta, ma + add t1, a2, a3 + vmv.s.x v0, zero +1: + vsetivli zero, 8, e8, mf2, tu, ma + vle8.v v12, (a2) + vle8.v v24, (t1) + addi a4, a4, -1 + vle8.v v4, (a1) + vaaddu.vv v12, v12, v24 + vwsubu.vv v16, v4, v12 + vwsubu.vv v20, v12, v4 + add a1, a1, a3 + vsetvli zero, zero, e16, m1, tu, ma + add a2, a2, a3 + vmax.vv v16, v16, v20 + add t1, t1, a3 + vwredsum.vs v0, v16, v0 + bnez a4, 1b + + pix_abs_ret +endfunc + +func ff_sse16_rvv, zve32x + vsetivli t0, 16, e32, m4, ta, ma + vmv.v.x v24, zero + vmv.s.x v0, zero +1: + vsetvli zero, zero, e8, m1, tu, ma + vle8.v v4, (a1) + vle8.v v12, (a2) + addi a4, a4, -1 + vwsubu.vv v16, v4, v12 + vsetvli zero, zero, e16, m2, tu, ma + vwmacc.vv v24, v16, v16 + add a1, a1, a3 + add a2, a2, a3 + bnez a4, 1b + + vsetvli zero, zero, e32, m4, tu, ma + vredsum.vs v0, v24, v0 + vmv.x.s a0, v0 + ret +endfunc + +func ff_sse8_rvv, zve32x + vsetivli t0, 8, e32, m2, ta, ma + vmv.v.x v24, zero + vmv.s.x v0, zero +1: + vsetvli zero, zero, e8, mf2, tu, ma + vle8.v v4, (a1) + vle8.v v12, (a2) + addi a4, a4, -1 + vwsubu.vv v16, v4, v12 + vsetvli zero, zero, e16, m1, tu, ma + vwmacc.vv v24, v16, v16 + add a1, a1, a3 + add a2, a2, a3 + bnez a4, 1b + + vsetvli zero, zero, e32, m2, tu, ma + vredsum.vs v0, v24, v0 + vmv.x.s a0, v0 + ret +endfunc + +func ff_sse4_rvv, zve32x + vsetivli t0, 4, e32, m1, ta, ma + vmv.v.x v24, zero + vmv.s.x v0, zero +1: + vsetvli zero, zero, e8, mf4, tu, ma + vle8.v v4, (a1) + vle8.v v12, (a2) + addi a4, a4, -1 + vwsubu.vv v16, v4, v12 + vsetvli zero, zero, e16, mf2, tu, ma + vwmacc.vv v24, v16, v16 + add a1, a1, a3 + add a2, a2, a3 + bnez a4, 1b + + vsetvli zero, zero, e32, m1, tu, ma + vredsum.vs v0, v24, v0 + vmv.x.s a0, v0 + ret +endfunc + +.macro vabsaddu dst src tmp + vneg.v \tmp, \src + vmax.vv \tmp, \src, \tmp + vwaddu.wv \dst, \dst, \tmp +.endm + +.macro vsad_vsse16 type + vsetivli t0, 16, e32, m4, ta, ma + addi a4, a4, -1 + add t1, a1, a3 + add t2, a2, a3 + vmv.v.x v24, zero + vmv.s.x v0, zero +1: + vsetvli zero, zero, e8, m1, tu, ma + vle8.v v4, (a1) + vle8.v v8, (t1) + vle8.v v12, (a2) + vle8.v v16, (t2) + addi a4, a4, -1 + vwsubu.vv v28, v4, v12 + vwsubu.wv v12, v28, v8 + vwaddu.wv v28, v12, v16 + vsetvli zero, zero, e16, m2, tu, ma + +.ifc \type,abs + vabsaddu v24, v28, v12 +.endif +.ifc \type,square + vwmacc.vv v24, v28, v28 +.endif + + add a1, a1, a3 + add a2, a2, a3 + add t1, t1, a3 + add t2, t2, a3 + bnez a4, 1b + + vsetvli zero, zero, e32, m4, tu, ma + vredsum.vs v0, v24, v0 + vmv.x.s a0, v0 + ret +.endm + +.macro vsad_vsse8 type + vsetivli t0, 8, e32, m2, ta, ma + addi a4, a4, -1 + add t1, a1, a3 + add t2, a2, a3 + vmv.v.x v24, zero + vmv.s.x v0, zero +1: + vsetvli zero, zero, e8, mf2, tu, ma + vle8.v v4, (a1) + vle8.v v8, (t1) + vle8.v v12, (a2) + vle8.v v16, (t2) + addi a4, a4, -1 + vwsubu.vv v28, v4, v12 + vwsubu.wv v12, v28, v8 + vwaddu.wv v28, v12, v16 + vsetvli zero, zero, e16, m1, tu, ma + +.ifc \type,abs + vabsaddu v24, v28, v12 +.endif +.ifc \type,square + vwmacc.vv v24, v28, v28 +.endif + + add a1, a1, a3 + add a2, a2, a3 + add t1, t1, a3 + add t2, t2, a3 + bnez a4, 1b + + vsetvli zero, zero, e32, m2, tu, ma + vredsum.vs v0, v24, v0 + vmv.x.s a0, v0 + ret +.endm + +.macro vsad_vsse_intra16 type + vsetivli t0, 16, e32, m4, ta, ma + addi a4, a4, -1 + add t1, a1, a3 + vmv.v.x v24, zero + vmv.s.x v0, zero +1: + vsetvli zero, zero, e8, m1, tu, ma + vle8.v v4, (a1) + vle8.v v12, (t1) + addi a4, a4, -1 + vwsubu.vv v16, v4, v12 + vsetvli zero, zero, e16, m2, tu, ma + +.ifc \type,abs + vabsaddu v24, v16, v12 +.endif +.ifc \type,square + vwmacc.vv v24, v16, v16 +.endif + + add a1, a1, a3 + add t1, t1, a3 + bnez a4, 1b + + vsetvli zero, zero, e32, m4, tu, ma + vredsum.vs v0, v24, v0 + vmv.x.s a0, v0 + ret +.endm + +.macro vsad_vsse_intra8 type + vsetivli t0, 8, e32, m2, ta, ma + addi a4, a4, -1 + add t1, a1, a3 + vmv.v.x v24, zero + vmv.s.x v0, zero +1: + vsetvli zero, zero, e8, mf2, tu, ma + vle8.v v4, (a1) + vle8.v v12, (t1) + addi a4, a4, -1 + vwsubu.vv v16, v4, v12 + vsetvli zero, zero, e16, m1, tu, ma + +.ifc \type,abs + vabsaddu v24, v16, v12 +.endif +.ifc \type,square + vwmacc.vv v24, v16, v16 +.endif + + add a1, a1, a3 + add t1, t1, a3 + bnez a4, 1b + + vsetvli zero, zero, e32, m2, tu, ma + vredsum.vs v0, v24, v0 + vmv.x.s a0, v0 + ret +.endm + +func ff_vsse16_rvv, zve32x + vsad_vsse16 square +endfunc + +func ff_vsse8_rvv, zve32x + vsad_vsse8 square +endfunc + +func ff_vsse_intra16_rvv, zve32x + vsad_vsse_intra16 square +endfunc + +func ff_vsse_intra8_rvv, zve32x + vsad_vsse_intra8 square +endfunc + +func ff_vsad16_rvv, zve32x + vsad_vsse16 abs +endfunc + +func ff_vsad8_rvv, zve32x + vsad_vsse8 abs +endfunc + +func ff_vsad_intra16_rvv, zve32x + vsad_vsse_intra16 abs +endfunc + +func ff_vsad_intra8_rvv, zve32x + vsad_vsse_intra8 abs +endfunc + +func ff_nsse16_rvv, zve32x + .macro squarediff16 + vsetivli zero, 16, e8, m1, tu, ma + vle8.v v4, (a1) + vle8.v v12, (a2) + vwsubu.vv v16, v4, v12 + vsetvli zero, zero, e16, m2, tu, ma + vwmacc.vv v24, v16, v16 + .endm + + .macro gradiff16 srcx srcv + vsetivli zero, 16, e8, m1, tu, ma + vle8.v v8, (\srcx) + vslide1down.vx v0, \srcv, t5 + vslide1down.vx v16, v8, t5 + vwsubu.vv v20, \srcv, v0 + vwsubu.wv v0, v20, v8 + vwaddu.wv v20, v0, v16 + vsetivli zero, 15, e16, m2, tu, ma + vneg.v v0, v20 + vmax.vv v0, v20, v0 + .endm + + csrwi vxrm, 0 + vsetivli t0, 16, e32, m4, ta, ma + addi a4, a4, -1 + li t5, 1 + vmv.v.x v24, zero + vmv.v.x v28, zero +1: + add t1, a1, a3 + add t2, a2, a3 + addi a4, a4, -1 + squarediff16 + gradiff16 t1, v4 + vwaddu.wv v28, v28, v0 + gradiff16 t2, v12 + vwsubu.wv v28, v28, v0 + add a1, a1, a3 + add a2, a2, a3 + bnez a4, 1b + + squarediff16 + vsetivli zero, 16, e32, m4, tu, ma + vmv.s.x v0, zero + vmv.s.x v4, zero + vredsum.vs v0, v24, v0 + vredsum.vs v4, v28, v4 + vmv.x.s t1, v0 + vmv.x.s t2, v4 + srai t3, t2, 31 + xor t2, t3, t2 + sub t2, t2, t3 + mul t2, t2, a0 + add a0, t2, t1 + + ret +endfunc + +func ff_nsse8_rvv, zve32x + .macro squarediff8 + vsetivli zero, 8, e8, mf2, tu, ma + vle8.v v4, (a1) + vle8.v v12, (a2) + vwsubu.vv v16, v4, v12 + vsetvli zero, zero, e16, m1, tu, ma + vwmacc.vv v24, v16, v16 + .endm + + .macro gradiff8 srcx srcv + vsetivli zero, 8, e8, mf2, tu, ma + vle8.v v8, (\srcx) + vslide1down.vx v0, \srcv, t5 + vslide1down.vx v16, v8, t5 + vwsubu.vv v20, \srcv, v0 + vwsubu.wv v0, v20, v8 + vwaddu.wv v20, v0, v16 + vsetivli zero, 7, e16, m1, tu, ma + vneg.v v0, v20 + vmax.vv v0, v20, v0 + .endm + + csrwi vxrm, 0 + vsetivli t0, 8, e32, m2, ta, ma + addi a4, a4, -1 + li t5, 1 + vmv.v.x v24, zero + vmv.v.x v28, zero +1: + add t1, a1, a3 + add t2, a2, a3 + addi a4, a4, -1 + squarediff8 + gradiff8 t1, v4 + vwaddu.wv v28, v28, v0 + gradiff8 t2, v12 + vwsubu.wv v28, v28, v0 + add a1, a1, a3 + add a2, a2, a3 + bnez a4, 1b + + squarediff8 + vsetivli zero, 8, e32, m2, tu, ma + vmv.s.x v0, zero + vmv.s.x v4, zero + vredsum.vs v0, v24, v0 + vredsum.vs v4, v28, v4 + vmv.x.s t1, v0 + vmv.x.s t2, v4 + srai t3, t2, 31 + xor t2, t3, t2 + sub t2, t2, t3 + mul t2, t2, a0 + add a0, t2, t1 + + ret +endfunc diff --git a/libavcodec/riscv/opusdsp_init.c b/libavcodec/riscv/opusdsp_init.c index 7fde9b1fa8b..88d8e77f0ef 100644 --- a/libavcodec/riscv/opusdsp_init.c +++ b/libavcodec/riscv/opusdsp_init.c @@ -25,30 +25,15 @@ #include "libavutil/riscv/cpu.h" #include "libavcodec/opusdsp.h" -void ff_opus_postfilter_rvv_128(float *data, int period, float *g, int len); -void ff_opus_postfilter_rvv_256(float *data, int period, float *g, int len); -void ff_opus_postfilter_rvv_512(float *data, int period, float *g, int len); -void ff_opus_postfilter_rvv_1024(float *data, int period, float *g, int len); +void ff_opus_postfilter_rvv(float *data, int period, float *g, int len); av_cold void ff_opus_dsp_init_riscv(OpusDSP *d) { #if HAVE_RVV int flags = av_get_cpu_flags(); - if (flags & AV_CPU_FLAG_RVV_F32) - switch (ff_get_rv_vlenb()) { - case 16: - d->postfilter = ff_opus_postfilter_rvv_128; - break; - case 32: - d->postfilter = ff_opus_postfilter_rvv_256; - break; - case 64: - d->postfilter = ff_opus_postfilter_rvv_512; - break; - case 128: - d->postfilter = ff_opus_postfilter_rvv_512; - break; - } + if ((flags & AV_CPU_FLAG_RVV_F32) && (flags & AV_CPU_FLAG_RVB_ADDR) && + (flags & AV_CPU_FLAG_RVB_BASIC)) + d->postfilter = ff_opus_postfilter_rvv; #endif } diff --git a/libavcodec/riscv/opusdsp_rvv.S b/libavcodec/riscv/opusdsp_rvv.S index b3d23a9de5d..9a8914c78d8 100644 --- a/libavcodec/riscv/opusdsp_rvv.S +++ b/libavcodec/riscv/opusdsp_rvv.S @@ -20,56 +20,41 @@ #include "libavutil/riscv/asm.S" -func ff_opus_postfilter_rvv_128, zve32f - lvtypei a5, e32, m2, ta, ma - j 1f -endfunc - -func ff_opus_postfilter_rvv_512, zve32f - lvtypei a5, e32, mf2, ta, ma - j 1f -endfunc - -func ff_opus_postfilter_rvv_1024, zve32f - lvtypei a5, e32, mf4, ta, ma - j 1f -endfunc - -func ff_opus_postfilter_rvv_256, zve32f - lvtypei a5, e32, m1, ta, ma +func ff_opus_postfilter_rvv, zve32f + flw fa0, 0(a2) // g0 + slli t1, a1, 2 + flw fa1, 4(a2) // g1 + sub t0, a0, t1 + flw fa2, 8(a2) // g2 + addi t1, t0, -2 * 4 // data - (period + 2) = initial &x4 + vsetivli zero, 4, e32, m4, ta, ma + addi t0, t0, 2 * 4 // data - (period - 2) = initial &x0 + vle32.v v16, (t1) + addi t3, a1, -2 // maximum parallelism w/o stepping our tail 1: - li a4, 5 - addi a1, a1, 2 - slli a1, a1, 2 - lw t1, 4(a2) - vsetivli zero, 3, e32, m1, ta, ma - vle32.v v24, (a2) - sub a1, a0, a1 // a1 = &x4 = &data[-(period + 2)] - vsetvl zero, a4, a5 - vslide1up.vx v8, v24, t1 - lw t2, 8(a2) - vle32.v v16, (a1) - vslide1up.vx v24, v8, t2 // v24 = { g[2], g[1], g[0], g[1], g[2] } -2: - vsetvl t0, a3, a5 - vle32.v v0, (a0) - sub a3, a3, t0 -3: - vsetvl zero, a4, a5 - lw t2, 20(a1) - vfmul.vv v8, v24, v16 - addi a0, a0, 4 - vslide1down.vx v16, v16, t2 - addi a1, a1, 4 - vfredusum.vs v0, v8, v0 - vsetvl zero, t0, a5 - vmv.x.s t1, v0 - addi t0, t0, -1 - vslide1down.vx v0, v0, zero - sw t1, -4(a0) - bnez t0, 3b - - bnez a3, 2b + vslidedown.vi v8, v16, 2 + min t1, a3, t3 + vslide1down.vx v12, v16, zero + vsetvli t1, t1, e32, m4, ta, ma + vle32.v v0, (t0) // x0 + sub a3, a3, t1 + vslide1down.vx v4, v8, zero + sh2add t0, t1, t0 + vle32.v v28, (a0) + addi t2, t1, -4 + vslideup.vi v4, v0, 1 + vslideup.vi v8, v4, 1 + vslideup.vi v12, v8, 1 + vslideup.vi v16, v12, 1 + vfadd.vv v20, v4, v12 + vfadd.vv v24, v0, v16 + vslidedown.vx v16, v0, t2 + vfmacc.vf v28, fa0, v8 + vfmacc.vf v28, fa1, v20 + vfmacc.vf v28, fa2, v24 + vse32.v v28, (a0) + sh2add a0, t1, a0 + bnez a3, 1b ret endfunc diff --git a/libavcodec/riscv/pixblockdsp_init.c b/libavcodec/riscv/pixblockdsp_init.c index aa39a8a6651..0584100cfcf 100644 --- a/libavcodec/riscv/pixblockdsp_init.c +++ b/libavcodec/riscv/pixblockdsp_init.c @@ -34,15 +34,18 @@ void ff_get_pixels_16_rvi(int16_t *block, const uint8_t *pixels, void ff_get_pixels_8_rvv(int16_t *block, const uint8_t *pixels, ptrdiff_t stride); -void ff_get_pixels_16_rvv(int16_t *block, const uint8_t *pixels, - ptrdiff_t stride); -void ff_diff_pixels_rvv(int16_t *block, const uint8_t *s1, const uint8_t *s2, - ptrdiff_t stride); +void ff_get_pixels_unaligned_8_rvv(int16_t *block, const uint8_t *pixels, + ptrdiff_t stride); +void ff_diff_pixels_rvv(int16_t *block, const uint8_t *s1, + const uint8_t *s2, ptrdiff_t stride); +void ff_diff_pixels_unaligned_rvv(int16_t *block, const uint8_t *s1, + const uint8_t *s2, ptrdiff_t stride); av_cold void ff_pixblockdsp_init_riscv(PixblockDSPContext *c, AVCodecContext *avctx, unsigned high_bit_depth) { +#if HAVE_RV int cpu_flags = av_get_cpu_flags(); if (cpu_flags & AV_CPU_FLAG_RVI) { @@ -54,12 +57,18 @@ av_cold void ff_pixblockdsp_init_riscv(PixblockDSPContext *c, #if HAVE_RVV if ((cpu_flags & AV_CPU_FLAG_RVV_I32) && ff_get_rv_vlenb() >= 16) { - if (high_bit_depth) - c->get_pixels_unaligned = c->get_pixels = ff_get_pixels_16_rvv; - else - c->get_pixels_unaligned = c->get_pixels = ff_get_pixels_8_rvv; + c->diff_pixels = ff_diff_pixels_unaligned_rvv; + c->diff_pixels_unaligned = ff_diff_pixels_unaligned_rvv; + } + + if ((cpu_flags & AV_CPU_FLAG_RVV_I64) && ff_get_rv_vlenb() >= 16) { + if (!high_bit_depth) { + c->get_pixels = ff_get_pixels_8_rvv; + c->get_pixels_unaligned = ff_get_pixels_unaligned_8_rvv; + } - c->diff_pixels_unaligned = c->diff_pixels = ff_diff_pixels_rvv; + c->diff_pixels = ff_diff_pixels_rvv; } #endif +#endif } diff --git a/libavcodec/riscv/pixblockdsp_rvv.S b/libavcodec/riscv/pixblockdsp_rvv.S index 1a364e6dabc..4213cd1b852 100644 --- a/libavcodec/riscv/pixblockdsp_rvv.S +++ b/libavcodec/riscv/pixblockdsp_rvv.S @@ -20,29 +20,49 @@ #include "libavutil/riscv/asm.S" -func ff_get_pixels_8_rvv, zve32x - vsetivli zero, 8, e8, mf2, ta, ma - vlsseg8e8.v v16, (a1), a2 +func ff_get_pixels_8_rvv, zve64x + vsetivli zero, 8, e8, mf2, ta, ma + li t0, 8 * 8 +1: + vlse64.v v16, (a1), a2 + vsetvli zero, t0, e8, m4, ta, ma vwcvtu.x.x.v v8, v16 - vwcvtu.x.x.v v9, v17 - vwcvtu.x.x.v v10, v18 - vwcvtu.x.x.v v11, v19 - vwcvtu.x.x.v v12, v20 - vwcvtu.x.x.v v13, v21 - vwcvtu.x.x.v v14, v22 - vwcvtu.x.x.v v15, v23 - vsseg8e16.v v8, (a0) + vse16.v v8, (a0) ret endfunc -func ff_get_pixels_16_rvv, zve32x - vsetivli zero, 8, e16, m1, ta, ma - vlsseg8e16.v v0, (a1), a2 - vsseg8e16.v v0, (a0) +func ff_get_pixels_unaligned_8_rvv, zve64x + andi t1, a1, 7 + vsetivli zero, 8, e64, m4, ta, ma + li t0, 8 * 8 + beqz t1, 1b + andi a1, a1, -8 + slli t2, t1, 3 + addi t1, a1, 8 + sub t3, t0, t2 + vlse64.v v16, (a1), a2 + vlse64.v v24, (t1), a2 + vsrl.vx v16, v16, t2 + vsll.vx v24, v24, t3 + vor.vv v16, v16, v24 + vsetvli zero, t0, e8, m4, ta, ma + vwcvtu.x.x.v v8, v16 + vse16.v v8, (a0) + ret +endfunc + +func ff_diff_pixels_rvv, zve64x + vsetivli zero, 8, e8, mf2, ta, ma + li t0, 8 * 8 + vlse64.v v16, (a1), a3 + vlse64.v v24, (a2), a3 + vsetvli zero, t0, e8, m4, ta, ma + vwsubu.vv v8, v16, v24 + vse16.v v8, (a0) ret endfunc -func ff_diff_pixels_rvv, zve32x +func ff_diff_pixels_unaligned_rvv, zve32x vsetivli zero, 8, e8, mf2, ta, ma vlsseg8e8.v v16, (a1), a3 vlsseg8e8.v v24, (a2), a3 diff --git a/libavcodec/x86/fft_init.c b/libavcodec/riscv/rv34dsp_init.c similarity index 57% rename from libavcodec/x86/fft_init.c rename to libavcodec/riscv/rv34dsp_init.c index df79d57dc76..7dcadc7e437 100644 --- a/libavcodec/x86/fft_init.c +++ b/libavcodec/riscv/rv34dsp_init.c @@ -1,4 +1,6 @@ /* + * Copyright (c) 2024 Institue of Software Chinese Academy of Sciences (ISCAS). + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -20,28 +22,20 @@ #include "libavutil/attributes.h" #include "libavutil/cpu.h" -#include "libavutil/x86/cpu.h" +#include "libavutil/riscv/cpu.h" +#include "libavcodec/rv34dsp.h" -#include "fft.h" +void ff_rv34_inv_transform_dc_rvv(int16_t *block); +void ff_rv34_idct_dc_add_rvv(uint8_t *dst, ptrdiff_t stride, int dc); -av_cold void ff_fft_init_x86(FFTContext *s) +av_cold void ff_rv34dsp_init_riscv(RV34DSPContext *c) { - int cpu_flags = av_get_cpu_flags(); - - if (s->nbits > 16) - return; - - if (EXTERNAL_SSE(cpu_flags)) { - s->imdct_calc = ff_imdct_calc_sse; - s->imdct_half = ff_imdct_half_sse; - s->fft_permute = ff_fft_permute_sse; - s->fft_calc = ff_fft_calc_sse; - s->fft_permutation = FF_FFT_PERM_SWAP_LSBS; - } +#if HAVE_RVV + int flags = av_get_cpu_flags(); - if (EXTERNAL_AVX_FAST(cpu_flags) && s->nbits >= 5) { - s->imdct_half = ff_imdct_half_avx; - s->fft_calc = ff_fft_calc_avx; - s->fft_permutation = FF_FFT_PERM_AVX; + if (flags & AV_CPU_FLAG_RVV_I32 && ff_get_rv_vlenb() >= 16) { + c->rv34_inv_transform_dc = ff_rv34_inv_transform_dc_rvv; + c->rv34_idct_dc_add = ff_rv34_idct_dc_add_rvv; } +#endif } diff --git a/libavcodec/riscv/rv34dsp_rvv.S b/libavcodec/riscv/rv34dsp_rvv.S new file mode 100644 index 00000000000..f1f63450123 --- /dev/null +++ b/libavcodec/riscv/rv34dsp_rvv.S @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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/riscv/asm.S" + +func ff_rv34_inv_transform_dc_rvv, zve32x + lh t1, 0(a0) + li t0, 13 * 13 * 3 + mul t2, t0, t1 + srai t2, t2, 11 + vsetivli zero, 16, e16, m2, ta, ma + vmv.v.x v8, t2 + vse16.v v8, (a0) + + ret +endfunc + +func ff_rv34_idct_dc_add_rvv, zve32x + vsetivli zero, 4, e8, mf4, ta, ma + vlse32.v v0, (a0), a1 + li t1, 169 + mul t1, t1, a2 + li a2, 255 + addi t1, t1, 512 + srai t1, t1, 10 + vsetivli zero, 4*4, e16, m2, ta, ma + vzext.vf2 v2, v0 + vadd.vx v2, v2, t1 + vmax.vx v2, v2, zero + vsetvli zero, zero, e8, m1, ta, ma + vnclipu.wi v0, v2, 0 + vsetivli zero, 4, e8, mf4, ta, ma + vsse32.v v0, (a0), a1 + + ret +endfunc diff --git a/libavcodec/riscv/sbrdsp_init.c b/libavcodec/riscv/sbrdsp_init.c new file mode 100644 index 00000000000..2ed46153ea2 --- /dev/null +++ b/libavcodec/riscv/sbrdsp_init.c @@ -0,0 +1,72 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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 "config.h" +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/riscv/cpu.h" +#include "libavcodec/sbrdsp.h" + +void ff_sbr_sum64x5_rvv(float *z); +float ff_sbr_sum_square_rvv(float (*x)[2], int n); +void ff_sbr_neg_odd_64_rvv(float *x); +void ff_sbr_autocorrelate_rvv(const float x[40][2], float phi[3][2][2]); +void ff_sbr_hf_gen_rvv(float (*X_high)[2], const float (*X_low)[2], + const float alpha0[2], const float alpha1[2], + float bw, int start, int end); +void ff_sbr_hf_g_filt_rvv(float (*Y)[2], const float (*X_high)[40][2], + const float *g_filt, int m_max, intptr_t ixh); +void ff_sbr_hf_apply_noise_0_rvv(float (*Y)[2], const float *s, + const float *f, int n, int kx, int max); +void ff_sbr_hf_apply_noise_1_rvv(float (*Y)[2], const float *s, + const float *f, int n, int kx, int max); +void ff_sbr_hf_apply_noise_2_rvv(float (*Y)[2], const float *s, + const float *f, int n, int kx, int max); +void ff_sbr_hf_apply_noise_3_rvv(float (*Y)[2], const float *s, + const float *f, int n, int kx, int max); + +av_cold void ff_sbrdsp_init_riscv(SBRDSPContext *c) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if (flags & AV_CPU_FLAG_RVV_F32) { + if (flags & AV_CPU_FLAG_RVB_ADDR) { + c->sum64x5 = ff_sbr_sum64x5_rvv; + c->sum_square = ff_sbr_sum_square_rvv; + c->hf_gen = ff_sbr_hf_gen_rvv; + c->hf_g_filt = ff_sbr_hf_g_filt_rvv; + if (ff_get_rv_vlenb() <= 16) { + c->hf_apply_noise[0] = ff_sbr_hf_apply_noise_0_rvv; + c->hf_apply_noise[2] = ff_sbr_hf_apply_noise_2_rvv; + if (flags & AV_CPU_FLAG_RVB_BASIC) { + c->hf_apply_noise[1] = ff_sbr_hf_apply_noise_1_rvv; + c->hf_apply_noise[3] = ff_sbr_hf_apply_noise_3_rvv; + } + } + } + c->autocorrelate = ff_sbr_autocorrelate_rvv; + } +#if __riscv_xlen >= 64 + if ((flags & AV_CPU_FLAG_RVV_I64) && (flags & AV_CPU_FLAG_RVB_ADDR)) + c->neg_odd_64 = ff_sbr_neg_odd_64_rvv; +#endif +#endif +} diff --git a/libavcodec/riscv/sbrdsp_rvv.S b/libavcodec/riscv/sbrdsp_rvv.S new file mode 100644 index 00000000000..02feb6451e2 --- /dev/null +++ b/libavcodec/riscv/sbrdsp_rvv.S @@ -0,0 +1,312 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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/riscv/asm.S" + +func ff_sbr_sum64x5_rvv, zve32f + li a5, 64 + addi a1, a0, 64 * 4 + addi a2, a0, 128 * 4 + addi a3, a0, 192 * 4 + addi a4, a0, 256 * 4 +1: + vsetvli t0, a5, e32, m8, ta, ma + sub a5, a5, t0 + vle32.v v0, (a0) + vle32.v v8, (a1) + sh2add a1, t0, a1 + vle32.v v16, (a2) + vfadd.vv v0, v0, v8 + sh2add a2, t0, a2 + vle32.v v24, (a3) + vfadd.vv v0, v0, v16 + sh2add a3, t0, a3 + vle32.v v8, (a4) + vfadd.vv v0, v0, v24 + sh2add a4, t0, a4 + vfadd.vv v0, v0, v8 + vse32.v v0, (a0) + sh2add a0, t0, a0 + bnez a5, 1b + + ret +endfunc + +func ff_sbr_sum_square_rvv, zve32f + vsetvli t0, zero, e32, m8, ta, ma + slli a1, a1, 1 + vmv.v.x v8, zero + vmv.s.x v0, zero +1: + vsetvli t0, a1, e32, m8, tu, ma + vle32.v v16, (a0) + sub a1, a1, t0 + vfmacc.vv v8, v16, v16 + sh2add a0, t0, a0 + bnez a1, 1b + + vfredusum.vs v0, v8, v0 + vfmv.f.s fa0, v0 +NOHWF fmv.x.w a0, fa0 + ret +endfunc + +#if __riscv_xlen >= 64 +func ff_sbr_neg_odd_64_rvv, zve64x + li a1, 32 + li t1, 1 << 63 +1: + vsetvli t0, a1, e64, m8, ta, ma + vle64.v v8, (a0) + sub a1, a1, t0 + vxor.vx v8, v8, t1 + vse64.v v8, (a0) + sh3add a0, t0, a0 + bnez t0, 1b + + ret +endfunc +#endif + +func ff_sbr_autocorrelate_rvv, zve32f + vsetvli t0, zero, e32, m4, ta, ma + vmv.v.x v0, zero + flw fa0, (a0) + vmv.v.x v4, zero + flw fa1, 4(a0) + vmv.v.x v8, zero + flw fa2, 8(a0) + li a2, 37 + flw fa3, 12(a0) + fmul.s ft10, fa0, fa0 + flw fa4, 16(a0) + fmul.s ft6, fa0, fa2 + flw fa5, 20(a0) + addi a0, a0, 38 * 8 + fmul.s ft7, fa0, fa3 + fmul.s ft2, fa0, fa4 + fmul.s ft3, fa0, fa5 + flw fa0, (a0) + fmadd.s ft10, fa1, fa1, ft10 + fmadd.s ft6, fa1, fa3, ft6 + flw fa3, 12(a0) + fnmsub.s ft7, fa1, fa2, ft7 + flw fa2, 8(a0) + fmadd.s ft2, fa1, fa5, ft2 + fnmsub.s ft3, fa1, fa4, ft3 + flw fa1, 4(a0) + fmul.s ft4, fa0, fa0 + fmul.s ft0, fa0, fa2 + fmul.s ft1, fa0, fa3 + fmadd.s ft4, fa1, fa1, ft4 + fmadd.s ft0, fa1, fa3, ft0 + fnmsub.s ft1, fa1, fa2, ft1 +1: + vsetvli t0, a2, e32, m2, tu, ma + slli t1, t0, 3 + sub a0, a0, t1 + vlseg2e32.v v16, (a0) + sub a2, a2, t0 + vfmacc.vv v0, v16, v16 + vfslide1down.vf v20, v16, fa0 + vfmacc.vv v4, v16, v20 + vfslide1down.vf v22, v18, fa1 + vfmacc.vv v0, v18, v18 + vfslide1down.vf v24, v20, fa2 + vfmacc.vv v4, v18, v22 + vfslide1down.vf v26, v22, fa3 + vfmacc.vv v6, v16, v22 + vfmv.f.s fa0, v16 + vfmacc.vv v8, v16, v24 + vfmv.f.s fa1, v18 + vfmacc.vv v10, v16, v26 + vfmv.f.s fa2, v20 + vfnmsac.vv v6, v18, v20 + vfmv.f.s fa3, v22 + vfmacc.vv v8, v18, v26 + vfnmsac.vv v10, v18, v24 + bnez a2, 1b + + vsetvli t0, zero, e32, m2, ta, ma + vfredusum.vs v0, v0, v2 + vfredusum.vs v4, v4, v2 + vfmv.f.s fa0, v0 + vfredusum.vs v6, v6, v2 + vfmv.f.s fa2, v4 + fadd.s ft4, ft4, fa0 + vfredusum.vs v8, v8, v2 + vfmv.f.s fa3, v6 + fadd.s ft0, ft0, fa2 + vfredusum.vs v10, v10, v2 + vfmv.f.s fa4, v8 + fadd.s ft1, ft1, fa3 + vfmv.f.s fa5, v10 + fsw ft0, (a1) + fadd.s ft2, ft2, fa4 + fsw ft1, 4(a1) + fadd.s ft3, ft3, fa5 + fsw ft2, 8(a1) + fadd.s ft6, ft6, fa2 + fsw ft3, 12(a1) + fadd.s ft7, ft7, fa3 + fsw ft4, 16(a1) + fadd.s ft10, ft10, fa0 + fsw ft6, 24(a1) + fsw ft7, 28(a1) + fsw ft10, 40(a1) + ret +endfunc + +func ff_sbr_hf_gen_rvv, zve32f +NOHWF fmv.w.x fa0, a4 +NOHWF mv a4, a5 +NOHWF mv a5, a6 + flw ft2, 0(a2) + fmul.s fa1, fa0, fa0 // bw * bw + sh3add a1, a5, a1 + flw ft3, 4(a2) + fmul.s fa2, ft2, fa0 // alpha[2] + sh3add a0, a5, a0 + flw ft0, 0(a3) + fmul.s fa3, ft3, fa0 // alpha[3] + sub a5, a5, a4 + flw ft1, 4(a3) + fmul.s fa0, ft0, fa1 // alpha[0] + flw ft0, -16(a1) // X_low[end - 2][0] + fmul.s fa1, ft1, fa1 // alpha[1] + flw ft1, -12(a1) // X_low[end - 2][1] + flw ft2, -8(a1) // X_low[end - 1][0] + flw ft3, -4(a1) // X_low[end - 1][1] + addi a1, a1, -16 +1: + vsetvli t0, a5, e32, m4, ta, ma + slli t1, t0, 3 + sub a1, a1, t1 + vlseg2e32.v v0, (a1) // X_low[i - 2] + sub a0, a0, t1 + vfslide1down.vf v8, v0, ft0 // X_low[i - 1][0] + sub a5, a5, t0 + vfslide1down.vf v12, v4, ft1 // X_low[i - 1][1] + vfslide1down.vf v16, v8, ft2 // X_low[i ][0] + vfslide1down.vf v20, v12, ft3 // X_low[i ][1] + vfmacc.vf v16, fa0, v0 + vfmacc.vf v20, fa0, v4 + vfmv.f.s ft0, v0 + vfnmsac.vf v16, fa1, v4 + vfmacc.vf v20, fa1, v0 + vfmv.f.s ft1, v4 + vfmacc.vf v16, fa2, v8 + vfmacc.vf v20, fa2, v12 + vfmv.f.s ft2, v8 + vfnmsac.vf v16, fa3, v12 + vfmacc.vf v20, fa3, v8 + vfmv.f.s ft3, v12 + vsseg2e32.v v16, (a0) + bnez a5, 1b + + ret +endfunc + +func ff_sbr_hf_g_filt_rvv, zve32f + li t1, 40 * 2 * 4 + sh3add a1, a4, a1 +1: + vsetvli t0, a3, e32, m4, ta, ma + vlsseg2e32.v v16, (a1), t1 + mul t2, t0, t1 + vle32.v v8, (a2) + sub a3, a3, t0 + vfmul.vv v16, v16, v8 + add a1, t2, a1 + vfmul.vv v20, v20, v8 + sh2add a2, t0, a2 + vsseg2e32.v v16, (a0) + sh3add a0, t0, a0 + bnez a3, 1b + + ret +endfunc + +.macro hf_apply_noise n + lla a6, ff_sbr_noise_table + fmv.s.x ft0, zero + addi a6, a6, 8 +1: +.if \n & 1 + min t0, t0, a5 // preserve parity of t0 for v4 sign injector + vsetvli zero, t0, e32, m4, ta, mu +.else + vsetvli t0, a5, e32, m4, ta, mu +.endif + sh3add t6, a3, a6 + vle32.v v8, (a1) // s_m + sub a5, a5, t0 + vle32.v v12, (a2) // q_filt + sh2add a1, t0, a1 + vmfeq.vf v0, v8, ft0 // s_m == 0.f + vlseg2e32.v v24, (t6) // ff_sbr_noise_table + sh2add a2, t0, a2 +.if \n == 2 + vfneg.v v8, v8 +.endif +.if \n & 1 + vfsgnjx.vv v8, v8, v4 // could equivalent use vxor.vv +.endif + add a3, t0, a3 + vlseg2e32.v v16, (a0) // Y + andi a3, a3, 0x1ff +.if \n & 1 + vfmul.vv v28, v12, v28 + vfmacc.vv v16, v12, v24, v0.t + vmerge.vvm v28, v8, v28, v0 + vfadd.vv v20, v20, v28 +.else + vfmul.vv v24, v12, v24 + vfmacc.vv v20, v12, v28, v0.t + vmerge.vvm v24, v8, v24, v0 + vfadd.vv v16, v16, v24 +.endif + vsseg2e32.v v16, (a0) + sh3add a0, t0, a0 + bnez a5, 1b + + ret +.endm + +func ff_sbr_hf_apply_noise_0_rvv, zve32f + hf_apply_noise 0 +endfunc + +func ff_sbr_hf_apply_noise_3_rvv, zve32f + not a4, a4 // invert parity of kx + // fall through +endfunc + +func ff_sbr_hf_apply_noise_1_rvv, zve32f + vsetvli t0, zero, e32, m4, ta, ma + vid.v v4 + vxor.vx v4, v4, a4 + vsll.vi v4, v4, 31 // v4[i] = (kx & 1) ? -0.f : +0.f + hf_apply_noise 1 +endfunc + +func ff_sbr_hf_apply_noise_2_rvv, zve32f + hf_apply_noise 2 +endfunc diff --git a/libavcodec/riscv/svqenc_init.c b/libavcodec/riscv/svqenc_init.c new file mode 100644 index 00000000000..f4c398960c8 --- /dev/null +++ b/libavcodec/riscv/svqenc_init.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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 "config.h" + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavcodec/svq1encdsp.h" + +int ff_ssd_int8_vs_int16_rvv(const int8_t *pix1, const int16_t *pix2, + intptr_t size); + +av_cold void ff_svq1enc_init_riscv(SVQ1EncDSPContext *c) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if (flags & AV_CPU_FLAG_RVV_I32) { + if (flags & AV_CPU_FLAG_RVB_ADDR) { + c->ssd_int8_vs_int16 = ff_ssd_int8_vs_int16_rvv; + } + } +#endif +} diff --git a/libavcodec/dynamic_hdr10_plus.h b/libavcodec/riscv/svqenc_rvv.S similarity index 51% rename from libavcodec/dynamic_hdr10_plus.h rename to libavcodec/riscv/svqenc_rvv.S index cd7acf04329..cfc27154ddc 100644 --- a/libavcodec/dynamic_hdr10_plus.h +++ b/libavcodec/riscv/svqenc_rvv.S @@ -1,4 +1,6 @@ /* + * Copyright (c) 2023 Institue of Software Chinese Academy of Sciences (ISCAS). + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -16,20 +18,26 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef AVCODEC_DYNAMIC_HDR10_PLUS_H -#define AVCODEC_DYNAMIC_HDR10_PLUS_H - -#include "libavutil/hdr_dynamic_metadata.h" +#include "libavutil/riscv/asm.S" -/** - * Parse the user data registered ITU-T T.35 to AVbuffer (AVDynamicHDRPlus). - * @param s A pointer containing the decoded AVDynamicHDRPlus structure. - * @param data The byte array containing the raw ITU-T T.35 data. - * @param size Size of the data array in bytes. - * - * @return 0 if succeed. Otherwise, returns the appropriate AVERROR. - */ -int ff_parse_itu_t_t35_to_dynamic_hdr10_plus(AVDynamicHDRPlus *s, const uint8_t *data, - int size); +func ff_ssd_int8_vs_int16_rvv, zve32x + vsetvli t0, zero, e32, m8, ta, ma + vmv.v.x v24, zero +1: + vsetvli t0, a2, e8, m2, tu, ma + vle16.v v8, (a1) + sub a2, a2, t0 + vle8.v v0, (a0) + vwsub.wv v16, v8, v0 + vsetvli zero, zero, e16, m4, tu, ma + add a0, a0, t0 + vwmacc.vv v24, v16, v16 + sh1add a1, t0, a1 + bnez a2, 1b + vsetvli zero, zero, e32, m8, ta, ma + vmv.s.x v0, zero + vredsum.vs v0, v24, v0 + vmv.x.s a0, v0 -#endif /* AVCODEC_DYNAMIC_HDR10_PLUS_H */ + ret +endfunc diff --git a/libavcodec/riscv/takdsp_init.c b/libavcodec/riscv/takdsp_init.c new file mode 100644 index 00000000000..58be83860b5 --- /dev/null +++ b/libavcodec/riscv/takdsp_init.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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 + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/riscv/cpu.h" +#include "libavcodec/takdsp.h" + +void ff_decorrelate_ls_rvv(const int32_t *p1, int32_t *p2, int length); +void ff_decorrelate_sr_rvv(int32_t *p1, const int32_t *p2, int length); +void ff_decorrelate_sm_rvv(int32_t *p1, int32_t *p2, int length); +void ff_decorrelate_sf_rvv(int32_t *p1, const int32_t *p2, int len, int, int); + +av_cold void ff_takdsp_init_riscv(TAKDSPContext *dsp) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if ((flags & AV_CPU_FLAG_RVV_I32) && (flags & AV_CPU_FLAG_RVB_ADDR)) { + dsp->decorrelate_ls = ff_decorrelate_ls_rvv; + dsp->decorrelate_sr = ff_decorrelate_sr_rvv; + dsp->decorrelate_sm = ff_decorrelate_sm_rvv; + dsp->decorrelate_sf = ff_decorrelate_sf_rvv; + } +#endif +} diff --git a/libavcodec/riscv/takdsp_rvv.S b/libavcodec/riscv/takdsp_rvv.S new file mode 100644 index 00000000000..fa942a3be61 --- /dev/null +++ b/libavcodec/riscv/takdsp_rvv.S @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2023 Institue of Software Chinese Academy of Sciences (ISCAS). + * Copyright (c) 2023 Rémi Denis-Courmont + * + * 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/riscv/asm.S" + +func ff_decorrelate_ls_rvv, zve32x +1: + vsetvli t0, a2, e32, m8, ta, ma + sub a2, a2, t0 + vle32.v v0, (a0) + sh2add a0, t0, a0 + vle32.v v8, (a1) + vadd.vv v16, v0, v8 + vse32.v v16, (a1) + sh2add a1, t0, a1 + bnez a2, 1b + ret +endfunc + +func ff_decorrelate_sr_rvv, zve32x +1: + vsetvli t0, a2, e32, m8, ta, ma + vle32.v v0, (a0) + sub a2, a2, t0 + vle32.v v8, (a1) + sh2add a1, t0, a1 + vsub.vv v16, v8, v0 + vse32.v v16, (a0) + sh2add a0, t0, a0 + bnez a2, 1b + ret +endfunc + +func ff_decorrelate_sm_rvv, zve32x +1: + vsetvli t0, a2, e32, m8, ta, ma + vle32.v v8, (a1) + sub a2, a2, t0 + vle32.v v0, (a0) + vsra.vi v16, v8, 1 + vsub.vv v0, v0, v16 + vse32.v v0, (a0) + sh2add a0, t0, a0 + vadd.vv v8, v0, v8 + vse32.v v8, (a1) + sh2add a1, t0, a1 + bnez a2, 1b + + ret +endfunc + +func ff_decorrelate_sf_rvv, zve32x + csrwi vxrm, 0 +1: + vsetvli t0, a2, e32, m8, ta, ma + vle32.v v8, (a1) + sub a2, a2, t0 + vsra.vx v8, v8, a3 + sh2add a1, t0, a1 + vle32.v v0, (a0) + vmul.vx v8, v8, a4 + vssra.vi v8, v8, 8 + vsll.vx v8, v8, a3 + vsub.vv v0, v8, v0 + vse32.v v0, (a0) + sh2add a0, t0, a0 + bnez a2, 1b + + ret +endfunc diff --git a/libavcodec/riscv/utvideodsp_init.c b/libavcodec/riscv/utvideodsp_init.c new file mode 100644 index 00000000000..f5038c4736a --- /dev/null +++ b/libavcodec/riscv/utvideodsp_init.c @@ -0,0 +1,45 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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 "config.h" +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavcodec/utvideodsp.h" + +void ff_restore_rgb_planes_rvv(uint8_t *r, uint8_t *g, uint8_t *b, + ptrdiff_t linesize_r, ptrdiff_t linesize_g, + ptrdiff_t linesize_b, int width, int height); +void ff_restore_rgb_planes10_rvv(uint16_t *r, uint16_t *g, uint16_t *b, + ptrdiff_t linesize_r, ptrdiff_t linesize_g, + ptrdiff_t linesize_b, int width, int height); + +av_cold void ff_utvideodsp_init_riscv(UTVideoDSPContext *c) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if (flags & AV_CPU_FLAG_RVV_I32) { + c->restore_rgb_planes = ff_restore_rgb_planes_rvv; + + if (flags & AV_CPU_FLAG_RVB_ADDR) + c->restore_rgb_planes10 = ff_restore_rgb_planes10_rvv; + } +#endif +} diff --git a/libavcodec/riscv/utvideodsp_rvv.S b/libavcodec/riscv/utvideodsp_rvv.S new file mode 100644 index 00000000000..fa70d0eb34e --- /dev/null +++ b/libavcodec/riscv/utvideodsp_rvv.S @@ -0,0 +1,88 @@ +/* + * Copyright © 2023 Rémi Denis-Courmont. + * + * 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/riscv/asm.S" + +func ff_restore_rgb_planes_rvv, zve32x + li t1, -0x80 + sub a3, a3, a6 + sub a4, a4, a6 + sub a5, a5, a6 +1: + mv t6, a6 + addi a7, a7, -1 +2: + vsetvli t0, t6, e8, m8, ta, ma + vle8.v v16, (a1) + sub t6, t6, t0 + vle8.v v8, (a0) + vadd.vx v16, v16, t1 + add a1, t0, a1 + vle8.v v24, (a2) + vadd.vv v8, v8, v16 + vadd.vv v24, v24, v16 + vse8.v v8, (a0) + add a0, t0, a0 + vse8.v v24, (a2) + add a2, t0, a2 + bnez t6, 2b + + add a0, a3, a0 + add a1, a4, a1 + add a2, a5, a2 + bnez a7, 1b + + ret +endfunc + +func ff_restore_rgb_planes10_rvv, zve32x + li t1, -0x200 + li t2, 0x3FF + sub a3, a3, a6 + sub a4, a4, a6 + sub a5, a5, a6 +1: + mv t6, a6 + addi a7, a7, -1 +2: + vsetvli t0, t6, e16, m8, ta, ma + vle16.v v16, (a1) + sub t6, t6, t0 + vle16.v v8, (a0) + vadd.vx v16, v16, t1 + sh1add a1, t0, a1 + vle16.v v24, (a2) + vadd.vv v8, v8, v16 + vadd.vv v24, v24, v16 + vand.vx v8, v8, t2 + vand.vx v24, v24, t2 + vse16.v v8, (a0) + sh1add a0, t0, a0 + vse16.v v24, (a2) + sh1add a2, t0, a2 + bnez t6, 2b + + sh1add a0, a3, a0 + sh1add a1, a4, a1 + sh1add a2, a5, a2 + bnez a7, 1b + + ret +endfunc diff --git a/libavcodec/riscv/vc1dsp_init.c b/libavcodec/riscv/vc1dsp_init.c new file mode 100644 index 00000000000..e47b644f805 --- /dev/null +++ b/libavcodec/riscv/vc1dsp_init.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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 + +#include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavutil/riscv/cpu.h" +#include "libavcodec/vc1.h" + +void ff_vc1_inv_trans_8x8_dc_rvv(uint8_t *dest, ptrdiff_t stride, int16_t *block); +void ff_vc1_inv_trans_4x8_dc_rvv(uint8_t *dest, ptrdiff_t stride, int16_t *block); +void ff_vc1_inv_trans_8x4_dc_rvv(uint8_t *dest, ptrdiff_t stride, int16_t *block); +void ff_vc1_inv_trans_4x4_dc_rvv(uint8_t *dest, ptrdiff_t stride, int16_t *block); + +av_cold void ff_vc1dsp_init_riscv(VC1DSPContext *dsp) +{ +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if (flags & AV_CPU_FLAG_RVV_I32 && ff_get_rv_vlenb() >= 16) { + dsp->vc1_inv_trans_4x8_dc = ff_vc1_inv_trans_4x8_dc_rvv; + dsp->vc1_inv_trans_4x4_dc = ff_vc1_inv_trans_4x4_dc_rvv; + if (flags & AV_CPU_FLAG_RVV_I64) { + dsp->vc1_inv_trans_8x8_dc = ff_vc1_inv_trans_8x8_dc_rvv; + dsp->vc1_inv_trans_8x4_dc = ff_vc1_inv_trans_8x4_dc_rvv; + } + } +#endif +} diff --git a/libavcodec/riscv/vc1dsp_rvv.S b/libavcodec/riscv/vc1dsp_rvv.S new file mode 100644 index 00000000000..4a00945ead5 --- /dev/null +++ b/libavcodec/riscv/vc1dsp_rvv.S @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2023 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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/riscv/asm.S" + +func ff_vc1_inv_trans_8x8_dc_rvv, zve64x + lh t2, (a2) + vsetivli zero, 8, e8, mf2, ta, ma + vlse64.v v0, (a0), a1 + sh1add t2, t2, t2 + addi t2, t2, 1 + srai t2, t2, 1 + sh1add t2, t2, t2 + addi t2, t2, 16 + srai t2, t2, 5 + li t0, 8*8 + vsetvli zero, t0, e16, m8, ta, ma + vzext.vf2 v8, v0 + vadd.vx v8, v8, t2 + vmax.vx v8, v8, zero + vsetvli zero, zero, e8, m4, ta, ma + vnclipu.wi v0, v8, 0 + vsetivli zero, 8, e8, mf2, ta, ma + vsse64.v v0, (a0), a1 + ret +endfunc + +func ff_vc1_inv_trans_4x8_dc_rvv, zve32x + lh t2, (a2) + vsetivli zero, 8, e8, mf2, ta, ma + vlse32.v v0, (a0), a1 + slli t1, t2, 4 + add t2, t2, t1 + addi t2, t2, 4 + srai t2, t2, 3 + sh1add t2, t2, t2 + slli t2, t2, 2 + addi t2, t2, 64 + srai t2, t2, 7 + li t0, 4*8 + vsetvli zero, t0, e16, m4, ta, ma + vzext.vf2 v4, v0 + vadd.vx v4, v4, t2 + vmax.vx v4, v4, zero + vsetvli zero, zero, e8, m2, ta, ma + vnclipu.wi v0, v4, 0 + vsetivli zero, 8, e8, mf2, ta, ma + vsse32.v v0, (a0), a1 + ret +endfunc + +func ff_vc1_inv_trans_8x4_dc_rvv, zve64x + lh t2, (a2) + vsetivli zero, 4, e8, mf4, ta, ma + vlse64.v v0, (a0), a1 + sh1add t2, t2, t2 + addi t2, t2, 1 + srai t2, t2, 1 + slli t1, t2, 4 + add t2, t2, t1 + addi t2, t2, 64 + srai t2, t2, 7 + li t0, 8*4 + vsetvli zero, t0, e16, m4, ta, ma + vzext.vf2 v4, v0 + vadd.vx v4, v4, t2 + vmax.vx v4, v4, zero + vsetvli zero, zero, e8, m2, ta, ma + vnclipu.wi v0, v4, 0 + vsetivli zero, 4, e8, mf4, ta, ma + vsse64.v v0, (a0), a1 + ret +endfunc + +func ff_vc1_inv_trans_4x4_dc_rvv, zve32x + lh t2, (a2) + vsetivli zero, 4, e8, mf4, ta, ma + vlse32.v v0, (a0), a1 + slli t1, t2, 4 + add t2, t2, t1 + addi t2, t2, 4 + srai t2, t2, 3 + slli t1, t2, 4 + add t2, t2, t1 + addi t2, t2, 64 + srai t2, t2, 7 + vsetivli zero, 4*4, e16, m2, ta, ma + vzext.vf2 v2, v0 + vadd.vx v2, v2, t2 + vmax.vx v2, v2, zero + vsetvli zero, zero, e8, m1, ta, ma + vnclipu.wi v0, v2, 0 + vsetivli zero, 4, e8, mf4, ta, ma + vsse32.v v0, (a0), a1 + ret +endfunc diff --git a/libavcodec/riscv/vorbisdsp_init.c b/libavcodec/riscv/vorbisdsp_init.c index 0c56ffcb9b7..0bbbcb68de6 100644 --- a/libavcodec/riscv/vorbisdsp_init.c +++ b/libavcodec/riscv/vorbisdsp_init.c @@ -31,7 +31,7 @@ av_cold void ff_vorbisdsp_init_riscv(VorbisDSPContext *c) #if HAVE_RVV int flags = av_get_cpu_flags(); - if (flags & AV_CPU_FLAG_RVV_I32) + if ((flags & AV_CPU_FLAG_RVV_I32) && (flags & AV_CPU_FLAG_RVB_ADDR)) c->vorbis_inverse_coupling = ff_vorbis_inverse_coupling_rvv; #endif } diff --git a/libavcodec/riscv/vorbisdsp_rvv.S b/libavcodec/riscv/vorbisdsp_rvv.S index f45e7dc2f13..81a6c62a657 100644 --- a/libavcodec/riscv/vorbisdsp_rvv.S +++ b/libavcodec/riscv/vorbisdsp_rvv.S @@ -23,7 +23,7 @@ func ff_vorbis_inverse_coupling_rvv, zve32f fmv.w.x ft0, zero 1: - vsetvli t0, a2, e32, m1, ta, ma + vsetvli t0, a2, e32, m4, ta, ma vle32.v v16, (a1) sub a2, a2, t0 vle32.v v24, (a0) diff --git a/libavcodec/x86/hpeldsp_vp3_init.c b/libavcodec/riscv/vp8dsp_init.c similarity index 50% rename from libavcodec/x86/hpeldsp_vp3_init.c rename to libavcodec/riscv/vp8dsp_init.c index 1dbd1ba6f92..af57aabb713 100644 --- a/libavcodec/x86/hpeldsp_vp3_init.c +++ b/libavcodec/riscv/vp8dsp_init.c @@ -1,4 +1,6 @@ /* + * Copyright (c) 2024 Institue of Software Chinese Academy of Sciences (ISCAS). + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -16,28 +18,28 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "config.h" + #include "libavutil/attributes.h" #include "libavutil/cpu.h" -#include "libavutil/x86/cpu.h" - -#include "libavcodec/avcodec.h" -#include "libavcodec/hpeldsp.h" +#include "libavutil/riscv/cpu.h" +#include "libavcodec/vp8dsp.h" -#include "hpeldsp.h" +void ff_vp8_idct_dc_add_rvv(uint8_t *dst, int16_t block[16], ptrdiff_t stride); +void ff_vp8_idct_dc_add4y_rvv(uint8_t *dst, int16_t block[4][16], ptrdiff_t stride); +void ff_vp8_idct_dc_add4uv_rvv(uint8_t *dst, int16_t block[4][16], ptrdiff_t stride); -void ff_put_no_rnd_pixels8_x2_exact_mmxext(uint8_t *block, - const uint8_t *pixels, - ptrdiff_t line_size, int h); -void ff_put_no_rnd_pixels8_y2_exact_mmxext(uint8_t *block, - const uint8_t *pixels, - ptrdiff_t line_size, int h); - -av_cold void ff_hpeldsp_vp3_init_x86(HpelDSPContext *c, int cpu_flags, int flags) +av_cold void ff_vp8dsp_init_riscv(VP8DSPContext *c) { - if (EXTERNAL_MMXEXT(cpu_flags)) { - if (flags & AV_CODEC_FLAG_BITEXACT) { - c->put_no_rnd_pixels_tab[1][1] = ff_put_no_rnd_pixels8_x2_exact_mmxext; - c->put_no_rnd_pixels_tab[1][2] = ff_put_no_rnd_pixels8_y2_exact_mmxext; +#if HAVE_RVV + int flags = av_get_cpu_flags(); + + if (flags & AV_CPU_FLAG_RVV_I32 && ff_get_rv_vlenb() >= 16) { + c->vp8_idct_dc_add = ff_vp8_idct_dc_add_rvv; + c->vp8_idct_dc_add4y = ff_vp8_idct_dc_add4y_rvv; + if (flags & AV_CPU_FLAG_RVB_ADDR) { + c->vp8_idct_dc_add4uv = ff_vp8_idct_dc_add4uv_rvv; } } +#endif } diff --git a/libavcodec/riscv/vp8dsp_rvv.S b/libavcodec/riscv/vp8dsp_rvv.S new file mode 100644 index 00000000000..8a0773f9649 --- /dev/null +++ b/libavcodec/riscv/vp8dsp_rvv.S @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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/riscv/asm.S" + +.macro vp8_idct_dc_add + vlse32.v v0, (a0), a2 + lh a5, 0(a1) + sh zero, 0(a1) + addi a5, a5, 4 + srai t1, a5, 3 + vsetivli zero, 4*4, e16, m2, ta, ma + vzext.vf2 v2, v0 + vadd.vx v2, v2, t1 + vmax.vx v2, v2, zero + vsetvli zero, zero, e8, m1, ta, ma + vnclipu.wi v0, v2, 0 + vsetivli zero, 4, e8, mf4, ta, ma + vsse32.v v0, (a0), a2 +.endm + +.macro vp8_idct_dc_addy + vp8_idct_dc_add + addi a0, a0, 4 + addi a1, a1, 32 +.endm + +func ff_vp8_idct_dc_add_rvv, zve32x + vsetivli zero, 4, e8, mf4, ta, ma + vp8_idct_dc_add + + ret +endfunc + +func ff_vp8_idct_dc_add4y_rvv, zve32x + vsetivli zero, 4, e8, mf4, ta, ma + .rept 3 + vp8_idct_dc_addy + .endr + vp8_idct_dc_add + + ret +endfunc + +func ff_vp8_idct_dc_add4uv_rvv, zve32x + vsetivli zero, 4, e8, mf4, ta, ma + vp8_idct_dc_addy + vp8_idct_dc_add + addi a0, a0, -4 + sh2add a0, a2, a0 + addi a1, a1, 32 + vp8_idct_dc_addy + vp8_idct_dc_add + + ret +endfunc diff --git a/libavcodec/rka.c b/libavcodec/rka.c index 7646d776bea..724107f3e2f 100644 --- a/libavcodec/rka.c +++ b/libavcodec/rka.c @@ -59,8 +59,8 @@ typedef struct AdaptiveModel { } AdaptiveModel; typedef struct ChContext { - int cmode; - int cmode2; + int qfactor; + int vrq; int last_nb_decoded; unsigned srate_pad; unsigned pos_idx; @@ -131,7 +131,7 @@ static void adaptive_model_free(AdaptiveModel *am) static av_cold int rka_decode_init(AVCodecContext *avctx) { RKAContext *s = avctx->priv_data; - int cmode; + int qfactor; if (avctx->extradata_size < 16) return AVERROR_INVALIDDATA; @@ -160,14 +160,18 @@ static av_cold int rka_decode_init(AVCodecContext *avctx) s->last_nb_samples = s->total_nb_samples % s->frame_samples; s->correlated = avctx->extradata[15] & 1; - cmode = avctx->extradata[14] & 0xf; + qfactor = avctx->extradata[14] & 0xf; if ((avctx->extradata[15] & 4) != 0) - cmode = -cmode; - - s->ch[0].cmode = s->ch[1].cmode = cmode < 0 ? 2 : cmode; - s->ch[0].cmode2 = cmode < 0 ? FFABS(cmode) : 0; - s->ch[1].cmode2 = cmode < 0 ? FFABS(cmode) : 0; - av_log(avctx, AV_LOG_DEBUG, "cmode: %d\n", cmode); + qfactor = -qfactor; + + s->ch[0].qfactor = s->ch[1].qfactor = qfactor < 0 ? 2 : qfactor; + s->ch[0].vrq = qfactor < 0 ? -qfactor : 0; + s->ch[1].vrq = qfactor < 0 ? -qfactor : 0; + if (qfactor < 0) { + s->ch[0].vrq = av_clip(s->ch[0].vrq, 1, 8); + s->ch[1].vrq = av_clip(s->ch[1].vrq, 1, 8); + } + av_log(avctx, AV_LOG_DEBUG, "qfactor: %d\n", qfactor); return 0; } @@ -665,18 +669,16 @@ static int mdl64_decode(ACoder *ac, Model64 *ctx, int *dst) return 0; } -static const uint8_t tab[16] = { - 0, 3, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 -}; +static const uint8_t vrq_qfactors[8] = { 3, 3, 2, 2, 1, 1, 1, 1 }; static int decode_filter(RKAContext *s, ChContext *ctx, ACoder *ac, int off, unsigned size) { FiltCoeffs filt; Model64 *mdl64; - int m = 0, split, val, last_val = 0, ret; - unsigned idx = 3, bits = 0; + int split, val, last_val = 0, ret; + unsigned rsize, idx = 3, bits = 0, m = 0; - if (ctx->cmode == 0) { + if (ctx->qfactor == 0) { if (amdl_decode_int(&ctx->fshift, ac, &bits, 15) < 0) return -1; bits &= 31U; @@ -698,10 +700,12 @@ static int decode_filter(RKAContext *s, ChContext *ctx, ACoder *ac, int off, uns if (amdl_decode_int(&ctx->position, ac, &idx, 10) < 0) return -1; + m = 0; idx = (ctx->pos_idx + idx) % 11; ctx->pos_idx = idx; - for (int y = 0; y < FFMIN(split, size - x); y++, off++) { + rsize = FFMIN(split, size - x); + for (int y = 0; y < rsize; y++, off++) { int midx, shift = idx, *src, sum = 16; if (off >= FF_ARRAY_ELEMS(ctx->buf0)) @@ -728,7 +732,7 @@ static int decode_filter(RKAContext *s, ChContext *ctx, ACoder *ac, int off, uns for (int i = 15; i < filt.size; i++) sum += filt.coeffs[i] * (unsigned)src[-i]; sum = sum >> 6; - if (ctx->cmode == 0) { + if (ctx->qfactor == 0) { if (bits == 0) { ctx->buf1[off] = sum + val; } else { @@ -737,7 +741,7 @@ static int decode_filter(RKAContext *s, ChContext *ctx, ACoder *ac, int off, uns } ctx->buf0[off] = ctx->buf1[off] + (unsigned)ctx->buf0[off + -1]; } else { - val *= 1U << ctx->cmode; + val *= 1U << ctx->qfactor; sum += ctx->buf0[off + -1] + (unsigned)val; switch (s->bps) { case 16: sum = av_clip_int16(sum); break; @@ -748,12 +752,12 @@ static int decode_filter(RKAContext *s, ChContext *ctx, ACoder *ac, int off, uns m += (unsigned)FFABS(ctx->buf1[off]); } } - if (ctx->cmode2 != 0) { + if (ctx->vrq != 0) { int sum = 0; - for (int i = (signed)((unsigned)m << 6) / split; i > 0; i = i >> 1) + for (unsigned i = (m << 6) / rsize; i > 0; i = i >> 1) sum++; - sum = sum - (ctx->cmode2 + 7); - ctx->cmode = FFMAX(sum, tab[ctx->cmode2]); + sum -= (ctx->vrq + 7); + ctx->qfactor = FFMAX(sum, vrq_qfactors[ctx->vrq - 1]); } x += split; @@ -949,6 +953,10 @@ static int rka_decode_frame(AVCodecContext *avctx, AVFrame *frame, } } + if (frame->nb_samples < s->frame_samples && + frame->nb_samples > s->last_nb_samples) + frame->nb_samples = s->last_nb_samples; + *got_frame_ptr = 1; return avpkt->size; diff --git a/libavcodec/rkmppdec.c b/libavcodec/rkmppdec.c index 8bf7c6ed163..53b6f6d5e89 100644 --- a/libavcodec/rkmppdec.c +++ b/libavcodec/rkmppdec.c @@ -404,8 +404,10 @@ static int rkmpp_retrieve_frame(AVCodecContext *avctx, AVFrame *frame) frame->colorspace = mpp_frame_get_colorspace(mppframe); mode = mpp_frame_get_mode(mppframe); - frame->interlaced_frame = ((mode & MPP_FRAME_FLAG_FIELD_ORDER_MASK) == MPP_FRAME_FLAG_DEINTERLACED); - frame->top_field_first = ((mode & MPP_FRAME_FLAG_FIELD_ORDER_MASK) == MPP_FRAME_FLAG_TOP_FIRST); + if ((mode & MPP_FRAME_FLAG_FIELD_ORDER_MASK) == MPP_FRAME_FLAG_DEINTERLACED) + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if ((mode & MPP_FRAME_FLAG_FIELD_ORDER_MASK) == MPP_FRAME_FLAG_TOP_FIRST) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; mppformat = mpp_frame_get_fmt(mppframe); drmformat = rkmpp_get_frameformat(mppformat); @@ -573,8 +575,6 @@ static const AVCodecHWConfigInternal *const rkmpp_hw_configs[] = { .flush = rkmpp_flush, \ .p.priv_class = &rkmpp_##NAME##_dec_class, \ .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_HARDWARE, \ - .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_DRM_PRIME, \ - AV_PIX_FMT_NONE}, \ .hw_configs = rkmpp_hw_configs, \ .bsfs = BSFS, \ .p.wrapper_name = "rkmpp", \ diff --git a/libavcodec/rl.c b/libavcodec/rl.c index 3f8271d37e4..a78242d488e 100644 --- a/libavcodec/rl.c +++ b/libavcodec/rl.c @@ -82,7 +82,9 @@ av_cold void ff_rl_init_vlc(RLTable *rl, unsigned static_size) VLCElem table[1500] = { 0 }; VLC vlc = { .table = table, .table_allocated = static_size }; av_assert0(static_size <= FF_ARRAY_ELEMS(table)); - init_vlc(&vlc, 9, rl->n + 1, &rl->table_vlc[0][1], 4, 2, &rl->table_vlc[0][0], 4, 2, INIT_VLC_USE_NEW_STATIC); + vlc_init(&vlc, 9, rl->n + 1, + &rl->table_vlc[0][1], 4, 2, + &rl->table_vlc[0][0], 4, 2, VLC_INIT_USE_STATIC); for (q = 0; q < 32; q++) { int qmul = q * 2; diff --git a/libavcodec/rl.h b/libavcodec/rl.h index 4380fda272c..c45d8659d1a 100644 --- a/libavcodec/rl.h +++ b/libavcodec/rl.h @@ -80,7 +80,7 @@ void ff_rl_init(RLTable *rl, uint8_t static_store[2][2*MAX_RUN + MAX_LEVEL + 3]) */ void ff_rl_init_vlc(RLTable *rl, unsigned static_size); -#define INIT_VLC_RL(rl, static_size)\ +#define VLC_INIT_RL(rl, static_size)\ {\ static RL_VLC_ELEM rl_vlc_table[32][static_size];\ \ diff --git a/libavcodec/roqvideo.c b/libavcodec/roqvideo.c index f9a3c8e083e..ca8c8de967e 100644 --- a/libavcodec/roqvideo.c +++ b/libavcodec/roqvideo.c @@ -111,13 +111,13 @@ static inline void apply_motion_generic(RoqContext *ri, int x, int y, int deltax /* check MV against frame boundaries */ if ((mx < 0) || (mx > ri->width - sz) || (my < 0) || (my > ri->height - sz)) { - av_log(ri->avctx, AV_LOG_ERROR, "motion vector out of bounds: MV = (%d, %d), boundaries = (0, 0, %d, %d)\n", + av_log(ri->logctx, AV_LOG_ERROR, "motion vector out of bounds: MV = (%d, %d), boundaries = (0, 0, %d, %d)\n", mx, my, ri->width, ri->height); return; } if (!ri->last_frame->data[0]) { - av_log(ri->avctx, AV_LOG_ERROR, "Invalid decode type. Invalid header?\n"); + av_log(ri->logctx, AV_LOG_ERROR, "Invalid decode type. Invalid header?\n"); return; } diff --git a/libavcodec/roqvideo.h b/libavcodec/roqvideo.h index 8318b6e5a0b..2c2e42884de 100644 --- a/libavcodec/roqvideo.h +++ b/libavcodec/roqvideo.h @@ -22,7 +22,7 @@ #ifndef AVCODEC_ROQVIDEO_H #define AVCODEC_ROQVIDEO_H -#include "avcodec.h" +#include "libavutil/frame.h" typedef struct roq_cell { unsigned char y[4]; @@ -39,7 +39,7 @@ typedef struct motion_vect { typedef struct RoqContext { const AVClass *class; - AVCodecContext *avctx; + void *logctx; AVFrame *last_frame; AVFrame *current_frame; int width, height; diff --git a/libavcodec/roqvideodec.c b/libavcodec/roqvideodec.c index 6f2e48d2f3d..bfc69a65c90 100644 --- a/libavcodec/roqvideodec.c +++ b/libavcodec/roqvideodec.c @@ -72,7 +72,7 @@ static void roqvideo_decode_frame(RoqContext *ri, GetByteContext *gb) xpos = ypos = 0; if (chunk_size > bytestream2_get_bytes_left(gb)) { - av_log(ri->avctx, AV_LOG_ERROR, "Chunk does not fit in input buffer\n"); + av_log(ri->logctx, AV_LOG_ERROR, "Chunk does not fit in input buffer\n"); chunk_size = bytestream2_get_bytes_left(gb); } @@ -80,7 +80,7 @@ static void roqvideo_decode_frame(RoqContext *ri, GetByteContext *gb) for (yp = ypos; yp < ypos + 16; yp += 8) for (xp = xpos; xp < xpos + 16; xp += 8) { if (bytestream2_tell(gb) >= chunk_start + chunk_size) { - av_log(ri->avctx, AV_LOG_VERBOSE, "Chunk is too short\n"); + av_log(ri->logctx, AV_LOG_VERBOSE, "Chunk is too short\n"); return; } if (vqflg_pos < 0) { @@ -114,7 +114,7 @@ static void roqvideo_decode_frame(RoqContext *ri, GetByteContext *gb) if(k & 0x02) y += 4; if (bytestream2_tell(gb) >= chunk_start + chunk_size) { - av_log(ri->avctx, AV_LOG_VERBOSE, "Chunk is too short\n"); + av_log(ri->logctx, AV_LOG_VERBOSE, "Chunk is too short\n"); return; } if (vqflg_pos < 0) { @@ -169,7 +169,7 @@ static av_cold int roq_decode_init(AVCodecContext *avctx) { RoqContext *s = avctx->priv_data; - s->avctx = avctx; + s->logctx = avctx; if (avctx->width % 16 || avctx->height % 16) { avpriv_request_sample(avctx, "Dimensions not being a multiple of 16"); diff --git a/libavcodec/roqvideoenc.c b/libavcodec/roqvideoenc.c index c25aa39b735..0933abf4f99 100644 --- a/libavcodec/roqvideoenc.c +++ b/libavcodec/roqvideoenc.c @@ -911,10 +911,10 @@ static int roq_encode_video(RoqEncContext *enc) /* Quake 3 can't handle chunks bigger than 65535 bytes */ if (tempData->mainChunkSize/8 > 65535 && enc->quake3_compat) { if (enc->lambda > 100000) { - av_log(roq->avctx, AV_LOG_ERROR, "Cannot encode video in Quake compatible form\n"); + av_log(roq->logctx, AV_LOG_ERROR, "Cannot encode video in Quake compatible form\n"); return AVERROR(EINVAL); } - av_log(roq->avctx, AV_LOG_ERROR, + av_log(roq->logctx, AV_LOG_ERROR, "Warning, generated a frame too big for Quake (%d > 65535), " "now switching to a bigger qscale value.\n", tempData->mainChunkSize/8); @@ -972,7 +972,7 @@ static av_cold int roq_encode_init(AVCodecContext *avctx) av_lfg_init(&enc->randctx, 1); - roq->avctx = avctx; + roq->logctx = avctx; enc->framesSinceKeyframe = 0; if ((avctx->width & 0xf) || (avctx->height & 0xf)) { @@ -1057,8 +1057,6 @@ static int roq_encode_frame(AVCodecContext *avctx, AVPacket *pkt, RoqContext *const roq = &enc->common; int size, ret; - roq->avctx = avctx; - enc->frame_to_enc = frame; if (frame->quality) diff --git a/libavcodec/rpzaenc.c b/libavcodec/rpzaenc.c index da9500e4242..d84555d6c66 100644 --- a/libavcodec/rpzaenc.c +++ b/libavcodec/rpzaenc.c @@ -30,6 +30,7 @@ #include "avcodec.h" #include "codec_internal.h" #include "encode.h" +#include "mathops.h" #include "put_bits.h" typedef struct RpzaContext { @@ -65,7 +66,7 @@ typedef struct rgb { #define SQR(x) ((x) * (x)) /* 15 bit components */ -#define GET_CHAN(color, chan) (((color) >> ((chan) * 5) & 0x1F) * 8) +#define GET_CHAN(color, chan) (((color) >> ((chan) * 5) & 0x1F)) #define R(color) GET_CHAN(color, RED) #define G(color) GET_CHAN(color, GREEN) #define B(color) GET_CHAN(color, BLUE) @@ -141,9 +142,9 @@ static uint16_t rgb24_to_rgb555(const uint8_t *rgb24) uint16_t rgb555 = 0; uint32_t r, g, b; - r = rgb24[0] >> 3; - g = rgb24[1] >> 3; - b = rgb24[2] >> 3; + r = rgb24[0]; + g = rgb24[1]; + b = rgb24[2]; rgb555 |= (r << 10); rgb555 |= (g << 5); @@ -185,7 +186,7 @@ static int max_component_diff(const uint16_t *colorA, const uint16_t *colorB) if (diff > max) { max = diff; } - return max * 8; + return max; } /* @@ -266,9 +267,9 @@ static int compare_blocks(const uint16_t *block1, const uint16_t *block2, */ static int leastsquares(const uint16_t *block_ptr, const BlockInfo *bi, channel_offset xchannel, channel_offset ychannel, - double *slope, double *y_intercept, double *correlation_coef) + int *slope, int *y_intercept, int *correlation_coef) { - double sumx = 0, sumy = 0, sumx2 = 0, sumy2 = 0, sumxy = 0, + int sumx = 0, sumy = 0, sumx2 = 0, sumy2 = 0, sumxy = 0, sumx_sq = 0, sumy_sq = 0, tmp, tmp2; int i, j, count; uint8_t x, y; @@ -305,10 +306,10 @@ static int leastsquares(const uint16_t *block_ptr, const BlockInfo *bi, tmp2 = count * sumy2 - sumy_sq; if (tmp2 == 0) { - *correlation_coef = 0.0; + *correlation_coef = 0; } else { *correlation_coef = (count * sumxy - sumx * sumy) / - sqrt(tmp * tmp2); + ff_sqrt((unsigned)tmp * tmp2); } return 0; // success @@ -332,18 +333,18 @@ static int calc_lsq_max_fit_error(const uint16_t *block_ptr, const BlockInfo *bi y = GET_CHAN(block_ptr[j], ychannel); /* calculate x_inc as the 4-color index (0..3) */ - x_inc = floor( (x - min) * 3.0 / (max - min) + 0.5); + x_inc = (x - min) * 3 / (max - min) + 1; x_inc = FFMAX(FFMIN(3, x_inc), 0); /* calculate lin_y corresponding to x_inc */ - lin_y = (int)(tmp_min + (tmp_max - tmp_min) * x_inc / 3.0 + 0.5); + lin_y = tmp_min + (tmp_max - tmp_min) * x_inc / 3 + 1; err = FFABS(lin_y - y); if (err > max_err) max_err = err; /* calculate lin_x corresponding to x_inc */ - lin_x = (int)(min + (max - min) * x_inc / 3.0 + 0.5); + lin_x = min + (max - min) * x_inc / 3 + 1; err = FFABS(lin_x - x); if (err > max_err) @@ -577,7 +578,7 @@ static void rpza_encode_stream(RpzaContext *s, const AVFrame *pict) uint8_t avg_color[3]; int pixel_count; uint8_t min_color[3], max_color[3]; - double slope, y_intercept, correlation_coef; + int slope, y_intercept, correlation_coef; const uint16_t *src_pixels = (const uint16_t *)pict->data[0]; uint16_t *prev_pixels = (uint16_t *)s->prev_frame->data[0]; @@ -730,8 +731,8 @@ post_skip : min_color[i] = GET_CHAN(src_pixels[block_offset], i); max_color[i] = GET_CHAN(src_pixels[block_offset], i); } else { - tmp_min = (int)(0.5 + min * slope + y_intercept); - tmp_max = (int)(0.5 + max * slope + y_intercept); + tmp_min = 1 + min * slope + y_intercept; + tmp_max = 1 + max * slope + y_intercept; av_assert0(tmp_min <= tmp_max); // clamp min and max color values @@ -802,7 +803,7 @@ static int rpza_encode_frame(AVCodecContext *avctx, AVPacket *pkt, { RpzaContext *s = avctx->priv_data; uint8_t *buf; - int ret = ff_alloc_packet(avctx, pkt, 4LL + 6LL * avctx->height * avctx->width); + int ret = ff_alloc_packet(avctx, pkt, 4LL + 6LL * FFMAX(avctx->height, 4) * FFMAX(avctx->width, 4)); if (ret < 0) return ret; diff --git a/libavcodec/rscc.c b/libavcodec/rscc.c index 61a25df3821..ace9aeeb405 100644 --- a/libavcodec/rscc.c +++ b/libavcodec/rscc.c @@ -339,14 +339,21 @@ static int rscc_decode_frame(AVCodecContext *avctx, AVFrame *frame, /* Keyframe when the number of pixels updated matches the whole surface */ if (pixel_size == ctx->inflated_size) { frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; } else { frame->pict_type = AV_PICTURE_TYPE_P; } /* Palette handling */ if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { - frame->palette_has_changed = ff_copy_palette(ctx->palette, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + frame->palette_has_changed = +#endif + ff_copy_palette(ctx->palette, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(frame->data[1], ctx->palette, AVPALETTE_SIZE); } // We only return a picture when enough of it is undamaged, this avoids copying nearly broken frames around diff --git a/libavcodec/rtjpeg.c b/libavcodec/rtjpeg.c index 8e02bce2e85..734e3875da7 100644 --- a/libavcodec/rtjpeg.c +++ b/libavcodec/rtjpeg.c @@ -167,7 +167,7 @@ void ff_rtjpeg_decode_init(RTJpegContext *c, int width, int height, c->h = height; } -void ff_rtjpeg_init(RTJpegContext *c, AVCodecContext *avctx) +void ff_rtjpeg_init(RTJpegContext *c, struct AVCodecContext *avctx) { int i; diff --git a/libavcodec/rtjpeg.h b/libavcodec/rtjpeg.h index d4dc0744081..14befb54898 100644 --- a/libavcodec/rtjpeg.h +++ b/libavcodec/rtjpeg.h @@ -24,6 +24,7 @@ #include +#include "libavutil/frame.h" #include "libavutil/mem_internal.h" #include "idctdsp.h" @@ -39,7 +40,8 @@ typedef struct RTJpegContext { DECLARE_ALIGNED(16, int16_t, block)[64]; } RTJpegContext; -void ff_rtjpeg_init(RTJpegContext *c, AVCodecContext *avctx); +struct AVCodecContext; +void ff_rtjpeg_init(RTJpegContext *c, struct AVCodecContext *avctx); void ff_rtjpeg_decode_init(RTJpegContext *c, int width, int height, const uint32_t *lquant, const uint32_t *cquant); diff --git a/libavcodec/rtv1.c b/libavcodec/rtv1.c new file mode 100644 index 00000000000..807c8a34666 --- /dev/null +++ b/libavcodec/rtv1.c @@ -0,0 +1,152 @@ +/* + * RTV1 decoder + * 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 +#include + +#include "avcodec.h" +#include "bytestream.h" +#include "codec_internal.h" +#include "decode.h" +#include "texturedsp.h" +#include "thread.h" + +static av_cold int decode_init(AVCodecContext *avctx) +{ + TextureDSPContext *dsp = avctx->priv_data; + avctx->pix_fmt = AV_PIX_FMT_BGR0; + ff_texturedsp_init(dsp); + return 0; +} + +static int decode_rtv1(GetByteContext *gb, uint8_t *dst, ptrdiff_t linesize, + int width, int height, int flag, + int (*dxt1_block)(uint8_t *dst, ptrdiff_t stride, const uint8_t *block)) +{ + uint8_t block[8] = { 0 }; + int run = 0; + + for (int y = 0; y < height; y += 4) { + for (int x = 0; x < width * 4; x += 16) { + int mode = 0; + + if (run && --run > 0) { + dxt1_block(dst + x, linesize, block); + } else { + int a, b; + + if (bytestream2_get_bytes_left(gb) < 4) + return AVERROR_INVALIDDATA; + + a = bytestream2_get_le16u(gb); + b = bytestream2_get_le16u(gb); + if (a == b && flag) { + AV_WL32(block + 4, 0); + } else if (a == 1 && b == 0xffff) { + mode = 1; + } else if (b && a == 0) { + run = b; + } else { + AV_WL16(block, a); + AV_WL16(block + 2, b); + AV_WL32(block + 4, bytestream2_get_le32(gb)); + } + if (run && !mode) { + dxt1_block(dst + x, linesize, block); + } else if (!mode) { + AV_WL16(block, a); + AV_WL16(block + 2, b); + dxt1_block(dst + x, linesize, block); + } else { + if (bytestream2_get_bytes_left(gb) < 12 * 4) + return AVERROR_INVALIDDATA; + + for (int by = 0; by < 4; by++) { + for (int bx = 0; bx < 4; bx++) + AV_WL32(dst + x + bx * 4 + by * linesize, bytestream2_get_le24u(gb)); + } + } + } + } + + dst += linesize * 4; + } + + return 0; +} + +static int decode_frame(AVCodecContext *avctx, AVFrame *p, + int *got_frame, AVPacket *avpkt) +{ + int ret, width, height, flags; + TextureDSPContext *dsp = avctx->priv_data; + GetByteContext gb; + ptrdiff_t linesize; + uint8_t *dst; + + if (avpkt->size < 22) + return AVERROR_INVALIDDATA; + + bytestream2_init(&gb, avpkt->data, avpkt->size); + + if (bytestream2_get_le32(&gb) != MKTAG('D','X','T','1')) + return AVERROR_INVALIDDATA; + flags = bytestream2_get_le32(&gb); + + width = bytestream2_get_le32(&gb); + height = bytestream2_get_le32(&gb); + if (width > INT_MAX-4U || height > INT_MAX-4U) + return AVERROR_INVALIDDATA; + ret = ff_set_dimensions(avctx, FFALIGN(width, 4), FFALIGN(height, 4)); + if (ret < 0) + return ret; + + avctx->width = width; + avctx->height = height; + + if ((ret = ff_thread_get_buffer(avctx, p, 0)) < 0) + return ret; + + dst = p->data[0] + p->linesize[0] * (avctx->coded_height - 1); + linesize = -p->linesize[0]; + + ret = decode_rtv1(&gb, dst, linesize, width, height, flags, dsp->dxt1_block); + if (ret < 0) + return ret; + + p->pict_type = AV_PICTURE_TYPE_I; + p->flags |= AV_FRAME_FLAG_KEY; + + *got_frame = 1; + + return avpkt->size; +} + +const FFCodec ff_rtv1_decoder = { + .p.name = "rtv1", + CODEC_LONG_NAME("RTV1 (RivaTuner Video)"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_RTV1, + .priv_data_size = sizeof(TextureDSPContext), + .init = decode_init, + FF_CODEC_DECODE_CB(decode_frame), + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, +}; diff --git a/libavcodec/rv10.c b/libavcodec/rv10.c index 2233edfca55..d32faa628bd 100644 --- a/libavcodec/rv10.c +++ b/libavcodec/rv10.c @@ -158,28 +158,14 @@ static int rv10_decode_picture_header(MpegEncContext *s) static int rv20_decode_picture_header(RVDecContext *rv, int whole_size) { + static const enum AVPictureType pict_types[] = + { AV_PICTURE_TYPE_I, AV_PICTURE_TYPE_I /* hmm ... */, + AV_PICTURE_TYPE_P, AV_PICTURE_TYPE_B }; MpegEncContext *s = &rv->m; - int seq, mb_pos, i, ret; + int seq, mb_pos, ret; int rpr_max; - i = get_bits(&s->gb, 2); - switch (i) { - case 0: - s->pict_type = AV_PICTURE_TYPE_I; - break; - case 1: - s->pict_type = AV_PICTURE_TYPE_I; - break; // hmm ... - case 2: - s->pict_type = AV_PICTURE_TYPE_P; - break; - case 3: - s->pict_type = AV_PICTURE_TYPE_B; - break; - default: - av_log(s->avctx, AV_LOG_ERROR, "unknown frame type\n"); - return AVERROR_INVALIDDATA; - } + s->pict_type = pict_types[get_bits(&s->gb, 2)]; if (s->low_delay && s->pict_type == AV_PICTURE_TYPE_B) { av_log(s->avctx, AV_LOG_ERROR, "low delay B\n"); @@ -334,8 +320,8 @@ static av_cold void rv10_build_vlc(VLC *vlc, const uint16_t len_count[15], for (unsigned tmp = nb_lens + len_count[i]; nb_lens < tmp; nb_lens++) lens[nb_lens] = i + 2; av_assert1(nb_lens == nb_syms); - ff_init_vlc_from_lengths(vlc, DC_VLC_BITS, nb_lens, lens, 1, - syms, 2, 2, 0, INIT_VLC_STATIC_OVERLONG, NULL); + ff_vlc_init_from_lengths(vlc, DC_VLC_BITS, nb_lens, lens, 1, + syms, 2, 2, 0, VLC_INIT_STATIC_OVERLONG, NULL); } static av_cold void rv10_init_static(void) @@ -420,7 +406,6 @@ static av_cold int rv10_decode_init(AVCodecContext *avctx) avctx->pix_fmt = AV_PIX_FMT_YUV420P; - ff_mpv_idct_init(s); if ((ret = ff_mpv_common_init(s)) < 0) return ret; @@ -477,7 +462,7 @@ static int rv10_decode_packet(AVCodecContext *avctx, const uint8_t *buf, if ((s->mb_x == 0 && s->mb_y == 0) || !s->current_picture_ptr) { // FIXME write parser so we always have complete frames? if (s->current_picture_ptr) { - ff_er_frame_end(&s->er); + ff_er_frame_end(&s->er, NULL); ff_mpv_frame_end(s); s->mb_x = s->mb_y = s->resync_mb_x = s->resync_mb_y = 0; } @@ -587,10 +572,7 @@ static int rv10_decode_packet(AVCodecContext *avctx, const uint8_t *buf, static int get_slice_offset(AVCodecContext *avctx, const uint8_t *buf, int n) { - if (avctx->slice_count) - return avctx->slice_offset[n]; - else - return AV_RL32(buf + n * 8); + return AV_RL32(buf + n * 8); } static int rv10_decode_frame(AVCodecContext *avctx, AVFrame *pict, @@ -610,21 +592,18 @@ static int rv10_decode_frame(AVCodecContext *avctx, AVFrame *pict, return 0; } - if (!avctx->slice_count) { - slice_count = (*buf++) + 1; - buf_size--; + slice_count = (*buf++) + 1; + buf_size--; - if (!slice_count || buf_size <= 8 * slice_count) { - av_log(avctx, AV_LOG_ERROR, "Invalid slice count: %d.\n", - slice_count); - return AVERROR_INVALIDDATA; - } + if (!slice_count || buf_size <= 8 * slice_count) { + av_log(avctx, AV_LOG_ERROR, "Invalid slice count: %d.\n", + slice_count); + return AVERROR_INVALIDDATA; + } - slices_hdr = buf + 4; - buf += 8 * slice_count; - buf_size -= 8 * slice_count; - } else - slice_count = avctx->slice_count; + slices_hdr = buf + 4; + buf += 8 * slice_count; + buf_size -= 8 * slice_count; for (i = 0; i < slice_count; i++) { unsigned offset = get_slice_offset(avctx, slices_hdr, i); @@ -655,7 +634,7 @@ static int rv10_decode_frame(AVCodecContext *avctx, AVFrame *pict, } if (s->current_picture_ptr && s->mb_y >= s->mb_height) { - ff_er_frame_end(&s->er); + ff_er_frame_end(&s->er, NULL); ff_mpv_frame_end(s); if (s->pict_type == AV_PICTURE_TYPE_B || s->low_delay) { @@ -692,10 +671,6 @@ const FFCodec ff_rv10_decoder = { FF_CODEC_DECODE_CB(rv10_decode_frame), .p.capabilities = AV_CODEC_CAP_DR1, .p.max_lowres = 3, - .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE - }, }; const FFCodec ff_rv20_decoder = { @@ -710,8 +685,4 @@ const FFCodec ff_rv20_decoder = { .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, .flush = ff_mpeg_flush, .p.max_lowres = 3, - .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE - }, }; diff --git a/libavcodec/rv30.c b/libavcodec/rv30.c index be62577f995..9e13e71805e 100644 --- a/libavcodec/rv30.c +++ b/libavcodec/rv30.c @@ -303,10 +303,6 @@ const FFCodec ff_rv30_decoder = { .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_FRAME_THREADS, .flush = ff_mpeg_flush, - .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE - }, UPDATE_THREAD_CONTEXT(ff_rv34_decode_update_thread_context), .caps_internal = FF_CODEC_CAP_ALLOCATE_PROGRESS, }; diff --git a/libavcodec/rv34.c b/libavcodec/rv34.c index be188edc475..ed630cd5971 100644 --- a/libavcodec/rv34.c +++ b/libavcodec/rv34.c @@ -90,13 +90,15 @@ static VLCElem table_data[117592]; * @param insyms symbols for input codes (NULL for default ones) * @param num VLC table number (for static initialization) */ -static void rv34_gen_vlc(const uint8_t *bits, int size, VLC *vlc, const uint8_t *syms, - int *offset) +static av_cold void rv34_gen_vlc_ext(const uint8_t *bits, int size, VLC *vlc, + const uint8_t *syms, int *offset) { int counts[17] = {0}, codes[17]; uint16_t cw[MAX_VLC_SIZE]; int maxbits; + av_assert1(size > 0); + for (int i = 0; i < size; i++) counts[bits[i]]++; @@ -113,13 +115,21 @@ static void rv34_gen_vlc(const uint8_t *bits, int size, VLC *vlc, const uint8_t vlc->table = &table_data[*offset]; vlc->table_allocated = FF_ARRAY_ELEMS(table_data) - *offset; - ff_init_vlc_sparse(vlc, FFMIN(maxbits, 9), size, + ff_vlc_init_sparse(vlc, FFMIN(maxbits, 9), size, bits, 1, 1, cw, 2, 2, - syms, !!syms, !!syms, INIT_VLC_STATIC_OVERLONG); + syms, !!syms, !!syms, VLC_INIT_STATIC_OVERLONG); *offset += vlc->table_size; } +static av_cold void rv34_gen_vlc(const uint8_t *bits, int size, const VLCElem **vlcp, + int *offset) +{ + VLC vlc = { 0 }; + rv34_gen_vlc_ext(bits, size, &vlc, NULL, offset); + *vlcp = vlc.table; +} + /** * Initialize all tables. */ @@ -130,41 +140,41 @@ static av_cold void rv34_init_tables(void) for(i = 0; i < NUM_INTRA_TABLES; i++){ for(j = 0; j < 2; j++){ rv34_gen_vlc(rv34_table_intra_cbppat [i][j], CBPPAT_VLC_SIZE, - &intra_vlcs[i].cbppattern[j], NULL, &offset); + &intra_vlcs[i].cbppattern[j], &offset); rv34_gen_vlc(rv34_table_intra_secondpat[i][j], OTHERBLK_VLC_SIZE, - &intra_vlcs[i].second_pattern[j], NULL, &offset); + &intra_vlcs[i].second_pattern[j], &offset); rv34_gen_vlc(rv34_table_intra_thirdpat [i][j], OTHERBLK_VLC_SIZE, - &intra_vlcs[i].third_pattern[j], NULL, &offset); + &intra_vlcs[i].third_pattern[j], &offset); for(k = 0; k < 4; k++){ - rv34_gen_vlc(rv34_table_intra_cbp[i][j+k*2], CBP_VLC_SIZE, - &intra_vlcs[i].cbp[j][k], rv34_cbp_code, &offset); + rv34_gen_vlc_ext(rv34_table_intra_cbp[i][j+k*2], CBP_VLC_SIZE, + &intra_vlcs[i].cbp[j][k], rv34_cbp_code, &offset); } } for(j = 0; j < 4; j++){ rv34_gen_vlc(rv34_table_intra_firstpat[i][j], FIRSTBLK_VLC_SIZE, - &intra_vlcs[i].first_pattern[j], NULL, &offset); + &intra_vlcs[i].first_pattern[j], &offset); } rv34_gen_vlc(rv34_intra_coeff[i], COEFF_VLC_SIZE, - &intra_vlcs[i].coefficient, NULL, &offset); + &intra_vlcs[i].coefficient, &offset); } for(i = 0; i < NUM_INTER_TABLES; i++){ rv34_gen_vlc(rv34_inter_cbppat[i], CBPPAT_VLC_SIZE, - &inter_vlcs[i].cbppattern[0], NULL, &offset); + &inter_vlcs[i].cbppattern[0], &offset); for(j = 0; j < 4; j++){ - rv34_gen_vlc(rv34_inter_cbp[i][j], CBP_VLC_SIZE, - &inter_vlcs[i].cbp[0][j], rv34_cbp_code, &offset); + rv34_gen_vlc_ext(rv34_inter_cbp[i][j], CBP_VLC_SIZE, + &inter_vlcs[i].cbp[0][j], rv34_cbp_code, &offset); } for(j = 0; j < 2; j++){ rv34_gen_vlc(rv34_table_inter_firstpat [i][j], FIRSTBLK_VLC_SIZE, - &inter_vlcs[i].first_pattern[j], NULL, &offset); + &inter_vlcs[i].first_pattern[j], &offset); rv34_gen_vlc(rv34_table_inter_secondpat[i][j], OTHERBLK_VLC_SIZE, - &inter_vlcs[i].second_pattern[j], NULL, &offset); + &inter_vlcs[i].second_pattern[j], &offset); rv34_gen_vlc(rv34_table_inter_thirdpat [i][j], OTHERBLK_VLC_SIZE, - &inter_vlcs[i].third_pattern[j], NULL, &offset); + &inter_vlcs[i].third_pattern[j], &offset); } rv34_gen_vlc(rv34_inter_coeff[i], COEFF_VLC_SIZE, - &inter_vlcs[i].coefficient, NULL, &offset); + &inter_vlcs[i].coefficient, &offset); } } @@ -178,7 +188,7 @@ static av_cold void rv34_init_tables(void) /** * Decode coded block pattern. */ -static int rv34_decode_cbp(GetBitContext *gb, RV34VLC *vlc, int table) +static int rv34_decode_cbp(GetBitContext *gb, const RV34VLC *vlc, int table) { int pattern, code, cbp=0; int ones; @@ -187,7 +197,7 @@ static int rv34_decode_cbp(GetBitContext *gb, RV34VLC *vlc, int table) const int *curshift = shifts; int i, t, mask; - code = get_vlc2(gb, vlc->cbppattern[table].table, 9, 2); + code = get_vlc2(gb, vlc->cbppattern[table], 9, 2); pattern = code & 0xF; code >>= 4; @@ -211,11 +221,12 @@ static int rv34_decode_cbp(GetBitContext *gb, RV34VLC *vlc, int table) /** * Get one coefficient value from the bitstream and store it. */ -static inline void decode_coeff(int16_t *dst, int coef, int esc, GetBitContext *gb, VLC* vlc, int q) +static inline void decode_coeff(int16_t *dst, int coef, int esc, GetBitContext *gb, + const VLCElem *vlc, int q) { if(coef){ if(coef == esc){ - coef = get_vlc2(gb, vlc->table, 9, 2); + coef = get_vlc2(gb, vlc, 9, 2); if(coef > 23){ coef -= 23; coef = 22 + ((1 << coef) | get_bits(gb, coef)); @@ -231,7 +242,8 @@ static inline void decode_coeff(int16_t *dst, int coef, int esc, GetBitContext * /** * Decode 2x2 subblock of coefficients. */ -static inline void decode_subblock(int16_t *dst, int code, const int is_block2, GetBitContext *gb, VLC *vlc, int q) +static inline void decode_subblock(int16_t *dst, int code, const int is_block2, + GetBitContext *gb, const VLCElem *vlc, int q) { int flags = modulo_three_table[code]; @@ -249,13 +261,15 @@ static inline void decode_subblock(int16_t *dst, int code, const int is_block2, /** * Decode a single coefficient. */ -static inline void decode_subblock1(int16_t *dst, int code, GetBitContext *gb, VLC *vlc, int q) +static inline void decode_subblock1(int16_t *dst, int code, GetBitContext *gb, + const VLCElem *vlc, int q) { int coeff = modulo_three_table[code] >> 6; decode_coeff(dst, coeff, 3, gb, vlc, q); } -static inline void decode_subblock3(int16_t *dst, int code, GetBitContext *gb, VLC *vlc, +static inline void decode_subblock3(int16_t *dst, int code, GetBitContext *gb, + const VLCElem *vlc, int q_dc, int q_ac1, int q_ac2) { int flags = modulo_three_table[code]; @@ -277,36 +291,37 @@ static inline void decode_subblock3(int16_t *dst, int code, GetBitContext *gb, V * o--o */ -static int rv34_decode_block(int16_t *dst, GetBitContext *gb, RV34VLC *rvlc, int fc, int sc, int q_dc, int q_ac1, int q_ac2) +static int rv34_decode_block(int16_t *dst, GetBitContext *gb, const RV34VLC *rvlc, + int fc, int sc, int q_dc, int q_ac1, int q_ac2) { int code, pattern, has_ac = 1; - code = get_vlc2(gb, rvlc->first_pattern[fc].table, 9, 2); + code = get_vlc2(gb, rvlc->first_pattern[fc], 9, 2); pattern = code & 0x7; code >>= 3; if (modulo_three_table[code] & 0x3F) { - decode_subblock3(dst, code, gb, &rvlc->coefficient, q_dc, q_ac1, q_ac2); + decode_subblock3(dst, code, gb, rvlc->coefficient, q_dc, q_ac1, q_ac2); } else { - decode_subblock1(dst, code, gb, &rvlc->coefficient, q_dc); + decode_subblock1(dst, code, gb, rvlc->coefficient, q_dc); if (!pattern) return 0; has_ac = 0; } if(pattern & 4){ - code = get_vlc2(gb, rvlc->second_pattern[sc].table, 9, 2); - decode_subblock(dst + 4*0+2, code, 0, gb, &rvlc->coefficient, q_ac2); + code = get_vlc2(gb, rvlc->second_pattern[sc], 9, 2); + decode_subblock(dst + 4*0+2, code, 0, gb, rvlc->coefficient, q_ac2); } if(pattern & 2){ // Looks like coefficients 1 and 2 are swapped for this block - code = get_vlc2(gb, rvlc->second_pattern[sc].table, 9, 2); - decode_subblock(dst + 4*2+0, code, 1, gb, &rvlc->coefficient, q_ac2); + code = get_vlc2(gb, rvlc->second_pattern[sc], 9, 2); + decode_subblock(dst + 4*2+0, code, 1, gb, rvlc->coefficient, q_ac2); } if(pattern & 1){ - code = get_vlc2(gb, rvlc->third_pattern[sc].table, 9, 2); - decode_subblock(dst + 4*2+2, code, 0, gb, &rvlc->coefficient, q_ac2); + code = get_vlc2(gb, rvlc->third_pattern[sc], 9, 2); + decode_subblock(dst + 4*2+2, code, 0, gb, rvlc->coefficient, q_ac2); } return has_ac | pattern; } @@ -1498,7 +1513,6 @@ av_cold int ff_rv34_decode_init(AVCodecContext *avctx) avctx->has_b_frames = 1; s->low_delay = 0; - ff_mpv_idct_init(s); if ((ret = ff_mpv_common_init(s)) < 0) return ret; @@ -1549,8 +1563,7 @@ int ff_rv34_decode_update_thread_context(AVCodecContext *dst, const AVCodecConte static int get_slice_offset(AVCodecContext *avctx, const uint8_t *buf, int n, int slice_count, int buf_size) { if (n < slice_count) { - if(avctx->slice_count) return avctx->slice_offset[n]; - else return AV_RL32(buf + n*8 - 4) == 1 ? AV_RL32(buf + n*8) : AV_RB32(buf + n*8); + return AV_RL32(buf + n*8 - 4) == 1 ? AV_RL32(buf + n*8) : AV_RB32(buf + n*8); } else return buf_size; } @@ -1561,7 +1574,7 @@ static int finish_frame(AVCodecContext *avctx, AVFrame *pict) MpegEncContext *s = &r->s; int got_picture = 0, ret; - ff_er_frame_end(&s->er); + ff_er_frame_end(&s->er, NULL); ff_mpv_frame_end(s); s->mb_num_left = 0; @@ -1623,13 +1636,10 @@ int ff_rv34_decode_frame(AVCodecContext *avctx, AVFrame *pict, return 0; } - if(!avctx->slice_count){ - slice_count = (*buf++) + 1; - slices_hdr = buf + 4; - buf += 8 * slice_count; - buf_size -= 1 + 8 * slice_count; - }else - slice_count = avctx->slice_count; + slice_count = (*buf++) + 1; + slices_hdr = buf + 4; + buf += 8 * slice_count; + buf_size -= 1 + 8 * slice_count; offset = get_slice_offset(avctx, slices_hdr, 0, slice_count, buf_size); //parse first slice header to check whether this frame can be decoded @@ -1659,7 +1669,7 @@ int ff_rv34_decode_frame(AVCodecContext *avctx, AVFrame *pict, av_log(avctx, AV_LOG_ERROR, "New frame but still %d MB left.\n", s->mb_num_left); if (!s->context_reinit) - ff_er_frame_end(&s->er); + ff_er_frame_end(&s->er, NULL); ff_mpv_frame_end(s); } @@ -1696,6 +1706,8 @@ int ff_rv34_decode_frame(AVCodecContext *avctx, AVFrame *pict, int i; r->tmp_b_block_base = av_malloc(s->linesize * 48); + if (!r->tmp_b_block_base) + return AVERROR(ENOMEM); for (i = 0; i < 2; i++) r->tmp_b_block_y[i] = r->tmp_b_block_base + i * 16 * s->linesize; @@ -1792,7 +1804,7 @@ int ff_rv34_decode_frame(AVCodecContext *avctx, AVFrame *pict, av_log(avctx, AV_LOG_INFO, "marking unfished frame as finished\n"); /* always mark the current frame as finished, frame-mt supports * only complete frames */ - ff_er_frame_end(&s->er); + ff_er_frame_end(&s->er, NULL); ff_mpv_frame_end(s); s->mb_num_left = 0; ff_thread_report_progress(&s->current_picture_ptr->tf, INT_MAX, 0); diff --git a/libavcodec/rv34.h b/libavcodec/rv34.h index 84789625ce4..6fe1f8087dc 100644 --- a/libavcodec/rv34.h +++ b/libavcodec/rv34.h @@ -63,12 +63,12 @@ enum RV40BlockTypes{ * Intra frame VLC sets do not contain some of those tables. */ typedef struct RV34VLC{ - VLC cbppattern[2]; ///< VLCs used for pattern of coded block patterns decoding - VLC cbp[2][4]; ///< VLCs used for coded block patterns decoding - VLC first_pattern[4]; ///< VLCs used for decoding coefficients in the first subblock - VLC second_pattern[2]; ///< VLCs used for decoding coefficients in the subblocks 2 and 3 - VLC third_pattern[2]; ///< VLCs used for decoding coefficients in the last subblock - VLC coefficient; ///< VLCs used for decoding big coefficients + const VLCElem *cbppattern[2]; ///< VLCs used for pattern of coded block patterns decoding + VLC cbp[2][4]; ///< VLCs used for coded block patterns decoding + const VLCElem *first_pattern[4]; ///< VLCs used for decoding coefficients in the first subblock + const VLCElem *second_pattern[2]; ///< VLCs used for decoding coefficients in the subblocks 2 and 3 + const VLCElem *third_pattern[2]; ///< VLCs used for decoding coefficients in the last subblock + const VLCElem *coefficient; ///< VLCs used for decoding big coefficients }RV34VLC; /** essential slice information */ @@ -92,7 +92,7 @@ typedef struct RV34DecContext{ const uint8_t *luma_dc_quant_i;///< luma subblock DC quantizer for intraframes const uint8_t *luma_dc_quant_p;///< luma subblock DC quantizer for interframes - RV34VLC *cur_vlcs; ///< VLC set used for current frame decoding + const RV34VLC *cur_vlcs; ///< VLC set used for current frame decoding H264PredContext h; ///< functions for 4x4 and 16x16 intra block prediction SliceInfo si; ///< current slice information diff --git a/libavcodec/rv34_parser.c b/libavcodec/rv34_parser.c index e17bc8562db..2997a4db708 100644 --- a/libavcodec/rv34_parser.c +++ b/libavcodec/rv34_parser.c @@ -24,13 +24,10 @@ * RV30/40 parser */ -#include "config_components.h" - -#include "parser.h" +#include "avcodec.h" #include "libavutil/intreadwrite.h" typedef struct RV34ParseContext { - ParseContext pc; int64_t key_dts; int key_pts; } RV34ParseContext; @@ -78,18 +75,8 @@ static int rv34_parse(AVCodecParserContext *s, return buf_size; } -#if CONFIG_RV30_PARSER -const AVCodecParser ff_rv30_parser = { - .codec_ids = { AV_CODEC_ID_RV30 }, - .priv_data_size = sizeof(RV34ParseContext), - .parser_parse = rv34_parse, -}; -#endif - -#if CONFIG_RV40_PARSER -const AVCodecParser ff_rv40_parser = { - .codec_ids = { AV_CODEC_ID_RV40 }, +const AVCodecParser ff_rv34_parser = { + .codec_ids = { AV_CODEC_ID_RV30, AV_CODEC_ID_RV40 }, .priv_data_size = sizeof(RV34ParseContext), .parser_parse = rv34_parse, }; -#endif diff --git a/libavcodec/rv34dsp.c b/libavcodec/rv34dsp.c index 8f9d88396ce..44486f8edd9 100644 --- a/libavcodec/rv34dsp.c +++ b/libavcodec/rv34dsp.c @@ -138,6 +138,8 @@ av_cold void ff_rv34dsp_init(RV34DSPContext *c) #if ARCH_ARM ff_rv34dsp_init_arm(c); +#elif ARCH_RISCV + ff_rv34dsp_init_riscv(c); #elif ARCH_X86 ff_rv34dsp_init_x86(c); #endif diff --git a/libavcodec/rv34dsp.h b/libavcodec/rv34dsp.h index 2e9ec4eee44..b15424d4ae2 100644 --- a/libavcodec/rv34dsp.h +++ b/libavcodec/rv34dsp.h @@ -79,6 +79,7 @@ void ff_rv34dsp_init(RV34DSPContext *c); void ff_rv40dsp_init(RV34DSPContext *c); void ff_rv34dsp_init_arm(RV34DSPContext *c); +void ff_rv34dsp_init_riscv(RV34DSPContext *c); void ff_rv34dsp_init_x86(RV34DSPContext *c); void ff_rv40dsp_init_aarch64(RV34DSPContext *c); diff --git a/libavcodec/rv40.c b/libavcodec/rv40.c index 75849b173e2..e48aa1f6849 100644 --- a/libavcodec/rv40.c +++ b/libavcodec/rv40.c @@ -40,22 +40,16 @@ #include "rv40vlc2.h" #include "rv40data.h" -static VLC aic_top_vlc; -static VLC aic_mode1_vlc[AIC_MODE1_NUM], aic_mode2_vlc[AIC_MODE2_NUM]; -static VLC ptype_vlc[NUM_PTYPE_VLCS], btype_vlc[NUM_BTYPE_VLCS]; +static VLCElem aic_top_vlc[23590]; +static const VLCElem *aic_mode1_vlc[AIC_MODE1_NUM], *aic_mode2_vlc[AIC_MODE2_NUM]; +static const VLCElem *ptype_vlc[NUM_PTYPE_VLCS], *btype_vlc[NUM_BTYPE_VLCS]; -static av_cold void rv40_init_table(VLC *vlc, unsigned *offset, int nb_bits, - int nb_codes, const uint8_t (*tab)[2]) +static av_cold const VLCElem *rv40_init_table(VLCInitState *state, int nb_bits, + int nb_codes, const uint8_t (*tab)[2]) { - static VLCElem vlc_buf[11776]; - - vlc->table = &vlc_buf[*offset]; - vlc->table_allocated = 1 << nb_bits; - *offset += 1 << nb_bits; - - ff_init_vlc_from_lengths(vlc, nb_bits, nb_codes, - &tab[0][1], 2, &tab[0][0], 2, 1, - 0, INIT_VLC_USE_NEW_STATIC, NULL); + return ff_vlc_init_tables_from_lengths(state, nb_bits, nb_codes, + &tab[0][1], 2, &tab[0][0], 2, 1, + 0, 0); } /** @@ -63,18 +57,19 @@ static av_cold void rv40_init_table(VLC *vlc, unsigned *offset, int nb_bits, */ static av_cold void rv40_init_tables(void) { - int i, offset = 0; - static VLCElem aic_mode2_table[11814]; + VLCInitState state = VLC_INIT_STATE(aic_top_vlc); + int i; - rv40_init_table(&aic_top_vlc, &offset, AIC_TOP_BITS, AIC_TOP_SIZE, + rv40_init_table(&state, AIC_TOP_BITS, AIC_TOP_SIZE, rv40_aic_top_vlc_tab); for(i = 0; i < AIC_MODE1_NUM; i++){ // Every tenth VLC table is empty if((i % 10) == 9) continue; - rv40_init_table(&aic_mode1_vlc[i], &offset, AIC_MODE1_BITS, - AIC_MODE1_SIZE, aic_mode1_vlc_tabs[i]); + aic_mode1_vlc[i] = + rv40_init_table(&state, AIC_MODE1_BITS, + AIC_MODE1_SIZE, aic_mode1_vlc_tabs[i]); } - for (unsigned i = 0, offset = 0; i < AIC_MODE2_NUM; i++){ + for (unsigned i = 0; i < AIC_MODE2_NUM; i++){ uint16_t syms[AIC_MODE2_SIZE]; for (int j = 0; j < AIC_MODE2_SIZE; j++) { @@ -85,20 +80,20 @@ static av_cold void rv40_init_tables(void) else syms[j] = first | (second << 8); } - aic_mode2_vlc[i].table = &aic_mode2_table[offset]; - aic_mode2_vlc[i].table_allocated = FF_ARRAY_ELEMS(aic_mode2_table) - offset; - ff_init_vlc_from_lengths(&aic_mode2_vlc[i], AIC_MODE2_BITS, AIC_MODE2_SIZE, - aic_mode2_vlc_bits[i], 1, - syms, 2, 2, 0, INIT_VLC_STATIC_OVERLONG, NULL); - offset += aic_mode2_vlc[i].table_size; + aic_mode2_vlc[i] = + ff_vlc_init_tables_from_lengths(&state, AIC_MODE2_BITS, AIC_MODE2_SIZE, + aic_mode2_vlc_bits[i], 1, + syms, 2, 2, 0, 0); } for(i = 0; i < NUM_PTYPE_VLCS; i++){ - rv40_init_table(&ptype_vlc[i], &offset, PTYPE_VLC_BITS, PTYPE_VLC_SIZE, - ptype_vlc_tabs[i]); + ptype_vlc[i] = + rv40_init_table(&state, PTYPE_VLC_BITS, PTYPE_VLC_SIZE, + ptype_vlc_tabs[i]); } for(i = 0; i < NUM_BTYPE_VLCS; i++){ - rv40_init_table(&btype_vlc[i], &offset, BTYPE_VLC_BITS, BTYPE_VLC_SIZE, - btype_vlc_tabs[i]); + btype_vlc[i] = + rv40_init_table(&state, BTYPE_VLC_BITS, BTYPE_VLC_SIZE, + btype_vlc_tabs[i]); } } @@ -178,7 +173,7 @@ static int rv40_decode_intra_types(RV34DecContext *r, GetBitContext *gb, int8_t for(i = 0; i < 4; i++, dst += r->intra_types_stride){ if(!i && s->first_slice_line){ - pattern = get_vlc2(gb, aic_top_vlc.table, AIC_TOP_BITS, 1); + pattern = get_vlc2(gb, aic_top_vlc, AIC_TOP_BITS, 1); dst[0] = (pattern >> 2) & 2; dst[1] = (pattern >> 1) & 2; dst[2] = pattern & 2; @@ -201,12 +196,12 @@ static int rv40_decode_intra_types(RV34DecContext *r, GetBitContext *gb, int8_t if(pattern == rv40_aic_table_index[k]) break; if(j < 3 && k < MODE2_PATTERNS_NUM){ //pattern is found, decoding 2 coefficients - AV_WN16(ptr, get_vlc2(gb, aic_mode2_vlc[k].table, AIC_MODE2_BITS, 2)); + AV_WN16(ptr, get_vlc2(gb, aic_mode2_vlc[k], AIC_MODE2_BITS, 2)); ptr += 2; j++; }else{ if(B != -1 && C != -1) - v = get_vlc2(gb, aic_mode1_vlc[B + C*10].table, AIC_MODE1_BITS, 1); + v = get_vlc2(gb, aic_mode1_vlc[B + C*10], AIC_MODE1_BITS, 1); else{ // tricky decoding v = 0; switch(C){ @@ -270,17 +265,17 @@ static int rv40_decode_mb_info(RV34DecContext *r) if(s->pict_type == AV_PICTURE_TYPE_P){ prev_type = block_num_to_ptype_vlc_num[prev_type]; - q = get_vlc2(gb, ptype_vlc[prev_type].table, PTYPE_VLC_BITS, 1); + q = get_vlc2(gb, ptype_vlc[prev_type], PTYPE_VLC_BITS, 1); if(q < PBTYPE_ESCAPE) return q; - q = get_vlc2(gb, ptype_vlc[prev_type].table, PTYPE_VLC_BITS, 1); + q = get_vlc2(gb, ptype_vlc[prev_type], PTYPE_VLC_BITS, 1); av_log(s->avctx, AV_LOG_ERROR, "Dquant for P-frame\n"); }else{ prev_type = block_num_to_btype_vlc_num[prev_type]; - q = get_vlc2(gb, btype_vlc[prev_type].table, BTYPE_VLC_BITS, 1); + q = get_vlc2(gb, btype_vlc[prev_type], BTYPE_VLC_BITS, 1); if(q < PBTYPE_ESCAPE) return q; - q = get_vlc2(gb, btype_vlc[prev_type].table, BTYPE_VLC_BITS, 1); + q = get_vlc2(gb, btype_vlc[prev_type], BTYPE_VLC_BITS, 1); av_log(s->avctx, AV_LOG_ERROR, "Dquant for B-frame\n"); } return 0; @@ -586,10 +581,6 @@ const FFCodec ff_rv40_decoder = { .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_FRAME_THREADS, .flush = ff_mpeg_flush, - .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE - }, UPDATE_THREAD_CONTEXT(ff_rv34_decode_update_thread_context), .caps_internal = FF_CODEC_CAP_ALLOCATE_PROGRESS, }; diff --git a/libavcodec/s302m.c b/libavcodec/s302m.c index f1b41608f3f..f8ab0337e2b 100644 --- a/libavcodec/s302m.c +++ b/libavcodec/s302m.c @@ -211,11 +211,11 @@ static int s302m_decode_frame(AVCodecContext *avctx, AVFrame *frame, #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_DECODING_PARAM static const AVOption s302m_options[] = { - {"non_pcm_mode", "Chooses what to do with NON-PCM", offsetof(S302Context, non_pcm_mode), AV_OPT_TYPE_INT, {.i64 = 3}, 0, 3, FLAGS, "non_pcm_mode"}, - {"copy" , "Pass NON-PCM through unchanged" , 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 3, FLAGS, "non_pcm_mode"}, - {"drop" , "Drop NON-PCM" , 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 3, FLAGS, "non_pcm_mode"}, - {"decode_copy" , "Decode if possible else passthrough", 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 3, FLAGS, "non_pcm_mode"}, - {"decode_drop" , "Decode if possible else drop" , 0, AV_OPT_TYPE_CONST, {.i64 = 3}, 0, 3, FLAGS, "non_pcm_mode"}, + {"non_pcm_mode", "Chooses what to do with NON-PCM", offsetof(S302Context, non_pcm_mode), AV_OPT_TYPE_INT, {.i64 = 3}, 0, 3, FLAGS, .unit = "non_pcm_mode"}, + {"copy" , "Pass NON-PCM through unchanged" , 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 3, FLAGS, .unit = "non_pcm_mode"}, + {"drop" , "Drop NON-PCM" , 0, AV_OPT_TYPE_CONST, {.i64 = 1}, 0, 3, FLAGS, .unit = "non_pcm_mode"}, + {"decode_copy" , "Decode if possible else passthrough", 0, AV_OPT_TYPE_CONST, {.i64 = 2}, 0, 3, FLAGS, .unit = "non_pcm_mode"}, + {"decode_drop" , "Decode if possible else drop" , 0, AV_OPT_TYPE_CONST, {.i64 = 3}, 0, 3, FLAGS, .unit = "non_pcm_mode"}, {NULL} }; diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c index bec3c770ec4..b70daab7ac9 100644 --- a/libavcodec/sanm.c +++ b/libavcodec/sanm.c @@ -1484,11 +1484,13 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; ctx->rotate_code = header.rotate_code; - if ((ctx->frame->key_frame = !header.seq_num)) { + if (!header.seq_num) { + ctx->frame->flags |= AV_FRAME_FLAG_KEY; ctx->frame->pict_type = AV_PICTURE_TYPE_I; fill_frame(ctx->frm1, ctx->npixels, header.bg_color); fill_frame(ctx->frm2, ctx->npixels, header.bg_color); } else { + ctx->frame->flags &= ~AV_FRAME_FLAG_KEY; ctx->frame->pict_type = AV_PICTURE_TYPE_P; } diff --git a/libavcodec/sbcdec.c b/libavcodec/sbcdec.c index fd7c2e5e806..033a8380d6f 100644 --- a/libavcodec/sbcdec.c +++ b/libavcodec/sbcdec.c @@ -371,7 +371,6 @@ const FFCodec ff_sbc_decoder = { .init = sbc_decode_init, FF_CODEC_DECODE_CB(sbc_decode_frame), .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, { 0 } }, diff --git a/libavcodec/sbcenc.c b/libavcodec/sbcenc.c index fccb0e3ea3f..f2c4fbe3297 100644 --- a/libavcodec/sbcenc.c +++ b/libavcodec/sbcenc.c @@ -31,6 +31,7 @@ */ #include "libavutil/channel_layout.h" +#include "libavutil/emms.h" #include "libavutil/opt.h" #include "avcodec.h" #include "codec_internal.h" @@ -198,7 +199,7 @@ static int sbc_encode_init(AVCodecContext *avctx) SBCEncContext *sbc = avctx->priv_data; struct sbc_frame *frame = &sbc->frame; - if (avctx->profile == FF_PROFILE_SBC_MSBC) + if (avctx->profile == AV_PROFILE_SBC_MSBC) sbc->msbc = 1; if (sbc->msbc) { @@ -332,7 +333,7 @@ static const AVOption options[] = { OFFSET(max_delay), AV_OPT_TYPE_DURATION, {.i64 = 13000}, 1000,13000, AE }, { "msbc", "use mSBC mode (wideband speech mono SBC)", OFFSET(msbc), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AE }, - FF_AVCTX_PROFILE_OPTION("msbc", NULL, AUDIO, FF_PROFILE_SBC_MSBC) + FF_AVCTX_PROFILE_OPTION("msbc", NULL, AUDIO, AV_PROFILE_SBC_MSBC) { NULL }, }; @@ -353,7 +354,6 @@ const FFCodec ff_sbc_encoder = { .priv_data_size = sizeof(SBCEncContext), .init = sbc_encode_init, FF_CODEC_ENCODE_CB(sbc_encode_frame), - CODEC_OLD_CHANNEL_LAYOUTS(AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO) .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, { 0 } }, diff --git a/libavcodec/sbr.h b/libavcodec/sbr.h index f949465ef5b..fe3a39603a1 100644 --- a/libavcodec/sbr.h +++ b/libavcodec/sbr.h @@ -37,8 +37,6 @@ #include "aacps.h" #include "sbrdsp.h" -typedef struct AACContext AACContext; - /** * Spectral Band Replication header - spectrum parameters that invoke a reset if they differ from the previous header. */ @@ -121,7 +119,7 @@ typedef struct SpectralBandReplication SpectralBandReplication; * aacsbr functions pointers */ typedef struct AACSBRContext { - int (*sbr_lf_gen)(AACContext *ac, SpectralBandReplication *sbr, + int (*sbr_lf_gen)(SpectralBandReplication *sbr, INTFLOAT X_low[32][40][2], const INTFLOAT W[2][32][32][2], int buf_idx); void (*sbr_hf_assemble)(INTFLOAT Y1[38][64][2], diff --git a/libavcodec/sbrdsp.h b/libavcodec/sbrdsp.h index 8513c423af9..49782202a7e 100644 --- a/libavcodec/sbrdsp.h +++ b/libavcodec/sbrdsp.h @@ -48,6 +48,7 @@ extern const INTFLOAT AAC_RENAME(ff_sbr_noise_table)[][2]; void AAC_RENAME(ff_sbrdsp_init)(SBRDSPContext *s); void ff_sbrdsp_init_arm(SBRDSPContext *s); void ff_sbrdsp_init_aarch64(SBRDSPContext *s); +void ff_sbrdsp_init_riscv(SBRDSPContext *s); void ff_sbrdsp_init_x86(SBRDSPContext *s); void ff_sbrdsp_init_mips(SBRDSPContext *s); diff --git a/libavcodec/sbrdsp_template.c b/libavcodec/sbrdsp_template.c index b3737c0fd80..75cf3dbdfce 100644 --- a/libavcodec/sbrdsp_template.c +++ b/libavcodec/sbrdsp_template.c @@ -20,6 +20,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "config.h" +#include "libavutil/attributes_internal.h" +#include "libavutil/mem_internal.h" + static void sbr_sum64x5_c(INTFLOAT *z) { int k; @@ -96,6 +100,8 @@ av_cold void AAC_RENAME(ff_sbrdsp_init)(SBRDSPContext *s) ff_sbrdsp_init_arm(s); #elif ARCH_AARCH64 ff_sbrdsp_init_aarch64(s); +#elif ARCH_RISCV + ff_sbrdsp_init_riscv(s); #elif ARCH_X86 ff_sbrdsp_init_x86(s); #elif ARCH_MIPS @@ -103,3 +109,274 @@ av_cold void AAC_RENAME(ff_sbrdsp_init)(SBRDSPContext *s) #endif #endif /* !USE_FIXED */ } + +/* First eight entries repeated at end to simplify SIMD implementations. */ +const attribute_visibility_hidden DECLARE_ALIGNED(16, INTFLOAT, AAC_RENAME(ff_sbr_noise_table))[][2] = { +{Q31(-0.99948153278296f), Q31(-0.59483417516607f)}, {Q31( 0.97113454393991f), Q31(-0.67528515225647f)}, +{Q31( 0.14130051758487f), Q31(-0.95090983575689f)}, {Q31(-0.47005496701697f), Q31(-0.37340549728647f)}, +{Q31( 0.80705063769351f), Q31( 0.29653668284408f)}, {Q31(-0.38981478896926f), Q31( 0.89572605717087f)}, +{Q31(-0.01053049862020f), Q31(-0.66959058036166f)}, {Q31(-0.91266367957293f), Q31(-0.11522938140034f)}, +{Q31( 0.54840422910309f), Q31( 0.75221367176302f)}, {Q31( 0.40009252867955f), Q31(-0.98929400334421f)}, +{Q31(-0.99867974711855f), Q31(-0.88147068645358f)}, {Q31(-0.95531076805040f), Q31( 0.90908757154593f)}, +{Q31(-0.45725933317144f), Q31(-0.56716323646760f)}, {Q31(-0.72929675029275f), Q31(-0.98008272727324f)}, +{Q31( 0.75622801399036f), Q31( 0.20950329995549f)}, {Q31( 0.07069442601050f), Q31(-0.78247898470706f)}, +{Q31( 0.74496252926055f), Q31(-0.91169004445807f)}, {Q31(-0.96440182703856f), Q31(-0.94739918296622f)}, +{Q31( 0.30424629369539f), Q31(-0.49438267012479f)}, {Q31( 0.66565033746925f), Q31( 0.64652935542491f)}, +{Q31( 0.91697008020594f), Q31( 0.17514097332009f)}, {Q31(-0.70774918760427f), Q31( 0.52548653416543f)}, +{Q31(-0.70051415345560f), Q31(-0.45340028808763f)}, {Q31(-0.99496513054797f), Q31(-0.90071908066973f)}, +{Q31( 0.98164490790123f), Q31(-0.77463155528697f)}, {Q31(-0.54671580548181f), Q31(-0.02570928536004f)}, +{Q31(-0.01689629065389f), Q31( 0.00287506445732f)}, {Q31(-0.86110349531986f), Q31( 0.42548583726477f)}, +{Q31(-0.98892980586032f), Q31(-0.87881132267556f)}, {Q31( 0.51756627678691f), Q31( 0.66926784710139f)}, +{Q31(-0.99635026409640f), Q31(-0.58107730574765f)}, {Q31(-0.99969370862163f), Q31( 0.98369989360250f)}, +{Q31( 0.55266258627194f), Q31( 0.59449057465591f)}, {Q31( 0.34581177741673f), Q31( 0.94879421061866f)}, +{Q31( 0.62664209577999f), Q31(-0.74402970906471f)}, {Q31(-0.77149701404973f), Q31(-0.33883658042801f)}, +{Q31(-0.91592244254432f), Q31( 0.03687901376713f)}, {Q31(-0.76285492357887f), Q31(-0.91371867919124f)}, +{Q31( 0.79788337195331f), Q31(-0.93180971199849f)}, {Q31( 0.54473080610200f), Q31(-0.11919206037186f)}, +{Q31(-0.85639281671058f), Q31( 0.42429854760451f)}, {Q31(-0.92882402971423f), Q31( 0.27871809078609f)}, +{Q31(-0.11708371046774f), Q31(-0.99800843444966f)}, {Q31( 0.21356749817493f), Q31(-0.90716295627033f)}, +{Q31(-0.76191692573909f), Q31( 0.99768118356265f)}, {Q31( 0.98111043100884f), Q31(-0.95854459734407f)}, +{Q31(-0.85913269895572f), Q31( 0.95766566168880f)}, {Q31(-0.93307242253692f), Q31( 0.49431757696466f)}, +{Q31( 0.30485754879632f), Q31(-0.70540034357529f)}, {Q31( 0.85289650925190f), Q31( 0.46766131791044f)}, +{Q31( 0.91328082618125f), Q31(-0.99839597361769f)}, {Q31(-0.05890199924154f), Q31( 0.70741827819497f)}, +{Q31( 0.28398686150148f), Q31( 0.34633555702188f)}, {Q31( 0.95258164539612f), Q31(-0.54893416026939f)}, +{Q31(-0.78566324168507f), Q31(-0.75568541079691f)}, {Q31(-0.95789495447877f), Q31(-0.20423194696966f)}, +{Q31( 0.82411158711197f), Q31( 0.96654618432562f)}, {Q31(-0.65185446735885f), Q31(-0.88734990773289f)}, +{Q31(-0.93643603134666f), Q31( 0.99870790442385f)}, {Q31( 0.91427159529618f), Q31(-0.98290505544444f)}, +{Q31(-0.70395684036886f), Q31( 0.58796798221039f)}, {Q31( 0.00563771969365f), Q31( 0.61768196727244f)}, +{Q31( 0.89065051931895f), Q31( 0.52783352697585f)}, {Q31(-0.68683707712762f), Q31( 0.80806944710339f)}, +{Q31( 0.72165342518718f), Q31(-0.69259857349564f)}, {Q31(-0.62928247730667f), Q31( 0.13627037407335f)}, +{Q31( 0.29938434065514f), Q31(-0.46051329682246f)}, {Q31(-0.91781958879280f), Q31(-0.74012716684186f)}, +{Q31( 0.99298717043688f), Q31( 0.40816610075661f)}, {Q31( 0.82368298622748f), Q31(-0.74036047190173f)}, +{Q31(-0.98512833386833f), Q31(-0.99972330709594f)}, {Q31(-0.95915368242257f), Q31(-0.99237800466040f)}, +{Q31(-0.21411126572790f), Q31(-0.93424819052545f)}, {Q31(-0.68821476106884f), Q31(-0.26892306315457f)}, +{Q31( 0.91851997982317f), Q31( 0.09358228901785f)}, {Q31(-0.96062769559127f), Q31( 0.36099095133739f)}, +{Q31( 0.51646184922287f), Q31(-0.71373332873917f)}, {Q31( 0.61130721139669f), Q31( 0.46950141175917f)}, +{Q31( 0.47336129371299f), Q31(-0.27333178296162f)}, {Q31( 0.90998308703519f), Q31( 0.96715662938132f)}, +{Q31( 0.44844799194357f), Q31( 0.99211574628306f)}, {Q31( 0.66614891079092f), Q31( 0.96590176169121f)}, +{Q31( 0.74922239129237f), Q31(-0.89879858826087f)}, {Q31(-0.99571588506485f), Q31( 0.52785521494349f)}, +{Q31( 0.97401082477563f), Q31(-0.16855870075190f)}, {Q31( 0.72683747733879f), Q31(-0.48060774432251f)}, +{Q31( 0.95432193457128f), Q31( 0.68849603408441f)}, {Q31(-0.72962208425191f), Q31(-0.76608443420917f)}, +{Q31(-0.85359479233537f), Q31( 0.88738125901579f)}, {Q31(-0.81412430338535f), Q31(-0.97480768049637f)}, +{Q31(-0.87930772356786f), Q31( 0.74748307690436f)}, {Q31(-0.71573331064977f), Q31(-0.98570608178923f)}, +{Q31( 0.83524300028228f), Q31( 0.83702537075163f)}, {Q31(-0.48086065601423f), Q31(-0.98848504923531f)}, +{Q31( 0.97139128574778f), Q31( 0.80093621198236f)}, {Q31( 0.51992825347895f), Q31( 0.80247631400510f)}, +{Q31(-0.00848591195325f), Q31(-0.76670128000486f)}, {Q31(-0.70294374303036f), Q31( 0.55359910445577f)}, +{Q31(-0.95894428168140f), Q31(-0.43265504344783f)}, {Q31( 0.97079252950321f), Q31( 0.09325857238682f)}, +{Q31(-0.92404293670797f), Q31( 0.85507704027855f)}, {Q31(-0.69506469500450f), Q31( 0.98633412625459f)}, +{Q31( 0.26559203620024f), Q31( 0.73314307966524f)}, {Q31( 0.28038443336943f), Q31( 0.14537913654427f)}, +{Q31(-0.74138124825523f), Q31( 0.99310339807762f)}, {Q31(-0.01752795995444f), Q31(-0.82616635284178f)}, +{Q31(-0.55126773094930f), Q31(-0.98898543862153f)}, {Q31( 0.97960898850996f), Q31(-0.94021446752851f)}, +{Q31(-0.99196309146936f), Q31( 0.67019017358456f)}, {Q31(-0.67684928085260f), Q31( 0.12631491649378f)}, +{Q31( 0.09140039465500f), Q31(-0.20537731453108f)}, {Q31(-0.71658965751996f), Q31(-0.97788200391224f)}, +{Q31( 0.81014640078925f), Q31( 0.53722648362443f)}, {Q31( 0.40616991671205f), Q31(-0.26469008598449f)}, +{Q31(-0.67680188682972f), Q31( 0.94502052337695f)}, {Q31( 0.86849774348749f), Q31(-0.18333598647899f)}, +{Q31(-0.99500381284851f), Q31(-0.02634122068550f)}, {Q31( 0.84329189340667f), Q31( 0.10406957462213f)}, +{Q31(-0.09215968531446f), Q31( 0.69540012101253f)}, {Q31( 0.99956173327206f), Q31(-0.12358542001404f)}, +{Q31(-0.79732779473535f), Q31(-0.91582524736159f)}, {Q31( 0.96349973642406f), Q31( 0.96640458041000f)}, +{Q31(-0.79942778496547f), Q31( 0.64323902822857f)}, {Q31(-0.11566039853896f), Q31( 0.28587846253726f)}, +{Q31(-0.39922954514662f), Q31( 0.94129601616966f)}, {Q31( 0.99089197565987f), Q31(-0.92062625581587f)}, +{Q31( 0.28631285179909f), Q31(-0.91035047143603f)}, {Q31(-0.83302725605608f), Q31(-0.67330410892084f)}, +{Q31( 0.95404443402072f), Q31( 0.49162765398743f)}, {Q31(-0.06449863579434f), Q31( 0.03250560813135f)}, +{Q31(-0.99575054486311f), Q31( 0.42389784469507f)}, {Q31(-0.65501142790847f), Q31( 0.82546114655624f)}, +{Q31(-0.81254441908887f), Q31(-0.51627234660629f)}, {Q31(-0.99646369485481f), Q31( 0.84490533520752f)}, +{Q31( 0.00287840603348f), Q31( 0.64768261158166f)}, {Q31( 0.70176989408455f), Q31(-0.20453028573322f)}, +{Q31( 0.96361882270190f), Q31( 0.40706967140989f)}, {Q31(-0.68883758192426f), Q31( 0.91338958840772f)}, +{Q31(-0.34875585502238f), Q31( 0.71472290693300f)}, {Q31( 0.91980081243087f), Q31( 0.66507455644919f)}, +{Q31(-0.99009048343881f), Q31( 0.85868021604848f)}, {Q31( 0.68865791458395f), Q31( 0.55660316809678f)}, +{Q31(-0.99484402129368f), Q31(-0.20052559254934f)}, {Q31( 0.94214511408023f), Q31(-0.99696425367461f)}, +{Q31(-0.67414626793544f), Q31( 0.49548221180078f)}, {Q31(-0.47339353684664f), Q31(-0.85904328834047f)}, +{Q31( 0.14323651387360f), Q31(-0.94145598222488f)}, {Q31(-0.29268293575672f), Q31( 0.05759224927952f)}, +{Q31( 0.43793861458754f), Q31(-0.78904969892724f)}, {Q31(-0.36345126374441f), Q31( 0.64874435357162f)}, +{Q31(-0.08750604656825f), Q31( 0.97686944362527f)}, {Q31(-0.96495267812511f), Q31(-0.53960305946511f)}, +{Q31( 0.55526940659947f), Q31( 0.78891523734774f)}, {Q31( 0.73538215752630f), Q31( 0.96452072373404f)}, +{Q31(-0.30889773919437f), Q31(-0.80664389776860f)}, {Q31( 0.03574995626194f), Q31(-0.97325616900959f)}, +{Q31( 0.98720684660488f), Q31( 0.48409133691962f)}, {Q31(-0.81689296271203f), Q31(-0.90827703628298f)}, +{Q31( 0.67866860118215f), Q31( 0.81284503870856f)}, {Q31(-0.15808569732583f), Q31( 0.85279555024382f)}, +{Q31( 0.80723395114371f), Q31(-0.24717418514605f)}, {Q31( 0.47788757329038f), Q31(-0.46333147839295f)}, +{Q31( 0.96367554763201f), Q31( 0.38486749303242f)}, {Q31(-0.99143875716818f), Q31(-0.24945277239809f)}, +{Q31( 0.83081876925833f), Q31(-0.94780851414763f)}, {Q31(-0.58753191905341f), Q31( 0.01290772389163f)}, +{Q31( 0.95538108220960f), Q31(-0.85557052096538f)}, {Q31(-0.96490920476211f), Q31(-0.64020970923102f)}, +{Q31(-0.97327101028521f), Q31( 0.12378128133110f)}, {Q31( 0.91400366022124f), Q31( 0.57972471346930f)}, +{Q31(-0.99925837363824f), Q31( 0.71084847864067f)}, {Q31(-0.86875903507313f), Q31(-0.20291699203564f)}, +{Q31(-0.26240034795124f), Q31(-0.68264554369108f)}, {Q31(-0.24664412953388f), Q31(-0.87642273115183f)}, +{Q31( 0.02416275806869f), Q31( 0.27192914288905f)}, {Q31( 0.82068619590515f), Q31(-0.85087787994476f)}, +{Q31( 0.88547373760759f), Q31(-0.89636802901469f)}, {Q31(-0.18173078152226f), Q31(-0.26152145156800f)}, +{Q31( 0.09355476558534f), Q31( 0.54845123045604f)}, {Q31(-0.54668414224090f), Q31( 0.95980774020221f)}, +{Q31( 0.37050990604091f), Q31(-0.59910140383171f)}, {Q31(-0.70373594262891f), Q31( 0.91227665827081f)}, +{Q31(-0.34600785879594f), Q31(-0.99441426144200f)}, {Q31(-0.68774481731008f), Q31(-0.30238837956299f)}, +{Q31(-0.26843291251234f), Q31( 0.83115668004362f)}, {Q31( 0.49072334613242f), Q31(-0.45359708737775f)}, +{Q31( 0.38975993093975f), Q31( 0.95515358099121f)}, {Q31(-0.97757125224150f), Q31( 0.05305894580606f)}, +{Q31(-0.17325552859616f), Q31(-0.92770672250494f)}, {Q31( 0.99948035025744f), Q31( 0.58285545563426f)}, +{Q31(-0.64946246527458f), Q31( 0.68645507104960f)}, {Q31(-0.12016920576437f), Q31(-0.57147322153312f)}, +{Q31(-0.58947456517751f), Q31(-0.34847132454388f)}, {Q31(-0.41815140454465f), Q31( 0.16276422358861f)}, +{Q31( 0.99885650204884f), Q31( 0.11136095490444f)}, {Q31(-0.56649614128386f), Q31(-0.90494866361587f)}, +{Q31( 0.94138021032330f), Q31( 0.35281916733018f)}, {Q31(-0.75725076534641f), Q31( 0.53650549640587f)}, +{Q31( 0.20541973692630f), Q31(-0.94435144369918f)}, {Q31( 0.99980371023351f), Q31( 0.79835913565599f)}, +{Q31( 0.29078277605775f), Q31( 0.35393777921520f)}, {Q31(-0.62858772103030f), Q31( 0.38765693387102f)}, +{Q31( 0.43440904467688f), Q31(-0.98546330463232f)}, {Q31(-0.98298583762390f), Q31( 0.21021524625209f)}, +{Q31( 0.19513029146934f), Q31(-0.94239832251867f)}, {Q31(-0.95476662400101f), Q31( 0.98364554179143f)}, +{Q31( 0.93379635304810f), Q31(-0.70881994583682f)}, {Q31(-0.85235410573336f), Q31(-0.08342347966410f)}, +{Q31(-0.86425093011245f), Q31(-0.45795025029466f)}, {Q31( 0.38879779059045f), Q31( 0.97274429344593f)}, +{Q31( 0.92045124735495f), Q31(-0.62433652524220f)}, {Q31( 0.89162532251878f), Q31( 0.54950955570563f)}, +{Q31(-0.36834336949252f), Q31( 0.96458298020975f)}, {Q31( 0.93891760988045f), Q31(-0.89968353740388f)}, +{Q31( 0.99267657565094f), Q31(-0.03757034316958f)}, {Q31(-0.94063471614176f), Q31( 0.41332338538963f)}, +{Q31( 0.99740224117019f), Q31(-0.16830494996370f)}, {Q31(-0.35899413170555f), Q31(-0.46633226649613f)}, +{Q31( 0.05237237274947f), Q31(-0.25640361602661f)}, {Q31( 0.36703583957424f), Q31(-0.38653265641875f)}, +{Q31( 0.91653180367913f), Q31(-0.30587628726597f)}, {Q31( 0.69000803499316f), Q31( 0.90952171386132f)}, +{Q31(-0.38658751133527f), Q31( 0.99501571208985f)}, {Q31(-0.29250814029851f), Q31( 0.37444994344615f)}, +{Q31(-0.60182204677608f), Q31( 0.86779651036123f)}, {Q31(-0.97418588163217f), Q31( 0.96468523666475f)}, +{Q31( 0.88461574003963f), Q31( 0.57508405276414f)}, {Q31( 0.05198933055162f), Q31( 0.21269661669964f)}, +{Q31(-0.53499621979720f), Q31( 0.97241553731237f)}, {Q31(-0.49429560226497f), Q31( 0.98183865291903f)}, +{Q31(-0.98935142339139f), Q31(-0.40249159006933f)}, {Q31(-0.98081380091130f), Q31(-0.72856895534041f)}, +{Q31(-0.27338148835532f), Q31( 0.99950922447209f)}, {Q31( 0.06310802338302f), Q31(-0.54539587529618f)}, +{Q31(-0.20461677199539f), Q31(-0.14209977628489f)}, {Q31( 0.66223843141647f), Q31( 0.72528579940326f)}, +{Q31(-0.84764345483665f), Q31( 0.02372316801261f)}, {Q31(-0.89039863483811f), Q31( 0.88866581484602f)}, +{Q31( 0.95903308477986f), Q31( 0.76744927173873f)}, {Q31( 0.73504123909879f), Q31(-0.03747203173192f)}, +{Q31(-0.31744434966056f), Q31(-0.36834111883652f)}, {Q31(-0.34110827591623f), Q31( 0.40211222807691f)}, +{Q31( 0.47803883714199f), Q31(-0.39423219786288f)}, {Q31( 0.98299195879514f), Q31( 0.01989791390047f)}, +{Q31(-0.30963073129751f), Q31(-0.18076720599336f)}, {Q31( 0.99992588229018f), Q31(-0.26281872094289f)}, +{Q31(-0.93149731080767f), Q31(-0.98313162570490f)}, {Q31( 0.99923472302773f), Q31(-0.80142993767554f)}, +{Q31(-0.26024169633417f), Q31(-0.75999759855752f)}, {Q31(-0.35712514743563f), Q31( 0.19298963768574f)}, +{Q31(-0.99899084509530f), Q31( 0.74645156992493f)}, {Q31( 0.86557171579452f), Q31( 0.55593866696299f)}, +{Q31( 0.33408042438752f), Q31( 0.86185953874709f)}, {Q31( 0.99010736374716f), Q31( 0.04602397576623f)}, +{Q31(-0.66694269691195f), Q31(-0.91643611810148f)}, {Q31( 0.64016792079480f), Q31( 0.15649530836856f)}, +{Q31( 0.99570534804836f), Q31( 0.45844586038111f)}, {Q31(-0.63431466947340f), Q31( 0.21079116459234f)}, +{Q31(-0.07706847005931f), Q31(-0.89581437101329f)}, {Q31( 0.98590090577724f), Q31( 0.88241721133981f)}, +{Q31( 0.80099335254678f), Q31(-0.36851896710853f)}, {Q31( 0.78368131392666f), Q31( 0.45506999802597f)}, +{Q31( 0.08707806671691f), Q31( 0.80938994918745f)}, {Q31(-0.86811883080712f), Q31( 0.39347308654705f)}, +{Q31(-0.39466529740375f), Q31(-0.66809432114456f)}, {Q31( 0.97875325649683f), Q31(-0.72467840967746f)}, +{Q31(-0.95038560288864f), Q31( 0.89563219587625f)}, {Q31( 0.17005239424212f), Q31( 0.54683053962658f)}, +{Q31(-0.76910792026848f), Q31(-0.96226617549298f)}, {Q31( 0.99743281016846f), Q31( 0.42697157037567f)}, +{Q31( 0.95437383549973f), Q31( 0.97002324109952f)}, {Q31( 0.99578905365569f), Q31(-0.54106826257356f)}, +{Q31( 0.28058259829990f), Q31(-0.85361420634036f)}, {Q31( 0.85256524470573f), Q31(-0.64567607735589f)}, +{Q31(-0.50608540105128f), Q31(-0.65846015480300f)}, {Q31(-0.97210735183243f), Q31(-0.23095213067791f)}, +{Q31( 0.95424048234441f), Q31(-0.99240147091219f)}, {Q31(-0.96926570524023f), Q31( 0.73775654896574f)}, +{Q31( 0.30872163214726f), Q31( 0.41514960556126f)}, {Q31(-0.24523839572639f), Q31( 0.63206633394807f)}, +{Q31(-0.33813265086024f), Q31(-0.38661779441897f)}, {Q31(-0.05826828420146f), Q31(-0.06940774188029f)}, +{Q31(-0.22898461455054f), Q31( 0.97054853316316f)}, {Q31(-0.18509915019881f), Q31( 0.47565762892084f)}, +{Q31(-0.10488238045009f), Q31(-0.87769947402394f)}, {Q31(-0.71886586182037f), Q31( 0.78030982480538f)}, +{Q31( 0.99793873738654f), Q31( 0.90041310491497f)}, {Q31( 0.57563307626120f), Q31(-0.91034337352097f)}, +{Q31( 0.28909646383717f), Q31( 0.96307783970534f)}, {Q31( 0.42188998312520f), Q31( 0.48148651230437f)}, +{Q31( 0.93335049681047f), Q31(-0.43537023883588f)}, {Q31(-0.97087374418267f), Q31( 0.86636445711364f)}, +{Q31( 0.36722871286923f), Q31( 0.65291654172961f)}, {Q31(-0.81093025665696f), Q31( 0.08778370229363f)}, +{Q31(-0.26240603062237f), Q31(-0.92774095379098f)}, {Q31( 0.83996497984604f), Q31( 0.55839849139647f)}, +{Q31(-0.99909615720225f), Q31(-0.96024605713970f)}, {Q31( 0.74649464155061f), Q31( 0.12144893606462f)}, +{Q31(-0.74774595569805f), Q31(-0.26898062008959f)}, {Q31( 0.95781667469567f), Q31(-0.79047927052628f)}, +{Q31( 0.95472308713099f), Q31(-0.08588776019550f)}, {Q31( 0.48708332746299f), Q31( 0.99999041579432f)}, +{Q31( 0.46332038247497f), Q31( 0.10964126185063f)}, {Q31(-0.76497004940162f), Q31( 0.89210929242238f)}, +{Q31( 0.57397389364339f), Q31( 0.35289703373760f)}, {Q31( 0.75374316974495f), Q31( 0.96705214651335f)}, +{Q31(-0.59174397685714f), Q31(-0.89405370422752f)}, {Q31( 0.75087906691890f), Q31(-0.29612672982396f)}, +{Q31(-0.98607857336230f), Q31( 0.25034911730023f)}, {Q31(-0.40761056640505f), Q31(-0.90045573444695f)}, +{Q31( 0.66929266740477f), Q31( 0.98629493401748f)}, {Q31(-0.97463695257310f), Q31(-0.00190223301301f)}, +{Q31( 0.90145509409859f), Q31( 0.99781390365446f)}, {Q31(-0.87259289048043f), Q31( 0.99233587353666f)}, +{Q31(-0.91529461447692f), Q31(-0.15698707534206f)}, {Q31(-0.03305738840705f), Q31(-0.37205262859764f)}, +{Q31( 0.07223051368337f), Q31(-0.88805001733626f)}, {Q31( 0.99498012188353f), Q31( 0.97094358113387f)}, +{Q31(-0.74904939500519f), Q31( 0.99985483641521f)}, {Q31( 0.04585228574211f), Q31( 0.99812337444082f)}, +{Q31(-0.89054954257993f), Q31(-0.31791913188064f)}, {Q31(-0.83782144651251f), Q31( 0.97637632547466f)}, +{Q31( 0.33454804933804f), Q31(-0.86231516800408f)}, {Q31(-0.99707579362824f), Q31( 0.93237990079441f)}, +{Q31(-0.22827527843994f), Q31( 0.18874759397997f)}, {Q31( 0.67248046289143f), Q31(-0.03646211390569f)}, +{Q31(-0.05146538187944f), Q31(-0.92599700120679f)}, {Q31( 0.99947295749905f), Q31( 0.93625229707912f)}, +{Q31( 0.66951124390363f), Q31( 0.98905825623893f)}, {Q31(-0.99602956559179f), Q31(-0.44654715757688f)}, +{Q31( 0.82104905483590f), Q31( 0.99540741724928f)}, {Q31( 0.99186510988782f), Q31( 0.72023001312947f)}, +{Q31(-0.65284592392918f), Q31( 0.52186723253637f)}, {Q31( 0.93885443798188f), Q31(-0.74895312615259f)}, +{Q31( 0.96735248738388f), Q31( 0.90891816978629f)}, {Q31(-0.22225968841114f), Q31( 0.57124029781228f)}, +{Q31(-0.44132783753414f), Q31(-0.92688840659280f)}, {Q31(-0.85694974219574f), Q31( 0.88844532719844f)}, +{Q31( 0.91783042091762f), Q31(-0.46356892383970f)}, {Q31( 0.72556974415690f), Q31(-0.99899555770747f)}, +{Q31(-0.99711581834508f), Q31( 0.58211560180426f)}, {Q31( 0.77638976371966f), Q31( 0.94321834873819f)}, +{Q31( 0.07717324253925f), Q31( 0.58638399856595f)}, {Q31(-0.56049829194163f), Q31( 0.82522301569036f)}, +{Q31( 0.98398893639988f), Q31( 0.39467440420569f)}, {Q31( 0.47546946844938f), Q31( 0.68613044836811f)}, +{Q31( 0.65675089314631f), Q31( 0.18331637134880f)}, {Q31( 0.03273375457980f), Q31(-0.74933109564108f)}, +{Q31(-0.38684144784738f), Q31( 0.51337349030406f)}, {Q31(-0.97346267944545f), Q31(-0.96549364384098f)}, +{Q31(-0.53282156061942f), Q31(-0.91423265091354f)}, {Q31( 0.99817310731176f), Q31( 0.61133572482148f)}, +{Q31(-0.50254500772635f), Q31(-0.88829338134294f)}, {Q31( 0.01995873238855f), Q31( 0.85223515096765f)}, +{Q31( 0.99930381973804f), Q31( 0.94578896296649f)}, {Q31( 0.82907767600783f), Q31(-0.06323442598128f)}, +{Q31(-0.58660709669728f), Q31( 0.96840773806582f)}, {Q31(-0.17573736667267f), Q31(-0.48166920859485f)}, +{Q31( 0.83434292401346f), Q31(-0.13023450646997f)}, {Q31( 0.05946491307025f), Q31( 0.20511047074866f)}, +{Q31( 0.81505484574602f), Q31(-0.94685947861369f)}, {Q31(-0.44976380954860f), Q31( 0.40894572671545f)}, +{Q31(-0.89746474625671f), Q31( 0.99846578838537f)}, {Q31( 0.39677256130792f), Q31(-0.74854668609359f)}, +{Q31(-0.07588948563079f), Q31( 0.74096214084170f)}, {Q31( 0.76343198951445f), Q31( 0.41746629422634f)}, +{Q31(-0.74490104699626f), Q31( 0.94725911744610f)}, {Q31( 0.64880119792759f), Q31( 0.41336660830571f)}, +{Q31( 0.62319537462542f), Q31(-0.93098313552599f)}, {Q31( 0.42215817594807f), Q31(-0.07712787385208f)}, +{Q31( 0.02704554141885f), Q31(-0.05417518053666f)}, {Q31( 0.80001773566818f), Q31( 0.91542195141039f)}, +{Q31(-0.79351832348816f), Q31(-0.36208897989136f)}, {Q31( 0.63872359151636f), Q31( 0.08128252493444f)}, +{Q31( 0.52890520960295f), Q31( 0.60048872455592f)}, {Q31( 0.74238552914587f), Q31( 0.04491915291044f)}, +{Q31( 0.99096131449250f), Q31(-0.19451182854402f)}, {Q31(-0.80412329643109f), Q31(-0.88513818199457f)}, +{Q31(-0.64612616129736f), Q31( 0.72198674804544f)}, {Q31( 0.11657770663191f), Q31(-0.83662833815041f)}, +{Q31(-0.95053182488101f), Q31(-0.96939905138082f)}, {Q31(-0.62228872928622f), Q31( 0.82767262846661f)}, +{Q31( 0.03004475787316f), Q31(-0.99738896333384f)}, {Q31(-0.97987214341034f), Q31( 0.36526129686425f)}, +{Q31(-0.99986980746200f), Q31(-0.36021610299715f)}, {Q31( 0.89110648599879f), Q31(-0.97894250343044f)}, +{Q31( 0.10407960510582f), Q31( 0.77357793811619f)}, {Q31( 0.95964737821728f), Q31(-0.35435818285502f)}, +{Q31( 0.50843233159162f), Q31( 0.96107691266205f)}, {Q31( 0.17006334670615f), Q31(-0.76854025314829f)}, +{Q31( 0.25872675063360f), Q31( 0.99893303933816f)}, {Q31(-0.01115998681937f), Q31( 0.98496019742444f)}, +{Q31(-0.79598702973261f), Q31( 0.97138411318894f)}, {Q31(-0.99264708948101f), Q31(-0.99542822402536f)}, +{Q31(-0.99829663752818f), Q31( 0.01877138824311f)}, {Q31(-0.70801016548184f), Q31( 0.33680685948117f)}, +{Q31(-0.70467057786826f), Q31( 0.93272777501857f)}, {Q31( 0.99846021905254f), Q31(-0.98725746254433f)}, +{Q31(-0.63364968534650f), Q31(-0.16473594423746f)}, {Q31(-0.16258217500792f), Q31(-0.95939125400802f)}, +{Q31(-0.43645594360633f), Q31(-0.94805030113284f)}, {Q31(-0.99848471702976f), Q31( 0.96245166923809f)}, +{Q31(-0.16796458968998f), Q31(-0.98987511890470f)}, {Q31(-0.87979225745213f), Q31(-0.71725725041680f)}, +{Q31( 0.44183099021786f), Q31(-0.93568974498761f)}, {Q31( 0.93310180125532f), Q31(-0.99913308068246f)}, +{Q31(-0.93941931782002f), Q31(-0.56409379640356f)}, {Q31(-0.88590003188677f), Q31( 0.47624600491382f)}, +{Q31( 0.99971463703691f), Q31(-0.83889954253462f)}, {Q31(-0.75376385639978f), Q31( 0.00814643438625f)}, +{Q31( 0.93887685615875f), Q31(-0.11284528204636f)}, {Q31( 0.85126435782309f), Q31( 0.52349251543547f)}, +{Q31( 0.39701421446381f), Q31( 0.81779634174316f)}, {Q31(-0.37024464187437f), Q31(-0.87071656222959f)}, +{Q31(-0.36024828242896f), Q31( 0.34655735648287f)}, {Q31(-0.93388812549209f), Q31(-0.84476541096429f)}, +{Q31(-0.65298804552119f), Q31(-0.18439575450921f)}, {Q31( 0.11960319006843f), Q31( 0.99899346780168f)}, +{Q31( 0.94292565553160f), Q31( 0.83163906518293f)}, {Q31( 0.75081145286948f), Q31(-0.35533223142265f)}, +{Q31( 0.56721979748394f), Q31(-0.24076836414499f)}, {Q31( 0.46857766746029f), Q31(-0.30140233457198f)}, +{Q31( 0.97312313923635f), Q31(-0.99548191630031f)}, {Q31(-0.38299976567017f), Q31( 0.98516909715427f)}, +{Q31( 0.41025800019463f), Q31( 0.02116736935734f)}, {Q31( 0.09638062008048f), Q31( 0.04411984381457f)}, +{Q31(-0.85283249275397f), Q31( 0.91475563922421f)}, {Q31( 0.88866808958124f), Q31(-0.99735267083226f)}, +{Q31(-0.48202429536989f), Q31(-0.96805608884164f)}, {Q31( 0.27572582416567f), Q31( 0.58634753335832f)}, +{Q31(-0.65889129659168f), Q31( 0.58835634138583f)}, {Q31( 0.98838086953732f), Q31( 0.99994349600236f)}, +{Q31(-0.20651349620689f), Q31( 0.54593044066355f)}, {Q31(-0.62126416356920f), Q31(-0.59893681700392f)}, +{Q31( 0.20320105410437f), Q31(-0.86879180355289f)}, {Q31(-0.97790548600584f), Q31( 0.96290806999242f)}, +{Q31( 0.11112534735126f), Q31( 0.21484763313301f)}, {Q31(-0.41368337314182f), Q31( 0.28216837680365f)}, +{Q31( 0.24133038992960f), Q31( 0.51294362630238f)}, {Q31(-0.66393410674885f), Q31(-0.08249679629081f)}, +{Q31(-0.53697829178752f), Q31(-0.97649903936228f)}, {Q31(-0.97224737889348f), Q31( 0.22081333579837f)}, +{Q31( 0.87392477144549f), Q31(-0.12796173740361f)}, {Q31( 0.19050361015753f), Q31( 0.01602615387195f)}, +{Q31(-0.46353441212724f), Q31(-0.95249041539006f)}, {Q31(-0.07064096339021f), Q31(-0.94479803205886f)}, +{Q31(-0.92444085484466f), Q31(-0.10457590187436f)}, {Q31(-0.83822593578728f), Q31(-0.01695043208885f)}, +{Q31( 0.75214681811150f), Q31(-0.99955681042665f)}, {Q31(-0.42102998829339f), Q31( 0.99720941999394f)}, +{Q31(-0.72094786237696f), Q31(-0.35008961934255f)}, {Q31( 0.78843311019251f), Q31( 0.52851398958271f)}, +{Q31( 0.97394027897442f), Q31(-0.26695944086561f)}, {Q31( 0.99206463477946f), Q31(-0.57010120849429f)}, +{Q31( 0.76789609461795f), Q31(-0.76519356730966f)}, {Q31(-0.82002421836409f), Q31(-0.73530179553767f)}, +{Q31( 0.81924990025724f), Q31( 0.99698425250579f)}, {Q31(-0.26719850873357f), Q31( 0.68903369776193f)}, +{Q31(-0.43311260380975f), Q31( 0.85321815947490f)}, {Q31( 0.99194979673836f), Q31( 0.91876249766422f)}, +{Q31(-0.80692001248487f), Q31(-0.32627540663214f)}, {Q31( 0.43080003649976f), Q31(-0.21919095636638f)}, +{Q31( 0.67709491937357f), Q31(-0.95478075822906f)}, {Q31( 0.56151770568316f), Q31(-0.70693811747778f)}, +{Q31( 0.10831862810749f), Q31(-0.08628837174592f)}, {Q31( 0.91229417540436f), Q31(-0.65987351408410f)}, +{Q31(-0.48972893932274f), Q31( 0.56289246362686f)}, {Q31(-0.89033658689697f), Q31(-0.71656563987082f)}, +{Q31( 0.65269447475094f), Q31( 0.65916004833932f)}, {Q31( 0.67439478141121f), Q31(-0.81684380846796f)}, +{Q31(-0.47770832416973f), Q31(-0.16789556203025f)}, {Q31(-0.99715979260878f), Q31(-0.93565784007648f)}, +{Q31(-0.90889593602546f), Q31( 0.62034397054380f)}, {Q31(-0.06618622548177f), Q31(-0.23812217221359f)}, +{Q31( 0.99430266919728f), Q31( 0.18812555317553f)}, {Q31( 0.97686402381843f), Q31(-0.28664534366620f)}, +{Q31( 0.94813650221268f), Q31(-0.97506640027128f)}, {Q31(-0.95434497492853f), Q31(-0.79607978501983f)}, +{Q31(-0.49104783137150f), Q31( 0.32895214359663f)}, {Q31( 0.99881175120751f), Q31( 0.88993983831354f)}, +{Q31( 0.50449166760303f), Q31(-0.85995072408434f)}, {Q31( 0.47162891065108f), Q31(-0.18680204049569f)}, +{Q31(-0.62081581361840f), Q31( 0.75000676218956f)}, {Q31(-0.43867015250812f), Q31( 0.99998069244322f)}, +{Q31( 0.98630563232075f), Q31(-0.53578899600662f)}, {Q31(-0.61510362277374f), Q31(-0.89515019899997f)}, +{Q31(-0.03841517601843f), Q31(-0.69888815681179f)}, {Q31(-0.30102157304644f), Q31(-0.07667808922205f)}, +{Q31( 0.41881284182683f), Q31( 0.02188098922282f)}, {Q31(-0.86135454941237f), Q31( 0.98947480909359f)}, +{Q31( 0.67226861393788f), Q31(-0.13494389011014f)}, {Q31(-0.70737398842068f), Q31(-0.76547349325992f)}, +{Q31( 0.94044946687963f), Q31( 0.09026201157416f)}, {Q31(-0.82386352534327f), Q31( 0.08924768823676f)}, +{Q31(-0.32070666698656f), Q31( 0.50143421908753f)}, {Q31( 0.57593163224487f), Q31(-0.98966422921509f)}, +{Q31(-0.36326018419965f), Q31( 0.07440243123228f)}, {Q31( 0.99979044674350f), Q31(-0.14130287347405f)}, +{Q31(-0.92366023326932f), Q31(-0.97979298068180f)}, {Q31(-0.44607178518598f), Q31(-0.54233252016394f)}, +{Q31( 0.44226800932956f), Q31( 0.71326756742752f)}, {Q31( 0.03671907158312f), Q31( 0.63606389366675f)}, +{Q31( 0.52175424682195f), Q31(-0.85396826735705f)}, {Q31(-0.94701139690956f), Q31(-0.01826348194255f)}, +{Q31(-0.98759606946049f), Q31( 0.82288714303073f)}, {Q31( 0.87434794743625f), Q31( 0.89399495655433f)}, +{Q31(-0.93412041758744f), Q31( 0.41374052024363f)}, {Q31( 0.96063943315511f), Q31( 0.93116709541280f)}, +{Q31( 0.97534253457837f), Q31( 0.86150930812689f)}, {Q31( 0.99642466504163f), Q31( 0.70190043427512f)}, +{Q31(-0.94705089665984f), Q31(-0.29580042814306f)}, {Q31( 0.91599807087376f), Q31(-0.98147830385781f)}, +// Start of duplicated table +{Q31(-0.99948153278296f), Q31(-0.59483417516607f)}, {Q31( 0.97113454393991f), Q31(-0.67528515225647f)}, +{Q31( 0.14130051758487f), Q31(-0.95090983575689f)}, {Q31(-0.47005496701697f), Q31(-0.37340549728647f)}, +{Q31( 0.80705063769351f), Q31( 0.29653668284408f)}, {Q31(-0.38981478896926f), Q31( 0.89572605717087f)}, +{Q31(-0.01053049862020f), Q31(-0.66959058036166f)}, {Q31(-0.91266367957293f), Q31(-0.11522938140034f)}, +#if ARCH_RISCV +{Q31( 0.54840422910309f), Q31( 0.75221367176302f)}, {Q31( 0.40009252867955f), Q31(-0.98929400334421f)}, +{Q31(-0.99867974711855f), Q31(-0.88147068645358f)}, {Q31(-0.95531076805040f), Q31( 0.90908757154593f)}, +{Q31(-0.45725933317144f), Q31(-0.56716323646760f)}, {Q31(-0.72929675029275f), Q31(-0.98008272727324f)}, +{Q31( 0.75622801399036f), Q31( 0.20950329995549f)}, {Q31( 0.07069442601050f), Q31(-0.78247898470706f)}, +#endif +}; diff --git a/libavcodec/scpr.c b/libavcodec/scpr.c index 7630adb3e09..b096965de54 100644 --- a/libavcodec/scpr.c +++ b/libavcodec/scpr.c @@ -516,18 +516,18 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, s->version = 1; s->get_freq = get_freq0; s->decode = decode0; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; ret = decompress_i(avctx, (uint32_t *)s->current_frame->data[0], s->current_frame->linesize[0] / 4); } else if (type == 18) { s->version = 2; s->get_freq = get_freq; s->decode = decode; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; ret = decompress_i(avctx, (uint32_t *)s->current_frame->data[0], s->current_frame->linesize[0] / 4); } else if (type == 34) { - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; s->version = 3; ret = decompress_i3(avctx, (uint32_t *)s->current_frame->data[0], s->current_frame->linesize[0] / 4); @@ -538,7 +538,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, if (bytestream2_get_bytes_left(gb) < 3) return AVERROR_INVALIDDATA; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; bytestream2_skip(gb, 1); if (avctx->bits_per_coded_sample == 16) { uint16_t value = bytestream2_get_le16(gb); @@ -557,7 +557,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, dst += s->current_frame->linesize[0] / 4; } } else if (type == 0 || type == 1) { - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; if (s->version == 1 || s->version == 2) ret = decompress_p(avctx, (uint32_t *)s->current_frame->data[0], @@ -612,7 +612,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } } - frame->pict_type = frame->key_frame ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + frame->pict_type = (frame->flags & AV_FRAME_FLAG_KEY) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; FFSWAP(AVFrame *, s->current_frame, s->last_frame); diff --git a/libavcodec/scpr3.c b/libavcodec/scpr3.c index 5271717ac79..e91c1983087 100644 --- a/libavcodec/scpr3.c +++ b/libavcodec/scpr3.c @@ -465,6 +465,8 @@ static int decode_adaptive6(PixelModel3 *m, uint32_t code, uint32_t *value, return 0; grow_dec(m); c = add_dec(m, q, g, f); + if (c < 0) + return AVERROR_INVALIDDATA; } incr_cntdec(m, c); @@ -868,11 +870,11 @@ static int decode_unit3(SCPRContext *s, PixelModel3 *m, uint32_t code, uint32_t sync_code3(gb, rc); break; case 6: - if (!decode_adaptive6(m, code, value, &a, &b)) { + ret = decode_adaptive6(m, code, value, &a, &b); + if (!ret) ret = update_model6_to_7(m); - if (ret < 0) - return AVERROR_INVALIDDATA; - } + if (ret < 0) + return ret; decode3(gb, rc, a, b); sync_code3(gb, rc); break; diff --git a/libavcodec/screenpresso.c b/libavcodec/screenpresso.c index 0d9e485043a..b27154991c9 100644 --- a/libavcodec/screenpresso.c +++ b/libavcodec/screenpresso.c @@ -173,7 +173,7 @@ static int screenpresso_decode_frame(AVCodecContext *avctx, AVFrame *frame, /* Usual properties */ if (keyframe) { frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; } else { frame->pict_type = AV_PICTURE_TYPE_P; } diff --git a/libavcodec/sga.c b/libavcodec/sga.c index d3f49242984..f474ffba9aa 100644 --- a/libavcodec/sga.c +++ b/libavcodec/sga.c @@ -72,7 +72,7 @@ static int decode_palette(GetByteContext *gb, uint32_t *pal) return AVERROR_INVALIDDATA; memset(pal, 0, 16 * sizeof(*pal)); - init_get_bits8(&gbit, gb->buffer, 18); + (void)init_get_bits8(&gbit, gb->buffer, 18); for (int RGBIndex = 0; RGBIndex < 3; RGBIndex++) { for (int index = 0; index < 16; index++) { @@ -127,19 +127,18 @@ static int decode_index_palmap(SGAVideoContext *s, AVFrame *frame) static int decode_index_tilemap(SGAVideoContext *s, AVFrame *frame) { - GetByteContext *gb = &s->gb; - GetBitContext pm; + GetByteContext *gb = &s->gb, gb2; bytestream2_seek(gb, s->tilemapdata_offset, SEEK_SET); if (bytestream2_get_bytes_left(gb) < s->tilemapdata_size) return AVERROR_INVALIDDATA; - init_get_bits8(&pm, gb->buffer, s->tilemapdata_size); + gb2 = *gb; for (int y = 0; y < s->tiles_h; y++) { for (int x = 0; x < s->tiles_w; x++) { uint8_t tile[64]; - int tilemap = get_bits(&pm, 16); + int tilemap = bytestream2_get_be16u(&gb2); int flip_x = (tilemap >> 11) & 1; int flip_y = (tilemap >> 12) & 1; int tindex = av_clip((tilemap & 511) - 1, 0, s->nb_tiles - 1); @@ -497,9 +496,13 @@ static int sga_decode_frame(AVCodecContext *avctx, AVFrame *frame, } memcpy(frame->data[1], s->pal, AVPALETTE_SIZE); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; diff --git a/libavcodec/sgidec.c b/libavcodec/sgidec.c index 92083f23de9..04a347c51e1 100644 --- a/libavcodec/sgidec.c +++ b/libavcodec/sgidec.c @@ -249,7 +249,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, break; } p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; /* Skip header. */ bytestream2_seek(&g, SGI_HEADER_SIZE, SEEK_SET); diff --git a/libavcodec/sgirledec.c b/libavcodec/sgirledec.c index 9e3a220ad41..18bf8081fc6 100644 --- a/libavcodec/sgirledec.c +++ b/libavcodec/sgirledec.c @@ -124,7 +124,7 @@ static int sgirle_decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; diff --git a/libavcodec/sheervideo.c b/libavcodec/sheervideo.c index eee60147422..660e2661a48 100644 --- a/libavcodec/sheervideo.c +++ b/libavcodec/sheervideo.c @@ -1796,8 +1796,8 @@ static av_cold int build_vlc(VLC *vlc, const SheerTable *table) lens[count] = len; } - ff_free_vlc(vlc); - return ff_init_vlc_from_lengths(vlc, SHEER_VLC_BITS, count, + ff_vlc_free(vlc); + return ff_vlc_init_from_lengths(vlc, SHEER_VLC_BITS, count, lens, sizeof(*lens), NULL, 0, 0, 0, 0, NULL); } @@ -1973,7 +1973,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, } p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; if ((ret = ff_thread_get_buffer(avctx, p, 0)) < 0) return ret; @@ -1992,8 +1992,8 @@ static av_cold int decode_end(AVCodecContext *avctx) { SheerVideoContext *s = avctx->priv_data; - ff_free_vlc(&s->vlc[0]); - ff_free_vlc(&s->vlc[1]); + ff_vlc_free(&s->vlc[0]); + ff_vlc_free(&s->vlc[1]); return 0; } diff --git a/libavcodec/shorten.c b/libavcodec/shorten.c index 1b2abd76b14..0ad95bf97ec 100644 --- a/libavcodec/shorten.c +++ b/libavcodec/shorten.c @@ -280,7 +280,7 @@ static int decode_wave_header(AVCodecContext *avctx, const uint8_t *header, int header_size) { int len, bps; - short wave_format; + uint16_t wave_format; GetByteContext gb; bytestream2_init(&gb, header, header_size); @@ -814,8 +814,10 @@ const FFCodec ff_shorten_decoder = { FF_CODEC_DECODE_CB(shorten_decode_frame), .p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | AV_CODEC_CAP_DELAY | - AV_CODEC_CAP_DR1 | - AV_CODEC_CAP_SUBFRAMES , +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_NONE }, diff --git a/libavcodec/sipr.c b/libavcodec/sipr.c index 692b59b3e89..3ddc579f097 100644 --- a/libavcodec/sipr.c +++ b/libavcodec/sipr.c @@ -532,7 +532,6 @@ static int sipr_decode_frame(AVCodecContext *avctx, AVFrame *frame, int subframe_size = ctx->mode == MODE_16k ? L_SUBFR_16k : SUBFR_SIZE; int i, ret; - ctx->avctx = avctx; if (avpkt->size < (mode_par->bits_per_frame >> 3)) { av_log(avctx, AV_LOG_ERROR, "Error processing packet: packet size (%d) too small\n", diff --git a/libavcodec/sipr.h b/libavcodec/sipr.h index e1ef35d6587..e7073987ed2 100644 --- a/libavcodec/sipr.h +++ b/libavcodec/sipr.h @@ -24,7 +24,6 @@ #ifndef AVCODEC_SIPR_H #define AVCODEC_SIPR_H -#include "avcodec.h" #include "acelp_pitch_delay.h" #include "libavutil/mem_internal.h" @@ -63,8 +62,6 @@ typedef struct SiprParameters { } SiprParameters; typedef struct SiprContext { - AVCodecContext *avctx; - SiprMode mode; float past_pitch_gain; diff --git a/libavcodec/smacker.c b/libavcodec/smacker.c index ecc27e9b672..ffa0820f52f 100644 --- a/libavcodec/smacker.c +++ b/libavcodec/smacker.c @@ -107,16 +107,16 @@ enum SmkBlockTypes { * Can read SMKTREE_DECODE_MAX_RECURSION before the first check; * does not overread gb on success. */ -static int smacker_decode_tree(GetBitContext *gb, HuffContext *hc, int length) +static int smacker_decode_tree(AVCodecContext *avctx, GetBitContext *gb, HuffContext *hc, int length) { if (length > SMKTREE_DECODE_MAX_RECURSION || length > 3 * SMKTREE_BITS) { - av_log(NULL, AV_LOG_ERROR, "Maximum tree recursion level exceeded.\n"); + av_log(avctx, AV_LOG_ERROR, "Maximum tree recursion level exceeded.\n"); return AVERROR_INVALIDDATA; } if(!get_bits1(gb)){ //Leaf if (hc->current >= 256) { - av_log(NULL, AV_LOG_ERROR, "Tree size exceeded!\n"); + av_log(avctx, AV_LOG_ERROR, "Tree size exceeded!\n"); return AVERROR_INVALIDDATA; } if (get_bits_left(gb) < 8) @@ -126,10 +126,10 @@ static int smacker_decode_tree(GetBitContext *gb, HuffContext *hc, int length) } else { //Node int r; length++; - r = smacker_decode_tree(gb, hc, length); + r = smacker_decode_tree(avctx, gb, hc, length); if(r) return r; - return smacker_decode_tree(gb, hc, length); + return smacker_decode_tree(avctx, gb, hc, length); } } @@ -138,7 +138,7 @@ static int smacker_decode_tree(GetBitContext *gb, HuffContext *hc, int length) * * Checks before the first read, can overread by 6 * SMKTREE_BITS on success. */ -static int smacker_decode_bigtree(GetBitContext *gb, DBCtx *ctx, int length) +static int smacker_decode_bigtree(AVCodecContext *avctx, GetBitContext *gb, DBCtx *ctx, int length) { // Larger length can cause segmentation faults due to too deep recursion. if (length > SMKTREE_DECODE_BIG_MAX_RECURSION) { @@ -176,12 +176,12 @@ static int smacker_decode_bigtree(GetBitContext *gb, DBCtx *ctx, int length) int r = 0, r_new, t; t = ctx->current++; - r = smacker_decode_bigtree(gb, ctx, length + 1); + r = smacker_decode_bigtree(avctx, gb, ctx, length + 1); if(r < 0) return r; ctx->values[t] = SMK_NODE | r; r++; - r_new = smacker_decode_bigtree(gb, ctx, length + 1); + r_new = smacker_decode_bigtree(avctx, gb, ctx, length + 1); if (r_new < 0) return r_new; return r + r_new; @@ -215,15 +215,15 @@ static int smacker_decode_header_tree(SmackVContext *smk, GetBitContext *gb, int i ? "high" : "low"); continue; } - err = smacker_decode_tree(gb, &h, 0); + err = smacker_decode_tree(smk->avctx, gb, &h, 0); if (err < 0) goto error; skip_bits1(gb); if (h.current > 1) { - err = ff_init_vlc_from_lengths(&vlc[i], SMKTREE_BITS, h.current, + err = ff_vlc_init_from_lengths(&vlc[i], SMKTREE_BITS, h.current, &h.entries[0].length, sizeof(*h.entries), &h.entries[0].value, sizeof(*h.entries), 1, - 0, INIT_VLC_OUTPUT_LE, smk->avctx); + 0, VLC_INIT_OUTPUT_LE, smk->avctx); if (err < 0) { av_log(smk->avctx, AV_LOG_ERROR, "Cannot build VLC table\n"); goto error; @@ -253,7 +253,7 @@ static int smacker_decode_header_tree(SmackVContext *smk, GetBitContext *gb, int } *recodes = ctx.values; - err = smacker_decode_bigtree(gb, &ctx, 0); + err = smacker_decode_bigtree(smk->avctx, gb, &ctx, 0); if (err < 0) goto error; skip_bits1(gb); @@ -264,7 +264,7 @@ static int smacker_decode_header_tree(SmackVContext *smk, GetBitContext *gb, int err = 0; error: for (int i = 0; i < 2; i++) { - ff_free_vlc(&vlc[i]); + ff_vlc_free(&vlc[i]); } return err; @@ -392,12 +392,18 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, pal = (uint32_t*)smk->pic->data[1]; bytestream2_init(&gb2, avpkt->data, avpkt->size); flags = bytestream2_get_byteu(&gb2); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS smk->pic->palette_has_changed = flags & 1; - smk->pic->key_frame = !!(flags & 2); - if (smk->pic->key_frame) +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (flags & 2) { + smk->pic->flags |= AV_FRAME_FLAG_KEY; smk->pic->pict_type = AV_PICTURE_TYPE_I; - else + } else { + smk->pic->flags &= ~AV_FRAME_FLAG_KEY; smk->pic->pict_type = AV_PICTURE_TYPE_P; + } for(i = 0; i < 256; i++) *pal++ = 0xFFU << 24 | bytestream2_get_be24u(&gb2); @@ -649,14 +655,14 @@ static int smka_decode_frame(AVCodecContext *avctx, AVFrame *frame, HuffContext h; h.current = 0; skip_bits1(&gb); - if ((ret = smacker_decode_tree(&gb, &h, 0)) < 0) + if ((ret = smacker_decode_tree(avctx, &gb, &h, 0)) < 0) goto error; skip_bits1(&gb); if (h.current > 1) { - ret = ff_init_vlc_from_lengths(&vlc[i], SMKTREE_BITS, h.current, + ret = ff_vlc_init_from_lengths(&vlc[i], SMKTREE_BITS, h.current, &h.entries[0].length, sizeof(*h.entries), &h.entries[0].value, sizeof(*h.entries), 1, - 0, INIT_VLC_OUTPUT_LE, avctx); + 0, VLC_INIT_OUTPUT_LE, avctx); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "Cannot build VLC table\n"); goto error; @@ -734,7 +740,7 @@ static int smka_decode_frame(AVCodecContext *avctx, AVFrame *frame, error: for(i = 0; i < 4; i++) { - ff_free_vlc(&vlc[i]); + ff_vlc_free(&vlc[i]); } return ret; diff --git a/libavcodec/smc.c b/libavcodec/smc.c index 2b10e74386f..3e8a89ced13 100644 --- a/libavcodec/smc.c +++ b/libavcodec/smc.c @@ -437,7 +437,14 @@ static int smc_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0) return ret; - s->frame->palette_has_changed = ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS + s->frame->palette_has_changed = +#endif + ff_copy_palette(s->pal, avpkt, avctx); +#if FF_API_PALETTE_HAS_CHANGED +FF_ENABLE_DEPRECATION_WARNINGS +#endif bytestream2_init(&gb, buf, buf_size); ret = smc_decode_stream(s, &gb); diff --git a/libavcodec/smcenc.c b/libavcodec/smcenc.c index 40b53c40eef..789aef4f770 100644 --- a/libavcodec/smcenc.c +++ b/libavcodec/smcenc.c @@ -566,8 +566,7 @@ static int smc_encode_frame(AVCodecContext *avctx, AVPacket *pkt, // write chunk length AV_WB24(pkt->data + 1, pkt->size); - av_frame_unref(s->prev_frame); - ret = av_frame_ref(s->prev_frame, frame); + ret = av_frame_replace(s->prev_frame, frame); if (ret < 0) { av_log(avctx, AV_LOG_ERROR, "cannot add reference\n"); return ret; @@ -583,7 +582,7 @@ static int smc_encode_frame(AVCodecContext *avctx, AVPacket *pkt, static int smc_encode_end(AVCodecContext *avctx) { - SMCContext *s = (SMCContext *)avctx->priv_data; + SMCContext *s = avctx->priv_data; av_frame_free(&s->prev_frame); diff --git a/libavcodec/snow.c b/libavcodec/snow.c index b6c8d5e2568..1b0fc6dc7df 100644 --- a/libavcodec/snow.c +++ b/libavcodec/snow.c @@ -21,8 +21,6 @@ #include "libavutil/log.h" #include "libavutil/thread.h" #include "avcodec.h" -#include "decode.h" -#include "encode.h" #include "snow_dwt.h" #include "snow.h" #include "snowdata.h" @@ -61,36 +59,6 @@ void ff_snow_inner_add_yblock(const uint8_t *obmc, const int obmc_stride, uint8_ } } -int ff_snow_get_buffer(SnowContext *s, AVFrame *frame) -{ - int ret, i; - int edges_needed = av_codec_is_encoder(s->avctx->codec); - - frame->width = s->avctx->width ; - frame->height = s->avctx->height; - if (edges_needed) { - frame->width += 2 * EDGE_WIDTH; - frame->height += 2 * EDGE_WIDTH; - - ret = ff_encode_alloc_frame(s->avctx, frame); - } else - ret = ff_get_buffer(s->avctx, frame, AV_GET_BUFFER_FLAG_REF); - if (ret < 0) - return ret; - if (edges_needed) { - for (i = 0; frame->data[i]; i++) { - int offset = (EDGE_WIDTH >> (i ? s->chroma_v_shift : 0)) * - frame->linesize[i] + - (EDGE_WIDTH >> (i ? s->chroma_h_shift : 0)); - frame->data[i] += offset; - } - frame->width = s->avctx->width; - frame->height = s->avctx->height; - } - - return 0; -} - void ff_snow_reset_contexts(SnowContext *s){ //FIXME better initial contexts int plane_index, level, orientation; @@ -433,36 +401,10 @@ av_cold int ff_snow_common_init(AVCodecContext *avctx){ s->max_ref_frames=1; //just make sure it's not an invalid value in case of no initial keyframe s->spatial_decomposition_count = 1; - ff_hpeldsp_init(&s->hdsp, avctx->flags); ff_videodsp_init(&s->vdsp, 8); ff_dwt_init(&s->dwt); ff_h264qpel_init(&s->h264qpel, 8); -#define mcf(dx,dy)\ - s->qdsp.put_qpel_pixels_tab [0][dy+dx/4]=\ - s->qdsp.put_no_rnd_qpel_pixels_tab[0][dy+dx/4]=\ - s->h264qpel.put_h264_qpel_pixels_tab[0][dy+dx/4];\ - s->qdsp.put_qpel_pixels_tab [1][dy+dx/4]=\ - s->qdsp.put_no_rnd_qpel_pixels_tab[1][dy+dx/4]=\ - s->h264qpel.put_h264_qpel_pixels_tab[1][dy+dx/4]; - - mcf( 0, 0) - mcf( 4, 0) - mcf( 8, 0) - mcf(12, 0) - mcf( 0, 4) - mcf( 4, 4) - mcf( 8, 4) - mcf(12, 4) - mcf( 0, 8) - mcf( 4, 8) - mcf( 8, 8) - mcf(12, 8) - mcf( 0,12) - mcf( 4,12) - mcf( 8,12) - mcf(12,12) - #define mcfh(dx,dy)\ s->hdsp.put_pixels_tab [0][dy/4+dx/8]=\ s->hdsp.put_no_rnd_pixels_tab[0][dy/4+dx/8]=\ @@ -485,7 +427,7 @@ av_cold int ff_snow_common_init(AVCodecContext *avctx){ !FF_ALLOCZ_TYPED_ARRAY(s->spatial_dwt_buffer, width * height) || //FIXME this does not belong here !FF_ALLOCZ_TYPED_ARRAY(s->temp_dwt_buffer, width) || !FF_ALLOCZ_TYPED_ARRAY(s->temp_idwt_buffer, width) || - !FF_ALLOCZ_TYPED_ARRAY(s->run_buffer, ((width + 1) >> 1) * ((height + 1) >> 1))) + !FF_ALLOCZ_TYPED_ARRAY(s->run_buffer, ((width + 1) >> 1) * ((height + 1) >> 1) + 1)) return AVERROR(ENOMEM); for(i=0; ipriv_data; int plane_index, level, orientation; - int ret, emu_buf_size; if(!s->scratchbuf) { - if (av_codec_is_decoder(avctx->codec)) { - if ((ret = ff_get_buffer(s->avctx, s->mconly_picture, - AV_GET_BUFFER_FLAG_REF)) < 0) - return ret; - } - + int emu_buf_size; emu_buf_size = FFMAX(s->mconly_picture->linesize[0], 2*avctx->width+256) * (2 * MB_SIZE + HTAPS_MAX - 1); if (!FF_ALLOCZ_TYPED_ARRAY(s->scratchbuf, FFMAX(s->mconly_picture->linesize[0], 2*avctx->width+256) * 7 * MB_SIZE) || !FF_ALLOCZ_TYPED_ARRAY(s->emu_edge_buffer, emu_buf_size)) return AVERROR(ENOMEM); } - if (av_codec_is_decoder(avctx->codec) && - s->mconly_picture->format != avctx->pix_fmt) { - av_log(avctx, AV_LOG_ERROR, "pixel format changed\n"); - return AVERROR_INVALIDDATA; - } - for(plane_index=0; plane_index < s->nb_planes; plane_index++){ int w= s->avctx->width; int h= s->avctx->height; @@ -589,35 +519,33 @@ void ff_snow_release_buffer(AVCodecContext *avctx) } } -int ff_snow_frame_start(SnowContext *s){ +int ff_snow_frames_prepare(SnowContext *s) +{ AVFrame *tmp; - int i, ret; ff_snow_release_buffer(s->avctx); tmp= s->last_picture[s->max_ref_frames-1]; - for(i=s->max_ref_frames-1; i>0; i--) + for (int i = s->max_ref_frames - 1; i > 0; i--) s->last_picture[i] = s->last_picture[i-1]; s->last_picture[0] = s->current_picture; s->current_picture = tmp; if(s->keyframe){ s->ref_frames= 0; + s->current_picture->flags |= AV_FRAME_FLAG_KEY; }else{ int i; for(i=0; imax_ref_frames && s->last_picture[i]->data[0]; i++) - if(i && s->last_picture[i-1]->key_frame) + if(i && (s->last_picture[i-1]->flags & AV_FRAME_FLAG_KEY)) break; s->ref_frames= i; if(s->ref_frames==0){ av_log(s->avctx,AV_LOG_ERROR, "No reference frames\n"); return AVERROR_INVALIDDATA; } + s->current_picture->flags &= ~AV_FRAME_FLAG_KEY; } - if ((ret = ff_snow_get_buffer(s, s->current_picture)) < 0) - return ret; - - s->current_picture->key_frame= s->keyframe; return 0; } @@ -632,18 +560,11 @@ av_cold void ff_snow_common_end(SnowContext *s) av_freep(&s->temp_idwt_buffer); av_freep(&s->run_buffer); - s->m.me.temp= NULL; - av_freep(&s->m.me.scratchpad); - av_freep(&s->m.me.map); - av_freep(&s->m.sc.obmc_scratchpad); - av_freep(&s->block); av_freep(&s->scratchbuf); av_freep(&s->emu_edge_buffer); for(i=0; iref_mvs[i]); - av_freep(&s->ref_scores[i]); if(s->last_picture[i] && s->last_picture[i]->data[0]) { av_assert0(s->last_picture[i]->data[0] != s->current_picture->data[0]); } diff --git a/libavcodec/snow.h b/libavcodec/snow.h index ed0f9abb423..a5e2c138cbf 100644 --- a/libavcodec/snow.h +++ b/libavcodec/snow.h @@ -24,21 +24,18 @@ #include "libavutil/motion_vector.h" +#include "avcodec.h" #include "hpeldsp.h" -#include "me_cmp.h" -#include "qpeldsp.h" #include "snow_dwt.h" #include "rangecoder.h" #include "mathops.h" -#include "mpegvideo.h" #include "h264qpel.h" +#include "videodsp.h" #define SNOW_MAX_PLANES 4 -#define FF_ME_ITER 3 - #define MID_STATE 128 #define MAX_PLANES 4 @@ -117,12 +114,9 @@ typedef struct SnowContext{ AVClass *class; AVCodecContext *avctx; RangeCoder c; - MECmpContext mecc; HpelDSPContext hdsp; - QpelDSPContext qdsp; VideoDSPContext vdsp; H264QpelContext h264qpel; - MpegvideoEncDSPContext mpvencdsp; SnowDWTContext dwt; AVFrame *input_picture; ///< new_picture with the internal linesizes AVFrame *current_picture; @@ -155,9 +149,6 @@ typedef struct SnowContext{ int spatial_scalability; int qlog; int last_qlog; - int lambda; - int lambda2; - int pass1_rc; int mv_scale; int last_mv_scale; int qbias; @@ -170,18 +161,7 @@ typedef struct SnowContext{ int nb_planes; Plane plane[MAX_PLANES]; BlockNode *block; -#define ME_CACHE_SIZE 1024 - unsigned me_cache[ME_CACHE_SIZE]; - unsigned me_cache_generation; slice_buffer sb; - int memc_only; - int no_bitstream; - int intra_penalty; - int motion_est; - int iterative_dia_size; - int scenechange_threshold; - - MpegEncContext m; // needed for motion estimation, should not be used for anything else, the idea is to eventually make the motion estimation independent of MpegEncContext, so this will be removed then (FIXME/XXX) uint8_t *scratchbuf; uint8_t *emu_edge_buffer; @@ -189,9 +169,6 @@ typedef struct SnowContext{ AVMotionVector *avmv; unsigned avmv_size; int avmv_index; - uint64_t encoding_error[SNOW_MAX_PLANES]; - - int pred; }SnowContext; /* Tables */ @@ -199,44 +176,6 @@ extern const uint8_t * const ff_obmc_tab[4]; extern const uint8_t ff_qexp[QROOT]; extern int ff_scale_mv_ref[MAX_REF_FRAMES][MAX_REF_FRAMES]; -/* C bits used by mmx/sse2/altivec */ - -static av_always_inline void snow_interleave_line_header(int * i, int width, IDWTELEM * low, IDWTELEM * high){ - (*i) = (width) - 2; - - if (width & 1){ - low[(*i)+1] = low[((*i)+1)>>1]; - (*i)--; - } -} - -static av_always_inline void snow_interleave_line_footer(int * i, IDWTELEM * low, IDWTELEM * high){ - for (; (*i)>=0; (*i)-=2){ - low[(*i)+1] = high[(*i)>>1]; - low[*i] = low[(*i)>>1]; - } -} - -static av_always_inline void snow_horizontal_compose_lift_lead_out(int i, IDWTELEM * dst, IDWTELEM * src, IDWTELEM * ref, int width, int w, int lift_high, int mul, int add, int shift){ - for(; i> shift); - } - - if((width^lift_high)&1){ - dst[w] = src[w] - ((mul * 2 * ref[w] + add) >> shift); - } -} - -static av_always_inline void snow_horizontal_compose_liftS_lead_out(int i, IDWTELEM * dst, IDWTELEM * src, IDWTELEM * ref, int width, int w){ - for(; i> W_BS); - } - - if(width&1){ - dst[w] = src[w] + ((2 * ref[w] + W_BO + 4 * src[w]) >> W_BS); - } -} - /* common code */ int ff_snow_common_init(AVCodecContext *avctx); @@ -245,11 +184,10 @@ void ff_snow_common_end(SnowContext *s); void ff_snow_release_buffer(AVCodecContext *avctx); void ff_snow_reset_contexts(SnowContext *s); int ff_snow_alloc_blocks(SnowContext *s); -int ff_snow_frame_start(SnowContext *s); +int ff_snow_frames_prepare(SnowContext *s); void ff_snow_pred_block(SnowContext *s, uint8_t *dst, uint8_t *tmp, ptrdiff_t stride, int sx, int sy, int b_w, int b_h, const BlockNode *block, int plane_index, int w, int h); -int ff_snow_get_buffer(SnowContext *s, AVFrame *frame); /* common inline functions */ //XXX doublecheck all of them should stay inlined @@ -486,227 +424,8 @@ static inline void set_blocks(SnowContext *s, int level, int x, int y, int l, in } } -static inline void init_ref(MotionEstContext *c, const uint8_t *const src[3], uint8_t *const ref[3], uint8_t *const ref2[3], int x, int y, int ref_index){ - SnowContext *s = c->avctx->priv_data; - const int offset[3]= { - y*c-> stride + x, - ((y*c->uvstride + x)>>s->chroma_h_shift), - ((y*c->uvstride + x)>>s->chroma_h_shift), - }; - int i; - for(i=0; i<3; i++){ - c->src[0][i]= src [i]; - c->ref[0][i]= ref [i] + offset[i]; - } - av_assert2(!ref_index); -} - - -/* bitstream functions */ - extern const int8_t ff_quant3bA[256]; #define QEXPSHIFT (7-FRAC_BITS+8) //FIXME try to change this to 0 -static inline void put_symbol(RangeCoder *c, uint8_t *state, int v, int is_signed){ - int i; - - if(v){ - const int a= FFABS(v); - const int e= av_log2(a); - const int el= FFMIN(e, 10); - put_rac(c, state+0, 0); - - for(i=0; i=el; i--){ - put_rac(c, state+22+9, (a>>i)&1); //22..31 - } - for(; i>=0; i--){ - put_rac(c, state+22+i, (a>>i)&1); //22..31 - } - - if(is_signed) - put_rac(c, state+11 + el, v < 0); //11..21 - }else{ - put_rac(c, state+0, 1); - } -} - -static inline int get_symbol(RangeCoder *c, uint8_t *state, int is_signed){ - if(get_rac(c, state+0)) - return 0; - else{ - int i, e; - unsigned a; - e= 0; - while(get_rac(c, state+1 + FFMIN(e,9))){ //1..10 - e++; - if (e > 31) - return AVERROR_INVALIDDATA; - } - - a= 1; - for(i=e-1; i>=0; i--){ - a += a + get_rac(c, state+22 + FFMIN(i,9)); //22..31 - } - - e= -(is_signed && get_rac(c, state+11 + FFMIN(e,10))); //11..21 - return (a^e)-e; - } -} - -static inline void put_symbol2(RangeCoder *c, uint8_t *state, int v, int log2){ - int i; - int r= log2>=0 ? 1<=0); - av_assert2(log2>=-4); - - while(v >= r){ - put_rac(c, state+4+log2, 1); - v -= r; - log2++; - if(log2>0) r+=r; - } - put_rac(c, state+4+log2, 0); - - for(i=log2-1; i>=0; i--){ - put_rac(c, state+31-i, (v>>i)&1); - } -} - -static inline int get_symbol2(RangeCoder *c, uint8_t *state, int log2){ - int i; - int r= log2>=0 ? 1<=-4); - - while(log2<28 && get_rac(c, state+4+log2)){ - v+= r; - log2++; - if(log2>0) r+=r; - } - - for(i=log2-1; i>=0; i--){ - v+= get_rac(c, state+31-i)<width; - const int h= b->height; - int x,y; - - int run, runs; - x_and_coeff *xc= b->x_coeff; - x_and_coeff *prev_xc= NULL; - x_and_coeff *prev2_xc= xc; - x_and_coeff *parent_xc= parent ? parent->x_coeff : NULL; - x_and_coeff *prev_parent_xc= parent_xc; - - runs= get_symbol2(&s->c, b->state[30], 0); - if(runs-- > 0) run= get_symbol2(&s->c, b->state[1], 3); - else run= INT_MAX; - - for(y=0; yx == 0){ - rt= prev_xc->coeff; - } - for(x=0; xx <= x) - prev_xc++; - if(prev_xc->x == x + 1) - rt= prev_xc->coeff; - else - rt=0; - } - if(parent_xc){ - if(x>>1 > parent_xc->x){ - parent_xc++; - } - if(x>>1 == parent_xc->x){ - p= parent_xc->coeff; - } - } - if(/*ll|*/l|lt|t|rt|p){ - int context= av_log2(/*FFABS(ll) + */3*(l>>1) + (lt>>1) + (t&~1) + (rt>>1) + (p>>1)); - - v=get_rac(&s->c, &b->state[0][context]); - if(v){ - v= 2*(get_symbol2(&s->c, b->state[context + 2], context-4) + 1); - v+=get_rac(&s->c, &b->state[0][16 + 1 + 3 + ff_quant3bA[l&0xFF] + 3*ff_quant3bA[t&0xFF]]); - if ((uint16_t)v != v) { - av_log(s->avctx, AV_LOG_ERROR, "Coefficient damaged\n"); - v = 1; - } - xc->x=x; - (xc++)->coeff= v; - } - }else{ - if(!run){ - if(runs-- > 0) run= get_symbol2(&s->c, b->state[1], 3); - else run= INT_MAX; - v= 2*(get_symbol2(&s->c, b->state[0 + 2], 0-4) + 1); - v+=get_rac(&s->c, &b->state[0][16 + 1 + 3]); - if ((uint16_t)v != v) { - av_log(s->avctx, AV_LOG_ERROR, "Coefficient damaged\n"); - v = 1; - } - - xc->x=x; - (xc++)->coeff= v; - }else{ - int max_run; - run--; - v=0; - av_assert2(run >= 0); - if(y) max_run= FFMIN(run, prev_xc->x - x - 2); - else max_run= FFMIN(run, w-x-1); - if(parent_xc) - max_run= FFMIN(max_run, 2*parent_xc->x - x - 1); - av_assert2(max_run >= 0 && max_run <= run); - - x+= max_run; - run-= max_run; - } - } - } - (xc++)->x= w+1; //end marker - prev_xc= prev2_xc; - prev2_xc= xc; - - if(parent_xc){ - if(y&1){ - while(parent_xc->x != parent->width+1) - parent_xc++; - parent_xc++; - prev_parent_xc= parent_xc; - }else{ - parent_xc= prev_parent_xc; - } - } - } - - (xc++)->x= w+1; //end marker -} - #endif /* AVCODEC_SNOW_H */ diff --git a/libavcodec/snow_dwt.h b/libavcodec/snow_dwt.h index 15b8a3007b1..6e7d22c71a6 100644 --- a/libavcodec/snow_dwt.h +++ b/libavcodec/snow_dwt.h @@ -24,6 +24,8 @@ #include #include +#include "libavutil/attributes.h" + struct MpegEncContext; typedef int DWTELEM; @@ -91,6 +93,44 @@ typedef struct SnowDWTContext { : ff_slice_buffer_load_line((slice_buf), \ (line_num))) +/* C bits used by mmx/sse2/altivec */ + +static av_always_inline void snow_interleave_line_header(int *i, int width, IDWTELEM *low, IDWTELEM *high) +{ + *i = width - 2; + + if (width & 1) { + low[*i + 1] = low[(*i + 1)>>1]; + (*i)--; + } +} + +static av_always_inline void snow_interleave_line_footer(int *i, IDWTELEM *low, const IDWTELEM *high) +{ + for (; *i >= 0; *i -= 2) { + low[*i + 1] = high[*i >> 1]; + low[*i] = low[*i >> 1]; + } +} + +static av_always_inline void snow_horizontal_compose_lift_lead_out(int i, IDWTELEM *dst, const IDWTELEM *src, const IDWTELEM *ref, int width, int w, int lift_high, int mul, int add, int shift) +{ + for (; i < w; i++) + dst[i] = src[i] - ((mul * (ref[i] + ref[i + 1]) + add) >> shift); + + if ((width ^ lift_high) & 1) + dst[w] = src[w] - ((mul * 2 * ref[w] + add) >> shift); +} + +static av_always_inline void snow_horizontal_compose_liftS_lead_out(int i, IDWTELEM *dst, const IDWTELEM *src, const IDWTELEM *ref, int width, int w) +{ + for (; i < w; i++) + dst[i] = src[i] + ((ref[i] + ref[(i+1)]+W_BO + 4 * src[i]) >> W_BS); + + if (width & 1) + dst[w] = src[w] + ((2 * ref[w] + W_BO + 4 * src[w]) >> W_BS); +} + int ff_slice_buffer_init(slice_buffer *buf, int line_count, int max_allocated_lines, int line_width, IDWTELEM *base_buffer); diff --git a/libavcodec/snowdec.c b/libavcodec/snowdec.c index bed29d33908..70fbab9a499 100644 --- a/libavcodec/snowdec.c +++ b/libavcodec/snowdec.c @@ -18,17 +18,166 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/emms.h" #include "libavutil/intmath.h" #include "libavutil/log.h" #include "libavutil/opt.h" #include "avcodec.h" #include "codec_internal.h" +#include "decode.h" #include "snow_dwt.h" #include "snow.h" #include "rangecoder.h" #include "mathops.h" +static inline int get_symbol(RangeCoder *c, uint8_t *state, int is_signed) +{ + if (get_rac(c, state + 0)) + return 0; + else { + int e; + unsigned a; + e = 0; + while (get_rac(c, state + 1 + FFMIN(e, 9))) { //1..10 + e++; + if (e > 31) + return AVERROR_INVALIDDATA; + } + + a = 1; + for (int i = e - 1; i >= 0; i--) + a += a + get_rac(c, state + 22 + FFMIN(i, 9)); //22..31 + + e = -(is_signed && get_rac(c, state + 11 + FFMIN(e, 10))); //11..21 + return (a ^ e) - e; + } +} + +static inline int get_symbol2(RangeCoder *c, uint8_t *state, int log2) +{ + int r = log2 >= 0 ? 1 << log2 : 1; + int v = 0; + + av_assert2(log2 >= -4); + + while (log2 < 28 && get_rac(c, state + 4 + log2)) { + v += r; + log2++; + if (log2 > 0) r += r; + } + + for (int i = log2 - 1; i >= 0; i--) + v += get_rac(c, state + 31 - i) << i; + + return v; +} + +static void unpack_coeffs(SnowContext *s, SubBand *b, SubBand * parent, int orientation) +{ + const int w = b->width; + const int h = b->height; + + int run, runs; + x_and_coeff *xc = b->x_coeff; + x_and_coeff *prev_xc = NULL; + x_and_coeff *prev2_xc = xc; + x_and_coeff *parent_xc = parent ? parent->x_coeff : NULL; + x_and_coeff *prev_parent_xc = parent_xc; + + runs = get_symbol2(&s->c, b->state[30], 0); + if (runs-- > 0) run = get_symbol2(&s->c, b->state[1], 3); + else run = INT_MAX; + + for (int y = 0; y < h; y++) { + int v = 0; + int lt = 0, t = 0, rt = 0; + + if (y && prev_xc->x == 0) + rt = prev_xc->coeff; + + for (int x = 0; x < w; x++) { + int p = 0; + const int l = v; + + lt= t; t= rt; + + if (y) { + if (prev_xc->x <= x) + prev_xc++; + if (prev_xc->x == x + 1) + rt = prev_xc->coeff; + else + rt = 0; + } + if (parent_xc) { + if (x>>1 > parent_xc->x) + parent_xc++; + if (x>>1 == parent_xc->x) + p = parent_xc->coeff; + } + if (/*ll|*/l|lt|t|rt|p) { + int context = av_log2(/*FFABS(ll) + */3*(l>>1) + (lt>>1) + (t&~1) + (rt>>1) + (p>>1)); + + v = get_rac(&s->c, &b->state[0][context]); + if (v) { + v = 2*(get_symbol2(&s->c, b->state[context + 2], context-4) + 1); + v += get_rac(&s->c, &b->state[0][16 + 1 + 3 + ff_quant3bA[l&0xFF] + 3 * ff_quant3bA[t&0xFF]]); + if ((uint16_t)v != v) { + av_log(s->avctx, AV_LOG_ERROR, "Coefficient damaged\n"); + v = 1; + } + xc->x = x; + (xc++)->coeff = v; + } + } else { + if (!run) { + if (runs-- > 0) run = get_symbol2(&s->c, b->state[1], 3); + else run = INT_MAX; + v = 2 * (get_symbol2(&s->c, b->state[0 + 2], 0-4) + 1); + v += get_rac(&s->c, &b->state[0][16 + 1 + 3]); + if ((uint16_t)v != v) { + av_log(s->avctx, AV_LOG_ERROR, "Coefficient damaged\n"); + v = 1; + } + + xc->x = x; + (xc++)->coeff = v; + } else { + int max_run; + run--; + v = 0; + av_assert2(run >= 0); + if (y) max_run = FFMIN(run, prev_xc->x - x - 2); + else max_run = FFMIN(run, w-x-1); + if (parent_xc) + max_run = FFMIN(max_run, 2*parent_xc->x - x - 1); + av_assert2(max_run >= 0 && max_run <= run); + + x += max_run; + run -= max_run; + } + } + } + (xc++)->x = w+1; //end marker + prev_xc = prev2_xc; + prev2_xc = xc; + + if (parent_xc) { + if (y & 1) { + while (parent_xc->x != parent->width+1) + parent_xc++; + parent_xc++; + prev_parent_xc= parent_xc; + } else { + parent_xc= prev_parent_xc; + } + } + } + + (xc++)->x = w + 1; //end marker +} + static av_always_inline void predict_slice_buffered(SnowContext *s, slice_buffer * sb, IDWTELEM * old_buffer, int plane_index, int add, int mb_y){ Plane *p= &s->plane[plane_index]; const int mb_w= s->b_width << s->block_max_depth; @@ -453,6 +602,17 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, s->current_picture->pict_type= AV_PICTURE_TYPE_I; //FIXME I vs. P if ((res = decode_header(s)) < 0) return res; + + if (!s->mconly_picture->data[0]) { + res = ff_get_buffer(avctx, s->mconly_picture, AV_GET_BUFFER_FLAG_REF); + if (res < 0) + return res; + } + if (s->mconly_picture->format != avctx->pix_fmt) { + av_log(avctx, AV_LOG_ERROR, "pixel format changed\n"); + return AVERROR_INVALIDDATA; + } + if ((res=ff_snow_common_init_after_header(avctx)) < 0) return res; @@ -474,7 +634,13 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, ff_snow_alloc_blocks(s); - if((res = ff_snow_frame_start(s)) < 0) + if ((res = ff_snow_frames_prepare(s)) < 0) + return res; + + s->current_picture->width = s->avctx->width; + s->current_picture->height = s->avctx->height; + res = ff_get_buffer(s->avctx, s->current_picture, AV_GET_BUFFER_FLAG_REF); + if (res < 0) return res; s->current_picture->pict_type = s->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; diff --git a/libavcodec/snowenc.c b/libavcodec/snowenc.c index 7163e931a1f..70a2de16717 100644 --- a/libavcodec/snowenc.c +++ b/libavcodec/snowenc.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/emms.h" #include "libavutil/intmath.h" #include "libavutil/libm.h" #include "libavutil/log.h" @@ -26,8 +27,10 @@ #include "avcodec.h" #include "codec_internal.h" #include "encode.h" +#include "internal.h" //For AVCodecInternal.recon_frame #include "me_cmp.h" #include "packet_internal.h" +#include "qpeldsp.h" #include "snow_dwt.h" #include "snow.h" @@ -37,20 +40,136 @@ #include "mpegvideo.h" #include "h263enc.h" +#define FF_ME_ITER 3 + +typedef struct SnowEncContext { + SnowContext com; + QpelDSPContext qdsp; + MpegvideoEncDSPContext mpvencdsp; + + int lambda; + int lambda2; + int pass1_rc; + + int pred; + int memc_only; + int no_bitstream; + int intra_penalty; + int motion_est; + int iterative_dia_size; + int scenechange_threshold; + + MECmpContext mecc; + MpegEncContext m; // needed for motion estimation, should not be used for anything else, the idea is to eventually make the motion estimation independent of MpegEncContext, so this will be removed then (FIXME/XXX) +#define ME_CACHE_SIZE 1024 + unsigned me_cache[ME_CACHE_SIZE]; + unsigned me_cache_generation; + + uint64_t encoding_error[SNOW_MAX_PLANES]; +} SnowEncContext; + +static void init_ref(MotionEstContext *c, const uint8_t *const src[3], + uint8_t *const ref[3], uint8_t *const ref2[3], + int x, int y, int ref_index) +{ + SnowContext *s = c->avctx->priv_data; + const int offset[3] = { + y*c-> stride + x, + ((y*c->uvstride + x) >> s->chroma_h_shift), + ((y*c->uvstride + x) >> s->chroma_h_shift), + }; + for (int i = 0; i < 3; i++) { + c->src[0][i] = src [i]; + c->ref[0][i] = ref [i] + offset[i]; + } + av_assert2(!ref_index); +} + +static inline void put_symbol(RangeCoder *c, uint8_t *state, int v, int is_signed) +{ + if (v) { + const int a = FFABS(v); + const int e = av_log2(a); + const int el = FFMIN(e, 10); + int i; + + put_rac(c, state + 0, 0); + + for (i = 0; i < el; i++) + put_rac(c, state + 1 + i, 1); //1..10 + for(; i < e; i++) + put_rac(c, state + 1 + 9, 1); //1..10 + put_rac(c, state + 1 + FFMIN(i, 9), 0); + + for (i = e - 1; i >= el; i--) + put_rac(c, state + 22 + 9, (a >> i) & 1); //22..31 + for(; i >= 0; i--) + put_rac(c, state + 22 + i, (a >> i) & 1); //22..31 + + if (is_signed) + put_rac(c, state + 11 + el, v < 0); //11..21 + } else { + put_rac(c, state + 0, 1); + } +} + +static inline void put_symbol2(RangeCoder *c, uint8_t *state, int v, int log2) +{ + int r = log2 >= 0 ? 1<= 0); + av_assert2(log2 >= -4); + + while (v >= r) { + put_rac(c, state + 4 + log2, 1); + v -= r; + log2++; + if (log2 > 0) r += r; + } + put_rac(c, state + 4 + log2, 0); + + for (int i = log2 - 1; i >= 0; i--) + put_rac(c, state + 31 - i, (v >> i) & 1); +} + +static int get_encode_buffer(SnowContext *s, AVFrame *frame) +{ + int ret; + + frame->width = s->avctx->width + 2 * EDGE_WIDTH; + frame->height = s->avctx->height + 2 * EDGE_WIDTH; + + ret = ff_encode_alloc_frame(s->avctx, frame); + if (ret < 0) + return ret; + for (int i = 0; frame->data[i]; i++) { + int offset = (EDGE_WIDTH >> (i ? s->chroma_v_shift : 0)) * + frame->linesize[i] + + (EDGE_WIDTH >> (i ? s->chroma_h_shift : 0)); + frame->data[i] += offset; + } + frame->width = s->avctx->width; + frame->height = s->avctx->height; + + return 0; +} + static av_cold int encode_init(AVCodecContext *avctx) { - SnowContext *s = avctx->priv_data; + SnowEncContext *const enc = avctx->priv_data; + SnowContext *const s = &enc->com; + MpegEncContext *const mpv = &enc->m; int plane_index, ret; int i; - if(s->pred == DWT_97 + if (enc->pred == DWT_97 && (avctx->flags & AV_CODEC_FLAG_QSCALE) && avctx->global_quality == 0){ av_log(avctx, AV_LOG_ERROR, "The 9/7 wavelet is incompatible with lossless mode.\n"); return AVERROR(EINVAL); } - s->spatial_decomposition_type= s->pred; //FIXME add decorrelator type r transform_type + s->spatial_decomposition_type = enc->pred; //FIXME add decorrelator type r transform_type s->mv_scale = (avctx->flags & AV_CODEC_FLAG_QPEL) ? 2 : 4; s->block_max_depth= (avctx->flags & AV_CODEC_FLAG_4MV ) ? 1 : 0; @@ -64,31 +183,59 @@ static av_cold int encode_init(AVCodecContext *avctx) s->plane[plane_index].fast_mc= 1; } + // Must be before ff_snow_common_init() + ff_hpeldsp_init(&s->hdsp, avctx->flags); if ((ret = ff_snow_common_init(avctx)) < 0) { return ret; } - ff_me_cmp_init(&s->mecc, avctx); - ff_mpegvideoencdsp_init(&s->mpvencdsp, avctx); + +#define mcf(dx,dy)\ + enc->qdsp.put_qpel_pixels_tab [0][dy+dx/4]=\ + enc->qdsp.put_no_rnd_qpel_pixels_tab[0][dy+dx/4]=\ + s->h264qpel.put_h264_qpel_pixels_tab[0][dy+dx/4];\ + enc->qdsp.put_qpel_pixels_tab [1][dy+dx/4]=\ + enc->qdsp.put_no_rnd_qpel_pixels_tab[1][dy+dx/4]=\ + s->h264qpel.put_h264_qpel_pixels_tab[1][dy+dx/4]; + + mcf( 0, 0) + mcf( 4, 0) + mcf( 8, 0) + mcf(12, 0) + mcf( 0, 4) + mcf( 4, 4) + mcf( 8, 4) + mcf(12, 4) + mcf( 0, 8) + mcf( 4, 8) + mcf( 8, 8) + mcf(12, 8) + mcf( 0,12) + mcf( 4,12) + mcf( 8,12) + mcf(12,12) + + ff_me_cmp_init(&enc->mecc, avctx); + ff_mpegvideoencdsp_init(&enc->mpvencdsp, avctx); ff_snow_alloc_blocks(s); s->version=0; - s->m.avctx = avctx; - s->m.bit_rate= avctx->bit_rate; - s->m.lmin = avctx->mb_lmin; - s->m.lmax = avctx->mb_lmax; - s->m.mb_num = (avctx->width * avctx->height + 255) / 256; // For ratecontrol - - s->m.me.temp = - s->m.me.scratchpad = av_calloc(avctx->width + 64, 2*16*2*sizeof(uint8_t)); - s->m.sc.obmc_scratchpad= av_mallocz(MB_SIZE*MB_SIZE*12*sizeof(uint32_t)); - s->m.me.map = av_mallocz(2 * ME_MAP_SIZE * sizeof(*s->m.me.map)); - if (!s->m.me.scratchpad || !s->m.me.map || !s->m.sc.obmc_scratchpad) + mpv->avctx = avctx; + mpv->bit_rate= avctx->bit_rate; + mpv->lmin = avctx->mb_lmin; + mpv->lmax = avctx->mb_lmax; + mpv->mb_num = (avctx->width * avctx->height + 255) / 256; // For ratecontrol + + mpv->me.temp = + mpv->me.scratchpad = av_calloc(avctx->width + 64, 2*16*2*sizeof(uint8_t)); + mpv->sc.obmc_scratchpad= av_mallocz(MB_SIZE*MB_SIZE*12*sizeof(uint32_t)); + mpv->me.map = av_mallocz(2 * ME_MAP_SIZE * sizeof(*mpv->me.map)); + if (!mpv->me.scratchpad || !mpv->me.map || !mpv->sc.obmc_scratchpad) return AVERROR(ENOMEM); - s->m.me.score_map = s->m.me.map + ME_MAP_SIZE; + mpv->me.score_map = mpv->me.map + ME_MAP_SIZE; - ff_h263_encode_init(&s->m); //mv_penalty + ff_h263_encode_init(mpv); //mv_penalty s->max_ref_frames = av_clip(avctx->refs, 1, MAX_REF_FRAMES); @@ -100,11 +247,11 @@ static av_cold int encode_init(AVCodecContext *avctx) return AVERROR(ENOMEM); } if((avctx->flags&AV_CODEC_FLAG_PASS2) || !(avctx->flags&AV_CODEC_FLAG_QSCALE)){ - ret = ff_rate_control_init(&s->m); + ret = ff_rate_control_init(mpv); if(ret < 0) return ret; } - s->pass1_rc= !(avctx->flags & (AV_CODEC_FLAG_QSCALE|AV_CODEC_FLAG_PASS2)); + enc->pass1_rc = !(avctx->flags & (AV_CODEC_FLAG_QSCALE|AV_CODEC_FLAG_PASS2)); switch(avctx->pix_fmt){ case AV_PIX_FMT_YUV444P: @@ -129,8 +276,8 @@ static av_cold int encode_init(AVCodecContext *avctx) if (ret) return ret; - ret = ff_set_cmp(&s->mecc, s->mecc.me_cmp, s->avctx->me_cmp); - ret |= ff_set_cmp(&s->mecc, s->mecc.me_sub_cmp, s->avctx->me_sub_cmp); + ret = ff_set_cmp(&enc->mecc, enc->mecc.me_cmp, s->avctx->me_cmp); + ret |= ff_set_cmp(&enc->mecc, enc->mecc.me_sub_cmp, s->avctx->me_sub_cmp); if (ret < 0) return AVERROR(EINVAL); @@ -138,10 +285,10 @@ static av_cold int encode_init(AVCodecContext *avctx) if (!s->input_picture) return AVERROR(ENOMEM); - if ((ret = ff_snow_get_buffer(s, s->input_picture)) < 0) + if ((ret = get_encode_buffer(s, s->input_picture)) < 0) return ret; - if(s->motion_est == FF_ME_ITER){ + if (enc->motion_est == FF_ME_ITER) { int size= s->b_width * s->b_height << 2*s->block_max_depth; for(i=0; imax_ref_frames; i++){ s->ref_mvs[i] = av_calloc(size, sizeof(*s->ref_mvs[i])); @@ -219,7 +366,10 @@ static inline int get_penalty_factor(int lambda, int lambda2, int type){ #define P_MV1 P[9] #define FLAG_QPEL 1 //must be 1 -static int encode_q_branch(SnowContext *s, int level, int x, int y){ +static int encode_q_branch(SnowEncContext *enc, int level, int x, int y) +{ + SnowContext *const s = &enc->com; + MotionEstContext *const c = &enc->m.me; uint8_t p_buffer[1024]; uint8_t i_buffer[1024]; uint8_t p_state[sizeof(s->block_state)]; @@ -256,12 +406,12 @@ static int encode_q_branch(SnowContext *s, int level, int x, int y){ int16_t last_mv[3][2]; int qpel= !!(s->avctx->flags & AV_CODEC_FLAG_QPEL); //unused const int shift= 1+qpel; - MotionEstContext *c= &s->m.me; int ref_context= av_log2(2*left->ref) + av_log2(2*top->ref); int mx_context= av_log2(2*FFABS(left->mx - top->mx)); int my_context= av_log2(2*FFABS(left->my - top->my)); int s_context= 2*left->level + 2*top->level + tl->level + tr->level; int ref, best_ref, ref_score, ref_mx, ref_my; + int range = MAX_MV >> (1 + qpel); av_assert0(sizeof(s->block_state) >= 256); if(s->keyframe){ @@ -285,24 +435,29 @@ static int encode_q_branch(SnowContext *s, int level, int x, int y){ last_mv[2][0]= bottom->mx; last_mv[2][1]= bottom->my; - s->m.mb_stride=2; - s->m.mb_x= - s->m.mb_y= 0; + enc->m.mb_stride = 2; + enc->m.mb_x = + enc->m.mb_y = 0; c->skip= 0; av_assert1(c-> stride == stride); av_assert1(c->uvstride == uvstride); - c->penalty_factor = get_penalty_factor(s->lambda, s->lambda2, c->avctx->me_cmp); - c->sub_penalty_factor= get_penalty_factor(s->lambda, s->lambda2, c->avctx->me_sub_cmp); - c->mb_penalty_factor = get_penalty_factor(s->lambda, s->lambda2, c->avctx->mb_cmp); - c->current_mv_penalty= c->mv_penalty[s->m.f_code=1] + MAX_DMV; + c->penalty_factor = get_penalty_factor(enc->lambda, enc->lambda2, c->avctx->me_cmp); + c->sub_penalty_factor= get_penalty_factor(enc->lambda, enc->lambda2, c->avctx->me_sub_cmp); + c->mb_penalty_factor = get_penalty_factor(enc->lambda, enc->lambda2, c->avctx->mb_cmp); + c->current_mv_penalty = c->mv_penalty[enc->m.f_code=1] + MAX_DMV; c->xmin = - x*block_w - 16+3; c->ymin = - y*block_w - 16+3; c->xmax = - (x+1)*block_w + (w<<(LOG2_MB_SIZE - s->block_max_depth)) + 16-3; c->ymax = - (y+1)*block_w + (h<<(LOG2_MB_SIZE - s->block_max_depth)) + 16-3; + c->xmin = FFMAX(c->xmin,-range); + c->xmax = FFMIN(c->xmax, range); + c->ymin = FFMAX(c->ymin,-range); + c->ymax = FFMIN(c->ymax, range); + if(P_LEFT[0] > (c->xmax<xmax< (c->ymax<ymax< (c->xmax<xmax<ref_frames; ref++){ init_ref(c, current_data, s->last_picture[ref]->data, NULL, block_w*x, block_w*y, 0); - ref_score= ff_epzs_motion_search(&s->m, &ref_mx, &ref_my, P, 0, /*ref_index*/ 0, last_mv, + ref_score= ff_epzs_motion_search(&enc->m, &ref_mx, &ref_my, P, 0, /*ref_index*/ 0, last_mv, (1<<16)>>shift, level-LOG2_MB_SIZE+4, block_w); av_assert2(ref_mx >= c->xmin); @@ -335,8 +490,8 @@ static int encode_q_branch(SnowContext *s, int level, int x, int y){ av_assert2(ref_my >= c->ymin); av_assert2(ref_my <= c->ymax); - ref_score= c->sub_motion_search(&s->m, &ref_mx, &ref_my, ref_score, 0, 0, level-LOG2_MB_SIZE+4, block_w); - ref_score= ff_get_mb_score(&s->m, ref_mx, ref_my, 0, 0, level-LOG2_MB_SIZE+4, block_w, 0); + ref_score= c->sub_motion_search(&enc->m, &ref_mx, &ref_my, ref_score, 0, 0, level-LOG2_MB_SIZE+4, block_w); + ref_score= ff_get_mb_score(&enc->m, ref_mx, ref_my, 0, 0, level-LOG2_MB_SIZE+4, block_w, 0); ref_score+= 2*av_log2(2*ref)*c->penalty_factor; if(s->ref_mvs[ref]){ s->ref_mvs[ref][index][0]= ref_mx; @@ -368,7 +523,7 @@ static int encode_q_branch(SnowContext *s, int level, int x, int y){ put_symbol(&pc, &p_state[128 + 32*(mx_context + 16*!!best_ref)], mx - pmx, 1); put_symbol(&pc, &p_state[128 + 32*(my_context + 16*!!best_ref)], my - pmy, 1); p_len= pc.bytestream - pc.bytestream_start; - score += (s->lambda2*(get_rac_count(&pc)-base_bits))>>FF_LAMBDA_SHIFT; + score += (enc->lambda2*(get_rac_count(&pc)-base_bits))>>FF_LAMBDA_SHIFT; block_s= block_w*block_w; sum = pix_sum(current_data[0], stride, block_w, block_w); @@ -399,9 +554,9 @@ static int encode_q_branch(SnowContext *s, int level, int x, int y){ put_symbol(&ic, &i_state[96], cr-pcr, 1); } i_len= ic.bytestream - ic.bytestream_start; - iscore += (s->lambda2*(get_rac_count(&ic)-base_bits))>>FF_LAMBDA_SHIFT; + iscore += (enc->lambda2*(get_rac_count(&ic)-base_bits))>>FF_LAMBDA_SHIFT; - av_assert1(iscore < 255*255*256 + s->lambda2*10); + av_assert1(iscore < 255*255*256 + enc->lambda2*10); av_assert1(iscore >= 0); av_assert1(l>=0 && l<=255); av_assert1(pl>=0 && pl<=255); @@ -412,16 +567,16 @@ static int encode_q_branch(SnowContext *s, int level, int x, int y){ if (vard <= 64 || vard < varc) c->scene_change_score+= ff_sqrt(vard) - ff_sqrt(varc); else - c->scene_change_score+= s->m.qscale; + c->scene_change_score += enc->m.qscale; } if(level!=s->block_max_depth){ put_rac(&s->c, &s->block_state[4 + s_context], 0); - score2 = encode_q_branch(s, level+1, 2*x+0, 2*y+0); - score2+= encode_q_branch(s, level+1, 2*x+1, 2*y+0); - score2+= encode_q_branch(s, level+1, 2*x+0, 2*y+1); - score2+= encode_q_branch(s, level+1, 2*x+1, 2*y+1); - score2+= s->lambda2>>FF_LAMBDA_SHIFT; //FIXME exact split overhead + score2 = encode_q_branch(enc, level+1, 2*x+0, 2*y+0); + score2+= encode_q_branch(enc, level+1, 2*x+1, 2*y+0); + score2+= encode_q_branch(enc, level+1, 2*x+0, 2*y+1); + score2+= encode_q_branch(enc, level+1, 2*x+1, 2*y+1); + score2+= enc->lambda2>>FF_LAMBDA_SHIFT; //FIXME exact split overhead if(score2 < score && score2 < iscore) return score2; @@ -503,7 +658,9 @@ static void encode_q_branch2(SnowContext *s, int level, int x, int y){ } } -static int get_dc(SnowContext *s, int mb_x, int mb_y, int plane_index){ +static int get_dc(SnowEncContext *enc, int mb_x, int mb_y, int plane_index) +{ + SnowContext *const s = &enc->com; int i, x2, y2; Plane *p= &s->plane[plane_index]; const int block_size = MB_SIZE >> s->block_max_depth; @@ -513,7 +670,7 @@ static int get_dc(SnowContext *s, int mb_x, int mb_y, int plane_index){ const int obmc_stride= plane_index ? (2*block_size)>>s->chroma_h_shift : 2*block_size; const int ref_stride= s->current_picture->linesize[plane_index]; const uint8_t *src = s->input_picture->data[plane_index]; - IDWTELEM *dst= (IDWTELEM*)s->m.sc.obmc_scratchpad + plane_index*block_size*block_size*4; //FIXME change to unsigned + IDWTELEM *dst= (IDWTELEM*)enc->m.sc.obmc_scratchpad + plane_index*block_size*block_size*4; //FIXME change to unsigned const int b_stride = s->b_width << s->block_max_depth; const int w= p->width; const int h= p->height; @@ -599,7 +756,10 @@ static inline int get_block_bits(SnowContext *s, int x, int y, int w){ } } -static int get_block_rd(SnowContext *s, int mb_x, int mb_y, int plane_index, uint8_t (*obmc_edged)[MB_SIZE * 2]){ +static int get_block_rd(SnowEncContext *enc, int mb_x, int mb_y, + int plane_index, uint8_t (*obmc_edged)[MB_SIZE * 2]) +{ + SnowContext *const s = &enc->com; Plane *p= &s->plane[plane_index]; const int block_size = MB_SIZE >> s->block_max_depth; const int block_w = plane_index ? block_size>>s->chroma_h_shift : block_size; @@ -608,7 +768,7 @@ static int get_block_rd(SnowContext *s, int mb_x, int mb_y, int plane_index, uin const int ref_stride= s->current_picture->linesize[plane_index]; uint8_t *dst= s->current_picture->data[plane_index]; const uint8_t *src = s->input_picture->data[plane_index]; - IDWTELEM *pred= (IDWTELEM*)s->m.sc.obmc_scratchpad + plane_index*block_size*block_size*4; + IDWTELEM *pred= (IDWTELEM*)enc->m.sc.obmc_scratchpad + plane_index*block_size*block_size*4; uint8_t *cur = s->scratchbuf; uint8_t *tmp = s->emu_edge_buffer; const int b_stride = s->b_width << s->block_max_depth; @@ -617,7 +777,7 @@ static int get_block_rd(SnowContext *s, int mb_x, int mb_y, int plane_index, uin const int h= p->height; int distortion; int rate= 0; - const int penalty_factor= get_penalty_factor(s->lambda, s->lambda2, s->avctx->me_cmp); + const int penalty_factor = get_penalty_factor(enc->lambda, enc->lambda2, s->avctx->me_cmp); int sx= block_w*mb_x - block_w/2; int sy= block_h*mb_y - block_h/2; int x0= FFMAX(0,-sx); @@ -671,19 +831,19 @@ static int get_block_rd(SnowContext *s, int mb_x, int mb_y, int plane_index, uin * to improve the score of the whole frame, thus iterative motion * estimation does not always converge. */ if(s->avctx->me_cmp == FF_CMP_W97) - distortion = ff_w97_32_c(&s->m, src + sx + sy*ref_stride, dst + sx + sy*ref_stride, ref_stride, 32); + distortion = ff_w97_32_c(&enc->m, src + sx + sy*ref_stride, dst + sx + sy*ref_stride, ref_stride, 32); else if(s->avctx->me_cmp == FF_CMP_W53) - distortion = ff_w53_32_c(&s->m, src + sx + sy*ref_stride, dst + sx + sy*ref_stride, ref_stride, 32); + distortion = ff_w53_32_c(&enc->m, src + sx + sy*ref_stride, dst + sx + sy*ref_stride, ref_stride, 32); else{ distortion = 0; for(i=0; i<4; i++){ int off = sx+16*(i&1) + (sy+16*(i>>1))*ref_stride; - distortion += s->mecc.me_cmp[0](&s->m, src + off, dst + off, ref_stride, 16); + distortion += enc->mecc.me_cmp[0](&enc->m, src + off, dst + off, ref_stride, 16); } } }else{ av_assert2(block_w==8); - distortion = s->mecc.me_cmp[0](&s->m, src + sx + sy*ref_stride, dst + sx + sy*ref_stride, ref_stride, block_w*2); + distortion = enc->mecc.me_cmp[0](&enc->m, src + sx + sy*ref_stride, dst + sx + sy*ref_stride, ref_stride, block_w*2); } if(plane_index==0){ @@ -700,7 +860,9 @@ static int get_block_rd(SnowContext *s, int mb_x, int mb_y, int plane_index, uin return distortion + rate*penalty_factor; } -static int get_4block_rd(SnowContext *s, int mb_x, int mb_y, int plane_index){ +static int get_4block_rd(SnowEncContext *enc, int mb_x, int mb_y, int plane_index) +{ + SnowContext *const s = &enc->com; int i, y2; Plane *p= &s->plane[plane_index]; const int block_size = MB_SIZE >> s->block_max_depth; @@ -719,7 +881,7 @@ static int get_4block_rd(SnowContext *s, int mb_x, int mb_y, int plane_index){ const int h= p->height; int distortion= 0; int rate= 0; - const int penalty_factor= get_penalty_factor(s->lambda, s->lambda2, s->avctx->me_cmp); + const int penalty_factor= get_penalty_factor(enc->lambda, enc->lambda2, s->avctx->me_cmp); av_assert2(s->chroma_h_shift == s->chroma_v_shift); //obmc and square assumtions below @@ -747,7 +909,7 @@ static int get_4block_rd(SnowContext *s, int mb_x, int mb_y, int plane_index){ } av_assert1(block_w== 8 || block_w==16); - distortion += s->mecc.me_cmp[block_w==8](&s->m, src + x + y*ref_stride, dst + x + y*ref_stride, ref_stride, block_h); + distortion += enc->mecc.me_cmp[block_w==8](&enc->m, src + x + y*ref_stride, dst + x + y*ref_stride, ref_stride, block_h); } if(plane_index==0){ @@ -896,9 +1058,10 @@ static int encode_subband(SnowContext *s, SubBand *b, const IDWTELEM *src, const // encode_subband_dzr(s, b, src, parent, stride, orientation); } -static av_always_inline int check_block_intra(SnowContext *s, int mb_x, int mb_y, int p[3], +static av_always_inline int check_block_intra(SnowEncContext *enc, int mb_x, int mb_y, int p[3], uint8_t (*obmc_edged)[MB_SIZE * 2], int *best_rd) { + SnowContext *const s = &enc->com; const int b_stride= s->b_width << s->block_max_depth; BlockNode *block= &s->block[mb_x + mb_y * b_stride]; BlockNode backup= *block; @@ -912,7 +1075,7 @@ static av_always_inline int check_block_intra(SnowContext *s, int mb_x, int mb_y block->color[2] = p[2]; block->type |= BLOCK_INTRA; - rd = get_block_rd(s, mb_x, mb_y, 0, obmc_edged) + s->intra_penalty; + rd = get_block_rd(enc, mb_x, mb_y, 0, obmc_edged) + enc->intra_penalty; //FIXME chroma if(rd < *best_rd){ @@ -926,7 +1089,11 @@ static av_always_inline int check_block_intra(SnowContext *s, int mb_x, int mb_y /* special case for int[2] args we discard afterwards, * fixes compilation problem with gcc 2.95 */ -static av_always_inline int check_block_inter(SnowContext *s, int mb_x, int mb_y, int p0, int p1, uint8_t (*obmc_edged)[MB_SIZE * 2], int *best_rd){ +static av_always_inline int check_block_inter(SnowEncContext *enc, + int mb_x, int mb_y, int p0, int p1, + uint8_t (*obmc_edged)[MB_SIZE * 2], int *best_rd) +{ + SnowContext *const s = &enc->com; const int b_stride = s->b_width << s->block_max_depth; BlockNode *block = &s->block[mb_x + mb_y * b_stride]; BlockNode backup = *block; @@ -937,16 +1104,16 @@ static av_always_inline int check_block_inter(SnowContext *s, int mb_x, int mb_y av_assert2(mb_x < b_stride); index = (p0 + 31 * p1) & (ME_CACHE_SIZE-1); - value = s->me_cache_generation + (p0 >> 10) + p1 * (1 << 6) + (block->ref << 12); - if (s->me_cache[index] == value) + value = enc->me_cache_generation + (p0 >> 10) + p1 * (1 << 6) + (block->ref << 12); + if (enc->me_cache[index] == value) return 0; - s->me_cache[index] = value; + enc->me_cache[index] = value; block->mx = p0; block->my = p1; block->type &= ~BLOCK_INTRA; - rd = get_block_rd(s, mb_x, mb_y, 0, obmc_edged); + rd = get_block_rd(enc, mb_x, mb_y, 0, obmc_edged); //FIXME chroma if (rd < *best_rd) { @@ -958,7 +1125,10 @@ static av_always_inline int check_block_inter(SnowContext *s, int mb_x, int mb_y } } -static av_always_inline int check_4block_inter(SnowContext *s, int mb_x, int mb_y, int p0, int p1, int ref, int *best_rd){ +static av_always_inline int check_4block_inter(SnowEncContext *enc, int mb_x, int mb_y, + int p0, int p1, int ref, int *best_rd) +{ + SnowContext *const s = &enc->com; const int b_stride= s->b_width << s->block_max_depth; BlockNode *block= &s->block[mb_x + mb_y * b_stride]; BlockNode backup[4]; @@ -978,10 +1148,10 @@ static av_always_inline int check_4block_inter(SnowContext *s, int mb_x, int mb_ av_assert2(((mb_x|mb_y)&1) == 0); index= (p0 + 31*p1) & (ME_CACHE_SIZE-1); - value= s->me_cache_generation + (p0>>10) + (p1<<6) + (block->ref<<12); - if(s->me_cache[index] == value) + value = enc->me_cache_generation + (p0>>10) + (p1<<6) + (block->ref<<12); + if (enc->me_cache[index] == value) return 0; - s->me_cache[index]= value; + enc->me_cache[index] = value; block->mx= p0; block->my= p1; @@ -989,7 +1159,7 @@ static av_always_inline int check_4block_inter(SnowContext *s, int mb_x, int mb_ block->type &= ~BLOCK_INTRA; block[1]= block[b_stride]= block[b_stride+1]= *block; - rd= get_4block_rd(s, mb_x, mb_y, 0); + rd = get_4block_rd(enc, mb_x, mb_y, 0); //FIXME chroma if(rd < *best_rd){ @@ -1004,7 +1174,9 @@ static av_always_inline int check_4block_inter(SnowContext *s, int mb_x, int mb_ } } -static void iterative_me(SnowContext *s){ +static void iterative_me(SnowEncContext *enc) +{ + SnowContext *const s = &enc->com; int pass, mb_x, mb_y; const int b_width = s->b_width << s->block_max_depth; const int b_height= s->b_height << s->block_max_depth; @@ -1017,7 +1189,7 @@ static void iterative_me(SnowContext *s){ memcpy(state, s->block_state, sizeof(s->block_state)); for(mb_y= 0; mb_yb_height; mb_y++) for(mb_x= 0; mb_xb_width; mb_x++) - encode_q_branch(s, 0, mb_x, mb_y); + encode_q_branch(enc, 0, mb_x, mb_y); s->c = r; memcpy(s->block_state, state, sizeof(s->block_state)); } @@ -1049,9 +1221,9 @@ static void iterative_me(SnowContext *s){ backup= *block; - if(!s->me_cache_generation) - memset(s->me_cache, 0, sizeof(s->me_cache)); - s->me_cache_generation += 1<<22; + if (!enc->me_cache_generation) + memset(enc->me_cache, 0, sizeof(enc->me_cache)); + enc->me_cache_generation += 1<<22; //FIXME precalculate { @@ -1107,14 +1279,14 @@ static void iterative_me(SnowContext *s){ // intra(black) = neighbors' contribution to the current block for(i=0; i < s->nb_planes; i++) - color[i]= get_dc(s, mb_x, mb_y, i); + color[i]= get_dc(enc, mb_x, mb_y, i); // get previous score (cannot be cached due to OBMC) if(pass > 0 && (block->type&BLOCK_INTRA)){ int color0[3]= {block->color[0], block->color[1], block->color[2]}; - check_block_intra(s, mb_x, mb_y, color0, obmc_edged, &best_rd); + check_block_intra(enc, mb_x, mb_y, color0, obmc_edged, &best_rd); }else - check_block_inter(s, mb_x, mb_y, block->mx, block->my, obmc_edged, &best_rd); + check_block_inter(enc, mb_x, mb_y, block->mx, block->my, obmc_edged, &best_rd); ref_b= *block; ref_rd= best_rd; @@ -1125,30 +1297,30 @@ static void iterative_me(SnowContext *s){ block->ref= ref; best_rd= INT_MAX; - check_block_inter(s, mb_x, mb_y, mvr[0][0], mvr[0][1], obmc_edged, &best_rd); - check_block_inter(s, mb_x, mb_y, 0, 0, obmc_edged, &best_rd); + check_block_inter(enc, mb_x, mb_y, mvr[0][0], mvr[0][1], obmc_edged, &best_rd); + check_block_inter(enc, mb_x, mb_y, 0, 0, obmc_edged, &best_rd); if(tb) - check_block_inter(s, mb_x, mb_y, mvr[-b_stride][0], mvr[-b_stride][1], obmc_edged, &best_rd); + check_block_inter(enc, mb_x, mb_y, mvr[-b_stride][0], mvr[-b_stride][1], obmc_edged, &best_rd); if(lb) - check_block_inter(s, mb_x, mb_y, mvr[-1][0], mvr[-1][1], obmc_edged, &best_rd); + check_block_inter(enc, mb_x, mb_y, mvr[-1][0], mvr[-1][1], obmc_edged, &best_rd); if(rb) - check_block_inter(s, mb_x, mb_y, mvr[1][0], mvr[1][1], obmc_edged, &best_rd); + check_block_inter(enc, mb_x, mb_y, mvr[1][0], mvr[1][1], obmc_edged, &best_rd); if(bb) - check_block_inter(s, mb_x, mb_y, mvr[b_stride][0], mvr[b_stride][1], obmc_edged, &best_rd); + check_block_inter(enc, mb_x, mb_y, mvr[b_stride][0], mvr[b_stride][1], obmc_edged, &best_rd); /* fullpel ME */ //FIXME avoid subpel interpolation / round to nearest integer do{ int newx = block->mx; int newy = block->my; - int dia_size = s->iterative_dia_size ? s->iterative_dia_size : FFMAX(s->avctx->dia_size, 1); + int dia_size = enc->iterative_dia_size ? enc->iterative_dia_size : FFMAX(s->avctx->dia_size, 1); dia_change=0; for(i=0; i < dia_size; i++){ for(j=0; jmx+square[i][0], block->my+square[i][1], obmc_edged, &best_rd); + dia_change |= check_block_inter(enc, mb_x, mb_y, block->mx+square[i][0], block->my+square[i][1], obmc_edged, &best_rd); }while(dia_change); //FIXME or try the standard 2 pass qpel or similar @@ -1170,7 +1342,7 @@ static void iterative_me(SnowContext *s){ } best_rd= ref_rd; *block= ref_b; - check_block_intra(s, mb_x, mb_y, color, obmc_edged, &best_rd); + check_block_intra(enc, mb_x, mb_y, color, obmc_edged, &best_rd); //FIXME RD style color selection if(!same_block(block, &backup)){ if(tb ) tb ->type &= ~BLOCK_OPT; @@ -1208,20 +1380,20 @@ static void iterative_me(SnowContext *s){ same_block(b[0], b[3])) continue; - if(!s->me_cache_generation) - memset(s->me_cache, 0, sizeof(s->me_cache)); - s->me_cache_generation += 1<<22; + if (!enc->me_cache_generation) + memset(enc->me_cache, 0, sizeof(enc->me_cache)); + enc->me_cache_generation += 1<<22; - init_rd= best_rd= get_4block_rd(s, mb_x, mb_y, 0); + init_rd = best_rd = get_4block_rd(enc, mb_x, mb_y, 0); //FIXME more multiref search? - check_4block_inter(s, mb_x, mb_y, + check_4block_inter(enc, mb_x, mb_y, (b[0]->mx + b[1]->mx + b[2]->mx + b[3]->mx + 2) >> 2, (b[0]->my + b[1]->my + b[2]->my + b[3]->my + 2) >> 2, 0, &best_rd); for(i=0; i<4; i++) if(!(b[i]->type&BLOCK_INTRA)) - check_4block_inter(s, mb_x, mb_y, b[i]->mx, b[i]->my, b[i]->ref, &best_rd); + check_4block_inter(enc, mb_x, mb_y, b[i]->mx, b[i]->my, b[i]->ref, &best_rd); if(init_rd != best_rd) change++; @@ -1231,13 +1403,15 @@ static void iterative_me(SnowContext *s){ } } -static void encode_blocks(SnowContext *s, int search){ +static void encode_blocks(SnowEncContext *enc, int search) +{ + SnowContext *const s = &enc->com; int x, y; int w= s->b_width; int h= s->b_height; - if(s->motion_est == FF_ME_ITER && !s->keyframe && search) - iterative_me(s); + if (enc->motion_est == FF_ME_ITER && !s->keyframe && search) + iterative_me(enc); for(y=0; yc.bytestream_end - s->c.bytestream < w*MB_SIZE*MB_SIZE*3){ //FIXME nicer limit @@ -1245,10 +1419,10 @@ static void encode_blocks(SnowContext *s, int search){ return; } for(x=0; xmotion_est == FF_ME_ITER || !search) + if (enc->motion_est == FF_ME_ITER || !search) encode_q_branch2(s, 0, x, y); else - encode_q_branch (s, 0, x, y); + encode_q_branch (enc, 0, x, y); } } } @@ -1495,8 +1669,9 @@ static int qscale2qlog(int qscale){ + 61*QROOT/8; ///< 64 > 60 } -static int ratecontrol_1pass(SnowContext *s, AVFrame *pict) +static int ratecontrol_1pass(SnowEncContext *enc, AVFrame *pict) { + SnowContext *const s = &enc->com; /* Estimate the frame's complexity as a sum of weighted dwt coefficients. * FIXME we know exact mv bits at this point, * but ratecontrol isn't set up to include them. */ @@ -1531,17 +1706,17 @@ static int ratecontrol_1pass(SnowContext *s, AVFrame *pict) coef_sum = (uint64_t)coef_sum * coef_sum >> 16; if(pict->pict_type == AV_PICTURE_TYPE_I){ - s->m.mb_var_sum = coef_sum; - s->m.mc_mb_var_sum = 0; + enc->m.mb_var_sum = coef_sum; + enc->m.mc_mb_var_sum = 0; }else{ - s->m.mc_mb_var_sum = coef_sum; - s->m.mb_var_sum = 0; + enc->m.mc_mb_var_sum = coef_sum; + enc->m.mb_var_sum = 0; } - pict->quality= ff_rate_estimate_qscale(&s->m, 1); + pict->quality= ff_rate_estimate_qscale(&enc->m, 1); if (pict->quality < 0) return INT_MIN; - s->lambda= pict->quality * 3/2; + enc->lambda= pict->quality * 3/2; delta_qlog= qscale2qlog(pict->quality) - s->qlog; s->qlog+= delta_qlog; return delta_qlog; @@ -1580,8 +1755,11 @@ static void calculate_visual_weight(SnowContext *s, Plane *p){ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, const AVFrame *pict, int *got_packet) { - SnowContext *s = avctx->priv_data; + SnowEncContext *const enc = avctx->priv_data; + SnowContext *const s = &enc->com; + MpegEncContext *const mpv = &enc->m; RangeCoder * const c= &s->c; + AVCodecInternal *avci = avctx->internal; AVFrame *pic; const int width= s->avctx->width; const int height= s->avctx->height; @@ -1589,7 +1767,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, uint8_t rc_header_bak[sizeof(s->header_state)]; uint8_t rc_block_bak[sizeof(s->block_state)]; - if ((ret = ff_alloc_packet(avctx, pkt, s->b_width*s->b_height*MB_SIZE*MB_SIZE*3 + AV_INPUT_BUFFER_MIN_SIZE)) < 0) + if ((ret = ff_alloc_packet(avctx, pkt, s->b_width*s->b_height*MB_SIZE*MB_SIZE*3 + FF_INPUT_BUFFER_MIN_SIZE)) < 0) return ret; ff_init_range_encoder(c, pkt->data, pkt->size); @@ -1602,7 +1780,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, memcpy(&s->input_picture->data[i][y * s->input_picture->linesize[i]], &pict->data[i][y * pict->linesize[i]], AV_CEIL_RSHIFT(width, hshift)); - s->mpvencdsp.draw_edges(s->input_picture->data[i], s->input_picture->linesize[i], + enc->mpvencdsp.draw_edges(s->input_picture->data[i], s->input_picture->linesize[i], AV_CEIL_RSHIFT(width, hshift), AV_CEIL_RSHIFT(height, vshift), EDGE_WIDTH >> hshift, EDGE_WIDTH >> vshift, EDGE_TOP | EDGE_BOTTOM); @@ -1613,54 +1791,57 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, pic->pict_type = pict->pict_type; pic->quality = pict->quality; - s->m.picture_number= avctx->frame_num; + mpv->picture_number = avctx->frame_num; if(avctx->flags&AV_CODEC_FLAG_PASS2){ - s->m.pict_type = pic->pict_type = s->m.rc_context.entry[avctx->frame_num].new_pict_type; + mpv->pict_type = pic->pict_type = mpv->rc_context.entry[avctx->frame_num].new_pict_type; s->keyframe = pic->pict_type == AV_PICTURE_TYPE_I; if(!(avctx->flags&AV_CODEC_FLAG_QSCALE)) { - pic->quality = ff_rate_estimate_qscale(&s->m, 0); + pic->quality = ff_rate_estimate_qscale(mpv, 0); if (pic->quality < 0) return -1; } }else{ s->keyframe= avctx->gop_size==0 || avctx->frame_num % avctx->gop_size == 0; - s->m.pict_type = pic->pict_type = s->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + mpv->pict_type = pic->pict_type = s->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; } - if(s->pass1_rc && avctx->frame_num == 0) + if (enc->pass1_rc && avctx->frame_num == 0) pic->quality = 2*FF_QP2LAMBDA; if (pic->quality) { s->qlog = qscale2qlog(pic->quality); - s->lambda = pic->quality * 3/2; + enc->lambda = pic->quality * 3/2; } if (s->qlog < 0 || (!pic->quality && (avctx->flags & AV_CODEC_FLAG_QSCALE))) { s->qlog= LOSSLESS_QLOG; - s->lambda = 0; + enc->lambda = 0; }//else keep previous frame's qlog until after motion estimation if (s->current_picture->data[0]) { int w = s->avctx->width; int h = s->avctx->height; - s->mpvencdsp.draw_edges(s->current_picture->data[0], - s->current_picture->linesize[0], w , h , - EDGE_WIDTH , EDGE_WIDTH , EDGE_TOP | EDGE_BOTTOM); + enc->mpvencdsp.draw_edges(s->current_picture->data[0], + s->current_picture->linesize[0], w , h , + EDGE_WIDTH , EDGE_WIDTH , EDGE_TOP | EDGE_BOTTOM); if (s->current_picture->data[2]) { - s->mpvencdsp.draw_edges(s->current_picture->data[1], - s->current_picture->linesize[1], w>>s->chroma_h_shift, h>>s->chroma_v_shift, - EDGE_WIDTH>>s->chroma_h_shift, EDGE_WIDTH>>s->chroma_v_shift, EDGE_TOP | EDGE_BOTTOM); - s->mpvencdsp.draw_edges(s->current_picture->data[2], - s->current_picture->linesize[2], w>>s->chroma_h_shift, h>>s->chroma_v_shift, - EDGE_WIDTH>>s->chroma_h_shift, EDGE_WIDTH>>s->chroma_v_shift, EDGE_TOP | EDGE_BOTTOM); + enc->mpvencdsp.draw_edges(s->current_picture->data[1], + s->current_picture->linesize[1], w>>s->chroma_h_shift, h>>s->chroma_v_shift, + EDGE_WIDTH>>s->chroma_h_shift, EDGE_WIDTH>>s->chroma_v_shift, EDGE_TOP | EDGE_BOTTOM); + enc->mpvencdsp.draw_edges(s->current_picture->data[2], + s->current_picture->linesize[2], w>>s->chroma_h_shift, h>>s->chroma_v_shift, + EDGE_WIDTH>>s->chroma_h_shift, EDGE_WIDTH>>s->chroma_v_shift, EDGE_TOP | EDGE_BOTTOM); } emms_c(); } - ff_snow_frame_start(s); + ff_snow_frames_prepare(s); + ret = get_encode_buffer(s, s->current_picture); + if (ret < 0) + return ret; - s->m.current_picture_ptr= &s->m.current_picture; - s->m.current_picture.f = s->current_picture; - s->m.current_picture.f->pts = pict->pts; + mpv->current_picture_ptr = &mpv->current_picture; + mpv->current_picture.f = s->current_picture; + mpv->current_picture.f->pts = pict->pts; if(pic->pict_type == AV_PICTURE_TYPE_P){ int block_width = (width +15)>>4; int block_height= (height+15)>>4; @@ -1669,40 +1850,40 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, av_assert0(s->current_picture->data[0]); av_assert0(s->last_picture[0]->data[0]); - s->m.avctx= s->avctx; - s->m. last_picture.f = s->last_picture[0]; - s->m. new_picture = s->input_picture; - s->m. last_picture_ptr= &s->m. last_picture; - s->m.linesize = stride; - s->m.uvlinesize= s->current_picture->linesize[1]; - s->m.width = width; - s->m.height= height; - s->m.mb_width = block_width; - s->m.mb_height= block_height; - s->m.mb_stride= s->m.mb_width+1; - s->m.b8_stride= 2*s->m.mb_width+1; - s->m.f_code=1; - s->m.pict_type = pic->pict_type; - s->m.motion_est= s->motion_est; - s->m.me.scene_change_score=0; - s->m.me.dia_size = avctx->dia_size; - s->m.quarter_sample= (s->avctx->flags & AV_CODEC_FLAG_QPEL)!=0; - s->m.out_format= FMT_H263; - s->m.unrestricted_mv= 1; - - s->m.lambda = s->lambda; - s->m.qscale= (s->m.lambda*139 + FF_LAMBDA_SCALE*64) >> (FF_LAMBDA_SHIFT + 7); - s->lambda2= s->m.lambda2= (s->m.lambda*s->m.lambda + FF_LAMBDA_SCALE/2) >> FF_LAMBDA_SHIFT; - - s->m.mecc= s->mecc; //move - s->m.qdsp= s->qdsp; //move - s->m.hdsp = s->hdsp; - ff_init_me(&s->m); - s->hdsp = s->m.hdsp; - s->mecc= s->m.mecc; - } - - if(s->pass1_rc){ + mpv->avctx = s->avctx; + mpv->last_picture.f = s->last_picture[0]; + mpv-> new_picture = s->input_picture; + mpv->last_picture_ptr = &mpv->last_picture; + mpv->linesize = stride; + mpv->uvlinesize = s->current_picture->linesize[1]; + mpv->width = width; + mpv->height = height; + mpv->mb_width = block_width; + mpv->mb_height = block_height; + mpv->mb_stride = mpv->mb_width + 1; + mpv->b8_stride = 2 * mpv->mb_width + 1; + mpv->f_code = 1; + mpv->pict_type = pic->pict_type; + mpv->motion_est = enc->motion_est; + mpv->me.scene_change_score = 0; + mpv->me.dia_size = avctx->dia_size; + mpv->quarter_sample = (s->avctx->flags & AV_CODEC_FLAG_QPEL)!=0; + mpv->out_format = FMT_H263; + mpv->unrestricted_mv = 1; + + mpv->lambda = enc->lambda; + mpv->qscale = (mpv->lambda*139 + FF_LAMBDA_SCALE*64) >> (FF_LAMBDA_SHIFT + 7); + enc->lambda2 = mpv->lambda2 = (mpv->lambda*mpv->lambda + FF_LAMBDA_SCALE/2) >> FF_LAMBDA_SHIFT; + + mpv->mecc = enc->mecc; //move + mpv->qdsp = enc->qdsp; //move + mpv->hdsp = s->hdsp; + ff_init_me(&enc->m); + s->hdsp = mpv->hdsp; + enc->mecc = mpv->mecc; + } + + if (enc->pass1_rc) { memcpy(rc_header_bak, s->header_state, sizeof(s->header_state)); memcpy(rc_block_bak, s->block_state, sizeof(s->block_state)); } @@ -1720,7 +1901,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, return AVERROR(EINVAL); } - s->m.pict_type = pic->pict_type; + mpv->pict_type = pic->pict_type; s->qbias = pic->pict_type == AV_PICTURE_TYPE_P ? 2 : 0; ff_snow_common_init_after_header(avctx); @@ -1732,9 +1913,9 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, } encode_header(s); - s->m.misc_bits = 8*(s->c.bytestream - s->c.bytestream_start); - encode_blocks(s, 1); - s->m.mv_bits = 8*(s->c.bytestream - s->c.bytestream_start) - s->m.misc_bits; + mpv->misc_bits = 8 * (s->c.bytestream - s->c.bytestream_start); + encode_blocks(enc, 1); + mpv->mv_bits = 8 * (s->c.bytestream - s->c.bytestream_start) - mpv->misc_bits; for(plane_index=0; plane_index < s->nb_planes; plane_index++){ Plane *p= &s->plane[plane_index]; @@ -1743,7 +1924,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, int x, y; // int bits= put_bits_count(&s->c.pb); - if (!s->memc_only) { + if (!enc->memc_only) { //FIXME optimize if(pict->data[plane_index]) //FIXME gray hack for(y=0; ypict_type == AV_PICTURE_TYPE_P && !(avctx->flags&AV_CODEC_FLAG_PASS2) - && s->m.me.scene_change_score > s->scenechange_threshold){ + && mpv->me.scene_change_score > enc->scenechange_threshold) { ff_init_range_encoder(c, pkt->data, pkt->size); ff_build_rac_states(c, (1LL<<32)/20, 256-8); pic->pict_type= AV_PICTURE_TYPE_I; s->keyframe=1; - s->current_picture->key_frame=1; + s->current_picture->flags |= AV_FRAME_FLAG_KEY; goto redo_frame; } @@ -1781,8 +1962,8 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, ff_spatial_dwt(s->spatial_dwt_buffer, s->temp_dwt_buffer, w, h, w, s->spatial_decomposition_type, s->spatial_decomposition_count); - if(s->pass1_rc && plane_index==0){ - int delta_qlog = ratecontrol_1pass(s, pic); + if (enc->pass1_rc && plane_index==0) { + int delta_qlog = ratecontrol_1pass(enc, pic); if (delta_qlog <= INT_MIN) return -1; if(delta_qlog){ @@ -1791,7 +1972,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, memcpy(s->header_state, rc_header_bak, sizeof(s->header_state)); memcpy(s->block_state, rc_block_bak, sizeof(s->block_state)); encode_header(s); - encode_blocks(s, 0); + encode_blocks(enc, 0); } } @@ -1802,7 +1983,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, quantize(s, b, b->ibuf, b->buf, b->stride, s->qbias); if(orientation==0) decorrelate(s, b, b->ibuf, b->stride, pic->pict_type == AV_PICTURE_TYPE_P, 0); - if (!s->no_bitstream) + if (!enc->no_bitstream) encode_subband(s, b, b->ibuf, b->parent ? b->parent->ibuf : NULL, b->stride, orientation); av_assert0(b->parent==NULL || b->parent->stride == b->stride*2); if(orientation==0) @@ -1852,7 +2033,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, } } s->avctx->error[plane_index] += error; - s->encoding_error[plane_index] = error; + enc->encoding_error[plane_index] = error; } } @@ -1864,28 +2045,31 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, s->current_picture->pict_type = pic->pict_type; s->current_picture->quality = pic->quality; - s->m.frame_bits = 8*(s->c.bytestream - s->c.bytestream_start); - s->m.p_tex_bits = s->m.frame_bits - s->m.misc_bits - s->m.mv_bits; - s->m.current_picture.display_picture_number = - s->m.current_picture.coded_picture_number = avctx->frame_num; - s->m.current_picture.f->quality = pic->quality; - s->m.total_bits += 8*(s->c.bytestream - s->c.bytestream_start); - if(s->pass1_rc) - if (ff_rate_estimate_qscale(&s->m, 0) < 0) + mpv->frame_bits = 8 * (s->c.bytestream - s->c.bytestream_start); + mpv->p_tex_bits = mpv->frame_bits - mpv->misc_bits - mpv->mv_bits; + mpv->total_bits += 8*(s->c.bytestream - s->c.bytestream_start); + mpv->current_picture.display_picture_number = + mpv->current_picture.coded_picture_number = avctx->frame_num; + mpv->current_picture.f->quality = pic->quality; + if (enc->pass1_rc) + if (ff_rate_estimate_qscale(mpv, 0) < 0) return -1; if(avctx->flags&AV_CODEC_FLAG_PASS1) - ff_write_pass1_stats(&s->m); - s->m.last_pict_type = s->m.pict_type; + ff_write_pass1_stats(mpv); + mpv->last_pict_type = mpv->pict_type; emms_c(); ff_side_data_set_encoder_stats(pkt, s->current_picture->quality, - s->encoding_error, + enc->encoding_error, (s->avctx->flags&AV_CODEC_FLAG_PSNR) ? SNOW_MAX_PLANES : 0, s->current_picture->pict_type); + if (s->avctx->flags & AV_CODEC_FLAG_RECON_FRAME) { + av_frame_replace(avci->recon_frame, s->current_picture); + } pkt->size = ff_rac_terminate(c, 0); - if (s->current_picture->key_frame) + if (s->current_picture->flags & AV_FRAME_FLAG_KEY) pkt->flags |= AV_PKT_FLAG_KEY; *got_packet = 1; @@ -1894,32 +2078,44 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, static av_cold int encode_end(AVCodecContext *avctx) { - SnowContext *s = avctx->priv_data; + SnowEncContext *const enc = avctx->priv_data; + SnowContext *const s = &enc->com; ff_snow_common_end(s); - ff_rate_control_uninit(&s->m); + ff_rate_control_uninit(&enc->m); av_frame_free(&s->input_picture); + + for (int i = 0; i < MAX_REF_FRAMES; i++) { + av_freep(&s->ref_mvs[i]); + av_freep(&s->ref_scores[i]); + } + + enc->m.me.temp = NULL; + av_freep(&enc->m.me.scratchpad); + av_freep(&enc->m.me.map); + av_freep(&enc->m.sc.obmc_scratchpad); + av_freep(&avctx->stats_out); return 0; } -#define OFFSET(x) offsetof(SnowContext, x) +#define OFFSET(x) offsetof(SnowEncContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - {"motion_est", "motion estimation algorithm", OFFSET(motion_est), AV_OPT_TYPE_INT, {.i64 = FF_ME_EPZS }, FF_ME_ZERO, FF_ME_ITER, VE, "motion_est" }, - { "zero", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_ZERO }, 0, 0, VE, "motion_est" }, - { "epzs", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_EPZS }, 0, 0, VE, "motion_est" }, - { "xone", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_XONE }, 0, 0, VE, "motion_est" }, - { "iter", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_ITER }, 0, 0, VE, "motion_est" }, + {"motion_est", "motion estimation algorithm", OFFSET(motion_est), AV_OPT_TYPE_INT, {.i64 = FF_ME_EPZS }, FF_ME_ZERO, FF_ME_ITER, VE, .unit = "motion_est" }, + { "zero", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_ZERO }, 0, 0, VE, .unit = "motion_est" }, + { "epzs", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_EPZS }, 0, 0, VE, .unit = "motion_est" }, + { "xone", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_XONE }, 0, 0, VE, .unit = "motion_est" }, + { "iter", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_ITER }, 0, 0, VE, .unit = "motion_est" }, { "memc_only", "Only do ME/MC (I frames -> ref, P frame -> ME+MC).", OFFSET(memc_only), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "no_bitstream", "Skip final bitstream writeout.", OFFSET(no_bitstream), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, { "intra_penalty", "Penalty for intra blocks in block decission", OFFSET(intra_penalty), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, { "iterative_dia_size", "Dia size for the iterative ME", OFFSET(iterative_dia_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, { "sc_threshold", "Scene change threshold", OFFSET(scenechange_threshold), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, VE }, - { "pred", "Spatial decomposition type", OFFSET(pred), AV_OPT_TYPE_INT, { .i64 = 0 }, DWT_97, DWT_53, VE, "pred" }, - { "dwt97", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, "pred" }, - { "dwt53", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, "pred" }, + { "pred", "Spatial decomposition type", OFFSET(pred), AV_OPT_TYPE_INT, { .i64 = 0 }, DWT_97, DWT_53, VE, .unit = "pred" }, + { "dwt97", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "dwt53", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "pred" }, { "rc_eq", "Set rate control equation. When computing the expression, besides the standard functions " "defined in the section 'Expression Evaluation', the following functions are available: " "bits2qp(bits), qp2bits(qp). Also the following constants are available: iTex pTex tex mv " @@ -1940,8 +2136,10 @@ const FFCodec ff_snow_encoder = { CODEC_LONG_NAME("Snow"), .p.type = AVMEDIA_TYPE_VIDEO, .p.id = AV_CODEC_ID_SNOW, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, - .priv_data_size = sizeof(SnowContext), + .p.capabilities = AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE | + AV_CODEC_CAP_ENCODER_RECON_FRAME, + .priv_data_size = sizeof(SnowEncContext), .init = encode_init, FF_CODEC_ENCODE_CB(encode_frame), .close = encode_end, diff --git a/libavcodec/speedhqdec.c b/libavcodec/speedhqdec.c index e1e6f9a5f54..d3605b06492 100644 --- a/libavcodec/speedhqdec.c +++ b/libavcodec/speedhqdec.c @@ -71,10 +71,10 @@ static const uint8_t unscaled_quant_matrix[64] = { 27, 29, 35, 38, 46, 56, 69, 83 }; -static VLC dc_lum_vlc_le; -static VLC dc_chroma_vlc_le; -static VLC dc_alpha_run_vlc_le; -static VLC dc_alpha_level_vlc_le; +static VLCElem dc_lum_vlc_le[512]; +static VLCElem dc_chroma_vlc_le[514]; +static VLCElem dc_alpha_run_vlc_le[160]; +static VLCElem dc_alpha_level_vlc_le[288]; static RL_VLC_ELEM speedhq_rl_vlc[674]; @@ -83,9 +83,9 @@ static inline int decode_dc_le(GetBitContext *gb, int component) int code, diff; if (component == 0 || component == 3) { - code = get_vlc2(gb, dc_lum_vlc_le.table, DC_VLC_BITS, 2); + code = get_vlc2(gb, dc_lum_vlc_le, DC_VLC_BITS, 2); } else { - code = get_vlc2(gb, dc_chroma_vlc_le.table, DC_VLC_BITS, 2); + code = get_vlc2(gb, dc_chroma_vlc_le, DC_VLC_BITS, 2); } if (!code) { diff = 0; @@ -109,7 +109,7 @@ static inline int decode_alpha_block(const SHQContext *s, GetBitContext *gb, uin int run, level; UPDATE_CACHE_LE(re, gb); - GET_VLC(run, re, gb, dc_alpha_run_vlc_le.table, ALPHA_VLC_BITS, 2); + GET_VLC(run, re, gb, dc_alpha_run_vlc_le, ALPHA_VLC_BITS, 2); if (run < 0) break; i += run; @@ -117,7 +117,7 @@ static inline int decode_alpha_block(const SHQContext *s, GetBitContext *gb, uin return AVERROR_INVALIDDATA; UPDATE_CACHE_LE(re, gb); - GET_VLC(level, re, gb, dc_alpha_level_vlc_le.table, ALPHA_VLC_BITS, 2); + GET_VLC(level, re, gb, dc_alpha_level_vlc_le, ALPHA_VLC_BITS, 2); block[i++] = level; } @@ -436,7 +436,7 @@ static int speedhq_decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) { return ret; } - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; if (second_field_offset == 4 || second_field_offset == (buf_size-4)) { /* @@ -506,11 +506,11 @@ static av_cold void compute_alpha_vlcs(void) av_assert0(entry == FF_ARRAY_ELEMS(run_code)); - INIT_LE_VLC_SPARSE_STATIC(&dc_alpha_run_vlc_le, ALPHA_VLC_BITS, - FF_ARRAY_ELEMS(run_code), - run_bits, 1, 1, - run_code, 2, 2, - run_symbols, 2, 2, 160); + VLC_INIT_STATIC_SPARSE_TABLE(dc_alpha_run_vlc_le, ALPHA_VLC_BITS, + FF_ARRAY_ELEMS(run_code), + run_bits, 1, 1, + run_code, 2, 2, + run_symbols, 2, 2, VLC_INIT_LE); /* Initialize VLC for alpha level. */ entry = 0; @@ -546,28 +546,28 @@ static av_cold void compute_alpha_vlcs(void) av_assert0(entry == FF_ARRAY_ELEMS(level_code)); - INIT_LE_VLC_SPARSE_STATIC(&dc_alpha_level_vlc_le, ALPHA_VLC_BITS, - FF_ARRAY_ELEMS(level_code), - level_bits, 1, 1, - level_code, 2, 2, - level_symbols, 2, 2, 288); + VLC_INIT_STATIC_SPARSE_TABLE(dc_alpha_level_vlc_le, ALPHA_VLC_BITS, + FF_ARRAY_ELEMS(level_code), + level_bits, 1, 1, + level_code, 2, 2, + level_symbols, 2, 2, VLC_INIT_LE); } static av_cold void speedhq_static_init(void) { /* Exactly the same as MPEG-2, except for a little-endian reader. */ - INIT_CUSTOM_VLC_STATIC(&dc_lum_vlc_le, DC_VLC_BITS, 12, - ff_mpeg12_vlc_dc_lum_bits, 1, 1, - ff_mpeg12_vlc_dc_lum_code, 2, 2, - INIT_VLC_OUTPUT_LE, 512); - INIT_CUSTOM_VLC_STATIC(&dc_chroma_vlc_le, DC_VLC_BITS, 12, - ff_mpeg12_vlc_dc_chroma_bits, 1, 1, - ff_mpeg12_vlc_dc_chroma_code, 2, 2, - INIT_VLC_OUTPUT_LE, 514); + VLC_INIT_STATIC_TABLE(dc_lum_vlc_le, DC_VLC_BITS, 12, + ff_mpeg12_vlc_dc_lum_bits, 1, 1, + ff_mpeg12_vlc_dc_lum_code, 2, 2, + VLC_INIT_OUTPUT_LE); + VLC_INIT_STATIC_TABLE(dc_chroma_vlc_le, DC_VLC_BITS, 12, + ff_mpeg12_vlc_dc_chroma_bits, 1, 1, + ff_mpeg12_vlc_dc_chroma_code, 2, 2, + VLC_INIT_OUTPUT_LE); ff_init_2d_vlc_rl(ff_speedhq_vlc_table, speedhq_rl_vlc, ff_speedhq_run, ff_speedhq_level, SPEEDHQ_RL_NB_ELEMS, - FF_ARRAY_ELEMS(speedhq_rl_vlc), INIT_VLC_LE); + FF_ARRAY_ELEMS(speedhq_rl_vlc), VLC_INIT_LE); compute_alpha_vlcs(); } diff --git a/libavcodec/speexdec.c b/libavcodec/speexdec.c index 08c7e77e7d5..ba0df687de3 100644 --- a/libavcodec/speexdec.c +++ b/libavcodec/speexdec.c @@ -52,6 +52,7 @@ */ #include "libavutil/avassert.h" +#include "libavutil/avstring.h" #include "libavutil/float_dsp.h" #include "avcodec.h" #include "bytestream.h" @@ -1397,9 +1398,9 @@ static int parse_speex_extradata(AVCodecContext *avctx, const uint8_t *extradata, int extradata_size) { SpeexContext *s = avctx->priv_data; - const uint8_t *buf = extradata; + const uint8_t *buf = av_strnstr(extradata, "Speex ", extradata_size); - if (memcmp(buf, "Speex ", 8)) + if (!buf) return AVERROR_INVALIDDATA; buf += 28; @@ -1420,8 +1421,10 @@ static int parse_speex_extradata(AVCodecContext *avctx, return AVERROR_INVALIDDATA; s->bitrate = bytestream_get_le32(&buf); s->frame_size = bytestream_get_le32(&buf); - if (s->frame_size < NB_FRAME_SIZE << s->mode) + if (s->frame_size < NB_FRAME_SIZE << (s->mode > 0) || + s->frame_size > INT32_MAX >> (s->mode > 0)) return AVERROR_INVALIDDATA; + s->frame_size <<= (s->mode > 0); s->vbr = bytestream_get_le32(&buf); s->frames_per_packet = bytestream_get_le32(&buf); if (s->frames_per_packet <= 0 || diff --git a/libavcodec/srtenc.c b/libavcodec/srtenc.c index 7e36a2200c2..a607beb9900 100644 --- a/libavcodec/srtenc.c +++ b/libavcodec/srtenc.c @@ -23,7 +23,6 @@ #include #include "avcodec.h" -#include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "ass_split.h" #include "ass.h" @@ -42,10 +41,7 @@ typedef struct { } SRTContext; -#ifdef __GNUC__ -__attribute__ ((__format__ (__printf__, 2, 3))) -#endif -static void srt_print(SRTContext *s, const char *str, ...) +static av_printf_format(2, 3) void srt_print(SRTContext *s, const char *str, ...) { va_list vargs; va_start(vargs, str); @@ -138,7 +134,6 @@ static av_cold int srt_encode_init(AVCodecContext *avctx) SRTContext *s = avctx->priv_data; s->avctx = avctx; s->ass_ctx = ff_ass_split(avctx->subtitle_header); - av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; } @@ -237,7 +232,7 @@ static int encode_frame(AVCodecContext *avctx, ASSDialog *dialog; int i; - av_bprint_clear(&s->buffer); + av_bprint_init_for_buffer(&s->buffer, buf, bufsize); for (i=0; inum_rects; i++) { const char *ass = sub->rects[i]->ass; @@ -257,16 +252,13 @@ static int encode_frame(AVCodecContext *avctx, ff_ass_free_dialog(&dialog); } - if (!av_bprint_is_complete(&s->buffer)) - return AVERROR(ENOMEM); if (!s->buffer.len) return 0; - if (s->buffer.len > bufsize) { + if (!av_bprint_is_complete(&s->buffer)) { av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); return AVERROR_BUFFER_TOO_SMALL; } - memcpy(buf, s->buffer.str, s->buffer.len); return s->buffer.len; } @@ -287,7 +279,6 @@ static int srt_encode_close(AVCodecContext *avctx) { SRTContext *s = avctx->priv_data; ff_ass_split_free(s->ass_ctx); - av_bprint_finalize(&s->buffer, NULL); return 0; } diff --git a/libavcodec/svq1dec.c b/libavcodec/svq1dec.c index c7269456e2f..d2cfe4cf400 100644 --- a/libavcodec/svq1dec.c +++ b/libavcodec/svq1dec.c @@ -45,12 +45,12 @@ #include "svq1.h" #define SVQ1_BLOCK_TYPE_VLC_BITS 3 -static VLC svq1_block_type; -static VLC svq1_motion_component; -static VLC svq1_intra_multistage[6]; -static VLC svq1_inter_multistage[6]; -static VLC svq1_intra_mean; -static VLC svq1_inter_mean; +static VLCElem svq1_block_type[8]; +static VLCElem svq1_motion_component[176]; +static const VLCElem *svq1_intra_multistage[6]; +static const VLCElem *svq1_inter_multistage[6]; +static VLCElem svq1_intra_mean[632]; +static VLCElem svq1_inter_mean[1434]; /* motion vector (prediction) */ typedef struct svq1_pmv_s { @@ -190,7 +190,7 @@ static int svq1_decode_block_intra(GetBitContext *bitbuf, uint8_t *pixels, height = 1 << ((3 + level) / 2); /* get number of stages (-1 skips vector, 0 for mean only) */ - stages = get_vlc2(bitbuf, svq1_intra_multistage[level].table, 3, 3) - 1; + stages = get_vlc2(bitbuf, svq1_intra_multistage[level], 4, 2) - 1; if (stages == -1) { for (y = 0; y < height; y++) @@ -206,7 +206,7 @@ static int svq1_decode_block_intra(GetBitContext *bitbuf, uint8_t *pixels, } av_assert0(stages >= 0); - mean = get_vlc2(bitbuf, svq1_intra_mean.table, 8, 3); + mean = get_vlc2(bitbuf, svq1_intra_mean, 8, 3); if (stages == 0) { for (y = 0; y < height; y++) @@ -257,7 +257,7 @@ static int svq1_decode_block_non_intra(GetBitContext *bitbuf, uint8_t *pixels, height = 1 << ((3 + level) / 2); /* get number of stages (-1 skips vector, 0 for mean only) */ - stages = get_vlc2(bitbuf, svq1_inter_multistage[level].table, 3, 2) - 1; + stages = get_vlc2(bitbuf, svq1_inter_multistage[level], 3, 2) - 1; if (stages == -1) continue; /* skip vector */ @@ -270,7 +270,7 @@ static int svq1_decode_block_non_intra(GetBitContext *bitbuf, uint8_t *pixels, } av_assert0(stages >= 0); - mean = get_vlc2(bitbuf, svq1_inter_mean.table, 9, 3) - 256; + mean = get_vlc2(bitbuf, svq1_inter_mean, 9, 3) - 256; if (buggy) { if (mean == -128) @@ -282,7 +282,7 @@ static int svq1_decode_block_non_intra(GetBitContext *bitbuf, uint8_t *pixels, SVQ1_CALC_CODEBOOK_ENTRIES(ff_svq1_inter_codebooks); for (y = 0; y < height; y++) { - for (x = 0; x < width / 4; x++, codebook++) { + for (x = 0; x < width / 4; x++) { n3 = dst[x]; /* add mean value to vector */ n1 = n4 + ((n3 & 0xFF00FF00) >> 8); @@ -290,6 +290,8 @@ static int svq1_decode_block_non_intra(GetBitContext *bitbuf, uint8_t *pixels, SVQ1_ADD_CODEBOOK() /* store result */ dst[x] = n1 << 8 | n2; + if (codebook != NULL) + codebook++; } dst += pitch / 4; } @@ -305,7 +307,7 @@ static int svq1_decode_motion_vector(GetBitContext *bitbuf, svq1_pmv *mv, for (i = 0; i < 2; i++) { /* get motion code */ - diff = get_vlc2(bitbuf, svq1_motion_component.table, 7, 2); + diff = get_vlc2(bitbuf, svq1_motion_component, 7, 2); if (diff < 0) return AVERROR_INVALIDDATA; else if (diff) { @@ -470,7 +472,7 @@ static int svq1_decode_delta_block(AVCodecContext *avctx, HpelDSPContext *hdsp, int result = 0; /* get block type */ - block_type = get_vlc2(bitbuf, svq1_block_type.table, + block_type = get_vlc2(bitbuf, svq1_block_type, SVQ1_BLOCK_TYPE_VLC_BITS, 1); /* reset motion vectors */ @@ -764,8 +766,7 @@ static int svq1_decode_frame(AVCodecContext *avctx, AVFrame *cur, } if (!s->nonref) { - av_frame_unref(s->prev); - result = av_frame_ref(s->prev, cur); + result = av_frame_replace(s->prev, cur); if (result < 0) return result; } @@ -778,41 +779,35 @@ static int svq1_decode_frame(AVCodecContext *avctx, AVFrame *cur, static av_cold void svq1_static_init(void) { - INIT_VLC_STATIC(&svq1_block_type, SVQ1_BLOCK_TYPE_VLC_BITS, 4, - &ff_svq1_block_type_vlc[0][1], 2, 1, - &ff_svq1_block_type_vlc[0][0], 2, 1, 8); - - INIT_VLC_STATIC(&svq1_motion_component, 7, 33, - &ff_mvtab[0][1], 2, 1, - &ff_mvtab[0][0], 2, 1, 176); - - for (int i = 0, offset = 0; i < 6; i++) { - static const uint8_t sizes[2][6] = { { 14, 10, 14, 18, 16, 18 }, - { 10, 10, 14, 14, 14, 16 } }; - static VLCElem table[168]; - svq1_intra_multistage[i].table = &table[offset]; - svq1_intra_multistage[i].table_allocated = sizes[0][i]; - offset += sizes[0][i]; - init_vlc(&svq1_intra_multistage[i], 3, 8, - &ff_svq1_intra_multistage_vlc[i][0][1], 2, 1, - &ff_svq1_intra_multistage_vlc[i][0][0], 2, 1, - INIT_VLC_USE_NEW_STATIC); - svq1_inter_multistage[i].table = &table[offset]; - svq1_inter_multistage[i].table_allocated = sizes[1][i]; - offset += sizes[1][i]; - init_vlc(&svq1_inter_multistage[i], 3, 8, - &ff_svq1_inter_multistage_vlc[i][0][1], 2, 1, - &ff_svq1_inter_multistage_vlc[i][0][0], 2, 1, - INIT_VLC_USE_NEW_STATIC); + static VLCElem table[196]; + VLCInitState state = VLC_INIT_STATE(table); + + VLC_INIT_STATIC_TABLE(svq1_block_type, SVQ1_BLOCK_TYPE_VLC_BITS, 4, + &ff_svq1_block_type_vlc[0][1], 2, 1, + &ff_svq1_block_type_vlc[0][0], 2, 1, 0); + + VLC_INIT_STATIC_TABLE(svq1_motion_component, 7, 33, + &ff_mvtab[0][1], 2, 1, + &ff_mvtab[0][0], 2, 1, 0); + + for (int i = 0; i < 6; i++) { + svq1_intra_multistage[i] = + ff_vlc_init_tables(&state, 4, 8, + &ff_svq1_intra_multistage_vlc[i][0][1], 2, 1, + &ff_svq1_intra_multistage_vlc[i][0][0], 2, 1, 0); + svq1_inter_multistage[i] = + ff_vlc_init_tables(&state, 3, 8, + &ff_svq1_inter_multistage_vlc[i][0][1], 2, 1, + &ff_svq1_inter_multistage_vlc[i][0][0], 2, 1, 0); } - INIT_VLC_STATIC(&svq1_intra_mean, 8, 256, - &ff_svq1_intra_mean_vlc[0][1], 4, 2, - &ff_svq1_intra_mean_vlc[0][0], 4, 2, 632); + VLC_INIT_STATIC_TABLE(svq1_intra_mean, 8, 256, + &ff_svq1_intra_mean_vlc[0][1], 4, 2, + &ff_svq1_intra_mean_vlc[0][0], 4, 2, 0); - INIT_VLC_STATIC(&svq1_inter_mean, 9, 512, - &ff_svq1_inter_mean_vlc[0][1], 4, 2, - &ff_svq1_inter_mean_vlc[0][0], 4, 2, 1434); + VLC_INIT_STATIC_TABLE(svq1_inter_mean, 9, 512, + &ff_svq1_inter_mean_vlc[0][1], 4, 2, + &ff_svq1_inter_mean_vlc[0][0], 4, 2, 0); } static av_cold int svq1_decode_init(AVCodecContext *avctx) @@ -868,6 +863,4 @@ const FFCodec ff_svq1_decoder = { FF_CODEC_DECODE_CB(svq1_decode_frame), .p.capabilities = AV_CODEC_CAP_DR1, .flush = svq1_flush, - .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV410P, - AV_PIX_FMT_NONE }, }; diff --git a/libavcodec/svq1enc.c b/libavcodec/svq1enc.c index 4651e01ae87..77dbf072751 100644 --- a/libavcodec/svq1enc.c +++ b/libavcodec/svq1enc.c @@ -26,6 +26,7 @@ * http://www.pcisys.net/~melanson/codecs/ */ +#include "libavutil/emms.h" #include "avcodec.h" #include "codec_internal.h" #include "encode.h" @@ -118,7 +119,7 @@ static void svq1_write_header(SVQ1EncContext *s, PutBitContext *pb, int frame_ty /* output 5 unknown bits (2 + 2 + 1) */ put_bits(pb, 5, 2); /* 2 needed by quicktime decoder */ - i = ff_match_2uint16((void*)ff_svq1_frame_size_table, + i = ff_match_2uint16(ff_svq1_frame_size_table, FF_ARRAY_ELEMS(ff_svq1_frame_size_table), s->frame_width, s->frame_height); put_bits(pb, 3, i); @@ -136,16 +137,6 @@ static void svq1_write_header(SVQ1EncContext *s, PutBitContext *pb, int frame_ty #define QUALITY_THRESHOLD 100 #define THRESHOLD_MULTIPLIER 0.6 -static int ssd_int8_vs_int16_c(const int8_t *pix1, const int16_t *pix2, - intptr_t size) -{ - int score = 0, i; - - for (i = 0; i < size; i++) - score += (pix1[i] - pix2[i]) * (pix1[i] - pix2[i]); - return score; -} - static int encode_block(SVQ1EncContext *s, uint8_t *src, uint8_t *ref, uint8_t *decoded, int stride, unsigned level, int threshold, int lambda, int intra) @@ -569,6 +560,7 @@ static av_cold int svq1_encode_end(AVCodecContext *avctx) av_frame_free(&s->current_picture); av_frame_free(&s->last_picture); + av_frame_free(&s->m.new_picture); return 0; } @@ -631,18 +623,14 @@ static av_cold int svq1_encode_init(AVCodecContext *avctx) s->dummy = av_mallocz((s->y_block_width + 1) * s->y_block_height * sizeof(int32_t)); s->m.me.map = av_mallocz(2 * ME_MAP_SIZE * sizeof(*s->m.me.map)); - s->svq1encdsp.ssd_int8_vs_int16 = ssd_int8_vs_int16_c; + s->m.new_picture = av_frame_alloc(); - if (!s->m.me.temp || !s->m.me.scratchpad || !s->m.me.map || - !s->mb_type || !s->dummy) + if (!s->m.me.scratchpad || !s->m.me.map || + !s->mb_type || !s->dummy || !s->m.new_picture) return AVERROR(ENOMEM); s->m.me.score_map = s->m.me.map + ME_MAP_SIZE; -#if ARCH_PPC - ff_svq1enc_init_ppc(&s->svq1encdsp); -#elif ARCH_X86 - ff_svq1enc_init_x86(&s->svq1encdsp); -#endif + ff_svq1enc_init(&s->svq1encdsp); ff_h263_encode_init(&s->m); // mv_penalty @@ -657,7 +645,7 @@ static int svq1_encode_frame(AVCodecContext *avctx, AVPacket *pkt, int i, ret; ret = ff_alloc_packet(avctx, pkt, s->y_block_width * s->y_block_height * - MAX_MB_BYTES * 3 + AV_INPUT_BUFFER_MIN_SIZE); + MAX_MB_BYTES * 3 + FF_INPUT_BUFFER_MIN_SIZE); if (ret < 0) return ret; @@ -732,10 +720,10 @@ static int svq1_encode_frame(AVCodecContext *avctx, AVPacket *pkt, #define OFFSET(x) offsetof(struct SVQ1EncContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "motion-est", "Motion estimation algorithm", OFFSET(motion_est), AV_OPT_TYPE_INT, { .i64 = FF_ME_EPZS }, FF_ME_ZERO, FF_ME_XONE, VE, "motion-est"}, - { "zero", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_ZERO }, 0, 0, FF_MPV_OPT_FLAGS, "motion-est" }, - { "epzs", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_EPZS }, 0, 0, FF_MPV_OPT_FLAGS, "motion-est" }, - { "xone", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_XONE }, 0, 0, FF_MPV_OPT_FLAGS, "motion-est" }, + { "motion-est", "Motion estimation algorithm", OFFSET(motion_est), AV_OPT_TYPE_INT, { .i64 = FF_ME_EPZS }, FF_ME_ZERO, FF_ME_XONE, VE, .unit = "motion-est"}, + { "zero", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_ZERO }, 0, 0, FF_MPV_OPT_FLAGS, .unit = "motion-est" }, + { "epzs", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_EPZS }, 0, 0, FF_MPV_OPT_FLAGS, .unit = "motion-est" }, + { "xone", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FF_ME_XONE }, 0, 0, FF_MPV_OPT_FLAGS, .unit = "motion-est" }, { NULL }, }; diff --git a/libavcodec/svq1encdsp.h b/libavcodec/svq1encdsp.h index 91b36735d7a..751b5eed865 100644 --- a/libavcodec/svq1encdsp.h +++ b/libavcodec/svq1encdsp.h @@ -23,12 +23,38 @@ #include +#include "config.h" + typedef struct SVQ1EncDSPContext { int (*ssd_int8_vs_int16)(const int8_t *pix1, const int16_t *pix2, intptr_t size); } SVQ1EncDSPContext; void ff_svq1enc_init_ppc(SVQ1EncDSPContext *c); +void ff_svq1enc_init_riscv(SVQ1EncDSPContext *c); void ff_svq1enc_init_x86(SVQ1EncDSPContext *c); +static int ssd_int8_vs_int16_c(const int8_t *pix1, const int16_t *pix2, + intptr_t size) +{ + int score = 0; + + for (intptr_t i = 0; i < size; i++) + score += (pix1[i] - pix2[i]) * (pix1[i] - pix2[i]); + return score; +} + +static inline void ff_svq1enc_init(SVQ1EncDSPContext *c) +{ + c->ssd_int8_vs_int16 = ssd_int8_vs_int16_c; + +#if ARCH_PPC + ff_svq1enc_init_ppc(c); +#elif ARCH_RISCV + ff_svq1enc_init_riscv(c); +#elif ARCH_X86 + ff_svq1enc_init_x86(c); +#endif +} + #endif /* AVCODEC_SVQ1ENCDSP_H */ diff --git a/libavcodec/svq3.c b/libavcodec/svq3.c index df514030b97..d2f53742e18 100644 --- a/libavcodec/svq3.c +++ b/libavcodec/svq3.c @@ -1408,7 +1408,10 @@ static int svq3_decode_frame(AVCodecContext *avctx, AVFrame *rframe, /* for skipping the frame */ s->cur_pic->f->pict_type = s->pict_type; - s->cur_pic->f->key_frame = (s->pict_type == AV_PICTURE_TYPE_I); + if (s->pict_type == AV_PICTURE_TYPE_I) + s->cur_pic->f->flags |= AV_FRAME_FLAG_KEY; + else + s->cur_pic->f->flags &= ~AV_FRAME_FLAG_KEY; ret = get_buffer(avctx, s->cur_pic); if (ret < 0) @@ -1599,7 +1602,5 @@ const FFCodec ff_svq3_decoder = { .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, - .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUVJ420P, - AV_PIX_FMT_NONE}, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, }; diff --git a/libavcodec/tak.c b/libavcodec/tak.c index 48fe83381f1..91feac5451d 100644 --- a/libavcodec/tak.c +++ b/libavcodec/tak.c @@ -92,10 +92,10 @@ int ff_tak_check_crc(const uint8_t *buf, unsigned int buf_size) return 0; } -void ff_tak_parse_streaminfo(TAKStreamInfo *s, GetBitContext *gb) +static int tak_parse_streaminfo(TAKStreamInfo *s, GetBitContext *gb) { uint64_t channel_mask = 0; - int frame_type, i; + int frame_type, i, ret; s->codec = get_bits(gb, TAK_ENCODER_CODEC_BITS); skip_bits(gb, TAK_ENCODER_PROFILE_BITS); @@ -124,7 +124,13 @@ void ff_tak_parse_streaminfo(TAKStreamInfo *s, GetBitContext *gb) } s->ch_layout = channel_mask; - s->frame_samples = tak_get_nb_samples(s->sample_rate, frame_type); + + ret = tak_get_nb_samples(s->sample_rate, frame_type); + if (ret < 0) + return ret; + s->frame_samples = ret; + + return 0; } int avpriv_tak_parse_streaminfo(TAKStreamInfo *s, const uint8_t *buf, int size) @@ -135,16 +141,14 @@ int avpriv_tak_parse_streaminfo(TAKStreamInfo *s, const uint8_t *buf, int size) if (ret < 0) return AVERROR_INVALIDDATA; - ff_tak_parse_streaminfo(s, &gb); - - return 0; + return tak_parse_streaminfo(s, &gb); } -int ff_tak_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, +int ff_tak_decode_frame_header(void *logctx, GetBitContext *gb, TAKStreamInfo *ti, int log_level_offset) { if (get_bits(gb, TAK_FRAME_HEADER_SYNC_ID_BITS) != TAK_FRAME_HEADER_SYNC_ID) { - av_log(avctx, AV_LOG_ERROR + log_level_offset, "missing sync id\n"); + av_log(logctx, AV_LOG_ERROR + log_level_offset, "missing sync id\n"); return AVERROR_INVALIDDATA; } @@ -159,7 +163,9 @@ int ff_tak_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, } if (ti->flags & TAK_FRAME_FLAG_HAS_INFO) { - ff_tak_parse_streaminfo(ti, gb); + int ret = tak_parse_streaminfo(ti, gb); + if (ret < 0) + return ret; if (get_bits(gb, 6)) skip_bits(gb, 25); diff --git a/libavcodec/tak.h b/libavcodec/tak.h index 60691189710..1d1ee645e83 100644 --- a/libavcodec/tak.h +++ b/libavcodec/tak.h @@ -29,7 +29,6 @@ #include -#include "avcodec.h" #include "get_bits.h" #define TAK_FORMAT_DATA_TYPE_BITS 3 @@ -149,17 +148,15 @@ int ff_tak_check_crc(const uint8_t *buf, unsigned int buf_size); */ int avpriv_tak_parse_streaminfo(TAKStreamInfo *s, const uint8_t *buf, int size); -void ff_tak_parse_streaminfo(TAKStreamInfo *s, GetBitContext *gb); - /** * Validate and decode a frame header. - * @param avctx AVCodecContext to use as av_log() context + * @param logctx for use as av_log() context * @param[in] gb GetBitContext from which to read frame header * @param[out] s frame information * @param log_level_offset log level offset, can be used to silence * error messages. * @return non-zero on error, 0 if OK */ -int ff_tak_decode_frame_header(AVCodecContext *avctx, GetBitContext *gb, +int ff_tak_decode_frame_header(void *logctx, GetBitContext *gb, TAKStreamInfo *s, int log_level_offset); #endif /* AVCODEC_TAK_H */ diff --git a/libavcodec/takdsp.c b/libavcodec/takdsp.c index b646a063db7..51b6658de48 100644 --- a/libavcodec/takdsp.c +++ b/libavcodec/takdsp.c @@ -23,7 +23,7 @@ #include "takdsp.h" #include "config.h" -static void decorrelate_ls(int32_t *p1, int32_t *p2, int length) +static void decorrelate_ls(const int32_t *p1, int32_t *p2, int length) { int i; @@ -34,7 +34,7 @@ static void decorrelate_ls(int32_t *p1, int32_t *p2, int length) } } -static void decorrelate_sr(int32_t *p1, int32_t *p2, int length) +static void decorrelate_sr(int32_t *p1, const int32_t *p2, int length) { int i; @@ -58,7 +58,7 @@ static void decorrelate_sm(int32_t *p1, int32_t *p2, int length) } } -static void decorrelate_sf(int32_t *p1, int32_t *p2, int length, int dshift, int dfactor) +static void decorrelate_sf(int32_t *p1, const int32_t *p2, int length, int dshift, int dfactor) { int i; @@ -77,7 +77,9 @@ av_cold void ff_takdsp_init(TAKDSPContext *c) c->decorrelate_sm = decorrelate_sm; c->decorrelate_sf = decorrelate_sf; -#if ARCH_X86 +#if ARCH_RISCV + ff_takdsp_init_riscv(c); +#elif ARCH_X86 ff_takdsp_init_x86(c); #endif } diff --git a/libavcodec/takdsp.h b/libavcodec/takdsp.h index c05b5741a46..13b5e530b27 100644 --- a/libavcodec/takdsp.h +++ b/libavcodec/takdsp.h @@ -22,13 +22,14 @@ #include typedef struct TAKDSPContext { - void (*decorrelate_ls)(int32_t *p1, int32_t *p2, int length); - void (*decorrelate_sr)(int32_t *p1, int32_t *p2, int length); + void (*decorrelate_ls)(const int32_t *p1, int32_t *p2, int length); + void (*decorrelate_sr)(int32_t *p1, const int32_t *p2, int length); void (*decorrelate_sm)(int32_t *p1, int32_t *p2, int length); - void (*decorrelate_sf)(int32_t *p1, int32_t *p2, int length, int dshift, int dfactor); + void (*decorrelate_sf)(int32_t *p1, const int32_t *p2, int length, int dshift, int dfactor); } TAKDSPContext; void ff_takdsp_init(TAKDSPContext *c); +void ff_takdsp_init_riscv(TAKDSPContext *c); void ff_takdsp_init_x86(TAKDSPContext *c); #endif /* AVCODEC_TAKDSP_H */ diff --git a/libavcodec/targa.c b/libavcodec/targa.c index 07005f2be69..59fdc428d95 100644 --- a/libavcodec/targa.c +++ b/libavcodec/targa.c @@ -249,7 +249,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, } break; } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS p->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } } diff --git a/libavcodec/targa_y216dec.c b/libavcodec/targa_y216dec.c index d5234c16ae9..2874a51aaed 100644 --- a/libavcodec/targa_y216dec.c +++ b/libavcodec/targa_y216dec.c @@ -47,7 +47,7 @@ static int y216_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; y = (uint16_t *)pic->data[0]; diff --git a/libavcodec/targaenc.c b/libavcodec/targaenc.c index d9c500b97de..8f496c62bd9 100644 --- a/libavcodec/targaenc.c +++ b/libavcodec/targaenc.c @@ -21,6 +21,7 @@ #include +#include "libavutil/avassert.h" #include "libavutil/imgutils.h" #include "libavutil/internal.h" #include "libavutil/intreadwrite.h" @@ -89,10 +90,11 @@ static int targa_encode_frame(AVCodecContext *avctx, AVPacket *pkt, TargaContext *s = avctx->priv_data; int bpp, picsize, datasize = -1, ret, i; uint8_t *out; + int maxpal = 32*32; picsize = av_image_get_buffer_size(avctx->pix_fmt, avctx->width, avctx->height, 1); - if ((ret = ff_alloc_packet(avctx, pkt, picsize + 45)) < 0) + if ((ret = ff_alloc_packet(avctx, pkt, picsize + 45 + maxpal)) < 0) return ret; /* zero out the header and only set applicable fields */ @@ -125,6 +127,7 @@ static int targa_encode_frame(AVCodecContext *avctx, AVPacket *pkt, AV_WL24(pkt->data + 18 + 3 * i, *(uint32_t *)(p->data[1] + i * 4)); } out += 32 * pal_bpp; /* skip past the palette we just output */ + av_assert0(32 * pal_bpp <= maxpal); break; } case AV_PIX_FMT_GRAY8: diff --git a/libavcodec/tdsc.c b/libavcodec/tdsc.c index b5ab2e171ba..624e219cb43 100644 --- a/libavcodec/tdsc.c +++ b/libavcodec/tdsc.c @@ -127,7 +127,6 @@ static av_cold int tdsc_init(AVCodecContext *avctx) return AVERROR(ENOMEM); ctx->jpeg_avctx->flags = avctx->flags; ctx->jpeg_avctx->flags2 = avctx->flags2; - ctx->jpeg_avctx->dct_algo = avctx->dct_algo; ctx->jpeg_avctx->idct_algo = avctx->idct_algo; ret = avcodec_open2(ctx->jpeg_avctx, codec, NULL); if (ret < 0) @@ -612,7 +611,7 @@ static int tdsc_decode_frame(AVCodecContext *avctx, AVFrame *frame, /* Frame is ready to be output */ if (keyframe) { frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; } else { frame->pict_type = AV_PICTURE_TYPE_P; } diff --git a/libavcodec/tests/av1_levels.c b/libavcodec/tests/av1_levels.c new file mode 100644 index 00000000000..e862d197d2a --- /dev/null +++ b/libavcodec/tests/av1_levels.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2023 Intel Corporation + * + * 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 +#include +#include "libavutil/log.h" +#include "libavcodec/av1_levels.h" + +static const struct { + int width; + int height; + float framerate; + int level_idx; +} test_sizes[] = { + { 426, 240, 30.0, 0 }, + { 640, 360, 30.0, 1 }, + { 854, 480, 30.0, 4 }, + { 1280, 720, 30.0, 5 }, + { 1920, 1080, 30.0, 8 }, + { 1920, 1080, 60.0, 9 }, + { 3840, 2160, 30.0, 12 }, + { 3840, 2160, 60.0, 13 }, + { 3840, 2160, 120.0, 14 }, + { 7680, 4320, 30.0, 16 }, + { 7680, 4320, 60.0, 17 }, + { 7680, 4320, 120.0, 18 }, +}; + +static const struct { + int64_t bitrate; + int tier; + int level_idx; +} test_bitrate[] = { + { 1500000, 0, 0 }, + { 3000000, 0, 1 }, + { 6000000, 0, 4 }, + { 10000000, 0, 5 }, + { 12000000, 0, 8 }, + { 30000000, 1, 8 }, + { 20000000, 0, 9 }, + { 50000000, 1, 9 }, + { 30000000, 0, 12 }, + { 100000000, 1, 12 }, + { 40000000, 0, 13 }, + { 160000000, 1, 13 }, + { 60000000, 0, 14 }, + { 240000000, 1, 14 }, + { 100000000, 0, 17 }, + { 480000000, 1, 17 }, + { 160000000, 0, 18 }, + { 800000000, 1, 18 }, +}; + +static const struct { + int tiles; + int tile_cols; + int level_idx; +} test_tiles[] = { + { 8, 4, 0 }, + { 16, 6, 4 }, + { 32, 8, 8 }, + { 64, 8, 12 }, + { 128, 16, 16 }, +}; + +int main(void) +{ + const AV1LevelDescriptor *level; + int i; + +#define CHECK(expected, format, ...) do { \ + if (level ? (level->level_idx != expected) \ + : !level) { \ + av_log(NULL, AV_LOG_ERROR, "Incorrect level for " \ + format ": expected %d, got %d.\n", __VA_ARGS__, \ + expected, level ? level->level_idx : -1); \ + return 1; \ + } \ + } while (0) + + for (i = 0; i < FF_ARRAY_ELEMS(test_sizes); i++) { + level = ff_av1_guess_level(0, 0, + test_sizes[i].width, + test_sizes[i].height, + 0, 0, test_sizes[i].framerate); + CHECK(test_sizes[i].level_idx, "size %dx%d, framerate %f", + test_sizes[i].width, test_sizes[i].height, test_sizes[i].framerate); + } + + for (i = 0; i < FF_ARRAY_ELEMS(test_bitrate); i++) { + level = ff_av1_guess_level(test_bitrate[i].bitrate, + test_bitrate[i].tier, + 0, 0, 0, 0, 0); + CHECK(test_bitrate[i].level_idx, "bitrate %"PRId64" tier %d", + test_bitrate[i].bitrate, test_bitrate[i].tier); + } + + for (i = 0; i < FF_ARRAY_ELEMS(test_tiles); i++) { + level = ff_av1_guess_level(0, 0, 0, 0, + test_tiles[i].tiles, + test_tiles[i].tile_cols, + 0); + CHECK(test_tiles[i].level_idx, "tiles %d, tile cols %d", + test_tiles[i].tiles, + test_tiles[i].tile_cols); + } + + return 0; +} diff --git a/libavcodec/tests/avcodec.c b/libavcodec/tests/avcodec.c index 4c1730425d7..08ca507bf0e 100644 --- a/libavcodec/tests/avcodec.c +++ b/libavcodec/tests/avcodec.c @@ -20,7 +20,6 @@ #include "libavcodec/codec.h" #include "libavcodec/codec_desc.h" #include "libavcodec/codec_internal.h" -#include "libavcodec/internal.h" static const char *get_type_string(enum AVMediaType type) { @@ -149,8 +148,7 @@ int main(void){ FF_CODEC_CAP_SETS_FRAME_PROPS) || codec->capabilities & (AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_CHANNEL_CONF | - AV_CODEC_CAP_DRAW_HORIZ_BAND | - AV_CODEC_CAP_SUBFRAMES)) + AV_CODEC_CAP_DRAW_HORIZ_BAND)) ERR("Encoder %s has decoder-only capabilities set\n"); if (codec->capabilities & AV_CODEC_CAP_FRAME_THREADS && codec->capabilities & AV_CODEC_CAP_ENCODER_FLUSH) @@ -177,6 +175,10 @@ int main(void){ !(codec->capabilities & AV_CODEC_CAP_FRAME_THREADS)) ERR("Decoder %s wants allocated progress without supporting" "frame threads\n"); + if (codec2->cb_type != FF_CODEC_CB_TYPE_DECODE && + codec2->caps_internal & FF_CODEC_CAP_SETS_PKT_DTS) + ERR("Decoder %s is marked as setting pkt_dts when it doesn't have" + "any effect\n"); } if (priv_data_size_wrong(codec2)) ERR_EXT("Private context of codec %s is impossibly-sized (size %d).", diff --git a/libavcodec/tests/bitstream_template.c b/libavcodec/tests/bitstream_template.c index ef59845154d..b4c8821a907 100644 --- a/libavcodec/tests/bitstream_template.c +++ b/libavcodec/tests/bitstream_template.c @@ -61,6 +61,7 @@ int main(int argc, char **argv) uint64_t val, val1; int32_t sval, sval1; unsigned count; + int ret; /* generate random input, using a given or random seed */ if (argc > 1) @@ -74,7 +75,8 @@ int main(int argc, char **argv) for (unsigned i = 0; i < SIZE; i++) buf[i] = av_lfg_get(&lfg); - bits_init8 (&bc, buf, SIZE); + ret = bits_init8 (&bc, buf, SIZE); + av_assert0(ret >= 0); init_put_bits(&pb, dst, SIZE); /* use a random sequence of bitreading operations to transfer data diff --git a/libavcodec/tests/codec_desc.c b/libavcodec/tests/codec_desc.c index c9b3497343a..bceb91a32aa 100644 --- a/libavcodec/tests/codec_desc.c +++ b/libavcodec/tests/codec_desc.c @@ -16,7 +16,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavcodec/avcodec.h" +#include "libavcodec/codec_desc.h" int main(int argc, char **argv) { diff --git a/libavcodec/tests/dct.c b/libavcodec/tests/dct.c index c847af2f110..17a08144597 100644 --- a/libavcodec/tests/dct.c +++ b/libavcodec/tests/dct.c @@ -37,12 +37,14 @@ #include "libavutil/cpu.h" #include "libavutil/common.h" +#include "libavutil/emms.h" #include "libavutil/internal.h" #include "libavutil/lfg.h" #include "libavutil/mem_internal.h" #include "libavutil/time.h" #include "libavcodec/dct.h" +#include "libavcodec/fdctdsp.h" #include "libavcodec/idctdsp.h" #include "libavcodec/simple_idct.h" #include "libavcodec/xvididct.h" @@ -224,8 +226,8 @@ static int dct_error(const struct algo *dct, int test, int is_idct, int speed, c v = abs(err); if (v > err_inf) err_inf = v; - err2_matrix[i] += v * v; - err2 += v * v; + err2_matrix[i] += v * (int64_t)v; + err2 += v * (int64_t)v; sysErr[i] += block[i] - block1[i]; blockSumErr += v; if (abs(block[i]) > maxout) diff --git a/libavcodec/tests/fft.c b/libavcodec/tests/fft.c deleted file mode 100644 index 163f3e89c42..00000000000 --- a/libavcodec/tests/fft.c +++ /dev/null @@ -1,677 +0,0 @@ -/* - * (c) 2002 Fabrice Bellard - * - * 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 - * FFT and MDCT tests. - */ - -#include "config.h" - -#ifndef AVFFT -#define AVFFT 0 -#endif - -#include -#if HAVE_UNISTD_H -#include -#endif -#include -#include -#include - -#include "libavutil/cpu.h" -#include "libavutil/error.h" -#include "libavutil/lfg.h" -#include "libavutil/log.h" -#include "libavutil/mathematics.h" -#include "libavutil/time.h" - -#if AVFFT -#include "libavcodec/avfft.h" -#else -#include "libavcodec/fft.h" -#endif - -#if FFT_FLOAT -#include "libavcodec/dct.h" -#include "libavcodec/rdft.h" -#endif - -/* reference fft */ - -#define MUL16(a, b) ((a) * (b)) - -#define CMAC(pre, pim, are, aim, bre, bim) \ - { \ - pre += (MUL16(are, bre) - MUL16(aim, bim)); \ - pim += (MUL16(are, bim) + MUL16(bre, aim)); \ - } - -#if FFT_FLOAT || AVFFT -#define RANGE 1.0 -#define REF_SCALE(x, bits) (x) -#define FMT "%10.6f" -#else -#define RANGE 8388608 -#define REF_SCALE(x, bits) (x) -#define FMT "%6d" -#endif - -static struct { - float re, im; -} *exptab; - -static int fft_ref_init(int nbits, int inverse) -{ - int i, n = 1 << nbits; - - exptab = av_malloc_array((n / 2), sizeof(*exptab)); - if (!exptab) - return AVERROR(ENOMEM); - - for (i = 0; i < (n / 2); i++) { - double alpha = 2 * M_PI * (float) i / (float) n; - double c1 = cos(alpha), s1 = sin(alpha); - if (!inverse) - s1 = -s1; - exptab[i].re = c1; - exptab[i].im = s1; - } - return 0; -} - -static void fft_ref(FFTComplex *tabr, FFTComplex *tab, int nbits) -{ - int i, j; - int n = 1 << nbits; - int n2 = n >> 1; - - for (i = 0; i < n; i++) { - double tmp_re = 0, tmp_im = 0; - FFTComplex *q = tab; - for (j = 0; j < n; j++) { - double s, c; - int k = (i * j) & (n - 1); - if (k >= n2) { - c = -exptab[k - n2].re; - s = -exptab[k - n2].im; - } else { - c = exptab[k].re; - s = exptab[k].im; - } - CMAC(tmp_re, tmp_im, c, s, q->re, q->im); - q++; - } - tabr[i].re = REF_SCALE(tmp_re, nbits); - tabr[i].im = REF_SCALE(tmp_im, nbits); - } -} - -#if CONFIG_MDCT -static void imdct_ref(FFTSample *out, FFTSample *in, int nbits) -{ - int i, k, n = 1 << nbits; - - for (i = 0; i < n; i++) { - double sum = 0; - for (k = 0; k < n / 2; k++) { - int a = (2 * i + 1 + (n / 2)) * (2 * k + 1); - double f = cos(M_PI * a / (double) (2 * n)); - sum += f * in[k]; - } - out[i] = REF_SCALE(-sum, nbits - 2); - } -} - -/* NOTE: no normalisation by 1 / N is done */ -static void mdct_ref(FFTSample *output, FFTSample *input, int nbits) -{ - int i, k, n = 1 << nbits; - - /* do it by hand */ - for (k = 0; k < n / 2; k++) { - double s = 0; - for (i = 0; i < n; i++) { - double a = (2 * M_PI * (2 * i + 1 + n / 2) * (2 * k + 1) / (4 * n)); - s += input[i] * cos(a); - } - output[k] = REF_SCALE(s, nbits - 1); - } -} -#endif /* CONFIG_MDCT */ - -#if FFT_FLOAT -#if CONFIG_DCT -static void idct_ref(FFTSample *output, FFTSample *input, int nbits) -{ - int i, k, n = 1 << nbits; - - /* do it by hand */ - for (i = 0; i < n; i++) { - double s = 0.5 * input[0]; - for (k = 1; k < n; k++) { - double a = M_PI * k * (i + 0.5) / n; - s += input[k] * cos(a); - } - output[i] = 2 * s / n; - } -} - -static void dct_ref(FFTSample *output, FFTSample *input, int nbits) -{ - int i, k, n = 1 << nbits; - - /* do it by hand */ - for (k = 0; k < n; k++) { - double s = 0; - for (i = 0; i < n; i++) { - double a = M_PI * k * (i + 0.5) / n; - s += input[i] * cos(a); - } - output[k] = s; - } -} -#endif /* CONFIG_DCT */ -#endif /* FFT_FLOAT */ - -static FFTSample frandom(AVLFG *prng) -{ - return (int16_t) av_lfg_get(prng) / 32768.0 * RANGE; -} - -static int check_diff(FFTSample *tab1, FFTSample *tab2, int n, double scale) -{ - int i, err = 0; - double error = 0, max = 0; - - for (i = 0; i < n; i++) { - double e = fabs(tab1[i] - (tab2[i] / scale)) / RANGE; - if (e >= 1e-3) { - av_log(NULL, AV_LOG_ERROR, "ERROR %5d: "FMT" "FMT"\n", - i, tab1[i], tab2[i]); - err = 1; - } - error += e * e; - if (e > max) - max = e; - } - av_log(NULL, AV_LOG_INFO, "max:%f e:%g\n", max, sqrt(error / n)); - return err; -} - -static inline void fft_init(FFTContext **s, int nbits, int inverse) -{ -#if AVFFT - *s = av_fft_init(nbits, inverse); -#else - ff_fft_init(*s, nbits, inverse); -#endif -} - -#if CONFIG_MDCT -static inline void mdct_init(FFTContext **s, int nbits, int inverse, double scale) -{ -#if AVFFT - *s = av_mdct_init(nbits, inverse, scale); -#else - ff_mdct_init(*s, nbits, inverse, scale); -#endif -} - -static inline void mdct_calc(FFTContext *s, FFTSample *output, const FFTSample *input) -{ -#if AVFFT - av_mdct_calc(s, output, input); -#else - s->mdct_calc(s, output, input); -#endif -} - -static inline void imdct_calc(struct FFTContext *s, FFTSample *output, const FFTSample *input) -{ -#if AVFFT - av_imdct_calc(s, output, input); -#else - s->imdct_calc(s, output, input); -#endif -} -#endif - -static inline void fft_permute(FFTContext *s, FFTComplex *z) -{ -#if AVFFT - av_fft_permute(s, z); -#else - s->fft_permute(s, z); -#endif -} - -static inline void fft_calc(FFTContext *s, FFTComplex *z) -{ -#if AVFFT - av_fft_calc(s, z); -#else - s->fft_calc(s, z); -#endif -} - -static inline void mdct_end(FFTContext *s) -{ -#if AVFFT - av_mdct_end(s); -#else - ff_mdct_end(s); -#endif -} - -static inline void fft_end(FFTContext *s) -{ -#if AVFFT - av_fft_end(s); -#else - ff_fft_end(s); -#endif -} - -#if FFT_FLOAT -static inline void rdft_init(RDFTContext **r, int nbits, enum RDFTransformType trans) -{ -#if AVFFT - *r = av_rdft_init(nbits, trans); -#else - ff_rdft_init(*r, nbits, trans); -#endif -} - -static inline void dct_init(DCTContext **d, int nbits, enum DCTTransformType trans) -{ -#if AVFFT - *d = av_dct_init(nbits, trans); -#else - ff_dct_init(*d, nbits, trans); -#endif -} - -static inline void rdft_calc(RDFTContext *r, FFTSample *tab) -{ -#if AVFFT - av_rdft_calc(r, tab); -#else - r->rdft_calc(r, tab); -#endif -} - -static inline void dct_calc(DCTContext *d, FFTSample *data) -{ -#if AVFFT - av_dct_calc(d, data); -#else - d->dct_calc(d, data); -#endif -} - -static inline void rdft_end(RDFTContext *r) -{ -#if AVFFT - av_rdft_end(r); -#else - ff_rdft_end(r); -#endif -} - -static inline void dct_end(DCTContext *d) -{ -#if AVFFT - av_dct_end(d); -#else - ff_dct_end(d); -#endif -} -#endif /* FFT_FLOAT */ - -static void help(void) -{ - av_log(NULL, AV_LOG_INFO, - "usage: fft-test [-h] [-s] [-i] [-n b]\n" - "-h print this help\n" - "-s speed test\n" - "-m (I)MDCT test\n" - "-d (I)DCT test\n" - "-r (I)RDFT test\n" - "-i inverse transform test\n" - "-n b set the transform size to 2^b\n" - "-f x set scale factor for output data of (I)MDCT to x\n"); -} - -enum tf_transform { - TRANSFORM_FFT, - TRANSFORM_MDCT, - TRANSFORM_RDFT, - TRANSFORM_DCT, -}; - -#if !HAVE_GETOPT -#include "compat/getopt.c" -#endif - -int main(int argc, char **argv) -{ - FFTComplex *tab, *tab1, *tab_ref; - FFTSample *tab2; - enum tf_transform transform = TRANSFORM_FFT; - FFTContext *m, *s; -#if FFT_FLOAT - RDFTContext *r; - DCTContext *d; -#endif /* FFT_FLOAT */ - int it, i, err = 1; - int do_speed = 0, do_inverse = 0; - int fft_nbits = 9, fft_size; - double scale = 1.0; - AVLFG prng; - -#if !AVFFT - s = av_mallocz(sizeof(*s)); - m = av_mallocz(sizeof(*m)); -#endif - -#if !AVFFT && FFT_FLOAT - r = av_mallocz(sizeof(*r)); - d = av_mallocz(sizeof(*d)); -#endif - - av_lfg_init(&prng, 1); - - for (;;) { - int c = getopt(argc, argv, "hsimrdn:f:c:"); - if (c == -1) - break; - switch (c) { - case 'h': - help(); - return 1; - case 's': - do_speed = 1; - break; - case 'i': - do_inverse = 1; - break; - case 'm': - transform = TRANSFORM_MDCT; - break; - case 'r': - transform = TRANSFORM_RDFT; - break; - case 'd': - transform = TRANSFORM_DCT; - break; - case 'n': - fft_nbits = atoi(optarg); - break; - case 'f': - scale = atof(optarg); - break; - case 'c': - { - unsigned cpuflags = av_get_cpu_flags(); - - if (av_parse_cpu_caps(&cpuflags, optarg) < 0) - return 1; - - av_force_cpu_flags(cpuflags); - break; - } - } - } - - fft_size = 1 << fft_nbits; - tab = av_malloc_array(fft_size, sizeof(FFTComplex)); - tab1 = av_malloc_array(fft_size, sizeof(FFTComplex)); - tab_ref = av_malloc_array(fft_size, sizeof(FFTComplex)); - tab2 = av_malloc_array(fft_size, sizeof(FFTSample)); - - if (!(tab && tab1 && tab_ref && tab2)) - goto cleanup; - - switch (transform) { -#if CONFIG_MDCT - case TRANSFORM_MDCT: - av_log(NULL, AV_LOG_INFO, "Scale factor is set to %f\n", scale); - if (do_inverse) - av_log(NULL, AV_LOG_INFO, "IMDCT"); - else - av_log(NULL, AV_LOG_INFO, "MDCT"); - mdct_init(&m, fft_nbits, do_inverse, scale); - break; -#endif /* CONFIG_MDCT */ - case TRANSFORM_FFT: - if (do_inverse) - av_log(NULL, AV_LOG_INFO, "IFFT"); - else - av_log(NULL, AV_LOG_INFO, "FFT"); - fft_init(&s, fft_nbits, do_inverse); - if ((err = fft_ref_init(fft_nbits, do_inverse)) < 0) - goto cleanup; - break; -#if FFT_FLOAT -# if CONFIG_RDFT - case TRANSFORM_RDFT: - if (do_inverse) - av_log(NULL, AV_LOG_INFO, "IDFT_C2R"); - else - av_log(NULL, AV_LOG_INFO, "DFT_R2C"); - rdft_init(&r, fft_nbits, do_inverse ? IDFT_C2R : DFT_R2C); - if ((err = fft_ref_init(fft_nbits, do_inverse)) < 0) - goto cleanup; - break; -# endif /* CONFIG_RDFT */ -# if CONFIG_DCT - case TRANSFORM_DCT: - if (do_inverse) - av_log(NULL, AV_LOG_INFO, "DCT_III"); - else - av_log(NULL, AV_LOG_INFO, "DCT_II"); - dct_init(&d, fft_nbits, do_inverse ? DCT_III : DCT_II); - break; -# endif /* CONFIG_DCT */ -#endif /* FFT_FLOAT */ - default: - av_log(NULL, AV_LOG_ERROR, "Requested transform not supported\n"); - goto cleanup; - } - av_log(NULL, AV_LOG_INFO, " %d test\n", fft_size); - - /* generate random data */ - - for (i = 0; i < fft_size; i++) { - tab1[i].re = frandom(&prng); - tab1[i].im = frandom(&prng); - } - - /* checking result */ - av_log(NULL, AV_LOG_INFO, "Checking...\n"); - - switch (transform) { -#if CONFIG_MDCT - case TRANSFORM_MDCT: - if (do_inverse) { - imdct_ref(&tab_ref->re, &tab1->re, fft_nbits); - imdct_calc(m, tab2, &tab1->re); - err = check_diff(&tab_ref->re, tab2, fft_size, scale); - } else { - mdct_ref(&tab_ref->re, &tab1->re, fft_nbits); - mdct_calc(m, tab2, &tab1->re); - err = check_diff(&tab_ref->re, tab2, fft_size / 2, scale); - } - break; -#endif /* CONFIG_MDCT */ - case TRANSFORM_FFT: - memcpy(tab, tab1, fft_size * sizeof(FFTComplex)); - fft_permute(s, tab); - fft_calc(s, tab); - - fft_ref(tab_ref, tab1, fft_nbits); - err = check_diff(&tab_ref->re, &tab->re, fft_size * 2, 1.0); - break; -#if FFT_FLOAT -#if CONFIG_RDFT - case TRANSFORM_RDFT: - { - int fft_size_2 = fft_size >> 1; - if (do_inverse) { - tab1[0].im = 0; - tab1[fft_size_2].im = 0; - for (i = 1; i < fft_size_2; i++) { - tab1[fft_size_2 + i].re = tab1[fft_size_2 - i].re; - tab1[fft_size_2 + i].im = -tab1[fft_size_2 - i].im; - } - - memcpy(tab2, tab1, fft_size * sizeof(FFTSample)); - tab2[1] = tab1[fft_size_2].re; - - rdft_calc(r, tab2); - fft_ref(tab_ref, tab1, fft_nbits); - for (i = 0; i < fft_size; i++) { - tab[i].re = tab2[i]; - tab[i].im = 0; - } - err = check_diff(&tab_ref->re, &tab->re, fft_size * 2, 0.5); - } else { - for (i = 0; i < fft_size; i++) { - tab2[i] = tab1[i].re; - tab1[i].im = 0; - } - rdft_calc(r, tab2); - fft_ref(tab_ref, tab1, fft_nbits); - tab_ref[0].im = tab_ref[fft_size_2].re; - err = check_diff(&tab_ref->re, tab2, fft_size, 1.0); - } - break; - } -#endif /* CONFIG_RDFT */ -#if CONFIG_DCT - case TRANSFORM_DCT: - memcpy(tab, tab1, fft_size * sizeof(FFTComplex)); - dct_calc(d, &tab->re); - if (do_inverse) - idct_ref(&tab_ref->re, &tab1->re, fft_nbits); - else - dct_ref(&tab_ref->re, &tab1->re, fft_nbits); - err = check_diff(&tab_ref->re, &tab->re, fft_size, 1.0); - break; -#endif /* CONFIG_DCT */ -#endif /* FFT_FLOAT */ - } - - /* do a speed test */ - - if (do_speed) { - int64_t time_start, duration; - int nb_its; - - av_log(NULL, AV_LOG_INFO, "Speed test...\n"); - /* we measure during about 1 seconds */ - nb_its = 1; - for (;;) { - time_start = av_gettime_relative(); - for (it = 0; it < nb_its; it++) { - switch (transform) { -#if CONFIG_MDCT - case TRANSFORM_MDCT: - if (do_inverse) - imdct_calc(m, &tab->re, &tab1->re); - else - mdct_calc(m, &tab->re, &tab1->re); - break; -#endif - case TRANSFORM_FFT: - memcpy(tab, tab1, fft_size * sizeof(FFTComplex)); - fft_calc(s, tab); - break; -#if FFT_FLOAT - case TRANSFORM_RDFT: - memcpy(tab2, tab1, fft_size * sizeof(FFTSample)); - rdft_calc(r, tab2); - break; - case TRANSFORM_DCT: - memcpy(tab2, tab1, fft_size * sizeof(FFTSample)); - dct_calc(d, tab2); - break; -#endif /* FFT_FLOAT */ - } - } - duration = av_gettime_relative() - time_start; - if (duration >= 1000000) - break; - nb_its *= 2; - } - av_log(NULL, AV_LOG_INFO, - "time: %0.1f us/transform [total time=%0.2f s its=%d]\n", - (double) duration / nb_its, - (double) duration / 1000000.0, - nb_its); - } - - switch (transform) { -#if CONFIG_MDCT - case TRANSFORM_MDCT: - mdct_end(m); - break; -#endif /* CONFIG_MDCT */ - case TRANSFORM_FFT: - fft_end(s); - break; -#if FFT_FLOAT -# if CONFIG_RDFT - case TRANSFORM_RDFT: - rdft_end(r); - break; -# endif /* CONFIG_RDFT */ -# if CONFIG_DCT - case TRANSFORM_DCT: - dct_end(d); - break; -# endif /* CONFIG_DCT */ -#endif /* FFT_FLOAT */ - } - -cleanup: - av_free(tab); - av_free(tab1); - av_free(tab2); - av_free(tab_ref); - av_free(exptab); - -#if !AVFFT - av_free(s); - av_free(m); -#endif - -#if !AVFFT && FFT_FLOAT - av_free(r); - av_free(d); -#endif - - if (err) - printf("Error: %d.\n", err); - - return !!err; -} diff --git a/libavcodec/tests/h264_levels.c b/libavcodec/tests/h264_levels.c index 98febcc41b8..af3bfe01a6f 100644 --- a/libavcodec/tests/h264_levels.c +++ b/libavcodec/tests/h264_levels.c @@ -16,7 +16,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/common.h" +#include +#include + +#include "libavutil/log.h" +#include "libavutil/macros.h" #include "libavcodec/h264_levels.h" static const struct { diff --git a/libavcodec/tests/jpeg2000dwt.c b/libavcodec/tests/jpeg2000dwt.c index 0e5a6ed9471..520ecc05a3a 100644 --- a/libavcodec/tests/jpeg2000dwt.c +++ b/libavcodec/tests/jpeg2000dwt.c @@ -52,12 +52,12 @@ static int test_dwt(int *array, int *ref, int border[2][2], int decomp_levels, i return 1; } for (j = 0; j max_diff) { + if (FFABS(array[j] - (int64_t)ref[j]) > max_diff) { fprintf(stderr, "missmatch at %d (%d != %d) decomp:%d border %d %d %d %d\n", j, array[j], ref[j],decomp_levels, border[0][0], border[0][1], border[1][0], border[1][1]); return 2; } - err2 += (array[j] - ref[j]) * (array[j] - ref[j]); + err2 += (array[j] - ref[j]) * (int64_t)(array[j] - ref[j]); array[j] = ref[j]; } ff_dwt_destroy(s); diff --git a/libavcodec/tests/motion.c b/libavcodec/tests/motion.c index ef6e1ff3099..caa8ecb8bed 100644 --- a/libavcodec/tests/motion.c +++ b/libavcodec/tests/motion.c @@ -30,6 +30,7 @@ #include "config.h" #include "libavcodec/me_cmp.h" #include "libavutil/cpu.h" +#include "libavutil/emms.h" #include "libavutil/internal.h" #include "libavutil/lfg.h" #include "libavutil/mem.h" diff --git a/libavcodec/tests/snowenc.c b/libavcodec/tests/snowenc.c index 37198cd4e3b..3dad07d3a5f 100644 --- a/libavcodec/tests/snowenc.c +++ b/libavcodec/tests/snowenc.c @@ -35,9 +35,9 @@ int main(void){ SnowContext s; int i; AVLFG prng; + int ret = 0; s.spatial_decomposition_count=6; s.spatial_decomposition_type=1; - int ret = 0; s.temp_dwt_buffer = av_calloc(width, sizeof(*s.temp_dwt_buffer)); s.temp_idwt_buffer = av_calloc(width, sizeof(*s.temp_idwt_buffer)); diff --git a/libavcodec/texturedsp.c b/libavcodec/texturedsp.c index b8938213ef3..721bfc17f5a 100644 --- a/libavcodec/texturedsp.c +++ b/libavcodec/texturedsp.c @@ -175,9 +175,12 @@ static av_always_inline void premult2straight(uint8_t *src) int b = src[2]; int a = src[3]; /* unchanged */ - src[0] = (uint8_t) r * a / 255; - src[1] = (uint8_t) g * a / 255; - src[2] = (uint8_t) b * a / 255; + if (a == 0) + return; + + src[0] = (uint8_t) FFMIN(r * 255 / a, 255); + src[1] = (uint8_t) FFMIN(g * 255 / a, 255); + src[2] = (uint8_t) FFMIN(b * 255 / a, 255); } /** @@ -653,6 +656,6 @@ av_cold void ff_texturedsp_init(TextureDSPContext *c) c->dxn3dc_block = dxn3dc_block; } -#define TEXTUREDSP_FUNC_NAME ff_texturedsp_decompress_thread +#define TEXTUREDSP_FUNC_NAME ff_texturedsp_exec_decompress_threads #define TEXTUREDSP_TEX_FUNC(a, b, c) tex_funct(a, b, c) #include "texturedsp_template.c" diff --git a/libavcodec/texturedsp.h b/libavcodec/texturedsp.h index e15d3c2b024..0bf49e87293 100644 --- a/libavcodec/texturedsp.h +++ b/libavcodec/texturedsp.h @@ -39,8 +39,6 @@ #include #include -#include "avcodec.h" - #define TEXTURE_BLOCK_W 4 #define TEXTURE_BLOCK_H 4 @@ -62,12 +60,19 @@ typedef struct TextureDSPContext { int (*dxn3dc_block) (uint8_t *dst, ptrdiff_t stride, const uint8_t *block); } TextureDSPContext; +typedef struct TextureDSPEncContext { + int (*dxt1_block) (uint8_t *dst, ptrdiff_t stride, const uint8_t *block); + int (*dxt5_block) (uint8_t *dst, ptrdiff_t stride, const uint8_t *block); + int (*dxt5ys_block) (uint8_t *dst, ptrdiff_t stride, const uint8_t *block); +} TextureDSPEncContext; + typedef struct TextureDSPThreadContext { union { const uint8_t *in; // Input frame data uint8_t *out; // Output frame data } frame_data; ptrdiff_t stride; // Frame linesize + int width, height; // Frame width / height union { const uint8_t *in; // Compressed texture for decompression uint8_t *out; // Compressed texture of compression @@ -81,9 +86,12 @@ typedef struct TextureDSPThreadContext { } TextureDSPThreadContext; void ff_texturedsp_init(TextureDSPContext *c); -void ff_texturedspenc_init(TextureDSPContext *c); +void ff_texturedspenc_init(TextureDSPEncContext *c); -int ff_texturedsp_decompress_thread(AVCodecContext *avctx, void *arg, int slice, int thread_nb); -int ff_texturedsp_compress_thread(AVCodecContext *avctx, void *arg, int slice, int thread_nb); +struct AVCodecContext; +int ff_texturedsp_exec_decompress_threads(struct AVCodecContext *avctx, + TextureDSPThreadContext *ctx); +int ff_texturedsp_exec_compress_threads(struct AVCodecContext *avctx, + TextureDSPThreadContext *ctx); #endif /* AVCODEC_TEXTUREDSP_H */ diff --git a/libavcodec/texturedsp_template.c b/libavcodec/texturedsp_template.c index bd193aa97c5..b9caf494cc0 100644 --- a/libavcodec/texturedsp_template.c +++ b/libavcodec/texturedsp_template.c @@ -20,13 +20,15 @@ * */ -int TEXTUREDSP_FUNC_NAME(AVCodecContext *avctx, void *arg, - int slice, int thread_nb) +#include "avcodec.h" + +static int exec_func(AVCodecContext *avctx, void *arg, + int slice, int thread_nb) { - TextureDSPThreadContext *ctx = arg; + const TextureDSPThreadContext *ctx = arg; uint8_t *d = ctx->tex_data.out; - int w_block = avctx->coded_width / TEXTURE_BLOCK_W; - int h_block = avctx->coded_height / TEXTURE_BLOCK_H; + int w_block = ctx->width / TEXTURE_BLOCK_W; + int h_block = ctx->height / TEXTURE_BLOCK_H; int x, y; int start_slice, end_slice; int base_blocks_per_slice = h_block / ctx->slice_count; @@ -55,3 +57,8 @@ int TEXTUREDSP_FUNC_NAME(AVCodecContext *avctx, void *arg, return 0; } + +int TEXTUREDSP_FUNC_NAME(AVCodecContext *avctx, TextureDSPThreadContext *ctx) +{ + return avctx->execute2(avctx, exec_func, ctx, NULL, ctx->slice_count); +} diff --git a/libavcodec/texturedspenc.c b/libavcodec/texturedspenc.c index 7ae28ea134a..5657a6ef61a 100644 --- a/libavcodec/texturedspenc.c +++ b/libavcodec/texturedspenc.c @@ -647,30 +647,13 @@ static int dxt5ys_block(uint8_t *dst, ptrdiff_t stride, const uint8_t *block) return 16; } -/** - * Compress one block of RGBA pixels in a RGTC1U texture and store the - * resulting bytes in 'dst'. Use the alpha channel of the input image. - * - * @param dst output buffer. - * @param stride scanline in bytes. - * @param block block to compress. - * @return how much texture data has been written. - */ -static int rgtc1u_alpha_block(uint8_t *dst, ptrdiff_t stride, const uint8_t *block) -{ - compress_alpha(dst, stride, block); - - return 8; -} - -av_cold void ff_texturedspenc_init(TextureDSPContext *c) +av_cold void ff_texturedspenc_init(TextureDSPEncContext *c) { c->dxt1_block = dxt1_block; c->dxt5_block = dxt5_block; c->dxt5ys_block = dxt5ys_block; - c->rgtc1u_alpha_block = rgtc1u_alpha_block; } -#define TEXTUREDSP_FUNC_NAME ff_texturedsp_compress_thread +#define TEXTUREDSP_FUNC_NAME ff_texturedsp_exec_compress_threads #define TEXTUREDSP_TEX_FUNC(a, b, c) tex_funct(c, b, a) #include "texturedsp_template.c" diff --git a/libavcodec/thread.h b/libavcodec/thread.h index 88a14cfeb12..f772d7ff181 100644 --- a/libavcodec/thread.h +++ b/libavcodec/thread.h @@ -52,6 +52,8 @@ void ff_thread_flush(AVCodecContext *avctx); int ff_thread_decode_frame(AVCodecContext *avctx, AVFrame *picture, int *got_picture_ptr, AVPacket *avpkt); +int ff_thread_can_start_frame(AVCodecContext *avctx); + /** * If the codec defines update_thread_context(), call this * when they are ready for the next thread to start decoding @@ -62,8 +64,6 @@ int ff_thread_decode_frame(AVCodecContext *avctx, AVFrame *picture, */ void ff_thread_finish_setup(AVCodecContext *avctx); -#define ff_thread_get_format ff_get_format - /** * Wrapper around get_buffer() for frame-multithreaded codecs. * Call this function instead of ff_get_buffer(f). @@ -74,19 +74,6 @@ void ff_thread_finish_setup(AVCodecContext *avctx); */ int ff_thread_get_buffer(AVCodecContext *avctx, AVFrame *f, int flags); -/** - * Wrapper around release_buffer() frame-for multithreaded codecs. - * Call this function instead of avctx->release_buffer(f). - * The AVFrame will be copied and the actual release_buffer() call - * will be performed later. The contents of data pointed to by the - * AVFrame should not be changed until ff_thread_get_buffer() is called - * on it. - * - * @param avctx The current context. - * @param f The picture being released. - */ -void ff_thread_release_buffer(AVCodecContext *avctx, AVFrame *f); - int ff_thread_init(AVCodecContext *s); int ff_slice_thread_execute_with_mainfunc(AVCodecContext *avctx, int (*action_func2)(AVCodecContext *c, void *arg, int jobnr, int threadnr), diff --git a/libavcodec/threadframe.h b/libavcodec/threadframe.h index d2f93c5cd09..d5954adc199 100644 --- a/libavcodec/threadframe.h +++ b/libavcodec/threadframe.h @@ -27,9 +27,7 @@ typedef struct ThreadFrame { AVFrame *f; AVCodecContext *owner[2]; - // progress->data is an array of 2 ints holding progress for top/bottom - // fields - AVBufferRef *progress; + struct ThreadFrameProgress *progress; } ThreadFrame; /** @@ -80,10 +78,10 @@ int ff_thread_get_ext_buffer(AVCodecContext *avctx, ThreadFrame *f, int flags); * @param avctx The current context. * @param f The picture being released. */ -void ff_thread_release_ext_buffer(AVCodecContext *avctx, ThreadFrame *f); +void ff_thread_release_ext_buffer(ThreadFrame *f); int ff_thread_ref_frame(ThreadFrame *dst, const ThreadFrame *src); -int ff_thread_can_start_frame(AVCodecContext *avctx); +int ff_thread_replace_frame(ThreadFrame *dst, const ThreadFrame *src); #endif diff --git a/libavcodec/tiertexseqv.c b/libavcodec/tiertexseqv.c index 19c0671bf6b..cdc885558b8 100644 --- a/libavcodec/tiertexseqv.c +++ b/libavcodec/tiertexseqv.c @@ -182,7 +182,11 @@ static int seqvideo_decode(SeqVideoContext *seq, const unsigned char *data, int c[j] = (*data << 2) | (*data >> 4); palette[i] = 0xFFU << 24 | AV_RB24(c); } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS seq->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } if (flags & 2) { diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c index ebc7505dcdf..bfa345b3d80 100644 --- a/libavcodec/tiff.c +++ b/libavcodec/tiff.c @@ -36,6 +36,7 @@ #include #include "libavutil/attributes.h" +#include "libavutil/avstring.h" #include "libavutil/error.h" #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" @@ -47,6 +48,7 @@ #include "faxcompr.h" #include "lzw.h" #include "tiff.h" +#include "tiff_common.h" #include "tiff_data.h" #include "mjpegdec.h" #include "thread.h" @@ -131,36 +133,37 @@ static void tiff_set_type(TiffContext *s, enum TiffType tiff_type) { static void free_geotags(TiffContext *const s) { - int i; - for (i = 0; i < s->geotag_count; i++) { - if (s->geotags[i].val) - av_freep(&s->geotags[i].val); - } + for (int i = 0; i < s->geotag_count; i++) + av_freep(&s->geotags[i].val); av_freep(&s->geotags); s->geotag_count = 0; } -#define RET_GEOKEY(TYPE, array, element)\ +static const char *get_geokey_name(int key) +{ +#define RET_GEOKEY_STR(TYPE, array)\ if (key >= TIFF_##TYPE##_KEY_ID_OFFSET &&\ key - TIFF_##TYPE##_KEY_ID_OFFSET < FF_ARRAY_ELEMS(tiff_##array##_name_type_map))\ - return tiff_##array##_name_type_map[key - TIFF_##TYPE##_KEY_ID_OFFSET].element; + return tiff_##array##_name_type_string + tiff_##array##_name_type_map[key - TIFF_##TYPE##_KEY_ID_OFFSET].offset; -static const char *get_geokey_name(int key) -{ - RET_GEOKEY(VERT, vert, name); - RET_GEOKEY(PROJ, proj, name); - RET_GEOKEY(GEOG, geog, name); - RET_GEOKEY(CONF, conf, name); + RET_GEOKEY_STR(VERT, vert); + RET_GEOKEY_STR(PROJ, proj); + RET_GEOKEY_STR(GEOG, geog); + RET_GEOKEY_STR(CONF, conf); return NULL; } static int get_geokey_type(int key) { - RET_GEOKEY(VERT, vert, type); - RET_GEOKEY(PROJ, proj, type); - RET_GEOKEY(GEOG, geog, type); - RET_GEOKEY(CONF, conf, type); +#define RET_GEOKEY_TYPE(TYPE, array)\ + if (key >= TIFF_##TYPE##_KEY_ID_OFFSET &&\ + key - TIFF_##TYPE##_KEY_ID_OFFSET < FF_ARRAY_ELEMS(tiff_##array##_name_type_map))\ + return tiff_##array##_name_type_map[key - TIFF_##TYPE##_KEY_ID_OFFSET].type; + RET_GEOKEY_TYPE(VERT, vert); + RET_GEOKEY_TYPE(PROJ, proj); + RET_GEOKEY_TYPE(GEOG, geog); + RET_GEOKEY_TYPE(CONF, conf); return AVERROR_INVALIDDATA; } @@ -179,19 +182,17 @@ static const char *search_keyval(const TiffGeoTagKeyName *keys, int n, int id) return NULL; } -static char *get_geokey_val(int key, int val) +static const char *get_geokey_val(int key, uint16_t val) { - char *ap; - if (val == TIFF_GEO_KEY_UNDEFINED) - return av_strdup("undefined"); + return "undefined"; if (val == TIFF_GEO_KEY_USER_DEFINED) - return av_strdup("User-Defined"); + return "User-Defined"; #define RET_GEOKEY_VAL(TYPE, array)\ if (val >= TIFF_##TYPE##_OFFSET &&\ val - TIFF_##TYPE##_OFFSET < FF_ARRAY_ELEMS(tiff_##array##_codes))\ - return av_strdup(tiff_##array##_codes[val - TIFF_##TYPE##_OFFSET]); + return tiff_##array##_codes[val - TIFF_##TYPE##_OFFSET]; switch (key) { case TIFF_GT_MODEL_TYPE_GEOKEY: @@ -224,13 +225,9 @@ static char *get_geokey_val(int key, int val) RET_GEOKEY_VAL(PRIME_MERIDIAN, prime_meridian); break; case TIFF_PROJECTED_CS_TYPE_GEOKEY: - ap = av_strdup(search_keyval(tiff_proj_cs_type_codes, FF_ARRAY_ELEMS(tiff_proj_cs_type_codes), val)); - if(ap) return ap; - break; + return search_keyval(tiff_proj_cs_type_codes, FF_ARRAY_ELEMS(tiff_proj_cs_type_codes), val); case TIFF_PROJECTION_GEOKEY: - ap = av_strdup(search_keyval(tiff_projection_codes, FF_ARRAY_ELEMS(tiff_projection_codes), val)); - if(ap) return ap; - break; + return search_keyval(tiff_projection_codes, FF_ARRAY_ELEMS(tiff_projection_codes), val); case TIFF_PROJ_COORD_TRANS_GEOKEY: RET_GEOKEY_VAL(COORD_TRANS, coord_trans); break; @@ -241,10 +238,7 @@ static char *get_geokey_val(int key, int val) } - ap = av_malloc(14); - if (ap) - snprintf(ap, 14, "Unknown-%d", val); - return ap; + return NULL; } static char *doubles2str(double *dp, int count, const char *sep) @@ -337,7 +331,7 @@ static void av_always_inline dng_blit(TiffContext *s, uint8_t *dst, int dst_stri (split vertically in the middle). */ for (line = 0; line < height / 2; line++) { uint16_t *dst_u16 = (uint16_t *)dst; - uint16_t *src_u16 = (uint16_t *)src; + const uint16_t *src_u16 = (const uint16_t *)src; /* Blit first half of input row row to initial row of output */ for (col = 0; col < width; col++) @@ -360,7 +354,7 @@ static void av_always_inline dng_blit(TiffContext *s, uint8_t *dst, int dst_stri if (is_u16) { for (line = 0; line < height; line++) { uint16_t *dst_u16 = (uint16_t *)dst; - uint16_t *src_u16 = (uint16_t *)src; + const uint16_t *src_u16 = (const uint16_t *)src; for (col = 0; col < width; col++) *dst_u16++ = dng_process_color16(*src_u16++, s->dng_lut, @@ -427,7 +421,8 @@ static void av_always_inline horizontal_fill(TiffContext *s, uint8_t shift = is_dng ? 0 : 16 - bpp; GetBitContext gb; - init_get_bits8(&gb, src, width); + int ret = init_get_bits8(&gb, src, width); + av_assert1(ret >= 0); for (int i = 0; i < s->width; i++) { dst16[i] = get_bits(&gb, bpp) << shift; } @@ -461,7 +456,8 @@ static void unpack_gray(TiffContext *s, AVFrame *p, GetBitContext gb; uint16_t *dst = (uint16_t *)(p->data[0] + lnum * p->linesize[0]); - init_get_bits8(&gb, src, width); + int ret = init_get_bits8(&gb, src, width); + av_assert1(ret >= 0); for (int i = 0; i < s->width; i++) { dst[i] = get_bits(&gb, bpp); @@ -570,7 +566,7 @@ static int tiff_uncompress_lzma(uint8_t *dst, uint64_t *len, const uint8_t *src, lzma_stream stream = LZMA_STREAM_INIT; lzma_ret ret; - stream.next_in = (uint8_t *)src; + stream.next_in = src; stream.avail_in = size; stream.next_out = dst; stream.avail_out = *len; @@ -1036,7 +1032,7 @@ static int dng_decode_tiles(AVCodecContext *avctx, AVFrame *frame, /* Frame is ready to be output */ frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; return avpkt->size; } @@ -1279,8 +1275,8 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) value = ff_tget(&s->gb, type, s->le); break; case TIFF_RATIONAL: - value = ff_tget(&s->gb, TIFF_LONG, s->le); - value2 = ff_tget(&s->gb, TIFF_LONG, s->le); + value = ff_tget_long(&s->gb, s->le); + value2 = ff_tget_long(&s->gb, s->le); if (!value2) { av_log(s->avctx, AV_LOG_WARNING, "Invalid denominator in rational\n"); value2 = 1; @@ -1301,9 +1297,13 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) s->is_thumbnail = (value != 0); break; case TIFF_WIDTH: + if (value > INT_MAX) + return AVERROR_INVALIDDATA; s->width = value; break; case TIFF_HEIGHT: + if (value > INT_MAX) + return AVERROR_INVALIDDATA; s->height = value; break; case TIFF_BPP: @@ -1435,19 +1435,25 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) s->tile_byte_counts_offset = off; break; case TIFF_TILE_LENGTH: + if (value > INT_MAX) + return AVERROR_INVALIDDATA; s->tile_length = value; break; case TIFF_TILE_WIDTH: + if (value > INT_MAX) + return AVERROR_INVALIDDATA; s->tile_width = value; break; case TIFF_PREDICTOR: + if (value > INT_MAX) + return AVERROR_INVALIDDATA; s->predictor = value; break; case TIFF_SUB_IFDS: if (count == 1) s->sub_ifd = value; else if (count > 1) - s->sub_ifd = ff_tget(&s->gb, TIFF_LONG, s->le); /** Only get the first SubIFD */ + s->sub_ifd = ff_tget_long(&s->gb, s->le); /** Only get the first SubIFD */ break; case TIFF_GRAY_RESPONSE_CURVE: case DNG_LINEARIZATION_TABLE: @@ -1463,8 +1469,8 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) s->black_level[0] = value / (float)value2; for (int i = 0; i < count && count > 1; i++) { if (type == TIFF_RATIONAL) { - value = ff_tget(&s->gb, TIFF_LONG, s->le); - value2 = ff_tget(&s->gb, TIFF_LONG, s->le); + value = ff_tget_long(&s->gb, s->le); + value2 = ff_tget_long(&s->gb, s->le); if (!value2) { av_log(s->avctx, AV_LOG_WARNING, "Invalid denominator\n"); value2 = 1; @@ -1472,8 +1478,8 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) s->black_level[i] = value / (float)value2; } else if (type == TIFF_SRATIONAL) { - int value = ff_tget(&s->gb, TIFF_LONG, s->le); - int value2 = ff_tget(&s->gb, TIFF_LONG, s->le); + int value = ff_tget_long(&s->gb, s->le); + int value2 = ff_tget_long(&s->gb, s->le); if (!value2) { av_log(s->avctx, AV_LOG_WARNING, "Invalid denominator\n"); value2 = 1; @@ -1585,12 +1591,18 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) } break; case TIFF_T4OPTIONS: - if (s->compr == TIFF_G3) + if (s->compr == TIFF_G3) { + if (value > INT_MAX) + return AVERROR_INVALIDDATA; s->fax_opts = value; + } break; case TIFF_T6OPTIONS: - if (s->compr == TIFF_G4) + if (s->compr == TIFF_G4) { + if (value > INT_MAX) + return AVERROR_INVALIDDATA; s->fax_opts = value; + } break; #define ADD_METADATA(count, name, sep)\ if ((ret = add_metadata(count, type, name, sep, s, frame)) < 0) {\ @@ -1630,14 +1642,20 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) goto end; } for (i = 0; i < s->geotag_count; i++) { + unsigned val; s->geotags[i].key = ff_tget_short(&s->gb, s->le); s->geotags[i].type = ff_tget_short(&s->gb, s->le); s->geotags[i].count = ff_tget_short(&s->gb, s->le); + val = ff_tget_short(&s->gb, s->le); - if (!s->geotags[i].type) - s->geotags[i].val = get_geokey_val(s->geotags[i].key, ff_tget_short(&s->gb, s->le)); - else - s->geotags[i].offset = ff_tget_short(&s->gb, s->le); + if (!s->geotags[i].type) { + const char *str = get_geokey_val(s->geotags[i].key, val); + + s->geotags[i].val = str ? av_strdup(str) : av_asprintf("Unknown-%u", val); + if (!s->geotags[i].val) + return AVERROR(ENOMEM); + } else + s->geotags[i].offset = val; } break; case TIFF_GEO_DOUBLE_PARAMS: @@ -1701,16 +1719,16 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) break; case TIFF_ICC_PROFILE: gb_temp = s->gb; - bytestream2_seek(&gb_temp, SEEK_SET, off); + bytestream2_seek(&gb_temp, off, SEEK_SET); if (bytestream2_get_bytes_left(&gb_temp) < count) return AVERROR_INVALIDDATA; - sd = av_frame_new_side_data(frame, AV_FRAME_DATA_ICC_PROFILE, count); - if (!sd) - return AVERROR(ENOMEM); - - bytestream2_get_bufferu(&gb_temp, sd->data, count); + ret = ff_frame_new_side_data(s->avctx, frame, AV_FRAME_DATA_ICC_PROFILE, count, &sd); + if (ret < 0) + return ret; + if (sd) + bytestream2_get_bufferu(&gb_temp, sd->data, count); break; case TIFF_ARTIST: ADD_METADATA(count, "artist", NULL); @@ -1744,7 +1762,7 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) // need to seek back to re-read the page number bytestream2_seek(&s->gb, -count * sizeof(uint16_t), SEEK_CUR); // read the page number - s->cur_page = ff_tget(&s->gb, TIFF_SHORT, s->le); + s->cur_page = ff_tget_short(&s->gb, s->le); // get back to where we were before the previous seek bytestream2_seek(&s->gb, count * sizeof(uint16_t) - sizeof(uint16_t), SEEK_CUR); break; @@ -1770,8 +1788,8 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) break; for (int i = 0; i < 3; i++) { - value = ff_tget(&s->gb, TIFF_LONG, s->le); - value2 = ff_tget(&s->gb, TIFF_LONG, s->le); + value = ff_tget_long(&s->gb, s->le); + value2 = ff_tget_long(&s->gb, s->le); if (!value2) { av_log(s->avctx, AV_LOG_WARNING, "Invalid denominator\n"); value2 = 1; @@ -1785,8 +1803,8 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) break; for (int i = 0; i < 3; i++) { - value = ff_tget(&s->gb, TIFF_LONG, s->le); - value2 = ff_tget(&s->gb, TIFF_LONG, s->le); + value = ff_tget_long(&s->gb, s->le); + value2 = ff_tget_long(&s->gb, s->le); if (!value2) { av_log(s->avctx, AV_LOG_WARNING, "Invalid denominator\n"); value2 = 1; @@ -1800,8 +1818,8 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) break; for (int i = 0; i < 2; i++) { - value = ff_tget(&s->gb, TIFF_LONG, s->le); - value2 = ff_tget(&s->gb, TIFF_LONG, s->le); + value = ff_tget_long(&s->gb, s->le); + value2 = ff_tget_long(&s->gb, s->le); if (!value2) { av_log(s->avctx, AV_LOG_WARNING, "Invalid denominator\n"); value2 = 1; @@ -1818,8 +1836,8 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) case DNG_COLOR_MATRIX2: for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - int value = ff_tget(&s->gb, TIFF_LONG, s->le); - int value2 = ff_tget(&s->gb, TIFF_LONG, s->le); + int value = ff_tget_long(&s->gb, s->le); + int value2 = ff_tget_long(&s->gb, s->le); if (!value2) { av_log(s->avctx, AV_LOG_WARNING, "Invalid denominator\n"); value2 = 1; @@ -1833,8 +1851,8 @@ static int tiff_decode_tag(TiffContext *s, AVFrame *frame) case DNG_CAMERA_CALIBRATION2: for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { - int value = ff_tget(&s->gb, TIFF_LONG, s->le); - int value2 = ff_tget(&s->gb, TIFF_LONG, s->le); + int value = ff_tget_long(&s->gb, s->le); + int value2 = ff_tget_long(&s->gb, s->le); if (!value2) { av_log(s->avctx, AV_LOG_WARNING, "Invalid denominator\n"); value2 = 1; @@ -2031,7 +2049,8 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, av_log(avctx, AV_LOG_WARNING, "Type of GeoTIFF key %d is wrong\n", s->geotags[i].key); continue; } - ret = av_dict_set(&p->metadata, keyname, s->geotags[i].val, 0); + ret = av_dict_set(&p->metadata, keyname, s->geotags[i].val, AV_DICT_DONT_STRDUP_VAL); + s->geotags[i].val = NULL; if (ret<0) { av_log(avctx, AV_LOG_ERROR, "Writing metadata with key '%s' failed\n", keyname); return ret; @@ -2379,6 +2398,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, } } + p->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; @@ -2415,7 +2435,6 @@ static av_cold int tiff_init(AVCodecContext *avctx) return AVERROR(ENOMEM); s->avctx_mjpeg->flags = avctx->flags; s->avctx_mjpeg->flags2 = avctx->flags2; - s->avctx_mjpeg->dct_algo = avctx->dct_algo; s->avctx_mjpeg->idct_algo = avctx->idct_algo; s->avctx_mjpeg->max_pixels = avctx->max_pixels; ret = avcodec_open2(s->avctx_mjpeg, codec, NULL); diff --git a/libavcodec/tiff.h b/libavcodec/tiff.h index e67c59abad2..12afcfa6e58 100644 --- a/libavcodec/tiff.h +++ b/libavcodec/tiff.h @@ -30,9 +30,6 @@ #ifndef AVCODEC_TIFF_H #define AVCODEC_TIFF_H -#include -#include "tiff_common.h" - /** TIFF types in ascenting priority (last in the list is highest) */ enum TiffType { /** TIFF image based on the TIFF 6.0 or TIFF/EP (ISO 12234-2) specifications */ @@ -225,9 +222,4 @@ typedef struct TiffGeoTagKeyName { const char *const name; } TiffGeoTagKeyName; -typedef struct TiffGeoTagNameType { - const char *const name; - const enum TiffGeoTagType type; -} TiffGeoTagNameType; - #endif /* AVCODEC_TIFF_H */ diff --git a/libavcodec/tiff_data.h b/libavcodec/tiff_data.h index 9b123ca8dff..1742ccf60f6 100644 --- a/libavcodec/tiff_data.h +++ b/libavcodec/tiff_data.h @@ -32,66 +32,89 @@ #include "tiff.h" +typedef struct TiffGeoTagNameType { + enum TiffGeoTagType type; + unsigned offset; +} TiffGeoTagNameType; + #define TIFF_CONF_KEY_ID_OFFSET 1024 -static const TiffGeoTagNameType tiff_conf_name_type_map[] = { - {"GTModelTypeGeoKey", GEOTIFF_SHORT }, - {"GTRasterTypeGeoKey", GEOTIFF_SHORT }, - {"GTCitationGeoKey", GEOTIFF_STRING} -}; +#define CONF_NAME_TYPE_MAP(KEY) \ + KEY(GTModelTypeGeoKey, SHORT ) \ + KEY(GTRasterTypeGeoKey, SHORT ) \ + KEY(GTCitationGeoKey, STRING) \ #define TIFF_GEOG_KEY_ID_OFFSET 2048 -static const TiffGeoTagNameType tiff_geog_name_type_map[] = { - {"GeographicTypeGeoKey", GEOTIFF_SHORT }, - {"GeogCitationGeoKey", GEOTIFF_STRING}, - {"GeogGeodeticDatumGeoKey", GEOTIFF_SHORT }, - {"GeogPrimeMeridianGeoKey", GEOTIFF_SHORT }, - {"GeogLinearUnitsGeoKey", GEOTIFF_SHORT }, - {"GeogLinearUnitSizeGeoKey", GEOTIFF_DOUBLE}, - {"GeogAngularUnitsGeoKey", GEOTIFF_SHORT }, - {"GeogAngularUnitSizeGeoKey", GEOTIFF_DOUBLE}, - {"GeogEllipsoidGeoKey", GEOTIFF_SHORT }, - {"GeogSemiMajorAxisGeoKey", GEOTIFF_DOUBLE}, - {"GeogSemiMinorAxisGeoKey", GEOTIFF_DOUBLE}, - {"GeogInvFlatteningGeoKey", GEOTIFF_DOUBLE}, - {"GeogAzimuthUnitsGeoKey", GEOTIFF_SHORT }, - {"GeogPrimeMeridianLongGeoKey", GEOTIFF_DOUBLE} -}; +#define GEOG_NAME_TYPE_MAP(KEY) \ + KEY(GeographicTypeGeoKey, SHORT ) \ + KEY(GeogCitationGeoKey, STRING) \ + KEY(GeogGeodeticDatumGeoKey, SHORT ) \ + KEY(GeogPrimeMeridianGeoKey, SHORT ) \ + KEY(GeogLinearUnitsGeoKey, SHORT ) \ + KEY(GeogLinearUnitSizeGeoKey, DOUBLE) \ + KEY(GeogAngularUnitsGeoKey, SHORT ) \ + KEY(GeogAngularUnitSizeGeoKey, DOUBLE) \ + KEY(GeogEllipsoidGeoKey, SHORT ) \ + KEY(GeogSemiMajorAxisGeoKey, DOUBLE) \ + KEY(GeogSemiMinorAxisGeoKey, DOUBLE) \ + KEY(GeogInvFlatteningGeoKey, DOUBLE) \ + KEY(GeogAzimuthUnitsGeoKey, SHORT ) \ + KEY(GeogPrimeMeridianLongGeoKey, DOUBLE) \ #define TIFF_PROJ_KEY_ID_OFFSET 3072 -static const TiffGeoTagNameType tiff_proj_name_type_map[] = { - {"ProjectedCSTypeGeoKey", GEOTIFF_SHORT }, - {"PCSCitationGeoKey", GEOTIFF_STRING}, - {"ProjectionGeoKey", GEOTIFF_SHORT }, - {"ProjCoordTransGeoKey", GEOTIFF_SHORT }, - {"ProjLinearUnitsGeoKey", GEOTIFF_SHORT }, - {"ProjLinearUnitSizeGeoKey", GEOTIFF_DOUBLE}, - {"ProjStdParallel1GeoKey", GEOTIFF_DOUBLE}, - {"ProjStdParallel2GeoKey", GEOTIFF_DOUBLE}, - {"ProjNatOriginLongGeoKey", GEOTIFF_DOUBLE}, - {"ProjNatOriginLatGeoKey", GEOTIFF_DOUBLE}, - {"ProjFalseEastingGeoKey", GEOTIFF_DOUBLE}, - {"ProjFalseNorthingGeoKey", GEOTIFF_DOUBLE}, - {"ProjFalseOriginLongGeoKey", GEOTIFF_DOUBLE}, - {"ProjFalseOriginLatGeoKey", GEOTIFF_DOUBLE}, - {"ProjFalseOriginEastingGeoKey", GEOTIFF_DOUBLE}, - {"ProjFalseOriginNorthingGeoKey", GEOTIFF_DOUBLE}, - {"ProjCenterLongGeoKey", GEOTIFF_DOUBLE}, - {"ProjCenterLatGeoKey", GEOTIFF_DOUBLE}, - {"ProjCenterEastingGeoKey", GEOTIFF_DOUBLE}, - {"ProjCenterNorthingGeoKey", GEOTIFF_DOUBLE}, - {"ProjScaleAtNatOriginGeoKey", GEOTIFF_DOUBLE}, - {"ProjScaleAtCenterGeoKey", GEOTIFF_DOUBLE}, - {"ProjAzimuthAngleGeoKey", GEOTIFF_DOUBLE}, - {"ProjStraightVertPoleLongGeoKey", GEOTIFF_DOUBLE} -}; +#define PROJ_NAME_TYPE_MAP(KEY) \ + KEY(ProjectedCSTypeGeoKey, SHORT ) \ + KEY(PCSCitationGeoKey, STRING) \ + KEY(ProjectionGeoKey, SHORT ) \ + KEY(ProjCoordTransGeoKey, SHORT ) \ + KEY(ProjLinearUnitsGeoKey, SHORT ) \ + KEY(ProjLinearUnitSizeGeoKey, DOUBLE) \ + KEY(ProjStdParallel1GeoKey, DOUBLE) \ + KEY(ProjStdParallel2GeoKey, DOUBLE) \ + KEY(ProjNatOriginLongGeoKey, DOUBLE) \ + KEY(ProjNatOriginLatGeoKey, DOUBLE) \ + KEY(ProjFalseEastingGeoKey, DOUBLE) \ + KEY(ProjFalseNorthingGeoKey, DOUBLE) \ + KEY(ProjFalseOriginLongGeoKey, DOUBLE) \ + KEY(ProjFalseOriginLatGeoKey, DOUBLE) \ + KEY(ProjFalseOriginEastingGeoKey, DOUBLE) \ + KEY(ProjFalseOriginNorthingGeoKey, DOUBLE) \ + KEY(ProjCenterLongGeoKey, DOUBLE) \ + KEY(ProjCenterLatGeoKey, DOUBLE) \ + KEY(ProjCenterEastingGeoKey, DOUBLE) \ + KEY(ProjCenterNorthingGeoKey, DOUBLE) \ + KEY(ProjScaleAtNatOriginGeoKey, DOUBLE) \ + KEY(ProjScaleAtCenterGeoKey, DOUBLE) \ + KEY(ProjAzimuthAngleGeoKey, DOUBLE) \ + KEY(ProjStraightVertPoleLongGeoKey, DOUBLE) \ #define TIFF_VERT_KEY_ID_OFFSET 4096 -static const TiffGeoTagNameType tiff_vert_name_type_map[] = { - {"VerticalCSTypeGeoKey", GEOTIFF_SHORT }, - {"VerticalCitationGeoKey", GEOTIFF_STRING}, - {"VerticalDatumGeoKey", GEOTIFF_SHORT }, - {"VerticalUnitsGeoKey", GEOTIFF_SHORT } -}; +#define VERT_NAME_TYPE_MAP(KEY) \ + KEY(VerticalCSTypeGeoKey, SHORT ) \ + KEY(VerticalCitationGeoKey, STRING) \ + KEY(VerticalDatumGeoKey, SHORT ) \ + KEY(VerticalUnitsGeoKey, SHORT ) \ + +#define ADD_OFFSET(NAME, TYPE) \ + NAME ## _OFFSET, \ + NAME ## _END = NAME ## _OFFSET + sizeof(#NAME) - 1, \ + +#define STRING(NAME, TYPE) #NAME "\0" + +#define ENTRY(NAME, TYPE) { .type = GEOTIFF_ ## TYPE, .offset = NAME ## _OFFSET }, +#define NAME_TYPE_MAP(NAME, name) \ + enum { \ + NAME ## _NAME_TYPE_MAP(ADD_OFFSET) \ + }; \ + static const TiffGeoTagNameType tiff_ ## name ## _name_type_map[] = { \ + NAME ## _NAME_TYPE_MAP(ENTRY) \ + }; \ + static const char *const tiff_ ## name ## _name_type_string = \ + NAME ## _NAME_TYPE_MAP(STRING) + +NAME_TYPE_MAP(CONF, conf); +NAME_TYPE_MAP(GEOG, geog); +NAME_TYPE_MAP(PROJ, proj); +NAME_TYPE_MAP(VERT, vert); #define TIFF_GEO_KEY_UNDEFINED 0 #define TIFF_GEO_KEY_USER_DEFINED 32767 @@ -781,13 +804,9 @@ static const TiffGeoTagKeyName tiff_proj_cs_type_codes[] = { {26771, "PCS_NAD27_Illinois_East"}, {26772, "PCS_NAD27_Illinois_West"}, {26773, "PCS_NAD27_Indiana_East"}, - {26774, "PCS_NAD27_BLM_14N_feet"}, {26774, "PCS_NAD27_Indiana_West"}, - {26775, "PCS_NAD27_BLM_15N_feet"}, {26775, "PCS_NAD27_Iowa_North"}, - {26776, "PCS_NAD27_BLM_16N_feet"}, {26776, "PCS_NAD27_Iowa_South"}, - {26777, "PCS_NAD27_BLM_17N_feet"}, {26777, "PCS_NAD27_Kansas_North"}, {26778, "PCS_NAD27_Kansas_South"}, {26779, "PCS_NAD27_Kentucky_North"}, diff --git a/libavcodec/tiffenc.c b/libavcodec/tiffenc.c index 06d7dcc99d8..7c3c03f1f38 100644 --- a/libavcodec/tiffenc.c +++ b/libavcodec/tiffenc.c @@ -30,7 +30,6 @@ #include #endif -#include "libavutil/imgutils.h" #include "libavutil/log.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" @@ -39,9 +38,9 @@ #include "codec_internal.h" #include "encode.h" #include "lzw.h" -#include "put_bits.h" #include "rle.h" #include "tiff.h" +#include "tiff_common.h" #include "version.h" #define TIFF_MAX_ENTRY 32 @@ -334,7 +333,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, bytes_per_row = (((s->width - 1) / s->subsampling[0] + 1) * s->bpp * s->subsampling[0] * s->subsampling[1] + 7) >> 3; packet_size = avctx->height * bytes_per_row * 2 + - avctx->height * 4 + AV_INPUT_BUFFER_MIN_SIZE; + avctx->height * 4 + FF_INPUT_BUFFER_MIN_SIZE; if ((ret = ff_alloc_packet(avctx, pkt, packet_size)) < 0) return ret; @@ -554,11 +553,11 @@ static av_cold int encode_close(AVCodecContext *avctx) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { {"dpi", "set the image resolution (in dpi)", OFFSET(dpi), AV_OPT_TYPE_INT, {.i64 = 72}, 1, 0x10000, AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_ENCODING_PARAM}, - { "compression_algo", NULL, OFFSET(compr), AV_OPT_TYPE_INT, { .i64 = TIFF_PACKBITS }, TIFF_RAW, TIFF_DEFLATE, VE, "compression_algo" }, - { "packbits", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TIFF_PACKBITS }, 0, 0, VE, "compression_algo" }, - { "raw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TIFF_RAW }, 0, 0, VE, "compression_algo" }, - { "lzw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TIFF_LZW }, 0, 0, VE, "compression_algo" }, - { "deflate", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TIFF_DEFLATE }, 0, 0, VE, "compression_algo" }, + { "compression_algo", NULL, OFFSET(compr), AV_OPT_TYPE_INT, { .i64 = TIFF_PACKBITS }, TIFF_RAW, TIFF_DEFLATE, VE, .unit = "compression_algo" }, + { "packbits", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TIFF_PACKBITS }, 0, 0, VE, .unit = "compression_algo" }, + { "raw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TIFF_RAW }, 0, 0, VE, .unit = "compression_algo" }, + { "lzw", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TIFF_LZW }, 0, 0, VE, .unit = "compression_algo" }, + { "deflate", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TIFF_DEFLATE }, 0, 0, VE, .unit = "compression_algo" }, { NULL }, }; diff --git a/libavcodec/tmv.c b/libavcodec/tmv.c index cdb83452e50..2a7e1a105f1 100644 --- a/libavcodec/tmv.c +++ b/libavcodec/tmv.c @@ -57,10 +57,14 @@ static int tmv_decode_frame(AVCodecContext *avctx, AVFrame *frame, } frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; dst = frame->data[0]; +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(frame->data[1], ff_cga_palette, 16 * 4); memset(frame->data[1] + 16 * 4, 0, AVPALETTE_SIZE - 16 * 4); diff --git a/libavcodec/truemotion1.c b/libavcodec/truemotion1.c index 6b0ee225691..784576d01b3 100644 --- a/libavcodec/truemotion1.c +++ b/libavcodec/truemotion1.c @@ -408,6 +408,11 @@ static int truemotion1_decode_header(TrueMotion1Context *s) return AVERROR_PATCHWELCOME; } + if (s->h & 3) { + avpriv_request_sample(s->avctx, "Frame with height not being a multiple of 4"); + return AVERROR_PATCHWELCOME; + } + if (s->w != s->avctx->width || s->h != s->avctx->height || new_pix_fmt != s->avctx->pix_fmt) { av_frame_unref(s->frame); diff --git a/libavcodec/truemotion2.c b/libavcodec/truemotion2.c index b168b9cda14..366d8aefc13 100644 --- a/libavcodec/truemotion2.c +++ b/libavcodec/truemotion2.c @@ -198,7 +198,7 @@ static int tm2_build_huff_table(TM2Context *ctx, TM2Codes *code) /* convert codes to vlc_table */ if (res >= 0) { - res = ff_init_vlc_from_lengths(&code->vlc, huff.max_bits, huff.max_num, + res = ff_vlc_init_from_lengths(&code->vlc, huff.max_bits, huff.max_num, huff.lens, sizeof(huff.lens[0]), NULL, 0, 0, 0, 0, ctx->avctx); if (res < 0) @@ -222,8 +222,7 @@ static int tm2_build_huff_table(TM2Context *ctx, TM2Codes *code) static void tm2_free_codes(TM2Codes *code) { av_free(code->recode); - if (code->vlc.table) - ff_free_vlc(&code->vlc); + ff_vlc_free(&code->vlc); } static inline int tm2_get_token(GetBitContext *gb, TM2Codes *code) @@ -930,11 +929,13 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, } offset += t; } - p->key_frame = tm2_decode_blocks(l, p); - if (p->key_frame) + if (tm2_decode_blocks(l, p)) { + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; - else + } else { + p->flags &= ~AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_P; + } l->cur = !l->cur; *got_frame = 1; diff --git a/libavcodec/truemotion2rt.c b/libavcodec/truemotion2rt.c index c6015b278a8..4f8590fc82c 100644 --- a/libavcodec/truemotion2rt.c +++ b/libavcodec/truemotion2rt.c @@ -202,7 +202,7 @@ static int truemotion2rt_decode_frame(AVCodecContext *avctx, AVFrame *p, } p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; diff --git a/libavcodec/tscc.c b/libavcodec/tscc.c index 0ebe641ab1b..346d93e1f24 100644 --- a/libavcodec/tscc.c +++ b/libavcodec/tscc.c @@ -106,7 +106,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, /* make the palette available on the way out */ if (c->avctx->pix_fmt == AV_PIX_FMT_PAL8) { +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = palette_has_changed; +FF_ENABLE_DEPRECATION_WARNINGS +#endif memcpy(frame->data[1], c->pal, AVPALETTE_SIZE); } diff --git a/libavcodec/tscc2.c b/libavcodec/tscc2.c index 6e4fe503c18..168efe93f66 100644 --- a/libavcodec/tscc2.c +++ b/libavcodec/tscc2.c @@ -61,9 +61,9 @@ static av_cold void tscc2_init_vlc(VLC *vlc, int *offset, int nb_codes, vlc->table = &vlc_buf[*offset]; vlc->table_allocated = FF_ARRAY_ELEMS(vlc_buf) - *offset; - ff_init_vlc_from_lengths(vlc, TSCC2_VLC_BITS, nb_codes, + ff_vlc_init_from_lengths(vlc, TSCC2_VLC_BITS, nb_codes, lens, 1, syms, sym_length, sym_length, 0, - INIT_VLC_STATIC_OVERLONG | INIT_VLC_OUTPUT_LE, NULL); + VLC_INIT_STATIC_OVERLONG | VLC_INIT_OUTPUT_LE, NULL); *offset += vlc->table_size; } diff --git a/libavcodec/tta.c b/libavcodec/tta.c index 072e86729d5..7763ed7ffc9 100644 --- a/libavcodec/tta.c +++ b/libavcodec/tta.c @@ -211,7 +211,7 @@ static av_cold int tta_decode_init(AVCodecContext * avctx) av_log(avctx, AV_LOG_DEBUG, "data_length: %d frame_length: %d last: %d total: %d\n", s->data_length, s->frame_length, s->last_frame_length, total_frames); - if(s->frame_length >= UINT_MAX / (s->channels * sizeof(int32_t))){ + if (s->frame_length >= UINT_MAX / (s->channels * sizeof(int32_t))) { av_log(avctx, AV_LOG_ERROR, "frame_length too large\n"); return AVERROR_INVALIDDATA; } @@ -306,14 +306,14 @@ static int tta_decode_frame(AVCodecContext *avctx, AVFrame *frame, rice->sum1 += value - (rice->sum1 >> 4); if (rice->k1 > 0 && rice->sum1 < ff_tta_shift_16[rice->k1]) rice->k1--; - else if(rice->sum1 > ff_tta_shift_16[rice->k1 + 1]) + else if (rice->sum1 > ff_tta_shift_16[rice->k1 + 1]) rice->k1++; value += ff_tta_shift_1[rice->k0]; default: rice->sum0 += value - (rice->sum0 >> 4); if (rice->k0 > 0 && rice->sum0 < ff_tta_shift_16[rice->k0]) rice->k0--; - else if(rice->sum0 > ff_tta_shift_16[rice->k0 + 1]) + else if (rice->sum0 > ff_tta_shift_16[rice->k0 + 1]) rice->k0++; } @@ -399,7 +399,8 @@ static int tta_decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; } -static av_cold int tta_decode_close(AVCodecContext *avctx) { +static av_cold int tta_decode_close(AVCodecContext *avctx) +{ TTAContext *s = avctx->priv_data; if (s->bps < 3) diff --git a/libavcodec/ttadata.c b/libavcodec/ttadata.c index 297d7094808..2137a230336 100644 --- a/libavcodec/ttadata.c +++ b/libavcodec/ttadata.c @@ -47,7 +47,8 @@ void ff_tta_rice_init(TTARice *c, uint32_t k0, uint32_t k1) c->sum1 = ff_tta_shift_16[k1]; } -void ff_tta_filter_init(TTAFilter *c, int32_t shift) { +void ff_tta_filter_init(TTAFilter *c, int32_t shift) +{ memset(c, 0, sizeof(TTAFilter)); c->shift = shift; c->round = ff_tta_shift_1[shift-1]; diff --git a/libavcodec/ttadsp.c b/libavcodec/ttadsp.c index fe9e3c69e48..5dda19587c2 100644 --- a/libavcodec/ttadsp.c +++ b/libavcodec/ttadsp.c @@ -22,7 +22,8 @@ static void tta_filter_process_c(int32_t *qmi, int32_t *dx, int32_t *dl, int32_t *error, int32_t *in, int32_t shift, - int32_t round) { + int32_t round) +{ uint32_t *qm = qmi; if (*error < 0) { diff --git a/libavcodec/ttmlenc.c b/libavcodec/ttmlenc.c index fb05c389686..779cb751993 100644 --- a/libavcodec/ttmlenc.c +++ b/libavcodec/ttmlenc.c @@ -29,11 +29,9 @@ #include "avcodec.h" #include "codec_internal.h" -#include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/internal.h" #include "ass_split.h" -#include "ass.h" #include "ttmlenc.h" typedef struct { @@ -45,7 +43,7 @@ typedef struct { static void ttml_text_cb(void *priv, const char *text, int len) { TTMLContext *s = priv; - AVBPrint cur_line = { 0 }; + AVBPrint cur_line; AVBPrint *buffer = &s->buffer; av_bprint_init(&cur_line, len, AV_BPRINT_SIZE_UNLIMITED); @@ -84,7 +82,7 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, ASSDialog *dialog; int i; - av_bprint_clear(&s->buffer); + av_bprint_init_for_buffer(&s->buffer, buf, bufsize); for (i=0; inum_rects; i++) { const char *ass = sub->rects[i]->ass; @@ -129,14 +127,9 @@ static int ttml_encode_frame(AVCodecContext *avctx, uint8_t *buf, ff_ass_free_dialog(&dialog); } - if (!av_bprint_is_complete(&s->buffer)) - return AVERROR(ENOMEM); if (!s->buffer.len) return 0; - - // force null-termination, so in case our destination buffer is - // too small, the return value is larger than bufsize minus null. - if (av_strlcpy(buf, s->buffer.str, bufsize) > bufsize - 1) { + if (!av_bprint_is_complete(&s->buffer)) { av_log(avctx, AV_LOG_ERROR, "Buffer too small for TTML event.\n"); return AVERROR_BUFFER_TOO_SMALL; } @@ -150,8 +143,6 @@ static av_cold int ttml_encode_close(AVCodecContext *avctx) ff_ass_split_free(s->ass_ctx); - av_bprint_finalize(&s->buffer, NULL); - return 0; } @@ -306,6 +297,7 @@ static int ttml_write_header_content(AVCodecContext *avctx) const size_t base_extradata_size = TTMLENC_EXTRADATA_SIGNATURE_SIZE + 1 + AV_INPUT_BUFFER_PADDING_SIZE; size_t additional_extradata_size = 0; + int ret; if (script_info.play_res_x <= 0 || script_info.play_res_y <= 0) { av_log(avctx, AV_LOG_ERROR, @@ -314,6 +306,8 @@ static int ttml_write_header_content(AVCodecContext *avctx) return AVERROR_INVALIDDATA; } + av_bprint_init(&s->buffer, 0, INT_MAX - base_extradata_size); + // write the first string in extradata, attributes in the base "tt" element. av_bprintf(&s->buffer, TTML_DEFAULT_NAMESPACING); // the cell resolution is in character cells, so not exactly 1:1 against @@ -329,10 +323,10 @@ static int ttml_write_header_content(AVCodecContext *avctx) av_bprintf(&s->buffer, " \n"); for (int i = 0; i < ass->styles_count; i++) { - int ret = ttml_write_region(avctx, &s->buffer, script_info, - ass->styles[i]); + ret = ttml_write_region(avctx, &s->buffer, script_info, + ass->styles[i]); if (ret < 0) - return ret; + goto fail; } av_bprintf(&s->buffer, " \n"); @@ -340,14 +334,16 @@ static int ttml_write_header_content(AVCodecContext *avctx) av_bprint_chars(&s->buffer, '\0', 1); if (!av_bprint_is_complete(&s->buffer)) { - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail; } additional_extradata_size = s->buffer.len; if (!(avctx->extradata = av_mallocz(base_extradata_size + additional_extradata_size))) { - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail; } avctx->extradata_size = @@ -355,13 +351,14 @@ static int ttml_write_header_content(AVCodecContext *avctx) memcpy(avctx->extradata, TTMLENC_EXTRADATA_SIGNATURE, TTMLENC_EXTRADATA_SIGNATURE_SIZE); - if (additional_extradata_size) - memcpy(avctx->extradata + TTMLENC_EXTRADATA_SIGNATURE_SIZE, - s->buffer.str, additional_extradata_size); + memcpy(avctx->extradata + TTMLENC_EXTRADATA_SIGNATURE_SIZE, + s->buffer.str, additional_extradata_size); - av_bprint_clear(&s->buffer); + ret = 0; +fail: + av_bprint_finalize(&s->buffer, NULL); - return 0; + return ret; } static av_cold int ttml_encode_init(AVCodecContext *avctx) @@ -370,8 +367,6 @@ static av_cold int ttml_encode_init(AVCodecContext *avctx) int ret = AVERROR_BUG; s->avctx = avctx; - av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); - if (!(s->ass_ctx = ff_ass_split(avctx->subtitle_header))) { return AVERROR_INVALIDDATA; } diff --git a/libavcodec/utils.c b/libavcodec/utils.c index 6d40b8ab483..7914f799041 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -35,9 +35,12 @@ #include "libavutil/pixfmt.h" #include "avcodec.h" #include "codec.h" +#include "codec_desc.h" #include "codec_internal.h" +#include "codec_par.h" #include "decode.h" #include "hwconfig.h" +#include "refstruct.h" #include "thread.h" #include "threadframe.h" #include "internal.h" @@ -256,6 +259,9 @@ void avcodec_align_dimensions2(AVCodecContext *s, int *width, int *height, if (s->codec_id == AV_CODEC_ID_SVQ1) { w_align = 64; h_align = 64; + } else if (s->codec_id == AV_CODEC_ID_SNOW) { + w_align = 16; + h_align = 16; } break; case AV_PIX_FMT_RGB555: @@ -359,17 +365,6 @@ void avcodec_align_dimensions(AVCodecContext *s, int *width, int *height) align = FFMAX3(align, linesize_align[1], linesize_align[2]); *width = FFALIGN(*width, align); } -#if FF_API_AVCODEC_CHROMA_POS -int avcodec_enum_to_chroma_pos(int *xpos, int *ypos, enum AVChromaLocation pos) -{ - return av_chroma_location_enum_to_pos(xpos, ypos, pos); -} - -enum AVChromaLocation avcodec_chroma_pos_to_enum(int xpos, int ypos) -{ - return av_chroma_location_pos_to_enum(xpos, ypos); -} -#endif int avcodec_fill_audio_frame(AVFrame *frame, int nb_channels, enum AVSampleFormat sample_fmt, const uint8_t *buf, @@ -406,34 +401,6 @@ int avcodec_fill_audio_frame(AVFrame *frame, int nb_channels, return ret; } -void ff_color_frame(AVFrame *frame, const int c[4]) -{ - const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); - int p, y; - - av_assert0(desc->flags & AV_PIX_FMT_FLAG_PLANAR); - - for (p = 0; pnb_components; p++) { - uint8_t *dst = frame->data[p]; - int is_chroma = p == 1 || p == 2; - int bytes = is_chroma ? AV_CEIL_RSHIFT(frame->width, desc->log2_chroma_w) : frame->width; - int height = is_chroma ? AV_CEIL_RSHIFT(frame->height, desc->log2_chroma_h) : frame->height; - if (desc->comp[0].depth >= 9) { - ((uint16_t*)dst)[0] = c[p]; - av_memcpy_backptr(dst + 2, 2, bytes - 2); - dst += frame->linesize[p]; - for (y = 1; y < height; y++) { - memcpy(dst, frame->data[p], 2*bytes); - dst += frame->linesize[p]; - } - } else { - for (y = 0; y < height; y++) { - memset(dst, c[p], bytes); - dst += frame->linesize[p]; - } - } - } -} int avpriv_codec_get_cap_skip_frame_fill_param(const AVCodec *codec){ return !!(ffcodec(codec)->caps_internal & FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM); @@ -462,10 +429,10 @@ const char *avcodec_get_name(enum AVCodecID id) const char *av_get_profile_name(const AVCodec *codec, int profile) { const AVProfile *p; - if (profile == FF_PROFILE_UNKNOWN || !codec->profiles) + if (profile == AV_PROFILE_UNKNOWN || !codec->profiles) return NULL; - for (p = codec->profiles; p->profile != FF_PROFILE_UNKNOWN; p++) + for (p = codec->profiles; p->profile != AV_PROFILE_UNKNOWN; p++) if (p->profile == profile) return p->name; @@ -477,10 +444,10 @@ const char *avcodec_profile_name(enum AVCodecID codec_id, int profile) const AVCodecDescriptor *desc = avcodec_descriptor_get(codec_id); const AVProfile *p; - if (profile == FF_PROFILE_UNKNOWN || !desc || !desc->profiles) + if (profile == AV_PROFILE_UNKNOWN || !desc || !desc->profiles) return NULL; - for (p = desc->profiles; p->profile != FF_PROFILE_UNKNOWN; p++) + for (p = desc->profiles; p->profile != AV_PROFILE_UNKNOWN; p++) if (p->profile == profile) return p->name; @@ -820,12 +787,7 @@ int av_get_audio_frame_duration(AVCodecContext *avctx, int frame_bytes) { int channels = avctx->ch_layout.nb_channels; int duration; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (!channels) - channels = avctx->channels; -FF_ENABLE_DEPRECATION_WARNINGS -#endif + duration = get_audio_frame_duration(avctx->codec_id, avctx->sample_rate, channels, avctx->block_align, avctx->codec_tag, avctx->bits_per_coded_sample, @@ -838,12 +800,7 @@ int av_get_audio_frame_duration2(AVCodecParameters *par, int frame_bytes) { int channels = par->ch_layout.nb_channels; int duration; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (!channels) - channels = par->channels; -FF_ENABLE_DEPRECATION_WARNINGS -#endif + duration = get_audio_frame_duration(par->codec_id, par->sample_rate, channels, par->block_align, par->codec_tag, par->bits_per_coded_sample, @@ -906,11 +863,24 @@ int ff_thread_ref_frame(ThreadFrame *dst, const ThreadFrame *src) av_assert0(!dst->progress); - if (src->progress && - !(dst->progress = av_buffer_ref(src->progress))) { - ff_thread_release_ext_buffer(dst->owner[0], dst); - return AVERROR(ENOMEM); - } + if (src->progress) + dst->progress = ff_refstruct_ref(src->progress); + + return 0; +} + +int ff_thread_replace_frame(ThreadFrame *dst, const ThreadFrame *src) +{ + int ret; + + dst->owner[0] = src->owner[0]; + dst->owner[1] = src->owner[1]; + + ret = av_frame_replace(dst->f, src->f); + if (ret < 0) + return ret; + + ff_refstruct_replace(&dst->progress, src->progress); return 0; } @@ -928,13 +898,7 @@ int ff_thread_get_ext_buffer(AVCodecContext *avctx, ThreadFrame *f, int flags) return ff_get_buffer(avctx, f->f, flags); } -void ff_thread_release_buffer(AVCodecContext *avctx, AVFrame *f) -{ - if (f) - av_frame_unref(f); -} - -void ff_thread_release_ext_buffer(AVCodecContext *avctx, ThreadFrame *f) +void ff_thread_release_ext_buffer(ThreadFrame *f) { f->owner[0] = f->owner[1] = NULL; if (f->f) @@ -978,9 +942,9 @@ void ff_thread_report_progress2(AVCodecContext *avctx, int field, int thread, in #endif -const uint8_t *avpriv_find_start_code(const uint8_t *av_restrict p, +const uint8_t *avpriv_find_start_code(const uint8_t *restrict p, const uint8_t *end, - uint32_t *av_restrict state) + uint32_t *restrict state) { int i; @@ -1025,37 +989,6 @@ AVCPBProperties *av_cpb_properties_alloc(size_t *size) return props; } -AVCPBProperties *ff_add_cpb_side_data(AVCodecContext *avctx) -{ - AVPacketSideData *tmp; - AVCPBProperties *props; - size_t size; - int i; - - for (i = 0; i < avctx->nb_coded_side_data; i++) - if (avctx->coded_side_data[i].type == AV_PKT_DATA_CPB_PROPERTIES) - return (AVCPBProperties *)avctx->coded_side_data[i].data; - - props = av_cpb_properties_alloc(&size); - if (!props) - return NULL; - - tmp = av_realloc_array(avctx->coded_side_data, avctx->nb_coded_side_data + 1, sizeof(*tmp)); - if (!tmp) { - av_freep(&props); - return NULL; - } - - avctx->coded_side_data = tmp; - avctx->nb_coded_side_data++; - - avctx->coded_side_data[avctx->nb_coded_side_data - 1].type = AV_PKT_DATA_CPB_PROPERTIES; - avctx->coded_side_data[avctx->nb_coded_side_data - 1].data = (uint8_t*)props; - avctx->coded_side_data[avctx->nb_coded_side_data - 1].size = size; - - return props; -} - static unsigned bcd2uint(uint8_t bcd) { unsigned low = bcd & 0xf; @@ -1149,22 +1082,3 @@ int64_t ff_guess_coded_bitrate(AVCodecContext *avctx) return bitrate; } - -int ff_int_from_list_or_default(void *ctx, const char * val_name, int val, - const int * array_valid_values, int default_value) -{ - int i = 0, ref_val; - - while (1) { - ref_val = array_valid_values[i]; - if (ref_val == INT_MAX) - break; - if (val == ref_val) - return val; - i++; - } - /* val is not a valid value */ - av_log(ctx, AV_LOG_DEBUG, - "%s %d are not supported. Set to default value : %d\n", val_name, val, default_value); - return default_value; -} diff --git a/libavcodec/utvideo.h b/libavcodec/utvideo.h index 9da9329ff33..b081b50a2f6 100644 --- a/libavcodec/utvideo.h +++ b/libavcodec/utvideo.h @@ -27,12 +27,7 @@ * Common Ut Video header */ -#include "libavutil/common.h" -#include "avcodec.h" -#include "bswapdsp.h" -#include "utvideodsp.h" -#include "lossless_videodsp.h" -#include "lossless_videoencdsp.h" +#include "libavutil/macros.h" enum { PRED_NONE = 0, @@ -61,31 +56,4 @@ enum { UTVIDEO_444 = MKTAG('Y', 'V', '2', '4'), }; -typedef struct UtvideoContext { - const AVClass *class; - AVCodecContext *avctx; - UTVideoDSPContext utdsp; - BswapDSPContext bdsp; - LLVidDSPContext llviddsp; - LLVidEncDSPContext llvidencdsp; - - uint32_t frame_info_size, flags, frame_info, offset; - int planes; - int slices; - int compression; - int interlaced; - int frame_pred; - int pro; - int pack; - - ptrdiff_t slice_stride; - uint8_t *slice_bits, *slice_buffer[4]; - int slice_bits_size; - - const uint8_t *packed_stream[4][256]; - size_t packed_stream_size[4][256]; - const uint8_t *control_stream[4][256]; - size_t control_stream_size[4][256]; -} UtvideoContext; - #endif /* AVCODEC_UTVIDEO_H */ diff --git a/libavcodec/utvideodec.c b/libavcodec/utvideodec.c index 83120d1b22c..0c2e67e282b 100644 --- a/libavcodec/utvideodec.c +++ b/libavcodec/utvideodec.c @@ -37,8 +37,35 @@ #include "bytestream.h" #include "codec_internal.h" #include "get_bits.h" +#include "lossless_videodsp.h" #include "thread.h" #include "utvideo.h" +#include "utvideodsp.h" + +typedef struct UtvideoContext { + AVCodecContext *avctx; + UTVideoDSPContext utdsp; + BswapDSPContext bdsp; + LLVidDSPContext llviddsp; + + uint32_t frame_info_size, flags, frame_info, offset; + int planes; + int slices; + int compression; + int interlaced; + int frame_pred; + int pro; + int pack; + + uint8_t *slice_bits; + int slice_bits_size; + void *buffer; + + const uint8_t *packed_stream[4][256]; + size_t packed_stream_size[4][256]; + const uint8_t *control_stream[4][256]; + size_t control_stream_size[4][256]; +} UtvideoContext; typedef struct HuffEntry { uint8_t len; @@ -46,7 +73,7 @@ typedef struct HuffEntry { } HuffEntry; static int build_huff(UtvideoContext *c, const uint8_t *src, VLC *vlc, - int *fsym, unsigned nb_elems) + VLC_MULTI *multi, int *fsym, unsigned nb_elems) { int i; HuffEntry he[1024]; @@ -82,11 +109,35 @@ static int build_huff(UtvideoContext *c, const uint8_t *src, VLC *vlc, he[--codes_count[bits[i]]] = (HuffEntry) { bits[i], i }; #define VLC_BITS 11 - return ff_init_vlc_from_lengths(vlc, VLC_BITS, codes_count[0], + return ff_vlc_init_multi_from_lengths(vlc, multi, VLC_BITS, nb_elems, codes_count[0], &he[0].len, sizeof(*he), &he[0].sym, sizeof(*he), 2, 0, 0, c->avctx); } +#define READ_PLANE(b, end) \ +{ \ + buf = !use_pred ? dest : c->buffer; \ + i = 0; \ + for (; CACHED_BITSTREAM_READER && i < width-end && get_bits_left(&gb) > 0;) {\ + ret = get_vlc_multi(&gb, (uint8_t *)buf + i * b, multi.table, \ + vlc.table, VLC_BITS, 3, b); \ + if (ret > 0) \ + i += ret; \ + if (ret <= 0) \ + goto fail; \ + } \ + for (; i < width && get_bits_left(&gb) > 0; i++) \ + buf[i] = get_vlc2(&gb, vlc.table, VLC_BITS, 3); \ + if (use_pred) { \ + if (b == 2) \ + c->llviddsp.add_left_pred_int16((uint16_t *)dest, (const uint16_t *)buf, 0x3ff, width, prev); \ + else \ + c->llviddsp.add_left_pred((uint8_t *)dest, (const uint8_t *)buf, width, prev); \ + } \ + prev = dest[width-1]; \ + dest += stride; \ +} + static int decode_plane10(UtvideoContext *c, int plane_no, uint16_t *dst, ptrdiff_t stride, int width, int height, @@ -95,11 +146,12 @@ static int decode_plane10(UtvideoContext *c, int plane_no, { int i, j, slice, pix, ret; int sstart, send; + VLC_MULTI multi; VLC vlc; GetBitContext gb; int prev, fsym; - if ((ret = build_huff(c, huff, &vlc, &fsym, 1024)) < 0) { + if ((ret = build_huff(c, huff, &vlc, &multi, &fsym, 1024)) < 0) { av_log(c->avctx, AV_LOG_ERROR, "Cannot build Huffman codes\n"); return ret; } @@ -131,7 +183,7 @@ static int decode_plane10(UtvideoContext *c, int plane_no, send = 0; for (slice = 0; slice < c->slices; slice++) { - uint16_t *dest; + uint16_t *dest, *buf; int slice_data_start, slice_data_end, slice_size; sstart = send; @@ -156,37 +208,20 @@ static int decode_plane10(UtvideoContext *c, int plane_no, init_get_bits(&gb, c->slice_bits, slice_size * 8); prev = 0x200; - for (j = sstart; j < send; j++) { - for (i = 0; i < width; i++) { - pix = get_vlc2(&gb, vlc.table, VLC_BITS, 3); - if (pix < 0) { - av_log(c->avctx, AV_LOG_ERROR, "Decoding error\n"); - goto fail; - } - if (use_pred) { - prev += pix; - prev &= 0x3FF; - pix = prev; - } - dest[i] = pix; - } - dest += stride; - if (get_bits_left(&gb) < 0) { - av_log(c->avctx, AV_LOG_ERROR, - "Slice decoding ran out of bits\n"); - goto fail; - } - } + for (j = sstart; j < send; j++) + READ_PLANE(2, 3) if (get_bits_left(&gb) > 32) av_log(c->avctx, AV_LOG_WARNING, "%d bits left after decoding slice\n", get_bits_left(&gb)); } - ff_free_vlc(&vlc); + ff_vlc_free(&vlc); + ff_vlc_free_multi(&multi); return 0; fail: - ff_free_vlc(&vlc); + ff_vlc_free(&vlc); + ff_vlc_free_multi(&multi); return AVERROR_INVALIDDATA; } @@ -207,6 +242,7 @@ static int decode_plane(UtvideoContext *c, int plane_no, { int i, j, slice, pix; int sstart, send; + VLC_MULTI multi; VLC vlc; GetBitContext gb; int ret, prev, fsym; @@ -259,7 +295,7 @@ static int decode_plane(UtvideoContext *c, int plane_no, return 0; } - if (build_huff(c, src, &vlc, &fsym, 256)) { + if (build_huff(c, src, &vlc, &multi, &fsym, 256)) { av_log(c->avctx, AV_LOG_ERROR, "Cannot build Huffman codes\n"); return AVERROR_INVALIDDATA; } @@ -292,7 +328,7 @@ static int decode_plane(UtvideoContext *c, int plane_no, send = 0; for (slice = 0; slice < c->slices; slice++) { - uint8_t *dest; + uint8_t *dest, *buf; int slice_data_start, slice_data_end, slice_size; sstart = send; @@ -317,36 +353,20 @@ static int decode_plane(UtvideoContext *c, int plane_no, init_get_bits(&gb, c->slice_bits, slice_size * 8); prev = 0x80; - for (j = sstart; j < send; j++) { - for (i = 0; i < width; i++) { - pix = get_vlc2(&gb, vlc.table, VLC_BITS, 3); - if (pix < 0) { - av_log(c->avctx, AV_LOG_ERROR, "Decoding error\n"); - goto fail; - } - if (use_pred) { - prev += pix; - pix = prev; - } - dest[i] = pix; - } - if (get_bits_left(&gb) < 0) { - av_log(c->avctx, AV_LOG_ERROR, - "Slice decoding ran out of bits\n"); - goto fail; - } - dest += stride; - } + for (j = sstart; j < send; j++) + READ_PLANE(1, 5) if (get_bits_left(&gb) > 32) av_log(c->avctx, AV_LOG_WARNING, "%d bits left after decoding slice\n", get_bits_left(&gb)); } - ff_free_vlc(&vlc); + ff_vlc_free(&vlc); + ff_vlc_free_multi(&multi); return 0; fail: - ff_free_vlc(&vlc); + ff_vlc_free(&vlc); + ff_vlc_free_multi(&multi); return AVERROR_INVALIDDATA; } @@ -869,9 +889,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, break; } - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; - frame->interlaced_frame = !!c->interlaced; + if (c->interlaced) + frame->flags |= AV_FRAME_FLAG_INTERLACED; *got_frame = 1; @@ -1038,6 +1059,10 @@ static av_cold int decode_init(AVCodecContext *avctx) return AVERROR_INVALIDDATA; } + c->buffer = av_calloc(avctx->width + 8, c->pro?2:1); + if (!c->buffer) + return AVERROR(ENOMEM); + return 0; } @@ -1046,6 +1071,7 @@ static av_cold int decode_end(AVCodecContext *avctx) UtvideoContext * const c = avctx->priv_data; av_freep(&c->slice_bits); + av_freep(&c->buffer); return 0; } diff --git a/libavcodec/utvideodsp.c b/libavcodec/utvideodsp.c index 6db0abf8da1..b63dafbe17f 100644 --- a/libavcodec/utvideodsp.c +++ b/libavcodec/utvideodsp.c @@ -77,7 +77,9 @@ av_cold void ff_utvideodsp_init(UTVideoDSPContext *c) c->restore_rgb_planes = restore_rgb_planes_c; c->restore_rgb_planes10 = restore_rgb_planes10_c; -#if ARCH_X86 +#if ARCH_RISCV + ff_utvideodsp_init_riscv(c); +#elif ARCH_X86 ff_utvideodsp_init_x86(c); #endif } diff --git a/libavcodec/utvideodsp.h b/libavcodec/utvideodsp.h index a3d2550dce4..10961da6c41 100644 --- a/libavcodec/utvideodsp.h +++ b/libavcodec/utvideodsp.h @@ -34,6 +34,7 @@ typedef struct UTVideoDSPContext { } UTVideoDSPContext; void ff_utvideodsp_init(UTVideoDSPContext *c); +void ff_utvideodsp_init_riscv(UTVideoDSPContext *c); void ff_utvideodsp_init_x86(UTVideoDSPContext *c); #endif /* AVCODEC_UTVIDEODSP_H */ diff --git a/libavcodec/utvideoenc.c b/libavcodec/utvideoenc.c index 6e87bbc2b6c..ad8d73fbb42 100644 --- a/libavcodec/utvideoenc.c +++ b/libavcodec/utvideoenc.c @@ -33,11 +33,28 @@ #include "encode.h" #include "bswapdsp.h" #include "bytestream.h" +#include "lossless_videoencdsp.h" #include "put_bits.h" #include "mathops.h" #include "utvideo.h" #include "huffman.h" +typedef struct UtvideoContext { + const AVClass *class; + BswapDSPContext bdsp; + LLVidEncDSPContext llvidencdsp; + + uint32_t frame_info_size, flags; + int planes; + int slices; + int compression; + int frame_pred; + + ptrdiff_t slice_stride; + uint8_t *slice_bits, *slice_buffer[4]; + int slice_bits_size; +} UtvideoContext; + typedef struct HuffEntry { uint16_t sym; uint8_t len; @@ -76,7 +93,6 @@ static av_cold int utvideo_encode_init(AVCodecContext *avctx) int i, subsampled_height; uint32_t original_format; - c->avctx = avctx; c->frame_info_size = 4; c->slice_stride = FFALIGN(avctx->width, 32); @@ -223,7 +239,7 @@ static av_cold int utvideo_encode_init(AVCodecContext *avctx) * - Compression mode (none/huff) * And write the flags. */ - c->flags = (c->slices - 1) << 24; + c->flags = (c->slices - 1U) << 24; c->flags |= 0 << 11; // bit field to signal interlaced encoding mode c->flags |= c->compression; @@ -627,11 +643,11 @@ static int utvideo_encode_frame(AVCodecContext *avctx, AVPacket *pkt, #define OFFSET(x) offsetof(UtvideoContext, x) #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { -{ "pred", "Prediction method", OFFSET(frame_pred), AV_OPT_TYPE_INT, { .i64 = PRED_LEFT }, PRED_NONE, PRED_MEDIAN, VE, "pred" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRED_NONE }, INT_MIN, INT_MAX, VE, "pred" }, - { "left", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRED_LEFT }, INT_MIN, INT_MAX, VE, "pred" }, - { "gradient", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRED_GRADIENT }, INT_MIN, INT_MAX, VE, "pred" }, - { "median", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRED_MEDIAN }, INT_MIN, INT_MAX, VE, "pred" }, +{ "pred", "Prediction method", OFFSET(frame_pred), AV_OPT_TYPE_INT, { .i64 = PRED_LEFT }, PRED_NONE, PRED_MEDIAN, VE, .unit = "pred" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRED_NONE }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "left", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRED_LEFT }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "gradient", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRED_GRADIENT }, INT_MIN, INT_MAX, VE, .unit = "pred" }, + { "median", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRED_MEDIAN }, INT_MIN, INT_MAX, VE, .unit = "pred" }, { NULL}, }; diff --git a/libavcodec/v210dec.c b/libavcodec/v210dec.c index 43b92f6ec93..d80fb4e7c6b 100644 --- a/libavcodec/v210dec.c +++ b/libavcodec/v210dec.c @@ -33,7 +33,7 @@ typedef struct ThreadData { AVFrame *frame; - uint8_t *buf; + const uint8_t *buf; int stride; } ThreadData; @@ -111,7 +111,7 @@ static int v210_decode_slice(AVCodecContext *avctx, void *arg, int jobnr, int th int stride = td->stride; int slice_start = (avctx->height * jobnr) / s->thread_count; int slice_end = (avctx->height * (jobnr+1)) / s->thread_count; - uint8_t *psrc = td->buf + stride * slice_start; + const uint8_t *psrc = td->buf + stride * slice_start; int16_t *py = (uint16_t*)frame->data[0] + slice_start * frame->linesize[0] / 2; int16_t *pu = (uint16_t*)frame->data[1] + slice_start * frame->linesize[1] / 2; int16_t *pv = (uint16_t*)frame->data[2] + slice_start * frame->linesize[2] / 2; @@ -187,11 +187,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; pic->pict_type = AV_PICTURE_TYPE_I; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; if (stride) { td.stride = stride; - td.buf = (uint8_t*)psrc; + td.buf = psrc; td.frame = pic; avctx->execute2(avctx, v210_decode_slice, &td, NULL, s->thread_count); } else { @@ -201,15 +201,16 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *pic, if (ret < 0) return ret; decode_row((const uint32_t *)psrc, (uint16_t *)pointers[0], (uint16_t *)pointers[1], (uint16_t *)pointers[2], avctx->width * avctx->height, s->unpack_frame); - av_image_copy(pic->data, pic->linesize, (const uint8_t **)pointers, linesizes, avctx->pix_fmt, avctx->width, avctx->height); + av_image_copy2(pic->data, pic->linesize, pointers, linesizes, + avctx->pix_fmt, avctx->width, avctx->height); av_freep(&pointers[0]); } if (avctx->field_order > AV_FIELD_PROGRESSIVE) { /* we have interlaced material flagged in container */ - pic->interlaced_frame = 1; + pic->flags |= AV_FRAME_FLAG_INTERLACED; if (avctx->field_order == AV_FIELD_TT || avctx->field_order == AV_FIELD_TB) - pic->top_field_first = 1; + pic->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; } *got_frame = 1; diff --git a/libavcodec/v210x.c b/libavcodec/v210x.c index 96594e2a43c..55630fa2fb9 100644 --- a/libavcodec/v210x.c +++ b/libavcodec/v210x.c @@ -62,7 +62,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *pic, vdst = (uint16_t *)pic->data[2]; yend = ydst + width; pic->pict_type = AV_PICTURE_TYPE_I; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; for (;;) { uint32_t v = av_be2ne32(*src++); diff --git a/libavcodec/v308dec.c b/libavcodec/v308dec.c index a81771fc5f4..4bc4ea4e21a 100644 --- a/libavcodec/v308dec.c +++ b/libavcodec/v308dec.c @@ -48,7 +48,7 @@ static int v308_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; y = pic->data[0]; diff --git a/libavcodec/v408dec.c b/libavcodec/v408dec.c index edc9976d94f..4d8bccd6501 100644 --- a/libavcodec/v408dec.c +++ b/libavcodec/v408dec.c @@ -29,10 +29,6 @@ static av_cold int v408_decode_init(AVCodecContext *avctx) { avctx->pix_fmt = AV_PIX_FMT_YUVA444P; -#if FF_API_AYUV_CODECID - if (avctx->codec_id==AV_CODEC_ID_AYUV) - av_log(avctx, AV_LOG_WARNING, "This decoder is deprecated and will be removed.\n"); -#endif return 0; } @@ -51,7 +47,7 @@ static int v408_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; y = pic->data[0]; @@ -61,20 +57,10 @@ static int v408_decode_frame(AVCodecContext *avctx, AVFrame *pic, for (i = 0; i < avctx->height; i++) { for (j = 0; j < avctx->width; j++) { -#if FF_API_AYUV_CODECID - if (avctx->codec_id==AV_CODEC_ID_AYUV) { - v[j] = *src++; - u[j] = *src++; - y[j] = *src++; - a[j] = *src++; - } else -#endif - { - u[j] = *src++; - y[j] = *src++; - v[j] = *src++; - a[j] = *src++; - } + u[j] = *src++; + y[j] = *src++; + v[j] = *src++; + a[j] = *src++; } y += pic->linesize[0]; @@ -88,19 +74,6 @@ static int v408_decode_frame(AVCodecContext *avctx, AVFrame *pic, return avpkt->size; } -#if FF_API_AYUV_CODECID -#if CONFIG_AYUV_DECODER -const FFCodec ff_ayuv_decoder = { - .p.name = "ayuv", - CODEC_LONG_NAME("Uncompressed packed MS 4:4:4:4"), - .p.type = AVMEDIA_TYPE_VIDEO, - .p.id = AV_CODEC_ID_AYUV, - .init = v408_decode_init, - FF_CODEC_DECODE_CB(v408_decode_frame), - .p.capabilities = AV_CODEC_CAP_DR1, -}; -#endif -#endif #if CONFIG_V408_DECODER const FFCodec ff_v408_decoder = { .p.name = "v408", diff --git a/libavcodec/v408enc.c b/libavcodec/v408enc.c index 1faac7cc363..c1bf0f61584 100644 --- a/libavcodec/v408enc.c +++ b/libavcodec/v408enc.c @@ -33,11 +33,6 @@ static av_cold int v408_encode_init(AVCodecContext *avctx) avctx->bits_per_coded_sample = 32; avctx->bit_rate = ff_guess_coded_bitrate(avctx); -#if FF_API_AYUV_CODECID - if (avctx->codec_id == AV_CODEC_ID_AYUV) - av_log(avctx, AV_LOG_WARNING, "This encoder is deprecated and will be removed.\n"); -#endif - return 0; } @@ -60,20 +55,10 @@ static int v408_encode_frame(AVCodecContext *avctx, AVPacket *pkt, for (i = 0; i < avctx->height; i++) { for (j = 0; j < avctx->width; j++) { -#if FF_API_AYUV_CODECID - if (avctx->codec_id==AV_CODEC_ID_AYUV) { - *dst++ = v[j]; - *dst++ = u[j]; - *dst++ = y[j]; - *dst++ = a[j]; - } else -#endif - { - *dst++ = u[j]; - *dst++ = y[j]; - *dst++ = v[j]; - *dst++ = a[j]; - } + *dst++ = u[j]; + *dst++ = y[j]; + *dst++ = v[j]; + *dst++ = a[j]; } y += pic->linesize[0]; u += pic->linesize[1]; @@ -87,20 +72,6 @@ static int v408_encode_frame(AVCodecContext *avctx, AVPacket *pkt, static const enum AVPixelFormat pix_fmt[] = { AV_PIX_FMT_YUVA444P, AV_PIX_FMT_NONE }; -#if FF_API_AYUV_CODECID -#if CONFIG_AYUV_ENCODER -const FFCodec ff_ayuv_encoder = { - .p.name = "ayuv", - CODEC_LONG_NAME("Uncompressed packed MS 4:4:4:4"), - .p.type = AVMEDIA_TYPE_VIDEO, - .p.id = AV_CODEC_ID_AYUV, - .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, - .init = v408_encode_init, - FF_CODEC_ENCODE_CB(v408_encode_frame), - .p.pix_fmts = pix_fmt, -}; -#endif -#endif #if CONFIG_V408_ENCODER const FFCodec ff_v408_encoder = { .p.name = "v408", diff --git a/libavcodec/v410dec.c b/libavcodec/v410dec.c index fb859e8cca8..35e4a8ae032 100644 --- a/libavcodec/v410dec.c +++ b/libavcodec/v410dec.c @@ -102,7 +102,7 @@ static int v410_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_thread_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; td.buf = src; diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c index 3f5471067a1..22771356998 100644 --- a/libavcodec/v4l2_buffers.c +++ b/libavcodec/v4l2_buffers.c @@ -427,7 +427,8 @@ int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf) return ret; /* 2. get frame information */ - frame->key_frame = !!(avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME); + if (avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME) + frame->flags |= AV_FRAME_FLAG_KEY; frame->color_primaries = v4l2_get_color_primaries(avbuf); frame->colorspace = v4l2_get_color_space(avbuf); frame->color_range = v4l2_get_color_range(avbuf); diff --git a/libavcodec/v4l2_context.c b/libavcodec/v4l2_context.c index a40be946904..f20f713e1d5 100644 --- a/libavcodec/v4l2_context.c +++ b/libavcodec/v4l2_context.c @@ -325,9 +325,13 @@ static V4L2Buffer* v4l2_dequeue_v4l2buf(V4L2Context *ctx, int timeout) /* 0. handle errors */ if (pfd.revents & POLLERR) { - /* if we are trying to get free buffers but none have been queued yet - no need to raise a warning */ + /* if we are trying to get free buffers but none have been queued yet, + * or if no buffers have been allocated yet, no need to raise a warning + */ if (timeout == 0) { + if (!ctx->buffers) + return NULL; + for (i = 0; i < ctx->num_buffers; i++) { if (ctx->buffers[i].status != V4L2BUF_AVAILABLE) av_log(logger(ctx), AV_LOG_WARNING, "%s POLLERR\n", ctx->name); diff --git a/libavcodec/v4l2_m2m.c b/libavcodec/v4l2_m2m.c index 602efb7a160..bac3eb0588e 100644 --- a/libavcodec/v4l2_m2m.c +++ b/libavcodec/v4l2_m2m.c @@ -255,7 +255,6 @@ static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context) if (s->fd >= 0) close(s->fd); - av_frame_unref(s->frame); av_frame_free(&s->frame); av_packet_unref(&s->buf_pkt); diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c index 4944d085119..aa2d759e1ea 100644 --- a/libavcodec/v4l2_m2m_dec.c +++ b/libavcodec/v4l2_m2m_dec.c @@ -253,7 +253,7 @@ static const AVOption options[] = { .bsfs = bsf_name, \ .p.capabilities = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_AVOID_PROBING, \ .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | \ - FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_CLEANUP, \ + FF_CODEC_CAP_INIT_CLEANUP, \ .p.wrapper_name = "v4l2m2m", \ } diff --git a/libavcodec/v4l2_m2m_enc.c b/libavcodec/v4l2_m2m_enc.c index 9a0837ecf3d..e08db5d5d36 100644 --- a/libavcodec/v4l2_m2m_enc.c +++ b/libavcodec/v4l2_m2m_enc.c @@ -103,17 +103,17 @@ static inline unsigned int v4l2_h264_profile_from_ff(int p) unsigned int ffmpeg_val; unsigned int v4l2_val; } profile[] = { - { FF_PROFILE_H264_CONSTRAINED_BASELINE, MPEG_VIDEO(H264_PROFILE_CONSTRAINED_BASELINE) }, - { FF_PROFILE_H264_HIGH_444_PREDICTIVE, MPEG_VIDEO(H264_PROFILE_HIGH_444_PREDICTIVE) }, - { FF_PROFILE_H264_HIGH_422_INTRA, MPEG_VIDEO(H264_PROFILE_HIGH_422_INTRA) }, - { FF_PROFILE_H264_HIGH_444_INTRA, MPEG_VIDEO(H264_PROFILE_HIGH_444_INTRA) }, - { FF_PROFILE_H264_HIGH_10_INTRA, MPEG_VIDEO(H264_PROFILE_HIGH_10_INTRA) }, - { FF_PROFILE_H264_HIGH_422, MPEG_VIDEO(H264_PROFILE_HIGH_422) }, - { FF_PROFILE_H264_BASELINE, MPEG_VIDEO(H264_PROFILE_BASELINE) }, - { FF_PROFILE_H264_EXTENDED, MPEG_VIDEO(H264_PROFILE_EXTENDED) }, - { FF_PROFILE_H264_HIGH_10, MPEG_VIDEO(H264_PROFILE_HIGH_10) }, - { FF_PROFILE_H264_MAIN, MPEG_VIDEO(H264_PROFILE_MAIN) }, - { FF_PROFILE_H264_HIGH, MPEG_VIDEO(H264_PROFILE_HIGH) }, + { AV_PROFILE_H264_CONSTRAINED_BASELINE, MPEG_VIDEO(H264_PROFILE_CONSTRAINED_BASELINE) }, + { AV_PROFILE_H264_HIGH_444_PREDICTIVE, MPEG_VIDEO(H264_PROFILE_HIGH_444_PREDICTIVE) }, + { AV_PROFILE_H264_HIGH_422_INTRA, MPEG_VIDEO(H264_PROFILE_HIGH_422_INTRA) }, + { AV_PROFILE_H264_HIGH_444_INTRA, MPEG_VIDEO(H264_PROFILE_HIGH_444_INTRA) }, + { AV_PROFILE_H264_HIGH_10_INTRA, MPEG_VIDEO(H264_PROFILE_HIGH_10_INTRA) }, + { AV_PROFILE_H264_HIGH_422, MPEG_VIDEO(H264_PROFILE_HIGH_422) }, + { AV_PROFILE_H264_BASELINE, MPEG_VIDEO(H264_PROFILE_BASELINE) }, + { AV_PROFILE_H264_EXTENDED, MPEG_VIDEO(H264_PROFILE_EXTENDED) }, + { AV_PROFILE_H264_HIGH_10, MPEG_VIDEO(H264_PROFILE_HIGH_10) }, + { AV_PROFILE_H264_MAIN, MPEG_VIDEO(H264_PROFILE_MAIN) }, + { AV_PROFILE_H264_HIGH, MPEG_VIDEO(H264_PROFILE_HIGH) }, }; int i; @@ -130,11 +130,11 @@ static inline int v4l2_mpeg4_profile_from_ff(int p) unsigned int ffmpeg_val; unsigned int v4l2_val; } profile[] = { - { FF_PROFILE_MPEG4_ADVANCED_CODING, MPEG_VIDEO(MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY) }, - { FF_PROFILE_MPEG4_ADVANCED_SIMPLE, MPEG_VIDEO(MPEG4_PROFILE_ADVANCED_SIMPLE) }, - { FF_PROFILE_MPEG4_SIMPLE_SCALABLE, MPEG_VIDEO(MPEG4_PROFILE_SIMPLE_SCALABLE) }, - { FF_PROFILE_MPEG4_SIMPLE, MPEG_VIDEO(MPEG4_PROFILE_SIMPLE) }, - { FF_PROFILE_MPEG4_CORE, MPEG_VIDEO(MPEG4_PROFILE_CORE) }, + { AV_PROFILE_MPEG4_ADVANCED_CODING, MPEG_VIDEO(MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY) }, + { AV_PROFILE_MPEG4_ADVANCED_SIMPLE, MPEG_VIDEO(MPEG4_PROFILE_ADVANCED_SIMPLE) }, + { AV_PROFILE_MPEG4_SIMPLE_SCALABLE, MPEG_VIDEO(MPEG4_PROFILE_SIMPLE_SCALABLE) }, + { AV_PROFILE_MPEG4_SIMPLE, MPEG_VIDEO(MPEG4_PROFILE_SIMPLE) }, + { AV_PROFILE_MPEG4_CORE, MPEG_VIDEO(MPEG4_PROFILE_CORE) }, }; int i; @@ -206,7 +206,7 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s) switch (avctx->codec_id) { case AV_CODEC_ID_H264: - if (avctx->profile != FF_PROFILE_UNKNOWN) { + if (avctx->profile != AV_PROFILE_UNKNOWN) { val = v4l2_h264_profile_from_ff(avctx->profile); if (val < 0) av_log(avctx, AV_LOG_WARNING, "h264 profile not found\n"); @@ -219,7 +219,7 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s) qmax = 51; break; case AV_CODEC_ID_MPEG4: - if (avctx->profile != FF_PROFILE_UNKNOWN) { + if (avctx->profile != AV_PROFILE_UNKNOWN) { val = v4l2_mpeg4_profile_from_ff(avctx->profile); if (val < 0) av_log(avctx, AV_LOG_WARNING, "mpeg4 profile not found\n"); diff --git a/libavcodec/vaapi_av1.c b/libavcodec/vaapi_av1.c index d0339b2705a..5bf81fc97fe 100644 --- a/libavcodec/vaapi_av1.c +++ b/libavcodec/vaapi_av1.c @@ -19,8 +19,7 @@ */ #include "libavutil/frame.h" -#include "libavutil/pixdesc.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vaapi_decode.h" #include "internal.h" #include "av1dec.h" @@ -76,19 +75,13 @@ static int vaapi_av1_decode_init(AVCodecContext *avctx) VAAPIAV1DecContext *ctx = avctx->internal->hwaccel_priv_data; ctx->tmp_frame = av_frame_alloc(); - if (!ctx->tmp_frame) { - av_log(avctx, AV_LOG_ERROR, - "Failed to allocate frame.\n"); + if (!ctx->tmp_frame) return AVERROR(ENOMEM); - } for (int i = 0; i < FF_ARRAY_ELEMS(ctx->ref_tab); i++) { ctx->ref_tab[i].frame = av_frame_alloc(); - if (!ctx->ref_tab[i].frame) { - av_log(avctx, AV_LOG_ERROR, - "Failed to allocate reference table frame %d.\n", i); + if (!ctx->ref_tab[i].frame) return AVERROR(ENOMEM); - } ctx->ref_tab[i].valid = 0; } @@ -99,15 +92,10 @@ static int vaapi_av1_decode_uninit(AVCodecContext *avctx) { VAAPIAV1DecContext *ctx = avctx->internal->hwaccel_priv_data; - if (ctx->tmp_frame->buf[0]) - ff_thread_release_buffer(avctx, ctx->tmp_frame); av_frame_free(&ctx->tmp_frame); - for (int i = 0; i < FF_ARRAY_ELEMS(ctx->ref_tab); i++) { - if (ctx->ref_tab[i].frame->buf[0]) - ff_thread_release_buffer(avctx, ctx->ref_tab[i].frame); + for (int i = 0; i < FF_ARRAY_ELEMS(ctx->ref_tab); i++) av_frame_free(&ctx->ref_tab[i].frame); - } return ff_vaapi_decode_uninit(avctx); } @@ -138,7 +126,7 @@ static int vaapi_av1_start_frame(AVCodecContext *avctx, if (apply_grain) { if (ctx->tmp_frame->buf[0]) - ff_thread_release_buffer(avctx, ctx->tmp_frame); + av_frame_unref(ctx->tmp_frame); err = ff_thread_get_buffer(avctx, ctx->tmp_frame, AV_GET_BUFFER_FLAG_REF); if (err < 0) goto fail; @@ -232,7 +220,7 @@ static int vaapi_av1_start_frame(AVCodecContext *avctx, .error_resilient_mode = frame_header->error_resilient_mode, .disable_cdf_update = frame_header->disable_cdf_update, .allow_screen_content_tools = frame_header->allow_screen_content_tools, - .force_integer_mv = frame_header->force_integer_mv, + .force_integer_mv = s->cur_frame.force_integer_mv, .allow_intrabc = frame_header->allow_intrabc, .use_superres = frame_header->use_superres, .allow_high_precision_mv = frame_header->allow_high_precision_mv, @@ -383,7 +371,7 @@ static int vaapi_av1_end_frame(AVCodecContext *avctx) for (int i = 0; i < AV1_NUM_REF_FRAMES; i++) { if (header->refresh_frame_flags & (1 << i)) { if (ctx->ref_tab[i].frame->buf[0]) - ff_thread_release_buffer(avctx, ctx->ref_tab[i].frame); + av_frame_unref(ctx->ref_tab[i].frame); if (apply_grain) { ret = av_frame_ref(ctx->ref_tab[i].frame, ctx->tmp_frame); @@ -434,11 +422,11 @@ static int vaapi_av1_decode_slice(AVCodecContext *avctx, return 0; } -const AVHWAccel ff_av1_vaapi_hwaccel = { - .name = "av1_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_AV1, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_av1_vaapi_hwaccel = { + .p.name = "av1_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = vaapi_av1_start_frame, .end_frame = vaapi_av1_end_frame, .decode_slice = vaapi_av1_decode_slice, diff --git a/libavcodec/vaapi_decode.c b/libavcodec/vaapi_decode.c index 134f10eca5f..cca94b5336a 100644 --- a/libavcodec/vaapi_decode.c +++ b/libavcodec/vaapi_decode.c @@ -23,6 +23,7 @@ #include "libavutil/pixdesc.h" #include "avcodec.h" +#include "codec_desc.h" #include "decode.h" #include "internal.h" #include "vaapi_decode.h" @@ -71,17 +72,14 @@ int ff_vaapi_decode_make_slice_buffer(AVCodecContext *avctx, av_assert0(pic->nb_slices <= pic->slices_allocated); if (pic->nb_slices == pic->slices_allocated) { - if (pic->slices_allocated > 0) - pic->slices_allocated *= 2; - else - pic->slices_allocated = 64; - pic->slice_buffers = av_realloc_array(pic->slice_buffers, - pic->slices_allocated, + pic->slices_allocated ? pic->slices_allocated * 2 : 64, 2 * sizeof(*pic->slice_buffers)); if (!pic->slice_buffers) return AVERROR(ENOMEM); + + pic->slices_allocated = pic->slices_allocated ? pic->slices_allocated * 2 : 64; } av_assert0(pic->nb_slices + 1 <= pic->slices_allocated); @@ -390,7 +388,7 @@ static const struct { VAProfile va_profile; VAProfile (*profile_parser)(AVCodecContext *avctx); } vaapi_profile_map[] = { -#define MAP(c, p, v, ...) { AV_CODEC_ID_ ## c, FF_PROFILE_ ## p, VAProfile ## v, __VA_ARGS__ } +#define MAP(c, p, v, ...) { AV_CODEC_ID_ ## c, AV_PROFILE_ ## p, VAProfile ## v, __VA_ARGS__ } MAP(MPEG2VIDEO, MPEG2_SIMPLE, MPEG2Simple ), MAP(MPEG2VIDEO, MPEG2_MAIN, MPEG2Main ), MAP(H263, UNKNOWN, H263Baseline), @@ -398,6 +396,11 @@ static const struct { MAP(MPEG4, MPEG4_ADVANCED_SIMPLE, MPEG4AdvancedSimple), MAP(MPEG4, MPEG4_MAIN, MPEG4Main ), +#if VA_CHECK_VERSION(1, 18, 0) + MAP(H264, H264_HIGH_10_INTRA, + H264High10 ), + MAP(H264, H264_HIGH_10, H264High10 ), +#endif MAP(H264, H264_CONSTRAINED_BASELINE, H264ConstrainedBaseline), MAP(H264, H264_MAIN, H264Main ), @@ -410,7 +413,9 @@ static const struct { #endif #if VA_CHECK_VERSION(1, 2, 0) && CONFIG_HEVC_VAAPI_HWACCEL MAP(HEVC, HEVC_REXT, None, - ff_vaapi_parse_hevc_rext_profile ), + ff_vaapi_parse_hevc_rext_scc_profile ), + MAP(HEVC, HEVC_SCC, None, + ff_vaapi_parse_hevc_rext_scc_profile ), #endif MAP(MJPEG, MJPEG_HUFFMAN_BASELINE_DCT, JPEGBaseline), @@ -490,7 +495,7 @@ static int vaapi_decode_make_config(AVCodecContext *avctx, if (avctx->codec_id != vaapi_profile_map[i].codec_id) continue; if (avctx->profile == vaapi_profile_map[i].codec_profile || - vaapi_profile_map[i].codec_profile == FF_PROFILE_UNKNOWN) + vaapi_profile_map[i].codec_profile == AV_PROFILE_UNKNOWN) profile_match = 1; va_profile = vaapi_profile_map[i].profile_parser ? diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c index bfca315a7ad..b8765a19c7b 100644 --- a/libavcodec/vaapi_encode.c +++ b/libavcodec/vaapi_encode.c @@ -16,11 +16,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "config_components.h" - #include #include +#include "config.h" + #include "libavutil/avassert.h" #include "libavutil/common.h" #include "libavutil/internal.h" @@ -30,6 +30,7 @@ #include "vaapi_encode.h" #include "encode.h" #include "avcodec.h" +#include "refstruct.h" const AVCodecHWConfigInternal *const ff_vaapi_encode_hw_configs[] = { HW_CONFIG_ENCODER_FRAMES(VAAPI, VAAPI), @@ -276,21 +277,34 @@ static int vaapi_encode_issue(AVCodecContext *avctx, av_log(avctx, AV_LOG_DEBUG, "Issuing encode for pic %"PRId64"/%"PRId64" " "as type %s.\n", pic->display_order, pic->encode_order, picture_type_name[pic->type]); - if (pic->nb_refs == 0) { + if (pic->nb_refs[0] == 0 && pic->nb_refs[1] == 0) { av_log(avctx, AV_LOG_DEBUG, "No reference pictures.\n"); } else { - av_log(avctx, AV_LOG_DEBUG, "Refers to:"); - for (i = 0; i < pic->nb_refs; i++) { + av_log(avctx, AV_LOG_DEBUG, "L0 refers to"); + for (i = 0; i < pic->nb_refs[0]; i++) { av_log(avctx, AV_LOG_DEBUG, " %"PRId64"/%"PRId64, - pic->refs[i]->display_order, pic->refs[i]->encode_order); + pic->refs[0][i]->display_order, pic->refs[0][i]->encode_order); } av_log(avctx, AV_LOG_DEBUG, ".\n"); + + if (pic->nb_refs[1]) { + av_log(avctx, AV_LOG_DEBUG, "L1 refers to"); + for (i = 0; i < pic->nb_refs[1]; i++) { + av_log(avctx, AV_LOG_DEBUG, " %"PRId64"/%"PRId64, + pic->refs[1][i]->display_order, pic->refs[1][i]->encode_order); + } + av_log(avctx, AV_LOG_DEBUG, ".\n"); + } } av_assert0(!pic->encode_issued); - for (i = 0; i < pic->nb_refs; i++) { - av_assert0(pic->refs[i]); - av_assert0(pic->refs[i]->encode_issued); + for (i = 0; i < pic->nb_refs[0]; i++) { + av_assert0(pic->refs[0][i]); + av_assert0(pic->refs[0][i]->encode_issued); + } + for (i = 0; i < pic->nb_refs[1]; i++) { + av_assert0(pic->refs[1][i]); + av_assert0(pic->refs[1][i]->encode_issued); } av_log(avctx, AV_LOG_DEBUG, "Input surface is %#x.\n", pic->input_surface); @@ -309,12 +323,12 @@ static int vaapi_encode_issue(AVCodecContext *avctx, pic->recon_surface = (VASurfaceID)(uintptr_t)pic->recon_image->data[3]; av_log(avctx, AV_LOG_DEBUG, "Recon surface is %#x.\n", pic->recon_surface); - pic->output_buffer_ref = av_buffer_pool_get(ctx->output_buffer_pool); + pic->output_buffer_ref = ff_refstruct_pool_get(ctx->output_buffer_pool); if (!pic->output_buffer_ref) { err = AVERROR(ENOMEM); goto fail; } - pic->output_buffer = (VABufferID)(uintptr_t)pic->output_buffer_ref->data; + pic->output_buffer = *pic->output_buffer_ref; av_log(avctx, AV_LOG_DEBUG, "Output buffer is %#x.\n", pic->output_buffer); @@ -645,85 +659,204 @@ static int vaapi_encode_issue(AVCodecContext *avctx, av_freep(&pic->slices); av_freep(&pic->roi); av_frame_free(&pic->recon_image); - av_buffer_unref(&pic->output_buffer_ref); + ff_refstruct_unref(&pic->output_buffer_ref); pic->output_buffer = VA_INVALID_ID; return err; } -static int vaapi_encode_output(AVCodecContext *avctx, - VAAPIEncodePicture *pic, AVPacket *pkt) +static int vaapi_encode_set_output_property(AVCodecContext *avctx, + VAAPIEncodePicture *pic, + AVPacket *pkt) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + + if (pic->type == PICTURE_TYPE_IDR) + pkt->flags |= AV_PKT_FLAG_KEY; + + pkt->pts = pic->pts; + pkt->duration = pic->duration; + + // for no-delay encoders this is handled in generic codec + if (avctx->codec->capabilities & AV_CODEC_CAP_DELAY && + avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { + pkt->opaque = pic->opaque; + pkt->opaque_ref = pic->opaque_ref; + pic->opaque_ref = NULL; + } + + if (ctx->codec->flags & FLAG_TIMESTAMP_NO_DELAY) { + pkt->dts = pkt->pts; + return 0; + } + + if (ctx->output_delay == 0) { + pkt->dts = pkt->pts; + } else if (pic->encode_order < ctx->decode_delay) { + if (ctx->ts_ring[pic->encode_order] < INT64_MIN + ctx->dts_pts_diff) + pkt->dts = INT64_MIN; + else + pkt->dts = ctx->ts_ring[pic->encode_order] - ctx->dts_pts_diff; + } else { + pkt->dts = ctx->ts_ring[(pic->encode_order - ctx->decode_delay) % + (3 * ctx->output_delay + ctx->async_depth)]; + } + + return 0; +} + +static int vaapi_encode_get_coded_buffer_size(AVCodecContext *avctx, VABufferID buf_id) { VAAPIEncodeContext *ctx = avctx->priv_data; VACodedBufferSegment *buf_list, *buf; + int size = 0; VAStatus vas; - int total_size = 0; - uint8_t *ptr; int err; - err = vaapi_encode_wait(avctx, pic); - if (err < 0) - return err; - - buf_list = NULL; - vas = vaMapBuffer(ctx->hwctx->display, pic->output_buffer, + vas = vaMapBuffer(ctx->hwctx->display, buf_id, (void**)&buf_list); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to map output buffers: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); - goto fail; + return err; } for (buf = buf_list; buf; buf = buf->next) - total_size += buf->size; + size += buf->size; - err = ff_get_encode_buffer(avctx, pkt, total_size, 0); - ptr = pkt->data; + vas = vaUnmapBuffer(ctx->hwctx->display, buf_id); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to unmap output buffers: " + "%d (%s).\n", vas, vaErrorStr(vas)); + err = AVERROR(EIO); + return err; + } - if (err < 0) - goto fail_mapped; + return size; +} + +static int vaapi_encode_get_coded_buffer_data(AVCodecContext *avctx, + VABufferID buf_id, uint8_t **dst) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VACodedBufferSegment *buf_list, *buf; + VAStatus vas; + int err; + + vas = vaMapBuffer(ctx->hwctx->display, buf_id, + (void**)&buf_list); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to map output buffers: " + "%d (%s).\n", vas, vaErrorStr(vas)); + err = AVERROR(EIO); + return err; + } for (buf = buf_list; buf; buf = buf->next) { av_log(avctx, AV_LOG_DEBUG, "Output buffer: %u bytes " "(status %08x).\n", buf->size, buf->status); - memcpy(ptr, buf->buf, buf->size); - ptr += buf->size; + memcpy(*dst, buf->buf, buf->size); + *dst += buf->size; } - if (pic->type == PICTURE_TYPE_IDR) - pkt->flags |= AV_PKT_FLAG_KEY; - - pkt->pts = pic->pts; - pkt->duration = pic->duration; - - vas = vaUnmapBuffer(ctx->hwctx->display, pic->output_buffer); + vas = vaUnmapBuffer(ctx->hwctx->display, buf_id); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to unmap output buffers: " "%d (%s).\n", vas, vaErrorStr(vas)); err = AVERROR(EIO); - goto fail; + return err; } - // for no-delay encoders this is handled in generic codec - if (avctx->codec->capabilities & AV_CODEC_CAP_DELAY && - avctx->flags & AV_CODEC_FLAG_COPY_OPAQUE) { - pkt->opaque = pic->opaque; - pkt->opaque_ref = pic->opaque_ref; - pic->opaque_ref = NULL; + return 0; +} + +static int vaapi_encode_get_coded_data(AVCodecContext *avctx, + VAAPIEncodePicture *pic, AVPacket *pkt) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VABufferID output_buffer_prev; + int total_size = 0; + uint8_t *ptr; + int ret; + + if (ctx->coded_buffer_ref) { + output_buffer_prev = *ctx->coded_buffer_ref; + ret = vaapi_encode_get_coded_buffer_size(avctx, output_buffer_prev); + if (ret < 0) + goto end; + total_size += ret; + } + + ret = vaapi_encode_get_coded_buffer_size(avctx, pic->output_buffer); + if (ret < 0) + goto end; + total_size += ret; + + ret = ff_get_encode_buffer(avctx, pkt, total_size, 0); + if (ret < 0) + goto end; + ptr = pkt->data; + + if (ctx->coded_buffer_ref) { + ret = vaapi_encode_get_coded_buffer_data(avctx, output_buffer_prev, &ptr); + if (ret < 0) + goto end; } - av_buffer_unref(&pic->output_buffer_ref); + ret = vaapi_encode_get_coded_buffer_data(avctx, pic->output_buffer, &ptr); + if (ret < 0) + goto end; + +end: + ff_refstruct_unref(&ctx->coded_buffer_ref); + ff_refstruct_unref(&pic->output_buffer_ref); pic->output_buffer = VA_INVALID_ID; + return ret; +} + +static int vaapi_encode_output(AVCodecContext *avctx, + VAAPIEncodePicture *pic, AVPacket *pkt) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + AVPacket *pkt_ptr = pkt; + int err; + + err = vaapi_encode_wait(avctx, pic); + if (err < 0) + return err; + + if (pic->non_independent_frame) { + av_assert0(!ctx->coded_buffer_ref); + ctx->coded_buffer_ref = ff_refstruct_ref(pic->output_buffer_ref); + + if (pic->tail_size) { + if (ctx->tail_pkt->size) { + err = AVERROR_BUG; + goto end; + } + + err = ff_get_encode_buffer(avctx, ctx->tail_pkt, pic->tail_size, 0); + if (err < 0) + goto end; + + memcpy(ctx->tail_pkt->data, pic->tail_data, pic->tail_size); + pkt_ptr = ctx->tail_pkt; + } + } else { + err = vaapi_encode_get_coded_data(avctx, pic, pkt); + if (err < 0) + goto end; + } + av_log(avctx, AV_LOG_DEBUG, "Output read for pic %"PRId64"/%"PRId64".\n", pic->display_order, pic->encode_order); - return 0; -fail_mapped: - vaUnmapBuffer(ctx->hwctx->display, pic->output_buffer); -fail: - av_buffer_unref(&pic->output_buffer_ref); + vaapi_encode_set_output_property(avctx, pic, pkt_ptr); + +end: + ff_refstruct_unref(&pic->output_buffer_ref); pic->output_buffer = VA_INVALID_ID; return err; } @@ -738,7 +871,7 @@ static int vaapi_encode_discard(AVCodecContext *avctx, "%"PRId64"/%"PRId64".\n", pic->display_order, pic->encode_order); - av_buffer_unref(&pic->output_buffer_ref); + ff_refstruct_unref(&pic->output_buffer_ref); pic->output_buffer = VA_INVALID_ID; } @@ -781,7 +914,6 @@ static int vaapi_encode_free(AVCodecContext *avctx, for (i = 0; i < pic->nb_slices; i++) av_freep(&pic->slices[i].codec_slice_params); } - av_freep(&pic->codec_picture_params); av_frame_free(&pic->input_image); av_frame_free(&pic->recon_image); @@ -811,8 +943,12 @@ static void vaapi_encode_add_ref(AVCodecContext *avctx, if (is_ref) { av_assert0(pic != target); - av_assert0(pic->nb_refs < MAX_PICTURE_REFERENCES); - pic->refs[pic->nb_refs++] = target; + av_assert0(pic->nb_refs[0] < MAX_PICTURE_REFERENCES && + pic->nb_refs[1] < MAX_PICTURE_REFERENCES); + if (target->display_order < pic->display_order) + pic->refs[0][pic->nb_refs[0]++] = target; + else + pic->refs[1][pic->nb_refs[1]++] = target; ++refs; } @@ -841,10 +977,16 @@ static void vaapi_encode_remove_refs(AVCodecContext *avctx, if (pic->ref_removed[level]) return; - for (i = 0; i < pic->nb_refs; i++) { - av_assert0(pic->refs[i]); - --pic->refs[i]->ref_count[level]; - av_assert0(pic->refs[i]->ref_count[level] >= 0); + for (i = 0; i < pic->nb_refs[0]; i++) { + av_assert0(pic->refs[0][i]); + --pic->refs[0][i]->ref_count[level]; + av_assert0(pic->refs[0][i]->ref_count[level] >= 0); + } + + for (i = 0; i < pic->nb_refs[1]; i++) { + av_assert0(pic->refs[1][i]); + --pic->refs[1][i]->ref_count[level]; + av_assert0(pic->refs[1][i]->ref_count[level] >= 0); } for (i = 0; i < pic->nb_dpb_pics; i++) { @@ -889,7 +1031,7 @@ static void vaapi_encode_set_b_pictures(AVCodecContext *avctx, vaapi_encode_add_ref(avctx, pic, end, 1, 1, 0); vaapi_encode_add_ref(avctx, pic, prev, 0, 0, 1); - for (ref = end->refs[1]; ref; ref = ref->refs[1]) + for (ref = end->refs[1][0]; ref; ref = ref->refs[1][0]) vaapi_encode_add_ref(avctx, pic, ref, 0, 1, 0); } *last = prev; @@ -912,7 +1054,7 @@ static void vaapi_encode_set_b_pictures(AVCodecContext *avctx, vaapi_encode_add_ref(avctx, pic, end, 1, 1, 0); vaapi_encode_add_ref(avctx, pic, prev, 0, 0, 1); - for (ref = end->refs[1]; ref; ref = ref->refs[1]) + for (ref = end->refs[1][0]; ref; ref = ref->refs[1][0]) vaapi_encode_add_ref(avctx, pic, ref, 0, 1, 0); if (i > 1) @@ -926,11 +1068,44 @@ static void vaapi_encode_set_b_pictures(AVCodecContext *avctx, } } +static void vaapi_encode_add_next_prev(AVCodecContext *avctx, + VAAPIEncodePicture *pic) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + int i; + + if (!pic) + return; + + if (pic->type == PICTURE_TYPE_IDR) { + for (i = 0; i < ctx->nb_next_prev; i++) { + --ctx->next_prev[i]->ref_count[0]; + ctx->next_prev[i] = NULL; + } + ctx->next_prev[0] = pic; + ++pic->ref_count[0]; + ctx->nb_next_prev = 1; + + return; + } + + if (ctx->nb_next_prev < MAX_PICTURE_REFERENCES) { + ctx->next_prev[ctx->nb_next_prev++] = pic; + ++pic->ref_count[0]; + } else { + --ctx->next_prev[0]->ref_count[0]; + for (i = 0; i < MAX_PICTURE_REFERENCES - 1; i++) + ctx->next_prev[i] = ctx->next_prev[i + 1]; + ctx->next_prev[i] = pic; + ++pic->ref_count[0]; + } +} + static int vaapi_encode_pick_next(AVCodecContext *avctx, VAAPIEncodePicture **pic_out) { VAAPIEncodeContext *ctx = avctx->priv_data; - VAAPIEncodePicture *pic = NULL, *next, *start; + VAAPIEncodePicture *pic = NULL, *prev = NULL, *next, *start; int i, b_counter, closed_gop_end; // If there are any B-frames already queued, the next one to encode @@ -941,11 +1116,18 @@ static int vaapi_encode_pick_next(AVCodecContext *avctx, continue; if (pic->type != PICTURE_TYPE_B) continue; - for (i = 0; i < pic->nb_refs; i++) { - if (!pic->refs[i]->encode_issued) + for (i = 0; i < pic->nb_refs[0]; i++) { + if (!pic->refs[0][i]->encode_issued) break; } - if (i == pic->nb_refs) + if (i != pic->nb_refs[0]) + continue; + + for (i = 0; i < pic->nb_refs[1]; i++) { + if (!pic->refs[1][i]->encode_issued) + break; + } + if (i == pic->nb_refs[1]) break; } @@ -1044,21 +1226,30 @@ static int vaapi_encode_pick_next(AVCodecContext *avctx, vaapi_encode_add_ref(avctx, pic, pic, 0, 1, 0); if (pic->type != PICTURE_TYPE_IDR) { - vaapi_encode_add_ref(avctx, pic, start, - pic->type == PICTURE_TYPE_P, - b_counter > 0, 0); - vaapi_encode_add_ref(avctx, pic, ctx->next_prev, 0, 0, 1); + // TODO: apply both previous and forward multi reference for all vaapi encoders. + // And L0/L1 reference frame number can be set dynamically through query + // VAConfigAttribEncMaxRefFrames attribute. + if (avctx->codec_id == AV_CODEC_ID_AV1) { + for (i = 0; i < ctx->nb_next_prev; i++) + vaapi_encode_add_ref(avctx, pic, ctx->next_prev[i], + pic->type == PICTURE_TYPE_P, + b_counter > 0, 0); + } else + vaapi_encode_add_ref(avctx, pic, start, + pic->type == PICTURE_TYPE_P, + b_counter > 0, 0); + + vaapi_encode_add_ref(avctx, pic, ctx->next_prev[ctx->nb_next_prev - 1], 0, 0, 1); } - if (ctx->next_prev) - --ctx->next_prev->ref_count[0]; if (b_counter > 0) { vaapi_encode_set_b_pictures(avctx, start, pic, pic, 1, - &ctx->next_prev); + &prev); } else { - ctx->next_prev = pic; + prev = pic; } - ++ctx->next_prev->ref_count[0]; + vaapi_encode_add_next_prev(avctx, prev); + return 0; } @@ -1205,10 +1396,23 @@ static int vaapi_encode_send_frame(AVCodecContext *avctx, AVFrame *frame) int ff_vaapi_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt) { VAAPIEncodeContext *ctx = avctx->priv_data; - VAAPIEncodePicture *pic; + VAAPIEncodePicture *pic = NULL; AVFrame *frame = ctx->frame; int err; +start: + /** if no B frame before repeat P frame, sent repeat P frame out. */ + if (ctx->tail_pkt->size) { + for (VAAPIEncodePicture *tmp = ctx->pic_start; tmp; tmp = tmp->next) { + if (tmp->type == PICTURE_TYPE_B && tmp->pts < ctx->tail_pkt->pts) + break; + else if (!tmp->next) { + av_packet_move_ref(pkt, ctx->tail_pkt); + goto end; + } + } + } + err = ff_encode_get_frame(avctx, frame); if (err < 0 && err != AVERROR_EOF) return err; @@ -1228,8 +1432,6 @@ int ff_vaapi_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt) } if (ctx->has_sync_buffer_func) { - pic = NULL; - if (av_fifo_can_write(ctx->encode_fifo)) { err = vaapi_encode_pick_next(avctx, &pic); if (!err) { @@ -1255,7 +1457,6 @@ int ff_vaapi_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt) av_fifo_read(ctx->encode_fifo, &pic, 1); ctx->encode_order = pic->encode_order + 1; } else { - pic = NULL; err = vaapi_encode_pick_next(avctx, &pic); if (err < 0) return err; @@ -1276,27 +1477,21 @@ int ff_vaapi_encode_receive_packet(AVCodecContext *avctx, AVPacket *pkt) return err; } - if (ctx->output_delay == 0) { - pkt->dts = pkt->pts; - } else if (pic->encode_order < ctx->decode_delay) { - if (ctx->ts_ring[pic->encode_order] < INT64_MIN + ctx->dts_pts_diff) - pkt->dts = INT64_MIN; - else - pkt->dts = ctx->ts_ring[pic->encode_order] - ctx->dts_pts_diff; - } else { - pkt->dts = ctx->ts_ring[(pic->encode_order - ctx->decode_delay) % - (3 * ctx->output_delay + ctx->async_depth)]; - } - av_log(avctx, AV_LOG_DEBUG, "Output packet: pts %"PRId64" dts %"PRId64".\n", - pkt->pts, pkt->dts); - ctx->output_order = pic->encode_order; vaapi_encode_clear_old(avctx); + /** loop to get an available pkt in encoder flushing. */ + if (ctx->end_of_stream && !pkt->size) + goto start; + +end: + if (pkt->size) + av_log(avctx, AV_LOG_DEBUG, "Output packet: pts %"PRId64", dts %"PRId64", " + "size %d bytes.\n", pkt->pts, pkt->dts, pkt->size); + return 0; } - static av_cold void vaapi_encode_add_global_param(AVCodecContext *avctx, int type, void *buffer, size_t size) { @@ -1414,7 +1609,7 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx) av_assert0(ctx->codec->profiles); for (i = 0; (ctx->codec->profiles[i].av_profile != - FF_PROFILE_UNKNOWN); i++) { + AV_PROFILE_UNKNOWN); i++) { profile = &ctx->codec->profiles[i]; if (depth != profile->depth || desc->nb_components != profile->nb_components) @@ -1424,7 +1619,7 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx) desc->log2_chroma_h != profile->log2_chroma_h)) continue; if (avctx->profile != profile->av_profile && - avctx->profile != FF_PROFILE_UNKNOWN) + avctx->profile != AV_PROFILE_UNKNOWN) continue; #if VA_CHECK_VERSION(1, 0, 0) @@ -1610,6 +1805,19 @@ static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx) int i, first = 1, res; supported_va_rc_modes = rc_attr.value; + if (ctx->blbrc) { +#if VA_CHECK_VERSION(0, 39, 2) + if (!(supported_va_rc_modes & VA_RC_MB)) { + ctx->blbrc = 0; + av_log(avctx, AV_LOG_WARNING, "Driver does not support BLBRC.\n"); + } +#else + ctx->blbrc = 0; + av_log(avctx, AV_LOG_WARNING, "Please consider to update to VAAPI 0.39.2 " + "or above, which can support BLBRC.\n"); +#endif + } + for (i = 0; i < FF_ARRAY_ELEMS(vaapi_encode_rc_modes); i++) { rc_mode = &vaapi_encode_rc_modes[i]; if (supported_va_rc_modes & rc_mode->va_mode) { @@ -1761,7 +1969,10 @@ static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx) if (ctx->explicit_qp) { rc_quality = ctx->explicit_qp; } else if (avctx->global_quality > 0) { - rc_quality = avctx->global_quality; + if (avctx->flags & AV_CODEC_FLAG_QSCALE) + rc_quality = avctx->global_quality / FF_QP2LAMBDA; + else + rc_quality = avctx->global_quality; } else { rc_quality = ctx->codec->default_quality; av_log(avctx, AV_LOG_WARNING, "No quality level set; " @@ -1821,13 +2032,22 @@ static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx) ctx->va_bit_rate = rc_bits_per_second; av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s.\n", rc_mode->name); + + if (ctx->blbrc && ctx->va_rc_mode == VA_RC_CQP) + ctx->blbrc = 0; + av_log(avctx, AV_LOG_VERBOSE, "Block Level bitrate control: %s.\n", ctx->blbrc ? "ON" : "OFF"); + if (rc_attr.value == VA_ATTRIB_NOT_SUPPORTED) { // This driver does not want the RC mode attribute to be set. } else { ctx->config_attributes[ctx->nb_config_attributes++] = (VAConfigAttrib) { .type = VAConfigAttribRateControl, +#if VA_CHECK_VERSION(0, 39, 2) + .value = ctx->blbrc ? ctx->va_rc_mode | VA_RC_MB : ctx->va_rc_mode, +#else .value = ctx->va_rc_mode, +#endif }; } @@ -1859,6 +2079,9 @@ static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx) #endif #if VA_CHECK_VERSION(1, 3, 0) .quality_factor = rc_quality, +#endif +#if VA_CHECK_VERSION(0, 39, 2) + .rc_flags.bits.mb_rate_control = ctx->blbrc ? 1 : 2, #endif }; vaapi_encode_add_global_param(avctx, @@ -2420,28 +2643,25 @@ static av_cold int vaapi_encode_init_roi(AVCodecContext *avctx) return 0; } -static void vaapi_encode_free_output_buffer(void *opaque, - uint8_t *data) +static void vaapi_encode_free_output_buffer(FFRefStructOpaque opaque, + void *obj) { - AVCodecContext *avctx = opaque; + AVCodecContext *avctx = opaque.nc; VAAPIEncodeContext *ctx = avctx->priv_data; - VABufferID buffer_id; - - buffer_id = (VABufferID)(uintptr_t)data; + VABufferID *buffer_id_ref = obj; + VABufferID buffer_id = *buffer_id_ref; vaDestroyBuffer(ctx->hwctx->display, buffer_id); av_log(avctx, AV_LOG_DEBUG, "Freed output buffer %#x\n", buffer_id); } -static AVBufferRef *vaapi_encode_alloc_output_buffer(void *opaque, - size_t size) +static int vaapi_encode_alloc_output_buffer(FFRefStructOpaque opaque, void *obj) { - AVCodecContext *avctx = opaque; + AVCodecContext *avctx = opaque.nc; VAAPIEncodeContext *ctx = avctx->priv_data; - VABufferID buffer_id; + VABufferID *buffer_id = obj; VAStatus vas; - AVBufferRef *ref; // The output buffer size is fixed, so it needs to be large enough // to hold the largest possible compressed frame. We assume here @@ -2450,25 +2670,16 @@ static AVBufferRef *vaapi_encode_alloc_output_buffer(void *opaque, vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context, VAEncCodedBufferType, 3 * ctx->surface_width * ctx->surface_height + - (1 << 16), 1, 0, &buffer_id); + (1 << 16), 1, 0, buffer_id); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to create bitstream " "output buffer: %d (%s).\n", vas, vaErrorStr(vas)); - return NULL; + return AVERROR(ENOMEM); } - av_log(avctx, AV_LOG_DEBUG, "Allocated output buffer %#x\n", buffer_id); - - ref = av_buffer_create((uint8_t*)(uintptr_t)buffer_id, - sizeof(buffer_id), - &vaapi_encode_free_output_buffer, - avctx, AV_BUFFER_FLAG_READONLY); - if (!ref) { - vaDestroyBuffer(ctx->hwctx->display, buffer_id); - return NULL; - } + av_log(avctx, AV_LOG_DEBUG, "Allocated output buffer %#x\n", *buffer_id); - return ref; + return 0; } static av_cold int vaapi_encode_create_recon_frames(AVCodecContext *avctx) @@ -2597,6 +2808,12 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx) ctx->device = (AVHWDeviceContext*)ctx->device_ref->data; ctx->hwctx = ctx->device->hwctx; + ctx->tail_pkt = av_packet_alloc(); + if (!ctx->tail_pkt) { + err = AVERROR(ENOMEM); + goto fail; + } + err = vaapi_encode_profile_entrypoint(avctx); if (err < 0) goto fail; @@ -2677,8 +2894,9 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx) } ctx->output_buffer_pool = - av_buffer_pool_init2(sizeof(VABufferID), avctx, - &vaapi_encode_alloc_output_buffer, NULL); + ff_refstruct_pool_alloc_ext(sizeof(VABufferID), 0, avctx, + &vaapi_encode_alloc_output_buffer, NULL, + vaapi_encode_free_output_buffer, NULL); if (!ctx->output_buffer_pool) { err = AVERROR(ENOMEM); goto fail; @@ -2776,19 +2994,22 @@ av_cold int ff_vaapi_encode_close(AVCodecContext *avctx) vaapi_encode_free(avctx, pic); } - av_buffer_pool_uninit(&ctx->output_buffer_pool); + ff_refstruct_pool_uninit(&ctx->output_buffer_pool); if (ctx->va_context != VA_INVALID_ID) { - vaDestroyContext(ctx->hwctx->display, ctx->va_context); + if (ctx->hwctx) + vaDestroyContext(ctx->hwctx->display, ctx->va_context); ctx->va_context = VA_INVALID_ID; } if (ctx->va_config != VA_INVALID_ID) { - vaDestroyConfig(ctx->hwctx->display, ctx->va_config); + if (ctx->hwctx) + vaDestroyConfig(ctx->hwctx->display, ctx->va_config); ctx->va_config = VA_INVALID_ID; } av_frame_free(&ctx->frame); + av_packet_free(&ctx->tail_pkt); av_freep(&ctx->codec_sequence_params); av_freep(&ctx->codec_picture_params); diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h index a1e639f56b0..0eed9691ca4 100644 --- a/libavcodec/vaapi_encode.h +++ b/libavcodec/vaapi_encode.h @@ -49,6 +49,7 @@ enum { // A.4.1: table A.6 allows at most 20 tile columns for any level. MAX_TILE_COLS = 20, MAX_ASYNC_DEPTH = 64, + MAX_REFERENCE_LIST_NUM = 2, }; extern const AVCodecHWConfigInternal *const ff_vaapi_encode_hw_configs[]; @@ -102,7 +103,8 @@ typedef struct VAAPIEncodePicture { int nb_param_buffers; VABufferID *param_buffers; - AVBufferRef *output_buffer_ref; + /* Refcounted via the refstruct-API */ + VABufferID *output_buffer_ref; VABufferID output_buffer; void *priv_data; @@ -116,10 +118,11 @@ typedef struct VAAPIEncodePicture { // but not if it isn't. int nb_dpb_pics; struct VAAPIEncodePicture *dpb[MAX_DPB_SIZE]; - // The reference pictures used in decoding this picture. If they are - // used by later pictures they will also appear in the DPB. - int nb_refs; - struct VAAPIEncodePicture *refs[MAX_PICTURE_REFERENCES]; + // The reference pictures used in decoding this picture. If they are + // used by later pictures they will also appear in the DPB. ref[0][] for + // previous reference frames. ref[1][] for future reference frames. + int nb_refs[MAX_REFERENCE_LIST_NUM]; + struct VAAPIEncodePicture *refs[MAX_REFERENCE_LIST_NUM][MAX_PICTURE_REFERENCES]; // The previous reference picture in encode order. Must be in at least // one of the reference list and DPB list. struct VAAPIEncodePicture *prev; @@ -131,10 +134,21 @@ typedef struct VAAPIEncodePicture { int nb_slices; VAAPIEncodeSlice *slices; + + /** + * indicate if current frame is an independent frame that the coded data + * can be pushed to downstream directly. Coded of non-independent frame + * data will be concatenated into next independent frame. + */ + int non_independent_frame; + /** Tail data of current pic, used only for repeat header of AV1. */ + char tail_data[MAX_PARAM_BUFFER_SIZE]; + /** Byte length of tail_data. */ + size_t tail_size; } VAAPIEncodePicture; typedef struct VAAPIEncodeProfile { - // lavc profile value (FF_PROFILE_*). + // lavc profile value (AV_PROFILE_*). int av_profile; // Supported bit depth. int depth; @@ -202,6 +216,9 @@ typedef struct VAAPIEncodeContext { // available modes). int explicit_rc_mode; + // Block Level based bitrate control. + int blbrc; + // Explicitly-set QP, for use with the "qp" options. // (Forces CQP mode when set, overriding everything else.) int explicit_qp; @@ -262,7 +279,7 @@ typedef struct VAAPIEncodeContext { AVHWFramesContext *recon_frames; // Pool of (reusable) bitstream output buffers. - AVBufferPool *output_buffer_pool; + struct FFRefStructPool *output_buffer_pool; // Global parameters which will be applied at the start of the // sequence (includes rate control parameters below). @@ -290,8 +307,9 @@ typedef struct VAAPIEncodeContext { // Current encoding window, in display (input) order. VAAPIEncodePicture *pic_start, *pic_end; // The next picture to use as the previous reference picture in - // encoding order. - VAAPIEncodePicture *next_prev; + // encoding order. Order from small to large in encoding order. + VAAPIEncodePicture *next_prev[MAX_PICTURE_REFERENCES]; + int nb_next_prev; // Next input order index (display order). int64_t input_order; @@ -364,6 +382,19 @@ typedef struct VAAPIEncodeContext { AVFifo *encode_fifo; // Max number of frame buffered in encoder. int async_depth; + + /** Head data for current output pkt, used only for AV1. */ + //void *header_data; + //size_t header_data_size; + + /** + * Buffered coded data of a pic if it is an non-independent frame. + * This is a RefStruct reference. + */ + VABufferID *coded_buffer_ref; + + /** Tail data of a pic, now only used for av1 repeat frame header. */ + AVPacket *tail_pkt; } VAAPIEncodeContext; enum { @@ -380,11 +411,14 @@ enum { // Codec supports non-IDR key pictures (that is, key pictures do // not necessarily empty the DPB). FLAG_NON_IDR_KEY_PICTURES = 1 << 5, + // Codec output packet without timestamp delay, which means the + // output packet has same PTS and DTS. + FLAG_TIMESTAMP_NO_DELAY = 1 << 6, }; typedef struct VAAPIEncodeType { // List of supported profiles and corresponding VAAPI profiles. - // (Must end with FF_PROFILE_UNKNOWN.) + // (Must end with AV_PROFILE_UNKNOWN.) const VAAPIEncodeProfile *profiles; // Codec feature flags. @@ -494,20 +528,24 @@ int ff_vaapi_encode_close(AVCodecContext *avctx); #define VAAPI_ENCODE_RC_MODE(name, desc) \ { #name, desc, 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_ ## name }, \ - 0, 0, FLAGS, "rc_mode" } + 0, 0, FLAGS, .unit = "rc_mode" } #define VAAPI_ENCODE_RC_OPTIONS \ { "rc_mode",\ "Set rate control mode", \ OFFSET(common.explicit_rc_mode), AV_OPT_TYPE_INT, \ - { .i64 = RC_MODE_AUTO }, RC_MODE_AUTO, RC_MODE_MAX, FLAGS, "rc_mode" }, \ + { .i64 = RC_MODE_AUTO }, RC_MODE_AUTO, RC_MODE_MAX, FLAGS, .unit = "rc_mode" }, \ { "auto", "Choose mode automatically based on other parameters", \ - 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_AUTO }, 0, 0, FLAGS, "rc_mode" }, \ + 0, AV_OPT_TYPE_CONST, { .i64 = RC_MODE_AUTO }, 0, 0, FLAGS, .unit = "rc_mode" }, \ VAAPI_ENCODE_RC_MODE(CQP, "Constant-quality"), \ VAAPI_ENCODE_RC_MODE(CBR, "Constant-bitrate"), \ VAAPI_ENCODE_RC_MODE(VBR, "Variable-bitrate"), \ VAAPI_ENCODE_RC_MODE(ICQ, "Intelligent constant-quality"), \ VAAPI_ENCODE_RC_MODE(QVBR, "Quality-defined variable-bitrate"), \ - VAAPI_ENCODE_RC_MODE(AVBR, "Average variable-bitrate") + VAAPI_ENCODE_RC_MODE(AVBR, "Average variable-bitrate"), \ + { "blbrc", \ + "Block level based bitrate control",\ + OFFSET(common.blbrc), AV_OPT_TYPE_BOOL, \ + { .i64 = 0 }, 0, 1, FLAGS } #endif /* AVCODEC_VAAPI_ENCODE_H */ diff --git a/libavcodec/vaapi_encode_av1.c b/libavcodec/vaapi_encode_av1.c new file mode 100644 index 00000000000..a46b882ab9f --- /dev/null +++ b/libavcodec/vaapi_encode_av1.c @@ -0,0 +1,951 @@ +/* + * Copyright (c) 2023 Intel Corporation + * + * 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 +#include + +#include "libavutil/pixdesc.h" +#include "libavutil/opt.h" + +#include "cbs_av1.h" +#include "put_bits.h" +#include "codec_internal.h" +#include "av1_levels.h" +#include "vaapi_encode.h" + +#define AV1_MAX_QUANT 255 + +typedef struct VAAPIEncodeAV1Picture { + int64_t last_idr_frame; + int slot; +} VAAPIEncodeAV1Picture; + +typedef struct VAAPIEncodeAV1Context { + VAAPIEncodeContext common; + AV1RawOBU sh; /**< sequence header.*/ + AV1RawOBU fh; /**< frame header.*/ + CodedBitstreamContext *cbc; + CodedBitstreamFragment current_obu; + VAConfigAttribValEncAV1 attr; + VAConfigAttribValEncAV1Ext1 attr_ext1; + VAConfigAttribValEncAV1Ext2 attr_ext2; + + char sh_data[MAX_PARAM_BUFFER_SIZE]; /**< coded sequence header data. */ + size_t sh_data_len; /**< bit length of sh_data. */ + char fh_data[MAX_PARAM_BUFFER_SIZE]; /**< coded frame header data. */ + size_t fh_data_len; /**< bit length of fh_data. */ + + uint8_t uniform_tile; + uint8_t use_128x128_superblock; + int sb_cols; + int sb_rows; + int tile_cols_log2; + int tile_rows_log2; + int max_tile_width_sb; + int max_tile_height_sb; + uint8_t width_in_sbs_minus_1[AV1_MAX_TILE_COLS]; + uint8_t height_in_sbs_minus_1[AV1_MAX_TILE_ROWS]; + + int min_log2_tile_cols; + int max_log2_tile_cols; + int min_log2_tile_rows; + int max_log2_tile_rows; + + int q_idx_idr; + int q_idx_p; + int q_idx_b; + + /** bit positions in current frame header */ + int qindex_offset; + int loopfilter_offset; + int cdef_start_offset; + int cdef_param_size; + + /** user options */ + int profile; + int level; + int tier; + int tile_cols, tile_rows; + int tile_groups; +} VAAPIEncodeAV1Context; + +static void vaapi_encode_av1_trace_write_log(void *ctx, + PutBitContext *pbc, int length, + const char *str, const int *subscripts, + int64_t value) +{ + VAAPIEncodeAV1Context *priv = ctx; + int position; + + position = put_bits_count(pbc); + av_assert0(position >= length); + + if (!strcmp(str, "base_q_idx")) + priv->qindex_offset = position - length; + else if (!strcmp(str, "loop_filter_level[0]")) + priv->loopfilter_offset = position - length; + else if (!strcmp(str, "cdef_damping_minus_3")) + priv->cdef_start_offset = position - length; + else if (!strcmp(str, "cdef_uv_sec_strength[i]")) + priv->cdef_param_size = position - priv->cdef_start_offset; +} + +static av_cold int vaapi_encode_av1_get_encoder_caps(AVCodecContext *avctx) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeAV1Context *priv = avctx->priv_data; + + // Surfaces must be aligned to superblock boundaries. + ctx->surface_width = FFALIGN(avctx->width, priv->use_128x128_superblock ? 128 : 64); + ctx->surface_height = FFALIGN(avctx->height, priv->use_128x128_superblock ? 128 : 64); + + return 0; +} + +static av_cold int vaapi_encode_av1_configure(AVCodecContext *avctx) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeAV1Context *priv = avctx->priv_data; + int ret; + + ret = ff_cbs_init(&priv->cbc, AV_CODEC_ID_AV1, avctx); + if (ret < 0) + return ret; + priv->cbc->trace_enable = 1; + priv->cbc->trace_level = AV_LOG_DEBUG; + priv->cbc->trace_context = ctx; + priv->cbc->trace_write_callback = vaapi_encode_av1_trace_write_log; + + if (ctx->rc_mode->quality) { + priv->q_idx_p = av_clip(ctx->rc_quality, 0, AV1_MAX_QUANT); + if (fabs(avctx->i_quant_factor) > 0.0) + priv->q_idx_idr = + av_clip((fabs(avctx->i_quant_factor) * priv->q_idx_p + + avctx->i_quant_offset) + 0.5, + 0, AV1_MAX_QUANT); + else + priv->q_idx_idr = priv->q_idx_p; + + if (fabs(avctx->b_quant_factor) > 0.0) + priv->q_idx_b = + av_clip((fabs(avctx->b_quant_factor) * priv->q_idx_p + + avctx->b_quant_offset) + 0.5, + 0, AV1_MAX_QUANT); + else + priv->q_idx_b = priv->q_idx_p; + } else { + /** Arbitrary value */ + priv->q_idx_idr = priv->q_idx_p = priv->q_idx_b = 128; + } + + return 0; +} + +static int vaapi_encode_av1_add_obu(AVCodecContext *avctx, + CodedBitstreamFragment *au, + uint8_t type, + void *obu_unit) +{ + int ret; + + ret = ff_cbs_insert_unit_content(au, -1, + type, obu_unit, NULL); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to add OBU unit: " + "type = %d.\n", type); + return ret; + } + + return 0; +} + +static int vaapi_encode_av1_write_obu(AVCodecContext *avctx, + char *data, size_t *data_len, + CodedBitstreamFragment *bs) +{ + VAAPIEncodeAV1Context *priv = avctx->priv_data; + int ret; + + ret = ff_cbs_write_fragment_data(priv->cbc, bs); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to write packed header.\n"); + return ret; + } + + if ((size_t)8 * MAX_PARAM_BUFFER_SIZE < 8 * bs->data_size - bs->data_bit_padding) { + av_log(avctx, AV_LOG_ERROR, "Access unit too large: " + "%zu < %zu.\n", (size_t)8 * MAX_PARAM_BUFFER_SIZE, + 8 * bs->data_size - bs->data_bit_padding); + return AVERROR(ENOSPC); + } + + memcpy(data, bs->data, bs->data_size); + *data_len = 8 * bs->data_size - bs->data_bit_padding; + + return 0; +} + +static int tile_log2(int blkSize, int target) { + int k; + for (k = 0; (blkSize << k) < target; k++); + return k; +} + +static int vaapi_encode_av1_set_tile(AVCodecContext *avctx) +{ + VAAPIEncodeAV1Context *priv = avctx->priv_data; + int mi_cols, mi_rows, sb_shift, sb_size; + int max_tile_area_sb, max_tile_area_sb_varied; + int tile_width_sb, tile_height_sb, widest_tile_sb; + int tile_cols, tile_rows; + int min_log2_tiles; + int i; + + if (priv->tile_cols > AV1_MAX_TILE_COLS || + priv->tile_rows > AV1_MAX_TILE_ROWS) { + av_log(avctx, AV_LOG_ERROR, "Invalid tile number %dx%d, should less than %dx%d.\n", + priv->tile_cols, priv->tile_rows, AV1_MAX_TILE_COLS, AV1_MAX_TILE_ROWS); + return AVERROR(EINVAL); + } + + mi_cols = 2 * ((avctx->width + 7) >> 3); + mi_rows = 2 * ((avctx->height + 7) >> 3); + priv->sb_cols = priv->use_128x128_superblock ? + ((mi_cols + 31) >> 5) : ((mi_cols + 15) >> 4); + priv->sb_rows = priv->use_128x128_superblock ? + ((mi_rows + 31) >> 5) : ((mi_rows + 15) >> 4); + sb_shift = priv->use_128x128_superblock ? 5 : 4; + sb_size = sb_shift + 2; + priv->max_tile_width_sb = AV1_MAX_TILE_WIDTH >> sb_size; + max_tile_area_sb = AV1_MAX_TILE_AREA >> (2 * sb_size); + + priv->min_log2_tile_cols = tile_log2(priv->max_tile_width_sb, priv->sb_cols); + priv->max_log2_tile_cols = tile_log2(1, FFMIN(priv->sb_cols, AV1_MAX_TILE_COLS)); + priv->max_log2_tile_rows = tile_log2(1, FFMIN(priv->sb_rows, AV1_MAX_TILE_ROWS)); + min_log2_tiles = FFMAX(priv->min_log2_tile_cols, + tile_log2(max_tile_area_sb, priv->sb_rows * priv->sb_cols)); + + tile_cols = av_clip(priv->tile_cols, (priv->sb_cols + priv->max_tile_width_sb - 1) / priv->max_tile_width_sb, priv->sb_cols); + + if (!priv->tile_cols) + priv->tile_cols = tile_cols; + else if (priv->tile_cols != tile_cols){ + av_log(avctx, AV_LOG_ERROR, "Invalid tile cols %d, should be in range of %d~%d\n", + priv->tile_cols, + (priv->sb_cols + priv->max_tile_width_sb - 1) / priv->max_tile_width_sb, + priv->sb_cols); + return AVERROR(EINVAL); + } + + priv->tile_cols_log2 = tile_log2(1, priv->tile_cols); + tile_width_sb = (priv->sb_cols + (1 << priv->tile_cols_log2) - 1) >> + priv->tile_cols_log2; + + if (priv->tile_rows > priv->sb_rows) { + av_log(avctx, AV_LOG_ERROR, "Invalid tile rows %d, should be less than %d.\n", + priv->tile_rows, priv->sb_rows); + return AVERROR(EINVAL); + } + + /** Try user setting tile rows number first. */ + tile_rows = priv->tile_rows ? priv->tile_rows : 1; + for (; tile_rows <= priv->sb_rows && tile_rows <= AV1_MAX_TILE_ROWS; tile_rows++) { + /** try uniformed tile. */ + priv->tile_rows_log2 = tile_log2(1, tile_rows); + if ((priv->sb_cols + tile_width_sb - 1) / tile_width_sb == priv->tile_cols) { + for (i = 0; i < priv->tile_cols - 1; i++) + priv->width_in_sbs_minus_1[i] = tile_width_sb - 1; + priv->width_in_sbs_minus_1[i] = priv->sb_cols - (priv->tile_cols - 1) * tile_width_sb - 1; + + tile_height_sb = (priv->sb_rows + (1 << priv->tile_rows_log2) - 1) >> + priv->tile_rows_log2; + + if ((priv->sb_rows + tile_height_sb - 1) / tile_height_sb == tile_rows && + tile_height_sb <= max_tile_area_sb / tile_width_sb) { + for (i = 0; i < tile_rows - 1; i++) + priv->height_in_sbs_minus_1[i] = tile_height_sb - 1; + priv->height_in_sbs_minus_1[i] = priv->sb_rows - (tile_rows - 1) * tile_height_sb - 1; + + priv->uniform_tile = 1; + priv->min_log2_tile_rows = FFMAX(min_log2_tiles - priv->tile_cols_log2, 0); + + break; + } + } + + /** try non-uniformed tile. */ + widest_tile_sb = 0; + for (i = 0; i < priv->tile_cols; i++) { + priv->width_in_sbs_minus_1[i] = (i + 1) * priv->sb_cols / priv->tile_cols - i * priv->sb_cols / priv->tile_cols - 1; + widest_tile_sb = FFMAX(widest_tile_sb, priv->width_in_sbs_minus_1[i] + 1); + } + + if (min_log2_tiles) + max_tile_area_sb_varied = (priv->sb_rows * priv->sb_cols) >> (min_log2_tiles + 1); + else + max_tile_area_sb_varied = priv->sb_rows * priv->sb_cols; + priv->max_tile_height_sb = FFMAX(1, max_tile_area_sb_varied / widest_tile_sb); + + if (tile_rows == av_clip(tile_rows, (priv->sb_rows + priv->max_tile_height_sb - 1) / priv->max_tile_height_sb, priv->sb_rows)) { + for (i = 0; i < tile_rows; i++) + priv->height_in_sbs_minus_1[i] = (i + 1) * priv->sb_rows / tile_rows - i * priv->sb_rows / tile_rows - 1; + + break; + } + + /** Return invalid parameter if explicit tile rows is set. */ + if (priv->tile_rows) { + av_log(avctx, AV_LOG_ERROR, "Invalid tile rows %d.\n", priv->tile_rows); + return AVERROR(EINVAL); + } + } + + priv->tile_rows = tile_rows; + av_log(avctx, AV_LOG_DEBUG, "Setting tile cols/rows to %d/%d.\n", + priv->tile_cols, priv->tile_rows); + + /** check if tile cols/rows is supported by driver. */ + if (priv->attr_ext2.bits.max_tile_num_minus1) { + if ((priv->tile_cols * priv->tile_rows - 1) > priv->attr_ext2.bits.max_tile_num_minus1) { + av_log(avctx, AV_LOG_ERROR, "Unsupported tile num %d * %d = %d by driver, " + "should be at most %d.\n", priv->tile_cols, priv->tile_rows, + priv->tile_cols * priv->tile_rows, + priv->attr_ext2.bits.max_tile_num_minus1 + 1); + return AVERROR(EINVAL); + } + } + + /** check if tile group numbers is valid. */ + if (priv->tile_groups > priv->tile_cols * priv->tile_rows) { + av_log(avctx, AV_LOG_WARNING, "Invalid tile groups number %d, " + "correct to %d.\n", priv->tile_groups, priv->tile_cols * priv->tile_rows); + priv->tile_groups = priv->tile_cols * priv->tile_rows; + } + + return 0; +} + +static int vaapi_encode_av1_write_sequence_header(AVCodecContext *avctx, + char *data, size_t *data_len) +{ + VAAPIEncodeAV1Context *priv = avctx->priv_data; + + memcpy(data, &priv->sh_data, MAX_PARAM_BUFFER_SIZE * sizeof(char)); + *data_len = priv->sh_data_len; + + return 0; +} + +static int vaapi_encode_av1_init_sequence_params(AVCodecContext *avctx) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeAV1Context *priv = avctx->priv_data; + AV1RawOBU *sh_obu = &priv->sh; + AV1RawSequenceHeader *sh = &sh_obu->obu.sequence_header; + VAEncSequenceParameterBufferAV1 *vseq = ctx->codec_sequence_params; + CodedBitstreamFragment *obu = &priv->current_obu; + const AVPixFmtDescriptor *desc; + int ret; + + memset(sh_obu, 0, sizeof(*sh_obu)); + sh_obu->header.obu_type = AV1_OBU_SEQUENCE_HEADER; + + desc = av_pix_fmt_desc_get(priv->common.input_frames->sw_format); + av_assert0(desc); + + sh->seq_profile = avctx->profile; + if (!sh->seq_force_screen_content_tools) + sh->seq_force_integer_mv = AV1_SELECT_INTEGER_MV; + sh->frame_width_bits_minus_1 = av_log2(avctx->width); + sh->frame_height_bits_minus_1 = av_log2(avctx->height); + sh->max_frame_width_minus_1 = avctx->width - 1; + sh->max_frame_height_minus_1 = avctx->height - 1; + sh->seq_tier[0] = priv->tier; + /** enable order hint and reserve maximum 8 bits for it by default. */ + sh->enable_order_hint = 1; + sh->order_hint_bits_minus_1 = 7; + + sh->color_config = (AV1RawColorConfig) { + .high_bitdepth = desc->comp[0].depth == 8 ? 0 : 1, + .color_primaries = avctx->color_primaries, + .transfer_characteristics = avctx->color_trc, + .matrix_coefficients = avctx->colorspace, + .color_description_present_flag = (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED || + avctx->color_trc != AVCOL_TRC_UNSPECIFIED || + avctx->colorspace != AVCOL_SPC_UNSPECIFIED), + .color_range = avctx->color_range == AVCOL_RANGE_JPEG, + .subsampling_x = desc->log2_chroma_w, + .subsampling_y = desc->log2_chroma_h, + }; + + switch (avctx->chroma_sample_location) { + case AVCHROMA_LOC_LEFT: + sh->color_config.chroma_sample_position = AV1_CSP_VERTICAL; + break; + case AVCHROMA_LOC_TOPLEFT: + sh->color_config.chroma_sample_position = AV1_CSP_COLOCATED; + break; + default: + sh->color_config.chroma_sample_position = AV1_CSP_UNKNOWN; + break; + } + + if (avctx->level != AV_LEVEL_UNKNOWN) { + sh->seq_level_idx[0] = avctx->level; + } else { + const AV1LevelDescriptor *level; + float framerate; + + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) + framerate = avctx->framerate.num / avctx->framerate.den; + else + framerate = 0; + + level = ff_av1_guess_level(avctx->bit_rate, priv->tier, + ctx->surface_width, ctx->surface_height, + priv->tile_rows * priv->tile_cols, + priv->tile_cols, framerate); + if (level) { + av_log(avctx, AV_LOG_VERBOSE, "Using level %s.\n", level->name); + sh->seq_level_idx[0] = level->level_idx; + } else { + av_log(avctx, AV_LOG_VERBOSE, "Stream will not conform to " + "any normal level, using maximum parameters level by default.\n"); + sh->seq_level_idx[0] = 31; + sh->seq_tier[0] = 1; + } + } + vseq->seq_profile = sh->seq_profile; + vseq->seq_level_idx = sh->seq_level_idx[0]; + vseq->seq_tier = sh->seq_tier[0]; + vseq->order_hint_bits_minus_1 = sh->order_hint_bits_minus_1; + vseq->intra_period = ctx->gop_size; + vseq->ip_period = ctx->b_per_p + 1; + + vseq->seq_fields.bits.enable_order_hint = sh->enable_order_hint; + + if (!(ctx->va_rc_mode & VA_RC_CQP)) { + vseq->bits_per_second = ctx->va_bit_rate; + vseq->seq_fields.bits.enable_cdef = sh->enable_cdef = 1; + } + + ret = vaapi_encode_av1_add_obu(avctx, obu, AV1_OBU_SEQUENCE_HEADER, &priv->sh); + if (ret < 0) + goto end; + + ret = vaapi_encode_av1_write_obu(avctx, priv->sh_data, &priv->sh_data_len, obu); + if (ret < 0) + goto end; + +end: + ff_cbs_fragment_reset(obu); + return ret; +} + +static int vaapi_encode_av1_init_picture_params(AVCodecContext *avctx, + VAAPIEncodePicture *pic) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeAV1Context *priv = avctx->priv_data; + VAAPIEncodeAV1Picture *hpic = pic->priv_data; + AV1RawOBU *fh_obu = &priv->fh; + AV1RawFrameHeader *fh = &fh_obu->obu.frame.header; + VAEncPictureParameterBufferAV1 *vpic = pic->codec_picture_params; + CodedBitstreamFragment *obu = &priv->current_obu; + VAAPIEncodePicture *ref; + VAAPIEncodeAV1Picture *href; + int slot, i; + int ret; + static const int8_t default_loop_filter_ref_deltas[AV1_TOTAL_REFS_PER_FRAME] = + { 1, 0, 0, 0, -1, 0, -1, -1 }; + + memset(fh_obu, 0, sizeof(*fh_obu)); + pic->nb_slices = priv->tile_groups; + pic->non_independent_frame = pic->encode_order < pic->display_order; + fh_obu->header.obu_type = AV1_OBU_FRAME_HEADER; + fh_obu->header.obu_has_size_field = 1; + + switch (pic->type) { + case PICTURE_TYPE_IDR: + av_assert0(pic->nb_refs[0] == 0 || pic->nb_refs[1]); + fh->frame_type = AV1_FRAME_KEY; + fh->refresh_frame_flags = 0xFF; + fh->base_q_idx = priv->q_idx_idr; + hpic->slot = 0; + hpic->last_idr_frame = pic->display_order; + break; + case PICTURE_TYPE_P: + av_assert0(pic->nb_refs[0]); + fh->frame_type = AV1_FRAME_INTER; + fh->base_q_idx = priv->q_idx_p; + ref = pic->refs[0][pic->nb_refs[0] - 1]; + href = ref->priv_data; + hpic->slot = !href->slot; + hpic->last_idr_frame = href->last_idr_frame; + fh->refresh_frame_flags = 1 << hpic->slot; + + /** set the nearest frame in L0 as all reference frame. */ + for (i = 0; i < AV1_REFS_PER_FRAME; i++) { + fh->ref_frame_idx[i] = href->slot; + } + fh->primary_ref_frame = href->slot; + fh->ref_order_hint[href->slot] = ref->display_order - href->last_idr_frame; + vpic->ref_frame_ctrl_l0.fields.search_idx0 = AV1_REF_FRAME_LAST; + + /** set the 2nd nearest frame in L0 as Golden frame. */ + if (pic->nb_refs[0] > 1) { + ref = pic->refs[0][pic->nb_refs[0] - 2]; + href = ref->priv_data; + fh->ref_frame_idx[3] = href->slot; + fh->ref_order_hint[href->slot] = ref->display_order - href->last_idr_frame; + vpic->ref_frame_ctrl_l0.fields.search_idx1 = AV1_REF_FRAME_GOLDEN; + } + break; + case PICTURE_TYPE_B: + av_assert0(pic->nb_refs[0] && pic->nb_refs[1]); + fh->frame_type = AV1_FRAME_INTER; + fh->base_q_idx = priv->q_idx_b; + fh->refresh_frame_flags = 0x0; + fh->reference_select = 1; + + /** B frame will not be referenced, disable its recon frame. */ + vpic->picture_flags.bits.disable_frame_recon = 1; + + /** Use LAST_FRAME and BWDREF_FRAME for reference. */ + vpic->ref_frame_ctrl_l0.fields.search_idx0 = AV1_REF_FRAME_LAST; + vpic->ref_frame_ctrl_l1.fields.search_idx0 = AV1_REF_FRAME_BWDREF; + + ref = pic->refs[0][pic->nb_refs[0] - 1]; + href = ref->priv_data; + hpic->last_idr_frame = href->last_idr_frame; + fh->primary_ref_frame = href->slot; + fh->ref_order_hint[href->slot] = ref->display_order - href->last_idr_frame; + for (i = 0; i < AV1_REF_FRAME_GOLDEN; i++) { + fh->ref_frame_idx[i] = href->slot; + } + + ref = pic->refs[1][pic->nb_refs[1] - 1]; + href = ref->priv_data; + fh->ref_order_hint[href->slot] = ref->display_order - href->last_idr_frame; + for (i = AV1_REF_FRAME_GOLDEN; i < AV1_REFS_PER_FRAME; i++) { + fh->ref_frame_idx[i] = href->slot; + } + break; + default: + av_assert0(0 && "invalid picture type"); + } + + fh->show_frame = pic->display_order <= pic->encode_order; + fh->showable_frame = fh->frame_type != AV1_FRAME_KEY; + fh->frame_width_minus_1 = avctx->width - 1; + fh->frame_height_minus_1 = avctx->height - 1; + fh->render_width_minus_1 = fh->frame_width_minus_1; + fh->render_height_minus_1 = fh->frame_height_minus_1; + fh->order_hint = pic->display_order - hpic->last_idr_frame; + fh->tile_cols = priv->tile_cols; + fh->tile_rows = priv->tile_rows; + fh->tile_cols_log2 = priv->tile_cols_log2; + fh->tile_rows_log2 = priv->tile_rows_log2; + fh->uniform_tile_spacing_flag = priv->uniform_tile; + fh->tile_size_bytes_minus1 = priv->attr_ext2.bits.tile_size_bytes_minus1; + + /** ignore ONLY_4x4 mode for codedlossless is not fully implemented. */ + if (priv->attr_ext2.bits.tx_mode_support & 0x04) + fh->tx_mode = AV1_TX_MODE_SELECT; + else if (priv->attr_ext2.bits.tx_mode_support & 0x02) + fh->tx_mode = AV1_TX_MODE_LARGEST; + else { + av_log(avctx, AV_LOG_ERROR, "No available tx mode found.\n"); + return AVERROR(EINVAL); + } + + for (i = 0; i < fh->tile_cols; i++) + fh->width_in_sbs_minus_1[i] = vpic->width_in_sbs_minus_1[i] = priv->width_in_sbs_minus_1[i]; + + for (i = 0; i < fh->tile_rows; i++) + fh->height_in_sbs_minus_1[i] = vpic->height_in_sbs_minus_1[i] = priv->height_in_sbs_minus_1[i]; + + memcpy(fh->loop_filter_ref_deltas, default_loop_filter_ref_deltas, + AV1_TOTAL_REFS_PER_FRAME * sizeof(int8_t)); + + if (fh->frame_type == AV1_FRAME_KEY && fh->show_frame) { + fh->error_resilient_mode = 1; + } + + if (fh->frame_type == AV1_FRAME_KEY || fh->error_resilient_mode) + fh->primary_ref_frame = AV1_PRIMARY_REF_NONE; + + vpic->base_qindex = fh->base_q_idx; + vpic->frame_width_minus_1 = fh->frame_width_minus_1; + vpic->frame_height_minus_1 = fh->frame_height_minus_1; + vpic->primary_ref_frame = fh->primary_ref_frame; + vpic->reconstructed_frame = pic->recon_surface; + vpic->coded_buf = pic->output_buffer; + vpic->tile_cols = fh->tile_cols; + vpic->tile_rows = fh->tile_rows; + vpic->order_hint = fh->order_hint; +#if VA_CHECK_VERSION(1, 15, 0) + vpic->refresh_frame_flags = fh->refresh_frame_flags; +#endif + + vpic->picture_flags.bits.enable_frame_obu = 0; + vpic->picture_flags.bits.frame_type = fh->frame_type; + vpic->picture_flags.bits.reduced_tx_set = fh->reduced_tx_set; + vpic->picture_flags.bits.error_resilient_mode = fh->error_resilient_mode; + + /** let driver decide to use single or compound reference prediction mode. */ + vpic->mode_control_flags.bits.reference_mode = fh->reference_select ? 2 : 0; + vpic->mode_control_flags.bits.tx_mode = fh->tx_mode; + + vpic->tile_group_obu_hdr_info.bits.obu_has_size_field = 1; + + /** set reference. */ + for (i = 0; i < AV1_REFS_PER_FRAME; i++) + vpic->ref_frame_idx[i] = fh->ref_frame_idx[i]; + + for (i = 0; i < FF_ARRAY_ELEMS(vpic->reference_frames); i++) + vpic->reference_frames[i] = VA_INVALID_SURFACE; + + for (i = 0; i < MAX_REFERENCE_LIST_NUM; i++) { + for (int j = 0; j < pic->nb_refs[i]; j++) { + VAAPIEncodePicture *ref_pic = pic->refs[i][j]; + + slot = ((VAAPIEncodeAV1Picture*)ref_pic->priv_data)->slot; + av_assert0(vpic->reference_frames[slot] == VA_INVALID_SURFACE); + + vpic->reference_frames[slot] = ref_pic->recon_surface; + } + } + + ret = vaapi_encode_av1_add_obu(avctx, obu, AV1_OBU_FRAME_HEADER, &priv->fh); + if (ret < 0) + goto end; + + ret = vaapi_encode_av1_write_obu(avctx, priv->fh_data, &priv->fh_data_len, obu); + if (ret < 0) + goto end; + + if (!(ctx->va_rc_mode & VA_RC_CQP)) { + vpic->min_base_qindex = av_clip(avctx->qmin, 1, AV1_MAX_QUANT); + vpic->max_base_qindex = av_clip(avctx->qmax, 1, AV1_MAX_QUANT); + + vpic->bit_offset_qindex = priv->qindex_offset; + vpic->bit_offset_loopfilter_params = priv->loopfilter_offset; + vpic->bit_offset_cdef_params = priv->cdef_start_offset; + vpic->size_in_bits_cdef_params = priv->cdef_param_size; + vpic->size_in_bits_frame_hdr_obu = priv->fh_data_len; + vpic->byte_offset_frame_hdr_obu_size = (((pic->type == PICTURE_TYPE_IDR) ? + priv->sh_data_len / 8 : 0) + + (fh_obu->header.obu_extension_flag ? + 2 : 1)); + } + +end: + ff_cbs_fragment_reset(obu); + return ret; +} + +static int vaapi_encode_av1_init_slice_params(AVCodecContext *avctx, + VAAPIEncodePicture *pic, + VAAPIEncodeSlice *slice) +{ + VAAPIEncodeAV1Context *priv = avctx->priv_data; + VAEncTileGroupBufferAV1 *vslice = slice->codec_slice_params; + CodedBitstreamAV1Context *cbctx = priv->cbc->priv_data; + int div; + + /** Set tile group info. */ + div = priv->tile_cols * priv->tile_rows / priv->tile_groups; + vslice->tg_start = slice->index * div; + if (slice->index == (priv->tile_groups - 1)) { + vslice->tg_end = priv->tile_cols * priv->tile_rows - 1; + cbctx->seen_frame_header = 0; + } else { + vslice->tg_end = (slice->index + 1) * div - 1; + } + + return 0; +} + +static int vaapi_encode_av1_write_picture_header(AVCodecContext *avctx, + VAAPIEncodePicture *pic, + char *data, size_t *data_len) +{ + VAAPIEncodeAV1Context *priv = avctx->priv_data; + CodedBitstreamFragment *obu = &priv->current_obu; + CodedBitstreamAV1Context *cbctx = priv->cbc->priv_data; + AV1RawOBU *fh_obu = &priv->fh; + AV1RawFrameHeader *rep_fh = &fh_obu->obu.frame_header; + VAAPIEncodeAV1Picture *href; + int ret = 0; + + pic->tail_size = 0; + /** Pack repeat frame header. */ + if (pic->display_order > pic->encode_order) { + memset(fh_obu, 0, sizeof(*fh_obu)); + href = pic->refs[0][pic->nb_refs[0] - 1]->priv_data; + fh_obu->header.obu_type = AV1_OBU_FRAME_HEADER; + fh_obu->header.obu_has_size_field = 1; + + rep_fh->show_existing_frame = 1; + rep_fh->frame_to_show_map_idx = href->slot == 0; + rep_fh->frame_type = AV1_FRAME_INTER; + rep_fh->frame_width_minus_1 = avctx->width - 1; + rep_fh->frame_height_minus_1 = avctx->height - 1; + rep_fh->render_width_minus_1 = rep_fh->frame_width_minus_1; + rep_fh->render_height_minus_1 = rep_fh->frame_height_minus_1; + + cbctx->seen_frame_header = 0; + + ret = vaapi_encode_av1_add_obu(avctx, obu, AV1_OBU_FRAME_HEADER, &priv->fh); + if (ret < 0) + goto end; + + ret = vaapi_encode_av1_write_obu(avctx, pic->tail_data, &pic->tail_size, obu); + if (ret < 0) + goto end; + + pic->tail_size /= 8; + } + + memcpy(data, &priv->fh_data, MAX_PARAM_BUFFER_SIZE * sizeof(char)); + *data_len = priv->fh_data_len; + +end: + ff_cbs_fragment_reset(obu); + return ret; +} + +static const VAAPIEncodeProfile vaapi_encode_av1_profiles[] = { + { AV_PROFILE_AV1_MAIN, 8, 3, 1, 1, VAProfileAV1Profile0 }, + { AV_PROFILE_AV1_MAIN, 10, 3, 1, 1, VAProfileAV1Profile0 }, + { AV_PROFILE_UNKNOWN } +}; + +static const VAAPIEncodeType vaapi_encode_type_av1 = { + .profiles = vaapi_encode_av1_profiles, + .flags = FLAG_B_PICTURES | FLAG_TIMESTAMP_NO_DELAY, + .default_quality = 25, + + .get_encoder_caps = &vaapi_encode_av1_get_encoder_caps, + .configure = &vaapi_encode_av1_configure, + + .sequence_header_type = VAEncPackedHeaderSequence, + .sequence_params_size = sizeof(VAEncSequenceParameterBufferAV1), + .init_sequence_params = &vaapi_encode_av1_init_sequence_params, + .write_sequence_header = &vaapi_encode_av1_write_sequence_header, + + .picture_priv_data_size = sizeof(VAAPIEncodeAV1Picture), + .picture_header_type = VAEncPackedHeaderPicture, + .picture_params_size = sizeof(VAEncPictureParameterBufferAV1), + .init_picture_params = &vaapi_encode_av1_init_picture_params, + .write_picture_header = &vaapi_encode_av1_write_picture_header, + + .slice_params_size = sizeof(VAEncTileGroupBufferAV1), + .init_slice_params = &vaapi_encode_av1_init_slice_params, +}; + +static av_cold int vaapi_encode_av1_init(AVCodecContext *avctx) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeAV1Context *priv = avctx->priv_data; + VAConfigAttrib attr; + VAStatus vas; + int ret; + + ctx->codec = &vaapi_encode_type_av1; + + ctx->desired_packed_headers = + VA_ENC_PACKED_HEADER_SEQUENCE | + VA_ENC_PACKED_HEADER_PICTURE; + + if (avctx->profile == AV_PROFILE_UNKNOWN) + avctx->profile = priv->profile; + if (avctx->level == AV_LEVEL_UNKNOWN) + avctx->level = priv->level; + + if (avctx->level != AV_LEVEL_UNKNOWN && avctx->level & ~0x1f) { + av_log(avctx, AV_LOG_ERROR, "Invalid level %d\n", avctx->level); + return AVERROR(EINVAL); + } + + ret = ff_vaapi_encode_init(avctx); + if (ret < 0) + return ret; + + attr.type = VAConfigAttribEncAV1; + vas = vaGetConfigAttributes(ctx->hwctx->display, + ctx->va_profile, + ctx->va_entrypoint, + &attr, 1); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to query " + "config attribute: %d (%s).\n", vas, vaErrorStr(vas)); + return AVERROR_EXTERNAL; + } else if (attr.value == VA_ATTRIB_NOT_SUPPORTED) { + priv->attr.value = 0; + av_log(avctx, AV_LOG_WARNING, "Attribute type:%d is not " + "supported.\n", attr.type); + } else { + priv->attr.value = attr.value; + } + + attr.type = VAConfigAttribEncAV1Ext1; + vas = vaGetConfigAttributes(ctx->hwctx->display, + ctx->va_profile, + ctx->va_entrypoint, + &attr, 1); + if (vas != VA_STATUS_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to query " + "config attribute: %d (%s).\n", vas, vaErrorStr(vas)); + return AVERROR_EXTERNAL; + } else if (attr.value == VA_ATTRIB_NOT_SUPPORTED) { + priv->attr_ext1.value = 0; + av_log(avctx, AV_LOG_WARNING, "Attribute type:%d is not " + "supported.\n", attr.type); + } else { + priv->attr_ext1.value = attr.value; + } + + /** This attr provides essential indicators, return error if not support. */ + attr.type = VAConfigAttribEncAV1Ext2; + vas = vaGetConfigAttributes(ctx->hwctx->display, + ctx->va_profile, + ctx->va_entrypoint, + &attr, 1); + if (vas != VA_STATUS_SUCCESS || attr.value == VA_ATTRIB_NOT_SUPPORTED) { + av_log(avctx, AV_LOG_ERROR, "Failed to query " + "config attribute: %d (%s).\n", vas, vaErrorStr(vas)); + return AVERROR_EXTERNAL; + } else { + priv->attr_ext2.value = attr.value; + } + + av_opt_set_int(priv->cbc->priv_data, "fixed_obu_size_length", + priv->attr_ext2.bits.obu_size_bytes_minus1 + 1, 0); + + ret = vaapi_encode_av1_set_tile(avctx); + if (ret < 0) + return ret; + + return 0; +} + +static av_cold int vaapi_encode_av1_close(AVCodecContext *avctx) +{ + VAAPIEncodeAV1Context *priv = avctx->priv_data; + + ff_cbs_fragment_free(&priv->current_obu); + ff_cbs_close(&priv->cbc); + + return ff_vaapi_encode_close(avctx); +} + +#define OFFSET(x) offsetof(VAAPIEncodeAV1Context, x) +#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM) + +static const AVOption vaapi_encode_av1_options[] = { + VAAPI_ENCODE_COMMON_OPTIONS, + VAAPI_ENCODE_RC_OPTIONS, + { "profile", "Set profile (seq_profile)", + OFFSET(profile), AV_OPT_TYPE_INT, + { .i64 = AV_PROFILE_UNKNOWN }, AV_PROFILE_UNKNOWN, 0xff, FLAGS, .unit = "profile" }, + +#define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ + { .i64 = value }, 0, 0, FLAGS, .unit = "profile" + { PROFILE("main", AV_PROFILE_AV1_MAIN) }, + { PROFILE("high", AV_PROFILE_AV1_HIGH) }, + { PROFILE("professional", AV_PROFILE_AV1_PROFESSIONAL) }, +#undef PROFILE + + { "tier", "Set tier (seq_tier)", + OFFSET(tier), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, .unit = "tier" }, + { "main", NULL, 0, AV_OPT_TYPE_CONST, + { .i64 = 0 }, 0, 0, FLAGS, .unit = "tier" }, + { "high", NULL, 0, AV_OPT_TYPE_CONST, + { .i64 = 1 }, 0, 0, FLAGS, .unit = "tier" }, + { "level", "Set level (seq_level_idx)", + OFFSET(level), AV_OPT_TYPE_INT, + { .i64 = AV_LEVEL_UNKNOWN }, AV_LEVEL_UNKNOWN, 0x1f, FLAGS, .unit = "level" }, + +#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ + { .i64 = value }, 0, 0, FLAGS, .unit = "level" + { LEVEL("2.0", 0) }, + { LEVEL("2.1", 1) }, + { LEVEL("3.0", 4) }, + { LEVEL("3.1", 5) }, + { LEVEL("4.0", 8) }, + { LEVEL("4.1", 9) }, + { LEVEL("5.0", 12) }, + { LEVEL("5.1", 13) }, + { LEVEL("5.2", 14) }, + { LEVEL("5.3", 15) }, + { LEVEL("6.0", 16) }, + { LEVEL("6.1", 17) }, + { LEVEL("6.2", 18) }, + { LEVEL("6.3", 19) }, +#undef LEVEL + + { "tiles", "Tile columns x rows (Use minimal tile column/row number automatically by default)", + OFFSET(tile_cols), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, 0, FLAGS }, + { "tile_groups", "Number of tile groups for encoding", + OFFSET(tile_groups), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, AV1_MAX_TILE_ROWS * AV1_MAX_TILE_COLS, FLAGS }, + + { NULL }, +}; + +static const FFCodecDefault vaapi_encode_av1_defaults[] = { + { "b", "0" }, + { "bf", "2" }, + { "g", "120" }, + { "qmin", "1" }, + { "qmax", "255" }, + { NULL }, +}; + +static const AVClass vaapi_encode_av1_class = { + .class_name = "av1_vaapi", + .item_name = av_default_item_name, + .option = vaapi_encode_av1_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_av1_vaapi_encoder = { + .p.name = "av1_vaapi", + CODEC_LONG_NAME("AV1 (VAAPI)"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .priv_data_size = sizeof(VAAPIEncodeAV1Context), + .init = &vaapi_encode_av1_init, + FF_CODEC_RECEIVE_PACKET_CB(&ff_vaapi_encode_receive_packet), + .close = &vaapi_encode_av1_close, + .p.priv_class = &vaapi_encode_av1_class, + .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE | + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE, + .caps_internal = FF_CODEC_CAP_NOT_INIT_THREADSAFE | + FF_CODEC_CAP_INIT_CLEANUP, + .defaults = vaapi_encode_av1_defaults, + .p.pix_fmts = (const enum AVPixelFormat[]) { + AV_PIX_FMT_VAAPI, + AV_PIX_FMT_NONE, + }, + .hw_configs = ff_vaapi_encode_hw_configs, + .p.wrapper_name = "vaapi", +}; diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c index 645f6a978db..37df9103aec 100644 --- a/libavcodec/vaapi_encode_h264.c +++ b/libavcodec/vaapi_encode_h264.c @@ -23,6 +23,7 @@ #include "libavutil/avassert.h" #include "libavutil/common.h" +#include "libavutil/pixdesc.h" #include "libavutil/internal.h" #include "libavutil/opt.h" @@ -301,24 +302,35 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx) H264RawPPS *pps = &priv->raw_pps; VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params; VAEncPictureParameterBufferH264 *vpic = ctx->codec_picture_params; + const AVPixFmtDescriptor *desc; + int bit_depth; memset(sps, 0, sizeof(*sps)); memset(pps, 0, sizeof(*pps)); + desc = av_pix_fmt_desc_get(priv->common.input_frames->sw_format); + av_assert0(desc); + if (desc->nb_components == 1 || desc->log2_chroma_w != 1 || desc->log2_chroma_h != 1) { + av_log(avctx, AV_LOG_ERROR, "Chroma format of input pixel format " + "%s is not supported.\n", desc->name); + return AVERROR(EINVAL); + } + bit_depth = desc->comp[0].depth; + sps->nal_unit_header.nal_ref_idc = 3; sps->nal_unit_header.nal_unit_type = H264_NAL_SPS; sps->profile_idc = avctx->profile & 0xff; - if (avctx->profile == FF_PROFILE_H264_CONSTRAINED_BASELINE || - avctx->profile == FF_PROFILE_H264_MAIN) + if (avctx->profile == AV_PROFILE_H264_CONSTRAINED_BASELINE || + avctx->profile == AV_PROFILE_H264_MAIN) sps->constraint_set1_flag = 1; - if (avctx->profile == FF_PROFILE_H264_HIGH) + if (avctx->profile == AV_PROFILE_H264_HIGH || avctx->profile == AV_PROFILE_H264_HIGH_10) sps->constraint_set3_flag = ctx->gop_size == 1; - if (avctx->profile == FF_PROFILE_H264_MAIN || - avctx->profile == FF_PROFILE_H264_HIGH) { + if (avctx->profile == AV_PROFILE_H264_MAIN || + avctx->profile == AV_PROFILE_H264_HIGH || avctx->profile == AV_PROFILE_H264_HIGH_10) { sps->constraint_set4_flag = 1; sps->constraint_set5_flag = ctx->b_per_p == 0; } @@ -328,7 +340,7 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx) else priv->dpb_frames = 1 + ctx->max_b_depth; - if (avctx->level != FF_LEVEL_UNKNOWN) { + if (avctx->level != AV_LEVEL_UNKNOWN) { sps->level_idc = avctx->level; } else { const H264LevelDescriptor *level; @@ -359,6 +371,8 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx) sps->seq_parameter_set_id = 0; sps->chroma_format_idc = 1; + sps->bit_depth_luma_minus8 = bit_depth - 8; + sps->bit_depth_chroma_minus8 = bit_depth - 8; sps->log2_max_frame_num_minus4 = 4; sps->pic_order_cnt_type = ctx->max_b_depth ? 0 : 2; @@ -498,9 +512,9 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx) pps->seq_parameter_set_id = 0; pps->entropy_coding_mode_flag = - !(sps->profile_idc == FF_PROFILE_H264_BASELINE || - sps->profile_idc == FF_PROFILE_H264_EXTENDED || - sps->profile_idc == FF_PROFILE_H264_CAVLC_444); + !(sps->profile_idc == AV_PROFILE_H264_BASELINE || + sps->profile_idc == AV_PROFILE_H264_EXTENDED || + sps->profile_idc == AV_PROFILE_H264_CAVLC_444); if (!priv->coder && pps->entropy_coding_mode_flag) pps->entropy_coding_mode_flag = 0; @@ -509,9 +523,9 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx) pps->pic_init_qp_minus26 = priv->fixed_qp_idr - 26; - if (sps->profile_idc == FF_PROFILE_H264_BASELINE || - sps->profile_idc == FF_PROFILE_H264_EXTENDED || - sps->profile_idc == FF_PROFILE_H264_MAIN) { + if (sps->profile_idc == AV_PROFILE_H264_BASELINE || + sps->profile_idc == AV_PROFILE_H264_EXTENDED || + sps->profile_idc == AV_PROFILE_H264_MAIN) { pps->more_rbsp_data = 0; } else { pps->more_rbsp_data = 1; @@ -614,7 +628,7 @@ static int vaapi_encode_h264_init_picture_params(AVCodecContext *avctx, VAAPIEncodePicture *prev = pic->prev; VAAPIEncodeH264Picture *hprev = prev ? prev->priv_data : NULL; VAEncPictureParameterBufferH264 *vpic = pic->codec_picture_params; - int i; + int i, j = 0; if (pic->type == PICTURE_TYPE_IDR) { av_assert0(pic->display_order == pic->encode_order); @@ -715,24 +729,26 @@ static int vaapi_encode_h264_init_picture_params(AVCodecContext *avctx, .TopFieldOrderCnt = hpic->pic_order_cnt, .BottomFieldOrderCnt = hpic->pic_order_cnt, }; - - for (i = 0; i < pic->nb_refs; i++) { - VAAPIEncodePicture *ref = pic->refs[i]; - VAAPIEncodeH264Picture *href; - - av_assert0(ref && ref->encode_order < pic->encode_order); - href = ref->priv_data; - - vpic->ReferenceFrames[i] = (VAPictureH264) { - .picture_id = ref->recon_surface, - .frame_idx = href->frame_num, - .flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE, - .TopFieldOrderCnt = href->pic_order_cnt, - .BottomFieldOrderCnt = href->pic_order_cnt, - }; + for (int k = 0; k < MAX_REFERENCE_LIST_NUM; k++) { + for (i = 0; i < pic->nb_refs[k]; i++) { + VAAPIEncodePicture *ref = pic->refs[k][i]; + VAAPIEncodeH264Picture *href; + + av_assert0(ref && ref->encode_order < pic->encode_order); + href = ref->priv_data; + + vpic->ReferenceFrames[j++] = (VAPictureH264) { + .picture_id = ref->recon_surface, + .frame_idx = href->frame_num, + .flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE, + .TopFieldOrderCnt = href->pic_order_cnt, + .BottomFieldOrderCnt = href->pic_order_cnt, + }; + } } - for (; i < FF_ARRAY_ELEMS(vpic->ReferenceFrames); i++) { - vpic->ReferenceFrames[i] = (VAPictureH264) { + + for (; j < FF_ARRAY_ELEMS(vpic->ReferenceFrames); j++) { + vpic->ReferenceFrames[j] = (VAPictureH264) { .picture_id = VA_INVALID_ID, .flags = VA_PICTURE_H264_INVALID, }; @@ -934,17 +950,17 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx, if (pic->type == PICTURE_TYPE_P) { int need_rplm = 0; - for (i = 0; i < pic->nb_refs; i++) { - av_assert0(pic->refs[i]); - if (pic->refs[i] != def_l0[i]) + for (i = 0; i < pic->nb_refs[0]; i++) { + av_assert0(pic->refs[0][i]); + if (pic->refs[0][i] != def_l0[i]) need_rplm = 1; } sh->ref_pic_list_modification_flag_l0 = need_rplm; if (need_rplm) { int pic_num = hpic->frame_num; - for (i = 0; i < pic->nb_refs; i++) { - href = pic->refs[i]->priv_data; + for (i = 0; i < pic->nb_refs[0]; i++) { + href = pic->refs[0][i]->priv_data; av_assert0(href->frame_num != pic_num); if (href->frame_num < pic_num) { sh->rplm_l0[i].modification_of_pic_nums_idc = 0; @@ -963,28 +979,29 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx, } else { int need_rplm_l0 = 0, need_rplm_l1 = 0; int n0 = 0, n1 = 0; - for (i = 0; i < pic->nb_refs; i++) { - av_assert0(pic->refs[i]); - href = pic->refs[i]->priv_data; - av_assert0(href->pic_order_cnt != hpic->pic_order_cnt); - if (href->pic_order_cnt < hpic->pic_order_cnt) { - if (pic->refs[i] != def_l0[n0]) - need_rplm_l0 = 1; - ++n0; - } else { - if (pic->refs[i] != def_l1[n1]) - need_rplm_l1 = 1; - ++n1; - } + for (i = 0; i < pic->nb_refs[0]; i++) { + av_assert0(pic->refs[0][i]); + href = pic->refs[0][i]->priv_data; + av_assert0(href->pic_order_cnt < hpic->pic_order_cnt); + if (pic->refs[0][i] != def_l0[n0]) + need_rplm_l0 = 1; + ++n0; + } + + for (i = 0; i < pic->nb_refs[1]; i++) { + av_assert0(pic->refs[1][i]); + href = pic->refs[1][i]->priv_data; + av_assert0(href->pic_order_cnt > hpic->pic_order_cnt); + if (pic->refs[1][i] != def_l1[n1]) + need_rplm_l1 = 1; + ++n1; } sh->ref_pic_list_modification_flag_l0 = need_rplm_l0; if (need_rplm_l0) { int pic_num = hpic->frame_num; - for (i = j = 0; i < pic->nb_refs; i++) { - href = pic->refs[i]->priv_data; - if (href->pic_order_cnt > hpic->pic_order_cnt) - continue; + for (i = j = 0; i < pic->nb_refs[0]; i++) { + href = pic->refs[0][i]->priv_data; av_assert0(href->frame_num != pic_num); if (href->frame_num < pic_num) { sh->rplm_l0[j].modification_of_pic_nums_idc = 0; @@ -1005,10 +1022,8 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx, sh->ref_pic_list_modification_flag_l1 = need_rplm_l1; if (need_rplm_l1) { int pic_num = hpic->frame_num; - for (i = j = 0; i < pic->nb_refs; i++) { - href = pic->refs[i]->priv_data; - if (href->pic_order_cnt < hpic->pic_order_cnt) - continue; + for (i = j = 0; i < pic->nb_refs[1]; i++) { + href = pic->refs[1][i]->priv_data; av_assert0(href->frame_num != pic_num); if (href->frame_num < pic_num) { sh->rplm_l1[j].modification_of_pic_nums_idc = 0; @@ -1048,14 +1063,13 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx, vslice->RefPicList1[i].flags = VA_PICTURE_H264_INVALID; } - av_assert0(pic->nb_refs <= 2); - if (pic->nb_refs >= 1) { + if (pic->nb_refs[0]) { // Backward reference for P- or B-frame. av_assert0(pic->type == PICTURE_TYPE_P || pic->type == PICTURE_TYPE_B); vslice->RefPicList0[0] = vpic->ReferenceFrames[0]; } - if (pic->nb_refs >= 2) { + if (pic->nb_refs[1]) { // Forward reference for B-frame. av_assert0(pic->type == PICTURE_TYPE_B); vslice->RefPicList1[0] = vpic->ReferenceFrames[1]; @@ -1144,11 +1158,14 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx) } static const VAAPIEncodeProfile vaapi_encode_h264_profiles[] = { - { FF_PROFILE_H264_HIGH, 8, 3, 1, 1, VAProfileH264High }, - { FF_PROFILE_H264_MAIN, 8, 3, 1, 1, VAProfileH264Main }, - { FF_PROFILE_H264_CONSTRAINED_BASELINE, +#if VA_CHECK_VERSION(1, 18, 0) + { AV_PROFILE_H264_HIGH_10, 10, 3, 1, 1, VAProfileH264High10 }, +#endif + { AV_PROFILE_H264_HIGH, 8, 3, 1, 1, VAProfileH264High }, + { AV_PROFILE_H264_MAIN, 8, 3, 1, 1, VAProfileH264Main }, + { AV_PROFILE_H264_CONSTRAINED_BASELINE, 8, 3, 1, 1, VAProfileH264ConstrainedBaseline }, - { FF_PROFILE_UNKNOWN } + { AV_PROFILE_UNKNOWN } }; static const VAAPIEncodeType vaapi_encode_type_h264 = { @@ -1190,41 +1207,40 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx) ctx->codec = &vaapi_encode_type_h264; - if (avctx->profile == FF_PROFILE_UNKNOWN) + if (avctx->profile == AV_PROFILE_UNKNOWN) avctx->profile = priv->profile; - if (avctx->level == FF_LEVEL_UNKNOWN) + if (avctx->level == AV_LEVEL_UNKNOWN) avctx->level = priv->level; if (avctx->compression_level == FF_COMPRESSION_DEFAULT) avctx->compression_level = priv->quality; // Reject unsupported profiles. switch (avctx->profile) { - case FF_PROFILE_H264_BASELINE: + case AV_PROFILE_H264_BASELINE: av_log(avctx, AV_LOG_WARNING, "H.264 baseline profile is not " "supported, using constrained baseline profile instead.\n"); - avctx->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE; + avctx->profile = AV_PROFILE_H264_CONSTRAINED_BASELINE; break; - case FF_PROFILE_H264_EXTENDED: + case AV_PROFILE_H264_EXTENDED: av_log(avctx, AV_LOG_ERROR, "H.264 extended profile " "is not supported.\n"); return AVERROR_PATCHWELCOME; - case FF_PROFILE_H264_HIGH_10: - case FF_PROFILE_H264_HIGH_10_INTRA: - av_log(avctx, AV_LOG_ERROR, "H.264 10-bit profiles " - "are not supported.\n"); + case AV_PROFILE_H264_HIGH_10_INTRA: + av_log(avctx, AV_LOG_ERROR, "H.264 high 10 intra profile " + "is not supported.\n"); return AVERROR_PATCHWELCOME; - case FF_PROFILE_H264_HIGH_422: - case FF_PROFILE_H264_HIGH_422_INTRA: - case FF_PROFILE_H264_HIGH_444: - case FF_PROFILE_H264_HIGH_444_PREDICTIVE: - case FF_PROFILE_H264_HIGH_444_INTRA: - case FF_PROFILE_H264_CAVLC_444: + case AV_PROFILE_H264_HIGH_422: + case AV_PROFILE_H264_HIGH_422_INTRA: + case AV_PROFILE_H264_HIGH_444: + case AV_PROFILE_H264_HIGH_444_PREDICTIVE: + case AV_PROFILE_H264_HIGH_444_INTRA: + case AV_PROFILE_H264_CAVLC_444: av_log(avctx, AV_LOG_ERROR, "H.264 non-4:2:0 profiles " "are not supported.\n"); return AVERROR_PATCHWELCOME; } - if (avctx->level != FF_LEVEL_UNKNOWN && avctx->level & ~0xff) { + if (avctx->level != AV_LEVEL_UNKNOWN && avctx->level & ~0xff) { av_log(avctx, AV_LOG_ERROR, "Invalid level %d: must fit " "in 8-bit unsigned integer.\n", avctx->level); return AVERROR(EINVAL); @@ -1269,11 +1285,11 @@ static const AVOption vaapi_encode_h264_options[] = { { "quality", "Set encode quality (trades off against speed, higher is faster)", OFFSET(quality), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS }, { "coder", "Entropy coder type", - OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS, "coder" }, - { "cavlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS, "coder" }, - { "cabac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, FLAGS, "coder" }, - { "vlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS, "coder" }, - { "ac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, FLAGS, "coder" }, + OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS, .unit = "coder" }, + { "cavlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS, .unit = "coder" }, + { "cabac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, FLAGS, .unit = "coder" }, + { "vlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS, .unit = "coder" }, + { "ac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, FLAGS, .unit = "coder" }, { "aud", "Include AUD", OFFSET(aud), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, @@ -1281,37 +1297,38 @@ static const AVOption vaapi_encode_h264_options[] = { { "sei", "Set SEI to include", OFFSET(sei), AV_OPT_TYPE_FLAGS, { .i64 = SEI_IDENTIFIER | SEI_TIMING | SEI_RECOVERY_POINT | SEI_A53_CC }, - 0, INT_MAX, FLAGS, "sei" }, + 0, INT_MAX, FLAGS, .unit = "sei" }, { "identifier", "Include encoder version identifier", 0, AV_OPT_TYPE_CONST, { .i64 = SEI_IDENTIFIER }, - INT_MIN, INT_MAX, FLAGS, "sei" }, + INT_MIN, INT_MAX, FLAGS, .unit = "sei" }, { "timing", "Include timing parameters (buffering_period and pic_timing)", 0, AV_OPT_TYPE_CONST, { .i64 = SEI_TIMING }, - INT_MIN, INT_MAX, FLAGS, "sei" }, + INT_MIN, INT_MAX, FLAGS, .unit = "sei" }, { "recovery_point", "Include recovery points where appropriate", 0, AV_OPT_TYPE_CONST, { .i64 = SEI_RECOVERY_POINT }, - INT_MIN, INT_MAX, FLAGS, "sei" }, + INT_MIN, INT_MAX, FLAGS, .unit = "sei" }, { "a53_cc", "Include A/53 caption data", 0, AV_OPT_TYPE_CONST, { .i64 = SEI_A53_CC }, - INT_MIN, INT_MAX, FLAGS, "sei" }, + INT_MIN, INT_MAX, FLAGS, .unit = "sei" }, { "profile", "Set profile (profile_idc and constraint_set*_flag)", OFFSET(profile), AV_OPT_TYPE_INT, - { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xffff, FLAGS, "profile" }, + { .i64 = AV_PROFILE_UNKNOWN }, AV_PROFILE_UNKNOWN, 0xffff, FLAGS, .unit = "profile" }, #define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ - { .i64 = value }, 0, 0, FLAGS, "profile" - { PROFILE("constrained_baseline", FF_PROFILE_H264_CONSTRAINED_BASELINE) }, - { PROFILE("main", FF_PROFILE_H264_MAIN) }, - { PROFILE("high", FF_PROFILE_H264_HIGH) }, + { .i64 = value }, 0, 0, FLAGS, .unit = "profile" + { PROFILE("constrained_baseline", AV_PROFILE_H264_CONSTRAINED_BASELINE) }, + { PROFILE("main", AV_PROFILE_H264_MAIN) }, + { PROFILE("high", AV_PROFILE_H264_HIGH) }, + { PROFILE("high10", AV_PROFILE_H264_HIGH_10) }, #undef PROFILE { "level", "Set level (level_idc)", OFFSET(level), AV_OPT_TYPE_INT, - { .i64 = FF_LEVEL_UNKNOWN }, FF_LEVEL_UNKNOWN, 0xff, FLAGS, "level" }, + { .i64 = AV_LEVEL_UNKNOWN }, AV_LEVEL_UNKNOWN, 0xff, FLAGS, .unit = "level" }, #define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ - { .i64 = value }, 0, 0, FLAGS, "level" + { .i64 = value }, 0, 0, FLAGS, .unit = "level" { LEVEL("1", 10) }, { LEVEL("1.1", 11) }, { LEVEL("1.2", 12) }, diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c index aa7e532f9a4..c4aabbf5ed0 100644 --- a/libavcodec/vaapi_encode_h265.c +++ b/libavcodec/vaapi_encode_h265.c @@ -346,7 +346,7 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx) ptl->general_lower_bit_rate_constraint_flag = 1; - if (avctx->level != FF_LEVEL_UNKNOWN) { + if (avctx->level != AV_LEVEL_UNKNOWN) { ptl->general_level_idc = avctx->level; } else { const H265LevelDescriptor *level; @@ -764,7 +764,7 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx, VAAPIEncodePicture *prev = pic->prev; VAAPIEncodeH265Picture *hprev = prev ? prev->priv_data : NULL; VAEncPictureParameterBufferHEVC *vpic = pic->codec_picture_params; - int i; + int i, j = 0; if (pic->type == PICTURE_TYPE_IDR) { av_assert0(pic->display_order == pic->encode_order); @@ -789,8 +789,8 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx, hpic->pic_type = 1; } else { VAAPIEncodePicture *irap_ref; - av_assert0(pic->refs[0] && pic->refs[1]); - for (irap_ref = pic; irap_ref; irap_ref = irap_ref->refs[1]) { + av_assert0(pic->refs[0][0] && pic->refs[1][0]); + for (irap_ref = pic; irap_ref; irap_ref = irap_ref->refs[1][0]) { if (irap_ref->type == PICTURE_TYPE_I) break; } @@ -915,24 +915,27 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx, .flags = 0, }; - for (i = 0; i < pic->nb_refs; i++) { - VAAPIEncodePicture *ref = pic->refs[i]; - VAAPIEncodeH265Picture *href; - - av_assert0(ref && ref->encode_order < pic->encode_order); - href = ref->priv_data; - - vpic->reference_frames[i] = (VAPictureHEVC) { - .picture_id = ref->recon_surface, - .pic_order_cnt = href->pic_order_cnt, - .flags = (ref->display_order < pic->display_order ? - VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE : 0) | - (ref->display_order > pic->display_order ? - VA_PICTURE_HEVC_RPS_ST_CURR_AFTER : 0), - }; + for (int k = 0; k < MAX_REFERENCE_LIST_NUM; k++) { + for (i = 0; i < pic->nb_refs[k]; i++) { + VAAPIEncodePicture *ref = pic->refs[k][i]; + VAAPIEncodeH265Picture *href; + + av_assert0(ref && ref->encode_order < pic->encode_order); + href = ref->priv_data; + + vpic->reference_frames[j++] = (VAPictureHEVC) { + .picture_id = ref->recon_surface, + .pic_order_cnt = href->pic_order_cnt, + .flags = (ref->display_order < pic->display_order ? + VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE : 0) | + (ref->display_order > pic->display_order ? + VA_PICTURE_HEVC_RPS_ST_CURR_AFTER : 0), + }; + } } - for (; i < FF_ARRAY_ELEMS(vpic->reference_frames); i++) { - vpic->reference_frames[i] = (VAPictureHEVC) { + + for (; j < FF_ARRAY_ELEMS(vpic->reference_frames); j++) { + vpic->reference_frames[j] = (VAPictureHEVC) { .picture_id = VA_INVALID_ID, .flags = VA_PICTURE_HEVC_INVALID, }; @@ -1016,21 +1019,33 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx, memset(rps, 0, sizeof(*rps)); rps_pics = 0; - for (i = 0; i < pic->nb_refs; i++) { - strp = pic->refs[i]->priv_data; - rps_poc[rps_pics] = strp->pic_order_cnt; - rps_used[rps_pics] = 1; - ++rps_pics; + for (i = 0; i < MAX_REFERENCE_LIST_NUM; i++) { + for (j = 0; j < pic->nb_refs[i]; j++) { + strp = pic->refs[i][j]->priv_data; + rps_poc[rps_pics] = strp->pic_order_cnt; + rps_used[rps_pics] = 1; + ++rps_pics; + } } + for (i = 0; i < pic->nb_dpb_pics; i++) { if (pic->dpb[i] == pic) continue; - for (j = 0; j < pic->nb_refs; j++) { - if (pic->dpb[i] == pic->refs[j]) + + for (j = 0; j < pic->nb_refs[0]; j++) { + if (pic->dpb[i] == pic->refs[0][j]) + break; + } + if (j < pic->nb_refs[0]) + continue; + + for (j = 0; j < pic->nb_refs[1]; j++) { + if (pic->dpb[i] == pic->refs[1][j]) break; } - if (j < pic->nb_refs) + if (j < pic->nb_refs[1]) continue; + strp = pic->dpb[i]->priv_data; rps_poc[rps_pics] = strp->pic_order_cnt; rps_used[rps_pics] = 0; @@ -1155,8 +1170,7 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx, vslice->ref_pic_list1[i].flags = VA_PICTURE_HEVC_INVALID; } - av_assert0(pic->nb_refs <= 2); - if (pic->nb_refs >= 1) { + if (pic->nb_refs[0]) { // Backward reference for P- or B-frame. av_assert0(pic->type == PICTURE_TYPE_P || pic->type == PICTURE_TYPE_B); @@ -1165,7 +1179,7 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx, // Reference for GPB B-frame, L0 == L1 vslice->ref_pic_list1[0] = vpic->reference_frames[0]; } - if (pic->nb_refs >= 2) { + if (pic->nb_refs[1]) { // Forward reference for B-frame. av_assert0(pic->type == PICTURE_TYPE_B); vslice->ref_pic_list1[0] = vpic->reference_frames[1]; @@ -1290,22 +1304,22 @@ static av_cold int vaapi_encode_h265_configure(AVCodecContext *avctx) } static const VAAPIEncodeProfile vaapi_encode_h265_profiles[] = { - { FF_PROFILE_HEVC_MAIN, 8, 3, 1, 1, VAProfileHEVCMain }, - { FF_PROFILE_HEVC_REXT, 8, 3, 1, 1, VAProfileHEVCMain }, + { AV_PROFILE_HEVC_MAIN, 8, 3, 1, 1, VAProfileHEVCMain }, + { AV_PROFILE_HEVC_REXT, 8, 3, 1, 1, VAProfileHEVCMain }, #if VA_CHECK_VERSION(0, 37, 0) - { FF_PROFILE_HEVC_MAIN_10, 10, 3, 1, 1, VAProfileHEVCMain10 }, - { FF_PROFILE_HEVC_REXT, 10, 3, 1, 1, VAProfileHEVCMain10 }, + { AV_PROFILE_HEVC_MAIN_10, 10, 3, 1, 1, VAProfileHEVCMain10 }, + { AV_PROFILE_HEVC_REXT, 10, 3, 1, 1, VAProfileHEVCMain10 }, #endif #if VA_CHECK_VERSION(1, 2, 0) - { FF_PROFILE_HEVC_REXT, 12, 3, 1, 1, VAProfileHEVCMain12 }, - { FF_PROFILE_HEVC_REXT, 8, 3, 1, 0, VAProfileHEVCMain422_10 }, - { FF_PROFILE_HEVC_REXT, 10, 3, 1, 0, VAProfileHEVCMain422_10 }, - { FF_PROFILE_HEVC_REXT, 12, 3, 1, 0, VAProfileHEVCMain422_12 }, - { FF_PROFILE_HEVC_REXT, 8, 3, 0, 0, VAProfileHEVCMain444 }, - { FF_PROFILE_HEVC_REXT, 10, 3, 0, 0, VAProfileHEVCMain444_10 }, - { FF_PROFILE_HEVC_REXT, 12, 3, 0, 0, VAProfileHEVCMain444_12 }, + { AV_PROFILE_HEVC_REXT, 12, 3, 1, 1, VAProfileHEVCMain12 }, + { AV_PROFILE_HEVC_REXT, 8, 3, 1, 0, VAProfileHEVCMain422_10 }, + { AV_PROFILE_HEVC_REXT, 10, 3, 1, 0, VAProfileHEVCMain422_10 }, + { AV_PROFILE_HEVC_REXT, 12, 3, 1, 0, VAProfileHEVCMain422_12 }, + { AV_PROFILE_HEVC_REXT, 8, 3, 0, 0, VAProfileHEVCMain444 }, + { AV_PROFILE_HEVC_REXT, 10, 3, 0, 0, VAProfileHEVCMain444_10 }, + { AV_PROFILE_HEVC_REXT, 12, 3, 0, 0, VAProfileHEVCMain444_12 }, #endif - { FF_PROFILE_UNKNOWN } + { AV_PROFILE_UNKNOWN } }; static const VAAPIEncodeType vaapi_encode_type_h265 = { @@ -1348,12 +1362,12 @@ static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx) ctx->codec = &vaapi_encode_type_h265; - if (avctx->profile == FF_PROFILE_UNKNOWN) + if (avctx->profile == AV_PROFILE_UNKNOWN) avctx->profile = priv->profile; - if (avctx->level == FF_LEVEL_UNKNOWN) + if (avctx->level == AV_LEVEL_UNKNOWN) avctx->level = priv->level; - if (avctx->level != FF_LEVEL_UNKNOWN && avctx->level & ~0xff) { + if (avctx->level != AV_LEVEL_UNKNOWN && avctx->level & ~0xff) { av_log(avctx, AV_LOG_ERROR, "Invalid level %d: must fit " "in 8-bit unsigned integer.\n", avctx->level); return AVERROR(EINVAL); @@ -1395,29 +1409,29 @@ static const AVOption vaapi_encode_h265_options[] = { { "profile", "Set profile (general_profile_idc)", OFFSET(profile), AV_OPT_TYPE_INT, - { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xff, FLAGS, "profile" }, + { .i64 = AV_PROFILE_UNKNOWN }, AV_PROFILE_UNKNOWN, 0xff, FLAGS, .unit = "profile" }, #define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ - { .i64 = value }, 0, 0, FLAGS, "profile" - { PROFILE("main", FF_PROFILE_HEVC_MAIN) }, - { PROFILE("main10", FF_PROFILE_HEVC_MAIN_10) }, - { PROFILE("rext", FF_PROFILE_HEVC_REXT) }, + { .i64 = value }, 0, 0, FLAGS, .unit = "profile" + { PROFILE("main", AV_PROFILE_HEVC_MAIN) }, + { PROFILE("main10", AV_PROFILE_HEVC_MAIN_10) }, + { PROFILE("rext", AV_PROFILE_HEVC_REXT) }, #undef PROFILE { "tier", "Set tier (general_tier_flag)", OFFSET(tier), AV_OPT_TYPE_INT, - { .i64 = 0 }, 0, 1, FLAGS, "tier" }, + { .i64 = 0 }, 0, 1, FLAGS, .unit = "tier" }, { "main", NULL, 0, AV_OPT_TYPE_CONST, - { .i64 = 0 }, 0, 0, FLAGS, "tier" }, + { .i64 = 0 }, 0, 0, FLAGS, .unit = "tier" }, { "high", NULL, 0, AV_OPT_TYPE_CONST, - { .i64 = 1 }, 0, 0, FLAGS, "tier" }, + { .i64 = 1 }, 0, 0, FLAGS, .unit = "tier" }, { "level", "Set level (general_level_idc)", OFFSET(level), AV_OPT_TYPE_INT, - { .i64 = FF_LEVEL_UNKNOWN }, FF_LEVEL_UNKNOWN, 0xff, FLAGS, "level" }, + { .i64 = AV_LEVEL_UNKNOWN }, AV_LEVEL_UNKNOWN, 0xff, FLAGS, .unit = "level" }, #define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ - { .i64 = value }, 0, 0, FLAGS, "level" + { .i64 = value }, 0, 0, FLAGS, .unit = "level" { LEVEL("1", 30) }, { LEVEL("2", 60) }, { LEVEL("2.1", 63) }, @@ -1436,18 +1450,18 @@ static const AVOption vaapi_encode_h265_options[] = { { "sei", "Set SEI to include", OFFSET(sei), AV_OPT_TYPE_FLAGS, { .i64 = SEI_MASTERING_DISPLAY | SEI_CONTENT_LIGHT_LEVEL | SEI_A53_CC }, - 0, INT_MAX, FLAGS, "sei" }, + 0, INT_MAX, FLAGS, .unit = "sei" }, { "hdr", "Include HDR metadata for mastering display colour volume " "and content light level information", 0, AV_OPT_TYPE_CONST, { .i64 = SEI_MASTERING_DISPLAY | SEI_CONTENT_LIGHT_LEVEL }, - INT_MIN, INT_MAX, FLAGS, "sei" }, + INT_MIN, INT_MAX, FLAGS, .unit = "sei" }, { "a53_cc", "Include A/53 caption data", 0, AV_OPT_TYPE_CONST, { .i64 = SEI_A53_CC }, - INT_MIN, INT_MAX, FLAGS, "sei" }, + INT_MIN, INT_MAX, FLAGS, .unit = "sei" }, { "tiles", "Tile columns x rows", OFFSET(common.tile_cols), AV_OPT_TYPE_IMAGE_SIZE, diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c index cb7588b94ba..c17747e3a94 100644 --- a/libavcodec/vaapi_encode_mjpeg.c +++ b/libavcodec/vaapi_encode_mjpeg.c @@ -480,15 +480,15 @@ static av_cold int vaapi_encode_mjpeg_configure(AVCodecContext *avctx) } static const VAAPIEncodeProfile vaapi_encode_mjpeg_profiles[] = { - { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT, + { AV_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT, 8, 1, 0, 0, VAProfileJPEGBaseline }, - { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT, + { AV_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT, 8, 3, 1, 1, VAProfileJPEGBaseline }, - { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT, + { AV_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT, 8, 3, 1, 0, VAProfileJPEGBaseline }, - { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT, + { AV_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT, 8, 3, 0, 0, VAProfileJPEGBaseline }, - { FF_PROFILE_UNKNOWN } + { AV_PROFILE_UNKNOWN } }; static const VAAPIEncodeType vaapi_encode_type_mjpeg = { diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c index 9261d19a831..c9b16fbcfc0 100644 --- a/libavcodec/vaapi_encode_mpeg2.c +++ b/libavcodec/vaapi_encode_mpeg2.c @@ -458,12 +458,12 @@ static int vaapi_encode_mpeg2_init_picture_params(AVCodecContext *avctx, break; case PICTURE_TYPE_P: vpic->picture_type = VAEncPictureTypePredictive; - vpic->forward_reference_picture = pic->refs[0]->recon_surface; + vpic->forward_reference_picture = pic->refs[0][0]->recon_surface; break; case PICTURE_TYPE_B: vpic->picture_type = VAEncPictureTypeBidirectional; - vpic->forward_reference_picture = pic->refs[0]->recon_surface; - vpic->backward_reference_picture = pic->refs[1]->recon_surface; + vpic->forward_reference_picture = pic->refs[0][0]->recon_surface; + vpic->backward_reference_picture = pic->refs[1][0]->recon_surface; break; default: av_assert0(0 && "invalid picture type"); @@ -558,9 +558,9 @@ static av_cold int vaapi_encode_mpeg2_configure(AVCodecContext *avctx) } static const VAAPIEncodeProfile vaapi_encode_mpeg2_profiles[] = { - { FF_PROFILE_MPEG2_MAIN, 8, 3, 1, 1, VAProfileMPEG2Main }, - { FF_PROFILE_MPEG2_SIMPLE, 8, 3, 1, 1, VAProfileMPEG2Simple }, - { FF_PROFILE_UNKNOWN } + { AV_PROFILE_MPEG2_MAIN, 8, 3, 1, 1, VAProfileMPEG2Main }, + { AV_PROFILE_MPEG2_SIMPLE, 8, 3, 1, 1, VAProfileMPEG2Simple }, + { AV_PROFILE_UNKNOWN } }; static const VAAPIEncodeType vaapi_encode_type_mpeg2 = { @@ -595,9 +595,9 @@ static av_cold int vaapi_encode_mpeg2_init(AVCodecContext *avctx) ctx->codec = &vaapi_encode_type_mpeg2; - if (avctx->profile == FF_PROFILE_UNKNOWN) + if (avctx->profile == AV_PROFILE_UNKNOWN) avctx->profile = priv->profile; - if (avctx->level == FF_LEVEL_UNKNOWN) + if (avctx->level == AV_LEVEL_UNKNOWN) avctx->level = priv->level; // Reject unknown levels (these are required to set f_code for @@ -644,20 +644,20 @@ static const AVOption vaapi_encode_mpeg2_options[] = { { "profile", "Set profile (in profile_and_level_indication)", OFFSET(profile), AV_OPT_TYPE_INT, - { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 7, FLAGS, "profile" }, + { .i64 = AV_PROFILE_UNKNOWN }, AV_PROFILE_UNKNOWN, 7, FLAGS, .unit = "profile" }, #define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ - { .i64 = value }, 0, 0, FLAGS, "profile" - { PROFILE("simple", FF_PROFILE_MPEG2_SIMPLE) }, - { PROFILE("main", FF_PROFILE_MPEG2_MAIN) }, + { .i64 = value }, 0, 0, FLAGS, .unit = "profile" + { PROFILE("simple", AV_PROFILE_MPEG2_SIMPLE) }, + { PROFILE("main", AV_PROFILE_MPEG2_MAIN) }, #undef PROFILE { "level", "Set level (in profile_and_level_indication)", OFFSET(level), AV_OPT_TYPE_INT, - { .i64 = 4 }, 0, 15, FLAGS, "level" }, + { .i64 = 4 }, 0, 15, FLAGS, .unit = "level" }, #define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \ - { .i64 = value }, 0, 0, FLAGS, "level" + { .i64 = value }, 0, 0, FLAGS, .unit = "level" { LEVEL("low", 10) }, { LEVEL("main", 8) }, { LEVEL("high_1440", 6) }, diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c index ae6a8d313ca..8a557b967e6 100644 --- a/libavcodec/vaapi_encode_vp8.c +++ b/libavcodec/vaapi_encode_vp8.c @@ -86,7 +86,7 @@ static int vaapi_encode_vp8_init_picture_params(AVCodecContext *avctx, switch (pic->type) { case PICTURE_TYPE_IDR: case PICTURE_TYPE_I: - av_assert0(pic->nb_refs == 0); + av_assert0(pic->nb_refs[0] == 0 && pic->nb_refs[1] == 0); vpic->ref_flags.bits.force_kf = 1; vpic->ref_last_frame = vpic->ref_gf_frame = @@ -94,14 +94,14 @@ static int vaapi_encode_vp8_init_picture_params(AVCodecContext *avctx, VA_INVALID_SURFACE; break; case PICTURE_TYPE_P: - av_assert0(pic->nb_refs == 1); + av_assert0(!pic->nb_refs[1]); vpic->ref_flags.bits.no_ref_last = 0; vpic->ref_flags.bits.no_ref_gf = 1; vpic->ref_flags.bits.no_ref_arf = 1; vpic->ref_last_frame = vpic->ref_gf_frame = vpic->ref_arf_frame = - pic->refs[0]->recon_surface; + pic->refs[0][0]->recon_surface; break; default: av_assert0(0 && "invalid picture type"); @@ -180,7 +180,7 @@ static av_cold int vaapi_encode_vp8_configure(AVCodecContext *avctx) static const VAAPIEncodeProfile vaapi_encode_vp8_profiles[] = { { 0 /* VP8 has no profiles */, 8, 3, 1, 1, VAProfileVP8Version0_3 }, - { FF_PROFILE_UNKNOWN } + { AV_PROFILE_UNKNOWN } }; static const VAAPIEncodeType vaapi_encode_type_vp8 = { diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c index af1353cea80..c2a8dec71b9 100644 --- a/libavcodec/vaapi_encode_vp9.c +++ b/libavcodec/vaapi_encode_vp9.c @@ -96,15 +96,15 @@ static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx, switch (pic->type) { case PICTURE_TYPE_IDR: - av_assert0(pic->nb_refs == 0); + av_assert0(pic->nb_refs[0] == 0 && pic->nb_refs[1] == 0); vpic->ref_flags.bits.force_kf = 1; vpic->refresh_frame_flags = 0xff; hpic->slot = 0; break; case PICTURE_TYPE_P: - av_assert0(pic->nb_refs == 1); + av_assert0(!pic->nb_refs[1]); { - VAAPIEncodeVP9Picture *href = pic->refs[0]->priv_data; + VAAPIEncodeVP9Picture *href = pic->refs[0][0]->priv_data; av_assert0(href->slot == 0 || href->slot == 1); if (ctx->max_b_depth > 0) { @@ -120,10 +120,10 @@ static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx, } break; case PICTURE_TYPE_B: - av_assert0(pic->nb_refs == 2); + av_assert0(pic->nb_refs[0] && pic->nb_refs[1]); { - VAAPIEncodeVP9Picture *href0 = pic->refs[0]->priv_data, - *href1 = pic->refs[1]->priv_data; + VAAPIEncodeVP9Picture *href0 = pic->refs[0][0]->priv_data, + *href1 = pic->refs[1][0]->priv_data; av_assert0(href0->slot < pic->b_depth + 1 && href1->slot < pic->b_depth + 1); @@ -157,12 +157,14 @@ static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx, for (i = 0; i < FF_ARRAY_ELEMS(vpic->reference_frames); i++) vpic->reference_frames[i] = VA_INVALID_SURFACE; - for (i = 0; i < pic->nb_refs; i++) { - VAAPIEncodePicture *ref_pic = pic->refs[i]; - int slot; - slot = ((VAAPIEncodeVP9Picture*)ref_pic->priv_data)->slot; - av_assert0(vpic->reference_frames[slot] == VA_INVALID_SURFACE); - vpic->reference_frames[slot] = ref_pic->recon_surface; + for (i = 0; i < MAX_REFERENCE_LIST_NUM; i++) { + for (int j = 0; j < pic->nb_refs[i]; j++) { + VAAPIEncodePicture *ref_pic = pic->refs[i][j]; + int slot; + slot = ((VAAPIEncodeVP9Picture*)ref_pic->priv_data)->slot; + av_assert0(vpic->reference_frames[slot] == VA_INVALID_SURFACE); + vpic->reference_frames[slot] = ref_pic->recon_surface; + } } vpic->pic_flags.bits.frame_type = (pic->type != PICTURE_TYPE_IDR); @@ -227,11 +229,11 @@ static av_cold int vaapi_encode_vp9_configure(AVCodecContext *avctx) } static const VAAPIEncodeProfile vaapi_encode_vp9_profiles[] = { - { FF_PROFILE_VP9_0, 8, 3, 1, 1, VAProfileVP9Profile0 }, - { FF_PROFILE_VP9_1, 8, 3, 0, 0, VAProfileVP9Profile1 }, - { FF_PROFILE_VP9_2, 10, 3, 1, 1, VAProfileVP9Profile2 }, - { FF_PROFILE_VP9_3, 10, 3, 0, 0, VAProfileVP9Profile3 }, - { FF_PROFILE_UNKNOWN } + { AV_PROFILE_VP9_0, 8, 3, 1, 1, VAProfileVP9Profile0 }, + { AV_PROFILE_VP9_1, 8, 3, 0, 0, VAProfileVP9Profile1 }, + { AV_PROFILE_VP9_2, 10, 3, 1, 1, VAProfileVP9Profile2 }, + { AV_PROFILE_VP9_3, 10, 3, 0, 0, VAProfileVP9Profile3 }, + { AV_PROFILE_UNKNOWN } }; static const VAAPIEncodeType vaapi_encode_type_vp9 = { diff --git a/libavcodec/vaapi_h264.c b/libavcodec/vaapi_h264.c index 9332aa6f310..55cf5a05ee3 100644 --- a/libavcodec/vaapi_h264.c +++ b/libavcodec/vaapi_h264.c @@ -22,7 +22,7 @@ #include "h264dec.h" #include "h264_ps.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vaapi_decode.h" /** @@ -385,11 +385,11 @@ static int vaapi_h264_decode_slice(AVCodecContext *avctx, return 0; } -const AVHWAccel ff_h264_vaapi_hwaccel = { - .name = "h264_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_h264_vaapi_hwaccel = { + .p.name = "h264_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_h264_start_frame, .end_frame = &vaapi_h264_end_frame, .decode_slice = &vaapi_h264_decode_slice, diff --git a/libavcodec/vaapi_hevc.c b/libavcodec/vaapi_hevc.c index 20fb36adfa8..3bdd2dd1b81 100644 --- a/libavcodec/vaapi_hevc.c +++ b/libavcodec/vaapi_hevc.c @@ -25,7 +25,7 @@ #include "avcodec.h" #include "hevcdec.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vaapi_decode.h" #include "vaapi_hevc.h" #include "h265_profile_level.h" @@ -60,10 +60,10 @@ static void fill_vaapi_pic(VAPictureHEVC *va_pic, const HEVCFrame *pic, int rps_ if (pic->flags & HEVC_FRAME_FLAG_LONG_REF) va_pic->flags |= VA_PICTURE_HEVC_LONG_TERM_REFERENCE; - if (pic->frame->interlaced_frame) { + if (pic->frame->flags & AV_FRAME_FLAG_INTERLACED) { va_pic->flags |= VA_PICTURE_HEVC_FIELD_PIC; - if (!pic->frame->top_field_first) + if (!(pic->frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)) va_pic->flags |= VA_PICTURE_HEVC_BOTTOM_FIELD; } } @@ -71,6 +71,7 @@ static void fill_vaapi_pic(VAPictureHEVC *va_pic, const HEVCFrame *pic, int rps_ static int find_frame_rps_type(const HEVCContext *h, const HEVCFrame *pic) { VASurfaceID pic_surf = ff_vaapi_get_surface_id(pic->frame); + const HEVCFrame *current_picture = h->ref; int i; for (i = 0; i < h->rps[ST_CURR_BEF].nb_refs; i++) { @@ -88,6 +89,9 @@ static int find_frame_rps_type(const HEVCContext *h, const HEVCFrame *pic) return VA_PICTURE_HEVC_RPS_LT_CURR; } + if (h->ps.pps->pps_curr_pic_ref_enabled_flag && current_picture->poc == pic->poc) + return VA_PICTURE_HEVC_LONG_TERM_REFERENCE; + return 0; } @@ -100,7 +104,8 @@ static void fill_vaapi_reference_frames(const HEVCContext *h, VAPictureParameter const HEVCFrame *frame = NULL; while (!frame && j < FF_ARRAY_ELEMS(h->DPB)) { - if (&h->DPB[j] != current_picture && (h->DPB[j].flags & (HEVC_FRAME_FLAG_LONG_REF | HEVC_FRAME_FLAG_SHORT_REF))) + if ((&h->DPB[j] != current_picture || h->ps.pps->pps_curr_pic_ref_enabled_flag) && + (h->DPB[j].flags & (HEVC_FRAME_FLAG_LONG_REF | HEVC_FRAME_FLAG_SHORT_REF))) frame = &h->DPB[j]; j++; } @@ -126,6 +131,10 @@ static int vaapi_hevc_start_frame(AVCodecContext *avctx, const ScalingList *scaling_list = NULL; int pic_param_size, err, i; +#if VA_CHECK_VERSION(1, 2, 0) + int num_comps, pre_palette_size; +#endif + VAPictureParameterBufferHEVC *pic_param = (VAPictureParameterBufferHEVC *)&pic->pic_param; pic->pic.output_surface = ff_vaapi_get_surface_id(h->ref->frame); @@ -218,7 +227,8 @@ static int vaapi_hevc_start_frame(AVCodecContext *avctx, } #if VA_CHECK_VERSION(1, 2, 0) - if (avctx->profile == FF_PROFILE_HEVC_REXT) { + if (avctx->profile == AV_PROFILE_HEVC_REXT || + avctx->profile == AV_PROFILE_HEVC_SCC) { pic->pic_param.rext = (VAPictureParameterBufferHEVCRext) { .range_extension_pic_fields.bits = { .transform_skip_rotation_enabled_flag = sps->transform_skip_rotation_enabled_flag, @@ -245,8 +255,46 @@ static int vaapi_hevc_start_frame(AVCodecContext *avctx, for (i = 0; i < 6; i++) pic->pic_param.rext.cr_qp_offset_list[i] = pps->cr_qp_offset_list[i]; } + + pre_palette_size = pps->pps_palette_predictor_initializers_present_flag ? + pps->pps_num_palette_predictor_initializers : + (sps->sps_palette_predictor_initializers_present_flag ? + sps->sps_num_palette_predictor_initializers : + 0); + + if (avctx->profile == AV_PROFILE_HEVC_SCC) { + pic->pic_param.scc = (VAPictureParameterBufferHEVCScc) { + .screen_content_pic_fields.bits = { + .pps_curr_pic_ref_enabled_flag = pps->pps_curr_pic_ref_enabled_flag, + .palette_mode_enabled_flag = sps->palette_mode_enabled_flag, + .motion_vector_resolution_control_idc = sps->motion_vector_resolution_control_idc, + .intra_boundary_filtering_disabled_flag = sps->intra_boundary_filtering_disabled_flag, + .residual_adaptive_colour_transform_enabled_flag + = pps->residual_adaptive_colour_transform_enabled_flag, + .pps_slice_act_qp_offsets_present_flag = pps->pps_slice_act_qp_offsets_present_flag, + }, + .palette_max_size = sps->palette_max_size, + .delta_palette_max_predictor_size = sps->delta_palette_max_predictor_size, + .predictor_palette_size = pre_palette_size, + .pps_act_y_qp_offset_plus5 = pps->residual_adaptive_colour_transform_enabled_flag ? + pps->pps_act_y_qp_offset + 5 : 0, + .pps_act_cb_qp_offset_plus5 = pps->residual_adaptive_colour_transform_enabled_flag ? + pps->pps_act_cb_qp_offset + 5 : 0, + .pps_act_cr_qp_offset_plus3 = pps->residual_adaptive_colour_transform_enabled_flag ? + pps->pps_act_cr_qp_offset + 3 : 0, + }; + + num_comps = pps->monochrome_palette_flag ? 1 : 3; + for (int comp = 0; comp < num_comps; comp++) + for (int j = 0; j < pre_palette_size; j++) + pic->pic_param.scc.predictor_palette_entries[comp][j] = + pps->pps_palette_predictor_initializers_present_flag ? + pps->pps_palette_predictor_initializer[comp][j]: + sps->sps_palette_predictor_initializer[comp][j]; + } + #endif - pic_param_size = avctx->profile == FF_PROFILE_HEVC_REXT ? + pic_param_size = avctx->profile >= AV_PROFILE_HEVC_REXT ? sizeof(pic->pic_param) : sizeof(VAPictureParameterBufferHEVC); err = ff_vaapi_decode_make_param_buffer(avctx, &pic->pic, @@ -299,7 +347,7 @@ static int vaapi_hevc_end_frame(AVCodecContext *avctx) VASliceParameterBufferHEVC *last_slice_param = (VASliceParameterBufferHEVC *)&pic->last_slice_param; int ret; - int slice_param_size = avctx->profile == FF_PROFILE_HEVC_REXT ? + int slice_param_size = avctx->profile >= AV_PROFILE_HEVC_REXT ? sizeof(pic->last_slice_param) : sizeof(VASliceParameterBufferHEVC); if (pic->last_size) { @@ -329,10 +377,10 @@ static void fill_pred_weight_table(AVCodecContext *avctx, { int i; #if VA_CHECK_VERSION(1, 2, 0) - int is_rext = avctx->profile >= FF_PROFILE_HEVC_REXT; + int is_rext = avctx->profile >= AV_PROFILE_HEVC_REXT; #else int is_rext = 0; - if (avctx->profile >= FF_PROFILE_HEVC_REXT) + if (avctx->profile >= AV_PROFILE_HEVC_REXT) av_log(avctx, AV_LOG_WARNING, "Please consider to update to VAAPI 1.2.0 " "or above, which can support REXT related setting correctly.\n"); #endif @@ -413,7 +461,7 @@ static int vaapi_hevc_decode_slice(AVCodecContext *avctx, VAAPIDecodePictureHEVC *pic = h->ref->hwaccel_picture_private; VASliceParameterBufferHEVC *last_slice_param = (VASliceParameterBufferHEVC *)&pic->last_slice_param; - int slice_param_size = avctx->profile == FF_PROFILE_HEVC_REXT ? + int slice_param_size = avctx->profile >= AV_PROFILE_HEVC_REXT ? sizeof(pic->last_slice_param) : sizeof(VASliceParameterBufferHEVC); int nb_list = (sh->slice_type == HEVC_SLICE_B) ? @@ -478,11 +526,15 @@ static int vaapi_hevc_decode_slice(AVCodecContext *avctx, fill_pred_weight_table(avctx, h, sh, last_slice_param); #if VA_CHECK_VERSION(1, 2, 0) - if (avctx->profile == FF_PROFILE_HEVC_REXT) { + if (avctx->profile >= AV_PROFILE_HEVC_REXT) { pic->last_slice_param.rext = (VASliceParameterBufferHEVCRext) { .slice_ext_flags.bits = { .cu_chroma_qp_offset_enabled_flag = sh->cu_chroma_qp_offset_enabled_flag, + .use_integer_mv_flag = sh->use_integer_mv_flag, }, + .slice_act_y_qp_offset = sh->slice_act_y_qp_offset, + .slice_act_cb_qp_offset = sh->slice_act_cb_qp_offset, + .slice_act_cr_qp_offset = sh->slice_act_cr_qp_offset, }; for (i = 0; i < 15 && i < sh->nb_refs[L0]; i++) { pic->last_slice_param.rext.luma_offset_l0[i] = sh->luma_offset_l0[i]; @@ -490,12 +542,6 @@ static int vaapi_hevc_decode_slice(AVCodecContext *avctx, pic->last_slice_param.rext.ChromaOffsetL0[i][1] = sh->chroma_offset_l0[i][1]; } - for (i = 0; i < 15 && i < sh->nb_refs[L0]; i++) { - pic->last_slice_param.rext.luma_offset_l0[i] = sh->luma_offset_l0[i]; - pic->last_slice_param.rext.ChromaOffsetL0[i][0] = sh->chroma_offset_l0[i][0]; - pic->last_slice_param.rext.ChromaOffsetL0[i][1] = sh->chroma_offset_l0[i][1]; - } - if (sh->slice_type == HEVC_SLICE_B) { for (i = 0; i < 15 && i < sh->nb_refs[L1]; i++) { pic->last_slice_param.rext.luma_offset_l1[i] = sh->luma_offset_l1[i]; @@ -544,9 +590,9 @@ static int ptl_convert(const PTLCommon *general_ptl, H265RawProfileTierLevel *h2 } /* - * Find exact va_profile for HEVC Range Extension + * Find exact va_profile for HEVC Range Extension and Screen Content Coding Extension */ -VAProfile ff_vaapi_parse_hevc_rext_profile(AVCodecContext *avctx) +VAProfile ff_vaapi_parse_hevc_rext_scc_profile(AVCodecContext *avctx) { const HEVCContext *h = avctx->priv_data; const HEVCSPS *sps = h->ps.sps; @@ -585,6 +631,16 @@ VAProfile ff_vaapi_parse_hevc_rext_profile(AVCodecContext *avctx) else if (!strcmp(profile->name, "Main 4:4:4 12") || !strcmp(profile->name, "Main 4:4:4 12 Intra")) return VAProfileHEVCMain444_12; + else if (!strcmp(profile->name, "Screen-Extended Main")) + return VAProfileHEVCSccMain; + else if (!strcmp(profile->name, "Screen-Extended Main 10")) + return VAProfileHEVCSccMain10; + else if (!strcmp(profile->name, "Screen-Extended Main 4:4:4")) + return VAProfileHEVCSccMain444; +#if VA_CHECK_VERSION(1, 8, 0) + else if (!strcmp(profile->name, "Screen-Extended Main 4:4:4 10")) + return VAProfileHEVCSccMain444_10; +#endif #else av_log(avctx, AV_LOG_WARNING, "HEVC profile %s is " "not supported with this VA version.\n", profile->name); @@ -598,11 +654,11 @@ VAProfile ff_vaapi_parse_hevc_rext_profile(AVCodecContext *avctx) return VAProfileNone; } -const AVHWAccel ff_hevc_vaapi_hwaccel = { - .name = "hevc_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_hevc_vaapi_hwaccel = { + .p.name = "hevc_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = vaapi_hevc_start_frame, .end_frame = vaapi_hevc_end_frame, .decode_slice = vaapi_hevc_decode_slice, diff --git a/libavcodec/vaapi_hevc.h b/libavcodec/vaapi_hevc.h index b3b0e6fc1e9..449635d0d70 100644 --- a/libavcodec/vaapi_hevc.h +++ b/libavcodec/vaapi_hevc.h @@ -22,6 +22,6 @@ #include #include "avcodec.h" -VAProfile ff_vaapi_parse_hevc_rext_profile(AVCodecContext *avctx); +VAProfile ff_vaapi_parse_hevc_rext_scc_profile(AVCodecContext *avctx); #endif /* AVCODEC_VAAPI_HEVC_H */ diff --git a/libavcodec/vaapi_mjpeg.c b/libavcodec/vaapi_mjpeg.c index 81582114b64..5b8d47bb2a5 100644 --- a/libavcodec/vaapi_mjpeg.c +++ b/libavcodec/vaapi_mjpeg.c @@ -19,7 +19,7 @@ #include #include -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vaapi_decode.h" #include "mjpegdec.h" @@ -142,11 +142,11 @@ static int vaapi_mjpeg_decode_slice(AVCodecContext *avctx, return err; } -const AVHWAccel ff_mjpeg_vaapi_hwaccel = { - .name = "mjpeg_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MJPEG, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_mjpeg_vaapi_hwaccel = { + .p.name = "mjpeg_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MJPEG, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_mjpeg_start_frame, .end_frame = &vaapi_mjpeg_end_frame, .decode_slice = &vaapi_mjpeg_decode_slice, diff --git a/libavcodec/vaapi_mpeg2.c b/libavcodec/vaapi_mpeg2.c index 5e2b8891375..eeb4e873210 100644 --- a/libavcodec/vaapi_mpeg2.c +++ b/libavcodec/vaapi_mpeg2.c @@ -20,7 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "mpegutils.h" #include "mpegvideo.h" #include "mpegvideodec.h" @@ -172,11 +172,11 @@ static int vaapi_mpeg2_decode_slice(AVCodecContext *avctx, const uint8_t *buffer return 0; } -const AVHWAccel ff_mpeg2_vaapi_hwaccel = { - .name = "mpeg2_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_mpeg2_vaapi_hwaccel = { + .p.name = "mpeg2_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_mpeg2_start_frame, .end_frame = &vaapi_mpeg2_end_frame, .decode_slice = &vaapi_mpeg2_decode_slice, diff --git a/libavcodec/vaapi_mpeg4.c b/libavcodec/vaapi_mpeg4.c index 4e74e0382b6..363b686e42e 100644 --- a/libavcodec/vaapi_mpeg4.c +++ b/libavcodec/vaapi_mpeg4.c @@ -23,7 +23,7 @@ #include "config_components.h" #include "h263.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "mpeg4videodec.h" #include "mpegvideo.h" #include "mpegvideodec.h" @@ -180,11 +180,11 @@ static int vaapi_mpeg4_decode_slice(AVCodecContext *avctx, const uint8_t *buffer } #if CONFIG_MPEG4_VAAPI_HWACCEL -const AVHWAccel ff_mpeg4_vaapi_hwaccel = { - .name = "mpeg4_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG4, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_mpeg4_vaapi_hwaccel = { + .p.name = "mpeg4_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG4, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_mpeg4_start_frame, .end_frame = &vaapi_mpeg4_end_frame, .decode_slice = &vaapi_mpeg4_decode_slice, @@ -198,11 +198,11 @@ const AVHWAccel ff_mpeg4_vaapi_hwaccel = { #endif #if CONFIG_H263_VAAPI_HWACCEL -const AVHWAccel ff_h263_vaapi_hwaccel = { - .name = "h263_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H263, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_h263_vaapi_hwaccel = { + .p.name = "h263_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H263, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_mpeg4_start_frame, .end_frame = &vaapi_mpeg4_end_frame, .decode_slice = &vaapi_mpeg4_decode_slice, diff --git a/libavcodec/vaapi_vc1.c b/libavcodec/vaapi_vc1.c index fb2132e814b..09a5c852fc2 100644 --- a/libavcodec/vaapi_vc1.c +++ b/libavcodec/vaapi_vc1.c @@ -22,11 +22,10 @@ #include "config_components.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "mpegvideodec.h" #include "vaapi_decode.h" #include "vc1.h" -#include "vc1data.h" /** Translate FFmpeg MV modes to VA API */ static int get_VAMvModeVC1(enum MVModes mv_mode) @@ -501,11 +500,11 @@ static int vaapi_vc1_decode_slice(AVCodecContext *avctx, const uint8_t *buffer, } #if CONFIG_WMV3_VAAPI_HWACCEL -const AVHWAccel ff_wmv3_vaapi_hwaccel = { - .name = "wmv3_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_WMV3, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_wmv3_vaapi_hwaccel = { + .p.name = "wmv3_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_WMV3, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_vc1_start_frame, .end_frame = &vaapi_vc1_end_frame, .decode_slice = &vaapi_vc1_decode_slice, @@ -518,11 +517,11 @@ const AVHWAccel ff_wmv3_vaapi_hwaccel = { }; #endif -const AVHWAccel ff_vc1_vaapi_hwaccel = { - .name = "vc1_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VC1, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_vc1_vaapi_hwaccel = { + .p.name = "vc1_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VC1, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_vc1_start_frame, .end_frame = &vaapi_vc1_end_frame, .decode_slice = &vaapi_vc1_decode_slice, diff --git a/libavcodec/vaapi_vp8.c b/libavcodec/vaapi_vp8.c index 5b18bf8f347..31137a45bd1 100644 --- a/libavcodec/vaapi_vp8.c +++ b/libavcodec/vaapi_vp8.c @@ -19,7 +19,7 @@ #include #include -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vaapi_decode.h" #include "vp8.h" @@ -220,11 +220,11 @@ static int vaapi_vp8_decode_slice(AVCodecContext *avctx, return err; } -const AVHWAccel ff_vp8_vaapi_hwaccel = { - .name = "vp8_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP8, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_vp8_vaapi_hwaccel = { + .p.name = "vp8_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP8, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = &vaapi_vp8_start_frame, .end_frame = &vaapi_vp8_end_frame, .decode_slice = &vaapi_vp8_decode_slice, diff --git a/libavcodec/vaapi_vp9.c b/libavcodec/vaapi_vp9.c index 776382f6837..9dc7d5e72b9 100644 --- a/libavcodec/vaapi_vp9.c +++ b/libavcodec/vaapi_vp9.c @@ -22,7 +22,7 @@ #include "libavutil/pixdesc.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vaapi_decode.h" #include "vp9shared.h" @@ -168,11 +168,11 @@ static int vaapi_vp9_decode_slice(AVCodecContext *avctx, return 0; } -const AVHWAccel ff_vp9_vaapi_hwaccel = { - .name = "vp9_vaapi", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_VAAPI, +const FFHWAccel ff_vp9_vaapi_hwaccel = { + .p.name = "vp9_vaapi", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_VAAPI, .start_frame = vaapi_vp9_start_frame, .end_frame = vaapi_vp9_end_frame, .decode_slice = vaapi_vp9_decode_slice, diff --git a/libavcodec/vb.c b/libavcodec/vb.c index 8b0e2164734..5744faa983f 100644 --- a/libavcodec/vb.c +++ b/libavcodec/vb.c @@ -230,7 +230,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } memcpy(frame->data[1], c->pal, AVPALETTE_SIZE); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = flags & VB_HAS_PALETTE; +FF_ENABLE_DEPRECATION_WARNINGS +#endif outptr = frame->data[0]; srcptr = c->frame; diff --git a/libavcodec/vble.c b/libavcodec/vble.c index 9307b0d165c..d27ab658ddf 100644 --- a/libavcodec/vble.c +++ b/libavcodec/vble.c @@ -135,7 +135,7 @@ static int vble_decode_frame(AVCodecContext *avctx, AVFrame *pic, return ret; /* Set flags */ - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; /* Version should always be 1 */ @@ -191,6 +191,9 @@ static av_cold int vble_decode_init(AVCodecContext *avctx) ctx->size = av_image_get_buffer_size(avctx->pix_fmt, avctx->width, avctx->height, 1); + if (ctx->size < 0) + return ctx->size; + ctx->val = av_malloc_array(ctx->size, sizeof(*ctx->val)); if (!ctx->val) { diff --git a/libavcodec/vbndec.c b/libavcodec/vbndec.c index d92dcd46b92..68b22361248 100644 --- a/libavcodec/vbndec.c +++ b/libavcodec/vbndec.c @@ -151,7 +151,7 @@ static int vbn_decode_frame(AVCodecContext *avctx, goto out; frame->pict_type = AV_PICTURE_TYPE_I; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; if (format == VBN_FORMAT_RAW) { uint8_t *flipped = frame->data[0] + frame->linesize[0] * (frame->height - 1); @@ -162,7 +162,9 @@ static int vbn_decode_frame(AVCodecContext *avctx, ctx->dec.raw_ratio = 16; ctx->dec.frame_data.out = frame->data[0] + frame->linesize[0] * (frame->height - 1); ctx->dec.stride = -frame->linesize[0]; - avctx->execute2(avctx, ff_texturedsp_decompress_thread, &ctx->dec, NULL, ctx->dec.slice_count); + ctx->dec.width = avctx->coded_width; + ctx->dec.height = avctx->coded_height; + ff_texturedsp_exec_decompress_threads(avctx, &ctx->dec); } *got_frame = 1; diff --git a/libavcodec/vbnenc.c b/libavcodec/vbnenc.c index 7ce91863d7f..55e436b5e5a 100644 --- a/libavcodec/vbnenc.c +++ b/libavcodec/vbnenc.c @@ -36,7 +36,7 @@ typedef struct VBNContext { AVClass *class; - TextureDSPContext dxtc; + TextureDSPEncContext dxtc; int format; TextureDSPThreadContext enc; } VBNContext; @@ -114,7 +114,9 @@ static int vbn_encode(AVCodecContext *avctx, AVPacket *pkt, ctx->enc.frame_data.in = (frame->height - 1) * frame->linesize[0] + frame->data[0]; ctx->enc.stride = -frame->linesize[0]; ctx->enc.tex_data.out = pkt->data + VBN_HEADER_SIZE; - avctx->execute2(avctx, ff_texturedsp_compress_thread, &ctx->enc, NULL, ctx->enc.slice_count); + ctx->enc.width = avctx->width; + ctx->enc.height = avctx->height; + ff_texturedsp_exec_compress_threads(avctx, &ctx->enc); } else { const uint8_t *flipped = frame->data[0] + frame->linesize[0] * (frame->height - 1); av_image_copy_plane(pkt->data + VBN_HEADER_SIZE, linesize, flipped, -frame->linesize[0], linesize, frame->height); @@ -134,10 +136,10 @@ static av_cold int vbn_init(AVCodecContext *avctx) #define OFFSET(x) offsetof(VBNContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "format", "Texture format", OFFSET(format), AV_OPT_TYPE_INT, { .i64 = VBN_FORMAT_DXT5 }, VBN_FORMAT_RAW, VBN_FORMAT_DXT5, FLAGS, "format" }, - { "raw", "RAW texture", 0, AV_OPT_TYPE_CONST, { .i64 = VBN_FORMAT_RAW }, 0, 0, FLAGS, "format" }, - { "dxt1", "DXT1 texture", 0, AV_OPT_TYPE_CONST, { .i64 = VBN_FORMAT_DXT1 }, 0, 0, FLAGS, "format" }, - { "dxt5", "DXT5 texture", 0, AV_OPT_TYPE_CONST, { .i64 = VBN_FORMAT_DXT5 }, 0, 0, FLAGS, "format" }, + { "format", "Texture format", OFFSET(format), AV_OPT_TYPE_INT, { .i64 = VBN_FORMAT_DXT5 }, VBN_FORMAT_RAW, VBN_FORMAT_DXT5, FLAGS, .unit = "format" }, + { "raw", "RAW texture", 0, AV_OPT_TYPE_CONST, { .i64 = VBN_FORMAT_RAW }, 0, 0, FLAGS, .unit = "format" }, + { "dxt1", "DXT1 texture", 0, AV_OPT_TYPE_CONST, { .i64 = VBN_FORMAT_DXT1 }, 0, 0, FLAGS, .unit = "format" }, + { "dxt5", "DXT5 texture", 0, AV_OPT_TYPE_CONST, { .i64 = VBN_FORMAT_DXT5 }, 0, 0, FLAGS, .unit = "format" }, { NULL }, }; diff --git a/libavcodec/vc1.c b/libavcodec/vc1.c index d4014d25abe..e234192fdd4 100644 --- a/libavcodec/vc1.c +++ b/libavcodec/vc1.c @@ -104,7 +104,7 @@ static int bitplane_decoding(uint8_t* data, int *raw_flag, VC1Context *v) height = v->s.mb_height >> v->field_mode; stride = v->s.mb_stride; invert = get_bits1(gb); - imode = get_vlc2(gb, ff_vc1_imode_vlc.table, VC1_IMODE_VLC_BITS, 1); + imode = get_vlc2(gb, ff_vc1_imode_vlc, VC1_IMODE_VLC_BITS, 1); *raw_flag = 0; switch (imode) { @@ -126,7 +126,7 @@ static int bitplane_decoding(uint8_t* data, int *raw_flag, VC1Context *v) y = offset = 0; // decode bitplane as one long line for (; y < height * width; y += 2) { - code = get_vlc2(gb, ff_vc1_norm2_vlc.table, VC1_NORM2_VLC_BITS, 1); + code = get_vlc2(gb, ff_vc1_norm2_vlc, VC1_NORM2_VLC_BITS, 1); *planep++ = code & 1; offset++; if (offset == width) { @@ -146,7 +146,7 @@ static int bitplane_decoding(uint8_t* data, int *raw_flag, VC1Context *v) if (!(height % 3) && (width % 3)) { // use 2x3 decoding for (y = 0; y < height; y += 3) { for (x = width & 1; x < width; x += 2) { - code = get_vlc2(gb, ff_vc1_norm6_vlc.table, VC1_NORM6_VLC_BITS, 2); + code = get_vlc2(gb, ff_vc1_norm6_vlc, VC1_NORM6_VLC_BITS, 2); if (code < 0) { av_log(v->s.avctx, AV_LOG_DEBUG, "invalid NORM-6 VLC\n"); return -1; @@ -166,7 +166,7 @@ static int bitplane_decoding(uint8_t* data, int *raw_flag, VC1Context *v) planep += (height & 1) * stride; for (y = height & 1; y < height; y += 2) { for (x = width % 3; x < width; x += 3) { - code = get_vlc2(gb, ff_vc1_norm6_vlc.table, VC1_NORM6_VLC_BITS, 2); + code = get_vlc2(gb, ff_vc1_norm6_vlc, VC1_NORM6_VLC_BITS, 2); if (code < 0) { av_log(v->s.avctx, AV_LOG_DEBUG, "invalid NORM-6 VLC\n"); return -1; @@ -418,6 +418,14 @@ static int decode_sequence_header_adv(VC1Context *v, GetBitContext *gb) v->s.loop_filter, v->chromaformat, v->broadcast, v->interlace, v->tfcntrflag, v->finterpflag); +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + if (v->broadcast) { // Pulldown may be present + v->s.avctx->ticks_per_frame = 2; + } +FF_ENABLE_DEPRECATION_WARNINGS +#endif + v->psf = get_bits1(gb); if (v->psf) { //PsF, 6.1.13 av_log(v->s.avctx, AV_LOG_ERROR, "Progressive Segmented Frame mode: not supported (yet)\n"); @@ -467,9 +475,6 @@ static int decode_sequence_header_adv(VC1Context *v, GetBitContext *gb) v->s.avctx->framerate.num = ff_vc1_fps_nr[nr - 1] * 1000; } } - if (v->broadcast) { // Pulldown may be present - v->s.avctx->ticks_per_frame = 2; - } } if (get_bits1(gb)) { @@ -760,7 +765,7 @@ int ff_vc1_parse_frame_header(VC1Context *v, GetBitContext* gb) /* Hopefully this is correct for P-frames */ v->s.mv_table_index = get_bits(gb, 2); //but using ff_vc1_ tables v->cbptab = get_bits(gb, 2); - v->cbpcy_vlc = &ff_vc1_cbpcy_p_vlc[v->cbptab]; + v->cbpcy_vlc = ff_vc1_cbpcy_p_vlc[v->cbptab]; if (v->dquant) { av_log(v->s.avctx, AV_LOG_DEBUG, "VOP DQuant info\n"); @@ -799,7 +804,7 @@ int ff_vc1_parse_frame_header(VC1Context *v, GetBitContext* gb) v->s.mv_table_index = get_bits(gb, 2); v->cbptab = get_bits(gb, 2); - v->cbpcy_vlc = &ff_vc1_cbpcy_p_vlc[v->cbptab]; + v->cbpcy_vlc = ff_vc1_cbpcy_p_vlc[v->cbptab]; if (v->dquant) { av_log(v->s.avctx, AV_LOG_DEBUG, "VOP DQuant info\n"); @@ -1056,19 +1061,19 @@ int ff_vc1_parse_frame_header_adv(VC1Context *v, GetBitContext* gb) "Imode: %i, Invert: %i\n", status>>1, status&1); v->mbmodetab = get_bits(gb, 2); if (v->fourmvswitch) - v->mbmode_vlc = &ff_vc1_intfr_4mv_mbmode_vlc[v->mbmodetab]; + v->mbmode_vlc = ff_vc1_intfr_4mv_mbmode_vlc[v->mbmodetab]; else - v->mbmode_vlc = &ff_vc1_intfr_non4mv_mbmode_vlc[v->mbmodetab]; + v->mbmode_vlc = ff_vc1_intfr_non4mv_mbmode_vlc[v->mbmodetab]; v->imvtab = get_bits(gb, 2); - v->imv_vlc = &ff_vc1_1ref_mvdata_vlc[v->imvtab]; + v->imv_vlc = ff_vc1_1ref_mvdata_vlc[v->imvtab]; // interlaced p-picture cbpcy range is [1, 63] v->icbptab = get_bits(gb, 3); - v->cbpcy_vlc = &ff_vc1_icbpcy_vlc[v->icbptab]; + v->cbpcy_vlc = ff_vc1_icbpcy_vlc[v->icbptab]; v->twomvbptab = get_bits(gb, 2); - v->twomvbp_vlc = &ff_vc1_2mv_block_pattern_vlc[v->twomvbptab]; + v->twomvbp_vlc = ff_vc1_2mv_block_pattern_vlc[v->twomvbptab]; if (v->fourmvswitch) { v->fourmvbptab = get_bits(gb, 2); - v->fourmvbp_vlc = &ff_vc1_4mv_block_pattern_vlc[v->fourmvbptab]; + v->fourmvbp_vlc = ff_vc1_4mv_block_pattern_vlc[v->fourmvbptab]; } } } @@ -1153,7 +1158,7 @@ int ff_vc1_parse_frame_header_adv(VC1Context *v, GetBitContext* gb) /* Hopefully this is correct for P-frames */ v->s.mv_table_index = get_bits(gb, 2); //but using ff_vc1_ tables v->cbptab = get_bits(gb, 2); - v->cbpcy_vlc = &ff_vc1_cbpcy_p_vlc[v->cbptab]; + v->cbpcy_vlc = ff_vc1_cbpcy_p_vlc[v->cbptab]; } else if (v->fcm == ILACE_FRAME) { // frame interlaced v->qs_last = v->s.quarter_sample; v->s.quarter_sample = 1; @@ -1162,18 +1167,18 @@ int ff_vc1_parse_frame_header_adv(VC1Context *v, GetBitContext* gb) v->mbmodetab = get_bits(gb, 3); v->imvtab = get_bits(gb, 2 + v->numref); if (!v->numref) - v->imv_vlc = &ff_vc1_1ref_mvdata_vlc[v->imvtab]; + v->imv_vlc = ff_vc1_1ref_mvdata_vlc[v->imvtab]; else - v->imv_vlc = &ff_vc1_2ref_mvdata_vlc[v->imvtab]; + v->imv_vlc = ff_vc1_2ref_mvdata_vlc[v->imvtab]; v->icbptab = get_bits(gb, 3); - v->cbpcy_vlc = &ff_vc1_icbpcy_vlc[v->icbptab]; + v->cbpcy_vlc = ff_vc1_icbpcy_vlc[v->icbptab]; if ((v->mv_mode == MV_PMODE_INTENSITY_COMP && v->mv_mode2 == MV_PMODE_MIXED_MV) || v->mv_mode == MV_PMODE_MIXED_MV) { v->fourmvbptab = get_bits(gb, 2); - v->fourmvbp_vlc = &ff_vc1_4mv_block_pattern_vlc[v->fourmvbptab]; - v->mbmode_vlc = &ff_vc1_if_mmv_mbmode_vlc[v->mbmodetab]; + v->fourmvbp_vlc = ff_vc1_4mv_block_pattern_vlc[v->fourmvbptab]; + v->mbmode_vlc = ff_vc1_if_mmv_mbmode_vlc[v->mbmodetab]; } else { - v->mbmode_vlc = &ff_vc1_if_1mv_mbmode_vlc[v->mbmodetab]; + v->mbmode_vlc = ff_vc1_if_1mv_mbmode_vlc[v->mbmodetab]; } } if (v->dquant) { @@ -1229,16 +1234,16 @@ int ff_vc1_parse_frame_header_adv(VC1Context *v, GetBitContext* gb) "Imode: %i, Invert: %i\n", status>>1, status&1); v->mbmodetab = get_bits(gb, 3); if (v->mv_mode == MV_PMODE_MIXED_MV) - v->mbmode_vlc = &ff_vc1_if_mmv_mbmode_vlc[v->mbmodetab]; + v->mbmode_vlc = ff_vc1_if_mmv_mbmode_vlc[v->mbmodetab]; else - v->mbmode_vlc = &ff_vc1_if_1mv_mbmode_vlc[v->mbmodetab]; + v->mbmode_vlc = ff_vc1_if_1mv_mbmode_vlc[v->mbmodetab]; v->imvtab = get_bits(gb, 3); - v->imv_vlc = &ff_vc1_2ref_mvdata_vlc[v->imvtab]; + v->imv_vlc = ff_vc1_2ref_mvdata_vlc[v->imvtab]; v->icbptab = get_bits(gb, 3); - v->cbpcy_vlc = &ff_vc1_icbpcy_vlc[v->icbptab]; + v->cbpcy_vlc = ff_vc1_icbpcy_vlc[v->icbptab]; if (v->mv_mode == MV_PMODE_MIXED_MV) { v->fourmvbptab = get_bits(gb, 2); - v->fourmvbp_vlc = &ff_vc1_4mv_block_pattern_vlc[v->fourmvbptab]; + v->fourmvbp_vlc = ff_vc1_4mv_block_pattern_vlc[v->fourmvbptab]; } v->numref = 1; // interlaced field B pictures are always 2-ref } else if (v->fcm == ILACE_FRAME) { @@ -1263,16 +1268,16 @@ int ff_vc1_parse_frame_header_adv(VC1Context *v, GetBitContext* gb) av_log(v->s.avctx, AV_LOG_DEBUG, "MB Skip plane encoding: " "Imode: %i, Invert: %i\n", status>>1, status&1); v->mbmodetab = get_bits(gb, 2); - v->mbmode_vlc = &ff_vc1_intfr_non4mv_mbmode_vlc[v->mbmodetab]; + v->mbmode_vlc = ff_vc1_intfr_non4mv_mbmode_vlc[v->mbmodetab]; v->imvtab = get_bits(gb, 2); - v->imv_vlc = &ff_vc1_1ref_mvdata_vlc[v->imvtab]; + v->imv_vlc = ff_vc1_1ref_mvdata_vlc[v->imvtab]; // interlaced p/b-picture cbpcy range is [1, 63] v->icbptab = get_bits(gb, 3); - v->cbpcy_vlc = &ff_vc1_icbpcy_vlc[v->icbptab]; + v->cbpcy_vlc = ff_vc1_icbpcy_vlc[v->icbptab]; v->twomvbptab = get_bits(gb, 2); - v->twomvbp_vlc = &ff_vc1_2mv_block_pattern_vlc[v->twomvbptab]; + v->twomvbp_vlc = ff_vc1_2mv_block_pattern_vlc[v->twomvbptab]; v->fourmvbptab = get_bits(gb, 2); - v->fourmvbp_vlc = &ff_vc1_4mv_block_pattern_vlc[v->fourmvbptab]; + v->fourmvbp_vlc = ff_vc1_4mv_block_pattern_vlc[v->fourmvbptab]; } else { v->mv_mode = get_bits1(gb) ? MV_PMODE_1MV : MV_PMODE_1MV_HPEL_BILIN; v->qs_last = v->s.quarter_sample; @@ -1290,7 +1295,7 @@ int ff_vc1_parse_frame_header_adv(VC1Context *v, GetBitContext* gb) "Imode: %i, Invert: %i\n", status>>1, status&1); v->s.mv_table_index = get_bits(gb, 2); v->cbptab = get_bits(gb, 2); - v->cbpcy_vlc = &ff_vc1_cbpcy_p_vlc[v->cbptab]; + v->cbpcy_vlc = ff_vc1_cbpcy_p_vlc[v->cbptab]; } if (v->dquant) { diff --git a/libavcodec/vc1.h b/libavcodec/vc1.h index 3b6be78141d..0e01458c891 100644 --- a/libavcodec/vc1.h +++ b/libavcodec/vc1.h @@ -279,7 +279,7 @@ typedef struct VC1Context{ */ uint8_t mvrange; ///< Extended MV range flag uint8_t pquantizer; ///< Uniform (over sequence) quantizer in use - VLC *cbpcy_vlc; ///< CBPCY VLC table + const VLCElem *cbpcy_vlc; ///< CBPCY VLC table int tt_index; ///< Index for Transform Type tables (to decode TTMB) uint8_t* mv_type_mb_plane; ///< bitplane for mv_type == (4MV) uint8_t* direct_mb_plane; ///< bitplane for "direct" MBs @@ -334,10 +334,10 @@ typedef struct VC1Context{ int intcomp; uint8_t lumscale2; ///< for interlaced field P picture uint8_t lumshift2; - VLC* mbmode_vlc; - VLC* imv_vlc; - VLC* twomvbp_vlc; - VLC* fourmvbp_vlc; + const VLCElem *mbmode_vlc; + const VLCElem *imv_vlc; + const VLCElem *twomvbp_vlc; + const VLCElem *fourmvbp_vlc; uint8_t twomvbp; uint8_t fourmvbp; uint8_t* fieldtx_plane; diff --git a/libavcodec/vc1_block.c b/libavcodec/vc1_block.c index 1baa6a9bf63..a6ee4922f95 100644 --- a/libavcodec/vc1_block.c +++ b/libavcodec/vc1_block.c @@ -227,7 +227,7 @@ static void vc1_put_blocks_clamped(VC1Context *v, int put_signed) * @param _dmv_y Vertical differential for decoded MV */ #define GET_MVDATA(_dmv_x, _dmv_y) \ - index = 1 + get_vlc2(gb, ff_vc1_mv_diff_vlc[s->mv_table_index].table, \ + index = 1 + get_vlc2(gb, ff_vc1_mv_diff_vlc[s->mv_table_index], \ VC1_MV_DIFF_VLC_BITS, 2); \ if (index > 36) { \ mb_has_coeffs = 1; \ @@ -282,7 +282,7 @@ static av_always_inline void get_mvdata_interlaced(VC1Context *v, int *dmv_x, } extend_x = v->dmvrange & 1; extend_y = (v->dmvrange >> 1) & 1; - index = get_vlc2(gb, v->imv_vlc->table, bits, 3); + index = get_vlc2(gb, v->imv_vlc, bits, 3); if (index == esc) { *dmv_x = get_bits(gb, v->k_x); *dmv_y = get_bits(gb, v->k_y); @@ -519,7 +519,7 @@ static int vc1_decode_ac_coeff(VC1Context *v, int *last, int *skip, GetBitContext *gb = &v->s.gb; int index, run, level, lst, sign; - index = get_vlc2(gb, ff_vc1_ac_coeff_table[codingset].table, AC_VLC_BITS, 3); + index = get_vlc2(gb, ff_vc1_ac_coeff_table[codingset], AC_VLC_BITS, 3); if (index < 0) return index; if (index != ff_vc1_ac_sizes[codingset] - 1) { @@ -530,7 +530,7 @@ static int vc1_decode_ac_coeff(VC1Context *v, int *last, int *skip, } else { int escape = decode210(gb); if (escape != 2) { - index = get_vlc2(gb, ff_vc1_ac_coeff_table[codingset].table, AC_VLC_BITS, 3); + index = get_vlc2(gb, ff_vc1_ac_coeff_table[codingset], AC_VLC_BITS, 3); if (index >= ff_vc1_ac_sizes[codingset] - 1U) return AVERROR_INVALIDDATA; run = vc1_index_decode_table[codingset][index][0]; @@ -592,13 +592,8 @@ static int vc1_decode_i_block(VC1Context *v, int16_t block[64], int n, int dcdiff, scale; /* Get DC differential */ - if (n < 4) { - dcdiff = get_vlc2(&s->gb, ff_msmp4_dc_luma_vlc[s->dc_table_index].table, - MSMP4_DC_VLC_BITS, 3); - } else { - dcdiff = get_vlc2(&s->gb, ff_msmp4_dc_chroma_vlc[s->dc_table_index].table, - MSMP4_DC_VLC_BITS, 3); - } + dcdiff = get_vlc2(&s->gb, ff_msmp4_dc_vlc[s->dc_table_index][n >= 4], + MSMP4_DC_VLC_BITS, 3); if (dcdiff) { const int m = (v->pq == 1 || v->pq == 2) ? 3 - v->pq : 0; if (dcdiff == 119 /* ESC index value */) { @@ -738,13 +733,8 @@ static int vc1_decode_i_block_adv(VC1Context *v, int16_t block[64], int n, int quant = FFABS(mquant); /* Get DC differential */ - if (n < 4) { - dcdiff = get_vlc2(&s->gb, ff_msmp4_dc_luma_vlc[s->dc_table_index].table, - MSMP4_DC_VLC_BITS, 3); - } else { - dcdiff = get_vlc2(&s->gb, ff_msmp4_dc_chroma_vlc[s->dc_table_index].table, - MSMP4_DC_VLC_BITS, 3); - } + dcdiff = get_vlc2(&s->gb, ff_msmp4_dc_vlc[s->dc_table_index][n >= 4], + MSMP4_DC_VLC_BITS, 3); if (dcdiff) { const int m = (quant == 1 || quant == 2) ? 3 - quant : 0; if (dcdiff == 119 /* ESC index value */) { @@ -940,13 +930,8 @@ static int vc1_decode_intra_block(VC1Context *v, int16_t block[64], int n, s->c_dc_scale = s->c_dc_scale_table[quant]; /* Get DC differential */ - if (n < 4) { - dcdiff = get_vlc2(&s->gb, ff_msmp4_dc_luma_vlc[s->dc_table_index].table, - MSMP4_DC_VLC_BITS, 3); - } else { - dcdiff = get_vlc2(&s->gb, ff_msmp4_dc_chroma_vlc[s->dc_table_index].table, - MSMP4_DC_VLC_BITS, 3); - } + dcdiff = get_vlc2(&s->gb, ff_msmp4_dc_vlc[s->dc_table_index][n >= 4], + MSMP4_DC_VLC_BITS, 3); if (dcdiff) { const int m = (quant == 1 || quant == 2) ? 3 - quant : 0; if (dcdiff == 119 /* ESC index value */) { @@ -1139,10 +1124,10 @@ static int vc1_decode_p_block(VC1Context *v, int16_t block[64], int n, s->bdsp.clear_block(block); if (ttmb == -1) { - ttblk = ff_vc1_ttblk_to_tt[v->tt_index][get_vlc2(gb, ff_vc1_ttblk_vlc[v->tt_index].table, VC1_TTBLK_VLC_BITS, 1)]; + ttblk = ff_vc1_ttblk_to_tt[v->tt_index][get_vlc2(gb, ff_vc1_ttblk_vlc[v->tt_index], VC1_TTBLK_VLC_BITS, 1)]; } if (ttblk == TT_4X4) { - subblkpat = ~(get_vlc2(gb, ff_vc1_subblkpat_vlc[v->tt_index].table, VC1_SUBBLKPAT_VLC_BITS, 1) + 1); + subblkpat = ~(get_vlc2(gb, ff_vc1_subblkpat_vlc[v->tt_index], VC1_SUBBLKPAT_VLC_BITS, 1) + 1); } if ((ttblk != TT_8X8 && ttblk != TT_4X4) && ((v->ttmbf || (ttmb != -1 && (ttmb & 8) && !first_block)) @@ -1343,7 +1328,7 @@ static int vc1_decode_p_mb(VC1Context *v) } else if (mb_has_coeffs) { if (s->mb_intra) s->ac_pred = get_bits1(gb); - cbp = get_vlc2(&v->s.gb, v->cbpcy_vlc->table, VC1_CBPCY_P_VLC_BITS, 2); + cbp = get_vlc2(&v->s.gb, v->cbpcy_vlc, VC1_CBPCY_P_VLC_BITS, 2); GET_MQUANT(); } else { mquant = v->pq; @@ -1352,7 +1337,7 @@ static int vc1_decode_p_mb(VC1Context *v) s->current_picture.qscale_table[mb_pos] = mquant; if (!v->ttmbf && !s->mb_intra && mb_has_coeffs) - ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index].table, + ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index], VC1_TTMB_VLC_BITS, 2); if (!s->mb_intra) ff_vc1_mc_1mv(v, 0); dst_idx = 0; @@ -1408,7 +1393,7 @@ static int vc1_decode_p_mb(VC1Context *v) int intra_count = 0, coded_inter = 0; int is_intra[6], is_coded[6]; /* Get CBPCY */ - cbp = get_vlc2(&v->s.gb, v->cbpcy_vlc->table, VC1_CBPCY_P_VLC_BITS, 2); + cbp = get_vlc2(&v->s.gb, v->cbpcy_vlc, VC1_CBPCY_P_VLC_BITS, 2); for (i = 0; i < 6; i++) { val = ((cbp >> (5 - i)) & 1); s->dc_val[0][s->block_index[i]] = 0; @@ -1460,7 +1445,7 @@ static int vc1_decode_p_mb(VC1Context *v) s->ac_pred = 0; } if (!v->ttmbf && coded_inter) - ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index].table, VC1_TTMB_VLC_BITS, 2); + ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index], VC1_TTMB_VLC_BITS, 2); for (i = 0; i < 6; i++) { dst_idx += i >> 2; off = (i & 4) ? 0 : ((i & 1) * 8 + (i & 2) * 4 * s->linesize); @@ -1554,9 +1539,9 @@ static int vc1_decode_p_mb_intfr(VC1Context *v) skipped = v->s.mbskip_table[mb_pos]; if (!skipped) { if (v->fourmvswitch) - idx_mbmode = get_vlc2(gb, v->mbmode_vlc->table, VC1_INTFR_4MV_MBMODE_VLC_BITS, 2); // try getting this done + idx_mbmode = get_vlc2(gb, v->mbmode_vlc, VC1_INTFR_4MV_MBMODE_VLC_BITS, 2); // try getting this done else - idx_mbmode = get_vlc2(gb, v->mbmode_vlc->table, VC1_INTFR_NON4MV_MBMODE_VLC_BITS, 2); // in a single line + idx_mbmode = get_vlc2(gb, v->mbmode_vlc, VC1_INTFR_NON4MV_MBMODE_VLC_BITS, 2); // in a single line switch (ff_vc1_mbmode_intfrp[v->fourmvswitch][idx_mbmode][0]) { /* store the motion vector type in a flag (useful later) */ case MV_PMODE_INTFR_4MV: @@ -1598,7 +1583,7 @@ static int vc1_decode_p_mb_intfr(VC1Context *v) fieldtx = v->fieldtx_plane[mb_pos] = get_bits1(gb); mb_has_coeffs = get_bits1(gb); if (mb_has_coeffs) - cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc->table, VC1_CBPCY_P_VLC_BITS, 2); + cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc, VC1_CBPCY_P_VLC_BITS, 2); v->s.ac_pred = v->acpred_plane[mb_pos] = get_bits1(gb); GET_MQUANT(); s->current_picture.qscale_table[mb_pos] = mquant; @@ -1632,13 +1617,13 @@ static int vc1_decode_p_mb_intfr(VC1Context *v) } else { // inter MB mb_has_coeffs = ff_vc1_mbmode_intfrp[v->fourmvswitch][idx_mbmode][3]; if (mb_has_coeffs) - cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc->table, VC1_CBPCY_P_VLC_BITS, 2); + cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc, VC1_CBPCY_P_VLC_BITS, 2); if (ff_vc1_mbmode_intfrp[v->fourmvswitch][idx_mbmode][0] == MV_PMODE_INTFR_2MV_FIELD) { - v->twomvbp = get_vlc2(gb, v->twomvbp_vlc->table, VC1_2MV_BLOCK_PATTERN_VLC_BITS, 1); + v->twomvbp = get_vlc2(gb, v->twomvbp_vlc, VC1_2MV_BLOCK_PATTERN_VLC_BITS, 1); } else { if ((ff_vc1_mbmode_intfrp[v->fourmvswitch][idx_mbmode][0] == MV_PMODE_INTFR_4MV) || (ff_vc1_mbmode_intfrp[v->fourmvswitch][idx_mbmode][0] == MV_PMODE_INTFR_4MV_FIELD)) { - v->fourmvbp = get_vlc2(gb, v->fourmvbp_vlc->table, VC1_4MV_BLOCK_PATTERN_VLC_BITS, 1); + v->fourmvbp = get_vlc2(gb, v->fourmvbp_vlc, VC1_4MV_BLOCK_PATTERN_VLC_BITS, 1); } } s->mb_intra = v->is_intra[s->mb_x] = 0; @@ -1687,7 +1672,7 @@ static int vc1_decode_p_mb_intfr(VC1Context *v) GET_MQUANT(); // p. 227 s->current_picture.qscale_table[mb_pos] = mquant; if (!v->ttmbf && cbp) - ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index].table, VC1_TTMB_VLC_BITS, 2); + ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index], VC1_TTMB_VLC_BITS, 2); for (i = 0; i < 6; i++) { s->dc_val[0][s->block_index[i]] = 0; dst_idx += i >> 2; @@ -1757,7 +1742,7 @@ static int vc1_decode_p_mb_intfi(VC1Context *v) mquant = v->pq; /* Lossy initialization */ - idx_mbmode = get_vlc2(gb, v->mbmode_vlc->table, VC1_IF_MBMODE_VLC_BITS, 2); + idx_mbmode = get_vlc2(gb, v->mbmode_vlc, VC1_IF_MBMODE_VLC_BITS, 2); if (idx_mbmode <= 1) { // intra MB v->is_intra[s->mb_x] = 0x3f; // Set the bitfield to all 1. s->mb_intra = 1; @@ -1772,7 +1757,7 @@ static int vc1_decode_p_mb_intfi(VC1Context *v) v->s.ac_pred = v->acpred_plane[mb_pos] = get_bits1(gb); mb_has_coeffs = idx_mbmode & 1; if (mb_has_coeffs) - cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc->table, VC1_ICBPCY_VLC_BITS, 2); + cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc, VC1_ICBPCY_VLC_BITS, 2); dst_idx = 0; for (i = 0; i < 6; i++) { v->a_avail = v->c_avail = 0; @@ -1807,7 +1792,7 @@ static int vc1_decode_p_mb_intfi(VC1Context *v) ff_vc1_mc_1mv(v, 0); mb_has_coeffs = !(idx_mbmode & 2); } else { // 4-MV - v->fourmvbp = get_vlc2(gb, v->fourmvbp_vlc->table, VC1_4MV_BLOCK_PATTERN_VLC_BITS, 1); + v->fourmvbp = get_vlc2(gb, v->fourmvbp_vlc, VC1_4MV_BLOCK_PATTERN_VLC_BITS, 1); for (i = 0; i < 4; i++) { dmv_x = dmv_y = pred_flag = 0; if (v->fourmvbp & (8 >> i)) @@ -1819,13 +1804,13 @@ static int vc1_decode_p_mb_intfi(VC1Context *v) mb_has_coeffs = idx_mbmode & 1; } if (mb_has_coeffs) - cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc->table, VC1_CBPCY_P_VLC_BITS, 2); + cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc, VC1_CBPCY_P_VLC_BITS, 2); if (cbp) { GET_MQUANT(); } s->current_picture.qscale_table[mb_pos] = mquant; if (!v->ttmbf && cbp) { - ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index].table, VC1_TTMB_VLC_BITS, 2); + ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index], VC1_TTMB_VLC_BITS, 2); } dst_idx = 0; for (i = 0; i < 6; i++) { @@ -1929,12 +1914,12 @@ static int vc1_decode_b_mb(VC1Context *v) return 0; } if (direct) { - cbp = get_vlc2(&v->s.gb, v->cbpcy_vlc->table, VC1_CBPCY_P_VLC_BITS, 2); + cbp = get_vlc2(&v->s.gb, v->cbpcy_vlc, VC1_CBPCY_P_VLC_BITS, 2); GET_MQUANT(); s->mb_intra = 0; s->current_picture.qscale_table[mb_pos] = mquant; if (!v->ttmbf) - ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index].table, VC1_TTMB_VLC_BITS, 2); + ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index], VC1_TTMB_VLC_BITS, 2); dmv_x[0] = dmv_y[0] = dmv_x[1] = dmv_y[1] = 0; ff_vc1_pred_b_mv(v, dmv_x, dmv_y, direct, bmvtype); vc1_b_mc(v, dmv_x, dmv_y, direct, bmvtype); @@ -1967,11 +1952,11 @@ static int vc1_decode_b_mb(VC1Context *v) } if (s->mb_intra) s->ac_pred = get_bits1(gb); - cbp = get_vlc2(&v->s.gb, v->cbpcy_vlc->table, VC1_CBPCY_P_VLC_BITS, 2); + cbp = get_vlc2(&v->s.gb, v->cbpcy_vlc, VC1_CBPCY_P_VLC_BITS, 2); GET_MQUANT(); s->current_picture.qscale_table[mb_pos] = mquant; if (!v->ttmbf && !s->mb_intra && mb_has_coeffs) - ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index].table, VC1_TTMB_VLC_BITS, 2); + ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index], VC1_TTMB_VLC_BITS, 2); } } dst_idx = 0; @@ -2040,7 +2025,7 @@ static int vc1_decode_b_mb_intfi(VC1Context *v) mquant = v->pq; /* Lossy initialization */ s->mb_intra = 0; - idx_mbmode = get_vlc2(gb, v->mbmode_vlc->table, VC1_IF_MBMODE_VLC_BITS, 2); + idx_mbmode = get_vlc2(gb, v->mbmode_vlc, VC1_IF_MBMODE_VLC_BITS, 2); if (idx_mbmode <= 1) { // intra MB v->is_intra[s->mb_x] = 0x3f; // Set the bitfield to all 1. s->mb_intra = 1; @@ -2055,7 +2040,7 @@ static int vc1_decode_b_mb_intfi(VC1Context *v) v->s.ac_pred = v->acpred_plane[mb_pos] = get_bits1(gb); mb_has_coeffs = idx_mbmode & 1; if (mb_has_coeffs) - cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc->table, VC1_ICBPCY_VLC_BITS, 2); + cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc, VC1_ICBPCY_VLC_BITS, 2); dst_idx = 0; for (i = 0; i < 6; i++) { v->a_avail = v->c_avail = 0; @@ -2133,7 +2118,7 @@ static int vc1_decode_b_mb_intfi(VC1Context *v) if (fwd) bmvtype = BMV_TYPE_FORWARD; v->bmvtype = bmvtype; - v->fourmvbp = get_vlc2(gb, v->fourmvbp_vlc->table, VC1_4MV_BLOCK_PATTERN_VLC_BITS, 1); + v->fourmvbp = get_vlc2(gb, v->fourmvbp_vlc, VC1_4MV_BLOCK_PATTERN_VLC_BITS, 1); for (i = 0; i < 4; i++) { dmv_x[0] = dmv_y[0] = pred_flag[0] = 0; dmv_x[1] = dmv_y[1] = pred_flag[1] = 0; @@ -2149,13 +2134,13 @@ static int vc1_decode_b_mb_intfi(VC1Context *v) mb_has_coeffs = idx_mbmode & 1; } if (mb_has_coeffs) - cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc->table, VC1_CBPCY_P_VLC_BITS, 2); + cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc, VC1_CBPCY_P_VLC_BITS, 2); if (cbp) { GET_MQUANT(); } s->current_picture.qscale_table[mb_pos] = mquant; if (!v->ttmbf && cbp) { - ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index].table, VC1_TTMB_VLC_BITS, 2); + ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index], VC1_TTMB_VLC_BITS, 2); } dst_idx = 0; for (i = 0; i < 6; i++) { @@ -2215,7 +2200,7 @@ static int vc1_decode_b_mb_intfr(VC1Context *v) skipped = v->s.mbskip_table[mb_pos]; if (!skipped) { - idx_mbmode = get_vlc2(gb, v->mbmode_vlc->table, VC1_INTFR_NON4MV_MBMODE_VLC_BITS, 2); + idx_mbmode = get_vlc2(gb, v->mbmode_vlc, VC1_INTFR_NON4MV_MBMODE_VLC_BITS, 2); if (ff_vc1_mbmode_intfrp[0][idx_mbmode][0] == MV_PMODE_INTFR_2MV_FIELD) { twomv = 1; v->blk_mv_type[s->block_index[0]] = 1; @@ -2243,7 +2228,7 @@ static int vc1_decode_b_mb_intfr(VC1Context *v) fieldtx = v->fieldtx_plane[mb_pos] = get_bits1(gb); mb_has_coeffs = get_bits1(gb); if (mb_has_coeffs) - cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc->table, VC1_CBPCY_P_VLC_BITS, 2); + cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc, VC1_CBPCY_P_VLC_BITS, 2); v->s.ac_pred = v->acpred_plane[mb_pos] = get_bits1(gb); GET_MQUANT(); s->current_picture.qscale_table[mb_pos] = mquant; @@ -2338,12 +2323,12 @@ static int vc1_decode_b_mb_intfr(VC1Context *v) if (!skipped) { // inter MB mb_has_coeffs = ff_vc1_mbmode_intfrp[0][idx_mbmode][3]; if (mb_has_coeffs) - cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc->table, VC1_CBPCY_P_VLC_BITS, 2); + cbp = 1 + get_vlc2(&v->s.gb, v->cbpcy_vlc, VC1_CBPCY_P_VLC_BITS, 2); if (!direct) { if (bmvtype == BMV_TYPE_INTERPOLATED && twomv) { - v->fourmvbp = get_vlc2(gb, v->fourmvbp_vlc->table, VC1_4MV_BLOCK_PATTERN_VLC_BITS, 1); + v->fourmvbp = get_vlc2(gb, v->fourmvbp_vlc, VC1_4MV_BLOCK_PATTERN_VLC_BITS, 1); } else if (bmvtype == BMV_TYPE_INTERPOLATED || twomv) { - v->twomvbp = get_vlc2(gb, v->twomvbp_vlc->table, VC1_2MV_BLOCK_PATTERN_VLC_BITS, 1); + v->twomvbp = get_vlc2(gb, v->twomvbp_vlc, VC1_2MV_BLOCK_PATTERN_VLC_BITS, 1); } } @@ -2453,7 +2438,7 @@ static int vc1_decode_b_mb_intfr(VC1Context *v) GET_MQUANT(); // p. 227 s->current_picture.qscale_table[mb_pos] = mquant; if (!v->ttmbf && cbp) - ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index].table, VC1_TTMB_VLC_BITS, 2); + ttmb = get_vlc2(gb, ff_vc1_ttmb_vlc[v->tt_index], VC1_TTMB_VLC_BITS, 2); for (i = 0; i < 6; i++) { s->dc_val[0][s->block_index[i]] = 0; dst_idx += i >> 2; @@ -2591,7 +2576,7 @@ static void vc1_decode_i_blocks(VC1Context *v) } // do actual MB decoding and displaying - cbp = get_vlc2(&v->s.gb, ff_msmp4_mb_i_vlc.table, + cbp = get_vlc2(&v->s.gb, ff_msmp4_mb_i_vlc, MSMP4_MB_INTRA_VLC_BITS, 2); v->s.ac_pred = get_bits1(&v->s.gb); @@ -2727,7 +2712,7 @@ static int vc1_decode_i_blocks_adv(VC1Context *v) return 0; } - cbp = get_vlc2(&v->s.gb, ff_msmp4_mb_i_vlc.table, + cbp = get_vlc2(&v->s.gb, ff_msmp4_mb_i_vlc, MSMP4_MB_INTRA_VLC_BITS, 2); if (v->acpred_is_raw) v->s.ac_pred = get_bits1(&v->s.gb); diff --git a/libavcodec/vc1_loopfilter.c b/libavcodec/vc1_loopfilter.c index 0f990cccefe..ee694ede288 100644 --- a/libavcodec/vc1_loopfilter.c +++ b/libavcodec/vc1_loopfilter.c @@ -1125,10 +1125,7 @@ static av_always_inline void vc1_b_h_intfi_loop_filter(VC1Context *v, uint8_t *d dst = dest + (block_num & 2) * 4 * s->linesize + (block_num & 1) * 8; if (!(flags & RIGHT_EDGE) || !(block_num & 5)) { - if (block_num > 3) - v->vc1dsp.vc1_h_loop_filter8(dst + 8, linesize, pq); - else - v->vc1dsp.vc1_h_loop_filter8(dst + 8, linesize, pq); + v->vc1dsp.vc1_h_loop_filter8(dst + 8, linesize, pq); } tt = ttblk[0] >> (block_num * 4) & 0xf; diff --git a/libavcodec/vc1_mc.c b/libavcodec/vc1_mc.c index 1b8d8799b3c..8f0b3f6fab1 100644 --- a/libavcodec/vc1_mc.c +++ b/libavcodec/vc1_mc.c @@ -233,7 +233,7 @@ void ff_vc1_mc_1mv(VC1Context *v, int dir) luty = v->last_luty; lutuv = v->last_lutuv; use_ic = v->last_use_ic; - interlace = s->last_picture.f->interlaced_frame; + interlace = !!(s->last_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } } else { srcY = s->next_picture.f->data[0]; @@ -242,7 +242,7 @@ void ff_vc1_mc_1mv(VC1Context *v, int dir) luty = v->next_luty; lutuv = v->next_lutuv; use_ic = v->next_use_ic; - interlace = s->next_picture.f->interlaced_frame; + interlace = !!(s->next_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } if (!srcY || !srcU) { @@ -482,13 +482,13 @@ void ff_vc1_mc_4mv_luma(VC1Context *v, int n, int dir, int avg) srcY = s->last_picture.f->data[0]; luty = v->last_luty; use_ic = v->last_use_ic; - interlace = s->last_picture.f->interlaced_frame; + interlace = !!(s->last_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } } else { srcY = s->next_picture.f->data[0]; luty = v->next_luty; use_ic = v->next_use_ic; - interlace = s->next_picture.f->interlaced_frame; + interlace = !!(s->next_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } if (!srcY) { @@ -708,14 +708,14 @@ void ff_vc1_mc_4mv_chroma(VC1Context *v, int dir) srcV = s->last_picture.f->data[2]; lutuv = v->last_lutuv; use_ic = v->last_use_ic; - interlace = s->last_picture.f->interlaced_frame; + interlace = !!(s->last_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } } else { srcU = s->next_picture.f->data[1]; srcV = s->next_picture.f->data[2]; lutuv = v->next_lutuv; use_ic = v->next_use_ic; - interlace = s->next_picture.f->interlaced_frame; + interlace = !!(s->next_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } if (!srcU) { @@ -884,13 +884,13 @@ void ff_vc1_mc_4mv_chroma4(VC1Context *v, int dir, int dir2, int avg) srcV = s->next_picture.f->data[2]; lutuv = v->next_lutuv; use_ic = v->next_use_ic; - interlace = s->next_picture.f->interlaced_frame; + interlace = !!(s->next_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } else { srcU = s->last_picture.f->data[1]; srcV = s->last_picture.f->data[2]; lutuv = v->last_lutuv; use_ic = v->last_use_ic; - interlace = s->last_picture.f->interlaced_frame; + interlace = !!(s->last_picture.f->flags & AV_FRAME_FLAG_INTERLACED); } if (!srcU) return; @@ -1034,7 +1034,7 @@ void ff_vc1_interp_mc(VC1Context *v) srcU = s->next_picture.f->data[1]; srcV = s->next_picture.f->data[2]; - interlace = s->next_picture.f->interlaced_frame; + interlace = !!(s->next_picture.f->flags & AV_FRAME_FLAG_INTERLACED); src_x = s->mb_x * 16 + (mx >> 2); src_y = s->mb_y * 16 + (my >> 2); diff --git a/libavcodec/vc1_parser.c b/libavcodec/vc1_parser.c index 4167215fb11..ec284dca009 100644 --- a/libavcodec/vc1_parser.c +++ b/libavcodec/vc1_parser.c @@ -89,11 +89,10 @@ static void vc1_extract_header(AVCodecParserContext *s, AVCodecContext *avctx, else s->pict_type = vpc->v.s.pict_type; - if (avctx->ticks_per_frame > 1){ + if (vpc->v.broadcast){ // process pulldown flags s->repeat_pict = 1; // Pulldown flags are only valid when 'broadcast' has been set. - // So ticks_per_frame will be 2 if (vpc->v.rff){ // repeat field s->repeat_pict = 2; diff --git a/libavcodec/vc1data.c b/libavcodec/vc1data.c index 5ebf20a0f02..4046ea042fc 100644 --- a/libavcodec/vc1data.c +++ b/libavcodec/vc1data.c @@ -102,26 +102,26 @@ const uint8_t ff_vc1_pquant_table[3][32] = { * @todo TODO move this into the context */ //@{ -VLC ff_vc1_imode_vlc; -VLC ff_vc1_norm2_vlc; -VLC ff_vc1_norm6_vlc; +VLCElem ff_vc1_imode_vlc[1 << VC1_IMODE_VLC_BITS]; +VLCElem ff_vc1_norm2_vlc[1 << VC1_NORM2_VLC_BITS]; +VLCElem ff_vc1_norm6_vlc[556]; /* Could be optimized, one table only needs 8 bits */ -VLC ff_vc1_ttmb_vlc[3]; -VLC ff_vc1_mv_diff_vlc[4]; -VLC ff_vc1_cbpcy_p_vlc[4]; -VLC ff_vc1_icbpcy_vlc[8]; -VLC ff_vc1_4mv_block_pattern_vlc[4]; -VLC ff_vc1_2mv_block_pattern_vlc[4]; -VLC ff_vc1_ttblk_vlc[3]; -VLC ff_vc1_subblkpat_vlc[3]; -VLC ff_vc1_intfr_4mv_mbmode_vlc[4]; -VLC ff_vc1_intfr_non4mv_mbmode_vlc[4]; -VLC ff_vc1_if_mmv_mbmode_vlc[8]; -VLC ff_vc1_if_1mv_mbmode_vlc[8]; -VLC ff_vc1_1ref_mvdata_vlc[4]; -VLC ff_vc1_2ref_mvdata_vlc[8]; +const VLCElem *ff_vc1_ttmb_vlc[3]; +const VLCElem *ff_vc1_mv_diff_vlc[4]; +const VLCElem *ff_vc1_cbpcy_p_vlc[4]; +const VLCElem *ff_vc1_icbpcy_vlc[8]; +const VLCElem *ff_vc1_4mv_block_pattern_vlc[4]; +const VLCElem *ff_vc1_2mv_block_pattern_vlc[4]; +const VLCElem *ff_vc1_ttblk_vlc[3]; +const VLCElem *ff_vc1_subblkpat_vlc[3]; +const VLCElem *ff_vc1_intfr_4mv_mbmode_vlc[4]; +const VLCElem *ff_vc1_intfr_non4mv_mbmode_vlc[4]; +const VLCElem *ff_vc1_if_mmv_mbmode_vlc[8]; +const VLCElem *ff_vc1_if_1mv_mbmode_vlc[8]; +const VLCElem *ff_vc1_1ref_mvdata_vlc[4]; +const VLCElem *ff_vc1_2ref_mvdata_vlc[8]; -VLC ff_vc1_ac_coeff_table[8]; +const VLCElem *ff_vc1_ac_coeff_table[8]; //@} diff --git a/libavcodec/vc1data.h b/libavcodec/vc1data.h index 0900469ab23..e71593d023f 100644 --- a/libavcodec/vc1data.h +++ b/libavcodec/vc1data.h @@ -56,42 +56,42 @@ extern const uint8_t ff_vc1_mbmode_intfrp[2][15][4]; */ //@{ #define VC1_IMODE_VLC_BITS 4 -extern VLC ff_vc1_imode_vlc; +extern VLCElem ff_vc1_imode_vlc[1 << VC1_IMODE_VLC_BITS]; #define VC1_NORM2_VLC_BITS 3 -extern VLC ff_vc1_norm2_vlc; +extern VLCElem ff_vc1_norm2_vlc[1 << VC1_NORM2_VLC_BITS]; #define VC1_NORM6_VLC_BITS 9 -extern VLC ff_vc1_norm6_vlc; +extern VLCElem ff_vc1_norm6_vlc[556]; /* Could be optimized, one table only needs 8 bits */ #define VC1_TTMB_VLC_BITS 9 //12 -extern VLC ff_vc1_ttmb_vlc[3]; +extern const VLCElem *ff_vc1_ttmb_vlc[3]; #define VC1_MV_DIFF_VLC_BITS 9 //15 -extern VLC ff_vc1_mv_diff_vlc[4]; +extern const VLCElem *ff_vc1_mv_diff_vlc[4]; #define VC1_CBPCY_P_VLC_BITS 9 //14 -extern VLC ff_vc1_cbpcy_p_vlc[4]; +extern const VLCElem *ff_vc1_cbpcy_p_vlc[4]; #define VC1_ICBPCY_VLC_BITS 9 -extern VLC ff_vc1_icbpcy_vlc[8]; +extern const VLCElem *ff_vc1_icbpcy_vlc[8]; #define VC1_4MV_BLOCK_PATTERN_VLC_BITS 6 -extern VLC ff_vc1_4mv_block_pattern_vlc[4]; +extern const VLCElem *ff_vc1_4mv_block_pattern_vlc[4]; #define VC1_2MV_BLOCK_PATTERN_VLC_BITS 3 -extern VLC ff_vc1_2mv_block_pattern_vlc[4]; +extern const VLCElem *ff_vc1_2mv_block_pattern_vlc[4]; #define VC1_TTBLK_VLC_BITS 5 -extern VLC ff_vc1_ttblk_vlc[3]; +extern const VLCElem *ff_vc1_ttblk_vlc[3]; #define VC1_SUBBLKPAT_VLC_BITS 6 -extern VLC ff_vc1_subblkpat_vlc[3]; +extern const VLCElem *ff_vc1_subblkpat_vlc[3]; #define VC1_INTFR_4MV_MBMODE_VLC_BITS 9 -extern VLC ff_vc1_intfr_4mv_mbmode_vlc[4]; +extern const VLCElem *ff_vc1_intfr_4mv_mbmode_vlc[4]; #define VC1_INTFR_NON4MV_MBMODE_VLC_BITS 6 -extern VLC ff_vc1_intfr_non4mv_mbmode_vlc[4]; +extern const VLCElem *ff_vc1_intfr_non4mv_mbmode_vlc[4]; #define VC1_IF_MMV_MBMODE_VLC_BITS 5 -extern VLC ff_vc1_if_mmv_mbmode_vlc[8]; +extern const VLCElem *ff_vc1_if_mmv_mbmode_vlc[8]; #define VC1_IF_1MV_MBMODE_VLC_BITS 5 -extern VLC ff_vc1_if_1mv_mbmode_vlc[8]; +extern const VLCElem *ff_vc1_if_1mv_mbmode_vlc[8]; #define VC1_1REF_MVDATA_VLC_BITS 9 -extern VLC ff_vc1_1ref_mvdata_vlc[4]; +extern const VLCElem *ff_vc1_1ref_mvdata_vlc[4]; #define VC1_2REF_MVDATA_VLC_BITS 9 -extern VLC ff_vc1_2ref_mvdata_vlc[8]; +extern const VLCElem *ff_vc1_2ref_mvdata_vlc[8]; -extern VLC ff_vc1_ac_coeff_table[8]; +extern const VLCElem *ff_vc1_ac_coeff_table[8]; #define VC1_IF_MBMODE_VLC_BITS 5 // as a placeholder for VC1_IF_MMV_MBMODE_VLC_BITS // or VC1_IF_1MV_MBMODE_VLC_BITS since they are the same diff --git a/libavcodec/vc1dec.c b/libavcodec/vc1dec.c index 5cb4c544c9a..3ca478e82a3 100644 --- a/libavcodec/vc1dec.c +++ b/libavcodec/vc1dec.c @@ -34,6 +34,7 @@ #include "decode.h" #include "get_bits.h" #include "h263dec.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "mpeg_er.h" #include "mpegvideo.h" @@ -50,6 +51,30 @@ #include "libavutil/thread.h" +static const enum AVPixelFormat vc1_hwaccel_pixfmt_list_420[] = { +#if CONFIG_VC1_DXVA2_HWACCEL + AV_PIX_FMT_DXVA2_VLD, +#endif +#if CONFIG_VC1_D3D11VA_HWACCEL + AV_PIX_FMT_D3D11VA_VLD, + AV_PIX_FMT_D3D11, +#endif +#if CONFIG_VC1_D3D12VA_HWACCEL + AV_PIX_FMT_D3D12, +#endif +#if CONFIG_VC1_NVDEC_HWACCEL + AV_PIX_FMT_CUDA, +#endif +#if CONFIG_VC1_VAAPI_HWACCEL + AV_PIX_FMT_VAAPI, +#endif +#if CONFIG_VC1_VDPAU_HWACCEL + AV_PIX_FMT_VDPAU, +#endif + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_NONE +}; + #if CONFIG_WMV3IMAGE_DECODER || CONFIG_VC1IMAGE_DECODER typedef struct SpriteData { @@ -416,7 +441,11 @@ static enum AVPixelFormat vc1_get_format(AVCodecContext *avctx) return AV_PIX_FMT_GRAY8; } - return ff_get_format(avctx, avctx->codec->pix_fmts); + if (avctx->codec_id == AV_CODEC_ID_VC1IMAGE || + avctx->codec_id == AV_CODEC_ID_WMV3IMAGE) + return AV_PIX_FMT_YUV420P; + + return ff_get_format(avctx, vc1_hwaccel_pixfmt_list_420); } av_cold int ff_vc1_decode_init(AVCodecContext *avctx) @@ -430,7 +459,6 @@ av_cold int ff_vc1_decode_init(AVCodecContext *avctx) return ret; ff_mpv_decode_init(s, avctx); - ff_mpv_idct_init(s); avctx->pix_fmt = vc1_get_format(avctx); @@ -469,122 +497,92 @@ av_cold void ff_vc1_init_transposed_scantables(VC1Context *v) v->top_blk_sh = 3; } -static const uint16_t vlc_offs[] = { - 0, 520, 552, 616, 1128, 1160, 1224, 1740, 1772, 1836, 1900, 2436, - 2986, 3050, 3610, 4154, 4218, 4746, 5326, 5390, 5902, 6554, 7658, 8342, - 9304, 9988, 10630, 11234, 12174, 13006, 13560, 14232, 14786, 15432, 16350, 17522, - 20372, 21818, 22330, 22394, 23166, 23678, 23742, 24820, 25332, 25396, 26460, 26980, - 27048, 27592, 27600, 27608, 27616, 27624, 28224, 28258, 28290, 28802, 28834, 28866, - 29378, 29412, 29444, 29960, 29994, 30026, 30538, 30572, 30604, 31120, 31154, 31186, - 31714, 31746, 31778, 32306, 32340, 32372 -}; - static av_cold void vc1_init_static(void) { static VLCElem vlc_table[32372]; - - INIT_VLC_STATIC(&ff_vc1_norm2_vlc, VC1_NORM2_VLC_BITS, 4, - vc1_norm2_bits, 1, 1, - vc1_norm2_codes, 1, 1, 1 << VC1_NORM2_VLC_BITS); - INIT_VLC_STATIC(&ff_vc1_norm6_vlc, VC1_NORM6_VLC_BITS, 64, - vc1_norm6_bits, 1, 1, - vc1_norm6_codes, 2, 2, 556); - INIT_VLC_STATIC(&ff_vc1_imode_vlc, VC1_IMODE_VLC_BITS, 7, - vc1_imode_bits, 1, 1, - vc1_imode_codes, 1, 1, 1 << VC1_IMODE_VLC_BITS); + VLCInitState state = VLC_INIT_STATE(vlc_table); + + VLC_INIT_STATIC_TABLE(ff_vc1_norm2_vlc, VC1_NORM2_VLC_BITS, 4, + vc1_norm2_bits, 1, 1, + vc1_norm2_codes, 1, 1, 0); + VLC_INIT_STATIC_TABLE(ff_vc1_norm6_vlc, VC1_NORM6_VLC_BITS, 64, + vc1_norm6_bits, 1, 1, + vc1_norm6_codes, 2, 2, 0); + VLC_INIT_STATIC_TABLE(ff_vc1_imode_vlc, VC1_IMODE_VLC_BITS, 7, + vc1_imode_bits, 1, 1, + vc1_imode_codes, 1, 1, 0); for (int i = 0; i < 3; i++) { - ff_vc1_ttmb_vlc[i].table = &vlc_table[vlc_offs[i * 3 + 0]]; - ff_vc1_ttmb_vlc[i].table_allocated = vlc_offs[i * 3 + 1] - vlc_offs[i * 3 + 0]; - init_vlc(&ff_vc1_ttmb_vlc[i], VC1_TTMB_VLC_BITS, 16, - vc1_ttmb_bits[i], 1, 1, - vc1_ttmb_codes[i], 2, 2, INIT_VLC_USE_NEW_STATIC); - ff_vc1_ttblk_vlc[i].table = &vlc_table[vlc_offs[i * 3 + 1]]; - ff_vc1_ttblk_vlc[i].table_allocated = vlc_offs[i * 3 + 2] - vlc_offs[i * 3 + 1]; - init_vlc(&ff_vc1_ttblk_vlc[i], VC1_TTBLK_VLC_BITS, 8, - vc1_ttblk_bits[i], 1, 1, - vc1_ttblk_codes[i], 1, 1, INIT_VLC_USE_NEW_STATIC); - ff_vc1_subblkpat_vlc[i].table = &vlc_table[vlc_offs[i * 3 + 2]]; - ff_vc1_subblkpat_vlc[i].table_allocated = vlc_offs[i * 3 + 3] - vlc_offs[i * 3 + 2]; - init_vlc(&ff_vc1_subblkpat_vlc[i], VC1_SUBBLKPAT_VLC_BITS, 15, - vc1_subblkpat_bits[i], 1, 1, - vc1_subblkpat_codes[i], 1, 1, INIT_VLC_USE_NEW_STATIC); - } - for (int i = 0; i < 4; i++) { - ff_vc1_4mv_block_pattern_vlc[i].table = &vlc_table[vlc_offs[i * 3 + 9]]; - ff_vc1_4mv_block_pattern_vlc[i].table_allocated = vlc_offs[i * 3 + 10] - vlc_offs[i * 3 + 9]; - init_vlc(&ff_vc1_4mv_block_pattern_vlc[i], VC1_4MV_BLOCK_PATTERN_VLC_BITS, 16, - vc1_4mv_block_pattern_bits[i], 1, 1, - vc1_4mv_block_pattern_codes[i], 1, 1, INIT_VLC_USE_NEW_STATIC); - ff_vc1_cbpcy_p_vlc[i].table = &vlc_table[vlc_offs[i * 3 + 10]]; - ff_vc1_cbpcy_p_vlc[i].table_allocated = vlc_offs[i * 3 + 11] - vlc_offs[i * 3 + 10]; - init_vlc(&ff_vc1_cbpcy_p_vlc[i], VC1_CBPCY_P_VLC_BITS, 64, - vc1_cbpcy_p_bits[i], 1, 1, - vc1_cbpcy_p_codes[i], 2, 2, INIT_VLC_USE_NEW_STATIC); - ff_vc1_mv_diff_vlc[i].table = &vlc_table[vlc_offs[i * 3 + 11]]; - ff_vc1_mv_diff_vlc[i].table_allocated = vlc_offs[i * 3 + 12] - vlc_offs[i * 3 + 11]; - init_vlc(&ff_vc1_mv_diff_vlc[i], VC1_MV_DIFF_VLC_BITS, 73, - vc1_mv_diff_bits[i], 1, 1, - vc1_mv_diff_codes[i], 2, 2, INIT_VLC_USE_NEW_STATIC); - } - for (int i = 0; i < 8; i++) { - ff_vc1_ac_coeff_table[i].table = &vlc_table[vlc_offs[i * 2 + 21]]; - ff_vc1_ac_coeff_table[i].table_allocated = vlc_offs[i * 2 + 22] - vlc_offs[i * 2 + 21]; - init_vlc(&ff_vc1_ac_coeff_table[i], AC_VLC_BITS, ff_vc1_ac_sizes[i], - &vc1_ac_tables[i][0][1], 8, 4, - &vc1_ac_tables[i][0][0], 8, 4, INIT_VLC_USE_NEW_STATIC); - /* initialize interlaced MVDATA tables (2-Ref) */ - ff_vc1_2ref_mvdata_vlc[i].table = &vlc_table[vlc_offs[i * 2 + 22]]; - ff_vc1_2ref_mvdata_vlc[i].table_allocated = vlc_offs[i * 2 + 23] - vlc_offs[i * 2 + 22]; - init_vlc(&ff_vc1_2ref_mvdata_vlc[i], VC1_2REF_MVDATA_VLC_BITS, 126, - vc1_2ref_mvdata_bits[i], 1, 1, - vc1_2ref_mvdata_codes[i], 4, 4, INIT_VLC_USE_NEW_STATIC); + ff_vc1_ttmb_vlc[i] = + ff_vlc_init_tables(&state, VC1_TTMB_VLC_BITS, 16, + vc1_ttmb_bits[i], 1, 1, + vc1_ttmb_codes[i], 2, 2, 0); + ff_vc1_ttblk_vlc[i] = + ff_vlc_init_tables(&state, VC1_TTBLK_VLC_BITS, 8, + vc1_ttblk_bits[i], 1, 1, + vc1_ttblk_codes[i], 1, 1, 0); + ff_vc1_subblkpat_vlc[i] = + ff_vlc_init_tables(&state, VC1_SUBBLKPAT_VLC_BITS, 15, + vc1_subblkpat_bits[i], 1, 1, + vc1_subblkpat_codes[i], 1, 1, 0); } for (int i = 0; i < 4; i++) { + ff_vc1_4mv_block_pattern_vlc[i] = + ff_vlc_init_tables(&state, VC1_4MV_BLOCK_PATTERN_VLC_BITS, 16, + vc1_4mv_block_pattern_bits[i], 1, 1, + vc1_4mv_block_pattern_codes[i], 1, 1, 0); + ff_vc1_cbpcy_p_vlc[i] = + ff_vlc_init_tables(&state, VC1_CBPCY_P_VLC_BITS, 64, + vc1_cbpcy_p_bits[i], 1, 1, + vc1_cbpcy_p_codes[i], 2, 2, 0); + ff_vc1_mv_diff_vlc[i] = + ff_vlc_init_tables(&state, VC1_MV_DIFF_VLC_BITS, 73, + vc1_mv_diff_bits[i], 1, 1, + vc1_mv_diff_codes[i], 2, 2, 0); /* initialize 4MV MBMODE VLC tables for interlaced frame P picture */ - ff_vc1_intfr_4mv_mbmode_vlc[i].table = &vlc_table[vlc_offs[i * 3 + 37]]; - ff_vc1_intfr_4mv_mbmode_vlc[i].table_allocated = vlc_offs[i * 3 + 38] - vlc_offs[i * 3 + 37]; - init_vlc(&ff_vc1_intfr_4mv_mbmode_vlc[i], VC1_INTFR_4MV_MBMODE_VLC_BITS, 15, - vc1_intfr_4mv_mbmode_bits[i], 1, 1, - vc1_intfr_4mv_mbmode_codes[i], 2, 2, INIT_VLC_USE_NEW_STATIC); + ff_vc1_intfr_4mv_mbmode_vlc[i] = + ff_vlc_init_tables(&state, VC1_INTFR_4MV_MBMODE_VLC_BITS, 15, + vc1_intfr_4mv_mbmode_bits[i], 1, 1, + vc1_intfr_4mv_mbmode_codes[i], 2, 2, 0); /* initialize NON-4MV MBMODE VLC tables for the same */ - ff_vc1_intfr_non4mv_mbmode_vlc[i].table = &vlc_table[vlc_offs[i * 3 + 38]]; - ff_vc1_intfr_non4mv_mbmode_vlc[i].table_allocated = vlc_offs[i * 3 + 39] - vlc_offs[i * 3 + 38]; - init_vlc(&ff_vc1_intfr_non4mv_mbmode_vlc[i], VC1_INTFR_NON4MV_MBMODE_VLC_BITS, 9, - vc1_intfr_non4mv_mbmode_bits[i], 1, 1, - vc1_intfr_non4mv_mbmode_codes[i], 1, 1, INIT_VLC_USE_NEW_STATIC); + ff_vc1_intfr_non4mv_mbmode_vlc[i] = + ff_vlc_init_tables(&state, VC1_INTFR_NON4MV_MBMODE_VLC_BITS, 9, + vc1_intfr_non4mv_mbmode_bits[i], 1, 1, + vc1_intfr_non4mv_mbmode_codes[i], 1, 1, 0); /* initialize interlaced MVDATA tables (1-Ref) */ - ff_vc1_1ref_mvdata_vlc[i].table = &vlc_table[vlc_offs[i * 3 + 39]]; - ff_vc1_1ref_mvdata_vlc[i].table_allocated = vlc_offs[i * 3 + 40] - vlc_offs[i * 3 + 39]; - init_vlc(&ff_vc1_1ref_mvdata_vlc[i], VC1_1REF_MVDATA_VLC_BITS, 72, - vc1_1ref_mvdata_bits[i], 1, 1, - vc1_1ref_mvdata_codes[i], 4, 4, INIT_VLC_USE_NEW_STATIC); - } - for (int i = 0; i < 4; i++) { + ff_vc1_1ref_mvdata_vlc[i] = + ff_vlc_init_tables(&state, VC1_1REF_MVDATA_VLC_BITS, 72, + vc1_1ref_mvdata_bits[i], 1, 1, + vc1_1ref_mvdata_codes[i], 4, 4, 0); /* Initialize 2MV Block pattern VLC tables */ - ff_vc1_2mv_block_pattern_vlc[i].table = &vlc_table[vlc_offs[i + 49]]; - ff_vc1_2mv_block_pattern_vlc[i].table_allocated = vlc_offs[i + 50] - vlc_offs[i + 49]; - init_vlc(&ff_vc1_2mv_block_pattern_vlc[i], VC1_2MV_BLOCK_PATTERN_VLC_BITS, 4, - vc1_2mv_block_pattern_bits[i], 1, 1, - vc1_2mv_block_pattern_codes[i], 1, 1, INIT_VLC_USE_NEW_STATIC); + ff_vc1_2mv_block_pattern_vlc[i] = + ff_vlc_init_tables(&state, VC1_2MV_BLOCK_PATTERN_VLC_BITS, 4, + vc1_2mv_block_pattern_bits[i], 1, 1, + vc1_2mv_block_pattern_codes[i], 1, 1, 0); } for (int i = 0; i < 8; i++) { + ff_vc1_ac_coeff_table[i] = + ff_vlc_init_tables(&state, AC_VLC_BITS, ff_vc1_ac_sizes[i], + &vc1_ac_tables[i][0][1], 8, 4, + &vc1_ac_tables[i][0][0], 8, 4, 0); + /* initialize interlaced MVDATA tables (2-Ref) */ + ff_vc1_2ref_mvdata_vlc[i] = + ff_vlc_init_tables(&state, VC1_2REF_MVDATA_VLC_BITS, 126, + vc1_2ref_mvdata_bits[i], 1, 1, + vc1_2ref_mvdata_codes[i], 4, 4, 0); /* Initialize interlaced CBPCY VLC tables (Table 124 - Table 131) */ - ff_vc1_icbpcy_vlc[i].table = &vlc_table[vlc_offs[i * 3 + 53]]; - ff_vc1_icbpcy_vlc[i].table_allocated = vlc_offs[i * 3 + 54] - vlc_offs[i * 3 + 53]; - init_vlc(&ff_vc1_icbpcy_vlc[i], VC1_ICBPCY_VLC_BITS, 63, - vc1_icbpcy_p_bits[i], 1, 1, - vc1_icbpcy_p_codes[i], 2, 2, INIT_VLC_USE_NEW_STATIC); + ff_vc1_icbpcy_vlc[i] = + ff_vlc_init_tables(&state, VC1_ICBPCY_VLC_BITS, 63, + vc1_icbpcy_p_bits[i], 1, 1, + vc1_icbpcy_p_codes[i], 2, 2, 0); /* Initialize interlaced field picture MBMODE VLC tables */ - ff_vc1_if_mmv_mbmode_vlc[i].table = &vlc_table[vlc_offs[i * 3 + 54]]; - ff_vc1_if_mmv_mbmode_vlc[i].table_allocated = vlc_offs[i * 3 + 55] - vlc_offs[i * 3 + 54]; - init_vlc(&ff_vc1_if_mmv_mbmode_vlc[i], VC1_IF_MMV_MBMODE_VLC_BITS, 8, - vc1_if_mmv_mbmode_bits[i], 1, 1, - vc1_if_mmv_mbmode_codes[i], 1, 1, INIT_VLC_USE_NEW_STATIC); - ff_vc1_if_1mv_mbmode_vlc[i].table = &vlc_table[vlc_offs[i * 3 + 55]]; - ff_vc1_if_1mv_mbmode_vlc[i].table_allocated = vlc_offs[i * 3 + 56] - vlc_offs[i * 3 + 55]; - init_vlc(&ff_vc1_if_1mv_mbmode_vlc[i], VC1_IF_1MV_MBMODE_VLC_BITS, 6, - vc1_if_1mv_mbmode_bits[i], 1, 1, - vc1_if_1mv_mbmode_codes[i], 1, 1, INIT_VLC_USE_NEW_STATIC); + ff_vc1_if_mmv_mbmode_vlc[i] = + ff_vlc_init_tables(&state, VC1_IF_MMV_MBMODE_VLC_BITS, 8, + vc1_if_mmv_mbmode_bits[i], 1, 1, + vc1_if_mmv_mbmode_codes[i], 1, 1, 0); + ff_vc1_if_1mv_mbmode_vlc[i] = + ff_vlc_init_tables(&state, VC1_IF_1MV_MBMODE_VLC_BITS, 6, + vc1_if_1mv_mbmode_bits[i], 1, 1, + vc1_if_1mv_mbmode_codes[i], 1, 1, 0); } ff_msmp4_vc1_vlcs_init_once(); } @@ -721,14 +719,6 @@ static av_cold int vc1_decode_init(AVCodecContext *avctx) if (v->profile == PROFILE_ADVANCED) avctx->level = v->level; - if (!CONFIG_GRAY || !(avctx->flags & AV_CODEC_FLAG_GRAY)) - avctx->pix_fmt = ff_get_format(avctx, avctx->codec->pix_fmts); - else { - avctx->pix_fmt = AV_PIX_FMT_GRAY8; - if (avctx->color_range == AVCOL_RANGE_UNSPECIFIED) - avctx->color_range = AVCOL_RANGE_MPEG; - } - ff_blockdsp_init(&s->bdsp); ff_h264chroma_init(&v->h264chroma, 8); @@ -836,6 +826,7 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, const uint8_t *rawbuf; int raw_size; } *slices = NULL, *tmp; + unsigned slices_allocated = 0; v->second_field = 0; @@ -859,6 +850,7 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, //for advanced profile we may need to parse and unescape data if (avctx->codec_id == AV_CODEC_ID_VC1 || avctx->codec_id == AV_CODEC_ID_VC1IMAGE) { int buf_size2 = 0; + size_t next_allocated = 0; buf2 = av_mallocz(buf_size + AV_INPUT_BUFFER_PADDING_SIZE); if (!buf2) return AVERROR(ENOMEM); @@ -874,15 +866,14 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, if (size <= 0) continue; switch (AV_RB32(start)) { case VC1_CODE_FRAME: - if (avctx->hwaccel) - buf_start = start; + buf_start = start; buf_size2 = v->vc1dsp.vc1_unescape_buffer(start + 4, size, buf2); break; case VC1_CODE_FIELD: { int buf_size3; - if (avctx->hwaccel) - buf_start_second_field = start; - tmp = av_realloc_array(slices, sizeof(*slices), n_slices+1); + buf_start_second_field = start; + av_size_mult(sizeof(*slices), n_slices+1, &next_allocated); + tmp = next_allocated ? av_fast_realloc(slices, &slices_allocated, next_allocated) : NULL; if (!tmp) { ret = AVERROR(ENOMEM); goto err; @@ -911,7 +902,8 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, break; case VC1_CODE_SLICE: { int buf_size3; - tmp = av_realloc_array(slices, sizeof(*slices), n_slices+1); + av_size_mult(sizeof(*slices), n_slices+1, &next_allocated); + tmp = next_allocated ? av_fast_realloc(slices, &slices_allocated, next_allocated) : NULL; if (!tmp) { ret = AVERROR(ENOMEM); goto err; @@ -944,9 +936,9 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, ret = AVERROR_INVALIDDATA; goto err; } else { // found field marker, unescape second field - if (avctx->hwaccel) - buf_start_second_field = divider; - tmp = av_realloc_array(slices, sizeof(*slices), n_slices+1); + buf_start_second_field = divider; + av_size_mult(sizeof(*slices), n_slices+1, &next_allocated); + tmp = next_allocated ? av_fast_realloc(slices, &slices_allocated, next_allocated) : NULL; if (!tmp) { ret = AVERROR(ENOMEM); goto err; @@ -1053,10 +1045,6 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, goto err; } - // for skipping the frame - s->current_picture.f->pict_type = s->pict_type; - s->current_picture.f->key_frame = s->pict_type == AV_PICTURE_TYPE_I; - /* skip B-frames if we don't have reference frames */ if (!s->last_picture_ptr && s->pict_type == AV_PICTURE_TYPE_B) { av_log(v->s.avctx, AV_LOG_DEBUG, "Skipping B frame without reference frames\n"); @@ -1073,13 +1061,12 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } v->s.current_picture_ptr->field_picture = v->field_mode; - v->s.current_picture_ptr->f->interlaced_frame = (v->fcm != PROGRESSIVE); - v->s.current_picture_ptr->f->top_field_first = v->tff; + v->s.current_picture_ptr->f->flags |= AV_FRAME_FLAG_INTERLACED * (v->fcm != PROGRESSIVE); + v->s.current_picture_ptr->f->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * !!v->tff; // process pulldown flags s->current_picture_ptr->f->repeat_pict = 0; // Pulldown flags are only valid when 'broadcast' has been set. - // So ticks_per_frame will be 2 if (v->rff) { // repeat field s->current_picture_ptr->f->repeat_pict = 1; @@ -1089,19 +1076,26 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } if (avctx->hwaccel) { + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); s->mb_y = 0; if (v->field_mode && buf_start_second_field) { // decode first field s->picture_structure = PICT_BOTTOM_FIELD - v->tff; - if ((ret = avctx->hwaccel->start_frame(avctx, buf_start, buf_start_second_field - buf_start)) < 0) + ret = hwaccel->start_frame(avctx, buf_start, + buf_start_second_field - buf_start); + if (ret < 0) goto err; if (n_slices1 == -1) { // no slices, decode the field as-is - if ((ret = avctx->hwaccel->decode_slice(avctx, buf_start, buf_start_second_field - buf_start)) < 0) + ret = hwaccel->decode_slice(avctx, buf_start, + buf_start_second_field - buf_start); + if (ret < 0) goto err; } else { - if ((ret = avctx->hwaccel->decode_slice(avctx, buf_start, slices[0].rawbuf - buf_start)) < 0) + ret = hwaccel->decode_slice(avctx, buf_start, + slices[0].rawbuf - buf_start); + if (ret < 0) goto err; for (i = 0 ; i < n_slices1 + 1; i++) { @@ -1119,12 +1113,14 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } } - if ((ret = avctx->hwaccel->decode_slice(avctx, slices[i].rawbuf, slices[i].raw_size)) < 0) + ret = hwaccel->decode_slice(avctx, slices[i].rawbuf, + slices[i].raw_size); + if (ret < 0) goto err; } } - if ((ret = avctx->hwaccel->end_frame(avctx)) < 0) + if ((ret = hwaccel->end_frame(avctx)) < 0) goto err; // decode second field @@ -1140,15 +1136,21 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } v->s.current_picture_ptr->f->pict_type = v->s.pict_type; - if ((ret = avctx->hwaccel->start_frame(avctx, buf_start_second_field, (buf + buf_size) - buf_start_second_field)) < 0) + ret = hwaccel->start_frame(avctx, buf_start_second_field, + (buf + buf_size) - buf_start_second_field); + if (ret < 0) goto err; if (n_slices - n_slices1 == 2) { // no slices, decode the field as-is - if ((ret = avctx->hwaccel->decode_slice(avctx, buf_start_second_field, (buf + buf_size) - buf_start_second_field)) < 0) + ret = hwaccel->decode_slice(avctx, buf_start_second_field, + (buf + buf_size) - buf_start_second_field); + if (ret < 0) goto err; } else { - if ((ret = avctx->hwaccel->decode_slice(avctx, buf_start_second_field, slices[n_slices1 + 2].rawbuf - buf_start_second_field)) < 0) + ret = hwaccel->decode_slice(avctx, buf_start_second_field, + slices[n_slices1 + 2].rawbuf - buf_start_second_field); + if (ret < 0) goto err; for (i = n_slices1 + 2; i < n_slices; i++) { @@ -1166,25 +1168,33 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } } - if ((ret = avctx->hwaccel->decode_slice(avctx, slices[i].rawbuf, slices[i].raw_size)) < 0) + ret = hwaccel->decode_slice(avctx, slices[i].rawbuf, + slices[i].raw_size); + if (ret < 0) goto err; } } - if ((ret = avctx->hwaccel->end_frame(avctx)) < 0) + if ((ret = hwaccel->end_frame(avctx)) < 0) goto err; } else { s->picture_structure = PICT_FRAME; - if ((ret = avctx->hwaccel->start_frame(avctx, buf_start, (buf + buf_size) - buf_start)) < 0) + ret = hwaccel->start_frame(avctx, buf_start, + (buf + buf_size) - buf_start); + if (ret < 0) goto err; if (n_slices == 0) { // no slices, decode the frame as-is - if ((ret = avctx->hwaccel->decode_slice(avctx, buf_start, (buf + buf_size) - buf_start)) < 0) + ret = hwaccel->decode_slice(avctx, buf_start, + (buf + buf_size) - buf_start); + if (ret < 0) goto err; } else { // decode the frame part as the first slice - if ((ret = avctx->hwaccel->decode_slice(avctx, buf_start, slices[0].rawbuf - buf_start)) < 0) + ret = hwaccel->decode_slice(avctx, buf_start, + slices[0].rawbuf - buf_start); + if (ret < 0) goto err; // and process the slices as additional slices afterwards @@ -1203,11 +1213,13 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } } - if ((ret = avctx->hwaccel->decode_slice(avctx, slices[i].rawbuf, slices[i].raw_size)) < 0) + ret = hwaccel->decode_slice(avctx, slices[i].rawbuf, + slices[i].raw_size); + if (ret < 0) goto err; } } - if ((ret = avctx->hwaccel->end_frame(avctx)) < 0) + if ((ret = hwaccel->end_frame(avctx)) < 0) goto err; } } else { @@ -1315,7 +1327,7 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, if ( !v->field_mode && avctx->codec_id != AV_CODEC_ID_WMV3IMAGE && avctx->codec_id != AV_CODEC_ID_VC1IMAGE) - ff_er_frame_end(&s->er); + ff_er_frame_end(&s->er, NULL); } ff_mpv_frame_end(s); @@ -1370,27 +1382,6 @@ static int vc1_decode_frame(AVCodecContext *avctx, AVFrame *pict, } -static const enum AVPixelFormat vc1_hwaccel_pixfmt_list_420[] = { -#if CONFIG_VC1_DXVA2_HWACCEL - AV_PIX_FMT_DXVA2_VLD, -#endif -#if CONFIG_VC1_D3D11VA_HWACCEL - AV_PIX_FMT_D3D11VA_VLD, - AV_PIX_FMT_D3D11, -#endif -#if CONFIG_VC1_NVDEC_HWACCEL - AV_PIX_FMT_CUDA, -#endif -#if CONFIG_VC1_VAAPI_HWACCEL - AV_PIX_FMT_VAAPI, -#endif -#if CONFIG_VC1_VDPAU_HWACCEL - AV_PIX_FMT_VDPAU, -#endif - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE -}; - const FFCodec ff_vc1_decoder = { .p.name = "vc1", CODEC_LONG_NAME("SMPTE VC-1"), @@ -1402,7 +1393,6 @@ const FFCodec ff_vc1_decoder = { FF_CODEC_DECODE_CB(vc1_decode_frame), .flush = ff_mpeg_flush, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, - .p.pix_fmts = vc1_hwaccel_pixfmt_list_420, .hw_configs = (const AVCodecHWConfigInternal *const []) { #if CONFIG_VC1_DXVA2_HWACCEL HWACCEL_DXVA2(vc1), @@ -1413,6 +1403,9 @@ const FFCodec ff_vc1_decoder = { #if CONFIG_VC1_D3D11VA2_HWACCEL HWACCEL_D3D11VA2(vc1), #endif +#if CONFIG_VC1_D3D12VA_HWACCEL + HWACCEL_D3D12VA(vc1), +#endif #if CONFIG_VC1_NVDEC_HWACCEL HWACCEL_NVDEC(vc1), #endif @@ -1439,7 +1432,6 @@ const FFCodec ff_wmv3_decoder = { FF_CODEC_DECODE_CB(vc1_decode_frame), .flush = ff_mpeg_flush, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, - .p.pix_fmts = vc1_hwaccel_pixfmt_list_420, .hw_configs = (const AVCodecHWConfigInternal *const []) { #if CONFIG_WMV3_DXVA2_HWACCEL HWACCEL_DXVA2(wmv3), @@ -1450,6 +1442,9 @@ const FFCodec ff_wmv3_decoder = { #if CONFIG_WMV3_D3D11VA2_HWACCEL HWACCEL_D3D11VA2(wmv3), #endif +#if CONFIG_WMV3_D3D12VA_HWACCEL + HWACCEL_D3D12VA(wmv3), +#endif #if CONFIG_WMV3_NVDEC_HWACCEL HWACCEL_NVDEC(wmv3), #endif @@ -1477,10 +1472,6 @@ const FFCodec ff_wmv3image_decoder = { FF_CODEC_DECODE_CB(vc1_decode_frame), .p.capabilities = AV_CODEC_CAP_DR1, .flush = vc1_sprite_flush, - .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE - }, }; #endif @@ -1496,9 +1487,5 @@ const FFCodec ff_vc1image_decoder = { FF_CODEC_DECODE_CB(vc1_decode_frame), .p.capabilities = AV_CODEC_CAP_DR1, .flush = vc1_sprite_flush, - .p.pix_fmts = (const enum AVPixelFormat[]) { - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE - }, }; #endif diff --git a/libavcodec/vc1dsp.c b/libavcodec/vc1dsp.c index 62c8eb21fa0..2caa3c68636 100644 --- a/libavcodec/vc1dsp.c +++ b/libavcodec/vc1dsp.c @@ -1039,6 +1039,8 @@ av_cold void ff_vc1dsp_init(VC1DSPContext *dsp) ff_vc1dsp_init_arm(dsp); #elif ARCH_PPC ff_vc1dsp_init_ppc(dsp); +#elif ARCH_RISCV + ff_vc1dsp_init_riscv(dsp); #elif ARCH_X86 ff_vc1dsp_init_x86(dsp); #elif ARCH_MIPS diff --git a/libavcodec/vc1dsp.h b/libavcodec/vc1dsp.h index 7ed1776ca7b..e3b90d2b626 100644 --- a/libavcodec/vc1dsp.h +++ b/libavcodec/vc1dsp.h @@ -89,6 +89,7 @@ void ff_vc1dsp_init(VC1DSPContext* c); void ff_vc1dsp_init_aarch64(VC1DSPContext* dsp); void ff_vc1dsp_init_arm(VC1DSPContext* dsp); void ff_vc1dsp_init_ppc(VC1DSPContext *c); +void ff_vc1dsp_init_riscv(VC1DSPContext *c); void ff_vc1dsp_init_x86(VC1DSPContext* dsp); void ff_vc1dsp_init_mips(VC1DSPContext* dsp); void ff_vc1dsp_init_loongarch(VC1DSPContext* dsp); diff --git a/libavcodec/vc2enc.c b/libavcodec/vc2enc.c index d978c67a3b0..349174c8c73 100644 --- a/libavcodec/vc2enc.c +++ b/libavcodec/vc2enc.c @@ -185,7 +185,9 @@ typedef struct VC2EncContext { static av_always_inline void put_vc2_ue_uint(PutBitContext *pb, uint32_t val) { int i; - int pbits = 0, bits = 0, topbit = 1, maxval = 1; + int bits = 0; + unsigned topbit = 1, maxval = 1; + uint64_t pbits = 0; if (!val++) { put_bits(pb, 1, 1); @@ -202,12 +204,13 @@ static av_always_inline void put_vc2_ue_uint(PutBitContext *pb, uint32_t val) for (i = 0; i < bits; i++) { topbit >>= 1; + av_assert2(pbits <= UINT64_MAX>>3); pbits <<= 2; if (val & topbit) pbits |= 0x1; } - put_bits(pb, bits*2 + 1, (pbits << 1) | 1); + put_bits64(pb, bits*2 + 1, (pbits << 1) | 1); } static av_always_inline int count_vc2_ue_uint(uint32_t val) @@ -1187,19 +1190,19 @@ static av_cold int vc2_encode_init(AVCodecContext *avctx) #define VC2ENC_FLAGS (AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) static const AVOption vc2enc_options[] = { - {"tolerance", "Max undershoot in percent", offsetof(VC2EncContext, tolerance), AV_OPT_TYPE_DOUBLE, {.dbl = 5.0f}, 0.0f, 45.0f, VC2ENC_FLAGS, "tolerance"}, - {"slice_width", "Slice width", offsetof(VC2EncContext, slice_width), AV_OPT_TYPE_INT, {.i64 = 32}, 32, 1024, VC2ENC_FLAGS, "slice_width"}, - {"slice_height", "Slice height", offsetof(VC2EncContext, slice_height), AV_OPT_TYPE_INT, {.i64 = 16}, 8, 1024, VC2ENC_FLAGS, "slice_height"}, - {"wavelet_depth", "Transform depth", offsetof(VC2EncContext, wavelet_depth), AV_OPT_TYPE_INT, {.i64 = 4}, 1, 5, VC2ENC_FLAGS, "wavelet_depth"}, - {"wavelet_type", "Transform type", offsetof(VC2EncContext, wavelet_idx), AV_OPT_TYPE_INT, {.i64 = VC2_TRANSFORM_9_7}, 0, VC2_TRANSFORMS_NB, VC2ENC_FLAGS, "wavelet_idx"}, - {"9_7", "Deslauriers-Dubuc (9,7)", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_TRANSFORM_9_7}, INT_MIN, INT_MAX, VC2ENC_FLAGS, "wavelet_idx"}, - {"5_3", "LeGall (5,3)", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_TRANSFORM_5_3}, INT_MIN, INT_MAX, VC2ENC_FLAGS, "wavelet_idx"}, - {"haar", "Haar (with shift)", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_TRANSFORM_HAAR_S}, INT_MIN, INT_MAX, VC2ENC_FLAGS, "wavelet_idx"}, - {"haar_noshift", "Haar (without shift)", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_TRANSFORM_HAAR}, INT_MIN, INT_MAX, VC2ENC_FLAGS, "wavelet_idx"}, - {"qm", "Custom quantization matrix", offsetof(VC2EncContext, quant_matrix), AV_OPT_TYPE_INT, {.i64 = VC2_QM_DEF}, 0, VC2_QM_NB, VC2ENC_FLAGS, "quant_matrix"}, - {"default", "Default from the specifications", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_QM_DEF}, INT_MIN, INT_MAX, VC2ENC_FLAGS, "quant_matrix"}, - {"color", "Prevents low bitrate discoloration", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_QM_COL}, INT_MIN, INT_MAX, VC2ENC_FLAGS, "quant_matrix"}, - {"flat", "Optimize for PSNR", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_QM_FLAT}, INT_MIN, INT_MAX, VC2ENC_FLAGS, "quant_matrix"}, + {"tolerance", "Max undershoot in percent", offsetof(VC2EncContext, tolerance), AV_OPT_TYPE_DOUBLE, {.dbl = 5.0f}, 0.0f, 45.0f, VC2ENC_FLAGS, .unit = "tolerance"}, + {"slice_width", "Slice width", offsetof(VC2EncContext, slice_width), AV_OPT_TYPE_INT, {.i64 = 32}, 32, 1024, VC2ENC_FLAGS, .unit = "slice_width"}, + {"slice_height", "Slice height", offsetof(VC2EncContext, slice_height), AV_OPT_TYPE_INT, {.i64 = 16}, 8, 1024, VC2ENC_FLAGS, .unit = "slice_height"}, + {"wavelet_depth", "Transform depth", offsetof(VC2EncContext, wavelet_depth), AV_OPT_TYPE_INT, {.i64 = 4}, 1, 5, VC2ENC_FLAGS, .unit = "wavelet_depth"}, + {"wavelet_type", "Transform type", offsetof(VC2EncContext, wavelet_idx), AV_OPT_TYPE_INT, {.i64 = VC2_TRANSFORM_9_7}, 0, VC2_TRANSFORMS_NB, VC2ENC_FLAGS, .unit = "wavelet_idx"}, + {"9_7", "Deslauriers-Dubuc (9,7)", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_TRANSFORM_9_7}, INT_MIN, INT_MAX, VC2ENC_FLAGS, .unit = "wavelet_idx"}, + {"5_3", "LeGall (5,3)", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_TRANSFORM_5_3}, INT_MIN, INT_MAX, VC2ENC_FLAGS, .unit = "wavelet_idx"}, + {"haar", "Haar (with shift)", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_TRANSFORM_HAAR_S}, INT_MIN, INT_MAX, VC2ENC_FLAGS, .unit = "wavelet_idx"}, + {"haar_noshift", "Haar (without shift)", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_TRANSFORM_HAAR}, INT_MIN, INT_MAX, VC2ENC_FLAGS, .unit = "wavelet_idx"}, + {"qm", "Custom quantization matrix", offsetof(VC2EncContext, quant_matrix), AV_OPT_TYPE_INT, {.i64 = VC2_QM_DEF}, 0, VC2_QM_NB, VC2ENC_FLAGS, .unit = "quant_matrix"}, + {"default", "Default from the specifications", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_QM_DEF}, INT_MIN, INT_MAX, VC2ENC_FLAGS, .unit = "quant_matrix"}, + {"color", "Prevents low bitrate discoloration", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_QM_COL}, INT_MIN, INT_MAX, VC2ENC_FLAGS, .unit = "quant_matrix"}, + {"flat", "Optimize for PSNR", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_QM_FLAT}, INT_MIN, INT_MAX, VC2ENC_FLAGS, .unit = "quant_matrix"}, {NULL} }; diff --git a/libavcodec/vcr1.c b/libavcodec/vcr1.c index 853d4459a80..771337e2624 100644 --- a/libavcodec/vcr1.c +++ b/libavcodec/vcr1.c @@ -63,7 +63,7 @@ static int vcr1_decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; for (i = 0; i < 16; i++) { a->delta[i] = *bytestream++; diff --git a/libavcodec/vdpau.c b/libavcodec/vdpau.c index 0bb793c0104..538c57f9e84 100644 --- a/libavcodec/vdpau.c +++ b/libavcodec/vdpau.c @@ -27,15 +27,13 @@ #include "avcodec.h" #include "decode.h" +#include "hwaccel_internal.h" #include "internal.h" #include "mpegvideodec.h" #include "vc1.h" #include "vdpau.h" #include "vdpau_internal.h" -// XXX: at the time of adding this ifdefery, av_assert* wasn't use outside. -// When dropping it, make sure other av_assert* were not added since then. - /** * @addtogroup VDPAU_Decoding * @@ -66,15 +64,19 @@ static int vdpau_error(VdpStatus status) } } +#if FF_API_VDPAU_ALLOC_GET_SET AVVDPAUContext *av_alloc_vdpaucontext(void) { +FF_DISABLE_DEPRECATION_WARNINGS return av_vdpau_alloc_context(); +FF_ENABLE_DEPRECATION_WARNINGS } #define MAKE_ACCESSORS(str, name, type, field) \ type av_##name##_get_##field(const str *s) { return s->field; } \ void av_##name##_set_##field(str *s, type v) { s->field = v; } MAKE_ACCESSORS(AVVDPAUContext, vdpau_hwaccel, AVVDPAU_Render2, render2) +#endif int av_vdpau_get_surface_parameters(AVCodecContext *avctx, VdpChromaType *type, @@ -324,8 +326,8 @@ static int ff_vdpau_common_reinit(AVCodecContext *avctx) avctx->coded_height == vdctx->height && (!hwctx || !hwctx->reset)) return 0; - avctx->hwaccel->uninit(avctx); - return avctx->hwaccel->init(avctx); + FF_HW_SIMPLE_CALL(avctx, uninit); + return FF_HW_SIMPLE_CALL(avctx, init); } int ff_vdpau_common_start_frame(struct vdpau_picture_context *pic_ctx, @@ -402,10 +404,12 @@ int ff_vdpau_add_buffer(struct vdpau_picture_context *pic_ctx, return 0; } +#if FF_API_VDPAU_ALLOC_GET_SET AVVDPAUContext *av_vdpau_alloc_context(void) { return av_mallocz(sizeof(VDPAUHWContext)); } +#endif int av_vdpau_bind_context(AVCodecContext *avctx, VdpDevice device, VdpGetProcAddress *get_proc, unsigned flags) diff --git a/libavcodec/vdpau.h b/libavcodec/vdpau.h index 35c4b1096ba..8021c257615 100644 --- a/libavcodec/vdpau.h +++ b/libavcodec/vdpau.h @@ -66,16 +66,14 @@ typedef int (*AVVDPAU_Render2)(struct AVCodecContext *, struct AVFrame *, /** * This structure is used to share data between the libavcodec library and * the client video application. - * The user shall allocate the structure via the av_alloc_vdpau_hwaccel - * function and make it available as - * AVCodecContext.hwaccel_context. Members can be set by the user once + * This structure will be allocated and stored in AVCodecContext.hwaccel_context + * by av_vdpau_bind_context(). Members can be set by the user once * during initialization or through each AVCodecContext.get_buffer() * function call. In any case, they must be valid prior to calling * decoding functions. * * The size of this structure is not a part of the public ABI and must not - * be used outside of libavcodec. Use av_vdpau_alloc_context() to allocate an - * AVVDPAUContext. + * be used outside of libavcodec. */ typedef struct AVVDPAUContext { /** @@ -95,15 +93,27 @@ typedef struct AVVDPAUContext { AVVDPAU_Render2 render2; } AVVDPAUContext; +#if FF_API_VDPAU_ALLOC_GET_SET /** * @brief allocation function for AVVDPAUContext * * Allows extending the struct without breaking API/ABI + * @deprecated use av_vdpau_bind_context() instead */ +attribute_deprecated AVVDPAUContext *av_alloc_vdpaucontext(void); +/** + * @deprecated render2 is public and can be accessed directly + */ +attribute_deprecated AVVDPAU_Render2 av_vdpau_hwaccel_get_render2(const AVVDPAUContext *); +/** + * @deprecated render2 is public and can be accessed directly + */ +attribute_deprecated void av_vdpau_hwaccel_set_render2(AVVDPAUContext *, AVVDPAU_Render2); +#endif /** * Associate a VDPAU device with a codec context for hardware acceleration. @@ -145,12 +155,16 @@ int av_vdpau_bind_context(AVCodecContext *avctx, VdpDevice device, int av_vdpau_get_surface_parameters(AVCodecContext *avctx, VdpChromaType *type, uint32_t *width, uint32_t *height); +#if FF_API_VDPAU_ALLOC_GET_SET /** * Allocate an AVVDPAUContext. * * @return Newly-allocated AVVDPAUContext or NULL on failure. + * @deprecated use av_vdpau_bind_context() instead */ +attribute_deprecated AVVDPAUContext *av_vdpau_alloc_context(void); +#endif /** @} */ diff --git a/libavcodec/vdpau_av1.c b/libavcodec/vdpau_av1.c index 3c3c8e61d10..80923092b9b 100644 --- a/libavcodec/vdpau_av1.c +++ b/libavcodec/vdpau_av1.c @@ -25,7 +25,7 @@ #include "avcodec.h" #include "internal.h" #include "av1dec.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vdpau.h" #include "vdpau_internal.h" @@ -337,13 +337,13 @@ static int vdpau_av1_init(AVCodecContext *avctx) uint32_t level = avctx->level; switch (avctx->profile) { - case FF_PROFILE_AV1_MAIN: + case AV_PROFILE_AV1_MAIN: profile = VDP_DECODER_PROFILE_AV1_MAIN; break; - case FF_PROFILE_AV1_HIGH: + case AV_PROFILE_AV1_HIGH: profile = VDP_DECODER_PROFILE_AV1_HIGH; break; - case FF_PROFILE_AV1_PROFESSIONAL: + case AV_PROFILE_AV1_PROFESSIONAL: profile = VDP_DECODER_PROFILE_AV1_PROFESSIONAL; break; default: @@ -353,11 +353,11 @@ static int vdpau_av1_init(AVCodecContext *avctx) return ff_vdpau_common_init(avctx, profile, level); } -const AVHWAccel ff_av1_vdpau_hwaccel = { - .name = "av1_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_AV1, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_av1_vdpau_hwaccel = { + .p.name = "av1_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_av1_start_frame, .end_frame = vdpau_av1_end_frame, .decode_slice = vdpau_av1_decode_slice, diff --git a/libavcodec/vdpau_h264.c b/libavcodec/vdpau_h264.c index 525e2084958..9c08e4048c6 100644 --- a/libavcodec/vdpau_h264.c +++ b/libavcodec/vdpau_h264.c @@ -26,7 +26,7 @@ #include "avcodec.h" #include "h264dec.h" #include "h264_ps.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "mpegutils.h" #include "vdpau.h" #include "vdpau_internal.h" @@ -219,35 +219,35 @@ static int vdpau_h264_init(AVCodecContext *avctx) VdpDecoderProfile profile; uint32_t level = avctx->level; - switch (avctx->profile & ~FF_PROFILE_H264_INTRA) { - case FF_PROFILE_H264_BASELINE: + switch (avctx->profile & ~AV_PROFILE_H264_INTRA) { + case AV_PROFILE_H264_BASELINE: profile = VDP_DECODER_PROFILE_H264_BASELINE; break; - case FF_PROFILE_H264_CONSTRAINED_BASELINE: + case AV_PROFILE_H264_CONSTRAINED_BASELINE: #ifdef VDP_DECODER_PROFILE_H264_CONSTRAINED_BASELINE profile = VDP_DECODER_PROFILE_H264_CONSTRAINED_BASELINE; break; #endif - case FF_PROFILE_H264_MAIN: + case AV_PROFILE_H264_MAIN: profile = VDP_DECODER_PROFILE_H264_MAIN; break; - case FF_PROFILE_H264_HIGH: + case AV_PROFILE_H264_HIGH: profile = VDP_DECODER_PROFILE_H264_HIGH; break; #ifdef VDP_DECODER_PROFILE_H264_EXTENDED - case FF_PROFILE_H264_EXTENDED: + case AV_PROFILE_H264_EXTENDED: profile = VDP_DECODER_PROFILE_H264_EXTENDED; break; #endif - case FF_PROFILE_H264_HIGH_10: + case AV_PROFILE_H264_HIGH_10: /* XXX: High 10 can be treated as High so long as only 8 bits per * format are supported. */ profile = VDP_DECODER_PROFILE_H264_HIGH; break; #ifdef VDP_DECODER_PROFILE_H264_HIGH_444_PREDICTIVE - case FF_PROFILE_H264_HIGH_422: - case FF_PROFILE_H264_HIGH_444_PREDICTIVE: - case FF_PROFILE_H264_CAVLC_444: + case AV_PROFILE_H264_HIGH_422: + case AV_PROFILE_H264_HIGH_444_PREDICTIVE: + case AV_PROFILE_H264_CAVLC_444: profile = VDP_DECODER_PROFILE_H264_HIGH_444_PREDICTIVE; break; #endif @@ -255,17 +255,17 @@ static int vdpau_h264_init(AVCodecContext *avctx) return AVERROR(ENOTSUP); } - if ((avctx->profile & FF_PROFILE_H264_INTRA) && avctx->level == 11) + if ((avctx->profile & AV_PROFILE_H264_INTRA) && avctx->level == 11) level = VDP_DECODER_LEVEL_H264_1b; return ff_vdpau_common_init(avctx, profile, level); } -const AVHWAccel ff_h264_vdpau_hwaccel = { - .name = "h264_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_h264_vdpau_hwaccel = { + .p.name = "h264_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_h264_start_frame, .end_frame = vdpau_h264_end_frame, .decode_slice = vdpau_h264_decode_slice, diff --git a/libavcodec/vdpau_hevc.c b/libavcodec/vdpau_hevc.c index 2669040f781..4cd7ce5621e 100644 --- a/libavcodec/vdpau_hevc.c +++ b/libavcodec/vdpau_hevc.c @@ -25,7 +25,7 @@ #include "avcodec.h" #include "hevc_data.h" #include "hevcdec.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vdpau.h" #include "vdpau_internal.h" #include "h265_profile_level.h" @@ -519,16 +519,16 @@ static int vdpau_hevc_init(AVCodecContext *avctx) int ret; switch (avctx->profile) { - case FF_PROFILE_HEVC_MAIN: + case AV_PROFILE_HEVC_MAIN: profile = VDP_DECODER_PROFILE_HEVC_MAIN; break; - case FF_PROFILE_HEVC_MAIN_10: + case AV_PROFILE_HEVC_MAIN_10: profile = VDP_DECODER_PROFILE_HEVC_MAIN_10; break; - case FF_PROFILE_HEVC_MAIN_STILL_PICTURE: + case AV_PROFILE_HEVC_MAIN_STILL_PICTURE: profile = VDP_DECODER_PROFILE_HEVC_MAIN_STILL; break; - case FF_PROFILE_HEVC_REXT: + case AV_PROFILE_HEVC_REXT: ret = vdpau_hevc_parse_rext_profile(avctx, &profile); if (ret) return AVERROR(ENOTSUP); @@ -540,11 +540,11 @@ static int vdpau_hevc_init(AVCodecContext *avctx) return ff_vdpau_common_init(avctx, profile, level); } -const AVHWAccel ff_hevc_vdpau_hwaccel = { - .name = "hevc_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_hevc_vdpau_hwaccel = { + .p.name = "hevc_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_hevc_start_frame, .end_frame = vdpau_hevc_end_frame, .decode_slice = vdpau_hevc_decode_slice, diff --git a/libavcodec/vdpau_mpeg12.c b/libavcodec/vdpau_mpeg12.c index 79007aa1a8f..bbf76eb4693 100644 --- a/libavcodec/vdpau_mpeg12.c +++ b/libavcodec/vdpau_mpeg12.c @@ -26,7 +26,7 @@ #include #include "avcodec.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "mpegvideo.h" #include "vdpau.h" #include "vdpau_internal.h" @@ -106,11 +106,11 @@ static int vdpau_mpeg1_init(AVCodecContext *avctx) VDP_DECODER_LEVEL_MPEG1_NA); } -const AVHWAccel ff_mpeg1_vdpau_hwaccel = { - .name = "mpeg1_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG1VIDEO, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_mpeg1_vdpau_hwaccel = { + .p.name = "mpeg1_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG1VIDEO, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_mpeg_start_frame, .end_frame = ff_vdpau_mpeg_end_frame, .decode_slice = vdpau_mpeg_decode_slice, @@ -128,10 +128,10 @@ static int vdpau_mpeg2_init(AVCodecContext *avctx) VdpDecoderProfile profile; switch (avctx->profile) { - case FF_PROFILE_MPEG2_MAIN: + case AV_PROFILE_MPEG2_MAIN: profile = VDP_DECODER_PROFILE_MPEG2_MAIN; break; - case FF_PROFILE_MPEG2_SIMPLE: + case AV_PROFILE_MPEG2_SIMPLE: profile = VDP_DECODER_PROFILE_MPEG2_SIMPLE; break; default: @@ -141,11 +141,11 @@ static int vdpau_mpeg2_init(AVCodecContext *avctx) return ff_vdpau_common_init(avctx, profile, VDP_DECODER_LEVEL_MPEG2_HL); } -const AVHWAccel ff_mpeg2_vdpau_hwaccel = { - .name = "mpeg2_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_mpeg2_vdpau_hwaccel = { + .p.name = "mpeg2_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_mpeg_start_frame, .end_frame = ff_vdpau_mpeg_end_frame, .decode_slice = vdpau_mpeg_decode_slice, diff --git a/libavcodec/vdpau_mpeg4.c b/libavcodec/vdpau_mpeg4.c index 1211b1df2c7..055426b95bd 100644 --- a/libavcodec/vdpau_mpeg4.c +++ b/libavcodec/vdpau_mpeg4.c @@ -24,7 +24,7 @@ #include #include "avcodec.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "mpeg4videodec.h" #include "vdpau.h" #include "vdpau_internal.h" @@ -95,13 +95,13 @@ static int vdpau_mpeg4_init(AVCodecContext *avctx) VdpDecoderProfile profile; switch (avctx->profile) { - case FF_PROFILE_MPEG4_SIMPLE: + case AV_PROFILE_MPEG4_SIMPLE: profile = VDP_DECODER_PROFILE_MPEG4_PART2_SP; break; // As any ASP decoder must be able to decode SP, this // should be a safe fallback if profile is unknown/unspecified. - case FF_PROFILE_UNKNOWN: - case FF_PROFILE_MPEG4_ADVANCED_SIMPLE: + case AV_PROFILE_UNKNOWN: + case AV_PROFILE_MPEG4_ADVANCED_SIMPLE: profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP; break; default: @@ -111,11 +111,11 @@ static int vdpau_mpeg4_init(AVCodecContext *avctx) return ff_vdpau_common_init(avctx, profile, avctx->level); } -const AVHWAccel ff_mpeg4_vdpau_hwaccel = { - .name = "mpeg4_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG4, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_mpeg4_vdpau_hwaccel = { + .p.name = "mpeg4_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG4, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_mpeg4_start_frame, .end_frame = ff_vdpau_mpeg_end_frame, .decode_slice = vdpau_mpeg4_decode_slice, diff --git a/libavcodec/vdpau_vc1.c b/libavcodec/vdpau_vc1.c index 00b33f420b0..0eacc4477dc 100644 --- a/libavcodec/vdpau_vc1.c +++ b/libavcodec/vdpau_vc1.c @@ -26,7 +26,7 @@ #include #include "avcodec.h" -#include "hwconfig.h" +#include "hwaccel_internal.h" #include "vc1.h" #include "vdpau.h" #include "vdpau_internal.h" @@ -48,16 +48,16 @@ static int vdpau_vc1_start_frame(AVCodecContext *avctx, switch (s->pict_type) { case AV_PICTURE_TYPE_B: if (s->next_picture_ptr) { - ref = ff_vdpau_get_surface_id(s->next_picture.f); - assert(ref != VDP_INVALID_HANDLE); - info->backward_reference = ref; + ref = ff_vdpau_get_surface_id(s->next_picture.f); + assert(ref != VDP_INVALID_HANDLE); + info->backward_reference = ref; } /* fall-through */ case AV_PICTURE_TYPE_P: if (s->last_picture_ptr) { - ref = ff_vdpau_get_surface_id(s->last_picture.f); - assert(ref != VDP_INVALID_HANDLE); - info->forward_reference = ref; + ref = ff_vdpau_get_surface_id(s->last_picture.f); + assert(ref != VDP_INVALID_HANDLE); + info->forward_reference = ref; } } @@ -121,13 +121,13 @@ static int vdpau_vc1_init(AVCodecContext *avctx) VdpDecoderProfile profile; switch (avctx->profile) { - case FF_PROFILE_VC1_SIMPLE: + case AV_PROFILE_VC1_SIMPLE: profile = VDP_DECODER_PROFILE_VC1_SIMPLE; break; - case FF_PROFILE_VC1_MAIN: + case AV_PROFILE_VC1_MAIN: profile = VDP_DECODER_PROFILE_VC1_MAIN; break; - case FF_PROFILE_VC1_ADVANCED: + case AV_PROFILE_VC1_ADVANCED: profile = VDP_DECODER_PROFILE_VC1_ADVANCED; break; default: @@ -138,11 +138,11 @@ static int vdpau_vc1_init(AVCodecContext *avctx) } #if CONFIG_WMV3_VDPAU_HWACCEL -const AVHWAccel ff_wmv3_vdpau_hwaccel = { - .name = "wm3_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_WMV3, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_wmv3_vdpau_hwaccel = { + .p.name = "wm3_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_WMV3, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_vc1_start_frame, .end_frame = ff_vdpau_mpeg_end_frame, .decode_slice = vdpau_vc1_decode_slice, @@ -155,11 +155,11 @@ const AVHWAccel ff_wmv3_vdpau_hwaccel = { }; #endif -const AVHWAccel ff_vc1_vdpau_hwaccel = { - .name = "vc1_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VC1, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_vc1_vdpau_hwaccel = { + .p.name = "vc1_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VC1, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_vc1_start_frame, .end_frame = ff_vdpau_mpeg_end_frame, .decode_slice = vdpau_vc1_decode_slice, diff --git a/libavcodec/vdpau_vp9.c b/libavcodec/vdpau_vp9.c index 49fe18189be..424e2e60fbd 100644 --- a/libavcodec/vdpau_vp9.c +++ b/libavcodec/vdpau_vp9.c @@ -23,9 +23,8 @@ #include #include "libavutil/pixdesc.h" #include "avcodec.h" -#include "vp9data.h" +#include "hwaccel_internal.h" #include "vp9dec.h" -#include "hwconfig.h" #include "vdpau.h" #include "vdpau_internal.h" @@ -204,16 +203,16 @@ static int vdpau_vp9_init(AVCodecContext *avctx) uint32_t level = avctx->level; switch (avctx->profile) { - case FF_PROFILE_VP9_0: + case AV_PROFILE_VP9_0: profile = VDP_DECODER_PROFILE_VP9_PROFILE_0; break; - case FF_PROFILE_VP9_1: + case AV_PROFILE_VP9_1: profile = VDP_DECODER_PROFILE_VP9_PROFILE_1; break; - case FF_PROFILE_VP9_2: + case AV_PROFILE_VP9_2: profile = VDP_DECODER_PROFILE_VP9_PROFILE_2; break; - case FF_PROFILE_VP9_3: + case AV_PROFILE_VP9_3: profile = VDP_DECODER_PROFILE_VP9_PROFILE_3; break; default: @@ -223,11 +222,11 @@ static int vdpau_vp9_init(AVCodecContext *avctx) return ff_vdpau_common_init(avctx, profile, level); } -const AVHWAccel ff_vp9_vdpau_hwaccel = { - .name = "vp9_vdpau", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_VDPAU, +const FFHWAccel ff_vp9_vdpau_hwaccel = { + .p.name = "vp9_vdpau", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_VDPAU, .start_frame = vdpau_vp9_start_frame, .end_frame = vdpau_vp9_end_frame, .decode_slice = vdpau_vp9_decode_slice, diff --git a/libavcodec/version_major.h b/libavcodec/version_major.h index c2f118b2620..ab1f4511b4d 100644 --- a/libavcodec/version_major.h +++ b/libavcodec/version_major.h @@ -25,7 +25,7 @@ * Libavcodec version macros. */ -#define LIBAVCODEC_VERSION_MAJOR 60 +#define LIBAVCODEC_VERSION_MAJOR 61 /** * FF_API_* defines may be placed below to indicate public API that will be @@ -37,16 +37,15 @@ * at once through the bump. This improves the git bisect-ability of the change. */ -#define FF_API_INIT_PACKET (LIBAVCODEC_VERSION_MAJOR < 61) -#define FF_API_IDCT_NONE (LIBAVCODEC_VERSION_MAJOR < 61) -#define FF_API_SVTAV1_OPTS (LIBAVCODEC_VERSION_MAJOR < 61) -#define FF_API_AYUV_CODECID (LIBAVCODEC_VERSION_MAJOR < 61) -#define FF_API_VT_OUTPUT_CALLBACK (LIBAVCODEC_VERSION_MAJOR < 61) -#define FF_API_AVCODEC_CHROMA_POS (LIBAVCODEC_VERSION_MAJOR < 61) -#define FF_API_VT_HWACCEL_CONTEXT (LIBAVCODEC_VERSION_MAJOR < 61) -#define FF_API_AVCTX_FRAME_NUMBER (LIBAVCODEC_VERSION_MAJOR < 61) +#define FF_API_INIT_PACKET (LIBAVCODEC_VERSION_MAJOR < 62) +#define FF_API_SUBFRAMES (LIBAVCODEC_VERSION_MAJOR < 62) +#define FF_API_TICKS_PER_FRAME (LIBAVCODEC_VERSION_MAJOR < 62) +#define FF_API_DROPCHANGED (LIBAVCODEC_VERSION_MAJOR < 62) -// reminder to remove CrystalHD decoders on next major bump -#define FF_CODEC_CRYSTAL_HD (LIBAVCODEC_VERSION_MAJOR < 61) +#define FF_API_AVFFT (LIBAVCODEC_VERSION_MAJOR < 62) +#define FF_API_FF_PROFILE_LEVEL (LIBAVCODEC_VERSION_MAJOR < 62) +#define FF_API_AVCODEC_CLOSE (LIBAVCODEC_VERSION_MAJOR < 62) +#define FF_API_BUFFER_MIN_SIZE (LIBAVCODEC_VERSION_MAJOR < 62) +#define FF_API_VDPAU_ALLOC_GET_SET (LIBAVCODEC_VERSION_MAJOR < 62) #endif /* AVCODEC_VERSION_MAJOR_H */ diff --git a/libavcodec/videotoolbox.c b/libavcodec/videotoolbox.c index e42fea6f325..f4da80640de 100644 --- a/libavcodec/videotoolbox.c +++ b/libavcodec/videotoolbox.c @@ -33,6 +33,7 @@ #include "internal.h" #include "h264dec.h" #include "hevcdec.h" +#include "hwaccel_internal.h" #include "mpegvideo.h" #include "proresdec.h" #include @@ -245,7 +246,7 @@ CFDataRef ff_videotoolbox_hvcc_extradata_create(AVCodecContext *avctx) #define COUNT_SIZE_PS(T, t) \ for (i = 0; i < HEVC_MAX_##T##PS_COUNT; i++) { \ if (h->ps.t##ps_list[i]) { \ - const HEVC##T##PS *lps = (const HEVC##T##PS *)h->ps.t##ps_list[i]->data; \ + const HEVC##T##PS *lps = h->ps.t##ps_list[i]; \ vt_extradata_size += 2 + escape_ps(NULL, lps->data, lps->data_size); \ num_##t##ps++; \ } \ @@ -368,7 +369,7 @@ CFDataRef ff_videotoolbox_hvcc_extradata_create(AVCodecContext *avctx) p += 3; \ for (i = 0; i < HEVC_MAX_##T##PS_COUNT; i++) { \ if (h->ps.t##ps_list[i]) { \ - const HEVC##T##PS *lps = (const HEVC##T##PS *)h->ps.t##ps_list[i]->data; \ + const HEVC##T##PS *lps = h->ps.t##ps_list[i]; \ int size = escape_ps(p + 2, lps->data, lps->data_size); \ /* unsigned int(16) nalUnitLength; */ \ AV_WB16(p, size); \ @@ -548,6 +549,7 @@ static int videotoolbox_buffer_create(AVCodecContext *avctx, AVFrame *frame) cached_frames->height != height) { AVBufferRef *hw_frames_ctx = av_hwframe_ctx_alloc(cached_frames->device_ref); AVHWFramesContext *hw_frames; + AVVTFramesContext *hw_ctx; if (!hw_frames_ctx) return AVERROR(ENOMEM); @@ -556,6 +558,8 @@ static int videotoolbox_buffer_create(AVCodecContext *avctx, AVFrame *frame) hw_frames->sw_format = sw_format; hw_frames->width = width; hw_frames->height = height; + hw_ctx = hw_frames->hwctx; + hw_ctx->color_range = avctx->color_range; ret = av_hwframe_ctx_init(hw_frames_ctx); if (ret < 0) { @@ -799,6 +803,9 @@ static CFDictionaryRef videotoolbox_buffer_attributes_create(int width, static CFDictionaryRef videotoolbox_decoder_config_create(CMVideoCodecType codec_type, AVCodecContext *avctx) { + CFMutableDictionaryRef avc_info; + CFDataRef data = NULL; + CFMutableDictionaryRef config_info = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, @@ -810,9 +817,6 @@ static CFDictionaryRef videotoolbox_decoder_config_create(CMVideoCodecType codec kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder, kCFBooleanTrue); - CFMutableDictionaryRef avc_info; - CFDataRef data = NULL; - avc_info = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, @@ -1072,13 +1076,14 @@ static int videotoolbox_hevc_end_frame(AVCodecContext *avctx) HEVCContext *h = avctx->priv_data; AVFrame *frame = h->ref->frame; VTContext *vtctx = avctx->internal->hwaccel_priv_data; + int ret; h->output_frame->crop_right = 0; h->output_frame->crop_left = 0; h->output_frame->crop_top = 0; h->output_frame->crop_bottom = 0; - int ret = ff_videotoolbox_common_end_frame(avctx, frame); + ret = ff_videotoolbox_common_end_frame(avctx, frame); vtctx->bitstream_size = 0; return ret; } @@ -1132,15 +1137,17 @@ static int videotoolbox_prores_end_frame(AVCodecContext *avctx) } static enum AVPixelFormat videotoolbox_best_pixel_format(AVCodecContext *avctx) { + int depth; const AVPixFmtDescriptor *descriptor = av_pix_fmt_desc_get(avctx->sw_pix_fmt); if (!descriptor) return AV_PIX_FMT_NV12; // same as av_videotoolbox_alloc_context() - int depth = descriptor->comp[0].depth; if (descriptor->flags & AV_PIX_FMT_FLAG_ALPHA) return AV_PIX_FMT_AYUV64; + depth = descriptor->comp[0].depth; + #if HAVE_KCVPIXELFORMATTYPE_444YPCBCR16BIPLANARVIDEORANGE if (depth > 10) return descriptor->log2_chroma_w == 0 ? AV_PIX_FMT_P416 : AV_PIX_FMT_P216; @@ -1193,7 +1200,9 @@ int ff_videotoolbox_common_init(AVCodecContext *avctx) { VTContext *vtctx = avctx->internal->hwaccel_priv_data; AVHWFramesContext *hw_frames; + AVVTFramesContext *hw_ctx; int err; + bool full_range; vtctx->logctx = avctx; @@ -1227,6 +1236,8 @@ int ff_videotoolbox_common_init(AVCodecContext *avctx) hw_frames->sw_format = videotoolbox_best_pixel_format(avctx); hw_frames->width = avctx->width; hw_frames->height = avctx->height; + hw_ctx = hw_frames->hwctx; + hw_ctx->color_range = avctx->color_range; err = av_hwframe_ctx_init(avctx->hw_frames_ctx); if (err < 0) { @@ -1241,7 +1252,7 @@ int ff_videotoolbox_common_init(AVCodecContext *avctx) goto fail; } - bool full_range = avctx->color_range == AVCOL_RANGE_JPEG; + full_range = avctx->color_range == AVCOL_RANGE_JPEG; vtctx->vt_ctx->cv_pix_fmt_type = av_map_videotoolbox_format_from_pixfmt2(hw_frames->sw_format, full_range); if (!vtctx->vt_ctx->cv_pix_fmt_type) { @@ -1280,11 +1291,11 @@ int ff_videotoolbox_frame_params(AVCodecContext *avctx, return 0; } -const AVHWAccel ff_h263_videotoolbox_hwaccel = { - .name = "h263_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H263, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_h263_videotoolbox_hwaccel = { + .p.name = "h263_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H263, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_mpeg_start_frame, .decode_slice = videotoolbox_mpeg_decode_slice, @@ -1295,11 +1306,11 @@ const AVHWAccel ff_h263_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -const AVHWAccel ff_hevc_videotoolbox_hwaccel = { - .name = "hevc_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_HEVC, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_hevc_videotoolbox_hwaccel = { + .p.name = "hevc_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_hevc_start_frame, .decode_slice = videotoolbox_hevc_decode_slice, @@ -1311,11 +1322,11 @@ const AVHWAccel ff_hevc_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -const AVHWAccel ff_h264_videotoolbox_hwaccel = { - .name = "h264_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_H264, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_h264_videotoolbox_hwaccel = { + .p.name = "h264_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = ff_videotoolbox_h264_start_frame, .decode_slice = ff_videotoolbox_h264_decode_slice, @@ -1327,11 +1338,11 @@ const AVHWAccel ff_h264_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -const AVHWAccel ff_mpeg1_videotoolbox_hwaccel = { - .name = "mpeg1_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG1VIDEO, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_mpeg1_videotoolbox_hwaccel = { + .p.name = "mpeg1_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG1VIDEO, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_mpeg_start_frame, .decode_slice = videotoolbox_mpeg_decode_slice, @@ -1342,11 +1353,11 @@ const AVHWAccel ff_mpeg1_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -const AVHWAccel ff_mpeg2_videotoolbox_hwaccel = { - .name = "mpeg2_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG2VIDEO, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_mpeg2_videotoolbox_hwaccel = { + .p.name = "mpeg2_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_mpeg_start_frame, .decode_slice = videotoolbox_mpeg_decode_slice, @@ -1357,11 +1368,11 @@ const AVHWAccel ff_mpeg2_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -const AVHWAccel ff_mpeg4_videotoolbox_hwaccel = { - .name = "mpeg4_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_MPEG4, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_mpeg4_videotoolbox_hwaccel = { + .p.name = "mpeg4_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG4, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_mpeg_start_frame, .decode_slice = videotoolbox_mpeg_decode_slice, @@ -1372,11 +1383,11 @@ const AVHWAccel ff_mpeg4_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; -const AVHWAccel ff_prores_videotoolbox_hwaccel = { - .name = "prores_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_PRORES, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_prores_videotoolbox_hwaccel = { + .p.name = "prores_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_PRORES, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_prores_start_frame, .decode_slice = videotoolbox_prores_decode_slice, @@ -1387,35 +1398,4 @@ const AVHWAccel ff_prores_videotoolbox_hwaccel = { .priv_data_size = sizeof(VTContext), }; - - -#if FF_API_VT_HWACCEL_CONTEXT -AVVideotoolboxContext *av_videotoolbox_alloc_context(void) -{ - return videotoolbox_alloc_context_with_pix_fmt(AV_PIX_FMT_NONE, false); -} - -int av_videotoolbox_default_init(AVCodecContext *avctx) -{ - return av_videotoolbox_default_init2(avctx, NULL); -} - -int av_videotoolbox_default_init2(AVCodecContext *avctx, AVVideotoolboxContext *vtctx) -{ - enum AVPixelFormat pix_fmt = videotoolbox_best_pixel_format(avctx); - bool full_range = avctx->color_range == AVCOL_RANGE_JPEG; - avctx->hwaccel_context = vtctx ?: videotoolbox_alloc_context_with_pix_fmt(pix_fmt, full_range); - if (!avctx->hwaccel_context) - return AVERROR(ENOMEM); - return 0; -} - -void av_videotoolbox_default_free(AVCodecContext *avctx) -{ - - videotoolbox_stop(avctx); - av_freep(&avctx->hwaccel_context); -} -#endif /* FF_API_VT_HWACCEL_CONTEXT */ - #endif /* CONFIG_VIDEOTOOLBOX */ diff --git a/libavcodec/videotoolbox.h b/libavcodec/videotoolbox.h index ba5eddbf460..d68d76e4006 100644 --- a/libavcodec/videotoolbox.h +++ b/libavcodec/videotoolbox.h @@ -60,15 +60,6 @@ typedef struct AVVideotoolboxContext { */ VTDecompressionSessionRef session; -#if FF_API_VT_OUTPUT_CALLBACK - /** - * The output callback that must be passed to the session. - * Set by av_videottoolbox_default_init() - */ - attribute_deprecated - VTDecompressionOutputCallback output_callback; -#endif - /** * CVPixelBuffer Format Type that Videotoolbox will use for decoded frames. * set by the caller. If this is set to 0, then no specific format is @@ -87,62 +78,6 @@ typedef struct AVVideotoolboxContext { int cm_codec_type; } AVVideotoolboxContext; -#if FF_API_VT_HWACCEL_CONTEXT - -/** - * Allocate and initialize a Videotoolbox context. - * - * This function should be called from the get_format() callback when the caller - * selects the AV_PIX_FMT_VIDETOOLBOX format. The caller must then create - * the decoder object (using the output callback provided by libavcodec) that - * will be used for Videotoolbox-accelerated decoding. - * - * When decoding with Videotoolbox is finished, the caller must destroy the decoder - * object and free the Videotoolbox context using av_free(). - * - * @return the newly allocated context or NULL on failure - * @deprecated Use AVCodecContext.hw_frames_ctx or hw_device_ctx instead. - */ -attribute_deprecated -AVVideotoolboxContext *av_videotoolbox_alloc_context(void); - -/** - * This is a convenience function that creates and sets up the Videotoolbox context using - * an internal implementation. - * - * @param avctx the corresponding codec context - * - * @return >= 0 on success, a negative AVERROR code on failure - * @deprecated Use AVCodecContext.hw_frames_ctx or hw_device_ctx instead. - */ -attribute_deprecated -int av_videotoolbox_default_init(AVCodecContext *avctx); - -/** - * This is a convenience function that creates and sets up the Videotoolbox context using - * an internal implementation. - * - * @param avctx the corresponding codec context - * @param vtctx the Videotoolbox context to use - * - * @return >= 0 on success, a negative AVERROR code on failure - * @deprecated Use AVCodecContext.hw_frames_ctx or hw_device_ctx instead. - */ -attribute_deprecated -int av_videotoolbox_default_init2(AVCodecContext *avctx, AVVideotoolboxContext *vtctx); - -/** - * This function must be called to free the Videotoolbox context initialized with - * av_videotoolbox_default_init(). - * - * @param avctx the corresponding codec context - * @deprecated Use AVCodecContext.hw_frames_ctx or hw_device_ctx instead. - */ -attribute_deprecated -void av_videotoolbox_default_free(AVCodecContext *avctx); - -#endif /* FF_API_VT_HWACCEL_CONTEXT */ - /** * @} */ diff --git a/libavcodec/videotoolbox_vp9.c b/libavcodec/videotoolbox_vp9.c index a998f36d1ad..f5489854e38 100644 --- a/libavcodec/videotoolbox_vp9.c +++ b/libavcodec/videotoolbox_vp9.c @@ -31,6 +31,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/pixdesc.h" #include "decode.h" +#include "hwaccel_internal.h" #include "internal.h" #include "vp9shared.h" @@ -125,11 +126,11 @@ static int videotoolbox_vp9_end_frame(AVCodecContext *avctx) return ff_videotoolbox_common_end_frame(avctx, frame); } -const AVHWAccel ff_vp9_videotoolbox_hwaccel = { - .name = "vp9_videotoolbox", - .type = AVMEDIA_TYPE_VIDEO, - .id = AV_CODEC_ID_VP9, - .pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, +const FFHWAccel ff_vp9_videotoolbox_hwaccel = { + .p.name = "vp9_videotoolbox", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX, .alloc_frame = ff_videotoolbox_alloc_frame, .start_frame = videotoolbox_vp9_start_frame, .decode_slice = videotoolbox_vp9_decode_slice, diff --git a/libavcodec/videotoolboxenc.c b/libavcodec/videotoolboxenc.c index 56971471e4e..5ea9afee22d 100644 --- a/libavcodec/videotoolboxenc.c +++ b/libavcodec/videotoolboxenc.c @@ -37,6 +37,7 @@ #include "encode.h" #include "h264.h" #include "h264_sei.h" +#include "hwconfig.h" #include #if !HAVE_KCMVIDEOCODECTYPE_HEVC @@ -63,7 +64,21 @@ typedef OSStatus (*getParameterSetAtIndex)(CMFormatDescriptionRef videoDesc, size_t *parameterSetCountOut, int *NALUnitHeaderLengthOut); -//These symbols may not be present +/* + * Symbols that aren't available in MacOS 10.8 and iOS 8.0 need to be accessed + * from compat_keys, or it will cause compiler errors when compiling for older + * OS versions. + * + * For example, kVTCompressionPropertyKey_H264EntropyMode was added in + * MacOS 10.9. If this constant were used directly, a compiler would generate + * an error when it has access to the MacOS 10.8 headers, but does not have + * 10.9 headers. + * + * Runtime errors will still occur when unknown keys are set. A warning is + * logged and encoding continues where possible. + * + * When adding new symbols, they should be loaded/set in loadVTEncSymbols(). + */ static struct{ CFStringRef kCVImageBufferColorPrimaries_ITU_R_2020; CFStringRef kCVImageBufferTransferFunction_ITU_R_2020; @@ -94,6 +109,8 @@ static struct{ CFStringRef kVTProfileLevel_H264_High_AutoLevel; CFStringRef kVTProfileLevel_H264_Extended_5_0; CFStringRef kVTProfileLevel_H264_Extended_AutoLevel; + CFStringRef kVTProfileLevel_H264_ConstrainedBaseline_AutoLevel; + CFStringRef kVTProfileLevel_H264_ConstrainedHigh_AutoLevel; CFStringRef kVTProfileLevel_HEVC_Main_AutoLevel; CFStringRef kVTProfileLevel_HEVC_Main10_AutoLevel; @@ -102,9 +119,16 @@ static struct{ CFStringRef kVTCompressionPropertyKey_TargetQualityForAlpha; CFStringRef kVTCompressionPropertyKey_PrioritizeEncodingSpeedOverQuality; CFStringRef kVTCompressionPropertyKey_ConstantBitRate; + CFStringRef kVTCompressionPropertyKey_EncoderID; CFStringRef kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder; CFStringRef kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder; + CFStringRef kVTVideoEncoderSpecification_EnableLowLatencyRateControl; + CFStringRef kVTCompressionPropertyKey_AllowOpenGOP; + CFStringRef kVTCompressionPropertyKey_MaximizePowerEfficiency; + CFStringRef kVTCompressionPropertyKey_ReferenceBufferCount; + CFStringRef kVTCompressionPropertyKey_MaxAllowedFrameQP; + CFStringRef kVTCompressionPropertyKey_MinAllowedFrameQP; getParameterSetAtIndex CMVideoFormatDescriptionGetHEVCParameterSetAtIndex; } compat_keys; @@ -120,7 +144,7 @@ do{ \ static pthread_once_t once_ctrl = PTHREAD_ONCE_INIT; -static void loadVTEncSymbols(){ +static void loadVTEncSymbols(void){ compat_keys.CMVideoFormatDescriptionGetHEVCParameterSetAtIndex = (getParameterSetAtIndex)dlsym( RTLD_DEFAULT, @@ -156,6 +180,8 @@ static void loadVTEncSymbols(){ GET_SYM(kVTProfileLevel_H264_High_AutoLevel, "H264_High_AutoLevel"); GET_SYM(kVTProfileLevel_H264_Extended_5_0, "H264_Extended_5_0"); GET_SYM(kVTProfileLevel_H264_Extended_AutoLevel, "H264_Extended_AutoLevel"); + GET_SYM(kVTProfileLevel_H264_ConstrainedBaseline_AutoLevel, "H264_ConstrainedBaseline_AutoLevel"); + GET_SYM(kVTProfileLevel_H264_ConstrainedHigh_AutoLevel, "H264_ConstrainedHigh_AutoLevel"); GET_SYM(kVTProfileLevel_HEVC_Main_AutoLevel, "HEVC_Main_AutoLevel"); GET_SYM(kVTProfileLevel_HEVC_Main10_AutoLevel, "HEVC_Main10_AutoLevel"); @@ -166,21 +192,24 @@ static void loadVTEncSymbols(){ GET_SYM(kVTCompressionPropertyKey_PrioritizeEncodingSpeedOverQuality, "PrioritizeEncodingSpeedOverQuality"); GET_SYM(kVTCompressionPropertyKey_ConstantBitRate, "ConstantBitRate"); + GET_SYM(kVTCompressionPropertyKey_EncoderID, "EncoderID"); GET_SYM(kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder, "EnableHardwareAcceleratedVideoEncoder"); GET_SYM(kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder, "RequireHardwareAcceleratedVideoEncoder"); + GET_SYM(kVTVideoEncoderSpecification_EnableLowLatencyRateControl, + "EnableLowLatencyRateControl"); + GET_SYM(kVTCompressionPropertyKey_AllowOpenGOP, "AllowOpenGOP"); + GET_SYM(kVTCompressionPropertyKey_MaximizePowerEfficiency, + "MaximizePowerEfficiency"); + GET_SYM(kVTCompressionPropertyKey_ReferenceBufferCount, + "ReferenceBufferCount"); + GET_SYM(kVTCompressionPropertyKey_MaxAllowedFrameQP, "MaxAllowedFrameQP"); + GET_SYM(kVTCompressionPropertyKey_MinAllowedFrameQP, "MinAllowedFrameQP"); } -typedef enum VT_H264Profile { - H264_PROF_AUTO, - H264_PROF_BASELINE, - H264_PROF_MAIN, - H264_PROF_HIGH, - H264_PROF_EXTENDED, - H264_PROF_COUNT -} VT_H264Profile; +#define H264_PROFILE_CONSTRAINED_HIGH (AV_PROFILE_H264_HIGH | AV_PROFILE_H264_CONSTRAINED) typedef enum VTH264Entropy{ VT_ENTROPY_NOT_SET, @@ -188,13 +217,6 @@ typedef enum VTH264Entropy{ VT_CABAC } VTH264Entropy; -typedef enum VT_HEVCProfile { - HEVC_PROF_AUTO, - HEVC_PROF_MAIN, - HEVC_PROF_MAIN10, - HEVC_PROF_COUNT -} VT_HEVCProfile; - static const uint8_t start_code[] = { 0, 0, 0, 1 }; typedef struct ExtraSEI { @@ -213,6 +235,7 @@ typedef struct VTEncContext { AVClass *class; enum AVCodecID codec_id; VTCompressionSessionRef session; + CFDictionaryRef supported_props; CFStringRef ycbcr_matrix; CFStringRef color_primaries; CFStringRef transfer_function; @@ -232,7 +255,7 @@ typedef struct VTEncContext { int64_t first_pts; int64_t dts_delta; - int64_t profile; + int profile; int level; int entropy; int realtime; @@ -251,8 +274,47 @@ typedef struct VTEncContext { /* can't be bool type since AVOption will access it as int */ int a53_cc; + + int max_slice_bytes; + int power_efficient; + int max_ref_frames; } VTEncContext; +static int vt_dump_encoder(AVCodecContext *avctx) +{ + VTEncContext *vtctx = avctx->priv_data; + CFStringRef encoder_id = NULL; + int status; + CFIndex length, max_size; + char *name; + + status = VTSessionCopyProperty(vtctx->session, + compat_keys.kVTCompressionPropertyKey_EncoderID, + kCFAllocatorDefault, + &encoder_id); + // OK if not supported + if (status != noErr) + return 0; + + length = CFStringGetLength(encoder_id); + max_size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); + name = av_malloc(max_size); + if (!name) { + CFRelease(encoder_id); + return AVERROR(ENOMEM); + } + + CFStringGetCString(encoder_id, + name, + max_size, + kCFStringEncodingUTF8); + av_log(avctx, AV_LOG_DEBUG, "Init the encoder: %s\n", name); + av_freep(&name); + CFRelease(encoder_id); + + return 0; +} + static int vtenc_populate_extradata(AVCodecContext *avctx, CMVideoCodecType codec_type, CFStringRef profile_level, @@ -298,6 +360,34 @@ static void clear_frame_queue(VTEncContext *vtctx) set_async_error(vtctx, 0); } +static void vtenc_reset(VTEncContext *vtctx) +{ + if (vtctx->session) { + CFRelease(vtctx->session); + vtctx->session = NULL; + } + + if (vtctx->supported_props) { + CFRelease(vtctx->supported_props); + vtctx->supported_props = NULL; + } + + if (vtctx->color_primaries) { + CFRelease(vtctx->color_primaries); + vtctx->color_primaries = NULL; + } + + if (vtctx->transfer_function) { + CFRelease(vtctx->transfer_function); + vtctx->transfer_function = NULL; + } + + if (vtctx->ycbcr_matrix) { + CFRelease(vtctx->ycbcr_matrix); + vtctx->ycbcr_matrix = NULL; + } +} + static int vtenc_q_pop(VTEncContext *vtctx, bool wait, CMSampleBufferRef *buf, ExtraSEI **sei) { BufNode *info; @@ -419,7 +509,7 @@ static int count_nalus(size_t length_code_size, } static CMVideoCodecType get_cm_codec_type(AVCodecContext *avctx, - int64_t profile, + int profile, double alpha_quality) { const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt == AV_PIX_FMT_VIDEOTOOLBOX ? avctx->sw_pix_fmt : avctx->pix_fmt); @@ -431,23 +521,25 @@ static CMVideoCodecType get_cm_codec_type(AVCodecContext *avctx, } return kCMVideoCodecType_HEVC; case AV_CODEC_ID_PRORES: + if (desc && (desc->flags & AV_PIX_FMT_FLAG_ALPHA)) + avctx->bits_per_coded_sample = 32; switch (profile) { - case FF_PROFILE_PRORES_PROXY: + case AV_PROFILE_PRORES_PROXY: return MKBETAG('a','p','c','o'); // kCMVideoCodecType_AppleProRes422Proxy - case FF_PROFILE_PRORES_LT: + case AV_PROFILE_PRORES_LT: return MKBETAG('a','p','c','s'); // kCMVideoCodecType_AppleProRes422LT - case FF_PROFILE_PRORES_STANDARD: + case AV_PROFILE_PRORES_STANDARD: return MKBETAG('a','p','c','n'); // kCMVideoCodecType_AppleProRes422 - case FF_PROFILE_PRORES_HQ: + case AV_PROFILE_PRORES_HQ: return MKBETAG('a','p','c','h'); // kCMVideoCodecType_AppleProRes422HQ - case FF_PROFILE_PRORES_4444: + case AV_PROFILE_PRORES_4444: return MKBETAG('a','p','4','h'); // kCMVideoCodecType_AppleProRes4444 - case FF_PROFILE_PRORES_XQ: + case AV_PROFILE_PRORES_XQ: return MKBETAG('a','p','4','x'); // kCMVideoCodecType_AppleProRes4444XQ default: - av_log(avctx, AV_LOG_ERROR, "Unknown profile ID: %"PRId64", using auto\n", profile); - case FF_PROFILE_UNKNOWN: + av_log(avctx, AV_LOG_ERROR, "Unknown profile ID: %d, using auto\n", profile); + case AV_PROFILE_UNKNOWN: if (desc && ((desc->flags & AV_PIX_FMT_FLAG_ALPHA) || desc->log2_chroma_w == 0)) @@ -711,20 +803,20 @@ static bool get_vt_h264_profile_level(AVCodecContext *avctx, CFStringRef *profile_level_val) { VTEncContext *vtctx = avctx->priv_data; - int64_t profile = vtctx->profile; + int profile = vtctx->profile; - if (profile == H264_PROF_AUTO && vtctx->level) { + if (profile == AV_PROFILE_UNKNOWN && vtctx->level) { //Need to pick a profile if level is not auto-selected. - profile = vtctx->has_b_frames ? H264_PROF_MAIN : H264_PROF_BASELINE; + profile = vtctx->has_b_frames ? AV_PROFILE_H264_MAIN : AV_PROFILE_H264_BASELINE; } *profile_level_val = NULL; switch (profile) { - case H264_PROF_AUTO: + case AV_PROFILE_UNKNOWN: return true; - case H264_PROF_BASELINE: + case AV_PROFILE_H264_BASELINE: switch (vtctx->level) { case 0: *profile_level_val = compat_keys.kVTProfileLevel_H264_Baseline_AutoLevel; break; @@ -746,7 +838,19 @@ static bool get_vt_h264_profile_level(AVCodecContext *avctx, } break; - case H264_PROF_MAIN: + case AV_PROFILE_H264_CONSTRAINED_BASELINE: + *profile_level_val = compat_keys.kVTProfileLevel_H264_ConstrainedBaseline_AutoLevel; + + if (vtctx->level != 0) { + av_log(avctx, + AV_LOG_WARNING, + "Level is auto-selected when constrained-baseline " + "profile is used. The output may be encoded with a " + "different level.\n"); + } + break; + + case AV_PROFILE_H264_MAIN: switch (vtctx->level) { case 0: *profile_level_val = compat_keys.kVTProfileLevel_H264_Main_AutoLevel; break; @@ -765,7 +869,19 @@ static bool get_vt_h264_profile_level(AVCodecContext *avctx, } break; - case H264_PROF_HIGH: + case H264_PROFILE_CONSTRAINED_HIGH: + *profile_level_val = compat_keys.kVTProfileLevel_H264_ConstrainedHigh_AutoLevel; + + if (vtctx->level != 0) { + av_log(avctx, + AV_LOG_WARNING, + "Level is auto-selected when constrained-high profile " + "is used. The output may be encoded with a different " + "level.\n"); + } + break; + + case AV_PROFILE_H264_HIGH: switch (vtctx->level) { case 0: *profile_level_val = compat_keys.kVTProfileLevel_H264_High_AutoLevel; break; @@ -788,7 +904,7 @@ static bool get_vt_h264_profile_level(AVCodecContext *avctx, compat_keys.kVTProfileLevel_H264_High_5_2; break; } break; - case H264_PROF_EXTENDED: + case AV_PROFILE_H264_EXTENDED: switch (vtctx->level) { case 0: *profile_level_val = compat_keys.kVTProfileLevel_H264_Extended_AutoLevel; break; @@ -816,18 +932,36 @@ static bool get_vt_hevc_profile_level(AVCodecContext *avctx, CFStringRef *profile_level_val) { VTEncContext *vtctx = avctx->priv_data; - int64_t profile = vtctx->profile; + int profile = vtctx->profile; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get( + avctx->pix_fmt == AV_PIX_FMT_VIDEOTOOLBOX ? avctx->sw_pix_fmt + : avctx->pix_fmt); + int bit_depth = desc ? desc->comp[0].depth : 0; *profile_level_val = NULL; switch (profile) { - case HEVC_PROF_AUTO: + case AV_PROFILE_UNKNOWN: + // Set profile automatically if user don't specify + if (bit_depth == 10) { + *profile_level_val = + compat_keys.kVTProfileLevel_HEVC_Main10_AutoLevel; + break; + } return true; - case HEVC_PROF_MAIN: + case AV_PROFILE_HEVC_MAIN: + if (bit_depth > 0 && bit_depth != 8) + av_log(avctx, AV_LOG_WARNING, + "main profile with %d bit input\n", bit_depth); *profile_level_val = compat_keys.kVTProfileLevel_HEVC_Main_AutoLevel; break; - case HEVC_PROF_MAIN10: + case AV_PROFILE_HEVC_MAIN_10: + if (bit_depth > 0 && bit_depth != 10) { + av_log(avctx, AV_LOG_ERROR, + "Invalid main10 profile with %d bit input\n", bit_depth); + return false; + } *profile_level_val = compat_keys.kVTProfileLevel_HEVC_Main10_AutoLevel; break; @@ -954,141 +1088,70 @@ static int create_cv_pixel_buffer_info(AVCodecContext* avctx, return AVERROR(ENOMEM); } -static int get_cv_color_primaries(AVCodecContext *avctx, - CFStringRef *primaries) +static int get_cv_gamma(AVCodecContext *avctx, + CFNumberRef *gamma_level) { - enum AVColorPrimaries pri = avctx->color_primaries; - switch (pri) { - case AVCOL_PRI_UNSPECIFIED: - *primaries = NULL; - break; - - case AVCOL_PRI_BT470BG: - *primaries = kCVImageBufferColorPrimaries_EBU_3213; - break; - - case AVCOL_PRI_SMPTE170M: - *primaries = kCVImageBufferColorPrimaries_SMPTE_C; - break; - - case AVCOL_PRI_BT709: - *primaries = kCVImageBufferColorPrimaries_ITU_R_709_2; - break; - - case AVCOL_PRI_BT2020: - *primaries = compat_keys.kCVImageBufferColorPrimaries_ITU_R_2020; - break; + enum AVColorTransferCharacteristic trc = avctx->color_trc; + Float32 gamma = 0; + *gamma_level = NULL; - default: - av_log(avctx, AV_LOG_ERROR, "Color primaries %s is not supported.\n", av_color_primaries_name(pri)); - *primaries = NULL; - return -1; - } + if (trc == AVCOL_TRC_GAMMA22) + gamma = 2.2; + else if (trc == AVCOL_TRC_GAMMA28) + gamma = 2.8; + if (gamma != 0) + *gamma_level = CFNumberCreate(NULL, kCFNumberFloat32Type, &gamma); return 0; } -static int get_cv_transfer_function(AVCodecContext *avctx, - CFStringRef *transfer_fnc, - CFNumberRef *gamma_level) +// constant quality only on Macs with Apple Silicon +static bool vtenc_qscale_enabled(void) { - enum AVColorTransferCharacteristic trc = avctx->color_trc; - Float32 gamma; - *gamma_level = NULL; - - switch (trc) { - case AVCOL_TRC_UNSPECIFIED: - *transfer_fnc = NULL; - break; - - case AVCOL_TRC_BT709: - *transfer_fnc = kCVImageBufferTransferFunction_ITU_R_709_2; - break; - - case AVCOL_TRC_SMPTE240M: - *transfer_fnc = kCVImageBufferTransferFunction_SMPTE_240M_1995; - break; - -#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_SMPTE_ST_2084_PQ - case AVCOL_TRC_SMPTE2084: - *transfer_fnc = kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ; - break; -#endif -#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_LINEAR - case AVCOL_TRC_LINEAR: - *transfer_fnc = kCVImageBufferTransferFunction_Linear; - break; -#endif -#if HAVE_KCVIMAGEBUFFERTRANSFERFUNCTION_ITU_R_2100_HLG - case AVCOL_TRC_ARIB_STD_B67: - *transfer_fnc = kCVImageBufferTransferFunction_ITU_R_2100_HLG; - break; -#endif - - case AVCOL_TRC_GAMMA22: - gamma = 2.2; - *transfer_fnc = kCVImageBufferTransferFunction_UseGamma; - *gamma_level = CFNumberCreate(NULL, kCFNumberFloat32Type, &gamma); - break; - - case AVCOL_TRC_GAMMA28: - gamma = 2.8; - *transfer_fnc = kCVImageBufferTransferFunction_UseGamma; - *gamma_level = CFNumberCreate(NULL, kCFNumberFloat32Type, &gamma); - break; + return !TARGET_OS_IPHONE && TARGET_CPU_ARM64; +} - case AVCOL_TRC_BT2020_10: - case AVCOL_TRC_BT2020_12: - *transfer_fnc = compat_keys.kCVImageBufferTransferFunction_ITU_R_2020; - break; +static void set_encoder_property_or_log(AVCodecContext *avctx, + CFStringRef key, + const char *print_option_name, + CFTypeRef value) { + int status; + VTEncContext *vtctx = avctx->priv_data; - default: - *transfer_fnc = NULL; - av_log(avctx, AV_LOG_ERROR, "Transfer function %s is not supported.\n", av_color_transfer_name(trc)); - return -1; + status = VTSessionSetProperty(vtctx->session, key, value); + if (status == kVTPropertyNotSupportedErr) { + av_log(avctx, + AV_LOG_INFO, + "This device does not support the %s option. Value ignored.\n", + print_option_name); + } else if (status != 0) { + av_log(avctx, + AV_LOG_ERROR, + "Error setting %s: Error %d\n", + print_option_name, + status); } - - return 0; } -static int get_cv_ycbcr_matrix(AVCodecContext *avctx, CFStringRef *matrix) { - switch(avctx->colorspace) { - case AVCOL_SPC_BT709: - *matrix = kCVImageBufferYCbCrMatrix_ITU_R_709_2; - break; +static int set_encoder_int_property_or_log(AVCodecContext* avctx, + CFStringRef key, + const char* print_option_name, + int value) { + CFNumberRef value_cfnum = CFNumberCreate(kCFAllocatorDefault, + kCFNumberIntType, + &value); - case AVCOL_SPC_UNSPECIFIED: - case AVCOL_SPC_RGB: - *matrix = NULL; - break; - - case AVCOL_SPC_BT470BG: - case AVCOL_SPC_SMPTE170M: - *matrix = kCVImageBufferYCbCrMatrix_ITU_R_601_4; - break; + if (value_cfnum == NULL) { + return AVERROR(ENOMEM); + } - case AVCOL_SPC_SMPTE240M: - *matrix = kCVImageBufferYCbCrMatrix_SMPTE_240M_1995; - break; + set_encoder_property_or_log(avctx, key, print_option_name, value_cfnum); - case AVCOL_SPC_BT2020_NCL: - *matrix = compat_keys.kCVImageBufferYCbCrMatrix_ITU_R_2020; - break; - - default: - av_log(avctx, AV_LOG_ERROR, "Color space %s is not supported.\n", av_color_space_name(avctx->colorspace)); - return -1; - } + CFRelease(value_cfnum); return 0; } -// constant quality only on Macs with Apple Silicon -static bool vtenc_qscale_enabled(void) -{ - return !TARGET_OS_IPHONE && TARGET_CPU_ARM64; -} - static int vtenc_create_encoder(AVCodecContext *avctx, CMVideoCodecType codec_type, CFStringRef profile_level, @@ -1134,6 +1197,26 @@ static int vtenc_create_encoder(AVCodecContext *avctx, return AVERROR_EXTERNAL; } +#if defined (MAC_OS_X_VERSION_10_13) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13) + if (__builtin_available(macOS 10.13, *)) { + status = VTCopySupportedPropertyDictionaryForEncoder(avctx->width, + avctx->height, + codec_type, + enc_info, + NULL, + &vtctx->supported_props); + + if (status != noErr) { + av_log(avctx, AV_LOG_ERROR,"Error retrieving the supported property dictionary err=%"PRId64"\n", (int64_t)status); + return AVERROR_EXTERNAL; + } + } +#endif + + status = vt_dump_encoder(avctx); + if (status < 0) + return status; + if (avctx->flags & AV_CODEC_FLAG_QSCALE && !vtenc_qscale_enabled()) { av_log(avctx, AV_LOG_ERROR, "Error: -q:v qscale not available for encoder. Use -b:v bitrate instead.\n"); return AVERROR_EXTERNAL; @@ -1245,6 +1328,13 @@ static int vtenc_create_encoder(AVCodecContext *avctx, compat_keys.kVTCompressionPropertyKey_TargetQualityForAlpha, alpha_quality_num); CFRelease(alpha_quality_num); + + if (status) { + av_log(avctx, + AV_LOG_ERROR, + "Error setting alpha quality: %d\n", + status); + } } } @@ -1441,6 +1531,64 @@ static int vtenc_create_encoder(AVCodecContext *avctx, } } + if ((avctx->flags & AV_CODEC_FLAG_CLOSED_GOP) != 0) { + set_encoder_property_or_log(avctx, + compat_keys.kVTCompressionPropertyKey_AllowOpenGOP, + "AllowOpenGop", + kCFBooleanFalse); + } + + if (avctx->qmin >= 0) { + status = set_encoder_int_property_or_log(avctx, + compat_keys.kVTCompressionPropertyKey_MinAllowedFrameQP, + "qmin", + avctx->qmin); + + if (status != 0) { + return status; + } + } + + if (avctx->qmax >= 0) { + status = set_encoder_int_property_or_log(avctx, + compat_keys.kVTCompressionPropertyKey_MaxAllowedFrameQP, + "qmax", + avctx->qmax); + + if (status != 0) { + return status; + } + } + + if (vtctx->max_slice_bytes >= 0 && avctx->codec_id == AV_CODEC_ID_H264) { + status = set_encoder_int_property_or_log(avctx, + kVTCompressionPropertyKey_MaxH264SliceBytes, + "max_slice_bytes", + vtctx->max_slice_bytes); + + if (status != 0) { + return status; + } + } + + if (vtctx->power_efficient >= 0) { + set_encoder_property_or_log(avctx, + compat_keys.kVTCompressionPropertyKey_MaximizePowerEfficiency, + "power_efficient", + vtctx->power_efficient ? kCFBooleanTrue : kCFBooleanFalse); + } + + if (vtctx->max_ref_frames > 0) { + status = set_encoder_int_property_or_log(avctx, + compat_keys.kVTCompressionPropertyKey_ReferenceBufferCount, + "max_ref_frames", + vtctx->max_ref_frames); + + if (status != 0) { + return status; + } + } + status = VTCompressionSessionPrepareToEncodeFrames(vtctx->session); if (status) { av_log(avctx, AV_LOG_ERROR, "Error: cannot prepare encoder: %d\n", status); @@ -1480,12 +1628,12 @@ static int vtenc_configure_encoder(AVCodecContext *avctx) vtctx->get_param_set_func = CMVideoFormatDescriptionGetH264ParameterSetAtIndex; vtctx->has_b_frames = avctx->max_b_frames > 0; - if(vtctx->has_b_frames && vtctx->profile == H264_PROF_BASELINE){ + if(vtctx->has_b_frames && (0xFF & vtctx->profile) == AV_PROFILE_H264_BASELINE){ av_log(avctx, AV_LOG_WARNING, "Cannot use B-frames with baseline profile. Output will not contain B-frames.\n"); vtctx->has_b_frames = 0; } - if (vtctx->entropy == VT_CABAC && vtctx->profile == H264_PROF_BASELINE) { + if (vtctx->entropy == VT_CABAC && (0xFF & vtctx->profile) == AV_PROFILE_H264_BASELINE) { av_log(avctx, AV_LOG_WARNING, "CABAC entropy requires 'main' or 'high' profile, but baseline was requested. Encode will not use CABAC entropy.\n"); vtctx->entropy = VT_ENTROPY_NOT_SET; } @@ -1526,6 +1674,13 @@ static int vtenc_configure_encoder(AVCodecContext *avctx) } #endif + // low-latency mode: eliminate frame reordering, follow a one-in-one-out encoding mode + if ((avctx->flags & AV_CODEC_FLAG_LOW_DELAY) && avctx->codec_id == AV_CODEC_ID_H264) { + CFDictionarySetValue(enc_info, + compat_keys.kVTVideoEncoderSpecification_EnableLowLatencyRateControl, + kCFBooleanTrue); + } + if (avctx->pix_fmt != AV_PIX_FMT_VIDEOTOOLBOX) { status = create_cv_pixel_buffer_info(avctx, &pixel_buffer_info); if (status) @@ -1534,9 +1689,10 @@ static int vtenc_configure_encoder(AVCodecContext *avctx) vtctx->dts_delta = vtctx->has_b_frames ? -1 : 0; - get_cv_transfer_function(avctx, &vtctx->transfer_function, &gamma_level); - get_cv_ycbcr_matrix(avctx, &vtctx->ycbcr_matrix); - get_cv_color_primaries(avctx, &vtctx->color_primaries); + get_cv_gamma(avctx, &gamma_level); + vtctx->transfer_function = av_map_videotoolbox_color_trc_from_av(avctx->color_trc); + vtctx->ycbcr_matrix = av_map_videotoolbox_color_matrix_from_av(avctx->colorspace); + vtctx->color_primaries = av_map_videotoolbox_color_primaries_from_av(avctx->color_primaries); if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { @@ -1582,7 +1738,9 @@ static av_cold int vtenc_init(AVCodecContext *avctx) pthread_mutex_init(&vtctx->lock, NULL); pthread_cond_init(&vtctx->cv_sample_sent, NULL); - vtctx->session = NULL; + // It can happen when user set avctx->profile directly. + if (vtctx->profile == AV_PROFILE_UNKNOWN) + vtctx->profile = avctx->profile; status = vtenc_configure_encoder(avctx); if (status) return status; @@ -1650,8 +1808,8 @@ static int find_sei_end(AVCodecContext *avctx, { int nal_type; size_t sei_payload_size = 0; - *sei_end = NULL; uint8_t *nal_start = nal_data; + *sei_end = NULL; if (!nal_size) return 0; @@ -2041,7 +2199,7 @@ static int vtenc_cm_to_avpacket( return AVERROR_EXTERNAL; } - int status = get_params_size(avctx, vid_fmt, &header_size); + status = get_params_size(avctx, vid_fmt, &header_size); if (status) return status; } @@ -2146,7 +2304,7 @@ static int get_cv_pixel_info( const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->pix_fmt); VTEncContext *vtctx = avctx->priv_data; int av_format = frame->format; - int av_color_range = frame->color_range; + int av_color_range = avctx->color_range; int i; int range_guessed; int status; @@ -2351,8 +2509,8 @@ static int create_cv_pixel_buffer(AVCodecContext *avctx, vtstatus = VTCompressionSessionPrepareToEncodeFrames(vtctx->session); if (vtstatus == kVTInvalidSessionErr) { - CFRelease(vtctx->session); - vtctx->session = NULL; + vtenc_reset(vtctx); + status = vtenc_configure_encoder(avctx); if (status == 0) pix_buf_pool = VTCompressionSessionGetPixelBufferPool(vtctx->session); @@ -2608,10 +2766,7 @@ static int vtenc_populate_extradata(AVCodecContext *avctx, pe_cleanup: CVPixelBufferRelease(pix_buf); - if(vtctx->session) - CFRelease(vtctx->session); - - vtctx->session = NULL; + vtenc_reset(vtctx); vtctx->frame_ct_out = 0; av_assert0(status != 0 || (avctx->extradata && avctx->extradata_size > 0)); @@ -2634,23 +2789,8 @@ static av_cold int vtenc_close(AVCodecContext *avctx) clear_frame_queue(vtctx); pthread_cond_destroy(&vtctx->cv_sample_sent); pthread_mutex_destroy(&vtctx->lock); - CFRelease(vtctx->session); - vtctx->session = NULL; - - if (vtctx->color_primaries) { - CFRelease(vtctx->color_primaries); - vtctx->color_primaries = NULL; - } - - if (vtctx->transfer_function) { - CFRelease(vtctx->transfer_function); - vtctx->transfer_function = NULL; - } - if (vtctx->ycbcr_matrix) { - CFRelease(vtctx->ycbcr_matrix); - vtctx->ycbcr_matrix = NULL; - } + vtenc_reset(vtctx); return 0; } @@ -2718,41 +2858,59 @@ static const enum AVPixelFormat prores_pix_fmts[] = { OFFSET(frames_after), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, \ { "prio_speed", "prioritize encoding speed", OFFSET(prio_speed), AV_OPT_TYPE_BOOL, \ { .i64 = -1 }, -1, 1, VE }, \ + { "power_efficient", "Set to 1 to enable more power-efficient encoding if supported.", \ + OFFSET(power_efficient), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, VE }, \ + { "max_ref_frames", \ + "Sets the maximum number of reference frames. This only has an effect when the value is less than the maximum allowed by the profile/level.", \ + OFFSET(max_ref_frames), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE }, + +static const AVCodecHWConfigInternal *const vt_encode_hw_configs[] = { + HW_CONFIG_ENCODER_FRAMES(VIDEOTOOLBOX, VIDEOTOOLBOX), + NULL, +}; #define OFFSET(x) offsetof(VTEncContext, x) static const AVOption h264_options[] = { - { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT64, { .i64 = H264_PROF_AUTO }, H264_PROF_AUTO, H264_PROF_COUNT, VE, "profile" }, - { "baseline", "Baseline Profile", 0, AV_OPT_TYPE_CONST, { .i64 = H264_PROF_BASELINE }, INT_MIN, INT_MAX, VE, "profile" }, - { "main", "Main Profile", 0, AV_OPT_TYPE_CONST, { .i64 = H264_PROF_MAIN }, INT_MIN, INT_MAX, VE, "profile" }, - { "high", "High Profile", 0, AV_OPT_TYPE_CONST, { .i64 = H264_PROF_HIGH }, INT_MIN, INT_MAX, VE, "profile" }, - { "extended", "Extend Profile", 0, AV_OPT_TYPE_CONST, { .i64 = H264_PROF_EXTENDED }, INT_MIN, INT_MAX, VE, "profile" }, - - { "level", "Level", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 52, VE, "level" }, - { "1.3", "Level 1.3, only available with Baseline Profile", 0, AV_OPT_TYPE_CONST, { .i64 = 13 }, INT_MIN, INT_MAX, VE, "level" }, - { "3.0", "Level 3.0", 0, AV_OPT_TYPE_CONST, { .i64 = 30 }, INT_MIN, INT_MAX, VE, "level" }, - { "3.1", "Level 3.1", 0, AV_OPT_TYPE_CONST, { .i64 = 31 }, INT_MIN, INT_MAX, VE, "level" }, - { "3.2", "Level 3.2", 0, AV_OPT_TYPE_CONST, { .i64 = 32 }, INT_MIN, INT_MAX, VE, "level" }, - { "4.0", "Level 4.0", 0, AV_OPT_TYPE_CONST, { .i64 = 40 }, INT_MIN, INT_MAX, VE, "level" }, - { "4.1", "Level 4.1", 0, AV_OPT_TYPE_CONST, { .i64 = 41 }, INT_MIN, INT_MAX, VE, "level" }, - { "4.2", "Level 4.2", 0, AV_OPT_TYPE_CONST, { .i64 = 42 }, INT_MIN, INT_MAX, VE, "level" }, - { "5.0", "Level 5.0", 0, AV_OPT_TYPE_CONST, { .i64 = 50 }, INT_MIN, INT_MAX, VE, "level" }, - { "5.1", "Level 5.1", 0, AV_OPT_TYPE_CONST, { .i64 = 51 }, INT_MIN, INT_MAX, VE, "level" }, - { "5.2", "Level 5.2", 0, AV_OPT_TYPE_CONST, { .i64 = 52 }, INT_MIN, INT_MAX, VE, "level" }, - - { "coder", "Entropy coding", OFFSET(entropy), AV_OPT_TYPE_INT, { .i64 = VT_ENTROPY_NOT_SET }, VT_ENTROPY_NOT_SET, VT_CABAC, VE, "coder" }, - { "cavlc", "CAVLC entropy coding", 0, AV_OPT_TYPE_CONST, { .i64 = VT_CAVLC }, INT_MIN, INT_MAX, VE, "coder" }, - { "vlc", "CAVLC entropy coding", 0, AV_OPT_TYPE_CONST, { .i64 = VT_CAVLC }, INT_MIN, INT_MAX, VE, "coder" }, - { "cabac", "CABAC entropy coding", 0, AV_OPT_TYPE_CONST, { .i64 = VT_CABAC }, INT_MIN, INT_MAX, VE, "coder" }, - { "ac", "CABAC entropy coding", 0, AV_OPT_TYPE_CONST, { .i64 = VT_CABAC }, INT_MIN, INT_MAX, VE, "coder" }, + { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = AV_PROFILE_UNKNOWN }, AV_PROFILE_UNKNOWN, INT_MAX, VE, .unit = "profile" }, + { "baseline", "Baseline Profile", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_H264_BASELINE }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "constrained_baseline", "Constrained Baseline Profile", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_H264_CONSTRAINED_BASELINE }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "main", "Main Profile", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_H264_MAIN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "high", "High Profile", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_H264_HIGH }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "constrained_high", "Constrained High Profile", 0, AV_OPT_TYPE_CONST, { .i64 = H264_PROFILE_CONSTRAINED_HIGH }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "extended", "Extend Profile", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_H264_EXTENDED }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + + { "level", "Level", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 52, VE, .unit = "level" }, + { "1.3", "Level 1.3, only available with Baseline Profile", 0, AV_OPT_TYPE_CONST, { .i64 = 13 }, INT_MIN, INT_MAX, VE, .unit = "level" }, + { "3.0", "Level 3.0", 0, AV_OPT_TYPE_CONST, { .i64 = 30 }, INT_MIN, INT_MAX, VE, .unit = "level" }, + { "3.1", "Level 3.1", 0, AV_OPT_TYPE_CONST, { .i64 = 31 }, INT_MIN, INT_MAX, VE, .unit = "level" }, + { "3.2", "Level 3.2", 0, AV_OPT_TYPE_CONST, { .i64 = 32 }, INT_MIN, INT_MAX, VE, .unit = "level" }, + { "4.0", "Level 4.0", 0, AV_OPT_TYPE_CONST, { .i64 = 40 }, INT_MIN, INT_MAX, VE, .unit = "level" }, + { "4.1", "Level 4.1", 0, AV_OPT_TYPE_CONST, { .i64 = 41 }, INT_MIN, INT_MAX, VE, .unit = "level" }, + { "4.2", "Level 4.2", 0, AV_OPT_TYPE_CONST, { .i64 = 42 }, INT_MIN, INT_MAX, VE, .unit = "level" }, + { "5.0", "Level 5.0", 0, AV_OPT_TYPE_CONST, { .i64 = 50 }, INT_MIN, INT_MAX, VE, .unit = "level" }, + { "5.1", "Level 5.1", 0, AV_OPT_TYPE_CONST, { .i64 = 51 }, INT_MIN, INT_MAX, VE, .unit = "level" }, + { "5.2", "Level 5.2", 0, AV_OPT_TYPE_CONST, { .i64 = 52 }, INT_MIN, INT_MAX, VE, .unit = "level" }, + + { "coder", "Entropy coding", OFFSET(entropy), AV_OPT_TYPE_INT, { .i64 = VT_ENTROPY_NOT_SET }, VT_ENTROPY_NOT_SET, VT_CABAC, VE, .unit = "coder" }, + { "cavlc", "CAVLC entropy coding", 0, AV_OPT_TYPE_CONST, { .i64 = VT_CAVLC }, INT_MIN, INT_MAX, VE, .unit = "coder" }, + { "vlc", "CAVLC entropy coding", 0, AV_OPT_TYPE_CONST, { .i64 = VT_CAVLC }, INT_MIN, INT_MAX, VE, .unit = "coder" }, + { "cabac", "CABAC entropy coding", 0, AV_OPT_TYPE_CONST, { .i64 = VT_CABAC }, INT_MIN, INT_MAX, VE, .unit = "coder" }, + { "ac", "CABAC entropy coding", 0, AV_OPT_TYPE_CONST, { .i64 = VT_CABAC }, INT_MIN, INT_MAX, VE, .unit = "coder" }, { "a53cc", "Use A53 Closed Captions (if available)", OFFSET(a53_cc), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, VE }, { "constant_bit_rate", "Require constant bit rate (macOS 13 or newer)", OFFSET(constant_bit_rate), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, - + { "max_slice_bytes", "Set the maximum number of bytes in an H.264 slice.", OFFSET(max_slice_bytes), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, VE }, COMMON_OPTIONS { NULL }, }; +static const FFCodecDefault vt_defaults[] = { + {"qmin", "-1"}, + {"qmax", "-1"}, + {NULL}, +}; + static const AVClass h264_videotoolbox_class = { .class_name = "h264_videotoolbox", .item_name = av_default_item_name, @@ -2768,17 +2926,19 @@ const FFCodec ff_h264_videotoolbox_encoder = { .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, .priv_data_size = sizeof(VTEncContext), .p.pix_fmts = avc_pix_fmts, + .defaults = vt_defaults, .init = vtenc_init, FF_CODEC_ENCODE_CB(vtenc_frame), .close = vtenc_close, .p.priv_class = &h264_videotoolbox_class, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .hw_configs = vt_encode_hw_configs, }; static const AVOption hevc_options[] = { - { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT64, { .i64 = HEVC_PROF_AUTO }, HEVC_PROF_AUTO, HEVC_PROF_COUNT, VE, "profile" }, - { "main", "Main Profile", 0, AV_OPT_TYPE_CONST, { .i64 = HEVC_PROF_MAIN }, INT_MIN, INT_MAX, VE, "profile" }, - { "main10", "Main10 Profile", 0, AV_OPT_TYPE_CONST, { .i64 = HEVC_PROF_MAIN10 }, INT_MIN, INT_MAX, VE, "profile" }, + { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = AV_PROFILE_UNKNOWN }, AV_PROFILE_UNKNOWN, INT_MAX, VE, .unit = "profile" }, + { "main", "Main Profile", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_HEVC_MAIN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "main10", "Main10 Profile", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_HEVC_MAIN_10 }, INT_MIN, INT_MAX, VE, .unit = "profile" }, { "alpha_quality", "Compression quality for the alpha channel", OFFSET(alpha_quality), AV_OPT_TYPE_DOUBLE, { .dbl = 0.0 }, 0.0, 1.0, VE }, @@ -2804,23 +2964,25 @@ const FFCodec ff_hevc_videotoolbox_encoder = { AV_CODEC_CAP_HARDWARE, .priv_data_size = sizeof(VTEncContext), .p.pix_fmts = hevc_pix_fmts, + .defaults = vt_defaults, .init = vtenc_init, FF_CODEC_ENCODE_CB(vtenc_frame), .close = vtenc_close, .p.priv_class = &hevc_videotoolbox_class, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.wrapper_name = "videotoolbox", + .hw_configs = vt_encode_hw_configs, }; static const AVOption prores_options[] = { - { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT64, { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, FF_PROFILE_PRORES_XQ, VE, "profile" }, - { "auto", "Automatically determine based on input format", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, VE, "profile" }, - { "proxy", "ProRes 422 Proxy", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_PRORES_PROXY }, INT_MIN, INT_MAX, VE, "profile" }, - { "lt", "ProRes 422 LT", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_PRORES_LT }, INT_MIN, INT_MAX, VE, "profile" }, - { "standard", "ProRes 422", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_PRORES_STANDARD }, INT_MIN, INT_MAX, VE, "profile" }, - { "hq", "ProRes 422 HQ", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_PRORES_HQ }, INT_MIN, INT_MAX, VE, "profile" }, - { "4444", "ProRes 4444", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_PRORES_4444 }, INT_MIN, INT_MAX, VE, "profile" }, - { "xq", "ProRes 4444 XQ", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_PRORES_XQ }, INT_MIN, INT_MAX, VE, "profile" }, + { "profile", "Profile", OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = AV_PROFILE_UNKNOWN }, AV_PROFILE_UNKNOWN, AV_PROFILE_PRORES_XQ, VE, .unit = "profile" }, + { "auto", "Automatically determine based on input format", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_UNKNOWN }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "proxy", "ProRes 422 Proxy", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_PRORES_PROXY }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "lt", "ProRes 422 LT", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_PRORES_LT }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "standard", "ProRes 422", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_PRORES_STANDARD }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "hq", "ProRes 422 HQ", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_PRORES_HQ }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "4444", "ProRes 4444", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_PRORES_4444 }, INT_MIN, INT_MAX, VE, .unit = "profile" }, + { "xq", "ProRes 4444 XQ", 0, AV_OPT_TYPE_CONST, { .i64 = AV_PROFILE_PRORES_XQ }, INT_MIN, INT_MAX, VE, .unit = "profile" }, COMMON_OPTIONS { NULL }, @@ -2842,10 +3004,12 @@ const FFCodec ff_prores_videotoolbox_encoder = { AV_CODEC_CAP_HARDWARE, .priv_data_size = sizeof(VTEncContext), .p.pix_fmts = prores_pix_fmts, + .defaults = vt_defaults, .init = vtenc_init, FF_CODEC_ENCODE_CB(vtenc_frame), .close = vtenc_close, .p.priv_class = &prores_videotoolbox_class, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.wrapper_name = "videotoolbox", + .hw_configs = vt_encode_hw_configs, }; diff --git a/libavcodec/vlc.c b/libavcodec/vlc.c index 96f2b28c7e6..7d940915df3 100644 --- a/libavcodec/vlc.c +++ b/libavcodec/vlc.c @@ -30,6 +30,7 @@ #include "libavutil/avassert.h" #include "libavutil/error.h" #include "libavutil/internal.h" +#include "libavutil/intreadwrite.h" #include "libavutil/log.h" #include "libavutil/macros.h" #include "libavutil/mem.h" @@ -63,7 +64,7 @@ static int alloc_table(VLC *vlc, int size, int use_static) vlc->table_size += size; if (vlc->table_size > vlc->table_allocated) { if (use_static) - abort(); // cannot do anything, init_vlc() is used with too little memory + abort(); // cannot do anything, vlc_init() is used with too little memory vlc->table_allocated += (1 << vlc->bits); vlc->table = av_realloc_f(vlc->table, vlc->table_allocated, sizeof(*vlc->table)); if (!vlc->table) { @@ -99,7 +100,7 @@ static int vlc_common_init(VLC *vlc, int nb_bits, int nb_codes, { vlc->bits = nb_bits; vlc->table_size = 0; - if (flags & INIT_VLC_USE_NEW_STATIC) { + if (flags & VLC_INIT_USE_STATIC) { av_assert0(nb_codes <= LOCALBUF_ELEMS); } else { vlc->table = NULL; @@ -143,7 +144,7 @@ static int build_table(VLC *vlc, int table_nb_bits, int nb_codes, if (table_nb_bits > 30) return AVERROR(EINVAL); table_size = 1 << table_nb_bits; - table_index = alloc_table(vlc, table_size, flags & INIT_VLC_USE_NEW_STATIC); + table_index = alloc_table(vlc, table_size, flags & VLC_INIT_USE_STATIC); ff_dlog(NULL, "new table index=%d size=%d\n", table_index, table_size); if (table_index < 0) return table_index; @@ -161,7 +162,7 @@ static int build_table(VLC *vlc, int table_nb_bits, int nb_codes, int nb = 1 << (table_nb_bits - n); int inc = 1; - if (flags & INIT_VLC_OUTPUT_LE) { + if (flags & VLC_INIT_OUTPUT_LE) { j = bitswap_32(code); inc = 1 << n; } @@ -199,7 +200,7 @@ static int build_table(VLC *vlc, int table_nb_bits, int nb_codes, subtable_bits = FFMAX(subtable_bits, n); } subtable_bits = FFMIN(subtable_bits, table_nb_bits); - j = (flags & INIT_VLC_OUTPUT_LE) ? bitswap_32(code_prefix) >> (32 - table_nb_bits) : code_prefix; + j = (flags & VLC_INIT_OUTPUT_LE) ? bitswap_32(code_prefix) >> (32 - table_nb_bits) : code_prefix; table[j].len = -subtable_bits; ff_dlog(NULL, "%4x: n=%d (subtable)\n", j, codes[i].bits + table_nb_bits); @@ -230,9 +231,9 @@ static int vlc_common_end(VLC *vlc, int nb_bits, int nb_codes, VLCcode *codes, { int ret = build_table(vlc, nb_bits, nb_codes, codes, flags); - if (flags & INIT_VLC_USE_NEW_STATIC) { + if (flags & VLC_INIT_USE_STATIC) { if (vlc->table_size != vlc->table_allocated && - !(flags & (INIT_VLC_STATIC_OVERLONG & ~INIT_VLC_USE_NEW_STATIC))) + !(flags & (VLC_INIT_STATIC_OVERLONG & ~VLC_INIT_USE_STATIC))) av_log(NULL, AV_LOG_ERROR, "needed %d had %d\n", vlc->table_size, vlc->table_allocated); av_assert0(ret >= 0); } else { @@ -246,30 +247,7 @@ static int vlc_common_end(VLC *vlc, int nb_bits, int nb_codes, VLCcode *codes, return 0; } -/* Build VLC decoding tables suitable for use with get_vlc(). - - 'nb_bits' sets the decoding table size (2^nb_bits) entries. The - bigger it is, the faster is the decoding. But it should not be too - big to save memory and L1 cache. '9' is a good compromise. - - 'nb_codes' : number of vlcs codes - - 'bits' : table which gives the size (in bits) of each vlc code. - - 'codes' : table which gives the bit pattern of of each vlc code. - - 'symbols' : table which gives the values to be returned from get_vlc(). - - 'xxx_wrap' : give the number of bytes between each entry of the - 'bits' or 'codes' tables. - - 'xxx_size' : gives the number of bytes of each entry of the 'bits' - or 'codes' tables. Currently 1,2 and 4 are supported. - - 'wrap' and 'size' make it possible to use any memory configuration and types - (byte/word/long) to store the 'bits', 'codes', and 'symbols' tables. -*/ -int ff_init_vlc_sparse(VLC *vlc, int nb_bits, int nb_codes, +int ff_vlc_init_sparse(VLC *vlc, int nb_bits, int nb_codes, const void *bits, int bits_wrap, int bits_size, const void *codes, int codes_wrap, int codes_size, const void *symbols, int symbols_wrap, int symbols_size, @@ -291,7 +269,7 @@ int ff_init_vlc_sparse(VLC *vlc, int nb_bits, int nb_codes, if (!(condition)) \ continue; \ if (len > 3*nb_bits || len > 32) { \ - av_log(NULL, AV_LOG_ERROR, "Too long VLC (%u) in init_vlc\n", len);\ + av_log(NULL, AV_LOG_ERROR, "Too long VLC (%u) in vlc_init\n", len);\ if (buf != localbuf) \ av_free(buf); \ return AVERROR(EINVAL); \ @@ -300,12 +278,12 @@ int ff_init_vlc_sparse(VLC *vlc, int nb_bits, int nb_codes, GET_DATA(buf[j].code, codes, i, codes_wrap, codes_size); \ if (buf[j].code >= (1LL< nb_bits); - // qsort is the slowest part of init_vlc, and could probably be improved or avoided + // qsort is the slowest part of vlc_init, and could probably be improved or avoided AV_QSORT(buf, j, struct VLCcode, compare_vlcspec); COPY(len && len <= nb_bits); nb_codes = j; @@ -325,7 +303,7 @@ int ff_init_vlc_sparse(VLC *vlc, int nb_bits, int nb_codes, flags, localbuf); } -int ff_init_vlc_from_lengths(VLC *vlc, int nb_bits, int nb_codes, +int ff_vlc_init_from_lengths(VLC *vlc, int nb_bits, int nb_codes, const int8_t *lens, int lens_wrap, const void *symbols, int symbols_wrap, int symbols_size, int offset, int flags, void *logctx) @@ -372,7 +350,234 @@ int ff_init_vlc_from_lengths(VLC *vlc, int nb_bits, int nb_codes, return AVERROR_INVALIDDATA; } -void ff_free_vlc(VLC *vlc) +av_cold void ff_vlc_init_table_from_lengths(VLCElem table[], int table_size, + int nb_bits, int nb_codes, + const int8_t *lens, int lens_wrap, + const void *symbols, int symbols_wrap, int symbols_size, + int offset, int flags) +{ + VLC vlc = { .table = table, .table_allocated = table_size }; + + ff_vlc_init_from_lengths(&vlc, nb_bits, nb_codes, lens, lens_wrap, + symbols, symbols_wrap, symbols_size, + offset, flags | VLC_INIT_USE_STATIC, NULL); +} + +av_cold const VLCElem *ff_vlc_init_tables_from_lengths(VLCInitState *state, + int nb_bits, int nb_codes, + const int8_t *lens, int lens_wrap, + const void *symbols, int symbols_wrap, int symbols_size, + int offset, int flags) +{ + VLC vlc = { .table = state->table, .table_allocated = state->size }; + + ff_vlc_init_from_lengths(&vlc, nb_bits, nb_codes, lens, lens_wrap, + symbols, symbols_wrap, symbols_size, + offset, flags | VLC_INIT_STATIC_OVERLONG, NULL); + + state->table += vlc.table_size; + state->size -= vlc.table_size; + + return vlc.table; +} + +av_cold void ff_vlc_init_table_sparse(VLCElem table[], int table_size, + int nb_bits, int nb_codes, + const void *bits, int bits_wrap, int bits_size, + const void *codes, int codes_wrap, int codes_size, + const void *symbols, int symbols_wrap, int symbols_size, + int flags) +{ + VLC vlc = { .table = table, .table_allocated = table_size }; + + ff_vlc_init_sparse(&vlc, nb_bits, nb_codes, + bits, bits_wrap, bits_size, + codes, codes_wrap, codes_size, + symbols, symbols_wrap, symbols_size, + flags | VLC_INIT_USE_STATIC); +} + +av_cold const VLCElem *ff_vlc_init_tables_sparse(VLCInitState *state, + int nb_bits, int nb_codes, + const void *bits, int bits_wrap, int bits_size, + const void *codes, int codes_wrap, int codes_size, + const void *symbols, int symbols_wrap, int symbols_size, + int flags) +{ + VLC vlc = { .table = state->table, .table_allocated = state->size }; + + ff_vlc_init_sparse(&vlc, nb_bits, nb_codes, + bits, bits_wrap, bits_size, + codes, codes_wrap, codes_size, + symbols, symbols_wrap, symbols_size, + flags | VLC_INIT_STATIC_OVERLONG); + + state->table += vlc.table_size; + state->size -= vlc.table_size; + + return vlc.table; +} + +static void add_level(VLC_MULTI_ELEM *table, const int is16bit, + const int num, const int numbits, + const VLCcode *buf, + uint32_t curcode, int curlen, + int curlimit, int curlevel, + const int minlen, const int max, + unsigned* levelcnt, VLC_MULTI_ELEM info) +{ + int max_symbols = VLC_MULTI_MAX_SYMBOLS >> is16bit; + for (int i = num-1; i >= max; i--) { + for (int j = 0; j < 2; j++) { + int newlimit, sym; + int t = j ? i-1 : i; + int l = buf[t].bits; + uint32_t code; + + sym = buf[t].symbol; + if (l >= curlimit) + return; + code = curcode + (buf[t].code >> curlen); + newlimit = curlimit - l; + l += curlen; + if (is16bit) AV_WN16(info.val+2*curlevel, sym); + else info.val[curlevel] = sym&0xFF; + + if (curlevel) { // let's not add single entries + uint32_t val = code >> (32 - numbits); + uint32_t nb = val + (1U << (numbits - l)); + info.len = l; + info.num = curlevel+1; + for (; val < nb; val++) + AV_COPY64(table+val, &info); + levelcnt[curlevel-1]++; + } + + if (curlevel+1 < max_symbols && newlimit >= minlen) { + add_level(table, is16bit, num, numbits, buf, + code, l, newlimit, curlevel+1, + minlen, max, levelcnt, info); + } + } + } +} + +static int vlc_multi_gen(VLC_MULTI_ELEM *table, const VLC *single, + const int is16bit, const int nb_codes, const int numbits, + VLCcode *buf, void *logctx) +{ + int minbits, maxbits, max; + unsigned count[VLC_MULTI_MAX_SYMBOLS-1] = { 0, }; + VLC_MULTI_ELEM info = { { 0, }, 0, 0, }; + int count0 = 0; + + for (int j = 0; j < 1<table[j].len > 0) { + count0 ++; + j += (1 << (numbits - single->table[j].len)) - 1; + } + } + + minbits = 32; + maxbits = 0; + + for (int n = nb_codes - count0; n < nb_codes; n++) { + minbits = FFMIN(minbits, buf[n].bits); + maxbits = FFMAX(maxbits, buf[n].bits); + } + av_assert0(maxbits <= numbits); + + for (max = nb_codes; max > nb_codes - count0; max--) { + // We can only add a code that fits with the shortest other code into the table + // We assume the table is sorted by bits and we skip subtables which from our + // point of view are basically random corrupted entries + // If we have not a single useable vlc we end with max = nb_codes + if (buf[max - 1].bits+minbits > numbits) + break; + } + + for (int j = 0; j < 1<table[j].len; + table[j].num = single->table[j].len > 0 ? 1 : 0; + if (is16bit) + AV_WN16(table[j].val, single->table[j].sym); + else + table[j].val[0] = single->table[j].sym; + } + + add_level(table, is16bit, nb_codes, numbits, buf, + 0, 0, FFMIN(maxbits, numbits), 0, minbits, max, count, info); + + av_log(logctx, AV_LOG_DEBUG, "Joint: %d/%d/%d/%d/%d codes min=%ubits max=%u\n", + count[0], count[1], count[2], count[3], count[4], minbits, max); + + return 0; +} + +int ff_vlc_init_multi_from_lengths(VLC *vlc, VLC_MULTI *multi, int nb_bits, int nb_elems, + int nb_codes, const int8_t *lens, int lens_wrap, + const void *symbols, int symbols_wrap, int symbols_size, + int offset, int flags, void *logctx) +{ + VLCcode localbuf[LOCALBUF_ELEMS], *buf = localbuf; + uint64_t code; + int ret, j, len_max = FFMIN(32, 3 * nb_bits); + + ret = vlc_common_init(vlc, nb_bits, nb_codes, &buf, flags); + if (ret < 0) + return ret; + + multi->table = av_malloc(sizeof(*multi->table) << nb_bits); + if (!multi->table) + goto fail; + + j = code = 0; + for (int i = 0; i < nb_codes; i++, lens += lens_wrap) { + int len = *lens; + if (len > 0) { + unsigned sym; + + buf[j].bits = len; + if (symbols) + GET_DATA(sym, symbols, i, symbols_wrap, symbols_size) + else + sym = i; + buf[j].symbol = sym + offset; + buf[j++].code = code; + } else if (len < 0) { + len = -len; + } else + continue; + if (len > len_max || code & ((1U << (32 - len)) - 1)) { + av_log(logctx, AV_LOG_ERROR, "Invalid VLC (length %u)\n", len); + goto fail; + } + code += 1U << (32 - len); + if (code > UINT32_MAX + 1ULL) { + av_log(logctx, AV_LOG_ERROR, "Overdetermined VLC tree\n"); + goto fail; + } + } + ret = vlc_common_end(vlc, nb_bits, j, buf, flags, buf); + if (ret < 0) + goto fail; + ret = vlc_multi_gen(multi->table, vlc, nb_elems > 256, j, nb_bits, buf, logctx); + if (buf != localbuf) + av_free(buf); + return ret; +fail: + if (buf != localbuf) + av_free(buf); + ff_vlc_free_multi(multi); + return AVERROR_INVALIDDATA; +} + +void ff_vlc_free_multi(VLC_MULTI *vlc) +{ + av_freep(&vlc->table); +} + +void ff_vlc_free(VLC *vlc) { av_freep(&vlc->table); } diff --git a/libavcodec/vlc.h b/libavcodec/vlc.h index e63c484755a..0cc106c499f 100644 --- a/libavcodec/vlc.h +++ b/libavcodec/vlc.h @@ -19,8 +19,13 @@ #ifndef AVCODEC_VLC_H #define AVCODEC_VLC_H +#include #include +#include "libavutil/macros.h" + +#define VLC_MULTI_MAX_SYMBOLS 6 + // When changing this, be sure to also update tableprint_vlc.h accordingly. typedef int16_t VLCBaseType; @@ -34,22 +39,62 @@ typedef struct VLC { int table_size, table_allocated; } VLC; +typedef struct VLC_MULTI_ELEM { + uint8_t val[VLC_MULTI_MAX_SYMBOLS]; + int8_t len; // -31,32 + uint8_t num; +} VLC_MULTI_ELEM; + +typedef struct VLC_MULTI { + VLC_MULTI_ELEM *table; + int table_size, table_allocated; +} VLC_MULTI; + typedef struct RL_VLC_ELEM { int16_t level; int8_t len; uint8_t run; } RL_VLC_ELEM; -#define init_vlc(vlc, nb_bits, nb_codes, \ +#define vlc_init(vlc, nb_bits, nb_codes, \ bits, bits_wrap, bits_size, \ codes, codes_wrap, codes_size, \ flags) \ - ff_init_vlc_sparse(vlc, nb_bits, nb_codes, \ + ff_vlc_init_sparse(vlc, nb_bits, nb_codes, \ bits, bits_wrap, bits_size, \ codes, codes_wrap, codes_size, \ NULL, 0, 0, flags) -int ff_init_vlc_sparse(VLC *vlc, int nb_bits, int nb_codes, +/** + * Build VLC decoding tables suitable for use with get_vlc2(). + * + * @param[in,out] vlc The VLC to be initialized; table and table_allocated + * must have been set when initializing a static VLC, + * otherwise this will be treated as uninitialized. + * @param[in] nb_bits The number of bits to use for the VLC table; + * higher values take up more memory and cache, but + * allow to read codes with fewer reads. + * Corresponds to the `bits` parameter of get_vlc2(). + * @param[in] nb_codes The number of provided bits, codes and (if supplied) + * symbol entries. + * @param[in] bits The lengths (in bits) of the codes. Entries > 0 + * correspond to valid codes; entries == 0 will be skipped. + * @param[in] bits_wrap Stride (in bytes) of the bits table. + * @param[in] codes_size Size of the bits. 1, 2 and 4 are supported. + * @param[in] codes Table which gives the bit pattern of of each vlc code. + * @param[in] codes_wrap Stride (in bytes) of the codes table. + * @param[in] codes_size Size of the codes. 1, 2 and 4 are supported. + * @param[in] symbols The symbols, i.e. what is returned from get_vlc2() + * when the corresponding code is encountered. + * May be NULL, then 0, 1, 2, 3, 4,... will be used. + * @param[in] symbols_wrap Stride (in bytes) of the symbols table. + * @param[in] symbols_size Size of the symbols. 1 and 2 are supported. + * @param[in] flags A combination of the VLC_INIT_* flags. + * + * 'wrap' and 'size' make it possible to use any memory configuration and types + * (byte/word/int) to store the 'bits', 'codes', and 'symbols' tables. + */ +int ff_vlc_init_sparse(VLC *vlc, int nb_bits, int nb_codes, const void *bits, int bits_wrap, int bits_size, const void *codes, int codes_wrap, int codes_size, const void *symbols, int symbols_wrap, int symbols_size, @@ -81,64 +126,162 @@ int ff_init_vlc_sparse(VLC *vlc, int nb_bits, int nb_codes, * @param[in] symbols_wrap Stride (in bytes) of the symbols. * @param[in] symbols_size Size of the symbols. 1 and 2 are supported. * @param[in] offset An offset to apply to all the valid symbols. - * @param[in] flags A combination of the INIT_VLC_* flags; notice that - * INIT_VLC_INPUT_LE is pointless and ignored. + * @param[in] flags A combination of the VLC_INIT_* flags; notice that + * VLC_INIT_INPUT_LE is pointless and ignored. */ -int ff_init_vlc_from_lengths(VLC *vlc, int nb_bits, int nb_codes, +int ff_vlc_init_from_lengths(VLC *vlc, int nb_bits, int nb_codes, const int8_t *lens, int lens_wrap, const void *symbols, int symbols_wrap, int symbols_size, int offset, int flags, void *logctx); -void ff_free_vlc(VLC *vlc); +/** + * Build VLC decoding tables suitable for use with get_vlc_multi() + * + * This function takes lengths and symbols and calculates the codes from them. + * For this the input lengths and symbols have to be sorted according to "left + * nodes in the corresponding tree first". + * + * @param[in,out] vlc The VLC to be initialized; table and table_allocated + * must have been set when initializing a static VLC, + * otherwise this will be treated as uninitialized. + * @param[in,out] multi The VLC_MULTI to be initialized; table and table_allocated + * must have been set when initializing a static VLC, + * otherwise this will be treated as uninitialized. + * @param[in] nb_bits The number of bits to use for the VLC table; + * higher values take up more memory and cache, but + * allow to read codes with fewer reads. + * @param[in] nb_elems The max possible number of elements. + * @param[in] nb_codes The number of provided length and (if supplied) symbol + * entries. + * @param[in] lens The lengths of the codes. Entries > 0 correspond to + * valid codes; entries == 0 will be skipped and entries + * with len < 0 indicate that the tree is incomplete and + * has an open end of length -len at this position. + * @param[in] lens_wrap Stride (in bytes) of the lengths. + * @param[in] symbols The symbols, i.e. what is returned from get_vlc2() + * when the corresponding code is encountered. + * May be NULL, then 0, 1, 2, 3, 4,... will be used. + * @param[in] symbols_wrap Stride (in bytes) of the symbols. + * @param[in] symbols_size Size of the symbols. 1 and 2 are supported. + * @param[in] offset An offset to apply to all the valid symbols. + * @param[in] flags A combination of the VLC_INIT_* flags; notice that + * VLC_INIT_INPUT_LE is pointless and ignored. + */ +int ff_vlc_init_multi_from_lengths(VLC *vlc, VLC_MULTI *multi, int nb_bits, int nb_elems, + int nb_codes, const int8_t *lens, int lens_wrap, + const void *symbols, int symbols_wrap, int symbols_size, + int offset, int flags, void *logctx); + -/* If INIT_VLC_INPUT_LE is set, the LSB bit of the codes used to +void ff_vlc_free_multi(VLC_MULTI *vlc); +void ff_vlc_free(VLC *vlc); + +#define VLC_INIT_USE_STATIC 1 +#define VLC_INIT_STATIC_OVERLONG (2 | VLC_INIT_USE_STATIC) +/* If VLC_INIT_INPUT_LE is set, the LSB bit of the codes used to * initialize the VLC table is the first bit to be read. */ -#define INIT_VLC_INPUT_LE 2 +#define VLC_INIT_INPUT_LE 4 /* If set the VLC is intended for a little endian bitstream reader. */ -#define INIT_VLC_OUTPUT_LE 8 -#define INIT_VLC_LE (INIT_VLC_INPUT_LE | INIT_VLC_OUTPUT_LE) -#define INIT_VLC_USE_NEW_STATIC 4 -#define INIT_VLC_STATIC_OVERLONG (1 | INIT_VLC_USE_NEW_STATIC) - -#define INIT_CUSTOM_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, \ - h, i, j, flags, static_size) \ - do { \ - static VLCElem table[static_size]; \ - (vlc)->table = table; \ - (vlc)->table_allocated = static_size; \ - ff_init_vlc_sparse(vlc, bits, a, b, c, d, e, f, g, h, i, j, \ - flags | INIT_VLC_USE_NEW_STATIC); \ - } while (0) - -#define INIT_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, h, i, j, static_size) \ - INIT_CUSTOM_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, \ - h, i, j, 0, static_size) - -#define INIT_LE_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, h, i, j, static_size) \ - INIT_CUSTOM_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, \ - h, i, j, INIT_VLC_LE, static_size) - -#define INIT_CUSTOM_VLC_STATIC(vlc, bits, a, b, c, d, e, f, g, flags, static_size) \ - INIT_CUSTOM_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, \ - NULL, 0, 0, flags, static_size) - -#define INIT_VLC_STATIC(vlc, bits, a, b, c, d, e, f, g, static_size) \ - INIT_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, NULL, 0, 0, static_size) - -#define INIT_LE_VLC_STATIC(vlc, bits, a, b, c, d, e, f, g, static_size) \ - INIT_LE_VLC_SPARSE_STATIC(vlc, bits, a, b, c, d, e, f, g, NULL, 0, 0, static_size) - -#define INIT_VLC_STATIC_FROM_LENGTHS(vlc, bits, nb_codes, lens, len_wrap, \ +#define VLC_INIT_OUTPUT_LE 8 +#define VLC_INIT_LE (VLC_INIT_INPUT_LE | VLC_INIT_OUTPUT_LE) + +/** + * For static VLCs, the number of bits can often be hardcoded + * at each get_vlc2() callsite. Then using a full VLC would be uneconomical, + * because only VLC.table would ever be accessed after initialization. + * The following functions provide wrappers around the relevant ff_vlc_init_* + * functions suitable for said task. + * + * The ff_vlc_init_tables_* functions are intended to be used for initializing + * a series of VLCs. The user initializes a VLCInitState with the details + * about the underlying array of VLCElem; it is automatically updated by + * the ff_vlc_init_tables_* functions (i.e. table is incremented and size + * decremented by the number of elements of the current table). + * The VLC_INIT_STATIC_OVERLONG flag is also automatically added. + * These functions return a pointer to the table just initialized, + * potentially to be used in arrays of pointer to VLC tables. + * + * The ff_vlc_init_table_* functions are intended to be used for initializing + * a single VLC table, given by table and table_size. The VLC_INIT_USE_STATIC + * flag is automatically added. + */ + +typedef struct VLCInitState { + VLCElem *table; ///< points to where the next VLC table will be placed + unsigned size; ///< remaining number of elements in table +} VLCInitState; + +#define VLC_INIT_STATE(_table) { .table = (_table), .size = FF_ARRAY_ELEMS(_table) } + +void ff_vlc_init_table_from_lengths(VLCElem table[], int table_size, + int nb_bits, int nb_codes, + const int8_t *lens, int lens_wrap, + const void *symbols, int symbols_wrap, int symbols_size, + int offset, int flags); + +const VLCElem *ff_vlc_init_tables_from_lengths(VLCInitState *state, + int nb_bits, int nb_codes, + const int8_t *lens, int lens_wrap, + const void *symbols, int symbols_wrap, int symbols_size, + int offset, int flags); + +void ff_vlc_init_table_sparse(VLCElem table[], int table_size, + int nb_bits, int nb_codes, + const void *bits, int bits_wrap, int bits_size, + const void *codes, int codes_wrap, int codes_size, + const void *symbols, int symbols_wrap, int symbols_size, + int flags); + +const VLCElem *ff_vlc_init_tables_sparse(VLCInitState *state, + int nb_bits, int nb_codes, + const void *bits, int bits_wrap, int bits_size, + const void *codes, int codes_wrap, int codes_size, + const void *symbols, int symbols_wrap, int symbols_size, + int flags); + +static inline +const VLCElem *ff_vlc_init_tables(VLCInitState *state, + int nb_bits, int nb_codes, + const void *bits, int bits_wrap, int bits_size, + const void *codes, int codes_wrap, int codes_size, + int flags) +{ + return ff_vlc_init_tables_sparse(state, nb_bits, nb_codes, + bits, bits_wrap, bits_size, + codes, codes_wrap, codes_size, + NULL, 0, 0, flags); +} + +#define VLC_INIT_STATIC_SPARSE_TABLE(vlc_table, nb_bits, nb_codes, \ + bits, bits_wrap, bits_size, \ + codes, codes_wrap, codes_size, \ symbols, symbols_wrap, symbols_size, \ - offset, flags, static_size) \ - do { \ - static VLCElem table[static_size]; \ - (vlc)->table = table; \ - (vlc)->table_allocated = static_size; \ - ff_init_vlc_from_lengths(vlc, bits, nb_codes, lens, len_wrap, \ - symbols, symbols_wrap, symbols_size, \ - offset, flags | INIT_VLC_USE_NEW_STATIC, \ - NULL); \ - } while (0) + flags) \ + ff_vlc_init_table_sparse(vlc_table, FF_ARRAY_ELEMS(vlc_table), \ + (nb_bits), (nb_codes), \ + (bits), (bits_wrap), (bits_size), \ + (codes), (codes_wrap), (codes_size), \ + (symbols), (symbols_wrap), (symbols_size), \ + (flags)) + +#define VLC_INIT_STATIC_TABLE(vlc_table, nb_bits, nb_codes, \ + bits, bits_wrap, bits_size, \ + codes, codes_wrap, codes_size, \ + flags) \ + ff_vlc_init_table_sparse(vlc_table, FF_ARRAY_ELEMS(vlc_table), \ + (nb_bits), (nb_codes), \ + (bits), (bits_wrap), (bits_size), \ + (codes), (codes_wrap), (codes_size), \ + NULL, 0, 0, (flags)) + +#define VLC_INIT_STATIC_TABLE_FROM_LENGTHS(vlc_table, nb_bits, nb_codes, \ + lens, lens_wrap, \ + syms, syms_wrap, syms_size, \ + offset, flags) \ + ff_vlc_init_table_from_lengths(vlc_table, FF_ARRAY_ELEMS(vlc_table), \ + (nb_bits), (nb_codes), \ + (lens), (lens_wrap), \ + (syms), (syms_wrap), (syms_size), \ + (offset), (flags)) #endif /* AVCODEC_VLC_H */ diff --git a/libavcodec/vmdvideo.c b/libavcodec/vmdvideo.c index 226ae0d316d..2da8af31f50 100644 --- a/libavcodec/vmdvideo.c +++ b/libavcodec/vmdvideo.c @@ -454,8 +454,7 @@ static int vmdvideo_decode_frame(AVCodecContext *avctx, AVFrame *frame, memcpy(frame->data[1], s->palette, PALETTE_COUNT * 4); /* shuffle frames */ - av_frame_unref(s->prev_frame); - if ((ret = av_frame_ref(s->prev_frame, frame)) < 0) + if ((ret = av_frame_replace(s->prev_frame, frame)) < 0) return ret; *got_frame = 1; diff --git a/libavcodec/vmixdec.c b/libavcodec/vmixdec.c new file mode 100644 index 00000000000..ab283d13db5 --- /dev/null +++ b/libavcodec/vmixdec.c @@ -0,0 +1,318 @@ +/* + * vMix decoder + * 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 +#include +#include + +#include "libavutil/intreadwrite.h" +#include "libavutil/mem_internal.h" + +#include "avcodec.h" +#include "codec_internal.h" +#include "decode.h" +#define CACHED_BITSTREAM_READER !ARCH_X86_32 +#include "golomb.h" +#include "get_bits.h" +#include "idctdsp.h" +#include "thread.h" + +typedef struct SliceContext { + const uint8_t *dc_ptr; + const uint8_t *ac_ptr; + unsigned dc_size; + unsigned ac_size; +} SliceContext; + +typedef struct VMIXContext { + int nb_slices; + int lshift; + + int16_t factors[64]; + uint8_t scan[64]; + + SliceContext *slices; + unsigned int slices_size; + + IDCTDSPContext idsp; +} VMIXContext; + +static const uint8_t quality[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1,64,63,62,61, + 60,59,58,57,56,55,54,53,52,51, + 50,49,48,47,46,45,44,43,42,41, + 40,39,38,37,36,35,34,33,32,31, + 30,29,28,27,26,25,24,23,22,21, + 20,19,18,17,16,15,14,13,12,11, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, +}; + +static const uint8_t quant[64] = { + 16, 16, 19, 22, 22, 26, 26, 27, + 16, 16, 22, 22, 26, 27, 27, 29, + 19, 22, 26, 26, 27, 29, 29, 35, + 22, 24, 27, 27, 29, 32, 34, 38, + 26, 27, 29, 29, 32, 35, 38, 46, + 27, 29, 34, 34, 35, 40, 46, 56, + 29, 34, 34, 37, 40, 48, 56, 69, + 34, 37, 38, 40, 48, 58, 69, 83, +}; + +static av_cold int decode_init(AVCodecContext *avctx) +{ + VMIXContext *s = avctx->priv_data; + + avctx->bits_per_raw_sample = 8; + avctx->pix_fmt = AV_PIX_FMT_YUV422P; + + avctx->coded_width = FFALIGN(avctx->width, 16); + avctx->coded_height = FFALIGN(avctx->height, 16); + + ff_idctdsp_init(&s->idsp, avctx); + ff_permute_scantable(s->scan, ff_zigzag_direct, + s->idsp.idct_permutation); + return 0; +} + +static inline int get_se_golomb_vmix(GetBitContext *gb) +{ + unsigned int buf = get_ue_golomb_long(gb); + int sign = (buf & 1) - 1; + return ((buf >> 1) ^ (~sign)); +} + +static int decode_dcac(AVCodecContext *avctx, + GetBitContext *dc_gb, GetBitContext *ac_gb, + unsigned *dcrun, unsigned *acrun, + AVFrame *frame, int width, int by, int plane) +{ + const ptrdiff_t linesize = frame->linesize[plane]; + uint8_t *dst = frame->data[plane] + by * linesize; + unsigned dc_run = *dcrun, ac_run = *acrun; + LOCAL_ALIGNED_32(int16_t, block, [64]); + VMIXContext *s = avctx->priv_data; + const int16_t *factors = s->factors; + const uint8_t *scan = s->scan; + const int add = plane ? 0 : 1024; + int i, dc_v = 0, ac_v = 0, dc = 0; + const int lshift = s->lshift; + + for (int y = 0; y < 2; y++) { + for (int x = 0; x < width; x += 8) { + memset(block, 0, sizeof(*block)*64); + + if (dc_run > 0) { + dc_run--; + } else { + if (get_bits_left(dc_gb) < 1) + return AVERROR_INVALIDDATA; + dc_v = get_se_golomb_vmix(dc_gb); + dc += (unsigned)dc_v; + if (!dc_v) + dc_run = get_ue_golomb_long(dc_gb); + } + + for (int n = 0; n < 64; n++) { + if (ac_run > 0) { + ac_run--; + continue; + } + + if (get_bits_left(ac_gb) < 1) + return AVERROR_INVALIDDATA; + ac_v = get_se_golomb_vmix(ac_gb); + i = scan[n]; + block[i] = ((unsigned)ac_v * factors[i]) >> 4; + if (!ac_v) + ac_run = get_ue_golomb_long(ac_gb); + } + + block[0] = ((unsigned)dc << lshift) + (unsigned)add; + s->idsp.idct_put(dst + x, linesize, block); + } + + dst += 8 * linesize; + } + + *dcrun = dc_run; + *acrun = ac_run; + + return 0; +} + +static int decode_slice(AVCodecContext *avctx, AVFrame *frame, + const uint8_t *dc_src, unsigned dc_slice_size, + const uint8_t *ac_src, unsigned ac_slice_size, + int by) +{ + unsigned dc_run = 0, ac_run = 0; + GetBitContext dc_gb, ac_gb; + int ret; + + ret = init_get_bits8(&dc_gb, dc_src, dc_slice_size); + if (ret < 0) + return ret; + + ret = init_get_bits8(&ac_gb, ac_src, ac_slice_size); + if (ret < 0) + return ret; + + for (int p = 0; p < 3; p++) { + const int rshift = !!p; + ret = decode_dcac(avctx, &dc_gb, &ac_gb, + &dc_run, &ac_run, frame, + frame->width >> rshift, by, p); + if (ret < 0) + return ret; + + if (get_bits_left(&dc_gb) < 0) + return AVERROR_INVALIDDATA; + if (get_bits_left(&ac_gb) < 0) + return AVERROR_INVALIDDATA; + + align_get_bits(&dc_gb); + align_get_bits(&ac_gb); + } + + if (get_bits_left(&dc_gb) > 0) + return AVERROR_INVALIDDATA; + if (get_bits_left(&ac_gb) > 0) + return AVERROR_INVALIDDATA; + + return 0; +} + +static int decode_slices(AVCodecContext *avctx, void *arg, + int n, int thread_nb) +{ + VMIXContext *s = avctx->priv_data; + const uint8_t *dc_slice_ptr = s->slices[n].dc_ptr; + const uint8_t *ac_slice_ptr = s->slices[n].ac_ptr; + unsigned dc_slice_size = s->slices[n].dc_size; + unsigned ac_slice_size = s->slices[n].ac_size; + AVFrame *frame = arg; + + return decode_slice(avctx, frame, dc_slice_ptr, dc_slice_size, + ac_slice_ptr, ac_slice_size, n * 16); +} + +static int decode_frame(AVCodecContext *avctx, + AVFrame *frame, int *got_frame, + AVPacket *avpkt) +{ + VMIXContext *s = avctx->priv_data; + unsigned offset, q; + int ret; + + if (avpkt->size <= 7) + return AVERROR_INVALIDDATA; + + s->lshift = 0; + offset = 2 + avpkt->data[0]; + if (offset == 5) + s->lshift = avpkt->data[1]; + else if (offset != 3) + return AVERROR_INVALIDDATA; + + if (s->lshift > 31) + return AVERROR_INVALIDDATA; + + q = quality[FFMIN(avpkt->data[offset - 2], FF_ARRAY_ELEMS(quality)-1)]; + for (int n = 0; n < 64; n++) + s->factors[n] = quant[n] * q; + + s->nb_slices = (avctx->height + 15) / 16; + av_fast_mallocz(&s->slices, &s->slices_size, s->nb_slices * sizeof(*s->slices)); + if (!s->slices) + return AVERROR(ENOMEM); + + for (int n = 0; n < s->nb_slices; n++) { + unsigned slice_size; + + if (offset + 4 > avpkt->size) + return AVERROR_INVALIDDATA; + + slice_size = AV_RL32(avpkt->data + offset); + if (slice_size > avpkt->size) + return AVERROR_INVALIDDATA; + + if (avpkt->size - slice_size - 4LL < offset) + return AVERROR_INVALIDDATA; + + s->slices[n].dc_size = slice_size; + s->slices[n].dc_ptr = avpkt->data + offset + 4; + offset += slice_size + 4; + } + + for (int n = 0; n < s->nb_slices; n++) { + unsigned slice_size; + + if (offset + 4 > avpkt->size) + return AVERROR_INVALIDDATA; + + slice_size = AV_RL32(avpkt->data + offset); + if (slice_size > avpkt->size) + return AVERROR_INVALIDDATA; + + if (avpkt->size - slice_size - 4LL < offset) + return AVERROR_INVALIDDATA; + + s->slices[n].ac_size = slice_size; + s->slices[n].ac_ptr = avpkt->data + offset + 4; + offset += slice_size + 4; + } + + ret = ff_thread_get_buffer(avctx, frame, 0); + if (ret < 0) + return ret; + + avctx->execute2(avctx, decode_slices, frame, NULL, s->nb_slices); + + frame->pict_type = AV_PICTURE_TYPE_I; + frame->flags |= AV_FRAME_FLAG_KEY; + + *got_frame = 1; + + return avpkt->size; +} + +static av_cold int decode_end(AVCodecContext *avctx) +{ + VMIXContext *s = avctx->priv_data; + av_freep(&s->slices); + return 0; +} + +const FFCodec ff_vmix_decoder = { + .p.name = "vmix", + CODEC_LONG_NAME("vMix Video"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VMIX, + .priv_data_size = sizeof(VMIXContext), + .init = decode_init, + .close = decode_end, + FF_CODEC_DECODE_CB(decode_frame), + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_SLICE_THREADS, +}; diff --git a/libavcodec/vmnc.c b/libavcodec/vmnc.c index 8daaf08c5d3..17e3a2f3c18 100644 --- a/libavcodec/vmnc.c +++ b/libavcodec/vmnc.c @@ -339,7 +339,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0) return ret; - c->pic->key_frame = 0; + c->pic->flags &= ~AV_FRAME_FLAG_KEY; c->pic->pict_type = AV_PICTURE_TYPE_P; // restore screen after cursor @@ -441,7 +441,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *rframe, bytestream2_skip(gb, 4); break; case MAGIC_WMVi: // ServerInitialization struct - c->pic->key_frame = 1; + c->pic->flags |= AV_FRAME_FLAG_KEY; c->pic->pict_type = AV_PICTURE_TYPE_I; depth = bytestream2_get_byte(gb); if (depth != c->bpp) { diff --git a/libavcodec/vorbis.c b/libavcodec/vorbis.c index d0b660b44ac..3d859975763 100644 --- a/libavcodec/vorbis.c +++ b/libavcodec/vorbis.c @@ -27,8 +27,10 @@ */ #include "libavutil/common.h" +#include "libavutil/error.h" +#include "libavutil/log.h" +#include "libavutil/macros.h" -#include "avcodec.h" #include "vorbis.h" #include "vorbis_data.h" @@ -104,7 +106,7 @@ int ff_vorbis_len2vlc(uint8_t *bits, uint32_t *codes, unsigned num) return 0; } -int ff_vorbis_ready_floor1_list(AVCodecContext *avctx, +int ff_vorbis_ready_floor1_list(void *logctx, vorbis_floor1_entry *list, int values) { int i; @@ -130,7 +132,7 @@ int ff_vorbis_ready_floor1_list(AVCodecContext *avctx, int j; for (j = i + 1; j < values; j++) { if (list[i].x == list[j].x) { - av_log(avctx, AV_LOG_ERROR, + av_log(logctx, AV_LOG_ERROR, "Duplicate value found in floor 1 X coordinates\n"); return AVERROR_INVALIDDATA; } diff --git a/libavcodec/vorbis.h b/libavcodec/vorbis.h index aa1ec5719d6..7190465f0df 100644 --- a/libavcodec/vorbis.h +++ b/libavcodec/vorbis.h @@ -23,8 +23,6 @@ #include -#include "avcodec.h" - typedef struct vorbis_floor1_entry { uint16_t x; uint16_t sort; @@ -32,7 +30,7 @@ typedef struct vorbis_floor1_entry { uint16_t high; } vorbis_floor1_entry; -int ff_vorbis_ready_floor1_list(AVCodecContext *avctx, +int ff_vorbis_ready_floor1_list(void *logctx, vorbis_floor1_entry *list, int values); unsigned int ff_vorbis_nth_root(unsigned int x, unsigned int n); // x^(1/n) int ff_vorbis_len2vlc(uint8_t *bits, uint32_t *codes, unsigned num); diff --git a/libavcodec/vorbis_data.c b/libavcodec/vorbis_data.c index 1ebe146d8f3..d7edc695c15 100644 --- a/libavcodec/vorbis_data.c +++ b/libavcodec/vorbis_data.c @@ -34,20 +34,6 @@ const uint8_t ff_vorbis_channel_layout_offsets[8][8] = { { 0, 2, 1, 7, 5, 6, 3, 4 }, }; -#if FF_API_OLD_CHANNEL_LAYOUT -const uint64_t ff_vorbis_channel_layouts[9] = { - AV_CH_LAYOUT_MONO, - AV_CH_LAYOUT_STEREO, - AV_CH_LAYOUT_SURROUND, - AV_CH_LAYOUT_QUAD, - AV_CH_LAYOUT_5POINT0_BACK, - AV_CH_LAYOUT_5POINT1_BACK, - AV_CH_LAYOUT_5POINT1|AV_CH_BACK_CENTER, - AV_CH_LAYOUT_7POINT1, - 0 -}; -#endif - const AVChannelLayout ff_vorbis_ch_layouts[9] = { AV_CHANNEL_LAYOUT_MONO, AV_CHANNEL_LAYOUT_STEREO, diff --git a/libavcodec/vorbis_data.h b/libavcodec/vorbis_data.h index 0fe19e509f3..327e5ab2efd 100644 --- a/libavcodec/vorbis_data.h +++ b/libavcodec/vorbis_data.h @@ -21,14 +21,14 @@ #include +#include "libavutil/attributes_internal.h" #include "libavutil/channel_layout.h" +FF_VISIBILITY_PUSH_HIDDEN extern const float ff_vorbis_floor1_inverse_db_table[256]; extern const float * const ff_vorbis_vwin[8]; extern const uint8_t ff_vorbis_channel_layout_offsets[8][8]; -#if FF_API_OLD_CHANNEL_LAYOUT -extern const uint64_t ff_vorbis_channel_layouts[9]; -#endif extern const AVChannelLayout ff_vorbis_ch_layouts[9]; +FF_VISIBILITY_POP_HIDDEN #endif /* AVCODEC_VORBIS_DATA_H */ diff --git a/libavcodec/vorbis_parser.c b/libavcodec/vorbis_parser.c index a7d15d4ce99..d2c9e647ce5 100644 --- a/libavcodec/vorbis_parser.c +++ b/libavcodec/vorbis_parser.c @@ -234,7 +234,8 @@ int av_vorbis_parse_frame_flags(AVVorbisParseContext *s, const uint8_t *buf, else if (buf[0] == 5) *flags |= VORBIS_FLAG_SETUP; else - goto bad_packet; + av_log(s, AV_LOG_VERBOSE, "Ignoring packet with unknown type %u\n", + buf[0]); /* Special packets have no duration. */ return 0; diff --git a/libavcodec/vorbisdec.c b/libavcodec/vorbisdec.c index 1d2a0997607..8462739c147 100644 --- a/libavcodec/vorbisdec.c +++ b/libavcodec/vorbisdec.c @@ -210,7 +210,7 @@ static void vorbis_free(vorbis_context *vc) if (vc->codebooks) for (i = 0; i < vc->codebook_count; ++i) { av_freep(&vc->codebooks[i].codevectors); - ff_free_vlc(&vc->codebooks[i].vlc); + ff_vlc_free(&vc->codebooks[i].vlc); } av_freep(&vc->codebooks); @@ -454,11 +454,11 @@ static int vorbis_parse_setup_hdr_codebooks(vorbis_context *vc) codebook_setup->maxdepth = (codebook_setup->maxdepth+codebook_setup->nb_bits - 1) / codebook_setup->nb_bits; - if ((ret = init_vlc(&codebook_setup->vlc, codebook_setup->nb_bits, + if ((ret = vlc_init(&codebook_setup->vlc, codebook_setup->nb_bits, entries, tmp_vlc_bits, sizeof(*tmp_vlc_bits), sizeof(*tmp_vlc_bits), tmp_vlc_codes, sizeof(*tmp_vlc_codes), sizeof(*tmp_vlc_codes), - INIT_VLC_LE))) { + VLC_INIT_LE))) { av_log(vc->avctx, AV_LOG_ERROR, " Error generating vlc tables. \n"); goto error; } @@ -1468,6 +1468,9 @@ static av_always_inline int vorbis_residue_decode_internal(vorbis_context *vc, unsigned step = FASTDIV(vr->partition_size << 1, dim << 1); vorbis_codebook codebook = vc->codebooks[vqbook]; + if (get_bits_left(gb) <= 0) + return AVERROR_INVALIDDATA; + if (vr_type == 0) { voffs = voffset+j*vlen; @@ -1890,7 +1893,6 @@ const FFCodec ff_vorbis_decoder = { .flush = vorbis_decode_flush, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_CHANNEL_CONF, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, - CODEC_OLD_CHANNEL_LAYOUTS_ARRAY(ff_vorbis_channel_layouts) .p.ch_layouts = ff_vorbis_ch_layouts, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, diff --git a/libavcodec/vp3.c b/libavcodec/vp3.c index 22348559461..96b0b66005f 100644 --- a/libavcodec/vp3.c +++ b/libavcodec/vp3.c @@ -35,16 +35,20 @@ #include #include +#include "libavutil/emms.h" #include "libavutil/imgutils.h" #include "libavutil/mem_internal.h" +#include "libavutil/thread.h" #include "avcodec.h" #include "codec_internal.h" #include "decode.h" #include "get_bits.h" #include "hpeldsp.h" +#include "internal.h" #include "jpegquanttables.h" #include "mathops.h" +#include "refstruct.h" #include "thread.h" #include "threadframe.h" #include "videodsp.h" @@ -157,6 +161,18 @@ static const uint8_t vp4_pred_block_type_map[8] = { [MODE_INTER_FOURMV] = VP4_DC_INTER, }; +static VLCElem superblock_run_length_vlc[88]; /* version < 2 */ +static VLCElem fragment_run_length_vlc[56]; /* version < 2 */ +static VLCElem motion_vector_vlc[112]; /* version < 2 */ + +// The VP4 tables reuse this vlc. +static VLCElem mode_code_vlc[24 + 2108 * CONFIG_VP4_DECODER]; + +#if CONFIG_VP4_DECODER +static const VLCElem *vp4_mv_vlc_table[2][7]; /* version >= 2 */ +static const VLCElem *block_pattern_vlc[2]; /* version >= 2 */ +#endif + typedef struct { int dc; int type; @@ -173,6 +189,11 @@ typedef struct HuffTable { uint8_t nb_entries; } HuffTable; +typedef struct CoeffVLCs { + const VLCElem *vlc_tabs[80]; + VLC vlcs[80]; +} CoeffVLCs; + typedef struct Vp3DecodeContext { AVCodecContext *avctx; int theora, theora_tables, theora_header; @@ -275,16 +296,12 @@ typedef struct Vp3DecodeContext { int *nkf_coded_fragment_list; int num_kf_coded_fragment[3]; - /* The first 16 of the following VLCs are for the dc coefficients; - the others are four groups of 16 VLCs each for ac coefficients. */ - VLC coeff_vlc[5 * 16]; - - VLC superblock_run_length_vlc; /* version < 2 */ - VLC fragment_run_length_vlc; /* version < 2 */ - VLC block_pattern_vlc[2]; /* version >= 2*/ - VLC mode_code_vlc; - VLC motion_vector_vlc; /* version < 2 */ - VLC vp4_mv_vlc[2][7]; /* version >=2 */ + /** + * The first 16 of the following VLCs are for the dc coefficients; + * the others are four groups of 16 VLCs each for ac coefficients. + * This is a RefStruct reference to share these VLCs between threads. + */ + CoeffVLCs *coeff_vlc; /* these arrays need to be on 16-byte boundaries since SSE2 operations * index into them */ @@ -336,17 +353,16 @@ static void vp3_decode_flush(AVCodecContext *avctx) Vp3DecodeContext *s = avctx->priv_data; if (s->golden_frame.f) - ff_thread_release_ext_buffer(avctx, &s->golden_frame); + ff_thread_release_ext_buffer(&s->golden_frame); if (s->last_frame.f) - ff_thread_release_ext_buffer(avctx, &s->last_frame); + ff_thread_release_ext_buffer(&s->last_frame); if (s->current_frame.f) - ff_thread_release_ext_buffer(avctx, &s->current_frame); + ff_thread_release_ext_buffer(&s->current_frame); } static av_cold int vp3_decode_end(AVCodecContext *avctx) { Vp3DecodeContext *s = avctx->priv_data; - int i, j; free_tables(avctx); av_freep(&s->edge_emu_buffer); @@ -359,20 +375,8 @@ static av_cold int vp3_decode_end(AVCodecContext *avctx) av_frame_free(&s->last_frame.f); av_frame_free(&s->golden_frame.f); - for (i = 0; i < FF_ARRAY_ELEMS(s->coeff_vlc); i++) - ff_free_vlc(&s->coeff_vlc[i]); - - ff_free_vlc(&s->superblock_run_length_vlc); - ff_free_vlc(&s->fragment_run_length_vlc); - ff_free_vlc(&s->mode_code_vlc); - ff_free_vlc(&s->motion_vector_vlc); + ff_refstruct_unref(&s->coeff_vlc); - for (j = 0; j < 2; j++) - for (i = 0; i < 7; i++) - ff_free_vlc(&s->vp4_mv_vlc[j][i]); - - for (i = 0; i < 2; i++) - ff_free_vlc(&s->block_pattern_vlc[i]); return 0; } @@ -385,10 +389,9 @@ static av_cold int vp3_decode_end(AVCodecContext *avctx) */ static int init_block_mapping(Vp3DecodeContext *s) { - int sb_x, sb_y, plane; - int x, y, i, j = 0; + int j = 0; - for (plane = 0; plane < 3; plane++) { + for (int plane = 0; plane < 3; plane++) { int sb_width = plane ? s->c_superblock_width : s->y_superblock_width; int sb_height = plane ? s->c_superblock_height @@ -396,11 +399,11 @@ static int init_block_mapping(Vp3DecodeContext *s) int frag_width = s->fragment_width[!!plane]; int frag_height = s->fragment_height[!!plane]; - for (sb_y = 0; sb_y < sb_height; sb_y++) - for (sb_x = 0; sb_x < sb_width; sb_x++) - for (i = 0; i < 16; i++) { - x = 4 * sb_x + hilbert_offset[i][0]; - y = 4 * sb_y + hilbert_offset[i][1]; + for (int sb_y = 0; sb_y < sb_height; sb_y++) + for (int sb_x = 0; sb_x < sb_width; sb_x++) + for (int i = 0; i < 16; i++) { + int x = 4 * sb_x + hilbert_offset[i][0]; + int y = 4 * sb_y + hilbert_offset[i][1]; if (x < frag_width && y < frag_height) s->superblock_fragments[j++] = s->fragment_start[plane] + @@ -420,12 +423,11 @@ static int init_block_mapping(Vp3DecodeContext *s) static void init_dequantizer(Vp3DecodeContext *s, int qpi) { int ac_scale_factor = s->coded_ac_scale_factor[s->qps[qpi]]; - int i, plane, inter, qri, bmi, bmj, qistart; - for (inter = 0; inter < 2; inter++) { - for (plane = 0; plane < 3; plane++) { + for (int inter = 0; inter < 2; inter++) { + for (int plane = 0; plane < 3; plane++) { int dc_scale_factor = s->coded_dc_scale_factor[!!plane][s->qps[qpi]]; - int sum = 0; + int sum = 0, bmi, bmj, qistart, qri; for (qri = 0; qri < s->qr_count[inter][plane]; qri++) { sum += s->qr_size[inter][plane][qri]; if (s->qps[qpi] <= sum) @@ -434,7 +436,7 @@ static void init_dequantizer(Vp3DecodeContext *s, int qpi) qistart = sum - s->qr_size[inter][plane][qri]; bmi = s->qr_base[inter][plane][qri]; bmj = s->qr_base[inter][plane][qri + 1]; - for (i = 0; i < 64; i++) { + for (int i = 0; i < 64; i++) { int coeff = (2 * (sum - s->qps[qpi]) * s->base_matrix[bmi][i] - 2 * (qistart - s->qps[qpi]) * s->base_matrix[bmj][i] + s->qr_size[inter][plane][qri]) / @@ -471,7 +473,7 @@ static void init_loop_filter(Vp3DecodeContext *s) */ static int unpack_superblocks(Vp3DecodeContext *s, GetBitContext *gb) { - int superblock_starts[3] = { + const int superblock_starts[3] = { 0, s->u_superblock_start, s->v_superblock_start }; int bit = 0; @@ -479,9 +481,7 @@ static int unpack_superblocks(Vp3DecodeContext *s, GetBitContext *gb) int current_run = 0; int num_partial_superblocks = 0; - int i, j; int current_fragment; - int plane; int plane0_num_coded_frags = 0; if (s->keyframe) { @@ -497,7 +497,7 @@ static int unpack_superblocks(Vp3DecodeContext *s, GetBitContext *gb) else bit ^= 1; - current_run = get_vlc2(gb, s->superblock_run_length_vlc.table, + current_run = get_vlc2(gb, superblock_run_length_vlc, SUPERBLOCK_VLC_BITS, 2); if (current_run == 34) current_run += get_bits(gb, 12); @@ -531,12 +531,12 @@ static int unpack_superblocks(Vp3DecodeContext *s, GetBitContext *gb) else bit ^= 1; - current_run = get_vlc2(gb, s->superblock_run_length_vlc.table, + current_run = get_vlc2(gb, superblock_run_length_vlc, SUPERBLOCK_VLC_BITS, 2); if (current_run == 34) current_run += get_bits(gb, 12); - for (j = 0; j < current_run; current_superblock++) { + for (int j = 0; j < current_run; current_superblock++) { if (current_superblock >= s->superblock_count) { av_log(s->avctx, AV_LOG_ERROR, "Invalid fully coded superblock run length\n"); @@ -572,7 +572,7 @@ static int unpack_superblocks(Vp3DecodeContext *s, GetBitContext *gb) s->coded_fragment_list[0] = s->keyframe ? s->kf_coded_fragment_list : s->nkf_coded_fragment_list; - for (plane = 0; plane < 3; plane++) { + for (int plane = 0; plane < 3; plane++) { int sb_start = superblock_starts[plane]; int sb_end = sb_start + (plane ? s->c_superblock_count : s->y_superblock_count); @@ -580,9 +580,9 @@ static int unpack_superblocks(Vp3DecodeContext *s, GetBitContext *gb) if (s->keyframe) { if (s->num_kf_coded_fragment[plane] == -1) { - for (i = sb_start; i < sb_end; i++) { + for (int i = sb_start; i < sb_end; i++) { /* iterate through all 16 fragments in a superblock */ - for (j = 0; j < 16; j++) { + for (int j = 0; j < 16; j++) { /* if the fragment is in bounds, check its coding status */ current_fragment = s->superblock_fragments[i * 16 + j]; if (current_fragment != -1) { @@ -595,12 +595,12 @@ static int unpack_superblocks(Vp3DecodeContext *s, GetBitContext *gb) } else num_coded_frags = s->num_kf_coded_fragment[plane]; } else { - for (i = sb_start; i < sb_end && get_bits_left(gb) > 0; i++) { + for (int i = sb_start; i < sb_end && get_bits_left(gb) > 0; i++) { if (get_bits_left(gb) < plane0_num_coded_frags >> 2) { return AVERROR_INVALIDDATA; } /* iterate through all 16 fragments in a superblock */ - for (j = 0; j < 16; j++) { + for (int j = 0; j < 16; j++) { /* if the fragment is in bounds, check its coding status */ current_fragment = s->superblock_fragments[i * 16 + j]; if (current_fragment != -1) { @@ -611,7 +611,7 @@ static int unpack_superblocks(Vp3DecodeContext *s, GetBitContext *gb) * that cares about the fragment coding runs */ if (current_run-- == 0) { bit ^= 1; - current_run = get_vlc2(gb, s->fragment_run_length_vlc.table, 5, 2); + current_run = get_vlc2(gb, fragment_run_length_vlc, 5, 2); } coded = bit; } @@ -635,7 +635,7 @@ static int unpack_superblocks(Vp3DecodeContext *s, GetBitContext *gb) if (!plane) plane0_num_coded_frags = num_coded_frags; s->total_num_coded_frags += num_coded_frags; - for (i = 0; i < 64; i++) + for (int i = 0; i < 64; i++) s->num_coded_frags[plane][i] = num_coded_frags; if (plane < 2) s->coded_fragment_list[plane + 1] = s->coded_fragment_list[plane] + @@ -688,16 +688,16 @@ static int vp4_get_mb_count(Vp3DecodeContext *s, GetBitContext *gb) return v; } -static int vp4_get_block_pattern(Vp3DecodeContext *s, GetBitContext *gb, int *next_block_pattern_table) +static int vp4_get_block_pattern(GetBitContext *gb, int *next_block_pattern_table) { - int v = get_vlc2(gb, s->block_pattern_vlc[*next_block_pattern_table].table, 3, 2); + int v = get_vlc2(gb, block_pattern_vlc[*next_block_pattern_table], 5, 1); *next_block_pattern_table = vp4_block_pattern_table_selector[v]; return v + 1; } static int vp4_unpack_macroblocks(Vp3DecodeContext *s, GetBitContext *gb) { - int plane, i, j, k, fragment; + int fragment; int next_block_pattern_table; int bit, current_run, has_partial; @@ -708,7 +708,7 @@ static int vp4_unpack_macroblocks(Vp3DecodeContext *s, GetBitContext *gb) has_partial = 0; bit = get_bits1(gb); - for (i = 0; i < s->yuv_macroblock_count; i += current_run) { + for (int i = 0; i < s->yuv_macroblock_count; i += current_run) { if (get_bits_left(gb) <= 0) return AVERROR_INVALIDDATA; current_run = vp4_get_mb_count(s, gb); @@ -724,7 +724,7 @@ static int vp4_unpack_macroblocks(Vp3DecodeContext *s, GetBitContext *gb) return AVERROR_INVALIDDATA; bit = get_bits1(gb); current_run = vp4_get_mb_count(s, gb); - for (i = 0; i < s->yuv_macroblock_count; i++) { + for (int i = 0; i < s->yuv_macroblock_count; i++) { if (!s->superblock_coding[i]) { if (!current_run) { bit ^= 1; @@ -739,9 +739,7 @@ static int vp4_unpack_macroblocks(Vp3DecodeContext *s, GetBitContext *gb) } next_block_pattern_table = 0; - i = 0; - for (plane = 0; plane < 3; plane++) { - int sb_x, sb_y; + for (int plane = 0, i = 0; plane < 3; plane++) { int sb_width = plane ? s->c_superblock_width : s->y_superblock_width; int sb_height = plane ? s->c_superblock_height : s->y_superblock_height; int mb_width = plane ? s->c_macroblock_width : s->macroblock_width; @@ -749,9 +747,9 @@ static int vp4_unpack_macroblocks(Vp3DecodeContext *s, GetBitContext *gb) int fragment_width = s->fragment_width[!!plane]; int fragment_height = s->fragment_height[!!plane]; - for (sb_y = 0; sb_y < sb_height; sb_y++) { - for (sb_x = 0; sb_x < sb_width; sb_x++) { - for (j = 0; j < 4; j++) { + for (int sb_y = 0; sb_y < sb_height; sb_y++) { + for (int sb_x = 0; sb_x < sb_width; sb_x++) { + for (int j = 0; j < 4; j++) { int mb_x = 2 * sb_x + (j >> 1); int mb_y = 2 * sb_y + (j >> 1) ^ (j & 1); int mb_coded, pattern, coded; @@ -764,11 +762,11 @@ static int vp4_unpack_macroblocks(Vp3DecodeContext *s, GetBitContext *gb) if (mb_coded == SB_FULLY_CODED) pattern = 0xF; else if (mb_coded == SB_PARTIALLY_CODED) - pattern = vp4_get_block_pattern(s, gb, &next_block_pattern_table); + pattern = vp4_get_block_pattern(gb, &next_block_pattern_table); else pattern = 0; - for (k = 0; k < 4; k++) { + for (int k = 0; k < 4; k++) { if (BLOCK_X >= fragment_width || BLOCK_Y >= fragment_height) continue; fragment = s->fragment_start[plane] + BLOCK_Y * fragment_width + BLOCK_X; @@ -791,7 +789,6 @@ static int vp4_unpack_macroblocks(Vp3DecodeContext *s, GetBitContext *gb) */ static int unpack_modes(Vp3DecodeContext *s, GetBitContext *gb) { - int i, j, k, sb_x, sb_y; int scheme; int current_macroblock; int current_fragment; @@ -801,7 +798,7 @@ static int unpack_modes(Vp3DecodeContext *s, GetBitContext *gb) Vp3Fragment *frag; if (s->keyframe) { - for (i = 0; i < s->fragment_count; i++) + for (int i = 0; i < s->fragment_count; i++) s->all_fragments[i].coding_method = MODE_INTRA; } else { /* fetch the mode coding scheme for this frame */ @@ -809,9 +806,9 @@ static int unpack_modes(Vp3DecodeContext *s, GetBitContext *gb) /* is it a custom coding scheme? */ if (scheme == 0) { - for (i = 0; i < 8; i++) + for (int i = 0; i < 8; i++) custom_mode_alphabet[i] = MODE_INTER_NO_MV; - for (i = 0; i < 8; i++) + for (int i = 0; i < 8; i++) custom_mode_alphabet[get_bits(gb, 3)] = i; alphabet = custom_mode_alphabet; } else @@ -819,12 +816,13 @@ static int unpack_modes(Vp3DecodeContext *s, GetBitContext *gb) /* iterate through all of the macroblocks that contain 1 or more * coded fragments */ - for (sb_y = 0; sb_y < s->y_superblock_height; sb_y++) { - for (sb_x = 0; sb_x < s->y_superblock_width; sb_x++) { + for (int sb_y = 0; sb_y < s->y_superblock_height; sb_y++) { + for (int sb_x = 0; sb_x < s->y_superblock_width; sb_x++) { if (get_bits_left(gb) <= 0) return -1; - for (j = 0; j < 4; j++) { + for (int j = 0; j < 4; j++) { + int k; int mb_x = 2 * sb_x + (j >> 1); int mb_y = 2 * sb_y + (((j >> 1) + j) & 1); current_macroblock = mb_y * s->macroblock_width + mb_x; @@ -851,7 +849,7 @@ static int unpack_modes(Vp3DecodeContext *s, GetBitContext *gb) if (scheme == 7) coding_mode = get_bits(gb, 3); else - coding_mode = alphabet[get_vlc2(gb, s->mode_code_vlc.table, 3, 3)]; + coding_mode = alphabet[get_vlc2(gb, mode_code_vlc, 4, 2)]; s->macroblock_coding[current_macroblock] = coding_mode; for (k = 0; k < 4; k++) { @@ -892,11 +890,15 @@ static int unpack_modes(Vp3DecodeContext *s, GetBitContext *gb) return 0; } -static int vp4_get_mv(Vp3DecodeContext *s, GetBitContext *gb, int axis, int last_motion) +static int vp4_get_mv(GetBitContext *gb, int axis, int last_motion) { - int v = get_vlc2(gb, s->vp4_mv_vlc[axis][vp4_mv_table_selector[FFABS(last_motion)]].table, +#if CONFIG_VP4_DECODER + int v = get_vlc2(gb, vp4_mv_vlc_table[axis][vp4_mv_table_selector[FFABS(last_motion)]], VP4_MV_VLC_BITS, 2); return last_motion < 0 ? -v : v; +#else + return 0; +#endif } /* @@ -905,7 +907,6 @@ static int vp4_get_mv(Vp3DecodeContext *s, GetBitContext *gb, int axis, int last */ static int unpack_vectors(Vp3DecodeContext *s, GetBitContext *gb) { - int j, k, sb_x, sb_y; int coding_mode; int motion_x[4]; int motion_y[4]; @@ -927,12 +928,12 @@ static int unpack_vectors(Vp3DecodeContext *s, GetBitContext *gb) /* iterate through all of the macroblocks that contain 1 or more * coded fragments */ - for (sb_y = 0; sb_y < s->y_superblock_height; sb_y++) { - for (sb_x = 0; sb_x < s->y_superblock_width; sb_x++) { + for (int sb_y = 0; sb_y < s->y_superblock_height; sb_y++) { + for (int sb_x = 0; sb_x < s->y_superblock_width; sb_x++) { if (get_bits_left(gb) <= 0) return -1; - for (j = 0; j < 4; j++) { + for (int j = 0; j < 4; j++) { int mb_x = 2 * sb_x + (j >> 1); int mb_y = 2 * sb_y + (((j >> 1) + j) & 1); current_macroblock = mb_y * s->macroblock_width + mb_x; @@ -945,23 +946,23 @@ static int unpack_vectors(Vp3DecodeContext *s, GetBitContext *gb) switch (s->macroblock_coding[current_macroblock]) { case MODE_GOLDEN_MV: if (coding_mode == 2) { /* VP4 */ - last_gold_motion_x = motion_x[0] = vp4_get_mv(s, gb, 0, last_gold_motion_x); - last_gold_motion_y = motion_y[0] = vp4_get_mv(s, gb, 1, last_gold_motion_y); + last_gold_motion_x = motion_x[0] = vp4_get_mv(gb, 0, last_gold_motion_x); + last_gold_motion_y = motion_y[0] = vp4_get_mv(gb, 1, last_gold_motion_y); break; } /* otherwise fall through */ case MODE_INTER_PLUS_MV: /* all 6 fragments use the same motion vector */ if (coding_mode == 0) { - motion_x[0] = get_vlc2(gb, s->motion_vector_vlc.table, + motion_x[0] = get_vlc2(gb, motion_vector_vlc, VP3_MV_VLC_BITS, 2); - motion_y[0] = get_vlc2(gb, s->motion_vector_vlc.table, + motion_y[0] = get_vlc2(gb, motion_vector_vlc, VP3_MV_VLC_BITS, 2); } else if (coding_mode == 1) { motion_x[0] = fixed_motion_vector_table[get_bits(gb, 6)]; motion_y[0] = fixed_motion_vector_table[get_bits(gb, 6)]; } else { /* VP4 */ - motion_x[0] = vp4_get_mv(s, gb, 0, last_motion_x); - motion_y[0] = vp4_get_mv(s, gb, 1, last_motion_y); + motion_x[0] = vp4_get_mv(gb, 0, last_motion_x); + motion_y[0] = vp4_get_mv(gb, 1, last_motion_y); } /* vector maintenance, only on MODE_INTER_PLUS_MV */ @@ -980,20 +981,20 @@ static int unpack_vectors(Vp3DecodeContext *s, GetBitContext *gb) /* fetch 4 vectors from the bitstream, one for each * Y fragment, then average for the C fragment vectors */ - for (k = 0; k < 4; k++) { + for (int k = 0; k < 4; k++) { current_fragment = BLOCK_Y * s->fragment_width[0] + BLOCK_X; if (s->all_fragments[current_fragment].coding_method != MODE_COPY) { if (coding_mode == 0) { - motion_x[k] = get_vlc2(gb, s->motion_vector_vlc.table, + motion_x[k] = get_vlc2(gb, motion_vector_vlc, VP3_MV_VLC_BITS, 2); - motion_y[k] = get_vlc2(gb, s->motion_vector_vlc.table, + motion_y[k] = get_vlc2(gb, motion_vector_vlc, VP3_MV_VLC_BITS, 2); } else if (coding_mode == 1) { motion_x[k] = fixed_motion_vector_table[get_bits(gb, 6)]; motion_y[k] = fixed_motion_vector_table[get_bits(gb, 6)]; } else { /* VP4 */ - motion_x[k] = vp4_get_mv(s, gb, 0, prior_last_motion_x); - motion_y[k] = vp4_get_mv(s, gb, 1, prior_last_motion_y); + motion_x[k] = vp4_get_mv(gb, 0, prior_last_motion_x); + motion_y[k] = vp4_get_mv(gb, 1, prior_last_motion_y); } last_motion_x = motion_x[k]; last_motion_y = motion_y[k]; @@ -1036,7 +1037,7 @@ static int unpack_vectors(Vp3DecodeContext *s, GetBitContext *gb) } /* assign the motion vectors to the correct fragments */ - for (k = 0; k < 4; k++) { + for (int k = 0; k < 4; k++) { current_fragment = BLOCK_Y * s->fragment_width[0] + BLOCK_X; if (s->macroblock_coding[current_macroblock] == MODE_INTER_FOURMV) { @@ -1077,13 +1078,13 @@ static int unpack_vectors(Vp3DecodeContext *s, GetBitContext *gb) motion_x[1] = (motion_x[1] >> 1) | (motion_x[1] & 1); } frag = 2 * mb_y * s->fragment_width[1] + mb_x; - for (k = 0; k < 2; k++) { + for (int k = 0; k < 2; k++) { s->motion_val[1][frag][0] = motion_x[k]; s->motion_val[1][frag][1] = motion_y[k]; frag += s->fragment_width[1]; } } else { - for (k = 0; k < 4; k++) { + for (int k = 0; k < 4; k++) { frag = BLOCK_Y * s->fragment_width[1] + BLOCK_X; if (s->macroblock_coding[current_macroblock] == MODE_INTER_FOURMV) { s->motion_val[1][frag][0] = motion_x[k]; @@ -1103,11 +1104,11 @@ static int unpack_vectors(Vp3DecodeContext *s, GetBitContext *gb) static int unpack_block_qpis(Vp3DecodeContext *s, GetBitContext *gb) { - int qpi, i, j, bit, run_length, blocks_decoded, num_blocks_at_qpi; int num_blocks = s->total_num_coded_frags; - for (qpi = 0; qpi < s->nqps - 1 && num_blocks > 0; qpi++) { - i = blocks_decoded = num_blocks_at_qpi = 0; + for (int qpi = 0; qpi < s->nqps - 1 && num_blocks > 0; qpi++) { + int i = 0, blocks_decoded = 0, num_blocks_at_qpi = 0; + int bit, run_length; bit = get_bits1(gb) ^ 1; run_length = 0; @@ -1118,7 +1119,7 @@ static int unpack_block_qpis(Vp3DecodeContext *s, GetBitContext *gb) else bit ^= 1; - run_length = get_vlc2(gb, s->superblock_run_length_vlc.table, + run_length = get_vlc2(gb, superblock_run_length_vlc, SUPERBLOCK_VLC_BITS, 2); if (run_length == 34) run_length += get_bits(gb, 12); @@ -1127,7 +1128,7 @@ static int unpack_block_qpis(Vp3DecodeContext *s, GetBitContext *gb) if (!bit) num_blocks_at_qpi += run_length; - for (j = 0; j < run_length; i++) { + for (int j = 0; j < run_length; i++) { if (i >= s->total_num_coded_frags) return -1; @@ -1181,11 +1182,11 @@ static inline int get_coeff(GetBitContext *gb, int token, int16_t *coeff) * be passed into the next call to this same function. */ static int unpack_vlcs(Vp3DecodeContext *s, GetBitContext *gb, - VLC *table, int coeff_index, + const VLCElem *vlc_table, int coeff_index, int plane, int eob_run) { - int i, j = 0; + int j = 0; int token; int zero_run = 0; int16_t coeff = 0; @@ -1195,9 +1196,8 @@ static int unpack_vlcs(Vp3DecodeContext *s, GetBitContext *gb, int16_t *dct_tokens = s->dct_tokens[plane][coeff_index]; /* local references to structure members to avoid repeated dereferences */ - int *coded_fragment_list = s->coded_fragment_list[plane]; + const int *coded_fragment_list = s->coded_fragment_list[plane]; Vp3Fragment *all_fragments = s->all_fragments; - const VLCElem *vlc_table = table->table; if (num_coeffs < 0) { av_log(s->avctx, AV_LOG_ERROR, @@ -1266,7 +1266,7 @@ static int unpack_vlcs(Vp3DecodeContext *s, GetBitContext *gb, // zero runs code multiple coefficients, // so don't try to decode coeffs for those higher levels - for (i = coeff_index + 1; i <= coeff_index + zero_run; i++) + for (int i = coeff_index + 1; i <= coeff_index + zero_run; i++) s->num_coded_frags[plane][i]--; coeff_i++; } else { @@ -1281,7 +1281,7 @@ static int unpack_vlcs(Vp3DecodeContext *s, GetBitContext *gb, // decrement the number of blocks that have higher coefficients for each // EOB run at this level if (blocks_ended) - for (i = coeff_index + 1; i < 64; i++) + for (int i = coeff_index + 1; i < 64; i++) s->num_coded_frags[plane][i] -= blocks_ended; // setup the next buffer @@ -1303,14 +1303,13 @@ static void reverse_dc_prediction(Vp3DecodeContext *s, */ static int unpack_dct_coeffs(Vp3DecodeContext *s, GetBitContext *gb) { - int i; + const VLCElem *const *coeff_vlc = s->coeff_vlc->vlc_tabs; int dc_y_table; int dc_c_table; int ac_y_table; int ac_c_table; int residual_eob_run = 0; - VLC *y_tables[64]; - VLC *c_tables[64]; + const VLCElem *y_tables[64], *c_tables[64]; s->dct_tokens[0][0] = s->dct_tokens_base; @@ -1322,7 +1321,7 @@ static int unpack_dct_coeffs(Vp3DecodeContext *s, GetBitContext *gb) dc_c_table = get_bits(gb, 4); /* unpack the Y plane DC coefficients */ - residual_eob_run = unpack_vlcs(s, gb, &s->coeff_vlc[dc_y_table], 0, + residual_eob_run = unpack_vlcs(s, gb, coeff_vlc[dc_y_table], 0, 0, residual_eob_run); if (residual_eob_run < 0) return residual_eob_run; @@ -1333,11 +1332,11 @@ static int unpack_dct_coeffs(Vp3DecodeContext *s, GetBitContext *gb) reverse_dc_prediction(s, 0, s->fragment_width[0], s->fragment_height[0]); /* unpack the C plane DC coefficients */ - residual_eob_run = unpack_vlcs(s, gb, &s->coeff_vlc[dc_c_table], 0, + residual_eob_run = unpack_vlcs(s, gb, coeff_vlc[dc_c_table], 0, 1, residual_eob_run); if (residual_eob_run < 0) return residual_eob_run; - residual_eob_run = unpack_vlcs(s, gb, &s->coeff_vlc[dc_c_table], 0, + residual_eob_run = unpack_vlcs(s, gb, coeff_vlc[dc_c_table], 0, 2, residual_eob_run); if (residual_eob_run < 0) return residual_eob_run; @@ -1357,29 +1356,29 @@ static int unpack_dct_coeffs(Vp3DecodeContext *s, GetBitContext *gb) ac_c_table = get_bits(gb, 4); /* build tables of AC VLC tables */ - for (i = 1; i <= 5; i++) { + for (int i = 1; i <= 5; i++) { /* AC VLC table group 1 */ - y_tables[i] = &s->coeff_vlc[ac_y_table + 16]; - c_tables[i] = &s->coeff_vlc[ac_c_table + 16]; + y_tables[i] = coeff_vlc[ac_y_table + 16]; + c_tables[i] = coeff_vlc[ac_c_table + 16]; } - for (i = 6; i <= 14; i++) { + for (int i = 6; i <= 14; i++) { /* AC VLC table group 2 */ - y_tables[i] = &s->coeff_vlc[ac_y_table + 32]; - c_tables[i] = &s->coeff_vlc[ac_c_table + 32]; + y_tables[i] = coeff_vlc[ac_y_table + 32]; + c_tables[i] = coeff_vlc[ac_c_table + 32]; } - for (i = 15; i <= 27; i++) { + for (int i = 15; i <= 27; i++) { /* AC VLC table group 3 */ - y_tables[i] = &s->coeff_vlc[ac_y_table + 48]; - c_tables[i] = &s->coeff_vlc[ac_c_table + 48]; + y_tables[i] = coeff_vlc[ac_y_table + 48]; + c_tables[i] = coeff_vlc[ac_c_table + 48]; } - for (i = 28; i <= 63; i++) { + for (int i = 28; i <= 63; i++) { /* AC VLC table group 4 */ - y_tables[i] = &s->coeff_vlc[ac_y_table + 64]; - c_tables[i] = &s->coeff_vlc[ac_c_table + 64]; + y_tables[i] = coeff_vlc[ac_y_table + 64]; + c_tables[i] = coeff_vlc[ac_c_table + 64]; } /* decode all AC coefficients */ - for (i = 1; i <= 63; i++) { + for (int i = 1; i <= 63; i++) { residual_eob_run = unpack_vlcs(s, gb, y_tables[i], i, 0, residual_eob_run); if (residual_eob_run < 0) @@ -1406,7 +1405,7 @@ static int unpack_dct_coeffs(Vp3DecodeContext *s, GetBitContext *gb) * @return < 0 on error */ static int vp4_unpack_vlcs(Vp3DecodeContext *s, GetBitContext *gb, - VLC *vlc_tables[64], + const VLCElem *const vlc_tables[64], int plane, int eob_tracker[64], int fragment) { int token; @@ -1419,7 +1418,7 @@ static int vp4_unpack_vlcs(Vp3DecodeContext *s, GetBitContext *gb, if (get_bits_left(gb) < 1) return AVERROR_INVALIDDATA; - token = get_vlc2(gb, vlc_tables[coeff_i]->table, 11, 3); + token = get_vlc2(gb, vlc_tables[coeff_i], 11, 3); /* use the token to get a zero run, a coefficient, and an eob run */ if ((unsigned) token <= 6U) { @@ -1466,24 +1465,20 @@ static void vp4_dc_predictor_reset(VP4Predictor *p) static void vp4_dc_pred_before(const Vp3DecodeContext *s, VP4Predictor dc_pred[6][6], int sb_x) { - int i, j; - - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) dc_pred[0][i + 1] = s->dc_pred_row[sb_x * 4 + i]; - for (j = 1; j < 5; j++) - for (i = 0; i < 4; i++) + for (int j = 1; j < 5; j++) + for (int i = 0; i < 4; i++) vp4_dc_predictor_reset(&dc_pred[j][i + 1]); } static void vp4_dc_pred_after(Vp3DecodeContext *s, VP4Predictor dc_pred[6][6], int sb_x) { - int i; - - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) s->dc_pred_row[sb_x * 4 + i] = dc_pred[4][i + 1]; - for (i = 1; i < 5; i++) + for (int i = 1; i < 5; i++) dc_pred[i][0] = dc_pred[i][4]; } @@ -1519,10 +1514,9 @@ static int vp4_dc_pred(const Vp3DecodeContext *s, const VP4Predictor * dc_pred, static void vp4_set_tokens_base(Vp3DecodeContext *s) { - int plane, i; int16_t *base = s->dct_tokens_base; - for (plane = 0; plane < 3; plane++) { - for (i = 0; i < 64; i++) { + for (int plane = 0; plane < 3; plane++) { + for (int i = 0; i < 64; i++) { s->dct_tokens[plane][i] = base; base += s->fragment_width[!!plane] * s->fragment_height[!!plane]; } @@ -1531,13 +1525,12 @@ static void vp4_set_tokens_base(Vp3DecodeContext *s) static int vp4_unpack_dct_coeffs(Vp3DecodeContext *s, GetBitContext *gb) { - int i, j; + const VLCElem *const *coeff_vlc = s->coeff_vlc->vlc_tabs; int dc_y_table; int dc_c_table; int ac_y_table; int ac_c_table; - VLC *tables[2][64]; - int plane, sb_y, sb_x; + const VLCElem *tables[2][64]; int eob_tracker[64]; VP4Predictor dc_pred[6][6]; int last_dc[NB_VP4_DC_TYPES]; @@ -1555,48 +1548,48 @@ static int vp4_unpack_dct_coeffs(Vp3DecodeContext *s, GetBitContext *gb) /* build tables of DC/AC VLC tables */ /* DC table group */ - tables[0][0] = &s->coeff_vlc[dc_y_table]; - tables[1][0] = &s->coeff_vlc[dc_c_table]; - for (i = 1; i <= 5; i++) { + tables[0][0] = coeff_vlc[dc_y_table]; + tables[1][0] = coeff_vlc[dc_c_table]; + for (int i = 1; i <= 5; i++) { /* AC VLC table group 1 */ - tables[0][i] = &s->coeff_vlc[ac_y_table + 16]; - tables[1][i] = &s->coeff_vlc[ac_c_table + 16]; + tables[0][i] = coeff_vlc[ac_y_table + 16]; + tables[1][i] = coeff_vlc[ac_c_table + 16]; } - for (i = 6; i <= 14; i++) { + for (int i = 6; i <= 14; i++) { /* AC VLC table group 2 */ - tables[0][i] = &s->coeff_vlc[ac_y_table + 32]; - tables[1][i] = &s->coeff_vlc[ac_c_table + 32]; + tables[0][i] = coeff_vlc[ac_y_table + 32]; + tables[1][i] = coeff_vlc[ac_c_table + 32]; } - for (i = 15; i <= 27; i++) { + for (int i = 15; i <= 27; i++) { /* AC VLC table group 3 */ - tables[0][i] = &s->coeff_vlc[ac_y_table + 48]; - tables[1][i] = &s->coeff_vlc[ac_c_table + 48]; + tables[0][i] = coeff_vlc[ac_y_table + 48]; + tables[1][i] = coeff_vlc[ac_c_table + 48]; } - for (i = 28; i <= 63; i++) { + for (int i = 28; i <= 63; i++) { /* AC VLC table group 4 */ - tables[0][i] = &s->coeff_vlc[ac_y_table + 64]; - tables[1][i] = &s->coeff_vlc[ac_c_table + 64]; + tables[0][i] = coeff_vlc[ac_y_table + 64]; + tables[1][i] = coeff_vlc[ac_c_table + 64]; } vp4_set_tokens_base(s); memset(last_dc, 0, sizeof(last_dc)); - for (plane = 0; plane < ((s->avctx->flags & AV_CODEC_FLAG_GRAY) ? 1 : 3); plane++) { + for (int plane = 0; plane < ((s->avctx->flags & AV_CODEC_FLAG_GRAY) ? 1 : 3); plane++) { memset(eob_tracker, 0, sizeof(eob_tracker)); /* initialise dc prediction */ - for (i = 0; i < s->fragment_width[!!plane]; i++) + for (int i = 0; i < s->fragment_width[!!plane]; i++) vp4_dc_predictor_reset(&s->dc_pred_row[i]); - for (j = 0; j < 6; j++) - for (i = 0; i < 6; i++) + for (int j = 0; j < 6; j++) + for (int i = 0; i < 6; i++) vp4_dc_predictor_reset(&dc_pred[j][i]); - for (sb_y = 0; sb_y * 4 < s->fragment_height[!!plane]; sb_y++) { - for (sb_x = 0; sb_x *4 < s->fragment_width[!!plane]; sb_x++) { + for (int sb_y = 0; sb_y * 4 < s->fragment_height[!!plane]; sb_y++) { + for (int sb_x = 0; sb_x *4 < s->fragment_width[!!plane]; sb_x++) { vp4_dc_pred_before(s, dc_pred, sb_x); - for (j = 0; j < 16; j++) { + for (int j = 0; j < 16; j++) { int hx = hilbert_offset[j][0]; int hy = hilbert_offset[j][1]; int x = 4 * sb_x + hx; @@ -1653,7 +1646,6 @@ static void reverse_dc_prediction(Vp3DecodeContext *s, #define PUR 2 #define PL 1 - int x, y; int i = first_fragment; int predicted_dc; @@ -1723,9 +1715,9 @@ static void reverse_dc_prediction(Vp3DecodeContext *s, last_dc[2] = 0; /* for each fragment row... */ - for (y = 0; y < fragment_height; y++) { + for (int y = 0; y < fragment_height; y++) { /* for each fragment in a row... */ - for (x = 0; x < fragment_width; x++, i++) { + for (int x = 0; x < fragment_width; x++, i++) { /* reverse prediction if this block was coded */ if (s->all_fragments[i].coding_method != MODE_COPY) { @@ -1796,7 +1788,6 @@ static void reverse_dc_prediction(Vp3DecodeContext *s, static void apply_loop_filter(Vp3DecodeContext *s, int plane, int ystart, int yend) { - int x, y; int *bounding_values = s->bounding_values_array + 127; int width = s->fragment_width[!!plane]; @@ -1808,8 +1799,8 @@ static void apply_loop_filter(Vp3DecodeContext *s, int plane, stride = -stride; plane_data += s->data_offset[plane] + 8 * ystart * stride; - for (y = ystart; y < yend; y++) { - for (x = 0; x < width; x++) { + for (int y = ystart; y < yend; y++) { + for (int x = 0; x < width; x++) { /* This code basically just deblocks on the edges of coded blocks. * However, it has to be much more complicated because of the * brain damaged deblock ordering used in VP3/Theora. Order matters @@ -1860,11 +1851,11 @@ static void apply_loop_filter(Vp3DecodeContext *s, int plane, * Pull DCT tokens from the 64 levels to decode and dequant the coefficients * for the next block in coding order */ -static inline int vp3_dequant(Vp3DecodeContext *s, Vp3Fragment *frag, +static inline int vp3_dequant(Vp3DecodeContext *s, const Vp3Fragment *frag, int plane, int inter, int16_t block[64]) { - int16_t *dequantizer = s->qmat[frag->qpi][inter][plane]; - uint8_t *perm = s->idct_scantable; + const int16_t *dequantizer = s->qmat[frag->qpi][inter][plane]; + const uint8_t *perm = s->idct_scantable; int i = 0; do { @@ -1907,7 +1898,7 @@ static inline int vp3_dequant(Vp3DecodeContext *s, Vp3Fragment *frag, */ static void vp3_draw_horiz_band(Vp3DecodeContext *s, int y) { - int h, cy, i; + int h, cy; int offset[AV_NUM_DATA_POINTERS]; if (HAVE_THREADS && s->avctx->active_thread_type & FF_THREAD_FRAME) { @@ -1936,7 +1927,7 @@ static void vp3_draw_horiz_band(Vp3DecodeContext *s, int y) offset[0] = s->current_frame.f->linesize[0] * y; offset[1] = s->current_frame.f->linesize[1] * cy; offset[2] = s->current_frame.f->linesize[2] * cy; - for (i = 3; i < AV_NUM_DATA_POINTERS; i++) + for (int i = 3; i < AV_NUM_DATA_POINTERS; i++) offset[i] = 0; emms_c(); @@ -1947,7 +1938,7 @@ static void vp3_draw_horiz_band(Vp3DecodeContext *s, int y) * Wait for the reference frame of the current fragment. * The progress value is in luma pixel rows. */ -static void await_reference_row(Vp3DecodeContext *s, Vp3Fragment *fragment, +static void await_reference_row(Vp3DecodeContext *s, const Vp3Fragment *fragment, int motion_y, int y) { const ThreadFrame *ref_frame; @@ -1971,13 +1962,13 @@ static void await_reference_row(Vp3DecodeContext *s, Vp3Fragment *fragment, * @return non-zero if temp (edge_emu_buffer) was populated */ static int vp4_mc_loop_filter(Vp3DecodeContext *s, int plane, int motion_x, int motion_y, int bx, int by, - uint8_t * motion_source, int stride, int src_x, int src_y, uint8_t *temp) + const uint8_t *motion_source, ptrdiff_t stride, + int src_x, int src_y, uint8_t *temp) { int motion_shift = plane ? 4 : 2; int subpel_mask = plane ? 3 : 1; int *bounding_values = s->bounding_values_array + 127; - int i; int x, y; int x2, y2; int x_subpel, y_subpel; @@ -2016,8 +2007,7 @@ static int vp4_mc_loop_filter(Vp3DecodeContext *s, int plane, int motion_x, int x_offset = (-(x + 2) & 7) + 2; y_offset = (-(y + 2) & 7) + 2; - if (x_offset > 8 + x_subpel && y_offset > 8 + y_subpel) - return 0; + av_assert1(!(x_offset > 8 + x_subpel && y_offset > 8 + y_subpel)); s->vdsp.emulated_edge_mc(loop, motion_source - stride - 1, loop_stride, stride, @@ -2058,7 +2048,7 @@ static int vp4_mc_loop_filter(Vp3DecodeContext *s, int plane, int motion_x, int safe_loop_filter(v_loop_filter, loop + (y_offset + 1)*loop_stride + 1, loop_stride, bounding_values); } - for (i = 0; i < 9; i++) + for (int i = 0; i < 9; i++) memcpy(temp + i*stride, loop + (i + 1) * loop_stride + 1, 9); return 1; @@ -2071,29 +2061,35 @@ static int vp4_mc_loop_filter(Vp3DecodeContext *s, int plane, int motion_x, int */ static void render_slice(Vp3DecodeContext *s, int slice) { - int x, y, i, j, fragment; int16_t *block = s->block; int motion_x = 0xdeadbeef, motion_y = 0xdeadbeef; + /* When decoding keyframes, the earlier frames may not be available, + * so to avoid using undefined pointer arithmetic on them we just + * use the current frame instead. Nothing is ever read from these + * frames in case of a keyframe. */ + const AVFrame *last_frame = s->last_frame.f->data[0] ? + s->last_frame.f : s->current_frame.f; + const AVFrame *golden_frame = s->golden_frame.f->data[0] ? + s->golden_frame.f : s->current_frame.f; int motion_halfpel_index; - uint8_t *motion_source; - int plane, first_pixel; + int first_pixel; if (slice >= s->c_superblock_height) return; - for (plane = 0; plane < 3; plane++) { + for (int plane = 0; plane < 3; plane++) { uint8_t *output_plane = s->current_frame.f->data[plane] + s->data_offset[plane]; - uint8_t *last_plane = s->last_frame.f->data[plane] + + const uint8_t *last_plane = last_frame->data[plane] + s->data_offset[plane]; - uint8_t *golden_plane = s->golden_frame.f->data[plane] + + const uint8_t *golden_plane = golden_frame->data[plane] + s->data_offset[plane]; ptrdiff_t stride = s->current_frame.f->linesize[plane]; int plane_width = s->width >> (plane && s->chroma_x_shift); int plane_height = s->height >> (plane && s->chroma_y_shift); - int8_t(*motion_val)[2] = s->motion_val[!!plane]; + const int8_t (*motion_val)[2] = s->motion_val[!!plane]; - int sb_x, sb_y = slice << (!plane && s->chroma_y_shift); + int sb_y = slice << (!plane && s->chroma_y_shift); int slice_height = sb_y + 1 + (!plane && s->chroma_y_shift); int slice_width = plane ? s->c_superblock_width : s->y_superblock_width; @@ -2113,14 +2109,14 @@ static void render_slice(Vp3DecodeContext *s, int slice) /* for each superblock row in the slice (both of them)... */ for (; sb_y < slice_height; sb_y++) { /* for each superblock in a row... */ - for (sb_x = 0; sb_x < slice_width; sb_x++) { + for (int sb_x = 0; sb_x < slice_width; sb_x++) { /* for each block in a superblock... */ - for (j = 0; j < 16; j++) { - x = 4 * sb_x + hilbert_offset[j][0]; - y = 4 * sb_y + hilbert_offset[j][1]; - fragment = y * fragment_width + x; + for (int j = 0; j < 16; j++) { + int x = 4 * sb_x + hilbert_offset[j][0]; + int y = 4 * sb_y + hilbert_offset[j][1]; + int fragment = y * fragment_width + x; - i = fragment_start + fragment; + int i = fragment_start + fragment; // bounds check if (x >= fragment_width || y >= fragment_height) @@ -2136,6 +2132,7 @@ static void render_slice(Vp3DecodeContext *s, int slice) /* transform if this block was coded */ if (s->all_fragments[i].coding_method != MODE_COPY) { + const uint8_t *motion_source; if ((s->all_fragments[i].coding_method == MODE_USING_GOLDEN) || (s->all_fragments[i].coding_method == MODE_GOLDEN_MV)) motion_source = golden_plane; @@ -2268,6 +2265,48 @@ static void render_slice(Vp3DecodeContext *s, int slice) s->height - 16)); } +static av_cold void init_tables_once(void) +{ + VLCInitState state = VLC_INIT_STATE(mode_code_vlc); + + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(superblock_run_length_vlc, + SUPERBLOCK_VLC_BITS, 34, + superblock_run_length_vlc_lens, 1, + NULL, 0, 0, 1, 0); + + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(fragment_run_length_vlc, 5, 30, + fragment_run_length_vlc_len, 1, + NULL, 0, 0, 0, 0); + + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(motion_vector_vlc, VP3_MV_VLC_BITS, 63, + &motion_vector_vlc_table[0][1], 2, + &motion_vector_vlc_table[0][0], 2, 1, + -31, 0); + + ff_vlc_init_tables_from_lengths(&state, 4, 8, + mode_code_vlc_len, 1, + NULL, 0, 0, 0, 0); + +#if CONFIG_VP4_DECODER + for (int j = 0; j < 2; j++) + for (int i = 0; i < 7; i++) { + vp4_mv_vlc_table[j][i] = + ff_vlc_init_tables_from_lengths(&state, VP4_MV_VLC_BITS, 63, + &vp4_mv_vlc[j][i][0][1], 2, + &vp4_mv_vlc[j][i][0][0], 2, 1, + -31, 0); + } + + /* version >= 2 */ + for (int i = 0; i < 2; i++) { + block_pattern_vlc[i] = + ff_vlc_init_tables(&state, 5, 14, + &vp4_block_pattern_vlc[i][0][1], 2, 1, + &vp4_block_pattern_vlc[i][0][0], 2, 1, 0); + } +#endif +} + /// Allocate tables for per-frame data in Vp3DecodeContext static av_cold int allocate_tables(AVCodecContext *avctx) { @@ -2324,16 +2363,22 @@ static av_cold int init_frames(Vp3DecodeContext *s) return 0; } +static av_cold void free_vlc_tables(FFRefStructOpaque unused, void *obj) +{ + CoeffVLCs *vlcs = obj; + + for (int i = 0; i < FF_ARRAY_ELEMS(vlcs->vlcs); i++) + ff_vlc_free(&vlcs->vlcs[i]); +} + static av_cold int vp3_decode_init(AVCodecContext *avctx) { + static AVOnce init_static_once = AV_ONCE_INIT; Vp3DecodeContext *s = avctx->priv_data; - int i, inter, plane, ret; + int ret; int c_width; int c_height; int y_fragment_count, c_fragment_count; -#if CONFIG_VP4_DECODER - int j; -#endif ret = init_frames(s); if (ret < 0) @@ -2362,7 +2407,7 @@ static av_cold int vp3_decode_init(AVCodecContext *avctx) ff_videodsp_init(&s->vdsp, 8); ff_vp3dsp_init(&s->vp3dsp, avctx->flags); - for (i = 0; i < 64; i++) { + for (int i = 0; i < 64; i++) { #define TRANSPOSE(x) (((x) >> 3) | (((x) & 7) << 3)) s->idct_permutation[i] = TRANSPOSE(i); s->idct_scantable[i] = TRANSPOSE(ff_zigzag_direct[i]); @@ -2371,7 +2416,7 @@ static av_cold int vp3_decode_init(AVCodecContext *avctx) /* initialize to an impossible value which will force a recalculation * in the first frame decode */ - for (i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) s->qps[i] = -1; ret = av_pix_fmt_get_chroma_sub_sample(avctx->pix_fmt, &s->chroma_x_shift, &s->chroma_y_shift); @@ -2414,9 +2459,7 @@ static av_cold int vp3_decode_init(AVCodecContext *avctx) s->fragment_start[2] = y_fragment_count + c_fragment_count; if (!s->theora_tables) { - const uint8_t (*bias_tabs)[32][2]; - - for (i = 0; i < 64; i++) { + for (int i = 0; i < 64; i++) { s->coded_dc_scale_factor[0][i] = s->version < 2 ? vp31_dc_scale_factor[i] : vp4_y_dc_scale_factor[i]; s->coded_dc_scale_factor[1][i] = s->version < 2 ? vp31_dc_scale_factor[i] : vp4_uv_dc_scale_factor[i]; s->coded_ac_scale_factor[i] = s->version < 2 ? vp31_ac_scale_factor[i] : vp4_ac_scale_factor[i]; @@ -2426,81 +2469,54 @@ static av_cold int vp3_decode_init(AVCodecContext *avctx) s->filter_limit_values[i] = s->version < 2 ? vp31_filter_limit_values[i] : vp4_filter_limit_values[i]; } - for (inter = 0; inter < 2; inter++) { - for (plane = 0; plane < 3; plane++) { + for (int inter = 0; inter < 2; inter++) { + for (int plane = 0; plane < 3; plane++) { s->qr_count[inter][plane] = 1; s->qr_size[inter][plane][0] = 63; s->qr_base[inter][plane][0] = s->qr_base[inter][plane][1] = 2 * inter + (!!plane) * !inter; } } - - /* init VLC tables */ - bias_tabs = CONFIG_VP4_DECODER && s->version >= 2 ? vp4_bias : vp3_bias; - for (int i = 0; i < FF_ARRAY_ELEMS(s->coeff_vlc); i++) { - ret = ff_init_vlc_from_lengths(&s->coeff_vlc[i], 11, 32, - &bias_tabs[i][0][1], 2, - &bias_tabs[i][0][0], 2, 1, - 0, 0, avctx); - if (ret < 0) - return ret; - } - } else { - for (i = 0; i < FF_ARRAY_ELEMS(s->coeff_vlc); i++) { - const HuffTable *tab = &s->huffman_table[i]; - - ret = ff_init_vlc_from_lengths(&s->coeff_vlc[i], 11, tab->nb_entries, - &tab->entries[0].len, sizeof(*tab->entries), - &tab->entries[0].sym, sizeof(*tab->entries), 1, - 0, 0, avctx); - if (ret < 0) - return ret; - } } - ret = ff_init_vlc_from_lengths(&s->superblock_run_length_vlc, SUPERBLOCK_VLC_BITS, 34, - superblock_run_length_vlc_lens, 1, - NULL, 0, 0, 1, 0, avctx); - if (ret < 0) - return ret; + if (!avctx->internal->is_copy) { + CoeffVLCs *vlcs = ff_refstruct_alloc_ext(sizeof(*s->coeff_vlc), 0, + NULL, free_vlc_tables); + if (!vlcs) + return AVERROR(ENOMEM); - ret = ff_init_vlc_from_lengths(&s->fragment_run_length_vlc, 5, 30, - fragment_run_length_vlc_len, 1, - NULL, 0, 0, 0, 0, avctx); - if (ret < 0) - return ret; - - ret = ff_init_vlc_from_lengths(&s->mode_code_vlc, 3, 8, - mode_code_vlc_len, 1, - NULL, 0, 0, 0, 0, avctx); - if (ret < 0) - return ret; + s->coeff_vlc = vlcs; - ret = ff_init_vlc_from_lengths(&s->motion_vector_vlc, VP3_MV_VLC_BITS, 63, - &motion_vector_vlc_table[0][1], 2, - &motion_vector_vlc_table[0][0], 2, 1, - -31, 0, avctx); - if (ret < 0) - return ret; + if (!s->theora_tables) { + const uint8_t (*bias_tabs)[32][2]; -#if CONFIG_VP4_DECODER - for (j = 0; j < 2; j++) - for (i = 0; i < 7; i++) { - ret = ff_init_vlc_from_lengths(&s->vp4_mv_vlc[j][i], VP4_MV_VLC_BITS, 63, - &vp4_mv_vlc[j][i][0][1], 2, - &vp4_mv_vlc[j][i][0][0], 2, 1, -31, - 0, avctx); - if (ret < 0) - return ret; + /* init VLC tables */ + bias_tabs = CONFIG_VP4_DECODER && s->version >= 2 ? vp4_bias : vp3_bias; + for (int i = 0; i < FF_ARRAY_ELEMS(vlcs->vlcs); i++) { + ret = ff_vlc_init_from_lengths(&vlcs->vlcs[i], 11, 32, + &bias_tabs[i][0][1], 2, + &bias_tabs[i][0][0], 2, 1, + 0, 0, avctx); + if (ret < 0) + return ret; + vlcs->vlc_tabs[i] = vlcs->vlcs[i].table; + } + } else { + for (int i = 0; i < FF_ARRAY_ELEMS(vlcs->vlcs); i++) { + const HuffTable *tab = &s->huffman_table[i]; + + ret = ff_vlc_init_from_lengths(&vlcs->vlcs[i], 11, tab->nb_entries, + &tab->entries[0].len, sizeof(*tab->entries), + &tab->entries[0].sym, sizeof(*tab->entries), 1, + 0, 0, avctx); + if (ret < 0) + return ret; + vlcs->vlc_tabs[i] = vlcs->vlcs[i].table; + } } + } - /* version >= 2 */ - for (i = 0; i < 2; i++) - if ((ret = init_vlc(&s->block_pattern_vlc[i], 3, 14, - &vp4_block_pattern_vlc[i][0][1], 2, 1, - &vp4_block_pattern_vlc[i][0][0], 2, 1, 0)) < 0) - return ret; -#endif + ff_thread_once(&init_static_once, init_tables_once); return allocate_tables(avctx); } @@ -2511,45 +2527,43 @@ static int update_frames(AVCodecContext *avctx) Vp3DecodeContext *s = avctx->priv_data; int ret = 0; - /* shuffle frames (last = current) */ - ff_thread_release_ext_buffer(avctx, &s->last_frame); - ret = ff_thread_ref_frame(&s->last_frame, &s->current_frame); - if (ret < 0) - goto fail; - if (s->keyframe) { - ff_thread_release_ext_buffer(avctx, &s->golden_frame); + ff_thread_release_ext_buffer(&s->golden_frame); ret = ff_thread_ref_frame(&s->golden_frame, &s->current_frame); } + /* shuffle frames */ + ff_thread_release_ext_buffer(&s->last_frame); + FFSWAP(ThreadFrame, s->last_frame, s->current_frame); -fail: - ff_thread_release_ext_buffer(avctx, &s->current_frame); return ret; } #if HAVE_THREADS -static int ref_frame(Vp3DecodeContext *s, ThreadFrame *dst, ThreadFrame *src) +static int ref_frame(ThreadFrame *dst, const ThreadFrame *src) { - ff_thread_release_ext_buffer(s->avctx, dst); + ff_thread_release_ext_buffer(dst); if (src->f->data[0]) return ff_thread_ref_frame(dst, src); return 0; } -static int ref_frames(Vp3DecodeContext *dst, Vp3DecodeContext *src) +static int ref_frames(Vp3DecodeContext *dst, const Vp3DecodeContext *src) { int ret; - if ((ret = ref_frame(dst, &dst->current_frame, &src->current_frame)) < 0 || - (ret = ref_frame(dst, &dst->golden_frame, &src->golden_frame)) < 0 || - (ret = ref_frame(dst, &dst->last_frame, &src->last_frame)) < 0) + if ((ret = ref_frame(&dst->current_frame, &src->current_frame)) < 0 || + (ret = ref_frame(&dst->golden_frame, &src->golden_frame)) < 0 || + (ret = ref_frame(&dst->last_frame, &src->last_frame)) < 0) return ret; return 0; } static int vp3_update_thread_context(AVCodecContext *dst, const AVCodecContext *src) { - Vp3DecodeContext *s = dst->priv_data, *s1 = src->priv_data; - int qps_changed = 0, i, err; + Vp3DecodeContext *s = dst->priv_data; + const Vp3DecodeContext *s1 = src->priv_data; + int qps_changed = 0, err; + + ff_refstruct_replace(&s->coeff_vlc, s1->coeff_vlc); if (!s1->current_frame.f->data[0] || s->width != s1->width || s->height != s1->height) { @@ -2566,7 +2580,7 @@ static int vp3_update_thread_context(AVCodecContext *dst, const AVCodecContext * s->keyframe = s1->keyframe; // copy qscale data if necessary - for (i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { if (s->qps[i] != s1->qps[1]) { qps_changed = 1; memcpy(&s->qmat[i], &s1->qmat[i], sizeof(s->qmat[i])); @@ -2595,7 +2609,7 @@ static int vp3_decode_frame(AVCodecContext *avctx, AVFrame *frame, int buf_size = avpkt->size; Vp3DecodeContext *s = avctx->priv_data; GetBitContext gb; - int i, ret; + int ret; if ((ret = init_get_bits8(&gb, buf, buf_size)) < 0) return ret; @@ -2645,14 +2659,14 @@ static int vp3_decode_frame(AVCodecContext *avctx, AVFrame *frame, } if (!s->theora) skip_bits(&gb, 1); - for (i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) s->last_qps[i] = s->qps[i]; s->nqps = 0; do { s->qps[s->nqps++] = get_bits(&gb, 6); } while (s->theora >= 0x030200 && s->nqps < 3 && get_bits1(&gb)); - for (i = s->nqps; i < 3; i++) + for (int i = s->nqps; i < 3; i++) s->qps[i] = -1; if (s->avctx->debug & FF_DEBUG_PICT_INFO) @@ -2666,7 +2680,7 @@ static int vp3_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (s->qps[0] != s->last_qps[0]) init_loop_filter(s); - for (i = 0; i < s->nqps; i++) + for (int i = 0; i < s->nqps; i++) // reinit all dequantizers if the first one changed, because // the DC of the first quantizer must be used for all matrices if (s->qps[i] != s->last_qps[i] || s->qps[0] != s->last_qps[0]) @@ -2677,7 +2691,10 @@ static int vp3_decode_frame(AVCodecContext *avctx, AVFrame *frame, s->current_frame.f->pict_type = s->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; - s->current_frame.f->key_frame = s->keyframe; + if (s->keyframe) + s->current_frame.f->flags |= AV_FRAME_FLAG_KEY; + else + s->current_frame.f->flags &= ~AV_FRAME_FLAG_KEY; if ((ret = ff_thread_get_ext_buffer(avctx, &s->current_frame, AV_GET_BUFFER_FLAG_REF)) < 0) goto error; @@ -2746,16 +2763,16 @@ static int vp3_decode_frame(AVCodecContext *avctx, AVFrame *frame, if ((ret = ff_thread_get_ext_buffer(avctx, &s->golden_frame, AV_GET_BUFFER_FLAG_REF)) < 0) goto error; - ff_thread_release_ext_buffer(avctx, &s->last_frame); + ff_thread_release_ext_buffer(&s->last_frame); if ((ret = ff_thread_ref_frame(&s->last_frame, &s->golden_frame)) < 0) goto error; ff_thread_report_progress(&s->last_frame, INT_MAX, 0); } } + ff_thread_finish_setup(avctx); memset(s->all_fragments, 0, s->fragment_count * sizeof(Vp3Fragment)); - ff_thread_finish_setup(avctx); if (s->version < 2) { if ((ret = unpack_superblocks(s, &gb)) < 0) { @@ -2797,7 +2814,7 @@ static int vp3_decode_frame(AVCodecContext *avctx, AVFrame *frame, #endif } - for (i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { int height = s->height >> (i && s->chroma_y_shift); if (s->flipped_image) s->data_offset[i] = 0; @@ -2806,12 +2823,12 @@ static int vp3_decode_frame(AVCodecContext *avctx, AVFrame *frame, } s->last_slice_end = 0; - for (i = 0; i < s->c_superblock_height; i++) + for (int i = 0; i < s->c_superblock_height; i++) render_slice(s, i); // filter the last row if (s->version < 2) - for (i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { int row = (s->height >> (3 + (i && s->chroma_y_shift))) - 1; apply_loop_filter(s, i, row, row + 1); } @@ -3000,7 +3017,7 @@ static int theora_decode_header(AVCodecContext *avctx, GetBitContext *gb) static int theora_decode_tables(AVCodecContext *avctx, GetBitContext *gb) { Vp3DecodeContext *s = avctx->priv_data; - int i, n, matrices, inter, plane, ret; + int n, matrices, ret; if (!s->theora_header) return AVERROR_INVALIDDATA; @@ -3009,7 +3026,7 @@ static int theora_decode_tables(AVCodecContext *avctx, GetBitContext *gb) n = get_bits(gb, 3); /* loop filter limit values table */ if (n) - for (i = 0; i < 64; i++) + for (int i = 0; i < 64; i++) s->filter_limit_values[i] = get_bits(gb, n); } @@ -3018,7 +3035,7 @@ static int theora_decode_tables(AVCodecContext *avctx, GetBitContext *gb) else n = 16; /* quality threshold table */ - for (i = 0; i < 64; i++) + for (int i = 0; i < 64; i++) s->coded_ac_scale_factor[i] = get_bits(gb, n); if (s->theora >= 0x030200) @@ -3026,7 +3043,7 @@ static int theora_decode_tables(AVCodecContext *avctx, GetBitContext *gb) else n = 16; /* dc scale factor table */ - for (i = 0; i < 64; i++) + for (int i = 0; i < 64; i++) s->coded_dc_scale_factor[0][i] = s->coded_dc_scale_factor[1][i] = get_bits(gb, n); @@ -3040,12 +3057,12 @@ static int theora_decode_tables(AVCodecContext *avctx, GetBitContext *gb) return -1; } - for (n = 0; n < matrices; n++) - for (i = 0; i < 64; i++) - s->base_matrix[n][i] = get_bits(gb, 8); + for (int j = 0; j < matrices; j++) + for (int i = 0; i < 64; i++) + s->base_matrix[j][i] = get_bits(gb, 8); - for (inter = 0; inter <= 1; inter++) { - for (plane = 0; plane <= 2; plane++) { + for (int inter = 0; inter <= 1; inter++) { + for (int plane = 0; plane <= 2; plane++) { int newqr = 1; if (inter || plane > 0) newqr = get_bits1(gb); @@ -3068,7 +3085,7 @@ static int theora_decode_tables(AVCodecContext *avctx, GetBitContext *gb) int qi = 0; for (;;) { - i = get_bits(gb, av_log2(matrices - 1) + 1); + int i = get_bits(gb, av_log2(matrices - 1) + 1); if (i >= matrices) { av_log(avctx, AV_LOG_ERROR, "invalid base matrix index\n"); @@ -3110,7 +3127,6 @@ static av_cold int theora_decode_init(AVCodecContext *avctx) int ptype; const uint8_t *header_start[3]; int header_len[3]; - int i; int ret; avctx->pix_fmt = AV_PIX_FMT_YUV420P; @@ -3128,7 +3144,7 @@ static av_cold int theora_decode_init(AVCodecContext *avctx) return -1; } - for (i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { if (header_len[i] <= 0) continue; ret = init_get_bits8(&gb, header_start[i], header_len[i]); diff --git a/libavcodec/vp5.c b/libavcodec/vp5.c index 579333506af..78d4b38ce3e 100644 --- a/libavcodec/vp5.c +++ b/libavcodec/vp5.c @@ -44,10 +44,13 @@ static int vp5_parse_header(VP56Context *s, const uint8_t *buf, int buf_size) ret = ff_vpx_init_range_decoder(&s->c, buf, buf_size); if (ret < 0) return ret; - s->frames[VP56_FRAME_CURRENT]->key_frame = !vpx_rac_get(c); + if (!vpx_rac_get(c)) + s->frames[VP56_FRAME_CURRENT]->flags |= AV_FRAME_FLAG_KEY; + else + s->frames[VP56_FRAME_CURRENT]->flags &= ~AV_FRAME_FLAG_KEY; vpx_rac_get(c); ff_vp56_init_dequant(s, vp56_rac_gets(c, 6)); - if (s->frames[VP56_FRAME_CURRENT]->key_frame) + if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) { int render_x, render_y; @@ -148,7 +151,7 @@ static int vp5_parse_coeff_models(VP56Context *s) if (vpx_rac_get_prob_branchy(c, vp5_dccv_pct[pt][node])) { def_prob[node] = vp56_rac_gets_nn(c, 7); model->coeff_dccv[pt][node] = def_prob[node]; - } else if (s->frames[VP56_FRAME_CURRENT]->key_frame) { + } else if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) { model->coeff_dccv[pt][node] = def_prob[node]; } @@ -159,7 +162,7 @@ static int vp5_parse_coeff_models(VP56Context *s) if (vpx_rac_get_prob_branchy(c, vp5_ract_pct[ct][pt][cg][node])) { def_prob[node] = vp56_rac_gets_nn(c, 7); model->coeff_ract[pt][ct][cg][node] = def_prob[node]; - } else if (s->frames[VP56_FRAME_CURRENT]->key_frame) { + } else if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) { model->coeff_ract[pt][ct][cg][node] = def_prob[node]; } diff --git a/libavcodec/vp56.c b/libavcodec/vp56.c index bd994428a42..f2ee3fafd46 100644 --- a/libavcodec/vp56.c +++ b/libavcodec/vp56.c @@ -350,7 +350,7 @@ static void vp56_mc(VP56Context *s, int b, int plane, uint8_t *src, if (s->avctx->skip_loop_filter >= AVDISCARD_ALL || (s->avctx->skip_loop_filter >= AVDISCARD_NONKEY - && !s->frames[VP56_FRAME_CURRENT]->key_frame)) + && !(s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY))) deblock_filtering = 0; dx = s->mv[b].x / s->vp56_coord_div[b]; @@ -493,7 +493,7 @@ static int vp56_decode_mb(VP56Context *s, int row, int col, int is_alpha) VP56mb mb_type; int ret; - if (s->frames[VP56_FRAME_CURRENT]->key_frame) + if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) mb_type = VP56_MB_INTRA; else mb_type = vp56_decode_mv(s, row, col); @@ -511,7 +511,7 @@ static int vp56_conceal_mb(VP56Context *s, int row, int col, int is_alpha) { VP56mb mb_type; - if (s->frames[VP56_FRAME_CURRENT]->key_frame) + if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) mb_type = VP56_MB_INTRA; else mb_type = vp56_conceal_mv(s, row, col); @@ -596,6 +596,7 @@ int ff_vp56_decode_frame(AVCodecContext *avctx, AVFrame *rframe, if (s->alpha_context) av_frame_unref(s->alpha_context->frames[i]); } + s->frames[VP56_FRAME_CURRENT]->flags |= AV_FRAME_FLAG_KEY; //FIXME } ret = ff_get_buffer(avctx, p, AV_GET_BUFFER_FLAG_REF); @@ -606,8 +607,7 @@ int ff_vp56_decode_frame(AVCodecContext *avctx, AVFrame *rframe, } if (avctx->pix_fmt == AV_PIX_FMT_YUVA420P) { - av_frame_unref(s->alpha_context->frames[VP56_FRAME_CURRENT]); - if ((ret = av_frame_ref(s->alpha_context->frames[VP56_FRAME_CURRENT], p)) < 0) { + if ((ret = av_frame_replace(s->alpha_context->frames[VP56_FRAME_CURRENT], p)) < 0) { av_frame_unref(p); if (res == VP56_SIZE_CHANGE) ff_set_dimensions(avctx, 0, 0); @@ -670,7 +670,7 @@ static int ff_vp56_decode_mbs(AVCodecContext *avctx, void *data, int res; int damaged = 0; - if (p->key_frame) { + if (p->flags & AV_FRAME_FLAG_KEY) { p->pict_type = AV_PICTURE_TYPE_I; s->default_models_init(s); for (block=0; blockmb_height*s->mb_width; block++) @@ -762,9 +762,8 @@ static int ff_vp56_decode_mbs(AVCodecContext *avctx, void *data, s->have_undamaged_frame = 1; next: - if (p->key_frame || s->golden_frame) { - av_frame_unref(s->frames[VP56_FRAME_GOLDEN]); - if ((res = av_frame_ref(s->frames[VP56_FRAME_GOLDEN], p)) < 0) + if ((p->flags & AV_FRAME_FLAG_KEY) || s->golden_frame) { + if ((res = av_frame_replace(s->frames[VP56_FRAME_GOLDEN], p)) < 0) return res; } diff --git a/libavcodec/vp6.c b/libavcodec/vp6.c index 9bbfa0eb5d7..97d63a58702 100644 --- a/libavcodec/vp6.c +++ b/libavcodec/vp6.c @@ -57,10 +57,13 @@ static int vp6_parse_header(VP56Context *s, const uint8_t *buf, int buf_size) int ret; int separated_coeff = buf[0] & 1; - s->frames[VP56_FRAME_CURRENT]->key_frame = !(buf[0] & 0x80); + if (!(buf[0] & 0x80)) + s->frames[VP56_FRAME_CURRENT]->flags |= AV_FRAME_FLAG_KEY; + else + s->frames[VP56_FRAME_CURRENT]->flags &= ~AV_FRAME_FLAG_KEY; ff_vp56_init_dequant(s, (buf[0] >> 1) & 0x3F); - if (s->frames[VP56_FRAME_CURRENT]->key_frame) { + if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) { sub_version = buf[1] >> 3; if (sub_version > 8) return AVERROR_INVALIDDATA; @@ -276,7 +279,7 @@ static int vp6_build_huff_tree(VP56Context *s, uint8_t coeff_model[], nodes[map[2*i+1]].count = b + !b; } - ff_free_vlc(vlc); + ff_vlc_free(vlc); /* then build the huffman tree according to probabilities */ return ff_huff_build_tree(s->avctx, vlc, size, FF_HUFFMAN_BITS, nodes, vp6_huff_cmp, @@ -299,7 +302,7 @@ static int vp6_parse_coeff_models(VP56Context *s) if (vpx_rac_get_prob_branchy(c, vp6_dccv_pct[pt][node])) { def_prob[node] = vp56_rac_gets_nn(c, 7); model->coeff_dccv[pt][node] = def_prob[node]; - } else if (s->frames[VP56_FRAME_CURRENT]->key_frame) { + } else if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) { model->coeff_dccv[pt][node] = def_prob[node]; } @@ -322,7 +325,7 @@ static int vp6_parse_coeff_models(VP56Context *s) if (vpx_rac_get_prob_branchy(c, vp6_ract_pct[ct][pt][cg][node])) { def_prob[node] = vp56_rac_gets_nn(c, 7); model->coeff_ract[pt][ct][cg][node] = def_prob[node]; - } else if (s->frames[VP56_FRAME_CURRENT]->key_frame) { + } else if (s->frames[VP56_FRAME_CURRENT]->flags & AV_FRAME_FLAG_KEY) { model->coeff_ract[pt][ct][cg][node] = def_prob[node]; } @@ -705,11 +708,11 @@ static av_cold void vp6_decode_free_context(VP56Context *s) ff_vp56_free_context(s); for (pt=0; pt<2; pt++) { - ff_free_vlc(&s->dccv_vlc[pt]); - ff_free_vlc(&s->runv_vlc[pt]); + ff_vlc_free(&s->dccv_vlc[pt]); + ff_vlc_free(&s->runv_vlc[pt]); for (ct=0; ct<3; ct++) for (cg=0; cg<6; cg++) - ff_free_vlc(&s->ract_vlc[pt][ct][cg]); + ff_vlc_free(&s->ract_vlc[pt][ct][cg]); } } diff --git a/libavcodec/vp8.c b/libavcodec/vp8.c index db2419deaf7..faca7ecc1bd 100644 --- a/libavcodec/vp8.c +++ b/libavcodec/vp8.c @@ -31,8 +31,10 @@ #include "avcodec.h" #include "codec_internal.h" #include "decode.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "mathops.h" +#include "refstruct.h" #include "thread.h" #include "threadframe.h" #include "vp8.h" @@ -104,53 +106,42 @@ static int vp8_alloc_frame(VP8Context *s, VP8Frame *f, int ref) if ((ret = ff_thread_get_ext_buffer(s->avctx, &f->tf, ref ? AV_GET_BUFFER_FLAG_REF : 0)) < 0) return ret; - if (!(f->seg_map = av_buffer_allocz(s->mb_width * s->mb_height))) + f->seg_map = ff_refstruct_allocz(s->mb_width * s->mb_height); + if (!f->seg_map) { + ret = AVERROR(ENOMEM); goto fail; - if (s->avctx->hwaccel) { - const AVHWAccel *hwaccel = s->avctx->hwaccel; - if (hwaccel->frame_priv_data_size) { - f->hwaccel_priv_buf = av_buffer_allocz(hwaccel->frame_priv_data_size); - if (!f->hwaccel_priv_buf) - goto fail; - f->hwaccel_picture_private = f->hwaccel_priv_buf->data; - } } + ret = ff_hwaccel_frame_priv_alloc(s->avctx, &f->hwaccel_picture_private); + if (ret < 0) + goto fail; + return 0; fail: - av_buffer_unref(&f->seg_map); - ff_thread_release_ext_buffer(s->avctx, &f->tf); - return AVERROR(ENOMEM); + ff_refstruct_unref(&f->seg_map); + ff_thread_release_ext_buffer(&f->tf); + return ret; } -static void vp8_release_frame(VP8Context *s, VP8Frame *f) +static void vp8_release_frame(VP8Frame *f) { - av_buffer_unref(&f->seg_map); - av_buffer_unref(&f->hwaccel_priv_buf); - f->hwaccel_picture_private = NULL; - ff_thread_release_ext_buffer(s->avctx, &f->tf); + ff_refstruct_unref(&f->seg_map); + ff_refstruct_unref(&f->hwaccel_picture_private); + ff_thread_release_ext_buffer(&f->tf); } #if CONFIG_VP8_DECODER -static int vp8_ref_frame(VP8Context *s, VP8Frame *dst, const VP8Frame *src) +static int vp8_ref_frame(VP8Frame *dst, const VP8Frame *src) { int ret; - vp8_release_frame(s, dst); + vp8_release_frame(dst); if ((ret = ff_thread_ref_frame(&dst->tf, &src->tf)) < 0) return ret; - if (src->seg_map && - !(dst->seg_map = av_buffer_ref(src->seg_map))) { - vp8_release_frame(s, dst); - return AVERROR(ENOMEM); - } - if (src->hwaccel_picture_private) { - dst->hwaccel_priv_buf = av_buffer_ref(src->hwaccel_priv_buf); - if (!dst->hwaccel_priv_buf) - return AVERROR(ENOMEM); - dst->hwaccel_picture_private = dst->hwaccel_priv_buf->data; - } + ff_refstruct_replace(&dst->seg_map, src->seg_map); + ff_refstruct_replace(&dst->hwaccel_picture_private, + src->hwaccel_picture_private); return 0; } @@ -162,11 +153,14 @@ static void vp8_decode_flush_impl(AVCodecContext *avctx, int free_mem) int i; for (i = 0; i < FF_ARRAY_ELEMS(s->frames); i++) - vp8_release_frame(s, &s->frames[i]); + vp8_release_frame(&s->frames[i]); memset(s->framep, 0, sizeof(s->framep)); if (free_mem) free_buffers(s); + + if (FF_HW_HAS_CB(avctx, flush)) + FF_HW_SIMPLE_CALL(avctx, flush); } static void vp8_decode_flush(AVCodecContext *avctx) @@ -193,7 +187,7 @@ static VP8Frame *vp8_find_free_buffer(VP8Context *s) abort(); } if (frame->tf.f->buf[0]) - vp8_release_frame(s, frame); + vp8_release_frame(frame); return frame; } @@ -269,8 +263,16 @@ int update_dimensions(VP8Context *s, int width, int height, int is_vp7) return AVERROR(ENOMEM); } #if HAVE_THREADS - pthread_mutex_init(&s->thread_data[i].lock, NULL); - pthread_cond_init(&s->thread_data[i].cond, NULL); + ret = pthread_mutex_init(&s->thread_data[i].lock, NULL); + if (ret) { + free_buffers(s); + return AVERROR(ret); + } + ret = pthread_cond_init(&s->thread_data[i].cond, NULL); + if (ret) { + free_buffers(s); + return AVERROR(ret); + } #endif } @@ -363,9 +365,8 @@ static int setup_partitions(VP8Context *s, const uint8_t *buf, int buf_size) } s->coeff_partition_size[i] = buf_size; - ff_vpx_init_range_decoder(&s->coeff_partition[i], buf, buf_size); - return 0; + return ff_vpx_init_range_decoder(&s->coeff_partition[i], buf, buf_size); } static void vp7_get_quants(VP8Context *s) @@ -467,7 +468,7 @@ static void vp78_update_probability_tables(VP8Context *s) for (j = 0; j < 8; j++) for (k = 0; k < 3; k++) for (l = 0; l < NUM_DCT_TOKENS-1; l++) - if (vpx_rac_get_prob_branchy(c, vp8_token_update_probs[i][j][k][l])) { + if (vpx_rac_get_prob_branchy(c, ff_vp8_token_update_probs[i][j][k][l])) { int prob = vp89_rac_get_uint(c, 8); for (m = 0; vp8_coeff_band_indexes[j][m] >= 0; m++) s->prob->token[i][vp8_coeff_band_indexes[j][m]][k][l] = prob; @@ -2332,9 +2333,9 @@ int vp78_decode_mv_mb_modes(AVCodecContext *avctx, VP8Frame *curframe, if (mb_y == 0) AV_WN32A((mb - s->mb_width - 1)->intra4x4_pred_mode_top, DC_PRED * 0x01010101); - decode_mb_mode(s, &s->mv_bounds, mb, mb_x, mb_y, curframe->seg_map->data + mb_xy, + decode_mb_mode(s, &s->mv_bounds, mb, mb_x, mb_y, curframe->seg_map + mb_xy, prev_frame && prev_frame->seg_map ? - prev_frame->seg_map->data + mb_xy : NULL, 1, is_vp7); + prev_frame->seg_map + mb_xy : NULL, 1, is_vp7); s->mv_bounds.mv_min.x -= 64; s->mv_bounds.mv_max.x -= 64; } @@ -2465,9 +2466,9 @@ static av_always_inline int decode_mb_row_no_filter(AVCodecContext *avctx, void dst[2] - dst[1], 2); if (!s->mb_layout) - decode_mb_mode(s, &td->mv_bounds, mb, mb_x, mb_y, curframe->seg_map->data + mb_xy, + decode_mb_mode(s, &td->mv_bounds, mb, mb_x, mb_y, curframe->seg_map + mb_xy, prev_frame && prev_frame->seg_map ? - prev_frame->seg_map->data + mb_xy : NULL, 0, is_vp7); + prev_frame->seg_map + mb_xy : NULL, 0, is_vp7); prefetch_motion(s, mb, mb_x, mb_y, mb_xy, VP8_FRAME_PREVIOUS); @@ -2674,7 +2675,11 @@ int vp78_decode_frame(AVCodecContext *avctx, AVFrame *rframe, int *got_frame, if (ret < 0) goto err; - if (s->actually_webp) { + if (!is_vp7 && s->actually_webp) { + // VP8 in WebP is supposed to be intra-only. Enforce this here + // to ensure that output is reproducible with frame-threading. + if (!s->keyframe) + return AVERROR_INVALIDDATA; // avctx->pix_fmt already set in caller. } else if (!is_vp7 && s->pix_fmt == AV_PIX_FMT_NONE) { s->pix_fmt = get_pixel_format(s); @@ -2708,7 +2713,7 @@ int vp78_decode_frame(AVCodecContext *avctx, AVFrame *rframe, int *got_frame, &s->frames[i] != s->framep[VP8_FRAME_PREVIOUS] && &s->frames[i] != s->framep[VP8_FRAME_GOLDEN] && &s->frames[i] != s->framep[VP8_FRAME_ALTREF]) - vp8_release_frame(s, &s->frames[i]); + vp8_release_frame(&s->frames[i]); curframe = s->framep[VP8_FRAME_CURRENT] = vp8_find_free_buffer(s); @@ -2732,7 +2737,10 @@ int vp78_decode_frame(AVCodecContext *avctx, AVFrame *rframe, int *got_frame, goto err; } - curframe->tf.f->key_frame = s->keyframe; + if (s->keyframe) + curframe->tf.f->flags |= AV_FRAME_FLAG_KEY; + else + curframe->tf.f->flags &= ~AV_FRAME_FLAG_KEY; curframe->tf.f->pict_type = s->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if ((ret = vp8_alloc_frame(s, curframe, referenced)) < 0) @@ -2756,19 +2764,20 @@ int vp78_decode_frame(AVCodecContext *avctx, AVFrame *rframe, int *got_frame, s->next_framep[VP8_FRAME_CURRENT] = curframe; - if (ffcodec(avctx->codec)->update_thread_context) + if (!is_vp7 && !s->actually_webp) ff_thread_finish_setup(avctx); if (avctx->hwaccel) { - ret = avctx->hwaccel->start_frame(avctx, avpkt->data, avpkt->size); + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); + ret = hwaccel->start_frame(avctx, avpkt->data, avpkt->size); if (ret < 0) goto err; - ret = avctx->hwaccel->decode_slice(avctx, avpkt->data, avpkt->size); + ret = hwaccel->decode_slice(avctx, avpkt->data, avpkt->size); if (ret < 0) goto err; - ret = avctx->hwaccel->end_frame(avctx); + ret = hwaccel->end_frame(avctx); if (ret < 0) goto err; @@ -2888,7 +2897,6 @@ int vp78_decode_init(AVCodecContext *avctx, int is_vp7) int ret; s->avctx = avctx; - s->vp7 = avctx->codec->id == AV_CODEC_ID_VP7; s->pix_fmt = AV_PIX_FMT_NONE; avctx->pix_fmt = AV_PIX_FMT_YUV420P; @@ -2955,7 +2963,7 @@ static int vp8_decode_update_thread_context(AVCodecContext *dst, for (i = 0; i < FF_ARRAY_ELEMS(s_src->frames); i++) { if (s_src->frames[i].tf.f->buf[0]) { - int ret = vp8_ref_frame(s, &s->frames[i], &s_src->frames[i]); + int ret = vp8_ref_frame(&s->frames[i], &s_src->frames[i]); if (ret < 0) return ret; } diff --git a/libavcodec/vp8.h b/libavcodec/vp8.h index 6f29156b537..798f67b3de2 100644 --- a/libavcodec/vp8.h +++ b/libavcodec/vp8.h @@ -28,7 +28,6 @@ #include -#include "libavutil/buffer.h" #include "libavutil/mem_internal.h" #include "libavutil/thread.h" @@ -152,10 +151,9 @@ typedef struct VP8ThreadData { typedef struct VP8Frame { ThreadFrame tf; - AVBufferRef *seg_map; + uint8_t *seg_map; ///< RefStruct reference - AVBufferRef *hwaccel_priv_buf; - void *hwaccel_picture_private; + void *hwaccel_picture_private; ///< RefStruct reference } VP8Frame; #define MAX_THREADS 8 @@ -333,8 +331,6 @@ typedef struct VP8Context { int (*decode_mb_row_no_filter)(AVCodecContext *avctx, void *tdata, int jobnr, int threadnr); void (*filter_mb_row)(AVCodecContext *avctx, void *tdata, int jobnr, int threadnr); - int vp7; - /** * Interframe DC prediction (VP7) * [0] VP8_FRAME_PREVIOUS diff --git a/libavcodec/vp8data.c b/libavcodec/vp8data.c new file mode 100644 index 00000000000..675c2a033da --- /dev/null +++ b/libavcodec/vp8data.c @@ -0,0 +1,212 @@ +/* + * 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 + +// cat 1 and 2 are defined in vp8data.h +static const uint8_t vp8_dct_cat3_prob[] = { + 173, 148, 140, 0 +}; +static const uint8_t vp8_dct_cat4_prob[] = { + 176, 155, 140, 135, 0 +}; +static const uint8_t vp8_dct_cat5_prob[] = { + 180, 157, 141, 134, 130, 0 +}; +static const uint8_t vp8_dct_cat6_prob[] = { + 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 +}; + +// only used for cat3 and above; cat 1 and 2 are referenced directly. +const uint8_t *const ff_vp8_dct_cat_prob[] = { + vp8_dct_cat3_prob, + vp8_dct_cat4_prob, + vp8_dct_cat5_prob, + vp8_dct_cat6_prob, +}; + +const uint8_t ff_vp8_token_update_probs[4][8][3][11] = { + { + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 }, + { 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + }, + { + { + { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 }, + { 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 }, + }, + { + { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + }, + { + { + { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 }, + { 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 }, + }, + { + { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + }, + { + { + { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 }, + { 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + { + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, + }, + }, +}; diff --git a/libavcodec/vp8data.h b/libavcodec/vp8data.h index 1fcce134eb2..c8e79fdbebb 100644 --- a/libavcodec/vp8data.h +++ b/libavcodec/vp8data.h @@ -339,26 +339,8 @@ static const uint8_t vp8_dct_cat1_prob[] = { static const uint8_t vp8_dct_cat2_prob[] = { 165, 145, 0 }; -static const uint8_t vp8_dct_cat3_prob[] = { - 173, 148, 140, 0 -}; -static const uint8_t vp8_dct_cat4_prob[] = { - 176, 155, 140, 135, 0 -}; -static const uint8_t vp8_dct_cat5_prob[] = { - 180, 157, 141, 134, 130, 0 -}; -static const uint8_t vp8_dct_cat6_prob[] = { - 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 -}; -// only used for cat3 and above; cat 1 and 2 are referenced directly -const uint8_t *const ff_vp8_dct_cat_prob[] = { - vp8_dct_cat3_prob, - vp8_dct_cat4_prob, - vp8_dct_cat5_prob, - vp8_dct_cat6_prob, -}; +extern const uint8_t *const ff_vp8_dct_cat_prob[]; static const uint8_t vp8_token_default_probs[4][8][3][NUM_DCT_TOKENS - 1] = { { @@ -531,176 +513,7 @@ static const uint8_t vp8_token_default_probs[4][8][3][NUM_DCT_TOKENS - 1] = { }, }; -static const uint8_t vp8_token_update_probs[4][8][3][NUM_DCT_TOKENS - 1] = { - { - { - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 }, - { 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - }, - { - { - { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 }, - { 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 }, - }, - { - { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - }, - { - { - { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 }, - { 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 }, - }, - { - { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - }, - { - { - { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 }, - { 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - { - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }, - }, - }, -}; +extern const uint8_t ff_vp8_token_update_probs[4][8][3][11]; static const uint8_t vp8_dc_qlookup[VP8_MAX_QUANT + 1] = { 4, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 17, diff --git a/libavcodec/vp8dsp.c b/libavcodec/vp8dsp.c index 7a85e9f4cae..72d4ea37933 100644 --- a/libavcodec/vp8dsp.c +++ b/libavcodec/vp8dsp.c @@ -742,6 +742,8 @@ av_cold void ff_vp8dsp_init(VP8DSPContext *dsp) ff_vp8dsp_init_aarch64(dsp); #elif ARCH_ARM ff_vp8dsp_init_arm(dsp); +#elif ARCH_RISCV + ff_vp8dsp_init_riscv(dsp); #elif ARCH_X86 ff_vp8dsp_init_x86(dsp); #elif ARCH_MIPS diff --git a/libavcodec/vp8dsp.h b/libavcodec/vp8dsp.h index 16b5e9c35b8..30dc2c6cc1c 100644 --- a/libavcodec/vp8dsp.h +++ b/libavcodec/vp8dsp.h @@ -92,6 +92,7 @@ void ff_vp78dsp_init_x86(VP8DSPContext *c); void ff_vp8dsp_init(VP8DSPContext *c); void ff_vp8dsp_init_aarch64(VP8DSPContext *c); void ff_vp8dsp_init_arm(VP8DSPContext *c); +void ff_vp8dsp_init_riscv(VP8DSPContext *c); void ff_vp8dsp_init_x86(VP8DSPContext *c); void ff_vp8dsp_init_mips(VP8DSPContext *c); void ff_vp8dsp_init_loongarch(VP8DSPContext *c); diff --git a/libavcodec/vp9.c b/libavcodec/vp9.c index 7c0a2464467..4cc52813038 100644 --- a/libavcodec/vp9.c +++ b/libavcodec/vp9.c @@ -27,8 +27,10 @@ #include "codec_internal.h" #include "decode.h" #include "get_bits.h" +#include "hwaccel_internal.h" #include "hwconfig.h" #include "profiles.h" +#include "refstruct.h" #include "thread.h" #include "threadframe.h" #include "pthread_internal.h" @@ -95,13 +97,12 @@ static void vp9_tile_data_free(VP9TileData *td) av_freep(&td->block_structure); } -static void vp9_frame_unref(AVCodecContext *avctx, VP9Frame *f) +static void vp9_frame_unref(VP9Frame *f) { - ff_thread_release_ext_buffer(avctx, &f->tf); - av_buffer_unref(&f->extradata); - av_buffer_unref(&f->hwaccel_priv_buf); + ff_thread_release_ext_buffer(&f->tf); + ff_refstruct_unref(&f->extradata); + ff_refstruct_unref(&f->hwaccel_picture_private); f->segmentation_map = NULL; - f->hwaccel_picture_private = NULL; } static int vp9_frame_alloc(AVCodecContext *avctx, VP9Frame *f) @@ -115,42 +116,37 @@ static int vp9_frame_alloc(AVCodecContext *avctx, VP9Frame *f) sz = 64 * s->sb_cols * s->sb_rows; if (sz != s->frame_extradata_pool_size) { - av_buffer_pool_uninit(&s->frame_extradata_pool); - s->frame_extradata_pool = av_buffer_pool_init(sz * (1 + sizeof(VP9mvrefPair)), NULL); + ff_refstruct_pool_uninit(&s->frame_extradata_pool); + s->frame_extradata_pool = ff_refstruct_pool_alloc(sz * (1 + sizeof(VP9mvrefPair)), + FF_REFSTRUCT_POOL_FLAG_ZERO_EVERY_TIME); if (!s->frame_extradata_pool) { s->frame_extradata_pool_size = 0; + ret = AVERROR(ENOMEM); goto fail; } s->frame_extradata_pool_size = sz; } - f->extradata = av_buffer_pool_get(s->frame_extradata_pool); + f->extradata = ff_refstruct_pool_get(s->frame_extradata_pool); if (!f->extradata) { + ret = AVERROR(ENOMEM); goto fail; } - memset(f->extradata->data, 0, f->extradata->size); - f->segmentation_map = f->extradata->data; - f->mv = (VP9mvrefPair *) (f->extradata->data + sz); + f->segmentation_map = f->extradata; + f->mv = (VP9mvrefPair *) ((char*)f->extradata + sz); - if (avctx->hwaccel) { - const AVHWAccel *hwaccel = avctx->hwaccel; - av_assert0(!f->hwaccel_picture_private); - if (hwaccel->frame_priv_data_size) { - f->hwaccel_priv_buf = av_buffer_allocz(hwaccel->frame_priv_data_size); - if (!f->hwaccel_priv_buf) - goto fail; - f->hwaccel_picture_private = f->hwaccel_priv_buf->data; - } - } + ret = ff_hwaccel_frame_priv_alloc(avctx, &f->hwaccel_picture_private); + if (ret < 0) + goto fail; return 0; fail: - vp9_frame_unref(avctx, f); - return AVERROR(ENOMEM); + vp9_frame_unref(f); + return ret; } -static int vp9_frame_ref(AVCodecContext *avctx, VP9Frame *dst, VP9Frame *src) +static int vp9_frame_ref(VP9Frame *dst, VP9Frame *src) { int ret; @@ -158,32 +154,23 @@ static int vp9_frame_ref(AVCodecContext *avctx, VP9Frame *dst, VP9Frame *src) if (ret < 0) return ret; - dst->extradata = av_buffer_ref(src->extradata); - if (!dst->extradata) - goto fail; + dst->extradata = ff_refstruct_ref(src->extradata); dst->segmentation_map = src->segmentation_map; dst->mv = src->mv; dst->uses_2pass = src->uses_2pass; - if (src->hwaccel_picture_private) { - dst->hwaccel_priv_buf = av_buffer_ref(src->hwaccel_priv_buf); - if (!dst->hwaccel_priv_buf) - goto fail; - dst->hwaccel_picture_private = dst->hwaccel_priv_buf->data; - } + ff_refstruct_replace(&dst->hwaccel_picture_private, + src->hwaccel_picture_private); return 0; - -fail: - vp9_frame_unref(avctx, dst); - return AVERROR(ENOMEM); } static int update_size(AVCodecContext *avctx, int w, int h) { #define HWACCEL_MAX (CONFIG_VP9_DXVA2_HWACCEL + \ CONFIG_VP9_D3D11VA_HWACCEL * 2 + \ + CONFIG_VP9_D3D12VA_HWACCEL + \ CONFIG_VP9_NVDEC_HWACCEL + \ CONFIG_VP9_VAAPI_HWACCEL + \ CONFIG_VP9_VDPAU_HWACCEL + \ @@ -210,6 +197,9 @@ static int update_size(AVCodecContext *avctx, int w, int h) *fmtp++ = AV_PIX_FMT_D3D11VA_VLD; *fmtp++ = AV_PIX_FMT_D3D11; #endif +#if CONFIG_VP9_D3D12VA_HWACCEL + *fmtp++ = AV_PIX_FMT_D3D12; +#endif #if CONFIG_VP9_NVDEC_HWACCEL *fmtp++ = AV_PIX_FMT_CUDA; #endif @@ -239,6 +229,13 @@ static int update_size(AVCodecContext *avctx, int w, int h) case AV_PIX_FMT_YUV444P12: #if CONFIG_VP9_VAAPI_HWACCEL *fmtp++ = AV_PIX_FMT_VAAPI; +#endif + break; + case AV_PIX_FMT_GBRP: + case AV_PIX_FMT_GBRP10: + case AV_PIX_FMT_GBRP12: +#if CONFIG_VP9_VAAPI_HWACCEL + *fmtp++ = AV_PIX_FMT_VAAPI; #endif break; } @@ -246,7 +243,7 @@ static int update_size(AVCodecContext *avctx, int w, int h) *fmtp++ = s->pix_fmt; *fmtp = AV_PIX_FMT_NONE; - ret = ff_thread_get_format(avctx, pix_fmts); + ret = ff_get_format(avctx, pix_fmts); if (ret < 0) return ret; @@ -720,6 +717,12 @@ static int decode_frame_header(AVCodecContext *avctx, s->s.h.segmentation.feat[i].skip_enabled = get_bits1(&s->gb); } } + } else { + // Reset fields under segmentation switch if segmentation is disabled. + // This is necessary because some hwaccels don't ignore these fields + // if segmentation is disabled. + s->s.h.segmentation.temporal = 0; + s->s.h.segmentation.update_map = 0; } // set qmul[] based on Y/UV, AC/DC and segmentation Q idx deltas @@ -1243,14 +1246,14 @@ static av_cold int vp9_decode_free(AVCodecContext *avctx) int i; for (i = 0; i < 3; i++) { - vp9_frame_unref(avctx, &s->s.frames[i]); + vp9_frame_unref(&s->s.frames[i]); av_frame_free(&s->s.frames[i].tf.f); } - av_buffer_pool_uninit(&s->frame_extradata_pool); + ff_refstruct_pool_uninit(&s->frame_extradata_pool); for (i = 0; i < 8; i++) { - ff_thread_release_ext_buffer(avctx, &s->s.refs[i]); + ff_thread_release_ext_buffer(&s->s.refs[i]); av_frame_free(&s->s.refs[i].f); - ff_thread_release_ext_buffer(avctx, &s->next_refs[i]); + ff_thread_release_ext_buffer(&s->next_refs[i]); av_frame_free(&s->next_refs[i].f); } @@ -1578,7 +1581,7 @@ static int vp9_decode_frame(AVCodecContext *avctx, AVFrame *frame, frame->pkt_dts = pkt->dts; for (i = 0; i < 8; i++) { if (s->next_refs[i].f->buf[0]) - ff_thread_release_ext_buffer(avctx, &s->next_refs[i]); + ff_thread_release_ext_buffer(&s->next_refs[i]); if (s->s.refs[i].f->buf[0] && (ret = ff_thread_ref_frame(&s->next_refs[i], &s->s.refs[i])) < 0) return ret; @@ -1591,34 +1594,37 @@ static int vp9_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (!retain_segmap_ref || s->s.h.keyframe || s->s.h.intraonly) { if (s->s.frames[REF_FRAME_SEGMAP].tf.f->buf[0]) - vp9_frame_unref(avctx, &s->s.frames[REF_FRAME_SEGMAP]); + vp9_frame_unref(&s->s.frames[REF_FRAME_SEGMAP]); if (!s->s.h.keyframe && !s->s.h.intraonly && !s->s.h.errorres && s->s.frames[CUR_FRAME].tf.f->buf[0] && - (ret = vp9_frame_ref(avctx, &s->s.frames[REF_FRAME_SEGMAP], &s->s.frames[CUR_FRAME])) < 0) + (ret = vp9_frame_ref(&s->s.frames[REF_FRAME_SEGMAP], &s->s.frames[CUR_FRAME])) < 0) return ret; } if (s->s.frames[REF_FRAME_MVPAIR].tf.f->buf[0]) - vp9_frame_unref(avctx, &s->s.frames[REF_FRAME_MVPAIR]); + vp9_frame_unref(&s->s.frames[REF_FRAME_MVPAIR]); if (!s->s.h.intraonly && !s->s.h.keyframe && !s->s.h.errorres && s->s.frames[CUR_FRAME].tf.f->buf[0] && - (ret = vp9_frame_ref(avctx, &s->s.frames[REF_FRAME_MVPAIR], &s->s.frames[CUR_FRAME])) < 0) + (ret = vp9_frame_ref(&s->s.frames[REF_FRAME_MVPAIR], &s->s.frames[CUR_FRAME])) < 0) return ret; if (s->s.frames[CUR_FRAME].tf.f->buf[0]) - vp9_frame_unref(avctx, &s->s.frames[CUR_FRAME]); + vp9_frame_unref(&s->s.frames[CUR_FRAME]); if ((ret = vp9_frame_alloc(avctx, &s->s.frames[CUR_FRAME])) < 0) return ret; f = s->s.frames[CUR_FRAME].tf.f; - f->key_frame = s->s.h.keyframe; + if (s->s.h.keyframe) + f->flags |= AV_FRAME_FLAG_KEY; + else + f->flags &= ~AV_FRAME_FLAG_KEY; f->pict_type = (s->s.h.keyframe || s->s.h.intraonly) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if (s->s.frames[REF_FRAME_SEGMAP].tf.f->buf[0] && (s->s.frames[REF_FRAME_MVPAIR].tf.f->width != s->s.frames[CUR_FRAME].tf.f->width || s->s.frames[REF_FRAME_MVPAIR].tf.f->height != s->s.frames[CUR_FRAME].tf.f->height)) { - vp9_frame_unref(avctx, &s->s.frames[REF_FRAME_SEGMAP]); + vp9_frame_unref(&s->s.frames[REF_FRAME_SEGMAP]); } // ref frame setup for (i = 0; i < 8; i++) { if (s->next_refs[i].f->buf[0]) - ff_thread_release_ext_buffer(avctx, &s->next_refs[i]); + ff_thread_release_ext_buffer(&s->next_refs[i]); if (s->s.h.refreshrefmask & (1 << i)) { ret = ff_thread_ref_frame(&s->next_refs[i], &s->s.frames[CUR_FRAME].tf); } else if (s->s.refs[i].f->buf[0]) { @@ -1629,13 +1635,14 @@ static int vp9_decode_frame(AVCodecContext *avctx, AVFrame *frame, } if (avctx->hwaccel) { - ret = avctx->hwaccel->start_frame(avctx, NULL, 0); + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); + ret = hwaccel->start_frame(avctx, NULL, 0); if (ret < 0) return ret; - ret = avctx->hwaccel->decode_slice(avctx, pkt->data, pkt->size); + ret = hwaccel->decode_slice(avctx, pkt->data, pkt->size); if (ret < 0) return ret; - ret = avctx->hwaccel->end_frame(avctx); + ret = hwaccel->end_frame(avctx); if (ret < 0) return ret; goto finish; @@ -1767,7 +1774,7 @@ static int vp9_decode_frame(AVCodecContext *avctx, AVFrame *frame, // ref frame setup for (i = 0; i < 8; i++) { if (s->s.refs[i].f->buf[0]) - ff_thread_release_ext_buffer(avctx, &s->s.refs[i]); + ff_thread_release_ext_buffer(&s->s.refs[i]); if (s->next_refs[i].f->buf[0] && (ret = ff_thread_ref_frame(&s->s.refs[i], &s->next_refs[i])) < 0) return ret; @@ -1788,9 +1795,12 @@ static void vp9_decode_flush(AVCodecContext *avctx) int i; for (i = 0; i < 3; i++) - vp9_frame_unref(avctx, &s->s.frames[i]); + vp9_frame_unref(&s->s.frames[i]); for (i = 0; i < 8; i++) - ff_thread_release_ext_buffer(avctx, &s->s.refs[i]); + ff_thread_release_ext_buffer(&s->s.refs[i]); + + if (FF_HW_HAS_CB(avctx, flush)) + FF_HW_SIMPLE_CALL(avctx, flush); } static av_cold int vp9_decode_init(AVCodecContext *avctx) @@ -1831,20 +1841,22 @@ static int vp9_decode_update_thread_context(AVCodecContext *dst, const AVCodecCo for (i = 0; i < 3; i++) { if (s->s.frames[i].tf.f->buf[0]) - vp9_frame_unref(dst, &s->s.frames[i]); + vp9_frame_unref(&s->s.frames[i]); if (ssrc->s.frames[i].tf.f->buf[0]) { - if ((ret = vp9_frame_ref(dst, &s->s.frames[i], &ssrc->s.frames[i])) < 0) + if ((ret = vp9_frame_ref(&s->s.frames[i], &ssrc->s.frames[i])) < 0) return ret; } } for (i = 0; i < 8; i++) { if (s->s.refs[i].f->buf[0]) - ff_thread_release_ext_buffer(dst, &s->s.refs[i]); + ff_thread_release_ext_buffer(&s->s.refs[i]); if (ssrc->next_refs[i].f->buf[0]) { if ((ret = ff_thread_ref_frame(&s->s.refs[i], &ssrc->next_refs[i])) < 0) return ret; } } + ff_refstruct_replace(&s->frame_extradata_pool, ssrc->frame_extradata_pool); + s->frame_extradata_pool_size = ssrc->frame_extradata_pool_size; s->s.h.invisible = ssrc->s.h.invisible; s->s.h.keyframe = ssrc->s.h.keyframe; @@ -1897,6 +1909,9 @@ const FFCodec ff_vp9_decoder = { #if CONFIG_VP9_D3D11VA2_HWACCEL HWACCEL_D3D11VA2(vp9), #endif +#if CONFIG_VP9_D3D12VA_HWACCEL + HWACCEL_D3D12VA(vp9), +#endif #if CONFIG_VP9_NVDEC_HWACCEL HWACCEL_NVDEC(vp9), #endif diff --git a/libavcodec/vp9dec.h b/libavcodec/vp9dec.h index de7aba04585..013aac49eba 100644 --- a/libavcodec/vp9dec.h +++ b/libavcodec/vp9dec.h @@ -28,7 +28,6 @@ #include #include -#include "libavutil/buffer.h" #include "libavutil/mem_internal.h" #include "libavutil/thread.h" #include "libavutil/internal.h" @@ -161,7 +160,7 @@ typedef struct VP9Context { uint8_t mvstep[3][2]; // frame specific buffer pools - AVBufferPool *frame_extradata_pool; + struct FFRefStructPool *frame_extradata_pool; int frame_extradata_pool_size; } VP9Context; diff --git a/libavcodec/vp9shared.h b/libavcodec/vp9shared.h index 543a496df8f..b445a2a746c 100644 --- a/libavcodec/vp9shared.h +++ b/libavcodec/vp9shared.h @@ -64,13 +64,12 @@ typedef struct VP9mvrefPair { typedef struct VP9Frame { ThreadFrame tf; - AVBufferRef *extradata; + void *extradata; ///< RefStruct reference uint8_t *segmentation_map; VP9mvrefPair *mv; int uses_2pass; - AVBufferRef *hwaccel_priv_buf; - void *hwaccel_picture_private; + void *hwaccel_picture_private; ///< RefStruct reference } VP9Frame; enum BlockLevel { diff --git a/libavcodec/vqavideo.c b/libavcodec/vqavideo.c index 0573696d94d..2977cf9a526 100644 --- a/libavcodec/vqavideo.c +++ b/libavcodec/vqavideo.c @@ -809,7 +809,11 @@ static int vqa_decode_frame(AVCodecContext *avctx, AVFrame *rframe, /* make the palette available on the way out */ memcpy(s->frame->data[1], s->palette, PALETTE_COUNT * 4); +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS s->frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else if (avctx->pix_fmt == AV_PIX_FMT_RGB555LE) { if ((res = vqa_decode_frame_hicolor(s, s->frame)) < 0) return res; diff --git a/libavcodec/vqcdec.c b/libavcodec/vqcdec.c index c3bce879747..dbcaba7b236 100644 --- a/libavcodec/vqcdec.c +++ b/libavcodec/vqcdec.c @@ -49,14 +49,15 @@ static const int8_t vector_symbols[] = { 2, 3, 4, SIGNED_8BIT, -2, -3, -4, SIGNED_6BIT }; -static VLC vector_vlc; +static VLCElem vector_vlc[1 << VECTOR_VLC_BITS]; static av_cold void vqc_init_static_data(void) { - INIT_VLC_STATIC_FROM_LENGTHS(&vector_vlc, VECTOR_VLC_BITS, FF_ARRAY_ELEMS(vector_nbits), - vector_nbits, 1, - vector_symbols, 1, 1, - 0, 0, 1 << VECTOR_VLC_BITS); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(vector_vlc, VECTOR_VLC_BITS, + FF_ARRAY_ELEMS(vector_nbits), + vector_nbits, 1, + vector_symbols, 1, 1, + 0, 0); } typedef struct VqcContext { @@ -145,10 +146,13 @@ static int decode_vectors(VqcContext * s, const uint8_t * buf, int size, int wid GetBitContext gb; uint8_t * vectors = s->vectors; uint8_t * vectors_end = s->vectors + (width * height * 3) / 2; + int ret; memset(vectors, 0, 3 * width * height / 2); - init_get_bits8(&gb, buf, size); + ret = init_get_bits8(&gb, buf, size); + if (ret < 0) + return ret; for (int i = 0; i < 3 * width * height / 2 / 32; i++) { uint8_t * dst = vectors; @@ -171,7 +175,7 @@ static int decode_vectors(VqcContext * s, const uint8_t * buf, int size, int wid continue; } - symbol = get_vlc2(&gb, vector_vlc.table, VECTOR_VLC_BITS, 1); + symbol = get_vlc2(&gb, vector_vlc, VECTOR_VLC_BITS, 1); switch(symbol) { case SKIP_3: dst += 3; break; case SKIP_4: dst += 4; break; diff --git a/libavcodec/fft_float.c b/libavcodec/vulkan.c similarity index 94% rename from libavcodec/fft_float.c rename to libavcodec/vulkan.c index a9fd01978de..fc8a1fa47ba 100644 --- a/libavcodec/fft_float.c +++ b/libavcodec/vulkan.c @@ -16,5 +16,4 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#define FFT_FLOAT 1 -#include "fft_template.c" +#include "libavutil/vulkan.c" diff --git a/libavcodec/mdct_float.c b/libavcodec/vulkan.h similarity index 87% rename from libavcodec/mdct_float.c rename to libavcodec/vulkan.h index 3d3d3a55482..b15efd4addb 100644 --- a/libavcodec/mdct_float.c +++ b/libavcodec/vulkan.h @@ -16,5 +16,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#define FFT_FLOAT 1 -#include "mdct_template.c" +#ifndef AVCODEC_VULKAN_H +#define AVCODEC_VULKAN_H + +#include "libavutil/vulkan.h" + +#endif /* AVCODEC_VULKAN_H */ diff --git a/libavcodec/vulkan_av1.c b/libavcodec/vulkan_av1.c new file mode 100644 index 00000000000..e607c1fc5f3 --- /dev/null +++ b/libavcodec/vulkan_av1.c @@ -0,0 +1,649 @@ +/* + * 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 "av1dec.h" + +#include "vulkan_decode.h" + +/* Maximum number of tiles specified by any defined level */ +#define MAX_TILES 256 + +const FFVulkanDecodeDescriptor ff_vk_dec_av1_desc = { + .codec_id = AV_CODEC_ID_AV1, + .decode_extension = FF_VK_EXT_VIDEO_DECODE_AV1, + .decode_op = VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR, + .ext_props = { + .extensionName = VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_EXTENSION_NAME, + .specVersion = VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_SPEC_VERSION, + }, +}; + +typedef struct AV1VulkanDecodePicture { + FFVulkanDecodePicture vp; + + /* TODO: investigate if this can be removed to make decoding completely + * independent. */ + FFVulkanDecodeContext *dec; + + uint32_t tile_sizes[MAX_TILES]; + + /* Current picture */ + StdVideoDecodeAV1ReferenceInfo std_ref; + VkVideoDecodeAV1DpbSlotInfoKHR vkav1_ref; + uint16_t width_in_sbs_minus1[64]; + uint16_t height_in_sbs_minus1[64]; + uint16_t mi_col_starts[64]; + uint16_t mi_row_starts[64]; + StdVideoAV1TileInfo tile_info; + StdVideoAV1Quantization quantization; + StdVideoAV1Segmentation segmentation; + StdVideoAV1LoopFilter loop_filter; + StdVideoAV1CDEF cdef; + StdVideoAV1LoopRestoration loop_restoration; + StdVideoAV1GlobalMotion global_motion; + StdVideoAV1FilmGrain film_grain; + StdVideoDecodeAV1PictureInfo std_pic_info; + VkVideoDecodeAV1PictureInfoKHR av1_pic_info; + + /* Picture refs */ + const AV1Frame *ref_src [AV1_NUM_REF_FRAMES]; + StdVideoDecodeAV1ReferenceInfo std_refs [AV1_NUM_REF_FRAMES]; + VkVideoDecodeAV1DpbSlotInfoKHR vkav1_refs[AV1_NUM_REF_FRAMES]; + + uint8_t frame_id_set; + uint8_t frame_id; + uint8_t ref_frame_sign_bias_mask; +} AV1VulkanDecodePicture; + +static int vk_av1_fill_pict(AVCodecContext *avctx, const AV1Frame **ref_src, + VkVideoReferenceSlotInfoKHR *ref_slot, /* Main structure */ + VkVideoPictureResourceInfoKHR *ref, /* Goes in ^ */ + StdVideoDecodeAV1ReferenceInfo *vkav1_std_ref, + VkVideoDecodeAV1DpbSlotInfoKHR *vkav1_ref, /* Goes in ^ */ + const AV1Frame *pic, int is_current, int has_grain, + const uint8_t *saved_order_hints) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + AV1VulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vkpic = &hp->vp; + + int err = ff_vk_decode_prepare_frame(dec, pic->f, vkpic, is_current, + has_grain || dec->dedicated_dpb); + if (err < 0) + return err; + + *vkav1_std_ref = (StdVideoDecodeAV1ReferenceInfo) { + .flags = (StdVideoDecodeAV1ReferenceInfoFlags) { + .disable_frame_end_update_cdf = pic->raw_frame_header->disable_frame_end_update_cdf, + .segmentation_enabled = pic->raw_frame_header->segmentation_enabled, + }, + .frame_type = pic->raw_frame_header->frame_type, + .OrderHint = pic->raw_frame_header->order_hint, + .RefFrameSignBias = hp->ref_frame_sign_bias_mask, + }; + + if (saved_order_hints) { + if (dec->quirk_av1_offset) + for (int i = 1; i < STD_VIDEO_AV1_TOTAL_REFS_PER_FRAME; i++) + vkav1_std_ref->SavedOrderHints[i - 1] = saved_order_hints[i]; + else + for (int i = 0; i < STD_VIDEO_AV1_TOTAL_REFS_PER_FRAME; i++) + vkav1_std_ref->SavedOrderHints[i] = saved_order_hints[i]; + } + + *vkav1_ref = (VkVideoDecodeAV1DpbSlotInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_DPB_SLOT_INFO_KHR, + .pStdReferenceInfo = vkav1_std_ref, + }; + + vkav1_std_ref->flags.disable_frame_end_update_cdf = pic->raw_frame_header->disable_frame_end_update_cdf; + vkav1_std_ref->flags.segmentation_enabled = pic->raw_frame_header->segmentation_enabled; + vkav1_std_ref->frame_type = pic->raw_frame_header->frame_type; + + *ref = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->f->width, pic->f->height }, + .baseArrayLayer = ((has_grain || dec->dedicated_dpb) && dec->layered_dpb) ? + hp->frame_id : 0, + .imageViewBinding = vkpic->img_view_ref, + }; + + *ref_slot = (VkVideoReferenceSlotInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR, + .pNext = vkav1_ref, + .slotIndex = hp->frame_id, + .pPictureResource = ref, + }; + + if (ref_src) + *ref_src = pic; + + return 0; +} + +static int vk_av1_create_params(AVCodecContext *avctx, AVBufferRef **buf) +{ + const AV1DecContext *s = avctx->priv_data; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = dec->shared_ctx; + + const AV1RawSequenceHeader *seq = s->raw_seq; + + StdVideoAV1SequenceHeader av1_sequence_header; + StdVideoAV1TimingInfo av1_timing_info; + StdVideoAV1ColorConfig av1_color_config; + VkVideoDecodeAV1SessionParametersCreateInfoKHR av1_params; + VkVideoSessionParametersCreateInfoKHR session_params_create; + + int err; + + av1_timing_info = (StdVideoAV1TimingInfo) { + .flags = (StdVideoAV1TimingInfoFlags) { + .equal_picture_interval = seq->timing_info.equal_picture_interval, + }, + .num_units_in_display_tick = seq->timing_info.num_units_in_display_tick, + .time_scale = seq->timing_info.time_scale, + .num_ticks_per_picture_minus_1 = seq->timing_info.num_ticks_per_picture_minus_1, + }; + + av1_color_config = (StdVideoAV1ColorConfig) { + .flags = (StdVideoAV1ColorConfigFlags) { + .mono_chrome = seq->color_config.mono_chrome, + .color_range = seq->color_config.color_range, + .separate_uv_delta_q = seq->color_config.separate_uv_delta_q, + }, + .BitDepth = seq->color_config.twelve_bit ? 12 : + seq->color_config.high_bitdepth ? 10 : 8, + .subsampling_x = seq->color_config.subsampling_x, + .subsampling_y = seq->color_config.subsampling_y, + .color_primaries = seq->color_config.color_primaries, + .transfer_characteristics = seq->color_config.transfer_characteristics, + .matrix_coefficients = seq->color_config.matrix_coefficients, + }; + + av1_sequence_header = (StdVideoAV1SequenceHeader) { + .flags = (StdVideoAV1SequenceHeaderFlags) { + .still_picture = seq->still_picture, + .reduced_still_picture_header = seq->reduced_still_picture_header, + .use_128x128_superblock = seq->use_128x128_superblock, + .enable_filter_intra = seq->enable_filter_intra, + .enable_intra_edge_filter = seq->enable_intra_edge_filter, + .enable_interintra_compound = seq->enable_interintra_compound, + .enable_masked_compound = seq->enable_masked_compound, + .enable_warped_motion = seq->enable_warped_motion, + .enable_dual_filter = seq->enable_dual_filter, + .enable_order_hint = seq->enable_order_hint, + .enable_jnt_comp = seq->enable_jnt_comp, + .enable_ref_frame_mvs = seq->enable_ref_frame_mvs, + .frame_id_numbers_present_flag = seq->frame_id_numbers_present_flag, + .enable_superres = seq->enable_superres, + .enable_cdef = seq->enable_cdef, + .enable_restoration = seq->enable_restoration, + .film_grain_params_present = seq->film_grain_params_present, + .timing_info_present_flag = seq->timing_info_present_flag, + .initial_display_delay_present_flag = seq->initial_display_delay_present_flag, + }, + .seq_profile = seq->seq_profile, + .frame_width_bits_minus_1 = seq->frame_width_bits_minus_1, + .frame_height_bits_minus_1 = seq->frame_height_bits_minus_1, + .max_frame_width_minus_1 = seq->max_frame_width_minus_1, + .max_frame_height_minus_1 = seq->max_frame_height_minus_1, + .delta_frame_id_length_minus_2 = seq->delta_frame_id_length_minus_2, + .additional_frame_id_length_minus_1 = seq->additional_frame_id_length_minus_1, + .order_hint_bits_minus_1 = seq->order_hint_bits_minus_1, + .seq_force_integer_mv = seq->seq_force_integer_mv, + .seq_force_screen_content_tools = seq->seq_force_screen_content_tools, + .pTimingInfo = &av1_timing_info, + .pColorConfig = &av1_color_config, + }; + + av1_params = (VkVideoDecodeAV1SessionParametersCreateInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pStdSequenceHeader = &av1_sequence_header, + }; + session_params_create = (VkVideoSessionParametersCreateInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pNext = &av1_params, + .videoSession = ctx->common.session, + .videoSessionParametersTemplate = VK_NULL_HANDLE, + }; + + err = ff_vk_decode_create_params(buf, avctx, ctx, &session_params_create); + if (err < 0) + return err; + + av_log(avctx, AV_LOG_DEBUG, "Created frame parameters\n"); + + return 0; +} + +static int vk_av1_start_frame(AVCodecContext *avctx, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + int err; + int ref_count = 0; + AV1DecContext *s = avctx->priv_data; + const AV1Frame *pic = &s->cur_frame; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + AV1VulkanDecodePicture *ap = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &ap->vp; + + const AV1RawFrameHeader *frame_header = s->raw_frame_header; + const AV1RawFilmGrainParams *film_grain = &s->cur_frame.film_grain; + + const int apply_grain = !(avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN) && + film_grain->apply_grain; + StdVideoAV1FrameRestorationType remap_lr_type[4] = { STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_NONE, + STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_SWITCHABLE, + STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_WIENER, + STD_VIDEO_AV1_FRAME_RESTORATION_TYPE_SGRPROJ }; + + if (!dec->session_params) { + err = vk_av1_create_params(avctx, &dec->session_params); + if (err < 0) + return err; + } + + if (!ap->frame_id_set) { + unsigned slot_idx = 0; + for (unsigned i = 0; i < 32; i++) { + if (!(dec->frame_id_alloc_mask & (1 << i))) { + slot_idx = i; + break; + } + } + ap->frame_id = slot_idx; + ap->frame_id_set = 1; + dec->frame_id_alloc_mask |= (1 << slot_idx); + } + + ap->ref_frame_sign_bias_mask = 0x0; + for (int i = 0; i < STD_VIDEO_AV1_TOTAL_REFS_PER_FRAME; i++) + ap->ref_frame_sign_bias_mask |= pic->ref_frame_sign_bias[i] << i; + + for (int i = 0; i < STD_VIDEO_AV1_REFS_PER_FRAME; i++) { + const int idx = pic->raw_frame_header->ref_frame_idx[i]; + const AV1Frame *ref_frame = &s->ref[idx]; + AV1VulkanDecodePicture *hp = ref_frame->hwaccel_picture_private; + int found = 0; + + if (ref_frame->f->pict_type == AV_PICTURE_TYPE_NONE) + continue; + + for (int j = 0; j < ref_count; j++) { + if (vp->ref_slots[j].slotIndex == hp->frame_id) { + found = 1; + break; + } + } + if (found) + continue; + + err = vk_av1_fill_pict(avctx, &ap->ref_src[ref_count], &vp->ref_slots[ref_count], + &vp->refs[ref_count], &ap->std_refs[ref_count], &ap->vkav1_refs[ref_count], + ref_frame, 0, 0, ref_frame->order_hints); + if (err < 0) + return err; + + ref_count++; + } + + err = vk_av1_fill_pict(avctx, NULL, &vp->ref_slot, &vp->ref, + &ap->std_ref, + &ap->vkav1_ref, + pic, 1, apply_grain, NULL); + if (err < 0) + return err; + + ap->av1_pic_info = (VkVideoDecodeAV1PictureInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_PICTURE_INFO_KHR, + .pStdPictureInfo = &ap->std_pic_info, + .frameHeaderOffset = 0, + .tileCount = 0, + .pTileOffsets = NULL, + .pTileSizes = ap->tile_sizes, + }; + + for (int i = 0; i < STD_VIDEO_AV1_REFS_PER_FRAME; i++) { + const int idx = pic->raw_frame_header->ref_frame_idx[i]; + const AV1Frame *ref_frame = &s->ref[idx]; + AV1VulkanDecodePicture *hp = ref_frame->hwaccel_picture_private; + + if (ref_frame->f->pict_type == AV_PICTURE_TYPE_NONE) + ap->av1_pic_info.referenceNameSlotIndices[i] = AV1_REF_FRAME_NONE; + else + ap->av1_pic_info.referenceNameSlotIndices[i] = hp->frame_id; + } + + vp->decode_info = (VkVideoDecodeInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_INFO_KHR, + .pNext = &ap->av1_pic_info, + .flags = 0x0, + .pSetupReferenceSlot = &vp->ref_slot, + .referenceSlotCount = ref_count, + .pReferenceSlots = vp->ref_slots, + .dstPictureResource = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->f->width, pic->f->height }, + .baseArrayLayer = 0, + .imageViewBinding = vp->img_view_out, + }, + }; + + ap->tile_info = (StdVideoAV1TileInfo) { + .flags = (StdVideoAV1TileInfoFlags) { + .uniform_tile_spacing_flag = frame_header->uniform_tile_spacing_flag, + }, + .TileCols = frame_header->tile_cols, + .TileRows = frame_header->tile_rows, + .context_update_tile_id = frame_header->context_update_tile_id, + .tile_size_bytes_minus_1 = frame_header->tile_size_bytes_minus1, + .pWidthInSbsMinus1 = ap->width_in_sbs_minus1, + .pHeightInSbsMinus1 = ap->height_in_sbs_minus1, + .pMiColStarts = ap->mi_col_starts, + .pMiRowStarts = ap->mi_row_starts, + }; + + ap->quantization = (StdVideoAV1Quantization) { + .flags.using_qmatrix = frame_header->using_qmatrix, + .flags.diff_uv_delta = frame_header->diff_uv_delta, + .base_q_idx = frame_header->base_q_idx, + .DeltaQYDc = frame_header->delta_q_y_dc, + .DeltaQUDc = frame_header->delta_q_u_dc, + .DeltaQUAc = frame_header->delta_q_u_ac, + .DeltaQVDc = frame_header->delta_q_v_dc, + .DeltaQVAc = frame_header->delta_q_v_ac, + .qm_y = frame_header->qm_y, + .qm_u = frame_header->qm_u, + .qm_v = frame_header->qm_v, + }; + + ap->loop_filter = (StdVideoAV1LoopFilter) { + .flags = (StdVideoAV1LoopFilterFlags) { + .loop_filter_delta_enabled = frame_header->loop_filter_delta_enabled, + .loop_filter_delta_update = frame_header->loop_filter_delta_update, + }, + .loop_filter_sharpness = frame_header->loop_filter_sharpness, + }; + + for (int i = 0; i < STD_VIDEO_AV1_MAX_LOOP_FILTER_STRENGTHS; i++) + ap->loop_filter.loop_filter_level[i] = frame_header->loop_filter_level[i]; + for (int i = 0; i < STD_VIDEO_AV1_LOOP_FILTER_ADJUSTMENTS; i++) + ap->loop_filter.loop_filter_mode_deltas[i] = frame_header->loop_filter_mode_deltas[i]; + + ap->cdef = (StdVideoAV1CDEF) { + .cdef_damping_minus_3 = frame_header->cdef_damping_minus_3, + .cdef_bits = frame_header->cdef_bits, + }; + + ap->loop_restoration = (StdVideoAV1LoopRestoration) { + .FrameRestorationType[0] = remap_lr_type[frame_header->lr_type[0]], + .FrameRestorationType[1] = remap_lr_type[frame_header->lr_type[1]], + .FrameRestorationType[2] = remap_lr_type[frame_header->lr_type[2]], + .LoopRestorationSize[0] = 1 + frame_header->lr_unit_shift, + .LoopRestorationSize[1] = 1 + frame_header->lr_unit_shift - frame_header->lr_uv_shift, + .LoopRestorationSize[2] = 1 + frame_header->lr_unit_shift - frame_header->lr_uv_shift, + }; + + ap->film_grain = (StdVideoAV1FilmGrain) { + .flags = (StdVideoAV1FilmGrainFlags) { + .chroma_scaling_from_luma = film_grain->chroma_scaling_from_luma, + .overlap_flag = film_grain->overlap_flag, + .clip_to_restricted_range = film_grain->clip_to_restricted_range, + }, + .grain_scaling_minus_8 = film_grain->grain_scaling_minus_8, + .ar_coeff_lag = film_grain->ar_coeff_lag, + .ar_coeff_shift_minus_6 = film_grain->ar_coeff_shift_minus_6, + .grain_scale_shift = film_grain->grain_scale_shift, + .grain_seed = film_grain->grain_seed, + .film_grain_params_ref_idx = film_grain->film_grain_params_ref_idx, + .num_y_points = film_grain->num_y_points, + .num_cb_points = film_grain->num_cb_points, + .num_cr_points = film_grain->num_cr_points, + .cb_mult = film_grain->cb_mult, + .cb_luma_mult = film_grain->cb_luma_mult, + .cb_offset = film_grain->cb_offset, + .cr_mult = film_grain->cr_mult, + .cr_luma_mult = film_grain->cr_luma_mult, + .cr_offset = film_grain->cr_offset, + }; + + /* Setup frame header */ + ap->std_pic_info = (StdVideoDecodeAV1PictureInfo) { + .flags = (StdVideoDecodeAV1PictureInfoFlags) { + .error_resilient_mode = frame_header->error_resilient_mode, + .disable_cdf_update = frame_header->disable_cdf_update, + .use_superres = frame_header->use_superres, + .render_and_frame_size_different = frame_header->render_and_frame_size_different, + .allow_screen_content_tools = frame_header->allow_screen_content_tools, + .is_filter_switchable = frame_header->is_filter_switchable, + .force_integer_mv = pic->force_integer_mv, + .frame_size_override_flag = frame_header->frame_size_override_flag, + .buffer_removal_time_present_flag = frame_header->buffer_removal_time_present_flag, + .allow_intrabc = frame_header->allow_intrabc, + .frame_refs_short_signaling = frame_header->frame_refs_short_signaling, + .allow_high_precision_mv = frame_header->allow_high_precision_mv, + .is_motion_mode_switchable = frame_header->is_motion_mode_switchable, + .use_ref_frame_mvs = frame_header->use_ref_frame_mvs, + .disable_frame_end_update_cdf = frame_header->disable_frame_end_update_cdf, + .allow_warped_motion = frame_header->allow_warped_motion, + .reduced_tx_set = frame_header->reduced_tx_set, + .reference_select = frame_header->reference_select, + .skip_mode_present = frame_header->skip_mode_present, + .delta_q_present = frame_header->delta_q_present, + .delta_lf_present = frame_header->delta_lf_present, + .delta_lf_multi = frame_header->delta_lf_multi, + .segmentation_enabled = frame_header->segmentation_enabled, + .segmentation_update_map = frame_header->segmentation_update_map, + .segmentation_temporal_update = frame_header->segmentation_temporal_update, + .segmentation_update_data = frame_header->segmentation_update_data, + .UsesLr = frame_header->lr_type[0] || frame_header->lr_type[1] || frame_header->lr_type[2], + .apply_grain = apply_grain, + }, + .frame_type = frame_header->frame_type, + .current_frame_id = frame_header->current_frame_id, + .OrderHint = frame_header->order_hint, + .primary_ref_frame = frame_header->primary_ref_frame, + .refresh_frame_flags = frame_header->refresh_frame_flags, + .interpolation_filter = frame_header->interpolation_filter, + .TxMode = frame_header->tx_mode, + .delta_q_res = frame_header->delta_q_res, + .delta_lf_res = frame_header->delta_lf_res, + .SkipModeFrame[0] = s->cur_frame.skip_mode_frame_idx[0], + .SkipModeFrame[1] = s->cur_frame.skip_mode_frame_idx[1], + .coded_denom = frame_header->coded_denom, + .pTileInfo = &ap->tile_info, + .pQuantization = &ap->quantization, + .pSegmentation = &ap->segmentation, + .pLoopFilter = &ap->loop_filter, + .pCDEF = &ap->cdef, + .pLoopRestoration = &ap->loop_restoration, + .pGlobalMotion = &ap->global_motion, + .pFilmGrain = apply_grain ? &ap->film_grain : NULL, + }; + + for (int i = 0; i < 64; i++) { + ap->width_in_sbs_minus1[i] = frame_header->width_in_sbs_minus_1[i]; + ap->height_in_sbs_minus1[i] = frame_header->height_in_sbs_minus_1[i]; + ap->mi_col_starts[i] = frame_header->tile_start_col_sb[i]; + ap->mi_row_starts[i] = frame_header->tile_start_row_sb[i]; + } + + for (int i = 0; i < STD_VIDEO_AV1_MAX_SEGMENTS; i++) { + ap->segmentation.FeatureEnabled[i] = 0x0; + for (int j = 0; j < STD_VIDEO_AV1_SEG_LVL_MAX; j++) { + ap->segmentation.FeatureEnabled[i] |= (frame_header->feature_enabled[i][j] << j); + ap->segmentation.FeatureData[i][j] = frame_header->feature_value[i][j]; + } + } + + if (dec->quirk_av1_offset) + for (int i = 1; i < STD_VIDEO_AV1_TOTAL_REFS_PER_FRAME; i++) + ap->std_pic_info.OrderHints[i - 1] = pic->order_hints[i]; + else + for (int i = 0; i < STD_VIDEO_AV1_TOTAL_REFS_PER_FRAME; i++) + ap->std_pic_info.OrderHints[i] = pic->order_hints[i]; + + for (int i = 0; i < STD_VIDEO_AV1_TOTAL_REFS_PER_FRAME; i++) { + ap->loop_filter.loop_filter_ref_deltas[i] = frame_header->loop_filter_ref_deltas[i]; + ap->global_motion.GmType[i] = s->cur_frame.gm_type[i]; + for (int j = 0; j < STD_VIDEO_AV1_GLOBAL_MOTION_PARAMS; j++) { + ap->global_motion.gm_params[i][j] = s->cur_frame.gm_params[i][j]; + } + } + + for (int i = 0; i < STD_VIDEO_AV1_MAX_CDEF_FILTER_STRENGTHS; i++) { + ap->cdef.cdef_y_pri_strength[i] = frame_header->cdef_y_pri_strength[i]; + ap->cdef.cdef_y_sec_strength[i] = frame_header->cdef_y_sec_strength[i]; + ap->cdef.cdef_uv_pri_strength[i] = frame_header->cdef_uv_pri_strength[i]; + ap->cdef.cdef_uv_sec_strength[i] = frame_header->cdef_uv_sec_strength[i]; + } + + if (apply_grain) { + for (int i = 0; i < STD_VIDEO_AV1_MAX_NUM_Y_POINTS; i++) { + ap->film_grain.point_y_value[i] = film_grain->point_y_value[i]; + ap->film_grain.point_y_scaling[i] = film_grain->point_y_scaling[i]; + } + + for (int i = 0; i < STD_VIDEO_AV1_MAX_NUM_CB_POINTS; i++) { + ap->film_grain.point_cb_value[i] = film_grain->point_cb_value[i]; + ap->film_grain.point_cb_scaling[i] = film_grain->point_cb_scaling[i]; + ap->film_grain.point_cr_value[i] = film_grain->point_cr_value[i]; + ap->film_grain.point_cr_scaling[i] = film_grain->point_cr_scaling[i]; + } + + for (int i = 0; i < STD_VIDEO_AV1_MAX_NUM_POS_LUMA; i++) + ap->film_grain.ar_coeffs_y_plus_128[i] = film_grain->ar_coeffs_y_plus_128[i]; + + for (int i = 0; i < STD_VIDEO_AV1_MAX_NUM_POS_CHROMA; i++) { + ap->film_grain.ar_coeffs_cb_plus_128[i] = film_grain->ar_coeffs_cb_plus_128[i]; + ap->film_grain.ar_coeffs_cr_plus_128[i] = film_grain->ar_coeffs_cr_plus_128[i]; + } + } + + ap->dec = dec; + + return 0; +} + +static int vk_av1_decode_slice(AVCodecContext *avctx, + const uint8_t *data, + uint32_t size) +{ + int err; + const AV1DecContext *s = avctx->priv_data; + AV1VulkanDecodePicture *ap = s->cur_frame.hwaccel_picture_private; + FFVulkanDecodePicture *vp = &ap->vp; + + /* Too many tiles, exceeding all defined levels in the AV1 spec */ + if (ap->av1_pic_info.tileCount > MAX_TILES) + return AVERROR(ENOSYS); + + for (int i = s->tg_start; i <= s->tg_end; i++) { + ap->tile_sizes[ap->av1_pic_info.tileCount] = s->tile_group_info[i].tile_size; + + err = ff_vk_decode_add_slice(avctx, vp, + data + s->tile_group_info[i].tile_offset, + s->tile_group_info[i].tile_size, 0, + &ap->av1_pic_info.tileCount, + &ap->av1_pic_info.pTileOffsets); + if (err < 0) + return err; + } + + return 0; +} + +static int vk_av1_end_frame(AVCodecContext *avctx) +{ + const AV1DecContext *s = avctx->priv_data; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + const AV1Frame *pic = &s->cur_frame; + AV1VulkanDecodePicture *ap = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &ap->vp; + FFVulkanDecodePicture *rvp[AV1_NUM_REF_FRAMES] = { 0 }; + AVFrame *rav[AV1_NUM_REF_FRAMES] = { 0 }; + + if (!ap->av1_pic_info.tileCount) + return 0; + + if (!dec->session_params) { + int err = vk_av1_create_params(avctx, &dec->session_params); + if (err < 0) + return err; + } + + for (int i = 0; i < vp->decode_info.referenceSlotCount; i++) { + const AV1Frame *rp = ap->ref_src[i]; + AV1VulkanDecodePicture *rhp = rp->hwaccel_picture_private; + + rvp[i] = &rhp->vp; + rav[i] = ap->ref_src[i]->f; + } + + av_log(avctx, AV_LOG_VERBOSE, "Decoding frame, %"SIZE_SPECIFIER" bytes, %i tiles\n", + vp->slices_size, ap->av1_pic_info.tileCount); + + return ff_vk_decode_frame(avctx, pic->f, vp, rav, rvp); +} + +static void vk_av1_free_frame_priv(FFRefStructOpaque _hwctx, void *data) +{ + AVHWDeviceContext *hwctx = _hwctx.nc; + AV1VulkanDecodePicture *ap = data; + + /* Workaround for a spec issue. */ + if (ap->frame_id_set) + ap->dec->frame_id_alloc_mask &= ~(1 << ap->frame_id); + + /* Free frame resources, this also destroys the session parameters. */ + ff_vk_decode_free_frame(hwctx, &ap->vp); +} + +const FFHWAccel ff_av1_vulkan_hwaccel = { + .p.name = "av1_vulkan", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_VULKAN, + .start_frame = &vk_av1_start_frame, + .decode_slice = &vk_av1_decode_slice, + .end_frame = &vk_av1_end_frame, + .free_frame_priv = &vk_av1_free_frame_priv, + .frame_priv_data_size = sizeof(AV1VulkanDecodePicture), + .init = &ff_vk_decode_init, + .update_thread_context = &ff_vk_update_thread_context, + .decode_params = &ff_vk_params_invalidate, + .flush = &ff_vk_decode_flush, + .uninit = &ff_vk_decode_uninit, + .frame_params = &ff_vk_frame_params, + .priv_data_size = sizeof(FFVulkanDecodeContext), + + /* NOTE: Threading is intentionally disabled here. Due to the design of Vulkan, + * where frames are opaque to users, and mostly opaque for driver developers, + * there's an issue with current hardware accelerator implementations of AV1, + * where they require an internal index. With regular hwaccel APIs, this index + * is given to users as an opaque handle directly. With Vulkan, due to increased + * flexibility, this index cannot be present anywhere. + * The current implementation tracks the index for the driver and submits it + * as necessary information. Due to needing to modify the decoding context, + * which is not thread-safe, on frame free, threading is disabled. */ + .caps_internal = HWACCEL_CAP_ASYNC_SAFE, +}; diff --git a/libavcodec/vulkan_decode.c b/libavcodec/vulkan_decode.c new file mode 100644 index 00000000000..5f6523920dd --- /dev/null +++ b/libavcodec/vulkan_decode.c @@ -0,0 +1,1297 @@ +/* + * 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 "refstruct.h" +#include "vulkan_video.h" +#include "vulkan_decode.h" +#include "config_components.h" +#include "libavutil/avassert.h" +#include "libavutil/vulkan_loader.h" + +#if CONFIG_H264_VULKAN_HWACCEL +extern const FFVulkanDecodeDescriptor ff_vk_dec_h264_desc; +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL +extern const FFVulkanDecodeDescriptor ff_vk_dec_hevc_desc; +#endif +#if CONFIG_AV1_VULKAN_HWACCEL +extern const FFVulkanDecodeDescriptor ff_vk_dec_av1_desc; +#endif + +static const FFVulkanDecodeDescriptor *dec_descs[] = { +#if CONFIG_H264_VULKAN_HWACCEL + &ff_vk_dec_h264_desc, +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + &ff_vk_dec_hevc_desc, +#endif +#if CONFIG_AV1_VULKAN_HWACCEL + &ff_vk_dec_av1_desc, +#endif +}; + +static const FFVulkanDecodeDescriptor *get_codecdesc(enum AVCodecID codec_id) +{ + for (size_t i = 0; i < FF_ARRAY_ELEMS(dec_descs); i++) + if (dec_descs[i]->codec_id == codec_id) + return dec_descs[i]; + av_assert1(!"no codec descriptor"); + return NULL; +} + +static const VkVideoProfileInfoKHR *get_video_profile(FFVulkanDecodeShared *ctx, enum AVCodecID codec_id) +{ + const VkVideoProfileListInfoKHR *profile_list; + + VkStructureType profile_struct_type = + codec_id == AV_CODEC_ID_H264 ? VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PROFILE_INFO_KHR : + codec_id == AV_CODEC_ID_HEVC ? VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_INFO_KHR : + codec_id == AV_CODEC_ID_AV1 ? VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_PROFILE_INFO_KHR : + 0; + + profile_list = ff_vk_find_struct(ctx->s.hwfc->create_pnext, + VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR); + if (!profile_list) + return NULL; + + for (int i = 0; i < profile_list->profileCount; i++) + if (ff_vk_find_struct(profile_list->pProfiles[i].pNext, profile_struct_type)) + return &profile_list->pProfiles[i]; + + return NULL; +} + +int ff_vk_update_thread_context(AVCodecContext *dst, const AVCodecContext *src) +{ + int err; + FFVulkanDecodeContext *src_ctx = src->internal->hwaccel_priv_data; + FFVulkanDecodeContext *dst_ctx = dst->internal->hwaccel_priv_data; + + if (!dst_ctx->exec_pool.cmd_bufs) { + FFVulkanDecodeShared *ctx = src_ctx->shared_ctx; + + const VkVideoProfileInfoKHR *profile = get_video_profile(ctx, dst->codec_id); + if (!profile) { + av_log(dst, AV_LOG_ERROR, "Video profile missing from frames context!"); + return AVERROR(EINVAL); + } + + err = ff_vk_exec_pool_init(&ctx->s, &ctx->qf, + &dst_ctx->exec_pool, + src_ctx->exec_pool.pool_size, + src_ctx->exec_pool.nb_queries, + VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR, 0, + profile); + if (err < 0) + return err; + } + + ff_refstruct_replace(&dst_ctx->shared_ctx, src_ctx->shared_ctx); + + if (src_ctx->session_params) { + err = av_buffer_replace(&dst_ctx->session_params, src_ctx->session_params); + if (err < 0) + return err; + } + + dst_ctx->dedicated_dpb = src_ctx->dedicated_dpb; + dst_ctx->layered_dpb = src_ctx->layered_dpb; + dst_ctx->external_fg = src_ctx->external_fg; + dst_ctx->frame_id_alloc_mask = src_ctx->frame_id_alloc_mask; + + return 0; +} + +int ff_vk_params_invalidate(AVCodecContext *avctx, int t, const uint8_t *b, uint32_t s) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + av_buffer_unref(&dec->session_params); + return 0; +} + +static int vk_decode_create_view(FFVulkanDecodeContext *dec, VkImageView *dst_view, + VkImageAspectFlags *aspect, AVVkFrame *src, + VkFormat vkf, int is_current) +{ + VkResult ret; + FFVulkanDecodeShared *ctx = dec->shared_ctx; + FFVulkanFunctions *vk = &ctx->s.vkfn; + VkImageAspectFlags aspect_mask = ff_vk_aspect_bits_from_vkfmt(vkf); + + VkSamplerYcbcrConversionInfo yuv_sampler_info = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO, + .conversion = ctx->yuv_sampler, + }; + VkImageViewCreateInfo img_view_create_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = &yuv_sampler_info, + .viewType = dec->layered_dpb && !is_current ? + VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D, + .format = vkf, + .image = src->img[0], + .components = (VkComponentMapping) { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseArrayLayer = 0, + .layerCount = dec->layered_dpb && !is_current ? + VK_REMAINING_ARRAY_LAYERS : 1, + .levelCount = 1, + }, + }; + + ret = vk->CreateImageView(ctx->s.hwctx->act_dev, &img_view_create_info, + ctx->s.hwctx->alloc, dst_view); + if (ret != VK_SUCCESS) + return AVERROR_EXTERNAL; + + *aspect = aspect_mask; + + return 0; +} + +static AVFrame *vk_get_dpb_pool(FFVulkanDecodeShared *ctx) +{ + int err; + AVFrame *avf = av_frame_alloc(); + if (!avf) + return NULL; + + err = av_hwframe_get_buffer(ctx->dpb_hwfc_ref, avf, 0x0); + if (err < 0) + av_frame_free(&avf); + + return avf; +} + +int ff_vk_decode_prepare_frame(FFVulkanDecodeContext *dec, AVFrame *pic, + FFVulkanDecodePicture *vkpic, int is_current, + int alloc_dpb) +{ + int err; + FFVulkanDecodeShared *ctx = dec->shared_ctx; + FFVulkanFunctions *vk = &ctx->s.vkfn; + + vkpic->slices_size = 0; + + /* If the decoder made a blank frame to make up for a missing ref, or the + * frame is the current frame so it's missing one, create a re-representation */ + if (vkpic->img_view_ref) + return 0; + + vkpic->dpb_frame = NULL; + vkpic->img_view_ref = VK_NULL_HANDLE; + vkpic->img_view_out = VK_NULL_HANDLE; + vkpic->img_view_dest = VK_NULL_HANDLE; + + vkpic->destroy_image_view = vk->DestroyImageView; + vkpic->wait_semaphores = vk->WaitSemaphores; + + if (dec->layered_dpb && alloc_dpb) { + vkpic->img_view_ref = ctx->layered_view; + vkpic->img_aspect_ref = ctx->layered_aspect; + } else if (alloc_dpb) { + AVHWFramesContext *dpb_frames = (AVHWFramesContext *)ctx->dpb_hwfc_ref->data; + AVVulkanFramesContext *dpb_hwfc = dpb_frames->hwctx; + + vkpic->dpb_frame = vk_get_dpb_pool(ctx); + if (!vkpic->dpb_frame) + return AVERROR(ENOMEM); + + err = vk_decode_create_view(dec, &vkpic->img_view_ref, + &vkpic->img_aspect_ref, + (AVVkFrame *)vkpic->dpb_frame->data[0], + dpb_hwfc->format[0], is_current); + if (err < 0) + return err; + + vkpic->img_view_dest = vkpic->img_view_ref; + } + + if (!alloc_dpb || is_current) { + AVHWFramesContext *frames = (AVHWFramesContext *)pic->hw_frames_ctx->data; + AVVulkanFramesContext *hwfc = frames->hwctx; + + err = vk_decode_create_view(dec, &vkpic->img_view_out, + &vkpic->img_aspect, + (AVVkFrame *)pic->data[0], + hwfc->format[0], is_current); + if (err < 0) + return err; + + if (!alloc_dpb) { + vkpic->img_view_ref = vkpic->img_view_out; + vkpic->img_aspect_ref = vkpic->img_aspect; + } + } + + return 0; +} + +int ff_vk_decode_add_slice(AVCodecContext *avctx, FFVulkanDecodePicture *vp, + const uint8_t *data, size_t size, int add_startcode, + uint32_t *nb_slices, const uint32_t **offsets) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = dec->shared_ctx; + + static const uint8_t startcode_prefix[3] = { 0x0, 0x0, 0x1 }; + const size_t startcode_len = add_startcode ? sizeof(startcode_prefix) : 0; + const int nb = *nb_slices; + uint8_t *slices; + uint32_t *slice_off; + FFVkVideoBuffer *vkbuf; + + size_t new_size = vp->slices_size + startcode_len + size + + ctx->caps.minBitstreamBufferSizeAlignment; + new_size = FFALIGN(new_size, ctx->caps.minBitstreamBufferSizeAlignment); + + slice_off = av_fast_realloc(dec->slice_off, &dec->slice_off_max, + (nb + 1)*sizeof(slice_off)); + if (!slice_off) + return AVERROR(ENOMEM); + + *offsets = dec->slice_off = slice_off; + slice_off[nb] = vp->slices_size; + + vkbuf = vp->slices_buf ? (FFVkVideoBuffer *)vp->slices_buf->data : NULL; + if (!vkbuf || vkbuf->buf.size < new_size) { + int err; + AVBufferRef *new_ref; + FFVkVideoBuffer *new_buf; + err = ff_vk_video_get_buffer(&ctx->s, &ctx->common, &new_ref, + VK_BUFFER_USAGE_VIDEO_DECODE_SRC_BIT_KHR, + ctx->s.hwfc->create_pnext, new_size); + if (err < 0) + return err; + + new_buf = (FFVkVideoBuffer *)new_ref->data; + + /* Copy data from the old buffer */ + if (vkbuf) { + memcpy(new_buf->mem, vkbuf->mem, vp->slices_size); + av_buffer_unref(&vp->slices_buf); + } + + vp->slices_buf = new_ref; + vkbuf = new_buf; + } + slices = vkbuf->mem; + + /* Startcode */ + memcpy(slices + vp->slices_size, startcode_prefix, startcode_len); + + /* Slice data */ + memcpy(slices + vp->slices_size + startcode_len, data, size); + + *nb_slices = nb + 1; + vp->slices_size += startcode_len + size; + + return 0; +} + +void ff_vk_decode_flush(AVCodecContext *avctx) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = dec->shared_ctx; + + FFVulkanFunctions *vk = &ctx->s.vkfn; + VkVideoBeginCodingInfoKHR decode_start = { + .sType = VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR, + .videoSession = ctx->common.session, + .videoSessionParameters = ctx->empty_session_params, + }; + VkVideoCodingControlInfoKHR decode_ctrl = { + .sType = VK_STRUCTURE_TYPE_VIDEO_CODING_CONTROL_INFO_KHR, + .flags = VK_VIDEO_CODING_CONTROL_RESET_BIT_KHR, + }; + VkVideoEndCodingInfoKHR decode_end = { + .sType = VK_STRUCTURE_TYPE_VIDEO_END_CODING_INFO_KHR, + }; + + VkCommandBuffer cmd_buf; + FFVkExecContext *exec = ff_vk_exec_get(&dec->exec_pool); + ff_vk_exec_start(&ctx->s, exec); + cmd_buf = exec->buf; + + vk->CmdBeginVideoCodingKHR(cmd_buf, &decode_start); + vk->CmdControlVideoCodingKHR(cmd_buf, &decode_ctrl); + vk->CmdEndVideoCodingKHR(cmd_buf, &decode_end); + ff_vk_exec_submit(&ctx->s, exec); +} + +int ff_vk_decode_frame(AVCodecContext *avctx, + AVFrame *pic, FFVulkanDecodePicture *vp, + AVFrame *rpic[], FFVulkanDecodePicture *rvkp[]) +{ + int err; + VkResult ret; + VkCommandBuffer cmd_buf; + FFVkVideoBuffer *sd_buf; + + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = dec->shared_ctx; + FFVulkanFunctions *vk = &ctx->s.vkfn; + + /* Output */ + AVVkFrame *vkf = (AVVkFrame *)pic->buf[0]->data; + + /* Quirks */ + const int layered_dpb = dec->layered_dpb; + + VkVideoSessionParametersKHR *par = (VkVideoSessionParametersKHR *)dec->session_params->data; + VkVideoBeginCodingInfoKHR decode_start = { + .sType = VK_STRUCTURE_TYPE_VIDEO_BEGIN_CODING_INFO_KHR, + .videoSession = ctx->common.session, + .videoSessionParameters = *par, + .referenceSlotCount = vp->decode_info.referenceSlotCount, + .pReferenceSlots = vp->decode_info.pReferenceSlots, + }; + VkVideoEndCodingInfoKHR decode_end = { + .sType = VK_STRUCTURE_TYPE_VIDEO_END_CODING_INFO_KHR, + }; + + VkImageMemoryBarrier2 img_bar[37]; + int nb_img_bar = 0; + size_t data_size = FFALIGN(vp->slices_size, + ctx->caps.minBitstreamBufferSizeAlignment); + + FFVkExecContext *exec = ff_vk_exec_get(&dec->exec_pool); + + /* The current decoding reference has to be bound as an inactive reference */ + VkVideoReferenceSlotInfoKHR *cur_vk_ref; + cur_vk_ref = (void *)&decode_start.pReferenceSlots[decode_start.referenceSlotCount]; + cur_vk_ref[0] = vp->ref_slot; + cur_vk_ref[0].slotIndex = -1; + decode_start.referenceSlotCount++; + + if (dec->exec_pool.nb_queries) { + int64_t prev_sub_res = 0; + ff_vk_exec_wait(&ctx->s, exec); + ret = ff_vk_exec_get_query(&ctx->s, exec, NULL, &prev_sub_res); + if (ret != VK_NOT_READY && ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to perform query: %s!\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + if (ret == VK_SUCCESS) + av_log(avctx, prev_sub_res < 0 ? AV_LOG_ERROR : AV_LOG_DEBUG, + "Result of previous frame decoding: %"PRId64"\n", prev_sub_res); + } + + sd_buf = (FFVkVideoBuffer *)vp->slices_buf->data; + + /* Flush if needed */ + if (!(sd_buf->buf.flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) { + VkMappedMemoryRange flush_buf = { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = sd_buf->buf.mem, + .offset = 0, + .size = FFALIGN(vp->slices_size, + ctx->s.props.properties.limits.nonCoherentAtomSize), + }; + + ret = vk->FlushMappedMemoryRanges(ctx->s.hwctx->act_dev, 1, &flush_buf); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Failed to flush memory: %s\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + } + + vp->decode_info.srcBuffer = sd_buf->buf.buf; + vp->decode_info.srcBufferOffset = 0; + vp->decode_info.srcBufferRange = data_size; + + /* Start command buffer recording */ + err = ff_vk_exec_start(&ctx->s, exec); + if (err < 0) + return err; + cmd_buf = exec->buf; + + /* Slices */ + err = ff_vk_exec_add_dep_buf(&ctx->s, exec, &vp->slices_buf, 1, 0); + if (err < 0) + return err; + vp->slices_buf = NULL; /* Owned by the exec buffer from now on */ + + /* Parameters */ + err = ff_vk_exec_add_dep_buf(&ctx->s, exec, &dec->session_params, 1, 1); + if (err < 0) + return err; + + err = ff_vk_exec_add_dep_frame(&ctx->s, exec, pic, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR); + if (err < 0) + return err; + + err = ff_vk_exec_mirror_sem_value(&ctx->s, exec, &vp->sem, &vp->sem_value, + pic); + if (err < 0) + return err; + + /* Output image - change layout, as it comes from a pool */ + img_bar[nb_img_bar] = (VkImageMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = NULL, + .srcStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + .dstStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + .srcAccessMask = VK_ACCESS_2_NONE, + .dstAccessMask = VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR, + .oldLayout = vkf->layout[0], + .newLayout = (dec->layered_dpb || vp->dpb_frame) ? + VK_IMAGE_LAYOUT_VIDEO_DECODE_DST_KHR : + VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR, /* Spec, 07252 utter madness */ + .srcQueueFamilyIndex = vkf->queue_family[0], + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = vkf->img[0], + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = vp->img_aspect, + .layerCount = 1, + .levelCount = 1, + }, + }; + ff_vk_exec_update_frame(&ctx->s, exec, pic, + &img_bar[nb_img_bar], &nb_img_bar); + + /* Reference for the current image, if existing and not layered */ + if (vp->dpb_frame) { + err = ff_vk_exec_add_dep_frame(&ctx->s, exec, vp->dpb_frame, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR); + if (err < 0) + return err; + } + + if (!layered_dpb) { + /* All references (apart from the current) for non-layered refs */ + + for (int i = 0; i < vp->decode_info.referenceSlotCount; i++) { + AVFrame *ref_frame = rpic[i]; + FFVulkanDecodePicture *rvp = rvkp[i]; + AVFrame *ref = rvp->dpb_frame ? rvp->dpb_frame : ref_frame; + + err = ff_vk_exec_add_dep_frame(&ctx->s, exec, ref, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR); + if (err < 0) + return err; + + if (err == 0) { + err = ff_vk_exec_mirror_sem_value(&ctx->s, exec, + &rvp->sem, &rvp->sem_value, + ref); + if (err < 0) + return err; + } + + if (!rvp->dpb_frame) { + AVVkFrame *rvkf = (AVVkFrame *)ref->data[0]; + + img_bar[nb_img_bar] = (VkImageMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .pNext = NULL, + .srcStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + .dstStageMask = VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + .srcAccessMask = VK_ACCESS_2_NONE, + .dstAccessMask = VK_ACCESS_2_VIDEO_DECODE_READ_BIT_KHR | + VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR, + .oldLayout = rvkf->layout[0], + .newLayout = VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR, + .srcQueueFamilyIndex = rvkf->queue_family[0], + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = rvkf->img[0], + .subresourceRange = (VkImageSubresourceRange) { + .aspectMask = rvp->img_aspect_ref, + .layerCount = 1, + .levelCount = 1, + }, + }; + ff_vk_exec_update_frame(&ctx->s, exec, ref, + &img_bar[nb_img_bar], &nb_img_bar); + } + } + } else if (vp->decode_info.referenceSlotCount || + vp->img_view_out != vp->img_view_ref) { + /* Single barrier for a single layered ref */ + err = ff_vk_exec_add_dep_frame(&ctx->s, exec, ctx->layered_frame, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR, + VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR); + if (err < 0) + return err; + } + + /* Change image layout */ + vk->CmdPipelineBarrier2(cmd_buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT, + .pImageMemoryBarriers = img_bar, + .imageMemoryBarrierCount = nb_img_bar, + }); + + /* Start, use parameters, decode and end decoding */ + vk->CmdBeginVideoCodingKHR(cmd_buf, &decode_start); + + /* Start status query */ + if (dec->exec_pool.nb_queries) + vk->CmdBeginQuery(cmd_buf, dec->exec_pool.query_pool, exec->query_idx + 0, 0); + + vk->CmdDecodeVideoKHR(cmd_buf, &vp->decode_info); + + /* End status query */ + if (dec->exec_pool.nb_queries) + vk->CmdEndQuery(cmd_buf, dec->exec_pool.query_pool, exec->query_idx + 0); + + vk->CmdEndVideoCodingKHR(cmd_buf, &decode_end); + + /* End recording and submit for execution */ + return ff_vk_exec_submit(&ctx->s, exec); +} + +void ff_vk_decode_free_frame(AVHWDeviceContext *dev_ctx, FFVulkanDecodePicture *vp) +{ + AVVulkanDeviceContext *hwctx = dev_ctx->hwctx; + + VkSemaphoreWaitInfo sem_wait = (VkSemaphoreWaitInfo) { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, + .pSemaphores = &vp->sem, + .pValues = &vp->sem_value, + .semaphoreCount = 1, + }; + + /* We do not have to lock the frame here because we're not interested + * in the actual current semaphore value, but only that it's later than + * the time we submitted the image for decoding. */ + if (vp->sem) + vp->wait_semaphores(hwctx->act_dev, &sem_wait, UINT64_MAX); + + /* Free slices data */ + av_buffer_unref(&vp->slices_buf); + + /* Destroy image view (out) */ + if (vp->img_view_out && vp->img_view_out != vp->img_view_dest) + vp->destroy_image_view(hwctx->act_dev, vp->img_view_out, hwctx->alloc); + + /* Destroy image view (ref, unlayered) */ + if (vp->img_view_dest) + vp->destroy_image_view(hwctx->act_dev, vp->img_view_dest, hwctx->alloc); + + av_frame_free(&vp->dpb_frame); +} + +static void free_common(FFRefStructOpaque unused, void *obj) +{ + FFVulkanDecodeShared *ctx = obj; + FFVulkanContext *s = &ctx->s; + FFVulkanFunctions *vk = &ctx->s.vkfn; + + /* Destroy layered view */ + if (ctx->layered_view) + vk->DestroyImageView(s->hwctx->act_dev, ctx->layered_view, s->hwctx->alloc); + + /* This also frees all references from this pool */ + av_frame_free(&ctx->layered_frame); + av_buffer_unref(&ctx->dpb_hwfc_ref); + + /* Destroy parameters */ + if (ctx->empty_session_params) + vk->DestroyVideoSessionParametersKHR(s->hwctx->act_dev, + ctx->empty_session_params, + s->hwctx->alloc); + + ff_vk_video_common_uninit(s, &ctx->common); + + if (ctx->yuv_sampler) + vk->DestroySamplerYcbcrConversion(s->hwctx->act_dev, ctx->yuv_sampler, + s->hwctx->alloc); + + ff_vk_uninit(s); +} + +static int vulkan_decode_bootstrap(AVCodecContext *avctx, AVBufferRef *frames_ref) +{ + int err; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + AVHWFramesContext *frames = (AVHWFramesContext *)frames_ref->data; + AVHWDeviceContext *device = (AVHWDeviceContext *)frames->device_ref->data; + AVVulkanDeviceContext *hwctx = device->hwctx; + FFVulkanDecodeShared *ctx; + + if (dec->shared_ctx) + return 0; + + dec->shared_ctx = ff_refstruct_alloc_ext(sizeof(*ctx), 0, NULL, + free_common); + if (!dec->shared_ctx) + return AVERROR(ENOMEM); + + ctx = dec->shared_ctx; + + ctx->s.extensions = ff_vk_extensions_to_mask(hwctx->enabled_dev_extensions, + hwctx->nb_enabled_dev_extensions); + + if (!(ctx->s.extensions & FF_VK_EXT_VIDEO_DECODE_QUEUE)) { + av_log(avctx, AV_LOG_ERROR, "Device does not support the %s extension!\n", + VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME); + ff_refstruct_unref(&dec->shared_ctx); + return AVERROR(ENOSYS); + } + + err = ff_vk_load_functions(device, &ctx->s.vkfn, ctx->s.extensions, 1, 1); + if (err < 0) { + ff_refstruct_unref(&dec->shared_ctx); + return err; + } + + return 0; +} + +static VkResult vulkan_setup_profile(AVCodecContext *avctx, + FFVulkanDecodeProfileData *prof, + AVVulkanDeviceContext *hwctx, + FFVulkanFunctions *vk, + const FFVulkanDecodeDescriptor *vk_desc, + VkVideoDecodeH264CapabilitiesKHR *h264_caps, + VkVideoDecodeH265CapabilitiesKHR *h265_caps, + VkVideoDecodeAV1CapabilitiesKHR *av1_caps, + VkVideoCapabilitiesKHR *caps, + VkVideoDecodeCapabilitiesKHR *dec_caps, + int cur_profile) +{ + VkVideoDecodeUsageInfoKHR *usage = &prof->usage; + VkVideoProfileInfoKHR *profile = &prof->profile; + VkVideoProfileListInfoKHR *profile_list = &prof->profile_list; + + VkVideoDecodeH264ProfileInfoKHR *h264_profile = &prof->h264_profile; + VkVideoDecodeH265ProfileInfoKHR *h265_profile = &prof->h265_profile; + VkVideoDecodeAV1ProfileInfoKHR *av1_profile = &prof->av1_profile; + + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(avctx->sw_pix_fmt); + if (!desc) + return AVERROR(EINVAL); + + if (avctx->codec_id == AV_CODEC_ID_H264) { + dec_caps->pNext = h264_caps; + usage->pNext = h264_profile; + h264_profile->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PROFILE_INFO_KHR; + + /* Vulkan transmits all the constrant_set flags, rather than wanting them + * merged in the profile IDC */ + h264_profile->stdProfileIdc = cur_profile & ~(AV_PROFILE_H264_CONSTRAINED | + AV_PROFILE_H264_INTRA); + + h264_profile->pictureLayout = avctx->field_order == AV_FIELD_UNKNOWN || + avctx->field_order == AV_FIELD_PROGRESSIVE ? + VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_PROGRESSIVE_KHR : + VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_INTERLEAVED_LINES_BIT_KHR; + } else if (avctx->codec_id == AV_CODEC_ID_H265) { + dec_caps->pNext = h265_caps; + usage->pNext = h265_profile; + h265_profile->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PROFILE_INFO_KHR; + h265_profile->stdProfileIdc = cur_profile; + } else if (avctx->codec_id == AV_CODEC_ID_AV1) { + dec_caps->pNext = av1_caps; + usage->pNext = av1_profile; + av1_profile->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_PROFILE_INFO_KHR; + av1_profile->stdProfile = cur_profile; + av1_profile->filmGrainSupport = !(avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN); + } + + usage->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_USAGE_INFO_KHR; + usage->videoUsageHints = VK_VIDEO_DECODE_USAGE_DEFAULT_KHR; + + profile->sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_INFO_KHR; + profile->pNext = usage; + profile->videoCodecOperation = vk_desc->decode_op; + profile->chromaSubsampling = ff_vk_subsampling_from_av_desc(desc); + profile->lumaBitDepth = ff_vk_depth_from_av_depth(desc->comp[0].depth); + profile->chromaBitDepth = profile->lumaBitDepth; + + profile_list->sType = VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR; + profile_list->profileCount = 1; + profile_list->pProfiles = profile; + + /* Get the capabilities of the decoder for the given profile */ + caps->sType = VK_STRUCTURE_TYPE_VIDEO_CAPABILITIES_KHR; + caps->pNext = dec_caps; + dec_caps->sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_CAPABILITIES_KHR; + /* dec_caps->pNext already filled in */ + + return vk->GetPhysicalDeviceVideoCapabilitiesKHR(hwctx->phys_dev, profile, + caps); +} + +static int vulkan_decode_get_profile(AVCodecContext *avctx, AVBufferRef *frames_ref, + enum AVPixelFormat *pix_fmt, VkFormat *vk_fmt, + FFVulkanDecodeProfileData *prof, + int *dpb_dedicate) +{ + VkResult ret; + int max_level, base_profile, cur_profile; + const FFVulkanDecodeDescriptor *vk_desc = get_codecdesc(avctx->codec_id); + AVHWFramesContext *frames = (AVHWFramesContext *)frames_ref->data; + AVHWDeviceContext *device = (AVHWDeviceContext *)frames->device_ref->data; + AVVulkanDeviceContext *hwctx = device->hwctx; + enum AVPixelFormat source_format; + enum AVPixelFormat best_format; + VkFormat best_vkfmt; + + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = dec->shared_ctx; + FFVulkanFunctions *vk = &ctx->s.vkfn; + + VkVideoCapabilitiesKHR *caps = &ctx->caps; + VkVideoDecodeCapabilitiesKHR *dec_caps = &ctx->dec_caps; + + VkVideoDecodeH264CapabilitiesKHR h264_caps = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_CAPABILITIES_KHR, + }; + VkVideoDecodeH265CapabilitiesKHR h265_caps = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_CAPABILITIES_KHR, + }; + VkVideoDecodeAV1CapabilitiesKHR av1_caps = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_CAPABILITIES_KHR, + }; + + VkPhysicalDeviceVideoFormatInfoKHR fmt_info = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VIDEO_FORMAT_INFO_KHR, + .pNext = &prof->profile_list, + }; + VkVideoFormatPropertiesKHR *ret_info; + uint32_t nb_out_fmts = 0; + + if (!(vk_desc->decode_extension & ctx->s.extensions)) { + av_log(avctx, AV_LOG_ERROR, "Device does not support decoding %s!\n", + avcodec_get_name(avctx->codec_id)); + return AVERROR(ENOSYS); + } + + cur_profile = avctx->profile; + base_profile = avctx->codec_id == AV_CODEC_ID_H264 ? AV_PROFILE_H264_CONSTRAINED_BASELINE : + avctx->codec_id == AV_CODEC_ID_H265 ? AV_PROFILE_HEVC_MAIN : + avctx->codec_id == AV_CODEC_ID_AV1 ? STD_VIDEO_AV1_PROFILE_MAIN : + 0; + + ret = vulkan_setup_profile(avctx, prof, hwctx, vk, vk_desc, + &h264_caps, + &h265_caps, + &av1_caps, + caps, + dec_caps, + cur_profile); + if (ret == VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR && + avctx->flags & AV_HWACCEL_FLAG_ALLOW_PROFILE_MISMATCH && + avctx->profile != base_profile) { + av_log(avctx, AV_LOG_VERBOSE, "%s profile %s not supported, attempting " + "again with profile %s\n", + avcodec_get_name(avctx->codec_id), + avcodec_profile_name(avctx->codec_id, cur_profile), + avcodec_profile_name(avctx->codec_id, base_profile)); + cur_profile = base_profile; + ret = vulkan_setup_profile(avctx, prof, hwctx, vk, vk_desc, + &h264_caps, + &h265_caps, + &av1_caps, + caps, + dec_caps, + cur_profile); + } + + if (ret == VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR) { + av_log(avctx, AV_LOG_VERBOSE, "Unable to initialize video session: " + "%s profile \"%s\" not supported!\n", + avcodec_get_name(avctx->codec_id), + avcodec_profile_name(avctx->codec_id, cur_profile)); + return AVERROR(EINVAL); + } else if (ret == VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR) { + av_log(avctx, AV_LOG_VERBOSE, "Unable to initialize video session: " + "format (%s) not supported!\n", + av_get_pix_fmt_name(avctx->sw_pix_fmt)); + return AVERROR(EINVAL); + } else if (ret == VK_ERROR_FEATURE_NOT_PRESENT || + ret == VK_ERROR_FORMAT_NOT_SUPPORTED) { + return AVERROR(EINVAL); + } else if (ret != VK_SUCCESS) { + return AVERROR_EXTERNAL; + } + + max_level = avctx->codec_id == AV_CODEC_ID_H264 ? ff_vk_h264_level_to_av(h264_caps.maxLevelIdc) : + avctx->codec_id == AV_CODEC_ID_H265 ? ff_vk_h265_level_to_av(h265_caps.maxLevelIdc) : + avctx->codec_id == AV_CODEC_ID_AV1 ? av1_caps.maxLevel : + 0; + + av_log(avctx, AV_LOG_VERBOSE, "Decoder capabilities for %s profile \"%s\":\n", + avcodec_get_name(avctx->codec_id), + avcodec_profile_name(avctx->codec_id, cur_profile)); + av_log(avctx, AV_LOG_VERBOSE, " Maximum level: %i (stream %i)\n", + max_level, avctx->level); + av_log(avctx, AV_LOG_VERBOSE, " Width: from %i to %i\n", + caps->minCodedExtent.width, caps->maxCodedExtent.width); + av_log(avctx, AV_LOG_VERBOSE, " Height: from %i to %i\n", + caps->minCodedExtent.height, caps->maxCodedExtent.height); + av_log(avctx, AV_LOG_VERBOSE, " Width alignment: %i\n", + caps->pictureAccessGranularity.width); + av_log(avctx, AV_LOG_VERBOSE, " Height alignment: %i\n", + caps->pictureAccessGranularity.height); + av_log(avctx, AV_LOG_VERBOSE, " Bitstream offset alignment: %"PRIu64"\n", + caps->minBitstreamBufferOffsetAlignment); + av_log(avctx, AV_LOG_VERBOSE, " Bitstream size alignment: %"PRIu64"\n", + caps->minBitstreamBufferSizeAlignment); + av_log(avctx, AV_LOG_VERBOSE, " Maximum references: %u\n", + caps->maxDpbSlots); + av_log(avctx, AV_LOG_VERBOSE, " Maximum active references: %u\n", + caps->maxActiveReferencePictures); + av_log(avctx, AV_LOG_VERBOSE, " Codec header name: '%s' (driver), '%s' (compiled)\n", + caps->stdHeaderVersion.extensionName, + vk_desc->ext_props.extensionName); + av_log(avctx, AV_LOG_VERBOSE, " Codec header version: %i.%i.%i (driver), %i.%i.%i (compiled)\n", + CODEC_VER(caps->stdHeaderVersion.specVersion), + CODEC_VER(vk_desc->ext_props.specVersion)); + av_log(avctx, AV_LOG_VERBOSE, " Decode modes:%s%s%s\n", + dec_caps->flags ? "" : + " invalid", + dec_caps->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR ? + " reuse_dst_dpb" : "", + dec_caps->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR ? + " dedicated_dpb" : ""); + av_log(avctx, AV_LOG_VERBOSE, " Capability flags:%s%s%s\n", + caps->flags ? "" : + " none", + caps->flags & VK_VIDEO_CAPABILITY_PROTECTED_CONTENT_BIT_KHR ? + " protected" : "", + caps->flags & VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR ? + " separate_references" : ""); + + /* Check if decoding is possible with the given parameters */ + if (avctx->coded_width < caps->minCodedExtent.width || + avctx->coded_height < caps->minCodedExtent.height || + avctx->coded_width > caps->maxCodedExtent.width || + avctx->coded_height > caps->maxCodedExtent.height) + return AVERROR(EINVAL); + + if (!(avctx->hwaccel_flags & AV_HWACCEL_FLAG_IGNORE_LEVEL) && + avctx->level > max_level) + return AVERROR(EINVAL); + + /* Some basic sanity checking */ + if (!(dec_caps->flags & (VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR | + VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR))) { + av_log(avctx, AV_LOG_ERROR, "Buggy driver signals invalid decoding mode: neither " + "VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR nor " + "VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR are set!\n"); + return AVERROR_EXTERNAL; + } else if ((dec_caps->flags & (VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR | + VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR) == + VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR) && + !(caps->flags & VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR)) { + av_log(avctx, AV_LOG_ERROR, "Cannot initialize Vulkan decoding session, buggy driver: " + "VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR set " + "but VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR is unset!\n"); + return AVERROR_EXTERNAL; + } + + /* TODO: make dedicated_dpb tunable */ + dec->dedicated_dpb = !(dec_caps->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR); + dec->layered_dpb = !(caps->flags & VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR); + + if (dec->dedicated_dpb) { + fmt_info.imageUsage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR; + } else { + fmt_info.imageUsage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR | + VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT; + } + + /* Get the format of the images necessary */ + ret = vk->GetPhysicalDeviceVideoFormatPropertiesKHR(hwctx->phys_dev, + &fmt_info, + &nb_out_fmts, NULL); + if (ret == VK_ERROR_FORMAT_NOT_SUPPORTED || + (!nb_out_fmts && ret == VK_SUCCESS)) { + return AVERROR(EINVAL); + } else if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to get Vulkan format properties: %s!\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + ret_info = av_mallocz(sizeof(*ret_info)*nb_out_fmts); + if (!ret_info) + return AVERROR(ENOMEM); + + for (int i = 0; i < nb_out_fmts; i++) + ret_info[i].sType = VK_STRUCTURE_TYPE_VIDEO_FORMAT_PROPERTIES_KHR; + + ret = vk->GetPhysicalDeviceVideoFormatPropertiesKHR(hwctx->phys_dev, + &fmt_info, + &nb_out_fmts, ret_info); + if (ret == VK_ERROR_FORMAT_NOT_SUPPORTED || + (!nb_out_fmts && ret == VK_SUCCESS)) { + av_free(ret_info); + return AVERROR(EINVAL); + } else if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to get Vulkan format properties: %s!\n", + ff_vk_ret2str(ret)); + av_free(ret_info); + return AVERROR_EXTERNAL; + } + + /* Find a format to use */ + *pix_fmt = best_format = AV_PIX_FMT_NONE; + *vk_fmt = best_vkfmt = VK_FORMAT_UNDEFINED; + source_format = avctx->sw_pix_fmt; + + av_log(avctx, AV_LOG_DEBUG, "Choosing best pixel format for decoding from %i:\n", nb_out_fmts); + for (int i = 0; i < nb_out_fmts; i++) { + enum AVPixelFormat tmp = ff_vk_pix_fmt_from_vkfmt(ret_info[i].format); + if (tmp == AV_PIX_FMT_NONE) { + av_log(avctx, AV_LOG_WARNING, "Invalid/unknown Vulkan format %i!\n", ret_info[i].format); + continue; + } + + best_format = av_find_best_pix_fmt_of_2(tmp, best_format, source_format, 0, NULL); + if (tmp == best_format) + best_vkfmt = ret_info[i].format; + + av_log(avctx, AV_LOG_DEBUG, " %s%s (Vulkan ID: %i)\n", + av_get_pix_fmt_name(tmp), tmp == best_format ? "*" : "", + ret_info[i].format); + } + + av_free(ret_info); + + if (best_format == AV_PIX_FMT_NONE) { + av_log(avctx, AV_LOG_ERROR, "No valid/compatible pixel format found for decoding!\n"); + return AVERROR(EINVAL); + } else { + av_log(avctx, AV_LOG_VERBOSE, "Chosen frame pixfmt: %s (Vulkan ID: %i)\n", + av_get_pix_fmt_name(best_format), best_vkfmt); + } + + *pix_fmt = best_format; + *vk_fmt = best_vkfmt; + + *dpb_dedicate = dec->dedicated_dpb; + + return 0; +} + +static void free_profile_data(AVHWFramesContext *hwfc) +{ + av_free(hwfc->user_opaque); +} + +int ff_vk_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx) +{ + VkFormat vkfmt; + int err, dedicated_dpb; + AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hw_frames_ctx->data; + AVVulkanFramesContext *hwfc = frames_ctx->hwctx; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeProfileData *prof; + + frames_ctx->sw_format = AV_PIX_FMT_NONE; + + err = vulkan_decode_bootstrap(avctx, hw_frames_ctx); + if (err < 0) + return err; + + prof = av_mallocz(sizeof(FFVulkanDecodeProfileData)); + if (!prof) + return AVERROR(ENOMEM); + + err = vulkan_decode_get_profile(avctx, hw_frames_ctx, + &frames_ctx->sw_format, &vkfmt, + prof, &dedicated_dpb); + if (err < 0) { + av_free(prof); + return err; + } + + frames_ctx->user_opaque = prof; + frames_ctx->free = free_profile_data; + + frames_ctx->width = avctx->coded_width; + frames_ctx->height = avctx->coded_height; + frames_ctx->format = AV_PIX_FMT_VULKAN; + + hwfc->format[0] = vkfmt; + hwfc->create_pnext = &prof->profile_list; + hwfc->tiling = VK_IMAGE_TILING_OPTIMAL; + hwfc->usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR; + + if (!dec->dedicated_dpb) + hwfc->usage |= VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR; + + return err; +} + +static void vk_decode_free_params(void *opaque, uint8_t *data) +{ + FFVulkanDecodeShared *ctx = opaque; + FFVulkanFunctions *vk = &ctx->s.vkfn; + VkVideoSessionParametersKHR *par = (VkVideoSessionParametersKHR *)data; + vk->DestroyVideoSessionParametersKHR(ctx->s.hwctx->act_dev, *par, + ctx->s.hwctx->alloc); + av_free(par); +} + +int ff_vk_decode_create_params(AVBufferRef **par_ref, void *logctx, FFVulkanDecodeShared *ctx, + const VkVideoSessionParametersCreateInfoKHR *session_params_create) +{ + VkVideoSessionParametersKHR *par = av_malloc(sizeof(*par)); + const FFVulkanFunctions *vk = &ctx->s.vkfn; + VkResult ret; + + if (!par) + return AVERROR(ENOMEM); + + /* Create session parameters */ + ret = vk->CreateVideoSessionParametersKHR(ctx->s.hwctx->act_dev, session_params_create, + ctx->s.hwctx->alloc, par); + if (ret != VK_SUCCESS) { + av_log(logctx, AV_LOG_ERROR, "Unable to create Vulkan video session parameters: %s!\n", + ff_vk_ret2str(ret)); + av_free(par); + return AVERROR_EXTERNAL; + } + *par_ref = av_buffer_create((uint8_t *)par, sizeof(*par), + vk_decode_free_params, ctx, 0); + if (!*par_ref) { + vk_decode_free_params(ctx, (uint8_t *)par); + return AVERROR(ENOMEM); + } + + return 0; +} + +int ff_vk_decode_uninit(AVCodecContext *avctx) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = dec->shared_ctx; + + /* Wait on and free execution pool */ + ff_vk_exec_pool_free(&ctx->s, &dec->exec_pool); + + av_freep(&dec->hevc_headers); + av_buffer_unref(&dec->session_params); + ff_refstruct_unref(&dec->shared_ctx); + av_freep(&dec->slice_off); + return 0; +} + +int ff_vk_decode_init(AVCodecContext *avctx) +{ + int err, qf, cxpos = 0, cypos = 0, nb_q = 0; + VkResult ret; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx; + FFVulkanContext *s; + FFVulkanFunctions *vk; + const VkVideoProfileInfoKHR *profile; + const FFVulkanDecodeDescriptor *vk_desc; + const VkPhysicalDeviceDriverProperties *driver_props; + + VkVideoDecodeH264SessionParametersCreateInfoKHR h264_params = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_KHR, + }; + VkVideoDecodeH265SessionParametersCreateInfoKHR h265_params = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_KHR, + }; + StdVideoAV1SequenceHeader av1_empty_seq = { 0 }; + VkVideoDecodeAV1SessionParametersCreateInfoKHR av1_params = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pStdSequenceHeader = &av1_empty_seq, + }; + VkVideoSessionParametersCreateInfoKHR session_params_create = { + .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pNext = avctx->codec_id == AV_CODEC_ID_H264 ? (void *)&h264_params : + avctx->codec_id == AV_CODEC_ID_HEVC ? (void *)&h265_params : + avctx->codec_id == AV_CODEC_ID_AV1 ? (void *)&av1_params : + NULL, + }; + VkVideoSessionCreateInfoKHR session_create = { + .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_CREATE_INFO_KHR, + }; + VkSamplerYcbcrConversionCreateInfo yuv_sampler_info = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO, + .components = ff_comp_identity_map, + .ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY, + .ycbcrRange = avctx->color_range == AVCOL_RANGE_MPEG, /* Ignored */ + }; + + err = ff_decode_get_hw_frames_ctx(avctx, AV_HWDEVICE_TYPE_VULKAN); + if (err < 0) + return err; + + /* Initialize contexts */ + ctx = dec->shared_ctx; + s = &ctx->s; + vk = &ctx->s.vkfn; + + s->frames_ref = av_buffer_ref(avctx->hw_frames_ctx); + s->frames = (AVHWFramesContext *)s->frames_ref->data; + s->hwfc = s->frames->hwctx; + + s->device = (AVHWDeviceContext *)s->frames->device_ref->data; + s->hwctx = s->device->hwctx; + + profile = get_video_profile(ctx, avctx->codec_id); + if (!profile) { + av_log(avctx, AV_LOG_ERROR, "Video profile missing from frames context!"); + return AVERROR(EINVAL); + } + + err = ff_vk_load_props(s); + if (err < 0) + goto fail; + + /* Create queue context */ + qf = ff_vk_qf_init(s, &ctx->qf, VK_QUEUE_VIDEO_DECODE_BIT_KHR); + + vk_desc = get_codecdesc(avctx->codec_id); + /* Check for support */ + if (!(s->video_props[qf].videoCodecOperations & vk_desc->decode_op)) { + av_log(avctx, AV_LOG_ERROR, "Decoding %s not supported on the given " + "queue family %i!\n", avcodec_get_name(avctx->codec_id), qf); + return AVERROR(EINVAL); + } + + /* Enable queries if supported */ + if (s->query_props[qf].queryResultStatusSupport) + nb_q = 1; + + session_create.flags = 0x0; + session_create.queueFamilyIndex = s->hwctx->queue_family_decode_index; + session_create.maxCodedExtent = ctx->caps.maxCodedExtent; + session_create.maxDpbSlots = ctx->caps.maxDpbSlots; + session_create.maxActiveReferencePictures = ctx->caps.maxActiveReferencePictures; + session_create.pictureFormat = s->hwfc->format[0]; + session_create.referencePictureFormat = session_create.pictureFormat; + session_create.pStdHeaderVersion = &vk_desc->ext_props; + session_create.pVideoProfile = profile; + + /* Create decode exec context for this specific main thread. + * 2 async contexts per thread was experimentally determined to be optimal + * for a majority of streams. */ + err = ff_vk_exec_pool_init(s, &ctx->qf, &dec->exec_pool, 2, + nb_q, VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR, 0, + profile); + if (err < 0) + goto fail; + + err = ff_vk_video_common_init(avctx, s, &ctx->common, &session_create); + if (err < 0) + goto fail; + + /* Get sampler */ + av_chroma_location_enum_to_pos(&cxpos, &cypos, avctx->chroma_sample_location); + yuv_sampler_info.xChromaOffset = cxpos >> 7; + yuv_sampler_info.yChromaOffset = cypos >> 7; + yuv_sampler_info.format = s->hwfc->format[0]; + ret = vk->CreateSamplerYcbcrConversion(s->hwctx->act_dev, &yuv_sampler_info, + s->hwctx->alloc, &ctx->yuv_sampler); + if (ret != VK_SUCCESS) { + err = AVERROR_EXTERNAL; + goto fail; + } + + /* If doing an out-of-place decoding, create a DPB pool */ + if (dec->dedicated_dpb || avctx->codec_id == AV_CODEC_ID_AV1) { + AVHWFramesContext *dpb_frames; + AVVulkanFramesContext *dpb_hwfc; + + ctx->dpb_hwfc_ref = av_hwframe_ctx_alloc(s->frames->device_ref); + if (!ctx->dpb_hwfc_ref) { + err = AVERROR(ENOMEM); + goto fail; + } + + dpb_frames = (AVHWFramesContext *)ctx->dpb_hwfc_ref->data; + dpb_frames->format = s->frames->format; + dpb_frames->sw_format = s->frames->sw_format; + dpb_frames->width = avctx->coded_width; + dpb_frames->height = avctx->coded_height; + + dpb_hwfc = dpb_frames->hwctx; + dpb_hwfc->create_pnext = (void *)ff_vk_find_struct(ctx->s.hwfc->create_pnext, + VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR); + dpb_hwfc->format[0] = s->hwfc->format[0]; + dpb_hwfc->tiling = VK_IMAGE_TILING_OPTIMAL; + dpb_hwfc->usage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR | + VK_IMAGE_USAGE_SAMPLED_BIT; /* Shuts validator up. */ + + if (dec->layered_dpb) + dpb_hwfc->nb_layers = ctx->caps.maxDpbSlots; + + err = av_hwframe_ctx_init(ctx->dpb_hwfc_ref); + if (err < 0) + goto fail; + + if (dec->layered_dpb) { + ctx->layered_frame = vk_get_dpb_pool(ctx); + if (!ctx->layered_frame) { + err = AVERROR(ENOMEM); + goto fail; + } + + err = vk_decode_create_view(dec, &ctx->layered_view, &ctx->layered_aspect, + (AVVkFrame *)ctx->layered_frame->data[0], + s->hwfc->format[0], 0); + if (err < 0) + goto fail; + } + } + + session_params_create.videoSession = ctx->common.session; + ret = vk->CreateVideoSessionParametersKHR(s->hwctx->act_dev, &session_params_create, + s->hwctx->alloc, &ctx->empty_session_params); + if (ret != VK_SUCCESS) { + av_log(avctx, AV_LOG_ERROR, "Unable to create empty Vulkan video session parameters: %s!\n", + ff_vk_ret2str(ret)); + return AVERROR_EXTERNAL; + } + + driver_props = &dec->shared_ctx->s.driver_props; + if (driver_props->driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY && + driver_props->conformanceVersion.major == 1 && + driver_props->conformanceVersion.minor == 3 && + driver_props->conformanceVersion.subminor == 8 && + driver_props->conformanceVersion.patch < 3) + dec->quirk_av1_offset = 1; + + ff_vk_decode_flush(avctx); + + av_log(avctx, AV_LOG_VERBOSE, "Vulkan decoder initialization sucessful\n"); + + return 0; + +fail: + ff_vk_decode_uninit(avctx); + + return err; +} diff --git a/libavcodec/vulkan_decode.h b/libavcodec/vulkan_decode.h new file mode 100644 index 00000000000..076af93499f --- /dev/null +++ b/libavcodec/vulkan_decode.h @@ -0,0 +1,191 @@ +/* + * 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 + */ + +#ifndef AVCODEC_VULKAN_DECODE_H +#define AVCODEC_VULKAN_DECODE_H + +#include "codec_id.h" +#include "decode.h" +#include "hwaccel_internal.h" +#include "internal.h" + +#include "vulkan_video.h" + +typedef struct FFVulkanDecodeDescriptor { + enum AVCodecID codec_id; + FFVulkanExtensions decode_extension; + VkVideoCodecOperationFlagBitsKHR decode_op; + + VkExtensionProperties ext_props; +} FFVulkanDecodeDescriptor; + +typedef struct FFVulkanDecodeProfileData { + VkVideoDecodeH264ProfileInfoKHR h264_profile; + VkVideoDecodeH265ProfileInfoKHR h265_profile; + VkVideoDecodeAV1ProfileInfoKHR av1_profile; + VkVideoDecodeUsageInfoKHR usage; + VkVideoProfileInfoKHR profile; + VkVideoProfileListInfoKHR profile_list; +} FFVulkanDecodeProfileData; + +typedef struct FFVulkanDecodeShared { + FFVulkanContext s; + FFVkVideoCommon common; + FFVkQueueFamilyCtx qf; + + VkVideoCapabilitiesKHR caps; + VkVideoDecodeCapabilitiesKHR dec_caps; + + AVBufferRef *dpb_hwfc_ref; /* Only used for dedicated_dpb */ + + AVFrame *layered_frame; /* Only used for layered_dpb */ + VkImageView layered_view; + VkImageAspectFlags layered_aspect; + + VkVideoSessionParametersKHR empty_session_params; + + VkSamplerYcbcrConversion yuv_sampler; +} FFVulkanDecodeShared; + +typedef struct FFVulkanDecodeContext { + FFVulkanDecodeShared *shared_ctx; + AVBufferRef *session_params; + FFVkExecPool exec_pool; + + int dedicated_dpb; /* Oddity #1 - separate DPB images */ + int layered_dpb; /* Madness #1 - layered DPB images */ + int external_fg; /* Oddity #2 - hardware can't apply film grain */ + uint32_t frame_id_alloc_mask; /* For AV1 only */ + + /* Workaround for NVIDIA drivers tested with CTS version 1.3.8 for AV1. + * The tests were incorrect as the OrderHints were offset by 1. */ + int quirk_av1_offset; + + /* Thread-local state below */ + struct HEVCHeaderSet *hevc_headers; + size_t hevc_headers_size; + + uint32_t *slice_off; + unsigned int slice_off_max; +} FFVulkanDecodeContext; + +typedef struct FFVulkanDecodePicture { + AVFrame *dpb_frame; /* Only used for out-of-place decoding. */ + + VkImageView img_view_ref; /* Image representation view (reference) */ + VkImageView img_view_out; /* Image representation view (output-only) */ + VkImageView img_view_dest; /* Set to img_view_out if no layered refs are used */ + VkImageAspectFlags img_aspect; /* Image plane mask bits */ + VkImageAspectFlags img_aspect_ref; /* Only used for out-of-place decoding */ + + VkSemaphore sem; + uint64_t sem_value; + + /* Current picture */ + VkVideoPictureResourceInfoKHR ref; + VkVideoReferenceSlotInfoKHR ref_slot; + + /* Picture refs. H264 has the maximum number of refs (36) of any supported codec. */ + VkVideoPictureResourceInfoKHR refs [36]; + VkVideoReferenceSlotInfoKHR ref_slots[36]; + + /* Main decoding struct */ + VkVideoDecodeInfoKHR decode_info; + + /* Slice data */ + AVBufferRef *slices_buf; + size_t slices_size; + + /* Vulkan functions needed for destruction, as no other context is guaranteed to exist */ + PFN_vkWaitSemaphores wait_semaphores; + PFN_vkDestroyImageView destroy_image_view; +} FFVulkanDecodePicture; + +/** + * Initialize decoder. + */ +int ff_vk_decode_init(AVCodecContext *avctx); + +/** + * Synchronize the contexts between 2 threads. + */ +int ff_vk_update_thread_context(AVCodecContext *dst, const AVCodecContext *src); + +/** + * Initialize hw_frames_ctx with the parameters needed to decode the stream + * using the parameters from avctx. + * + * NOTE: if avctx->internal->hwaccel_priv_data exists, will partially initialize + * the context. + */ +int ff_vk_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx); + +/** + * Removes current session parameters to recreate them + */ +int ff_vk_params_invalidate(AVCodecContext *avctx, int t, const uint8_t *b, uint32_t s); + +/** + * Prepare a frame, creates the image view, and sets up the dpb fields. + */ +int ff_vk_decode_prepare_frame(FFVulkanDecodeContext *dec, AVFrame *pic, + FFVulkanDecodePicture *vkpic, int is_current, + int alloc_dpb); + +/** + * Add slice data to frame. + */ +int ff_vk_decode_add_slice(AVCodecContext *avctx, FFVulkanDecodePicture *vp, + const uint8_t *data, size_t size, int add_startcode, + uint32_t *nb_slices, const uint32_t **offsets); + +/** + * Decode a frame. + */ +int ff_vk_decode_frame(AVCodecContext *avctx, + AVFrame *pic, FFVulkanDecodePicture *vp, + AVFrame *rpic[], FFVulkanDecodePicture *rvkp[]); + +/** + * Free a frame and its state. + */ +void ff_vk_decode_free_frame(AVHWDeviceContext *dev_ctx, FFVulkanDecodePicture *vp); + +/** + * Get an FFVkBuffer suitable for decoding from. + */ +int ff_vk_get_decode_buffer(FFVulkanDecodeContext *ctx, AVBufferRef **buf, + void *create_pNext, size_t size); + +/** + * Create VkVideoSessionParametersKHR wrapped in an AVBufferRef. + */ +int ff_vk_decode_create_params(AVBufferRef **par_ref, void *logctx, FFVulkanDecodeShared *ctx, + const VkVideoSessionParametersCreateInfoKHR *session_params_create); + +/** + * Flush decoder. + */ +void ff_vk_decode_flush(AVCodecContext *avctx); + +/** + * Free decoder. + */ +int ff_vk_decode_uninit(AVCodecContext *avctx); + +#endif /* AVCODEC_VULKAN_DECODE_H */ diff --git a/libavcodec/vulkan_h264.c b/libavcodec/vulkan_h264.c new file mode 100644 index 00000000000..0b296b3cc3c --- /dev/null +++ b/libavcodec/vulkan_h264.c @@ -0,0 +1,570 @@ +/* + * 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 "h264dec.h" +#include "h264_ps.h" + +#include "vulkan_decode.h" + +const FFVulkanDecodeDescriptor ff_vk_dec_h264_desc = { + .codec_id = AV_CODEC_ID_H264, + .decode_extension = FF_VK_EXT_VIDEO_DECODE_H264, + .decode_op = VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR, + .ext_props = { + .extensionName = VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_EXTENSION_NAME, + .specVersion = VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_SPEC_VERSION, + }, +}; + +typedef struct H264VulkanDecodePicture { + FFVulkanDecodePicture vp; + + /* Current picture */ + StdVideoDecodeH264ReferenceInfo h264_ref; + VkVideoDecodeH264DpbSlotInfoKHR vkh264_ref; + + /* Picture refs */ + H264Picture *ref_src [H264_MAX_PICTURE_COUNT]; + StdVideoDecodeH264ReferenceInfo h264_refs [H264_MAX_PICTURE_COUNT]; + VkVideoDecodeH264DpbSlotInfoKHR vkh264_refs[H264_MAX_PICTURE_COUNT]; + + /* Current picture (contd.) */ + StdVideoDecodeH264PictureInfo h264pic; + VkVideoDecodeH264PictureInfoKHR h264_pic_info; +} H264VulkanDecodePicture; + +const static int h264_scaling_list8_order[] = { 0, 3, 1, 4, 2, 5 }; + +static int vk_h264_fill_pict(AVCodecContext *avctx, H264Picture **ref_src, + VkVideoReferenceSlotInfoKHR *ref_slot, /* Main structure */ + VkVideoPictureResourceInfoKHR *ref, /* Goes in ^ */ + VkVideoDecodeH264DpbSlotInfoKHR *vkh264_ref, /* Goes in ^ */ + StdVideoDecodeH264ReferenceInfo *h264_ref, /* Goes in ^ */ + H264Picture *pic, int is_current, + int is_field, int picture_structure, + int dpb_slot_index) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + H264VulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vkpic = &hp->vp; + + int err = ff_vk_decode_prepare_frame(dec, pic->f, vkpic, is_current, + dec->dedicated_dpb); + if (err < 0) + return err; + + *h264_ref = (StdVideoDecodeH264ReferenceInfo) { + .FrameNum = pic->long_ref ? pic->pic_id : pic->frame_num, + .PicOrderCnt = { pic->field_poc[0], pic->field_poc[1] }, + .flags = (StdVideoDecodeH264ReferenceInfoFlags) { + .top_field_flag = is_field ? !!(picture_structure & PICT_TOP_FIELD) : 0, + .bottom_field_flag = is_field ? !!(picture_structure & PICT_BOTTOM_FIELD) : 0, + .used_for_long_term_reference = pic->reference && pic->long_ref, + /* + * flags.is_non_existing is used to indicate whether the picture is marked as + * “non-existing” as defined in section 8.2.5.2 of the ITU-T H.264 Specification; + * 8.2.5.2 Decoding process for gaps in frame_num + * corresponds to the code in h264_slice.c:h264_field_start, + * which sets the invalid_gap flag when decoding. + */ + .is_non_existing = pic->invalid_gap, + }, + }; + + *vkh264_ref = (VkVideoDecodeH264DpbSlotInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_DPB_SLOT_INFO_KHR, + .pStdReferenceInfo = h264_ref, + }; + + *ref = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->f->width, pic->f->height }, + .baseArrayLayer = dec->layered_dpb ? dpb_slot_index : 0, + .imageViewBinding = vkpic->img_view_ref, + }; + + *ref_slot = (VkVideoReferenceSlotInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR, + .pNext = vkh264_ref, + .slotIndex = dpb_slot_index, + .pPictureResource = ref, + }; + + if (ref_src) + *ref_src = pic; + + return 0; +} + +static StdVideoH264LevelIdc convert_to_vk_level_idc(int level_idc) +{ + switch (level_idc) { + case 10: return STD_VIDEO_H264_LEVEL_IDC_1_0; + case 11: return STD_VIDEO_H264_LEVEL_IDC_1_1; + case 12: return STD_VIDEO_H264_LEVEL_IDC_1_2; + case 13: return STD_VIDEO_H264_LEVEL_IDC_1_3; + case 20: return STD_VIDEO_H264_LEVEL_IDC_2_0; + case 21: return STD_VIDEO_H264_LEVEL_IDC_2_1; + case 22: return STD_VIDEO_H264_LEVEL_IDC_2_2; + case 30: return STD_VIDEO_H264_LEVEL_IDC_3_0; + case 31: return STD_VIDEO_H264_LEVEL_IDC_3_1; + case 32: return STD_VIDEO_H264_LEVEL_IDC_3_2; + case 40: return STD_VIDEO_H264_LEVEL_IDC_4_0; + case 41: return STD_VIDEO_H264_LEVEL_IDC_4_1; + case 42: return STD_VIDEO_H264_LEVEL_IDC_4_2; + case 50: return STD_VIDEO_H264_LEVEL_IDC_5_0; + case 51: return STD_VIDEO_H264_LEVEL_IDC_5_1; + case 52: return STD_VIDEO_H264_LEVEL_IDC_5_2; + case 60: return STD_VIDEO_H264_LEVEL_IDC_6_0; + case 61: return STD_VIDEO_H264_LEVEL_IDC_6_1; + default: + case 62: return STD_VIDEO_H264_LEVEL_IDC_6_2; + } +} + +static void set_sps(const SPS *sps, + StdVideoH264ScalingLists *vksps_scaling, + StdVideoH264HrdParameters *vksps_vui_header, + StdVideoH264SequenceParameterSetVui *vksps_vui, + StdVideoH264SequenceParameterSet *vksps) +{ + *vksps_scaling = (StdVideoH264ScalingLists) { + .scaling_list_present_mask = sps->scaling_matrix_present_mask, + .use_default_scaling_matrix_mask = 0, /* We already fill in the default matrix */ + }; + + for (int i = 0; i < STD_VIDEO_H264_SCALING_LIST_4X4_NUM_LISTS; i++) + for (int j = 0; j < STD_VIDEO_H264_SCALING_LIST_4X4_NUM_ELEMENTS; j++) + vksps_scaling->ScalingList4x4[i][j] = sps->scaling_matrix4[i][ff_zigzag_scan[j]]; + + for (int i = 0; i < STD_VIDEO_H264_SCALING_LIST_8X8_NUM_LISTS; i++) + for (int j = 0; j < STD_VIDEO_H264_SCALING_LIST_8X8_NUM_ELEMENTS; j++) + vksps_scaling->ScalingList8x8[i][j] = + sps->scaling_matrix8[h264_scaling_list8_order[i]][ff_zigzag_direct[j]]; + + *vksps_vui_header = (StdVideoH264HrdParameters) { + .cpb_cnt_minus1 = sps->cpb_cnt - 1, + .bit_rate_scale = sps->bit_rate_scale, + .initial_cpb_removal_delay_length_minus1 = sps->initial_cpb_removal_delay_length - 1, + .cpb_removal_delay_length_minus1 = sps->cpb_removal_delay_length - 1, + .dpb_output_delay_length_minus1 = sps->dpb_output_delay_length - 1, + .time_offset_length = sps->time_offset_length, + }; + + for (int i = 0; i < sps->cpb_cnt; i++) { + vksps_vui_header->bit_rate_value_minus1[i] = sps->bit_rate_value[i] - 1; + vksps_vui_header->cpb_size_value_minus1[i] = sps->cpb_size_value[i] - 1; + vksps_vui_header->cbr_flag[i] = (sps->cpr_flag >> i) & 0x1; + } + + *vksps_vui = (StdVideoH264SequenceParameterSetVui) { + .aspect_ratio_idc = sps->vui.aspect_ratio_idc, + .sar_width = sps->vui.sar.num, + .sar_height = sps->vui.sar.den, + .video_format = sps->vui.video_format, + .colour_primaries = sps->vui.colour_primaries, + .transfer_characteristics = sps->vui.transfer_characteristics, + .matrix_coefficients = sps->vui.matrix_coeffs, + .num_units_in_tick = sps->num_units_in_tick, + .time_scale = sps->time_scale, + .pHrdParameters = vksps_vui_header, + .max_num_reorder_frames = sps->num_reorder_frames, + .max_dec_frame_buffering = sps->max_dec_frame_buffering, + .flags = (StdVideoH264SpsVuiFlags) { + .aspect_ratio_info_present_flag = sps->vui.aspect_ratio_info_present_flag, + .overscan_info_present_flag = sps->vui.overscan_info_present_flag, + .overscan_appropriate_flag = sps->vui.overscan_appropriate_flag, + .video_signal_type_present_flag = sps->vui.video_signal_type_present_flag, + .video_full_range_flag = sps->vui.video_full_range_flag, + .color_description_present_flag = sps->vui.colour_description_present_flag, + .chroma_loc_info_present_flag = sps->vui.chroma_location, + .timing_info_present_flag = sps->timing_info_present_flag, + .fixed_frame_rate_flag = sps->fixed_frame_rate_flag, + .bitstream_restriction_flag = sps->bitstream_restriction_flag, + .nal_hrd_parameters_present_flag = sps->nal_hrd_parameters_present_flag, + .vcl_hrd_parameters_present_flag = sps->vcl_hrd_parameters_present_flag, + }, + }; + + *vksps = (StdVideoH264SequenceParameterSet) { + .profile_idc = sps->profile_idc, + .level_idc = convert_to_vk_level_idc(sps->level_idc), + .seq_parameter_set_id = sps->sps_id, + .chroma_format_idc = sps->chroma_format_idc, + .bit_depth_luma_minus8 = sps->bit_depth_luma - 8, + .bit_depth_chroma_minus8 = sps->bit_depth_chroma - 8, + .log2_max_frame_num_minus4 = sps->log2_max_frame_num - 4, + .pic_order_cnt_type = sps->poc_type, + .log2_max_pic_order_cnt_lsb_minus4 = sps->poc_type ? 0 : sps->log2_max_poc_lsb - 4, + .offset_for_non_ref_pic = sps->offset_for_non_ref_pic, + .offset_for_top_to_bottom_field = sps->offset_for_top_to_bottom_field, + .num_ref_frames_in_pic_order_cnt_cycle = sps->poc_cycle_length, + .max_num_ref_frames = sps->ref_frame_count, + .pic_width_in_mbs_minus1 = sps->mb_width - 1, + .pic_height_in_map_units_minus1 = (sps->mb_height/(2 - sps->frame_mbs_only_flag)) - 1, + .frame_crop_left_offset = sps->crop_left, + .frame_crop_right_offset = sps->crop_right, + .frame_crop_top_offset = sps->crop_top, + .frame_crop_bottom_offset = sps->crop_bottom, + .flags = (StdVideoH264SpsFlags) { + .constraint_set0_flag = (sps->constraint_set_flags >> 0) & 0x1, + .constraint_set1_flag = (sps->constraint_set_flags >> 1) & 0x1, + .constraint_set2_flag = (sps->constraint_set_flags >> 2) & 0x1, + .constraint_set3_flag = (sps->constraint_set_flags >> 3) & 0x1, + .constraint_set4_flag = (sps->constraint_set_flags >> 4) & 0x1, + .constraint_set5_flag = (sps->constraint_set_flags >> 5) & 0x1, + .direct_8x8_inference_flag = sps->direct_8x8_inference_flag, + .mb_adaptive_frame_field_flag = sps->mb_aff, + .frame_mbs_only_flag = sps->frame_mbs_only_flag, + .delta_pic_order_always_zero_flag = sps->delta_pic_order_always_zero_flag, + .separate_colour_plane_flag = sps->residual_color_transform_flag, + .gaps_in_frame_num_value_allowed_flag = sps->gaps_in_frame_num_allowed_flag, + .qpprime_y_zero_transform_bypass_flag = sps->transform_bypass, + .frame_cropping_flag = sps->crop, + .seq_scaling_matrix_present_flag = sps->scaling_matrix_present, + .vui_parameters_present_flag = sps->vui_parameters_present_flag, + }, + .pOffsetForRefFrame = sps->offset_for_ref_frame, + .pScalingLists = vksps_scaling, + .pSequenceParameterSetVui = vksps_vui, + }; +} + +static void set_pps(const PPS *pps, const SPS *sps, + StdVideoH264ScalingLists *vkpps_scaling, + StdVideoH264PictureParameterSet *vkpps) +{ + *vkpps_scaling = (StdVideoH264ScalingLists) { + .scaling_list_present_mask = pps->pic_scaling_matrix_present_mask, + .use_default_scaling_matrix_mask = 0, /* We already fill in the default matrix */ + }; + + for (int i = 0; i < STD_VIDEO_H264_SCALING_LIST_4X4_NUM_LISTS; i++) + for (int j = 0; j < STD_VIDEO_H264_SCALING_LIST_4X4_NUM_ELEMENTS; j++) + vkpps_scaling->ScalingList4x4[i][j] = pps->scaling_matrix4[i][ff_zigzag_scan[j]]; + + for (int i = 0; i < STD_VIDEO_H264_SCALING_LIST_8X8_NUM_LISTS; i++) + for (int j = 0; j < STD_VIDEO_H264_SCALING_LIST_8X8_NUM_ELEMENTS; j++) + vkpps_scaling->ScalingList8x8[i][j] = + pps->scaling_matrix8[h264_scaling_list8_order[i]][ff_zigzag_direct[j]]; + + *vkpps = (StdVideoH264PictureParameterSet) { + .seq_parameter_set_id = pps->sps_id, + .pic_parameter_set_id = pps->pps_id, + .num_ref_idx_l0_default_active_minus1 = pps->ref_count[0] - 1, + .num_ref_idx_l1_default_active_minus1 = pps->ref_count[1] - 1, + .weighted_bipred_idc = pps->weighted_bipred_idc, + .pic_init_qp_minus26 = pps->init_qp - 26, + .pic_init_qs_minus26 = pps->init_qs - 26, + .chroma_qp_index_offset = pps->chroma_qp_index_offset[0], + .second_chroma_qp_index_offset = pps->chroma_qp_index_offset[1], + .flags = (StdVideoH264PpsFlags) { + .transform_8x8_mode_flag = pps->transform_8x8_mode, + .redundant_pic_cnt_present_flag = pps->redundant_pic_cnt_present, + .constrained_intra_pred_flag = pps->constrained_intra_pred, + .deblocking_filter_control_present_flag = pps->deblocking_filter_parameters_present, + .weighted_pred_flag = pps->weighted_pred, + .bottom_field_pic_order_in_frame_present_flag = pps->pic_order_present, + .entropy_coding_mode_flag = pps->cabac, + .pic_scaling_matrix_present_flag = pps->pic_scaling_matrix_present_flag, + }, + .pScalingLists = vkpps_scaling, + }; +} + +static int vk_h264_create_params(AVCodecContext *avctx, AVBufferRef **buf) +{ + int err; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = dec->shared_ctx; + const H264Context *h = avctx->priv_data; + + /* SPS */ + StdVideoH264ScalingLists vksps_scaling[MAX_SPS_COUNT]; + StdVideoH264HrdParameters vksps_vui_header[MAX_SPS_COUNT]; + StdVideoH264SequenceParameterSetVui vksps_vui[MAX_SPS_COUNT]; + StdVideoH264SequenceParameterSet vksps[MAX_SPS_COUNT]; + + /* PPS */ + StdVideoH264ScalingLists vkpps_scaling[MAX_PPS_COUNT]; + StdVideoH264PictureParameterSet vkpps[MAX_PPS_COUNT]; + + VkVideoDecodeH264SessionParametersAddInfoKHR h264_params_info = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_KHR, + .pStdSPSs = vksps, + .stdSPSCount = 0, + .pStdPPSs = vkpps, + .stdPPSCount = 0, + }; + VkVideoDecodeH264SessionParametersCreateInfoKHR h264_params = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pParametersAddInfo = &h264_params_info, + }; + VkVideoSessionParametersCreateInfoKHR session_params_create = { + .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pNext = &h264_params, + .videoSession = ctx->common.session, + .videoSessionParametersTemplate = VK_NULL_HANDLE, + }; + + /* SPS list */ + for (int i = 0; i < FF_ARRAY_ELEMS(h->ps.sps_list); i++) { + if (h->ps.sps_list[i]) { + const SPS *sps_l = h->ps.sps_list[i]; + int idx = h264_params_info.stdSPSCount; + set_sps(sps_l, &vksps_scaling[idx], &vksps_vui_header[idx], &vksps_vui[idx], &vksps[idx]); + h264_params_info.stdSPSCount++; + } + } + + /* PPS list */ + for (int i = 0; i < FF_ARRAY_ELEMS(h->ps.pps_list); i++) { + if (h->ps.pps_list[i]) { + const PPS *pps_l = h->ps.pps_list[i]; + int idx = h264_params_info.stdPPSCount; + set_pps(pps_l, pps_l->sps, &vkpps_scaling[idx], &vkpps[idx]); + h264_params_info.stdPPSCount++; + } + } + + h264_params.maxStdSPSCount = h264_params_info.stdSPSCount; + h264_params.maxStdPPSCount = h264_params_info.stdPPSCount; + + err = ff_vk_decode_create_params(buf, avctx, ctx, &session_params_create); + if (err < 0) + return err; + + av_log(avctx, AV_LOG_DEBUG, "Created frame parameters: %i SPS %i PPS\n", + h264_params_info.stdSPSCount, h264_params_info.stdPPSCount); + + return 0; +} + +static int vk_h264_start_frame(AVCodecContext *avctx, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + int err; + int dpb_slot_index = 0; + H264Context *h = avctx->priv_data; + H264Picture *pic = h->cur_pic_ptr; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + H264VulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &hp->vp; + + if (!dec->session_params) { + err = vk_h264_create_params(avctx, &dec->session_params); + if (err < 0) + return err; + } + + /* Fill in main slot */ + dpb_slot_index = 0; + for (unsigned slot = 0; slot < H264_MAX_PICTURE_COUNT; slot++) { + if (pic == &h->DPB[slot]) { + dpb_slot_index = slot; + break; + } + } + + err = vk_h264_fill_pict(avctx, NULL, &vp->ref_slot, &vp->ref, + &hp->vkh264_ref, &hp->h264_ref, pic, 1, + h->DPB[dpb_slot_index].field_picture, + h->DPB[dpb_slot_index].reference, + dpb_slot_index); + if (err < 0) + return err; + + /* Fill in short-term references */ + for (int i = 0; i < h->short_ref_count; i++) { + dpb_slot_index = 0; + for (unsigned slot = 0; slot < H264_MAX_PICTURE_COUNT; slot++) { + if (h->short_ref[i] == &h->DPB[slot]) { + dpb_slot_index = slot; + break; + } + } + err = vk_h264_fill_pict(avctx, &hp->ref_src[i], &vp->ref_slots[i], + &vp->refs[i], &hp->vkh264_refs[i], + &hp->h264_refs[i], h->short_ref[i], 0, + h->DPB[dpb_slot_index].field_picture, + h->DPB[dpb_slot_index].reference, + dpb_slot_index); + if (err < 0) + return err; + } + + /* Fill in long-term refs */ + for (int r = 0, i = h->short_ref_count; r < H264_MAX_DPB_FRAMES && + i < h->short_ref_count + h->long_ref_count; r++) { + if (!h->long_ref[r]) + continue; + + dpb_slot_index = 0; + for (unsigned slot = 0; slot < 16; slot++) { + if (h->long_ref[r] == &h->DPB[slot]) { + dpb_slot_index = slot; + break; + } + } + err = vk_h264_fill_pict(avctx, &hp->ref_src[i], &vp->ref_slots[i], + &vp->refs[i], &hp->vkh264_refs[i], + &hp->h264_refs[i], h->long_ref[r], 0, + h->DPB[dpb_slot_index].field_picture, + h->DPB[dpb_slot_index].reference, + dpb_slot_index); + if (err < 0) + return err; + i++; + } + + hp->h264pic = (StdVideoDecodeH264PictureInfo) { + .seq_parameter_set_id = pic->pps->sps_id, + .pic_parameter_set_id = pic->pps->pps_id, + .frame_num = 0, /* Set later */ + .idr_pic_id = 0, /* Set later */ + .PicOrderCnt[0] = pic->field_poc[0], + .PicOrderCnt[1] = pic->field_poc[1], + .flags = (StdVideoDecodeH264PictureInfoFlags) { + .field_pic_flag = FIELD_PICTURE(h), + .is_intra = 1, /* Set later */ + .IdrPicFlag = h->picture_idr, + .bottom_field_flag = h->picture_structure != PICT_FRAME && + h->picture_structure & PICT_BOTTOM_FIELD, + .is_reference = h->nal_ref_idc != 0, + .complementary_field_pair = h->first_field && FIELD_PICTURE(h), + }, + }; + + hp->h264_pic_info = (VkVideoDecodeH264PictureInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_PICTURE_INFO_KHR, + .pStdPictureInfo = &hp->h264pic, + }; + + vp->decode_info = (VkVideoDecodeInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_INFO_KHR, + .pNext = &hp->h264_pic_info, + .flags = 0x0, + .pSetupReferenceSlot = &vp->ref_slot, + .referenceSlotCount = h->short_ref_count + h->long_ref_count, + .pReferenceSlots = vp->ref_slots, + .dstPictureResource = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->f->width, pic->f->height }, + .baseArrayLayer = 0, + .imageViewBinding = vp->img_view_out, + }, + }; + + return 0; +} + +static int vk_h264_decode_slice(AVCodecContext *avctx, + const uint8_t *data, + uint32_t size) +{ + const H264Context *h = avctx->priv_data; + const H264SliceContext *sl = &h->slice_ctx[0]; + H264VulkanDecodePicture *hp = h->cur_pic_ptr->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &hp->vp; + + int err = ff_vk_decode_add_slice(avctx, vp, data, size, 1, + &hp->h264_pic_info.sliceCount, + &hp->h264_pic_info.pSliceOffsets); + if (err < 0) + return err; + + hp->h264pic.frame_num = sl->frame_num; + hp->h264pic.idr_pic_id = sl->idr_pic_id; + + /* Frame is only intra of all slices are marked as intra */ + if (sl->slice_type != AV_PICTURE_TYPE_I && sl->slice_type != AV_PICTURE_TYPE_SI) + hp->h264pic.flags.is_intra = 0; + + return 0; +} + +static int vk_h264_end_frame(AVCodecContext *avctx) +{ + const H264Context *h = avctx->priv_data; + H264Picture *pic = h->cur_pic_ptr; + H264VulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodePicture *vp = &hp->vp; + FFVulkanDecodePicture *rvp[H264_MAX_PICTURE_COUNT] = { 0 }; + AVFrame *rav[H264_MAX_PICTURE_COUNT] = { 0 }; + + if (!hp->h264_pic_info.sliceCount) + return 0; + + if (!vp->slices_buf) + return AVERROR(EINVAL); + + if (!dec->session_params) { + int err = vk_h264_create_params(avctx, &dec->session_params); + if (err < 0) + return err; + + hp->h264pic.seq_parameter_set_id = pic->pps->sps_id; + hp->h264pic.pic_parameter_set_id = pic->pps->pps_id; + } + + for (int i = 0; i < vp->decode_info.referenceSlotCount; i++) { + H264Picture *rp = hp->ref_src[i]; + H264VulkanDecodePicture *rhp = rp->hwaccel_picture_private; + + rvp[i] = &rhp->vp; + rav[i] = hp->ref_src[i]->f; + } + + av_log(avctx, AV_LOG_VERBOSE, "Decoding frame, %"SIZE_SPECIFIER" bytes, %i slices\n", + vp->slices_size, hp->h264_pic_info.sliceCount); + + return ff_vk_decode_frame(avctx, pic->f, vp, rav, rvp); +} + +static void vk_h264_free_frame_priv(FFRefStructOpaque _hwctx, void *data) +{ + AVHWDeviceContext *hwctx = _hwctx.nc; + H264VulkanDecodePicture *hp = data; + + /* Free frame resources, this also destroys the session parameters. */ + ff_vk_decode_free_frame(hwctx, &hp->vp); +} + +const FFHWAccel ff_h264_vulkan_hwaccel = { + .p.name = "h264_vulkan", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_VULKAN, + .start_frame = &vk_h264_start_frame, + .decode_slice = &vk_h264_decode_slice, + .end_frame = &vk_h264_end_frame, + .free_frame_priv = &vk_h264_free_frame_priv, + .frame_priv_data_size = sizeof(H264VulkanDecodePicture), + .init = &ff_vk_decode_init, + .update_thread_context = &ff_vk_update_thread_context, + .decode_params = &ff_vk_params_invalidate, + .flush = &ff_vk_decode_flush, + .uninit = &ff_vk_decode_uninit, + .frame_params = &ff_vk_frame_params, + .priv_data_size = sizeof(FFVulkanDecodeContext), + .caps_internal = HWACCEL_CAP_ASYNC_SAFE | HWACCEL_CAP_THREAD_SAFE, +}; diff --git a/libavcodec/vulkan_hevc.c b/libavcodec/vulkan_hevc.c new file mode 100644 index 00000000000..239bff75e57 --- /dev/null +++ b/libavcodec/vulkan_hevc.c @@ -0,0 +1,944 @@ +/* + * 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 "hevcdec.h" +#include "hevc_data.h" +#include "hevc_ps.h" + +#include "vulkan_decode.h" + +const FFVulkanDecodeDescriptor ff_vk_dec_hevc_desc = { + .codec_id = AV_CODEC_ID_HEVC, + .decode_extension = FF_VK_EXT_VIDEO_DECODE_H265, + .decode_op = VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR, + .ext_props = { + .extensionName = VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_EXTENSION_NAME, + .specVersion = VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_SPEC_VERSION, + }, +}; + +typedef struct HEVCHeaderSPS { + StdVideoH265ScalingLists scaling; + StdVideoH265HrdParameters vui_header; + StdVideoH265SequenceParameterSetVui vui; + StdVideoH265ProfileTierLevel ptl; + StdVideoH265DecPicBufMgr dpbm; + StdVideoH265PredictorPaletteEntries pal; + StdVideoH265SubLayerHrdParameters nal_hdr[HEVC_MAX_SUB_LAYERS]; + StdVideoH265SubLayerHrdParameters vcl_hdr[HEVC_MAX_SUB_LAYERS]; + StdVideoH265ShortTermRefPicSet str[HEVC_MAX_SHORT_TERM_REF_PIC_SETS]; + StdVideoH265LongTermRefPicsSps ltr; +} HEVCHeaderSPS; + +typedef struct HEVCHeaderPPS { + StdVideoH265ScalingLists scaling; + StdVideoH265PredictorPaletteEntries pal; +} HEVCHeaderPPS; + +typedef struct HEVCHeaderVPSSet { + StdVideoH265SubLayerHrdParameters nal_hdr[HEVC_MAX_SUB_LAYERS]; + StdVideoH265SubLayerHrdParameters vcl_hdr[HEVC_MAX_SUB_LAYERS]; +} HEVCHeaderVPSSet; + +typedef struct HEVCHeaderVPS { + StdVideoH265ProfileTierLevel ptl; + StdVideoH265DecPicBufMgr dpbm; + StdVideoH265HrdParameters hdr[HEVC_MAX_LAYER_SETS]; + HEVCHeaderVPSSet *sls; +} HEVCHeaderVPS; + +typedef struct HEVCHeaderSet { + StdVideoH265SequenceParameterSet sps[HEVC_MAX_SPS_COUNT]; + HEVCHeaderSPS hsps[HEVC_MAX_SPS_COUNT]; + + StdVideoH265PictureParameterSet pps[HEVC_MAX_PPS_COUNT]; + HEVCHeaderPPS hpps[HEVC_MAX_PPS_COUNT]; + + StdVideoH265VideoParameterSet vps[HEVC_MAX_PPS_COUNT]; + HEVCHeaderVPS *hvps; +} HEVCHeaderSet; + +static int alloc_hevc_header_structs(FFVulkanDecodeContext *s, + int nb_vps, + const int vps_list_idx[HEVC_MAX_VPS_COUNT], + const HEVCVPS * const vps_list[HEVC_MAX_VPS_COUNT]) +{ + uint8_t *data_ptr; + HEVCHeaderSet *hdr; + + size_t buf_size = sizeof(HEVCHeaderSet) + nb_vps*sizeof(HEVCHeaderVPS); + for (int i = 0; i < nb_vps; i++) { + const HEVCVPS *vps = vps_list[vps_list_idx[i]]; + buf_size += sizeof(HEVCHeaderVPSSet)*vps->vps_num_hrd_parameters; + } + + if (buf_size > s->hevc_headers_size) { + av_freep(&s->hevc_headers); + s->hevc_headers_size = 0; + s->hevc_headers = av_mallocz(buf_size); + if (!s->hevc_headers) + return AVERROR(ENOMEM); + s->hevc_headers_size = buf_size; + } + + /* Setup struct pointers */ + hdr = s->hevc_headers; + data_ptr = (uint8_t *)hdr; + hdr->hvps = (HEVCHeaderVPS *)(data_ptr + sizeof(HEVCHeaderSet)); + data_ptr += sizeof(HEVCHeaderSet) + nb_vps*sizeof(HEVCHeaderVPS); + for (int i = 0; i < nb_vps; i++) { + const HEVCVPS *vps = vps_list[vps_list_idx[i]]; + hdr->hvps[i].sls = (HEVCHeaderVPSSet *)data_ptr; + data_ptr += sizeof(HEVCHeaderVPSSet)*vps->vps_num_hrd_parameters; + } + + return 0; +} + +typedef struct HEVCVulkanDecodePicture { + FFVulkanDecodePicture vp; + + /* Current picture */ + StdVideoDecodeH265ReferenceInfo h265_ref; + VkVideoDecodeH265DpbSlotInfoKHR vkh265_ref; + + /* Picture refs */ + HEVCFrame *ref_src [HEVC_MAX_REFS]; + StdVideoDecodeH265ReferenceInfo h265_refs [HEVC_MAX_REFS]; + VkVideoDecodeH265DpbSlotInfoKHR vkh265_refs[HEVC_MAX_REFS]; + + /* Current picture (contd.) */ + StdVideoDecodeH265PictureInfo h265pic; + VkVideoDecodeH265PictureInfoKHR h265_pic_info; +} HEVCVulkanDecodePicture; + +static int vk_hevc_fill_pict(AVCodecContext *avctx, HEVCFrame **ref_src, + VkVideoReferenceSlotInfoKHR *ref_slot, /* Main structure */ + VkVideoPictureResourceInfoKHR *ref, /* Goes in ^ */ + VkVideoDecodeH265DpbSlotInfoKHR *vkh265_ref, /* Goes in ^ */ + StdVideoDecodeH265ReferenceInfo *h265_ref, /* Goes in ^ */ + HEVCFrame *pic, int is_current, int pic_id) +{ + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + HEVCVulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vkpic = &hp->vp; + + int err = ff_vk_decode_prepare_frame(dec, pic->frame, vkpic, is_current, + dec->dedicated_dpb); + if (err < 0) + return err; + + *h265_ref = (StdVideoDecodeH265ReferenceInfo) { + .flags = (StdVideoDecodeH265ReferenceInfoFlags) { + .used_for_long_term_reference = pic->flags & HEVC_FRAME_FLAG_LONG_REF, + .unused_for_reference = 0, + }, + .PicOrderCntVal = pic->poc, + }; + + *vkh265_ref = (VkVideoDecodeH265DpbSlotInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_DPB_SLOT_INFO_KHR, + .pStdReferenceInfo = h265_ref, + }; + + *ref = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->frame->width, pic->frame->height }, + .baseArrayLayer = dec->layered_dpb ? pic_id : 0, + .imageViewBinding = vkpic->img_view_ref, + }; + + *ref_slot = (VkVideoReferenceSlotInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_REFERENCE_SLOT_INFO_KHR, + .pNext = vkh265_ref, + .slotIndex = pic_id, + .pPictureResource = ref, + }; + + if (ref_src) + *ref_src = pic; + + return 0; +} + +static StdVideoH265LevelIdc convert_to_vk_level_idc(int level_idc) +{ + switch (level_idc) { + case 10: return STD_VIDEO_H265_LEVEL_IDC_1_0; + case 20: return STD_VIDEO_H265_LEVEL_IDC_2_0; + case 21: return STD_VIDEO_H265_LEVEL_IDC_2_1; + case 30: return STD_VIDEO_H265_LEVEL_IDC_3_0; + case 31: return STD_VIDEO_H265_LEVEL_IDC_3_1; + case 40: return STD_VIDEO_H265_LEVEL_IDC_4_0; + case 41: return STD_VIDEO_H265_LEVEL_IDC_4_1; + case 50: return STD_VIDEO_H265_LEVEL_IDC_5_0; + case 51: return STD_VIDEO_H265_LEVEL_IDC_5_1; + case 60: return STD_VIDEO_H265_LEVEL_IDC_6_0; + case 61: return STD_VIDEO_H265_LEVEL_IDC_6_1; + default: + case 62: return STD_VIDEO_H265_LEVEL_IDC_6_2; + } +} + +static void copy_scaling_list(const ScalingList *sl, StdVideoH265ScalingLists *vksl) +{ + for (int i = 0; i < STD_VIDEO_H265_SCALING_LIST_4X4_NUM_LISTS; i++) { + for (int j = 0; j < STD_VIDEO_H265_SCALING_LIST_4X4_NUM_ELEMENTS; j++) { + uint8_t pos = 4 * ff_hevc_diag_scan4x4_y[j] + ff_hevc_diag_scan4x4_x[j]; + vksl->ScalingList4x4[i][j] = sl->sl[0][i][pos]; + } + } + + for (int i = 0; i < STD_VIDEO_H265_SCALING_LIST_8X8_NUM_LISTS; i++) { + for (int j = 0; j < STD_VIDEO_H265_SCALING_LIST_8X8_NUM_ELEMENTS; j++) { + uint8_t pos = 8 * ff_hevc_diag_scan8x8_y[j] + ff_hevc_diag_scan8x8_x[j]; + vksl->ScalingList8x8[i][j] = sl->sl[1][i][pos]; + } + } + + for (int i = 0; i < STD_VIDEO_H265_SCALING_LIST_16X16_NUM_LISTS; i++) { + for (int j = 0; j < STD_VIDEO_H265_SCALING_LIST_16X16_NUM_ELEMENTS; j++) { + uint8_t pos = 8 * ff_hevc_diag_scan8x8_y[j] + ff_hevc_diag_scan8x8_x[j]; + vksl->ScalingList16x16[i][j] = sl->sl[2][i][pos]; + } + } + + for (int i = 0; i < STD_VIDEO_H265_SCALING_LIST_32X32_NUM_LISTS; i++) { + for (int j = 0; j < STD_VIDEO_H265_SCALING_LIST_32X32_NUM_ELEMENTS; j++) { + uint8_t pos = 8 * ff_hevc_diag_scan8x8_y[j] + ff_hevc_diag_scan8x8_x[j]; + vksl->ScalingList32x32[i][j] = sl->sl[3][i * 3][pos]; + } + } + + memcpy(vksl->ScalingListDCCoef16x16, sl->sl_dc[0], + STD_VIDEO_H265_SCALING_LIST_16X16_NUM_LISTS * sizeof(*vksl->ScalingListDCCoef16x16)); + + for (int i = 0; i < STD_VIDEO_H265_SCALING_LIST_32X32_NUM_LISTS; i++) + vksl->ScalingListDCCoef32x32[i] = sl->sl_dc[1][i * 3]; +} + +static void set_sps(const HEVCSPS *sps, int sps_idx, + StdVideoH265ScalingLists *vksps_scaling, + StdVideoH265HrdParameters *vksps_vui_header, + StdVideoH265SequenceParameterSetVui *vksps_vui, + StdVideoH265SequenceParameterSet *vksps, + StdVideoH265SubLayerHrdParameters *slhdrnal, + StdVideoH265SubLayerHrdParameters *slhdrvcl, + StdVideoH265ProfileTierLevel *ptl, + StdVideoH265DecPicBufMgr *dpbm, + StdVideoH265PredictorPaletteEntries *pal, + StdVideoH265ShortTermRefPicSet *str, + StdVideoH265LongTermRefPicsSps *ltr) +{ + copy_scaling_list(&sps->scaling_list, vksps_scaling); + + *vksps_vui_header = (StdVideoH265HrdParameters) { + .flags = (StdVideoH265HrdFlags) { + .nal_hrd_parameters_present_flag = sps->hdr.nal_hrd_parameters_present_flag, + .vcl_hrd_parameters_present_flag = sps->hdr.vcl_hrd_parameters_present_flag, + .sub_pic_hrd_params_present_flag = sps->hdr.sub_pic_hrd_params_present_flag, + .sub_pic_cpb_params_in_pic_timing_sei_flag = sps->hdr.sub_pic_cpb_params_in_pic_timing_sei_flag, + .fixed_pic_rate_general_flag = sps->hdr.flags.fixed_pic_rate_general_flag, + .fixed_pic_rate_within_cvs_flag = sps->hdr.flags.fixed_pic_rate_within_cvs_flag, + .low_delay_hrd_flag = sps->hdr.flags.low_delay_hrd_flag, + }, + .tick_divisor_minus2 = sps->hdr.tick_divisor_minus2, + .du_cpb_removal_delay_increment_length_minus1 = sps->hdr.du_cpb_removal_delay_increment_length_minus1, + .dpb_output_delay_du_length_minus1 = sps->hdr.dpb_output_delay_du_length_minus1, + .bit_rate_scale = sps->hdr.bit_rate_scale, + .cpb_size_scale = sps->hdr.cpb_size_scale, + .cpb_size_du_scale = sps->hdr.cpb_size_du_scale, + .initial_cpb_removal_delay_length_minus1 = sps->hdr.initial_cpb_removal_delay_length_minus1, + .au_cpb_removal_delay_length_minus1 = sps->hdr.au_cpb_removal_delay_length_minus1, + .dpb_output_delay_length_minus1 = sps->hdr.dpb_output_delay_length_minus1, + /* Reserved - 3*16 bits */ + .pSubLayerHrdParametersNal = slhdrnal, + .pSubLayerHrdParametersVcl = slhdrvcl, + }; + + memcpy(vksps_vui_header->cpb_cnt_minus1, sps->hdr.cpb_cnt_minus1, + STD_VIDEO_H265_SUBLAYERS_LIST_SIZE*sizeof(*vksps_vui_header->cpb_cnt_minus1)); + memcpy(vksps_vui_header->elemental_duration_in_tc_minus1, sps->hdr.elemental_duration_in_tc_minus1, + STD_VIDEO_H265_SUBLAYERS_LIST_SIZE*sizeof(*vksps_vui_header->elemental_duration_in_tc_minus1)); + + memcpy(slhdrnal, sps->hdr.nal_params, HEVC_MAX_SUB_LAYERS*sizeof(*slhdrnal)); + memcpy(slhdrvcl, sps->hdr.vcl_params, HEVC_MAX_SUB_LAYERS*sizeof(*slhdrvcl)); + + *vksps_vui = (StdVideoH265SequenceParameterSetVui) { + .flags = (StdVideoH265SpsVuiFlags) { + .aspect_ratio_info_present_flag = sps->vui.common.aspect_ratio_info_present_flag, + .overscan_info_present_flag = sps->vui.common.overscan_info_present_flag, + .overscan_appropriate_flag = sps->vui.common.overscan_appropriate_flag, + .video_signal_type_present_flag = sps->vui.common.video_signal_type_present_flag, + .video_full_range_flag = sps->vui.common.video_full_range_flag, + .colour_description_present_flag = sps->vui.common.colour_description_present_flag, + .chroma_loc_info_present_flag = sps->vui.common.chroma_loc_info_present_flag, + .neutral_chroma_indication_flag = sps->vui.neutra_chroma_indication_flag, + .field_seq_flag = sps->vui.field_seq_flag, + .frame_field_info_present_flag = sps->vui.frame_field_info_present_flag, + .default_display_window_flag = sps->vui.default_display_window_flag, + .vui_timing_info_present_flag = sps->vui.vui_timing_info_present_flag, + .vui_poc_proportional_to_timing_flag = sps->vui.vui_poc_proportional_to_timing_flag, + .vui_hrd_parameters_present_flag = sps->vui.vui_hrd_parameters_present_flag, + .bitstream_restriction_flag = sps->vui.bitstream_restriction_flag, + .tiles_fixed_structure_flag = sps->vui.tiles_fixed_structure_flag, + .motion_vectors_over_pic_boundaries_flag = sps->vui.motion_vectors_over_pic_boundaries_flag, + .restricted_ref_pic_lists_flag = sps->vui.restricted_ref_pic_lists_flag, + }, + .aspect_ratio_idc = sps->vui.common.aspect_ratio_idc, + .sar_width = sps->vui.common.sar.num, + .sar_height = sps->vui.common.sar.den, + .video_format = sps->vui.common.video_format, + .colour_primaries = sps->vui.common.colour_primaries, + .transfer_characteristics = sps->vui.common.transfer_characteristics, + .matrix_coeffs = sps->vui.common.matrix_coeffs, + .chroma_sample_loc_type_top_field = sps->vui.common.chroma_sample_loc_type_top_field, + .chroma_sample_loc_type_bottom_field = sps->vui.common.chroma_sample_loc_type_bottom_field, + /* Reserved */ + /* Reserved */ + .def_disp_win_left_offset = sps->vui.def_disp_win.left_offset, + .def_disp_win_right_offset = sps->vui.def_disp_win.right_offset, + .def_disp_win_top_offset = sps->vui.def_disp_win.top_offset, + .def_disp_win_bottom_offset = sps->vui.def_disp_win.bottom_offset, + .vui_num_units_in_tick = sps->vui.vui_num_units_in_tick, + .vui_time_scale = sps->vui.vui_time_scale, + .vui_num_ticks_poc_diff_one_minus1 = sps->vui.vui_num_ticks_poc_diff_one_minus1, + .min_spatial_segmentation_idc = sps->vui.min_spatial_segmentation_idc, + .max_bytes_per_pic_denom = sps->vui.max_bytes_per_pic_denom, + .max_bits_per_min_cu_denom = sps->vui.max_bits_per_min_cu_denom, + .log2_max_mv_length_horizontal = sps->vui.log2_max_mv_length_horizontal, + .log2_max_mv_length_vertical = sps->vui.log2_max_mv_length_vertical, + .pHrdParameters = vksps_vui_header, + }; + + *ptl = (StdVideoH265ProfileTierLevel) { + .flags = (StdVideoH265ProfileTierLevelFlags) { + .general_tier_flag = sps->ptl.general_ptl.tier_flag, + .general_progressive_source_flag = sps->ptl.general_ptl.progressive_source_flag, + .general_interlaced_source_flag = sps->ptl.general_ptl.interlaced_source_flag, + .general_non_packed_constraint_flag = sps->ptl.general_ptl.non_packed_constraint_flag, + .general_frame_only_constraint_flag = sps->ptl.general_ptl.frame_only_constraint_flag, + }, + .general_profile_idc = sps->ptl.general_ptl.profile_idc, + .general_level_idc = convert_to_vk_level_idc(sps->ptl.general_ptl.level_idc), + }; + + for (int i = 0; i < sps->max_sub_layers; i++) { + dpbm->max_latency_increase_plus1[i] = sps->temporal_layer[i].max_latency_increase + 1; + dpbm->max_dec_pic_buffering_minus1[i] = sps->temporal_layer[i].max_dec_pic_buffering - 1; + dpbm->max_num_reorder_pics[i] = sps->temporal_layer[i].num_reorder_pics; + } + + for (int i = 0; i < (sps->chroma_format_idc ? 3 : 1); i++) + for (int j = 0; j < sps->sps_num_palette_predictor_initializers; j++) + pal->PredictorPaletteEntries[i][j] = sps->sps_palette_predictor_initializer[i][j]; + + for (int i = 0; i < sps->nb_st_rps; i++) { + str[i] = (StdVideoH265ShortTermRefPicSet) { + .flags = (StdVideoH265ShortTermRefPicSetFlags) { + .inter_ref_pic_set_prediction_flag = sps->st_rps[i].rps_predict, + .delta_rps_sign = sps->st_rps[i].delta_rps_sign, + }, + .delta_idx_minus1 = sps->st_rps[i].delta_idx - 1, + .use_delta_flag = sps->st_rps[i].use_delta_flag, + .abs_delta_rps_minus1 = sps->st_rps[i].abs_delta_rps - 1, + .used_by_curr_pic_flag = 0x0, + .used_by_curr_pic_s0_flag = 0x0, + .used_by_curr_pic_s1_flag = 0x0, + /* Reserved */ + /* Reserved */ + /* Reserved */ + .num_negative_pics = sps->st_rps[i].num_negative_pics, + .num_positive_pics = sps->st_rps[i].num_delta_pocs - sps->st_rps[i].num_negative_pics, + }; + + /* NOTE: This is the predicted, and *reordered* version. + * Probably incorrect, but the spec doesn't say which version to use. */ + for (int j = 0; j < sps->st_rps[i].num_delta_pocs; j++) + str[i].used_by_curr_pic_flag |= sps->st_rps[i].used[j] << j; + + for (int j = 0; j < str[i].num_negative_pics; j++) { + str[i].delta_poc_s0_minus1[j] = sps->st_rps[i].delta_poc_s0[j] - 1; + str[i].used_by_curr_pic_s0_flag |= sps->st_rps[i].used[j] << j; + } + + for (int j = 0; j < str[i].num_positive_pics; j++) { + str[i].delta_poc_s1_minus1[j] = sps->st_rps[i].delta_poc_s1[j] - 1; + str[i].used_by_curr_pic_s0_flag |= sps->st_rps[i].used[str[i].num_negative_pics + j] << j; + } + } + + *ltr = (StdVideoH265LongTermRefPicsSps) { + .used_by_curr_pic_lt_sps_flag = 0x0, + }; + + for (int i = 0; i < sps->num_long_term_ref_pics_sps; i++) { + ltr->used_by_curr_pic_lt_sps_flag |= sps->used_by_curr_pic_lt_sps_flag[i] << i; + ltr->lt_ref_pic_poc_lsb_sps[i] = sps->lt_ref_pic_poc_lsb_sps[i]; + } + + *vksps = (StdVideoH265SequenceParameterSet) { + .flags = (StdVideoH265SpsFlags) { + .sps_temporal_id_nesting_flag = sps->temporal_id_nesting_flag, + .separate_colour_plane_flag = sps->separate_colour_plane_flag, + .conformance_window_flag = sps->conformance_window_flag, + .sps_sub_layer_ordering_info_present_flag = sps->sublayer_ordering_info_flag, + .scaling_list_enabled_flag = sps->scaling_list_enable_flag, + .sps_scaling_list_data_present_flag = sps->scaling_list_enable_flag, + .amp_enabled_flag = sps->amp_enabled_flag, + .sample_adaptive_offset_enabled_flag = sps->sao_enabled, + .pcm_enabled_flag = sps->pcm_enabled_flag, + .pcm_loop_filter_disabled_flag = sps->pcm.loop_filter_disable_flag, + .long_term_ref_pics_present_flag = sps->long_term_ref_pics_present_flag, + .sps_temporal_mvp_enabled_flag = sps->sps_temporal_mvp_enabled_flag, + .strong_intra_smoothing_enabled_flag = sps->sps_strong_intra_smoothing_enable_flag, + .vui_parameters_present_flag = sps->vui_present, + .sps_extension_present_flag = sps->sps_extension_present_flag, + .sps_range_extension_flag = sps->sps_range_extension_flag, + .transform_skip_rotation_enabled_flag = sps->transform_skip_rotation_enabled_flag, + .transform_skip_context_enabled_flag = sps->transform_skip_context_enabled_flag, + .implicit_rdpcm_enabled_flag = sps->implicit_rdpcm_enabled_flag, + .explicit_rdpcm_enabled_flag = sps->explicit_rdpcm_enabled_flag, + .extended_precision_processing_flag = sps->extended_precision_processing_flag, + .intra_smoothing_disabled_flag = sps->intra_smoothing_disabled_flag, + .high_precision_offsets_enabled_flag = sps->high_precision_offsets_enabled_flag, + .persistent_rice_adaptation_enabled_flag = sps->persistent_rice_adaptation_enabled_flag, + .cabac_bypass_alignment_enabled_flag = sps->cabac_bypass_alignment_enabled_flag, + .sps_scc_extension_flag = sps->sps_scc_extension_flag, + .sps_curr_pic_ref_enabled_flag = sps->sps_curr_pic_ref_enabled_flag, + .palette_mode_enabled_flag = sps->palette_mode_enabled_flag, + .sps_palette_predictor_initializers_present_flag = sps->sps_palette_predictor_initializers_present_flag, + .intra_boundary_filtering_disabled_flag = sps->intra_boundary_filtering_disabled_flag, + }, + .chroma_format_idc = sps->chroma_format_idc, + .pic_width_in_luma_samples = sps->width, + .pic_height_in_luma_samples = sps->height, + .sps_video_parameter_set_id = sps->vps_id, + .sps_max_sub_layers_minus1 = sps->max_sub_layers - 1, + .sps_seq_parameter_set_id = sps_idx, + .bit_depth_luma_minus8 = sps->bit_depth - 8, + .bit_depth_chroma_minus8 = sps->bit_depth_chroma - 8, + .log2_max_pic_order_cnt_lsb_minus4 = sps->log2_max_poc_lsb - 4, + .log2_min_luma_coding_block_size_minus3 = sps->log2_min_cb_size - 3, + .log2_diff_max_min_luma_coding_block_size = sps->log2_diff_max_min_coding_block_size, + .log2_min_luma_transform_block_size_minus2 = sps->log2_min_tb_size - 2, + .log2_diff_max_min_luma_transform_block_size = sps->log2_diff_max_min_transform_block_size, + .max_transform_hierarchy_depth_inter = sps->max_transform_hierarchy_depth_inter, + .max_transform_hierarchy_depth_intra = sps->max_transform_hierarchy_depth_intra, + .num_short_term_ref_pic_sets = sps->nb_st_rps, + .num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps, + .pcm_sample_bit_depth_luma_minus1 = sps->pcm.bit_depth - 1, + .pcm_sample_bit_depth_chroma_minus1 = sps->pcm.bit_depth_chroma - 1, + .log2_min_pcm_luma_coding_block_size_minus3 = sps->pcm.log2_min_pcm_cb_size - 3, + .log2_diff_max_min_pcm_luma_coding_block_size = sps->pcm.log2_max_pcm_cb_size - sps->pcm.log2_min_pcm_cb_size, + /* Reserved */ + /* Reserved */ + .palette_max_size = sps->palette_max_size, + .delta_palette_max_predictor_size = sps->delta_palette_max_predictor_size, + .motion_vector_resolution_control_idc = sps->motion_vector_resolution_control_idc, + .sps_num_palette_predictor_initializers_minus1 = sps->sps_num_palette_predictor_initializers - 1, + .conf_win_left_offset = sps->pic_conf_win.left_offset, + .conf_win_right_offset = sps->pic_conf_win.right_offset, + .conf_win_top_offset = sps->pic_conf_win.top_offset, + .conf_win_bottom_offset = sps->pic_conf_win.bottom_offset, + .pProfileTierLevel = ptl, + .pDecPicBufMgr = dpbm, + .pScalingLists = vksps_scaling, + .pShortTermRefPicSet = str, + .pLongTermRefPicsSps = ltr, + .pSequenceParameterSetVui = vksps_vui, + .pPredictorPaletteEntries = pal, + }; +} + +static void set_pps(const HEVCPPS *pps, const HEVCSPS *sps, + StdVideoH265ScalingLists *vkpps_scaling, + StdVideoH265PictureParameterSet *vkpps, + StdVideoH265PredictorPaletteEntries *pal) +{ + copy_scaling_list(&pps->scaling_list, vkpps_scaling); + + *vkpps = (StdVideoH265PictureParameterSet) { + .flags = (StdVideoH265PpsFlags) { + .dependent_slice_segments_enabled_flag = pps->dependent_slice_segments_enabled_flag, + .output_flag_present_flag = pps->output_flag_present_flag, + .sign_data_hiding_enabled_flag = pps->sign_data_hiding_flag, + .cabac_init_present_flag = pps->cabac_init_present_flag, + .constrained_intra_pred_flag = pps->constrained_intra_pred_flag, + .transform_skip_enabled_flag = pps->transform_skip_enabled_flag, + .cu_qp_delta_enabled_flag = pps->cu_qp_delta_enabled_flag, + .pps_slice_chroma_qp_offsets_present_flag = pps->pic_slice_level_chroma_qp_offsets_present_flag, + .weighted_pred_flag = pps->weighted_pred_flag, + .weighted_bipred_flag = pps->weighted_bipred_flag, + .transquant_bypass_enabled_flag = pps->transquant_bypass_enable_flag, + .tiles_enabled_flag = pps->tiles_enabled_flag, + .entropy_coding_sync_enabled_flag = pps->entropy_coding_sync_enabled_flag, + .uniform_spacing_flag = pps->uniform_spacing_flag, + .loop_filter_across_tiles_enabled_flag = pps->loop_filter_across_tiles_enabled_flag, + .pps_loop_filter_across_slices_enabled_flag = pps->seq_loop_filter_across_slices_enabled_flag, + .deblocking_filter_control_present_flag = pps->deblocking_filter_control_present_flag, + .deblocking_filter_override_enabled_flag = pps->deblocking_filter_override_enabled_flag, + .pps_deblocking_filter_disabled_flag = pps->disable_dbf, + .pps_scaling_list_data_present_flag = pps->scaling_list_data_present_flag, + .lists_modification_present_flag = pps->lists_modification_present_flag, + .slice_segment_header_extension_present_flag = pps->slice_header_extension_present_flag, + .pps_extension_present_flag = pps->pps_extension_present_flag, + .cross_component_prediction_enabled_flag = pps->cross_component_prediction_enabled_flag, + .chroma_qp_offset_list_enabled_flag = pps->chroma_qp_offset_list_enabled_flag, + .pps_curr_pic_ref_enabled_flag = pps->pps_curr_pic_ref_enabled_flag, + .residual_adaptive_colour_transform_enabled_flag = pps->residual_adaptive_colour_transform_enabled_flag, + .pps_slice_act_qp_offsets_present_flag = pps->pps_slice_act_qp_offsets_present_flag, + .pps_palette_predictor_initializers_present_flag = pps->pps_palette_predictor_initializers_present_flag, + .monochrome_palette_flag = pps->monochrome_palette_flag, + .pps_range_extension_flag = pps->pps_range_extensions_flag, + }, + .pps_pic_parameter_set_id = pps->pps_id, + .pps_seq_parameter_set_id = pps->sps_id, + .sps_video_parameter_set_id = sps->vps_id, + .num_extra_slice_header_bits = pps->num_extra_slice_header_bits, + .num_ref_idx_l0_default_active_minus1 = pps->num_ref_idx_l0_default_active - 1, + .num_ref_idx_l1_default_active_minus1 = pps->num_ref_idx_l1_default_active - 1, + .init_qp_minus26 = pps->pic_init_qp_minus26, + .diff_cu_qp_delta_depth = pps->diff_cu_qp_delta_depth, + .pps_cb_qp_offset = pps->cb_qp_offset, + .pps_cr_qp_offset = pps->cr_qp_offset, + .pps_beta_offset_div2 = pps->beta_offset >> 1, + .pps_tc_offset_div2 = pps->tc_offset >> 1, + .log2_parallel_merge_level_minus2 = pps->log2_parallel_merge_level - 2, + .log2_max_transform_skip_block_size_minus2 = pps->log2_max_transform_skip_block_size - 2, + .diff_cu_chroma_qp_offset_depth = pps->diff_cu_chroma_qp_offset_depth, + .chroma_qp_offset_list_len_minus1 = pps->chroma_qp_offset_list_len_minus1, + .log2_sao_offset_scale_luma = pps->log2_sao_offset_scale_luma, + .log2_sao_offset_scale_chroma = pps->log2_sao_offset_scale_chroma, + .pps_act_y_qp_offset_plus5 = pps->pps_act_y_qp_offset + 5, + .pps_act_cb_qp_offset_plus5 = pps->pps_act_cb_qp_offset + 5, + .pps_act_cr_qp_offset_plus3 = pps->pps_act_cr_qp_offset + 3, + .pps_num_palette_predictor_initializers = pps->pps_num_palette_predictor_initializers, + .luma_bit_depth_entry_minus8 = pps->luma_bit_depth_entry - 8, + .chroma_bit_depth_entry_minus8 = pps->chroma_bit_depth_entry - 8, + .num_tile_columns_minus1 = pps->num_tile_columns - 1, + .num_tile_rows_minus1 = pps->num_tile_rows - 1, + .pScalingLists = vkpps_scaling, + .pPredictorPaletteEntries = pal, + }; + + for (int i = 0; i < (pps->monochrome_palette_flag ? 1 : 3); i++) { + for (int j = 0; j < pps->pps_num_palette_predictor_initializers; j++) + pal->PredictorPaletteEntries[i][j] = pps->pps_palette_predictor_initializer[i][j]; + } + + for (int i = 0; i < pps->num_tile_columns - 1; i++) + vkpps->column_width_minus1[i] = pps->column_width[i] - 1; + + for (int i = 0; i < pps->num_tile_rows - 1; i++) + vkpps->row_height_minus1[i] = pps->row_height[i] - 1; + + for (int i = 0; i <= pps->chroma_qp_offset_list_len_minus1; i++) { + vkpps->cb_qp_offset_list[i] = pps->cb_qp_offset_list[i]; + vkpps->cr_qp_offset_list[i] = pps->cr_qp_offset_list[i]; + } +} + +static void set_vps(const HEVCVPS *vps, + StdVideoH265VideoParameterSet *vkvps, + StdVideoH265ProfileTierLevel *ptl, + StdVideoH265DecPicBufMgr *dpbm, + StdVideoH265HrdParameters *sls_hdr, + HEVCHeaderVPSSet sls[]) +{ + for (int i = 0; i < vps->vps_num_hrd_parameters; i++) { + const HEVCHdrParams *src = &vps->hdr[i]; + + sls_hdr[i] = (StdVideoH265HrdParameters) { + .flags = (StdVideoH265HrdFlags) { + .nal_hrd_parameters_present_flag = src->nal_hrd_parameters_present_flag, + .vcl_hrd_parameters_present_flag = src->vcl_hrd_parameters_present_flag, + .sub_pic_hrd_params_present_flag = src->sub_pic_hrd_params_present_flag, + .sub_pic_cpb_params_in_pic_timing_sei_flag = src->sub_pic_cpb_params_in_pic_timing_sei_flag, + .fixed_pic_rate_general_flag = src->flags.fixed_pic_rate_general_flag, + .fixed_pic_rate_within_cvs_flag = src->flags.fixed_pic_rate_within_cvs_flag, + .low_delay_hrd_flag = src->flags.low_delay_hrd_flag, + }, + .tick_divisor_minus2 = src->tick_divisor_minus2, + .du_cpb_removal_delay_increment_length_minus1 = src->du_cpb_removal_delay_increment_length_minus1, + .dpb_output_delay_du_length_minus1 = src->dpb_output_delay_du_length_minus1, + .bit_rate_scale = src->bit_rate_scale, + .cpb_size_scale = src->cpb_size_scale, + .cpb_size_du_scale = src->cpb_size_du_scale, + .initial_cpb_removal_delay_length_minus1 = src->initial_cpb_removal_delay_length_minus1, + .au_cpb_removal_delay_length_minus1 = src->au_cpb_removal_delay_length_minus1, + .dpb_output_delay_length_minus1 = src->dpb_output_delay_length_minus1, + /* Reserved - 3*16 bits */ + .pSubLayerHrdParametersNal = sls[i].nal_hdr, + .pSubLayerHrdParametersVcl = sls[i].vcl_hdr, + }; + + memcpy(sls_hdr[i].cpb_cnt_minus1, src->cpb_cnt_minus1, + STD_VIDEO_H265_SUBLAYERS_LIST_SIZE*sizeof(*sls_hdr[i].cpb_cnt_minus1)); + memcpy(sls_hdr[i].elemental_duration_in_tc_minus1, src->elemental_duration_in_tc_minus1, + STD_VIDEO_H265_SUBLAYERS_LIST_SIZE*sizeof(*sls_hdr[i].elemental_duration_in_tc_minus1)); + + memcpy(sls[i].nal_hdr, src->nal_params, HEVC_MAX_SUB_LAYERS*sizeof(*sls[i].nal_hdr)); + memcpy(sls[i].vcl_hdr, src->vcl_params, HEVC_MAX_SUB_LAYERS*sizeof(*sls[i].vcl_hdr)); + } + + *ptl = (StdVideoH265ProfileTierLevel) { + .flags = (StdVideoH265ProfileTierLevelFlags) { + .general_tier_flag = vps->ptl.general_ptl.tier_flag, + .general_progressive_source_flag = vps->ptl.general_ptl.progressive_source_flag, + .general_interlaced_source_flag = vps->ptl.general_ptl.interlaced_source_flag, + .general_non_packed_constraint_flag = vps->ptl.general_ptl.non_packed_constraint_flag, + .general_frame_only_constraint_flag = vps->ptl.general_ptl.frame_only_constraint_flag, + }, + .general_profile_idc = vps->ptl.general_ptl.profile_idc, + .general_level_idc = convert_to_vk_level_idc(vps->ptl.general_ptl.level_idc), + }; + + for (int i = 0; i < vps->vps_max_sub_layers; i++) { + dpbm->max_latency_increase_plus1[i] = vps->vps_max_latency_increase[i] + 1; + dpbm->max_dec_pic_buffering_minus1[i] = vps->vps_max_dec_pic_buffering[i] - 1; + dpbm->max_num_reorder_pics[i] = vps->vps_num_reorder_pics[i]; + } + + *vkvps = (StdVideoH265VideoParameterSet) { + .flags = (StdVideoH265VpsFlags) { + .vps_temporal_id_nesting_flag = vps->vps_temporal_id_nesting_flag, + .vps_sub_layer_ordering_info_present_flag = vps->vps_sub_layer_ordering_info_present_flag, + .vps_timing_info_present_flag = vps->vps_timing_info_present_flag, + .vps_poc_proportional_to_timing_flag = vps->vps_poc_proportional_to_timing_flag, + }, + .vps_video_parameter_set_id = vps->vps_id, + .vps_max_sub_layers_minus1 = vps->vps_max_sub_layers - 1, + /* Reserved */ + /* Reserved */ + .vps_num_units_in_tick = vps->vps_num_units_in_tick, + .vps_time_scale = vps->vps_time_scale, + .vps_num_ticks_poc_diff_one_minus1 = vps->vps_num_ticks_poc_diff_one - 1, + /* Reserved */ + .pDecPicBufMgr = dpbm, + .pHrdParameters = sls_hdr, + .pProfileTierLevel = ptl, + }; +} + +static int vk_hevc_create_params(AVCodecContext *avctx, AVBufferRef **buf) +{ + int err; + const HEVCContext *h = avctx->priv_data; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + FFVulkanDecodeShared *ctx = dec->shared_ctx; + + VkVideoDecodeH265SessionParametersAddInfoKHR h265_params_info = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_KHR, + .stdSPSCount = 0, + .stdPPSCount = 0, + .stdVPSCount = 0, + }; + VkVideoDecodeH265SessionParametersCreateInfoKHR h265_params = { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pParametersAddInfo = &h265_params_info, + }; + VkVideoSessionParametersCreateInfoKHR session_params_create = { + .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_PARAMETERS_CREATE_INFO_KHR, + .pNext = &h265_params, + .videoSession = ctx->common.session, + .videoSessionParametersTemplate = VK_NULL_HANDLE, + }; + + HEVCHeaderSet *hdr; + int nb_vps = 0; + int vps_list_idx[HEVC_MAX_VPS_COUNT]; + + for (int i = 0; i < HEVC_MAX_VPS_COUNT; i++) + if (h->ps.vps_list[i]) + vps_list_idx[nb_vps++] = i; + + err = alloc_hevc_header_structs(dec, nb_vps, vps_list_idx, h->ps.vps_list); + if (err < 0) + return err; + + hdr = dec->hevc_headers; + + h265_params_info.pStdSPSs = hdr->sps; + h265_params_info.pStdPPSs = hdr->pps; + h265_params_info.pStdVPSs = hdr->vps; + + /* SPS list */ + for (int i = 0; i < HEVC_MAX_SPS_COUNT; i++) { + if (h->ps.sps_list[i]) { + const HEVCSPS *sps_l = h->ps.sps_list[i]; + int idx = h265_params_info.stdSPSCount++; + set_sps(sps_l, i, &hdr->hsps[idx].scaling, &hdr->hsps[idx].vui_header, + &hdr->hsps[idx].vui, &hdr->sps[idx], hdr->hsps[idx].nal_hdr, + hdr->hsps[idx].vcl_hdr, &hdr->hsps[idx].ptl, &hdr->hsps[idx].dpbm, + &hdr->hsps[idx].pal, hdr->hsps[idx].str, &hdr->hsps[idx].ltr); + } + } + + /* PPS list */ + for (int i = 0; i < HEVC_MAX_PPS_COUNT; i++) { + if (h->ps.pps_list[i]) { + const HEVCPPS *pps_l = h->ps.pps_list[i]; + const HEVCSPS *sps_l = h->ps.sps_list[pps_l->sps_id]; + int idx = h265_params_info.stdPPSCount++; + set_pps(pps_l, sps_l, &hdr->hpps[idx].scaling, + &hdr->pps[idx], &hdr->hpps[idx].pal); + } + } + + /* VPS list */ + for (int i = 0; i < nb_vps; i++) { + const HEVCVPS *vps_l = h->ps.vps_list[vps_list_idx[i]]; + set_vps(vps_l, &hdr->vps[i], &hdr->hvps[i].ptl, &hdr->hvps[i].dpbm, + hdr->hvps[i].hdr, hdr->hvps[i].sls); + h265_params_info.stdVPSCount++; + } + + h265_params.maxStdSPSCount = h265_params_info.stdSPSCount; + h265_params.maxStdPPSCount = h265_params_info.stdPPSCount; + h265_params.maxStdVPSCount = h265_params_info.stdVPSCount; + + err = ff_vk_decode_create_params(buf, avctx, ctx, &session_params_create); + if (err < 0) + return err; + + av_log(avctx, AV_LOG_DEBUG, "Created frame parameters: %i SPS %i PPS %i VPS\n", + h265_params_info.stdSPSCount, h265_params_info.stdPPSCount, + h265_params_info.stdVPSCount); + + return 0; +} + +static int vk_hevc_start_frame(AVCodecContext *avctx, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + int err; + HEVCContext *h = avctx->priv_data; + HEVCFrame *pic = h->ref; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + HEVCVulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &hp->vp; + const HEVCSPS *sps = h->ps.sps; + const HEVCPPS *pps = h->ps.pps; + int nb_refs = 0; + + if (!dec->session_params) { + err = vk_hevc_create_params(avctx, &dec->session_params); + if (err < 0) + return err; + } + + hp->h265pic = (StdVideoDecodeH265PictureInfo) { + .flags = (StdVideoDecodeH265PictureInfoFlags) { + .IrapPicFlag = IS_IRAP(h), + .IdrPicFlag = IS_IDR(h), + .IsReference = h->nal_unit_type < 16 ? h->nal_unit_type & 1 : 1, + .short_term_ref_pic_set_sps_flag = h->sh.short_term_ref_pic_set_sps_flag, + }, + .sps_video_parameter_set_id = sps->vps_id, + .pps_seq_parameter_set_id = pps->sps_id, + .pps_pic_parameter_set_id = pps->pps_id, + .NumDeltaPocsOfRefRpsIdx = h->sh.short_term_rps ? h->sh.short_term_rps->rps_idx_num_delta_pocs : 0, + .PicOrderCntVal = h->poc, + .NumBitsForSTRefPicSetInSlice = !h->sh.short_term_ref_pic_set_sps_flag ? + h->sh.short_term_ref_pic_set_size : 0, + }; + + /* Fill in references */ + for (int i = 0; i < FF_ARRAY_ELEMS(h->DPB); i++) { + const HEVCFrame *ref = &h->DPB[i]; + int idx = nb_refs; + + if (!(ref->flags & (HEVC_FRAME_FLAG_SHORT_REF | HEVC_FRAME_FLAG_LONG_REF))) + continue; + + if (ref == pic) { + err = vk_hevc_fill_pict(avctx, NULL, &vp->ref_slot, &vp->ref, + &hp->vkh265_ref, &hp->h265_ref, pic, 1, i); + if (err < 0) + return err; + + continue; + } + + err = vk_hevc_fill_pict(avctx, &hp->ref_src[idx], &vp->ref_slots[idx], + &vp->refs[idx], &hp->vkh265_refs[idx], + &hp->h265_refs[idx], (HEVCFrame *)ref, 0, i); + if (err < 0) + return err; + + nb_refs++; + } + + memset(hp->h265pic.RefPicSetStCurrBefore, 0xff, 8); + for (int i = 0; i < h->rps[ST_CURR_BEF].nb_refs; i++) { + HEVCFrame *frame = h->rps[ST_CURR_BEF].ref[i]; + for (int j = 0; j < FF_ARRAY_ELEMS(h->DPB); j++) { + const HEVCFrame *ref = &h->DPB[j]; + if (ref == frame) { + hp->h265pic.RefPicSetStCurrBefore[i] = j; + break; + } + } + } + memset(hp->h265pic.RefPicSetStCurrAfter, 0xff, 8); + for (int i = 0; i < h->rps[ST_CURR_AFT].nb_refs; i++) { + HEVCFrame *frame = h->rps[ST_CURR_AFT].ref[i]; + for (int j = 0; j < FF_ARRAY_ELEMS(h->DPB); j++) { + const HEVCFrame *ref = &h->DPB[j]; + if (ref == frame) { + hp->h265pic.RefPicSetStCurrAfter[i] = j; + break; + } + } + } + memset(hp->h265pic.RefPicSetLtCurr, 0xff, 8); + for (int i = 0; i < h->rps[LT_CURR].nb_refs; i++) { + HEVCFrame *frame = h->rps[LT_CURR].ref[i]; + for (int j = 0; j < FF_ARRAY_ELEMS(h->DPB); j++) { + const HEVCFrame *ref = &h->DPB[j]; + if (ref == frame) { + hp->h265pic.RefPicSetLtCurr[i] = j; + break; + } + } + } + + hp->h265_pic_info = (VkVideoDecodeH265PictureInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_PICTURE_INFO_KHR, + .pStdPictureInfo = &hp->h265pic, + .sliceSegmentCount = 0, + }; + + vp->decode_info = (VkVideoDecodeInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_DECODE_INFO_KHR, + .pNext = &hp->h265_pic_info, + .flags = 0x0, + .pSetupReferenceSlot = &vp->ref_slot, + .referenceSlotCount = nb_refs, + .pReferenceSlots = vp->ref_slots, + .dstPictureResource = (VkVideoPictureResourceInfoKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_PICTURE_RESOURCE_INFO_KHR, + .codedOffset = (VkOffset2D){ 0, 0 }, + .codedExtent = (VkExtent2D){ pic->frame->width, pic->frame->height }, + .baseArrayLayer = 0, + .imageViewBinding = vp->img_view_out, + }, + }; + + return 0; +} + +static int vk_hevc_decode_slice(AVCodecContext *avctx, + const uint8_t *data, + uint32_t size) +{ + const HEVCContext *h = avctx->priv_data; + HEVCVulkanDecodePicture *hp = h->ref->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &hp->vp; + + int err = ff_vk_decode_add_slice(avctx, vp, data, size, 1, + &hp->h265_pic_info.sliceSegmentCount, + &hp->h265_pic_info.pSliceSegmentOffsets); + if (err < 0) + return err; + + return 0; +} + +static int vk_hevc_end_frame(AVCodecContext *avctx) +{ + const HEVCContext *h = avctx->priv_data; + FFVulkanDecodeContext *dec = avctx->internal->hwaccel_priv_data; + HEVCFrame *pic = h->ref; + HEVCVulkanDecodePicture *hp = pic->hwaccel_picture_private; + FFVulkanDecodePicture *vp = &hp->vp; + FFVulkanDecodePicture *rvp[HEVC_MAX_REFS] = { 0 }; + AVFrame *rav[HEVC_MAX_REFS] = { 0 }; + int err; + + if (!hp->h265_pic_info.sliceSegmentCount) + return 0; + + if (!dec->session_params) { + const HEVCSPS *sps = h->ps.sps; + const HEVCPPS *pps = h->ps.pps; + + if (!pps) { + unsigned int pps_id = h->sh.pps_id; + if (pps_id < HEVC_MAX_PPS_COUNT && h->ps.pps_list[pps_id] != NULL) + pps = h->ps.pps_list[pps_id]; + } + + if (!pps) { + av_log(avctx, AV_LOG_ERROR, + "Encountered frame without a valid active PPS reference.\n"); + return AVERROR_INVALIDDATA; + } + + err = vk_hevc_create_params(avctx, &dec->session_params); + if (err < 0) + return err; + + hp->h265pic.sps_video_parameter_set_id = sps->vps_id; + hp->h265pic.pps_seq_parameter_set_id = pps->sps_id; + hp->h265pic.pps_pic_parameter_set_id = pps->pps_id; + } + + for (int i = 0; i < vp->decode_info.referenceSlotCount; i++) { + HEVCVulkanDecodePicture *rfhp = hp->ref_src[i]->hwaccel_picture_private; + rav[i] = hp->ref_src[i]->frame; + rvp[i] = &rfhp->vp; + } + + av_log(avctx, AV_LOG_VERBOSE, "Decoding frame, %"SIZE_SPECIFIER" bytes, %i slices\n", + vp->slices_size, hp->h265_pic_info.sliceSegmentCount); + + return ff_vk_decode_frame(avctx, pic->frame, vp, rav, rvp); +} + +static void vk_hevc_free_frame_priv(FFRefStructOpaque _hwctx, void *data) +{ + AVHWDeviceContext *hwctx = _hwctx.nc; + HEVCVulkanDecodePicture *hp = data; + + /* Free frame resources */ + ff_vk_decode_free_frame(hwctx, &hp->vp); +} + +const FFHWAccel ff_hevc_vulkan_hwaccel = { + .p.name = "hevc_vulkan", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_VULKAN, + .start_frame = &vk_hevc_start_frame, + .decode_slice = &vk_hevc_decode_slice, + .end_frame = &vk_hevc_end_frame, + .free_frame_priv = &vk_hevc_free_frame_priv, + .frame_priv_data_size = sizeof(HEVCVulkanDecodePicture), + .init = &ff_vk_decode_init, + .update_thread_context = &ff_vk_update_thread_context, + .decode_params = &ff_vk_params_invalidate, + .flush = &ff_vk_decode_flush, + .uninit = &ff_vk_decode_uninit, + .frame_params = &ff_vk_frame_params, + .priv_data_size = sizeof(FFVulkanDecodeContext), + .caps_internal = HWACCEL_CAP_ASYNC_SAFE | HWACCEL_CAP_THREAD_SAFE, +}; diff --git a/libavcodec/vulkan_video.c b/libavcodec/vulkan_video.c new file mode 100644 index 00000000000..4be13ecc557 --- /dev/null +++ b/libavcodec/vulkan_video.c @@ -0,0 +1,377 @@ +/* + * 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 "vulkan_video.h" + +#define ASPECT_2PLANE (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT) +#define ASPECT_3PLANE (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT) + +static const struct FFVkFormatMapEntry { + VkFormat vkf; + enum AVPixelFormat pixfmt; + VkImageAspectFlags aspect; +} vk_format_map[] = { + /* Gray formats */ + { VK_FORMAT_R8_UNORM, AV_PIX_FMT_GRAY8, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R16_UNORM, AV_PIX_FMT_GRAY16, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R32_SFLOAT, AV_PIX_FMT_GRAYF32, VK_IMAGE_ASPECT_COLOR_BIT }, + + /* RGB formats */ + { VK_FORMAT_R16G16B16A16_UNORM, AV_PIX_FMT_XV36, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_B8G8R8A8_UNORM, AV_PIX_FMT_BGRA, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R8G8B8A8_UNORM, AV_PIX_FMT_RGBA, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R8G8B8_UNORM, AV_PIX_FMT_RGB24, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_B8G8R8_UNORM, AV_PIX_FMT_BGR24, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R16G16B16_UNORM, AV_PIX_FMT_RGB48, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R16G16B16A16_UNORM, AV_PIX_FMT_RGBA64, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R5G6B5_UNORM_PACK16, AV_PIX_FMT_RGB565, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_B5G6R5_UNORM_PACK16, AV_PIX_FMT_BGR565, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_B8G8R8A8_UNORM, AV_PIX_FMT_BGR0, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R8G8B8A8_UNORM, AV_PIX_FMT_RGB0, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_A2R10G10B10_UNORM_PACK32, AV_PIX_FMT_X2RGB10, VK_IMAGE_ASPECT_COLOR_BIT }, + + /* Planar RGB */ + { VK_FORMAT_R8_UNORM, AV_PIX_FMT_GBRAP, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R16_UNORM, AV_PIX_FMT_GBRAP16, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R32_SFLOAT, AV_PIX_FMT_GBRPF32, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_R32_SFLOAT, AV_PIX_FMT_GBRAPF32, VK_IMAGE_ASPECT_COLOR_BIT }, + + /* Two-plane 420 YUV at 8, 10, 12 and 16 bits */ + { VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, AV_PIX_FMT_NV12, ASPECT_2PLANE }, + { VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, AV_PIX_FMT_P010, ASPECT_2PLANE }, + { VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, AV_PIX_FMT_P012, ASPECT_2PLANE }, + { VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, AV_PIX_FMT_P016, ASPECT_2PLANE }, + + /* Two-plane 422 YUV at 8, 10 and 16 bits */ + { VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, AV_PIX_FMT_NV16, ASPECT_2PLANE }, + { VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, AV_PIX_FMT_P210, ASPECT_2PLANE }, + { VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, AV_PIX_FMT_P212, ASPECT_2PLANE }, + { VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, AV_PIX_FMT_P216, ASPECT_2PLANE }, + + /* Two-plane 444 YUV at 8, 10 and 16 bits */ + { VK_FORMAT_G8_B8R8_2PLANE_444_UNORM, AV_PIX_FMT_NV24, ASPECT_2PLANE }, + { VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16, AV_PIX_FMT_P410, ASPECT_2PLANE }, + { VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16, AV_PIX_FMT_P412, ASPECT_2PLANE }, + { VK_FORMAT_G16_B16R16_2PLANE_444_UNORM, AV_PIX_FMT_P416, ASPECT_2PLANE }, + + /* Three-plane 420, 422, 444 at 8, 10, 12 and 16 bits */ + { VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, AV_PIX_FMT_YUV420P, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, AV_PIX_FMT_YUV420P10, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, AV_PIX_FMT_YUV420P12, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, AV_PIX_FMT_YUV420P16, ASPECT_3PLANE }, + { VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, AV_PIX_FMT_YUV422P, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, AV_PIX_FMT_YUV422P10, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, AV_PIX_FMT_YUV422P12, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, AV_PIX_FMT_YUV422P16, ASPECT_3PLANE }, + { VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, AV_PIX_FMT_YUV444P, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, AV_PIX_FMT_YUV444P10, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, AV_PIX_FMT_YUV444P12, ASPECT_3PLANE }, + { VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, AV_PIX_FMT_YUV444P16, ASPECT_3PLANE }, + + /* Single plane 422 at 8, 10 and 12 bits */ + { VK_FORMAT_G8B8G8R8_422_UNORM, AV_PIX_FMT_YUYV422, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_B8G8R8G8_422_UNORM, AV_PIX_FMT_UYVY422, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, AV_PIX_FMT_Y210, VK_IMAGE_ASPECT_COLOR_BIT }, + { VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, AV_PIX_FMT_Y212, VK_IMAGE_ASPECT_COLOR_BIT }, +}; +static const int nb_vk_format_map = FF_ARRAY_ELEMS(vk_format_map); + +enum AVPixelFormat ff_vk_pix_fmt_from_vkfmt(VkFormat vkf) +{ + for (int i = 0; i < nb_vk_format_map; i++) + if (vk_format_map[i].vkf == vkf) + return vk_format_map[i].pixfmt; + return AV_PIX_FMT_NONE; +} + +VkImageAspectFlags ff_vk_aspect_bits_from_vkfmt(VkFormat vkf) +{ + for (int i = 0; i < nb_vk_format_map; i++) + if (vk_format_map[i].vkf == vkf) + return vk_format_map[i].aspect; + return VK_IMAGE_ASPECT_NONE; +} + +VkVideoChromaSubsamplingFlagBitsKHR ff_vk_subsampling_from_av_desc(const AVPixFmtDescriptor *desc) +{ + if (desc->nb_components == 1) + return VK_VIDEO_CHROMA_SUBSAMPLING_MONOCHROME_BIT_KHR; + else if (!desc->log2_chroma_w && !desc->log2_chroma_h) + return VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR; + else if (!desc->log2_chroma_w && desc->log2_chroma_h == 1) + return VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR; + else if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 1) + return VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR; + return VK_VIDEO_CHROMA_SUBSAMPLING_INVALID_KHR; +} + +VkVideoComponentBitDepthFlagBitsKHR ff_vk_depth_from_av_depth(int depth) +{ + switch (depth) { + case 8: return VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR; + case 10: return VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR; + case 12: return VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR; + default: break; + } + return VK_VIDEO_COMPONENT_BIT_DEPTH_INVALID_KHR; +} + +int ff_vk_h264_level_to_av(StdVideoH264LevelIdc level) +{ + switch (level) { + case STD_VIDEO_H264_LEVEL_IDC_1_0: return 10; + case STD_VIDEO_H264_LEVEL_IDC_1_1: return 11; + case STD_VIDEO_H264_LEVEL_IDC_1_2: return 12; + case STD_VIDEO_H264_LEVEL_IDC_1_3: return 13; + case STD_VIDEO_H264_LEVEL_IDC_2_0: return 20; + case STD_VIDEO_H264_LEVEL_IDC_2_1: return 21; + case STD_VIDEO_H264_LEVEL_IDC_2_2: return 22; + case STD_VIDEO_H264_LEVEL_IDC_3_0: return 30; + case STD_VIDEO_H264_LEVEL_IDC_3_1: return 31; + case STD_VIDEO_H264_LEVEL_IDC_3_2: return 32; + case STD_VIDEO_H264_LEVEL_IDC_4_0: return 40; + case STD_VIDEO_H264_LEVEL_IDC_4_1: return 41; + case STD_VIDEO_H264_LEVEL_IDC_4_2: return 42; + case STD_VIDEO_H264_LEVEL_IDC_5_0: return 50; + case STD_VIDEO_H264_LEVEL_IDC_5_1: return 51; + case STD_VIDEO_H264_LEVEL_IDC_5_2: return 52; + case STD_VIDEO_H264_LEVEL_IDC_6_0: return 60; + case STD_VIDEO_H264_LEVEL_IDC_6_1: return 61; + default: + case STD_VIDEO_H264_LEVEL_IDC_6_2: return 62; + } +} + +int ff_vk_h265_level_to_av(StdVideoH265LevelIdc level) +{ + switch (level) { + case STD_VIDEO_H265_LEVEL_IDC_1_0: return 10; + case STD_VIDEO_H265_LEVEL_IDC_2_0: return 20; + case STD_VIDEO_H265_LEVEL_IDC_2_1: return 21; + case STD_VIDEO_H265_LEVEL_IDC_3_0: return 30; + case STD_VIDEO_H265_LEVEL_IDC_3_1: return 31; + case STD_VIDEO_H265_LEVEL_IDC_4_0: return 40; + case STD_VIDEO_H265_LEVEL_IDC_4_1: return 41; + case STD_VIDEO_H265_LEVEL_IDC_5_0: return 50; + case STD_VIDEO_H265_LEVEL_IDC_5_1: return 51; + case STD_VIDEO_H265_LEVEL_IDC_6_0: return 60; + case STD_VIDEO_H265_LEVEL_IDC_6_1: return 61; + default: + case STD_VIDEO_H265_LEVEL_IDC_6_2: return 62; + } +} + +static void free_data_buf(void *opaque, uint8_t *data) +{ + FFVulkanContext *ctx = opaque; + FFVkVideoBuffer *buf = (FFVkVideoBuffer *)data; + ff_vk_unmap_buffer(ctx, &buf->buf, 0); + ff_vk_free_buf(ctx, &buf->buf); + av_free(data); +} + +static AVBufferRef *alloc_data_buf(void *opaque, size_t size) +{ + AVBufferRef *ref; + uint8_t *buf = av_mallocz(size); + if (!buf) + return NULL; + + ref = av_buffer_create(buf, size, free_data_buf, opaque, 0); + if (!ref) + av_free(buf); + return ref; +} + +int ff_vk_video_get_buffer(FFVulkanContext *ctx, FFVkVideoCommon *s, + AVBufferRef **buf, VkBufferUsageFlags usage, + void *create_pNext, size_t size) +{ + int err; + AVBufferRef *ref; + FFVkVideoBuffer *data; + + if (!s->buf_pool) { + s->buf_pool = av_buffer_pool_init2(sizeof(FFVkVideoBuffer), ctx, + alloc_data_buf, NULL); + if (!s->buf_pool) + return AVERROR(ENOMEM); + } + + *buf = ref = av_buffer_pool_get(s->buf_pool); + if (!ref) + return AVERROR(ENOMEM); + + data = (FFVkVideoBuffer *)ref->data; + + if (data->buf.size >= size) + return 0; + + /* No point in requesting anything smaller. */ + size = FFMAX(size, 1024*1024); + + /* Align buffer to nearest power of two. Makes fragmentation management + * easier, and gives us ample headroom. */ + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size++; + + ff_vk_free_buf(ctx, &data->buf); + memset(data, 0, sizeof(FFVkVideoBuffer)); + + err = ff_vk_create_buf(ctx, &data->buf, size, + create_pNext, NULL, usage, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + if (err < 0) { + av_buffer_unref(&ref); + return err; + } + + /* Map the buffer */ + err = ff_vk_map_buffer(ctx, &data->buf, &data->mem, 0); + if (err < 0) { + av_buffer_unref(&ref); + return err; + } + + return 0; +} + +av_cold void ff_vk_video_common_uninit(FFVulkanContext *s, + FFVkVideoCommon *common) +{ + FFVulkanFunctions *vk = &s->vkfn; + + if (common->session) { + vk->DestroyVideoSessionKHR(s->hwctx->act_dev, common->session, + s->hwctx->alloc); + common->session = VK_NULL_HANDLE; + } + + if (common->nb_mem && common->mem) + for (int i = 0; i < common->nb_mem; i++) + vk->FreeMemory(s->hwctx->act_dev, common->mem[i], s->hwctx->alloc); + + av_freep(&common->mem); + + av_buffer_pool_uninit(&common->buf_pool); +} + +av_cold int ff_vk_video_common_init(void *log, FFVulkanContext *s, + FFVkVideoCommon *common, + VkVideoSessionCreateInfoKHR *session_create) +{ + int err; + VkResult ret; + FFVulkanFunctions *vk = &s->vkfn; + VkVideoSessionMemoryRequirementsKHR *mem = NULL; + VkBindVideoSessionMemoryInfoKHR *bind_mem = NULL; + + /* Create session */ + ret = vk->CreateVideoSessionKHR(s->hwctx->act_dev, session_create, + s->hwctx->alloc, &common->session); + if (ret != VK_SUCCESS) + return AVERROR_EXTERNAL; + + /* Get memory requirements */ + ret = vk->GetVideoSessionMemoryRequirementsKHR(s->hwctx->act_dev, + common->session, + &common->nb_mem, + NULL); + if (ret != VK_SUCCESS) { + err = AVERROR_EXTERNAL; + goto fail; + } + + /* Allocate all memory needed to actually allocate memory */ + common->mem = av_mallocz(sizeof(*common->mem)*common->nb_mem); + if (!common->mem) { + err = AVERROR(ENOMEM); + goto fail; + } + mem = av_mallocz(sizeof(*mem)*common->nb_mem); + if (!mem) { + err = AVERROR(ENOMEM); + goto fail; + } + bind_mem = av_mallocz(sizeof(*bind_mem)*common->nb_mem); + if (!bind_mem) { + err = AVERROR(ENOMEM); + goto fail; + } + + /* Set the needed fields to get the memory requirements */ + for (int i = 0; i < common->nb_mem; i++) { + mem[i] = (VkVideoSessionMemoryRequirementsKHR) { + .sType = VK_STRUCTURE_TYPE_VIDEO_SESSION_MEMORY_REQUIREMENTS_KHR, + }; + } + + /* Finally get the memory requirements */ + ret = vk->GetVideoSessionMemoryRequirementsKHR(s->hwctx->act_dev, + common->session, &common->nb_mem, + mem); + if (ret != VK_SUCCESS) { + err = AVERROR_EXTERNAL; + goto fail; + } + + /* Now allocate each requested memory. + * For ricing, could pool together memory that ends up in the same index. */ + for (int i = 0; i < common->nb_mem; i++) { + err = ff_vk_alloc_mem(s, &mem[i].memoryRequirements, + UINT32_MAX, NULL, NULL, &common->mem[i]); + if (err < 0) + goto fail; + + bind_mem[i] = (VkBindVideoSessionMemoryInfoKHR) { + .sType = VK_STRUCTURE_TYPE_BIND_VIDEO_SESSION_MEMORY_INFO_KHR, + .memory = common->mem[i], + .memoryBindIndex = mem[i].memoryBindIndex, + .memoryOffset = 0, + .memorySize = mem[i].memoryRequirements.size, + }; + + av_log(log, AV_LOG_VERBOSE, "Allocating %"PRIu64" bytes in bind index %i for video session\n", + bind_mem[i].memorySize, bind_mem[i].memoryBindIndex); + } + + /* Bind the allocated memory */ + ret = vk->BindVideoSessionMemoryKHR(s->hwctx->act_dev, common->session, + common->nb_mem, bind_mem); + if (ret != VK_SUCCESS) { + err = AVERROR_EXTERNAL; + goto fail; + } + + av_freep(&mem); + av_freep(&bind_mem); + + return 0; + +fail: + av_freep(&mem); + av_freep(&bind_mem); + + ff_vk_video_common_uninit(s, common); + return err; +} diff --git a/libavcodec/vulkan_video.h b/libavcodec/vulkan_video.h new file mode 100644 index 00000000000..01a1de7d9d5 --- /dev/null +++ b/libavcodec/vulkan_video.h @@ -0,0 +1,91 @@ +/* + * 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 + */ + +#ifndef AVCODEC_VULKAN_VIDEO_H +#define AVCODEC_VULKAN_VIDEO_H + +#include "vulkan.h" + +#include + +#define CODEC_VER_MAJ(ver) (ver >> 22) +#define CODEC_VER_MIN(ver) ((ver >> 12) & ((1 << 10) - 1)) +#define CODEC_VER_PAT(ver) (ver & ((1 << 12) - 1)) +#define CODEC_VER(ver) CODEC_VER_MAJ(ver), CODEC_VER_MIN(ver), CODEC_VER_PAT(ver) + +typedef struct FFVkVideoSession { + VkVideoSessionKHR session; + VkDeviceMemory *mem; + uint32_t nb_mem; + + AVBufferPool *buf_pool; +} FFVkVideoCommon; + +/** + * Get pixfmt from a Vulkan format. + */ +enum AVPixelFormat ff_vk_pix_fmt_from_vkfmt(VkFormat vkf); + +/** + * Get aspect bits which include all planes from a VkFormat. + */ +VkImageAspectFlags ff_vk_aspect_bits_from_vkfmt(VkFormat vkf); + +/** + * Get Vulkan's chroma subsampling from a pixfmt descriptor. + */ +VkVideoChromaSubsamplingFlagBitsKHR ff_vk_subsampling_from_av_desc(const AVPixFmtDescriptor *desc); + +/** + * Get Vulkan's bit depth from an [8:12] integer. + */ +VkVideoComponentBitDepthFlagBitsKHR ff_vk_depth_from_av_depth(int depth); + + +/** + * Convert level from Vulkan to AV. + */ +int ff_vk_h264_level_to_av(StdVideoH264LevelIdc level); +int ff_vk_h265_level_to_av(StdVideoH265LevelIdc level); + +typedef struct FFVkVideoBuffer { + FFVkBuffer buf; + uint8_t *mem; +} FFVkVideoBuffer; + +/** + * Get a mapped FFVkPooledBuffer with a specific guaranteed minimum size + * from a pool. + */ +int ff_vk_video_get_buffer(FFVulkanContext *ctx, FFVkVideoCommon *s, + AVBufferRef **buf, VkBufferUsageFlags usage, + void *create_pNext, size_t size); + +/** + * Initialize video session, allocating and binding necessary memory. + */ +int ff_vk_video_common_init(void *log, FFVulkanContext *s, + FFVkVideoCommon *common, + VkVideoSessionCreateInfoKHR *session_create); + +/** + * Free video session and required resources. + */ +void ff_vk_video_common_uninit(FFVulkanContext *s, FFVkVideoCommon *common); + +#endif /* AVCODEC_VULKAN_VIDEO_H */ diff --git a/libavcodec/vvc.h b/libavcodec/vvc.h new file mode 100644 index 00000000000..c4cec1eb8ff --- /dev/null +++ b/libavcodec/vvc.h @@ -0,0 +1,156 @@ +/* + * H.266 / VVC shared code + * + * 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 + */ + +#ifndef AVCODEC_VVC_H +#define AVCODEC_VVC_H + +/** + * Table 5 – NAL unit type codes and NAL unit type classes + * in T-REC-H.266-202008 + */ +enum VVCNALUnitType { + VVC_TRAIL_NUT = 0, + VVC_STSA_NUT = 1, + VVC_RADL_NUT = 2, + VVC_RASL_NUT = 3, + VVC_RSV_VCL_4 = 4, + VVC_RSV_VCL_5 = 5, + VVC_RSV_VCL_6 = 6, + VVC_IDR_W_RADL = 7, + VVC_IDR_N_LP = 8, + VVC_CRA_NUT = 9, + VVC_GDR_NUT = 10, + VVC_RSV_IRAP_11 = 11, + VVC_OPI_NUT = 12, + VVC_DCI_NUT = 13, + VVC_VPS_NUT = 14, + VVC_SPS_NUT = 15, + VVC_PPS_NUT = 16, + VVC_PREFIX_APS_NUT = 17, + VVC_SUFFIX_APS_NUT = 18, + VVC_PH_NUT = 19, + VVC_AUD_NUT = 20, + VVC_EOS_NUT = 21, + VVC_EOB_NUT = 22, + VVC_PREFIX_SEI_NUT = 23, + VVC_SUFFIX_SEI_NUT = 24, + VVC_FD_NUT = 25, + VVC_RSV_NVCL_26 = 26, + VVC_RSV_NVCL_27 = 27, + VVC_UNSPEC_28 = 28, + VVC_UNSPEC_29 = 29, + VVC_UNSPEC_30 = 30, + VVC_UNSPEC_31 = 31, +}; + +enum VVCSliceType { + VVC_SLICE_TYPE_B = 0, + VVC_SLICE_TYPE_P = 1, + VVC_SLICE_TYPE_I = 2, +}; + +enum VVCAPSType { + VVC_ASP_TYPE_ALF = 0, + VVC_ASP_TYPE_LMCS = 1, + VVC_ASP_TYPE_SCALING = 2, +}; + +enum { + //6.2 we can have 3 sample arrays + VVC_MAX_SAMPLE_ARRAYS = 3, + + //7.4.3.3 vps_max_layers_minus1 is u(6) + VVC_MAX_LAYERS = 64, + + //7.4.3.3 The value of vps_max_sublayers_minus1 shall be in the range of 0 to 6, inclusive + VVC_MAX_SUBLAYERS = 7, + + //7.3.2.1 dci_num_ptls_minus1 is u(4) + VVC_MAX_DCI_PTLS = 16, + + //7.4.3.3 vps_num_ptls_minus1 is u(8) + VVC_MAX_PTLS = 256, + + //7.4.3.3 vps_num_output_layer_sets_minus2 is u(8) + VVC_MAX_TOTAL_NUM_OLSS = 257, + + // 7.3.2.3: vps_video_parameter_set_id is u(4). + VVC_MAX_VPS_COUNT = 16, + // 7.3.2.4: sps_seq_parameter_set_id is u(4) + VVC_MAX_SPS_COUNT = 16, + // 7.3.2.5: pps_pic_parameter_set_id is u(6) + VVC_MAX_PPS_COUNT = 64, + + // 7.4.4.1: ptl_num_sub_profiles is u(8) + VVC_MAX_SUB_PROFILES = 256, + + // 7.4.3.18: The variable NumAlfFilters specifying the number of different adaptive loop + // filters is set equal to 25. + VVC_NUM_ALF_FILTERS = 25, + + // A.4.2: according to (1577), MaxDpbSize is bounded above by 2 * maxDpbPicBuf(8) + VVC_MAX_DPB_SIZE = 16, + + //7.4.3.4 sps_num_ref_pic_lists in range [0, 64] + VVC_MAX_REF_PIC_LISTS = 64, + + //7.4.11 num_ref_entries in range [0, MaxDpbSize + 13] + VVC_MAX_REF_ENTRIES = VVC_MAX_DPB_SIZE + 13, + + //7.4.3.3 sps_num_points_in_qp_table_minus1[i] in range [0, 36 − sps_qp_table_start_minus26[i]], + //and sps_qp_table_start_minus26[i] in range [−26 − QpBdOffset, 36]. + //so sps_num_points_in_qp_table_minus1[i] should in range [0, 62 + QpBdOffset] + //since 16 bits QpBdOffset is 48, sps_num_points_in_qp_table_minus1[i] should range [0, 110] + VVC_MAX_POINTS_IN_QP_TABLE = 111, + + // 7.4.6.1: hrd_cpb_cnt_minus1 is in [0, 31]. + VVC_MAX_CPB_CNT = 32, + + // A.4.1: the highest level allows a MaxLumaPs of 80,216,064. + VVC_MAX_LUMA_PS = 80216064, + + // A.4.1: pic_width_in_luma_samples and pic_height_in_luma_samples are + // constrained to be not greater than sqrt(MaxLumaPs * 8). Hence height/ + // width are bounded above by sqrt(8 * 80216064) = 25332.4 samples. + VVC_MAX_WIDTH = 25332, + VVC_MAX_HEIGHT = 25332, + + // A.4.1: table A.2 allows at most 990 tiles per AU for any level. + VVC_MAX_TILES_PER_AU = 990, + // A.4.1: table A.2 did not define max tile rows. + // in worest a case, we can have 1x990 tiles picture. + VVC_MAX_TILE_ROWS = VVC_MAX_TILES_PER_AU, + // A.4.1: table A.2 allows at most 30 tile columns for any level. + VVC_MAX_TILE_COLUMNS = 30, + + // A.4.1 table A.2 allows at most 1000 slices for any level. + VVC_MAX_SLICES = 1000, + + // 7.4.8: in the worst case (!pps_no_pic_partition_flag and + // sps_entropy_coding_sync_enabled_flag are both true), entry points can be + // placed at the beginning of every Ctb row in every tile, giving an + // upper bound of (num_tile_columns_minus1 + 1) * PicHeightInCtbsY - 1. + // Only a stream with very high resolution and perverse parameters could + // get near that, though, so set a lower limit here with the maximum + // possible value for 8K video (at most 135 32x32 Ctb rows). + VVC_MAX_ENTRY_POINTS = VVC_MAX_TILE_COLUMNS * 135, +}; + +#endif /* AVCODEC_VVC_H */ diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile new file mode 100644 index 00000000000..2a0055d4941 --- /dev/null +++ b/libavcodec/vvc/Makefile @@ -0,0 +1,17 @@ +clean:: + $(RM) $(CLEANSUFFIXES:%=libavcodec/vvc/%) + +OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \ + vvc/vvcdsp.o \ + vvc/vvc_cabac.o \ + vvc/vvc_ctu.o \ + vvc/vvc_data.o \ + vvc/vvc_filter.o \ + vvc/vvc_inter.o \ + vvc/vvc_intra.o \ + vvc/vvc_intra_utils.o \ + vvc/vvc_itx_1d.o \ + vvc/vvc_mvs.o \ + vvc/vvc_ps.o \ + vvc/vvc_refs.o \ + vvc/vvc_thread.o \ diff --git a/libavcodec/vvc/vvc_cabac.c b/libavcodec/vvc/vvc_cabac.c new file mode 100644 index 00000000000..5e24a1b677f --- /dev/null +++ b/libavcodec/vvc/vvc_cabac.c @@ -0,0 +1,2478 @@ +/* + * VVC CABAC decoder + * + * Copyright (C) 2021 Nuo Mi + * + * 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 "libavcodec/cabac_functions.h" + +#include "vvc_cabac.h" +#include "vvc_ctu.h" +#include "vvc_data.h" + +#define CABAC_MAX_BIN 31 + +#define CNU 35 + +enum SyntaxElement { + ALF_CTB_FLAG = 0, + ALF_USE_APS_FLAG = ALF_CTB_FLAG + 9, + ALF_CTB_CC_CB_IDC, + ALF_CTB_CC_CR_IDC = ALF_CTB_CC_CB_IDC + 3, + ALF_CTB_FILTER_ALT_IDX = ALF_CTB_CC_CR_IDC + 3, + SAO_MERGE_FLAG = ALF_CTB_FILTER_ALT_IDX + 2, + SAO_TYPE_IDX, + SPLIT_CU_FLAG, + SPLIT_QT_FLAG = SPLIT_CU_FLAG + 9, + MTT_SPLIT_CU_VERTICAL_FLAG = SPLIT_QT_FLAG + 6, + MTT_SPLIT_CU_BINARY_FLAG = MTT_SPLIT_CU_VERTICAL_FLAG + 5, + NON_INTER_FLAG = MTT_SPLIT_CU_BINARY_FLAG + 4, + CU_SKIP_FLAG = NON_INTER_FLAG + 2, + PRED_MODE_IBC_FLAG = CU_SKIP_FLAG + 3, + PRED_MODE_FLAG = PRED_MODE_IBC_FLAG + 3, + PRED_MODE_PLT_FLAG = PRED_MODE_FLAG + 2, + CU_ACT_ENABLED_FLAG, + INTRA_BDPCM_LUMA_FLAG, + INTRA_BDPCM_LUMA_DIR_FLAG, + INTRA_MIP_FLAG, + INTRA_LUMA_REF_IDX = INTRA_MIP_FLAG + 4, + INTRA_SUBPARTITIONS_MODE_FLAG = INTRA_LUMA_REF_IDX + 2, + INTRA_SUBPARTITIONS_SPLIT_FLAG, + INTRA_LUMA_MPM_FLAG, + INTRA_LUMA_NOT_PLANAR_FLAG, + INTRA_BDPCM_CHROMA_FLAG = INTRA_LUMA_NOT_PLANAR_FLAG + 2, + INTRA_BDPCM_CHROMA_DIR_FLAG, + CCLM_MODE_FLAG, + CCLM_MODE_IDX, + INTRA_CHROMA_PRED_MODE, + GENERAL_MERGE_FLAG, + INTER_PRED_IDC, + INTER_AFFINE_FLAG = INTER_PRED_IDC + 6, + CU_AFFINE_TYPE_FLAG = INTER_AFFINE_FLAG + 3, + SYM_MVD_FLAG, + REF_IDX_LX, + MVP_LX_FLAG = REF_IDX_LX + 2, + AMVR_FLAG, + AMVR_PRECISION_IDX = AMVR_FLAG + 2, + BCW_IDX = AMVR_PRECISION_IDX + 3, + CU_CODED_FLAG, + CU_SBT_FLAG, + CU_SBT_QUAD_FLAG = CU_SBT_FLAG + 2, + CU_SBT_HORIZONTAL_FLAG, + CU_SBT_POS_FLAG = CU_SBT_HORIZONTAL_FLAG + 3, + LFNST_IDX, + MTS_IDX = LFNST_IDX + 3, + COPY_ABOVE_PALETTE_INDICES_FLAG = MTS_IDX + 4, + PALETTE_TRANSPOSE_FLAG, + RUN_COPY_FLAG, + REGULAR_MERGE_FLAG = RUN_COPY_FLAG + 8, + MMVD_MERGE_FLAG = REGULAR_MERGE_FLAG + 2, + MMVD_CAND_FLAG, + MMVD_DISTANCE_IDX, + CIIP_FLAG, + MERGE_SUBBLOCK_FLAG, + MERGE_SUBBLOCK_IDX = MERGE_SUBBLOCK_FLAG + 3, + MERGE_IDX, + ABS_MVD_GREATER0_FLAG, + ABS_MVD_GREATER1_FLAG, + TU_Y_CODED_FLAG, + TU_CB_CODED_FLAG = TU_Y_CODED_FLAG + 4, + TU_CR_CODED_FLAG = TU_CB_CODED_FLAG + 2, + CU_QP_DELTA_ABS = TU_CR_CODED_FLAG + 3, + CU_CHROMA_QP_OFFSET_FLAG = CU_QP_DELTA_ABS + 2, + CU_CHROMA_QP_OFFSET_IDX, + TRANSFORM_SKIP_FLAG, + TU_JOINT_CBCR_RESIDUAL_FLAG = TRANSFORM_SKIP_FLAG + 2, + LAST_SIG_COEFF_X_PREFIX = TU_JOINT_CBCR_RESIDUAL_FLAG + 3, + LAST_SIG_COEFF_Y_PREFIX = LAST_SIG_COEFF_X_PREFIX +23, + SB_CODED_FLAG = LAST_SIG_COEFF_Y_PREFIX +23, + SIG_COEFF_FLAG = SB_CODED_FLAG + 7, + PAR_LEVEL_FLAG = SIG_COEFF_FLAG +63, + ABS_LEVEL_GTX_FLAG = PAR_LEVEL_FLAG +33, + COEFF_SIGN_FLAG = ABS_LEVEL_GTX_FLAG +72, + SYNTAX_ELEMENT_LAST = COEFF_SIGN_FLAG + 6, +}; + +static const uint8_t init_values[4][SYNTAX_ELEMENT_LAST] = { + { + //alf_ctb_flag + 62, 39, 39, 54, 39, 39, 31, 39, 39, + //alf_use_aps_flag + 46, + //alf_ctb_cc_cb_idc + 18, 30, 31, + //alf_ctb_cc_cr_idc + 18, 30, 31, + //alf_ctb_filter_alt_idx + 11, 11, + //sao_merge_left_flag and sao_merge_up_flag + 60, + //sao_type_idx_luma and sao_type_idx_chroma + 13, + //split_cu_flag + 19, 28, 38, 27, 29, 38, 20, 30, 31, + //split_qt_flag + 27, 6, 15, 25, 19, 37, + //mtt_split_cu_vertical_flag + 43, 42, 29, 27, 44, + //mtt_split_cu_binary_flag + 36, 45, 36, 45, + //non_inter_flag + CNU, CNU, + //cu_skip_flag + 0, 26, 28, + //pred_mode_ibc_flag + 17, 42, 36, + //pred_mode_flag + CNU, CNU, + //pred_mode_plt_flag + 25, + //cu_act_enabled_flag + 52, + //intra_bdpcm_luma_flag + 19, + //intra_bdpcm_luma_dir_flag + 35, + //intra_mip_flag + 33, 49, 50, 25, + //intra_luma_ref_idx + 25, 60, + //intra_subpartitions_mode_flag + 33, + //intra_subpartitions_split_flag + 43, + //intra_luma_mpm_flag + 45, + //intra_luma_not_planar_flag + 13, 28, + //intra_bdpcm_chroma_flag + 1, + //intra_bdpcm_chroma_dir_flag + 27, + //cclm_mode_flag + 59, + //cclm_mode_idx + 27, + //intra_chroma_pred_mode + 34, + //general_merge_flag + 26, + //inter_pred_idc + CNU, CNU, CNU, CNU, CNU, CNU, + //inter_affine_flag + CNU, CNU, CNU, + //cu_affine_type_flag + CNU, + //sym_mvd_flag + CNU, + //ref_idx_l0 and ref_idx_l1 + CNU, CNU, + //mvp_l0_flag and mvp_l1_flag + 42, + //amvr_flag + CNU, CNU, + //amvr_precision_idx + 35, 34, 35, + //bcw_idx + CNU, + //cu_coded_flag + 6, + //cu_sbt_flag + CNU, CNU, + //cu_sbt_quad_flag + CNU, + //cu_sbt_horizontal_flag + CNU, CNU, CNU, + //cu_sbt_pos_flag + CNU, + //lfnst_idx + 28, 52, 42, + //mts_idx + 29, 0, 28, 0, + //copy_above_palette_indices_flag + 42, + //palette_transpose_flag + 42, + //run_copy_flag + 50, 37, 45, 30, 46, 45, 38, 46, + //regular_merge_flag + CNU, CNU, + //mmvd_merge_flag + CNU, + //mmvd_cand_flag + CNU, + //mmvd_distance_idx + CNU, + //ciip_flag + CNU, + //merge_subblock_flag + CNU, CNU, CNU, + //merge_subblock_idx + CNU, + //merge_idx, merge_gpm_idx0, and merge_gpm_idx1 + 34, + //abs_mvd_greater0_flag + 14, + //abs_mvd_greater1_flag + 45, + //tu_y_coded_flag + 15, 12, 5, 7, + //tu_cb_coded_flag + 12, 21, + //tu_cr_coded_flag + 33, 28, 36, + //cu_qp_delta_abs + CNU, CNU, + //cu_chroma_qp_offset_flag + CNU, + //cu_chroma_qp_offset_idx + CNU, + //transform_skip_flag + 25, 9, + //tu_joint_cbcr_residual_flag + 12, 21, 35, + //last_sig_coeff_x_prefix + 13, 5, 4, 21, 14, 4, 6, 14, 21, 11, 14, 7, 14, 5, 11, 21, + 30, 22, 13, 42, 12, 4, 3, + //last_sig_coeff_y_prefix + 13, 5, 4, 6, 13, 11, 14, 6, 5, 3, 14, 22, 6, 4, 3, 6, + 22, 29, 20, 34, 12, 4, 3, + //sb_coded_flag + 18, 31, 25, 15, 18, 20, 38, + //sig_coeff_flag + 25, 19, 28, 14, 25, 20, 29, 30, 19, 37, 30, 38, 11, 38, 46, 54, + 27, 39, 39, 39, 44, 39, 39, 39, 18, 39, 39, 39, 27, 39, 39, 39, + 0, 39, 39, 39, 25, 27, 28, 37, 34, 53, 53, 46, 19, 46, 38, 39, + 52, 39, 39, 39, 11, 39, 39, 39, 19, 39, 39, 39, 25, 28, 38, + //par_level_flag + 33, 25, 18, 26, 34, 27, 25, 26, 19, 42, 35, 33, 19, 27, 35, 35, + 34, 42, 20, 43, 20, 33, 25, 26, 42, 19, 27, 26, 50, 35, 20, 43, + 11, + //abs_level_gtx_flag + 25, 25, 11, 27, 20, 21, 33, 12, 28, 21, 22, 34, 28, 29, 29, 30, + 36, 29, 45, 30, 23, 40, 33, 27, 28, 21, 37, 36, 37, 45, 38, 46, + 25, 1, 40, 25, 33, 11, 17, 25, 25, 18, 4, 17, 33, 26, 19, 13, + 33, 19, 20, 28, 22, 40, 9, 25, 18, 26, 35, 25, 26, 35, 28, 37, + 11, 5, 5, 14, 10, 3, 3, 3, + //coeff_sign_flag + 12, 17, 46, 28, 25, 46, + }, + { + //alf_ctb_flag + 13, 23, 46, 4, 61, 54, 19, 46, 54, + //alf_use_aps_flag + 46, + //alf_ctb_cc_cb_idc + 18, 21, 38, + //alf_ctb_cc_cr_idc + 18, 21, 38, + //alf_ctb_filter_alt_idx + 20, 12, + //sao_merge_left_flag and sao_merge_up_flag + 60, + //sao_type_idx_luma and sao_type_idx_chroma + 5, + //split_cu_flag + 11, 35, 53, 12, 6, 30, 13, 15, 31, + //split_qt_flag + 20, 14, 23, 18, 19, 6, + //mtt_split_cu_vertical_flag + 43, 35, 37, 34, 52, + //mtt_split_cu_binary_flag + 43, 37, 21, 22, + //non_inter_flag + 25, 12, + //cu_skip_flag + 57, 59, 45, + //pred_mode_ibc_flag + 0, 57, 44, + //pred_mode_flag + 40, 35, + //pred_mode_plt_flag + 0, + //cu_act_enabled_flag + 46, + //intra_bdpcm_luma_flag + 40, + //intra_bdpcm_luma_dir_flag + 36, + //intra_mip_flag + 41, 57, 58, 26, + //intra_luma_ref_idx + 25, 58, + //intra_subpartitions_mode_flag + 33, + //intra_subpartitions_split_flag + 36, + //intra_luma_mpm_flag + 36, + //intra_luma_not_planar_flag + 12, 20, + //intra_bdpcm_chroma_flag + 0, + //intra_bdpcm_chroma_dir_flag + 13, + //cclm_mode_flag + 34, + //cclm_mode_idx + 27, + //intra_chroma_pred_mode + 25, + //general_merge_flag + 21, + //inter_pred_idc + 7, 6, 5, 12, 4, 40, + //inter_affine_flag + 12, 13, 14, + //cu_affine_type_flag + 35, + //sym_mvd_flag + 28, + //ref_idx_l0 and ref_idx_l1 + 20, 35, + //mvp_l0_flag and mvp_l1_flag + 34, + //amvr_flag + 59, 58, + //amvr_precision_idx + 60, 48, 60, + //bcw_idx + 4, + //cu_coded_flag + 5, + //cu_sbt_flag + 56, 57, + //cu_sbt_quad_flag + 42, + //cu_sbt_horizontal_flag + 20, 43, 12, + //cu_sbt_pos_flag + 28, + //lfnst_idx + 37, 45, 27, + //mts_idx + 45, 40, 27, 0, + //copy_above_palette_indices_flag + 59, + //palette_transpose_flag + 42, + //run_copy_flag + 51, 30, 30, 38, 23, 38, 53, 46, + //regular_merge_flag + 38, 7, + //mmvd_merge_flag + 26, + //mmvd_cand_flag + 43, + //mmvd_distance_idx + 60, + //ciip_flag + 57, + //merge_subblock_flag + 48, 57, 44, + //merge_subblock_idx + 5, + //merge_idx, merge_gpm_idx0, and merge_gpm_idx1 + 20, + //abs_mvd_greater0_flag + 44, + //abs_mvd_greater1_flag + 43, + //tu_y_coded_flag + 23, 5, 20, 7, + //tu_cb_coded_flag + 25, 28, + //tu_cr_coded_flag + 25, 29, 45, + //cu_qp_delta_abs + CNU, CNU, + //cu_chroma_qp_offset_flag + CNU, + //cu_chroma_qp_offset_idx + CNU, + //transform_skip_flag + 25, 9, + //tu_joint_cbcr_residual_flag + 27, 36, 45, + //last_sig_coeff_x_prefix + 6, 13, 12, 6, 6, 12, 14, 14, 13, 12, 29, 7, 6, 13, 36, 28, + 14, 13, 5, 26, 12, 4, 18, + //last_sig_coeff_y_prefix + 5, 5, 12, 6, 6, 4, 6, 14, 5, 12, 14, 7, 13, 5, 13, 21, + 14, 20, 12, 34, 11, 4, 18, + //sb_coded_flag + 25, 30, 25, 45, 18, 12, 29, + //sig_coeff_flag + 17, 41, 42, 29, 25, 49, 43, 37, 33, 58, 51, 30, 19, 38, 38, 46, + 34, 54, 54, 39, 6, 39, 39, 39, 19, 39, 54, 39, 19, 39, 39, 39, + 56, 39, 39, 39, 17, 34, 35, 21, 41, 59, 60, 38, 35, 45, 53, 54, + 44, 39, 39, 39, 34, 38, 62, 39, 26, 39, 39, 39, 40, 35, 44, + //par_level_flag + 18, 17, 33, 18, 26, 42, 25, 33, 26, 42, 27, 25, 34, 42, 42, 35, + 26, 27, 42, 20, 20, 25, 25, 26, 11, 19, 27, 33, 42, 35, 35, 43, + 3, + //abs_level_gtx_flag + 0, 17, 26, 19, 35, 21, 25, 34, 20, 28, 29, 33, 27, 28, 29, 22, + 34, 28, 44, 37, 38, 0, 25, 19, 20, 13, 14, 57, 44, 30, 30, 23, + 17, 0, 1, 17, 25, 18, 0, 9, 25, 33, 34, 9, 25, 18, 26, 20, + 25, 18, 19, 27, 29, 17, 9, 25, 10, 18, 4, 17, 33, 19, 20, 29, + 18, 11, 4, 28, 2, 10, 3, 3, + //coeff_sign_flag + 5, 10, 53, 43, 25, 46, + }, + { + //alf_ctb_flag + 33, 52, 46, 25, 61, 54, 25, 61, 54, + //alf_use_aps_flag + 46, + //alf_ctb_cc_cb_idc + 25, 35, 38, + //alf_ctb_cc_cr_idc + 25, 28, 38, + //alf_ctb_filter_alt_idx + 11, 26, + //sao_merge_left_flag and sao_merge_up_flag + 2, + //sao_type_idx_luma and sao_type_idx_chroma + 2, + //split_cu_flag + 18, 27, 15, 18, 28, 45, 26, 7, 23, + //split_qt_flag + 26, 36, 38, 18, 34, 21, + //mtt_split_cu_vertical_flag + 43, 42, 37, 42, 44, + //mtt_split_cu_binary_flag + 28, 29, 28, 29, + //non_inter_flag + 25, 20, + //cu_skip_flag + 57, 60, 46, + //pred_mode_ibc_flag + 0, 43, 45, + //pred_mode_flag + 40, 35, + //pred_mode_plt_flag + 17, + //cu_act_enabled_flag + 46, + //intra_bdpcm_luma_flag + 19, + //intra_bdpcm_luma_dir_flag + 21, + //intra_mip_flag + 56, 57, 50, 26, + //intra_luma_ref_idx + 25, 59, + //intra_subpartitions_mode_flag + 33, + //intra_subpartitions_split_flag + 43, + //intra_luma_mpm_flag + 44, + //intra_luma_not_planar_flag + 13, 6, + //intra_bdpcm_chroma_flag + 0, + //intra_bdpcm_chroma_dir_flag + 28, + //cclm_mode_flag + 26, + //cclm_mode_idx + 27, + //intra_chroma_pred_mode + 25, + //general_merge_flag + 6, + //inter_pred_idc + 14, 13, 5, 4, 3, 40, + //inter_affine_flag + 19, 13, 6, + //cu_affine_type_flag + 35, + //sym_mvd_flag + 28, + //ref_idx_l0 and ref_idx_l1 + 5, 35, + //mvp_l0_flag and mvp_l1_flag + 34, + //amvr_flag + 59, 50, + //amvr_precision_idx + 38, 26, 60, + //bcw_idx + 5, + //cu_coded_flag + 12, + //cu_sbt_flag + 41, 57, + //cu_sbt_quad_flag + 42, + //cu_sbt_horizontal_flag + 35, 51, 27, + //cu_sbt_pos_flag + 28, + //lfnst_idx + 52, 37, 27, + //mts_idx + 45, 25, 27, 0, + //copy_above_palette_indices_flag + 50, + //palette_transpose_flag + 35, + //run_copy_flag + 58, 45, 45, 30, 38, 45, 38, 46, + //regular_merge_flag + 46, 15, + //mmvd_merge_flag + 25, + //mmvd_cand_flag + 43, + //mmvd_distance_idx + 59, + //ciip_flag + 57, + //merge_subblock_flag + 25, 58, 45, + //merge_subblock_idx + 4, + //merge_idx, merge_gpm_idx0, and merge_gpm_idx1 + 18, + //abs_mvd_greater0_flag + 51, + //abs_mvd_greater1_flag + 36, + //tu_y_coded_flag + 15, 6, 5, 14, + //tu_cb_coded_flag + 25, 37, + //tu_cr_coded_flag + 9, 36, 45, + //cu_qp_delta_abs + CNU, CNU, + //cu_chroma_qp_offset_flag + CNU, + //cu_chroma_qp_offset_idx + CNU, + //transform_skip_flag + 25, 17, + //tu_joint_cbcr_residual_flag + 42, 43, 52, + //last_sig_coeff_x_prefix + 6, 6, 12, 14, 6, 4, 14, 7, 6, 4, 29, 7, 6, 6, 12, 28, + 7, 13, 13, 35, 19, 5, 4, + //last_sig_coeff_y_prefix + 5, 5, 20, 13, 13, 19, 21, 6, 12, 12, 14, 14, 5, 4, 12, 13, + 7, 13, 12, 41, 11, 5, 27, + //sb_coded_flag + 25, 45, 25, 14, 18, 35, 45, + //sig_coeff_flag + 17, 41, 49, 36, 1, 49, 50, 37, 48, 51, 58, 45, 26, 45, 53, 46, + 49, 54, 61, 39, 35, 39, 39, 39, 19, 54, 39, 39, 50, 39, 39, 39, + 0, 39, 39, 39, 9, 49, 50, 36, 48, 59, 59, 38, 34, 45, 38, 31, + 58, 39, 39, 39, 34, 38, 54, 39, 41, 39, 39, 39, 25, 50, 37, + //par_level_flag + 33, 40, 25, 41, 26, 42, 25, 33, 26, 34, 27, 25, 41, 42, 42, 35, + 33, 27, 35, 42, 43, 33, 25, 26, 34, 19, 27, 33, 42, 43, 35, 43, + 11, + //abs_level_gtx_flag + 0, 0, 33, 34, 35, 21, 25, 34, 35, 28, 29, 40, 42, 43, 29, 30, + 49, 36, 37, 45, 38, 0, 40, 34, 43, 36, 37, 57, 52, 45, 38, 46, + 25, 0, 0, 17, 25, 26, 0, 9, 25, 33, 19, 0, 25, 33, 26, 20, + 25, 33, 27, 35, 22, 25, 1, 25, 33, 26, 12, 25, 33, 27, 28, 37, + 19, 11, 4, 6, 3, 4, 4, 5, + //coeff_sign_flag + 35, 25, 46, 28, 33, 38, + }, + //shiftIdx + { + //alf_ctb_flag + 0, 0, 0, 4, 0, 0, 1, 0, 0, + //alf_use_aps_flag + 0, + //alf_ctb_cc_cb_idc + 4, 1, 4, + //alf_ctb_cc_cr_idc + 4, 1, 4, + //alf_ctb_filter_alt_idx + 0, 0, + //sao_merge_left_flag and sao_merge_up_flag + 0, + //sao_type_idx_luma and sao_type_idx_chroma + 4, + //split_cu_flag + 12, 13, 8, 8, 13, 12, 5, 9, 9, + //split_qt_flag + 0, 8, 8, 12, 12, 8, + //mtt_split_cu_vertical_flag + 9, 8, 9, 8, 5, + //mtt_split_cu_binary_flag + 12, 13, 12, 13, + //non_inter_flag + 1, 0, + //cu_skip_flag + 5, 4, 8, + //pred_mode_ibc_flag + 1, 5, 8, + //pred_mode_flag + 5, 1, + //pred_mode_plt_flag + 1, + //cu_act_enabled_flag + 1, + //intra_bdpcm_luma_flag + 1, + //intra_bdpcm_luma_dir_flag + 4, + //intra_mip_flag + 9, 10, 9, 6, + //intra_luma_ref_idx + 5, 8, + //intra_subpartitions_mode_flag + 9, + //intra_subpartitions_split_flag + 2, + //intra_luma_mpm_flag + 6, + //intra_luma_not_planar_flag + 1, 5, + //intra_bdpcm_chroma_flag + 1, + //intra_bdpcm_chroma_dir_flag + 0, + //cclm_mode_flag + 4, + //cclm_mode_idx + 9, + //intra_chroma_pred_mode + 5, + //general_merge_flag + 4, + //inter_pred_idc + 0, 0, 1, 4, 4, 0, + //inter_affine_flag + 4, 0, 0, + //cu_affine_type_flag + 4, + //sym_mvd_flag + 5, + //ref_idx_l0 and ref_idx_l1 + 0, 4, + //mvp_l0_flag and mvp_l1_flag + 12, + //amvr_flag + 0, 0, + //amvr_precision_idx + 4, 5, 0, + //bcw_idx + 1, + //cu_coded_flag + 4, + //cu_sbt_flag + 1, 5, + //cu_sbt_quad_flag + 10, + //cu_sbt_horizontal_flag + 8, 4, 1, + //cu_sbt_pos_flag + 13, + //lfnst_idx + 9, 9, 10, + //mts_idx + 8, 0, 9, 0, + //copy_above_palette_indices_flag + 9, + //palette_transpose_flag + 5, + //run_copy_flag + 9, 6, 9, 10, 5, 0, 9, 5, + //regular_merge_flag + 5, 5, + //mmvd_merge_flag + 4, + //mmvd_cand_flag + 10, + //mmvd_distance_idx + 0, + //ciip_flag + 1, + //merge_subblock_flag + 4, 4, 4, + //merge_subblock_idx + 0, + //merge_idx, merge_gpm_idx0, and merge_gpm_idx1 + 4, + //abs_mvd_greater0_flag + 9, + //abs_mvd_greater1_flag + 5, + //tu_y_coded_flag + 5, 1, 8, 9, + //tu_cb_coded_flag + 5, 0, + //tu_cr_coded_flag + 2, 1, 0, + //cu_qp_delta_abs + 8, 8, + //cu_chroma_qp_offset_flag + 8, + //cu_chroma_qp_offset_idx + 8, + //transform_skip_flag + 1, 1, + //tu_joint_cbcr_residual_flag + 1, 1, 0, + //last_sig_coeff_x_prefix + 8, 5, 4, 5, 4, 4, 5, 4, 1, 0, 4, 1, 0, 0, 0, 0, + 1, 0, 0, 0, 5, 4, 4, + //last_sig_coeff_y_prefix + 8, 5, 8, 5, 5, 4, 5, 5, 4, 0, 5, 4, 1, 0, 0, 1, + 4, 0, 0, 0, 6, 5, 5, + //sb_coded_flag + 8, 5, 5, 8, 5, 8, 8, + //sig_coeff_flag + 12, 9, 9, 10, 9, 9, 9, 10, 8, 8, 8, 10, 9, 13, 8, 8, + 8, 8, 8, 5, 8, 0, 0, 0, 8, 8, 8, 8, 8, 0, 4, 4, + 0, 0, 0, 0, 12, 12, 9, 13, 4, 5, 8, 9, 8, 12, 12, 8, + 4, 0, 0, 0, 8, 8, 8, 8, 4, 0, 0, 0, 13, 13, 8, + //par_level_flag + 8, 9, 12, 13, 13, 13, 10, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 10, 13, 13, 13, 13, 8, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, + 6, + //abs_level_gtx_flag + 9, 5, 10, 13, 13, 10, 9, 10, 13, 13, 13, 9, 10, 10, 10, 13, + 8, 9, 10, 10, 13, 8, 8, 9, 12, 12, 10, 5, 9, 9, 9, 13, + 1, 5, 9, 9, 9, 6, 5, 9, 10, 10, 9, 9, 9, 9, 9, 9, + 6, 8, 9, 9, 10, 1, 5, 8, 8, 9, 6, 6, 9, 8, 8, 9, + 4, 2, 1, 6, 1, 1, 1, 1, + //coeff_sign_flag + 1, 4, 4, 5, 8, 8, + } +}; + +#define MAX_SUB_BLOCKS 16 +#define MAX_SUB_BLOCK_SIZE 4 +#define MAX_TB_SIZE 64 + +typedef struct ResidualCoding { + //common for ts and non ts + TransformBlock *tb; + + int log2_sb_w; + int log2_sb_h; + int last_sub_block; + int hist_value; + int update_hist; + int num_sb_coeff; + int rem_bins_pass1; + + int width_in_sbs; + int height_in_sbs; + int nb_sbs; + + const uint8_t *sb_scan_x_off; + const uint8_t *sb_scan_y_off; + const uint8_t *scan_x_off; + const uint8_t *scan_y_off; + + uint8_t sb_coded_flag[MAX_SUB_BLOCKS * MAX_SUB_BLOCKS]; + int sig_coeff_flag[MAX_TB_SIZE * MAX_TB_SIZE]; + int abs_level_pass1[MAX_TB_SIZE * MAX_TB_SIZE]; ///< AbsLevelPass1[][] + int abs_level[MAX_TB_SIZE * MAX_TB_SIZE]; + + //for ts only + uint8_t infer_sb_cbf; + int coeff_sign_level[MAX_TB_SIZE * MAX_TB_SIZE]; ///< CoeffSignLevel[][] + + //for non ts only + int qstate; + int last_scan_pos; + int last_significant_coeff_x; + int last_significant_coeff_y; +} ResidualCoding; + +static int cabac_reinit(VVCLocalContext *lc) +{ + return skip_bytes(&lc->ep->cc, 0) == NULL ? AVERROR_INVALIDDATA : 0; +} + +static void cabac_init_state(VVCLocalContext *lc) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int qp = av_clip_uintp2(lc->sc->sh.slice_qp_y, 6); + int init_type = 2 - rsh->sh_slice_type; + + av_assert0(VVC_CONTEXTS == SYNTAX_ELEMENT_LAST); + + ff_vvc_ep_init_stat_coeff(lc->ep, sps->bit_depth, sps->r->sps_persistent_rice_adaptation_enabled_flag); + + if (rsh->sh_cabac_init_flag && !IS_I(rsh)) + init_type ^= 3; + + for (int i = 0; i < VVC_CONTEXTS; i++) { + VVCCabacState *state = &lc->ep->cabac_state[i]; + const int init_value = init_values[init_type][i]; + const int shift_idx = init_values[3][i]; + const int m = (init_value >> 3) - 4; + const int n = ((init_value & 7) * 18) + 1; + const int pre = av_clip(((m * (qp - 16)) >> 1) + n, 1, 127); + + state->state[0] = pre << 3; + state->state[1] = pre << 7; + state->shift[0] = (shift_idx >> 2 ) + 2; + state->shift[1] = (shift_idx & 3 ) + 3 + state->shift[0]; + } +} + +int ff_vvc_cabac_init(VVCLocalContext *lc, + const int ctu_idx, const int rx, const int ry) +{ + int ret = 0; + const VVCPPS *pps = lc->fc->ps.pps; + const int first_ctb_in_slice = !ctu_idx; + const int first_ctb_in_tile = rx == pps->ctb_to_col_bd[rx] && ry == pps->ctb_to_row_bd[ry]; + + if (first_ctb_in_slice|| first_ctb_in_tile) { + if (lc->sc->nb_eps == 1 && !first_ctb_in_slice) + ret = cabac_reinit(lc); + if (!ret) + cabac_init_state(lc); + } + return ret; +} + +//fixme +static void vvc_refill2(CABACContext* c) { + int i; + unsigned x; +#if !HAVE_FAST_CLZ + x = c->low ^ (c->low - 1); + i = 7 - ff_h264_norm_shift[x >> (CABAC_BITS - 1)]; +#else + i = ff_ctz(c->low) - CABAC_BITS; +#endif + + x = -CABAC_MASK; + +#if CABAC_BITS == 16 + x += (c->bytestream[0] << 9) + (c->bytestream[1] << 1); +#else + x += c->bytestream[0] << 1; +#endif + + c->low += x << i; +#if !UNCHECKED_BITSTREAM_READER + if (c->bytestream < c->bytestream_end) +#endif + c->bytestream += CABAC_BITS / 8; +} + +static int inline vvc_get_cabac(CABACContext *c, VVCCabacState* base, const int ctx) +{ + VVCCabacState *s = base + ctx; + const int qRangeIdx = c->range >> 5; + const int pState = s->state[1] + (s->state[0] << 4); + const int valMps = pState >> 14; + const int RangeLPS = (qRangeIdx * ((valMps ? 32767 - pState : pState) >> 9 ) >> 1) + 4; + int bit, lps_mask; + + c->range -= RangeLPS; + lps_mask = ((c->range<<(CABAC_BITS+1)) - c->low)>>31; + + c->low -= (c->range<<(CABAC_BITS+1)) & lps_mask; + c->range += (RangeLPS - c->range) & lps_mask; + + bit = valMps ^ (lps_mask & 1); + + lps_mask = ff_h264_norm_shift[c->range]; + c->range <<= lps_mask; + c->low <<= lps_mask; + + if (!(c->low & CABAC_MASK)) + vvc_refill2(c); + s->state[0] = s->state[0] - (s->state[0] >> s->shift[0]) + (1023 * bit >> s->shift[0]); + s->state[1] = s->state[1] - (s->state[1] >> s->shift[1]) + (16383 * bit >> s->shift[1]); + return bit; +} + +#define GET_CABAC(ctx) vvc_get_cabac(&lc->ep->cc, lc->ep->cabac_state, ctx) + +//9.3.3.4 Truncated binary (TB) binarization process +static int truncated_binary_decode(VVCLocalContext *lc, const int c_max) +{ + const int n = c_max + 1; + const int k = av_log2(n); + const int u = (1 << (k+1)) - n; + int v = 0; + for (int i = 0; i < k; i++) + v = (v << 1) | get_cabac_bypass(&lc->ep->cc); + if (v >= u) { + v = (v << 1) | get_cabac_bypass(&lc->ep->cc); + v -= u; + } + return v; +} + +// 9.3.3.6 Limited k-th order Exp-Golomb binarization process +static int limited_kth_order_egk_decode(CABACContext *c, const int k, const int max_pre_ext_len, const int trunc_suffix_len) +{ + int pre_ext_len = 0; + int escape_length; + int val = 0; + while ((pre_ext_len < max_pre_ext_len) && get_cabac_bypass(c)) + pre_ext_len++; + if (pre_ext_len == max_pre_ext_len) + escape_length = trunc_suffix_len; + else + escape_length = pre_ext_len + k; + while (escape_length-- > 0) { + val = (val << 1) + get_cabac_bypass(c); + } + val += ((1 << pre_ext_len) - 1) << k; + return val; +} + +static av_always_inline +void get_left_top(const VVCLocalContext *lc, uint8_t *left, uint8_t *top, + const int x0, const int y0, const uint8_t *left_ctx, const uint8_t *top_ctx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y); + const int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int x_cb = x0 >> sps->min_cb_log2_size_y; + const int y_cb = y0 >> sps->min_cb_log2_size_y; + + if (lc->ctb_left_flag || x0b) + *left = SAMPLE_CTB(left_ctx, x_cb - 1, y_cb); + if (lc->ctb_up_flag || y0b) + *top = SAMPLE_CTB(top_ctx, x_cb, y_cb - 1); +} + +static av_always_inline +uint8_t get_inc(VVCLocalContext *lc, const uint8_t *ctx) +{ + uint8_t left = 0, top = 0; + get_left_top(lc, &left, &top, lc->cu->x0, lc->cu->y0, ctx, ctx); + return left + top; +} + +int ff_vvc_sao_merge_flag_decode(VVCLocalContext *lc) +{ + return GET_CABAC(SAO_MERGE_FLAG); +} + +int ff_vvc_sao_type_idx_decode(VVCLocalContext *lc) +{ + if (!GET_CABAC(SAO_TYPE_IDX)) + return SAO_NOT_APPLIED; + + if (!get_cabac_bypass(&lc->ep->cc)) + return SAO_BAND; + return SAO_EDGE; +} + +int ff_vvc_sao_band_position_decode(VVCLocalContext *lc) +{ + int value = get_cabac_bypass(&lc->ep->cc); + + for (int i = 0; i < 4; i++) + value = (value << 1) | get_cabac_bypass(&lc->ep->cc); + return value; +} + +int ff_vvc_sao_offset_abs_decode(VVCLocalContext *lc) +{ + int i = 0; + const int length = (1 << (FFMIN(lc->fc->ps.sps->bit_depth, 10) - 5)) - 1; + + while (i < length && get_cabac_bypass(&lc->ep->cc)) + i++; + return i; +} + +int ff_vvc_sao_offset_sign_decode(VVCLocalContext *lc) +{ + return get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_sao_eo_class_decode(VVCLocalContext *lc) +{ + int ret = get_cabac_bypass(&lc->ep->cc) << 1; + ret |= get_cabac_bypass(&lc->ep->cc); + return ret; +} + +int ff_vvc_alf_ctb_flag(VVCLocalContext *lc, const int rx, const int ry, const int c_idx) +{ + int inc = c_idx * 3; + const VVCFrameContext *fc = lc->fc; + if (lc->ctb_left_flag) { + const ALFParams *left = &CTB(fc->tab.alf, rx - 1, ry); + inc += left->ctb_flag[c_idx]; + } + if (lc->ctb_up_flag) { + const ALFParams *above = &CTB(fc->tab.alf, rx, ry - 1); + inc += above->ctb_flag[c_idx]; + } + return GET_CABAC(ALF_CTB_FLAG + inc); +} + +int ff_vvc_alf_use_aps_flag(VVCLocalContext *lc) +{ + return GET_CABAC(ALF_USE_APS_FLAG); +} + +int ff_vvc_alf_luma_prev_filter_idx(VVCLocalContext *lc) +{ + return truncated_binary_decode(lc, lc->sc->sh.r->sh_num_alf_aps_ids_luma - 1); +} + +int ff_vvc_alf_luma_fixed_filter_idx(VVCLocalContext *lc) +{ + return truncated_binary_decode(lc, 15); +} + +int ff_vvc_alf_ctb_filter_alt_idx(VVCLocalContext *lc, const int c_idx, const int num_chroma_filters) +{ + int i = 0; + const int length = num_chroma_filters - 1; + + while (i < length && GET_CABAC(ALF_CTB_FILTER_ALT_IDX + c_idx - 1)) + i++; + return i; +} + +int ff_vvc_alf_ctb_cc_idc(VVCLocalContext *lc, const int rx, const int ry, const int idx, const int cc_filters_signalled) +{ + int inc = !idx ? ALF_CTB_CC_CB_IDC : ALF_CTB_CC_CR_IDC; + int i = 0; + const VVCFrameContext *fc = lc->fc; + if (lc->ctb_left_flag) { + const ALFParams *left = &CTB(fc->tab.alf, rx - 1, ry); + inc += left->ctb_cc_idc[idx] != 0; + } + if (lc->ctb_up_flag) { + const ALFParams *above = &CTB(fc->tab.alf, rx, ry - 1); + inc += above->ctb_cc_idc[idx] != 0; + } + + if (!GET_CABAC(inc)) + return 0; + i++; + while (i < cc_filters_signalled && get_cabac_bypass(&lc->ep->cc)) + i++; + return i; +} + +int ff_vvc_split_cu_flag(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const int is_chroma, const VVCAllowedSplit *a) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const int is_inside = (x0 + cb_width <= pps->width) && (y0 + cb_height <= pps->height); + + if ((a->btv || a->bth || a->ttv || a->tth || a->qt) && is_inside) + { + uint8_t inc = 0, left_height = cb_height, top_width = cb_width; + + get_left_top(lc, &left_height, &top_width, x0, y0, fc->tab.cb_height[is_chroma], fc->tab.cb_width[is_chroma]); + inc += left_height < cb_height; + inc += top_width < cb_width; + inc += (a->btv + a->bth + a->ttv + a->tth + 2 * a->qt - 1) / 2 * 3; + + return GET_CABAC(SPLIT_CU_FLAG + inc); + + } + return !is_inside; +} + +static int split_qt_flag_decode(VVCLocalContext *lc, const int x0, const int y0, const int ch_type, const int cqt_depth) +{ + const VVCFrameContext *fc = lc->fc; + int inc = 0; + uint8_t depth_left = 0, depth_top = 0; + + get_left_top(lc, &depth_left, &depth_top, x0, y0, fc->tab.cqt_depth[ch_type], fc->tab.cqt_depth[ch_type]); + inc += depth_left > cqt_depth; + inc += depth_top > cqt_depth; + inc += (cqt_depth >= 2) * 3; + + return GET_CABAC(SPLIT_QT_FLAG + inc); +} + +static int mtt_split_cu_vertical_flag_decode(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const int ch_type, const VVCAllowedSplit* a) +{ + if ((a->bth || a->tth) && (a->btv || a->ttv)) { + int inc; + const int v = a->btv + a->ttv; + const int h = a->bth + a->tth; + if (v > h) + inc = 4; + else if (v < h) + inc = 3; + else { + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y); + const int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int x_cb = x0 >> sps->min_cb_log2_size_y; + const int y_cb = y0 >> sps->min_cb_log2_size_y; + const int available_a = lc->ctb_up_flag || y0b; + const int available_l = lc->ctb_left_flag || x0b; + const int da = cb_width / (available_a ? SAMPLE_CTB(fc->tab.cb_width[ch_type], x_cb, y_cb - 1) : 1); + const int dl = cb_height / (available_l ? SAMPLE_CTB(fc->tab.cb_height[ch_type], x_cb - 1, y_cb) : 1); + + if (da == dl || !available_a || !available_l) + inc = 0; + else if (da < dl) + inc = 1; + else + inc = 2; + } + return GET_CABAC(MTT_SPLIT_CU_VERTICAL_FLAG + inc); + } + return !(a->bth || a->tth); +} + +static int mtt_split_cu_binary_flag_decode(VVCLocalContext *lc, const int mtt_split_cu_vertical_flag, const int mtt_depth) +{ + const int inc = (2 * mtt_split_cu_vertical_flag) + ((mtt_depth <= 1) ? 1 : 0); + return GET_CABAC(MTT_SPLIT_CU_BINARY_FLAG + inc); +} + +VVCSplitMode ff_vvc_split_mode(VVCLocalContext *lc, const int x0, const int y0, const int cb_width, const int cb_height, + const int cqt_depth, const int mtt_depth, const int ch_type, const VVCAllowedSplit *a) +{ + const int allow_no_qt = a->btv || a->bth || a->ttv || a->tth; + int split_qt_flag; + int mtt_split_cu_vertical_flag; + int mtt_split_cu_binary_flag; + const VVCSplitMode mtt_split_modes[] = { + SPLIT_TT_HOR, SPLIT_BT_HOR, SPLIT_TT_VER, SPLIT_BT_VER, + }; + if (allow_no_qt && a->qt) { + split_qt_flag = split_qt_flag_decode(lc, x0, y0, ch_type, cqt_depth); + } else { + split_qt_flag = !allow_no_qt || a->qt; + } + if (split_qt_flag) + return SPLIT_QT; + mtt_split_cu_vertical_flag = mtt_split_cu_vertical_flag_decode(lc, x0, y0, cb_width, cb_height, ch_type, a); + if ((a->btv && a->ttv && mtt_split_cu_vertical_flag) || + (a->bth && a->tth && !mtt_split_cu_vertical_flag)) { + mtt_split_cu_binary_flag = mtt_split_cu_binary_flag_decode(lc, mtt_split_cu_vertical_flag, mtt_depth); + } else { + if (!a->btv && !a->bth) + mtt_split_cu_binary_flag = 0; + else if (!a->ttv && !a->tth) + mtt_split_cu_binary_flag = 1; + else if (a->bth && a->ttv) + mtt_split_cu_binary_flag = 1 - mtt_split_cu_vertical_flag; + else + mtt_split_cu_binary_flag = mtt_split_cu_vertical_flag; + } + return mtt_split_modes[(mtt_split_cu_vertical_flag << 1) + mtt_split_cu_binary_flag]; +} + +int ff_vvc_non_inter_flag(VVCLocalContext *lc, const int x0, const int y0, const int ch_type) +{ + const VVCFrameContext *fc = lc->fc; + uint8_t inc, left = MODE_INTER, top = MODE_INTER; + + get_left_top(lc, &left, &top, x0, y0, fc->tab.cpm[ch_type], fc->tab.cpm[ch_type]); + inc = left == MODE_INTRA || top == MODE_INTRA; + return GET_CABAC(NON_INTER_FLAG + inc); +} + +int ff_vvc_pred_mode_flag(VVCLocalContext *lc, const int is_chroma) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + uint8_t inc, left = MODE_INTER, top = MODE_INTER; + + get_left_top(lc, &left, &top, cu->x0, cu->y0, fc->tab.cpm[is_chroma], fc->tab.cpm[is_chroma]); + inc = left == MODE_INTRA || top == MODE_INTRA; + return GET_CABAC(PRED_MODE_FLAG + inc); +} + +int ff_vvc_pred_mode_plt_flag(VVCLocalContext *lc) +{ + return GET_CABAC(PRED_MODE_PLT_FLAG); +} + +int ff_vvc_intra_bdpcm_luma_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_BDPCM_LUMA_FLAG); +} + +int ff_vvc_intra_bdpcm_luma_dir_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_BDPCM_LUMA_DIR_FLAG); +} + +int ff_vvc_intra_bdpcm_chroma_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_BDPCM_CHROMA_FLAG); +} + +int ff_vvc_intra_bdpcm_chroma_dir_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_BDPCM_CHROMA_DIR_FLAG); +} + +int ff_vvc_cu_skip_flag(VVCLocalContext *lc, const uint8_t *cu_skip_flag) +{ + const int inc = get_inc(lc, cu_skip_flag); + return GET_CABAC(CU_SKIP_FLAG + inc); +} + +int ff_vvc_pred_mode_ibc_flag(VVCLocalContext *lc, const int is_chroma) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + uint8_t left_mode = MODE_INTER, top_mode = MODE_INTER; + int inc; + + get_left_top(lc, &left_mode, &top_mode, cu->x0, cu->y0, fc->tab.cpm[is_chroma], fc->tab.cpm[is_chroma]); + inc = (left_mode == MODE_IBC) + (top_mode == MODE_IBC); + return GET_CABAC(PRED_MODE_IBC_FLAG + inc); +} + +int ff_vvc_intra_mip_flag(VVCLocalContext *lc, const uint8_t *intra_mip_flag) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + const int inc = (w > h * 2 || h > w * 2) ? 3 : get_inc(lc, intra_mip_flag); + return GET_CABAC(INTRA_MIP_FLAG + inc); +} + +int ff_vvc_intra_mip_transposed_flag(VVCLocalContext *lc) +{ + return get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_intra_mip_mode(VVCLocalContext *lc) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + const int c_max = (w == 4 && h == 4) ? 15 : + ((w == 4 || h == 4) || (w == 8 && h == 8)) ? 7: 5; + return truncated_binary_decode(lc, c_max); +} + +int ff_vvc_intra_luma_ref_idx(VVCLocalContext *lc) +{ + int i; + for (i = 0; i < 2; i++) { + if (!GET_CABAC(INTRA_LUMA_REF_IDX + i)) + return i; + } + return i; +} + +int ff_vvc_intra_subpartitions_mode_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_SUBPARTITIONS_MODE_FLAG); +} + +enum IspType ff_vvc_isp_split_type(VVCLocalContext *lc, const int intra_subpartitions_mode_flag) +{ + if (!intra_subpartitions_mode_flag) + return ISP_NO_SPLIT; + return 1 + GET_CABAC(INTRA_SUBPARTITIONS_SPLIT_FLAG); +} + +int ff_vvc_intra_luma_mpm_flag(VVCLocalContext *lc) +{ + return GET_CABAC(INTRA_LUMA_MPM_FLAG); +} + +int ff_vvc_intra_luma_not_planar_flag(VVCLocalContext *lc, const int intra_subpartitions_mode_flag) +{ + return GET_CABAC(INTRA_LUMA_NOT_PLANAR_FLAG + !intra_subpartitions_mode_flag); +} + +int ff_vvc_intra_luma_mpm_idx(VVCLocalContext *lc) +{ + int i; + for (i = 0; i < 4 && get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + return i; +} + +int ff_vvc_intra_luma_mpm_remainder(VVCLocalContext *lc) +{ + return truncated_binary_decode(lc, 60); +} + +int ff_vvc_cclm_mode_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CCLM_MODE_FLAG); +} + +int ff_vvc_cclm_mode_idx(VVCLocalContext *lc) +{ + if (!GET_CABAC(CCLM_MODE_IDX)) + return 0; + return get_cabac_bypass(&lc->ep->cc) + 1; +} + +int ff_vvc_intra_chroma_pred_mode(VVCLocalContext *lc) +{ + if (!GET_CABAC(INTRA_CHROMA_PRED_MODE)) + return 4; + return (get_cabac_bypass(&lc->ep->cc) << 1) | get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_general_merge_flag(VVCLocalContext *lc) +{ + return GET_CABAC(GENERAL_MERGE_FLAG); +} + +static int get_inter_flag_inc(VVCLocalContext *lc, const int x0, const int y0) +{ + uint8_t left_merge = 0, top_merge = 0; + uint8_t left_affine = 0, top_affine = 0; + const VVCFrameContext *fc = lc->fc; + + get_left_top(lc, &left_merge, &top_merge, x0, y0, fc->tab.msf, fc->tab.msf); + get_left_top(lc, &left_affine, &top_affine, x0, y0, fc->tab.iaf, fc->tab.iaf); + return (left_merge || left_affine) + (top_merge + top_affine); +} + +int ff_vvc_merge_subblock_flag(VVCLocalContext *lc) +{ + const int inc = get_inter_flag_inc(lc, lc->cu->x0, lc->cu->y0); + return GET_CABAC(MERGE_SUBBLOCK_FLAG + inc); +} + +int ff_vvc_merge_subblock_idx(VVCLocalContext *lc, const int max_num_subblock_merge_cand) +{ + int i; + if (!GET_CABAC(MERGE_SUBBLOCK_IDX)) + return 0; + for (i = 1; i < max_num_subblock_merge_cand - 1 && get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + return i; +} + +int ff_vvc_regular_merge_flag(VVCLocalContext *lc, const int cu_skip_flag) +{ + int inc = !cu_skip_flag; + return GET_CABAC(REGULAR_MERGE_FLAG + inc); +} + +int ff_vvc_mmvd_merge_flag(VVCLocalContext *lc) +{ + return GET_CABAC(MMVD_MERGE_FLAG); +} + +int ff_vvc_mmvd_cand_flag(VVCLocalContext *lc) +{ + return GET_CABAC(MMVD_CAND_FLAG); +} + +static int mmvd_distance_idx_decode(VVCLocalContext *lc) +{ + int i; + if (!GET_CABAC(MMVD_DISTANCE_IDX)) + return 0; + for (i = 1; i < 7 && get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + return i; +} + +static int mmvd_direction_idx_decode(VVCLocalContext *lc) +{ + return (get_cabac_bypass(&lc->ep->cc) << 1) | get_cabac_bypass(&lc->ep->cc); +} + +void ff_vvc_mmvd_offset_coding(VVCLocalContext *lc, Mv *mmvd_offset, const int ph_mmvd_fullpel_only_flag) +{ + const int shift = ph_mmvd_fullpel_only_flag ? 4 : 2; + const int mmvd_distance = 1 << (mmvd_distance_idx_decode(lc) + shift); + const int mmvd_direction_idx = mmvd_direction_idx_decode(lc); + const int mmvd_signs[][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} }; + mmvd_offset->x = mmvd_distance * mmvd_signs[mmvd_direction_idx][0]; + mmvd_offset->y = mmvd_distance * mmvd_signs[mmvd_direction_idx][1]; +} + +static PredMode get_luma_pred_mode(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + PredMode pred_mode; + + if (cu->tree_type != DUAL_TREE_CHROMA) { + pred_mode = cu->pred_mode; + } else { + const int x_cb = cu->x0 >> fc->ps.sps->min_cb_log2_size_y; + const int y_cb = cu->y0 >> fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + pred_mode = SAMPLE_CTB(fc->tab.cpm[0], x_cb, y_cb); + } + return pred_mode; +} + +int ff_vvc_merge_idx(VVCLocalContext *lc) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const int is_ibc = get_luma_pred_mode(lc) == MODE_IBC; + const int c_max = (is_ibc ? sps->max_num_ibc_merge_cand : sps->max_num_merge_cand) - 1; + int i; + + if (!GET_CABAC(MERGE_IDX)) + return 0; + + for (i = 1; i < c_max && get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + return i; +} + +int ff_vvc_merge_gpm_partition_idx(VVCLocalContext *lc) +{ + int i = 0; + + for (int j = 0; j < 6; j++) + i = (i << 1) | get_cabac_bypass(&lc->ep->cc); + + return i; +} + +int ff_vvc_merge_gpm_idx(VVCLocalContext *lc, const int idx) +{ + const int c_max = lc->fc->ps.sps->max_num_gpm_merge_cand - idx - 1; + int i; + + if (!GET_CABAC(MERGE_IDX)) + return 0; + + for (i = 1; i < c_max && get_cabac_bypass(&lc->ep->cc); i++) + /* nothing */; + + return i; +} + +int ff_vvc_ciip_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CIIP_FLAG); +} + +PredFlag ff_vvc_pred_flag(VVCLocalContext *lc, const int is_b) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + if (!is_b) + return PF_L0; + if (w + h > 12) { + const int log2 = av_log2(w) + av_log2(h); + const int inc = 7 - ((1 + log2)>>1); + if (GET_CABAC(INTER_PRED_IDC + inc)) + return PF_BI; + } + return PF_L0 + GET_CABAC(INTER_PRED_IDC + 5); +} + +int ff_vvc_inter_affine_flag(VVCLocalContext *lc) +{ + const int inc = get_inter_flag_inc(lc, lc->cu->x0, lc->cu->y0); + return GET_CABAC(INTER_AFFINE_FLAG + inc); +} + +int ff_vvc_cu_affine_type_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_AFFINE_TYPE_FLAG); +} + +int ff_vvc_sym_mvd_flag(VVCLocalContext *lc) +{ + return GET_CABAC(SYM_MVD_FLAG); +} + +int ff_vvc_ref_idx_lx(VVCLocalContext *lc, const uint8_t nb_refs) +{ + const int c_max = nb_refs - 1; + const int max_ctx = FFMIN(c_max, 2); + int i = 0; + + while (i < max_ctx && GET_CABAC(REF_IDX_LX + i)) + i++; + if (i == 2) { + while (i < c_max && get_cabac_bypass(&lc->ep->cc)) + i++; + } + return i; +} + +int ff_vvc_abs_mvd_greater0_flag(VVCLocalContext *lc) +{ + return GET_CABAC(ABS_MVD_GREATER0_FLAG); +} + +int ff_vvc_abs_mvd_greater1_flag(VVCLocalContext *lc) +{ + return GET_CABAC(ABS_MVD_GREATER1_FLAG); +} + +int ff_vvc_abs_mvd_minus2(VVCLocalContext *lc) +{ + return limited_kth_order_egk_decode(&lc->ep->cc, 1, 15, 17); +} + +int ff_vvc_mvd_sign_flag(VVCLocalContext *lc) +{ + return get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_mvp_lx_flag(VVCLocalContext *lc) +{ + return GET_CABAC(MVP_LX_FLAG); +} + +static int amvr_flag(VVCLocalContext *lc, const int inter_affine_flag) +{ + return GET_CABAC(AMVR_FLAG + inter_affine_flag); +} + +static int amvr_precision_idx(VVCLocalContext *lc, const int inc, const int c_max) +{ + int i = 0; + if (!GET_CABAC(AMVR_PRECISION_IDX + inc)) + return 0; + i++; + if (i < c_max && GET_CABAC(AMVR_PRECISION_IDX + 1)) + i++; + return i; +} + +int ff_vvc_amvr_shift(VVCLocalContext *lc, const int inter_affine_flag, + const PredMode pred_mode, const int has_amvr_flag) +{ + int amvr_shift = 2; + if (has_amvr_flag) { + if (pred_mode == MODE_IBC || amvr_flag(lc, inter_affine_flag)) { + int idx; + if (inter_affine_flag) { + idx = amvr_precision_idx(lc, 2, 1); + amvr_shift = idx * 4; + } else if (pred_mode == MODE_IBC) { + idx = amvr_precision_idx(lc, 1, 1); + amvr_shift = 4 + idx * 2; + } else { + static const int shifts[] = {3, 4, 6}; + idx = amvr_precision_idx(lc, 0, 2); + amvr_shift = shifts[idx]; + } + } + } + return amvr_shift; +} + +int ff_vvc_bcw_idx(VVCLocalContext *lc, const int no_backward_pred_flag) +{ + const int c_max = no_backward_pred_flag ? 4 : 2; + int i = 1; + if (!GET_CABAC(BCW_IDX)) + return 0; + while (i < c_max && get_cabac_bypass(&lc->ep->cc)) + i++; + return i; +} + +int ff_vvc_tu_cb_coded_flag(VVCLocalContext *lc) +{ + return GET_CABAC(TU_CB_CODED_FLAG + lc->cu->bdpcm_flag[1]); +} + +int ff_vvc_tu_cr_coded_flag(VVCLocalContext *lc, int tu_cb_coded_flag) +{ + return GET_CABAC(TU_CR_CODED_FLAG + (lc->cu->bdpcm_flag[1] ? 2 : tu_cb_coded_flag)); +} + +int ff_vvc_tu_y_coded_flag(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + int inc; + if (cu->bdpcm_flag[0]) + inc = 1; + else if (cu->isp_split_type == ISP_NO_SPLIT) + inc = 0; + else + inc = 2 + lc->parse.prev_tu_cbf_y; + lc->parse.prev_tu_cbf_y = GET_CABAC(TU_Y_CODED_FLAG + inc); + return lc->parse.prev_tu_cbf_y; +} + +int ff_vvc_cu_qp_delta_abs(VVCLocalContext *lc) +{ + int v, i, k; + if (!GET_CABAC(CU_QP_DELTA_ABS)) + return 0; + + // prefixVal + for (v = 1; v < 5 && GET_CABAC(CU_QP_DELTA_ABS + 1); v++) + /* nothing */; + if (v < 5) + return v; + + // 9.3.3.5 k-th order Exp-Golomb binarization process + // suffixVal + + // CuQpDeltaVal shall in the range of −( 32 + QpBdOffset / 2 ) to +( 31 + QpBdOffset / 2 ) + // so k = 6 should enough + for (k = 0; k < 6 && get_cabac_bypass(&lc->ep->cc); k++) + /* nothing */; + i = (1 << k) - 1; + v = 0; + while (k--) + v = (v << 1) + get_cabac_bypass(&lc->ep->cc); + v += i; + + return v + 5; +} + +int ff_vvc_cu_qp_delta_sign_flag(VVCLocalContext *lc) +{ + return get_cabac_bypass(&lc->ep->cc); +} + +int ff_vvc_cu_chroma_qp_offset_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_CHROMA_QP_OFFSET_FLAG); +} + +int ff_vvc_cu_chroma_qp_offset_idx(VVCLocalContext *lc) +{ + const int c_max = lc->fc->ps.pps->r->pps_chroma_qp_offset_list_len_minus1; + int i; + for (i = 0; i < c_max && GET_CABAC(CU_CHROMA_QP_OFFSET_IDX); i++) + /* nothing */; + return i; +} + +static av_always_inline int last_significant_coeff_xy_prefix(VVCLocalContext *lc, + const int log2_tb_size, const int log2_zo_tb_size, const int c_idx, const int ctx) +{ + int i = 0; + int max = (log2_zo_tb_size << 1) - 1; + int ctx_offset, ctx_shift; + if (!log2_tb_size) + return 0; + if (!c_idx) { + const int offset_y[] = {0, 0, 3, 6, 10, 15}; + ctx_offset = offset_y[log2_tb_size - 1]; + ctx_shift = (log2_tb_size + 1) >> 2; + } else { + const int shifts[] = {0, 0, 0, 1, 2, 2, 2}; + ctx_offset = 20; + ctx_shift = shifts[log2_tb_size]; + } + while (i < max && GET_CABAC(ctx + (i >> ctx_shift) + ctx_offset)) + i++; + return i; +} + +static av_always_inline int last_significant_coeff_x_prefix_decode(VVCLocalContext *lc, + const int log2_tb_width, const int log2_zo_tb_width, const int c_idx) +{ + return last_significant_coeff_xy_prefix(lc, log2_tb_width, log2_zo_tb_width, c_idx, LAST_SIG_COEFF_X_PREFIX); +} + +static av_always_inline int last_significant_coeff_y_prefix_decode(VVCLocalContext *lc, + const int log2_tb_height, const int log2_zo_tb_height, const int c_idx) +{ + return last_significant_coeff_xy_prefix(lc, log2_tb_height, log2_zo_tb_height, c_idx, LAST_SIG_COEFF_Y_PREFIX); +} + +static av_always_inline int last_sig_coeff_suffix_decode(VVCLocalContext *lc, + const int last_significant_coeff_y_prefix) +{ + const int length = (last_significant_coeff_y_prefix >> 1) - 1; + int value = get_cabac_bypass(&lc->ep->cc); + + for (int i = 1; i < length; i++) + value = (value << 1) | get_cabac_bypass(&lc->ep->cc); + return value; +} + +int ff_vvc_tu_joint_cbcr_residual_flag(VVCLocalContext *lc, const int tu_cb_coded_flag, const int tu_cr_coded_flag) +{ + return GET_CABAC(TU_JOINT_CBCR_RESIDUAL_FLAG + 2 * tu_cb_coded_flag + tu_cr_coded_flag - 1); +} + +int ff_vvc_transform_skip_flag(VVCLocalContext *lc, const int inc) +{ + return GET_CABAC(TRANSFORM_SKIP_FLAG + inc); +} + +//9.3.4.2.7 Derivation process for the variables locNumSig, locSumAbsPass1 +static int get_local_sum(const int *level, const int w, const int h, + const int xc, const int yc, const int hist_value) +{ + int loc_sum = 3 * hist_value; + level += w * yc + xc; + if (xc < w - 1) { + loc_sum += level[1]; + if (xc < w - 2) + loc_sum += level[2] - hist_value; + if (yc < h - 1) + loc_sum += level[w + 1] - hist_value; + } + if (yc < h - 1) { + loc_sum += level[w]; + if (yc < h - 2) + loc_sum += level[w << 1] - hist_value; + } + return loc_sum; +} + +//9.3.4.2.7 Derivation process for the variables locNumSig, locSumAbsPass1 +static int get_local_sum_ts(const int *level, const int w, const int h, const int xc, const int yc) +{ + int loc_sum = 0; + level += w * yc + xc; + if (xc > 0) + loc_sum += level[-1]; + if (yc > 0) + loc_sum += level[-w]; + return loc_sum; +} + +static int get_gtx_flag_inc(const ResidualCoding* rc, const int xc, const int yc, const int last) +{ + const TransformBlock *tb = rc->tb; + int inc; + if (last) { + const int incs[] = {0, 21, 21}; + inc = incs[tb->c_idx]; + } else { + const int d = xc + yc; + const int local_sum_sig = get_local_sum(rc->sig_coeff_flag, + tb->tb_width,tb->tb_height, xc, yc, rc->hist_value); + const int loc_sum_abs_pass1 = get_local_sum(rc->abs_level_pass1, + tb->tb_width, tb->tb_height, xc, yc, rc->hist_value); + const int offset = FFMIN(loc_sum_abs_pass1 - local_sum_sig, 4); + + if (!tb->c_idx) + inc = 1 + offset + (!d ? 15 : (d < 3 ? 10 : (d < 10 ? 5 : 0))); + else + inc = 22 + offset + (!d ? 5 : 0); + } + return inc; +} + +static int abs_level_gtx_flag_decode(VVCLocalContext *lc, const int inc) +{ + return GET_CABAC(ABS_LEVEL_GTX_FLAG + inc); +} + +static int par_level_flag_decode(VVCLocalContext *lc, const int inc) +{ + return GET_CABAC(PAR_LEVEL_FLAG + inc); +} + +static int par_level_flag_ts_decode(VVCLocalContext *lc) +{ + const int inc = 32; + return GET_CABAC(PAR_LEVEL_FLAG + inc); +} + +static int sb_coded_flag_decode(VVCLocalContext *lc, const uint8_t *sb_coded_flag, + const ResidualCoding *rc, const int xs, const int ys) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const TransformBlock *tb = rc->tb; + const int w = rc->width_in_sbs; + const int h = rc->height_in_sbs; + int inc; + + if (tb->ts && !rsh->sh_ts_residual_coding_disabled_flag) { + const int left = xs > 0 ? sb_coded_flag[-1] : 0; + const int above = ys > 0 ? sb_coded_flag[-w] : 0; + inc = left + above + 4; + } else { + const int right = (xs < w - 1) ? sb_coded_flag[1] : 0; + const int bottom = (ys < h - 1) ? sb_coded_flag[w] : 0; + inc = (right | bottom) + (tb->c_idx ? 2 : 0); + } + return GET_CABAC(SB_CODED_FLAG + inc); +} + +static int sig_coeff_flag_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const TransformBlock *tb = rc->tb; + int inc; + + if (tb->ts && !rsh->sh_ts_residual_coding_disabled_flag) { + const int local_num_sig = get_local_sum_ts(rc->sig_coeff_flag, tb->tb_width, tb->tb_height, xc, yc); + inc = 60 + local_num_sig; + } else { + const int d = xc + yc; + const int loc_sum_abs_pass1 = get_local_sum(rc->abs_level_pass1, + tb->tb_width, tb->tb_height, xc, yc, 0); + + if (!tb->c_idx) { + inc = 12 * FFMAX(0, rc->qstate - 1) + FFMIN((loc_sum_abs_pass1 + 1) >> 1, 3) + ((d < 2) ? 8 : (d < 5 ? 4 : 0)); + } else { + inc = 36 + 8 * FFMAX(0, rc->qstate - 1) + FFMIN((loc_sum_abs_pass1 + 1) >> 1, 3) + (d < 2 ? 4 : 0); + } + } + return GET_CABAC(SIG_COEFF_FLAG + inc); +} + +static int abs_get_rice_param(VVCLocalContext *lc, const ResidualCoding* rc, + const int xc, const int yc, const int base_level) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const TransformBlock* tb = rc->tb; + const int rice_params[] = { + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, + }; + int loc_sum_abs; + int shift_val; + + loc_sum_abs = get_local_sum(rc->abs_level, tb->tb_width, tb->tb_height, xc, + yc, rc->hist_value); + + if (!sps->r->sps_rrc_rice_extension_flag) { + shift_val = 0; + } else { + shift_val = (av_log2(FFMAX(FFMIN(loc_sum_abs, 2048), 8)) - 3) & ~1; + } + + loc_sum_abs = av_clip_uintp2((loc_sum_abs >> shift_val) - base_level * 5, 5); + + return rice_params[loc_sum_abs] + shift_val; +} + +static int abs_decode(VVCLocalContext *lc, const int c_rice_param) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const int MAX_BIN = 6; + int prefix = 0; + int suffix = 0; + + while (prefix < MAX_BIN && get_cabac_bypass(&lc->ep->cc)) + prefix++; + if (prefix < MAX_BIN) { + for (int i = 0; i < c_rice_param; i++) { + suffix = (suffix << 1) | get_cabac_bypass(&lc->ep->cc); + } + } else { + suffix = limited_kth_order_egk_decode(&lc->ep->cc, + c_rice_param + 1, + 26 - sps->log2_transform_range, + sps->log2_transform_range); + } + return suffix + (prefix << c_rice_param); +} + +static int abs_remainder_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int base_level[][2][2] = { + { {4, 4}, {4, 4} }, + { {3, 2}, {2, 1} } + }; + const int c_rice_param = abs_get_rice_param(lc, rc, xc, yc, + base_level[sps->r->sps_rrc_rice_extension_flag][sps->bit_depth > 12][IS_I(rsh)]); + const int rem = abs_decode(lc, c_rice_param); + + return rem; +} + +static int abs_remainder_ts_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int c_rice_param = rsh->sh_ts_residual_coding_rice_idx_minus1 + 1; + const int rem = abs_decode(lc, c_rice_param); + + return rem; +} + +static int coeff_sign_flag_decode(VVCLocalContext *lc) +{ + return get_cabac_bypass(&lc->ep->cc); +} + +//9.3.4.2.10 Derivation process of ctxInc for the syntax element coeff_sign_flag for transform skip mode +static int coeff_sign_flag_ts_decode(VVCLocalContext *lc, const CodingUnit *cu, const ResidualCoding *rc, const int xc, const int yc) +{ + const TransformBlock *tb = rc->tb; + const int w = tb->tb_width; + const int *level = rc->coeff_sign_level + yc * w + xc; + const int left_sign = xc ? level[-1] : 0; + const int above_sign = yc ? level[-w] : 0; + const int bdpcm_flag = cu->bdpcm_flag[tb->c_idx]; + int inc; + + if (left_sign == -above_sign) + inc = bdpcm_flag ? 3 : 0; + else if (left_sign >= 0 && above_sign >= 0) + inc = bdpcm_flag ? 4 : 1; + else + inc = bdpcm_flag ? 5 : 2; + return GET_CABAC(COEFF_SIGN_FLAG + inc); +} + +static int abs_level_gt1_flag_ts_decode(VVCLocalContext *lc, const CodingUnit *cu, const ResidualCoding *rc, const int xc, const int yc) +{ + const TransformBlock *tb = rc->tb; + const int *sig_coeff_flag = rc->sig_coeff_flag + yc * tb->tb_width + xc; + int inc; + + if (cu->bdpcm_flag[tb->c_idx]) { + inc = 67; + } else { + const int l = xc > 0 ? sig_coeff_flag[-1] : 0; + const int a = yc > 0 ? sig_coeff_flag[-tb->tb_width] : 0; + inc = 64 + a + l; + } + return GET_CABAC(ABS_LEVEL_GTX_FLAG + inc); +} + +static int abs_level_gtx_flag_ts_decode(VVCLocalContext *lc, const int j) +{ + const int inc = 67 + j; + return GET_CABAC(ABS_LEVEL_GTX_FLAG + inc); +} + +static const uint8_t qstate_translate_table[][2] = { + { 0, 2 }, { 2, 0 }, { 1, 3 }, { 3, 1 } +}; + +static int dec_abs_level_decode(VVCLocalContext *lc, const ResidualCoding *rc, + const int xc, const int yc, int *abs_level) +{ + const int c_rice_param = abs_get_rice_param(lc, rc, xc, yc, 0); + const int dec_abs_level = abs_decode(lc, c_rice_param); + const int zero_pos = (rc->qstate < 2 ? 1 : 2) << c_rice_param; + + *abs_level = 0; + if (dec_abs_level != zero_pos) { + *abs_level = dec_abs_level; + if (dec_abs_level < zero_pos) + *abs_level += 1; + } + return dec_abs_level; +} + +static void ep_update_hist(EntryPoint *ep, ResidualCoding *rc, + const int remainder, const int addin) +{ + int *stat = ep->stat_coeff + rc->tb->c_idx; + if (rc->update_hist && remainder > 0) { + *stat = (*stat + av_log2(remainder) + addin) >> 1; + rc->update_hist = 0; + } +} + +static void init_residual_coding(const VVCLocalContext *lc, ResidualCoding *rc, + const int log2_zo_tb_width, const int log2_zo_tb_height, + TransformBlock *tb) +{ + const VVCSPS *sps = lc->fc->ps.sps; + int log2_sb_w = (FFMIN(log2_zo_tb_width, log2_zo_tb_height ) < 2 ? 1 : 2 ); + int log2_sb_h = log2_sb_w; + + if ( log2_zo_tb_width + log2_zo_tb_height > 3 ) { + if ( log2_zo_tb_width < 2 ) { + log2_sb_w = log2_zo_tb_width; + log2_sb_h = 4 - log2_sb_w; + } else if ( log2_zo_tb_height < 2 ) { + log2_sb_h = log2_zo_tb_height; + log2_sb_w = 4 - log2_sb_h; + } + } + rc->log2_sb_w = log2_sb_w; + rc->log2_sb_h = log2_sb_h; + rc->num_sb_coeff = 1 << (log2_sb_w + log2_sb_h); + rc->last_sub_block = ( 1 << ( log2_zo_tb_width + log2_zo_tb_height - (log2_sb_w + log2_sb_h))) - 1; + rc->hist_value = sps->r->sps_persistent_rice_adaptation_enabled_flag ? (1 << lc->ep->stat_coeff[tb->c_idx]) : 0; + rc->update_hist = sps->r->sps_persistent_rice_adaptation_enabled_flag ? 1 : 0; + rc->rem_bins_pass1 = (( 1 << ( log2_zo_tb_width + log2_zo_tb_height)) * 7 ) >> 2; + + + rc->sb_scan_x_off = ff_vvc_diag_scan_x[log2_zo_tb_width - log2_sb_w][log2_zo_tb_height - log2_sb_h]; + rc->sb_scan_y_off = ff_vvc_diag_scan_y[log2_zo_tb_width - log2_sb_w][log2_zo_tb_height - log2_sb_h]; + + rc->scan_x_off = ff_vvc_diag_scan_x[log2_sb_w][log2_sb_h]; + rc->scan_y_off = ff_vvc_diag_scan_y[log2_sb_w][log2_sb_h]; + + rc->infer_sb_cbf = 1; + + rc->width_in_sbs = (1 << (log2_zo_tb_width - log2_sb_w)); + rc->height_in_sbs = (1 << (log2_zo_tb_height - log2_sb_h)); + rc->nb_sbs = rc->width_in_sbs * rc->height_in_sbs; + + rc->last_scan_pos = rc->num_sb_coeff; + rc->qstate = 0; + + rc->tb = tb; +} + +static int residual_ts_coding_subblock(VVCLocalContext *lc, ResidualCoding* rc, const int i) +{ + const CodingUnit *cu = lc->cu; + TransformBlock *tb = rc->tb; + const int bdpcm_flag = cu->bdpcm_flag[tb->c_idx]; + const int xs = rc->sb_scan_x_off[i]; + const int ys = rc->sb_scan_y_off[i]; + uint8_t *sb_coded_flag = rc->sb_coded_flag + ys * rc->width_in_sbs + xs; + int infer_sb_sig_coeff_flag = 1; + int last_scan_pos_pass1 = -1, last_scan_pos_pass2 = -1, n; + int abs_level_gtx_flag[MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE]; + int abs_level_pass2[MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE]; ///< AbsLevelPass2 + + if (i != rc->last_sub_block || !rc->infer_sb_cbf) + *sb_coded_flag = sb_coded_flag_decode(lc, sb_coded_flag, rc, xs, ys); + else + *sb_coded_flag = 1; + if (*sb_coded_flag && i < rc->last_sub_block) + rc->infer_sb_cbf = 0; + + //first scan pass + for (n = 0; n < rc->num_sb_coeff && rc->rem_bins_pass1 >= 4; n++) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int off = yc * tb->tb_width + xc; + int *sig_coeff_flag = rc->sig_coeff_flag + off; + int *abs_level_pass1 = rc->abs_level_pass1 + off; + int *coeff_sign_level = rc->coeff_sign_level + off; + int par_level_flag = 0; + + abs_level_gtx_flag[n] = 0; + last_scan_pos_pass1 = n; + if (*sb_coded_flag && (n != rc->num_sb_coeff - 1 || !infer_sb_sig_coeff_flag)) { + *sig_coeff_flag = sig_coeff_flag_decode(lc, rc, xc, yc); + rc->rem_bins_pass1--; + if (*sig_coeff_flag) + infer_sb_sig_coeff_flag = 0; + } else { + *sig_coeff_flag = (n == rc->num_sb_coeff - 1) && infer_sb_sig_coeff_flag && *sb_coded_flag; + } + *coeff_sign_level = 0; + if (*sig_coeff_flag) { + *coeff_sign_level = 1 - 2 * coeff_sign_flag_ts_decode(lc, cu, rc, xc, yc); + abs_level_gtx_flag[n] = abs_level_gt1_flag_ts_decode(lc, cu, rc, xc, yc); + rc->rem_bins_pass1 -= 2; + if (abs_level_gtx_flag[n]) { + par_level_flag = par_level_flag_ts_decode(lc); + rc->rem_bins_pass1--; + } + } + *abs_level_pass1 = *sig_coeff_flag + par_level_flag + abs_level_gtx_flag[n]; + } + + //greater than x scan pass + for (n = 0; n < rc->num_sb_coeff && rc->rem_bins_pass1 >= 4; n++) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int off = yc * tb->tb_width + xc; + + abs_level_pass2[n] = rc->abs_level_pass1[off]; + for (int j = 1; j < 5 && abs_level_gtx_flag[n]; j++) { + abs_level_gtx_flag[n] = abs_level_gtx_flag_ts_decode(lc, j); + abs_level_pass2[n] += abs_level_gtx_flag[n] << 1; + rc->rem_bins_pass1--; + } + last_scan_pos_pass2 = n; + } + + /* remainder scan pass */ + for (n = 0; n < rc->num_sb_coeff; n++) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int off = yc * tb->tb_width + xc; + const int *abs_level_pass1 = rc->abs_level_pass1 + off; + int *abs_level = rc->abs_level + off; + int *coeff_sign_level = rc->coeff_sign_level + off; + int abs_remainder = 0; + + if ((n <= last_scan_pos_pass2 && abs_level_pass2[n] >= 10) || + (n > last_scan_pos_pass2 && n <= last_scan_pos_pass1 && + *abs_level_pass1 >= 2) || + (n > last_scan_pos_pass1 && *sb_coded_flag)) + abs_remainder = abs_remainder_ts_decode(lc, rc, xc, yc); + if (n <= last_scan_pos_pass2) { + *abs_level = abs_level_pass2[n] + 2 * abs_remainder; + } else if (n <= last_scan_pos_pass1) { + *abs_level = *abs_level_pass1 + 2 * abs_remainder; + } else { + *abs_level = abs_remainder; + if (abs_remainder) { + //n > lastScanPosPass1 + *coeff_sign_level = 1 - 2 * coeff_sign_flag_decode(lc); + } + } + if (!bdpcm_flag && n <= last_scan_pos_pass1) { + const int left = xc > 0 ? abs_level[-1] : 0; + const int above = yc > 0 ? abs_level[-tb->tb_width] : 0; + const int pred = FFMAX(left, above); + + if (*abs_level == 1 && pred > 0) + *abs_level = pred; + else if (*abs_level > 0 && *abs_level <= pred) + (*abs_level)--; + } + if (*abs_level) { + tb->coeffs[off] = *coeff_sign_level * *abs_level; + tb->max_scan_x = FFMAX(xc, tb->max_scan_x); + tb->max_scan_y = FFMAX(yc, tb->max_scan_y); + tb->min_scan_x = FFMIN(xc, tb->min_scan_x); + tb->min_scan_y = FFMIN(yc, tb->min_scan_y); + } else { + tb->coeffs[off] = 0; + } + } + + return 0; +} + +static int hls_residual_ts_coding(VVCLocalContext *lc, TransformBlock *tb) +{ + ResidualCoding rc; + tb->min_scan_x = tb->min_scan_y = INT_MAX; + init_residual_coding(lc, &rc, tb->log2_tb_width, tb->log2_tb_height, tb); + for (int i = 0; i <= rc.last_sub_block; i++) { + int ret = residual_ts_coding_subblock(lc, &rc, i); + if (ret < 0) + return ret; + } + + return 0; +} + +static inline int residual_coding_subblock(VVCLocalContext *lc, ResidualCoding *rc, const int i) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + TransformBlock *tb = rc->tb; + int first_sig_scan_pos_sb, last_sig_scan_pos_sb; + int first_pos_mode0, first_pos_mode1; + int infer_sb_dc_sig_coeff_flag = 0; + int n, sig_hidden_flag, sum = 0; + int abs_level_gt2_flag[MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE]; + const int start_qstate_sb = rc->qstate; + const int xs = rc->sb_scan_x_off[i]; + const int ys = rc->sb_scan_y_off[i]; + uint8_t *sb_coded_flag = rc->sb_coded_flag + ys * rc->width_in_sbs + xs; + + + av_assert0(rc->num_sb_coeff <= MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE); + if (i < rc->last_sub_block && i > 0) { + *sb_coded_flag = sb_coded_flag_decode(lc, sb_coded_flag, rc, xs, ys); + infer_sb_dc_sig_coeff_flag = 1; + } else { + *sb_coded_flag = 1; + } + if (*sb_coded_flag && (xs > 3 || ys > 3) && !tb->c_idx) + lc->parse.mts_zero_out_sig_coeff_flag = 0; + + if (!*sb_coded_flag) + return 0; + + first_sig_scan_pos_sb = rc->num_sb_coeff; + last_sig_scan_pos_sb = -1; + first_pos_mode0 = (i == rc->last_sub_block ? rc->last_scan_pos : rc->num_sb_coeff -1); + first_pos_mode1 = first_pos_mode0; + for (n = first_pos_mode0; n >= 0 && rc->rem_bins_pass1 >= 4; n--) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int last = (xc == rc->last_significant_coeff_x && yc == rc->last_significant_coeff_y); + int *abs_level_pass1 = rc->abs_level_pass1 + yc * tb->tb_width + xc; + int *sig_coeff_flag = rc->sig_coeff_flag + yc * tb->tb_width + xc; + + if ((n > 0 || !infer_sb_dc_sig_coeff_flag ) && !last) { + *sig_coeff_flag = sig_coeff_flag_decode(lc, rc, xc, yc); + rc->rem_bins_pass1--; + if (*sig_coeff_flag) + infer_sb_dc_sig_coeff_flag = 0; + } else { + *sig_coeff_flag = last || (!rc->scan_x_off[n] && !rc ->scan_y_off[n] && + infer_sb_dc_sig_coeff_flag); + } + *abs_level_pass1 = 0; + if (*sig_coeff_flag) { + int abs_level_gt1_flag, par_level_flag = 0; + const int inc = get_gtx_flag_inc(rc, xc, yc, last); + abs_level_gt1_flag = abs_level_gtx_flag_decode(lc, inc); + rc->rem_bins_pass1--; + if (abs_level_gt1_flag) { + par_level_flag = par_level_flag_decode(lc, inc); + abs_level_gt2_flag[n] = abs_level_gtx_flag_decode(lc, inc + 32); + rc->rem_bins_pass1 -= 2; + } else { + abs_level_gt2_flag[n] = 0; + } + if (last_sig_scan_pos_sb == -1) + last_sig_scan_pos_sb = n; + first_sig_scan_pos_sb = n; + + *abs_level_pass1 = + 1 + par_level_flag + abs_level_gt1_flag + (abs_level_gt2_flag[n] << 1); + } else { + abs_level_gt2_flag[n] = 0; + } + + if (rsh->sh_dep_quant_used_flag) + rc->qstate = qstate_translate_table[rc->qstate][*abs_level_pass1 & 1]; + + first_pos_mode1 = n - 1; + } + for (n = first_pos_mode0; n > first_pos_mode1; n--) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int *abs_level_pass1 = rc->abs_level_pass1 + yc * tb->tb_width + xc; + int *abs_level = rc->abs_level + yc * tb->tb_width + xc; + + *abs_level = *abs_level_pass1; + if (abs_level_gt2_flag[n]) { + const int abs_remainder = abs_remainder_decode(lc, rc, xc, yc); + ep_update_hist(lc->ep, rc, abs_remainder, 2); + *abs_level += 2 * abs_remainder; + } + } + for (n = first_pos_mode1; n >= 0; n--) { + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + int *abs_level = rc->abs_level + yc * tb->tb_width + xc; + + if (*sb_coded_flag) { + const int dec_abs_level = dec_abs_level_decode(lc, rc, xc, yc, abs_level); + ep_update_hist(lc->ep, rc, dec_abs_level, 0); + } + if (*abs_level > 0) { + if (last_sig_scan_pos_sb == -1) + last_sig_scan_pos_sb = n; + first_sig_scan_pos_sb = n; + } + if (rsh->sh_dep_quant_used_flag) + rc->qstate = qstate_translate_table[rc->qstate][*abs_level & 1]; + } + sig_hidden_flag = rsh->sh_sign_data_hiding_used_flag && + (last_sig_scan_pos_sb - first_sig_scan_pos_sb > 3 ? 1 : 0); + + if (rsh->sh_dep_quant_used_flag) + rc->qstate = start_qstate_sb; + n = (i == rc->last_sub_block ? rc->last_scan_pos : rc->num_sb_coeff -1); + for (/* nothing */; n >= 0; n--) { + int trans_coeff_level; + const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n]; + const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n]; + const int off = yc * tb->tb_width + xc; + const int *abs_level = rc->abs_level + off; + + if (*abs_level > 0) { + int sign = 1; + if (!sig_hidden_flag || (n != first_sig_scan_pos_sb)) + sign = 1 - 2 * coeff_sign_flag_decode(lc); + if (rsh->sh_dep_quant_used_flag) { + trans_coeff_level = (2 * *abs_level - (rc->qstate > 1)) * sign; + } else { + trans_coeff_level = *abs_level * sign; + if (sig_hidden_flag) { + sum += *abs_level; + if (n == first_sig_scan_pos_sb && (sum % 2)) + trans_coeff_level = -trans_coeff_level; + } + } + tb->coeffs[off] = trans_coeff_level; + tb->max_scan_x = FFMAX(xc, tb->max_scan_x); + tb->max_scan_y = FFMAX(yc, tb->max_scan_y); + } + if (rsh->sh_dep_quant_used_flag) + rc->qstate = qstate_translate_table[rc->qstate][*abs_level & 1]; + } + + return 0; +} + +static void derive_last_scan_pos(ResidualCoding *rc) +{ + int xc, yc, xs, ys; + do { + if (!rc->last_scan_pos) { + rc->last_scan_pos = rc->num_sb_coeff; + rc->last_sub_block--; + } + rc->last_scan_pos--; + xs = rc->sb_scan_x_off[rc->last_sub_block]; + ys = rc->sb_scan_y_off[rc->last_sub_block]; + xc = (xs << rc->log2_sb_w) + rc->scan_x_off[rc->last_scan_pos]; + yc = (ys << rc->log2_sb_h) + rc->scan_y_off[rc->last_scan_pos]; + } while ((xc != rc->last_significant_coeff_x) || (yc != rc->last_significant_coeff_y)); +} + +static void last_significant_coeff_x_y_decode(ResidualCoding *rc, VVCLocalContext *lc, + const int log2_zo_tb_width, const int log2_zo_tb_height) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const TransformBlock *tb = rc->tb; + int last_significant_coeff_x, last_significant_coeff_y; + + last_significant_coeff_x = last_significant_coeff_x_prefix_decode(lc, + tb->log2_tb_width, log2_zo_tb_width, tb->c_idx); + + last_significant_coeff_y = last_significant_coeff_y_prefix_decode(lc, + tb->log2_tb_height, log2_zo_tb_height, tb->c_idx); + + if (last_significant_coeff_x > 3) { + int suffix = last_sig_coeff_suffix_decode(lc, last_significant_coeff_x); + last_significant_coeff_x = (1 << ((last_significant_coeff_x >> 1) - 1)) * + (2 + (last_significant_coeff_x & 1)) + suffix; + } + if (last_significant_coeff_y > 3) { + int suffix = last_sig_coeff_suffix_decode(lc, last_significant_coeff_y); + last_significant_coeff_y = (1 << ((last_significant_coeff_y >> 1) - 1)) * + (2 + (last_significant_coeff_y & 1)) + suffix; + } + if (rsh->sh_reverse_last_sig_coeff_flag) { + last_significant_coeff_x = (1 << log2_zo_tb_width) - 1 - last_significant_coeff_x; + last_significant_coeff_y = (1 << log2_zo_tb_height) - 1 - last_significant_coeff_y; + } + rc->last_significant_coeff_x = last_significant_coeff_x; + rc->last_significant_coeff_y = last_significant_coeff_y; +} + +static int hls_residual_coding(VVCLocalContext *lc, TransformBlock *tb) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const CodingUnit *cu = lc->cu; + const int log2_tb_width = tb->log2_tb_width; + const int log2_tb_height = tb->log2_tb_height; + const int c_idx = tb->c_idx; + int log2_zo_tb_width, log2_zo_tb_height; + ResidualCoding rc; + + if (sps->r->sps_mts_enabled_flag && cu->sbt_flag && !c_idx && log2_tb_width == 5 && log2_tb_height < 6) + log2_zo_tb_width = 4; + else + log2_zo_tb_width = FFMIN(log2_tb_width, 5 ); + + if (sps->r->sps_mts_enabled_flag && cu->sbt_flag && !c_idx && log2_tb_width < 6 && log2_tb_height == 5 ) + log2_zo_tb_height = 4; + else + log2_zo_tb_height = FFMIN(log2_tb_height, 5); + + init_residual_coding(lc, &rc, log2_zo_tb_width, log2_zo_tb_height, tb); + last_significant_coeff_x_y_decode(&rc, lc, log2_zo_tb_width, log2_zo_tb_height); + derive_last_scan_pos(&rc); + + if (!rc.last_sub_block && log2_tb_width >= 2 && log2_tb_height >= 2 && !tb->ts && rc.last_scan_pos > 0) + lc->parse.lfnst_dc_only = 0; + if ((rc.last_sub_block > 0 && log2_tb_width >= 2 && log2_tb_height >= 2 ) || + (rc.last_scan_pos > 7 && (log2_tb_width == 2 || log2_tb_width == 3 ) && + log2_tb_width == log2_tb_height)) + lc->parse.lfnst_zero_out_sig_coeff_flag = 0; + if ((rc.last_sub_block > 0 || rc.last_scan_pos > 0 ) && !c_idx) + lc->parse.mts_dc_only = 0; + + memset(tb->coeffs, 0, tb->tb_width * tb->tb_height * sizeof(*tb->coeffs)); + memset(rc.abs_level, 0, tb->tb_width * tb->tb_height * sizeof(rc.abs_level[0])); + memset(rc.sb_coded_flag, 0, rc.nb_sbs); + memset(rc.abs_level_pass1, 0, tb->tb_width * tb->tb_height * sizeof(rc.abs_level_pass1[0])); + memset(rc.sig_coeff_flag, 0, tb->tb_width * tb->tb_height * sizeof(rc.sig_coeff_flag[0])); + + for (int i = rc.last_sub_block; i >= 0; i--) { + int ret = residual_coding_subblock(lc, &rc, i); + if (ret < 0) + return ret; + } + + return 0; +} + +int ff_vvc_residual_coding(VVCLocalContext *lc, TransformBlock *tb) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int ts = !rsh->sh_ts_residual_coding_disabled_flag && tb->ts; + + return ts ? hls_residual_ts_coding(lc, tb) : hls_residual_coding(lc, tb); +} + +int ff_vvc_cu_coded_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_CODED_FLAG); +} + +int ff_vvc_sbt_flag(VVCLocalContext *lc) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + const int inc = w * h <= 256; + return GET_CABAC(CU_SBT_FLAG + inc); +} + +int ff_vvc_sbt_quad_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_SBT_QUAD_FLAG); +} + +int ff_vvc_sbt_horizontal_flag(VVCLocalContext *lc) +{ + const int w = lc->cu->cb_width; + const int h = lc->cu->cb_height; + const int inc = (w == h) ? 0 : ((w < h) ? 1 : 2); + return GET_CABAC(CU_SBT_HORIZONTAL_FLAG + inc); +} + +int ff_vvc_sbt_pos_flag(VVCLocalContext *lc) +{ + return GET_CABAC(CU_SBT_POS_FLAG); +} + +int ff_vvc_lfnst_idx(VVCLocalContext *lc, const int inc) +{ + if (!GET_CABAC(LFNST_IDX + inc)) + return 0; + if (!GET_CABAC(LFNST_IDX + 2)) + return 1; + return 2; +} + +int ff_vvc_mts_idx(VVCLocalContext *lc) +{ + int i; + for (i = 0; i < 4; i++) { + if (!GET_CABAC(MTS_IDX + i)) + return i; + } + return i; +} + +int ff_vvc_end_of_slice_flag_decode(VVCLocalContext *lc) +{ + return get_cabac_terminate(&lc->ep->cc); +} + +int ff_vvc_end_of_tile_one_bit(VVCLocalContext *lc) +{ + return get_cabac_terminate(&lc->ep->cc); +} + +int ff_vvc_end_of_subset_one_bit(VVCLocalContext *lc) +{ + return get_cabac_terminate(&lc->ep->cc); +} diff --git a/libavcodec/vvc/vvc_cabac.h b/libavcodec/vvc/vvc_cabac.h new file mode 100644 index 00000000000..172ab272ff2 --- /dev/null +++ b/libavcodec/vvc/vvc_cabac.h @@ -0,0 +1,126 @@ +/* + * VVC CABAC decoder + * + * Copyright (C) 2022 Nuo Mi + * + * 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 + */ + +#ifndef AVCODEC_VVC_VVC_CABAC_H +#define AVCODEC_VVC_VVC_CABAC_H + +#include "vvc_ctu.h" + +int ff_vvc_cabac_init(VVCLocalContext *lc, int ctu_idx, int rx, int ry); + +//sao +int ff_vvc_sao_merge_flag_decode(VVCLocalContext *lc); +int ff_vvc_sao_type_idx_decode(VVCLocalContext *lc); +int ff_vvc_sao_band_position_decode(VVCLocalContext *lc); +int ff_vvc_sao_offset_abs_decode(VVCLocalContext *lc); +int ff_vvc_sao_offset_sign_decode(VVCLocalContext *lc); +int ff_vvc_sao_eo_class_decode(VVCLocalContext *lc); + +//alf +int ff_vvc_alf_ctb_flag(VVCLocalContext *lc, int rx, int ry, int c_idx); +int ff_vvc_alf_use_aps_flag(VVCLocalContext *lc); +int ff_vvc_alf_luma_prev_filter_idx(VVCLocalContext *lc); +int ff_vvc_alf_luma_fixed_filter_idx(VVCLocalContext *lc); +int ff_vvc_alf_ctb_filter_alt_idx(VVCLocalContext *lc, int c_idx, int num_chroma_filters); +int ff_vvc_alf_ctb_cc_idc(VVCLocalContext *lc, int rx, int ry, int idx, int cc_filters_signalled); + +//coding_tree +int ff_vvc_split_cu_flag(VVCLocalContext* lc, int x0, int y0, int cb_width, int cb_height, + int ch_type, const VVCAllowedSplit *a); +VVCSplitMode ff_vvc_split_mode(VVCLocalContext *lc, int x0, int y0, int cb_width, int cb_height, + int cqt_depth, int mtt_depth, int ch_type, const VVCAllowedSplit *a); +int ff_vvc_non_inter_flag(VVCLocalContext *lc, int x0, int y0, int ch_type); + +//coding unit +int ff_vvc_pred_mode_flag(VVCLocalContext *lc, int is_chroma); +int ff_vvc_pred_mode_plt_flag(VVCLocalContext *lc); +int ff_vvc_intra_bdpcm_luma_flag(VVCLocalContext *lc); +int ff_vvc_intra_bdpcm_luma_dir_flag(VVCLocalContext *lc); +int ff_vvc_intra_bdpcm_chroma_flag(VVCLocalContext *lc); +int ff_vvc_intra_bdpcm_chroma_dir_flag(VVCLocalContext *lc); +int ff_vvc_cu_skip_flag(VVCLocalContext *lc, const uint8_t *cu_skip_flag); +int ff_vvc_pred_mode_ibc_flag(VVCLocalContext *lc, int ch_type); +int ff_vvc_cu_coded_flag(VVCLocalContext *lc); +int ff_vvc_cu_qp_delta_abs(VVCLocalContext *lc); +int ff_vvc_cu_qp_delta_sign_flag(VVCLocalContext *lc); +int ff_vvc_sbt_flag(VVCLocalContext *lc); +int ff_vvc_sbt_quad_flag(VVCLocalContext *lc); +int ff_vvc_sbt_horizontal_flag(VVCLocalContext *lc); +int ff_vvc_sbt_pos_flag(VVCLocalContext *lc); + +//intra +int ff_vvc_intra_mip_flag(VVCLocalContext *lc, const uint8_t *intra_mip_flag); +int ff_vvc_intra_mip_transposed_flag(VVCLocalContext *lc); +int ff_vvc_intra_mip_mode(VVCLocalContext *lc); +int ff_vvc_intra_luma_ref_idx(VVCLocalContext *lc); +int ff_vvc_intra_subpartitions_mode_flag(VVCLocalContext *lc); +enum IspType ff_vvc_isp_split_type(VVCLocalContext *lc, int intra_subpartitions_mode_flag); +int ff_vvc_intra_luma_mpm_flag(VVCLocalContext *lc); +int ff_vvc_intra_luma_not_planar_flag(VVCLocalContext *lc, int intra_subpartitions_mode_flag); +int ff_vvc_intra_luma_mpm_idx(VVCLocalContext *lc); +int ff_vvc_intra_luma_mpm_remainder(VVCLocalContext *lc); +int ff_vvc_cclm_mode_flag(VVCLocalContext *lc); +int ff_vvc_cclm_mode_idx(VVCLocalContext *lc); +int ff_vvc_intra_chroma_pred_mode(VVCLocalContext *lc); + +//inter +int ff_vvc_general_merge_flag(VVCLocalContext *lc); +int ff_vvc_merge_subblock_flag(VVCLocalContext *lc); +int ff_vvc_merge_subblock_idx(VVCLocalContext *lc, int max_num_subblock_merge_cand); +int ff_vvc_regular_merge_flag(VVCLocalContext *lc, int cu_skip_flag); +int ff_vvc_merge_idx(VVCLocalContext *lc); +int ff_vvc_mmvd_merge_flag(VVCLocalContext *lc); +int ff_vvc_mmvd_cand_flag(VVCLocalContext *lc); +void ff_vvc_mmvd_offset_coding(VVCLocalContext *lc, Mv *mvd_offset, int ph_mmvd_fullpel_only_flag); +int ff_vvc_ciip_flag(VVCLocalContext *lc); +int ff_vvc_merge_gpm_partition_idx(VVCLocalContext *lc); +int ff_vvc_merge_gpm_idx(VVCLocalContext *lc, int idx); +PredFlag ff_vvc_pred_flag(VVCLocalContext *lc, int is_b); +int ff_vvc_inter_affine_flag(VVCLocalContext *lc); +int ff_vvc_cu_affine_type_flag(VVCLocalContext *lc); +int ff_vvc_sym_mvd_flag(VVCLocalContext *lc); +int ff_vvc_ref_idx_lx(VVCLocalContext *lc, uint8_t nb_refs); +int ff_vvc_abs_mvd_greater0_flag(VVCLocalContext *lc); +int ff_vvc_abs_mvd_greater1_flag(VVCLocalContext *lc); +int ff_vvc_abs_mvd_minus2(VVCLocalContext *lc); +int ff_vvc_mvd_sign_flag(VVCLocalContext *lc); +int ff_vvc_mvp_lx_flag(VVCLocalContext *lc); +int ff_vvc_amvr_shift(VVCLocalContext *lc, int inter_affine_flag, PredMode pred_mode, int has_amvr_flag); +int ff_vvc_bcw_idx(VVCLocalContext *lc, int no_backward_pred_flag); + +//transform +int ff_vvc_tu_cb_coded_flag(VVCLocalContext *lc); +int ff_vvc_tu_cr_coded_flag(VVCLocalContext *lc, int tu_cb_coded_flag); +int ff_vvc_tu_y_coded_flag(VVCLocalContext *lc); +int ff_vvc_cu_chroma_qp_offset_flag(VVCLocalContext *lc); +int ff_vvc_cu_chroma_qp_offset_idx(VVCLocalContext *lc); +int ff_vvc_tu_joint_cbcr_residual_flag(VVCLocalContext *lc, int tu_cb_coded_flag, int tu_cr_coded_flag); +int ff_vvc_transform_skip_flag(VVCLocalContext *lc, int ctx); +int ff_vvc_residual_coding(VVCLocalContext *lc, TransformBlock *tb); +int ff_vvc_lfnst_idx(VVCLocalContext *lc, int inc); +int ff_vvc_mts_idx(VVCLocalContext *lc); + +int ff_vvc_end_of_slice_flag_decode(VVCLocalContext *lc); +int ff_vvc_end_of_tile_one_bit(VVCLocalContext *lc); +int ff_vvc_end_of_subset_one_bit(VVCLocalContext *lc); + +#endif //AVCODEC_VVC_VVC_CABAC_H diff --git a/libavcodec/vvc/vvc_ctu.c b/libavcodec/vvc/vvc_ctu.c new file mode 100644 index 00000000000..ffd83e4ea02 --- /dev/null +++ b/libavcodec/vvc/vvc_ctu.c @@ -0,0 +1,2551 @@ +/* + * VVC CTU(Coding Tree Unit) parser + * + * Copyright (C) 2022 Nuo Mi + * + * 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 "libavcodec/refstruct.h" + +#include "vvc_cabac.h" +#include "vvc_ctu.h" +#include "vvc_inter.h" +#include "vvc_mvs.h" + +#define PROF_TEMP_SIZE (PROF_BLOCK_SIZE) * sizeof(int16_t) + +#define TAB_MSM(fc, depth, x, y) fc->tab.msm[(depth)][((y) >> 5) * fc->ps.pps->width32 + ((x) >> 5)] +#define TAB_ISPMF(fc, x, y) fc->tab.ispmf[((y) >> 6) * fc->ps.pps->width64 + ((x) >> 6)] + +typedef enum VVCModeType { + MODE_TYPE_ALL, + MODE_TYPE_INTER, + MODE_TYPE_INTRA, +} VVCModeType; + +static void set_tb_pos(const VVCFrameContext *fc, const TransformBlock *tb) +{ + const int x_tb = tb->x0 >> MIN_TU_LOG2; + const int y_tb = tb->y0 >> MIN_TU_LOG2; + const int hs = fc->ps.sps->hshift[tb->c_idx]; + const int vs = fc->ps.sps->vshift[tb->c_idx]; + const int is_chroma = tb->c_idx != 0; + const int width = FFMAX(1, tb->tb_width >> (MIN_TU_LOG2 - hs)); + const int end = y_tb + FFMAX(1, tb->tb_height >> (MIN_TU_LOG2 - vs)); + + for (int y = y_tb; y < end; y++) { + const int off = y * fc->ps.pps->min_tu_width + x_tb; + for (int i = 0; i < width; i++) { + fc->tab.tb_pos_x0[is_chroma][off + i] = tb->x0; + fc->tab.tb_pos_y0[is_chroma][off + i] = tb->y0; + } + memset(fc->tab.tb_width [is_chroma] + off, tb->tb_width, width); + memset(fc->tab.tb_height[is_chroma] + off, tb->tb_height, width); + } +} + +static void set_tb_tab(uint8_t *tab, uint8_t v, const VVCFrameContext *fc, + const TransformBlock *tb) +{ + const int width = tb->tb_width << fc->ps.sps->hshift[tb->c_idx]; + const int height = tb->tb_height << fc->ps.sps->vshift[tb->c_idx]; + + for (int h = 0; h < height; h += MIN_TU_SIZE) { + const int y = (tb->y0 + h) >> MIN_TU_LOG2; + const int off = y * fc->ps.pps->min_tu_width + (tb->x0 >> MIN_TU_LOG2); + const int w = FFMAX(1, width >> MIN_TU_LOG2); + memset(tab + off, v, w); + } +} + +// 8.7.1 Derivation process for quantization parameters +static int get_qp_y_pred(const VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const CodingUnit *cu = lc->cu; + const int ctb_log2_size = sps->ctb_log2_size_y; + const int ctb_size_mask = (1 << ctb_log2_size) - 1; + const int xQg = lc->parse.cu_qg_top_left_x; + const int yQg = lc->parse.cu_qg_top_left_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x_cb = cu->x0 >> sps->min_cb_log2_size_y; + const int y_cb = cu->y0 >> sps->min_cb_log2_size_y; + const int x_ctb = cu->x0 >> ctb_log2_size; + const int y_ctb = cu->y0 >> ctb_log2_size; + const int in_same_ctb_a = ((xQg - 1) >> ctb_log2_size) == x_ctb && (yQg >> ctb_log2_size) == y_ctb; + const int in_same_ctb_b = (xQg >> ctb_log2_size) == x_ctb && ((yQg - 1) >> ctb_log2_size) == y_ctb; + int qPy_pred, qPy_a, qPy_b; + + if (lc->na.cand_up) { + const int first_qg_in_ctu = !(xQg & ctb_size_mask) && !(yQg & ctb_size_mask); + const int qPy_up = fc->tab.qp[LUMA][x_cb + (y_cb - 1) * min_cb_width]; + if (first_qg_in_ctu && pps->ctb_to_col_bd[xQg >> ctb_log2_size] == xQg >> ctb_log2_size) + return qPy_up; + } + + // qPy_pred + qPy_pred = lc->ep->is_first_qg ? lc->sc->sh.slice_qp_y : lc->ep->qp_y; + + // qPy_b + if (!lc->na.cand_up || !in_same_ctb_b) + qPy_b = qPy_pred; + else + qPy_b = fc->tab.qp[LUMA][x_cb + (y_cb - 1) * min_cb_width]; + + // qPy_a + if (!lc->na.cand_left || !in_same_ctb_a) + qPy_a = qPy_pred; + else + qPy_a = fc->tab.qp[LUMA][(x_cb - 1) + y_cb * min_cb_width]; + + av_assert2(qPy_a >= -fc->ps.sps->qp_bd_offset && qPy_a < 63); + av_assert2(qPy_b >= -fc->ps.sps->qp_bd_offset && qPy_b < 63); + + return (qPy_a + qPy_b + 1) >> 1; +} + +static void set_cb_tab(const VVCLocalContext *lc, uint8_t *tab, const uint8_t v) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const CodingUnit *cu = lc->cu; + const int log2_min_cb_size = fc->ps.sps->min_cb_log2_size_y; + const int x_cb = cu->x0 >> log2_min_cb_size; + const int y_cb = cu->y0 >> log2_min_cb_size; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + int x = y_cb * pps->min_cb_width + x_cb; + + for (int y = 0; y < (cb_height >> log2_min_cb_size); y++) { + const int width = cb_width >> log2_min_cb_size; + + memset(&tab[x], v, width); + x += pps->min_cb_width; + } +} + +static int set_qp_y(VVCLocalContext *lc, const int x0, const int y0, const int has_qp_delta) +{ + const VVCSPS *sps = lc->fc->ps.sps; + EntryPoint *ep = lc->ep; + CodingUnit *cu = lc->cu; + int cu_qp_delta = 0; + + if (!lc->fc->ps.pps->r->pps_cu_qp_delta_enabled_flag) { + ep->qp_y = lc->sc->sh.slice_qp_y; + } else if (ep->is_first_qg || (lc->parse.cu_qg_top_left_x == x0 && lc->parse.cu_qg_top_left_y == y0)) { + ep->qp_y = get_qp_y_pred(lc); + ep->is_first_qg = 0; + } + + if (has_qp_delta) { + const int cu_qp_delta_abs = ff_vvc_cu_qp_delta_abs(lc); + + if (cu_qp_delta_abs) + cu_qp_delta = ff_vvc_cu_qp_delta_sign_flag(lc) ? -cu_qp_delta_abs : cu_qp_delta_abs; + if (cu_qp_delta > (31 + sps->qp_bd_offset / 2) || cu_qp_delta < -(32 + sps->qp_bd_offset / 2)) + return AVERROR_INVALIDDATA; + lc->parse.is_cu_qp_delta_coded = 1; + + if (cu_qp_delta) { + int off = sps->qp_bd_offset; + ep->qp_y = FFUMOD(ep->qp_y + cu_qp_delta + 64 + 2 * off, 64 + off) - off; + } + } + + set_cb_tab(lc, lc->fc->tab.qp[LUMA], ep->qp_y); + cu->qp[LUMA] = ep->qp_y; + + return 0; +} + +static void set_qp_c_tab(const VVCLocalContext *lc, const TransformUnit *tu, const TransformBlock *tb) +{ + const int is_jcbcr = tu->joint_cbcr_residual_flag && tu->coded_flag[CB] && tu->coded_flag[CR]; + const int idx = is_jcbcr ? JCBCR : tb->c_idx; + + set_tb_tab(lc->fc->tab.qp[tb->c_idx], lc->cu->qp[idx], lc->fc, tb); +} + +static void set_qp_c(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + CodingUnit *cu = lc->cu; + const int x_center = cu->x0 + cu->cb_width / 2; + const int y_center = cu->y0 + cu->cb_height / 2; + const int single_tree = cu->tree_type == SINGLE_TREE; + const int qp_luma = (single_tree ? lc->ep->qp_y : ff_vvc_get_qPy(fc, x_center, y_center)) + sps->qp_bd_offset; + const int qp_chroma = av_clip(qp_luma, 0, MAX_QP + sps->qp_bd_offset); + const int sh_chroma_qp_offset[] = { + rsh->sh_cb_qp_offset, + rsh->sh_cr_qp_offset, + rsh->sh_joint_cbcr_qp_offset, + }; + int qp; + + for (int i = CB - 1; i < CR + sps->r->sps_joint_cbcr_enabled_flag; i++) { + qp = sps->chroma_qp_table[i][qp_chroma]; + qp = qp + pps->chroma_qp_offset[i] + sh_chroma_qp_offset[i] + lc->parse.chroma_qp_offset[i]; + qp = av_clip(qp, -sps->qp_bd_offset, MAX_QP) + sps->qp_bd_offset; + cu->qp[i + 1] = qp; + } +} + +static TransformUnit* alloc_tu(VVCFrameContext *fc, CodingUnit *cu) +{ + TransformUnit *tu = ff_refstruct_pool_get(fc->tu_pool); + if (!tu) + return NULL; + + tu->next = NULL; + + if (cu->tus.tail) + cu->tus.tail->next = tu; + else + cu->tus.head = tu; + cu->tus.tail = tu; + + return tu; +} + +static TransformUnit* add_tu(VVCFrameContext *fc, CodingUnit *cu, const int x0, const int y0, const int tu_width, const int tu_height) +{ + TransformUnit *tu = alloc_tu(fc, cu); + + if (!tu) + return NULL; + + tu->x0 = x0; + tu->y0 = y0; + tu->width = tu_width; + tu->height = tu_height; + tu->joint_cbcr_residual_flag = 0; + memset(tu->coded_flag, 0, sizeof(tu->coded_flag)); + tu->nb_tbs = 0; + + return tu; +} + +static TransformBlock* add_tb(TransformUnit *tu, VVCLocalContext *lc, + const int x0, const int y0, const int tb_width, const int tb_height, const int c_idx) +{ + TransformBlock *tb; + + tb = &tu->tbs[tu->nb_tbs++]; + tb->has_coeffs = 0; + tb->x0 = x0; + tb->y0 = y0; + tb->tb_width = tb_width; + tb->tb_height = tb_height; + tb->log2_tb_width = av_log2(tb_width); + tb->log2_tb_height = av_log2(tb_height); + + tb->max_scan_x = tb->max_scan_y = 0; + tb->min_scan_x = tb->min_scan_y = 0; + + tb->c_idx = c_idx; + tb->ts = 0; + tb->coeffs = lc->coeffs; + lc->coeffs += tb_width * tb_height; + return tb; +} + +static uint8_t tu_y_coded_flag_decode(VVCLocalContext *lc, const int is_sbt_not_coded, + const int sub_tu_index, const int is_isp, const int is_chroma_coded) +{ + uint8_t tu_y_coded_flag = 0; + const VVCSPS *sps = lc->fc->ps.sps; + CodingUnit *cu = lc->cu; + + if (!is_sbt_not_coded) { + int has_y_coded_flag = sub_tu_index < cu->num_intra_subpartitions - 1 || !lc->parse.infer_tu_cbf_luma; + if (!is_isp) { + const int is_large = cu->cb_width > sps->max_tb_size_y || cu->cb_height > sps->max_tb_size_y; + has_y_coded_flag = (cu->pred_mode == MODE_INTRA && !cu->act_enabled_flag) || is_chroma_coded || is_large; + } + tu_y_coded_flag = has_y_coded_flag ? ff_vvc_tu_y_coded_flag(lc) : 1; + } + if (is_isp) + lc->parse.infer_tu_cbf_luma = lc->parse.infer_tu_cbf_luma && !tu_y_coded_flag; + return tu_y_coded_flag; +} + +static void chroma_qp_offset_decode(VVCLocalContext *lc, const int is_128, const int is_chroma_coded) +{ + const VVCPPS *pps = lc->fc->ps.pps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + + if ((is_128 || is_chroma_coded) && + rsh->sh_cu_chroma_qp_offset_enabled_flag && !lc->parse.is_cu_chroma_qp_offset_coded) { + const int cu_chroma_qp_offset_flag = ff_vvc_cu_chroma_qp_offset_flag(lc); + if (cu_chroma_qp_offset_flag) { + int cu_chroma_qp_offset_idx = 0; + if (pps->r->pps_chroma_qp_offset_list_len_minus1 > 0) + cu_chroma_qp_offset_idx = ff_vvc_cu_chroma_qp_offset_idx(lc); + for (int i = CB - 1; i < JCBCR; i++) + lc->parse.chroma_qp_offset[i] = pps->chroma_qp_offset_list[cu_chroma_qp_offset_idx][i]; + } else { + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + } + lc->parse.is_cu_chroma_qp_offset_coded = 1; + } +} + +static int hls_transform_unit(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height, int sub_tu_index, int ch_type) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + CodingUnit *cu = lc->cu; + TransformUnit *tu = add_tu(fc, cu, x0, y0, tu_width, tu_height); + const int min_cb_width = pps->min_cb_width; + const VVCTreeType tree_type = cu->tree_type; + const int is_128 = cu->cb_width > 64 || cu->cb_height > 64; + const int is_isp = cu->isp_split_type != ISP_NO_SPLIT; + const int is_isp_last_tu = is_isp && (sub_tu_index == cu->num_intra_subpartitions - 1); + const int is_sbt_not_coded = cu->sbt_flag && + ((sub_tu_index == 0 && cu->sbt_pos_flag) || (sub_tu_index == 1 && !cu->sbt_pos_flag)); + const int chroma_available = tree_type != DUAL_TREE_LUMA && sps->r->sps_chroma_format_idc && + (!is_isp || is_isp_last_tu); + int ret, xc, yc, wc, hc, is_chroma_coded; + + if (!tu) + return AVERROR_INVALIDDATA; + + if (tree_type == SINGLE_TREE && is_isp_last_tu) { + const int x_cu = x0 >> fc->ps.sps->min_cb_log2_size_y; + const int y_cu = y0 >> fc->ps.sps->min_cb_log2_size_y; + xc = SAMPLE_CTB(fc->tab.cb_pos_x[ch_type], x_cu, y_cu); + yc = SAMPLE_CTB(fc->tab.cb_pos_y[ch_type], x_cu, y_cu); + wc = SAMPLE_CTB(fc->tab.cb_width[ch_type], x_cu, y_cu); + hc = SAMPLE_CTB(fc->tab.cb_height[ch_type], x_cu, y_cu); + } else { + xc = x0, yc = y0, wc = tu_width, hc = tu_height; + } + + if (chroma_available && !is_sbt_not_coded) { + tu->coded_flag[CB] = ff_vvc_tu_cb_coded_flag(lc); + tu->coded_flag[CR] = ff_vvc_tu_cr_coded_flag(lc, tu->coded_flag[CB]); + } + + is_chroma_coded = chroma_available && (tu->coded_flag[CB] || tu->coded_flag[CR]); + + if (tree_type != DUAL_TREE_CHROMA) { + int has_qp_delta; + tu->coded_flag[LUMA] = tu_y_coded_flag_decode(lc, is_sbt_not_coded, sub_tu_index, is_isp, is_chroma_coded); + has_qp_delta = (is_128 || tu->coded_flag[LUMA] || is_chroma_coded) && + pps->r->pps_cu_qp_delta_enabled_flag && !lc->parse.is_cu_qp_delta_coded; + ret = set_qp_y(lc, x0, y0, has_qp_delta); + if (ret < 0) + return ret; + add_tb(tu, lc, x0, y0, tu_width, tu_height, LUMA); + } + if (tree_type != DUAL_TREE_LUMA) { + chroma_qp_offset_decode(lc, is_128, is_chroma_coded); + if (chroma_available) { + const int hs = sps->hshift[CHROMA]; + const int vs = sps->vshift[CHROMA]; + add_tb(tu, lc, xc, yc, wc >> hs, hc >> vs, CB); + add_tb(tu, lc, xc, yc, wc >> hs, hc >> vs, CR); + } + } + if (sps->r->sps_joint_cbcr_enabled_flag && ((cu->pred_mode == MODE_INTRA && + (tu->coded_flag[CB] || tu->coded_flag[CR])) || + (tu->coded_flag[CB] && tu->coded_flag[CR])) && + chroma_available) { + tu->joint_cbcr_residual_flag = ff_vvc_tu_joint_cbcr_residual_flag(lc, tu->coded_flag[1], tu->coded_flag[2]); + } + + for (int i = 0; i < tu->nb_tbs; i++) { + TransformBlock *tb = &tu->tbs[i]; + const int is_chroma = tb->c_idx != LUMA; + tb->has_coeffs = tu->coded_flag[tb->c_idx]; + if (tb->has_coeffs && is_chroma) + tb->has_coeffs = tb->c_idx == CB ? 1 : !(tu->coded_flag[CB] && tu->joint_cbcr_residual_flag); + if (tb->has_coeffs) { + tb->ts = cu->bdpcm_flag[tb->c_idx]; + if (sps->r->sps_transform_skip_enabled_flag && !cu->bdpcm_flag[tb->c_idx] && + tb->tb_width <= sps->max_ts_size && tb->tb_height <= sps->max_ts_size && + !cu->sbt_flag && (is_chroma || !is_isp)) { + tb->ts = ff_vvc_transform_skip_flag(lc, is_chroma); + } + ret = ff_vvc_residual_coding(lc, tb); + if (ret < 0) + return ret; + set_tb_tab(fc->tab.tu_coded_flag[tb->c_idx], tu->coded_flag[tb->c_idx], fc, tb); + } + if (tb->c_idx != CR) + set_tb_pos(fc, tb); + if (tb->c_idx == CB) + set_tb_tab(fc->tab.tu_joint_cbcr_residual_flag, tu->joint_cbcr_residual_flag, fc, tb); + } + + return 0; +} + +static int hls_transform_tree(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height, int ch_type) +{ + const CodingUnit *cu = lc->cu; + const VVCSPS *sps = lc->fc->ps.sps; + int ret; + + lc->parse.infer_tu_cbf_luma = 1; + if (cu->isp_split_type == ISP_NO_SPLIT && !cu->sbt_flag) { + if (tu_width > sps->max_tb_size_y || tu_height > sps->max_tb_size_y) { + const int ver_split_first = tu_width > sps->max_tb_size_y && tu_width > tu_height; + const int trafo_width = ver_split_first ? (tu_width / 2) : tu_width; + const int trafo_height = !ver_split_first ? (tu_height / 2) : tu_height; + + #define TRANSFORM_TREE(x, y) do { \ + ret = hls_transform_tree(lc, x, y, trafo_width, trafo_height, ch_type); \ + if (ret < 0) \ + return ret; \ + } while (0) + + TRANSFORM_TREE(x0, y0); + if (ver_split_first) + TRANSFORM_TREE(x0 + trafo_width, y0); + else + TRANSFORM_TREE(x0, y0 + trafo_height); + + } else { + ret = hls_transform_unit(lc, x0, y0, tu_width, tu_height, 0, ch_type); + if (ret < 0) + return ret; + + } + } else if (cu->sbt_flag) { + if (!cu->sbt_horizontal_flag) { + #define TRANSFORM_UNIT(x, width, idx) do { \ + ret = hls_transform_unit(lc, x, y0, width, tu_height, idx, ch_type); \ + if (ret < 0) \ + return ret; \ + } while (0) + + const int trafo_width = tu_width * lc->parse.sbt_num_fourths_tb0 / 4; + TRANSFORM_UNIT(x0, trafo_width, 0); + TRANSFORM_UNIT(x0 + trafo_width, tu_width - trafo_width, 1); + + #undef TRANSFORM_UNIT + } else { + #define TRANSFORM_UNIT(y, height, idx) do { \ + ret = hls_transform_unit(lc, x0, y, tu_width, height, idx, ch_type); \ + if (ret < 0) \ + return ret; \ + } while (0) + + const int trafo_height = tu_height * lc->parse.sbt_num_fourths_tb0 / 4; + TRANSFORM_UNIT(y0, trafo_height, 0); + TRANSFORM_UNIT(y0 + trafo_height, tu_height - trafo_height, 1); + + #undef TRANSFORM_UNIT + } + } else if (cu->isp_split_type == ISP_HOR_SPLIT) { + const int trafo_height = tu_height / cu->num_intra_subpartitions; + for (int i = 0; i < cu->num_intra_subpartitions; i++) { + ret = hls_transform_unit(lc, x0, y0 + trafo_height * i, tu_width, trafo_height, i, 0); + if (ret < 0) + return ret; + } + } else if (cu->isp_split_type == ISP_VER_SPLIT) { + const int trafo_width = tu_width / cu->num_intra_subpartitions; + for (int i = 0; i < cu->num_intra_subpartitions; i++) { + ret = hls_transform_unit(lc, x0 + trafo_width * i , y0, trafo_width, tu_height, i, 0); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int skipped_transform_tree(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height) +{ + VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const VVCSPS *sps = fc->ps.sps; + + if (tu_width > sps->max_tb_size_y || tu_height > sps->max_tb_size_y) { + const int ver_split_first = tu_width > sps->max_tb_size_y && tu_width > tu_height; + const int trafo_width = ver_split_first ? (tu_width / 2) : tu_width; + const int trafo_height = !ver_split_first ? (tu_height / 2) : tu_height; + + #define SKIPPED_TRANSFORM_TREE(x, y) do { \ + int ret = skipped_transform_tree(lc, x, y, trafo_width, trafo_height); \ + if (ret < 0) \ + return ret; \ + } while (0) + + SKIPPED_TRANSFORM_TREE(x0, y0); + if (ver_split_first) + SKIPPED_TRANSFORM_TREE(x0 + trafo_width, y0); + else + SKIPPED_TRANSFORM_TREE(x0, y0 + trafo_height); + } else { + TransformUnit *tu = add_tu(fc, lc->cu, x0, y0, tu_width, tu_height); + const int has_chroma = sps->r->sps_chroma_format_idc && cu->tree_type != DUAL_TREE_LUMA; + const int c_start = cu->tree_type == DUAL_TREE_CHROMA ? CB : LUMA; + const int c_end = has_chroma ? VVC_MAX_SAMPLE_ARRAYS : CB; + + if (!tu) + return AVERROR_INVALIDDATA; + for (int i = c_start; i < c_end; i++) { + TransformBlock *tb = add_tb(tu, lc, x0, y0, tu_width >> sps->hshift[i], tu_height >> sps->vshift[i], i); + if (i != CR) + set_tb_pos(fc, tb); + } + } + + return 0; +} + +//6.4.1 Allowed quad split process +//6.4.2 Allowed binary split process +//6.4.3 Allowed ternary split process +static void can_split(const VVCLocalContext *lc, int x0, int y0,int cb_width, int cb_height, + int mtt_depth, int depth_offset, int part_idx, VVCSplitMode last_split_mode, + VVCTreeType tree_type, VVCModeType mode_type, VVCAllowedSplit* split) +{ + int min_qt_size, max_bt_size, max_tt_size, max_mtt_depth; + const VVCFrameContext *fc = lc->fc; + const VVCSH *sh = &lc->sc->sh; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int chroma = tree_type == DUAL_TREE_CHROMA; + int min_cb_size_y = sps->min_cb_size_y; + int *qt = &split->qt; + int *btv = &split->btv; + int *bth = &split->bth; + int *ttv = &split->ttv; + int *tth = &split->tth; + + *qt = *bth = *btv = *tth = *ttv = 1; + + if (mtt_depth) + *qt = 0; + + min_qt_size = sh->min_qt_size[chroma]; + if (cb_width <= min_qt_size) + *qt = 0; + + if (chroma) { + int chroma_area = (cb_width >> sps->hshift[1]) * (cb_height >> sps->vshift[1]); + int chroma_width = cb_width >> sps->hshift[1]; + + if (chroma_width == 8) + *ttv = 0; + else if (chroma_width <= 4) { + if (chroma_width == 4) + *btv = 0; + *qt = 0; + } + if (mode_type == MODE_TYPE_INTRA) + *qt = *btv = *bth = *ttv = *tth = 0; + if (chroma_area <= 32) { + *ttv = *tth = 0; + if (chroma_area <= 16) + *btv = *bth = 0; + } + } + max_bt_size = sh->max_bt_size[chroma]; + max_tt_size = sh->max_tt_size[chroma]; + max_mtt_depth = sh->max_mtt_depth[chroma] + depth_offset; + + if (mode_type == MODE_TYPE_INTER) { + int area = cb_width * cb_height; + if (area == 32) + *btv = *bth = 0; + else if (area == 64) + *ttv = *tth = 0; + } + if (cb_width <= 2 * min_cb_size_y) { + *ttv = 0; + if (cb_width <= min_cb_size_y) + *btv = 0; + } + if (cb_height <= 2 * min_cb_size_y) { + *tth = 0; + if (cb_height <= min_cb_size_y) + *bth = 0; + } + if (cb_width > max_bt_size || cb_height > max_bt_size) + *btv = *bth = 0; + max_tt_size = FFMIN(64, max_tt_size); + if (cb_width > max_tt_size || cb_height > max_tt_size) + *ttv = *tth = 0; + if (mtt_depth >= max_mtt_depth) + *btv = *bth = *ttv = *tth = 0; + if (x0 + cb_width > pps->width) { + *ttv = *tth = 0; + if (cb_height > 64) + *btv = 0; + if (y0 + cb_height <= pps->height) + *bth = 0; + else if (cb_width > min_qt_size) + *btv = *bth = 0; + } + if (y0 + cb_height > pps->height) { + *btv = *ttv = *tth = 0; + if (cb_width > 64) + *bth = 0; + } + if (mtt_depth > 0 && part_idx == 1) { + if (last_split_mode == SPLIT_TT_VER) + *btv = 0; + else if (last_split_mode == SPLIT_TT_HOR) + *bth = 0; + } + if (cb_width <= 64 && cb_height > 64) + *btv = 0; + if (cb_width > 64 && cb_height <= 64) + *bth = 0; +} + +static int get_num_intra_subpartitions(enum IspType isp_split_type, int cb_width, int cb_height) +{ + if (isp_split_type == ISP_NO_SPLIT) + return 1; + if ((cb_width == 4 && cb_height == 8) || (cb_width == 8 && cb_height == 4)) + return 2; + return 4; +} + +static int get_cclm_enabled(const VVCLocalContext *lc, const int x0, const int y0) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + int enabled = 0; + + if (!sps->r->sps_cclm_enabled_flag) + return 0; + if (!sps->r->sps_qtbtt_dual_tree_intra_flag || !IS_I(lc->sc->sh.r) || sps->ctb_log2_size_y < 6) + return 1; + else { + const int x64 = x0 >> 6 << 6; + const int y64 = y0 >> 6 << 6; + const int y32 = y0 >> 5 << 5; + const int x64_cu = x64 >> fc->ps.sps->min_cb_log2_size_y; + const int y64_cu = y64 >> fc->ps.sps->min_cb_log2_size_y; + const int y32_cu = y32 >> fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int depth = SAMPLE_CTB(fc->tab.cqt_depth[1], x64_cu, y64_cu); + const int min_depth = fc->ps.sps->ctb_log2_size_y - 6; + const VVCSplitMode msm64 = (VVCSplitMode)TAB_MSM(fc, 0, x64, y64); + const VVCSplitMode msm32 = (VVCSplitMode)TAB_MSM(fc, 1, x64, y32); + + enabled = SAMPLE_CTB(fc->tab.cb_width[1], x64_cu, y64_cu) == 64 && + SAMPLE_CTB(fc->tab.cb_height[1], x64_cu, y64_cu) == 64; + enabled |= depth == min_depth && msm64 == SPLIT_BT_HOR && + SAMPLE_CTB(fc->tab.cb_width[1], x64_cu, y32_cu) == 64 && + SAMPLE_CTB(fc->tab.cb_height[1], x64_cu, y32_cu) == 32; + enabled |= depth > min_depth; + enabled |= depth == min_depth && msm64 == SPLIT_BT_HOR && msm32 == SPLIT_BT_VER; + + if (enabled) { + const int w = SAMPLE_CTB(fc->tab.cb_width[0], x64_cu, y64_cu); + const int h = SAMPLE_CTB(fc->tab.cb_height[0], x64_cu, y64_cu); + const int depth0 = SAMPLE_CTB(fc->tab.cqt_depth[0], x64_cu, y64_cu); + if ((w == 64 && h == 64 && TAB_ISPMF(fc, x64, y64)) || + ((w < 64 || h < 64) && depth0 == min_depth)) + return 0; + } + + } + + return enabled; +} + +static int less(const void *a, const void *b) +{ + return *(const int*)a - *(const int*)b; +} + +//8.4.2 Derivation process for luma intra prediction mode +static enum IntraPredMode luma_intra_pred_mode(VVCLocalContext* lc, const int intra_subpartitions_mode_flag) +{ + VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const int x0 = cu->x0; + const int y0 = cu->y0; + enum IntraPredMode pred; + int intra_luma_not_planar_flag = 1; + int intra_luma_mpm_remainder = 0; + int intra_luma_mpm_flag = 1; + int intra_luma_mpm_idx = 0; + + if (!cu->intra_luma_ref_idx) + intra_luma_mpm_flag = ff_vvc_intra_luma_mpm_flag(lc); + if (intra_luma_mpm_flag) { + if (!cu->intra_luma_ref_idx) + intra_luma_not_planar_flag = ff_vvc_intra_luma_not_planar_flag(lc, intra_subpartitions_mode_flag); + if (intra_luma_not_planar_flag) + intra_luma_mpm_idx = ff_vvc_intra_luma_mpm_idx(lc); + } else { + intra_luma_mpm_remainder = ff_vvc_intra_luma_mpm_remainder(lc); + } + + if (!intra_luma_not_planar_flag) { + pred = INTRA_PLANAR; + } else { + const VVCSPS *sps = fc->ps.sps; + const int x_a = (x0 - 1) >> sps->min_cb_log2_size_y; + const int y_a = (y0 + cu->cb_height - 1) >> sps->min_cb_log2_size_y; + const int x_b = (x0 + cu->cb_width - 1) >> sps->min_cb_log2_size_y; + const int y_b = (y0 - 1) >> sps->min_cb_log2_size_y; + int min_cb_width = fc->ps.pps->min_cb_width; + int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y); + int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int available_l = lc->ctb_left_flag || x0b; + const int available_u = lc->ctb_up_flag || y0b; + + int a, b, cand[5]; + + if (!available_l || (SAMPLE_CTB(fc->tab.cpm[0], x_a, y_a) != MODE_INTRA) || + SAMPLE_CTB(fc->tab.imf, x_a, y_a)) { + a = INTRA_PLANAR; + } else { + a = SAMPLE_CTB(fc->tab.ipm, x_a, y_a); + } + + if (!available_u || (SAMPLE_CTB(fc->tab.cpm[0], x_b, y_b) != MODE_INTRA) || + SAMPLE_CTB(fc->tab.imf, x_b, y_b) || !y0b) { + b = INTRA_PLANAR; + } else { + b = SAMPLE_CTB(fc->tab.ipm, x_b, y_b); + } + + if (a == b && a > INTRA_DC) { + cand[0] = a; + cand[1] = 2 + ((a + 61) % 64); + cand[2] = 2 + ((a - 1) % 64); + cand[3] = 2 + ((a + 60) % 64); + cand[4] = 2 + (a % 64); + } else { + const int minab = FFMIN(a, b); + const int maxab = FFMAX(a, b); + if (a > INTRA_DC && b > INTRA_DC) { + const int diff = maxab - minab; + cand[0] = a; + cand[1] = b; + if (diff == 1) { + cand[2] = 2 + ((minab + 61) % 64); + cand[3] = 2 + ((maxab - 1) % 64); + cand[4] = 2 + ((minab + 60) % 64); + } else if (diff >= 62) { + cand[2] = 2 + ((minab - 1) % 64); + cand[3] = 2 + ((maxab + 61) % 64); + cand[4] = 2 + (minab % 64); + } else if (diff == 2) { + cand[2] = 2 + ((minab - 1) % 64); + cand[3] = 2 + ((minab + 61) % 64); + cand[4] = 2 + ((maxab - 1) % 64); + } else { + cand[2] = 2 + ((minab + 61) % 64); + cand[3] = 2 + ((minab - 1) % 64); + cand[4] = 2 + ((maxab + 61) % 64); + } + } else if (a > INTRA_DC || b > INTRA_DC) { + cand[0] = maxab; + cand[1] = 2 + ((maxab + 61 ) % 64); + cand[2] = 2 + ((maxab - 1) % 64); + cand[3] = 2 + ((maxab + 60 ) % 64); + cand[4] = 2 + (maxab % 64); + } else { + cand[0] = INTRA_DC; + cand[1] = INTRA_VERT; + cand[2] = INTRA_HORZ; + cand[3] = INTRA_VERT - 4; + cand[4] = INTRA_VERT + 4; + } + } + if (intra_luma_mpm_flag) { + pred = cand[intra_luma_mpm_idx]; + } else { + qsort(cand, FF_ARRAY_ELEMS(cand), sizeof(cand[0]), less); + pred = intra_luma_mpm_remainder + 1; + for (int i = 0; i < FF_ARRAY_ELEMS(cand); i++) { + if (pred >= cand[i]) + pred++; + } + } + } + return pred; +} + +static int lfnst_idx_decode(VVCLocalContext *lc) +{ + CodingUnit *cu = lc->cu; + const VVCTreeType tree_type = cu->tree_type; + const VVCSPS *sps = lc->fc->ps.sps; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const TransformUnit *tu = cu->tus.head; + int lfnst_width, lfnst_height, min_lfnst; + int lfnst_idx = 0; + + memset(cu->apply_lfnst_flag, 0, sizeof(cu->apply_lfnst_flag)); + + if (!sps->r->sps_lfnst_enabled_flag || cu->pred_mode != MODE_INTRA || FFMAX(cb_width, cb_height) > sps->max_tb_size_y) + return 0; + + while (tu) { + for (int j = 0; j < tu->nb_tbs; j++) { + const TransformBlock *tb = tu->tbs + j; + if (tu->coded_flag[tb->c_idx] && tb->ts) + return 0; + } + tu = tu->next; + } + + if (tree_type == DUAL_TREE_CHROMA) { + lfnst_width = cb_width >> sps->hshift[1]; + lfnst_height = cb_height >> sps->vshift[1]; + } else { + const int vs = cu->isp_split_type == ISP_VER_SPLIT; + const int hs = cu->isp_split_type == ISP_HOR_SPLIT; + lfnst_width = vs ? cb_width / cu->num_intra_subpartitions : cb_width; + lfnst_height = hs ? cb_height / cu->num_intra_subpartitions : cb_height; + } + min_lfnst = FFMIN(lfnst_width, lfnst_height); + if (tree_type != DUAL_TREE_CHROMA && cu->intra_mip_flag && min_lfnst < 16) + return 0; + + if (min_lfnst >= 4) { + if ((cu->isp_split_type != ISP_NO_SPLIT || !lc->parse.lfnst_dc_only) && lc->parse.lfnst_zero_out_sig_coeff_flag) + lfnst_idx = ff_vvc_lfnst_idx(lc, tree_type != SINGLE_TREE); + } + + if (lfnst_idx) { + cu->apply_lfnst_flag[LUMA] = tree_type != DUAL_TREE_CHROMA; + cu->apply_lfnst_flag[CB] = cu->apply_lfnst_flag[CR] = tree_type == DUAL_TREE_CHROMA; + } + + return lfnst_idx; +} + +static MtsIdx mts_idx_decode(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + const VVCSPS *sps = lc->fc->ps.sps; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const uint8_t transform_skip_flag = cu->tus.head->tbs[0].ts; //fix me + int mts_idx = MTS_DCT2_DCT2; + if (cu->tree_type != DUAL_TREE_CHROMA && !cu->lfnst_idx && + !transform_skip_flag && FFMAX(cb_width, cb_height) <= 32 && + cu->isp_split_type == ISP_NO_SPLIT && !cu->sbt_flag && + lc->parse.mts_zero_out_sig_coeff_flag && !lc->parse.mts_dc_only) { + if ((cu->pred_mode == MODE_INTER && sps->r->sps_explicit_mts_inter_enabled_flag) || + (cu->pred_mode == MODE_INTRA && sps->r->sps_explicit_mts_intra_enabled_flag)) { + mts_idx = ff_vvc_mts_idx(lc); + } + } + + return mts_idx; +} + +static enum IntraPredMode derive_center_luma_intra_pred_mode(const VVCFrameContext *fc, const VVCSPS *sps, const VVCPPS *pps, const CodingUnit *cu) +{ + const int x_center = (cu->x0 + cu->cb_width / 2) >> sps->min_cb_log2_size_y; + const int y_center = (cu->y0 + cu->cb_height / 2) >> sps->min_cb_log2_size_y; + const int min_cb_width = pps->min_cb_width; + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_center, y_center); + const int cu_pred_mode = SAMPLE_CTB(fc->tab.cpm[0], x_center, y_center); + const int intra_pred_mode_y = SAMPLE_CTB(fc->tab.ipm, x_center, y_center); + + if (intra_mip_flag) { + if (cu->tree_type == SINGLE_TREE && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444) + return INTRA_INVALID; + return INTRA_PLANAR; + } + if (cu_pred_mode == MODE_IBC || cu_pred_mode == MODE_PLT) + return INTRA_DC; + return intra_pred_mode_y; +} + +static void derive_chroma_intra_pred_mode(VVCLocalContext *lc, + const int cclm_mode_flag, const int cclm_mode_idx, const int intra_chroma_pred_mode) +{ + const VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int x_cb = cu->x0 >> sps->min_cb_log2_size_y; + const int y_cb = cu->y0 >> sps->min_cb_log2_size_y; + const int min_cb_width = pps->min_cb_width; + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_cb, y_cb); + enum IntraPredMode luma_intra_pred_mode = SAMPLE_CTB(fc->tab.ipm, x_cb, y_cb); + + if (cu->tree_type == SINGLE_TREE && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444 && + intra_chroma_pred_mode == 4 && intra_mip_flag) { + cu->mip_chroma_direct_flag = 1; + cu->intra_pred_mode_c = luma_intra_pred_mode; + return; + } + luma_intra_pred_mode = derive_center_luma_intra_pred_mode(fc, sps, pps, cu); + + if (cu->act_enabled_flag) { + cu->intra_pred_mode_c = luma_intra_pred_mode; + return; + } + if (cclm_mode_flag) { + cu->intra_pred_mode_c = INTRA_LT_CCLM + cclm_mode_idx; + } else if (intra_chroma_pred_mode == 4){ + cu->intra_pred_mode_c = luma_intra_pred_mode; + } else { + const static IntraPredMode pred_mode_c[][4 + 1] = { + {INTRA_VDIAG, INTRA_PLANAR, INTRA_PLANAR, INTRA_PLANAR, INTRA_PLANAR}, + {INTRA_VERT, INTRA_VDIAG, INTRA_VERT, INTRA_VERT, INTRA_VERT}, + {INTRA_HORZ, INTRA_HORZ, INTRA_VDIAG, INTRA_HORZ, INTRA_HORZ}, + {INTRA_DC, INTRA_DC, INTRA_DC, INTRA_VDIAG, INTRA_DC}, + }; + const int modes[4] = {INTRA_PLANAR, INTRA_VERT, INTRA_HORZ, INTRA_DC}; + int idx; + + // This workaround is necessary to have 4:4:4 video decode correctly + // See VVC ticket https://jvet.hhi.fraunhofer.de/trac/vvc/ticket/1602 + // and VTM source https://vcgit.hhi.fraunhofer.de/jvet/VVCSoftware_VTM/-/blob/master/source/Lib/CommonLib/UnitTools.cpp#L736 + if (cu->tree_type == SINGLE_TREE && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444 && intra_mip_flag) { + idx = 4; + } else { + for (idx = 0; idx < FF_ARRAY_ELEMS(modes); idx++) { + if (modes[idx] == luma_intra_pred_mode) + break; + } + } + + cu->intra_pred_mode_c = pred_mode_c[intra_chroma_pred_mode][idx]; + } + if (sps->r->sps_chroma_format_idc == CHROMA_FORMAT_422 && cu->intra_pred_mode_c <= INTRA_VDIAG) { + const static int mode_map_422[INTRA_VDIAG + 1] = { + 0, 1, 61, 62, 63, 64, 65, 66, 2, 3, 5, 6, 8, 10, 12, 13, + 14, 16, 18, 20, 22, 23, 24, 26, 28, 30, 31, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 41, 42, 43, 43, 44, 44, 45, 45, 46, 47, 48, 48, + 49, 49, 50, 51, 51, 52, 52, 53, 54, 55, 55, 56, 56, 57, 57, 58, + 59, 59, 60, + }; + cu->intra_pred_mode_c = mode_map_422[cu->intra_pred_mode_c]; + } +} + +static void intra_luma_pred_modes(VVCLocalContext *lc) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + CodingUnit *cu = lc->cu; + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int x0 = cu->x0; + const int y0 = cu->y0; + const int x_cb = x0 >> log2_min_cb_size; + const int y_cb = y0 >> log2_min_cb_size; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + + cu->intra_luma_ref_idx = 0; + if (sps->r->sps_bdpcm_enabled_flag && cb_width <= sps->max_ts_size && cb_height <= sps->max_ts_size) + cu->bdpcm_flag[LUMA] = ff_vvc_intra_bdpcm_luma_flag(lc); + if (cu->bdpcm_flag[LUMA]) { + cu->intra_pred_mode_y = ff_vvc_intra_bdpcm_luma_dir_flag(lc) ? INTRA_VERT : INTRA_HORZ; + } else { + if (sps->r->sps_mip_enabled_flag) + cu->intra_mip_flag = ff_vvc_intra_mip_flag(lc, fc->tab.imf); + if (cu->intra_mip_flag) { + int intra_mip_transposed_flag = ff_vvc_intra_mip_transposed_flag(lc); + int intra_mip_mode = ff_vvc_intra_mip_mode(lc); + int x = y_cb * pps->min_cb_width + x_cb; + for (int y = 0; y < (cb_height>>log2_min_cb_size); y++) { + int width = cb_width>>log2_min_cb_size; + memset(&fc->tab.imf[x], cu->intra_mip_flag, width); + fc->tab.imtf[x] = intra_mip_transposed_flag; + fc->tab.imm[x] = intra_mip_mode; + x += pps->min_cb_width; + } + cu->intra_pred_mode_y = intra_mip_mode; + } else { + int intra_subpartitions_mode_flag = 0; + if (sps->r->sps_mrl_enabled_flag && ((y0 % sps->ctb_size_y) > 0)) + cu->intra_luma_ref_idx = ff_vvc_intra_luma_ref_idx(lc); + if (sps->r->sps_isp_enabled_flag && !cu->intra_luma_ref_idx && + (cb_width <= sps->max_tb_size_y && cb_height <= sps->max_tb_size_y) && + (cb_width * cb_height > MIN_TU_SIZE * MIN_TU_SIZE) && + !cu->act_enabled_flag) + intra_subpartitions_mode_flag = ff_vvc_intra_subpartitions_mode_flag(lc); + if (!(x0 & 63) && !(y0 & 63)) + TAB_ISPMF(fc, x0, y0) = intra_subpartitions_mode_flag; + cu->isp_split_type = ff_vvc_isp_split_type(lc, intra_subpartitions_mode_flag); + cu->num_intra_subpartitions = get_num_intra_subpartitions(cu->isp_split_type, cb_width, cb_height); + cu->intra_pred_mode_y = luma_intra_pred_mode(lc, intra_subpartitions_mode_flag); + } + } + set_cb_tab(lc, fc->tab.ipm, cu->intra_pred_mode_y); +} + +static void intra_chroma_pred_modes(VVCLocalContext *lc) +{ + const VVCSPS *sps = lc->fc->ps.sps; + CodingUnit *cu = lc->cu; + const int hs = sps->hshift[CHROMA]; + const int vs = sps->vshift[CHROMA]; + + cu->mip_chroma_direct_flag = 0; + if (sps->r->sps_bdpcm_enabled_flag && + (cu->cb_width >> hs) <= sps->max_ts_size && + (cu->cb_height >> vs) <= sps->max_ts_size) { + cu->bdpcm_flag[CB] = cu->bdpcm_flag[CR] = ff_vvc_intra_bdpcm_chroma_flag(lc); + } + if (cu->bdpcm_flag[CHROMA]) { + cu->intra_pred_mode_c = ff_vvc_intra_bdpcm_chroma_dir_flag(lc) ? INTRA_VERT : INTRA_HORZ; + } else { + const int cclm_enabled = get_cclm_enabled(lc, cu->x0, cu->y0); + int cclm_mode_flag = 0; + int cclm_mode_idx = 0; + int intra_chroma_pred_mode = 0; + + if (cclm_enabled) + cclm_mode_flag = ff_vvc_cclm_mode_flag(lc); + + if (cclm_mode_flag) + cclm_mode_idx = ff_vvc_cclm_mode_idx(lc); + else + intra_chroma_pred_mode = ff_vvc_intra_chroma_pred_mode(lc); + derive_chroma_intra_pred_mode(lc, cclm_mode_flag, cclm_mode_idx, intra_chroma_pred_mode); + } +} + +static PredMode pred_mode_decode(VVCLocalContext *lc, + const VVCTreeType tree_type, + const VVCModeType mode_type) +{ + const VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const VVCSPS *sps = fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int ch_type = tree_type == DUAL_TREE_CHROMA ? 1 : 0; + const int is_4x4 = cu->cb_width == 4 && cu->cb_height == 4; + int pred_mode_flag; + int pred_mode_ibc_flag; + PredMode pred_mode; + + cu->skip_flag = 0; + if (!IS_I(rsh) || sps->r->sps_ibc_enabled_flag) { + const int is_128 = cu->cb_width == 128 || cu->cb_height == 128; + if (tree_type != DUAL_TREE_CHROMA && + ((!is_4x4 && mode_type != MODE_TYPE_INTRA) || + (sps->r->sps_ibc_enabled_flag && !is_128))) { + cu->skip_flag = ff_vvc_cu_skip_flag(lc, fc->tab.skip); + } + + if (is_4x4 || mode_type == MODE_TYPE_INTRA || IS_I(rsh)) { + pred_mode_flag = 1; + } else if (mode_type == MODE_TYPE_INTER || cu->skip_flag) { + pred_mode_flag = 0; + } else { + pred_mode_flag = ff_vvc_pred_mode_flag(lc, ch_type); + } + pred_mode = pred_mode_flag ? MODE_INTRA : MODE_INTER; + + if (((IS_I(rsh) && !cu->skip_flag) || + (!IS_I(rsh) && (pred_mode != MODE_INTRA || + ((is_4x4 || mode_type == MODE_TYPE_INTRA) && !cu->skip_flag)))) && + !is_128 && mode_type != MODE_TYPE_INTER && sps->r->sps_ibc_enabled_flag && + tree_type != DUAL_TREE_CHROMA) { + pred_mode_ibc_flag = ff_vvc_pred_mode_ibc_flag(lc, ch_type); + } else if (cu->skip_flag && (is_4x4 || mode_type == MODE_TYPE_INTRA)) { + pred_mode_ibc_flag = 1; + } else if (is_128 || mode_type == MODE_TYPE_INTER || tree_type == DUAL_TREE_CHROMA) { + pred_mode_ibc_flag = 0; + } else { + pred_mode_ibc_flag = (IS_I(rsh)) ? sps->r->sps_ibc_enabled_flag : 0; + } + if (pred_mode_ibc_flag) + pred_mode = MODE_IBC; + } else { + pred_mode_flag = is_4x4 || mode_type == MODE_TYPE_INTRA || + mode_type != MODE_TYPE_INTER || IS_I(rsh); + pred_mode = pred_mode_flag ? MODE_INTRA : MODE_INTER; + } + return pred_mode; +} + +static void sbt_info(VVCLocalContext *lc, const VVCSPS *sps) +{ + CodingUnit *cu = lc->cu; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + + if (cu->pred_mode == MODE_INTER && sps->r->sps_sbt_enabled_flag && !cu->ciip_flag + && cb_width <= sps->max_tb_size_y && cb_height <= sps->max_tb_size_y) { + const int sbt_ver_h = cb_width >= 8; + const int sbt_hor_h = cb_height >= 8; + cu->sbt_flag = 0; + if (sbt_ver_h || sbt_hor_h) + cu->sbt_flag = ff_vvc_sbt_flag(lc); + if (cu->sbt_flag) { + const int sbt_ver_q = cb_width >= 16; + const int sbt_hor_q = cb_height >= 16; + int cu_sbt_quad_flag = 0; + + if ((sbt_ver_h || sbt_hor_h) && (sbt_ver_q || sbt_hor_q)) + cu_sbt_quad_flag = ff_vvc_sbt_quad_flag(lc); + if (cu_sbt_quad_flag) { + cu->sbt_horizontal_flag = sbt_hor_q; + if (sbt_ver_q && sbt_hor_q) + cu->sbt_horizontal_flag = ff_vvc_sbt_horizontal_flag(lc); + } else { + cu->sbt_horizontal_flag = sbt_hor_h; + if (sbt_ver_h && sbt_hor_h) + cu->sbt_horizontal_flag = ff_vvc_sbt_horizontal_flag(lc); + } + cu->sbt_pos_flag = ff_vvc_sbt_pos_flag(lc); + + { + const int sbt_min = cu_sbt_quad_flag ? 1 : 2; + lc->parse.sbt_num_fourths_tb0 = cu->sbt_pos_flag ? (4 - sbt_min) : sbt_min; + } + } + } +} + +static int skipped_transform_tree_unit(VVCLocalContext *lc) +{ + const H266RawSPS *rsps = lc->fc->ps.sps->r; + const CodingUnit *cu = lc->cu; + int ret; + + if (cu->tree_type != DUAL_TREE_CHROMA) + set_qp_y(lc, cu->x0, cu->y0, 0); + if (rsps->sps_chroma_format_idc && cu->tree_type != DUAL_TREE_LUMA) + set_qp_c(lc); + ret = skipped_transform_tree(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (ret < 0) + return ret; + return 0; +} + +static void set_cb_pos(const VVCFrameContext *fc, const CodingUnit *cu) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int x_cb = cu->x0 >> log2_min_cb_size; + const int y_cb = cu->y0 >> log2_min_cb_size; + const int ch_type = cu->ch_type; + int x, y; + + x = y_cb * pps->min_cb_width + x_cb; + for (y = 0; y < (cu->cb_height >> log2_min_cb_size); y++) { + const int width = cu->cb_width >> log2_min_cb_size; + + for (int i = 0; i < width; i++) { + fc->tab.cb_pos_x[ch_type][x + i] = cu->x0; + fc->tab.cb_pos_y[ch_type][x + i] = cu->y0; + } + memset(&fc->tab.cb_width[ch_type][x], cu->cb_width, width); + memset(&fc->tab.cb_height[ch_type][x], cu->cb_height, width); + memset(&fc->tab.cqt_depth[ch_type][x], cu->cqt_depth, width); + + x += pps->min_cb_width; + } +} + +static CodingUnit* alloc_cu(VVCLocalContext *lc, const int x0, const int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int rx = x0 >> sps->ctb_log2_size_y; + const int ry = y0 >> sps->ctb_log2_size_y; + CTU *ctu = fc->tab.ctus + ry * pps->ctb_width + rx; + CodingUnit *cu = ff_refstruct_pool_get(fc->cu_pool); + + if (!cu) + return NULL; + cu->next = NULL; + + if (lc->cu) + lc->cu->next = cu; + else + ctu->cus = cu; + lc->cu = cu; + + return cu; +} + +static CodingUnit* add_cu(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const int cqt_depth, const VVCTreeType tree_type) +{ + VVCFrameContext *fc = lc->fc; + const int ch_type = tree_type == DUAL_TREE_CHROMA ? 1 : 0; + CodingUnit *cu = alloc_cu(lc, x0, y0); + + if (!cu) + return NULL; + + memset(&cu->pu, 0, sizeof(cu->pu)); + + lc->parse.prev_tu_cbf_y = 0; + + cu->sbt_flag = 0; + cu->act_enabled_flag = 0; + + cu->tree_type = tree_type; + cu->x0 = x0; + cu->y0 = y0; + cu->cb_width = cb_width; + cu->cb_height = cb_height; + cu->ch_type = ch_type; + cu->cqt_depth = cqt_depth; + cu->tus.head = cu->tus.tail = NULL; + cu->bdpcm_flag[LUMA] = cu->bdpcm_flag[CB] = cu->bdpcm_flag[CR] = 0; + cu->isp_split_type = ISP_NO_SPLIT; + cu->intra_mip_flag = 0; + cu->ciip_flag = 0; + cu->coded_flag = 1; + cu->num_intra_subpartitions = 1; + cu->pu.dmvr_flag = 0; + + set_cb_pos(fc, cu); + return cu; +} + +static void set_cu_tabs(const VVCLocalContext *lc, const CodingUnit *cu) +{ + const VVCFrameContext *fc = lc->fc; + const TransformUnit *tu = cu->tus.head; + + if (cu->tree_type != DUAL_TREE_CHROMA) { + set_cb_tab(lc, fc->tab.cpm[LUMA], cu->pred_mode); + set_cb_tab(lc, fc->tab.skip, cu->skip_flag); + } + if (fc->ps.sps->r->sps_chroma_format_idc && cu->tree_type != DUAL_TREE_LUMA) + set_cb_tab(lc, fc->tab.cpm[CHROMA], cu->pred_mode); + + while (tu) { + for (int j = 0; j < tu->nb_tbs; j++) { + const TransformBlock *tb = tu->tbs + j; + if (tb->c_idx != LUMA) + set_qp_c_tab(lc, tu, tb); + if (tb->c_idx != CR && cu->bdpcm_flag[tb->c_idx]) + set_tb_tab(fc->tab.pcmf[tb->c_idx], 1, fc, tb); + } + tu = tu->next; + } +} + +//8.5.2.7 Derivation process for merge motion vector difference +static void derive_mmvd(const VVCLocalContext *lc, MvField *mvf, const Mv *mmvd_offset) +{ + const SliceContext *sc = lc->sc; + Mv mmvd[2]; + + if (mvf->pred_flag == PF_BI) { + const RefPicList *rpl = sc->rpl; + const int poc = lc->fc->ps.ph.poc; + const int diff[] = { + poc - rpl[0].list[mvf->ref_idx[0]], + poc - rpl[1].list[mvf->ref_idx[1]] + }; + const int sign = FFSIGN(diff[0]) != FFSIGN(diff[1]); + + if (diff[0] == diff[1]) { + mmvd[1] = mmvd[0] = *mmvd_offset; + } + else { + const int i = FFABS(diff[0]) < FFABS(diff[1]); + const int o = !i; + mmvd[i] = *mmvd_offset; + if (!rpl[0].isLongTerm[mvf->ref_idx[0]] && !rpl[1].isLongTerm[mvf->ref_idx[1]]) { + ff_vvc_mv_scale(&mmvd[o], mmvd_offset, diff[i], diff[o]); + } + else { + mmvd[o].x = sign ? -mmvd[i].x : mmvd[i].x; + mmvd[o].y = sign ? -mmvd[i].y : mmvd[i].y; + } + } + mvf->mv[0].x += mmvd[0].x; + mvf->mv[0].y += mmvd[0].y; + mvf->mv[1].x += mmvd[1].x; + mvf->mv[1].y += mmvd[1].y; + } else { + const int idx = mvf->pred_flag - PF_L0; + mvf->mv[idx].x += mmvd_offset->x; + mvf->mv[idx].y += mmvd_offset->y; + } + +} + +static void mvf_to_mi(const MvField *mvf, MotionInfo *mi) +{ + mi->pred_flag = mvf->pred_flag; + mi->bcw_idx = mvf->bcw_idx; + mi->hpel_if_idx = mvf->hpel_if_idx; + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + 1; + if (mvf->pred_flag & mask) { + mi->mv[i][0] = mvf->mv[i]; + mi->ref_idx[i] = mvf->ref_idx[i]; + } + } +} + +static void mv_merge_refine_pred_flag(MvField *mvf, const int width, const int height) +{ + if (mvf->pred_flag == PF_BI && (width + height) == 12) { + mvf->pred_flag = PF_L0; + mvf->bcw_idx = 0; + } +} + +// subblock-based inter prediction data +static void merge_data_subblock(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPH *ph = &fc->ps.ph; + CodingUnit* cu = lc->cu; + PredictionUnit *pu = &cu->pu; + int merge_subblock_idx = 0; + + set_cb_tab(lc, fc->tab.msf, pu->merge_subblock_flag); + if (ph->max_num_subblock_merge_cand > 1) { + merge_subblock_idx = ff_vvc_merge_subblock_idx(lc, ph->max_num_subblock_merge_cand); + } + ff_vvc_sb_mv_merge_mode(lc, merge_subblock_idx, pu); +} + +static void merge_data_regular(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPH *ph = &fc->ps.ph; + const CodingUnit* cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + int merge_idx = 0; + Mv mmvd_offset; + MvField mvf; + + if (sps->r->sps_mmvd_enabled_flag) + pu->mmvd_merge_flag = ff_vvc_mmvd_merge_flag(lc); + if (pu->mmvd_merge_flag) { + int mmvd_cand_flag = 0; + if (sps->max_num_merge_cand > 1) + mmvd_cand_flag = ff_vvc_mmvd_cand_flag(lc); + ff_vvc_mmvd_offset_coding(lc, &mmvd_offset, ph->r->ph_mmvd_fullpel_only_flag); + merge_idx = mmvd_cand_flag; + } else if (sps->max_num_merge_cand > 1) { + merge_idx = ff_vvc_merge_idx(lc); + } + ff_vvc_luma_mv_merge_mode(lc, merge_idx, 0, &mvf); + if (pu->mmvd_merge_flag) + derive_mmvd(lc, &mvf, &mmvd_offset); + mv_merge_refine_pred_flag(&mvf, cu->cb_width, cu->cb_height); + ff_vvc_store_mvf(lc, &mvf); + mvf_to_mi(&mvf, &pu->mi); +} + +static int ciip_flag_decode(VVCLocalContext *lc, const int ciip_avaiable, const int gpm_avaiable, const int is_128) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const CodingUnit *cu = lc->cu; + + if (ciip_avaiable && gpm_avaiable) + return ff_vvc_ciip_flag(lc); + return sps->r->sps_ciip_enabled_flag && !cu->skip_flag && + !is_128 && (cu->cb_width * cu->cb_height >= 64); +} + +static void merge_data_gpm(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + PredictionUnit *pu = &lc->cu->pu; + int merge_gpm_idx[2]; + + pu->merge_gpm_flag = 1; + pu->gpm_partition_idx = ff_vvc_merge_gpm_partition_idx(lc); + merge_gpm_idx[0] = ff_vvc_merge_gpm_idx(lc, 0); + merge_gpm_idx[1] = 0; + if (sps->max_num_gpm_merge_cand > 2) + merge_gpm_idx[1] = ff_vvc_merge_gpm_idx(lc, 1); + + ff_vvc_luma_mv_merge_gpm(lc, merge_gpm_idx, pu->gpm_mv); + ff_vvc_store_gpm_mvf(lc, pu); +} + +static void merge_data_ciip(VVCLocalContext *lc) +{ + const VVCFrameContext* fc = lc->fc; + const VVCSPS* sps = fc->ps.sps; + CodingUnit *cu = lc->cu; + MotionInfo *mi = &cu->pu.mi; + int merge_idx = 0; + MvField mvf; + + if (sps->max_num_merge_cand > 1) + merge_idx = ff_vvc_merge_idx(lc); + ff_vvc_luma_mv_merge_mode(lc, merge_idx, 1, &mvf); + mv_merge_refine_pred_flag(&mvf, cu->cb_width, cu->cb_height); + ff_vvc_store_mvf(lc, &mvf); + mvf_to_mi(&mvf, mi); + cu->intra_pred_mode_y = cu->intra_pred_mode_c = INTRA_PLANAR; + cu->intra_luma_ref_idx = 0; + cu->intra_mip_flag = 0; +} + +// block-based inter prediction data +static void merge_data_block(VVCLocalContext *lc) +{ + const VVCFrameContext* fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + CodingUnit *cu = lc->cu; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const int is_128 = cb_width == 128 || cb_height == 128; + const int ciip_avaiable = sps->r->sps_ciip_enabled_flag && + !cu->skip_flag && (cb_width * cb_height >= 64); + const int gpm_avaiable = sps->r->sps_gpm_enabled_flag && IS_B(rsh) && + (cb_width >= 8) && (cb_height >=8) && + (cb_width < 8 * cb_height) && (cb_height < 8 *cb_width); + + int regular_merge_flag = 1; + + if (!is_128 && (ciip_avaiable || gpm_avaiable)) + regular_merge_flag = ff_vvc_regular_merge_flag(lc, cu->skip_flag); + if (regular_merge_flag) { + merge_data_regular(lc); + } else { + cu->ciip_flag = ciip_flag_decode(lc, ciip_avaiable, gpm_avaiable, is_128); + if (cu->ciip_flag) + merge_data_ciip(lc); + else + merge_data_gpm(lc); + } +} + +static void merge_data_ibc(VVCLocalContext *lc) +{ + const VVCFrameContext* fc = lc->fc; + const VVCSPS* sps = fc->ps.sps; + MotionInfo *mi = &lc->cu->pu.mi; + int merge_idx = 0; + + mi->pred_flag = PF_IBC; + + if (sps->max_num_ibc_merge_cand > 1) + merge_idx = ff_vvc_merge_idx(lc); + + ff_vvc_luma_mv_merge_ibc(lc, merge_idx, &mi->mv[L0][0]); + ff_vvc_store_mv(lc, mi); +} + +static int hls_merge_data(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPH *ph = &fc->ps.ph; + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + + pu->merge_gpm_flag = 0; + pu->mi.num_sb_x = pu->mi.num_sb_y = 1; + if (cu->pred_mode == MODE_IBC) { + merge_data_ibc(lc); + } else { + if (ph->max_num_subblock_merge_cand > 0 && cu->cb_width >= 8 && cu->cb_height >= 8) + pu->merge_subblock_flag = ff_vvc_merge_subblock_flag(lc); + if (pu->merge_subblock_flag) + merge_data_subblock(lc); + else + merge_data_block(lc); + } + return 0; +} + +static void hls_mvd_coding(VVCLocalContext *lc, Mv* mvd) +{ + int16_t mv[2]; + + for (int i = 0; i < 2; i++) { + mv[i] = ff_vvc_abs_mvd_greater0_flag(lc); + } + + for (int i = 0; i < 2; i++) { + if (mv[i]) + mv[i] += ff_vvc_abs_mvd_greater1_flag(lc); + } + + for (int i = 0; i < 2; i++) { + if (mv[i] > 0) { + if (mv[i] == 2) + mv[i] += ff_vvc_abs_mvd_minus2(lc); + mv[i] = (1 - 2 * ff_vvc_mvd_sign_flag(lc)) * mv[i]; + } + } + mvd->x = mv[0]; + mvd->y = mv[1]; +} + +static int bcw_idx_decode(VVCLocalContext *lc, const MotionInfo *mi, const int cb_width, const int cb_height) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const VVCPH *ph = &fc->ps.ph; + const VVCSH *sh = &lc->sc->sh; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &ph->pwt : &sh->pwt; + int bcw_idx = 0; + + if (sps->r->sps_bcw_enabled_flag && mi->pred_flag == PF_BI && + !w->weight_flag[L0][LUMA][mi->ref_idx[0]] && + !w->weight_flag[L1][LUMA][mi->ref_idx[1]] && + !w->weight_flag[L0][CHROMA][mi->ref_idx[0]] && + !w->weight_flag[L1][CHROMA][mi->ref_idx[1]] && + cb_width * cb_height >= 256) { + bcw_idx = ff_vvc_bcw_idx(lc, ff_vvc_no_backward_pred_flag(lc)); + } + return bcw_idx; +} + +static int8_t ref_idx_decode(VVCLocalContext *lc, const VVCSH *sh, const int sym_mvd_flag, const int lx) +{ + const H266RawSliceHeader *rsh = sh->r; + int ref_idx = 0; + + if (rsh->num_ref_idx_active[lx] > 1 && !sym_mvd_flag) + ref_idx = ff_vvc_ref_idx_lx(lc, rsh->num_ref_idx_active[lx]); + else if (sym_mvd_flag) + ref_idx = sh->ref_idx_sym[lx]; + return ref_idx; +} + +static int mvds_decode(VVCLocalContext *lc, Mv mvds[2][MAX_CONTROL_POINTS], + const int num_cp_mv, const int lx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPH *ph = &fc->ps.ph; + const PredictionUnit *pu = &lc->cu->pu; + const MotionInfo *mi = &pu->mi; + int has_no_zero_mvd = 0; + + if (lx == L1 && ph->r->ph_mvd_l1_zero_flag && mi->pred_flag == PF_BI) { + for (int j = 0; j < num_cp_mv; j++) + AV_ZERO64(&mvds[lx][j]); + } else { + Mv *mvd0 = &mvds[lx][0]; + if (lx == L1 && pu->sym_mvd_flag) { + mvd0->x = -mvds[L0][0].x; + mvd0->y = -mvds[L0][0].y; + } else { + hls_mvd_coding(lc, mvd0); + } + has_no_zero_mvd |= (mvd0->x || mvd0->y); + for (int j = 1; j < num_cp_mv; j++) { + Mv *mvd = &mvds[lx][j]; + hls_mvd_coding(lc, mvd); + mvd->x += mvd0->x; + mvd->y += mvd0->y; + has_no_zero_mvd |= (mvd->x || mvd->y); + } + } + return has_no_zero_mvd; +} + +static void mvp_add_difference(MotionInfo *mi, const int num_cp_mv, + const Mv mvds[2][MAX_CONTROL_POINTS], const int amvr_shift) +{ + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + PF_L0; + if (mi->pred_flag & mask) { + for (int j = 0; j < num_cp_mv; j++) { + const Mv *mvd = &mvds[i][j]; + mi->mv[i][j].x += mvd->x * (1 << amvr_shift); + mi->mv[i][j].y += mvd->y * (1 << amvr_shift); + } + } + } +} + +static int mvp_data_ibc(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const PredictionUnit *pu = &lc->cu->pu; + const VVCSPS *sps = fc->ps.sps; + MotionInfo *mi = &lc->cu->pu.mi; + int mvp_l0_flag = 0; + int amvr_shift = 4; + Mv *mv = &mi->mv[L0][0]; + + mi->pred_flag = PF_IBC; + mi->num_sb_x = 1; + mi->num_sb_y = 1; + + hls_mvd_coding(lc, mv); + if (sps->max_num_ibc_merge_cand > 1) + mvp_l0_flag = ff_vvc_mvp_lx_flag(lc); + if (sps->r->sps_amvr_enabled_flag && (mv->x || mv->y)) + amvr_shift = ff_vvc_amvr_shift(lc, pu->inter_affine_flag, cu->pred_mode, 1); + + ff_vvc_mvp_ibc(lc, mvp_l0_flag, amvr_shift, mv); + ff_vvc_store_mv(lc, mi); + + return 0; +} + +static int mvp_data(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + const VVCSPS *sps = fc->ps.sps; + const VVCPH *ph = &fc->ps.ph; + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + MotionInfo *mi = &pu->mi; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + + int mvp_lx_flag[2] = {0}; + int cu_affine_type_flag = 0; + int num_cp_mv; + int amvr_enabled, has_no_zero_mvd = 0, amvr_shift; + Mv mvds[2][MAX_CONTROL_POINTS]; + + mi->pred_flag = ff_vvc_pred_flag(lc, IS_B(rsh)); + if (sps->r->sps_affine_enabled_flag && cb_width >= 16 && cb_height >= 16) { + pu->inter_affine_flag = ff_vvc_inter_affine_flag(lc); + set_cb_tab(lc, fc->tab.iaf, pu->inter_affine_flag); + if (sps->r->sps_6param_affine_enabled_flag && pu->inter_affine_flag) + cu_affine_type_flag = ff_vvc_cu_affine_type_flag(lc); + } + mi->motion_model_idc = pu->inter_affine_flag + cu_affine_type_flag; + num_cp_mv = mi->motion_model_idc + 1; + + if (sps->r->sps_smvd_enabled_flag && !ph->r->ph_mvd_l1_zero_flag && + mi->pred_flag == PF_BI && !pu->inter_affine_flag && + sh->ref_idx_sym[0] > -1 && sh->ref_idx_sym[1] > -1) + pu->sym_mvd_flag = ff_vvc_sym_mvd_flag(lc); + + for (int i = L0; i <= L1; i++) { + const PredFlag pred_flag = PF_L0 + !i; + if (mi->pred_flag != pred_flag) { + mi->ref_idx[i] = ref_idx_decode(lc, sh, pu->sym_mvd_flag, i); + has_no_zero_mvd |= mvds_decode(lc, mvds, num_cp_mv, i); + mvp_lx_flag[i] = ff_vvc_mvp_lx_flag(lc); + } + } + + amvr_enabled = mi->motion_model_idc == MOTION_TRANSLATION ? + sps->r->sps_amvr_enabled_flag : sps->r->sps_affine_amvr_enabled_flag; + amvr_enabled &= has_no_zero_mvd; + + amvr_shift = ff_vvc_amvr_shift(lc, pu->inter_affine_flag, cu->pred_mode, amvr_enabled); + + mi->hpel_if_idx = amvr_shift == 3; + mi->bcw_idx = bcw_idx_decode(lc, mi, cb_width, cb_height); + + if (mi->motion_model_idc) + ff_vvc_affine_mvp(lc, mvp_lx_flag, amvr_shift, mi); + else + ff_vvc_mvp(lc, mvp_lx_flag, amvr_shift, mi); + + mvp_add_difference(mi, num_cp_mv, mvds, amvr_shift); + + if (mi->motion_model_idc) + ff_vvc_store_sb_mvs(lc, pu); + else + ff_vvc_store_mv(lc, &pu->mi); + + return 0; +} + +// derive bdofFlag from 8.5.6 Decoding process for inter blocks +// derive dmvr from 8.5.1 General decoding process for coding units coded in inter prediction mode +static void derive_dmvr_bdof_flag(const VVCLocalContext *lc, PredictionUnit *pu) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCPH *ph = &fc->ps.ph; + const VVCSH *sh = &lc->sc->sh; + const int poc = ph->poc; + const RefPicList *rpl0 = lc->sc->rpl + L0; + const RefPicList *rpl1 = lc->sc->rpl + L1; + const int8_t *ref_idx = pu->mi.ref_idx; + const MotionInfo *mi = &pu->mi; + const CodingUnit *cu = lc->cu; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &fc->ps.ph.pwt : &sh->pwt; + + pu->bdof_flag = 0; + + if (mi->pred_flag == PF_BI && + (poc - rpl0->list[ref_idx[L0]] == rpl1->list[ref_idx[L1]] - poc) && + !rpl0->isLongTerm[ref_idx[L0]] && !rpl1->isLongTerm[ref_idx[L1]] && + !cu->ciip_flag && + !mi->bcw_idx && + !w->weight_flag[L0][LUMA][mi->ref_idx[L0]] && !w->weight_flag[L1][LUMA][mi->ref_idx[L1]] && + !w->weight_flag[L0][CHROMA][mi->ref_idx[L0]] && !w->weight_flag[L1][CHROMA][mi->ref_idx[L1]] && + cu->cb_width >= 8 && cu->cb_height >= 8 && + (cu->cb_width * cu->cb_height >= 128)) { + // fixme: for RprConstraintsActiveFlag + if (!ph->r->ph_bdof_disabled_flag && + mi->motion_model_idc == MOTION_TRANSLATION && + !pu->merge_subblock_flag && + !pu->sym_mvd_flag) + pu->bdof_flag = 1; + if (!ph->r->ph_dmvr_disabled_flag && + pu->general_merge_flag && + !pu->mmvd_merge_flag) + pu->dmvr_flag = 1; + } +} + +// part of 8.5.1 General decoding process for coding units coded in inter prediction mode +static void refine_regular_subblock(const VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + + derive_dmvr_bdof_flag(lc, pu); + if (pu->dmvr_flag || pu->bdof_flag) { + pu->mi.num_sb_x = (cu->cb_width > 16) ? (cu->cb_width >> 4) : 1; + pu->mi.num_sb_y = (cu->cb_height > 16) ? (cu->cb_height >> 4) : 1; + } +} + +static void fill_dmvr_info(const VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + + if (cu->pred_mode == MODE_IBC) { + ff_vvc_set_intra_mvf(lc, 1); + } else { + const VVCPPS *pps = fc->ps.pps; + const int w = cu->cb_width >> MIN_PU_LOG2; + + for (int y = cu->y0 >> MIN_PU_LOG2; y < (cu->y0 + cu->cb_height) >> MIN_PU_LOG2; y++) { + const int idx = pps->min_pu_width * y + (cu->x0 >> MIN_PU_LOG2); + const MvField *mvf = fc->tab.mvf + idx; + MvField *dmvr_mvf = fc->ref->tab_dmvr_mvf + idx; + + memcpy(dmvr_mvf, mvf, sizeof(MvField) * w); + } + } +} + +static int inter_data(VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + const MotionInfo *mi = &pu->mi; + int ret = 0; + + pu->general_merge_flag = 1; + if (!cu->skip_flag) + pu->general_merge_flag = ff_vvc_general_merge_flag(lc); + + if (pu->general_merge_flag) { + hls_merge_data(lc); + } else if (cu->pred_mode == MODE_IBC){ + ret = mvp_data_ibc(lc); + } else { + ret = mvp_data(lc); + } + + if (cu->pred_mode == MODE_IBC) + { + ff_vvc_update_hmvp(lc, mi); + } else if (!pu->merge_gpm_flag && !pu->inter_affine_flag && !pu->merge_subblock_flag) { + refine_regular_subblock(lc); + ff_vvc_update_hmvp(lc, mi); + } + + if (!pu->dmvr_flag) + fill_dmvr_info(lc); + return ret; +} + +static int hls_coding_unit(VVCLocalContext *lc, int x0, int y0, int cb_width, int cb_height, + int cqt_depth, const VVCTreeType tree_type, VVCModeType mode_type) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int hs = sps->hshift[CHROMA]; + const int vs = sps->vshift[CHROMA]; + const int is_128 = cb_width > 64 || cb_height > 64; + int pred_mode_plt_flag = 0; + int ret; + + CodingUnit *cu = add_cu(lc, x0, y0, cb_width, cb_height, cqt_depth, tree_type); + + if (!cu) + return AVERROR(ENOMEM); + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + + if (IS_I(rsh) && is_128) + mode_type = MODE_TYPE_INTRA; + cu->pred_mode = pred_mode_decode(lc, tree_type, mode_type); + + if (cu->pred_mode == MODE_INTRA && sps->r->sps_palette_enabled_flag && !is_128 && !cu->skip_flag && + mode_type != MODE_TYPE_INTER && ((cb_width * cb_height) > + (tree_type != DUAL_TREE_CHROMA ? 16 : (16 << hs << vs))) && + (mode_type != MODE_TYPE_INTRA || tree_type != DUAL_TREE_CHROMA)) { + pred_mode_plt_flag = ff_vvc_pred_mode_plt_flag(lc); + if (pred_mode_plt_flag) { + avpriv_report_missing_feature(fc->log_ctx, "Palette"); + return AVERROR_PATCHWELCOME; + } + } + if (cu->pred_mode == MODE_INTRA && sps->r->sps_act_enabled_flag && tree_type == SINGLE_TREE) { + avpriv_report_missing_feature(fc->log_ctx, "Adaptive Color Transform"); + return AVERROR_PATCHWELCOME; + } + if (cu->pred_mode == MODE_INTRA || cu->pred_mode == MODE_PLT) { + if (tree_type == SINGLE_TREE || tree_type == DUAL_TREE_LUMA) { + if (pred_mode_plt_flag) { + avpriv_report_missing_feature(fc->log_ctx, "Palette"); + return AVERROR_PATCHWELCOME; + } else { + intra_luma_pred_modes(lc); + } + ff_vvc_set_intra_mvf(lc, 0); + } + if ((tree_type == SINGLE_TREE || tree_type == DUAL_TREE_CHROMA) && sps->r->sps_chroma_format_idc) { + if (pred_mode_plt_flag && tree_type == DUAL_TREE_CHROMA) { + avpriv_report_missing_feature(fc->log_ctx, "Palette"); + return AVERROR_PATCHWELCOME; + } else if (!pred_mode_plt_flag) { + if (!cu->act_enabled_flag) + intra_chroma_pred_modes(lc); + } + } + } else if (tree_type != DUAL_TREE_CHROMA) { /* MODE_INTER or MODE_IBC */ + if ((ret = inter_data(lc)) < 0) + return ret; + } + if (cu->pred_mode != MODE_INTRA && !pred_mode_plt_flag && !lc->cu->pu.general_merge_flag) + cu->coded_flag = ff_vvc_cu_coded_flag(lc); + else + cu->coded_flag = !(cu->skip_flag || pred_mode_plt_flag); + + if (cu->coded_flag) { + sbt_info(lc, sps); + if (sps->r->sps_act_enabled_flag && cu->pred_mode != MODE_INTRA && tree_type == SINGLE_TREE) { + avpriv_report_missing_feature(fc->log_ctx, "Adaptive Color Transform"); + return AVERROR_PATCHWELCOME; + } + lc->parse.lfnst_dc_only = 1; + lc->parse.lfnst_zero_out_sig_coeff_flag = 1; + lc->parse.mts_dc_only = 1; + lc->parse.mts_zero_out_sig_coeff_flag = 1; + ret = hls_transform_tree(lc, x0, y0, cb_width, cb_height, cu->ch_type); + if (ret < 0) + return ret; + cu->lfnst_idx = lfnst_idx_decode(lc); + cu->mts_idx = mts_idx_decode(lc); + set_qp_c(lc); + } else { + ret = skipped_transform_tree_unit(lc); + if (ret < 0) + return ret; + } + set_cu_tabs(lc, cu); + + return 0; +} + +static int derive_mode_type_condition(const VVCLocalContext *lc, + const VVCSplitMode split, const int cb_width, const int cb_height, const VVCModeType mode_type_curr) +{ + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const VVCSPS *sps = lc->fc->ps.sps; + const int area = cb_width * cb_height; + + if ((IS_I(rsh) && sps->r->sps_qtbtt_dual_tree_intra_flag) || + mode_type_curr != MODE_TYPE_ALL || !sps->r->sps_chroma_format_idc || + sps->r->sps_chroma_format_idc == CHROMA_FORMAT_444) + return 0; + if ((area == 64 && (split == SPLIT_QT || split == SPLIT_TT_HOR || split == SPLIT_TT_VER)) || + (area == 32 && (split == SPLIT_BT_HOR || split == SPLIT_BT_VER))) + return 1; + if ((area == 64 && (split == SPLIT_BT_HOR || split == SPLIT_BT_VER) && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_420) || + (area == 128 && (split == SPLIT_TT_HOR || split == SPLIT_TT_VER) && sps->r->sps_chroma_format_idc == CHROMA_FORMAT_420) || + (cb_width == 8 && split == SPLIT_BT_VER) || (cb_width == 16 && split == SPLIT_TT_VER)) + return 1 + !IS_I(rsh); + + return 0; +} + +static VVCModeType mode_type_decode(VVCLocalContext *lc, const int x0, const int y0, + const int cb_width, const int cb_height, const VVCSplitMode split, const int ch_type, + const VVCModeType mode_type_curr) +{ + VVCModeType mode_type; + const int mode_type_condition = derive_mode_type_condition(lc, split, cb_width, cb_height, mode_type_curr); + + if (mode_type_condition == 1) + mode_type = MODE_TYPE_INTRA; + else if (mode_type_condition == 2) { + mode_type = ff_vvc_non_inter_flag(lc, x0, y0, ch_type) ? MODE_TYPE_INTRA : MODE_TYPE_INTER; + } else { + mode_type = mode_type_curr; + } + + return mode_type; +} + +static int hls_coding_tree(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, int part_idx, + VVCSplitMode last_split_mode, VVCTreeType tree_type_curr, VVCModeType mode_type_curr); + +static int coding_tree_btv(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(x, idx) do { \ + ret = hls_coding_tree(lc, x, y0, cb_width / 2, cb_height, \ + qg_on_y, qg_on_c, cb_sub_div + 1, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_BT_VER, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ +} while (0); + + const VVCPPS *pps = lc->fc->ps.pps; + const int x1 = x0 + cb_width / 2; + int ret = 0; + + depth_offset += (x0 + cb_width > pps->width) ? 1 : 0; + CODING_TREE(x0, 0); + if (x1 < pps->width) + CODING_TREE(x1, 1); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_bth(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(y, idx) do { \ + ret = hls_coding_tree(lc, x0, y, cb_width , cb_height / 2, \ + qg_on_y, qg_on_c, cb_sub_div + 1, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_BT_HOR, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCPPS *pps = lc->fc->ps.pps; + const int y1 = y0 + (cb_height / 2); + int ret = 0; + + depth_offset += (y0 + cb_height > pps->height) ? 1 : 0; + CODING_TREE(y0, 0); + if (y1 < pps->height) + CODING_TREE(y1, 1); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_ttv(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(x, w, sub_div, idx) do { \ + ret = hls_coding_tree(lc, x, y0, w, cb_height, \ + qg_on_y, qg_on_c, sub_div, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_TT_VER, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCSH *sh = &lc->sc->sh; + const int x1 = x0 + cb_width / 4; + const int x2 = x0 + cb_width * 3 / 4; + int ret; + + qg_on_y = qg_on_y && (cb_sub_div + 2 <= sh->cu_qp_delta_subdiv); + qg_on_c = qg_on_c && (cb_sub_div + 2 <= sh->cu_chroma_qp_offset_subdiv); + + CODING_TREE(x0, cb_width / 4, cb_sub_div + 2, 0); + CODING_TREE(x1, cb_width / 2, cb_sub_div + 1, 1); + CODING_TREE(x2, cb_width / 4, cb_sub_div + 2, 2); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_tth(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(y, h, sub_div, idx) do { \ + ret = hls_coding_tree(lc, x0, y, cb_width, h, \ + qg_on_y, qg_on_c, sub_div, cqt_depth, mtt_depth + 1, \ + depth_offset, idx, SPLIT_TT_HOR, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCSH *sh = &lc->sc->sh; + const int y1 = y0 + (cb_height / 4); + const int y2 = y0 + (3 * cb_height / 4); + int ret; + + qg_on_y = qg_on_y && (cb_sub_div + 2 <= sh->cu_qp_delta_subdiv); + qg_on_c = qg_on_c && (cb_sub_div + 2 <= sh->cu_chroma_qp_offset_subdiv); + + CODING_TREE(y0, cb_height / 4, cb_sub_div + 2, 0); + CODING_TREE(y1, cb_height / 2, cb_sub_div + 1, 1); + CODING_TREE(y2, cb_height / 4, cb_sub_div + 2, 2); + + return 0; + +#undef CODING_TREE +} + +static int coding_tree_qt(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type) +{ +#define CODING_TREE(x, y, idx) do { \ + ret = hls_coding_tree(lc, x, y, cb_width / 2, cb_height / 2, \ + qg_on_y, qg_on_c, cb_sub_div + 2, cqt_depth + 1, 0, 0, \ + idx, SPLIT_QT, tree_type, mode_type); \ + if (ret < 0) \ + return ret; \ + } while (0); + + const VVCPPS *pps = lc->fc->ps.pps; + const int x1 = x0 + cb_width / 2; + const int y1 = y0 + cb_height / 2; + int ret = 0; + + CODING_TREE(x0, y0, 0); + if (x1 < pps->width) + CODING_TREE(x1, y0, 1); + if (y1 < pps->height) + CODING_TREE(x0, y1, 2); + if (x1 < pps->width && + y1 < pps->height) + CODING_TREE(x1, y1, 3); + + return 0; + +#undef CODING_TREE +} + +typedef int (*coding_tree_fn)(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, + VVCTreeType tree_type, VVCModeType mode_type); + +const static coding_tree_fn coding_tree[] = { + coding_tree_tth, + coding_tree_bth, + coding_tree_ttv, + coding_tree_btv, + coding_tree_qt, +}; + +static int hls_coding_tree(VVCLocalContext *lc, + int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c, + int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, int part_idx, + VVCSplitMode last_split_mode, VVCTreeType tree_type_curr, VVCModeType mode_type_curr) +{ + VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + const int ch_type = tree_type_curr == DUAL_TREE_CHROMA; + int ret; + VVCAllowedSplit allowed; + + if (pps->r->pps_cu_qp_delta_enabled_flag && qg_on_y && cb_sub_div <= sh->cu_qp_delta_subdiv) { + lc->parse.is_cu_qp_delta_coded = 0; + lc->parse.cu_qg_top_left_x = x0; + lc->parse.cu_qg_top_left_y = y0; + } + if (rsh->sh_cu_chroma_qp_offset_enabled_flag && qg_on_c && + cb_sub_div <= sh->cu_chroma_qp_offset_subdiv) { + lc->parse.is_cu_chroma_qp_offset_coded = 0; + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + } + + can_split(lc, x0, y0, cb_width, cb_height, mtt_depth, depth_offset, part_idx, + last_split_mode, tree_type_curr, mode_type_curr, &allowed); + if (ff_vvc_split_cu_flag(lc, x0, y0, cb_width, cb_height, ch_type, &allowed)) { + VVCSplitMode split = ff_vvc_split_mode(lc, x0, y0, cb_width, cb_height, cqt_depth, mtt_depth, ch_type, &allowed); + VVCModeType mode_type = mode_type_decode(lc, x0, y0, cb_width, cb_height, split, ch_type, mode_type_curr); + + VVCTreeType tree_type = (mode_type == MODE_TYPE_INTRA) ? DUAL_TREE_LUMA : tree_type_curr; + + if (split != SPLIT_QT) { + if (!(x0 & 31) && !(y0 & 31) && mtt_depth <= 1) + TAB_MSM(fc, mtt_depth, x0, y0) = split; + } + ret = coding_tree[split - 1](lc, x0, y0, cb_width, cb_height, qg_on_y, qg_on_c, + cb_sub_div, cqt_depth, mtt_depth, depth_offset, tree_type, mode_type); + if (ret < 0) + return ret; + if (mode_type_curr == MODE_TYPE_ALL && mode_type == MODE_TYPE_INTRA) { + ret = hls_coding_tree(lc, x0, y0, cb_width, cb_height, 0, qg_on_c, cb_sub_div, + cqt_depth, mtt_depth, 0, 0, split, DUAL_TREE_CHROMA, mode_type); + if (ret < 0) + return ret; + } + } else { + ret = hls_coding_unit(lc, x0, y0, cb_width, cb_height, cqt_depth, tree_type_curr, mode_type_curr); + if (ret < 0) + return ret; + } + + return 0; +} + +static int dual_tree_implicit_qt_split(VVCLocalContext *lc, + const int x0, const int y0, const int cb_size, const int cqt_depth) +{ + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + const VVCPPS *pps = lc->fc->ps.pps; + const int cb_subdiv = 2 * cqt_depth; + int ret; + + if (cb_size > 64) { + #define DUAL_TREE(x, y) do { \ + ret = dual_tree_implicit_qt_split(lc, x, y, cb_size / 2, cqt_depth + 1); \ + if (ret < 0) \ + return ret; \ + } while (0) + + const int x1 = x0 + (cb_size / 2); + const int y1 = y0 + (cb_size / 2); + if (pps->r->pps_cu_qp_delta_enabled_flag && cb_subdiv <= sh->cu_qp_delta_subdiv) { + lc->parse.is_cu_qp_delta_coded = 0; + lc->parse.cu_qg_top_left_x = x0; + lc->parse.cu_qg_top_left_y = y0; + } + if (rsh->sh_cu_chroma_qp_offset_enabled_flag && cb_subdiv <= sh->cu_chroma_qp_offset_subdiv) { + lc->parse.is_cu_chroma_qp_offset_coded = 0; + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + } + DUAL_TREE(x0, y0); + if (x1 < pps->width) + DUAL_TREE(x1, y0); + if (y1 < pps->height) + DUAL_TREE(x0, y1); + if (x1 < pps->width && y1 < pps->height) + DUAL_TREE(x1, y1); + #undef DUAL_TREE + } else { + #define CODING_TREE(tree_type) do { \ + const int qg_on_y = tree_type == DUAL_TREE_LUMA; \ + ret = hls_coding_tree(lc, x0, y0, cb_size, cb_size, qg_on_y, !qg_on_y, \ + cb_subdiv, cqt_depth, 0, 0, 0, SPLIT_NONE, tree_type, MODE_TYPE_ALL); \ + if (ret < 0) \ + return ret; \ + } while (0) + CODING_TREE(DUAL_TREE_LUMA); + CODING_TREE(DUAL_TREE_CHROMA); + #undef CODING_TREE + } + return 0; +} + +#define SET_SAO(elem, value) \ +do { \ + if (!sao_merge_up_flag && !sao_merge_left_flag) \ + sao->elem = value; \ + else if (sao_merge_left_flag) \ + sao->elem = CTB(fc->tab.sao, rx-1, ry).elem; \ + else if (sao_merge_up_flag) \ + sao->elem = CTB(fc->tab.sao, rx, ry-1).elem; \ + else \ + sao->elem = 0; \ +} while (0) + +static void hls_sao(VVCLocalContext *lc, const int rx, const int ry) +{ + VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + int sao_merge_left_flag = 0; + int sao_merge_up_flag = 0; + SAOParams *sao = &CTB(fc->tab.sao, rx, ry); + int c_idx, i; + + if (rsh->sh_sao_luma_used_flag || rsh->sh_sao_chroma_used_flag) { + if (rx > 0) { + if (lc->ctb_left_flag) + sao_merge_left_flag = ff_vvc_sao_merge_flag_decode(lc); + } + if (ry > 0 && !sao_merge_left_flag) { + if (lc->ctb_up_flag) + sao_merge_up_flag = ff_vvc_sao_merge_flag_decode(lc); + } + } + + for (c_idx = 0; c_idx < (fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1); c_idx++) { + const int sao_used_flag = !c_idx ? rsh->sh_sao_luma_used_flag : rsh->sh_sao_chroma_used_flag; + if (!sao_used_flag) { + sao->type_idx[c_idx] = SAO_NOT_APPLIED; + continue; + } + + if (c_idx == 2) { + sao->type_idx[2] = sao->type_idx[1]; + sao->eo_class[2] = sao->eo_class[1]; + } else { + SET_SAO(type_idx[c_idx], ff_vvc_sao_type_idx_decode(lc)); + } + + if (sao->type_idx[c_idx] == SAO_NOT_APPLIED) + continue; + + for (i = 0; i < 4; i++) + SET_SAO(offset_abs[c_idx][i], ff_vvc_sao_offset_abs_decode(lc)); + + if (sao->type_idx[c_idx] == SAO_BAND) { + for (i = 0; i < 4; i++) { + if (sao->offset_abs[c_idx][i]) { + SET_SAO(offset_sign[c_idx][i], + ff_vvc_sao_offset_sign_decode(lc)); + } else { + sao->offset_sign[c_idx][i] = 0; + } + } + SET_SAO(band_position[c_idx], ff_vvc_sao_band_position_decode(lc)); + } else if (c_idx != 2) { + SET_SAO(eo_class[c_idx], ff_vvc_sao_eo_class_decode(lc)); + } + + // Inferred parameters + sao->offset_val[c_idx][0] = 0; + for (i = 0; i < 4; i++) { + sao->offset_val[c_idx][i + 1] = sao->offset_abs[c_idx][i]; + if (sao->type_idx[c_idx] == SAO_EDGE) { + if (i > 1) + sao->offset_val[c_idx][i + 1] = -sao->offset_val[c_idx][i + 1]; + } else if (sao->offset_sign[c_idx][i]) { + sao->offset_val[c_idx][i + 1] = -sao->offset_val[c_idx][i + 1]; + } + sao->offset_val[c_idx][i + 1] *= 1 << (fc->ps.sps->bit_depth - FFMIN(10, fc->ps.sps->bit_depth)); + } + } +} + +static void alf_params(VVCLocalContext *lc, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *sh = lc->sc->sh.r; + ALFParams *alf = &CTB(fc->tab.alf, rx, ry); + + alf->ctb_flag[LUMA] = alf->ctb_flag[CB] = alf->ctb_flag[CR] = 0; + if (sh->sh_alf_enabled_flag) { + alf->ctb_flag[LUMA] = ff_vvc_alf_ctb_flag(lc, rx, ry, LUMA); + if (alf->ctb_flag[LUMA]) { + uint8_t alf_use_aps_flag = 0; + if (sh->sh_num_alf_aps_ids_luma > 0) + alf_use_aps_flag = ff_vvc_alf_use_aps_flag(lc); + if (alf_use_aps_flag) { + alf->ctb_filt_set_idx_y = 16; + if (sh->sh_num_alf_aps_ids_luma > 1) + alf->ctb_filt_set_idx_y += ff_vvc_alf_luma_prev_filter_idx(lc); + } else { + alf->ctb_filt_set_idx_y = ff_vvc_alf_luma_fixed_filter_idx(lc); + } + } + for (int c_idx = CB; c_idx <= CR; c_idx++) { + const uint8_t alf_enabled_flag = + c_idx == CB ? sh->sh_alf_cb_enabled_flag : sh->sh_alf_cr_enabled_flag; + if (alf_enabled_flag) { + const VVCALF *aps = fc->ps.alf_list[sh->sh_alf_aps_id_chroma]; + alf->ctb_flag[c_idx] = ff_vvc_alf_ctb_flag(lc, rx, ry, c_idx); + alf->alf_ctb_filter_alt_idx[c_idx - 1] = 0; + if (alf->ctb_flag[c_idx] && aps->num_chroma_filters > 1) + alf->alf_ctb_filter_alt_idx[c_idx - 1] = ff_vvc_alf_ctb_filter_alt_idx(lc, c_idx, aps->num_chroma_filters); + } + } + } + if (fc->ps.sps->r->sps_ccalf_enabled_flag) { + const uint8_t cc_enabled[] = { sh->sh_alf_cc_cb_enabled_flag, sh->sh_alf_cc_cr_enabled_flag }; + const uint8_t cc_aps_id[] = { sh->sh_alf_cc_cb_aps_id, sh->sh_alf_cc_cr_aps_id }; + for (int i = 0; i < 2; i++) { + alf->ctb_cc_idc[i] = 0; + if (cc_enabled[i]) { + const VVCALF *aps = fc->ps.alf_list[cc_aps_id[i]]; + alf->ctb_cc_idc[i] = ff_vvc_alf_ctb_cc_idc(lc, rx, ry, i, aps->num_cc_filters[i]); + } + } + } +} + +static void deblock_params(VVCLocalContext *lc, const int rx, const int ry) +{ + VVCFrameContext *fc = lc->fc; + const VVCSH *sh = &lc->sc->sh; + CTB(fc->tab.deblock, rx, ry) = sh->deblock; +} + +static int hls_coding_tree_unit(VVCLocalContext *lc, + const int x0, const int y0, const int ctu_idx, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const H266RawSliceHeader *rsh = sh->r; + const unsigned int ctb_size = sps->ctb_size_y; + int ret = 0; + + memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset)); + + hls_sao(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y); + alf_params(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y); + deblock_params(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y); + + if (IS_I(rsh) && sps->r->sps_qtbtt_dual_tree_intra_flag) + ret = dual_tree_implicit_qt_split(lc, x0, y0, ctb_size, 0); + else + ret = hls_coding_tree(lc, x0, y0, ctb_size, ctb_size, + 1, 1, 0, 0, 0, 0, 0, SPLIT_NONE, SINGLE_TREE, MODE_TYPE_ALL); + if (ret < 0) + return ret; + + if (rx == pps->ctb_to_col_bd[rx + 1] - 1) { + if (ctu_idx == sh->num_ctus_in_curr_slice - 1) { + const int end_of_slice_one_bit = ff_vvc_end_of_slice_flag_decode(lc); + if (!end_of_slice_one_bit) + return AVERROR_INVALIDDATA; + } else { + if (ry == pps->ctb_to_row_bd[ry + 1] - 1) { + const int end_of_tile_one_bit = ff_vvc_end_of_tile_one_bit(lc); + if (!end_of_tile_one_bit) + return AVERROR_INVALIDDATA; + } else { + if (fc->ps.sps->r->sps_entropy_coding_sync_enabled_flag) { + const int end_of_subset_one_bit = ff_vvc_end_of_subset_one_bit(lc); + if (!end_of_subset_one_bit) + return AVERROR_INVALIDDATA; + } + } + } + } + + return 0; +} + +static int has_inter_luma(const CodingUnit *cu) +{ + return cu->pred_mode != MODE_INTRA && cu->pred_mode != MODE_PLT && cu->tree_type != DUAL_TREE_CHROMA; +} + +static int pred_get_y(const int y0, const Mv *mv, const int height) +{ + return FFMAX(0, y0 + (mv->y >> 4) + height); +} + +static void cu_get_max_y(const CodingUnit *cu, int max_y[2][VVC_MAX_REF_ENTRIES], const VVCFrameContext *fc) +{ + const PredictionUnit *pu = &cu->pu; + + if (pu->merge_gpm_flag) { + for (int i = 0; i < FF_ARRAY_ELEMS(pu->gpm_mv); i++) { + const MvField *mvf = pu->gpm_mv + i; + const int lx = mvf->pred_flag - PF_L0; + const int idx = mvf->ref_idx[lx]; + const int y = pred_get_y(cu->y0, mvf->mv + lx, cu->cb_height); + + max_y[lx][idx] = FFMAX(max_y[lx][idx], y); + } + } else { + const MotionInfo *mi = &pu->mi; + const int max_dmvr_off = (!pu->inter_affine_flag && pu->dmvr_flag) ? 2 : 0; + const int sbw = cu->cb_width / mi->num_sb_x; + const int sbh = cu->cb_height / mi->num_sb_y; + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + const int x0 = cu->x0 + sbx * sbw; + const int y0 = cu->y0 + sby * sbh; + const MvField *mvf = ff_vvc_get_mvf(fc, x0, y0); + for (int lx = 0; lx < 2; lx++) { + const PredFlag mask = 1 << lx; + if (mvf->pred_flag & mask) { + const int idx = mvf->ref_idx[lx]; + const int y = pred_get_y(y0, mvf->mv + lx, sbh); + + max_y[lx][idx] = FFMAX(max_y[lx][idx], y + max_dmvr_off); + } + } + } + } + } +} + +static void ctu_get_pred(VVCLocalContext *lc, const int rs) +{ + const VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + CTU *ctu = fc->tab.ctus + rs; + const CodingUnit *cu = ctu->cus; + + if (IS_I(rsh)) + return; + + for (int lx = 0; lx < 2; lx++) + memset(ctu->max_y[lx], -1, sizeof(ctu->max_y[0][0]) * rsh->num_ref_idx_active[lx]); + + while (cu) { + if (has_inter_luma(cu)) { + cu_get_max_y(cu, ctu->max_y, fc); + ctu->has_dmvr |= cu->pu.dmvr_flag; + } + cu = cu->next; + } + ctu->max_y_idx[0] = ctu->max_y_idx[1] = 0; +} + +int ff_vvc_coding_tree_unit(VVCLocalContext *lc, + const int ctu_idx, const int rs, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int x_ctb = rx << sps->ctb_log2_size_y; + const int y_ctb = ry << sps->ctb_log2_size_y; + const int ctb_size = 1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y; + EntryPoint* ep = lc->ep; + int ret; + + if (rx == pps->ctb_to_col_bd[rx]) { + ep->num_hmvp = 0; + ep->num_hmvp_ibc = 0; + ep->is_first_qg = ry == pps->ctb_to_row_bd[ry] || !ctu_idx; + } + + lc->coeffs = fc->tab.coeffs + rs * ctb_size * VVC_MAX_SAMPLE_ARRAYS; + lc->cu = NULL; + + ff_vvc_cabac_init(lc, ctu_idx, rx, ry); + ff_vvc_decode_neighbour(lc, x_ctb, y_ctb, rx, ry, rs); + ret = hls_coding_tree_unit(lc, x_ctb, y_ctb, ctu_idx, rx, ry); + if (ret < 0) + return ret; + ctu_get_pred(lc, rs); + + return 0; +} + +void ff_vvc_decode_neighbour(VVCLocalContext *lc, const int x_ctb, const int y_ctb, + const int rx, const int ry, const int rs) +{ + VVCFrameContext *fc = lc->fc; + const int ctb_size = fc->ps.sps->ctb_size_y; + + lc->end_of_tiles_x = fc->ps.pps->width; + lc->end_of_tiles_y = fc->ps.pps->height; + if (fc->ps.pps->ctb_to_col_bd[rx] != fc->ps.pps->ctb_to_col_bd[rx + 1]) + lc->end_of_tiles_x = FFMIN(x_ctb + ctb_size, lc->end_of_tiles_x); + if (fc->ps.pps->ctb_to_row_bd[ry] != fc->ps.pps->ctb_to_row_bd[ry + 1]) + lc->end_of_tiles_y = FFMIN(y_ctb + ctb_size, lc->end_of_tiles_y); + + lc->boundary_flags = 0; + if (rx > 0 && fc->ps.pps->ctb_to_col_bd[rx] != fc->ps.pps->ctb_to_col_bd[rx - 1]) + lc->boundary_flags |= BOUNDARY_LEFT_TILE; + if (rx > 0 && fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - 1]) + lc->boundary_flags |= BOUNDARY_LEFT_SLICE; + if (ry > 0 && fc->ps.pps->ctb_to_row_bd[ry] != fc->ps.pps->ctb_to_row_bd[ry - 1]) + lc->boundary_flags |= BOUNDARY_UPPER_TILE; + if (ry > 0 && fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - fc->ps.pps->ctb_width]) + lc->boundary_flags |= BOUNDARY_UPPER_SLICE; + lc->ctb_left_flag = rx > 0 && !(lc->boundary_flags & BOUNDARY_LEFT_TILE); + lc->ctb_up_flag = ry > 0 && !(lc->boundary_flags & BOUNDARY_UPPER_TILE) && !(lc->boundary_flags & BOUNDARY_UPPER_SLICE); + lc->ctb_up_right_flag = lc->ctb_up_flag && (fc->ps.pps->ctb_to_col_bd[rx] == fc->ps.pps->ctb_to_col_bd[rx + 1]) && + (fc->ps.pps->ctb_to_row_bd[ry] == fc->ps.pps->ctb_to_row_bd[ry - 1]); + lc->ctb_up_left_flag = lc->ctb_left_flag && lc->ctb_up_flag; +} + +void ff_vvc_set_neighbour_available(VVCLocalContext *lc, + const int x0, const int y0, const int w, const int h) +{ + const int log2_ctb_size = lc->fc->ps.sps->ctb_log2_size_y; + const int x0b = av_mod_uintp2(x0, log2_ctb_size); + const int y0b = av_mod_uintp2(y0, log2_ctb_size); + + lc->na.cand_up = (lc->ctb_up_flag || y0b); + lc->na.cand_left = (lc->ctb_left_flag || x0b); + lc->na.cand_up_left = (x0b || y0b) ? lc->na.cand_left && lc->na.cand_up : lc->ctb_up_left_flag; + lc->na.cand_up_right_sap = + (x0b + w == 1 << log2_ctb_size) ? lc->ctb_up_right_flag && !y0b : lc->na.cand_up; + lc->na.cand_up_right = lc->na.cand_up_right_sap && (x0 + w) < lc->end_of_tiles_x; +} + +void ff_vvc_ctu_free_cus(CTU *ctu) +{ + CodingUnit **cus = &ctu->cus; + while (*cus) { + CodingUnit *cu = *cus; + TransformUnit **head = &cu->tus.head; + + *cus = cu->next; + + while (*head) { + TransformUnit *tu = *head; + *head = tu->next; + ff_refstruct_unref(&tu); + } + cu->tus.tail = NULL; + + ff_refstruct_unref(&cu); + } +} + +int ff_vvc_get_qPy(const VVCFrameContext *fc, const int xc, const int yc) +{ + const int min_cb_log2_size_y = fc->ps.sps->min_cb_log2_size_y; + const int x = xc >> min_cb_log2_size_y; + const int y = yc >> min_cb_log2_size_y; + return fc->tab.qp[LUMA][x + y * fc->ps.pps->min_cb_width]; +} + +void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, + const int bit_depth, const int persistent_rice_adaptation_enabled_flag) +{ + for (size_t i = 0; i < FF_ARRAY_ELEMS(ep->stat_coeff); ++i) { + ep->stat_coeff[i] = + persistent_rice_adaptation_enabled_flag ? 2 * (av_log2(bit_depth - 10)) : 0; + } +} diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h new file mode 100644 index 00000000000..8020e184c5d --- /dev/null +++ b/libavcodec/vvc/vvc_ctu.h @@ -0,0 +1,484 @@ +/* + * VVC CTU(Coding Tree Unit) parser + * + * Copyright (C) 2022 Nuo Mi + * + * 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 + */ + +#ifndef AVCODEC_VVC_VVC_CTU_H +#define AVCODEC_VVC_VVC_CTU_H + +#include "libavcodec/cabac.h" +#include "libavutil/mem_internal.h" + +#include "vvcdec.h" + +#define MAX_CTU_SIZE 128 + +#define MAX_CU_SIZE MAX_CTU_SIZE +#define MIN_CU_SIZE 4 +#define MIN_CU_LOG2 2 +#define MAX_CU_DEPTH 7 + +#define MAX_PARTS_IN_CTU ((MAX_CTU_SIZE >> MIN_CU_LOG2) * (MAX_CTU_SIZE >> MIN_CU_LOG2)) + +#define MIN_PU_SIZE 4 + +#define MAX_TB_SIZE 64 +#define MIN_TU_SIZE 4 +#define MAX_TUS_IN_CU 64 + +#define MAX_QP 63 + +#define MAX_PB_SIZE 128 +#define EDGE_EMU_BUFFER_STRIDE (MAX_PB_SIZE + 32) + +#define CHROMA_EXTRA_BEFORE 1 +#define CHROMA_EXTRA_AFTER 2 +#define CHROMA_EXTRA 3 +#define LUMA_EXTRA_BEFORE 3 +#define LUMA_EXTRA_AFTER 4 +#define LUMA_EXTRA 7 +#define BILINEAR_EXTRA_BEFORE 0 +#define BILINEAR_EXTRA_AFTER 1 +#define BILINEAR_EXTRA 1 + +#define MAX_CONTROL_POINTS 3 + +#define AFFINE_MIN_BLOCK_SIZE 4 + +#define MRG_MAX_NUM_CANDS 6 +#define MAX_NUM_HMVP_CANDS 5 + +#define SAO_PADDING_SIZE 1 + +#define ALF_PADDING_SIZE 8 +#define ALF_BLOCK_SIZE 4 + +#define ALF_BORDER_LUMA 3 +#define ALF_BORDER_CHROMA 2 + +#define ALF_VB_POS_ABOVE_LUMA 4 +#define ALF_VB_POS_ABOVE_CHROMA 2 + +#define ALF_GRADIENT_STEP 2 +#define ALF_GRADIENT_BORDER 2 +#define ALF_GRADIENT_SIZE ((MAX_CU_SIZE + ALF_GRADIENT_BORDER * 2) / ALF_GRADIENT_STEP) +#define ALF_NUM_DIR 4 + + +/** + * Value of the luma sample at position (x, y) in the 2D array tab. + */ +#define SAMPLE(tab, x, y) ((tab)[(y) * s->pps->width + (x)]) +#define SAMPLE_CTB(tab, x, y) ((tab)[(y) * min_cb_width + (x)]) +#define CTB(tab, x, y) ((tab)[(y) * fc->ps.pps->ctb_width + (x)]) + +enum SAOType { + SAO_NOT_APPLIED = 0, + SAO_BAND, + SAO_EDGE, +}; + +enum SAOEOClass { + SAO_EO_HORIZ = 0, + SAO_EO_VERT, + SAO_EO_135D, + SAO_EO_45D, +}; + +typedef struct NeighbourAvailable { + int cand_left; + int cand_up; + int cand_up_left; + int cand_up_right; + int cand_up_right_sap; +} NeighbourAvailable; + +enum IspType{ + ISP_NO_SPLIT, + ISP_HOR_SPLIT, + ISP_VER_SPLIT, +}; + +typedef enum VVCSplitMode { + SPLIT_NONE, + SPLIT_TT_HOR, + SPLIT_BT_HOR, + SPLIT_TT_VER, + SPLIT_BT_VER, + SPLIT_QT, +} VVCSplitMode; + +typedef enum MtsIdx { + MTS_DCT2_DCT2, + MTS_DST7_DST7, + MTS_DST7_DCT8, + MTS_DCT8_DST7, + MTS_DCT8_DCT8, +} MtsIdx; + +typedef struct TransformBlock { + uint8_t has_coeffs; + uint8_t c_idx; + uint8_t ts; ///< transform_skip_flag + int x0; + int y0; + + int tb_width; + int tb_height; + int log2_tb_width; + int log2_tb_height; + + int max_scan_x; + int max_scan_y; + int min_scan_x; + int min_scan_y; + + int qp; + int rect_non_ts_flag; + int bd_shift; + int bd_offset; + + int *coeffs; +} TransformBlock; + +typedef enum VVCTreeType { + SINGLE_TREE, + DUAL_TREE_LUMA, + DUAL_TREE_CHROMA, +} VVCTreeType; + +typedef struct TransformUnit { + int x0; + int y0; + int width; + int height; + + uint8_t joint_cbcr_residual_flag; ///< tu_joint_cbcr_residual_flag + + uint8_t coded_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< tu_y_coded_flag, tu_cb_coded_flag, tu_cr_coded_flag + uint8_t nb_tbs; + TransformBlock tbs[VVC_MAX_SAMPLE_ARRAYS]; + + struct TransformUnit *next; ///< RefStruct reference +} TransformUnit; + +typedef enum PredMode { + MODE_INTER, + MODE_INTRA, + MODE_SKIP, + MODE_PLT, + MODE_IBC, +} PredMode; + +typedef struct Mv { + int x; ///< horizontal component of motion vector + int y; ///< vertical component of motion vector +} Mv; + +typedef struct MvField { + DECLARE_ALIGNED(8, Mv, mv)[2]; ///< mvL0, vvL1 + int8_t ref_idx[2]; ///< refIdxL0, refIdxL1 + uint8_t hpel_if_idx; ///< hpelIfIdx + uint8_t bcw_idx; ///< bcwIdx + uint8_t pred_flag; + uint8_t ciip_flag; ///< ciip_flag +} MvField; + +typedef struct DMVRInfo { + DECLARE_ALIGNED(8, Mv, mv)[2]; ///< mvL0, vvL1 + uint8_t dmvr_enabled; +} DMVRInfo; + +typedef enum MotionModelIdc { + MOTION_TRANSLATION, + MOTION_4_PARAMS_AFFINE, + MOTION_6_PARAMS_AFFINE, +} MotionModelIdc; + +typedef enum PredFlag { + PF_INTRA = 0x0, + PF_L0 = 0x1, + PF_L1 = 0x2, + PF_BI = 0x3, + PF_IBC = PF_L0 | 0x4, +} PredFlag; + +typedef enum IntraPredMode { + INTRA_INVALID = -1, + INTRA_PLANAR = 0, + INTRA_DC, + INTRA_HORZ = 18, + INTRA_DIAG = 34, + INTRA_VERT = 50, + INTRA_VDIAG = 66, + INTRA_LT_CCLM = 81, + INTRA_L_CCLM, + INTRA_T_CCLM +} IntraPredMode; + +typedef struct MotionInfo { + MotionModelIdc motion_model_idc; ///< MotionModelIdc + int8_t ref_idx[2]; ///< refIdxL0, refIdxL1 + uint8_t hpel_if_idx; ///< hpelIfIdx + uint8_t bcw_idx; ///< bcwIdx + PredFlag pred_flag; + + Mv mv[2][MAX_CONTROL_POINTS]; + + int num_sb_x, num_sb_y; +} MotionInfo; + +typedef struct PredictionUnit { + uint8_t general_merge_flag; + uint8_t mmvd_merge_flag; + //InterPredIdc inter_pred_idc; + uint8_t inter_affine_flag; + + //subblock predict + uint8_t merge_subblock_flag; + + uint8_t merge_gpm_flag; + uint8_t gpm_partition_idx; + MvField gpm_mv[2]; + + int sym_mvd_flag; + + MotionInfo mi; + + // for regular prediction only + uint8_t dmvr_flag; + uint8_t bdof_flag; + + int16_t diff_mv_x[2][AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; ///< diffMvLX + int16_t diff_mv_y[2][AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; ///< diffMvLX + int cb_prof_flag[2]; +} PredictionUnit; + +typedef struct CodingUnit { + VVCTreeType tree_type; + int x0; + int y0; + int cb_width; + int cb_height; + int ch_type; + int cqt_depth; + + uint8_t coded_flag; + + uint8_t sbt_flag; + uint8_t sbt_horizontal_flag; + uint8_t sbt_pos_flag; + + int lfnst_idx; + MtsIdx mts_idx; + + uint8_t act_enabled_flag; + + uint8_t intra_luma_ref_idx; ///< IntraLumaRefLineIdx[][] + uint8_t intra_mip_flag; ///< intra_mip_flag + uint8_t skip_flag; ///< cu_skip_flag; + + //inter + uint8_t ciip_flag; + + // Inferred parameters + enum IspType isp_split_type; ///< IntraSubPartitionsSplitType + + enum PredMode pred_mode; ///< PredMode + + int num_intra_subpartitions; + + IntraPredMode intra_pred_mode_y; ///< IntraPredModeY + IntraPredMode intra_pred_mode_c; ///< IntraPredModeC + int mip_chroma_direct_flag; ///< MipChromaDirectFlag + + int bdpcm_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< BdpcmFlag + + int apply_lfnst_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< ApplyLfnstFlag[] + + struct { + TransformUnit *head; ///< RefStruct reference + TransformUnit *tail; ///< RefStruct reference + } tus; + + int8_t qp[4]; ///< QpY, Qp′Cb, Qp′Cr, Qp′CbCr + + PredictionUnit pu; + + struct CodingUnit *next; ///< RefStruct reference +} CodingUnit; + +typedef struct CTU { + CodingUnit *cus; + int max_y[2][VVC_MAX_REF_ENTRIES]; + int max_y_idx[2]; + int has_dmvr; +} CTU; + +typedef struct ReconstructedArea { + int x; + int y; + int w; + int h; +} ReconstructedArea; + +typedef struct VVCCabacState { + uint16_t state[2]; + uint8_t shift[2]; +} VVCCabacState; + +// VVC_CONTEXTS matched with SYNTAX_ELEMENT_LAST, it's checked by cabac_init_state. +#define VVC_CONTEXTS 378 +typedef struct EntryPoint { + int8_t qp_y; ///< QpY + + int stat_coeff[VVC_MAX_SAMPLE_ARRAYS]; ///< StatCoeff + + VVCCabacState cabac_state[VVC_CONTEXTS]; + CABACContext cc; + + int ctu_start; + int ctu_end; + + uint8_t is_first_qg; // first quantization group + + MvField hmvp[MAX_NUM_HMVP_CANDS]; ///< HmvpCandList + int num_hmvp; ///< NumHmvpCand + MvField hmvp_ibc[MAX_NUM_HMVP_CANDS]; ///< HmvpIbcCandList + int num_hmvp_ibc; ///< NumHmvpIbcCand +} EntryPoint; + +typedef struct VVCLocalContext { + uint8_t ctb_left_flag; + uint8_t ctb_up_flag; + uint8_t ctb_up_right_flag; + uint8_t ctb_up_left_flag; + int end_of_tiles_x; + int end_of_tiles_y; + + /* +7 is for subpixel interpolation, *2 for high bit depths */ + DECLARE_ALIGNED(32, uint8_t, edge_emu_buffer)[(MAX_PB_SIZE + 7) * EDGE_EMU_BUFFER_STRIDE * 2]; + /* The extended size between the new edge emu buffer is abused by SAO */ + DECLARE_ALIGNED(32, uint8_t, edge_emu_buffer2)[(MAX_PB_SIZE + 7) * EDGE_EMU_BUFFER_STRIDE * 2]; + DECLARE_ALIGNED(32, int16_t, tmp)[MAX_PB_SIZE * MAX_PB_SIZE]; + DECLARE_ALIGNED(32, int16_t, tmp1)[MAX_PB_SIZE * MAX_PB_SIZE]; + DECLARE_ALIGNED(32, int16_t, tmp2)[MAX_PB_SIZE * MAX_PB_SIZE]; + DECLARE_ALIGNED(32, uint8_t, ciip_tmp1)[MAX_PB_SIZE * MAX_PB_SIZE * 2]; + DECLARE_ALIGNED(32, uint8_t, ciip_tmp2)[MAX_PB_SIZE * MAX_PB_SIZE * 2]; + DECLARE_ALIGNED(32, uint8_t, sao_buffer)[(MAX_CTU_SIZE + 2 * SAO_PADDING_SIZE) * EDGE_EMU_BUFFER_STRIDE * 2]; + DECLARE_ALIGNED(32, uint8_t, alf_buffer_luma)[(MAX_CTU_SIZE + 2 * ALF_PADDING_SIZE) * EDGE_EMU_BUFFER_STRIDE * 2]; + DECLARE_ALIGNED(32, uint8_t, alf_buffer_chroma)[(MAX_CTU_SIZE + 2 * ALF_PADDING_SIZE) * EDGE_EMU_BUFFER_STRIDE * 2]; + DECLARE_ALIGNED(32, int32_t, alf_gradient_tmp)[ALF_GRADIENT_SIZE * ALF_GRADIENT_SIZE * ALF_NUM_DIR]; + + struct { + int sbt_num_fourths_tb0; ///< SbtNumFourthsTb0 + + uint8_t is_cu_qp_delta_coded; ///< IsCuQpDeltaCoded + int cu_qg_top_left_x; ///< CuQgTopLeftX + int cu_qg_top_left_y; ///< CuQgTopLeftY + int is_cu_chroma_qp_offset_coded; ///< IsCuChromaQpOffsetCoded + int chroma_qp_offset[3]; ///< CuQpOffsetCb, CuQpOffsetCr, CuQpOffsetCbCr + + int infer_tu_cbf_luma; ///< InferTuCbfLuma + int prev_tu_cbf_y; ///< prevTuCbfY; + + int lfnst_dc_only; ///< LfnstDcOnly + int lfnst_zero_out_sig_coeff_flag; ///< LfnstZeroOutSigCoeffFlag + + int mts_dc_only; ///< MtsDcOnly + int mts_zero_out_sig_coeff_flag; ///< MtsZeroOutSigCoeffFlag; + } parse; + + struct { + // lmcs cache, for recon only + int chroma_scale; + int x_vpdu; + int y_vpdu; + } lmcs; + + CodingUnit *cu; + ReconstructedArea ras[2][MAX_PARTS_IN_CTU]; + int num_ras[2]; + + NeighbourAvailable na; + +#define BOUNDARY_LEFT_SLICE (1 << 0) +#define BOUNDARY_LEFT_TILE (1 << 1) +#define BOUNDARY_UPPER_SLICE (1 << 2) +#define BOUNDARY_UPPER_TILE (1 << 3) + /* properties of the boundary of the current CTB for the purposes + * of the deblocking filter */ + int boundary_flags; + + SliceContext *sc; + VVCFrameContext *fc; + EntryPoint *ep; + int *coeffs; +} VVCLocalContext; + +typedef struct VVCAllowedSplit { + int qt; + int btv; + int bth; + int ttv; + int tth; +} VVCAllowedSplit; + +typedef struct SAOParams { + int offset_abs[3][4]; ///< sao_offset_abs + int offset_sign[3][4]; ///< sao_offset_sign + + uint8_t band_position[3]; ///< sao_band_position + + int eo_class[3]; ///< sao_eo_class + + int16_t offset_val[3][5]; ///< SaoOffsetVal + + uint8_t type_idx[3]; ///< sao_type_idx +} SAOParams; + +typedef struct ALFParams { + uint8_t ctb_flag[3]; ///< alf_ctb_flag[] + uint8_t ctb_filt_set_idx_y; ///< AlfCtbFiltSetIdxY + uint8_t alf_ctb_filter_alt_idx[2]; ///< alf_ctb_filter_alt_idx[] + uint8_t ctb_cc_idc[2]; ///< alf_ctb_cc_cb_idc, alf_ctb_cc_cr_idc + + uint8_t applied[3]; +} ALFParams; + +/** + * parse a CTU + * @param lc local context for CTU + * @param ctb_idx CTB(CTU) address in the current slice + * @param rs raster order for the CTU. + * @param rx raster order x for the CTU. + * @param ry raster order y for the CTU. + * @return AVERROR + */ +int ff_vvc_coding_tree_unit(VVCLocalContext *lc, int ctu_idx, int rs, int rx, int ry); + +//utils +void ff_vvc_set_neighbour_available(VVCLocalContext *lc, int x0, int y0, int w, int h); +void ff_vvc_decode_neighbour(VVCLocalContext *lc, int x_ctb, int y_ctb, int rx, int ry, int rs); +void ff_vvc_ctu_free_cus(CTU *ctu); +int ff_vvc_get_qPy(const VVCFrameContext *fc, int xc, int yc); +void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, int bit_depth, int persistent_rice_adaptation_enabled_flag); + +#endif // AVCODEC_VVC_VVC_CTU_H diff --git a/libavcodec/vvc/vvc_data.c b/libavcodec/vvc/vvc_data.c new file mode 100644 index 00000000000..0c9376d098f --- /dev/null +++ b/libavcodec/vvc/vvc_data.c @@ -0,0 +1,3486 @@ +/* + * VVC shared tables + * + * 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 + +#include "libavutil/avassert.h" + +#include "vvc_data.h" + +const uint8_t ff_vvc_diag_scan_x[5][5][16*16] = { + { + //1x1 + { 0, }, + //1x2 + { 0, 0, }, + //1x4 + { 0, 0, 0, 0, }, + //1x8 + { 0, 0, 0, 0, 0, 0, 0, 0, }, + //1x16 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + }, + { + //2x1 + { 0, 1, }, + //2x2 + { 0, 0, 1, 1, }, + //2x4 + { 0, 0, 1, 0, 1, 0, 1, 1, }, + //2x8 + { 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, }, + //2x16 + { + 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, + 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, + }, + }, + { + //4x1 + { 0, 1, 2, 3, }, + //4x2 + { 0, 0, 1, 1, 2, 2, 3, 3, }, + //4x4 + { 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 2, 3, 3, }, + //4x8 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 2, 3, 3, + }, + //4x16 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 2, 3, 3, + }, + }, + { + //8x1 + { 0, 1, 2, 3, 4, 5, 6, 7, }, + //8x2 + { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, }, + //8x4 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, + 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7, + }, + //8x8 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, + 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, + 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, + 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7, + }, + //8x16 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, + 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, + 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6, + 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7, + }, + }, + { + //16x1 + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, }, + //16x2 + { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, + }, + //16x4 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 4, 2, 3, + 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 8, 6, 7, + 8, 9, 7, 8, 9, 10, 8, 9, 10, 11, 9, 10, 11, 12, 10, 11, + 12, 13, 11, 12, 13, 14, 12, 13, 14, 15, 13, 14, 15, 14, 15, 15, + }, + //16x8 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, + 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, + 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8, 2, 3, 4, 5, + 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 10, 4, 5, 6, 7, + 8, 9, 10, 11, 5, 6, 7, 8, 9, 10, 11, 12, 6, 7, 8, 9, + 10, 11, 12, 13, 7, 8, 9, 10, 11, 12, 13, 14, 8, 9, 10, 11, + 12, 13, 14, 15, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14, + 15, 11, 12, 13, 14, 15, 12, 13, 14, 15, 13, 14, 15, 14, 15, 15, + }, + //16x16 + { + 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, + 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, + 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 7, 8, 9, 10, 11, 12, 13, 14, 15, 8, 9, 10, 11, + 12, 13, 14, 15, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14, + 15, 11, 12, 13, 14, 15, 12, 13, 14, 15, 13, 14, 15, 14, 15, 15, + }, + }, +}; + +const uint8_t ff_vvc_diag_scan_y[5][5][16*16] = { + { + //1x1 + { 0, }, + //1x2 + { 0, 1, }, + //1x4 + { 0, 1, 2, 3, }, + //1x8 + { 0, 1, 2, 3, 4, 5, 6, 7, }, + //1x16 + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, }, + }, + { + //2x1 + { 0, 0, }, + //2x2 + { 0, 1, 0, 1, }, + //2x4 + { 0, 1, 0, 2, 1, 3, 2, 3, }, + //2x8 + { 0, 1, 0, 2, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 6, 7, }, + //2x16 + { + 0, 1, 0, 2, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 6, 8, + 7, 9, 8, 10, 9, 11, 10, 12, 11, 13, 12, 14, 13, 15, 14, 15, + }, + }, + { + //4x1 + { 0, 0, 0, 0, }, + //4x2 + { 0, 1, 0, 1, 0, 1, 0, 1, }, + //4x4 + { 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 3, 2, 3, }, + //4x8 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 5, 4, + 3, 2, 6, 5, 4, 3, 7, 6, 5, 4, 7, 6, 5, 7, 6, 7, + }, + //4x16 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 5, 4, + 3, 2, 6, 5, 4, 3, 7, 6, 5, 4, 8, 7, 6, 5, 9, 8, + 7, 6, 10, 9, 8, 7, 11, 10, 9, 8, 12, 11, 10, 9, 13, 12, + 11, 10, 14, 13, 12, 11, 15, 14, 13, 12, 15, 14, 13, 15, 14, 15, + }, + }, + { + //8x1 + { 0, 0, 0, 0, 0, 0, 0, 0, }, + //8x2 + { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, }, + //8x4 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, + 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 3, 2, 3, + }, + //8x8 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5, + 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, + 2, 7, 6, 5, 4, 3, 7, 6, 5, 4, 7, 6, 5, 7, 6, 7, + }, + //8x16 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5, + 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 8, 7, 6, 5, 4, 3, 2, 1, 9, 8, 7, 6, + 5, 4, 3, 2, 10, 9, 8, 7, 6, 5, 4, 3, 11, 10, 9, 8, + 7, 6, 5, 4, 12, 11, 10, 9, 8, 7, 6, 5, 13, 12, 11, 10, + 9, 8, 7, 6, 14, 13, 12, 11, 10, 9, 8, 7, 15, 14, 13, 12, + 11, 10, 9, 8, 15, 14, 13, 12, 11, 10, 9, 15, 14, 13, 12, 11, + 10, 15, 14, 13, 12, 11, 15, 14, 13, 12, 15, 14, 13, 15, 14, 15, + }, + }, + { + //16x1 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + //16x2 + { + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + }, + //16x4 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, + 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, + 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, + 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 3, 2, 3, + }, + //16x8 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5, + 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3, + 2, 7, 6, 5, 4, 3, 7, 6, 5, 4, 7, 6, 5, 7, 6, 7, + }, + //16x16 + { + 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5, + 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, + 3, 2, 1, 0, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7, + 6, 5, 4, 3, 2, 1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2, + 1, 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 12, 11, + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 13, 12, 11, 10, 9, + 8, 7, 6, 5, 4, 3, 2, 1, 0, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, + 7, 6, 5, 4, 3, 2, 1, 15, 14, 13, 12, 11, 10, 9, 8, 7, + 6, 5, 4, 3, 2, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, + 4, 3, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 15, 14, + 13, 12, 11, 10, 9, 8, 7, 6, 5, 15, 14, 13, 12, 11, 10, 9, + 8, 7, 6, 15, 14, 13, 12, 11, 10, 9, 8, 7, 15, 14, 13, 12, + 11, 10, 9, 8, 15, 14, 13, 12, 11, 10, 9, 15, 14, 13, 12, 11, + 10, 15, 14, 13, 12, 11, 15, 14, 13, 12, 15, 14, 13, 15, 14, 15, + }, + }, +}; + +const uint8_t ff_vvc_scaling_pred_8[8 * 8] = { + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, +}; + +const uint8_t ff_vvc_scaling_pred_16[8 * 8] = { + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, +}; + +const int ff_vvc_scaling_list0[8 * 8] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const uint8_t mip_matrix_4x4[16][16][4] = { + { + { 32, 30, 90, 28 }, + { 32, 32, 72, 28 }, + { 34, 77, 53, 30 }, + { 51, 124, 36, 37 }, + { 31, 31, 95, 37 }, + { 33, 31, 70, 50 }, + { 52, 80, 25, 60 }, + { 78, 107, 1, 65 }, + { 31, 29, 37, 95 }, + { 38, 34, 19, 101 }, + { 73, 85, 0, 81 }, + { 92, 99, 0, 65 }, + { 34, 29, 14, 111 }, + { 48, 48, 7, 100 }, + { 80, 91, 0, 74 }, + { 89, 97, 0, 64 } + }, + { + { 31, 23, 34, 29 }, + { 31, 43, 34, 31 }, + { 30, 95, 34, 32 }, + { 29, 100, 35, 33 }, + { 31, 23, 34, 29 }, + { 31, 43, 34, 31 }, + { 30, 95, 34, 32 }, + { 29, 99, 35, 33 }, + { 31, 24, 35, 29 }, + { 31, 44, 34, 31 }, + { 30, 95, 35, 32 }, + { 29, 99, 35, 33 }, + { 31, 24, 35, 30 }, + { 31, 44, 35, 31 }, + { 30, 95, 35, 32 }, + { 29, 99, 35, 33 } + }, + { + { 32, 32, 36, 58 }, + { 32, 29, 26, 66 }, + { 36, 37, 23, 61 }, + { 79, 84, 3, 37 }, + { 32, 32, 30, 69 }, + { 33, 29, 24, 71 }, + { 44, 16, 21, 70 }, + { 96, 18, 0, 57 }, + { 32, 31, 24, 74 }, + { 33, 30, 23, 71 }, + { 36, 24, 24, 71 }, + { 59, 9, 16, 68 }, + { 32, 32, 23, 75 }, + { 33, 30, 24, 70 }, + { 32, 30, 25, 71 }, + { 36, 26, 25, 70 } + }, + { + { 32, 33, 34, 32 }, + { 32, 30, 22, 38 }, + { 29, 46, 25, 38 }, + { 53, 123, 28, 22 }, + { 32, 33, 30, 37 }, + { 32, 30, 21, 38 }, + { 32, 40, 24, 38 }, + { 64, 116, 26, 17 }, + { 32, 32, 23, 49 }, + { 32, 30, 21, 39 }, + { 34, 39, 24, 37 }, + { 72, 109, 23, 16 }, + { 33, 31, 17, 60 }, + { 32, 31, 21, 39 }, + { 35, 41, 24, 37 }, + { 72, 106, 22, 18 } + }, + { + { 34, 25, 89, 20 }, + { 38, 32, 47, 24 }, + { 40, 86, 29, 27 }, + { 38, 98, 32, 29 }, + { 34, 31, 94, 40 }, + { 44, 25, 83, 27 }, + { 54, 72, 43, 16 }, + { 47, 94, 33, 22 }, + { 33, 31, 36, 94 }, + { 43, 23, 51, 76 }, + { 62, 55, 64, 25 }, + { 57, 89, 38, 15 }, + { 32, 32, 28, 101 }, + { 38, 26, 33, 94 }, + { 55, 38, 68, 47 }, + { 59, 80, 52, 16 } + }, + { + { 28, 30, 68, 29 }, + { 23, 48, 23, 48 }, + { 39, 98, 16, 42 }, + { 84, 86, 20, 17 }, + { 25, 31, 52, 74 }, + { 38, 68, 5, 70 }, + { 95, 78, 7, 21 }, + { 127, 54, 12, 0 }, + { 30, 47, 14, 107 }, + { 79, 76, 0, 53 }, + { 127, 59, 7, 1 }, + { 127, 51, 9, 0 }, + { 50, 71, 1, 96 }, + { 109, 69, 7, 25 }, + { 127, 56, 9, 0 }, + { 123, 53, 13, 0 } + }, + { + { 40, 20, 72, 18 }, + { 48, 29, 44, 18 }, + { 53, 81, 35, 18 }, + { 48, 96, 33, 22 }, + { 45, 23, 79, 49 }, + { 61, 21, 56, 49 }, + { 72, 52, 32, 48 }, + { 65, 69, 20, 50 }, + { 41, 27, 29, 96 }, + { 49, 22, 28, 94 }, + { 52, 22, 28, 93 }, + { 49, 27, 27, 92 }, + { 37, 29, 26, 98 }, + { 39, 28, 28, 97 }, + { 38, 28, 30, 97 }, + { 38, 29, 30, 95 } + }, + { + { 33, 27, 43, 27 }, + { 32, 29, 31, 31 }, + { 31, 73, 33, 31 }, + { 35, 104, 34, 28 }, + { 32, 30, 63, 22 }, + { 33, 26, 33, 29 }, + { 33, 57, 33, 30 }, + { 37, 100, 35, 27 }, + { 32, 31, 85, 25 }, + { 34, 25, 39, 25 }, + { 35, 39, 32, 28 }, + { 40, 91, 35, 25 }, + { 32, 30, 77, 50 }, + { 34, 26, 54, 22 }, + { 37, 31, 34, 27 }, + { 45, 75, 34, 23 } + }, + { + { 34, 25, 77, 19 }, + { 36, 34, 56, 24 }, + { 41, 83, 39, 30 }, + { 47, 96, 28, 35 }, + { 34, 31, 70, 65 }, + { 38, 29, 53, 77 }, + { 43, 36, 37, 83 }, + { 48, 39, 28, 83 }, + { 33, 31, 31, 98 }, + { 33, 31, 30, 99 }, + { 34, 30, 31, 98 }, + { 36, 29, 31, 96 }, + { 32, 32, 30, 97 }, + { 32, 32, 31, 96 }, + { 31, 33, 33, 96 }, + { 32, 33, 34, 94 } + }, + { + { 30, 30, 93, 19 }, + { 31, 59, 67, 34 }, + { 31, 79, 36, 59 }, + { 30, 67, 17, 79 }, + { 30, 38, 68, 69 }, + { 29, 40, 43, 91 }, + { 26, 35, 32, 101 }, + { 23, 32, 30, 101 }, + { 26, 34, 30, 101 }, + { 23, 33, 30, 102 }, + { 20, 32, 31, 102 }, + { 18, 33, 32, 102 }, + { 23, 33, 31, 100 }, + { 20, 34, 32, 100 }, + { 18, 35, 33, 100 }, + { 18, 35, 33, 100 } + }, + { + { 31, 54, 90, 26 }, + { 32, 60, 53, 61 }, + { 34, 49, 37, 84 }, + { 34, 39, 35, 89 }, + { 35, 38, 41, 88 }, + { 35, 35, 32, 96 }, + { 35, 31, 33, 96 }, + { 35, 32, 35, 94 }, + { 34, 34, 30, 97 }, + { 35, 32, 33, 95 }, + { 35, 32, 34, 94 }, + { 35, 34, 34, 93 }, + { 34, 34, 34, 93 }, + { 35, 34, 34, 93 }, + { 35, 34, 34, 92 }, + { 36, 34, 35, 91 } + }, + { + { 32, 29, 54, 24 }, + { 31, 32, 34, 29 }, + { 31, 43, 34, 29 }, + { 32, 67, 36, 28 }, + { 31, 34, 69, 37 }, + { 31, 35, 46, 33 }, + { 30, 35, 39, 33 }, + { 30, 42, 39, 36 }, + { 31, 35, 39, 88 }, + { 30, 38, 41, 84 }, + { 30, 39, 40, 81 }, + { 39, 46, 38, 78 }, + { 31, 36, 34, 96 }, + { 34, 38, 37, 93 }, + { 55, 42, 38, 82 }, + { 89, 53, 38, 65 } + }, + { + { 32, 33, 43, 29 }, + { 32, 30, 29, 33 }, + { 31, 47, 31, 33 }, + { 33, 100, 31, 31 }, + { 32, 33, 74, 25 }, + { 32, 32, 34, 31 }, + { 32, 33, 30, 33 }, + { 32, 68, 30, 32 }, + { 32, 31, 91, 40 }, + { 32, 32, 58, 26 }, + { 31, 31, 30, 32 }, + { 31, 42, 30, 33 }, + { 32, 31, 49, 85 }, + { 32, 31, 83, 35 }, + { 31, 33, 48, 29 }, + { 31, 36, 32, 33 } + }, + { + { 31, 29, 81, 35 }, + { 32, 28, 34, 50 }, + { 31, 75, 16, 43 }, + { 34, 103, 29, 32 }, + { 32, 32, 53, 78 }, + { 31, 28, 36, 88 }, + { 30, 52, 18, 73 }, + { 52, 88, 17, 35 }, + { 32, 32, 35, 94 }, + { 30, 31, 35, 95 }, + { 36, 29, 31, 92 }, + { 100, 43, 16, 40 }, + { 32, 32, 35, 93 }, + { 30, 32, 38, 93 }, + { 55, 18, 37, 83 }, + { 127, 0, 30, 40 } + }, + { + { 31, 22, 47, 30 }, + { 31, 48, 25, 34 }, + { 30, 95, 31, 32 }, + { 32, 103, 33, 32 }, + { 30, 24, 57, 31 }, + { 30, 47, 26, 34 }, + { 31, 95, 31, 32 }, + { 43, 97, 35, 25 }, + { 29, 26, 44, 63 }, + { 37, 38, 24, 47 }, + { 74, 63, 28, 20 }, + { 110, 58, 34, 3 }, + { 46, 22, 5, 108 }, + { 93, 5, 9, 77 }, + { 127, 0, 17, 52 }, + { 127, 0, 15, 50 } + }, + { + { 32, 27, 68, 24 }, + { 35, 23, 35, 28 }, + { 35, 64, 29, 29 }, + { 37, 104, 33, 28 }, + { 32, 32, 91, 40 }, + { 36, 23, 67, 36 }, + { 49, 23, 39, 28 }, + { 60, 67, 30, 20 }, + { 32, 32, 36, 95 }, + { 35, 29, 38, 93 }, + { 50, 16, 30, 84 }, + { 72, 16, 15, 65 }, + { 32, 32, 27, 100 }, + { 33, 32, 29, 100 }, + { 37, 29, 30, 98 }, + { 48, 21, 29, 90 } + } +}; + +static const uint8_t mip_matrix_8x8[8][16][8] = { + { + { 30, 63, 46, 37, 25, 33, 33, 34 }, + { 30, 60, 66, 38, 32, 31, 32, 33 }, + { 29, 45, 74, 42, 32, 32, 32, 33 }, + { 30, 39, 62, 58, 32, 33, 32, 33 }, + { 30, 66, 55, 39, 32, 30, 30, 36 }, + { 29, 54, 69, 40, 33, 31, 31, 33 }, + { 28, 48, 71, 43, 32, 33, 32, 33 }, + { 28, 41, 72, 46, 32, 34, 32, 33 }, + { 30, 66, 56, 40, 32, 33, 28, 33 }, + { 29, 55, 69, 39, 33, 33, 30, 32 }, + { 27, 46, 72, 43, 33, 33, 32, 33 }, + { 27, 42, 69, 48, 32, 34, 32, 33 }, + { 30, 63, 55, 40, 32, 33, 35, 30 }, + { 29, 56, 66, 40, 33, 33, 33, 30 }, + { 27, 47, 69, 44, 33, 33, 33, 32 }, + { 27, 42, 65, 50, 32, 34, 32, 33 } + }, + { + { 32, 33, 30, 31, 74, 30, 31, 32 }, + { 33, 56, 28, 30, 41, 29, 32, 32 }, + { 33, 77, 52, 26, 29, 34, 30, 32 }, + { 33, 37, 80, 41, 31, 34, 30, 32 }, + { 32, 32, 33, 31, 59, 76, 28, 31 }, + { 33, 31, 31, 30, 78, 40, 28, 32 }, + { 33, 47, 28, 29, 53, 27, 31, 31 }, + { 33, 61, 44, 28, 34, 32, 31, 31 }, + { 32, 31, 34, 30, 26, 64, 76, 27 }, + { 32, 31, 34, 29, 45, 86, 36, 29 }, + { 33, 27, 34, 29, 73, 55, 25, 32 }, + { 33, 33, 34, 30, 62, 33, 30, 31 }, + { 32, 31, 34, 30, 30, 29, 58, 74 }, + { 32, 31, 35, 29, 27, 53, 77, 35 }, + { 32, 30, 36, 29, 40, 80, 44, 31 }, + { 33, 28, 37, 30, 58, 60, 31, 33 } + }, + { + { 32, 51, 27, 32, 27, 50, 29, 32 }, + { 32, 95, 42, 29, 29, 42, 30, 32 }, + { 32, 27, 99, 34, 31, 41, 29, 32 }, + { 32, 34, 21, 104, 31, 42, 30, 32 }, + { 32, 45, 30, 32, 9, 88, 40, 30 }, + { 32, 77, 38, 30, 9, 76, 38, 30 }, + { 32, 38, 78, 33, 14, 67, 37, 30 }, + { 32, 30, 30, 87, 20, 59, 38, 31 }, + { 33, 37, 32, 32, 27, 18, 106, 34 }, + { 34, 44, 34, 31, 25, 17, 108, 31 }, + { 36, 39, 45, 31, 24, 15, 108, 30 }, + { 37, 31, 31, 54, 25, 14, 101, 32 }, + { 36, 33, 32, 30, 29, 37, 13, 110 }, + { 39, 32, 32, 29, 27, 37, 15, 108 }, + { 44, 33, 31, 27, 25, 37, 16, 106 }, + { 47, 30, 31, 32, 25, 34, 19, 102 } + }, + { + { 32, 48, 35, 35, 47, 68, 31, 31 }, + { 32, 33, 59, 40, 27, 71, 33, 30 }, + { 32, 29, 47, 65, 24, 62, 37, 30 }, + { 33, 33, 31, 81, 26, 50, 42, 32 }, + { 32, 30, 40, 38, 30, 70, 55, 31 }, + { 32, 20, 46, 50, 26, 55, 64, 31 }, + { 33, 30, 29, 66, 25, 41, 72, 33 }, + { 36, 34, 27, 69, 26, 31, 67, 39 }, + { 33, 28, 36, 40, 30, 26, 85, 47 }, + { 36, 27, 33, 50, 31, 20, 79, 53 }, + { 43, 30, 26, 57, 28, 17, 67, 62 }, + { 51, 27, 28, 55, 22, 23, 49, 70 }, + { 38, 29, 32, 39, 28, 30, 22, 104 }, + { 51, 31, 28, 43, 24, 31, 17, 102 }, + { 69, 23, 30, 40, 15, 38, 10, 95 }, + { 77, 13, 35, 38, 8, 43, 8, 90 } + }, + { + { 32, 38, 32, 33, 101, 40, 29, 32 }, + { 32, 40, 37, 32, 100, 36, 30, 32 }, + { 32, 37, 46, 35, 94, 33, 30, 31 }, + { 33, 34, 30, 62, 81, 35, 30, 31 }, + { 32, 32, 33, 32, 22, 102, 39, 29 }, + { 32, 31, 33, 33, 26, 104, 34, 28 }, + { 33, 33, 33, 33, 31, 103, 32, 28 }, + { 33, 32, 34, 36, 37, 94, 33, 28 }, + { 32, 33, 32, 32, 34, 24, 99, 36 }, + { 32, 34, 33, 33, 33, 30, 98, 32 }, + { 33, 33, 34, 33, 31, 37, 95, 29 }, + { 33, 33, 33, 36, 30, 46, 85, 31 }, + { 32, 33, 32, 33, 30, 34, 23, 104 }, + { 32, 34, 33, 33, 31, 32, 30, 98 }, + { 32, 33, 34, 34, 31, 29, 39, 91 }, + { 33, 33, 32, 37, 32, 30, 47, 82 } + }, + { + { 32, 52, 48, 31, 38, 76, 26, 32 }, + { 33, 19, 62, 50, 25, 50, 51, 31 }, + { 33, 30, 20, 74, 29, 29, 54, 51 }, + { 34, 35, 23, 56, 31, 25, 41, 76 }, + { 33, 25, 38, 39, 28, 39, 83, 35 }, + { 35, 28, 25, 47, 31, 23, 57, 74 }, + { 37, 35, 22, 38, 31, 27, 30, 101 }, + { 38, 32, 33, 29, 30, 31, 27, 103 }, + { 34, 32, 27, 37, 32, 25, 41, 92 }, + { 38, 33, 28, 32, 30, 31, 18, 111 }, + { 40, 32, 33, 27, 29, 33, 18, 111 }, + { 40, 32, 34, 27, 28, 33, 23, 105 }, + { 35, 32, 30, 33, 31, 33, 20, 107 }, + { 38, 31, 33, 30, 29, 33, 21, 106 }, + { 40, 32, 33, 29, 29, 34, 22, 105 }, + { 40, 32, 33, 30, 29, 34, 24, 101 } + }, + { + { 32, 28, 31, 33, 92, 33, 30, 31 }, + { 33, 30, 28, 33, 71, 26, 32, 30 }, + { 33, 60, 26, 33, 47, 28, 33, 30 }, + { 33, 63, 44, 36, 37, 31, 33, 30 }, + { 33, 30, 31, 33, 43, 90, 33, 29 }, + { 33, 28, 29, 34, 71, 71, 26, 30 }, + { 33, 30, 26, 33, 86, 45, 28, 30 }, + { 33, 38, 29, 32, 74, 32, 33, 29 }, + { 33, 32, 30, 32, 29, 41, 95, 27 }, + { 34, 31, 29, 33, 26, 71, 73, 22 }, + { 34, 31, 29, 33, 37, 88, 46, 25 }, + { 33, 32, 28, 34, 55, 75, 36, 28 }, + { 34, 31, 30, 32, 33, 27, 43, 89 }, + { 35, 32, 28, 33, 33, 23, 77, 59 }, + { 34, 33, 28, 33, 30, 35, 91, 37 }, + { 34, 34, 28, 34, 33, 53, 74, 31 } + }, + { + { 33, 49, 26, 32, 26, 52, 28, 31 }, + { 33, 71, 72, 24, 30, 32, 34, 31 }, + { 32, 23, 70, 68, 32, 32, 32, 32 }, + { 31, 33, 21, 106, 33, 32, 32, 33 }, + { 34, 47, 32, 29, 5, 86, 44, 26 }, + { 34, 44, 89, 28, 28, 37, 33, 30 }, + { 32, 27, 46, 89, 33, 31, 31, 32 }, + { 30, 33, 20, 107, 33, 33, 32, 33 }, + { 35, 39, 42, 27, 26, 24, 92, 35 }, + { 34, 27, 87, 43, 30, 34, 38, 31 }, + { 31, 31, 32, 100, 32, 33, 30, 32 }, + { 29, 32, 22, 106, 33, 33, 32, 33 }, + { 35, 29, 47, 32, 32, 32, 17, 100 }, + { 34, 24, 69, 60, 34, 33, 28, 44 }, + { 31, 33, 31, 99, 32, 33, 32, 31 }, + { 29, 33, 25, 103, 33, 33, 32, 35 } + } +}; + +static const uint8_t mip_matrix_16x16[6][64][7] = { + { + { 42, 37, 33, 27, 44, 33, 35 }, + { 71, 39, 34, 24, 36, 35, 36 }, + { 77, 46, 35, 33, 30, 34, 36 }, + { 64, 60, 35, 33, 31, 32, 36 }, + { 49, 71, 38, 32, 32, 31, 36 }, + { 42, 66, 50, 33, 31, 32, 36 }, + { 40, 52, 67, 33, 31, 32, 35 }, + { 38, 43, 75, 33, 32, 32, 35 }, + { 56, 40, 33, 26, 43, 38, 36 }, + { 70, 49, 34, 30, 28, 38, 38 }, + { 65, 57, 36, 34, 28, 33, 39 }, + { 59, 60, 39, 33, 30, 31, 38 }, + { 55, 60, 43, 33, 30, 31, 38 }, + { 51, 61, 47, 33, 30, 32, 37 }, + { 46, 62, 51, 34, 30, 32, 37 }, + { 42, 60, 55, 33, 31, 32, 37 }, + { 60, 42, 34, 30, 37, 43, 38 }, + { 68, 52, 35, 35, 22, 37, 40 }, + { 62, 58, 37, 34, 28, 31, 40 }, + { 58, 59, 41, 33, 30, 30, 39 }, + { 56, 59, 44, 34, 30, 31, 38 }, + { 53, 60, 45, 33, 30, 31, 38 }, + { 49, 65, 45, 33, 30, 31, 38 }, + { 45, 64, 47, 33, 31, 32, 38 }, + { 59, 44, 35, 31, 34, 43, 41 }, + { 66, 53, 36, 35, 25, 31, 43 }, + { 61, 58, 38, 34, 29, 30, 40 }, + { 59, 57, 41, 33, 30, 31, 39 }, + { 57, 58, 43, 33, 30, 31, 39 }, + { 54, 61, 43, 33, 31, 31, 39 }, + { 51, 64, 43, 33, 31, 31, 39 }, + { 48, 64, 45, 33, 32, 31, 39 }, + { 57, 45, 35, 30, 35, 40, 44 }, + { 65, 54, 37, 33, 33, 24, 44 }, + { 63, 56, 38, 34, 30, 29, 39 }, + { 61, 56, 41, 34, 30, 32, 39 }, + { 58, 58, 42, 33, 31, 31, 39 }, + { 54, 62, 41, 33, 31, 31, 39 }, + { 51, 65, 42, 33, 31, 31, 39 }, + { 48, 63, 43, 33, 32, 31, 39 }, + { 55, 46, 35, 30, 36, 38, 47 }, + { 65, 53, 37, 32, 36, 26, 40 }, + { 65, 54, 38, 33, 31, 30, 38 }, + { 63, 55, 39, 33, 30, 32, 38 }, + { 59, 58, 40, 33, 31, 31, 39 }, + { 54, 64, 40, 33, 31, 30, 40 }, + { 49, 66, 40, 32, 32, 30, 41 }, + { 48, 64, 42, 32, 32, 30, 41 }, + { 54, 46, 35, 30, 34, 39, 49 }, + { 64, 52, 36, 32, 34, 34, 35 }, + { 65, 53, 37, 33, 32, 32, 37 }, + { 63, 55, 38, 33, 31, 31, 39 }, + { 59, 60, 38, 33, 31, 31, 40 }, + { 54, 64, 38, 33, 32, 30, 40 }, + { 49, 66, 39, 33, 32, 29, 41 }, + { 47, 64, 42, 32, 33, 29, 42 }, + { 51, 46, 35, 31, 33, 37, 54 }, + { 61, 51, 36, 32, 33, 38, 36 }, + { 63, 53, 37, 32, 32, 34, 37 }, + { 62, 55, 37, 33, 32, 32, 39 }, + { 58, 59, 37, 33, 32, 31, 40 }, + { 53, 63, 38, 33, 32, 31, 40 }, + { 49, 64, 40, 33, 33, 30, 41 }, + { 46, 62, 42, 33, 33, 30, 42 } + }, + { + { 39, 34, 33, 58, 44, 31, 32 }, + { 60, 38, 32, 40, 51, 30, 31 }, + { 73, 49, 31, 39, 48, 32, 31 }, + { 60, 73, 30, 39, 46, 33, 32 }, + { 43, 87, 35, 38, 45, 33, 32 }, + { 35, 78, 54, 36, 45, 33, 32 }, + { 33, 47, 86, 35, 44, 33, 32 }, + { 31, 17, 114, 34, 44, 34, 33 }, + { 43, 37, 32, 53, 70, 30, 31 }, + { 53, 50, 30, 42, 72, 31, 30 }, + { 52, 66, 30, 39, 70, 32, 30 }, + { 46, 78, 35, 37, 68, 34, 30 }, + { 43, 75, 48, 37, 66, 34, 30 }, + { 40, 62, 68, 35, 65, 35, 30 }, + { 33, 37, 97, 33, 62, 37, 31 }, + { 26, 14, 122, 32, 59, 38, 33 }, + { 40, 39, 33, 34, 87, 37, 30 }, + { 45, 54, 32, 34, 84, 41, 29 }, + { 41, 70, 35, 33, 83, 40, 29 }, + { 37, 73, 44, 32, 82, 40, 30 }, + { 37, 65, 60, 31, 81, 41, 29 }, + { 35, 48, 82, 30, 79, 43, 29 }, + { 28, 27, 108, 28, 76, 45, 30 }, + { 19, 11, 127, 27, 70, 46, 32 }, + { 38, 40, 34, 27, 73, 62, 28 }, + { 39, 54, 35, 30, 73, 62, 28 }, + { 33, 65, 41, 29, 75, 59, 28 }, + { 30, 65, 53, 27, 76, 58, 29 }, + { 29, 53, 72, 26, 77, 58, 29 }, + { 27, 35, 95, 24, 77, 60, 28 }, + { 19, 19, 117, 23, 74, 61, 30 }, + { 9, 16, 127, 23, 68, 60, 34 }, + { 35, 40, 35, 29, 44, 89, 30 }, + { 33, 51, 39, 29, 49, 86, 30 }, + { 28, 57, 49, 28, 53, 83, 30 }, + { 24, 52, 65, 26, 56, 82, 30 }, + { 22, 39, 86, 24, 58, 82, 30 }, + { 18, 22, 108, 23, 59, 82, 31 }, + { 10, 13, 125, 22, 58, 80, 33 }, + { 0, 19, 127, 22, 56, 74, 40 }, + { 33, 40, 36, 31, 28, 90, 45 }, + { 29, 46, 44, 29, 31, 92, 43 }, + { 24, 45, 58, 28, 34, 91, 43 }, + { 19, 37, 78, 26, 37, 91, 43 }, + { 15, 22, 99, 25, 38, 91, 42 }, + { 11, 11, 118, 24, 39, 90, 44 }, + { 2, 11, 127, 23, 41, 85, 48 }, + { 0, 17, 127, 23, 43, 75, 55 }, + { 31, 37, 39, 30, 28, 54, 82 }, + { 27, 37, 52, 28, 30, 58, 79 }, + { 22, 30, 70, 27, 32, 58, 79 }, + { 15, 19, 91, 26, 33, 58, 79 }, + { 10, 8, 111, 25, 34, 58, 79 }, + { 5, 2, 125, 25, 35, 57, 80 }, + { 0, 9, 127, 25, 36, 53, 84 }, + { 0, 13, 127, 25, 39, 47, 88 }, + { 28, 29, 46, 28, 39, 2, 123 }, + { 24, 24, 62, 27, 41, 1, 125 }, + { 19, 14, 81, 25, 43, 0, 126 }, + { 13, 4, 101, 24, 44, 0, 127 }, + { 6, 0, 116, 23, 45, 0, 127 }, + { 0, 0, 126, 23, 45, 1, 127 }, + { 0, 4, 127, 25, 44, 2, 127 }, + { 0, 9, 127, 25, 44, 3, 127 } + }, + { + { 30, 32, 32, 42, 34, 32, 32 }, + { 63, 26, 34, 16, 38, 32, 32 }, + { 98, 26, 34, 25, 34, 33, 32 }, + { 75, 61, 30, 31, 32, 33, 32 }, + { 36, 94, 32, 30, 33, 32, 32 }, + { 26, 76, 58, 30, 33, 32, 32 }, + { 30, 39, 91, 31, 32, 33, 31 }, + { 32, 23, 105, 32, 32, 32, 32 }, + { 34, 30, 33, 31, 52, 29, 32 }, + { 66, 24, 34, 11, 41, 33, 32 }, + { 97, 28, 34, 24, 34, 33, 32 }, + { 71, 65, 30, 30, 32, 33, 32 }, + { 34, 92, 35, 30, 33, 32, 32 }, + { 26, 70, 64, 29, 34, 32, 32 }, + { 30, 37, 94, 30, 33, 32, 31 }, + { 32, 23, 105, 31, 33, 33, 31 }, + { 37, 29, 33, 8, 79, 27, 32 }, + { 71, 22, 35, 5, 50, 32, 32 }, + { 98, 29, 34, 23, 34, 34, 32 }, + { 66, 70, 30, 31, 31, 33, 32 }, + { 31, 92, 38, 30, 33, 32, 32 }, + { 26, 66, 68, 29, 34, 32, 31 }, + { 30, 34, 97, 30, 34, 33, 31 }, + { 31, 22, 106, 30, 34, 33, 31 }, + { 40, 28, 34, 0, 76, 46, 28 }, + { 76, 21, 35, 0, 55, 35, 32 }, + { 97, 32, 34, 21, 37, 33, 33 }, + { 61, 75, 29, 30, 32, 32, 32 }, + { 29, 92, 40, 29, 33, 32, 32 }, + { 26, 62, 73, 29, 34, 32, 31 }, + { 29, 32, 99, 30, 34, 33, 30 }, + { 31, 22, 107, 30, 34, 33, 31 }, + { 42, 27, 34, 1, 48, 79, 25 }, + { 80, 20, 35, 0, 48, 47, 31 }, + { 94, 36, 32, 17, 40, 33, 33 }, + { 55, 80, 29, 27, 35, 31, 32 }, + { 27, 90, 43, 28, 34, 32, 31 }, + { 26, 58, 76, 29, 33, 33, 30 }, + { 29, 30, 101, 29, 34, 34, 30 }, + { 31, 21, 108, 29, 35, 34, 30 }, + { 44, 26, 34, 6, 30, 80, 40 }, + { 81, 21, 35, 0, 41, 52, 35 }, + { 90, 41, 31, 14, 41, 35, 33 }, + { 51, 82, 29, 24, 37, 32, 32 }, + { 27, 87, 47, 27, 35, 32, 31 }, + { 26, 54, 79, 29, 34, 33, 30 }, + { 29, 29, 102, 28, 34, 33, 30 }, + { 31, 21, 108, 28, 35, 33, 31 }, + { 47, 26, 34, 7, 34, 44, 75 }, + { 80, 24, 34, 0, 41, 41, 50 }, + { 84, 45, 31, 12, 40, 36, 36 }, + { 49, 81, 31, 22, 37, 33, 32 }, + { 28, 81, 51, 26, 35, 33, 31 }, + { 28, 51, 81, 28, 34, 33, 30 }, + { 29, 30, 101, 28, 35, 33, 31 }, + { 31, 22, 107, 28, 35, 33, 32 }, + { 48, 27, 34, 10, 40, 16, 97 }, + { 75, 27, 34, 3, 42, 26, 66 }, + { 77, 47, 33, 12, 40, 32, 43 }, + { 49, 75, 36, 21, 37, 33, 35 }, + { 32, 72, 55, 25, 36, 33, 32 }, + { 30, 49, 81, 27, 35, 33, 31 }, + { 30, 32, 98, 28, 35, 32, 32 }, + { 31, 24, 104, 28, 35, 32, 33 } + }, + { + { 36, 29, 33, 43, 47, 29, 31 }, + { 74, 20, 35, 19, 47, 34, 32 }, + { 92, 35, 32, 29, 31, 40, 34 }, + { 53, 80, 26, 33, 28, 36, 37 }, + { 24, 91, 41, 31, 31, 31, 38 }, + { 25, 57, 74, 31, 32, 30, 37 }, + { 32, 28, 99, 32, 32, 29, 36 }, + { 34, 20, 105, 33, 32, 30, 35 }, + { 50, 26, 34, 33, 74, 30, 31 }, + { 75, 28, 33, 23, 46, 47, 33 }, + { 64, 58, 29, 30, 26, 46, 40 }, + { 31, 85, 37, 31, 27, 33, 44 }, + { 22, 67, 64, 30, 31, 28, 42 }, + { 29, 35, 93, 31, 32, 27, 40 }, + { 33, 20, 105, 32, 33, 27, 37 }, + { 34, 19, 106, 33, 32, 29, 36 }, + { 51, 29, 33, 25, 72, 51, 30 }, + { 61, 42, 31, 30, 31, 60, 39 }, + { 40, 70, 34, 32, 24, 41, 50 }, + { 22, 72, 54, 30, 31, 27, 50 }, + { 25, 44, 83, 30, 33, 25, 44 }, + { 32, 23, 102, 32, 33, 26, 40 }, + { 34, 18, 107, 32, 33, 28, 37 }, + { 34, 19, 105, 33, 32, 30, 35 }, + { 45, 35, 32, 30, 39, 79, 33 }, + { 43, 53, 33, 35, 24, 53, 55 }, + { 27, 67, 45, 32, 29, 27, 61 }, + { 22, 53, 72, 30, 33, 22, 52 }, + { 28, 31, 95, 31, 33, 25, 43 }, + { 32, 20, 105, 32, 33, 27, 38 }, + { 34, 18, 107, 32, 32, 29, 36 }, + { 34, 20, 105, 33, 31, 31, 35 }, + { 38, 40, 32, 35, 23, 72, 54 }, + { 31, 55, 39, 34, 29, 32, 73 }, + { 22, 57, 60, 31, 35, 18, 64 }, + { 25, 39, 86, 31, 35, 22, 49 }, + { 30, 24, 101, 32, 33, 27, 40 }, + { 33, 19, 106, 32, 32, 30, 36 }, + { 34, 18, 107, 33, 31, 31, 35 }, + { 34, 20, 104, 33, 31, 32, 34 }, + { 33, 42, 35, 34, 28, 39, 82 }, + { 26, 51, 50, 33, 34, 18, 80 }, + { 23, 46, 74, 31, 35, 20, 59 }, + { 27, 32, 93, 32, 34, 26, 44 }, + { 31, 22, 103, 32, 32, 30, 37 }, + { 33, 19, 106, 33, 31, 31, 35 }, + { 34, 19, 106, 33, 31, 32, 34 }, + { 35, 21, 103, 34, 31, 32, 34 }, + { 29, 41, 41, 33, 34, 20, 92 }, + { 24, 44, 62, 34, 35, 18, 73 }, + { 24, 37, 83, 34, 33, 25, 52 }, + { 28, 28, 97, 33, 32, 30, 40 }, + { 32, 23, 103, 33, 31, 32, 36 }, + { 34, 20, 105, 34, 30, 33, 34 }, + { 35, 20, 104, 34, 30, 33, 33 }, + { 35, 22, 102, 34, 30, 33, 34 }, + { 27, 38, 51, 34, 34, 20, 86 }, + { 26, 37, 71, 35, 34, 24, 64 }, + { 27, 33, 87, 35, 32, 30, 47 }, + { 30, 28, 96, 34, 31, 32, 39 }, + { 32, 24, 100, 35, 30, 32, 36 }, + { 34, 23, 101, 34, 30, 33, 34 }, + { 35, 23, 101, 34, 30, 32, 34 }, + { 34, 24, 99, 35, 30, 33, 34 } + }, + { + { 39, 30, 31, 67, 33, 34, 31 }, + { 72, 21, 32, 43, 39, 33, 31 }, + { 100, 23, 32, 35, 39, 34, 31 }, + { 75, 63, 24, 32, 38, 34, 32 }, + { 32, 98, 26, 29, 37, 35, 32 }, + { 22, 77, 55, 29, 36, 35, 31 }, + { 31, 37, 90, 31, 35, 35, 32 }, + { 35, 22, 100, 33, 33, 36, 33 }, + { 47, 29, 32, 74, 54, 32, 31 }, + { 71, 24, 32, 60, 50, 36, 30 }, + { 86, 31, 30, 46, 48, 37, 30 }, + { 65, 63, 25, 34, 46, 39, 30 }, + { 33, 85, 32, 28, 43, 40, 30 }, + { 26, 64, 60, 27, 39, 41, 30 }, + { 33, 33, 87, 29, 35, 41, 31 }, + { 37, 23, 93, 32, 33, 41, 32 }, + { 41, 32, 32, 45, 84, 32, 32 }, + { 55, 31, 32, 50, 70, 40, 30 }, + { 62, 37, 31, 45, 61, 45, 29 }, + { 53, 55, 31, 36, 55, 48, 29 }, + { 38, 63, 40, 29, 48, 50, 28 }, + { 34, 49, 60, 27, 43, 51, 29 }, + { 38, 30, 78, 28, 38, 50, 31 }, + { 40, 24, 83, 30, 36, 48, 33 }, + { 35, 33, 33, 29, 75, 58, 29 }, + { 39, 35, 33, 34, 68, 59, 29 }, + { 41, 39, 34, 36, 61, 62, 29 }, + { 41, 43, 37, 33, 54, 64, 28 }, + { 41, 43, 45, 30, 48, 65, 29 }, + { 42, 36, 56, 27, 44, 63, 30 }, + { 42, 30, 65, 27, 41, 60, 33 }, + { 42, 28, 68, 28, 37, 56, 36 }, + { 33, 34, 33, 31, 42, 88, 30 }, + { 31, 36, 34, 31, 44, 84, 31 }, + { 31, 37, 35, 32, 43, 83, 31 }, + { 35, 35, 39, 32, 40, 82, 31 }, + { 40, 32, 44, 31, 38, 81, 31 }, + { 44, 30, 48, 30, 37, 78, 33 }, + { 44, 30, 52, 28, 37, 72, 36 }, + { 43, 30, 55, 29, 35, 66, 40 }, + { 32, 33, 33, 34, 25, 85, 48 }, + { 30, 34, 34, 33, 25, 88, 44 }, + { 30, 34, 36, 34, 25, 90, 41 }, + { 33, 32, 38, 34, 25, 90, 40 }, + { 38, 29, 41, 34, 26, 88, 40 }, + { 42, 29, 41, 33, 27, 85, 41 }, + { 43, 30, 42, 31, 28, 80, 43 }, + { 42, 31, 45, 31, 30, 72, 47 }, + { 32, 33, 33, 33, 26, 54, 79 }, + { 31, 32, 34, 35, 20, 68, 68 }, + { 32, 32, 35, 36, 17, 76, 62 }, + { 34, 31, 36, 36, 17, 79, 59 }, + { 37, 29, 37, 36, 18, 78, 58 }, + { 39, 29, 37, 35, 20, 77, 58 }, + { 41, 30, 37, 34, 22, 74, 58 }, + { 40, 31, 40, 32, 26, 68, 59 }, + { 33, 31, 34, 33, 29, 31, 98 }, + { 34, 30, 34, 35, 23, 45, 88 }, + { 34, 31, 34, 36, 20, 54, 82 }, + { 35, 31, 34, 36, 18, 59, 78 }, + { 36, 31, 34, 37, 19, 60, 76 }, + { 38, 30, 34, 36, 20, 61, 74 }, + { 39, 31, 35, 35, 22, 60, 73 }, + { 39, 31, 37, 34, 24, 59, 71 } + }, + { + { 30, 33, 32, 55, 32, 32, 32 }, + { 47, 30, 31, 29, 36, 32, 32 }, + { 81, 28, 32, 28, 34, 32, 32 }, + { 85, 46, 29, 32, 32, 33, 32 }, + { 54, 82, 26, 32, 32, 33, 32 }, + { 30, 90, 38, 31, 32, 33, 32 }, + { 30, 56, 73, 31, 33, 32, 32 }, + { 37, 21, 102, 32, 32, 32, 32 }, + { 33, 32, 31, 68, 39, 31, 31 }, + { 38, 32, 31, 43, 34, 33, 31 }, + { 63, 30, 31, 29, 34, 32, 32 }, + { 82, 37, 30, 29, 33, 32, 32 }, + { 71, 63, 27, 31, 32, 33, 32 }, + { 44, 86, 30, 30, 33, 33, 32 }, + { 33, 72, 55, 30, 32, 32, 31 }, + { 37, 37, 86, 31, 32, 33, 31 }, + { 34, 33, 32, 60, 61, 29, 32 }, + { 36, 33, 31, 56, 38, 32, 31 }, + { 51, 30, 31, 38, 33, 33, 32 }, + { 75, 31, 31, 30, 33, 33, 32 }, + { 80, 47, 29, 30, 32, 33, 31 }, + { 60, 73, 27, 30, 33, 33, 31 }, + { 41, 78, 41, 30, 33, 32, 31 }, + { 38, 53, 68, 30, 32, 33, 31 }, + { 33, 33, 32, 43, 77, 35, 30 }, + { 35, 33, 31, 55, 54, 29, 32 }, + { 43, 32, 31, 46, 39, 31, 32 }, + { 64, 30, 31, 35, 34, 33, 32 }, + { 79, 37, 30, 31, 32, 33, 31 }, + { 73, 57, 28, 30, 32, 33, 31 }, + { 54, 73, 33, 30, 32, 33, 31 }, + { 43, 64, 52, 30, 32, 33, 31 }, + { 33, 33, 32, 34, 68, 58, 28 }, + { 34, 33, 31, 45, 70, 33, 31 }, + { 38, 33, 31, 48, 52, 29, 32 }, + { 54, 31, 31, 40, 39, 31, 32 }, + { 73, 32, 31, 34, 34, 33, 31 }, + { 77, 45, 29, 31, 32, 32, 32 }, + { 65, 63, 30, 31, 31, 33, 31 }, + { 51, 66, 42, 30, 32, 33, 31 }, + { 33, 32, 32, 34, 44, 81, 31 }, + { 34, 33, 31, 38, 66, 52, 28 }, + { 36, 33, 30, 44, 62, 34, 31 }, + { 47, 31, 31, 43, 48, 30, 32 }, + { 64, 31, 31, 38, 38, 32, 32 }, + { 75, 38, 30, 33, 34, 32, 32 }, + { 71, 53, 30, 31, 32, 33, 32 }, + { 59, 61, 37, 30, 32, 33, 32 }, + { 33, 32, 31, 35, 31, 71, 54 }, + { 34, 33, 31, 37, 49, 70, 33 }, + { 36, 33, 31, 41, 60, 48, 30 }, + { 43, 32, 31, 43, 54, 35, 31 }, + { 56, 31, 31, 40, 44, 32, 32 }, + { 68, 35, 30, 36, 37, 32, 32 }, + { 70, 45, 30, 33, 34, 33, 32 }, + { 63, 55, 35, 31, 33, 33, 32 }, + { 33, 32, 31, 33, 34, 36, 87 }, + { 34, 32, 31, 36, 38, 62, 52 }, + { 36, 33, 31, 39, 50, 57, 36 }, + { 41, 33, 31, 41, 53, 43, 33 }, + { 50, 33, 31, 41, 48, 36, 32 }, + { 59, 35, 31, 37, 41, 34, 32 }, + { 65, 42, 31, 35, 36, 33, 32 }, + { 62, 49, 35, 33, 34, 34, 33 } + } +}; + +const uint8_t* ff_vvc_get_mip_matrix(const int size_id, const int mode_id) +{ + av_assert0(size_id < 3); + if (size_id == 0) + return &mip_matrix_4x4[mode_id][0][0]; + if (size_id == 1) + return &mip_matrix_8x8[mode_id][0][0]; + return &mip_matrix_16x16[mode_id][0][0]; +} + +// DCT-8 +#define DEFINE_DCT8_P4_MATRIX(a,b,c,d) \ +{ \ + { a, b, c, d }, \ + { b, 0, -b, -b }, \ + { c, -b, -d, a }, \ + { d, -b, a, -c }, \ +} + +#define DEFINE_DCT8_P8_MATRIX(a,b,c,d,e,f,g,h) \ +{ \ + { a, b, c, d, e, f, g, h }, \ + { b, e, h, -g, -d, -a, -c, -f }, \ + { c, h, -e, -a, -f, g, b, d }, \ + { d, -g, -a, -h, c, e, -f, -b }, \ + { e, -d, -f, c, g, -b, -h, a }, \ + { f, -a, g, e, -b, h, d, -c }, \ + { g, -c, b, -f, -h, d, -a, e }, \ + { h, -f, d, -b, a, -c, e, -g }, \ +} + +#define DEFINE_DCT8_P16_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ +{ \ + { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p }, \ + { b, e, h, k, n, 0, -n, -k, -h, -e, -b, -b, -e, -h, -k, -n }, \ + { c, h, m, -p, -k, -f, -a, -e, -j, -o, n, i, d, b, g, l }, \ + { d, k, -p, -i, -b, -f, -m, n, g, a, h, o, -l, -e, -c, -j }, \ + { e, n, -k, -b, -h, 0, h, b, k, -n, -e, -e, -n, k, b, h }, \ + { f, 0, -f, -f, 0, f, f, 0, -f, -f, 0, f, f, 0, -f, -f }, \ + { g, -n, -a, -m, h, f, -o, -b, -l, i, e, -p, -c, -k, j, d }, \ + { h, -k, -e, n, b, 0, -b, -n, e, k, -h, -h, k, e, -n, -b }, \ + { i, -h, -j, g, k, -f, -l, e, m, -d, -n, c, o, -b, -p, a }, \ + { j, -e, -o, a, -n, -f, i, k, -d, -p, b, -m, -g, h, l, -c }, \ + { k, -b, n, h, -e, 0, e, -h, -n, b, -k, -k, b, -n, -h, e }, \ + { l, -b, i, o, -e, f, -p, -h, c, -m, -k, a, -j, -n, d, -g }, \ + { m, -e, d, -l, -n, f, -c, k, o, -g, b, -j, -p, h, -a, i }, \ + { n, -h, b, -e, k, 0, -k, e, -b, h, -n, -n, h, -b, e, -k }, \ + { o, -k, g, -c, b, -f, j, -n, -p, l, -h, d, -a, e, -i, m }, \ + { p, -n, l, -j, h, -f, d, -b, a, -c, e, -g, i, -k, m, -o }, \ +} + +#define DEFINE_DCT8_P32_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F) \ +{ \ + { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F }, \ + { b, e, h, k, n, q, t, w, z, C, F, -E, -B, -y, -v, -s, -p, -m, -j, -g, -d, -a, -c, -f, -i, -l, -o, -r, -u, -x, -A, -D }, \ + { c, h, m, r, w, B, 0, -B, -w, -r, -m, -h, -c, -c, -h, -m, -r, -w, -B, 0, B, w, r, m, h, c, c, h, m, r, w, B }, \ + { d, k, r, y, F, -A, -t, -m, -f, -b, -i, -p, -w, -D, C, v, o, h, a, g, n, u, B, -E, -x, -q, -j, -c, -e, -l, -s, -z }, \ + { e, n, w, F, -y, -p, -g, -c, -l, -u, -D, A, r, i, a, j, s, B, -C, -t, -k, -b, -h, -q, -z, E, v, m, d, f, o, x }, \ + { f, q, B, -A, -p, -e, -g, -r, -C, z, o, d, h, s, D, -y, -n, -c, -i, -t, -E, x, m, b, j, u, F, -w, -l, -a, -k, -v }, \ + { g, t, 0, -t, -g, -g, -t, 0, t, g, g, t, 0, -t, -g, -g, -t, 0, t, g, g, t, 0, -t, -g, -g, -t, 0, t, g, g, t }, \ + { h, w, -B, -m, -c, -r, 0, r, c, m, B, -w, -h, -h, -w, B, m, c, r, 0, -r, -c, -m, -B, w, h, h, w, -B, -m, -c, -r }, \ + { i, z, -w, -f, -l, -C, t, c, o, F, -q, -a, -r, E, n, d, u, -B, -k, -g, -x, y, h, j, A, -v, -e, -m, -D, s, b, p }, \ + { j, C, -r, -b, -u, z, g, m, F, -o, -e, -x, w, d, p, -E, -l, -h, -A, t, a, s, -B, -i, -k, -D, q, c, v, -y, -f, -n }, \ + { k, F, -m, -i, -D, o, g, B, -q, -e, -z, s, c, x, -u, -a, -v, w, b, t, -y, -d, -r, A, f, p, -C, -h, -n, E, j, l }, \ + { l, -E, -h, -p, A, d, t, -w, -a, -x, s, e, B, -o, -i, -F, k, m, -D, -g, -q, z, c, u, -v, -b, -y, r, f, C, -n, -j }, \ + { m, -B, -c, -w, r, h, 0, -h, -r, w, c, B, -m, -m, B, c, w, -r, -h, 0, h, r, -w, -c, -B, m, m, -B, -c, -w, r, h }, \ + { n, -y, -c, -D, i, s, -t, -h, E, d, x, -o, -m, z, b, C, -j, -r, u, g, -F, -e, -w, p, l, -A, -a, -B, k, q, -v, -f }, \ + { o, -v, -h, C, a, D, -g, -w, n, p, -u, -i, B, b, E, -f, -x, m, q, -t, -j, A, c, F, -e, -y, l, r, -s, -k, z, d }, \ + { p, -s, -m, v, j, -y, -g, B, d, -E, -a, -F, c, C, -f, -z, i, w, -l, -t, o, q, -r, -n, u, k, -x, -h, A, e, -D, -b }, \ + { q, -p, -r, o, s, -n, -t, m, u, -l, -v, k, w, -j, -x, i, y, -h, -z, g, A, -f, -B, e, C, -d, -D, c, E, -b, -F, a }, \ + { r, -m, -w, h, B, -c, 0, c, -B, -h, w, m, -r, -r, m, w, -h, -B, c, 0, -c, B, h, -w, -m, r, r, -m, -w, h, B, -c }, \ + { s, -j, -B, a, -C, -i, t, r, -k, -A, b, -D, -h, u, q, -l, -z, c, -E, -g, v, p, -m, -y, d, -F, -f, w, o, -n, -x, e }, \ + { t, -g, 0, g, -t, -t, g, 0, -g, t, t, -g, 0, g, -t, -t, g, 0, -g, t, t, -g, 0, g, -t, -t, g, 0, -g, t, t, -g }, \ + { u, -d, B, n, -k, -E, g, -r, -x, a, -y, -q, h, -F, -j, o, A, -c, v, t, -e, C, m, -l, -D, f, -s, -w, b, -z, -p, i }, \ + { v, -a, w, u, -b, x, t, -c, y, s, -d, z, r, -e, A, q, -f, B, p, -g, C, o, -h, D, n, -i, E, m, -j, F, l, -k }, \ + { w, -c, r, B, -h, m, 0, -m, h, -B, -r, c, -w, -w, c, -r, -B, h, -m, 0, m, -h, B, r, -c, w, w, -c, r, B, -h, m }, \ + { x, -f, m, -E, -q, b, -t, -B, j, -i, A, u, -c, p, F, -n, e, -w, -y, g, -l, D, r, -a, s, C, -k, h, -z, -v, d, -o }, \ + { y, -i, h, -x, -z, j, -g, w, A, -k, f, -v, -B, l, -e, u, C, -m, d, -t, -D, n, -c, s, E, -o, b, -r, -F, p, -a, q }, \ + { z, -l, c, -q, E, u, -g, h, -v, -D, p, -b, m, -A, -y, k, -d, r, -F, -t, f, -i, w, C, -o, a, -n, B, x, -j, e, -s }, \ + { A, -o, c, -j, v, F, -t, h, -e, q, -C, -y, m, -a, l, -x, -D, r, -f, g, -s, E, w, -k, b, -n, z, B, -p, d, -i, u }, \ + { B, -r, h, -c, m, -w, 0, w, -m, c, -h, r, -B, -B, r, -h, c, -m, w, 0, -w, m, -c, h, -r, B, B, -r, h, -c, m, -w }, \ + { C, -u, m, -e, d, -l, t, -B, -D, v, -n, f, -c, k, -s, A, E, -w, o, -g, b, -j, r, -z, -F, x, -p, h, -a, i, -q, y }, \ + { D, -x, r, -l, f, -a, g, -m, s, -y, E, C, -w, q, -k, e, -b, h, -n, t, -z, F, B, -v, p, -j, d, -c, i, -o, u, -A }, \ + { E, -A, w, -s, o, -k, g, -c, b, -f, j, -n, r, -v, z, -D, -F, B, -x, t, -p, l, -h, d, -a, e, -i, m, -q, u, -y, C }, \ + { F, -D, B, -z, x, -v, t, -r, p, -n, l, -j, h, -f, d, -b, a, -c, e, -g, i, -k, m, -o, q, -s, u, -w, y, -A, C, -E }, \ +} + +const int8_t ff_vvc_dct8_4x4[4][4] = DEFINE_DCT8_P4_MATRIX(84, 74, 55, 29); +const int8_t ff_vvc_dct8_8x8[8][8] = DEFINE_DCT8_P8_MATRIX(86, 85, 78, 71, 60, 46, 32, 17); +const int8_t ff_vvc_dct8_16x16[16][16] = DEFINE_DCT8_P16_MATRIX(88, 88, 87, 85, 81, 77, 73, 68, 62, 55, 48, 40, 33, 25, 17, 8); +const int8_t ff_vvc_dct8_32x32[32][32] = DEFINE_DCT8_P32_MATRIX(90, 90, 89, 88, 87, 86, 85, 84, 82, 80, 78, 77, 74, 72, 68, 66, 63, 60, 56, 53, 50, 46, 42, 38, 34, 30, 26, 21, 17, 13, 9, 4); + +// DST-7 +#define DEFINE_DST7_P4_MATRIX(a,b,c,d) \ +{ \ + { a, b, c, d }, \ + { c, c, 0, -c }, \ + { d, -a, -c, b }, \ + { b, -d, c, -a }, \ +} + +#define DEFINE_DST7_P8_MATRIX(a,b,c,d,e,f,g,h) \ +{ \ + { a, b, c, d, e, f, g, h }, \ + { c, f, h, e, b, -a, -d, -g }, \ + { e, g, b, -c, -h, -d, a, f }, \ + { g, c, -d, -f, a, h, b, -e }, \ + { h, -a, -g, b, f, -c, -e, d }, \ + { f, -e, -a, g, -d, -b, h, -c }, \ + { d, -h, e, -a, -c, g, -f, b }, \ + { b, -d, f, -h, g, -e, c, -a }, \ +} + +#define DEFINE_DST7_P16_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \ +{ \ + { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p }, \ + { c, f, i, l, o, o, l, i, f, c, 0, -c, -f, -i, -l, -o }, \ + { e, j, o, m, h, c, -b, -g, -l, -p, -k, -f, -a, d, i, n }, \ + { g, n, l, e, -b, -i, -p, -j, -c, d, k, o, h, a, -f, -m }, \ + { i, o, f, -c, -l, -l, -c, f, o, i, 0, -i, -o, -f, c, l }, \ + { k, k, 0, -k, -k, 0, k, k, 0, -k, -k, 0, k, k, 0, -k }, \ + { m, g, -f, -n, -a, l, h, -e, -o, -b, k, i, -d, -p, -c, j }, \ + { o, c, -l, -f, i, i, -f, -l, c, o, 0, -o, -c, l, f, -i }, \ + { p, -a, -o, b, n, -c, -m, d, l, -e, -k, f, j, -g, -i, h }, \ + { n, -e, -i, j, d, -o, a, m, -f, -h, k, c, -p, b, l, -g }, \ + { l, -i, -c, o, -f, -f, o, -c, -i, l, 0, -l, i, c, -o, f }, \ + { j, -m, c, g, -p, f, d, -n, i, a, -k, l, -b, -h, o, -e }, \ + { h, -p, i, -a, -g, o, -j, b, f, -n, k, -c, -e, m, -l, d }, \ + { f, -l, o, -i, c, c, -i, o, -l, f, 0, -f, l, -o, i, -c }, \ + { d, -h, l, -p, m, -i, e, -a, -c, g, -k, o, -n, j, -f, b }, \ + { b, -d, f, -h, j, -l, n, -p, o, -m, k, -i, g, -e, c, -a }, \ +} + +#define DEFINE_DST7_P32_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F) \ +{ \ + { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F }, \ + { c, f, i, l, o, r, u, x, A, D, F, C, z, w, t, q, n, k, h, e, b, -a, -d, -g, -j, -m, -p, -s, -v, -y, -B, -E }, \ + { e, j, o, t, y, D, D, y, t, o, j, e, 0, -e, -j, -o, -t, -y, -D, -D, -y, -t, -o, -j, -e, 0, e, j, o, t, y, D }, \ + { g, n, u, B, D, w, p, i, b, -e, -l, -s, -z, -F, -y, -r, -k, -d, c, j, q, x, E, A, t, m, f, -a, -h, -o, -v, -C }, \ + { i, r, A, C, t, k, b, -g, -p, -y, -E, -v, -m, -d, e, n, w, F, x, o, f, -c, -l, -u, -D, -z, -q, -h, a, j, s, B }, \ + { k, v, F, u, j, -a, -l, -w, -E, -t, -i, b, m, x, D, s, h, -c, -n, -y, -C, -r, -g, d, o, z, B, q, f, -e, -p, -A }, \ + { m, z, z, m, 0, -m, -z, -z, -m, 0, m, z, z, m, 0, -m, -z, -z, -m, 0, m, z, z, m, 0, -m, -z, -z, -m, 0, m, z }, \ + { o, D, t, e, -j, -y, -y, -j, e, t, D, o, 0, -o, -D, -t, -e, j, y, y, j, -e, -t, -D, -o, 0, o, D, t, e, -j, -y }, \ + { q, E, n, -c, -t, -B, -k, f, w, y, h, -i, -z, -v, -e, l, C, s, b, -o, -F, -p, a, r, D, m, -d, -u, -A, -j, g, x }, \ + { s, A, h, -k, -D, -p, c, v, x, e, -n, -F, -m, f, y, u, b, -q, -C, -j, i, B, r, -a, -t, -z, -g, l, E, o, -d, -w }, \ + { u, w, b, -s, -y, -d, q, A, f, -o, -C, -h, m, E, j, -k, -F, -l, i, D, n, -g, -B, -p, e, z, r, -c, -x, -t, a, v }, \ + { w, s, -d, -A, -o, h, E, k, -l, -D, -g, p, z, c, -t, -v, a, x, r, -e, -B, -n, i, F, j, -m, -C, -f, q, y, b, -u }, \ + { y, o, -j, -D, -e, t, t, -e, -D, -j, o, y, 0, -y, -o, j, D, e, -t, -t, e, D, j, -o, -y, 0, y, o, -j, -D, -e, t }, \ + { A, k, -p, -v, e, F, f, -u, -q, j, B, a, -z, -l, o, w, -d, -E, -g, t, r, -i, -C, -b, y, m, -n, -x, c, D, h, -s }, \ + { C, g, -v, -n, o, u, -h, -B, a, D, f, -w, -m, p, t, -i, -A, b, E, e, -x, -l, q, s, -j, -z, c, F, d, -y, -k, r }, \ + { E, c, -B, -f, y, i, -v, -l, s, o, -p, -r, m, u, -j, -x, g, A, -d, -D, a, F, b, -C, -e, z, h, -w, -k, t, n, -q }, \ + { F, -a, -E, b, D, -c, -C, d, B, -e, -A, f, z, -g, -y, h, x, -i, -w, j, v, -k, -u, l, t, -m, -s, n, r, -o, -q, p }, \ + { D, -e, -y, j, t, -o, -o, t, j, -y, -e, D, 0, -D, e, y, -j, -t, o, o, -t, -j, y, e, -D, 0, D, -e, -y, j, t, -o }, \ + { B, -i, -s, r, j, -A, -a, C, -h, -t, q, k, -z, -b, D, -g, -u, p, l, -y, -c, E, -f, -v, o, m, -x, -d, F, -e, -w, n }, \ + { z, -m, -m, z, 0, -z, m, m, -z, 0, z, -m, -m, z, 0, -z, m, m, -z, 0, z, -m, -m, z, 0, -z, m, m, -z, 0, z, -m }, \ + { x, -q, -g, E, -j, -n, A, -c, -u, t, d, -B, m, k, -D, f, r, -w, -a, y, -p, -h, F, -i, -o, z, -b, -v, s, e, -C, l }, \ + { v, -u, -a, w, -t, -b, x, -s, -c, y, -r, -d, z, -q, -e, A, -p, -f, B, -o, -g, C, -n, -h, D, -m, -i, E, -l, -j, F, -k }, \ + { t, -y, e, o, -D, j, j, -D, o, e, -y, t, 0, -t, y, -e, -o, D, -j, -j, D, -o, -e, y, -t, 0, t, -y, e, o, -D, j }, \ + { r, -C, k, g, -y, v, -d, -n, F, -o, -c, u, -z, h, j, -B, s, -a, -q, D, -l, -f, x, -w, e, m, -E, p, b, -t, A, -i }, \ + { p, -F, q, -a, -o, E, -r, b, n, -D, s, -c, -m, C, -t, d, l, -B, u, -e, -k, A, -v, f, j, -z, w, -g, -i, y, -x, h }, \ + { n, -B, w, -i, -e, s, -F, r, -d, -j, x, -A, m, a, -o, C, -v, h, f, -t, E, -q, c, k, -y, z, -l, -b, p, -D, u, -g }, \ + { l, -x, C, -q, e, g, -s, E, -v, j, b, -n, z, -A, o, -c, -i, u, -F, t, -h, -d, p, -B, y, -m, a, k, -w, D, -r, f }, \ + { j, -t, D, -y, o, -e, -e, o, -y, D, -t, j, 0, -j, t, -D, y, -o, e, e, -o, y, -D, t, -j, 0, j, -t, D, -y, o, -e }, \ + { h, -p, x, -F, y, -q, i, -a, -g, o, -w, E, -z, r, -j, b, f, -n, v, -D, A, -s, k, -c, -e, m, -u, C, -B, t, -l, d }, \ + { f, -l, r, -x, D, -C, w, -q, k, -e, -a, g, -m, s, -y, E, -B, v, -p, j, -d, -b, h, -n, t, -z, F, -A, u, -o, i, -c }, \ + { d, -h, l, -p, t, -x, B, -F, C, -y, u, -q, m, -i, e, -a, -c, g, -k, o, -s, w, -A, E, -D, z, -v, r, -n, j, -f, b }, \ + { b, -d, f, -h, j, -l, n, -p, r, -t, v, -x, z, -B, D, -F, E, -C, A, -y, w, -u, s, -q, o, -m, k, -i, g, -e, c, -a }, \ +} + +const int8_t ff_vvc_dst7_4x4[4][4] = DEFINE_DST7_P4_MATRIX (29, 55, 74, 84); +const int8_t ff_vvc_dst7_8x8[8][8] = DEFINE_DST7_P8_MATRIX (17, 32, 46, 60, 71, 78, 85, 86); +const int8_t ff_vvc_dst7_16x16[16][16] = DEFINE_DST7_P16_MATRIX( 8, 17, 25, 33, 40, 48, 55, 62, 68, 73, 77, 81, 85, 87, 88, 88); +const int8_t ff_vvc_dst7_32x32[32][32] = DEFINE_DST7_P32_MATRIX( 4, 9, 13, 17, 21, 26, 30, 34, 38, 42, 46, 50, 53, 56, 60, 63, 66, 68, 72, 74, 77, 78, 80, 82, 84, 85, 86, 87, 88, 89, 90, 90); + +const int8_t ff_vvc_lfnst_8x8[4][2][16][48] = { + { //0 + { + { -117, 28, 18, 2, 4, 1, 2, 1, 32, -18, -2, 0, -1, 0, 0, 0, 14, -1, -3, 0, -1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 }, + { -29, -91, 47, 1, 9, 0, 3, 0, -54, 26, -8, 3, 0, 1, 0, 0, 33, 5, -9, -1, -2, 0, -1, 0, -3, 3, 0, 0, 0, 0, 0, 0, 7, 2, -2, 0, -1, 1, 0, 0, 2, 1, -1, 0, 0, 0, 0, 0 }, + { -10, 62, -11, -8, -2, -2, -1, -1, -95, 3, 32, 0, 4, 0, 2, 0, 32, -30, -4, 4, -1, 1, 0, 0, 6, 2, -5, 0, 0, 0, 0, 0, 6, -3, 0, 0, 2, 0, -1, 0, 2, -1, 0, 0, 1, 0, 0, 0 }, + { -15, 15, -10, -2, 1, 0, 1, 0, 10, 112, -20, -17, -4, -4, -1, -2, -20, -26, 31, 1, 0, 0, 0, 0, 2, -16, -1, 6, 0, 1, 0, 0, 1, -4, 0, 0, 0, -3, 0, 1, 0, -1, 0, 0, 0, -2, 0, 0 }, + { 32, 39, 92, -44, 4, -10, 1, -4, 26, 12, -15, 13, -5, 2, -2, 0, 29, -16, -22, 8, 0, 1, 0, 1, -20, 6, 4, -3, 1, 0, 0, 0, 1, -4, -3, 2, -4, 1, 0, 0, 1, -1, -2, 1, -2, 0, 0, 0 }, + { -10, 1, 50, -15, 2, -3, 1, -1, -28, -15, 14, 6, 1, 1, 1, 0, -99, -4, 9, 5, 5, 2, 2, 1, 44, -10, -11, 1, -2, 0, -1, 0, -5, 4, -3, 0, 8, -1, -2, 0, -2, 1, -1, 0, 4, 0, -1, 0 }, + { 1, -33, -11, -14, 7, -2, 2, 0, 29, -12, 37, -7, -4, 0, -1, 0, 6, -99, 3, 26, -1, 5, 0, 2, 14, 30, -27, -2, 1, -1, 0, -1, -6, 6, 6, -3, 1, 3, -3, 0, -1, 1, 1, 0, 0, 1, -1, 0 }, + { 0, 6, -6, 21, -4, 2, 0, 0, -20, -24, -104, 30, 5, 5, 1, 2, -7, -46, 10, -14, 7, 0, 1, 0, 9, 21, 7, -6, -2, -1, 0, -1, 2, 2, 5, -2, 0, 3, 4, -1, 0, 0, 1, 0, 0, 1, 2, -1 }, + { -13, -13, -37, -101, 29, -11, 8, -3, -12, -15, -20, 2, -11, 5, -2, 1, -12, 10, 26, 12, -6, 0, -1, 0, -32, -2, 11, 3, 3, -1, 1, 0, 11, -5, -1, 6, -4, 2, 1, 0, 3, -1, 1, 2, -1, 0, 0, 0 }, + { 6, 1, -14, -36, 9, -3, 2, 0, 10, 9, -18, -1, -3, 1, 0, 0, 38, 26, -13, -1, -5, -1, -1, 0, 102, 3, -14, -1, -5, -1, -2, 0, -29, 10, 10, 0, 10, -4, -1, 1, -7, 1, 2, 1, 2, -1, 0, 0 }, + { -12, -2, -26, -12, -9, 2, -1, 1, -3, 30, 4, 34, -4, 0, -1, 0, -30, 3, -92, 14, 19, 0, 3, 0, -11, 34, 21, -33, 1, -2, 0, -1, -9, -4, 18, 3, 2, 0, 0, -2, -1, -1, 3, 0, 0, 0, 0, -1 }, + { 0, -3, 0, -4, -15, 6, -3, 1, -7, -15, -28, -86, 19, -5, 4, -1, -5, -17, -41, 42, -6, 2, -1, 1, -1, -40, 37, 13, -4, 2, -1, 1, -10, 13, -1, -4, 4, -4, 3, 4, -2, 2, -1, -1, 1, -1, 1, 2 }, + { -1, 9, 13, 5, 14, -2, 2, -1, -8, 3, -4, -62, 4, 1, 1, 0, -12, 23, 16, -11, -17, 0, -1, 0, -11, 97, -3, -3, 0, -6, 0, -2, -21, -5, 23, 0, 2, -2, -1, 6, -3, -3, 1, 0, 0, 0, 0, 2 }, + { 6, 2, -3, 2, 10, -1, 2, 0, 8, 3, -1, -20, 0, 1, 0, 0, -4, 4, -16, 0, -2, 0, 1, 0, 34, 23, 6, -7, -4, -2, -1, 0, 108, -5, -30, 6, -27, 10, 7, -2, 11, -3, -1, 1, -4, 1, 0, 1 }, + { 6, 9, -2, 35, 110, -22, 11, -4, -2, 0, -3, 1, -18, 12, -3, 2, -5, -4, -22, 8, -25, 3, 0, 0, -3, -21, 2, -3, 9, -2, 1, 0, -7, 1, 3, -5, 3, 0, -1, 0, 0, 1, 0, -1, 1, 0, 0, 0 }, + { -1, 7, -2, 9, -11, 5, -1, 1, -7, 2, -22, 4, -13, 0, -1, 0, 0, 28, 0, 76, 4, -6, 0, -2, -13, 5, -76, -4, 33, -1, 3, 0, 9, 18, -3, -35, -4, -1, 6, 1, 1, 2, 0, -3, -1, 0, 2, 0 }, + }, + { + { -108, 48, 9, 1, 1, 1, 0, 0, 44, -6, -9, -1, -1, 0, -1, 0, 9, -9, -1, 1, 0, 0, 0, 0, 3, -1, 1, 0, 0, 0, 0, 0, 1, -1, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0 }, + { 55, 66, -37, -5, -6, -1, -2, 0, 67, -30, -20, 4, -2, 0, -1, 0, -31, -19, 14, 4, 1, 1, 1, 0, -6, 3, 5, -2, 0, 0, 0, 0, -7, -1, 1, 0, -1, 1, 1, 0, -2, -1, 1, 0, 0, 0, 0, 0 }, + { 2, 86, -21, -13, -4, -2, -1, -1, -88, 5, 6, 4, 5, 1, 1, 0, 14, -5, 0, 3, 0, 0, 0, 0, 10, -5, -2, 0, -1, 0, 0, 0, 6, -5, 0, 1, 2, -1, 0, 0, 1, -1, 0, 0, 1, 0, 0, 0 }, + { -24, -21, -38, 19, 0, 4, -1, 2, -23, -89, 31, 20, 2, 3, 1, 1, -30, 26, 36, -8, -2, -2, 0, -1, 14, 18, -7, -9, -1, -1, 0, 0, 1, 3, -2, -1, 3, 2, -2, -1, 0, 1, 0, 0, 1, 1, -1, 0 }, + { 9, 20, 98, -26, -3, -5, 0, -2, -9, -26, 15, -16, 2, 0, 1, 0, -61, -3, -2, 3, 7, 1, 1, 0, 12, 16, -6, -1, 0, -1, 0, 0, 2, 0, -8, 1, 3, 1, -1, 1, 0, -1, -2, 0, 1, 0, -1, 0 }, + { -21, -7, -37, 10, 2, 2, -1, 1, -10, 69, -5, -7, -2, -2, 0, -1, -93, 2, 19, 0, 3, 0, 2, 0, 17, 4, 0, 0, -1, 0, 0, 0, 5, -4, -2, 0, 4, -2, 0, 1, 0, 0, 0, 0, 2, -1, 0, 0 }, + { -10, -25, 4, -17, 8, -2, 2, -1, -27, -17, -71, 25, 8, 2, 1, 1, -4, -66, 28, 36, -5, 3, 0, 1, -10, 20, 33, -13, -8, 0, 0, -1, 3, 6, -3, -7, -1, 3, 3, -1, 1, 0, -1, 0, 0, 1, 1, -1 }, + { 2, 5, 10, 64, -9, 4, -3, 1, -4, 8, 62, 3, -17, 1, -2, 0, -3, -75, 5, -14, 1, 4, 0, 1, -36, 3, 18, -4, 4, 0, 1, 0, 1, 14, -2, -8, -2, 1, -3, 0, 2, 2, -1, -2, 0, 1, -1, 0 }, + { -11, -15, -28, -97, 6, -1, 4, -1, 7, 3, 57, -15, 10, -2, 0, -1, -1, -27, 13, 6, 1, -1, 0, 0, -34, -6, 0, 3, 4, 1, 2, 0, -2, 8, 1, 5, -2, 0, -3, 1, 1, 1, 0, 2, -1, 0, -1, 0 }, + { 9, 13, 24, -6, 7, -2, 1, -1, 16, 39, 20, 47, -2, -2, -2, 0, 28, 23, 76, -5, -25, -3, -3, -1, 6, 36, -7, -39, -4, -1, 0, -1, 2, -4, -18, -3, -1, -1, -2, -2, 1, -2, -2, 0, 0, 0, -1, -1 }, + { -7, 11, 12, 7, 2, -1, 0, -1, -14, -1, -24, 11, 2, 0, 0, 0, -20, 48, 11, -13, -5, -2, 0, -1, -105, -19, 17, 0, 6, 2, 3, 0, -14, 8, 8, 2, 1, 2, -1, -2, 3, 0, -1, 0, 0, 0, 0, 0 }, + { 0, 0, 7, -6, 23, -3, 3, -1, 5, 1, 18, 96, 13, -9, -1, -1, -21, -7, -42, 14, -24, -3, 0, 0, 11, -47, -7, 3, -5, 9, 1, 2, 0, -1, 19, -1, 1, 0, -1, -6, -1, 1, 2, 0, 1, 0, 0, -2 }, + { -2, -6, -1, -10, 0, 1, 1, 0, -7, -2, -28, 20, -15, 4, -3, 1, -2, -32, -2, -66, 3, 7, 1, 2, -11, 13, -70, 5, 43, -2, 3, 0, 8, -14, -3, 43, -1, 2, 7, -1, 1, -2, 1, 3, -1, 1, 1, 0 }, + { -1, 6, -16, 0, 24, -3, 1, -1, 2, 6, 6, 16, 18, -7, 1, -1, -3, 11, -63, 9, 4, -5, 2, -1, -22, 94, -4, -6, -4, -4, 1, -2, 10, 23, -19, -5, 0, -6, -4, 6, 3, -2, 1, 1, 0, -1, 0, 0 }, + { -5, -6, -3, -19, -104, 18, -4, 3, 0, 6, 0, 35, -41, 20, -2, 2, -2, 10, -18, 16, 21, 3, -2, 0, -2, 11, 6, -10, 6, -3, -1, 0, -1, 5, -1, -6, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, -1 }, + { -1, -2, 0, 23, -9, 0, -2, 0, 1, 1, 8, -1, 29, 1, 1, 0, 3, -6, 13, 76, 30, -11, -1, -2, -26, -8, -69, 7, -9, -7, 3, -1, -10, -34, -25, 13, -1, 0, 11, 5, 1, -1, 1, -2, 0, 0, 2, 0 }, + } + }, + { //1 + { + { 110, -49, -3, -4, -1, -1, 0, -1, -38, -1, 10, 0, 2, 0, 1, 0, -9, 13, 1, -2, 0, 0, 0, 0, -4, 2, -3, 0, 0, 0, 0, 0, -2, 2, 0, 1, -1, 1, 0, 0, -1, 1, 0, 0, -1, 0, 0, 0 }, + { -43, -19, 17, -1, 3, 0, 1, 0, -98, 46, 14, -1, 2, 0, 1, 0, 26, 26, -15, -3, -2, -1, -1, 0, 11, -7, -9, 2, 0, 0, 0, 0, 9, -3, -1, 2, 3, -3, 0, 0, 4, -1, 0, 0, 2, -1, 0, 0 }, + { -19, 17, -7, 3, -2, 1, -1, 0, -32, -59, 29, 3, 4, 0, 2, 0, -72, 43, 34, -9, 3, -2, 1, -1, 13, 36, -18, -10, 0, -2, 0, -1, 3, 0, -12, 3, 6, 1, -3, 2, 1, -1, -2, 0, 3, 1, -1, 1 }, + { -35, -103, 39, 1, 7, 0, 2, 0, 38, -13, 25, -6, 1, -1, 0, 0, -1, 7, 6, -7, 1, -1, 0, 0, -13, 14, 2, -4, 2, -1, 0, 0, -2, 11, -6, -2, -2, 4, -3, 0, 0, 3, -2, 0, -1, 1, -1, 0 }, + { 9, 5, -6, -1, -1, 0, -1, 0, 42, 4, 21, -11, 1, -3, 1, -1, 21, 70, -32, -21, 0, -4, -1, -1, 34, -26, -57, 11, 4, 2, 0, 1, -4, -32, 5, 24, 1, -6, 12, 4, -3, -2, 4, -2, 0, -1, 0, 0 }, + { -5, -5, -28, 9, -3, 2, -1, 1, -20, -78, 22, 16, 1, 3, 0, 1, 80, -6, 25, -5, -4, -1, -1, 0, 6, -24, 7, -9, 0, 0, 0, 0, -7, 3, 13, -4, -3, 5, 1, -5, -2, 3, 1, -2, -1, 2, -1, -2 }, + { 14, 17, 27, -12, 1, -3, 1, -1, 8, 19, -13, 4, -2, 1, -1, 0, 48, -1, 48, -15, -4, -2, -1, -1, 1, 60, -28, -42, 5, -6, 1, -2, 11, -11, -51, 11, -2, -10, -2, 13, 2, -6, -4, 4, -2, -3, 2, 2 }, + { 7, 35, 17, -4, -1, 0, 0, 0, 3, 8, 54, -17, 1, -2, 1, -1, 10, 14, -11, -34, 4, -4, 1, -1, -80, -7, -6, 2, 15, 0, 3, 0, -16, 46, 1, 3, 2, 7, -24, 0, 2, -2, -5, 8, 1, -1, -2, 2 }, + { -13, -27, -101, 24, -8, 6, -3, 2, 11, 43, 6, 28, -6, 3, -1, 1, -3, 14, 21, -12, -7, -2, -1, -1, -23, 10, -4, -12, 3, 0, 1, 0, 2, 9, -10, 0, 1, -5, -4, 4, 2, -2, 2, 2, 0, -2, 1, 0 }, + { -11, -13, -3, -10, 3, -1, 1, 0, -19, -19, -37, 8, 4, 2, 0, 1, -12, -30, 3, -9, 5, 0, 1, 0, -56, -9, -47, 8, 21, 1, 4, 1, -11, -30, 10, 59, -2, 8, 41, 8, 2, 5, 6, -7, -1, 3, 5, -2 }, + { -4, -10, -24, -11, 3, -2, 0, -1, -6, -37, -45, -17, 8, -2, 2, -1, 17, 14, -58, 14, 15, 0, 2, 0, -10, 34, -7, 28, 4, -1, 1, 0, 23, 34, -31, 4, 10, -22, -30, 22, 4, -15, 9, 20, 2, -5, 9, 4 }, + { -2, 1, 13, -17, 3, -5, 1, -2, 3, 0, -55, 22, 6, 1, 1, 0, 8, 74, 21, 40, -14, 0, -2, 0, -36, -8, 11, -13, -23, 1, -3, 0, -36, 6, 16, -14, 2, 19, -4, -12, -1, 0, -7, -3, 0, 2, -2, -1 }, + { 3, 1, 5, -15, 1, -2, 1, -1, 7, 4, -7, 29, -1, 2, -1, 1, 8, 3, 12, -14, -9, -1, -1, 0, 4, 29, -15, 31, 10, 4, 1, 1, 61, 22, 55, 14, 13, 3, -9, -65, 1, -11, -21, -7, 0, 0, -1, 3 }, + { -4, -8, -1, -50, 6, -4, 2, -2, -1, 5, -22, 20, 6, 1, 0, 0, -16, -15, 18, -29, -11, 2, -2, 1, 40, -45, -19, -22, 31, 2, 4, 1, -25, 41, 0, 12, 9, 7, -42, 12, -3, -14, 2, 28, 5, 1, 6, 2 }, + { 5, -1, 26, 102, -13, 12, -4, 4, -4, -2, -40, -7, -23, 3, -5, 1, -1, 5, 8, -23, 7, 2, 1, 1, 10, -11, -13, -3, 12, -3, 2, 0, -9, 23, 4, 9, 14, 9, -14, -4, 0, -12, -7, 6, 3, 0, 6, 3 }, + { -5, -6, -27, -22, -12, 0, -3, 0, -5, 8, -20, -83, 0, 0, 0, 0, 9, 7, 24, -20, 41, 3, 6, 1, 15, 20, 12, 11, 17, -9, 1, -2, -26, -1, 18, -1, -12, 32, 3, -18, -5, 10, -25, -5, -2, 1, -8, 10 }, + }, + { + { 80, -49, 6, -4, 1, -1, 1, -1, -72, 36, 4, 0, 1, 0, 0, 0, 26, 0, -12, 2, -2, 1, -1, 0, -7, -9, 6, 1, 0, 0, 0, 0, 3, 5, -1, -2, -2, -2, -1, 1, 1, 1, 0, 0, -1, -1, 0, 0 }, + { -72, -6, 17, 0, 3, 0, 1, 0, -23, 58, -21, 2, -3, 1, -1, 0, 55, -46, -1, 6, -2, 1, -1, 0, -22, 7, 17, -7, 2, -1, 1, 0, 9, 5, -12, 1, -3, -4, 4, 2, 4, 1, -2, -1, -1, -1, 1, 0 }, + { -50, 19, -15, 4, -1, 1, -1, 1, -58, -2, 30, -3, 4, -1, 2, 0, 6, 57, -34, 0, -2, 0, -1, 0, 34, -48, -2, 14, -4, 3, -1, 1, -10, 7, 21, -10, 6, 1, -11, 0, -1, -1, 4, 2, 3, 0, -2, -1 }, + { -33, -43, 28, -7, 4, -2, 2, -1, -38, 11, -8, 4, 1, 1, 0, 0, -55, 24, 26, -5, 2, -1, 1, 0, 15, 46, -40, -1, -1, 0, -1, 0, 17, -38, 1, 17, -3, 11, 15, -11, 3, -1, -10, 1, 0, 1, 3, 2 }, + { 10, 66, -21, -3, -3, 0, -1, 0, -53, -41, -2, 16, -1, 4, -1, 1, 36, -5, 41, -20, 3, -3, 1, -1, -30, 26, -32, -3, 7, -2, 2, -1, 15, -8, 1, 17, -1, -2, 4, -8, 2, 0, -1, 3, 0, 0, 0, -1 }, + { 18, 14, 13, -9, 2, -2, 1, -1, 34, 32, -31, 12, -5, 2, -2, 1, 40, 4, -4, -9, -3, -2, -1, -1, 27, -31, -43, 19, -2, 3, -1, 1, 7, -49, 52, 10, -11, 22, 7, -26, -1, -6, -9, 6, -2, 2, 4, -2 }, + { 21, 66, -1, 9, -4, 2, -1, 1, -21, 41, -30, -10, 0, -2, 0, -1, -35, -17, -3, 26, -6, 5, -2, 2, 56, 3, 18, -25, -1, -2, -1, -1, -15, -13, -27, 9, 9, -6, 20, 5, -3, 2, -6, -9, 3, -3, 1, 5 }, + { 1, -6, -24, 17, -5, 3, -2, 1, 24, 10, 39, -21, 5, -4, 2, -1, 33, 32, -30, 4, -3, -1, -1, 0, -4, 13, -16, -10, 0, -1, 0, 0, 24, -26, -37, 33, 5, -32, 55, -5, -7, 22, -14, -22, 1, -9, -3, 13 }, + { 9, 33, -24, 1, 4, 0, 1, 0, 6, 50, 26, 1, -10, 0, -2, 0, -27, 1, -28, -21, 16, -5, 3, -2, -23, 36, -2, 40, -17, 4, -3, 1, 43, -13, 4, -41, -19, -2, -24, 17, 11, -4, 8, 4, -3, -3, -3, -3 }, + { -7, -9, -32, 14, -3, 3, -1, 1, -23, -28, 0, -5, -1, 0, 0, 0, -36, -59, -24, 14, 4, 2, 1, 1, -23, -26, 23, 26, -3, 5, 0, 2, 10, -26, 38, 7, -12, 11, 42, -22, -5, 20, -14, -15, -1, -2, 1, 6 }, + { 6, 30, 69, -18, 5, -4, 3, -1, -3, -11, -34, -16, 9, -4, 2, -1, -16, 35, -35, 30, -9, 3, -2, 1, -57, -13, 6, 4, -5, 5, -1, 1, 28, 10, 4, 7, 0, -15, 7, -10, -1, 7, -2, 2, 1, -3, 0, 0 }, + { 1, -8, 24, -3, 7, -2, 2, -1, -6, -51, -6, -4, -5, 0, -1, 0, 38, -1, 0, 25, 6, 2, 1, 1, 47, 20, 35, 1, -27, 1, -5, 0, 37, -37, -9, -47, -28, 5, 0, 18, 8, 6, 0, -8, -4, -3, -3, 1 }, + { 4, 10, 4, 17, -9, 4, -2, 1, 5, 14, 32, -15, 9, -3, 2, -1, 7, 13, 19, 15, -8, 1, -1, 0, 3, 25, 30, -18, 1, -2, 0, -1, 11, 24, 22, -11, -3, 37, -13, -58, -5, 12, -63, 26, 9, -15, 11, 8 }, + { -3, -9, -23, 10, -10, 3, -3, 1, -5, -14, -16, -27, 13, -5, 2, -1, -1, -13, -30, 11, -5, 2, -1, 0, -5, -8, -22, -16, 10, 0, 1, 0, 0, -29, -27, 6, -27, -10, -30, 9, -3, -10, -7, 77, 9, -13, 45, -8 }, + { 2, 11, 22, 2, 9, -2, 2, 0, -6, -7, 20, -32, -3, -4, 0, -1, 13, -5, -28, 6, 18, -4, 3, -1, -26, 27, -14, 6, -20, 0, -2, 0, -76, -26, -4, -7, 12, 51, 5, 24, 7, -17, -16, -12, -5, 4, 2, 13 }, + { 2, -3, 8, 14, -5, 3, -1, 1, -2, -11, 5, -18, 8, -3, 2, -1, 12, -23, -19, 22, 2, 0, 1, 0, 23, 41, -7, 35, -10, 4, -1, 1, 5, 7, 23, 5, 69, -38, -8, -32, -15, -31, 24, 11, 2, 18, 11, -15 }, + } + }, + { //2 + { + { -121, 33, 4, 4, 1, 2, 0, 1, -1, -1, 1, 0, 0, 0, 0, 0, 24, -5, -1, -1, 0, 0, 0, 0, 5, -1, 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 2, -1, 0, 0, 2, -1, 0, 0, 1, 0, 0, 0 }, + { 0, -2, 0, 0, 0, 0, 0, 0, 121, -23, -7, -3, -2, -1, -1, 0, 17, 1, -2, 0, 0, 0, 0, 0, -27, 4, 2, 0, 0, 0, 0, 0, -12, 2, 1, 0, -5, 1, 0, 0, -1, 0, 0, 0, -2, 0, 0, 0 }, + { -20, 19, -5, 2, -1, 1, 0, 0, 16, 3, -2, 0, 0, 0, 0, 0, -120, 14, 8, 1, 3, 1, 1, 0, -18, -2, 3, 0, 1, 0, 0, 0, 17, -3, -1, 0, 6, -1, -1, 0, 2, 0, 0, 0, 2, 0, 0, 0 }, + { 32, 108, -43, 10, -9, 3, -3, 1, 4, 19, -7, 1, -1, 0, 0, 0, 11, -30, 9, -2, 1, -1, 0, 0, 0, -8, 2, 0, 0, 0, 0, 0, -7, -1, 2, 0, -3, -1, 1, 0, -2, -2, 1, 0, 0, 0, 0, 0 }, + { -3, 0, -1, 0, 0, 0, 0, 0, -29, 11, -2, 1, 0, 0, 0, 0, 12, 7, -1, 0, 0, 0, 0, 0, -117, 12, 9, 1, 3, 0, 1, 0, -32, -3, 3, 0, 12, -2, -1, 0, 7, 0, 0, 0, 1, 0, 0, 0 }, + { -4, -12, -3, 1, -1, 0, 0, 0, 19, 105, -31, 7, -6, 1, -2, 0, 9, 46, -6, 0, 0, 0, 0, 0, 8, -29, 9, -3, 1, 0, 0, 0, -3, -19, 3, 0, -4, -6, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0 }, + { 7, 1, 2, 0, 0, 0, 0, 0, 4, 3, -2, 0, 0, 0, 0, 0, 22, -8, 1, -1, 0, 0, 0, 0, -28, -9, 4, 0, 1, 0, 0, 0, 117, -10, -8, 0, 32, 1, -4, 0, 3, 1, -1, 0, -3, 1, 0, 0 }, + { -8, -31, 14, -4, 3, -1, 1, 0, 9, 43, 0, 1, -1, 0, 0, 0, -13, -105, 17, -2, 2, 0, 0, 0, -8, -25, -3, 0, 0, 0, 0, 0, -7, 32, -5, 1, -1, 4, 0, 0, 2, -1, 0, 0, 1, 0, -1, 0 }, + { -15, -43, -100, 23, -12, 6, -4, 2, -6, -17, -48, 10, -5, 2, -1, 1, 1, -5, 19, -6, 3, -1, 1, 0, 2, 7, 15, -3, 1, -1, 0, 0, 4, 10, 5, -1, 0, 3, 1, 0, -2, 1, 2, 0, -1, 1, 1, 0 }, + { -3, 1, 2, 0, 0, 0, 0, 0, -6, 3, 1, 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0, -20, 8, -2, 0, 0, 0, 0, 0, 30, 13, -3, 0, -116, 6, 10, 0, -35, -5, 4, 0, -3, -1, 0, 0 }, + { -1, -6, -3, 2, -1, 0, 0, 0, -6, -35, 9, 0, 2, 0, 0, 0, 1, -6, 11, -2, 2, 0, 1, 0, -9, -100, 17, -1, 1, 0, 0, 0, -10, -63, 1, 2, -17, 3, -4, 0, -1, 9, -1, 0, 3, 4, -1, 0 }, + { -5, -14, -48, 2, -5, 1, -2, 0, 10, 24, 99, -17, 10, -4, 3, -1, 4, 14, 32, 0, 2, 0, 1, 0, -4, 0, -39, 6, -4, 1, -1, 0, 2, -3, -4, 0, 2, -2, -2, 0, 0, 0, -1, 0, 0, -1, -1, 0 }, + { -2, 0, 2, 0, 0, 0, 0, 0, -2, 0, 1, 0, 0, 0, 0, 0, -1, -1, 1, -1, 0, 0, 0, 0, -1, -4, 2, 0, 0, 0, 0, 0, -8, -2, -1, 1, 30, 4, -4, 1, -102, 4, 8, -1, -69, -2, 6, -1 }, + { -2, -10, -4, 0, 0, 0, 0, 0, 3, 11, -1, -1, 0, 0, 0, 0, -6, -40, -15, 6, -2, 1, 0, 0, 5, 57, -6, 2, 0, 0, 0, 0, 1, -95, 18, -6, -10, -34, -2, 0, -4, 17, -2, 0, 0, 2, 1, 0 }, + { -2, -3, -25, -2, -3, 0, -1, 0, -1, -3, -1, 4, -2, 2, 0, 1, -7, -8, -97, 17, -9, 3, -3, 1, -8, -26, -61, -1, -3, -1, -1, -1, 2, 10, 24, -7, 5, 9, 19, -1, 0, 1, 4, 0, -2, 0, 1, 0 }, + { 4, -4, 28, 103, -42, 24, -9, 7, 1, 2, 4, 0, 3, -1, 0, 0, -1, 0, -9, -42, 17, -9, 3, -2, -1, 1, -14, 6, -4, 2, -1, 0, -1, -2, -4, 4, 0, 3, 1, -1, 0, 2, 0, -2, 2, 0, 0, 0 }, + }, + { + { 87, -41, 3, -4, 1, -1, 0, -1, -73, 28, 2, 1, 1, 1, 0, 0, 30, -5, -6, 1, -1, 0, 0, 0, -8, -3, 3, 0, 0, 0, 0, 0, 3, 2, -1, 0, -2, -1, 0, 0, 1, 1, 0, 0, -1, 0, 0, 0 }, + { -75, 4, 7, 0, 2, 0, 1, 0, -41, 36, -7, 3, -1, 1, 0, 0, 72, -29, -2, 0, -1, 0, -1, 0, -37, 6, 7, -2, 1, 0, 0, 0, 12, 3, -4, 0, -3, -2, 1, 0, 4, 0, 0, 0, -1, 0, 0, 0 }, + { 26, -44, 22, -6, 4, -2, 1, -1, 77, 24, -22, 2, -4, 0, -1, 0, 7, -38, 10, 0, 1, 0, 0, 0, -51, 27, 4, -3, 2, -1, 1, 0, 31, -5, -8, 3, -14, 0, 5, -1, 6, 1, -3, 0, -4, -1, 1, 0 }, + { -39, -68, 37, -7, 6, -2, 2, 0, -9, 56, -21, 1, -2, 0, -1, 0, -45, 4, -3, 6, -1, 2, 0, 1, 49, -13, 3, -3, -1, 0, 0, 0, -19, 2, 0, 0, 5, 1, 1, 0, -2, 0, -1, 0, 1, 0, 0, 0 }, + { 10, -20, 2, 0, 1, 0, 0, 0, 50, -1, 8, -5, 1, -1, 0, 0, 66, 17, -24, 4, -3, 1, -1, 0, 13, -49, 15, 1, 0, 0, 0, 0, -53, 34, 6, -5, 30, -7, -11, 3, -11, -2, 5, 1, 4, 2, -1, -1 }, + { -21, -45, 8, -2, 3, -1, 1, 0, -7, -30, 26, -8, 3, -1, 1, -1, -9, 69, -33, 5, -2, 0, -1, 0, -44, -31, 10, 7, -2, 2, 0, 1, 49, 7, 2, -6, -23, -3, -2, 2, 9, 4, 0, 0, -2, -1, -1, 0 }, + { -4, -2, -55, 28, -8, 5, -3, 2, -2, 37, 43, -19, 1, -2, 1, -1, -47, -34, -27, 5, 4, -1, 1, 0, -39, -2, 27, 4, -2, 1, 0, 0, -11, 32, -8, -7, 27, -12, -6, 6, -13, 0, 4, -3, 3, -1, -2, 1 }, + { 2, 19, 47, -23, 6, -4, 2, -1, -23, -22, -44, 17, -2, 2, -1, 0, -33, 3, 22, -2, -4, 1, -1, 0, -58, -17, 6, -6, 7, -1, 1, 0, -23, 40, -2, 5, 43, -11, -8, -1, -18, -4, 5, 2, 4, 3, 0, -1 }, + { -19, -62, -9, 3, 0, 0, 0, 0, -12, -56, 27, -7, 3, -1, 1, 0, 7, -8, 16, -6, 4, -2, 1, -1, -15, 54, -23, 2, -1, 0, 0, 0, -42, -25, 4, 6, 34, 8, 2, -2, -15, -1, 0, -1, 3, 2, 0, 1 }, + { 1, 9, -5, 0, -1, 0, 0, 0, 0, 22, -1, 2, 0, 1, 0, 0, -13, 17, 0, -2, 0, -1, 0, 0, -46, -10, -10, 4, -1, 1, 0, 0, -80, -27, 20, -4, -66, 23, -2, -2, 20, -3, -2, 3, -14, 2, 3, -1 }, + { 5, 17, -9, 0, -2, 1, 0, 0, 13, 54, -2, 7, -1, 1, 0, 0, 4, 51, -3, -6, -1, -1, 0, 0, -20, 6, -34, 9, -2, 2, -1, 0, 16, -52, 28, 1, 59, 15, -8, -5, -28, -7, 2, 2, 10, 3, 0, -1 }, + { 7, 27, 56, -2, 10, -3, 3, -1, -2, -6, 8, -28, 3, -4, 1, -1, -1, -4, -68, 35, -5, 5, -2, 1, 0, 35, 43, -4, -6, 1, -1, 0, -14, -38, -12, -10, 9, 5, 7, 6, -9, 7, -4, -3, 4, -4, 0, 3 }, + { 0, 0, 19, -4, 3, -2, 2, -1, -3, -13, 10, -4, 1, 0, 0, 0, -6, -37, -18, -5, 2, -2, 1, -1, 6, -6, -7, 25, -6, 4, -1, 1, 16, 10, 55, -24, 15, 46, -52, 1, 35, -43, 10, 12, -23, 13, 5, -8 }, + { -3, 0, -27, -80, 40, -16, 6, -4, 4, 3, 31, 61, -22, 7, -1, 1, -4, -7, -26, -6, -10, 6, -4, 1, 3, 8, 14, -18, 15, -5, 2, -1, -2, -4, -1, 13, 0, 2, -4, -3, 3, -1, 2, 1, -2, 0, -2, -1 }, + { 1, 2, -8, 6, -1, 1, 0, 0, 2, 8, -5, -1, 0, 0, 0, 0, 1, 24, 3, 5, -1, 1, 0, 0, -3, 12, 6, -10, 1, -1, 0, 0, -9, -1, -25, 10, 45, -11, 18, 2, 86, 1, -13, -4, -65, -6, 7, 2 }, + { -4, -18, -57, 8, -8, 1, -3, 0, -5, -20, -69, 7, -6, 2, -2, 1, 1, 4, 0, 33, -7, 5, -2, 1, 0, -9, 53, -22, 3, -1, 0, 0, 4, -27, -2, -9, 5, 36, -13, 5, -7, -17, 1, 2, 4, 6, 4, -1 }, + } + }, + { //3 + { + { -115, 37, 9, 2, 2, 1, 1, 0, 10, -29, 8, 0, 1, 0, 1, 0, 23, -8, -8, 1, -1, 0, 0, 0, 3, 3, -2, -1, 0, 0, 0, 0, 4, 0, 0, -1, 1, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 }, + { 15, 51, -18, 0, -3, 0, -1, 0, -95, 7, 34, -3, 5, -1, 2, 0, 23, -47, 1, 6, 0, 1, 0, 1, 8, 5, -12, 0, -1, 0, 0, 0, 3, -3, 1, -1, 2, 1, -2, 0, 1, -1, 0, 0, 1, 1, -1, 0 }, + { 29, -22, 16, -6, 3, -2, 1, -1, -4, -80, 12, 15, 0, 3, 0, 1, 45, 7, -59, 7, -2, 1, -1, 0, -15, 41, -3, -16, 2, -3, 0, -1, 1, 0, 7, -2, -3, 6, 1, -2, 0, 0, 1, 0, -1, 2, 0, -1 }, + { -36, -98, 25, 5, 4, 1, 2, 1, -59, 11, -17, 1, 1, 1, 0, 0, 6, -13, 7, -3, 0, 0, 0, 0, 14, -4, -14, 3, -1, 0, 0, 0, 2, 8, -3, -5, 2, 0, 0, 0, 0, 3, 0, -1, 1, 0, 0, 0 }, + { -6, 18, 3, -3, -1, 0, 0, 0, -50, -5, -38, 12, 0, 2, 0, 1, 3, 67, -7, -40, 3, -6, 1, -3, -12, -13, 65, -3, -10, 0, -1, 0, 9, -20, -5, 22, -2, 0, 0, -1, 2, -3, -2, 3, -1, 0, 1, 0 }, + { 4, 15, 52, -13, 5, -3, 2, -1, -17, -45, 16, 24, -2, 4, -1, 2, -87, -8, -14, 7, 8, 1, 2, 0, 23, -35, -6, -3, 1, 1, 0, 0, 2, 5, -17, 0, 3, -1, -1, -5, 0, 1, -4, 0, 1, 0, 0, -2 }, + { -20, -7, -43, 4, 0, 1, -1, 1, -7, 35, 0, 12, -4, 1, -1, 0, -51, -2, -57, 5, 15, 0, 4, 0, 7, 39, 5, -55, 1, -7, 1, -3, 1, -10, 41, 2, 4, -3, -2, 3, -1, -2, 7, 1, 1, -1, -1, 0 }, + { 4, 29, 1, 26, -5, 4, -2, 1, -17, -7, -73, 6, 6, 2, 1, 1, -5, 21, -3, 5, -1, -3, 0, -1, -11, 2, -52, -3, 27, -2, 5, 0, 0, 27, 8, -58, 2, -5, 25, 3, 0, 3, 0, -5, 0, -2, 7, 0 }, + { 12, 13, 10, 2, -1, 3, -1, 1, 17, -2, -46, 12, 7, 0, 2, 0, 16, -45, -9, -53, 6, 1, 1, 0, 70, 16, 8, -4, -37, 1, -7, 0, -12, 29, 3, 21, 4, 0, 5, -1, -3, 4, 1, 4, 2, 0, 1, 0 }, + { 5, 20, 90, -17, 4, -3, 2, -1, 6, 66, 8, 28, -7, 3, -1, 1, 29, 5, -19, 12, 9, -1, 1, 0, -10, 14, -1, -13, 7, 0, 1, 0, 0, -6, 13, -4, 0, -4, 1, 5, 0, -1, -1, 1, 0, -1, 0, 0 }, + { -3, -4, -34, -12, 2, -1, -1, 0, 5, 25, 11, 43, -10, 4, -2, 1, 23, 20, -40, 12, 21, -3, 4, -1, 25, -28, -10, 5, 8, 6, 0, 2, -4, 21, -64, -8, -5, 19, 10, -48, 3, -1, 10, -3, 0, 4, 3, -6 }, + { -1, -3, 2, 19, -2, 4, -1, 2, 9, 3, -35, 22, 11, 1, 2, 0, -7, -65, -19, -22, 11, 4, 2, 1, -75, -18, 3, -1, -10, 2, 0, 1, 2, -35, -27, 4, 1, 8, -17, -19, 3, 0, 3, -6, 0, 2, -1, -2 }, + { 10, -4, -6, 12, 5, 1, 1, 0, 11, -9, -12, -2, -7, 0, -1, 0, 33, -10, -4, 18, 18, -4, 4, -1, 28, -72, 1, -49, 15, 2, 2, 1, 56, -23, 22, -1, 4, -1, -15, 26, 6, 4, -10, 0, 0, 2, -3, 2 }, + { 4, 6, 14, 53, -4, 4, 0, 2, 0, -1, -20, -13, 3, 2, -1, 1, -3, 1, -5, 35, -16, -6, -1, -2, 46, 29, 13, 21, 37, -5, 4, -1, -10, -53, -18, 8, 9, 12, -41, -25, -2, 2, 13, -16, 4, 1, -5, 1 }, + { 2, 9, 13, 37, 19, 6, 2, 2, -9, -3, -9, -28, -20, -4, -3, -1, 1, 18, 9, 28, 24, 6, 2, 2, -20, -5, -25, -33, -36, 9, -2, 2, -13, 42, 1, 57, -22, -2, -25, -28, 5, 6, 19, -12, -5, -3, -2, 4 }, + { 3, -3, 12, 84, -12, 8, -2, 3, 6, 13, 50, -1, 45, 1, 7, 0, -2, 18, -22, -37, -13, 14, 0, 3, 1, -12, -3, 2, -15, -8, 1, -1, 19, 14, -4, -12, -4, 5, 17, 8, 2, -4, -4, 4, -2, 2, 1, 0 }, + }, + { + { 109, -26, -8, -3, -2, -1, -1, 0, -50, 28, 2, 1, 0, 0, 0, 0, -18, -8, 6, 0, 1, 0, 1, 0, 6, -2, -3, 0, 0, 0, 0, 0, -3, 2, 1, -1, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0 }, + { -39, 31, -5, 2, -1, 1, 0, 0, -95, 6, 18, 0, 4, 0, 1, 0, 32, -49, 5, 1, 1, 0, 0, 0, 27, -1, -14, 2, -2, 1, -1, 0, 3, 5, -3, -2, 4, 1, -1, -1, 2, 0, 0, 0, 2, 0, 0, 0 }, + { 29, -3, -2, -2, 0, 0, 0, 0, 0, -41, 9, 0, 2, 0, 1, 0, 86, 4, -33, 2, -6, 1, -2, 0, -32, 58, 1, -7, 0, -2, 0, -1, -14, -8, 20, 0, -2, -3, 0, 4, -1, -1, 0, 0, -1, 1, 0, 0 }, + { 18, 96, -23, 2, -5, 1, -2, 0, -10, 6, 10, -2, 1, -1, 1, 0, -14, 26, 2, -4, 1, -1, 0, 0, -43, -9, 35, -2, 4, -1, 1, 0, 14, -40, 1, 10, 2, 1, -10, 1, 2, -4, -1, -1, 0, 0, -1, 0 }, + { -29, -60, 16, -2, 3, -1, 1, 0, -52, 9, -17, 5, -2, 1, -1, 1, 13, 56, -2, -9, 0, -2, 0, -1, -34, -18, 41, 0, 3, 0, 1, 0, 19, -36, -10, 13, 3, 6, -14, -1, 3, 1, -1, -3, 1, 1, -1, -1 }, + { -23, -5, -15, 5, -2, 1, -1, 1, 2, 79, -13, -4, -2, -1, -1, 0, -9, 1, 5, -1, 1, 0, 0, 0, -4, 49, 2, -14, 1, -3, 0, -1, -31, -14, 56, -1, 13, -37, -4, 20, -2, 2, -10, 0, 2, -4, 0, -1 }, + { -7, -3, 12, -3, 3, -1, 1, 0, -31, -62, 8, 7, 0, 2, 0, 1, -75, 9, -45, 5, -1, 1, -1, 0, 14, 35, 0, -23, 2, -5, 1, -2, 1, -8, 32, -1, 7, -12, -4, 10, 0, 2, -6, -1, 2, 0, 0, -2 }, + { 1, -26, 5, 0, 1, 0, 1, 0, 24, -3, 43, -6, 4, -2, 1, -1, -7, -64, 9, 14, 0, 3, 0, 1, -12, -4, 5, 3, -1, 1, 0, 0, 8, -59, -3, 26, 14, 6, -58, 6, -5, 17, -7, -18, 3, 3, -1, -5 }, + { 11, 14, 6, -3, 1, -1, 1, 0, 10, -7, -9, 3, -2, 1, -1, 0, 22, 21, 1, -21, 2, -4, 1, -2, 92, 1, 53, 0, -9, 1, -2, 0, -21, -11, 1, 40, -5, -4, -24, 5, -4, 5, -6, -5, 0, 0, 0, -3 }, + { -10, -11, -47, 3, -4, 1, -1, 0, 5, 28, 11, -2, -1, 0, 0, 0, -12, -2, -38, 2, 0, 1, 0, 0, 16, 38, 11, -16, -1, -3, 0, -2, 12, -9, -22, 7, -8, 60, 4, -36, -6, -15, 54, 7, 3, -7, -8, 14 }, + { -8, -24, -99, 11, -10, 3, -4, 1, -5, -36, 19, -26, 4, -5, 1, -2, 0, 25, 41, 5, -3, 1, 0, 0, 10, -5, -7, 12, 2, 1, 0, 0, -1, 1, 9, -3, -3, -14, -3, 12, 2, 4, -13, -2, -1, 3, 2, -4 }, + { -5, 1, -1, 0, 1, 0, 0, 0, -10, -14, -6, 8, 0, 1, 0, 0, -17, -2, 7, -5, 3, -1, 0, 0, -16, 13, 3, 31, -1, 6, 0, 2, -93, -15, -46, -3, 23, -19, 0, -47, 8, 4, 8, 3, 2, 3, 0, 0 }, + { 1, 12, -20, 21, -4, 5, -2, 2, -5, -2, -75, 9, -1, 2, -1, 1, -1, -2, -16, -4, 0, -1, 0, 0, -7, 7, -31, 0, 3, 0, 0, 0, 4, 11, -12, 4, -12, 14, -50, -1, -8, 32, -4, -54, 2, 0, 30, -15 }, + { 2, -9, -18, 8, -3, 3, -1, 1, 3, -25, -62, -6, 0, -2, 0, -1, -6, -61, 14, -51, 2, -6, 0, -2, -19, 0, 40, -7, -17, 0, -3, 0, 13, -4, 11, 9, 17, 0, 24, 5, 1, -12, 4, 28, 0, 0, -15, 8 }, + { 4, 9, 39, 18, 0, 2, 0, 1, -6, -16, -22, -37, 5, -5, 1, -2, -5, 15, 63, 9, -16, 0, -3, 0, 18, 42, -18, 27, 15, 1, 3, 1, 12, -34, 9, -24, 4, 28, -2, 4, -11, -4, 30, 2, 5, -13, -4, 18 }, + { -7, -2, 15, -6, 1, -1, 1, -1, -11, -3, 22, -14, 0, -2, 1, -1, -18, -7, 30, -9, -4, 0, -1, 0, -35, 23, 23, 10, -17, 1, -3, 0, -19, 53, 6, 48, -65, 12, -12, 11, -8, -16, 10, -21, -2, -12, 6, 2 }, + } + } +}; + +const int8_t ff_vvc_lfnst_4x4[4][2][16][16] = { + { //0 + { + { 108, -44, -15, 1, -44, 19, 7, -1, -11, 6, 2, -1, 0, -1, -1, 0 }, + { -40, -97, 56, 12, -11, 29, -12, -3, 18, 18, -15, -3, -1, -3, 2, 1 }, + { 25, -31, -1, 7, 100, -16, -29, 1, -54, 21, 14, -4, -7, 2, 4, 0 }, + { -32, -39, -92, 51, -6, -16, 36, -8, 3, 22, 18, -15, 4, 1, -5, 2 }, + { 8, -9, 33, -8, -16, -102, 36, 23, -4, 38, -27, -5, 5, 16, -8, -6 }, + { -25, 5, 16, -3, -38, 14, 11, -3, -97, 7, 26, 1, 55, -10, -19, 3 }, + { 8, 9, 16, 1, 37, 36, 94, -38, -7, 3, -47, 11, -6, -13, -17, 10 }, + { 2, 34, -5, 1, -7, 24, -25, -3, 8, 99, -28, -29, 6, -43, 21, 11 }, + { -16, -27, -39, -109, 6, 10, 16, 24, 3, 19, 10, 24, -4, -7, -2, -3 }, + { -9, -10, -34, 4, -9, -5, -29, 5, -33, -26, -96, 33, 14, 4, 39, -14 }, + { -13, 1, 4, -9, -30, -17, -3, -64, -35, 11, 17, 19, -86, 6, 36, 14 }, + { 8, -7, -5, -15, 7, -30, -28, -87, 31, 4, 4, 33, 61, -5, -17, 22 }, + { -2, 13, -6, -4, -2, 28, -13, -14, -3, 37, -15, -3, -2, 107, -36, -24 }, + { 4, 9, 11, 31, 4, 9, 16, 19, 12, 33, 32, 94, 12, 0, 34, -45 }, + { 2, -2, 8, -16, 8, 5, 28, -17, 6, -7, 18, -45, 40, 36, 97, -8 }, + { 0, -2, 0, -10, -1, -7, -3, -35, -1, -7, -2, -32, -6, -33, -16, -112 }, + }, + { + { 119, -30, -22, -3, -23, -2, 3, 2, -16, 3, 6, 0, -3, 2, 1, 0 }, + { -27, -101, 31, 17, -47, 2, 22, 3, 19, 30, -7, -9, 5, 3, -5, -1 }, + { 0, 58, 22, -15, -102, 2, 38, 2, 10, -13, -5, 4, 14, -1, -9, 0 }, + { 23, 4, 66, -11, 22, 89, -2, -26, 13, -8, -38, -1, -9, -20, -2, 8 }, + { -19, -5, -89, 2, -26, 76, -11, -17, 20, 13, 18, -4, 1, -15, 3, 5 }, + { -10, -1, -1, 6, 23, 25, 87, -7, -74, 4, 39, -5, 0, -1, -20, -1 }, + { -17, -28, 12, -8, -32, 14, -53, -6, -68, -67, 17, 29, 2, 6, 25, 4 }, + { 1, -24, -23, 1, 17, -7, 52, 9, 50, -92, -15, 27, -15, -10, -6, 3 }, + { -6, -17, -2, -111, 7, -17, 8, -42, 9, 18, 16, 25, -4, 2, -1, 11 }, + { 9, 5, 35, 0, 6, 21, -9, 34, 44, -3, 102, 11, -7, 13, 11, -20 }, + { 4, -5, -5, -10, 15, 19, -2, 6, 6, -12, -13, 6, 95, 69, -29, -24 }, + { -6, -4, -9, -39, 1, 22, 0, 102, -19, 19, -32, 30, -16, -14, -8, -23 }, + { 4, -4, 7, 8, 4, -13, -18, 5, 0, 0, 21, 22, 58, -88, -54, 28 }, + { -4, -7, 0, -24, -7, 0, -25, 3, -3, -30, 8, -76, -34, 4, -80, -26 }, + { 0, 6, 0, 30, -6, 1, -13, -23, 1, 20, -2, 80, -44, 37, -68, 1 }, + { 0, 0, -1, 5, -1, -7, 1, -34, -2, 3, -6, 19, 5, -38, 11, -115 }, + } + }, + { //1 + { + { -111, 39, 4, 3, 44, 11, -12, -1, 7, -16, -5, 2, 3, -1, 4, 2 }, + { -47, -27, 15, -1, -92, 43, 20, -2, 20, 39, -16, -5, 10, -5, -13, 2 }, + { -35, -23, 4, 4, -17, -72, 32, 6, -59, 18, 50, -6, 0, 40, 0, -13 }, + { 13, 93, -27, -4, -48, 13, -34, 4, -52, 11, 1, 10, 3, 16, -3, 1 }, + { -11, -27, 1, 2, -47, -4, -36, 10, -2, -85, 14, 29, -20, -2, 57, 4 }, + { 0, -35, 32, -2, 26, 60, -3, -17, -82, 1, -30, 0, -37, 21, 3, 12 }, + { -17, -46, -92, 14, 7, -10, -39, 29, -17, 27, -28, 17, 1, -15, -13, 17 }, + { 4, -10, -23, 4, 16, 58, -17, 26, 30, 21, 67, 2, -13, 59, 13, -40 }, + { 5, -20, 32, -5, 8, -3, -46, -7, -4, 2, -15, 24, 100, 44, 0, 5 }, + { -4, -1, 38, -18, -7, -42, -63, -6, 33, 34, -23, 15, -65, 33, -20, 2 }, + { -2, -10, 35, -19, 5, 8, -44, 14, -25, 25, 58, 17, 7, -84, -16, -18 }, + { 5, 13, 18, 34, 11, -4, 18, 18, 5, 58, -3, 42, -2, -10, 85, 38 }, + { -5, -7, -34, -83, 2, -1, -4, -73, 4, 20, 15, -12, 4, -3, 44, 12 }, + { 0, 4, -2, -60, 5, 9, 42, 34, 5, -14, 9, 80, -5, 13, -38, 37 }, + { -1, 2, 7, -57, 3, -7, 9, 68, -9, 6, -49, -20, 6, -4, 36, -64 }, + { -1, 0, -12, 23, 1, -4, 17, -53, -3, 4, -21, 72, -4, -8, -3, -83 }, + }, + { + { 88, -55, 6, -3, -66, 27, 9, -2, 11, 11, -13, 1, -2, -7, 1, 2 }, + { -58, -20, 27, -2, -27, 75, -29, 0, 47, -42, -11, 11, -9, -3, 19, -4 }, + { -51, 23, -22, 5, -63, 3, 37, -5, 1, 64, -35, -4, 29, -31, -11, 13 }, + { -27, -76, 49, -2, 40, 14, 9, -17, -56, 36, -25, 6, 14, 3, -6, 8 }, + { 19, -4, -36, 22, 52, 7, 36, -23, 28, -17, -64, 15, -5, -44, 48, 9 }, + { 29, 50, 13, -10, 1, 34, -59, 1, -51, 4, -16, 30, 52, -33, 24, -5 }, + { -12, -21, -74, 43, -13, 39, 18, -5, -58, -35, 27, -5, 19, 26, 6, -5 }, + { 19, 38, -10, -5, 28, 66, 0, -5, -4, 19, -30, -26, -40, 28, -60, 37 }, + { -6, 27, 18, -5, -37, -18, 12, -25, -44, -10, -38, 37, -66, 45, 40, -7 }, + { -13, -28, -45, -39, 0, -5, -39, 69, -23, 16, -12, -18, -50, -31, 24, 13 }, + { -1, 8, 24, -51, -15, -9, 44, 10, -28, -70, -12, -39, 24, -18, -4, 51 }, + { -8, -22, -17, 33, -18, -45, -57, -27, 0, -31, -30, 29, -2, -13, -53, 49 }, + { 1, 12, 32, 51, -8, 8, -2, -31, -22, 4, 46, -39, -49, -67, 14, 17 }, + { 4, 5, 24, 60, -5, -14, -23, 38, 9, 8, -34, -59, 24, 47, 42, 28 }, + { -1, -5, -20, -34, 4, 4, -15, -46, 18, 31, 42, 10, 10, 27, 49, 78 }, + { -3, -7, -22, -34, -5, -11, -36, -69, -1, -3, -25, -73, 5, 4, 4, -49 }, + } + }, + { //2 + { + { -112, 47, -2, 2, -34, 13, 2, 0, 15, -7, 1, 0, 8, -3, -1, 0 }, + { 29, -7, 1, -1, -108, 40, 2, 0, -45, 13, 4, -1, 8, -5, 1, 0 }, + { -36, -87, 69, -10, -17, -33, 26, -2, 7, 14, -11, 2, 6, 8, -7, 0 }, + { 28, -5, 2, -2, -29, 13, -2, 0, 103, -36, -4, 1, 48, -16, -4, 1 }, + { -12, -24, 15, -3, 26, 80, -61, 9, 15, 54, -36, 2, 0, -4, 6, -2 }, + { 18, 53, 69, -74, 14, 24, 28, -30, -6, -7, -11, 12, -5, -7, -6, 8 }, + { 5, -1, 2, 0, -26, 6, 0, 1, 45, -9, -1, 0, -113, 28, 8, -1 }, + { -13, -32, 18, -2, 15, 34, -27, 7, -25, -80, 47, -1, -16, -50, 28, 2 }, + { -4, -13, -10, 19, 18, 46, 60, -48, 16, 33, 60, -48, 1, 0, 5, -2 }, + { 15, 33, 63, 89, 8, 15, 25, 40, -4, -8, -15, -8, -2, -6, -9, -7 }, + { -8, -24, -27, 15, 12, 41, 26, -29, -17, -50, -39, 27, 0, 35, -67, 26 }, + { -2, -6, -24, 13, -1, -8, 37, -22, 3, 18, -51, 22, -23, -95, 17, 17 }, + { -3, -7, -16, -21, 10, 24, 46, 75, 8, 20, 38, 72, 1, 2, 1, 7 }, + { 2, 6, 10, -3, -5, -16, -31, 12, 7, 24, 41, -16, -16, -41, -89, 49 }, + { 4, 8, 21, 40, -4, -11, -28, -57, 5, 14, 31, 70, 7, 18, 32, 52 }, + { 0, 1, 4, 11, -2, -4, -13, -34, 3, 7, 20, 47, -6, -19, -42, -101 }, + }, + { + { -99, 39, -1, 2, 65, -20, -5, 0, -15, -2, 5, -1, 0, 3, -1, 0 }, + { 58, 42, -33, 3, 33, -63, 23, -1, -55, 32, 3, -5, 21, -2, -8, 3 }, + { -15, 71, -44, 5, -58, -29, 25, 3, 62, -7, -4, -4, -19, 4, 0, 1 }, + { 46, 5, 4, -6, 71, -12, -15, 5, 52, -38, 13, -2, -63, 23, 3, -3 }, + { -14, -54, -29, 29, 25, -9, 61, -29, 27, 44, -48, 5, -27, -21, 12, 7 }, + { -3, 3, 69, -42, -11, -50, -26, 26, 24, 63, -19, -5, -18, -22, 12, 0 }, + { 17, 16, -2, 1, 38, 18, -12, 0, 62, 1, -14, 5, 89, -42, 8, -2 }, + { 15, 54, -8, 6, 6, 60, -26, -8, -30, 17, -38, 22, -43, -45, 42, -7 }, + { -6, -17, -55, -28, 9, 30, -8, 58, 4, 34, 41, -52, -16, -36, -20, 16 }, + { -2, -1, -9, -79, 7, 11, 48, 44, -13, -34, -55, 6, 12, 23, 20, -11 }, + { 7, 29, 14, -6, 12, 53, 10, -11, 14, 59, -15, -3, 5, 71, -54, 13 }, + { -5, -24, -53, 15, -3, -15, -61, 26, 6, 30, -16, 23, 13, 56, 44, -35 }, + { 4, 8, 21, 52, -1, -1, -5, 29, -7, -17, -44, -84, 8, 20, 31, 39 }, + { -2, -11, -25, -4, -4, -21, -53, 2, -5, -26, -64, 19, -8, -19, -73, 39 }, + { -3, -5, -23, -57, -2, -4, -24, -75, 1, 3, 9, -25, 6, 15, 41, 61 }, + { 1, 1, 7, 18, 1, 2, 16, 47, 2, 5, 24, 67, 3, 9, 25, 88 }, + } + }, + { //3 + { + { -114, 37, 3, 2, -22, -23, 14, 0, 21, -17, -5, 2, 5, 2, -4, -1 }, + { -19, -41, 19, -2, 85, -60, -11, 7, 17, 31, -34, 2, -11, 19, 2, -8 }, + { 36, -25, 18, -2, -42, -53, 35, 5, 46, -60, -25, 19, 8, 21, -33, -1 }, + { -27, -80, 44, -3, -58, 1, -29, 19, -41, 18, -12, -7, 12, -17, 7, -6 }, + { -11, -21, 37, -10, 44, -4, 47, -12, -37, -41, 58, 18, 10, -46, -16, 31 }, + { 15, 47, 10, -6, -16, -44, 42, 10, -80, 25, -40, 21, -23, -2, 3, -14 }, + { 13, 25, 79, -39, -13, 10, 31, -4, 49, 45, 12, -8, 3, -1, 43, 7 }, + { 16, 11, -26, 13, -13, -74, -20, -1, 5, -6, 29, -47, 26, -49, 54, 2 }, + { -8, -34, -26, 7, -26, -19, 29, -37, 1, 22, 46, -9, -81, 37, 14, 20 }, + { -6, -30, -42, -12, -3, 5, 57, -52, -2, 37, -12, 6, 74, 10, 6, -15 }, + { 5, 9, -6, 42, -15, -18, -9, 26, 15, 58, 14, 43, 23, -10, -37, 75 }, + { -5, -23, -23, 36, 3, 22, 36, 40, 27, -4, -16, 56, -25, -46, 56, -24 }, + { 1, 3, 23, 73, 8, 5, 34, 46, -12, 2, 35, -38, 26, 52, 2, -31 }, + { -3, -2, -21, -52, 1, -10, -17, 44, -19, -20, 30, 45, 27, 61, 49, 21 }, + { -2, -7, -33, -56, -4, -6, 21, 63, 15, 31, 32, -22, -10, -26, -52, -38 }, + { -5, -12, -18, -12, 8, 22, 38, 36, -5, -15, -51, -63, -5, 0, 15, 73 }, + }, + { + { -102, 22, 7, 2, 66, -25, -6, -1, -15, 14, 1, -1, 2, -2, 1, 0 }, + { 12, 93, -27, -6, -27, -64, 36, 6, 13, 5, -23, 0, -2, 6, 5, -3 }, + { -59, -24, 17, 1, -62, -2, -3, 2, 83, -12, -17, -2, -24, 14, 7, -2 }, + { -33, 23, -36, 11, -21, 50, 35, -16, -23, -78, 16, 19, 22, 15, -30, -5 }, + { 0, -38, -81, 30, 27, 5, 51, -32, 24, 36, -16, 12, -24, -8, 9, 1 }, + { 28, 38, 8, -9, 62, 32, -13, 2, 51, -32, 15, 5, -66, 28, 0, -1 }, + { 11, -35, 21, -17, 30, -18, 31, 18, -11, -36, -80, 12, 16, 49, 13, -32 }, + { -13, 23, 22, -36, -12, 64, 39, 25, -19, 23, -36, 9, -30, -58, 33, -7 }, + { -9, -20, -55, -83, 3, -2, 1, 62, 8, 2, 27, -28, 7, 15, -11, 5 }, + { -6, 24, -38, 23, -8, 40, -49, 0, -7, 9, -25, -44, 23, 39, 70, -3 }, + { 12, 17, 17, 0, 32, 27, 21, 2, 67, 11, -6, -10, 89, -22, -12, 16 }, + { 2, -9, 8, 45, 7, -8, 27, 35, -9, -31, -17, -87, -23, -22, -19, 44 }, + { -1, -9, 28, -24, -1, -10, 49, -30, -8, -7, 40, 1, 4, 33, 65, 67 }, + { 5, -12, -24, -17, 13, -34, -32, -16, 14, -67, -7, 9, 7, -74, 49, 1 }, + { 2, -6, 11, 45, 3, -10, 33, 55, 8, -5, 59, 4, 7, -4, 44, -66 }, + { -1, 1, -14, 36, -1, 2, -20, 69, 0, 0, -15, 72, 3, 4, 5, 65 }, + } + } +}; + +const uint8_t ff_vvc_lfnst_tr_set_index[95] = +{ + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 +}; + +uint8_t ff_vvc_default_scale_m[64 * 64]; + +//< AlfFixFiltCoeff +const int16_t ff_vvc_alf_fix_filt_coeff[64][12] = { + { 0, 0, 2, -3, 1, -4, 1, 7, -1, 1, -1, 5 }, + { 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, -1, 2 }, + { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1 }, + { 2, 2, -7, -3, 0, -5, 13, 22, 12, -3, -3, 17 }, + { -1, 0, 6, -8, 1, -5, 1, 23, 0, 2, -5, 10 }, + { 0, 0, -1, -1, 0, -1, 2, 1, 0, 0, -1, 4 }, + { 0, 0, 3, -11, 1, 0, -1, 35, 5, 2, -9, 9 }, + { 0, 0, 8, -8, -2, -7, 4, 4, 2, 1, -1, 25 }, + { 0, 0, 1, -1, 0, -3, 1, 3, -1, 1, -1, 3 }, + { 0, 0, 3, -3, 0, -6, 5, -1, 2, 1, -4, 21 }, + { -7, 1, 5, 4, -3, 5, 11, 13, 12, -8, 11, 12 }, + { -5, -3, 6, -2, -3, 8, 14, 15, 2, -7, 11, 16 }, + { 2, -1, -6, -5, -2, -2, 20, 14, -4, 0, -3, 25 }, + { 3, 1, -8, -4, 0, -8, 22, 5, -3, 2, -10, 29 }, + { 2, 1, -7, -1, 2, -11, 23, -5, 0, 2, -10, 29 }, + { -6, -3, 8, 9, -4, 8, 9, 7, 14, -2, 8, 9 }, + { 2, 1, -4, -7, 0, -8, 17, 22, 1, -1, -4, 23 }, + { 3, 0, -5, -7, 0, -7, 15, 18, -5, 0, -5, 27 }, + { 2, 0, 0, -7, 1, -10, 13, 13, -4, 2, -7, 24 }, + { 3, 3, -13, 4, -2, -5, 9, 21, 25, -2, -3, 12 }, + { -5, -2, 7, -3, -7, 9, 8, 9, 16, -2, 15, 12 }, + { 0, -1, 0, -7, -5, 4, 11, 11, 8, -6, 12, 21 }, + { 3, -2, -3, -8, -4, -1, 16, 15, -2, -3, 3, 26 }, + { 2, 1, -5, -4, -1, -8, 16, 4, -2, 1, -7, 33 }, + { 2, 1, -4, -2, 1, -10, 17, -2, 0, 2, -11, 33 }, + { 1, -2, 7, -15, -16, 10, 8, 8, 20, 11, 14, 11 }, + { 2, 2, 3, -13, -13, 4, 8, 12, 2, -3, 16, 24 }, + { 1, 4, 0, -7, -8, -4, 9, 9, -2, -2, 8, 29 }, + { 1, 1, 2, -4, -1, -6, 6, 3, -1, -1, -3, 30 }, + { -7, 3, 2, 10, -2, 3, 7, 11, 19, -7, 8, 10 }, + { 0, -2, -5, -3, -2, 4, 20, 15, -1, -3, -1, 22 }, + { 3, -1, -8, -4, -1, -4, 22, 8, -4, 2, -8, 28 }, + { 0, 3, -14, 3, 0, 1, 19, 17, 8, -3, -7, 20 }, + { 0, 2, -1, -8, 3, -6, 5, 21, 1, 1, -9, 13 }, + { -4, -2, 8, 20, -2, 2, 3, 5, 21, 4, 6, 1 }, + { 2, -2, -3, -9, -4, 2, 14, 16, 3, -6, 8, 24 }, + { 2, 1, 5, -16, -7, 2, 3, 11, 15, -3, 11, 22 }, + { 1, 2, 3, -11, -2, -5, 4, 8, 9, -3, -2, 26 }, + { 0, -1, 10, -9, -1, -8, 2, 3, 4, 0, 0, 29 }, + { 1, 2, 0, -5, 1, -9, 9, 3, 0, 1, -7, 20 }, + { -2, 8, -6, -4, 3, -9, -8, 45, 14, 2, -13, 7 }, + { 1, -1, 16, -19, -8, -4, -3, 2, 19, 0, 4, 30 }, + { 1, 1, -3, 0, 2, -11, 15, -5, 1, 2, -9, 24 }, + { 0, 1, -2, 0, 1, -4, 4, 0, 0, 1, -4, 7 }, + { 0, 1, 2, -5, 1, -6, 4, 10, -2, 1, -4, 10 }, + { 3, 0, -3, -6, -2, -6, 14, 8, -1, -1, -3, 31 }, + { 0, 1, 0, -2, 1, -6, 5, 1, 0, 1, -5, 13 }, + { 3, 1, 9, -19, -21, 9, 7, 6, 13, 5, 15, 21 }, + { 2, 4, 3, -12, -13, 1, 7, 8, 3, 0, 12, 26 }, + { 3, 1, -8, -2, 0, -6, 18, 2, -2, 3, -10, 23 }, + { 1, 1, -4, -1, 1, -5, 8, 1, -1, 2, -5, 10 }, + { 0, 1, -1, 0, 0, -2, 2, 0, 0, 1, -2, 3 }, + { 1, 1, -2, -7, 1, -7, 14, 18, 0, 0, -7, 21 }, + { 0, 1, 0, -2, 0, -7, 8, 1, -2, 0, -3, 24 }, + { 0, 1, 1, -2, 2, -10, 10, 0, -2, 1, -7, 23 }, + { 0, 2, 2, -11, 2, -4, -3, 39, 7, 1, -10, 9 }, + { 1, 0, 13, -16, -5, -6, -1, 8, 6, 0, 6, 29 }, + { 1, 3, 1, -6, -4, -7, 9, 6, -3, -2, 3, 33 }, + { 4, 0, -17, -1, -1, 5, 26, 8, -2, 3, -15, 30 }, + { 0, 1, -2, 0, 2, -8, 12, -6, 1, 1, -6, 16 }, + { 0, 0, 0, -1, 1, -4, 4, 0, 0, 0, -3, 11 }, + { 0, 1, 2, -8, 2, -6, 5, 15, 0, 2, -7, 9 }, + { 1, -1, 12, -15, -7, -2, 3, 6, 6, -1, 7, 30 }, +}; + +//< AlfClassToFiltMap +const uint8_t ff_vvc_alf_class_to_filt_map[16][25] = { + { 8, 2, 2, 2, 3, 4, 53, 9, 9, 52, 4, 4, 5, 9, 2, 8, 10, 9, 1, 3, 39, 39, 10, 9, 52 }, + { 11, 12, 13, 14, 15, 30, 11, 17, 18, 19, 16, 20, 20, 4, 53, 21, 22, 23, 14, 25, 26, 26, 27, 28, 10 }, + { 16, 12, 31, 32, 14, 16, 30, 33, 53, 34, 35, 16, 20, 4, 7, 16, 21, 36, 18, 19, 21, 26, 37, 38, 39 }, + { 35, 11, 13, 14, 43, 35, 16, 4, 34, 62, 35, 35, 30, 56, 7, 35, 21, 38, 24, 40, 16, 21, 48, 57, 39 }, + { 11, 31, 32, 43, 44, 16, 4, 17, 34, 45, 30, 20, 20, 7, 5, 21, 22, 46, 40, 47, 26, 48, 63, 58, 10 }, + { 12, 13, 50, 51, 52, 11, 17, 53, 45, 9, 30, 4, 53, 19, 0, 22, 23, 25, 43, 44, 37, 27, 28, 10, 55 }, + { 30, 33, 62, 51, 44, 20, 41, 56, 34, 45, 20, 41, 41, 56, 5, 30, 56, 38, 40, 47, 11, 37, 42, 57, 8 }, + { 35, 11, 23, 32, 14, 35, 20, 4, 17, 18, 21, 20, 20, 20, 4, 16, 21, 36, 46, 25, 41, 26, 48, 49, 58 }, + { 12, 31, 59, 59, 3, 33, 33, 59, 59, 52, 4, 33, 17, 59, 55, 22, 36, 59, 59, 60, 22, 36, 59, 25, 55 }, + { 31, 25, 15, 60, 60, 22, 17, 19, 55, 55, 20, 20, 53, 19, 55, 22, 46, 25, 43, 60, 37, 28, 10, 55, 52 }, + { 12, 31, 32, 50, 51, 11, 33, 53, 19, 45, 16, 4, 4, 53, 5, 22, 36, 18, 25, 43, 26, 27, 27, 28, 10 }, + { 5, 2, 44, 52, 3, 4, 53, 45, 9, 3, 4, 56, 5, 0, 2, 5, 10, 47, 52, 3, 63, 39, 10, 9, 52 }, + { 12, 34, 44, 44, 3, 56, 56, 62, 45, 9, 56, 56, 7, 5, 0, 22, 38, 40, 47, 52, 48, 57, 39, 10, 9 }, + { 35, 11, 23, 14, 51, 35, 20, 41, 56, 62, 16, 20, 41, 56, 7, 16, 21, 38, 24, 40, 26, 26, 42, 57, 39 }, + { 33, 34, 51, 51, 52, 41, 41, 34, 62, 0, 41, 41, 56, 7, 5, 56, 38, 38, 40, 44, 37, 42, 57, 39, 10 }, + { 16, 31, 32, 15, 60, 30, 4, 17, 19, 25, 22, 20, 4, 53, 19, 21, 22, 46, 25, 55, 26, 48, 63, 58, 55 }, +}; + +const uint8_t ff_vvc_alf_aps_class_to_filt_map[25] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, +}; + +const int8_t ff_vvc_inter_luma_filters[VVC_INTER_FILTER_TYPES][VVC_INTER_LUMA_FACTS][VVC_INTER_LUMA_TAPS] = { + { + //1x, hpelIfIdx == 0, Table 27 + { 0, 0, 0, 64, 0, 0, 0, 0 }, + { 0, 1, -3, 63, 4, -2, 1, 0 }, + { -1, 2, -5, 62, 8, -3, 1, 0 }, + { -1, 3, -8, 60, 13, -4, 1, 0 }, + { -1, 4, -10, 58, 17, -5, 1, 0 }, + { -1, 4, -11, 52, 26, -8, 3, -1 }, + { -1, 3, -9, 47, 31, -10, 4, -1 }, + { -1, 4, -11, 45, 34, -10, 4, -1 }, + { -1, 4, -11, 40, 40, -11, 4, -1 }, + { -1, 4, -10, 34, 45, -11, 4, -1 }, + { -1, 4, -10, 31, 47, -9, 3, -1 }, + { -1, 3, -8, 26, 52, -11, 4, -1 }, + { 0, 1, -5, 17, 58, -10, 4, -1 }, + { 0, 1, -4, 13, 60, -8, 3, -1 }, + { 0, 1, -3, 8, 62, -5, 2, -1 }, + { 0, 1, -2, 4, 63, -3, 1, 0 }, + }, + + { + //1x, hpelIfIdx == 1, Table 27 + { 0, 0, 0, 64, 0, 0, 0, 0 }, + { 0, 1, -3, 63, 4, -2, 1, 0 }, + { -1, 2, -5, 62, 8, -3, 1, 0 }, + { -1, 3, -8, 60, 13, -4, 1, 0 }, + { -1, 4, -10, 58, 17, -5, 1, 0 }, + { -1, 4, -11, 52, 26, -8, 3, -1 }, + { -1, 3, -9, 47, 31, -10, 4, -1 }, + { -1, 4, -11, 45, 34, -10, 4, -1 }, + { 0, 3, 9, 20, 20, 9, 3, 0 }, + { -1, 4, -10, 34, 45, -11, 4, -1 }, + { -1, 4, -10, 31, 47, -9, 3, -1 }, + { -1, 3, -8, 26, 52, -11, 4, -1 }, + { 0, 1, -5, 17, 58, -10, 4, -1 }, + { 0, 1, -4, 13, 60, -8, 3, -1 }, + { 0, 1, -3, 8, 62, -5, 2, -1 }, + { 0, 1, -2, 4, 63, -3, 1, 0 }, + }, + + { + //1x, affine, Table 30 + { 0, 0, 0, 64, 0, 0, 0, 0 }, + { 0, 1, -3, 63, 4, -2, 1, 0 }, + { 0, 1, -5, 62, 8, -3, 1, 0 }, + { 0, 2, -8, 60, 13, -4, 1, 0 }, + { 0, 3, -10, 58, 17, -5, 1, 0 }, + { 0, 3, -11, 52, 26, -8, 2, 0 }, + { 0, 2, -9, 47, 31, -10, 3, 0 }, + { 0, 3, -11, 45, 34, -10, 3, 0 }, + { 0, 3, -11, 40, 40, -11, 3, 0 }, + { 0, 3, -10, 34, 45, -11, 3, 0 }, + { 0, 3, -10, 31, 47, -9, 2, 0 }, + { 0, 2, -8, 26, 52, -11, 3, 0 }, + { 0, 1, -5, 17, 58, -10, 3, 0 }, + { 0, 1, -4, 13, 60, -8, 2, 0 }, + { 0, 1, -3, 8, 62, -5, 1, 0 }, + { 0, 1, -2, 4, 63, -3, 1, 0 }, + }, + +}; + +const int8_t ff_vvc_inter_chroma_filters[VVC_INTER_FILTER_TYPES][VVC_INTER_CHROMA_FACTS][VVC_INTER_CHROMA_TAPS] = { + { + //1x, Table 33 + { 0, 64, 0, 0 }, + { -1, 63, 2, 0 }, + { -2, 62, 4, 0 }, + { -2, 60, 7, -1 }, + { -2, 58, 10, -2 }, + { -3, 57, 12, -2 }, + { -4, 56, 14, -2 }, + { -4, 55, 15, -2 }, + { -4, 54, 16, -2 }, + { -5, 53, 18, -2 }, + { -6, 52, 20, -2 }, + { -6, 49, 24, -3 }, + { -6, 46, 28, -4 }, + { -5, 44, 29, -4 }, + { -4, 42, 30, -4 }, + { -4, 39, 33, -4 }, + { -4, 36, 36, -4 }, + { -4, 33, 39, -4 }, + { -4, 30, 42, -4 }, + { -4, 29, 44, -5 }, + { -4, 28, 46, -6 }, + { -3, 24, 49, -6 }, + { -2, 20, 52, -6 }, + { -2, 18, 53, -5 }, + { -2, 16, 54, -4 }, + { -2, 15, 55, -4 }, + { -2, 14, 56, -4 }, + { -2, 12, 57, -3 }, + { -2, 10, 58, -2 }, + { -1, 7, 60, -2 }, + { 0, 4, 62, -2 }, + { 0, 2, 63, -1 }, + }, + { + //1.5x, Table 34 + { 12, 40, 12, 0 }, + { 11, 40, 13, 0 }, + { 10, 40, 15, -1 }, + { 9, 40, 16, -1 }, + { 8, 40, 17, -1 }, + { 8, 39, 18, -1 }, + { 7, 39, 19, -1 }, + { 6, 38, 21, -1 }, + { 5, 38, 22, -1 }, + { 4, 38, 23, -1 }, + { 4, 37, 24, -1 }, + { 3, 36, 25, 0 }, + { 3, 35, 26, 0 }, + { 2, 34, 28, 0 }, + { 2, 33, 29, 0 }, + { 1, 33, 30, 0 }, + { 1, 31, 31, 1 }, + { 0, 30, 33, 1 }, + { 0, 29, 33, 2 }, + { 0, 28, 34, 2 }, + { 0, 26, 35, 3 }, + { 0, 25, 36, 3 }, + { -1, 24, 37, 4 }, + { -1, 23, 38, 4 }, + { -1, 22, 38, 5 }, + { -1, 21, 38, 6 }, + { -1, 19, 39, 7 }, + { -1, 18, 39, 8 }, + { -1, 17, 40, 8 }, + { -1, 16, 40, 9 }, + { -1, 15, 40, 10 }, + { 0, 13, 40, 11 }, + }, + { + //2x, Table 35 + { 17, 30, 17, 0 }, + { 17, 30, 18, -1 }, + { 16, 30, 18, 0 }, + { 16, 30, 18, 0 }, + { 15, 30, 18, 1 }, + { 14, 30, 18, 2 }, + { 13, 29, 19, 3 }, + { 13, 29, 19, 3 }, + { 12, 29, 20, 3 }, + { 11, 28, 21, 4 }, + { 10, 28, 22, 4 }, + { 10, 27, 22, 5 }, + { 9, 27, 23, 5 }, + { 9, 26, 24, 5 }, + { 8, 26, 24, 6 }, + { 7, 26, 25, 6 }, + { 7, 25, 25, 7 }, + { 6, 25, 26, 7 }, + { 6, 24, 26, 8 }, + { 5, 24, 26, 9 }, + { 5, 23, 27, 9 }, + { 5, 22, 27, 10 }, + { 4, 22, 28, 10 }, + { 4, 21, 28, 11 }, + { 3, 20, 29, 12 }, + { 3, 19, 29, 13 }, + { 3, 19, 29, 13 }, + { 2, 18, 30, 14 }, + { 1, 18, 30, 15 }, + { 0, 18, 30, 16 }, + { 0, 18, 30, 16 }, + { -1, 18, 30, 17 }, + }, +}; + +const int8_t ff_vvc_inter_luma_dmvr_filters[VVC_INTER_LUMA_DMVR_FACTS][VVC_INTER_LUMA_DMVR_TAPS] = { + { 16, 0 }, + { 15, 1 }, + { 14, 2 }, + { 13, 3 }, + { 12, 4 }, + { 11, 5 }, + { 10, 6 }, + { 9, 7 }, + { 8, 8 }, + { 7, 9 }, + { 6, 10 }, + { 5, 11 }, + { 4, 12 }, + { 3, 13 }, + { 2, 14 }, + { 1, 15 }, +}; + +#define FILTER_G(fact) { 16 - (fact >> 1), 32 - (fact >> 1), 16 + (fact >> 1), fact >> 1} +// Table 25 – Specification of interpolation filter coefficients fC and fG +const int8_t ff_vvc_intra_luma_filter[VVC_INTRA_LUMA_TYPES][VVC_INTRA_LUMA_FACTS][VVC_INTRA_LUMA_TAPS] = { + { + { 0, 64, 0, 0 }, + { -1, 63, 2, 0 }, + { -2, 62, 4, 0 }, + { -2, 60, 7, -1 }, + { -2, 58, 10, -2 }, + { -3, 57, 12, -2 }, + { -4, 56, 14, -2 }, + { -4, 55, 15, -2 }, + { -4, 54, 16, -2 }, + { -5, 53, 18, -2 }, + { -6, 52, 20, -2 }, + { -6, 49, 24, -3 }, + { -6, 46, 28, -4 }, + { -5, 44, 29, -4 }, + { -4, 42, 30, -4 }, + { -4, 39, 33, -4 }, + { -4, 36, 36, -4 }, + { -4, 33, 39, -4 }, + { -4, 30, 42, -4 }, + { -4, 29, 44, -5 }, + { -4, 28, 46, -6 }, + { -3, 24, 49, -6 }, + { -2, 20, 52, -6 }, + { -2, 18, 53, -5 }, + { -2, 16, 54, -4 }, + { -2, 15, 55, -4 }, + { -2, 14, 56, -4 }, + { -2, 12, 57, -3 }, + { -2, 10, 58, -2 }, + { -1, 7, 60, -2 }, + { 0, 4, 62, -2 }, + { 0, 2, 63, -1 }, + }, + { + FILTER_G(0), + FILTER_G(1), + FILTER_G(2), + FILTER_G(3), + FILTER_G(4), + FILTER_G(5), + FILTER_G(6), + FILTER_G(7), + FILTER_G(8), + FILTER_G(9), + FILTER_G(10), + FILTER_G(11), + FILTER_G(12), + FILTER_G(13), + FILTER_G(14), + FILTER_G(15), + FILTER_G(16), + FILTER_G(17), + FILTER_G(18), + FILTER_G(19), + FILTER_G(20), + FILTER_G(21), + FILTER_G(22), + FILTER_G(23), + FILTER_G(24), + FILTER_G(25), + FILTER_G(26), + FILTER_G(27), + FILTER_G(28), + FILTER_G(29), + FILTER_G(30), + FILTER_G(31), + } +}; + +const uint8_t ff_vvc_gpm_angle_idx[VVC_GPM_NUM_PARTITION] = { + 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, + 5, 5, 8, 8, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, + 14, 14, 14, 14, 16, 16, 18, 18, 18, 19, 19, 19, 20, 20, 20, 21, + 21, 21, 24, 24, 27, 27, 27, 28, 28, 28, 29, 29, 29, 30, 30, 30, +}; + +const uint8_t ff_vvc_gpm_distance_idx[VVC_GPM_NUM_PARTITION] = { + 1, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, + 2, 3, 1, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, + 0, 1, 2, 3, 1, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, + 2, 3, 1, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, +}; + +const int8_t ff_vvc_gpm_distance_lut[VVC_GPM_NUM_ANGLES] = { + 8, 8, 8, 8, 4, 4, 2, 1, 0, -1, -2, -4, -4, -8, -8, -8, -8, -8, -8, -8, -4, -4, -2, -1, 0, 1, 2, 4, 4, 8, 8, 8, +}; + +const uint8_t ff_vvc_gpm_angle_to_mirror[VVC_GPM_NUM_ANGLES] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, +}; + +#define INV -1 +const uint8_t ff_vvc_gpm_angle_to_weights_idx[VVC_GPM_NUM_ANGLES] = { + 0, INV, 1, 2, 3, 4, INV, INV, 5, INV, INV, 4, 3, 2, 1, INV, + 0, INV, 1, 2, 3, 4, INV, INV, 5, INV, INV, 4, 3, 2, 1, INV, +}; +#undef INV + +const uint8_t ff_vvc_gpm_weights_offset_x[VVC_GPM_NUM_PARTITION][4][4] = { + { + { 53, 50, 44, 32 }, + { 53, 50, 44, 32 }, + { 53, 50, 44, 32 }, + { 53, 50, 44, 32 }, + }, + { + { 55, 54, 52, 48 }, + { 55, 54, 52, 48 }, + { 55, 54, 52, 48 }, + { 55, 54, 52, 48 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 50, 44, 32 }, + { 52, 48, 44, 32 }, + { 52, 48, 40, 32 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 52, 48, 40 }, + { 52, 48, 48, 40 }, + { 52, 48, 40, 40 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 54, 52, 48 }, + { 52, 48, 52, 48 }, + { 52, 48, 40, 48 }, + { 52, 48, 40, 24 }, + }, + { + { 51, 46, 36, 16 }, + { 51, 46, 36, 16 }, + { 51, 46, 36, 16 }, + { 51, 46, 36, 16 }, + }, + { + { 49, 42, 28, 0 }, + { 49, 42, 28, 0 }, + { 49, 42, 28, 0 }, + { 49, 42, 28, 0 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 46, 36, 16 }, + { 52, 48, 36, 16 }, + { 52, 48, 40, 16 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 44, 32, 8 }, + { 52, 48, 32, 8 }, + { 52, 48, 40, 8 }, + { 52, 48, 40, 24 }, + }, + { + { 52, 42, 28, 0 }, + { 52, 48, 28, 0 }, + { 52, 48, 40, 0 }, + { 52, 48, 40, 24 }, + }, +}; + +const uint8_t ff_vvc_gpm_weights_offset_y[VVC_GPM_NUM_PARTITION][4][4] = { + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 53, 53, 53, 53 }, + { 50, 50, 50, 50 }, + { 44, 44, 44, 44 }, + { 32, 32, 32, 32 }, + }, + { + { 55, 55, 55, 55 }, + { 54, 54, 54, 54 }, + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 53, 52, 52, 52 }, + { 50, 50, 48, 48 }, + { 44, 44, 44, 40 }, + { 32, 32, 32, 32 }, + }, + { + { 54, 52, 52, 52 }, + { 52, 52, 48, 48 }, + { 48, 48, 48, 40 }, + { 40, 40, 40, 40 }, + }, + { + { 55, 52, 52, 52 }, + { 54, 54, 48, 48 }, + { 52, 52, 52, 40 }, + { 48, 48, 48, 48 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 52, 52, 52, 52 }, + { 48, 48, 48, 48 }, + { 40, 40, 40, 40 }, + { 24, 24, 24, 24 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 51, 51, 51 }, + { 46, 46, 46, 46 }, + { 36, 36, 36, 36 }, + { 16, 16, 16, 16 }, + }, + { + { 49, 49, 49, 49 }, + { 42, 42, 42, 42 }, + { 28, 28, 28, 28 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, + { + { 51, 52, 52, 52 }, + { 46, 46, 48, 48 }, + { 36, 36, 36, 40 }, + { 16, 16, 16, 16 }, + }, + { + { 50, 52, 52, 52 }, + { 44, 44, 48, 48 }, + { 32, 32, 32, 40 }, + { 8, 8, 8, 8 }, + }, + { + { 49, 52, 52, 52 }, + { 42, 42, 48, 48 }, + { 28, 28, 28, 40 }, + { 0, 0, 0, 0 }, + }, +}; + +const uint8_t ff_vvc_gpm_weights[6][VVC_GPM_WEIGHT_SIZE * VVC_GPM_WEIGHT_SIZE] = { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + }, + { + 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + }, + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, +}; diff --git a/libavcodec/vvc/vvc_data.h b/libavcodec/vvc/vvc_data.h new file mode 100644 index 00000000000..1f9a463bc72 --- /dev/null +++ b/libavcodec/vvc/vvc_data.h @@ -0,0 +1,80 @@ +/* + * VVC shared tables + * + * 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 + */ + +#ifndef AVCODEC_VVC_VVC_DATA_H +#define AVCODEC_VVC_VVC_DATA_H + +#include + +extern const uint8_t ff_vvc_diag_scan_x[5][5][16 * 16]; +extern const uint8_t ff_vvc_diag_scan_y[5][5][16 * 16]; + +extern const uint8_t ff_vvc_scaling_pred_8[8 * 8]; +extern const uint8_t ff_vvc_scaling_pred_16[8 * 8]; +extern const int ff_vvc_scaling_list0[8 * 8]; + +extern const int8_t ff_vvc_dct8_4x4[4][4]; +extern const int8_t ff_vvc_dct8_8x8[8][8]; +extern const int8_t ff_vvc_dct8_16x16[16][16]; +extern const int8_t ff_vvc_dct8_32x32[32][32]; +extern const int8_t ff_vvc_dst7_4x4[4][4]; +extern const int8_t ff_vvc_dst7_8x8[8][8]; +extern const int8_t ff_vvc_dst7_16x16[16][16]; +extern const int8_t ff_vvc_dst7_32x32[32][32]; +extern const int8_t ff_vvc_lfnst_4x4[4][2][16][16]; +extern const int8_t ff_vvc_lfnst_8x8[4][2][16][48]; +extern const uint8_t ff_vvc_lfnst_tr_set_index[95]; +extern uint8_t ff_vvc_default_scale_m[64 * 64]; + +#define VVC_INTER_FILTER_TYPES 3 +#define VVC_INTER_LUMA_FACTS 16 +#define VVC_INTER_LUMA_TAPS 8 +#define VVC_INTER_CHROMA_FACTS 32 +#define VVC_INTER_CHROMA_TAPS 4 +#define VVC_INTER_LUMA_DMVR_FACTS 16 +#define VVC_INTER_LUMA_DMVR_TAPS 2 +extern const int8_t ff_vvc_inter_luma_filters[VVC_INTER_FILTER_TYPES][VVC_INTER_LUMA_FACTS][VVC_INTER_LUMA_TAPS]; +extern const int8_t ff_vvc_inter_chroma_filters[VVC_INTER_FILTER_TYPES][VVC_INTER_CHROMA_FACTS][VVC_INTER_CHROMA_TAPS]; +extern const int8_t ff_vvc_inter_luma_dmvr_filters[VVC_INTER_LUMA_DMVR_FACTS][VVC_INTER_LUMA_DMVR_TAPS]; + +#define VVC_INTRA_LUMA_TYPES 2 +#define VVC_INTRA_LUMA_FACTS 32 +#define VVC_INTRA_LUMA_TAPS 4 +extern const int8_t ff_vvc_intra_luma_filter[VVC_INTRA_LUMA_TYPES][VVC_INTRA_LUMA_FACTS][VVC_INTRA_LUMA_TAPS]; + +#define VVC_GPM_NUM_PARTITION 64 +#define VVC_GPM_NUM_ANGLES 32 +#define VVC_GPM_WEIGHT_SIZE 112 +extern const uint8_t ff_vvc_gpm_angle_idx[VVC_GPM_NUM_PARTITION]; +extern const uint8_t ff_vvc_gpm_distance_idx[VVC_GPM_NUM_PARTITION]; +extern const int8_t ff_vvc_gpm_distance_lut[VVC_GPM_NUM_ANGLES]; +extern const uint8_t ff_vvc_gpm_angle_to_mirror[VVC_GPM_NUM_ANGLES]; +extern const uint8_t ff_vvc_gpm_angle_to_weights_idx[VVC_GPM_NUM_ANGLES]; +extern const uint8_t ff_vvc_gpm_weights_offset_x[VVC_GPM_NUM_PARTITION][4][4]; +extern const uint8_t ff_vvc_gpm_weights_offset_y[VVC_GPM_NUM_PARTITION][4][4]; +extern const uint8_t ff_vvc_gpm_weights[6][VVC_GPM_WEIGHT_SIZE * VVC_GPM_WEIGHT_SIZE]; + +extern const int16_t ff_vvc_alf_fix_filt_coeff[64][12]; +extern const uint8_t ff_vvc_alf_class_to_filt_map[16][25]; +extern const uint8_t ff_vvc_alf_aps_class_to_filt_map[25]; + +const uint8_t* ff_vvc_get_mip_matrix(const int size_id, const int mode_idx); + +#endif /* AVCODEC_VVC_VVC_DATA_H */ diff --git a/libavcodec/vvc/vvc_filter.c b/libavcodec/vvc/vvc_filter.c new file mode 100644 index 00000000000..dded447bfac --- /dev/null +++ b/libavcodec/vvc/vvc_filter.c @@ -0,0 +1,1279 @@ +/* + * VVC filters + * + * Copyright (C) 2021 Nuo Mi + * + * 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/frame.h" + +#include "vvc_ctu.h" +#include "vvc_data.h" +#include "vvc_filter.h" +#include "vvc_refs.h" + +#define LEFT 0 +#define TOP 1 +#define RIGHT 2 +#define BOTTOM 3 +#define MAX_EDGES 4 + +#define DEFAULT_INTRA_TC_OFFSET 2 + +//Table 43 Derivation of threshold variables beta' and tc' from input Q +static const uint16_t tctable[66] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 4, 4, 4, 4, 5, 5, 5, 5, 7, 7, 8, 9, 10, + 10, 11, 13, 14, 15, 17, 19, 21, 24, 25, 29, 33, 36, 41, 45, 51, + 57, 64, 71, 80, 89, 100, 112, 125, 141, 157, 177, 198, 222, 250, 280, 314, + 352, 395, +}; + +//Table 43 Derivation of threshold variables beta' and tc' from input Q +static const uint8_t betatable[64] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24, + 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, + 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, +}; + +static int get_qPc(const VVCFrameContext *fc, const int x0, const int y0, const int chroma) +{ + const int x = x0 >> MIN_TU_LOG2; + const int y = y0 >> MIN_TU_LOG2; + const int min_tu_width = fc->ps.pps->min_tu_width; + return fc->tab.qp[chroma][x + y * min_tu_width]; +} + +static void copy_ctb(uint8_t *dst, const uint8_t *src, const int width, const int height, + const ptrdiff_t dst_stride, const ptrdiff_t src_stride) +{ + for (int y = 0; y < height; y++) { + memcpy(dst, src, width); + + dst += dst_stride; + src += src_stride; + } +} + +static void copy_pixel(uint8_t *dst, const uint8_t *src, const int pixel_shift) +{ + if (pixel_shift) + *(uint16_t *)dst = *(uint16_t *)src; + else + *dst = *src; +} + +static void copy_vert(uint8_t *dst, const uint8_t *src, const int pixel_shift, const int height, + const ptrdiff_t dst_stride, const ptrdiff_t src_stride) +{ + int i; + if (pixel_shift == 0) { + for (i = 0; i < height; i++) { + *dst = *src; + dst += dst_stride; + src += src_stride; + } + } else { + for (i = 0; i < height; i++) { + *(uint16_t *)dst = *(uint16_t *)src; + dst += dst_stride; + src += src_stride; + } + } +} + +static void copy_ctb_to_hv(VVCFrameContext *fc, const uint8_t *src, + const ptrdiff_t src_stride, const int x, const int y, const int width, const int height, + const int c_idx, const int x_ctb, const int y_ctb, const int top) +{ + const int ps = fc->ps.sps->pixel_shift; + const int w = fc->ps.pps->width >> fc->ps.sps->hshift[c_idx]; + const int h = fc->ps.pps->height >> fc->ps.sps->vshift[c_idx]; + + if (top) { + /* top */ + memcpy(fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb) * w + x) << ps), + src, width << ps); + } else { + /* bottom */ + memcpy(fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb + 1) * w + x) << ps), + src + src_stride * (height - 1), width << ps); + + /* copy vertical edges */ + copy_vert(fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb) * h + y) << ps), src, ps, height, 1 << ps, src_stride); + copy_vert(fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb + 1) * h + y) << ps), src + ((width - 1) << ps), ps, height, 1 << ps, src_stride); + } +} + +static void sao_copy_ctb_to_hv(VVCLocalContext *lc, const int rx, const int ry, const int top) +{ + VVCFrameContext *fc = lc->fc; + const int ctb_size_y = fc->ps.sps->ctb_size_y; + const int x0 = rx << fc->ps.sps->ctb_log2_size_y; + const int y0 = ry << fc->ps.sps->ctb_log2_size_y; + + for (int c_idx = 0; c_idx < (fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1); c_idx++) { + const int x = x0 >> fc->ps.sps->hshift[c_idx]; + const int y = y0 >> fc->ps.sps->vshift[c_idx]; + const ptrdiff_t src_stride = fc->frame->linesize[c_idx]; + const int ctb_size_h = ctb_size_y >> fc->ps.sps->hshift[c_idx]; + const int ctb_size_v = ctb_size_y >> fc->ps.sps->vshift[c_idx]; + const int width = FFMIN(ctb_size_h, (fc->ps.pps->width >> fc->ps.sps->hshift[c_idx]) - x); + const int height = FFMIN(ctb_size_v, (fc->ps.pps->height >> fc->ps.sps->vshift[c_idx]) - y); + const uint8_t *src = &fc->frame->data[c_idx][y * src_stride + (x << fc->ps.sps->pixel_shift)]; + copy_ctb_to_hv(fc, src, src_stride, x, y, width, height, c_idx, rx, ry, top); + } +} + +void ff_vvc_sao_copy_ctb_to_hv(VVCLocalContext *lc, const int rx, const int ry, const int last_row) +{ + if (ry) + sao_copy_ctb_to_hv(lc, rx, ry - 1, 0); + + sao_copy_ctb_to_hv(lc, rx, ry, 1); + + if (last_row) + sao_copy_ctb_to_hv(lc, rx, ry, 0); +} + +void ff_vvc_sao_filter(VVCLocalContext *lc, int x, int y) +{ + VVCFrameContext *fc = lc->fc; + const int ctb_size_y = fc->ps.sps->ctb_size_y; + static const uint8_t sao_tab[16] = { 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8 }; + int c_idx; + int edges[4]; // 0 left 1 top 2 right 3 bottom + const int x_ctb = x >> fc->ps.sps->ctb_log2_size_y; + const int y_ctb = y >> fc->ps.sps->ctb_log2_size_y; + const SAOParams *sao = &CTB(fc->tab.sao, x_ctb, y_ctb); + // flags indicating unfilterable edges + uint8_t vert_edge[] = { 0, 0 }; + uint8_t horiz_edge[] = { 0, 0 }; + uint8_t diag_edge[] = { 0, 0, 0, 0 }; + const uint8_t lfase = fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag; + const uint8_t no_tile_filter = fc->ps.pps->r->num_tiles_in_pic > 1 && + !fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag; + const uint8_t restore = no_tile_filter || !lfase; + uint8_t left_tile_edge = 0; + uint8_t right_tile_edge = 0; + uint8_t up_tile_edge = 0; + uint8_t bottom_tile_edge = 0; + + edges[LEFT] = x_ctb == 0; + edges[TOP] = y_ctb == 0; + edges[RIGHT] = x_ctb == fc->ps.pps->ctb_width - 1; + edges[BOTTOM] = y_ctb == fc->ps.pps->ctb_height - 1; + + if (restore) { + if (!edges[LEFT]) { + left_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_col_bd[x_ctb] == x_ctb; + vert_edge[0] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb - 1, y_ctb)) || left_tile_edge; + } + if (!edges[RIGHT]) { + right_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_col_bd[x_ctb] != fc->ps.pps->ctb_to_col_bd[x_ctb + 1]; + vert_edge[1] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb)) || right_tile_edge; + } + if (!edges[TOP]) { + up_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_row_bd[y_ctb] == y_ctb; + horiz_edge[0] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb, y_ctb - 1)) || up_tile_edge; + } + if (!edges[BOTTOM]) { + bottom_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_row_bd[y_ctb] != fc->ps.pps->ctb_to_row_bd[y_ctb + 1]; + horiz_edge[1] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb, y_ctb + 1)) || bottom_tile_edge; + } + if (!edges[LEFT] && !edges[TOP]) { + diag_edge[0] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb - 1, y_ctb - 1)) || left_tile_edge || up_tile_edge; + } + if (!edges[TOP] && !edges[RIGHT]) { + diag_edge[1] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb - 1)) || right_tile_edge || up_tile_edge; + } + if (!edges[RIGHT] && !edges[BOTTOM]) { + diag_edge[2] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb + 1)) || right_tile_edge || bottom_tile_edge; + } + if (!edges[LEFT] && !edges[BOTTOM]) { + diag_edge[3] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb - 1, y_ctb + 1)) || left_tile_edge || bottom_tile_edge; + } + } + + for (c_idx = 0; c_idx < (fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1); c_idx++) { + int x0 = x >> fc->ps.sps->hshift[c_idx]; + int y0 = y >> fc->ps.sps->vshift[c_idx]; + ptrdiff_t src_stride = fc->frame->linesize[c_idx]; + int ctb_size_h = ctb_size_y >> fc->ps.sps->hshift[c_idx]; + int ctb_size_v = ctb_size_y >> fc->ps.sps->vshift[c_idx]; + int width = FFMIN(ctb_size_h, (fc->ps.pps->width >> fc->ps.sps->hshift[c_idx]) - x0); + int height = FFMIN(ctb_size_v, (fc->ps.pps->height >> fc->ps.sps->vshift[c_idx]) - y0); + int tab = sao_tab[(FFALIGN(width, 8) >> 3) - 1]; + uint8_t *src = &fc->frame->data[c_idx][y0 * src_stride + (x0 << fc->ps.sps->pixel_shift)]; + ptrdiff_t dst_stride; + uint8_t *dst; + + switch (sao->type_idx[c_idx]) { + case SAO_BAND: + fc->vvcdsp.sao.band_filter[tab](src, src, src_stride, src_stride, + sao->offset_val[c_idx], sao->band_position[c_idx], width, height); + break; + case SAO_EDGE: + { + const int w = fc->ps.pps->width >> fc->ps.sps->hshift[c_idx]; + const int h = fc->ps.pps->height >> fc->ps.sps->vshift[c_idx]; + const int sh = fc->ps.sps->pixel_shift; + + dst_stride = 2*MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE; + dst = lc->sao_buffer + dst_stride + AV_INPUT_BUFFER_PADDING_SIZE; + + if (!edges[TOP]) { + const int left = 1 - edges[LEFT]; + const int right = 1 - edges[RIGHT]; + const uint8_t *src1; + uint8_t *dst1; + int pos = 0; + + dst1 = dst - dst_stride - (left << sh); + src1 = fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb - 1) * w + x0 - left) << sh); + if (left) { + copy_pixel(dst1, src1, sh); + pos += (1 << sh); + } + memcpy(dst1 + pos, src1 + pos, width << sh); + if (right) { + pos += width << sh; + copy_pixel(dst1 + pos, src1 + pos, sh); + } + } + if (!edges[BOTTOM]) { + const int left = 1 - edges[LEFT]; + const int right = 1 - edges[RIGHT]; + const uint8_t *src1; + uint8_t *dst1; + int pos = 0; + + dst1 = dst + height * dst_stride - (left << sh); + src1 = fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb + 2) * w + x0 - left) << sh); + if (left) { + copy_pixel(dst1, src1, sh); + pos += (1 << sh); + } + memcpy(dst1 + pos, src1 + pos, width << sh); + if (right) { + pos += width << sh; + copy_pixel(dst1 + pos, src1 + pos, sh); + } + } + if (!edges[LEFT]) { + copy_vert(dst - (1 << sh), + fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb - 1) * h + y0) << sh), + sh, height, dst_stride, 1 << sh); + } + if (!edges[RIGHT]) { + copy_vert(dst + (width << sh), + fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb + 2) * h + y0) << sh), + sh, height, dst_stride, 1 << sh); + } + + copy_ctb(dst, src, width << sh, height, dst_stride, src_stride); + fc->vvcdsp.sao.edge_filter[tab](src, dst, src_stride, sao->offset_val[c_idx], + sao->eo_class[c_idx], width, height); + fc->vvcdsp.sao.edge_restore[restore](src, dst, src_stride, dst_stride, + sao, edges, width, height, c_idx, vert_edge, horiz_edge, diag_edge); + break; + } + } + } +} + +#define TAB_BS(t, x, y) (t)[((y) >> 2) * (fc->tab.sz.bs_width) + ((x) >> 2)] +#define TAB_MAX_LEN(t, x, y) (t)[((y) >> 2) * (fc->tab.sz.bs_width) + ((x) >> 2)] + +//8 samples a time +#define DEBLOCK_STEP 8 +#define LUMA_GRID 4 +#define CHROMA_GRID 8 + +static int boundary_strength(const VVCLocalContext *lc, const MvField *curr, const MvField *neigh, + const RefPicList *neigh_rpl) +{ + RefPicList *rpl = lc->sc->rpl; + + if (curr->pred_flag == PF_IBC) + return FFABS(neigh->mv[0].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[0].y) >= 8; + + if (curr->pred_flag == PF_BI && neigh->pred_flag == PF_BI) { + // same L0 and L1 + if (rpl[0].list[curr->ref_idx[0]] == neigh_rpl[0].list[neigh->ref_idx[0]] && + rpl[0].list[curr->ref_idx[0]] == rpl[1].list[curr->ref_idx[1]] && + neigh_rpl[0].list[neigh->ref_idx[0]] == neigh_rpl[1].list[neigh->ref_idx[1]]) { + if ((FFABS(neigh->mv[0].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[0].y) >= 8 || + FFABS(neigh->mv[1].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[1].y) >= 8) && + (FFABS(neigh->mv[1].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[0].y) >= 8 || + FFABS(neigh->mv[0].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[1].y) >= 8)) + return 1; + else + return 0; + } else if (neigh_rpl[0].list[neigh->ref_idx[0]] == rpl[0].list[curr->ref_idx[0]] && + neigh_rpl[1].list[neigh->ref_idx[1]] == rpl[1].list[curr->ref_idx[1]]) { + if (FFABS(neigh->mv[0].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[0].y) >= 8 || + FFABS(neigh->mv[1].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[1].y) >= 8) + return 1; + else + return 0; + } else if (neigh_rpl[1].list[neigh->ref_idx[1]] == rpl[0].list[curr->ref_idx[0]] && + neigh_rpl[0].list[neigh->ref_idx[0]] == rpl[1].list[curr->ref_idx[1]]) { + if (FFABS(neigh->mv[1].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[0].y) >= 8 || + FFABS(neigh->mv[0].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[1].y) >= 8) + return 1; + else + return 0; + } else { + return 1; + } + } else if ((curr->pred_flag != PF_BI) && (neigh->pred_flag != PF_BI)){ // 1 MV + Mv A, B; + int ref_A, ref_B; + + if (curr->pred_flag & 1) { + A = curr->mv[0]; + ref_A = rpl[0].list[curr->ref_idx[0]]; + } else { + A = curr->mv[1]; + ref_A = rpl[1].list[curr->ref_idx[1]]; + } + + if (neigh->pred_flag & 1) { + B = neigh->mv[0]; + ref_B = neigh_rpl[0].list[neigh->ref_idx[0]]; + } else { + B = neigh->mv[1]; + ref_B = neigh_rpl[1].list[neigh->ref_idx[1]]; + } + + if (ref_A == ref_B) { + if (FFABS(A.x - B.x) >= 8 || FFABS(A.y - B.y) >= 8) + return 1; + else + return 0; + } else + return 1; + } + + return 1; +} + +//part of 8.8.3.3 Derivation process of transform block boundary +static void derive_max_filter_length_luma(const VVCFrameContext *fc, const int qx, const int qy, + const int is_intra, const int has_subblock, const int vertical, uint8_t *max_len_p, uint8_t *max_len_q) +{ + const int px = vertical ? qx - 1 : qx; + const int py = !vertical ? qy - 1 : qy; + const uint8_t *tb_size = vertical ? fc->tab.tb_width[LUMA] : fc->tab.tb_height[LUMA]; + const int size_p = tb_size[(py >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (px >> MIN_TU_LOG2)]; + const int size_q = tb_size[(qy >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (qx >> MIN_TU_LOG2)]; + const int min_cb_log2 = fc->ps.sps->min_cb_log2_size_y; + const int off_p = (py >> min_cb_log2) * fc->ps.pps->min_cb_width + (px >> min_cb_log2); + if (size_p <= 4 || size_q <= 4) { + *max_len_p = *max_len_q = 1; + } else { + *max_len_p = *max_len_q = 3; + if (size_p >= 32) + *max_len_p = 7; + if (size_q >= 32) + *max_len_q = 7; + } + if (has_subblock) + *max_len_q = FFMIN(5, *max_len_q); + if (fc->tab.msf[off_p] || fc->tab.iaf[off_p]) + *max_len_p = FFMIN(5, *max_len_p); +} + +static void vvc_deblock_subblock_bs_vertical(const VVCLocalContext *lc, + const int cb_x, const int cb_y, const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + const MvField *tab_mvf = fc->tab.mvf; + const RefPicList *rpl = lc->sc->rpl; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int log2_min_pu_size = MIN_PU_LOG2; + + // bs for TU internal vertical PU boundaries + for (int j = 0; j < height; j += 4) { + const int y_pu = (y0 + j) >> log2_min_pu_size; + + for (int i = 8 - ((x0 - cb_x) % 8); i < width; i += 8) { + const int xp_pu = (x0 + i - 1) >> log2_min_pu_size; + const int xq_pu = (x0 + i) >> log2_min_pu_size; + const MvField *left = &tab_mvf[y_pu * min_pu_width + xp_pu]; + const MvField *curr = &tab_mvf[y_pu * min_pu_width + xq_pu]; + const int x = x0 + i; + const int y = y0 + j; + const int bs = boundary_strength(lc, curr, left, rpl); + uint8_t max_len_p = 0, max_len_q = 0; + + TAB_BS(fc->tab.vertical_bs[LUMA], x, y) = bs; + + if (i == 4 || i == width - 4) + max_len_p = max_len_q = 1; + else if (i == 8 || i == width - 8) + max_len_p = max_len_q = 2; + else + max_len_p = max_len_q = 3; + + TAB_MAX_LEN(fc->tab.vertical_p, x, y) = max_len_p; + TAB_MAX_LEN(fc->tab.vertical_q, x, y) = max_len_q; + } + } +} + +static void vvc_deblock_subblock_bs_horizontal(const VVCLocalContext *lc, + const int cb_x, const int cb_y, const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + const MvField* tab_mvf = fc->tab.mvf; + const RefPicList* rpl = lc->sc->rpl; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int log2_min_pu_size = MIN_PU_LOG2; + + // bs for TU internal horizontal PU boundaries + for (int j = 8 - ((y0 - cb_y) % 8); j < height; j += 8) { + int yp_pu = (y0 + j - 1) >> log2_min_pu_size; + int yq_pu = (y0 + j) >> log2_min_pu_size; + + for (int i = 0; i < width; i += 4) { + const int x_pu = (x0 + i) >> log2_min_pu_size; + const MvField *top = &tab_mvf[yp_pu * min_pu_width + x_pu]; + const MvField *curr = &tab_mvf[yq_pu * min_pu_width + x_pu]; + const int x = x0 + i; + const int y = y0 + j; + const int bs = boundary_strength(lc, curr, top, rpl); + uint8_t max_len_p = 0, max_len_q = 0; + + TAB_BS(fc->tab.horizontal_bs[LUMA], x, y) = bs; + + //fixme: + //edgeTbFlags[ x − sbW ][ y ] is equal to 1 + //edgeTbFlags[ x + sbW ][ y ] is equal to 1 + if (j == 4 || j == height - 4) + max_len_p = max_len_q = 1; + else if (j == 8 || j == height - 8) + max_len_p = max_len_q = 2; + else + max_len_p = max_len_q = 3; + TAB_MAX_LEN(fc->tab.horizontal_p, x, y) = max_len_p; + TAB_MAX_LEN(fc->tab.horizontal_q, x, y) = max_len_q; + } + } +} + +static av_always_inline int deblock_bs(const VVCLocalContext *lc, + const int x_p, const int y_p, const int x_q, const int y_q, + const RefPicList *rpl_p, const int c_idx, const int off_to_cb, const uint8_t has_sub_block) +{ + const VVCFrameContext *fc = lc->fc; + const MvField *tab_mvf = fc->tab.mvf; + const int log2_min_pu_size = MIN_PU_LOG2; + const int log2_min_tu_size = MIN_TU_LOG2; + const int log2_min_cb_size = fc->ps.sps->min_cb_log2_size_y; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_tu_width = fc->ps.pps->min_tu_width; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int pu_p = (y_p >> log2_min_pu_size) * min_pu_width + (x_p >> log2_min_pu_size); + const int pu_q = (y_q >> log2_min_pu_size) * min_pu_width + (x_q >> log2_min_pu_size); + const MvField *mvf_p = &tab_mvf[pu_p]; + const MvField *mvf_q = &tab_mvf[pu_q]; + const uint8_t chroma = !!c_idx; + const int tu_p = (y_p >> log2_min_tu_size) * min_tu_width + (x_p >> log2_min_tu_size); + const int tu_q = (y_q >> log2_min_tu_size) * min_tu_width + (x_q >> log2_min_tu_size); + const uint8_t pcmf = fc->tab.pcmf[chroma][tu_p] && fc->tab.pcmf[chroma][tu_q]; + const int cb_p = (y_p >> log2_min_cb_size) * min_cb_width + (x_p >> log2_min_cb_size); + const int cb_q = (y_q >> log2_min_cb_size) * min_cb_width + (x_q >> log2_min_cb_size); + const uint8_t intra = fc->tab.cpm[chroma][cb_p] == MODE_INTRA || fc->tab.cpm[chroma][cb_q] == MODE_INTRA; + const uint8_t same_mode = fc->tab.cpm[chroma][cb_p] == fc->tab.cpm[chroma][cb_q]; + + if (pcmf) + return 0; + + if (intra || mvf_p->ciip_flag || mvf_q->ciip_flag) + return 2; + + if (chroma) { + return fc->tab.tu_coded_flag[c_idx][tu_p] || + fc->tab.tu_coded_flag[c_idx][tu_q] || + fc->tab.tu_joint_cbcr_residual_flag[tu_p] || + fc->tab.tu_joint_cbcr_residual_flag[tu_q]; + } + + if (fc->tab.tu_coded_flag[LUMA][tu_p] || fc->tab.tu_coded_flag[LUMA][tu_q]) + return 1; + + if ((off_to_cb && ((off_to_cb % 8) || !has_sub_block))) + return 0; // inside a cu, not aligned to 8 or with no subblocks + + if (!same_mode) + return 1; + + return boundary_strength(lc, mvf_q, mvf_p, rpl_p); +} + +static void vvc_deblock_bs_luma_vertical(const VVCLocalContext *lc, + const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + const MvField *tab_mvf = fc->tab.mvf; + const int log2_min_pu_size = MIN_PU_LOG2; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_cb_log2 = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int is_intra = tab_mvf[(y0 >> log2_min_pu_size) * min_pu_width + + (x0 >> log2_min_pu_size)].pred_flag == PF_INTRA; + int boundary_left; + int has_vertical_sb = 0; + + const int off_q = (y0 >> min_cb_log2) * min_cb_width + (x0 >> min_cb_log2); + const int cb_x = fc->tab.cb_pos_x[LUMA][off_q]; + const int cb_y = fc->tab.cb_pos_y[LUMA][off_q]; + const int cb_width = fc->tab.cb_width[LUMA][off_q]; + const int off_x = cb_x - x0; + + if (!is_intra) { + if (fc->tab.msf[off_q] || fc->tab.iaf[off_q]) + has_vertical_sb = cb_width > 8; + } + + // bs for vertical TU boundaries + boundary_left = x0 > 0 && !(x0 & 3); + if (boundary_left && + ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag && + lc->boundary_flags & BOUNDARY_LEFT_SLICE && + (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) || + (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag && + lc->boundary_flags & BOUNDARY_LEFT_TILE && + (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0))) + boundary_left = 0; + + if (boundary_left) { + const RefPicList *rpl_left = + (lc->boundary_flags & BOUNDARY_LEFT_SLICE) ? ff_vvc_get_ref_list(fc, fc->ref, x0 - 1, y0) : lc->sc->rpl; + for (int i = 0; i < height; i += 4) { + uint8_t max_len_p, max_len_q; + const int bs = deblock_bs(lc, x0 - 1, y0 + i, x0, y0 + i, rpl_left, 0, off_x, has_vertical_sb); + + TAB_BS(fc->tab.vertical_bs[LUMA], x0, (y0 + i)) = bs; + + derive_max_filter_length_luma(fc, x0, y0 + i, is_intra, has_vertical_sb, 1, &max_len_p, &max_len_q); + TAB_MAX_LEN(fc->tab.vertical_p, x0, y0 + i) = max_len_p; + TAB_MAX_LEN(fc->tab.vertical_q, x0, y0 + i) = max_len_q; + } + } + + if (!is_intra) { + if (fc->tab.msf[off_q] || fc->tab.iaf[off_q]) + vvc_deblock_subblock_bs_vertical(lc, cb_x, cb_y, x0, y0, width, height); + } +} + +static void vvc_deblock_bs_luma_horizontal(const VVCLocalContext *lc, + const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + const MvField *tab_mvf = fc->tab.mvf; + const int log2_min_pu_size = MIN_PU_LOG2; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_cb_log2 = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int is_intra = tab_mvf[(y0 >> log2_min_pu_size) * min_pu_width + + (x0 >> log2_min_pu_size)].pred_flag == PF_INTRA; + int boundary_upper; + int has_horizontal_sb = 0; + + const int off_q = (y0 >> min_cb_log2) * min_cb_width + (x0 >> min_cb_log2); + const int cb_x = fc->tab.cb_pos_x[LUMA][off_q]; + const int cb_y = fc->tab.cb_pos_y[LUMA][off_q]; + const int cb_height = fc->tab.cb_height[LUMA][off_q]; + const int off_y = y0 - cb_y; + + if (!is_intra) { + if (fc->tab.msf[off_q] || fc->tab.iaf[off_q]) + has_horizontal_sb = cb_height > 8; + } + + boundary_upper = y0 > 0 && !(y0 & 3); + if (boundary_upper && + ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag && + lc->boundary_flags & BOUNDARY_UPPER_SLICE && + (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) || + (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag && + lc->boundary_flags & BOUNDARY_UPPER_TILE && + (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0))) + boundary_upper = 0; + + if (boundary_upper) { + const RefPicList *rpl_top = + (lc->boundary_flags & BOUNDARY_UPPER_SLICE) ? ff_vvc_get_ref_list(fc, fc->ref, x0, y0 - 1) : lc->sc->rpl; + + for (int i = 0; i < width; i += 4) { + uint8_t max_len_p, max_len_q; + const int bs = deblock_bs(lc, x0 + i, y0 - 1, x0 + i, y0, rpl_top, 0, off_y, has_horizontal_sb); + + TAB_BS(fc->tab.horizontal_bs[LUMA], x0 + i, y0) = bs; + + derive_max_filter_length_luma(fc, x0 + i, y0, is_intra, has_horizontal_sb, 0, &max_len_p, &max_len_q); + TAB_MAX_LEN(fc->tab.horizontal_p, x0 + i, y0) = max_len_p; + TAB_MAX_LEN(fc->tab.horizontal_q, x0 + i, y0) = max_len_q; + } + } + + if (!is_intra) { + if (fc->tab.msf[off_q] || fc->tab.iaf[off_q]) + vvc_deblock_subblock_bs_horizontal(lc, cb_x, cb_y, x0, y0, width, height); + } +} + +static void vvc_deblock_bs_chroma_vertical(const VVCLocalContext *lc, + const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + int boundary_left; + + // bs for vertical TU boundaries + boundary_left = x0 > 0 && !(x0 & ((CHROMA_GRID << fc->ps.sps->hshift[1]) - 1)); + if (boundary_left && + ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag && + lc->boundary_flags & BOUNDARY_LEFT_SLICE && + (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) || + (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag && + lc->boundary_flags & BOUNDARY_LEFT_TILE && + (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0))) + boundary_left = 0; + + if (boundary_left) { + for (int i = 0; i < height; i += 2) { + for (int c_idx = CB; c_idx <= CR; c_idx++) { + const int bs = deblock_bs(lc, x0 - 1, y0 + i, x0, y0 + i, NULL, c_idx, 0, 0); + + TAB_BS(fc->tab.vertical_bs[c_idx], x0, (y0 + i)) = bs; + } + } + } +} + +static void vvc_deblock_bs_chroma_horizontal(const VVCLocalContext *lc, + const int x0, const int y0, const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + int boundary_upper; + + boundary_upper = y0 > 0 && !(y0 & ((CHROMA_GRID << fc->ps.sps->vshift[1]) - 1)); + if (boundary_upper && + ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag && + lc->boundary_flags & BOUNDARY_UPPER_SLICE && + (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) || + (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag && + lc->boundary_flags & BOUNDARY_UPPER_TILE && + (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0))) + boundary_upper = 0; + + if (boundary_upper) { + for (int i = 0; i < width; i += 2) { + for (int c_idx = CB; c_idx <= CR; c_idx++) { + const int bs = deblock_bs(lc, x0 + i, y0 - 1, x0 + i, y0, NULL, c_idx, 0, 0); + + TAB_BS(fc->tab.horizontal_bs[c_idx], x0 + i, y0) = bs; + } + } + } +} + +typedef void (*deblock_bs_fn)(const VVCLocalContext *lc, const int x0, const int y0, + const int width, const int height); + +static void vvc_deblock_bs(const VVCLocalContext *lc, const int x0, const int y0, const int vertical) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int ctb_size = sps->ctb_size_y; + const int x_end = FFMIN(x0 + ctb_size, pps->width) >> MIN_TU_LOG2; + const int y_end = FFMIN(y0 + ctb_size, pps->height) >> MIN_TU_LOG2; + deblock_bs_fn deblock_bs[2][2] = { + { vvc_deblock_bs_luma_horizontal, vvc_deblock_bs_chroma_horizontal }, + { vvc_deblock_bs_luma_vertical, vvc_deblock_bs_chroma_vertical } + }; + + for (int is_chroma = 0; is_chroma <= 1; is_chroma++) { + const int hs = sps->hshift[is_chroma]; + const int vs = sps->vshift[is_chroma]; + for (int y = y0 >> MIN_TU_LOG2; y < y_end; y++) { + for (int x = x0 >> MIN_TU_LOG2; x < x_end; x++) { + const int off = y * fc->ps.pps->min_tu_width + x; + if ((fc->tab.tb_pos_x0[is_chroma][off] >> MIN_TU_LOG2) == x && (fc->tab.tb_pos_y0[is_chroma][off] >> MIN_TU_LOG2) == y) { + deblock_bs[vertical][is_chroma](lc, x << MIN_TU_LOG2, y << MIN_TU_LOG2, + fc->tab.tb_width[is_chroma][off] << hs, fc->tab.tb_height[is_chroma][off] << vs); + } + } + } + } +} + +//part of 8.8.3.3 Derivation process of transform block boundary +static void max_filter_length_luma(const VVCFrameContext *fc, const int qx, const int qy, + const int vertical, uint8_t *max_len_p, uint8_t *max_len_q) +{ + const uint8_t *tab_len_p = vertical ? fc->tab.vertical_p : fc->tab.horizontal_p; + const uint8_t *tab_len_q = vertical ? fc->tab.vertical_q : fc->tab.horizontal_q; + *max_len_p = TAB_MAX_LEN(tab_len_p, qx, qy); + *max_len_q = TAB_MAX_LEN(tab_len_q, qx, qy); +} + +//part of 8.8.3.3 Derivation process of transform block boundary +static void max_filter_length_chroma(const VVCFrameContext *fc, const int qx, const int qy, + const int vertical, const int horizontal_ctu_edge, const int bs, uint8_t *max_len_p, uint8_t *max_len_q) +{ + const int px = vertical ? qx - 1 : qx; + const int py = !vertical ? qy - 1 : qy; + const uint8_t *tb_size = vertical ? fc->tab.tb_width[CHROMA] : fc->tab.tb_height[CHROMA]; + + const int size_p = tb_size[(py >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (px >> MIN_TU_LOG2)]; + const int size_q = tb_size[(qy >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (qx >> MIN_TU_LOG2)]; + if (size_p >= 8 && size_q >= 8) { + *max_len_p = *max_len_q = 3; + if (horizontal_ctu_edge) + *max_len_p = 1; + } else { + //part of 8.8.3.6.4 Decision process for chroma block edges + *max_len_p = *max_len_q = (bs == 2); + } +} + +static void max_filter_length(const VVCFrameContext *fc, const int qx, const int qy, + const int c_idx, const int vertical, const int horizontal_ctu_edge, const int bs, uint8_t *max_len_p, uint8_t *max_len_q) +{ + if (!c_idx) + max_filter_length_luma(fc, qx, qy, vertical, max_len_p, max_len_q); + else + max_filter_length_chroma(fc, qx, qy, vertical, horizontal_ctu_edge, bs, max_len_p, max_len_q); +} + +#define TC_CALC(qp, bs) \ + tctable[av_clip((qp) + DEFAULT_INTRA_TC_OFFSET * ((bs) - 1) + \ + (tc_offset & -2), \ + 0, MAX_QP + DEFAULT_INTRA_TC_OFFSET)] + +// part of 8.8.3.6.2 Decision process for luma block edges +static int get_qp_y(const VVCFrameContext *fc, const uint8_t *src, const int x, const int y, const int vertical) +{ + const VVCSPS *sps = fc->ps.sps; + const int qp = (ff_vvc_get_qPy(fc, x - vertical, y - !vertical) + ff_vvc_get_qPy(fc, x, y) + 1) >> 1; + int qp_offset = 0; + int level; + + if (!sps->r->sps_ladf_enabled_flag) + return qp; + + level = fc->vvcdsp.lf.ladf_level[vertical](src, fc->frame->linesize[LUMA]); + qp_offset = sps->r->sps_ladf_lowest_interval_qp_offset; + for (int i = 0; i < sps->num_ladf_intervals - 1 && level > sps->ladf_interval_lower_bound[i + 1]; i++) + qp_offset = sps->r->sps_ladf_qp_offset[i]; + + return qp + qp_offset; +} + +// part of 8.8.3.6.2 Decision process for luma block edges +static int get_qp_c(const VVCFrameContext *fc, const int x, const int y, const int c_idx, const int vertical) +{ + const VVCSPS *sps = fc->ps.sps; + return (get_qPc(fc, x - vertical, y - !vertical, c_idx) + get_qPc(fc, x, y, c_idx) - 2 * sps->qp_bd_offset + 1) >> 1; +} + +static int get_qp(const VVCFrameContext *fc, const uint8_t *src, const int x, const int y, const int c_idx, const int vertical) +{ + if (!c_idx) + return get_qp_y(fc, src, x, y, vertical); + return get_qp_c(fc, x, y, c_idx, vertical); +} + +void ff_vvc_deblock_vertical(const VVCLocalContext *lc, int x0, int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int c_end = sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + uint8_t *src; + int x, y, qp; + + //not use this yet, may needed by plt. + const uint8_t no_p[4] = { 0 }; + const uint8_t no_q[4] = { 0 } ; + + const int ctb_log2_size_y = fc->ps.sps->ctb_log2_size_y; + int x_end, y_end; + const int ctb_size = 1 << ctb_log2_size_y; + const int ctb = (x0 >> ctb_log2_size_y) + + (y0 >> ctb_log2_size_y) * fc->ps.pps->ctb_width; + const DBParams *params = fc->tab.deblock + ctb; + + vvc_deblock_bs(lc, x0, y0, 1); + + x_end = x0 + ctb_size; + if (x_end > fc->ps.pps->width) + x_end = fc->ps.pps->width; + y_end = y0 + ctb_size; + if (y_end > fc->ps.pps->height) + y_end = fc->ps.pps->height; + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int grid = c_idx ? (CHROMA_GRID << hs) : LUMA_GRID; + const int tc_offset = params->tc_offset[c_idx]; + const int beta_offset = params->beta_offset[c_idx]; + + for (y = y0; y < y_end; y += (DEBLOCK_STEP << vs)) { + for (x = x0 ? x0 : grid; x < x_end; x += grid) { + int32_t bs[4], beta[4], tc[4], all_zero_bs = 1; + uint8_t max_len_p[4], max_len_q[4]; + + for (int i = 0; i < DEBLOCK_STEP >> (2 - vs); i++) { + const int dy = i << 2; + bs[i] = (y + dy < y_end) ? TAB_BS(fc->tab.vertical_bs[c_idx], x, y + dy) : 0; + if (bs[i]) { + src = &fc->frame->data[c_idx][((y + dy) >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)]; + qp = get_qp(fc, src, x, y + dy, c_idx, 1); + + beta[i] = betatable[av_clip(qp + beta_offset, 0, MAX_QP)]; + + max_filter_length(fc, x, y + dy, c_idx, 1, 0, bs[i], &max_len_p[i], &max_len_q[i]); + all_zero_bs = 0; + } + tc[i] = bs[i] ? TC_CALC(qp, bs[i]) : 0; + } + + if (!all_zero_bs) { + src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)]; + if (!c_idx) { + fc->vvcdsp.lf.filter_luma[1](src, fc->frame->linesize[c_idx], + beta, tc, no_p, no_q, max_len_p, max_len_q, 0); + } else { + fc->vvcdsp.lf.filter_chroma[1](src, fc->frame->linesize[c_idx], + beta, tc, no_p, no_q, max_len_p, max_len_q, vs); + } + } + } + } + } +} + +void ff_vvc_deblock_horizontal(const VVCLocalContext *lc, int x0, int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + uint8_t* src; + int x, y, qp; + + //not use this yet, may needed by plt. + const uint8_t no_p[4] = { 0 }; + const uint8_t no_q[4] = { 0 } ; + + const int ctb_log2_size_y = fc->ps.sps->ctb_log2_size_y; + int x_end, y_end; + const int ctb_size = 1 << ctb_log2_size_y; + const int ctb = (x0 >> ctb_log2_size_y) + + (y0 >> ctb_log2_size_y) * fc->ps.pps->ctb_width; + const DBParams *params = fc->tab.deblock + ctb; + + vvc_deblock_bs(lc, x0, y0, 0); + + x_end = x0 + ctb_size; + if (x_end > fc->ps.pps->width) + x_end = fc->ps.pps->width; + y_end = y0 + ctb_size; + if (y_end > fc->ps.pps->height) + y_end = fc->ps.pps->height; + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int grid = c_idx ? (CHROMA_GRID << vs) : LUMA_GRID; + const int beta_offset = params->beta_offset[c_idx]; + const int tc_offset = params->tc_offset[c_idx]; + + for (y = y0; y < y_end; y += grid) { + const uint8_t horizontal_ctu_edge = !(y % fc->ps.sps->ctb_size_y); + if (!y) + continue; + + for (x = x0 ? x0: 0; x < x_end; x += (DEBLOCK_STEP << hs)) { + int32_t bs[4], beta[4], tc[4], all_zero_bs = 1; + uint8_t max_len_p[4], max_len_q[4]; + + for (int i = 0; i < DEBLOCK_STEP >> (2 - hs); i++) { + const int dx = i << 2; + + bs[i] = (x + dx < x_end) ? TAB_BS(fc->tab.horizontal_bs[c_idx], x + dx, y) : 0; + if (bs[i]) { + src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + (((x + dx)>> hs) << fc->ps.sps->pixel_shift)]; + qp = get_qp(fc, src, x + dx, y, c_idx, 0); + + beta[i] = betatable[av_clip(qp + beta_offset, 0, MAX_QP)]; + + max_filter_length(fc, x + dx, y, c_idx, 0, horizontal_ctu_edge, bs[i], &max_len_p[i], &max_len_q[i]); + all_zero_bs = 0; + } + tc[i] = bs[i] ? TC_CALC(qp, bs[i]) : 0; + } + if (!all_zero_bs) { + src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)]; + if (!c_idx) { + fc->vvcdsp.lf.filter_luma[0](src, fc->frame->linesize[c_idx], + beta, tc, no_p, no_q, max_len_p, max_len_q, horizontal_ctu_edge); + } else { + fc->vvcdsp.lf.filter_chroma[0](src, fc->frame->linesize[c_idx], + beta, tc, no_p, no_q, max_len_p, max_len_q, hs); + } + } + } + } + } +} + +static void alf_copy_border(uint8_t *dst, const uint8_t *src, + const int pixel_shift, int width, const int height, const ptrdiff_t dst_stride, const ptrdiff_t src_stride) +{ + width <<= pixel_shift; + for (int i = 0; i < height; i++) { + memcpy(dst, src, width); + dst += dst_stride; + src += src_stride; + } +} + +static void alf_extend_vert(uint8_t *_dst, const uint8_t *_src, + const int pixel_shift, const int width, const int height, ptrdiff_t stride) +{ + if (pixel_shift == 0) { + for (int i = 0; i < height; i++) { + memset(_dst, *_src, width); + _src += stride; + _dst += stride; + } + } else { + const uint16_t *src = (const uint16_t *)_src; + uint16_t *dst = (uint16_t *)_dst; + stride >>= pixel_shift; + + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) + dst[j] = *src; + src += stride; + dst += stride; + } + } +} + +static void alf_extend_horz(uint8_t *dst, const uint8_t *src, + const int pixel_shift, int width, const int height, const ptrdiff_t stride) +{ + width <<= pixel_shift; + for (int i = 0; i < height; i++) { + memcpy(dst, src, width); + dst += stride; + } +} + +static void alf_copy_ctb_to_hv(VVCFrameContext *fc, const uint8_t *src, const ptrdiff_t src_stride, + const int x, const int y, const int width, const int height, const int x_ctb, const int y_ctb, const int c_idx) +{ + const int ps = fc->ps.sps->pixel_shift; + const int w = fc->ps.pps->width >> fc->ps.sps->hshift[c_idx]; + const int h = fc->ps.pps->height >> fc->ps.sps->vshift[c_idx]; + const int border_pixels = (c_idx == 0) ? ALF_BORDER_LUMA : ALF_BORDER_CHROMA; + const int offset_h[] = { 0, height - border_pixels }; + const int offset_v[] = { 0, width - border_pixels }; + + /* copy horizontal edges */ + for (int i = 0; i < FF_ARRAY_ELEMS(offset_h); i++) { + alf_copy_border(fc->tab.alf_pixel_buffer_h[c_idx][i] + ((border_pixels * y_ctb * w + x)<< ps), + src + offset_h[i] * src_stride, ps, width, border_pixels, w << ps, src_stride); + } + /* copy vertical edges */ + for (int i = 0; i < FF_ARRAY_ELEMS(offset_v); i++) { + alf_copy_border(fc->tab.alf_pixel_buffer_v[c_idx][i] + ((h * x_ctb + y) * (border_pixels << ps)), + src + (offset_v[i] << ps), ps, border_pixels, height, border_pixels << ps, src_stride); + } +} + +static void alf_fill_border_h(uint8_t *dst, const ptrdiff_t dst_stride, const uint8_t *src, const ptrdiff_t src_stride, + const uint8_t *border, const int width, const int border_pixels, const int ps, const int edge) +{ + if (edge) + alf_extend_horz(dst, border, ps, width, border_pixels, dst_stride); + else + alf_copy_border(dst, src, ps, width, border_pixels, dst_stride, src_stride); +} + +static void alf_fill_border_v(uint8_t *dst, const ptrdiff_t dst_stride, const uint8_t *src, + const uint8_t *border, const int border_pixels, const int height, const int pixel_shift, const int *edges, const int edge) +{ + const ptrdiff_t src_stride = (border_pixels << pixel_shift); + + if (edge) { + alf_extend_vert(dst, border, pixel_shift, border_pixels, height + 2 * border_pixels, dst_stride); + return; + } + + //left/right + alf_copy_border(dst + dst_stride * border_pixels * edges[TOP], src + src_stride * border_pixels * edges[TOP], + pixel_shift, border_pixels, height + (!edges[TOP] + !edges[BOTTOM]) * border_pixels, dst_stride, src_stride); + + //top left/right + if (edges[TOP]) + alf_extend_horz(dst, dst + dst_stride * border_pixels, pixel_shift, border_pixels, border_pixels, dst_stride); + + //bottom left/right + if (edges[BOTTOM]) { + dst += dst_stride * (border_pixels + height); + alf_extend_horz(dst, dst - dst_stride, pixel_shift, border_pixels, border_pixels, dst_stride); + } +} + +static void alf_prepare_buffer(VVCFrameContext *fc, uint8_t *_dst, const uint8_t *_src, const int x, const int y, + const int x_ctb, const int y_ctb, const int width, const int height, const ptrdiff_t dst_stride, const ptrdiff_t src_stride, + const int c_idx, const int *edges) +{ + const int ps = fc->ps.sps->pixel_shift; + const int w = fc->ps.pps->width >> fc->ps.sps->hshift[c_idx]; + const int h = fc->ps.pps->height >> fc->ps.sps->vshift[c_idx]; + const int border_pixels = c_idx == 0 ? ALF_BORDER_LUMA : ALF_BORDER_CHROMA; + uint8_t *dst, *src; + + copy_ctb(_dst, _src, width << ps, height, dst_stride, src_stride); + + //top + src = fc->tab.alf_pixel_buffer_h[c_idx][1] + (((border_pixels * w) << ps) * (y_ctb - 1) + (x << ps)); + dst = _dst - border_pixels * dst_stride; + alf_fill_border_h(dst, dst_stride, src, w << ps, _dst, width, border_pixels, ps, edges[TOP]); + + //bottom + src = fc->tab.alf_pixel_buffer_h[c_idx][0] + (((border_pixels * w) << ps) * (y_ctb + 1) + (x << ps)); + dst = _dst + height * dst_stride; + alf_fill_border_h(dst, dst_stride, src, w << ps, _dst + (height - 1) * dst_stride, width, border_pixels, ps, edges[BOTTOM]); + + + //left + src = fc->tab.alf_pixel_buffer_v[c_idx][1] + (h * (x_ctb - 1) + y - border_pixels) * (border_pixels << ps); + dst = _dst - (border_pixels << ps) - border_pixels * dst_stride; + alf_fill_border_v(dst, dst_stride, src, dst + (border_pixels << ps), border_pixels, height, ps, edges, edges[LEFT]); + + //right + src = fc->tab.alf_pixel_buffer_v[c_idx][0] + (h * (x_ctb + 1) + y - border_pixels) * (border_pixels << ps); + dst = _dst + (width << ps) - border_pixels * dst_stride; + alf_fill_border_v(dst, dst_stride, src, dst - (1 << ps), border_pixels, height, ps, edges, edges[RIGHT]); +} + +#define ALF_MAX_BLOCKS_IN_CTU (MAX_CTU_SIZE * MAX_CTU_SIZE / ALF_BLOCK_SIZE / ALF_BLOCK_SIZE) +#define ALF_MAX_FILTER_SIZE (ALF_MAX_BLOCKS_IN_CTU * ALF_NUM_COEFF_LUMA) + +static void alf_get_coeff_and_clip(VVCLocalContext *lc, int16_t *coeff, int16_t *clip, + const uint8_t *src, ptrdiff_t src_stride, int width, int height, int vb_pos, ALFParams *alf) +{ + const VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + uint8_t fixed_clip_set[ALF_NUM_FILTERS_LUMA][ALF_NUM_COEFF_LUMA] = { 0 }; + const int16_t *coeff_set; + const uint8_t *clip_idx_set; + const uint8_t *class_to_filt; + const int size = width * height / ALF_BLOCK_SIZE / ALF_BLOCK_SIZE; + int class_idx[ALF_MAX_BLOCKS_IN_CTU]; + int transpose_idx[ALF_MAX_BLOCKS_IN_CTU]; + + if (alf->ctb_filt_set_idx_y < 16) { + coeff_set = &ff_vvc_alf_fix_filt_coeff[0][0]; + clip_idx_set = &fixed_clip_set[0][0]; + class_to_filt = ff_vvc_alf_class_to_filt_map[alf->ctb_filt_set_idx_y]; + } else { + const int id = rsh->sh_alf_aps_id_luma[alf->ctb_filt_set_idx_y - 16]; + const VVCALF *aps = fc->ps.alf_list[id]; + coeff_set = &aps->luma_coeff[0][0]; + clip_idx_set = &aps->luma_clip_idx[0][0]; + class_to_filt = ff_vvc_alf_aps_class_to_filt_map; + } + fc->vvcdsp.alf.classify(class_idx, transpose_idx, src, src_stride, width, height, + vb_pos, lc->alf_gradient_tmp); + fc->vvcdsp.alf.recon_coeff_and_clip(coeff, clip, class_idx, transpose_idx, size, + coeff_set, clip_idx_set, class_to_filt); +} + +static void alf_filter_luma(VVCLocalContext *lc, uint8_t *dst, const uint8_t *src, + const ptrdiff_t dst_stride, const ptrdiff_t src_stride, const int x0, const int y0, + const int width, const int height, const int _vb_pos, ALFParams *alf) +{ + const VVCFrameContext *fc = lc->fc; + int vb_pos = _vb_pos - y0; + int16_t *coeff = (int16_t*)lc->tmp; + int16_t *clip = (int16_t *)lc->tmp1; + + av_assert0(ALF_MAX_FILTER_SIZE <= sizeof(lc->tmp)); + av_assert0(ALF_MAX_FILTER_SIZE * sizeof(int16_t) <= sizeof(lc->tmp1)); + + alf_get_coeff_and_clip(lc, coeff, clip, src, src_stride, width, height, vb_pos, alf); + fc->vvcdsp.alf.filter[LUMA](dst, dst_stride, src, src_stride, width, height, coeff, clip, vb_pos); +} + +static int alf_clip_from_idx(const VVCFrameContext *fc, const int idx) +{ + const VVCSPS *sps = fc->ps.sps; + const int offset[] = {0, 3, 5, 7}; + + return 1 << (sps->bit_depth - offset[idx]); +} + +static void alf_filter_chroma(VVCLocalContext *lc, uint8_t *dst, const uint8_t *src, + const ptrdiff_t dst_stride, const ptrdiff_t src_stride, const int c_idx, + const int width, const int height, const int vb_pos, ALFParams *alf) +{ + VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const VVCALF *aps = fc->ps.alf_list[rsh->sh_alf_aps_id_chroma]; + const int idx = alf->alf_ctb_filter_alt_idx[c_idx - 1]; + const int16_t *coeff = aps->chroma_coeff[idx]; + int16_t clip[ALF_NUM_COEFF_CHROMA]; + + for (int i = 0; i < ALF_NUM_COEFF_CHROMA; i++) + clip[i] = alf_clip_from_idx(fc, aps->chroma_clip_idx[idx][i]); + + fc->vvcdsp.alf.filter[CHROMA](dst, dst_stride, src, src_stride, width, height, coeff, clip, vb_pos); +} + +static void alf_filter_cc(VVCLocalContext *lc, uint8_t *dst, const uint8_t *luma, + const ptrdiff_t dst_stride, const ptrdiff_t luma_stride, const int c_idx, + const int width, const int height, const int hs, const int vs, const int vb_pos, ALFParams *alf) +{ + const VVCFrameContext *fc = lc->fc; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int idx = c_idx - 1; + const int cc_aps_id = c_idx == CB ? rsh->sh_alf_cc_cb_aps_id : rsh->sh_alf_cc_cr_aps_id; + const VVCALF *aps = fc->ps.alf_list[cc_aps_id]; + + if (aps) { + const int16_t *coeff = aps->cc_coeff[idx][alf->ctb_cc_idc[idx] - 1]; + + fc->vvcdsp.alf.filter_cc(dst, dst_stride, luma, luma_stride, width, height, hs, vs, coeff, vb_pos); + } +} + +void ff_vvc_alf_copy_ctu_to_hv(VVCLocalContext* lc, const int x0, const int y0) +{ + VVCFrameContext *fc = lc->fc; + const int x_ctb = x0 >> fc->ps.sps->ctb_log2_size_y; + const int y_ctb = y0 >> fc->ps.sps->ctb_log2_size_y; + const int ctb_size_y = fc->ps.sps->ctb_size_y; + const int ps = fc->ps.sps->pixel_shift; + const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = fc->ps.sps->hshift[c_idx]; + const int vs = fc->ps.sps->vshift[c_idx]; + const int x = x0 >> hs; + const int y = y0 >> vs; + const int width = FFMIN(fc->ps.pps->width - x0, ctb_size_y) >> hs; + const int height = FFMIN(fc->ps.pps->height - y0, ctb_size_y) >> vs; + + const int src_stride = fc->frame->linesize[c_idx]; + uint8_t* src = &fc->frame->data[c_idx][y * src_stride + (x << ps)]; + + alf_copy_ctb_to_hv(fc, src, src_stride, x, y, width, height, x_ctb, y_ctb, c_idx); + } +} + +void ff_vvc_alf_filter(VVCLocalContext *lc, const int x0, const int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const int x_ctb = x0 >> fc->ps.sps->ctb_log2_size_y; + const int y_ctb = y0 >> fc->ps.sps->ctb_log2_size_y; + const int ctb_size_y = fc->ps.sps->ctb_size_y; + const int ps = fc->ps.sps->pixel_shift; + const int padded_stride = EDGE_EMU_BUFFER_STRIDE << ps; + const int padded_offset = padded_stride * ALF_PADDING_SIZE + (ALF_PADDING_SIZE << ps); + const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + ALFParams *alf = &CTB(fc->tab.alf, x_ctb, y_ctb); + int edges[MAX_EDGES] = { x_ctb == 0, y_ctb == 0, x_ctb == pps->ctb_width - 1, y_ctb == pps->ctb_height - 1 }; + + if (!pps->r->pps_loop_filter_across_tiles_enabled_flag) { + edges[LEFT] = edges[LEFT] || (lc->boundary_flags & BOUNDARY_LEFT_TILE); + edges[TOP] = edges[TOP] || (lc->boundary_flags & BOUNDARY_UPPER_TILE); + edges[RIGHT] = edges[RIGHT] || pps->ctb_to_col_bd[x_ctb] != pps->ctb_to_col_bd[x_ctb + 1]; + edges[BOTTOM] = edges[BOTTOM] || pps->ctb_to_row_bd[y_ctb] != pps->ctb_to_row_bd[y_ctb + 1]; + } + + if (!pps->r->pps_loop_filter_across_slices_enabled_flag) { + edges[LEFT] = edges[LEFT] || (lc->boundary_flags & BOUNDARY_LEFT_SLICE); + edges[TOP] = edges[TOP] || (lc->boundary_flags & BOUNDARY_UPPER_SLICE); + edges[RIGHT] = edges[RIGHT] || CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb); + edges[BOTTOM] = edges[BOTTOM] || CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb, y_ctb + 1); + } + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = fc->ps.sps->hshift[c_idx]; + const int vs = fc->ps.sps->vshift[c_idx]; + const int ctb_size_h = ctb_size_y >> hs; + const int ctb_size_v = ctb_size_y >> vs; + const int x = x0 >> hs; + const int y = y0 >> vs; + const int pic_width = fc->ps.pps->width >> hs; + const int pic_height = fc->ps.pps->height >> vs; + const int width = FFMIN(pic_width - x, ctb_size_h); + const int height = FFMIN(pic_height - y, ctb_size_v); + const int src_stride = fc->frame->linesize[c_idx]; + uint8_t *src = &fc->frame->data[c_idx][y * src_stride + (x << ps)]; + uint8_t *padded; + + if (alf->ctb_flag[c_idx] || (!c_idx && (alf->ctb_cc_idc[0] || alf->ctb_cc_idc[1]))) { + padded = (c_idx ? lc->alf_buffer_chroma : lc->alf_buffer_luma) + padded_offset; + alf_prepare_buffer(fc, padded, src, x, y, x_ctb, y_ctb, width, height, + padded_stride, src_stride, c_idx, edges); + } + if (alf->ctb_flag[c_idx]) { + if (!c_idx) { + alf_filter_luma(lc, src, padded, src_stride, padded_stride, x, y, + width, height, y + ctb_size_v - ALF_VB_POS_ABOVE_LUMA, alf); + } else { + alf_filter_chroma(lc, src, padded, src_stride, padded_stride, c_idx, + width, height, ctb_size_v - ALF_VB_POS_ABOVE_CHROMA, alf); + } + } + if (c_idx && alf->ctb_cc_idc[c_idx - 1]) { + padded = lc->alf_buffer_luma + padded_offset; + alf_filter_cc(lc, src, padded, src_stride, padded_stride, c_idx, + width, height, hs, vs, (ctb_size_v << vs) - ALF_VB_POS_ABOVE_LUMA, alf); + } + + alf->applied[c_idx] = 1; + } +} + + +void ff_vvc_lmcs_filter(const VVCLocalContext *lc, const int x, const int y) +{ + const SliceContext *sc = lc->sc; + const VVCFrameContext *fc = lc->fc; + const int ctb_size = fc->ps.sps->ctb_size_y; + const int width = FFMIN(fc->ps.pps->width - x, ctb_size); + const int height = FFMIN(fc->ps.pps->height - y, ctb_size); + uint8_t *data = fc->frame->data[LUMA] + y * fc->frame->linesize[LUMA] + (x << fc->ps.sps->pixel_shift); + if (sc->sh.r->sh_lmcs_used_flag) + fc->vvcdsp.lmcs.filter(data, fc->frame->linesize[LUMA], width, height, &fc->ps.lmcs.inv_lut); +} diff --git a/libavcodec/vvc/vvc_filter.h b/libavcodec/vvc/vvc_filter.h new file mode 100644 index 00000000000..2ae4c33e2da --- /dev/null +++ b/libavcodec/vvc/vvc_filter.h @@ -0,0 +1,71 @@ +/* + * VVC filters + * + * Copyright (C) 2022 Nuo Mi + * + * + * 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 + */ +#ifndef AVCODEC_VVC_VVC_FILTER_H +#define AVCODEC_VVC_VVC_FILTER_H + +#include "vvcdec.h" + +/** + * lmcs filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_lmcs_filter(const VVCLocalContext *lc, const int x0, const int y0); + +/** + * vertical deblock filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_deblock_vertical(const VVCLocalContext *lc, int x0, int y0); + +/** + * horizontal deblock filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_deblock_horizontal(const VVCLocalContext *lc, int x0, int y0); + +/** + * sao filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_sao_filter(VVCLocalContext *lc, const int x0, const int y0); + +void ff_vvc_sao_copy_ctb_to_hv(VVCLocalContext* lc, int rx, int ry, int last_row); +void ff_vvc_alf_copy_ctu_to_hv(VVCLocalContext* lc, int x0, int y0); + +/** + * alf filter for the CTU + * @param lc local context for CTU + * @param x0 x position for the CTU + * @param y0 y position for the CTU + */ +void ff_vvc_alf_filter(VVCLocalContext *lc, const int x0, const int y0); + +#endif // AVCODEC_VVC_VVC_CTU_H diff --git a/libavcodec/vvc/vvc_filter_template.c b/libavcodec/vvc/vvc_filter_template.c new file mode 100644 index 00000000000..9b3a0e46f76 --- /dev/null +++ b/libavcodec/vvc/vvc_filter_template.c @@ -0,0 +1,872 @@ +/* + * VVC filters DSP + * + * Copyright (C) 2022 Nuo Mi + * + * 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 "libavcodec/h26x/h2656_sao_template.c" + +static void FUNC(lmcs_filter_luma)(uint8_t *_dst, ptrdiff_t dst_stride, const int width, const int height, const void *_lut) +{ + const pixel *lut = _lut; + pixel *dst = (pixel*)_dst; + dst_stride /= sizeof(pixel); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = lut[dst[x]]; + dst += dst_stride; + } +} + +static av_always_inline int16_t FUNC(alf_clip)(pixel curr, pixel v0, pixel v1, int16_t clip) +{ + return av_clip(v0 - curr, -clip, clip) + av_clip(v1 - curr, -clip, clip); +} + +static void FUNC(alf_filter_luma)(uint8_t *_dst, ptrdiff_t dst_stride, const uint8_t *_src, ptrdiff_t src_stride, + const int width, const int height, const int16_t *filter, const int16_t *clip, const int vb_pos) +{ + const pixel *src = (pixel *)_src; + const int shift = 7; + const int offset = 1 << ( shift - 1 ); + const int vb_above = vb_pos - 4; + const int vb_below = vb_pos + 3; + + dst_stride /= sizeof(pixel); + src_stride /= sizeof(pixel); + + for (int y = 0; y < height; y += ALF_BLOCK_SIZE) { + for (int x = 0; x < width; x += ALF_BLOCK_SIZE) { + const pixel *s0 = src + y * src_stride + x; + const pixel *s1 = s0 + src_stride; + const pixel *s2 = s0 - src_stride; + const pixel *s3 = s1 + src_stride; + const pixel *s4 = s2 - src_stride; + const pixel *s5 = s3 + src_stride; + const pixel *s6 = s4 - src_stride; + + for (int i = 0; i < ALF_BLOCK_SIZE; i++) { + pixel *dst = (pixel *)_dst + (y + i) * dst_stride + x; + + const pixel *p0 = s0 + i * src_stride; + const pixel *p1 = s1 + i * src_stride; + const pixel *p2 = s2 + i * src_stride; + const pixel *p3 = s3 + i * src_stride; + const pixel *p4 = s4 + i * src_stride; + const pixel *p5 = s5 + i * src_stride; + const pixel *p6 = s6 + i * src_stride; + + const int is_near_vb_above = (y + i < vb_pos) && (y + i >= vb_pos - 1); + const int is_near_vb_below = (y + i >= vb_pos) && (y + i <= vb_pos); + const int is_near_vb = is_near_vb_above || is_near_vb_below; + + if ((y + i < vb_pos) && ((y + i) >= vb_above)) { + p1 = (y + i == vb_pos - 1) ? p0 : p1; + p3 = (y + i >= vb_pos - 2) ? p1 : p3; + p5 = (y + i >= vb_pos - 3) ? p3 : p5; + + p2 = (y + i == vb_pos - 1) ? p0 : p2; + p4 = (y + i >= vb_pos - 2) ? p2 : p4; + p6 = (y + i >= vb_pos - 3) ? p4 : p6; + } else if ((y + i >= vb_pos) && ((y + i) <= vb_below)) { + p2 = (y + i == vb_pos ) ? p0 : p2; + p4 = (y + i <= vb_pos + 1) ? p2 : p4; + p6 = (y + i <= vb_pos + 2) ? p4 : p6; + + p1 = (y + i == vb_pos ) ? p0 : p1; + p3 = (y + i <= vb_pos + 1) ? p1 : p3; + p5 = (y + i <= vb_pos + 2) ? p3 : p5; + } + + for (int j = 0; j < ALF_BLOCK_SIZE; j++) { + int sum = 0; + const pixel curr = *p0; + + sum += filter[0] * FUNC(alf_clip)(curr, p5[+0], p6[+0], clip[0]); + sum += filter[1] * FUNC(alf_clip)(curr, p3[+1], p4[-1], clip[1]); + sum += filter[2] * FUNC(alf_clip)(curr, p3[+0], p4[+0], clip[2]); + sum += filter[3] * FUNC(alf_clip)(curr, p3[-1], p4[+1], clip[3]); + sum += filter[4] * FUNC(alf_clip)(curr, p1[+2], p2[-2], clip[4]); + sum += filter[5] * FUNC(alf_clip)(curr, p1[+1], p2[-1], clip[5]); + sum += filter[6] * FUNC(alf_clip)(curr, p1[+0], p2[+0], clip[6]); + sum += filter[7] * FUNC(alf_clip)(curr, p1[-1], p2[+1], clip[7]); + sum += filter[8] * FUNC(alf_clip)(curr, p1[-2], p2[+2], clip[8]); + sum += filter[9] * FUNC(alf_clip)(curr, p0[+3], p0[-3], clip[9]); + sum += filter[10] * FUNC(alf_clip)(curr, p0[+2], p0[-2], clip[10]); + sum += filter[11] * FUNC(alf_clip)(curr, p0[+1], p0[-1], clip[11]); + + if (!is_near_vb) + sum = (sum + offset) >> shift; + else + sum = (sum + (1 << ((shift + 3) - 1))) >> (shift + 3); + sum += curr; + dst[j] = CLIP(sum); + + p0++; + p1++; + p2++; + p3++; + p4++; + p5++; + p6++; + } + } + filter += ALF_NUM_COEFF_LUMA; + clip += ALF_NUM_COEFF_LUMA; + } + } +} + +static void FUNC(alf_filter_chroma)(uint8_t* _dst, ptrdiff_t dst_stride, const uint8_t* _src, ptrdiff_t src_stride, + const int width, const int height, const int16_t* filter, const int16_t* clip, const int vb_pos) +{ + const pixel *src = (pixel *)_src; + const int shift = 7; + const int offset = 1 << ( shift - 1 ); + const int vb_above = vb_pos - 2; + const int vb_below = vb_pos + 1; + + dst_stride /= sizeof(pixel); + src_stride /= sizeof(pixel); + + for (int y = 0; y < height; y += ALF_BLOCK_SIZE) { + for (int x = 0; x < width; x += ALF_BLOCK_SIZE) { + const pixel *s0 = src + y * src_stride + x; + const pixel *s1 = s0 + src_stride; + const pixel *s2 = s0 - src_stride; + const pixel *s3 = s1 + src_stride; + const pixel *s4 = s2 - src_stride; + const pixel *s5 = s3 + src_stride; + const pixel *s6 = s4 - src_stride; + + for (int i = 0; i < ALF_BLOCK_SIZE; i++) { + pixel *dst = (pixel *)_dst + (y + i) * dst_stride + x; + + const pixel *p0 = s0 + i * src_stride; + const pixel *p1 = s1 + i * src_stride; + const pixel *p2 = s2 + i * src_stride; + const pixel *p3 = s3 + i * src_stride; + const pixel *p4 = s4 + i * src_stride; + const pixel *p5 = s5 + i * src_stride; + const pixel *p6 = s6 + i * src_stride; + + const int is_near_vb_above = (y + i < vb_pos) && (y + i >= vb_pos - 1); + const int is_near_vb_below = (y + i >= vb_pos) && (y + i <= vb_pos); + const int is_near_vb = is_near_vb_above || is_near_vb_below; + + if ((y + i < vb_pos) && ((y + i) >= vb_above)) { + p1 = (y + i == vb_pos - 1) ? p0 : p1; + p3 = (y + i >= vb_pos - 2) ? p1 : p3; + p5 = (y + i >= vb_pos - 3) ? p3 : p5; + + p2 = (y + i == vb_pos - 1) ? p0 : p2; + p4 = (y + i >= vb_pos - 2) ? p2 : p4; + p6 = (y + i >= vb_pos - 3) ? p4 : p6; + } else if ((y + i >= vb_pos) && ((y + i) <= vb_below)) { + p2 = (y + i == vb_pos ) ? p0 : p2; + p4 = (y + i <= vb_pos + 1) ? p2 : p4; + p6 = (y + i <= vb_pos + 2) ? p4 : p6; + + p1 = (y + i == vb_pos ) ? p0 : p1; + p3 = (y + i <= vb_pos + 1) ? p1 : p3; + p5 = (y + i <= vb_pos + 2) ? p3 : p5; + } + + for (int j = 0; j < ALF_BLOCK_SIZE; j++) { + int sum = 0; + const pixel curr = *p0; + + sum += filter[0] * FUNC(alf_clip)(curr, p3[+0], p4[+0], clip[0]); + sum += filter[1] * FUNC(alf_clip)(curr, p1[+1], p2[-1], clip[1]); + sum += filter[2] * FUNC(alf_clip)(curr, p1[+0], p2[+0], clip[2]); + sum += filter[3] * FUNC(alf_clip)(curr, p1[-1], p2[+1], clip[3]); + sum += filter[4] * FUNC(alf_clip)(curr, p0[+2], p0[-2], clip[4]); + sum += filter[5] * FUNC(alf_clip)(curr, p0[+1], p0[-1], clip[5]); + + if (!is_near_vb) + sum = (sum + offset) >> shift; + else + sum = (sum + (1 << ((shift + 3) - 1))) >> (shift + 3); + sum += curr; + dst[j] = CLIP(sum); + + p0++; + p1++; + p2++; + p3++; + p4++; + p5++; + p6++; + } + } + } + } +} + +static void FUNC(alf_filter_cc)(uint8_t *_dst, ptrdiff_t dst_stride, const uint8_t *_luma, const ptrdiff_t luma_stride, + const int width, const int height, const int hs, const int vs, const int16_t *filter, const int vb_pos) +{ + const ptrdiff_t stride = luma_stride / sizeof(pixel); + + dst_stride /= sizeof(pixel); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int sum = 0; + pixel *dst = (pixel *)_dst + y * dst_stride + x; + const pixel *src = (pixel *)_luma + (y << vs) * stride + (x << hs); + + const pixel *s0 = src - stride; + const pixel *s1 = src; + const pixel *s2 = src + stride; + const pixel *s3 = src + 2 * stride; + + const int pos = y << vs; + if (!vs && (pos == vb_pos || pos == vb_pos + 1)) + continue; + + if (pos == (vb_pos - 2) || pos == (vb_pos + 1)) + s3 = s2; + else if (pos == (vb_pos - 1) || pos == vb_pos) + s3 = s2 = s0 = s1; + + + sum += filter[0] * (*s0 - *src); + sum += filter[1] * (*(s1 - 1) - *src); + sum += filter[2] * (*(s1 + 1) - *src); + sum += filter[3] * (*(s2 - 1) - *src); + sum += filter[4] * (*s2 - *src); + sum += filter[5] * (*(s2 + 1) - *src); + sum += filter[6] * (*s3 - *src); + sum = av_clip((sum + 64) >> 7, -(1 << (BIT_DEPTH - 1)), (1 << (BIT_DEPTH - 1)) - 1); + sum += *dst; + *dst = av_clip_pixel(sum); + } + } +} + +#define ALF_DIR_VERT 0 +#define ALF_DIR_HORZ 1 +#define ALF_DIR_DIGA0 2 +#define ALF_DIR_DIGA1 3 + +static void FUNC(alf_get_idx)(int *class_idx, int *transpose_idx, const int *sum, const int ac) +{ + static const int arg_var[] = {0, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4 }; + + int hv0, hv1, dir_hv, d0, d1, dir_d, hvd1, hvd0, sum_hv, dir1; + + dir_hv = sum[ALF_DIR_VERT] <= sum[ALF_DIR_HORZ]; + hv1 = FFMAX(sum[ALF_DIR_VERT], sum[ALF_DIR_HORZ]); + hv0 = FFMIN(sum[ALF_DIR_VERT], sum[ALF_DIR_HORZ]); + + dir_d = sum[ALF_DIR_DIGA0] <= sum[ALF_DIR_DIGA1]; + d1 = FFMAX(sum[ALF_DIR_DIGA0], sum[ALF_DIR_DIGA1]); + d0 = FFMIN(sum[ALF_DIR_DIGA0], sum[ALF_DIR_DIGA1]); + + //promote to avoid overflow + dir1 = (uint64_t)d1 * hv0 <= (uint64_t)hv1 * d0; + hvd1 = dir1 ? hv1 : d1; + hvd0 = dir1 ? hv0 : d0; + + sum_hv = sum[ALF_DIR_HORZ] + sum[ALF_DIR_VERT]; + *class_idx = arg_var[av_clip_uintp2(sum_hv * ac >> (BIT_DEPTH - 1), 4)]; + if (hvd1 * 2 > 9 * hvd0) + *class_idx += ((dir1 << 1) + 2) * 5; + else if (hvd1 > 2 * hvd0) + *class_idx += ((dir1 << 1) + 1) * 5; + + *transpose_idx = dir_d * 2 + dir_hv; +} + +static void FUNC(alf_classify)(int *class_idx, int *transpose_idx, + const uint8_t *_src, const ptrdiff_t _src_stride, const int width, const int height, + const int vb_pos, int *gradient_tmp) +{ + int *grad; + + const int h = height + ALF_GRADIENT_BORDER * 2; + const int w = width + ALF_GRADIENT_BORDER * 2; + const int size = (ALF_BLOCK_SIZE + ALF_GRADIENT_BORDER * 2) / ALF_GRADIENT_STEP; + const int gstride = (w / ALF_GRADIENT_STEP) * ALF_NUM_DIR; + + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + src -= (ALF_GRADIENT_BORDER + 1) * src_stride + ALF_GRADIENT_BORDER; + + grad = gradient_tmp; + for (int y = 0; y < h; y += ALF_GRADIENT_STEP) { + const pixel *s0 = src + y * src_stride; + const pixel *s1 = s0 + src_stride; + const pixel *s2 = s1 + src_stride; + const pixel *s3 = s2 + src_stride; + + if (y == vb_pos) //above + s3 = s2; + else if (y == vb_pos + ALF_GRADIENT_BORDER) + s0 = s1; + + for (int x = 0; x < w; x += ALF_GRADIENT_STEP) { + //two points a time + const pixel *a0 = s0 + x; + const pixel *p0 = s1 + x; + const pixel *b0 = s2 + x; + const int val0 = (*p0) << 1; + + const pixel *a1 = s1 + x + 1; + const pixel *p1 = s2 + x + 1; + const pixel *b1 = s3 + x + 1; + const int val1 = (*p1) << 1; + + grad[ALF_DIR_VERT] = FFABS(val0 - *a0 - *b0) + FFABS(val1 - *a1 - *b1); + grad[ALF_DIR_HORZ] = FFABS(val0 - *(p0 - 1) - *(p0 + 1)) + FFABS(val1 - *(p1 - 1) - *(p1 + 1)); + grad[ALF_DIR_DIGA0] = FFABS(val0 - *(a0 - 1) - *(b0 + 1)) + FFABS(val1 - *(a1 - 1) - *(b1 + 1)); + grad[ALF_DIR_DIGA1] = FFABS(val0 - *(a0 + 1) - *(b0 - 1)) + FFABS(val1 - *(a1 + 1) - *(b1 - 1)); + grad += ALF_NUM_DIR; + } + } + + for (int y = 0; y < height ; y += ALF_BLOCK_SIZE ) { + int start = 0; + int end = (ALF_BLOCK_SIZE + ALF_GRADIENT_BORDER * 2) / ALF_GRADIENT_STEP; + int ac = 2; + if (y + ALF_BLOCK_SIZE == vb_pos) { + end -= ALF_GRADIENT_BORDER / ALF_GRADIENT_STEP; + ac = 3; + } else if (y == vb_pos) { + start += ALF_GRADIENT_BORDER / ALF_GRADIENT_STEP; + ac = 3; + } + for (int x = 0; x < width; x += ALF_BLOCK_SIZE) { + const int xg = x / ALF_GRADIENT_STEP; + const int yg = y / ALF_GRADIENT_STEP; + int sum[ALF_NUM_DIR] = { 0 }; + + grad = gradient_tmp + (yg + start) * gstride + xg * ALF_NUM_DIR; + //todo: optimize this loop + for (int i = start; i < end; i++) { + for (int j = 0; j < size; j++) { + sum[ALF_DIR_VERT] += grad[ALF_DIR_VERT]; + sum[ALF_DIR_HORZ] += grad[ALF_DIR_HORZ]; + sum[ALF_DIR_DIGA0] += grad[ALF_DIR_DIGA0]; + sum[ALF_DIR_DIGA1] += grad[ALF_DIR_DIGA1]; + grad += ALF_NUM_DIR; + } + grad += gstride - size * ALF_NUM_DIR; + } + FUNC(alf_get_idx)(class_idx, transpose_idx, sum, ac); + + class_idx++; + transpose_idx++; + } + } + +} + +static void FUNC(alf_recon_coeff_and_clip)(int16_t *coeff, int16_t *clip, + const int *class_idx, const int *transpose_idx, const int size, + const int16_t *coeff_set, const uint8_t *clip_idx_set, const uint8_t *class_to_filt) +{ + const static int index[][ALF_NUM_COEFF_LUMA] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, + { 9, 4, 10, 8, 1, 5, 11, 7, 3, 0, 2, 6 }, + { 0, 3, 2, 1, 8, 7, 6, 5, 4, 9, 10, 11 }, + { 9, 8, 10, 4, 3, 7, 11, 5, 1, 0, 2, 6 }, + }; + + const int16_t clip_set[] = { + 1 << BIT_DEPTH, 1 << (BIT_DEPTH - 3), 1 << (BIT_DEPTH - 5), 1 << (BIT_DEPTH - 7) + }; + + for (int i = 0; i < size; i++) { + const int16_t *src_coeff = coeff_set + class_to_filt[class_idx[i]] * ALF_NUM_COEFF_LUMA; + const uint8_t *clip_idx = clip_idx_set + class_idx[i] * ALF_NUM_COEFF_LUMA; + + for (int j = 0; j < ALF_NUM_COEFF_LUMA; j++) { + const int idx = index[transpose_idx[i]][j]; + *coeff++ = src_coeff[idx]; + *clip++ = clip_set[clip_idx[idx]]; + } + } +} + +#undef ALF_DIR_HORZ +#undef ALF_DIR_VERT +#undef ALF_DIR_DIGA0 +#undef ALF_DIR_DIGA1 + +// line zero +#define P7 pix[-8 * xstride] +#define P6 pix[-7 * xstride] +#define P5 pix[-6 * xstride] +#define P4 pix[-5 * xstride] +#define P3 pix[-4 * xstride] +#define P2 pix[-3 * xstride] +#define P1 pix[-2 * xstride] +#define P0 pix[-1 * xstride] +#define Q0 pix[0 * xstride] +#define Q1 pix[1 * xstride] +#define Q2 pix[2 * xstride] +#define Q3 pix[3 * xstride] +#define Q4 pix[4 * xstride] +#define Q5 pix[5 * xstride] +#define Q6 pix[6 * xstride] +#define Q7 pix[7 * xstride] +#define P(x) pix[(-(x)-1) * xstride] +#define Q(x) pix[(x) * xstride] + +// line three. used only for deblocking decision +#define TP7 pix[-8 * xstride + 3 * ystride] +#define TP6 pix[-7 * xstride + 3 * ystride] +#define TP5 pix[-6 * xstride + 3 * ystride] +#define TP4 pix[-5 * xstride + 3 * ystride] +#define TP3 pix[-4 * xstride + 3 * ystride] +#define TP2 pix[-3 * xstride + 3 * ystride] +#define TP1 pix[-2 * xstride + 3 * ystride] +#define TP0 pix[-1 * xstride + 3 * ystride] +#define TQ0 pix[0 * xstride + 3 * ystride] +#define TQ1 pix[1 * xstride + 3 * ystride] +#define TQ2 pix[2 * xstride + 3 * ystride] +#define TQ3 pix[3 * xstride + 3 * ystride] +#define TQ4 pix[4 * xstride + 3 * ystride] +#define TQ5 pix[5 * xstride + 3 * ystride] +#define TQ6 pix[6 * xstride + 3 * ystride] +#define TQ7 pix[7 * xstride + 3 * ystride] +#define TP(x) pix[(-(x)-1) * xstride + 3 * ystride] +#define TQ(x) pix[(x) * xstride + 3 * ystride] + +#define FP3 pix[-4 * xstride + 1 * ystride] +#define FP2 pix[-3 * xstride + 1 * ystride] +#define FP1 pix[-2 * xstride + 1 * ystride] +#define FP0 pix[-1 * xstride + 1 * ystride] +#define FQ0 pix[0 * xstride + 1 * ystride] +#define FQ1 pix[1 * xstride + 1 * ystride] +#define FQ2 pix[2 * xstride + 1 * ystride] +#define FQ3 pix[3 * xstride + 1 * ystride] + +#include "libavcodec/h26x/h2656_deblock_template.c" + +static void FUNC(loop_filter_luma_large)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, const int32_t tc, + const uint8_t no_p, const uint8_t no_q, const uint8_t max_len_p, const uint8_t max_len_q) +{ + for (int d = 0; d < 4; d++) { + const int p6 = P6; + const int p5 = P5; + const int p4 = P4; + const int p3 = P3; + const int p2 = P2; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + const int q3 = Q3; + const int q4 = Q4; + const int q5 = Q5; + const int q6 = Q6; + int m; + if (max_len_p == 5 && max_len_q == 5) + m = (p4 + p3 + 2 * (p2 + p1 + p0 + q0 + q1 + q2) + q3 + q4 + 8) >> 4; + else if (max_len_p == max_len_q) + m = (p6 + p5 + p4 + p3 + p2 + p1 + 2 * (p0 + q0) + q1 + q2 + q3 + q4 + q5 + q6 + 8) >> 4; + else if (max_len_p + max_len_q == 12) + m = (p5 + p4 + p3 + p2 + 2 * (p1 + p0 + q0 + q1) + q2 + q3 + q4 + q5 + 8) >> 4; + else if (max_len_p + max_len_q == 8) + m = (p3 + p2 + p1 + p0 + q0 + q1 + q2 + q3 + 4) >> 3; + else if (max_len_q == 7) + m = (2 * (p2 + p1 + p0 + q0) + p0 + p1 + q1 + q2 + q3 + q4 + q5 + q6 + 8) >> 4; + else + m = (p6 + p5 + p4 + p3 + p2 + p1 + 2 * (q2 + q1 + q0 + p0) + q0 + q1 + 8) >> 4; + if (!no_p) { + const int refp = (P(max_len_p) + P(max_len_p - 1) + 1) >> 1; + if (max_len_p == 3) { + P0 = p0 + av_clip(((m * 53 + refp * 11 + 32) >> 6) - p0, -(tc * 6 >> 1), (tc * 6 >> 1)); + P1 = p1 + av_clip(((m * 32 + refp * 32 + 32) >> 6) - p1, -(tc * 4 >> 1), (tc * 4 >> 1)); + P2 = p2 + av_clip(((m * 11 + refp * 53 + 32) >> 6) - p2, -(tc * 2 >> 1), (tc * 2 >> 1)); + } else if (max_len_p == 5) { + P0 = p0 + av_clip(((m * 58 + refp * 6 + 32) >> 6) - p0, -(tc * 6 >> 1), (tc * 6 >> 1)); + P1 = p1 + av_clip(((m * 45 + refp * 19 + 32) >> 6) - p1, -(tc * 5 >> 1), (tc * 5 >> 1)); + P2 = p2 + av_clip(((m * 32 + refp * 32 + 32) >> 6) - p2, -(tc * 4 >> 1), (tc * 4 >> 1)); + P3 = p3 + av_clip(((m * 19 + refp * 45 + 32) >> 6) - p3, -(tc * 3 >> 1), (tc * 3 >> 1)); + P4 = p4 + av_clip(((m * 6 + refp * 58 + 32) >> 6) - p4, -(tc * 2 >> 1), (tc * 2 >> 1)); + } else { + P0 = p0 + av_clip(((m * 59 + refp * 5 + 32) >> 6) - p0, -(tc * 6 >> 1), (tc * 6 >> 1)); + P1 = p1 + av_clip(((m * 50 + refp * 14 + 32) >> 6) - p1, -(tc * 5 >> 1), (tc * 5 >> 1)); + P2 = p2 + av_clip(((m * 41 + refp * 23 + 32) >> 6) - p2, -(tc * 4 >> 1), (tc * 4 >> 1)); + P3 = p3 + av_clip(((m * 32 + refp * 32 + 32) >> 6) - p3, -(tc * 3 >> 1), (tc * 3 >> 1)); + P4 = p4 + av_clip(((m * 23 + refp * 41 + 32) >> 6) - p4, -(tc * 2 >> 1), (tc * 2 >> 1)); + P5 = p5 + av_clip(((m * 14 + refp * 50 + 32) >> 6) - p5, -(tc * 1 >> 1), (tc * 1 >> 1)); + P6 = p6 + av_clip(((m * 5 + refp * 59 + 32) >> 6) - p6, -(tc * 1 >> 1), (tc * 1 >> 1)); + } + } + if (!no_q) { + const int refq = (Q(max_len_q) + Q(max_len_q - 1) + 1) >> 1; + if (max_len_q == 3) { + Q0 = q0 + av_clip(((m * 53 + refq * 11 + 32) >> 6) - q0, -(tc * 6 >> 1), (tc * 6 >> 1)); + Q1 = q1 + av_clip(((m * 32 + refq * 32 + 32) >> 6) - q1, -(tc * 4 >> 1), (tc * 4 >> 1)); + Q2 = q2 + av_clip(((m * 11 + refq * 53 + 32) >> 6) - q2, -(tc * 2 >> 1), (tc * 2 >> 1)); + } else if (max_len_q == 5) { + Q0 = q0 + av_clip(((m * 58 + refq * 6 + 32) >> 6) - q0, -(tc * 6 >> 1), (tc * 6 >> 1)); + Q1 = q1 + av_clip(((m * 45 + refq * 19 + 32) >> 6) - q1, -(tc * 5 >> 1), (tc * 5 >> 1)); + Q2 = q2 + av_clip(((m * 32 + refq * 32 + 32) >> 6) - q2, -(tc * 4 >> 1), (tc * 4 >> 1)); + Q3 = q3 + av_clip(((m * 19 + refq * 45 + 32) >> 6) - q3, -(tc * 3 >> 1), (tc * 3 >> 1)); + Q4 = q4 + av_clip(((m * 6 + refq * 58 + 32) >> 6) - q4, -(tc * 2 >> 1), (tc * 2 >> 1)); + } else { + Q0 = q0 + av_clip(((m * 59 + refq * 5 + 32) >> 6) - q0, -(tc * 6 >> 1), (tc * 6 >> 1)); + Q1 = q1 + av_clip(((m * 50 + refq * 14 + 32) >> 6) - q1, -(tc * 5 >> 1), (tc * 5 >> 1)); + Q2 = q2 + av_clip(((m * 41 + refq * 23 + 32) >> 6) - q2, -(tc * 4 >> 1), (tc * 4 >> 1)); + Q3 = q3 + av_clip(((m * 32 + refq * 32 + 32) >> 6) - q3, -(tc * 3 >> 1), (tc * 3 >> 1)); + Q4 = q4 + av_clip(((m * 23 + refq * 41 + 32) >> 6) - q4, -(tc * 2 >> 1), (tc * 2 >> 1)); + Q5 = q5 + av_clip(((m * 14 + refq * 50 + 32) >> 6) - q5, -(tc * 1 >> 1), (tc * 1 >> 1)); + Q6 = q6 + av_clip(((m * 5 + refq * 59 + 32) >> 6) - q6, -(tc * 1 >> 1), (tc * 1 >> 1)); + } + + } + pix += ystride; + } +} + +static void FUNC(vvc_loop_filter_luma)(uint8_t* _pix, ptrdiff_t _xstride, ptrdiff_t _ystride, + const int32_t *_beta, const int32_t *_tc, const uint8_t *_no_p, const uint8_t *_no_q, + const uint8_t *_max_len_p, const uint8_t *_max_len_q, const int hor_ctu_edge) +{ + const ptrdiff_t xstride = _xstride / sizeof(pixel); + const ptrdiff_t ystride = _ystride / sizeof(pixel); + + for (int i = 0; i < 2; i++) { +#if BIT_DEPTH < 10 + const int tc = (_tc[i] + (1 << (9 - BIT_DEPTH))) >> (10 - BIT_DEPTH); +#else + const int tc = _tc[i] << (BIT_DEPTH - 10); +#endif + if (tc) { + pixel* pix = (pixel*)_pix + i * 4 * ystride; + const int dp0 = abs(P2 - 2 * P1 + P0); + const int dq0 = abs(Q2 - 2 * Q1 + Q0); + const int dp3 = abs(TP2 - 2 * TP1 + TP0); + const int dq3 = abs(TQ2 - 2 * TQ1 + TQ0); + const int d0 = dp0 + dq0; + const int d3 = dp3 + dq3; + const int tc25 = ((tc * 5 + 1) >> 1); + + const int no_p = _no_p[i]; + const int no_q = _no_q[i]; + + int max_len_p = _max_len_p[i]; + int max_len_q = _max_len_q[i]; + + const int large_p = (max_len_p > 3 && !hor_ctu_edge); + const int large_q = max_len_q > 3; + + const int beta = _beta[i] << BIT_DEPTH - 8; + const int beta_3 = beta >> 3; + const int beta_2 = beta >> 2; + + if (large_p || large_q) { + const int dp0l = large_p ? ((dp0 + abs(P5 - 2 * P4 + P3) + 1) >> 1) : dp0; + const int dq0l = large_q ? ((dq0 + abs(Q5 - 2 * Q4 + Q3) + 1) >> 1) : dq0; + const int dp3l = large_p ? ((dp3 + abs(TP5 - 2 * TP4 + TP3) + 1) >> 1) : dp3; + const int dq3l = large_q ? ((dq3 + abs(TQ5 - 2 * TQ4 + TQ3) + 1) >> 1) : dq3; + const int d0l = dp0l + dq0l; + const int d3l = dp3l + dq3l; + const int beta53 = beta * 3 >> 5; + const int beta_4 = beta >> 4; + max_len_p = large_p ? max_len_p : 3; + max_len_q = large_q ? max_len_q : 3; + + if (d0l + d3l < beta) { + const int sp0l = abs(P3 - P0) + (max_len_p == 7 ? abs(P7 - P6 - P5 + P4) : 0); + const int sq0l = abs(Q0 - Q3) + (max_len_q == 7 ? abs(Q4 - Q5 - Q6 + Q7) : 0); + const int sp3l = abs(TP3 - TP0) + (max_len_p == 7 ? abs(TP7 - TP6 - TP5 + TP4) : 0); + const int sq3l = abs(TQ0 - TQ3) + (max_len_q == 7 ? abs(TQ4 - TQ5 - TQ6 + TQ7) : 0); + const int sp0 = large_p ? ((sp0l + abs(P3 - P(max_len_p)) + 1) >> 1) : sp0l; + const int sp3 = large_p ? ((sp3l + abs(TP3 - TP(max_len_p)) + 1) >> 1) : sp3l; + const int sq0 = large_q ? ((sq0l + abs(Q3 - Q(max_len_q)) + 1) >> 1) : sq0l; + const int sq3 = large_q ? ((sq3l + abs(TQ3 - TQ(max_len_q)) + 1) >> 1) : sq3l; + if (sp0 + sq0 < beta53 && abs(P0 - Q0) < tc25 && + sp3 + sq3 < beta53 && abs(TP0 - TQ0) < tc25 && + (d0l << 1) < beta_4 && (d3l << 1) < beta_4) { + FUNC(loop_filter_luma_large)(pix, xstride, ystride, tc, no_p, no_q, max_len_p, max_len_q); + continue; + } + } + } + if (d0 + d3 < beta) { + if (max_len_p > 2 && max_len_q > 2 && + abs(P3 - P0) + abs(Q3 - Q0) < beta_3 && abs(P0 - Q0) < tc25 && + abs(TP3 - TP0) + abs(TQ3 - TQ0) < beta_3 && abs(TP0 - TQ0) < tc25 && + (d0 << 1) < beta_2 && (d3 << 1) < beta_2) { + FUNC(loop_filter_luma_strong)(pix, xstride, ystride, tc, tc << 1, tc * 3, no_p, no_q); + } else { + int nd_p = 1; + int nd_q = 1; + if (max_len_p > 1 && max_len_q > 1) { + if (dp0 + dp3 < ((beta + (beta >> 1)) >> 3)) + nd_p = 2; + if (dq0 + dq3 < ((beta + (beta >> 1)) >> 3)) + nd_q = 2; + } + FUNC(loop_filter_luma_weak)(pix, xstride, ystride, tc, beta, no_p, no_q, nd_p, nd_q); + } + } + } + } +} + +static void FUNC(loop_filter_chroma_strong)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, + const int size, const int32_t tc, const uint8_t no_p, const uint8_t no_q) +{ + for (int d = 0; d < size; d++) { + const int p3 = P3; + const int p2 = P2; + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + const int q3 = Q3; + if (!no_p) { + P0 = av_clip((p3 + p2 + p1 + 2 * p0 + q0 + q1 + q2 + 4) >> 3, p0 - tc, p0 + tc); + P1 = av_clip((2 * p3 + p2 + 2 * p1 + p0 + q0 + q1 + 4) >> 3, p1 - tc, p1 + tc); + P2 = av_clip((3 * p3 + 2 * p2 + p1 + p0 + q0 + 4) >> 3, p2 - tc, p2 + tc ); + } + if (!no_q) { + Q0 = av_clip((p2 + p1 + p0 + 2 * q0 + q1 + q2 + q3 + 4) >> 3, q0 - tc, q0 + tc); + Q1 = av_clip((p1 + p0 + q0 + 2 * q1 + q2 + 2 * q3 + 4) >> 3, q1 - tc, q1 + tc); + Q2 = av_clip((p0 + q0 + q1 + 2 * q2 + 3 * q3 + 4) >> 3, q2 - tc, q2 + tc); + } + pix += ystride; + } +} + +static void FUNC(loop_filter_chroma_strong_one_side)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, + const int size, const int32_t tc, const uint8_t no_p, const uint8_t no_q) +{ + for (int d = 0; d < size; d++) { + const int p1 = P1; + const int p0 = P0; + const int q0 = Q0; + const int q1 = Q1; + const int q2 = Q2; + const int q3 = Q3; + if (!no_p) { + P0 = av_clip((3 * p1 + 2 * p0 + q0 + q1 + q2 + 4) >> 3, p0 - tc, p0 + tc); + } + if (!no_q) { + Q0 = av_clip((2 * p1 + p0 + 2 * q0 + q1 + q2 + q3 + 4) >> 3, q0 - tc, q0 + tc); + Q1 = av_clip((p1 + p0 + q0 + 2 * q1 + q2 + 2 * q3 + 4) >> 3, q1 - tc, q1 + tc); + Q2 = av_clip((p0 + q0 + q1 + 2 * q2 + 3 * q3 + 4) >> 3, q2 - tc, q2 + tc); + } + pix += ystride; + } +} + +static void FUNC(vvc_loop_filter_chroma)(uint8_t *_pix, const ptrdiff_t _xstride, const ptrdiff_t _ystride, + const int32_t *_beta, const int32_t *_tc, const uint8_t *_no_p, const uint8_t *_no_q, + const uint8_t *_max_len_p, const uint8_t *_max_len_q, const int shift) +{ + const ptrdiff_t xstride = _xstride / sizeof(pixel); + const ptrdiff_t ystride = _ystride / sizeof(pixel); + const int size = shift ? 2 : 4; + const int end = 8 / size; // 8 samples a loop + + for (int i = 0; i < end; i++) { +#if BIT_DEPTH < 10 + const int tc = (_tc[i] + (1 << (9 - BIT_DEPTH))) >> (10 - BIT_DEPTH); +#else + const int tc = _tc[i] << (BIT_DEPTH - 10); +#endif + if (tc) { + pixel *pix = (pixel *)_pix + i * size * ystride; + const uint8_t no_p = _no_p[i]; + const uint8_t no_q = _no_q[i]; + + const int beta = _beta[i] << (BIT_DEPTH - 8); + const int beta_3 = beta >> 3; + const int beta_2 = beta >> 2; + + const int tc25 = ((tc * 5 + 1) >> 1); + + uint8_t max_len_p = _max_len_p[i]; + uint8_t max_len_q = _max_len_q[i]; + + if (!max_len_p || !max_len_q) + continue; + + if (max_len_q == 3){ + const int p1n = shift ? FP1 : TP1; + const int p2n = max_len_p == 1 ? p1n : (shift ? FP2 : TP2); + const int p0n = shift ? FP0 : TP0; + const int q0n = shift ? FQ0 : TQ0; + const int q1n = shift ? FQ1 : TQ1; + const int q2n = shift ? FQ2 : TQ2; + const int p3 = max_len_p == 1 ? P1 : P3; + const int p2 = max_len_p == 1 ? P1 : P2; + const int p1 = P1; + const int p0 = P0; + const int dp0 = abs(p2 - 2 * p1 + p0); + const int dq0 = abs(Q2 - 2 * Q1 + Q0); + + const int dp1 = abs(p2n - 2 * p1n + p0n); + const int dq1 = abs(q2n - 2 * q1n + q0n); + const int d0 = dp0 + dq0; + const int d1 = dp1 + dq1; + + if (d0 + d1 < beta) { + const int p3n = max_len_p == 1 ? p1n : (shift ? FP3 : TP3); + const int q3n = shift ? FQ3 : TQ3; + const int dsam0 = (d0 << 1) < beta_2 && (abs(p3 - p0) + abs(Q0 - Q3) < beta_3) && + abs(p0 - Q0) < tc25; + const int dsam1 = (d1 << 1) < beta_2 && (abs(p3n - p0n) + abs(q0n - q3n) < beta_3) && + abs(p0n - q0n) < tc25; + if (!dsam0 || !dsam1) + max_len_p = max_len_q = 1; + } else { + max_len_p = max_len_q = 1; + } + } + + if (max_len_p == 3 && max_len_q == 3) + FUNC(loop_filter_chroma_strong)(pix, xstride, ystride, size, tc, no_p, no_q); + else if (max_len_q == 3) + FUNC(loop_filter_chroma_strong_one_side)(pix, xstride, ystride, size, tc, no_p, no_q); + else + FUNC(loop_filter_chroma_weak)(pix, xstride, ystride, size, tc, no_p, no_q); + } + } +} + +static void FUNC(vvc_h_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, + const int32_t *beta, const int32_t *tc, const uint8_t *no_p, const uint8_t *no_q, + const uint8_t *max_len_p, const uint8_t *max_len_q, int shift) +{ + FUNC(vvc_loop_filter_chroma)(pix, stride, sizeof(pixel), beta, tc, + no_p, no_q, max_len_p, max_len_q, shift); +} + +static void FUNC(vvc_v_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, + const int32_t *beta, const int32_t *tc, const uint8_t *no_p, const uint8_t *no_q, + const uint8_t *max_len_p, const uint8_t *max_len_q, int shift) +{ + FUNC(vvc_loop_filter_chroma)(pix, sizeof(pixel), stride, beta, tc, + no_p, no_q, max_len_p, max_len_q, shift); +} + +static void FUNC(vvc_h_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, + const int32_t *beta, const int32_t *tc, const uint8_t *no_p, const uint8_t *no_q, + const uint8_t *max_len_p, const uint8_t *max_len_q, const int hor_ctu_edge) +{ + FUNC(vvc_loop_filter_luma)(pix, stride, sizeof(pixel), beta, tc, + no_p, no_q, max_len_p, max_len_q, hor_ctu_edge); +} + +static void FUNC(vvc_v_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, + const int32_t *beta, const int32_t *tc, const uint8_t *no_p, const uint8_t *no_q, + const uint8_t *max_len_p, const uint8_t *max_len_q, const int hor_ctu_edge) +{ + FUNC(vvc_loop_filter_luma)(pix, sizeof(pixel), stride, beta, tc, + no_p, no_q, max_len_p, max_len_q, hor_ctu_edge); +} + +static int FUNC(vvc_loop_ladf_level)(const uint8_t *_pix, const ptrdiff_t _xstride, const ptrdiff_t _ystride) +{ + const pixel *pix = (pixel *)_pix; + const ptrdiff_t xstride = _xstride / sizeof(pixel); + const ptrdiff_t ystride = _ystride / sizeof(pixel); + return (P0 + TP0 + Q0 + TQ0) >> 2; +} + +static int FUNC(vvc_h_loop_ladf_level)(const uint8_t *pix, ptrdiff_t stride) +{ + return FUNC(vvc_loop_ladf_level)(pix, stride, sizeof(pixel)); +} + +static int FUNC(vvc_v_loop_ladf_level)(const uint8_t *pix, ptrdiff_t stride) +{ + return FUNC(vvc_loop_ladf_level)(pix, sizeof(pixel), stride); +} + +#undef P7 +#undef P6 +#undef P5 +#undef P4 +#undef P3 +#undef P2 +#undef P1 +#undef P0 +#undef Q0 +#undef Q1 +#undef Q2 +#undef Q3 +#undef Q4 +#undef Q5 +#undef Q6 +#undef Q7 + +#undef TP7 +#undef TP6 +#undef TP5 +#undef TP4 +#undef TP3 +#undef TP2 +#undef TP1 +#undef TP0 +#undef TQ0 +#undef TQ1 +#undef TQ2 +#undef TQ3 +#undef TQ4 +#undef TQ5 +#undef TQ6 +#undef TQ7 + +static void FUNC(ff_vvc_lmcs_dsp_init)(VVCLMCSDSPContext *const lmcs) +{ + lmcs->filter = FUNC(lmcs_filter_luma); +} + +static void FUNC(ff_vvc_lf_dsp_init)(VVCLFDSPContext *const lf) +{ + lf->ladf_level[0] = FUNC(vvc_h_loop_ladf_level); + lf->ladf_level[1] = FUNC(vvc_v_loop_ladf_level); + lf->filter_luma[0] = FUNC(vvc_h_loop_filter_luma); + lf->filter_luma[1] = FUNC(vvc_v_loop_filter_luma); + lf->filter_chroma[0] = FUNC(vvc_h_loop_filter_chroma); + lf->filter_chroma[1] = FUNC(vvc_v_loop_filter_chroma); +} + +static void FUNC(ff_vvc_sao_dsp_init)(VVCSAODSPContext *const sao) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(sao->band_filter); i++) + sao->band_filter[i] = FUNC(sao_band_filter); + for (int i = 0; i < FF_ARRAY_ELEMS(sao->edge_filter); i++) + sao->edge_filter[i] = FUNC(sao_edge_filter); + sao->edge_restore[0] = FUNC(sao_edge_restore_0); + sao->edge_restore[1] = FUNC(sao_edge_restore_1); +} + +static void FUNC(ff_vvc_alf_dsp_init)(VVCALFDSPContext *const alf) +{ + alf->filter[LUMA] = FUNC(alf_filter_luma); + alf->filter[CHROMA] = FUNC(alf_filter_chroma); + alf->filter_cc = FUNC(alf_filter_cc); + alf->classify = FUNC(alf_classify); + alf->recon_coeff_and_clip = FUNC(alf_recon_coeff_and_clip); +} diff --git a/libavcodec/vvc/vvc_inter.c b/libavcodec/vvc/vvc_inter.c new file mode 100644 index 00000000000..c5629f7f6ff --- /dev/null +++ b/libavcodec/vvc/vvc_inter.c @@ -0,0 +1,926 @@ +/* + * VVC inter prediction + * + * Copyright (C) 2022 Nuo Mi + * + * 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/frame.h" + +#include "vvc_data.h" +#include "vvc_inter.h" +#include "vvc_mvs.h" +#include "vvc_refs.h" + +// +1 is enough, + 32 for asm alignment +#define PROF_TEMP_OFFSET (MAX_PB_SIZE + 32) +static const int bcw_w_lut[] = {4, 5, 3, 10, -2}; + +static int emulated_edge(const VVCFrameContext *fc, uint8_t *dst, const uint8_t **src, ptrdiff_t *src_stride, + const int x_off, const int y_off, const int block_w, const int block_h, const int is_luma) +{ + const int extra_before = is_luma ? LUMA_EXTRA_BEFORE : CHROMA_EXTRA_BEFORE; + const int extra_after = is_luma ? LUMA_EXTRA_AFTER : CHROMA_EXTRA_AFTER; + const int extra = is_luma ? LUMA_EXTRA : CHROMA_EXTRA; + const int pic_width = is_luma ? fc->ps.pps->width : (fc->ps.pps->width >> fc->ps.sps->hshift[1]); + const int pic_height = is_luma ? fc->ps.pps->height : (fc->ps.pps->height >> fc->ps.sps->vshift[1]); + + if (x_off < extra_before || y_off < extra_before || + x_off >= pic_width - block_w - extra_after || + y_off >= pic_height - block_h - extra_after) { + const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << fc->ps.sps->pixel_shift; + int offset = extra_before * *src_stride + (extra_before << fc->ps.sps->pixel_shift); + int buf_offset = extra_before * edge_emu_stride + (extra_before << fc->ps.sps->pixel_shift); + + fc->vdsp.emulated_edge_mc(dst, *src - offset, edge_emu_stride, *src_stride, + block_w + extra, block_h + extra, x_off - extra_before, y_off - extra_before, + pic_width, pic_height); + + *src = dst + buf_offset; + *src_stride = edge_emu_stride; + return 1; + } + return 0; +} + +static void emulated_edge_dmvr(const VVCFrameContext *fc, uint8_t *dst, const uint8_t **src, ptrdiff_t *src_stride, + const int x_sb, const int y_sb, const int x_off, const int y_off, const int block_w, const int block_h, const int is_luma) +{ + const int extra_before = is_luma ? LUMA_EXTRA_BEFORE : CHROMA_EXTRA_BEFORE; + const int extra_after = is_luma ? LUMA_EXTRA_AFTER : CHROMA_EXTRA_AFTER; + const int extra = is_luma ? LUMA_EXTRA : CHROMA_EXTRA; + const int pic_width = is_luma ? fc->ps.pps->width : (fc->ps.pps->width >> fc->ps.sps->hshift[1]); + const int pic_height = is_luma ? fc->ps.pps->height : (fc->ps.pps->height >> fc->ps.sps->vshift[1]); + + if (x_off < extra_before || y_off < extra_before || + x_off >= pic_width - block_w - extra_after || + y_off >= pic_height - block_h - extra_after|| + (x_off != x_sb || y_off != y_sb)) { + const int ps = fc->ps.sps->pixel_shift; + const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << ps; + const int offset = extra_before * *src_stride + (extra_before << ps); + const int buf_offset = extra_before * edge_emu_stride + (extra_before << ps); + + const int start_x = FFMIN(FFMAX(x_sb - extra_before, 0), pic_width - 1); + const int start_y = FFMIN(FFMAX(y_sb - extra_before, 0), pic_height - 1); + const int width = FFMAX(FFMIN(pic_width, x_sb + block_w + extra_after) - start_x, 1); + const int height = FFMAX(FFMIN(pic_height, y_sb + block_h + extra_after) - start_y, 1); + + fc->vdsp.emulated_edge_mc(dst, *src - offset, edge_emu_stride, *src_stride, block_w + extra, block_h + extra, + x_off - start_x - extra_before, y_off - start_y - extra_before, width, height); + + *src = dst + buf_offset; + *src_stride = edge_emu_stride; + } +} + +static void emulated_edge_bilinear(const VVCFrameContext *fc, uint8_t *dst, const uint8_t **src, ptrdiff_t *src_stride, + const int x_off, const int y_off, const int block_w, const int block_h) +{ + int pic_width = fc->ps.pps->width; + int pic_height = fc->ps.pps->height; + + if (x_off < BILINEAR_EXTRA_BEFORE || y_off < BILINEAR_EXTRA_BEFORE || + x_off >= pic_width - block_w - BILINEAR_EXTRA_AFTER || + y_off >= pic_height - block_h - BILINEAR_EXTRA_AFTER) { + const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << fc->ps.sps->pixel_shift; + const int offset = BILINEAR_EXTRA_BEFORE * *src_stride + (BILINEAR_EXTRA_BEFORE << fc->ps.sps->pixel_shift); + const int buf_offset = BILINEAR_EXTRA_BEFORE * edge_emu_stride + (BILINEAR_EXTRA_BEFORE << fc->ps.sps->pixel_shift); + + fc->vdsp.emulated_edge_mc(dst, *src - offset, edge_emu_stride, *src_stride, block_w + BILINEAR_EXTRA, block_h + BILINEAR_EXTRA, + x_off - BILINEAR_EXTRA_BEFORE, y_off - BILINEAR_EXTRA_BEFORE, pic_width, pic_height); + + *src = dst + buf_offset; + *src_stride = edge_emu_stride; + } +} + + +#define EMULATED_EDGE_LUMA(dst, src, src_stride, x_off, y_off) \ + emulated_edge(fc, dst, src, src_stride, x_off, y_off, block_w, block_h, 1) + +#define EMULATED_EDGE_CHROMA(dst, src, src_stride, x_off, y_off) \ + emulated_edge(fc, dst, src, src_stride, x_off, y_off, block_w, block_h, 0) + +#define EMULATED_EDGE_DMVR_LUMA(dst, src, src_stride, x_sb, y_sb, x_off, y_off) \ + emulated_edge_dmvr(fc, dst, src, src_stride, x_sb, y_sb, x_off, y_off, block_w, block_h, 1) + +#define EMULATED_EDGE_DMVR_CHROMA(dst, src, src_stride, x_sb, y_sb, x_off, y_off) \ + emulated_edge_dmvr(fc, dst, src, src_stride, x_sb, y_sb, x_off, y_off, block_w, block_h, 0) + +#define EMULATED_EDGE_BILINEAR(dst, src, src_stride, x_off, y_off) \ + emulated_edge_bilinear(fc, dst, src, src_stride, x_off, y_off, pred_w, pred_h) + +// part of 8.5.6.6 Weighted sample prediction process +static int derive_weight_uni(int *denom, int *wx, int *ox, + const VVCLocalContext *lc, const MvField *mvf, const int c_idx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const int weight_flag = (IS_P(sh->r) && pps->r->pps_weighted_pred_flag) || + (IS_B(sh->r) && pps->r->pps_weighted_bipred_flag); + if (weight_flag) { + const int lx = mvf->pred_flag - PF_L0; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &fc->ps.ph.pwt : &sh->pwt; + + *denom = w->log2_denom[c_idx > 0]; + *wx = w->weight[lx][c_idx][mvf->ref_idx[lx]]; + *ox = w->offset[lx][c_idx][mvf->ref_idx[lx]]; + } + return weight_flag; +} + +// part of 8.5.6.6 Weighted sample prediction process +static int derive_weight(int *denom, int *w0, int *w1, int *o0, int *o1, + const VVCLocalContext *lc, const MvField *mvf, const int c_idx, const int dmvr_flag) +{ + const VVCFrameContext *fc = lc->fc; + const VVCPPS *pps = fc->ps.pps; + const VVCSH *sh = &lc->sc->sh; + const int bcw_idx = mvf->bcw_idx; + const int weight_flag = (IS_P(sh->r) && pps->r->pps_weighted_pred_flag) || + (IS_B(sh->r) && pps->r->pps_weighted_bipred_flag && !dmvr_flag); + if ((!weight_flag && !bcw_idx) || (bcw_idx && lc->cu->ciip_flag)) + return 0; + + if (bcw_idx) { + *denom = 2; + *w1 = bcw_w_lut[bcw_idx]; + *w0 = 8 - *w1; + *o0 = *o1 = 0; + } else { + const VVCPPS *pps = fc->ps.pps; + const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &fc->ps.ph.pwt : &sh->pwt; + + *denom = w->log2_denom[c_idx > 0]; + *w0 = w->weight[L0][c_idx][mvf->ref_idx[L0]]; + *w1 = w->weight[L1][c_idx][mvf->ref_idx[L1]]; + *o0 = w->offset[L0][c_idx][mvf->ref_idx[L0]]; + *o1 = w->offset[L1][c_idx][mvf->ref_idx[L1]]; + } + return 1; +} + +static void luma_mc(VVCLocalContext *lc, int16_t *dst, const AVFrame *ref, const Mv *mv, + int x_off, int y_off, const int block_w, const int block_h) +{ + const VVCFrameContext *fc = lc->fc; + const uint8_t *src = ref->data[0]; + ptrdiff_t src_stride = ref->linesize[0]; + const int idx = av_log2(block_w) - 1; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + const int8_t *hf = ff_vvc_inter_luma_filters[0][mx]; + const int8_t *vf = ff_vvc_inter_luma_filters[0][my]; + + x_off += mv->x >> 4; + y_off += mv->y >> 4; + src += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift)); + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + + fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](dst, src, src_stride, block_h, hf, vf, block_w); +} + +static void chroma_mc(VVCLocalContext *lc, int16_t *dst, const AVFrame *ref, const Mv *mv, + int x_off, int y_off, const int block_w, const int block_h, const int c_idx) +{ + const VVCFrameContext *fc = lc->fc; + const uint8_t *src = ref->data[c_idx]; + ptrdiff_t src_stride = ref->linesize[c_idx]; + int hs = fc->ps.sps->hshift[c_idx]; + int vs = fc->ps.sps->vshift[c_idx]; + const int idx = av_log2(block_w) - 1; + const intptr_t mx = av_mod_uintp2(mv->x, 4 + hs) << (1 - hs); + const intptr_t my = av_mod_uintp2(mv->y, 4 + vs) << (1 - vs); + const int8_t *hf = ff_vvc_inter_chroma_filters[0][mx]; + const int8_t *vf = ff_vvc_inter_chroma_filters[0][my]; + + x_off += mv->x >> (4 + hs); + y_off += mv->y >> (4 + vs); + src += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift)); + + EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + fc->vvcdsp.inter.put[CHROMA][idx][!!my][!!mx](dst, src, src_stride, block_h, hf, vf, block_w); +} + +static void luma_mc_uni(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref, const MvField *mvf, int x_off, int y_off, const int block_w, const int block_h, + const int hf_idx, const int vf_idx) +{ + const VVCFrameContext *fc = lc->fc; + const int lx = mvf->pred_flag - PF_L0; + const Mv *mv = mvf->mv + lx; + const uint8_t *src = ref->data[0]; + ptrdiff_t src_stride = ref->linesize[0]; + const int idx = av_log2(block_w) - 1; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + const int8_t *hf = ff_vvc_inter_luma_filters[hf_idx][mx]; + const int8_t *vf = ff_vvc_inter_luma_filters[vf_idx][my]; + int denom, wx, ox; + + x_off += mv->x >> 4; + y_off += mv->y >> 4; + src += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift)); + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + + if (derive_weight_uni(&denom, &wx, &ox, lc, mvf, LUMA)) { + fc->vvcdsp.inter.put_uni_w[LUMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, denom, wx, ox, hf, vf, block_w); + } else { + fc->vvcdsp.inter.put_uni[LUMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, hf, vf, block_w); + } +} + +static void luma_mc_bi(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref0, const Mv *mv0, const int x_off, const int y_off, const int block_w, const int block_h, + const AVFrame *ref1, const Mv *mv1, const MvField *mvf, const int hf_idx, const int vf_idx, + const MvField *orig_mv, const int sb_bdof_flag) +{ + const VVCFrameContext *fc = lc->fc; + const PredictionUnit *pu = &lc->cu->pu; + const int idx = av_log2(block_w) - 1; + const AVFrame *ref[] = { ref0, ref1 }; + int16_t *tmp[] = { lc->tmp + sb_bdof_flag * PROF_TEMP_OFFSET, lc->tmp1 + sb_bdof_flag * PROF_TEMP_OFFSET }; + int denom, w0, w1, o0, o1; + const int weight_flag = derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, LUMA, pu->dmvr_flag); + + for (int i = L0; i <= L1; i++) { + const Mv *mv = mvf->mv + i; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + const int ox = x_off + (mv->x >> 4); + const int oy = y_off + (mv->y >> 4); + ptrdiff_t src_stride = ref[i]->linesize[0]; + const uint8_t *src = ref[i]->data[0] + oy * src_stride + (ox * (1 << fc->ps.sps->pixel_shift)); + const int8_t *hf = ff_vvc_inter_luma_filters[hf_idx][mx]; + const int8_t *vf = ff_vvc_inter_luma_filters[vf_idx][my]; + + if (pu->dmvr_flag) { + const int x_sb = x_off + (orig_mv->mv[i].x >> 4); + const int y_sb = y_off + (orig_mv->mv[i].y >> 4); + + EMULATED_EDGE_DMVR_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_sb, y_sb, ox, oy); + } else { + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, ox, oy); + } + fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](tmp[i], src, src_stride, block_h, hf, vf, block_w); + if (sb_bdof_flag) + fc->vvcdsp.inter.bdof_fetch_samples(tmp[i], src, src_stride, mx, my, block_w, block_h); + } + + if (sb_bdof_flag) + fc->vvcdsp.inter.apply_bdof(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h); + else if (weight_flag) + fc->vvcdsp.inter.w_avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h, denom, w0, w1, o0, o1); + else + fc->vvcdsp.inter.avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h); +} + +static void chroma_mc_uni(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const uint8_t *src, ptrdiff_t src_stride, int x_off, int y_off, + const int block_w, const int block_h, const MvField *mvf, const int c_idx, + const int hf_idx, const int vf_idx) +{ + const VVCFrameContext *fc = lc->fc; + const int lx = mvf->pred_flag - PF_L0; + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const int idx = av_log2(block_w) - 1; + const Mv *mv = &mvf->mv[lx]; + const intptr_t mx = av_mod_uintp2(mv->x, 4 + hs) << (1 - hs); + const intptr_t my = av_mod_uintp2(mv->y, 4 + vs) << (1 - vs); + const int8_t *hf = ff_vvc_inter_chroma_filters[hf_idx][mx]; + const int8_t *vf = ff_vvc_inter_chroma_filters[vf_idx][my]; + int denom, wx, ox; + + x_off += mv->x >> (4 + hs); + y_off += mv->y >> (4 + vs); + src += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift)); + + + EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + if (derive_weight_uni(&denom, &wx, &ox, lc, mvf, c_idx)) { + fc->vvcdsp.inter.put_uni_w[CHROMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, denom, wx, ox, hf, vf, block_w); + } else { + fc->vvcdsp.inter.put_uni[CHROMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, + block_h, hf, vf, block_w); + } +} + +static void chroma_mc_bi(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref0, const AVFrame *ref1, const int x_off, const int y_off, + const int block_w, const int block_h, const MvField *mvf, const int c_idx, + const int hf_idx, const int vf_idx, const MvField *orig_mv, const int dmvr_flag, const int ciip_flag) +{ + const VVCFrameContext *fc = lc->fc; + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const int idx = av_log2(block_w) - 1; + const AVFrame *ref[] = { ref0, ref1 }; + int16_t *tmp[] = { lc->tmp, lc->tmp1 }; + int denom, w0, w1, o0, o1; + const int weight_flag = derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, c_idx, dmvr_flag); + + for (int i = L0; i <= L1; i++) { + const Mv *mv = mvf->mv + i; + const int mx = av_mod_uintp2(mv->x, 4 + hs) << (1 - hs); + const int my = av_mod_uintp2(mv->y, 4 + vs) << (1 - vs); + const int ox = x_off + (mv->x >> (4 + hs)); + const int oy = y_off + (mv->y >> (4 + vs)); + ptrdiff_t src_stride = ref[i]->linesize[c_idx]; + const uint8_t *src = ref[i]->data[c_idx] + oy * src_stride + (ox * (1 << fc->ps.sps->pixel_shift)); + const int8_t *hf = ff_vvc_inter_chroma_filters[hf_idx][mx]; + const int8_t *vf = ff_vvc_inter_chroma_filters[vf_idx][my]; + if (dmvr_flag) { + const int x_sb = x_off + (orig_mv->mv[i].x >> (4 + hs)); + const int y_sb = y_off + (orig_mv->mv[i].y >> (4 + vs)); + EMULATED_EDGE_DMVR_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_sb, y_sb, ox, oy); + } else { + EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, ox, oy); + } + fc->vvcdsp.inter.put[CHROMA][idx][!!my][!!mx](tmp[i], src, src_stride, block_h, hf, vf, block_w); + } + if (weight_flag) + fc->vvcdsp.inter.w_avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h, denom, w0, w1, o0, o1); + else + fc->vvcdsp.inter.avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h); +} + +static void luma_prof_uni(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref, const MvField *mvf, int x_off, int y_off, const int block_w, const int block_h, + const int cb_prof_flag, const int16_t *diff_mv_x, const int16_t *diff_mv_y) +{ + const VVCFrameContext *fc = lc->fc; + const uint8_t *src = ref->data[0]; + ptrdiff_t src_stride = ref->linesize[0]; + uint16_t *prof_tmp = lc->tmp + PROF_TEMP_OFFSET; + const int idx = av_log2(block_w) - 1; + const int lx = mvf->pred_flag - PF_L0; + const Mv *mv = mvf->mv + lx; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + const int8_t *hf = ff_vvc_inter_luma_filters[2][mx]; + const int8_t *vf = ff_vvc_inter_luma_filters[2][my]; + int denom, wx, ox; + const int weight_flag = derive_weight_uni(&denom, &wx, &ox, lc, mvf, LUMA); + + x_off += mv->x >> 4; + y_off += mv->y >> 4; + src += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift)); + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off); + if (cb_prof_flag) { + fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](prof_tmp, src, src_stride, AFFINE_MIN_BLOCK_SIZE, hf, vf, AFFINE_MIN_BLOCK_SIZE); + fc->vvcdsp.inter.fetch_samples(prof_tmp, src, src_stride, mx, my); + if (!weight_flag) + fc->vvcdsp.inter.apply_prof_uni(dst, dst_stride, prof_tmp, diff_mv_x, diff_mv_y); + else + fc->vvcdsp.inter.apply_prof_uni_w(dst, dst_stride, prof_tmp, diff_mv_x, diff_mv_y, denom, wx, ox); + } else { + if (!weight_flag) + fc->vvcdsp.inter.put_uni[LUMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, block_h, hf, vf, block_w); + else + fc->vvcdsp.inter.put_uni_w[LUMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, block_h, denom, wx, ox, hf, vf, block_w); + } +} + +static void luma_prof_bi(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride, + const AVFrame *ref0, const AVFrame *ref1, const MvField *mvf, const int x_off, const int y_off, + const int block_w, const int block_h) +{ + const VVCFrameContext *fc = lc->fc; + const PredictionUnit *pu = &lc->cu->pu; + const AVFrame *ref[] = { ref0, ref1 }; + int16_t *tmp[] = { lc->tmp, lc->tmp1 }; + uint16_t *prof_tmp = lc->tmp2 + PROF_TEMP_OFFSET; + const int idx = av_log2(block_w) - 1; + int denom, w0, w1, o0, o1; + const int weight_flag = derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, LUMA, 0); + + for (int i = L0; i <= L1; i++) { + const Mv *mv = mvf->mv + i; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + const int ox = x_off + (mv->x >> 4); + const int oy = y_off + (mv->y >> 4); + ptrdiff_t src_stride = ref[i]->linesize[0]; + const uint8_t *src = ref[i]->data[0] + oy * src_stride + (ox * (1 << fc->ps.sps->pixel_shift)); + const int8_t *hf = ff_vvc_inter_luma_filters[2][mx]; + const int8_t *vf = ff_vvc_inter_luma_filters[2][my]; + + EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, ox, oy); + if (!pu->cb_prof_flag[i]) { + fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](tmp[i], src, src_stride, block_h, hf, vf, block_w); + } else { + fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](prof_tmp, src, src_stride, AFFINE_MIN_BLOCK_SIZE, hf, vf, AFFINE_MIN_BLOCK_SIZE); + fc->vvcdsp.inter.fetch_samples(prof_tmp, src, src_stride, mx, my); + fc->vvcdsp.inter.apply_prof(tmp[i], prof_tmp, pu->diff_mv_x[i], pu->diff_mv_y[i]); + } + } + + if (weight_flag) + fc->vvcdsp.inter.w_avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h, denom, w0, w1, o0, o1); + else + fc->vvcdsp.inter.avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h); +} + +static int pred_get_refs(const VVCLocalContext *lc, VVCFrame *ref[2], const MvField *mv) +{ + const RefPicList *rpl = lc->sc->rpl; + + for (int mask = PF_L0; mask <= PF_L1; mask++) { + if (mv->pred_flag & mask) { + const int lx = mask - PF_L0; + ref[lx] = rpl[lx].ref[mv->ref_idx[lx]]; + if (!ref[lx]) + return AVERROR_INVALIDDATA; + } + } + return 0; +} + +#define POS(c_idx, x, y) \ + &fc->frame->data[c_idx][((y) >> fc->ps.sps->vshift[c_idx]) * fc->frame->linesize[c_idx] + \ + (((x) >> fc->ps.sps->hshift[c_idx]) << fc->ps.sps->pixel_shift)] + +static void pred_gpm_blk(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const PredictionUnit *pu = &cu->pu; + + const uint8_t angle_idx = ff_vvc_gpm_angle_idx[pu->gpm_partition_idx]; + const uint8_t weights_idx = ff_vvc_gpm_angle_to_weights_idx[angle_idx]; + const int w = av_log2(cu->cb_width) - 3; + const int h = av_log2(cu->cb_height) - 3; + const uint8_t off_x = ff_vvc_gpm_weights_offset_x[pu->gpm_partition_idx][h][w]; + const uint8_t off_y = ff_vvc_gpm_weights_offset_y[pu->gpm_partition_idx][h][w]; + const uint8_t mirror_type = ff_vvc_gpm_angle_to_mirror[angle_idx]; + const uint8_t *weights; + + const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1; + + int16_t *tmp[2] = {lc->tmp, lc->tmp1}; + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int hs = fc->ps.sps->hshift[c_idx]; + const int vs = fc->ps.sps->vshift[c_idx]; + const int x = lc->cu->x0 >> hs; + const int y = lc->cu->y0 >> vs; + const int width = cu->cb_width >> hs; + const int height = cu->cb_height >> vs; + uint8_t *dst = POS(c_idx, lc->cu->x0, lc->cu->y0); + ptrdiff_t dst_stride = fc->frame->linesize[c_idx]; + + int step_x = 1 << hs; + int step_y = VVC_GPM_WEIGHT_SIZE << vs; + if (!mirror_type) { + weights = &ff_vvc_gpm_weights[weights_idx][off_y * VVC_GPM_WEIGHT_SIZE + off_x]; + } else if (mirror_type == 1) { + step_x = -step_x; + weights = &ff_vvc_gpm_weights[weights_idx][off_y * VVC_GPM_WEIGHT_SIZE + VVC_GPM_WEIGHT_SIZE - 1- off_x]; + } else { + step_y = -step_y; + weights = &ff_vvc_gpm_weights[weights_idx][(VVC_GPM_WEIGHT_SIZE - 1 - off_y) * VVC_GPM_WEIGHT_SIZE + off_x]; + } + + for (int i = 0; i < 2; i++) { + const MvField *mv = pu->gpm_mv + i; + const int lx = mv->pred_flag - PF_L0; + VVCFrame *ref = lc->sc->rpl[lx].ref[mv->ref_idx[lx]]; + if (!ref) + return; + if (c_idx) + chroma_mc(lc, tmp[i], ref->frame, mv->mv + lx, x, y, width, height, c_idx); + else + luma_mc(lc, tmp[i], ref->frame, mv->mv + lx, x, y, width, height); + } + fc->vvcdsp.inter.put_gpm(dst, dst_stride, width, height, tmp[0], tmp[1], weights, step_x, step_y); + } + return; +} + +static int ciip_derive_intra_weight(const VVCLocalContext *lc, const int x0, const int y0, + const int width, const int height) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y); + const int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int available_l = lc->ctb_left_flag || x0b; + const int available_u = lc->ctb_up_flag || y0b; + const int min_pu_width = fc->ps.pps->min_pu_width; + + int w = 1; + + if (available_u &&fc->tab.mvf[((y0 - 1) >> MIN_PU_LOG2) * min_pu_width + ((x0 - 1 + width)>> MIN_PU_LOG2)].pred_flag == PF_INTRA) + w++; + + if (available_l && fc->tab.mvf[((y0 - 1 + height)>> MIN_PU_LOG2) * min_pu_width + ((x0 - 1) >> MIN_PU_LOG2)].pred_flag == PF_INTRA) + w++; + + return w; +} + +static void pred_regular_luma(VVCLocalContext *lc, const int hf_idx, const int vf_idx, const MvField *mv, + const int x0, const int y0, const int sbw, const int sbh, const MvField *orig_mv, const int sb_bdof_flag) +{ + const SliceContext *sc = lc->sc; + const VVCFrameContext *fc = lc->fc; + const int ciip_flag = lc->cu->ciip_flag; + uint8_t *dst = POS(0, x0, y0); + const ptrdiff_t dst_stride = fc->frame->linesize[0]; + uint8_t *inter = ciip_flag ? (uint8_t *)lc->ciip_tmp1 : dst; + const ptrdiff_t inter_stride = ciip_flag ? (MAX_PB_SIZE * sizeof(uint16_t)) : dst_stride; + VVCFrame *ref[2]; + + if (pred_get_refs(lc, ref, mv) < 0) + return; + + if (mv->pred_flag != PF_BI) { + const int lx = mv->pred_flag - PF_L0; + luma_mc_uni(lc, inter, inter_stride, ref[lx]->frame, + mv, x0, y0, sbw, sbh, hf_idx, vf_idx); + } else { + luma_mc_bi(lc, inter, inter_stride, ref[0]->frame, + &mv->mv[0], x0, y0, sbw, sbh, ref[1]->frame, &mv->mv[1], mv, + hf_idx, vf_idx, orig_mv, sb_bdof_flag); + } + + if (ciip_flag) { + const int intra_weight = ciip_derive_intra_weight(lc, x0, y0, sbw, sbh); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, sbw, sbh, 0); + if (sc->sh.r->sh_lmcs_used_flag) + fc->vvcdsp.lmcs.filter(inter, inter_stride, sbw, sbh, &fc->ps.lmcs.fwd_lut); + fc->vvcdsp.inter.put_ciip(dst, dst_stride, sbw, sbh, inter, inter_stride, intra_weight); + + } +} + +static void pred_regular_chroma(VVCLocalContext *lc, const MvField *mv, + const int x0, const int y0, const int sbw, const int sbh, const MvField *orig_mv, const int dmvr_flag) +{ + const VVCFrameContext *fc = lc->fc; + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const int x0_c = x0 >> hs; + const int y0_c = y0 >> vs; + const int w_c = sbw >> hs; + const int h_c = sbh >> vs; + const int do_ciip = lc->cu->ciip_flag && (w_c > 2); + + uint8_t* dst1 = POS(1, x0, y0); + uint8_t* dst2 = POS(2, x0, y0); + const ptrdiff_t dst1_stride = fc->frame->linesize[1]; + const ptrdiff_t dst2_stride = fc->frame->linesize[2]; + + uint8_t *inter1 = do_ciip ? (uint8_t *)lc->ciip_tmp1 : dst1; + const ptrdiff_t inter1_stride = do_ciip ? (MAX_PB_SIZE * sizeof(uint16_t)) : dst1_stride; + + uint8_t *inter2 = do_ciip ? (uint8_t *)lc->ciip_tmp2 : dst2; + const ptrdiff_t inter2_stride = do_ciip ? (MAX_PB_SIZE * sizeof(uint16_t)) : dst2_stride; + + //fix me + const int hf_idx = 0; + const int vf_idx = 0; + VVCFrame *ref[2]; + + if (pred_get_refs(lc, ref, mv) < 0) + return; + + if (mv->pred_flag != PF_BI) { + const int lx = mv->pred_flag - PF_L0; + if (!ref[lx]) + return; + + chroma_mc_uni(lc, inter1, inter1_stride, ref[lx]->frame->data[1], ref[lx]->frame->linesize[1], + x0_c, y0_c, w_c, h_c, mv, CB, hf_idx, vf_idx); + chroma_mc_uni(lc, inter2, inter2_stride, ref[lx]->frame->data[2], ref[lx]->frame->linesize[2], + x0_c, y0_c, w_c, h_c, mv, CR, hf_idx, vf_idx); + } else { + if (!ref[0] || !ref[1]) + return; + + chroma_mc_bi(lc, inter1, inter1_stride, ref[0]->frame, ref[1]->frame, + x0_c, y0_c, w_c, h_c, mv, CB, hf_idx, vf_idx, orig_mv, dmvr_flag, lc->cu->ciip_flag); + + chroma_mc_bi(lc, inter2, inter2_stride, ref[0]->frame, ref[1]->frame, + x0_c, y0_c, w_c, h_c, mv, CR, hf_idx, vf_idx, orig_mv, dmvr_flag, lc->cu->ciip_flag); + + } + if (do_ciip) { + const int intra_weight = ciip_derive_intra_weight(lc, x0, y0, sbw, sbh); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, sbw, sbh, 1); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, sbw, sbh, 2); + fc->vvcdsp.inter.put_ciip(dst1, dst1_stride, w_c, h_c, inter1, inter1_stride, intra_weight); + fc->vvcdsp.inter.put_ciip(dst2, dst2_stride, w_c, h_c, inter2, inter2_stride, intra_weight); + + } +} + +// 8.5.3.5 Parametric motion vector refinement process +static int parametric_mv_refine(const int *sad, const int stride) +{ + const int sad_minus = sad[-stride]; + const int sad_center = sad[0]; + const int sad_plus = sad[stride]; + int dmvc; + int denom = (( sad_minus + sad_plus) - (sad_center << 1 ) ) << 3; + if (!denom) + dmvc = 0; + else { + if (sad_minus == sad_center) + dmvc = -8; + else if (sad_plus == sad_center) + dmvc = 8; + else { + int num = ( sad_minus - sad_plus ) * (1 << 4); + int sign_num = 0; + int quotient = 0; + int counter = 3; + if (num < 0 ) { + num = - num; + sign_num = 1; + } + while (counter > 0) { + counter = counter - 1; + quotient = quotient << 1; + if ( num >= denom ) { + num = num - denom; + quotient = quotient + 1; + } + denom = (denom >> 1); + } + if (sign_num == 1 ) + dmvc = -quotient; + else + dmvc = quotient; + } + } + return dmvc; +} + +#define SAD_ARRAY_SIZE 5 +//8.5.3 Decoder-side motion vector refinement process +static void dmvr_mv_refine(VVCLocalContext *lc, MvField *mvf, MvField *orig_mv, int *sb_bdof_flag, + const AVFrame *ref0, const AVFrame *ref1, const int x_off, const int y_off, const int block_w, const int block_h) +{ + const VVCFrameContext *fc = lc->fc; + const int sr_range = 2; + const AVFrame *ref[] = { ref0, ref1 }; + int16_t *tmp[] = { lc->tmp, lc->tmp1 }; + int sad[SAD_ARRAY_SIZE][SAD_ARRAY_SIZE]; + int min_dx, min_dy, min_sad, dx, dy; + + *orig_mv = *mvf; + min_dx = min_dy = dx = dy = 2; + + for (int i = L0; i <= L1; i++) { + const int pred_w = block_w + 2 * sr_range; + const int pred_h = block_h + 2 * sr_range; + const Mv *mv = mvf->mv + i; + const int mx = mv->x & 0xf; + const int my = mv->y & 0xf; + const int ox = x_off + (mv->x >> 4) - sr_range; + const int oy = y_off + (mv->y >> 4) - sr_range; + ptrdiff_t src_stride = ref[i]->linesize[LUMA]; + const uint8_t *src = ref[i]->data[LUMA] + oy * src_stride + (ox * (1 << fc->ps.sps->pixel_shift)); + EMULATED_EDGE_BILINEAR(lc->edge_emu_buffer, &src, &src_stride, ox, oy); + fc->vvcdsp.inter.dmvr[!!my][!!mx](tmp[i], src, src_stride, pred_h, mx, my, pred_w); + } + + min_sad = fc->vvcdsp.inter.sad(tmp[L0], tmp[L1], dx, dy, block_w, block_h); + min_sad -= min_sad >> 2; + sad[dy][dx] = min_sad; + + if (min_sad >= block_w * block_h) { + int dmv[2]; + // 8.5.3.4 Array entry selection process + for (dy = 0; dy < SAD_ARRAY_SIZE; dy++) { + for (dx = 0; dx < SAD_ARRAY_SIZE; dx++) { + if (dx != sr_range || dy != sr_range) { + sad[dy][dx] = fc->vvcdsp.inter.sad(lc->tmp, lc->tmp1, dx, dy, block_w, block_h); + if (sad[dy][dx] < min_sad) { + min_sad = sad[dy][dx]; + min_dx = dx; + min_dy = dy; + } + } + } + } + dmv[0] = (min_dx - sr_range) * (1 << 4); + dmv[1] = (min_dy - sr_range) * (1 << 4); + if (min_dx != 0 && min_dx != 4 && min_dy != 0 && min_dy != 4) { + dmv[0] += parametric_mv_refine(&sad[min_dy][min_dx], 1); + dmv[1] += parametric_mv_refine(&sad[min_dy][min_dx], SAD_ARRAY_SIZE); + } + + for (int i = L0; i <= L1; i++) { + Mv *mv = mvf->mv + i; + mv->x += (1 - 2 * i) * dmv[0]; + mv->y += (1 - 2 * i) * dmv[1]; + ff_vvc_clip_mv(mv); + } + } + if (min_sad < 2 * block_w * block_h) { + *sb_bdof_flag = 0; + } +} + +static void set_dmvr_info(VVCFrameContext *fc, const int x0, const int y0, + const int width, const int height, const MvField *mvf) + +{ + const VVCPPS *pps = fc->ps.pps; + + for (int y = y0; y < y0 + height; y += MIN_PU_SIZE) { + for (int x = x0; x < x0 + width; x += MIN_PU_SIZE) { + const int idx = pps->min_pu_width * (y >> MIN_PU_LOG2) + (x >> MIN_PU_LOG2); + fc->ref->tab_dmvr_mvf[idx] = *mvf; + } + } +} + +static void derive_sb_mv(VVCLocalContext *lc, MvField *mv, MvField *orig_mv, int *sb_bdof_flag, + const int x0, const int y0, const int sbw, const int sbh) +{ + VVCFrameContext *fc = lc->fc; + const PredictionUnit *pu = &lc->cu->pu; + + *orig_mv = *mv = *ff_vvc_get_mvf(fc, x0, y0); + if (pu->bdof_flag) + *sb_bdof_flag = 1; + if (pu->dmvr_flag) { + VVCFrame* ref[2]; + if (pred_get_refs(lc, ref, mv) < 0) + return; + dmvr_mv_refine(lc, mv, orig_mv, sb_bdof_flag, ref[0]->frame, ref[1]->frame, x0, y0, sbw, sbh); + set_dmvr_info(fc, x0, y0, sbw, sbh, mv); + } +} + +static void pred_regular_blk(VVCLocalContext *lc, const int skip_ciip) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + PredictionUnit *pu = &lc->cu->pu; + const MotionInfo *mi = &pu->mi; + MvField mv, orig_mv; + int sbw, sbh, sb_bdof_flag = 0; + + if (cu->ciip_flag && skip_ciip) + return; + + sbw = cu->cb_width / mi->num_sb_x; + sbh = cu->cb_height / mi->num_sb_y; + + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + const int x0 = cu->x0 + sbx * sbw; + const int y0 = cu->y0 + sby * sbh; + + if (cu->ciip_flag) + ff_vvc_set_neighbour_available(lc, x0, y0, sbw, sbh); + + derive_sb_mv(lc, &mv, &orig_mv, &sb_bdof_flag, x0, y0, sbw, sbh); + pred_regular_luma(lc, mi->hpel_if_idx, mi->hpel_if_idx, &mv, x0, y0, sbw, sbh, &orig_mv, sb_bdof_flag); + if (fc->ps.sps->r->sps_chroma_format_idc) + pred_regular_chroma(lc, &mv, x0, y0, sbw, sbh, &orig_mv, pu->dmvr_flag); + } + } +} + +static void derive_affine_mvc(MvField *mvc, const VVCFrameContext *fc, const MvField *mv, + const int x0, const int y0, const int sbw, const int sbh) +{ + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const MvField* mv2 = ff_vvc_get_mvf(fc, x0 + hs * sbw, y0 + vs * sbh); + *mvc = *mv; + + // Due to different pred_flag, one of the motion vectors may have an invalid value. + // Cast them to an unsigned type to avoid undefined behavior. + mvc->mv[0].x += (unsigned int)mv2->mv[0].x; + mvc->mv[0].y += (unsigned int)mv2->mv[0].y; + mvc->mv[1].x += (unsigned int)mv2->mv[1].x; + mvc->mv[1].y += (unsigned int)mv2->mv[1].y; + ff_vvc_round_mv(mvc->mv + 0, 0, 1); + ff_vvc_round_mv(mvc->mv + 1, 0, 1); +} + +static void pred_affine_blk(VVCLocalContext *lc) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const PredictionUnit *pu = &cu->pu; + const MotionInfo *mi = &pu->mi; + const int x0 = cu->x0; + const int y0 = cu->y0; + const int sbw = cu->cb_width / mi->num_sb_x; + const int sbh = cu->cb_height / mi->num_sb_y; + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + const int x = x0 + sbx * sbw; + const int y = y0 + sby * sbh; + + uint8_t *dst0 = POS(0, x, y); + const MvField *mv = ff_vvc_get_mvf(fc, x, y); + VVCFrame *ref[2]; + + if (pred_get_refs(lc, ref, mv) < 0) + return; + + if (mi->pred_flag != PF_BI) { + const int lx = mi->pred_flag - PF_L0; + luma_prof_uni(lc, dst0, fc->frame->linesize[0], ref[lx]->frame, + mv, x, y, sbw, sbh, pu->cb_prof_flag[lx], + pu->diff_mv_x[lx], pu->diff_mv_y[lx]); + } else { + luma_prof_bi(lc, dst0, fc->frame->linesize[0], ref[0]->frame, ref[1]->frame, + mv, x, y, sbw, sbh); + } + if (fc->ps.sps->r->sps_chroma_format_idc) { + if (!av_mod_uintp2(sby, vs) && !av_mod_uintp2(sbx, hs)) { + MvField mvc; + derive_affine_mvc(&mvc, fc, mv, x, y, sbw, sbh); + pred_regular_chroma(lc, &mvc, x, y, sbw<fc; + const CodingUnit *cu = lc->cu; + const PredictionUnit *pu = &cu->pu; + + if (pu->merge_gpm_flag) + pred_gpm_blk(lc); + else if (pu->inter_affine_flag) + pred_affine_blk(lc); + else + pred_regular_blk(lc, 1); //intra block is not ready yet, skip ciip + + if (lc->sc->sh.r->sh_lmcs_used_flag && !cu->ciip_flag) { + uint8_t* dst0 = POS(0, cu->x0, cu->y0); + fc->vvcdsp.lmcs.filter(dst0, fc->frame->linesize[LUMA], cu->cb_width, cu->cb_height, &fc->ps.lmcs.fwd_lut); + } +} + +static int has_inter_luma(const CodingUnit *cu) +{ + return (cu->pred_mode == MODE_INTER || cu->pred_mode == MODE_SKIP) && cu->tree_type != DUAL_TREE_CHROMA; +} + +int ff_vvc_predict_inter(VVCLocalContext *lc, const int rs) +{ + const VVCFrameContext *fc = lc->fc; + const CTU *ctu = fc->tab.ctus + rs; + CodingUnit *cu = ctu->cus; + + while (cu) { + lc->cu = cu; + if (has_inter_luma(cu)) + predict_inter(lc); + cu = cu->next; + } + + return 0; +} + +void ff_vvc_predict_ciip(VVCLocalContext *lc) +{ + av_assert0(lc->cu->ciip_flag); + + //todo: refact out ciip from pred_regular_blk + pred_regular_blk(lc, 0); +} + +#undef POS diff --git a/libavcodec/vvc/vvc_inter.h b/libavcodec/vvc/vvc_inter.h new file mode 100644 index 00000000000..61fc44b3a01 --- /dev/null +++ b/libavcodec/vvc/vvc_inter.h @@ -0,0 +1,42 @@ +/* + * VVC inter prediction + * + * Copyright (C) 2023 Nuo Mi + * + * 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 + */ + +#ifndef AVCODEC_VVC_VVC_INTER_H +#define AVCODEC_VVC_VVC_INTER_H + +#include "vvc_ctu.h" + +/** + * Loop entire CTU to predict all inter coding blocks + * @param lc local context for CTU + * @param rs raster order for the CTU + * @return AVERROR + */ +int ff_vvc_predict_inter(VVCLocalContext *lc, int rs); + +/** + * CIIP(Combined Inter-Intra Prediction) for a coding block + * @param lc local context for CTU + */ +void ff_vvc_predict_ciip(VVCLocalContext *lc); + +#endif // AVCODEC_VVC_VVC_INTER_H diff --git a/libavcodec/vvc/vvc_inter_template.c b/libavcodec/vvc/vvc_inter_template.c new file mode 100644 index 00000000000..e5cff079fb1 --- /dev/null +++ b/libavcodec/vvc/vvc_inter_template.c @@ -0,0 +1,466 @@ +/* + * VVC inter prediction DSP + * + * Copyright (C) 2022 Nuo Mi + * + * 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 "libavcodec/h26x/h2656_inter_template.c" + +static void FUNC(avg)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int16_t *src0, const int16_t *src1, const int width, const int height) +{ + pixel *dst = (pixel*)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = FFMAX(3, 15 - BIT_DEPTH); + const int offset = 1 << (shift - 1); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((src0[x] + src1[x] + offset) >> shift); + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(w_avg)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int16_t *src0, const int16_t *src1, const int width, const int height, + const int denom, const int w0, const int w1, const int o0, const int o1) +{ + pixel *dst = (pixel*)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = denom + FFMAX(3, 15 - BIT_DEPTH); + const int offset = (((o0 + o1) << (BIT_DEPTH - 8)) + 1) << (shift - 1); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = av_clip_pixel((src0[x] * w0 + src1[x] * w1 + offset) >> shift); + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(put_ciip)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int width, const int height, + const uint8_t *_inter, const ptrdiff_t _inter_stride, const int intra_weight) +{ + pixel *dst = (pixel *)_dst; + pixel *inter = (pixel *)_inter; + const size_t dst_stride = _dst_stride / sizeof(pixel); + const size_t inter_stride = _inter_stride / sizeof(pixel); + const int inter_weight = 4 - intra_weight; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = (dst[x] * intra_weight + inter[x] * inter_weight + 2) >> 2; + dst += dst_stride; + inter += inter_stride; + } +} + +static void FUNC(put_gpm)(uint8_t *_dst, ptrdiff_t dst_stride, + const int width, const int height, + const int16_t *src0, const int16_t *src1, + const uint8_t *weights, const int step_x, const int step_y) +{ + const int shift = FFMAX(5, 17 - BIT_DEPTH); + const int offset = 1 << (shift - 1); + pixel *dst = (pixel *)_dst; + + dst_stride /= sizeof(pixel); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const uint8_t w = weights[x * step_x]; + dst[x] = av_clip_pixel((src0[x] * w + src1[x] * (8 - w) + offset) >> shift); + } + dst += dst_stride; + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + weights += step_y; + } +} + +//8.5.6.3.3 Luma integer sample fetching process, add one extra pad line +static void FUNC(bdof_fetch_samples)(int16_t *_dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int x_frac, const int y_frac, const int width, const int height) +{ + const int x_off = (x_frac >> 3) - 1; + const int y_off = (y_frac >> 3) - 1; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const pixel *src = (pixel*)_src + (x_off) + y_off * src_stride; + int16_t *dst = _dst - 1 - MAX_PB_SIZE; + const int shift = 14 - BIT_DEPTH; + const int bdof_width = width + 2 * BDOF_BORDER_EXT; + + // top + for (int i = 0; i < bdof_width; i++) + dst[i] = src[i] << shift; + + dst += MAX_PB_SIZE; + src += src_stride; + + for (int i = 0; i < height; i++) { + dst[0] = src[0] << shift; + dst[1 + width] = src[1 + width] << shift; + dst += MAX_PB_SIZE; + src += src_stride; + } + for (int i = 0; i < bdof_width; i++) + dst[i] = src[i] << shift; +} + +//8.5.6.3.3 Luma integer sample fetching process +static void FUNC(fetch_samples)(int16_t *_dst, const uint8_t *_src, const ptrdiff_t _src_stride, const int x_frac, const int y_frac) +{ + FUNC(bdof_fetch_samples)(_dst, _src, _src_stride, x_frac, y_frac, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE); +} + +static void FUNC(prof_grad_filter)(int16_t *_gradient_h, int16_t *_gradient_v, const ptrdiff_t gradient_stride, + const int16_t *_src, const ptrdiff_t src_stride, const int width, const int height, const int pad) +{ + const int shift = 6; + const int16_t *src = _src; + int16_t *gradient_h = _gradient_h + pad * (1 + gradient_stride); + int16_t *gradient_v = _gradient_v + pad * (1 + gradient_stride); + + for (int y = 0; y < height; y++) { + const int16_t *p = src; + for (int x = 0; x < width; x++) { + gradient_h[x] = (p[1] >> shift) - (p[-1] >> shift); + gradient_v[x] = (p[src_stride] >> shift) - (p[-src_stride] >> shift); + p++; + } + gradient_h += gradient_stride; + gradient_v += gradient_stride; + src += src_stride; + } + if (pad) { + pad_int16(_gradient_h + 1 + gradient_stride, gradient_stride, width, height); + pad_int16(_gradient_v + 1 + gradient_stride, gradient_stride, width, height); + } +} + +static void FUNC(apply_prof)(int16_t *dst, const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + + int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0); + + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + const int o = y * AFFINE_MIN_BLOCK_SIZE + x; + const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o]; + const int val = src[x] + av_clip(di, -limit, limit - 1); + dst[x] = val; + + } + src += MAX_PB_SIZE; + dst += MAX_PB_SIZE; + } +} + +static void FUNC(apply_prof_uni)(uint8_t *_dst, const ptrdiff_t _dst_stride, const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + pixel *dst = (pixel*)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = 14 - BIT_DEPTH; +#if BIT_DEPTH < 14 + const int offset = 1 << (shift - 1); +#else + const int offset = 0; +#endif + int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + + FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0); + + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + const int o = y * AFFINE_MIN_BLOCK_SIZE + x; + const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o]; + const int val = src[x] + av_clip(di, -limit, limit - 1); + dst[x] = av_clip_pixel((val + offset) >> shift); + + } + src += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(apply_prof_uni_w)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y, + const int denom, const int wx, const int _ox) +{ + const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit + pixel *dst = (pixel*)_dst; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + const int shift = denom + FFMAX(2, 14 - BIT_DEPTH); + const int offset = 1 << (shift - 1); + const int ox = _ox * (1 << (BIT_DEPTH - 8)); + int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; + + FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0); + + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + const int o = y * AFFINE_MIN_BLOCK_SIZE + x; + const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o]; + const int val = src[x] + av_clip(di, -limit, limit - 1); + dst[x] = av_clip_pixel(((val * wx + offset) >> shift) + ox); + } + src += MAX_PB_SIZE; + dst += dst_stride; + } +} + +static void FUNC(derive_bdof_vx_vy)(const int16_t *_src0, const int16_t *_src1, + const int16_t **gradient_h, const int16_t **gradient_v, ptrdiff_t gradient_stride, + int* vx, int* vy) +{ + const int shift2 = 4; + const int shift3 = 1; + const int thres = 1 << 4; + int sgx2 = 0, sgy2 = 0, sgxgy = 0, sgxdi = 0, sgydi = 0; + const int16_t *src0 = _src0 - 1 - MAX_PB_SIZE; + const int16_t *src1 = _src1 - 1 - MAX_PB_SIZE; + + for (int y = 0; y < BDOF_GRADIENT_SIZE; y++) { + for (int x = 0; x < BDOF_GRADIENT_SIZE; x++) { + const int diff = (src0[x] >> shift2) - (src1[x] >> shift2); + const int idx = gradient_stride * y + x; + const int temph = (gradient_h[0][idx] + gradient_h[1][idx]) >> shift3; + const int tempv = (gradient_v[0][idx] + gradient_v[1][idx]) >> shift3; + sgx2 += FFABS(temph); + sgy2 += FFABS(tempv); + sgxgy += VVC_SIGN(tempv) * temph; + sgxdi += -VVC_SIGN(temph) * diff; + sgydi += -VVC_SIGN(tempv) * diff; + } + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + } + *vx = sgx2 > 0 ? av_clip((sgxdi * (1 << 2)) >> av_log2(sgx2) , -thres + 1, thres - 1) : 0; + *vy = sgy2 > 0 ? av_clip(((sgydi * (1 << 2)) - ((*vx * sgxgy) >> 1)) >> av_log2(sgy2), -thres + 1, thres - 1) : 0; +} + +static void FUNC(apply_bdof_min_block)(pixel* dst, const ptrdiff_t dst_stride, const int16_t *src0, const int16_t *src1, + const int16_t **gradient_h, const int16_t **gradient_v, const int vx, const int vy) +{ + const int shift4 = 15 - BIT_DEPTH; + const int offset4 = 1 << (shift4 - 1); + + const int16_t* gh[] = { gradient_h[0] + 1 + BDOF_PADDED_SIZE, gradient_h[1] + 1 + BDOF_PADDED_SIZE }; + const int16_t* gv[] = { gradient_v[0] + 1 + BDOF_PADDED_SIZE, gradient_v[1] + 1 + BDOF_PADDED_SIZE }; + + for (int y = 0; y < BDOF_BLOCK_SIZE; y++) { + for (int x = 0; x < BDOF_BLOCK_SIZE; x++) { + const int idx = y * BDOF_PADDED_SIZE + x; + const int bdof_offset = vx * (gh[0][idx] - gh[1][idx]) + vy * (gv[0][idx] - gv[1][idx]); + dst[x] = av_clip_pixel((src0[x] + offset4 + src1[x] + bdof_offset) >> shift4); + } + dst += dst_stride; + src0 += MAX_PB_SIZE; + src1 += MAX_PB_SIZE; + } +} + +static void FUNC(apply_bdof)(uint8_t *_dst, const ptrdiff_t _dst_stride, int16_t *_src0, int16_t *_src1, + const int block_w, const int block_h) +{ + int16_t gradient_h[2][BDOF_PADDED_SIZE * BDOF_PADDED_SIZE]; + int16_t gradient_v[2][BDOF_PADDED_SIZE * BDOF_PADDED_SIZE]; + int vx, vy; + const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel); + pixel* dst = (pixel*)_dst; + + FUNC(prof_grad_filter)(gradient_h[0], gradient_v[0], BDOF_PADDED_SIZE, + _src0, MAX_PB_SIZE, block_w, block_h, 1); + pad_int16(_src0, MAX_PB_SIZE, block_w, block_h); + FUNC(prof_grad_filter)(gradient_h[1], gradient_v[1], BDOF_PADDED_SIZE, + _src1, MAX_PB_SIZE, block_w, block_h, 1); + pad_int16(_src1, MAX_PB_SIZE, block_w, block_h); + + for (int y = 0; y < block_h; y += BDOF_BLOCK_SIZE) { + for (int x = 0; x < block_w; x += BDOF_BLOCK_SIZE) { + const int16_t* src0 = _src0 + y * MAX_PB_SIZE + x; + const int16_t* src1 = _src1 + y * MAX_PB_SIZE + x; + pixel *d = dst + x; + const int idx = BDOF_PADDED_SIZE * y + x; + const int16_t* gh[] = { gradient_h[0] + idx, gradient_h[1] + idx }; + const int16_t* gv[] = { gradient_v[0] + idx, gradient_v[1] + idx }; + FUNC(derive_bdof_vx_vy)(src0, src1, gh, gv, BDOF_PADDED_SIZE, &vx, &vy); + FUNC(apply_bdof_min_block)(d, dst_stride, src0, src1, gh, gv, vx, vy); + } + dst += BDOF_BLOCK_SIZE * dst_stride; + } +} + +#define DMVR_FILTER(src, stride) \ + (filter[0] * src[x] + \ + filter[1] * src[x + stride]) + +//8.5.3.2.2 Luma sample bilinear interpolation process +static void FUNC(dmvr)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width) +{ + const pixel *src = (const pixel *)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); +#if BIT_DEPTH > 10 + const int shift4 = BIT_DEPTH - 10; + const int offset4 = 1 << (shift4 - 1); + #define DMVR_SHIFT(s) (((s) + offset4) >> shift4) +#else + #define DMVR_SHIFT(s) ((s) << (10 - BIT_DEPTH)) +#endif + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = DMVR_SHIFT(src[x]); + src += src_stride; + dst += MAX_PB_SIZE; + } +#undef DMVR_SHIFT +} + +//8.5.3.2.2 Luma sample bilinear interpolation process +static void FUNC(dmvr_h)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width) +{ + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_inter_luma_dmvr_filters[mx]; + const int shift1 = BIT_DEPTH - 6; + const int offset1 = 1 << (shift1 - 1); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = (DMVR_FILTER(src, 1) + offset1) >> shift1; + src += src_stride; + dst += MAX_PB_SIZE; + } +} + +//8.5.3.2.2 Luma sample bilinear interpolation process +static void FUNC(dmvr_v)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width) +{ + const pixel *src = (pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_inter_luma_dmvr_filters[my]; + const int shift1 = BIT_DEPTH - 6; + const int offset1 = 1 << (shift1 - 1); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = (DMVR_FILTER(src, src_stride) + offset1) >> shift1; + src += src_stride; + dst += MAX_PB_SIZE; + } + +} + +//8.5.3.2.2 Luma sample bilinear interpolation process +static void FUNC(dmvr_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride, + const int height, const intptr_t mx, const intptr_t my, const int width) +{ + int16_t tmp_array[(MAX_PB_SIZE + BILINEAR_EXTRA) * MAX_PB_SIZE]; + int16_t *tmp = tmp_array; + const pixel *src = (const pixel*)_src; + const ptrdiff_t src_stride = _src_stride / sizeof(pixel); + const int8_t *filter = ff_vvc_inter_luma_dmvr_filters[mx]; + const int shift1 = BIT_DEPTH - 6; + const int offset1 = 1 << (shift1 - 1); + const int shift2 = 4; + const int offset2 = 1 << (shift2 - 1); + + src -= BILINEAR_EXTRA_BEFORE * src_stride; + for (int y = 0; y < height + BILINEAR_EXTRA; y++) { + for (int x = 0; x < width; x++) + tmp[x] = (DMVR_FILTER(src, 1) + offset1) >> shift1; + src += src_stride; + tmp += MAX_PB_SIZE; + } + + tmp = tmp_array + BILINEAR_EXTRA_BEFORE * MAX_PB_SIZE; + filter = ff_vvc_inter_luma_dmvr_filters[my]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) + dst[x] = (DMVR_FILTER(tmp, MAX_PB_SIZE) + offset2) >> shift2; + tmp += MAX_PB_SIZE; + dst += MAX_PB_SIZE; + } +} + +#define PEL_FUNC(dst, C, idx1, idx2, a) \ + do { \ + for (int w = 0; w < 7; w++) \ + inter->dst[C][w][idx1][idx2] = FUNC(a); \ + } while (0) \ + +#define DIR_FUNCS(d, C, c) \ + PEL_FUNC(put_##d, C, 0, 0, put_##d##_pixels); \ + PEL_FUNC(put_##d, C, 0, 1, put_##d##_##c##_h); \ + PEL_FUNC(put_##d, C, 1, 0, put_##d##_##c##_v); \ + PEL_FUNC(put_##d, C, 1, 1, put_##d##_##c##_hv); \ + PEL_FUNC(put_##d##_w, C, 0, 0, put_##d##_w_pixels); \ + PEL_FUNC(put_##d##_w, C, 0, 1, put_##d##_##c##_w_h); \ + PEL_FUNC(put_##d##_w, C, 1, 0, put_##d##_##c##_w_v); \ + PEL_FUNC(put_##d##_w, C, 1, 1, put_##d##_##c##_w_hv); + +#define FUNCS(C, c) \ + PEL_FUNC(put, C, 0, 0, put_pixels); \ + PEL_FUNC(put, C, 0, 1, put_##c##_h); \ + PEL_FUNC(put, C, 1, 0, put_##c##_v); \ + PEL_FUNC(put, C, 1, 1, put_##c##_hv); \ + DIR_FUNCS(uni, C, c); \ + +static void FUNC(ff_vvc_inter_dsp_init)(VVCInterDSPContext *const inter) +{ + FUNCS(LUMA, luma); + FUNCS(CHROMA, chroma); + + inter->avg = FUNC(avg); + inter->w_avg = FUNC(w_avg); + + inter->dmvr[0][0] = FUNC(dmvr); + inter->dmvr[0][1] = FUNC(dmvr_h); + inter->dmvr[1][0] = FUNC(dmvr_v); + inter->dmvr[1][1] = FUNC(dmvr_hv); + + inter->put_ciip = FUNC(put_ciip); + inter->put_gpm = FUNC(put_gpm); + + inter->fetch_samples = FUNC(fetch_samples); + inter->bdof_fetch_samples = FUNC(bdof_fetch_samples); + inter->apply_prof = FUNC(apply_prof); + inter->apply_prof_uni = FUNC(apply_prof_uni); + inter->apply_prof_uni_w = FUNC(apply_prof_uni_w); + inter->apply_bdof = FUNC(apply_bdof); + inter->prof_grad_filter = FUNC(prof_grad_filter); + inter->sad = vvc_sad; +} + +#undef FUNCS +#undef PEL_FUNC +#undef DMVR_FUNCS diff --git a/libavcodec/vvc/vvc_intra.c b/libavcodec/vvc/vvc_intra.c new file mode 100644 index 00000000000..6d976320f8c --- /dev/null +++ b/libavcodec/vvc/vvc_intra.c @@ -0,0 +1,695 @@ +/* + * VVC intra prediction + * + * Copyright (C) 2021 Nuo Mi + * + * 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/frame.h" +#include "libavutil/imgutils.h" + +#include "vvc_data.h" +#include "vvc_inter.h" +#include "vvc_intra.h" +#include "vvc_itx_1d.h" + +static int is_cclm(enum IntraPredMode mode) +{ + return mode == INTRA_LT_CCLM || mode == INTRA_L_CCLM || mode == INTRA_T_CCLM; +} + +static int derive_ilfnst_pred_mode_intra(const VVCLocalContext *lc, const TransformBlock *tb) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const CodingUnit *cu = lc->cu; + const int x_tb = tb->x0 >> fc->ps.sps->min_cb_log2_size_y; + const int y_tb = tb->y0 >> fc->ps.sps->min_cb_log2_size_y; + const int x_c = (tb->x0 + (tb->tb_width << sps->hshift[1] >> 1) ) >> fc->ps.sps->min_cb_log2_size_y; + const int y_c = (tb->y0 + (tb->tb_height << sps->vshift[1] >> 1)) >> fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_tb, y_tb); + int pred_mode_intra = tb->c_idx == 0 ? cu->intra_pred_mode_y : cu->intra_pred_mode_c; + if (intra_mip_flag && !tb->c_idx) { + pred_mode_intra = INTRA_PLANAR; + } else if (is_cclm(pred_mode_intra)) { + int intra_mip_flag_c = SAMPLE_CTB(fc->tab.imf, x_c, y_c); + int cu_pred_mode = SAMPLE_CTB(fc->tab.cpm[0], x_c, y_c); + if (intra_mip_flag_c) { + pred_mode_intra = INTRA_PLANAR; + } else if (cu_pred_mode == MODE_IBC || cu_pred_mode == MODE_PLT) { + pred_mode_intra = INTRA_DC; + } else { + pred_mode_intra = SAMPLE_CTB(fc->tab.ipm, x_c, y_c); + } + } + pred_mode_intra = ff_vvc_wide_angle_mode_mapping(cu, tb->tb_width, tb->tb_height, tb->c_idx, pred_mode_intra); + + return pred_mode_intra; +} + +//8.7.4 Transformation process for scaled transform coefficients +static void ilfnst_transform(const VVCLocalContext *lc, TransformBlock *tb) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const CodingUnit *cu = lc->cu; + const int w = tb->tb_width; + const int h = tb->tb_height; + const int n_lfnst_out_size = (w >= 8 && h >= 8) ? 48 : 16; ///< nLfnstOutSize + const int log2_lfnst_size = (w >= 8 && h >= 8) ? 3 : 2; ///< log2LfnstSize + const int n_lfnst_size = 1 << log2_lfnst_size; ///< nLfnstSize + const int non_zero_size = ((w == 8 && h == 8) || (w == 4 && h == 4)) ? 8 : 16; ///< nonZeroSize + const int pred_mode_intra = derive_ilfnst_pred_mode_intra(lc, tb); + const int transpose = pred_mode_intra > 34; + int u[16], v[48]; + + for (int x = 0; x < non_zero_size; x++) { + int xc = ff_vvc_diag_scan_x[2][2][x]; + int yc = ff_vvc_diag_scan_y[2][2][x]; + u[x] = tb->coeffs[w * yc + xc]; + } + ff_vvc_inv_lfnst_1d(v, u, non_zero_size, n_lfnst_out_size, pred_mode_intra, + cu->lfnst_idx, sps->log2_transform_range); + if (transpose) { + int *dst = tb->coeffs; + const int *src = v; + if (n_lfnst_size == 4) { + for (int y = 0; y < 4; y++) { + dst[0] = src[0]; + dst[1] = src[4]; + dst[2] = src[8]; + dst[3] = src[12]; + src++; + dst += w; + } + } else { + for (int y = 0; y < 8; y++) { + dst[0] = src[0]; + dst[1] = src[8]; + dst[2] = src[16]; + dst[3] = src[24]; + if (y < 4) { + dst[4] = src[32]; + dst[5] = src[36]; + dst[6] = src[40]; + dst[7] = src[44]; + } + src++; + dst += w; + } + } + + } else { + int *dst = tb->coeffs; + const int *src = v; + for (int y = 0; y < n_lfnst_size; y++) { + int size = (y < 4) ? n_lfnst_size : 4; + memcpy(dst, src, size * sizeof(int)); + src += size; + dst += w; + } + } + tb->max_scan_x = n_lfnst_size - 1; + tb->max_scan_y = n_lfnst_size - 1; +} + +//part of 8.7.4 Transformation process for scaled transform coefficients +static void derive_transform_type(const VVCFrameContext *fc, const VVCLocalContext *lc, const TransformBlock *tb, enum TxType *trh, enum TxType *trv) +{ + const CodingUnit *cu = lc->cu; + static const enum TxType mts_to_trh[] = {DCT2, DST7, DCT8, DST7, DCT8}; + static const enum TxType mts_to_trv[] = {DCT2, DST7, DST7, DCT8, DCT8}; + const VVCSPS *sps = fc->ps.sps; + int implicit_mts_enabled = 0; + if (tb->c_idx || (cu->isp_split_type != ISP_NO_SPLIT && cu->lfnst_idx)) { + *trh = *trv = DCT2; + return; + } + + if (sps->r->sps_mts_enabled_flag) { + if (cu->isp_split_type != ISP_NO_SPLIT || + (cu->sbt_flag && FFMAX(tb->tb_width, tb->tb_height) <= 32) || + (!sps->r->sps_explicit_mts_intra_enabled_flag && cu->pred_mode == MODE_INTRA && + !cu->lfnst_idx && !cu->intra_mip_flag)) { + implicit_mts_enabled = 1; + } + } + if (implicit_mts_enabled) { + const int w = tb->tb_width; + const int h = tb->tb_height; + if (cu->sbt_flag) { + *trh = (cu->sbt_horizontal_flag || cu->sbt_pos_flag) ? DST7 : DCT8; + *trv = (!cu->sbt_horizontal_flag || cu->sbt_pos_flag) ? DST7 : DCT8; + } else { + *trh = (w >= 4 && w <= 16) ? DST7 : DCT2; + *trv = (h >= 4 && h <= 16) ? DST7 : DCT2; + } + return; + } + *trh = mts_to_trh[cu->mts_idx]; + *trv = mts_to_trv[cu->mts_idx]; +} + +static void add_residual_for_joint_coding_chroma(VVCLocalContext *lc, + const TransformUnit *tu, TransformBlock *tb, const int chroma_scale) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const int c_sign = 1 - 2 * fc->ps.ph.r->ph_joint_cbcr_sign_flag; + const int shift = tu->coded_flag[1] ^ tu->coded_flag[2]; + const int c_idx = 1 + tu->coded_flag[1]; + const ptrdiff_t stride = fc->frame->linesize[c_idx]; + const int hs = fc->ps.sps->hshift[c_idx]; + const int vs = fc->ps.sps->vshift[c_idx]; + uint8_t *dst = &fc->frame->data[c_idx][(tb->y0 >> vs) * stride + + ((tb->x0 >> hs) << fc->ps.sps->pixel_shift)]; + if (chroma_scale) { + fc->vvcdsp.itx.pred_residual_joint(tb->coeffs, tb->tb_width, tb->tb_height, c_sign, shift); + fc->vvcdsp.intra.lmcs_scale_chroma(lc, tb->coeffs, tb->coeffs, tb->tb_width, tb->tb_height, cu->x0, cu->y0); + fc->vvcdsp.itx.add_residual(dst, tb->coeffs, tb->tb_width, tb->tb_height, stride); + } else { + fc->vvcdsp.itx.add_residual_joint(dst, tb->coeffs, tb->tb_width, tb->tb_height, stride, c_sign, shift); + } +} + +static int add_reconstructed_area(VVCLocalContext *lc, const int ch_type, const int x0, const int y0, const int w, const int h) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const int hs = sps->hshift[ch_type]; + const int vs = sps->vshift[ch_type]; + ReconstructedArea *a; + + if (lc->num_ras[ch_type] >= FF_ARRAY_ELEMS(lc->ras[ch_type])) + return AVERROR_INVALIDDATA; + + a = &lc->ras[ch_type][lc->num_ras[ch_type]]; + a->x = x0 >> hs; + a->y = y0 >> vs; + a->w = w >> hs; + a->h = h >> vs; + lc->num_ras[ch_type]++; + + return 0; +} + +static void add_tu_area(const TransformUnit *tu, int *x0, int *y0, int *w, int *h) +{ + *x0 = tu->x0; + *y0 = tu->y0; + *w = tu->width; + *h = tu->height; +} + +#define MIN_ISP_PRED_WIDTH 4 +static int get_luma_predict_unit(const CodingUnit *cu, const TransformUnit *tu, const int idx, int *x0, int *y0, int *w, int *h) +{ + int has_luma = 1; + add_tu_area(tu, x0, y0, w, h); + if (cu->isp_split_type == ISP_VER_SPLIT && tu->width < MIN_ISP_PRED_WIDTH) { + *w = MIN_ISP_PRED_WIDTH; + has_luma = !(idx % (MIN_ISP_PRED_WIDTH / tu->width)); + } + return has_luma; +} + +static int get_chroma_predict_unit(const CodingUnit *cu, const TransformUnit *tu, const int idx, int *x0, int *y0, int *w, int *h) +{ + if (cu->isp_split_type == ISP_NO_SPLIT) { + add_tu_area(tu, x0, y0, w, h); + return 1; + } + if (idx == cu->num_intra_subpartitions - 1) { + *x0 = cu->x0; + *y0 = cu->y0; + *w = cu->cb_width; + *h = cu->cb_height; + return 1; + } + return 0; +} + +//8.4.5.1 General decoding process for intra blocks +static void predict_intra(VVCLocalContext *lc, const TransformUnit *tu, const int idx, const int target_ch_type) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const VVCTreeType tree_type = cu->tree_type; + int x0, y0, w, h; + if (cu->pred_mode != MODE_INTRA) { + add_reconstructed_area(lc, target_ch_type, tu->x0, tu->y0, tu->width, tu->height); + return; + } + if (!target_ch_type && tree_type != DUAL_TREE_CHROMA) { + if (get_luma_predict_unit(cu, tu, idx, &x0, &y0, &w, &h)) { + ff_vvc_set_neighbour_available(lc, x0, y0, w, h); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, w, h, 0); + add_reconstructed_area(lc, 0, x0, y0, w, h); + } + } + if (target_ch_type && tree_type != DUAL_TREE_LUMA) { + if (get_chroma_predict_unit(cu, tu, idx, &x0, &y0, &w, &h)){ + ff_vvc_set_neighbour_available(lc, x0, y0, w, h); + if (is_cclm(cu->intra_pred_mode_c)) { + fc->vvcdsp.intra.intra_cclm_pred(lc, x0, y0, w, h); + } else { + fc->vvcdsp.intra.intra_pred(lc, x0, y0, w, h, 1); + fc->vvcdsp.intra.intra_pred(lc, x0, y0, w, h, 2); + } + add_reconstructed_area(lc, 1, x0, y0, w, h); + } + } +} + +static void scale_clip(int *coeff, const int nzw, const int w, const int h, + const int shift, const int log2_transform_range) +{ + const int add = 1 << (shift - 1); + for (int y = 0; y < h; y++) { + int *p = coeff + y * w; + for (int x = 0; x < nzw; x++) { + *p = av_clip_intp2((*p + add) >> shift, log2_transform_range); + p++; + } + memset(p, 0, sizeof(*p) * (w - nzw)); + } +} + +static void scale(int *out, const int *in, const int w, const int h, const int shift) +{ + const int add = 1 << (shift - 1); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int *o = out + y * w + x; + const int *i = in + y * w + x; + *o = (*i + add) >> shift; + } + } +} + +// part of 8.7.3 Scaling process for transform coefficients +static void derive_qp(const VVCLocalContext *lc, const TransformUnit *tu, TransformBlock *tb) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const CodingUnit *cu = lc->cu; + int qp, qp_act_offset; + + if (tb->c_idx == 0) { + //fix me + qp = cu->qp[LUMA] + sps->qp_bd_offset; + qp_act_offset = cu->act_enabled_flag ? -5 : 0; + } else { + const int is_jcbcr = tu->joint_cbcr_residual_flag && tu->coded_flag[CB] && tu->coded_flag[CR]; + const int idx = is_jcbcr ? JCBCR : tb->c_idx; + qp = cu->qp[idx]; + qp_act_offset = cu->act_enabled_flag ? 1 : 0; + } + if (tb->ts) { + const int qp_prime_ts_min = 4 + 6 * sps->r->sps_min_qp_prime_ts; + + tb->qp = av_clip(qp + qp_act_offset, qp_prime_ts_min, 63 + sps->qp_bd_offset); + tb->rect_non_ts_flag = 0; + tb->bd_shift = 10; + } else { + const int log_sum = tb->log2_tb_width + tb->log2_tb_height; + const int rect_non_ts_flag = log_sum & 1; + + tb->qp = av_clip(qp + qp_act_offset, 0, 63 + sps->qp_bd_offset); + tb->rect_non_ts_flag = rect_non_ts_flag; + tb->bd_shift = sps->bit_depth + rect_non_ts_flag + (log_sum / 2) + + 10 - sps->log2_transform_range + rsh->sh_dep_quant_used_flag; + } + tb->bd_offset = (1 << tb->bd_shift) >> 1; +} + +//8.7.3 Scaling process for transform coefficients +static av_always_inline int derive_scale(const TransformBlock *tb, const int sh_dep_quant_used_flag) +{ + static const uint8_t rem6[63 + 2 * 6 + 1] = { + 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, + 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, + 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, + 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3 + }; + + static const uint8_t div6[63 + 2 * 6 + 1] = { + 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, + 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12 + }; + + const static int level_scale[2][6] = { + { 40, 45, 51, 57, 64, 72 }, + { 57, 64, 72, 80, 90, 102 } + }; + const int addin = sh_dep_quant_used_flag && !tb->ts; + const int qp = tb->qp + addin; + + return level_scale[tb->rect_non_ts_flag][rem6[qp]] << div6[qp]; +} + +//8.7.3 Scaling process for transform coefficients +static const uint8_t* derive_scale_m(const VVCLocalContext *lc, const TransformBlock *tb, uint8_t *scale_m) +{ + //Table 38 – Specification of the scaling matrix identifier variable id according to predMode, cIdx, nTbW, and nTbH + const int ids[2][3][6] = { + { + { 0, 2, 8, 14, 20, 26 }, + { 0, 3, 9, 15, 21, 21 }, + { 0, 4, 10, 16, 22, 22 } + }, + { + { 0, 5, 11, 17, 23, 27 }, + { 0, 6, 12, 18, 24, 24 }, + { 1, 7, 13, 19, 25, 25 }, + } + }; + const VVCFrameParamSets *ps = &lc->fc->ps; + const VVCSPS *sps = ps->sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const CodingUnit *cu = lc->cu; + const VVCScalingList *sl = ps->sl; + const int id = ids[cu->pred_mode != MODE_INTRA][tb->c_idx][FFMAX(tb->log2_tb_height, tb->log2_tb_width) - 1]; + const int log2_matrix_size = (id < 2) ? 1 : (id < 8) ? 2 : 3; + uint8_t *p = scale_m; + + av_assert0(!sps->r->sps_scaling_matrix_for_alternative_colour_space_disabled_flag); + + if (!rsh->sh_explicit_scaling_list_used_flag || tb->ts || + sps->r->sps_scaling_matrix_for_lfnst_disabled_flag && cu->apply_lfnst_flag[tb->c_idx]) + return ff_vvc_default_scale_m; + + if (!sl) { + av_log(lc->fc->log_ctx, AV_LOG_WARNING, "bug: no scaling list aps, id = %d", ps->ph.r->ph_scaling_list_aps_id); + return ff_vvc_default_scale_m; + } + + for (int y = tb->min_scan_y; y <= tb->max_scan_y; y++) { + const int off = y << log2_matrix_size >> tb->log2_tb_height << log2_matrix_size; + const uint8_t *m = &sl->scaling_matrix_rec[id][off]; + + for (int x = tb->min_scan_x; x <= tb->max_scan_x; x++) + *p++ = m[x << log2_matrix_size >> tb->log2_tb_width]; + } + if (id >= SL_START_16x16 && !tb->min_scan_x && !tb->min_scan_y) + *scale_m = sl->scaling_matrix_dc_rec[id - SL_START_16x16]; + + return scale_m; +} + +//8.7.3 Scaling process for transform coefficients +static av_always_inline int scale_coeff(const TransformBlock *tb, int coeff, + const int scale, const int scale_m, const int log2_transform_range) +{ + coeff = (coeff * scale * scale_m + tb->bd_offset) >> tb->bd_shift; + coeff = av_clip_intp2(coeff, log2_transform_range); + return coeff; +} + +static void dequant(const VVCLocalContext *lc, const TransformUnit *tu, TransformBlock *tb) +{ + uint8_t tmp[MAX_TB_SIZE * MAX_TB_SIZE]; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const VVCSPS *sps = lc->fc->ps.sps; + const uint8_t *scale_m = derive_scale_m(lc, tb, tmp); + int scale; + + derive_qp(lc, tu, tb); + scale = derive_scale(tb, rsh->sh_dep_quant_used_flag); + + for (int y = tb->min_scan_y; y <= tb->max_scan_y; y++) { + for (int x = tb->min_scan_x; x <= tb->max_scan_x; x++) { + int *coeff = tb->coeffs + y * tb->tb_width + x; + + if (*coeff) + *coeff = scale_coeff(tb, *coeff, scale, *scale_m, sps->log2_transform_range); + scale_m++; + } + } +} + +//transmatrix[0][0] +#define DCT_A 64 +static void itx_2d(const VVCFrameContext *fc, TransformBlock *tb, const enum TxType trh, const enum TxType trv) +{ + const VVCSPS *sps = fc->ps.sps; + const int w = tb->tb_width; + const int h = tb->tb_height; + const size_t nzw = tb->max_scan_x + 1; + const size_t nzh = tb->max_scan_y + 1; + const int shift[] = { 7, 5 + sps->log2_transform_range - sps->bit_depth }; + + if (w == h && nzw == 1 && nzh == 1 && trh == DCT2 && trv == DCT2) { + const int add[] = { 1 << (shift[0] - 1), 1 << (shift[1] - 1) }; + const int t = (tb->coeffs[0] * DCT_A + add[0]) >> shift[0]; + const int dc = (t * DCT_A + add[1]) >> shift[1]; + + for (int i = 0; i < w * h; i++) + tb->coeffs[i] = dc; + + return; + } + + for (int x = 0; x < nzw; x++) + fc->vvcdsp.itx.itx[trv][tb->log2_tb_height - 1](tb->coeffs + x, w, nzh); + scale_clip(tb->coeffs, nzw, w, h, shift[0], sps->log2_transform_range); + + for (int y = 0; y < h; y++) + fc->vvcdsp.itx.itx[trh][tb->log2_tb_width - 1](tb->coeffs + y * w, 1, nzw); + scale(tb->coeffs, tb->coeffs, w, h, shift[1]); +} + +static void itx_1d(const VVCFrameContext *fc, TransformBlock *tb, const enum TxType trh, const enum TxType trv) +{ + const VVCSPS *sps = fc->ps.sps; + const int w = tb->tb_width; + const int h = tb->tb_height; + const size_t nzw = tb->max_scan_x + 1; + const size_t nzh = tb->max_scan_y + 1; + + if ((w > 1 && nzw == 1 && trh == DCT2) || (h > 1 && nzh == 1 && trv == DCT2)) { + const int shift = 6 + sps->log2_transform_range - sps->bit_depth; + const int add = 1 << (shift - 1); + const int dc = (tb->coeffs[0] * DCT_A + add) >> shift; + + for (int i = 0; i < w * h; i++) + tb->coeffs[i] = dc; + + return; + } + + if (w > 1) + fc->vvcdsp.itx.itx[trh][tb->log2_tb_width - 1](tb->coeffs, 1, nzw); + else + fc->vvcdsp.itx.itx[trv][tb->log2_tb_height - 1](tb->coeffs, 1, nzh); + scale(tb->coeffs, tb->coeffs, w, h, 6 + sps->log2_transform_range - sps->bit_depth); +} + +static void transform_bdpcm(TransformBlock *tb, const VVCLocalContext *lc, const CodingUnit *cu) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const IntraPredMode mode = tb->c_idx ? cu->intra_pred_mode_c : cu->intra_pred_mode_y; + const int vertical = mode == INTRA_VERT; + lc->fc->vvcdsp.itx.transform_bdpcm(tb->coeffs, tb->tb_width, tb->tb_height, + vertical, sps->log2_transform_range); + if (vertical) + tb->max_scan_y = tb->tb_height - 1; + else + tb->max_scan_x = tb->tb_width - 1; +} + +static void itransform(VVCLocalContext *lc, TransformUnit *tu, const int tu_idx, const int target_ch_type) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCSH *sh = &lc->sc->sh; + const CodingUnit *cu = lc->cu; + const int ps = fc->ps.sps->pixel_shift; + DECLARE_ALIGNED(32, int, temp)[MAX_TB_SIZE * MAX_TB_SIZE]; + + for (int i = 0; i < tu->nb_tbs; i++) { + TransformBlock *tb = &tu->tbs[i]; + const int c_idx = tb->c_idx; + const int ch_type = c_idx > 0; + + if (ch_type == target_ch_type && tb->has_coeffs) { + const int w = tb->tb_width; + const int h = tb->tb_height; + const int chroma_scale = ch_type && sh->r->sh_lmcs_used_flag && fc->ps.ph.r->ph_chroma_residual_scale_flag && (w * h > 4); + const ptrdiff_t stride = fc->frame->linesize[c_idx]; + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + uint8_t *dst = &fc->frame->data[c_idx][(tb->y0 >> vs) * stride + ((tb->x0 >> hs) << ps)]; + + if (cu->bdpcm_flag[tb->c_idx]) + transform_bdpcm(tb, lc, cu); + dequant(lc, tu, tb); + if (!tb->ts) { + enum TxType trh, trv; + + if (cu->apply_lfnst_flag[c_idx]) + ilfnst_transform(lc, tb); + derive_transform_type(fc, lc, tb, &trh, &trv); + if (w > 1 && h > 1) + itx_2d(fc, tb, trh, trv); + else + itx_1d(fc, tb, trh, trv); + } + + if (chroma_scale) + fc->vvcdsp.intra.lmcs_scale_chroma(lc, temp, tb->coeffs, w, h, cu->x0, cu->y0); + // TODO: Address performance issue here by combining transform, lmcs_scale_chroma, and add_residual into one function. + // Complete this task before implementing ASM code. + fc->vvcdsp.itx.add_residual(dst, chroma_scale ? temp : tb->coeffs, w, h, stride); + + if (tu->joint_cbcr_residual_flag && tb->c_idx) + add_residual_for_joint_coding_chroma(lc, tu, tb, chroma_scale); + } + } +} + +static int reconstruct(VVCLocalContext *lc) +{ + VVCFrameContext *fc = lc->fc; + CodingUnit *cu = lc->cu; + const int start = cu->tree_type == DUAL_TREE_CHROMA; + const int end = fc->ps.sps->r->sps_chroma_format_idc && (cu->tree_type != DUAL_TREE_LUMA); + + for (int ch_type = start; ch_type <= end; ch_type++) { + TransformUnit *tu = cu->tus.head; + for (int i = 0; tu; i++) { + predict_intra(lc, tu, i, ch_type); + itransform(lc, tu, i, ch_type); + tu = tu->next; + } + } + return 0; +} + +#define POS(c_idx, x, y) \ + &fc->frame->data[c_idx][((y) >> fc->ps.sps->vshift[c_idx]) * fc->frame->linesize[c_idx] + \ + (((x) >> fc->ps.sps->hshift[c_idx]) << fc->ps.sps->pixel_shift)] + +#define IBC_POS(c_idx, x, y) \ + (fc->tab.ibc_vir_buf[c_idx] + \ + (x << ps) + (y + ((cu->y0 & ~(sps->ctb_size_y - 1)) >> vs)) * ibc_stride) +#define IBC_X(x) ((x) & ((fc->tab.sz.ibc_buffer_width >> hs) - 1)) +#define IBC_Y(y) ((y) & ((1 << sps->ctb_log2_size_y >> vs) - 1)) + +static void intra_block_copy(const VVCLocalContext *lc, const int c_idx) +{ + const CodingUnit *cu = lc->cu; + const PredictionUnit *pu = &cu->pu; + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const Mv *bv = &pu->mi.mv[L0][0]; + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int ps = sps->pixel_shift; + const int ref_x = IBC_X((cu->x0 >> hs) + (bv->x >> (4 + hs))); + const int ref_y = IBC_Y((cu->y0 >> vs) + (bv->y >> (4 + vs))); + const int w = cu->cb_width >> hs; + const int h = cu->cb_height >> vs; + const int ibc_buf_width = fc->tab.sz.ibc_buffer_width >> hs; ///< IbcBufWidthY and IbcBufWidthC + const int rw = FFMIN(w, ibc_buf_width - ref_x); + const int ibc_stride = ibc_buf_width << ps; + const int dst_stride = fc->frame->linesize[c_idx]; + const uint8_t *ibc_buf = IBC_POS(c_idx, ref_x, ref_y); + uint8_t *dst = POS(c_idx, cu->x0, cu->y0); + + av_image_copy_plane(dst, dst_stride, ibc_buf, ibc_stride, rw << ps, h); + + if (w > rw) { + //wrap around, left part + ibc_buf = IBC_POS(c_idx, 0, ref_y); + dst += rw << ps; + av_image_copy_plane(dst, dst_stride, ibc_buf, ibc_stride, (w - rw) << ps, h); + } +} + +static void vvc_predict_ibc(const VVCLocalContext *lc) +{ + const H266RawSPS *rsps = lc->fc->ps.sps->r; + + intra_block_copy(lc, LUMA); + if (lc->cu->tree_type == SINGLE_TREE && rsps->sps_chroma_format_idc) { + intra_block_copy(lc, CB); + intra_block_copy(lc, CR); + } +} + +static void ibc_fill_vir_buf(const VVCLocalContext *lc, const CodingUnit *cu) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int has_chroma = sps->r->sps_chroma_format_idc && cu->tree_type != DUAL_TREE_LUMA; + const int start = cu->tree_type == DUAL_TREE_CHROMA; + const int end = has_chroma ? CR : LUMA; + + for (int c_idx = start; c_idx <= end; c_idx++) { + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int ps = sps->pixel_shift; + const int x = IBC_X(cu->x0 >> hs); + const int y = IBC_Y(cu->y0 >> vs); + const int src_stride = fc->frame->linesize[c_idx]; + const int ibc_stride = fc->tab.sz.ibc_buffer_width >> hs << ps; + const uint8_t *src = POS(c_idx, cu->x0, cu->y0); + uint8_t *ibc_buf = IBC_POS(c_idx, x, y); + + av_image_copy_plane(ibc_buf, ibc_stride, src, src_stride, cu->cb_width >> hs << ps , cu->cb_height >> vs); + } +} + +int ff_vvc_reconstruct(VVCLocalContext *lc, const int rs, const int rx, const int ry) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int x_ctb = rx << sps->ctb_log2_size_y; + const int y_ctb = ry << sps->ctb_log2_size_y; + CTU *ctu = fc->tab.ctus + rs; + CodingUnit *cu = ctu->cus; + int ret = 0; + + lc->num_ras[0] = lc->num_ras[1] = 0; + lc->lmcs.x_vpdu = -1; + lc->lmcs.y_vpdu = -1; + ff_vvc_decode_neighbour(lc, x_ctb, y_ctb, rx, ry, rs); + while (cu) { + lc->cu = cu; + + if (cu->ciip_flag) + ff_vvc_predict_ciip(lc); + else if (cu->pred_mode == MODE_IBC) + vvc_predict_ibc(lc); + if (cu->coded_flag) { + ret = reconstruct(lc); + } else { + if (cu->tree_type != DUAL_TREE_CHROMA) + add_reconstructed_area(lc, LUMA, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (sps->r->sps_chroma_format_idc && cu->tree_type != DUAL_TREE_LUMA) + add_reconstructed_area(lc, CHROMA, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + } + if (sps->r->sps_ibc_enabled_flag) + ibc_fill_vir_buf(lc, cu); + cu = cu->next; + } + ff_vvc_ctu_free_cus(ctu); + return ret; +} + diff --git a/libavcodec/vvc/vvc_intra.h b/libavcodec/vvc/vvc_intra.h new file mode 100644 index 00000000000..6b674008f9c --- /dev/null +++ b/libavcodec/vvc/vvc_intra.h @@ -0,0 +1,49 @@ +/* + * VVC intra prediction + * + * Copyright (C) 2021 Nuo Mi + * + * 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 + */ +#ifndef AVCODEC_VVC_VVC_INTRA_H +#define AVCODEC_VVC_VVC_INTRA_H + +#include "vvc_ctu.h" + +/** + * reconstruct a CTU + * @param lc local context for CTU + * @param rs raster order for the CTU. + * @param rx raster order x for the CTU. + * @param ry raster order y for the CTU. + * @return AVERROR + */ +int ff_vvc_reconstruct(VVCLocalContext *lc, const int rs, const int rx, const int ry); + +//utils for vvc_intra_template +int ff_vvc_get_top_available(const VVCLocalContext *lc, int x0, int y0, int target_size, int c_idx); +int ff_vvc_get_left_available(const VVCLocalContext *lc, int x0, int y0, int target_size, int c_idx); +int ff_vvc_get_mip_size_id(int w, int h); +int ff_vvc_need_pdpc(int w, int h, uint8_t bdpcm_flag, int mode, int ref_idx); +int ff_vvc_nscale_derive(int w, int h, int mode); +int ff_vvc_ref_filter_flag_derive(int mode); +int ff_vvc_intra_pred_angle_derive(int pred_mode); +int ff_vvc_intra_inv_angle_derive(int pred_mode); +int ff_vvc_wide_angle_mode_mapping(const CodingUnit *cu, + int tb_width, int tb_height, int c_idx, int pred_mode_intra); + +#endif // AVCODEC_VVC_VVC_INTRA_H diff --git a/libavcodec/vvc/vvc_intra_template.c b/libavcodec/vvc/vvc_intra_template.c new file mode 100644 index 00000000000..a0788852660 --- /dev/null +++ b/libavcodec/vvc/vvc_intra_template.c @@ -0,0 +1,1015 @@ +/* + * VVC intra prediction DSP + * + * Copyright (C) 2021-2023 Nuomi + * + * 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 "libavcodec/bit_depth_template.c" + +#include "vvc_intra.h" + +#define POS(x, y) src[(x) + stride * (y)] + +static av_always_inline void FUNC(cclm_linear_pred)(VVCFrameContext *fc, const int x0, const int y0, + const int w, const int h, const pixel* pdsy, const int *a, const int *b, const int *k) +{ + const VVCSPS *sps = fc->ps.sps; + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS - 1; i++) { + const int c_idx = i + 1; + const int x = x0 >> sps->hshift[c_idx]; + const int y = y0 >> sps->vshift[c_idx]; + const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel); + pixel *src = (pixel*)fc->frame->data[c_idx] + x + y * stride; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + const int dsy = pdsy[y * w + x]; + const int pred = ((dsy * a[i]) >> k[i]) + b[i]; + POS(x, y) = CLIP(pred); + } + } + } +} + +#define MAX_PICK_POS 4 +#define TOP 0 +#define LEFT 1 + +static av_always_inline void FUNC(cclm_get_params_default)(int *a, int *b, int *k) +{ + for (int i = 0; i < 2; i++) { + a[i] = k[i] = 0; + b[i] = 1 << (BIT_DEPTH - 1); + } +} + +static av_always_inline int FUNC(cclm_get_select_pos)(const VVCLocalContext *lc, + const int x, const int y, const int w, const int h, const int avail_t, const int avail_l, + int cnt[2], int pos[2][MAX_PICK_POS]) +{ + const enum IntraPredMode mode = lc->cu->intra_pred_mode_c; + const int num_is4 = !avail_t || !avail_l || mode != INTRA_LT_CCLM; + int num_samp[2]; + + if (mode == INTRA_LT_CCLM) { + num_samp[TOP] = avail_t ? w : 0; + num_samp[LEFT] = avail_l ? h : 0; + } else { + num_samp[TOP] = (avail_t && mode == INTRA_T_CCLM) ? ff_vvc_get_top_available(lc, x, y, w + FFMIN(w, h), 1) : 0; + num_samp[LEFT] = (avail_l && mode == INTRA_L_CCLM) ? ff_vvc_get_left_available(lc, x, y, h + FFMIN(w, h), 1) : 0; + } + if (!num_samp[TOP] && !num_samp[LEFT]) { + return 0; + } + for (int i = TOP; i <= LEFT; i++) { + const int start = num_samp[i] >> (2 + num_is4); + const int step = FFMAX(1, num_samp[i] >> (1 + num_is4)) ; + cnt[i] = FFMIN(num_samp[i], (1 + num_is4) << 1); + for (int c = 0; c < cnt[i]; c++) + pos[i][c] = start + c * step; + } + return 1; +} + +static av_always_inline void FUNC(cclm_select_luma_444)(const pixel *src, const int step, + const int cnt, const int pos[MAX_PICK_POS], pixel *sel_luma) +{ + for (int i = 0; i < cnt; i++) + sel_luma[i] = src[pos[i] * step]; +} + +static av_always_inline void FUNC(cclm_select_luma)(const VVCFrameContext *fc, + const int x0, const int y0, const int avail_t, const int avail_l, const int cnt[2], const int pos[2][MAX_PICK_POS], + pixel *sel_luma) +{ + const VVCSPS *sps = fc->ps.sps; + + const int b_ctu_boundary = !av_mod_uintp2(y0, sps->ctb_log2_size_y); + const int hs = sps->hshift[1]; + const int vs = sps->vshift[1]; + const ptrdiff_t stride = fc->frame->linesize[0] / sizeof(pixel); + + if (!hs && !vs) { + const pixel* src = (pixel*)fc->frame->data[0] + x0 + y0 * stride; + FUNC(cclm_select_luma_444)(src - avail_t * stride, 1, cnt[TOP], pos[TOP], sel_luma); + FUNC(cclm_select_luma_444)(src - avail_l, stride, cnt[LEFT], pos[LEFT], sel_luma + cnt[TOP]); + } else { + // top + if (vs && !b_ctu_boundary) { + const pixel *source = (pixel *)fc->frame->data[0] + x0 + (y0 - 2) * stride; + for (int i = 0; i < cnt[TOP]; i++) { + const int x = pos[TOP][i] << hs; + const pixel *src = source + x; + const int has_left = x || avail_l; + const pixel l = has_left ? POS(-1, 0) : POS(0, 0); + if (sps->r->sps_chroma_vertical_collocated_flag) { + sel_luma[i] = (POS(0, -1) + l + 4 * POS(0, 0) + POS(1, 0) + POS(0, 1) + 4) >> 3; + } else { + const pixel l1 = has_left ? POS(-1, 1) : POS(0, 1); + sel_luma[i] = (l + l1 + 2 * (POS(0, 0) + POS(0, 1)) + POS(1, 0) + POS(1, 1) + 4) >> 3; + } + } + } else { + const pixel *source = (pixel*)fc->frame->data[0] + x0 + (y0 - 1) * stride; + for (int i = 0; i < cnt[TOP]; i++) { + const int x = pos[TOP][i] << hs; + const pixel *src = source + x; + const int has_left = x || avail_l; + const pixel l = has_left ? POS(-1, 0) : POS(0, 0); + sel_luma[i] = (l + 2 * POS(0, 0) + POS(1, 0) + 2) >> 2; + } + } + + // left + { + const pixel *left; + const pixel *source = (pixel *)fc->frame->data[0] + x0 + y0 * stride - (1 + hs) * avail_l; + left = source - avail_l; + + for (int i = 0; i < cnt[LEFT]; i++) { + const int y = pos[LEFT][i] << vs; + const int offset = y * stride; + const pixel *l = left + offset; + const pixel *src = source + offset; + pixel pred; + if (!vs) { + pred = (*l + 2 * POS(0, 0) + POS(1, 0) + 2) >> 2; + } else { + if (sps->r->sps_chroma_vertical_collocated_flag) { + const int has_top = y || avail_t; + const pixel t = has_top ? POS(0, -1) : POS(0, 0); + pred = (*l + t + 4 * POS(0, 0) + POS(1, 0) + POS(0, 1) + 4) >> 3; + } else { + pred = (*l + *(l + stride) + 2 * POS(0, 0) + 2 * POS(0, 1) + POS(1, 0) + POS(1, 1) + 4) >> 3; + } + } + sel_luma[i + cnt[TOP]] = pred; + } + } + } +} + +static av_always_inline void FUNC(cclm_select_chroma)(const VVCFrameContext *fc, + const int x, const int y, const int cnt[2], const int pos[2][MAX_PICK_POS], + pixel sel[][MAX_PICK_POS * 2]) +{ + for (int c_idx = 1; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) { + const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel); + + //top + const pixel *src = (pixel*)fc->frame->data[c_idx] + x + (y - 1)* stride; + for (int i = 0; i < cnt[TOP]; i++) { + sel[c_idx][i] = src[pos[TOP][i]]; + } + + //left + src = (pixel*)fc->frame->data[c_idx] + x - 1 + y * stride; + for (int i = 0; i < cnt[LEFT]; i++) { + sel[c_idx][i + cnt[TOP]] = src[pos[LEFT][i] * stride]; + } + } +} + +static av_always_inline int FUNC(cclm_select_samples)(const VVCLocalContext *lc, + const int x0, const int y0, const int w, const int h, const int avail_t, const int avail_l, + pixel sel[][MAX_PICK_POS * 2]) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int x = x0 >> sps->hshift[1]; + const int y = y0 >> sps->vshift[1]; + int cnt[2], pos[2][MAX_PICK_POS]; + + if (!FUNC(cclm_get_select_pos)(lc, x, y, w, h, avail_t, avail_l, cnt, pos)) + return 0; + + FUNC(cclm_select_luma)(fc, x0, y0, avail_t, avail_l, cnt, pos, sel[LUMA]); + FUNC(cclm_select_chroma)(fc, x, y, cnt, pos, sel); + + if (cnt[TOP] + cnt[LEFT] == 2) { + for (int c_idx = 0; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) { + sel[c_idx][3] = sel[c_idx][0]; + sel[c_idx][2] = sel[c_idx][1]; + sel[c_idx][0] = sel[c_idx][1]; + sel[c_idx][1] = sel[c_idx][3]; + } + } + return 1; +} + +static av_always_inline void FUNC(cclm_get_min_max)( + const pixel sel[][MAX_PICK_POS * 2], int *min, int *max) +{ + int min_grp_idx[] = { 0, 2 }; + int max_grp_idx[] = { 1, 3 }; + + if (sel[LUMA][min_grp_idx[0]] > sel[LUMA][min_grp_idx[1]]) + FFSWAP(int, min_grp_idx[0], min_grp_idx[1]); + if (sel[LUMA][max_grp_idx[0]] > sel[LUMA][max_grp_idx[1]]) + FFSWAP(int, max_grp_idx[0], max_grp_idx[1]); + if (sel[LUMA][min_grp_idx[0]] > sel[LUMA][max_grp_idx[1]]) { + FFSWAP(int, min_grp_idx[0], max_grp_idx[0]); + FFSWAP(int, min_grp_idx[1], max_grp_idx[1]); + } + if (sel[LUMA][min_grp_idx[1]] > sel[LUMA][max_grp_idx[0]]) + FFSWAP(int, min_grp_idx[1], max_grp_idx[0]); + for (int c_idx = 0; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) { + max[c_idx] = (sel[c_idx][max_grp_idx[0]] + sel[c_idx][max_grp_idx[1]] + 1) >> 1; + min[c_idx] = (sel[c_idx][min_grp_idx[0]] + sel[c_idx][min_grp_idx[1]] + 1) >> 1; + } +} + +static av_always_inline void FUNC(cclm_get_params)(const VVCLocalContext *lc, + const int x0, const int y0, const int w, const int h, const int avail_t, const int avail_l, + int *a, int *b, int *k) +{ + pixel sel[VVC_MAX_SAMPLE_ARRAYS][MAX_PICK_POS * 2]; + int max[VVC_MAX_SAMPLE_ARRAYS], min[VVC_MAX_SAMPLE_ARRAYS]; + int diff; + + if (!FUNC(cclm_select_samples)(lc, x0, y0, w, h, avail_t, avail_l, sel)) { + FUNC(cclm_get_params_default)(a, b, k); + return; + } + + FUNC(cclm_get_min_max)(sel, min, max); + + diff = max[LUMA] - min[LUMA]; + if (diff == 0) { + for (int i = 0; i < 2; i++) { + a[i] = k[i] = 0; + b[i] = min[i + 1]; + } + return; + } + for (int i = 0; i < 2; i++) { + const static int div_sig_table[] = {0, 7, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 1, 1, 0}; + const int diffc = max[i + 1] - min[i + 1]; + int x = av_log2(diff); + int y, v, sign, add; + const int norm_diff = ((diff << 4) >> x) & 15; + x += (norm_diff) ? 1 : 0; + y = abs(diffc) > 0 ? av_log2(abs(diffc)) + 1 : 0; + v = div_sig_table[norm_diff] | 8; + add = (1 << y >> 1); + a[i] = (diffc * v + add) >> y; + k[i] = FFMAX(1, 3 + x -y); + sign = a[i] < 0 ? -1 : (a[i] > 0); + a[i] = ((3 + x - y) < 1) ? sign * 15 : a[i]; + b[i] = min[i + 1] - ((a[i] * min[0]) >> k[i]); + } + +} + +#undef TOP +#undef LEFT + +static av_always_inline void FUNC(cclm_get_luma_rec_pixels)(const VVCFrameContext *fc, + const int x0, const int y0, const int w, const int h, const int avail_t, const int avail_l, + pixel *pdsy) +{ + const int hs = fc->ps.sps->hshift[1]; + const int vs = fc->ps.sps->vshift[1]; + const ptrdiff_t stride = fc->frame->linesize[0] / sizeof(pixel); + const pixel *source = (pixel*)fc->frame->data[0] + x0 + y0 * stride; + const pixel *left = source - avail_l; + const pixel *top = source - avail_t * stride; + + const VVCSPS *sps = fc->ps.sps; + if (!hs && !vs) { + for (int i = 0; i < h; i++) + memcpy(pdsy + i * w, source + i * stride, w * sizeof(pixel)); + return; + } + for (int i = 0; i < h; i++) { + const pixel *src = source; + const pixel *l = left; + const pixel *t = top; + if (!vs) { + for (int j = 0; j < w; j++) { + pixel pred = (*l + 2 * POS(0, 0) + POS(1, 0) + 2) >> 2; + pdsy[i * w + j] = pred; + src += 2; + l = src - 1; + } + + } else { + if (sps->r->sps_chroma_vertical_collocated_flag) { + for (int j = 0; j < w; j++) { + pixel pred = (*l + *t + 4 * POS(0, 0) + POS(1, 0) + POS(0, 1) + 4) >> 3; + pdsy[i * w + j] = pred; + src += 2; + t += 2; + l = src - 1; + } + } else { + for (int j = 0; j < w; j++) { + pixel pred = (*l + *(l + stride) + 2 * POS(0, 0) + 2 * POS(0, 1) + POS(1, 0) + POS(1, 1) + 4) >> 3; + + pdsy[i * w + j] = pred; + src += 2; + l = src - 1; + } + } + } + source += (stride << vs); + left += (stride << vs); + top = source - stride; + } +} + +static av_always_inline void FUNC(cclm_pred_default)(VVCFrameContext *fc, + const int x, const int y, const int w, const int h, const int avail_t, const int avail_l) +{ + for (int c_idx = 1; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) { + const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel); + pixel *dst = (pixel*)fc->frame->data[c_idx] + x + y * stride; + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + dst[j] = 1 << (BIT_DEPTH - 1); + } + dst += stride; + } + } +} + +//8.4.5.2.14 Specification of INTRA_LT_CCLM, INTRA_L_CCLM and INTRA_T_CCLM intra prediction mode +static void FUNC(intra_cclm_pred)(const VVCLocalContext *lc, const int x0, const int y0, + const int width, const int height) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int avail_t = ff_vvc_get_top_available(lc, x0, y0, 1, 0); + const int avail_l = ff_vvc_get_left_available(lc, x0, y0, 1, 0); + const int hs = sps->hshift[1]; + const int vs = sps->vshift[1]; + const int x = x0 >> hs; + const int y = y0 >> vs; + const int w = width >> hs; + const int h = height >> vs; + int a[2], b[2], k[2]; + + pixel dsy[MAX_TB_SIZE * MAX_TB_SIZE]; + if (!avail_t && !avail_l) { + FUNC(cclm_pred_default)(fc, x, y, w, h, avail_t, avail_l); + return; + } + FUNC(cclm_get_luma_rec_pixels)(fc, x0, y0, w, h, avail_t, avail_l, dsy); + FUNC(cclm_get_params) (lc, x0, y0, w, h, avail_t, avail_l, a, b, k); + FUNC(cclm_linear_pred)(fc, x0, y0, w, h, dsy, a, b, k); +} + +static int FUNC(lmcs_sum_samples)(const pixel *start, ptrdiff_t stride, const int avail, const int target_size) +{ + const int size = FFMIN(avail, target_size); + int sum = 0; + for (int i = 0; i < size; i++) { + sum += *start; + start += stride; + } + sum += *(start - stride) * (target_size - size); + return sum; +} + +// 8.7.5.3 Picture reconstruction with luma dependent chroma residual scaling process for chroma samples +static int FUNC(lmcs_derive_chroma_scale)(VVCLocalContext *lc, const int x0, const int y0) +{ + VVCFrameContext *fc = lc->fc; + const VVCLMCS *lmcs = &fc->ps.lmcs; + const int size_y = FFMIN(fc->ps.sps->ctb_size_y, 64); + + const int x = x0 & ~(size_y - 1); + const int y = y0 & ~(size_y - 1); + if (lc->lmcs.x_vpdu != x || lc->lmcs.y_vpdu != y) { + int cnt = 0, luma = 0, i; + const pixel *src = (const pixel *)(fc->frame->data[LUMA] + y * fc->frame->linesize[LUMA] + (x << fc->ps.sps->pixel_shift)); + const ptrdiff_t stride = fc->frame->linesize[LUMA] / sizeof(pixel); + const int avail_t = ff_vvc_get_top_available (lc, x, y, 1, 0); + const int avail_l = ff_vvc_get_left_available(lc, x, y, 1, 0); + if (avail_l) { + luma += FUNC(lmcs_sum_samples)(src - 1, stride, fc->ps.pps->height - y, size_y); + cnt = size_y; + } + if (avail_t) { + luma += FUNC(lmcs_sum_samples)(src - stride, 1, fc->ps.pps->width - x, size_y); + cnt += size_y; + } + if (cnt) + luma = (luma + (cnt >> 1)) >> av_log2(cnt); + else + luma = 1 << (BIT_DEPTH - 1); + + for (i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++) { + if (luma < lmcs->pivot[i + 1]) + break; + } + i = FFMIN(i, LMCS_MAX_BIN_SIZE - 1); + + lc->lmcs.chroma_scale = lmcs->chroma_scale_coeff[i]; + lc->lmcs.x_vpdu = x; + lc->lmcs.y_vpdu = y; + } + return lc->lmcs.chroma_scale; +} + +// 8.7.5.3 Picture reconstruction with luma dependent chroma residual scaling process for chroma samples +static void FUNC(lmcs_scale_chroma)(VVCLocalContext *lc, int *dst, const int *coeff, + const int width, const int height, const int x0_cu, const int y0_cu) +{ + const int chroma_scale = FUNC(lmcs_derive_chroma_scale)(lc, x0_cu, y0_cu); + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int c = av_clip_intp2(*coeff, BIT_DEPTH); + + if (c > 0) + *dst = (c * chroma_scale + (1 << 10)) >> 11; + else + *dst = -((-c * chroma_scale + (1 << 10)) >> 11); + coeff++; + dst++; + } + } +} + +static av_always_inline void FUNC(ref_filter)(const pixel *left, const pixel *top, + pixel *filtered_left, pixel *filtered_top, const int left_size, const int top_size, + const int unfilter_last_one) +{ + filtered_left[-1] = filtered_top[-1] = (left[0] + 2 * left[-1] + top[0] + 2 ) >> 2; + for (int i = 0; i < left_size - unfilter_last_one; i++) { + filtered_left[i] = (left[i- 1] + 2 * left[i] + left[i + 1] + 2) >> 2; + } + for (int i = 0; i < top_size - unfilter_last_one; i++) { + filtered_top[i] = (top[i-1] + 2 * top[i] + top[i + 1] + 2) >> 2; + } + if (unfilter_last_one) { + filtered_top[top_size - 1] = top[top_size - 1]; + filtered_left[left_size - 1] = left[left_size - 1]; + } +} + +static av_always_inline void FUNC(prepare_intra_edge_params)(const VVCLocalContext *lc, + IntraEdgeParams* edge, const pixel *src, const ptrdiff_t stride, + const int x, int y, int w, int h, int c_idx, const int is_intra_mip, + const int mode, const int ref_idx, const int need_pdpc) +{ +#define EXTEND(ptr, val, len) \ +do { \ + for (i = 0; i < (len); i++) \ + *(ptr + i) = val; \ +} while (0) + const CodingUnit *cu = lc->cu; + const int ref_filter_flag = is_intra_mip ? 0 : ff_vvc_ref_filter_flag_derive(mode); + const int filter_flag = !ref_idx && w * h > 32 && !c_idx && + cu->isp_split_type == ISP_NO_SPLIT && ref_filter_flag; + int cand_up_left = lc->na.cand_up_left; + pixel *left = (pixel*)edge->left_array + MAX_TB_SIZE + 3; + pixel *top = (pixel*)edge->top_array + MAX_TB_SIZE + 3; + pixel *filtered_left = (pixel*)edge->filtered_left_array + MAX_TB_SIZE + 3; + pixel *filtered_top = (pixel*)edge->filtered_top_array + MAX_TB_SIZE + 3; + const int ref_line = ref_idx == 3 ? -4 : (-1 - ref_idx); + int left_size, top_size, unfilter_left_size, unfilter_top_size; + int left_available, top_available; + int refw, refh; + int intra_pred_angle, inv_angle; + int i; + + if (is_intra_mip || mode == INTRA_PLANAR) { + left_size = h + 1; + top_size = w + 1; + unfilter_left_size = left_size + filter_flag; + unfilter_top_size = top_size + filter_flag; + } else if (mode == INTRA_DC) { + unfilter_left_size = left_size = h; + unfilter_top_size = top_size = w; + } else if (mode == INTRA_VERT) { + //we may need 1 pixel to predict the top left. + unfilter_left_size = left_size = need_pdpc ? h : 1; + unfilter_top_size = top_size = w; + } else if (mode == INTRA_HORZ) { + unfilter_left_size = left_size = h; + //even need_pdpc == 0, we may need 1 pixel to predict the top left. + unfilter_top_size = top_size = need_pdpc ? w : 1; + } else { + if (cu->isp_split_type == ISP_NO_SPLIT || c_idx) { + refw = w * 2; + refh = h * 2; + } else { + refw = cu->cb_width + w; + refh = cu->cb_height + h; + } + intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode); + inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle); + unfilter_top_size = top_size = refw; + unfilter_left_size = left_size = refh; + } + + left_available = ff_vvc_get_left_available(lc, x, y, unfilter_left_size, c_idx); + for (i = 0; i < left_available; i++) + left[i] = POS(ref_line, i); + + top_available = ff_vvc_get_top_available(lc, x, y, unfilter_top_size, c_idx); + memcpy(top, src + ref_line * stride, top_available * sizeof(pixel)); + + for (int i = -1; i >= ref_line; i--) { + if (cand_up_left) { + left[i] = POS(ref_line, i); + top[i] = POS(i, ref_line); + } else if (left_available) { + left[i] = top[i] = left[0]; + } else if (top_available) { + left[i] = top[i] = top[0]; + } else { + left[i] = top[i] = 1 << (BIT_DEPTH - 1); + } + } + + EXTEND(top + top_available, top[top_available-1], unfilter_top_size - top_available); + EXTEND(left + left_available, left[left_available-1], unfilter_left_size - left_available); + + if (ref_filter_flag) { + if (!ref_idx && w * h > 32 && !c_idx && cu->isp_split_type == ISP_NO_SPLIT ) { + const int unfilter_last_one = left_size == unfilter_left_size; + FUNC(ref_filter)(left, top, filtered_left, filtered_top, unfilter_left_size, unfilter_top_size, unfilter_last_one); + left = filtered_left; + top = filtered_top; + } + } + if (!is_intra_mip && mode != INTRA_PLANAR && mode != INTRA_DC) { + if (ref_filter_flag || ref_idx || cu->isp_split_type != ISP_NO_SPLIT) { + edge->filter_flag = 0; + } else { + const int min_dist_ver_hor = FFMIN(abs(mode - 50), abs(mode - 18)); + const int intra_hor_ver_dist_thres[] = {24, 14, 2, 0, 0}; + const int ntbs = (av_log2(w) + av_log2(h)) >> 1; + edge->filter_flag = min_dist_ver_hor > intra_hor_ver_dist_thres[ntbs - 2]; + } + + if (mode != INTRA_VERT && mode != INTRA_HORZ) { + if (mode >= INTRA_DIAG) { + if (intra_pred_angle < 0) { + pixel *p = top - (ref_idx + 1); + for (int x = -h; x < 0; x++) { + const int idx = -1 - ref_idx + FFMIN((x*inv_angle + 256) >> 9, h); + p[x] = left[idx]; + } + } else { + for (int i = refw; i <= refw + FFMAX(1, w/h) * ref_idx + 1; i++) + top[i] = top[refw - 1]; + } + } else { + if (intra_pred_angle < 0) { + pixel *p = left - (ref_idx + 1); + for (int x = -w; x < 0; x++) { + const int idx = -1 - ref_idx + FFMIN((x*inv_angle + 256) >> 9, w); + p[x] = top[idx]; + } + } else { + for (int i = refh; i <= refh + FFMAX(1, h/w) * ref_idx + 1; i++) + left[i] = left[refh - 1]; + } + } + } + } + edge->left = (uint8_t*)left; + edge->top = (uint8_t*)top; +} + +//8.4.1 General decoding process for coding units coded in intra prediction mode +static void FUNC(intra_pred)(const VVCLocalContext *lc, int x0, int y0, + const int width, const int height, int c_idx) +{ + VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const CodingUnit *cu = lc->cu; + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int min_cb_width = pps->min_cb_width; + const int x_cb = x0 >> log2_min_cb_size; + const int y_cb = y0 >> log2_min_cb_size; + + const int hshift = fc->ps.sps->hshift[c_idx]; + const int vshift = fc->ps.sps->vshift[c_idx]; + const int x = x0 >> hshift; + const int y = y0 >> vshift; + const int w = width >> hshift; + const int h = height >> vshift; + const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel); + + const int pred_mode = c_idx ? cu->intra_pred_mode_c : cu->intra_pred_mode_y; + const int mode = ff_vvc_wide_angle_mode_mapping(cu, w, h, c_idx, pred_mode); + + const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_cb, y_cb); + const int is_intra_mip = intra_mip_flag && (!c_idx || cu->mip_chroma_direct_flag); + const int ref_idx = c_idx ? 0 : cu->intra_luma_ref_idx; + const int need_pdpc = ff_vvc_need_pdpc(w, h, cu->bdpcm_flag[c_idx], mode, ref_idx); + + + pixel *src = (pixel*)fc->frame->data[c_idx] + x + y * stride; + IntraEdgeParams edge; + + FUNC(prepare_intra_edge_params)(lc, &edge, src, stride, x, y, w, h, c_idx, is_intra_mip, mode, ref_idx, need_pdpc); + + if (is_intra_mip) { + int intra_mip_transposed_flag = SAMPLE_CTB(fc->tab.imtf, x_cb, y_cb); + int intra_mip_mode = SAMPLE_CTB(fc->tab.imm, x_cb, y_cb); + + fc->vvcdsp.intra.pred_mip((uint8_t *)src, edge.top, edge.left, + w, h, stride, intra_mip_mode, intra_mip_transposed_flag); + } else if (mode == INTRA_PLANAR) { + fc->vvcdsp.intra.pred_planar((uint8_t *)src, edge.top, edge.left, w, h, stride); + } else if (mode == INTRA_DC) { + fc->vvcdsp.intra.pred_dc((uint8_t *)src, edge.top, edge.left, w, h, stride); + } else if (mode == INTRA_VERT) { + fc->vvcdsp.intra.pred_v((uint8_t *)src, edge.top, w, h, stride); + } else if (mode == INTRA_HORZ) { + fc->vvcdsp.intra.pred_h((uint8_t *)src, edge.left, w, h, stride); + } else { + if (mode >= INTRA_DIAG) { + fc->vvcdsp.intra.pred_angular_v((uint8_t *)src, edge.top, edge.left, + w, h, stride, c_idx, mode, ref_idx, + edge.filter_flag, need_pdpc); + } else { + fc->vvcdsp.intra.pred_angular_h((uint8_t *)src, edge.top, edge.left, + w, h, stride, c_idx, mode, ref_idx, + edge.filter_flag, need_pdpc); + } + } + if (need_pdpc) { + //8.4.5.2.15 Position-dependent intra prediction sample filtering process + if (!is_intra_mip && (mode == INTRA_PLANAR || mode == INTRA_DC || + mode == INTRA_VERT || mode == INTRA_HORZ)) { + const int scale = (av_log2(w) + av_log2(h) - 2) >> 2; + const pixel *left = (pixel*)edge.left; + const pixel *top = (pixel*)edge.top; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int l, t, wl, wt, pred; + pixel val; + if (mode == INTRA_PLANAR || mode == INTRA_DC) { + l = left[y]; + t = top[x]; + wl = 32 >> FFMIN((x << 1) >> scale, 31); + wt = 32 >> FFMIN((y << 1) >> scale, 31); + } else { + l = left[y] - left[-1] + POS(x,y); + t = top[x] - top[-1] + POS(x,y); + wl = (mode == INTRA_VERT) ? (32 >> FFMIN((x << 1) >> scale, 31)) : 0; + wt = (mode == INTRA_HORZ) ? (32 >> FFMIN((y << 1) >> scale, 31)) : 0; + } + val = POS(x, y); + pred = val + ((wl * (l - val) + wt * (t - val) + 32) >> 6); + POS(x, y) = CLIP(pred); + } + } + } + } +} + +//8.4.5.2.11 Specification of INTRA_PLANAR intra prediction mode +static av_always_inline void FUNC(pred_planar)(uint8_t *_src, const uint8_t *_top, + const uint8_t *_left, const int w, const int h, const ptrdiff_t stride) +{ + int x, y; + pixel *src = (pixel *)_src; + const pixel *top = (const pixel *)_top; + const pixel *left = (const pixel *)_left; + const int logw = av_log2(w); + const int logh = av_log2(h); + const int size = w * h; + const int shift = (logw + logh + 1); + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + const int pred_v = ((h - 1 - y) * top[x] + (y + 1) * left[h]) << logw; + const int pred_h = ((w - 1 - x) * left[y] + (x + 1) * top[w]) << logh; + const int pred = (pred_v + pred_h + size) >> shift; + POS(x, y) = pred; + } + } +} + +//8.4.5.2.3 MIP boundary sample downsampling process +static av_always_inline void FUNC(mip_downsampling)(int *reduced, const int boundary_size, + const pixel *ref, const int n_tb_s) +{ + const int b_dwn = n_tb_s / boundary_size; + const int log2 = av_log2(b_dwn); + + if (boundary_size == n_tb_s) { + for (int i = 0; i < n_tb_s; i++) + reduced[i] = ref[i]; + return; + } + for (int i = 0; i < boundary_size; i++) { + int r; + r = *ref++; + for (int j = 1; j < b_dwn; j++) + r += *ref++; + reduced[i] = (r + (1 << (log2 - 1))) >> log2; + } +} + +static av_always_inline void FUNC(mip_reduced_pred)(pixel *src, const ptrdiff_t stride, + const int up_hor, const int up_ver, const int pred_size, const int *reduced, const int reduced_size, + const int ow, const int temp0, const uint8_t *matrix, int is_transposed) +{ + src = &POS(up_hor - 1, up_ver - 1); + for (int y = 0; y < pred_size; y++) { + for (int x = 0; x < pred_size; x++) { + int pred = 0; + for (int i = 0; i < reduced_size; i++) + pred += reduced[i] * matrix[i]; + matrix += reduced_size; + pred = ((pred + ow) >> 6) + temp0; + pred = av_clip(pred, 0, (1< 1 || up_ver > 1) { + if (up_hor > 1) + FUNC(mip_upsampling_1d)(&POS(0, up_ver - 1), 1, up_ver * stride, pred_size, up_hor, left + up_ver - 1, up_ver, pred_size); + if (up_ver > 1) + FUNC(mip_upsampling_1d)(src, stride, 1, w, up_ver, top, 1, pred_size); + } +} + +static av_always_inline pixel FUNC(pred_dc_val)(const pixel *top, const pixel *left, + const int w, const int h) +{ + pixel dc_val; + int sum = 0; + unsigned int offset = (w == h) ? (w << 1) : FFMAX(w, h); + const int shift = av_log2(offset); + offset >>= 1; + if (w >= h) { + for (int i = 0; i < w; i++) + sum += top[i]; + } + if (w <= h) { + for (int i = 0; i < h; i++) + sum += left[i]; + } + dc_val = (sum + offset) >> shift; + return dc_val; +} + +//8.4.5.2.12 Specification of INTRA_DC intra prediction mode +static av_always_inline void FUNC(pred_dc)(uint8_t *_src, const uint8_t *_top, + const uint8_t *_left, const int w, const int h, const ptrdiff_t stride) +{ + int x, y; + pixel *src = (pixel *)_src; + const pixel *top = (const pixel *)_top; + const pixel *left = (const pixel *)_left; + const pixel dc = FUNC(pred_dc_val)(top, left, w, h); + const pixel4 a = PIXEL_SPLAT_X4(dc); + for (y = 0; y < h; y++) { + pixel *s = src; + for (x = 0; x < w; x += 4) { + AV_WN4P(s, a); + s += 4; + } + src += stride; + } +} + +static av_always_inline void FUNC(pred_v)(uint8_t *_src, const uint8_t *_top, + const int w, const int h, const ptrdiff_t stride) +{ + pixel *src = (pixel *)_src; + const pixel *top = (const pixel *)_top; + for (int y = 0; y < h; y++) { + memcpy(src, top, sizeof(pixel) * w); + src += stride; + } +} + +static void FUNC(pred_h)(uint8_t *_src, const uint8_t *_left, const int w, const int h, + const ptrdiff_t stride) +{ + pixel *src = (pixel *)_src; + const pixel *left = (const pixel *)_left; + for (int y = 0; y < h; y++) { + const pixel4 a = PIXEL_SPLAT_X4(left[y]); + for (int x = 0; x < w; x += 4) { + AV_WN4P(&POS(x, y), a); + } + } +} + +#define INTRA_LUMA_FILTER(p) CLIP((p[0] * f[0] + p[1] * f[1] + p[2] * f[2] + p[3] * f[3] + 32) >> 6) +#define INTRA_CHROMA_FILTER(p) (((32 - fact) * p[1] + fact * p[2] + 16) >> 5) + +//8.4.5.2.13 Specification of INTRA_ANGULAR2..INTRA_ANGULAR66 intra prediction modes +static void FUNC(pred_angular_v)(uint8_t *_src, const uint8_t *_top, const uint8_t *_left, + const int w, const int h, const ptrdiff_t stride, const int c_idx, const int mode, + const int ref_idx, const int filter_flag, const int need_pdpc) +{ + pixel *src = (pixel *)_src; + const pixel *left = (const pixel *)_left; + const pixel *top = (const pixel *)_top - (1 + ref_idx); + const int intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode); + int pos = (1 + ref_idx) * intra_pred_angle; + const int dp = intra_pred_angle; + const int is_luma = !c_idx; + int nscale, inv_angle; + + if (need_pdpc) { + inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle); + nscale = ff_vvc_nscale_derive(w, h, mode); + } + + for (int y = 0; y < h; y++) { + const int idx = (pos >> 5) + ref_idx; + const int fact = pos & 31; + if (!fact && (!is_luma || !filter_flag)) { + for (int x = 0; x < w; x++) { + const pixel *p = top + x + idx + 1; + POS(x, y) = *p; + } + } else { + if (!c_idx) { + const int8_t *f = ff_vvc_intra_luma_filter[filter_flag][fact]; + for (int x = 0; x < w; x++) { + const pixel *p = top + x + idx; + POS(x, y) = INTRA_LUMA_FILTER(p); + } + } else { + for (int x = 0; x < w; x++) { + const pixel *p = top + x + idx; + POS(x, y) = INTRA_CHROMA_FILTER(p); + } + } + } + if (need_pdpc) { + int inv_angle_sum = 256 + inv_angle; + for (int x = 0; x < FFMIN(w, 3 << nscale); x++) { + const pixel l = left[y + (inv_angle_sum >> 9)]; + const pixel val = POS(x, y); + const int wl = 32 >> ((x << 1) >> nscale); + const int pred = val + (((l - val) * wl + 32) >> 6); + POS(x, y) = CLIP(pred); + inv_angle_sum += inv_angle; + } + } + pos += dp; + } +} + +//8.4.5.2.13 Specification of INTRA_ANGULAR2..INTRA_ANGULAR66 intra prediction modes +static void FUNC(pred_angular_h)(uint8_t *_src, const uint8_t *_top, const uint8_t *_left, + const int w, const int h, const ptrdiff_t stride, const int c_idx, const int mode, + const int ref_idx, const int filter_flag, const int need_pdpc) +{ + pixel *src = (pixel *)_src; + const pixel *left = (const pixel *)_left - (1 + ref_idx); + const pixel *top = (const pixel *)_top; + const int is_luma = !c_idx; + const int intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode); + const int dp = intra_pred_angle; + int nscale = 0, inv_angle, inv_angle_sum; + + if (need_pdpc) { + inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle); + inv_angle_sum = 256 + inv_angle; + nscale = ff_vvc_nscale_derive(w, h, mode); + } + + for (int y = 0; y < h; y++) { + int pos = (1 + ref_idx) * intra_pred_angle; + int wt; + if (need_pdpc) + wt = (32 >> FFMIN(31, (y * 2) >> nscale)); + + for (int x = 0; x < w; x++) { + const int idx = (pos >> 5) + ref_idx; + const int fact = pos & 31; + const pixel *p = left + y + idx; + int pred; + if (!fact && (!is_luma || !filter_flag)) { + pred = p[1]; + } else { + if (!c_idx) { + const int8_t *f = ff_vvc_intra_luma_filter[filter_flag][fact]; + pred = INTRA_LUMA_FILTER(p); + } else { + pred = INTRA_CHROMA_FILTER(p); + } + } + if (need_pdpc) { + if (y < (3 << nscale)) { + const pixel t = top[x + (inv_angle_sum >> 9)]; + pred = CLIP(pred + (((t - pred) * wt + 32) >> 6)); + } + } + POS(x, y) = pred; + pos += dp; + } + if (need_pdpc) + inv_angle_sum += inv_angle; + } +} + +static void FUNC(ff_vvc_intra_dsp_init)(VVCIntraDSPContext *const intra) +{ + intra->lmcs_scale_chroma = FUNC(lmcs_scale_chroma); + intra->intra_cclm_pred = FUNC(intra_cclm_pred); + intra->intra_pred = FUNC(intra_pred); + intra->pred_planar = FUNC(pred_planar); + intra->pred_mip = FUNC(pred_mip); + intra->pred_dc = FUNC(pred_dc); + intra->pred_v = FUNC(pred_v); + intra->pred_h = FUNC(pred_h); + intra->pred_angular_v = FUNC(pred_angular_v); + intra->pred_angular_h = FUNC(pred_angular_h); +} diff --git a/libavcodec/vvc/vvc_intra_utils.c b/libavcodec/vvc/vvc_intra_utils.c new file mode 100644 index 00000000000..7d34cff023d --- /dev/null +++ b/libavcodec/vvc/vvc_intra_utils.c @@ -0,0 +1,218 @@ +/* + * VVC intra prediction utils + * + * Copyright (C) 2021 Nuo Mi + * + * 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 +#include +#include "libavutil/avassert.h" +#include "libavutil/macros.h" +#include "libavutil/common.h" +#include "vvc_ctu.h" +#include "vvc_intra.h" +#include "vvc_ps.h" +#include "vvcdec.h" + +int ff_vvc_get_mip_size_id(const int w, const int h) +{ + if (w == 4 && h == 4) + return 0; + if ((w == 4 || h == 4) || (w == 8 && h == 8)) + return 1; + return 2; +} + +int ff_vvc_nscale_derive(const int w, const int h, const int mode) +{ + int side_size, nscale; + av_assert0(mode < INTRA_LT_CCLM && !(mode > INTRA_HORZ && mode < INTRA_VERT)); + if (mode == INTRA_PLANAR || mode == INTRA_DC || + mode == INTRA_HORZ || mode == INTRA_VERT) { + nscale = (av_log2(w) + av_log2(h) - 2) >> 2; + } else { + const int intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode); + const int inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle); + if (mode >= INTRA_VERT) + side_size = h; + if (mode <= INTRA_HORZ) + side_size = w; + nscale = FFMIN(2, av_log2(side_size) - av_log2(3 * inv_angle - 2) + 8); + } + return nscale; +} + +int ff_vvc_need_pdpc(const int w, const int h, const uint8_t bdpcm_flag, const int mode, const int ref_idx) +{ + av_assert0(mode < INTRA_LT_CCLM); + if ((w >= 4 && h >= 4) && !ref_idx && !bdpcm_flag) { + int nscale; + if (mode == INTRA_PLANAR || mode == INTRA_DC || + mode == INTRA_HORZ || mode == INTRA_VERT) + return 1; + if (mode > INTRA_HORZ && mode < INTRA_VERT) + return 0; + nscale = ff_vvc_nscale_derive(w, h, mode); + return nscale >= 0; + + } + return 0; +} + +static const ReconstructedArea* get_reconstructed_area(const VVCLocalContext *lc, const int x, const int y, const int c_idx) +{ + const int ch_type = c_idx > 0; + for (int i = lc->num_ras[ch_type] - 1; i >= 0; i--) { + const ReconstructedArea* a = &lc->ras[ch_type][i]; + const int r = (a->x + a->w); + const int b = (a->y + a->h); + if (a->x <= x && x < r && a->y <= y && y < b) + return a; + + //it's too far away, no need check it; + if (x >= r && y >= b) + break; + } + return NULL; +} + +int ff_vvc_get_top_available(const VVCLocalContext *lc, const int x, const int y, int target_size, const int c_idx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int log2_ctb_size_v = sps->ctb_log2_size_y - vs; + const int end_of_ctb_x = ((lc->cu->x0 >> sps->ctb_log2_size_y) + 1) << sps->ctb_log2_size_y; + const int y0b = av_mod_uintp2(y, log2_ctb_size_v); + const int max_x = FFMIN(fc->ps.pps->width, end_of_ctb_x) >> hs; + const ReconstructedArea *a; + int px = x; + + if (!y0b) { + if (!lc->ctb_up_flag) + return 0; + target_size = FFMIN(target_size, (lc->end_of_tiles_x >> hs) - x); + if (sps->r->sps_entropy_coding_sync_enabled_flag) + target_size = FFMIN(target_size, (end_of_ctb_x >> hs) - x); + return target_size; + } + + target_size = FFMAX(0, FFMIN(target_size, max_x - x)); + while (target_size > 0 && (a = get_reconstructed_area(lc, px, y - 1, c_idx))) { + const int sz = FFMIN(target_size, a->x + a->w - px); + px += sz; + target_size -= sz; + } + return px - x; +} + +int ff_vvc_get_left_available(const VVCLocalContext *lc, const int x, const int y, int target_size, const int c_idx) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int hs = sps->hshift[c_idx]; + const int vs = sps->vshift[c_idx]; + const int log2_ctb_size_h = sps->ctb_log2_size_y - hs; + const int x0b = av_mod_uintp2(x, log2_ctb_size_h); + const int end_of_ctb_y = ((lc->cu->y0 >> sps->ctb_log2_size_y) + 1) << sps->ctb_log2_size_y; + const int max_y = FFMIN(fc->ps.pps->height, end_of_ctb_y) >> vs; + const ReconstructedArea *a; + int py = y; + + if (!x0b && !lc->ctb_left_flag) + return 0; + + target_size = FFMAX(0, FFMIN(target_size, max_y - y)); + if (!x0b) + return target_size; + + while (target_size > 0 && (a = get_reconstructed_area(lc, x - 1, py, c_idx))) { + const int sz = FFMIN(target_size, a->y + a->h - py); + py += sz; + target_size -= sz; + } + return py - y; +} + +static int less(const void *a, const void *b) +{ + return *(const int*)a - *(const int*)b; +} + +int ff_vvc_ref_filter_flag_derive(const int mode) +{ + static const int modes[] = { -14, -12, -10, -6, INTRA_PLANAR, 2, 34, 66, 72, 76, 78, 80}; + return bsearch(&mode, modes, FF_ARRAY_ELEMS(modes), sizeof(int), less) != NULL; +} + +int ff_vvc_intra_pred_angle_derive(const int pred_mode) +{ + static const int angles[] = { + 0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 23, 26, 29, + 32, 35, 39, 45, 51, 57, 64, 73, 86, 102, 128, 171, 256, 341, 512 + }; + int sign = 1, idx, intra_pred_angle; + if (pred_mode > INTRA_DIAG) { + idx = pred_mode - INTRA_VERT; + } else if (pred_mode > 0) { + idx = INTRA_HORZ - pred_mode; + } else { + idx = INTRA_HORZ - 2 - pred_mode; + } + if (idx < 0) { + idx = -idx; + sign = -1; + } + intra_pred_angle = sign * angles[idx]; + return intra_pred_angle; +} + +#define ROUND(f) (int)(f < 0 ? -(-f + 0.5) : (f + 0.5)) +int ff_vvc_intra_inv_angle_derive(const int intra_pred_angle) +{ + float inv_angle; + av_assert0(intra_pred_angle); + inv_angle = 32 * 512.0 / intra_pred_angle; + return ROUND(inv_angle); +} + +//8.4.5.2.7 Wide angle intra prediction mode mapping proces +int ff_vvc_wide_angle_mode_mapping(const CodingUnit *cu, + const int tb_width, const int tb_height, const int c_idx, int pred_mode_intra) +{ + int nw, nh, wh_ratio, min, max; + + if (cu->isp_split_type == ISP_NO_SPLIT || c_idx) { + nw = tb_width; + nh = tb_height; + } else { + nw = cu->cb_width; + nh = cu->cb_height; + } + wh_ratio = FFABS(ff_log2(nw) - ff_log2(nh)); + max = (wh_ratio > 1) ? (8 + 2 * wh_ratio) : 8; + min = (wh_ratio > 1) ? (60 - 2 * wh_ratio) : 60; + + if (nw > nh && pred_mode_intra >=2 && pred_mode_intra < max) + pred_mode_intra += 65; + else if (nh > nw && pred_mode_intra <= 66 && pred_mode_intra > min) + pred_mode_intra -= 67; + return pred_mode_intra; +} diff --git a/libavcodec/vvc/vvc_itx_1d.c b/libavcodec/vvc/vvc_itx_1d.c new file mode 100644 index 00000000000..cb076f680fe --- /dev/null +++ b/libavcodec/vvc/vvc_itx_1d.c @@ -0,0 +1,708 @@ +/* + * VVC 1D transform + * + * Copyright (C) 2023 Nuo Mi + * + * 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 + */ + +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2021, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* optimizaed with partial butterfly, see Hung C-Y, Landman P (1997) + Compact inverse discrete cosine transform circuit for MPEG video decoding. + */ + +#include "vvc_data.h" +#include "vvc_itx_1d.h" +#include "libavutil/avutil.h" + +#define G2(m) ((nz > 2) ? (m) : 0) +#define G4(m) ((nz > 4) ? (m) : 0) +#define G8(m) ((nz > 8) ? (m) : 0) +#define G16(m) ((nz > 16) ? (m) : 0) + +/* +transmatrix[2][2] = { + { a, a }, + { a, -a }, +} + */ +void ff_vvc_inv_dct2_2(int *coeffs, const ptrdiff_t stride, const size_t nz) +{ + const int a = 64; + const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride]; + + coeffs[0 * stride] = a * (x0 + x1); + coeffs[1 * stride] = a * (x0 - x1); +} + +/* +transmatrix[4][4] = { + { a, a, a, a}, + { b, c, -c, -b}, + { a, -a, -a, a}, + { c, -b, b, -c}, +} + */ +void ff_vvc_inv_dct2_4(int *coeffs, const ptrdiff_t stride, const size_t nz) +{ + const int a = 64, b = 83, c = 36; + const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride]; + const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride]; + const int E[2] = { + a * (x0 + G2(+x2)), + a * (x0 + G2(-x2)), + }; + const int O[2] = { + b * x1 + G2(+c * x3), + c * x1 + G2(-b * x3), + }; + + coeffs[0 * stride] = E[0] + O[0]; + coeffs[1 * stride] = E[1] + O[1]; + coeffs[2 * stride] = E[1] - O[1]; + coeffs[3 * stride] = E[0] - O[0]; +} + +/* +transmatrix[8][8] = { + { a, a, a, a, a, a, a, a}, + { d, e, f, g, -g, -f, -e, -d}, + { b, c, -c, -b, -b, -c, c, b}, + { e, -g, -d, -f, f, d, g, -e}, + { a, -a, -a, a, a, -a, -a, a}, + { f, -d, g, e, -e, -g, d, -f}, + { c, -b, b, -c, -c, b, -b, c}, + { g, -f, e, -d, d, -e, f, -g}, +} + */ +void ff_vvc_inv_dct2_8(int *coeffs, const ptrdiff_t stride, const size_t nz) +{ + const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18; + const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride]; + const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride]; + const int x4 = coeffs[4 * stride], x5 = coeffs[5 * stride]; + const int x6 = coeffs[6 * stride], x7 = coeffs[7 * stride]; + const int EE[2] = { + a * (x0 + G4(+x4)), + a * (x0 + G4(-x4)), + }; + const int EO[2] = { + G2(b * x2) + G4(+c * x6), + G2(c * x2) + G4(-b * x6), + }; + const int E[4] = { + EE[0] + EO[0], EE[1] + EO[1], + EE[1] - EO[1], EE[0] - EO[0], + }; + const int O[4] = { + d * x1 + G2(+e * x3) + G4(+f * x5 + g * x7), + e * x1 + G2(-g * x3) + G4(-d * x5 - f * x7), + f * x1 + G2(-d * x3) + G4(+g * x5 + e * x7), + g * x1 + G2(-f * x3) + G4(+e * x5 - d * x7), + }; + + coeffs[0 * stride] = E[0] + O[0]; + coeffs[1 * stride] = E[1] + O[1]; + coeffs[2 * stride] = E[2] + O[2]; + coeffs[3 * stride] = E[3] + O[3]; + coeffs[4 * stride] = E[3] - O[3]; + coeffs[5 * stride] = E[2] - O[2]; + coeffs[6 * stride] = E[1] - O[1]; + coeffs[7 * stride] = E[0] - O[0]; +} + +/* +transmatrix[16][16] = { + { a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a}, + { h, i, j, k, l, m, n, o, -o, -n, -m, -l, -k, -j, -i, -h}, + { d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d}, + { i, l, o, -m, -j, -h, -k, -n, n, k, h, j, m, -o, -l, -i}, + { b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b}, + { j, o, -k, -i, -n, l, h, m, -m, -h, -l, n, i, k, -o, -j}, + { e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e}, + { k, -m, -i, o, h, n, -j, -l, l, j, -n, -h, -o, i, m, -k}, + { a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a}, + { l, -j, -n, h, -o, -i, m, k, -k, -m, i, o, -h, n, j, -l}, + { f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f}, + { m, -h, l, n, -i, k, o, -j, j, -o, -k, i, -n, -l, h, -m}, + { c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c}, + { n, -k, h, -j, m, o, -l, i, -i, l, -o, -m, j, -h, k, -n}, + { g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g}, + { o, -n, m, -l, k, -j, i, -h, h, -i, j, -k, l, -m, n, -o}, +} + */ +void ff_vvc_inv_dct2_16(int *coeffs, const ptrdiff_t stride, const size_t nz) +{ + const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18, h = 90; + const int i = 87, j = 80, k = 70, l = 57, m = 43, n = 25, o = 9; + const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride]; + const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride]; + const int x4 = coeffs[4 * stride], x5 = coeffs[5 * stride]; + const int x6 = coeffs[6 * stride], x7 = coeffs[7 * stride]; + const int x8 = coeffs[8 * stride], x9 = coeffs[9 * stride]; + const int x10 = coeffs[10 * stride], x11 = coeffs[11 * stride]; + const int x12 = coeffs[12 * stride], x13 = coeffs[13 * stride]; + const int x14 = coeffs[14 * stride], x15 = coeffs[15 * stride]; + const int EEE[2] = { + a * (x0 + G8(+x8)), + a * (x0 + G8(-x8)), + }; + const int EEO[2] = { + G4(b * x4) + G8(+c * x12), + G4(c * x4) + G8(-b * x12), + }; + const int EE[4] = { + EEE[0] + EEO[0], EEE[1] + EEO[1], + EEE[1] - EEO[1], EEE[0] - EEO[0], + }; + const int EO[4] = { + G2(d * x2) + G4(+e * x6) + G8(+f * x10 + g * x14), + G2(e * x2) + G4(-g * x6) + G8(-d * x10 - f * x14), + G2(f * x2) + G4(-d * x6) + G8(+g * x10 + e * x14), + G2(g * x2) + G4(-f * x6) + G8(+e * x10 - d * x14), + }; + const int E[8] = { + EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3], + EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0], + }; + const int O[8] = { + h * x1 + G2(+i * x3) + G4(+j * x5 + k * x7) + G8(+l * x9 + m * x11 + n * x13 + o * x15), + i * x1 + G2(+l * x3) + G4(+o * x5 - m * x7) + G8(-j * x9 - h * x11 - k * x13 - n * x15), + j * x1 + G2(+o * x3) + G4(-k * x5 - i * x7) + G8(-n * x9 + l * x11 + h * x13 + m * x15), + k * x1 + G2(-m * x3) + G4(-i * x5 + o * x7) + G8(+h * x9 + n * x11 - j * x13 - l * x15), + l * x1 + G2(-j * x3) + G4(-n * x5 + h * x7) + G8(-o * x9 - i * x11 + m * x13 + k * x15), + m * x1 + G2(-h * x3) + G4(+l * x5 + n * x7) + G8(-i * x9 + k * x11 + o * x13 - j * x15), + n * x1 + G2(-k * x3) + G4(+h * x5 - j * x7) + G8(+m * x9 + o * x11 - l * x13 + i * x15), + o * x1 + G2(-n * x3) + G4(+m * x5 - l * x7) + G8(+k * x9 - j * x11 + i * x13 - h * x15), + }; + + coeffs[0 * stride] = E[0] + O[0]; + coeffs[1 * stride] = E[1] + O[1]; + coeffs[2 * stride] = E[2] + O[2]; + coeffs[3 * stride] = E[3] + O[3]; + coeffs[4 * stride] = E[4] + O[4]; + coeffs[5 * stride] = E[5] + O[5]; + coeffs[6 * stride] = E[6] + O[6]; + coeffs[7 * stride] = E[7] + O[7]; + coeffs[8 * stride] = E[7] - O[7]; + coeffs[9 * stride] = E[6] - O[6]; + coeffs[10 * stride] = E[5] - O[5]; + coeffs[11 * stride] = E[4] - O[4]; + coeffs[12 * stride] = E[3] - O[3]; + coeffs[13 * stride] = E[2] - O[2]; + coeffs[14 * stride] = E[1] - O[1]; + coeffs[15 * stride] = E[0] - O[0]; +} + +/* +transMatrix[32][32] = { + { a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a}, + { p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, -E, -D, -C, -B, -A, -z, -y, -x, -w, -v, -u, -t, -s, -r, -q, -p}, + { h, i, j, k, l, m, n, o, -o, -n, -m, -l, -k, -j, -i, -h, -h, -i, -j, -k, -l, -m, -n, -o, o, n, m, l, k, j, i, h}, + { q, t, w, z, C, -E, -B, -y, -v, -s, -p, -r, -u, -x, -A, -D, D, A, x, u, r, p, s, v, y, B, E, -C, -z, -w, -t, -q}, + { d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d, d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d}, + { r, w, B, -D, -y, -t, -p, -u, -z, -E, A, v, q, s, x, C, -C, -x, -s, -q, -v, -A, E, z, u, p, t, y, D, -B, -w, -r}, + { i, l, o, -m, -j, -h, -k, -n, n, k, h, j, m, -o, -l, -i, -i, -l, -o, m, j, h, k, n, -n, -k, -h, -j, -m, o, l, i}, + { s, z, -D, -w, -p, -v, -C, A, t, r, y, -E, -x, -q, -u, -B, B, u, q, x, E, -y, -r, -t, -A, C, v, p, w, D, -z, -s}, + { b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b}, + { t, C, -y, -p, -x, D, u, s, B, -z, -q, -w, E, v, r, A, -A, -r, -v, -E, w, q, z, -B, -s, -u, -D, x, p, y, -C, -t}, + { j, o, -k, -i, -n, l, h, m, -m, -h, -l, n, i, k, -o, -j, -j, -o, k, i, n, -l, -h, -m, m, h, l, -n, -i, -k, o, j}, + { u, -E, -t, -v, D, s, w, -C, -r, -x, B, q, y, -A, -p, -z, z, p, A, -y, -q, -B, x, r, C, -w, -s, -D, v, t, E, -u}, + { e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e, e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e}, + { v, -B, -p, -C, u, w, -A, -q, -D, t, x, -z, -r, -E, s, y, -y, -s, E, r, z, -x, -t, D, q, A, -w, -u, C, p, B, -v}, + { k, -m, -i, o, h, n, -j, -l, l, j, -n, -h, -o, i, m, -k, -k, m, i, -o, -h, -n, j, l, -l, -j, n, h, o, -i, -m, k}, + { w, -y, -u, A, s, -C, -q, E, p, D, -r, -B, t, z, -v, -x, x, v, -z, -t, B, r, -D, -p, -E, q, C, -s, -A, u, y, -w}, + { a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a}, + { x, -v, -z, t, B, -r, -D, p, -E, -q, C, s, -A, -u, y, w, -w, -y, u, A, -s, -C, q, E, -p, D, r, -B, -t, z, v, -x}, + { l, -j, -n, h, -o, -i, m, k, -k, -m, i, o, -h, n, j, -l, -l, j, n, -h, o, i, -m, -k, k, m, -i, -o, h, -n, -j, l}, + { y, -s, -E, r, -z, -x, t, D, -q, A, w, -u, -C, p, -B, -v, v, B, -p, C, u, -w, -A, q, -D, -t, x, z, -r, E, s, -y}, + { f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f, f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f}, + { z, -p, A, y, -q, B, x, -r, C, w, -s, D, v, -t, E, u, -u, -E, t, -v, -D, s, -w, -C, r, -x, -B, q, -y, -A, p, -z}, + { m, -h, l, n, -i, k, o, -j, j, -o, -k, i, -n, -l, h, -m, -m, h, -l, -n, i, -k, -o, j, -j, o, k, -i, n, l, -h, m}, + { A, -r, v, -E, -w, q, -z, -B, s, -u, D, x, -p, y, C, -t, t, -C, -y, p, -x, -D, u, -s, B, z, -q, w, E, -v, r, -A}, + { c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c}, + { B, -u, q, -x, E, y, -r, t, -A, -C, v, -p, w, -D, -z, s, -s, z, D, -w, p, -v, C, A, -t, r, -y, -E, x, -q, u, -B}, + { n, -k, h, -j, m, o, -l, i, -i, l, -o, -m, j, -h, k, -n, -n, k, -h, j, -m, -o, l, -i, i, -l, o, m, -j, h, -k, n}, + { C, -x, s, -q, v, -A, -E, z, -u, p, -t, y, -D, -B, w, -r, r, -w, B, D, -y, t, -p, u, -z, E, A, -v, q, -s, x, -C}, + { g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g, g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g}, + { D, -A, x, -u, r, -p, s, -v, y, -B, E, C, -z, w, -t, q, -q, t, -w, z, -C, -E, B, -y, v, -s, p, -r, u, -x, A, -D}, + { o, -n, m, -l, k, -j, i, -h, h, -i, j, -k, l, -m, n, -o, -o, n, -m, l, -k, j, -i, h, -h, i, -j, k, -l, m, -n, o}, + { E, -D, C, -B, A, -z, y, -x, w, -v, u, -t, s, -r, q, -p, p, -q, r, -s, t, -u, v, -w, x, -y, z, -A, B, -C, D, -E}, +} + */ +void ff_vvc_inv_dct2_32(int *coeffs, const ptrdiff_t stride, const size_t nz) +{ + const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18, h = 90; + const int i = 87, j = 80, k = 70, l = 57, m = 43, n = 25, o = 9, p = 90; + const int q = 90, r = 88, s = 85, t = 82, u = 78, v = 73, w = 67, x = 61; + const int y = 54, z = 46, A = 38, B = 31, C = 22, D = 13, E_= 4; + const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride]; + const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride]; + const int x4 = coeffs[4 * stride], x5 = coeffs[5 * stride]; + const int x6 = coeffs[6 * stride], x7 = coeffs[7 * stride]; + const int x8 = coeffs[8 * stride], x9 = coeffs[9 * stride]; + const int x10 = coeffs[10 * stride], x11 = coeffs[11 * stride]; + const int x12 = coeffs[12 * stride], x13 = coeffs[13 * stride]; + const int x14 = coeffs[14 * stride], x15 = coeffs[15 * stride]; + const int x16 = coeffs[16 * stride], x17 = coeffs[17 * stride]; + const int x18 = coeffs[18 * stride], x19 = coeffs[19 * stride]; + const int x20 = coeffs[20 * stride], x21 = coeffs[21 * stride]; + const int x22 = coeffs[22 * stride], x23 = coeffs[23 * stride]; + const int x24 = coeffs[24 * stride], x25 = coeffs[25 * stride]; + const int x26 = coeffs[26 * stride], x27 = coeffs[27 * stride]; + const int x28 = coeffs[28 * stride], x29 = coeffs[29 * stride]; + const int x30 = coeffs[30 * stride], x31 = coeffs[31 * stride]; + const int EEEE[2] = { + a * (x0 + G16(+x16)), + a * (x0 + G16(-x16)), + }; + const int EEEO[2] = { + G8(b * x8) + G16(+c * x24), + G8(c * x8) + G16(-b * x24), + }; + const int EEE[4] = { + EEEE[0] + EEEO[0], EEEE[1] + EEEO[1], + EEEE[1] - EEEO[1], EEEE[0] - EEEO[0], + }; + const int EEO[4] = { + G4(d * x4) + G8(+e * x12) + G16(+f * x20 + g * x28), + G4(e * x4) + G8(-g * x12) + G16(-d * x20 - f * x28), + G4(f * x4) + G8(-d * x12) + G16(+g * x20 + e * x28), + G4(g * x4) + G8(-f * x12) + G16(+e * x20 - d * x28), + }; + const int EE[8] = { + EEE[0] + EEO[0], EEE[1] + EEO[1], EEE[2] + EEO[2], EEE[3] + EEO[3], + EEE[3] - EEO[3], EEE[2] - EEO[2], EEE[1] - EEO[1], EEE[0] - EEO[0], + }; + const int EO[8] = { + G2(h * x2) + G4(+i * x6) + G8(+ j * x10 + k * x14) + G16(+l * x18 + m * x22 + n * x26 + o * x30), + G2(i * x2) + G4(+l * x6) + G8(+ o * x10 - m * x14) + G16(-j * x18 - h * x22 - k * x26 - n * x30), + G2(j * x2) + G4(+o * x6) + G8(- k * x10 - i * x14) + G16(-n * x18 + l * x22 + h * x26 + m * x30), + G2(k * x2) + G4(-m * x6) + G8(- i * x10 + o * x14) + G16(+h * x18 + n * x22 - j * x26 - l * x30), + G2(l * x2) + G4(-j * x6) + G8(- n * x10 + h * x14) + G16(-o * x18 - i * x22 + m * x26 + k * x30), + G2(m * x2) + G4(-h * x6) + G8(+ l * x10 + n * x14) + G16(-i * x18 + k * x22 + o * x26 - j * x30), + G2(n * x2) + G4(-k * x6) + G8(+ h * x10 - j * x14) + G16(+m * x18 + o * x22 - l * x26 + i * x30), + G2(o * x2) + G4(-n * x6) + G8(+ m * x10 - l * x14) + G16(+k * x18 - j * x22 + i * x26 - h * x30), + }; + const int E[16] = { + EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3], EE[4] + EO[4], EE[5] + EO[5], EE[6] + EO[6], EE[7] + EO[7], + EE[7] - EO[7], EE[6] - EO[6], EE[5] - EO[5], EE[4] - EO[4], EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0], + }; + const int O[16] = { + p * x1 + G2(+q * x3) + G4(+r * x5 + s * x7) + G8(+t * x9 + u * x11 + v * x13 + w * x15) + G16(+x * x17 + y * x19 + z * x21 + A * x23 + B * x25 + C * x27 + D * x29 + E_* x31), + q * x1 + G2(+t * x3) + G4(+w * x5 + z * x7) + G8(+C * x9 - E_* x11 - B * x13 - y * x15) + G16(-v * x17 - s * x19 - p * x21 - r * x23 - u * x25 - x * x27 - A * x29 - D * x31), + r * x1 + G2(+w * x3) + G4(+B * x5 - D * x7) + G8(-y * x9 - t * x11 - p * x13 - u * x15) + G16(-z * x17 - E_* x19 + A * x21 + v * x23 + q * x25 + s * x27 + x * x29 + C * x31), + s * x1 + G2(+z * x3) + G4(-D * x5 - w * x7) + G8(-p * x9 - v * x11 - C * x13 + A * x15) + G16(+t * x17 + r * x19 + y * x21 - E_* x23 - x * x25 - q * x27 - u * x29 - B * x31), + t * x1 + G2(+C * x3) + G4(-y * x5 - p * x7) + G8(-x * x9 + D * x11 + u * x13 + s * x15) + G16(+B * x17 - z * x19 - q * x21 - w * x23 + E_* x25 + v * x27 + r * x29 + A * x31), + u * x1 + G2(-E_* x3) + G4(-t * x5 - v * x7) + G8(+D * x9 + s * x11 + w * x13 - C * x15) + G16(-r * x17 - x * x19 + B * x21 + q * x23 + y * x25 - A * x27 - p * x29 - z * x31), + v * x1 + G2(-B * x3) + G4(-p * x5 - C * x7) + G8(+u * x9 + w * x11 - A * x13 - q * x15) + G16(-D * x17 + t * x19 + x * x21 - z * x23 - r * x25 - E_* x27 + s * x29 + y * x31), + w * x1 + G2(-y * x3) + G4(-u * x5 + A * x7) + G8(+s * x9 - C * x11 - q * x13 + E_* x15) + G16(+p * x17 + D * x19 - r * x21 - B * x23 + t * x25 + z * x27 - v * x29 - x * x31), + x * x1 + G2(-v * x3) + G4(-z * x5 + t * x7) + G8(+B * x9 - r * x11 - D * x13 + p * x15) + G16(-E_* x17 - q * x19 + C * x21 + s * x23 - A * x25 - u * x27 + y * x29 + w * x31), + y * x1 + G2(-s * x3) + G4(-E_* x5 + r * x7) + G8(-z * x9 - x * x11 + t * x13 + D * x15) + G16(-q * x17 + A * x19 + w * x21 - u * x23 - C * x25 + p * x27 - B * x29 - v * x31), + z * x1 + G2(-p * x3) + G4(+A * x5 + y * x7) + G8(-q * x9 + B * x11 + x * x13 - r * x15) + G16(+C * x17 + w * x19 - s * x21 + D * x23 + v * x25 - t * x27 + E_* x29 + u * x31), + A * x1 + G2(-r * x3) + G4(+v * x5 - E_* x7) + G8(-w * x9 + q * x11 - z * x13 - B * x15) + G16(+s * x17 - u * x19 + D * x21 + x * x23 - p * x25 + y * x27 + C * x29 - t * x31), + B * x1 + G2(-u * x3) + G4(+q * x5 - x * x7) + G8(+E_* x9 + y * x11 - r * x13 + t * x15) + G16(-A * x17 - C * x19 + v * x21 - p * x23 + w * x25 - D * x27 - z * x29 + s * x31), + C * x1 + G2(-x * x3) + G4(+s * x5 - q * x7) + G8(+v * x9 - A * x11 - E_* x13 + z * x15) + G16(-u * x17 + p * x19 - t * x21 + y * x23 - D * x25 - B * x27 + w * x29 - r * x31), + D * x1 + G2(-A * x3) + G4(+x * x5 - u * x7) + G8(+r * x9 - p * x11 + s * x13 - v * x15) + G16(+y * x17 - B * x19 + E_* x21 + C * x23 - z * x25 + w * x27 - t * x29 + q * x31), + E_* x1 + G2(-D * x3) + G4(+C * x5 - B * x7) + G8(+A * x9 - z * x11 + y * x13 - x * x15) + G16(+w * x17 - v * x19 + u * x21 - t * x23 + s * x25 - r * x27 + q * x29 - p * x31), + }; + + coeffs[0 * stride] = E[0] + O[0]; + coeffs[1 * stride] = E[1] + O[1]; + coeffs[2 * stride] = E[2] + O[2]; + coeffs[3 * stride] = E[3] + O[3]; + coeffs[4 * stride] = E[4] + O[4]; + coeffs[5 * stride] = E[5] + O[5]; + coeffs[6 * stride] = E[6] + O[6]; + coeffs[7 * stride] = E[7] + O[7]; + coeffs[8 * stride] = E[8] + O[8]; + coeffs[9 * stride] = E[9] + O[9]; + coeffs[10 * stride] = E[10] + O[10]; + coeffs[11 * stride] = E[11] + O[11]; + coeffs[12 * stride] = E[12] + O[12]; + coeffs[13 * stride] = E[13] + O[13]; + coeffs[14 * stride] = E[14] + O[14]; + coeffs[15 * stride] = E[15] + O[15]; + coeffs[16 * stride] = E[15] - O[15]; + coeffs[17 * stride] = E[14] - O[14]; + coeffs[18 * stride] = E[13] - O[13]; + coeffs[19 * stride] = E[12] - O[12]; + coeffs[20 * stride] = E[11] - O[11]; + coeffs[21 * stride] = E[10] - O[10]; + coeffs[22 * stride] = E[9] - O[9]; + coeffs[23 * stride] = E[8] - O[8]; + coeffs[24 * stride] = E[7] - O[7]; + coeffs[25 * stride] = E[6] - O[6]; + coeffs[26 * stride] = E[5] - O[5]; + coeffs[27 * stride] = E[4] - O[4]; + coeffs[28 * stride] = E[3] - O[3]; + coeffs[29 * stride] = E[2] - O[2]; + coeffs[30 * stride] = E[1] - O[1]; + coeffs[31 * stride] = E[0] - O[0]; +} + +/* +transMatrix[64][64] = { + { aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa }, + { bf, bg, bh, bi, bj, bk, bl, bm, bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz, ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, -ck, -cj, -ci, -ch, -cg, -cf, -ce, -cd, -cc, -cb, -ca, -bz, -by, -bx, -bw, -bv, -bu, -bt, -bs, -br, -bq, -bp, -bo, -bn, -bm, -bl, -bk, -bj, -bi, -bh, -bg, -bf }, + { ap, aq, ar, as, at, au, av, aw, ax, ay, az, ba, bb, bc, bd, be, -be, -bd, -bc, -bb, -ba, -az, -ay, -ax, -aw, -av, -au, -at, -as, -ar, -aq, -ap, -ap, -aq, -ar, -as, -at, -au, -av, -aw, -ax, -ay, -az, -ba, -bb, -bc, -bd, -be, be, bd, bc, bb, ba, az, ay, ax, aw, av, au, at, as, ar, aq, ap }, + { bg, bj, bm, bp, bs, bv, by, cb, ce, ch, ck, -ci, -cf, -cc, -bz, -bw, -bt, -bq, -bn, -bk, -bh, -bf, -bi, -bl, -bo, -br, -bu, -bx, -ca, -cd, -cg, -cj, cj, cg, cd, ca, bx, bu, br, bo, bl, bi, bf, bh, bk, bn, bq, bt, bw, bz, cc, cf, ci, -ck, -ch, -ce, -cb, -by, -bv, -bs, -bp, -bm, -bj, -bg }, + { ah, ai, aj, ak, al, am, an, ao, -ao, -an, -am, -al, -ak, -aj, -ai, -ah, -ah, -ai, -aj, -ak, -al, -am, -an, -ao, ao, an, am, al, ak, aj, ai, ah, ah, ai, aj, ak, al, am, an, ao, -ao, -an, -am, -al, -ak, -aj, -ai, -ah, -ah, -ai, -aj, -ak, -al, -am, -an, -ao, ao, an, am, al, ak, aj, ai, ah }, + { bh, bm, br, bw, cb, cg, -ck, -cf, -ca, -bv, -bq, -bl, -bg, -bi, -bn, -bs, -bx, -cc, -ch, cj, ce, bz, bu, bp, bk, bf, bj, bo, bt, by, cd, ci, -ci, -cd, -by, -bt, -bo, -bj, -bf, -bk, -bp, -bu, -bz, -ce, -cj, ch, cc, bx, bs, bn, bi, bg, bl, bq, bv, ca, cf, ck, -cg, -cb, -bw, -br, -bm, -bh }, + { aq, at, aw, az, bc, -be, -bb, -ay, -av, -as, -ap, -ar, -au, -ax, -ba, -bd, bd, ba, ax, au, ar, ap, as, av, ay, bb, be, -bc, -az, -aw, -at, -aq, -aq, -at, -aw, -az, -bc, be, bb, ay, av, as, ap, ar, au, ax, ba, bd, -bd, -ba, -ax, -au, -ar, -ap, -as, -av, -ay, -bb, -be, bc, az, aw, at, aq }, + { bi, bp, bw, cd, ck, -ce, -bx, -bq, -bj, -bh, -bo, -bv, -cc, -cj, cf, by, br, bk, bg, bn, bu, cb, ci, -cg, -bz, -bs, -bl, -bf, -bm, -bt, -ca, -ch, ch, ca, bt, bm, bf, bl, bs, bz, cg, -ci, -cb, -bu, -bn, -bg, -bk, -br, -by, -cf, cj, cc, bv, bo, bh, bj, bq, bx, ce, -ck, -cd, -bw, -bp, -bi }, + { ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad }, + { bj, bs, cb, ck, -cc, -bt, -bk, -bi, -br, -ca, -cj, cd, bu, bl, bh, bq, bz, ci, -ce, -bv, -bm, -bg, -bp, -by, -ch, cf, bw, bn, bf, bo, bx, cg, -cg, -bx, -bo, -bf, -bn, -bw, -cf, ch, by, bp, bg, bm, bv, ce, -ci, -bz, -bq, -bh, -bl, -bu, -cd, cj, ca, br, bi, bk, bt, cc, -ck, -cb, -bs, -bj }, + { ar, aw, bb, -bd, -ay, -at, -ap, -au, -az, -be, ba, av, aq, as, ax, bc, -bc, -ax, -as, -aq, -av, -ba, be, az, au, ap, at, ay, bd, -bb, -aw, -ar, -ar, -aw, -bb, bd, ay, at, ap, au, az, be, -ba, -av, -aq, -as, -ax, -bc, bc, ax, as, aq, av, ba, -be, -az, -au, -ap, -at, -ay, -bd, bb, aw, ar }, + { bk, bv, cg, -ce, -bt, -bi, -bm, -bx, -ci, cc, br, bg, bo, bz, ck, -ca, -bp, -bf, -bq, -cb, cj, by, bn, bh, bs, cd, -ch, -bw, -bl, -bj, -bu, -cf, cf, bu, bj, bl, bw, ch, -cd, -bs, -bh, -bn, -by, -cj, cb, bq, bf, bp, ca, -ck, -bz, -bo, -bg, -br, -cc, ci, bx, bm, bi, bt, ce, -cg, -bv, -bk }, + { ai, al, ao, -am, -aj, -ah, -ak, -an, an, ak, ah, aj, am, -ao, -al, -ai, -ai, -al, -ao, am, aj, ah, ak, an, -an, -ak, -ah, -aj, -am, ao, al, ai, ai, al, ao, -am, -aj, -ah, -ak, -an, an, ak, ah, aj, am, -ao, -al, -ai, -ai, -al, -ao, am, aj, ah, ak, an, -an, -ak, -ah, -aj, -am, ao, al, ai }, + { bl, by, -ck, -bx, -bk, -bm, -bz, cj, bw, bj, bn, ca, -ci, -bv, -bi, -bo, -cb, ch, bu, bh, bp, cc, -cg, -bt, -bg, -bq, -cd, cf, bs, bf, br, ce, -ce, -br, -bf, -bs, -cf, cd, bq, bg, bt, cg, -cc, -bp, -bh, -bu, -ch, cb, bo, bi, bv, ci, -ca, -bn, -bj, -bw, -cj, bz, bm, bk, bx, ck, -by, -bl }, + { as, az, -bd, -aw, -ap, -av, -bc, ba, at, ar, ay, -be, -ax, -aq, -au, -bb, bb, au, aq, ax, be, -ay, -ar, -at, -ba, bc, av, ap, aw, bd, -az, -as, -as, -az, bd, aw, ap, av, bc, -ba, -at, -ar, -ay, be, ax, aq, au, bb, -bb, -au, -aq, -ax, -be, ay, ar, at, ba, -bc, -av, -ap, -aw, -bd, az, as }, + { bm, cb, -cf, -bq, -bi, -bx, cj, bu, bf, bt, ci, -by, -bj, -bp, -ce, cc, bn, bl, ca, -cg, -br, -bh, -bw, ck, bv, bg, bs, ch, -bz, -bk, -bo, -cd, cd, bo, bk, bz, -ch, -bs, -bg, -bv, -ck, bw, bh, br, cg, -ca, -bl, -bn, -cc, ce, bp, bj, by, -ci, -bt, -bf, -bu, -cj, bx, bi, bq, cf, -cb, -bm }, + { ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab }, + { bn, ce, -ca, -bj, -br, -ci, bw, bf, bv, -cj, -bs, -bi, -bz, cf, bo, bm, cd, -cb, -bk, -bq, -ch, bx, bg, bu, -ck, -bt, -bh, -by, cg, bp, bl, cc, -cc, -bl, -bp, -cg, by, bh, bt, ck, -bu, -bg, -bx, ch, bq, bk, cb, -cd, -bm, -bo, -cf, bz, bi, bs, cj, -bv, -bf, -bw, ci, br, bj, ca, -ce, -bn }, + { at, bc, -ay, -ap, -ax, bd, au, as, bb, -az, -aq, -aw, be, av, ar, ba, -ba, -ar, -av, -be, aw, aq, az, -bb, -as, -au, -bd, ax, ap, ay, -bc, -at, -at, -bc, ay, ap, ax, -bd, -au, -as, -bb, az, aq, aw, -be, -av, -ar, -ba, ba, ar, av, be, -aw, -aq, -az, bb, as, au, bd, -ax, -ap, -ay, bc, at }, + { bo, ch, -bv, -bh, -ca, cc, bj, bt, -cj, -bq, -bm, -cf, bx, bf, by, -ce, -bl, -br, -ck, bs, bk, cd, -bz, -bg, -bw, cg, bn, bp, ci, -bu, -bi, -cb, cb, bi, bu, -ci, -bp, -bn, -cg, bw, bg, bz, -cd, -bk, -bs, ck, br, bl, ce, -by, -bf, -bx, cf, bm, bq, cj, -bt, -bj, -cc, ca, bh, bv, -ch, -bo }, + { aj, ao, -ak, -ai, -an, al, ah, am, -am, -ah, -al, an, ai, ak, -ao, -aj, -aj, -ao, ak, ai, an, -al, -ah, -am, am, ah, al, -an, -ai, -ak, ao, aj, aj, ao, -ak, -ai, -an, al, ah, am, -am, -ah, -al, an, ai, ak, -ao, -aj, -aj, -ao, ak, ai, an, -al, -ah, -am, am, ah, al, -an, -ai, -ak, ao, aj }, + { bp, ck, -bq, -bo, -cj, br, bn, ci, -bs, -bm, -ch, bt, bl, cg, -bu, -bk, -cf, bv, bj, ce, -bw, -bi, -cd, bx, bh, cc, -by, -bg, -cb, bz, bf, ca, -ca, -bf, -bz, cb, bg, by, -cc, -bh, -bx, cd, bi, bw, -ce, -bj, -bv, cf, bk, bu, -cg, -bl, -bt, ch, bm, bs, -ci, -bn, -br, cj, bo, bq, -ck, -bp }, + { au, -be, -at, -av, bd, as, aw, -bc, -ar, -ax, bb, aq, ay, -ba, -ap, -az, az, ap, ba, -ay, -aq, -bb, ax, ar, bc, -aw, -as, -bd, av, at, be, -au, -au, be, at, av, -bd, -as, -aw, bc, ar, ax, -bb, -aq, -ay, ba, ap, az, -az, -ap, -ba, ay, aq, bb, -ax, -ar, -bc, aw, as, bd, -av, -at, -be, au }, + { bq, -ci, -bl, -bv, cd, bg, ca, -by, -bi, -cf, bt, bn, ck, -bo, -bs, cg, bj, bx, -cb, -bf, -cc, bw, bk, ch, -br, -bp, cj, bm, bu, -ce, -bh, -bz, bz, bh, ce, -bu, -bm, -cj, bp, br, -ch, -bk, -bw, cc, bf, cb, -bx, -bj, -cg, bs, bo, -ck, -bn, -bt, cf, bi, by, -ca, -bg, -cd, bv, bl, ci, -bq }, + { ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae }, + { br, -cf, -bg, -cc, bu, bo, -ci, -bj, -bz, bx, bl, ck, -bm, -bw, ca, bi, ch, -bp, -bt, cd, bf, ce, -bs, -bq, cg, bh, cb, -bv, -bn, cj, bk, by, -by, -bk, -cj, bn, bv, -cb, -bh, -cg, bq, bs, -ce, -bf, -cd, bt, bp, -ch, -bi, -ca, bw, bm, -ck, -bl, -bx, bz, bj, ci, -bo, -bu, cc, bg, cf, -br }, + { av, -bb, -ap, -bc, au, aw, -ba, -aq, -bd, at, ax, -az, -ar, -be, as, ay, -ay, -as, be, ar, az, -ax, -at, bd, aq, ba, -aw, -au, bc, ap, bb, -av, -av, bb, ap, bc, -au, -aw, ba, aq, bd, -at, -ax, az, ar, be, -as, -ay, ay, as, -be, -ar, -az, ax, at, -bd, -aq, -ba, aw, au, -bc, -ap, -bb, av }, + { bs, -cc, -bi, -cj, bl, bz, -bv, -bp, cf, bf, cg, -bo, -bw, by, bm, -ci, -bh, -cd, br, bt, -cb, -bj, -ck, bk, ca, -bu, -bq, ce, bg, ch, -bn, -bx, bx, bn, -ch, -bg, -ce, bq, bu, -ca, -bk, ck, bj, cb, -bt, -br, cd, bh, ci, -bm, -by, bw, bo, -cg, -bf, -cf, bp, bv, -bz, -bl, cj, bi, cc, -bs }, + { ak, -am, -ai, ao, ah, an, -aj, -al, al, aj, -an, -ah, -ao, ai, am, -ak, -ak, am, ai, -ao, -ah, -an, aj, al, -al, -aj, an, ah, ao, -ai, -am, ak, ak, -am, -ai, ao, ah, an, -aj, -al, al, aj, -an, -ah, -ao, ai, am, -ak, -ak, am, ai, -ao, -ah, -an, aj, al, -al, -aj, an, ah, ao, -ai, -am, ak }, + { bt, -bz, -bn, cf, bh, ck, -bi, -ce, bo, by, -bu, -bs, ca, bm, -cg, -bg, -cj, bj, cd, -bp, -bx, bv, br, -cb, -bl, ch, bf, ci, -bk, -cc, bq, bw, -bw, -bq, cc, bk, -ci, -bf, -ch, bl, cb, -br, -bv, bx, bp, -cd, -bj, cj, bg, cg, -bm, -ca, bs, bu, -by, -bo, ce, bi, -ck, -bh, -cf, bn, bz, -bt }, + { aw, -ay, -au, ba, as, -bc, -aq, be, ap, bd, -ar, -bb, at, az, -av, -ax, ax, av, -az, -at, bb, ar, -bd, -ap, -be, aq, bc, -as, -ba, au, ay, -aw, -aw, ay, au, -ba, -as, bc, aq, -be, -ap, -bd, ar, bb, -at, -az, av, ax, -ax, -av, az, at, -bb, -ar, bd, ap, be, -aq, -bc, as, ba, -au, -ay, aw }, + { bu, -bw, -bs, by, bq, -ca, -bo, cc, bm, -ce, -bk, cg, bi, -ci, -bg, ck, bf, cj, -bh, -ch, bj, cf, -bl, -cd, bn, cb, -bp, -bz, br, bx, -bt, -bv, bv, bt, -bx, -br, bz, bp, -cb, -bn, cd, bl, -cf, -bj, ch, bh, -cj, -bf, -ck, bg, ci, -bi, -cg, bk, ce, -bm, -cc, bo, ca, -bq, -by, bs, bw, -bu }, + { aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa }, + { bv, -bt, -bx, br, bz, -bp, -cb, bn, cd, -bl, -cf, bj, ch, -bh, -cj, bf, -ck, -bg, ci, bi, -cg, -bk, ce, bm, -cc, -bo, ca, bq, -by, -bs, bw, bu, -bu, -bw, bs, by, -bq, -ca, bo, cc, -bm, -ce, bk, cg, -bi, -ci, bg, ck, -bf, cj, bh, -ch, -bj, cf, bl, -cd, -bn, cb, bp, -bz, -br, bx, bt, -bv }, + { ax, -av, -az, at, bb, -ar, -bd, ap, -be, -aq, bc, as, -ba, -au, ay, aw, -aw, -ay, au, ba, -as, -bc, aq, be, -ap, bd, ar, -bb, -at, az, av, -ax, -ax, av, az, -at, -bb, ar, bd, -ap, be, aq, -bc, -as, ba, au, -ay, -aw, aw, ay, -au, -ba, as, bc, -aq, -be, ap, -bd, -ar, bb, at, -az, -av, ax }, + { bw, -bq, -cc, bk, ci, -bf, ch, bl, -cb, -br, bv, bx, -bp, -cd, bj, cj, -bg, cg, bm, -ca, -bs, bu, by, -bo, -ce, bi, ck, -bh, cf, bn, -bz, -bt, bt, bz, -bn, -cf, bh, -ck, -bi, ce, bo, -by, -bu, bs, ca, -bm, -cg, bg, -cj, -bj, cd, bp, -bx, -bv, br, cb, -bl, -ch, bf, -ci, -bk, cc, bq, -bw }, + { al, -aj, -an, ah, -ao, -ai, am, ak, -ak, -am, ai, ao, -ah, an, aj, -al, -al, aj, an, -ah, ao, ai, -am, -ak, ak, am, -ai, -ao, ah, -an, -aj, al, al, -aj, -an, ah, -ao, -ai, am, ak, -ak, -am, ai, ao, -ah, an, aj, -al, -al, aj, an, -ah, ao, ai, -am, -ak, ak, am, -ai, -ao, ah, -an, -aj, al }, + { bx, -bn, -ch, bg, -ce, -bq, bu, ca, -bk, -ck, bj, -cb, -bt, br, cd, -bh, ci, bm, -by, -bw, bo, cg, -bf, cf, bp, -bv, -bz, bl, cj, -bi, cc, bs, -bs, -cc, bi, -cj, -bl, bz, bv, -bp, -cf, bf, -cg, -bo, bw, by, -bm, -ci, bh, -cd, -br, bt, cb, -bj, ck, bk, -ca, -bu, bq, ce, -bg, ch, bn, -bx }, + { ay, -as, -be, ar, -az, -ax, at, bd, -aq, ba, aw, -au, -bc, ap, -bb, -av, av, bb, -ap, bc, au, -aw, -ba, aq, -bd, -at, ax, az, -ar, be, as, -ay, -ay, as, be, -ar, az, ax, -at, -bd, aq, -ba, -aw, au, bc, -ap, bb, av, -av, -bb, ap, -bc, -au, aw, ba, -aq, bd, at, -ax, -az, ar, -be, -as, ay }, + { by, -bk, cj, bn, -bv, -cb, bh, -cg, -bq, bs, ce, -bf, cd, bt, -bp, -ch, bi, -ca, -bw, bm, ck, -bl, bx, bz, -bj, ci, bo, -bu, -cc, bg, -cf, -br, br, cf, -bg, cc, bu, -bo, -ci, bj, -bz, -bx, bl, -ck, -bm, bw, ca, -bi, ch, bp, -bt, -cd, bf, -ce, -bs, bq, cg, -bh, cb, bv, -bn, -cj, bk, -by }, + { af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af }, + { bz, -bh, ce, bu, -bm, cj, bp, -br, -ch, bk, -bw, -cc, bf, -cb, -bx, bj, -cg, -bs, bo, ck, -bn, bt, cf, -bi, by, ca, -bg, cd, bv, -bl, ci, bq, -bq, -ci, bl, -bv, -cd, bg, -ca, -by, bi, -cf, -bt, bn, -ck, -bo, bs, cg, -bj, bx, cb, -bf, cc, bw, -bk, ch, br, -bp, -cj, bm, -bu, -ce, bh, -bz }, + { az, -ap, ba, ay, -aq, bb, ax, -ar, bc, aw, -as, bd, av, -at, be, au, -au, -be, at, -av, -bd, as, -aw, -bc, ar, -ax, -bb, aq, -ay, -ba, ap, -az, -az, ap, -ba, -ay, aq, -bb, -ax, ar, -bc, -aw, as, -bd, -av, at, -be, -au, au, be, -at, av, bd, -as, aw, bc, -ar, ax, bb, -aq, ay, ba, -ap, az }, + { ca, -bf, bz, cb, -bg, by, cc, -bh, bx, cd, -bi, bw, ce, -bj, bv, cf, -bk, bu, cg, -bl, bt, ch, -bm, bs, ci, -bn, br, cj, -bo, bq, ck, -bp, bp, -ck, -bq, bo, -cj, -br, bn, -ci, -bs, bm, -ch, -bt, bl, -cg, -bu, bk, -cf, -bv, bj, -ce, -bw, bi, -cd, -bx, bh, -cc, -by, bg, -cb, -bz, bf, -ca }, + { am, -ah, al, an, -ai, ak, ao, -aj, aj, -ao, -ak, ai, -an, -al, ah, -am, -am, ah, -al, -an, ai, -ak, -ao, aj, -aj, ao, ak, -ai, an, al, -ah, am, am, -ah, al, an, -ai, ak, ao, -aj, aj, -ao, -ak, ai, -an, -al, ah, -am, -am, ah, -al, -an, ai, -ak, -ao, aj, -aj, ao, ak, -ai, an, al, -ah, am }, + { cb, -bi, bu, ci, -bp, bn, -cg, -bw, bg, -bz, -cd, bk, -bs, -ck, br, -bl, ce, by, -bf, bx, cf, -bm, bq, -cj, -bt, bj, -cc, -ca, bh, -bv, -ch, bo, -bo, ch, bv, -bh, ca, cc, -bj, bt, cj, -bq, bm, -cf, -bx, bf, -by, -ce, bl, -br, ck, bs, -bk, cd, bz, -bg, bw, cg, -bn, bp, -ci, -bu, bi, -cb }, + { ba, -ar, av, -be, -aw, aq, -az, -bb, as, -au, bd, ax, -ap, ay, bc, -at, at, -bc, -ay, ap, -ax, -bd, au, -as, bb, az, -aq, aw, be, -av, ar, -ba, -ba, ar, -av, be, aw, -aq, az, bb, -as, au, -bd, -ax, ap, -ay, -bc, at, -at, bc, ay, -ap, ax, bd, -au, as, -bb, -az, aq, -aw, -be, av, -ar, ba }, + { cc, -bl, bp, -cg, -by, bh, -bt, ck, bu, -bg, bx, ch, -bq, bk, -cb, -cd, bm, -bo, cf, bz, -bi, bs, -cj, -bv, bf, -bw, -ci, br, -bj, ca, ce, -bn, bn, -ce, -ca, bj, -br, ci, bw, -bf, bv, cj, -bs, bi, -bz, -cf, bo, -bm, cd, cb, -bk, bq, -ch, -bx, bg, -bu, -ck, bt, -bh, by, cg, -bp, bl, -cc }, + { ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac }, + { cd, -bo, bk, -bz, -ch, bs, -bg, bv, -ck, -bw, bh, -br, cg, ca, -bl, bn, -cc, -ce, bp, -bj, by, ci, -bt, bf, -bu, cj, bx, -bi, bq, -cf, -cb, bm, -bm, cb, cf, -bq, bi, -bx, -cj, bu, -bf, bt, -ci, -by, bj, -bp, ce, cc, -bn, bl, -ca, -cg, br, -bh, bw, ck, -bv, bg, -bs, ch, bz, -bk, bo, -cd }, + { bb, -au, aq, -ax, be, ay, -ar, at, -ba, -bc, av, -ap, aw, -bd, -az, as, -as, az, bd, -aw, ap, -av, bc, ba, -at, ar, -ay, -be, ax, -aq, au, -bb, -bb, au, -aq, ax, -be, -ay, ar, -at, ba, bc, -av, ap, -aw, bd, az, -as, as, -az, -bd, aw, -ap, av, -bc, -ba, at, -ar, ay, be, -ax, aq, -au, bb }, + { ce, -br, bf, -bs, cf, cd, -bq, bg, -bt, cg, cc, -bp, bh, -bu, ch, cb, -bo, bi, -bv, ci, ca, -bn, bj, -bw, cj, bz, -bm, bk, -bx, ck, by, -bl, bl, -by, -ck, bx, -bk, bm, -bz, -cj, bw, -bj, bn, -ca, -ci, bv, -bi, bo, -cb, -ch, bu, -bh, bp, -cc, -cg, bt, -bg, bq, -cd, -cf, bs, -bf, br, -ce }, + { an, -ak, ah, -aj, am, ao, -al, ai, -ai, al, -ao, -am, aj, -ah, ak, -an, -an, ak, -ah, aj, -am, -ao, al, -ai, ai, -al, ao, am, -aj, ah, -ak, an, an, -ak, ah, -aj, am, ao, -al, ai, -ai, al, -ao, -am, aj, -ah, ak, -an, -an, ak, -ah, aj, -am, -ao, al, -ai, ai, -al, ao, am, -aj, ah, -ak, an }, + { cf, -bu, bj, -bl, bw, -ch, -cd, bs, -bh, bn, -by, cj, cb, -bq, bf, -bp, ca, ck, -bz, bo, -bg, br, -cc, -ci, bx, -bm, bi, -bt, ce, cg, -bv, bk, -bk, bv, -cg, -ce, bt, -bi, bm, -bx, ci, cc, -br, bg, -bo, bz, -ck, -ca, bp, -bf, bq, -cb, -cj, by, -bn, bh, -bs, cd, ch, -bw, bl, -bj, bu, -cf }, + { bc, -ax, as, -aq, av, -ba, -be, az, -au, ap, -at, ay, -bd, -bb, aw, -ar, ar, -aw, bb, bd, -ay, at, -ap, au, -az, be, ba, -av, aq, -as, ax, -bc, -bc, ax, -as, aq, -av, ba, be, -az, au, -ap, at, -ay, bd, bb, -aw, ar, -ar, aw, -bb, -bd, ay, -at, ap, -au, az, -be, -ba, av, -aq, as, -ax, bc }, + { cg, -bx, bo, -bf, bn, -bw, cf, ch, -by, bp, -bg, bm, -bv, ce, ci, -bz, bq, -bh, bl, -bu, cd, cj, -ca, br, -bi, bk, -bt, cc, ck, -cb, bs, -bj, bj, -bs, cb, -ck, -cc, bt, -bk, bi, -br, ca, -cj, -cd, bu, -bl, bh, -bq, bz, -ci, -ce, bv, -bm, bg, -bp, by, -ch, -cf, bw, -bn, bf, -bo, bx, -cg }, + { ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag }, + { ch, -ca, bt, -bm, bf, -bl, bs, -bz, cg, ci, -cb, bu, -bn, bg, -bk, br, -by, cf, cj, -cc, bv, -bo, bh, -bj, bq, -bx, ce, ck, -cd, bw, -bp, bi, -bi, bp, -bw, cd, -ck, -ce, bx, -bq, bj, -bh, bo, -bv, cc, -cj, -cf, by, -br, bk, -bg, bn, -bu, cb, -ci, -cg, bz, -bs, bl, -bf, bm, -bt, ca, -ch }, + { bd, -ba, ax, -au, ar, -ap, as, -av, ay, -bb, be, bc, -az, aw, -at, aq, -aq, at, -aw, az, -bc, -be, bb, -ay, av, -as, ap, -ar, au, -ax, ba, -bd, -bd, ba, -ax, au, -ar, ap, -as, av, -ay, bb, -be, -bc, az, -aw, at, -aq, aq, -at, aw, -az, bc, be, -bb, ay, -av, as, -ap, ar, -au, ax, -ba, bd }, + { ci, -cd, by, -bt, bo, -bj, bf, -bk, bp, -bu, bz, -ce, cj, ch, -cc, bx, -bs, bn, -bi, bg, -bl, bq, -bv, ca, -cf, ck, cg, -cb, bw, -br, bm, -bh, bh, -bm, br, -bw, cb, -cg, -ck, cf, -ca, bv, -bq, bl, -bg, bi, -bn, bs, -bx, cc, -ch, -cj, ce, -bz, bu, -bp, bk, -bf, bj, -bo, bt, -by, cd, -ci }, + { ao, -an, am, -al, ak, -aj, ai, -ah, ah, -ai, aj, -ak, al, -am, an, -ao, -ao, an, -am, al, -ak, aj, -ai, ah, -ah, ai, -aj, ak, -al, am, -an, ao, ao, -an, am, -al, ak, -aj, ai, -ah, ah, -ai, aj, -ak, al, -am, an, -ao, -ao, an, -am, al, -ak, aj, -ai, ah, -ah, ai, -aj, ak, -al, am, -an, ao }, + { cj, -cg, cd, -ca, bx, -bu, br, -bo, bl, -bi, bf, -bh, bk, -bn, bq, -bt, bw, -bz, cc, -cf, ci, ck, -ch, ce, -cb, by, -bv, bs, -bp, bm, -bj, bg, -bg, bj, -bm, bp, -bs, bv, -by, cb, -ce, ch, -ck, -ci, cf, -cc, bz, -bw, bt, -bq, bn, -bk, bh, -bf, bi, -bl, bo, -br, bu, -bx, ca, -cd, cg, -cj }, + { be, -bd, bc, -bb, ba, -az, ay, -ax, aw, -av, au, -at, as, -ar, aq, -ap, ap, -aq, ar, -as, at, -au, av, -aw, ax, -ay, az, -ba, bb, -bc, bd, -be, -be, bd, -bc, bb, -ba, az, -ay, ax, -aw, av, -au, at, -as, ar, -aq, ap, -ap, aq, -ar, as, -at, au, -av, aw, -ax, ay, -az, ba, -bb, bc, -bd, be }, + { ck, -cj, ci, -ch, cg, -cf, ce, -cd, cc, -cb, ca, -bz, by, -bx, bw, -bv, bu, -bt, bs, -br, bq, -bp, bo, -bn, bm, -bl, bk, -bj, bi, -bh, bg, -bf, bf, -bg, bh, -bi, bj, -bk, bl, -bm, bn, -bo, bp, -bq, br, -bs, bt, -bu, bv, -bw, bx, -by, bz, -ca, cb, -cc, cd, -ce, cf, -cg, ch, -ci, cj, -ck }, +} + */ + +void ff_vvc_inv_dct2_64(int *coeffs, const ptrdiff_t stride, const size_t nz) +{ + const int aa = 64, ab = 83, ac = 36, ad = 89, ae = 75, af = 50, ag = 18, ah = 90; + const int ai = 87, aj = 80, ak = 70, al = 57, am = 43, an = 25, ao = 9, ap = 90; + const int aq = 90, ar = 88, as = 85, at = 82, au = 78, av = 73, aw = 67, ax = 61; + const int ay = 54, az = 46, ba = 38, bb = 31, bc = 22, bd = 13, be = 4, bf = 91; + const int bg = 90, bh = 90, bi = 90, bj = 88, bk = 87, bl = 86, bm = 84, bn = 83; + const int bo = 81, bp = 79, bq = 77, br = 73, bs = 71, bt = 69, bu = 65, bv = 62; + const int bw = 59, bx = 56, by = 52, bz = 48, ca = 44, cb = 41, cc = 37, cd = 33; + const int ce = 28, cf = 24, cg = 20, ch = 15, ci = 11, cj = 7, ck = 2; + const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride]; + const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride]; + const int x4 = coeffs[4 * stride], x5 = coeffs[5 * stride]; + const int x6 = coeffs[6 * stride], x7 = coeffs[7 * stride]; + const int x8 = coeffs[8 * stride], x9 = coeffs[9 * stride]; + const int x10 = coeffs[10 * stride], x11 = coeffs[11 * stride]; + const int x12 = coeffs[12 * stride], x13 = coeffs[13 * stride]; + const int x14 = coeffs[14 * stride], x15 = coeffs[15 * stride]; + const int x16 = coeffs[16 * stride], x17 = coeffs[17 * stride]; + const int x18 = coeffs[18 * stride], x19 = coeffs[19 * stride]; + const int x20 = coeffs[20 * stride], x21 = coeffs[21 * stride]; + const int x22 = coeffs[22 * stride], x23 = coeffs[23 * stride]; + const int x24 = coeffs[24 * stride], x25 = coeffs[25 * stride]; + const int x26 = coeffs[26 * stride], x27 = coeffs[27 * stride]; + const int x28 = coeffs[28 * stride], x29 = coeffs[29 * stride]; + const int x30 = coeffs[30 * stride], x31 = coeffs[31 * stride]; + //according to vvc specification, x31 to x63 are zeros + const int EEEEE[2] = { + aa * x0, + aa * x0, + }; + const int EEEEO[2] = { + G16(ab * x16), + G16(ac * x16), + }; + const int EEEE[4] = { + EEEEE[0] + EEEEO[0], EEEEE[1] + EEEEO[1], + EEEEE[1] - EEEEO[1], EEEEE[0] - EEEEO[0], + }; + const int EEEO[4] = { + G8(ad * x8) + G16(+ae * x24), + G8(ae * x8) + G16(-ag * x24), + G8(af * x8) + G16(-ad * x24), + G8(ag * x8) + G16(-af * x24), + }; + const int EEE[8] = { + EEEE[0] + EEEO[0], EEEE[1] + EEEO[1], EEEE[2] + EEEO[2], EEEE[3] + EEEO[3], + EEEE[3] - EEEO[3], EEEE[2] - EEEO[2], EEEE[1] - EEEO[1], EEEE[0] - EEEO[0], + }; + const int EEO[8] = { + G4(ah * x4) + G8(+ai * x12) + G16(+aj * x20 + ak * x28), + G4(ai * x4) + G8(+al * x12) + G16(+ao * x20 - am * x28), + G4(aj * x4) + G8(+ao * x12) + G16(-ak * x20 - ai * x28), + G4(ak * x4) + G8(-am * x12) + G16(-ai * x20 + ao * x28), + G4(al * x4) + G8(-aj * x12) + G16(-an * x20 + ah * x28), + G4(am * x4) + G8(-ah * x12) + G16(+al * x20 + an * x28), + G4(an * x4) + G8(-ak * x12) + G16(+ah * x20 - aj * x28), + G4(ao * x4) + G8(-an * x12) + G16(+am * x20 - al * x28), + }; + const int EE[16] = { + EEE[0] + EEO[0], EEE[1] + EEO[1], EEE[2] + EEO[2], EEE[3] + EEO[3], EEE[4] + EEO[4], EEE[5] + EEO[5], EEE[6] + EEO[6], EEE[7] + EEO[7], + EEE[7] - EEO[7], EEE[6] - EEO[6], EEE[5] - EEO[5], EEE[4] - EEO[4], EEE[3] - EEO[3], EEE[2] - EEO[2], EEE[1] - EEO[1], EEE[0] - EEO[0], + }; + const int EO[16] = { + G2(ap * x2) + G4(+aq * x6) + G8(+ar * x10 + as * x14) + G16(+at * x18 + au * x22 + av * x26 + aw * x30), + G2(aq * x2) + G4(+at * x6) + G8(+aw * x10 + az * x14) + G16(+bc * x18 - be * x22 - bb * x26 - ay * x30), + G2(ar * x2) + G4(+aw * x6) + G8(+bb * x10 - bd * x14) + G16(-ay * x18 - at * x22 - ap * x26 - au * x30), + G2(as * x2) + G4(+az * x6) + G8(-bd * x10 - aw * x14) + G16(-ap * x18 - av * x22 - bc * x26 + ba * x30), + G2(at * x2) + G4(+bc * x6) + G8(-ay * x10 - ap * x14) + G16(-ax * x18 + bd * x22 + au * x26 + as * x30), + G2(au * x2) + G4(-be * x6) + G8(-at * x10 - av * x14) + G16(+bd * x18 + as * x22 + aw * x26 - bc * x30), + G2(av * x2) + G4(-bb * x6) + G8(-ap * x10 - bc * x14) + G16(+au * x18 + aw * x22 - ba * x26 - aq * x30), + G2(aw * x2) + G4(-ay * x6) + G8(-au * x10 + ba * x14) + G16(+as * x18 - bc * x22 - aq * x26 + be * x30), + G2(ax * x2) + G4(-av * x6) + G8(-az * x10 + at * x14) + G16(+bb * x18 - ar * x22 - bd * x26 + ap * x30), + G2(ay * x2) + G4(-as * x6) + G8(-be * x10 + ar * x14) + G16(-az * x18 - ax * x22 + at * x26 + bd * x30), + G2(az * x2) + G4(-ap * x6) + G8(+ba * x10 + ay * x14) + G16(-aq * x18 + bb * x22 + ax * x26 - ar * x30), + G2(ba * x2) + G4(-ar * x6) + G8(+av * x10 - be * x14) + G16(-aw * x18 + aq * x22 - az * x26 - bb * x30), + G2(bb * x2) + G4(-au * x6) + G8(+aq * x10 - ax * x14) + G16(+be * x18 + ay * x22 - ar * x26 + at * x30), + G2(bc * x2) + G4(-ax * x6) + G8(+as * x10 - aq * x14) + G16(+av * x18 - ba * x22 - be * x26 + az * x30), + G2(bd * x2) + G4(-ba * x6) + G8(+ax * x10 - au * x14) + G16(+ar * x18 - ap * x22 + as * x26 - av * x30), + G2(be * x2) + G4(-bd * x6) + G8(+bc * x10 - bb * x14) + G16(+ba * x18 - az * x22 + ay * x26 - ax * x30), + }; + const int E[32] = { + EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3], EE[4] + EO[4], EE[5] + EO[5], EE[6] + EO[6], EE[7] + EO[7], EE[8] + EO[8], EE[9] + EO[9], EE[10] + EO[10], EE[11] + EO[11], EE[12] + EO[12], EE[13] + EO[13], EE[14] + EO[14], EE[15] + EO[15], + EE[15] - EO[15], EE[14] - EO[14], EE[13] - EO[13], EE[12] - EO[12], EE[11] - EO[11], EE[10] - EO[10], EE[9] - EO[9], EE[8] - EO[8], EE[7] - EO[7], EE[6] - EO[6], EE[5] - EO[5], EE[4] - EO[4], EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0], + }; + const int O[32] = { + bf * x1 + G2(+bg * x3) + G4(+bh * x5 + bi * x7) + G8(+bj * x9 + bk * x11 + bl * x13 + bm * x15) + G16(+bn * x17 + bo * x19 + bp * x21 + bq * x23 + br * x25 + bs * x27 + bt * x29 + bu * x31), + bg * x1 + G2(+bj * x3) + G4(+bm * x5 + bp * x7) + G8(+bs * x9 + bv * x11 + by * x13 + cb * x15) + G16(+ce * x17 + ch * x19 + ck * x21 - ci * x23 + -cf * x25 - cc * x27 - bz * x29 - bw * x31), + bh * x1 + G2(+bm * x3) + G4(+br * x5 + bw * x7) + G8(+cb * x9 + cg * x11 - ck * x13 - cf * x15) + G16(-ca * x17 - bv * x19 - bq * x21 - bl * x23 + -bg * x25 - bi * x27 - bn * x29 - bs * x31), + bi * x1 + G2(+bp * x3) + G4(+bw * x5 + cd * x7) + G8(+ck * x9 - ce * x11 - bx * x13 - bq * x15) + G16(-bj * x17 - bh * x19 - bo * x21 - bv * x23 + -cc * x25 - cj * x27 + cf * x29 + by * x31), + bj * x1 + G2(+bs * x3) + G4(+cb * x5 + ck * x7) + G8(-cc * x9 - bt * x11 - bk * x13 - bi * x15) + G16(-br * x17 - ca * x19 - cj * x21 + cd * x23 + bu * x25 + bl * x27 + bh * x29 + bq * x31), + bk * x1 + G2(+bv * x3) + G4(+cg * x5 - ce * x7) + G8(-bt * x9 - bi * x11 - bm * x13 - bx * x15) + G16(-ci * x17 + cc * x19 + br * x21 + bg * x23 + bo * x25 + bz * x27 + ck * x29 - ca * x31), + bl * x1 + G2(+by * x3) + G4(-ck * x5 - bx * x7) + G8(-bk * x9 - bm * x11 - bz * x13 + cj * x15) + G16(+bw * x17 + bj * x19 + bn * x21 + ca * x23 + -ci * x25 - bv * x27 - bi * x29 - bo * x31), + bm * x1 + G2(+cb * x3) + G4(-cf * x5 - bq * x7) + G8(-bi * x9 - bx * x11 + cj * x13 + bu * x15) + G16(+bf * x17 + bt * x19 + ci * x21 - by * x23 + -bj * x25 - bp * x27 - ce * x29 + cc * x31), + bn * x1 + G2(+ce * x3) + G4(-ca * x5 - bj * x7) + G8(-br * x9 - ci * x11 + bw * x13 + bf * x15) + G16(+bv * x17 - cj * x19 - bs * x21 - bi * x23 + -bz * x25 + cf * x27 + bo * x29 + bm * x31), + bo * x1 + G2(+ch * x3) + G4(-bv * x5 - bh * x7) + G8(-ca * x9 + cc * x11 + bj * x13 + bt * x15) + G16(-cj * x17 - bq * x19 - bm * x21 - cf * x23 + bx * x25 + bf * x27 + by * x29 - ce * x31), + bp * x1 + G2(+ck * x3) + G4(-bq * x5 - bo * x7) + G8(-cj * x9 + br * x11 + bn * x13 + ci * x15) + G16(-bs * x17 - bm * x19 - ch * x21 + bt * x23 + bl * x25 + cg * x27 - bu * x29 - bk * x31), + bq * x1 + G2(-ci * x3) + G4(-bl * x5 - bv * x7) + G8(+cd * x9 + bg * x11 + ca * x13 - by * x15) + G16(-bi * x17 - cf * x19 + bt * x21 + bn * x23 + ck * x25 - bo * x27 - bs * x29 + cg * x31), + br * x1 + G2(-cf * x3) + G4(-bg * x5 - cc * x7) + G8(+bu * x9 + bo * x11 - ci * x13 - bj * x15) + G16(-bz * x17 + bx * x19 + bl * x21 + ck * x23 + -bm * x25 - bw * x27 + ca * x29 + bi * x31), + bs * x1 + G2(-cc * x3) + G4(-bi * x5 - cj * x7) + G8(+bl * x9 + bz * x11 - bv * x13 - bp * x15) + G16(+cf * x17 + bf * x19 + cg * x21 - bo * x23 + -bw * x25 + by * x27 + bm * x29 - ci * x31), + bt * x1 + G2(-bz * x3) + G4(-bn * x5 + cf * x7) + G8(+bh * x9 + ck * x11 - bi * x13 - ce * x15) + G16(+bo * x17 + by * x19 - bu * x21 - bs * x23 + ca * x25 + bm * x27 - cg * x29 - bg * x31), + bu * x1 + G2(-bw * x3) + G4(-bs * x5 + by * x7) + G8(+bq * x9 - ca * x11 - bo * x13 + cc * x15) + G16(+bm * x17 - ce * x19 - bk * x21 + cg * x23 + bi * x25 - ci * x27 - bg * x29 + ck * x31), + bv * x1 + G2(-bt * x3) + G4(-bx * x5 + br * x7) + G8(+bz * x9 - bp * x11 - cb * x13 + bn * x15) + G16(+cd * x17 - bl * x19 - cf * x21 + bj * x23 + ch * x25 - bh * x27 - cj * x29 + bf * x31), + bw * x1 + G2(-bq * x3) + G4(-cc * x5 + bk * x7) + G8(+ci * x9 - bf * x11 + ch * x13 + bl * x15) + G16(-cb * x17 - br * x19 + bv * x21 + bx * x23 + -bp * x25 - cd * x27 + bj * x29 + cj * x31), + bx * x1 + G2(-bn * x3) + G4(-ch * x5 + bg * x7) + G8(-ce * x9 - bq * x11 + bu * x13 + ca * x15) + G16(-bk * x17 - ck * x19 + bj * x21 - cb * x23 + -bt * x25 + br * x27 + cd * x29 - bh * x31), + by * x1 + G2(-bk * x3) + G4(+cj * x5 + bn * x7) + G8(-bv * x9 - cb * x11 + bh * x13 - cg * x15) + G16(-bq * x17 + bs * x19 + ce * x21 - bf * x23 + cd * x25 + bt * x27 - bp * x29 - ch * x31), + bz * x1 + G2(-bh * x3) + G4(+ce * x5 + bu * x7) + G8(-bm * x9 + cj * x11 + bp * x13 - br * x15) + G16(-ch * x17 + bk * x19 - bw * x21 - cc * x23 + bf * x25 - cb * x27 - bx * x29 + bj * x31), + ca * x1 + G2(-bf * x3) + G4(+bz * x5 + cb * x7) + G8(-bg * x9 + by * x11 + cc * x13 - bh * x15) + G16(+bx * x17 + cd * x19 - bi * x21 + bw * x23 + ce * x25 - bj * x27 + bv * x29 + cf * x31), + cb * x1 + G2(-bi * x3) + G4(+bu * x5 + ci * x7) + G8(-bp * x9 + bn * x11 - cg * x13 - bw * x15) + G16(+bg * x17 - bz * x19 - cd * x21 + bk * x23 + -bs * x25 - ck * x27 + br * x29 - bl * x31), + cc * x1 + G2(-bl * x3) + G4(+bp * x5 - cg * x7) + G8(-by * x9 + bh * x11 - bt * x13 + ck * x15) + G16(+bu * x17 - bg * x19 + bx * x21 + ch * x23 + -bq * x25 + bk * x27 - cb * x29 - cd * x31), + cd * x1 + G2(-bo * x3) + G4(+bk * x5 - bz * x7) + G8(-ch * x9 + bs * x11 - bg * x13 + bv * x15) + G16(-ck * x17 - bw * x19 + bh * x21 - br * x23 + cg * x25 + ca * x27 - bl * x29 + bn * x31), + ce * x1 + G2(-br * x3) + G4(+bf * x5 - bs * x7) + G8(+cf * x9 + cd * x11 - bq * x13 + bg * x15) + G16(-bt * x17 + cg * x19 + cc * x21 - bp * x23 + bh * x25 - bu * x27 + ch * x29 + cb * x31), + cf * x1 + G2(-bu * x3) + G4(+bj * x5 - bl * x7) + G8(+bw * x9 - ch * x11 - cd * x13 + bs * x15) + G16(-bh * x17 + bn * x19 - by * x21 + cj * x23 + cb * x25 - bq * x27 + bf * x29 - bp * x31), + cg * x1 + G2(-bx * x3) + G4(+bo * x5 - bf * x7) + G8(+bn * x9 - bw * x11 + cf * x13 + ch * x15) + G16(-by * x17 + bp * x19 - bg * x21 + bm * x23 + -bv * x25 + ce * x27 + ci * x29 - bz * x31), + ch * x1 + G2(-ca * x3) + G4(+bt * x5 - bm * x7) + G8(+bf * x9 - bl * x11 + bs * x13 - bz * x15) + G16(+cg * x17 + ci * x19 - cb * x21 + bu * x23 + -bn * x25 + bg * x27 - bk * x29 + br * x31), + ci * x1 + G2(-cd * x3) + G4(+by * x5 - bt * x7) + G8(+bo * x9 - bj * x11 + bf * x13 - bk * x15) + G16(+bp * x17 - bu * x19 + bz * x21 - ce * x23 + cj * x25 + ch * x27 - cc * x29 + bx * x31), + cj * x1 + G2(-cg * x3) + G4(+cd * x5 - ca * x7) + G8(+bx * x9 - bu * x11 + br * x13 - bo * x15) + G16(+bl * x17 - bi * x19 + bf * x21 - bh * x23 + bk * x25 - bn * x27 + bq * x29 - bt * x31), + ck * x1 + G2(-cj * x3) + G4(+ci * x5 - ch * x7) + G8(+cg * x9 - cf * x11 + ce * x13 - cd * x15) + G16(+cc * x17 - cb * x19 + ca * x21 - bz * x23 + by * x25 - bx * x27 + bw * x29 - bv * x31), + }; + coeffs[0 * stride] = E[0 ] + O[0 ]; + coeffs[1 * stride] = E[1 ] + O[1 ]; + coeffs[2 * stride] = E[2 ] + O[2 ]; + coeffs[3 * stride] = E[3 ] + O[3 ]; + coeffs[4 * stride] = E[4 ] + O[4 ]; + coeffs[5 * stride] = E[5 ] + O[5 ]; + coeffs[6 * stride] = E[6 ] + O[6 ]; + coeffs[7 * stride] = E[7 ] + O[7 ]; + coeffs[8 * stride] = E[8 ] + O[8 ]; + coeffs[9 * stride] = E[9 ] + O[9 ]; + coeffs[10 * stride] = E[10] + O[10]; + coeffs[11 * stride] = E[11] + O[11]; + coeffs[12 * stride] = E[12] + O[12]; + coeffs[13 * stride] = E[13] + O[13]; + coeffs[14 * stride] = E[14] + O[14]; + coeffs[15 * stride] = E[15] + O[15]; + coeffs[16 * stride] = E[16] + O[16]; + coeffs[17 * stride] = E[17] + O[17]; + coeffs[18 * stride] = E[18] + O[18]; + coeffs[19 * stride] = E[19] + O[19]; + coeffs[20 * stride] = E[20] + O[20]; + coeffs[21 * stride] = E[21] + O[21]; + coeffs[22 * stride] = E[22] + O[22]; + coeffs[23 * stride] = E[23] + O[23]; + coeffs[24 * stride] = E[24] + O[24]; + coeffs[25 * stride] = E[25] + O[25]; + coeffs[26 * stride] = E[26] + O[26]; + coeffs[27 * stride] = E[27] + O[27]; + coeffs[28 * stride] = E[28] + O[28]; + coeffs[29 * stride] = E[29] + O[29]; + coeffs[30 * stride] = E[30] + O[30]; + coeffs[31 * stride] = E[31] + O[31]; + coeffs[32 * stride] = E[31] - O[31]; + coeffs[33 * stride] = E[30] - O[30]; + coeffs[34 * stride] = E[29] - O[29]; + coeffs[35 * stride] = E[28] - O[28]; + coeffs[36 * stride] = E[27] - O[27]; + coeffs[37 * stride] = E[26] - O[26]; + coeffs[38 * stride] = E[25] - O[25]; + coeffs[39 * stride] = E[24] - O[24]; + coeffs[40 * stride] = E[23] - O[23]; + coeffs[41 * stride] = E[22] - O[22]; + coeffs[42 * stride] = E[21] - O[21]; + coeffs[43 * stride] = E[20] - O[20]; + coeffs[44 * stride] = E[19] - O[19]; + coeffs[45 * stride] = E[18] - O[18]; + coeffs[46 * stride] = E[17] - O[17]; + coeffs[47 * stride] = E[16] - O[16]; + coeffs[48 * stride] = E[15] - O[15]; + coeffs[49 * stride] = E[14] - O[14]; + coeffs[50 * stride] = E[13] - O[13]; + coeffs[51 * stride] = E[12] - O[12]; + coeffs[52 * stride] = E[11] - O[11]; + coeffs[53 * stride] = E[10] - O[10]; + coeffs[54 * stride] = E[9] - O[9]; + coeffs[55 * stride] = E[8] - O[8]; + coeffs[56 * stride] = E[7] - O[7]; + coeffs[57 * stride] = E[6] - O[6]; + coeffs[58 * stride] = E[5] - O[5]; + coeffs[59 * stride] = E[4] - O[4]; + coeffs[60 * stride] = E[3] - O[3]; + coeffs[61 * stride] = E[2] - O[2]; + coeffs[62 * stride] = E[1] - O[1]; + coeffs[63 * stride] = E[0] - O[0]; +} + +static void matrix_mul(int *coeffs, const ptrdiff_t stride, const int8_t* matrix, const int size, const size_t nz) +{ + //for dst7 and dct8, coeffs > 16 are zero out + int tmp[16]; + + for (int i = 0; i < nz; i++) + tmp[i] = coeffs[i * stride]; + + for (int i = 0; i < size; i++) { + int o = 0; + + for (int j = 0; j < nz; j++) + o += tmp[j] * matrix[j * size]; + *coeffs = o; + coeffs += stride; + matrix++; + } +} + +static void inv_dct8(int *coeffs, const ptrdiff_t stride, const int8_t *matrix, const int size, const size_t nz) +{ + matrix_mul(coeffs, stride, matrix, size, nz); +} + +#define DEFINE_INV_DCT8_1D(S) \ +void ff_vvc_inv_dct8_ ## S(int *coeffs, const ptrdiff_t stride, const size_t nz) \ +{ \ + inv_dct8(coeffs, stride, &ff_vvc_dct8_##S##x##S[0][0], S, nz); \ +} + +DEFINE_INV_DCT8_1D( 4) +DEFINE_INV_DCT8_1D( 8) +DEFINE_INV_DCT8_1D(16) +DEFINE_INV_DCT8_1D(32) + +static void inv_dst7(int *coeffs, const ptrdiff_t stride, const int8_t *matrix, const int size, const size_t nz) +{ + matrix_mul(coeffs, stride, matrix, size, nz); +} + +#define DEFINE_INV_DST7_1D(S) \ +void ff_vvc_inv_dst7_ ## S(int *coeffs, const ptrdiff_t stride, const size_t nz) \ +{ \ + inv_dst7(coeffs, stride, &ff_vvc_dst7_##S##x##S[0][0], S, nz); \ +} + +DEFINE_INV_DST7_1D( 4) +DEFINE_INV_DST7_1D( 8) +DEFINE_INV_DST7_1D(16) +DEFINE_INV_DST7_1D(32) + +void ff_vvc_inv_lfnst_1d(int *v, const int *u, int no_zero_size, int n_tr_s, + int pred_mode_intra, int lfnst_idx, int log2_transform_range) +{ + int lfnst_tr_set_idx = pred_mode_intra < 0 ? 1 : ff_vvc_lfnst_tr_set_index[pred_mode_intra]; + const int8_t *tr_mat = n_tr_s > 16 ? ff_vvc_lfnst_8x8[lfnst_tr_set_idx][lfnst_idx-1][0] : ff_vvc_lfnst_4x4[lfnst_tr_set_idx][lfnst_idx - 1][0]; + + for (int j = 0; j < n_tr_s; j++, tr_mat++) { + int t = 0; + + for (int i = 0; i < no_zero_size; i++) + t += u[i] * tr_mat[i * n_tr_s]; + v[j] = av_clip_intp2((t + 64) >> 7 , log2_transform_range); + } +} diff --git a/libavcodec/vvc/vvc_itx_1d.h b/libavcodec/vvc/vvc_itx_1d.h new file mode 100644 index 00000000000..d9094f524bd --- /dev/null +++ b/libavcodec/vvc/vvc_itx_1d.h @@ -0,0 +1,52 @@ +/* + * VVC 1D transform + * + * Copyright (C) 2023 Nuo Mi + * + * 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 + */ + +#ifndef AVCODEC_VVC_VVC_ITX_1D_H +#define AVCODEC_VVC_VVC_ITX_1D_H + +#include +#include + +#define vvc_itx_1d_fn(name) \ + void (name)(int *coeffs, ptrdiff_t stride, size_t nz) +typedef vvc_itx_1d_fn(*vvc_itx_1d_fn); + +vvc_itx_1d_fn(ff_vvc_inv_dct2_2); +vvc_itx_1d_fn(ff_vvc_inv_dct2_4); +vvc_itx_1d_fn(ff_vvc_inv_dct2_8); +vvc_itx_1d_fn(ff_vvc_inv_dct2_16); +vvc_itx_1d_fn(ff_vvc_inv_dct2_32); +vvc_itx_1d_fn(ff_vvc_inv_dct2_64); +vvc_itx_1d_fn(ff_vvc_inv_dst7_4); +vvc_itx_1d_fn(ff_vvc_inv_dst7_8); +vvc_itx_1d_fn(ff_vvc_inv_dst7_16); +vvc_itx_1d_fn(ff_vvc_inv_dst7_32); +vvc_itx_1d_fn(ff_vvc_inv_dct8_4); +vvc_itx_1d_fn(ff_vvc_inv_dct8_8); +vvc_itx_1d_fn(ff_vvc_inv_dct8_16); +vvc_itx_1d_fn(ff_vvc_inv_dct8_32); + + +void ff_vvc_inv_lfnst_1d(int *v, const int *u, int no_zero_size, int n_tr_s, + int pred_mode_intra, int lfnst_idx, int log2_transform_range); + +#endif // AVCODEC_VVC_VVC_ITX_1D_H diff --git a/libavcodec/vvc/vvc_mvs.c b/libavcodec/vvc/vvc_mvs.c new file mode 100644 index 00000000000..51d5e3b6b0d --- /dev/null +++ b/libavcodec/vvc/vvc_mvs.c @@ -0,0 +1,1917 @@ +/* + * VVC motion vector decoder + * + * Copyright (C) 2023 Nuo Mi + * Copyright (C) 2022 Xu Mu + * 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 "vvc_ctu.h" +#include "vvc_data.h" +#include "vvc_refs.h" +#include "vvc_mvs.h" + +#define IS_SAME_MV(a, b) (AV_RN64A(a) == AV_RN64A(b)) + +//check if the two luma locations belong to the same motion estimation region +static av_always_inline int is_same_mer(const VVCFrameContext *fc, const int xN, const int yN, const int xP, const int yP) +{ + const uint8_t plevel = fc->ps.sps->log2_parallel_merge_level; + + return xN >> plevel == xP >> plevel && + yN >> plevel == yP >> plevel; +} + +//return true if we have same mvs and ref_idxs +static av_always_inline int compare_mv_ref_idx(const MvField *n, const MvField *o) +{ + if (!o || n->pred_flag != o->pred_flag) + return 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (n->pred_flag & mask) { + const int same_ref_idx = n->ref_idx[i] == o->ref_idx[i]; + const int same_mv = IS_SAME_MV(n->mv + i, o->mv + i); + if (!same_ref_idx || !same_mv) + return 0; + } + } + return 1; +} + +// 8.5.2.15 Temporal motion buffer compression process for collocated motion vectors +static av_always_inline void mv_compression(Mv *motion) +{ + int mv[2] = {motion->x, motion->y}; + for (int i = 0; i < 2; i++) { + const int s = mv[i] >> 17; + const int f = av_log2((mv[i] ^ s) | 31) - 4; + const int mask = (-1 * (1 << f)) >> 1; + const int round = (1 << f) >> 2; + mv[i] = (mv[i] + round) & mask; + } + motion->x = mv[0]; + motion->y = mv[1]; +} + +void ff_vvc_mv_scale(Mv *dst, const Mv *src, int td, int tb) +{ + int tx, scale_factor; + + td = av_clip_int8(td); + tb = av_clip_int8(tb); + tx = (0x4000 + (abs(td) >> 1)) / td; + scale_factor = av_clip_intp2((tb * tx + 32) >> 6, 12); + dst->x = av_clip_intp2((scale_factor * src->x + 127 + + (scale_factor * src->x < 0)) >> 8, 17); + dst->y = av_clip_intp2((scale_factor * src->y + 127 + + (scale_factor * src->y < 0)) >> 8, 17); +} + +//part of 8.5.2.12 Derivation process for collocated motion vectors +static int check_mvset(Mv *mvLXCol, Mv *mvCol, + int colPic, int poc, + const RefPicList *refPicList, int X, int refIdxLx, + const RefPicList *refPicList_col, int listCol, int refidxCol) +{ + int cur_lt = refPicList[X].isLongTerm[refIdxLx]; + int col_lt = refPicList_col[listCol].isLongTerm[refidxCol]; + int col_poc_diff, cur_poc_diff; + + if (cur_lt != col_lt) { + mvLXCol->x = 0; + mvLXCol->y = 0; + return 0; + } + + col_poc_diff = colPic - refPicList_col[listCol].list[refidxCol]; + cur_poc_diff = poc - refPicList[X].list[refIdxLx]; + + mv_compression(mvCol); + if (cur_lt || col_poc_diff == cur_poc_diff) { + mvLXCol->x = av_clip_intp2(mvCol->x, 17); + mvLXCol->y = av_clip_intp2(mvCol->y, 17); + } else { + ff_vvc_mv_scale(mvLXCol, mvCol, col_poc_diff, cur_poc_diff); + } + return 1; +} + +#define CHECK_MVSET(l) \ + check_mvset(mvLXCol, temp_col.mv + l, \ + colPic, fc->ps.ph.poc, \ + refPicList, X, refIdxLx, \ + refPicList_col, L ## l, temp_col.ref_idx[l]) + +//derive NoBackwardPredFlag +int ff_vvc_no_backward_pred_flag(const VVCLocalContext *lc) +{ + int check_diffpicount = 0; + int i, j; + const RefPicList *rpl = lc->sc->rpl; + + for (j = 0; j < 2; j++) { + for (i = 0; i < rpl[j].nb_refs; i++) { + if (rpl[j].list[i] > lc->fc->ps.ph.poc) { + check_diffpicount++; + break; + } + } + } + return !check_diffpicount; +} + +//8.5.2.12 Derivation process for collocated motion vectors +static int derive_temporal_colocated_mvs(const VVCLocalContext *lc, MvField temp_col, + int refIdxLx, Mv *mvLXCol, int X, + int colPic, const RefPicList *refPicList_col, int sb_flag) +{ + const VVCFrameContext *fc = lc->fc; + const SliceContext *sc = lc->sc; + RefPicList* refPicList = sc->rpl; + + if (temp_col.pred_flag == PF_INTRA) + return 0; + + if (sb_flag){ + if (X == 0) { + if (temp_col.pred_flag & PF_L0) + return CHECK_MVSET(0); + else if (ff_vvc_no_backward_pred_flag(lc) && (temp_col.pred_flag & PF_L1)) + return CHECK_MVSET(1); + } else { + if (temp_col.pred_flag & PF_L1) + return CHECK_MVSET(1); + else if (ff_vvc_no_backward_pred_flag(lc) && (temp_col.pred_flag & PF_L0)) + return CHECK_MVSET(0); + } + } else { + if (!(temp_col.pred_flag & PF_L0)) + return CHECK_MVSET(1); + else if (temp_col.pred_flag == PF_L0) + return CHECK_MVSET(0); + else if (temp_col.pred_flag == PF_BI) { + if (ff_vvc_no_backward_pred_flag(lc)) { + if (X == 0) + return CHECK_MVSET(0); + else + return CHECK_MVSET(1); + } else { + if (!lc->sc->sh.r->sh_collocated_from_l0_flag) + return CHECK_MVSET(0); + else + return CHECK_MVSET(1); + } + } + } + return 0; +} + +#define TAB_MVF(x, y) \ + tab_mvf[((y) >> MIN_PU_LOG2) * min_pu_width + ((x) >> MIN_PU_LOG2)] + +#define TAB_MVF_PU(v) \ + TAB_MVF(x ## v, y ## v) + +#define TAB_CP_MV(lx, x, y) \ + fc->tab.cp_mv[lx][((((y) >> min_cb_log2_size) * min_cb_width + ((x) >> min_cb_log2_size)) ) * MAX_CONTROL_POINTS] + + +#define DERIVE_TEMPORAL_COLOCATED_MVS(sb_flag) \ + derive_temporal_colocated_mvs(lc, temp_col, \ + refIdxLx, mvLXCol, X, colPic, \ + ff_vvc_get_ref_list(fc, ref, x, y), sb_flag) + +//8.5.2.11 Derivation process for temporal luma motion vector prediction +static int temporal_luma_motion_vector(const VVCLocalContext *lc, + const int refIdxLx, Mv *mvLXCol, const int X, int check_center, int sb_flag) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const CodingUnit *cu = lc->cu; + int x, y, colPic, availableFlagLXCol = 0; + int min_pu_width = fc->ps.pps->min_pu_width; + VVCFrame *ref = fc->ref->collocated_ref; + MvField *tab_mvf; + MvField temp_col; + + if (!ref) { + memset(mvLXCol, 0, sizeof(*mvLXCol)); + return 0; + } + + if (!fc->ps.ph.r->ph_temporal_mvp_enabled_flag || (cu->cb_width * cu->cb_height <= 32)) + return 0; + + tab_mvf = ref->tab_dmvr_mvf; + colPic = ref->poc; + + //bottom right collocated motion vector + x = cu->x0 + cu->cb_width; + y = cu->y0 + cu->cb_height; + + if (tab_mvf && + (cu->y0 >> sps->ctb_log2_size_y) == (y >> sps->ctb_log2_size_y) && + y < fc->ps.pps->height && + x < fc->ps.pps->width) { + x &= ~7; + y &= ~7; + temp_col = TAB_MVF(x, y); + availableFlagLXCol = DERIVE_TEMPORAL_COLOCATED_MVS(sb_flag); + } + if (check_center) { + // derive center collocated motion vector + if (tab_mvf && !availableFlagLXCol) { + x = cu->x0 + (cu->cb_width >> 1); + y = cu->y0 + (cu->cb_height >> 1); + x &= ~7; + y &= ~7; + temp_col = TAB_MVF(x, y); + availableFlagLXCol = DERIVE_TEMPORAL_COLOCATED_MVS(sb_flag); + } + } + return availableFlagLXCol; +} + +void ff_vvc_set_mvf(const VVCLocalContext *lc, const int x0, const int y0, const int w, const int h, const MvField *mvf) +{ + const VVCFrameContext *fc = lc->fc; + MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_pu_size = 1 << MIN_PU_LOG2; + for (int dy = 0; dy < h; dy += min_pu_size) { + for (int dx = 0; dx < w; dx += min_pu_size) { + const int x = x0 + dx; + const int y = y0 + dy; + TAB_MVF(x, y) = *mvf; + } + } +} + +void ff_vvc_set_intra_mvf(const VVCLocalContext *lc, const int dmvr) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + MvField *tab_mvf = dmvr ? fc->ref->tab_dmvr_mvf : fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + const int min_pu_size = 1 << MIN_PU_LOG2; + for (int dy = 0; dy < cu->cb_height; dy += min_pu_size) { + for (int dx = 0; dx < cu->cb_width; dx += min_pu_size) { + const int x = cu->x0 + dx; + const int y = cu->y0 + dy; + TAB_MVF(x, y).pred_flag = PF_INTRA; + } + } +} + +//cbProfFlagLX from 8.5.5.9 Derivation process for motion vector arrays from affine control point motion vectors +static int derive_cb_prof_flag_lx(const VVCLocalContext *lc, const PredictionUnit* pu, int lx, int is_fallback) +{ + const MotionInfo* mi = &pu->mi; + const Mv* cp_mv = &mi->mv[lx][0]; + if (lc->fc->ps.ph.r->ph_prof_disabled_flag || is_fallback) + return 0; + if (mi->motion_model_idc == MOTION_4_PARAMS_AFFINE) { + if (IS_SAME_MV(cp_mv, cp_mv + 1)) + return 0; + } + if (mi->motion_model_idc == MOTION_6_PARAMS_AFFINE) { + if (IS_SAME_MV(cp_mv, cp_mv + 1) && IS_SAME_MV(cp_mv, cp_mv + 2)) + return 0; + } + //fixme: RprConstraintsActiveFlag + return 1; +} + +typedef struct SubblockParams { + int d_hor_x; + int d_ver_x; + int d_hor_y; + int d_ver_y; + int mv_scale_hor; + int mv_scale_ver; + int is_fallback; + + int cb_width; + int cb_height; +} SubblockParams; + +static int is_fallback_mode(const SubblockParams *sp, const PredFlag pred_flag) +{ + const int a = 4 * (2048 + sp->d_hor_x); + const int b = 4 * sp->d_hor_y; + const int c = 4 * (2048 + sp->d_ver_y); + const int d = 4 * sp->d_ver_x; + if (pred_flag == PF_BI) { + const int max_w4 = FFMAX(0, FFMAX(a, FFMAX(b, a + b))); + const int min_w4 = FFMIN(0, FFMIN(a, FFMIN(b, a + b))); + const int max_h4 = FFMAX(0, FFMAX(c, FFMAX(d, c + d))); + const int min_h4 = FFMIN(0, FFMIN(c, FFMIN(d, c + d))); + const int bx_wx4 = ((max_w4 - min_w4) >> 11) + 9; + const int bx_hx4 = ((max_h4 - min_h4) >> 11) + 9; + return bx_wx4 * bx_hx4 > 225; + } else { + const int bx_wxh = (FFABS(a) >> 11) + 9; + const int bx_hxh = (FFABS(d) >> 11) + 9; + const int bx_wxv = (FFABS(b) >> 11) + 9; + const int bx_hxv = (FFABS(c) >> 11) + 9; + if (bx_wxh * bx_hxh <= 165 && bx_wxv * bx_hxv <= 165) + return 0; + } + return 1; +} + +static void init_subblock_params(SubblockParams *sp, const MotionInfo* mi, + const int cb_width, const int cb_height, const int lx) +{ + const int log2_cbw = av_log2(cb_width); + const int log2_cbh = av_log2(cb_height); + const Mv* cp_mv = mi->mv[lx]; + const int num_cp_mv = mi->motion_model_idc + 1; + sp->d_hor_x = (cp_mv[1].x - cp_mv[0].x) * (1 << (MAX_CU_DEPTH - log2_cbw)); + sp->d_ver_x = (cp_mv[1].y - cp_mv[0].y) * (1 << (MAX_CU_DEPTH - log2_cbw)); + if (num_cp_mv == 3) { + sp->d_hor_y = (cp_mv[2].x - cp_mv[0].x) * (1 << (MAX_CU_DEPTH - log2_cbh)); + sp->d_ver_y = (cp_mv[2].y - cp_mv[0].y) * (1 << (MAX_CU_DEPTH - log2_cbh)); + } else { + sp->d_hor_y = -sp->d_ver_x; + sp->d_ver_y = sp->d_hor_x; + } + sp->mv_scale_hor = (cp_mv[0].x) * (1 << MAX_CU_DEPTH); + sp->mv_scale_ver = (cp_mv[0].y) * (1 << MAX_CU_DEPTH); + sp->cb_width = cb_width; + sp->cb_height = cb_height; + sp->is_fallback = is_fallback_mode(sp, mi->pred_flag); +} + +static void derive_subblock_diff_mvs(const VVCLocalContext *lc, PredictionUnit* pu, const SubblockParams* sp, const int lx) +{ + pu->cb_prof_flag[lx] = derive_cb_prof_flag_lx(lc, pu, lx, sp->is_fallback); + if (pu->cb_prof_flag[lx]) { + const int dmv_limit = 1 << 5; + const int pos_offset_x = 6 * (sp->d_hor_x + sp->d_hor_y); + const int pos_offset_y = 6 * (sp->d_ver_x + sp->d_ver_y); + for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) { + for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) { + LOCAL_ALIGNED_8(Mv, diff, [1]); + diff->x = x * (sp->d_hor_x * (1 << 2)) + y * (sp->d_hor_y * (1 << 2)) - pos_offset_x; + diff->y = x * (sp->d_ver_x * (1 << 2)) + y * (sp->d_ver_y * (1 << 2)) - pos_offset_y; + ff_vvc_round_mv(diff, 0, 8); + pu->diff_mv_x[lx][AFFINE_MIN_BLOCK_SIZE * y + x] = av_clip(diff->x, -dmv_limit + 1, dmv_limit - 1); + pu->diff_mv_y[lx][AFFINE_MIN_BLOCK_SIZE * y + x] = av_clip(diff->y, -dmv_limit + 1, dmv_limit - 1); + } + } + } +} + +static void store_cp_mv(const VVCLocalContext *lc, const MotionInfo *mi, const int lx) +{ + VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const int log2_min_cb_size = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_size = fc->ps.sps->min_cb_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int num_cp_mv = mi->motion_model_idc + 1; + + for (int dy = 0; dy < cu->cb_height; dy += min_cb_size) { + for (int dx = 0; dx < cu->cb_width; dx += min_cb_size) { + const int x_cb = (cu->x0 + dx) >> log2_min_cb_size; + const int y_cb = (cu->y0 + dy) >> log2_min_cb_size; + const int offset = (y_cb * min_cb_width + x_cb) * MAX_CONTROL_POINTS; + + memcpy(&fc->tab.cp_mv[lx][offset], mi->mv[lx], sizeof(Mv) * num_cp_mv); + SAMPLE_CTB(fc->tab.mmi, x_cb, y_cb) = mi->motion_model_idc; + } + } +} + +//8.5.5.9 Derivation process for motion vector arrays from affine control point motion vectors +void ff_vvc_store_sb_mvs(const VVCLocalContext *lc, PredictionUnit *pu) +{ + const CodingUnit *cu = lc->cu; + const MotionInfo *mi = &pu->mi; + const int sbw = cu->cb_width / mi->num_sb_x; + const int sbh = cu->cb_height / mi->num_sb_y; + SubblockParams params[2]; + MvField mvf = {0}; + + mvf.pred_flag = mi->pred_flag; + mvf.bcw_idx = mi->bcw_idx; + mvf.hpel_if_idx = mi->hpel_if_idx; + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + 1; + if (mi->pred_flag & mask) { + store_cp_mv(lc, mi, i); + init_subblock_params(params + i, mi, cu->cb_width, cu->cb_height, i); + derive_subblock_diff_mvs(lc, pu, params + i, i); + mvf.ref_idx[i] = mi->ref_idx[i]; + } + } + + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + const int x0 = cu->x0 + sbx * sbw; + const int y0 = cu->y0 + sby * sbh; + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + 1; + if (mi->pred_flag & mask) { + const SubblockParams* sp = params + i; + const int x_pos_cb = sp->is_fallback ? (cu->cb_width >> 1) : (2 + (sbx << MIN_CU_LOG2)); + const int y_pos_cb = sp->is_fallback ? (cu->cb_height >> 1) : (2 + (sby << MIN_CU_LOG2)); + Mv *mv = mvf.mv + i; + + mv->x = sp->mv_scale_hor + sp->d_hor_x * x_pos_cb + sp->d_hor_y * y_pos_cb; + mv->y = sp->mv_scale_ver + sp->d_ver_x * x_pos_cb + sp->d_ver_y * y_pos_cb; + ff_vvc_round_mv(mv, 0, MAX_CU_DEPTH); + ff_vvc_clip_mv(mv); + } + } + ff_vvc_set_mvf(lc, x0, y0, sbw, sbh, &mvf); + } + } +} + +void ff_vvc_store_gpm_mvf(const VVCLocalContext *lc, const PredictionUnit *pu) +{ + const CodingUnit *cu = lc->cu; + const int angle_idx = ff_vvc_gpm_angle_idx[pu->gpm_partition_idx]; + const int distance_idx = ff_vvc_gpm_distance_idx[pu->gpm_partition_idx]; + const int displacement_x = ff_vvc_gpm_distance_lut[angle_idx]; + const int displacement_y = ff_vvc_gpm_distance_lut[(angle_idx + 8) % 32]; + const int is_flip = angle_idx >= 13 &&angle_idx <= 27; + const int shift_hor = (angle_idx % 16 == 8 || (angle_idx % 16 && cu->cb_height >= cu->cb_width)) ? 0 : 1; + const int sign = angle_idx < 16 ? 1 : -1; + const int block_size = 4; + int offset_x = (-cu->cb_width) >> 1; + int offset_y = (-cu->cb_height) >> 1; + + if (!shift_hor) + offset_y += sign * ((distance_idx * cu->cb_height) >> 3); + else + offset_x += sign * ((distance_idx * cu->cb_width) >> 3); + + for (int y = 0; y < cu->cb_height; y += block_size) { + for (int x = 0; x < cu->cb_width; x += block_size) { + const int motion_idx = (((x + offset_x) * (1 << 1)) + 5) * displacement_x + + (((y + offset_y) * (1 << 1)) + 5) * displacement_y; + const int s_type = FFABS(motion_idx) < 32 ? 2 : (motion_idx <= 0 ? (1 - is_flip) : is_flip); + const int pred_flag = pu->gpm_mv[0].pred_flag | pu->gpm_mv[1].pred_flag; + const int x0 = cu->x0 + x; + const int y0 = cu->y0 + y; + + if (!s_type) + ff_vvc_set_mvf(lc, x0, y0, block_size, block_size, pu->gpm_mv + 0); + else if (s_type == 1 || (s_type == 2 && pred_flag != PF_BI)) + ff_vvc_set_mvf(lc, x0, y0, block_size, block_size, pu->gpm_mv + 1); + else { + MvField mvf = pu->gpm_mv[0]; + const MvField *mv1 = &pu->gpm_mv[1]; + const int lx = mv1->pred_flag - PF_L0; + mvf.pred_flag = PF_BI; + mvf.ref_idx[lx] = mv1->ref_idx[lx]; + mvf.mv[lx] = mv1->mv[lx]; + ff_vvc_set_mvf(lc, x0, y0, block_size, block_size, &mvf); + } + } + } +} + +void ff_vvc_store_mvf(const VVCLocalContext *lc, const MvField *mvf) +{ + const CodingUnit *cu = lc->cu; + ff_vvc_set_mvf(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height, mvf); +} + +void ff_vvc_store_mv(const VVCLocalContext *lc, const MotionInfo *mi) +{ + const CodingUnit *cu = lc->cu; + MvField mvf = {0}; + + mvf.hpel_if_idx = mi->hpel_if_idx; + mvf.bcw_idx = mi->bcw_idx; + mvf.pred_flag = mi->pred_flag; + + for (int i = 0; i < 2; i++) { + const PredFlag mask = i + 1; + if (mvf.pred_flag & mask) { + mvf.mv[i] = mi->mv[i][0]; + mvf.ref_idx[i] = mi->ref_idx[i]; + } + } + ff_vvc_set_mvf(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height, &mvf); +} + +typedef enum NeighbourIdx { + A0, + A1, + A2, + B0, + B1, + B2, + B3, + NUM_NBS, + NB_IDX_NONE = NUM_NBS, +} NeighbourIdx; + +typedef struct Neighbour { + int x; + int y; + + int checked; + int available; +} Neighbour; + +typedef struct NeighbourContext { + Neighbour neighbours[NUM_NBS]; + const VVCLocalContext *lc; +} NeighbourContext; + +static int is_a0_available(const VVCLocalContext *lc, const CodingUnit *cu) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const int x0b = av_mod_uintp2(cu->x0, sps->ctb_log2_size_y); + int cand_bottom_left; + + if (!x0b && !lc->ctb_left_flag) { + cand_bottom_left = 0; + } else { + const int log2_min_cb_size = sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x = (cu->x0 - 1) >> log2_min_cb_size; + const int y = (cu->y0 + cu->cb_height) >> log2_min_cb_size; + const int max_y = FFMIN(fc->ps.pps->height, ((cu->y0 >> sps->ctb_log2_size_y) + 1) << sps->ctb_log2_size_y); + if (cu->y0 + cu->cb_height >= max_y) + cand_bottom_left = 0; + else + cand_bottom_left = SAMPLE_CTB(fc->tab.cb_width[0], x, y) != 0; + } + return cand_bottom_left; +} + +static void init_neighbour_context(NeighbourContext *ctx, const VVCLocalContext *lc) +{ + const CodingUnit *cu = lc->cu; + const NeighbourAvailable *na = &lc->na; + const int x0 = cu->x0; + const int y0 = cu->y0; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const int a0_available = is_a0_available(lc, cu); + + Neighbour neighbours[NUM_NBS] = { + { x0 - 1, y0 + cb_height, !a0_available }, //A0 + { x0 - 1, y0 + cb_height - 1, !na->cand_left }, //A1 + { x0 - 1, y0, !na->cand_left }, //A2 + { x0 + cb_width, y0 - 1, !na->cand_up_right }, //B0 + { x0 + cb_width - 1, y0 - 1, !na->cand_up }, //B1 + { x0 - 1, y0 - 1, !na->cand_up_left }, //B2 + { x0, y0 - 1, !na->cand_up }, //B3 + }; + + memcpy(ctx->neighbours, neighbours, sizeof(neighbours)); + ctx->lc = lc; +} + +static av_always_inline PredMode pred_flag_to_mode(PredFlag pred) +{ + return pred == PF_IBC ? MODE_IBC : (pred == PF_INTRA ? MODE_INTRA : MODE_INTER); +} + +static int check_available(Neighbour *n, const VVCLocalContext *lc, const int check_mer) +{ + const VVCFrameContext *fc = lc->fc; + const VVCSPS *sps = fc->ps.sps; + const CodingUnit *cu = lc->cu; + const MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + + if (!n->checked) { + n->checked = 1; + n->available = !sps->r->sps_entropy_coding_sync_enabled_flag || ((n->x >> sps->ctb_log2_size_y) <= (cu->x0 >> sps->ctb_log2_size_y)); + n->available &= cu->pred_mode == pred_flag_to_mode(TAB_MVF(n->x, n->y).pred_flag); + if (check_mer) + n->available &= !is_same_mer(fc, n->x, n->y, cu->x0, cu->y0); + } + return n->available; +} + +static const MvField *mv_merge_candidate(const VVCLocalContext *lc, const int x_cand, const int y_cand) +{ + const VVCFrameContext *fc = lc->fc; + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + const MvField *mvf = &TAB_MVF(x_cand, y_cand); + + return mvf; +} + +static const MvField* mv_merge_from_nb(NeighbourContext *ctx, const NeighbourIdx nb) +{ + const VVCLocalContext *lc = ctx->lc; + Neighbour *n = &ctx->neighbours[nb]; + + if (check_available(n, lc, 1)) + return mv_merge_candidate(lc, n->x, n->y); + return 0; +} +#define MV_MERGE_FROM_NB(nb) mv_merge_from_nb(&nctx, nb) + +//8.5.2.3 Derivation process for spatial merging candidates +static int mv_merge_spatial_candidates(const VVCLocalContext *lc, const int merge_idx, + const MvField **nb_list, MvField *cand_list, int *nb_merge_cand) +{ + const MvField *cand; + int num_cands = 0; + NeighbourContext nctx; + + static NeighbourIdx nbs[][2] = { + {B1, NB_IDX_NONE }, + {A1, B1 }, + {B0, B1 }, + {A0, A1 }, + }; + + init_neighbour_context(&nctx, lc); + for (int i = 0; i < FF_ARRAY_ELEMS(nbs); i++) { + NeighbourIdx nb = nbs[i][0]; + NeighbourIdx old = nbs[i][1]; + cand = nb_list[nb] = MV_MERGE_FROM_NB(nb); + if (cand && !compare_mv_ref_idx(cand, nb_list[old])) { + cand_list[num_cands] = *cand; + if (merge_idx == num_cands) + return 1; + num_cands++; + } + } + if (num_cands != 4) { + cand = MV_MERGE_FROM_NB(B2); + if (cand && !compare_mv_ref_idx(cand, nb_list[A1]) + && !compare_mv_ref_idx(cand, nb_list[B1])) { + cand_list[num_cands] = *cand; + if (merge_idx == num_cands) + return 1; + num_cands++; + } + } + *nb_merge_cand = num_cands; + return 0; +} + +static int mv_merge_temporal_candidate(const VVCLocalContext *lc, MvField *cand) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + + memset(cand, 0, sizeof(*cand)); + if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag && (cu->cb_width * cu->cb_height > 32)) { + int available_l0 = temporal_luma_motion_vector(lc, 0, cand->mv + 0, 0, 1, 0); + int available_l1 = IS_B(lc->sc->sh.r) ? + temporal_luma_motion_vector(lc, 0, cand->mv + 1, 1, 1, 0) : 0; + cand->pred_flag = available_l0 + (available_l1 << 1); + } + return cand->pred_flag; +} + +//8.5.2.6 Derivation process for history-based merging candidates +static int mv_merge_history_candidates(const VVCLocalContext *lc, const int merge_idx, + const MvField **nb_list, MvField *cand_list, int *num_cands) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const EntryPoint* ep = lc->ep; + for (int i = 1; i <= ep->num_hmvp && (*num_cands < sps->max_num_merge_cand - 1); i++) { + const MvField *h = &ep->hmvp[ep->num_hmvp - i]; + const int same_motion = i <= 2 && (compare_mv_ref_idx(h, nb_list[A1]) || compare_mv_ref_idx(h, nb_list[B1])); + if (!same_motion) { + cand_list[*num_cands] = *h; + if (merge_idx == *num_cands) + return 1; + (*num_cands)++; + } + } + return 0; +} + +//8.5.2.4 Derivation process for pairwise average merging candidate +static int mv_merge_pairwise_candidate(MvField *cand_list, const int num_cands, const int is_b) +{ + if (num_cands > 1) { + const int num_ref_rists = is_b ? 2 : 1; + const MvField* p0 = cand_list + 0; + const MvField* p1 = cand_list + 1; + MvField* cand = cand_list + num_cands; + + cand->pred_flag = 0; + for (int i = 0; i < num_ref_rists; i++) { + PredFlag mask = i + 1; + if (p0->pred_flag & mask) { + cand->pred_flag |= mask; + cand->ref_idx[i] = p0->ref_idx[i]; + if (p1->pred_flag & mask) { + Mv *mv = cand->mv + i; + mv->x = p0->mv[i].x + p1->mv[i].x; + mv->y = p0->mv[i].y + p1->mv[i].y; + ff_vvc_round_mv(mv, 0, 1); + } else { + cand->mv[i] = p0->mv[i]; + } + } else if (p1->pred_flag & mask) { + cand->pred_flag |= mask; + cand->mv[i] = p1->mv[i]; + cand->ref_idx[i] = p1->ref_idx[i]; + } + } + if (cand->pred_flag) { + cand->hpel_if_idx = p0->hpel_if_idx == p1->hpel_if_idx ? p0->hpel_if_idx : 0; + cand->bcw_idx = 0; + cand->ciip_flag = 0; + return 1; + } + } + return 0; +} + +//8.5.2.5 Derivation process for zero motion vector merging candidates +static void mv_merge_zero_motion_candidate(const VVCLocalContext *lc, const int merge_idx, + MvField *cand_list, int num_cands) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const H266RawSliceHeader *rsh = lc->sc->sh.r; + const int num_ref_idx = IS_P(rsh) ? + rsh->num_ref_idx_active[L0] : FFMIN(rsh->num_ref_idx_active[L0], rsh->num_ref_idx_active[L1]); + int zero_idx = 0; + + while (num_cands < sps->max_num_merge_cand) { + MvField *cand = cand_list + num_cands; + + cand->pred_flag = PF_L0 + (IS_B(rsh) << 1); + AV_ZERO64(cand->mv + 0); + AV_ZERO64(cand->mv + 1); + cand->ref_idx[0] = zero_idx < num_ref_idx ? zero_idx : 0; + cand->ref_idx[1] = zero_idx < num_ref_idx ? zero_idx : 0; + cand->bcw_idx = 0; + cand->hpel_if_idx = 0; + if (merge_idx == num_cands) + return; + num_cands++; + zero_idx++; + } +} + +static void mv_merge_mode(const VVCLocalContext *lc, const int merge_idx, MvField *cand_list) +{ + int num_cands = 0; + const MvField *nb_list[NUM_NBS + 1] = { NULL }; + + if (mv_merge_spatial_candidates(lc, merge_idx, nb_list, cand_list, &num_cands)) + return; + + if (mv_merge_temporal_candidate(lc, &cand_list[num_cands])) { + if (merge_idx == num_cands) + return; + num_cands++; + } + + if (mv_merge_history_candidates(lc, merge_idx, nb_list, cand_list, &num_cands)) + return; + + if (mv_merge_pairwise_candidate(cand_list, num_cands, IS_B(lc->sc->sh.r))) { + if (merge_idx == num_cands) + return; + num_cands++; + } + + mv_merge_zero_motion_candidate(lc, merge_idx, cand_list, num_cands); +} + +//8.5.2.2 Derivation process for luma motion vectors for merge mode +void ff_vvc_luma_mv_merge_mode(VVCLocalContext *lc, const int merge_idx, const int ciip_flag, MvField *mv) +{ + const CodingUnit *cu = lc->cu; + MvField cand_list[MRG_MAX_NUM_CANDS]; + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + mv_merge_mode(lc, merge_idx, cand_list); + *mv = cand_list[merge_idx]; + //ciip flag in not inhritable + mv->ciip_flag = ciip_flag; +} + +//8.5.4.2 Derivation process for luma motion vectors for geometric partitioning merge mode +void ff_vvc_luma_mv_merge_gpm(VVCLocalContext *lc, const int merge_gpm_idx[2], MvField *mv) +{ + const CodingUnit *cu = lc->cu; + MvField cand_list[MRG_MAX_NUM_CANDS]; + + const int idx[] = { merge_gpm_idx[0], merge_gpm_idx[1] + (merge_gpm_idx[1] >= merge_gpm_idx[0]) }; + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + mv_merge_mode(lc, FFMAX(idx[0], idx[1]), cand_list); + memset(mv, 0, 2 * sizeof(*mv)); + for (int i = 0; i < 2; i++) { + int lx = idx[i] & 1; + int mask = lx + PF_L0; + MvField *cand = cand_list + idx[i]; + if (!(cand->pred_flag & mask)) { + lx = !lx; + mask = lx + PF_L0; + } + mv[i].pred_flag = mask; + mv[i].ref_idx[lx] = cand->ref_idx[lx]; + mv[i].mv[lx] = cand->mv[lx]; + } + +} + +//8.5.5.5 Derivation process for luma affine control point motion vectors from a neighbouring block +static void affine_cps_from_nb(const VVCLocalContext *lc, + const int x_nb, int y_nb, const int nbw, const int nbh, const int lx, + Mv *cps, int num_cps) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const int x0 = cu->x0; + const int y0 = cu->y0; + const int cb_width = cu->cb_width; + const int cb_height = cu->cb_height; + const MvField* tab_mvf = fc->tab.mvf; + const int min_cb_log2_size = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + + const int log2_nbw = ff_log2(nbw); + const int log2_nbh = ff_log2(nbh); + const int is_ctb_boundary = !((y_nb + nbh) % fc->ps.sps->ctb_size_y) && (y_nb + nbh == y0); + const Mv *l, *r; + int mv_scale_hor, mv_scale_ver, d_hor_x, d_ver_x, d_hor_y, d_ver_y, motion_model_idc_nb; + if (is_ctb_boundary) { + const int min_pu_width = fc->ps.pps->min_pu_width; + l = &TAB_MVF(x_nb, y_nb + nbh - 1).mv[lx]; + r = &TAB_MVF(x_nb + nbw - 1, y_nb + nbh - 1).mv[lx]; + } else { + const int x = x_nb >> min_cb_log2_size; + const int y = y_nb >> min_cb_log2_size; + motion_model_idc_nb = SAMPLE_CTB(fc->tab.mmi, x, y); + + l = &TAB_CP_MV(lx, x_nb, y_nb); + r = &TAB_CP_MV(lx, x_nb + nbw - 1, y_nb) + 1; + } + mv_scale_hor = l->x * (1 << 7); + mv_scale_ver = l->y * (1 << 7); + d_hor_x = (r->x - l->x) * (1 << (7 - log2_nbw)); + d_ver_x = (r->y - l->y) * (1 << (7 - log2_nbw)); + if (!is_ctb_boundary && motion_model_idc_nb == MOTION_6_PARAMS_AFFINE) { + const Mv* lb = &TAB_CP_MV(lx, x_nb, y_nb + nbh - 1) + 2; + d_hor_y = (lb->x - l->x) * (1 << (7 - log2_nbh)); + d_ver_y = (lb->y - l->y) * (1 << (7 - log2_nbh)); + } else { + d_hor_y = -d_ver_x; + d_ver_y = d_hor_x; + } + + if (is_ctb_boundary) { + y_nb = y0; + } + cps[0].x = mv_scale_hor + d_hor_x * (x0 - x_nb) + d_hor_y * (y0 - y_nb); + cps[0].y = mv_scale_ver + d_ver_x * (x0 - x_nb) + d_ver_y * (y0 - y_nb); + cps[1].x = mv_scale_hor + d_hor_x * (x0 + cb_width - x_nb) + d_hor_y * (y0 - y_nb); + cps[1].y = mv_scale_ver + d_ver_x * (x0 + cb_width - x_nb) + d_ver_y * (y0 - y_nb); + if (num_cps == 3) { + cps[2].x = mv_scale_hor + d_hor_x * (x0 - x_nb) + d_hor_y * (y0 + cb_height - y_nb); + cps[2].y = mv_scale_ver + d_ver_x * (x0 - x_nb) + d_ver_y * (y0 + cb_height - y_nb); + } + for (int i = 0; i < num_cps; i++) { + ff_vvc_round_mv(cps + i, 0, 7); + ff_vvc_clip_mv(cps + i); + } +} + +//derive affine neighbour's postion, width and height, +static int affine_neighbour_cb(const VVCFrameContext *fc, const int x_nb, const int y_nb, int *x_cb, int *y_cb, int *cbw, int *cbh) +{ + const int log2_min_cb_size = fc->ps.sps->min_cb_log2_size_y; + const int min_cb_width = fc->ps.pps->min_cb_width; + const int x = x_nb >> log2_min_cb_size; + const int y = y_nb >> log2_min_cb_size; + const int motion_model_idc = SAMPLE_CTB(fc->tab.mmi, x, y); + if (motion_model_idc) { + *x_cb = SAMPLE_CTB(fc->tab.cb_pos_x[0], x, y); + *y_cb = SAMPLE_CTB(fc->tab.cb_pos_y[0], x, y); + *cbw = SAMPLE_CTB(fc->tab.cb_width[0], x, y); + *cbh = SAMPLE_CTB(fc->tab.cb_height[0], x, y); + } + return motion_model_idc; +} + +//part of 8.5.5.2 Derivation process for motion vectors and reference indices in subblock merge mode +static int affine_merge_candidate(const VVCLocalContext *lc, const int x_cand, const int y_cand, MotionInfo* mi) +{ + const VVCFrameContext *fc = lc->fc; + int x, y, w, h, motion_model_idc; + + motion_model_idc = affine_neighbour_cb(fc, x_cand, y_cand, &x, &y, &w, &h); + if (motion_model_idc) { + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + const MvField *mvf = &TAB_MVF(x, y); + + mi->bcw_idx = mvf->bcw_idx; + mi->pred_flag = mvf->pred_flag; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (mi->pred_flag & mask) { + affine_cps_from_nb(lc, x, y, w, h, i, &mi->mv[i][0], motion_model_idc + 1); + } + mi->ref_idx[i] = mvf->ref_idx[i]; + } + mi->motion_model_idc = motion_model_idc; + } + return motion_model_idc; +} + +static int affine_merge_from_nbs(NeighbourContext *ctx, const NeighbourIdx *nbs, const int num_nbs, MotionInfo* cand) +{ + const VVCLocalContext *lc = ctx->lc; + for (int i = 0; i < num_nbs; i++) { + Neighbour *n = &ctx->neighbours[nbs[i]]; + if (check_available(n, lc, 1) && affine_merge_candidate(lc, n->x, n->y, cand)) + return 1; + } + return 0; +} +#define AFFINE_MERGE_FROM_NBS(nbs) affine_merge_from_nbs(&nctx, nbs, FF_ARRAY_ELEMS(nbs), mi) + + +static const MvField* derive_corner_mvf(NeighbourContext *ctx, const NeighbourIdx *neighbour, const int num_neighbour) +{ + const VVCFrameContext *fc = ctx->lc->fc; + const MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + for (int i = 0; i < num_neighbour; i++) { + Neighbour *n = &ctx->neighbours[neighbour[i]]; + if (check_available(n, ctx->lc, 1)) { + return &TAB_MVF(n->x, n->y); + } + } + return NULL; +} + +#define DERIVE_CORNER_MV(nbs) derive_corner_mvf(nctx, nbs, FF_ARRAY_ELEMS(nbs)) + +// check if the mv's and refidx are the same between A and B +static av_always_inline int compare_pf_ref_idx(const MvField *A, const struct MvField *B, const struct MvField *C, const int lx) +{ + + const PredFlag mask = (lx + 1) & A->pred_flag; + if (!(B->pred_flag & mask)) + return 0; + if (A->ref_idx[lx] != B->ref_idx[lx]) + return 0; + if (C) { + if (!(C->pred_flag & mask)) + return 0; + if (A->ref_idx[lx] != C->ref_idx[lx]) + return 0; + } + return 1; +} + +static av_always_inline void sb_clip_location(const VVCFrameContext *fc, + const int x_ctb, const int y_ctb, const Mv* temp_mv, int *x, int *y) +{ + const VVCPPS *pps = fc->ps.pps; + const int ctb_log2_size = fc->ps.sps->ctb_log2_size_y; + *y = av_clip(*y + temp_mv->y, y_ctb, FFMIN(pps->height - 1, y_ctb + (1 << ctb_log2_size) - 1)) & ~7; + *x = av_clip(*x + temp_mv->x, x_ctb, FFMIN(pps->width - 1, x_ctb + (1 << ctb_log2_size) + 3)) & ~7; +} + +static void sb_temproal_luma_motion(const VVCLocalContext *lc, + const int x_ctb, const int y_ctb, const Mv *temp_mv, + int x, int y, uint8_t *pred_flag, Mv *mv) +{ + MvField temp_col; + Mv* mvLXCol; + const int refIdxLx = 0; + const VVCFrameContext *fc = lc->fc; + const VVCSH *sh = &lc->sc->sh; + const int min_pu_width = fc->ps.pps->min_pu_width; + VVCFrame *ref = fc->ref->collocated_ref; + MvField *tab_mvf = ref->tab_dmvr_mvf; + int colPic = ref->poc; + int X = 0; + + sb_clip_location(fc, x_ctb, y_ctb, temp_mv, &x, &y); + + temp_col = TAB_MVF(x, y); + mvLXCol = mv + 0; + *pred_flag = DERIVE_TEMPORAL_COLOCATED_MVS(1); + if (IS_B(sh->r)) { + X = 1; + mvLXCol = mv + 1; + *pred_flag |= (DERIVE_TEMPORAL_COLOCATED_MVS(1)) << 1; + } +} + +//8.5.5.4 Derivation process for subblock-based temporal merging base motion data +static int sb_temporal_luma_motion_data(const VVCLocalContext *lc, const MvField *a1, + const int x_ctb, const int y_ctb, MvField *ctr_mvf, Mv *temp_mv) +{ + const VVCFrameContext *fc = lc->fc; + const RefPicList *rpl = lc->sc->rpl; + const CodingUnit *cu = lc->cu; + const int x = cu->x0 + cu->cb_width / 2; + const int y = cu->y0 + cu->cb_height / 2; + const VVCFrame *ref = fc->ref->collocated_ref; + + int colPic; + + memset(temp_mv, 0, sizeof(*temp_mv)); + + if (!ref) { + memset(ctr_mvf, 0, sizeof(*ctr_mvf)); + return 0; + } + + colPic = ref->poc; + + if (a1) { + if ((a1->pred_flag & PF_L0) && colPic == rpl[0].list[a1->ref_idx[0]]) + *temp_mv = a1->mv[0]; + else if ((a1->pred_flag & PF_L1) && colPic == rpl[1].list[a1->ref_idx[1]]) + *temp_mv = a1->mv[1]; + ff_vvc_round_mv(temp_mv, 0, 4); + } + sb_temproal_luma_motion(lc, x_ctb, y_ctb, temp_mv, x, y, &ctr_mvf->pred_flag , ctr_mvf->mv); + + return ctr_mvf->pred_flag; +} + + +//8.5.5.3 Derivation process for subblock-based temporal merging candidates +static int sb_temporal_merge_candidate(const VVCLocalContext* lc, NeighbourContext *nctx, PredictionUnit *pu) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const VVCSPS *sps = fc->ps.sps; + const VVCPH *ph = &fc->ps.ph; + MotionInfo *mi = &pu->mi; + const int ctb_log2_size = sps->ctb_log2_size_y; + const int x0 = cu->x0; + const int y0 = cu->y0; + const NeighbourIdx n = A1; + const MvField *a1; + MvField ctr_mvf; + LOCAL_ALIGNED_8(Mv, temp_mv, [1]); + const int x_ctb = (x0 >> ctb_log2_size) << ctb_log2_size; + const int y_ctb = (y0 >> ctb_log2_size) << ctb_log2_size; + + + if (!ph->r->ph_temporal_mvp_enabled_flag || + !sps->r->sps_sbtmvp_enabled_flag || + (cu->cb_width < 8 && cu->cb_height < 8)) + return 0; + + mi->num_sb_x = cu->cb_width >> 3; + mi->num_sb_y = cu->cb_height >> 3; + + a1 = derive_corner_mvf(nctx, &n, 1); + if (sb_temporal_luma_motion_data(lc, a1, x_ctb, y_ctb, &ctr_mvf, temp_mv)) { + const int sbw = cu->cb_width / mi->num_sb_x; + const int sbh = cu->cb_height / mi->num_sb_y; + MvField mvf = {0}; + for (int sby = 0; sby < mi->num_sb_y; sby++) { + for (int sbx = 0; sbx < mi->num_sb_x; sbx++) { + int x = x0 + sbx * sbw; + int y = y0 + sby * sbh; + sb_temproal_luma_motion(lc, x_ctb, y_ctb, temp_mv, x + sbw / 2, y + sbh / 2, &mvf.pred_flag, mvf.mv); + if (!mvf.pred_flag) { + mvf.pred_flag = ctr_mvf.pred_flag; + memcpy(mvf.mv, ctr_mvf.mv, sizeof(mvf.mv)); + } + ff_vvc_set_mvf(lc, x, y, sbw, sbh, &mvf); + } + } + return 1; + } + return 0; +} + +static int affine_merge_const1(const MvField *c0, const MvField *c1, const MvField *c2, MotionInfo *mi) +{ + if (c0 && c1 && c2) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c1, c2, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1] = c1->mv[i]; + mi->mv[i][2] = c2->mv[i]; + } + } + if (mi->pred_flag) { + if (mi->pred_flag == PF_BI) + mi->bcw_idx = c0->bcw_idx; + mi->motion_model_idc = MOTION_6_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static int affine_merge_const2(const MvField *c0, const MvField *c1, const MvField *c3, MotionInfo *mi) +{ + if (c0 && c1 && c3) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c1, c3, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1] = c1->mv[i]; + mi->mv[i][2].x = c3->mv[i].x + c0->mv[i].x - c1->mv[i].x; + mi->mv[i][2].y = c3->mv[i].y + c0->mv[i].y - c1->mv[i].y; + ff_vvc_clip_mv(&mi->mv[i][2]); + } + } + if (mi->pred_flag) { + mi->bcw_idx = mi->pred_flag == PF_BI ? c0->bcw_idx : 0; + mi->motion_model_idc = MOTION_6_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static int affine_merge_const3(const MvField *c0, const MvField *c2, const MvField *c3, MotionInfo *mi) +{ + if (c0 && c2 && c3) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c2, c3, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1].x = c3->mv[i].x + c0->mv[i].x - c2->mv[i].x; + mi->mv[i][1].y = c3->mv[i].y + c0->mv[i].y - c2->mv[i].y; + ff_vvc_clip_mv(&mi->mv[i][1]); + mi->mv[i][2] = c2->mv[i]; + } + } + if (mi->pred_flag) { + mi->bcw_idx = mi->pred_flag == PF_BI ? c0->bcw_idx : 0; + mi->motion_model_idc = MOTION_6_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static int affine_merge_const4(const MvField *c1, const MvField *c2, const MvField *c3, MotionInfo *mi) +{ + if (c1 && c2 && c3) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c1, c2, c3, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c1->ref_idx[i]; + mi->mv[i][0].x = c1->mv[i].x + c2->mv[i].x - c3->mv[i].x; + mi->mv[i][0].y = c1->mv[i].y + c2->mv[i].y - c3->mv[i].y; + ff_vvc_clip_mv(&mi->mv[i][0]); + mi->mv[i][1] = c1->mv[i]; + mi->mv[i][2] = c2->mv[i]; + } + } + if (mi->pred_flag) { + mi->bcw_idx = mi->pred_flag == PF_BI ? c1->bcw_idx : 0; + mi->motion_model_idc = MOTION_6_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static int affine_merge_const5(const MvField *c0, const MvField *c1, MotionInfo *mi) +{ + if (c0 && c1) { + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c1, NULL, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1] = c1->mv[i]; + } + } + if (mi->pred_flag) { + if (mi->pred_flag == PF_BI) + mi->bcw_idx = c0->bcw_idx; + mi->motion_model_idc = MOTION_4_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static int affine_merge_const6(const MvField* c0, const MvField* c2, const int cb_width, const int cb_height, MotionInfo *mi) +{ + if (c0 && c2) { + const int shift = 7 + av_log2(cb_width) - av_log2(cb_height); + mi->pred_flag = 0; + for (int i = 0; i < 2; i++) { + PredFlag mask = i + 1; + if (compare_pf_ref_idx(c0, c2, NULL, i)) { + mi->pred_flag |= mask; + mi->ref_idx[i] = c0->ref_idx[i]; + mi->mv[i][0] = c0->mv[i]; + mi->mv[i][1].x = (c0->mv[i].x * (1 << 7)) + ((c2->mv[i].y - c0->mv[i].y) * (1 << shift)); + mi->mv[i][1].y = (c0->mv[i].y * (1 << 7)) - ((c2->mv[i].x - c0->mv[i].x) * (1 << shift)); + ff_vvc_round_mv(&mi->mv[i][1], 0, 7); + ff_vvc_clip_mv(&mi->mv[i][1]); + } + } + if (mi->pred_flag) { + if (mi->pred_flag == PF_BI) + mi->bcw_idx = c0->bcw_idx; + mi->motion_model_idc = MOTION_4_PARAMS_AFFINE; + return 1; + } + } + return 0; +} + +static void affine_merge_zero_motion(const VVCLocalContext *lc, MotionInfo *mi) +{ + const CodingUnit *cu = lc->cu; + + memset(mi, 0, sizeof(*mi)); + mi->pred_flag = PF_L0 + (IS_B(lc->sc->sh.r) << 1); + mi->motion_model_idc = MOTION_4_PARAMS_AFFINE; + mi->num_sb_x = cu->cb_width >> MIN_PU_LOG2; + mi->num_sb_y = cu->cb_height >> MIN_PU_LOG2; +} + +//8.5.5.6 Derivation process for constructed affine control point motion vector merging candidates +static int affine_merge_const_candidates(const VVCLocalContext *lc, MotionInfo *mi, + NeighbourContext *nctx, const int merge_subblock_idx, int num_cands) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const NeighbourIdx tl[] = { B2, B3, A2 }; + const NeighbourIdx tr[] = { B1, B0}; + const NeighbourIdx bl[] = { A1, A0}; + const MvField *c0, *c1, *c2; + + c0 = DERIVE_CORNER_MV(tl); + c1 = DERIVE_CORNER_MV(tr); + c2 = DERIVE_CORNER_MV(bl); + + if (fc->ps.sps->r->sps_6param_affine_enabled_flag) { + MvField corner3, *c3 = NULL; + //Const1 + if (affine_merge_const1(c0, c1, c2, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + memset(&corner3, 0, sizeof(corner3)); + if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag){ + const int available_l0 = temporal_luma_motion_vector(lc, 0, corner3.mv + 0, 0, 0, 0); + const int available_l1 = (lc->sc->sh.r->sh_slice_type == VVC_SLICE_TYPE_B) ? + temporal_luma_motion_vector(lc, 0, corner3.mv + 1, 1, 0, 0) : 0; + + corner3.pred_flag = available_l0 + (available_l1 << 1); + if (corner3.pred_flag) + c3 = &corner3; + } + + //Const2 + if (affine_merge_const2(c0, c1, c3, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + //Const3 + if (affine_merge_const3(c0, c2, c3, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + //Const4 + if (affine_merge_const4(c1, c2, c3, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + } + + //Const5 + if (affine_merge_const5(c0, c1, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + if (affine_merge_const6(c0, c2, cu->cb_width, cu->cb_height, mi)) { + if (merge_subblock_idx == num_cands) + return 1; + } + return 0; +} + +//8.5.5.2 Derivation process for motion vectors and reference indices in subblock merge mode +//return 1 if candidate is SbCol +static int sb_mv_merge_mode(const VVCLocalContext *lc, const int merge_subblock_idx, PredictionUnit *pu) +{ + const VVCSPS *sps = lc->fc->ps.sps; + const CodingUnit *cu = lc->cu; + MotionInfo *mi = &pu->mi; + int num_cands = 0; + NeighbourContext nctx; + + init_neighbour_context(&nctx, lc); + + //SbCol + if (sb_temporal_merge_candidate(lc, &nctx, pu)) { + if (merge_subblock_idx == num_cands) + return 1; + num_cands++; + } + + pu->inter_affine_flag = 1; + mi->num_sb_x = cu->cb_width >> MIN_PU_LOG2; + mi->num_sb_y = cu->cb_height >> MIN_PU_LOG2; + + if (sps->r->sps_affine_enabled_flag) { + const NeighbourIdx ak[] = { A0, A1 }; + const NeighbourIdx bk[] = { B0, B1, B2 }; + //A + if (AFFINE_MERGE_FROM_NBS(ak)) { + if (merge_subblock_idx == num_cands) + return 0; + num_cands++; + } + + //B + if (AFFINE_MERGE_FROM_NBS(bk)) { + if (merge_subblock_idx == num_cands) + return 0; + num_cands++; + } + + //Const1 to Const6 + if (affine_merge_const_candidates(lc, mi, &nctx, merge_subblock_idx, num_cands)) + return 0; + } + //Zero + affine_merge_zero_motion(lc, mi); + return 0; +} + +void ff_vvc_sb_mv_merge_mode(VVCLocalContext *lc, const int merge_subblock_idx, PredictionUnit *pu) +{ + const CodingUnit *cu = lc->cu; + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (!sb_mv_merge_mode(lc, merge_subblock_idx, pu)) { + ff_vvc_store_sb_mvs(lc, pu); + } +} + +static int mvp_candidate(const VVCLocalContext *lc, const int x_cand, const int y_cand, + const int lx, const int8_t *ref_idx, Mv *mv) +{ + const VVCFrameContext *fc = lc->fc; + const RefPicList *rpl = lc->sc->rpl; + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + const MvField *mvf = &TAB_MVF(x_cand, y_cand); + const PredFlag maskx = lx + 1; + const int poc = rpl[lx].list[ref_idx[lx]]; + int available = 0; + + if ((mvf->pred_flag & maskx) && rpl[lx].list[mvf->ref_idx[lx]] == poc) { + available = 1; + *mv = mvf->mv[lx]; + } else { + const int ly = !lx; + const PredFlag masky = ly + 1; + if ((mvf->pred_flag & masky) && rpl[ly].list[mvf->ref_idx[ly]] == poc) { + available = 1; + *mv = mvf->mv[ly]; + } + } + + return available; +} + +static int affine_mvp_candidate(const VVCLocalContext *lc, + const int x_cand, const int y_cand, const int lx, const int8_t *ref_idx, + Mv *cps, const int num_cp) +{ + const VVCFrameContext *fc = lc->fc; + int x_nb, y_nb, nbw, nbh, motion_model_idc, available = 0; + + motion_model_idc = affine_neighbour_cb(fc, x_cand, y_cand, &x_nb, &y_nb, &nbw, &nbh); + if (motion_model_idc) { + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField* tab_mvf = fc->tab.mvf; + const MvField *mvf = &TAB_MVF(x_nb, y_nb); + RefPicList* rpl = lc->sc->rpl; + const PredFlag maskx = lx + 1; + const int poc = rpl[lx].list[ref_idx[lx]]; + + if ((mvf->pred_flag & maskx) && rpl[lx].list[mvf->ref_idx[lx]] == poc) { + available = 1; + affine_cps_from_nb(lc, x_nb, y_nb, nbw, nbh, lx, cps, num_cp); + } else { + const int ly = !lx; + const PredFlag masky = ly + 1; + if ((mvf->pred_flag & masky) && rpl[ly].list[mvf->ref_idx[ly]] == poc) { + available = 1; + affine_cps_from_nb(lc, x_nb, y_nb, nbw, nbh, ly, cps, num_cp); + } + } + + } + return available; +} + +static int mvp_from_nbs(NeighbourContext *ctx, + const NeighbourIdx *nbs, const int num_nbs, const int lx, const int8_t *ref_idx, const int amvr_shift, + Mv *cps, const int num_cps) +{ + const VVCLocalContext *lc = ctx->lc; + int available = 0; + + for (int i = 0; i < num_nbs; i++) { + Neighbour *n = &ctx->neighbours[nbs[i]]; + if (check_available(n, lc, 0)) { + if (num_cps > 1) + available = affine_mvp_candidate(lc, n->x, n->y, lx, ref_idx, cps, num_cps); + else + available = mvp_candidate(lc, n->x, n->y, lx, ref_idx, cps); + if (available) { + for (int c = 0; c < num_cps; c++) + ff_vvc_round_mv(cps + c, amvr_shift, amvr_shift); + return 1; + } + } + } + return 0; +} + +//get mvp from neighbours +#define AFFINE_MVP_FROM_NBS(nbs) \ + mvp_from_nbs(&nctx, nbs, FF_ARRAY_ELEMS(nbs), lx, ref_idx, amvr_shift, cps, num_cp) \ + +#define MVP_FROM_NBS(nbs) \ + mvp_from_nbs(&nctx, nbs, FF_ARRAY_ELEMS(nbs), lx, ref_idx, amvr_shift, mv, 1) \ + +static int mvp_spatial_candidates(const VVCLocalContext *lc, + const int mvp_lx_flag, const int lx, const int8_t* ref_idx, const int amvr_shift, + Mv* mv, int *nb_merge_cand) +{ + const NeighbourIdx ak[] = { A0, A1 }; + const NeighbourIdx bk[] = { B0, B1, B2 }; + NeighbourContext nctx; + int available_a, num_cands = 0; + LOCAL_ALIGNED_8(Mv, mv_a, [1]); + + init_neighbour_context(&nctx, lc); + + available_a = MVP_FROM_NBS(ak); + if (available_a) { + if (mvp_lx_flag == num_cands) + return 1; + num_cands++; + *mv_a = *mv; + } + if (MVP_FROM_NBS(bk)) { + if (!available_a || !IS_SAME_MV(mv_a, mv)) { + if (mvp_lx_flag == num_cands) + return 1; + num_cands++; + } + } + *nb_merge_cand = num_cands; + return 0; +} + +static int mvp_temporal_candidates(const VVCLocalContext* lc, + const int mvp_lx_flag, const int lx, const int8_t *ref_idx, const int amvr_shift, + Mv* mv, int *num_cands) +{ + if (temporal_luma_motion_vector(lc, ref_idx[lx], mv, lx, 1, 0)) { + if (mvp_lx_flag == *num_cands) { + ff_vvc_round_mv(mv, amvr_shift, amvr_shift); + return 1; + } + (*num_cands)++; + } + return 0; + +} + +static int mvp_history_candidates(const VVCLocalContext *lc, + const int mvp_lx_flag, const int lx, const int8_t ref_idx, const int amvr_shift, + Mv *mv, int num_cands) +{ + const EntryPoint* ep = lc->ep; + const RefPicList* rpl = lc->sc->rpl; + const int poc = rpl[lx].list[ref_idx]; + + if (ep->num_hmvp == 0) + return 0; + for (int i = 1; i <= FFMIN(4, ep->num_hmvp); i++) { + const MvField* h = &ep->hmvp[i - 1]; + for (int j = 0; j < 2; j++) { + const int ly = (j ? !lx : lx); + PredFlag mask = PF_L0 + ly; + if ((h->pred_flag & mask) && poc == rpl[ly].list[h->ref_idx[ly]]) { + if (mvp_lx_flag == num_cands) { + *mv = h->mv[ly]; + ff_vvc_round_mv(mv, amvr_shift, amvr_shift); + return 1; + } + num_cands++; + } + } + } + return 0; +} + +//8.5.2.8 Derivation process for luma motion vector prediction +static void mvp(const VVCLocalContext *lc, const int mvp_lx_flag, const int lx, + const int8_t *ref_idx, const int amvr_shift, Mv *mv) +{ + int num_cands; + + if (mvp_spatial_candidates(lc, mvp_lx_flag, lx, ref_idx, amvr_shift, mv, &num_cands)) + return; + + if (mvp_temporal_candidates(lc, mvp_lx_flag, lx, ref_idx, amvr_shift, mv, &num_cands)) + return; + + if (mvp_history_candidates(lc, mvp_lx_flag, lx, ref_idx[lx], amvr_shift, mv, num_cands)) + return; + + memset(mv, 0, sizeof(*mv)); +} + +void ff_vvc_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo *mi) +{ + const CodingUnit *cu = lc->cu; + mi->num_sb_x = 1; + mi->num_sb_y = 1; + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (mi->pred_flag != PF_L1) + mvp(lc, mvp_lx_flag[L0], L0, mi->ref_idx, amvr_shift, &mi->mv[L0][0]); + if (mi->pred_flag != PF_L0) + mvp(lc, mvp_lx_flag[L1], L1, mi->ref_idx, amvr_shift, &mi->mv[L1][0]); +} + +static int ibc_spatial_candidates(const VVCLocalContext *lc, const int merge_idx, Mv *const cand_list, int *nb_merge_cand) +{ + const CodingUnit *cu = lc->cu; + const VVCFrameContext *fc = lc->fc; + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField *tab_mvf = fc->tab.mvf; + const int is_gt4by4 = (cu->cb_width * cu->cb_height) > 16; + int num_cands = 0; + + NeighbourContext nctx; + Neighbour *a1 = &nctx.neighbours[A1]; + Neighbour *b1 = &nctx.neighbours[B1]; + + if (!is_gt4by4) { + *nb_merge_cand = 0; + return 0; + } + + init_neighbour_context(&nctx, lc); + + if (check_available(a1, lc, 1)) { + cand_list[num_cands++] = TAB_MVF(a1->x, a1->y).mv[L0]; + if (num_cands > merge_idx) + return 1; + } + if (check_available(b1, lc, 1)) { + const MvField *mvf = &TAB_MVF(b1->x, b1->y); + if (!num_cands || !IS_SAME_MV(&cand_list[0], mvf->mv)) { + cand_list[num_cands++] = mvf->mv[L0]; + if (num_cands > merge_idx) + return 1; + } + } + + *nb_merge_cand = num_cands; + return 0; +} + +static int ibc_history_candidates(const VVCLocalContext *lc, + const int merge_idx, Mv *cand_list, int *nb_merge_cand) +{ + const CodingUnit *cu = lc->cu; + const EntryPoint *ep = lc->ep; + const int is_gt4by4 = (cu->cb_width * cu->cb_height) > 16; + int num_cands = *nb_merge_cand; + + for (int i = 1; i <= ep->num_hmvp_ibc; i++) { + int same_motion = 0; + const MvField *mvf = &ep->hmvp_ibc[ep->num_hmvp_ibc - i]; + for (int j = 0; j < *nb_merge_cand; j++) { + same_motion = is_gt4by4 && i == 1 && IS_SAME_MV(&mvf->mv[L0], &cand_list[j]); + if (same_motion) + break; + } + if (!same_motion) { + cand_list[num_cands++] = mvf->mv[L0]; + if (num_cands > merge_idx) + return 1; + } + } + + *nb_merge_cand = num_cands; + return 0; +} + +#define MV_BITS 18 +#define IBC_SHIFT(v) ((v) >= (1 << (MV_BITS - 1)) ? ((v) - (1 << MV_BITS)) : (v)) + +static inline void ibc_add_mvp(Mv *mv, Mv *mvp, const int amvr_shift) +{ + ff_vvc_round_mv(mv, amvr_shift, 0); + ff_vvc_round_mv(mvp, amvr_shift, amvr_shift); + mv->x = IBC_SHIFT(mv->x + mvp->x); + mv->y = IBC_SHIFT(mv->y + mvp->y); +} + +static void ibc_merge_candidates(VVCLocalContext *lc, const int merge_idx, Mv *mv) +{ + const CodingUnit *cu = lc->cu; + LOCAL_ALIGNED_8(Mv, cand_list, [MRG_MAX_NUM_CANDS]); + int nb_cands; + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (ibc_spatial_candidates(lc, merge_idx, cand_list, &nb_cands) || + ibc_history_candidates(lc, merge_idx, cand_list, &nb_cands)) { + *mv = cand_list[merge_idx]; + return; + } + + //zero mv + memset(mv, 0, sizeof(*mv)); +} + +void ff_vvc_mvp_ibc(VVCLocalContext *lc, const int mvp_l0_flag, const int amvr_shift, Mv *mv) +{ + LOCAL_ALIGNED_8(Mv, mvp, [1]); + + ibc_merge_candidates(lc, mvp_l0_flag, mvp); + ibc_add_mvp(mv, mvp, amvr_shift); +} + +void ff_vvc_luma_mv_merge_ibc(VVCLocalContext *lc, const int merge_idx, Mv *mv) +{ + ibc_merge_candidates(lc, merge_idx, mv); +} + +static int affine_mvp_constructed_cp(NeighbourContext *ctx, + const NeighbourIdx *neighbour, const int num_neighbour, + const int lx, const int8_t ref_idx, const int amvr_shift, Mv *cp) +{ + const VVCLocalContext *lc = ctx->lc; + const VVCFrameContext *fc = lc->fc; + const MvField *tab_mvf = fc->tab.mvf; + const int min_pu_width = fc->ps.pps->min_pu_width; + const RefPicList* rpl = lc->sc->rpl; + int available = 0; + + for (int i = 0; i < num_neighbour; i++) { + Neighbour *n = &ctx->neighbours[neighbour[i]]; + if (check_available(n, ctx->lc, 0)) { + const PredFlag maskx = lx + 1; + const MvField* mvf = &TAB_MVF(n->x, n->y); + const int poc = rpl[lx].list[ref_idx]; + if ((mvf->pred_flag & maskx) && rpl[lx].list[mvf->ref_idx[lx]] == poc) { + available = 1; + *cp = mvf->mv[lx]; + } else { + const int ly = !lx; + const PredFlag masky = ly + 1; + if ((mvf->pred_flag & masky) && rpl[ly].list[mvf->ref_idx[ly]] == poc) { + available = 1; + *cp = mvf->mv[ly]; + } + } + if (available) { + ff_vvc_round_mv(cp, amvr_shift, amvr_shift); + return 1; + } + } + } + return 0; +} + +#define AFFINE_MVP_CONSTRUCTED_CP(cands, cp) \ + affine_mvp_constructed_cp(nctx, cands, FF_ARRAY_ELEMS(cands), lx, ref_idx, \ + amvr_shift, cp) + +//8.5.5.8 Derivation process for constructed affine control point motion vector prediction candidates +static int affine_mvp_const1(NeighbourContext* nctx, + const int lx, const int8_t ref_idx, const int amvr_shift, + Mv *cps, int *available) +{ + const NeighbourIdx tl[] = { B2, B3, A2 }; + const NeighbourIdx tr[] = { B1, B0 }; + const NeighbourIdx bl[] = { A1, A0 }; + + available[0] = AFFINE_MVP_CONSTRUCTED_CP(tl, cps + 0); + available[1] = AFFINE_MVP_CONSTRUCTED_CP(tr, cps + 1); + available[2] = AFFINE_MVP_CONSTRUCTED_CP(bl, cps + 2); + return available[0] && available[1]; +} + +//8.5.5.7 item 7 +static void affine_mvp_const2(const int idx, Mv *cps, const int num_cp) +{ + const Mv mv = cps[idx]; + for (int j = 0; j < num_cp; j++) + cps[j] = mv; +} + +//8.5.5.7 Derivation process for luma affine control point motion vector predictors +static void affine_mvp(const VVCLocalContext *lc, + const int mvp_lx_flag, const int lx, const int8_t *ref_idx, const int amvr_shift, + MotionModelIdc motion_model_idc, Mv *cps) +{ + const NeighbourIdx ak[] = { A0, A1 }; + const NeighbourIdx bk[] = { B0, B1, B2 }; + const int num_cp = motion_model_idc + 1; + NeighbourContext nctx; + int available[MAX_CONTROL_POINTS]; + int num_cands = 0; + + init_neighbour_context(&nctx, lc); + //Ak + if (AFFINE_MVP_FROM_NBS(ak)) { + if (mvp_lx_flag == num_cands) + return; + num_cands++; + } + //Bk + if (AFFINE_MVP_FROM_NBS(bk)) { + if (mvp_lx_flag == num_cands) + return; + num_cands++; + } + + //Const1 + if (affine_mvp_const1(&nctx, lx, ref_idx[lx], amvr_shift, cps, available)) { + if (available[2] || motion_model_idc == MOTION_4_PARAMS_AFFINE) { + if (mvp_lx_flag == num_cands) + return; + num_cands++; + } + } + + //Const2 + for (int i = 2; i >= 0; i--) { + if (available[i]) { + if (mvp_lx_flag == num_cands) { + affine_mvp_const2(i, cps, num_cp); + return; + } + num_cands++; + } + } + if (temporal_luma_motion_vector(lc, ref_idx[lx], cps, lx, 1, 0)) { + if (mvp_lx_flag == num_cands) { + ff_vvc_round_mv(cps, amvr_shift, amvr_shift); + for (int i = 1; i < num_cp; i++) + cps[i] = cps[0]; + return; + } + num_cands++; + } + + //Zero Mv + memset(cps, 0, num_cp * sizeof(Mv)); +} + +void ff_vvc_affine_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo *mi) +{ + const CodingUnit *cu = lc->cu; + + mi->num_sb_x = cu->cb_width >> MIN_PU_LOG2; + mi->num_sb_y = cu->cb_height >> MIN_PU_LOG2; + + ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height); + if (mi->pred_flag != PF_L1) + affine_mvp(lc, mvp_lx_flag[L0], L0, mi->ref_idx, amvr_shift, mi->motion_model_idc, &mi->mv[L0][0]); + if (mi->pred_flag != PF_L0) + affine_mvp(lc, mvp_lx_flag[L1], L1, mi->ref_idx, amvr_shift, mi->motion_model_idc, &mi->mv[L1][0]); +} + +//8.5.2.14 Rounding process for motion vectors +void ff_vvc_round_mv(Mv *mv, const int lshift, const int rshift) +{ + if (rshift) { + const int offset = 1 << (rshift - 1); + mv->x = ((mv->x + offset - (mv->x >= 0)) >> rshift) * (1 << lshift); + mv->y = ((mv->y + offset - (mv->y >= 0)) >> rshift) * (1 << lshift); + } else { + mv->x = mv->x * (1 << lshift); + mv->y = mv->y * (1 << lshift); + } +} + +void ff_vvc_clip_mv(Mv *mv) +{ + mv->x = av_clip(mv->x, -(1 << 17), (1 << 17) - 1); + mv->y = av_clip(mv->y, -(1 << 17), (1 << 17) - 1); +} + +//8.5.2.1 Derivation process for motion vector components and reference indices +static av_always_inline int is_greater_mer(const VVCFrameContext *fc, const int x0, const int y0, const int x0_br, const int y0_br) +{ + const uint8_t plevel = fc->ps.sps->log2_parallel_merge_level; + + return x0_br >> plevel > x0 >> plevel && + y0_br >> plevel > y0 >> plevel; +} + +static void update_hmvp(MvField *hmvp, int *num_hmvp, const MvField *mvf, + int (*compare)(const MvField *n, const MvField *o)) +{ + int i; + for (i = 0; i < *num_hmvp; i++) { + if (compare(mvf, hmvp + i)) { + (*num_hmvp)--; + break; + } + } + if (i == MAX_NUM_HMVP_CANDS) { + (*num_hmvp)--; + i = 0; + } + + memmove(hmvp + i, hmvp + i + 1, (*num_hmvp - i) * sizeof(MvField)); + hmvp[(*num_hmvp)++] = *mvf; +} + +static int compare_l0_mv(const MvField *n, const MvField *o) +{ + return IS_SAME_MV(&n->mv[L0], &o->mv[L0]); +} + +//8.6.2.4 Derivation process for IBC history-based block vector candidates +//8.5.2.16 Updating process for the history-based motion vector predictor candidate list +void ff_vvc_update_hmvp(VVCLocalContext *lc, const MotionInfo *mi) +{ + const VVCFrameContext *fc = lc->fc; + const CodingUnit *cu = lc->cu; + const int min_pu_width = fc->ps.pps->min_pu_width; + const MvField *tab_mvf = fc->tab.mvf; + EntryPoint *ep = lc->ep; + + if (cu->pred_mode == MODE_IBC) { + if (cu->cb_width * cu->cb_height <= 16) + return; + update_hmvp(ep->hmvp_ibc, &ep->num_hmvp_ibc, &TAB_MVF(cu->x0, cu->y0), compare_l0_mv); + } else { + if (!is_greater_mer(fc, cu->x0, cu->y0, cu->x0 + cu->cb_width, cu->y0 + cu->cb_height)) + return; + update_hmvp(ep->hmvp, &ep->num_hmvp, &TAB_MVF(cu->x0, cu->y0), compare_mv_ref_idx); + } +} + +MvField* ff_vvc_get_mvf(const VVCFrameContext *fc, const int x0, const int y0) +{ + const int min_pu_width = fc->ps.pps->min_pu_width; + MvField* tab_mvf = fc->tab.mvf; + + return &TAB_MVF(x0, y0); +} diff --git a/libavcodec/vvc/vvc_mvs.h b/libavcodec/vvc/vvc_mvs.h new file mode 100644 index 00000000000..78ad17c3033 --- /dev/null +++ b/libavcodec/vvc/vvc_mvs.h @@ -0,0 +1,48 @@ +/* + * VVC motion vector decoder + * + * Copyright (C) 2023 Nuo Mi + * + * 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 + */ + +#ifndef AVCODEC_VVC_VVC_MVS_H +#define AVCODEC_VVC_VVC_MVS_H + +#include "vvc_ctu.h" + +void ff_vvc_round_mv(Mv *mv, int lshift, int rshift); +void ff_vvc_clip_mv(Mv *mv); +void ff_vvc_mv_scale(Mv *dst, const Mv *src, int td, int tb); +void ff_vvc_luma_mv_merge_mode(VVCLocalContext *lc, int merge_idx, int ciip_flag, MvField *mv); +void ff_vvc_luma_mv_merge_gpm(VVCLocalContext *lc, const int merge_gpm_idx[2], MvField *mv); +void ff_vvc_luma_mv_merge_ibc(VVCLocalContext *lc, int merge_idx, Mv *mv); +void ff_vvc_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo *mi); +void ff_vvc_mvp_ibc(VVCLocalContext *lc, int mvp_l0_flag, int amvr_shift, Mv *mv); +void ff_vvc_sb_mv_merge_mode(VVCLocalContext *lc, int merge_subblock_idx, PredictionUnit *pu); +void ff_vvc_affine_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo* mi); +void ff_vvc_store_sb_mvs(const VVCLocalContext *lc, PredictionUnit *pu); +void ff_vvc_store_mv(const VVCLocalContext *lc, const MotionInfo *mi); +void ff_vvc_store_mvf(const VVCLocalContext *lc, const MvField *mvf); +void ff_vvc_store_gpm_mvf(const VVCLocalContext *lc, const PredictionUnit* pu); +void ff_vvc_update_hmvp(VVCLocalContext *lc, const MotionInfo *mi); +int ff_vvc_no_backward_pred_flag(const VVCLocalContext *lc); +MvField* ff_vvc_get_mvf(const VVCFrameContext *fc, const int x0, const int y0); +void ff_vvc_set_mvf(const VVCLocalContext *lc, const int x0, const int y0, const int w, const int h, const MvField *mvf); +void ff_vvc_set_intra_mvf(const VVCLocalContext *lc, int dmvr); + +#endif //AVCODEC_VVC_VVC_MVS_H diff --git a/libavcodec/vvc/vvc_ps.c b/libavcodec/vvc/vvc_ps.c new file mode 100644 index 00000000000..7972803da65 --- /dev/null +++ b/libavcodec/vvc/vvc_ps.c @@ -0,0 +1,1178 @@ +/* + * VVC parameter set parser + * + * Copyright (C) 2023 Nuo Mi + * Copyright (C) 2022 Xu Mu + * + * 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 "libavcodec/cbs_h266.h" +#include "libavutil/imgutils.h" +#include "libavcodec/refstruct.h" +#include "vvc_data.h" +#include "vvc_ps.h" +#include "vvcdec.h" + +static int sps_map_pixel_format(VVCSPS *sps, void *log_ctx) +{ + const H266RawSPS *r = sps->r; + const AVPixFmtDescriptor *desc; + + switch (sps->bit_depth) { + case 8: + if (r->sps_chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY8; + if (r->sps_chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P; + if (r->sps_chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P; + if (r->sps_chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P; + break; + case 10: + if (r->sps_chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY10; + if (r->sps_chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P10; + if (r->sps_chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P10; + if (r->sps_chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P10; + break; + case 12: + if (r->sps_chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY12; + if (r->sps_chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P12; + if (r->sps_chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P12; + if (r->sps_chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P12; + break; + default: + av_log(log_ctx, AV_LOG_ERROR, + "The following bit-depths are currently specified: 8, 10, 12 bits, " + "chroma_format_idc is %d, depth is %d\n", + r->sps_chroma_format_idc, sps->bit_depth); + return AVERROR_INVALIDDATA; + } + + desc = av_pix_fmt_desc_get(sps->pix_fmt); + if (!desc) + return AVERROR(EINVAL); + + sps->hshift[0] = sps->vshift[0] = 0; + sps->hshift[2] = sps->hshift[1] = desc->log2_chroma_w; + sps->vshift[2] = sps->vshift[1] = desc->log2_chroma_h; + + sps->pixel_shift = sps->bit_depth > 8; + + return 0; +} + +static int sps_bit_depth(VVCSPS *sps, void *log_ctx) +{ + const H266RawSPS *r = sps->r; + + sps->bit_depth = r->sps_bitdepth_minus8 + 8; + sps->qp_bd_offset = 6 * (sps->bit_depth - 8); + sps->log2_transform_range = + r->sps_extended_precision_flag ? FFMAX(15, FFMIN(20, sps->bit_depth + 6)) : 15; + return sps_map_pixel_format(sps, log_ctx); +} + +static int sps_chroma_qp_table(VVCSPS *sps) +{ + const H266RawSPS *r = sps->r; + const int num_qp_tables = r->sps_same_qp_table_for_chroma_flag ? + 1 : (r->sps_joint_cbcr_enabled_flag ? 3 : 2); + + for (int i = 0; i < num_qp_tables; i++) { + int num_points_in_qp_table; + int8_t qp_in[VVC_MAX_POINTS_IN_QP_TABLE], qp_out[VVC_MAX_POINTS_IN_QP_TABLE]; + unsigned int delta_qp_in[VVC_MAX_POINTS_IN_QP_TABLE]; + int off = sps->qp_bd_offset; + + num_points_in_qp_table = r->sps_num_points_in_qp_table_minus1[i] + 1; + + qp_out[0] = qp_in[0] = r->sps_qp_table_start_minus26[i] + 26; + for (int j = 0; j < num_points_in_qp_table; j++ ) { + delta_qp_in[j] = r->sps_delta_qp_in_val_minus1[i][j] + 1; + qp_in[j+1] = qp_in[j] + delta_qp_in[j]; + qp_out[j+1] = qp_out[j] + (r->sps_delta_qp_in_val_minus1[i][j] ^ r->sps_delta_qp_diff_val[i][j]); + } + sps->chroma_qp_table[i][qp_in[0] + off] = qp_out[0]; + for (int k = qp_in[0] - 1 + off; k >= 0; k--) + sps->chroma_qp_table[i][k] = av_clip(sps->chroma_qp_table[i][k+1]-1, -off, 63); + + for (int j = 0; j < num_points_in_qp_table; j++) { + int sh = delta_qp_in[j] >> 1; + for (int k = qp_in[j] + 1 + off, m = 1; k <= qp_in[j+1] + off; k++, m++) { + sps->chroma_qp_table[i][k] = sps->chroma_qp_table[i][qp_in[j] + off] + + ((qp_out[j+1] - qp_out[j]) * m + sh) / delta_qp_in[j]; + } + } + for (int k = qp_in[num_points_in_qp_table] + 1 + off; k <= 63 + off; k++) + sps->chroma_qp_table[i][k] = av_clip(sps->chroma_qp_table[i][k-1] + 1, -sps->qp_bd_offset, 63); + } + if (r->sps_same_qp_table_for_chroma_flag) { + memcpy(&sps->chroma_qp_table[1], &sps->chroma_qp_table[0], sizeof(sps->chroma_qp_table[0])); + memcpy(&sps->chroma_qp_table[2], &sps->chroma_qp_table[0], sizeof(sps->chroma_qp_table[0])); + } + + return 0; +} + +static void sps_poc(VVCSPS *sps) +{ + sps->max_pic_order_cnt_lsb = 1 << (sps->r->sps_log2_max_pic_order_cnt_lsb_minus4 + 4); +} + +static void sps_inter(VVCSPS *sps) +{ + const H266RawSPS *r = sps->r; + + sps->max_num_merge_cand = 6 - r->sps_six_minus_max_num_merge_cand; + sps->max_num_ibc_merge_cand = 6 - r->sps_six_minus_max_num_ibc_merge_cand; + + if (sps->r->sps_gpm_enabled_flag) { + sps->max_num_gpm_merge_cand = 2; + if (sps->max_num_merge_cand >= 3) + sps->max_num_gpm_merge_cand = sps->max_num_merge_cand - r->sps_max_num_merge_cand_minus_max_num_gpm_cand; + } + + sps->log2_parallel_merge_level = r->sps_log2_parallel_merge_level_minus2 + 2; +} + +static void sps_partition_constraints(VVCSPS* sps) +{ + const H266RawSPS *r = sps->r; + + sps->ctb_log2_size_y = r->sps_log2_ctu_size_minus5 + 5; + sps->ctb_size_y = 1 << sps->ctb_log2_size_y; + sps->min_cb_log2_size_y = r->sps_log2_min_luma_coding_block_size_minus2 + 2; + sps->min_cb_size_y = 1 << sps->min_cb_log2_size_y; + sps->max_tb_size_y = 1 << (r->sps_max_luma_transform_size_64_flag ? 6 : 5); + sps->max_ts_size = 1 << (r->sps_log2_transform_skip_max_size_minus2 + 2); +} + +static void sps_ladf(VVCSPS* sps) +{ + const H266RawSPS *r = sps->r; + + if (r->sps_ladf_enabled_flag) { + sps->num_ladf_intervals = r->sps_num_ladf_intervals_minus2 + 2; + sps->ladf_interval_lower_bound[0] = 0; + for (int i = 0; i < sps->num_ladf_intervals - 1; i++) { + sps->ladf_interval_lower_bound[i + 1] = + sps->ladf_interval_lower_bound[i] + r->sps_ladf_delta_threshold_minus1[i] + 1; + } + } +} + +static int sps_derive(VVCSPS *sps, void *log_ctx) +{ + int ret; + const H266RawSPS *r = sps->r; + + ret = sps_bit_depth(sps, log_ctx); + if (ret < 0) + return ret; + sps_poc(sps); + sps_inter(sps); + sps_partition_constraints(sps); + sps_ladf(sps); + if (r->sps_chroma_format_idc != 0) + sps_chroma_qp_table(sps); + + return 0; +} + +static void sps_free(FFRefStructOpaque opaque, void *obj) +{ + VVCSPS *sps = obj; + ff_refstruct_unref(&sps->r); +} + +static const VVCSPS *sps_alloc(const H266RawSPS *rsps, void *log_ctx) +{ + int ret; + VVCSPS *sps = ff_refstruct_alloc_ext(sizeof(*sps), 0, NULL, sps_free); + + if (!sps) + return NULL; + + ff_refstruct_replace(&sps->r, rsps); + + ret = sps_derive(sps, log_ctx); + if (ret < 0) + goto fail; + + return sps; + +fail: + ff_refstruct_unref(&sps); + return NULL; +} + +static int decode_sps(VVCParamSets *ps, const H266RawSPS *rsps, void *log_ctx) +{ + const int sps_id = rsps->sps_seq_parameter_set_id; + const VVCSPS *old_sps = ps->sps_list[sps_id]; + const VVCSPS *sps; + + if (old_sps && old_sps->r == rsps) + return 0; + + sps = sps_alloc(rsps, log_ctx); + if (!sps) + return AVERROR(ENOMEM); + + ff_refstruct_unref(&ps->sps_list[sps_id]); + ps->sps_list[sps_id] = sps; + + return 0; +} + +static void pps_chroma_qp_offset(VVCPPS *pps) +{ + pps->chroma_qp_offset[CB - 1] = pps->r->pps_cb_qp_offset; + pps->chroma_qp_offset[CR - 1] = pps->r->pps_cr_qp_offset; + pps->chroma_qp_offset[JCBCR - 1]= pps->r->pps_joint_cbcr_qp_offset_value; + for (int i = 0; i < 6; i++) { + pps->chroma_qp_offset_list[i][CB - 1] = pps->r->pps_cb_qp_offset_list[i]; + pps->chroma_qp_offset_list[i][CR - 1] = pps->r->pps_cr_qp_offset_list[i]; + pps->chroma_qp_offset_list[i][JCBCR - 1]= pps->r->pps_joint_cbcr_qp_offset_list[i]; + } +} + +static void pps_width_height(VVCPPS *pps, const VVCSPS *sps) +{ + const H266RawPPS *r = pps->r; + + pps->width = r->pps_pic_width_in_luma_samples; + pps->height = r->pps_pic_height_in_luma_samples; + + pps->ctb_width = AV_CEIL_RSHIFT(pps->width, sps->ctb_log2_size_y); + pps->ctb_height = AV_CEIL_RSHIFT(pps->height, sps->ctb_log2_size_y); + pps->ctb_count = pps->ctb_width * pps->ctb_height; + + pps->min_cb_width = pps->width >> sps->min_cb_log2_size_y; + pps->min_cb_height = pps->height >> sps->min_cb_log2_size_y; + + pps->min_pu_width = pps->width >> MIN_PU_LOG2; + pps->min_pu_height = pps->height >> MIN_PU_LOG2; + pps->min_tu_width = pps->width >> MIN_TU_LOG2; + pps->min_tu_height = pps->height >> MIN_TU_LOG2; + + pps->width32 = AV_CEIL_RSHIFT(pps->width, 5); + pps->height32 = AV_CEIL_RSHIFT(pps->height, 5); + pps->width64 = AV_CEIL_RSHIFT(pps->width, 6); + pps->height64 = AV_CEIL_RSHIFT(pps->height, 6); +} + +static int pps_bd(VVCPPS *pps) +{ + const H266RawPPS *r = pps->r; + + pps->col_bd = av_calloc(r->num_tile_columns + 1, sizeof(*pps->col_bd)); + pps->row_bd = av_calloc(r->num_tile_rows + 1, sizeof(*pps->row_bd)); + pps->ctb_to_col_bd = av_calloc(pps->ctb_width + 1, sizeof(*pps->ctb_to_col_bd)); + pps->ctb_to_row_bd = av_calloc(pps->ctb_height + 1, sizeof(*pps->ctb_to_col_bd)); + if (!pps->col_bd || !pps->row_bd || !pps->ctb_to_col_bd || !pps->ctb_to_row_bd) + return AVERROR(ENOMEM); + + for (int i = 0, j = 0; i < r->num_tile_columns; i++) { + pps->col_bd[i] = j; + j += r->col_width_val[i]; + for (int k = pps->col_bd[i]; k < j; k++) + pps->ctb_to_col_bd[k] = pps->col_bd[i]; + } + + for (int i = 0, j = 0; i < r->num_tile_rows; i++) { + pps->row_bd[i] = j; + j += r->row_height_val[i]; + for (int k = pps->row_bd[i]; k < j; k++) + pps->ctb_to_row_bd[k] = pps->row_bd[i]; + } + return 0; +} + + +static int next_tile_idx(int tile_idx, const int i, const H266RawPPS *r) +{ + if (r->pps_tile_idx_delta_present_flag) { + tile_idx += r->pps_tile_idx_delta_val[i]; + } else { + tile_idx += r->pps_slice_width_in_tiles_minus1[i] + 1; + if (tile_idx % r->num_tile_columns == 0) + tile_idx += (r->pps_slice_height_in_tiles_minus1[i]) * r->num_tile_columns; + } + return tile_idx; +} + +static void tile_xy(int *tile_x, int *tile_y, const int tile_idx, const VVCPPS *pps) +{ + *tile_x = tile_idx % pps->r->num_tile_columns; + *tile_y = tile_idx / pps->r->num_tile_columns; +} + +static void ctu_xy(int *ctu_x, int *ctu_y, const int tile_x, const int tile_y, const VVCPPS *pps) +{ + *ctu_x = pps->col_bd[tile_x]; + *ctu_y = pps->row_bd[tile_y]; +} + +static int ctu_rs(const int ctu_x, const int ctu_y, const VVCPPS *pps) +{ + return pps->ctb_width * ctu_y + ctu_x; +} + +static int pps_add_ctus(VVCPPS *pps, int *off, const int ctu_x, const int ctu_y, + const int w, const int h) +{ + int start = *off; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + pps->ctb_addr_in_slice[*off] = ctu_rs(ctu_x + x, ctu_y + y, pps); + (*off)++; + } + } + return *off - start; +} + +static int pps_one_tile_slices(VVCPPS *pps, const int tile_idx, int i, int *off) +{ + const H266RawPPS *r = pps->r; + int ctu_x, ctu_y, ctu_y_end, tile_x, tile_y; + + tile_xy(&tile_x, &tile_y, tile_idx, pps); + ctu_xy(&ctu_x, &ctu_y, tile_x, tile_y, pps); + ctu_y_end = ctu_y + r->row_height_val[tile_y]; + while (ctu_y < ctu_y_end) { + pps->slice_start_offset[i] = *off; + pps->num_ctus_in_slice[i] = pps_add_ctus(pps, off, ctu_x, ctu_y, + r->col_width_val[tile_x], r->slice_height_in_ctus[i]); + ctu_y += r->slice_height_in_ctus[i++]; + } + i--; + return i; +} + +static void pps_multi_tiles_slice(VVCPPS *pps, const int tile_idx, const int i, int *off) +{ + const H266RawPPS *r = pps->r; + int ctu_x, ctu_y,tile_x, tile_y; + + tile_xy(&tile_x, &tile_y, tile_idx, pps); + pps->slice_start_offset[i] = *off; + pps->num_ctus_in_slice[i] = 0; + for (int ty = tile_y; ty <= tile_y + r->pps_slice_height_in_tiles_minus1[i]; ty++) { + for (int tx = tile_x; tx <= tile_x + r->pps_slice_width_in_tiles_minus1[i]; tx++) { + ctu_xy(&ctu_x, &ctu_y, tx, ty, pps); + pps->num_ctus_in_slice[i] += pps_add_ctus(pps, off, ctu_x, ctu_y, + r->col_width_val[tx], r->row_height_val[ty]); + } + } +} + +static void pps_rect_slice(VVCPPS* pps) +{ + const H266RawPPS* r = pps->r; + int tile_idx = 0, off = 0; + + for (int i = 0; i < r->pps_num_slices_in_pic_minus1 + 1; i++) { + if (!r->pps_slice_width_in_tiles_minus1[i] && + !r->pps_slice_height_in_tiles_minus1[i]) { + i = pps_one_tile_slices(pps, tile_idx, i, &off); + } else { + pps_multi_tiles_slice(pps, tile_idx, i, &off); + + } + tile_idx = next_tile_idx(tile_idx, i, r); + } +} + +static void pps_no_rect_slice(VVCPPS* pps) +{ + const H266RawPPS* r = pps->r; + int ctu_x, ctu_y, off = 0; + + for (int tile_y = 0; tile_y < r->num_tile_rows; tile_y++) { + for (int tile_x = 0; tile_x < r->num_tile_columns; tile_x++) { + ctu_xy(&ctu_x, &ctu_y, tile_x, tile_y, pps); + pps_add_ctus(pps, &off, ctu_x, ctu_y, r->col_width_val[tile_x], r->row_height_val[tile_y]); + } + } +} + +static int pps_slice_map(VVCPPS *pps) +{ + pps->ctb_addr_in_slice = av_calloc(pps->ctb_count, sizeof(*pps->ctb_addr_in_slice)); + if (!pps->ctb_addr_in_slice) + return AVERROR(ENOMEM); + + if (pps->r->pps_rect_slice_flag) + pps_rect_slice(pps); + else + pps_no_rect_slice(pps); + + return 0; +} + +static void pps_ref_wraparound_offset(VVCPPS *pps, const VVCSPS *sps) +{ + const H266RawPPS *r = pps->r; + + if (r->pps_ref_wraparound_enabled_flag) + pps->ref_wraparound_offset = (pps->width / sps->min_cb_size_y) - r->pps_pic_width_minus_wraparound_offset; +} + +static int pps_derive(VVCPPS *pps, const VVCSPS *sps) +{ + int ret; + + pps_chroma_qp_offset(pps); + pps_width_height(pps, sps); + + ret = pps_bd(pps); + if (ret < 0) + return ret; + + ret = pps_slice_map(pps); + if (ret < 0) + return ret; + + pps_ref_wraparound_offset(pps, sps); + + return 0; +} + +static void pps_free(FFRefStructOpaque opaque, void *obj) +{ + VVCPPS *pps = obj; + + ff_refstruct_unref(&pps->r); + + av_freep(&pps->col_bd); + av_freep(&pps->row_bd); + av_freep(&pps->ctb_to_col_bd); + av_freep(&pps->ctb_to_row_bd); + av_freep(&pps->ctb_addr_in_slice); +} + +static const VVCPPS *pps_alloc(const H266RawPPS *rpps, const VVCSPS *sps) +{ + int ret; + VVCPPS *pps = ff_refstruct_alloc_ext(sizeof(*pps), 0, NULL, pps_free); + + if (!pps) + return NULL; + + ff_refstruct_replace(&pps->r, rpps); + + ret = pps_derive(pps, sps); + if (ret < 0) + goto fail; + + return pps; + +fail: + ff_refstruct_unref(&pps); + return NULL; +} + +static int decode_pps(VVCParamSets *ps, const H266RawPPS *rpps) +{ + int ret = 0; + const int pps_id = rpps->pps_pic_parameter_set_id; + const int sps_id = rpps->pps_seq_parameter_set_id; + const VVCPPS *old_pps = ps->pps_list[pps_id]; + const VVCPPS *pps; + + if (old_pps && old_pps->r == rpps) + return 0; + + pps = pps_alloc(rpps, ps->sps_list[sps_id]); + if (!pps) + return AVERROR(ENOMEM); + + ff_refstruct_unref(&ps->pps_list[pps_id]); + ps->pps_list[pps_id] = pps; + + return ret; +} + +static int decode_ps(VVCParamSets *ps, const CodedBitstreamH266Context *h266, void *log_ctx) +{ + const H266RawPictureHeader *ph = h266->ph; + const H266RawPPS *rpps; + const H266RawSPS *rsps; + int ret; + + if (!ph) + return AVERROR_INVALIDDATA; + + rpps = h266->pps[ph->ph_pic_parameter_set_id]; + if (!rpps) + return AVERROR_INVALIDDATA; + + rsps = h266->sps[rpps->pps_seq_parameter_set_id]; + if (!rsps) + return AVERROR_INVALIDDATA; + + ret = decode_sps(ps, rsps, log_ctx); + if (ret < 0) + return ret; + + ret = decode_pps(ps, rpps); + if (ret < 0) + return ret; + + return 0; +} + +#define WEIGHT_TABLE(x) \ + w->nb_weights[L##x] = r->num_weights_l##x; \ + for (int i = 0; i < w->nb_weights[L##x]; i++) { \ + w->weight_flag[L##x][LUMA][i] = r->luma_weight_l##x##_flag[i]; \ + w->weight_flag[L##x][CHROMA][i] = r->chroma_weight_l##x##_flag[i]; \ + w->weight[L##x][LUMA][i] = denom[LUMA] + r->delta_luma_weight_l##x[i]; \ + w->offset[L##x][LUMA][i] = r->luma_offset_l##x[i]; \ + for (int j = CB; j <= CR; j++) { \ + w->weight[L##x][j][i] = denom[CHROMA] + r->delta_chroma_weight_l##x[i][j - 1]; \ + w->offset[L##x][j][i] = 128 + r->delta_chroma_offset_l##x[i][j - 1]; \ + w->offset[L##x][j][i] -= (128 * w->weight[L##x][j][i]) >> w->log2_denom[CHROMA]; \ + w->offset[L##x][j][i] = av_clip_intp2(w->offset[L##x][j][i], 7); \ + } \ + } \ + +static void pred_weight_table(PredWeightTable *w, const H266RawPredWeightTable *r) +{ + int denom[2]; + + w->log2_denom[LUMA] = r->luma_log2_weight_denom; + w->log2_denom[CHROMA] = w->log2_denom[LUMA] + r->delta_chroma_log2_weight_denom; + denom[LUMA] = 1 << w->log2_denom[LUMA]; + denom[CHROMA] = 1 << w->log2_denom[CHROMA]; + WEIGHT_TABLE(0) + WEIGHT_TABLE(1) +} + +// 8.3.1 Decoding process for picture order count +static int ph_compute_poc(const H266RawPictureHeader *ph, const H266RawSPS *sps, const int poc_tid0, const int is_clvss) +{ + const int max_poc_lsb = 1 << (sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4); + const int prev_poc_lsb = poc_tid0 % max_poc_lsb; + const int prev_poc_msb = poc_tid0 - prev_poc_lsb; + const int poc_lsb = ph->ph_pic_order_cnt_lsb; + int poc_msb; + + if (ph->ph_poc_msb_cycle_present_flag) { + poc_msb = ph->ph_poc_msb_cycle_val * max_poc_lsb; + } else if (is_clvss) { + poc_msb = 0; + } else { + if (poc_lsb < prev_poc_lsb && prev_poc_lsb - poc_lsb >= max_poc_lsb / 2) + poc_msb = prev_poc_msb + max_poc_lsb; + else if (poc_lsb > prev_poc_lsb && poc_lsb - prev_poc_lsb > max_poc_lsb / 2) + poc_msb = prev_poc_msb - max_poc_lsb; + else + poc_msb = prev_poc_msb; + } + + return poc_msb + poc_lsb; +} + +static av_always_inline uint16_t lmcs_derive_lut_sample(uint16_t sample, + uint16_t *pivot1, uint16_t *pivot2, uint16_t *scale_coeff, const int idx, const int max) +{ + const int lut_sample = + pivot1[idx] + ((scale_coeff[idx] * (sample - pivot2[idx]) + (1<< 10)) >> 11); + return av_clip(lut_sample, 0, max - 1); +} + +//8.8.2.2 Inverse mapping process for a luma sample +static int lmcs_derive_lut(VVCLMCS *lmcs, const H266RawAPS *rlmcs, const H266RawSPS *sps) +{ + const int bit_depth = (sps->sps_bitdepth_minus8 + 8); + const int max = (1 << bit_depth); + const int org_cw = max / LMCS_MAX_BIN_SIZE; + const int shift = av_log2(org_cw); + const int off = 1 << (shift - 1); + int cw[LMCS_MAX_BIN_SIZE]; + uint16_t input_pivot[LMCS_MAX_BIN_SIZE]; + uint16_t scale_coeff[LMCS_MAX_BIN_SIZE]; + uint16_t inv_scale_coeff[LMCS_MAX_BIN_SIZE]; + int i, delta_crs; + if (bit_depth > LMCS_MAX_BIT_DEPTH) + return AVERROR_PATCHWELCOME; + + if (!rlmcs) + return AVERROR_INVALIDDATA; + + lmcs->min_bin_idx = rlmcs->lmcs_min_bin_idx; + lmcs->max_bin_idx = LMCS_MAX_BIN_SIZE - 1 - rlmcs->lmcs_min_bin_idx; + + memset(cw, 0, sizeof(cw)); + for (int i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++) + cw[i] = org_cw + (1 - 2 * rlmcs->lmcs_delta_sign_cw_flag[i]) * rlmcs->lmcs_delta_abs_cw[i]; + + delta_crs = (1 - 2 * rlmcs->lmcs_delta_sign_crs_flag) * rlmcs->lmcs_delta_abs_crs; + + lmcs->pivot[0] = 0; + for (i = 0; i < LMCS_MAX_BIN_SIZE; i++) { + input_pivot[i] = i * org_cw; + lmcs->pivot[i + 1] = lmcs->pivot[i] + cw[i]; + scale_coeff[i] = (cw[i] * (1 << 11) + off) >> shift; + if (cw[i] == 0) { + inv_scale_coeff[i] = 0; + lmcs->chroma_scale_coeff[i] = (1 << 11); + } else { + inv_scale_coeff[i] = org_cw * (1 << 11) / cw[i]; + lmcs->chroma_scale_coeff[i] = org_cw * (1 << 11) / (cw[i] + delta_crs); + } + } + + //derive lmcs_fwd_lut + for (uint16_t sample = 0; sample < max; sample++) { + const int idx_y = sample / org_cw; + const uint16_t fwd_sample = lmcs_derive_lut_sample(sample, lmcs->pivot, + input_pivot, scale_coeff, idx_y, max); + if (bit_depth > 8) + lmcs->fwd_lut.u16[sample] = fwd_sample; + else + lmcs->fwd_lut.u8 [sample] = fwd_sample; + + } + + //derive lmcs_inv_lut + i = lmcs->min_bin_idx; + for (uint16_t sample = 0; sample < max; sample++) { + uint16_t inv_sample; + while (i <= lmcs->max_bin_idx && sample >= lmcs->pivot[i + 1]) + i++; + + inv_sample = lmcs_derive_lut_sample(sample, input_pivot, lmcs->pivot, + inv_scale_coeff, i, max); + + if (bit_depth > 8) + lmcs->inv_lut.u16[sample] = inv_sample; + else + lmcs->inv_lut.u8 [sample] = inv_sample; + } + + return 0; +} + +static int ph_max_num_subblock_merge_cand(const H266RawSPS *sps, const H266RawPictureHeader *ph) +{ + if (sps->sps_affine_enabled_flag) + return 5 - sps->sps_five_minus_max_num_subblock_merge_cand; + return sps->sps_sbtmvp_enabled_flag && ph->ph_temporal_mvp_enabled_flag; +} + +static int ph_derive(VVCPH *ph, const H266RawSPS *sps, const H266RawPPS *pps, const int poc_tid0, const int is_clvss) +{ + ph->max_num_subblock_merge_cand = ph_max_num_subblock_merge_cand(sps, ph->r); + + ph->poc = ph_compute_poc(ph->r, sps, poc_tid0, is_clvss); + + if (pps->pps_wp_info_in_ph_flag) + pred_weight_table(&ph->pwt, &ph->r->ph_pred_weight_table); + + return 0; +} + +static int decode_ph(VVCFrameParamSets *fps, const H266RawPictureHeader *rph, void *rph_ref, + const int poc_tid0, const int is_clvss) +{ + int ret; + VVCPH *ph = &fps->ph; + const H266RawSPS *sps = fps->sps->r; + const H266RawPPS *pps = fps->pps->r; + + ph->r = rph; + ff_refstruct_replace(&ph->rref, rph_ref); + ret = ph_derive(ph, sps, pps, poc_tid0, is_clvss); + if (ret < 0) + return ret; + + return 0; +} + +static int decode_frame_ps(VVCFrameParamSets *fps, const VVCParamSets *ps, + const CodedBitstreamH266Context *h266, const int poc_tid0, const int is_clvss) +{ + const H266RawPictureHeader *ph = h266->ph; + const H266RawPPS *rpps; + int ret; + + if (!ph) + return AVERROR_INVALIDDATA; + + rpps = h266->pps[ph->ph_pic_parameter_set_id]; + if (!rpps) + return AVERROR_INVALIDDATA; + + ff_refstruct_replace(&fps->sps, ps->sps_list[rpps->pps_seq_parameter_set_id]); + ff_refstruct_replace(&fps->pps, ps->pps_list[rpps->pps_pic_parameter_set_id]); + + ret = decode_ph(fps, ph, h266->ph_ref, poc_tid0, is_clvss); + if (ret < 0) + return ret; + + if (ph->ph_explicit_scaling_list_enabled_flag) + ff_refstruct_replace(&fps->sl, ps->scaling_list[ph->ph_scaling_list_aps_id]); + + if (ph->ph_lmcs_enabled_flag) { + ret = lmcs_derive_lut(&fps->lmcs, ps->lmcs_list[ph->ph_lmcs_aps_id], fps->sps->r); + if (ret < 0) + return ret; + } + + for (int i = 0; i < FF_ARRAY_ELEMS(fps->alf_list); i++) + ff_refstruct_replace(&fps->alf_list[i], ps->alf_list[i]); + + return 0; +} + +static void decode_recovery_flag(VVCContext *s) +{ + if (IS_IDR(s)) + s->no_output_before_recovery_flag = 1; + else if (IS_CRA(s) || IS_GDR(s)) + s->no_output_before_recovery_flag = s->last_eos; +} + +static void decode_recovery_poc(VVCContext *s, const VVCPH *ph) +{ + if (s->no_output_before_recovery_flag) { + if (IS_GDR(s)) + s->gdr_recovery_point_poc = ph->poc + ph->r->ph_recovery_poc_cnt; + if (!GDR_IS_RECOVERED(s) && s->gdr_recovery_point_poc <= ph->poc) + GDR_SET_RECOVERED(s); + } +} + +int ff_vvc_decode_frame_ps(VVCFrameParamSets *fps, struct VVCContext *s) +{ + int ret = 0; + VVCParamSets *ps = &s->ps; + const CodedBitstreamH266Context *h266 = s->cbc->priv_data; + + ret = decode_ps(ps, h266, s->avctx); + if (ret < 0) + return ret; + + decode_recovery_flag(s); + ret = decode_frame_ps(fps, ps, h266, s->poc_tid0, IS_CLVSS(s)); + decode_recovery_poc(s, &fps->ph); + return ret; +} + +void ff_vvc_frame_ps_free(VVCFrameParamSets *fps) +{ + ff_refstruct_unref(&fps->sps); + ff_refstruct_unref(&fps->pps); + ff_refstruct_unref(&fps->ph.rref); + ff_refstruct_unref(&fps->sl); + for (int i = 0; i < FF_ARRAY_ELEMS(fps->alf_list); i++) + ff_refstruct_unref(&fps->alf_list[i]); +} + +void ff_vvc_ps_uninit(VVCParamSets *ps) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(ps->scaling_list); i++) + ff_refstruct_unref(&ps->scaling_list[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(ps->lmcs_list); i++) + ff_refstruct_unref(&ps->lmcs_list[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(ps->alf_list); i++) + ff_refstruct_unref(&ps->alf_list[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(ps->sps_list); i++) + ff_refstruct_unref(&ps->sps_list[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(ps->pps_list); i++) + ff_refstruct_unref(&ps->pps_list[i]); +} + +static void alf_coeff(int16_t *coeff, + const uint8_t *abs, const uint8_t *sign, const int size) +{ + for (int i = 0; i < size; i++) + coeff[i] = (1 - 2 * sign[i]) * abs[i]; +} + +static void alf_coeff_cc(int16_t *coeff, + const uint8_t *mapped_abs, const uint8_t *sign) +{ + for (int i = 0; i < ALF_NUM_COEFF_CC; i++) { + int c = mapped_abs[i]; + if (c) + c = (1 - 2 * sign[i]) * (1 << (c - 1)); + coeff[i] = c; + } +} + +static void alf_luma(VVCALF *alf, const H266RawAPS *aps) +{ + if (!aps->alf_luma_filter_signal_flag) + return; + + for (int i = 0; i < ALF_NUM_FILTERS_LUMA; i++) { + const int ref = aps->alf_luma_coeff_delta_idx[i]; + const uint8_t *abs = aps->alf_luma_coeff_abs[ref]; + const uint8_t *sign = aps->alf_luma_coeff_sign[ref]; + + alf_coeff(alf->luma_coeff[i], abs, sign, ALF_NUM_COEFF_LUMA); + memcpy(alf->luma_clip_idx[i], aps->alf_luma_clip_idx[ref], + sizeof(alf->luma_clip_idx[i])); + } +} + +static void alf_chroma(VVCALF *alf, const H266RawAPS *aps) +{ + if (!aps->alf_chroma_filter_signal_flag) + return; + + alf->num_chroma_filters = aps->alf_chroma_num_alt_filters_minus1 + 1; + for (int i = 0; i < alf->num_chroma_filters; i++) { + const uint8_t *abs = aps->alf_chroma_coeff_abs[i]; + const uint8_t *sign = aps->alf_chroma_coeff_sign[i]; + + alf_coeff(alf->chroma_coeff[i], abs, sign, ALF_NUM_COEFF_CHROMA); + memcpy(alf->chroma_clip_idx[i], aps->alf_chroma_clip_idx[i], + sizeof(alf->chroma_clip_idx[i])); + } +} + +static void alf_cc(VVCALF *alf, const H266RawAPS *aps) +{ + const uint8_t (*abs[])[ALF_NUM_COEFF_CC] = + { aps->alf_cc_cb_mapped_coeff_abs, aps->alf_cc_cr_mapped_coeff_abs }; + const uint8_t (*sign[])[ALF_NUM_COEFF_CC] = + {aps->alf_cc_cb_coeff_sign, aps->alf_cc_cr_coeff_sign }; + const int signaled[] = { aps->alf_cc_cb_filter_signal_flag, aps->alf_cc_cr_filter_signal_flag}; + + alf->num_cc_filters[0] = aps->alf_cc_cb_filters_signalled_minus1 + 1; + alf->num_cc_filters[1] = aps->alf_cc_cr_filters_signalled_minus1 + 1; + + for (int idx = 0; idx < 2; idx++) { + if (signaled[idx]) { + for (int i = 0; i < alf->num_cc_filters[idx]; i++) + alf_coeff_cc(alf->cc_coeff[idx][i], abs[idx][i], sign[idx][i]); + } + } +} + +static void alf_derive(VVCALF *alf, const H266RawAPS *aps) +{ + alf_luma(alf, aps); + alf_chroma(alf, aps); + alf_cc(alf, aps); +} + +static int aps_decode_alf(const VVCALF **alf, const H266RawAPS *aps) +{ + VVCALF *a = ff_refstruct_allocz(sizeof(*a)); + if (!a) + return AVERROR(ENOMEM); + + alf_derive(a, aps); + ff_refstruct_replace(alf, a); + ff_refstruct_unref(&a); + + return 0; +} + +static int is_luma_list(const int id) +{ + return id % VVC_MAX_SAMPLE_ARRAYS == SL_START_4x4 || id == SL_START_64x64 + 1; +} + +static int derive_matrix_size(const int id) +{ + return id < SL_START_4x4 ? 2 : (id < SL_START_8x8 ? 4 : 8); +} + +// 7.4.3.20 Scaling list data semantics +static void scaling_derive(VVCScalingList *sl, const H266RawAPS *aps) +{ + for (int id = 0; id < SL_MAX_ID; id++) { + const int matrix_size = derive_matrix_size(id); + const int log2_size = av_log2(matrix_size); + const int list_size = matrix_size * matrix_size; + int coeff[SL_MAX_MATRIX_SIZE * SL_MAX_MATRIX_SIZE]; + const uint8_t *pred; + const int *scaling_list; + int dc = 0; + + if (aps->aps_chroma_present_flag || is_luma_list(id)) { + if (!aps->scaling_list_copy_mode_flag[id]) { + int next_coef = 0; + + if (id >= SL_START_16x16) + dc = next_coef = aps->scaling_list_dc_coef[id - SL_START_16x16]; + + for (int i = 0; i < list_size; i++) { + const int x = ff_vvc_diag_scan_x[3][3][i]; + const int y = ff_vvc_diag_scan_y[3][3][i]; + + if (!(id >= SL_START_64x64 && x >= 4 && y >= 4)) + next_coef += aps->scaling_list_delta_coef[id][i]; + coeff[i] = next_coef; + } + } + } + + //dc + if (id >= SL_START_16x16) { + if (!aps->scaling_list_copy_mode_flag[id] && !aps->scaling_list_pred_mode_flag[id]) { + sl->scaling_matrix_dc_rec[id - SL_START_16x16] = 8; + } else if (!aps->scaling_list_pred_id_delta[id]) { + sl->scaling_matrix_dc_rec[id - SL_START_16x16] = 16; + } else { + const int ref_id = id - aps->scaling_list_pred_id_delta[id]; + if (ref_id >= SL_START_16x16) + dc += sl->scaling_matrix_dc_rec[ref_id - SL_START_16x16]; + else + dc += sl->scaling_matrix_rec[ref_id][0]; + sl->scaling_matrix_dc_rec[id - SL_START_16x16] = dc & 255; + } + } + + //ac + scaling_list = aps->scaling_list_copy_mode_flag[id] ? ff_vvc_scaling_list0 : coeff; + if (!aps->scaling_list_copy_mode_flag[id] && !aps->scaling_list_pred_mode_flag[id]) + pred = ff_vvc_scaling_pred_8; + else if (!aps->scaling_list_pred_id_delta[id]) + pred = ff_vvc_scaling_pred_16; + else + pred = sl->scaling_matrix_rec[id - aps->scaling_list_pred_id_delta[id]]; + for (int i = 0; i < list_size; i++) { + const int x = ff_vvc_diag_scan_x[log2_size][log2_size][i]; + const int y = ff_vvc_diag_scan_y[log2_size][log2_size][i]; + const int off = y * matrix_size + x; + sl->scaling_matrix_rec[id][off] = (pred[off] + scaling_list[i]) & 255; + } + } +} + +static int aps_decode_scaling(const VVCScalingList **scaling, const H266RawAPS *aps) +{ + VVCScalingList *sl = ff_refstruct_allocz(sizeof(*sl)); + if (!sl) + return AVERROR(ENOMEM); + + scaling_derive(sl, aps); + ff_refstruct_replace(scaling, sl); + ff_refstruct_unref(&sl); + + return 0; +} + +int ff_vvc_decode_aps(VVCParamSets *ps, const CodedBitstreamUnit *unit) +{ + const H266RawAPS *aps = unit->content_ref; + int ret = 0; + + if (!aps) + return AVERROR_INVALIDDATA; + + switch (aps->aps_params_type) { + case VVC_ASP_TYPE_ALF: + ret = aps_decode_alf(&ps->alf_list[aps->aps_adaptation_parameter_set_id], aps); + break; + case VVC_ASP_TYPE_LMCS: + ff_refstruct_replace(&ps->lmcs_list[aps->aps_adaptation_parameter_set_id], aps); + break; + case VVC_ASP_TYPE_SCALING: + ret = aps_decode_scaling(&ps->scaling_list[aps->aps_adaptation_parameter_set_id], aps); + break; + } + + return ret; +} + +static int sh_alf_aps(const VVCSH *sh, const VVCFrameParamSets *fps) +{ + if (!sh->r->sh_alf_enabled_flag) + return 0; + + for (int i = 0; i < sh->r->sh_num_alf_aps_ids_luma; i++) { + const VVCALF *alf_aps_luma = fps->alf_list[sh->r->sh_alf_aps_id_luma[i]]; + if (!alf_aps_luma) + return AVERROR_INVALIDDATA; + } + + if (sh->r->sh_alf_cb_enabled_flag || sh->r->sh_alf_cr_enabled_flag) { + const VVCALF *alf_aps_chroma = fps->alf_list[sh->r->sh_alf_aps_id_chroma]; + if (!alf_aps_chroma) + return AVERROR_INVALIDDATA; + } + + if (fps->sps->r->sps_ccalf_enabled_flag) { + if (sh->r->sh_alf_cc_cb_enabled_flag) { + const VVCALF *alf_aps_cc_cr = fps->alf_list[sh->r->sh_alf_cc_cb_aps_id]; + if (!alf_aps_cc_cr) + return AVERROR_INVALIDDATA; + } + if (sh->r->sh_alf_cc_cr_enabled_flag) { + const VVCALF *alf_aps_cc_cr = fps->alf_list[sh->r->sh_alf_cc_cr_aps_id]; + if (!alf_aps_cc_cr) + return AVERROR_INVALIDDATA; + } + } + + return 0; +} + +static void sh_slice_address(VVCSH *sh, const H266RawSPS *sps, const VVCPPS *pps) +{ + const int slice_address = sh->r->sh_slice_address; + + if (pps->r->pps_rect_slice_flag) { + int pic_level_slice_idx = slice_address; + for (int j = 0; j < sh->r->curr_subpic_idx; j++) + pic_level_slice_idx += pps->r->num_slices_in_subpic[j]; + sh->ctb_addr_in_curr_slice = pps->ctb_addr_in_slice + pps->slice_start_offset[pic_level_slice_idx]; + sh->num_ctus_in_curr_slice = pps->num_ctus_in_slice[pic_level_slice_idx]; + } else { + int tile_x = slice_address % pps->r->num_tile_columns; + int tile_y = slice_address / pps->r->num_tile_columns; + const int slice_start_ctb = pps->row_bd[tile_y] * pps->ctb_width + pps->col_bd[tile_x] * pps->r->row_height_val[tile_y]; + + sh->ctb_addr_in_curr_slice = pps->ctb_addr_in_slice + slice_start_ctb; + + sh->num_ctus_in_curr_slice = 0; + for (int tile_idx = slice_address; tile_idx <= slice_address + sh->r->sh_num_tiles_in_slice_minus1; tile_idx++) { + tile_x = tile_idx % pps->r->num_tile_columns; + tile_y = tile_idx / pps->r->num_tile_columns; + sh->num_ctus_in_curr_slice += pps->r->row_height_val[tile_y] * pps->r->col_width_val[tile_x]; + } + } +} + +static void sh_qp_y(VVCSH *sh, const H266RawPPS *pps, const H266RawPictureHeader *ph) +{ + const int init_qp = pps->pps_init_qp_minus26 + 26; + + if (!pps->pps_qp_delta_info_in_ph_flag) + sh->slice_qp_y = init_qp + sh->r->sh_qp_delta; + else + sh->slice_qp_y = init_qp + ph->ph_qp_delta; +} + +static void sh_inter(VVCSH *sh, const H266RawSPS *sps, const H266RawPPS *pps) +{ + const H266RawSliceHeader *rsh = sh->r; + + if (!pps->pps_wp_info_in_ph_flag && + ((pps->pps_weighted_pred_flag && IS_P(rsh)) || + (pps->pps_weighted_bipred_flag && IS_B(rsh)))) + pred_weight_table(&sh->pwt, &rsh->sh_pred_weight_table); +} + +static void sh_deblock_offsets(VVCSH *sh) +{ + const H266RawSliceHeader *r = sh->r; + + if (!r->sh_deblocking_filter_disabled_flag) { + sh->deblock.beta_offset[LUMA] = r->sh_luma_beta_offset_div2 << 1; + sh->deblock.tc_offset[LUMA] = r->sh_luma_tc_offset_div2 << 1; + sh->deblock.beta_offset[CB] = r->sh_cb_beta_offset_div2 << 1; + sh->deblock.tc_offset[CB] = r->sh_cb_tc_offset_div2 << 1; + sh->deblock.beta_offset[CR] = r->sh_cr_beta_offset_div2 << 1; + sh->deblock.tc_offset[CR] = r->sh_cr_tc_offset_div2 << 1; + } +} + +static void sh_partition_constraints(VVCSH *sh, const H266RawSPS *sps, const H266RawPictureHeader *ph) +{ + const int min_cb_log2_size_y = sps->sps_log2_min_luma_coding_block_size_minus2 + 2; + int min_qt_log2_size_y[2]; + + if (IS_I(sh->r)) { + min_qt_log2_size_y[LUMA] = (min_cb_log2_size_y + ph->ph_log2_diff_min_qt_min_cb_intra_slice_luma); + min_qt_log2_size_y[CHROMA] = (min_cb_log2_size_y + ph->ph_log2_diff_min_qt_min_cb_intra_slice_chroma); + + sh->max_bt_size[LUMA] = 1 << (min_qt_log2_size_y[LUMA] + ph->ph_log2_diff_max_bt_min_qt_intra_slice_luma); + sh->max_bt_size[CHROMA] = 1 << (min_qt_log2_size_y[CHROMA]+ ph->ph_log2_diff_max_bt_min_qt_intra_slice_chroma); + + sh->max_tt_size[LUMA] = 1 << (min_qt_log2_size_y[LUMA] + ph->ph_log2_diff_max_tt_min_qt_intra_slice_luma); + sh->max_tt_size[CHROMA] = 1 << (min_qt_log2_size_y[CHROMA]+ ph->ph_log2_diff_max_tt_min_qt_intra_slice_chroma); + + sh->max_mtt_depth[LUMA] = ph->ph_max_mtt_hierarchy_depth_intra_slice_luma; + sh->max_mtt_depth[CHROMA] = ph->ph_max_mtt_hierarchy_depth_intra_slice_chroma; + + sh->cu_qp_delta_subdiv = ph->ph_cu_qp_delta_subdiv_intra_slice; + sh->cu_chroma_qp_offset_subdiv = ph->ph_cu_chroma_qp_offset_subdiv_intra_slice; + } else { + for (int i = LUMA; i <= CHROMA; i++) { + min_qt_log2_size_y[i] = (min_cb_log2_size_y + ph->ph_log2_diff_min_qt_min_cb_inter_slice); + sh->max_bt_size[i] = 1 << (min_qt_log2_size_y[i] + ph->ph_log2_diff_max_bt_min_qt_inter_slice); + sh->max_tt_size[i] = 1 << (min_qt_log2_size_y[i] + ph->ph_log2_diff_max_tt_min_qt_inter_slice); + sh->max_mtt_depth[i] = ph->ph_max_mtt_hierarchy_depth_inter_slice; + } + + sh->cu_qp_delta_subdiv = ph->ph_cu_qp_delta_subdiv_inter_slice; + sh->cu_chroma_qp_offset_subdiv = ph->ph_cu_chroma_qp_offset_subdiv_inter_slice; + } + + sh->min_qt_size[LUMA] = 1 << min_qt_log2_size_y[LUMA]; + sh->min_qt_size[CHROMA] = 1 << min_qt_log2_size_y[CHROMA]; +} + +static void sh_entry_points(VVCSH *sh, const H266RawSPS *sps, const VVCPPS *pps) +{ + if (sps->sps_entry_point_offsets_present_flag) { + for (int i = 1, j = 0; i < sh->num_ctus_in_curr_slice; i++) { + const int pre_ctb_addr_x = sh->ctb_addr_in_curr_slice[i - 1] % pps->ctb_width; + const int pre_ctb_addr_y = sh->ctb_addr_in_curr_slice[i - 1] / pps->ctb_width; + const int ctb_addr_x = sh->ctb_addr_in_curr_slice[i] % pps->ctb_width; + const int ctb_addr_y = sh->ctb_addr_in_curr_slice[i] / pps->ctb_width; + if (pps->ctb_to_row_bd[ctb_addr_y] != pps->ctb_to_row_bd[pre_ctb_addr_y] || + pps->ctb_to_col_bd[ctb_addr_x] != pps->ctb_to_col_bd[pre_ctb_addr_x] || + (ctb_addr_y != pre_ctb_addr_y && sps->sps_entropy_coding_sync_enabled_flag)) { + sh->entry_point_start_ctu[j++] = i; + } + } + } +} + +static int sh_derive(VVCSH *sh, const VVCFrameParamSets *fps) +{ + const H266RawSPS *sps = fps->sps->r; + const H266RawPPS *pps = fps->pps->r; + const H266RawPictureHeader *ph = fps->ph.r; + int ret; + + sh_slice_address(sh, sps, fps->pps); + ret = sh_alf_aps(sh, fps); + if (ret < 0) + return ret; + sh_inter(sh, sps, pps); + sh_qp_y(sh, pps, ph); + sh_deblock_offsets(sh); + sh_partition_constraints(sh, sps, ph); + sh_entry_points(sh, sps, fps->pps); + + return 0; +} + +int ff_vvc_decode_sh(VVCSH *sh, const VVCFrameParamSets *fps, const CodedBitstreamUnit *unit) +{ + int ret; + + if (!fps->sps || !fps->pps) + return AVERROR_INVALIDDATA; + + ff_refstruct_replace(&sh->r, unit->content_ref); + + ret = sh_derive(sh, fps); + if (ret < 0) + return ret; + + return 0; +} diff --git a/libavcodec/vvc/vvc_ps.h b/libavcodec/vvc/vvc_ps.h new file mode 100644 index 00000000000..1164d0eab68 --- /dev/null +++ b/libavcodec/vvc/vvc_ps.h @@ -0,0 +1,262 @@ +/* + * VVC parameter set parser + * + * Copyright (C) 2023 Nuo Mi + * + * 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 + */ + +#ifndef AVCODEC_VVC_VVC_PS_H +#define AVCODEC_VVC_VVC_PS_H + +#include "libavcodec/cbs_h266.h" +#include "libavcodec/vvc.h" + +#define IS_IDR(s) ((s)->vcl_unit_type == VVC_IDR_W_RADL || (s)->vcl_unit_type == VVC_IDR_N_LP) +#define IS_CRA(s) ((s)->vcl_unit_type == VVC_CRA_NUT) +#define IS_IRAP(s) (IS_IDR(s) || IS_CRA(s)) +#define IS_GDR(s) ((s)->vcl_unit_type == VVC_GDR_NUT) +#define IS_CVSS(s) (IS_IRAP(s)|| IS_GDR(s)) +#define IS_CLVSS(s) (IS_CVSS(s) && s->no_output_before_recovery_flag) +#define IS_RASL(s) ((s)->vcl_unit_type == VVC_RASL_NUT) +#define IS_RADL(s) ((s)->vcl_unit_type == VVC_RADL_NUT) + +#define IS_I(rsh) ((rsh)->sh_slice_type == VVC_SLICE_TYPE_I) +#define IS_P(rsh) ((rsh)->sh_slice_type == VVC_SLICE_TYPE_P) +#define IS_B(rsh) ((rsh)->sh_slice_type == VVC_SLICE_TYPE_B) + +#define INV_POC INT_MIN +#define GDR_IS_RECOVERED(s) (s->gdr_recovery_point_poc == INV_POC) +#define GDR_SET_RECOVERED(s) (s->gdr_recovery_point_poc = INV_POC) + +#define LMCS_MAX_BIT_DEPTH 12 +#define LMCS_MAX_LUT_SIZE (1 << LMCS_MAX_BIT_DEPTH) +#define LMCS_MAX_BIN_SIZE 16 +#define LADF_MAX_INTERVAL 5 + +enum { + CHROMA_FORMAT_MONO, + CHROMA_FORMAT_420, + CHROMA_FORMAT_422, + CHROMA_FORMAT_444, +}; + +typedef struct VVCSPS { + const H266RawSPS *r; ///< RefStruct reference + + //derived values + uint8_t hshift[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t vshift[VVC_MAX_SAMPLE_ARRAYS]; + uint32_t max_pic_order_cnt_lsb; ///< MaxPicOrderCntLsb + + uint8_t pixel_shift; + enum AVPixelFormat pix_fmt; + + uint8_t bit_depth; ///< BitDepth + uint8_t qp_bd_offset; ///< QpBdOffset + uint8_t ctb_log2_size_y; ///< CtbLog2SizeY + uint8_t ctb_size_y; ///< CtbSizeY + uint8_t min_cb_log2_size_y; ///< MinCbLog2SizeY + uint8_t min_cb_size_y; ///< MinCbSizeY + uint8_t max_tb_size_y; ///< MaxTbSizeY + uint8_t max_ts_size; ///< MaxTsSize + uint8_t max_num_merge_cand; ///< MaxNumMergeCand + uint8_t max_num_ibc_merge_cand; ///< MaxNumIbcMergeCand + uint8_t max_num_gpm_merge_cand; ///< MaxNumGpmMergeCand + uint8_t num_ladf_intervals; ///< sps_num_ladf_intervals_minus2 + 2; + uint32_t ladf_interval_lower_bound[LADF_MAX_INTERVAL]; ///< SpsLadfIntervalLowerBound[] + uint8_t log2_parallel_merge_level; ///< sps_log2_parallel_merge_level_minus2 + 2; + uint8_t log2_transform_range; ///< Log2TransformRange + int8_t chroma_qp_table[3][VVC_MAX_POINTS_IN_QP_TABLE]; ///< ChromaQpTable +} VVCSPS; + +typedef struct DBParams { + int8_t beta_offset[VVC_MAX_SAMPLE_ARRAYS]; + int8_t tc_offset[VVC_MAX_SAMPLE_ARRAYS]; +} DBParams; + +typedef struct VVCPPS { + const H266RawPPS *r; ///< RefStruct reference + + //derived value; + int8_t chroma_qp_offset[3]; ///< pps_cb_qp_offset, pps_cr_qp_offset, pps_joint_cbcr_qp_offset_value + int8_t chroma_qp_offset_list[6][3]; ///< pps_cb_qp_offset_list, pps_cr_qp_offset_list, pps_joint_cbcr_qp_offset_list + + uint16_t width; + uint16_t height; + + uint16_t slice_start_offset[VVC_MAX_SLICES]; + uint16_t num_ctus_in_slice [VVC_MAX_SLICES]; + + uint16_t min_cb_width; + uint16_t min_cb_height; + + uint16_t ctb_width; + uint16_t ctb_height; + uint32_t ctb_count; + + uint16_t min_pu_width; + uint16_t min_pu_height; + uint16_t min_tu_width; + uint16_t min_tu_height; + + uint32_t *ctb_addr_in_slice; ///< CtbAddrInCurrSlice for entire picture + uint16_t *col_bd; + uint16_t *row_bd; + uint16_t *ctb_to_col_bd; + uint16_t *ctb_to_row_bd; + + uint16_t width32; ///< width in 32 pixels + uint16_t height32; ///< height in 32 pixels + uint16_t width64; ///< width in 64 pixels + uint16_t height64; ///< height in 64 pixels + + uint16_t ref_wraparound_offset; ///< PpsRefWraparoundOffset + +} VVCPPS; + +#define MAX_WEIGHTS 15 +typedef struct PredWeightTable { + uint8_t log2_denom[2]; ///< luma_log2_weight_denom, ChromaLog2WeightDenom + + uint8_t nb_weights[2]; ///< num_l0_weights, num_l1_weights + uint8_t weight_flag[2][2][MAX_WEIGHTS]; ///< luma_weight_l0_flag, chroma_weight_l0_flag, + ///< luma_weight_l1_flag, chroma_weight_l1_flag, + int16_t weight[2][VVC_MAX_SAMPLE_ARRAYS][MAX_WEIGHTS]; ///< LumaWeightL0, LumaWeightL1, ChromaWeightL0, ChromaWeightL1 + int16_t offset[2][VVC_MAX_SAMPLE_ARRAYS][MAX_WEIGHTS]; ///< luma_offset_l0, luma_offset_l1, ChromaOffsetL0, ChromaOffsetL1 +} PredWeightTable; + +typedef struct VVCPH { + const H266RawPictureHeader *r; + void *rref; ///< RefStruct reference, backing ph above + + //derived values + uint32_t max_num_subblock_merge_cand; ///< MaxNumSubblockMergeCand + int32_t poc; ///< PicOrderCntVal + PredWeightTable pwt; +} VVCPH; + +#define ALF_NUM_FILTERS_LUMA 25 +#define ALF_NUM_FILTERS_CHROMA 8 +#define ALF_NUM_FILTERS_CC 4 + +#define ALF_NUM_COEFF_LUMA 12 +#define ALF_NUM_COEFF_CHROMA 6 +#define ALF_NUM_COEFF_CC 7 + +typedef struct VVCALF { + int16_t luma_coeff [ALF_NUM_FILTERS_LUMA][ALF_NUM_COEFF_LUMA]; + uint8_t luma_clip_idx [ALF_NUM_FILTERS_LUMA][ALF_NUM_COEFF_LUMA]; + + uint8_t num_chroma_filters; + int16_t chroma_coeff [ALF_NUM_FILTERS_CHROMA][ALF_NUM_COEFF_CHROMA]; + uint8_t chroma_clip_idx[ALF_NUM_FILTERS_CHROMA][ALF_NUM_COEFF_CHROMA]; + + uint8_t num_cc_filters[2]; ///< alf_cc_cb_filters_signalled_minus1 + 1, alf_cc_cr_filters_signalled_minus1 + 1 + int16_t cc_coeff[2][ALF_NUM_FILTERS_CC][ALF_NUM_COEFF_CC]; +} VVCALF; + +enum { + SL_START_2x2 = 0, + SL_START_4x4 = 2, + SL_START_8x8 = 8, + SL_START_16x16 = 14, + SL_START_32x32 = 20, + SL_START_64x64 = 26, + SL_MAX_ID = 28, +}; + +#define SL_MAX_MATRIX_SIZE 8 + +typedef struct VVCScalingList { + uint8_t scaling_matrix_rec[SL_MAX_ID][SL_MAX_MATRIX_SIZE * SL_MAX_MATRIX_SIZE]; ///< ScalingMatrixRec + uint8_t scaling_matrix_dc_rec[SL_MAX_ID - SL_START_16x16]; ///< ScalingMatrixDcRec[refId − 14] +} VVCScalingList; + +typedef struct VVCLMCS { + uint8_t min_bin_idx; + uint8_t max_bin_idx; + + union { + uint8_t u8[LMCS_MAX_LUT_SIZE]; + uint16_t u16[LMCS_MAX_LUT_SIZE]; ///< for high bit-depth + } fwd_lut, inv_lut; + + uint16_t pivot[LMCS_MAX_BIN_SIZE + 1]; + uint16_t chroma_scale_coeff[LMCS_MAX_BIN_SIZE]; +} VVCLMCS; + +#define VVC_MAX_ALF_COUNT 8 +#define VVC_MAX_LMCS_COUNT 4 +#define VVC_MAX_SL_COUNT 8 + +typedef struct VVCParamSets { + const VVCSPS *sps_list[VVC_MAX_SPS_COUNT]; ///< RefStruct reference + const VVCPPS *pps_list[VVC_MAX_PPS_COUNT]; ///< RefStruct reference + const VVCALF *alf_list[VVC_MAX_ALF_COUNT]; ///< RefStruct reference + const H266RawAPS *lmcs_list[VVC_MAX_LMCS_COUNT]; ///< RefStruct reference + const VVCScalingList *scaling_list[VVC_MAX_SL_COUNT]; ///< RefStruct reference +} VVCParamSets; + +typedef struct VVCFrameParamSets { + const VVCSPS *sps; ///< RefStruct reference + const VVCPPS *pps; ///< RefStruct reference + VVCPH ph; + const VVCALF *alf_list[VVC_MAX_ALF_COUNT]; ///< RefStruct reference + VVCLMCS lmcs; + const VVCScalingList *sl; ///< RefStruct reference +} VVCFrameParamSets; + +typedef struct VVCSH { + const H266RawSliceHeader *r; ///< RefStruct reference + + // derived values + // ctu address + uint32_t num_ctus_in_curr_slice; ///< NumCtusInCurrSlice + const uint32_t* ctb_addr_in_curr_slice; ///< CtbAddrInCurrSlice + + // inter + PredWeightTable pwt; + int8_t ref_idx_sym[2]; ///< RefIdxSymL0, RefIdxSymL1 + + // qp_y + int8_t slice_qp_y; ///< SliceQpY + + // deblock_offsets + DBParams deblock; + + // partition constrains + uint8_t min_qt_size[2]; ///< MinQtSizeY, MinQtSizeC + uint8_t max_bt_size[2]; ///< MaxBtSizeY, MaxBtSizeC + uint8_t max_tt_size[2]; ///< MaxTtSizeY, MaxTtSizeC + uint8_t max_mtt_depth[2]; ///< MaxMttDepthY, MaxMttDepthC + uint8_t cu_qp_delta_subdiv; ///< CuQpDeltaSubdiv + uint8_t cu_chroma_qp_offset_subdiv; ///< CuChromaQpOffsetSubdiv + + // entries + uint32_t entry_point_start_ctu[VVC_MAX_ENTRY_POINTS]; ///< entry point start in ctu_addr +} VVCSH; + +struct VVCContext; + +int ff_vvc_decode_frame_ps(VVCFrameParamSets *fps, struct VVCContext *s); +int ff_vvc_decode_aps(VVCParamSets *ps, const CodedBitstreamUnit *unit); +int ff_vvc_decode_sh(VVCSH *sh, const VVCFrameParamSets *ps, const CodedBitstreamUnit *unit); +void ff_vvc_frame_ps_free(VVCFrameParamSets *fps); +void ff_vvc_ps_uninit(VVCParamSets *ps); + +#endif /* AVCODEC_VVC_VVC_PS_H */ diff --git a/libavcodec/vvc/vvc_refs.c b/libavcodec/vvc/vvc_refs.c new file mode 100644 index 00000000000..893048454ba --- /dev/null +++ b/libavcodec/vvc/vvc_refs.c @@ -0,0 +1,567 @@ +/* + * VVC reference management + * + * Copyright (C) 2023 Nuo Mi + * + * 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 + +#include "libavutil/thread.h" +#include "libavcodec/refstruct.h" +#include "libavcodec/thread.h" + +#include "vvc_refs.h" + +#define VVC_FRAME_FLAG_OUTPUT (1 << 0) +#define VVC_FRAME_FLAG_SHORT_REF (1 << 1) +#define VVC_FRAME_FLAG_LONG_REF (1 << 2) +#define VVC_FRAME_FLAG_BUMPING (1 << 3) + +typedef struct FrameProgress { + atomic_int progress[VVC_PROGRESS_LAST]; + VVCProgressListener *listener[VVC_PROGRESS_LAST]; + AVMutex lock; + AVCond cond; + uint8_t has_lock; + uint8_t has_cond; +} FrameProgress; + +void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, int flags) +{ + /* frame->frame can be NULL if context init failed */ + if (!frame->frame || !frame->frame->buf[0]) + return; + + frame->flags &= ~flags; + if (!frame->flags) { + av_frame_unref(frame->frame); + ff_refstruct_unref(&frame->progress); + + ff_refstruct_unref(&frame->tab_dmvr_mvf); + + ff_refstruct_unref(&frame->rpl); + frame->nb_rpl_elems = 0; + ff_refstruct_unref(&frame->rpl_tab); + + frame->collocated_ref = NULL; + } +} + +const RefPicList *ff_vvc_get_ref_list(const VVCFrameContext *fc, const VVCFrame *ref, int x0, int y0) +{ + const int x_cb = x0 >> fc->ps.sps->ctb_log2_size_y; + const int y_cb = y0 >> fc->ps.sps->ctb_log2_size_y; + const int pic_width_cb = fc->ps.pps->ctb_width; + const int ctb_addr_rs = y_cb * pic_width_cb + x_cb; + + return (const RefPicList *)ref->rpl_tab[ctb_addr_rs]; +} + +void ff_vvc_clear_refs(VVCFrameContext *fc) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) + ff_vvc_unref_frame(fc, &fc->DPB[i], + VVC_FRAME_FLAG_SHORT_REF | VVC_FRAME_FLAG_LONG_REF); +} + +void ff_vvc_flush_dpb(VVCFrameContext *fc) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) + ff_vvc_unref_frame(fc, &fc->DPB[i], ~0); +} + +static void free_progress(FFRefStructOpaque unused, void *obj) +{ + FrameProgress *p = (FrameProgress *)obj; + + if (p->has_cond) + ff_cond_destroy(&p->cond); + if (p->has_lock) + ff_mutex_destroy(&p->lock); +} + +static FrameProgress *alloc_progress(void) +{ + FrameProgress *p = ff_refstruct_alloc_ext(sizeof(*p), 0, NULL, free_progress); + + if (p) { + p->has_lock = !ff_mutex_init(&p->lock, NULL); + p->has_cond = !ff_cond_init(&p->cond, NULL); + if (!p->has_lock || !p->has_cond) + ff_refstruct_unref(&p); + } + return p; +} + +static VVCFrame *alloc_frame(VVCContext *s, VVCFrameContext *fc) +{ + const VVCPPS *pps = fc->ps.pps; + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + int ret; + VVCFrame *frame = &fc->DPB[i]; + if (frame->frame->buf[0]) + continue; + + ret = ff_thread_get_buffer(s->avctx, frame->frame, AV_GET_BUFFER_FLAG_REF); + if (ret < 0) + return NULL; + + frame->rpl = ff_refstruct_allocz(s->current_frame.nb_units * sizeof(RefPicListTab)); + if (!frame->rpl) + goto fail; + frame->nb_rpl_elems = s->current_frame.nb_units; + + frame->tab_dmvr_mvf = ff_refstruct_pool_get(fc->tab_dmvr_mvf_pool); + if (!frame->tab_dmvr_mvf) + goto fail; + + frame->rpl_tab = ff_refstruct_pool_get(fc->rpl_tab_pool); + if (!frame->rpl_tab) + goto fail; + frame->ctb_count = pps->ctb_width * pps->ctb_height; + for (int j = 0; j < frame->ctb_count; j++) + frame->rpl_tab[j] = frame->rpl; + + frame->progress = alloc_progress(); + if (!frame->progress) + goto fail; + + return frame; +fail: + ff_vvc_unref_frame(fc, frame, ~0); + return NULL; + } + av_log(s->avctx, AV_LOG_ERROR, "Error allocating frame, DPB full.\n"); + return NULL; +} + +int ff_vvc_set_new_ref(VVCContext *s, VVCFrameContext *fc, AVFrame **frame) +{ + const VVCPH *ph= &fc->ps.ph; + const int poc = ph->poc; + VVCFrame *ref; + + /* check that this POC doesn't already exist */ + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + + if (frame->frame->buf[0] && frame->sequence == s->seq_decode && + frame->poc == poc) { + av_log(s->avctx, AV_LOG_ERROR, "Duplicate POC in a sequence: %d.\n", poc); + return AVERROR_INVALIDDATA; + } + } + + ref = alloc_frame(s, fc); + if (!ref) + return AVERROR(ENOMEM); + + *frame = ref->frame; + fc->ref = ref; + + if (s->no_output_before_recovery_flag && (IS_RASL(s) || !GDR_IS_RECOVERED(s))) + ref->flags = 0; + else if (ph->r->ph_pic_output_flag) + ref->flags = VVC_FRAME_FLAG_OUTPUT; + + if (!ph->r->ph_non_ref_pic_flag) + ref->flags |= VVC_FRAME_FLAG_SHORT_REF; + + ref->poc = poc; + ref->sequence = s->seq_decode; + ref->frame->crop_left = fc->ps.pps->r->pps_conf_win_left_offset << fc->ps.sps->hshift[CHROMA]; + ref->frame->crop_right = fc->ps.pps->r->pps_conf_win_right_offset << fc->ps.sps->hshift[CHROMA]; + ref->frame->crop_top = fc->ps.pps->r->pps_conf_win_top_offset << fc->ps.sps->vshift[CHROMA]; + ref->frame->crop_bottom = fc->ps.pps->r->pps_conf_win_bottom_offset << fc->ps.sps->vshift[CHROMA]; + + return 0; +} + +int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, AVFrame *out, const int no_output_of_prior_pics_flag, int flush) +{ + const VVCSPS *sps = fc->ps.sps; + do { + int nb_output = 0; + int min_poc = INT_MAX; + int min_idx, ret; + + if (no_output_of_prior_pics_flag) { + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if (!(frame->flags & VVC_FRAME_FLAG_BUMPING) && frame->poc != fc->ps.ph.poc && + frame->sequence == s->seq_output) { + ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT); + } + } + } + + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if ((frame->flags & VVC_FRAME_FLAG_OUTPUT) && + frame->sequence == s->seq_output) { + nb_output++; + if (frame->poc < min_poc || nb_output == 1) { + min_poc = frame->poc; + min_idx = i; + } + } + } + + /* wait for more frames before output */ + if (!flush && s->seq_output == s->seq_decode && sps && + nb_output <= sps->r->sps_dpb_params.dpb_max_dec_pic_buffering_minus1[sps->r->sps_max_sublayers_minus1] + 1) + return 0; + + if (nb_output) { + VVCFrame *frame = &fc->DPB[min_idx]; + + ret = av_frame_ref(out, frame->frame); + if (frame->flags & VVC_FRAME_FLAG_BUMPING) + ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT | VVC_FRAME_FLAG_BUMPING); + else + ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT); + if (ret < 0) + return ret; + + av_log(s->avctx, AV_LOG_DEBUG, + "Output frame with POC %d.\n", frame->poc); + return 1; + } + + if (s->seq_output != s->seq_decode) + s->seq_output = (s->seq_output + 1) & 0xff; + else + break; + } while (1); + return 0; +} + +void ff_vvc_bump_frame(VVCContext *s, VVCFrameContext *fc) +{ + const VVCSPS *sps = fc->ps.sps; + const int poc = fc->ps.ph.poc; + int dpb = 0; + int min_poc = INT_MAX; + + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if ((frame->flags) && + frame->sequence == s->seq_output && + frame->poc != poc) { + dpb++; + } + } + + if (sps && dpb >= sps->r->sps_dpb_params.dpb_max_dec_pic_buffering_minus1[sps->r->sps_max_sublayers_minus1] + 1) { + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if ((frame->flags) && + frame->sequence == s->seq_output && + frame->poc != poc) { + if (frame->flags == VVC_FRAME_FLAG_OUTPUT && frame->poc < min_poc) { + min_poc = frame->poc; + } + } + } + + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + if (frame->flags & VVC_FRAME_FLAG_OUTPUT && + frame->sequence == s->seq_output && + frame->poc <= min_poc) { + frame->flags |= VVC_FRAME_FLAG_BUMPING; + } + } + + dpb--; + } +} + +static VVCFrame *find_ref_idx(VVCContext *s, VVCFrameContext *fc, int poc, uint8_t use_msb) +{ + const unsigned mask = use_msb ? ~0 : fc->ps.sps->max_pic_order_cnt_lsb - 1; + + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *ref = &fc->DPB[i]; + if (ref->frame->buf[0] && ref->sequence == s->seq_decode) { + if ((ref->poc & mask) == poc) + return ref; + } + } + return NULL; +} + +static void mark_ref(VVCFrame *frame, int flag) +{ + frame->flags &= ~(VVC_FRAME_FLAG_LONG_REF | VVC_FRAME_FLAG_SHORT_REF); + frame->flags |= flag; +} + +static VVCFrame *generate_missing_ref(VVCContext *s, VVCFrameContext *fc, int poc) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + VVCFrame *frame; + + frame = alloc_frame(s, fc); + if (!frame) + return NULL; + + if (!s->avctx->hwaccel) { + if (!sps->pixel_shift) { + for (int i = 0; frame->frame->buf[i]; i++) + memset(frame->frame->buf[i]->data, 1 << (sps->bit_depth - 1), + frame->frame->buf[i]->size); + } else { + for (int i = 0; frame->frame->data[i]; i++) + for (int y = 0; y < (pps->height >> sps->vshift[i]); y++) { + uint8_t *dst = frame->frame->data[i] + y * frame->frame->linesize[i]; + AV_WN16(dst, 1 << (sps->bit_depth - 1)); + av_memcpy_backptr(dst + 2, 2, 2*(pps->width >> sps->hshift[i]) - 2); + } + } + } + + frame->poc = poc; + frame->sequence = s->seq_decode; + frame->flags = 0; + + ff_vvc_report_frame_finished(frame); + + return frame; +} + +/* add a reference with the given poc to the list and mark it as used in DPB */ +static int add_candidate_ref(VVCContext *s, VVCFrameContext *fc, RefPicList *list, + int poc, int ref_flag, uint8_t use_msb) +{ + VVCFrame *ref = find_ref_idx(s, fc, poc, use_msb); + + if (ref == fc->ref || list->nb_refs >= VVC_MAX_REF_ENTRIES) + return AVERROR_INVALIDDATA; + + if (!ref) { + ref = generate_missing_ref(s, fc, poc); + if (!ref) + return AVERROR(ENOMEM); + } + + list->list[list->nb_refs] = poc; + list->ref[list->nb_refs] = ref; + list->isLongTerm[list->nb_refs] = ref_flag & VVC_FRAME_FLAG_LONG_REF; + list->nb_refs++; + + mark_ref(ref, ref_flag); + return 0; +} + +static int init_slice_rpl(const VVCFrameContext *fc, SliceContext *sc) +{ + VVCFrame *frame = fc->ref; + const VVCSH *sh = &sc->sh; + + if (sc->slice_idx >= frame->nb_rpl_elems) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < sh->num_ctus_in_curr_slice; i++) { + const int rs = sh->ctb_addr_in_curr_slice[i]; + frame->rpl_tab[rs] = frame->rpl + sc->slice_idx; + } + + sc->rpl = frame->rpl_tab[sh->ctb_addr_in_curr_slice[0]]->refPicList; + + return 0; +} + +static int delta_poc_st(const H266RefPicListStruct *rpls, + const int lx, const int i, const VVCSPS *sps) +{ + int abs_delta_poc_st = rpls->abs_delta_poc_st[i]; + if (!((sps->r->sps_weighted_pred_flag || + sps->r->sps_weighted_bipred_flag) && i != 0)) + abs_delta_poc_st++; + return (1 - 2 * rpls->strp_entry_sign_flag[i]) * abs_delta_poc_st; +} + +static int poc_lt(int *prev_delta_poc_msb, const int poc, const H266RefPicLists *ref_lists, + const int lx, const int j, const int max_poc_lsb) +{ + const H266RefPicListStruct *rpls = ref_lists->rpl_ref_list + lx; + int lt_poc = rpls->ltrp_in_header_flag ? ref_lists->poc_lsb_lt[lx][j] : rpls->rpls_poc_lsb_lt[j]; + + if (ref_lists->delta_poc_msb_cycle_present_flag[lx][j]) { + const uint32_t delta = ref_lists->delta_poc_msb_cycle_lt[lx][j] + *prev_delta_poc_msb; + lt_poc += poc - delta * max_poc_lsb - (poc & (max_poc_lsb - 1)); + *prev_delta_poc_msb = delta; + } + return lt_poc; +} + +int ff_vvc_slice_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc) +{ + const VVCSPS *sps = fc->ps.sps; + const H266RawPPS *pps = fc->ps.pps->r; + const VVCPH *ph = &fc->ps.ph; + const H266RawSliceHeader *rsh = sc->sh.r; + const int max_poc_lsb = sps->max_pic_order_cnt_lsb; + const H266RefPicLists *ref_lists = + pps->pps_rpl_info_in_ph_flag ? &ph->r->ph_ref_pic_lists : &rsh->sh_ref_pic_lists; + int ret = 0; + + ret = init_slice_rpl(fc, sc); + if (ret < 0) + return ret; + + for (int lx = L0; lx <= L1; lx++) { + const H266RefPicListStruct *rpls = ref_lists->rpl_ref_list + lx; + RefPicList *rpl = sc->rpl + lx; + int poc_base = ph->poc; + int prev_delta_poc_msb = 0; + + rpl->nb_refs = 0; + for (int i = 0, j = 0; i < rpls->num_ref_entries; i++) { + int poc; + if (!rpls->inter_layer_ref_pic_flag[i]) { + int use_msb = 1; + int ref_flag; + if (rpls->st_ref_pic_flag[i]) { + poc = poc_base + delta_poc_st(rpls, lx, i, sps); + poc_base = poc; + ref_flag = VVC_FRAME_FLAG_SHORT_REF; + } else { + use_msb = ref_lists->delta_poc_msb_cycle_present_flag[lx][j]; + poc = poc_lt(&prev_delta_poc_msb, ph->poc, ref_lists, lx, j, max_poc_lsb); + ref_flag = VVC_FRAME_FLAG_LONG_REF; + j++; + } + ret = add_candidate_ref(s, fc, rpl, poc, ref_flag, use_msb); + if (ret < 0) + return ret; + } else { + // OPI_B_3.bit and VPS_A_3.bit should cover this + avpriv_report_missing_feature(fc->log_ctx, "Inter layer ref"); + ret = AVERROR_PATCHWELCOME; + return ret; + } + } + if ((!rsh->sh_collocated_from_l0_flag) == lx && + rsh->sh_collocated_ref_idx < rpl->nb_refs) + fc->ref->collocated_ref = rpl->ref[rsh->sh_collocated_ref_idx]; + } + return 0; +} + +int ff_vvc_frame_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc) +{ + int ret = 0; + + /* clear the reference flags on all frames except the current one */ + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + VVCFrame *frame = &fc->DPB[i]; + + if (frame == fc->ref) + continue; + + mark_ref(frame, 0); + } + + if ((ret = ff_vvc_slice_rpl(s, fc, sc)) < 0) + goto fail; + +fail: + /* release any frames that are now unused */ + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) + ff_vvc_unref_frame(fc, &fc->DPB[i], 0); + return ret; +} + +void ff_vvc_report_frame_finished(VVCFrame *frame) +{ + ff_vvc_report_progress(frame, VVC_PROGRESS_MV, INT_MAX); + ff_vvc_report_progress(frame, VVC_PROGRESS_PIXEL, INT_MAX); +} + +static int is_progress_done(const FrameProgress *p, const VVCProgressListener *l) +{ + return p->progress[l->vp] > l->y; +} + +static void add_listener(VVCProgressListener **prev, VVCProgressListener *l) +{ + l->next = *prev; + *prev = l; +} + +static VVCProgressListener* remove_listener(VVCProgressListener **prev, VVCProgressListener *l) +{ + *prev = l->next; + l->next = NULL; + return l; +} + +static VVCProgressListener* get_done_listener(FrameProgress *p, const VVCProgress vp) +{ + VVCProgressListener *list = NULL; + VVCProgressListener **prev = &p->listener[vp]; + + while (*prev) { + if (is_progress_done(p, *prev)) { + VVCProgressListener *l = remove_listener(prev, *prev); + add_listener(&list, l); + } else { + prev = &(*prev)->next; + } + } + return list; +} + +void ff_vvc_report_progress(VVCFrame *frame, const VVCProgress vp, const int y) +{ + FrameProgress *p = frame->progress; + VVCProgressListener *l = NULL; + + ff_mutex_lock(&p->lock); + + av_assert0(p->progress[vp] < y || p->progress[vp] == INT_MAX); + p->progress[vp] = y; + l = get_done_listener(p, vp); + ff_cond_signal(&p->cond); + + ff_mutex_unlock(&p->lock); + + while (l) { + l->progress_done(l); + l = l->next; + } +} + +void ff_vvc_add_progress_listener(VVCFrame *frame, VVCProgressListener *l) +{ + FrameProgress *p = frame->progress; + + ff_mutex_lock(&p->lock); + + if (is_progress_done(p, l)) { + ff_mutex_unlock(&p->lock); + l->progress_done(l); + } else { + add_listener(p->listener + l->vp, l); + ff_mutex_unlock(&p->lock); + } +} diff --git a/libavcodec/vvc/vvc_refs.h b/libavcodec/vvc/vvc_refs.h new file mode 100644 index 00000000000..eba4422fb4e --- /dev/null +++ b/libavcodec/vvc/vvc_refs.h @@ -0,0 +1,58 @@ +/* + * VVC reference management + * + * Copyright (C) 2023 Nuo Mi + * + * 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 + */ + +#ifndef AVCODEC_VVC_VVC_REFS_H +#define AVCODEC_VVC_VVC_REFS_H + +#include "vvcdec.h" + +int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, struct AVFrame *out, int no_output_of_prior_pics_flag, int flush); +void ff_vvc_bump_frame(VVCContext *s, VVCFrameContext *fc); +int ff_vvc_set_new_ref(VVCContext *s, VVCFrameContext *fc, struct AVFrame **frame); +const RefPicList *ff_vvc_get_ref_list(const VVCFrameContext *fc, const VVCFrame *ref, int x0, int y0); +int ff_vvc_frame_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc); +int ff_vvc_slice_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc); +void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, int flags); +void ff_vvc_clear_refs(VVCFrameContext *fc); +void ff_vvc_flush_dpb(VVCFrameContext *fc); + +typedef enum VVCProgress { + VVC_PROGRESS_MV, + VVC_PROGRESS_PIXEL, + VVC_PROGRESS_LAST, +} VVCProgress; + +typedef struct VVCProgressListener VVCProgressListener; +typedef void (*progress_done_fn)(VVCProgressListener *l); + +struct VVCProgressListener { + VVCProgress vp; + int y; + progress_done_fn progress_done; + VVCProgressListener *next; //used by ff_vvc_add_progress_listener only +}; + +void ff_vvc_report_frame_finished(VVCFrame *frame); +void ff_vvc_report_progress(VVCFrame *frame, VVCProgress vp, int y); +void ff_vvc_add_progress_listener(VVCFrame *frame, VVCProgressListener *l); + +#endif // AVCODEC_VVC_VVC_REFS_H diff --git a/libavcodec/vvc/vvc_thread.c b/libavcodec/vvc/vvc_thread.c new file mode 100644 index 00000000000..31c931f050d --- /dev/null +++ b/libavcodec/vvc/vvc_thread.c @@ -0,0 +1,828 @@ +/* + * VVC thread logic + * + * Copyright (C) 2023 Nuo Mi + * + * 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 + +#include "libavutil/executor.h" +#include "libavutil/thread.h" + +#include "vvc_thread.h" +#include "vvc_ctu.h" +#include "vvc_filter.h" +#include "vvc_inter.h" +#include "vvc_intra.h" +#include "vvc_refs.h" + +typedef struct ProgressListener { + VVCProgressListener l; + struct VVCTask *task; + VVCContext *s; +} ProgressListener; + +typedef enum VVCTaskStage { + VVC_TASK_STAGE_PARSE, + VVC_TASK_STAGE_INTER, + VVC_TASK_STAGE_RECON, + VVC_TASK_STAGE_LMCS, + VVC_TASK_STAGE_DEBLOCK_V, + VVC_TASK_STAGE_DEBLOCK_H, + VVC_TASK_STAGE_SAO, + VVC_TASK_STAGE_ALF, + VVC_TASK_STAGE_LAST +} VVCTaskStage; + +typedef struct VVCTask { + union { + struct VVCTask *next; //for executor debug only + AVTask task; + } u; + + VVCTaskStage stage; + + // ctu x, y, and raster scan order + int rx, ry, rs; + VVCFrameContext *fc; + + ProgressListener col_listener; + ProgressListener listener[2][VVC_MAX_REF_ENTRIES]; + + // for parse task only + SliceContext *sc; + EntryPoint *ep; + int ctu_idx; //ctu idx in the current slice + + // tasks with target scores met are ready for scheduling + atomic_uchar score[VVC_TASK_STAGE_LAST]; + atomic_uchar target_inter_score; +} VVCTask; + +typedef struct VVCRowThread { + atomic_int col_progress[VVC_PROGRESS_LAST]; +} VVCRowThread; + +typedef struct VVCFrameThread { + // error return for tasks + atomic_int ret; + + VVCRowThread *rows; + VVCTask *tasks; + + int ctu_size; + int ctu_width; + int ctu_height; + int ctu_count; + + //protected by lock + atomic_int nb_scheduled_tasks; + atomic_int nb_scheduled_listeners; + + int row_progress[VVC_PROGRESS_LAST]; + + AVMutex lock; + AVCond cond; +} VVCFrameThread; + +static void add_task(VVCContext *s, VVCTask *t) +{ + VVCFrameThread *ft = t->fc->ft; + + atomic_fetch_add(&ft->nb_scheduled_tasks, 1); + + av_executor_execute(s->executor, &t->u.task); +} + +static void task_init(VVCTask *t, VVCTaskStage stage, VVCFrameContext *fc, const int rx, const int ry) +{ + memset(t, 0, sizeof(*t)); + t->stage = stage; + t->fc = fc; + t->rx = rx; + t->ry = ry; + t->rs = ry * fc->ft->ctu_width + rx; + for (int i = 0; i < FF_ARRAY_ELEMS(t->score); i++) + atomic_store(t->score + i, 0); + atomic_store(&t->target_inter_score, 0); +} + +static void task_init_parse(VVCTask *t, SliceContext *sc, EntryPoint *ep, const int ctu_idx) +{ + t->sc = sc; + t->ep = ep; + t->ctu_idx = ctu_idx; +} + +static uint8_t task_add_score(VVCTask *t, const VVCTaskStage stage) +{ + return atomic_fetch_add(&t->score[stage], 1) + 1; +} + +static uint8_t task_get_score(VVCTask *t, const VVCTaskStage stage) +{ + return atomic_load(&t->score[stage]); +} + +//first row in tile or slice +static int is_first_row(const VVCFrameContext *fc, const int rx, const int ry) +{ + const VVCFrameThread *ft = fc->ft; + const VVCPPS *pps = fc->ps.pps; + + if (ry != pps->ctb_to_row_bd[ry]) { + const int rs = ry * ft->ctu_width + rx; + return fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - ft->ctu_width]; + } + return 1; +} + +static int task_has_target_score(VVCTask *t, const VVCTaskStage stage, const uint8_t score) +{ + // l:left, r:right, t: top, b: bottom + static const uint8_t target_score[] = + { + 2, //VVC_TASK_STAGE_RECON, need l + rt recon + 3, //VVC_TASK_STAGE_LMCS, need r + b + rb recon + 1, //VVC_TASK_STAGE_DEBLOCK_V, need l deblock v + 2, //VVC_TASK_STAGE_DEBLOCK_H, need r deblock v + t deblock h + 5, //VVC_TASK_STAGE_SAO, need l + r + lb + b + rb deblock h + 8, //VVC_TASK_STAGE_ALF, need sao around the ctu + }; + uint8_t target = 0; + VVCFrameContext *fc = t->fc; + + if (stage == VVC_TASK_STAGE_PARSE) { + const H266RawSPS *rsps = fc->ps.sps->r; + const int wpp = rsps->sps_entropy_coding_sync_enabled_flag && !is_first_row(fc, t->rx, t->ry); + target = 2 + wpp - 1; //left parse + colocation + wpp - no previous stage + } else if (stage == VVC_TASK_STAGE_INTER) { + target = atomic_load(&t->target_inter_score); + } else { + target = target_score[stage - VVC_TASK_STAGE_RECON]; + } + + //+1 for previous stage + av_assert0(score <= target + 1); + return score == target + 1; +} + +static void frame_thread_add_score(VVCContext *s, VVCFrameThread *ft, + const int rx, const int ry, const VVCTaskStage stage) +{ + VVCTask *t = ft->tasks + ft->ctu_width * ry + rx; + uint8_t score; + + if (rx < 0 || rx >= ft->ctu_width || ry < 0 || ry >= ft->ctu_height) + return; + + score = task_add_score(t, stage); + if (task_has_target_score(t, stage, score)) { + av_assert0(s); + av_assert0(stage == t->stage); + add_task(s, t); + } +} + +static void sheduled_done(VVCFrameThread *ft, atomic_int *scheduled) +{ + if (atomic_fetch_sub(scheduled, 1) == 1) { + ff_mutex_lock(&ft->lock); + ff_cond_signal(&ft->cond); + ff_mutex_unlock(&ft->lock); + } +} + +static void progress_done(VVCProgressListener *_l, const int type) +{ + const ProgressListener *l = (ProgressListener *)_l; + const VVCTask *t = l->task; + VVCFrameThread *ft = t->fc->ft; + + frame_thread_add_score(l->s, ft, t->rx, t->ry, type); + sheduled_done(ft, &ft->nb_scheduled_listeners); +} + +static void pixel_done(VVCProgressListener *l) +{ + progress_done(l, VVC_TASK_STAGE_INTER); +} + +static void mv_done(VVCProgressListener *l) +{ + progress_done(l, VVC_TASK_STAGE_PARSE); +} + +static void listener_init(ProgressListener *l, VVCTask *t, VVCContext *s, const VVCProgress vp, const int y) +{ + const int is_inter = vp == VVC_PROGRESS_PIXEL; + + l->task = t; + l->s = s; + l->l.vp = vp; + l->l.y = y; + l->l.progress_done = is_inter ? pixel_done : mv_done; + if (is_inter) + atomic_fetch_add(&t->target_inter_score, 1); +} + +static void add_progress_listener(VVCFrame *ref, ProgressListener *l, + VVCTask *t, VVCContext *s, const VVCProgress vp, const int y) +{ + VVCFrameThread *ft = t->fc->ft; + + atomic_fetch_add(&ft->nb_scheduled_listeners, 1); + listener_init(l, t, s, vp, y); + ff_vvc_add_progress_listener(ref, (VVCProgressListener*)l); +} + +static void schedule_next_parse(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, const VVCTask *t) +{ + VVCFrameThread *ft = fc->ft; + EntryPoint *ep = t->ep; + const VVCSPS *sps = fc->ps.sps; + + if (sps->r->sps_entropy_coding_sync_enabled_flag) { + if (t->rx == fc->ps.pps->ctb_to_col_bd[t->rx]) { + EntryPoint *next = ep + 1; + if (next < sc->eps + sc->nb_eps && !is_first_row(fc, t->rx, t->ry + 1)) { + memcpy(next->cabac_state, ep->cabac_state, sizeof(next->cabac_state)); + ff_vvc_ep_init_stat_coeff(next, sps->bit_depth, sps->r->sps_persistent_rice_adaptation_enabled_flag); + } + } + if (t->ry + 1 < ft->ctu_height && !is_first_row(fc, t->rx, t->ry + 1)) + frame_thread_add_score(s, ft, t->rx, t->ry + 1, VVC_TASK_STAGE_PARSE); + } + + if (t->ctu_idx + 1 < t->ep->ctu_end) { + const int next_rs = sc->sh.ctb_addr_in_curr_slice[t->ctu_idx + 1]; + const int next_rx = next_rs % ft->ctu_width; + const int next_ry = next_rs / ft->ctu_width; + frame_thread_add_score(s, ft, next_rx, next_ry, VVC_TASK_STAGE_PARSE); + } +} + +static void schedule_inter(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc, VVCTask *t, const int rs) +{ + const VVCSH *sh = &sc->sh; + + if (!IS_I(sh->r)) { + CTU *ctu = fc->tab.ctus + rs; + for (int lx = 0; lx < 2; lx++) { + for (int i = 0; i < sh->r->num_ref_idx_active[lx]; i++) { + const int y = ctu->max_y[lx][i]; + VVCFrame *ref = sc->rpl[lx].ref[i]; + if (ref && y >= 0) + add_progress_listener(ref, &t->listener[lx][i], t, s, VVC_PROGRESS_PIXEL, y + LUMA_EXTRA_AFTER); + } + } + } +} + +static void parse_task_done(VVCContext *s, VVCFrameContext *fc, const int rx, const int ry) +{ + VVCFrameThread *ft = fc->ft; + const int rs = ry * ft->ctu_width + rx; + const int slice_idx = fc->tab.slice_idx[rs]; + VVCTask *t = ft->tasks + rs; + const SliceContext *sc = fc->slices[slice_idx]; + + schedule_next_parse(s, fc, sc, t); + schedule_inter(s, fc, sc, t, rs); +} + +static void task_stage_done(const VVCTask *t, VVCContext *s) +{ + VVCFrameContext *fc = t->fc; + VVCFrameThread *ft = fc->ft; + const VVCTaskStage stage = t->stage; + +#define ADD(dx, dy, stage) frame_thread_add_score(s, ft, t->rx + (dx), t->ry + (dy), stage) + + //this is a reserve map of ready_score, ordered by zigzag + if (stage == VVC_TASK_STAGE_PARSE) { + parse_task_done(s, fc, t->rx, t->ry); + } else if (stage == VVC_TASK_STAGE_RECON) { + ADD(-1, 1, VVC_TASK_STAGE_RECON); + ADD( 1, 0, VVC_TASK_STAGE_RECON); + ADD(-1, -1, VVC_TASK_STAGE_LMCS); + ADD( 0, -1, VVC_TASK_STAGE_LMCS); + ADD(-1, 0, VVC_TASK_STAGE_LMCS); + } else if (stage == VVC_TASK_STAGE_DEBLOCK_V) { + ADD( 1, 0, VVC_TASK_STAGE_DEBLOCK_V); + ADD(-1, 0, VVC_TASK_STAGE_DEBLOCK_H); + } else if (stage == VVC_TASK_STAGE_DEBLOCK_H) { + ADD( 0, 1, VVC_TASK_STAGE_DEBLOCK_H); + ADD(-1, -1, VVC_TASK_STAGE_SAO); + ADD( 0, -1, VVC_TASK_STAGE_SAO); + ADD(-1, 0, VVC_TASK_STAGE_SAO); + ADD( 1, -1, VVC_TASK_STAGE_SAO); + ADD( 1, 0, VVC_TASK_STAGE_SAO); + } else if (stage == VVC_TASK_STAGE_SAO) { + ADD(-1, -1, VVC_TASK_STAGE_ALF); + ADD( 0, -1, VVC_TASK_STAGE_ALF); + ADD(-1, 0, VVC_TASK_STAGE_ALF); + ADD( 1, -1, VVC_TASK_STAGE_ALF); + ADD(-1, 1, VVC_TASK_STAGE_ALF); + ADD( 1, 0, VVC_TASK_STAGE_ALF); + ADD( 0, 1, VVC_TASK_STAGE_ALF); + ADD( 1, 1, VVC_TASK_STAGE_ALF); + } +} + +static int task_is_stage_ready(VVCTask *t, int add) +{ + const VVCTaskStage stage = t->stage; + uint8_t score; + if (stage > VVC_TASK_STAGE_ALF) + return 0; + score = task_get_score(t, stage) + add; + return task_has_target_score(t, stage, score); +} + +static int task_ready(const AVTask *_t, void *user_data) +{ + VVCTask *t = (VVCTask*)_t; + + return task_is_stage_ready(t, 0); +} + +#define CHECK(a, b) \ + do { \ + if ((a) != (b)) \ + return (a) < (b); \ + } while (0) + +static int task_priority_higher(const AVTask *_a, const AVTask *_b) +{ + const VVCTask *a = (const VVCTask*)_a; + const VVCTask *b = (const VVCTask*)_b; + + CHECK(a->fc->decode_order, b->fc->decode_order); //decode order + + if (a->stage == VVC_TASK_STAGE_PARSE || b->stage == VVC_TASK_STAGE_PARSE) { + CHECK(a->stage, b->stage); + CHECK(a->ry, b->ry); + return a->rx < b->rx; + } + + CHECK(a->rx + a->ry + a->stage, b->rx + b->ry + b->stage); //zigzag with type + CHECK(a->rx + a->ry, b->rx + b->ry); //zigzag + return a->ry < b->ry; +} + +static void report_frame_progress(VVCFrameContext *fc, + const int ry, const VVCProgress idx) +{ + VVCFrameThread *ft = fc->ft; + const int ctu_size = ft->ctu_size; + int old; + + if (atomic_fetch_add(&ft->rows[ry].col_progress[idx], 1) == ft->ctu_width - 1) { + int y; + ff_mutex_lock(&ft->lock); + y = old = ft->row_progress[idx]; + while (y < ft->ctu_height && atomic_load(&ft->rows[y].col_progress[idx]) == ft->ctu_width) + y++; + if (old != y) { + const int progress = y == ft->ctu_height ? INT_MAX : y * ctu_size; + ft->row_progress[idx] = y; + ff_vvc_report_progress(fc->ref, idx, progress); + } + ff_mutex_unlock(&ft->lock); + } +} + +static int run_parse(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + int ret; + VVCFrameContext *fc = lc->fc; + const int rs = t->rs; + const CTU *ctu = fc->tab.ctus + rs; + + lc->sc = t->sc; + lc->ep = t->ep; + + ret = ff_vvc_coding_tree_unit(lc, t->ctu_idx, rs, t->rx, t->ry); + if (ret < 0) + return ret; + + if (!ctu->has_dmvr) + report_frame_progress(lc->fc, t->ry, VVC_PROGRESS_MV); + + return 0; +} + +static int run_inter(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->ft; + const int rs = t->ry * ft->ctu_width + t->rx; + const CTU *ctu = fc->tab.ctus + rs; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + ff_vvc_predict_inter(lc, rs); + } + + if (ctu->has_dmvr) + report_frame_progress(fc, t->ry, VVC_PROGRESS_MV); + + return 0; +} + +static int run_recon(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + const int rs = t->rs; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + ff_vvc_reconstruct(lc, rs, t->rx, t->ry); + } + + return 0; +} + +static int run_lmcs(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->ft; + const int ctu_size = ft->ctu_size; + const int x0 = t->rx * ctu_size; + const int y0 = t->ry * ctu_size; + const int rs = t->ry * ft->ctu_width + t->rx; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + ff_vvc_lmcs_filter(lc, x0, y0); + } + + return 0; +} + +static int run_deblock_v(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->ft; + const int rs = t->ry * ft->ctu_width + t->rx; + const int ctb_size = ft->ctu_size; + const int x0 = t->rx * ctb_size; + const int y0 = t->ry * ctb_size; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + if (!lc->sc->sh.r->sh_deblocking_filter_disabled_flag) { + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs); + ff_vvc_deblock_vertical(lc, x0, y0); + } + } + + return 0; +} + +static int run_deblock_h(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->ft; + const int ctb_size = ft->ctu_size; + const int rs = t->ry * ft->ctu_width + t->rx; + const int x0 = t->rx * ctb_size; + const int y0 = t->ry * ctb_size; + const int slice_idx = fc->tab.slice_idx[rs]; + + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + if (!lc->sc->sh.r->sh_deblocking_filter_disabled_flag) { + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs); + ff_vvc_deblock_horizontal(lc, x0, y0); + } + if (fc->ps.sps->r->sps_sao_enabled_flag) + ff_vvc_sao_copy_ctb_to_hv(lc, t->rx, t->ry, t->ry == ft->ctu_height - 1); + } + + return 0; +} + +static int run_sao(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->ft; + const int rs = t->ry * fc->ps.pps->ctb_width + t->rx; + const int ctb_size = ft->ctu_size; + const int x0 = t->rx * ctb_size; + const int y0 = t->ry * ctb_size; + + if (fc->ps.sps->r->sps_sao_enabled_flag) { + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs); + ff_vvc_sao_filter(lc, x0, y0); + } + + if (fc->ps.sps->r->sps_alf_enabled_flag) + ff_vvc_alf_copy_ctu_to_hv(lc, x0, y0); + + return 0; +} + +static int run_alf(VVCContext *s, VVCLocalContext *lc, VVCTask *t) +{ + VVCFrameContext *fc = lc->fc; + VVCFrameThread *ft = fc->ft; + const int ctu_size = ft->ctu_size; + const int x0 = t->rx * ctu_size; + const int y0 = t->ry * ctu_size; + + if (fc->ps.sps->r->sps_alf_enabled_flag) { + const int slice_idx = CTB(fc->tab.slice_idx, t->rx, t->ry); + if (slice_idx != -1) { + lc->sc = fc->slices[slice_idx]; + ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, t->rs); + ff_vvc_alf_filter(lc, x0, y0); + } + } + report_frame_progress(fc, t->ry, VVC_PROGRESS_PIXEL); + + return 0; +} + +#define VVC_THREAD_DEBUG +#ifdef VVC_THREAD_DEBUG +const static char* task_name[] = { + "P", + "I", + "R", + "L", + "V", + "H", + "S", + "A" +}; +#endif + +typedef int (*run_func)(VVCContext *s, VVCLocalContext *lc, VVCTask *t); + +static void task_run_stage(VVCTask *t, VVCContext *s, VVCLocalContext *lc) +{ + int ret; + VVCFrameContext *fc = t->fc; + VVCFrameThread *ft = fc->ft; + const VVCTaskStage stage = t->stage; + run_func run[] = { + run_parse, + run_inter, + run_recon, + run_lmcs, + run_deblock_v, + run_deblock_h, + run_sao, + run_alf, + }; + +#ifdef VVC_THREAD_DEBUG + av_log(s->avctx, AV_LOG_DEBUG, "frame %5d, %s(%3d, %3d)\r\n", (int)t->fc->decode_order, task_name[stage], t->rx, t->ry); +#endif + + if (!atomic_load(&ft->ret)) { + if ((ret = run[stage](s, lc, t)) < 0) { +#ifdef COMPAT_ATOMICS_WIN32_STDATOMIC_H + intptr_t zero = 0; +#else + int zero = 0; +#endif + atomic_compare_exchange_strong(&ft->ret, &zero, ret); + av_log(s->avctx, AV_LOG_ERROR, + "frame %5d, %s(%3d, %3d) failed with %d\r\n", + (int)fc->decode_order, task_name[stage], t->rx, t->ry, ret); + } + } + + task_stage_done(t, s); + return; +} + +static int task_run(AVTask *_t, void *local_context, void *user_data) +{ + VVCTask *t = (VVCTask*)_t; + VVCContext *s = (VVCContext *)user_data; + VVCLocalContext *lc = local_context; + VVCFrameThread *ft = t->fc->ft; + + lc->fc = t->fc; + + do { + task_run_stage(t, s, lc); + t->stage++; + } while (task_is_stage_ready(t, 1)); + + if (t->stage != VVC_TASK_STAGE_LAST) + frame_thread_add_score(s, ft, t->rx, t->ry, t->stage); + + sheduled_done(ft, &ft->nb_scheduled_tasks); + + return 0; +} + +AVExecutor* ff_vvc_executor_alloc(VVCContext *s, const int thread_count) +{ + AVTaskCallbacks callbacks = { + s, + sizeof(VVCLocalContext), + task_priority_higher, + task_ready, + task_run, + }; + return av_executor_alloc(&callbacks, thread_count); +} + +void ff_vvc_executor_free(AVExecutor **e) +{ + av_executor_free(e); +} + +void ff_vvc_frame_thread_free(VVCFrameContext *fc) +{ + VVCFrameThread *ft = fc->ft; + + if (!ft) + return; + + ff_mutex_destroy(&ft->lock); + ff_cond_destroy(&ft->cond); + av_freep(&ft->rows); + av_freep(&ft->tasks); + av_freep(&ft); +} + +static void frame_thread_init_score(VVCFrameContext *fc) +{ + const VVCFrameThread *ft = fc->ft; + VVCTask task; + + task_init(&task, VVC_TASK_STAGE_RECON, fc, 0, 0); + + for (int i = VVC_TASK_STAGE_RECON; i < VVC_TASK_STAGE_LAST; i++) { + task.stage = i; + + for (task.rx = -1; task.rx <= ft->ctu_width; task.rx++) { + task.ry = -1; //top + task_stage_done(&task, NULL); + task.ry = ft->ctu_height; //bottom + task_stage_done(&task, NULL); + } + + for (task.ry = 0; task.ry < ft->ctu_height; task.ry++) { + task.rx = -1; //left + task_stage_done(&task, NULL); + task.rx = ft->ctu_width; //right + task_stage_done(&task, NULL); + } + } +} + +int ff_vvc_frame_thread_init(VVCFrameContext *fc) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + VVCFrameThread *ft = fc->ft; + int ret; + + if (!ft || ft->ctu_width != pps->ctb_width || + ft->ctu_height != pps->ctb_height || + ft->ctu_size != sps->ctb_size_y) { + + ff_vvc_frame_thread_free(fc); + ft = av_calloc(1, sizeof(*fc->ft)); + if (!ft) + return AVERROR(ENOMEM); + + ft->ctu_width = fc->ps.pps->ctb_width; + ft->ctu_height = fc->ps.pps->ctb_height; + ft->ctu_count = fc->ps.pps->ctb_count; + ft->ctu_size = fc->ps.sps->ctb_size_y; + + ft->rows = av_calloc(ft->ctu_height, sizeof(*ft->rows)); + if (!ft->rows) + goto fail; + + ft->tasks = av_malloc(ft->ctu_count * sizeof(*ft->tasks)); + if (!ft->tasks) + goto fail; + + if ((ret = ff_cond_init(&ft->cond, NULL))) + goto fail; + + if ((ret = ff_mutex_init(&ft->lock, NULL))) { + ff_cond_destroy(&ft->cond); + goto fail; + } + } + fc->ft = ft; + ft->ret = 0; + for (int y = 0; y < ft->ctu_height; y++) { + VVCRowThread *row = ft->rows + y; + memset(row->col_progress, 0, sizeof(row->col_progress)); + } + + for (int rs = 0; rs < ft->ctu_count; rs++) { + VVCTask *t = ft->tasks + rs; + task_init(t, VVC_TASK_STAGE_PARSE, fc, rs % ft->ctu_width, rs / ft->ctu_width); + } + + memset(&ft->row_progress[0], 0, sizeof(ft->row_progress)); + + frame_thread_init_score(fc); + + return 0; + +fail: + if (ft) { + av_freep(&ft->rows); + av_freep(&ft->tasks); + av_freep(&ft); + } + + return AVERROR(ENOMEM); +} + +static void check_colocation(VVCContext *s, VVCTask *t) +{ + const VVCFrameContext *fc = t->fc; + + if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag || fc->ps.sps->r->sps_sbtmvp_enabled_flag) { + VVCFrame *col = fc->ref->collocated_ref; + const int first_col = t->rx == fc->ps.pps->ctb_to_col_bd[t->rx]; + if (col && first_col) { + //we depend on bottom and right boundary, do not - 1 for y + const int y = (t->ry << fc->ps.sps->ctb_log2_size_y); + add_progress_listener(col, &t->col_listener, t, s, VVC_PROGRESS_MV, y); + return; + } + } + frame_thread_add_score(s, fc->ft, t->rx, t->ry, VVC_TASK_STAGE_PARSE); +} + +static void submit_entry_point(VVCContext *s, VVCFrameThread *ft, SliceContext *sc, EntryPoint *ep) +{ + const int rs = sc->sh.ctb_addr_in_curr_slice[ep->ctu_start]; + VVCTask *t = ft->tasks + rs; + + frame_thread_add_score(s, ft, t->rx, t->ry, VVC_TASK_STAGE_PARSE); +} + +void ff_vvc_frame_submit(VVCContext *s, VVCFrameContext *fc) +{ + VVCFrameThread *ft = fc->ft; + + for (int i = 0; i < fc->nb_slices; i++) { + SliceContext *sc = fc->slices[i]; + for (int j = 0; j < sc->nb_eps; j++) { + EntryPoint *ep = sc->eps + j; + for (int k = ep->ctu_start; k < ep->ctu_end; k++) { + const int rs = sc->sh.ctb_addr_in_curr_slice[k]; + VVCTask *t = ft->tasks + rs; + + task_init_parse(t, sc, ep, k); + check_colocation(s, t); + } + submit_entry_point(s, ft, sc, ep); + } + } +} + +int ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc) +{ + VVCFrameThread *ft = fc->ft; + + ff_mutex_lock(&ft->lock); + + while (atomic_load(&ft->nb_scheduled_tasks) || atomic_load(&ft->nb_scheduled_listeners)) + ff_cond_wait(&ft->cond, &ft->lock); + + ff_mutex_unlock(&ft->lock); + ff_vvc_report_frame_finished(fc->ref); + +#ifdef VVC_THREAD_DEBUG + av_log(s->avctx, AV_LOG_DEBUG, "frame %5d done\r\n", (int)fc->decode_order); +#endif + return ft->ret; +} diff --git a/libavcodec/vvc/vvc_thread.h b/libavcodec/vvc/vvc_thread.h new file mode 100644 index 00000000000..6c726744f84 --- /dev/null +++ b/libavcodec/vvc/vvc_thread.h @@ -0,0 +1,36 @@ +/* + * VVC thread logic + * + * Copyright (C) 2023 Nuo Mi + * + * 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 + */ + +#ifndef AVCODEC_VVC_VVC_THREAD_H +#define AVCODEC_VVC_VVC_THREAD_H + +#include "vvcdec.h" + +struct AVExecutor* ff_vvc_executor_alloc(VVCContext *s, int thread_count); +void ff_vvc_executor_free(struct AVExecutor **e); + +int ff_vvc_frame_thread_init(VVCFrameContext *fc); +void ff_vvc_frame_thread_free(VVCFrameContext *fc); +void ff_vvc_frame_submit(VVCContext *s, VVCFrameContext *fc); +int ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc); + +#endif // AVCODEC_VVC_VVC_THREAD_H diff --git a/libavcodec/vvc/vvcdec.c b/libavcodec/vvc/vvcdec.c new file mode 100644 index 00000000000..ccb1b59862f --- /dev/null +++ b/libavcodec/vvc/vvcdec.c @@ -0,0 +1,1062 @@ +/* + * VVC video decoder + * + * Copyright (C) 2021 Nuo Mi + * Copyright (C) 2022 Xu Mu + * + * 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 "libavcodec/codec_internal.h" +#include "libavcodec/decode.h" +#include "libavcodec/profiles.h" +#include "libavcodec/refstruct.h" +#include "libavutil/cpu.h" +#include "libavutil/thread.h" + +#include "vvcdec.h" +#include "vvc_ctu.h" +#include "vvc_data.h" +#include "vvc_refs.h" +#include "vvc_thread.h" + +#define TAB_MAX 32 + +typedef struct Tab { + void **tab; + size_t size; +} Tab; + +typedef struct TabList { + Tab tabs[TAB_MAX]; + int nb_tabs; + + int zero; + int realloc; +} TabList; + +#define TL_ADD(t, s) do { \ + av_assert0(l->nb_tabs < TAB_MAX); \ + l->tabs[l->nb_tabs].tab = (void**)&fc->tab.t; \ + l->tabs[l->nb_tabs].size = sizeof(*fc->tab.t) * (s); \ + l->nb_tabs++; \ +} while (0) + +static void tl_init(TabList *l, const int zero, const int realloc) +{ + l->nb_tabs = 0; + l->zero = zero; + l->realloc = realloc; +} + +static int tl_free(TabList *l) +{ + for (int i = 0; i < l->nb_tabs; i++) + av_freep(l->tabs[i].tab); + + return 0; +} + +static int tl_create(TabList *l) +{ + if (l->realloc) { + tl_free(l); + + for (int i = 0; i < l->nb_tabs; i++) { + Tab *t = l->tabs + i; + *t->tab = l->zero ? av_mallocz(t->size) : av_malloc(t->size); + if (!*t->tab) + return AVERROR(ENOMEM); + } + } else if (l->zero) { + for (int i = 0; i < l->nb_tabs; i++) { + Tab *t = l->tabs + i; + memset(*t->tab, 0, t->size); + } + } + return 0; +} + +static void ctu_tl_init(TabList *l, VVCFrameContext *fc) +{ + const VVCPPS *pps = fc->ps.pps; + const int ctu_count = pps ? pps->ctb_count : 0; + const int changed = fc->tab.sz.ctu_count != ctu_count; + + tl_init(l, 1, changed); + + TL_ADD(deblock, ctu_count); + TL_ADD(sao, ctu_count); + TL_ADD(alf, ctu_count); + TL_ADD(ctus, ctu_count); +} + +static void ctu_nz_tl_init(TabList *l, VVCFrameContext *fc) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int ctu_size = sps ? (1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y) : 0; + const int ctu_count = pps ? pps->ctb_count : 0; + const int changed = fc->tab.sz.ctu_count != ctu_count || fc->tab.sz.ctu_size != ctu_size; + + tl_init(l, 0, changed); + TL_ADD(slice_idx, ctu_count); + TL_ADD(coeffs, ctu_count * ctu_size * VVC_MAX_SAMPLE_ARRAYS); +} + +static void min_cb_tl_init(TabList *l, VVCFrameContext *fc) +{ + const VVCPPS *pps = fc->ps.pps; + const int pic_size_in_min_cb = pps ? pps->min_cb_width * pps->min_cb_height : 0; + const int changed = fc->tab.sz.pic_size_in_min_cb != pic_size_in_min_cb; + + tl_init(l, 1, changed); + + TL_ADD(skip, pic_size_in_min_cb); + TL_ADD(imf, pic_size_in_min_cb); + TL_ADD(imtf, pic_size_in_min_cb); + TL_ADD(imm, pic_size_in_min_cb); + TL_ADD(ipm, pic_size_in_min_cb); + + for (int i = LUMA; i <= CHROMA; i++) { + TL_ADD(cb_pos_x[i], pic_size_in_min_cb); + TL_ADD(cb_pos_y[i], pic_size_in_min_cb); + TL_ADD(cb_width[i], pic_size_in_min_cb); + TL_ADD(cb_height[i], pic_size_in_min_cb); + TL_ADD(cqt_depth[i], pic_size_in_min_cb); + TL_ADD(cpm[i], pic_size_in_min_cb); + TL_ADD(cp_mv[i], pic_size_in_min_cb * MAX_CONTROL_POINTS); + }; +} + +static void min_pu_tl_init(TabList *l, VVCFrameContext *fc) +{ + const VVCPPS *pps = fc->ps.pps; + const int pic_size_in_min_pu = pps ? pps->min_pu_width * pps->min_pu_height : 0; + const int changed = fc->tab.sz.pic_size_in_min_pu != pic_size_in_min_pu; + + tl_init(l, 1, changed); + + TL_ADD(msf, pic_size_in_min_pu); + TL_ADD(iaf, pic_size_in_min_pu); + TL_ADD(mmi, pic_size_in_min_pu); + TL_ADD(mvf, pic_size_in_min_pu); +} + +static void min_tu_tl_init(TabList *l, VVCFrameContext *fc) +{ + const VVCPPS *pps = fc->ps.pps; + const int pic_size_in_min_tu = pps ? pps->min_tu_width * pps->min_tu_height : 0; + const int changed = fc->tab.sz.pic_size_in_min_tu != pic_size_in_min_tu; + + tl_init(l, 1, changed); + + TL_ADD(tu_joint_cbcr_residual_flag, pic_size_in_min_tu); + for (int i = LUMA; i <= CHROMA; i++) { + TL_ADD(tb_pos_x0[i], pic_size_in_min_tu); + TL_ADD(tb_pos_y0[i], pic_size_in_min_tu); + TL_ADD(tb_width[i], pic_size_in_min_tu); + TL_ADD(tb_height[i], pic_size_in_min_tu); + TL_ADD(pcmf[i], pic_size_in_min_tu); + } + + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + TL_ADD(tu_coded_flag[i], pic_size_in_min_tu); + TL_ADD(qp[i], pic_size_in_min_tu); + } +} + +static void bs_tl_init(TabList *l, VVCFrameContext *fc) +{ + const VVCPPS *pps = fc->ps.pps; + const int bs_width = pps ? (pps->width >> 2) + 1 : 0; + const int bs_height = pps ? (pps->height >> 2) + 1 : 0; + const int bs_count = bs_width * bs_height; + const int changed = fc->tab.sz.bs_width != bs_width || + fc->tab.sz.bs_height != bs_height; + + tl_init(l, 1, changed); + + for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + TL_ADD(horizontal_bs[i], bs_count); + TL_ADD(vertical_bs[i], bs_count); + } + TL_ADD(horizontal_q, bs_count); + TL_ADD(horizontal_p, bs_count); + TL_ADD(vertical_p, bs_count); + TL_ADD(vertical_q, bs_count); +} + +static void pixel_buffer_nz_tl_init(TabList *l, VVCFrameContext *fc) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int width = pps ? pps->width : 0; + const int height = pps ? pps->height : 0; + const int ctu_width = pps ? pps->ctb_width : 0; + const int ctu_height = pps ? pps->ctb_height : 0; + const int chroma_idc = sps ? sps->r->sps_chroma_format_idc : 0; + const int ps = sps ? sps->pixel_shift : 0; + const int c_end = chroma_idc ? VVC_MAX_SAMPLE_ARRAYS : 1; + const int changed = fc->tab.sz.chroma_format_idc != chroma_idc || + fc->tab.sz.width != width || fc->tab.sz.height != height || + fc->tab.sz.ctu_width != ctu_width || fc->tab.sz.ctu_height != ctu_height; + + tl_init(l, 0, changed); + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int w = width >> (sps ? sps->hshift[c_idx] : 0); + const int h = height >> (sps ? sps->vshift[c_idx] : 0); + TL_ADD(sao_pixel_buffer_h[c_idx], (w * 2 * ctu_height) << ps); + TL_ADD(sao_pixel_buffer_v[c_idx], (h * 2 * ctu_width) << ps); + } + + for (int c_idx = 0; c_idx < c_end; c_idx++) { + const int w = width >> (sps ? sps->hshift[c_idx] : 0); + const int h = height >> (sps ? sps->vshift[c_idx] : 0); + const int border_pixels = c_idx ? ALF_BORDER_CHROMA : ALF_BORDER_LUMA; + for (int i = 0; i < 2; i++) { + TL_ADD(alf_pixel_buffer_h[c_idx][i], (w * border_pixels * ctu_height) << ps); + TL_ADD(alf_pixel_buffer_v[c_idx][i], h * ALF_PADDING_SIZE * ctu_width); + } + } +} + +static void msm_tl_init(TabList *l, VVCFrameContext *fc) +{ + const VVCPPS *pps = fc->ps.pps; + const int w32 = pps ? AV_CEIL_RSHIFT(pps->width, 5) : 0; + const int h32 = pps ? AV_CEIL_RSHIFT(pps->height, 5) : 0; + const int changed = AV_CEIL_RSHIFT(fc->tab.sz.width, 5) != w32 || + AV_CEIL_RSHIFT(fc->tab.sz.height, 5) != h32; + + tl_init(l, 1, changed); + + for (int i = LUMA; i <= CHROMA; i++) + TL_ADD(msm[i], w32 * h32); +} + +static void ispmf_tl_init(TabList *l, VVCFrameContext *fc) +{ + const VVCPPS *pps = fc->ps.pps; + const int w64 = pps ? AV_CEIL_RSHIFT(pps->width, 6) : 0; + const int h64 = pps ? AV_CEIL_RSHIFT(pps->height, 6) : 0; + const int changed = AV_CEIL_RSHIFT(fc->tab.sz.width, 6) != w64 || + AV_CEIL_RSHIFT(fc->tab.sz.height, 6) != h64; + + tl_init(l, 1, changed); + + TL_ADD(ispmf, w64 * h64); +} + +static void ibc_tl_init(TabList *l, VVCFrameContext *fc) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int ctu_height = pps ? pps->ctb_height : 0; + const int ctu_size = sps ? sps->ctb_size_y : 0; + const int ps = sps ? sps->pixel_shift : 0; + const int chroma_idc = sps ? sps->r->sps_chroma_format_idc : 0; + const int has_ibc = sps ? sps->r->sps_ibc_enabled_flag : 0; + const int changed = fc->tab.sz.chroma_format_idc != chroma_idc || + fc->tab.sz.ctu_height != ctu_height || + fc->tab.sz.ctu_size != ctu_size || + fc->tab.sz.pixel_shift != ps; + + fc->tab.sz.ibc_buffer_width = ctu_size ? 2 * MAX_CTU_SIZE * MAX_CTU_SIZE / ctu_size : 0; + + tl_init(l, has_ibc, changed); + + for (int i = LUMA; i < VVC_MAX_SAMPLE_ARRAYS; i++) { + const int hs = sps ? sps->hshift[i] : 0; + const int vs = sps ? sps->vshift[i] : 0; + TL_ADD(ibc_vir_buf[i], fc->tab.sz.ibc_buffer_width * ctu_size * ctu_height << ps >> hs >> vs); + } +} + +typedef void (*tl_init_fn)(TabList *l, VVCFrameContext *fc); + +static int frame_context_for_each_tl(VVCFrameContext *fc, int (*unary_fn)(TabList *l)) +{ + const tl_init_fn init[] = { + ctu_tl_init, + ctu_nz_tl_init, + min_cb_tl_init, + min_pu_tl_init, + min_tu_tl_init, + bs_tl_init, + pixel_buffer_nz_tl_init, + msm_tl_init, + ispmf_tl_init, + ibc_tl_init, + }; + + for (int i = 0; i < FF_ARRAY_ELEMS(init); i++) { + TabList l; + int ret; + + init[i](&l, fc); + ret = unary_fn(&l); + if (ret < 0) + return ret; + } + return 0; +} + +static void free_cus(VVCFrameContext *fc) +{ + if (fc->tab.ctus) { + for (int i = 0; i < fc->tab.sz.ctu_count; i++) + ff_vvc_ctu_free_cus(fc->tab.ctus + i); + } +} + +static void pic_arrays_free(VVCFrameContext *fc) +{ + free_cus(fc); + frame_context_for_each_tl(fc, tl_free); + ff_refstruct_pool_uninit(&fc->rpl_tab_pool); + ff_refstruct_pool_uninit(&fc->tab_dmvr_mvf_pool); + + memset(&fc->tab.sz, 0, sizeof(fc->tab.sz)); +} + +static int pic_arrays_init(VVCContext *s, VVCFrameContext *fc) +{ + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + const int ctu_count = pps->ctb_count; + const int pic_size_in_min_pu = pps->min_pu_width * pps->min_pu_height; + int ret; + + free_cus(fc); + + ret = frame_context_for_each_tl(fc, tl_create); + if (ret < 0) + return ret; + + memset(fc->tab.slice_idx, -1, sizeof(*fc->tab.slice_idx) * ctu_count); + + if (fc->tab.sz.ctu_count != ctu_count) { + ff_refstruct_pool_uninit(&fc->rpl_tab_pool); + fc->rpl_tab_pool = ff_refstruct_pool_alloc(ctu_count * sizeof(RefPicListTab), 0); + if (!fc->rpl_tab_pool) + return AVERROR(ENOMEM); + } + + if (fc->tab.sz.pic_size_in_min_pu != pic_size_in_min_pu) { + ff_refstruct_pool_uninit(&fc->tab_dmvr_mvf_pool); + fc->tab_dmvr_mvf_pool = ff_refstruct_pool_alloc( + pic_size_in_min_pu * sizeof(MvField), FF_REFSTRUCT_POOL_FLAG_ZERO_EVERY_TIME); + if (!fc->tab_dmvr_mvf_pool) + return AVERROR(ENOMEM); + } + + fc->tab.sz.ctu_count = pps->ctb_count; + fc->tab.sz.ctu_size = 1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y; + fc->tab.sz.pic_size_in_min_cb = pps->min_cb_width * pps->min_cb_height; + fc->tab.sz.pic_size_in_min_pu = pic_size_in_min_pu; + fc->tab.sz.pic_size_in_min_tu = pps->min_tu_width * pps->min_tu_height; + fc->tab.sz.width = pps->width; + fc->tab.sz.height = pps->height; + fc->tab.sz.ctu_width = pps->ctb_width; + fc->tab.sz.ctu_height = pps->ctb_height; + fc->tab.sz.chroma_format_idc = sps->r->sps_chroma_format_idc; + fc->tab.sz.pixel_shift = sps->pixel_shift; + fc->tab.sz.bs_width = (fc->ps.pps->width >> 2) + 1; + fc->tab.sz.bs_height = (fc->ps.pps->height >> 2) + 1; + + return 0; +} + +static int min_positive(const int idx, const int diff, const int min_diff) +{ + return diff > 0 && (idx < 0 || diff < min_diff); +} + +static int max_negtive(const int idx, const int diff, const int max_diff) +{ + return diff < 0 && (idx < 0 || diff > max_diff); +} + +typedef int (*smvd_find_fxn)(const int idx, const int diff, const int old_diff); + +static int8_t smvd_find(const VVCFrameContext *fc, const SliceContext *sc, int lx, smvd_find_fxn find) +{ + const H266RawSliceHeader *rsh = sc->sh.r; + const RefPicList *rpl = sc->rpl + lx; + const int poc = fc->ref->poc; + int8_t idx = -1; + int old_diff = -1; + for (int i = 0; i < rsh->num_ref_idx_active[lx]; i++) { + if (!rpl->isLongTerm[i]) { + int diff = poc - rpl->list[i]; + if (find(idx, diff, old_diff)) { + idx = i; + old_diff = diff; + } + } + } + return idx; +} + +static void smvd_ref_idx(const VVCFrameContext *fc, SliceContext *sc) +{ + VVCSH *sh = &sc->sh; + if (IS_B(sh->r)) { + sh->ref_idx_sym[0] = smvd_find(fc, sc, 0, min_positive); + sh->ref_idx_sym[1] = smvd_find(fc, sc, 1, max_negtive); + if (sh->ref_idx_sym[0] == -1 || sh->ref_idx_sym[1] == -1) { + sh->ref_idx_sym[0] = smvd_find(fc, sc, 0, max_negtive); + sh->ref_idx_sym[1] = smvd_find(fc, sc, 1, min_positive); + } + } +} + +static void eps_free(SliceContext *slice) +{ + av_freep(&slice->eps); + slice->nb_eps = 0; +} + +static void slices_free(VVCFrameContext *fc) +{ + if (fc->slices) { + for (int i = 0; i < fc->nb_slices_allocated; i++) { + SliceContext *slice = fc->slices[i]; + if (slice) { + ff_refstruct_unref(&slice->ref); + ff_refstruct_unref(&slice->sh.r); + eps_free(slice); + av_free(slice); + } + } + av_freep(&fc->slices); + } + fc->nb_slices_allocated = 0; + fc->nb_slices = 0; +} + +static int slices_realloc(VVCFrameContext *fc) +{ + void *p; + const int size = (fc->nb_slices_allocated + 1) * 3 / 2; + + if (fc->nb_slices < fc->nb_slices_allocated) + return 0; + + p = av_realloc_array(fc->slices, size, sizeof(*fc->slices)); + if (!p) + return AVERROR(ENOMEM); + + fc->slices = p; + for (int i = fc->nb_slices_allocated; i < size; i++) { + fc->slices[i] = av_mallocz(sizeof(*fc->slices[0])); + if (!fc->slices[i]) { + fc->nb_slices_allocated = i; + return AVERROR(ENOMEM); + } + fc->slices[i]->slice_idx = i; + } + fc->nb_slices_allocated = size; + + return 0; +} + +static int ep_init_cabac_decoder(SliceContext *sc, const int index, + const H2645NAL *nal, GetBitContext *gb, const CodedBitstreamUnit *unit) +{ + const H266RawSlice *slice = unit->content_ref; + const H266RawSliceHeader *rsh = sc->sh.r; + EntryPoint *ep = sc->eps + index; + int size; + int ret; + + if (index < rsh->num_entry_points) { + int skipped = 0; + int64_t start = (gb->index >> 3); + int64_t end = start + rsh->sh_entry_point_offset_minus1[index] + 1; + while (skipped < nal->skipped_bytes && nal->skipped_bytes_pos[skipped] <= start + slice->header_size) { + skipped++; + } + while (skipped < nal->skipped_bytes && nal->skipped_bytes_pos[skipped] <= end + slice->header_size) { + end--; + skipped++; + } + size = end - start; + } else { + size = get_bits_left(gb) / 8; + } + ret = ff_init_cabac_decoder (&ep->cc, gb->buffer + get_bits_count(gb) / 8, size); + if (ret < 0) + return ret; + skip_bits(gb, size * 8); + return 0; +} + +static int slice_init_entry_points(SliceContext *sc, + VVCFrameContext *fc, const H2645NAL *nal, const CodedBitstreamUnit *unit) +{ + const VVCSH *sh = &sc->sh; + const H266RawSlice *slice = unit->content_ref; + int nb_eps = sh->r->num_entry_points + 1; + int ctu_addr = 0; + GetBitContext gb; + int ret; + + if (sc->nb_eps != nb_eps) { + eps_free(sc); + sc->eps = av_calloc(nb_eps, sizeof(*sc->eps)); + if (!sc->eps) + return AVERROR(ENOMEM); + sc->nb_eps = nb_eps; + } + + ret = init_get_bits8(&gb, slice->data, slice->data_size); + if (ret < 0) + return ret; + for (int i = 0; i < sc->nb_eps; i++) + { + EntryPoint *ep = sc->eps + i; + + ep->ctu_start = ctu_addr; + ep->ctu_end = (i + 1 == sc->nb_eps ? sh->num_ctus_in_curr_slice : sh->entry_point_start_ctu[i]); + + for (int j = ep->ctu_start; j < ep->ctu_end; j++) { + const int rs = sc->sh.ctb_addr_in_curr_slice[j]; + fc->tab.slice_idx[rs] = sc->slice_idx; + } + + ret = ep_init_cabac_decoder(sc, i, nal, &gb, unit); + if (ret < 0) + return ret; + + if (i + 1 < sc->nb_eps) + ctu_addr = sh->entry_point_start_ctu[i]; + } + + return 0; +} + +static VVCFrameContext* get_frame_context(const VVCContext *s, const VVCFrameContext *fc, const int delta) +{ + const int size = s->nb_fcs; + const int idx = (fc - s->fcs + delta + size) % size; + return s->fcs + idx; +} + +static int ref_frame(VVCFrame *dst, const VVCFrame *src) +{ + int ret; + + ret = av_frame_ref(dst->frame, src->frame); + if (ret < 0) + return ret; + + ff_refstruct_replace(&dst->progress, src->progress); + + ff_refstruct_replace(&dst->tab_dmvr_mvf, src->tab_dmvr_mvf); + + ff_refstruct_replace(&dst->rpl_tab, src->rpl_tab); + ff_refstruct_replace(&dst->rpl, src->rpl); + dst->nb_rpl_elems = src->nb_rpl_elems; + + dst->poc = src->poc; + dst->ctb_count = src->ctb_count; + dst->flags = src->flags; + dst->sequence = src->sequence; + + return 0; +} + +static av_cold void frame_context_free(VVCFrameContext *fc) +{ + slices_free(fc); + + ff_refstruct_pool_uninit(&fc->tu_pool); + ff_refstruct_pool_uninit(&fc->cu_pool); + + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + ff_vvc_unref_frame(fc, &fc->DPB[i], ~0); + av_frame_free(&fc->DPB[i].frame); + } + + ff_vvc_frame_thread_free(fc); + pic_arrays_free(fc); + av_frame_free(&fc->output_frame); + ff_vvc_frame_ps_free(&fc->ps); +} + +static av_cold int frame_context_init(VVCFrameContext *fc, AVCodecContext *avctx) +{ + + fc->log_ctx = avctx; + + fc->output_frame = av_frame_alloc(); + if (!fc->output_frame) + return AVERROR(ENOMEM); + + for (int j = 0; j < FF_ARRAY_ELEMS(fc->DPB); j++) { + fc->DPB[j].frame = av_frame_alloc(); + if (!fc->DPB[j].frame) + return AVERROR(ENOMEM); + } + fc->cu_pool = ff_refstruct_pool_alloc(sizeof(CodingUnit), 0); + if (!fc->cu_pool) + return AVERROR(ENOMEM); + + fc->tu_pool = ff_refstruct_pool_alloc(sizeof(TransformUnit), 0); + if (!fc->tu_pool) + return AVERROR(ENOMEM); + + return 0; +} + +static int frame_context_setup(VVCFrameContext *fc, VVCContext *s) +{ + int ret; + + fc->ref = NULL; + + // copy refs from the last frame + if (s->nb_frames && s->nb_fcs > 1) { + VVCFrameContext *prev = get_frame_context(s, fc, -1); + for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) { + ff_vvc_unref_frame(fc, &fc->DPB[i], ~0); + if (prev->DPB[i].frame->buf[0]) { + ret = ref_frame(&fc->DPB[i], &prev->DPB[i]); + if (ret < 0) + return ret; + } + } + } + + if (IS_IDR(s)) { + s->seq_decode = (s->seq_decode + 1) & 0xff; + ff_vvc_clear_refs(fc); + } + + ret = pic_arrays_init(s, fc); + if (ret < 0) + return ret; + ff_vvc_dsp_init(&fc->vvcdsp, fc->ps.sps->bit_depth); + ff_videodsp_init(&fc->vdsp, fc->ps.sps->bit_depth); + return 0; +} + +static int frame_start(VVCContext *s, VVCFrameContext *fc, SliceContext *sc) +{ + const VVCPH *ph = &fc->ps.ph; + const H266RawSliceHeader *rsh = sc->sh.r; + int ret; + + // 8.3.1 Decoding process for picture order count + if (!s->temporal_id && !ph->r->ph_non_ref_pic_flag && !(IS_RASL(s) || IS_RADL(s))) + s->poc_tid0 = ph->poc; + + if ((ret = ff_vvc_set_new_ref(s, fc, &fc->frame)) < 0) + goto fail; + + if (!IS_IDR(s)) + ff_vvc_bump_frame(s, fc); + + av_frame_unref(fc->output_frame); + + if ((ret = ff_vvc_output_frame(s, fc, fc->output_frame,rsh->sh_no_output_of_prior_pics_flag, 0)) < 0) + goto fail; + + if ((ret = ff_vvc_frame_rpl(s, fc, sc)) < 0) + goto fail; + + if ((ret = ff_vvc_frame_thread_init(fc)) < 0) + goto fail; + return 0; +fail: + if (fc->ref) + ff_vvc_unref_frame(fc, fc->ref, ~0); + fc->ref = NULL; + return ret; +} + +static int slice_start(SliceContext *sc, VVCContext *s, VVCFrameContext *fc, + const CodedBitstreamUnit *unit, const int is_first_slice) +{ + VVCSH *sh = &sc->sh; + int ret; + + ret = ff_vvc_decode_sh(sh, &fc->ps, unit); + if (ret < 0) + return ret; + + ff_refstruct_replace(&sc->ref, unit->content_ref); + + if (is_first_slice) { + ret = frame_start(s, fc, sc); + if (ret < 0) + return ret; + } else if (fc->ref) { + if (!IS_I(sh->r)) { + ret = ff_vvc_slice_rpl(s, fc, sc); + if (ret < 0) { + av_log(fc->log_ctx, AV_LOG_WARNING, + "Error constructing the reference lists for the current slice.\n"); + return ret; + } + } + } else { + av_log(fc->log_ctx, AV_LOG_ERROR, "First slice in a frame missing.\n"); + return ret; + } + + if (!IS_I(sh->r)) + smvd_ref_idx(fc, sc); + + return 0; +} + +static void export_frame_params(VVCContext *s, const VVCFrameContext *fc) +{ + AVCodecContext *c = s->avctx; + const VVCSPS *sps = fc->ps.sps; + const VVCPPS *pps = fc->ps.pps; + + c->pix_fmt = sps->pix_fmt; + c->coded_width = pps->width; + c->coded_height = pps->height; + c->width = pps->width - ((pps->r->pps_conf_win_left_offset + pps->r->pps_conf_win_right_offset) << sps->hshift[CHROMA]); + c->height = pps->height - ((pps->r->pps_conf_win_top_offset + pps->r->pps_conf_win_bottom_offset) << sps->vshift[CHROMA]); +} + +static int frame_setup(VVCFrameContext *fc, VVCContext *s) +{ + int ret = ff_vvc_decode_frame_ps(&fc->ps, s); + if (ret < 0) + return ret; + + ret = frame_context_setup(fc, s); + if (ret < 0) + return ret; + + export_frame_params(s, fc); + return ret; +} + +static int decode_slice(VVCContext *s, VVCFrameContext *fc, const H2645NAL *nal, const CodedBitstreamUnit *unit) +{ + int ret; + SliceContext *sc; + const int is_first_slice = !fc->nb_slices; + + ret = slices_realloc(fc); + if (ret < 0) + return ret; + + sc = fc->slices[fc->nb_slices]; + + s->vcl_unit_type = nal->type; + if (is_first_slice) { + ret = frame_setup(fc, s); + if (ret < 0) + return ret; + } + + ret = slice_start(sc, s, fc, unit, is_first_slice); + if (ret < 0) + return ret; + + ret = slice_init_entry_points(sc, fc, nal, unit); + if (ret < 0) + return ret; + fc->nb_slices++; + + return 0; +} + +static int decode_nal_unit(VVCContext *s, VVCFrameContext *fc, const H2645NAL *nal, const CodedBitstreamUnit *unit) +{ + int ret; + + s->temporal_id = nal->temporal_id; + + if (nal->nuh_layer_id > 0) { + avpriv_report_missing_feature(fc->log_ctx, + "Decoding of multilayer bitstreams"); + return AVERROR_PATCHWELCOME; + } + + switch (unit->type) { + case VVC_VPS_NUT: + case VVC_SPS_NUT: + case VVC_PPS_NUT: + /* vps, sps, sps cached by s->cbc */ + break; + case VVC_TRAIL_NUT: + case VVC_STSA_NUT: + case VVC_RADL_NUT: + case VVC_RASL_NUT: + case VVC_IDR_W_RADL: + case VVC_IDR_N_LP: + case VVC_CRA_NUT: + case VVC_GDR_NUT: + ret = decode_slice(s, fc, nal, unit); + if (ret < 0) + return ret; + break; + case VVC_PREFIX_APS_NUT: + case VVC_SUFFIX_APS_NUT: + ret = ff_vvc_decode_aps(&s->ps, unit); + if (ret < 0) + return ret; + break; + } + + return 0; +} + +static int decode_nal_units(VVCContext *s, VVCFrameContext *fc, AVPacket *avpkt) +{ + const CodedBitstreamH266Context *h266 = s->cbc->priv_data; + CodedBitstreamFragment *frame = &s->current_frame; + int ret = 0; + s->last_eos = s->eos; + s->eos = 0; + + ff_cbs_fragment_reset(frame); + ret = ff_cbs_read_packet(s->cbc, frame, avpkt); + if (ret < 0) { + av_log(s->avctx, AV_LOG_ERROR, "Failed to read packet.\n"); + return ret; + } + /* decode the NAL units */ + for (int i = 0; i < frame->nb_units; i++) { + const H2645NAL *nal = h266->common.read_packet.nals + i; + const CodedBitstreamUnit *unit = frame->units + i; + + if (unit->type == VVC_EOB_NUT || unit->type == VVC_EOS_NUT) { + s->last_eos = 1; + } else { + ret = decode_nal_unit(s, fc, nal, unit); + if (ret < 0) { + av_log(s->avctx, AV_LOG_WARNING, + "Error parsing NAL unit #%d.\n", i); + goto fail; + } + } + } + return 0; + +fail: + if (fc->ref) + ff_vvc_report_frame_finished(fc->ref); + return ret; +} + +static int set_output_format(const VVCContext *s, const AVFrame *output) +{ + AVCodecContext *c = s->avctx; + int ret; + + if (output->width != c->width || output->height != c->height) { + if ((ret = ff_set_dimensions(c, output->width, output->height)) < 0) + return ret; + } + c->pix_fmt = output->format; + return 0; +} + +static int wait_delayed_frame(VVCContext *s, AVFrame *output, int *got_output) +{ + VVCFrameContext *delayed = get_frame_context(s, s->fcs, s->nb_frames - s->nb_delayed); + int ret = ff_vvc_frame_wait(s, delayed); + + if (!ret && delayed->output_frame->buf[0] && output) { + av_frame_move_ref(output, delayed->output_frame); + ret = set_output_format(s, output); + if (!ret) + *got_output = 1; + } + s->nb_delayed--; + + return ret; +} + +static int submit_frame(VVCContext *s, VVCFrameContext *fc, AVFrame *output, int *got_output) +{ + int ret; + s->nb_frames++; + s->nb_delayed++; + ff_vvc_frame_submit(s, fc); + if (s->nb_delayed >= s->nb_fcs) { + if ((ret = wait_delayed_frame(s, output, got_output)) < 0) + return ret; + } + return 0; +} + +static int get_decoded_frame(VVCContext *s, AVFrame *output, int *got_output) +{ + int ret; + while (s->nb_delayed) { + if ((ret = wait_delayed_frame(s, output, got_output)) < 0) + return ret; + if (*got_output) + return 0; + } + if (s->nb_frames) { + //we still have frames cached in dpb. + VVCFrameContext *last = get_frame_context(s, s->fcs, s->nb_frames - 1); + + ret = ff_vvc_output_frame(s, last, output, 0, 1); + if (ret < 0) + return ret; + if (ret) { + *got_output = ret; + if ((ret = set_output_format(s, output)) < 0) + return ret; + } + } + return 0; +} + +static int vvc_decode_frame(AVCodecContext *avctx, AVFrame *output, + int *got_output, AVPacket *avpkt) +{ + VVCContext *s = avctx->priv_data; + VVCFrameContext *fc; + int ret; + + if (!avpkt->size) + return get_decoded_frame(s, output, got_output); + + fc = get_frame_context(s, s->fcs, s->nb_frames); + + fc->nb_slices = 0; + fc->decode_order = s->nb_frames; + + ret = decode_nal_units(s, fc, avpkt); + if (ret < 0) + return ret; + + if (!fc->ft) + return avpkt->size; + + ret = submit_frame(s, fc, output, got_output); + if (ret < 0) + return ret; + + return avpkt->size; +} + +static av_cold void vvc_decode_flush(AVCodecContext *avctx) +{ + VVCContext *s = avctx->priv_data; + int got_output = 0; + + while (s->nb_delayed) + wait_delayed_frame(s, NULL, &got_output); + + if (s->fcs) { + VVCFrameContext *last = get_frame_context(s, s->fcs, s->nb_frames - 1); + ff_vvc_flush_dpb(last); + } + + s->eos = 1; +} + +static av_cold int vvc_decode_free(AVCodecContext *avctx) +{ + VVCContext *s = avctx->priv_data; + + ff_cbs_fragment_free(&s->current_frame); + vvc_decode_flush(avctx); + ff_vvc_executor_free(&s->executor); + if (s->fcs) { + for (int i = 0; i < s->nb_fcs; i++) + frame_context_free(s->fcs + i); + av_free(s->fcs); + } + ff_vvc_ps_uninit(&s->ps); + ff_cbs_close(&s->cbc); + + return 0; +} + +static av_cold void init_default_scale_m(void) +{ + memset(&ff_vvc_default_scale_m, 16, sizeof(ff_vvc_default_scale_m)); +} + +#define VVC_MAX_DELAYED_FRAMES 16 +static av_cold int vvc_decode_init(AVCodecContext *avctx) +{ + VVCContext *s = avctx->priv_data; + static AVOnce init_static_once = AV_ONCE_INIT; + const int cpu_count = av_cpu_count(); + const int delayed = FFMIN(cpu_count, VVC_MAX_DELAYED_FRAMES); + const int thread_count = avctx->thread_count ? avctx->thread_count : delayed; + int ret; + + s->avctx = avctx; + + ret = ff_cbs_init(&s->cbc, AV_CODEC_ID_VVC, avctx); + if (ret) + return ret; + + if (avctx->extradata_size > 0 && avctx->extradata) { + ret = ff_cbs_read_extradata_from_codec(s->cbc, &s->current_frame, avctx); + if (ret < 0) + return ret; + } + + s->nb_fcs = (avctx->flags & AV_CODEC_FLAG_LOW_DELAY) ? 1 : delayed; + s->fcs = av_calloc(s->nb_fcs, sizeof(*s->fcs)); + if (!s->fcs) + return AVERROR(ENOMEM); + + for (int i = 0; i < s->nb_fcs; i++) { + VVCFrameContext *fc = s->fcs + i; + ret = frame_context_init(fc, avctx); + if (ret < 0) + return ret; + } + + s->executor = ff_vvc_executor_alloc(s, thread_count); + if (!s->executor) + return AVERROR(ENOMEM); + + s->eos = 1; + GDR_SET_RECOVERED(s); + ff_thread_once(&init_static_once, init_default_scale_m); + + return 0; +} + +const FFCodec ff_vvc_decoder = { + .p.name = "vvc", + .p.long_name = NULL_IF_CONFIG_SMALL("VVC (Versatile Video Coding)"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VVC, + .priv_data_size = sizeof(VVCContext), + .init = vvc_decode_init, + .close = vvc_decode_free, + FF_CODEC_DECODE_CB(vvc_decode_frame), + .flush = vvc_decode_flush, + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS | + AV_CODEC_CAP_EXPERIMENTAL, + .caps_internal = FF_CODEC_CAP_EXPORTS_CROPPING | FF_CODEC_CAP_INIT_CLEANUP | + FF_CODEC_CAP_AUTO_THREADS, + .p.profiles = NULL_IF_CONFIG_SMALL(ff_vvc_profiles), +}; diff --git a/libavcodec/vvc/vvcdec.h b/libavcodec/vvc/vvcdec.h new file mode 100644 index 00000000000..aa3d7155249 --- /dev/null +++ b/libavcodec/vvc/vvcdec.h @@ -0,0 +1,229 @@ +/* + * VVC video decoder + * + * Copyright (C) 2021 Nuo Mi + * Copyright (C) 2022 Xu Mu + * + * 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 + */ + +#ifndef AVCODEC_VVC_VVCDEC_H +#define AVCODEC_VVC_VVCDEC_H + +#include "libavcodec/videodsp.h" +#include "libavcodec/vvc.h" + +#include "vvc_ps.h" +#include "vvcdsp.h" + +#define LUMA 0 +#define CHROMA 1 +#define CB 1 +#define CR 2 +#define JCBCR 3 + +#define MIN_TU_LOG2 2 ///< MinTbLog2SizeY +#define MIN_PU_LOG2 2 + +#define L0 0 +#define L1 1 + +typedef struct RefPicList { + struct VVCFrame *ref[VVC_MAX_REF_ENTRIES]; + int list[VVC_MAX_REF_ENTRIES]; + int isLongTerm[VVC_MAX_REF_ENTRIES]; + int nb_refs; +} RefPicList; + +typedef struct RefPicListTab { + RefPicList refPicList[2]; +} RefPicListTab; + +typedef struct VVCFrame { + struct AVFrame *frame; + + struct MvField *tab_dmvr_mvf; ///< RefStruct reference + RefPicListTab **rpl_tab; ///< RefStruct reference + RefPicListTab *rpl; ///< RefStruct reference + int nb_rpl_elems; + + int ctb_count; + + int poc; + + struct VVCFrame *collocated_ref; + + struct FrameProgress *progress; ///< RefStruct reference + + /** + * A sequence counter, so that old frames are output first + * after a POC reset + */ + uint16_t sequence; + /** + * A combination of VVC_FRAME_FLAG_* + */ + uint8_t flags; +} VVCFrame; + +typedef struct SliceContext { + int slice_idx; + VVCSH sh; + struct EntryPoint *eps; + int nb_eps; + RefPicList *rpl; + void *ref; ///< RefStruct reference, backing slice data +} SliceContext; + +typedef struct VVCFrameContext { + void *log_ctx; + + // +1 for the current frame + VVCFrame DPB[VVC_MAX_DPB_SIZE + 1]; + + struct AVFrame *frame; + struct AVFrame *output_frame; + + VVCFrameParamSets ps; + + SliceContext **slices; + int nb_slices; + int nb_slices_allocated; + + VVCFrame *ref; + + VVCDSPContext vvcdsp; + VideoDSPContext vdsp; + + struct VVCFrameThread *ft; + + uint64_t decode_order; + + struct FFRefStructPool *tab_dmvr_mvf_pool; + struct FFRefStructPool *rpl_tab_pool; + + struct FFRefStructPool *cu_pool; + struct FFRefStructPool *tu_pool; + + struct { + int16_t *slice_idx; + + DBParams *deblock; + struct SAOParams *sao; + struct ALFParams *alf; + + int *cb_pos_x[2]; ///< CbPosX[][][] + int *cb_pos_y[2]; ///< CbPosY[][][] + uint8_t *cb_width[2]; ///< CbWidth[][][] + uint8_t *cb_height[2]; ///< CbHeight[][][] + uint8_t *cqt_depth[2]; ///< CqtDepth[][][] + int8_t *qp[VVC_MAX_SAMPLE_ARRAYS]; + + uint8_t *skip; ///< CuSkipFlag[][] + uint8_t *ispmf; ///< intra_sub_partitions_mode_flag + uint8_t *msm[2]; ///< MttSplitMode[][][] in 32 pixels + uint8_t *imf; ///< IntraMipFlag[][] + uint8_t *imtf; ///< intra_mip_transposed_flag[][] + uint8_t *imm; ///< intra_mip_mode[][] + uint8_t *ipm; ///< IntraPredModeY[][] + uint8_t *cpm[2]; ///< CuPredMode[][][] + uint8_t *msf; ///< MergeSubblockFlag[][] + uint8_t *iaf; ///< InterAffineFlag[][] + uint8_t *mmi; ///< MotionModelIdc[][] + struct Mv *cp_mv[2]; ///< CpMvLX[][][][MAX_CONTROL_POINTS]; + struct MvField *mvf; ///< MvDmvrL0, MvDmvrL1 + + uint8_t *tu_coded_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< tu_y_coded_flag[][], tu_cb_coded_flag[][], tu_cr_coded_flag[][] + uint8_t *tu_joint_cbcr_residual_flag; ///< tu_joint_cbcr_residual_flag[][] + int *tb_pos_x0[2]; + int *tb_pos_y0[2]; + uint8_t *tb_width[2]; + uint8_t *tb_height[2]; + uint8_t *pcmf[2]; + + uint8_t *horizontal_bs[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t *vertical_bs[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t *horizontal_p; ///< horizontal maxFilterLengthPs for luma + uint8_t *horizontal_q; ///< horizontal maxFilterLengthQs for luma + uint8_t *vertical_p; ///< vertical maxFilterLengthPs for luma + uint8_t *vertical_q; ///< vertical maxFilterLengthQs for luma + + uint8_t *sao_pixel_buffer_h[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t *sao_pixel_buffer_v[VVC_MAX_SAMPLE_ARRAYS]; + uint8_t *alf_pixel_buffer_h[VVC_MAX_SAMPLE_ARRAYS][2]; + uint8_t *alf_pixel_buffer_v[VVC_MAX_SAMPLE_ARRAYS][2]; + + int *coeffs; + struct CTU *ctus; + + uint8_t *ibc_vir_buf[VVC_MAX_SAMPLE_ARRAYS]; ///< IbcVirBuf[] + + //used in arrays_init only + struct { + int ctu_count; + int ctu_size; + int pic_size_in_min_cb; + int pic_size_in_min_pu; + int pic_size_in_min_tu; + int ctu_width; + int ctu_height; + int width; + int height; + int chroma_format_idc; + int pixel_shift; + int bs_width; + int bs_height; + int ibc_buffer_width; ///< IbcBufWidth + } sz; + } tab; +} VVCFrameContext; + +typedef struct VVCContext { + struct AVCodecContext *avctx; + + CodedBitstreamContext *cbc; + CodedBitstreamFragment current_frame; + + VVCParamSets ps; + + int temporal_id; ///< temporal_id_plus1 - 1 + int poc_tid0; + + int eos; ///< current packet contains an EOS/EOB NAL + int last_eos; ///< last packet contains an EOS/EOB NAL + + enum VVCNALUnitType vcl_unit_type; + int no_output_before_recovery_flag; ///< NoOutputBeforeRecoveryFlag + int gdr_recovery_point_poc; ///< recoveryPointPocVal + + /** + * Sequence counters for decoded and output frames, so that old + * frames are output first after a POC reset + */ + uint16_t seq_decode; + uint16_t seq_output; + + struct AVExecutor *executor; + + VVCFrameContext *fcs; + int nb_fcs; + + uint64_t nb_frames; ///< processed frames + int nb_delayed; ///< delayed frames +} VVCContext ; + +#endif /* AVCODEC_VVC_VVCDEC_H */ diff --git a/libavcodec/vvc/vvcdsp.c b/libavcodec/vvc/vvcdsp.c new file mode 100644 index 00000000000..d63b9bc9b33 --- /dev/null +++ b/libavcodec/vvc/vvcdsp.c @@ -0,0 +1,127 @@ +/* + * VVC DSP + * + * Copyright (C) 2021 Nuo Mi + * + * 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 "vvcdsp.h" +#include "vvc_ctu.h" +#include "vvc_itx_1d.h" + +#define VVC_SIGN(v) (v < 0 ? -1 : !!v) + +static void av_always_inline pad_int16(int16_t *_dst, const ptrdiff_t dst_stride, const int width, const int height) +{ + const int padded_width = width + 2; + int16_t *dst; + for (int y = 0; y < height; y++) { + dst = _dst + y * dst_stride; + for (int x = 0; x < width; x++) { + dst[-1] = dst[0]; + dst[width] = dst[width - 1]; + } + } + + _dst--; + //top + memcpy(_dst - dst_stride, _dst, padded_width * sizeof(int16_t)); + //bottom + _dst += dst_stride * height; + memcpy(_dst, _dst - dst_stride, padded_width * sizeof(int16_t)); +} + +static int vvc_sad(const int16_t *src0, const int16_t *src1, int dx, int dy, + const int block_w, const int block_h) +{ + int sad = 0; + dx -= 2; + dy -= 2; + src0 += (2 + dy) * MAX_PB_SIZE + 2 + dx; + src1 += (2 - dy) * MAX_PB_SIZE + 2 - dx; + for (int y = 0; y < block_h; y += 2) { + for (int x = 0; x < block_w; x++) { + sad += FFABS(src0[x] - src1[x]); + } + src0 += 2 * MAX_PB_SIZE; + src1 += 2 * MAX_PB_SIZE; + } + return sad; +} + +typedef struct IntraEdgeParams { + uint8_t* top; + uint8_t* left; + int filter_flag; + + uint16_t left_array[6 * MAX_TB_SIZE + 5]; + uint16_t filtered_left_array[6 * MAX_TB_SIZE + 5]; + uint16_t top_array[6 * MAX_TB_SIZE + 5]; + uint16_t filtered_top_array[6 * MAX_TB_SIZE + 5]; +} IntraEdgeParams; + +#define PROF_BORDER_EXT 1 +#define PROF_BLOCK_SIZE (AFFINE_MIN_BLOCK_SIZE + PROF_BORDER_EXT * 2) +#define BDOF_BORDER_EXT 1 + +#define BDOF_PADDED_SIZE (16 + BDOF_BORDER_EXT * 2) +#define BDOF_BLOCK_SIZE 4 +#define BDOF_GRADIENT_SIZE (BDOF_BLOCK_SIZE + BDOF_BORDER_EXT * 2) + +#define BIT_DEPTH 8 +#include "vvcdsp_template.c" +#undef BIT_DEPTH + +#define BIT_DEPTH 10 +#include "vvcdsp_template.c" +#undef BIT_DEPTH + +#define BIT_DEPTH 12 +#include "vvcdsp_template.c" +#undef BIT_DEPTH + +void ff_vvc_dsp_init(VVCDSPContext *vvcdsp, int bit_depth) +{ +#undef FUNC +#define FUNC(a, depth) a ## _ ## depth + +#define VVC_DSP(depth) \ + FUNC(ff_vvc_inter_dsp_init, depth)(&vvcdsp->inter); \ + FUNC(ff_vvc_intra_dsp_init, depth)(&vvcdsp->intra); \ + FUNC(ff_vvc_itx_dsp_init, depth)(&vvcdsp->itx); \ + FUNC(ff_vvc_lmcs_dsp_init, depth)(&vvcdsp->lmcs); \ + FUNC(ff_vvc_lf_dsp_init, depth)(&vvcdsp->lf); \ + FUNC(ff_vvc_sao_dsp_init, depth)(&vvcdsp->sao); \ + FUNC(ff_vvc_alf_dsp_init, depth)(&vvcdsp->alf); \ + + switch (bit_depth) { + case 12: + VVC_DSP(12); + break; + case 10: + VVC_DSP(10); + break; + default: + VVC_DSP(8); + break; + } + +#if ARCH_X86 + ff_vvc_dsp_init_x86(vvcdsp, bit_depth); +#endif +} diff --git a/libavcodec/vvc/vvcdsp.h b/libavcodec/vvc/vvcdsp.h new file mode 100644 index 00000000000..f4fb3cb7d7b --- /dev/null +++ b/libavcodec/vvc/vvcdsp.h @@ -0,0 +1,172 @@ +/* + * VVC DSP + * + * Copyright (C) 2021 Nuo Mi + * + * + * 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 + */ + +#ifndef AVCODEC_VVC_VVCDSP_H +#define AVCODEC_VVC_VVCDSP_H + +#include +#include + +enum TxType { + DCT2, + DST7, + DCT8, + N_TX_TYPE, +}; + +enum TxSize { + TX_SIZE_2, + TX_SIZE_4, + TX_SIZE_8, + TX_SIZE_16, + TX_SIZE_32, + TX_SIZE_64, + N_TX_SIZE, +}; + +typedef struct VVCInterDSPContext { + void (*put[2 /* luma, chroma */][7 /* log2(width) - 1 */][2 /* int, frac */][2 /* int, frac */])( + int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int height, + const int8_t *hf, const int8_t *vf, int width); + + void (*put_uni[2 /* luma, chroma */][7 /* log2(width) - 1 */][2 /* int, frac */][2 /* int, frac */])( + uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, int height, + const int8_t *hf, const int8_t *vf, int width); + + void (*put_uni_w[2 /* luma, chroma */][7 /* log2(width) - 1 */][2 /* int, frac */][2 /* int, frac */])( + uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, int height, + int denom, int wx, int ox, const int8_t *hf, const int8_t *vf, int width); + + void (*avg)(uint8_t *dst, ptrdiff_t dst_stride, + const int16_t *src0, const int16_t *src1, int width, int height); + + void (*w_avg)(uint8_t *_dst, const ptrdiff_t _dst_stride, + const int16_t *src0, const int16_t *src1, int width, int height, + int denom, int w0, int w1, int o0, int o1); + + void (*put_ciip)(uint8_t *dst, ptrdiff_t dst_stride, int width, int height, + const uint8_t *inter, ptrdiff_t inter_stride, int inter_weight); + + void (*put_gpm)(uint8_t *dst, ptrdiff_t dst_stride, int width, int height, + const int16_t *src0, const int16_t *src1, + const uint8_t *weights, int step_x, int step_y); + + void (*fetch_samples)(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int x_frac, int y_frac); + void (*bdof_fetch_samples)(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int x_frac, int y_frac, + int width, int height); + + void (*prof_grad_filter)(int16_t *gradient_h, int16_t *gradient_v, const ptrdiff_t gradient_stride, + const int16_t *src, const ptrdiff_t src_stride, int width, int height, const int pad); + void (*apply_prof)(int16_t *dst, const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y); + + void (*apply_prof_uni)(uint8_t *dst, ptrdiff_t dst_stride, const int16_t *src, + const int16_t *diff_mv_x, const int16_t *diff_mv_y); + void (*apply_prof_uni_w)(uint8_t *dst, const ptrdiff_t dst_stride, const int16_t *src, + const int16_t *diff_mv_x, const int16_t *diff_mv_y, int denom, int wx, int ox); + + void (*apply_bdof)(uint8_t *dst, ptrdiff_t dst_stride, int16_t *src0, int16_t *src1, int block_w, int block_h); + + int (*sad)(const int16_t *src0, const int16_t *src1, int dx, int dy, int block_w, int block_h); + void (*dmvr[2][2])(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int height, + intptr_t mx, intptr_t my, int width); +} VVCInterDSPContext; + +struct VVCLocalContext; + +typedef struct VVCIntraDSPContext { + void (*intra_cclm_pred)(const struct VVCLocalContext *lc, int x0, int y0, int w, int h); + void (*lmcs_scale_chroma)(struct VVCLocalContext *lc, int *dst, const int *coeff, int w, int h, int x0_cu, int y0_cu); + void (*intra_pred)(const struct VVCLocalContext *lc, int x0, int y0, int w, int h, int c_idx); + void (*pred_planar)(uint8_t *src, const uint8_t *top, const uint8_t *left, int w, int h, ptrdiff_t stride); + void (*pred_mip)(uint8_t *src, const uint8_t *top, const uint8_t *left, int w, int h, ptrdiff_t stride, + int mode_id, int is_transpose); + void (*pred_dc)(uint8_t *src, const uint8_t *top, const uint8_t *left, int w, int h, ptrdiff_t stride); + void (*pred_v)(uint8_t *src, const uint8_t *_top, int w, int h, ptrdiff_t stride); + void (*pred_h)(uint8_t *src, const uint8_t *_left, int w, int h, ptrdiff_t stride); + void (*pred_angular_v)(uint8_t *src, const uint8_t *_top, const uint8_t *_left, + int w, int h, ptrdiff_t stride, int c_idx, int mode, int ref_idx, int filter_flag, int need_pdpc); + void (*pred_angular_h)(uint8_t *src, const uint8_t *_top, const uint8_t *_left, int w, int h, ptrdiff_t stride, + int c_idx, int mode, int ref_idx, int filter_flag, int need_pdpc); +} VVCIntraDSPContext; + +typedef struct VVCItxDSPContext { + void (*add_residual)(uint8_t *dst, const int *res, int width, int height, ptrdiff_t stride); + void (*add_residual_joint)(uint8_t *dst, const int *res, int width, int height, ptrdiff_t stride, int c_sign, int shift); + void (*pred_residual_joint)(int *buf, int width, int height, int c_sign, int shift); + + void (*itx[N_TX_TYPE][N_TX_SIZE])(int *coeffs, ptrdiff_t step, size_t nz); + void (*transform_bdpcm)(int *coeffs, int width, int height, int vertical, int log2_transform_range); +} VVCItxDSPContext; + +typedef struct VVCLMCSDSPContext { + void (*filter)(uint8_t *dst, ptrdiff_t dst_stride, int width, int height, const void *lut); +} VVCLMCSDSPContext; + +typedef struct VVCLFDSPContext { + int (*ladf_level[2 /* h, v */])(const uint8_t *pix, ptrdiff_t stride); + + void (*filter_luma[2 /* h, v */])(uint8_t *pix, ptrdiff_t stride, const int32_t *beta, const int32_t *tc, + const uint8_t *no_p, const uint8_t *no_q, const uint8_t *max_len_p, const uint8_t *max_len_q, int hor_ctu_edge); + void (*filter_chroma[2 /* h, v */])(uint8_t *pix, ptrdiff_t stride, const int32_t *beta, const int32_t *tc, + const uint8_t *no_p, const uint8_t *no_q, const uint8_t *max_len_p, const uint8_t *max_len_q, int shift); +} VVCLFDSPContext; + +struct SAOParams; +typedef struct VVCSAODSPContext { + void (*band_filter[9])(uint8_t *dst, const uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride, + const int16_t *sao_offset_val, int sao_left_class, int width, int height); + /* implicit src_stride parameter has value of 2 * MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE */ + void (*edge_filter[9])(uint8_t *dst /* align 16 */, const uint8_t *src /* align 32 */, ptrdiff_t dst_stride, + const int16_t *sao_offset_val, int sao_eo_class, int width, int height); + void (*edge_restore[2])(uint8_t *dst, const uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride, + const struct SAOParams *sao, const int *borders, int width, int height, int c_idx, + const uint8_t *vert_edge, const uint8_t *horiz_edge, const uint8_t *diag_edge); +} VVCSAODSPContext; + +typedef struct VVCALFDSPContext { + void (*filter[2 /* luma, chroma */])(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, + int width, int height, const int16_t *filter, const int16_t *clip, int vb_pos); + void (*filter_cc)(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *luma, ptrdiff_t luma_stride, + int width, int height, int hs, int vs, const int16_t *filter, int vb_pos); + + void (*classify)(int *class_idx, int *transpose_idx, const uint8_t *src, ptrdiff_t src_stride, int width, int height, + int vb_pos, int *gradient_tmp); + void (*recon_coeff_and_clip)(int16_t *coeff, int16_t *clip, const int *class_idx, const int *transpose_idx, int size, + const int16_t *coeff_set, const uint8_t *clip_idx_set, const uint8_t *class_to_filt); +} VVCALFDSPContext; + +typedef struct VVCDSPContext { + VVCInterDSPContext inter; + VVCIntraDSPContext intra; + VVCItxDSPContext itx; + VVCLMCSDSPContext lmcs; + VVCLFDSPContext lf; + VVCSAODSPContext sao; + VVCALFDSPContext alf; +} VVCDSPContext; + +void ff_vvc_dsp_init(VVCDSPContext *hpc, int bit_depth); + +void ff_vvc_dsp_init_x86(VVCDSPContext *hpc, const int bit_depth); + +#endif /* AVCODEC_VVC_VVCDSP_H */ diff --git a/libavcodec/vvc/vvcdsp_template.c b/libavcodec/vvc/vvcdsp_template.c new file mode 100644 index 00000000000..33815d67652 --- /dev/null +++ b/libavcodec/vvc/vvcdsp_template.c @@ -0,0 +1,120 @@ +/* + * VVC transform and residual DSP + * + * Copyright (C) 2021 Nuo Mi + * + * 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/frame.h" +#include "libavcodec/bit_depth_template.c" + +#include "vvcdec.h" +#include "vvc_data.h" + +#include "vvc_inter_template.c" +#include "vvc_intra_template.c" +#include "vvc_filter_template.c" + +static void FUNC(add_residual)(uint8_t *_dst, const int *res, + const int w, const int h, const ptrdiff_t _stride) +{ + pixel *dst = (pixel *)_dst; + + const int stride = _stride / sizeof(pixel); + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + dst[x] = av_clip_pixel(dst[x] + *res); + res++; + } + dst += stride; + } +} + +static void FUNC(add_residual_joint)(uint8_t *_dst, const int *res, + const int w, const int h, const ptrdiff_t _stride, const int c_sign, const int shift) +{ + pixel *dst = (pixel *)_dst; + + const int stride = _stride / sizeof(pixel); + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + const int r = ((*res) * c_sign) >> shift; + dst[x] = av_clip_pixel(dst[x] + r); + res++; + } + dst += stride; + } +} + +static void FUNC(pred_residual_joint)(int *buf, const int w, const int h, + const int c_sign, const int shift) +{ + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + *buf = ((*buf) * c_sign) >> shift; + buf++; + } + } +} + +static void FUNC(transform_bdpcm)(int *coeffs, const int width, const int height, + const int vertical, const int log2_transform_range) +{ + int x, y; + + if (vertical) { + coeffs += width; + for (y = 0; y < height - 1; y++) { + for (x = 0; x < width; x++) + coeffs[x] = av_clip_intp2(coeffs[x] + coeffs[x - width], log2_transform_range); + coeffs += width; + } + } else { + for (y = 0; y < height; y++) { + for (x = 1; x < width; x++) + coeffs[x] = av_clip_intp2(coeffs[x] + coeffs[x - 1], log2_transform_range); + coeffs += width; + } + } +} + +static void FUNC(ff_vvc_itx_dsp_init)(VVCItxDSPContext *const itx) +{ +#define VVC_ITX(TYPE, type, s) \ + itx->itx[TYPE][TX_SIZE_##s] = ff_vvc_inv_##type##_##s; \ + +#define VVC_ITX_COMMON(TYPE, type) \ + VVC_ITX(TYPE, type, 4); \ + VVC_ITX(TYPE, type, 8); \ + VVC_ITX(TYPE, type, 16); \ + VVC_ITX(TYPE, type, 32); + + itx->add_residual = FUNC(add_residual); + itx->add_residual_joint = FUNC(add_residual_joint); + itx->pred_residual_joint = FUNC(pred_residual_joint); + itx->transform_bdpcm = FUNC(transform_bdpcm); + VVC_ITX(DCT2, dct2, 2) + VVC_ITX(DCT2, dct2, 64) + VVC_ITX_COMMON(DCT2, dct2) + VVC_ITX_COMMON(DCT8, dct8) + VVC_ITX_COMMON(DST7, dst7) + +#undef VVC_ITX +#undef VVC_ITX_COMMON +} diff --git a/libavcodec/vvc_parser.c b/libavcodec/vvc_parser.c new file mode 100644 index 00000000000..245cb214c97 --- /dev/null +++ b/libavcodec/vvc_parser.c @@ -0,0 +1,517 @@ +/* + * H.266 / VVC parser + * + * Copyright (C) 2021 Nuo Mi + * + * 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 "cbs.h" +#include "cbs_h266.h" +#include "parser.h" + +#define START_CODE 0x000001 ///< start_code_prefix_one_3bytes +#define IS_IDR(nut) (nut == VVC_IDR_W_RADL || nut == VVC_IDR_N_LP) +#define IS_H266_SLICE(nut) (nut <= VVC_RASL_NUT || (nut >= VVC_IDR_W_RADL && nut <= VVC_GDR_NUT)) + +typedef struct PuInfo { + const H266RawPPS *pps; + const H266RawSPS *sps; + const H266RawPictureHeader *ph; + const H266RawSlice *slice; + int pic_type; +} PuInfo; + +typedef struct AuDetector { + uint8_t prev_layer_id; + int prev_tid0_poc; + int prev_poc; +} AuDetector; + +typedef struct VVCParserContext { + ParseContext pc; + CodedBitstreamContext *cbc; + + CodedBitstreamFragment picture_unit; + + AVPacket au; + AVPacket last_au; + + AuDetector au_detector; + + int parsed_extradata; +} VVCParserContext; + +static const enum AVPixelFormat pix_fmts_8bit[] = { + AV_PIX_FMT_GRAY8, AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P +}; + +static const enum AVPixelFormat pix_fmts_10bit[] = { + AV_PIX_FMT_GRAY10, AV_PIX_FMT_YUV420P10, + AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10 +}; + +static int get_format(const H266RawSPS *sps) +{ + switch (sps->sps_bitdepth_minus8) { + case 0: + return pix_fmts_8bit[sps->sps_chroma_format_idc]; + case 2: + return pix_fmts_10bit[sps->sps_chroma_format_idc]; + } + return AV_PIX_FMT_NONE; +} + +/** + * Find the end of the current frame in the bitstream. + * @return the position of the first byte of the next frame, or END_NOT_FOUND + */ +static int find_frame_end(AVCodecParserContext *s, const uint8_t *buf, + int buf_size) +{ + VVCParserContext *ctx = s->priv_data; + ParseContext *pc = &ctx->pc; + int i; + + for (i = 0; i < buf_size; i++) { + int nut, code_len; + + pc->state64 = (pc->state64 << 8) | buf[i]; + + if (((pc->state64 >> 3 * 8) & 0xFFFFFF) != START_CODE) + continue; + + code_len = ((pc->state64 >> 3 * 8) & 0xFFFFFFFF) == 0x01 ? 4 : 3; + + nut = (pc->state64 >> (8 + 3)) & 0x1F; + // 7.4.2.4.3 and 7.4.2.4.4 + if ((nut >= VVC_OPI_NUT && nut <= VVC_PREFIX_APS_NUT && + nut != VVC_PH_NUT) || nut == VVC_AUD_NUT + || (nut == VVC_PREFIX_SEI_NUT && !pc->frame_start_found) + || nut == VVC_RSV_NVCL_26 || nut == VVC_UNSPEC_28 + || nut == VVC_UNSPEC_29) { + if (pc->frame_start_found) { + pc->frame_start_found = 0; + return i - (code_len + 2); + } + } else if (nut == VVC_PH_NUT || IS_H266_SLICE(nut)) { + int sh_picture_header_in_slice_header_flag = buf[i] >> 7; + + if (nut == VVC_PH_NUT || sh_picture_header_in_slice_header_flag) { + if (!pc->frame_start_found) { + pc->frame_start_found = 1; + } else { // First slice of next frame found + pc->frame_start_found = 0; + return i - (code_len + 2); + } + } + } + } + return END_NOT_FOUND; +} + +static int get_pict_type(const CodedBitstreamFragment *pu) +{ + int has_p = 0; + for (int i = 0; i < pu->nb_units; i++) { + CodedBitstreamUnit *unit = &pu->units[i]; + if (IS_H266_SLICE(unit->type)) { + const H266RawSlice *slice = unit->content; + uint8_t type = slice->header.sh_slice_type; + if (type == VVC_SLICE_TYPE_B) { + return AV_PICTURE_TYPE_B; + } + if (type == VVC_SLICE_TYPE_P) { + has_p = 1; + } + } + } + return has_p ? AV_PICTURE_TYPE_P : AV_PICTURE_TYPE_I; +} + +static void set_parser_ctx(AVCodecParserContext *s, AVCodecContext *avctx, + const PuInfo *pu) +{ + static const uint8_t h266_sub_width_c[] = { + 1, 2, 2, 1 + }; + static const uint8_t h266_sub_height_c[] = { + 1, 2, 1, 1 + }; + const H266RawSPS *sps = pu->sps; + const H266RawPPS *pps = pu->pps; + const H266RawNALUnitHeader *nal = &pu->slice->header.nal_unit_header; + + s->pict_type = pu->pic_type; + s->format = get_format(sps); + s->picture_structure = AV_PICTURE_STRUCTURE_FRAME; + + s->key_frame = nal->nal_unit_type == VVC_IDR_W_RADL || + nal->nal_unit_type == VVC_IDR_N_LP || + nal->nal_unit_type == VVC_CRA_NUT || + nal->nal_unit_type == VVC_GDR_NUT; + + s->coded_width = pps->pps_pic_width_in_luma_samples; + s->coded_height = pps->pps_pic_height_in_luma_samples; + s->width = pps->pps_pic_width_in_luma_samples - + (pps->pps_conf_win_left_offset + pps->pps_conf_win_right_offset) * + h266_sub_width_c[sps->sps_chroma_format_idc]; + s->height = pps->pps_pic_height_in_luma_samples - + (pps->pps_conf_win_top_offset + pps->pps_conf_win_bottom_offset) * + h266_sub_height_c[sps->sps_chroma_format_idc]; + + avctx->profile = sps->profile_tier_level.general_profile_idc; + avctx->level = sps->profile_tier_level.general_level_idc; + + avctx->colorspace = (enum AVColorSpace) sps->vui.vui_matrix_coeffs; + avctx->color_primaries = (enum AVColorPrimaries) sps->vui.vui_colour_primaries; + avctx->color_trc = (enum AVColorTransferCharacteristic) sps->vui.vui_transfer_characteristics; + avctx->color_range = + sps->vui.vui_full_range_flag ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; + + avctx->has_b_frames = (sps->sps_max_sublayers_minus1 + 1) > 2 ? 2 : + sps->sps_max_sublayers_minus1; + avctx->max_b_frames = sps->sps_max_sublayers_minus1; + + if (sps->sps_ptl_dpb_hrd_params_present_flag && + sps->sps_timing_hrd_params_present_flag) { + int num = sps->sps_general_timing_hrd_parameters.num_units_in_tick; + int den = sps->sps_general_timing_hrd_parameters.time_scale; + + if (num != 0 && den != 0) + av_reduce(&avctx->framerate.den, &avctx->framerate.num, + num, den, 1 << 30); + } +} + +//8.3.1 Decoding process for picture order count. +//VTM did not follow the spec, and it's much simpler than spec. +//We follow the VTM. +static void get_slice_poc(VVCParserContext *s, int *poc, + const H266RawSPS *sps, + const H266RawPictureHeader *ph, + const H266RawSliceHeader *slice, void *log_ctx) +{ + int poc_msb, max_poc_lsb, poc_lsb; + AuDetector *d = &s->au_detector; + max_poc_lsb = 1 << (sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4); + poc_lsb = ph->ph_pic_order_cnt_lsb; + if (IS_IDR(slice->nal_unit_header.nal_unit_type)) { + if (ph->ph_poc_msb_cycle_present_flag) + poc_msb = ph->ph_poc_msb_cycle_val * max_poc_lsb; + else + poc_msb = 0; + } else { + int prev_poc = d->prev_tid0_poc; + int prev_poc_lsb = prev_poc & (max_poc_lsb - 1); + int prev_poc_msb = prev_poc - prev_poc_lsb; + if (ph->ph_poc_msb_cycle_present_flag) { + poc_msb = ph->ph_poc_msb_cycle_val * max_poc_lsb; + } else { + if ((poc_lsb < prev_poc_lsb) && ((prev_poc_lsb - poc_lsb) >= + (max_poc_lsb / 2))) + poc_msb = prev_poc_msb + (unsigned)max_poc_lsb; + else if ((poc_lsb > prev_poc_lsb) && ((poc_lsb - prev_poc_lsb) > + (max_poc_lsb / 2))) + poc_msb = prev_poc_msb - (unsigned)max_poc_lsb; + else + poc_msb = prev_poc_msb; + } + } + + *poc = poc_msb + poc_lsb; +} + +static void au_detector_init(AuDetector *d) +{ + d->prev_layer_id = UINT8_MAX; + d->prev_poc = INT_MAX; + d->prev_tid0_poc = INT_MAX; +} + +static int is_au_start(VVCParserContext *s, const PuInfo *pu, void *log_ctx) +{ + //7.4.2.4.3 + AuDetector *d = &s->au_detector; + const H266RawSPS *sps = pu->sps; + const H266RawNALUnitHeader *nal = &pu->slice->header.nal_unit_header; + const H266RawPictureHeader *ph = pu->ph; + const H266RawSlice *slice = pu->slice; + int ret, poc, nut; + + get_slice_poc(s, &poc, sps, ph, &slice->header, log_ctx); + + ret = (nal->nuh_layer_id <= d->prev_layer_id) || (poc != d->prev_poc); + + nut = nal->nal_unit_type; + d->prev_layer_id = nal->nuh_layer_id; + d->prev_poc = poc; + if (nal->nuh_temporal_id_plus1 == 1 && + !ph->ph_non_ref_pic_flag && nut != VVC_RADL_NUT + && nut != VVC_RASL_NUT) { + d->prev_tid0_poc = poc; + } + return ret; +} + +static int get_pu_info(PuInfo *info, const CodedBitstreamH266Context *h266, + const CodedBitstreamFragment *pu, void *logctx) +{ + const H266RawNALUnitHeader *nal; + int ret; + + memset(info, 0, sizeof(*info)); + for (int i = 0; i < pu->nb_units; i++) { + nal = pu->units[i].content; + if (!nal) + continue; + if ( nal->nal_unit_type == VVC_PH_NUT ) { + const H266RawPH *ph = pu->units[i].content; + info->ph = &ph->ph_picture_header; + } else if (IS_H266_SLICE(nal->nal_unit_type)) { + info->slice = pu->units[i].content; + if (info->slice->header.sh_picture_header_in_slice_header_flag) + info->ph = &info->slice->header.sh_picture_header; + if (!info->ph) { + av_log(logctx, AV_LOG_ERROR, + "can't find picture header in picture unit.\n"); + ret = AVERROR_INVALIDDATA; + goto error; + } + break; + } + } + if (!info->slice) { + av_log(logctx, AV_LOG_ERROR, "can't find slice in picture unit.\n"); + ret = AVERROR_INVALIDDATA; + goto error; + } + info->pps = h266->pps[info->ph->ph_pic_parameter_set_id]; + if (!info->pps) { + av_log(logctx, AV_LOG_ERROR, "PPS id %d is not avaliable.\n", + info->ph->ph_pic_parameter_set_id); + ret = AVERROR_INVALIDDATA; + goto error; + } + info->sps = h266->sps[info->pps->pps_seq_parameter_set_id]; + if (!info->sps) { + av_log(logctx, AV_LOG_ERROR, "SPS id %d is not avaliable.\n", + info->pps->pps_seq_parameter_set_id); + ret = AVERROR_INVALIDDATA; + goto error; + } + info->pic_type = get_pict_type(pu); + return 0; +error: + memset(info, 0, sizeof(*info)); + return ret; +} + +static int append_au(AVPacket *pkt, const uint8_t *buf, int buf_size) +{ + int offset = pkt->size; + int ret; + if ((ret = av_grow_packet(pkt, buf_size)) < 0) + goto end; + memcpy(pkt->data + offset, buf, buf_size); +end: + return ret; +} + +/** + * Parse NAL units of found picture and decode some basic information. + * + * @param s parser context. + * @param avctx codec context. + * @param buf buffer with field/frame data. + * @param buf_size size of the buffer. + * @return < 0 for error, == 0 for a complete au, > 0 is not a completed au. + */ +static int parse_nal_units(AVCodecParserContext *s, const uint8_t *buf, + int buf_size, AVCodecContext *avctx) +{ + VVCParserContext *ctx = s->priv_data; + const CodedBitstreamH266Context *h266 = ctx->cbc->priv_data; + + CodedBitstreamFragment *pu = &ctx->picture_unit; + int ret; + PuInfo info; + + if (!buf_size) { + if (ctx->au.size) { + av_packet_move_ref(&ctx->last_au, &ctx->au); + return 0; + } + return 1; + } + + if ((ret = ff_cbs_read(ctx->cbc, pu, buf, buf_size)) < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to parse picture unit.\n"); + goto end; + } + if ((ret = get_pu_info(&info, h266, pu, avctx)) < 0) + goto end; + if (append_au(&ctx->au, buf, buf_size) < 0) { + ret = AVERROR(ENOMEM); + goto end; + } + if (is_au_start(ctx, &info, avctx)) { + set_parser_ctx(s, avctx, &info); + av_packet_move_ref(&ctx->last_au, &ctx->au); + } else { + ret = 1; //not a completed au + } +end: + ff_cbs_fragment_reset(pu); + return ret; +} + +/** + * Combine PU to AU + * + * @param s parser context. + * @param avctx codec context. + * @param buf buffer to a PU. + * @param buf_size size of the buffer. + * @return < 0 for error, == 0 a complete au, > 0 not a completed au. + */ +static int combine_au(AVCodecParserContext *s, AVCodecContext *avctx, + const uint8_t **buf, int *buf_size) +{ + VVCParserContext *ctx = s->priv_data; + int ret; + + ctx->cbc->log_ctx = avctx; + + av_packet_unref(&ctx->last_au); + ret = parse_nal_units(s, *buf, *buf_size, avctx); + if (ret == 0) { + if (ctx->last_au.size) { + *buf = ctx->last_au.data; + *buf_size = ctx->last_au.size; + } else { + ret = 1; //no output + } + } + ctx->cbc->log_ctx = NULL; + return ret; +} + +static int vvc_parser_parse(AVCodecParserContext *s, AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + int next, ret; + VVCParserContext *ctx = s->priv_data; + ParseContext *pc = &ctx->pc; + CodedBitstreamFragment *pu = &ctx->picture_unit; + + int is_dummy_buf = !buf_size; + int flush = !buf_size; + const uint8_t *dummy_buf = buf; + + *poutbuf = NULL; + *poutbuf_size = 0; + + if (avctx->extradata_size && !ctx->parsed_extradata) { + ctx->parsed_extradata = 1; + + ret = ff_cbs_read_extradata_from_codec(ctx->cbc, pu, avctx); + if (ret < 0) + av_log(avctx, AV_LOG_WARNING, "Failed to parse extradata.\n"); + + ff_cbs_fragment_reset(pu); + } + + if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) { + next = buf_size; + } else { + next = find_frame_end(s, buf, buf_size); + if (ff_combine_frame(pc, next, &buf, &buf_size) < 0) + return buf_size; + } + + is_dummy_buf &= (dummy_buf == buf); + + if (!is_dummy_buf) { + ret = combine_au(s, avctx, &buf, &buf_size); + if (ret > 0 && flush) { + buf_size = 0; + ret = combine_au(s, avctx, &buf, &buf_size); + } + if (ret != 0) + return next; + } + + *poutbuf = buf; + *poutbuf_size = buf_size; + + return next; +} + +static const CodedBitstreamUnitType decompose_unit_types[] = { + VVC_TRAIL_NUT, + VVC_STSA_NUT, + VVC_RADL_NUT, + VVC_RASL_NUT, + VVC_IDR_W_RADL, + VVC_IDR_N_LP, + VVC_CRA_NUT, + VVC_GDR_NUT, + VVC_VPS_NUT, + VVC_SPS_NUT, + VVC_PPS_NUT, + VVC_PH_NUT, + VVC_AUD_NUT, +}; + +static av_cold int vvc_parser_init(AVCodecParserContext *s) +{ + VVCParserContext *ctx = s->priv_data; + int ret; + + ret = ff_cbs_init(&ctx->cbc, AV_CODEC_ID_VVC, NULL); + if (ret < 0) + return ret; + au_detector_init(&ctx->au_detector); + + ctx->cbc->decompose_unit_types = decompose_unit_types; + ctx->cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types); + + return ret; +} + +static av_cold void vvc_parser_close(AVCodecParserContext *s) +{ + VVCParserContext *ctx = s->priv_data; + + av_packet_unref(&ctx->au); + av_packet_unref(&ctx->last_au); + ff_cbs_fragment_free(&ctx->picture_unit); + + ff_cbs_close(&ctx->cbc); + av_freep(&ctx->pc.buffer); +} + +const AVCodecParser ff_vvc_parser = { + .codec_ids = { AV_CODEC_ID_VVC }, + .priv_data_size = sizeof(VVCParserContext), + .parser_init = vvc_parser_init, + .parser_close = vvc_parser_close, + .parser_parse = vvc_parser_parse, +}; diff --git a/libavcodec/wavarc.c b/libavcodec/wavarc.c index 1106b5281ad..536c74e478d 100644 --- a/libavcodec/wavarc.c +++ b/libavcodec/wavarc.c @@ -30,6 +30,8 @@ #include "unary.h" typedef struct WavArcContext { + AVClass *av_class; + GetBitContext gb; int shift; @@ -47,6 +49,16 @@ typedef struct WavArcContext { int pred[2][70]; int filter[2][70]; int samples[2][640]; + uint8_t model[256]; + uint16_t freqs[257]; + uint16_t ac_value; + uint16_t ac_low; + uint16_t ac_high; + uint16_t range_high; + uint16_t range_low; + uint16_t freq_range; + int ac_pred[70]; + int ac_out[570]; } WavArcContext; static av_cold int wavarc_init(AVCodecContext *avctx) @@ -90,6 +102,7 @@ static av_cold int wavarc_init(AVCodecContext *avctx) case MKTAG('2','S','L','P'): case MKTAG('3','N','L','P'): case MKTAG('4','A','L','P'): + case MKTAG('5','E','L','P'): s->nb_samples = 570; s->offset = 70; break; @@ -230,18 +243,18 @@ static int decode_1dif(AVCodecContext *avctx, break; case 3: for (int n = 0; n < s->nb_samples; n++) - samples[n + 4] = get_srice(gb, k) + (samples[n + 3] - samples[n + 2]) * 3 + + samples[n + 4] = get_srice(gb, k) + (samples[n + 3] - (unsigned)samples[n + 2]) * 3 + samples[n + 1]; finished = 1; break; case 2: for (int n = 0; n < s->nb_samples; n++) - samples[n + 4] = get_srice(gb, k) + (samples[n + 3] * 2 - samples[n + 2]); + samples[n + 4] = get_srice(gb, k) + (samples[n + 3] * 2U - samples[n + 2]); finished = 1; break; case 1: for (int n = 0; n < s->nb_samples; n++) - samples[n + 4] = get_srice(gb, k) + samples[n + 3]; + samples[n + 4] = get_srice(gb, k) + (unsigned)samples[n + 3]; finished = 1; break; case 0: @@ -330,13 +343,13 @@ static int decode_2slp(AVCodecContext *avctx, break; case 4: for (int n = 0; n < s->nb_samples; n++) - samples[n + 70] = get_srice(gb, k) + (samples[n + 69] - samples[n + 68]) * 3 + + samples[n + 70] = get_srice(gb, k) + (samples[n + 69] - (unsigned)samples[n + 68]) * 3 + samples[n + 67]; finished = 1; break; case 3: for (int n = 0; n < s->nb_samples; n++) - samples[n + 70] = get_srice(gb, k) + (samples[n + 69] * 2 - samples[n + 68]); + samples[n + 70] = get_srice(gb, k) + (samples[n + 69] * 2U - samples[n + 68]); finished = 1; break; case 2: @@ -346,12 +359,12 @@ static int decode_2slp(AVCodecContext *avctx, break; case 1: for (int n = 0; n < s->nb_samples; n++) - samples[n + 70] = get_srice(gb, k) + samples[n + 69]; + samples[n + 70] = get_srice(gb, k) + (unsigned)samples[n + 69]; finished = 1; break; case 0: order = get_urice(gb, 2); - if ((unsigned)order >= FF_ARRAY_ELEMS(s->filter[ch])) + if ((unsigned)order > FF_ARRAY_ELEMS(s->filter[ch])) return AVERROR_INVALIDDATA; for (int o = 0; o < order; o++) s->filter[ch][o] = get_srice(gb, 2); @@ -361,7 +374,366 @@ static int decode_2slp(AVCodecContext *avctx, for (int o = 0; o < order; o++) sum += s->filter[ch][o] * (unsigned)samples[n + 70 - o - 1]; - samples[n + 70] = get_srice(gb, k) + (sum >> 4); + samples[n + 70] = get_srice(gb, k) + (unsigned)(sum >> 4); + } + finished = 1; + break; + default: + return AVERROR_INVALIDDATA; + } + + if (finished == 1 && avctx->ch_layout.nb_channels == 2) { + if (ch == 0) + correlated = get_bits1(gb); + finished = ch != 0; + do_stereo(s, ch, correlated, 70); + ch = 1; + } + } + + if (avctx->ch_layout.nb_channels == 1) { + for (int n = 0; n < 70; n++) + s->samples[0][n] = s->samples[0][s->nb_samples + n]; + } + + return 0; +} + +static int ac_init(AVCodecContext *avctx, + WavArcContext *s, GetBitContext *gb) +{ + s->ac_low = 0; + s->ac_high = 0xffffu; + s->ac_value = get_bits(gb, 16); + + s->freq_range = s->freqs[256]; + if (!s->freq_range) + return AVERROR_INVALIDDATA; + return 0; +} + +static uint16_t ac_get_prob(WavArcContext *s) +{ + return ((s->freq_range - 1) + (s->ac_value - s->ac_low) * (unsigned)s->freq_range) / + ((s->ac_high - s->ac_low) + 1U); +} + +static uint8_t ac_map_symbol(WavArcContext *s, uint16_t prob) +{ + int idx = 255; + + while (prob < s->freqs[idx]) + idx--; + + s->range_high = s->freqs[idx + 1]; + s->range_low = s->freqs[idx]; + + return idx; +} + +static int ac_normalize(AVCodecContext *avctx, WavArcContext *s, GetBitContext *gb) +{ + int range; + + if (s->ac_high < s->ac_low) + goto fail; + + range = (s->ac_high - s->ac_low) + 1; + s->ac_high = (range * (unsigned)s->range_high) / s->freq_range + s->ac_low - 1; + s->ac_low += (range * (unsigned)s->range_low) / s->freq_range; + + if (s->ac_high < s->ac_low) + goto fail; + + for (;;) { + if ((s->ac_high & 0x8000) != (s->ac_low & 0x8000)) { + if (((s->ac_low & 0x4000) == 0) || ((s->ac_high & 0x4000) != 0)) + return 0; + s->ac_value ^= 0x4000; + s->ac_low &= 0x3fff; + s->ac_high |= 0x4000; + } + + s->ac_low = s->ac_low * 2; + s->ac_high = s->ac_high * 2 | 1; + if (s->ac_high < s->ac_low) + goto fail; + + if (get_bits_left(gb) <= 0) { + av_log(avctx, AV_LOG_ERROR, "overread in arithmetic coder\n"); + goto fail; + } + + s->ac_value = s->ac_value * 2 + get_bits1(gb); + if (s->ac_low > s->ac_value || s->ac_high < s->ac_value) + goto fail; + } + +fail: + av_log(avctx, AV_LOG_ERROR, "invalid state\n"); + return AVERROR_INVALIDDATA; +} + +static void ac_init_model(WavArcContext *s) +{ + memset(s->freqs, 0, sizeof(s->freqs)); + + for (int n = 0; n < 256; n++) + s->freqs[n+1] = s->model[n] + s->freqs[n]; +} + +static int ac_read_model(AVCodecContext *avctx, + WavArcContext *s, + GetBitContext *gb) +{ + unsigned start, end; + + memset(s->model, 0, sizeof(s->model)); + + start = get_bits(gb, 8); + end = get_bits(gb, 8); + + for (;;) { + while (start <= end) { + if (get_bits_left(gb) < 8) + return AVERROR_INVALIDDATA; + s->model[start++] = get_bits(gb, 8); + } + + if (get_bits_left(gb) < 8) + return AVERROR_INVALIDDATA; + + start = get_bits(gb, 8); + if (!start) + break; + + end = get_bits(gb, 8); + } + + ac_init_model(s); + + return 0; +} + +static int decode_5elp(AVCodecContext *avctx, + WavArcContext *s, GetBitContext *gb) +{ + int ch, finished, fill, correlated, order = 0; + + ch = 0; + finished = 0; + while (!finished) { + int *samples = s->samples[ch]; + int *ac_pred = s->ac_pred; + int *ac_out = s->ac_out; + int k, block_type; + + if (get_bits_left(gb) <= 0) + return AVERROR_INVALIDDATA; + + memset(s->ac_out, 0, sizeof(s->ac_out)); + + block_type = get_urice(gb, 1); + av_log(avctx, AV_LOG_DEBUG, "block_type : %d\n", block_type); + + if (block_type >= 0 && block_type <= 7) { + k = 1 + (avctx->sample_fmt == AV_SAMPLE_FMT_S16P); + k = get_urice(gb, k) + 1; + if (k >= 32) + return AVERROR_INVALIDDATA; + } + + if (block_type <= 2 || block_type == 6 || block_type == 13 || + block_type == 14 || block_type == 15 || block_type == 19) { + order = get_urice(gb, 2); + if ((unsigned)order > FF_ARRAY_ELEMS(s->filter[ch])) + return AVERROR_INVALIDDATA; + for (int o = 0; o < order; o++) + s->filter[ch][o] = get_srice(gb, 2); + } + + if (block_type >= 0 && block_type <= 7) { + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = get_srice(gb, k); + } else { + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = 0; + } + + if (block_type >= 13 && block_type <= 20) { + const int ac_size = get_bits(gb, 12); + const int ac_pos = get_bits_count(gb); + GetBitContext ac_gb = *gb; + int ret; + + skip_bits_long(gb, ac_size); + ret = ac_read_model(avctx, s, &ac_gb); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "bad arithmetic model\n"); + return ret; + } + + ret = ac_init(avctx, s, &ac_gb); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "cannot init arithmetic decoder\n"); + return ret; + } + + for (int n = 0; n < s->nb_samples; n++) { + uint16_t prob = ac_get_prob(s); + int ac = ac_map_symbol(s, prob); + ac_out[n] = ac - 0x80; + if ((ret = ac_normalize(avctx, s, &ac_gb)) < 0) + return ret; + } + + if (get_bits_count(&ac_gb) != ac_pos + ac_size) { + av_log(avctx, AV_LOG_DEBUG, "over/under-read in arithmetic coder: %d\n", + ac_pos + ac_size - get_bits_count(&ac_gb)); + } + } + + switch (block_type) { + case 12: + s->eof = 1; + return AVERROR_EOF; + case 11: + s->nb_samples = get_urice(gb, 8); + if (s->nb_samples > 570U) { + s->nb_samples = 570; + return AVERROR_INVALIDDATA; + } + continue; + case 10: + s->shift = get_urice(gb, 2); + if ((unsigned)s->shift > 31) { + s->shift = 0; + return AVERROR_INVALIDDATA; + } + continue; + case 9: + if (avctx->sample_fmt == AV_SAMPLE_FMT_U8P) { + fill = (int8_t)get_bits(gb, 8); + fill -= 0x80; + } else { + fill = (int16_t)get_bits(gb, 16); + fill -= 0x8000; + } + + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = fill; + finished = 1; + break; + case 8: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] = 0; + finished = 1; + break; + case 20: + case 7: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] += ac_out[n] + samples[n + 69] * 3U - samples[n + 68] * 3U + samples[n + 67]; + finished = 1; + break; + case 19: + case 6: + for (int n = 0; n < 70; n++) { + ac_pred[n] = samples[n]; + samples[n] = 0; + } + + for (int n = 0; n < s->nb_samples; n++) { + int sum = 15; + + for (int o = 0; o < order; o++) + sum += s->filter[ch][o] * (unsigned)samples[n + 70 - o - 1]; + + samples[n + 70] += ac_out[n] + (unsigned)(sum >> 4); + } + + for (int n = 0; n < 70; n++) + samples[n] = ac_pred[n]; + + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] += ac_out[n] + samples[n + 69] * 3U - samples[n + 68] * 3U + samples[n + 67]; + + finished = 1; + break; + case 18: + case 5: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] += ac_out[n] + samples[n + 69] * 2U - samples[n + 68]; + finished = 1; + break; + case 17: + case 4: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] += ac_out[n]; + finished = 1; + break; + case 16: + case 3: + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] += ac_out[n] + (unsigned)samples[n + 69]; + finished = 1; + break; + case 15: + case 2: + for (int n = 0; n < 70; n++) { + ac_pred[n] = samples[n]; + samples[n] = 0; + } + + for (int n = 0; n < s->nb_samples; n++) { + int sum = 15; + + for (int o = 0; o < order; o++) + sum += s->filter[ch][o] * (unsigned)samples[n + 70 - o - 1]; + + samples[n + 70] += ac_out[n] + (unsigned)(sum >> 4); + } + + for (int n = 0; n < 70; n++) + samples[n] = ac_pred[n]; + + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] += samples[n + 69] * 2U - samples[n + 68]; + + finished = 1; + break; + case 14: + case 1: + for (int n = 0; n < 70; n++) { + ac_pred[n] = samples[n]; + samples[n] = 0; + } + + for (int n = 0; n < s->nb_samples; n++) { + int sum = 15; + + for (int o = 0; o < order; o++) + sum += s->filter[ch][o] * (unsigned)samples[n + 70 - o - 1]; + + samples[n + 70] += (unsigned)ac_out[n] + (sum >> 4); + } + + for (int n = 0; n < 70; n++) + samples[n] = ac_pred[n]; + + for (int n = 0; n < s->nb_samples; n++) + samples[n + 70] += (unsigned)samples[n + 69]; + + finished = 1; + break; + case 13: + case 0: + for (int n = 0; n < s->nb_samples; n++) { + int sum = 15; + + for (int o = 0; o < order; o++) + sum += s->filter[ch][o] * (unsigned)samples[n + 70 - o - 1]; + + samples[n + 70] += (unsigned)ac_out[n] + (sum >> 4); } finished = 1; break; @@ -432,6 +804,9 @@ static int wavarc_decode(AVCodecContext *avctx, AVFrame *frame, case MKTAG('4','A','L','P'): ret = decode_2slp(avctx, s, gb); break; + case MKTAG('5','E','L','P'): + ret = decode_5elp(avctx, s, gb); + break; default: ret = AVERROR_INVALIDDATA; } @@ -507,7 +882,9 @@ const FFCodec ff_wavarc_decoder = { FF_CODEC_DECODE_CB(wavarc_decode), .close = wavarc_close, .p.capabilities = AV_CODEC_CAP_DR1 | +#if FF_API_SUBFRAMES AV_CODEC_CAP_SUBFRAMES | +#endif AV_CODEC_CAP_DELAY, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_S16P, diff --git a/libavcodec/wavpack.c b/libavcodec/wavpack.c index 4346304f54d..505bd3c96a8 100644 --- a/libavcodec/wavpack.c +++ b/libavcodec/wavpack.c @@ -28,6 +28,7 @@ #include "bytestream.h" #include "codec_internal.h" #include "get_bits.h" +#include "refstruct.h" #include "thread.h" #include "threadframe.h" #include "unary.h" @@ -96,12 +97,10 @@ typedef struct WavpackFrameContext { uint8_t *value_lookup[MAX_HISTORY_BINS]; } WavpackFrameContext; -#define WV_MAX_FRAME_DECODERS 14 - typedef struct WavpackContext { AVCodecContext *avctx; - WavpackFrameContext *fdec[WV_MAX_FRAME_DECODERS]; + WavpackFrameContext **fdec; int fdec_num; int block; @@ -112,8 +111,7 @@ typedef struct WavpackContext { ThreadFrame curr_frame, prev_frame; Modulation modulation; - AVBufferRef *dsd_ref; - DSDContext *dsdctx; + DSDContext *dsdctx; ///< RefStruct reference int dsd_channels; } WavpackContext; @@ -796,71 +794,73 @@ static inline int wv_unpack_stereo(WavpackFrameContext *s, GetBitContext *gb, if (last) break; for (i = 0; i < s->terms; i++) { - t = s->decorr[i].value; + Decorr *decorr = &s->decorr[i]; + + t = decorr->value; if (t > 0) { if (t > 8) { if (t & 1) { - A = 2U * s->decorr[i].samplesA[0] - s->decorr[i].samplesA[1]; - B = 2U * s->decorr[i].samplesB[0] - s->decorr[i].samplesB[1]; + A = 2U * decorr->samplesA[0] - decorr->samplesA[1]; + B = 2U * decorr->samplesB[0] - decorr->samplesB[1]; } else { - A = (int)(3U * s->decorr[i].samplesA[0] - s->decorr[i].samplesA[1]) >> 1; - B = (int)(3U * s->decorr[i].samplesB[0] - s->decorr[i].samplesB[1]) >> 1; + A = (int)(3U * decorr->samplesA[0] - decorr->samplesA[1]) >> 1; + B = (int)(3U * decorr->samplesB[0] - decorr->samplesB[1]) >> 1; } - s->decorr[i].samplesA[1] = s->decorr[i].samplesA[0]; - s->decorr[i].samplesB[1] = s->decorr[i].samplesB[0]; - j = 0; + decorr->samplesA[1] = decorr->samplesA[0]; + decorr->samplesB[1] = decorr->samplesB[0]; + j = 0; } else { - A = s->decorr[i].samplesA[pos]; - B = s->decorr[i].samplesB[pos]; + A = decorr->samplesA[pos]; + B = decorr->samplesB[pos]; j = (pos + t) & 7; } if (type != AV_SAMPLE_FMT_S16P) { - L2 = L + ((s->decorr[i].weightA * (int64_t)A + 512) >> 10); - R2 = R + ((s->decorr[i].weightB * (int64_t)B + 512) >> 10); + L2 = L + ((decorr->weightA * (int64_t)A + 512) >> 10); + R2 = R + ((decorr->weightB * (int64_t)B + 512) >> 10); } else { - L2 = L + (unsigned)((int)(s->decorr[i].weightA * (unsigned)A + 512) >> 10); - R2 = R + (unsigned)((int)(s->decorr[i].weightB * (unsigned)B + 512) >> 10); + L2 = L + (unsigned)((int)(decorr->weightA * (unsigned)A + 512) >> 10); + R2 = R + (unsigned)((int)(decorr->weightB * (unsigned)B + 512) >> 10); } if (A && L) - s->decorr[i].weightA -= ((((L ^ A) >> 30) & 2) - 1) * s->decorr[i].delta; + decorr->weightA -= ((((L ^ A) >> 30) & 2) - 1) * decorr->delta; if (B && R) - s->decorr[i].weightB -= ((((R ^ B) >> 30) & 2) - 1) * s->decorr[i].delta; - s->decorr[i].samplesA[j] = L = L2; - s->decorr[i].samplesB[j] = R = R2; + decorr->weightB -= ((((R ^ B) >> 30) & 2) - 1) * decorr->delta; + decorr->samplesA[j] = L = L2; + decorr->samplesB[j] = R = R2; } else if (t == -1) { if (type != AV_SAMPLE_FMT_S16P) - L2 = L + ((s->decorr[i].weightA * (int64_t)s->decorr[i].samplesA[0] + 512) >> 10); + L2 = L + ((decorr->weightA * (int64_t)decorr->samplesA[0] + 512) >> 10); else - L2 = L + (unsigned)((int)(s->decorr[i].weightA * (unsigned)s->decorr[i].samplesA[0] + 512) >> 10); - UPDATE_WEIGHT_CLIP(s->decorr[i].weightA, s->decorr[i].delta, s->decorr[i].samplesA[0], L); + L2 = L + (unsigned)((int)(decorr->weightA * (unsigned)decorr->samplesA[0] + 512) >> 10); + UPDATE_WEIGHT_CLIP(decorr->weightA, decorr->delta, decorr->samplesA[0], L); L = L2; if (type != AV_SAMPLE_FMT_S16P) - R2 = R + ((s->decorr[i].weightB * (int64_t)L2 + 512) >> 10); + R2 = R + ((decorr->weightB * (int64_t)L2 + 512) >> 10); else - R2 = R + (unsigned)((int)(s->decorr[i].weightB * (unsigned)L2 + 512) >> 10); - UPDATE_WEIGHT_CLIP(s->decorr[i].weightB, s->decorr[i].delta, L2, R); - R = R2; - s->decorr[i].samplesA[0] = R; + R2 = R + (unsigned)((int)(decorr->weightB * (unsigned)L2 + 512) >> 10); + UPDATE_WEIGHT_CLIP(decorr->weightB, decorr->delta, L2, R); + R = R2; + decorr->samplesA[0] = R; } else { if (type != AV_SAMPLE_FMT_S16P) - R2 = R + ((s->decorr[i].weightB * (int64_t)s->decorr[i].samplesB[0] + 512) >> 10); + R2 = R + ((decorr->weightB * (int64_t)decorr->samplesB[0] + 512) >> 10); else - R2 = R + (unsigned)((int)(s->decorr[i].weightB * (unsigned)s->decorr[i].samplesB[0] + 512) >> 10); - UPDATE_WEIGHT_CLIP(s->decorr[i].weightB, s->decorr[i].delta, s->decorr[i].samplesB[0], R); + R2 = R + (unsigned)((int)(decorr->weightB * (unsigned)decorr->samplesB[0] + 512) >> 10); + UPDATE_WEIGHT_CLIP(decorr->weightB, decorr->delta, decorr->samplesB[0], R); R = R2; if (t == -3) { - R2 = s->decorr[i].samplesA[0]; - s->decorr[i].samplesA[0] = R; + R2 = decorr->samplesA[0]; + decorr->samplesA[0] = R; } if (type != AV_SAMPLE_FMT_S16P) - L2 = L + ((s->decorr[i].weightA * (int64_t)R2 + 512) >> 10); + L2 = L + ((decorr->weightA * (int64_t)R2 + 512) >> 10); else - L2 = L + (unsigned)((int)(s->decorr[i].weightA * (unsigned)R2 + 512) >> 10); - UPDATE_WEIGHT_CLIP(s->decorr[i].weightA, s->decorr[i].delta, R2, L); - L = L2; - s->decorr[i].samplesB[0] = L; + L2 = L + (unsigned)((int)(decorr->weightA * (unsigned)R2 + 512) >> 10); + UPDATE_WEIGHT_CLIP(decorr->weightA, decorr->delta, R2, L); + L = L2; + decorr->samplesB[0] = L; } } @@ -922,25 +922,27 @@ static inline int wv_unpack_mono(WavpackFrameContext *s, GetBitContext *gb, if (last) break; for (i = 0; i < s->terms; i++) { - t = s->decorr[i].value; + Decorr *decorr = &s->decorr[i]; + + t = decorr->value; if (t > 8) { if (t & 1) - A = 2U * s->decorr[i].samplesA[0] - s->decorr[i].samplesA[1]; + A = 2U * decorr->samplesA[0] - decorr->samplesA[1]; else - A = (int)(3U * s->decorr[i].samplesA[0] - s->decorr[i].samplesA[1]) >> 1; - s->decorr[i].samplesA[1] = s->decorr[i].samplesA[0]; - j = 0; + A = (int)(3U * decorr->samplesA[0] - decorr->samplesA[1]) >> 1; + decorr->samplesA[1] = decorr->samplesA[0]; + j = 0; } else { - A = s->decorr[i].samplesA[pos]; + A = decorr->samplesA[pos]; j = (pos + t) & 7; } if (type != AV_SAMPLE_FMT_S16P) - S = T + ((s->decorr[i].weightA * (int64_t)A + 512) >> 10); + S = T + ((decorr->weightA * (int64_t)A + 512) >> 10); else - S = T + (unsigned)((int)(s->decorr[i].weightA * (unsigned)A + 512) >> 10); + S = T + (unsigned)((int)(decorr->weightA * (unsigned)A + 512) >> 10); if (A && T) - s->decorr[i].weightA -= ((((T ^ A) >> 30) & 2) - 1) * s->decorr[i].delta; - s->decorr[i].samplesA[j] = T = S; + decorr->weightA -= ((((T ^ A) >> 30) & 2) - 1) * decorr->delta; + decorr->samplesA[j] = T = S; } pos = (pos + 1) & 7; crc = crc * 3 + S; @@ -971,8 +973,11 @@ static inline int wv_unpack_mono(WavpackFrameContext *s, GetBitContext *gb, static av_cold int wv_alloc_frame_context(WavpackContext *c) { - if (c->fdec_num == WV_MAX_FRAME_DECODERS) + WavpackFrameContext **fdec = av_realloc_array(c->fdec, c->fdec_num + 1, sizeof(*c->fdec)); + + if (!fdec) return -1; + c->fdec = fdec; c->fdec[c->fdec_num] = av_mallocz(sizeof(**c->fdec)); if (!c->fdec[c->fdec_num]) @@ -987,9 +992,8 @@ static int wv_dsd_reset(WavpackContext *s, int channels) { int i; - s->dsdctx = NULL; s->dsd_channels = 0; - av_buffer_unref(&s->dsd_ref); + ff_refstruct_unref(&s->dsdctx); if (!channels) return 0; @@ -997,10 +1001,9 @@ static int wv_dsd_reset(WavpackContext *s, int channels) if (channels > INT_MAX / sizeof(*s->dsdctx)) return AVERROR(EINVAL); - s->dsd_ref = av_buffer_allocz(channels * sizeof(*s->dsdctx)); - if (!s->dsd_ref) + s->dsdctx = ff_refstruct_allocz(channels * sizeof(*s->dsdctx)); + if (!s->dsdctx) return AVERROR(ENOMEM); - s->dsdctx = (DSDContext*)s->dsd_ref->data; s->dsd_channels = channels; for (i = 0; i < channels; i++) @@ -1019,21 +1022,14 @@ static int update_thread_context(AVCodecContext *dst, const AVCodecContext *src) if (dst == src) return 0; - ff_thread_release_ext_buffer(dst, &fdst->curr_frame); + ff_thread_release_ext_buffer(&fdst->curr_frame); if (fsrc->curr_frame.f->data[0]) { if ((ret = ff_thread_ref_frame(&fdst->curr_frame, &fsrc->curr_frame)) < 0) return ret; } - fdst->dsdctx = NULL; - fdst->dsd_channels = 0; - ret = av_buffer_replace(&fdst->dsd_ref, fsrc->dsd_ref); - if (ret < 0) - return ret; - if (fsrc->dsd_ref) { - fdst->dsdctx = (DSDContext*)fdst->dsd_ref->data; - fdst->dsd_channels = fsrc->dsd_channels; - } + ff_refstruct_replace(&fdst->dsdctx, fsrc->dsdctx); + fdst->dsd_channels = fsrc->dsd_channels; return 0; } @@ -1064,15 +1060,16 @@ static av_cold int wavpack_decode_end(AVCodecContext *avctx) for (int i = 0; i < s->fdec_num; i++) av_freep(&s->fdec[i]); + av_freep(&s->fdec); s->fdec_num = 0; - ff_thread_release_ext_buffer(avctx, &s->curr_frame); + ff_thread_release_ext_buffer(&s->curr_frame); av_frame_free(&s->curr_frame.f); - ff_thread_release_ext_buffer(avctx, &s->prev_frame); + ff_thread_release_ext_buffer(&s->prev_frame); av_frame_free(&s->prev_frame.f); - av_buffer_unref(&s->dsd_ref); + ff_refstruct_unref(&s->dsdctx); return 0; } @@ -1100,11 +1097,6 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no, } s = wc->fdec[block_no]; - if (!s) { - av_log(avctx, AV_LOG_ERROR, "Context for block %d is not present\n", - block_no); - return AVERROR_INVALIDDATA; - } memset(s->decorr, 0, MAX_TERMS * sizeof(Decorr)); memset(s->ch, 0, sizeof(s->ch)); @@ -1127,7 +1119,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no, else if ((s->frame_flags & 0x03) <= 1) sample_fmt = AV_SAMPLE_FMT_S16P; else - sample_fmt = AV_SAMPLE_FMT_S32P; + sample_fmt = AV_SAMPLE_FMT_S32P; if (wc->ch_offset && avctx->sample_fmt != sample_fmt) return AVERROR_INVALIDDATA; @@ -1219,36 +1211,38 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no, } t = 0; for (i = s->terms - 1; (i >= 0) && (t < size); i--) { - if (s->decorr[i].value > 8) { - s->decorr[i].samplesA[0] = + Decorr *decorr = &s->decorr[i]; + + if (decorr->value > 8) { + decorr->samplesA[0] = wp_exp2(bytestream2_get_le16(&gb)); - s->decorr[i].samplesA[1] = + decorr->samplesA[1] = wp_exp2(bytestream2_get_le16(&gb)); if (s->stereo_in) { - s->decorr[i].samplesB[0] = + decorr->samplesB[0] = wp_exp2(bytestream2_get_le16(&gb)); - s->decorr[i].samplesB[1] = + decorr->samplesB[1] = wp_exp2(bytestream2_get_le16(&gb)); - t += 4; + t += 4; } t += 4; - } else if (s->decorr[i].value < 0) { - s->decorr[i].samplesA[0] = + } else if (decorr->value < 0) { + decorr->samplesA[0] = wp_exp2(bytestream2_get_le16(&gb)); - s->decorr[i].samplesB[0] = + decorr->samplesB[0] = wp_exp2(bytestream2_get_le16(&gb)); - t += 4; + t += 4; } else { - for (j = 0; j < s->decorr[i].value; j++) { - s->decorr[i].samplesA[j] = + for (j = 0; j < decorr->value; j++) { + decorr->samplesA[j] = wp_exp2(bytestream2_get_le16(&gb)); if (s->stereo_in) { - s->decorr[i].samplesB[j] = + decorr->samplesB[j] = wp_exp2(bytestream2_get_le16(&gb)); } } - t += s->decorr[i].value * 2 * (s->stereo_in + 1); + t += decorr->value * 2 * (s->stereo_in + 1); } } got_samples = 1; @@ -1415,18 +1409,12 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no, size = bytestream2_get_byte(&gb); chan |= (bytestream2_get_byte(&gb) & 0xF) << 8; chan += 1; - if (avctx->ch_layout.nb_channels != chan) - av_log(avctx, AV_LOG_WARNING, "%i channels signalled" - " instead of %i.\n", chan, avctx->ch_layout.nb_channels); chmask = bytestream2_get_le24(&gb); break; case 5: size = bytestream2_get_byte(&gb); chan |= (bytestream2_get_byte(&gb) & 0xF) << 8; chan += 1; - if (avctx->ch_layout.nb_channels != chan) - av_log(avctx, AV_LOG_WARNING, "%i channels signalled" - " instead of %i.\n", chan, avctx->ch_layout.nb_channels); chmask = bytestream2_get_le32(&gb); break; default: @@ -1519,11 +1507,7 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no, return AVERROR_INVALIDDATA; } } else { - ret = av_channel_layout_copy(&new_ch_layout, &avctx->ch_layout); - if (ret < 0) { - av_log(avctx, AV_LOG_ERROR, "Error copying channel layout\n"); - return ret; - } + av_channel_layout_default(&new_ch_layout, chan); } } else { av_channel_layout_default(&new_ch_layout, s->stereo + 1); @@ -1539,14 +1523,14 @@ static int wavpack_decode_block(AVCodecContext *avctx, int block_no, av_log(avctx, AV_LOG_ERROR, "Error reinitializing the DSD context\n"); return ret; } - ff_thread_release_ext_buffer(avctx, &wc->curr_frame); + ff_thread_release_ext_buffer(&wc->curr_frame); } av_channel_layout_copy(&avctx->ch_layout, &new_ch_layout); avctx->sample_rate = new_samplerate; avctx->sample_fmt = sample_fmt; avctx->bits_per_raw_sample = orig_bpp; - ff_thread_release_ext_buffer(avctx, &wc->prev_frame); + ff_thread_release_ext_buffer(&wc->prev_frame); FFSWAP(ThreadFrame, wc->curr_frame, wc->prev_frame); /* get output buffer */ @@ -1677,7 +1661,7 @@ static int wavpack_decode_frame(AVCodecContext *avctx, AVFrame *rframe, } ff_thread_await_progress(&s->prev_frame, INT_MAX, 0); - ff_thread_release_ext_buffer(avctx, &s->prev_frame); + ff_thread_release_ext_buffer(&s->prev_frame); if (s->modulation == MODULATION_DSD) avctx->execute2(avctx, dsd_channel, s->frame, NULL, avctx->ch_layout.nb_channels); @@ -1694,7 +1678,7 @@ static int wavpack_decode_frame(AVCodecContext *avctx, AVFrame *rframe, error: if (s->frame) { ff_thread_await_progress(&s->prev_frame, INT_MAX, 0); - ff_thread_release_ext_buffer(avctx, &s->prev_frame); + ff_thread_release_ext_buffer(&s->prev_frame); ff_thread_report_progress(&s->curr_frame, INT_MAX, 0); } diff --git a/libavcodec/wavpackenc.c b/libavcodec/wavpackenc.c index 3d2d45360de..923eae55fc9 100644 --- a/libavcodec/wavpackenc.c +++ b/libavcodec/wavpackenc.c @@ -1978,7 +1978,7 @@ static void encode_flush(WavPackEncodeContext *s) put_bits(pb, 31, 0x7FFFFFFF); cbits -= 31; } else { - put_bits(pb, cbits, (1 << cbits) - 1); + put_bits(pb, cbits, (1U << cbits) - 1); cbits = 0; } } while (cbits); @@ -2007,7 +2007,7 @@ static void encode_flush(WavPackEncodeContext *s) put_bits(pb, 31, 0x7FFFFFFF); cbits -= 31; } else { - put_bits(pb, cbits, (1 << cbits) - 1); + put_bits(pb, cbits, (1U << cbits) - 1); cbits = 0; } } while (cbits); @@ -2592,7 +2592,16 @@ static int wavpack_encode_block(WavPackEncodeContext *s, s->avctx->ch_layout.u.mask != AV_CH_LAYOUT_STEREO) { put_metadata_block(&pb, WP_ID_CHANINFO, 5); bytestream2_put_byte(&pb, s->avctx->ch_layout.nb_channels); - bytestream2_put_le32(&pb, s->avctx->ch_layout.u.mask); + if (s->avctx->ch_layout.u.mask >> 32) + bytestream2_put_le32(&pb, 0); + else + bytestream2_put_le32(&pb, s->avctx->ch_layout.u.mask); + bytestream2_put_byte(&pb, 0); + } else if (s->flags & WV_INITIAL_BLOCK && + s->avctx->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) { + put_metadata_block(&pb, WP_ID_CHANINFO, 5); + bytestream2_put_byte(&pb, s->avctx->ch_layout.nb_channels); + bytestream2_put_le32(&pb, 0); bytestream2_put_byte(&pb, 0); } @@ -2824,7 +2833,7 @@ static void fill_buffer(WavPackEncodeContext *s, switch (s->avctx->sample_fmt) { case AV_SAMPLE_FMT_U8P: - COPY_SAMPLES(int8_t, 0x80, 0); + COPY_SAMPLES(uint8_t, 0x80, 0); break; case AV_SAMPLE_FMT_S16P: COPY_SAMPLES(int16_t, 0, 0); diff --git a/libavcodec/wbmpdec.c b/libavcodec/wbmpdec.c index 8b105bc1352..3b5753abcd0 100644 --- a/libavcodec/wbmpdec.c +++ b/libavcodec/wbmpdec.c @@ -74,7 +74,7 @@ static int wbmp_decode_frame(AVCodecContext *avctx, AVFrame *p, else readbits(p->data[0], width, height, p->linesize[0], gb.buffer, gb.buffer_end - gb.buffer); - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/wcmv.c b/libavcodec/wcmv.c index 097ac8b8e9a..b2413ee9c0e 100644 --- a/libavcodec/wcmv.c +++ b/libavcodec/wcmv.c @@ -195,7 +195,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, } } - s->prev_frame->key_frame = intra; + if (intra) + s->prev_frame->flags |= AV_FRAME_FLAG_KEY; + else + s->prev_frame->flags &= ~AV_FRAME_FLAG_KEY; s->prev_frame->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; if ((ret = av_frame_ref(frame, s->prev_frame)) < 0) diff --git a/libavcodec/webp.c b/libavcodec/webp.c index b4357f95d57..9308ea2b69f 100644 --- a/libavcodec/webp.c +++ b/libavcodec/webp.c @@ -233,7 +233,7 @@ static void image_ctx_free(ImageContext *img) if (img->huffman_groups) { for (i = 0; i < img->nb_huffman_groups; i++) { for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; j++) - ff_free_vlc(&img->huffman_groups[i * HUFFMAN_CODES_PER_META_CODE + j].vlc); + ff_vlc_free(&img->huffman_groups[i * HUFFMAN_CODES_PER_META_CODE + j].vlc); } av_free(img->huffman_groups); } @@ -300,9 +300,9 @@ static int huff_reader_build_canonical(HuffReader *r, const uint8_t *code_length return AVERROR_INVALIDDATA; } - ret = init_vlc(&r->vlc, 8, alphabet_size, + ret = vlc_init(&r->vlc, 8, alphabet_size, code_lengths, sizeof(*code_lengths), sizeof(*code_lengths), - codes, sizeof(*codes), sizeof(*codes), INIT_VLC_OUTPUT_LE); + codes, sizeof(*codes), sizeof(*codes), VLC_INIT_OUTPUT_LE); if (ret < 0) { av_free(codes); return ret; @@ -415,7 +415,7 @@ static int read_huffman_code_normal(WebPContext *s, HuffReader *hc, ret = huff_reader_build_canonical(hc, code_lengths, alphabet_size); finish: - ff_free_vlc(&code_len_hc.vlc); + ff_vlc_free(&code_len_hc.vlc); av_free(code_lengths); return ret; } @@ -1186,7 +1186,7 @@ static int vp8_lossless_decode_frame(AVCodecContext *avctx, AVFrame *p, *got_frame = 1; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; ret = data_size; free_and_return: @@ -1500,11 +1500,16 @@ static int webp_decode_frame(AVCodecContext *avctx, AVFrame *p, "VP8X header\n"); s->has_iccp = 1; - sd = av_frame_new_side_data(p, AV_FRAME_DATA_ICC_PROFILE, chunk_size); - if (!sd) - return AVERROR(ENOMEM); - bytestream2_get_buffer(&gb, sd->data, chunk_size); + ret = ff_frame_new_side_data(avctx, p, AV_FRAME_DATA_ICC_PROFILE, chunk_size, &sd); + if (ret < 0) + return ret; + + if (sd) { + bytestream2_get_buffer(&gb, sd->data, chunk_size); + } else { + bytestream2_skip(&gb, chunk_size); + } break; } case MKTAG('A', 'N', 'I', 'M'): diff --git a/libavcodec/webvttenc.c b/libavcodec/webvttenc.c index 4369aacb749..3b66b4f2ddf 100644 --- a/libavcodec/webvttenc.c +++ b/libavcodec/webvttenc.c @@ -22,7 +22,6 @@ #include #include "avcodec.h" -#include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "ass_split.h" #include "ass.h" @@ -39,10 +38,7 @@ typedef struct { int stack_ptr; } WebVTTContext; -#ifdef __GNUC__ -__attribute__ ((__format__ (__printf__, 2, 3))) -#endif -static void webvtt_print(WebVTTContext *s, const char *str, ...) +static av_printf_format(2, 3) void webvtt_print(WebVTTContext *s, const char *str, ...) { va_list vargs; va_start(vargs, str); @@ -162,7 +158,7 @@ static int webvtt_encode_frame(AVCodecContext *avctx, ASSDialog *dialog; int i; - av_bprint_clear(&s->buffer); + av_bprint_init_for_buffer(&s->buffer, buf, bufsize); for (i=0; inum_rects; i++) { const char *ass = sub->rects[i]->ass; @@ -180,16 +176,13 @@ static int webvtt_encode_frame(AVCodecContext *avctx, ff_ass_free_dialog(&dialog); } - if (!av_bprint_is_complete(&s->buffer)) - return AVERROR(ENOMEM); if (!s->buffer.len) return 0; - if (s->buffer.len > bufsize) { + if (!av_bprint_is_complete(&s->buffer)) { av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n"); return AVERROR_BUFFER_TOO_SMALL; } - memcpy(buf, s->buffer.str, s->buffer.len); return s->buffer.len; } @@ -198,7 +191,6 @@ static int webvtt_encode_close(AVCodecContext *avctx) { WebVTTContext *s = avctx->priv_data; ff_ass_split_free(s->ass_ctx); - av_bprint_finalize(&s->buffer, NULL); return 0; } @@ -207,7 +199,6 @@ static av_cold int webvtt_encode_init(AVCodecContext *avctx) WebVTTContext *s = avctx->priv_data; s->avctx = avctx; s->ass_ctx = ff_ass_split(avctx->subtitle_header); - av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED); return s->ass_ctx ? 0 : AVERROR_INVALIDDATA; } diff --git a/libavcodec/wma.c b/libavcodec/wma.c index 41d16e52f87..5eacf230fab 100644 --- a/libavcodec/wma.c +++ b/libavcodec/wma.c @@ -42,7 +42,7 @@ static av_cold int init_coef_vlc(VLC *vlc, uint16_t **prun_table, float *flevel_table; int i, l, j, k, level, ret; - ret = init_vlc(vlc, VLCBITS, n, table_bits, 1, 1, table_codes, 4, 4, 0); + ret = vlc_init(vlc, VLCBITS, n, table_bits, 1, 1, table_codes, 4, 4, 0); if (ret < 0) return ret; @@ -372,11 +372,11 @@ int ff_wma_end(AVCodecContext *avctx) av_tx_uninit(&s->mdct_ctx[i]); if (s->use_exp_vlc) - ff_free_vlc(&s->exp_vlc); + ff_vlc_free(&s->exp_vlc); if (s->use_noise_coding) - ff_free_vlc(&s->hgain_vlc); + ff_vlc_free(&s->hgain_vlc); for (i = 0; i < 2; i++) { - ff_free_vlc(&s->coef_vlc[i]); + ff_vlc_free(&s->coef_vlc[i]); av_freep(&s->run_table[i]); av_freep(&s->level_table[i]); av_freep(&s->int_table[i]); @@ -424,7 +424,7 @@ unsigned int ff_wma_get_large_val(GetBitContext *gb) * @return 0 on success, -1 otherwise */ int ff_wma_run_level_decode(AVCodecContext *avctx, GetBitContext *gb, - VLC *vlc, const float *level_table, + const VLCElem *vlc, const float *level_table, const uint16_t *run_table, int version, WMACoef *ptr, int offset, int num_coefs, int block_len, int frame_len_bits, @@ -435,7 +435,7 @@ int ff_wma_run_level_decode(AVCodecContext *avctx, GetBitContext *gb, uint32_t *iptr = (uint32_t *) ptr; const unsigned int coef_mask = block_len - 1; for (; offset < num_coefs; offset++) { - code = get_vlc2(gb, vlc->table, VLCBITS, VLCMAX); + code = get_vlc2(gb, vlc, VLCBITS, VLCMAX); if (code > 1) { /** normal code */ offset += run_table[code]; diff --git a/libavcodec/wma.h b/libavcodec/wma.h index 5dc604154d9..3d0d872ea33 100644 --- a/libavcodec/wma.h +++ b/libavcodec/wma.h @@ -155,7 +155,7 @@ int ff_wma_total_gain_to_bits(int total_gain); int ff_wma_end(AVCodecContext *avctx); unsigned int ff_wma_get_large_val(GetBitContext *gb); int ff_wma_run_level_decode(AVCodecContext *avctx, GetBitContext *gb, - VLC *vlc, const float *level_table, + const VLCElem *vlc, const float *level_table, const uint16_t *run_table, int version, WMACoef *ptr, int offset, int num_coefs, int block_len, int frame_len_bits, diff --git a/libavcodec/wmadec.c b/libavcodec/wmadec.c index bc18d18222e..3427e482dce 100644 --- a/libavcodec/wmadec.c +++ b/libavcodec/wmadec.c @@ -119,7 +119,7 @@ static av_cold int wma_decode_init(AVCodecContext *avctx) } if (s->use_noise_coding) { - ret = ff_init_vlc_from_lengths(&s->hgain_vlc, HGAINVLCBITS, + ret = ff_vlc_init_from_lengths(&s->hgain_vlc, HGAINVLCBITS, FF_ARRAY_ELEMS(ff_wma_hgain_hufftab), &ff_wma_hgain_hufftab[0][1], 2, &ff_wma_hgain_hufftab[0][0], 2, 1, @@ -130,7 +130,7 @@ static av_cold int wma_decode_init(AVCodecContext *avctx) if (s->use_exp_vlc) { // FIXME move out of context - ret = init_vlc(&s->exp_vlc, EXPVLCBITS, sizeof(ff_aac_scalefactor_bits), + ret = vlc_init(&s->exp_vlc, EXPVLCBITS, sizeof(ff_aac_scalefactor_bits), ff_aac_scalefactor_bits, 1, 1, ff_aac_scalefactor_code, 4, 4, 0); if (ret < 0) @@ -616,7 +616,7 @@ static int wma_decode_block(WMACodecContext *s) * there is potentially less energy there */ tindex = (ch == 1 && s->ms_stereo); memset(ptr, 0, s->block_len * sizeof(WMACoef)); - ret = ff_wma_run_level_decode(s->avctx, &s->gb, &s->coef_vlc[tindex], + ret = ff_wma_run_level_decode(s->avctx, &s->gb, s->coef_vlc[tindex].table, s->level_table[tindex], s->run_table[tindex], 0, ptr, 0, nb_coefs[ch], s->block_len, s->frame_len_bits, coef_nb_bits); diff --git a/libavcodec/wmalosslessdec.c b/libavcodec/wmalosslessdec.c index d545d848e2a..5d1c7ac66bb 100644 --- a/libavcodec/wmalosslessdec.c +++ b/libavcodec/wmalosslessdec.c @@ -1334,7 +1334,11 @@ const FFCodec ff_wmalossless_decoder = { .close = decode_close, FF_CODEC_DECODE_CB(decode_packet), .flush = flush, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P, diff --git a/libavcodec/wmaprodec.c b/libavcodec/wmaprodec.c index 35e9caec560..65b269adda9 100644 --- a/libavcodec/wmaprodec.c +++ b/libavcodec/wmaprodec.c @@ -132,12 +132,12 @@ #define SCALEMAXDEPTH ((HUFF_SCALE_MAXBITS+SCALEVLCBITS-1)/SCALEVLCBITS) #define SCALERLMAXDEPTH ((HUFF_SCALE_RL_MAXBITS+VLCBITS-1)/VLCBITS) -static VLC sf_vlc; ///< scale factor DPCM vlc -static VLC sf_rl_vlc; ///< scale factor run length vlc -static VLC vec4_vlc; ///< 4 coefficients per symbol -static VLC vec2_vlc; ///< 2 coefficients per symbol -static VLC vec1_vlc; ///< 1 coefficient per symbol -static VLC coef_vlc[2]; ///< coefficient run length vlc codes +static VLCElem sf_vlc[616]; ///< scale factor DPCM vlc +static VLCElem sf_rl_vlc[1406]; ///< scale factor run length vlc +static VLCElem vec4_vlc[604]; ///< 4 coefficients per symbol +static VLCElem vec2_vlc[562]; ///< 2 coefficients per symbol +static VLCElem vec1_vlc[562]; ///< 1 coefficient per symbol +static const VLCElem *coef_vlc[2]; ///< coefficient run length vlc codes static float sin64[33]; ///< sine table for decorrelation /** @@ -320,27 +320,32 @@ static av_cold int get_rate(AVCodecContext *avctx) static av_cold void decode_init_static(void) { - INIT_VLC_STATIC_FROM_LENGTHS(&sf_vlc, SCALEVLCBITS, HUFF_SCALE_SIZE, - &scale_table[0][1], 2, - &scale_table[0][0], 2, 1, -60, 0, 616); - INIT_VLC_STATIC_FROM_LENGTHS(&sf_rl_vlc, VLCBITS, HUFF_SCALE_RL_SIZE, - &scale_rl_table[0][1], 2, - &scale_rl_table[0][0], 2, 1, 0, 0, 1406); - INIT_VLC_STATIC_FROM_LENGTHS(&coef_vlc[0], VLCBITS, HUFF_COEF0_SIZE, - coef0_lens, 1, - coef0_syms, 2, 2, 0, 0, 2108); - INIT_VLC_STATIC_FROM_LENGTHS(&coef_vlc[1], VLCBITS, HUFF_COEF1_SIZE, + static VLCElem vlc_buf[2108 + 3912]; + VLCInitState state = VLC_INIT_STATE(vlc_buf); + + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(sf_vlc, SCALEVLCBITS, HUFF_SCALE_SIZE, + &scale_table[0][1], 2, + &scale_table[0][0], 2, 1, -60, 0); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(sf_rl_vlc, VLCBITS, HUFF_SCALE_RL_SIZE, + &scale_rl_table[0][1], 2, + &scale_rl_table[0][0], 2, 1, 0, 0); + coef_vlc[0] = + ff_vlc_init_tables_from_lengths(&state, VLCBITS, HUFF_COEF0_SIZE, + coef0_lens, 1, + coef0_syms, 2, 2, 0, 0); + coef_vlc[1] = + ff_vlc_init_tables_from_lengths(&state, VLCBITS, HUFF_COEF1_SIZE, &coef1_table[0][1], 2, - &coef1_table[0][0], 2, 1, 0, 0, 3912); - INIT_VLC_STATIC_FROM_LENGTHS(&vec4_vlc, VLCBITS, HUFF_VEC4_SIZE, - vec4_lens, 1, - vec4_syms, 2, 2, -1, 0, 604); - INIT_VLC_STATIC_FROM_LENGTHS(&vec2_vlc, VLCBITS, HUFF_VEC2_SIZE, - &vec2_table[0][1], 2, - &vec2_table[0][0], 2, 1, -1, 0, 562); - INIT_VLC_STATIC_FROM_LENGTHS(&vec1_vlc, VLCBITS, HUFF_VEC1_SIZE, - &vec1_table[0][1], 2, - &vec1_table[0][0], 2, 1, 0, 0, 562); + &coef1_table[0][0], 2, 1, 0, 0); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(vec4_vlc, VLCBITS, HUFF_VEC4_SIZE, + vec4_lens, 1, + vec4_syms, 2, 2, -1, 0); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(vec2_vlc, VLCBITS, HUFF_VEC2_SIZE, + &vec2_table[0][1], 2, + &vec2_table[0][0], 2, 1, -1, 0); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(vec1_vlc, VLCBITS, HUFF_VEC1_SIZE, + &vec1_table[0][1], 2, + &vec1_table[0][0], 2, 1, 0, 0); /** calculate sine values for the decorrelation matrix */ for (int i = 0; i < 33; i++) @@ -929,7 +934,7 @@ static int decode_coeffs(WMAProDecodeCtx *s, int c) 0x41400000, 0x41500000, 0x41600000, 0x41700000, }; int vlctable; - VLC* vlc; + const VLCElem *vlc; WMAProChannelCtx* ci = &s->channel[c]; int rl_mode = 0; int cur_coeff = 0; @@ -940,7 +945,7 @@ static int decode_coeffs(WMAProDecodeCtx *s, int c) ff_dlog(s->avctx, "decode coefficients for channel %i\n", c); vlctable = get_bits1(&s->gb); - vlc = &coef_vlc[vlctable]; + vlc = coef_vlc[vlctable]; if (vlctable) { run = coef1_run; @@ -958,17 +963,17 @@ static int decode_coeffs(WMAProDecodeCtx *s, int c) int i; unsigned int idx; - idx = get_vlc2(&s->gb, vec4_vlc.table, VLCBITS, VEC4MAXDEPTH); + idx = get_vlc2(&s->gb, vec4_vlc, VLCBITS, VEC4MAXDEPTH); if ((int)idx < 0) { for (i = 0; i < 4; i += 2) { - idx = get_vlc2(&s->gb, vec2_vlc.table, VLCBITS, VEC2MAXDEPTH); + idx = get_vlc2(&s->gb, vec2_vlc, VLCBITS, VEC2MAXDEPTH); if ((int)idx < 0) { uint32_t v0, v1; - v0 = get_vlc2(&s->gb, vec1_vlc.table, VLCBITS, VEC1MAXDEPTH); + v0 = get_vlc2(&s->gb, vec1_vlc, VLCBITS, VEC1MAXDEPTH); if (v0 == HUFF_VEC1_SIZE - 1) v0 += ff_wma_get_large_val(&s->gb); - v1 = get_vlc2(&s->gb, vec1_vlc.table, VLCBITS, VEC1MAXDEPTH); + v1 = get_vlc2(&s->gb, vec1_vlc, VLCBITS, VEC1MAXDEPTH); if (v1 == HUFF_VEC1_SIZE - 1) v1 += ff_wma_get_large_val(&s->gb); vals[i ] = av_float2int(v0); @@ -1059,7 +1064,7 @@ static int decode_scale_factors(WMAProDecodeCtx* s) s->channel[c].scale_factor_step = get_bits(&s->gb, 2) + 1; val = 45 / s->channel[c].scale_factor_step; for (sf = s->channel[c].scale_factors; sf < sf_end; sf++) { - val += get_vlc2(&s->gb, sf_vlc.table, SCALEVLCBITS, SCALEMAXDEPTH); + val += get_vlc2(&s->gb, sf_vlc, SCALEVLCBITS, SCALEMAXDEPTH); *sf = val; } } else { @@ -1071,7 +1076,7 @@ static int decode_scale_factors(WMAProDecodeCtx* s) int val; int sign; - idx = get_vlc2(&s->gb, sf_rl_vlc.table, VLCBITS, SCALERLMAXDEPTH); + idx = get_vlc2(&s->gb, sf_rl_vlc, VLCBITS, SCALERLMAXDEPTH); if (!idx) { uint32_t code = get_bits(&s->gb, 14); @@ -2094,7 +2099,11 @@ const FFCodec ff_wmapro_decoder = { .init = wmapro_decode_init, .close = wmapro_decode_end, FF_CODEC_DECODE_CB(wmapro_decode_packet), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1, .flush = wmapro_flush, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, @@ -2111,7 +2120,11 @@ const FFCodec ff_xma1_decoder = { .close = xma_decode_end, FF_CODEC_DECODE_CB(xma_decode_packet), .flush = xma_flush, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, @@ -2127,7 +2140,11 @@ const FFCodec ff_xma2_decoder = { .close = xma_decode_end, FF_CODEC_DECODE_CB(xma_decode_packet), .flush = xma_flush, - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, diff --git a/libavcodec/wmavoice.c b/libavcodec/wmavoice.c index bb98f841a50..4e93aadab2e 100644 --- a/libavcodec/wmavoice.c +++ b/libavcodec/wmavoice.c @@ -31,6 +31,7 @@ #include "libavutil/float_dsp.h" #include "libavutil/mem_internal.h" #include "libavutil/thread.h" +#include "libavutil/tx.h" #include "avcodec.h" #include "codec_internal.h" #include "decode.h" @@ -41,8 +42,6 @@ #include "acelp_vectors.h" #include "acelp_filters.h" #include "lsp.h" -#include "dct.h" -#include "rdft.h" #include "sinewin.h" #define MAX_BLOCKS 8 ///< maximum number of blocks per frame @@ -61,7 +60,7 @@ /** * Frame type VLC coding. */ -static VLC frame_type_vlc; +static VLCElem frame_type_vlc[132]; /** * Adaptive codebook types. @@ -263,10 +262,10 @@ typedef struct WMAVoiceContext { * smoothing and so on, and context variables for FFT/iFFT. * @{ */ - RDFTContext rdft, irdft; ///< contexts for FFT-calculation in the - ///< postfilter (for denoise filter) - DCTContext dct, dst; ///< contexts for phase shift (in Hilbert - ///< transform, part of postfilter) + AVTXContext *rdft, *irdft; ///< contexts for FFT-calculation in the + av_tx_fn rdft_fn, irdft_fn; ///< postfilter (for denoise filter) + AVTXContext *dct, *dst; ///< contexts for phase shift (in Hilbert + av_tx_fn dct_fn, dst_fn; ///< transform, part of postfilter) float sin[511], cos[511]; ///< 8-bit cosine/sine windows over [-pi,pi] ///< range float postfilter_agc; ///< gain control memory, used in @@ -277,9 +276,9 @@ typedef struct WMAVoiceContext { ///< by postfilter float denoise_filter_cache[MAX_FRAMESIZE]; int denoise_filter_cache_size; ///< samples in #denoise_filter_cache - DECLARE_ALIGNED(32, float, tilted_lpcs_pf)[0x80]; + DECLARE_ALIGNED(32, float, tilted_lpcs_pf)[0x82]; ///< aligned buffer for LPC tilting - DECLARE_ALIGNED(32, float, denoise_coeffs_pf)[0x80]; + DECLARE_ALIGNED(32, float, denoise_coeffs_pf)[0x82]; ///< aligned buffer for denoise coefficients DECLARE_ALIGNED(32, float, synth_filter_out_buf)[0x80 + MAX_LSPS_ALIGN16]; ///< aligned buffer for postfilter speech @@ -321,9 +320,9 @@ static av_cold void wmavoice_init_static_data(void) 14, 14, 14, 14 }; - INIT_VLC_STATIC_FROM_LENGTHS(&frame_type_vlc, VLC_NBITS, - FF_ARRAY_ELEMS(bits), bits, - 1, NULL, 0, 0, 0, 0, 132); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(frame_type_vlc, VLC_NBITS, + FF_ARRAY_ELEMS(bits), bits, + 1, NULL, 0, 0, 0, 0); } static av_cold void wmavoice_flush(AVCodecContext *ctx) @@ -388,10 +387,24 @@ static av_cold int wmavoice_decode_init(AVCodecContext *ctx) s->spillover_bitsize = 3 + av_ceil_log2(ctx->block_align); s->do_apf = flags & 0x1; if (s->do_apf) { - if ((ret = ff_rdft_init(&s->rdft, 7, DFT_R2C)) < 0 || - (ret = ff_rdft_init(&s->irdft, 7, IDFT_C2R)) < 0 || - (ret = ff_dct_init (&s->dct, 6, DCT_I)) < 0 || - (ret = ff_dct_init (&s->dst, 6, DST_I)) < 0) + float scale = 1.0f; + + ret = av_tx_init(&s->rdft, &s->rdft_fn, AV_TX_FLOAT_RDFT, 0, 1 << 7, &scale, 0); + if (ret < 0) + return ret; + + ret = av_tx_init(&s->irdft, &s->irdft_fn, AV_TX_FLOAT_RDFT, 1, 1 << 7, &scale, 0); + if (ret < 0) + return ret; + + scale = 1.0 / (1 << 6); + ret = av_tx_init(&s->dct, &s->dct_fn, AV_TX_FLOAT_DCT_I, 0, 1 << 6, &scale, 0); + if (ret < 0) + return ret; + + scale = 1.0 / (1 << 6); + ret = av_tx_init(&s->dst, &s->dst_fn, AV_TX_FLOAT_DST_I, 0, 1 << 6, &scale, 0); + if (ret < 0) return ret; ff_sine_window_init(s->cos, 256); @@ -596,20 +609,25 @@ static float tilt_factor(const float *lpcs, int n_lpcs) /** * Derive denoise filter coefficients (in real domain) from the LPCs. */ -static void calc_input_response(WMAVoiceContext *s, float *lpcs, - int fcb_type, float *coeffs, int remainder) +static void calc_input_response(WMAVoiceContext *s, float *lpcs_src, + int fcb_type, float *coeffs_dst, int remainder) { float last_coeff, min = 15.0, max = -15.0; float irange, angle_mul, gain_mul, range, sq; + LOCAL_ALIGNED_32(float, coeffs, [0x82]); + LOCAL_ALIGNED_32(float, lpcs, [0x82]); + LOCAL_ALIGNED_32(float, lpcs_dct, [0x82]); int n, idx; + memcpy(coeffs, coeffs_dst, 0x82*sizeof(float)); + /* Create frequency power spectrum of speech input (i.e. RDFT of LPCs) */ - s->rdft.rdft_calc(&s->rdft, lpcs); + s->rdft_fn(s->rdft, lpcs, lpcs_src, sizeof(float)); #define log_range(var, assign) do { \ float tmp = log10f(assign); var = tmp; \ max = FFMAX(max, tmp); min = FFMIN(min, tmp); \ } while (0) - log_range(last_coeff, lpcs[1] * lpcs[1]); + log_range(last_coeff, lpcs[64] * lpcs[64]); for (n = 1; n < 64; n++) log_range(lpcs[n], lpcs[n * 2] * lpcs[n * 2] + lpcs[n * 2 + 1] * lpcs[n * 2 + 1]); @@ -649,8 +667,8 @@ static void calc_input_response(WMAVoiceContext *s, float *lpcs, * is a sine input) by doing a phase shift (in theory, H(sin())=cos()). * Hilbert_Transform(RDFT(x)) = Laplace_Transform(x), which calculates the * "moment" of the LPCs in this filter. */ - s->dct.dct_calc(&s->dct, lpcs); - s->dst.dct_calc(&s->dst, lpcs); + s->dct_fn(s->dct, lpcs_dct, lpcs, sizeof(float)); + s->dst_fn(s->dst, lpcs, lpcs_dct, sizeof(float)); /* Split out the coefficient indexes into phase/magnitude pairs */ idx = 255 + av_clip(lpcs[64], -255, 255); @@ -668,25 +686,25 @@ static void calc_input_response(WMAVoiceContext *s, float *lpcs, coeffs[n * 2 + 1] = coeffs[n] * s->sin[idx]; coeffs[n * 2] = coeffs[n] * s->cos[idx]; } - coeffs[1] = last_coeff; + coeffs[64] = last_coeff; /* move into real domain */ - s->irdft.rdft_calc(&s->irdft, coeffs); + s->irdft_fn(s->irdft, coeffs_dst, coeffs, sizeof(AVComplexFloat)); /* tilt correction and normalize scale */ - memset(&coeffs[remainder], 0, sizeof(coeffs[0]) * (128 - remainder)); + memset(&coeffs_dst[remainder], 0, sizeof(coeffs_dst[0]) * (128 - remainder)); if (s->denoise_tilt_corr) { float tilt_mem = 0; - coeffs[remainder - 1] = 0; + coeffs_dst[remainder - 1] = 0; ff_tilt_compensation(&tilt_mem, - -1.8 * tilt_factor(coeffs, remainder - 1), - coeffs, remainder); + -1.8 * tilt_factor(coeffs_dst, remainder - 1), + coeffs_dst, remainder); } - sq = (1.0 / 64.0) * sqrtf(1 / avpriv_scalarproduct_float_c(coeffs, coeffs, + sq = (1.0 / 64.0) * sqrtf(1 / avpriv_scalarproduct_float_c(coeffs_dst, coeffs_dst, remainder)); for (n = 0; n < remainder; n++) - coeffs[n] *= sq; + coeffs_dst[n] *= sq; } /** @@ -722,6 +740,8 @@ static void wiener_denoise(WMAVoiceContext *s, int fcb_type, int remainder, lim, n; if (fcb_type != FCB_TYPE_SILENCE) { + LOCAL_ALIGNED_32(float, coeffs_f, [0x82]); + LOCAL_ALIGNED_32(float, synth_f, [0x82]); float *tilted_lpcs = s->tilted_lpcs_pf, *coeffs = s->denoise_coeffs_pf, tilt_mem = 0; @@ -742,16 +762,16 @@ static void wiener_denoise(WMAVoiceContext *s, int fcb_type, /* apply coefficients (in frequency spectrum domain), i.e. complex * number multiplication */ memset(&synth_pf[size], 0, sizeof(synth_pf[0]) * (128 - size)); - s->rdft.rdft_calc(&s->rdft, synth_pf); - s->rdft.rdft_calc(&s->rdft, coeffs); - synth_pf[0] *= coeffs[0]; - synth_pf[1] *= coeffs[1]; - for (n = 1; n < 64; n++) { - float v1 = synth_pf[n * 2], v2 = synth_pf[n * 2 + 1]; - synth_pf[n * 2] = v1 * coeffs[n * 2] - v2 * coeffs[n * 2 + 1]; - synth_pf[n * 2 + 1] = v2 * coeffs[n * 2] + v1 * coeffs[n * 2 + 1]; + s->rdft_fn(s->rdft, synth_f, synth_pf, sizeof(float)); + s->rdft_fn(s->rdft, coeffs_f, coeffs, sizeof(float)); + synth_f[0] *= coeffs_f[0]; + synth_f[1] *= coeffs_f[1]; + for (n = 1; n <= 64; n++) { + float v1 = synth_f[n * 2], v2 = synth_f[n * 2 + 1]; + synth_f[n * 2] = v1 * coeffs_f[n * 2] - v2 * coeffs_f[n * 2 + 1]; + synth_f[n * 2 + 1] = v2 * coeffs_f[n * 2] + v1 * coeffs_f[n * 2 + 1]; } - s->irdft.rdft_calc(&s->irdft, synth_pf); + s->irdft_fn(s->irdft, synth_pf, synth_f, sizeof(AVComplexFloat)); } /* merge filter output with the history of previous runs */ @@ -1483,7 +1503,7 @@ static int synth_frame(AVCodecContext *ctx, GetBitContext *gb, int frame_idx, int pitch[MAX_BLOCKS], av_uninit(last_block_pitch); /* Parse frame type ("frame header"), see frame_descs */ - int bd_idx = s->vbm_tree[get_vlc2(gb, frame_type_vlc.table, 6, 3)], block_nsamples; + int bd_idx = s->vbm_tree[get_vlc2(gb, frame_type_vlc, 6, 3)], block_nsamples; if (bd_idx < 0) { av_log(ctx, AV_LOG_ERROR, @@ -1986,10 +2006,10 @@ static av_cold int wmavoice_decode_end(AVCodecContext *ctx) WMAVoiceContext *s = ctx->priv_data; if (s->do_apf) { - ff_rdft_end(&s->rdft); - ff_rdft_end(&s->irdft); - ff_dct_end(&s->dct); - ff_dct_end(&s->dst); + av_tx_uninit(&s->rdft); + av_tx_uninit(&s->irdft); + av_tx_uninit(&s->dct); + av_tx_uninit(&s->dst); } return 0; @@ -2004,7 +2024,11 @@ const FFCodec ff_wmavoice_decoder = { .init = wmavoice_decode_init, .close = wmavoice_decode_end, FF_CODEC_DECODE_CB(wmavoice_decode_packet), - .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, + .p.capabilities = +#if FF_API_SUBFRAMES + AV_CODEC_CAP_SUBFRAMES | +#endif + AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, .flush = wmavoice_flush, }; diff --git a/libavcodec/wmv2dec.c b/libavcodec/wmv2dec.c index 469b2c4b2be..ff27d1b4d00 100644 --- a/libavcodec/wmv2dec.c +++ b/libavcodec/wmv2dec.c @@ -203,12 +203,8 @@ static int decode_ext_header(WMV2DecContext *w) int ff_wmv2_decode_picture_header(MpegEncContext *s) { - WMV2DecContext *const w = (WMV2DecContext *) s; int code; - if (s->picture_number == 0) - decode_ext_header(w); - s->pict_type = get_bits1(&s->gb) + 1; if (s->pict_type == AV_PICTURE_TYPE_I) { code = get_bits(&s->gb, 7); @@ -333,7 +329,6 @@ int ff_wmv2_decode_secondary_picture_header(MpegEncContext *s) } s->esc3_level_length = 0; s->esc3_run_length = 0; - s->picture_number++; // FIXME ? if (w->j_type) { ff_intrax8_decode_picture(&w->x8, &s->current_picture, @@ -473,7 +468,7 @@ static int wmv2_decode_mb(MpegEncContext *s, int16_t block[6][64]) if (get_bits_left(&s->gb) <= 0) return AVERROR_INVALIDDATA; - code = get_vlc2(&s->gb, ff_mb_non_intra_vlc[w->cbp_table_index].table, + code = get_vlc2(&s->gb, ff_mb_non_intra_vlc[w->cbp_table_index], MB_NON_INTRA_VLC_BITS, 3); s->mb_intra = (~code & 0x40) >> 6; @@ -482,7 +477,7 @@ static int wmv2_decode_mb(MpegEncContext *s, int16_t block[6][64]) s->mb_intra = 1; if (get_bits_left(&s->gb) <= 0) return AVERROR_INVALIDDATA; - code = get_vlc2(&s->gb, ff_msmp4_mb_i_vlc.table, + code = get_vlc2(&s->gb, ff_msmp4_mb_i_vlc, MSMP4_MB_INTRA_VLC_BITS, 2); /* predict coded block pattern */ cbp = 0; @@ -539,7 +534,7 @@ static int wmv2_decode_mb(MpegEncContext *s, int16_t block[6][64]) show_bits(&s->gb, 24)); s->ac_pred = get_bits1(&s->gb); if (s->inter_intra_pred) { - s->h263_aic_dir = get_vlc2(&s->gb, ff_inter_intra_vlc.table, + s->h263_aic_dir = get_vlc2(&s->gb, ff_inter_intra_vlc, INTER_INTRA_VLC_BITS, 1); ff_dlog(s->avctx, "%d%d %d %d/", s->ac_pred, s->h263_aic_dir, s->mb_x, s->mb_y); @@ -578,6 +573,8 @@ static av_cold int wmv2_decode_init(AVCodecContext *avctx) ff_wmv2_common_init(s); + decode_ext_header(w); + return ff_intrax8_common_init(avctx, &w->x8, w->s.block, w->s.block_last_index, w->s.mb_width, w->s.mb_height); @@ -602,6 +599,4 @@ const FFCodec ff_wmv2_decoder = { FF_CODEC_DECODE_CB(ff_h263_decode_frame), .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1, .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, - .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, - AV_PIX_FMT_NONE }, }; diff --git a/libavcodec/wnv1.c b/libavcodec/wnv1.c index 88532ee4266..0e8dae598f8 100644 --- a/libavcodec/wnv1.c +++ b/libavcodec/wnv1.c @@ -39,12 +39,12 @@ static const uint8_t code_tab[16][2] = { }; #define CODE_VLC_BITS 9 -static VLC code_vlc; +static VLCElem code_vlc[1 << CODE_VLC_BITS]; /* returns modified base_value */ static inline int wnv1_get_code(GetBitContext *gb, int shift, int base_value) { - int v = get_vlc2(gb, code_vlc.table, CODE_VLC_BITS, 1); + int v = get_vlc2(gb, code_vlc, CODE_VLC_BITS, 1); if (v == 8) return get_bits(gb, 8 - shift) << shift; @@ -69,7 +69,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; if ((ret = init_get_bits8(&gb, buf + 8, buf_size - 8)) < 0) return ret; @@ -115,10 +115,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, static av_cold void wnv1_init_static(void) { - INIT_VLC_STATIC_FROM_LENGTHS(&code_vlc, CODE_VLC_BITS, 16, - &code_tab[0][1], 2, - &code_tab[0][0], 2, 1, - -7, INIT_VLC_OUTPUT_LE, 1 << CODE_VLC_BITS); + VLC_INIT_STATIC_TABLE_FROM_LENGTHS(code_vlc, CODE_VLC_BITS, 16, + &code_tab[0][1], 2, + &code_tab[0][0], 2, 1, + -7, VLC_INIT_OUTPUT_LE); } static av_cold int decode_init(AVCodecContext *avctx) diff --git a/libavcodec/x86/Makefile b/libavcodec/x86/Makefile index 118daca333f..21c5e9b0d47 100644 --- a/libavcodec/x86/Makefile +++ b/libavcodec/x86/Makefile @@ -5,11 +5,9 @@ OBJS-$(CONFIG_AC3DSP) += x86/ac3dsp_init.o OBJS-$(CONFIG_AUDIODSP) += x86/audiodsp_init.o OBJS-$(CONFIG_BLOCKDSP) += x86/blockdsp_init.o OBJS-$(CONFIG_BSWAPDSP) += x86/bswapdsp_init.o -OBJS-$(CONFIG_DCT) += x86/dct_init.o OBJS-$(CONFIG_DIRAC_DECODER) += x86/diracdsp_init.o \ x86/dirac_dwt_init.o OBJS-$(CONFIG_FDCTDSP) += x86/fdctdsp_init.o -OBJS-$(CONFIG_FFT) += x86/fft_init.o OBJS-$(CONFIG_FMTCONVERT) += x86/fmtconvert_init.o OBJS-$(CONFIG_H263DSP) += x86/h263dsp_init.o OBJS-$(CONFIG_H264CHROMA) += x86/h264chroma_init.o @@ -56,7 +54,7 @@ OBJS-$(CONFIG_FLAC_DECODER) += x86/flacdsp_init.o OBJS-$(CONFIG_FLAC_ENCODER) += x86/flacencdsp_init.o OBJS-$(CONFIG_OPUS_DECODER) += x86/opusdsp_init.o OBJS-$(CONFIG_OPUS_ENCODER) += x86/celt_pvq_init.o -OBJS-$(CONFIG_HEVC_DECODER) += x86/hevcdsp_init.o +OBJS-$(CONFIG_HEVC_DECODER) += x86/hevcdsp_init.o x86/h26x/h2656dsp.o OBJS-$(CONFIG_JPEG2000_DECODER) += x86/jpeg2000dsp_init.o OBJS-$(CONFIG_LSCR_DECODER) += x86/pngdsp_init.o OBJS-$(CONFIG_MLP_DECODER) += x86/mlpdsp_init.o @@ -75,7 +73,6 @@ OBJS-$(CONFIG_UTVIDEO_DECODER) += x86/utvideodsp_init.o OBJS-$(CONFIG_V210_DECODER) += x86/v210-init.o OBJS-$(CONFIG_V210_ENCODER) += x86/v210enc_init.o OBJS-$(CONFIG_VORBIS_DECODER) += x86/vorbisdsp_init.o -OBJS-$(CONFIG_VP3_DECODER) += x86/hpeldsp_vp3_init.o OBJS-$(CONFIG_VP6_DECODER) += x86/vp6dsp_init.o OBJS-$(CONFIG_VP9_DECODER) += x86/vp9dsp_init.o \ x86/vp9dsp_init_10bpp.o \ @@ -99,8 +96,6 @@ X86ASM-OBJS-$(CONFIG_AC3DSP) += x86/ac3dsp.o \ X86ASM-OBJS-$(CONFIG_AUDIODSP) += x86/audiodsp.o X86ASM-OBJS-$(CONFIG_BLOCKDSP) += x86/blockdsp.o X86ASM-OBJS-$(CONFIG_BSWAPDSP) += x86/bswapdsp.o -X86ASM-OBJS-$(CONFIG_DCT) += x86/dct32.o -X86ASM-OBJS-$(CONFIG_FFT) += x86/fft.o X86ASM-OBJS-$(CONFIG_FMTCONVERT) += x86/fmtconvert.o X86ASM-OBJS-$(CONFIG_H263DSP) += x86/h263_loopfilter.o X86ASM-OBJS-$(CONFIG_H264CHROMA) += x86/h264_chromamc.o \ @@ -127,7 +122,7 @@ X86ASM-OBJS-$(CONFIG_LLVIDDSP) += x86/lossless_videodsp.o X86ASM-OBJS-$(CONFIG_LLVIDENCDSP) += x86/lossless_videoencdsp.o X86ASM-OBJS-$(CONFIG_LPC) += x86/lpc.o X86ASM-OBJS-$(CONFIG_ME_CMP) += x86/me_cmp.o -X86ASM-OBJS-$(CONFIG_MPEGAUDIODSP) += x86/imdct36.o +X86ASM-OBJS-$(CONFIG_MPEGAUDIODSP) += x86/dct32.o x86/imdct36.o X86ASM-OBJS-$(CONFIG_MPEGVIDEOENC) += x86/mpegvideoencdsp.o X86ASM-OBJS-$(CONFIG_OPUS_DECODER) += x86/opusdsp.o X86ASM-OBJS-$(CONFIG_OPUS_ENCODER) += x86/celt_pvq_search.o @@ -172,6 +167,7 @@ X86ASM-OBJS-$(CONFIG_HEVC_DECODER) += x86/hevc_add_res.o \ x86/hevc_deblock.o \ x86/hevc_idct.o \ x86/hevc_mc.o \ + x86/h26x/h2656_inter.o \ x86/hevc_sao.o \ x86/hevc_sao_10bit.o X86ASM-OBJS-$(CONFIG_JPEG2000_DECODER) += x86/jpeg2000dsp.o @@ -192,7 +188,6 @@ X86ASM-OBJS-$(CONFIG_UTVIDEO_DECODER) += x86/utvideodsp.o X86ASM-OBJS-$(CONFIG_V210_ENCODER) += x86/v210enc.o X86ASM-OBJS-$(CONFIG_V210_DECODER) += x86/v210.o X86ASM-OBJS-$(CONFIG_VORBIS_DECODER) += x86/vorbisdsp.o -X86ASM-OBJS-$(CONFIG_VP3_DECODER) += x86/hpeldsp_vp3.o X86ASM-OBJS-$(CONFIG_VP6_DECODER) += x86/vp6dsp.o X86ASM-OBJS-$(CONFIG_VP9_DECODER) += x86/vp9intrapred.o \ x86/vp9intrapred_16bpp.o \ diff --git a/libavcodec/x86/aacencdsp.asm b/libavcodec/x86/aacencdsp.asm index 97af571ec8b..0d3ba4b89d5 100644 --- a/libavcodec/x86/aacencdsp.asm +++ b/libavcodec/x86/aacencdsp.asm @@ -34,7 +34,7 @@ SECTION .text INIT_XMM sse cglobal abs_pow34, 3, 3, 3, out, in, size mova m2, [float_abs_mask] - shl sizeq, 2 + shl sized, 2 add inq, sizeq add outq, sizeq neg sizeq diff --git a/libavcodec/x86/aacencdsp_init.c b/libavcodec/x86/aacencdsp_init.c index 049a2417d91..e0d8dec4f84 100644 --- a/libavcodec/x86/aacencdsp_init.c +++ b/libavcodec/x86/aacencdsp_init.c @@ -22,9 +22,8 @@ #include "config.h" #include "libavutil/attributes.h" -#include "libavutil/float_dsp.h" #include "libavutil/x86/cpu.h" -#include "libavcodec/aacenc.h" +#include "libavcodec/aacencdsp.h" void ff_abs_pow34_sse(float *out, const float *in, const int size); @@ -32,7 +31,7 @@ void ff_aac_quantize_bands_sse2(int *out, const float *in, const float *scaled, int size, int is_signed, int maxval, const float Q34, const float rounding); -av_cold void ff_aac_dsp_init_x86(AACEncContext *s) +av_cold void ff_aacenc_dsp_init_x86(AACEncDSPContext *s) { int cpu_flags = av_get_cpu_flags(); diff --git a/libavcodec/x86/ac3dsp.asm b/libavcodec/x86/ac3dsp.asm index a95d359d952..0ba980aa7ba 100644 --- a/libavcodec/x86/ac3dsp.asm +++ b/libavcodec/x86/ac3dsp.asm @@ -77,16 +77,20 @@ AC3_EXPONENT_MIN INIT_XMM sse2 cglobal float_to_fixed24, 3, 3, 9, dst, src, len movaps m0, [pf_1_24] + shl lenq, 2 + add srcq, lenq + add dstq, lenq + neg lenq .loop: - movaps m1, [srcq ] - movaps m2, [srcq+16 ] - movaps m3, [srcq+32 ] - movaps m4, [srcq+48 ] + movaps m1, [srcq+lenq ] + movaps m2, [srcq+lenq+16 ] + movaps m3, [srcq+lenq+32 ] + movaps m4, [srcq+lenq+48 ] %ifdef m8 - movaps m5, [srcq+64 ] - movaps m6, [srcq+80 ] - movaps m7, [srcq+96 ] - movaps m8, [srcq+112] + movaps m5, [srcq+lenq+64 ] + movaps m6, [srcq+lenq+80 ] + movaps m7, [srcq+lenq+96 ] + movaps m8, [srcq+lenq+112] %endif mulps m1, m0 mulps m2, m0 @@ -108,24 +112,44 @@ cglobal float_to_fixed24, 3, 3, 9, dst, src, len cvtps2dq m7, m7 cvtps2dq m8, m8 %endif - movdqa [dstq ], m1 - movdqa [dstq+16 ], m2 - movdqa [dstq+32 ], m3 - movdqa [dstq+48 ], m4 + movdqa [dstq+lenq ], m1 + movdqa [dstq+lenq+16 ], m2 + movdqa [dstq+lenq+32 ], m3 + movdqa [dstq+lenq+48 ], m4 %ifdef m8 - movdqa [dstq+64 ], m5 - movdqa [dstq+80 ], m6 - movdqa [dstq+96 ], m7 - movdqa [dstq+112], m8 - add srcq, 128 - add dstq, 128 - sub lenq, 32 + movdqa [dstq+lenq+64 ], m5 + movdqa [dstq+lenq+80 ], m6 + movdqa [dstq+lenq+96 ], m7 + movdqa [dstq+lenq+112], m8 + add lenq, 128 %else - add srcq, 64 - add dstq, 64 - sub lenq, 16 + add lenq, 64 %endif - ja .loop + jl .loop + RET + +INIT_YMM avx +cglobal float_to_fixed24, 3, 3, 5, dst, src, len + vbroadcastf128 m0, [pf_1_24] + shl lenq, 2 + add srcq, lenq + add dstq, lenq + neg lenq +.loop: + mulps m1, m0, [srcq+lenq+mmsize*0] + mulps m2, m0, [srcq+lenq+mmsize*1] + mulps m3, m0, [srcq+lenq+mmsize*2] + mulps m4, m0, [srcq+lenq+mmsize*3] + cvtps2dq m1, m1 + cvtps2dq m2, m2 + cvtps2dq m3, m3 + cvtps2dq m4, m4 + mova [dstq+lenq+mmsize*0], m1 + mova [dstq+lenq+mmsize*1], m2 + mova [dstq+lenq+mmsize*2], m3 + mova [dstq+lenq+mmsize*3], m4 + add lenq, mmsize*4 + jl .loop RET ;------------------------------------------------------------------------------ diff --git a/libavcodec/x86/ac3dsp_init.c b/libavcodec/x86/ac3dsp_init.c index 43b3b4ac85a..353cf38f86c 100644 --- a/libavcodec/x86/ac3dsp_init.c +++ b/libavcodec/x86/ac3dsp_init.c @@ -26,7 +26,8 @@ void ff_ac3_exponent_min_sse2 (uint8_t *exp, int num_reuse_blocks, int nb_coefs); -void ff_float_to_fixed24_sse2 (int32_t *dst, const float *src, unsigned int len); +void ff_float_to_fixed24_sse2 (int32_t *dst, const float *src, size_t len); +void ff_float_to_fixed24_avx (int32_t *dst, const float *src, size_t len); int ff_ac3_compute_mantissa_size_sse2(uint16_t mant_cnt[6][16]); @@ -48,6 +49,9 @@ av_cold void ff_ac3dsp_init_x86(AC3DSPContext *c) if (!(cpu_flags & AV_CPU_FLAG_ATOM)) c->extract_exponents = ff_ac3_extract_exponents_ssse3; } + if (EXTERNAL_AVX_FAST(cpu_flags)) { + c->float_to_fixed24 = ff_float_to_fixed24_avx; + } } #define DOWNMIX_FUNC_OPT(ch, opt) \ diff --git a/libavcodec/x86/cabac.h b/libavcodec/x86/cabac.h index b046a56a6b7..ce2aefcbac8 100644 --- a/libavcodec/x86/cabac.h +++ b/libavcodec/x86/cabac.h @@ -178,7 +178,7 @@ #if HAVE_7REGS && !BROKEN_COMPILER #define get_cabac_inline get_cabac_inline_x86 static -#if defined(_WIN32) && !defined(_WIN64) && defined(__clang__) +#if ARCH_X86_32 av_noinline #else av_always_inline diff --git a/libavcodec/x86/celt_pvq_search.asm b/libavcodec/x86/celt_pvq_search.asm index 5c1e6d6174c..e9bff026503 100644 --- a/libavcodec/x86/celt_pvq_search.asm +++ b/libavcodec/x86/celt_pvq_search.asm @@ -74,7 +74,7 @@ SECTION .text ; "movaps m0, [r5 + r4]" if PIC is enabled ; "movaps m0, [constant_name + r4]" if texrel are used %macro SET_PIC_BASE 3; reg, const_label -%ifdef PIC +%if PIC %{1} %2, [%3] ; lea r5, [rip+const] %define pic_base_%3 %2 %else @@ -195,7 +195,7 @@ align 16 ; PIC relative addressing. Use this ; to count it in cglobal ; -%ifdef PIC +%if PIC %define num_pic_regs 1 %else %define num_pic_regs 0 diff --git a/libavcodec/x86/fft.asm b/libavcodec/x86/fft.asm deleted file mode 100644 index 34c3fc9a0f5..00000000000 --- a/libavcodec/x86/fft.asm +++ /dev/null @@ -1,838 +0,0 @@ -;****************************************************************************** -;* FFT transform with SSE/AVX optimizations -;* Copyright (c) 2008 Loren Merritt -;* Copyright (c) 2011 Vitor Sessak -;* -;* This algorithm (though not any of the implementation details) is -;* based on libdjbfft by D. J. Bernstein. -;* -;* 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 -;****************************************************************************** - -; These functions are not individually interchangeable with the C versions. -; While C takes arrays of FFTComplex, SSE/3DNow leave intermediate results -; in blocks as conventient to the vector size. -; i.e. {4x real, 4x imaginary, 4x real, ...} (or 2x respectively) - -%include "libavutil/x86/x86util.asm" - -%if ARCH_X86_64 -%define pointer resq -%else -%define pointer resd -%endif - -struc FFTContext - .nbits: resd 1 - .reverse: resd 1 - .revtab: pointer 1 - .tmpbuf: pointer 1 - .mdctsize: resd 1 - .mdctbits: resd 1 - .tcos: pointer 1 - .tsin: pointer 1 - .fftperm: pointer 1 - .fftcalc: pointer 1 - .imdctcalc:pointer 1 - .imdcthalf:pointer 1 -endstruc - -SECTION_RODATA 32 - -%define M_SQRT1_2 0.70710678118654752440 -%define M_COS_PI_1_8 0.923879532511287 -%define M_COS_PI_3_8 0.38268343236509 - -ps_cos16_1: dd 1.0, M_COS_PI_1_8, M_SQRT1_2, M_COS_PI_3_8, 1.0, M_COS_PI_1_8, M_SQRT1_2, M_COS_PI_3_8 -ps_cos16_2: dd 0, M_COS_PI_3_8, M_SQRT1_2, M_COS_PI_1_8, 0, -M_COS_PI_3_8, -M_SQRT1_2, -M_COS_PI_1_8 - -ps_root2: times 8 dd M_SQRT1_2 -ps_root2mppm: dd -M_SQRT1_2, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2, -M_SQRT1_2, M_SQRT1_2, M_SQRT1_2, -M_SQRT1_2 -ps_p1p1m1p1: dd 0, 0, 1<<31, 0, 0, 0, 1<<31, 0 - -perm1: dd 0x00, 0x02, 0x03, 0x01, 0x03, 0x00, 0x02, 0x01 -perm2: dd 0x00, 0x01, 0x02, 0x03, 0x01, 0x00, 0x02, 0x03 -ps_p1p1m1p1root2: dd 1.0, 1.0, -1.0, 1.0, M_SQRT1_2, M_SQRT1_2, M_SQRT1_2, M_SQRT1_2 -ps_m1m1p1m1p1m1m1m1: dd 1<<31, 1<<31, 0, 1<<31, 0, 1<<31, 1<<31, 1<<31 -ps_m1p1: dd 1<<31, 0 - -cextern ps_neg - -%assign i 16 -%rep 14 -cextern cos_ %+ i -%assign i i<<1 -%endrep - -%if ARCH_X86_64 - %define pointer dq -%else - %define pointer dd -%endif - -%macro IF0 1+ -%endmacro -%macro IF1 1+ - %1 -%endmacro - -SECTION .text - -; in: %1 = {r0,i0,r2,i2,r4,i4,r6,i6} -; %2 = {r1,i1,r3,i3,r5,i5,r7,i7} -; %3, %4, %5 tmp -; out: %1 = {r0,r1,r2,r3,i0,i1,i2,i3} -; %2 = {r4,r5,r6,r7,i4,i5,i6,i7} -%macro T8_AVX 5 - vsubps %5, %1, %2 ; v = %1 - %2 - vaddps %3, %1, %2 ; w = %1 + %2 - vmulps %2, %5, [ps_p1p1m1p1root2] ; v *= vals1 - vpermilps %2, %2, [perm1] - vblendps %1, %2, %3, 0x33 ; q = {w1,w2,v4,v2,w5,w6,v7,v6} - vshufps %5, %3, %2, 0x4e ; r = {w3,w4,v1,v3,w7,w8,v8,v5} - vsubps %4, %5, %1 ; s = r - q - vaddps %1, %5, %1 ; u = r + q - vpermilps %1, %1, [perm2] ; k = {u1,u2,u3,u4,u6,u5,u7,u8} - vshufps %5, %4, %1, 0xbb - vshufps %3, %4, %1, 0xee - vperm2f128 %3, %3, %5, 0x13 - vxorps %4, %4, [ps_m1m1p1m1p1m1m1m1] ; s *= {1,1,-1,-1,1,-1,-1,-1} - vshufps %2, %1, %4, 0xdd - vshufps %1, %1, %4, 0x88 - vperm2f128 %4, %2, %1, 0x02 ; v = {k1,k3,s1,s3,k2,k4,s2,s4} - vperm2f128 %1, %1, %2, 0x13 ; w = {k6,k8,s6,s8,k5,k7,s5,s7} - vsubps %5, %1, %3 - vblendps %1, %5, %1, 0x55 ; w -= {0,s7,0,k7,0,s8,0,k8} - vsubps %2, %4, %1 ; %2 = v - w - vaddps %1, %4, %1 ; %1 = v + w -%endmacro - -; In SSE mode do one fft4 transforms -; in: %1={r0,i0,r2,i2} %2={r1,i1,r3,i3} -; out: %1={r0,r1,r2,r3} %2={i0,i1,i2,i3} -; -; In AVX mode do two fft4 transforms -; in: %1={r0,i0,r2,i2,r4,i4,r6,i6} %2={r1,i1,r3,i3,r5,i5,r7,i7} -; out: %1={r0,r1,r2,r3,r4,r5,r6,r7} %2={i0,i1,i2,i3,i4,i5,i6,i7} -%macro T4_SSE 3 - subps %3, %1, %2 ; {t3,t4,-t8,t7} - addps %1, %1, %2 ; {t1,t2,t6,t5} - xorps %3, %3, [ps_p1p1m1p1] - shufps %2, %1, %3, 0xbe ; {t6,t5,t7,t8} - shufps %1, %1, %3, 0x44 ; {t1,t2,t3,t4} - subps %3, %1, %2 ; {r2,i2,r3,i3} - addps %1, %1, %2 ; {r0,i0,r1,i1} - shufps %2, %1, %3, 0xdd ; {i0,i1,i2,i3} - shufps %1, %1, %3, 0x88 ; {r0,r1,r2,r3} -%endmacro - -; In SSE mode do one FFT8 -; in: %1={r0,r1,r2,r3} %2={i0,i1,i2,i3} %3={r4,i4,r6,i6} %4={r5,i5,r7,i7} -; out: %1={r0,r1,r2,r3} %2={i0,i1,i2,i3} %1={r4,r5,r6,r7} %2={i4,i5,i6,i7} -; -; In AVX mode do two FFT8 -; in: %1={r0,i0,r2,i2,r8, i8, r10,i10} %2={r1,i1,r3,i3,r9, i9, r11,i11} -; %3={r4,i4,r6,i6,r12,i12,r14,i14} %4={r5,i5,r7,i7,r13,i13,r15,i15} -; out: %1={r0,r1,r2,r3,r8, r9, r10,r11} %2={i0,i1,i2,i3,i8, i9, i10,i11} -; %3={r4,r5,r6,r7,r12,r13,r14,r15} %4={i4,i5,i6,i7,i12,i13,i14,i15} -%macro T8_SSE 6 - addps %6, %3, %4 ; {t1,t2,t3,t4} - subps %3, %3, %4 ; {r5,i5,r7,i7} - shufps %4, %3, %3, 0xb1 ; {i5,r5,i7,r7} - mulps %3, %3, [ps_root2mppm] ; {-r5,i5,r7,-i7} - mulps %4, %4, [ps_root2] - addps %3, %3, %4 ; {t8,t7,ta,t9} - shufps %4, %6, %3, 0x9c ; {t1,t4,t7,ta} - shufps %6, %6, %3, 0x36 ; {t3,t2,t9,t8} - subps %3, %6, %4 ; {t6,t5,tc,tb} - addps %6, %6, %4 ; {t1,t2,t9,ta} - shufps %5, %6, %3, 0x8d ; {t2,ta,t6,tc} - shufps %6, %6, %3, 0xd8 ; {t1,t9,t5,tb} - subps %3, %1, %6 ; {r4,r5,r6,r7} - addps %1, %1, %6 ; {r0,r1,r2,r3} - subps %4, %2, %5 ; {i4,i5,i6,i7} - addps %2, %2, %5 ; {i0,i1,i2,i3} -%endmacro - -%macro INTERL 5 -%if cpuflag(avx) - vunpckhps %3, %2, %1 - vunpcklps %2, %2, %1 - vextractf128 %4(%5), %2, 0 - vextractf128 %4 %+ H(%5), %3, 0 - vextractf128 %4(%5 + 1), %2, 1 - vextractf128 %4 %+ H(%5 + 1), %3, 1 -%elif cpuflag(sse) - mova %3, %2 - unpcklps %2, %1 - unpckhps %3, %1 - mova %4(%5), %2 - mova %4(%5+1), %3 -%endif -%endmacro - -; scheduled for cpu-bound sizes -%macro PASS_SMALL 3 ; (to load m4-m7), wre, wim -IF%1 mova m4, Z(4) -IF%1 mova m5, Z(5) - mova m0, %2 ; wre - mova m1, %3 ; wim - mulps m2, m4, m0 ; r2*wre -IF%1 mova m6, Z2(6) - mulps m3, m5, m1 ; i2*wim -IF%1 mova m7, Z2(7) - mulps m4, m4, m1 ; r2*wim - mulps m5, m5, m0 ; i2*wre - addps m2, m2, m3 ; r2*wre + i2*wim - mulps m3, m1, m7 ; i3*wim - subps m5, m5, m4 ; i2*wre - r2*wim - mulps m1, m1, m6 ; r3*wim - mulps m4, m0, m6 ; r3*wre - mulps m0, m0, m7 ; i3*wre - subps m4, m4, m3 ; r3*wre - i3*wim - mova m3, Z(0) - addps m0, m0, m1 ; i3*wre + r3*wim - subps m1, m4, m2 ; t3 - addps m4, m4, m2 ; t5 - subps m3, m3, m4 ; r2 - addps m4, m4, Z(0) ; r0 - mova m6, Z(2) - mova Z(4), m3 - mova Z(0), m4 - subps m3, m5, m0 ; t4 - subps m4, m6, m3 ; r3 - addps m3, m3, m6 ; r1 - mova Z2(6), m4 - mova Z(2), m3 - mova m2, Z(3) - addps m3, m5, m0 ; t6 - subps m2, m2, m1 ; i3 - mova m7, Z(1) - addps m1, m1, Z(3) ; i1 - mova Z2(7), m2 - mova Z(3), m1 - subps m4, m7, m3 ; i2 - addps m3, m3, m7 ; i0 - mova Z(5), m4 - mova Z(1), m3 -%endmacro - -; scheduled to avoid store->load aliasing -%macro PASS_BIG 1 ; (!interleave) - mova m4, Z(4) ; r2 - mova m5, Z(5) ; i2 - mova m0, [wq] ; wre - mova m1, [wq+o1q] ; wim - mulps m2, m4, m0 ; r2*wre - mova m6, Z2(6) ; r3 - mulps m3, m5, m1 ; i2*wim - mova m7, Z2(7) ; i3 - mulps m4, m4, m1 ; r2*wim - mulps m5, m5, m0 ; i2*wre - addps m2, m2, m3 ; r2*wre + i2*wim - mulps m3, m1, m7 ; i3*wim - mulps m1, m1, m6 ; r3*wim - subps m5, m5, m4 ; i2*wre - r2*wim - mulps m4, m0, m6 ; r3*wre - mulps m0, m0, m7 ; i3*wre - subps m4, m4, m3 ; r3*wre - i3*wim - mova m3, Z(0) - addps m0, m0, m1 ; i3*wre + r3*wim - subps m1, m4, m2 ; t3 - addps m4, m4, m2 ; t5 - subps m3, m3, m4 ; r2 - addps m4, m4, Z(0) ; r0 - mova m6, Z(2) - mova Z(4), m3 - mova Z(0), m4 - subps m3, m5, m0 ; t4 - subps m4, m6, m3 ; r3 - addps m3, m3, m6 ; r1 -IF%1 mova Z2(6), m4 -IF%1 mova Z(2), m3 - mova m2, Z(3) - addps m5, m5, m0 ; t6 - subps m2, m2, m1 ; i3 - mova m7, Z(1) - addps m1, m1, Z(3) ; i1 -IF%1 mova Z2(7), m2 -IF%1 mova Z(3), m1 - subps m6, m7, m5 ; i2 - addps m5, m5, m7 ; i0 -IF%1 mova Z(5), m6 -IF%1 mova Z(1), m5 -%if %1==0 - INTERL m1, m3, m7, Z, 2 - INTERL m2, m4, m0, Z2, 6 - - mova m1, Z(0) - mova m2, Z(4) - - INTERL m5, m1, m3, Z, 0 - INTERL m6, m2, m7, Z, 4 -%endif -%endmacro - -%define Z(x) [r0+mmsize*x] -%define Z2(x) [r0+mmsize*x] -%define ZH(x) [r0+mmsize*x+mmsize/2] - -INIT_YMM avx - -%if HAVE_AVX_EXTERNAL -align 16 -fft8_avx: - mova m0, Z(0) - mova m1, Z(1) - T8_AVX m0, m1, m2, m3, m4 - mova Z(0), m0 - mova Z(1), m1 - ret - - -align 16 -fft16_avx: - mova m2, Z(2) - mova m3, Z(3) - T4_SSE m2, m3, m7 - - mova m0, Z(0) - mova m1, Z(1) - T8_AVX m0, m1, m4, m5, m7 - - mova m4, [ps_cos16_1] - mova m5, [ps_cos16_2] - vmulps m6, m2, m4 - vmulps m7, m3, m5 - vaddps m7, m7, m6 - vmulps m2, m2, m5 - vmulps m3, m3, m4 - vsubps m3, m3, m2 - vblendps m2, m7, m3, 0xf0 - vperm2f128 m3, m7, m3, 0x21 - vaddps m4, m2, m3 - vsubps m2, m3, m2 - vperm2f128 m2, m2, m2, 0x01 - vsubps m3, m1, m2 - vaddps m1, m1, m2 - vsubps m5, m0, m4 - vaddps m0, m0, m4 - vextractf128 Z(0), m0, 0 - vextractf128 ZH(0), m1, 0 - vextractf128 Z(1), m0, 1 - vextractf128 ZH(1), m1, 1 - vextractf128 Z(2), m5, 0 - vextractf128 ZH(2), m3, 0 - vextractf128 Z(3), m5, 1 - vextractf128 ZH(3), m3, 1 - ret - -align 16 -fft32_avx: - call fft16_avx - - mova m0, Z(4) - mova m1, Z(5) - - T4_SSE m0, m1, m4 - - mova m2, Z(6) - mova m3, Z(7) - - T8_SSE m0, m1, m2, m3, m4, m6 - ; m0={r0,r1,r2,r3,r8, r9, r10,r11} m1={i0,i1,i2,i3,i8, i9, i10,i11} - ; m2={r4,r5,r6,r7,r12,r13,r14,r15} m3={i4,i5,i6,i7,i12,i13,i14,i15} - - vperm2f128 m4, m0, m2, 0x20 - vperm2f128 m5, m1, m3, 0x20 - vperm2f128 m6, m0, m2, 0x31 - vperm2f128 m7, m1, m3, 0x31 - - PASS_SMALL 0, [cos_32], [cos_32+32] - - ret - -fft32_interleave_avx: - call fft32_avx - mov r2d, 32 -.deint_loop: - mova m2, Z(0) - mova m3, Z(1) - vunpcklps m0, m2, m3 - vunpckhps m1, m2, m3 - vextractf128 Z(0), m0, 0 - vextractf128 ZH(0), m1, 0 - vextractf128 Z(1), m0, 1 - vextractf128 ZH(1), m1, 1 - add r0, mmsize*2 - sub r2d, mmsize/4 - jg .deint_loop - ret - -%endif - -INIT_XMM sse - -align 16 -fft4_avx: -fft4_sse: - mova m0, Z(0) - mova m1, Z(1) - T4_SSE m0, m1, m2 - mova Z(0), m0 - mova Z(1), m1 - ret - -align 16 -fft8_sse: - mova m0, Z(0) - mova m1, Z(1) - T4_SSE m0, m1, m2 - mova m2, Z(2) - mova m3, Z(3) - T8_SSE m0, m1, m2, m3, m4, m5 - mova Z(0), m0 - mova Z(1), m1 - mova Z(2), m2 - mova Z(3), m3 - ret - -align 16 -fft16_sse: - mova m0, Z(0) - mova m1, Z(1) - T4_SSE m0, m1, m2 - mova m2, Z(2) - mova m3, Z(3) - T8_SSE m0, m1, m2, m3, m4, m5 - mova m4, Z(4) - mova m5, Z(5) - mova Z(0), m0 - mova Z(1), m1 - mova Z(2), m2 - mova Z(3), m3 - T4_SSE m4, m5, m6 - mova m6, Z2(6) - mova m7, Z2(7) - T4_SSE m6, m7, m0 - PASS_SMALL 0, [cos_16], [cos_16+16] - ret - - -%define Z(x) [zcq + o1q*(x&6) + mmsize*(x&1)] -%define Z2(x) [zcq + o3q + mmsize*(x&1)] -%define ZH(x) [zcq + o1q*(x&6) + mmsize*(x&1) + mmsize/2] -%define Z2H(x) [zcq + o3q + mmsize*(x&1) + mmsize/2] - -%macro DECL_PASS 2+ ; name, payload -align 16 -%1: -DEFINE_ARGS zc, w, n, o1, o3 - lea o3q, [nq*3] - lea o1q, [nq*8] - shl o3q, 4 -.loop: - %2 - add zcq, mmsize*2 - add wq, mmsize - sub nd, mmsize/8 - jg .loop - rep ret -%endmacro - -%macro FFT_DISPATCH 2; clobbers 5 GPRs, 8 XMMs - lea r2, [dispatch_tab%1] - mov r2, [r2 + (%2q-2)*gprsize] -%ifdef PIC - lea r3, [$$] - add r2, r3 -%endif - call r2 -%endmacro ; FFT_DISPATCH - -INIT_YMM avx - -%if HAVE_AVX_EXTERNAL -DECL_PASS pass_avx, PASS_BIG 1 -DECL_PASS pass_interleave_avx, PASS_BIG 0 - -cglobal fft_calc, 2,5,8 - mov r3d, [r0 + FFTContext.nbits] - mov r0, r1 - mov r1, r3 - FFT_DISPATCH _interleave %+ SUFFIX, r1 - RET - -%endif - -INIT_XMM sse - -DECL_PASS pass_sse, PASS_BIG 1 -DECL_PASS pass_interleave_sse, PASS_BIG 0 - -INIT_XMM sse -cglobal fft_calc, 2,5,8 - mov r3d, [r0 + FFTContext.nbits] - PUSH r1 - PUSH r3 - mov r0, r1 - mov r1, r3 - FFT_DISPATCH _interleave %+ SUFFIX, r1 - POP rcx - POP r4 - cmp rcx, 3+(mmsize/16) - jg .end - mov r2, -1 - add rcx, 3 - shl r2, cl - sub r4, r2 -.loop: - movaps xmm0, [r4 + r2] - movaps xmm1, xmm0 - unpcklps xmm0, [r4 + r2 + 16] - unpckhps xmm1, [r4 + r2 + 16] - movaps [r4 + r2], xmm0 - movaps [r4 + r2 + 16], xmm1 - add r2, mmsize*2 - jl .loop -.end: - RET - -cglobal fft_permute, 2,7,1 - mov r4, [r0 + FFTContext.revtab] - mov r5, [r0 + FFTContext.tmpbuf] - mov ecx, [r0 + FFTContext.nbits] - mov r2, 1 - shl r2, cl - xor r0, r0 -%if ARCH_X86_32 - mov r1, r1m -%endif -.loop: - movaps xmm0, [r1 + 8*r0] - movzx r6, word [r4 + 2*r0] - movzx r3, word [r4 + 2*r0 + 2] - movlps [r5 + 8*r6], xmm0 - movhps [r5 + 8*r3], xmm0 - add r0, 2 - cmp r0, r2 - jl .loop - shl r2, 3 - add r1, r2 - add r5, r2 - neg r2 -; nbits >= 2 (FFT4) and sizeof(FFTComplex)=8 => at least 32B -.loopcopy: - movaps xmm0, [r5 + r2] - movaps xmm1, [r5 + r2 + 16] - movaps [r1 + r2], xmm0 - movaps [r1 + r2 + 16], xmm1 - add r2, 32 - jl .loopcopy - RET - -INIT_XMM sse -cglobal imdct_calc, 3,5,3 - mov r3d, [r0 + FFTContext.mdctsize] - mov r4, [r0 + FFTContext.imdcthalf] - add r1, r3 - PUSH r3 - PUSH r1 -%if ARCH_X86_32 - push r2 - push r1 - push r0 -%else - sub rsp, 8+32*WIN64 ; allocate win64 shadow space -%endif - call r4 -%if ARCH_X86_32 - add esp, 12 -%else - add rsp, 8+32*WIN64 -%endif - POP r1 - POP r3 - lea r0, [r1 + 2*r3] - mov r2, r3 - sub r3, mmsize - neg r2 - mova m2, [ps_neg] -.loop: - mova m0, [r1 + r3] - mova m1, [r0 + r2] - shufps m0, m0, 0x1b - shufps m1, m1, 0x1b - xorps m0, m2 - mova [r0 + r3], m1 - mova [r1 + r2], m0 - sub r3, mmsize - add r2, mmsize - jl .loop - RET - -%ifdef PIC -%define SECTION_REL - $$ -%else -%define SECTION_REL -%endif - -%macro DECL_FFT 1-2 ; nbits, suffix -%ifidn %0, 1 -%xdefine fullsuffix SUFFIX -%else -%xdefine fullsuffix %2 %+ SUFFIX -%endif -%xdefine list_of_fft fft4 %+ SUFFIX SECTION_REL, fft8 %+ SUFFIX SECTION_REL -%if %1>=5 -%xdefine list_of_fft list_of_fft, fft16 %+ SUFFIX SECTION_REL -%endif -%if %1>=6 -%xdefine list_of_fft list_of_fft, fft32 %+ fullsuffix SECTION_REL -%endif - -%assign n 1<<%1 -%rep 18-%1 -%assign n2 n/2 -%assign n4 n/4 -%xdefine list_of_fft list_of_fft, fft %+ n %+ fullsuffix SECTION_REL - -align 16 -fft %+ n %+ fullsuffix: - call fft %+ n2 %+ SUFFIX - add r0, n*4 - (n&(-2<<%1)) - call fft %+ n4 %+ SUFFIX - add r0, n*2 - (n2&(-2<<%1)) - call fft %+ n4 %+ SUFFIX - sub r0, n*6 + (n2&(-2<<%1)) - lea r1, [cos_ %+ n] - mov r2d, n4/2 - jmp pass %+ fullsuffix - -%assign n n*2 -%endrep -%undef n - -align 8 -dispatch_tab %+ fullsuffix: pointer list_of_fft -%endmacro ; DECL_FFT - -%if HAVE_AVX_EXTERNAL -INIT_YMM avx -DECL_FFT 6 -DECL_FFT 6, _interleave -%endif -INIT_XMM sse -DECL_FFT 5 -DECL_FFT 5, _interleave - -INIT_XMM sse -%undef mulps -%undef addps -%undef subps -%undef unpcklps -%undef unpckhps - -%macro PREROTATER 5 ;-2*k, 2*k, input+n4, tcos+n8, tsin+n8 - movaps xmm0, [%3+%2*4] - movaps xmm1, [%3+%1*4-0x10] - movaps xmm2, xmm0 - shufps xmm0, xmm1, 0x88 - shufps xmm1, xmm2, 0x77 - movlps xmm4, [%4+%2*2] - movlps xmm5, [%5+%2*2+0x0] - movhps xmm4, [%4+%1*2-0x8] - movhps xmm5, [%5+%1*2-0x8] - movaps xmm2, xmm0 - movaps xmm3, xmm1 - mulps xmm0, xmm5 - mulps xmm1, xmm4 - mulps xmm2, xmm4 - mulps xmm3, xmm5 - subps xmm1, xmm0 - addps xmm2, xmm3 - movaps xmm0, xmm1 - unpcklps xmm1, xmm2 - unpckhps xmm0, xmm2 -%endmacro - -%macro CMUL 6 ;j, xmm0, xmm1, 3, 4, 5 - mulps m6, %3, [%5+%1] - mulps m7, %2, [%5+%1] - mulps %2, %2, [%6+%1] - mulps %3, %3, [%6+%1] - subps %2, %2, m6 - addps %3, %3, m7 -%endmacro - -%macro POSROTATESHUF 5 ;j, k, z+n8, tcos+n8, tsin+n8 -.post: -%if cpuflag(avx) - vmovaps ymm1, [%3+%1*2] - vmovaps ymm0, [%3+%1*2+0x20] - vmovaps ymm3, [%3+%2*2] - vmovaps ymm2, [%3+%2*2+0x20] - - CMUL %1, ymm0, ymm1, %3, %4, %5 - CMUL %2, ymm2, ymm3, %3, %4, %5 - vshufps ymm1, ymm1, ymm1, 0x1b - vshufps ymm3, ymm3, ymm3, 0x1b - vperm2f128 ymm1, ymm1, ymm1, 0x01 - vperm2f128 ymm3, ymm3, ymm3, 0x01 - vunpcklps ymm6, ymm2, ymm1 - vunpckhps ymm4, ymm2, ymm1 - vunpcklps ymm7, ymm0, ymm3 - vunpckhps ymm5, ymm0, ymm3 - - vextractf128 [%3+%1*2], ymm7, 0 - vextractf128 [%3+%1*2+0x10], ymm5, 0 - vextractf128 [%3+%1*2+0x20], ymm7, 1 - vextractf128 [%3+%1*2+0x30], ymm5, 1 - - vextractf128 [%3+%2*2], ymm6, 0 - vextractf128 [%3+%2*2+0x10], ymm4, 0 - vextractf128 [%3+%2*2+0x20], ymm6, 1 - vextractf128 [%3+%2*2+0x30], ymm4, 1 - sub %2, 0x20 - add %1, 0x20 - jl .post -%else - movaps xmm1, [%3+%1*2] - movaps xmm0, [%3+%1*2+0x10] - CMUL %1, xmm0, xmm1, %3, %4, %5 - movaps xmm5, [%3+%2*2] - movaps xmm4, [%3+%2*2+0x10] - CMUL %2, xmm4, xmm5, %3, %4, %5 - shufps xmm1, xmm1, 0x1b - shufps xmm5, xmm5, 0x1b - movaps xmm6, xmm4 - unpckhps xmm4, xmm1 - unpcklps xmm6, xmm1 - movaps xmm2, xmm0 - unpcklps xmm0, xmm5 - unpckhps xmm2, xmm5 - movaps [%3+%2*2], xmm6 - movaps [%3+%2*2+0x10], xmm4 - movaps [%3+%1*2], xmm0 - movaps [%3+%1*2+0x10], xmm2 - sub %2, 0x10 - add %1, 0x10 - jl .post -%endif -%endmacro - -%macro DECL_IMDCT 0 -cglobal imdct_half, 3,12,8; FFTContext *s, FFTSample *output, const FFTSample *input -%if ARCH_X86_64 -%define rrevtab r7 -%define rtcos r8 -%define rtsin r9 -%else -%define rrevtab r6 -%define rtsin r6 -%define rtcos r5 -%endif - mov r3d, [r0+FFTContext.mdctsize] - add r2, r3 - shr r3, 1 - mov rtcos, [r0+FFTContext.tcos] - mov rtsin, [r0+FFTContext.tsin] - add rtcos, r3 - add rtsin, r3 -%if ARCH_X86_64 == 0 - push rtcos - push rtsin -%endif - shr r3, 1 - mov rrevtab, [r0+FFTContext.revtab] - add rrevtab, r3 -%if ARCH_X86_64 == 0 - push rrevtab -%endif - - sub r3, 4 -%if ARCH_X86_64 - xor r4, r4 - sub r4, r3 -%endif -.pre: -%if ARCH_X86_64 == 0 -;unspill - xor r4, r4 - sub r4, r3 - mov rtcos, [esp+8] - mov rtsin, [esp+4] -%endif - - PREROTATER r4, r3, r2, rtcos, rtsin -%if ARCH_X86_64 - movzx r5, word [rrevtab+r4-4] - movzx r6, word [rrevtab+r4-2] - movzx r10, word [rrevtab+r3] - movzx r11, word [rrevtab+r3+2] - movlps [r1+r5 *8], xmm0 - movhps [r1+r6 *8], xmm0 - movlps [r1+r10*8], xmm1 - movhps [r1+r11*8], xmm1 - add r4, 4 -%else - mov r6, [esp] - movzx r5, word [r6+r4-4] - movzx r4, word [r6+r4-2] - movlps [r1+r5*8], xmm0 - movhps [r1+r4*8], xmm0 - movzx r5, word [r6+r3] - movzx r4, word [r6+r3+2] - movlps [r1+r5*8], xmm1 - movhps [r1+r4*8], xmm1 -%endif - sub r3, 4 - jns .pre - - mov r5, r0 - mov r6, r1 - mov r0, r1 - mov r1d, [r5+FFTContext.nbits] - - FFT_DISPATCH SUFFIX, r1 - - mov r0d, [r5+FFTContext.mdctsize] - add r6, r0 - shr r0, 1 -%if ARCH_X86_64 == 0 -%define rtcos r2 -%define rtsin r3 - mov rtcos, [esp+8] - mov rtsin, [esp+4] -%endif - neg r0 - mov r1, -mmsize - sub r1, r0 - POSROTATESHUF r0, r1, r6, rtcos, rtsin -%if ARCH_X86_64 == 0 - add esp, 12 -%endif - RET -%endmacro - -DECL_IMDCT - -INIT_YMM avx - -%if HAVE_AVX_EXTERNAL -DECL_IMDCT -%endif diff --git a/libavcodec/x86/flacdsp.asm b/libavcodec/x86/flacdsp.asm index 44416e4dfde..4b2fd654352 100644 --- a/libavcodec/x86/flacdsp.asm +++ b/libavcodec/x86/flacdsp.asm @@ -43,6 +43,7 @@ INIT_XMM %1 cglobal flac_lpc_32, 5,6,5, decoded, coeffs, pred_order, qlevel, len, j sub lend, pred_orderd jle .ret + movsxdifnidn pred_orderq, pred_orderd lea decodedq, [decodedq+pred_orderq*4-8] lea coeffsq, [coeffsq+pred_orderq*4] neg pred_orderq diff --git a/libavcodec/x86/fpel.asm b/libavcodec/x86/fpel.asm index ebe8e43750a..b07b7890741 100644 --- a/libavcodec/x86/fpel.asm +++ b/libavcodec/x86/fpel.asm @@ -25,16 +25,6 @@ SECTION .text -%macro PAVGB_MMX 4 - LOAD %3, %1 - por %3, %2 - pxor %2, %1 - pand %2, %4 - psrlq %2, 1 - psubb %3, %2 - SWAP %2, %3 -%endmacro - ; void ff_put/avg_pixels(uint8_t *block, const uint8_t *pixels, ; ptrdiff_t line_size, int h) %macro OP_PIXELS 2 @@ -49,12 +39,6 @@ SECTION .text %endif cglobal %1_pixels%2, 4,5,4 lea r4, [r2*3] -%ifidn %1, avg -%if notcpuflag(mmxext) - pcmpeqd m6, m6 - paddb m6, m6 -%endif -%endif .loop: %assign %%i 0 %rep LEN/mmsize @@ -63,17 +47,10 @@ cglobal %1_pixels%2, 4,5,4 LOAD m2, [r1+r2*2 + %%i] LOAD m3, [r1+r4 + %%i] %ifidn %1, avg -%if notcpuflag(mmxext) - PAVGB_MMX [r0 + %%i], m0, m4, m6 - PAVGB_MMX [r0+r2 + %%i], m1, m5, m6 - PAVGB_MMX [r0+r2*2 + %%i], m2, m4, m6 - PAVGB_MMX [r0+r4 + %%i], m3, m5, m6 -%else pavgb m0, [r0 + %%i] pavgb m1, [r0+r2 + %%i] pavgb m2, [r0+r2*2 + %%i] pavgb m3, [r0+r4 + %%i] -%endif %endif SAVE [r0 + %%i], m0 SAVE [r0+r2 + %%i], m1 @@ -89,10 +66,8 @@ cglobal %1_pixels%2, 4,5,4 %endmacro INIT_MMX mmx -OP_PIXELS put, 4 OP_PIXELS put, 8 OP_PIXELS put, 16 -OP_PIXELS avg, 16 INIT_MMX mmxext OP_PIXELS avg, 4 diff --git a/libavcodec/x86/fpel.h b/libavcodec/x86/fpel.h index 4e83cf71c30..47ffc8eec76 100644 --- a/libavcodec/x86/fpel.h +++ b/libavcodec/x86/fpel.h @@ -22,22 +22,14 @@ #include #include -void ff_avg_pixels4_mmx(uint8_t *block, const uint8_t *pixels, - ptrdiff_t line_size, int h); void ff_avg_pixels4_mmxext(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h); -void ff_avg_pixels8_mmx(uint8_t *block, const uint8_t *pixels, - ptrdiff_t line_size, int h); void ff_avg_pixels8_mmxext(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h); -void ff_avg_pixels16_mmx(uint8_t *block, const uint8_t *pixels, - ptrdiff_t line_size, int h); void ff_avg_pixels16_mmxext(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h); void ff_avg_pixels16_sse2(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h); -void ff_put_pixels4_mmx(uint8_t *block, const uint8_t *pixels, - ptrdiff_t line_size, int h); void ff_put_pixels8_mmx(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h); void ff_put_pixels16_mmx(uint8_t *block, const uint8_t *pixels, diff --git a/libavcodec/x86/h264_chromamc.asm b/libavcodec/x86/h264_chromamc.asm index e70bc492b2d..ec6288d48e8 100644 --- a/libavcodec/x86/h264_chromamc.asm +++ b/libavcodec/x86/h264_chromamc.asm @@ -91,7 +91,7 @@ SECTION .text %macro chroma_mc8_mmx_func 2-3 %ifidn %2, rv40 -%ifdef PIC +%if PIC %define rnd_1d_rv40 r8 %define rnd_2d_rv40 r8 %define extra_regs 2 @@ -147,7 +147,7 @@ cglobal %1_%2_chroma_mc8%3, 6, 7 + extra_regs, 0 or r4d, r5d ; x + y %ifidn %2, rv40 -%ifdef PIC +%if PIC lea r8, [rnd_rv40_1d_tbl] %endif %if ARCH_X86_64 == 0 @@ -198,7 +198,7 @@ cglobal %1_%2_chroma_mc8%3, 6, 7 + extra_regs, 0 movd m4, r4d ; x movd m6, r5d ; y %ifidn %2, rv40 -%ifdef PIC +%if PIC lea r8, [rnd_rv40_2d_tbl] %endif %if ARCH_X86_64 == 0 @@ -283,7 +283,7 @@ cglobal %1_%2_chroma_mc8%3, 6, 7 + extra_regs, 0 %macro chroma_mc4_mmx_func 2 %define extra_regs 0 %ifidn %2, rv40 -%ifdef PIC +%if PIC %define extra_regs 1 %endif ; PIC %endif ; rv40 @@ -301,7 +301,7 @@ cglobal %1_%2_chroma_mc4, 6, 6 + extra_regs, 0 psubw m5, m3 %ifidn %2, rv40 -%ifdef PIC +%if PIC lea r6, [rnd_rv40_2d_tbl] %define rnd_2d_rv40 r6 %else diff --git a/libavcodec/x86/h264_idct.asm b/libavcodec/x86/h264_idct.asm index 1f86e51d82d..d9c3c9c8628 100644 --- a/libavcodec/x86/h264_idct.asm +++ b/libavcodec/x86/h264_idct.asm @@ -42,7 +42,7 @@ scan8_mem: db 4+ 1*8, 5+ 1*8, 4+ 2*8, 5+ 2*8 db 6+11*8, 7+11*8, 6+12*8, 7+12*8 db 4+13*8, 5+13*8, 4+14*8, 5+14*8 db 6+13*8, 7+13*8, 6+14*8, 7+14*8 -%ifdef PIC +%if PIC %define npicregs 1 %define scan8 picregq %else @@ -322,7 +322,7 @@ INIT_XMM sse2 cglobal h264_idct8_add4_8, 5, 8 + npicregs, 10, dst1, block_offset, block, stride, nnzc, cntr, coeff, dst2, picreg movsxdifnidn r3, r3d xor r5, r5 -%ifdef PIC +%if PIC lea picregq, [scan8_mem] %endif .nextblock: @@ -398,7 +398,7 @@ h264_idct_add8_mmx_plane: cglobal h264_idct_add8_422_8, 5, 8 + npicregs, 0, dst1, block_offset, block, stride, nnzc, cntr, coeff, dst2, picreg ; dst1, block_offset, block, stride, nnzc, cntr, coeff, dst2, picreg movsxdifnidn r3, r3d -%ifdef PIC +%if PIC lea picregq, [scan8_mem] %endif %if ARCH_X86_64 @@ -716,11 +716,9 @@ RET %endif %endmacro -%macro IDCT_DC_DEQUANT 1 -cglobal h264_luma_dc_dequant_idct, 3, 4, %1 - ; manually spill XMM registers for Win64 because - ; the code here is initialized with INIT_MMX - WIN64_SPILL_XMM %1 +INIT_XMM sse2 +cglobal h264_luma_dc_dequant_idct, 3, 4, 7 +INIT_MMX cpuname movq m3, [r1+24] movq m2, [r1+16] movq m1, [r1+ 8] @@ -757,10 +755,6 @@ cglobal h264_luma_dc_dequant_idct, 3, 4, %1 movd xmm6, t1d DEQUANT_STORE xmm6 RET -%endmacro - -INIT_MMX sse2 -IDCT_DC_DEQUANT 7 %ifdef __NASM_VER__ %if __NASM_MAJOR__ >= 2 && __NASM_MINOR__ >= 4 diff --git a/libavcodec/x86/h264_intrapred.asm b/libavcodec/x86/h264_intrapred.asm index 8a38ba2bb5d..a8a630dbe6c 100644 --- a/libavcodec/x86/h264_intrapred.asm +++ b/libavcodec/x86/h264_intrapred.asm @@ -86,8 +86,6 @@ cglobal pred16x16_horizontal_8, 2,3 punpcklbw m1, m1 SPLATW m0, m0, 3 SPLATW m1, m1, 3 - mova [r0+r1*0+8], m0 - mova [r0+r1*1+8], m1 %endif mova [r0+r1*0], m0 @@ -98,7 +96,7 @@ cglobal pred16x16_horizontal_8, 2,3 RET %endmacro -INIT_MMX mmxext +INIT_XMM sse2 PRED16x16_H INIT_XMM ssse3 PRED16x16_H @@ -568,17 +566,17 @@ H264_PRED8x8_PLANE ; void ff_pred8x8_vertical_8(uint8_t *src, ptrdiff_t stride) ;----------------------------------------------------------------------------- -INIT_MMX mmx +INIT_XMM sse2 cglobal pred8x8_vertical_8, 2,2 sub r0, r1 - movq mm0, [r0] + movq m0, [r0] %rep 3 - movq [r0+r1*1], mm0 - movq [r0+r1*2], mm0 + movq [r0+r1*1], m0 + movq [r0+r1*2], m0 lea r0, [r0+r1*2] %endrep - movq [r0+r1*1], mm0 - movq [r0+r1*2], mm0 + movq [r0+r1*1], m0 + movq [r0+r1*2], m0 RET ;----------------------------------------------------------------------------- @@ -1313,10 +1311,7 @@ PRED8x8L_DOWN_RIGHT ;----------------------------------------------------------------------------- %macro PRED8x8L_VERTICAL_RIGHT 0 -cglobal pred8x8l_vertical_right_8, 4,5,7 - ; manually spill XMM registers for Win64 because - ; the code here is initialized with INIT_MMX - WIN64_SPILL_XMM 7 +cglobal pred8x8l_vertical_right_8, 4,5,6 sub r0, r3 lea r4, [r0+r3*2] movq mm0, [r0+r3*1-8] @@ -1386,7 +1381,6 @@ cglobal pred8x8l_vertical_right_8, 4,5,7 movq2dq xmm4, mm6 pslldq xmm4, 8 por xmm0, xmm4 - movdqa xmm6, [pw_ff00] movdqa xmm1, xmm0 lea r2, [r1+r3*2] movdqa xmm2, xmm0 @@ -1396,15 +1390,16 @@ cglobal pred8x8l_vertical_right_8, 4,5,7 pavgb xmm2, xmm0 INIT_XMM cpuname PRED4x4_LOWPASS xmm4, xmm3, xmm1, xmm0, xmm5 - pandn xmm6, xmm4 + movdqa xmm0, [pw_ff00] + pandn xmm0, xmm4 movdqa xmm5, xmm4 psrlw xmm4, 8 - packuswb xmm6, xmm4 - movhlps xmm4, xmm6 + packuswb xmm0, xmm4 + movhlps xmm4, xmm0 movhps [r0+r3*2], xmm5 movhps [r0+r3*1], xmm2 psrldq xmm5, 4 - movss xmm5, xmm6 + movss xmm5, xmm0 psrldq xmm2, 4 movss xmm2, xmm4 lea r0, [r2+r3*2] diff --git a/libavcodec/x86/h264_intrapred_init.c b/libavcodec/x86/h264_intrapred_init.c index ee46927a24c..aa9bc721f04 100644 --- a/libavcodec/x86/h264_intrapred_init.c +++ b/libavcodec/x86/h264_intrapred_init.c @@ -100,7 +100,7 @@ PRED16x16(horizontal, 10, sse2) /* 8-bit versions */ PRED16x16(vertical, 8, sse) -PRED16x16(horizontal, 8, mmxext) +PRED16x16(horizontal, 8, sse2) PRED16x16(horizontal, 8, ssse3) PRED16x16(dc, 8, sse2) PRED16x16(dc, 8, ssse3) @@ -116,7 +116,7 @@ PRED16x16(tm_vp8, 8, avx2) PRED8x8(top_dc, 8, mmxext) PRED8x8(dc_rv40, 8, mmxext) PRED8x8(dc, 8, mmxext) -PRED8x8(vertical, 8, mmx) +PRED8x8(vertical, 8, sse2) PRED8x8(horizontal, 8, mmxext) PRED8x8(horizontal, 8, ssse3) PRED8x8(plane, 8, sse2) @@ -163,14 +163,7 @@ av_cold void ff_h264_pred_init_x86(H264PredContext *h, int codec_id, int cpu_flags = av_get_cpu_flags(); if (bit_depth == 8) { - if (EXTERNAL_MMX(cpu_flags)) { - if (chroma_format_idc <= 1) { - h->pred8x8 [VERT_PRED8x8 ] = ff_pred8x8_vertical_8_mmx; - } - } - if (EXTERNAL_MMXEXT(cpu_flags)) { - h->pred16x16[HOR_PRED8x8 ] = ff_pred16x16_horizontal_8_mmxext; if (chroma_format_idc <= 1) h->pred8x8[HOR_PRED8x8 ] = ff_pred8x8_horizontal_8_mmxext; h->pred8x8l [TOP_DC_PRED ] = ff_pred8x8l_top_dc_8_mmxext; @@ -210,12 +203,15 @@ av_cold void ff_h264_pred_init_x86(H264PredContext *h, int codec_id, } if (EXTERNAL_SSE2(cpu_flags)) { + h->pred16x16[HOR_PRED8x8 ] = ff_pred16x16_horizontal_8_sse2; h->pred16x16[DC_PRED8x8 ] = ff_pred16x16_dc_8_sse2; h->pred8x8l [DIAG_DOWN_LEFT_PRED ] = ff_pred8x8l_down_left_8_sse2; h->pred8x8l [DIAG_DOWN_RIGHT_PRED ] = ff_pred8x8l_down_right_8_sse2; h->pred8x8l [VERT_RIGHT_PRED ] = ff_pred8x8l_vertical_right_8_sse2; h->pred8x8l [VERT_LEFT_PRED ] = ff_pred8x8l_vertical_left_8_sse2; h->pred8x8l [HOR_DOWN_PRED ] = ff_pred8x8l_horizontal_down_8_sse2; + if (chroma_format_idc <= 1) + h->pred8x8 [VERT_PRED8x8 ] = ff_pred8x8_vertical_8_sse2; if (codec_id == AV_CODEC_ID_VP7 || codec_id == AV_CODEC_ID_VP8) { h->pred16x16[PLANE_PRED8x8 ] = ff_pred16x16_tm_vp8_8_sse2; h->pred8x8 [PLANE_PRED8x8 ] = ff_pred8x8_tm_vp8_8_sse2; diff --git a/libavcodec/x86/h264_qpel.c b/libavcodec/x86/h264_qpel.c index 2df4c11f82d..d69ccda89c6 100644 --- a/libavcodec/x86/h264_qpel.c +++ b/libavcodec/x86/h264_qpel.c @@ -47,8 +47,8 @@ void ff_avg_pixels16_l2_mmxext(uint8_t *dst, const uint8_t *src1, const uint8_t #define ff_put_pixels16_l2_sse2 ff_put_pixels16_l2_mmxext #define ff_avg_pixels16_l2_sse2 ff_avg_pixels16_l2_mmxext #define ff_put_pixels16_mmxext ff_put_pixels16_mmx -#define ff_put_pixels8_mmxext ff_put_pixels8_mmx -#define ff_put_pixels4_mmxext ff_put_pixels4_mmx +#define ff_put_pixels8_mmxext(...) +#define ff_put_pixels4_mmxext(...) #define DEF_QPEL(OPNAME)\ void ff_ ## OPNAME ## _h264_qpel4_h_lowpass_mmxext(uint8_t *dst, const uint8_t *src, int dstStride, int srcStride);\ @@ -217,11 +217,10 @@ static void avg_h264_qpel16_mc00_sse2 (uint8_t *dst, const uint8_t *src, { ff_avg_pixels16_sse2(dst, src, stride, 16); } -#define put_h264_qpel8_mc00_sse2 put_h264_qpel8_mc00_mmxext #define avg_h264_qpel8_mc00_sse2 avg_h264_qpel8_mc00_mmxext #define H264_MC_C(OPNAME, SIZE, MMX, ALIGN) \ -static void OPNAME ## h264_qpel ## SIZE ## _mc00_ ## MMX (uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ +static void av_unused OPNAME ## h264_qpel ## SIZE ## _mc00_ ## MMX (uint8_t *dst, const uint8_t *src, ptrdiff_t stride)\ {\ ff_ ## OPNAME ## pixels ## SIZE ## _ ## MMX(dst, src, stride, SIZE);\ }\ @@ -424,16 +423,20 @@ LUMA_MC_816(10, mc33, sse2) #endif /* HAVE_X86ASM */ -#define SET_QPEL_FUNCS0123(PFX, IDX, SIZE, CPU, PREFIX) \ +#define SET_QPEL_FUNCS123(PFX, IDX, SIZE, CPU, PREFIX) \ do { \ - c->PFX ## _pixels_tab[IDX][ 0] = PREFIX ## PFX ## SIZE ## _mc00_ ## CPU; \ c->PFX ## _pixels_tab[IDX][ 1] = PREFIX ## PFX ## SIZE ## _mc10_ ## CPU; \ c->PFX ## _pixels_tab[IDX][ 2] = PREFIX ## PFX ## SIZE ## _mc20_ ## CPU; \ c->PFX ## _pixels_tab[IDX][ 3] = PREFIX ## PFX ## SIZE ## _mc30_ ## CPU; \ } while (0) -#define SET_QPEL_FUNCS(PFX, IDX, SIZE, CPU, PREFIX) \ +#define SET_QPEL_FUNCS0123(PFX, IDX, SIZE, CPU, PREFIX) \ do { \ - SET_QPEL_FUNCS0123(PFX, IDX, SIZE, CPU, PREFIX); \ + c->PFX ## _pixels_tab[IDX][ 0] = PREFIX ## PFX ## SIZE ## _mc00_ ## CPU; \ + SET_QPEL_FUNCS123(PFX, IDX, SIZE, CPU, PREFIX); \ + } while (0) +#define SET_QPEL_FUNCS_1PP(PFX, IDX, SIZE, CPU, PREFIX) \ + do { \ + SET_QPEL_FUNCS123(PFX, IDX, SIZE, CPU, PREFIX); \ c->PFX ## _pixels_tab[IDX][ 4] = PREFIX ## PFX ## SIZE ## _mc01_ ## CPU; \ c->PFX ## _pixels_tab[IDX][ 5] = PREFIX ## PFX ## SIZE ## _mc11_ ## CPU; \ c->PFX ## _pixels_tab[IDX][ 6] = PREFIX ## PFX ## SIZE ## _mc21_ ## CPU; \ @@ -447,6 +450,11 @@ LUMA_MC_816(10, mc33, sse2) c->PFX ## _pixels_tab[IDX][14] = PREFIX ## PFX ## SIZE ## _mc23_ ## CPU; \ c->PFX ## _pixels_tab[IDX][15] = PREFIX ## PFX ## SIZE ## _mc33_ ## CPU; \ } while (0) +#define SET_QPEL_FUNCS(PFX, IDX, SIZE, CPU, PREFIX) \ + do { \ + c->PFX ## _pixels_tab[IDX][ 0] = PREFIX ## PFX ## SIZE ## _mc00_ ## CPU; \ + SET_QPEL_FUNCS_1PP(PFX, IDX, SIZE, CPU, PREFIX); \ + } while (0) #define H264_QPEL_FUNCS(x, y, CPU) \ do { \ @@ -473,8 +481,8 @@ av_cold void ff_h264qpel_init_x86(H264QpelContext *c, int bit_depth) if (EXTERNAL_MMXEXT(cpu_flags)) { if (!high_bit_depth) { SET_QPEL_FUNCS0123(put_h264_qpel, 0, 16, mmxext, ); - SET_QPEL_FUNCS0123(put_h264_qpel, 1, 8, mmxext, ); - SET_QPEL_FUNCS(put_h264_qpel, 2, 4, mmxext, ); + SET_QPEL_FUNCS123 (put_h264_qpel, 1, 8, mmxext, ); + SET_QPEL_FUNCS_1PP(put_h264_qpel, 2, 4, mmxext, ); SET_QPEL_FUNCS0123(avg_h264_qpel, 0, 16, mmxext, ); SET_QPEL_FUNCS0123(avg_h264_qpel, 1, 8, mmxext, ); SET_QPEL_FUNCS(avg_h264_qpel, 2, 4, mmxext, ); @@ -513,7 +521,9 @@ av_cold void ff_h264qpel_init_x86(H264QpelContext *c, int bit_depth) if (EXTERNAL_SSE2_FAST(cpu_flags)) { if (!high_bit_depth) { - H264_QPEL_FUNCS(0, 0, sse2); + c->put_h264_qpel_pixels_tab[0][0] = put_h264_qpel16_mc00_sse2; + c->avg_h264_qpel_pixels_tab[0][0] = avg_h264_qpel16_mc00_sse2; + c->avg_h264_qpel_pixels_tab[1][0] = avg_h264_qpel8_mc00_sse2; } } diff --git a/libavcodec/x86/h26x/h2656_inter.asm b/libavcodec/x86/h26x/h2656_inter.asm new file mode 100644 index 00000000000..cbba0c1ea53 --- /dev/null +++ b/libavcodec/x86/h26x/h2656_inter.asm @@ -0,0 +1,1143 @@ +; /* +; * Provide SSE luma and chroma mc functions for HEVC/VVC decoding +; * Copyright (c) 2013 Pierre-Edouard LEPERE +; * Copyright (c) 2023-2024 Nuo Mi +; * Copyright (c) 2023-2024 Wu Jianhua +; * +; * 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/x86/x86util.asm" + +SECTION_RODATA 32 +cextern pw_255 +cextern pw_512 +cextern pw_2048 +cextern pw_1023 +cextern pw_1024 +cextern pw_4096 +cextern pw_8192 +%define scale_8 pw_512 +%define scale_10 pw_2048 +%define scale_12 pw_8192 +%define max_pixels_8 pw_255 +%define max_pixels_10 pw_1023 +max_pixels_12: times 16 dw ((1 << 12)-1) +cextern pb_0 + +SECTION .text +%macro SIMPLE_LOAD 4 ;width, bitd, tab, r1 +%if %1 == 2 || (%2 == 8 && %1 <= 4) + movd %4, [%3] ; load data from source +%elif %1 == 4 || (%2 == 8 && %1 <= 8) + movq %4, [%3] ; load data from source +%elif notcpuflag(avx) + movu %4, [%3] ; load data from source +%elif %1 <= 8 || (%2 == 8 && %1 <= 16) + movdqu %4, [%3] +%else + movu %4, [%3] +%endif +%endmacro + +%macro VPBROADCASTW 2 +%if notcpuflag(avx2) + movd %1, %2 + pshuflw %1, %1, 0 + punpcklwd %1, %1 +%else + vpbroadcastw %1, %2 +%endif +%endmacro + +%macro MC_4TAP_FILTER 4 ; bitdepth, filter, a, b, + VPBROADCASTW %3, [%2q + 0 * 2] ; coeff 0, 1 + VPBROADCASTW %4, [%2q + 1 * 2] ; coeff 2, 3 +%if %1 != 8 + pmovsxbw %3, xmm%3 + pmovsxbw %4, xmm%4 +%endif +%endmacro + +%macro MC_4TAP_HV_FILTER 1 + VPBROADCASTW m12, [vfq + 0 * 2] ; vf 0, 1 + VPBROADCASTW m13, [vfq + 1 * 2] ; vf 2, 3 + VPBROADCASTW m14, [hfq + 0 * 2] ; hf 0, 1 + VPBROADCASTW m15, [hfq + 1 * 2] ; hf 2, 3 + + pmovsxbw m12, xm12 + pmovsxbw m13, xm13 +%if %1 != 8 + pmovsxbw m14, xm14 + pmovsxbw m15, xm15 +%endif + lea r3srcq, [srcstrideq*3] +%endmacro + +%macro MC_8TAP_SAVE_FILTER 5 ;offset, mm registers + mova [rsp + %1 + 0*mmsize], %2 + mova [rsp + %1 + 1*mmsize], %3 + mova [rsp + %1 + 2*mmsize], %4 + mova [rsp + %1 + 3*mmsize], %5 +%endmacro + +%macro MC_8TAP_FILTER 2-3 ;bitdepth, filter, offset + VPBROADCASTW m12, [%2q + 0 * 2] ; coeff 0, 1 + VPBROADCASTW m13, [%2q + 1 * 2] ; coeff 2, 3 + VPBROADCASTW m14, [%2q + 2 * 2] ; coeff 4, 5 + VPBROADCASTW m15, [%2q + 3 * 2] ; coeff 6, 7 +%if %0 == 3 + MC_8TAP_SAVE_FILTER %3, m12, m13, m14, m15 +%endif + +%if %1 != 8 + pmovsxbw m12, xm12 + pmovsxbw m13, xm13 + pmovsxbw m14, xm14 + pmovsxbw m15, xm15 + %if %0 == 3 + MC_8TAP_SAVE_FILTER %3 + 4*mmsize, m12, m13, m14, m15 + %endif +%elif %0 == 3 + pmovsxbw m8, xm12 + pmovsxbw m9, xm13 + pmovsxbw m10, xm14 + pmovsxbw m11, xm15 + MC_8TAP_SAVE_FILTER %3 + 4*mmsize, m8, m9, m10, m11 +%endif + +%endmacro + +%macro MC_4TAP_LOAD 4 +%if (%1 == 8 && %4 <= 4) +%define %%load movd +%elif (%1 == 8 && %4 <= 8) || (%1 > 8 && %4 <= 4) +%define %%load movq +%else +%define %%load movdqu +%endif + + %%load m0, [%2q ] +%ifnum %3 + %%load m1, [%2q+ %3] + %%load m2, [%2q+2*%3] + %%load m3, [%2q+3*%3] +%else + %%load m1, [%2q+ %3q] + %%load m2, [%2q+2*%3q] + %%load m3, [%2q+r3srcq] +%endif +%if %1 == 8 +%if %4 > 8 + SBUTTERFLY bw, 0, 1, 7 + SBUTTERFLY bw, 2, 3, 7 +%else + punpcklbw m0, m1 + punpcklbw m2, m3 +%endif +%else +%if %4 > 4 + SBUTTERFLY wd, 0, 1, 7 + SBUTTERFLY wd, 2, 3, 7 +%else + punpcklwd m0, m1 + punpcklwd m2, m3 +%endif +%endif +%endmacro + +%macro MC_8TAP_H_LOAD 4 +%assign %%stride (%1+7)/8 +%if %1 == 8 +%if %3 <= 4 +%define %%load movd +%elif %3 == 8 +%define %%load movq +%else +%define %%load movu +%endif +%else +%if %3 == 2 +%define %%load movd +%elif %3 == 4 +%define %%load movq +%else +%define %%load movu +%endif +%endif + %%load m0, [%2-3*%%stride] ;load data from source + %%load m1, [%2-2*%%stride] + %%load m2, [%2-%%stride ] + %%load m3, [%2 ] + %%load m4, [%2+%%stride ] + %%load m5, [%2+2*%%stride] + %%load m6, [%2+3*%%stride] + %%load m7, [%2+4*%%stride] + +%if %1 == 8 +%if %3 > 8 + SBUTTERFLY wd, 0, 1, %4 + SBUTTERFLY wd, 2, 3, %4 + SBUTTERFLY wd, 4, 5, %4 + SBUTTERFLY wd, 6, 7, %4 +%else + punpcklbw m0, m1 + punpcklbw m2, m3 + punpcklbw m4, m5 + punpcklbw m6, m7 +%endif +%else +%if %3 > 4 + SBUTTERFLY dq, 0, 1, %4 + SBUTTERFLY dq, 2, 3, %4 + SBUTTERFLY dq, 4, 5, %4 + SBUTTERFLY dq, 6, 7, %4 +%else + punpcklwd m0, m1 + punpcklwd m2, m3 + punpcklwd m4, m5 + punpcklwd m6, m7 +%endif +%endif +%endmacro + +%macro MC_8TAP_V_LOAD 5 + lea %5q, [%2] + sub %5q, r3srcq + movu m0, [%5q ] ;load x- 3*srcstride + movu m1, [%5q+ %3q ] ;load x- 2*srcstride + movu m2, [%5q+ 2*%3q ] ;load x-srcstride + movu m3, [%2 ] ;load x + movu m4, [%2+ %3q] ;load x+stride + movu m5, [%2+ 2*%3q] ;load x+2*stride + movu m6, [%2+r3srcq] ;load x+3*stride + movu m7, [%2+ 4*%3q] ;load x+4*stride +%if %1 == 8 +%if %4 > 8 + SBUTTERFLY bw, 0, 1, 8 + SBUTTERFLY bw, 2, 3, 8 + SBUTTERFLY bw, 4, 5, 8 + SBUTTERFLY bw, 6, 7, 8 +%else + punpcklbw m0, m1 + punpcklbw m2, m3 + punpcklbw m4, m5 + punpcklbw m6, m7 +%endif +%else +%if %4 > 4 + SBUTTERFLY wd, 0, 1, 8 + SBUTTERFLY wd, 2, 3, 8 + SBUTTERFLY wd, 4, 5, 8 + SBUTTERFLY wd, 6, 7, 8 +%else + punpcklwd m0, m1 + punpcklwd m2, m3 + punpcklwd m4, m5 + punpcklwd m6, m7 +%endif +%endif +%endmacro + +%macro PEL_12STORE2 3 + movd [%1], %2 +%endmacro +%macro PEL_12STORE4 3 + movq [%1], %2 +%endmacro +%macro PEL_12STORE6 3 + movq [%1], %2 + psrldq %2, 8 + movd [%1+8], %2 +%endmacro +%macro PEL_12STORE8 3 + movdqu [%1], %2 +%endmacro +%macro PEL_12STORE12 3 + PEL_12STORE8 %1, %2, %3 + movq [%1+16], %3 +%endmacro +%macro PEL_12STORE16 3 +%if cpuflag(avx2) + movu [%1], %2 +%else + PEL_12STORE8 %1, %2, %3 + movdqu [%1+16], %3 +%endif +%endmacro + +%macro PEL_10STORE2 3 + movd [%1], %2 +%endmacro +%macro PEL_10STORE4 3 + movq [%1], %2 +%endmacro +%macro PEL_10STORE6 3 + movq [%1], %2 + psrldq %2, 8 + movd [%1+8], %2 +%endmacro +%macro PEL_10STORE8 3 + movdqu [%1], %2 +%endmacro +%macro PEL_10STORE12 3 + PEL_10STORE8 %1, %2, %3 + movq [%1+16], %3 +%endmacro +%macro PEL_10STORE16 3 +%if cpuflag(avx2) + movu [%1], %2 +%else + PEL_10STORE8 %1, %2, %3 + movdqu [%1+16], %3 +%endif +%endmacro +%macro PEL_10STORE32 3 + PEL_10STORE16 %1, %2, %3 + movu [%1+32], %3 +%endmacro + +%macro PEL_8STORE2 3 + pextrw [%1], %2, 0 +%endmacro +%macro PEL_8STORE4 3 + movd [%1], %2 +%endmacro +%macro PEL_8STORE6 3 + movd [%1], %2 + pextrw [%1+4], %2, 2 +%endmacro +%macro PEL_8STORE8 3 + movq [%1], %2 +%endmacro +%macro PEL_8STORE12 3 + movq [%1], %2 + psrldq %2, 8 + movd [%1+8], %2 +%endmacro +%macro PEL_8STORE16 3 +%if cpuflag(avx2) + movdqu [%1], %2 +%else + movu [%1], %2 +%endif ; avx +%endmacro +%macro PEL_8STORE32 3 + movu [%1], %2 +%endmacro + +%macro LOOP_END 3 + add %1q, dststrideq ; dst += dststride + add %2q, %3q ; src += srcstride + dec heightd ; cmp height + jnz .loop ; height loop +%endmacro + + +%macro MC_PIXEL_COMPUTE 2-3 ;width, bitdepth +%if %2 == 8 +%if cpuflag(avx2) && %0 ==3 +%if %1 > 16 + vextracti128 xm1, m0, 1 + pmovzxbw m1, xm1 + psllw m1, 14-%2 +%endif + pmovzxbw m0, xm0 +%else ; not avx +%if %1 > 8 + punpckhbw m1, m0, m2 + psllw m1, 14-%2 +%endif + punpcklbw m0, m2 +%endif +%endif ;avx + psllw m0, 14-%2 +%endmacro + +%macro MC_4TAP_COMPUTE 4-8 ; bitdepth, width, filter1, filter2, HV/m0, m2, m1, m3 +%if %0 == 8 +%define %%reg0 %5 +%define %%reg2 %6 +%define %%reg1 %7 +%define %%reg3 %8 +%else +%define %%reg0 m0 +%define %%reg2 m2 +%define %%reg1 m1 +%define %%reg3 m3 +%endif +%if %1 == 8 +%if cpuflag(avx2) && (%0 == 5) +%if %2 > 16 + vperm2i128 m10, m0, m1, q0301 +%endif + vinserti128 m0, m0, xm1, 1 + mova m1, m10 +%if %2 > 16 + vperm2i128 m10, m2, m3, q0301 +%endif + vinserti128 m2, m2, xm3, 1 + mova m3, m10 +%endif + pmaddubsw %%reg0, %3 ;x1*c1+x2*c2 + pmaddubsw %%reg2, %4 ;x3*c3+x4*c4 + paddw %%reg0, %%reg2 +%if %2 > 8 + pmaddubsw %%reg1, %3 + pmaddubsw %%reg3, %4 + paddw %%reg1, %%reg3 +%endif +%else + pmaddwd %%reg0, %3 + pmaddwd %%reg2, %4 + paddd %%reg0, %%reg2 +%if %2 > 4 + pmaddwd %%reg1, %3 + pmaddwd %%reg3, %4 + paddd %%reg1, %%reg3 +%if %1 != 8 + psrad %%reg1, %1-8 +%endif +%endif +%if %1 != 8 + psrad %%reg0, %1-8 +%endif + packssdw %%reg0, %%reg1 +%endif +%endmacro + +%macro MC_8TAP_HV_COMPUTE 4 ; width, bitdepth, filter + +%if %2 == 8 + pmaddubsw m0, [%3q+0*mmsize] ;x1*c1+x2*c2 + pmaddubsw m2, [%3q+1*mmsize] ;x3*c3+x4*c4 + pmaddubsw m4, [%3q+2*mmsize] ;x5*c5+x6*c6 + pmaddubsw m6, [%3q+3*mmsize] ;x7*c7+x8*c8 + paddw m0, m2 + paddw m4, m6 + paddw m0, m4 +%else + pmaddwd m0, [%3q+4*mmsize] + pmaddwd m2, [%3q+5*mmsize] + pmaddwd m4, [%3q+6*mmsize] + pmaddwd m6, [%3q+7*mmsize] + paddd m0, m2 + paddd m4, m6 + paddd m0, m4 +%if %2 != 8 + psrad m0, %2-8 +%endif +%if %1 > 4 + pmaddwd m1, [%3q+4*mmsize] + pmaddwd m3, [%3q+5*mmsize] + pmaddwd m5, [%3q+6*mmsize] + pmaddwd m7, [%3q+7*mmsize] + paddd m1, m3 + paddd m5, m7 + paddd m1, m5 +%if %2 != 8 + psrad m1, %2-8 +%endif +%endif + p%4 m0, m1 +%endif +%endmacro + + +%macro MC_8TAP_COMPUTE 2-3 ; width, bitdepth +%if %2 == 8 +%if cpuflag(avx2) && (%0 == 3) + + vperm2i128 m10, m0, m1, q0301 + vinserti128 m0, m0, xm1, 1 + SWAP 1, 10 + + vperm2i128 m10, m2, m3, q0301 + vinserti128 m2, m2, xm3, 1 + SWAP 3, 10 + + + vperm2i128 m10, m4, m5, q0301 + vinserti128 m4, m4, xm5, 1 + SWAP 5, 10 + + vperm2i128 m10, m6, m7, q0301 + vinserti128 m6, m6, xm7, 1 + SWAP 7, 10 +%endif + + pmaddubsw m0, m12 ;x1*c1+x2*c2 + pmaddubsw m2, m13 ;x3*c3+x4*c4 + pmaddubsw m4, m14 ;x5*c5+x6*c6 + pmaddubsw m6, m15 ;x7*c7+x8*c8 + paddw m0, m2 + paddw m4, m6 + paddw m0, m4 +%if %1 > 8 + pmaddubsw m1, m12 + pmaddubsw m3, m13 + pmaddubsw m5, m14 + pmaddubsw m7, m15 + paddw m1, m3 + paddw m5, m7 + paddw m1, m5 +%endif +%else + pmaddwd m0, m12 + pmaddwd m2, m13 + pmaddwd m4, m14 + pmaddwd m6, m15 + paddd m0, m2 + paddd m4, m6 + paddd m0, m4 +%if %2 != 8 + psrad m0, %2-8 +%endif +%if %1 > 4 + pmaddwd m1, m12 + pmaddwd m3, m13 + pmaddwd m5, m14 + pmaddwd m7, m15 + paddd m1, m3 + paddd m5, m7 + paddd m1, m5 +%if %2 != 8 + psrad m1, %2-8 +%endif +%endif +%endif +%endmacro +%macro UNI_COMPUTE 5 + pmulhrsw %3, %5 +%if %1 > 8 || (%2 > 8 && %1 > 4) + pmulhrsw %4, %5 +%endif +%if %2 == 8 + packuswb %3, %4 +%else + CLIPW %3, [pb_0], [max_pixels_%2] +%if (%1 > 8 && notcpuflag(avx)) || %1 > 16 + CLIPW %4, [pb_0], [max_pixels_%2] +%endif +%endif +%endmacro + + +; ****************************** +; void %1_put_pixels(int16_t *dst, ptrdiff_t dststride, const uint8_t *_src, ptrdiff_t srcstride, +; int height, const int8_t *hf, const int8_t *vf, int width) +; ****************************** + +%macro PUT_PIXELS 3 + MC_PIXELS %1, %2, %3 + MC_UNI_PIXELS %1, %2, %3 +%endmacro + +%macro MC_PIXELS 3 +cglobal %1_put_pixels%2_%3, 5, 5, 3, dst, dststride, src, srcstride, height + pxor m2, m2 +.loop: + SIMPLE_LOAD %2, %3, srcq, m0 + MC_PIXEL_COMPUTE %2, %3, 1 + PEL_10STORE%2 dstq, m0, m1 + LOOP_END dst, src, srcstride + RET +%endmacro + +%macro MC_UNI_PIXELS 3 +cglobal %1_put_uni_pixels%2_%3, 5, 5, 2, dst, dststride, src, srcstride, height +.loop: + SIMPLE_LOAD %2, %3, srcq, m0 + PEL_%3STORE%2 dstq, m0, m1 + add dstq, dststrideq ; dst += dststride + add srcq, srcstrideq ; src += srcstride + dec heightd ; cmp height + jnz .loop ; height loop + RET +%endmacro + +%macro PUT_4TAP 3 +%if cpuflag(avx2) +%define XMM_REGS 11 +%else +%define XMM_REGS 8 +%endif + +; ****************************** +; void %1_put_4tap_hX(int16_t *dst, ptrdiff_t dststride, +; const uint8_t *_src, ptrdiff_t _srcstride, int height, int8_t *hf, int8_t *vf, int width); +; ****************************** +cglobal %1_put_4tap_h%2_%3, 6, 6, XMM_REGS, dst, dststride, src, srcstride, height, hf +%assign %%stride ((%3 + 7)/8) + MC_4TAP_FILTER %3, hf, m4, m5 +.loop: + MC_4TAP_LOAD %3, srcq-%%stride, %%stride, %2 + MC_4TAP_COMPUTE %3, %2, m4, m5, 1 + PEL_10STORE%2 dstq, m0, m1 + LOOP_END dst, src, srcstride + RET + +; ****************************** +; void %1_put_uni_4tap_hX(uint8_t *dst, ptrdiff_t dststride, +; const uint8_t *_src, ptrdiff_t _srcstride, int height, int8_t *hf, int8_t *vf, int width); +; ****************************** +cglobal %1_put_uni_4tap_h%2_%3, 6, 7, XMM_REGS, dst, dststride, src, srcstride, height, hf +%assign %%stride ((%3 + 7)/8) + movdqa m6, [scale_%3] + MC_4TAP_FILTER %3, hf, m4, m5 +.loop: + MC_4TAP_LOAD %3, srcq-%%stride, %%stride, %2 + MC_4TAP_COMPUTE %3, %2, m4, m5 + UNI_COMPUTE %2, %3, m0, m1, m6 + PEL_%3STORE%2 dstq, m0, m1 + add dstq, dststrideq ; dst += dststride + add srcq, srcstrideq ; src += srcstride + dec heightd ; cmp height + jnz .loop ; height loop + RET + +; ****************************** +; void %1_put_4tap_v(int16_t *dst, ptrdiff_t dststride, +; const uint8_t *_src, ptrdiff_t _srcstride, int height, int8_t *hf, int8_t *vf, int width) +; ****************************** +cglobal %1_put_4tap_v%2_%3, 7, 7, XMM_REGS, dst, dststride, src, srcstride, height, r3src, vf + sub srcq, srcstrideq + MC_4TAP_FILTER %3, vf, m4, m5 + lea r3srcq, [srcstrideq*3] +.loop: + MC_4TAP_LOAD %3, srcq, srcstride, %2 + MC_4TAP_COMPUTE %3, %2, m4, m5, 1 + PEL_10STORE%2 dstq, m0, m1 + LOOP_END dst, src, srcstride + RET + +; ****************************** +; void %1_put_uni_4tap_vX(uint8_t *dst, ptrdiff_t dststride, +; const uint8_t *_src, ptrdiff_t _srcstride, int height, int8_t *hf, int8_t *vf, int width); +; ****************************** +cglobal %1_put_uni_4tap_v%2_%3, 7, 7, XMM_REGS, dst, dststride, src, srcstride, height, r3src, vf + movdqa m6, [scale_%3] + sub srcq, srcstrideq + MC_4TAP_FILTER %3, vf, m4, m5 + lea r3srcq, [srcstrideq*3] +.loop: + MC_4TAP_LOAD %3, srcq, srcstride, %2 + MC_4TAP_COMPUTE %3, %2, m4, m5 + UNI_COMPUTE %2, %3, m0, m1, m6 + PEL_%3STORE%2 dstq, m0, m1 + add dstq, dststrideq ; dst += dststride + add srcq, srcstrideq ; src += srcstride + dec heightd ; cmp height + jnz .loop ; height loop + RET +%endmacro + +%macro PUT_4TAP_HV 3 +; ****************************** +; void put_4tap_hv(int16_t *dst, ptrdiff_t dststride, +; const uint8_t *_src, ptrdiff_t _srcstride, int height, int8_t *hf, int8_t *vf, int width) +; ****************************** +cglobal %1_put_4tap_hv%2_%3, 7, 8, 16 , dst, dststride, src, srcstride, height, hf, vf, r3src +%assign %%stride ((%3 + 7)/8) + sub srcq, srcstrideq + MC_4TAP_HV_FILTER %3 + MC_4TAP_LOAD %3, srcq-%%stride, %%stride, %2 + MC_4TAP_COMPUTE %3, %2, m14, m15 +%if (%2 > 8 && (%3 == 8)) + SWAP m8, m1 +%endif + SWAP m4, m0 + add srcq, srcstrideq + MC_4TAP_LOAD %3, srcq-%%stride, %%stride, %2 + MC_4TAP_COMPUTE %3, %2, m14, m15 +%if (%2 > 8 && (%3 == 8)) + SWAP m9, m1 +%endif + SWAP m5, m0 + add srcq, srcstrideq + MC_4TAP_LOAD %3, srcq-%%stride, %%stride, %2 + MC_4TAP_COMPUTE %3, %2, m14, m15 +%if (%2 > 8 && (%3 == 8)) + SWAP m10, m1 +%endif + SWAP m6, m0 + add srcq, srcstrideq +.loop: + MC_4TAP_LOAD %3, srcq-%%stride, %%stride, %2 + MC_4TAP_COMPUTE %3, %2, m14, m15 +%if (%2 > 8 && (%3 == 8)) + SWAP m11, m1 +%endif + SWAP m7, m0 + punpcklwd m0, m4, m5 + punpcklwd m2, m6, m7 +%if %2 > 4 + punpckhwd m1, m4, m5 + punpckhwd m3, m6, m7 +%endif + MC_4TAP_COMPUTE 14, %2, m12, m13 +%if (%2 > 8 && (%3 == 8)) + punpcklwd m4, m8, m9 + punpcklwd m2, m10, m11 + punpckhwd m8, m8, m9 + punpckhwd m3, m10, m11 + MC_4TAP_COMPUTE 14, %2, m12, m13, m4, m2, m8, m3 +%if cpuflag(avx2) + vinserti128 m2, m0, xm4, 1 + vperm2i128 m3, m0, m4, q0301 + PEL_10STORE%2 dstq, m2, m3 +%else + PEL_10STORE%2 dstq, m0, m4 +%endif +%else + PEL_10STORE%2 dstq, m0, m1 +%endif + movdqa m4, m5 + movdqa m5, m6 + movdqa m6, m7 +%if (%2 > 8 && (%3 == 8)) + mova m8, m9 + mova m9, m10 + mova m10, m11 +%endif + LOOP_END dst, src, srcstride + RET + +cglobal %1_put_uni_4tap_hv%2_%3, 7, 8, 16 , dst, dststride, src, srcstride, height, hf, vf, r3src +%assign %%stride ((%3 + 7)/8) + sub srcq, srcstrideq + MC_4TAP_HV_FILTER %3 + MC_4TAP_LOAD %3, srcq-%%stride, %%stride, %2 + MC_4TAP_COMPUTE %3, %2, m14, m15 +%if (%2 > 8 && (%3 == 8)) + SWAP m8, m1 +%endif + SWAP m4, m0 + add srcq, srcstrideq + MC_4TAP_LOAD %3, srcq-%%stride, %%stride, %2 + MC_4TAP_COMPUTE %3, %2, m14, m15 +%if (%2 > 8 && (%3 == 8)) + SWAP m9, m1 +%endif + SWAP m5, m0 + add srcq, srcstrideq + MC_4TAP_LOAD %3, srcq-%%stride, %%stride, %2 + MC_4TAP_COMPUTE %3, %2, m14, m15 +%if (%2 > 8 && (%3 == 8)) + SWAP m10, m1 +%endif + SWAP m6, m0 + add srcq, srcstrideq +.loop: + MC_4TAP_LOAD %3, srcq-%%stride, %%stride, %2 + MC_4TAP_COMPUTE %3, %2, m14, m15 +%if (%2 > 8 && (%3 == 8)) + SWAP m11, m1 +%endif + mova m7, m0 + punpcklwd m0, m4, m5 + punpcklwd m2, m6, m7 +%if %2 > 4 + punpckhwd m1, m4, m5 + punpckhwd m3, m6, m7 +%endif + MC_4TAP_COMPUTE 14, %2, m12, m13 +%if (%2 > 8 && (%3 == 8)) + punpcklwd m4, m8, m9 + punpcklwd m2, m10, m11 + punpckhwd m8, m8, m9 + punpckhwd m3, m10, m11 + MC_4TAP_COMPUTE 14, %2, m12, m13, m4, m2, m8, m3 + UNI_COMPUTE %2, %3, m0, m4, [scale_%3] +%else + UNI_COMPUTE %2, %3, m0, m1, [scale_%3] +%endif + PEL_%3STORE%2 dstq, m0, m1 + mova m4, m5 + mova m5, m6 + mova m6, m7 +%if (%2 > 8 && (%3 == 8)) + mova m8, m9 + mova m9, m10 + mova m10, m11 +%endif + add dstq, dststrideq ; dst += dststride + add srcq, srcstrideq ; src += srcstride + dec heightd ; cmp height + jnz .loop ; height loop + RET +%endmacro + +; ****************************** +; void put_8tap_hX_X_X(int16_t *dst, ptrdiff_t dststride, const uint8_t *_src, ptrdiff_t srcstride, +; int height, const int8_t *hf, const int8_t *vf, int width) +; ****************************** + +%macro PUT_8TAP 3 +cglobal %1_put_8tap_h%2_%3, 6, 6, 16, dst, dststride, src, srcstride, height, hf + MC_8TAP_FILTER %3, hf +.loop: + MC_8TAP_H_LOAD %3, srcq, %2, 10 + MC_8TAP_COMPUTE %2, %3, 1 +%if %3 > 8 + packssdw m0, m1 +%endif + PEL_10STORE%2 dstq, m0, m1 + LOOP_END dst, src, srcstride + RET + +; ****************************** +; void put_uni_8tap_hX_X_X(int16_t *dst, ptrdiff_t dststride, const uint8_t *_src, ptrdiff_t srcstride, +; int height, const int8_t *hf, const int8_t *vf, int width) +; ****************************** +cglobal %1_put_uni_8tap_h%2_%3, 6, 7, 16 , dst, dststride, src, srcstride, height, hf + mova m9, [scale_%3] + MC_8TAP_FILTER %3, hf +.loop: + MC_8TAP_H_LOAD %3, srcq, %2, 10 + MC_8TAP_COMPUTE %2, %3 +%if %3 > 8 + packssdw m0, m1 +%endif + UNI_COMPUTE %2, %3, m0, m1, m9 + PEL_%3STORE%2 dstq, m0, m1 + add dstq, dststrideq ; dst += dststride + add srcq, srcstrideq ; src += srcstride + dec heightd ; cmp height + jnz .loop ; height loop + RET + + +; ****************************** +; void put_8tap_vX_X_X(int16_t *dst, ptrdiff_t dststride, const uint8_t *_src, ptrdiff_t srcstride, +; int height, const int8_t *hf, const int8_t *vf, int width) +; ****************************** +cglobal %1_put_8tap_v%2_%3, 7, 8, 16, dst, dststride, src, srcstride, height, r3src, vf + MC_8TAP_FILTER %3, vf + lea r3srcq, [srcstrideq*3] +.loop: + MC_8TAP_V_LOAD %3, srcq, srcstride, %2, r7 + MC_8TAP_COMPUTE %2, %3, 1 +%if %3 > 8 + packssdw m0, m1 +%endif + PEL_10STORE%2 dstq, m0, m1 + LOOP_END dst, src, srcstride + RET + +; ****************************** +; void put_uni_8tap_vX_X_X(int16_t *dst, ptrdiff_t dststride, const uint8_t *_src, ptrdiff_t srcstride, +; int height, const int8_t *hf, const int8_t *vf, int width) +; ****************************** +cglobal %1_put_uni_8tap_v%2_%3, 7, 9, 16, dst, dststride, src, srcstride, height, r3src, vf + MC_8TAP_FILTER %3, vf + movdqa m9, [scale_%3] + lea r3srcq, [srcstrideq*3] +.loop: + MC_8TAP_V_LOAD %3, srcq, srcstride, %2, r8 + MC_8TAP_COMPUTE %2, %3 +%if %3 > 8 + packssdw m0, m1 +%endif + UNI_COMPUTE %2, %3, m0, m1, m9 + PEL_%3STORE%2 dstq, m0, m1 + add dstq, dststrideq ; dst += dststride + add srcq, srcstrideq ; src += srcstride + dec heightd ; cmp height + jnz .loop ; height loop + RET + +%endmacro + + +; ****************************** +; void put_8tap_hvX_X(int16_t *dst, ptrdiff_t dststride, const uint8_t *_src, ptrdiff_t srcstride, +; int height, const int8_t *hf, const int8_t *vf, int width) +; ****************************** +%macro PUT_8TAP_HV 3 +cglobal %1_put_8tap_hv%2_%3, 7, 8, 16, 0 - mmsize*16, dst, dststride, src, srcstride, height, hf, vf, r3src + MC_8TAP_FILTER %3, hf, 0 + lea hfq, [rsp] + MC_8TAP_FILTER %3, vf, 8*mmsize + lea vfq, [rsp + 8*mmsize] + + lea r3srcq, [srcstrideq*3] + sub srcq, r3srcq + + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m8, m0 + add srcq, srcstrideq + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m9, m0 + add srcq, srcstrideq + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m10, m0 + add srcq, srcstrideq + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m11, m0 + add srcq, srcstrideq + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m12, m0 + add srcq, srcstrideq + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m13, m0 + add srcq, srcstrideq + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m14, m0 + add srcq, srcstrideq +.loop: + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m15, m0 + punpcklwd m0, m8, m9 + punpcklwd m2, m10, m11 + punpcklwd m4, m12, m13 + punpcklwd m6, m14, m15 +%if %2 > 4 + punpckhwd m1, m8, m9 + punpckhwd m3, m10, m11 + punpckhwd m5, m12, m13 + punpckhwd m7, m14, m15 +%endif +%if %2 <= 4 + movq m8, m9 + movq m9, m10 + movq m10, m11 + movq m11, m12 + movq m12, m13 + movq m13, m14 + movq m14, m15 +%else + movdqa m8, m9 + movdqa m9, m10 + movdqa m10, m11 + movdqa m11, m12 + movdqa m12, m13 + movdqa m13, m14 + movdqa m14, m15 +%endif + MC_8TAP_HV_COMPUTE %2, 14, vf, ackssdw + PEL_10STORE%2 dstq, m0, m1 + + LOOP_END dst, src, srcstride + RET + + +cglobal %1_put_uni_8tap_hv%2_%3, 7, 9, 16, 0 - 16*mmsize, dst, dststride, src, srcstride, height, hf, vf, r3src + MC_8TAP_FILTER %3, hf, 0 + lea hfq, [rsp] + MC_8TAP_FILTER %3, vf, 8*mmsize + lea vfq, [rsp + 8*mmsize] + lea r3srcq, [srcstrideq*3] + sub srcq, r3srcq + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m8, m0 + add srcq, srcstrideq + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m9, m0 + add srcq, srcstrideq + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m10, m0 + add srcq, srcstrideq + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m11, m0 + add srcq, srcstrideq + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m12, m0 + add srcq, srcstrideq + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m13, m0 + add srcq, srcstrideq + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m14, m0 + add srcq, srcstrideq +.loop: + MC_8TAP_H_LOAD %3, srcq, %2, 15 + MC_8TAP_HV_COMPUTE %2, %3, hf, ackssdw + SWAP m15, m0 + punpcklwd m0, m8, m9 + punpcklwd m2, m10, m11 + punpcklwd m4, m12, m13 + punpcklwd m6, m14, m15 +%if %2 > 4 + punpckhwd m1, m8, m9 + punpckhwd m3, m10, m11 + punpckhwd m5, m12, m13 + punpckhwd m7, m14, m15 +%endif + MC_8TAP_HV_COMPUTE %2, 14, vf, ackusdw + UNI_COMPUTE %2, %3, m0, m1, [scale_%3] + PEL_%3STORE%2 dstq, m0, m1 + +%if %2 <= 4 + movq m8, m9 + movq m9, m10 + movq m10, m11 + movq m11, m12 + movq m12, m13 + movq m13, m14 + movq m14, m15 +%else + mova m8, m9 + mova m9, m10 + mova m10, m11 + mova m11, m12 + mova m12, m13 + mova m13, m14 + mova m14, m15 +%endif + add dstq, dststrideq ; dst += dststride + add srcq, srcstrideq ; src += srcstride + dec heightd ; cmp height + jnz .loop ; height loop + RET + +%endmacro + +%macro H2656PUT_PIXELS 2 + PUT_PIXELS h2656, %1, %2 +%endmacro + +%macro H2656PUT_4TAP 2 + PUT_4TAP h2656, %1, %2 +%endmacro + +%macro H2656PUT_4TAP_HV 2 + PUT_4TAP_HV h2656, %1, %2 +%endmacro + +%macro H2656PUT_8TAP 2 + PUT_8TAP h2656, %1, %2 +%endmacro + +%macro H2656PUT_8TAP_HV 2 + PUT_8TAP_HV h2656, %1, %2 +%endmacro + +%if ARCH_X86_64 + +INIT_XMM sse4 +H2656PUT_PIXELS 2, 8 +H2656PUT_PIXELS 4, 8 +H2656PUT_PIXELS 6, 8 +H2656PUT_PIXELS 8, 8 +H2656PUT_PIXELS 12, 8 +H2656PUT_PIXELS 16, 8 + +H2656PUT_PIXELS 2, 10 +H2656PUT_PIXELS 4, 10 +H2656PUT_PIXELS 6, 10 +H2656PUT_PIXELS 8, 10 + +H2656PUT_PIXELS 2, 12 +H2656PUT_PIXELS 4, 12 +H2656PUT_PIXELS 6, 12 +H2656PUT_PIXELS 8, 12 + +H2656PUT_4TAP 2, 8 +H2656PUT_4TAP 4, 8 +H2656PUT_4TAP 6, 8 +H2656PUT_4TAP 8, 8 + +H2656PUT_4TAP 12, 8 +H2656PUT_4TAP 16, 8 + +H2656PUT_4TAP 2, 10 +H2656PUT_4TAP 4, 10 +H2656PUT_4TAP 6, 10 +H2656PUT_4TAP 8, 10 + +H2656PUT_4TAP 2, 12 +H2656PUT_4TAP 4, 12 +H2656PUT_4TAP 6, 12 +H2656PUT_4TAP 8, 12 + +H2656PUT_4TAP_HV 2, 8 +H2656PUT_4TAP_HV 4, 8 +H2656PUT_4TAP_HV 6, 8 +H2656PUT_4TAP_HV 8, 8 +H2656PUT_4TAP_HV 16, 8 + +H2656PUT_4TAP_HV 2, 10 +H2656PUT_4TAP_HV 4, 10 +H2656PUT_4TAP_HV 6, 10 +H2656PUT_4TAP_HV 8, 10 + +H2656PUT_4TAP_HV 2, 12 +H2656PUT_4TAP_HV 4, 12 +H2656PUT_4TAP_HV 6, 12 +H2656PUT_4TAP_HV 8, 12 + +H2656PUT_8TAP 4, 8 +H2656PUT_8TAP 8, 8 +H2656PUT_8TAP 12, 8 +H2656PUT_8TAP 16, 8 + +H2656PUT_8TAP 4, 10 +H2656PUT_8TAP 8, 10 + +H2656PUT_8TAP 4, 12 +H2656PUT_8TAP 8, 12 + +H2656PUT_8TAP_HV 4, 8 +H2656PUT_8TAP_HV 8, 8 + +H2656PUT_8TAP_HV 4, 10 +H2656PUT_8TAP_HV 8, 10 + +H2656PUT_8TAP_HV 4, 12 +H2656PUT_8TAP_HV 8, 12 + +%if HAVE_AVX2_EXTERNAL +INIT_YMM avx2 + +H2656PUT_PIXELS 32, 8 +H2656PUT_PIXELS 16, 10 +H2656PUT_PIXELS 16, 12 + +H2656PUT_8TAP 32, 8 +H2656PUT_8TAP 16, 10 +H2656PUT_8TAP 16, 12 + +H2656PUT_8TAP_HV 32, 8 +H2656PUT_8TAP_HV 16, 10 +H2656PUT_8TAP_HV 16, 12 + +H2656PUT_4TAP 32, 8 +H2656PUT_4TAP 16, 10 +H2656PUT_4TAP 16, 12 + +H2656PUT_4TAP_HV 32, 8 +H2656PUT_4TAP_HV 16, 10 +H2656PUT_4TAP_HV 16, 12 + +%endif + +%endif diff --git a/libavcodec/x86/h26x/h2656dsp.c b/libavcodec/x86/h26x/h2656dsp.c new file mode 100644 index 00000000000..c402f9e21c7 --- /dev/null +++ b/libavcodec/x86/h26x/h2656dsp.c @@ -0,0 +1,101 @@ +/* + * DSP for HEVC/VVC + * + * Copyright (C) 2022-2024 Nuo Mi + * Copyright (c) 2023-2024 Wu Jianhua + * + * 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 "h2656dsp.h" + +#define mc_rep_func(name, bitd, step, W, opt) \ +void ff_h2656_put_##name##W##_##bitd##_##opt(int16_t *_dst, ptrdiff_t dststride, \ + const uint8_t *_src, ptrdiff_t _srcstride, int height, const int8_t *hf, const int8_t *vf, int width) \ +{ \ + int i; \ + int16_t *dst; \ + for (i = 0; i < W; i += step) { \ + const uint8_t *src = _src + (i * ((bitd + 7) / 8)); \ + dst = _dst + i; \ + ff_h2656_put_##name##step##_##bitd##_##opt(dst, dststride, src, _srcstride, height, hf, vf, width); \ + } \ +} + +#define mc_rep_uni_func(name, bitd, step, W, opt) \ +void ff_h2656_put_uni_##name##W##_##bitd##_##opt(uint8_t *_dst, ptrdiff_t dststride, \ + const uint8_t *_src, ptrdiff_t _srcstride, int height, const int8_t *hf, const int8_t *vf, int width) \ +{ \ + int i; \ + uint8_t *dst; \ + for (i = 0; i < W; i += step) { \ + const uint8_t *src = _src + (i * ((bitd + 7) / 8)); \ + dst = _dst + (i * ((bitd + 7) / 8)); \ + ff_h2656_put_uni_##name##step##_##bitd##_##opt(dst, dststride, src, _srcstride, \ + height, hf, vf, width); \ + } \ +} + +#define mc_rep_funcs(name, bitd, step, W, opt) \ + mc_rep_func(name, bitd, step, W, opt) \ + mc_rep_uni_func(name, bitd, step, W, opt) + +#define MC_REP_FUNCS_SSE4(fname) \ + mc_rep_funcs(fname, 8, 16,128, sse4) \ + mc_rep_funcs(fname, 8, 16, 64, sse4) \ + mc_rep_funcs(fname, 8, 16, 32, sse4) \ + mc_rep_funcs(fname, 10, 8,128, sse4) \ + mc_rep_funcs(fname, 10, 8, 64, sse4) \ + mc_rep_funcs(fname, 10, 8, 32, sse4) \ + mc_rep_funcs(fname, 10, 8, 16, sse4) \ + mc_rep_funcs(fname, 12, 8,128, sse4) \ + mc_rep_funcs(fname, 12, 8, 64, sse4) \ + mc_rep_funcs(fname, 12, 8, 32, sse4) \ + mc_rep_funcs(fname, 12, 8, 16, sse4) \ + +#if ARCH_X86_64 && HAVE_SSE4_EXTERNAL + +MC_REP_FUNCS_SSE4(pixels) +MC_REP_FUNCS_SSE4(4tap_h) +MC_REP_FUNCS_SSE4(4tap_v) +MC_REP_FUNCS_SSE4(4tap_hv) +MC_REP_FUNCS_SSE4(8tap_h) +MC_REP_FUNCS_SSE4(8tap_v) +MC_REP_FUNCS_SSE4(8tap_hv) +mc_rep_funcs(8tap_hv, 8, 8, 16, sse4) + +#if HAVE_AVX2_EXTERNAL + +#define MC_REP_FUNCS_AVX2(fname) \ + mc_rep_funcs(fname, 8, 32, 64, avx2) \ + mc_rep_funcs(fname, 8, 32,128, avx2) \ + mc_rep_funcs(fname,10, 16, 32, avx2) \ + mc_rep_funcs(fname,10, 16, 64, avx2) \ + mc_rep_funcs(fname,10, 16,128, avx2) \ + mc_rep_funcs(fname,12, 16, 32, avx2) \ + mc_rep_funcs(fname,12, 16, 64, avx2) \ + mc_rep_funcs(fname,12, 16,128, avx2) \ + +MC_REP_FUNCS_AVX2(pixels) +MC_REP_FUNCS_AVX2(8tap_h) +MC_REP_FUNCS_AVX2(8tap_v) +MC_REP_FUNCS_AVX2(8tap_hv) +MC_REP_FUNCS_AVX2(4tap_h) +MC_REP_FUNCS_AVX2(4tap_v) +MC_REP_FUNCS_AVX2(4tap_hv) +#endif +#endif diff --git a/libavcodec/x86/h26x/h2656dsp.h b/libavcodec/x86/h26x/h2656dsp.h new file mode 100644 index 00000000000..0ea08b6a20a --- /dev/null +++ b/libavcodec/x86/h26x/h2656dsp.h @@ -0,0 +1,103 @@ +/* + * DSP for HEVC/VVC + * + * Copyright (C) 2022-2024 Nuo Mi + * Copyright (c) 2023-2024 Wu Jianhua + * + * 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 + */ + +#ifndef AVCODEC_X86_H26X_H2656DSP_H +#define AVCODEC_X86_H26X_H2656DSP_H + +#include "config.h" +#include "libavutil/x86/asm.h" +#include "libavutil/x86/cpu.h" +#include + +#define H2656_PEL_PROTOTYPE(name, D, opt) \ +void ff_h2656_put_ ## name ## _ ## D ## _##opt(int16_t *dst, ptrdiff_t dststride, const uint8_t *_src, ptrdiff_t _srcstride, int height, const int8_t *hf, const int8_t *vf, int width); \ +void ff_h2656_put_uni_ ## name ## _ ## D ## _##opt(uint8_t *_dst, ptrdiff_t _dststride, const uint8_t *_src, ptrdiff_t _srcstride, int height, const int8_t *hf, const int8_t *vf, int width) \ + +#define H2656_MC_8TAP_PROTOTYPES(fname, bitd, opt) \ + H2656_PEL_PROTOTYPE(fname##4, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##6, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##8, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##12, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##16, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##32, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##64, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##128, bitd, opt) + +H2656_MC_8TAP_PROTOTYPES(pixels , 8, sse4); +H2656_MC_8TAP_PROTOTYPES(pixels , 10, sse4); +H2656_MC_8TAP_PROTOTYPES(pixels , 12, sse4); +H2656_MC_8TAP_PROTOTYPES(8tap_h , 8, sse4); +H2656_MC_8TAP_PROTOTYPES(8tap_h , 10, sse4); +H2656_MC_8TAP_PROTOTYPES(8tap_h , 12, sse4); +H2656_MC_8TAP_PROTOTYPES(8tap_v , 8, sse4); +H2656_MC_8TAP_PROTOTYPES(8tap_v , 10, sse4); +H2656_MC_8TAP_PROTOTYPES(8tap_v , 12, sse4); +H2656_MC_8TAP_PROTOTYPES(8tap_hv , 8, sse4); +H2656_MC_8TAP_PROTOTYPES(8tap_hv , 10, sse4); +H2656_MC_8TAP_PROTOTYPES(8tap_hv , 12, sse4); + +#define H2656_MC_4TAP_PROTOTYPES(fname, bitd, opt) \ + H2656_PEL_PROTOTYPE(fname##2, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##4, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##6, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##8, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##12, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##16, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##32, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##64, bitd, opt); \ + H2656_PEL_PROTOTYPE(fname##128, bitd, opt) + +#define H2656_MC_4TAP_PROTOTYPES_SSE4(bitd) \ + H2656_PEL_PROTOTYPE(pixels2, bitd, sse4); \ + H2656_MC_4TAP_PROTOTYPES(4tap_h, bitd, sse4); \ + H2656_MC_4TAP_PROTOTYPES(4tap_v, bitd, sse4); \ + H2656_MC_4TAP_PROTOTYPES(4tap_hv, bitd, sse4); \ + +H2656_MC_4TAP_PROTOTYPES_SSE4(8) +H2656_MC_4TAP_PROTOTYPES_SSE4(10) +H2656_MC_4TAP_PROTOTYPES_SSE4(12) + +#define H2656_MC_8TAP_PROTOTYPES_AVX2(fname) \ + H2656_PEL_PROTOTYPE(fname##32 , 8, avx2); \ + H2656_PEL_PROTOTYPE(fname##64 , 8, avx2); \ + H2656_PEL_PROTOTYPE(fname##128, 8, avx2); \ + H2656_PEL_PROTOTYPE(fname##16 ,10, avx2); \ + H2656_PEL_PROTOTYPE(fname##32 ,10, avx2); \ + H2656_PEL_PROTOTYPE(fname##64 ,10, avx2); \ + H2656_PEL_PROTOTYPE(fname##128,10, avx2); \ + H2656_PEL_PROTOTYPE(fname##16 ,12, avx2); \ + H2656_PEL_PROTOTYPE(fname##32 ,12, avx2); \ + H2656_PEL_PROTOTYPE(fname##64 ,12, avx2); \ + H2656_PEL_PROTOTYPE(fname##128,12, avx2) \ + +H2656_MC_8TAP_PROTOTYPES_AVX2(pixels); +H2656_MC_8TAP_PROTOTYPES_AVX2(8tap_h); +H2656_MC_8TAP_PROTOTYPES_AVX2(8tap_v); +H2656_MC_8TAP_PROTOTYPES_AVX2(8tap_hv); +H2656_PEL_PROTOTYPE(8tap_hv16, 8, avx2); + +H2656_MC_8TAP_PROTOTYPES_AVX2(4tap_h); +H2656_MC_8TAP_PROTOTYPES_AVX2(4tap_v); +H2656_MC_8TAP_PROTOTYPES_AVX2(4tap_hv); + +#endif diff --git a/libavcodec/x86/hevc_deblock.asm b/libavcodec/x86/hevc_deblock.asm index 85ee4800bb0..61b79f8079b 100644 --- a/libavcodec/x86/hevc_deblock.asm +++ b/libavcodec/x86/hevc_deblock.asm @@ -541,19 +541,41 @@ ALIGN 16 add betaq, r13 shr betaq, 3; ((beta + (beta >> 1)) >> 3)) - mova m13, [pw_8] psubw m12, m4, m3 ; q0 - p0 - psllw m10, m12, 3; 8 * (q0 - p0) - paddw m12, m10 ; 9 * (q0 - p0) - + paddw m10, m12, m12 + paddw m12, m10 ; 3 * (q0 - p0) psubw m10, m5, m2 ; q1 - p1 - psllw m8, m10, 1; 2 * ( q1 - p1 ) - paddw m10, m8; 3 * ( q1 - p1 ) - psubw m12, m10; 9 * (q0 - p0) - 3 * ( q1 - p1 ) - paddw m12, m13; + 8 + psubw m12, m10 ; 3 * (q0 - p0) - (q1 - p1) +%if %1 < 12 + paddw m10, m12, m12 + paddw m12, [pw_8]; + 8 + paddw m12, m10 ; 9 * (q0 - p0) - 3 * ( q1 - p1 ) psraw m12, 4; >> 4 , delta0 PABSW m13, m12; abs(delta0) - +%elif cpuflag(ssse3) + pabsw m13, m12 + paddw m10, m13, m13 + paddw m13, [pw_8] + paddw m13, m10 ; abs(9 * (q0 - p0) - 3 * ( q1 - p1 )) + pxor m10, m10 + pcmpgtw m10, m12 + paddw m13, m10 + psrlw m13, 4; >> 4, abs(delta0) + psignw m10, m13, m12 + SWAP 10, 12 +%else + pxor m10, m10 + pcmpgtw m10, m12 + pxor m12, m10 + psubw m12, m10 ; abs() + paddw m13, m12, m12 + paddw m12, [pw_8] + paddw m13, m12 ; 3*abs(m12) + paddw m13, m10 + psrlw m13, 4 + pxor m12, m13, m10 + psubw m12, m10 +%endif psllw m10, m9, 2; 8 * tc paddw m10, m9; 10 * tc diff --git a/libavcodec/x86/hevc_mc.asm b/libavcodec/x86/hevc_mc.asm index eb267453fe0..b3b589b2713 100644 --- a/libavcodec/x86/hevc_mc.asm +++ b/libavcodec/x86/hevc_mc.asm @@ -180,7 +180,7 @@ SECTION .text %macro EPEL_FILTER 5 ; bit depth, filter index, xmma, xmmb, gprtmp %if cpuflag(avx2) %assign %%offset 32 -%ifdef PIC +%if PIC lea %5q, [hevc_epel_filters_avx2_%1] %define FILTER %5q %else @@ -188,7 +188,7 @@ SECTION .text %endif %else %assign %%offset 16 -%ifdef PIC +%if PIC lea %5q, [hevc_epel_filters_sse4_%1] %define FILTER %5q %else @@ -216,7 +216,7 @@ SECTION .text %define %%table hevc_epel_filters_sse4_%1 %endif -%ifdef PIC +%if PIC lea r3srcq, [%%table] %define FILTER r3srcq %else @@ -234,7 +234,7 @@ SECTION .text %else %define %%table hevc_epel_filters_sse4_10 %endif -%ifdef PIC +%if PIC lea r3srcq, [%%table] %define FILTER r3srcq %else @@ -257,7 +257,7 @@ SECTION .text %define %%table hevc_qpel_filters_sse4_%1 %endif -%ifdef PIC +%if PIC lea rfilterq, [%%table] %else %define rfilterq %%table @@ -576,7 +576,7 @@ SECTION .text %define %%table hevc_qpel_filters_sse4_%2 %endif -%ifdef PIC +%if PIC lea rfilterq, [%%table] %else %define rfilterq %%table @@ -715,35 +715,6 @@ SECTION .text ; int height, int mx, int my) ; ****************************** -%macro HEVC_PUT_HEVC_PEL_PIXELS 2 -HEVC_PEL_PIXELS %1, %2 -HEVC_UNI_PEL_PIXELS %1, %2 -HEVC_BI_PEL_PIXELS %1, %2 -%endmacro - -%macro HEVC_PEL_PIXELS 2 -cglobal hevc_put_hevc_pel_pixels%1_%2, 4, 4, 3, dst, src, srcstride,height - pxor m2, m2 -.loop: - SIMPLE_LOAD %1, %2, srcq, m0 - MC_PIXEL_COMPUTE %1, %2, 1 - PEL_10STORE%1 dstq, m0, m1 - LOOP_END dst, src, srcstride - RET - %endmacro - -%macro HEVC_UNI_PEL_PIXELS 2 -cglobal hevc_put_hevc_uni_pel_pixels%1_%2, 5, 5, 2, dst, dststride, src, srcstride,height -.loop: - SIMPLE_LOAD %1, %2, srcq, m0 - PEL_%2STORE%1 dstq, m0, m1 - add dstq, dststrideq ; dst += dststride - add srcq, srcstrideq ; src += srcstride - dec heightd ; cmp height - jnz .loop ; height loop - RET -%endmacro - %macro HEVC_BI_PEL_PIXELS 2 cglobal hevc_put_hevc_bi_pel_pixels%1_%2, 6, 6, 6, dst, dststride, src, srcstride, src2, height pxor m2, m2 @@ -777,32 +748,8 @@ cglobal hevc_put_hevc_bi_pel_pixels%1_%2, 6, 6, 6, dst, dststride, src, srcstrid %define XMM_REGS 8 %endif -cglobal hevc_put_hevc_epel_h%1_%2, 5, 6, XMM_REGS, dst, src, srcstride, height, mx, rfilter -%assign %%stride ((%2 + 7)/8) - EPEL_FILTER %2, mx, m4, m5, rfilter -.loop: - EPEL_LOAD %2, srcq-%%stride, %%stride, %1 - EPEL_COMPUTE %2, %1, m4, m5, 1 - PEL_10STORE%1 dstq, m0, m1 - LOOP_END dst, src, srcstride - RET - -cglobal hevc_put_hevc_uni_epel_h%1_%2, 6, 7, XMM_REGS, dst, dststride, src, srcstride, height, mx, rfilter -%assign %%stride ((%2 + 7)/8) - movdqa m6, [pw_%2] - EPEL_FILTER %2, mx, m4, m5, rfilter -.loop: - EPEL_LOAD %2, srcq-%%stride, %%stride, %1 - EPEL_COMPUTE %2, %1, m4, m5 - UNI_COMPUTE %1, %2, m0, m1, m6 - PEL_%2STORE%1 dstq, m0, m1 - add dstq, dststrideq ; dst += dststride - add srcq, srcstrideq ; src += srcstride - dec heightd ; cmp height - jnz .loop ; height loop - RET - cglobal hevc_put_hevc_bi_epel_h%1_%2, 7, 8, XMM_REGS, dst, dststride, src, srcstride, src2, height, mx, rfilter +%assign %%stride ((%2 + 7)/8) movdqa m6, [pw_bi_%2] EPEL_FILTER %2, mx, m4, m5, rfilter .loop: @@ -824,36 +771,6 @@ cglobal hevc_put_hevc_bi_epel_h%1_%2, 7, 8, XMM_REGS, dst, dststride, src, srcst ; int height, int mx, int my, int width) ; ****************************** -cglobal hevc_put_hevc_epel_v%1_%2, 4, 6, XMM_REGS, dst, src, srcstride, height, r3src, my - movifnidn myd, mym - sub srcq, srcstrideq - EPEL_FILTER %2, my, m4, m5, r3src - lea r3srcq, [srcstrideq*3] -.loop: - EPEL_LOAD %2, srcq, srcstride, %1 - EPEL_COMPUTE %2, %1, m4, m5, 1 - PEL_10STORE%1 dstq, m0, m1 - LOOP_END dst, src, srcstride - RET - -cglobal hevc_put_hevc_uni_epel_v%1_%2, 5, 7, XMM_REGS, dst, dststride, src, srcstride, height, r3src, my - movifnidn myd, mym - movdqa m6, [pw_%2] - sub srcq, srcstrideq - EPEL_FILTER %2, my, m4, m5, r3src - lea r3srcq, [srcstrideq*3] -.loop: - EPEL_LOAD %2, srcq, srcstride, %1 - EPEL_COMPUTE %2, %1, m4, m5 - UNI_COMPUTE %1, %2, m0, m1, m6 - PEL_%2STORE%1 dstq, m0, m1 - add dstq, dststrideq ; dst += dststride - add srcq, srcstrideq ; src += srcstride - dec heightd ; cmp height - jnz .loop ; height loop - RET - - cglobal hevc_put_hevc_bi_epel_v%1_%2, 6, 8, XMM_REGS, dst, dststride, src, srcstride, src2, height, r3src, my movifnidn myd, mym movdqa m6, [pw_bi_%2] @@ -882,135 +799,6 @@ cglobal hevc_put_hevc_bi_epel_v%1_%2, 6, 8, XMM_REGS, dst, dststride, src, srcst ; ****************************** %macro HEVC_PUT_HEVC_EPEL_HV 2 -cglobal hevc_put_hevc_epel_hv%1_%2, 6, 7, 16 , dst, src, srcstride, height, mx, my, r3src -%assign %%stride ((%2 + 7)/8) - sub srcq, srcstrideq - EPEL_HV_FILTER %2 - EPEL_LOAD %2, srcq-%%stride, %%stride, %1 - EPEL_COMPUTE %2, %1, m14, m15 -%if (%1 > 8 && (%2 == 8)) - SWAP m8, m1 -%endif - SWAP m4, m0 - add srcq, srcstrideq - EPEL_LOAD %2, srcq-%%stride, %%stride, %1 - EPEL_COMPUTE %2, %1, m14, m15 -%if (%1 > 8 && (%2 == 8)) - SWAP m9, m1 -%endif - SWAP m5, m0 - add srcq, srcstrideq - EPEL_LOAD %2, srcq-%%stride, %%stride, %1 - EPEL_COMPUTE %2, %1, m14, m15 -%if (%1 > 8 && (%2 == 8)) - SWAP m10, m1 -%endif - SWAP m6, m0 - add srcq, srcstrideq -.loop: - EPEL_LOAD %2, srcq-%%stride, %%stride, %1 - EPEL_COMPUTE %2, %1, m14, m15 -%if (%1 > 8 && (%2 == 8)) - SWAP m11, m1 -%endif - SWAP m7, m0 - punpcklwd m0, m4, m5 - punpcklwd m2, m6, m7 -%if %1 > 4 - punpckhwd m1, m4, m5 - punpckhwd m3, m6, m7 -%endif - EPEL_COMPUTE 14, %1, m12, m13 -%if (%1 > 8 && (%2 == 8)) - punpcklwd m4, m8, m9 - punpcklwd m2, m10, m11 - punpckhwd m8, m8, m9 - punpckhwd m3, m10, m11 - EPEL_COMPUTE 14, %1, m12, m13, m4, m2, m8, m3 -%if cpuflag(avx2) - vinserti128 m2, m0, xm4, 1 - vperm2i128 m3, m0, m4, q0301 - PEL_10STORE%1 dstq, m2, m3 -%else - PEL_10STORE%1 dstq, m0, m4 -%endif -%else - PEL_10STORE%1 dstq, m0, m1 -%endif - movdqa m4, m5 - movdqa m5, m6 - movdqa m6, m7 -%if (%1 > 8 && (%2 == 8)) - mova m8, m9 - mova m9, m10 - mova m10, m11 -%endif - LOOP_END dst, src, srcstride - RET - -cglobal hevc_put_hevc_uni_epel_hv%1_%2, 7, 8, 16 , dst, dststride, src, srcstride, height, mx, my, r3src -%assign %%stride ((%2 + 7)/8) - sub srcq, srcstrideq - EPEL_HV_FILTER %2 - EPEL_LOAD %2, srcq-%%stride, %%stride, %1 - EPEL_COMPUTE %2, %1, m14, m15 -%if (%1 > 8 && (%2 == 8)) - SWAP m8, m1 -%endif - SWAP m4, m0 - add srcq, srcstrideq - EPEL_LOAD %2, srcq-%%stride, %%stride, %1 - EPEL_COMPUTE %2, %1, m14, m15 -%if (%1 > 8 && (%2 == 8)) - SWAP m9, m1 -%endif - SWAP m5, m0 - add srcq, srcstrideq - EPEL_LOAD %2, srcq-%%stride, %%stride, %1 - EPEL_COMPUTE %2, %1, m14, m15 -%if (%1 > 8 && (%2 == 8)) - SWAP m10, m1 -%endif - SWAP m6, m0 - add srcq, srcstrideq -.loop: - EPEL_LOAD %2, srcq-%%stride, %%stride, %1 - EPEL_COMPUTE %2, %1, m14, m15 -%if (%1 > 8 && (%2 == 8)) - SWAP m11, m1 -%endif - mova m7, m0 - punpcklwd m0, m4, m5 - punpcklwd m2, m6, m7 -%if %1 > 4 - punpckhwd m1, m4, m5 - punpckhwd m3, m6, m7 -%endif - EPEL_COMPUTE 14, %1, m12, m13 -%if (%1 > 8 && (%2 == 8)) - punpcklwd m4, m8, m9 - punpcklwd m2, m10, m11 - punpckhwd m8, m8, m9 - punpckhwd m3, m10, m11 - EPEL_COMPUTE 14, %1, m12, m13, m4, m2, m8, m3 - UNI_COMPUTE %1, %2, m0, m4, [pw_%2] -%else - UNI_COMPUTE %1, %2, m0, m1, [pw_%2] -%endif - PEL_%2STORE%1 dstq, m0, m1 - mova m4, m5 - mova m5, m6 - mova m6, m7 -%if (%1 > 8 && (%2 == 8)) - mova m8, m9 - mova m9, m10 - mova m10, m11 -%endif - add dstq, dststrideq ; dst += dststride - add srcq, srcstrideq ; src += srcstride - dec heightd ; cmp height - jnz .loop ; height loop - RET cglobal hevc_put_hevc_bi_epel_hv%1_%2, 8, 9, 16, dst, dststride, src, srcstride, src2, height, mx, my, r3src %assign %%stride ((%2 + 7)/8) @@ -1093,34 +881,6 @@ cglobal hevc_put_hevc_bi_epel_hv%1_%2, 8, 9, 16, dst, dststride, src, srcstride, ; ****************************** %macro HEVC_PUT_HEVC_QPEL 2 -cglobal hevc_put_hevc_qpel_h%1_%2, 5, 6, 16, dst, src, srcstride, height, mx, rfilter - QPEL_FILTER %2, mx -.loop: - QPEL_H_LOAD %2, srcq, %1, 10 - QPEL_COMPUTE %1, %2, 1 -%if %2 > 8 - packssdw m0, m1 -%endif - PEL_10STORE%1 dstq, m0, m1 - LOOP_END dst, src, srcstride - RET - -cglobal hevc_put_hevc_uni_qpel_h%1_%2, 6, 7, 16 , dst, dststride, src, srcstride, height, mx, rfilter - mova m9, [pw_%2] - QPEL_FILTER %2, mx -.loop: - QPEL_H_LOAD %2, srcq, %1, 10 - QPEL_COMPUTE %1, %2 -%if %2 > 8 - packssdw m0, m1 -%endif - UNI_COMPUTE %1, %2, m0, m1, m9 - PEL_%2STORE%1 dstq, m0, m1 - add dstq, dststrideq ; dst += dststride - add srcq, srcstrideq ; src += srcstride - dec heightd ; cmp height - jnz .loop ; height loop - RET cglobal hevc_put_hevc_bi_qpel_h%1_%2, 7, 8, 16 , dst, dststride, src, srcstride, src2, height, mx, rfilter movdqa m9, [pw_bi_%2] @@ -1148,38 +908,6 @@ cglobal hevc_put_hevc_bi_qpel_h%1_%2, 7, 8, 16 , dst, dststride, src, srcstride, ; int height, int mx, int my, int width) ; ****************************** -cglobal hevc_put_hevc_qpel_v%1_%2, 4, 8, 16, dst, src, srcstride, height, r3src, my, rfilter - movifnidn myd, mym - lea r3srcq, [srcstrideq*3] - QPEL_FILTER %2, my -.loop: - QPEL_V_LOAD %2, srcq, srcstride, %1, r7 - QPEL_COMPUTE %1, %2, 1 -%if %2 > 8 - packssdw m0, m1 -%endif - PEL_10STORE%1 dstq, m0, m1 - LOOP_END dst, src, srcstride - RET - -cglobal hevc_put_hevc_uni_qpel_v%1_%2, 5, 9, 16, dst, dststride, src, srcstride, height, r3src, my, rfilter - movifnidn myd, mym - movdqa m9, [pw_%2] - lea r3srcq, [srcstrideq*3] - QPEL_FILTER %2, my -.loop: - QPEL_V_LOAD %2, srcq, srcstride, %1, r8 - QPEL_COMPUTE %1, %2 -%if %2 > 8 - packssdw m0, m1 -%endif - UNI_COMPUTE %1, %2, m0, m1, m9 - PEL_%2STORE%1 dstq, m0, m1 - add dstq, dststrideq ; dst += dststride - add srcq, srcstrideq ; src += srcstride - dec heightd ; cmp height - jnz .loop ; height loop - RET cglobal hevc_put_hevc_bi_qpel_v%1_%2, 6, 10, 16, dst, dststride, src, srcstride, src2, height, r3src, my, rfilter movifnidn myd, mym @@ -1210,162 +938,6 @@ cglobal hevc_put_hevc_bi_qpel_v%1_%2, 6, 10, 16, dst, dststride, src, srcstride, ; int height, int mx, int my) ; ****************************** %macro HEVC_PUT_HEVC_QPEL_HV 2 -cglobal hevc_put_hevc_qpel_hv%1_%2, 6, 8, 16, dst, src, srcstride, height, mx, my, r3src, rfilter -%if cpuflag(avx2) -%assign %%shift 4 -%else -%assign %%shift 3 -%endif - sub mxq, 1 - sub myq, 1 - shl mxq, %%shift ; multiply by 32 - shl myq, %%shift ; multiply by 32 - lea r3srcq, [srcstrideq*3] - sub srcq, r3srcq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m8, m0 - add srcq, srcstrideq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m9, m0 - add srcq, srcstrideq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m10, m0 - add srcq, srcstrideq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m11, m0 - add srcq, srcstrideq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m12, m0 - add srcq, srcstrideq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m13, m0 - add srcq, srcstrideq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m14, m0 - add srcq, srcstrideq -.loop: - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m15, m0 - punpcklwd m0, m8, m9 - punpcklwd m2, m10, m11 - punpcklwd m4, m12, m13 - punpcklwd m6, m14, m15 -%if %1 > 4 - punpckhwd m1, m8, m9 - punpckhwd m3, m10, m11 - punpckhwd m5, m12, m13 - punpckhwd m7, m14, m15 -%endif - QPEL_HV_COMPUTE %1, 14, my, ackssdw - PEL_10STORE%1 dstq, m0, m1 -%if %1 <= 4 - movq m8, m9 - movq m9, m10 - movq m10, m11 - movq m11, m12 - movq m12, m13 - movq m13, m14 - movq m14, m15 -%else - movdqa m8, m9 - movdqa m9, m10 - movdqa m10, m11 - movdqa m11, m12 - movdqa m12, m13 - movdqa m13, m14 - movdqa m14, m15 -%endif - LOOP_END dst, src, srcstride - RET - -cglobal hevc_put_hevc_uni_qpel_hv%1_%2, 7, 9, 16 , dst, dststride, src, srcstride, height, mx, my, r3src, rfilter -%if cpuflag(avx2) -%assign %%shift 4 -%else -%assign %%shift 3 -%endif - sub mxq, 1 - sub myq, 1 - shl mxq, %%shift ; multiply by 32 - shl myq, %%shift ; multiply by 32 - lea r3srcq, [srcstrideq*3] - sub srcq, r3srcq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m8, m0 - add srcq, srcstrideq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m9, m0 - add srcq, srcstrideq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m10, m0 - add srcq, srcstrideq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m11, m0 - add srcq, srcstrideq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m12, m0 - add srcq, srcstrideq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m13, m0 - add srcq, srcstrideq - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m14, m0 - add srcq, srcstrideq -.loop: - QPEL_H_LOAD %2, srcq, %1, 15 - QPEL_HV_COMPUTE %1, %2, mx, ackssdw - SWAP m15, m0 - punpcklwd m0, m8, m9 - punpcklwd m2, m10, m11 - punpcklwd m4, m12, m13 - punpcklwd m6, m14, m15 -%if %1 > 4 - punpckhwd m1, m8, m9 - punpckhwd m3, m10, m11 - punpckhwd m5, m12, m13 - punpckhwd m7, m14, m15 -%endif - QPEL_HV_COMPUTE %1, 14, my, ackusdw - UNI_COMPUTE %1, %2, m0, m1, [pw_%2] - PEL_%2STORE%1 dstq, m0, m1 - -%if %1 <= 4 - movq m8, m9 - movq m9, m10 - movq m10, m11 - movq m11, m12 - movq m12, m13 - movq m13, m14 - movq m14, m15 -%else - mova m8, m9 - mova m9, m10 - mova m10, m11 - mova m11, m12 - mova m12, m13 - mova m13, m14 - mova m14, m15 -%endif - add dstq, dststrideq ; dst += dststride - add srcq, srcstrideq ; src += srcstride - dec heightd ; cmp height - jnz .loop ; height loop - RET cglobal hevc_put_hevc_bi_qpel_hv%1_%2, 8, 10, 16, dst, dststride, src, srcstride, src2, height, mx, my, r3src, rfilter %if cpuflag(avx2) @@ -1613,22 +1185,22 @@ WEIGHTING_FUNCS 4, 12 WEIGHTING_FUNCS 6, 12 WEIGHTING_FUNCS 8, 12 -HEVC_PUT_HEVC_PEL_PIXELS 2, 8 -HEVC_PUT_HEVC_PEL_PIXELS 4, 8 -HEVC_PUT_HEVC_PEL_PIXELS 6, 8 -HEVC_PUT_HEVC_PEL_PIXELS 8, 8 -HEVC_PUT_HEVC_PEL_PIXELS 12, 8 -HEVC_PUT_HEVC_PEL_PIXELS 16, 8 +HEVC_BI_PEL_PIXELS 2, 8 +HEVC_BI_PEL_PIXELS 4, 8 +HEVC_BI_PEL_PIXELS 6, 8 +HEVC_BI_PEL_PIXELS 8, 8 +HEVC_BI_PEL_PIXELS 12, 8 +HEVC_BI_PEL_PIXELS 16, 8 -HEVC_PUT_HEVC_PEL_PIXELS 2, 10 -HEVC_PUT_HEVC_PEL_PIXELS 4, 10 -HEVC_PUT_HEVC_PEL_PIXELS 6, 10 -HEVC_PUT_HEVC_PEL_PIXELS 8, 10 +HEVC_BI_PEL_PIXELS 2, 10 +HEVC_BI_PEL_PIXELS 4, 10 +HEVC_BI_PEL_PIXELS 6, 10 +HEVC_BI_PEL_PIXELS 8, 10 -HEVC_PUT_HEVC_PEL_PIXELS 2, 12 -HEVC_PUT_HEVC_PEL_PIXELS 4, 12 -HEVC_PUT_HEVC_PEL_PIXELS 6, 12 -HEVC_PUT_HEVC_PEL_PIXELS 8, 12 +HEVC_BI_PEL_PIXELS 2, 12 +HEVC_BI_PEL_PIXELS 4, 12 +HEVC_BI_PEL_PIXELS 6, 12 +HEVC_BI_PEL_PIXELS 8, 12 HEVC_PUT_HEVC_EPEL 2, 8 HEVC_PUT_HEVC_EPEL 4, 8 @@ -1693,8 +1265,8 @@ HEVC_PUT_HEVC_QPEL_HV 8, 12 %if HAVE_AVX2_EXTERNAL INIT_YMM avx2 ; adds ff_ and _avx2 to function name & enables 256b registers : m0 for 256b, xm0 for 128b. cpuflag(avx2) = 1 / notcpuflag(avx) = 0 -HEVC_PUT_HEVC_PEL_PIXELS 32, 8 -HEVC_PUT_HEVC_PEL_PIXELS 16, 10 +HEVC_BI_PEL_PIXELS 32, 8 +HEVC_BI_PEL_PIXELS 16, 10 HEVC_PUT_HEVC_EPEL 32, 8 HEVC_PUT_HEVC_EPEL 16, 10 @@ -1716,7 +1288,7 @@ HEVC_PUT_HEVC_QPEL_HV 16, 10 %assign %%offset 4 dec %2q shl %2q, 3 -%ifdef PIC +%if PIC lea %5q, [%%table] %define FILTER %5q %else @@ -1793,7 +1365,7 @@ cglobal hevc_put_hevc_qpel_hv%1_%2, 6, 7, 27, dst, src, srcstride, height, mx, m sub myq, 1 shl myq, 5 %define %%table hevc_qpel_filters_avx512icl_v_%1 -%ifdef PIC +%if PIC lea tmpq, [%%table] %define FILTER tmpq %else diff --git a/libavcodec/x86/hevcdsp_init.c b/libavcodec/x86/hevcdsp_init.c index 6f45e5e0dbc..f5bc342cd5f 100644 --- a/libavcodec/x86/hevcdsp_init.c +++ b/libavcodec/x86/hevcdsp_init.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2013 Seppo Tomperi - * Copyright (c) 2013 - 2014 Pierre-Edouard Lepere + * Copyright (c) 2013-2014 Pierre-Edouard Lepere + * Copyright (c) 2023-2024 Wu Jianhua * * This file is part of FFmpeg. * @@ -27,6 +28,7 @@ #include "libavutil/x86/cpu.h" #include "libavcodec/hevcdsp.h" #include "libavcodec/x86/hevcdsp.h" +#include "libavcodec/x86/h26x/h2656dsp.h" #define LFC_FUNC(DIR, DEPTH, OPT) \ void ff_hevc_ ## DIR ## _loop_filter_chroma_ ## DEPTH ## _ ## OPT(uint8_t *pix, ptrdiff_t stride, const int *tc, const uint8_t *no_p, const uint8_t *no_q); @@ -83,6 +85,110 @@ void ff_hevc_idct_32x32_10_ ## opt(int16_t *coeffs, int col_limit); IDCT_FUNCS(sse2) IDCT_FUNCS(avx) + +#define ff_hevc_pel_filters ff_hevc_qpel_filters +#define DECL_HV_FILTER(f) \ + const uint8_t *hf = ff_hevc_ ## f ## _filters[mx]; \ + const uint8_t *vf = ff_hevc_ ## f ## _filters[my]; + +#define FW_PUT(p, a, b, depth, opt) \ +void ff_hevc_put_hevc_ ## a ## _ ## depth ## _##opt(int16_t *dst, const uint8_t *src, ptrdiff_t srcstride, \ + int height, intptr_t mx, intptr_t my,int width) \ +{ \ + DECL_HV_FILTER(p) \ + ff_h2656_put_ ## b ## _ ## depth ## _##opt(dst, 2 * MAX_PB_SIZE, src, srcstride, height, hf, vf, width); \ +} + +#define FW_PUT_UNI(p, a, b, depth, opt) \ +void ff_hevc_put_hevc_uni_ ## a ## _ ## depth ## _##opt(uint8_t *dst, ptrdiff_t dststride, \ + const uint8_t *src, ptrdiff_t srcstride, \ + int height, intptr_t mx, intptr_t my, int width) \ +{ \ + DECL_HV_FILTER(p) \ + ff_h2656_put_uni_ ## b ## _ ## depth ## _##opt(dst, dststride, src, srcstride, height, hf, vf, width); \ +} + +#if ARCH_X86_64 && HAVE_SSE4_EXTERNAL + +#define FW_PUT_FUNCS(p, a, b, depth, opt) \ + FW_PUT(p, a, b, depth, opt) \ + FW_PUT_UNI(p, a, b, depth, opt) + +#define FW_PEL(w, depth, opt) FW_PUT_FUNCS(pel, pel_pixels##w, pixels##w, depth, opt) + +#define FW_DIR(npel, n, w, depth, opt) \ + FW_PUT_FUNCS(npel, npel ## _h##w, n ## tap_h##w, depth, opt) \ + FW_PUT_FUNCS(npel, npel ## _v##w, n ## tap_v##w, depth, opt) + +#define FW_DIR_HV(npel, n, w, depth, opt) \ + FW_PUT_FUNCS(npel, npel ## _hv##w, n ## tap_hv##w, depth, opt) + +FW_PEL(4, 8, sse4) +FW_PEL(6, 8, sse4) +FW_PEL(8, 8, sse4) +FW_PEL(12, 8, sse4) +FW_PEL(16, 8, sse4) +FW_PEL(4, 10, sse4) +FW_PEL(6, 10, sse4) +FW_PEL(8, 10, sse4) +FW_PEL(4, 12, sse4) +FW_PEL(6, 12, sse4) +FW_PEL(8, 12, sse4) + +#define FW_EPEL(w, depth, opt) FW_DIR(epel, 4, w, depth, opt) +#define FW_EPEL_HV(w, depth, opt) FW_DIR_HV(epel, 4, w, depth, opt) +#define FW_EPEL_FUNCS(w, depth, opt) \ + FW_EPEL(w, depth, opt) \ + FW_EPEL_HV(w, depth, opt) + +FW_EPEL(12, 8, sse4) + +FW_EPEL_FUNCS(4, 8, sse4) +FW_EPEL_FUNCS(6, 8, sse4) +FW_EPEL_FUNCS(8, 8, sse4) +FW_EPEL_FUNCS(16, 8, sse4) +FW_EPEL_FUNCS(4, 10, sse4) +FW_EPEL_FUNCS(6, 10, sse4) +FW_EPEL_FUNCS(8, 10, sse4) +FW_EPEL_FUNCS(4, 12, sse4) +FW_EPEL_FUNCS(6, 12, sse4) +FW_EPEL_FUNCS(8, 12, sse4) + +#define FW_QPEL(w, depth, opt) FW_DIR(qpel, 8, w, depth, opt) +#define FW_QPEL_HV(w, depth, opt) FW_DIR_HV(qpel, 8, w, depth, opt) +#define FW_QPEL_FUNCS(w, depth, opt) \ + FW_QPEL(w, depth, opt) \ + FW_QPEL_HV(w, depth, opt) + +FW_QPEL(12, 8, sse4) +FW_QPEL(16, 8, sse4) + +FW_QPEL_FUNCS(4, 8, sse4) +FW_QPEL_FUNCS(8, 8, sse4) +FW_QPEL_FUNCS(4, 10, sse4) +FW_QPEL_FUNCS(8, 10, sse4) +FW_QPEL_FUNCS(4, 12, sse4) +FW_QPEL_FUNCS(8, 12, sse4) + +#if HAVE_AVX2_EXTERNAL + +FW_PEL(32, 8, avx2) +FW_PUT(pel, pel_pixels16, pixels16, 10, avx2) + +FW_EPEL(32, 8, avx2) +FW_EPEL(16, 10, avx2) + +FW_EPEL_HV(32, 8, avx2) +FW_EPEL_HV(16, 10, avx2) + +FW_QPEL(32, 8, avx2) +FW_QPEL(16, 10, avx2) + +FW_QPEL_HV(16, 10, avx2) + +#endif +#endif + #define mc_rep_func(name, bitd, step, W, opt) \ void ff_hevc_put_hevc_##name##W##_##bitd##_##opt(int16_t *_dst, \ const uint8_t *_src, ptrdiff_t _srcstride, int height, \ diff --git a/libavcodec/x86/hpeldsp.asm b/libavcodec/x86/hpeldsp.asm index 7a2b7135d80..3bc278618cb 100644 --- a/libavcodec/x86/hpeldsp.asm +++ b/libavcodec/x86/hpeldsp.asm @@ -165,6 +165,47 @@ cglobal put_no_rnd_pixels8_x2, 4,5 RET +; void ff_put_no_rnd_pixels8_x2_exact(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h) +INIT_MMX mmxext +cglobal put_no_rnd_pixels8_x2_exact, 4,5 + lea r4, [r2*3] + pcmpeqb m6, m6 +.loop: + mova m0, [r1] + mova m2, [r1+r2] + mova m1, [r1+1] + mova m3, [r1+r2+1] + pxor m0, m6 + pxor m2, m6 + pxor m1, m6 + pxor m3, m6 + PAVGB m0, m1 + PAVGB m2, m3 + pxor m0, m6 + pxor m2, m6 + mova [r0], m0 + mova [r0+r2], m2 + mova m0, [r1+r2*2] + mova m1, [r1+r2*2+1] + mova m2, [r1+r4] + mova m3, [r1+r4+1] + pxor m0, m6 + pxor m1, m6 + pxor m2, m6 + pxor m3, m6 + PAVGB m0, m1 + PAVGB m2, m3 + pxor m0, m6 + pxor m2, m6 + mova [r0+r2*2], m0 + mova [r0+r4], m2 + lea r1, [r1+r2*4] + lea r0, [r0+r2*4] + sub r3d, 4 + jg .loop + RET + + ; void ff_put_pixels8_y2(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h) %macro PUT_PIXELS8_Y2 0 %if cpuflag(sse2) @@ -235,6 +276,42 @@ cglobal put_no_rnd_pixels8_y2, 4,5 RET +; void ff_put_no_rnd_pixels8_y2_exact(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h) +INIT_MMX mmxext +cglobal put_no_rnd_pixels8_y2_exact, 4,5 + lea r4, [r2*3] + mova m0, [r1] + pcmpeqb m6, m6 + add r1, r2 + pxor m0, m6 +.loop: + mova m1, [r1] + mova m2, [r1+r2] + pxor m1, m6 + pxor m2, m6 + PAVGB m0, m1 + PAVGB m1, m2 + pxor m0, m6 + pxor m1, m6 + mova [r0], m0 + mova [r0+r2], m1 + mova m1, [r1+r2*2] + mova m0, [r1+r4] + pxor m1, m6 + pxor m0, m6 + PAVGB m2, m1 + PAVGB m1, m0 + pxor m2, m6 + pxor m1, m6 + mova [r0+r2*2], m2 + mova [r0+r4], m1 + lea r1, [r1+r2*4] + lea r0, [r0+r2*4] + sub r3d, 4 + jg .loop + RET + + ; void ff_avg_pixels8_x2(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h) %macro AVG_PIXELS8_X2 0 %if cpuflag(sse2) diff --git a/libavcodec/x86/hpeldsp.h b/libavcodec/x86/hpeldsp.h index fd740da72eb..ac7e625fda0 100644 --- a/libavcodec/x86/hpeldsp.h +++ b/libavcodec/x86/hpeldsp.h @@ -22,8 +22,6 @@ #include #include -#include "libavcodec/hpeldsp.h" - void ff_avg_pixels8_x2_mmx(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h); @@ -50,6 +48,4 @@ void ff_put_pixels16_xy2_sse2(uint8_t *block, const uint8_t *pixels, void ff_put_pixels16_xy2_ssse3(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h); -void ff_hpeldsp_vp3_init_x86(HpelDSPContext *c, int cpu_flags, int flags); - #endif /* AVCODEC_X86_HPELDSP_H */ diff --git a/libavcodec/x86/hpeldsp_init.c b/libavcodec/x86/hpeldsp_init.c index 09c48c341ea..4a0513d06d5 100644 --- a/libavcodec/x86/hpeldsp_init.c +++ b/libavcodec/x86/hpeldsp_init.c @@ -22,8 +22,6 @@ * MMX optimization by Nick Kurshev */ -#include "config_components.h" - #include "libavutil/attributes.h" #include "libavutil/cpu.h" #include "libavutil/x86/cpu.h" @@ -47,10 +45,16 @@ void ff_avg_pixels16_y2_sse2(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h); void ff_put_no_rnd_pixels8_x2_mmxext(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h); +void ff_put_no_rnd_pixels8_x2_exact_mmxext(uint8_t *block, + const uint8_t *pixels, + ptrdiff_t line_size, int h); void ff_put_pixels8_y2_mmxext(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h); void ff_put_no_rnd_pixels8_y2_mmxext(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h); +void ff_put_no_rnd_pixels8_y2_exact_mmxext(uint8_t *block, + const uint8_t *pixels, + ptrdiff_t line_size, int h); void ff_avg_pixels8_x2_mmxext(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h); void ff_avg_pixels8_y2_mmxext(uint8_t *block, const uint8_t *pixels, @@ -58,11 +62,9 @@ void ff_avg_pixels8_y2_mmxext(uint8_t *block, const uint8_t *pixels, void ff_avg_approx_pixels8_xy2_mmxext(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h); -#define avg_pixels16_mmx ff_avg_pixels16_mmx #define put_pixels8_mmx ff_put_pixels8_mmx #define put_pixels16_mmx ff_put_pixels16_mmx #define put_pixels8_xy2_mmx ff_put_pixels8_xy2_mmx -#define avg_no_rnd_pixels16_mmx ff_avg_pixels16_mmx #define put_no_rnd_pixels8_mmx ff_put_pixels8_mmx #define put_no_rnd_pixels16_mmx ff_put_pixels16_mmx @@ -134,7 +136,6 @@ HPELDSP_AVG_PIXELS16(_mmxext) if (HAVE_MMX_EXTERNAL) \ c->PFX ## _pixels_tab IDX [0] = PFX ## _pixels ## SIZE ## _ ## CPU -#if HAVE_MMX_INLINE #define SET_HPEL_FUNCS03(PFX, IDX, SIZE, CPU) \ do { \ SET_HPEL_FUNCS_EXT(PFX, IDX, SIZE, CPU); \ @@ -145,10 +146,6 @@ HPELDSP_AVG_PIXELS16(_mmxext) c->PFX ## _pixels_tab IDX [1] = PFX ## _pixels ## SIZE ## _x2_ ## CPU; \ c->PFX ## _pixels_tab IDX [2] = PFX ## _pixels ## SIZE ## _y2_ ## CPU; \ } while (0) -#else -#define SET_HPEL_FUNCS03(PFX, IDX, SIZE, CPU) SET_HPEL_FUNCS_EXT(PFX, IDX, SIZE, CPU) -#define SET_HPEL_FUNCS12(PFX, IDX, SIZE, CPU) ((void)0) -#endif #define SET_HPEL_FUNCS(PFX, IDX, SIZE, CPU) \ do { \ SET_HPEL_FUNCS03(PFX, IDX, SIZE, CPU); \ @@ -157,11 +154,14 @@ HPELDSP_AVG_PIXELS16(_mmxext) static void hpeldsp_init_mmx(HpelDSPContext *c, int flags) { +#if HAVE_MMX_INLINE SET_HPEL_FUNCS03(put, [0], 16, mmx); SET_HPEL_FUNCS(put_no_rnd, [0], 16, mmx); - SET_HPEL_FUNCS(avg_no_rnd, , 16, mmx); + SET_HPEL_FUNCS12(avg_no_rnd, , 16, mmx); + c->avg_no_rnd_pixels_tab[3] = avg_no_rnd_pixels16_xy2_mmx; SET_HPEL_FUNCS03(put, [1], 8, mmx); SET_HPEL_FUNCS(put_no_rnd, [1], 8, mmx); +#endif } static void hpeldsp_init_mmxext(HpelDSPContext *c, int flags) @@ -183,6 +183,11 @@ static void hpeldsp_init_mmxext(HpelDSPContext *c, int flags) c->avg_pixels_tab[1][2] = ff_avg_pixels8_y2_mmxext; c->avg_pixels_tab[1][3] = ff_avg_pixels8_xy2_mmxext; + c->put_no_rnd_pixels_tab[1][1] = ff_put_no_rnd_pixels8_x2_exact_mmxext; + c->put_no_rnd_pixels_tab[1][2] = ff_put_no_rnd_pixels8_y2_exact_mmxext; + + c->avg_no_rnd_pixels_tab[0] = ff_avg_pixels16_mmxext; + if (!(flags & AV_CODEC_FLAG_BITEXACT)) { c->put_no_rnd_pixels_tab[0][1] = put_no_rnd_pixels16_x2_mmxext; c->put_no_rnd_pixels_tab[0][2] = put_no_rnd_pixels16_y2_mmxext; @@ -207,6 +212,7 @@ static void hpeldsp_init_sse2_fast(HpelDSPContext *c, int flags) c->avg_pixels_tab[0][1] = ff_avg_pixels16_x2_sse2; c->avg_pixels_tab[0][2] = ff_avg_pixels16_y2_sse2; c->avg_pixels_tab[0][3] = ff_avg_pixels16_xy2_sse2; + c->avg_no_rnd_pixels_tab[0] = ff_avg_pixels16_sse2; #endif /* HAVE_SSE2_EXTERNAL */ } @@ -235,7 +241,4 @@ av_cold void ff_hpeldsp_init_x86(HpelDSPContext *c, int flags) if (EXTERNAL_SSSE3(cpu_flags)) hpeldsp_init_ssse3(c, flags); - - if (CONFIG_VP3_DECODER) - ff_hpeldsp_vp3_init_x86(c, cpu_flags, flags); } diff --git a/libavcodec/x86/hpeldsp_vp3.asm b/libavcodec/x86/hpeldsp_vp3.asm deleted file mode 100644 index e580133e454..00000000000 --- a/libavcodec/x86/hpeldsp_vp3.asm +++ /dev/null @@ -1,99 +0,0 @@ -;****************************************************************************** -;* SIMD-optimized halfpel functions for VP3 -;* -;* 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/x86/x86util.asm" - -SECTION .text - -; void ff_put_no_rnd_pixels8_x2_exact(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h) -INIT_MMX mmxext -cglobal put_no_rnd_pixels8_x2_exact, 4,5 - lea r4, [r2*3] - pcmpeqb m6, m6 -.loop: - mova m0, [r1] - mova m2, [r1+r2] - mova m1, [r1+1] - mova m3, [r1+r2+1] - pxor m0, m6 - pxor m2, m6 - pxor m1, m6 - pxor m3, m6 - PAVGB m0, m1 - PAVGB m2, m3 - pxor m0, m6 - pxor m2, m6 - mova [r0], m0 - mova [r0+r2], m2 - mova m0, [r1+r2*2] - mova m1, [r1+r2*2+1] - mova m2, [r1+r4] - mova m3, [r1+r4+1] - pxor m0, m6 - pxor m1, m6 - pxor m2, m6 - pxor m3, m6 - PAVGB m0, m1 - PAVGB m2, m3 - pxor m0, m6 - pxor m2, m6 - mova [r0+r2*2], m0 - mova [r0+r4], m2 - lea r1, [r1+r2*4] - lea r0, [r0+r2*4] - sub r3d, 4 - jg .loop - RET - - -; void ff_put_no_rnd_pixels8_y2_exact(uint8_t *block, const uint8_t *pixels, ptrdiff_t line_size, int h) -INIT_MMX mmxext -cglobal put_no_rnd_pixels8_y2_exact, 4,5 - lea r4, [r2*3] - mova m0, [r1] - pcmpeqb m6, m6 - add r1, r2 - pxor m0, m6 -.loop: - mova m1, [r1] - mova m2, [r1+r2] - pxor m1, m6 - pxor m2, m6 - PAVGB m0, m1 - PAVGB m1, m2 - pxor m0, m6 - pxor m1, m6 - mova [r0], m0 - mova [r0+r2], m1 - mova m1, [r1+r2*2] - mova m0, [r1+r4] - pxor m1, m6 - pxor m0, m6 - PAVGB m2, m1 - PAVGB m1, m0 - pxor m2, m6 - pxor m1, m6 - mova [r0+r2*2], m2 - mova [r0+r4], m1 - lea r1, [r1+r2*4] - lea r0, [r0+r2*4] - sub r3d, 4 - jg .loop - RET diff --git a/libavcodec/x86/idctdsp_init.c b/libavcodec/x86/idctdsp_init.c index f28a1ad744c..2d165b975ba 100644 --- a/libavcodec/x86/idctdsp_init.c +++ b/libavcodec/x86/idctdsp_init.c @@ -45,10 +45,12 @@ av_cold int ff_init_scantable_permutation_x86(uint8_t *idct_permutation, int i; switch (perm_type) { +#if ARCH_X86_32 case FF_IDCT_PERM_SIMPLE: for (i = 0; i < 64; i++) idct_permutation[i] = simple_mmx_permutation[i]; return 1; +#endif case FF_IDCT_PERM_SSE2: for (i = 0; i < 64; i++) idct_permutation[i] = (i & 0x38) | idct_sse2_row_perm[i & 7]; diff --git a/libavcodec/x86/lossless_audiodsp.asm b/libavcodec/x86/lossless_audiodsp.asm index ff18eb7081f..095f3daf33b 100644 --- a/libavcodec/x86/lossless_audiodsp.asm +++ b/libavcodec/x86/lossless_audiodsp.asm @@ -26,7 +26,7 @@ SECTION .text ; int order, int mul) INIT_XMM sse2 cglobal scalarproduct_and_madd_int16, 4,4,8, v1, v2, v3, order, mul - shl orderq, 1 + shl orderd, 1 movd m7, mulm pshuflw m7, m7, 0 punpcklqdq m7, m7 @@ -62,7 +62,7 @@ INIT_XMM sse4 ; int ff_scalarproduct_and_madd_int32(int16_t *v1, int32_t *v2, int16_t *v3, ; int order, int mul) cglobal scalarproduct_and_madd_int32, 4,4,8, v1, v2, v3, order, mul - shl orderq, 1 + shl orderd, 1 movd m7, mulm SPLATW m7, m7 pxor m6, m6 @@ -140,7 +140,7 @@ align 16 ; int order, int mul) INIT_XMM ssse3 cglobal scalarproduct_and_madd_int16, 4,5,10, v1, v2, v3, order, mul - shl orderq, 1 + shl orderd, 1 movd m7, mulm pshuflw m7, m7, 0 punpcklqdq m7, m7 diff --git a/libavcodec/x86/mpegaudiodsp.c b/libavcodec/x86/mpegaudiodsp.c index 6586fe0726c..43662cd279b 100644 --- a/libavcodec/x86/mpegaudiodsp.c +++ b/libavcodec/x86/mpegaudiodsp.c @@ -45,6 +45,9 @@ void ff_four_imdct36_float_sse(float *out, float *buf, float *in, float *win, void ff_four_imdct36_float_avx(float *out, float *buf, float *in, float *win, float *tmpbuf); +void ff_dct32_float_sse2(float *out, const float *in); +void ff_dct32_float_avx (float *out, const float *in); + DECLARE_ALIGNED(16, static float, mdct_win_sse)[2][4][4*40]; #if HAVE_6REGS && HAVE_SSE_INLINE @@ -267,6 +270,7 @@ av_cold void ff_mpadsp_init_x86(MPADSPContext *s) #if HAVE_SSE if (EXTERNAL_SSE2(cpu_flags)) { s->imdct36_blocks_float = imdct36_blocks_sse2; + s->dct32_float = ff_dct32_float_sse2; } if (EXTERNAL_SSE3(cpu_flags)) { s->imdct36_blocks_float = imdct36_blocks_sse3; @@ -279,6 +283,8 @@ av_cold void ff_mpadsp_init_x86(MPADSPContext *s) if (EXTERNAL_AVX(cpu_flags)) { s->imdct36_blocks_float = imdct36_blocks_avx; } + if (EXTERNAL_AVX_FAST(cpu_flags)) + s->dct32_float = ff_dct32_float_avx; #endif #endif /* HAVE_X86ASM */ } diff --git a/libavcodec/x86/mpegvideoenc_template.c b/libavcodec/x86/mpegvideoenc_template.c index b5b0a5ffa29..5f013be7f5e 100644 --- a/libavcodec/x86/mpegvideoenc_template.c +++ b/libavcodec/x86/mpegvideoenc_template.c @@ -225,7 +225,8 @@ static int RENAME(dct_quantize)(MpegEncContext *s, if(s->mb_intra) block[0]= level; else block[0]= temp_block[0]; - if (s->idsp.perm_type == FF_IDCT_PERM_SIMPLE) { + av_assert2(ARCH_X86_32 || s->idsp.perm_type != FF_IDCT_PERM_SIMPLE); + if (ARCH_X86_32 && s->idsp.perm_type == FF_IDCT_PERM_SIMPLE) { if(last_non_zero_p1 <= 1) goto end; block[0x08] = temp_block[0x01]; block[0x10] = temp_block[0x08]; block[0x20] = temp_block[0x10]; diff --git a/libavcodec/x86/proresdsp_init.c b/libavcodec/x86/proresdsp_init.c index bde79ab8c07..f7abbfa692a 100644 --- a/libavcodec/x86/proresdsp_init.c +++ b/libavcodec/x86/proresdsp_init.c @@ -30,12 +30,12 @@ void ff_prores_idct_put_10_sse2(uint16_t *dst, ptrdiff_t linesize, void ff_prores_idct_put_10_avx (uint16_t *dst, ptrdiff_t linesize, int16_t *block, const int16_t *qmat); -av_cold void ff_proresdsp_init_x86(ProresDSPContext *dsp, AVCodecContext *avctx) +av_cold void ff_proresdsp_init_x86(ProresDSPContext *dsp, int bits_per_raw_sample) { #if ARCH_X86_64 int cpu_flags = av_get_cpu_flags(); - if (avctx->bits_per_raw_sample == 10){ + if (bits_per_raw_sample == 10) { if (EXTERNAL_SSE2(cpu_flags)) { dsp->idct_permutation_type = FF_IDCT_PERM_TRANSPOSE; dsp->idct_put = ff_prores_idct_put_10_sse2; diff --git a/libavcodec/x86/rv34dsp.asm b/libavcodec/x86/rv34dsp.asm index f29bfd715c7..01a3d6f590d 100644 --- a/libavcodec/x86/rv34dsp.asm +++ b/libavcodec/x86/rv34dsp.asm @@ -113,15 +113,15 @@ cglobal rv34_idct_dc_noround, 1, 2, 0 movd %1, %2 %endmacro INIT_MMX mmxext -cglobal rv34_idct_add, 3,3,0, d, s, b - ROW_TRANSFORM bq - COL_TRANSFORM [dq], mm0, [pw_col_coeffs+ 0], [pw_col_coeffs+ 8] - mova mm0, [pw_col_coeffs+ 0] - COL_TRANSFORM [dq+sq], mm4, mm0, [pw_col_coeffs+ 8] - mova mm4, [pw_col_coeffs+ 8] - lea dq, [dq + 2*sq] - COL_TRANSFORM [dq], mm6, mm0, mm4 - COL_TRANSFORM [dq+sq], mm7, mm0, mm4 +cglobal rv34_idct_add, 3, 3, 0, dst, s, b + ROW_TRANSFORM bq + COL_TRANSFORM [dstq], mm0, [pw_col_coeffs+ 0], [pw_col_coeffs+ 8] + mova mm0, [pw_col_coeffs+ 0] + COL_TRANSFORM [dstq+sq], mm4, mm0, [pw_col_coeffs+ 8] + mova mm4, [pw_col_coeffs+ 8] + lea dstq, [dstq + 2*sq] + COL_TRANSFORM [dstq], mm6, mm0, mm4 + COL_TRANSFORM [dstq+sq], mm7, mm0, mm4 ret ; ff_rv34_idct_dc_add_sse4(uint8_t *dst, int stride, int dc); diff --git a/libavcodec/x86/rv40dsp.asm b/libavcodec/x86/rv40dsp.asm index e02ad2c63f1..dc520dbeb4d 100644 --- a/libavcodec/x86/rv40dsp.asm +++ b/libavcodec/x86/rv40dsp.asm @@ -51,7 +51,7 @@ sixtap_filter_v_m: times 8 dw 1 times 8 dw 20 times 8 dw 52 -%ifdef PIC +%if PIC %define sixtap_filter_hw picregq %define sixtap_filter_hb picregq %define sixtap_filter_v picregq @@ -84,7 +84,7 @@ SECTION .text %if WIN64 movsxd %1q, %1d %endif -%ifdef PIC +%if PIC add %1q, picregq %else add %1q, %2 @@ -104,7 +104,7 @@ SECTION .text %macro FILTER_V 1 cglobal %1_rv40_qpel_v, 6,6+npicregs,12, dst, dststride, src, srcstride, height, my, picreg -%ifdef PIC +%if PIC lea picregq, [sixtap_filter_v_m] %endif pxor m7, m7 @@ -175,7 +175,7 @@ cglobal %1_rv40_qpel_v, 6,6+npicregs,12, dst, dststride, src, srcstride, height, %macro FILTER_H 1 cglobal %1_rv40_qpel_h, 6, 6+npicregs, 12, dst, dststride, src, srcstride, height, mx, picreg -%ifdef PIC +%if PIC lea picregq, [sixtap_filter_v_m] %endif pxor m7, m7 @@ -238,7 +238,7 @@ FILTER_V avg %macro FILTER_SSSE3 1 cglobal %1_rv40_qpel_v, 6,6+npicregs,8, dst, dststride, src, srcstride, height, my, picreg -%ifdef PIC +%if PIC lea picregq, [sixtap_filter_hb_m] %endif @@ -283,7 +283,7 @@ cglobal %1_rv40_qpel_v, 6,6+npicregs,8, dst, dststride, src, srcstride, height, RET cglobal %1_rv40_qpel_h, 6,6+npicregs,8, dst, dststride, src, srcstride, height, mx, picreg -%ifdef PIC +%if PIC lea picregq, [sixtap_filter_hb_m] %endif mova m3, [filter_h6_shuf2] @@ -401,15 +401,6 @@ FILTER_SSSE3 avg %macro MAIN_LOOP 2 -%if mmsize == 8 - RV40_WCORE %2, r0, r1, r2 -%if %1 == 16 - RV40_WCORE %2, r0 + 8, r1 + 8, r2 + 8 -%endif - - ; Prepare for next loop - add r6, r5 -%else %ifidn %1, 8 RV40_WCORE %2, r0, r1, r2, r5 ; Prepare 2 next lines @@ -419,7 +410,6 @@ FILTER_SSSE3 avg ; Prepare single next line add r6, r5 %endif -%endif %endmacro diff --git a/libavcodec/x86/sbrdsp.asm b/libavcodec/x86/sbrdsp.asm index d02f70d7043..63e9f0d33ae 100644 --- a/libavcodec/x86/sbrdsp.asm +++ b/libavcodec/x86/sbrdsp.asm @@ -308,7 +308,7 @@ cglobal sbr_qmf_pre_shuffle, 1,4,6,z movq [r2q], m2 RET -%ifdef PIC +%if PIC %define NREGS 1 %if UNIX64 %define NOISE_TABLE r6q ; r5q is m_max @@ -321,7 +321,7 @@ cglobal sbr_qmf_pre_shuffle, 1,4,6,z %endif %macro LOAD_NST 1 -%ifdef PIC +%if PIC lea NOISE_TABLE, [%1] mova m0, [kxq + NOISE_TABLE] %else @@ -371,7 +371,7 @@ apply_noise_main: movsxdifnidn noiseq, noised dec noiseq shl countd, 2 -%ifdef PIC +%if PIC lea NOISE_TABLE, [sbr_noise_table] %endif lea Yq, [Yq + 2*countq] diff --git a/libavcodec/x86/simple_idct.asm b/libavcodec/x86/simple_idct.asm index 982b2f0bbba..c79519372a7 100644 --- a/libavcodec/x86/simple_idct.asm +++ b/libavcodec/x86/simple_idct.asm @@ -783,68 +783,33 @@ SECTION .text %macro PUT_PIXELS_CLAMPED_HALF 1 mova m0, [blockq+mmsize*0+%1] mova m1, [blockq+mmsize*2+%1] -%if mmsize == 8 - mova m2, [blockq+mmsize*4+%1] - mova m3, [blockq+mmsize*6+%1] -%endif packuswb m0, [blockq+mmsize*1+%1] packuswb m1, [blockq+mmsize*3+%1] -%if mmsize == 8 - packuswb m2, [blockq+mmsize*5+%1] - packuswb m3, [blockq+mmsize*7+%1] - movq [pixelsq], m0 - movq [lsizeq+pixelsq], m1 - movq [2*lsizeq+pixelsq], m2 - movq [lsize3q+pixelsq], m3 -%else movq [pixelsq], m0 movhps [lsizeq+pixelsq], m0 movq [2*lsizeq+pixelsq], m1 movhps [lsize3q+pixelsq], m1 -%endif %endmacro %macro ADD_PIXELS_CLAMPED 1 mova m0, [blockq+mmsize*0+%1] mova m1, [blockq+mmsize*1+%1] -%if mmsize == 8 - mova m5, [blockq+mmsize*2+%1] - mova m6, [blockq+mmsize*3+%1] -%endif movq m2, [pixelsq] movq m3, [pixelsq+lsizeq] -%if mmsize == 8 - mova m7, m2 - punpcklbw m2, m4 - punpckhbw m7, m4 - paddsw m0, m2 - paddsw m1, m7 - mova m7, m3 - punpcklbw m3, m4 - punpckhbw m7, m4 - paddsw m5, m3 - paddsw m6, m7 -%else punpcklbw m2, m4 punpcklbw m3, m4 paddsw m0, m2 paddsw m1, m3 -%endif packuswb m0, m1 -%if mmsize == 8 - packuswb m5, m6 - movq [pixelsq], m0 - movq [pixelsq+lsizeq], m5 -%else movq [pixelsq], m0 movhps [pixelsq+lsizeq], m0 -%endif %endmacro INIT_MMX mmx cglobal simple_idct, 1, 2, 8, 128, block, t0 IDCT + emms RET INIT_XMM sse2 diff --git a/libavcodec/x86/snowdsp.c b/libavcodec/x86/snowdsp.c index bca1f9bd2ed..bd0aa766e57 100644 --- a/libavcodec/x86/snowdsp.c +++ b/libavcodec/x86/snowdsp.c @@ -24,7 +24,6 @@ #include "libavutil/attributes.h" #include "libavutil/cpu.h" #include "libavutil/x86/asm.h" -#include "libavcodec/snow.h" #include "libavcodec/snow_dwt.h" #if HAVE_INLINE_ASM diff --git a/libavcodec/x86/takdsp.asm b/libavcodec/x86/takdsp.asm index be8e1ab5533..c9aec571145 100644 --- a/libavcodec/x86/takdsp.asm +++ b/libavcodec/x86/takdsp.asm @@ -28,7 +28,7 @@ pd_128: times 4 dd 128 SECTION .text -INIT_XMM sse2 +%macro TAK_DECORRELATE 0 cglobal tak_decorrelate_ls, 3, 3, 2, p1, p2, length shl lengthd, 2 add p1q, lengthq @@ -73,10 +73,8 @@ cglobal tak_decorrelate_sm, 3, 3, 6, p1, p2, length mova m1, [p2q+lengthq] mova m3, [p1q+lengthq+mmsize] mova m4, [p2q+lengthq+mmsize] - mova m2, m1 - mova m5, m4 - psrad m2, 1 - psrad m5, 1 + psrad m2, m1, 1 + psrad m5, m4, 1 psubd m0, m2 psubd m3, m5 paddd m1, m0 @@ -88,29 +86,48 @@ cglobal tak_decorrelate_sm, 3, 3, 6, p1, p2, length add lengthq, mmsize*2 jl .loop RET +%endmacro -INIT_XMM sse4 +INIT_XMM sse2 +TAK_DECORRELATE +%if HAVE_AVX2_EXTERNAL +INIT_YMM avx2 +TAK_DECORRELATE +%endif + +%macro TAK_DECORRELATE_SF 0 cglobal tak_decorrelate_sf, 3, 3, 5, p1, p2, length, dshift, dfactor shl lengthd, 2 add p1q, lengthq add p2q, lengthq neg lengthq - movd m2, dshiftm - movd m3, dfactorm - pshufd m3, m3, 0 - mova m4, [pd_128] + movd xm2, dshiftm +%if UNIX64 + movd xm3, dfactorm + VPBROADCASTD m3, xm3 +%else + VPBROADCASTD m3, dfactorm +%endif + VBROADCASTI128 m4, [pd_128] .loop: - mova m0, [p1q+lengthq] mova m1, [p2q+lengthq] - psrad m1, m2 + psrad m1, xm2 pmulld m1, m3 paddd m1, m4 psrad m1, 8 - pslld m1, m2 - psubd m1, m0 + pslld m1, xm2 + psubd m1, [p1q+lengthq] mova [p1q+lengthq], m1 add lengthq, mmsize jl .loop RET +%endmacro + +INIT_XMM sse4 +TAK_DECORRELATE_SF +%if HAVE_AVX2_EXTERNAL +INIT_YMM avx2 +TAK_DECORRELATE_SF +%endif diff --git a/libavcodec/x86/takdsp_init.c b/libavcodec/x86/takdsp_init.c index b2e6e639ee4..9553f8442c7 100644 --- a/libavcodec/x86/takdsp_init.c +++ b/libavcodec/x86/takdsp_init.c @@ -23,10 +23,14 @@ #include "libavutil/x86/cpu.h" #include "config.h" -void ff_tak_decorrelate_ls_sse2(int32_t *p1, int32_t *p2, int length); -void ff_tak_decorrelate_sr_sse2(int32_t *p1, int32_t *p2, int length); +void ff_tak_decorrelate_ls_sse2(const int32_t *p1, int32_t *p2, int length); +void ff_tak_decorrelate_ls_avx2(const int32_t *p1, int32_t *p2, int length); +void ff_tak_decorrelate_sr_sse2(int32_t *p1, const int32_t *p2, int length); +void ff_tak_decorrelate_sr_avx2(int32_t *p1, const int32_t *p2, int length); void ff_tak_decorrelate_sm_sse2(int32_t *p1, int32_t *p2, int length); -void ff_tak_decorrelate_sf_sse4(int32_t *p1, int32_t *p2, int length, int dshift, int dfactor); +void ff_tak_decorrelate_sm_avx2(int32_t *p1, int32_t *p2, int length); +void ff_tak_decorrelate_sf_sse4(int32_t *p1, const int32_t *p2, int length, int dshift, int dfactor); +void ff_tak_decorrelate_sf_avx2(int32_t *p1, const int32_t *p2, int length, int dshift, int dfactor); av_cold void ff_takdsp_init_x86(TAKDSPContext *c) { @@ -42,5 +46,12 @@ av_cold void ff_takdsp_init_x86(TAKDSPContext *c) if (EXTERNAL_SSE4(cpu_flags)) { c->decorrelate_sf = ff_tak_decorrelate_sf_sse4; } + + if (EXTERNAL_AVX2_FAST(cpu_flags)) { + c->decorrelate_ls = ff_tak_decorrelate_ls_avx2; + c->decorrelate_sr = ff_tak_decorrelate_sr_avx2; + c->decorrelate_sm = ff_tak_decorrelate_sm_avx2; + c->decorrelate_sf = ff_tak_decorrelate_sf_avx2; + } #endif } diff --git a/libavcodec/x86/vp3dsp_init.c b/libavcodec/x86/vp3dsp_init.c index f54fa57b3e4..edac1764cb0 100644 --- a/libavcodec/x86/vp3dsp_init.c +++ b/libavcodec/x86/vp3dsp_init.c @@ -53,7 +53,7 @@ av_cold void ff_vp3dsp_init_x86(VP3DSPContext *c, int flags) if (!(flags & AV_CODEC_FLAG_BITEXACT)) { c->v_loop_filter = c->v_loop_filter_unaligned = ff_vp3_v_loop_filter_mmxext; - c->h_loop_filter = c->v_loop_filter_unaligned = ff_vp3_h_loop_filter_mmxext; + c->h_loop_filter = c->h_loop_filter_unaligned = ff_vp3_h_loop_filter_mmxext; } } diff --git a/libavcodec/x86/vp6dsp.asm b/libavcodec/x86/vp6dsp.asm index 512fe89def8..01065417343 100644 --- a/libavcodec/x86/vp6dsp.asm +++ b/libavcodec/x86/vp6dsp.asm @@ -27,44 +27,6 @@ cextern pw_64 SECTION .text %macro DIAG4 6 -%if mmsize == 8 - movq m0, [%1+%2] - movq m1, [%1+%3] - movq m3, m0 - movq m4, m1 - punpcklbw m0, m7 - punpcklbw m1, m7 - punpckhbw m3, m7 - punpckhbw m4, m7 - pmullw m0, [rsp+8*11] ; src[x-8 ] * biweight [0] - pmullw m1, [rsp+8*12] ; src[x ] * biweight [1] - pmullw m3, [rsp+8*11] ; src[x-8 ] * biweight [0] - pmullw m4, [rsp+8*12] ; src[x ] * biweight [1] - paddw m0, m1 - paddw m3, m4 - movq m1, [%1+%4] - movq m2, [%1+%5] - movq m4, m1 - movq m5, m2 - punpcklbw m1, m7 - punpcklbw m2, m7 - punpckhbw m4, m7 - punpckhbw m5, m7 - pmullw m1, [rsp+8*13] ; src[x+8 ] * biweight [2] - pmullw m2, [rsp+8*14] ; src[x+16] * biweight [3] - pmullw m4, [rsp+8*13] ; src[x+8 ] * biweight [2] - pmullw m5, [rsp+8*14] ; src[x+16] * biweight [3] - paddw m1, m2 - paddw m4, m5 - paddsw m0, m1 - paddsw m3, m4 - paddsw m0, m6 ; Add 64 - paddsw m3, m6 ; Add 64 - psraw m0, 7 - psraw m3, 7 - packuswb m0, m3 - movq [%6], m0 -%else ; mmsize == 16 movq m0, [%1+%2] movq m1, [%1+%3] punpcklbw m0, m7 @@ -84,25 +46,9 @@ SECTION .text psraw m0, 7 packuswb m0, m0 movq [%6], m0 -%endif ; mmsize == 8/16 %endmacro %macro SPLAT4REGS 0 -%if mmsize == 8 - movq m5, m3 - punpcklwd m3, m3 - movq m4, m3 - punpckldq m3, m3 - punpckhdq m4, m4 - punpckhwd m5, m5 - movq m2, m5 - punpckhdq m2, m2 - punpckldq m5, m5 - movq [rsp+8*11], m3 - movq [rsp+8*12], m4 - movq [rsp+8*13], m5 - movq [rsp+8*14], m2 -%else ; mmsize == 16 pshuflw m4, m3, 0x0 pshuflw m5, m3, 0x55 pshuflw m6, m3, 0xAA @@ -111,7 +57,6 @@ SECTION .text punpcklqdq m5, m5 punpcklqdq m6, m6 punpcklqdq m3, m3 -%endif ; mmsize == 8/16 %endmacro ; void ff_vp6_filter_diag4_(uint8_t *dst, uint8_t *src, ptrdiff_t stride, diff --git a/libavcodec/x86/vp8dsp.asm b/libavcodec/x86/vp8dsp.asm index 6ac5a7721bf..231c21ea0dc 100644 --- a/libavcodec/x86/vp8dsp.asm +++ b/libavcodec/x86/vp8dsp.asm @@ -114,7 +114,7 @@ bilinear_filter_vb_m: times 8 db 7, 1 times 8 db 2, 6 times 8 db 1, 7 -%ifdef PIC +%if PIC %define fourtap_filter_hw picregq %define sixtap_filter_hw picregq %define fourtap_filter_hb picregq @@ -166,7 +166,7 @@ cglobal put_vp8_epel%1_h6, 6, 6 + npicregs, 8, dst, dststride, src, srcstride, h lea mxd, [mxq*3] mova m3, [filter_h6_shuf2] mova m4, [filter_h6_shuf3] -%ifdef PIC +%if PIC lea picregq, [sixtap_filter_hb_m] %endif mova m5, [sixtap_filter_hb+mxq*8-48] ; set up 6tap filter in bytes @@ -207,7 +207,7 @@ cglobal put_vp8_epel%1_h4, 6, 6 + npicregs, 7, dst, dststride, src, srcstride, h mova m2, [pw_256] mova m3, [filter_h2_shuf] mova m4, [filter_h4_shuf] -%ifdef PIC +%if PIC lea picregq, [fourtap_filter_hb_m] %endif mova m5, [fourtap_filter_hb+mxq-16] ; set up 4tap filter in bytes @@ -234,7 +234,7 @@ cglobal put_vp8_epel%1_h4, 6, 6 + npicregs, 7, dst, dststride, src, srcstride, h cglobal put_vp8_epel%1_v4, 7, 7, 8, dst, dststride, src, srcstride, height, picreg, my shl myd, 4 -%ifdef PIC +%if PIC lea picregq, [fourtap_filter_hb_m] %endif mova m5, [fourtap_filter_hb+myq-16] @@ -272,7 +272,7 @@ cglobal put_vp8_epel%1_v4, 7, 7, 8, dst, dststride, src, srcstride, height, picr cglobal put_vp8_epel%1_v6, 7, 7, 8, dst, dststride, src, srcstride, height, picreg, my lea myd, [myq*3] -%ifdef PIC +%if PIC lea picregq, [sixtap_filter_hb_m] %endif lea myq, [sixtap_filter_hb+myq*8] @@ -326,7 +326,7 @@ FILTER_SSSE3 8 INIT_MMX mmxext cglobal put_vp8_epel4_h4, 6, 6 + npicregs, 0, dst, dststride, src, srcstride, height, mx, picreg shl mxd, 4 -%ifdef PIC +%if PIC lea picregq, [fourtap_filter_hw_m] %endif movq mm4, [fourtap_filter_hw+mxq-16] ; set up 4tap filter in words @@ -374,7 +374,7 @@ cglobal put_vp8_epel4_h4, 6, 6 + npicregs, 0, dst, dststride, src, srcstride, he INIT_MMX mmxext cglobal put_vp8_epel4_h6, 6, 6 + npicregs, 0, dst, dststride, src, srcstride, height, mx, picreg lea mxd, [mxq*3] -%ifdef PIC +%if PIC lea picregq, [sixtap_filter_hw_m] %endif movq mm4, [sixtap_filter_hw+mxq*8-48] ; set up 4tap filter in words @@ -431,7 +431,7 @@ cglobal put_vp8_epel4_h6, 6, 6 + npicregs, 0, dst, dststride, src, srcstride, he INIT_XMM sse2 cglobal put_vp8_epel8_h4, 6, 6 + npicregs, 10, dst, dststride, src, srcstride, height, mx, picreg shl mxd, 5 -%ifdef PIC +%if PIC lea picregq, [fourtap_filter_v_m] %endif lea mxq, [fourtap_filter_v+mxq-32] @@ -480,7 +480,7 @@ INIT_XMM sse2 cglobal put_vp8_epel8_h6, 6, 6 + npicregs, 14, dst, dststride, src, srcstride, height, mx, picreg lea mxd, [mxq*3] shl mxd, 4 -%ifdef PIC +%if PIC lea picregq, [sixtap_filter_v_m] %endif lea mxq, [sixtap_filter_v+mxq-96] @@ -543,7 +543,7 @@ cglobal put_vp8_epel8_h6, 6, 6 + npicregs, 14, dst, dststride, src, srcstride, h ; 4x4 block, V-only 4-tap filter cglobal put_vp8_epel%1_v4, 7, 7, 8, dst, dststride, src, srcstride, height, picreg, my shl myd, 5 -%ifdef PIC +%if PIC lea picregq, [fourtap_filter_v_m] %endif lea myq, [fourtap_filter_v+myq-32] @@ -597,7 +597,7 @@ cglobal put_vp8_epel%1_v4, 7, 7, 8, dst, dststride, src, srcstride, height, picr cglobal put_vp8_epel%1_v6, 7, 7, 8, dst, dststride, src, srcstride, height, picreg, my shl myd, 4 lea myq, [myq*3] -%ifdef PIC +%if PIC lea picregq, [sixtap_filter_v_m] %endif lea myq, [sixtap_filter_v+myq-96] @@ -667,7 +667,7 @@ FILTER_V 8 %if cpuflag(ssse3) cglobal put_vp8_bilinear%1_v, 7, 7, 5, dst, dststride, src, srcstride, height, picreg, my shl myd, 4 -%ifdef PIC +%if PIC lea picregq, [bilinear_filter_vb_m] %endif pxor m4, m4 @@ -697,7 +697,7 @@ cglobal put_vp8_bilinear%1_v, 7, 7, 5, dst, dststride, src, srcstride, height, p %else ; cpuflag(ssse3) cglobal put_vp8_bilinear%1_v, 7, 7, 7, dst, dststride, src, srcstride, height, picreg, my shl myd, 4 -%ifdef PIC +%if PIC lea picregq, [bilinear_filter_vw_m] %endif pxor m6, m6 @@ -743,7 +743,7 @@ cglobal put_vp8_bilinear%1_v, 7, 7, 7, dst, dststride, src, srcstride, height, p %if cpuflag(ssse3) cglobal put_vp8_bilinear%1_h, 6, 6 + npicregs, 5, dst, dststride, src, srcstride, height, mx, picreg shl mxd, 4 -%ifdef PIC +%if PIC lea picregq, [bilinear_filter_vb_m] %endif pxor m4, m4 @@ -773,7 +773,7 @@ cglobal put_vp8_bilinear%1_h, 6, 6 + npicregs, 5, dst, dststride, src, srcstride %else ; cpuflag(ssse3) cglobal put_vp8_bilinear%1_h, 6, 6 + npicregs, 7, dst, dststride, src, srcstride, height, mx, picreg shl mxd, 4 -%ifdef PIC +%if PIC lea picregq, [bilinear_filter_vw_m] %endif pxor m6, m6 diff --git a/libavcodec/x86/vp9itxfm.asm b/libavcodec/x86/vp9itxfm.asm index 2c63fe514a3..2f290f2f883 100644 --- a/libavcodec/x86/vp9itxfm.asm +++ b/libavcodec/x86/vp9itxfm.asm @@ -330,7 +330,9 @@ IDCT_4x4_FN ssse3 INIT_MMX %5 cglobal vp9_%1_%3_4x4_add, 3, 3, 0, dst, stride, block, eob %if WIN64 && notcpuflag(ssse3) +INIT_XMM cpuname WIN64_SPILL_XMM 8 +INIT_MMX cpuname %endif movdqa xmm5, [pd_8192] mova m0, [blockq+ 0] diff --git a/libavcodec/x86/vp9itxfm_16bpp.asm b/libavcodec/x86/vp9itxfm_16bpp.asm index 902685edf6b..ebe6222285b 100644 --- a/libavcodec/x86/vp9itxfm_16bpp.asm +++ b/libavcodec/x86/vp9itxfm_16bpp.asm @@ -303,7 +303,9 @@ IDCT4_10_FN %macro IADST4_FN 4 cglobal vp9_%1_%3_4x4_add_10, 3, 3, 0, dst, stride, block, eob %if WIN64 && notcpuflag(ssse3) +INIT_XMM cpuname WIN64_SPILL_XMM 8 +INIT_MMX cpuname %endif movdqa xmm5, [pd_8192] mova m0, [blockq+0*16+0] @@ -672,7 +674,7 @@ cglobal vp9_idct_idct_8x8_add_10, 4, 6 + ARCH_X86_64, 14, \ mov dstbakq, dstq movsxd cntq, cntd %endif -%ifdef PIC +%if PIC lea ptrq, [default_8x8] movzx cntd, byte [ptrq+cntq-1] %else @@ -921,7 +923,7 @@ cglobal vp9_%1_%3_8x8_add_10, 4, 6 + ARCH_X86_64, 16, \ mov dstbakq, dstq movsxd cntq, cntd %endif -%ifdef PIC +%if PIC lea ptrq, [%5_8x8] movzx cntd, byte [ptrq+cntq-1] %else @@ -1128,7 +1130,7 @@ cglobal vp9_idct_idct_16x16_add_10, 4, 6 + ARCH_X86_64, 16, \ mov dstbakq, dstq movsxd cntq, cntd %endif -%ifdef PIC +%if PIC lea ptrq, [default_16x16] movzx cntd, byte [ptrq+cntq-1] %else @@ -1445,7 +1447,7 @@ cglobal vp9_%1_%4_16x16_add_10, 4, 6 + ARCH_X86_64, 16, \ mov dstbakq, dstq movsxd cntq, cntd %endif -%ifdef PIC +%if PIC lea ptrq, [%7_16x16] movzx cntd, byte [ptrq+cntq-1] %else @@ -1958,7 +1960,7 @@ cglobal vp9_idct_idct_32x32_add_10, 4, 6 + ARCH_X86_64, 16, \ mov dstbakq, dstq movsxd cntq, cntd %endif -%ifdef PIC +%if PIC lea ptrq, [default_32x32] movzx cntd, byte [ptrq+cntq-1] %else diff --git a/libavcodec/x86/vvc/Makefile b/libavcodec/x86/vvc/Makefile new file mode 100644 index 00000000000..d1623bd46a7 --- /dev/null +++ b/libavcodec/x86/vvc/Makefile @@ -0,0 +1,7 @@ +clean:: + $(RM) $(CLEANSUFFIXES:%=libavcodec/x86/vvc/%) $(CLEANSUFFIXES:%=libavcodec/x86/h26x/%) + +OBJS-$(CONFIG_VVC_DECODER) += x86/vvc/vvcdsp_init.o \ + x86/h26x/h2656dsp.o +X86ASM-OBJS-$(CONFIG_VVC_DECODER) += x86/vvc/vvc_mc.o \ + x86/h26x/h2656_inter.o diff --git a/libavcodec/x86/vvc/vvc_mc.asm b/libavcodec/x86/vvc/vvc_mc.asm new file mode 100644 index 00000000000..30aa97c65a7 --- /dev/null +++ b/libavcodec/x86/vvc/vvc_mc.asm @@ -0,0 +1,302 @@ +; /* +; * Provide SIMD MC functions for VVC decoding +; * +; * Copyright © 2021, VideoLAN and dav1d authors +; * Copyright © 2021, Two Orioles, LLC +; * All rights reserved. +; * +; * Copyright (c) 2023-2024 Nuo Mi +; * Copyright (c) 2023-2024 Wu Jianhua +; * +; * 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/x86/x86util.asm" + +%define MAX_PB_SIZE 128 + +SECTION_RODATA 32 + +%if ARCH_X86_64 + +%if HAVE_AVX2_EXTERNAL + +pw_0 times 2 dw 0 +pw_1 times 2 dw 1 +pw_4 times 2 dw 4 +pw_12 times 2 dw 12 +pw_256 times 2 dw 256 + +%macro AVG_JMP_TABLE 3-* + %xdefine %1_%2_%3_table (%%table - 2*%4) + %xdefine %%base %1_%2_%3_table + %xdefine %%prefix mangle(private_prefix %+ _vvc_%1_%2bpc_%3) + %%table: + %rep %0 - 3 + dd %%prefix %+ .w%4 - %%base + %rotate 1 + %endrep +%endmacro + +AVG_JMP_TABLE avg, 8, avx2, 2, 4, 8, 16, 32, 64, 128 +AVG_JMP_TABLE avg, 16, avx2, 2, 4, 8, 16, 32, 64, 128 +AVG_JMP_TABLE w_avg, 8, avx2, 2, 4, 8, 16, 32, 64, 128 +AVG_JMP_TABLE w_avg, 16, avx2, 2, 4, 8, 16, 32, 64, 128 + +SECTION .text + +%macro AVG_W16_FN 3 ; bpc, op, count + %assign %%i 0 + %rep %3 + %define off %%i + AVG_LOAD_W16 0, off + %2 + AVG_SAVE_W16 %1, 0, off + + + AVG_LOAD_W16 1, off + %2 + AVG_SAVE_W16 %1, 1, off + + %assign %%i %%i+1 + %endrep +%endmacro + +%macro AVG_FN 2 ; bpc, op + jmp wq + +.w2: + movd xm0, [src0q] + pinsrd xm0, [src0q + AVG_SRC_STRIDE], 1 + movd xm1, [src1q] + pinsrd xm1, [src1q + AVG_SRC_STRIDE], 1 + %2 + AVG_SAVE_W2 %1 + AVG_LOOP_END .w2 + +.w4: + movq xm0, [src0q] + pinsrq xm0, [src0q + AVG_SRC_STRIDE], 1 + movq xm1, [src1q] + pinsrq xm1, [src1q + AVG_SRC_STRIDE], 1 + %2 + AVG_SAVE_W4 %1 + + AVG_LOOP_END .w4 + +.w8: + vinserti128 m0, m0, [src0q], 0 + vinserti128 m0, m0, [src0q + AVG_SRC_STRIDE], 1 + vinserti128 m1, m1, [src1q], 0 + vinserti128 m1, m1, [src1q + AVG_SRC_STRIDE], 1 + %2 + AVG_SAVE_W8 %1 + + AVG_LOOP_END .w8 + +.w16: + AVG_W16_FN %1, %2, 1 + + AVG_LOOP_END .w16 + +.w32: + AVG_W16_FN %1, %2, 2 + + AVG_LOOP_END .w32 + +.w64: + AVG_W16_FN %1, %2, 4 + + AVG_LOOP_END .w64 + +.w128: + AVG_W16_FN %1, %2, 8 + + AVG_LOOP_END .w128 + +.ret: + RET +%endmacro + +%macro AVG 0 + paddsw m0, m1 + pmulhrsw m0, m2 + CLIPW m0, m3, m4 +%endmacro + +%macro W_AVG 0 + punpckhwd m5, m0, m1 + pmaddwd m5, m3 + paddd m5, m4 + psrad m5, xm2 + + punpcklwd m0, m0, m1 + pmaddwd m0, m3 + paddd m0, m4 + psrad m0, xm2 + + packssdw m0, m5 + CLIPW m0, m6, m7 +%endmacro + +%macro AVG_LOAD_W16 2 ; line, offset + movu m0, [src0q + %1 * AVG_SRC_STRIDE + %2 * 32] + movu m1, [src1q + %1 * AVG_SRC_STRIDE + %2 * 32] +%endmacro + +%macro AVG_SAVE_W2 1 ;bpc + %if %1 == 16 + pextrd [dstq], xm0, 0 + pextrd [dstq + strideq], xm0, 1 + %else + packuswb m0, m0 + pextrw [dstq], xm0, 0 + pextrw [dstq + strideq], xm0, 1 + %endif +%endmacro + +%macro AVG_SAVE_W4 1 ;bpc + %if %1 == 16 + pextrq [dstq], xm0, 0 + pextrq [dstq + strideq], xm0, 1 + %else + packuswb m0, m0 + pextrd [dstq], xm0, 0 + pextrd [dstq + strideq], xm0, 1 + %endif +%endmacro + +%macro AVG_SAVE_W8 1 ;bpc + %if %1 == 16 + vextracti128 [dstq], m0, 0 + vextracti128 [dstq + strideq], m0, 1 + %else + packuswb m0, m0 + vpermq m0, m0, 1000b + pextrq [dstq], xm0, 0 + pextrq [dstq + strideq], xm0, 1 + %endif +%endmacro + +%macro AVG_SAVE_W16 3 ; bpc, line, offset + %if %1 == 16 + movu [dstq + %2 * strideq + %3 * 32], m0 + %else + packuswb m0, m0 + vpermq m0, m0, 1000b + vextracti128 [dstq + %2 * strideq + %3 * 16], m0, 0 + %endif +%endmacro + +%macro AVG_LOOP_END 1 + sub hd, 2 + je .ret + + lea src0q, [src0q + 2 * AVG_SRC_STRIDE] + lea src1q, [src1q + 2 * AVG_SRC_STRIDE] + lea dstq, [dstq + 2 * strideq] + jmp %1 +%endmacro + +%define AVG_SRC_STRIDE MAX_PB_SIZE*2 + +;void ff_vvc_avg_%1bpc_avx2(uint8_t *dst, ptrdiff_t dst_stride, +; const int16_t *src0, const int16_t *src1, intptr_t width, intptr_t height, intptr_t pixel_max); +%macro VVC_AVG_AVX2 1 +cglobal vvc_avg_%1bpc, 4, 7, 5, dst, stride, src0, src1, w, h, bd + movifnidn hd, hm + + pxor m3, m3 ; pixel min + vpbroadcastw m4, bdm ; pixel max + + movifnidn bdd, bdm + inc bdd + tzcnt bdd, bdd ; bit depth + + sub bdd, 8 + movd xm0, bdd + vpbroadcastd m1, [pw_4] + pminuw m0, m1 + vpbroadcastd m2, [pw_256] + psllw m2, xm0 ; shift + + lea r6, [avg_%1 %+ SUFFIX %+ _table] + tzcnt wd, wm + movsxd wq, dword [r6+wq*4] + add wq, r6 + AVG_FN %1, AVG +%endmacro + +;void ff_vvc_w_avg_%1bpc_avx(uint8_t *dst, ptrdiff_t dst_stride, +; const int16_t *src0, const int16_t *src1, intptr_t width, intptr_t height, +; intptr_t denom, intptr_t w0, intptr_t w1, intptr_t o0, intptr_t o1, intptr_t pixel_max); +%macro VVC_W_AVG_AVX2 1 +cglobal vvc_w_avg_%1bpc, 4, 8, 8, dst, stride, src0, src1, w, h, t0, t1 + + movifnidn hd, hm + + movifnidn t0d, r8m ; w1 + shl t0d, 16 + mov t0w, r7m ; w0 + movd xm3, t0d + vpbroadcastd m3, xm3 ; w0, w1 + + pxor m6, m6 ;pixel min + vpbroadcastw m7, r11m ;pixel max + + mov t1q, rcx ; save ecx + mov ecx, r11m + inc ecx ; bd + tzcnt ecx, ecx + sub ecx, 8 + mov t0d, r9m ; o0 + add t0d, r10m ; o1 + shl t0d, cl + inc t0d ;((o0 + o1) << (BIT_DEPTH - 8)) + 1 + + neg ecx + add ecx, 4 ; bd - 12 + cmovl ecx, [pw_0] + add ecx, 3 + add ecx, r6m + movd xm2, ecx ; shift + + dec ecx + shl t0d, cl + movd xm4, t0d + vpbroadcastd m4, xm4 ; offset + mov rcx, t1q ; restore ecx + + lea r6, [w_avg_%1 %+ SUFFIX %+ _table] + tzcnt wd, wm + movsxd wq, dword [r6+wq*4] + add wq, r6 + AVG_FN %1, W_AVG +%endmacro + +INIT_YMM avx2 + +VVC_AVG_AVX2 16 + +VVC_AVG_AVX2 8 + +VVC_W_AVG_AVX2 16 + +VVC_W_AVG_AVX2 8 +%endif + +%endif diff --git a/libavcodec/x86/vvc/vvcdsp_init.c b/libavcodec/x86/vvc/vvcdsp_init.c new file mode 100644 index 00000000000..0d2c683f0f3 --- /dev/null +++ b/libavcodec/x86/vvc/vvcdsp_init.c @@ -0,0 +1,256 @@ +/* + * VVC DSP init for x86 + * + * Copyright (C) 2022-2024 Nuo Mi + * Copyright (c) 2023-2024 Wu Jianhua + * + * 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 "config.h" + +#include "libavutil/cpu.h" +#include "libavutil/x86/asm.h" +#include "libavutil/x86/cpu.h" +#include "libavcodec/vvc/vvcdec.h" +#include "libavcodec/vvc/vvc_ctu.h" +#include "libavcodec/vvc/vvcdsp.h" +#include "libavcodec/x86/h26x/h2656dsp.h" + +#if ARCH_X86_64 +#define FW_PUT(name, depth, opt) \ +static void ff_vvc_put_ ## name ## _ ## depth ## _##opt(int16_t *dst, const uint8_t *src, ptrdiff_t srcstride, \ + int height, const int8_t *hf, const int8_t *vf, int width) \ +{ \ + ff_h2656_put_## name ## _ ## depth ## _##opt(dst, 2 * MAX_PB_SIZE, src, srcstride, height, hf, vf, width); \ +} + +#define FW_PUT_TAP(fname, bitd, opt ) \ + FW_PUT(fname##4, bitd, opt ) \ + FW_PUT(fname##8, bitd, opt ) \ + FW_PUT(fname##16, bitd, opt ) \ + FW_PUT(fname##32, bitd, opt ) \ + FW_PUT(fname##64, bitd, opt ) \ + FW_PUT(fname##128, bitd, opt ) \ + +#define FW_PUT_4TAP(fname, bitd, opt) \ + FW_PUT(fname ## 2, bitd, opt) \ + FW_PUT_TAP(fname, bitd, opt) + +#define FW_PUT_4TAP_SSE4(bitd) \ + FW_PUT_4TAP(pixels, bitd, sse4) \ + FW_PUT_4TAP(4tap_h, bitd, sse4) \ + FW_PUT_4TAP(4tap_v, bitd, sse4) \ + FW_PUT_4TAP(4tap_hv, bitd, sse4) + +#define FW_PUT_8TAP_SSE4(bitd) \ + FW_PUT_TAP(8tap_h, bitd, sse4) \ + FW_PUT_TAP(8tap_v, bitd, sse4) \ + FW_PUT_TAP(8tap_hv, bitd, sse4) + +#define FW_PUT_SSE4(bitd) \ + FW_PUT_4TAP_SSE4(bitd) \ + FW_PUT_8TAP_SSE4(bitd) + +FW_PUT_SSE4( 8) +FW_PUT_SSE4(10) +FW_PUT_SSE4(12) + +#define FW_PUT_TAP_AVX2(n, bitd) \ + FW_PUT(n ## tap_h32, bitd, avx2) \ + FW_PUT(n ## tap_h64, bitd, avx2) \ + FW_PUT(n ## tap_h128, bitd, avx2) \ + FW_PUT(n ## tap_v32, bitd, avx2) \ + FW_PUT(n ## tap_v64, bitd, avx2) \ + FW_PUT(n ## tap_v128, bitd, avx2) + +#define FW_PUT_AVX2(bitd) \ + FW_PUT(pixels32, bitd, avx2) \ + FW_PUT(pixels64, bitd, avx2) \ + FW_PUT(pixels128, bitd, avx2) \ + FW_PUT_TAP_AVX2(4, bitd) \ + FW_PUT_TAP_AVX2(8, bitd) \ + +FW_PUT_AVX2( 8) +FW_PUT_AVX2(10) +FW_PUT_AVX2(12) + +#define FW_PUT_TAP_16BPC_AVX2(n, bitd) \ + FW_PUT(n ## tap_h16, bitd, avx2) \ + FW_PUT(n ## tap_v16, bitd, avx2) \ + FW_PUT(n ## tap_hv16, bitd, avx2) \ + FW_PUT(n ## tap_hv32, bitd, avx2) \ + FW_PUT(n ## tap_hv64, bitd, avx2) \ + FW_PUT(n ## tap_hv128, bitd, avx2) + +#define FW_PUT_16BPC_AVX2(bitd) \ + FW_PUT(pixels16, bitd, avx2) \ + FW_PUT_TAP_16BPC_AVX2(4, bitd) \ + FW_PUT_TAP_16BPC_AVX2(8, bitd) + +FW_PUT_16BPC_AVX2(10) +FW_PUT_16BPC_AVX2(12) + +#define PEL_LINK(dst, C, W, idx1, idx2, name, D, opt) \ + dst[C][W][idx1][idx2] = ff_vvc_put_## name ## _ ## D ## _##opt; \ + dst ## _uni[C][W][idx1][idx2] = ff_h2656_put_uni_ ## name ## _ ## D ## _##opt; \ + +#define MC_TAP_LINKS(pointer, C, my, mx, fname, bitd, opt ) \ + PEL_LINK(pointer, C, 1, my , mx , fname##4 , bitd, opt ); \ + PEL_LINK(pointer, C, 2, my , mx , fname##8 , bitd, opt ); \ + PEL_LINK(pointer, C, 3, my , mx , fname##16, bitd, opt ); \ + PEL_LINK(pointer, C, 4, my , mx , fname##32, bitd, opt ); \ + PEL_LINK(pointer, C, 5, my , mx , fname##64, bitd, opt ); \ + PEL_LINK(pointer, C, 6, my , mx , fname##128, bitd, opt ); + +#define MC_8TAP_LINKS(pointer, my, mx, fname, bitd, opt) \ + MC_TAP_LINKS(pointer, LUMA, my, mx, fname, bitd, opt) + +#define MC_8TAP_LINKS_SSE4(bd) \ + MC_8TAP_LINKS(c->inter.put, 0, 0, pixels, bd, sse4); \ + MC_8TAP_LINKS(c->inter.put, 0, 1, 8tap_h, bd, sse4); \ + MC_8TAP_LINKS(c->inter.put, 1, 0, 8tap_v, bd, sse4); \ + MC_8TAP_LINKS(c->inter.put, 1, 1, 8tap_hv, bd, sse4) + +#define MC_4TAP_LINKS(pointer, my, mx, fname, bitd, opt) \ + PEL_LINK(pointer, CHROMA, 0, my , mx , fname##2 , bitd, opt ); \ + MC_TAP_LINKS(pointer, CHROMA, my, mx, fname, bitd, opt) \ + +#define MC_4TAP_LINKS_SSE4(bd) \ + MC_4TAP_LINKS(c->inter.put, 0, 0, pixels, bd, sse4); \ + MC_4TAP_LINKS(c->inter.put, 0, 1, 4tap_h, bd, sse4); \ + MC_4TAP_LINKS(c->inter.put, 1, 0, 4tap_v, bd, sse4); \ + MC_4TAP_LINKS(c->inter.put, 1, 1, 4tap_hv, bd, sse4) + +#define MC_LINK_SSE4(bd) \ + MC_4TAP_LINKS_SSE4(bd) \ + MC_8TAP_LINKS_SSE4(bd) + +#define MC_TAP_LINKS_AVX2(C,tap,bd) do { \ + PEL_LINK(c->inter.put, C, 4, 0, 0, pixels32, bd, avx2) \ + PEL_LINK(c->inter.put, C, 5, 0, 0, pixels64, bd, avx2) \ + PEL_LINK(c->inter.put, C, 6, 0, 0, pixels128, bd, avx2) \ + PEL_LINK(c->inter.put, C, 4, 0, 1, tap##tap_h32, bd, avx2) \ + PEL_LINK(c->inter.put, C, 5, 0, 1, tap##tap_h64, bd, avx2) \ + PEL_LINK(c->inter.put, C, 6, 0, 1, tap##tap_h128, bd, avx2) \ + PEL_LINK(c->inter.put, C, 4, 1, 0, tap##tap_v32, bd, avx2) \ + PEL_LINK(c->inter.put, C, 5, 1, 0, tap##tap_v64, bd, avx2) \ + PEL_LINK(c->inter.put, C, 6, 1, 0, tap##tap_v128, bd, avx2) \ + } while (0) + +#define MC_LINKS_AVX2(bd) \ + MC_TAP_LINKS_AVX2(LUMA, 8, bd); \ + MC_TAP_LINKS_AVX2(CHROMA, 4, bd); + +#define MC_TAP_LINKS_16BPC_AVX2(C, tap, bd) do { \ + PEL_LINK(c->inter.put, C, 3, 0, 0, pixels16, bd, avx2) \ + PEL_LINK(c->inter.put, C, 3, 0, 1, tap##tap_h16, bd, avx2) \ + PEL_LINK(c->inter.put, C, 3, 1, 0, tap##tap_v16, bd, avx2) \ + PEL_LINK(c->inter.put, C, 3, 1, 1, tap##tap_hv16, bd, avx2) \ + PEL_LINK(c->inter.put, C, 4, 1, 1, tap##tap_hv32, bd, avx2) \ + PEL_LINK(c->inter.put, C, 5, 1, 1, tap##tap_hv64, bd, avx2) \ + PEL_LINK(c->inter.put, C, 6, 1, 1, tap##tap_hv128, bd, avx2) \ + } while (0) + +#define MC_LINKS_16BPC_AVX2(bd) \ + MC_TAP_LINKS_16BPC_AVX2(LUMA, 8, bd); \ + MC_TAP_LINKS_16BPC_AVX2(CHROMA, 4, bd); + +#define bf(fn, bd, opt) fn##_##bd##_##opt +#define BF(fn, bpc, opt) fn##_##bpc##bpc_##opt + +#define AVG_BPC_FUNC(bpc, opt) \ +void BF(ff_vvc_avg, bpc, opt)(uint8_t *dst, ptrdiff_t dst_stride, \ + const int16_t *src0, const int16_t *src1, intptr_t width, intptr_t height, intptr_t pixel_max); \ +void BF(ff_vvc_w_avg, bpc, opt)(uint8_t *dst, ptrdiff_t dst_stride, \ + const int16_t *src0, const int16_t *src1, intptr_t width, intptr_t height, \ + intptr_t denom, intptr_t w0, intptr_t w1, intptr_t o0, intptr_t o1, intptr_t pixel_max); + +#define AVG_FUNCS(bpc, bd, opt) \ +static void bf(avg, bd, opt)(uint8_t *dst, ptrdiff_t dst_stride, \ + const int16_t *src0, const int16_t *src1, int width, int height) \ +{ \ + BF(ff_vvc_avg, bpc, opt)(dst, dst_stride, src0, src1, width, height, (1 << bd) - 1); \ +} \ +static void bf(w_avg, bd, opt)(uint8_t *dst, ptrdiff_t dst_stride, \ + const int16_t *src0, const int16_t *src1, int width, int height, \ + int denom, int w0, int w1, int o0, int o1) \ +{ \ + BF(ff_vvc_w_avg, bpc, opt)(dst, dst_stride, src0, src1, width, height, \ + denom, w0, w1, o0, o1, (1 << bd) - 1); \ +} + +AVG_BPC_FUNC(8, avx2) +AVG_BPC_FUNC(16, avx2) + +AVG_FUNCS(8, 8, avx2) +AVG_FUNCS(16, 10, avx2) +AVG_FUNCS(16, 12, avx2) + +#define AVG_INIT(bd, opt) do { \ + c->inter.avg = bf(avg, bd, opt); \ + c->inter.w_avg = bf(w_avg, bd, opt); \ +} while (0) +#endif + +void ff_vvc_dsp_init_x86(VVCDSPContext *const c, const int bd) +{ +#if ARCH_X86_64 + const int cpu_flags = av_get_cpu_flags(); + + if (bd == 8) { + if (EXTERNAL_SSE4(cpu_flags)) { + MC_LINK_SSE4(8); + } + if (EXTERNAL_AVX2_FAST(cpu_flags)) { + MC_LINKS_AVX2(8); + } + } else if (bd == 10) { + if (EXTERNAL_SSE4(cpu_flags)) { + MC_LINK_SSE4(10); + } + if (EXTERNAL_AVX2_FAST(cpu_flags)) { + MC_LINKS_AVX2(10); + MC_LINKS_16BPC_AVX2(10); + } + } else if (bd == 12) { + if (EXTERNAL_SSE4(cpu_flags)) { + MC_LINK_SSE4(12); + } + if (EXTERNAL_AVX2_FAST(cpu_flags)) { + MC_LINKS_AVX2(12); + MC_LINKS_16BPC_AVX2(12); + } + } + + if (EXTERNAL_AVX2(cpu_flags)) { + switch (bd) { + case 8: + AVG_INIT(8, avx2); + break; + case 10: + AVG_INIT(10, avx2); + break; + case 12: + AVG_INIT(12, avx2); + break; + default: + break; + } + } +#endif +} diff --git a/libavcodec/xan.c b/libavcodec/xan.c index 14fc2443c5e..cc0ecea5ebf 100644 --- a/libavcodec/xan.c +++ b/libavcodec/xan.c @@ -622,8 +622,7 @@ static int xan_decode_frame(AVCodecContext *avctx, AVFrame *frame, if (xan_wc3_decode_frame(s, frame) < 0) return AVERROR_INVALIDDATA; - av_frame_unref(s->last_frame); - if ((ret = av_frame_ref(s->last_frame, frame)) < 0) + if ((ret = av_frame_replace(s->last_frame, frame)) < 0) return ret; *got_frame = 1; diff --git a/libavcodec/xbmdec.c b/libavcodec/xbmdec.c index a0cc1cb8c61..2d84327e025 100644 --- a/libavcodec/xbmdec.c +++ b/libavcodec/xbmdec.c @@ -28,17 +28,22 @@ static int get_nibble(uint8_t x) { - int ret = 255; - - if (x <= '9') { - if (x >= '0') - ret = x - '0'; - } else if (x >= 'a') { - if (x <= 'f') - ret = x - ('a' - 10); - } else if (x >= 'A' && x <= 'F') - ret = x - ('A' - 10); - return ret; +#define TIMES256(idx) \ +TIMES64(4 * (idx)) TIMES64(4 * (idx) + 1) TIMES64(4 * (idx) + 2) TIMES64(4 * (idx) + 3) +#define TIMES64(idx) \ +TIMES16(4 * (idx)) TIMES16(4 * (idx) + 1) TIMES16(4 * (idx) + 2) TIMES16(4 * (idx) + 3) +#define TIMES16(idx) \ +TIMES4(4 * (idx)) TIMES4(4 * (idx) + 1) TIMES4(4 * (idx) + 2) TIMES4(4 * (idx) + 3) +#define TIMES4(idx) \ +ENTRY(4 * (idx)) ENTRY(4 * (idx) + 1) ENTRY(4 * (idx) + 2) ENTRY(4 * (idx) + 3) +#define ENTRY(x) [x] = ((x) >= 'a' && (x) <= 'f') ? (x) - ('a' - 10) : \ + ((x) >= 'A' && (x) <= 'F') ? (x) - ('A' - 10) : \ + ((x) >= '0' && (x) <= '9') ? (x) - '0' : 255, + + static const uint8_t lut[] = { + TIMES256(0) + }; + return lut[x]; } static int parse_str_int(const uint8_t *p, const uint8_t *end, const uint8_t *key) @@ -130,7 +135,7 @@ static int xbm_decode_frame(AVCodecContext *avctx, AVFrame *p, } } - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/xbmenc.c b/libavcodec/xbmenc.c index cd8b73afa3e..5231d4691dc 100644 --- a/libavcodec/xbmenc.c +++ b/libavcodec/xbmenc.c @@ -20,11 +20,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/reverse.h" #include "avcodec.h" #include "codec_internal.h" #include "encode.h" -#include "mathops.h" #define ANSI_MIN_READLINE 509 @@ -57,14 +55,25 @@ static int xbm_encode_frame(AVCodecContext *avctx, AVPacket *pkt, buf += snprintf(buf, 39, "static unsigned char image_bits[] = {\n"); for (i = 0, l = lineout; i < avctx->height; i++) { for (j = 0; j < linesize; j++) { - buf += snprintf(buf, 6, " 0x%02X", ff_reverse[*ptr++]); + // 0..15 bitreversed as chars + static const char lut[] = { + '0', '8', '4', 'C', '2', 'A', '6', 'E', + '1', '9', '5', 'D', '3', 'B', '7', 'F' + }; + buf[0] = ' '; + buf[1] = '0'; + buf[2] = 'x'; + buf[3] = lut[*ptr & 0xF]; + buf[4] = lut[*ptr >> 4]; + buf += 5; + ptr++; if (--commas <= 0) { - buf += snprintf(buf, 2, "\n"); + *buf++ = '\n'; break; } - buf += snprintf(buf, 2, ","); + *buf++ = ','; if (--l <= 0) { - buf += snprintf(buf, 2, "\n"); + *buf++ = '\n'; l = lineout; } } diff --git a/libavcodec/xfacedec.c b/libavcodec/xfacedec.c index 378d6aea0ea..78b70cae8c7 100644 --- a/libavcodec/xfacedec.c +++ b/libavcodec/xfacedec.c @@ -182,5 +182,4 @@ const FFCodec ff_xface_decoder = { .priv_data_size = sizeof(XFaceContext), .init = xface_decode_init, FF_CODEC_DECODE_CB(xface_decode_frame), - .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_MONOWHITE, AV_PIX_FMT_NONE }, }; diff --git a/libavcodec/xl.c b/libavcodec/xl.c index 283cd39aa74..f008d56e896 100644 --- a/libavcodec/xl.c +++ b/libavcodec/xl.c @@ -60,7 +60,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; Y = p->data[0]; U = p->data[1]; diff --git a/libavcodec/xpmdec.c b/libavcodec/xpmdec.c index 2550afb9d68..b915abe0bfd 100644 --- a/libavcodec/xpmdec.c +++ b/libavcodec/xpmdec.c @@ -193,15 +193,22 @@ static const ColorEntry color_table[] = { static unsigned hex_char_to_number(uint8_t x) { - if (x >= 'a' && x <= 'f') - x -= 'a' - 10; - else if (x >= 'A' && x <= 'F') - x -= 'A' - 10; - else if (x >= '0' && x <= '9') - x -= '0'; - else - x = 0; - return x; +#define TIMES256(idx) \ +TIMES64(4 * (idx)) TIMES64(4 * (idx) + 1) TIMES64(4 * (idx) + 2) TIMES64(4 * (idx) + 3) +#define TIMES64(idx) \ +TIMES16(4 * (idx)) TIMES16(4 * (idx) + 1) TIMES16(4 * (idx) + 2) TIMES16(4 * (idx) + 3) +#define TIMES16(idx) \ +TIMES4(4 * (idx)) TIMES4(4 * (idx) + 1) TIMES4(4 * (idx) + 2) TIMES4(4 * (idx) + 3) +#define TIMES4(idx) \ +ENTRY(4 * (idx)) ENTRY(4 * (idx) + 1) ENTRY(4 * (idx) + 2) ENTRY(4 * (idx) + 3) +#define ENTRY(x) [x] = ((x) >= 'a' && (x) <= 'f') ? (x) - ('a' - 10) : \ + ((x) >= 'A' && (x) <= 'F') ? (x) - ('A' - 10) : \ + ((x) >= '0' && (x) <= '9') ? (x) - '0' : 0, + + static const uint8_t lut[] = { + TIMES256(0) + }; + return lut[x]; } /* @@ -233,14 +240,12 @@ static size_t mod_strcspn(const char *string, const char *reject) return i; } -static uint32_t color_string_to_rgba(const char *p, int len) +static uint32_t color_string_to_rgba(const char *p, size_t len) { uint32_t ret = 0xFF000000; const ColorEntry *entry; char color_name[100]; - len = FFMIN(FFMAX(len, 0), sizeof(color_name) - 1); - if (*p == '#') { p++; len--; @@ -271,6 +276,7 @@ static uint32_t color_string_to_rgba(const char *p, int len) (hex_char_to_number(p[0]) << 28); } } else { + len = FFMIN(len, sizeof(color_name) - 1); strncpy(color_name, p, len); color_name[len] = '\0'; @@ -375,7 +381,7 @@ static int xpm_decode_frame(AVCodecContext *avctx, AVFrame *p, for (i = 0; i < ncolors; i++) { const uint8_t *index; - int len; + size_t len; ptr += mod_strcspn(ptr, "\"") + 1; if (end - ptr < cpp) @@ -422,7 +428,7 @@ static int xpm_decode_frame(AVCodecContext *avctx, AVFrame *p, ptr += mod_strcspn(ptr, ",") + 1; } - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; *got_frame = 1; diff --git a/libavcodec/xsubdec.c b/libavcodec/xsubdec.c index f86b7c58e79..b804a902986 100644 --- a/libavcodec/xsubdec.c +++ b/libavcodec/xsubdec.c @@ -59,6 +59,7 @@ static int decode_frame(AVCodecContext *avctx, AVSubtitle *sub, int64_t packet_time = 0; GetBitContext gb; int has_alpha = avctx->codec_tag == MKTAG('D','X','S','A'); + int64_t start_display_time, end_display_time; // check that at least header fits if (buf_size < 27 + 7 * 2 + 4 * (3 + has_alpha)) { @@ -73,8 +74,14 @@ static int decode_frame(AVCodecContext *avctx, AVSubtitle *sub, } if (avpkt->pts != AV_NOPTS_VALUE) packet_time = av_rescale_q(avpkt->pts, AV_TIME_BASE_Q, (AVRational){1, 1000}); - sub->start_display_time = parse_timecode(buf + 1, packet_time); - sub->end_display_time = parse_timecode(buf + 14, packet_time); + + sub->start_display_time = start_display_time = parse_timecode(buf + 1, packet_time); + sub->end_display_time = end_display_time = parse_timecode(buf + 14, packet_time); + if (sub->start_display_time != start_display_time || + sub-> end_display_time != end_display_time) { + av_log(avctx, AV_LOG_ERROR, "time code not representable in 32bit\n"); + return -1; + } buf += 27; // read header diff --git a/libavcodec/xvmc.h b/libavcodec/xvmc.h deleted file mode 100644 index 52e70c0d77e..00000000000 --- a/libavcodec/xvmc.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2003 Ivan Kalvachev - * - * 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 - */ - -#ifndef AVCODEC_XVMC_H -#define AVCODEC_XVMC_H - -/** - * @file - * @ingroup lavc_codec_hwaccel_xvmc - * Public libavcodec XvMC header. - */ - -#pragma message("XvMC is no longer supported; this header is deprecated and will be removed") - -#include - -#include "libavutil/attributes.h" -#include "avcodec.h" - -/** - * @defgroup lavc_codec_hwaccel_xvmc XvMC - * @ingroup lavc_codec_hwaccel - * - * @{ - */ - -#define AV_XVMC_ID 0x1DC711C0 /**< special value to ensure that regular pixel routines haven't corrupted the struct - the number is 1337 speak for the letters IDCT MCo (motion compensation) */ - -struct attribute_deprecated xvmc_pix_fmt { - /** The field contains the special constant value AV_XVMC_ID. - It is used as a test that the application correctly uses the API, - and that there is no corruption caused by pixel routines. - - application - set during initialization - - libavcodec - unchanged - */ - int xvmc_id; - - /** Pointer to the block array allocated by XvMCCreateBlocks(). - The array has to be freed by XvMCDestroyBlocks(). - Each group of 64 values represents one data block of differential - pixel information (in MoCo mode) or coefficients for IDCT. - - application - set the pointer during initialization - - libavcodec - fills coefficients/pixel data into the array - */ - short* data_blocks; - - /** Pointer to the macroblock description array allocated by - XvMCCreateMacroBlocks() and freed by XvMCDestroyMacroBlocks(). - - application - set the pointer during initialization - - libavcodec - fills description data into the array - */ - XvMCMacroBlock* mv_blocks; - - /** Number of macroblock descriptions that can be stored in the mv_blocks - array. - - application - set during initialization - - libavcodec - unchanged - */ - int allocated_mv_blocks; - - /** Number of blocks that can be stored at once in the data_blocks array. - - application - set during initialization - - libavcodec - unchanged - */ - int allocated_data_blocks; - - /** Indicate that the hardware would interpret data_blocks as IDCT - coefficients and perform IDCT on them. - - application - set during initialization - - libavcodec - unchanged - */ - int idct; - - /** In MoCo mode it indicates that intra macroblocks are assumed to be in - unsigned format; same as the XVMC_INTRA_UNSIGNED flag. - - application - set during initialization - - libavcodec - unchanged - */ - int unsigned_intra; - - /** Pointer to the surface allocated by XvMCCreateSurface(). - It has to be freed by XvMCDestroySurface() on application exit. - It identifies the frame and its state on the video hardware. - - application - set during initialization - - libavcodec - unchanged - */ - XvMCSurface* p_surface; - -/** Set by the decoder before calling ff_draw_horiz_band(), - needed by the XvMCRenderSurface function. */ -//@{ - /** Pointer to the surface used as past reference - - application - unchanged - - libavcodec - set - */ - XvMCSurface* p_past_surface; - - /** Pointer to the surface used as future reference - - application - unchanged - - libavcodec - set - */ - XvMCSurface* p_future_surface; - - /** top/bottom field or frame - - application - unchanged - - libavcodec - set - */ - unsigned int picture_structure; - - /** XVMC_SECOND_FIELD - 1st or 2nd field in the sequence - - application - unchanged - - libavcodec - set - */ - unsigned int flags; -//}@ - - /** Number of macroblock descriptions in the mv_blocks array - that have already been passed to the hardware. - - application - zeroes it on get_buffer(). - A successful ff_draw_horiz_band() may increment it - with filled_mb_block_num or zero both. - - libavcodec - unchanged - */ - int start_mv_blocks_num; - - /** Number of new macroblock descriptions in the mv_blocks array (after - start_mv_blocks_num) that are filled by libavcodec and have to be - passed to the hardware. - - application - zeroes it on get_buffer() or after successful - ff_draw_horiz_band(). - - libavcodec - increment with one of each stored MB - */ - int filled_mv_blocks_num; - - /** Number of the next free data block; one data block consists of - 64 short values in the data_blocks array. - All blocks before this one have already been claimed by placing their - position into the corresponding block description structure field, - that are part of the mv_blocks array. - - application - zeroes it on get_buffer(). - A successful ff_draw_horiz_band() may zero it together - with start_mb_blocks_num. - - libavcodec - each decoded macroblock increases it by the number - of coded blocks it contains. - */ - int next_free_data_block_num; -}; - -/** - * @} - */ - -#endif /* AVCODEC_XVMC_H */ diff --git a/libavcodec/xwddec.c b/libavcodec/xwddec.c index 6c5bc44a029..f691587be90 100644 --- a/libavcodec/xwddec.c +++ b/libavcodec/xwddec.c @@ -216,7 +216,7 @@ static int xwd_decode_frame(AVCodecContext *avctx, AVFrame *p, if ((ret = ff_get_buffer(avctx, p, 0)) < 0) return ret; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; p->pict_type = AV_PICTURE_TYPE_I; if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { diff --git a/libavcodec/y41pdec.c b/libavcodec/y41pdec.c index b461f349ad2..14e36dc9985 100644 --- a/libavcodec/y41pdec.c +++ b/libavcodec/y41pdec.c @@ -51,7 +51,7 @@ static int y41p_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; for (i = avctx->height - 1; i >= 0 ; i--) { diff --git a/libavcodec/ylc.c b/libavcodec/ylc.c index 29c10f05da0..b03df31556e 100644 --- a/libavcodec/ylc.c +++ b/libavcodec/ylc.c @@ -87,7 +87,7 @@ static int build_vlc(AVCodecContext *avctx, VLC *vlc, const uint32_t *table) uint8_t xlat[256]; int cur_node, i, j, pos = 0; - ff_free_vlc(vlc); + ff_vlc_free(vlc); for (i = 0; i < 256; i++) { nodes[i].count = table[i]; @@ -142,7 +142,7 @@ static int build_vlc(AVCodecContext *avctx, VLC *vlc, const uint32_t *table) get_tree_codes(bits, lens, xlat, nodes, cur_node - 1, 0, 0, &pos); - return ff_init_vlc_sparse(vlc, YLC_VLC_BITS, pos, lens, 2, 2, + return ff_vlc_init_sparse(vlc, YLC_VLC_BITS, pos, lens, 2, 2, bits, 4, 4, xlat, 1, 1, 0); } @@ -427,7 +427,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *p, } p->pict_type = AV_PICTURE_TYPE_I; - p->key_frame = 1; + p->flags |= AV_FRAME_FLAG_KEY; *got_frame = 1; return avpkt->size; @@ -438,7 +438,7 @@ static av_cold int decode_end(AVCodecContext *avctx) YLCContext *s = avctx->priv_data; for (int i = 0; i < FF_ARRAY_ELEMS(s->vlc); i++) - ff_free_vlc(&s->vlc[i]); + ff_vlc_free(&s->vlc[i]); av_freep(&s->buffer); s->buffer_size = 0; diff --git a/libavcodec/yop.c b/libavcodec/yop.c index 14244c942a6..1294c5cc004 100644 --- a/libavcodec/yop.c +++ b/libavcodec/yop.c @@ -232,7 +232,11 @@ static int yop_decode_frame(AVCodecContext *avctx, AVFrame *rframe, (palette[i + firstcolor] >> 6) & 0x30303; } +#if FF_API_PALETTE_HAS_CHANGED +FF_DISABLE_DEPRECATION_WARNINGS frame->palette_has_changed = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif for (y = 0; y < avctx->height; y += 2) { for (x = 0; x < avctx->width; x += 2) { diff --git a/libavcodec/yuv4dec.c b/libavcodec/yuv4dec.c index 15424b1940b..ad83a2125c0 100644 --- a/libavcodec/yuv4dec.c +++ b/libavcodec/yuv4dec.c @@ -46,7 +46,7 @@ static int yuv4_decode_frame(AVCodecContext *avctx, AVFrame *pic, if ((ret = ff_get_buffer(avctx, pic, 0)) < 0) return ret; - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; y = pic->data[0]; diff --git a/libavcodec/yuv4enc.c b/libavcodec/yuv4enc.c index 8123260d5db..2a9d3442ca4 100644 --- a/libavcodec/yuv4enc.c +++ b/libavcodec/yuv4enc.c @@ -29,10 +29,10 @@ static int yuv4_encode_frame(AVCodecContext *avctx, AVPacket *pkt, { uint8_t *dst; const uint8_t *y, *u, *v; - int i, j, ret; + int ret; - ret = ff_get_encode_buffer(avctx, pkt, 6 * (avctx->width + 1 >> 1) - * (avctx->height + 1 >> 1), 0); + ret = ff_get_encode_buffer(avctx, pkt, 6 * ((avctx->width + 1) / 2) + * ((avctx->height + 1) / 2), 0); if (ret < 0) return ret; dst = pkt->data; @@ -41,8 +41,8 @@ static int yuv4_encode_frame(AVCodecContext *avctx, AVPacket *pkt, u = pic->data[1]; v = pic->data[2]; - for (i = 0; i < avctx->height + 1 >> 1; i++) { - for (j = 0; j < avctx->width + 1 >> 1; j++) { + for (int i = 0; i < avctx->height / 2; i++) { + for (int j = 0; j < (avctx->width + 1) / 2; j++) { *dst++ = u[j] ^ 0x80; *dst++ = v[j] ^ 0x80; *dst++ = y[ 2 * j ]; @@ -55,6 +55,17 @@ static int yuv4_encode_frame(AVCodecContext *avctx, AVPacket *pkt, v += pic->linesize[2]; } + if (avctx->height & 1) { + for (int j = 0; j < (avctx->width + 1) / 2; j++) { + *dst++ = u[j] ^ 0x80; + *dst++ = v[j] ^ 0x80; + *dst++ = y[2 * j ]; + *dst++ = y[2 * j + 1]; + *dst++ = y[2 * j ]; + *dst++ = y[2 * j + 1]; + } + } + *got_packet = 1; return 0; } diff --git a/libavcodec/zerocodec.c b/libavcodec/zerocodec.c index 42fb24ff6c5..48f78d1d86c 100644 --- a/libavcodec/zerocodec.c +++ b/libavcodec/zerocodec.c @@ -40,7 +40,7 @@ static int zerocodec_decode_frame(AVCodecContext *avctx, AVFrame *pic, int i, j, zret, ret; if (avpkt->flags & AV_PKT_FLAG_KEY) { - pic->key_frame = 1; + pic->flags |= AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_I; } else { if (!prev) { @@ -50,7 +50,7 @@ static int zerocodec_decode_frame(AVCodecContext *avctx, AVFrame *pic, prev += (avctx->height - 1) * prev_pic->linesize[0]; - pic->key_frame = 0; + pic->flags &= ~AV_FRAME_FLAG_KEY; pic->pict_type = AV_PICTURE_TYPE_P; } @@ -93,8 +93,7 @@ static int zerocodec_decode_frame(AVCodecContext *avctx, AVFrame *pic, dst -= pic->linesize[0]; } - av_frame_unref(zc->previous_frame); - if ((ret = av_frame_ref(zc->previous_frame, pic)) < 0) + if ((ret = av_frame_replace(zc->previous_frame, pic)) < 0) return ret; *got_frame = 1; diff --git a/libavcodec/zmbv.c b/libavcodec/zmbv.c index 0b44851811f..d309a8612b4 100644 --- a/libavcodec/zmbv.c +++ b/libavcodec/zmbv.c @@ -559,11 +559,11 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, return ret; if (c->flags & ZMBV_KEYFRAME) { - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_I; zmbv_decode_intra(c); } else { - frame->key_frame = 0; + frame->flags &= ~AV_FRAME_FLAG_KEY; frame->pict_type = AV_PICTURE_TYPE_P; if (c->decomp_len < 2LL * ((c->width + c->bw - 1) / c->bw) * ((c->height + c->bh - 1) / c->bh)) return AVERROR_INVALIDDATA; diff --git a/libavdevice/Makefile b/libavdevice/Makefile index 8a62822b69e..c30449201d4 100644 --- a/libavdevice/Makefile +++ b/libavdevice/Makefile @@ -57,6 +57,7 @@ OBJS-$(CONFIG_LIBDC1394_INDEV) += libdc1394.o # Objects duplicated from other libraries for shared builds SHLIBOBJS-$(CONFIG_DECKLINK_INDEV) += reverse.o +SHLIBOBJS-$(CONFIG_DECKLINK_OUTDEV) += ccfifo.o # Windows resource file SHLIBOBJS-$(HAVE_GNU_WINDRES) += avdeviceres.o diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c index 8a90fcb5d78..9b9a9146c7d 100644 --- a/libavdevice/alldevices.c +++ b/libavdevice/alldevices.c @@ -18,66 +18,71 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/attributes.h" +#include "libavutil/attributes_internal.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "libavformat/mux.h" #include "avdevice.h" +FF_VISIBILITY_PUSH_HIDDEN /* devices */ -extern const AVInputFormat ff_alsa_demuxer; +extern const FFInputFormat ff_alsa_demuxer; extern const FFOutputFormat ff_alsa_muxer; -extern const AVInputFormat ff_android_camera_demuxer; +extern const FFInputFormat ff_android_camera_demuxer; extern const FFOutputFormat ff_audiotoolbox_muxer; -extern const AVInputFormat ff_avfoundation_demuxer; -extern const AVInputFormat ff_bktr_demuxer; +extern const FFInputFormat ff_avfoundation_demuxer; +extern const FFInputFormat ff_bktr_demuxer; extern const FFOutputFormat ff_caca_muxer; -extern const AVInputFormat ff_decklink_demuxer; +extern const FFInputFormat ff_decklink_demuxer; extern const FFOutputFormat ff_decklink_muxer; -extern const AVInputFormat ff_dshow_demuxer; -extern const AVInputFormat ff_fbdev_demuxer; +extern const FFInputFormat ff_dshow_demuxer; +extern const FFInputFormat ff_fbdev_demuxer; extern const FFOutputFormat ff_fbdev_muxer; -extern const AVInputFormat ff_gdigrab_demuxer; -extern const AVInputFormat ff_iec61883_demuxer; -extern const AVInputFormat ff_jack_demuxer; -extern const AVInputFormat ff_kmsgrab_demuxer; -extern const AVInputFormat ff_lavfi_demuxer; -extern const AVInputFormat ff_openal_demuxer; +extern const FFInputFormat ff_gdigrab_demuxer; +extern const FFInputFormat ff_iec61883_demuxer; +extern const FFInputFormat ff_jack_demuxer; +extern const FFInputFormat ff_kmsgrab_demuxer; +extern const FFInputFormat ff_lavfi_demuxer; +extern const FFInputFormat ff_openal_demuxer; extern const FFOutputFormat ff_opengl_muxer; -extern const AVInputFormat ff_oss_demuxer; +extern const FFInputFormat ff_oss_demuxer; extern const FFOutputFormat ff_oss_muxer; -extern const AVInputFormat ff_pulse_demuxer; +extern const FFInputFormat ff_pulse_demuxer; extern const FFOutputFormat ff_pulse_muxer; extern const FFOutputFormat ff_sdl2_muxer; -extern const AVInputFormat ff_sndio_demuxer; +extern const FFInputFormat ff_sndio_demuxer; extern const FFOutputFormat ff_sndio_muxer; -extern const AVInputFormat ff_v4l2_demuxer; +extern const FFInputFormat ff_v4l2_demuxer; extern const FFOutputFormat ff_v4l2_muxer; -extern const AVInputFormat ff_vfwcap_demuxer; -extern const AVInputFormat ff_xcbgrab_demuxer; +extern const FFInputFormat ff_vfwcap_demuxer; +extern const FFInputFormat ff_xcbgrab_demuxer; extern const FFOutputFormat ff_xv_muxer; /* external libraries */ -extern const AVInputFormat ff_libcdio_demuxer; -extern const AVInputFormat ff_libdc1394_demuxer; +extern const FFInputFormat ff_libcdio_demuxer; +extern const FFInputFormat ff_libdc1394_demuxer; +FF_VISIBILITY_POP_HIDDEN #include "libavdevice/outdev_list.c" #include "libavdevice/indev_list.c" -void avdevice_register_all(void) +av_cold void avdevice_register_all(void) { avpriv_register_devices(outdev_list, indev_list); } -static const void *next_input(const AVInputFormat *prev, AVClassCategory c2) +static av_cold const void *next_input(const AVInputFormat *prev, AVClassCategory c2) { const AVClass *pc; const AVClassCategory c1 = AV_CLASS_CATEGORY_DEVICE_INPUT; AVClassCategory category = AV_CLASS_CATEGORY_NA; - const AVInputFormat *fmt = NULL; + const FFInputFormat *fmt = NULL; int i = 0; while (prev && (fmt = indev_list[i])) { i++; - if (prev == fmt) + if (prev == &fmt->p) break; } @@ -85,7 +90,7 @@ static const void *next_input(const AVInputFormat *prev, AVClassCategory c2) fmt = indev_list[i++]; if (!fmt) break; - pc = fmt->priv_class; + pc = fmt->p.priv_class; if (!pc) continue; category = pc->category; @@ -93,7 +98,7 @@ static const void *next_input(const AVInputFormat *prev, AVClassCategory c2) return fmt; } -static const void *next_output(const AVOutputFormat *prev, AVClassCategory c2) +static av_cold const void *next_output(const AVOutputFormat *prev, AVClassCategory c2) { const AVClass *pc; const AVClassCategory c1 = AV_CLASS_CATEGORY_DEVICE_OUTPUT; @@ -119,22 +124,22 @@ static const void *next_output(const AVOutputFormat *prev, AVClassCategory c2) return fmt; } -const AVInputFormat *av_input_audio_device_next(const AVInputFormat *d) +av_cold const AVInputFormat *av_input_audio_device_next(const AVInputFormat *d) { return next_input(d, AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT); } -const AVInputFormat *av_input_video_device_next(const AVInputFormat *d) +av_cold const AVInputFormat *av_input_video_device_next(const AVInputFormat *d) { return next_input(d, AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT); } -const AVOutputFormat *av_output_audio_device_next(const AVOutputFormat *d) +av_cold const AVOutputFormat *av_output_audio_device_next(const AVOutputFormat *d) { return next_output(d, AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT); } -const AVOutputFormat *av_output_video_device_next(const AVOutputFormat *d) +av_cold const AVOutputFormat *av_output_video_device_next(const AVOutputFormat *d) { return next_output(d, AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT); } diff --git a/libavdevice/alsa_dec.c b/libavdevice/alsa_dec.c index b518bbdac6c..018afaef08d 100644 --- a/libavdevice/alsa_dec.c +++ b/libavdevice/alsa_dec.c @@ -52,6 +52,7 @@ #include "libavutil/opt.h" #include "libavutil/time.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "avdevice.h" @@ -157,14 +158,14 @@ static const AVClass alsa_demuxer_class = { .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT, }; -const AVInputFormat ff_alsa_demuxer = { - .name = "alsa", - .long_name = NULL_IF_CONFIG_SMALL("ALSA audio input"), +const FFInputFormat ff_alsa_demuxer = { + .p.name = "alsa", + .p.long_name = NULL_IF_CONFIG_SMALL("ALSA audio input"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &alsa_demuxer_class, .priv_data_size = sizeof(AlsaData), .read_header = audio_read_header, .read_packet = audio_read_packet, .read_close = ff_alsa_close, .get_device_list = audio_get_device_list, - .flags = AVFMT_NOFILE, - .priv_class = &alsa_demuxer_class, }; diff --git a/libavdevice/alsa_enc.c b/libavdevice/alsa_enc.c index 62a20c7ba48..0b4c7834f79 100644 --- a/libavdevice/alsa_enc.c +++ b/libavdevice/alsa_enc.c @@ -39,6 +39,7 @@ #include +#include "libavutil/frame.h" #include "libavutil/internal.h" #include "libavutil/time.h" @@ -131,13 +132,6 @@ static int audio_write_frame(AVFormatContext *s1, int stream_index, pkt.data = (*frame)->data[0]; pkt.size = (*frame)->nb_samples * s->frame_size; pkt.dts = (*frame)->pkt_dts; -#if FF_API_PKT_DURATION -FF_DISABLE_DEPRECATION_WARNINGS - if ((*frame)->pkt_duration) - pkt.duration = (*frame)->pkt_duration; - else -FF_ENABLE_DEPRECATION_WARNINGS -#endif pkt.duration = (*frame)->duration; return audio_write_packet(s1, &pkt); } diff --git a/libavdevice/android_camera.c b/libavdevice/android_camera.c index 1934999c182..3aa8597c63a 100644 --- a/libavdevice/android_camera.c +++ b/libavdevice/android_camera.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,7 @@ #include #include "libavformat/avformat.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "libavutil/avstring.h" #include "libavutil/display.h" @@ -638,7 +640,7 @@ static int wait_for_image_format(AVFormatContext *avctx) static int add_display_matrix(AVFormatContext *avctx, AVStream *st) { AndroidCameraCtx *ctx = avctx->priv_data; - uint8_t *side_data; + AVPacketSideData *side_data; int32_t display_matrix[9]; av_display_rotation_set(display_matrix, ctx->sensor_orientation); @@ -647,14 +649,16 @@ static int add_display_matrix(AVFormatContext *avctx, AVStream *st) av_display_matrix_flip(display_matrix, 1, 0); } - side_data = av_stream_new_side_data(st, - AV_PKT_DATA_DISPLAYMATRIX, sizeof(display_matrix)); + side_data = av_packet_side_data_new(&st->codecpar->coded_side_data, + &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_DISPLAYMATRIX, + sizeof(display_matrix), 0); if (!side_data) { return AVERROR(ENOMEM); } - memcpy(side_data, display_matrix, sizeof(display_matrix)); + memcpy(side_data->data, display_matrix, sizeof(display_matrix)); return 0; } @@ -857,13 +861,13 @@ static const AVClass android_camera_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, }; -const AVInputFormat ff_android_camera_demuxer = { - .name = "android_camera", - .long_name = NULL_IF_CONFIG_SMALL("Android camera input device"), +const FFInputFormat ff_android_camera_demuxer = { + .p.name = "android_camera", + .p.long_name = NULL_IF_CONFIG_SMALL("Android camera input device"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &android_camera_class, .priv_data_size = sizeof(AndroidCameraCtx), .read_header = android_camera_read_header, .read_packet = android_camera_read_packet, .read_close = android_camera_read_close, - .flags = AVFMT_NOFILE, - .priv_class = &android_camera_class, }; diff --git a/libavdevice/audiotoolbox.m b/libavdevice/audiotoolbox.m index aa49e2c992a..3bd707f2360 100644 --- a/libavdevice/audiotoolbox.m +++ b/libavdevice/audiotoolbox.m @@ -85,7 +85,11 @@ static av_cold int at_write_header(AVFormatContext *avctx) AudioObjectPropertyAddress prop; prop.mSelector = kAudioHardwarePropertyDevices; prop.mScope = kAudioObjectPropertyScopeGlobal; +#if !TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 120000 + prop.mElement = kAudioObjectPropertyElementMain; +#else prop.mElement = kAudioObjectPropertyElementMaster; +#endif err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &data_size); if (check_status(avctx, &err, "AudioObjectGetPropertyDataSize devices")) return AVERROR(EINVAL); diff --git a/libavdevice/avdevice.c b/libavdevice/avdevice.c index 38110ddfdb2..cd7b03ef48d 100644 --- a/libavdevice/avdevice.c +++ b/libavdevice/avdevice.c @@ -19,6 +19,7 @@ #include "libavutil/avassert.h" #include "avdevice.h" #include "internal.h" +#include "libavformat/demux.h" #include "libavformat/mux.h" int avdevice_app_to_dev_control_message(struct AVFormatContext *s, enum AVAppToDevMessageType type, @@ -44,7 +45,7 @@ int avdevice_list_devices(AVFormatContext *s, AVDeviceInfoList **device_list) av_assert0(device_list); av_assert0(s->oformat || s->iformat); if ((s->oformat && !ffofmt(s->oformat)->get_device_list) || - (s->iformat && !s->iformat->get_device_list)) { + (s->iformat && !ffifmt(s->iformat)->get_device_list)) { *device_list = NULL; return AVERROR(ENOSYS); } @@ -56,7 +57,7 @@ int avdevice_list_devices(AVFormatContext *s, AVDeviceInfoList **device_list) if (s->oformat) ret = ffofmt(s->oformat)->get_device_list(s, *device_list); else - ret = s->iformat->get_device_list(s, *device_list); + ret = ffifmt(s->iformat)->get_device_list(s, *device_list); if (ret < 0) { avdevice_free_list_devices(device_list); return ret; diff --git a/libavdevice/avfoundation.m b/libavdevice/avfoundation.m index 36ad8347535..3fe3df2cb75 100644 --- a/libavdevice/avfoundation.m +++ b/libavdevice/avfoundation.m @@ -32,6 +32,7 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "libavutil/avstring.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "libavutil/internal.h" #include "libavutil/parseutils.h" @@ -761,6 +762,64 @@ static int get_audio_config(AVFormatContext *s) return 0; } +static NSArray* getDevicesWithMediaType(AVMediaType mediaType) { +#if ((TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || (TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)) + NSMutableArray *deviceTypes = nil; + if (mediaType == AVMediaTypeVideo) { + deviceTypes = [NSMutableArray arrayWithArray:@[AVCaptureDeviceTypeBuiltInWideAngleCamera]]; + #if (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) + [deviceTypes addObject: AVCaptureDeviceTypeBuiltInDualCamera]; + [deviceTypes addObject: AVCaptureDeviceTypeBuiltInTelephotoCamera]; + #endif + #if (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110100) + [deviceTypes addObject: AVCaptureDeviceTypeBuiltInTrueDepthCamera]; + #endif + #if (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 130000) + [deviceTypes addObject: AVCaptureDeviceTypeBuiltInTripleCamera]; + [deviceTypes addObject: AVCaptureDeviceTypeBuiltInDualWideCamera]; + [deviceTypes addObject: AVCaptureDeviceTypeBuiltInUltraWideCamera]; + #endif + #if (TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED >= 130000) + [deviceTypes addObject: AVCaptureDeviceTypeDeskViewCamera]; + #endif + #if (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 150400) + [deviceTypes addObject: AVCaptureDeviceTypeBuiltInLiDARDepthCamera]; + #endif + #if (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 170000 || (TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED >= 140000)) + [deviceTypes addObject: AVCaptureDeviceTypeContinuityCamera]; + [deviceTypes addObject: AVCaptureDeviceTypeExternal]; + #elif (TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED < 140000) + [deviceTypes addObject: AVCaptureDeviceTypeExternalUnknown]; + #endif + } else if (mediaType == AVMediaTypeAudio) { + #if (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 170000 || (TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED >= 140000)) + deviceTypes = [NSMutableArray arrayWithArray:@[AVCaptureDeviceTypeMicrophone]]; + #else + deviceTypes = [NSMutableArray arrayWithArray:@[AVCaptureDeviceTypeBuiltInMicrophone]]; + #endif + } else if (mediaType == AVMediaTypeMuxed) { + #if (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 170000 || (TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED >= 140000)) + deviceTypes = [NSMutableArray arrayWithArray:@[AVCaptureDeviceTypeExternal]]; + #elif (TARGET_OS_OSX && __MAC_OS_X_VERSION_MIN_REQUIRED < 140000) + deviceTypes = [NSMutableArray arrayWithArray:@[AVCaptureDeviceTypeExternalUnknown]]; + #else + return nil; + #endif + } else { + return nil; + } + + AVCaptureDeviceDiscoverySession *captureDeviceDiscoverySession = + [AVCaptureDeviceDiscoverySession + discoverySessionWithDeviceTypes:deviceTypes + mediaType:mediaType + position:AVCaptureDevicePositionUnspecified]; + return [captureDeviceDiscoverySession devices]; +#else + return [AVCaptureDevice devicesWithMediaType:mediaType]; +#endif +} + static int avf_read_header(AVFormatContext *s) { int ret = 0; @@ -770,8 +829,8 @@ static int avf_read_header(AVFormatContext *s) AVCaptureDevice *video_device = nil; AVCaptureDevice *audio_device = nil; // Find capture device - NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; - NSArray *devices_muxed = [AVCaptureDevice devicesWithMediaType:AVMediaTypeMuxed]; + NSArray *devices = getDevicesWithMediaType(AVMediaTypeVideo); + NSArray *devices_muxed = getDevicesWithMediaType(AVMediaTypeMuxed); ctx->num_video_devices = [devices count] + [devices_muxed count]; @@ -806,7 +865,7 @@ static int avf_read_header(AVFormatContext *s) #endif av_log(ctx, AV_LOG_INFO, "AVFoundation audio devices:\n"); - devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio]; + devices = getDevicesWithMediaType(AVMediaTypeAudio); for (AVCaptureDevice *device in devices) { const char *name = [[device localizedName] UTF8String]; int index = [devices indexOfObject:device]; @@ -930,7 +989,7 @@ static int avf_read_header(AVFormatContext *s) // get audio device if (ctx->audio_device_index >= 0) { - NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio]; + NSArray *devices = getDevicesWithMediaType(AVMediaTypeAudio); if (ctx->audio_device_index >= [devices count]) { av_log(ctx, AV_LOG_ERROR, "Invalid audio device index\n"); @@ -943,7 +1002,7 @@ static int avf_read_header(AVFormatContext *s) if (!strncmp(ctx->audio_filename, "default", 7)) { audio_device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; } else { - NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio]; + NSArray *devices = getDevicesWithMediaType(AVMediaTypeAudio); for (AVCaptureDevice *device in devices) { if (!strncmp(ctx->audio_filename, [[device localizedName] UTF8String], strlen(ctx->audio_filename))) { @@ -1237,13 +1296,13 @@ static int avf_close(AVFormatContext *s) .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, }; -const AVInputFormat ff_avfoundation_demuxer = { - .name = "avfoundation", - .long_name = NULL_IF_CONFIG_SMALL("AVFoundation input device"), +const FFInputFormat ff_avfoundation_demuxer = { + .p.name = "avfoundation", + .p.long_name = NULL_IF_CONFIG_SMALL("AVFoundation input device"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &avf_class, .priv_data_size = sizeof(AVFContext), .read_header = avf_read_header, .read_packet = avf_read_packet, .read_close = avf_close, - .flags = AVFMT_NOFILE, - .priv_class = &avf_class, }; diff --git a/libavdevice/bktr.c b/libavdevice/bktr.c index 196637852f4..ac5b5348681 100644 --- a/libavdevice/bktr.c +++ b/libavdevice/bktr.c @@ -24,6 +24,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "libavutil/file_open.h" #include "libavutil/internal.h" @@ -264,6 +265,9 @@ static int grab_read_header(AVFormatContext *s1) AVRational framerate; int ret = 0; + av_log(s1, AV_LOG_WARNING, "bktr input is deprecated and will be removed. " + "Please contact the developers if you are interested in maintaining it.\n"); + if (!s->framerate) switch (s->standard) { case PAL: s->framerate = av_strdup("pal"); break; @@ -329,13 +333,13 @@ static int grab_read_close(AVFormatContext *s1) #define OFFSET(x) offsetof(VideoData, x) #define DEC AV_OPT_FLAG_DECODING_PARAM static const AVOption options[] = { - { "standard", "", offsetof(VideoData, standard), AV_OPT_TYPE_INT, {.i64 = VIDEO_FORMAT}, PAL, NTSCJ, AV_OPT_FLAG_DECODING_PARAM, "standard" }, - { "PAL", "", 0, AV_OPT_TYPE_CONST, {.i64 = PAL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "standard" }, - { "NTSC", "", 0, AV_OPT_TYPE_CONST, {.i64 = NTSC}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "standard" }, - { "SECAM", "", 0, AV_OPT_TYPE_CONST, {.i64 = SECAM}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "standard" }, - { "PALN", "", 0, AV_OPT_TYPE_CONST, {.i64 = PALN}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "standard" }, - { "PALM", "", 0, AV_OPT_TYPE_CONST, {.i64 = PALM}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "standard" }, - { "NTSCJ", "", 0, AV_OPT_TYPE_CONST, {.i64 = NTSCJ}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "standard" }, + { "standard", "", offsetof(VideoData, standard), AV_OPT_TYPE_INT, {.i64 = VIDEO_FORMAT}, PAL, NTSCJ, AV_OPT_FLAG_DECODING_PARAM, .unit = "standard" }, + { "PAL", "", 0, AV_OPT_TYPE_CONST, {.i64 = PAL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, .unit = "standard" }, + { "NTSC", "", 0, AV_OPT_TYPE_CONST, {.i64 = NTSC}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, .unit = "standard" }, + { "SECAM", "", 0, AV_OPT_TYPE_CONST, {.i64 = SECAM}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, .unit = "standard" }, + { "PALN", "", 0, AV_OPT_TYPE_CONST, {.i64 = PALN}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, .unit = "standard" }, + { "PALM", "", 0, AV_OPT_TYPE_CONST, {.i64 = PALM}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, .unit = "standard" }, + { "NTSCJ", "", 0, AV_OPT_TYPE_CONST, {.i64 = NTSCJ}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, .unit = "standard" }, { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, {.str = "vga"}, 0, 0, DEC }, { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, { NULL }, @@ -349,13 +353,13 @@ static const AVClass bktr_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, }; -const AVInputFormat ff_bktr_demuxer = { - .name = "bktr", - .long_name = NULL_IF_CONFIG_SMALL("video grab"), +const FFInputFormat ff_bktr_demuxer = { + .p.name = "bktr", + .p.long_name = NULL_IF_CONFIG_SMALL("video grab"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &bktr_class, .priv_data_size = sizeof(VideoData), .read_header = grab_read_header, .read_packet = grab_read_packet, .read_close = grab_read_close, - .flags = AVFMT_NOFILE, - .priv_class = &bktr_class, }; diff --git a/libavdevice/caca.c b/libavdevice/caca.c index 6af1649137e..c3b23297582 100644 --- a/libavdevice/caca.c +++ b/libavdevice/caca.c @@ -24,6 +24,13 @@ #include "libavformat/mux.h" #include "avdevice.h" +enum { + LIST_ALGORITHMS = 1 << 0, + LIST_ANTIALIASES = 1 << 1, + LIST_CHARSETS = 1 << 2, + LIST_COLORS = 1 << 3, +}; + typedef struct CACAContext { AVClass *class; AVFormatContext *ctx; @@ -38,7 +45,7 @@ typedef struct CACAContext { char *charset, *color; char *driver; - char *list_dither; + int list_dither; int list_drivers; } CACAContext; @@ -99,21 +106,14 @@ static int caca_write_header(AVFormatContext *s) return AVERROR_EXIT; } if (c->list_dither) { - if (!strcmp(c->list_dither, "colors")) { + if (c->list_dither & LIST_COLORS) list_dither_color(c); - } else if (!strcmp(c->list_dither, "charsets")) { + if (c->list_dither & LIST_CHARSETS) list_dither_charset(c); - } else if (!strcmp(c->list_dither, "algorithms")) { + if (c->list_dither & LIST_ALGORITHMS) list_dither_algorithm(c); - } else if (!strcmp(c->list_dither, "antialiases")) { + if (c->list_dither & LIST_ANTIALIASES) list_dither_antialias(c); - } else { - av_log(s, AV_LOG_ERROR, - "Invalid argument '%s', for 'list_dither' option\n" - "Argument must be one of 'algorithms, 'antialiases', 'charsets', 'colors'\n", - c->list_dither); - return AVERROR(EINVAL); - } return AVERROR_EXIT; } @@ -205,11 +205,11 @@ static const AVOption options[] = { { "charset", "set charset used to render output", OFFSET(charset), AV_OPT_TYPE_STRING, {.str = "default" }, 0, 0, ENC }, { "color", "set color used to render output", OFFSET(color), AV_OPT_TYPE_STRING, {.str = "default" }, 0, 0, ENC }, { "list_drivers", "list available drivers", OFFSET(list_drivers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, ENC }, - { "list_dither", "list available dither options", OFFSET(list_dither), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, ENC, "list_dither" }, - { "algorithms", NULL, 0, AV_OPT_TYPE_CONST, {.str = "algorithms"}, 0, 0, ENC, "list_dither" }, - { "antialiases", NULL, 0, AV_OPT_TYPE_CONST, {.str = "antialiases"},0, 0, ENC, "list_dither" }, - { "charsets", NULL, 0, AV_OPT_TYPE_CONST, {.str = "charsets"}, 0, 0, ENC, "list_dither" }, - { "colors", NULL, 0, AV_OPT_TYPE_CONST, {.str = "colors"}, 0, 0, ENC, "list_dither" }, + { "list_dither", "list available dither options", OFFSET(list_dither), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, 0, INT_MAX, ENC, .unit = "list_dither" }, + { "algorithms", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = LIST_ALGORITHMS }, 0, 0, ENC, .unit = "list_dither" }, + { "antialiases", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = LIST_ANTIALIASES }, 0, 0, ENC, .unit = "list_dither" }, + { "charsets", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = LIST_CHARSETS }, 0, 0, ENC, .unit = "list_dither" }, + { "colors", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = LIST_COLORS }, 0, 0, ENC, .unit = "list_dither" }, { NULL }, }; diff --git a/libavdevice/ccfifo.c b/libavdevice/ccfifo.c new file mode 100644 index 00000000000..9007094f0b3 --- /dev/null +++ b/libavdevice/ccfifo.c @@ -0,0 +1,24 @@ +/* + * CEA-708 Closed Captioning FIFO + * Copyright (c) 2023 LTN Global Communications + * + * Author: Devin Heitmueller + * + * 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 "libavfilter/ccfifo.c" diff --git a/libavdevice/decklink_common.cpp b/libavdevice/decklink_common.cpp index acd1f77e6c9..47de7ef6b0f 100644 --- a/libavdevice/decklink_common.cpp +++ b/libavdevice/decklink_common.cpp @@ -390,6 +390,116 @@ int ff_decklink_set_format(AVFormatContext *avctx, decklink_direction_t directio return ff_decklink_set_format(avctx, 0, 0, 0, 0, AV_FIELD_UNKNOWN, direction); } +void ff_decklink_packet_queue_init(AVFormatContext *avctx, DecklinkPacketQueue *q, int64_t queue_size) +{ + memset(q, 0, sizeof(DecklinkPacketQueue)); + pthread_mutex_init(&q->mutex, NULL); + pthread_cond_init(&q->cond, NULL); + q->avctx = avctx; + q->max_q_size = queue_size; +} + +void ff_decklink_packet_queue_flush(DecklinkPacketQueue *q) +{ + AVPacket pkt; + + pthread_mutex_lock(&q->mutex); + while (avpriv_packet_list_get(&q->pkt_list, &pkt) == 0) { + av_packet_unref(&pkt); + } + q->nb_packets = 0; + q->size = 0; + pthread_mutex_unlock(&q->mutex); +} + +void ff_decklink_packet_queue_end(DecklinkPacketQueue *q) +{ + ff_decklink_packet_queue_flush(q); + pthread_mutex_destroy(&q->mutex); + pthread_cond_destroy(&q->cond); +} + +unsigned long long ff_decklink_packet_queue_size(DecklinkPacketQueue *q) +{ + unsigned long long size; + pthread_mutex_lock(&q->mutex); + size = q->size; + pthread_mutex_unlock(&q->mutex); + return size; +} + +int ff_decklink_packet_queue_put(DecklinkPacketQueue *q, AVPacket *pkt) +{ + int pkt_size = pkt->size; + int ret; + + // Drop Packet if queue size is > maximum queue size + if (ff_decklink_packet_queue_size(q) > (uint64_t)q->max_q_size) { + av_packet_unref(pkt); + av_log(q->avctx, AV_LOG_WARNING, "Decklink input buffer overrun!\n"); + return -1; + } + /* ensure the packet is reference counted */ + if (av_packet_make_refcounted(pkt) < 0) { + av_packet_unref(pkt); + return -1; + } + + pthread_mutex_lock(&q->mutex); + + ret = avpriv_packet_list_put(&q->pkt_list, pkt, NULL, 0); + if (ret == 0) { + q->nb_packets++; + q->size += pkt_size + sizeof(AVPacket); + pthread_cond_signal(&q->cond); + } else { + av_packet_unref(pkt); + } + + pthread_mutex_unlock(&q->mutex); + return ret; +} + +int ff_decklink_packet_queue_get(DecklinkPacketQueue *q, AVPacket *pkt, int block) +{ + int ret; + + pthread_mutex_lock(&q->mutex); + + for (;; ) { + ret = avpriv_packet_list_get(&q->pkt_list, pkt); + if (ret == 0) { + q->nb_packets--; + q->size -= pkt->size + sizeof(AVPacket); + ret = 1; + break; + } else if (!block) { + ret = 0; + break; + } else { + pthread_cond_wait(&q->cond, &q->mutex); + } + } + pthread_mutex_unlock(&q->mutex); + return ret; +} + +int64_t ff_decklink_packet_queue_peekpts(DecklinkPacketQueue *q) +{ + PacketListEntry *pkt1; + int64_t pts = -1; + + pthread_mutex_lock(&q->mutex); + pkt1 = q->pkt_list.head; + if (pkt1) { + pts = pkt1->pkt.pts; + } + pthread_mutex_unlock(&q->mutex); + + return pts; +} + + int ff_decklink_list_devices(AVFormatContext *avctx, struct AVDeviceInfoList *device_list, int show_inputs, int show_outputs) diff --git a/libavdevice/decklink_common.h b/libavdevice/decklink_common.h index 79d6ac5b384..34ab1b96700 100644 --- a/libavdevice/decklink_common.h +++ b/libavdevice/decklink_common.h @@ -31,6 +31,7 @@ extern "C" { #include "libavcodec/packet_internal.h" +#include "libavfilter/ccfifo.h" } #include "libavutil/thread.h" #include "decklink_common_c.h" @@ -77,7 +78,7 @@ static char *dup_cfstring_to_utf8(CFStringRef w) class decklink_output_callback; class decklink_input_callback; -typedef struct AVPacketQueue { +typedef struct DecklinkPacketQueue { PacketList pkt_list; int nb_packets; unsigned long long size; @@ -86,7 +87,7 @@ typedef struct AVPacketQueue { pthread_cond_t cond; AVFormatContext *avctx; int64_t max_q_size; -} AVPacketQueue; +} DecklinkPacketQueue; struct decklink_ctx { /* DeckLink SDK interfaces */ @@ -110,7 +111,12 @@ struct decklink_ctx { int supports_vanc; /* Capture buffer queue */ - AVPacketQueue queue; + DecklinkPacketQueue queue; + + CCFifo cc_fifo; ///< closed captions + + /* Output VANC queue */ + DecklinkPacketQueue vanc_queue; /* Streams present */ int audio; @@ -118,6 +124,7 @@ struct decklink_ctx { /* Status */ int playback_started; + int64_t first_pts; int64_t last_pts; unsigned long frameCount; unsigned int dropped; @@ -231,4 +238,12 @@ int ff_decklink_list_formats(AVFormatContext *avctx, decklink_direction_t direct void ff_decklink_cleanup(AVFormatContext *avctx); int ff_decklink_init_device(AVFormatContext *avctx, const char* name); +void ff_decklink_packet_queue_init(AVFormatContext *avctx, DecklinkPacketQueue *q, int64_t queue_size); +void ff_decklink_packet_queue_flush(DecklinkPacketQueue *q); +void ff_decklink_packet_queue_end(DecklinkPacketQueue *q); +unsigned long long ff_decklink_packet_queue_size(DecklinkPacketQueue *q); +int ff_decklink_packet_queue_put(DecklinkPacketQueue *q, AVPacket *pkt); +int ff_decklink_packet_queue_get(DecklinkPacketQueue *q, AVPacket *pkt, int block); +int64_t ff_decklink_packet_queue_peekpts(DecklinkPacketQueue *q); + #endif /* AVDEVICE_DECKLINK_COMMON_H */ diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h index 75896ad32b2..9c55d891494 100644 --- a/libavdevice/decklink_common_c.h +++ b/libavdevice/decklink_common_c.h @@ -63,6 +63,7 @@ struct decklink_cctx { char *format_code; int raw_format; int64_t queue_size; + int64_t vanc_queue_size; int copyts; int64_t timestamp_align; int timing_offset; diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp index 7bf5e3724ca..671573853ba 100644 --- a/libavdevice/decklink_dec.cpp +++ b/libavdevice/decklink_dec.cpp @@ -471,120 +471,6 @@ static uint8_t *get_metadata(AVFormatContext *avctx, uint16_t *buf, size_t width return tgt; } -static void avpacket_queue_init(AVFormatContext *avctx, AVPacketQueue *q) -{ - struct decklink_cctx *ctx = (struct decklink_cctx *)avctx->priv_data; - memset(q, 0, sizeof(AVPacketQueue)); - pthread_mutex_init(&q->mutex, NULL); - pthread_cond_init(&q->cond, NULL); - q->avctx = avctx; - q->max_q_size = ctx->queue_size; -} - -static void avpacket_queue_flush(AVPacketQueue *q) -{ - PacketListEntry *pkt, *pkt1; - - pthread_mutex_lock(&q->mutex); - for (pkt = q->pkt_list.head; pkt != NULL; pkt = pkt1) { - pkt1 = pkt->next; - av_packet_unref(&pkt->pkt); - av_freep(&pkt); - } - q->pkt_list.head = NULL; - q->pkt_list.tail = NULL; - q->nb_packets = 0; - q->size = 0; - pthread_mutex_unlock(&q->mutex); -} - -static void avpacket_queue_end(AVPacketQueue *q) -{ - avpacket_queue_flush(q); - pthread_mutex_destroy(&q->mutex); - pthread_cond_destroy(&q->cond); -} - -static unsigned long long avpacket_queue_size(AVPacketQueue *q) -{ - unsigned long long size; - pthread_mutex_lock(&q->mutex); - size = q->size; - pthread_mutex_unlock(&q->mutex); - return size; -} - -static int avpacket_queue_put(AVPacketQueue *q, AVPacket *pkt) -{ - PacketListEntry *pkt1; - - // Drop Packet if queue size is > maximum queue size - if (avpacket_queue_size(q) > (uint64_t)q->max_q_size) { - av_packet_unref(pkt); - av_log(q->avctx, AV_LOG_WARNING, "Decklink input buffer overrun!\n"); - return -1; - } - /* ensure the packet is reference counted */ - if (av_packet_make_refcounted(pkt) < 0) { - av_packet_unref(pkt); - return -1; - } - - pkt1 = (PacketListEntry *)av_malloc(sizeof(*pkt1)); - if (!pkt1) { - av_packet_unref(pkt); - return -1; - } - av_packet_move_ref(&pkt1->pkt, pkt); - pkt1->next = NULL; - - pthread_mutex_lock(&q->mutex); - - if (!q->pkt_list.tail) { - q->pkt_list.head = pkt1; - } else { - q->pkt_list.tail->next = pkt1; - } - - q->pkt_list.tail = pkt1; - q->nb_packets++; - q->size += pkt1->pkt.size + sizeof(*pkt1); - - pthread_cond_signal(&q->cond); - - pthread_mutex_unlock(&q->mutex); - return 0; -} - -static int avpacket_queue_get(AVPacketQueue *q, AVPacket *pkt, int block) -{ - int ret; - - pthread_mutex_lock(&q->mutex); - - for (;; ) { - PacketListEntry *pkt1 = q->pkt_list.head; - if (pkt1) { - q->pkt_list.head = pkt1->next; - if (!q->pkt_list.head) { - q->pkt_list.tail = NULL; - } - q->nb_packets--; - q->size -= pkt1->pkt.size + sizeof(*pkt1); - *pkt = pkt1->pkt; - av_free(pkt1); - ret = 1; - break; - } else if (!block) { - ret = 0; - break; - } else { - pthread_cond_wait(&q->cond, &q->mutex); - } - } - pthread_mutex_unlock(&q->mutex); - return ret; -} static void handle_klv(AVFormatContext *avctx, decklink_ctx *ctx, IDeckLinkVideoInputFrame *videoFrame, int64_t pts) { @@ -682,7 +568,7 @@ static void handle_klv(AVFormatContext *avctx, decklink_ctx *ctx, IDeckLinkVideo klv_packet.data = klv.data(); klv_packet.size = klv.size(); - if (avpacket_queue_put(&ctx->queue, &klv_packet) < 0) { + if (ff_decklink_packet_queue_put(&ctx->queue, &klv_packet) < 0) { ++ctx->dropped; } } @@ -691,7 +577,7 @@ static void handle_klv(AVFormatContext *avctx, decklink_ctx *ctx, IDeckLinkVideo class decklink_input_callback : public IDeckLinkInputCallback { public: - decklink_input_callback(AVFormatContext *_avctx); + explicit decklink_input_callback(AVFormatContext *_avctx); ~decklink_input_callback(); virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; } @@ -874,7 +760,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( if (videoFrame) { AVPacket pkt = { 0 }; if (ctx->frameCount % 25 == 0) { - unsigned long long qsize = avpacket_queue_size(&ctx->queue); + unsigned long long qsize = ff_decklink_packet_queue_size(&ctx->queue); av_log(avctx, AV_LOG_DEBUG, "Frame received (#%lu) - Valid (%liB) - QSize %fMB\n", ctx->frameCount, @@ -1038,7 +924,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( txt_pkt.stream_index = ctx->teletext_st->index; txt_pkt.data = txt_buf0; txt_pkt.size = txt_buf - txt_buf0; - if (avpacket_queue_put(&ctx->queue, &txt_pkt) < 0) { + if (ff_decklink_packet_queue_put(&ctx->queue, &txt_pkt) < 0) { ++ctx->dropped; } } @@ -1049,7 +935,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( if (pkt.buf) videoFrame->AddRef(); - if (avpacket_queue_put(&ctx->queue, &pkt) < 0) { + if (ff_decklink_packet_queue_put(&ctx->queue, &pkt) < 0) { ++ctx->dropped; } } @@ -1071,7 +957,7 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( pkt.stream_index = ctx->audio_st->index; pkt.data = (uint8_t *)audioFrameBytes; - if (avpacket_queue_put(&ctx->queue, &pkt) < 0) { + if (ff_decklink_packet_queue_put(&ctx->queue, &pkt) < 0) { ++ctx->dropped; } } @@ -1153,7 +1039,7 @@ av_cold int ff_decklink_read_close(AVFormatContext *avctx) } ff_decklink_cleanup(avctx); - avpacket_queue_end(&ctx->queue); + ff_decklink_packet_queue_end(&ctx->queue); av_freep(&cctx->ctx); @@ -1411,7 +1297,7 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) goto error; } - avpacket_queue_init (avctx, &ctx->queue); + ff_decklink_packet_queue_init(avctx, &ctx->queue, cctx->queue_size); if (ctx->dli->StartStreams() != S_OK) { av_log(avctx, AV_LOG_ERROR, "Cannot start input stream\n"); @@ -1431,7 +1317,7 @@ int ff_decklink_read_packet(AVFormatContext *avctx, AVPacket *pkt) struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx; - avpacket_queue_get(&ctx->queue, pkt, 1); + ff_decklink_packet_queue_get(&ctx->queue, pkt, 1); if (ctx->tc_format && !(av_dict_get(ctx->video_st->metadata, "timecode", NULL, 0))) { size_t size; diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c index 2159702c96b..e211c9d3f4e 100644 --- a/libavdevice/decklink_dec_c.c +++ b/libavdevice/decklink_dec_c.c @@ -21,6 +21,7 @@ */ #include "libavformat/avformat.h" +#include "libavformat/demux.h" #include "libavutil/opt.h" #include "decklink_common_c.h" @@ -33,67 +34,67 @@ static const AVOption options[] = { { "list_devices", "use ffmpeg -sources decklink instead", OFFSET(list_devices), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC | AV_OPT_FLAG_DEPRECATED}, { "list_formats", "list supported formats" , OFFSET(list_formats), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, DEC }, { "format_code", "set format by fourcc" , OFFSET(format_code), AV_OPT_TYPE_STRING, { .str = NULL}, 0, 0, DEC }, - { "raw_format", "pixel format to be returned by the card when capturing" , OFFSET(raw_format), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 5, DEC, "raw_format" }, - { "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, DEC, "raw_format"}, - { "uyvy422", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, DEC, "raw_format"}, - { "yuv422p10", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, DEC, "raw_format"}, - { "argb", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3 }, 0, 0, DEC, "raw_format"}, - { "bgra", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 4 }, 0, 0, DEC, "raw_format"}, - { "rgb10", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 5 }, 0, 0, DEC, "raw_format"}, + { "raw_format", "pixel format to be returned by the card when capturing" , OFFSET(raw_format), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 5, DEC, .unit = "raw_format" }, + { "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, DEC, .unit = "raw_format"}, + { "uyvy422", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, DEC, .unit = "raw_format"}, + { "yuv422p10", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, DEC, .unit = "raw_format"}, + { "argb", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3 }, 0, 0, DEC, .unit = "raw_format"}, + { "bgra", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 4 }, 0, 0, DEC, .unit = "raw_format"}, + { "rgb10", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 5 }, 0, 0, DEC, .unit = "raw_format"}, { "enable_klv", "output klv if present in vanc", OFFSET(enable_klv), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, DEC }, - { "teletext_lines", "teletext lines bitmask", OFFSET(teletext_lines), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, 0x7ffffffffLL, DEC, "teletext_lines"}, - { "standard", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0x7fff9fffeLL}, 0, 0, DEC, "teletext_lines"}, - { "all", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0x7ffffffffLL}, 0, 0, DEC, "teletext_lines"}, + { "teletext_lines", "teletext lines bitmask", OFFSET(teletext_lines), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, 0x7ffffffffLL, DEC, .unit = "teletext_lines"}, + { "standard", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0x7fff9fffeLL}, 0, 0, DEC, .unit = "teletext_lines"}, + { "all", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0x7ffffffffLL}, 0, 0, DEC, .unit = "teletext_lines"}, { "channels", "number of audio channels", OFFSET(audio_channels), AV_OPT_TYPE_INT , { .i64 = 2 }, 2, 16, DEC }, #if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000 - { "duplex_mode", "duplex mode", OFFSET(duplex_mode), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 5, DEC, "duplex_mode"}, + { "duplex_mode", "duplex mode", OFFSET(duplex_mode), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 5, DEC, .unit = "duplex_mode"}, #else - { "duplex_mode", "duplex mode", OFFSET(duplex_mode), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, DEC, "duplex_mode"}, + { "duplex_mode", "duplex mode", OFFSET(duplex_mode), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, DEC, .unit = "duplex_mode"}, #endif - { "unset", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0}, 0, 0, DEC, "duplex_mode"}, - { "half", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1}, 0, 0, DEC, "duplex_mode"}, - { "full", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2}, 0, 0, DEC, "duplex_mode"}, + { "unset", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0}, 0, 0, DEC, .unit = "duplex_mode"}, + { "half", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1}, 0, 0, DEC, .unit = "duplex_mode"}, + { "full", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2}, 0, 0, DEC, .unit = "duplex_mode"}, #if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000 - { "one_sub_device_full", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2}, 0, 0, DEC, "duplex_mode"}, - { "one_sub_device_half", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3}, 0, 0, DEC, "duplex_mode"}, - { "two_sub_device_full", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 4}, 0, 0, DEC, "duplex_mode"}, - { "four_sub_device_half", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 5}, 0, 0, DEC, "duplex_mode"}, + { "one_sub_device_full", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2}, 0, 0, DEC, .unit = "duplex_mode"}, + { "one_sub_device_half", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3}, 0, 0, DEC, .unit = "duplex_mode"}, + { "two_sub_device_full", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 4}, 0, 0, DEC, .unit = "duplex_mode"}, + { "four_sub_device_half", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 5}, 0, 0, DEC, .unit = "duplex_mode"}, #endif - { "timecode_format", "timecode format", OFFSET(tc_format), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 8, DEC, "tc_format"}, - { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0}, 0, 0, DEC, "tc_format"}, - { "rp188vitc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1}, 0, 0, DEC, "tc_format"}, - { "rp188vitc2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2}, 0, 0, DEC, "tc_format"}, - { "rp188ltc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3}, 0, 0, DEC, "tc_format"}, - { "rp188any", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 4}, 0, 0, DEC, "tc_format"}, - { "vitc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 5}, 0, 0, DEC, "tc_format"}, - { "vitc2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 6}, 0, 0, DEC, "tc_format"}, - { "serial", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 7}, 0, 0, DEC, "tc_format"}, + { "timecode_format", "timecode format", OFFSET(tc_format), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 8, DEC, .unit = "tc_format"}, + { "none", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0}, 0, 0, DEC, .unit = "tc_format"}, + { "rp188vitc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1}, 0, 0, DEC, .unit = "tc_format"}, + { "rp188vitc2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2}, 0, 0, DEC, .unit = "tc_format"}, + { "rp188ltc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3}, 0, 0, DEC, .unit = "tc_format"}, + { "rp188any", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 4}, 0, 0, DEC, .unit = "tc_format"}, + { "vitc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 5}, 0, 0, DEC, .unit = "tc_format"}, + { "vitc2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 6}, 0, 0, DEC, .unit = "tc_format"}, + { "serial", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 7}, 0, 0, DEC, .unit = "tc_format"}, #if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000 - { "rp188hfr", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 8}, 0, 0, DEC, "tc_format"}, + { "rp188hfr", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 8}, 0, 0, DEC, .unit = "tc_format"}, #endif - { "video_input", "video input", OFFSET(video_input), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 6, DEC, "video_input"}, - { "unset", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0}, 0, 0, DEC, "video_input"}, - { "sdi", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1}, 0, 0, DEC, "video_input"}, - { "hdmi", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2}, 0, 0, DEC, "video_input"}, - { "optical_sdi", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3}, 0, 0, DEC, "video_input"}, - { "component", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 4}, 0, 0, DEC, "video_input"}, - { "composite", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 5}, 0, 0, DEC, "video_input"}, - { "s_video", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 6}, 0, 0, DEC, "video_input"}, - { "audio_input", "audio input", OFFSET(audio_input), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 6, DEC, "audio_input"}, - { "unset", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0}, 0, 0, DEC, "audio_input"}, - { "embedded", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1}, 0, 0, DEC, "audio_input"}, - { "aes_ebu", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2}, 0, 0, DEC, "audio_input"}, - { "analog", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3}, 0, 0, DEC, "audio_input"}, - { "analog_xlr", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 4}, 0, 0, DEC, "audio_input"}, - { "analog_rca", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 5}, 0, 0, DEC, "audio_input"}, - { "microphone", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 6}, 0, 0, DEC, "audio_input"}, - { "audio_pts", "audio pts source", OFFSET(audio_pts_source), AV_OPT_TYPE_INT, { .i64 = PTS_SRC_AUDIO }, 1, PTS_SRC_NB-1, DEC, "pts_source"}, - { "video_pts", "video pts source", OFFSET(video_pts_source), AV_OPT_TYPE_INT, { .i64 = PTS_SRC_VIDEO }, 1, PTS_SRC_NB-1, DEC, "pts_source"}, - { "audio", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PTS_SRC_AUDIO }, 0, 0, DEC, "pts_source"}, - { "video", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PTS_SRC_VIDEO }, 0, 0, DEC, "pts_source"}, - { "reference", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PTS_SRC_REFERENCE}, 0, 0, DEC, "pts_source"}, - { "wallclock", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PTS_SRC_WALLCLOCK}, 0, 0, DEC, "pts_source"}, - { "abs_wallclock", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PTS_SRC_ABS_WALLCLOCK}, 0, 0, DEC, "pts_source"}, + { "video_input", "video input", OFFSET(video_input), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 6, DEC, .unit = "video_input"}, + { "unset", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0}, 0, 0, DEC, .unit = "video_input"}, + { "sdi", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1}, 0, 0, DEC, .unit = "video_input"}, + { "hdmi", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2}, 0, 0, DEC, .unit = "video_input"}, + { "optical_sdi", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3}, 0, 0, DEC, .unit = "video_input"}, + { "component", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 4}, 0, 0, DEC, .unit = "video_input"}, + { "composite", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 5}, 0, 0, DEC, .unit = "video_input"}, + { "s_video", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 6}, 0, 0, DEC, .unit = "video_input"}, + { "audio_input", "audio input", OFFSET(audio_input), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 6, DEC, .unit = "audio_input"}, + { "unset", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0}, 0, 0, DEC, .unit = "audio_input"}, + { "embedded", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1}, 0, 0, DEC, .unit = "audio_input"}, + { "aes_ebu", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2}, 0, 0, DEC, .unit = "audio_input"}, + { "analog", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 3}, 0, 0, DEC, .unit = "audio_input"}, + { "analog_xlr", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 4}, 0, 0, DEC, .unit = "audio_input"}, + { "analog_rca", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 5}, 0, 0, DEC, .unit = "audio_input"}, + { "microphone", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 6}, 0, 0, DEC, .unit = "audio_input"}, + { "audio_pts", "audio pts source", OFFSET(audio_pts_source), AV_OPT_TYPE_INT, { .i64 = PTS_SRC_AUDIO }, 1, PTS_SRC_NB-1, DEC, .unit = "pts_source"}, + { "video_pts", "video pts source", OFFSET(video_pts_source), AV_OPT_TYPE_INT, { .i64 = PTS_SRC_VIDEO }, 1, PTS_SRC_NB-1, DEC, .unit = "pts_source"}, + { "audio", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PTS_SRC_AUDIO }, 0, 0, DEC, .unit = "pts_source"}, + { "video", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PTS_SRC_VIDEO }, 0, 0, DEC, .unit = "pts_source"}, + { "reference", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PTS_SRC_REFERENCE}, 0, 0, DEC, .unit = "pts_source"}, + { "wallclock", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PTS_SRC_WALLCLOCK}, 0, 0, DEC, .unit = "pts_source"}, + { "abs_wallclock", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PTS_SRC_ABS_WALLCLOCK}, 0, 0, DEC, .unit = "pts_source"}, { "draw_bars", "draw bars on signal loss" , OFFSET(draw_bars), AV_OPT_TYPE_BOOL, { .i64 = 1}, 0, 1, DEC }, { "queue_size", "input queue buffer size", OFFSET(queue_size), AV_OPT_TYPE_INT64, { .i64 = (1024 * 1024 * 1024)}, 0, INT64_MAX, DEC }, { "audio_depth", "audio bitdepth (16 or 32)", OFFSET(audio_depth), AV_OPT_TYPE_INT, { .i64 = 16}, 16, 32, DEC }, @@ -111,11 +112,11 @@ static const AVClass decklink_demuxer_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, }; -const AVInputFormat ff_decklink_demuxer = { - .name = "decklink", - .long_name = NULL_IF_CONFIG_SMALL("Blackmagic DeckLink input"), - .flags = AVFMT_NOFILE, - .priv_class = &decklink_demuxer_class, +const FFInputFormat ff_decklink_demuxer = { + .p.name = "decklink", + .p.long_name = NULL_IF_CONFIG_SMALL("Blackmagic DeckLink input"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &decklink_demuxer_class, .priv_data_size = sizeof(struct decklink_cctx), .get_device_list = ff_decklink_list_input_devices, .read_header = ff_decklink_read_header, diff --git a/libavdevice/decklink_enc.cpp b/libavdevice/decklink_enc.cpp index fb686b9032d..cb8f91730ee 100644 --- a/libavdevice/decklink_enc.cpp +++ b/libavdevice/decklink_enc.cpp @@ -32,6 +32,8 @@ extern "C" { extern "C" { #include "libavformat/avformat.h" +#include "libavcodec/bytestream.h" +#include "libavutil/frame.h" #include "libavutil/internal.h" #include "libavutil/imgutils.h" #include "avdevice.h" @@ -243,19 +245,32 @@ static int decklink_setup_audio(AVFormatContext *avctx, AVStream *st) av_log(avctx, AV_LOG_ERROR, "Only one audio stream is supported!\n"); return -1; } - if (c->sample_rate != 48000) { - av_log(avctx, AV_LOG_ERROR, "Unsupported sample rate!" - " Only 48kHz is supported.\n"); - return -1; - } - if (c->ch_layout.nb_channels != 2 && c->ch_layout.nb_channels != 8 && c->ch_layout.nb_channels != 16) { - av_log(avctx, AV_LOG_ERROR, "Unsupported number of channels!" - " Only 2, 8 or 16 channels are supported.\n"); + + if (c->codec_id == AV_CODEC_ID_AC3) { + /* Regardless of the number of channels in the codec, we're only + using 2 SDI audio channels at 48000Hz */ + ctx->channels = 2; + } else if (c->codec_id == AV_CODEC_ID_PCM_S16LE) { + if (c->sample_rate != 48000) { + av_log(avctx, AV_LOG_ERROR, "Unsupported sample rate!" + " Only 48kHz is supported.\n"); + return -1; + } + if (c->ch_layout.nb_channels != 2 && c->ch_layout.nb_channels != 8 && c->ch_layout.nb_channels != 16) { + av_log(avctx, AV_LOG_ERROR, "Unsupported number of channels!" + " Only 2, 8 or 16 channels are supported.\n"); + return -1; + } + ctx->channels = c->ch_layout.nb_channels; + } else { + av_log(avctx, AV_LOG_ERROR, "Unsupported codec specified!" + " Only PCM_S16LE and AC-3 are supported.\n"); return -1; } + if (ctx->dlo->EnableAudioOutput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, - c->ch_layout.nb_channels, + ctx->channels, bmdAudioOutputStreamTimestamped) != S_OK) { av_log(avctx, AV_LOG_ERROR, "Could not enable audio output!\n"); return -1; @@ -266,14 +281,90 @@ static int decklink_setup_audio(AVFormatContext *avctx, AVStream *st) } /* The device expects the sample rate to be fixed. */ - avpriv_set_pts_info(st, 64, 1, c->sample_rate); - ctx->channels = c->ch_layout.nb_channels; + avpriv_set_pts_info(st, 64, 1, 48000); ctx->audio = 1; return 0; } +/* Wrap the AC-3 packet into an S337 payload that is in S16LE format which can be easily + injected into the PCM stream. Note: despite the function name, only AC-3 is implemented */ +static int create_s337_payload(AVPacket *pkt, uint8_t **outbuf, int *outsize) +{ + /* Note: if the packet size is not divisible by four, we need to make the actual + payload larger to ensure it ends on an two channel S16LE boundary */ + int payload_size = FFALIGN(pkt->size, 4) + 8; + uint16_t bitcount = pkt->size * 8; + uint8_t *s337_payload; + PutByteContext pb; + + /* Sanity check: According to SMPTE ST 340:2015 Sec 4.1, the AC-3 sync frame will + exactly match the 1536 samples of baseband (PCM) audio that it represents. */ + if (pkt->size > 1536) + return AVERROR(EINVAL); + + /* Encapsulate AC3 syncframe into SMPTE 337 packet */ + s337_payload = (uint8_t *) av_malloc(payload_size); + if (s337_payload == NULL) + return AVERROR(ENOMEM); + bytestream2_init_writer(&pb, s337_payload, payload_size); + bytestream2_put_le16u(&pb, 0xf872); /* Sync word 1 */ + bytestream2_put_le16u(&pb, 0x4e1f); /* Sync word 1 */ + bytestream2_put_le16u(&pb, 0x0001); /* Burst Info, including data type (1=ac3) */ + bytestream2_put_le16u(&pb, bitcount); /* Length code */ + for (int i = 0; i < (pkt->size - 1); i += 2) + bytestream2_put_le16u(&pb, (pkt->data[i] << 8) | pkt->data[i+1]); + + /* Ensure final payload is aligned on 4-byte boundary */ + if (pkt->size & 1) + bytestream2_put_le16u(&pb, pkt->data[pkt->size - 1] << 8); + if ((pkt->size & 3) == 1 || (pkt->size & 3) == 2) + bytestream2_put_le16u(&pb, 0); + + *outsize = payload_size; + *outbuf = s337_payload; + return 0; +} + +static int decklink_setup_subtitle(AVFormatContext *avctx, AVStream *st) +{ + int ret = -1; + + switch(st->codecpar->codec_id) { +#if CONFIG_LIBKLVANC + case AV_CODEC_ID_EIA_608: + /* No special setup required */ + ret = 0; + break; +#endif + default: + av_log(avctx, AV_LOG_ERROR, "Unsupported subtitle codec specified\n"); + break; + } + + return ret; +} + +static int decklink_setup_data(AVFormatContext *avctx, AVStream *st) +{ + int ret = -1; + + switch(st->codecpar->codec_id) { +#if CONFIG_LIBKLVANC + case AV_CODEC_ID_SMPTE_2038: + /* No specific setup required */ + ret = 0; + break; +#endif + default: + av_log(avctx, AV_LOG_ERROR, "Unsupported data codec specified\n"); + break; + } + + return ret; +} + av_cold int ff_decklink_write_trailer(AVFormatContext *avctx) { struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; @@ -299,7 +390,9 @@ av_cold int ff_decklink_write_trailer(AVFormatContext *avctx) #if CONFIG_LIBKLVANC klvanc_context_destroy(ctx->vanc_ctx); #endif + ff_decklink_packet_queue_end(&ctx->vanc_queue); + ff_ccfifo_uninit(&ctx->cc_fifo); av_freep(&cctx->ctx); return 0; @@ -328,7 +421,7 @@ static void construct_cc(AVFormatContext *avctx, struct decklink_ctx *ctx, ret = klvanc_set_framerate_EIA_708B(cdp, ctx->bmd_tb_num, ctx->bmd_tb_den); if (ret) { - av_log(avctx, AV_LOG_ERROR, "Invalid framerate specified: %lld/%lld\n", + av_log(avctx, AV_LOG_ERROR, "Invalid framerate specified: %" PRId64 "/%" PRId64 "\n", ctx->bmd_tb_num, ctx->bmd_tb_den); klvanc_destroy_eia708_cdp(cdp); return; @@ -367,8 +460,108 @@ static void construct_cc(AVFormatContext *avctx, struct decklink_ctx *ctx, } } +/* See SMPTE ST 2016-3:2009 */ +static void construct_afd(AVFormatContext *avctx, struct decklink_ctx *ctx, + AVPacket *pkt, struct klvanc_line_set_s *vanc_lines, + AVStream *st) +{ + struct klvanc_packet_afd_s *afd = NULL; + uint16_t *afd_words = NULL; + uint16_t len; + size_t size; + int f1_line = 12, f2_line = 0, ret; + + const uint8_t *data = av_packet_get_side_data(pkt, AV_PKT_DATA_AFD, &size); + if (!data || size == 0) + return; + + ret = klvanc_create_AFD(&afd); + if (ret) + return; + + ret = klvanc_set_AFD_val(afd, data[0]); + if (ret) { + av_log(avctx, AV_LOG_ERROR, "Invalid AFD value specified: %d\n", + data[0]); + klvanc_destroy_AFD(afd); + return; + } + + /* Compute the AR flag based on the DAR (see ST 2016-1:2009 Sec 9.1). Note, we treat + anything below 1.4 as 4:3 (as opposed to the standard 1.33), because there are lots + of streams in the field that aren't *exactly* 4:3 but a tiny bit larger after doing + the math... */ + if (av_cmp_q((AVRational) {st->codecpar->width * st->codecpar->sample_aspect_ratio.num, + st->codecpar->height * st->codecpar->sample_aspect_ratio.den}, (AVRational) {14, 10}) == 1) + afd->aspectRatio = ASPECT_16x9; + else + afd->aspectRatio = ASPECT_4x3; + + ret = klvanc_convert_AFD_to_words(afd, &afd_words, &len); + if (ret) { + av_log(avctx, AV_LOG_ERROR, "Failed converting AFD packet to words\n"); + goto out; + } + + ret = klvanc_line_insert(ctx->vanc_ctx, vanc_lines, afd_words, len, f1_line, 0); + if (ret) { + av_log(avctx, AV_LOG_ERROR, "VANC line insertion failed\n"); + goto out; + } + + /* For interlaced video, insert into both fields. Switching lines for field 2 + derived from SMPTE RP 168:2009, Sec 6, Table 2. */ + switch (ctx->bmd_mode) { + case bmdModeNTSC: + case bmdModeNTSC2398: + f2_line = 273 - 10 + f1_line; + break; + case bmdModePAL: + f2_line = 319 - 6 + f1_line; + break; + case bmdModeHD1080i50: + case bmdModeHD1080i5994: + case bmdModeHD1080i6000: + f2_line = 569 - 7 + f1_line; + break; + default: + f2_line = 0; + break; + } + + if (f2_line > 0) { + ret = klvanc_line_insert(ctx->vanc_ctx, vanc_lines, afd_words, len, f2_line, 0); + if (ret) { + av_log(avctx, AV_LOG_ERROR, "VANC line insertion failed\n"); + goto out; + } + } + +out: + if (afd) + klvanc_destroy_AFD(afd); + if (afd_words) + free(afd_words); +} + +/* Parse any EIA-608 subtitles sitting on the queue, and write packet side data + that will later be handled by construct_cc... */ +static void parse_608subs(AVFormatContext *avctx, struct decklink_ctx *ctx, AVPacket *pkt) +{ + size_t cc_size = ff_ccfifo_getoutputsize(&ctx->cc_fifo); + uint8_t *cc_data; + + if (!ff_ccfifo_ccdetected(&ctx->cc_fifo)) + return; + + cc_data = av_packet_new_side_data(pkt, AV_PKT_DATA_A53_CC, cc_size); + if (cc_data) + ff_ccfifo_injectbytes(&ctx->cc_fifo, cc_data, cc_size); +} + static int decklink_construct_vanc(AVFormatContext *avctx, struct decklink_ctx *ctx, - AVPacket *pkt, decklink_frame *frame) + AVPacket *pkt, decklink_frame *frame, + AVStream *st) { struct klvanc_line_set_s vanc_lines = { 0 }; int ret = 0, i; @@ -376,7 +569,61 @@ static int decklink_construct_vanc(AVFormatContext *avctx, struct decklink_ctx * if (!ctx->supports_vanc) return 0; + parse_608subs(avctx, ctx, pkt); construct_cc(avctx, ctx, pkt, &vanc_lines); + construct_afd(avctx, ctx, pkt, &vanc_lines, st); + + /* See if there any pending data packets to process */ + while (ff_decklink_packet_queue_size(&ctx->vanc_queue) > 0) { + AVStream *vanc_st; + AVPacket vanc_pkt; + int64_t pts; + + pts = ff_decklink_packet_queue_peekpts(&ctx->vanc_queue); + if (pts > ctx->last_pts) { + /* We haven't gotten to the video frame we are supposed to inject + the oldest VANC packet into yet, so leave it on the queue... */ + break; + } + + ret = ff_decklink_packet_queue_get(&ctx->vanc_queue, &vanc_pkt, 1); + if (vanc_pkt.pts + 1 < ctx->last_pts) { + av_log(avctx, AV_LOG_WARNING, "VANC packet too old, throwing away\n"); + av_packet_unref(&vanc_pkt); + continue; + } + + vanc_st = avctx->streams[vanc_pkt.stream_index]; + if (vanc_st->codecpar->codec_id == AV_CODEC_ID_SMPTE_2038) { + struct klvanc_smpte2038_anc_data_packet_s *pkt_2038 = NULL; + + klvanc_smpte2038_parse_pes_payload(vanc_pkt.data, vanc_pkt.size, &pkt_2038); + if (pkt_2038 == NULL) { + av_log(avctx, AV_LOG_ERROR, "failed to decode SMPTE 2038 PES packet"); + av_packet_unref(&vanc_pkt); + continue; + } + for (int i = 0; i < pkt_2038->lineCount; i++) { + struct klvanc_smpte2038_anc_data_line_s *l = &pkt_2038->lines[i]; + uint16_t *vancWords = NULL; + uint16_t vancWordCount; + + if (klvanc_smpte2038_convert_line_to_words(l, &vancWords, + &vancWordCount) < 0) + break; + + ret = klvanc_line_insert(ctx->vanc_ctx, &vanc_lines, vancWords, + vancWordCount, l->line_number, 0); + free(vancWords); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "VANC line insertion failed\n"); + break; + } + } + klvanc_smpte2038_anc_data_packet_free(pkt_2038); + } + av_packet_unref(&vanc_pkt); + } IDeckLinkVideoFrameAncillary *vanc; int result = ctx->dlo->CreateAncillaryData(bmdFormat10BitYUV, &vanc); @@ -441,6 +688,8 @@ static int decklink_write_video_packet(AVFormatContext *avctx, AVPacket *pkt) uint32_t buffered; HRESULT hr; + ctx->last_pts = FFMAX(ctx->last_pts, pkt->pts); + if (st->codecpar->codec_id == AV_CODEC_ID_WRAPPED_AVFRAME) { if (tmp->format != AV_PIX_FMT_UYVY422 || tmp->width != ctx->bmd_width || @@ -466,7 +715,7 @@ static int decklink_write_video_packet(AVFormatContext *avctx, AVPacket *pkt) frame = new decklink_frame(ctx, avpacket, st->codecpar->codec_id, ctx->bmd_height, ctx->bmd_width); #if CONFIG_LIBKLVANC - if (decklink_construct_vanc(avctx, ctx, pkt, frame)) + if (decklink_construct_vanc(avctx, ctx, pkt, frame, st)) av_log(avctx, AV_LOG_ERROR, "Failed to construct VANC\n"); #endif } @@ -486,6 +735,9 @@ static int decklink_write_video_packet(AVFormatContext *avctx, AVPacket *pkt) ctx->frames_buffer_available_spots--; pthread_mutex_unlock(&ctx->mutex); + if (ctx->first_pts == AV_NOPTS_VALUE) + ctx->first_pts = pkt->pts; + /* Schedule frame for playback. */ hr = ctx->dlo->ScheduleVideoFrame((class IDeckLinkVideoFrame *) frame, pkt->pts * ctx->bmd_tb_num, @@ -505,14 +757,14 @@ static int decklink_write_video_packet(AVFormatContext *avctx, AVPacket *pkt) " Video may misbehave!\n"); /* Preroll video frames. */ - if (!ctx->playback_started && pkt->pts > ctx->frames_preroll) { + if (!ctx->playback_started && pkt->pts > (ctx->first_pts + ctx->frames_preroll)) { av_log(avctx, AV_LOG_DEBUG, "Ending audio preroll.\n"); if (ctx->audio && ctx->dlo->EndAudioPreroll() != S_OK) { av_log(avctx, AV_LOG_ERROR, "Could not end audio preroll!\n"); return AVERROR(EIO); } av_log(avctx, AV_LOG_DEBUG, "Starting scheduled playback.\n"); - if (ctx->dlo->StartScheduledPlayback(0, ctx->bmd_tb_den, 1.0) != S_OK) { + if (ctx->dlo->StartScheduledPlayback(ctx->first_pts * ctx->bmd_tb_num, ctx->bmd_tb_den, 1.0) != S_OK) { av_log(avctx, AV_LOG_ERROR, "Could not start scheduled playback!\n"); return AVERROR(EIO); } @@ -526,18 +778,58 @@ static int decklink_write_audio_packet(AVFormatContext *avctx, AVPacket *pkt) { struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx; - int sample_count = pkt->size / (ctx->channels << 1); + AVStream *st = avctx->streams[pkt->stream_index]; + int sample_count; uint32_t buffered; + uint8_t *outbuf = NULL; + int ret = 0; ctx->dlo->GetBufferedAudioSampleFrameCount(&buffered); if (pkt->pts > 1 && !buffered) av_log(avctx, AV_LOG_WARNING, "There's no buffered audio." " Audio will misbehave!\n"); - if (ctx->dlo->ScheduleAudioSamples(pkt->data, sample_count, pkt->pts, + if (st->codecpar->codec_id == AV_CODEC_ID_AC3) { + /* Encapsulate AC3 syncframe into SMPTE 337 packet */ + int outbuf_size; + ret = create_s337_payload(pkt, &outbuf, &outbuf_size); + if (ret < 0) + return ret; + sample_count = outbuf_size / 4; + } else { + sample_count = pkt->size / (ctx->channels << 1); + outbuf = pkt->data; + } + + if (ctx->dlo->ScheduleAudioSamples(outbuf, sample_count, pkt->pts, bmdAudioSampleRate48kHz, NULL) != S_OK) { av_log(avctx, AV_LOG_ERROR, "Could not schedule audio samples.\n"); - return AVERROR(EIO); + ret = AVERROR(EIO); + } + + if (st->codecpar->codec_id == AV_CODEC_ID_AC3) + av_freep(&outbuf); + + return ret; +} + +static int decklink_write_subtitle_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; + struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx; + + ff_ccfifo_extractbytes(&ctx->cc_fifo, pkt->data, pkt->size); + + return 0; +} + +static int decklink_write_data_packet(AVFormatContext *avctx, AVPacket *pkt) +{ + struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; + struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx; + + if (ff_decklink_packet_queue_put(&ctx->vanc_queue, pkt) < 0) { + av_log(avctx, AV_LOG_WARNING, "Failed to queue DATA packet\n"); } return 0; @@ -559,6 +851,7 @@ av_cold int ff_decklink_write_header(AVFormatContext *avctx) ctx->list_formats = cctx->list_formats; ctx->preroll = cctx->preroll; ctx->duplex_mode = cctx->duplex_mode; + ctx->first_pts = AV_NOPTS_VALUE; if (cctx->link > 0 && (unsigned int)cctx->link < FF_ARRAY_ELEMS(decklink_link_conf_map)) ctx->link = decklink_link_conf_map[cctx->link]; cctx->ctx = ctx; @@ -606,12 +899,35 @@ av_cold int ff_decklink_write_header(AVFormatContext *avctx) } else if (c->codec_type == AVMEDIA_TYPE_VIDEO) { if (decklink_setup_video(avctx, st)) goto error; + } else if (c->codec_type == AVMEDIA_TYPE_DATA) { + if (decklink_setup_data(avctx, st)) + goto error; + } else if (c->codec_type == AVMEDIA_TYPE_SUBTITLE) { + if (decklink_setup_subtitle(avctx, st)) + goto error; } else { av_log(avctx, AV_LOG_ERROR, "Unsupported stream type.\n"); goto error; } } + /* Reconfigure the data/subtitle stream clocks to match the video */ + for (n = 0; n < avctx->nb_streams; n++) { + AVStream *st = avctx->streams[n]; + AVCodecParameters *c = st->codecpar; + + if(c->codec_type == AVMEDIA_TYPE_DATA || + c->codec_type == AVMEDIA_TYPE_SUBTITLE) + avpriv_set_pts_info(st, 64, ctx->bmd_tb_num, ctx->bmd_tb_den); + } + ff_decklink_packet_queue_init(avctx, &ctx->vanc_queue, cctx->vanc_queue_size); + + ret = ff_ccfifo_init(&ctx->cc_fifo, av_make_q(ctx->bmd_tb_den, ctx->bmd_tb_num), avctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); + goto error; + } + return 0; error: @@ -621,16 +937,16 @@ av_cold int ff_decklink_write_header(AVFormatContext *avctx) int ff_decklink_write_packet(AVFormatContext *avctx, AVPacket *pkt) { - struct decklink_cctx *cctx = (struct decklink_cctx *)avctx->priv_data; - struct decklink_ctx *ctx = (struct decklink_ctx *)cctx->ctx; AVStream *st = avctx->streams[pkt->stream_index]; - ctx->last_pts = FFMAX(ctx->last_pts, pkt->pts); - if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) return decklink_write_video_packet(avctx, pkt); else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) return decklink_write_audio_packet(avctx, pkt); + else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) + return decklink_write_data_packet(avctx, pkt); + else if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) + return decklink_write_subtitle_packet(avctx, pkt); return AVERROR(EIO); } diff --git a/libavdevice/decklink_enc_c.c b/libavdevice/decklink_enc_c.c index f7e315057fc..914e42e5ca7 100644 --- a/libavdevice/decklink_enc_c.c +++ b/libavdevice/decklink_enc_c.c @@ -32,35 +32,36 @@ static const AVOption options[] = { { "list_devices", "use ffmpeg -sinks decklink instead", OFFSET(list_devices), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ENC | AV_OPT_FLAG_DEPRECATED}, { "list_formats", "list supported formats" , OFFSET(list_formats), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, ENC }, { "preroll" , "video preroll in seconds", OFFSET(preroll ), AV_OPT_TYPE_DOUBLE, { .dbl = 0.5 }, 0, 5, ENC }, + { "vanc_queue_size", "VANC queue buffer size", OFFSET(vanc_queue_size), AV_OPT_TYPE_INT64, { .i64 = (1024 * 1024)}, 0, INT64_MAX, ENC }, #if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000 - { "duplex_mode" , "duplex mode" , OFFSET(duplex_mode ), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 5, ENC, "duplex_mode"}, + { "duplex_mode" , "duplex mode" , OFFSET(duplex_mode ), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 5, ENC, .unit = "duplex_mode"}, #else - { "duplex_mode" , "duplex mode" , OFFSET(duplex_mode ), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 2, ENC, "duplex_mode"}, + { "duplex_mode" , "duplex mode" , OFFSET(duplex_mode ), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 2, ENC, .unit = "duplex_mode"}, #endif - { "unset" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 0 }, 0, 0, ENC, "duplex_mode"}, - { "half" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 1 }, 0, 0, ENC, "duplex_mode"}, - { "full" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 2 }, 0, 0, ENC, "duplex_mode"}, + { "unset" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 0 }, 0, 0, ENC, .unit = "duplex_mode"}, + { "half" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 1 }, 0, 0, ENC, .unit = "duplex_mode"}, + { "full" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 2 }, 0, 0, ENC, .unit = "duplex_mode"}, #if BLACKMAGIC_DECKLINK_API_VERSION >= 0x0b000000 - { "one_sub_device_full", NULL ,0 , AV_OPT_TYPE_CONST , { .i64 = 2 }, 0, 0, ENC, "duplex_mode"}, - { "one_sub_device_half", NULL ,0 , AV_OPT_TYPE_CONST , { .i64 = 3 }, 0, 0, ENC, "duplex_mode"}, - { "two_sub_device_full", NULL ,0 , AV_OPT_TYPE_CONST , { .i64 = 4 }, 0, 0, ENC, "duplex_mode"}, - { "four_sub_device_half", NULL ,0 , AV_OPT_TYPE_CONST , { .i64 = 5 }, 0, 0, ENC, "duplex_mode"}, + { "one_sub_device_full", NULL ,0 , AV_OPT_TYPE_CONST , { .i64 = 2 }, 0, 0, ENC, .unit = "duplex_mode"}, + { "one_sub_device_half", NULL ,0 , AV_OPT_TYPE_CONST , { .i64 = 3 }, 0, 0, ENC, .unit = "duplex_mode"}, + { "two_sub_device_full", NULL ,0 , AV_OPT_TYPE_CONST , { .i64 = 4 }, 0, 0, ENC, .unit = "duplex_mode"}, + { "four_sub_device_half", NULL ,0 , AV_OPT_TYPE_CONST , { .i64 = 5 }, 0, 0, ENC, .unit = "duplex_mode"}, #endif - { "link" , "single/dual/quad SDI link configuration", OFFSET(link), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 3, ENC, "link"}, - { "unset" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 0 }, 0, 0, ENC, "link"}, - { "single" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 1 }, 0, 0, ENC, "link"}, - { "dual" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 2 }, 0, 0, ENC, "link"}, - { "quad" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 3 }, 0, 0, ENC, "link"}, - { "sqd" , "set Square Division" , OFFSET(sqd) , AV_OPT_TYPE_INT, { .i64 = -1 }, -1,1, ENC, "sqd"}, - { "unset" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = -1 }, 0, 0, ENC, "sqd"}, - { "false" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 0 }, 0, 0, ENC, "sqd"}, - { "true" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 1 }, 0, 0, ENC, "sqd"}, - { "level_a" , "set SMPTE LevelA" , OFFSET(level_a) , AV_OPT_TYPE_INT, { .i64 = -1 }, -1,1, ENC, "level_a"}, - { "unset" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = -1 }, 0, 0, ENC, "level_a"}, - { "false" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 0 }, 0, 0, ENC, "level_a"}, - { "true" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 1 }, 0, 0, ENC, "level_a"}, - { "timing_offset", "genlock timing pixel offset", OFFSET(timing_offset), AV_OPT_TYPE_INT, { .i64 = INT_MIN }, INT_MIN, INT_MAX, ENC, "timing_offset"}, - { "unset" , NULL , 0 , AV_OPT_TYPE_CONST, { .i64 = INT_MIN }, 0, 0, ENC, "timing_offset"}, + { "link" , "single/dual/quad SDI link configuration", OFFSET(link), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 3, ENC, .unit = "link"}, + { "unset" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 0 }, 0, 0, ENC, .unit = "link"}, + { "single" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 1 }, 0, 0, ENC, .unit = "link"}, + { "dual" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 2 }, 0, 0, ENC, .unit = "link"}, + { "quad" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 3 }, 0, 0, ENC, .unit = "link"}, + { "sqd" , "set Square Division" , OFFSET(sqd) , AV_OPT_TYPE_INT, { .i64 = -1 }, -1,1, ENC, .unit = "sqd"}, + { "unset" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = -1 }, 0, 0, ENC, .unit = "sqd"}, + { "false" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 0 }, 0, 0, ENC, .unit = "sqd"}, + { "true" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 1 }, 0, 0, ENC, .unit = "sqd"}, + { "level_a" , "set SMPTE LevelA" , OFFSET(level_a) , AV_OPT_TYPE_INT, { .i64 = -1 }, -1,1, ENC, .unit = "level_a"}, + { "unset" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = -1 }, 0, 0, ENC, .unit = "level_a"}, + { "false" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 0 }, 0, 0, ENC, .unit = "level_a"}, + { "true" , NULL , 0 , AV_OPT_TYPE_CONST , { .i64 = 1 }, 0, 0, ENC, .unit = "level_a"}, + { "timing_offset", "genlock timing pixel offset", OFFSET(timing_offset), AV_OPT_TYPE_INT, { .i64 = INT_MIN }, INT_MIN, INT_MAX, ENC, .unit = "timing_offset"}, + { "unset" , NULL , 0 , AV_OPT_TYPE_CONST, { .i64 = INT_MIN }, 0, 0, ENC, .unit = "timing_offset"}, { NULL }, }; @@ -77,7 +78,7 @@ const FFOutputFormat ff_decklink_muxer = { .p.long_name = NULL_IF_CONFIG_SMALL("Blackmagic DeckLink output"), .p.audio_codec = AV_CODEC_ID_PCM_S16LE, .p.video_codec = AV_CODEC_ID_WRAPPED_AVFRAME, - .p.subtitle_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_EIA_608, .p.flags = AVFMT_NOFILE, .p.priv_class = &decklink_muxer_class, .get_device_list = ff_decklink_list_output_devices, diff --git a/libavdevice/dshow.c b/libavdevice/dshow.c index 5946a72cc26..57d8e1c0afe 100644 --- a/libavdevice/dshow.c +++ b/libavdevice/dshow.c @@ -24,6 +24,7 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "libavutil/mem.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "libavformat/riff.h" #include "avdevice.h" @@ -431,8 +432,8 @@ dshow_get_device_media_types(AVFormatContext *avctx, enum dshowDeviceType devtyp IEnumMediaTypes_Release(types); if (p) IKsPropertySet_Release(p); - if (pin) - IPin_Release(pin); + + IPin_Release(pin); } IEnumPins_Release(pins); @@ -644,7 +645,7 @@ static int dshow_get_device_list(AVFormatContext *avctx, AVDeviceInfoList *devic } ret = dshow_cycle_devices(avctx, devenum, VideoDevice, VideoSourceDevice, NULL, NULL, &device_list); - if (ret < S_OK) + if (ret < S_OK && ret != AVERROR(EIO)) goto error; ret = dshow_cycle_devices(avctx, devenum, AudioDevice, AudioSourceDevice, NULL, NULL, &device_list); @@ -1000,7 +1001,7 @@ dshow_cycle_formats(AVFormatContext *avctx, enum dshowDeviceType devtype, " ch=%2u, bits=%2u, rate=%6lu\n", fx->nChannels, fx->wBitsPerSample, fx->nSamplesPerSec ); - continue; + goto next; } if ( (requested_sample_rate && requested_sample_rate != fx->nSamplesPerSec) || @@ -1369,10 +1370,10 @@ dshow_open_device(AVFormatContext *avctx, ICreateDevEnum *devenum, goto error; } } - if (ctx->device_filter[otherDevType]) { + if (ctx->device_filter[otherDevType]) { // avoid adding add two instances of the same device to the graph, one for video, one for audio // a few devices don't support this (could also do this check earlier to avoid double crossbars, etc. but they seem OK) - if (strcmp(device_filter_unique_name, ctx->device_unique_name[otherDevType]) == 0) { + if (!device_filter_unique_name || strcmp(device_filter_unique_name, ctx->device_unique_name[otherDevType]) == 0) { av_log(avctx, AV_LOG_DEBUG, "reusing previous graph capture filter... %s\n", device_filter_unique_name); IBaseFilter_Release(device_filter); device_filter = ctx->device_filter[otherDevType]; @@ -1464,7 +1465,7 @@ dshow_open_device(AVFormatContext *avctx, ICreateDevEnum *devenum, av_log(avctx, AV_LOG_ERROR, "Could not create CaptureGraphBuilder2\n"); goto error; } - ICaptureGraphBuilder2_SetFiltergraph(graph_builder2, graph); + r = ICaptureGraphBuilder2_SetFiltergraph(graph_builder2, graph); if (r != S_OK) { av_log(avctx, AV_LOG_ERROR, "Could not set graph for CaptureGraphBuilder2\n"); goto error; @@ -1545,7 +1546,10 @@ dshow_add_device(AVFormatContext *avctx, ctx->capture_filter[devtype]->stream_index = st->index; - ff_dshow_pin_ConnectionMediaType(ctx->capture_pin[devtype], &type); + if (ff_dshow_pin_ConnectionMediaType(ctx->capture_pin[devtype], &type) != S_OK) { + ret = AVERROR(EIO); + goto error; + } fmt_info = dshow_get_format_info(&type); if (!fmt_info) { ret = AVERROR(EIO); @@ -1924,14 +1928,15 @@ static const AVClass dshow_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, }; -const AVInputFormat ff_dshow_demuxer = { - .name = "dshow", - .long_name = NULL_IF_CONFIG_SMALL("DirectShow capture"), +const FFInputFormat ff_dshow_demuxer = { + .p.name = "dshow", + .p.long_name = NULL_IF_CONFIG_SMALL("DirectShow capture"), + .p.flags = AVFMT_NOFILE | AVFMT_NOBINSEARCH | + AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK, + .p.priv_class = &dshow_class, .priv_data_size = sizeof(struct dshow_ctx), .read_header = dshow_read_header, .read_packet = dshow_read_packet, .read_close = dshow_read_close, .get_device_list= dshow_get_device_list, - .flags = AVFMT_NOFILE | AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK, - .priv_class = &dshow_class, }; diff --git a/libavdevice/dshow_capture.h b/libavdevice/dshow_capture.h index b548cd7afc8..bb39d4947aa 100644 --- a/libavdevice/dshow_capture.h +++ b/libavdevice/dshow_capture.h @@ -27,7 +27,6 @@ #include "avdevice.h" #define COBJMACROS -#define WIN32_LEAN_AND_MEAN #include #define NO_DSHOW_STRSAFE #include @@ -125,14 +124,15 @@ void ff_dshow_##prefix##_Destroy(class *this) \ class *ff_dshow_##prefix##_Create(__VA_ARGS__) \ { \ class *this = CoTaskMemAlloc(sizeof(class)); \ - void *vtbl = CoTaskMemAlloc(sizeof(*this->vtbl)); \ dshowdebug("ff_dshow_"AV_STRINGIFY(prefix)"_Create(%p)\n", this); \ - if (!this || !vtbl) \ + if (!this) \ goto fail; \ ZeroMemory(this, sizeof(class)); \ - ZeroMemory(vtbl, sizeof(*this->vtbl)); \ + this->vtbl = CoTaskMemAlloc(sizeof(*this->vtbl)); \ + if (!this->vtbl) \ + goto fail; \ + ZeroMemory(this->vtbl, sizeof(*this->vtbl)); \ this->ref = 1; \ - this->vtbl = vtbl; \ if (!setup) \ goto fail; \ dshowdebug("created ff_dshow_"AV_STRINGIFY(prefix)" %p\n", this); \ diff --git a/libavdevice/dshow_filter.c b/libavdevice/dshow_filter.c index 4642ac077c5..2122c846262 100644 --- a/libavdevice/dshow_filter.c +++ b/libavdevice/dshow_filter.c @@ -135,7 +135,7 @@ long WINAPI ff_dshow_filter_JoinFilterGraph(DShowFilter *this, IFilterGraph *gra this->info.pGraph = graph; if (name) - wcscpy(this->info.achName, name); + wcscpy_s(this->info.achName, sizeof(this->info.achName) / sizeof(wchar_t), name); return S_OK; } diff --git a/libavdevice/fbdev_common.c b/libavdevice/fbdev_common.c index 47e7edde5cf..2c70abb1f0e 100644 --- a/libavdevice/fbdev_common.c +++ b/libavdevice/fbdev_common.c @@ -62,7 +62,7 @@ enum AVPixelFormat ff_get_pixfmt_from_fb_varinfo(struct fb_var_screeninfo *varin return AV_PIX_FMT_NONE; } -const char* ff_fbdev_default_device() +const char *ff_fbdev_default_device(void) { const char *dev = getenv("FRAMEBUFFER"); if (!dev) diff --git a/libavdevice/fbdev_dec.c b/libavdevice/fbdev_dec.c index 460a71d13ff..51e7bd73ab5 100644 --- a/libavdevice/fbdev_dec.c +++ b/libavdevice/fbdev_dec.c @@ -41,6 +41,7 @@ #include "libavutil/time.h" #include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "avdevice.h" #include "fbdev_common.h" @@ -232,14 +233,14 @@ static const AVClass fbdev_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, }; -const AVInputFormat ff_fbdev_demuxer = { - .name = "fbdev", - .long_name = NULL_IF_CONFIG_SMALL("Linux framebuffer"), +const FFInputFormat ff_fbdev_demuxer = { + .p.name = "fbdev", + .p.long_name = NULL_IF_CONFIG_SMALL("Linux framebuffer"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &fbdev_class, .priv_data_size = sizeof(FBDevContext), .read_header = fbdev_read_header, .read_packet = fbdev_read_packet, .read_close = fbdev_read_close, .get_device_list = fbdev_get_device_list, - .flags = AVFMT_NOFILE, - .priv_class = &fbdev_class, }; diff --git a/libavdevice/gdigrab.c b/libavdevice/gdigrab.c index c0692324725..58e876ea9ec 100644 --- a/libavdevice/gdigrab.c +++ b/libavdevice/gdigrab.c @@ -29,6 +29,7 @@ */ #include "config.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "libavutil/opt.h" #include "libavutil/time.h" @@ -273,9 +274,22 @@ gdigrab_read_header(AVFormatContext *s1) } } else if (!strcmp(filename, "desktop")) { hwnd = NULL; + } else if (!strncmp(filename, "hwnd=", 5)) { + char *p; + name = filename + 5; + + hwnd = (HWND) strtoull(name, &p, 0); + + if (p == NULL || p == name || p[0] == '\0') + { + av_log(s1, AV_LOG_ERROR, + "Invalid window handle '%s', must be a valid integer.\n", name); + ret = AVERROR(EINVAL); + goto error; + } } else { av_log(s1, AV_LOG_ERROR, - "Please use \"desktop\" or \"title=\" to specify your target.\n"); + "Please use \"desktop\", \"title=\" or \"hwnd=\" to specify your target.\n"); ret = AVERROR(EIO); goto error; } @@ -664,13 +678,13 @@ static const AVClass gdigrab_class = { }; /** gdi grabber device demuxer declaration */ -const AVInputFormat ff_gdigrab_demuxer = { - .name = "gdigrab", - .long_name = NULL_IF_CONFIG_SMALL("GDI API Windows frame grabber"), +const FFInputFormat ff_gdigrab_demuxer = { + .p.name = "gdigrab", + .p.long_name = NULL_IF_CONFIG_SMALL("GDI API Windows frame grabber"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &gdigrab_class, .priv_data_size = sizeof(struct gdigrab), .read_header = gdigrab_read_header, .read_packet = gdigrab_read_packet, .read_close = gdigrab_read_close, - .flags = AVFMT_NOFILE, - .priv_class = &gdigrab_class, }; diff --git a/libavdevice/iec61883.c b/libavdevice/iec61883.c index 7223ba2e646..689bcc81d58 100644 --- a/libavdevice/iec61883.c +++ b/libavdevice/iec61883.c @@ -30,6 +30,7 @@ #include #include #include +#include "libavformat/demux.h" #include "libavformat/dv.h" #include "libavformat/mpegts.h" #include "libavutil/opt.h" @@ -485,10 +486,10 @@ static int iec61883_close(AVFormatContext *context) } static const AVOption options[] = { - { "dvtype", "override autodetection of DV/HDV", offsetof(struct iec61883_data, type), AV_OPT_TYPE_INT, {.i64 = IEC61883_AUTO}, IEC61883_AUTO, IEC61883_HDV, AV_OPT_FLAG_DECODING_PARAM, "dvtype" }, - { "auto", "auto detect DV/HDV", 0, AV_OPT_TYPE_CONST, {.i64 = IEC61883_AUTO}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "dvtype" }, - { "dv", "force device being treated as DV device", 0, AV_OPT_TYPE_CONST, {.i64 = IEC61883_DV}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "dvtype" }, - { "hdv" , "force device being treated as HDV device", 0, AV_OPT_TYPE_CONST, {.i64 = IEC61883_HDV}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "dvtype" }, + { "dvtype", "override autodetection of DV/HDV", offsetof(struct iec61883_data, type), AV_OPT_TYPE_INT, {.i64 = IEC61883_AUTO}, IEC61883_AUTO, IEC61883_HDV, AV_OPT_FLAG_DECODING_PARAM, .unit = "dvtype" }, + { "auto", "auto detect DV/HDV", 0, AV_OPT_TYPE_CONST, {.i64 = IEC61883_AUTO}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, .unit = "dvtype" }, + { "dv", "force device being treated as DV device", 0, AV_OPT_TYPE_CONST, {.i64 = IEC61883_DV}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, .unit = "dvtype" }, + { "hdv" , "force device being treated as HDV device", 0, AV_OPT_TYPE_CONST, {.i64 = IEC61883_HDV}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, .unit = "dvtype" }, { "dvbuffer", "set queue buffer size (in packets)", offsetof(struct iec61883_data, max_packets), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, { "dvguid", "select one of multiple DV devices by its GUID", offsetof(struct iec61883_data, device_guid), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, { NULL }, @@ -502,13 +503,13 @@ static const AVClass iec61883_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, }; -const AVInputFormat ff_iec61883_demuxer = { - .name = "iec61883", - .long_name = NULL_IF_CONFIG_SMALL("libiec61883 (new DV1394) A/V input device"), +const FFInputFormat ff_iec61883_demuxer = { + .p.name = "iec61883", + .p.long_name = NULL_IF_CONFIG_SMALL("libiec61883 (new DV1394) A/V input device"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &iec61883_class, .priv_data_size = sizeof(struct iec61883_data), .read_header = iec61883_read_header, .read_packet = iec61883_read_packet, .read_close = iec61883_close, - .flags = AVFMT_NOFILE, - .priv_class = &iec61883_class, }; diff --git a/libavdevice/jack.c b/libavdevice/jack.c index db056d824fb..40f2af5cccd 100644 --- a/libavdevice/jack.c +++ b/libavdevice/jack.c @@ -30,6 +30,7 @@ #include "libavutil/opt.h" #include "libavutil/time.h" #include "libavformat/avformat.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "timefilter.h" #include "avdevice.h" @@ -341,13 +342,13 @@ static const AVClass jack_indev_class = { .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT, }; -const AVInputFormat ff_jack_demuxer = { - .name = "jack", - .long_name = NULL_IF_CONFIG_SMALL("JACK Audio Connection Kit"), +const FFInputFormat ff_jack_demuxer = { + .p.name = "jack", + .p.long_name = NULL_IF_CONFIG_SMALL("JACK Audio Connection Kit"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &jack_indev_class, .priv_data_size = sizeof(JackData), .read_header = audio_read_header, .read_packet = audio_read_packet, .read_close = audio_read_close, - .flags = AVFMT_NOFILE, - .priv_class = &jack_indev_class, }; diff --git a/libavdevice/kmsgrab.c b/libavdevice/kmsgrab.c index ba9b306c652..bf6341e9fd9 100644 --- a/libavdevice/kmsgrab.c +++ b/libavdevice/kmsgrab.c @@ -42,6 +42,7 @@ #include "libavutil/time.h" #include "libavformat/avformat.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" typedef struct KMSGrabContext { @@ -708,13 +709,13 @@ static const AVClass kmsgrab_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, }; -const AVInputFormat ff_kmsgrab_demuxer = { - .name = "kmsgrab", - .long_name = NULL_IF_CONFIG_SMALL("KMS screen capture"), +const FFInputFormat ff_kmsgrab_demuxer = { + .p.name = "kmsgrab", + .p.long_name = NULL_IF_CONFIG_SMALL("KMS screen capture"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &kmsgrab_class, .priv_data_size = sizeof(KMSGrabContext), .read_header = &kmsgrab_read_header, .read_packet = &kmsgrab_read_packet, .read_close = &kmsgrab_read_close, - .flags = AVFMT_NOFILE, - .priv_class = &kmsgrab_class, }; diff --git a/libavdevice/lavfi.c b/libavdevice/lavfi.c index 246f7dff3b3..ce10d61f8ad 100644 --- a/libavdevice/lavfi.c +++ b/libavdevice/lavfi.c @@ -39,7 +39,7 @@ #include "libavutil/pixdesc.h" #include "libavfilter/avfilter.h" #include "libavfilter/buffersink.h" -#include "libavformat/avio_internal.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "avdevice.h" @@ -174,6 +174,10 @@ av_cold static int lavfi_read_header(AVFormatContext *avctx) * create a mapping between them and the streams */ for (i = 0, inout = output_links; inout; i++, inout = inout->next) { int stream_idx = 0, suffix = 0, use_subcc = 0; + if (!inout->name) { + av_log(avctx, AV_LOG_ERROR, "Missing %d outpad name\n", i); + FAIL(AVERROR(EINVAL)); + } sscanf(inout->name, "out%n%d%n", &suffix, &stream_idx, &suffix); if (!suffix) { av_log(avctx, AV_LOG_ERROR, @@ -343,7 +347,11 @@ static int create_subcc_packet(AVFormatContext *avctx, AVFrame *frame, memcpy(lavfi->subcc_packet.data, sd->data, sd->size); lavfi->subcc_packet.stream_index = stream_idx; lavfi->subcc_packet.pts = frame->pts; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS lavfi->subcc_packet.pos = frame->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif return 0; } @@ -358,7 +366,7 @@ static int lavfi_read_packet(AVFormatContext *avctx, AVPacket *pkt) LavfiContext *lavfi = avctx->priv_data; double min_pts = DBL_MAX; int stream_idx, min_pts_sink_idx = 0; - AVFrame *frame; + AVFrame *frame, *frame_to_free; AVDictionary *frame_metadata; int ret, i; AVStream *st; @@ -371,6 +379,7 @@ static int lavfi_read_packet(AVFormatContext *avctx, AVPacket *pkt) frame = av_frame_alloc(); if (!frame) return AVERROR(ENOMEM); + frame_to_free = frame; /* iterate through all the graph sinks. Select the sink with the * minimum PTS */ @@ -416,6 +425,7 @@ static int lavfi_read_packet(AVFormatContext *avctx, AVPacket *pkt) ret = AVERROR(ENOMEM); goto fail; } + frame_to_free = NULL; pkt->data = pkt->buf->data; pkt->size = pkt->buf->size; @@ -450,14 +460,17 @@ static int lavfi_read_packet(AVFormatContext *avctx, AVPacket *pkt) pkt->stream_index = stream_idx; pkt->pts = frame->pts; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS pkt->pos = frame->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif - if (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) - av_frame_free(&frame); + av_frame_free(&frame_to_free); return pkt->size; fail: - av_frame_free(&frame); + av_frame_free(&frame_to_free); return ret; } @@ -481,14 +494,14 @@ static const AVClass lavfi_class = { .category = AV_CLASS_CATEGORY_DEVICE_INPUT, }; -const AVInputFormat ff_lavfi_demuxer = { - .name = "lavfi", - .long_name = NULL_IF_CONFIG_SMALL("Libavfilter virtual input device"), +const FFInputFormat ff_lavfi_demuxer = { + .p.name = "lavfi", + .p.long_name = NULL_IF_CONFIG_SMALL("Libavfilter virtual input device"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &lavfi_class, .priv_data_size = sizeof(LavfiContext), .read_header = lavfi_read_header, .read_packet = lavfi_read_packet, .read_close = lavfi_read_close, - .flags = AVFMT_NOFILE, - .priv_class = &lavfi_class, - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, }; diff --git a/libavdevice/libcdio.c b/libavdevice/libcdio.c index 28c339564f9..5e97c25b893 100644 --- a/libavdevice/libcdio.c +++ b/libavdevice/libcdio.c @@ -163,12 +163,12 @@ static int read_seek(AVFormatContext *ctx, int stream_index, int64_t timestamp, #define DEC AV_OPT_FLAG_DECODING_PARAM static const AVOption options[] = { { "speed", "set drive reading speed", OFFSET(speed), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, DEC }, - { "paranoia_mode", "set error recovery mode", OFFSET(paranoia_mode), AV_OPT_TYPE_FLAGS, { .i64 = PARANOIA_MODE_DISABLE }, INT_MIN, INT_MAX, DEC, "paranoia_mode" }, - { "disable", "apply no fixups", 0, AV_OPT_TYPE_CONST, { .i64 = PARANOIA_MODE_DISABLE }, 0, 0, DEC, "paranoia_mode" }, - { "verify", "verify data integrity in overlap area", 0, AV_OPT_TYPE_CONST, { .i64 = PARANOIA_MODE_VERIFY }, 0, 0, DEC, "paranoia_mode" }, - { "overlap", "perform overlapped reads", 0, AV_OPT_TYPE_CONST, { .i64 = PARANOIA_MODE_OVERLAP }, 0, 0, DEC, "paranoia_mode" }, - { "neverskip", "do not skip failed reads", 0, AV_OPT_TYPE_CONST, { .i64 = PARANOIA_MODE_NEVERSKIP }, 0, 0, DEC, "paranoia_mode" }, - { "full", "apply all recovery modes", 0, AV_OPT_TYPE_CONST, { .i64 = PARANOIA_MODE_FULL }, 0, 0, DEC, "paranoia_mode" }, + { "paranoia_mode", "set error recovery mode", OFFSET(paranoia_mode), AV_OPT_TYPE_FLAGS, { .i64 = PARANOIA_MODE_DISABLE }, INT_MIN, INT_MAX, DEC, .unit = "paranoia_mode" }, + { "disable", "apply no fixups", 0, AV_OPT_TYPE_CONST, { .i64 = PARANOIA_MODE_DISABLE }, 0, 0, DEC, .unit = "paranoia_mode" }, + { "verify", "verify data integrity in overlap area", 0, AV_OPT_TYPE_CONST, { .i64 = PARANOIA_MODE_VERIFY }, 0, 0, DEC, .unit = "paranoia_mode" }, + { "overlap", "perform overlapped reads", 0, AV_OPT_TYPE_CONST, { .i64 = PARANOIA_MODE_OVERLAP }, 0, 0, DEC, .unit = "paranoia_mode" }, + { "neverskip", "do not skip failed reads", 0, AV_OPT_TYPE_CONST, { .i64 = PARANOIA_MODE_NEVERSKIP }, 0, 0, DEC, .unit = "paranoia_mode" }, + { "full", "apply all recovery modes", 0, AV_OPT_TYPE_CONST, { .i64 = PARANOIA_MODE_FULL }, 0, 0, DEC, .unit = "paranoia_mode" }, { NULL }, }; @@ -180,13 +180,13 @@ static const AVClass libcdio_class = { .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT, }; -const AVInputFormat ff_libcdio_demuxer = { - .name = "libcdio", +const FFInputFormat ff_libcdio_demuxer = { + .p.name = "libcdio", + .p.flags = AVFMT_NOFILE, + .p.priv_class = &libcdio_class, .read_header = read_header, .read_packet = read_packet, .read_close = read_close, .read_seek = read_seek, .priv_data_size = sizeof(CDIOContext), - .flags = AVFMT_NOFILE, - .priv_class = &libcdio_class, }; diff --git a/libavdevice/libdc1394.c b/libavdevice/libdc1394.c index e98b88c1a2f..c73d07a60f6 100644 --- a/libavdevice/libdc1394.c +++ b/libavdevice/libdc1394.c @@ -31,6 +31,7 @@ #include "libavutil/pixdesc.h" #include "libavformat/avformat.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" typedef struct dc1394_data { @@ -288,13 +289,13 @@ static int dc1394_close(AVFormatContext * context) return 0; } -const AVInputFormat ff_libdc1394_demuxer = { - .name = "libdc1394", - .long_name = NULL_IF_CONFIG_SMALL("dc1394 v.2 A/V grab"), +const FFInputFormat ff_libdc1394_demuxer = { + .p.name = "libdc1394", + .p.long_name = NULL_IF_CONFIG_SMALL("dc1394 v.2 A/V grab"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &libdc1394_class, .priv_data_size = sizeof(struct dc1394_data), .read_header = dc1394_read_header, .read_packet = dc1394_read_packet, .read_close = dc1394_close, - .flags = AVFMT_NOFILE, - .priv_class = &libdc1394_class, }; diff --git a/libavdevice/openal-dec.c b/libavdevice/openal-dec.c index 91a40ae0202..7aa165ee74e 100644 --- a/libavdevice/openal-dec.c +++ b/libavdevice/openal-dec.c @@ -26,6 +26,7 @@ #include "libavutil/opt.h" #include "libavutil/time.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "avdevice.h" @@ -234,9 +235,9 @@ static const AVOption options[] = { {"channels", "set number of channels", OFFSET(channels), AV_OPT_TYPE_INT, {.i64=2}, 1, 2, AV_OPT_FLAG_DECODING_PARAM }, {"sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, 192000, AV_OPT_FLAG_DECODING_PARAM }, {"sample_size", "set sample size", OFFSET(sample_size), AV_OPT_TYPE_INT, {.i64=16}, 8, 16, AV_OPT_FLAG_DECODING_PARAM }, - {"list_devices", "list available devices", OFFSET(list_devices), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, "list_devices" }, - {"true", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "list_devices" }, - {"false", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "list_devices" }, + {"list_devices", "list available devices", OFFSET(list_devices), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, .unit = "list_devices" }, + {"true", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, .unit = "list_devices" }, + {"false", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, .unit = "list_devices" }, {NULL}, }; @@ -248,14 +249,14 @@ static const AVClass class = { .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT, }; -const AVInputFormat ff_openal_demuxer = { - .name = "openal", - .long_name = NULL_IF_CONFIG_SMALL("OpenAL audio capture device"), +const FFInputFormat ff_openal_demuxer = { + .p.name = "openal", + .p.long_name = NULL_IF_CONFIG_SMALL("OpenAL audio capture device"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &class, .priv_data_size = sizeof(al_data), .read_probe = NULL, .read_header = read_header, .read_packet = read_packet, .read_close = read_close, - .flags = AVFMT_NOFILE, - .priv_class = &class }; diff --git a/libavdevice/opengl_enc.c b/libavdevice/opengl_enc.c index 06750bbba1e..69de6fad031 100644 --- a/libavdevice/opengl_enc.c +++ b/libavdevice/opengl_enc.c @@ -30,7 +30,6 @@ #include "config.h" #if HAVE_WINDOWS_H -#define WIN32_LEAN_AND_MEAN #include #endif #if HAVE_OPENGL_GL3_H @@ -50,6 +49,7 @@ #endif #include "libavutil/common.h" +#include "libavutil/frame.h" #include "libavutil/pixdesc.h" #include "libavutil/log.h" #include "libavutil/opt.h" @@ -152,7 +152,7 @@ typedef struct FFOpenGLFunctions { {\ GLenum err_code; \ if ((err_code = glGetError()) != GL_NO_ERROR) { \ - av_log(ctx, AV_LOG_ERROR, "OpenGL error occurred in '%s', line %d: %d\n", __FUNCTION__, __LINE__, err_code); \ + av_log(ctx, AV_LOG_ERROR, "OpenGL error occurred in '%s', line %d: %d\n", __func__, __LINE__, err_code); \ goto fail; \ } \ }\ @@ -224,6 +224,8 @@ typedef struct OpenGLContext { int picture_height; ///< Rendered height int window_width; int window_height; + + int warned; } OpenGLContext; static const struct OpenGLFormatDesc { @@ -594,7 +596,8 @@ static av_cold int opengl_read_limits(AVFormatContext *h) } av_log(h, AV_LOG_DEBUG, "OpenGL version: %s\n", version); - sscanf(version, "%d.%d", &major, &minor); + if (sscanf(version, "%d.%d", &major, &minor) != 2) + return AVERROR(ENOSYS); for (i = 0; required_extensions[i].extension; i++) { if (major < required_extensions[i].major && @@ -1059,6 +1062,15 @@ static av_cold int opengl_write_header(AVFormatContext *h) AVStream *st; int ret; + if (!opengl->warned) { + av_log(opengl, AV_LOG_WARNING, + "The opengl output device is deprecated due to being fundamentally incompatible with libavformat API. " + "For monitoring purposes in ffmpeg you can output to a file or use pipes and a video player.\n" + "Example: ffmpeg -i INPUT -f nut -c:v rawvideo - | ffplay -\n" + ); + opengl->warned = 1; + } + if (h->nb_streams != 1 || par->codec_type != AVMEDIA_TYPE_VIDEO || (par->codec_id != AV_CODEC_ID_WRAPPED_AVFRAME && par->codec_id != AV_CODEC_ID_RAWVIDEO)) { @@ -1200,6 +1212,10 @@ static int opengl_draw(AVFormatContext *h, void *input, int repaint, int is_pkt) int ret; #if CONFIG_SDL2 + /* At this point, opengl->glcontext implies opengl->glcontext */ + if (opengl->glcontext) + SDL_GL_MakeCurrent(opengl->window, opengl->glcontext); + if (!opengl->no_window && (ret = opengl_sdl_process_events(h)) < 0) goto fail; #endif diff --git a/libavdevice/oss_dec.c b/libavdevice/oss_dec.c index 2cdc4324e86..f727d6e1100 100644 --- a/libavdevice/oss_dec.c +++ b/libavdevice/oss_dec.c @@ -35,6 +35,7 @@ #include "libavutil/time.h" #include "avdevice.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "oss.h" @@ -130,13 +131,13 @@ static const AVClass oss_demuxer_class = { .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT, }; -const AVInputFormat ff_oss_demuxer = { - .name = "oss", - .long_name = NULL_IF_CONFIG_SMALL("OSS (Open Sound System) capture"), +const FFInputFormat ff_oss_demuxer = { + .p.name = "oss", + .p.long_name = NULL_IF_CONFIG_SMALL("OSS (Open Sound System) capture"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &oss_demuxer_class, .priv_data_size = sizeof(OSSAudioData), .read_header = audio_read_header, .read_packet = audio_read_packet, .read_close = audio_read_close, - .flags = AVFMT_NOFILE, - .priv_class = &oss_demuxer_class, }; diff --git a/libavdevice/pulse_audio_common.h b/libavdevice/pulse_audio_common.h index 902795e4f76..9def3cb7968 100644 --- a/libavdevice/pulse_audio_common.h +++ b/libavdevice/pulse_audio_common.h @@ -23,7 +23,7 @@ #define AVDEVICE_PULSE_AUDIO_COMMON_H #include -#include "libavcodec/avcodec.h" +#include "libavcodec/codec_id.h" #include "avdevice.h" pa_sample_format_t ff_codec_id_to_pulse_format(enum AVCodecID codec_id); diff --git a/libavdevice/pulse_audio_dec.c b/libavdevice/pulse_audio_dec.c index 2545462939f..32be18e7dde 100644 --- a/libavdevice/pulse_audio_dec.c +++ b/libavdevice/pulse_audio_dec.c @@ -29,6 +29,7 @@ #include "libavutil/time.h" #include "libavformat/avformat.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "libavformat/version.h" #include "pulse_audio_common.h" @@ -393,14 +394,14 @@ static const AVClass pulse_demuxer_class = { .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT, }; -const AVInputFormat ff_pulse_demuxer = { - .name = "pulse", - .long_name = NULL_IF_CONFIG_SMALL("Pulse audio input"), +const FFInputFormat ff_pulse_demuxer = { + .p.name = "pulse", + .p.long_name = NULL_IF_CONFIG_SMALL("Pulse audio input"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &pulse_demuxer_class, .priv_data_size = sizeof(PulseData), .read_header = pulse_read_header, .read_packet = pulse_read_packet, .read_close = pulse_close, .get_device_list = pulse_get_device_list, - .flags = AVFMT_NOFILE, - .priv_class = &pulse_demuxer_class, }; diff --git a/libavdevice/pulse_audio_enc.c b/libavdevice/pulse_audio_enc.c index 3d8323233f7..80136d1e20c 100644 --- a/libavdevice/pulse_audio_enc.c +++ b/libavdevice/pulse_audio_enc.c @@ -26,6 +26,7 @@ #include "libavformat/mux.h" #include "libavformat/version.h" #include "libavutil/channel_layout.h" +#include "libavutil/frame.h" #include "libavutil/internal.h" #include "libavutil/opt.h" #include "libavutil/time.h" @@ -470,10 +471,11 @@ static av_cold int pulse_write_header(AVFormatContext *h) s->nonblocking = (h->flags & AVFMT_FLAG_NONBLOCK); if (s->buffer_duration) { - int64_t bytes = s->buffer_duration; - bytes *= st->codecpar->ch_layout.nb_channels * st->codecpar->sample_rate * - av_get_bytes_per_sample(st->codecpar->format); - bytes /= 1000; + int64_t bytes = av_rescale(s->buffer_duration, + st->codecpar->ch_layout.nb_channels * + (int64_t)st->codecpar->sample_rate * + av_get_bytes_per_sample(st->codecpar->format), + 1000); buffer_attributes.tlength = FFMAX(s->buffer_size, av_clip64(bytes, 0, UINT32_MAX - 1)); av_log(s, AV_LOG_DEBUG, "Buffer duration: %ums recalculated into %"PRId64" bytes buffer.\n", @@ -503,7 +505,7 @@ static av_cold int pulse_write_header(AVFormatContext *h) pulse_map_channels_to_pulse(&st->codecpar->ch_layout, &channel_map); /* Unknown channel is present in channel_layout, let PulseAudio use its default. */ if (channel_map.channels != sample_spec.channels) { - av_log(s, AV_LOG_WARNING, "Unknown channel. Using defaul channel map.\n"); + av_log(s, AV_LOG_WARNING, "Unknown channel. Using default channel map.\n"); channel_map.channels = 0; } } else @@ -686,13 +688,6 @@ static int pulse_write_frame(AVFormatContext *h, int stream_index, pkt.data = (*frame)->data[0]; pkt.size = (*frame)->nb_samples * av_get_bytes_per_sample((*frame)->format) * (*frame)->ch_layout.nb_channels; pkt.dts = (*frame)->pkt_dts; -#if FF_API_PKT_DURATION -FF_DISABLE_DEPRECATION_WARNINGS - if ((*frame)->pkt_duration) - pkt.duration = (*frame)->pkt_duration; - else -FF_ENABLE_DEPRECATION_WARNINGS -#endif pkt.duration = (*frame)->duration; return pulse_write_packet(h, &pkt); } @@ -801,6 +796,11 @@ const FFOutputFormat ff_pulse_muxer = { .get_output_timestamp = pulse_get_output_timestamp, .get_device_list = pulse_get_device_list, .control_message = pulse_control_message, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, +#else + .p.flags = AVFMT_NOFILE, +#endif .p.priv_class = &pulse_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; diff --git a/libavdevice/sdl2.c b/libavdevice/sdl2.c index 342a253dc09..ec3c3d19b5d 100644 --- a/libavdevice/sdl2.c +++ b/libavdevice/sdl2.c @@ -51,6 +51,7 @@ typedef struct { SDL_Rect texture_rect; int inited; + int warned; } SDLContext; static const struct sdl_texture_format_entry { @@ -165,6 +166,15 @@ static int sdl2_write_header(AVFormatContext *s) int i, ret = 0; int flags = 0; + if (!sdl->warned) { + av_log(sdl, AV_LOG_WARNING, + "The sdl output device is deprecated due to being fundamentally incompatible with libavformat API. " + "For monitoring purposes in ffmpeg you can output to a file or use pipes and a video player.\n" + "Example: ffmpeg -i INPUT -f nut -c:v rawvideo - | ffplay -\n" + ); + sdl->warned = 1; + } + if (!sdl->window_title) sdl->window_title = av_strdup(s->url); diff --git a/libavdevice/sndio_dec.c b/libavdevice/sndio_dec.c index 6059830367c..309394189b9 100644 --- a/libavdevice/sndio_dec.c +++ b/libavdevice/sndio_dec.c @@ -27,6 +27,7 @@ #include "libavutil/time.h" #include "libavformat/avformat.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" #include "libavdevice/sndio.h" @@ -109,13 +110,13 @@ static const AVClass sndio_demuxer_class = { .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT, }; -const AVInputFormat ff_sndio_demuxer = { - .name = "sndio", - .long_name = NULL_IF_CONFIG_SMALL("sndio audio capture"), +const FFInputFormat ff_sndio_demuxer = { + .p.name = "sndio", + .p.long_name = NULL_IF_CONFIG_SMALL("sndio audio capture"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &sndio_demuxer_class, .priv_data_size = sizeof(SndioData), .read_header = audio_read_header, .read_packet = audio_read_packet, .read_close = audio_read_close, - .flags = AVFMT_NOFILE, - .priv_class = &sndio_demuxer_class, }; diff --git a/libavdevice/utils.c b/libavdevice/utils.c index d9a52c53ab8..c72a839dfa8 100644 --- a/libavdevice/utils.c +++ b/libavdevice/utils.c @@ -19,6 +19,7 @@ #include "internal.h" #include "libavutil/opt.h" #include "libavformat/avformat.h" +#include "libavformat/demux.h" int ff_alloc_input_device_context(AVFormatContext **avctx, const AVInputFormat *iformat, const char *format) { @@ -38,8 +39,8 @@ int ff_alloc_input_device_context(AVFormatContext **avctx, const AVInputFormat * goto error; } s->iformat = iformat; - if (s->iformat->priv_data_size > 0) { - s->priv_data = av_mallocz(s->iformat->priv_data_size); + if (ffifmt(s->iformat)->priv_data_size > 0) { + s->priv_data = av_mallocz(ffifmt(s->iformat)->priv_data_size); if (!s->priv_data) { ret = AVERROR(ENOMEM); goto error; diff --git a/libavdevice/v4l2.c b/libavdevice/v4l2.c index 5e85d1a2b34..50ac47ec5a5 100644 --- a/libavdevice/v4l2.c +++ b/libavdevice/v4l2.c @@ -107,10 +107,10 @@ struct video_data { int (*open_f)(const char *file, int oflag, ...); int (*close_f)(int fd); int (*dup_f)(int fd); -#ifdef __GLIBC__ - int (*ioctl_f)(int fd, unsigned long int request, ...); -#else +#if defined(__sun) || defined(__BIONIC__) || defined(__musl__) /* POSIX-like */ int (*ioctl_f)(int fd, int request, ...); +#else + int (*ioctl_f)(int fd, unsigned long int request, ...); #endif ssize_t (*read_f)(int fd, void *buffer, size_t n); void *(*mmap_f)(void *start, size_t length, int prot, int flags, int fd, int64_t offset); @@ -1109,19 +1109,19 @@ static const AVOption options[] = { { "input_format", "set preferred pixel format (for raw video) or codec name", OFFSET(pixel_format), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, { "framerate", "set frame rate", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, DEC }, - { "list_formats", "list available formats and exit", OFFSET(list_format), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC, "list_formats" }, - { "all", "show all available formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.i64 = V4L_ALLFORMATS }, 0, INT_MAX, DEC, "list_formats" }, - { "raw", "show only non-compressed formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.i64 = V4L_RAWFORMATS }, 0, INT_MAX, DEC, "list_formats" }, - { "compressed", "show only compressed formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.i64 = V4L_COMPFORMATS }, 0, INT_MAX, DEC, "list_formats" }, + { "list_formats", "list available formats and exit", OFFSET(list_format), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, INT_MAX, DEC, .unit = "list_formats" }, + { "all", "show all available formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.i64 = V4L_ALLFORMATS }, 0, INT_MAX, DEC, .unit = "list_formats" }, + { "raw", "show only non-compressed formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.i64 = V4L_RAWFORMATS }, 0, INT_MAX, DEC, .unit = "list_formats" }, + { "compressed", "show only compressed formats", OFFSET(list_format), AV_OPT_TYPE_CONST, {.i64 = V4L_COMPFORMATS }, 0, INT_MAX, DEC, .unit = "list_formats" }, - { "list_standards", "list supported standards and exit", OFFSET(list_standard), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, DEC, "list_standards" }, - { "all", "show all supported standards", OFFSET(list_standard), AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, DEC, "list_standards" }, + { "list_standards", "list supported standards and exit", OFFSET(list_standard), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, DEC, .unit = "list_standards" }, + { "all", "show all supported standards", OFFSET(list_standard), AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, DEC, .unit = "list_standards" }, - { "timestamps", "set type of timestamps for grabbed frames", OFFSET(ts_mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 2, DEC, "timestamps" }, - { "ts", "set type of timestamps for grabbed frames", OFFSET(ts_mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 2, DEC, "timestamps" }, - { "default", "use timestamps from the kernel", OFFSET(ts_mode), AV_OPT_TYPE_CONST, {.i64 = V4L_TS_DEFAULT }, 0, 2, DEC, "timestamps" }, - { "abs", "use absolute timestamps (wall clock)", OFFSET(ts_mode), AV_OPT_TYPE_CONST, {.i64 = V4L_TS_ABS }, 0, 2, DEC, "timestamps" }, - { "mono2abs", "force conversion from monotonic to absolute timestamps", OFFSET(ts_mode), AV_OPT_TYPE_CONST, {.i64 = V4L_TS_MONO2ABS }, 0, 2, DEC, "timestamps" }, + { "timestamps", "set type of timestamps for grabbed frames", OFFSET(ts_mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 2, DEC, .unit = "timestamps" }, + { "ts", "set type of timestamps for grabbed frames", OFFSET(ts_mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 2, DEC, .unit = "timestamps" }, + { "default", "use timestamps from the kernel", OFFSET(ts_mode), AV_OPT_TYPE_CONST, {.i64 = V4L_TS_DEFAULT }, 0, 2, DEC, .unit = "timestamps" }, + { "abs", "use absolute timestamps (wall clock)", OFFSET(ts_mode), AV_OPT_TYPE_CONST, {.i64 = V4L_TS_ABS }, 0, 2, DEC, .unit = "timestamps" }, + { "mono2abs", "force conversion from monotonic to absolute timestamps", OFFSET(ts_mode), AV_OPT_TYPE_CONST, {.i64 = V4L_TS_MONO2ABS }, 0, 2, DEC, .unit = "timestamps" }, { "use_libv4l2", "use libv4l2 (v4l-utils) conversion functions", OFFSET(use_libv4l2), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DEC }, { NULL }, }; @@ -1134,15 +1134,15 @@ static const AVClass v4l2_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT, }; -const AVInputFormat ff_v4l2_demuxer = { - .name = "video4linux2,v4l2", - .long_name = NULL_IF_CONFIG_SMALL("Video4Linux2 device grab"), +const FFInputFormat ff_v4l2_demuxer = { + .p.name = "video4linux2,v4l2", + .p.long_name = NULL_IF_CONFIG_SMALL("Video4Linux2 device grab"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &v4l2_class, .priv_data_size = sizeof(struct video_data), .read_probe = v4l2_read_probe, .read_header = v4l2_read_header, .read_packet = v4l2_read_packet, .read_close = v4l2_read_close, .get_device_list = v4l2_get_device_list, - .flags = AVFMT_NOFILE, - .priv_class = &v4l2_class, }; diff --git a/libavdevice/version_major.h b/libavdevice/version_major.h index b884fd42246..f16abb6909b 100644 --- a/libavdevice/version_major.h +++ b/libavdevice/version_major.h @@ -25,7 +25,7 @@ * Libavdevice version macros */ -#define LIBAVDEVICE_VERSION_MAJOR 60 +#define LIBAVDEVICE_VERSION_MAJOR 61 /** * FF_API_* defines may be placed below to indicate public API that will be @@ -33,4 +33,11 @@ * the public API and may change, break or disappear at any time. */ +// reminder to remove the bktr device on next major bump +#define FF_API_BKTR_DEVICE (LIBAVDEVICE_VERSION_MAJOR < 62) +// reminder to remove the opengl device on next major bump +#define FF_API_OPENGL_DEVICE (LIBAVDEVICE_VERSION_MAJOR < 62) +// reminder to remove the sdl2 device on next major bump +#define FF_API_SDL2_DEVICE (LIBAVDEVICE_VERSION_MAJOR < 62) + #endif /* AVDEVICE_VERSION_MAJOR_H */ diff --git a/libavdevice/vfwcap.c b/libavdevice/vfwcap.c index 86a40b4af44..dd077dd08c4 100644 --- a/libavdevice/vfwcap.c +++ b/libavdevice/vfwcap.c @@ -25,6 +25,7 @@ #include "libavutil/parseutils.h" #include "libavcodec/packet_internal.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" // windows.h must no be included before winsock2.h, and libavformat internal @@ -482,13 +483,13 @@ static const AVClass vfw_class = { .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT }; -const AVInputFormat ff_vfwcap_demuxer = { - .name = "vfwcap", - .long_name = NULL_IF_CONFIG_SMALL("VfW video capture"), +const FFInputFormat ff_vfwcap_demuxer = { + .p.name = "vfwcap", + .p.long_name = NULL_IF_CONFIG_SMALL("VfW video capture"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &vfw_class, .priv_data_size = sizeof(struct vfw_ctx), .read_header = vfw_read_header, .read_packet = vfw_read_packet, .read_close = vfw_read_close, - .flags = AVFMT_NOFILE, - .priv_class = &vfw_class, }; diff --git a/libavdevice/xcbgrab.c b/libavdevice/xcbgrab.c index 64a68ba4974..329e79bf3fa 100644 --- a/libavdevice/xcbgrab.c +++ b/libavdevice/xcbgrab.c @@ -45,6 +45,7 @@ #include "libavutil/time.h" #include "libavformat/avformat.h" +#include "libavformat/demux.h" #include "libavformat/internal.h" typedef struct XCBGrabContext { @@ -92,8 +93,8 @@ static const AVOption options[] = { { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "ntsc" }, 0, 0, D }, { "draw_mouse", "Draw the mouse pointer.", OFFSET(draw_mouse), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, D }, { "follow_mouse", "Move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region.", - OFFSET(follow_mouse), AV_OPT_TYPE_INT, { .i64 = 0 }, FOLLOW_CENTER, INT_MAX, D, "follow_mouse" }, - { "centered", "Keep the mouse pointer at the center of grabbing region when following.", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX, D, "follow_mouse" }, + OFFSET(follow_mouse), AV_OPT_TYPE_INT, { .i64 = 0 }, FOLLOW_CENTER, INT_MAX, D, .unit = "follow_mouse" }, + { "centered", "Keep the mouse pointer at the center of grabbing region when following.", 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, INT_MIN, INT_MAX, D, .unit = "follow_mouse" }, { "show_region", "Show the grabbing region.", OFFSET(show_region), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, D }, { "region_border", "Set the region border thickness.", OFFSET(region_border), AV_OPT_TYPE_INT, { .i64 = 3 }, 1, 128, D }, { "select_region", "Select the grabbing region graphically using the pointer.", OFFSET(select_region), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, D }, @@ -826,7 +827,10 @@ static av_cold int xcbgrab_read_header(AVFormatContext *s) if (!sscanf(s->url, "%[^+]+%d,%d", display_name, &c->x, &c->y)) { *display_name = 0; - sscanf(s->url, "+%d,%d", &c->x, &c->y); + if(sscanf(s->url, "+%d,%d", &c->x, &c->y) != 2) { + if (*s->url) + av_log(s, AV_LOG_WARNING, "Ambigous URL: %s\n", s->url); + } } c->conn = xcb_connect(display_name[0] ? display_name : NULL, &screen_num); @@ -900,13 +904,13 @@ static av_cold int xcbgrab_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_xcbgrab_demuxer = { - .name = "x11grab", - .long_name = NULL_IF_CONFIG_SMALL("X11 screen capture, using XCB"), +const FFInputFormat ff_xcbgrab_demuxer = { + .p.name = "x11grab", + .p.long_name = NULL_IF_CONFIG_SMALL("X11 screen capture, using XCB"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &xcbgrab_class, .priv_data_size = sizeof(XCBGrabContext), .read_header = xcbgrab_read_header, .read_packet = xcbgrab_read_packet, .read_close = xcbgrab_read_close, - .flags = AVFMT_NOFILE, - .priv_class = &xcbgrab_class, }; diff --git a/libavdevice/xv.c b/libavdevice/xv.c index 441f8541218..cba933ef178 100644 --- a/libavdevice/xv.c +++ b/libavdevice/xv.c @@ -32,6 +32,7 @@ #include #include +#include "libavutil/frame.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/imgutils.h" @@ -314,8 +315,8 @@ static int write_picture(AVFormatContext *s, uint8_t *input_data[4], } } - av_image_copy(data, img->pitches, (const uint8_t **)input_data, linesize, - xv->image_format, img->width, img->height); + av_image_copy2(data, img->pitches, input_data, linesize, + xv->image_format, img->width, img->height); return xv_repaint(s); } diff --git a/libavfilter/Makefile b/libavfilter/Makefile index b3d3d981dd4..994d9773ba5 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -14,8 +14,8 @@ OBJS = allfilters.o \ buffersink.o \ buffersrc.o \ colorspace.o \ + ccfifo.o \ drawutils.o \ - fifo.o \ formats.o \ framepool.o \ framequeue.o \ @@ -34,6 +34,7 @@ OBJS-$(CONFIG_DNN) += dnn_filter_common.o include $(SRC_PATH)/libavfilter/dnn/Makefile # audio filters +OBJS-$(CONFIG_AAP_FILTER) += af_aap.o OBJS-$(CONFIG_ABENCH_FILTER) += f_bench.o OBJS-$(CONFIG_ACOMPRESSOR_FILTER) += af_sidechaincompress.o OBJS-$(CONFIG_ACONTRAST_FILTER) += af_acontrast.o @@ -83,11 +84,13 @@ OBJS-$(CONFIG_APAD_FILTER) += af_apad.o OBJS-$(CONFIG_APERMS_FILTER) += f_perms.o OBJS-$(CONFIG_APHASER_FILTER) += af_aphaser.o generate_wave_table.o OBJS-$(CONFIG_APHASESHIFT_FILTER) += af_afreqshift.o +OBJS-$(CONFIG_APSNR_FILTER) += af_asdr.o OBJS-$(CONFIG_APSYCLIP_FILTER) += af_apsyclip.o OBJS-$(CONFIG_APULSATOR_FILTER) += af_apulsator.o OBJS-$(CONFIG_AREALTIME_FILTER) += f_realtime.o OBJS-$(CONFIG_ARESAMPLE_FILTER) += af_aresample.o OBJS-$(CONFIG_AREVERSE_FILTER) += f_reverse.o +OBJS-$(CONFIG_ARLS_FILTER) += af_arls.o OBJS-$(CONFIG_ARNNDN_FILTER) += af_arnndn.o OBJS-$(CONFIG_ASDR_FILTER) += af_asdr.o OBJS-$(CONFIG_ASEGMENT_FILTER) += f_segment.o @@ -99,6 +102,7 @@ OBJS-$(CONFIG_ASETRATE_FILTER) += af_asetrate.o OBJS-$(CONFIG_ASETTB_FILTER) += settb.o OBJS-$(CONFIG_ASHOWINFO_FILTER) += af_ashowinfo.o OBJS-$(CONFIG_ASIDEDATA_FILTER) += f_sidedata.o +OBJS-$(CONFIG_ASISDR_FILTER) += af_asdr.o OBJS-$(CONFIG_ASOFTCLIP_FILTER) += af_asoftclip.o OBJS-$(CONFIG_ASPECTRALSTATS_FILTER) += af_aspectralstats.o OBJS-$(CONFIG_ASPLIT_FILTER) += split.o @@ -172,6 +176,7 @@ OBJS-$(CONFIG_VOLUMEDETECT_FILTER) += af_volumedetect.o OBJS-$(CONFIG_AEVALSRC_FILTER) += aeval.o OBJS-$(CONFIG_AFDELAYSRC_FILTER) += asrc_afdelaysrc.o +OBJS-$(CONFIG_AFIREQSRC_FILTER) += asrc_afirsrc.o OBJS-$(CONFIG_AFIRSRC_FILTER) += asrc_afirsrc.o OBJS-$(CONFIG_ANOISESRC_FILTER) += asrc_anoisesrc.o OBJS-$(CONFIG_ANULLSRC_FILTER) += asrc_anullsrc.o @@ -209,8 +214,12 @@ OBJS-$(CONFIG_BM3D_FILTER) += vf_bm3d.o framesync.o OBJS-$(CONFIG_BOXBLUR_FILTER) += vf_boxblur.o boxblur.o OBJS-$(CONFIG_BOXBLUR_OPENCL_FILTER) += vf_avgblur_opencl.o opencl.o \ opencl/avgblur.o boxblur.o -OBJS-$(CONFIG_BWDIF_FILTER) += vf_bwdif.o yadif_common.o +OBJS-$(CONFIG_BWDIF_FILTER) += vf_bwdif.o bwdifdsp.o yadif_common.o +OBJS-$(CONFIG_BWDIF_CUDA_FILTER) += vf_bwdif_cuda.o vf_bwdif_cuda.ptx.o \ + yadif_common.o +OBJS-$(CONFIG_BWDIF_VULKAN_FILTER) += vf_bwdif_vulkan.o yadif_common.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_CAS_FILTER) += vf_cas.o +OBJS-$(CONFIG_CCREPACK_FILTER) += vf_ccrepack.o OBJS-$(CONFIG_CHROMABER_VULKAN_FILTER) += vf_chromaber_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_CHROMAHOLD_FILTER) += vf_chromakey.o OBJS-$(CONFIG_CHROMAKEY_FILTER) += vf_chromakey.o @@ -246,7 +255,7 @@ OBJS-$(CONFIG_COREIMAGE_FILTER) += vf_coreimage.o OBJS-$(CONFIG_CORR_FILTER) += vf_corr.o framesync.o OBJS-$(CONFIG_COVER_RECT_FILTER) += vf_cover_rect.o lavfutils.o OBJS-$(CONFIG_CROP_FILTER) += vf_crop.o -OBJS-$(CONFIG_CROPDETECT_FILTER) += vf_cropdetect.o +OBJS-$(CONFIG_CROPDETECT_FILTER) += vf_cropdetect.o edge_common.o OBJS-$(CONFIG_CUE_FILTER) += f_cue.o OBJS-$(CONFIG_CURVES_FILTER) += vf_curves.o OBJS-$(CONFIG_DATASCOPE_FILTER) += vf_datascope.o @@ -281,7 +290,7 @@ OBJS-$(CONFIG_DOUBLEWEAVE_FILTER) += vf_weave.o OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o OBJS-$(CONFIG_DRAWGRAPH_FILTER) += f_drawgraph.o OBJS-$(CONFIG_DRAWGRID_FILTER) += vf_drawbox.o -OBJS-$(CONFIG_DRAWTEXT_FILTER) += vf_drawtext.o +OBJS-$(CONFIG_DRAWTEXT_FILTER) += vf_drawtext.o textutils.o OBJS-$(CONFIG_EDGEDETECT_FILTER) += vf_edgedetect.o edge_common.o OBJS-$(CONFIG_ELBG_FILTER) += vf_elbg.o OBJS-$(CONFIG_ENTROPY_FILTER) += vf_entropy.o @@ -313,6 +322,7 @@ OBJS-$(CONFIG_FREEZEDETECT_FILTER) += vf_freezedetect.o OBJS-$(CONFIG_FREEZEFRAMES_FILTER) += vf_freezeframes.o OBJS-$(CONFIG_FREI0R_FILTER) += vf_frei0r.o OBJS-$(CONFIG_FSPP_FILTER) += vf_fspp.o qp_table.o +OBJS-$(CONFIG_FSYNC_FILTER) += vf_fsync.o OBJS-$(CONFIG_GBLUR_FILTER) += vf_gblur.o OBJS-$(CONFIG_GBLUR_VULKAN_FILTER) += vf_gblur_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_GEQ_FILTER) += vf_geq.o @@ -354,6 +364,7 @@ OBJS-$(CONFIG_LENSCORRECTION_FILTER) += vf_lenscorrection.o OBJS-$(CONFIG_LENSFUN_FILTER) += vf_lensfun.o OBJS-$(CONFIG_LIBPLACEBO_FILTER) += vf_libplacebo.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_LIBVMAF_FILTER) += vf_libvmaf.o framesync.o +OBJS-$(CONFIG_LIBVMAF_CUDA_FILTER) += vf_libvmaf.o framesync.o OBJS-$(CONFIG_LIMITDIFF_FILTER) += vf_limitdiff.o framesync.o OBJS-$(CONFIG_LIMITER_FILTER) += vf_limiter.o OBJS-$(CONFIG_LOOP_FILTER) += f_loop.o @@ -385,6 +396,7 @@ OBJS-$(CONFIG_MULTIPLY_FILTER) += vf_multiply.o OBJS-$(CONFIG_NEGATE_FILTER) += vf_negate.o OBJS-$(CONFIG_NLMEANS_FILTER) += vf_nlmeans.o OBJS-$(CONFIG_NLMEANS_OPENCL_FILTER) += vf_nlmeans_opencl.o opencl.o opencl/nlmeans.o +OBJS-$(CONFIG_NLMEANS_VULKAN_FILTER) += vf_nlmeans_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_NNEDI_FILTER) += vf_nnedi.o OBJS-$(CONFIG_NOFORMAT_FILTER) += vf_format.o OBJS-$(CONFIG_NOISE_FILTER) += vf_noise.o @@ -425,6 +437,7 @@ OBJS-$(CONFIG_PSEUDOCOLOR_FILTER) += vf_pseudocolor.o OBJS-$(CONFIG_PSNR_FILTER) += vf_psnr.o framesync.o OBJS-$(CONFIG_PULLUP_FILTER) += vf_pullup.o OBJS-$(CONFIG_QP_FILTER) += vf_qp.o +OBJS-$(CONFIG_QUIRC_FILTER) += vf_quirc.o OBJS-$(CONFIG_RANDOM_FILTER) += vf_random.o OBJS-$(CONFIG_READEIA608_FILTER) += vf_readeia608.o OBJS-$(CONFIG_READVITC_FILTER) += vf_readvitc.o @@ -448,6 +461,7 @@ OBJS-$(CONFIG_SCALE_CUDA_FILTER) += vf_scale_cuda.o scale_eval.o \ OBJS-$(CONFIG_SCALE_NPP_FILTER) += vf_scale_npp.o scale_eval.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_VT_FILTER) += vf_scale_vt.o scale_eval.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 OBJS-$(CONFIG_SCALE2REF_NPP_FILTER) += vf_scale_npp.o scale_eval.o @@ -501,6 +515,7 @@ OBJS-$(CONFIG_THUMBNAIL_FILTER) += vf_thumbnail.o OBJS-$(CONFIG_THUMBNAIL_CUDA_FILTER) += vf_thumbnail_cuda.o vf_thumbnail_cuda.ptx.o \ cuda/load_helper.o OBJS-$(CONFIG_TILE_FILTER) += vf_tile.o +OBJS-$(CONFIG_TILTANDSHIFT_FILTER) += vf_tiltandshift.o OBJS-$(CONFIG_TINTERLACE_FILTER) += vf_tinterlace.o OBJS-$(CONFIG_TLUT2_FILTER) += vf_lut2.o framesync.o OBJS-$(CONFIG_TMEDIAN_FILTER) += vf_xmedian.o framesync.o @@ -515,6 +530,7 @@ OBJS-$(CONFIG_TRANSPOSE_FILTER) += vf_transpose.o OBJS-$(CONFIG_TRANSPOSE_NPP_FILTER) += vf_transpose_npp.o OBJS-$(CONFIG_TRANSPOSE_OPENCL_FILTER) += vf_transpose_opencl.o opencl.o opencl/transpose.o OBJS-$(CONFIG_TRANSPOSE_VAAPI_FILTER) += vf_transpose_vaapi.o vaapi_vpp.o +OBJS-$(CONFIG_TRANSPOSE_VT_FILTER) += vf_transpose_vt.o OBJS-$(CONFIG_TRANSPOSE_VULKAN_FILTER) += vf_transpose_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_TRIM_FILTER) += trim.o OBJS-$(CONFIG_UNPREMULTIPLY_FILTER) += vf_premultiply.o framesync.o @@ -545,6 +561,7 @@ OBJS-$(CONFIG_XBR_FILTER) += vf_xbr.o OBJS-$(CONFIG_XCORRELATE_FILTER) += vf_convolve.o framesync.o OBJS-$(CONFIG_XFADE_FILTER) += vf_xfade.o OBJS-$(CONFIG_XFADE_OPENCL_FILTER) += vf_xfade_opencl.o opencl.o opencl/xfade.o +OBJS-$(CONFIG_XFADE_VULKAN_FILTER) += vf_xfade_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_XMEDIAN_FILTER) += vf_xmedian.o framesync.o OBJS-$(CONFIG_XSTACK_FILTER) += vf_stack.o framesync.o OBJS-$(CONFIG_YADIF_FILTER) += vf_yadif.o yadif_common.o @@ -583,13 +600,17 @@ OBJS-$(CONFIG_NULLSRC_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_OPENCLSRC_FILTER) += vf_program_opencl.o opencl.o OBJS-$(CONFIG_PAL75BARS_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_PAL100BARS_FILTER) += vsrc_testsrc.o +OBJS-$(CONFIG_QRENCODE_FILTER) += qrencode.o textutils.o +OBJS-$(CONFIG_QRENCODESRC_FILTER) += qrencode.o textutils.o OBJS-$(CONFIG_RGBTESTSRC_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_SIERPINSKI_FILTER) += vsrc_sierpinski.o OBJS-$(CONFIG_SMPTEBARS_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_SMPTEHDBARS_FILTER) += vsrc_testsrc.o +OBJS-$(CONFIG_COLOR_VULKAN_FILTER) += vsrc_testsrc_vulkan.o vulkan.o vulkan_filter.o OBJS-$(CONFIG_TESTSRC_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_TESTSRC2_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_YUVTESTSRC_FILTER) += vsrc_testsrc.o +OBJS-$(CONFIG_ZONEPLATE_FILTER) += vsrc_testsrc.o OBJS-$(CONFIG_NULLSINK_FILTER) += vsink_nullsink.o @@ -618,6 +639,10 @@ OBJS-$(CONFIG_AVSYNCTEST_FILTER) += src_avsynctest.o OBJS-$(CONFIG_AMOVIE_FILTER) += src_movie.o OBJS-$(CONFIG_MOVIE_FILTER) += src_movie.o +# vulkan libs +OBJS-$(CONFIG_LIBGLSLANG) += vulkan_glslang.o +OBJS-$(CONFIG_LIBSHADERC) += vulkan_shaderc.o + # Objects duplicated from other libraries for shared builds SHLIBOBJS += log2_tab.o @@ -627,24 +652,31 @@ SHLIBOBJS-$(HAVE_GNU_WINDRES) += avfilterres.o SKIPHEADERS-$(CONFIG_LCMS2) += fflcms2.h SKIPHEADERS-$(CONFIG_LIBVIDSTAB) += vidstabutils.h -SKIPHEADERS-$(CONFIG_QSVVPP) += qsvvpp.h +SKIPHEADERS-$(CONFIG_QSVVPP) += qsvvpp.h stack_internal.h SKIPHEADERS-$(CONFIG_OPENCL) += opencl.h -SKIPHEADERS-$(CONFIG_VAAPI) += vaapi_vpp.h +SKIPHEADERS-$(CONFIG_VAAPI) += vaapi_vpp.h stack_internal.h SKIPHEADERS-$(CONFIG_VULKAN) += vulkan.h vulkan_filter.h +SKIPHEADERS-$(CONFIG_LIBSHADERC) += vulkan_spirv.h +SKIPHEADERS-$(CONFIG_LIBGLSLANG) += vulkan_spirv.h TOOLS = graph2dot TESTPROGS = drawutils filtfmts formats integral -TESTPROGS-$(CONFIG_DNN) += dnn-layer-avgpool dnn-layer-conv2d dnn-layer-dense \ - dnn-layer-depth2space dnn-layer-mathbinary \ - dnn-layer-mathunary dnn-layer-maximum dnn-layer-pad \ TOOLS-$(CONFIG_LIBZMQ) += zmqsend clean:: - $(RM) $(CLEANSUFFIXES:%=libavfilter/dnn/%) $(CLEANSUFFIXES:%=libavfilter/opencl/%) + $(RM) $(CLEANSUFFIXES:%=libavfilter/dnn/%) $(CLEANSUFFIXES:%=libavfilter/opencl/%) \ + $(CLEANSUFFIXES:%=libavfilter/metal/%) \ + $(CLEANSUFFIXES:%=libavfilter/vulkan/%) OPENCL = $(subst $(SRC_PATH)/,,$(wildcard $(SRC_PATH)/libavfilter/opencl/*.cl)) .SECONDARY: $(OPENCL:.cl=.c) libavfilter/opencl/%.c: TAG = OPENCL libavfilter/opencl/%.c: $(SRC_PATH)/libavfilter/opencl/%.cl - $(M)$(SRC_PATH)/tools/cl2c $< $@ + $(M)$(SRC_PATH)/tools/source2c $< $@ + +VULKAN = $(subst $(SRC_PATH)/,,$(wildcard $(SRC_PATH)/libavfilter/vulkan/*.comp)) +.SECONDARY: $(VULKAN:.comp=.c) +libavfilter/vulkan/%.c: TAG = OPENCL +libavfilter/vulkan/%.c: $(SRC_PATH)/libavfilter/vulkan/%.comp + $(M)$(SRC_PATH)/tools/source2c $< $@ diff --git a/libavfilter/aap_template.c b/libavfilter/aap_template.c new file mode 100644 index 00000000000..ea9c815a89b --- /dev/null +++ b/libavfilter/aap_template.c @@ -0,0 +1,227 @@ +/* + * 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 + */ + +#undef ZERO +#undef ONE +#undef ftype +#undef SAMPLE_FORMAT +#if DEPTH == 32 +#define SAMPLE_FORMAT float +#define ftype float +#define ONE 1.f +#define ZERO 0.f +#else +#define SAMPLE_FORMAT double +#define ftype double +#define ONE 1.0 +#define ZERO 0.0 +#endif + +#define fn3(a,b) a##_##b +#define fn2(a,b) fn3(a,b) +#define fn(a) fn2(a, SAMPLE_FORMAT) + +#if DEPTH == 64 +static double scalarproduct_double(const double *v1, const double *v2, int len) +{ + double p = 0.0; + + for (int i = 0; i < len; i++) + p += v1[i] * v2[i]; + + return p; +} +#endif + +static ftype fn(fir_sample)(AudioAPContext *s, ftype sample, ftype *delay, + ftype *coeffs, ftype *tmp, int *offset) +{ + const int order = s->order; + ftype output; + + delay[*offset] = sample; + + memcpy(tmp, coeffs + order - *offset, order * sizeof(ftype)); +#if DEPTH == 32 + output = s->fdsp->scalarproduct_float(delay, tmp, s->kernel_size); +#else + output = scalarproduct_double(delay, tmp, s->kernel_size); +#endif + + if (--(*offset) < 0) + *offset = order - 1; + + return output; +} + +static int fn(lup_decompose)(ftype **MA, const int N, const ftype tol, int *P) +{ + for (int i = 0; i <= N; i++) + P[i] = i; + + for (int i = 0; i < N; i++) { + ftype maxA = ZERO; + int imax = i; + + for (int k = i; k < N; k++) { + ftype absA = fabs(MA[k][i]); + if (absA > maxA) { + maxA = absA; + imax = k; + } + } + + if (maxA < tol) + return 0; + + if (imax != i) { + FFSWAP(int, P[i], P[imax]); + FFSWAP(ftype *, MA[i], MA[imax]); + P[N]++; + } + + for (int j = i + 1; j < N; j++) { + MA[j][i] /= MA[i][i]; + + for (int k = i + 1; k < N; k++) + MA[j][k] -= MA[j][i] * MA[i][k]; + } + } + + return 1; +} + +static void fn(lup_invert)(ftype *const *MA, const int *P, const int N, ftype **IA) +{ + for (int j = 0; j < N; j++) { + for (int i = 0; i < N; i++) { + IA[i][j] = P[i] == j ? ONE : ZERO; + + for (int k = 0; k < i; k++) + IA[i][j] -= MA[i][k] * IA[k][j]; + } + + for (int i = N - 1; i >= 0; i--) { + for (int k = i + 1; k < N; k++) + IA[i][j] -= MA[i][k] * IA[k][j]; + + IA[i][j] /= MA[i][i]; + } + } +} + +static ftype fn(process_sample)(AudioAPContext *s, ftype input, ftype desired, int ch) +{ + ftype *dcoeffs = (ftype *)s->dcoeffs->extended_data[ch]; + ftype *coeffs = (ftype *)s->coeffs->extended_data[ch]; + ftype *delay = (ftype *)s->delay->extended_data[ch]; + ftype **itmpmp = (ftype **)&s->itmpmp[s->projection * ch]; + ftype **tmpmp = (ftype **)&s->tmpmp[s->projection * ch]; + ftype *tmpm = (ftype *)s->tmpm->extended_data[ch]; + ftype *tmp = (ftype *)s->tmp->extended_data[ch]; + ftype *e = (ftype *)s->e->extended_data[ch]; + ftype *x = (ftype *)s->x->extended_data[ch]; + ftype *w = (ftype *)s->w->extended_data[ch]; + int *p = (int *)s->p->extended_data[ch]; + int *offset = (int *)s->offset->extended_data[ch]; + const int projection = s->projection; + const ftype delta = s->delta; + const int order = s->order; + const int length = projection + order; + const ftype mu = s->mu; + const ftype tol = 0.00001f; + ftype output; + + x[offset[2] + length] = x[offset[2]] = input; + delay[offset[0] + order] = input; + + output = fn(fir_sample)(s, input, delay, coeffs, tmp, offset); + e[offset[1]] = e[offset[1] + projection] = desired - output; + + for (int i = 0; i < projection; i++) { + const int iprojection = i * projection; + + for (int j = i; j < projection; j++) { + ftype sum = ZERO; + for (int k = 0; k < order; k++) + sum += x[offset[2] + i + k] * x[offset[2] + j + k]; + tmpm[iprojection + j] = sum; + if (i != j) + tmpm[j * projection + i] = sum; + } + + tmpm[iprojection + i] += delta; + } + + fn(lup_decompose)(tmpmp, projection, tol, p); + fn(lup_invert)(tmpmp, p, projection, itmpmp); + + for (int i = 0; i < projection; i++) { + ftype sum = ZERO; + for (int j = 0; j < projection; j++) + sum += itmpmp[i][j] * e[j + offset[1]]; + w[i] = sum; + } + + for (int i = 0; i < order; i++) { + ftype sum = ZERO; + for (int j = 0; j < projection; j++) + sum += x[offset[2] + i + j] * w[j]; + dcoeffs[i] = sum; + } + + for (int i = 0; i < order; i++) + coeffs[i] = coeffs[i + order] = coeffs[i] + mu * dcoeffs[i]; + + if (--offset[1] < 0) + offset[1] = projection - 1; + + if (--offset[2] < 0) + offset[2] = length - 1; + + switch (s->output_mode) { + case IN_MODE: output = input; break; + case DESIRED_MODE: output = desired; break; + case OUT_MODE: output = desired - output; break; + case NOISE_MODE: output = input - output; break; + case ERROR_MODE: break; + } + return output; +} + +static int fn(filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + AudioAPContext *s = ctx->priv; + AVFrame *out = arg; + const int start = (out->ch_layout.nb_channels * jobnr) / nb_jobs; + const int end = (out->ch_layout.nb_channels * (jobnr+1)) / nb_jobs; + + for (int c = start; c < end; c++) { + const ftype *input = (const ftype *)s->frame[0]->extended_data[c]; + const ftype *desired = (const ftype *)s->frame[1]->extended_data[c]; + ftype *output = (ftype *)out->extended_data[c]; + + for (int n = 0; n < out->nb_samples; n++) { + output[n] = fn(process_sample)(s, input[n], desired[n], c); + if (ctx->is_disabled) + output[n] = input[n]; + } + } + + return 0; +} diff --git a/libavfilter/aarch64/Makefile b/libavfilter/aarch64/Makefile index b58daa3a3fe..b68209bc94a 100644 --- a/libavfilter/aarch64/Makefile +++ b/libavfilter/aarch64/Makefile @@ -1,3 +1,5 @@ +OBJS-$(CONFIG_BWDIF_FILTER) += aarch64/vf_bwdif_init_aarch64.o OBJS-$(CONFIG_NLMEANS_FILTER) += aarch64/vf_nlmeans_init.o +NEON-OBJS-$(CONFIG_BWDIF_FILTER) += aarch64/vf_bwdif_neon.o NEON-OBJS-$(CONFIG_NLMEANS_FILTER) += aarch64/vf_nlmeans_neon.o diff --git a/libavfilter/aarch64/vf_bwdif_init_aarch64.c b/libavfilter/aarch64/vf_bwdif_init_aarch64.c new file mode 100644 index 00000000000..74530ed5b70 --- /dev/null +++ b/libavfilter/aarch64/vf_bwdif_init_aarch64.c @@ -0,0 +1,125 @@ +/* + * bwdif aarch64 NEON optimisations + * + * Copyright (c) 2023 John Cox + * + * 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/common.h" +#include "libavfilter/bwdifdsp.h" +#include "libavutil/aarch64/cpu.h" + +void ff_bwdif_filter_edge_neon(void *dst1, const void *prev1, const void *cur1, const void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int parity, int clip_max, int spat); + +void ff_bwdif_filter_intra_neon(void *dst1, const void *cur1, int w, int prefs, int mrefs, + int prefs3, int mrefs3, int parity, int clip_max); + +void ff_bwdif_filter_line_neon(void *dst1, const void *prev1, const void *cur1, const void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int prefs3, int mrefs3, int prefs4, int mrefs4, + int parity, int clip_max); + +void ff_bwdif_filter_line3_neon(void * dst1, int d_stride, + const void * prev1, const void * cur1, const void * next1, int s_stride, + int w, int parity, int clip_max); + + +static void filter_line3_helper(void * dst1, int d_stride, + const void * prev1, const void * cur1, const void * next1, int s_stride, + int w, int parity, int clip_max) +{ + // Asm works on 16 byte chunks + // If w is a multiple of 16 then all is good - if not then if width rounded + // up to nearest 16 will fit in both src & dst strides then allow the asm + // to write over the padding bytes as that is almost certainly faster than + // having to invoke the C version to clean up the tail. + const int w1 = FFALIGN(w, 16); + const int w0 = clip_max != 255 ? 0 : + d_stride <= w1 && s_stride <= w1 ? w : w & ~15; + + ff_bwdif_filter_line3_neon(dst1, d_stride, + prev1, cur1, next1, s_stride, + w0, parity, clip_max); + + if (w0 < w) + ff_bwdif_filter_line3_c((char *)dst1 + w0, d_stride, + (const char *)prev1 + w0, (const char *)cur1 + w0, (const char *)next1 + w0, s_stride, + w - w0, parity, clip_max); +} + +static void filter_line_helper(void *dst1, const void *prev1, const void *cur1, const void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int prefs3, int mrefs3, int prefs4, int mrefs4, + int parity, int clip_max) +{ + const int w0 = clip_max != 255 ? 0 : w & ~15; + + ff_bwdif_filter_line_neon(dst1, prev1, cur1, next1, + w0, prefs, mrefs, prefs2, mrefs2, prefs3, mrefs3, prefs4, mrefs4, parity, clip_max); + + if (w0 < w) + ff_bwdif_filter_line_c((char *)dst1 + w0, (char *)prev1 + w0, (char *)cur1 + w0, (char *)next1 + w0, + w - w0, prefs, mrefs, prefs2, mrefs2, prefs3, mrefs3, prefs4, mrefs4, parity, clip_max); +} + +static void filter_edge_helper(void *dst1, const void *prev1, const void *cur1, const void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int parity, int clip_max, int spat) +{ + const int w0 = clip_max != 255 ? 0 : w & ~15; + + ff_bwdif_filter_edge_neon(dst1, prev1, cur1, next1, w0, prefs, mrefs, prefs2, mrefs2, + parity, clip_max, spat); + + if (w0 < w) + ff_bwdif_filter_edge_c((char *)dst1 + w0, (char *)prev1 + w0, (char *)cur1 + w0, (char *)next1 + w0, + w - w0, prefs, mrefs, prefs2, mrefs2, + parity, clip_max, spat); +} + +static void filter_intra_helper(void *dst1, const void *cur1, int w, int prefs, int mrefs, + int prefs3, int mrefs3, int parity, int clip_max) +{ + const int w0 = clip_max != 255 ? 0 : w & ~15; + + ff_bwdif_filter_intra_neon(dst1, cur1, w0, prefs, mrefs, prefs3, mrefs3, parity, clip_max); + + if (w0 < w) + ff_bwdif_filter_intra_c((char *)dst1 + w0, (char *)cur1 + w0, + w - w0, prefs, mrefs, prefs3, mrefs3, parity, clip_max); +} + +void +ff_bwdif_init_aarch64(BWDIFDSPContext *s, int bit_depth) +{ + const int cpu_flags = av_get_cpu_flags(); + + if (bit_depth != 8) + return; + + if (!have_neon(cpu_flags)) + return; + + s->filter_intra = filter_intra_helper; + s->filter_line = filter_line_helper; + s->filter_edge = filter_edge_helper; + s->filter_line3 = filter_line3_helper; +} + diff --git a/libavfilter/aarch64/vf_bwdif_neon.S b/libavfilter/aarch64/vf_bwdif_neon.S new file mode 100644 index 00000000000..bf268b12f89 --- /dev/null +++ b/libavfilter/aarch64/vf_bwdif_neon.S @@ -0,0 +1,788 @@ +/* + * bwdif aarch64 NEON optimisations + * + * Copyright (c) 2023 John Cox + * + * 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/aarch64/asm.S" + +// Space taken on the stack by an int (32-bit) +#ifdef __APPLE__ +.set SP_INT, 4 +#else +.set SP_INT, 8 +#endif + +.macro SQSHRUNN b, s0, s1, s2, s3, n + sqshrun \s0\().4h, \s0\().4s, #\n - 8 + sqshrun2 \s0\().8h, \s1\().4s, #\n - 8 + sqshrun \s1\().4h, \s2\().4s, #\n - 8 + sqshrun2 \s1\().8h, \s3\().4s, #\n - 8 + uzp2 \b\().16b, \s0\().16b, \s1\().16b +.endm + +.macro SMULL4K a0, a1, a2, a3, s0, s1, k + smull \a0\().4s, \s0\().4h, \k + smull2 \a1\().4s, \s0\().8h, \k + smull \a2\().4s, \s1\().4h, \k + smull2 \a3\().4s, \s1\().8h, \k +.endm + +.macro UMULL4K a0, a1, a2, a3, s0, s1, k + umull \a0\().4s, \s0\().4h, \k + umull2 \a1\().4s, \s0\().8h, \k + umull \a2\().4s, \s1\().4h, \k + umull2 \a3\().4s, \s1\().8h, \k +.endm + +.macro UMLAL4K a0, a1, a2, a3, s0, s1, k + umlal \a0\().4s, \s0\().4h, \k + umlal2 \a1\().4s, \s0\().8h, \k + umlal \a2\().4s, \s1\().4h, \k + umlal2 \a3\().4s, \s1\().8h, \k +.endm + +.macro UMLSL4K a0, a1, a2, a3, s0, s1, k + umlsl \a0\().4s, \s0\().4h, \k + umlsl2 \a1\().4s, \s0\().8h, \k + umlsl \a2\().4s, \s1\().4h, \k + umlsl2 \a3\().4s, \s1\().8h, \k +.endm + +// int b = m2s1 - m1; +// int f = p2s1 - p1; +// int dc = c0s1 - m1; +// int de = c0s1 - p1; +// int sp_max = FFMIN(p1 - c0s1, m1 - c0s1); +// sp_max = FFMIN(sp_max, FFMAX(-b,-f)); +// int sp_min = FFMIN(c0s1 - p1, c0s1 - m1); +// sp_min = FFMIN(sp_min, FFMAX(b,f)); +// diff = diff == 0 ? 0 : FFMAX3(diff, sp_min, sp_max); +.macro SPAT_CHECK diff, m2s1, m1, c0s1, p1, p2s1, t0, t1, t2, t3 + uqsub \t0\().16b, \p1\().16b, \c0s1\().16b + uqsub \t2\().16b, \m1\().16b, \c0s1\().16b + umin \t2\().16b, \t0\().16b, \t2\().16b + + uqsub \t1\().16b, \m1\().16b, \m2s1\().16b + uqsub \t3\().16b, \p1\().16b, \p2s1\().16b + umax \t3\().16b, \t3\().16b, \t1\().16b + umin \t3\().16b, \t3\().16b, \t2\().16b + + uqsub \t0\().16b, \c0s1\().16b, \p1\().16b + uqsub \t2\().16b, \c0s1\().16b, \m1\().16b + umin \t2\().16b, \t0\().16b, \t2\().16b + + uqsub \t1\().16b, \m2s1\().16b, \m1\().16b + uqsub \t0\().16b, \p2s1\().16b, \p1\().16b + umax \t0\().16b, \t0\().16b, \t1\().16b + umin \t2\().16b, \t2\().16b, \t0\().16b + + cmeq \t1\().16b, \diff\().16b, #0 + umax \diff\().16b, \diff\().16b, \t3\().16b + umax \diff\().16b, \diff\().16b, \t2\().16b + bic \diff\().16b, \diff\().16b, \t1\().16b +.endm + +// i0 = s0; +// if (i0 > d0 + diff0) +// i0 = d0 + diff0; +// else if (i0 < d0 - diff0) +// i0 = d0 - diff0; +// +// i0 = s0 is safe +.macro DIFF_CLIP i0, s0, d0, diff, t0, t1 + uqadd \t0\().16b, \d0\().16b, \diff\().16b + uqsub \t1\().16b, \d0\().16b, \diff\().16b + umin \i0\().16b, \s0\().16b, \t0\().16b + umax \i0\().16b, \i0\().16b, \t1\().16b +.endm + +// i0 = FFABS(m1 - p1) > td0 ? i1 : i2; +// DIFF_CLIP +// +// i0 = i1 is safe +.macro INTERPOL i0, i1, i2, m1, d0, p1, td0, diff, t0, t1, t2 + uabd \t0\().16b, \m1\().16b, \p1\().16b + cmhi \t0\().16b, \t0\().16b, \td0\().16b + bsl \t0\().16b, \i1\().16b, \i2\().16b + DIFF_CLIP \i0, \t0, \d0, \diff, \t1, \t2 +.endm + +.macro PUSH_VREGS + stp d8, d9, [sp, #-64]! + stp d10, d11, [sp, #16] + stp d12, d13, [sp, #32] + stp d14, d15, [sp, #48] +.endm + +.macro POP_VREGS + ldp d14, d15, [sp, #48] + ldp d12, d13, [sp, #32] + ldp d10, d11, [sp, #16] + ldp d8, d9, [sp], #64 +.endm + +.macro LDR_COEFFS d, t0 + movrel \t0, coeffs, 0 + ld1 {\d\().8h}, [\t0] +.endm + +// static const uint16_t coef_lf[2] = { 4309, 213 }; +// static const uint16_t coef_hf[3] = { 5570, 3801, 1016 }; +// static const uint16_t coef_sp[2] = { 5077, 981 }; + +const coeffs, align=4 // align 4 means align on 2^4 boundry + .hword 4309 * 4, 213 * 4 // lf[0]*4 = v0.h[0] + .hword 5570, 3801, 1016, -3801 // hf[0] = v0.h[2], -hf[1] = v0.h[5] + .hword 5077, 981 // sp[0] = v0.h[6] +endconst + +// =========================================================================== +// +// void ff_bwdif_filter_line3_neon( +// void * dst1, // x0 +// int d_stride, // w1 +// const void * prev1, // x2 +// const void * cur1, // x3 +// const void * next1, // x4 +// int s_stride, // w5 +// int w, // w6 +// int parity, // w7 +// int clip_max); // [sp, #0] (Ignored) + +function ff_bwdif_filter_line3_neon, export=1 + // Sanity check w + cmp w6, #0 + ble 99f + + LDR_COEFFS v0, x17 + +// #define prev2 cur +// const uint8_t * restrict next2 = parity ? prev : next; + cmp w7, #0 + csel x17, x2, x4, ne + + // We want all the V registers - save all the ones we must + PUSH_VREGS + + // Some rearrangement of initial values for nice layout of refs in regs + mov w10, w6 // w10 = loop count + neg w9, w5 // w9 = mref + lsl w8, w9, #1 // w8 = mref2 + add w7, w9, w9, lsl #1 // w7 = mref3 + lsl w6, w9, #2 // w6 = mref4 + mov w11, w5 // w11 = pref + lsl w12, w5, #1 // w12 = pref2 + add w13, w5, w5, lsl #1 // w13 = pref3 + lsl w14, w5, #2 // w14 = pref4 + add w15, w5, w5, lsl #2 // w15 = pref5 + add w16, w14, w12 // w16 = pref6 + + lsl w5, w1, #1 // w5 = d_stride * 2 + +// for (x = 0; x < w; x++) { +// int diff0, diff2; +// int d0, d2; +// int temporal_diff0, temporal_diff2; +// +// int i1, i2; +// int j1, j2; +// int p6, p5, p4, p3, p2, p1, c0, m1, m2, m3, m4; + +10: +// c0 = prev2[0] + next2[0]; // c0 = v20, v21 +// d0 = c0 >> 1; // d0 = v10 +// temporal_diff0 = FFABS(prev2[0] - next2[0]); // td0 = v11 + ldr q31, [x3] + ldr q21, [x17] + uhadd v10.16b, v31.16b, v21.16b + uabd v11.16b, v31.16b, v21.16b + uaddl v20.8h, v21.8b, v31.8b + uaddl2 v21.8h, v21.16b, v31.16b + + ldr q31, [x3, w6, sxtw] + ldr q23, [x17, w6, sxtw] + +// i1 = coef_hf[0] * c0; // i1 = v2-v5 + UMULL4K v2, v3, v4, v5, v20, v21, v0.h[2] + + ldr q30, [x3, w14, sxtw] + ldr q25, [x17, w14, sxtw] + +// m4 = prev2[mrefs4] + next2[mrefs4]; // m4 = v22,v23 + uaddl v22.8h, v23.8b, v31.8b + uaddl2 v23.8h, v23.16b, v31.16b + +// p4 = prev2[prefs4] + next2[prefs4]; // p4 = v24,v25, (p4 >> 1) = v12 + uhadd v12.16b, v25.16b, v30.16b + uaddl v24.8h, v25.8b, v30.8b + uaddl2 v25.8h, v25.16b, v30.16b + +// j1 = -coef_hf[1] * (c0 + p4); // j1 = v6-v9 (-c0:v20,v21) + add v20.8h, v20.8h, v24.8h + add v21.8h, v21.8h, v25.8h + SMULL4K v6, v7, v8, v9, v20, v21, v0.h[5] + +// m3 = cur[mrefs3]; // m3 = v20 + ldr q20, [x3, w7, sxtw] + +// p3 = cur[prefs3]; // p3 = v21 + ldr q21, [x3, w13, sxtw] + +// i1 += coef_hf[2] * (m4 + p4); // (-m4:v22,v23) (-p4:v24,v25) + add v22.8h, v22.8h, v24.8h + add v23.8h, v23.8h, v25.8h + UMLAL4K v2, v3, v4, v5, v22, v23, v0.h[4] + + ldr q29, [x3, w8, sxtw] + ldr q23, [x17, w8, sxtw] + +// i1 -= coef_lf[1] * 4 * (m3 + p3); // - + uaddl v30.8h, v20.8b, v21.8b + uaddl2 v31.8h, v20.16b, v21.16b + + ldr q28, [x3, w16, sxtw] + ldr q25, [x17, w16, sxtw] + + UMLSL4K v2, v3, v4, v5, v30, v31, v0.h[1] + +// m2 = prev2[mrefs2] + next2[mrefs2]; // m2 = v22,v23, (m2 >> 1) = v13 + uhadd v13.16b, v23.16b, v29.16b + uaddl v22.8h, v23.8b, v29.8b + uaddl2 v23.8h, v23.16b, v29.16b + + ldr q31, [x3, w12, sxtw] + ldr q27, [x17, w12, sxtw] + +// p6 = prev2[prefs6] + next2[prefs6]; // p6 = v24,v25 + uaddl v24.8h, v25.8b, v28.8b + uaddl2 v25.8h, v25.16b, v28.16b + +// j1 += coef_hf[2] * (m2 + p6); // (-p6:v24,v25) + add v24.8h, v24.8h, v22.8h + add v25.8h, v25.8h, v23.8h + UMLAL4K v6, v7, v8, v9, v24, v25, v0.h[4] + +// m1 = cur[mrefs]; // m1 = v24 + ldr q24, [x3, w9, sxtw] + +// p5 = cur[prefs5]; // p5 = v25 + ldr q25, [x3, w15, sxtw] + +// p2 = prev2[prefs2] + next2[prefs2]; // p2 = v26, v27 +// temporal_diff2 = FFABS(prev2[prefs2] - next2[prefs2]); // td2 = v14 +// d2 = p2 >> 1; // d2 = v15 + uabd v14.16b, v31.16b, v27.16b + uhadd v15.16b, v31.16b, v27.16b + uaddl v26.8h, v27.8b, v31.8b + uaddl2 v27.8h, v27.16b, v31.16b + +// j1 += coef_hf[0] * p2; // - + UMLAL4K v6, v7, v8, v9, v26, v27, v0.h[2] + +// i1 -= coef_hf[1] * (m2 + p2); // (-m2:v22,v23*) (-p2:v26*,v27*) + add v22.8h, v22.8h, v26.8h + add v23.8h, v23.8h, v27.8h + UMLSL4K v2, v3, v4, v5, v22, v23, v0.h[3] + +// p1 = cur[prefs]; // p1 = v22 + ldr q22, [x3, w11, sxtw] + +// j1 -= coef_lf[1] * 4 * (m1 + p5); // - + uaddl v26.8h, v24.8b, v25.8b + uaddl2 v27.8h, v24.16b, v25.16b + UMLSL4K v6, v7, v8, v9, v26, v27, v0.h[1] + +// j2 = (coef_sp[0] * (p1 + p3) - coef_sp[1] * (m1 + p5)) >> 13; // (-p5:v25*) j2=v16 + uaddl v18.8h, v22.8b, v21.8b + uaddl2 v19.8h, v22.16b, v21.16b + UMULL4K v28, v29, v30, v31, v18, v19, v0.h[6] + + uaddl v18.8h, v24.8b, v25.8b + uaddl2 v19.8h, v24.16b, v25.16b + UMLSL4K v28, v29, v30, v31, v18, v19, v0.h[7] + + SQSHRUNN v16, v28, v29, v30, v31, 13 + +// i2 = (coef_sp[0] * (m1 + p1) - coef_sp[1] * (m3 + p3)) >> 13; // (-m3:v20*) i2=v17 + uaddl v18.8h, v22.8b, v24.8b + uaddl2 v19.8h, v22.16b, v24.16b + UMULL4K v28, v29, v30, v31, v18, v19, v0.h[6] + + uaddl v18.8h, v20.8b, v21.8b + uaddl2 v19.8h, v20.16b, v21.16b + UMLSL4K v28, v29, v30, v31, v18, v19, v0.h[7] + + SQSHRUNN v17, v28, v29, v30, v31, 13 + +// i1 += coef_lf[0] * 4 * (m1 + p1); // p1 = v22, m1 = v24 + uaddl v26.8h, v24.8b, v22.8b + uaddl2 v27.8h, v24.16b, v22.16b + UMLAL4K v2, v3, v4, v5, v26, v27, v0.h[0] + + ldr q31, [x2, w9, sxtw] + ldr q29, [x4, w9, sxtw] + +// j1 += coef_lf[0] * 4 * (p1 + p3); // p1 = v22, p3 = v21 + uaddl v26.8h, v21.8b, v22.8b + uaddl2 v27.8h, v21.16b, v22.16b + UMLAL4K v6, v7, v8, v9, v26, v27, v0.h[0] + + ldr q30, [x2, w11, sxtw] + ldr q28, [x4, w11, sxtw] + +// i1 >>= 15; // i1 = v2, -v3, -v4*, -v5* + SQSHRUNN v2, v2, v3, v4, v5, 15 + +// j1 >>= 15; // j1 = v3, -v6*, -v7*, -v8*, -v9* + SQSHRUNN v3, v6, v7, v8, v9, 15 + +// { +// int t1 =(FFABS(prev[mrefs] - m1) + FFABS(prev[prefs] - p1)) >> 1; +// int t2 =(FFABS(next[mrefs] - m1) + FFABS(next[prefs] - p1)) >> 1; + uabd v30.16b, v22.16b, v30.16b + uabd v31.16b, v24.16b, v31.16b + uabd v28.16b, v22.16b, v28.16b + uabd v29.16b, v24.16b, v29.16b + uhadd v31.16b, v31.16b, v30.16b + uhadd v29.16b, v29.16b, v28.16b + + ldr q27, [x2, w13, sxtw] + ldr q26, [x4, w13, sxtw] + +// diff0 = FFMAX3(temporal_diff0 >> 1, t1, t2); // diff0=v18 + ushr v18.16b, v11.16b, #1 + umax v18.16b, v18.16b, v31.16b + umax v18.16b, v18.16b, v29.16b +// } // v28, v30 preserved for next block +// { // tdiff2 = v14 +// int t1 =(FFABS(prev[prefs] - p1) + FFABS(prev[prefs3] - p3)) >> 1; +// int t2 =(FFABS(next[prefs] - p1) + FFABS(next[prefs3] - p3)) >> 1; + uabd v31.16b, v21.16b, v27.16b + uabd v29.16b, v21.16b, v26.16b + uhadd v31.16b, v31.16b, v30.16b + uhadd v29.16b, v29.16b, v28.16b + +// diff2 = FFMAX3(temporal_diff2 >> 1, t1, t2); // diff2=v19 + ushr v19.16b, v14.16b, #1 + umax v19.16b, v19.16b, v31.16b + umax v19.16b, v19.16b, v29.16b +// } + + // diff0 = v18, (m2 >> 1) = v13, m1 = v24, d0 = v10, p1 = v22, d2 = v15 + SPAT_CHECK v18, v13, v24, v10, v22, v15, v31, v30, v29, v28 + + // diff2 = v19, d0 = v10, p1 = v22, d2 = v15, p3 = v21, (p4 >> 1) = v12 + SPAT_CHECK v19, v10, v22, v15, v21, v12, v31, v30, v29, v28 + + // j1 = v3, j2 = v16, p1 = v22, d2 = v15, p3 = v21, td2 = v14, diff2 = v19 + INTERPOL v3, v3, v16, v22, v15, v21, v14, v19, v31, v30, v29 + +// dst[d_stride * 2] = av_clip_uint8(interpol); + str q3, [x0, w5, sxtw] + +// dst[d_stride] = p1; + str q22, [x0, w1, sxtw] + + // i1 = v2, i2 = v17, m1 = v24, d0 = v10, p1 = v22, td2 = v11, diff2 = v18 + INTERPOL v2, v2, v17, v24, v10, v22, v11, v18, v31, v30, v29 + +// dst[0] = av_clip_uint8(interpol); + str q2, [x0], #16 +// } +// +// dst++; +// cur++; +// prev++; +// prev2++; +// next++; +// } + subs w10, w10, #16 + add x2, x2, #16 + add x3, x3, #16 + add x4, x4, #16 + add x17, x17, #16 + bgt 10b + + POP_VREGS +99: + ret +endfunc + +// =========================================================================== +// +// void filter_line( +// void *dst1, // x0 +// void *prev1, // x1 +// void *cur1, // x2 +// void *next1, // x3 +// int w, // w4 +// int prefs, // w5 +// int mrefs, // w6 +// int prefs2, // w7 +// int mrefs2, // [sp, #0] +// int prefs3, // [sp, #SP_INT] +// int mrefs3, // [sp, #SP_INT*2] +// int prefs4, // [sp, #SP_INT*3] +// int mrefs4, // [sp, #SP_INT*4] +// int parity, // [sp, #SP_INT*5] +// int clip_max) // [sp, #SP_INT*6] + +function ff_bwdif_filter_line_neon, export=1 + // Sanity check w + cmp w4, #0 + ble 99f + + // Rearrange regs to be the same as line3 for ease of debug! + mov w10, w4 // w10 = loop count + mov w9, w6 // w9 = mref + mov w12, w7 // w12 = pref2 + mov w11, w5 // w11 = pref + ldr w8, [sp, #0] // w8 = mref2 + ldr w7, [sp, #SP_INT*2] // w7 = mref3 + ldr w6, [sp, #SP_INT*4] // w6 = mref4 + ldr w13, [sp, #SP_INT] // w13 = pref3 + ldr w14, [sp, #SP_INT*3] // w14 = pref4 + + mov x4, x3 + mov x3, x2 + mov x2, x1 + + LDR_COEFFS v0, x17 + +// #define prev2 cur +// const uint8_t * restrict next2 = parity ? prev : next; + ldr w17, [sp, #SP_INT*5] // parity + cmp w17, #0 + csel x17, x2, x4, ne + + PUSH_VREGS + +// for (x = 0; x < w; x++) { +// int diff0, diff2; +// int d0, d2; +// int temporal_diff0, temporal_diff2; +// +// int i1, i2; +// int j1, j2; +// int p6, p5, p4, p3, p2, p1, c0, m1, m2, m3, m4; + +10: +// c0 = prev2[0] + next2[0]; // c0 = v20, v21 +// d0 = c0 >> 1; // d0 = v10 +// temporal_diff0 = FFABS(prev2[0] - next2[0]); // td0 = v11 + ldr q31, [x3] + ldr q21, [x17] + uhadd v10.16b, v31.16b, v21.16b + uabd v11.16b, v31.16b, v21.16b + uaddl v20.8h, v21.8b, v31.8b + uaddl2 v21.8h, v21.16b, v31.16b + + ldr q31, [x3, w6, sxtw] + ldr q23, [x17, w6, sxtw] + +// i1 = coef_hf[0] * c0; // i1 = v2-v5 + UMULL4K v2, v3, v4, v5, v20, v21, v0.h[2] + + ldr q30, [x3, w14, sxtw] + ldr q25, [x17, w14, sxtw] + +// m4 = prev2[mrefs4] + next2[mrefs4]; // m4 = v22,v23 + uaddl v22.8h, v23.8b, v31.8b + uaddl2 v23.8h, v23.16b, v31.16b + +// p4 = prev2[prefs4] + next2[prefs4]; // p4 = v24,v25, (p4 >> 1) = v12 + uhadd v12.16b, v25.16b, v30.16b + uaddl v24.8h, v25.8b, v30.8b + uaddl2 v25.8h, v25.16b, v30.16b + +// m3 = cur[mrefs3]; // m3 = v20 + ldr q20, [x3, w7, sxtw] + +// p3 = cur[prefs3]; // p3 = v21 + ldr q21, [x3, w13, sxtw] + +// i1 += coef_hf[2] * (m4 + p4); // (-m4:v22,v23) (-p4:v24,v25) + add v22.8h, v22.8h, v24.8h + add v23.8h, v23.8h, v25.8h + UMLAL4K v2, v3, v4, v5, v22, v23, v0.h[4] + + ldr q29, [x3, w8, sxtw] + ldr q23, [x17, w8, sxtw] + +// i1 -= coef_lf[1] * 4 * (m3 + p3); // - + uaddl v30.8h, v20.8b, v21.8b + uaddl2 v31.8h, v20.16b, v21.16b + + UMLSL4K v2, v3, v4, v5, v30, v31, v0.h[1] + + ldr q31, [x3, w12, sxtw] + ldr q27, [x17, w12, sxtw] + +// m2 = prev2[mrefs2] + next2[mrefs2]; // m2 = v22,v23, (m2 >> 1) = v13 + uhadd v13.16b, v23.16b, v29.16b + uaddl v22.8h, v23.8b, v29.8b + uaddl2 v23.8h, v23.16b, v29.16b + +// m1 = cur[mrefs]; // m1 = v24 + ldr q24, [x3, w9, sxtw] + +// p2 = prev2[prefs2] + next2[prefs2]; // p2 = v26, v27 +// temporal_diff2 = FFABS(prev2[prefs2] - next2[prefs2]); // td2 = v14 +// d2 = p2 >> 1; // d2 = v15 + uabd v14.16b, v31.16b, v27.16b + uhadd v15.16b, v31.16b, v27.16b + uaddl v26.8h, v27.8b, v31.8b + uaddl2 v27.8h, v27.16b, v31.16b + +// i1 -= coef_hf[1] * (m2 + p2); // (-m2:v22,v23*) (-p2:v26*,v27*) + add v22.8h, v22.8h, v26.8h + add v23.8h, v23.8h, v27.8h + UMLSL4K v2, v3, v4, v5, v22, v23, v0.h[3] + +// p1 = cur[prefs]; // p1 = v22 + ldr q22, [x3, w11, sxtw] + +// i2 = (coef_sp[0] * (m1 + p1) - coef_sp[1] * (m3 + p3)) >> 13; // (-m3:v20*) i2=v17 + uaddl v18.8h, v22.8b, v24.8b + uaddl2 v19.8h, v22.16b, v24.16b + UMULL4K v28, v29, v30, v31, v18, v19, v0.h[6] + + uaddl v18.8h, v20.8b, v21.8b + uaddl2 v19.8h, v20.16b, v21.16b + UMLSL4K v28, v29, v30, v31, v18, v19, v0.h[7] + + SQSHRUNN v17, v28, v29, v30, v31, 13 + +// i1 += coef_lf[0] * 4 * (m1 + p1); // p1 = v22, m1 = v24 + uaddl v26.8h, v24.8b, v22.8b + uaddl2 v27.8h, v24.16b, v22.16b + UMLAL4K v2, v3, v4, v5, v26, v27, v0.h[0] + + ldr q31, [x2, w9, sxtw] + ldr q29, [x4, w9, sxtw] + + ldr q30, [x2, w11, sxtw] + ldr q28, [x4, w11, sxtw] + +// i1 >>= 15; // i1 = v2, -v3, -v4*, -v5* + SQSHRUNN v2, v2, v3, v4, v5, 15 + +// { +// int t1 =(FFABS(prev[mrefs] - m1) + FFABS(prev[prefs] - p1)) >> 1; +// int t2 =(FFABS(next[mrefs] - m1) + FFABS(next[prefs] - p1)) >> 1; + uabd v30.16b, v22.16b, v30.16b + uabd v31.16b, v24.16b, v31.16b + uabd v28.16b, v22.16b, v28.16b + uabd v29.16b, v24.16b, v29.16b + uhadd v31.16b, v31.16b, v30.16b + uhadd v29.16b, v29.16b, v28.16b + +// diff0 = FFMAX3(temporal_diff0 >> 1, t1, t2); // diff0=v18 + ushr v18.16b, v11.16b, #1 + umax v18.16b, v18.16b, v31.16b + umax v18.16b, v18.16b, v29.16b + + // diff0 = v18, (m2 >> 1) = v13, m1 = v24, d0 = v10, p1 = v22, d2 = v15 + SPAT_CHECK v18, v13, v24, v10, v22, v15, v31, v30, v29, v28 + + // i1 = v2, i2 = v17, m1 = v24, d0 = v10, p1 = v22, td2 = v11, diff2 = v18 + INTERPOL v2, v2, v17, v24, v10, v22, v11, v18, v31, v30, v29 + +// dst[0] = av_clip_uint8(interpol); + str q2, [x0], #16 +// } +// +// dst++; +// cur++; +// prev++; +// prev2++; +// next++; +// } + + subs w10, w10, #16 + add x2, x2, #16 + add x3, x3, #16 + add x4, x4, #16 + add x17, x17, #16 + bgt 10b + + POP_VREGS +99: + ret +endfunc + +// ============================================================================ +// +// void ff_bwdif_filter_edge_neon( +// void *dst1, // x0 +// void *prev1, // x1 +// void *cur1, // x2 +// void *next1, // x3 +// int w, // w4 +// int prefs, // w5 +// int mrefs, // w6 +// int prefs2, // w7 +// int mrefs2, // [sp, #0] +// int parity, // [sp, #SP_INT] +// int clip_max, // [sp, #SP_INT*2] unused +// int spat); // [sp, #SP_INT*3] + +function ff_bwdif_filter_edge_neon, export=1 + // Sanity check w + cmp w4, #0 + ble 99f + +// #define prev2 cur +// const uint8_t * restrict next2 = parity ? prev : next; + + ldr w8, [sp, #0] // mrefs2 + + ldr w17, [sp, #SP_INT] // parity + ldr w16, [sp, #SP_INT*3] // spat + cmp w17, #0 + csel x17, x1, x3, ne + +// for (x = 0; x < w; x++) { + +10: +// int m1 = cur[mrefs]; +// int d = (prev2[0] + next2[0]) >> 1; +// int p1 = cur[prefs]; +// int temporal_diff0 = FFABS(prev2[0] - next2[0]); +// int temporal_diff1 =(FFABS(prev[mrefs] - m1) + FFABS(prev[prefs] - p1)) >> 1; +// int temporal_diff2 =(FFABS(next[mrefs] - m1) + FFABS(next[prefs] - p1)) >> 1; +// int diff = FFMAX3(temporal_diff0 >> 1, temporal_diff1, temporal_diff2); + ldr q31, [x2] + ldr q21, [x17] + uhadd v16.16b, v31.16b, v21.16b // d0 = v16 + uabd v17.16b, v31.16b, v21.16b // td0 = v17 + ldr q24, [x2, w6, sxtw] // m1 = v24 + ldr q22, [x2, w5, sxtw] // p1 = v22 + + ldr q0, [x1, w6, sxtw] // prev[mrefs] + ldr q2, [x1, w5, sxtw] // prev[prefs] + ldr q1, [x3, w6, sxtw] // next[mrefs] + ldr q3, [x3, w5, sxtw] // next[prefs] + + ushr v29.16b, v17.16b, #1 + + uabd v31.16b, v0.16b, v24.16b + uabd v30.16b, v2.16b, v22.16b + uhadd v0.16b, v31.16b, v30.16b // td1 = q0 + + uabd v31.16b, v1.16b, v24.16b + uabd v30.16b, v3.16b, v22.16b + uhadd v1.16b, v31.16b, v30.16b // td2 = q1 + + umax v0.16b, v0.16b, v29.16b + umax v0.16b, v0.16b, v1.16b // diff = v0 + +// if (spat) { +// SPAT_CHECK() +// } +// i0 = (m1 + p1) >> 1; + cbz w16, 1f + + ldr q31, [x2, w8, sxtw] + ldr q18, [x17, w8, sxtw] + ldr q30, [x2, w7, sxtw] + ldr q19, [x17, w7, sxtw] + uhadd v18.16b, v18.16b, v31.16b + uhadd v19.16b, v19.16b, v30.16b + + SPAT_CHECK v0, v18, v24, v16, v22, v19, v31, v30, v29, v28 + +1: + uhadd v2.16b, v22.16b, v24.16b + + // i0 = v2, s0 = v2, d0 = v16, diff = v0, t0 = v31, t1 = v30 + DIFF_CLIP v2, v2, v16, v0, v31, v30 + +// dst[0] = av_clip(interpol, 0, clip_max); + str q2, [x0], #16 + +// dst++; +// cur++; +// } + subs w4, w4, #16 + add x1, x1, #16 + add x2, x2, #16 + add x3, x3, #16 + add x17, x17, #16 + bgt 10b + +99: + ret +endfunc + +// ============================================================================ +// +// void ff_bwdif_filter_intra_neon( +// void *dst1, // x0 +// void *cur1, // x1 +// int w, // w2 +// int prefs, // w3 +// int mrefs, // w4 +// int prefs3, // w5 +// int mrefs3, // w6 +// int parity, // w7 unused +// int clip_max) // [sp, #0] unused + +function ff_bwdif_filter_intra_neon, export=1 + cmp w2, #0 + ble 99f + + LDR_COEFFS v0, x17 + +// for (x = 0; x < w; x++) { +10: + +// interpol = (coef_sp[0] * (cur[mrefs] + cur[prefs]) - coef_sp[1] * (cur[mrefs3] + cur[prefs3])) >> 13; + ldr q31, [x1, w4, sxtw] + ldr q30, [x1, w3, sxtw] + ldr q29, [x1, w6, sxtw] + ldr q28, [x1, w5, sxtw] + + uaddl v20.8h, v31.8b, v30.8b + uaddl2 v21.8h, v31.16b, v30.16b + + UMULL4K v2, v3, v4, v5, v20, v21, v0.h[6] + + uaddl v20.8h, v29.8b, v28.8b + uaddl2 v21.8h, v29.16b, v28.16b + + UMLSL4K v2, v3, v4, v5, v20, v21, v0.h[7] + +// dst[0] = av_clip(interpol, 0, clip_max); + SQSHRUNN v2, v2, v3, v4, v5, 13 + str q2, [x0], #16 + +// dst++; +// cur++; +// } + + subs w2, w2, #16 + add x1, x1, #16 + bgt 10b + +99: + ret +endfunc diff --git a/libavfilter/aarch64/vf_nlmeans_neon.S b/libavfilter/aarch64/vf_nlmeans_neon.S index e69b0dd9231..a788cffd856 100644 --- a/libavfilter/aarch64/vf_nlmeans_neon.S +++ b/libavfilter/aarch64/vf_nlmeans_neon.S @@ -22,52 +22,52 @@ // acc_sum_store(ABCD) = {X+A, X+A+B, X+A+B+C, X+A+B+C+D} .macro acc_sum_store x, xb - dup v24.4S, v24.S[3] // ...X -> XXXX - ext v25.16B, v26.16B, \xb, #12 // ext(0000,ABCD,12)=0ABC - add v24.4S, v24.4S, \x // XXXX+ABCD={X+A,X+B,X+C,X+D} - add v24.4S, v24.4S, v25.4S // {X+A,X+B+A,X+C+B,X+D+C} (+0ABC) - ext v25.16B, v26.16B, v25.16B, #12 // ext(0000,0ABC,12)=00AB - add v24.4S, v24.4S, v25.4S // {X+A,X+B+A,X+C+B+A,X+D+C+B} (+00AB) - ext v25.16B, v26.16B, v25.16B, #12 // ext(0000,00AB,12)=000A - add v24.4S, v24.4S, v25.4S // {X+A,X+B+A,X+C+B+A,X+D+C+B+A} (+000A) - st1 {v24.4S}, [x0], #16 // write 4x32-bit final values + dup v24.4s, v24.s[3] // ...X -> XXXX + ext v25.16b, v26.16b, \xb, #12 // ext(0000,ABCD,12)=0ABC + add v24.4s, v24.4s, \x // XXXX+ABCD={X+A,X+B,X+C,X+D} + add v24.4s, v24.4s, v25.4s // {X+A,X+B+A,X+C+B,X+D+C} (+0ABC) + ext v25.16b, v26.16b, v25.16b, #12 // ext(0000,0ABC,12)=00AB + add v24.4s, v24.4s, v25.4s // {X+A,X+B+A,X+C+B+A,X+D+C+B} (+00AB) + ext v25.16b, v26.16b, v25.16b, #12 // ext(0000,00AB,12)=000A + add v24.4s, v24.4s, v25.4s // {X+A,X+B+A,X+C+B+A,X+D+C+B+A} (+000A) + st1 {v24.4s}, [x0], #16 // write 4x32-bit final values .endm function ff_compute_safe_ssd_integral_image_neon, export=1 - movi v26.4S, #0 // used as zero for the "rotations" in acc_sum_store - sub x3, x3, w6, UXTW // s1 padding (s1_linesize - w) - sub x5, x5, w6, UXTW // s2 padding (s2_linesize - w) - sub x9, x0, w1, UXTW #2 // dst_top - sub x1, x1, w6, UXTW // dst padding (dst_linesize_32 - w) + movi v26.4s, #0 // used as zero for the "rotations" in acc_sum_store + sub x3, x3, w6, uxtw // s1 padding (s1_linesize - w) + sub x5, x5, w6, uxtw // s2 padding (s2_linesize - w) + sub x9, x0, w1, uxtw #2 // dst_top + sub x1, x1, w6, uxtw // dst padding (dst_linesize_32 - w) lsl x1, x1, #2 // dst padding expressed in bytes 1: mov w10, w6 // width copy for each line sub x0, x0, #16 // beginning of the dst line minus 4 sums sub x8, x9, #4 // dst_top-1 - ld1 {v24.4S}, [x0], #16 // load ...X (contextual last sums) -2: ld1 {v0.16B}, [x2], #16 // s1[x + 0..15] - ld1 {v1.16B}, [x4], #16 // s2[x + 0..15] - ld1 {v16.4S,v17.4S}, [x8], #32 // dst_top[x + 0..7 - 1] - usubl v2.8H, v0.8B, v1.8B // d[x + 0..7] = s1[x + 0..7] - s2[x + 0..7] - usubl2 v3.8H, v0.16B, v1.16B // d[x + 8..15] = s1[x + 8..15] - s2[x + 8..15] - ld1 {v18.4S,v19.4S}, [x8], #32 // dst_top[x + 8..15 - 1] - smull v4.4S, v2.4H, v2.4H // d[x + 0..3]^2 - smull2 v5.4S, v2.8H, v2.8H // d[x + 4..7]^2 - ld1 {v20.4S,v21.4S}, [x9], #32 // dst_top[x + 0..7] - smull v6.4S, v3.4H, v3.4H // d[x + 8..11]^2 - smull2 v7.4S, v3.8H, v3.8H // d[x + 12..15]^2 - ld1 {v22.4S,v23.4S}, [x9], #32 // dst_top[x + 8..15] - sub v0.4S, v20.4S, v16.4S // dst_top[x + 0..3] - dst_top[x + 0..3 - 1] - sub v1.4S, v21.4S, v17.4S // dst_top[x + 4..7] - dst_top[x + 4..7 - 1] - add v0.4S, v0.4S, v4.4S // + d[x + 0..3]^2 - add v1.4S, v1.4S, v5.4S // + d[x + 4..7]^2 - sub v2.4S, v22.4S, v18.4S // dst_top[x + 8..11] - dst_top[x + 8..11 - 1] - sub v3.4S, v23.4S, v19.4S // dst_top[x + 12..15] - dst_top[x + 12..15 - 1] - add v2.4S, v2.4S, v6.4S // + d[x + 8..11]^2 - add v3.4S, v3.4S, v7.4S // + d[x + 12..15]^2 - acc_sum_store v0.4S, v0.16B // accumulate and store dst[ 0..3] - acc_sum_store v1.4S, v1.16B // accumulate and store dst[ 4..7] - acc_sum_store v2.4S, v2.16B // accumulate and store dst[ 8..11] - acc_sum_store v3.4S, v3.16B // accumulate and store dst[12..15] + ld1 {v24.4s}, [x0], #16 // load ...X (contextual last sums) +2: ld1 {v0.16b}, [x2], #16 // s1[x + 0..15] + ld1 {v1.16b}, [x4], #16 // s2[x + 0..15] + ld1 {v16.4s,v17.4s}, [x8], #32 // dst_top[x + 0..7 - 1] + usubl v2.8h, v0.8b, v1.8b // d[x + 0..7] = s1[x + 0..7] - s2[x + 0..7] + usubl2 v3.8h, v0.16b, v1.16b // d[x + 8..15] = s1[x + 8..15] - s2[x + 8..15] + ld1 {v18.4s,v19.4s}, [x8], #32 // dst_top[x + 8..15 - 1] + smull v4.4s, v2.4h, v2.4h // d[x + 0..3]^2 + smull2 v5.4s, v2.8h, v2.8h // d[x + 4..7]^2 + ld1 {v20.4s,v21.4s}, [x9], #32 // dst_top[x + 0..7] + smull v6.4s, v3.4h, v3.4h // d[x + 8..11]^2 + smull2 v7.4s, v3.8h, v3.8h // d[x + 12..15]^2 + ld1 {v22.4s,v23.4s}, [x9], #32 // dst_top[x + 8..15] + sub v0.4s, v20.4s, v16.4s // dst_top[x + 0..3] - dst_top[x + 0..3 - 1] + sub v1.4s, v21.4s, v17.4s // dst_top[x + 4..7] - dst_top[x + 4..7 - 1] + add v0.4s, v0.4s, v4.4s // + d[x + 0..3]^2 + add v1.4s, v1.4s, v5.4s // + d[x + 4..7]^2 + sub v2.4s, v22.4s, v18.4s // dst_top[x + 8..11] - dst_top[x + 8..11 - 1] + sub v3.4s, v23.4s, v19.4s // dst_top[x + 12..15] - dst_top[x + 12..15 - 1] + add v2.4s, v2.4s, v6.4s // + d[x + 8..11]^2 + add v3.4s, v3.4s, v7.4s // + d[x + 12..15]^2 + acc_sum_store v0.4s, v0.16b // accumulate and store dst[ 0..3] + acc_sum_store v1.4s, v1.16b // accumulate and store dst[ 4..7] + acc_sum_store v2.4s, v2.16b // accumulate and store dst[ 8..11] + acc_sum_store v3.4s, v3.16b // accumulate and store dst[12..15] subs w10, w10, #16 // width dec b.ne 2b // loop til next line add x2, x2, x3 // skip to next line (s1) diff --git a/libavfilter/adynamicequalizer_template.c b/libavfilter/adynamicequalizer_template.c new file mode 100644 index 00000000000..4eb2489cd61 --- /dev/null +++ b/libavfilter/adynamicequalizer_template.c @@ -0,0 +1,438 @@ +/* + * 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 + */ + +#undef ftype +#undef SQRT +#undef TAN +#undef ONE +#undef TWO +#undef ZERO +#undef FMAX +#undef FMIN +#undef CLIP +#undef SAMPLE_FORMAT +#undef FABS +#undef FLOG +#undef FEXP +#undef FLOG2 +#undef FLOG10 +#undef FEXP2 +#undef FEXP10 +#undef EPSILON +#if DEPTH == 32 +#define SAMPLE_FORMAT float +#define SQRT sqrtf +#define TAN tanf +#define ONE 1.f +#define TWO 2.f +#define ZERO 0.f +#define FMIN fminf +#define FMAX fmaxf +#define CLIP av_clipf +#define FABS fabsf +#define FLOG logf +#define FEXP expf +#define FLOG2 log2f +#define FLOG10 log10f +#define FEXP2 exp2f +#define FEXP10 ff_exp10f +#define EPSILON (1.f / (1 << 23)) +#define ftype float +#else +#define SAMPLE_FORMAT double +#define SQRT sqrt +#define TAN tan +#define ONE 1.0 +#define TWO 2.0 +#define ZERO 0.0 +#define FMIN fmin +#define FMAX fmax +#define CLIP av_clipd +#define FABS fabs +#define FLOG log +#define FEXP exp +#define FLOG2 log2 +#define FLOG10 log10 +#define FEXP2 exp2 +#define FEXP10 ff_exp10 +#define EPSILON (1.0 / (1LL << 53)) +#define ftype double +#endif + +#define LIN2LOG(x) (20.0 * FLOG10(x)) +#define LOG2LIN(x) (FEXP10(x / 20.0)) + +#define fn3(a,b) a##_##b +#define fn2(a,b) fn3(a,b) +#define fn(a) fn2(a, SAMPLE_FORMAT) + +static ftype fn(get_svf)(ftype in, const ftype *m, const ftype *a, ftype *b) +{ + const ftype v0 = in; + const ftype v3 = v0 - b[1]; + const ftype v1 = a[0] * b[0] + a[1] * v3; + const ftype v2 = b[1] + a[1] * b[0] + a[2] * v3; + + b[0] = TWO * v1 - b[0]; + b[1] = TWO * v2 - b[1]; + + return m[0] * v0 + m[1] * v1 + m[2] * v2; +} + +static int fn(filter_prepare)(AVFilterContext *ctx) +{ + AudioDynamicEqualizerContext *s = ctx->priv; + const ftype sample_rate = ctx->inputs[0]->sample_rate; + const ftype dfrequency = FMIN(s->dfrequency, sample_rate * 0.5); + const ftype dg = TAN(M_PI * dfrequency / sample_rate); + const ftype dqfactor = s->dqfactor; + const int dftype = s->dftype; + ftype *da = fn(s->da); + ftype *dm = fn(s->dm); + ftype k; + + s->threshold_log = LIN2LOG(s->threshold); + s->dattack_coef = get_coef(s->dattack, sample_rate); + s->drelease_coef = get_coef(s->drelease, sample_rate); + s->gattack_coef = s->dattack_coef * 0.25; + s->grelease_coef = s->drelease_coef * 0.25; + + switch (dftype) { + case 0: + k = ONE / dqfactor; + + da[0] = ONE / (ONE + dg * (dg + k)); + da[1] = dg * da[0]; + da[2] = dg * da[1]; + + dm[0] = ZERO; + dm[1] = k; + dm[2] = ZERO; + break; + case 1: + k = ONE / dqfactor; + + da[0] = ONE / (ONE + dg * (dg + k)); + da[1] = dg * da[0]; + da[2] = dg * da[1]; + + dm[0] = ZERO; + dm[1] = ZERO; + dm[2] = ONE; + break; + case 2: + k = ONE / dqfactor; + + da[0] = ONE / (ONE + dg * (dg + k)); + da[1] = dg * da[0]; + da[2] = dg * da[1]; + + dm[0] = ZERO; + dm[1] = -k; + dm[2] = -ONE; + break; + case 3: + k = ONE / dqfactor; + + da[0] = ONE / (ONE + dg * (dg + k)); + da[1] = dg * da[0]; + da[2] = dg * da[1]; + + dm[0] = ONE; + dm[1] = -k; + dm[2] = -TWO; + break; + } + + return 0; +} + +#define PEAKS(empty_value,op,sample, psample)\ + if (!empty && psample == ss[front]) { \ + ss[front] = empty_value; \ + if (back != front) { \ + front--; \ + if (front < 0) \ + front = n - 1; \ + } \ + empty = front == back; \ + } \ + \ + if (!empty && sample op ss[front]) { \ + while (1) { \ + ss[front] = empty_value; \ + if (back == front) { \ + empty = 1; \ + break; \ + } \ + front--; \ + if (front < 0) \ + front = n - 1; \ + } \ + } \ + \ + while (!empty && sample op ss[back]) { \ + ss[back] = empty_value; \ + if (back == front) { \ + empty = 1; \ + break; \ + } \ + back++; \ + if (back >= n) \ + back = 0; \ + } \ + \ + if (!empty) { \ + back--; \ + if (back < 0) \ + back = n - 1; \ + } + +static void fn(queue_sample)(ChannelContext *cc, + const ftype x, + const int nb_samples) +{ + ftype *ss = cc->dqueue; + ftype *qq = cc->queue; + int front = cc->front; + int back = cc->back; + int empty, n, pos = cc->position; + ftype px = qq[pos]; + + fn(cc->sum) += x; + fn(cc->log_sum) += FLOG2(x); + if (cc->size >= nb_samples) { + fn(cc->sum) -= px; + fn(cc->log_sum) -= FLOG2(px); + } + + qq[pos] = x; + pos++; + if (pos >= nb_samples) + pos = 0; + cc->position = pos; + + if (cc->size < nb_samples) + cc->size++; + n = cc->size; + + empty = (front == back) && (ss[front] == ZERO); + PEAKS(ZERO, >, x, px) + + ss[back] = x; + + cc->front = front; + cc->back = back; +} + +static ftype fn(get_peak)(ChannelContext *cc, ftype *score) +{ + ftype s, *ss = cc->dqueue; + s = FEXP2(fn(cc->log_sum) / cc->size) / (fn(cc->sum) / cc->size); + *score = LIN2LOG(s); + return ss[cc->front]; +} + +static int fn(filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + AudioDynamicEqualizerContext *s = ctx->priv; + ThreadData *td = arg; + AVFrame *in = td->in; + AVFrame *out = td->out; + const ftype sample_rate = in->sample_rate; + const int isample_rate = in->sample_rate; + const ftype makeup = s->makeup; + const ftype ratio = s->ratio; + const ftype range = s->range; + const ftype tfrequency = FMIN(s->tfrequency, sample_rate * 0.5); + const int mode = s->mode; + const ftype power = (mode == CUT_BELOW || mode == CUT_ABOVE) ? -ONE : ONE; + const ftype grelease = s->grelease_coef; + const ftype gattack = s->gattack_coef; + const ftype drelease = s->drelease_coef; + const ftype dattack = s->dattack_coef; + const ftype tqfactor = s->tqfactor; + const ftype itqfactor = ONE / tqfactor; + const ftype fg = TAN(M_PI * tfrequency / sample_rate); + const int start = (in->ch_layout.nb_channels * jobnr) / nb_jobs; + const int end = (in->ch_layout.nb_channels * (jobnr+1)) / nb_jobs; + const int is_disabled = ctx->is_disabled; + const int detection = s->detection; + const int tftype = s->tftype; + const ftype *da = fn(s->da); + const ftype *dm = fn(s->dm); + + if (detection == DET_ON) { + for (int ch = start; ch < end; ch++) { + const ftype *src = (const ftype *)in->extended_data[ch]; + ChannelContext *cc = &s->cc[ch]; + ftype *tstate = fn(cc->tstate); + ftype new_threshold = ZERO; + + if (cc->detection != detection) { + cc->detection = detection; + fn(cc->new_threshold_log) = LIN2LOG(EPSILON); + } + + for (int n = 0; n < in->nb_samples; n++) { + ftype detect = FABS(fn(get_svf)(src[n], dm, da, tstate)); + new_threshold = FMAX(new_threshold, detect); + } + + fn(cc->new_threshold_log) = FMAX(fn(cc->new_threshold_log), LIN2LOG(new_threshold)); + } + } else if (detection == DET_ADAPTIVE) { + for (int ch = start; ch < end; ch++) { + const ftype *src = (const ftype *)in->extended_data[ch]; + ChannelContext *cc = &s->cc[ch]; + ftype *tstate = fn(cc->tstate); + ftype score, peak; + + for (int n = 0; n < in->nb_samples; n++) { + ftype detect = FMAX(FABS(fn(get_svf)(src[n], dm, da, tstate)), EPSILON); + fn(queue_sample)(cc, detect, isample_rate); + } + + peak = fn(get_peak)(cc, &score); + + if (score >= -3.5) { + fn(cc->threshold_log) = LIN2LOG(peak); + } else if (cc->detection == DET_UNSET) { + fn(cc->threshold_log) = s->threshold_log; + } + cc->detection = detection; + } + } else if (detection == DET_DISABLED) { + for (int ch = start; ch < end; ch++) { + ChannelContext *cc = &s->cc[ch]; + fn(cc->threshold_log) = s->threshold_log; + cc->detection = detection; + } + } else if (detection == DET_OFF) { + for (int ch = start; ch < end; ch++) { + ChannelContext *cc = &s->cc[ch]; + if (cc->detection == DET_ON) + fn(cc->threshold_log) = fn(cc->new_threshold_log); + else if (cc->detection == DET_UNSET) + fn(cc->threshold_log) = s->threshold_log; + cc->detection = detection; + } + } + + for (int ch = start; ch < end; ch++) { + const ftype *src = (const ftype *)in->extended_data[ch]; + ftype *dst = (ftype *)out->extended_data[ch]; + ChannelContext *cc = &s->cc[ch]; + const ftype threshold_log = fn(cc->threshold_log); + ftype *fa = fn(cc->fa), *fm = fn(cc->fm); + ftype *fstate = fn(cc->fstate); + ftype *dstate = fn(cc->dstate); + ftype detect = fn(cc->detect); + ftype lin_gain = fn(cc->lin_gain); + int init = cc->init; + + for (int n = 0; n < out->nb_samples; n++) { + ftype new_detect, new_lin_gain = ONE; + ftype f, v, listen, k, g, ld; + + listen = fn(get_svf)(src[n], dm, da, dstate); + if (mode > LISTEN) { + new_detect = FABS(listen); + f = (new_detect > detect) * dattack + (new_detect <= detect) * drelease; + detect = f * new_detect + (ONE - f) * detect; + } + + switch (mode) { + case LISTEN: + break; + case CUT_BELOW: + case BOOST_BELOW: + ld = LIN2LOG(detect); + if (ld < threshold_log) { + ftype new_log_gain = CLIP(makeup + (threshold_log - ld) * ratio, ZERO, range) * power; + new_lin_gain = LOG2LIN(new_log_gain); + } + break; + case CUT_ABOVE: + case BOOST_ABOVE: + ld = LIN2LOG(detect); + if (ld > threshold_log) { + ftype new_log_gain = CLIP(makeup + (ld - threshold_log) * ratio, ZERO, range) * power; + new_lin_gain = LOG2LIN(new_log_gain); + } + break; + } + + f = (new_lin_gain > lin_gain) * gattack + (new_lin_gain <= lin_gain) * grelease; + new_lin_gain = f * new_lin_gain + (ONE - f) * lin_gain; + + if (lin_gain != new_lin_gain || !init) { + init = 1; + lin_gain = new_lin_gain; + + switch (tftype) { + case 0: + k = itqfactor / lin_gain; + + fa[0] = ONE / (ONE + fg * (fg + k)); + fa[1] = fg * fa[0]; + fa[2] = fg * fa[1]; + + fm[0] = ONE; + fm[1] = k * (lin_gain * lin_gain - ONE); + fm[2] = ZERO; + break; + case 1: + k = itqfactor; + g = fg / SQRT(lin_gain); + + fa[0] = ONE / (ONE + g * (g + k)); + fa[1] = g * fa[0]; + fa[2] = g * fa[1]; + + fm[0] = ONE; + fm[1] = k * (lin_gain - ONE); + fm[2] = lin_gain * lin_gain - ONE; + break; + case 2: + k = itqfactor; + g = fg * SQRT(lin_gain); + + fa[0] = ONE / (ONE + g * (g + k)); + fa[1] = g * fa[0]; + fa[2] = g * fa[1]; + + fm[0] = lin_gain * lin_gain; + fm[1] = k * (ONE - lin_gain) * lin_gain; + fm[2] = ONE - lin_gain * lin_gain; + break; + } + } + + v = fn(get_svf)(src[n], fm, fa, fstate); + v = mode == LISTEN ? listen : v; + dst[n] = is_disabled ? src[n] : v; + } + + fn(cc->detect) = detect; + fn(cc->lin_gain) = lin_gain; + cc->init = 1; + } + + return 0; +} diff --git a/libavfilter/aeval.c b/libavfilter/aeval.c index 94d7690ef4d..b26d75f129a 100644 --- a/libavfilter/aeval.c +++ b/libavfilter/aeval.c @@ -33,6 +33,7 @@ #include "avfilter.h" #include "audio.h" #include "filters.h" +#include "formats.h" #include "internal.h" static const char * const var_names[] = { diff --git a/libavfilter/af_aap.c b/libavfilter/af_aap.c new file mode 100644 index 00000000000..85d905c1ab1 --- /dev/null +++ b/libavfilter/af_aap.c @@ -0,0 +1,332 @@ +/* + * 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/common.h" +#include "libavutil/float_dsp.h" +#include "libavutil/opt.h" + +#include "audio.h" +#include "avfilter.h" +#include "formats.h" +#include "filters.h" +#include "internal.h" + +enum OutModes { + IN_MODE, + DESIRED_MODE, + OUT_MODE, + NOISE_MODE, + ERROR_MODE, + NB_OMODES +}; + +typedef struct AudioAPContext { + const AVClass *class; + + int order; + int projection; + float mu; + float delta; + int output_mode; + int precision; + + int kernel_size; + AVFrame *offset; + AVFrame *delay; + AVFrame *coeffs; + AVFrame *e; + AVFrame *p; + AVFrame *x; + AVFrame *w; + AVFrame *dcoeffs; + AVFrame *tmp; + AVFrame *tmpm; + AVFrame *itmpm; + + void **tmpmp; + void **itmpmp; + + AVFrame *frame[2]; + + int (*filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + + AVFloatDSPContext *fdsp; +} AudioAPContext; + +#define OFFSET(x) offsetof(AudioAPContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define AT AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM + +static const AVOption aap_options[] = { + { "order", "set the filter order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=16}, 1, INT16_MAX, A }, + { "projection", "set the filter projection", OFFSET(projection), AV_OPT_TYPE_INT, {.i64=2}, 1, 256, A }, + { "mu", "set the filter mu", OFFSET(mu), AV_OPT_TYPE_FLOAT, {.dbl=0.0001},0,1, AT }, + { "delta", "set the filter delta", OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=0.001},0, 1, AT }, + { "out_mode", "set output mode", OFFSET(output_mode), AV_OPT_TYPE_INT, {.i64=OUT_MODE}, 0, NB_OMODES-1, AT, .unit = "mode" }, + { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64=IN_MODE}, 0, 0, AT, .unit = "mode" }, + { "d", "desired", 0, AV_OPT_TYPE_CONST, {.i64=DESIRED_MODE}, 0, 0, AT, .unit = "mode" }, + { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64=OUT_MODE}, 0, 0, AT, .unit = "mode" }, + { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_MODE}, 0, 0, AT, .unit = "mode" }, + { "e", "error", 0, AV_OPT_TYPE_CONST, {.i64=ERROR_MODE}, 0, 0, AT, .unit = "mode" }, + { "precision", "set processing precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, A, .unit = "precision" }, + { "auto", "set auto processing precision", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, .unit = "precision" }, + { "float", "set single-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, .unit = "precision" }, + { "double","set double-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, A, .unit = "precision" }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(aap); + +static int query_formats(AVFilterContext *ctx) +{ + AudioAPContext *s = ctx->priv; + static const enum AVSampleFormat sample_fmts[3][3] = { + { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE }, + { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, + { AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE }, + }; + int ret; + + if ((ret = ff_set_common_all_channel_counts(ctx)) < 0) + return ret; + + if ((ret = ff_set_common_formats_from_list(ctx, sample_fmts[s->precision])) < 0) + return ret; + + return ff_set_common_all_samplerates(ctx); +} + +static int activate(AVFilterContext *ctx) +{ + AudioAPContext *s = ctx->priv; + int i, ret, status; + int nb_samples; + int64_t pts; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); + + nb_samples = FFMIN(ff_inlink_queued_samples(ctx->inputs[0]), + ff_inlink_queued_samples(ctx->inputs[1])); + for (i = 0; i < ctx->nb_inputs && nb_samples > 0; i++) { + if (s->frame[i]) + continue; + + if (ff_inlink_check_available_samples(ctx->inputs[i], nb_samples) > 0) { + ret = ff_inlink_consume_samples(ctx->inputs[i], nb_samples, nb_samples, &s->frame[i]); + if (ret < 0) + return ret; + } + } + + if (s->frame[0] && s->frame[1]) { + AVFrame *out; + + out = ff_get_audio_buffer(ctx->outputs[0], s->frame[0]->nb_samples); + if (!out) { + av_frame_free(&s->frame[0]); + av_frame_free(&s->frame[1]); + return AVERROR(ENOMEM); + } + + ff_filter_execute(ctx, s->filter_channels, out, NULL, + FFMIN(ctx->outputs[0]->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx))); + + out->pts = s->frame[0]->pts; + out->duration = s->frame[0]->duration; + + av_frame_free(&s->frame[0]); + av_frame_free(&s->frame[1]); + + ret = ff_filter_frame(ctx->outputs[0], out); + if (ret < 0) + return ret; + } + + if (!nb_samples) { + for (i = 0; i < 2; i++) { + if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) { + ff_outlink_set_status(ctx->outputs[0], status, pts); + return 0; + } + } + } + + if (ff_outlink_frame_wanted(ctx->outputs[0])) { + for (i = 0; i < 2; i++) { + if (s->frame[i] || ff_inlink_queued_samples(ctx->inputs[i]) > 0) + continue; + ff_inlink_request_frame(ctx->inputs[i]); + return 0; + } + } + return 0; +} + +#define DEPTH 32 +#include "aap_template.c" + +#undef DEPTH +#define DEPTH 64 +#include "aap_template.c" + +static int config_output(AVFilterLink *outlink) +{ + const int channels = outlink->ch_layout.nb_channels; + AVFilterContext *ctx = outlink->src; + AudioAPContext *s = ctx->priv; + + s->kernel_size = FFALIGN(s->order, 16); + + if (!s->offset) + s->offset = ff_get_audio_buffer(outlink, 3); + if (!s->delay) + s->delay = ff_get_audio_buffer(outlink, 2 * s->kernel_size); + if (!s->dcoeffs) + s->dcoeffs = ff_get_audio_buffer(outlink, s->kernel_size); + if (!s->coeffs) + s->coeffs = ff_get_audio_buffer(outlink, 2 * s->kernel_size); + if (!s->e) + s->e = ff_get_audio_buffer(outlink, 2 * s->projection); + if (!s->p) + s->p = ff_get_audio_buffer(outlink, s->projection + 1); + if (!s->x) + s->x = ff_get_audio_buffer(outlink, 2 * (s->projection + s->order)); + if (!s->w) + s->w = ff_get_audio_buffer(outlink, s->projection); + if (!s->tmp) + s->tmp = ff_get_audio_buffer(outlink, s->kernel_size); + if (!s->tmpm) + s->tmpm = ff_get_audio_buffer(outlink, s->projection * s->projection); + if (!s->itmpm) + s->itmpm = ff_get_audio_buffer(outlink, s->projection * s->projection); + + if (!s->tmpmp) + s->tmpmp = av_calloc(s->projection * channels, sizeof(*s->tmpmp)); + if (!s->itmpmp) + s->itmpmp = av_calloc(s->projection * channels, sizeof(*s->itmpmp)); + + if (!s->offset || !s->delay || !s->dcoeffs || !s->coeffs || !s->tmpmp || !s->itmpmp || + !s->e || !s->p || !s->x || !s->w || !s->tmp || !s->tmpm || !s->itmpm) + return AVERROR(ENOMEM); + + switch (outlink->format) { + case AV_SAMPLE_FMT_DBLP: + for (int ch = 0; ch < channels; ch++) { + double *itmpm = (double *)s->itmpm->extended_data[ch]; + double *tmpm = (double *)s->tmpm->extended_data[ch]; + double **itmpmp = (double **)&s->itmpmp[s->projection * ch]; + double **tmpmp = (double **)&s->tmpmp[s->projection * ch]; + + for (int i = 0; i < s->projection; i++) { + itmpmp[i] = &itmpm[i * s->projection]; + tmpmp[i] = &tmpm[i * s->projection]; + } + } + + s->filter_channels = filter_channels_double; + break; + case AV_SAMPLE_FMT_FLTP: + for (int ch = 0; ch < channels; ch++) { + float *itmpm = (float *)s->itmpm->extended_data[ch]; + float *tmpm = (float *)s->tmpm->extended_data[ch]; + float **itmpmp = (float **)&s->itmpmp[s->projection * ch]; + float **tmpmp = (float **)&s->tmpmp[s->projection * ch]; + + for (int i = 0; i < s->projection; i++) { + itmpmp[i] = &itmpm[i * s->projection]; + tmpmp[i] = &tmpm[i * s->projection]; + } + } + + s->filter_channels = filter_channels_float; + break; + } + + return 0; +} + +static av_cold int init(AVFilterContext *ctx) +{ + AudioAPContext *s = ctx->priv; + + s->fdsp = avpriv_float_dsp_alloc(0); + if (!s->fdsp) + return AVERROR(ENOMEM); + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + AudioAPContext *s = ctx->priv; + + av_freep(&s->fdsp); + + av_frame_free(&s->offset); + av_frame_free(&s->delay); + av_frame_free(&s->dcoeffs); + av_frame_free(&s->coeffs); + av_frame_free(&s->e); + av_frame_free(&s->p); + av_frame_free(&s->w); + av_frame_free(&s->x); + av_frame_free(&s->tmp); + av_frame_free(&s->tmpm); + av_frame_free(&s->itmpm); + + av_freep(&s->tmpmp); + av_freep(&s->itmpmp); +} + +static const AVFilterPad inputs[] = { + { + .name = "input", + .type = AVMEDIA_TYPE_AUDIO, + }, + { + .name = "desired", + .type = AVMEDIA_TYPE_AUDIO, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, + }, +}; + +const AVFilter ff_af_aap = { + .name = "aap", + .description = NULL_IF_CONFIG_SMALL("Apply Affine Projection algorithm to first audio stream."), + .priv_size = sizeof(AudioAPContext), + .priv_class = &aap_class, + .init = init, + .uninit = uninit, + .activate = activate, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_QUERY_FUNC(query_formats), + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | + AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, +}; diff --git a/libavfilter/af_acontrast.c b/libavfilter/af_acontrast.c index a4ed29e30aa..1fcbad8782f 100644 --- a/libavfilter/af_acontrast.c +++ b/libavfilter/af_acontrast.c @@ -23,7 +23,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" typedef struct AudioContrastContext { const AVClass *class; @@ -169,20 +168,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_acontrast = { .name = "acontrast", .description = NULL_IF_CONFIG_SMALL("Simple audio dynamic range compression/expansion filter."), .priv_size = sizeof(AudioContrastContext), .priv_class = &acontrast_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP), }; diff --git a/libavfilter/af_acopy.c b/libavfilter/af_acopy.c index 1591ec9639f..f65f04d4618 100644 --- a/libavfilter/af_acopy.c +++ b/libavfilter/af_acopy.c @@ -53,17 +53,10 @@ static const AVFilterPad acopy_inputs[] = { }, }; -static const AVFilterPad acopy_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_acopy = { .name = "acopy", .description = NULL_IF_CONFIG_SMALL("Copy the input audio unchanged to the output."), .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(acopy_inputs), - FILTER_OUTPUTS(acopy_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; diff --git a/libavfilter/af_acrossover.c b/libavfilter/af_acrossover.c index 417e8571334..0e7781c77a4 100644 --- a/libavfilter/af_acrossover.c +++ b/libavfilter/af_acrossover.c @@ -87,23 +87,23 @@ typedef struct AudioCrossoverContext { static const AVOption acrossover_options[] = { { "split", "set split frequencies", OFFSET(splits_str), AV_OPT_TYPE_STRING, {.str="500"}, 0, 0, AF }, - { "order", "set filter order", OFFSET(order_opt), AV_OPT_TYPE_INT, {.i64=1}, 0, 9, AF, "m" }, - { "2nd", "2nd order (12 dB/8ve)", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "m" }, - { "4th", "4th order (24 dB/8ve)", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "m" }, - { "6th", "6th order (36 dB/8ve)", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, "m" }, - { "8th", "8th order (48 dB/8ve)", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, AF, "m" }, - { "10th", "10th order (60 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, AF, "m" }, - { "12th", "12th order (72 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, AF, "m" }, - { "14th", "14th order (84 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, AF, "m" }, - { "16th", "16th order (96 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=7}, 0, 0, AF, "m" }, - { "18th", "18th order (108 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=8}, 0, 0, AF, "m" }, - { "20th", "20th order (120 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=9}, 0, 0, AF, "m" }, + { "order", "set filter order", OFFSET(order_opt), AV_OPT_TYPE_INT, {.i64=1}, 0, 9, AF, .unit = "m" }, + { "2nd", "2nd order (12 dB/8ve)", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "m" }, + { "4th", "4th order (24 dB/8ve)", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "m" }, + { "6th", "6th order (36 dB/8ve)", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, .unit = "m" }, + { "8th", "8th order (48 dB/8ve)", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, AF, .unit = "m" }, + { "10th", "10th order (60 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, AF, .unit = "m" }, + { "12th", "12th order (72 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, AF, .unit = "m" }, + { "14th", "14th order (84 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, AF, .unit = "m" }, + { "16th", "16th order (96 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=7}, 0, 0, AF, .unit = "m" }, + { "18th", "18th order (108 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=8}, 0, 0, AF, .unit = "m" }, + { "20th", "20th order (120 dB/8ve)",0, AV_OPT_TYPE_CONST, {.i64=9}, 0, 0, AF, .unit = "m" }, { "level", "set input gain", OFFSET(level_in), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 1, AF }, { "gain", "set output bands gain", OFFSET(gains_str), AV_OPT_TYPE_STRING, {.str="1.f"}, 0, 0, AF }, - { "precision", "set processing precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, AF, "precision" }, - { "auto", "set auto processing precision", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "precision" }, - { "float", "set single-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "precision" }, - { "double","set double-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, "precision" }, + { "precision", "set processing precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, AF, .unit = "precision" }, + { "auto", "set auto processing precision", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "precision" }, + { "float", "set single-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "precision" }, + { "double","set double-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, .unit = "precision" }, { NULL } }; diff --git a/libavfilter/af_acrusher.c b/libavfilter/af_acrusher.c index 5c9f36529c7..5fc942224f7 100644 --- a/libavfilter/af_acrusher.c +++ b/libavfilter/af_acrusher.c @@ -75,9 +75,9 @@ static const AVOption acrusher_options[] = { { "level_out","set level out", OFFSET(level_out), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A }, { "bits", "set bit reduction", OFFSET(bits), AV_OPT_TYPE_DOUBLE, {.dbl=8}, 1, 64, A }, { "mix", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=.5}, 0, 1, A }, - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A, "mode" }, - { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, "mode" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, "mode" }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A, .unit = "mode" }, + { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, .unit = "mode" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, .unit = "mode" }, { "dc", "set DC", OFFSET(dc), AV_OPT_TYPE_DOUBLE, {.dbl=1}, .25, 4, A }, { "aa", "set anti-aliasing", OFFSET(aa), AV_OPT_TYPE_DOUBLE, {.dbl=.5}, 0, 1, A }, { "samples", "set sample reduction", OFFSET(samples), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 1, 250, A }, @@ -325,13 +325,6 @@ static const AVFilterPad avfilter_af_acrusher_inputs[] = { }, }; -static const AVFilterPad avfilter_af_acrusher_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_acrusher = { .name = "acrusher", .description = NULL_IF_CONFIG_SMALL("Reduce audio bit resolution."), @@ -339,7 +332,7 @@ const AVFilter ff_af_acrusher = { .priv_class = &acrusher_class, .uninit = uninit, FILTER_INPUTS(avfilter_af_acrusher_inputs), - FILTER_OUTPUTS(avfilter_af_acrusher_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBL), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/af_adeclick.c b/libavfilter/af_adeclick.c index 2db9a29fd35..37892257395 100644 --- a/libavfilter/af_adeclick.c +++ b/libavfilter/af_adeclick.c @@ -20,10 +20,10 @@ #include "libavutil/audio_fifo.h" #include "libavutil/opt.h" +#include "libavutil/tx.h" #include "avfilter.h" #include "audio.h" #include "filters.h" -#include "formats.h" #include "internal.h" typedef struct DeclickChannel { @@ -102,12 +102,12 @@ static const AVOption adeclick_options[] = { { "t", "set threshold", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 1, 100, AF }, { "burst", "set burst fusion", OFFSET(burst), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 0, 10, AF }, { "b", "set burst fusion", OFFSET(burst), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 0, 10, AF }, - { "method", "set overlap method", OFFSET(method), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AF, "m" }, - { "m", "set overlap method", OFFSET(method), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AF, "m" }, - { "add", "overlap-add", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "m" }, - { "a", "overlap-add", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "m" }, - { "save", "overlap-save", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "m" }, - { "s", "overlap-save", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "m" }, + { "method", "set overlap method", OFFSET(method), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AF, .unit = "m" }, + { "m", "set overlap method", OFFSET(method), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AF, .unit = "m" }, + { "add", "overlap-add", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "m" }, + { "a", "overlap-add", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "m" }, + { "save", "overlap-save", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "m" }, + { "s", "overlap-save", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "m" }, { NULL } }; @@ -120,21 +120,74 @@ static int config_input(AVFilterLink *inlink) int i; s->pts = AV_NOPTS_VALUE; - s->window_size = inlink->sample_rate * s->w / 1000.; - if (s->window_size < 100) - return AVERROR(EINVAL); + s->window_size = FFMAX(100, inlink->sample_rate * s->w / 1000.); s->ar_order = FFMAX(s->window_size * s->ar / 100., 1); s->nb_burst_samples = s->window_size * s->burst / 1000.; - s->hop_size = s->window_size * (1. - (s->overlap / 100.)); - if (s->hop_size < 1) - return AVERROR(EINVAL); + s->hop_size = FFMAX(1, s->window_size * (1. - (s->overlap / 100.))); s->window_func_lut = av_calloc(s->window_size, sizeof(*s->window_func_lut)); if (!s->window_func_lut) return AVERROR(ENOMEM); - for (i = 0; i < s->window_size; i++) - s->window_func_lut[i] = sin(M_PI * i / s->window_size) * - (1. - (s->overlap / 100.)) * M_PI_2; + + { + double *tx_in[2], *tx_out[2]; + AVTXContext *tx, *itx; + av_tx_fn tx_fn, itx_fn; + int ret, tx_size; + double scale; + + tx_size = 1 << (32 - ff_clz(s->window_size)); + + scale = 1.0; + ret = av_tx_init(&tx, &tx_fn, AV_TX_DOUBLE_RDFT, 0, tx_size, &scale, 0); + if (ret < 0) + return ret; + + scale = 1.0 / tx_size; + ret = av_tx_init(&itx, &itx_fn, AV_TX_DOUBLE_RDFT, 1, tx_size, &scale, 0); + if (ret < 0) + return ret; + + tx_in[0] = av_calloc(tx_size + 2, sizeof(*tx_in[0])); + tx_in[1] = av_calloc(tx_size + 2, sizeof(*tx_in[1])); + tx_out[0] = av_calloc(tx_size + 2, sizeof(*tx_out[0])); + tx_out[1] = av_calloc(tx_size + 2, sizeof(*tx_out[1])); + if (!tx_in[0] || !tx_in[1] || !tx_out[0] || !tx_out[1]) + return AVERROR(ENOMEM); + + for (int n = 0; n < s->window_size - s->hop_size; n++) + tx_in[0][n] = 1.0; + + for (int n = 0; n < s->hop_size; n++) + tx_in[1][n] = 1.0; + + tx_fn(tx, tx_out[0], tx_in[0], sizeof(double)); + tx_fn(tx, tx_out[1], tx_in[1], sizeof(double)); + + for (int n = 0; n <= tx_size/2; n++) { + double re0 = tx_out[0][2*n]; + double im0 = tx_out[0][2*n+1]; + double re1 = tx_out[1][2*n]; + double im1 = tx_out[1][2*n+1]; + + tx_in[0][2*n] = re0 * re1 - im0 * im1; + tx_in[0][2*n+1] = re0 * im1 + re1 * im0; + } + + itx_fn(itx, tx_out[0], tx_in[0], sizeof(AVComplexDouble)); + + scale = 1.0 / (s->window_size - s->hop_size); + for (int n = 0; n < s->window_size; n++) + s->window_func_lut[n] = tx_out[0][n] * scale; + + av_tx_uninit(&tx); + av_tx_uninit(&itx); + + av_freep(&tx_in[0]); + av_freep(&tx_in[1]); + av_freep(&tx_out[0]); + av_freep(&tx_out[1]); + } av_frame_free(&s->in); av_frame_free(&s->out); @@ -677,9 +730,10 @@ static av_cold void uninit(AVFilterContext *ctx) AudioDeclickContext *s = ctx->priv; int i; - av_log(ctx, AV_LOG_INFO, "Detected %s in %"PRId64" of %"PRId64" samples (%g%%).\n", - s->is_declip ? "clips" : "clicks", s->detected_errors, - s->nb_samples, 100. * s->detected_errors / s->nb_samples); + if (s->nb_samples > 0) + av_log(ctx, AV_LOG_INFO, "Detected %s in %"PRId64" of %"PRId64" samples (%g%%).\n", + s->is_declip ? "clips" : "clicks", s->detected_errors, + s->nb_samples, 100. * s->detected_errors / s->nb_samples); av_audio_fifo_free(s->fifo); av_audio_fifo_free(s->efifo); @@ -724,13 +778,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_adeclick = { .name = "adeclick", .description = NULL_IF_CONFIG_SMALL("Remove impulsive noise from input audio."), @@ -740,7 +787,7 @@ const AVFilter ff_af_adeclick = { .activate = activate, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, }; @@ -756,12 +803,12 @@ static const AVOption adeclip_options[] = { { "t", "set threshold", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl=10}, 1, 100, AF }, { "hsize", "set histogram size", OFFSET(nb_hbins), AV_OPT_TYPE_INT, {.i64=1000}, 100, 9999, AF }, { "n", "set histogram size", OFFSET(nb_hbins), AV_OPT_TYPE_INT, {.i64=1000}, 100, 9999, AF }, - { "method", "set overlap method", OFFSET(method), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AF, "m" }, - { "m", "set overlap method", OFFSET(method), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AF, "m" }, - { "add", "overlap-add", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "m" }, - { "a", "overlap-add", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "m" }, - { "save", "overlap-save", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "m" }, - { "s", "overlap-save", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "m" }, + { "method", "set overlap method", OFFSET(method), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AF, .unit = "m" }, + { "m", "set overlap method", OFFSET(method), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AF, .unit = "m" }, + { "add", "overlap-add", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "m" }, + { "a", "overlap-add", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "m" }, + { "save", "overlap-save", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "m" }, + { "s", "overlap-save", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "m" }, { NULL } }; @@ -776,7 +823,7 @@ const AVFilter ff_af_adeclip = { .activate = activate, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, }; diff --git a/libavfilter/af_adecorrelate.c b/libavfilter/af_adecorrelate.c index cadc62c1f7f..87355e72e0e 100644 --- a/libavfilter/af_adecorrelate.c +++ b/libavfilter/af_adecorrelate.c @@ -26,7 +26,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #define MAX_STAGES 16 #define FILTER_FC 1100.0 @@ -230,13 +229,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_adecorrelate = { .name = "adecorrelate", .description = NULL_IF_CONFIG_SMALL("Apply decorrelation to input audio."), @@ -244,7 +236,7 @@ const AVFilter ff_af_adecorrelate = { .priv_class = &adecorrelate_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/af_adelay.c b/libavfilter/af_adelay.c index 9e63e2d6184..87a86170a87 100644 --- a/libavfilter/af_adelay.c +++ b/libavfilter/af_adelay.c @@ -44,9 +44,12 @@ typedef struct AudioDelayContext { int block_align; int64_t padding; int64_t max_delay; + int64_t offset; int64_t next_pts; int eof; + AVFrame *input; + void (*delay_channel)(ChanDelay *d, int nb_samples, const uint8_t *src, uint8_t *dst); int (*resize_channel_samples)(ChanDelay *d, int64_t new_delay); @@ -187,6 +190,7 @@ static int config_input(AVFilterLink *inlink) char *p, *saveptr = NULL; int i; + s->next_pts = AV_NOPTS_VALUE; s->chandelay = av_calloc(inlink->ch_layout.nb_channels, sizeof(*s->chandelay)); if (!s->chandelay) return AVERROR(ENOMEM); @@ -224,6 +228,10 @@ static int config_input(AVFilterLink *inlink) d->delay -= s->padding; } + + s->offset = av_rescale_q(s->padding, + av_make_q(1, inlink->sample_rate), + inlink->time_base); } for (i = 0; i < s->nb_delays; i++) { @@ -323,11 +331,16 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) AVFrame *out_frame; int i; - if (ctx->is_disabled || !s->delays) + if (ctx->is_disabled || !s->delays) { + s->input = NULL; return ff_filter_frame(outlink, frame); + } + + s->next_pts = av_rescale_q(frame->pts, inlink->time_base, outlink->time_base); out_frame = ff_get_audio_buffer(outlink, frame->nb_samples); if (!out_frame) { + s->input = NULL; av_frame_free(&frame); return AVERROR(ENOMEM); } @@ -344,9 +357,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) s->delay_channel(d, frame->nb_samples, src, dst); } - out_frame->pts = s->next_pts; - s->next_pts += av_rescale_q(frame->nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base); + out_frame->pts = s->next_pts + s->offset; + out_frame->duration = av_rescale_q(out_frame->nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base); + s->next_pts += out_frame->duration; av_frame_free(&frame); + s->input = NULL; return ff_filter_frame(outlink, out_frame); } @@ -361,6 +376,20 @@ static int activate(AVFilterContext *ctx) FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + if (!s->input) { + ret = ff_inlink_consume_frame(inlink, &s->input); + if (ret < 0) + return ret; + } + + if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { + if (status == AVERROR_EOF) + s->eof = 1; + } + + if (s->next_pts == AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE) + s->next_pts = av_rescale_q(pts, inlink->time_base, outlink->time_base); + if (s->padding) { int nb_samples = FFMIN(s->padding, 2048); @@ -374,24 +403,17 @@ static int activate(AVFilterContext *ctx) outlink->ch_layout.nb_channels, frame->format); + frame->duration = av_rescale_q(frame->nb_samples, + (AVRational){1, outlink->sample_rate}, + outlink->time_base); frame->pts = s->next_pts; - if (s->next_pts != AV_NOPTS_VALUE) - s->next_pts += av_rescale_q(nb_samples, (AVRational){1, outlink->sample_rate}, outlink->time_base); + s->next_pts += frame->duration; return ff_filter_frame(outlink, frame); } - ret = ff_inlink_consume_frame(inlink, &frame); - if (ret < 0) - return ret; - - if (ret > 0) - return filter_frame(inlink, frame); - - if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { - if (status == AVERROR_EOF) - s->eof = 1; - } + if (s->input) + return filter_frame(inlink, s->input); if (s->eof && s->max_delay) { int nb_samples = FFMIN(s->max_delay, 2048); @@ -406,7 +428,11 @@ static int activate(AVFilterContext *ctx) outlink->ch_layout.nb_channels, frame->format); + frame->duration = av_rescale_q(frame->nb_samples, + (AVRational){1, outlink->sample_rate}, + outlink->time_base); frame->pts = s->next_pts; + s->next_pts += frame->duration; return filter_frame(inlink, frame); } @@ -440,13 +466,6 @@ static const AVFilterPad adelay_inputs[] = { }, }; -static const AVFilterPad adelay_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_adelay = { .name = "adelay", .description = NULL_IF_CONFIG_SMALL("Delay one or more audio channels."), @@ -455,7 +474,7 @@ const AVFilter ff_af_adelay = { .activate = activate, .uninit = uninit, FILTER_INPUTS(adelay_inputs), - FILTER_OUTPUTS(adelay_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_U8P, AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/af_adenorm.c b/libavfilter/af_adenorm.c index 583b7fafee7..f5f90391720 100644 --- a/libavfilter/af_adenorm.c +++ b/libavfilter/af_adenorm.c @@ -252,11 +252,11 @@ static const AVFilterPad adenorm_outputs[] = { static const AVOption adenorm_options[] = { { "level", "set level", OFFSET(level_db), AV_OPT_TYPE_DOUBLE, {.dbl=-351}, -451, -90, FLAGS }, - { "type", "set type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=DC_TYPE}, 0, NB_TYPES-1, FLAGS, "type" }, - { "dc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=DC_TYPE}, 0, 0, FLAGS, "type"}, - { "ac", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AC_TYPE}, 0, 0, FLAGS, "type"}, - { "square",NULL, 0, AV_OPT_TYPE_CONST, {.i64=SQ_TYPE}, 0, 0, FLAGS, "type"}, - { "pulse", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PS_TYPE}, 0, 0, FLAGS, "type"}, + { "type", "set type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=DC_TYPE}, 0, NB_TYPES-1, FLAGS, .unit = "type" }, + { "dc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=DC_TYPE}, 0, 0, FLAGS, .unit = "type"}, + { "ac", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AC_TYPE}, 0, 0, FLAGS, .unit = "type"}, + { "square",NULL, 0, AV_OPT_TYPE_CONST, {.i64=SQ_TYPE}, 0, 0, FLAGS, .unit = "type"}, + { "pulse", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PS_TYPE}, 0, 0, FLAGS, .unit = "type"}, { NULL } }; diff --git a/libavfilter/af_aderivative.c b/libavfilter/af_aderivative.c index 853c0f34122..4883972dcf1 100644 --- a/libavfilter/af_aderivative.c +++ b/libavfilter/af_aderivative.c @@ -126,6 +126,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->prev = ff_get_audio_buffer(inlink, 1); if (!s->prev) { av_frame_free(&in); + av_frame_free(&out); return AVERROR(ENOMEM); } } @@ -153,13 +154,6 @@ static const AVFilterPad aderivative_inputs[] = { }, }; -static const AVFilterPad aderivative_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVOption aderivative_options[] = { { NULL } }; @@ -173,7 +167,7 @@ const AVFilter ff_af_aderivative = { .priv_class = &aderivative_class, .uninit = uninit, FILTER_INPUTS(aderivative_inputs), - FILTER_OUTPUTS(aderivative_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, @@ -186,7 +180,7 @@ const AVFilter ff_af_aintegral = { .priv_class = &aderivative_class, .uninit = uninit, FILTER_INPUTS(aderivative_inputs), - FILTER_OUTPUTS(aderivative_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, }; diff --git a/libavfilter/af_adrc.c b/libavfilter/af_adrc.c index 54997c383ec..34e5433a6b6 100644 --- a/libavfilter/af_adrc.c +++ b/libavfilter/af_adrc.c @@ -485,13 +485,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_adrc = { .name = "adrc", .description = NULL_IF_CONFIG_SMALL("Audio Spectral Dynamic Range Controller."), @@ -499,7 +492,7 @@ const AVFilter ff_af_adrc = { .priv_class = &adrc_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/af_adynamicequalizer.c b/libavfilter/af_adynamicequalizer.c index 0e44922c1e3..aa1dc5f45c6 100644 --- a/libavfilter/af_adynamicequalizer.c +++ b/libavfilter/af_adynamicequalizer.c @@ -18,15 +18,66 @@ #include +#include "libavutil/ffmath.h" #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" #include "formats.h" +enum DetectionModes { + DET_UNSET = 0, + DET_DISABLED, + DET_OFF, + DET_ON, + DET_ADAPTIVE, + NB_DMODES, +}; + +enum FilterModes { + LISTEN = -1, + CUT_BELOW, + CUT_ABOVE, + BOOST_BELOW, + BOOST_ABOVE, + NB_FMODES, +}; + +typedef struct ChannelContext { + double fa_double[3], fm_double[3]; + double dstate_double[2]; + double fstate_double[2]; + double tstate_double[2]; + double lin_gain_double; + double detect_double; + double threshold_log_double; + double new_threshold_log_double; + double log_sum_double; + double sum_double; + float fa_float[3], fm_float[3]; + float dstate_float[2]; + float fstate_float[2]; + float tstate_float[2]; + float lin_gain_float; + float detect_float; + float threshold_log_float; + float new_threshold_log_float; + float log_sum_float; + float sum_float; + void *dqueue; + void *queue; + int position; + int size; + int front; + int back; + int detection; + int init; +} ChannelContext; + typedef struct AudioDynamicEqualizerContext { const AVClass *class; double threshold; + double threshold_log; double dfrequency; double dqfactor; double tfrequency; @@ -34,194 +85,97 @@ typedef struct AudioDynamicEqualizerContext { double ratio; double range; double makeup; - double attack; - double release; - double attack_coef; - double release_coef; + double dattack; + double drelease; + double dattack_coef; + double drelease_coef; + double gattack_coef; + double grelease_coef; int mode; - int direction; int detection; - int type; + int tftype; + int dftype; + int precision; + int format; + int nb_channels; + + int (*filter_prepare)(AVFilterContext *ctx); + int (*filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + + double da_double[3], dm_double[3]; + float da_float[3], dm_float[3]; - AVFrame *state; + ChannelContext *cc; } AudioDynamicEqualizerContext; -static int config_input(AVFilterLink *inlink) +static int query_formats(AVFilterContext *ctx) { - AVFilterContext *ctx = inlink->dst; AudioDynamicEqualizerContext *s = ctx->priv; + static const enum AVSampleFormat sample_fmts[3][3] = { + { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE }, + { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, + { AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE }, + }; + int ret; - s->state = ff_get_audio_buffer(inlink, 8); - if (!s->state) - return AVERROR(ENOMEM); - - for (int ch = 0; ch < inlink->ch_layout.nb_channels; ch++) { - double *state = (double *)s->state->extended_data[ch]; + if ((ret = ff_set_common_all_channel_counts(ctx)) < 0) + return ret; - state[4] = 1.; - } + if ((ret = ff_set_common_formats_from_list(ctx, sample_fmts[s->precision])) < 0) + return ret; - return 0; + return ff_set_common_all_samplerates(ctx); } -static double get_svf(double in, double *m, double *a, double *b) +static double get_coef(double x, double sr) { - const double v0 = in; - const double v3 = v0 - b[1]; - const double v1 = a[0] * b[0] + a[1] * v3; - const double v2 = b[1] + a[1] * b[0] + a[2] * v3; - - b[0] = 2. * v1 - b[0]; - b[1] = 2. * v2 - b[1]; - - return m[0] * v0 + m[1] * v1 + m[2] * v2; + return 1.0 - exp(-1.0 / (0.001 * x * sr)); } typedef struct ThreadData { AVFrame *in, *out; } ThreadData; -static int filter_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - AudioDynamicEqualizerContext *s = ctx->priv; - ThreadData *td = arg; - AVFrame *in = td->in; - AVFrame *out = td->out; - const double sample_rate = in->sample_rate; - const double makeup = s->makeup; - const double ratio = s->ratio; - const double range = s->range; - const double dfrequency = fmin(s->dfrequency, sample_rate * 0.5); - const double tfrequency = fmin(s->tfrequency, sample_rate * 0.5); - const double release = s->release_coef; - const double irelease = 1. - release; - const double attack = s->attack_coef; - const double iattack = 1. - attack; - const double dqfactor = s->dqfactor; - const double tqfactor = s->tqfactor; - const double fg = tan(M_PI * tfrequency / sample_rate); - const double dg = tan(M_PI * dfrequency / sample_rate); - const int start = (in->ch_layout.nb_channels * jobnr) / nb_jobs; - const int end = (in->ch_layout.nb_channels * (jobnr+1)) / nb_jobs; - const int detection = s->detection; - const int direction = s->direction; - const int mode = s->mode; - const int type = s->type; - double da[3], dm[3]; +#define DEPTH 32 +#include "adynamicequalizer_template.c" - { - double k = 1. / dqfactor; +#undef DEPTH +#define DEPTH 64 +#include "adynamicequalizer_template.c" - da[0] = 1. / (1. + dg * (dg + k)); - da[1] = dg * da[0]; - da[2] = dg * da[1]; +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + AudioDynamicEqualizerContext *s = ctx->priv; - dm[0] = 0.; - dm[1] = k; - dm[2] = 0.; + s->format = inlink->format; + s->cc = av_calloc(inlink->ch_layout.nb_channels, sizeof(*s->cc)); + if (!s->cc) + return AVERROR(ENOMEM); + s->nb_channels = inlink->ch_layout.nb_channels; + + switch (s->format) { + case AV_SAMPLE_FMT_DBLP: + s->filter_prepare = filter_prepare_double; + s->filter_channels = filter_channels_double; + break; + case AV_SAMPLE_FMT_FLTP: + s->filter_prepare = filter_prepare_float; + s->filter_channels = filter_channels_float; + break; } - for (int ch = start; ch < end; ch++) { - const double *src = (const double *)in->extended_data[ch]; - double *dst = (double *)out->extended_data[ch]; - double *state = (double *)s->state->extended_data[ch]; - const double threshold = detection == 0 ? state[5] : s->threshold; - - if (detection < 0) - state[5] = threshold; - - for (int n = 0; n < out->nb_samples; n++) { - double detect, gain, v, listen; - double fa[3], fm[3]; - double k, g; - - detect = listen = get_svf(src[n], dm, da, state); - detect = fabs(detect); - - if (detection > 0) - state[5] = fmax(state[5], detect); - - if (direction == 0 && mode == 0 && detect < threshold) - detect = 1. / av_clipd(1. + makeup + (threshold - detect) * ratio, 1., range); - else if (direction == 0 && mode == 1 && detect < threshold) - detect = av_clipd(1. + makeup + (threshold - detect) * ratio, 1., range); - else if (direction == 1 && mode == 0 && detect > threshold) - detect = 1. / av_clipd(1. + makeup + (detect - threshold) * ratio, 1., range); - else if (direction == 1 && mode == 1 && detect > threshold) - detect = av_clipd(1. + makeup + (detect - threshold) * ratio, 1., range); - else - detect = 1.; - - if (direction == 0) { - if (detect > state[4]) { - detect = iattack * detect + attack * state[4]; - } else { - detect = irelease * detect + release * state[4]; - } - } else { - if (detect < state[4]) { - detect = iattack * detect + attack * state[4]; - } else { - detect = irelease * detect + release * state[4]; - } - } - - if (state[4] != detect || n == 0) { - state[4] = gain = detect; - - switch (type) { - case 0: - k = 1. / (tqfactor * gain); - - fa[0] = 1. / (1. + fg * (fg + k)); - fa[1] = fg * fa[0]; - fa[2] = fg * fa[1]; - - fm[0] = 1.; - fm[1] = k * (gain * gain - 1.); - fm[2] = 0.; - break; - case 1: - k = 1. / tqfactor; - g = fg / sqrt(gain); - - fa[0] = 1. / (1. + g * (g + k)); - fa[1] = g * fa[0]; - fa[2] = g * fa[1]; - - fm[0] = 1.; - fm[1] = k * (gain - 1.); - fm[2] = gain * gain - 1.; - break; - case 2: - k = 1. / tqfactor; - g = fg / sqrt(gain); - - fa[0] = 1. / (1. + g * (g + k)); - fa[1] = g * fa[0]; - fa[2] = g * fa[1]; - - fm[0] = gain * gain; - fm[1] = k * (1. - gain) * gain; - fm[2] = 1. - gain * gain; - break; - } - } - - v = get_svf(src[n], fm, fa, &state[2]); - v = mode == -1 ? listen : v; - dst[n] = ctx->is_disabled ? src[n] : v; - } + for (int ch = 0; ch < s->nb_channels; ch++) { + ChannelContext *cc = &s->cc[ch]; + cc->queue = av_calloc(inlink->sample_rate, sizeof(double)); + cc->dqueue = av_calloc(inlink->sample_rate, sizeof(double)); + if (!cc->queue || !cc->dqueue) + return AVERROR(ENOMEM); } return 0; } -static double get_coef(double x, double sr) -{ - return exp(-1000. / (x * sr)); -} - static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; @@ -241,12 +195,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_frame_copy_props(out, in); } - s->attack_coef = get_coef(s->attack, in->sample_rate); - s->release_coef = get_coef(s->release, in->sample_rate); - td.in = in; td.out = out; - ff_filter_execute(ctx, filter_channels, &td, NULL, + s->filter_prepare(ctx); + ff_filter_execute(ctx, s->filter_channels, &td, NULL, FFMIN(outlink->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx))); if (out != in) @@ -258,10 +210,16 @@ static av_cold void uninit(AVFilterContext *ctx) { AudioDynamicEqualizerContext *s = ctx->priv; - av_frame_free(&s->state); + for (int ch = 0; ch < s->nb_channels; ch++) { + ChannelContext *cc = &s->cc[ch]; + av_freep(&cc->queue); + av_freep(&cc->dqueue); + } + av_freep(&s->cc); } #define OFFSET(x) offsetof(AudioDynamicEqualizerContext, x) +#define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption adynamicequalizer_options[] = { @@ -270,26 +228,35 @@ static const AVOption adynamicequalizer_options[] = { { "dqfactor", "set detection Q factor", OFFSET(dqfactor), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.001, 1000, FLAGS }, { "tfrequency", "set target frequency", OFFSET(tfrequency), AV_OPT_TYPE_DOUBLE, {.dbl=1000}, 2, 1000000, FLAGS }, { "tqfactor", "set target Q factor", OFFSET(tqfactor), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.001, 1000, FLAGS }, - { "attack", "set attack duration", OFFSET(attack), AV_OPT_TYPE_DOUBLE, {.dbl=20}, 1, 2000, FLAGS }, - { "release", "set release duration", OFFSET(release), AV_OPT_TYPE_DOUBLE, {.dbl=200}, 1, 2000, FLAGS }, + { "attack", "set detection attack duration", OFFSET(dattack), AV_OPT_TYPE_DOUBLE, {.dbl=20}, 0.01, 2000, FLAGS }, + { "release","set detection release duration",OFFSET(drelease), AV_OPT_TYPE_DOUBLE, {.dbl=200}, 0.01, 2000, FLAGS }, { "ratio", "set ratio factor", OFFSET(ratio), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 30, FLAGS }, - { "makeup", "set makeup gain", OFFSET(makeup), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 100, FLAGS }, - { "range", "set max gain", OFFSET(range), AV_OPT_TYPE_DOUBLE, {.dbl=50}, 1, 200, FLAGS }, - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, -1, 1, FLAGS, "mode" }, - { "listen", 0, 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, FLAGS, "mode" }, - { "cut", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" }, - { "boost", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" }, - { "tftype", "set target filter type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "type" }, - { "bell", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "type" }, - { "lowshelf", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "type" }, - { "highshelf",0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "type" }, - { "direction", "set direction", OFFSET(direction), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "direction" }, - { "downward", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "direction" }, - { "upward", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "direction" }, - { "auto", "set auto threshold", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=-1}, -1, 1, FLAGS, "auto" }, - { "disabled", 0, 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, FLAGS, "auto" }, - { "off", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "auto" }, - { "on", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "auto" }, + { "makeup", "set makeup gain", OFFSET(makeup), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1000, FLAGS }, + { "range", "set max gain", OFFSET(range), AV_OPT_TYPE_DOUBLE, {.dbl=50}, 1, 2000, FLAGS }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, LISTEN,NB_FMODES-1,FLAGS, .unit = "mode" }, + { "listen", 0, 0, AV_OPT_TYPE_CONST, {.i64=LISTEN}, 0, 0, FLAGS, .unit = "mode" }, + { "cutbelow", 0, 0, AV_OPT_TYPE_CONST, {.i64=CUT_BELOW},0, 0, FLAGS, .unit = "mode" }, + { "cutabove", 0, 0, AV_OPT_TYPE_CONST, {.i64=CUT_ABOVE},0, 0, FLAGS, .unit = "mode" }, + { "boostbelow", 0, 0, AV_OPT_TYPE_CONST, {.i64=BOOST_BELOW},0, 0, FLAGS, .unit = "mode" }, + { "boostabove", 0, 0, AV_OPT_TYPE_CONST, {.i64=BOOST_ABOVE},0, 0, FLAGS, .unit = "mode" }, + { "dftype", "set detection filter type",OFFSET(dftype), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, .unit = "dftype" }, + { "bandpass", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "dftype" }, + { "lowpass", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "dftype" }, + { "highpass", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "dftype" }, + { "peak", 0, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, .unit = "dftype" }, + { "tftype", "set target filter type", OFFSET(tftype), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, .unit = "tftype" }, + { "bell", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "tftype" }, + { "lowshelf", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "tftype" }, + { "highshelf",0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "tftype" }, + { "auto", "set auto threshold", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=DET_OFF},DET_DISABLED,NB_DMODES-1,FLAGS, .unit = "auto" }, + { "disabled", 0, 0, AV_OPT_TYPE_CONST, {.i64=DET_DISABLED}, 0, 0, FLAGS, .unit = "auto" }, + { "off", 0, 0, AV_OPT_TYPE_CONST, {.i64=DET_OFF}, 0, 0, FLAGS, .unit = "auto" }, + { "on", 0, 0, AV_OPT_TYPE_CONST, {.i64=DET_ON}, 0, 0, FLAGS, .unit = "auto" }, + { "adaptive", 0, 0, AV_OPT_TYPE_CONST, {.i64=DET_ADAPTIVE}, 0, 0, FLAGS, .unit = "auto" }, + { "precision", "set processing precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, AF, .unit = "precision" }, + { "auto", "set auto processing precision", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "precision" }, + { "float", "set single-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "precision" }, + { "double","set double-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, .unit = "precision" }, { NULL } }; @@ -304,13 +271,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_adynamicequalizer = { .name = "adynamicequalizer", .description = NULL_IF_CONFIG_SMALL("Apply Dynamic Equalization of input audio."), @@ -318,8 +278,8 @@ const AVFilter ff_af_adynamicequalizer = { .priv_class = &adynamicequalizer_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), - FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), + FILTER_OUTPUTS(ff_audio_default_filterpad), + FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_adynamicsmooth.c b/libavfilter/af_adynamicsmooth.c index 3f98d09f5d7..8afe5922261 100644 --- a/libavfilter/af_adynamicsmooth.c +++ b/libavfilter/af_adynamicsmooth.c @@ -20,7 +20,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" typedef struct AudioDynamicSmoothContext { const AVClass *class; @@ -121,13 +120,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_adynamicsmooth = { .name = "adynamicsmooth", .description = NULL_IF_CONFIG_SMALL("Apply Dynamic Smoothing of input audio."), @@ -135,7 +127,7 @@ const AVFilter ff_af_adynamicsmooth = { .priv_class = &adynamicsmooth_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_aecho.c b/libavfilter/af_aecho.c index e610c30d592..0b57c0e5870 100644 --- a/libavfilter/af_aecho.c +++ b/libavfilter/af_aecho.c @@ -328,13 +328,6 @@ static int activate(AVFilterContext *ctx) return request_frame(outlink); } -static const AVFilterPad aecho_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad aecho_outputs[] = { { .name = "default", @@ -351,7 +344,7 @@ const AVFilter ff_af_aecho = { .init = init, .activate = activate, .uninit = uninit, - FILTER_INPUTS(aecho_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(aecho_outputs), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_S16P, AV_SAMPLE_FMT_S32P, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), diff --git a/libavfilter/af_aemphasis.c b/libavfilter/af_aemphasis.c index 91878987d38..d808eec1caa 100644 --- a/libavfilter/af_aemphasis.c +++ b/libavfilter/af_aemphasis.c @@ -49,19 +49,19 @@ typedef struct AudioEmphasisContext { static const AVOption aemphasis_options[] = { { "level_in", "set input gain", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 64, FLAGS }, { "level_out", "set output gain", OFFSET(level_out), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 64, FLAGS }, - { "mode", "set filter mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "mode" }, - { "reproduction", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" }, - { "production", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" }, - { "type", "set filter type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=4}, 0, 8, FLAGS, "type" }, - { "col", "Columbia", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "type" }, - { "emi", "EMI", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "type" }, - { "bsi", "BSI (78RPM)", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "type" }, - { "riaa", "RIAA", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "type" }, - { "cd", "Compact Disc (CD)", 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, "type" }, - { "50fm", "50µs (FM)", 0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, FLAGS, "type" }, - { "75fm", "75µs (FM)", 0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, FLAGS, "type" }, - { "50kf", "50µs (FM-KF)", 0, AV_OPT_TYPE_CONST, {.i64=7}, 0, 0, FLAGS, "type" }, - { "75kf", "75µs (FM-KF)", 0, AV_OPT_TYPE_CONST, {.i64=8}, 0, 0, FLAGS, "type" }, + { "mode", "set filter mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "mode" }, + { "reproduction", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "mode" }, + { "production", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "mode" }, + { "type", "set filter type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=4}, 0, 8, FLAGS, .unit = "type" }, + { "col", "Columbia", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "type" }, + { "emi", "EMI", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "type" }, + { "bsi", "BSI (78RPM)", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "type" }, + { "riaa", "RIAA", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, .unit = "type" }, + { "cd", "Compact Disc (CD)", 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, .unit = "type" }, + { "50fm", "50µs (FM)", 0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, FLAGS, .unit = "type" }, + { "75fm", "75µs (FM)", 0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, FLAGS, .unit = "type" }, + { "50kf", "50µs (FM-KF)", 0, AV_OPT_TYPE_CONST, {.i64=7}, 0, 0, FLAGS, .unit = "type" }, + { "75kf", "75µs (FM-KF)", 0, AV_OPT_TYPE_CONST, {.i64=8}, 0, 0, FLAGS, .unit = "type" }, { NULL } }; @@ -361,13 +361,6 @@ static const AVFilterPad avfilter_af_aemphasis_inputs[] = { }, }; -static const AVFilterPad avfilter_af_aemphasis_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_aemphasis = { .name = "aemphasis", .description = NULL_IF_CONFIG_SMALL("Audio emphasis."), @@ -375,7 +368,7 @@ const AVFilter ff_af_aemphasis = { .priv_class = &aemphasis_class, .uninit = uninit, FILTER_INPUTS(avfilter_af_aemphasis_inputs), - FILTER_OUTPUTS(avfilter_af_aemphasis_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | diff --git a/libavfilter/af_aexciter.c b/libavfilter/af_aexciter.c index 8e829f992ba..400d6707b54 100644 --- a/libavfilter/af_aexciter.c +++ b/libavfilter/af_aexciter.c @@ -264,13 +264,6 @@ static const AVFilterPad avfilter_af_aexciter_inputs[] = { }, }; -static const AVFilterPad avfilter_af_aexciter_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_aexciter = { .name = "aexciter", .description = NULL_IF_CONFIG_SMALL("Enhance high frequency part of audio."), @@ -278,7 +271,7 @@ const AVFilter ff_af_aexciter = { .priv_class = &aexciter_class, .uninit = uninit, FILTER_INPUTS(avfilter_af_aexciter_inputs), - FILTER_OUTPUTS(avfilter_af_aexciter_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBL), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/af_afade.c b/libavfilter/af_afade.c index 48938384ffc..3a458734600 100644 --- a/libavfilter/af_afade.c +++ b/libavfilter/af_afade.c @@ -42,8 +42,8 @@ typedef struct AudioFadeContext { double silence; double unity; int overlap; - int cf0_eof; - int crossfade_is_over; + int status[2]; + int passthrough; int64_t pts; void (*fade_samples)(uint8_t **dst, uint8_t * const *src, @@ -58,7 +58,7 @@ typedef struct AudioFadeContext { int curve0, int curve1); } AudioFadeContext; -enum CurveType { NONE = -1, TRI, QSIN, ESIN, HSIN, LOG, IPAR, QUA, CUB, SQU, CBR, PAR, EXP, IQSIN, IHSIN, DESE, DESI, LOSI, SINC, ISINC, NB_CURVES }; +enum CurveType { NONE = -1, TRI, QSIN, ESIN, HSIN, LOG, IPAR, QUA, CUB, SQU, CBR, PAR, EXP, IQSIN, IHSIN, DESE, DESI, LOSI, SINC, ISINC, QUAT, QUATR, QSIN2, HSIN2, NB_CURVES }; #define OFFSET(x) offsetof(AudioFadeContext, x) #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM @@ -142,6 +142,18 @@ static double fade_gain(int curve, int64_t index, int64_t range, double silence, case ISINC: gain = gain <= 0.0 ? 0.0 : 1.0 - sin(M_PI * gain) / (M_PI * gain); break; + case QUAT: + gain = gain * gain * gain * gain; + break; + case QUATR: + gain = pow(gain, 0.25); + break; + case QSIN2: + gain = sin(gain * M_PI / 2.0) * sin(gain * M_PI / 2.0); + break; + case HSIN2: + gain = pow((1.0 - cos(gain * M_PI)) / 2.0, 2.0); + break; case NONE: gain = 1.0; break; @@ -282,10 +294,10 @@ static int config_output(AVFilterLink *outlink) #if CONFIG_AFADE_FILTER static const AVOption afade_options[] = { - { "type", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, TFLAGS, "type" }, - { "t", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, TFLAGS, "type" }, - { "in", "fade-in", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, TFLAGS, "type" }, - { "out", "fade-out", 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, TFLAGS, "type" }, + { "type", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, TFLAGS, .unit = "type" }, + { "t", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, TFLAGS, .unit = "type" }, + { "in", "fade-in", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, TFLAGS, .unit = "type" }, + { "out", "fade-out", 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, TFLAGS, .unit = "type" }, { "start_sample", "set number of first sample to start fading", OFFSET(start_sample), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, TFLAGS }, { "ss", "set number of first sample to start fading", OFFSET(start_sample), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, TFLAGS }, { "nb_samples", "set number of samples for fade duration", OFFSET(nb_samples), AV_OPT_TYPE_INT64, {.i64 = 44100}, 1, INT64_MAX, TFLAGS }, @@ -294,28 +306,32 @@ static const AVOption afade_options[] = { { "st", "set time to start fading", OFFSET(start_time), AV_OPT_TYPE_DURATION, {.i64 = 0 }, 0, INT64_MAX, TFLAGS }, { "duration", "set fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0 }, 0, INT64_MAX, TFLAGS }, { "d", "set fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0 }, 0, INT64_MAX, TFLAGS }, - { "curve", "set fade curve type", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, TFLAGS, "curve" }, - { "c", "set fade curve type", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, TFLAGS, "curve" }, - { "nofade", "no fade; keep audio as-is", 0, AV_OPT_TYPE_CONST, {.i64 = NONE }, 0, 0, TFLAGS, "curve" }, - { "tri", "linear slope", 0, AV_OPT_TYPE_CONST, {.i64 = TRI }, 0, 0, TFLAGS, "curve" }, - { "qsin", "quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = QSIN }, 0, 0, TFLAGS, "curve" }, - { "esin", "exponential sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = ESIN }, 0, 0, TFLAGS, "curve" }, - { "hsin", "half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = HSIN }, 0, 0, TFLAGS, "curve" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64 = LOG }, 0, 0, TFLAGS, "curve" }, - { "ipar", "inverted parabola", 0, AV_OPT_TYPE_CONST, {.i64 = IPAR }, 0, 0, TFLAGS, "curve" }, - { "qua", "quadratic", 0, AV_OPT_TYPE_CONST, {.i64 = QUA }, 0, 0, TFLAGS, "curve" }, - { "cub", "cubic", 0, AV_OPT_TYPE_CONST, {.i64 = CUB }, 0, 0, TFLAGS, "curve" }, - { "squ", "square root", 0, AV_OPT_TYPE_CONST, {.i64 = SQU }, 0, 0, TFLAGS, "curve" }, - { "cbr", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64 = CBR }, 0, 0, TFLAGS, "curve" }, - { "par", "parabola", 0, AV_OPT_TYPE_CONST, {.i64 = PAR }, 0, 0, TFLAGS, "curve" }, - { "exp", "exponential", 0, AV_OPT_TYPE_CONST, {.i64 = EXP }, 0, 0, TFLAGS, "curve" }, - { "iqsin", "inverted quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IQSIN}, 0, 0, TFLAGS, "curve" }, - { "ihsin", "inverted half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IHSIN}, 0, 0, TFLAGS, "curve" }, - { "dese", "double-exponential seat", 0, AV_OPT_TYPE_CONST, {.i64 = DESE }, 0, 0, TFLAGS, "curve" }, - { "desi", "double-exponential sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = DESI }, 0, 0, TFLAGS, "curve" }, - { "losi", "logistic sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = LOSI }, 0, 0, TFLAGS, "curve" }, - { "sinc", "sine cardinal function", 0, AV_OPT_TYPE_CONST, {.i64 = SINC }, 0, 0, TFLAGS, "curve" }, - { "isinc", "inverted sine cardinal function", 0, AV_OPT_TYPE_CONST, {.i64 = ISINC}, 0, 0, TFLAGS, "curve" }, + { "curve", "set fade curve type", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, TFLAGS, .unit = "curve" }, + { "c", "set fade curve type", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, TFLAGS, .unit = "curve" }, + { "nofade", "no fade; keep audio as-is", 0, AV_OPT_TYPE_CONST, {.i64 = NONE }, 0, 0, TFLAGS, .unit = "curve" }, + { "tri", "linear slope", 0, AV_OPT_TYPE_CONST, {.i64 = TRI }, 0, 0, TFLAGS, .unit = "curve" }, + { "qsin", "quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = QSIN }, 0, 0, TFLAGS, .unit = "curve" }, + { "esin", "exponential sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = ESIN }, 0, 0, TFLAGS, .unit = "curve" }, + { "hsin", "half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = HSIN }, 0, 0, TFLAGS, .unit = "curve" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64 = LOG }, 0, 0, TFLAGS, .unit = "curve" }, + { "ipar", "inverted parabola", 0, AV_OPT_TYPE_CONST, {.i64 = IPAR }, 0, 0, TFLAGS, .unit = "curve" }, + { "qua", "quadratic", 0, AV_OPT_TYPE_CONST, {.i64 = QUA }, 0, 0, TFLAGS, .unit = "curve" }, + { "cub", "cubic", 0, AV_OPT_TYPE_CONST, {.i64 = CUB }, 0, 0, TFLAGS, .unit = "curve" }, + { "squ", "square root", 0, AV_OPT_TYPE_CONST, {.i64 = SQU }, 0, 0, TFLAGS, .unit = "curve" }, + { "cbr", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64 = CBR }, 0, 0, TFLAGS, .unit = "curve" }, + { "par", "parabola", 0, AV_OPT_TYPE_CONST, {.i64 = PAR }, 0, 0, TFLAGS, .unit = "curve" }, + { "exp", "exponential", 0, AV_OPT_TYPE_CONST, {.i64 = EXP }, 0, 0, TFLAGS, .unit = "curve" }, + { "iqsin", "inverted quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IQSIN}, 0, 0, TFLAGS, .unit = "curve" }, + { "ihsin", "inverted half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IHSIN}, 0, 0, TFLAGS, .unit = "curve" }, + { "dese", "double-exponential seat", 0, AV_OPT_TYPE_CONST, {.i64 = DESE }, 0, 0, TFLAGS, .unit = "curve" }, + { "desi", "double-exponential sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = DESI }, 0, 0, TFLAGS, .unit = "curve" }, + { "losi", "logistic sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = LOSI }, 0, 0, TFLAGS, .unit = "curve" }, + { "sinc", "sine cardinal function", 0, AV_OPT_TYPE_CONST, {.i64 = SINC }, 0, 0, TFLAGS, .unit = "curve" }, + { "isinc", "inverted sine cardinal function", 0, AV_OPT_TYPE_CONST, {.i64 = ISINC}, 0, 0, TFLAGS, .unit = "curve" }, + { "quat", "quartic", 0, AV_OPT_TYPE_CONST, {.i64 = QUAT }, 0, 0, TFLAGS, .unit = "curve" }, + { "quatr", "quartic root", 0, AV_OPT_TYPE_CONST, {.i64 = QUATR}, 0, 0, TFLAGS, .unit = "curve" }, + { "qsin2", "squared quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = QSIN2}, 0, 0, TFLAGS, .unit = "curve" }, + { "hsin2", "squared half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = HSIN2}, 0, 0, TFLAGS, .unit = "curve" }, { "silence", "set the silence gain", OFFSET(silence), AV_OPT_TYPE_DOUBLE, {.dbl = 0 }, 0, 1, TFLAGS }, { "unity", "set the unity gain", OFFSET(unity), AV_OPT_TYPE_DOUBLE, {.dbl = 1 }, 0, 1, TFLAGS }, { NULL } @@ -442,30 +458,34 @@ static const AVOption acrossfade_options[] = { { "d", "set cross fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0 }, 0, 60000000, FLAGS }, { "overlap", "overlap 1st stream end with 2nd stream start", OFFSET(overlap), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, FLAGS }, { "o", "overlap 1st stream end with 2nd stream start", OFFSET(overlap), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, FLAGS }, - { "curve1", "set fade curve type for 1st stream", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, FLAGS, "curve" }, - { "c1", "set fade curve type for 1st stream", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, FLAGS, "curve" }, - { "nofade", "no fade; keep audio as-is", 0, AV_OPT_TYPE_CONST, {.i64 = NONE }, 0, 0, FLAGS, "curve" }, - { "tri", "linear slope", 0, AV_OPT_TYPE_CONST, {.i64 = TRI }, 0, 0, FLAGS, "curve" }, - { "qsin", "quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = QSIN }, 0, 0, FLAGS, "curve" }, - { "esin", "exponential sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = ESIN }, 0, 0, FLAGS, "curve" }, - { "hsin", "half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = HSIN }, 0, 0, FLAGS, "curve" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64 = LOG }, 0, 0, FLAGS, "curve" }, - { "ipar", "inverted parabola", 0, AV_OPT_TYPE_CONST, {.i64 = IPAR }, 0, 0, FLAGS, "curve" }, - { "qua", "quadratic", 0, AV_OPT_TYPE_CONST, {.i64 = QUA }, 0, 0, FLAGS, "curve" }, - { "cub", "cubic", 0, AV_OPT_TYPE_CONST, {.i64 = CUB }, 0, 0, FLAGS, "curve" }, - { "squ", "square root", 0, AV_OPT_TYPE_CONST, {.i64 = SQU }, 0, 0, FLAGS, "curve" }, - { "cbr", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64 = CBR }, 0, 0, FLAGS, "curve" }, - { "par", "parabola", 0, AV_OPT_TYPE_CONST, {.i64 = PAR }, 0, 0, FLAGS, "curve" }, - { "exp", "exponential", 0, AV_OPT_TYPE_CONST, {.i64 = EXP }, 0, 0, FLAGS, "curve" }, - { "iqsin", "inverted quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IQSIN}, 0, 0, FLAGS, "curve" }, - { "ihsin", "inverted half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IHSIN}, 0, 0, FLAGS, "curve" }, - { "dese", "double-exponential seat", 0, AV_OPT_TYPE_CONST, {.i64 = DESE }, 0, 0, FLAGS, "curve" }, - { "desi", "double-exponential sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = DESI }, 0, 0, FLAGS, "curve" }, - { "losi", "logistic sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = LOSI }, 0, 0, FLAGS, "curve" }, - { "sinc", "sine cardinal function", 0, AV_OPT_TYPE_CONST, {.i64 = SINC }, 0, 0, FLAGS, "curve" }, - { "isinc", "inverted sine cardinal function", 0, AV_OPT_TYPE_CONST, {.i64 = ISINC}, 0, 0, FLAGS, "curve" }, - { "curve2", "set fade curve type for 2nd stream", OFFSET(curve2), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, FLAGS, "curve" }, - { "c2", "set fade curve type for 2nd stream", OFFSET(curve2), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, FLAGS, "curve" }, + { "curve1", "set fade curve type for 1st stream", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, FLAGS, .unit = "curve" }, + { "c1", "set fade curve type for 1st stream", OFFSET(curve), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, FLAGS, .unit = "curve" }, + { "nofade", "no fade; keep audio as-is", 0, AV_OPT_TYPE_CONST, {.i64 = NONE }, 0, 0, FLAGS, .unit = "curve" }, + { "tri", "linear slope", 0, AV_OPT_TYPE_CONST, {.i64 = TRI }, 0, 0, FLAGS, .unit = "curve" }, + { "qsin", "quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = QSIN }, 0, 0, FLAGS, .unit = "curve" }, + { "esin", "exponential sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = ESIN }, 0, 0, FLAGS, .unit = "curve" }, + { "hsin", "half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = HSIN }, 0, 0, FLAGS, .unit = "curve" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64 = LOG }, 0, 0, FLAGS, .unit = "curve" }, + { "ipar", "inverted parabola", 0, AV_OPT_TYPE_CONST, {.i64 = IPAR }, 0, 0, FLAGS, .unit = "curve" }, + { "qua", "quadratic", 0, AV_OPT_TYPE_CONST, {.i64 = QUA }, 0, 0, FLAGS, .unit = "curve" }, + { "cub", "cubic", 0, AV_OPT_TYPE_CONST, {.i64 = CUB }, 0, 0, FLAGS, .unit = "curve" }, + { "squ", "square root", 0, AV_OPT_TYPE_CONST, {.i64 = SQU }, 0, 0, FLAGS, .unit = "curve" }, + { "cbr", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64 = CBR }, 0, 0, FLAGS, .unit = "curve" }, + { "par", "parabola", 0, AV_OPT_TYPE_CONST, {.i64 = PAR }, 0, 0, FLAGS, .unit = "curve" }, + { "exp", "exponential", 0, AV_OPT_TYPE_CONST, {.i64 = EXP }, 0, 0, FLAGS, .unit = "curve" }, + { "iqsin", "inverted quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IQSIN}, 0, 0, FLAGS, .unit = "curve" }, + { "ihsin", "inverted half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = IHSIN}, 0, 0, FLAGS, .unit = "curve" }, + { "dese", "double-exponential seat", 0, AV_OPT_TYPE_CONST, {.i64 = DESE }, 0, 0, FLAGS, .unit = "curve" }, + { "desi", "double-exponential sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = DESI }, 0, 0, FLAGS, .unit = "curve" }, + { "losi", "logistic sigmoid", 0, AV_OPT_TYPE_CONST, {.i64 = LOSI }, 0, 0, FLAGS, .unit = "curve" }, + { "sinc", "sine cardinal function", 0, AV_OPT_TYPE_CONST, {.i64 = SINC }, 0, 0, FLAGS, .unit = "curve" }, + { "isinc", "inverted sine cardinal function", 0, AV_OPT_TYPE_CONST, {.i64 = ISINC}, 0, 0, FLAGS, .unit = "curve" }, + { "quat", "quartic", 0, AV_OPT_TYPE_CONST, {.i64 = QUAT }, 0, 0, FLAGS, .unit = "curve" }, + { "quatr", "quartic root", 0, AV_OPT_TYPE_CONST, {.i64 = QUATR}, 0, 0, FLAGS, .unit = "curve" }, + { "qsin2", "squared quarter of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = QSIN2}, 0, 0, FLAGS, .unit = "curve" }, + { "hsin2", "squared half of sine wave", 0, AV_OPT_TYPE_CONST, {.i64 = HSIN2}, 0, 0, FLAGS, .unit = "curve" }, + { "curve2", "set fade curve type for 2nd stream", OFFSET(curve2), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, FLAGS, .unit = "curve" }, + { "c2", "set fade curve type for 2nd stream", OFFSET(curve2), AV_OPT_TYPE_INT, {.i64 = TRI }, NONE, NB_CURVES - 1, FLAGS, .unit = "curve" }, { NULL } }; @@ -521,6 +541,13 @@ CROSSFADE(flt, float) CROSSFADE(s16, int16_t) CROSSFADE(s32, int32_t) +static int check_input(AVFilterLink *inlink) +{ + const int queued_samples = ff_inlink_queued_samples(inlink); + + return ff_inlink_check_available_samples(inlink, queued_samples + 1) == 1; +} + static int activate(AVFilterContext *ctx) { AudioFadeContext *s = ctx->priv; @@ -531,7 +558,7 @@ static int activate(AVFilterContext *ctx) FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); - if (s->crossfade_is_over) { + if (s->passthrough && s->status[0]) { ret = ff_inlink_consume_frame(ctx->inputs[1], &in); if (ret > 0) { in->pts = s->pts; @@ -541,10 +568,10 @@ static int activate(AVFilterContext *ctx) } else if (ret < 0) { return ret; } else if (ff_inlink_acknowledge_status(ctx->inputs[1], &status, &pts)) { - ff_outlink_set_status(ctx->outputs[0], status, pts); + ff_outlink_set_status(outlink, status, pts); return 0; } else if (!ret) { - if (ff_outlink_frame_wanted(ctx->outputs[0])) { + if (ff_outlink_frame_wanted(outlink)) { ff_inlink_request_frame(ctx->inputs[1]); return 0; } @@ -554,6 +581,7 @@ static int activate(AVFilterContext *ctx) nb_samples = ff_inlink_queued_samples(ctx->inputs[0]); if (nb_samples > s->nb_samples) { nb_samples -= s->nb_samples; + s->passthrough = 1; ret = ff_inlink_consume_samples(ctx->inputs[0], nb_samples, nb_samples, &in); if (ret < 0) return ret; @@ -561,7 +589,7 @@ static int activate(AVFilterContext *ctx) s->pts += av_rescale_q(in->nb_samples, (AVRational){ 1, outlink->sample_rate }, outlink->time_base); return ff_filter_frame(outlink, in); - } else if (s->cf0_eof && nb_samples >= s->nb_samples && + } else if (s->status[0] && nb_samples >= s->nb_samples && ff_inlink_queued_samples(ctx->inputs[1]) >= s->nb_samples) { if (s->overlap) { out = ff_get_audio_buffer(outlink, s->nb_samples); @@ -587,7 +615,7 @@ static int activate(AVFilterContext *ctx) out->pts = s->pts; s->pts += av_rescale_q(s->nb_samples, (AVRational){ 1, outlink->sample_rate }, outlink->time_base); - s->crossfade_is_over = 1; + s->passthrough = 1; av_frame_free(&cf[0]); av_frame_free(&cf[1]); return ff_filter_frame(outlink, out); @@ -627,19 +655,20 @@ static int activate(AVFilterContext *ctx) out->pts = s->pts; s->pts += av_rescale_q(s->nb_samples, (AVRational){ 1, outlink->sample_rate }, outlink->time_base); - s->crossfade_is_over = 1; + s->passthrough = 1; av_frame_free(&cf[1]); return ff_filter_frame(outlink, out); } - } else if (ff_outlink_frame_wanted(ctx->outputs[0])) { - if (!s->cf0_eof && ff_outlink_get_status(ctx->inputs[0])) { - s->cf0_eof = 1; - } - if (ff_outlink_get_status(ctx->inputs[1])) { - ff_outlink_set_status(ctx->outputs[0], AVERROR_EOF, AV_NOPTS_VALUE); + } else if (ff_outlink_frame_wanted(outlink)) { + if (!s->status[0] && check_input(ctx->inputs[0])) + s->status[0] = AVERROR_EOF; + s->passthrough = !s->status[0]; + if (check_input(ctx->inputs[1])) { + s->status[1] = AVERROR_EOF; + ff_outlink_set_status(outlink, AVERROR_EOF, AV_NOPTS_VALUE); return 0; } - if (!s->cf0_eof) + if (!s->status[0]) ff_inlink_request_frame(ctx->inputs[0]); else ff_inlink_request_frame(ctx->inputs[1]); @@ -672,14 +701,26 @@ static int acrossfade_config_output(AVFilterLink *outlink) return 0; } +static AVFrame *get_audio_buffer(AVFilterLink *inlink, int nb_samples) +{ + AVFilterContext *ctx = inlink->dst; + AudioFadeContext *s = ctx->priv; + + return s->passthrough ? + ff_null_get_audio_buffer (inlink, nb_samples) : + ff_default_get_audio_buffer(inlink, nb_samples); +} + static const AVFilterPad avfilter_af_acrossfade_inputs[] = { { .name = "crossfade0", .type = AVMEDIA_TYPE_AUDIO, + .get_buffer.audio = get_audio_buffer, }, { .name = "crossfade1", .type = AVMEDIA_TYPE_AUDIO, + .get_buffer.audio = get_audio_buffer, }, }; diff --git a/libavfilter/af_afftdn.c b/libavfilter/af_afftdn.c index de17dc1b2ce..5c8712daf08 100644 --- a/libavfilter/af_afftdn.c +++ b/libavfilter/af_afftdn.c @@ -20,13 +20,13 @@ #include +#include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/channel_layout.h" #include "libavutil/opt.h" #include "libavutil/tx.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #include "filters.h" #define C (M_LN10 * 0.1) @@ -168,16 +168,16 @@ static const AVOption afftdn_options[] = { { "nr", "set the noise reduction", OFFSET(noise_reduction), AV_OPT_TYPE_FLOAT, {.dbl = 12}, .01, 97, AFR }, { "noise_floor", "set the noise floor",OFFSET(noise_floor), AV_OPT_TYPE_FLOAT, {.dbl =-50}, -80,-20, AFR }, { "nf", "set the noise floor", OFFSET(noise_floor), AV_OPT_TYPE_FLOAT, {.dbl =-50}, -80,-20, AFR }, - { "noise_type", "set the noise type", OFFSET(noise_type), AV_OPT_TYPE_INT, {.i64 = WHITE_NOISE}, WHITE_NOISE, NB_NOISE-1, AF, "type" }, - { "nt", "set the noise type", OFFSET(noise_type), AV_OPT_TYPE_INT, {.i64 = WHITE_NOISE}, WHITE_NOISE, NB_NOISE-1, AF, "type" }, - { "white", "white noise", 0, AV_OPT_TYPE_CONST, {.i64 = WHITE_NOISE}, 0, 0, AF, "type" }, - { "w", "white noise", 0, AV_OPT_TYPE_CONST, {.i64 = WHITE_NOISE}, 0, 0, AF, "type" }, - { "vinyl", "vinyl noise", 0, AV_OPT_TYPE_CONST, {.i64 = VINYL_NOISE}, 0, 0, AF, "type" }, - { "v", "vinyl noise", 0, AV_OPT_TYPE_CONST, {.i64 = VINYL_NOISE}, 0, 0, AF, "type" }, - { "shellac", "shellac noise", 0, AV_OPT_TYPE_CONST, {.i64 = SHELLAC_NOISE}, 0, 0, AF, "type" }, - { "s", "shellac noise", 0, AV_OPT_TYPE_CONST, {.i64 = SHELLAC_NOISE}, 0, 0, AF, "type" }, - { "custom", "custom noise", 0, AV_OPT_TYPE_CONST, {.i64 = CUSTOM_NOISE}, 0, 0, AF, "type" }, - { "c", "custom noise", 0, AV_OPT_TYPE_CONST, {.i64 = CUSTOM_NOISE}, 0, 0, AF, "type" }, + { "noise_type", "set the noise type", OFFSET(noise_type), AV_OPT_TYPE_INT, {.i64 = WHITE_NOISE}, WHITE_NOISE, NB_NOISE-1, AF, .unit = "type" }, + { "nt", "set the noise type", OFFSET(noise_type), AV_OPT_TYPE_INT, {.i64 = WHITE_NOISE}, WHITE_NOISE, NB_NOISE-1, AF, .unit = "type" }, + { "white", "white noise", 0, AV_OPT_TYPE_CONST, {.i64 = WHITE_NOISE}, 0, 0, AF, .unit = "type" }, + { "w", "white noise", 0, AV_OPT_TYPE_CONST, {.i64 = WHITE_NOISE}, 0, 0, AF, .unit = "type" }, + { "vinyl", "vinyl noise", 0, AV_OPT_TYPE_CONST, {.i64 = VINYL_NOISE}, 0, 0, AF, .unit = "type" }, + { "v", "vinyl noise", 0, AV_OPT_TYPE_CONST, {.i64 = VINYL_NOISE}, 0, 0, AF, .unit = "type" }, + { "shellac", "shellac noise", 0, AV_OPT_TYPE_CONST, {.i64 = SHELLAC_NOISE}, 0, 0, AF, .unit = "type" }, + { "s", "shellac noise", 0, AV_OPT_TYPE_CONST, {.i64 = SHELLAC_NOISE}, 0, 0, AF, .unit = "type" }, + { "custom", "custom noise", 0, AV_OPT_TYPE_CONST, {.i64 = CUSTOM_NOISE}, 0, 0, AF, .unit = "type" }, + { "c", "custom noise", 0, AV_OPT_TYPE_CONST, {.i64 = CUSTOM_NOISE}, 0, 0, AF, .unit = "type" }, { "band_noise", "set the custom bands noise", OFFSET(band_noise_str), AV_OPT_TYPE_STRING, {.str = 0}, 0, 0, AF }, { "bn", "set the custom bands noise", OFFSET(band_noise_str), AV_OPT_TYPE_STRING, {.str = 0}, 0, 0, AF }, { "residual_floor", "set the residual floor",OFFSET(residual_floor), AV_OPT_TYPE_FLOAT, {.dbl =-38}, -80,-20, AFR }, @@ -186,33 +186,33 @@ static const AVOption afftdn_options[] = { { "tn", "track noise", OFFSET(track_noise), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AFR }, { "track_residual", "track residual", OFFSET(track_residual), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AFR }, { "tr", "track residual", OFFSET(track_residual), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AFR }, - { "output_mode", "set output mode", OFFSET(output_mode), AV_OPT_TYPE_INT, {.i64 = OUT_MODE}, 0, NB_MODES-1, AFR, "mode" }, - { "om", "set output mode", OFFSET(output_mode), AV_OPT_TYPE_INT, {.i64 = OUT_MODE}, 0, NB_MODES-1, AFR, "mode" }, - { "input", "input", 0, AV_OPT_TYPE_CONST, {.i64 = IN_MODE}, 0, 0, AFR, "mode" }, - { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64 = IN_MODE}, 0, 0, AFR, "mode" }, - { "output", "output", 0, AV_OPT_TYPE_CONST, {.i64 = OUT_MODE}, 0, 0, AFR, "mode" }, - { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64 = OUT_MODE}, 0, 0, AFR, "mode" }, - { "noise", "noise", 0, AV_OPT_TYPE_CONST, {.i64 = NOISE_MODE}, 0, 0, AFR, "mode" }, - { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64 = NOISE_MODE}, 0, 0, AFR, "mode" }, + { "output_mode", "set output mode", OFFSET(output_mode), AV_OPT_TYPE_INT, {.i64 = OUT_MODE}, 0, NB_MODES-1, AFR, .unit = "mode" }, + { "om", "set output mode", OFFSET(output_mode), AV_OPT_TYPE_INT, {.i64 = OUT_MODE}, 0, NB_MODES-1, AFR, .unit = "mode" }, + { "input", "input", 0, AV_OPT_TYPE_CONST, {.i64 = IN_MODE}, 0, 0, AFR, .unit = "mode" }, + { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64 = IN_MODE}, 0, 0, AFR, .unit = "mode" }, + { "output", "output", 0, AV_OPT_TYPE_CONST, {.i64 = OUT_MODE}, 0, 0, AFR, .unit = "mode" }, + { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64 = OUT_MODE}, 0, 0, AFR, .unit = "mode" }, + { "noise", "noise", 0, AV_OPT_TYPE_CONST, {.i64 = NOISE_MODE}, 0, 0, AFR, .unit = "mode" }, + { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64 = NOISE_MODE}, 0, 0, AFR, .unit = "mode" }, { "adaptivity", "set adaptivity factor",OFFSET(ratio), AV_OPT_TYPE_FLOAT, {.dbl = 0.5}, 0, 1, AFR }, { "ad", "set adaptivity factor",OFFSET(ratio), AV_OPT_TYPE_FLOAT, {.dbl = 0.5}, 0, 1, AFR }, { "floor_offset", "set noise floor offset factor",OFFSET(floor_offset), AV_OPT_TYPE_FLOAT, {.dbl = 1.0}, -2, 2, AFR }, { "fo", "set noise floor offset factor",OFFSET(floor_offset), AV_OPT_TYPE_FLOAT, {.dbl = 1.0}, -2, 2, AFR }, - { "noise_link", "set the noise floor link",OFFSET(noise_floor_link),AV_OPT_TYPE_INT,{.i64 = MIN_LINK}, 0, NB_LINK-1, AFR, "link" }, - { "nl", "set the noise floor link", OFFSET(noise_floor_link),AV_OPT_TYPE_INT,{.i64 = MIN_LINK}, 0, NB_LINK-1, AFR, "link" }, - { "none", "none", 0, AV_OPT_TYPE_CONST, {.i64 = NONE_LINK}, 0, 0, AFR, "link" }, - { "min", "min", 0, AV_OPT_TYPE_CONST, {.i64 = MIN_LINK}, 0, 0, AFR, "link" }, - { "max", "max", 0, AV_OPT_TYPE_CONST, {.i64 = MAX_LINK}, 0, 0, AFR, "link" }, - { "average", "average", 0, AV_OPT_TYPE_CONST, {.i64 = AVERAGE_LINK}, 0, 0, AFR, "link" }, + { "noise_link", "set the noise floor link",OFFSET(noise_floor_link),AV_OPT_TYPE_INT,{.i64 = MIN_LINK}, 0, NB_LINK-1, AFR, .unit = "link" }, + { "nl", "set the noise floor link", OFFSET(noise_floor_link),AV_OPT_TYPE_INT,{.i64 = MIN_LINK}, 0, NB_LINK-1, AFR, .unit = "link" }, + { "none", "none", 0, AV_OPT_TYPE_CONST, {.i64 = NONE_LINK}, 0, 0, AFR, .unit = "link" }, + { "min", "min", 0, AV_OPT_TYPE_CONST, {.i64 = MIN_LINK}, 0, 0, AFR, .unit = "link" }, + { "max", "max", 0, AV_OPT_TYPE_CONST, {.i64 = MAX_LINK}, 0, 0, AFR, .unit = "link" }, + { "average", "average", 0, AV_OPT_TYPE_CONST, {.i64 = AVERAGE_LINK}, 0, 0, AFR, .unit = "link" }, { "band_multiplier", "set band multiplier",OFFSET(band_multiplier), AV_OPT_TYPE_FLOAT,{.dbl = 1.25}, 0.2,5, AF }, { "bm", "set band multiplier", OFFSET(band_multiplier), AV_OPT_TYPE_FLOAT,{.dbl = 1.25}, 0.2,5, AF }, - { "sample_noise", "set sample noise mode",OFFSET(sample_noise_mode),AV_OPT_TYPE_INT,{.i64 = SAMPLE_NONE}, 0, NB_SAMPLEMODES-1, AFR, "sample" }, - { "sn", "set sample noise mode",OFFSET(sample_noise_mode),AV_OPT_TYPE_INT,{.i64 = SAMPLE_NONE}, 0, NB_SAMPLEMODES-1, AFR, "sample" }, - { "none", "none", 0, AV_OPT_TYPE_CONST, {.i64 = SAMPLE_NONE}, 0, 0, AFR, "sample" }, - { "start", "start", 0, AV_OPT_TYPE_CONST, {.i64 = SAMPLE_START}, 0, 0, AFR, "sample" }, - { "begin", "start", 0, AV_OPT_TYPE_CONST, {.i64 = SAMPLE_START}, 0, 0, AFR, "sample" }, - { "stop", "stop", 0, AV_OPT_TYPE_CONST, {.i64 = SAMPLE_STOP}, 0, 0, AFR, "sample" }, - { "end", "stop", 0, AV_OPT_TYPE_CONST, {.i64 = SAMPLE_STOP}, 0, 0, AFR, "sample" }, + { "sample_noise", "set sample noise mode",OFFSET(sample_noise_mode),AV_OPT_TYPE_INT,{.i64 = SAMPLE_NONE}, 0, NB_SAMPLEMODES-1, AFR, .unit = "sample" }, + { "sn", "set sample noise mode",OFFSET(sample_noise_mode),AV_OPT_TYPE_INT,{.i64 = SAMPLE_NONE}, 0, NB_SAMPLEMODES-1, AFR, .unit = "sample" }, + { "none", "none", 0, AV_OPT_TYPE_CONST, {.i64 = SAMPLE_NONE}, 0, 0, AFR, .unit = "sample" }, + { "start", "start", 0, AV_OPT_TYPE_CONST, {.i64 = SAMPLE_START}, 0, 0, AFR, .unit = "sample" }, + { "begin", "start", 0, AV_OPT_TYPE_CONST, {.i64 = SAMPLE_START}, 0, 0, AFR, .unit = "sample" }, + { "stop", "stop", 0, AV_OPT_TYPE_CONST, {.i64 = SAMPLE_STOP}, 0, 0, AFR, .unit = "sample" }, + { "end", "stop", 0, AV_OPT_TYPE_CONST, {.i64 = SAMPLE_STOP}, 0, 0, AFR, .unit = "sample" }, { "gain_smooth", "set gain smooth radius",OFFSET(gain_smooth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 50, AFR }, { "gs", "set gain smooth radius",OFFSET(gain_smooth), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 50, AFR }, { NULL } @@ -376,6 +376,8 @@ static void process_frame(AVFilterContext *ctx, case AV_SAMPLE_FMT_DBLP: noisy_data[i] = mag = hypot(fft_data_dbl[i].re, fft_data_dbl[i].im); break; + default: + av_assert2(0); } power = mag * mag; @@ -970,6 +972,8 @@ static void sample_noise_block(AudioFFTDeNoiseContext *s, mag2 = fft_out_dbl[n].re * fft_out_dbl[n].re + fft_out_dbl[n].im * fft_out_dbl[n].im; break; + default: + av_assert2(0); } mag2 = fmax(mag2, s->sample_floor); @@ -1357,13 +1361,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_afftdn = { .name = "afftdn", .description = NULL_IF_CONFIG_SMALL("Denoise audio samples using FFT."), @@ -1372,7 +1369,7 @@ const AVFilter ff_af_afftdn = { .activate = activate, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | diff --git a/libavfilter/af_afftfilt.c b/libavfilter/af_afftfilt.c index 9b09dcaa39f..2b2a70b0b67 100644 --- a/libavfilter/af_afftfilt.c +++ b/libavfilter/af_afftfilt.c @@ -439,20 +439,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_afftfilt = { .name = "afftfilt", .description = NULL_IF_CONFIG_SMALL("Apply arbitrary expressions to samples in frequency domain."), .priv_size = sizeof(AFFTFiltContext), .priv_class = &afftfilt_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .activate = activate, .uninit = uninit, diff --git a/libavfilter/af_afir.c b/libavfilter/af_afir.c index 0fe038972f0..9df9c28c7b7 100644 --- a/libavfilter/af_afir.c +++ b/libavfilter/af_afir.c @@ -25,6 +25,7 @@ #include +#include "libavutil/avassert.h" #include "libavutil/cpu.h" #include "libavutil/tx.h" #include "libavutil/avstring.h" @@ -36,7 +37,6 @@ #include "libavutil/log.h" #include "libavutil/opt.h" #include "libavutil/rational.h" -#include "libavutil/xga_font_data.h" #include "audio.h" #include "avfilter.h" @@ -45,55 +45,7 @@ #include "internal.h" #include "af_afir.h" #include "af_afirdsp.h" - -static void drawtext(AVFrame *pic, int x, int y, const char *txt, uint32_t color) -{ - const uint8_t *font; - int font_height; - int i; - - font = avpriv_cga_font, font_height = 8; - - for (i = 0; txt[i]; i++) { - int char_y, mask; - - uint8_t *p = pic->data[0] + y * pic->linesize[0] + (x + i * 8) * 4; - for (char_y = 0; char_y < font_height; char_y++) { - for (mask = 0x80; mask; mask >>= 1) { - if (font[txt[i] * font_height + char_y] & mask) - AV_WL32(p, color); - p += 4; - } - p += pic->linesize[0] - 8 * 4; - } - } -} - -static void draw_line(AVFrame *out, int x0, int y0, int x1, int y1, uint32_t color) -{ - int dx = FFABS(x1-x0); - int dy = FFABS(y1-y0), sy = y0 < y1 ? 1 : -1; - int err = (dx>dy ? dx : -dy) / 2, e2; - - for (;;) { - AV_WL32(out->data[0] + y0 * out->linesize[0] + x0 * 4, color); - - if (x0 == x1 && y0 == y1) - break; - - e2 = err; - - if (e2 >-dx) { - err -= dy; - x0--; - } - - if (e2 < dy) { - err += dx; - y0 += sy; - } - } -} +#include "video.h" #define DEPTH 32 #include "afir_template.c" @@ -106,16 +58,21 @@ static int fir_channel(AVFilterContext *ctx, AVFrame *out, int ch) { AudioFIRContext *s = ctx->priv; const int min_part_size = s->min_part_size; + const int prev_selir = s->prev_selir; + const int selir = s->selir; for (int offset = 0; offset < out->nb_samples; offset += min_part_size) { switch (s->format) { case AV_SAMPLE_FMT_FLTP: - fir_quantum_float(ctx, out, ch, offset); + fir_quantums_float(ctx, s, out, min_part_size, ch, offset, prev_selir, selir); break; case AV_SAMPLE_FMT_DBLP: - fir_quantum_double(ctx, out, ch, offset); + fir_quantums_double(ctx, s, out, min_part_size, ch, offset, prev_selir, selir); break; } + + if (selir != prev_selir && s->loading[ch] != 0) + s->loading[ch] += min_part_size; } return 0; @@ -149,6 +106,7 @@ static int fir_frame(AudioFIRContext *s, AVFrame *in, AVFilterLink *outlink) s->in = in; ff_filter_execute(ctx, fir_channels, out, NULL, FFMIN(outlink->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx))); + s->prev_is_disabled = ctx->is_disabled; av_frame_free(&in); s->in = NULL; @@ -173,16 +131,15 @@ static int init_segment(AVFilterContext *ctx, AudioFIRSegment *seg, int selir, seg->fft_length = (part_size + 1) * 2; seg->part_size = part_size; - seg->block_size = FFALIGN(seg->fft_length, cpu_align); seg->coeff_size = FFALIGN(seg->part_size + 1, cpu_align); + seg->block_size = FFMAX(seg->coeff_size * 2, FFALIGN(seg->fft_length, cpu_align)); seg->nb_partitions = nb_partitions; seg->input_size = offset + s->min_part_size; seg->input_offset = offset; - seg->loading = av_calloc(ctx->inputs[0]->ch_layout.nb_channels, sizeof(*seg->loading)); seg->part_index = av_calloc(ctx->inputs[0]->ch_layout.nb_channels, sizeof(*seg->part_index)); seg->output_offset = av_calloc(ctx->inputs[0]->ch_layout.nb_channels, sizeof(*seg->output_offset)); - if (!seg->part_index || !seg->output_offset || !seg->loading) + if (!seg->part_index || !seg->output_offset) return AVERROR(ENOMEM); switch (s->format) { @@ -198,6 +155,8 @@ static int init_segment(AVFilterContext *ctx, AudioFIRSegment *seg, int selir, iscale.d = 1.0 / sqrt(2.0 * part_size); tx_type = AV_TX_DOUBLE_RDFT; break; + default: + av_assert1(0); } for (int ch = 0; ch < ctx->inputs[0]->ch_layout.nb_channels && part_size >= 1; ch++) { @@ -253,7 +212,6 @@ static void uninit_segment(AVFilterContext *ctx, AudioFIRSegment *seg) } av_freep(&seg->itx); - av_freep(&seg->loading); av_freep(&seg->output_offset); av_freep(&seg->part_index); @@ -268,7 +226,7 @@ static void uninit_segment(AVFilterContext *ctx, AudioFIRSegment *seg) seg->input_size = 0; for (int i = 0; i < MAX_IR_STREAMS; i++) - av_frame_free(&seg->coeff[i]); + av_frame_free(&seg->coeff); } static int convert_coeffs(AVFilterContext *ctx, int selir) @@ -287,24 +245,23 @@ static int convert_coeffs(AVFilterContext *ctx, int selir) if (s->minp > s->maxp) s->maxp = s->minp; - if (s->nb_segments) + if (s->nb_segments[selir]) goto skip; left = s->nb_taps[selir]; part_size = 1 << av_log2(s->minp); max_part_size = 1 << av_log2(s->maxp); - s->min_part_size = part_size; - for (int i = 0; left > 0; i++) { - int step = part_size == max_part_size ? INT_MAX : 1 + (i == 0); + int step = (part_size == max_part_size) ? INT_MAX : 1 + (i == 0); int nb_partitions = FFMIN(step, (left + part_size - 1) / part_size); - s->nb_segments = i + 1; - ret = init_segment(ctx, &s->seg[i], selir, offset, nb_partitions, part_size, i); + s->nb_segments[selir] = i + 1; + ret = init_segment(ctx, &s->seg[selir][i], selir, offset, nb_partitions, part_size, i); if (ret < 0) return ret; offset += nb_partitions * part_size; + s->max_offset[selir] = offset; left -= nb_partitions * part_size; part_size *= 2; part_size = FFMIN(part_size, max_part_size); @@ -320,17 +277,6 @@ static int convert_coeffs(AVFilterContext *ctx, int selir) return AVERROR_BUG; } - if (s->response) { - switch (s->format) { - case AV_SAMPLE_FMT_FLTP: - draw_response_float(ctx, s->video); - break; - case AV_SAMPLE_FMT_DBLP: - draw_response_double(ctx, s->video); - break; - } - } - cur_nb_taps = s->ir[selir]->nb_samples; nb_taps = cur_nb_taps; @@ -342,10 +288,26 @@ static int convert_coeffs(AVFilterContext *ctx, int selir) } av_log(ctx, AV_LOG_DEBUG, "nb_taps: %d\n", cur_nb_taps); - av_log(ctx, AV_LOG_DEBUG, "nb_segments: %d\n", s->nb_segments); + av_log(ctx, AV_LOG_DEBUG, "nb_segments: %d\n", s->nb_segments[selir]); switch (s->format) { case AV_SAMPLE_FMT_FLTP: + for (int ch = 0; ch < s->nb_channels; ch++) { + const float *tsrc = (const float *)s->ir[selir]->extended_data[!s->one2many * ch]; + + s->ch_gain[ch] = ir_gain_float(ctx, s, nb_taps, tsrc); + } + + if (s->ir_link) { + float gain = +INFINITY; + + for (int ch = 0; ch < s->nb_channels; ch++) + gain = fminf(gain, s->ch_gain[ch]); + + for (int ch = 0; ch < s->nb_channels; ch++) + s->ch_gain[ch] = gain; + } + for (int ch = 0; ch < s->nb_channels; ch++) { const float *tsrc = (const float *)s->ir[selir]->extended_data[!s->one2many * ch]; float *time = (float *)s->norm_ir[selir]->extended_data[ch]; @@ -354,14 +316,14 @@ static int convert_coeffs(AVFilterContext *ctx, int selir) for (int i = FFMAX(1, s->length * nb_taps); i < nb_taps; i++) time[i] = 0; - get_power_float(ctx, s, nb_taps, ch, time); + ir_scale_float(ctx, s, nb_taps, ch, time, s->ch_gain[ch]); - for (int n = 0; n < s->nb_segments; n++) { - AudioFIRSegment *seg = &s->seg[n]; + for (int n = 0; n < s->nb_segments[selir]; n++) { + AudioFIRSegment *seg = &s->seg[selir][n]; - if (!seg->coeff[selir]) - seg->coeff[selir] = ff_get_audio_buffer(ctx->inputs[0], seg->nb_partitions * seg->coeff_size * 2); - if (!seg->coeff[selir]) + if (!seg->coeff) + seg->coeff = ff_get_audio_buffer(ctx->inputs[0], seg->nb_partitions * seg->coeff_size * 2); + if (!seg->coeff) return AVERROR(ENOMEM); for (int i = 0; i < seg->nb_partitions; i++) @@ -370,6 +332,22 @@ static int convert_coeffs(AVFilterContext *ctx, int selir) } break; case AV_SAMPLE_FMT_DBLP: + for (int ch = 0; ch < s->nb_channels; ch++) { + const double *tsrc = (const double *)s->ir[selir]->extended_data[!s->one2many * ch]; + + s->ch_gain[ch] = ir_gain_double(ctx, s, nb_taps, tsrc); + } + + if (s->ir_link) { + double gain = +INFINITY; + + for (int ch = 0; ch < s->nb_channels; ch++) + gain = fmin(gain, s->ch_gain[ch]); + + for (int ch = 0; ch < s->nb_channels; ch++) + s->ch_gain[ch] = gain; + } + for (int ch = 0; ch < s->nb_channels; ch++) { const double *tsrc = (const double *)s->ir[selir]->extended_data[!s->one2many * ch]; double *time = (double *)s->norm_ir[selir]->extended_data[ch]; @@ -378,13 +356,14 @@ static int convert_coeffs(AVFilterContext *ctx, int selir) for (int i = FFMAX(1, s->length * nb_taps); i < nb_taps; i++) time[i] = 0; - get_power_double(ctx, s, nb_taps, ch, time); - for (int n = 0; n < s->nb_segments; n++) { - AudioFIRSegment *seg = &s->seg[n]; + ir_scale_double(ctx, s, nb_taps, ch, time, s->ch_gain[ch]); + + for (int n = 0; n < s->nb_segments[selir]; n++) { + AudioFIRSegment *seg = &s->seg[selir][n]; - if (!seg->coeff[selir]) - seg->coeff[selir] = ff_get_audio_buffer(ctx->inputs[0], seg->nb_partitions * seg->coeff_size * 2); - if (!seg->coeff[selir]) + if (!seg->coeff) + seg->coeff = ff_get_audio_buffer(ctx->inputs[0], seg->nb_partitions * seg->coeff_size * 2); + if (!seg->coeff) return AVERROR(ENOMEM); for (int i = 0; i < seg->nb_partitions; i++) @@ -399,7 +378,7 @@ static int convert_coeffs(AVFilterContext *ctx, int selir) return 0; } -static int check_ir(AVFilterLink *link) +static int check_ir(AVFilterLink *link, int selir) { AVFilterContext *ctx = link->dst; AudioFIRContext *s = ctx->priv; @@ -412,6 +391,9 @@ static int check_ir(AVFilterLink *link) return AVERROR(EINVAL); } + if (ff_inlink_check_available_samples(link, nb_taps + 1) == 1) + s->eof_coeffs[selir] = 1; + return 0; } @@ -424,29 +406,30 @@ static int activate(AVFilterContext *ctx) int64_t pts; FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); - if (s->response) - FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[1], ctx); - if (!s->eof_coeffs[s->selir]) { - ret = check_ir(ctx->inputs[1 + s->selir]); - if (ret < 0) - return ret; - if (ff_outlink_get_status(ctx->inputs[1 + s->selir]) == AVERROR_EOF) - s->eof_coeffs[s->selir] = 1; + for (int i = 0; i < s->nb_irs; i++) { + const int selir = i; + + if (s->ir_load && selir != s->selir) + continue; - if (!s->eof_coeffs[s->selir]) { - if (ff_outlink_frame_wanted(ctx->outputs[0])) - ff_inlink_request_frame(ctx->inputs[1 + s->selir]); - else if (s->response && ff_outlink_frame_wanted(ctx->outputs[1])) - ff_inlink_request_frame(ctx->inputs[1 + s->selir]); - return 0; + if (!s->eof_coeffs[selir]) { + ret = check_ir(ctx->inputs[1 + selir], selir); + if (ret < 0) + return ret; + + if (!s->eof_coeffs[selir]) { + if (ff_outlink_frame_wanted(ctx->outputs[0])) + ff_inlink_request_frame(ctx->inputs[1 + selir]); + return 0; + } } - } - if (!s->have_coeffs[s->selir] && s->eof_coeffs[s->selir]) { - ret = convert_coeffs(ctx, s->selir); - if (ret < 0) - return ret; + if (!s->have_coeffs[selir] && s->eof_coeffs[selir]) { + ret = convert_coeffs(ctx, selir); + if (ret < 0) + return ret; + } } available = ff_inlink_queued_samples(ctx->inputs[0]); @@ -455,23 +438,12 @@ static int activate(AVFilterContext *ctx) if (ret > 0) ret = fir_frame(s, in, outlink); + if (s->selir != s->prev_selir && s->loading[0] == 0) + s->prev_selir = s->selir; + if (ret < 0) return ret; - if (s->response && s->have_coeffs[s->selir]) { - int64_t old_pts = s->video->pts; - int64_t new_pts = av_rescale_q(s->pts, ctx->inputs[0]->time_base, ctx->outputs[1]->time_base); - - if (ff_outlink_frame_wanted(ctx->outputs[1]) && old_pts < new_pts) { - AVFrame *clone; - s->video->pts = new_pts; - clone = av_frame_clone(s->video); - if (!clone) - return AVERROR(ENOMEM); - return ff_filter_frame(ctx->outputs[1], clone); - } - } - if (ff_inlink_queued_samples(ctx->inputs[0]) >= s->min_part_size) { ff_filter_set_ready(ctx, 10); return 0; @@ -480,21 +452,11 @@ static int activate(AVFilterContext *ctx) if (ff_inlink_acknowledge_status(ctx->inputs[0], &status, &pts)) { if (status == AVERROR_EOF) { ff_outlink_set_status(ctx->outputs[0], status, pts); - if (s->response) - ff_outlink_set_status(ctx->outputs[1], status, pts); return 0; } } - if (ff_outlink_frame_wanted(ctx->outputs[0]) && - !ff_outlink_get_status(ctx->inputs[0])) { - ff_inlink_request_frame(ctx->inputs[0]); - return 0; - } - - if (s->response && - ff_outlink_frame_wanted(ctx->outputs[1]) && - !ff_outlink_get_status(ctx->inputs[0])) { + if (ff_outlink_frame_wanted(ctx->outputs[0])) { ff_inlink_request_frame(ctx->inputs[0]); return 0; } @@ -510,19 +472,8 @@ static int query_formats(AVFilterContext *ctx) { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, { AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE }, }; - static const enum AVPixelFormat pix_fmts[] = { - AV_PIX_FMT_RGB0, - AV_PIX_FMT_NONE - }; int ret; - if (s->response) { - AVFilterLink *videolink = ctx->outputs[1]; - AVFilterFormats *formats = ff_make_format_list(pix_fmts); - if ((ret = ff_formats_ref(formats, &videolink->incfg.formats)) < 0) - return ret; - } - if (s->ir_format) { ret = ff_set_common_all_channel_counts(ctx); if (ret < 0) @@ -560,17 +511,51 @@ static int config_output(AVFilterLink *outlink) s->one2many = ctx->inputs[1 + s->selir]->ch_layout.nb_channels == 1; outlink->sample_rate = ctx->inputs[0]->sample_rate; outlink->time_base = ctx->inputs[0]->time_base; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - outlink->channel_layout = ctx->inputs[0]->channel_layout; -FF_ENABLE_DEPRECATION_WARNINGS -#endif if ((ret = av_channel_layout_copy(&outlink->ch_layout, &ctx->inputs[0]->ch_layout)) < 0) return ret; outlink->ch_layout.nb_channels = ctx->inputs[0]->ch_layout.nb_channels; s->format = outlink->format; s->nb_channels = outlink->ch_layout.nb_channels; + s->ch_gain = av_calloc(ctx->inputs[0]->ch_layout.nb_channels, sizeof(*s->ch_gain)); + s->loading = av_calloc(ctx->inputs[0]->ch_layout.nb_channels, sizeof(*s->loading)); + if (!s->loading || !s->ch_gain) + return AVERROR(ENOMEM); + + s->fadein[0] = ff_get_audio_buffer(outlink, s->min_part_size); + s->fadein[1] = ff_get_audio_buffer(outlink, s->min_part_size); + if (!s->fadein[0] || !s->fadein[1]) + return AVERROR(ENOMEM); + + s->xfade[0] = ff_get_audio_buffer(outlink, s->min_part_size); + s->xfade[1] = ff_get_audio_buffer(outlink, s->min_part_size); + if (!s->xfade[0] || !s->xfade[1]) + return AVERROR(ENOMEM); + + switch (s->format) { + case AV_SAMPLE_FMT_FLTP: + for (int ch = 0; ch < s->nb_channels; ch++) { + float *dst0 = (float *)s->xfade[0]->extended_data[ch]; + float *dst1 = (float *)s->xfade[1]->extended_data[ch]; + + for (int n = 0; n < s->min_part_size; n++) { + dst0[n] = (n + 1.f) / s->min_part_size; + dst1[n] = 1.f - dst0[n]; + } + } + break; + case AV_SAMPLE_FMT_DBLP: + for (int ch = 0; ch < s->nb_channels; ch++) { + double *dst0 = (double *)s->xfade[0]->extended_data[ch]; + double *dst1 = (double *)s->xfade[1]->extended_data[ch]; + + for (int n = 0; n < s->min_part_size; n++) { + dst0[n] = (n + 1.0) / s->min_part_size; + dst1[n] = 1.0 - dst0[n]; + } + } + break; + } return 0; } @@ -579,42 +564,29 @@ static av_cold void uninit(AVFilterContext *ctx) { AudioFIRContext *s = ctx->priv; - for (int i = 0; i < s->nb_segments; i++) - uninit_segment(ctx, &s->seg[i]); - av_freep(&s->fdsp); + av_freep(&s->ch_gain); + av_freep(&s->loading); for (int i = 0; i < s->nb_irs; i++) { + for (int j = 0; j < s->nb_segments[i]; j++) + uninit_segment(ctx, &s->seg[i][j]); + av_frame_free(&s->ir[i]); av_frame_free(&s->norm_ir[i]); } - av_frame_free(&s->video); -} - -static int config_video(AVFilterLink *outlink) -{ - AVFilterContext *ctx = outlink->src; - AudioFIRContext *s = ctx->priv; - - outlink->sample_aspect_ratio = (AVRational){1,1}; - outlink->w = s->w; - outlink->h = s->h; - outlink->frame_rate = s->frame_rate; - outlink->time_base = av_inv_q(outlink->frame_rate); - - av_frame_free(&s->video); - s->video = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!s->video) - return AVERROR(ENOMEM); + av_frame_free(&s->fadein[0]); + av_frame_free(&s->fadein[1]); - return 0; + av_frame_free(&s->xfade[0]); + av_frame_free(&s->xfade[1]); } static av_cold int init(AVFilterContext *ctx) { AudioFIRContext *s = ctx->priv; - AVFilterPad pad, vpad; + AVFilterPad pad; int ret; s->prev_selir = FFMIN(s->nb_irs - 1, s->selir); @@ -642,34 +614,15 @@ static av_cold int init(AVFilterContext *ctx) return ret; } - pad = (AVFilterPad) { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .config_props = config_output, - }; - - ret = ff_append_outpad(ctx, &pad); - if (ret < 0) - return ret; - - if (s->response) { - vpad = (AVFilterPad){ - .name = "filter_response", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_video, - }; - - ret = ff_append_outpad(ctx, &vpad); - if (ret < 0) - return ret; - } - s->fdsp = avpriv_float_dsp_alloc(0); if (!s->fdsp) return AVERROR(ENOMEM); ff_afir_init(&s->afirdsp); + s->min_part_size = 1 << av_log2(s->minp); + s->max_part_size = 1 << av_log2(s->maxp); + return 0; } @@ -691,12 +644,9 @@ static int process_command(AVFilterContext *ctx, s->selir = FFMIN(s->nb_irs - 1, s->selir); if (s->selir != prev_selir) { s->prev_selir = prev_selir; - for (int n = 0; n < s->nb_segments; n++) { - AudioFIRSegment *seg = &s->seg[n]; - for (int ch = 0; ch < s->nb_channels; ch++) - seg->loading[ch] = 0; - } + for (int ch = 0; ch < s->nb_channels; ch++) + s->loading[ch] = 1; } return 0; @@ -711,46 +661,60 @@ static const AVOption afir_options[] = { { "dry", "set dry gain", OFFSET(dry_gain), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 10, AFR }, { "wet", "set wet gain", OFFSET(wet_gain), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 10, AFR }, { "length", "set IR length", OFFSET(length), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 1, AF }, - { "gtype", "set IR auto gain type",OFFSET(gtype), AV_OPT_TYPE_INT, {.i64=0}, -1, 4, AF, "gtype" }, - { "none", "without auto gain", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, AF, "gtype" }, - { "peak", "peak gain", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "gtype" }, - { "dc", "DC gain", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "gtype" }, - { "gn", "gain to noise", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, "gtype" }, - { "ac", "AC gain", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, AF, "gtype" }, - { "rms", "RMS gain", 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, AF, "gtype" }, + { "gtype", "set IR auto gain type",OFFSET(gtype), AV_OPT_TYPE_INT, {.i64=0}, -1, 4, AF|AV_OPT_FLAG_DEPRECATED, .unit = "gtype" }, + { "none", "without auto gain", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, AF|AV_OPT_FLAG_DEPRECATED, .unit = "gtype" }, + { "peak", "peak gain", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF|AV_OPT_FLAG_DEPRECATED, .unit = "gtype" }, + { "dc", "DC gain", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF|AV_OPT_FLAG_DEPRECATED, .unit = "gtype" }, + { "gn", "gain to noise", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF|AV_OPT_FLAG_DEPRECATED, .unit = "gtype" }, + { "ac", "AC gain", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, AF|AV_OPT_FLAG_DEPRECATED, .unit = "gtype" }, + { "rms", "RMS gain", 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, AF|AV_OPT_FLAG_DEPRECATED, .unit = "gtype" }, + { "irnorm", "set IR norm", OFFSET(ir_norm), AV_OPT_TYPE_FLOAT, {.dbl=1}, -1, 2, AF }, + { "irlink", "set IR link", OFFSET(ir_link), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, AF }, { "irgain", "set IR gain", OFFSET(ir_gain), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 1, AF }, - { "irfmt", "set IR format", OFFSET(ir_format), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, AF, "irfmt" }, - { "mono", "single channel", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "irfmt" }, - { "input", "same as input", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "irfmt" }, + { "irfmt", "set IR format", OFFSET(ir_format), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, AF, .unit = "irfmt" }, + { "mono", "single channel", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "irfmt" }, + { "input", "same as input", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "irfmt" }, { "maxir", "set max IR length", OFFSET(max_ir_len), AV_OPT_TYPE_FLOAT, {.dbl=30}, 0.1, 60, AF }, - { "response", "show IR frequency response", OFFSET(response), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, VF }, - { "channel", "set IR channel to display frequency response", OFFSET(ir_channel), AV_OPT_TYPE_INT, {.i64=0}, 0, 1024, VF }, - { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "hd720"}, 0, 0, VF }, - { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT32_MAX, VF }, + { "response", "show IR frequency response", OFFSET(response), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, VF|AV_OPT_FLAG_DEPRECATED }, + { "channel", "set IR channel to display frequency response", OFFSET(ir_channel), AV_OPT_TYPE_INT, {.i64=0}, 0, 1024, VF|AV_OPT_FLAG_DEPRECATED }, + { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "hd720"}, 0, 0, VF|AV_OPT_FLAG_DEPRECATED }, + { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT32_MAX, VF|AV_OPT_FLAG_DEPRECATED }, { "minp", "set min partition size", OFFSET(minp), AV_OPT_TYPE_INT, {.i64=8192}, 1, 65536, AF }, { "maxp", "set max partition size", OFFSET(maxp), AV_OPT_TYPE_INT, {.i64=8192}, 8, 65536, AF }, { "nbirs", "set number of input IRs",OFFSET(nb_irs),AV_OPT_TYPE_INT, {.i64=1}, 1, 32, AF }, { "ir", "select IR", OFFSET(selir), AV_OPT_TYPE_INT, {.i64=0}, 0, 31, AFR }, - { "precision", "set processing precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, AF, "precision" }, - { "auto", "set auto processing precision", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "precision" }, - { "float", "set single-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "precision" }, - { "double","set double-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, "precision" }, + { "precision", "set processing precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, AF, .unit = "precision" }, + { "auto", "set auto processing precision", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "precision" }, + { "float", "set single-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "precision" }, + { "double","set double-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, .unit = "precision" }, + { "irload", "set IR loading type", OFFSET(ir_load), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AF, .unit = "irload" }, + { "init", "load all IRs on init", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "irload" }, + { "access", "load IR on access", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "irload" }, { NULL } }; AVFILTER_DEFINE_CLASS(afir); +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, + }, +}; + const AVFilter ff_af_afir = { .name = "afir", .description = NULL_IF_CONFIG_SMALL("Apply Finite Impulse Response filter with supplied coefficients in additional stream(s)."), .priv_size = sizeof(AudioFIRContext), .priv_class = &afir_class, FILTER_QUERY_FUNC(query_formats), + FILTER_OUTPUTS(outputs), .init = init, .activate = activate, .uninit = uninit, .process_command = process_command, .flags = AVFILTER_FLAG_DYNAMIC_INPUTS | - AVFILTER_FLAG_DYNAMIC_OUTPUTS | + AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/af_afir.h b/libavfilter/af_afir.h index a9f6d217f41..8e4afd79c97 100644 --- a/libavfilter/af_afir.h +++ b/libavfilter/af_afir.h @@ -39,7 +39,6 @@ typedef struct AudioFIRSegment { int input_size; int input_offset; - int *loading; int *output_offset; int *part_index; @@ -49,7 +48,7 @@ typedef struct AudioFIRSegment { AVFrame *tempin; AVFrame *tempout; AVFrame *buffer; - AVFrame *coeff[MAX_IR_STREAMS]; + AVFrame *coeff; AVFrame *input; AVFrame *output; @@ -64,8 +63,11 @@ typedef struct AudioFIRContext { float dry_gain; float length; int gtype; + float ir_norm; + float ir_link; float ir_gain; int ir_format; + int ir_load; float max_ir_len; int response; int w, h; @@ -82,17 +84,23 @@ typedef struct AudioFIRContext { int eof_coeffs[MAX_IR_STREAMS]; int have_coeffs[MAX_IR_STREAMS]; int nb_taps[MAX_IR_STREAMS]; + int nb_segments[MAX_IR_STREAMS]; + int max_offset[MAX_IR_STREAMS]; int nb_channels; int one2many; + int prev_is_disabled; + int *loading; + double *ch_gain; - AudioFIRSegment seg[1024]; - int nb_segments; + AudioFIRSegment seg[MAX_IR_STREAMS][1024]; AVFrame *in; + AVFrame *xfade[2]; + AVFrame *fadein[2]; AVFrame *ir[MAX_IR_STREAMS]; AVFrame *norm_ir[MAX_IR_STREAMS]; - AVFrame *video; int min_part_size; + int max_part_size; int64_t pts; AudioFIRDSPContext afirdsp; diff --git a/libavfilter/af_afirdsp.h b/libavfilter/af_afirdsp.h index 42085013938..827e067a9bc 100644 --- a/libavfilter/af_afirdsp.h +++ b/libavfilter/af_afirdsp.h @@ -33,6 +33,7 @@ typedef struct AudioFIRDSPContext { ptrdiff_t len); } AudioFIRDSPContext; +void ff_afir_init_riscv(AudioFIRDSPContext *s); void ff_afir_init_x86(AudioFIRDSPContext *s); static void fcmul_add_c(float *sum, const float *t, const float *c, ptrdiff_t len) @@ -74,7 +75,9 @@ static av_unused void ff_afir_init(AudioFIRDSPContext *dsp) dsp->fcmul_add = fcmul_add_c; dsp->dcmul_add = dcmul_add_c; -#if ARCH_X86 +#if ARCH_RISCV + ff_afir_init_riscv(dsp); +#elif ARCH_X86 ff_afir_init_x86(dsp); #endif } diff --git a/libavfilter/af_aformat.c b/libavfilter/af_aformat.c index a14e4c1240e..8a3bdee7f8c 100644 --- a/libavfilter/af_aformat.c +++ b/libavfilter/af_aformat.c @@ -103,21 +103,8 @@ static int parse_channel_layouts(AVFilterContext *ctx) ret = av_channel_layout_from_string(&fmt, cur); if (ret < 0) { -#if FF_API_OLD_CHANNEL_LAYOUT - uint64_t mask; -FF_DISABLE_DEPRECATION_WARNINGS - mask = av_get_channel_layout(cur); - if (!mask) { -#endif av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout: %s.\n", cur); return AVERROR(EINVAL); -#if FF_API_OLD_CHANNEL_LAYOUT - } -FF_ENABLE_DEPRECATION_WARNINGS - av_log(ctx, AV_LOG_WARNING, "Channel layout '%s' uses a deprecated syntax.\n", - cur); - av_channel_layout_from_mask(&fmt, mask); -#endif } ret = ff_add_channel_layout(&s->channel_layouts, &fmt); av_channel_layout_uninit(&fmt); @@ -176,20 +163,6 @@ static int query_formats(AVFilterContext *ctx) return ret; } -static const AVFilterPad avfilter_af_aformat_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - -static const AVFilterPad avfilter_af_aformat_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO - }, -}; - const AVFilter ff_af_aformat = { .name = "aformat", .description = NULL_IF_CONFIG_SMALL("Convert the input audio to one of the specified formats."), @@ -198,7 +171,7 @@ const AVFilter ff_af_aformat = { .priv_size = sizeof(AFormatContext), .priv_class = &aformat_class, .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_af_aformat_inputs), - FILTER_OUTPUTS(avfilter_af_aformat_outputs), + FILTER_INPUTS(ff_audio_default_filterpad), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_afreqshift.c b/libavfilter/af_afreqshift.c index 70bf8e419f3..9a6cfd2fc18 100644 --- a/libavfilter/af_afreqshift.c +++ b/libavfilter/af_afreqshift.c @@ -24,7 +24,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #define MAX_NB_COEFFS 16 @@ -66,6 +65,7 @@ static void pfilter_channel_## name(AVFilterContext *ctx, \ type *o1 = (type *)s->o1->extended_data[ch]; \ type *i2 = (type *)s->i2->extended_data[ch]; \ type *o2 = (type *)s->o2->extended_data[ch]; \ + const int nb_coeffs = s->nb_coeffs; \ const type *c = s->cc; \ const type level = s->level; \ type shift = s->shift * M_PI; \ @@ -76,7 +76,7 @@ static void pfilter_channel_## name(AVFilterContext *ctx, \ type xn1 = src[n], xn2 = src[n]; \ type I, Q; \ \ - for (int j = 0; j < s->nb_coeffs; j++) { \ + for (int j = 0; j < nb_coeffs; j++) { \ I = c[j] * (xn1 + o2[j]) - i2[j]; \ i2[j] = i1[j]; \ i1[j] = xn1; \ @@ -85,7 +85,7 @@ static void pfilter_channel_## name(AVFilterContext *ctx, \ xn1 = I; \ } \ \ - for (int j = s->nb_coeffs; j < s->nb_coeffs*2; j++) { \ + for (int j = nb_coeffs; j < nb_coeffs*2; j++) { \ Q = c[j] * (xn2 + o2[j]) - i2[j]; \ i2[j] = i1[j]; \ i1[j] = xn2; \ @@ -93,7 +93,7 @@ static void pfilter_channel_## name(AVFilterContext *ctx, \ o1[j] = Q; \ xn2 = Q; \ } \ - Q = o2[s->nb_coeffs * 2 - 1]; \ + Q = o2[nb_coeffs * 2 - 1]; \ \ dst[n] = (I * cos_theta - Q * sin_theta) * level; \ } \ @@ -115,6 +115,7 @@ static void ffilter_channel_## name(AVFilterContext *ctx, \ type *o1 = (type *)s->o1->extended_data[ch]; \ type *i2 = (type *)s->i2->extended_data[ch]; \ type *o2 = (type *)s->o2->extended_data[ch]; \ + const int nb_coeffs = s->nb_coeffs; \ const type *c = s->cc; \ const type level = s->level; \ type ts = 1. / in->sample_rate; \ @@ -125,7 +126,7 @@ static void ffilter_channel_## name(AVFilterContext *ctx, \ type xn1 = src[n], xn2 = src[n]; \ type I, Q, theta; \ \ - for (int j = 0; j < s->nb_coeffs; j++) { \ + for (int j = 0; j < nb_coeffs; j++) { \ I = c[j] * (xn1 + o2[j]) - i2[j]; \ i2[j] = i1[j]; \ i1[j] = xn1; \ @@ -134,7 +135,7 @@ static void ffilter_channel_## name(AVFilterContext *ctx, \ xn1 = I; \ } \ \ - for (int j = s->nb_coeffs; j < s->nb_coeffs*2; j++) { \ + for (int j = nb_coeffs; j < nb_coeffs*2; j++) { \ Q = c[j] * (xn2 + o2[j]) - i2[j]; \ i2[j] = i1[j]; \ i1[j] = xn2; \ @@ -142,7 +143,7 @@ static void ffilter_channel_## name(AVFilterContext *ctx, \ o1[j] = Q; \ xn2 = Q; \ } \ - Q = o2[s->nb_coeffs * 2 - 1]; \ + Q = o2[nb_coeffs * 2 - 1]; \ \ theta = 2. * M_PI * fmod(shift * (N + n) * ts, 1.); \ dst[n] = (I * cos(theta) - Q * sin(theta)) * level; \ @@ -364,13 +365,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_afreqshift = { .name = "afreqshift", .description = NULL_IF_CONFIG_SMALL("Apply frequency shifting to input audio."), @@ -378,7 +372,7 @@ const AVFilter ff_af_afreqshift = { .priv_class = &afreqshift_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS_ARRAY(sample_fmts), .process_command = ff_filter_process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | @@ -401,7 +395,7 @@ const AVFilter ff_af_aphaseshift = { .priv_class = &aphaseshift_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS_ARRAY(sample_fmts), .process_command = ff_filter_process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | diff --git a/libavfilter/af_afwtdn.c b/libavfilter/af_afwtdn.c index cf41b6f4bc8..8d69259e754 100644 --- a/libavfilter/af_afwtdn.c +++ b/libavfilter/af_afwtdn.c @@ -21,12 +21,10 @@ #include #include "libavutil/avassert.h" -#include "libavutil/avstring.h" #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" #include "filters.h" -#include "formats.h" enum WaveletTypes { SYM2, @@ -410,6 +408,7 @@ typedef struct AudioFWTDNContext { uint64_t sn; int64_t eof_pts; + int eof; int wavelet_type; int channels; @@ -443,14 +442,14 @@ typedef struct AudioFWTDNContext { static const AVOption afwtdn_options[] = { { "sigma", "set noise sigma", OFFSET(sigma), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1, AFR }, { "levels", "set number of wavelet levels", OFFSET(levels), AV_OPT_TYPE_INT, {.i64=10}, 1, MAX_LEVELS-1, AF }, - { "wavet", "set wavelet type", OFFSET(wavelet_type), AV_OPT_TYPE_INT, {.i64=SYM10}, 0, NB_WAVELET_TYPES - 1, AF, "wavet" }, - { "sym2", "sym2", 0, AV_OPT_TYPE_CONST, {.i64=SYM2}, 0, 0, AF, "wavet" }, - { "sym4", "sym4", 0, AV_OPT_TYPE_CONST, {.i64=SYM4}, 0, 0, AF, "wavet" }, - { "rbior68", "rbior68", 0, AV_OPT_TYPE_CONST, {.i64=RBIOR68}, 0, 0, AF, "wavet" }, - { "deb10", "deb10", 0, AV_OPT_TYPE_CONST, {.i64=DEB10}, 0, 0, AF, "wavet" }, - { "sym10", "sym10", 0, AV_OPT_TYPE_CONST, {.i64=SYM10}, 0, 0, AF, "wavet" }, - { "coif5", "coif5", 0, AV_OPT_TYPE_CONST, {.i64=COIF5}, 0, 0, AF, "wavet" }, - { "bl3", "bl3", 0, AV_OPT_TYPE_CONST, {.i64=BL3}, 0, 0, AF, "wavet" }, + { "wavet", "set wavelet type", OFFSET(wavelet_type), AV_OPT_TYPE_INT, {.i64=SYM10}, 0, NB_WAVELET_TYPES - 1, AF, .unit = "wavet" }, + { "sym2", "sym2", 0, AV_OPT_TYPE_CONST, {.i64=SYM2}, 0, 0, AF, .unit = "wavet" }, + { "sym4", "sym4", 0, AV_OPT_TYPE_CONST, {.i64=SYM4}, 0, 0, AF, .unit = "wavet" }, + { "rbior68", "rbior68", 0, AV_OPT_TYPE_CONST, {.i64=RBIOR68}, 0, 0, AF, .unit = "wavet" }, + { "deb10", "deb10", 0, AV_OPT_TYPE_CONST, {.i64=DEB10}, 0, 0, AF, .unit = "wavet" }, + { "sym10", "sym10", 0, AV_OPT_TYPE_CONST, {.i64=SYM10}, 0, 0, AF, .unit = "wavet" }, + { "coif5", "coif5", 0, AV_OPT_TYPE_CONST, {.i64=COIF5}, 0, 0, AF, .unit = "wavet" }, + { "bl3", "bl3", 0, AV_OPT_TYPE_CONST, {.i64=BL3}, 0, 0, AF, .unit = "wavet" }, { "percent", "set percent of full denoising", OFFSET(percent),AV_OPT_TYPE_DOUBLE, {.dbl=85}, 0, 100, AFR }, { "profile", "profile noise", OFFSET(need_profile), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AFR }, { "adaptive", "adaptive profiling of noise", OFFSET(adaptive), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, AFR }, @@ -1071,7 +1070,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->drop_samples = 0; } else { if (s->padd_samples < 0 && eof) { - out->nb_samples += s->padd_samples; + out->nb_samples = FFMAX(0, out->nb_samples + s->padd_samples); s->padd_samples = 0; } if (!eof) @@ -1210,23 +1209,26 @@ static int activate(AVFilterContext *ctx) FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); - ret = ff_inlink_consume_samples(inlink, s->nb_samples, s->nb_samples, &in); - if (ret < 0) - return ret; - if (ret > 0) - return filter_frame(inlink, in); + if (!s->eof) { + ret = ff_inlink_consume_samples(inlink, s->nb_samples, s->nb_samples, &in); + if (ret < 0) + return ret; + if (ret > 0) + return filter_frame(inlink, in); + } if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { - if (status == AVERROR_EOF) { - while (s->padd_samples != 0) { - ret = filter_frame(inlink, NULL); - if (ret < 0) - return ret; - } - ff_outlink_set_status(outlink, status, pts); - return ret; - } + if (status == AVERROR_EOF) + s->eof = 1; } + + if (s->eof && s->padd_samples != 0) { + return filter_frame(inlink, NULL); + } else if (s->eof) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->eof_pts); + return 0; + } + FF_FILTER_FORWARD_WANTED(outlink, inlink); return FFERROR_NOT_READY; @@ -1290,13 +1292,6 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar return 0; } -static const AVFilterPad inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad outputs[] = { { .name = "default", @@ -1312,7 +1307,7 @@ const AVFilter ff_af_afwtdn = { .priv_class = &afwtdn_class, .activate = activate, .uninit = uninit, - FILTER_INPUTS(inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(outputs), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .process_command = process_command, diff --git a/libavfilter/af_agate.c b/libavfilter/af_agate.c index 51bc8c04d4b..d725485950c 100644 --- a/libavfilter/af_agate.c +++ b/libavfilter/af_agate.c @@ -68,9 +68,9 @@ typedef struct AudioGateContext { static const AVOption options[] = { { "level_in", "set input level", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A }, - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A, "mode" }, - { "downward",0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, "mode" }, - { "upward", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, "mode" }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A, .unit = "mode" }, + { "downward",0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, .unit = "mode" }, + { "upward", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, .unit = "mode" }, { "range", "set max gain reduction", OFFSET(range), AV_OPT_TYPE_DOUBLE, {.dbl=0.06125}, 0, 1, A }, { "threshold", "set threshold", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0.125}, 0, 1, A }, { "ratio", "set ratio", OFFSET(ratio), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 1, 9000, A }, @@ -78,12 +78,12 @@ static const AVOption options[] = { { "release", "set release", OFFSET(release), AV_OPT_TYPE_DOUBLE, {.dbl=250}, 0.01, 9000, A }, { "makeup", "set makeup gain", OFFSET(makeup), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 1, 64, A }, { "knee", "set knee", OFFSET(knee), AV_OPT_TYPE_DOUBLE, {.dbl=2.828427125}, 1, 8, A }, - { "detection", "set detection", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, A, "detection" }, - { "peak", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, "detection" }, - { "rms", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, "detection" }, - { "link", "set link", OFFSET(link), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A, "link" }, - { "average", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, "link" }, - { "maximum", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, "link" }, + { "detection", "set detection", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, A, .unit = "detection" }, + { "peak", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, .unit = "detection" }, + { "rms", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, .unit = "detection" }, + { "link", "set link", OFFSET(link), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A, .unit = "link" }, + { "average", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, .unit = "link" }, + { "maximum", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, .unit = "link" }, { "level_sc", "set sidechain gain", OFFSET(level_sc), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A }, { NULL } }; @@ -228,20 +228,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_agate = { .name = "agate", .description = NULL_IF_CONFIG_SMALL("Audio gate."), .priv_class = &agate_sidechaingate_class, .priv_size = sizeof(AudioGateContext), FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBL), .process_command = ff_filter_process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/af_aiir.c b/libavfilter/af_aiir.c index 3e7ec78b635..ff35f6b9260 100644 --- a/libavfilter/af_aiir.c +++ b/libavfilter/af_aiir.c @@ -26,7 +26,9 @@ #include "libavutil/xga_font_data.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" +#include "video.h" typedef struct ThreadData { AVFrame *in, *out; @@ -1307,7 +1309,7 @@ static int config_output(AVFilterLink *outlink) av_log(ctx, AV_LOG_WARNING, "transfer function coefficients format is not recommended for too high number of zeros/poles.\n"); if (s->format > 0 && s->process == 0) { - av_log(ctx, AV_LOG_WARNING, "Direct processsing is not recommended for zp coefficients format.\n"); + av_log(ctx, AV_LOG_WARNING, "Direct processing is not recommended for zp coefficients format.\n"); ret = convert_zp2tf(ctx, inlink->ch_layout.nb_channels); if (ret < 0) @@ -1533,26 +1535,26 @@ static const AVOption aiir_options[] = { { "k", "set channels gains", OFFSET(g_str), AV_OPT_TYPE_STRING, {.str="1|1"}, 0, 0, AF }, { "dry", "set dry gain", OFFSET(dry_gain), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, AF }, { "wet", "set wet gain", OFFSET(wet_gain), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, AF }, - { "format", "set coefficients format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=1}, -2, 4, AF, "format" }, - { "f", "set coefficients format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=1}, -2, 4, AF, "format" }, - { "ll", "lattice-ladder function", 0, AV_OPT_TYPE_CONST, {.i64=-2}, 0, 0, AF, "format" }, - { "sf", "analog transfer function", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, AF, "format" }, - { "tf", "digital transfer function", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "format" }, - { "zp", "Z-plane zeros/poles", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "format" }, - { "pr", "Z-plane zeros/poles (polar radians)", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, "format" }, - { "pd", "Z-plane zeros/poles (polar degrees)", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, AF, "format" }, - { "sp", "S-plane zeros/poles", 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, AF, "format" }, - { "process", "set kind of processing", OFFSET(process), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, AF, "process" }, - { "r", "set kind of processing", OFFSET(process), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, AF, "process" }, - { "d", "direct", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "process" }, - { "s", "serial", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "process" }, - { "p", "parallel", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, "process" }, - { "precision", "set filtering precision", OFFSET(precision),AV_OPT_TYPE_INT, {.i64=0}, 0, 3, AF, "precision" }, - { "e", "set precision", OFFSET(precision),AV_OPT_TYPE_INT, {.i64=0}, 0, 3, AF, "precision" }, - { "dbl", "double-precision floating-point", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "precision" }, - { "flt", "single-precision floating-point", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "precision" }, - { "i32", "32-bit integers", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, "precision" }, - { "i16", "16-bit integers", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, AF, "precision" }, + { "format", "set coefficients format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=1}, -2, 4, AF, .unit = "format" }, + { "f", "set coefficients format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=1}, -2, 4, AF, .unit = "format" }, + { "ll", "lattice-ladder function", 0, AV_OPT_TYPE_CONST, {.i64=-2}, 0, 0, AF, .unit = "format" }, + { "sf", "analog transfer function", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, AF, .unit = "format" }, + { "tf", "digital transfer function", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "format" }, + { "zp", "Z-plane zeros/poles", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "format" }, + { "pr", "Z-plane zeros/poles (polar radians)", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, .unit = "format" }, + { "pd", "Z-plane zeros/poles (polar degrees)", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, AF, .unit = "format" }, + { "sp", "S-plane zeros/poles", 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, AF, .unit = "format" }, + { "process", "set kind of processing", OFFSET(process), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, AF, .unit = "process" }, + { "r", "set kind of processing", OFFSET(process), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, AF, .unit = "process" }, + { "d", "direct", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "process" }, + { "s", "serial", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "process" }, + { "p", "parallel", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, .unit = "process" }, + { "precision", "set filtering precision", OFFSET(precision),AV_OPT_TYPE_INT, {.i64=0}, 0, 3, AF, .unit = "precision" }, + { "e", "set precision", OFFSET(precision),AV_OPT_TYPE_INT, {.i64=0}, 0, 3, AF, .unit = "precision" }, + { "dbl", "double-precision floating-point", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "precision" }, + { "flt", "single-precision floating-point", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "precision" }, + { "i32", "32-bit integers", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, .unit = "precision" }, + { "i16", "16-bit integers", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, AF, .unit = "precision" }, { "normalize", "normalize coefficients", OFFSET(normalize),AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, AF }, { "n", "normalize coefficients", OFFSET(normalize),AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, AF }, { "mix", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, AF }, diff --git a/libavfilter/af_alimiter.c b/libavfilter/af_alimiter.c index c683c4bcf41..9a867047643 100644 --- a/libavfilter/af_alimiter.c +++ b/libavfilter/af_alimiter.c @@ -31,7 +31,6 @@ #include "audio.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" typedef struct MetaItem { @@ -196,9 +195,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) int j = i % buffer_size; double ppeak = 0, pdelta; - for (c = 0; c < channels; c++) { - ppeak = FFMAX(ppeak, fabs(buffer[nextpos[j] + c])); - } + if (nextpos[j] >= 0) + for (c = 0; c < channels; c++) { + ppeak = FFMAX(ppeak, fabs(buffer[nextpos[j] + c])); + } pdelta = (limit / peak - limit / ppeak) / (((buffer_size - nextpos[j] + s->pos) % buffer_size) / channels); if (pdelta < nextdelta[j]) { nextdelta[j] = pdelta; diff --git a/libavfilter/af_amerge.c b/libavfilter/af_amerge.c index 8bcc0ac5be9..26040e5ed5f 100644 --- a/libavfilter/af_amerge.c +++ b/libavfilter/af_amerge.c @@ -30,6 +30,7 @@ #include "avfilter.h" #include "filters.h" #include "audio.h" +#include "formats.h" #include "internal.h" #define SWR_CH_MAX 64 @@ -152,21 +153,18 @@ static int config_output(AVFilterLink *outlink) AVFilterContext *ctx = outlink->src; AMergeContext *s = ctx->priv; AVBPrint bp; - char buf[128]; int i; - s->bps = av_get_bytes_per_sample(ctx->outputs[0]->format); + s->bps = av_get_bytes_per_sample(outlink->format); outlink->time_base = ctx->inputs[0]->time_base; av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC); for (i = 0; i < s->nb_inputs; i++) { av_bprintf(&bp, "%sin%d:", i ? " + " : "", i); - av_channel_layout_describe(&ctx->inputs[i]->ch_layout, buf, sizeof(buf)); - av_bprintf(&bp, "%s", buf); + av_channel_layout_describe_bprint(&ctx->inputs[i]->ch_layout, &bp); } av_bprintf(&bp, " -> out:"); - av_channel_layout_describe(&ctx->outputs[0]->ch_layout, buf, sizeof(buf)); - av_bprintf(&bp, "%s", buf); + av_channel_layout_describe_bprint(&outlink->ch_layout, &bp); av_log(ctx, AV_LOG_VERBOSE, "%s\n", bp.str); return 0; @@ -233,7 +231,7 @@ static int try_push_frame(AVFilterContext *ctx, int nb_samples) ins[i] = inbuf[i]->data[0]; } - outbuf = ff_get_audio_buffer(ctx->outputs[0], nb_samples); + outbuf = ff_get_audio_buffer(outlink, nb_samples); if (!outbuf) { free_frames(s->nb_inputs, inbuf); return AVERROR(ENOMEM); @@ -243,14 +241,15 @@ static int try_push_frame(AVFilterContext *ctx, int nb_samples) outbuf->pts = inbuf[0]->pts; outbuf->nb_samples = nb_samples; - if ((ret = av_channel_layout_copy(&outbuf->ch_layout, &outlink->ch_layout)) < 0) + outbuf->duration = av_rescale_q(outbuf->nb_samples, + av_make_q(1, outlink->sample_rate), + outlink->time_base); + + if ((ret = av_channel_layout_copy(&outbuf->ch_layout, &outlink->ch_layout)) < 0) { + free_frames(s->nb_inputs, inbuf); + av_frame_free(&outbuf); return ret; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - outbuf->channel_layout = outlink->channel_layout; - outbuf->channels = outlink->ch_layout.nb_channels; -FF_ENABLE_DEPRECATION_WARNINGS -#endif + } while (nb_samples) { /* Unroll the most common sample formats: speed +~350% for the loop, @@ -274,7 +273,7 @@ FF_ENABLE_DEPRECATION_WARNINGS } free_frames(s->nb_inputs, inbuf); - return ff_filter_frame(ctx->outputs[0], outbuf); + return ff_filter_frame(outlink, outbuf); } static int activate(AVFilterContext *ctx) diff --git a/libavfilter/af_amix.c b/libavfilter/af_amix.c index d7e00ab1f11..f55c5724d64 100644 --- a/libavfilter/af_amix.c +++ b/libavfilter/af_amix.c @@ -43,7 +43,6 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" #define INPUT_ON 1 /**< input is active */ @@ -187,10 +186,10 @@ static const AVOption amix_options[] = { { "inputs", "Number of inputs.", OFFSET(nb_inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, INT16_MAX, A|F }, { "duration", "How to determine the end-of-stream.", - OFFSET(duration_mode), AV_OPT_TYPE_INT, { .i64 = DURATION_LONGEST }, 0, 2, A|F, "duration" }, - { "longest", "Duration of longest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_LONGEST }, 0, 0, A|F, "duration" }, - { "shortest", "Duration of shortest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_SHORTEST }, 0, 0, A|F, "duration" }, - { "first", "Duration of first input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_FIRST }, 0, 0, A|F, "duration" }, + OFFSET(duration_mode), AV_OPT_TYPE_INT, { .i64 = DURATION_LONGEST }, 0, 2, A|F, .unit = "duration" }, + { "longest", "Duration of longest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_LONGEST }, 0, 0, A|F, .unit = "duration" }, + { "shortest", "Duration of shortest input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_SHORTEST }, 0, 0, A|F, .unit = "duration" }, + { "first", "Duration of first input.", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_FIRST }, 0, 0, A|F, .unit = "duration" }, { "dropout_transition", "Transition time, in seconds, for volume " "renormalization when an input stream ends.", OFFSET(dropout_transition), AV_OPT_TYPE_FLOAT, { .dbl = 2.0 }, 0, INT_MAX, A|F }, @@ -380,6 +379,9 @@ static int output_frame(AVFilterLink *outlink) av_frame_free(&in_buf); out_buf->pts = s->next_pts; + out_buf->duration = av_rescale_q(out_buf->nb_samples, av_make_q(1, outlink->sample_rate), + outlink->time_base); + if (s->next_pts != AV_NOPTS_VALUE) s->next_pts += nb_samples; @@ -395,6 +397,8 @@ static int request_samples(AVFilterContext *ctx, int min_samples) int i; av_assert0(s->nb_inputs > 1); + if (min_samples == 1 && s->duration_mode == DURATION_FIRST) + min_samples = av_audio_fifo_size(s->fifos[0]); for (i = 1; i < s->nb_inputs; i++) { if (!(s->input_state[i] & INPUT_ON) || @@ -403,6 +407,7 @@ static int request_samples(AVFilterContext *ctx, int min_samples) if (av_audio_fifo_size(s->fifos[i]) >= min_samples) continue; ff_inlink_request_frame(ctx->inputs[i]); + return 0; } return output_frame(ctx->outputs[0]); } @@ -472,17 +477,13 @@ static int activate(AVFilterContext *ctx) if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) { if (status == AVERROR_EOF) { - if (i == 0) { - s->input_state[i] = 0; + s->input_state[i] |= INPUT_EOF; + if (av_audio_fifo_size(s->fifos[i]) == 0) { + s->input_state[i] &= ~INPUT_ON; if (s->nb_inputs == 1) { ff_outlink_set_status(outlink, status, pts); return 0; } - } else { - s->input_state[i] |= INPUT_EOF; - if (av_audio_fifo_size(s->fifos[i]) == 0) { - s->input_state[i] = 0; - } } } } diff --git a/libavfilter/af_amultiply.c b/libavfilter/af_amultiply.c index 97728954a5c..4ae355c7941 100644 --- a/libavfilter/af_amultiply.c +++ b/libavfilter/af_amultiply.c @@ -21,11 +21,9 @@ #include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/float_dsp.h" -#include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" -#include "formats.h" #include "filters.h" #include "internal.h" @@ -76,6 +74,7 @@ static int activate(AVFilterContext *ctx) return AVERROR(ENOMEM); out->pts = s->frames[0]->pts; + out->duration = s->frames[0]->duration; if (av_get_packed_sample_fmt(ctx->inputs[0]->format) == AV_SAMPLE_FMT_FLT) { for (i = 0; i < s->planes; i++) { @@ -92,7 +91,6 @@ static int activate(AVFilterContext *ctx) plane_samples); } } - emms_c(); av_frame_free(&s->frames[0]); av_frame_free(&s->frames[1]); @@ -113,7 +111,7 @@ static int activate(AVFilterContext *ctx) if (ff_outlink_frame_wanted(ctx->outputs[0])) { for (i = 0; i < 2; i++) { - if (ff_inlink_queued_samples(ctx->inputs[i]) > 0) + if (s->frames[i] || ff_inlink_queued_samples(ctx->inputs[i]) > 0) continue; ff_inlink_request_frame(ctx->inputs[i]); return 0; diff --git a/libavfilter/af_anequalizer.c b/libavfilter/af_anequalizer.c index c7b9a83d99b..37ebcb5db19 100644 --- a/libavfilter/af_anequalizer.c +++ b/libavfilter/af_anequalizer.c @@ -25,8 +25,10 @@ #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "audio.h" +#include "video.h" #define FILTER_ORDER 4 @@ -82,9 +84,9 @@ static const AVOption anequalizer_options[] = { { "curves", "draw frequency response curves", OFFSET(draw_curves), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, V|F }, { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "hd720"}, 0, 0, V|F }, { "mgain", "set max gain", OFFSET(mag), AV_OPT_TYPE_DOUBLE, {.dbl=60}, -900, 900, V|F }, - { "fscale", "set frequency scale", OFFSET(fscale), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, V|F, "fscale" }, - { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, V|F, "fscale" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, V|F, "fscale" }, + { "fscale", "set frequency scale", OFFSET(fscale), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, V|F, .unit = "fscale" }, + { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, V|F, .unit = "fscale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, V|F, .unit = "fscale" }, { "colors", "set channels curves colors", OFFSET(colors), AV_OPT_TYPE_STRING, {.str = "red|green|blue|yellow|orange|lime|pink|magenta|brown" }, 0, 0, V|F }, { NULL } }; diff --git a/libavfilter/af_anlmdn.c b/libavfilter/af_anlmdn.c index 8f01c5f8a27..f8e4f92c475 100644 --- a/libavfilter/af_anlmdn.c +++ b/libavfilter/af_anlmdn.c @@ -21,11 +21,9 @@ #include #include "libavutil/avassert.h" -#include "libavutil/avstring.h" #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #include "filters.h" #include "af_anlmdndsp.h" @@ -74,11 +72,11 @@ static const AVOption anlmdn_options[] = { { "p", "set patch duration", OFFSET(pd), AV_OPT_TYPE_DURATION, {.i64=2000}, 1000, 100000, AFT }, { "research", "set research duration", OFFSET(rd), AV_OPT_TYPE_DURATION, {.i64=6000}, 2000, 300000, AFT }, { "r", "set research duration", OFFSET(rd), AV_OPT_TYPE_DURATION, {.i64=6000}, 2000, 300000, AFT }, - { "output", "set output mode", OFFSET(om), AV_OPT_TYPE_INT, {.i64=OUT_MODE}, 0, NB_MODES-1, AFT, "mode" }, - { "o", "set output mode", OFFSET(om), AV_OPT_TYPE_INT, {.i64=OUT_MODE}, 0, NB_MODES-1, AFT, "mode" }, - { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64=IN_MODE}, 0, 0, AFT, "mode" }, - { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64=OUT_MODE}, 0, 0, AFT, "mode" }, - { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_MODE},0, 0, AFT, "mode" }, + { "output", "set output mode", OFFSET(om), AV_OPT_TYPE_INT, {.i64=OUT_MODE}, 0, NB_MODES-1, AFT, .unit = "mode" }, + { "o", "set output mode", OFFSET(om), AV_OPT_TYPE_INT, {.i64=OUT_MODE}, 0, NB_MODES-1, AFT, .unit = "mode" }, + { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64=IN_MODE}, 0, 0, AFT, .unit = "mode" }, + { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64=OUT_MODE}, 0, 0, AFT, .unit = "mode" }, + { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_MODE},0, 0, AFT, .unit = "mode" }, { "smooth", "set smooth factor", OFFSET(m), AV_OPT_TYPE_FLOAT, {.dbl=11.}, 1, 1000, AFT }, { "m", "set smooth factor", OFFSET(m), AV_OPT_TYPE_FLOAT, {.dbl=11.}, 1, 1000, AFT }, { NULL } @@ -343,13 +341,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&s->window); } -static const AVFilterPad inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad outputs[] = { { .name = "default", @@ -365,7 +356,7 @@ const AVFilter ff_af_anlmdn = { .priv_class = &anlmdn_class, .activate = activate, .uninit = uninit, - FILTER_INPUTS(inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(outputs), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .process_command = process_command, diff --git a/libavfilter/af_anlms.c b/libavfilter/af_anlms.c index 6658cd29400..9b0d11cd6d7 100644 --- a/libavfilter/af_anlms.c +++ b/libavfilter/af_anlms.c @@ -25,8 +25,8 @@ #include "audio.h" #include "avfilter.h" -#include "formats.h" #include "filters.h" +#include "formats.h" #include "internal.h" enum OutModes { @@ -34,6 +34,7 @@ enum OutModes { DESIRED_MODE, OUT_MODE, NOISE_MODE, + ERROR_MODE, NB_OMODES }; @@ -45,6 +46,7 @@ typedef struct AudioNLMSContext { float eps; float leakage; int output_mode; + int precision; int kernel_size; AVFrame *offset; @@ -56,6 +58,8 @@ typedef struct AudioNLMSContext { int anlmf; + int (*filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + AVFloatDSPContext *fdsp; } AudioNLMSContext; @@ -68,11 +72,16 @@ static const AVOption anlms_options[] = { { "mu", "set the filter mu", OFFSET(mu), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 2, AT }, { "eps", "set the filter eps", OFFSET(eps), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 1, AT }, { "leakage", "set the filter leakage", OFFSET(leakage), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, 1, AT }, - { "out_mode", "set output mode", OFFSET(output_mode), AV_OPT_TYPE_INT, {.i64=OUT_MODE}, 0, NB_OMODES-1, AT, "mode" }, - { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64=IN_MODE}, 0, 0, AT, "mode" }, - { "d", "desired", 0, AV_OPT_TYPE_CONST, {.i64=DESIRED_MODE}, 0, 0, AT, "mode" }, - { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64=OUT_MODE}, 0, 0, AT, "mode" }, - { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_MODE}, 0, 0, AT, "mode" }, + { "out_mode", "set output mode", OFFSET(output_mode), AV_OPT_TYPE_INT, {.i64=OUT_MODE}, 0, NB_OMODES-1, AT, .unit = "mode" }, + { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64=IN_MODE}, 0, 0, AT, .unit = "mode" }, + { "d", "desired", 0, AV_OPT_TYPE_CONST, {.i64=DESIRED_MODE}, 0, 0, AT, .unit = "mode" }, + { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64=OUT_MODE}, 0, 0, AT, .unit = "mode" }, + { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_MODE}, 0, 0, AT, .unit = "mode" }, + { "e", "error", 0, AV_OPT_TYPE_CONST, {.i64=ERROR_MODE}, 0, 0, AT, .unit = "mode" }, + { "precision", "set processing precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, A, .unit = "precision" }, + { "auto", "set auto processing precision", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, .unit = "precision" }, + { "float", "set single-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, .unit = "precision" }, + { "double","set double-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, A, .unit = "precision" }, { NULL } }; @@ -80,104 +89,23 @@ AVFILTER_DEFINE_CLASS_EXT(anlms, "anlm(f|s)", anlms_options); static int query_formats(AVFilterContext *ctx) { - static const enum AVSampleFormat sample_fmts[] = { - AV_SAMPLE_FMT_FLTP, - AV_SAMPLE_FMT_NONE + AudioNLMSContext *s = ctx->priv; + static const enum AVSampleFormat sample_fmts[3][3] = { + { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE }, + { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, + { AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE }, }; - int ret = ff_set_common_all_channel_counts(ctx); - if (ret < 0) + int ret; + + if ((ret = ff_set_common_all_channel_counts(ctx)) < 0) return ret; - ret = ff_set_common_formats_from_list(ctx, sample_fmts); - if (ret < 0) + if ((ret = ff_set_common_formats_from_list(ctx, sample_fmts[s->precision])) < 0) return ret; return ff_set_common_all_samplerates(ctx); } -static float fir_sample(AudioNLMSContext *s, float sample, float *delay, - float *coeffs, float *tmp, int *offset) -{ - const int order = s->order; - float output; - - delay[*offset] = sample; - - memcpy(tmp, coeffs + order - *offset, order * sizeof(float)); - - output = s->fdsp->scalarproduct_float(delay, tmp, s->kernel_size); - - if (--(*offset) < 0) - *offset = order - 1; - - return output; -} - -static float process_sample(AudioNLMSContext *s, float input, float desired, - float *delay, float *coeffs, float *tmp, int *offsetp) -{ - const int order = s->order; - const float leakage = s->leakage; - const float mu = s->mu; - const float a = 1.f - leakage * mu; - float sum, output, e, norm, b; - int offset = *offsetp; - - delay[offset + order] = input; - - output = fir_sample(s, input, delay, coeffs, tmp, offsetp); - e = desired - output; - - sum = s->fdsp->scalarproduct_float(delay, delay, s->kernel_size); - - norm = s->eps + sum; - b = mu * e / norm; - if (s->anlmf) - b *= 4.f * e * e; - - memcpy(tmp, delay + offset, order * sizeof(float)); - - s->fdsp->vector_fmul_scalar(coeffs, coeffs, a, s->kernel_size); - - s->fdsp->vector_fmac_scalar(coeffs, tmp, b, s->kernel_size); - - memcpy(coeffs + order, coeffs, order * sizeof(float)); - - switch (s->output_mode) { - case IN_MODE: output = input; break; - case DESIRED_MODE: output = desired; break; - case OUT_MODE: /*output = output;*/ break; - case NOISE_MODE: output = desired - output; break; - } - return output; -} - -static int process_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - AudioNLMSContext *s = ctx->priv; - AVFrame *out = arg; - const int start = (out->ch_layout.nb_channels * jobnr) / nb_jobs; - const int end = (out->ch_layout.nb_channels * (jobnr+1)) / nb_jobs; - - for (int c = start; c < end; c++) { - const float *input = (const float *)s->frame[0]->extended_data[c]; - const float *desired = (const float *)s->frame[1]->extended_data[c]; - float *delay = (float *)s->delay->extended_data[c]; - float *coeffs = (float *)s->coeffs->extended_data[c]; - float *tmp = (float *)s->tmp->extended_data[c]; - int *offset = (int *)s->offset->extended_data[c]; - float *output = (float *)out->extended_data[c]; - - for (int n = 0; n < out->nb_samples; n++) { - output[n] = process_sample(s, input[n], desired[n], delay, coeffs, tmp, offset); - if (ctx->is_disabled) - output[n] = input[n]; - } - } - - return 0; -} - static int activate(AVFilterContext *ctx) { AudioNLMSContext *s = ctx->priv; @@ -210,10 +138,11 @@ static int activate(AVFilterContext *ctx) return AVERROR(ENOMEM); } - ff_filter_execute(ctx, process_channels, out, NULL, + ff_filter_execute(ctx, s->filter_channels, out, NULL, FFMIN(ctx->outputs[0]->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx))); out->pts = s->frame[0]->pts; + out->duration = s->frame[0]->duration; av_frame_free(&s->frame[0]); av_frame_free(&s->frame[1]); @@ -234,7 +163,7 @@ static int activate(AVFilterContext *ctx) if (ff_outlink_frame_wanted(ctx->outputs[0])) { for (i = 0; i < 2; i++) { - if (ff_inlink_queued_samples(ctx->inputs[i]) > 0) + if (s->frame[i] || ff_inlink_queued_samples(ctx->inputs[i]) > 0) continue; ff_inlink_request_frame(ctx->inputs[i]); return 0; @@ -243,6 +172,13 @@ static int activate(AVFilterContext *ctx) return 0; } +#define DEPTH 32 +#include "anlms_template.c" + +#undef DEPTH +#define DEPTH 64 +#include "anlms_template.c" + static int config_output(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; @@ -262,6 +198,15 @@ static int config_output(AVFilterLink *outlink) if (!s->delay || !s->coeffs || !s->offset || !s->tmp) return AVERROR(ENOMEM); + switch (outlink->format) { + case AV_SAMPLE_FMT_DBLP: + s->filter_channels = filter_channels_double; + break; + case AV_SAMPLE_FMT_FLTP: + s->filter_channels = filter_channels_float; + break; + } + return 0; } @@ -316,7 +261,7 @@ const AVFilter ff_af_anlms = { .activate = activate, FILTER_INPUTS(inputs), FILTER_OUTPUTS(outputs), - FILTER_QUERY_FUNC(query_formats), + FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_anull.c b/libavfilter/af_anull.c index 78c5faeb230..257dba1a41c 100644 --- a/libavfilter/af_anull.c +++ b/libavfilter/af_anull.c @@ -27,24 +27,10 @@ #include "internal.h" #include "libavutil/internal.h" -static const AVFilterPad avfilter_af_anull_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - -static const AVFilterPad avfilter_af_anull_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_anull = { .name = "anull", .description = NULL_IF_CONFIG_SMALL("Pass the source unchanged to the output."), .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_af_anull_inputs), - FILTER_OUTPUTS(avfilter_af_anull_outputs), + FILTER_INPUTS(ff_audio_default_filterpad), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; diff --git a/libavfilter/af_apad.c b/libavfilter/af_apad.c index df17c9a5315..1e0ecd201e1 100644 --- a/libavfilter/af_apad.c +++ b/libavfilter/af_apad.c @@ -32,12 +32,14 @@ #include "libavutil/avassert.h" #include "avfilter.h" #include "audio.h" +#include "filters.h" #include "internal.h" typedef struct APadContext { const AVClass *class; int64_t next_pts; + int eof; int packet_size; int64_t pad_len, pad_len_left; int64_t whole_len, whole_len_left; @@ -87,50 +89,86 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) return ff_filter_frame(ctx->outputs[0], frame); } -static int request_frame(AVFilterLink *outlink) +static int push_frame(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; APadContext *s = ctx->priv; - int ret; + AVFrame *outsamplesref; + int n_out; - ret = ff_request_frame(ctx->inputs[0]); + if (ctx->is_disabled) + return 0; + n_out = s->packet_size; - if (ret == AVERROR_EOF && !ctx->is_disabled) { - int n_out = s->packet_size; - AVFrame *outsamplesref; + if (s->whole_len >= 0 && s->pad_len < 0) { + s->pad_len = s->pad_len_left = s->whole_len_left; + } + if (s->pad_len >=0 || s->whole_len >= 0) { + n_out = FFMIN(n_out, s->pad_len_left); + s->pad_len_left -= n_out; + av_log(ctx, AV_LOG_DEBUG, + "padding n_out:%d pad_len_left:%"PRId64"\n", n_out, s->pad_len_left); + } - if (s->whole_len >= 0 && s->pad_len < 0) { - s->pad_len = s->pad_len_left = s->whole_len_left; - } - if (s->pad_len >=0 || s->whole_len >= 0) { - n_out = FFMIN(n_out, s->pad_len_left); - s->pad_len_left -= n_out; - av_log(ctx, AV_LOG_DEBUG, - "padding n_out:%d pad_len_left:%"PRId64"\n", n_out, s->pad_len_left); - } + if (!n_out) + return AVERROR_EOF; + + outsamplesref = ff_get_audio_buffer(outlink, n_out); + if (!outsamplesref) + return AVERROR(ENOMEM); + + av_assert0(outsamplesref->sample_rate == outlink->sample_rate); + av_assert0(outsamplesref->nb_samples == n_out); + + av_samples_set_silence(outsamplesref->extended_data, 0, + n_out, + outsamplesref->ch_layout.nb_channels, + outsamplesref->format); + + outsamplesref->pts = s->next_pts; + if (s->next_pts != AV_NOPTS_VALUE) + s->next_pts += av_rescale_q(n_out, (AVRational){1, outlink->sample_rate}, outlink->time_base); + + return ff_filter_frame(outlink, outsamplesref); +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + APadContext *s = ctx->priv; + int64_t pts; + int status; - if (!n_out) - return AVERROR_EOF; + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); - outsamplesref = ff_get_audio_buffer(outlink, n_out); - if (!outsamplesref) - return AVERROR(ENOMEM); + if (!s->eof && ff_inlink_queued_frames(inlink)) { + AVFrame *frame = NULL; + int ret; - av_assert0(outsamplesref->sample_rate == outlink->sample_rate); - av_assert0(outsamplesref->nb_samples == n_out); + ret = ff_inlink_consume_frame(inlink, &frame); + if (ret < 0) + return ret; + if (ret > 0) + return filter_frame(inlink, frame); + } - av_samples_set_silence(outsamplesref->extended_data, 0, - n_out, - outsamplesref->ch_layout.nb_channels, - outsamplesref->format); + if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) + s->eof = status == AVERROR_EOF; - outsamplesref->pts = s->next_pts; - if (s->next_pts != AV_NOPTS_VALUE) - s->next_pts += av_rescale_q(n_out, (AVRational){1, outlink->sample_rate}, outlink->time_base); + if (s->eof) { + int ret = push_frame(outlink); - return ff_filter_frame(outlink, outsamplesref); + if (ret == AVERROR_EOF) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->next_pts); + return 0; + } + return ret; } - return ret; + + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; } static int config_output(AVFilterLink *outlink) @@ -149,20 +187,11 @@ static int config_output(AVFilterLink *outlink) return 0; } -static const AVFilterPad apad_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = filter_frame, - }, -}; - static const AVFilterPad apad_outputs[] = { { .name = "default", - .request_frame = request_frame, - .config_props = config_output, .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, }, }; @@ -170,8 +199,9 @@ const AVFilter ff_af_apad = { .name = "apad", .description = NULL_IF_CONFIG_SMALL("Pad audio with silence."), .init = init, + .activate = activate, .priv_size = sizeof(APadContext), - FILTER_INPUTS(apad_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(apad_outputs), .priv_class = &apad_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/af_aphaser.c b/libavfilter/af_aphaser.c index 1f5dcb1f9e1..4690276a65d 100644 --- a/libavfilter/af_aphaser.c +++ b/libavfilter/af_aphaser.c @@ -61,11 +61,11 @@ static const AVOption aphaser_options[] = { { "delay", "set delay in milliseconds", OFFSET(delay), AV_OPT_TYPE_DOUBLE, {.dbl=3.}, 0, 5, FLAGS }, { "decay", "set decay", OFFSET(decay), AV_OPT_TYPE_DOUBLE, {.dbl=.4}, 0, .99, FLAGS }, { "speed", "set modulation speed", OFFSET(speed), AV_OPT_TYPE_DOUBLE, {.dbl=.5}, .1, 2, FLAGS }, - { "type", "set modulation type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=WAVE_TRI}, 0, WAVE_NB-1, FLAGS, "type" }, - { "triangular", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_TRI}, 0, 0, FLAGS, "type" }, - { "t", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_TRI}, 0, 0, FLAGS, "type" }, - { "sinusoidal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_SIN}, 0, 0, FLAGS, "type" }, - { "s", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_SIN}, 0, 0, FLAGS, "type" }, + { "type", "set modulation type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=WAVE_TRI}, 0, WAVE_NB-1, FLAGS, .unit = "type" }, + { "triangular", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_TRI}, 0, 0, FLAGS, .unit = "type" }, + { "t", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_TRI}, 0, 0, FLAGS, .unit = "type" }, + { "sinusoidal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_SIN}, 0, 0, FLAGS, .unit = "type" }, + { "s", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_SIN}, 0, 0, FLAGS, .unit = "type" }, { NULL } }; diff --git a/libavfilter/af_apsyclip.c b/libavfilter/af_apsyclip.c index 0b5859068bb..6a0c23d6eb0 100644 --- a/libavfilter/af_apsyclip.c +++ b/libavfilter/af_apsyclip.c @@ -637,13 +637,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_apsyclip = { .name = "apsyclip", .description = NULL_IF_CONFIG_SMALL("Audio Psychoacoustic Clipper."), @@ -651,7 +644,7 @@ const AVFilter ff_af_apsyclip = { .priv_class = &apsyclip_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/af_apulsator.c b/libavfilter/af_apulsator.c index d4d3c59c38b..e471936d16f 100644 --- a/libavfilter/af_apulsator.c +++ b/libavfilter/af_apulsator.c @@ -22,6 +22,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/opt.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "audio.h" @@ -61,20 +62,20 @@ typedef struct AudioPulsatorContext { static const AVOption apulsator_options[] = { { "level_in", "set input gain", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, FLAGS, }, { "level_out", "set output gain", OFFSET(level_out), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, FLAGS, }, - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=SINE}, SINE, NB_MODES-1, FLAGS, "mode" }, - { "sine", NULL, 0, AV_OPT_TYPE_CONST, {.i64=SINE}, 0, 0, FLAGS, "mode" }, - { "triangle", NULL, 0, AV_OPT_TYPE_CONST, {.i64=TRIANGLE},0, 0, FLAGS, "mode" }, - { "square", NULL, 0, AV_OPT_TYPE_CONST, {.i64=SQUARE}, 0, 0, FLAGS, "mode" }, - { "sawup", NULL, 0, AV_OPT_TYPE_CONST, {.i64=SAWUP}, 0, 0, FLAGS, "mode" }, - { "sawdown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=SAWDOWN}, 0, 0, FLAGS, "mode" }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=SINE}, SINE, NB_MODES-1, FLAGS, .unit = "mode" }, + { "sine", NULL, 0, AV_OPT_TYPE_CONST, {.i64=SINE}, 0, 0, FLAGS, .unit = "mode" }, + { "triangle", NULL, 0, AV_OPT_TYPE_CONST, {.i64=TRIANGLE},0, 0, FLAGS, .unit = "mode" }, + { "square", NULL, 0, AV_OPT_TYPE_CONST, {.i64=SQUARE}, 0, 0, FLAGS, .unit = "mode" }, + { "sawup", NULL, 0, AV_OPT_TYPE_CONST, {.i64=SAWUP}, 0, 0, FLAGS, .unit = "mode" }, + { "sawdown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=SAWDOWN}, 0, 0, FLAGS, .unit = "mode" }, { "amount", "set modulation", OFFSET(amount), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS }, { "offset_l", "set offset L", OFFSET(offset_l), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1, FLAGS }, { "offset_r", "set offset R", OFFSET(offset_r), AV_OPT_TYPE_DOUBLE, {.dbl=.5}, 0, 1, FLAGS }, { "width", "set pulse width", OFFSET(pwidth), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 2, FLAGS }, - { "timing", "set timing", OFFSET(timing), AV_OPT_TYPE_INT, {.i64=2}, 0, NB_TIMINGS-1, FLAGS, "timing" }, - { "bpm", NULL, 0, AV_OPT_TYPE_CONST, {.i64=UNIT_BPM}, 0, 0, FLAGS, "timing" }, - { "ms", NULL, 0, AV_OPT_TYPE_CONST, {.i64=UNIT_MS}, 0, 0, FLAGS, "timing" }, - { "hz", NULL, 0, AV_OPT_TYPE_CONST, {.i64=UNIT_HZ}, 0, 0, FLAGS, "timing" }, + { "timing", "set timing", OFFSET(timing), AV_OPT_TYPE_INT, {.i64=2}, 0, NB_TIMINGS-1, FLAGS, .unit = "timing" }, + { "bpm", NULL, 0, AV_OPT_TYPE_CONST, {.i64=UNIT_BPM}, 0, 0, FLAGS, .unit = "timing" }, + { "ms", NULL, 0, AV_OPT_TYPE_CONST, {.i64=UNIT_MS}, 0, 0, FLAGS, .unit = "timing" }, + { "hz", NULL, 0, AV_OPT_TYPE_CONST, {.i64=UNIT_HZ}, 0, 0, FLAGS, .unit = "timing" }, { "bpm", "set BPM", OFFSET(bpm), AV_OPT_TYPE_DOUBLE, {.dbl=120}, 30, 300, FLAGS }, { "ms", "set ms", OFFSET(ms), AV_OPT_TYPE_INT, {.i64=500}, 10, 2000, FLAGS }, { "hz", "set frequency", OFFSET(hertz), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 0.01, 100, FLAGS }, @@ -237,19 +238,12 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_apulsator = { .name = "apulsator", .description = NULL_IF_CONFIG_SMALL("Audio pulsator."), .priv_size = sizeof(AudioPulsatorContext), .priv_class = &apulsator_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_aresample.c b/libavfilter/af_aresample.c index 971c861d0ef..8ff2fe5973f 100644 --- a/libavfilter/af_aresample.c +++ b/libavfilter/af_aresample.c @@ -32,6 +32,8 @@ #include "libswresample/swresample.h" #include "avfilter.h" #include "audio.h" +#include "filters.h" +#include "formats.h" #include "internal.h" typedef struct AResampleContext { @@ -41,6 +43,7 @@ typedef struct AResampleContext { struct SwrContext *swr; int64_t next_pts; int more_data; + int eof; } AResampleContext; static av_cold int preinit(AVFilterContext *ctx) @@ -169,7 +172,8 @@ static int config_output(AVFilterLink *outlink) static int filter_frame(AVFilterLink *inlink, AVFrame *insamplesref) { - AResampleContext *aresample = inlink->dst->priv; + AVFilterContext *ctx = inlink->dst; + AResampleContext *aresample = ctx->priv; const int n_in = insamplesref->nb_samples; int64_t delay; int n_out = n_in * aresample->ratio + 32; @@ -190,15 +194,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamplesref) av_frame_copy_props(outsamplesref, insamplesref); outsamplesref->format = outlink->format; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - outsamplesref->channels = outlink->ch_layout.nb_channels; - outsamplesref->channel_layout = outlink->channel_layout; -FF_ENABLE_DEPRECATION_WARNINGS -#endif ret = av_channel_layout_copy(&outsamplesref->ch_layout, &outlink->ch_layout); - if (ret < 0) + if (ret < 0) { + av_frame_free(&outsamplesref); + av_frame_free(&insamplesref); return ret; + } outsamplesref->sample_rate = outlink->sample_rate; if(insamplesref->pts != AV_NOPTS_VALUE) { @@ -214,6 +215,7 @@ FF_ENABLE_DEPRECATION_WARNINGS if (n_out <= 0) { av_frame_free(&outsamplesref); av_frame_free(&insamplesref); + ff_inlink_request_frame(inlink); return 0; } @@ -260,8 +262,10 @@ static int flush_frame(AVFilterLink *outlink, int final, AVFrame **outsamplesref static int request_frame(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; + AVFilterLink *inlink = ctx->inputs[0]; AResampleContext *aresample = ctx->priv; - int ret; + int ret = 0, status; + int64_t pts; // First try to get data from the internal buffers if (aresample->more_data) { @@ -273,19 +277,52 @@ static int request_frame(AVFilterLink *outlink) } aresample->more_data = 0; + if (!aresample->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) + aresample->eof = 1; + // Second request more data from the input - ret = ff_request_frame(ctx->inputs[0]); + if (!aresample->eof) + FF_FILTER_FORWARD_WANTED(outlink, inlink); // Third if we hit the end flush - if (ret == AVERROR_EOF) { + if (aresample->eof) { AVFrame *outsamplesref; - if ((ret = flush_frame(outlink, 1, &outsamplesref)) < 0) + if ((ret = flush_frame(outlink, 1, &outsamplesref)) < 0) { + if (ret == AVERROR_EOF) { + ff_outlink_set_status(outlink, AVERROR_EOF, aresample->next_pts); + return 0; + } return ret; + } return ff_filter_frame(outlink, outsamplesref); } - return ret; + + ff_filter_set_ready(ctx, 100); + return 0; +} + +static int activate(AVFilterContext *ctx) +{ + AResampleContext *aresample = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + if (!aresample->eof && ff_inlink_queued_frames(inlink)) { + AVFrame *frame = NULL; + int ret; + + ret = ff_inlink_consume_frame(inlink, &frame); + if (ret < 0) + return ret; + if (ret > 0) + return filter_frame(inlink, frame); + } + + return request_frame(outlink); } static const AVClass *resample_child_class_iterate(void **iter) @@ -318,19 +355,10 @@ static const AVClass aresample_class = { .child_next = resample_child_next, }; -static const AVFilterPad aresample_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = filter_frame, - }, -}; - static const AVFilterPad aresample_outputs[] = { { .name = "default", .config_props = config_output, - .request_frame = request_frame, .type = AVMEDIA_TYPE_AUDIO, }, }; @@ -339,10 +367,11 @@ const AVFilter ff_af_aresample = { .name = "aresample", .description = NULL_IF_CONFIG_SMALL("Resample audio data."), .preinit = preinit, + .activate = activate, .uninit = uninit, .priv_size = sizeof(AResampleContext), .priv_class = &aresample_class, - FILTER_INPUTS(aresample_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(aresample_outputs), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_arls.c b/libavfilter/af_arls.c new file mode 100644 index 00000000000..bbe8e3cb21f --- /dev/null +++ b/libavfilter/af_arls.c @@ -0,0 +1,298 @@ +/* + * 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/common.h" +#include "libavutil/float_dsp.h" +#include "libavutil/opt.h" + +#include "audio.h" +#include "avfilter.h" +#include "formats.h" +#include "filters.h" +#include "internal.h" + +enum OutModes { + IN_MODE, + DESIRED_MODE, + OUT_MODE, + NOISE_MODE, + ERROR_MODE, + NB_OMODES +}; + +typedef struct AudioRLSContext { + const AVClass *class; + + int order; + float lambda; + float delta; + int output_mode; + int precision; + + int kernel_size; + AVFrame *offset; + AVFrame *delay; + AVFrame *coeffs; + AVFrame *p, *dp; + AVFrame *gains; + AVFrame *u, *tmp; + + AVFrame *frame[2]; + + int (*filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + + AVFloatDSPContext *fdsp; +} AudioRLSContext; + +#define OFFSET(x) offsetof(AudioRLSContext, x) +#define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define AT AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM + +static const AVOption arls_options[] = { + { "order", "set the filter order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=16}, 1, INT16_MAX, A }, + { "lambda", "set the filter lambda", OFFSET(lambda), AV_OPT_TYPE_FLOAT, {.dbl=1.f}, 0, 1, AT }, + { "delta", "set the filter delta", OFFSET(delta), AV_OPT_TYPE_FLOAT, {.dbl=2.f}, 0, INT16_MAX, A }, + { "out_mode", "set output mode", OFFSET(output_mode), AV_OPT_TYPE_INT, {.i64=OUT_MODE}, 0, NB_OMODES-1, AT, .unit = "mode" }, + { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64=IN_MODE}, 0, 0, AT, .unit = "mode" }, + { "d", "desired", 0, AV_OPT_TYPE_CONST, {.i64=DESIRED_MODE}, 0, 0, AT, .unit = "mode" }, + { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64=OUT_MODE}, 0, 0, AT, .unit = "mode" }, + { "n", "noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_MODE}, 0, 0, AT, .unit = "mode" }, + { "e", "error", 0, AV_OPT_TYPE_CONST, {.i64=ERROR_MODE}, 0, 0, AT, .unit = "mode" }, + { "precision", "set processing precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, A, .unit = "precision" }, + { "auto", "set auto processing precision", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, .unit = "precision" }, + { "float", "set single-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, .unit = "precision" }, + { "double","set double-floating point processing precision", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, A, .unit = "precision" }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(arls); + +static int query_formats(AVFilterContext *ctx) +{ + AudioRLSContext *s = ctx->priv; + static const enum AVSampleFormat sample_fmts[3][3] = { + { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE }, + { AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE }, + { AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_NONE }, + }; + int ret; + + if ((ret = ff_set_common_all_channel_counts(ctx)) < 0) + return ret; + + if ((ret = ff_set_common_formats_from_list(ctx, sample_fmts[s->precision])) < 0) + return ret; + + return ff_set_common_all_samplerates(ctx); +} + +static int activate(AVFilterContext *ctx) +{ + AudioRLSContext *s = ctx->priv; + int i, ret, status; + int nb_samples; + int64_t pts; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); + + nb_samples = FFMIN(ff_inlink_queued_samples(ctx->inputs[0]), + ff_inlink_queued_samples(ctx->inputs[1])); + for (i = 0; i < ctx->nb_inputs && nb_samples > 0; i++) { + if (s->frame[i]) + continue; + + if (ff_inlink_check_available_samples(ctx->inputs[i], nb_samples) > 0) { + ret = ff_inlink_consume_samples(ctx->inputs[i], nb_samples, nb_samples, &s->frame[i]); + if (ret < 0) + return ret; + } + } + + if (s->frame[0] && s->frame[1]) { + AVFrame *out; + + out = ff_get_audio_buffer(ctx->outputs[0], s->frame[0]->nb_samples); + if (!out) { + av_frame_free(&s->frame[0]); + av_frame_free(&s->frame[1]); + return AVERROR(ENOMEM); + } + + ff_filter_execute(ctx, s->filter_channels, out, NULL, + FFMIN(ctx->outputs[0]->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx))); + + out->pts = s->frame[0]->pts; + out->duration = s->frame[0]->duration; + + av_frame_free(&s->frame[0]); + av_frame_free(&s->frame[1]); + + ret = ff_filter_frame(ctx->outputs[0], out); + if (ret < 0) + return ret; + } + + if (!nb_samples) { + for (i = 0; i < 2; i++) { + if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) { + ff_outlink_set_status(ctx->outputs[0], status, pts); + return 0; + } + } + } + + if (ff_outlink_frame_wanted(ctx->outputs[0])) { + for (i = 0; i < 2; i++) { + if (s->frame[i] || ff_inlink_queued_samples(ctx->inputs[i]) > 0) + continue; + ff_inlink_request_frame(ctx->inputs[i]); + return 0; + } + } + return 0; +} + +#define DEPTH 32 +#include "arls_template.c" + +#undef DEPTH +#define DEPTH 64 +#include "arls_template.c" + +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AudioRLSContext *s = ctx->priv; + + s->kernel_size = FFALIGN(s->order, 16); + + if (!s->offset) + s->offset = ff_get_audio_buffer(outlink, 1); + if (!s->delay) + s->delay = ff_get_audio_buffer(outlink, 2 * s->kernel_size); + if (!s->coeffs) + s->coeffs = ff_get_audio_buffer(outlink, 2 * s->kernel_size); + if (!s->gains) + s->gains = ff_get_audio_buffer(outlink, s->kernel_size); + if (!s->p) + s->p = ff_get_audio_buffer(outlink, s->kernel_size * s->kernel_size); + if (!s->dp) + s->dp = ff_get_audio_buffer(outlink, s->kernel_size * s->kernel_size); + if (!s->u) + s->u = ff_get_audio_buffer(outlink, s->kernel_size); + if (!s->tmp) + s->tmp = ff_get_audio_buffer(outlink, s->kernel_size); + + if (!s->delay || !s->coeffs || !s->p || !s->dp || !s->gains || !s->offset || !s->u || !s->tmp) + return AVERROR(ENOMEM); + + for (int ch = 0; ch < s->offset->ch_layout.nb_channels; ch++) { + int *dst = (int *)s->offset->extended_data[ch]; + + for (int i = 0; i < s->kernel_size; i++) + dst[0] = s->kernel_size - 1; + } + + switch (outlink->format) { + case AV_SAMPLE_FMT_DBLP: + for (int ch = 0; ch < s->p->ch_layout.nb_channels; ch++) { + double *dst = (double *)s->p->extended_data[ch]; + + for (int i = 0; i < s->kernel_size; i++) + dst[i * s->kernel_size + i] = s->delta; + } + + s->filter_channels = filter_channels_double; + break; + case AV_SAMPLE_FMT_FLTP: + for (int ch = 0; ch < s->p->ch_layout.nb_channels; ch++) { + float *dst = (float *)s->p->extended_data[ch]; + + for (int i = 0; i < s->kernel_size; i++) + dst[i * s->kernel_size + i] = s->delta; + } + + s->filter_channels = filter_channels_float; + break; + } + + return 0; +} + +static av_cold int init(AVFilterContext *ctx) +{ + AudioRLSContext *s = ctx->priv; + + s->fdsp = avpriv_float_dsp_alloc(0); + if (!s->fdsp) + return AVERROR(ENOMEM); + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + AudioRLSContext *s = ctx->priv; + + av_freep(&s->fdsp); + av_frame_free(&s->delay); + av_frame_free(&s->coeffs); + av_frame_free(&s->gains); + av_frame_free(&s->offset); + av_frame_free(&s->p); + av_frame_free(&s->dp); + av_frame_free(&s->u); + av_frame_free(&s->tmp); +} + +static const AVFilterPad inputs[] = { + { + .name = "input", + .type = AVMEDIA_TYPE_AUDIO, + }, + { + .name = "desired", + .type = AVMEDIA_TYPE_AUDIO, + }, +}; + +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, + }, +}; + +const AVFilter ff_af_arls = { + .name = "arls", + .description = NULL_IF_CONFIG_SMALL("Apply Recursive Least Squares algorithm to first audio stream."), + .priv_size = sizeof(AudioRLSContext), + .priv_class = &arls_class, + .init = init, + .uninit = uninit, + .activate = activate, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_QUERY_FUNC(query_formats), + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | + AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, +}; diff --git a/libavfilter/af_arnndn.c b/libavfilter/af_arnndn.c index 634f3b972fe..ee005eb34c6 100644 --- a/libavfilter/af_arnndn.c +++ b/libavfilter/af_arnndn.c @@ -1585,13 +1585,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - #define OFFSET(x) offsetof(AudioRNNContext, x) #define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -1613,7 +1606,7 @@ const AVFilter ff_af_arnndn = { .init = init, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/af_asdr.c b/libavfilter/af_asdr.c index a40246f2802..bb5c30faba4 100644 --- a/libavfilter/af_asdr.c +++ b/libavfilter/af_asdr.c @@ -18,53 +18,139 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include "libavutil/channel_layout.h" #include "libavutil/common.h" -#include "libavutil/opt.h" -#include "audio.h" #include "avfilter.h" -#include "formats.h" #include "filters.h" #include "internal.h" +typedef struct ChanStats { + double u; + double v; + double uv; +} ChanStats; + typedef struct AudioSDRContext { int channels; - int64_t pts; - double *sum_u; - double *sum_uv; + uint64_t nb_samples; + double max; + + ChanStats *chs; AVFrame *cache[2]; + + int (*filter)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); } AudioSDRContext; -static void sdr(AVFilterContext *ctx, const AVFrame *u, const AVFrame *v) -{ - AudioSDRContext *s = ctx->priv; +#define SDR_FILTER(name, type) \ +static int sdr_##name(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)\ +{ \ + AudioSDRContext *s = ctx->priv; \ + AVFrame *u = s->cache[0]; \ + AVFrame *v = s->cache[1]; \ + const int channels = u->ch_layout.nb_channels; \ + const int start = (channels * jobnr) / nb_jobs; \ + const int end = (channels * (jobnr+1)) / nb_jobs; \ + const int nb_samples = u->nb_samples; \ + \ + for (int ch = start; ch < end; ch++) { \ + ChanStats *chs = &s->chs[ch]; \ + const type *const us = (type *)u->extended_data[ch]; \ + const type *const vs = (type *)v->extended_data[ch]; \ + double sum_uv = 0.; \ + double sum_u = 0.; \ + \ + for (int n = 0; n < nb_samples; n++) { \ + sum_u += us[n] * us[n]; \ + sum_uv += (us[n] - vs[n]) * (us[n] - vs[n]); \ + } \ + \ + chs->uv += sum_uv; \ + chs->u += sum_u; \ + } \ + \ + return 0; \ +} - for (int ch = 0; ch < u->ch_layout.nb_channels; ch++) { - const double *const us = (double *)u->extended_data[ch]; - const double *const vs = (double *)v->extended_data[ch]; - double sum_uv = s->sum_uv[ch]; - double sum_u = s->sum_u[ch]; +SDR_FILTER(fltp, float) +SDR_FILTER(dblp, double) - for (int n = 0; n < u->nb_samples; n++) { - sum_u += us[n] * us[n]; - sum_uv += (us[n] - vs[n]) * (us[n] - vs[n]); - } +#define SISDR_FILTER(name, type) \ +static int sisdr_##name(AVFilterContext *ctx, void *arg,int jobnr,int nb_jobs)\ +{ \ + AudioSDRContext *s = ctx->priv; \ + AVFrame *u = s->cache[0]; \ + AVFrame *v = s->cache[1]; \ + const int channels = u->ch_layout.nb_channels; \ + const int start = (channels * jobnr) / nb_jobs; \ + const int end = (channels * (jobnr+1)) / nb_jobs; \ + const int nb_samples = u->nb_samples; \ + \ + for (int ch = start; ch < end; ch++) { \ + ChanStats *chs = &s->chs[ch]; \ + const type *const us = (type *)u->extended_data[ch]; \ + const type *const vs = (type *)v->extended_data[ch]; \ + double sum_uv = 0.; \ + double sum_u = 0.; \ + double sum_v = 0.; \ + \ + for (int n = 0; n < nb_samples; n++) { \ + sum_u += us[n] * us[n]; \ + sum_v += vs[n] * vs[n]; \ + sum_uv += us[n] * vs[n]; \ + } \ + \ + chs->uv += sum_uv; \ + chs->u += sum_u; \ + chs->v += sum_v; \ + } \ + \ + return 0; \ +} - s->sum_uv[ch] = sum_uv; - s->sum_u[ch] = sum_u; - } +SISDR_FILTER(fltp, float) +SISDR_FILTER(dblp, double) + +#define PSNR_FILTER(name, type) \ +static int psnr_##name(AVFilterContext *ctx, void *arg, int jobnr,int nb_jobs)\ +{ \ + AudioSDRContext *s = ctx->priv; \ + AVFrame *u = s->cache[0]; \ + AVFrame *v = s->cache[1]; \ + const int channels = u->ch_layout.nb_channels; \ + const int start = (channels * jobnr) / nb_jobs; \ + const int end = (channels * (jobnr+1)) / nb_jobs; \ + const int nb_samples = u->nb_samples; \ + \ + for (int ch = start; ch < end; ch++) { \ + ChanStats *chs = &s->chs[ch]; \ + const type *const us = (type *)u->extended_data[ch]; \ + const type *const vs = (type *)v->extended_data[ch]; \ + double sum_uv = 0.; \ + \ + for (int n = 0; n < nb_samples; n++) \ + sum_uv += (us[n] - vs[n]) * (us[n] - vs[n]); \ + \ + chs->uv += sum_uv; \ + } \ + \ + return 0; \ } +PSNR_FILTER(fltp, float) +PSNR_FILTER(dblp, double) + static int activate(AVFilterContext *ctx) { AudioSDRContext *s = ctx->priv; - int ret, status; - int available; + AVFilterLink *outlink = ctx->outputs[0]; + int ret, status, available; int64_t pts; - FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); available = FFMIN(ff_inlink_queued_samples(ctx->inputs[0]), ff_inlink_queued_samples(ctx->inputs[1])); if (available > 0) { @@ -72,38 +158,39 @@ static int activate(AVFilterContext *ctx) for (int i = 0; i < 2; i++) { ret = ff_inlink_consume_samples(ctx->inputs[i], available, available, &s->cache[i]); - if (ret > 0) { - if (s->pts == AV_NOPTS_VALUE) - s->pts = s->cache[i]->pts; + if (ret < 0) { + av_frame_free(&s->cache[0]); + av_frame_free(&s->cache[1]); + return ret; } } - sdr(ctx, s->cache[0], s->cache[1]); + if (!ctx->is_disabled) + ff_filter_execute(ctx, s->filter, NULL, NULL, + FFMIN(outlink->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx))); av_frame_free(&s->cache[1]); out = s->cache[0]; - out->nb_samples = available; - out->pts = s->pts; - s->pts += available; s->cache[0] = NULL; - return ff_filter_frame(ctx->outputs[0], out); + s->nb_samples += available; + return ff_filter_frame(outlink, out); } for (int i = 0; i < 2; i++) { if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) { - ff_outlink_set_status(ctx->outputs[0], status, s->pts); + ff_outlink_set_status(outlink, status, pts); return 0; } } - if (ff_outlink_frame_wanted(ctx->outputs[0])) { + if (ff_outlink_frame_wanted(outlink)) { for (int i = 0; i < 2; i++) { - if (ff_inlink_queued_samples(ctx->inputs[i]) > 0) + if (s->cache[i] || ff_inlink_queued_samples(ctx->inputs[i]) > 0) continue; ff_inlink_request_frame(ctx->inputs[i]); + return 0; } - return 0; } return FFERROR_NOT_READY; @@ -115,13 +202,18 @@ static int config_output(AVFilterLink *outlink) AVFilterLink *inlink = ctx->inputs[0]; AudioSDRContext *s = ctx->priv; - s->pts = AV_NOPTS_VALUE; - s->channels = inlink->ch_layout.nb_channels; - s->sum_u = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->sum_u)); - s->sum_uv = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->sum_uv)); - if (!s->sum_u || !s->sum_uv) + if (!strcmp(ctx->filter->name, "asdr")) + s->filter = inlink->format == AV_SAMPLE_FMT_FLTP ? sdr_fltp : sdr_dblp; + else if (!strcmp(ctx->filter->name, "asisdr")) + s->filter = inlink->format == AV_SAMPLE_FMT_FLTP ? sisdr_fltp : sisdr_dblp; + else + s->filter = inlink->format == AV_SAMPLE_FMT_FLTP ? psnr_fltp : psnr_dblp; + s->max = inlink->format == AV_SAMPLE_FMT_FLTP ? FLT_MAX : DBL_MAX; + + s->chs = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->chs)); + if (!s->chs) return AVERROR(ENOMEM); return 0; @@ -131,14 +223,28 @@ static av_cold void uninit(AVFilterContext *ctx) { AudioSDRContext *s = ctx->priv; - for (int ch = 0; ch < s->channels; ch++) - av_log(ctx, AV_LOG_INFO, "SDR ch%d: %g dB\n", ch, 20. * log10(s->sum_u[ch] / s->sum_uv[ch])); + if (!strcmp(ctx->filter->name, "asdr")) { + for (int ch = 0; ch < s->channels; ch++) + av_log(ctx, AV_LOG_INFO, "SDR ch%d: %g dB\n", ch, 10. * log10(s->chs[ch].u / s->chs[ch].uv)); + } else if (!strcmp(ctx->filter->name, "asisdr")) { + for (int ch = 0; ch < s->channels; ch++) { + double scale = s->chs[ch].uv / s->chs[ch].v; + double sisdr = scale * scale * s->chs[ch].v / fmax(0., s->chs[ch].u + scale*scale*s->chs[ch].v - 2.0*scale*s->chs[ch].uv); + + av_log(ctx, AV_LOG_INFO, "SI-SDR ch%d: %g dB\n", ch, 10. * log10(sisdr)); + } + } else { + for (int ch = 0; ch < s->channels; ch++) { + double psnr = s->chs[ch].uv > 0.0 ? 2.0 * log(s->max) - log(s->nb_samples / s->chs[ch].uv) : INFINITY; + + av_log(ctx, AV_LOG_INFO, "PSNR ch%d: %g dB\n", ch, psnr); + } + } av_frame_free(&s->cache[0]); av_frame_free(&s->cache[1]); - av_freep(&s->sum_u); - av_freep(&s->sum_uv); + av_freep(&s->chs); } static const AVFilterPad inputs[] = { @@ -166,8 +272,41 @@ const AVFilter ff_af_asdr = { .priv_size = sizeof(AudioSDRContext), .activate = activate, .uninit = uninit, - .flags = AVFILTER_FLAG_METADATA_ONLY, + .flags = AVFILTER_FLAG_METADATA_ONLY | + AVFILTER_FLAG_SLICE_THREADS | + AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, + AV_SAMPLE_FMT_DBLP), +}; + +const AVFilter ff_af_apsnr = { + .name = "apsnr", + .description = NULL_IF_CONFIG_SMALL("Measure Audio Peak Signal-to-Noise Ratio."), + .priv_size = sizeof(AudioSDRContext), + .activate = activate, + .uninit = uninit, + .flags = AVFILTER_FLAG_METADATA_ONLY | + AVFILTER_FLAG_SLICE_THREADS | + AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), + FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, + AV_SAMPLE_FMT_DBLP), +}; + +const AVFilter ff_af_asisdr = { + .name = "asisdr", + .description = NULL_IF_CONFIG_SMALL("Measure Audio Scale-Invariant Signal-to-Distortion Ratio."), + .priv_size = sizeof(AudioSDRContext), + .activate = activate, + .uninit = uninit, + .flags = AVFILTER_FLAG_METADATA_ONLY | + AVFILTER_FLAG_SLICE_THREADS | + AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, FILTER_INPUTS(inputs), FILTER_OUTPUTS(outputs), - FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), + FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, + AV_SAMPLE_FMT_DBLP), }; diff --git a/libavfilter/af_asetnsamples.c b/libavfilter/af_asetnsamples.c index 74d3fde35e3..a12e6cadf99 100644 --- a/libavfilter/af_asetnsamples.c +++ b/libavfilter/af_asetnsamples.c @@ -30,7 +30,6 @@ #include "audio.h" #include "filters.h" #include "internal.h" -#include "formats.h" typedef struct ASNSContext { const AVClass *class; @@ -39,7 +38,7 @@ typedef struct ASNSContext { } ASNSContext; #define OFFSET(x) offsetof(ASNSContext, x) -#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption asetnsamples_options[] = { { "nb_out_samples", "set the number of per-frame output samples", OFFSET(nb_out_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, FLAGS }, @@ -61,12 +60,15 @@ static int activate(AVFilterContext *ctx) FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); - ret = ff_inlink_consume_samples(inlink, s->nb_out_samples, s->nb_out_samples, &frame); + if (ctx->is_disabled) + ret = ff_inlink_consume_frame(inlink, &frame); + else + ret = ff_inlink_consume_samples(inlink, s->nb_out_samples, s->nb_out_samples, &frame); if (ret < 0) return ret; if (ret > 0) { - if (!s->pad || frame->nb_samples == s->nb_out_samples) + if (!s->pad || ctx->is_disabled || frame->nb_samples == s->nb_out_samples) return ff_filter_frame(outlink, frame); pad_frame = ff_get_audio_buffer(outlink, s->nb_out_samples); @@ -101,26 +103,14 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad asetnsamples_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - -static const AVFilterPad asetnsamples_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asetnsamples = { .name = "asetnsamples", .description = NULL_IF_CONFIG_SMALL("Set the number of samples for each output audio frames."), .priv_size = sizeof(ASNSContext), .priv_class = &asetnsamples_class, - FILTER_INPUTS(asetnsamples_inputs), - FILTER_OUTPUTS(asetnsamples_outputs), + FILTER_INPUTS(ff_audio_default_filterpad), + FILTER_OUTPUTS(ff_audio_default_filterpad), .activate = activate, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/af_asetrate.c b/libavfilter/af_asetrate.c index 76f29144e5e..50ccfc91b9e 100644 --- a/libavfilter/af_asetrate.c +++ b/libavfilter/af_asetrate.c @@ -20,6 +20,7 @@ #include "libavutil/opt.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" typedef struct ASetRateContext { @@ -49,7 +50,17 @@ AVFILTER_DEFINE_CLASS(asetrate); static av_cold int query_formats(AVFilterContext *ctx) { ASetRateContext *sr = ctx->priv; - int sample_rates[] = { sr->sample_rate, -1 }; + int ret, sample_rates[] = { sr->sample_rate, -1 }; + + if ((ret = ff_set_common_formats(ctx, ff_all_formats(AVMEDIA_TYPE_AUDIO))) < 0) + return ret; + + if ((ret = ff_set_common_all_channel_counts(ctx)) < 0) + return ret; + + if ((ret = ff_formats_ref(ff_all_samplerates(), + &ctx->inputs[0]->outcfg.samplerates)) < 0) + return ret; return ff_formats_ref(ff_make_format_list(sample_rates), &ctx->outputs[0]->incfg.samplerates); diff --git a/libavfilter/af_ashowinfo.c b/libavfilter/af_ashowinfo.c index 36ba38b478b..b1b462d68f5 100644 --- a/libavfilter/af_ashowinfo.c +++ b/libavfilter/af_ashowinfo.c @@ -178,9 +178,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) { AVFilterContext *ctx = inlink->dst; AShowInfoContext *s = ctx->priv; -#if FF_API_OLD_CHANNEL_LAYOUT - AVChannelLayout layout = { 0 }; -#endif char chlayout_str[128]; uint32_t checksum = 0; int channels = inlink->ch_layout.nb_channels; @@ -203,22 +200,14 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) s->plane_checksums[0]; } -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (av_channel_layout_from_mask(&layout, buf->channel_layout)) { - av_channel_layout_describe(&layout, chlayout_str, sizeof(chlayout_str)); -FF_ENABLE_DEPRECATION_WARNINGS - } else if (buf->ch_layout.nb_channels) -#endif av_channel_layout_describe(&buf->ch_layout, chlayout_str, sizeof(chlayout_str)); av_log(ctx, AV_LOG_INFO, - "n:%"PRId64" pts:%s pts_time:%s pos:%"PRId64" " + "n:%"PRId64" pts:%s pts_time:%s " "fmt:%s channels:%d chlayout:%s rate:%d nb_samples:%d " "checksum:%08"PRIX32" ", inlink->frame_count_out, av_ts2str(buf->pts), av_ts2timestr(buf->pts, &inlink->time_base), - buf->pkt_pos, av_get_sample_fmt_name(buf->format), buf->ch_layout.nb_channels, chlayout_str, buf->sample_rate, buf->nb_samples, checksum); @@ -254,13 +243,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_ashowinfo = { .name = "ashowinfo", .description = NULL_IF_CONFIG_SMALL("Show textual information for each audio frame."), @@ -268,5 +250,5 @@ const AVFilter ff_af_ashowinfo = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; diff --git a/libavfilter/af_asoftclip.c b/libavfilter/af_asoftclip.c index 6212e1d6fee..e6483c439ce 100644 --- a/libavfilter/af_asoftclip.c +++ b/libavfilter/af_asoftclip.c @@ -23,7 +23,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #define MAX_OVERSAMPLE 64 @@ -69,16 +68,16 @@ typedef struct ASoftClipContext { #define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption asoftclip_options[] = { - { "type", "set softclip type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, -1, NB_TYPES-1, A, "types" }, - { "hard", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_HARD}, 0, 0, A, "types" }, - { "tanh", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_TANH}, 0, 0, A, "types" }, - { "atan", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_ATAN}, 0, 0, A, "types" }, - { "cubic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_CUBIC}, 0, 0, A, "types" }, - { "exp", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_EXP}, 0, 0, A, "types" }, - { "alg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_ALG}, 0, 0, A, "types" }, - { "quintic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_QUINTIC},0, 0, A, "types" }, - { "sin", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_SIN}, 0, 0, A, "types" }, - { "erf", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_ERF}, 0, 0, A, "types" }, + { "type", "set softclip type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, -1, NB_TYPES-1, A, .unit = "types" }, + { "hard", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_HARD}, 0, 0, A, .unit = "types" }, + { "tanh", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_TANH}, 0, 0, A, .unit = "types" }, + { "atan", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_ATAN}, 0, 0, A, .unit = "types" }, + { "cubic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_CUBIC}, 0, 0, A, .unit = "types" }, + { "exp", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_EXP}, 0, 0, A, .unit = "types" }, + { "alg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_ALG}, 0, 0, A, .unit = "types" }, + { "quintic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_QUINTIC},0, 0, A, .unit = "types" }, + { "sin", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_SIN}, 0, 0, A, .unit = "types" }, + { "erf", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ASC_ERF}, 0, 0, A, .unit = "types" }, { "threshold", "set softclip threshold", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.000001, 1, A }, { "output", "set softclip output gain", OFFSET(output), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.000001, 16, A }, { "param", "set softclip parameter", OFFSET(param), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.01, 3, A }, @@ -473,20 +472,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asoftclip = { .name = "asoftclip", .description = NULL_IF_CONFIG_SMALL("Audio Soft Clipper."), .priv_size = sizeof(ASoftClipContext), .priv_class = &asoftclip_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), .uninit = uninit, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_aspectralstats.c b/libavfilter/af_aspectralstats.c index b9db6bcfa59..49c9d97e8d0 100644 --- a/libavfilter/af_aspectralstats.c +++ b/libavfilter/af_aspectralstats.c @@ -87,22 +87,22 @@ static const AVOption aspectralstats_options[] = { { "win_size", "set the window size", OFFSET(win_size), AV_OPT_TYPE_INT, {.i64=2048}, 32, 65536, A }, WIN_FUNC_OPTION("win_func", OFFSET(win_func), A, WFUNC_HANNING), { "overlap", "set window overlap", OFFSET(overlap), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, A }, - { "measure", "select the parameters which are measured", OFFSET(measure), AV_OPT_TYPE_FLAGS, {.i64=MEASURE_ALL}, 0, UINT_MAX, A, "measure" }, - { "none", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NONE }, 0, 0, A, "measure" }, - { "all", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ALL }, 0, 0, A, "measure" }, - { "mean", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MEAN }, 0, 0, A, "measure" }, - { "variance", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_VARIANCE}, 0, 0, A, "measure" }, - { "centroid", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_CENTROID}, 0, 0, A, "measure" }, - { "spread", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_SPREAD }, 0, 0, A, "measure" }, - { "skewness", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_SKEWNESS}, 0, 0, A, "measure" }, - { "kurtosis", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_KURTOSIS}, 0, 0, A, "measure" }, - { "entropy", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ENTROPY }, 0, 0, A, "measure" }, - { "flatness", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_FLATNESS}, 0, 0, A, "measure" }, - { "crest", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_CREST }, 0, 0, A, "measure" }, - { "flux", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_FLUX }, 0, 0, A, "measure" }, - { "slope", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_SLOPE }, 0, 0, A, "measure" }, - { "decrease", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_DECREASE}, 0, 0, A, "measure" }, - { "rolloff", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ROLLOFF }, 0, 0, A, "measure" }, + { "measure", "select the parameters which are measured", OFFSET(measure), AV_OPT_TYPE_FLAGS, {.i64=MEASURE_ALL}, 0, UINT_MAX, A, .unit = "measure" }, + { "none", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NONE }, 0, 0, A, .unit = "measure" }, + { "all", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ALL }, 0, 0, A, .unit = "measure" }, + { "mean", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MEAN }, 0, 0, A, .unit = "measure" }, + { "variance", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_VARIANCE}, 0, 0, A, .unit = "measure" }, + { "centroid", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_CENTROID}, 0, 0, A, .unit = "measure" }, + { "spread", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_SPREAD }, 0, 0, A, .unit = "measure" }, + { "skewness", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_SKEWNESS}, 0, 0, A, .unit = "measure" }, + { "kurtosis", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_KURTOSIS}, 0, 0, A, .unit = "measure" }, + { "entropy", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ENTROPY }, 0, 0, A, .unit = "measure" }, + { "flatness", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_FLATNESS}, 0, 0, A, .unit = "measure" }, + { "crest", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_CREST }, 0, 0, A, .unit = "measure" }, + { "flux", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_FLUX }, 0, 0, A, .unit = "measure" }, + { "slope", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_SLOPE }, 0, 0, A, .unit = "measure" }, + { "decrease", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_DECREASE}, 0, 0, A, .unit = "measure" }, + { "rolloff", "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ROLLOFF }, 0, 0, A, .unit = "measure" }, { NULL } }; @@ -600,13 +600,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&s->window); } -static const AVFilterPad aspectralstats_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad aspectralstats_outputs[] = { { .name = "default", @@ -622,7 +615,7 @@ const AVFilter ff_af_aspectralstats = { .priv_class = &aspectralstats_class, .uninit = uninit, .activate = activate, - FILTER_INPUTS(aspectralstats_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(aspectralstats_outputs), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .flags = AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/af_asr.c b/libavfilter/af_asr.c index b402f5ff261..884e17afb7e 100644 --- a/libavfilter/af_asr.c +++ b/libavfilter/af_asr.c @@ -25,6 +25,7 @@ #include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" typedef struct ASRContext { @@ -158,13 +159,6 @@ static const AVFilterPad asr_inputs[] = { }, }; -static const AVFilterPad asr_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asr = { .name = "asr", .description = NULL_IF_CONFIG_SMALL("Automatic Speech Recognition."), @@ -174,6 +168,6 @@ const AVFilter ff_af_asr = { .uninit = asr_uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(asr_inputs), - FILTER_OUTPUTS(asr_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_astats.c b/libavfilter/af_astats.c index f3f2bd743b5..9e8c9635826 100644 --- a/libavfilter/af_astats.c +++ b/libavfilter/af_astats.c @@ -58,6 +58,7 @@ #define MEASURE_NOISE_FLOOR (1 << 22) #define MEASURE_NOISE_FLOOR_COUNT (1 << 23) #define MEASURE_ENTROPY (1 << 24) +#define MEASURE_ABS_PEAK_COUNT (1 << 25) #define MEASURE_MINMAXPEAK (MEASURE_MIN_LEVEL | MEASURE_MAX_LEVEL | MEASURE_PEAK_LEVEL) @@ -74,8 +75,10 @@ typedef struct ChannelStats { double min_diff, max_diff; double diff1_sum; double diff1_sum_x2; - uint64_t mask, imask; + double abs_peak; + uint64_t mask[4]; uint64_t min_count, max_count; + uint64_t abs_peak_count; uint64_t noise_floor_count; uint64_t zero_runs; uint64_t nb_samples; @@ -83,8 +86,11 @@ typedef struct ChannelStats { uint64_t nb_infs; uint64_t nb_denormals; double *win_samples; - uint64_t histogram[HISTOGRAM_SIZE]; + double *sorted_samples; uint64_t ehistogram[HISTOGRAM_SIZE]; + int64_t lasti; + int sorted_front; + int sorted_back; int win_pos; int max_index; double noise_floor; @@ -116,35 +122,36 @@ static const AVOption astats_options[] = { { "length", "set the window length", OFFSET(time_constant), AV_OPT_TYPE_DOUBLE, {.dbl=.05}, 0, 10, FLAGS }, { "metadata", "inject metadata in the filtergraph", OFFSET(metadata), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { "reset", "Set the number of frames over which cumulative stats are calculated before being reset", OFFSET(reset_count), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, - { "measure_perchannel", "Select the parameters which are measured per channel", OFFSET(measure_perchannel), AV_OPT_TYPE_FLAGS, {.i64=MEASURE_ALL}, 0, UINT_MAX, FLAGS, "measure" }, - { "none" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NONE }, 0, 0, FLAGS, "measure" }, - { "all" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ALL }, 0, 0, FLAGS, "measure" }, - { "Bit_depth" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_BIT_DEPTH }, 0, 0, FLAGS, "measure" }, - { "Crest_factor" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_CREST_FACTOR }, 0, 0, FLAGS, "measure" }, - { "DC_offset" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_DC_OFFSET }, 0, 0, FLAGS, "measure" }, - { "Dynamic_range" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_DYNAMIC_RANGE }, 0, 0, FLAGS, "measure" }, - { "Entropy" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ENTROPY }, 0, 0, FLAGS, "measure" }, - { "Flat_factor" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_FLAT_FACTOR }, 0, 0, FLAGS, "measure" }, - { "Max_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MAX_DIFFERENCE }, 0, 0, FLAGS, "measure" }, - { "Max_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MAX_LEVEL }, 0, 0, FLAGS, "measure" }, - { "Mean_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MEAN_DIFFERENCE }, 0, 0, FLAGS, "measure" }, - { "Min_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MIN_DIFFERENCE }, 0, 0, FLAGS, "measure" }, - { "Min_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MIN_LEVEL }, 0, 0, FLAGS, "measure" }, - { "Noise_floor" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NOISE_FLOOR }, 0, 0, FLAGS, "measure" }, - { "Noise_floor_count" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NOISE_FLOOR_COUNT }, 0, 0, FLAGS, "measure" }, - { "Number_of_Infs" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_INFS }, 0, 0, FLAGS, "measure" }, - { "Number_of_NaNs" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_NANS }, 0, 0, FLAGS, "measure" }, - { "Number_of_denormals" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_DENORMALS }, 0, 0, FLAGS, "measure" }, - { "Number_of_samples" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_SAMPLES }, 0, 0, FLAGS, "measure" }, - { "Peak_count" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_PEAK_COUNT }, 0, 0, FLAGS, "measure" }, - { "Peak_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_PEAK_LEVEL }, 0, 0, FLAGS, "measure" }, - { "RMS_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_RMS_DIFFERENCE }, 0, 0, FLAGS, "measure" }, - { "RMS_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_RMS_LEVEL }, 0, 0, FLAGS, "measure" }, - { "RMS_peak" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_RMS_PEAK }, 0, 0, FLAGS, "measure" }, - { "RMS_trough" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_RMS_TROUGH }, 0, 0, FLAGS, "measure" }, - { "Zero_crossings" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ZERO_CROSSINGS }, 0, 0, FLAGS, "measure" }, - { "Zero_crossings_rate" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ZERO_CROSSINGS_RATE }, 0, 0, FLAGS, "measure" }, - { "measure_overall", "Select the parameters which are measured overall", OFFSET(measure_overall), AV_OPT_TYPE_FLAGS, {.i64=MEASURE_ALL}, 0, UINT_MAX, FLAGS, "measure" }, + { "measure_perchannel", "Select the parameters which are measured per channel", OFFSET(measure_perchannel), AV_OPT_TYPE_FLAGS, {.i64=MEASURE_ALL}, 0, UINT_MAX, FLAGS, .unit = "measure" }, + { "none" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NONE }, 0, 0, FLAGS, .unit = "measure" }, + { "all" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ALL }, 0, 0, FLAGS, .unit = "measure" }, + { "Bit_depth" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_BIT_DEPTH }, 0, 0, FLAGS, .unit = "measure" }, + { "Crest_factor" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_CREST_FACTOR }, 0, 0, FLAGS, .unit = "measure" }, + { "DC_offset" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_DC_OFFSET }, 0, 0, FLAGS, .unit = "measure" }, + { "Dynamic_range" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_DYNAMIC_RANGE }, 0, 0, FLAGS, .unit = "measure" }, + { "Entropy" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ENTROPY }, 0, 0, FLAGS, .unit = "measure" }, + { "Flat_factor" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_FLAT_FACTOR }, 0, 0, FLAGS, .unit = "measure" }, + { "Max_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MAX_DIFFERENCE }, 0, 0, FLAGS, .unit = "measure" }, + { "Max_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MAX_LEVEL }, 0, 0, FLAGS, .unit = "measure" }, + { "Mean_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MEAN_DIFFERENCE }, 0, 0, FLAGS, .unit = "measure" }, + { "Min_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MIN_DIFFERENCE }, 0, 0, FLAGS, .unit = "measure" }, + { "Min_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_MIN_LEVEL }, 0, 0, FLAGS, .unit = "measure" }, + { "Noise_floor" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NOISE_FLOOR }, 0, 0, FLAGS, .unit = "measure" }, + { "Noise_floor_count" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NOISE_FLOOR_COUNT }, 0, 0, FLAGS, .unit = "measure" }, + { "Number_of_Infs" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_INFS }, 0, 0, FLAGS, .unit = "measure" }, + { "Number_of_NaNs" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_NANS }, 0, 0, FLAGS, .unit = "measure" }, + { "Number_of_denormals" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_DENORMALS }, 0, 0, FLAGS, .unit = "measure" }, + { "Number_of_samples" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_NUMBER_OF_SAMPLES }, 0, 0, FLAGS, .unit = "measure" }, + { "Peak_count" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_PEAK_COUNT }, 0, 0, FLAGS, .unit = "measure" }, + { "Peak_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_PEAK_LEVEL }, 0, 0, FLAGS, .unit = "measure" }, + { "RMS_difference" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_RMS_DIFFERENCE }, 0, 0, FLAGS, .unit = "measure" }, + { "RMS_level" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_RMS_LEVEL }, 0, 0, FLAGS, .unit = "measure" }, + { "RMS_peak" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_RMS_PEAK }, 0, 0, FLAGS, .unit = "measure" }, + { "RMS_trough" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_RMS_TROUGH }, 0, 0, FLAGS, .unit = "measure" }, + { "Zero_crossings" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ZERO_CROSSINGS }, 0, 0, FLAGS, .unit = "measure" }, + { "Zero_crossings_rate" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ZERO_CROSSINGS_RATE }, 0, 0, FLAGS, .unit = "measure" }, + { "Abs_Peak_count" , "", 0, AV_OPT_TYPE_CONST, {.i64=MEASURE_ABS_PEAK_COUNT }, 0, 0, FLAGS, .unit = "measure" }, + { "measure_overall", "Select the parameters which are measured overall", OFFSET(measure_overall), AV_OPT_TYPE_FLAGS, {.i64=MEASURE_ALL}, 0, UINT_MAX, FLAGS, .unit = "measure" }, { NULL } }; @@ -159,6 +166,7 @@ static void reset_stats(AudioStatsContext *s) p->min = p->nmin = p->min_sigma_x2 = DBL_MAX; p->max = p->nmax = p->max_sigma_x2 =-DBL_MAX; + p->abs_peak = 0; p->min_non_zero = DBL_MAX; p->min_diff = DBL_MAX; p->max_diff = 0; @@ -171,10 +179,13 @@ static void reset_stats(AudioStatsContext *s) p->max_runs = 0; p->diff1_sum = 0; p->diff1_sum_x2 = 0; - p->mask = 0; - p->imask = 0xFFFFFFFFFFFFFFFF; + p->mask[0] = 0; + p->mask[1] = 0; + p->mask[2] =~0; + p->mask[3] = 0; p->min_count = 0; p->max_count = 0; + p->abs_peak_count = 0; p->zero_runs = 0; p->nb_samples = 0; p->nb_nans = 0; @@ -185,9 +196,12 @@ static void reset_stats(AudioStatsContext *s) p->noise_floor_count = 0; p->entropy = 0; p->win_pos = 0; + p->sorted_front = 0; + p->sorted_back = 0; memset(p->win_samples, 0, s->tc_samples * sizeof(*p->win_samples)); - memset(p->histogram, 0, sizeof(p->histogram)); memset(p->ehistogram, 0, sizeof(p->ehistogram)); + for (int n = 0; n < s->tc_samples; n++) + p->sorted_samples[n] = -1.0; } } @@ -208,6 +222,10 @@ static int config_output(AVFilterLink *outlink) p->win_samples = av_calloc(s->tc_samples, sizeof(*p->win_samples)); if (!p->win_samples) return AVERROR(ENOMEM); + + p->sorted_samples = av_calloc(s->tc_samples, sizeof(*p->sorted_samples)); + if (!p->sorted_samples) + return AVERROR(ENOMEM); } s->mult = exp((-1 / s->time_constant / outlink->sample_rate)); @@ -224,20 +242,28 @@ static int config_output(AVFilterLink *outlink) return 0; } -static void bit_depth(AudioStatsContext *s, uint64_t mask, uint64_t imask, AVRational *depth) +static void bit_depth(AudioStatsContext *s, const uint64_t *const mask, uint8_t *depth) { unsigned result = s->maxbitdepth; + uint64_t amask = mask[1] & (~mask[2]); - mask = mask & (~imask); + depth[0] = 0; + for (int i = 0; i < result; i++) + depth[0] += !!(mask[0] & (1ULL << i)); - for (; result && !(mask & 1); --result, mask >>= 1); + depth[1] = 0; + for (int i = 0; i < result; i++) + depth[1] += !!(mask[1] & (1ULL << i)); - depth->den = result; - depth->num = 0; + depth[2] = result; + for (int i = 0; i < result && !(amask & 1); i++) { + depth[2]--; + amask >>= 1; + } - for (; result; --result, mask >>= 1) - if (mask & 1) - depth->num++; + depth[3] = 0; + for (int i = 0; i < result; i++) + depth[3] += !!(mask[3] & (1ULL << i)); } static double calc_entropy(AudioStatsContext *s, ChannelStats *p) @@ -254,6 +280,63 @@ static double calc_entropy(AudioStatsContext *s, ChannelStats *p) return -entropy / log2(HISTOGRAM_SIZE); } +static double calc_noise_floor(double *ss, double x, double px, + int n, int *ffront, int *bback) +{ + double r, ax = fabs(x); + int front = *ffront; + int back = *bback; + int empty = front == back && ss[front] == -1.0; + + if (!empty && fabs(px) == ss[front]) { + ss[front] = -1.0; + if (back != front) { + front--; + if (front < 0) + front = n - 1; + } + empty = front == back; + } + + if (!empty && ax >= ss[front]) { + while (1) { + ss[front] = -1.0; + if (back == front) { + empty = 1; + break; + } + front--; + if (front < 0) + front = n - 1; + } + } + + while (!empty && ax >= ss[back]) { + ss[back] = -1.0; + if (back == front) { + empty = 1; + break; + } + back++; + if (back >= n) + back = 0; + } + + if (!empty) { + back--; + if (back < 0) + back = n - 1; + } + + ss[back] = ax; + r = ss[front]; + + *ffront = front; + *bback = back; + + return r; +} + static inline void update_minmax(AudioStatsContext *s, ChannelStats *p, double d) { if (d < p->min) @@ -264,9 +347,16 @@ static inline void update_minmax(AudioStatsContext *s, ChannelStats *p, double d static inline void update_stat(AudioStatsContext *s, ChannelStats *p, double d, double nd, int64_t i) { - double drop; + double abs_d = FFABS(d); + double drop, noise_floor; int index; + if (p->abs_peak < abs_d) { + p->abs_peak = abs_d; + p->abs_peak_count = 1; + } else if (p->abs_peak == abs_d) { + p->abs_peak_count++; + } if (d < p->min) { p->min = d; p->nmin = nd; @@ -310,32 +400,33 @@ static inline void update_stat(AudioStatsContext *s, ChannelStats *p, double d, p->diff1_sum += fabs(d - p->last); p->diff1_sum_x2 += (d - p->last) * (d - p->last); } + p->mask[0] |= (i < 0) ? -i : i; + p->mask[1] |= i; + p->mask[2] &= i; + if (!isnan(p->last)) + p->mask[3] |= i ^ p->lasti; + p->lasti = i; p->last = d; - p->mask |= i; - p->imask &= i; drop = p->win_samples[p->win_pos]; p->win_samples[p->win_pos] = nd; index = av_clip(lrint(av_clipd(FFABS(nd), 0.0, 1.0) * HISTOGRAM_MAX), 0, HISTOGRAM_MAX); p->max_index = FFMAX(p->max_index, index); - p->histogram[index]++; p->ehistogram[index]++; - if (!isnan(p->noise_floor)) - p->histogram[av_clip(lrint(av_clipd(FFABS(drop), 0.0, 1.0) * HISTOGRAM_MAX), 0, HISTOGRAM_MAX)]--; p->win_pos++; - while (p->histogram[p->max_index] == 0) - p->max_index--; - if (p->win_pos >= s->tc_samples || !isnan(p->noise_floor)) { - double noise_floor = 1.; + if (p->win_pos >= s->tc_samples) + p->win_pos = 0; - for (int i = p->max_index; i >= 0; i--) { - if (p->histogram[i]) { - noise_floor = i / (double)HISTOGRAM_MAX; - break; - } - } + if (p->nb_samples >= s->tc_samples) { + p->max_sigma_x2 = FFMAX(p->max_sigma_x2, p->avg_sigma_x2); + p->min_sigma_x2 = FFMIN(p->min_sigma_x2, p->avg_sigma_x2); + } + p->nb_samples++; + noise_floor = calc_noise_floor(p->sorted_samples, nd, drop, + s->tc_samples, &p->sorted_front, &p->sorted_back); + if (p->nb_samples >= s->tc_samples) { if (isnan(p->noise_floor)) { p->noise_floor = noise_floor; p->noise_floor_count = 1; @@ -348,16 +439,6 @@ static inline void update_stat(AudioStatsContext *s, ChannelStats *p, double d, } } } - - if (p->win_pos >= s->tc_samples) { - p->win_pos = 0; - } - - if (p->nb_samples >= s->tc_samples) { - p->max_sigma_x2 = FFMAX(p->max_sigma_x2, p->avg_sigma_x2); - p->min_sigma_x2 = FFMIN(p->min_sigma_x2, p->avg_sigma_x2); - } - p->nb_samples++; } static inline void update_float_stat(AudioStatsContext *s, ChannelStats *p, float d) @@ -396,8 +477,9 @@ static void set_meta(AVDictionary **metadata, int chan, const char *key, static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) { - uint64_t mask = 0, imask = 0xFFFFFFFFFFFFFFFF, min_count = 0, max_count = 0, nb_samples = 0, noise_floor_count = 0; + uint64_t mask[4], min_count = 0, max_count = 0, nb_samples = 0, noise_floor_count = 0; uint64_t nb_nans = 0, nb_infs = 0, nb_denormals = 0; + uint64_t abs_peak_count = 0; double min_runs = 0, max_runs = 0, min = DBL_MAX, max =-DBL_MAX, min_diff = DBL_MAX, max_diff = 0, nmin = DBL_MAX, nmax =-DBL_MAX, @@ -409,9 +491,14 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) entropy = 0, min_sigma_x2 = DBL_MAX, max_sigma_x2 =-DBL_MAX; - AVRational depth; + uint8_t depth[4]; int c; + mask[0] = 0; + mask[1] = 0; + mask[2] =~0; + mask[3] = 0; + for (c = 0; c < s->nb_channels; c++) { ChannelStats *p = &s->chstats[c]; @@ -435,10 +522,13 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) entropy += p->entropy; min_count += p->min_count; max_count += p->max_count; + abs_peak_count += p->abs_peak_count; min_runs += p->min_runs; max_runs += p->max_runs; - mask |= p->mask; - imask &= p->imask; + mask[0] |= p->mask[0]; + mask[1] |= p->mask[1]; + mask[2] &= p->mask[2]; + mask[3] |= p->mask[3]; nb_samples += p->nb_samples; nb_nans += p->nb_nans; nb_infs += p->nb_infs; @@ -474,6 +564,8 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) set_meta(metadata, c + 1, "Flat_factor", "%f", LINEAR_TO_DB((p->min_runs + p->max_runs) / (p->min_count + p->max_count))); if (s->measure_perchannel & MEASURE_PEAK_COUNT) set_meta(metadata, c + 1, "Peak_count", "%f", (float)(p->min_count + p->max_count)); + if (s->measure_perchannel & MEASURE_ABS_PEAK_COUNT) + set_meta(metadata, c + 1, "Peak_count", "%f", p->abs_peak_count); if (s->measure_perchannel & MEASURE_NOISE_FLOOR) set_meta(metadata, c + 1, "Noise_floor", "%f", LINEAR_TO_DB(p->noise_floor)); if (s->measure_perchannel & MEASURE_NOISE_FLOOR_COUNT) @@ -481,9 +573,11 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) if (s->measure_perchannel & MEASURE_ENTROPY) set_meta(metadata, c + 1, "Entropy", "%f", p->entropy); if (s->measure_perchannel & MEASURE_BIT_DEPTH) { - bit_depth(s, p->mask, p->imask, &depth); - set_meta(metadata, c + 1, "Bit_depth", "%f", depth.num); - set_meta(metadata, c + 1, "Bit_depth2", "%f", depth.den); + bit_depth(s, p->mask, depth); + set_meta(metadata, c + 1, "Bit_depth", "%f", depth[0]); + set_meta(metadata, c + 1, "Bit_depth2", "%f", depth[1]); + set_meta(metadata, c + 1, "Bit_depth3", "%f", depth[2]); + set_meta(metadata, c + 1, "Bit_depth4", "%f", depth[3]); } if (s->measure_perchannel & MEASURE_DYNAMIC_RANGE) set_meta(metadata, c + 1, "Dynamic_range", "%f", LINEAR_TO_DB(2 * FFMAX(FFABS(p->min), FFABS(p->max))/ p->min_non_zero)); @@ -525,6 +619,8 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) set_meta(metadata, 0, "Overall.Flat_factor", "%f", LINEAR_TO_DB((min_runs + max_runs) / (min_count + max_count))); if (s->measure_overall & MEASURE_PEAK_COUNT) set_meta(metadata, 0, "Overall.Peak_count", "%f", (float)(min_count + max_count) / (double)s->nb_channels); + if (s->measure_overall & MEASURE_ABS_PEAK_COUNT) + set_meta(metadata, 0, "Overall.Abs_Peak_count", "%f", (float)(abs_peak_count) / (double)s->nb_channels); if (s->measure_overall & MEASURE_NOISE_FLOOR) set_meta(metadata, 0, "Overall.Noise_floor", "%f", LINEAR_TO_DB(noise_floor)); if (s->measure_overall & MEASURE_NOISE_FLOOR_COUNT) @@ -532,9 +628,11 @@ static void set_metadata(AudioStatsContext *s, AVDictionary **metadata) if (s->measure_overall & MEASURE_ENTROPY) set_meta(metadata, 0, "Overall.Entropy", "%f", entropy / (double)s->nb_channels); if (s->measure_overall & MEASURE_BIT_DEPTH) { - bit_depth(s, mask, imask, &depth); - set_meta(metadata, 0, "Overall.Bit_depth", "%f", depth.num); - set_meta(metadata, 0, "Overall.Bit_depth2", "%f", depth.den); + bit_depth(s, mask, depth); + set_meta(metadata, 0, "Overall.Bit_depth", "%f", depth[0]); + set_meta(metadata, 0, "Overall.Bit_depth2", "%f", depth[1]); + set_meta(metadata, 0, "Overall.Bit_depth3", "%f", depth[2]); + set_meta(metadata, 0, "Overall.Bit_depth4", "%f", depth[3]); } if (s->measure_overall & MEASURE_NUMBER_OF_SAMPLES) set_meta(metadata, 0, "Overall.Number_of_samples", "%f", nb_samples / s->nb_channels); @@ -652,8 +750,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) static void print_stats(AVFilterContext *ctx) { AudioStatsContext *s = ctx->priv; - uint64_t mask = 0, imask = 0xFFFFFFFFFFFFFFFF, min_count = 0, max_count = 0, nb_samples = 0, noise_floor_count = 0; - uint64_t nb_nans = 0, nb_infs = 0, nb_denormals = 0; + uint64_t mask[4], min_count = 0, max_count = 0, nb_samples = 0, noise_floor_count = 0; + uint64_t nb_nans = 0, nb_infs = 0, nb_denormals = 0, abs_peak_count = 0; double min_runs = 0, max_runs = 0, min = DBL_MAX, max =-DBL_MAX, min_diff = DBL_MAX, max_diff = 0, nmin = DBL_MAX, nmax =-DBL_MAX, @@ -665,9 +763,14 @@ static void print_stats(AVFilterContext *ctx) entropy = 0, min_sigma_x2 = DBL_MAX, max_sigma_x2 =-DBL_MAX; - AVRational depth; + uint8_t depth[4]; int c; + mask[0] = 0; + mask[1] = 0; + mask[2] =~0; + mask[3] = 0; + for (c = 0; c < s->nb_channels; c++) { ChannelStats *p = &s->chstats[c]; @@ -693,11 +796,14 @@ static void print_stats(AVFilterContext *ctx) entropy += p->entropy; min_count += p->min_count; max_count += p->max_count; + abs_peak_count += p->abs_peak_count; noise_floor_count += p->noise_floor_count; min_runs += p->min_runs; max_runs += p->max_runs; - mask |= p->mask; - imask &= p->imask; + mask[0] |= p->mask[0]; + mask[1] |= p->mask[1]; + mask[2] &= p->mask[2]; + mask[3] |= p->mask[3]; nb_samples += p->nb_samples; nb_nans += p->nb_nans; nb_infs += p->nb_infs; @@ -736,6 +842,8 @@ static void print_stats(AVFilterContext *ctx) av_log(ctx, AV_LOG_INFO, "Flat factor: %f\n", LINEAR_TO_DB((p->min_runs + p->max_runs) / (p->min_count + p->max_count))); if (s->measure_perchannel & MEASURE_PEAK_COUNT) av_log(ctx, AV_LOG_INFO, "Peak count: %"PRId64"\n", p->min_count + p->max_count); + if (s->measure_perchannel & MEASURE_ABS_PEAK_COUNT) + av_log(ctx, AV_LOG_INFO, "Abs Peak count: %"PRId64"\n", p->abs_peak_count); if (s->measure_perchannel & MEASURE_NOISE_FLOOR) av_log(ctx, AV_LOG_INFO, "Noise floor dB: %f\n", LINEAR_TO_DB(p->noise_floor)); if (s->measure_perchannel & MEASURE_NOISE_FLOOR_COUNT) @@ -743,8 +851,8 @@ static void print_stats(AVFilterContext *ctx) if (s->measure_perchannel & MEASURE_ENTROPY) av_log(ctx, AV_LOG_INFO, "Entropy: %f\n", p->entropy); if (s->measure_perchannel & MEASURE_BIT_DEPTH) { - bit_depth(s, p->mask, p->imask, &depth); - av_log(ctx, AV_LOG_INFO, "Bit depth: %u/%u\n", depth.num, depth.den); + bit_depth(s, p->mask, depth); + av_log(ctx, AV_LOG_INFO, "Bit depth: %u/%u/%u/%u\n", depth[0], depth[1], depth[2], depth[3]); } if (s->measure_perchannel & MEASURE_DYNAMIC_RANGE) av_log(ctx, AV_LOG_INFO, "Dynamic range: %f\n", LINEAR_TO_DB(2 * FFMAX(FFABS(p->min), FFABS(p->max))/ p->min_non_zero)); @@ -792,6 +900,8 @@ static void print_stats(AVFilterContext *ctx) av_log(ctx, AV_LOG_INFO, "Flat factor: %f\n", LINEAR_TO_DB((min_runs + max_runs) / (min_count + max_count))); if (s->measure_overall & MEASURE_PEAK_COUNT) av_log(ctx, AV_LOG_INFO, "Peak count: %f\n", (min_count + max_count) / (double)s->nb_channels); + if (s->measure_overall & MEASURE_ABS_PEAK_COUNT) + av_log(ctx, AV_LOG_INFO, "Abs Peak count: %f\n", abs_peak_count / (double)s->nb_channels); if (s->measure_overall & MEASURE_NOISE_FLOOR) av_log(ctx, AV_LOG_INFO, "Noise floor dB: %f\n", LINEAR_TO_DB(noise_floor)); if (s->measure_overall & MEASURE_NOISE_FLOOR_COUNT) @@ -799,8 +909,8 @@ static void print_stats(AVFilterContext *ctx) if (s->measure_overall & MEASURE_ENTROPY) av_log(ctx, AV_LOG_INFO, "Entropy: %f\n", entropy / (double)s->nb_channels); if (s->measure_overall & MEASURE_BIT_DEPTH) { - bit_depth(s, mask, imask, &depth); - av_log(ctx, AV_LOG_INFO, "Bit depth: %u/%u\n", depth.num, depth.den); + bit_depth(s, mask, depth); + av_log(ctx, AV_LOG_INFO, "Bit depth: %u/%u/%u/%u\n", depth[0], depth[1], depth[2], depth[3]); } if (s->measure_overall & MEASURE_NUMBER_OF_SAMPLES) av_log(ctx, AV_LOG_INFO, "Number of samples: %"PRId64"\n", nb_samples / s->nb_channels); @@ -823,6 +933,7 @@ static av_cold void uninit(AVFilterContext *ctx) ChannelStats *p = &s->chstats[i]; av_freep(&p->win_samples); + av_freep(&p->sorted_samples); } } av_freep(&s->chstats); diff --git a/libavfilter/af_asubboost.c b/libavfilter/af_asubboost.c index 29a1f66ce65..31db4b81a0c 100644 --- a/libavfilter/af_asubboost.c +++ b/libavfilter/af_asubboost.c @@ -21,7 +21,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" typedef struct ASubBoostContext { const AVClass *class; @@ -237,13 +236,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asubboost = { .name = "asubboost", .description = NULL_IF_CONFIG_SMALL("Boost subwoofer frequencies."), @@ -251,7 +243,7 @@ const AVFilter ff_af_asubboost = { .priv_class = &asubboost_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | diff --git a/libavfilter/af_asupercut.c b/libavfilter/af_asupercut.c index 5c06d559ed3..848388c5208 100644 --- a/libavfilter/af_asupercut.c +++ b/libavfilter/af_asupercut.c @@ -24,7 +24,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" typedef struct BiquadCoeffs { double a1, a2; @@ -333,13 +332,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asupercut = { .name = "asupercut", .description = NULL_IF_CONFIG_SMALL("Cut super frequencies."), @@ -347,7 +339,7 @@ const AVFilter ff_af_asupercut = { .priv_class = &asupercut_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS_ARRAY(sample_fmts), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | @@ -370,7 +362,7 @@ const AVFilter ff_af_asubcut = { .priv_class = &asubcut_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS_ARRAY(sample_fmts), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | @@ -395,7 +387,7 @@ const AVFilter ff_af_asuperpass = { .priv_size = sizeof(ASuperCutContext), .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS_ARRAY(sample_fmts), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | @@ -409,7 +401,7 @@ const AVFilter ff_af_asuperstop = { .priv_size = sizeof(ASuperCutContext), .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS_ARRAY(sample_fmts), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | diff --git a/libavfilter/af_atempo.c b/libavfilter/af_atempo.c index 27f13638b02..654b080e893 100644 --- a/libavfilter/af_atempo.c +++ b/libavfilter/af_atempo.c @@ -247,10 +247,10 @@ static void yae_release_buffers(ATempoContext *atempo) /* av_realloc is not aligned enough; fortunately, the data does not need to * be preserved */ -#define RE_MALLOC_OR_FAIL(field, field_size) \ +#define RE_MALLOC_OR_FAIL(field, field_size, element_size) \ do { \ av_freep(&field); \ - field = av_calloc(field_size, 1); \ + field = av_calloc(field_size, element_size); \ if (!field) { \ yae_release_buffers(atempo); \ return AVERROR(ENOMEM); \ @@ -290,12 +290,12 @@ static int yae_reset(ATempoContext *atempo, } // initialize audio fragment buffers: - RE_MALLOC_OR_FAIL(atempo->frag[0].data, atempo->window * atempo->stride); - RE_MALLOC_OR_FAIL(atempo->frag[1].data, atempo->window * atempo->stride); - RE_MALLOC_OR_FAIL(atempo->frag[0].xdat_in, (atempo->window + 1) * sizeof(AVComplexFloat)); - RE_MALLOC_OR_FAIL(atempo->frag[1].xdat_in, (atempo->window + 1) * sizeof(AVComplexFloat)); - RE_MALLOC_OR_FAIL(atempo->frag[0].xdat, (atempo->window + 1) * sizeof(AVComplexFloat)); - RE_MALLOC_OR_FAIL(atempo->frag[1].xdat, (atempo->window + 1) * sizeof(AVComplexFloat)); + RE_MALLOC_OR_FAIL(atempo->frag[0].data, atempo->window, atempo->stride); + RE_MALLOC_OR_FAIL(atempo->frag[1].data, atempo->window, atempo->stride); + RE_MALLOC_OR_FAIL(atempo->frag[0].xdat_in, (atempo->window + 1), sizeof(AVComplexFloat)); + RE_MALLOC_OR_FAIL(atempo->frag[1].xdat_in, (atempo->window + 1), sizeof(AVComplexFloat)); + RE_MALLOC_OR_FAIL(atempo->frag[0].xdat, (atempo->window + 1), sizeof(AVComplexFloat)); + RE_MALLOC_OR_FAIL(atempo->frag[1].xdat, (atempo->window + 1), sizeof(AVComplexFloat)); // initialize rDFT contexts: av_tx_uninit(&atempo->real_to_complex); @@ -313,14 +313,14 @@ static int yae_reset(ATempoContext *atempo, return AVERROR(ENOMEM); } - RE_MALLOC_OR_FAIL(atempo->correlation_in, (atempo->window + 1) * sizeof(AVComplexFloat)); - RE_MALLOC_OR_FAIL(atempo->correlation, atempo->window * sizeof(AVComplexFloat)); + RE_MALLOC_OR_FAIL(atempo->correlation_in, (atempo->window + 1), sizeof(AVComplexFloat)); + RE_MALLOC_OR_FAIL(atempo->correlation, atempo->window, sizeof(AVComplexFloat)); atempo->ring = atempo->window * 3; - RE_MALLOC_OR_FAIL(atempo->buffer, atempo->ring * atempo->stride); + RE_MALLOC_OR_FAIL(atempo->buffer, atempo->ring, atempo->stride); // initialize the Hann window function: - RE_MALLOC_OR_FAIL(atempo->hann, atempo->window * sizeof(float)); + RE_MALLOC_OR_FAIL(atempo->hann, atempo->window, sizeof(float)); for (i = 0; i < atempo->window; i++) { double t = (double)i / (double)(atempo->window - 1); @@ -531,21 +531,21 @@ static int yae_load_frag(ATempoContext *atempo, dst = frag->data; start = atempo->position[0] - atempo->size; - zeros = 0; - if (frag->position[0] < start) { - // what we don't have we substitute with zeros: - zeros = FFMIN(start - frag->position[0], (int64_t)nsamples); - av_assert0(zeros != nsamples); - - memset(dst, 0, zeros * atempo->stride); - dst += zeros * atempo->stride; - } + // what we don't have we substitute with zeros: + zeros = + frag->position[0] < start ? + FFMIN(start - frag->position[0], (int64_t)nsamples) : 0; if (zeros == nsamples) { return 0; } + if (frag->position[0] < start) { + memset(dst, 0, zeros * atempo->stride); + dst += zeros * atempo->stride; + } + // get the remaining data from the ring buffer: na = (atempo->head < atempo->tail ? atempo->tail - atempo->head : diff --git a/libavfilter/af_atilt.c b/libavfilter/af_atilt.c index 9ece531ea4d..172e3259dbc 100644 --- a/libavfilter/af_atilt.c +++ b/libavfilter/af_atilt.c @@ -21,7 +21,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #define MAX_ORDER 30 @@ -246,13 +245,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_atilt = { .name = "atilt", .description = NULL_IF_CONFIG_SMALL("Apply spectral tilt to audio."), @@ -260,7 +252,7 @@ const AVFilter ff_af_atilt = { .priv_class = &atilt_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | diff --git a/libavfilter/af_axcorrelate.c b/libavfilter/af_axcorrelate.c index 9be3f1c9215..82a36f257d7 100644 --- a/libavfilter/af_axcorrelate.c +++ b/libavfilter/af_axcorrelate.c @@ -25,7 +25,6 @@ #include "audio.h" #include "avfilter.h" -#include "formats.h" #include "filters.h" #include "internal.h" @@ -110,7 +109,7 @@ static int xcorrelate_slow_##suffix(AVFilterContext *ctx, \ AVFrame *out, int available) \ { \ AudioXCorrelateContext *s = ctx->priv; \ - const int size = FFMIN(available, s->size); \ + const int size = s->size; \ int used; \ \ for (int ch = 0; ch < out->ch_layout.nb_channels; ch++) { \ @@ -127,13 +126,13 @@ static int xcorrelate_slow_##suffix(AVFilterContext *ctx, \ used = 1; \ } \ \ - for (int n = 0; n < out->nb_samples; n++) { \ - const int idx = available <= s->size ? out->nb_samples - n - 1 : n + size; \ - \ - dst[n] = xcorrelate_##suffix(x + n, y + n, \ - sumx[0], sumy[0], \ - size); \ - \ + for (int n = 0; n < out->nb_samples; n++) { \ + const int idx = n + size; \ + \ + dst[n] = xcorrelate_##suffix(x + n, y + n, \ + sumx[0], sumy[0],\ + size); \ + \ sumx[0] -= x[n]; \ sumx[0] += x[idx]; \ sumy[0] -= y[n]; \ @@ -147,12 +146,15 @@ static int xcorrelate_slow_##suffix(AVFilterContext *ctx, \ XCORRELATE_SLOW(f, float) XCORRELATE_SLOW(d, double) -#define XCORRELATE_FAST(suffix, type, zero, small, sqrtfun) \ +#define clipf(x) (av_clipf(x, -1.f, 1.f)) +#define clipd(x) (av_clipd(x, -1.0, 1.0)) + +#define XCORRELATE_FAST(suffix, type, zero, small, sqrtfun, CLIP) \ static int xcorrelate_fast_##suffix(AVFilterContext *ctx, AVFrame *out, \ int available) \ { \ AudioXCorrelateContext *s = ctx->priv; \ - const int size = FFMIN(available, s->size); \ + const int size = s->size; \ int used; \ \ for (int ch = 0; ch < out->ch_layout.nb_channels; ch++) { \ @@ -171,14 +173,14 @@ static int xcorrelate_fast_##suffix(AVFilterContext *ctx, AVFrame *out, \ used = 1; \ } \ \ - for (int n = 0; n < out->nb_samples; n++) { \ - const int idx = available <= s->size ? out->nb_samples - n - 1 : n + size; \ - type num, den; \ + for (int n = 0; n < out->nb_samples; n++) { \ + const int idx = n + size; \ + type num, den; \ \ num = num_sum[0] / size; \ den = sqrtfun((den_sumx[0] * den_sumy[0]) / size / size); \ \ - dst[n] = den <= small ? zero : num / den; \ + dst[n] = den <= small ? zero : CLIP(num / den); \ \ num_sum[0] -= x[n] * y[n]; \ num_sum[0] += x[idx] * y[idx]; \ @@ -194,20 +196,82 @@ static int xcorrelate_fast_##suffix(AVFilterContext *ctx, AVFrame *out, \ return used; \ } -XCORRELATE_FAST(f, float, 0.f, 1e-6f, sqrtf) -XCORRELATE_FAST(d, double, 0.0, 1e-9, sqrt) +XCORRELATE_FAST(f, float, 0.f, 1e-6f, sqrtf, clipf) +XCORRELATE_FAST(d, double, 0.0, 1e-9, sqrt, clipd) + +#define XCORRELATE_BEST(suffix, type, zero, small, sqrtfun, FMAX, CLIP) \ +static int xcorrelate_best_##suffix(AVFilterContext *ctx, AVFrame *out, \ + int available) \ +{ \ + AudioXCorrelateContext *s = ctx->priv; \ + const int size = s->size; \ + int used; \ + \ + for (int ch = 0; ch < out->ch_layout.nb_channels; ch++) { \ + const type *x = (const type *)s->cache[0]->extended_data[ch]; \ + const type *y = (const type *)s->cache[1]->extended_data[ch]; \ + type *mean_sumx = (type *)s->mean_sum[0]->extended_data[ch]; \ + type *mean_sumy = (type *)s->mean_sum[1]->extended_data[ch]; \ + type *num_sum = (type *)s->num_sum->extended_data[ch]; \ + type *den_sumx = (type *)s->den_sum[0]->extended_data[ch]; \ + type *den_sumy = (type *)s->den_sum[1]->extended_data[ch]; \ + type *dst = (type *)out->extended_data[ch]; \ + \ + used = s->used; \ + if (!used) { \ + num_sum[0] = square_sum_##suffix(x, y, size); \ + den_sumx[0] = square_sum_##suffix(x, x, size); \ + den_sumy[0] = square_sum_##suffix(y, y, size); \ + mean_sumx[0] = mean_sum_##suffix(x, size); \ + mean_sumy[0] = mean_sum_##suffix(y, size); \ + used = 1; \ + } \ + \ + for (int n = 0; n < out->nb_samples; n++) { \ + const int idx = n + size; \ + type num, den, xm, ym; \ + \ + xm = mean_sumx[0] / size; \ + ym = mean_sumy[0] / size; \ + num = num_sum[0] - size * xm * ym; \ + den = sqrtfun(FMAX(den_sumx[0] - size * xm * xm, zero)) * \ + sqrtfun(FMAX(den_sumy[0] - size * ym * ym, zero)); \ + \ + dst[n] = den <= small ? zero : CLIP(num / den); \ + \ + mean_sumx[0]-= x[n]; \ + mean_sumx[0]+= x[idx]; \ + mean_sumy[0]-= y[n]; \ + mean_sumy[0]+= y[idx]; \ + num_sum[0] -= x[n] * y[n]; \ + num_sum[0] += x[idx] * y[idx]; \ + den_sumx[0] -= x[n] * x[n]; \ + den_sumx[0] += x[idx] * x[idx]; \ + den_sumx[0] = FMAX(den_sumx[0], zero); \ + den_sumy[0] -= y[n] * y[n]; \ + den_sumy[0] += y[idx] * y[idx]; \ + den_sumy[0] = FMAX(den_sumy[0], zero); \ + } \ + } \ + \ + return used; \ +} + +XCORRELATE_BEST(f, float, 0.f, 1e-6f, sqrtf, fmaxf, clipf) +XCORRELATE_BEST(d, double, 0.0, 1e-9, sqrt, fmax, clipd) static int activate(AVFilterContext *ctx) { AudioXCorrelateContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; AVFrame *frame = NULL; int ret, status; int available; int64_t pts; - FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 2 && !s->eof; i++) { ret = ff_inlink_consume_frame(ctx->inputs[i], &frame); if (ret > 0) { if (s->pts == AV_NOPTS_VALUE) @@ -221,20 +285,20 @@ static int activate(AVFilterContext *ctx) } available = FFMIN(av_audio_fifo_size(s->fifo[0]), av_audio_fifo_size(s->fifo[1])); - if (available > s->size || (s->eof && available > 0)) { - const int out_samples = s->eof ? available : available - s->size; + if (available > s->size) { + const int out_samples = available - s->size; AVFrame *out; if (!s->cache[0] || s->cache[0]->nb_samples < available) { av_frame_free(&s->cache[0]); - s->cache[0] = ff_get_audio_buffer(ctx->outputs[0], available); + s->cache[0] = ff_get_audio_buffer(outlink, available); if (!s->cache[0]) return AVERROR(ENOMEM); } if (!s->cache[1] || s->cache[1]->nb_samples < available) { av_frame_free(&s->cache[1]); - s->cache[1] = ff_get_audio_buffer(ctx->outputs[0], available); + s->cache[1] = ff_get_audio_buffer(outlink, available); if (!s->cache[1]) return AVERROR(ENOMEM); } @@ -247,7 +311,7 @@ static int activate(AVFilterContext *ctx) if (ret < 0) return ret; - out = ff_get_audio_buffer(ctx->outputs[0], out_samples); + out = ff_get_audio_buffer(outlink, out_samples); if (!out) return AVERROR(ENOMEM); @@ -259,18 +323,31 @@ static int activate(AVFilterContext *ctx) av_audio_fifo_drain(s->fifo[0], out_samples); av_audio_fifo_drain(s->fifo[1], out_samples); - return ff_filter_frame(ctx->outputs[0], out); + return ff_filter_frame(outlink, out); } for (int i = 0; i < 2 && !s->eof; i++) { - if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) + if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) { + AVFrame *silence = ff_get_audio_buffer(outlink, s->size); + s->eof = 1; + if (!silence) + return AVERROR(ENOMEM); + + av_audio_fifo_write(s->fifo[0], (void **)silence->extended_data, + silence->nb_samples); + + av_audio_fifo_write(s->fifo[1], (void **)silence->extended_data, + silence->nb_samples); + + av_frame_free(&silence); + } } if (s->eof && - (av_audio_fifo_size(s->fifo[0]) <= 0 || - av_audio_fifo_size(s->fifo[1]) <= 0)) { - ff_outlink_set_status(ctx->outputs[0], AVERROR_EOF, s->pts); + (av_audio_fifo_size(s->fifo[0]) <= s->size || + av_audio_fifo_size(s->fifo[1]) <= s->size)) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); return 0; } @@ -280,7 +357,7 @@ static int activate(AVFilterContext *ctx) return 0; } - if (ff_outlink_frame_wanted(ctx->outputs[0]) && !s->eof) { + if (ff_outlink_frame_wanted(outlink) && !s->eof) { for (int i = 0; i < 2; i++) { if (av_audio_fifo_size(s->fifo[i]) > s->size) continue; @@ -316,12 +393,14 @@ static int config_output(AVFilterLink *outlink) switch (s->algo) { case 0: s->xcorrelate = xcorrelate_slow_f; break; case 1: s->xcorrelate = xcorrelate_fast_f; break; + case 2: s->xcorrelate = xcorrelate_best_f; break; } if (outlink->format == AV_SAMPLE_FMT_DBLP) { switch (s->algo) { case 0: s->xcorrelate = xcorrelate_slow_d; break; case 1: s->xcorrelate = xcorrelate_fast_d; break; + case 2: s->xcorrelate = xcorrelate_best_d; break; } } @@ -366,10 +445,11 @@ static const AVFilterPad outputs[] = { #define OFFSET(x) offsetof(AudioXCorrelateContext, x) static const AVOption axcorrelate_options[] = { - { "size", "set segment size", OFFSET(size), AV_OPT_TYPE_INT, {.i64=256}, 2, 131072, AF }, - { "algo", "set algorithm", OFFSET(algo), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, AF, "algo" }, - { "slow", "slow algorithm", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "algo" }, - { "fast", "fast algorithm", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "algo" }, + { "size", "set the segment size", OFFSET(size), AV_OPT_TYPE_INT, {.i64=256}, 2, 131072, AF }, + { "algo", "set the algorithm", OFFSET(algo), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, AF, .unit = "algo" }, + { "slow", "slow algorithm", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "algo" }, + { "fast", "fast algorithm", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "algo" }, + { "best", "best algorithm", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, .unit = "algo" }, { NULL } }; diff --git a/libavfilter/af_biquads.c b/libavfilter/af_biquads.c index 49ad0a471f0..ddca9d87480 100644 --- a/libavfilter/af_biquads.c +++ b/libavfilter/af_biquads.c @@ -71,6 +71,7 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" enum FilterType { @@ -109,14 +110,6 @@ enum TransformType { NB_TTYPE, }; -typedef struct ChanCache { - double i1, i2; - double o1, o2; - double ri1, ri2; - double ro1, ro2; - int clippings; -} ChanCache; - typedef struct BiquadsContext { const AVClass *class; @@ -139,24 +132,26 @@ typedef struct BiquadsContext { int normalize; int order; - double a0, a1, a2; - double b0, b1, b2; + double a_double[3]; + double b_double[3]; + + float a_float[3]; + float b_float[3]; - double oa0, oa1, oa2; - double ob0, ob1, ob2; + double oa[3]; + double ob[3]; AVFrame *block[3]; - ChanCache *cache; + int *clip; + AVFrame *cache[2]; int block_align; int64_t pts; int nb_samples; void (*filter)(struct BiquadsContext *s, const void *ibuf, void *obuf, int len, - double *i1, double *i2, double *o1, double *o2, - double b0, double b1, double b2, double a0, double a1, double a2, int *clippings, - int disabled); + void *cache, int *clip, int disabled); } BiquadsContext; static int query_formats(AVFilterContext *ctx) @@ -202,27 +197,26 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_all_samplerates(ctx); } -#define BIQUAD_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_FILTER(name, type, ftype, min, max, need_clipping) \ static void biquad_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *in1, double *in2, \ - double *out1, double *out2, \ - double b0, double b1, double b2, \ - double a0, double a1, double a2, int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double i1 = *in1; \ - double i2 = *in2; \ - double o1 = *out1; \ - double o2 = *out2; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double out; \ + ftype *fcache = cache; \ + ftype i1 = fcache[0], i2 = fcache[1], o1 = fcache[2], o2 = fcache[3]; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype a1 = -a[1]; \ + ftype a2 = -a[2]; \ + ftype b0 = b[0]; \ + ftype b1 = b[1]; \ + ftype b2 = b[2]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype out; \ int i; \ - a1 = -a1; \ - a2 = -a2; \ \ for (i = 0; i+1 < len; i++) { \ o2 = i2 * b2 + i1 * b1 + ibuf[i] * b0 + o2 * a2 + o1 * a1; \ @@ -256,7 +250,7 @@ static void biquad_## name (BiquadsContext *s, \ } \ } \ if (i < len) { \ - double o0 = ibuf[i] * b0 + i1 * b1 + i2 * b2 + o1 * a1 + o2 * a2; \ + ftype o0 = ibuf[i] * b0 + i1 * b1 + i2 * b2 + o1 * a1 + o2 * a2; \ i2 = i1; \ i1 = ibuf[i]; \ o2 = o1; \ @@ -274,36 +268,37 @@ static void biquad_## name (BiquadsContext *s, \ obuf[i] = out; \ } \ } \ - *in1 = i1; \ - *in2 = i2; \ - *out1 = o1; \ - *out2 = o2; \ + fcache[0] = i1; \ + fcache[1] = i2; \ + fcache[2] = o1; \ + fcache[3] = o2; \ } -BIQUAD_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_FILTER(flt, float, -1., 1., 0) -BIQUAD_FILTER(dbl, double, -1., 1., 0) +BIQUAD_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1) +BIQUAD_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1) +BIQUAD_FILTER(flt, float, float, -1.f, 1.f, 0) +BIQUAD_FILTER(dbl, double, double, -1., 1., 0) -#define BIQUAD_DII_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_DII_FILTER(name, type, ftype, min, max, need_clipping) \ static void biquad_dii_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *z1, double *z2, \ - double *unused1, double *unused2, \ - double b0, double b1, double b2, \ - double a0, double a1, double a2, int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double w1 = *z1; \ - double w2 = *z2; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double in, out, w0; \ - \ - a1 = -a1; \ - a2 = -a2; \ + ftype *fcache = cache; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype a1 = -a[1]; \ + ftype a2 = -a[2]; \ + ftype b0 = b[0]; \ + ftype b1 = b[1]; \ + ftype b2 = b[2]; \ + ftype w1 = fcache[0]; \ + ftype w2 = fcache[1]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype in, out, w0; \ \ for (int i = 0; i < len; i++) { \ in = ibuf[i]; \ @@ -324,39 +319,40 @@ static void biquad_dii_## name (BiquadsContext *s, \ obuf[i] = out; \ } \ } \ - *z1 = w1; \ - *z2 = w2; \ + fcache[0] = w1; \ + fcache[1] = w2; \ } -BIQUAD_DII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_DII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_DII_FILTER(flt, float, -1., 1., 0) -BIQUAD_DII_FILTER(dbl, double, -1., 1., 0) +BIQUAD_DII_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1) +BIQUAD_DII_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1) +BIQUAD_DII_FILTER(flt, float, float, -1.f, 1.f, 0) +BIQUAD_DII_FILTER(dbl, double, double, -1., 1., 0) -#define BIQUAD_TDI_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_TDI_FILTER(name, type, ftype, min, max, need_clipping) \ static void biquad_tdi_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *z1, double *z2, \ - double *z3, double *z4, \ - double b0, double b1, double b2, \ - double a0, double a1, double a2, int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double s1 = *z1; \ - double s2 = *z2; \ - double s3 = *z3; \ - double s4 = *z4; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double in, out; \ - \ - a1 = -a1; \ - a2 = -a2; \ + ftype *fcache = cache; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype a1 = -a[1]; \ + ftype a2 = -a[2]; \ + ftype b0 = b[0]; \ + ftype b1 = b[1]; \ + ftype b2 = b[2]; \ + ftype s1 = fcache[0]; \ + ftype s2 = fcache[1]; \ + ftype s3 = fcache[2]; \ + ftype s4 = fcache[3]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype in, out; \ \ for (int i = 0; i < len; i++) { \ - double t1, t2, t3, t4; \ + ftype t1, t2, t3, t4; \ in = ibuf[i] + s1; \ t1 = in * a1 + s2; \ t2 = in * a2; \ @@ -378,36 +374,37 @@ static void biquad_tdi_## name (BiquadsContext *s, \ } \ } \ \ - *z1 = s1; \ - *z2 = s2; \ - *z3 = s3; \ - *z4 = s4; \ + fcache[0] = s1; \ + fcache[1] = s2; \ + fcache[2] = s3; \ + fcache[3] = s4; \ } -BIQUAD_TDI_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_TDI_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_TDI_FILTER(flt, float, -1., 1., 0) -BIQUAD_TDI_FILTER(dbl, double, -1., 1., 0) +BIQUAD_TDI_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1) +BIQUAD_TDI_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1) +BIQUAD_TDI_FILTER(flt, float, float, -1.f, 1.f, 0) +BIQUAD_TDI_FILTER(dbl, double, double, -1., 1., 0) -#define BIQUAD_TDII_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_TDII_FILTER(name, type, ftype, min, max, need_clipping) \ static void biquad_tdii_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *z1, double *z2, \ - double *unused1, double *unused2, \ - double b0, double b1, double b2, \ - double a0, double a1, double a2, int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double w1 = *z1; \ - double w2 = *z2; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double in, out; \ - \ - a1 = -a1; \ - a2 = -a2; \ + ftype *fcache = cache; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype a1 = -a[1]; \ + ftype a2 = -a[2]; \ + ftype b0 = b[0]; \ + ftype b1 = b[1]; \ + ftype b2 = b[2]; \ + ftype w1 = fcache[0]; \ + ftype w2 = fcache[1]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype in, out; \ \ for (int i = 0; i < len; i++) { \ in = ibuf[i]; \ @@ -427,33 +424,36 @@ static void biquad_tdii_## name (BiquadsContext *s, \ obuf[i] = out; \ } \ } \ - *z1 = w1; \ - *z2 = w2; \ + fcache[0] = w1; \ + fcache[1] = w2; \ } -BIQUAD_TDII_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_TDII_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_TDII_FILTER(flt, float, -1., 1., 0) -BIQUAD_TDII_FILTER(dbl, double, -1., 1., 0) +BIQUAD_TDII_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1) +BIQUAD_TDII_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1) +BIQUAD_TDII_FILTER(flt, float, float, -1.f, 1.f, 0) +BIQUAD_TDII_FILTER(dbl, double, double, -1., 1., 0) -#define BIQUAD_LATT_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_LATT_FILTER(name, type, ftype, min, max, need_clipping) \ static void biquad_latt_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *z1, double *z2, \ - double *unused1, double *unused2, \ - double v0, double v1, double v2, \ - double unused, double k0, double k1, \ - int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double s0 = *z1; \ - double s1 = *z2; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double in, out; \ - double t0, t1; \ + ftype *fcache = cache; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype k0 = a[1]; \ + ftype k1 = a[2]; \ + ftype v0 = b[0]; \ + ftype v1 = b[1]; \ + ftype v2 = b[2]; \ + ftype s0 = fcache[0]; \ + ftype s1 = fcache[1]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype in, out; \ + ftype t0, t1; \ \ for (int i = 0; i < len; i++) { \ out = 0.; \ @@ -483,32 +483,36 @@ static void biquad_latt_## name (BiquadsContext *s, \ obuf[i] = out; \ } \ } \ - *z1 = s0; \ - *z2 = s1; \ + fcache[0] = s0; \ + fcache[1] = s1; \ } -BIQUAD_LATT_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_LATT_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_LATT_FILTER(flt, float, -1., 1., 0) -BIQUAD_LATT_FILTER(dbl, double, -1., 1., 0) +BIQUAD_LATT_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1) +BIQUAD_LATT_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1) +BIQUAD_LATT_FILTER(flt, float, float, -1.f, 1.f, 0) +BIQUAD_LATT_FILTER(dbl, double, double, -1., 1., 0) -#define BIQUAD_SVF_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_SVF_FILTER(name, type, ftype, min, max, need_clipping) \ static void biquad_svf_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *y0, double *y1, \ - double *unused1, double *unused2, \ - double b0, double b1, double b2, \ - double a0, double a1, double a2, int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double s0 = *y0; \ - double s1 = *y1; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double in, out; \ - double t0, t1; \ + ftype *fcache = cache; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype a1 = a[1]; \ + ftype a2 = a[2]; \ + ftype b0 = b[0]; \ + ftype b1 = b[1]; \ + ftype b2 = b[2]; \ + ftype s0 = fcache[0]; \ + ftype s1 = fcache[1]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype in, out; \ + ftype t0, t1; \ \ for (int i = 0; i < len; i++) { \ in = ibuf[i]; \ @@ -531,41 +535,46 @@ static void biquad_svf_## name (BiquadsContext *s, \ obuf[i] = out; \ } \ } \ - *y0 = s0; \ - *y1 = s1; \ + fcache[0] = s0; \ + fcache[1] = s1; \ } -BIQUAD_SVF_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_SVF_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_SVF_FILTER(flt, float, -1., 1., 0) -BIQUAD_SVF_FILTER(dbl, double, -1., 1., 0) +BIQUAD_SVF_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1) +BIQUAD_SVF_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1) +BIQUAD_SVF_FILTER(flt, float, float, -1.f, 1.f, 0) +BIQUAD_SVF_FILTER(dbl, double, double, -1., 1., 0) -#define BIQUAD_ZDF_FILTER(name, type, min, max, need_clipping) \ +#define BIQUAD_ZDF_FILTER(name, type, ftype, min, max, need_clipping, two) \ static void biquad_zdf_## name (BiquadsContext *s, \ const void *input, void *output, int len, \ - double *y0, double *y1, \ - double *unused1, double *unused2, \ - double m0, double m1, double m2, \ - double a0, double a1, double a2, int *clippings, \ - int disabled) \ + void *cache, int *clippings, int disabled) \ { \ const type *ibuf = input; \ type *obuf = output; \ - double b0 = *y0; \ - double b1 = *y1; \ - double wet = s->mix; \ - double dry = 1. - wet; \ - double out; \ + ftype *fcache = cache; \ + ftype *a = s->a_##ftype; \ + ftype *b = s->b_##ftype; \ + ftype m0 = b[0]; \ + ftype m1 = b[1]; \ + ftype m2 = b[2]; \ + ftype a0 = a[0]; \ + ftype a1 = a[1]; \ + ftype a2 = a[2]; \ + ftype b0 = fcache[0]; \ + ftype b1 = fcache[1]; \ + ftype wet = s->mix; \ + ftype dry = 1. - wet; \ + ftype out; \ \ for (int i = 0; i < len; i++) { \ - const double in = ibuf[i]; \ - const double v0 = in; \ - const double v3 = v0 - b1; \ - const double v1 = a0 * b0 + a1 * v3; \ - const double v2 = b1 + a1 * b0 + a2 * v3; \ + const ftype in = ibuf[i]; \ + const ftype v0 = in; \ + const ftype v3 = v0 - b1; \ + const ftype v1 = a0 * b0 + a1 * v3; \ + const ftype v2 = b1 + a1 * b0 + a2 * v3; \ \ - b0 = 2. * v1 - b0; \ - b1 = 2. * v2 - b1; \ + b0 = two * v1 - b0; \ + b1 = two * v2 - b1; \ \ out = m0 * v0 + m1 * v1 + m2 * v2; \ out = out * wet + in * dry; \ @@ -581,30 +590,30 @@ static void biquad_zdf_## name (BiquadsContext *s, \ obuf[i] = out; \ } \ } \ - *y0 = b0; \ - *y1 = b1; \ + fcache[0] = b0; \ + fcache[1] = b1; \ } -BIQUAD_ZDF_FILTER(s16, int16_t, INT16_MIN, INT16_MAX, 1) -BIQUAD_ZDF_FILTER(s32, int32_t, INT32_MIN, INT32_MAX, 1) -BIQUAD_ZDF_FILTER(flt, float, -1., 1., 0) -BIQUAD_ZDF_FILTER(dbl, double, -1., 1., 0) +BIQUAD_ZDF_FILTER(s16, int16_t, float, INT16_MIN, INT16_MAX, 1, 2.f) +BIQUAD_ZDF_FILTER(s32, int32_t, double, INT32_MIN, INT32_MAX, 1, 2.0) +BIQUAD_ZDF_FILTER(flt, float, float, -1.f, 1.f, 0, 2.f) +BIQUAD_ZDF_FILTER(dbl, double, double, -1., 1., 0, 2.0) static void convert_dir2latt(BiquadsContext *s) { double k0, k1, v0, v1, v2; - k1 = s->a2; - k0 = s->a1 / (1. + k1); - v2 = s->b2; - v1 = s->b1 - v2 * s->a1; - v0 = s->b0 - v1 * k0 - v2 * k1; - - s->a1 = k0; - s->a2 = k1; - s->b0 = v0; - s->b1 = v1; - s->b2 = v2; + k1 = s->a_double[2]; + k0 = s->a_double[1] / (1. + k1); + v2 = s->b_double[2]; + v1 = s->b_double[1] - v2 * s->a_double[1]; + v0 = s->b_double[0] - v1 * k0 - v2 * k1; + + s->a_double[1] = k0; + s->a_double[2] = k1; + s->b_double[0] = v0; + s->b_double[1] = v1; + s->b_double[2] = v2; } static void convert_dir2svf(BiquadsContext *s) @@ -612,17 +621,17 @@ static void convert_dir2svf(BiquadsContext *s) double a[2]; double b[3]; - a[0] = -s->a1; - a[1] = -s->a2; - b[0] = s->b1 - s->a1 * s->b0; - b[1] = s->b2 - s->a2 * s->b0; - b[2] = s->b0; - - s->a1 = a[0]; - s->a2 = a[1]; - s->b0 = b[0]; - s->b1 = b[1]; - s->b2 = b[2]; + a[0] = -s->a_double[1]; + a[1] = -s->a_double[2]; + b[0] = s->b_double[1] - s->a_double[1] * s->b_double[0]; + b[1] = s->b_double[2] - s->a_double[2] * s->b_double[0]; + b[2] = s->b_double[0]; + + s->a_double[1] = a[0]; + s->a_double[2] = a[1]; + s->b_double[0] = b[0]; + s->b_double[1] = b[1]; + s->b_double[2] = b[2]; } static double convert_width2qfactor(double width, @@ -669,12 +678,12 @@ static void convert_dir2zdf(BiquadsContext *s, int sample_rate) switch (s->filter_type) { case biquad: - a[0] = s->oa0; - a[1] = s->oa1; - a[2] = s->oa2; - m[0] = s->ob0; - m[1] = s->ob1; - m[2] = s->ob2; + a[0] = s->oa[0]; + a[1] = s->oa[1]; + a[2] = s->oa[2]; + m[0] = s->ob[0]; + m[1] = s->ob[1]; + m[2] = s->ob[2]; break; case equalizer: A = ff_exp10(s->gain / 40.); @@ -713,7 +722,7 @@ static void convert_dir2zdf(BiquadsContext *s, int sample_rate) case treble: case highshelf: A = ff_exp10(s->gain / 40.); - g = tan(M_PI * s->frequency / sample_rate) / sqrt(A); + g = tan(M_PI * s->frequency / sample_rate) * sqrt(A); k = 1. / Q; a[0] = 1. / (1. + g * (g + k)); a[1] = g * a[0]; @@ -776,12 +785,12 @@ static void convert_dir2zdf(BiquadsContext *s, int sample_rate) av_assert0(0); } - s->a0 = a[0]; - s->a1 = a[1]; - s->a2 = a[2]; - s->b0 = m[0]; - s->b1 = m[1]; - s->b2 = m[2]; + s->a_double[0] = a[0]; + s->a_double[1] = a[1]; + s->a_double[2] = a[2]; + s->b_double[0] = m[0]; + s->b_double[1] = m[1]; + s->b_double[2] = m[2]; } static int config_filter(AVFilterLink *outlink, int reset) @@ -831,20 +840,20 @@ static int config_filter(AVFilterLink *outlink, int reset) switch (s->filter_type) { case biquad: - s->a0 = s->oa0; - s->a1 = s->oa1; - s->a2 = s->oa2; - s->b0 = s->ob0; - s->b1 = s->ob1; - s->b2 = s->ob2; + s->a_double[0] = s->oa[0]; + s->a_double[1] = s->oa[1]; + s->a_double[2] = s->oa[2]; + s->b_double[0] = s->ob[0]; + s->b_double[1] = s->ob[1]; + s->b_double[2] = s->ob[2]; break; case equalizer: - s->a0 = 1 + alpha / A; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha / A; - s->b0 = 1 + alpha * A; - s->b1 = -2 * cos(w0); - s->b2 = 1 - alpha * A; + s->a_double[0] = 1 + alpha / A; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha / A; + s->b_double[0] = 1 + alpha * A; + s->b_double[1] = -2 * cos(w0); + s->b_double[2] = 1 - alpha * A; break; case bass: beta = sqrt((A * A + 1) - (A - 1) * (A - 1)); @@ -858,19 +867,19 @@ static int config_filter(AVFilterLink *outlink, int reset) double beta0 = ((1 + A) + (1 - A) * alpha1) * 0.5; double beta1 = ((1 - A) + (1 + A) * alpha1) * 0.5; - s->a0 = 1 + ro * alpha1; - s->a1 = -ro - alpha1; - s->a2 = 0; - s->b0 = beta0 + ro * beta1; - s->b1 = -beta1 - ro * beta0; - s->b2 = 0; + s->a_double[0] = 1 + ro * alpha1; + s->a_double[1] = -ro - alpha1; + s->a_double[2] = 0; + s->b_double[0] = beta0 + ro * beta1; + s->b_double[1] = -beta1 - ro * beta0; + s->b_double[2] = 0; } else { - s->a0 = (A + 1) + (A - 1) * cos(w0) + beta * alpha; - s->a1 = -2 * ((A - 1) + (A + 1) * cos(w0)); - s->a2 = (A + 1) + (A - 1) * cos(w0) - beta * alpha; - s->b0 = A * ((A + 1) - (A - 1) * cos(w0) + beta * alpha); - s->b1 = 2 * A * ((A - 1) - (A + 1) * cos(w0)); - s->b2 = A * ((A + 1) - (A - 1) * cos(w0) - beta * alpha); + s->a_double[0] = (A + 1) + (A - 1) * cos(w0) + beta * alpha; + s->a_double[1] = -2 * ((A - 1) + (A + 1) * cos(w0)); + s->a_double[2] = (A + 1) + (A - 1) * cos(w0) - beta * alpha; + s->b_double[0] = A * ((A + 1) - (A - 1) * cos(w0) + beta * alpha); + s->b_double[1] = 2 * A * ((A - 1) - (A + 1) * cos(w0)); + s->b_double[2] = A * ((A + 1) - (A - 1) * cos(w0) - beta * alpha); } break; case treble: @@ -884,97 +893,97 @@ static int config_filter(AVFilterLink *outlink, int reset) double beta0 = ((1 + A) + (1 - A) * alpha1) * 0.5; double beta1 = ((1 - A) + (1 + A) * alpha1) * 0.5; - s->a0 = 1 + ro * alpha1; - s->a1 = ro + alpha1; - s->a2 = 0; - s->b0 = beta0 + ro * beta1; - s->b1 = beta1 + ro * beta0; - s->b2 = 0; + s->a_double[0] = 1 + ro * alpha1; + s->a_double[1] = ro + alpha1; + s->a_double[2] = 0; + s->b_double[0] = beta0 + ro * beta1; + s->b_double[1] = beta1 + ro * beta0; + s->b_double[2] = 0; } else { - s->a0 = (A + 1) - (A - 1) * cos(w0) + beta * alpha; - s->a1 = 2 * ((A - 1) - (A + 1) * cos(w0)); - s->a2 = (A + 1) - (A - 1) * cos(w0) - beta * alpha; - s->b0 = A * ((A + 1) + (A - 1) * cos(w0) + beta * alpha); - s->b1 =-2 * A * ((A - 1) + (A + 1) * cos(w0)); - s->b2 = A * ((A + 1) + (A - 1) * cos(w0) - beta * alpha); + s->a_double[0] = (A + 1) - (A - 1) * cos(w0) + beta * alpha; + s->a_double[1] = 2 * ((A - 1) - (A + 1) * cos(w0)); + s->a_double[2] = (A + 1) - (A - 1) * cos(w0) - beta * alpha; + s->b_double[0] = A * ((A + 1) + (A - 1) * cos(w0) + beta * alpha); + s->b_double[1] =-2 * A * ((A - 1) + (A + 1) * cos(w0)); + s->b_double[2] = A * ((A + 1) + (A - 1) * cos(w0) - beta * alpha); } break; case bandpass: if (s->csg) { - s->a0 = 1 + alpha; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha; - s->b0 = sin(w0) / 2; - s->b1 = 0; - s->b2 = -sin(w0) / 2; + s->a_double[0] = 1 + alpha; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha; + s->b_double[0] = sin(w0) / 2; + s->b_double[1] = 0; + s->b_double[2] = -sin(w0) / 2; } else { - s->a0 = 1 + alpha; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha; - s->b0 = alpha; - s->b1 = 0; - s->b2 = -alpha; + s->a_double[0] = 1 + alpha; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha; + s->b_double[0] = alpha; + s->b_double[1] = 0; + s->b_double[2] = -alpha; } break; case bandreject: - s->a0 = 1 + alpha; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha; - s->b0 = 1; - s->b1 = -2 * cos(w0); - s->b2 = 1; + s->a_double[0] = 1 + alpha; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha; + s->b_double[0] = 1; + s->b_double[1] = -2 * cos(w0); + s->b_double[2] = 1; break; case lowpass: if (s->poles == 1) { - s->a0 = 1; - s->a1 = -exp(-w0); - s->a2 = 0; - s->b0 = 1 + s->a1; - s->b1 = 0; - s->b2 = 0; + s->a_double[0] = 1; + s->a_double[1] = -exp(-w0); + s->a_double[2] = 0; + s->b_double[0] = 1 + s->a_double[1]; + s->b_double[1] = 0; + s->b_double[2] = 0; } else { - s->a0 = 1 + alpha; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha; - s->b0 = (1 - cos(w0)) / 2; - s->b1 = 1 - cos(w0); - s->b2 = (1 - cos(w0)) / 2; + s->a_double[0] = 1 + alpha; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha; + s->b_double[0] = (1 - cos(w0)) / 2; + s->b_double[1] = 1 - cos(w0); + s->b_double[2] = (1 - cos(w0)) / 2; } break; case highpass: if (s->poles == 1) { - s->a0 = 1; - s->a1 = -exp(-w0); - s->a2 = 0; - s->b0 = (1 - s->a1) / 2; - s->b1 = -s->b0; - s->b2 = 0; + s->a_double[0] = 1; + s->a_double[1] = -exp(-w0); + s->a_double[2] = 0; + s->b_double[0] = (1 - s->a_double[1]) / 2; + s->b_double[1] = -s->b_double[0]; + s->b_double[2] = 0; } else { - s->a0 = 1 + alpha; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha; - s->b0 = (1 + cos(w0)) / 2; - s->b1 = -(1 + cos(w0)); - s->b2 = (1 + cos(w0)) / 2; + s->a_double[0] = 1 + alpha; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha; + s->b_double[0] = (1 + cos(w0)) / 2; + s->b_double[1] = -(1 + cos(w0)); + s->b_double[2] = (1 + cos(w0)) / 2; } break; case allpass: switch (s->order) { case 1: - s->a0 = 1.; - s->a1 = -(1. - K) / (1. + K); - s->a2 = 0.; - s->b0 = s->a1; - s->b1 = s->a0; - s->b2 = 0.; + s->a_double[0] = 1.; + s->a_double[1] = -(1. - K) / (1. + K); + s->a_double[2] = 0.; + s->b_double[0] = s->a_double[1]; + s->b_double[1] = s->a_double[0]; + s->b_double[2] = 0.; break; case 2: - s->a0 = 1 + alpha; - s->a1 = -2 * cos(w0); - s->a2 = 1 - alpha; - s->b0 = 1 - alpha; - s->b1 = -2 * cos(w0); - s->b2 = 1 + alpha; + s->a_double[0] = 1 + alpha; + s->a_double[1] = -2 * cos(w0); + s->a_double[2] = 1 - alpha; + s->b_double[0] = 1 - alpha; + s->b_double[1] = -2 * cos(w0); + s->b_double[2] = 1 + alpha; break; } break; @@ -982,40 +991,55 @@ static int config_filter(AVFilterLink *outlink, int reset) av_assert0(0); } - av_log(ctx, AV_LOG_VERBOSE, "a=%f %f %f:b=%f %f %f\n", s->a0, s->a1, s->a2, s->b0, s->b1, s->b2); + av_log(ctx, AV_LOG_VERBOSE, "a=%f %f %f:b=%f %f %f\n", + s->a_double[0], s->a_double[1], s->a_double[2], + s->b_double[0], s->b_double[1], s->b_double[2]); - s->a1 /= s->a0; - s->a2 /= s->a0; - s->b0 /= s->a0; - s->b1 /= s->a0; - s->b2 /= s->a0; - s->a0 /= s->a0; + s->a_double[1] /= s->a_double[0]; + s->a_double[2] /= s->a_double[0]; + s->b_double[0] /= s->a_double[0]; + s->b_double[1] /= s->a_double[0]; + s->b_double[2] /= s->a_double[0]; + s->a_double[0] /= s->a_double[0]; - if (s->normalize && fabs(s->b0 + s->b1 + s->b2) > 1e-6) { - double factor = (s->a0 + s->a1 + s->a2) / (s->b0 + s->b1 + s->b2); + if (s->normalize && fabs(s->b_double[0] + s->b_double[1] + s->b_double[2]) > 1e-6) { + double factor = (s->a_double[0] + s->a_double[1] + s->a_double[2]) / + (s->b_double[0] + s->b_double[1] + s->b_double[2]); - s->b0 *= factor; - s->b1 *= factor; - s->b2 *= factor; + s->b_double[0] *= factor; + s->b_double[1] *= factor; + s->b_double[2] *= factor; } switch (s->filter_type) { case tiltshelf: - s->b0 /= A; - s->b1 /= A; - s->b2 /= A; + s->b_double[0] /= A; + s->b_double[1] /= A; + s->b_double[2] /= A; break; } - s->cache = av_realloc_f(s->cache, sizeof(ChanCache), inlink->ch_layout.nb_channels); - if (!s->cache) + if (!s->cache[0]) + s->cache[0] = ff_get_audio_buffer(outlink, 4 * sizeof(double)); + if (!s->clip) + s->clip = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->clip)); + if (!s->cache[0] || !s->clip) return AVERROR(ENOMEM); - if (reset) - memset(s->cache, 0, sizeof(ChanCache) * inlink->ch_layout.nb_channels); + if (reset) { + av_samples_set_silence(s->cache[0]->extended_data, 0, s->cache[0]->nb_samples, + s->cache[0]->ch_layout.nb_channels, s->cache[0]->format); + } if (reset && s->block_samples > 0) { + if (!s->cache[1]) + s->cache[1] = ff_get_audio_buffer(outlink, 4 * sizeof(double)); + if (!s->cache[1]) + return AVERROR(ENOMEM); + av_samples_set_silence(s->cache[1]->extended_data, 0, s->cache[1]->nb_samples, + s->cache[1]->ch_layout.nb_channels, s->cache[1]->format); for (int i = 0; i < 3; i++) { - s->block[i] = ff_get_audio_buffer(outlink, s->block_samples * 2); + if (!s->block[i]) + s->block[i] = ff_get_audio_buffer(outlink, s->block_samples * 2); if (!s->block[i]) return AVERROR(ENOMEM); av_samples_set_silence(s->block[i]->extended_data, 0, s->block_samples * 2, @@ -1145,16 +1169,23 @@ static int config_filter(AVFilterLink *outlink, int reset) break; default: av_assert0(0); - } + } + + s->block_align = av_get_bytes_per_sample(inlink->format); - s->block_align = av_get_bytes_per_sample(inlink->format); + if (s->transform_type == LATT) + convert_dir2latt(s); + else if (s->transform_type == SVF) + convert_dir2svf(s); + else if (s->transform_type == ZDF) + convert_dir2zdf(s, inlink->sample_rate); - if (s->transform_type == LATT) - convert_dir2latt(s); - else if (s->transform_type == SVF) - convert_dir2svf(s); - else if (s->transform_type == ZDF) - convert_dir2zdf(s, inlink->sample_rate); + s->a_float[0] = s->a_double[0]; + s->a_float[1] = s->a_double[1]; + s->a_float[2] = s->a_double[2]; + s->b_float[0] = s->b_double[0]; + s->b_float[1] = s->b_double[1]; + s->b_float[2] = s->b_double[2]; return 0; } @@ -1227,8 +1258,7 @@ static int filter_channel(AVFilterContext *ctx, void *arg, int jobnr, int nb_job if (!s->block_samples) { s->filter(s, buf->extended_data[ch], out_buf->extended_data[ch], buf->nb_samples, - &s->cache[ch].i1, &s->cache[ch].i2, &s->cache[ch].o1, &s->cache[ch].o2, - s->b0, s->b1, s->b2, s->a0, s->a1, s->a2, &s->cache[ch].clippings, ctx->is_disabled); + s->cache[0]->extended_data[ch], s->clip+ch, ctx->is_disabled); } else if (td->eof) { memcpy(out_buf->extended_data[ch], s->block[1]->extended_data[ch] + s->block_align * s->block_samples, s->nb_samples * s->block_align); @@ -1238,25 +1268,19 @@ static int filter_channel(AVFilterContext *ctx, void *arg, int jobnr, int nb_job memset(s->block[0]->extended_data[ch] + s->block_align * (s->block_samples + buf->nb_samples), 0, (s->block_samples - buf->nb_samples) * s->block_align); s->filter(s, s->block[0]->extended_data[ch], s->block[1]->extended_data[ch], s->block_samples, - &s->cache[ch].i1, &s->cache[ch].i2, &s->cache[ch].o1, &s->cache[ch].o2, - s->b0, s->b1, s->b2, s->a0, s->a1, s->a2, &s->cache[ch].clippings, ctx->is_disabled); - s->cache[ch].ri1 = s->cache[ch].i1; - s->cache[ch].ri2 = s->cache[ch].i2; - s->cache[ch].ro1 = s->cache[ch].o1; - s->cache[ch].ro2 = s->cache[ch].o2; + s->cache[0]->extended_data[ch], s->clip+ch, ctx->is_disabled); + av_samples_copy(s->cache[1]->extended_data, s->cache[0]->extended_data, 0, 0, + s->cache[0]->nb_samples, s->cache[0]->ch_layout.nb_channels, + s->cache[0]->format); s->filter(s, s->block[0]->extended_data[ch] + s->block_samples * s->block_align, s->block[1]->extended_data[ch] + s->block_samples * s->block_align, - s->block_samples, - &s->cache[ch].ri1, &s->cache[ch].ri2, &s->cache[ch].ro1, &s->cache[ch].ro2, - s->b0, s->b1, s->b2, s->a0, s->a1, s->a2, &s->cache[ch].clippings, ctx->is_disabled); + s->block_samples, s->cache[1]->extended_data[ch], s->clip+ch, + ctx->is_disabled); reverse_samples(s->block[2], s->block[1], ch, 0, 0, 2 * s->block_samples); - s->cache[ch].ri1 = 0.; - s->cache[ch].ri2 = 0.; - s->cache[ch].ro1 = 0.; - s->cache[ch].ro2 = 0.; + av_samples_set_silence(s->cache[1]->extended_data, 0, s->cache[1]->nb_samples, + s->cache[1]->ch_layout.nb_channels, s->cache[1]->format); s->filter(s, s->block[2]->extended_data[ch], s->block[2]->extended_data[ch], 2 * s->block_samples, - &s->cache[ch].ri1, &s->cache[ch].ri2, &s->cache[ch].ro1, &s->cache[ch].ro2, - s->b0, s->b1, s->b2, s->a0, s->a1, s->a2, &s->cache[ch].clippings, ctx->is_disabled); + s->cache[1]->extended_data[ch], s->clip+ch, ctx->is_disabled); reverse_samples(s->block[1], s->block[2], ch, 0, 0, 2 * s->block_samples); memcpy(out_buf->extended_data[ch], s->block[1]->extended_data[ch], s->block_samples * s->block_align); @@ -1309,10 +1333,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf, int eof) FFMIN(outlink->ch_layout.nb_channels, ff_filter_get_nb_threads(ctx))); for (ch = 0; ch < outlink->ch_layout.nb_channels; ch++) { - if (s->cache[ch].clippings > 0) + if (s->clip[ch] > 0) av_log(ctx, AV_LOG_WARNING, "Channel %d clipping %d times. Please reduce gain.\n", - ch, s->cache[ch].clippings); - s->cache[ch].clippings = 0; + ch, s->clip[ch]); + s->clip[ch] = 0; } if (s->block_samples > 0) { @@ -1402,17 +1426,12 @@ static av_cold void uninit(AVFilterContext *ctx) for (int i = 0; i < 3; i++) av_frame_free(&s->block[i]); - av_freep(&s->cache); + av_frame_free(&s->cache[0]); + av_frame_free(&s->cache[1]); + av_freep(&s->clip); av_channel_layout_uninit(&s->ch_layout); } -static const AVFilterPad inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad outputs[] = { { .name = "default", @@ -1442,7 +1461,7 @@ const AVFilter ff_af_##name_ = { \ .init = name_##_init, \ .activate = activate, \ .uninit = uninit, \ - FILTER_INPUTS(inputs), \ + FILTER_INPUTS(ff_audio_default_filterpad), \ FILTER_OUTPUTS(outputs), \ FILTER_QUERY_FUNC(query_formats), \ .process_command = process_command, \ @@ -1458,13 +1477,13 @@ const AVFilter ff_af_##name_ = { \ {"w", "set width", OFFSET(width), AV_OPT_TYPE_DOUBLE, {.dbl=x}, 0, 99999, FLAGS} #define WIDTH_TYPE_OPTION(x) \ - {"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=x}, HERTZ, NB_WTYPE-1, FLAGS, "width_type"}, \ - {"t", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=x}, HERTZ, NB_WTYPE-1, FLAGS, "width_type"}, \ - {"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, "width_type"}, \ - {"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, "width_type"}, \ - {"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, "width_type"}, \ - {"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, "width_type"}, \ - {"k", "kHz", 0, AV_OPT_TYPE_CONST, {.i64=KHERTZ}, 0, 0, FLAGS, "width_type"} + {"width_type", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=x}, HERTZ, NB_WTYPE-1, FLAGS, .unit = "width_type"}, \ + {"t", "set filter-width type", OFFSET(width_type), AV_OPT_TYPE_INT, {.i64=x}, HERTZ, NB_WTYPE-1, FLAGS, .unit = "width_type"}, \ + {"h", "Hz", 0, AV_OPT_TYPE_CONST, {.i64=HERTZ}, 0, 0, FLAGS, .unit = "width_type"}, \ + {"q", "Q-Factor", 0, AV_OPT_TYPE_CONST, {.i64=QFACTOR}, 0, 0, FLAGS, .unit = "width_type"}, \ + {"o", "octave", 0, AV_OPT_TYPE_CONST, {.i64=OCTAVE}, 0, 0, FLAGS, .unit = "width_type"}, \ + {"s", "slope", 0, AV_OPT_TYPE_CONST, {.i64=SLOPE}, 0, 0, FLAGS, .unit = "width_type"}, \ + {"k", "kHz", 0, AV_OPT_TYPE_CONST, {.i64=KHERTZ}, 0, 0, FLAGS, .unit = "width_type"} #define MIX_CHANNELS_NORMALIZE_OPTION(x, y, z) \ {"mix", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=x}, 0, 1, FLAGS}, \ @@ -1475,24 +1494,24 @@ const AVFilter ff_af_##name_ = { \ {"n", "normalize coefficients", OFFSET(normalize), AV_OPT_TYPE_BOOL, {.i64=z}, 0, 1, FLAGS} #define TRANSFORM_OPTION(x) \ - {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=x}, 0, NB_TTYPE-1, AF, "transform_type"}, \ - {"a", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=x}, 0, NB_TTYPE-1, AF, "transform_type"}, \ - {"di", "direct form I", 0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, "transform_type"}, \ - {"dii", "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, "transform_type"}, \ - {"tdi", "transposed direct form I", 0, AV_OPT_TYPE_CONST, {.i64=TDI}, 0, 0, AF, "transform_type"}, \ - {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, "transform_type"}, \ - {"latt", "lattice-ladder form", 0, AV_OPT_TYPE_CONST, {.i64=LATT}, 0, 0, AF, "transform_type"}, \ - {"svf", "state variable filter form", 0, AV_OPT_TYPE_CONST, {.i64=SVF}, 0, 0, AF, "transform_type"}, \ - {"zdf", "zero-delay filter form", 0, AV_OPT_TYPE_CONST, {.i64=ZDF}, 0, 0, AF, "transform_type"} + {"transform", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=x}, 0, NB_TTYPE-1, AF, .unit = "transform_type"}, \ + {"a", "set transform type", OFFSET(transform_type), AV_OPT_TYPE_INT, {.i64=x}, 0, NB_TTYPE-1, AF, .unit = "transform_type"}, \ + {"di", "direct form I", 0, AV_OPT_TYPE_CONST, {.i64=DI}, 0, 0, AF, .unit = "transform_type"}, \ + {"dii", "direct form II", 0, AV_OPT_TYPE_CONST, {.i64=DII}, 0, 0, AF, .unit = "transform_type"}, \ + {"tdi", "transposed direct form I", 0, AV_OPT_TYPE_CONST, {.i64=TDI}, 0, 0, AF, .unit = "transform_type"}, \ + {"tdii", "transposed direct form II", 0, AV_OPT_TYPE_CONST, {.i64=TDII}, 0, 0, AF, .unit = "transform_type"}, \ + {"latt", "lattice-ladder form", 0, AV_OPT_TYPE_CONST, {.i64=LATT}, 0, 0, AF, .unit = "transform_type"}, \ + {"svf", "state variable filter form", 0, AV_OPT_TYPE_CONST, {.i64=SVF}, 0, 0, AF, .unit = "transform_type"}, \ + {"zdf", "zero-delay filter form", 0, AV_OPT_TYPE_CONST, {.i64=ZDF}, 0, 0, AF, .unit = "transform_type"} #define PRECISION_OPTION(x) \ - {"precision", "set filtering precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=x}, -1, 3, AF, "precision"}, \ - {"r", "set filtering precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=x}, -1, 3, AF, "precision"}, \ - {"auto", "automatic", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, AF, "precision"}, \ - {"s16", "signed 16-bit", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, "precision"}, \ - {"s32", "signed 32-bit", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, "precision"}, \ - {"f32", "floating-point single", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, "precision"}, \ - {"f64", "floating-point double", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, AF, "precision"} + {"precision", "set filtering precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=x}, -1, 3, AF, .unit = "precision"}, \ + {"r", "set filtering precision", OFFSET(precision), AV_OPT_TYPE_INT, {.i64=x}, -1, 3, AF, .unit = "precision"}, \ + {"auto", "automatic", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, AF, .unit = "precision"}, \ + {"s16", "signed 16-bit", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, AF, .unit = "precision"}, \ + {"s32", "signed 32-bit", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, AF, .unit = "precision"}, \ + {"f32", "floating-point single", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, AF, .unit = "precision"}, \ + {"f64", "floating-point double", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, AF, .unit = "precision"} #define BLOCKSIZE_OPTION(x) \ {"blocksize", "set the block size", OFFSET(block_samples), AV_OPT_TYPE_INT, {.i64=x}, 0, 32768, AF}, \ @@ -1657,12 +1676,12 @@ DEFINE_BIQUAD_FILTER(allpass, "Apply a two-pole all-pass filter."); #endif /* CONFIG_ALLPASS_FILTER */ #if CONFIG_BIQUAD_FILTER static const AVOption biquad_options[] = { - {"a0", NULL, OFFSET(oa0), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT32_MIN, INT32_MAX, FLAGS}, - {"a1", NULL, OFFSET(oa1), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, - {"a2", NULL, OFFSET(oa2), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, - {"b0", NULL, OFFSET(ob0), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, - {"b1", NULL, OFFSET(ob1), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, - {"b2", NULL, OFFSET(ob2), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, + {"a0", NULL, OFFSET(oa[0]), AV_OPT_TYPE_DOUBLE, {.dbl=1}, INT32_MIN, INT32_MAX, FLAGS}, + {"a1", NULL, OFFSET(oa[1]), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, + {"a2", NULL, OFFSET(oa[2]), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, + {"b0", NULL, OFFSET(ob[0]), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, + {"b1", NULL, OFFSET(ob[1]), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, + {"b2", NULL, OFFSET(ob[2]), AV_OPT_TYPE_DOUBLE, {.dbl=0}, INT32_MIN, INT32_MAX, FLAGS}, MIX_CHANNELS_NORMALIZE_OPTION(1, "all", 0), TRANSFORM_OPTION(DI), PRECISION_OPTION(-1), diff --git a/libavfilter/af_bs2b.c b/libavfilter/af_bs2b.c index 20e6d8141a5..ebd50d4d779 100644 --- a/libavfilter/af_bs2b.c +++ b/libavfilter/af_bs2b.c @@ -51,10 +51,10 @@ typedef struct Bs2bContext { static const AVOption bs2b_options[] = { { "profile", "Apply a pre-defined crossfeed level", - OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = BS2B_DEFAULT_CLEVEL }, 0, INT_MAX, A, "profile" }, - { "default", "default profile", 0, AV_OPT_TYPE_CONST, { .i64 = BS2B_DEFAULT_CLEVEL }, 0, 0, A, "profile" }, - { "cmoy", "Chu Moy circuit", 0, AV_OPT_TYPE_CONST, { .i64 = BS2B_CMOY_CLEVEL }, 0, 0, A, "profile" }, - { "jmeier", "Jan Meier circuit", 0, AV_OPT_TYPE_CONST, { .i64 = BS2B_JMEIER_CLEVEL }, 0, 0, A, "profile" }, + OFFSET(profile), AV_OPT_TYPE_INT, { .i64 = BS2B_DEFAULT_CLEVEL }, 0, INT_MAX, A, .unit = "profile" }, + { "default", "default profile", 0, AV_OPT_TYPE_CONST, { .i64 = BS2B_DEFAULT_CLEVEL }, 0, 0, A, .unit = "profile" }, + { "cmoy", "Chu Moy circuit", 0, AV_OPT_TYPE_CONST, { .i64 = BS2B_CMOY_CLEVEL }, 0, 0, A, .unit = "profile" }, + { "jmeier", "Jan Meier circuit", 0, AV_OPT_TYPE_CONST, { .i64 = BS2B_JMEIER_CLEVEL }, 0, 0, A, .unit = "profile" }, { "fcut", "Set cut frequency (in Hz)", OFFSET(fcut), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, BS2B_MAXFCUT, A }, { "feed", "Set feed level (in Hz)", diff --git a/libavfilter/af_channelmap.c b/libavfilter/af_channelmap.c index eb173d20c4c..64efacb5763 100644 --- a/libavfilter/af_channelmap.c +++ b/libavfilter/af_channelmap.c @@ -58,7 +58,6 @@ enum MappingMode { typedef struct ChannelMapContext { const AVClass *class; char *mapping_str; - char *channel_layout_str; AVChannelLayout output_layout; struct ChannelMap map[MAX_CH]; int nch; @@ -72,7 +71,7 @@ static const AVOption channelmap_options[] = { { "map", "A comma-separated list of input channel numbers in output order.", OFFSET(mapping_str), AV_OPT_TYPE_STRING, .flags = A|F }, { "channel_layout", "Output channel layout.", - OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, .flags = A|F }, + OFFSET(output_layout), AV_OPT_TYPE_CHLAYOUT, .flags = A|F }, { NULL } }; @@ -85,7 +84,7 @@ static char* split(char *message, char delim) { return next; } -static int get_channel_idx(char **map, int *ch, char delim, int max_ch) +static int get_channel_idx(char **map, int *ch, char delim, int max_nb_channels) { char *next; int len; @@ -99,7 +98,7 @@ static int get_channel_idx(char **map, int *ch, char delim, int max_ch) sscanf(*map, "%d%n", ch, &n); if (n != len) return AVERROR(EINVAL); - if (*ch < 0 || *ch > max_ch) + if (*ch < 0 || *ch >= max_nb_channels) return AVERROR(EINVAL); *map = next; return 0; @@ -117,14 +116,38 @@ static int get_channel(char **map, int *ch, char delim) return 0; } +static int check_idx_and_id(AVFilterContext *ctx, int channel_idx, int channel, AVChannelLayout *ch_layout, const char *io) +{ + char channel_name[64]; + char layout_name[256]; + int nb_channels = ch_layout->nb_channels; + + if (channel_idx < 0 || channel_idx >= nb_channels) { + av_channel_layout_describe(ch_layout, layout_name, sizeof(layout_name)); + if (channel >= 0) { + av_channel_name(channel_name, sizeof(channel_name), channel); + av_log(ctx, AV_LOG_ERROR, + "%sput channel '%s' not available from %sput layout '%s'\n", + io, channel_name, io, layout_name); + } else { + av_log(ctx, AV_LOG_ERROR, + "%sput channel #%d not available from %sput layout '%s'\n", + io, channel_idx, io, layout_name); + } + return AVERROR(EINVAL); + } + + return 0; +} + static av_cold int channelmap_init(AVFilterContext *ctx) { ChannelMapContext *s = ctx->priv; char *mapping, separator = '|'; int map_entries = 0; - char buf[256]; enum MappingMode mode; - uint64_t out_ch_mask = 0; + int64_t out_ch_mask = 0; + uint64_t presence_mask; int i; mapping = s->mapping_str; @@ -165,9 +188,16 @@ static av_cold int channelmap_init(AVFilterContext *ctx) return AVERROR(EINVAL); } + for (i = 0; i < MAX_CH; i++) { + s->map[i].in_channel_idx = -1; + s->map[i].out_channel_idx = -1; + s->map[i].in_channel = -1; + s->map[i].out_channel = -1; + } + for (i = 0; i < map_entries; i++) { int in_ch_idx = -1, out_ch_idx = -1; - int in_ch = 0, out_ch = 0; + int in_ch = -1, out_ch = -1; static const char err[] = "Failed to parse channel map\n"; switch (mode) { case MAP_ONE_INT: @@ -197,14 +227,16 @@ static av_cold int channelmap_init(AVFilterContext *ctx) break; case MAP_PAIR_INT_STR: if (get_channel_idx(&mapping, &in_ch_idx, '-', MAX_CH) < 0 || - get_channel(&mapping, &out_ch, separator) < 0 || - (1ULL << out_ch) & out_ch_mask) { + get_channel(&mapping, &out_ch, separator) < 0) { av_log(ctx, AV_LOG_ERROR, err); return AVERROR(EINVAL); } s->map[i].in_channel_idx = in_ch_idx; s->map[i].out_channel = out_ch; - out_ch_mask |= 1ULL << out_ch; + if (out_ch < 63) + out_ch_mask |= 1ULL << out_ch; + else + out_ch_mask = -1; break; case MAP_PAIR_STR_INT: if (get_channel(&mapping, &in_ch, '-') < 0 || @@ -217,65 +249,44 @@ static av_cold int channelmap_init(AVFilterContext *ctx) break; case MAP_PAIR_STR_STR: if (get_channel(&mapping, &in_ch, '-') < 0 || - get_channel(&mapping, &out_ch, separator) < 0 || - (1ULL << out_ch) & out_ch_mask) { + get_channel(&mapping, &out_ch, separator) < 0) { av_log(ctx, AV_LOG_ERROR, err); return AVERROR(EINVAL); } s->map[i].in_channel = in_ch; s->map[i].out_channel = out_ch; - out_ch_mask |= 1ULL << out_ch; + if (out_ch < 63) + out_ch_mask |= 1ULL << out_ch; + else + out_ch_mask = -1; break; } } s->mode = mode; s->nch = map_entries; - if (out_ch_mask) - av_channel_layout_from_mask(&s->output_layout, out_ch_mask); - else - av_channel_layout_default(&s->output_layout, map_entries); - - if (s->channel_layout_str) { - AVChannelLayout fmt = { 0 }; - int ret; - if ((ret = av_channel_layout_from_string(&fmt, s->channel_layout_str)) < 0) { -#if FF_API_OLD_CHANNEL_LAYOUT - uint64_t mask; -FF_DISABLE_DEPRECATION_WARNINGS - if ((mask = av_get_channel_layout(s->channel_layout_str)) == 0) { -#endif - av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout: '%s'.\n", - s->channel_layout_str); - return AVERROR(EINVAL); -#if FF_API_OLD_CHANNEL_LAYOUT - } -FF_ENABLE_DEPRECATION_WARNINGS - av_log(ctx, AV_LOG_WARNING, "Channel layout '%s' uses a deprecated syntax.\n", - s->channel_layout_str); - av_channel_layout_from_mask(&fmt, mask); -#endif - } - if (mode == MAP_NONE) { - int i; - s->nch = fmt.nb_channels; - for (i = 0; i < s->nch; i++) { - s->map[i].in_channel_idx = i; - s->map[i].out_channel_idx = i; - } - } else if (out_ch_mask && av_channel_layout_compare(&s->output_layout, &fmt)) { - av_channel_layout_describe(&s->output_layout, buf, sizeof(buf)); - av_log(ctx, AV_LOG_ERROR, - "Output channel layout '%s' does not match the list of channel mapped: '%s'.\n", - s->channel_layout_str, buf); - return AVERROR(EINVAL); - } else if (s->nch != fmt.nb_channels) { - av_log(ctx, AV_LOG_ERROR, - "Output channel layout %s does not match the number of channels mapped %d.\n", - s->channel_layout_str, s->nch); - return AVERROR(EINVAL); + if (s->output_layout.nb_channels == 0) { + if (out_ch_mask > 0) + av_channel_layout_from_mask(&s->output_layout, out_ch_mask); + else if (map_entries) + av_channel_layout_default(&s->output_layout, map_entries); + } + + if (mode == MAP_NONE) { + int i; + s->nch = s->output_layout.nb_channels; + for (i = 0; i < s->nch; i++) { + s->map[i].in_channel_idx = i; + s->map[i].out_channel_idx = i; } - s->output_layout = fmt; + } else if (s->nch != s->output_layout.nb_channels) { + char buf[256]; + av_channel_layout_describe(&s->output_layout, buf, sizeof(buf)); + av_log(ctx, AV_LOG_ERROR, + "Output channel layout %s does not match the number of channels mapped %d.\n", + buf, s->nch); + return AVERROR(EINVAL); } + if (!s->output_layout.nb_channels) { av_log(ctx, AV_LOG_ERROR, "Output channel layout is not set and " "cannot be guessed from the maps.\n"); @@ -289,6 +300,23 @@ FF_ENABLE_DEPRECATION_WARNINGS } } + presence_mask = 0; + for (i = 0; i < s->nch; i++) { + uint64_t idx_mask; + int ret = check_idx_and_id(ctx, s->map[i].out_channel_idx, s->map[i].out_channel, &s->output_layout, "out"); + if (ret < 0) + return ret; + idx_mask = (1ULL << s->map[i].out_channel_idx); + if (presence_mask & idx_mask) { + char layout_name[256]; + av_channel_layout_describe(&s->output_layout, layout_name, sizeof(layout_name)); + av_log(ctx, AV_LOG_ERROR, "Mapping %d assigns channel #%d twice in output layout '%s'.\n", + i + 1, s->map[i].out_channel_idx, layout_name); + return AVERROR(EINVAL); + } + presence_mask |= idx_mask; + } + return 0; } @@ -351,12 +379,6 @@ static int channelmap_filter_frame(AVFilterLink *inlink, AVFrame *buf) memcpy(buf->data, buf->extended_data, FFMIN(FF_ARRAY_ELEMS(buf->data), nch_out) * sizeof(buf->data[0])); -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - buf->channels = outlink->ch_layout.nb_channels; - buf->channel_layout = outlink->channel_layout; -FF_ENABLE_DEPRECATION_WARNINGS -#endif if ((ret = av_channel_layout_copy(&buf->ch_layout, &outlink->ch_layout)) < 0) return ret; @@ -367,33 +389,18 @@ static int channelmap_config_input(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; ChannelMapContext *s = ctx->priv; - int nb_channels = inlink->ch_layout.nb_channels; int i, err = 0; - char channel_name[64]; - char layout_name[256]; for (i = 0; i < s->nch; i++) { struct ChannelMap *m = &s->map[i]; - if (s->mode == MAP_PAIR_STR_INT || s->mode == MAP_PAIR_STR_STR) { + if (s->mode == MAP_PAIR_STR_INT || s->mode == MAP_PAIR_STR_STR || s->mode == MAP_ONE_STR) { m->in_channel_idx = av_channel_layout_index_from_channel( &inlink->ch_layout, m->in_channel); } - if (m->in_channel_idx < 0 || m->in_channel_idx >= nb_channels) { - av_channel_layout_describe(&inlink->ch_layout, layout_name, sizeof(layout_name)); - if (m->in_channel) { - av_channel_name(channel_name, sizeof(channel_name), m->in_channel); - av_log(ctx, AV_LOG_ERROR, - "input channel '%s' not available from input layout '%s'\n", - channel_name, layout_name); - } else { - av_log(ctx, AV_LOG_ERROR, - "input channel #%d not available from input layout '%s'\n", - m->in_channel_idx, layout_name); - } + if (check_idx_and_id(ctx, m->in_channel_idx, m->in_channel, &inlink->ch_layout, "in") < 0) err = AVERROR(EINVAL); - } } return err; @@ -409,13 +416,6 @@ static const AVFilterPad avfilter_af_channelmap_inputs[] = { }, }; -static const AVFilterPad avfilter_af_channelmap_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO - }, -}; - const AVFilter ff_af_channelmap = { .name = "channelmap", .description = NULL_IF_CONFIG_SMALL("Remap audio channels."), @@ -423,6 +423,6 @@ const AVFilter ff_af_channelmap = { .priv_size = sizeof(ChannelMapContext), .priv_class = &channelmap_class, FILTER_INPUTS(avfilter_af_channelmap_inputs), - FILTER_OUTPUTS(avfilter_af_channelmap_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(channelmap_query_formats), }; diff --git a/libavfilter/af_channelsplit.c b/libavfilter/af_channelsplit.c index b537e1380f1..92e605e1bf4 100644 --- a/libavfilter/af_channelsplit.c +++ b/libavfilter/af_channelsplit.c @@ -22,7 +22,7 @@ * * Split an audio stream into per-channel streams. */ - +#include "libavutil/avassert.h" #include "libavutil/attributes.h" #include "libavutil/channel_layout.h" #include "libavutil/internal.h" @@ -40,7 +40,6 @@ typedef struct ChannelSplitContext { const AVClass *class; AVChannelLayout channel_layout; - char *channel_layout_str; char *channels_str; int map[64]; @@ -50,7 +49,7 @@ typedef struct ChannelSplitContext { #define A AV_OPT_FLAG_AUDIO_PARAM #define F AV_OPT_FLAG_FILTERING_PARAM static const AVOption channelsplit_options[] = { - { "channel_layout", "Input channel layout.", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, { .str = "stereo" }, .flags = A|F }, + { "channel_layout", "Input channel layout.", OFFSET(channel_layout), AV_OPT_TYPE_CHLAYOUT, { .str = "stereo" }, .flags = A|F }, { "channels", "Channels to extract.", OFFSET(channels_str), AV_OPT_TYPE_STRING, { .str = "all" }, .flags = A|F }, { NULL } }; @@ -63,13 +62,6 @@ static av_cold int init(AVFilterContext *ctx) AVChannelLayout channel_layout = { 0 }; int all = 0, ret = 0, i; - if ((ret = av_channel_layout_from_string(&s->channel_layout, s->channel_layout_str)) < 0) { - av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout '%s'.\n", - s->channel_layout_str); - ret = AVERROR(EINVAL); - goto fail; - } - if (!strcmp(s->channels_str, "all")) { if ((ret = av_channel_layout_copy(&channel_layout, &s->channel_layout)) < 0) goto fail; @@ -100,9 +92,11 @@ static av_cold int init(AVFilterContext *ctx) if (all) { s->map[i] = i; } else { + char buf[128]; + av_channel_layout_describe(&s->channel_layout, buf, sizeof(buf)); if ((ret = av_channel_layout_index_from_channel(&s->channel_layout, channel)) < 0) { av_log(ctx, AV_LOG_ERROR, "Channel name '%s' not present in channel layout '%s'.\n", - pad.name, s->channel_layout_str); + pad.name, buf); av_freep(&pad.name); goto fail; } @@ -162,6 +156,8 @@ static int filter_frame(AVFilterLink *outlink, AVFrame *buf) enum AVChannel channel = av_channel_layout_channel_from_index(&buf->ch_layout, s->map[i]); int ret; + av_assert1(channel >= 0); + AVFrame *buf_out = av_frame_clone(buf); if (!buf_out) return AVERROR(ENOMEM); @@ -170,13 +166,6 @@ static int filter_frame(AVFilterLink *outlink, AVFrame *buf) ret = av_channel_layout_from_mask(&buf_out->ch_layout, 1ULL << channel); if (ret < 0) return ret; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - buf_out->channel_layout = - av_channel_layout_extract_channel(buf->channel_layout, s->map[i]); - buf_out->channels = 1; -FF_ENABLE_DEPRECATION_WARNINGS -#endif return ff_filter_frame(ctx->outputs[i], buf_out); } @@ -232,13 +221,6 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad avfilter_af_channelsplit_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_channelsplit = { .name = "channelsplit", .description = NULL_IF_CONFIG_SMALL("Split audio into per-channel streams."), @@ -247,7 +229,7 @@ const AVFilter ff_af_channelsplit = { .init = init, .activate = activate, .uninit = uninit, - FILTER_INPUTS(avfilter_af_channelsplit_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), .outputs = NULL, FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS, diff --git a/libavfilter/af_compensationdelay.c b/libavfilter/af_compensationdelay.c index 754e02e3ee6..924ccefe94b 100644 --- a/libavfilter/af_compensationdelay.c +++ b/libavfilter/af_compensationdelay.c @@ -166,13 +166,6 @@ static const AVFilterPad compensationdelay_inputs[] = { }, }; -static const AVFilterPad compensationdelay_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_compensationdelay = { .name = "compensationdelay", .description = NULL_IF_CONFIG_SMALL("Audio Compensation Delay Line."), @@ -180,7 +173,7 @@ const AVFilter ff_af_compensationdelay = { .priv_class = &compensationdelay_class, .uninit = uninit, FILTER_INPUTS(compensationdelay_inputs), - FILTER_OUTPUTS(compensationdelay_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/af_crossfeed.c b/libavfilter/af_crossfeed.c index 9a518e52586..ee6540a9fce 100644 --- a/libavfilter/af_crossfeed.c +++ b/libavfilter/af_crossfeed.c @@ -362,13 +362,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_crossfeed = { .name = "crossfeed", .description = NULL_IF_CONFIG_SMALL("Apply headphone crossfeed filter."), @@ -377,7 +370,7 @@ const AVFilter ff_af_crossfeed = { .activate = activate, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = process_command, diff --git a/libavfilter/af_crystalizer.c b/libavfilter/af_crystalizer.c index 449f24a082b..01cdf8bd630 100644 --- a/libavfilter/af_crystalizer.c +++ b/libavfilter/af_crystalizer.c @@ -22,7 +22,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" typedef struct CrystalizerContext { const AVClass *class; @@ -233,13 +232,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_crystalizer = { .name = "crystalizer", .description = NULL_IF_CONFIG_SMALL("Simple audio noise sharpening filter."), @@ -247,7 +239,7 @@ const AVFilter ff_af_crystalizer = { .priv_class = &crystalizer_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP), .process_command = ff_filter_process_command, diff --git a/libavfilter/af_dcshift.c b/libavfilter/af_dcshift.c index 480f01f4a3d..3e9ba7e5284 100644 --- a/libavfilter/af_dcshift.c +++ b/libavfilter/af_dcshift.c @@ -122,13 +122,6 @@ static const AVFilterPad dcshift_inputs[] = { }, }; -static const AVFilterPad dcshift_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_dcshift = { .name = "dcshift", .description = NULL_IF_CONFIG_SMALL("Apply a DC shift to the audio."), @@ -136,7 +129,7 @@ const AVFilter ff_af_dcshift = { .priv_class = &dcshift_class, .init = init, FILTER_INPUTS(dcshift_inputs), - FILTER_OUTPUTS(dcshift_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_S32P), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/af_deesser.c b/libavfilter/af_deesser.c index cdef8371554..16c10d21bcb 100644 --- a/libavfilter/af_deesser.c +++ b/libavfilter/af_deesser.c @@ -24,7 +24,6 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" typedef struct DeesserChannel { double s1, s2, s3; @@ -59,10 +58,10 @@ static const AVOption deesser_options[] = { { "i", "set intensity", OFFSET(intensity), AV_OPT_TYPE_DOUBLE, {.dbl=0.0}, 0.0, 1.0, A }, { "m", "set max deessing", OFFSET(max), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0.0, 1.0, A }, { "f", "set frequency", OFFSET(frequency), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0.0, 1.0, A }, - { "s", "set output mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=OUT_MODE}, 0, NB_MODES-1, A, "mode" }, - { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64=IN_MODE}, 0, 0, A, "mode" }, - { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64=OUT_MODE}, 0, 0, A, "mode" }, - { "e", "ess", 0, AV_OPT_TYPE_CONST, {.i64=ESS_MODE}, 0, 0, A, "mode" }, + { "s", "set output mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=OUT_MODE}, 0, NB_MODES-1, A, .unit = "mode" }, + { "i", "input", 0, AV_OPT_TYPE_CONST, {.i64=IN_MODE}, 0, 0, A, .unit = "mode" }, + { "o", "output", 0, AV_OPT_TYPE_CONST, {.i64=OUT_MODE}, 0, 0, A, .unit = "mode" }, + { "e", "ess", 0, AV_OPT_TYPE_CONST, {.i64=ESS_MODE}, 0, 0, A, .unit = "mode" }, { NULL } }; @@ -193,13 +192,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_deesser = { .name = "deesser", .description = NULL_IF_CONFIG_SMALL("Apply de-essing to the audio."), @@ -207,7 +199,7 @@ const AVFilter ff_af_deesser = { .priv_class = &deesser_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, }; diff --git a/libavfilter/af_dialoguenhance.c b/libavfilter/af_dialoguenhance.c index b613733fafa..03bfccb0763 100644 --- a/libavfilter/af_dialoguenhance.c +++ b/libavfilter/af_dialoguenhance.c @@ -24,8 +24,8 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" -#include "window_func.h" #include @@ -37,8 +37,11 @@ typedef struct AudioDialogueEnhancementContext { int fft_size; int overlap; - float *window; - float prev_vad; + void *window; + float *window_float; + double *window_double; + float prev_vad_float; + double prev_vad_double; AVFrame *in; AVFrame *in_frame; @@ -48,6 +51,8 @@ typedef struct AudioDialogueEnhancementContext { AVFrame *windowed_prev; AVFrame *center_frame; + int (*de_stereo)(AVFilterContext *ctx, AVFrame *out); + AVTXContext *tx_ctx[2], *itx_ctx; av_tx_fn tx_fn, itx_fn; } AudioDialogueEnhanceContext; @@ -71,6 +76,7 @@ static int query_formats(AVFilterContext *ctx) int ret; if ((ret = ff_add_format (&formats, AV_SAMPLE_FMT_FLTP )) < 0 || + (ret = ff_add_format (&formats, AV_SAMPLE_FMT_DBLP )) < 0 || (ret = ff_set_common_formats (ctx , formats )) < 0 || (ret = ff_add_channel_layout (&in_layout , &(AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO)) < 0 || (ret = ff_channel_layouts_ref(in_layout, &ctx->inputs[0]->outcfg.channel_layouts)) < 0 || @@ -81,226 +87,44 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_all_samplerates(ctx); } +#define DEPTH 32 +#include "dialoguenhance_template.c" + +#undef DEPTH +#define DEPTH 64 +#include "dialoguenhance_template.c" + static int config_input(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; AudioDialogueEnhanceContext *s = ctx->priv; - float scale = 1.f, iscale, overlap; int ret; s->fft_size = inlink->sample_rate > 100000 ? 8192 : inlink->sample_rate > 50000 ? 4096 : 2048; s->overlap = s->fft_size / 4; - s->window = av_calloc(s->fft_size, sizeof(*s->window)); - if (!s->window) - return AVERROR(ENOMEM); - - s->in_frame = ff_get_audio_buffer(inlink, s->fft_size * 4); - s->center_frame = ff_get_audio_buffer(inlink, s->fft_size * 4); - s->out_dist_frame = ff_get_audio_buffer(inlink, s->fft_size * 4); - s->windowed_frame = ff_get_audio_buffer(inlink, s->fft_size * 4); - s->windowed_out = ff_get_audio_buffer(inlink, s->fft_size * 4); - s->windowed_prev = ff_get_audio_buffer(inlink, s->fft_size * 4); + s->in_frame = ff_get_audio_buffer(inlink, (s->fft_size + 2) * 2); + s->center_frame = ff_get_audio_buffer(inlink, (s->fft_size + 2) * 2); + s->out_dist_frame = ff_get_audio_buffer(inlink, (s->fft_size + 2) * 2); + s->windowed_frame = ff_get_audio_buffer(inlink, (s->fft_size + 2) * 2); + s->windowed_out = ff_get_audio_buffer(inlink, (s->fft_size + 2) * 2); + s->windowed_prev = ff_get_audio_buffer(inlink, (s->fft_size + 2) * 2); if (!s->in_frame || !s->windowed_out || !s->windowed_prev || !s->out_dist_frame || !s->windowed_frame || !s->center_frame) return AVERROR(ENOMEM); - generate_window_func(s->window, s->fft_size, WFUNC_SINE, &overlap); - - iscale = 1.f / s->fft_size; - - ret = av_tx_init(&s->tx_ctx[0], &s->tx_fn, AV_TX_FLOAT_RDFT, 0, s->fft_size, &scale, 0); - if (ret < 0) - return ret; - - ret = av_tx_init(&s->tx_ctx[1], &s->tx_fn, AV_TX_FLOAT_RDFT, 0, s->fft_size, &scale, 0); - if (ret < 0) - return ret; - - ret = av_tx_init(&s->itx_ctx, &s->itx_fn, AV_TX_FLOAT_RDFT, 1, s->fft_size, &iscale, 0); - if (ret < 0) - return ret; - - return 0; -} - -static void apply_window(AudioDialogueEnhanceContext *s, - const float *in_frame, float *out_frame, const int add_to_out_frame) -{ - const float *window = s->window; - - if (add_to_out_frame) { - for (int i = 0; i < s->fft_size; i++) - out_frame[i] += in_frame[i] * window[i]; - } else { - for (int i = 0; i < s->fft_size; i++) - out_frame[i] = in_frame[i] * window[i]; - } -} - -static float sqrf(float x) -{ - return x * x; -} - -static void get_centere(AVComplexFloat *left, AVComplexFloat *right, - AVComplexFloat *center, int N) -{ - for (int i = 0; i < N; i++) { - const float l_re = left[i].re; - const float l_im = left[i].im; - const float r_re = right[i].re; - const float r_im = right[i].im; - const float a = 0.5f * (1.f - sqrtf((sqrf(l_re - r_re) + sqrf(l_im - r_im))/ - (sqrf(l_re + r_re) + sqrf(l_im + r_im) + FLT_EPSILON))); - - center[i].re = a * (l_re + r_re); - center[i].im = a * (l_im + r_im); - } -} - -static float flux(float *curf, float *prevf, int N) -{ - AVComplexFloat *cur = (AVComplexFloat *)curf; - AVComplexFloat *prev = (AVComplexFloat *)prevf; - float sum = 0.f; - - for (int i = 0; i < N; i++) { - float c_re = cur[i].re; - float c_im = cur[i].im; - float p_re = prev[i].re; - float p_im = prev[i].im; - - sum += sqrf(hypotf(c_re, c_im) - hypotf(p_re, p_im)); - } - - return sum; -} - -static float fluxlr(float *lf, float *lpf, - float *rf, float *rpf, - int N) -{ - AVComplexFloat *l = (AVComplexFloat *)lf; - AVComplexFloat *lp = (AVComplexFloat *)lpf; - AVComplexFloat *r = (AVComplexFloat *)rf; - AVComplexFloat *rp = (AVComplexFloat *)rpf; - float sum = 0.f; - - for (int i = 0; i < N; i++) { - float c_re = l[i].re - r[i].re; - float c_im = l[i].im - r[i].im; - float p_re = lp[i].re - rp[i].re; - float p_im = lp[i].im - rp[i].im; - - sum += sqrf(hypotf(c_re, c_im) - hypotf(p_re, p_im)); - } - - return sum; -} - -static float calc_vad(float fc, float flr, float a) -{ - const float vad = a * (fc / (fc + flr) - 0.5f); - - return av_clipf(vad, 0.f, 1.f); -} - -static void get_final(float *c, float *l, - float *r, float vad, int N, - float original, float enhance) -{ - AVComplexFloat *center = (AVComplexFloat *)c; - AVComplexFloat *left = (AVComplexFloat *)l; - AVComplexFloat *right = (AVComplexFloat *)r; - - for (int i = 0; i < N; i++) { - float cP = sqrf(center[i].re) + sqrf(center[i].im); - float lrP = sqrf(left[i].re - right[i].re) + sqrf(left[i].im - right[i].im); - float G = cP / (cP + lrP + FLT_EPSILON); - float re, im; - - re = center[i].re * (original + vad * G * enhance); - im = center[i].im * (original + vad * G * enhance); - - center[i].re = re; - center[i].im = im; + switch (inlink->format) { + case AV_SAMPLE_FMT_FLTP: + s->de_stereo = de_stereo_float; + ret = de_tx_init_float(ctx); + break; + case AV_SAMPLE_FMT_DBLP: + s->de_stereo = de_stereo_double; + ret = de_tx_init_double(ctx); + break; } -} -static int de_stereo(AVFilterContext *ctx, AVFrame *out) -{ - AudioDialogueEnhanceContext *s = ctx->priv; - float *center = (float *)s->center_frame->extended_data[0]; - float *center_prev = (float *)s->center_frame->extended_data[1]; - float *left_in = (float *)s->in_frame->extended_data[0]; - float *right_in = (float *)s->in_frame->extended_data[1]; - float *left_out = (float *)s->out_dist_frame->extended_data[0]; - float *right_out = (float *)s->out_dist_frame->extended_data[1]; - float *left_samples = (float *)s->in->extended_data[0]; - float *right_samples = (float *)s->in->extended_data[1]; - float *windowed_left = (float *)s->windowed_frame->extended_data[0]; - float *windowed_right = (float *)s->windowed_frame->extended_data[1]; - float *windowed_oleft = (float *)s->windowed_out->extended_data[0]; - float *windowed_oright = (float *)s->windowed_out->extended_data[1]; - float *windowed_pleft = (float *)s->windowed_prev->extended_data[0]; - float *windowed_pright = (float *)s->windowed_prev->extended_data[1]; - float *left_osamples = (float *)out->extended_data[0]; - float *right_osamples = (float *)out->extended_data[1]; - float *center_osamples = (float *)out->extended_data[2]; - const int offset = s->fft_size - s->overlap; - float vad; - - // shift in/out buffers - memmove(left_in, &left_in[s->overlap], offset * sizeof(float)); - memmove(right_in, &right_in[s->overlap], offset * sizeof(float)); - memmove(left_out, &left_out[s->overlap], offset * sizeof(float)); - memmove(right_out, &right_out[s->overlap], offset * sizeof(float)); - - memcpy(&left_in[offset], left_samples, s->overlap * sizeof(float)); - memcpy(&right_in[offset], right_samples, s->overlap * sizeof(float)); - memset(&left_out[offset], 0, s->overlap * sizeof(float)); - memset(&right_out[offset], 0, s->overlap * sizeof(float)); - - apply_window(s, left_in, windowed_left, 0); - apply_window(s, right_in, windowed_right, 0); - - s->tx_fn(s->tx_ctx[0], windowed_oleft, windowed_left, sizeof(float)); - s->tx_fn(s->tx_ctx[1], windowed_oright, windowed_right, sizeof(float)); - - get_centere((AVComplexFloat *)windowed_oleft, - (AVComplexFloat *)windowed_oright, - (AVComplexFloat *)center, - s->fft_size / 2 + 1); - - vad = calc_vad(flux(center, center_prev, s->fft_size / 2 + 1), - fluxlr(windowed_oleft, windowed_pleft, - windowed_oright, windowed_pright, s->fft_size / 2 + 1), s->voice); - vad = vad * 0.1 + 0.9 * s->prev_vad; - s->prev_vad = vad; - - memcpy(center_prev, center, s->fft_size * sizeof(float)); - memcpy(windowed_pleft, windowed_oleft, s->fft_size * sizeof(float)); - memcpy(windowed_pright, windowed_oright, s->fft_size * sizeof(float)); - - get_final(center, windowed_oleft, windowed_oright, vad, s->fft_size / 2 + 1, - s->original, s->enhance); - - s->itx_fn(s->itx_ctx, windowed_oleft, center, sizeof(AVComplexFloat)); - - apply_window(s, windowed_oleft, left_out, 1); - - for (int i = 0; i < s->overlap; i++) { - // 4 times overlap with squared hanning window results in 1.5 time increase in amplitude - if (!ctx->is_disabled) - center_osamples[i] = left_out[i] / 1.5f; - else - center_osamples[i] = 0.f; - left_osamples[i] = left_in[i]; - right_osamples[i] = right_in[i]; - } - - return 0; + return ret; } static int filter_frame(AVFilterLink *inlink, AVFrame *in) @@ -318,7 +142,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) } s->in = in; - de_stereo(ctx, out); + s->de_stereo(ctx, out); av_frame_copy_props(out, in); out->nb_samples = in->nb_samples; @@ -385,13 +209,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_dialoguenhance = { .name = "dialoguenhance", .description = NULL_IF_CONFIG_SMALL("Audio Dialogue Enhancement."), @@ -399,7 +216,7 @@ const AVFilter ff_af_dialoguenhance = { .priv_class = &dialoguenhance_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .activate = activate, diff --git a/libavfilter/af_drmeter.c b/libavfilter/af_drmeter.c index 3e80691339c..5eea865575e 100644 --- a/libavfilter/af_drmeter.c +++ b/libavfilter/af_drmeter.c @@ -41,7 +41,7 @@ typedef struct DRMeterContext { const AVClass *class; ChannelStats *chstats; int nb_channels; - uint64_t tc_samples; + int64_t tc_samples; double time_constant; } DRMeterContext; @@ -59,11 +59,11 @@ static int config_output(AVFilterLink *outlink) { DRMeterContext *s = outlink->src->priv; - s->chstats = av_calloc(sizeof(*s->chstats), outlink->ch_layout.nb_channels); + s->chstats = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->chstats)); if (!s->chstats) return AVERROR(ENOMEM); s->nb_channels = outlink->ch_layout.nb_channels; - s->tc_samples = s->time_constant * outlink->sample_rate + .5; + s->tc_samples = lrint(s->time_constant * outlink->sample_rate); return 0; } @@ -73,7 +73,7 @@ static void finish_block(ChannelStats *p) int peak_bin, rms_bin; float peak, rms; - rms = sqrt(2 * p->sum / p->nb_samples); + rms = sqrtf(2.f * p->sum / p->nb_samples); peak = p->peak; rms_bin = av_clip(lrintf(rms * BINS), 0, BINS); peak_bin = av_clip(lrintf(peak * BINS), 0, BINS); @@ -88,36 +88,33 @@ static void finish_block(ChannelStats *p) static void update_stat(DRMeterContext *s, ChannelStats *p, float sample) { - if (p->nb_samples >= s->tc_samples) { - finish_block(p); - } - - p->peak = FFMAX(FFABS(sample), p->peak); + p->peak = fmaxf(fabsf(sample), p->peak); p->sum += sample * sample; p->nb_samples++; + if (p->nb_samples >= s->tc_samples) + finish_block(p); } static int filter_frame(AVFilterLink *inlink, AVFrame *buf) { DRMeterContext *s = inlink->dst->priv; const int channels = s->nb_channels; - int i, c; switch (inlink->format) { case AV_SAMPLE_FMT_FLTP: - for (c = 0; c < channels; c++) { + for (int c = 0; c < channels; c++) { ChannelStats *p = &s->chstats[c]; const float *src = (const float *)buf->extended_data[c]; - for (i = 0; i < buf->nb_samples; i++, src++) + for (int i = 0; i < buf->nb_samples; i++, src++) update_stat(s, p, *src); } break; case AV_SAMPLE_FMT_FLT: { const float *src = (const float *)buf->extended_data[0]; - for (i = 0; i < buf->nb_samples; i++) { - for (c = 0; c < channels; c++, src++) + for (int i = 0; i < buf->nb_samples; i++) { + for (int c = 0; c < channels; c++, src++) update_stat(s, &s->chstats[c], *src); }} break; @@ -131,39 +128,42 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) static void print_stats(AVFilterContext *ctx) { DRMeterContext *s = ctx->priv; - float dr = 0; - int ch; + float dr = 0.f; - for (ch = 0; ch < s->nb_channels; ch++) { + for (int ch = 0; ch < s->nb_channels; ch++) { ChannelStats *p = &s->chstats[ch]; - float chdr, secondpeak, rmssum = 0; - int i, j, first = 0; + float chdr, secondpeak, rmssum = 0.f; + int first = 0, last = lrintf(0.2f * p->blknum); + int peak_bin = BINS; if (!p->nb_samples) { av_log(ctx, AV_LOG_INFO, "No data, dynamic range not meassurable\n"); return; } - finish_block(p); + if (p->nb_samples) + finish_block(p); - for (i = 0; i <= BINS; i++) { - if (p->peaks[BINS - i]) { - if (first) + for (int i = BINS; i >= 0; i--) { + if (p->peaks[i]) { + if (first || p->peaks[i] > 1) { + peak_bin = i; break; + } first = 1; } } - secondpeak = (BINS - i) / (double)BINS; + secondpeak = peak_bin / (float)BINS; - for (i = BINS, j = 0; i >= 0 && j < 0.2 * p->blknum; i--) { + for (int64_t i = BINS, j = 0; i >= 0 && j < last; i--) { if (p->rms[i]) { - rmssum += SQR(i / (double)BINS); + rmssum += SQR(i / (float)BINS) * p->rms[i]; j += p->rms[i]; } } - chdr = 20 * log10(secondpeak / sqrt(rmssum / (0.2 * p->blknum))); + chdr = 20.f * log10f(secondpeak / sqrtf(rmssum / (float)last)); dr += chdr; av_log(ctx, AV_LOG_INFO, "Channel %d: DR: %g\n", ch + 1, chdr); } diff --git a/libavfilter/af_dynaudnorm.c b/libavfilter/af_dynaudnorm.c index e9d8ad8ec86..fb0581b6e4b 100644 --- a/libavfilter/af_dynaudnorm.c +++ b/libavfilter/af_dynaudnorm.c @@ -1019,13 +1019,6 @@ static const AVFilterPad avfilter_af_dynaudnorm_inputs[] = { }, }; -static const AVFilterPad avfilter_af_dynaudnorm_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_dynaudnorm = { .name = "dynaudnorm", .description = NULL_IF_CONFIG_SMALL("Dynamic Audio Normalizer."), @@ -1034,7 +1027,7 @@ const AVFilter ff_af_dynaudnorm = { .uninit = uninit, .activate = activate, FILTER_INPUTS(avfilter_af_dynaudnorm_inputs), - FILTER_OUTPUTS(avfilter_af_dynaudnorm_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .priv_class = &dynaudnorm_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | diff --git a/libavfilter/af_earwax.c b/libavfilter/af_earwax.c index f420a5ac551..38ff9c1f3ff 100644 --- a/libavfilter/af_earwax.c +++ b/libavfilter/af_earwax.c @@ -222,19 +222,12 @@ static const AVFilterPad earwax_inputs[] = { }, }; -static const AVFilterPad earwax_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_earwax = { .name = "earwax", .description = NULL_IF_CONFIG_SMALL("Widen the stereo image."), .priv_size = sizeof(EarwaxContext), .uninit = uninit, FILTER_INPUTS(earwax_inputs), - FILTER_OUTPUTS(earwax_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_extrastereo.c b/libavfilter/af_extrastereo.c index 6f1d691d8e0..2b1b09f9c29 100644 --- a/libavfilter/af_extrastereo.c +++ b/libavfilter/af_extrastereo.c @@ -110,20 +110,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_extrastereo = { .name = "extrastereo", .description = NULL_IF_CONFIG_SMALL("Increase difference between stereo audio channels."), .priv_size = sizeof(ExtraStereoContext), .priv_class = &extrastereo_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_firequalizer.c b/libavfilter/af_firequalizer.c index 40054f07de1..657dfbc0ca5 100644 --- a/libavfilter/af_firequalizer.c +++ b/libavfilter/af_firequalizer.c @@ -132,27 +132,27 @@ static const AVOption firequalizer_options[] = { { "gain_entry", "set gain entry", OFFSET(gain_entry), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, TFLAGS }, { "delay", "set delay", OFFSET(delay), AV_OPT_TYPE_DOUBLE, { .dbl = 0.01 }, 0.0, 1e10, FLAGS }, { "accuracy", "set accuracy", OFFSET(accuracy), AV_OPT_TYPE_DOUBLE, { .dbl = 5.0 }, 0.0, 1e10, FLAGS }, - { "wfunc", "set window function", OFFSET(wfunc), AV_OPT_TYPE_INT, { .i64 = WFUNC_HANN }, 0, NB_WFUNC-1, FLAGS, "wfunc" }, - { "rectangular", "rectangular window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_RECTANGULAR }, 0, 0, FLAGS, "wfunc" }, - { "hann", "hann window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_HANN }, 0, 0, FLAGS, "wfunc" }, - { "hamming", "hamming window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_HAMMING }, 0, 0, FLAGS, "wfunc" }, - { "blackman", "blackman window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_BLACKMAN }, 0, 0, FLAGS, "wfunc" }, - { "nuttall3", "3-term nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_NUTTALL3 }, 0, 0, FLAGS, "wfunc" }, - { "mnuttall3", "minimum 3-term nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_MNUTTALL3 }, 0, 0, FLAGS, "wfunc" }, - { "nuttall", "nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_NUTTALL }, 0, 0, FLAGS, "wfunc" }, - { "bnuttall", "blackman-nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_BNUTTALL }, 0, 0, FLAGS, "wfunc" }, - { "bharris", "blackman-harris window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_BHARRIS }, 0, 0, FLAGS, "wfunc" }, - { "tukey", "tukey window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_TUKEY }, 0, 0, FLAGS, "wfunc" }, + { "wfunc", "set window function", OFFSET(wfunc), AV_OPT_TYPE_INT, { .i64 = WFUNC_HANN }, 0, NB_WFUNC-1, FLAGS, .unit = "wfunc" }, + { "rectangular", "rectangular window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_RECTANGULAR }, 0, 0, FLAGS, .unit = "wfunc" }, + { "hann", "hann window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_HANN }, 0, 0, FLAGS, .unit = "wfunc" }, + { "hamming", "hamming window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_HAMMING }, 0, 0, FLAGS, .unit = "wfunc" }, + { "blackman", "blackman window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_BLACKMAN }, 0, 0, FLAGS, .unit = "wfunc" }, + { "nuttall3", "3-term nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_NUTTALL3 }, 0, 0, FLAGS, .unit = "wfunc" }, + { "mnuttall3", "minimum 3-term nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_MNUTTALL3 }, 0, 0, FLAGS, .unit = "wfunc" }, + { "nuttall", "nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_NUTTALL }, 0, 0, FLAGS, .unit = "wfunc" }, + { "bnuttall", "blackman-nuttall window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_BNUTTALL }, 0, 0, FLAGS, .unit = "wfunc" }, + { "bharris", "blackman-harris window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_BHARRIS }, 0, 0, FLAGS, .unit = "wfunc" }, + { "tukey", "tukey window", 0, AV_OPT_TYPE_CONST, { .i64 = WFUNC_TUKEY }, 0, 0, FLAGS, .unit = "wfunc" }, { "fixed", "set fixed frame samples", OFFSET(fixed), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "multi", "set multi channels mode", OFFSET(multi), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "zero_phase", "set zero phase mode", OFFSET(zero_phase), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, - { "scale", "set gain scale", OFFSET(scale), AV_OPT_TYPE_INT, { .i64 = SCALE_LINLOG }, 0, NB_SCALE-1, FLAGS, "scale" }, - { "linlin", "linear-freq linear-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LINLIN }, 0, 0, FLAGS, "scale" }, - { "linlog", "linear-freq logarithmic-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LINLOG }, 0, 0, FLAGS, "scale" }, - { "loglin", "logarithmic-freq linear-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LOGLIN }, 0, 0, FLAGS, "scale" }, - { "loglog", "logarithmic-freq logarithmic-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LOGLOG }, 0, 0, FLAGS, "scale" }, + { "scale", "set gain scale", OFFSET(scale), AV_OPT_TYPE_INT, { .i64 = SCALE_LINLOG }, 0, NB_SCALE-1, FLAGS, .unit = "scale" }, + { "linlin", "linear-freq linear-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LINLIN }, 0, 0, FLAGS, .unit = "scale" }, + { "linlog", "linear-freq logarithmic-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LINLOG }, 0, 0, FLAGS, .unit = "scale" }, + { "loglin", "logarithmic-freq linear-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LOGLIN }, 0, 0, FLAGS, .unit = "scale" }, + { "loglog", "logarithmic-freq logarithmic-gain", 0, AV_OPT_TYPE_CONST, { .i64 = SCALE_LOGLOG }, 0, 0, FLAGS, .unit = "scale" }, { "dumpfile", "set dump file", OFFSET(dumpfile), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, - { "dumpscale", "set dump scale", OFFSET(dumpscale), AV_OPT_TYPE_INT, { .i64 = SCALE_LINLOG }, 0, NB_SCALE-1, FLAGS, "scale" }, + { "dumpscale", "set dump scale", OFFSET(dumpscale), AV_OPT_TYPE_INT, { .i64 = SCALE_LINLOG }, 0, NB_SCALE-1, FLAGS, .unit = "scale" }, { "fft2", "set 2-channels fft", OFFSET(fft2), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "min_phase", "set minimum phase mode", OFFSET(min_phase), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { NULL } @@ -196,8 +196,8 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->gain_entry_cmd); } -static void fast_convolute(FIREqualizerContext *av_restrict s, const float *av_restrict kernel_buf, float *av_restrict conv_buf, - OverlapIndex *av_restrict idx, float *av_restrict data, int nsamples) +static void fast_convolute(FIREqualizerContext *restrict s, const float *restrict kernel_buf, float *restrict conv_buf, + OverlapIndex *restrict idx, float *restrict data, int nsamples) { if (nsamples <= s->nsamples_max) { float *buf = conv_buf + idx->buf_idx * s->rdft_len; @@ -233,9 +233,9 @@ static void fast_convolute(FIREqualizerContext *av_restrict s, const float *av_r } } -static void fast_convolute_nonlinear(FIREqualizerContext *av_restrict s, const float *av_restrict kernel_buf, - float *av_restrict conv_buf, OverlapIndex *av_restrict idx, - float *av_restrict data, int nsamples) +static void fast_convolute_nonlinear(FIREqualizerContext *restrict s, const float *restrict kernel_buf, + float *restrict conv_buf, OverlapIndex *restrict idx, + float *restrict data, int nsamples) { if (nsamples <= s->nsamples_max) { float *buf = conv_buf + idx->buf_idx * s->rdft_len; @@ -272,8 +272,8 @@ static void fast_convolute_nonlinear(FIREqualizerContext *av_restrict s, const f } } -static void fast_convolute2(FIREqualizerContext *av_restrict s, const float *av_restrict kernel_buf, AVComplexFloat *av_restrict conv_buf, - OverlapIndex *av_restrict idx, float *av_restrict data0, float *av_restrict data1, int nsamples) +static void fast_convolute2(FIREqualizerContext *restrict s, const float *restrict kernel_buf, AVComplexFloat *restrict conv_buf, + OverlapIndex *restrict idx, float *restrict data0, float *restrict data1, int nsamples) { if (nsamples <= s->nsamples_max) { AVComplexFloat *buf = conv_buf + idx->buf_idx * s->rdft_len; diff --git a/libavfilter/af_flanger.c b/libavfilter/af_flanger.c index 452436a4b66..453704ea63f 100644 --- a/libavfilter/af_flanger.c +++ b/libavfilter/af_flanger.c @@ -58,15 +58,15 @@ static const AVOption flanger_options[] = { { "regen", "percentage regeneration (delayed signal feedback)", OFFSET(feedback_gain), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -95, 95, A }, { "width", "percentage of delayed signal mixed with original", OFFSET(delay_gain), AV_OPT_TYPE_DOUBLE, {.dbl=71}, 0, 100, A }, { "speed", "sweeps per second (Hz)", OFFSET(speed), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0.1, 10, A }, - { "shape", "swept wave shape", OFFSET(wave_shape), AV_OPT_TYPE_INT, {.i64=WAVE_SIN}, WAVE_SIN, WAVE_NB-1, A, "type" }, - { "triangular", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_TRI}, 0, 0, A, "type" }, - { "t", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_TRI}, 0, 0, A, "type" }, - { "sinusoidal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_SIN}, 0, 0, A, "type" }, - { "s", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_SIN}, 0, 0, A, "type" }, + { "shape", "swept wave shape", OFFSET(wave_shape), AV_OPT_TYPE_INT, {.i64=WAVE_SIN}, WAVE_SIN, WAVE_NB-1, A, .unit = "type" }, + { "triangular", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_TRI}, 0, 0, A, .unit = "type" }, + { "t", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_TRI}, 0, 0, A, .unit = "type" }, + { "sinusoidal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_SIN}, 0, 0, A, .unit = "type" }, + { "s", NULL, 0, AV_OPT_TYPE_CONST, {.i64=WAVE_SIN}, 0, 0, A, .unit = "type" }, { "phase", "swept wave percentage phase-shift for multi-channel", OFFSET(channel_phase), AV_OPT_TYPE_DOUBLE, {.dbl=25}, 0, 100, A }, - { "interp", "delay-line interpolation", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A, "itype" }, - { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATION_LINEAR}, 0, 0, A, "itype" }, - { "quadratic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATION_QUADRATIC}, 0, 0, A, "itype" }, + { "interp", "delay-line interpolation", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A, .unit = "itype" }, + { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATION_LINEAR}, 0, 0, A, .unit = "itype" }, + { "quadratic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATION_QUADRATIC}, 0, 0, A, .unit = "itype" }, { NULL } }; @@ -195,13 +195,6 @@ static const AVFilterPad flanger_inputs[] = { }, }; -static const AVFilterPad flanger_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_flanger = { .name = "flanger", .description = NULL_IF_CONFIG_SMALL("Apply a flanging effect to the audio."), @@ -210,6 +203,6 @@ const AVFilter ff_af_flanger = { .init = init, .uninit = uninit, FILTER_INPUTS(flanger_inputs), - FILTER_OUTPUTS(flanger_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), }; diff --git a/libavfilter/af_haas.c b/libavfilter/af_haas.c index d283da72618..dd2ea26ec3d 100644 --- a/libavfilter/af_haas.c +++ b/libavfilter/af_haas.c @@ -60,11 +60,11 @@ static const AVOption haas_options[] = { { "level_in", "set level in", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A }, { "level_out", "set level out", OFFSET(level_out), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A }, { "side_gain", "set side gain", OFFSET(par_side_gain), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A }, - { "middle_source", "set middle source", OFFSET(par_m_source), AV_OPT_TYPE_INT, {.i64=2}, 0, 3, A, "source" }, - { "left", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, "source" }, - { "right", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, "source" }, - { "mid", "L+R", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, A, "source" }, - { "side", "L-R", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, A, "source" }, + { "middle_source", "set middle source", OFFSET(par_m_source), AV_OPT_TYPE_INT, {.i64=2}, 0, 3, A, .unit = "source" }, + { "left", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, .unit = "source" }, + { "right", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, .unit = "source" }, + { "mid", "L+R", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, A, .unit = "source" }, + { "side", "L-R", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, A, .unit = "source" }, { "middle_phase", "set middle phase", OFFSET(par_middle_phase), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, A }, { "left_delay", "set left delay", OFFSET(par_delay0), AV_OPT_TYPE_DOUBLE, {.dbl=2.05}, 0, MAX_HAAS_DELAY, A }, { "left_balance", "set left balance", OFFSET(par_balance0), AV_OPT_TYPE_DOUBLE, {.dbl=-1.0}, -1, 1, A }, @@ -206,13 +206,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_haas = { .name = "haas", .description = NULL_IF_CONFIG_SMALL("Apply Haas Stereo Enhancer."), @@ -220,6 +213,6 @@ const AVFilter ff_af_haas = { .priv_class = &haas_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_hdcd.c b/libavfilter/af_hdcd.c index 49bfa1bfa8e..27f35dd1962 100644 --- a/libavfilter/af_hdcd.c +++ b/libavfilter/af_hdcd.c @@ -47,6 +47,7 @@ #include "libavutil/opt.h" #include "libavutil/avassert.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "audio.h" @@ -994,17 +995,17 @@ static const AVOption hdcd_options[] = { { "force_pe", "Always extend peaks above -3dBFS even when PE is not signaled.", OFFSET(force_pe), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, A }, { "analyze_mode", "Replace audio with solid tone and signal some processing aspect in the amplitude.", - OFFSET(analyze_mode), AV_OPT_TYPE_INT, { .i64=HDCD_ANA_OFF }, 0, HDCD_ANA_TOP-1, A, "analyze_mode"}, - { "off", HDCD_ANA_OFF_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_OFF}, 0, 0, A, "analyze_mode" }, - { "lle", HDCD_ANA_LLE_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_LLE}, 0, 0, A, "analyze_mode" }, - { "pe", HDCD_ANA_PE_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_PE}, 0, 0, A, "analyze_mode" }, - { "cdt", HDCD_ANA_CDT_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_CDT}, 0, 0, A, "analyze_mode" }, - { "tgm", HDCD_ANA_TGM_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_TGM}, 0, 0, A, "analyze_mode" }, + OFFSET(analyze_mode), AV_OPT_TYPE_INT, { .i64=HDCD_ANA_OFF }, 0, HDCD_ANA_TOP-1, A, .unit = "analyze_mode"}, + { "off", HDCD_ANA_OFF_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_OFF}, 0, 0, A, .unit = "analyze_mode" }, + { "lle", HDCD_ANA_LLE_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_LLE}, 0, 0, A, .unit = "analyze_mode" }, + { "pe", HDCD_ANA_PE_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_PE}, 0, 0, A, .unit = "analyze_mode" }, + { "cdt", HDCD_ANA_CDT_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_CDT}, 0, 0, A, .unit = "analyze_mode" }, + { "tgm", HDCD_ANA_TGM_DESC, 0, AV_OPT_TYPE_CONST, {.i64=HDCD_ANA_TGM}, 0, 0, A, .unit = "analyze_mode" }, { "bits_per_sample", "Valid bits per sample (location of the true LSB).", - OFFSET(bits_per_sample), AV_OPT_TYPE_INT, { .i64=16 }, 16, 24, A, "bits_per_sample"}, - { "16", "16-bit (in s32 or s16)", 0, AV_OPT_TYPE_CONST, {.i64=16}, 0, 0, A, "bits_per_sample" }, - { "20", "20-bit (in s32)", 0, AV_OPT_TYPE_CONST, {.i64=20}, 0, 0, A, "bits_per_sample" }, - { "24", "24-bit (in s32)", 0, AV_OPT_TYPE_CONST, {.i64=24}, 0, 0, A, "bits_per_sample" }, + OFFSET(bits_per_sample), AV_OPT_TYPE_INT, { .i64=16 }, 16, 24, A, .unit = "bits_per_sample"}, + { "16", "16-bit (in s32 or s16)", 0, AV_OPT_TYPE_CONST, {.i64=16}, 0, 0, A, .unit = "bits_per_sample" }, + { "20", "20-bit (in s32)", 0, AV_OPT_TYPE_CONST, {.i64=20}, 0, 0, A, .unit = "bits_per_sample" }, + { "24", "24-bit (in s32)", 0, AV_OPT_TYPE_CONST, {.i64=24}, 0, 0, A, .unit = "bits_per_sample" }, {NULL} }; @@ -1713,9 +1714,6 @@ static int config_input(AVFilterLink *inlink) { HDCDContext *s = ctx->priv; int c; - av_log(ctx, AV_LOG_VERBOSE, "Auto-convert: %s\n", - (ctx->graph->disable_auto_convert) ? "disabled" : "enabled"); - if ((inlink->format == AV_SAMPLE_FMT_S16 || inlink->format == AV_SAMPLE_FMT_S16P) && s->bits_per_sample != 16) { @@ -1763,13 +1761,6 @@ static const AVFilterPad avfilter_af_hdcd_inputs[] = { }, }; -static const AVFilterPad avfilter_af_hdcd_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_hdcd = { .name = "hdcd", .description = NULL_IF_CONFIG_SMALL("Apply High Definition Compatible Digital (HDCD) decoding."), @@ -1778,6 +1769,6 @@ const AVFilter ff_af_hdcd = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_af_hdcd_inputs), - FILTER_OUTPUTS(avfilter_af_hdcd_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_headphone.c b/libavfilter/af_headphone.c index 005e151e2c2..9124f945a8e 100644 --- a/libavfilter/af_headphone.c +++ b/libavfilter/af_headphone.c @@ -29,6 +29,7 @@ #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "audio.h" @@ -319,6 +320,16 @@ static int check_ir(AVFilterLink *inlink, int input_number) s->hrir_in[input_number].ir_len = ir_len; s->ir_len = FFMAX(ir_len, s->ir_len); + if (ff_inlink_check_available_samples(inlink, ir_len + 1) == 1) { + s->hrir_in[input_number].eof = 1; + return 1; + } + + if (!s->hrir_in[input_number].eof) { + ff_inlink_request_frame(inlink); + return 0; + } + return 0; } @@ -348,7 +359,6 @@ static int headphone_frame(HeadphoneContext *s, AVFrame *in, AVFilterLink *outli } else { ff_filter_execute(ctx, headphone_fast_convolute, &td, NULL, 2); } - emms_c(); if (n_clippings[0] + n_clippings[1] > 0) { av_log(ctx, AV_LOG_WARNING, "%d of %d samples clipped. Please reduce gain.\n", @@ -534,7 +544,7 @@ static int activate(AVFilterContext *ctx) AVFrame *in = NULL; int i, ret; - FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); if (!s->eof_hrirs) { int eof = 1; for (i = 0; i < s->nb_hrir_inputs; i++) { @@ -543,24 +553,23 @@ static int activate(AVFilterContext *ctx) if (s->hrir_in[i].eof) continue; - if ((ret = check_ir(input, i)) < 0) + if ((ret = check_ir(input, i)) <= 0) return ret; - if (ff_outlink_get_status(input) == AVERROR_EOF) { + if (s->hrir_in[i].eof) { if (!ff_inlink_queued_samples(input)) { av_log(ctx, AV_LOG_ERROR, "No samples provided for " "HRIR stream %d.\n", i); return AVERROR_INVALIDDATA; } - s->hrir_in[i].eof = 1; } else { - if (ff_outlink_frame_wanted(ctx->outputs[0])) - ff_inlink_request_frame(input); eof = 0; } } - if (!eof) + if (!eof) { + ff_filter_set_ready(ctx, 100); return 0; + } s->eof_hrirs = 1; ret = convert_coeffs(ctx, inlink); @@ -569,7 +578,7 @@ static int activate(AVFilterContext *ctx) } else if (!s->have_hrirs) return AVERROR_EOF; - if ((ret = ff_inlink_consume_samples(ctx->inputs[0], s->size, s->size, &in)) > 0) { + if ((ret = ff_inlink_consume_samples(inlink, s->size, s->size, &in)) > 0) { ret = headphone_frame(s, in, outlink); if (ret < 0) return ret; @@ -578,9 +587,9 @@ static int activate(AVFilterContext *ctx) if (ret < 0) return ret; - FF_FILTER_FORWARD_STATUS(ctx->inputs[0], ctx->outputs[0]); - if (ff_outlink_frame_wanted(ctx->outputs[0])) - ff_inlink_request_frame(ctx->inputs[0]); + FF_FILTER_FORWARD_STATUS(inlink, outlink); + if (ff_outlink_frame_wanted(outlink)) + ff_inlink_request_frame(inlink); return 0; } @@ -743,13 +752,13 @@ static const AVOption headphone_options[] = { { "map", "set channels convolution mappings", OFFSET(map), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, { "gain", "set gain in dB", OFFSET(gain), AV_OPT_TYPE_FLOAT, {.dbl=0}, -20, 40, .flags = FLAGS }, { "lfe", "set lfe gain in dB", OFFSET(lfe_gain), AV_OPT_TYPE_FLOAT, {.dbl=0}, -20, 40, .flags = FLAGS }, - { "type", "set processing", OFFSET(type), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, .flags = FLAGS, "type" }, - { "time", "time domain", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = FLAGS, "type" }, - { "freq", "frequency domain", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = FLAGS, "type" }, + { "type", "set processing", OFFSET(type), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, .flags = FLAGS, .unit = "type" }, + { "time", "time domain", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = FLAGS, .unit = "type" }, + { "freq", "frequency domain", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = FLAGS, .unit = "type" }, { "size", "set frame size", OFFSET(size), AV_OPT_TYPE_INT, {.i64=1024},1024,96000, .flags = FLAGS }, - { "hrir", "set hrir format", OFFSET(hrir_fmt), AV_OPT_TYPE_INT, {.i64=HRIR_STEREO}, 0, 1, .flags = FLAGS, "hrir" }, - { "stereo", "hrir files have exactly 2 channels", 0, AV_OPT_TYPE_CONST, {.i64=HRIR_STEREO}, 0, 0, .flags = FLAGS, "hrir" }, - { "multich", "single multichannel hrir file", 0, AV_OPT_TYPE_CONST, {.i64=HRIR_MULTI}, 0, 0, .flags = FLAGS, "hrir" }, + { "hrir", "set hrir format", OFFSET(hrir_fmt), AV_OPT_TYPE_INT, {.i64=HRIR_STEREO}, 0, 1, .flags = FLAGS, .unit = "hrir" }, + { "stereo", "hrir files have exactly 2 channels", 0, AV_OPT_TYPE_CONST, {.i64=HRIR_STEREO}, 0, 0, .flags = FLAGS, .unit = "hrir" }, + { "multich", "single multichannel hrir file", 0, AV_OPT_TYPE_CONST, {.i64=HRIR_MULTI}, 0, 0, .flags = FLAGS, .unit = "hrir" }, { NULL } }; diff --git a/libavfilter/af_join.c b/libavfilter/af_join.c index dc075a8b27d..8dab3f0931c 100644 --- a/libavfilter/af_join.c +++ b/libavfilter/af_join.c @@ -48,10 +48,10 @@ typedef struct JoinContext { int inputs; char *map; - char *channel_layout_str; AVChannelLayout ch_layout; int64_t eof_pts; + int eof; ChannelMap *channels; @@ -72,7 +72,7 @@ typedef struct JoinContext { static const AVOption join_options[] = { { "inputs", "Number of input streams.", OFFSET(inputs), AV_OPT_TYPE_INT, { .i64 = 2 }, 1, INT_MAX, A|F }, { "channel_layout", "Channel layout of the " - "output stream.", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, {.str = "stereo"}, 0, 0, A|F }, + "output stream.", OFFSET(ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str = "stereo"}, 0, 0, A|F }, { "map", "A comma-separated list of channels maps in the format " "'input_stream.input_channel-output_channel.", OFFSET(map), AV_OPT_TYPE_STRING, .flags = A|F }, @@ -156,26 +156,6 @@ static av_cold int join_init(AVFilterContext *ctx) JoinContext *s = ctx->priv; int ret, i; - ret = av_channel_layout_from_string(&s->ch_layout, s->channel_layout_str); - if (ret < 0) { -#if FF_API_OLD_CHANNEL_LAYOUT - uint64_t mask; -FF_DISABLE_DEPRECATION_WARNINGS - mask = av_get_channel_layout(s->channel_layout_str); - if (!mask) { -#endif - av_log(ctx, AV_LOG_ERROR, "Error parsing channel layout '%s'.\n", - s->channel_layout_str); - return AVERROR(EINVAL); -#if FF_API_OLD_CHANNEL_LAYOUT - } -FF_ENABLE_DEPRECATION_WARNINGS - av_log(ctx, AV_LOG_WARNING, "Channel layout '%s' uses a deprecated syntax.\n", - s->channel_layout_str); - av_channel_layout_from_mask(&s->ch_layout, mask); -#endif - } - s->channels = av_calloc(s->ch_layout.nb_channels, sizeof(*s->channels)); s->buffers = av_calloc(s->ch_layout.nb_channels, sizeof(*s->buffers)); s->input_frames = av_calloc(s->inputs, sizeof(*s->input_frames)); @@ -520,14 +500,12 @@ static int try_push_frame(AVFilterContext *ctx) } frame->nb_samples = nb_samples; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - frame->channel_layout = outlink->channel_layout; - frame->channels = outlink->ch_layout.nb_channels; -FF_ENABLE_DEPRECATION_WARNINGS -#endif + frame->duration = av_rescale_q(frame->nb_samples, + av_make_q(1, outlink->sample_rate), + outlink->time_base); + if ((ret = av_channel_layout_copy(&frame->ch_layout, &outlink->ch_layout)) < 0) - return ret; + goto fail; frame->sample_rate = outlink->sample_rate; frame->format = outlink->format; frame->pts = s->input_frames[0]->pts; @@ -552,10 +530,11 @@ FF_ENABLE_DEPRECATION_WARNINGS return ret; eof: for (i = 0; i < ctx->nb_inputs; i++) { - if (ff_outlink_get_status(ctx->inputs[i]) && + if (s->eof && ff_inlink_queued_samples(ctx->inputs[i]) <= 0 && !s->input_frames[i]) { ff_outlink_set_status(outlink, AVERROR_EOF, s->eof_pts); + break; } } @@ -576,11 +555,10 @@ static int activate(AVFilterContext *ctx) if (ret < 0) { return ret; } else if (ret == 0 && ff_inlink_acknowledge_status(ctx->inputs[0], &status, &pts)) { - ff_outlink_set_status(ctx->outputs[0], status, s->eof_pts); - return 0; + s->eof |= status == AVERROR_EOF; } - if (!s->input_frames[0] && ff_outlink_frame_wanted(ctx->outputs[0])) { + if (!s->eof && !s->input_frames[0] && ff_outlink_frame_wanted(ctx->outputs[0])) { ff_inlink_request_frame(ctx->inputs[0]); return 0; } @@ -596,11 +574,10 @@ static int activate(AVFilterContext *ctx) if (ret < 0) { return ret; } else if (ff_inlink_acknowledge_status(ctx->inputs[i], &status, &pts)) { - ff_outlink_set_status(ctx->outputs[0], status, pts); - return 0; + s->eof |= status == AVERROR_EOF; } - if (!s->input_frames[i]) { + if (!s->eof && !s->input_frames[i]) { ff_inlink_request_frame(ctx->inputs[i]); return 0; } diff --git a/libavfilter/af_ladspa.c b/libavfilter/af_ladspa.c index fc93d56af7f..7567c0577d2 100644 --- a/libavfilter/af_ladspa.c +++ b/libavfilter/af_ladspa.c @@ -33,6 +33,7 @@ #include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" typedef struct MetaItem { @@ -401,11 +402,6 @@ static int config_output(AVFilterLink *outlink) if (s->nb_inputs == s->nb_outputs) { if ((ret = av_channel_layout_copy(&outlink->ch_layout, &inlink->ch_layout)) < 0) return ret; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - outlink->channel_layout = inlink->channel_layout; -FF_ENABLE_DEPRECATION_WARNINGS -#endif } ret = 0; diff --git a/libavfilter/af_loudnorm.c b/libavfilter/af_loudnorm.c index 609eae797c8..6cb9c06064b 100644 --- a/libavfilter/af_loudnorm.c +++ b/libavfilter/af_loudnorm.c @@ -23,6 +23,7 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "audio.h" #include "ebur128.h" @@ -116,10 +117,10 @@ static const AVOption loudnorm_options[] = { { "offset", "set offset gain", OFFSET(offset), AV_OPT_TYPE_DOUBLE, {.dbl = 0.}, -99., 99., FLAGS }, { "linear", "normalize linearly if possible", OFFSET(linear), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, { "dual_mono", "treat mono input as dual-mono", OFFSET(dual_mono), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, - { "print_format", "set print format for stats", OFFSET(print_format), AV_OPT_TYPE_INT, {.i64 = NONE}, NONE, PF_NB -1, FLAGS, "print_format" }, - { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NONE}, 0, 0, FLAGS, "print_format" }, - { "json", 0, 0, AV_OPT_TYPE_CONST, {.i64 = JSON}, 0, 0, FLAGS, "print_format" }, - { "summary", 0, 0, AV_OPT_TYPE_CONST, {.i64 = SUMMARY}, 0, 0, FLAGS, "print_format" }, + { "print_format", "set print format for stats", OFFSET(print_format), AV_OPT_TYPE_INT, {.i64 = NONE}, NONE, PF_NB -1, FLAGS, .unit = "print_format" }, + { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NONE}, 0, 0, FLAGS, .unit = "print_format" }, + { "json", 0, 0, AV_OPT_TYPE_CONST, {.i64 = JSON}, 0, 0, FLAGS, .unit = "print_format" }, + { "summary", 0, 0, AV_OPT_TYPE_CONST, {.i64 = SUMMARY}, 0, 0, FLAGS, .unit = "print_format" }, { NULL } }; @@ -730,26 +731,24 @@ static int activate(AVFilterContext *ctx) static int query_formats(AVFilterContext *ctx) { LoudNormContext *s = ctx->priv; - AVFilterFormats *formats = NULL; static const int input_srate[] = {192000, -1}; + static const enum AVSampleFormat sample_fmts[] = { + AV_SAMPLE_FMT_DBL, + AV_SAMPLE_FMT_NONE + }; int ret = ff_set_common_all_channel_counts(ctx); if (ret < 0) return ret; - ret = ff_add_format(&formats, AV_SAMPLE_FMT_DBL); - if (ret) - return ret; - ret = ff_set_common_formats(ctx, formats); - if (ret) + ret = ff_set_common_formats_from_list(ctx, sample_fmts); + if (ret < 0) return ret; - if (s->frame_type != LINEAR_MODE) { - formats = ff_make_format_list(input_srate); + if (s->frame_type == LINEAR_MODE) { + return ff_set_common_all_samplerates(ctx); } else { - formats = ff_all_samplerates(); + return ff_set_common_samplerates_from_list(ctx, input_srate); } - - return ff_set_common_samplerates(ctx, formats); } static int config_input(AVFilterLink *inlink) @@ -928,13 +927,6 @@ static const AVFilterPad avfilter_af_loudnorm_inputs[] = { }, }; -static const AVFilterPad avfilter_af_loudnorm_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_loudnorm = { .name = "loudnorm", .description = NULL_IF_CONFIG_SMALL("EBU R128 loudness normalization"), @@ -944,6 +936,6 @@ const AVFilter ff_af_loudnorm = { .activate = activate, .uninit = uninit, FILTER_INPUTS(avfilter_af_loudnorm_inputs), - FILTER_OUTPUTS(avfilter_af_loudnorm_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_lv2.c b/libavfilter/af_lv2.c index 3cdf16457cd..7b3ddf94489 100644 --- a/libavfilter/af_lv2.c +++ b/libavfilter/af_lv2.c @@ -33,6 +33,7 @@ #include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" typedef struct URITable { @@ -291,11 +292,6 @@ static int config_output(AVFilterLink *outlink) int ret; if ((ret = av_channel_layout_copy(&outlink->ch_layout, &inlink->ch_layout)) < 0) return ret; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - outlink->channel_layout = inlink->channel_layout; -FF_ENABLE_DEPRECATION_WARNINGS -#endif } } else { diff --git a/libavfilter/af_mcompand.c b/libavfilter/af_mcompand.c index 1267cd9f343..9347f06d87f 100644 --- a/libavfilter/af_mcompand.c +++ b/libavfilter/af_mcompand.c @@ -417,8 +417,8 @@ static int config_output(AVFilterLink *outlink) } new_nb_items += sscanf(tstr2, "%lf", &s->bands[i].topfreq) == 1; - if (s->bands[i].topfreq < 0 || s->bands[i].topfreq >= outlink->sample_rate / 2) { - av_log(ctx, AV_LOG_ERROR, "crossover_frequency: %f, should be >=0 and lower than half of sample rate: %d.\n", s->bands[i].topfreq, outlink->sample_rate / 2); + if (s->bands[i].topfreq < 0 || s->bands[i].topfreq >= outlink->sample_rate / 2.0) { + av_log(ctx, AV_LOG_ERROR, "crossover_frequency: %f, should be >=0 and lower than half of sample rate: %f.\n", s->bands[i].topfreq, outlink->sample_rate / 2.0); return AVERROR(EINVAL); } diff --git a/libavfilter/af_pan.c b/libavfilter/af_pan.c index 4672648d467..04bf7d3fe35 100644 --- a/libavfilter/af_pan.c +++ b/libavfilter/af_pan.c @@ -118,6 +118,14 @@ static av_cold int init(AVFilterContext *ctx) if (ret < 0) goto fail; + if (pan->nb_output_channels > MAX_CHANNELS) { + av_log(ctx, AV_LOG_ERROR, + "af_pan supports a maximum of %d channels. " + "Feel free to ask for a higher limit.\n", MAX_CHANNELS); + ret = AVERROR_PATCHWELCOME; + goto fail; + } + /* parse channel specifications */ while ((arg = arg0 = av_strtok(NULL, "|", &tokenizer))) { int used_in_ch[MAX_CHANNELS] = {0}; @@ -379,24 +387,21 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) swr_convert(pan->swr, outsamples->extended_data, n, (void *)insamples->extended_data, n); av_frame_copy_props(outsamples, insamples); -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - outsamples->channel_layout = outlink->channel_layout; - outsamples->channels = outlink->ch_layout.nb_channels; -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if ((ret = av_channel_layout_copy(&outsamples->ch_layout, &outlink->ch_layout)) < 0) + if ((ret = av_channel_layout_copy(&outsamples->ch_layout, &outlink->ch_layout)) < 0) { + av_frame_free(&outsamples); + av_frame_free(&insamples); return ret; + } - ret = ff_filter_frame(outlink, outsamples); av_frame_free(&insamples); - return ret; + return ff_filter_frame(outlink, outsamples); } static av_cold void uninit(AVFilterContext *ctx) { PanContext *pan = ctx->priv; swr_free(&pan->swr); + av_channel_layout_uninit(&pan->out_channel_layout); } #define OFFSET(x) offsetof(PanContext, x) @@ -417,13 +422,6 @@ static const AVFilterPad pan_inputs[] = { }, }; -static const AVFilterPad pan_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_pan = { .name = "pan", .description = NULL_IF_CONFIG_SMALL("Remix channels with coefficients (panning)."), @@ -432,6 +430,6 @@ const AVFilter ff_af_pan = { .init = init, .uninit = uninit, FILTER_INPUTS(pan_inputs), - FILTER_OUTPUTS(pan_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/af_replaygain.c b/libavfilter/af_replaygain.c index 53852ac8bc1..266121e2c00 100644 --- a/libavfilter/af_replaygain.c +++ b/libavfilter/af_replaygain.c @@ -23,10 +23,14 @@ * ReplayGain scanner */ +#include + #include "libavutil/avassert.h" #include "libavutil/channel_layout.h" +#include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #define HISTOGRAM_SLOTS 12000 @@ -306,8 +310,11 @@ static const ReplayGainFreqInfo freqinfos[] = }; typedef struct ReplayGainContext { + const AVClass *class; + uint32_t histogram[HISTOGRAM_SLOTS]; float peak; + float gain; int yule_hist_i, butter_hist_i; const double *yule_coeff_a; const double *yule_coeff_b; @@ -576,13 +583,22 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) return ff_filter_frame(outlink, in); } -static av_cold void uninit(AVFilterContext *ctx) +static int request_frame(AVFilterLink *outlink) { + AVFilterContext *ctx = outlink->src; ReplayGainContext *s = ctx->priv; - float gain = calc_replaygain(s->histogram); + int ret = 0; - av_log(ctx, AV_LOG_INFO, "track_gain = %+.2f dB\n", gain); - av_log(ctx, AV_LOG_INFO, "track_peak = %.6f\n", s->peak); + ret = ff_request_frame(ctx->inputs[0]); + + if (ret == AVERROR_EOF) { + s->gain = calc_replaygain(s->histogram); + + av_log(ctx, AV_LOG_INFO, "track_gain = %+.2f dB\n", s->gain); + av_log(ctx, AV_LOG_INFO, "track_peak = %.6f\n", s->peak); + } + + return ret; } static const AVFilterPad replaygain_inputs[] = { @@ -598,14 +614,26 @@ static const AVFilterPad replaygain_outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_AUDIO, + .request_frame = request_frame, }, }; +#define OFFSET(x) offsetof(ReplayGainContext, x) +#define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_EXPORT|AV_OPT_FLAG_READONLY + +static const AVOption replaygain_options[] = { + { "track_gain", "track gain (dB)", OFFSET(gain), AV_OPT_TYPE_FLOAT,{.dbl=0}, -FLT_MAX, FLT_MAX, FLAGS }, + { "track_peak", "track peak", OFFSET(peak), AV_OPT_TYPE_FLOAT,{.dbl=0}, -FLT_MAX, FLT_MAX, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(replaygain); + const AVFilter ff_af_replaygain = { .name = "replaygain", .description = NULL_IF_CONFIG_SMALL("ReplayGain scanner."), - .uninit = uninit, .priv_size = sizeof(ReplayGainContext), + .priv_class = &replaygain_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(replaygain_inputs), FILTER_OUTPUTS(replaygain_outputs), diff --git a/libavfilter/af_rubberband.c b/libavfilter/af_rubberband.c index 34a16056673..a820ba75385 100644 --- a/libavfilter/af_rubberband.c +++ b/libavfilter/af_rubberband.c @@ -25,7 +25,6 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" typedef struct RubberBandContext { @@ -38,7 +37,9 @@ typedef struct RubberBandContext { int64_t nb_samples_out; int64_t nb_samples_in; int64_t first_pts; + int64_t last_pts; int nb_samples; + int eof; } RubberBandContext; #define OFFSET(x) offsetof(RubberBandContext, x) @@ -48,34 +49,34 @@ typedef struct RubberBandContext { static const AVOption rubberband_options[] = { { "tempo", "set tempo scale factor", OFFSET(tempo), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.01, 100, AT }, { "pitch", "set pitch scale factor", OFFSET(pitch), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.01, 100, AT }, - { "transients", "set transients", OFFSET(transients), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, "transients" }, - { "crisp", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionTransientsCrisp}, 0, 0, A, "transients" }, - { "mixed", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionTransientsMixed}, 0, 0, A, "transients" }, - { "smooth", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionTransientsSmooth}, 0, 0, A, "transients" }, - { "detector", "set detector", OFFSET(detector), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, "detector" }, - { "compound", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionDetectorCompound}, 0, 0, A, "detector" }, - { "percussive", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionDetectorPercussive}, 0, 0, A, "detector" }, - { "soft", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionDetectorSoft}, 0, 0, A, "detector" }, - { "phase", "set phase", OFFSET(phase), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, "phase" }, - { "laminar", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionPhaseLaminar}, 0, 0, A, "phase" }, - { "independent", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionPhaseIndependent}, 0, 0, A, "phase" }, - { "window", "set window", OFFSET(window), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, "window" }, - { "standard", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionWindowStandard}, 0, 0, A, "window" }, - { "short", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionWindowShort}, 0, 0, A, "window" }, - { "long", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionWindowLong}, 0, 0, A, "window" }, - { "smoothing", "set smoothing", OFFSET(smoothing), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, "smoothing" }, - { "off", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionSmoothingOff}, 0, 0, A, "smoothing" }, - { "on", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionSmoothingOn}, 0, 0, A, "smoothing" }, - { "formant", "set formant", OFFSET(formant), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, "formant" }, - { "shifted", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionFormantShifted}, 0, 0, A, "formant" }, - { "preserved", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionFormantPreserved}, 0, 0, A, "formant" }, - { "pitchq", "set pitch quality", OFFSET(opitch), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, "pitch" }, - { "quality", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionPitchHighQuality}, 0, 0, A, "pitch" }, - { "speed", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionPitchHighSpeed}, 0, 0, A, "pitch" }, - { "consistency", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionPitchHighConsistency}, 0, 0, A, "pitch" }, - { "channels", "set channels", OFFSET(channels), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, "channels" }, - { "apart", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionChannelsApart}, 0, 0, A, "channels" }, - { "together", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionChannelsTogether}, 0, 0, A, "channels" }, + { "transients", "set transients", OFFSET(transients), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, .unit = "transients" }, + { "crisp", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionTransientsCrisp}, 0, 0, A, .unit = "transients" }, + { "mixed", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionTransientsMixed}, 0, 0, A, .unit = "transients" }, + { "smooth", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionTransientsSmooth}, 0, 0, A, .unit = "transients" }, + { "detector", "set detector", OFFSET(detector), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, .unit = "detector" }, + { "compound", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionDetectorCompound}, 0, 0, A, .unit = "detector" }, + { "percussive", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionDetectorPercussive}, 0, 0, A, .unit = "detector" }, + { "soft", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionDetectorSoft}, 0, 0, A, .unit = "detector" }, + { "phase", "set phase", OFFSET(phase), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, .unit = "phase" }, + { "laminar", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionPhaseLaminar}, 0, 0, A, .unit = "phase" }, + { "independent", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionPhaseIndependent}, 0, 0, A, .unit = "phase" }, + { "window", "set window", OFFSET(window), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, .unit = "window" }, + { "standard", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionWindowStandard}, 0, 0, A, .unit = "window" }, + { "short", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionWindowShort}, 0, 0, A, .unit = "window" }, + { "long", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionWindowLong}, 0, 0, A, .unit = "window" }, + { "smoothing", "set smoothing", OFFSET(smoothing), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, .unit = "smoothing" }, + { "off", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionSmoothingOff}, 0, 0, A, .unit = "smoothing" }, + { "on", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionSmoothingOn}, 0, 0, A, .unit = "smoothing" }, + { "formant", "set formant", OFFSET(formant), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, .unit = "formant" }, + { "shifted", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionFormantShifted}, 0, 0, A, .unit = "formant" }, + { "preserved", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionFormantPreserved}, 0, 0, A, .unit = "formant" }, + { "pitchq", "set pitch quality", OFFSET(opitch), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, .unit = "pitch" }, + { "quality", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionPitchHighQuality}, 0, 0, A, .unit = "pitch" }, + { "speed", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionPitchHighSpeed}, 0, 0, A, .unit = "pitch" }, + { "consistency", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionPitchHighConsistency}, 0, 0, A, .unit = "pitch" }, + { "channels", "set channels", OFFSET(channels), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, A, .unit = "channels" }, + { "apart", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionChannelsApart}, 0, 0, A, .unit = "channels" }, + { "together", 0, 0, AV_OPT_TYPE_CONST, {.i64=RubberBandOptionChannelsTogether}, 0, 0, A, .unit = "channels" }, { NULL }, }; @@ -100,7 +101,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) if (s->first_pts == AV_NOPTS_VALUE) s->first_pts = in->pts; - rubberband_process(s->rbs, (const float *const *)in->data, in->nb_samples, ff_outlink_get_status(inlink)); + rubberband_process(s->rbs, (const float *const *)in->extended_data, in->nb_samples, s->eof); s->nb_samples_in += in->nb_samples; nb_samples = rubberband_available(s->rbs); @@ -113,7 +114,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) out->pts = s->first_pts + av_rescale_q(s->nb_samples_out, (AVRational){ 1, outlink->sample_rate }, outlink->time_base); - nb_samples = rubberband_retrieve(s->rbs, (float *const *)out->data, nb_samples); + s->last_pts = out->pts; + nb_samples = rubberband_retrieve(s->rbs, (float *const *)out->extended_data, nb_samples); out->nb_samples = nb_samples; ret = ff_filter_frame(outlink, out); s->nb_samples_out += nb_samples; @@ -151,11 +153,16 @@ static int activate(AVFilterContext *ctx) AVFilterLink *outlink = ctx->outputs[0]; RubberBandContext *s = ctx->priv; AVFrame *in = NULL; - int ret; + int64_t pts; + int status, ret; FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); ret = ff_inlink_consume_samples(inlink, s->nb_samples, s->nb_samples, &in); + + if (ff_inlink_acknowledge_status(inlink, &status, &pts)) + s->eof |= status == AVERROR_EOF; + if (ret < 0) return ret; if (ret > 0) { @@ -164,7 +171,11 @@ static int activate(AVFilterContext *ctx) return ret; } - FF_FILTER_FORWARD_STATUS(inlink, outlink); + if (s->eof) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->last_pts); + return 0; + } + FF_FILTER_FORWARD_WANTED(outlink, inlink); return FFERROR_NOT_READY; @@ -195,13 +206,6 @@ static const AVFilterPad rubberband_inputs[] = { }, }; -static const AVFilterPad rubberband_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_rubberband = { .name = "rubberband", .description = NULL_IF_CONFIG_SMALL("Apply time-stretching and pitch-shifting."), @@ -210,7 +214,7 @@ const AVFilter ff_af_rubberband = { .uninit = uninit, .activate = activate, FILTER_INPUTS(rubberband_inputs), - FILTER_OUTPUTS(rubberband_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_FLTP), .process_command = process_command, }; diff --git a/libavfilter/af_sidechaincompress.c b/libavfilter/af_sidechaincompress.c index 4d060060e91..d152a829537 100644 --- a/libavfilter/af_sidechaincompress.c +++ b/libavfilter/af_sidechaincompress.c @@ -75,21 +75,21 @@ typedef struct SidechainCompressContext { static const AVOption options[] = { { "level_in", "set input gain", OFFSET(level_in), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A|F|R }, - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A|F|R, "mode" }, - { "downward",0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A|F|R, "mode" }, - { "upward", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A|F|R, "mode" }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A|F|R, .unit = "mode" }, + { "downward",0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A|F|R, .unit = "mode" }, + { "upward", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A|F|R, .unit = "mode" }, { "threshold", "set threshold", OFFSET(threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0.125}, 0.000976563, 1, A|F|R }, { "ratio", "set ratio", OFFSET(ratio), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 1, 20, A|F|R }, { "attack", "set attack", OFFSET(attack), AV_OPT_TYPE_DOUBLE, {.dbl=20}, 0.01, 2000, A|F|R }, { "release", "set release", OFFSET(release), AV_OPT_TYPE_DOUBLE, {.dbl=250}, 0.01, 9000, A|F|R }, { "makeup", "set make up gain", OFFSET(makeup), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 1, 64, A|F|R }, { "knee", "set knee", OFFSET(knee), AV_OPT_TYPE_DOUBLE, {.dbl=2.82843}, 1, 8, A|F|R }, - { "link", "set link type", OFFSET(link), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A|F|R, "link" }, - { "average", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A|F|R, "link" }, - { "maximum", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A|F|R, "link" }, - { "detection", "set detection", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, A|F|R, "detection" }, - { "peak", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A|F|R, "detection" }, - { "rms", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A|F|R, "detection" }, + { "link", "set link type", OFFSET(link), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, A|F|R, .unit = "link" }, + { "average", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A|F|R, .unit = "link" }, + { "maximum", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A|F|R, .unit = "link" }, + { "detection", "set detection", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, A|F|R, .unit = "detection" }, + { "peak", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A|F|R, .unit = "detection" }, + { "rms", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A|F|R, .unit = "detection" }, { "level_sc", "set sidechain gain", OFFSET(level_sc), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A|F|R }, { "mix", "set mix", OFFSET(mix), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, A|F|R }, { NULL } diff --git a/libavfilter/af_silencedetect.c b/libavfilter/af_silencedetect.c index 6518283d9f4..845c65bfed6 100644 --- a/libavfilter/af_silencedetect.c +++ b/libavfilter/af_silencedetect.c @@ -28,7 +28,6 @@ #include "libavutil/opt.h" #include "libavutil/timestamp.h" #include "audio.h" -#include "formats.h" #include "avfilter.h" #include "internal.h" @@ -253,20 +252,13 @@ static const AVFilterPad silencedetect_inputs[] = { }, }; -static const AVFilterPad silencedetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_silencedetect = { .name = "silencedetect", .description = NULL_IF_CONFIG_SMALL("Detect silence."), .priv_size = sizeof(SilenceDetectContext), .uninit = uninit, FILTER_INPUTS(silencedetect_inputs), - FILTER_OUTPUTS(silencedetect_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP, AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_S32, AV_SAMPLE_FMT_S32P, diff --git a/libavfilter/af_silenceremove.c b/libavfilter/af_silenceremove.c index d5a2ac6a410..d60c86ad93e 100644 --- a/libavfilter/af_silenceremove.c +++ b/libavfilter/af_silenceremove.c @@ -23,18 +23,27 @@ #include /* DBL_MAX */ -#include "libavutil/audio_fifo.h" #include "libavutil/avassert.h" #include "libavutil/opt.h" -#include "libavutil/timestamp.h" #include "audio.h" -#include "formats.h" +#include "filters.h" #include "avfilter.h" #include "internal.h" enum SilenceDetect { - D_PEAK, + D_AVG, D_RMS, + D_PEAK, + D_MEDIAN, + D_PTP, + D_DEV, + D_NB +}; + +enum TimestampMode { + TS_WRITE, + TS_COPY, + TS_NB }; enum ThresholdMode { @@ -42,371 +51,116 @@ enum ThresholdMode { T_ALL, }; -enum SilenceMode { - SILENCE_TRIM, - SILENCE_TRIM_FLUSH, - SILENCE_COPY, - SILENCE_COPY_FLUSH, - SILENCE_STOP -}; - typedef struct SilenceRemoveContext { const AVClass *class; - enum SilenceMode mode; - + int start_mode; int start_periods; int64_t start_duration; int64_t start_duration_opt; double start_threshold; int64_t start_silence; int64_t start_silence_opt; - int start_mode; + int stop_mode; int stop_periods; int64_t stop_duration; int64_t stop_duration_opt; double stop_threshold; int64_t stop_silence; int64_t stop_silence_opt; - int stop_mode; int64_t window_duration_opt; - AVFrame *start_holdoff; - AVFrame *start_silence_hold; - size_t start_holdoff_offset; - size_t start_holdoff_end; - size_t start_silence_offset; - size_t start_silence_end; - int start_found_periods; - - AVFrame *stop_holdoff; - AVFrame *stop_silence_hold; - size_t stop_holdoff_offset; - size_t stop_holdoff_end; - size_t stop_silence_offset; - size_t stop_silence_end; - int stop_found_periods; - - AVFrame *window; - int window_offset; + int timestamp_mode; + + int start_found_periods; + int stop_found_periods; + + int start_sample_count; + int start_silence_count; + + int stop_sample_count; + int stop_silence_count; + + AVFrame *start_window; + AVFrame *stop_window; + + int *start_front; + int *start_back; + + int *stop_front; + int *stop_back; + int64_t window_duration; - double sum; + int cache_size; + + int start_window_pos; + int start_window_size; + + int stop_window_pos; + int stop_window_size; + + double *start_cache; + double *stop_cache; + + AVFrame *start_queuef; + int start_queue_pos; + int start_queue_size; + + AVFrame *stop_queuef; + int stop_queue_pos; + int stop_queue_size; - int one_period; int restart; + int found_nonsilence; int64_t next_pts; int detection; - void (*update)(struct SilenceRemoveContext *s, AVFrame *frame, int ch, int offset); - double (*compute)(struct SilenceRemoveContext *s, AVFrame *frame, int ch, int offset); - void (*copy)(struct SilenceRemoveContext *s, AVFrame *out, AVFrame *in, - int ch, int out_offset, int in_offset); - AVAudioFifo *fifo; + float (*compute_flt)(float *c, float s, float ws, int size, int *front, int *back); + double (*compute_dbl)(double *c, double s, double ws, int size, int *front, int *back); } SilenceRemoveContext; #define OFFSET(x) offsetof(SilenceRemoveContext, x) #define AF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM +#define AFR AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption silenceremove_options[] = { { "start_periods", "set periods of silence parts to skip from start", OFFSET(start_periods), AV_OPT_TYPE_INT, {.i64=0}, 0, 9000, AF }, { "start_duration", "set start duration of non-silence part", OFFSET(start_duration_opt), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT32_MAX, AF }, - { "start_threshold", "set threshold for start silence detection", OFFSET(start_threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, DBL_MAX, AF }, + { "start_threshold", "set threshold for start silence detection", OFFSET(start_threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, DBL_MAX, AFR }, { "start_silence", "set start duration of silence part to keep", OFFSET(start_silence_opt), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT32_MAX, AF }, - { "start_mode", "set which channel will trigger trimming from start", OFFSET(start_mode), AV_OPT_TYPE_INT, {.i64=T_ANY}, T_ANY, T_ALL, AF, "mode" }, - { "any", 0, 0, AV_OPT_TYPE_CONST, {.i64=T_ANY}, 0, 0, AF, "mode" }, - { "all", 0, 0, AV_OPT_TYPE_CONST, {.i64=T_ALL}, 0, 0, AF, "mode" }, + { "start_mode", "set which channel will trigger trimming from start", OFFSET(start_mode), AV_OPT_TYPE_INT, {.i64=T_ANY}, T_ANY, T_ALL, AFR, .unit = "mode" }, + { "any", 0, 0, AV_OPT_TYPE_CONST, {.i64=T_ANY}, 0, 0, AFR, .unit = "mode" }, + { "all", 0, 0, AV_OPT_TYPE_CONST, {.i64=T_ALL}, 0, 0, AFR, .unit = "mode" }, { "stop_periods", "set periods of silence parts to skip from end", OFFSET(stop_periods), AV_OPT_TYPE_INT, {.i64=0}, -9000, 9000, AF }, - { "stop_duration", "set stop duration of non-silence part", OFFSET(stop_duration_opt), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT32_MAX, AF }, - { "stop_threshold", "set threshold for stop silence detection", OFFSET(stop_threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, DBL_MAX, AF }, + { "stop_duration", "set stop duration of silence part", OFFSET(stop_duration_opt), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT32_MAX, AF }, + { "stop_threshold", "set threshold for stop silence detection", OFFSET(stop_threshold), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, DBL_MAX, AFR }, { "stop_silence", "set stop duration of silence part to keep", OFFSET(stop_silence_opt), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT32_MAX, AF }, - { "stop_mode", "set which channel will trigger trimming from end", OFFSET(stop_mode), AV_OPT_TYPE_INT, {.i64=T_ANY}, T_ANY, T_ALL, AF, "mode" }, - { "detection", "set how silence is detected", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=D_RMS}, D_PEAK,D_RMS, AF, "detection" }, - { "peak", "use absolute values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_PEAK},0, 0, AF, "detection" }, - { "rms", "use squared values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_RMS}, 0, 0, AF, "detection" }, + { "stop_mode", "set which channel will trigger trimming from end", OFFSET(stop_mode), AV_OPT_TYPE_INT, {.i64=T_ALL}, T_ANY, T_ALL, AFR, .unit = "mode" }, + { "detection", "set how silence is detected", OFFSET(detection), AV_OPT_TYPE_INT, {.i64=D_RMS}, 0, D_NB-1, AF, .unit = "detection" }, + { "avg", "use mean absolute values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_AVG}, 0, 0, AF, .unit = "detection" }, + { "rms", "use root mean squared values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_RMS}, 0, 0, AF, .unit = "detection" }, + { "peak", "use max absolute values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_PEAK},0, 0, AF, .unit = "detection" }, + { "median", "use median of absolute values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_MEDIAN},0, 0, AF, .unit = "detection" }, + { "ptp", "use absolute of max peak to min peak difference", 0, AV_OPT_TYPE_CONST, {.i64=D_PTP}, 0, 0, AF, .unit = "detection" }, + { "dev", "use standard deviation from values of samples", 0, AV_OPT_TYPE_CONST, {.i64=D_DEV}, 0, 0, AF, .unit = "detection" }, { "window", "set duration of window for silence detection", OFFSET(window_duration_opt), AV_OPT_TYPE_DURATION, {.i64=20000}, 0, 100000000, AF }, + { "timestamp", "set how every output frame timestamp is processed", OFFSET(timestamp_mode), AV_OPT_TYPE_INT, {.i64=TS_WRITE}, 0, TS_NB-1, AF, .unit = "timestamp" }, + { "write", "full timestamps rewrite, keep only the start time", 0, AV_OPT_TYPE_CONST, {.i64=TS_WRITE}, 0, 0, AF, .unit = "timestamp" }, + { "copy", "non-dropped frames are left with same timestamp", 0, AV_OPT_TYPE_CONST, {.i64=TS_COPY}, 0, 0, AF, .unit = "timestamp" }, { NULL } }; AVFILTER_DEFINE_CLASS(silenceremove); -static void copy_double(SilenceRemoveContext *s, AVFrame *out, AVFrame *in, - int ch, int out_offset, int in_offset) -{ - const double *srcp = (const double *)in->data[0]; - const double src = srcp[in->ch_layout.nb_channels * in_offset + ch]; - double *dstp = (double *)out->data[0]; - - dstp[out->ch_layout.nb_channels * out_offset + ch] = src; -} - -static void copy_doublep(SilenceRemoveContext *s, AVFrame *out, AVFrame *in, - int ch, int out_offset, int in_offset) -{ - const double *srcp = (const double *)in->extended_data[ch]; - const double src = srcp[in_offset]; - double *dstp = (double *)out->extended_data[ch]; +#define DEPTH 32 +#include "silenceremove_template.c" - dstp[out_offset] = src; -} - -static void copy_float(SilenceRemoveContext *s, AVFrame *out, AVFrame *in, - int ch, int out_offset, int in_offset) -{ - const float *srcp = (const float *)in->data[0]; - const float src = srcp[in->ch_layout.nb_channels * in_offset + ch]; - float *dstp = (float *)out->data[0]; - - dstp[out->ch_layout.nb_channels * out_offset + ch] = src; -} - -static void copy_floatp(SilenceRemoveContext *s, AVFrame *out, AVFrame *in, - int ch, int out_offset, int in_offset) -{ - const float *srcp = (const float *)in->extended_data[ch]; - const float src = srcp[in_offset]; - float *dstp = (float *)out->extended_data[ch]; - - dstp[out_offset] = src; -} - -static double compute_peak_double(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->data[0]; - const double *wsamples = (const double *)s->window->data[0]; - double sample = samples[frame->ch_layout.nb_channels * offset + ch]; - double wsample = wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - double new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmax(new_sum, 0.); - new_sum += fabs(sample); - - return new_sum / s->window_duration; -} - -static void update_peak_double(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->data[0]; - double *wsamples = (double *)s->window->data[0]; - double sample = samples[frame->ch_layout.nb_channels * offset + ch]; - double *wsample = &wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - - s->sum -= *wsample; - s->sum = fmax(s->sum, 0.); - *wsample = fabs(sample); - s->sum += *wsample; -} - -static double compute_peak_float(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->data[0]; - const float *wsamples = (const float *)s->window->data[0]; - float sample = samples[frame->ch_layout.nb_channels * offset + ch]; - float wsample = wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - float new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmaxf(new_sum, 0.f); - new_sum += fabsf(sample); - - return new_sum / s->window_duration; -} - -static void update_peak_float(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->data[0]; - float *wsamples = (float *)s->window->data[0]; - float sample = samples[frame->ch_layout.nb_channels * offset + ch]; - float *wsample = &wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - - s->sum -= *wsample; - s->sum = fmaxf(s->sum, 0.f); - *wsample = fabsf(sample); - s->sum += *wsample; -} - -static double compute_rms_double(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->data[0]; - const double *wsamples = (const double *)s->window->data[0]; - double sample = samples[frame->ch_layout.nb_channels * offset + ch]; - double wsample = wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - double new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmax(new_sum, 0.); - new_sum += sample * sample; - - av_assert2(new_sum >= 0.); - return sqrt(new_sum / s->window_duration); -} - -static void update_rms_double(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->data[0]; - double *wsamples = (double *)s->window->data[0]; - double sample = samples[frame->ch_layout.nb_channels * offset + ch]; - double *wsample = &wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - - s->sum -= *wsample; - s->sum = fmax(s->sum, 0.); - *wsample = sample * sample; - s->sum += *wsample; -} - -static double compute_rms_float(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->data[0]; - const float *wsamples = (const float *)s->window->data[0]; - float sample = samples[frame->ch_layout.nb_channels * offset + ch]; - float wsample = wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - float new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmaxf(new_sum, 0.f); - new_sum += sample * sample; - - av_assert2(new_sum >= 0.f); - return sqrtf(new_sum / s->window_duration); -} - -static void update_rms_float(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->data[0]; - float sample = samples[frame->ch_layout.nb_channels * offset + ch]; - float *wsamples = (float *)s->window->data[0]; - float *wsample = &wsamples[frame->ch_layout.nb_channels * s->window_offset + ch]; - - s->sum -= *wsample; - s->sum = fmaxf(s->sum, 0.f); - *wsample = sample * sample; - s->sum += *wsample; -} - -static double compute_peak_doublep(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->extended_data[ch]; - const double *wsamples = (const double *)s->window->extended_data[ch]; - double sample = samples[offset]; - double wsample = wsamples[s->window_offset]; - double new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmax(new_sum, 0.); - new_sum += fabs(sample); - - return new_sum / s->window_duration; -} - -static void update_peak_doublep(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->extended_data[ch]; - double *wsamples = (double *)s->window->extended_data[ch]; - double sample = samples[offset]; - double *wsample = &wsamples[s->window_offset]; - - s->sum -= *wsample; - s->sum = fmax(s->sum, 0.); - *wsample = fabs(sample); - s->sum += *wsample; -} - -static double compute_peak_floatp(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->extended_data[ch]; - const float *wsamples = (const float *)s->window->extended_data[ch]; - float sample = samples[offset]; - float wsample = wsamples[s->window_offset]; - float new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmaxf(new_sum, 0.f); - new_sum += fabsf(sample); - - return new_sum / s->window_duration; -} - -static void update_peak_floatp(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->extended_data[ch]; - float *wsamples = (float *)s->window->extended_data[ch]; - float sample = samples[offset]; - float *wsample = &wsamples[s->window_offset]; - - s->sum -= *wsample; - s->sum = fmaxf(s->sum, 0.f); - *wsample = fabsf(sample); - s->sum += *wsample; -} - -static double compute_rms_doublep(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->extended_data[ch]; - const double *wsamples = (const double *)s->window->extended_data[ch]; - double sample = samples[offset]; - double wsample = wsamples[s->window_offset]; - double new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmax(new_sum, 0.); - new_sum += sample * sample; - - av_assert2(new_sum >= 0.); - return sqrt(new_sum / s->window_duration); -} - -static void update_rms_doublep(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const double *samples = (const double *)frame->extended_data[ch]; - double *wsamples = (double *)s->window->extended_data[ch]; - double sample = samples[offset]; - double *wsample = &wsamples[s->window_offset]; - - s->sum -= *wsample; - s->sum = fmax(s->sum, 0.); - *wsample = sample * sample; - s->sum += *wsample; -} - -static double compute_rms_floatp(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->extended_data[ch]; - const float *wsamples = (const float *)s->window->extended_data[ch]; - float sample = samples[offset]; - float wsample = wsamples[s->window_offset]; - float new_sum; - - new_sum = s->sum; - new_sum -= wsample; - new_sum = fmaxf(new_sum, 0.f); - new_sum += sample * sample; - - av_assert2(new_sum >= 0.f); - return sqrtf(new_sum / s->window_duration); -} - -static void update_rms_floatp(SilenceRemoveContext *s, AVFrame *frame, int ch, int offset) -{ - const float *samples = (const float *)frame->extended_data[ch]; - float *wsamples = (float *)s->window->extended_data[ch]; - float sample = samples[offset]; - float *wsample = &wsamples[s->window_offset]; - - s->sum -= *wsample; - s->sum = fmaxf(s->sum, 0.f); - *wsample = sample * sample; - s->sum += *wsample; -} +#undef DEPTH +#define DEPTH 64 +#include "silenceremove_template.c" static av_cold int init(AVFilterContext *ctx) { @@ -420,13 +174,25 @@ static av_cold int init(AVFilterContext *ctx) return 0; } -static void clear_window(SilenceRemoveContext *s) +static void clear_windows(SilenceRemoveContext *s) { - av_samples_set_silence(s->window->extended_data, 0, s->window_duration, - s->window->ch_layout.nb_channels, s->window->format); - - s->window_offset = 0; - s->sum = 0; + av_samples_set_silence(s->start_window->extended_data, 0, + s->start_window->nb_samples, + s->start_window->ch_layout.nb_channels, + s->start_window->format); + av_samples_set_silence(s->stop_window->extended_data, 0, + s->stop_window->nb_samples, + s->stop_window->ch_layout.nb_channels, + s->stop_window->format); + + s->start_window_pos = 0; + s->start_window_size = 0; + s->stop_window_pos = 0; + s->stop_window_size = 0; + s->start_queue_pos = 0; + s->start_queue_size = 0; + s->stop_queue_pos = 0; + s->stop_queue_size = 0; } static int config_input(AVFilterLink *inlink) @@ -438,505 +204,262 @@ static int config_input(AVFilterLink *inlink) s->window_duration = av_rescale(s->window_duration_opt, inlink->sample_rate, AV_TIME_BASE); s->window_duration = FFMAX(1, s->window_duration); - s->window = ff_get_audio_buffer(ctx->outputs[0], s->window_duration); - if (!s->window) - return AVERROR(ENOMEM); - - clear_window(s); s->start_duration = av_rescale(s->start_duration_opt, inlink->sample_rate, AV_TIME_BASE); s->start_silence = av_rescale(s->start_silence_opt, inlink->sample_rate, AV_TIME_BASE); - s->stop_duration = av_rescale(s->stop_duration_opt, inlink->sample_rate, + s->stop_duration = av_rescale(s->stop_duration_opt, inlink->sample_rate, AV_TIME_BASE); - s->stop_silence = av_rescale(s->stop_silence_opt, inlink->sample_rate, + s->stop_silence = av_rescale(s->stop_silence_opt, inlink->sample_rate, AV_TIME_BASE); - s->start_holdoff = ff_get_audio_buffer(ctx->outputs[0], - FFMAX(s->start_duration, 1)); - if (!s->start_holdoff) - return AVERROR(ENOMEM); + s->start_found_periods = 0; + s->stop_found_periods = 0; - s->start_silence_hold = ff_get_audio_buffer(ctx->outputs[0], - FFMAX(s->start_silence, 1)); - if (!s->start_silence_hold) - return AVERROR(ENOMEM); + return 0; +} - s->start_holdoff_offset = 0; - s->start_holdoff_end = 0; - s->start_found_periods = 0; +static int config_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + SilenceRemoveContext *s = ctx->priv; - s->stop_holdoff = ff_get_audio_buffer(ctx->outputs[0], - FFMAX(s->stop_duration, 1)); - if (!s->stop_holdoff) + switch (s->detection) { + case D_AVG: + case D_RMS: + s->cache_size = 1; + break; + case D_DEV: + s->cache_size = 2; + break; + case D_MEDIAN: + case D_PEAK: + case D_PTP: + s->cache_size = s->window_duration; + break; + } + + s->start_window = ff_get_audio_buffer(outlink, s->window_duration); + s->stop_window = ff_get_audio_buffer(outlink, s->window_duration); + s->start_cache = av_calloc(outlink->ch_layout.nb_channels, s->cache_size * sizeof(*s->start_cache)); + s->stop_cache = av_calloc(outlink->ch_layout.nb_channels, s->cache_size * sizeof(*s->stop_cache)); + if (!s->start_window || !s->stop_window || !s->start_cache || !s->stop_cache) return AVERROR(ENOMEM); - s->stop_silence_hold = ff_get_audio_buffer(ctx->outputs[0], - FFMAX(s->stop_silence, 1)); - if (!s->stop_silence_hold) + s->start_queuef = ff_get_audio_buffer(outlink, s->start_silence + 1); + s->stop_queuef = ff_get_audio_buffer(outlink, s->stop_silence + 1); + if (!s->start_queuef || !s->stop_queuef) return AVERROR(ENOMEM); - s->stop_holdoff_offset = 0; - s->stop_holdoff_end = 0; - s->stop_found_periods = 0; + s->start_front = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->start_front)); + s->start_back = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->start_back)); + s->stop_front = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->stop_front)); + s->stop_back = av_calloc(outlink->ch_layout.nb_channels, sizeof(*s->stop_back)); + if (!s->start_front || !s->start_back || !s->stop_front || !s->stop_back) + return AVERROR(ENOMEM); - if (s->start_periods) { - s->mode = SILENCE_TRIM; - s->one_period = 1; - } else { - s->mode = SILENCE_COPY; - } + clear_windows(s); - switch (inlink->format) { - case AV_SAMPLE_FMT_DBL: - s->copy = copy_double; - switch (s->detection) { - case D_PEAK: - s->update = update_peak_double; - s->compute = compute_peak_double; - break; - case D_RMS: - s->update = update_rms_double; - s->compute = compute_rms_double; - break; - } + switch (s->detection) { + case D_AVG: + s->compute_flt = compute_avg_flt; + s->compute_dbl = compute_avg_dbl; break; - case AV_SAMPLE_FMT_FLT: - s->copy = copy_float; - switch (s->detection) { - case D_PEAK: - s->update = update_peak_float; - s->compute = compute_peak_float; - break; - case D_RMS: - s->update = update_rms_float; - s->compute = compute_rms_float; - break; - } + case D_DEV: + s->compute_flt = compute_dev_flt; + s->compute_dbl = compute_dev_dbl; break; - case AV_SAMPLE_FMT_DBLP: - s->copy = copy_doublep; - switch (s->detection) { - case D_PEAK: - s->update = update_peak_doublep; - s->compute = compute_peak_doublep; - break; - case D_RMS: - s->update = update_rms_doublep; - s->compute = compute_rms_doublep; - break; - } + case D_PTP: + s->compute_flt = compute_ptp_flt; + s->compute_dbl = compute_ptp_dbl; break; - case AV_SAMPLE_FMT_FLTP: - s->copy = copy_floatp; - switch (s->detection) { - case D_PEAK: - s->update = update_peak_floatp; - s->compute = compute_peak_floatp; - break; - case D_RMS: - s->update = update_rms_floatp; - s->compute = compute_rms_floatp; - break; - } + case D_MEDIAN: + s->compute_flt = compute_median_flt; + s->compute_dbl = compute_median_dbl; + break; + case D_PEAK: + s->compute_flt = compute_peak_flt; + s->compute_dbl = compute_peak_dbl; + break; + case D_RMS: + s->compute_flt = compute_rms_flt; + s->compute_dbl = compute_rms_dbl; break; - default: - return AVERROR_BUG; } - s->fifo = av_audio_fifo_alloc(inlink->format, inlink->ch_layout.nb_channels, 1024); - if (!s->fifo) - return AVERROR(ENOMEM); - return 0; } -static void flush(SilenceRemoveContext *s, - AVFrame *out, AVFilterLink *outlink, - int *nb_samples_written, int flush_silence) +static int filter_frame(AVFilterLink *outlink, AVFrame *in) { - AVFrame *silence; - - if (*nb_samples_written) { - out->nb_samples = *nb_samples_written; - - av_audio_fifo_write(s->fifo, (void **)out->extended_data, out->nb_samples); - *nb_samples_written = 0; - } - - av_frame_free(&out); - - if (s->stop_silence_end <= 0 || !flush_silence) - return; - - silence = ff_get_audio_buffer(outlink, s->stop_silence_end); - if (!silence) - return; - - if (s->stop_silence_offset < s->stop_silence_end) { - av_samples_copy(silence->extended_data, s->stop_silence_hold->extended_data, 0, - s->stop_silence_offset, - s->stop_silence_end - s->stop_silence_offset, - outlink->ch_layout.nb_channels, outlink->format); - } - - if (s->stop_silence_offset > 0) { - av_samples_copy(silence->extended_data, s->stop_silence_hold->extended_data, - s->stop_silence_end - s->stop_silence_offset, - 0, s->stop_silence_offset, - outlink->ch_layout.nb_channels, outlink->format); - } - - s->stop_silence_offset = 0; - s->stop_silence_end = 0; - - av_audio_fifo_write(s->fifo, (void **)silence->extended_data, silence->nb_samples); - av_frame_free(&silence); -} - -static int filter_frame(AVFilterLink *inlink, AVFrame *in) -{ - AVFilterContext *ctx = inlink->dst; - AVFilterLink *outlink = ctx->outputs[0]; + const int nb_channels = outlink->ch_layout.nb_channels; + AVFilterContext *ctx = outlink->src; SilenceRemoveContext *s = ctx->priv; - int nbs, nb_samples_read, nb_samples_written; - int i, j, threshold, ret = 0; + int max_out_nb_samples; + int out_nb_samples = 0; + int in_nb_samples; + const double *srcd; + const float *srcf; AVFrame *out; - - nb_samples_read = nb_samples_written = 0; + double *dstd; + float *dstf; if (s->next_pts == AV_NOPTS_VALUE) s->next_pts = in->pts; - switch (s->mode) { - case SILENCE_TRIM: -silence_trim: - nbs = in->nb_samples - nb_samples_read; - if (!nbs) - break; - - for (i = 0; i < nbs; i++) { - if (s->start_mode == T_ANY) { - threshold = 0; - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - threshold |= s->compute(s, in, j, nb_samples_read) > s->start_threshold; - } - } else { - threshold = 1; - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - threshold &= s->compute(s, in, j, nb_samples_read) > s->start_threshold; - } - } - - if (threshold) { - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - s->update(s, in, j, nb_samples_read); - s->copy(s, s->start_holdoff, in, j, s->start_holdoff_end, nb_samples_read); - } + in_nb_samples = in->nb_samples; + max_out_nb_samples = in->nb_samples + + s->start_silence + + s->stop_silence; + if (max_out_nb_samples <= 0) { + av_frame_free(&in); + ff_filter_set_ready(ctx, 100); + return 0; + } - s->window_offset++; - if (s->window_offset >= s->window_duration) - s->window_offset = 0; - s->start_holdoff_end++; - nb_samples_read++; - - if (s->start_holdoff_end >= s->start_duration) { - s->start_found_periods += s->one_period >= 1; - s->one_period = 0; - if (s->start_found_periods >= s->start_periods) { - s->mode = SILENCE_TRIM_FLUSH; - goto silence_trim_flush; - } - - s->start_holdoff_offset = 0; - s->start_holdoff_end = 0; - s->start_silence_offset = 0; - s->start_silence_end = 0; - } - } else { - s->start_holdoff_end = 0; - s->one_period++; - - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - s->update(s, in, j, nb_samples_read); - if (s->start_silence) - s->copy(s, s->start_silence_hold, in, j, s->start_silence_offset, nb_samples_read); - } + out = ff_get_audio_buffer(outlink, max_out_nb_samples); + if (!out) { + av_frame_free(&in); + return AVERROR(ENOMEM); + } - s->window_offset++; - if (s->window_offset >= s->window_duration) - s->window_offset = 0; - nb_samples_read++; - s->start_silence_offset++; + if (s->timestamp_mode == TS_WRITE) + out->pts = s->next_pts; + else + out->pts = in->pts; - if (s->start_silence) { - s->start_silence_end = FFMIN(s->start_silence_end + 1, s->start_silence); - if (s->start_silence_offset >= s->start_silence) - s->start_silence_offset = 0; + switch (outlink->format) { + case AV_SAMPLE_FMT_FLT: + srcf = (const float *)in->data[0]; + dstf = (float *)out->data[0]; + if (s->start_periods > 0 && s->stop_periods > 0) { + const float *src = srcf; + if (s->start_found_periods >= 0) { + for (int n = 0; n < in_nb_samples; n++) { + filter_start_flt(ctx, src + n * nb_channels, + dstf, &out_nb_samples, + nb_channels); } + in_nb_samples = out_nb_samples; + out_nb_samples = 0; + src = dstf; } - } - break; - - case SILENCE_TRIM_FLUSH: -silence_trim_flush: - nbs = s->start_holdoff_end - s->start_holdoff_offset; - if (!nbs) - break; - - out = ff_get_audio_buffer(outlink, nbs + s->start_silence_end); - if (!out) { - av_frame_free(&in); - return AVERROR(ENOMEM); - } - - if (s->start_silence_end > 0) { - if (s->start_silence_offset < s->start_silence_end) { - av_samples_copy(out->extended_data, s->start_silence_hold->extended_data, 0, - s->start_silence_offset, - s->start_silence_end - s->start_silence_offset, - outlink->ch_layout.nb_channels, outlink->format); + for (int n = 0; n < in_nb_samples; n++) { + filter_stop_flt(ctx, src + n * nb_channels, + dstf, &out_nb_samples, + nb_channels); } - - if (s->start_silence_offset > 0) { - av_samples_copy(out->extended_data, s->start_silence_hold->extended_data, - s->start_silence_end - s->start_silence_offset, - 0, s->start_silence_offset, - outlink->ch_layout.nb_channels, outlink->format); + } else if (s->start_periods > 0) { + for (int n = 0; n < in_nb_samples; n++) { + filter_start_flt(ctx, srcf + n * nb_channels, + dstf, &out_nb_samples, + nb_channels); + } + } else if (s->stop_periods > 0) { + for (int n = 0; n < in_nb_samples; n++) { + filter_stop_flt(ctx, srcf + n * nb_channels, + dstf, &out_nb_samples, + nb_channels); } - } - - av_samples_copy(out->extended_data, s->start_holdoff->extended_data, - s->start_silence_end, - s->start_holdoff_offset, nbs, - outlink->ch_layout.nb_channels, outlink->format); - - s->start_holdoff_offset += nbs; - - av_audio_fifo_write(s->fifo, (void **)out->extended_data, out->nb_samples); - av_frame_free(&out); - - if (s->start_holdoff_offset == s->start_holdoff_end) { - s->start_holdoff_offset = 0; - s->start_holdoff_end = 0; - s->start_silence_offset = 0; - s->start_silence_end = 0; - s->mode = SILENCE_COPY; - goto silence_copy; } break; - - case SILENCE_COPY: -silence_copy: - nbs = in->nb_samples - nb_samples_read; - if (!nbs) - break; - - out = ff_get_audio_buffer(outlink, nbs); - if (!out) { - av_frame_free(&in); - return AVERROR(ENOMEM); - } - - if (s->stop_periods) { - for (i = 0; i < nbs; i++) { - if (s->stop_mode == T_ANY) { - threshold = 0; - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - threshold |= s->compute(s, in, j, nb_samples_read) > s->stop_threshold; - } - } else { - threshold = 1; - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - threshold &= s->compute(s, in, j, nb_samples_read) > s->stop_threshold; - } - } - - if (threshold && s->stop_holdoff_end && !s->stop_silence) { - s->mode = SILENCE_COPY_FLUSH; - flush(s, out, outlink, &nb_samples_written, 0); - s->one_period++; - goto silence_copy_flush; - } else if (threshold) { - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - s->update(s, in, j, nb_samples_read); - s->copy(s, out, in, j, nb_samples_written, nb_samples_read); - } - - s->window_offset++; - if (s->window_offset >= s->window_duration) - s->window_offset = 0; - nb_samples_read++; - nb_samples_written++; - s->one_period++; - } else if (!threshold) { - for (j = 0; j < outlink->ch_layout.nb_channels; j++) { - s->update(s, in, j, nb_samples_read); - if (s->stop_silence) - s->copy(s, s->stop_silence_hold, in, j, s->stop_silence_offset, nb_samples_read); - - s->copy(s, s->stop_holdoff, in, j, s->stop_holdoff_end, nb_samples_read); - } - - if (s->stop_silence) { - s->stop_silence_offset++; - s->stop_silence_end = FFMIN(s->stop_silence_end + 1, s->stop_silence); - if (s->stop_silence_offset >= s->stop_silence) { - s->stop_silence_offset = 0; - } - } - - s->window_offset++; - if (s->window_offset >= s->window_duration) - s->window_offset = 0; - nb_samples_read++; - s->stop_holdoff_end++; - - if (s->stop_holdoff_end >= s->stop_duration) { - s->stop_found_periods += s->one_period >= 1; - s->one_period = 0; - if (s->stop_found_periods >= s->stop_periods) { - s->stop_holdoff_offset = 0; - s->stop_holdoff_end = 0; - - if (!s->restart) { - s->mode = SILENCE_STOP; - flush(s, out, outlink, &nb_samples_written, 1); - goto silence_stop; - } else { - s->stop_found_periods = 0; - s->start_found_periods = 0; - s->start_holdoff_offset = 0; - s->start_holdoff_end = 0; - s->start_silence_offset = 0; - s->start_silence_end = 0; - clear_window(s); - s->mode = SILENCE_TRIM; - flush(s, out, outlink, &nb_samples_written, 1); - goto silence_trim; - } - } - s->mode = SILENCE_COPY_FLUSH; - flush(s, out, outlink, &nb_samples_written, 0); - goto silence_copy_flush; - } + case AV_SAMPLE_FMT_DBL: + srcd = (const double *)in->data[0]; + dstd = (double *)out->data[0]; + if (s->start_periods > 0 && s->stop_periods > 0) { + const double *src = srcd; + if (s->start_found_periods >= 0) { + for (int n = 0; n < in_nb_samples; n++) { + filter_start_dbl(ctx, src + n * nb_channels, + dstd, &out_nb_samples, + nb_channels); } + in_nb_samples = out_nb_samples; + out_nb_samples = 0; + src = dstd; + } + for (int n = 0; n < in_nb_samples; n++) { + filter_stop_dbl(ctx, src + n * nb_channels, + dstd, &out_nb_samples, + nb_channels); + } + } else if (s->start_periods > 0) { + for (int n = 0; n < in_nb_samples; n++) { + filter_start_dbl(ctx, srcd + n * nb_channels, + dstd, &out_nb_samples, + nb_channels); + } + } else if (s->stop_periods > 0) { + for (int n = 0; n < in_nb_samples; n++) { + filter_stop_dbl(ctx, srcd + n * nb_channels, + dstd, &out_nb_samples, + nb_channels); } - s->one_period++; - flush(s, out, outlink, &nb_samples_written, 0); - } else { - av_samples_copy(out->extended_data, in->extended_data, - nb_samples_written, - nb_samples_read, nbs, - outlink->ch_layout.nb_channels, outlink->format); - - av_audio_fifo_write(s->fifo, (void **)out->extended_data, out->nb_samples); - av_frame_free(&out); - } - break; - - case SILENCE_COPY_FLUSH: -silence_copy_flush: - nbs = s->stop_holdoff_end - s->stop_holdoff_offset; - if (!nbs) - break; - - out = ff_get_audio_buffer(outlink, nbs); - if (!out) { - av_frame_free(&in); - return AVERROR(ENOMEM); - } - - av_samples_copy(out->extended_data, s->stop_holdoff->extended_data, 0, - s->stop_holdoff_offset, nbs, - outlink->ch_layout.nb_channels, outlink->format); - - s->stop_holdoff_offset += nbs; - - av_audio_fifo_write(s->fifo, (void **)out->extended_data, out->nb_samples); - av_frame_free(&out); - - if (s->stop_holdoff_offset == s->stop_holdoff_end) { - s->stop_holdoff_offset = 0; - s->stop_holdoff_end = 0; - s->stop_silence_offset = 0; - s->stop_silence_end = 0; - s->mode = SILENCE_COPY; - goto silence_copy; } break; - case SILENCE_STOP: -silence_stop: - break; - default: - ret = AVERROR_BUG; } av_frame_free(&in); - - if (av_audio_fifo_size(s->fifo) > 0) { - out = ff_get_audio_buffer(outlink, av_audio_fifo_size(s->fifo)); - if (!out) - return AVERROR(ENOMEM); - - av_audio_fifo_read(s->fifo, (void **)out->extended_data, out->nb_samples); - out->pts = s->next_pts; - s->next_pts += av_rescale_q(out->nb_samples, - (AVRational){1, outlink->sample_rate}, - outlink->time_base); - - ret = ff_filter_frame(outlink, out); + if (out_nb_samples > 0) { + s->next_pts += out_nb_samples; + out->nb_samples = out_nb_samples; + return ff_filter_frame(outlink, out); } - return ret; + av_frame_free(&out); + ff_filter_set_ready(ctx, 100); + + return 0; } -static int request_frame(AVFilterLink *outlink) +static int activate(AVFilterContext *ctx) { - AVFilterContext *ctx = outlink->src; + AVFilterLink *outlink = ctx->outputs[0]; + AVFilterLink *inlink = ctx->inputs[0]; SilenceRemoveContext *s = ctx->priv; + AVFrame *in; int ret; - ret = ff_request_frame(ctx->inputs[0]); - if (ret == AVERROR_EOF && (s->mode == SILENCE_COPY_FLUSH || - s->mode == SILENCE_COPY)) { - int nbs = s->stop_holdoff_end - s->stop_holdoff_offset; - if (nbs) { - AVFrame *frame; - - frame = ff_get_audio_buffer(outlink, nbs); - if (!frame) - return AVERROR(ENOMEM); - - av_samples_copy(frame->extended_data, s->stop_holdoff->extended_data, 0, - s->stop_holdoff_offset, nbs, - outlink->ch_layout.nb_channels, outlink->format); - - frame->pts = s->next_pts; - s->next_pts += av_rescale_q(frame->nb_samples, - (AVRational){1, outlink->sample_rate}, - outlink->time_base); - - ret = ff_filter_frame(outlink, frame); + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + ret = ff_inlink_consume_frame(inlink, &in); + if (ret < 0) + return ret; + if (ret > 0) { + if (s->start_periods == 1 && s->stop_periods == 0 && + s->start_found_periods < 0) { + if (s->timestamp_mode == TS_WRITE) + in->pts = s->next_pts; + s->next_pts += in->nb_samples; + return ff_filter_frame(outlink, in); } - s->mode = SILENCE_STOP; + if (s->start_periods == 0 && s->stop_periods == 0) + return ff_filter_frame(outlink, in); + return filter_frame(outlink, in); } - return ret; + + FF_FILTER_FORWARD_STATUS(inlink, outlink); + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; } static av_cold void uninit(AVFilterContext *ctx) { SilenceRemoveContext *s = ctx->priv; - av_frame_free(&s->start_holdoff); - av_frame_free(&s->start_silence_hold); - av_frame_free(&s->stop_holdoff); - av_frame_free(&s->stop_silence_hold); - av_frame_free(&s->window); - - av_audio_fifo_free(s->fifo); - s->fifo = NULL; + av_frame_free(&s->start_window); + av_frame_free(&s->stop_window); + av_frame_free(&s->start_queuef); + av_frame_free(&s->stop_queuef); + + av_freep(&s->start_cache); + av_freep(&s->stop_cache); + av_freep(&s->start_front); + av_freep(&s->start_back); + av_freep(&s->stop_front); + av_freep(&s->stop_back); } static const AVFilterPad silenceremove_inputs[] = { @@ -944,15 +467,14 @@ static const AVFilterPad silenceremove_inputs[] = { .name = "default", .type = AVMEDIA_TYPE_AUDIO, .config_props = config_input, - .filter_frame = filter_frame, }, }; static const AVFilterPad silenceremove_outputs[] = { { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .request_frame = request_frame, + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_output, }, }; @@ -962,9 +484,12 @@ const AVFilter ff_af_silenceremove = { .priv_size = sizeof(SilenceRemoveContext), .priv_class = &silenceremove_class, .init = init, + .activate = activate, .uninit = uninit, FILTER_INPUTS(silenceremove_inputs), FILTER_OUTPUTS(silenceremove_outputs), - FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLTP, - AV_SAMPLE_FMT_DBL, AV_SAMPLE_FMT_DBLP), + FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLT, + AV_SAMPLE_FMT_DBL), + .process_command = ff_filter_process_command, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, }; diff --git a/libavfilter/af_sofalizer.c b/libavfilter/af_sofalizer.c index be947314f3a..7349fa06f5b 100644 --- a/libavfilter/af_sofalizer.c +++ b/libavfilter/af_sofalizer.c @@ -36,6 +36,7 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "audio.h" @@ -597,7 +598,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) } else if (s->type == FREQUENCY_DOMAIN) { ff_filter_execute(ctx, sofalizer_fast_convolute, &td, NULL, 2); } - emms_c(); /* display error message if clipping occurred */ if (n_clippings[0] + n_clippings[1] > 0) { @@ -1062,9 +1062,9 @@ static const AVOption sofalizer_options[] = { { "rotation", "set rotation" , OFFSET(rotation), AV_OPT_TYPE_FLOAT, {.dbl=0}, -360, 360, .flags = FLAGS }, { "elevation", "set elevation", OFFSET(elevation), AV_OPT_TYPE_FLOAT, {.dbl=0}, -90, 90, .flags = FLAGS }, { "radius", "set radius", OFFSET(radius), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 5, .flags = FLAGS }, - { "type", "set processing", OFFSET(type), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, .flags = FLAGS, "type" }, - { "time", "time domain", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = FLAGS, "type" }, - { "freq", "frequency domain", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = FLAGS, "type" }, + { "type", "set processing", OFFSET(type), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, .flags = FLAGS, .unit = "type" }, + { "time", "time domain", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = FLAGS, .unit = "type" }, + { "freq", "frequency domain", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = FLAGS, .unit = "type" }, { "speakers", "set speaker custom positions", OFFSET(speakers_pos), AV_OPT_TYPE_STRING, {.str=0}, 0, 0, .flags = FLAGS }, { "lfegain", "set lfe gain", OFFSET(lfe_gain), AV_OPT_TYPE_FLOAT, {.dbl=0}, -20,40, .flags = FLAGS }, { "framesize", "set frame size", OFFSET(framesize), AV_OPT_TYPE_INT, {.i64=1024},1024,96000, .flags = FLAGS }, @@ -1086,13 +1086,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_sofalizer = { .name = "sofalizer", .description = NULL_IF_CONFIG_SMALL("SOFAlizer (Spatially Oriented Format for Acoustics)."), @@ -1102,7 +1095,7 @@ const AVFilter ff_af_sofalizer = { .activate = activate, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/af_speechnorm.c b/libavfilter/af_speechnorm.c index 4f56ad9a8f0..a8bed2cb8f4 100644 --- a/libavfilter/af_speechnorm.c +++ b/libavfilter/af_speechnorm.c @@ -590,13 +590,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_speechnorm = { .name = "speechnorm", .description = NULL_IF_CONFIG_SMALL("Speech Normalizer."), @@ -605,7 +598,7 @@ const AVFilter ff_af_speechnorm = { .activate = activate, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = process_command, diff --git a/libavfilter/af_stereotools.c b/libavfilter/af_stereotools.c index eedc7c68bd0..330d91cd2bb 100644 --- a/libavfilter/af_stereotools.c +++ b/libavfilter/af_stereotools.c @@ -69,18 +69,18 @@ static const AVOption stereotools_options[] = { { "muter", "mute R", OFFSET(mute_r), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, A }, { "phasel", "phase L", OFFSET(phase_l), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, A }, { "phaser", "phase R", OFFSET(phase_r), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, A }, - { "mode", "set stereo mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 10, A, "mode" }, - { "lr>lr", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, "mode" }, - { "lr>ms", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, "mode" }, - { "ms>lr", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, A, "mode" }, - { "lr>ll", 0, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, A, "mode" }, - { "lr>rr", 0, 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, A, "mode" }, - { "lr>l+r", 0, 0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, A, "mode" }, - { "lr>rl", 0, 0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, A, "mode" }, - { "ms>ll", 0, 0, AV_OPT_TYPE_CONST, {.i64=7}, 0, 0, A, "mode" }, - { "ms>rr", 0, 0, AV_OPT_TYPE_CONST, {.i64=8}, 0, 0, A, "mode" }, - { "ms>rl", 0, 0, AV_OPT_TYPE_CONST, {.i64=9}, 0, 0, A, "mode" }, - { "lr>l-r", 0, 0, AV_OPT_TYPE_CONST, {.i64=10}, 0, 0, A, "mode" }, + { "mode", "set stereo mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 10, A, .unit = "mode" }, + { "lr>lr", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, .unit = "mode" }, + { "lr>ms", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, .unit = "mode" }, + { "ms>lr", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, A, .unit = "mode" }, + { "lr>ll", 0, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, A, .unit = "mode" }, + { "lr>rr", 0, 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, A, .unit = "mode" }, + { "lr>l+r", 0, 0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, A, .unit = "mode" }, + { "lr>rl", 0, 0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, A, .unit = "mode" }, + { "ms>ll", 0, 0, AV_OPT_TYPE_CONST, {.i64=7}, 0, 0, A, .unit = "mode" }, + { "ms>rr", 0, 0, AV_OPT_TYPE_CONST, {.i64=8}, 0, 0, A, .unit = "mode" }, + { "ms>rl", 0, 0, AV_OPT_TYPE_CONST, {.i64=9}, 0, 0, A, .unit = "mode" }, + { "lr>l-r", 0, 0, AV_OPT_TYPE_CONST, {.i64=10}, 0, 0, A, .unit = "mode" }, { "slev", "set side level", OFFSET(slev), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A }, { "sbal", "set side balance", OFFSET(sbal), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -1, 1, A }, { "mlev", "set middle level", OFFSET(mlev), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.015625, 64, A }, @@ -89,11 +89,11 @@ static const AVOption stereotools_options[] = { { "delay", "set delay", OFFSET(delay), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -20, 20, A }, { "sclevel", "set S/C level", OFFSET(sc_level), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 1, 100, A }, { "phase", "set stereo phase", OFFSET(phase), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 360, A }, - { "bmode_in", "set balance in mode", OFFSET(bmode_in), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, A, "bmode" }, - { "balance", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, "bmode" }, - { "amplitude", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, "bmode" }, - { "power", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, A, "bmode" }, - { "bmode_out", "set balance out mode", OFFSET(bmode_out), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, A, "bmode" }, + { "bmode_in", "set balance in mode", OFFSET(bmode_in), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, A, .unit = "bmode" }, + { "balance", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, A, .unit = "bmode" }, + { "amplitude", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, A, .unit = "bmode" }, + { "power", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, A, .unit = "bmode" }, + { "bmode_out", "set balance out mode", OFFSET(bmode_out), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, A, .unit = "bmode" }, { NULL } }; @@ -119,7 +119,7 @@ static int config_input(AVFilterLink *inlink) AVFilterContext *ctx = inlink->dst; StereoToolsContext *s = ctx->priv; - s->length = FFALIGN(inlink->sample_rate / 10, 2); + s->length = FFALIGN((inlink->sample_rate + 9) / 10, 2); if (!s->buffer) s->buffer = av_calloc(s->length, sizeof(*s->buffer)); if (!s->buffer) @@ -365,13 +365,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_stereotools = { .name = "stereotools", .description = NULL_IF_CONFIG_SMALL("Apply various stereo tools."), @@ -379,7 +372,7 @@ const AVFilter ff_af_stereotools = { .priv_class = &stereotools_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/af_stereowiden.c b/libavfilter/af_stereowiden.c index a91ea039577..1273c77116e 100644 --- a/libavfilter/af_stereowiden.c +++ b/libavfilter/af_stereowiden.c @@ -72,8 +72,10 @@ static int config_input(AVFilterLink *inlink) AVFilterContext *ctx = inlink->dst; StereoWidenContext *s = ctx->priv; - s->length = s->delay * inlink->sample_rate / 1000; + s->length = lrintf(s->delay * inlink->sample_rate / 1000); s->length *= 2; + if (s->length == 0) + return AVERROR(EINVAL); s->buffer = av_calloc(s->length, sizeof(*s->buffer)); if (!s->buffer) return AVERROR(ENOMEM); @@ -146,13 +148,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_stereowiden = { .name = "stereowiden", .description = NULL_IF_CONFIG_SMALL("Apply stereo widening effect."), @@ -160,7 +155,7 @@ const AVFilter ff_af_stereowiden = { .priv_class = &stereowiden_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_surround.c b/libavfilter/af_surround.c index 925cb6e66c8..59578602837 100644 --- a/libavfilter/af_surround.c +++ b/libavfilter/af_surround.c @@ -61,8 +61,8 @@ static const int sc_map[16] = { typedef struct AudioSurroundContext { const AVClass *class; - char *out_channel_layout_str; - char *in_channel_layout_str; + AVChannelLayout out_ch_layout; + AVChannelLayout in_ch_layout; float level_in; float level_out; @@ -74,6 +74,7 @@ typedef struct AudioSurroundContext { float focus; int win_size; int win_func; + float win_gain; float overlap; float all_x; @@ -92,8 +93,6 @@ typedef struct AudioSurroundContext { float lowcut; float highcut; - AVChannelLayout out_ch_layout; - AVChannelLayout in_ch_layout; int nb_in_channels; int nb_out_channels; @@ -330,16 +329,16 @@ static void angle_transform(float *x, float *y, float angle) if (angle == 90.f) return; - reference = angle * M_PI / 180.f; + reference = angle * M_PIf / 180.f; r = hypotf(*x, *y); a = atan2f(*x, *y); r /= r_distance(a); - if (fabsf(a) <= M_PI_4) - a *= reference / M_PI_2; + if (fabsf(a) <= M_PI_4f) + a *= reference / M_PI_2f; else - a = M_PI + (-2.f * M_PI + reference) * (M_PI - fabsf(a)) * FFDIFFSIGN(a, 0.f) / (3.f * M_PI_2); + a = M_PIf + (-2.f * M_PIf + reference) * (M_PIf - fabsf(a)) * FFDIFFSIGN(a, 0.f) / (3.f * M_PI_2f); r *= r_distance(a); @@ -366,16 +365,16 @@ static void focus_transform(float *x, float *y, float focus) static void stereo_position(float a, float p, float *x, float *y) { av_assert2(a >= -1.f && a <= 1.f); - av_assert2(p >= 0.f && p <= M_PI); - *x = av_clipf(a+a*fmaxf(0.f, p*p-M_PI_2), -1.f, 1.f); - *y = av_clipf(cosf(a*M_PI_2+M_PI)*cosf(M_PI_2-p/M_PI)*M_LN10+1.f, -1.f, 1.f); + av_assert2(p >= 0.f && p <= M_PIf); + *x = av_clipf(a+a*fmaxf(0.f, p*p-M_PI_2f), -1.f, 1.f); + *y = av_clipf(cosf(a*M_PI_2f+M_PIf)*cosf(M_PI_2f-p/M_PIf)*M_LN10f+1.f, -1.f, 1.f); } static inline void get_lfe(int output_lfe, int n, float lowcut, float highcut, float *lfe_mag, float c_mag, float *mag_total, int lfe_mode) { if (output_lfe && n < highcut) { - *lfe_mag = n < lowcut ? 1.f : .5f*(1.f+cosf(M_PI*(lowcut-n)/(lowcut-highcut))); + *lfe_mag = n < lowcut ? 1.f : .5f*(1.f+cosf(M_PIf*(lowcut-n)/(lowcut-highcut))); *lfe_mag *= c_mag; if (lfe_mode) *mag_total -= *lfe_mag; @@ -413,7 +412,7 @@ static void calculate_factors(AVFilterContext *ctx, int ch, int chan) break; case AV_CHAN_LOW_FREQUENCY: for (int n = 0; n < rdft_size; n++) - factor[n] = powf(1.f - fabsf(x[n]), f_x) * powf((1.f - fabs(y[n])), f_y); + factor[n] = powf(1.f - fabsf(x[n]), f_x) * powf((1.f - fabsf(y[n])), f_y); break; case AV_CHAN_BACK_CENTER: for (int n = 0; n < rdft_size; n++) @@ -637,7 +636,7 @@ static void upmix_7_1_5_0_side(AVFilterContext *ctx, { float fl_mag, fr_mag, ls_mag, rs_mag, lb_mag, rb_mag; float *dstc, *dstl, *dstr, *dstls, *dstrs, *dstlb, *dstrb, *dstlfe; - float lfe_mag, c_phase, mag_total = (mag_totall + mag_totalr) * 0.5; + float lfe_mag, c_phase, mag_total = (mag_totall + mag_totalr) * 0.5f; AudioSurroundContext *s = ctx->priv; dstl = (float *)s->output->extended_data[0]; @@ -747,6 +746,7 @@ static void filter_stereo(AVFilterContext *ctx) const float *srcl = (const float *)s->input->extended_data[0]; const float *srcr = (const float *)s->input->extended_data[1]; const int output_lfe = s->output_lfe && s->create_lfe; + const int rdft_size = s->rdft_size; const int lfe_mode = s->lfe_mode; const float highcut = s->highcut; const float lowcut = s->lowcut; @@ -761,7 +761,7 @@ static void filter_stereo(AVFilterContext *ctx) float *xpos = s->x_pos; float *ypos = s->y_pos; - for (int n = 0; n < s->rdft_size; n++) { + for (int n = 0; n < rdft_size; n++) { float l_re = srcl[2 * n], r_re = srcr[2 * n]; float l_im = srcl[2 * n + 1], r_im = srcr[2 * n + 1]; float c_phase = atan2f(l_im + r_im, l_re + r_re); @@ -777,8 +777,8 @@ static void filter_stereo(AVFilterContext *ctx) mag_sum = mag_sum < MIN_MAG_SUM ? 1.f : mag_sum; mag_dif = (l_mag - r_mag) / mag_sum; - if (phase_dif > M_PI) - phase_dif = 2.f * M_PI - phase_dif; + if (phase_dif > M_PIf) + phase_dif = 2.f * M_PIf - phase_dif; stereo_position(mag_dif, phase_dif, &x, &y); angle_transform(&x, &y, angle); @@ -801,6 +801,7 @@ static void filter_2_1(AVFilterContext *ctx) const float *srcl = (const float *)s->input->extended_data[0]; const float *srcr = (const float *)s->input->extended_data[1]; const float *srclfe = (const float *)s->input->extended_data[2]; + const int rdft_size = s->rdft_size; const float angle = s->angle; const float focus = s->focus; float *magtotal = s->mag_total; @@ -813,7 +814,7 @@ static void filter_2_1(AVFilterContext *ctx) float *xpos = s->x_pos; float *ypos = s->y_pos; - for (int n = 0; n < s->rdft_size; n++) { + for (int n = 0; n < rdft_size; n++) { float l_re = srcl[2 * n], r_re = srcr[2 * n]; float l_im = srcl[2 * n + 1], r_im = srcr[2 * n + 1]; float lfe_re = srclfe[2 * n], lfe_im = srclfe[2 * n + 1]; @@ -832,8 +833,8 @@ static void filter_2_1(AVFilterContext *ctx) mag_sum = mag_sum < MIN_MAG_SUM ? 1.f : mag_sum; mag_dif = (l_mag - r_mag) / mag_sum; - if (phase_dif > M_PI) - phase_dif = 2.f * M_PI - phase_dif; + if (phase_dif > M_PIf) + phase_dif = 2.f * M_PIf - phase_dif; stereo_position(mag_dif, phase_dif, &x, &y); angle_transform(&x, &y, angle); @@ -858,6 +859,7 @@ static void filter_surround(AVFilterContext *ctx) const float *srcr = (const float *)s->input->extended_data[1]; const float *srcc = (const float *)s->input->extended_data[2]; const int output_lfe = s->output_lfe && s->create_lfe; + const int rdft_size = s->rdft_size; const int lfe_mode = s->lfe_mode; const float highcut = s->highcut; const float lowcut = s->lowcut; @@ -872,7 +874,7 @@ static void filter_surround(AVFilterContext *ctx) float *xpos = s->x_pos; float *ypos = s->y_pos; - for (int n = 0; n < s->rdft_size; n++) { + for (int n = 0; n < rdft_size; n++) { float l_re = srcl[2 * n], r_re = srcr[2 * n]; float l_im = srcl[2 * n + 1], r_im = srcr[2 * n + 1]; float c_re = srcc[2 * n], c_im = srcc[2 * n + 1]; @@ -889,8 +891,8 @@ static void filter_surround(AVFilterContext *ctx) mag_sum = mag_sum < MIN_MAG_SUM ? 1.f : mag_sum; mag_dif = (l_mag - r_mag) / mag_sum; - if (phase_dif > M_PI) - phase_dif = 2.f * M_PI - phase_dif; + if (phase_dif > M_PIf) + phase_dif = 2.f * M_PIf - phase_dif; stereo_position(mag_dif, phase_dif, &x, &y); angle_transform(&x, &y, angle); @@ -910,6 +912,7 @@ static void filter_surround(AVFilterContext *ctx) static void filter_5_0_side(AVFilterContext *ctx) { AudioSurroundContext *s = ctx->priv; + const int rdft_size = s->rdft_size; float *srcl, *srcr, *srcc, *srcsl, *srcsr; int n; @@ -919,7 +922,7 @@ static void filter_5_0_side(AVFilterContext *ctx) srcsl = (float *)s->input->extended_data[3]; srcsr = (float *)s->input->extended_data[4]; - for (n = 0; n < s->rdft_size; n++) { + for (n = 0; n < rdft_size; n++) { float fl_re = srcl[2 * n], fr_re = srcr[2 * n]; float fl_im = srcl[2 * n + 1], fr_im = srcr[2 * n + 1]; float c_re = srcc[2 * n], c_im = srcc[2 * n + 1]; @@ -946,11 +949,11 @@ static void filter_5_0_side(AVFilterContext *ctx) float xl, yl; float xr, yr; - if (phase_difl > M_PI) - phase_difl = 2.f * M_PI - phase_difl; + if (phase_difl > M_PIf) + phase_difl = 2.f * M_PIf - phase_difl; - if (phase_difr > M_PI) - phase_difr = 2.f * M_PI - phase_difr; + if (phase_difr > M_PIf) + phase_difr = 2.f * M_PIf - phase_difr; stereo_position(mag_difl, phase_difl, &xl, &yl); stereo_position(mag_difr, phase_difr, &xr, &yr); @@ -967,6 +970,7 @@ static void filter_5_0_side(AVFilterContext *ctx) static void filter_5_1_side(AVFilterContext *ctx) { AudioSurroundContext *s = ctx->priv; + const int rdft_size = s->rdft_size; float *srcl, *srcr, *srcc, *srclfe, *srcsl, *srcsr; int n; @@ -977,7 +981,7 @@ static void filter_5_1_side(AVFilterContext *ctx) srcsl = (float *)s->input->extended_data[4]; srcsr = (float *)s->input->extended_data[5]; - for (n = 0; n < s->rdft_size; n++) { + for (n = 0; n < rdft_size; n++) { float fl_re = srcl[2 * n], fr_re = srcr[2 * n]; float fl_im = srcl[2 * n + 1], fr_im = srcr[2 * n + 1]; float c_re = srcc[2 * n], c_im = srcc[2 * n + 1]; @@ -1005,11 +1009,11 @@ static void filter_5_1_side(AVFilterContext *ctx) float xl, yl; float xr, yr; - if (phase_difl > M_PI) - phase_difl = 2.f * M_PI - phase_difl; + if (phase_difl > M_PIf) + phase_difl = 2.f * M_PIf - phase_difl; - if (phase_difr > M_PI) - phase_difr = 2.f * M_PI - phase_difr; + if (phase_difr > M_PIf) + phase_difr = 2.f * M_PIf - phase_difr; stereo_position(mag_difl, phase_difl, &xl, &yl); stereo_position(mag_difr, phase_difr, &xr, &yr); @@ -1026,6 +1030,7 @@ static void filter_5_1_side(AVFilterContext *ctx) static void filter_5_1_back(AVFilterContext *ctx) { AudioSurroundContext *s = ctx->priv; + const int rdft_size = s->rdft_size; float *srcl, *srcr, *srcc, *srclfe, *srcbl, *srcbr; int n; @@ -1036,7 +1041,7 @@ static void filter_5_1_back(AVFilterContext *ctx) srcbl = (float *)s->input->extended_data[4]; srcbr = (float *)s->input->extended_data[5]; - for (n = 0; n < s->rdft_size; n++) { + for (n = 0; n < rdft_size; n++) { float fl_re = srcl[2 * n], fr_re = srcr[2 * n]; float fl_im = srcl[2 * n + 1], fr_im = srcr[2 * n + 1]; float c_re = srcc[2 * n], c_im = srcc[2 * n + 1]; @@ -1064,11 +1069,11 @@ static void filter_5_1_back(AVFilterContext *ctx) float xl, yl; float xr, yr; - if (phase_difl > M_PI) - phase_difl = 2.f * M_PI - phase_difl; + if (phase_difl > M_PIf) + phase_difl = 2.f * M_PIf - phase_difl; - if (phase_difr > M_PI) - phase_difr = 2.f * M_PI - phase_difr; + if (phase_difr > M_PIf) + phase_difr = 2.f * M_PIf - phase_difr; stereo_position(mag_difl, phase_difl, &xl, &yl); stereo_position(mag_difr, phase_difr, &xr, &yr); @@ -1100,20 +1105,8 @@ static av_cold int init(AVFilterContext *ctx) { AudioSurroundContext *s = ctx->priv; int64_t in_channel_layout, out_channel_layout; + char in_name[128], out_name[128]; float overlap; - int ret; - - if ((ret = av_channel_layout_from_string(&s->out_ch_layout, s->out_channel_layout_str)) < 0) { - av_log(ctx, AV_LOG_ERROR, "Error parsing output channel layout '%s'.\n", - s->out_channel_layout_str); - return ret; - } - - if ((ret = av_channel_layout_from_string(&s->in_ch_layout, s->in_channel_layout_str)) < 0) { - av_log(ctx, AV_LOG_ERROR, "Error parsing input channel layout '%s'.\n", - s->in_channel_layout_str); - return AVERROR(EINVAL); - } if (s->lowcutf >= s->highcutf) { av_log(ctx, AV_LOG_ERROR, "Low cut-off '%d' should be less than high cut-off '%d'.\n", @@ -1174,8 +1167,10 @@ static av_cold int init(AVFilterContext *ctx) break; default: fail: + av_channel_layout_describe(&s->out_ch_layout, out_name, sizeof(out_name)); + av_channel_layout_describe(&s->in_ch_layout, in_name, sizeof(in_name)); av_log(ctx, AV_LOG_ERROR, "Unsupported upmix: '%s' -> '%s'.\n", - s->in_channel_layout_str, s->out_channel_layout_str); + in_name, out_name); return AVERROR(EINVAL); } @@ -1191,6 +1186,23 @@ static av_cold int init(AVFilterContext *ctx) s->window_func_lut[i] = sqrtf(s->window_func_lut[i] / s->win_size); s->hop_size = FFMAX(1, s->win_size * (1. - s->overlap)); + { + float max = 0.f, *temp_lut = av_calloc(s->win_size, sizeof(*temp_lut)); + if (!temp_lut) + return AVERROR(ENOMEM); + + for (int j = 0; j < s->win_size; j += s->hop_size) { + for (int i = 0; i < s->win_size; i++) + temp_lut[(i + j) % s->win_size] += s->window_func_lut[i]; + } + + for (int i = 0; i < s->win_size; i++) + max = fmaxf(temp_lut[i], max); + av_freep(&temp_lut); + + s->win_gain = 1.f / (max * sqrtf(s->win_size)); + } + allchannels_spread(ctx); return 0; @@ -1201,15 +1213,17 @@ static int fft_channel(AVFilterContext *ctx, AVFrame *in, int ch) AudioSurroundContext *s = ctx->priv; float *src = (float *)s->input_in->extended_data[ch]; float *win = (float *)s->window->extended_data[ch]; + const float *window_func_lut = s->window_func_lut; const int offset = s->win_size - s->hop_size; const float level_in = s->input_levels[ch]; + const int win_size = s->win_size; memmove(src, &src[s->hop_size], offset * sizeof(float)); memcpy(&src[offset], in->extended_data[ch], in->nb_samples * sizeof(float)); memset(&src[offset + in->nb_samples], 0, (s->hop_size - in->nb_samples) * sizeof(float)); - for (int n = 0; n < s->win_size; n++) - win[n] = src[n] * s->window_func_lut[n] * level_in; + for (int n = 0; n < win_size; n++) + win[n] = src[n] * window_func_lut[n] * level_in; s->tx_fn(s->rdft[ch], (float *)s->input->extended_data[ch], win, sizeof(float)); @@ -1231,7 +1245,9 @@ static int fft_channels(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) static int ifft_channel(AVFilterContext *ctx, AVFrame *out, int ch) { AudioSurroundContext *s = ctx->priv; - const float level_out = s->output_levels[ch]; + const float level_out = s->output_levels[ch] * s->win_gain; + const float *window_func_lut = s->window_func_lut; + const int win_size = s->win_size; float *dst, *ptr; dst = (float *)s->output_out->extended_data[ch]; @@ -1244,8 +1260,8 @@ static int ifft_channel(AVFilterContext *ctx, AVFrame *out, int ch) memset(s->overlap_buffer->extended_data[ch] + s->win_size * sizeof(float), 0, s->hop_size * sizeof(float)); - for (int n = 0; n < s->win_size; n++) - ptr[n] += dst[n] * s->window_func_lut[n] * level_out; + for (int n = 0; n < win_size; n++) + ptr[n] += dst[n] * window_func_lut[n] * level_out; ptr = (float *)s->overlap_buffer->extended_data[ch]; dst = (float *)out->extended_data[ch]; @@ -1393,16 +1409,16 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar #define TFLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption surround_options[] = { - { "chl_out", "set output channel layout", OFFSET(out_channel_layout_str), AV_OPT_TYPE_STRING, {.str="5.1"}, 0, 0, FLAGS }, - { "chl_in", "set input channel layout", OFFSET(in_channel_layout_str), AV_OPT_TYPE_STRING, {.str="stereo"},0, 0, FLAGS }, + { "chl_out", "set output channel layout", OFFSET(out_ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str="5.1"}, 0, 0, FLAGS }, + { "chl_in", "set input channel layout", OFFSET(in_ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str="stereo"},0, 0, FLAGS }, { "level_in", "set input level", OFFSET(level_in), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 10, TFLAGS }, { "level_out", "set output level", OFFSET(level_out), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 10, TFLAGS }, { "lfe", "output LFE", OFFSET(output_lfe), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, TFLAGS }, { "lfe_low", "LFE low cut off", OFFSET(lowcutf), AV_OPT_TYPE_INT, {.i64=128}, 0, 256, FLAGS }, { "lfe_high", "LFE high cut off", OFFSET(highcutf), AV_OPT_TYPE_INT, {.i64=256}, 0, 512, FLAGS }, - { "lfe_mode", "set LFE channel mode", OFFSET(lfe_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, TFLAGS, "lfe_mode" }, - { "add", "just add LFE channel", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 1, TFLAGS, "lfe_mode" }, - { "sub", "substract LFE channel with others", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 1, TFLAGS, "lfe_mode" }, + { "lfe_mode", "set LFE channel mode", OFFSET(lfe_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, TFLAGS, .unit = "lfe_mode" }, + { "add", "just add LFE channel", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 1, TFLAGS, .unit = "lfe_mode" }, + { "sub", "subtract LFE channel with others", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 1, TFLAGS, .unit = "lfe_mode" }, { "smooth", "set temporal smoothness strength", OFFSET(smooth), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, 1, TFLAGS }, { "angle", "set soundfield transform angle", OFFSET(angle), AV_OPT_TYPE_FLOAT, {.dbl=90}, 0, 360, TFLAGS }, { "focus", "set soundfield transform focus", OFFSET(focus), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, TFLAGS }, diff --git a/libavfilter/af_tremolo.c b/libavfilter/af_tremolo.c index 3e3e2be6f8a..024c402b794 100644 --- a/libavfilter/af_tremolo.c +++ b/libavfilter/af_tremolo.c @@ -121,13 +121,6 @@ static const AVFilterPad avfilter_af_tremolo_inputs[] = { }, }; -static const AVFilterPad avfilter_af_tremolo_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_tremolo = { .name = "tremolo", .description = NULL_IF_CONFIG_SMALL("Apply tremolo effect."), @@ -135,7 +128,7 @@ const AVFilter ff_af_tremolo = { .priv_class = &tremolo_class, .uninit = uninit, FILTER_INPUTS(avfilter_af_tremolo_inputs), - FILTER_OUTPUTS(avfilter_af_tremolo_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBL), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/af_vibrato.c b/libavfilter/af_vibrato.c index 807d2b59965..e54ae2ad4b4 100644 --- a/libavfilter/af_vibrato.c +++ b/libavfilter/af_vibrato.c @@ -54,9 +54,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; VibratoContext *s = ctx->priv; + const int wave_table_size = s->wave_table_size; + const double *wave_table = s->wave_table; AVFilterLink *outlink = ctx->outputs[0]; + const int channels = s->channels; + const int buf_size = s->buf_size; + const double depth = s->depth; + int wave_table_index = s->wave_table_index; + int buf_index = s->buf_index; AVFrame *out; - int n, c; const double *src; double *dst; @@ -71,39 +77,40 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) av_frame_copy_props(out, in); } - for (n = 0; n < in->nb_samples; n++) { + for (int n = 0; n < in->nb_samples; n++) { + int samp1_index, samp2_index; double integer, decimal; - decimal = modf(s->depth * s->wave_table[s->wave_table_index], &integer); + decimal = modf(depth * wave_table[wave_table_index], &integer); - s->wave_table_index++; - if (s->wave_table_index >= s->wave_table_size) - s->wave_table_index -= s->wave_table_size; + wave_table_index++; + if (wave_table_index >= wave_table_size) + wave_table_index -= wave_table_size; - for (c = 0; c < inlink->ch_layout.nb_channels; c++) { - int samp1_index, samp2_index; - double *buf; - double this_samp; + samp1_index = buf_index + integer; + if (samp1_index >= buf_size) + samp1_index -= buf_size; + samp2_index = samp1_index + 1; + if (samp2_index >= buf_size) + samp2_index -= buf_size; + + for (int c = 0; c < channels; c++) { + double *buf, this_samp; src = (const double *)in->extended_data[c]; dst = (double *)out->extended_data[c]; buf = s->buf[c]; - samp1_index = s->buf_index + integer; - if (samp1_index >= s->buf_size) - samp1_index -= s->buf_size; - samp2_index = samp1_index + 1; - if (samp2_index >= s->buf_size) - samp2_index -= s->buf_size; - this_samp = src[n]; dst[n] = buf[samp1_index] + (decimal * (buf[samp2_index] - buf[samp1_index])); - buf[s->buf_index] = this_samp; + buf[buf_index] = this_samp; } - s->buf_index++; - if (s->buf_index >= s->buf_size) - s->buf_index -= s->buf_size; + buf_index++; + if (buf_index >= buf_size) + buf_index -= buf_size; } + s->wave_table_index = wave_table_index; + s->buf_index = buf_index; if (in != out) av_frame_free(&in); @@ -158,13 +165,6 @@ static const AVFilterPad avfilter_af_vibrato_inputs[] = { }, }; -static const AVFilterPad avfilter_af_vibrato_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_vibrato = { .name = "vibrato", .description = NULL_IF_CONFIG_SMALL("Apply vibrato effect."), @@ -172,7 +172,7 @@ const AVFilter ff_af_vibrato = { .priv_class = &vibrato_class, .uninit = uninit, FILTER_INPUTS(avfilter_af_vibrato_inputs), - FILTER_OUTPUTS(avfilter_af_vibrato_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBLP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/af_virtualbass.c b/libavfilter/af_virtualbass.c index 76b3f420302..9b9967c4191 100644 --- a/libavfilter/af_virtualbass.c +++ b/libavfilter/af_virtualbass.c @@ -23,6 +23,7 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include @@ -163,20 +164,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_virtualbass = { .name = "virtualbass", .description = NULL_IF_CONFIG_SMALL("Audio Virtual Bass."), .priv_size = sizeof(AudioVirtualBassContext), .priv_class = &virtualbass_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = ff_filter_process_command, diff --git a/libavfilter/af_volume.c b/libavfilter/af_volume.c index f6e183df8d3..686e8c651c2 100644 --- a/libavfilter/af_volume.c +++ b/libavfilter/af_volume.c @@ -48,7 +48,9 @@ static const char *const var_names[] = { "nb_channels", ///< number of channels "nb_consumed_samples", ///< number of samples consumed by the filter "nb_samples", ///< number of samples in the current frame +#if FF_API_FRAME_PKT "pos", ///< position in the file of the frame +#endif "pts", ///< frame presentation timestamp "sample_rate", ///< sample rate "startpts", ///< PTS at start of stream @@ -68,19 +70,19 @@ static const AVOption volume_options[] = { { "volume", "set volume adjustment expression", OFFSET(volume_expr), AV_OPT_TYPE_STRING, { .str = "1.0" }, .flags = A|F|T }, { "precision", "select mathematical precision", - OFFSET(precision), AV_OPT_TYPE_INT, { .i64 = PRECISION_FLOAT }, PRECISION_FIXED, PRECISION_DOUBLE, A|F, "precision" }, - { "fixed", "select 8-bit fixed-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FIXED }, INT_MIN, INT_MAX, A|F, "precision" }, - { "float", "select 32-bit floating-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FLOAT }, INT_MIN, INT_MAX, A|F, "precision" }, - { "double", "select 64-bit floating-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_DOUBLE }, INT_MIN, INT_MAX, A|F, "precision" }, - { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_ONCE}, 0, EVAL_MODE_NB-1, .flags = A|F, "eval" }, + OFFSET(precision), AV_OPT_TYPE_INT, { .i64 = PRECISION_FLOAT }, PRECISION_FIXED, PRECISION_DOUBLE, A|F, .unit = "precision" }, + { "fixed", "select 8-bit fixed-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FIXED }, INT_MIN, INT_MAX, A|F, .unit = "precision" }, + { "float", "select 32-bit floating-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_FLOAT }, INT_MIN, INT_MAX, A|F, .unit = "precision" }, + { "double", "select 64-bit floating-point", 0, AV_OPT_TYPE_CONST, { .i64 = PRECISION_DOUBLE }, INT_MIN, INT_MAX, A|F, .unit = "precision" }, + { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_ONCE}, 0, EVAL_MODE_NB-1, .flags = A|F, .unit = "eval" }, { "once", "eval volume expression once", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_ONCE}, .flags = A|F, .unit = "eval" }, { "frame", "eval volume expression per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = A|F, .unit = "eval" }, { "replaygain", "Apply replaygain side data when present", - OFFSET(replaygain), AV_OPT_TYPE_INT, { .i64 = REPLAYGAIN_DROP }, REPLAYGAIN_DROP, REPLAYGAIN_ALBUM, A|F, "replaygain" }, - { "drop", "replaygain side data is dropped", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_DROP }, 0, 0, A|F, "replaygain" }, - { "ignore", "replaygain side data is ignored", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_IGNORE }, 0, 0, A|F, "replaygain" }, - { "track", "track gain is preferred", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_TRACK }, 0, 0, A|F, "replaygain" }, - { "album", "album gain is preferred", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_ALBUM }, 0, 0, A|F, "replaygain" }, + OFFSET(replaygain), AV_OPT_TYPE_INT, { .i64 = REPLAYGAIN_DROP }, REPLAYGAIN_DROP, REPLAYGAIN_ALBUM, A|F, .unit = "replaygain" }, + { "drop", "replaygain side data is dropped", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_DROP }, 0, 0, A|F, .unit = "replaygain" }, + { "ignore", "replaygain side data is ignored", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_IGNORE }, 0, 0, A|F, .unit = "replaygain" }, + { "track", "track gain is preferred", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_TRACK }, 0, 0, A|F, .unit = "replaygain" }, + { "album", "album gain is preferred", 0, AV_OPT_TYPE_CONST, { .i64 = REPLAYGAIN_ALBUM }, 0, 0, A|F, .unit = "replaygain" }, { "replaygain_preamp", "Apply replaygain pre-amplification", OFFSET(replaygain_preamp), AV_OPT_TYPE_DOUBLE, { .dbl = 0.0 }, -15.0, 15.0, A|F }, { "replaygain_noclip", "Apply replaygain clipping prevention", @@ -288,7 +290,9 @@ static int config_output(AVFilterLink *outlink) vol->var_values[VAR_N] = vol->var_values[VAR_NB_CONSUMED_SAMPLES] = vol->var_values[VAR_NB_SAMPLES] = +#if FF_API_FRAME_PKT vol->var_values[VAR_POS] = +#endif vol->var_values[VAR_PTS] = vol->var_values[VAR_STARTPTS] = vol->var_values[VAR_STARTT] = @@ -330,7 +334,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) AVFilterLink *outlink = inlink->dst->outputs[0]; int nb_samples = buf->nb_samples; AVFrame *out_buf; - int64_t pos; AVFrameSideData *sd = av_frame_get_side_data(buf, AV_FRAME_DATA_REPLAYGAIN); int ret; @@ -380,8 +383,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) vol->var_values[VAR_T ] = TS2T(buf->pts, inlink->time_base); vol->var_values[VAR_N ] = inlink->frame_count_out; - pos = buf->pkt_pos; - vol->var_values[VAR_POS] = pos == -1 ? NAN : pos; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + { + int64_t pos; + pos = buf->pkt_pos; + vol->var_values[VAR_POS] = pos == -1 ? NAN : pos; + } +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (vol->eval_mode == EVAL_MODE_FRAME) set_volume(ctx); @@ -437,8 +447,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) } } - emms_c(); - if (buf != out_buf) av_frame_free(&buf); diff --git a/libavfilter/af_volume.h b/libavfilter/af_volume.h index c97902969eb..c3756ee9696 100644 --- a/libavfilter/af_volume.h +++ b/libavfilter/af_volume.h @@ -47,7 +47,9 @@ enum VolumeVarName { VAR_NB_CHANNELS, VAR_NB_CONSUMED_SAMPLES, VAR_NB_SAMPLES, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_PTS, VAR_SAMPLE_RATE, VAR_STARTPTS, diff --git a/libavfilter/af_volumedetect.c b/libavfilter/af_volumedetect.c index ebfad6914f6..8b001d1cf22 100644 --- a/libavfilter/af_volumedetect.c +++ b/libavfilter/af_volumedetect.c @@ -122,13 +122,6 @@ static const AVFilterPad volumedetect_inputs[] = { }, }; -static const AVFilterPad volumedetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_volumedetect = { .name = "volumedetect", .description = NULL_IF_CONFIG_SMALL("Detect audio volume."), @@ -136,6 +129,6 @@ const AVFilter ff_af_volumedetect = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(volumedetect_inputs), - FILTER_OUTPUTS(volumedetect_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), FILTER_SAMPLEFMTS(AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_S16P), }; diff --git a/libavfilter/afir_template.c b/libavfilter/afir_template.c index 3f3778c6756..08e1ca46926 100644 --- a/libavfilter/afir_template.c +++ b/libavfilter/afir_template.c @@ -20,7 +20,6 @@ #include "libavutil/tx.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "audio.h" @@ -30,6 +29,8 @@ #undef HYPOT #undef SAMPLE_FORMAT #undef TX_TYPE +#undef FABS +#undef POW #if DEPTH == 32 #define SAMPLE_FORMAT float #define SQRT sqrtf @@ -37,6 +38,8 @@ #define ctype AVComplexFloat #define ftype float #define TX_TYPE AV_TX_FLOAT_RDFT +#define FABS fabsf +#define POW powf #else #define SAMPLE_FORMAT double #define SQRT sqrt @@ -44,191 +47,40 @@ #define ctype AVComplexDouble #define ftype double #define TX_TYPE AV_TX_DOUBLE_RDFT +#define FABS fabs +#define POW pow #endif #define fn3(a,b) a##_##b #define fn2(a,b) fn3(a,b) #define fn(a) fn2(a, SAMPLE_FORMAT) -static void fn(draw_response)(AVFilterContext *ctx, AVFrame *out) +static ftype fn(ir_gain)(AVFilterContext *ctx, AudioFIRContext *s, + int cur_nb_taps, const ftype *time) { - AudioFIRContext *s = ctx->priv; - ftype *mag, *phase, *delay, min = FLT_MAX, max = FLT_MIN; - ftype min_delay = FLT_MAX, max_delay = FLT_MIN; - int prev_ymag = -1, prev_yphase = -1, prev_ydelay = -1; - char text[32]; - int channel, i, x; - - for (int y = 0; y < s->h; y++) - memset(out->data[0] + y * out->linesize[0], 0, s->w * 4); - - phase = av_malloc_array(s->w, sizeof(*phase)); - mag = av_malloc_array(s->w, sizeof(*mag)); - delay = av_malloc_array(s->w, sizeof(*delay)); - if (!mag || !phase || !delay) - goto end; - - channel = av_clip(s->ir_channel, 0, s->ir[s->selir]->ch_layout.nb_channels - 1); - for (i = 0; i < s->w; i++) { - const ftype *src = (const ftype *)s->ir[s->selir]->extended_data[channel]; - double w = i * M_PI / (s->w - 1); - double div, real_num = 0., imag_num = 0., real = 0., imag = 0.; - - for (x = 0; x < s->nb_taps[s->selir]; x++) { - real += cos(-x * w) * src[x]; - imag += sin(-x * w) * src[x]; - real_num += cos(-x * w) * src[x] * x; - imag_num += sin(-x * w) * src[x] * x; - } - - mag[i] = hypot(real, imag); - phase[i] = atan2(imag, real); - div = real * real + imag * imag; - delay[i] = (real_num * real + imag_num * imag) / div; - min = fminf(min, mag[i]); - max = fmaxf(max, mag[i]); - min_delay = fminf(min_delay, delay[i]); - max_delay = fmaxf(max_delay, delay[i]); - } - - for (i = 0; i < s->w; i++) { - int ymag = mag[i] / max * (s->h - 1); - int ydelay = (delay[i] - min_delay) / (max_delay - min_delay) * (s->h - 1); - int yphase = (0.5 * (1. + phase[i] / M_PI)) * (s->h - 1); - - ymag = s->h - 1 - av_clip(ymag, 0, s->h - 1); - yphase = s->h - 1 - av_clip(yphase, 0, s->h - 1); - ydelay = s->h - 1 - av_clip(ydelay, 0, s->h - 1); - - if (prev_ymag < 0) - prev_ymag = ymag; - if (prev_yphase < 0) - prev_yphase = yphase; - if (prev_ydelay < 0) - prev_ydelay = ydelay; - - draw_line(out, i, ymag, FFMAX(i - 1, 0), prev_ymag, 0xFFFF00FF); - draw_line(out, i, yphase, FFMAX(i - 1, 0), prev_yphase, 0xFF00FF00); - draw_line(out, i, ydelay, FFMAX(i - 1, 0), prev_ydelay, 0xFF00FFFF); - - prev_ymag = ymag; - prev_yphase = yphase; - prev_ydelay = ydelay; - } + ftype ch_gain, sum = 0; - if (s->w > 400 && s->h > 100) { - drawtext(out, 2, 2, "Max Magnitude:", 0xDDDDDDDD); - snprintf(text, sizeof(text), "%.2f", max); - drawtext(out, 15 * 8 + 2, 2, text, 0xDDDDDDDD); - - drawtext(out, 2, 12, "Min Magnitude:", 0xDDDDDDDD); - snprintf(text, sizeof(text), "%.2f", min); - drawtext(out, 15 * 8 + 2, 12, text, 0xDDDDDDDD); - - drawtext(out, 2, 22, "Max Delay:", 0xDDDDDDDD); - snprintf(text, sizeof(text), "%.2f", max_delay); - drawtext(out, 11 * 8 + 2, 22, text, 0xDDDDDDDD); + if (s->ir_norm < 0.f) { + ch_gain = 1; + } else if (s->ir_norm == 0.f) { + for (int i = 0; i < cur_nb_taps; i++) + sum += time[i]; + ch_gain = 1. / sum; + } else { + ftype ir_norm = s->ir_norm; - drawtext(out, 2, 32, "Min Delay:", 0xDDDDDDDD); - snprintf(text, sizeof(text), "%.2f", min_delay); - drawtext(out, 11 * 8 + 2, 32, text, 0xDDDDDDDD); + for (int i = 0; i < cur_nb_taps; i++) + sum += POW(FABS(time[i]), ir_norm); + ch_gain = 1. / POW(sum, 1. / ir_norm); } -end: - av_free(delay); - av_free(phase); - av_free(mag); + return ch_gain; } -static int fn(get_power)(AVFilterContext *ctx, AudioFIRContext *s, +static void fn(ir_scale)(AVFilterContext *ctx, AudioFIRContext *s, int cur_nb_taps, int ch, - ftype *time) + ftype *time, ftype ch_gain) { - ftype ch_gain = 1; - - switch (s->gtype) { - case -1: - ch_gain = 1; - break; - case 0: - { - ftype sum = 0; - - for (int i = 0; i < cur_nb_taps; i++) - sum += FFABS(time[i]); - ch_gain = 1. / sum; - } - break; - case 1: - { - ftype sum = 0; - - for (int i = 0; i < cur_nb_taps; i++) - sum += time[i]; - ch_gain = 1. / sum; - } - break; - case 2: - { - ftype sum = 0; - - for (int i = 0; i < cur_nb_taps; i++) - sum += time[i] * time[i]; - ch_gain = 1. / SQRT(sum); - } - break; - case 3: - case 4: - { - ftype *inc, *outc, scale, power; - AVTXContext *tx; - av_tx_fn tx_fn; - int ret, size; - - size = 1 << av_ceil_log2_c(cur_nb_taps); - inc = av_calloc(size + 2, sizeof(SAMPLE_FORMAT)); - outc = av_calloc(size + 2, sizeof(SAMPLE_FORMAT)); - if (!inc || !outc) { - av_free(outc); - av_free(inc); - break; - } - - scale = 1.; - ret = av_tx_init(&tx, &tx_fn, TX_TYPE, 0, size, &scale, 0); - if (ret < 0) { - av_free(outc); - av_free(inc); - break; - } - - { - memcpy(inc, time, cur_nb_taps * sizeof(SAMPLE_FORMAT)); - tx_fn(tx, outc, inc, sizeof(SAMPLE_FORMAT)); - - power = 0; - if (s->gtype == 3) { - for (int i = 0; i < size / 2 + 1; i++) - power = FFMAX(power, HYPOT(outc[i * 2], outc[i * 2 + 1])); - } else { - ftype sum = 0; - for (int i = 0; i < size / 2 + 1; i++) - sum += HYPOT(outc[i * 2], outc[i * 2 + 1]); - power = SQRT(sum / (size / 2 + 1)); - } - - ch_gain = 1. / power; - } - - av_tx_uninit(&tx); - av_free(outc); - av_free(inc); - } - break; - default: - return AVERROR_BUG; - } - if (ch_gain != 1. || s->ir_gain != 1.) { ftype gain = ch_gain * s->ir_gain; @@ -239,8 +91,6 @@ static int fn(get_power)(AVFilterContext *ctx, AudioFIRContext *s, s->fdsp->vector_dmul_scalar(time, time, gain, FFALIGN(cur_nb_taps, 8)); #endif } - - return 0; } static void fn(convert_channel)(AVFilterContext *ctx, AudioFIRContext *s, int ch, @@ -251,7 +101,7 @@ static void fn(convert_channel)(AVFilterContext *ctx, AudioFIRContext *s, int ch ftype *time = (ftype *)s->norm_ir[selir]->extended_data[ch]; ftype *tempin = (ftype *)seg->tempin->extended_data[ch]; ftype *tempout = (ftype *)seg->tempout->extended_data[ch]; - ctype *coeff = (ctype *)seg->coeff[selir]->extended_data[ch]; + ctype *coeff = (ctype *)seg->coeff->extended_data[ch]; const int remaining = nb_taps - (seg->input_offset + coeff_partition * seg->part_size); const int size = remaining >= seg->part_size ? seg->part_size : remaining; @@ -285,19 +135,19 @@ static void fn(fir_fadd)(AudioFIRContext *s, ftype *dst, const ftype *src, int n } } -static int fn(fir_quantum)(AVFilterContext *ctx, AVFrame *out, int ch, int offset) +static int fn(fir_quantum)(AVFilterContext *ctx, AVFrame *out, int ch, int ioffset, int offset, int selir) { AudioFIRContext *s = ctx->priv; - const ftype *in = (const ftype *)s->in->extended_data[ch] + offset; + const ftype *in = (const ftype *)s->in->extended_data[ch] + ioffset; ftype *blockout, *ptr = (ftype *)out->extended_data[ch] + offset; const int min_part_size = s->min_part_size; const int nb_samples = FFMIN(min_part_size, out->nb_samples - offset); - const int nb_segments = s->nb_segments; + const int nb_segments = s->nb_segments[selir]; const float dry_gain = s->dry_gain; - const int selir = s->selir; + const float wet_gain = s->wet_gain; for (int segment = 0; segment < nb_segments; segment++) { - AudioFIRSegment *seg = &s->seg[segment]; + AudioFIRSegment *seg = &s->seg[selir][segment]; ftype *src = (ftype *)seg->input->extended_data[ch]; ftype *dst = (ftype *)seg->output->extended_data[ch]; ftype *sumin = (ftype *)seg->sumin->extended_data[ch]; @@ -311,13 +161,14 @@ static int fn(fir_quantum)(AVFilterContext *ctx, AVFrame *out, int ch, int offse int j; seg->part_index[ch] = seg->part_index[ch] % nb_partitions; - if (min_part_size >= 8) { + if (dry_gain == 1.f) { + memcpy(src + input_offset, in, nb_samples * sizeof(*src)); + } else if (min_part_size >= 8) { #if DEPTH == 32 s->fdsp->vector_fmul_scalar(src + input_offset, in, dry_gain, FFALIGN(nb_samples, 4)); #else s->fdsp->vector_dmul_scalar(src + input_offset, in, dry_gain, FFALIGN(nb_samples, 8)); #endif - emms_c(); } else { ftype *src2 = src + input_offset; for (int n = 0; n < nb_samples; n++) @@ -337,70 +188,18 @@ static int fn(fir_quantum)(AVFilterContext *ctx, AVFrame *out, int ch, int offse memset(sumin, 0, sizeof(*sumin) * seg->fft_length); - if (seg->loading[ch] < nb_partitions) { - j = seg->part_index[ch] <= 0 ? nb_partitions - 1 : seg->part_index[ch] - 1; - for (int i = 0; i < nb_partitions; i++) { - const int input_partition = j; - const int coeff_partition = i; - const int coffset = coeff_partition * seg->coeff_size; - const ftype *blockout = (const ftype *)seg->blockout->extended_data[ch] + input_partition * seg->block_size; - const ctype *coeff = ((const ctype *)seg->coeff[selir]->extended_data[ch]) + coffset; - - if (j == 0) - j = nb_partitions; - j--; - -#if DEPTH == 32 - s->afirdsp.fcmul_add(sumin, blockout, (const ftype *)coeff, part_size); -#else - s->afirdsp.dcmul_add(sumin, blockout, (const ftype *)coeff, part_size); -#endif - } - - seg->itx_fn(seg->itx[ch], sumout, sumin, sizeof(ctype)); - memcpy(dst + part_size, sumout + part_size, part_size * sizeof(*buf)); - memset(sumin, 0, sizeof(*sumin) * seg->fft_length); - } - blockout = (ftype *)seg->blockout->extended_data[ch] + seg->part_index[ch] * seg->block_size; memset(tempin + part_size, 0, sizeof(*tempin) * (seg->block_size - part_size)); memcpy(tempin, src, sizeof(*src) * part_size); seg->tx_fn(seg->tx[ch], blockout, tempin, sizeof(ftype)); - if (seg->loading[ch] < nb_partitions) { - const int selir = s->prev_selir; - - j = seg->part_index[ch]; - for (int i = 0; i < nb_partitions; i++) { - const int input_partition = j; - const int coeff_partition = i; - const int coffset = coeff_partition * seg->coeff_size; - const ftype *blockout = (const ftype *)seg->blockout->extended_data[ch] + input_partition * seg->block_size; - const ctype *coeff = ((const ctype *)seg->coeff[selir]->extended_data[ch]) + coffset; - - if (j == 0) - j = nb_partitions; - j--; - -#if DEPTH == 32 - s->afirdsp.fcmul_add(sumin, blockout, (const ftype *)coeff, part_size); -#else - s->afirdsp.dcmul_add(sumin, blockout, (const ftype *)coeff, part_size); -#endif - } - - seg->itx_fn(seg->itx[ch], sumout, sumin, sizeof(ctype)); - memcpy(dst + 2 * part_size, sumout, 2 * part_size * sizeof(*dst)); - memset(sumin, 0, sizeof(*sumin) * seg->fft_length); - } - j = seg->part_index[ch]; for (int i = 0; i < nb_partitions; i++) { const int input_partition = j; const int coeff_partition = i; const int coffset = coeff_partition * seg->coeff_size; const ftype *blockout = (const ftype *)seg->blockout->extended_data[ch] + input_partition * seg->block_size; - const ctype *coeff = ((const ctype *)seg->coeff[selir]->extended_data[ch]) + coffset; + const ctype *coeff = ((const ctype *)seg->coeff->extended_data[ch]) + coffset; if (j == 0) j = nb_partitions; @@ -415,34 +214,9 @@ static int fn(fir_quantum)(AVFilterContext *ctx, AVFrame *out, int ch, int offse seg->itx_fn(seg->itx[ch], sumout, sumin, sizeof(ctype)); - if (seg->loading[ch] < nb_partitions) { - ftype *ptr1 = dst + part_size; - ftype *ptr2 = dst + part_size * 2; - ftype *ptr3 = dst + part_size * 3; - ftype *ptr4 = dst + part_size * 4; - if (seg->loading[ch] == 0) - memcpy(ptr4, buf, sizeof(*ptr4) * part_size); - for (int n = 0; n < part_size; n++) - ptr2[n] += ptr4[n]; - - if (seg->loading[ch] < nb_partitions - 1) - memcpy(ptr4, ptr3, part_size * sizeof(*dst)); - for (int n = 0; n < part_size; n++) - ptr1[n] += sumout[n]; - - if (seg->loading[ch] == nb_partitions - 1) - memcpy(buf, sumout + part_size, part_size * sizeof(*buf)); - - for (int i = 0; i < part_size; i++) { - const ftype factor = (part_size * seg->loading[ch] + i) / (ftype)(part_size * nb_partitions); - const ftype ifactor = 1 - factor; - dst[i] = ptr1[i] * factor + ptr2[i] * ifactor; - } - } else { - fn(fir_fadd)(s, buf, sumout, part_size); - memcpy(dst, buf, part_size * sizeof(*dst)); - memcpy(buf, sumout + part_size, part_size * sizeof(*buf)); - } + fn(fir_fadd)(s, buf, sumout, part_size); + memcpy(dst, buf, part_size * sizeof(*dst)); + memcpy(buf, sumout + part_size, part_size * sizeof(*buf)); fn(fir_fadd)(s, ptr, dst, nb_samples); @@ -450,24 +224,71 @@ static int fn(fir_quantum)(AVFilterContext *ctx, AVFrame *out, int ch, int offse memmove(src, src + min_part_size, (seg->input_size - min_part_size) * sizeof(*src)); seg->part_index[ch] = (seg->part_index[ch] + 1) % nb_partitions; - if (seg->loading[ch] < nb_partitions) - seg->loading[ch]++; } - if (s->wet_gain == 1.f) + if (wet_gain == 1.f) return 0; if (min_part_size >= 8) { #if DEPTH == 32 - s->fdsp->vector_fmul_scalar(ptr, ptr, s->wet_gain, FFALIGN(nb_samples, 4)); + s->fdsp->vector_fmul_scalar(ptr, ptr, wet_gain, FFALIGN(nb_samples, 4)); #else - s->fdsp->vector_dmul_scalar(ptr, ptr, s->wet_gain, FFALIGN(nb_samples, 8)); + s->fdsp->vector_dmul_scalar(ptr, ptr, wet_gain, FFALIGN(nb_samples, 8)); #endif - emms_c(); } else { for (int n = 0; n < nb_samples; n++) - ptr[n] *= s->wet_gain; + ptr[n] *= wet_gain; } return 0; } + +static void fn(fir_quantums)(AVFilterContext *ctx, AudioFIRContext *s, AVFrame *out, + int min_part_size, int ch, int offset, + int prev_selir, int selir) +{ + if (ctx->is_disabled || s->prev_is_disabled) { + const ftype *in = (const ftype *)s->in->extended_data[ch] + offset; + const ftype *xfade0 = (const ftype *)s->xfade[0]->extended_data[ch]; + const ftype *xfade1 = (const ftype *)s->xfade[1]->extended_data[ch]; + ftype *src0 = (ftype *)s->fadein[0]->extended_data[ch]; + ftype *src1 = (ftype *)s->fadein[1]->extended_data[ch]; + ftype *dst = ((ftype *)out->extended_data[ch]) + offset; + + if (ctx->is_disabled && !s->prev_is_disabled) { + memset(src0, 0, min_part_size * sizeof(ftype)); + fn(fir_quantum)(ctx, s->fadein[0], ch, offset, 0, selir); + for (int n = 0; n < min_part_size; n++) + dst[n] = xfade1[n] * src0[n] + xfade0[n] * in[n]; + } else if (!ctx->is_disabled && s->prev_is_disabled) { + memset(src1, 0, min_part_size * sizeof(ftype)); + fn(fir_quantum)(ctx, s->fadein[1], ch, offset, 0, selir); + for (int n = 0; n < min_part_size; n++) + dst[n] = xfade1[n] * in[n] + xfade0[n] * src1[n]; + } else { + memcpy(dst, in, sizeof(ftype) * min_part_size); + } + } else if (prev_selir != selir && s->loading[ch] != 0) { + const ftype *xfade0 = (const ftype *)s->xfade[0]->extended_data[ch]; + const ftype *xfade1 = (const ftype *)s->xfade[1]->extended_data[ch]; + ftype *src0 = (ftype *)s->fadein[0]->extended_data[ch]; + ftype *src1 = (ftype *)s->fadein[1]->extended_data[ch]; + ftype *dst = ((ftype *)out->extended_data[ch]) + offset; + + memset(src0, 0, min_part_size * sizeof(ftype)); + memset(src1, 0, min_part_size * sizeof(ftype)); + + fn(fir_quantum)(ctx, s->fadein[0], ch, offset, 0, prev_selir); + fn(fir_quantum)(ctx, s->fadein[1], ch, offset, 0, selir); + + if (s->loading[ch] > s->max_offset[selir]) { + for (int n = 0; n < min_part_size; n++) + dst[n] = xfade1[n] * src0[n] + xfade0[n] * src1[n]; + s->loading[ch] = 0; + } else { + memcpy(dst, src0, min_part_size * sizeof(ftype)); + } + } else { + fn(fir_quantum)(ctx, out, ch, offset, offset, selir); + } +} diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index d7db46c2af9..149bf509973 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -21,6 +21,7 @@ #include "avfilter.h" +extern const AVFilter ff_af_aap; extern const AVFilter ff_af_abench; extern const AVFilter ff_af_acompressor; extern const AVFilter ff_af_acontrast; @@ -70,11 +71,13 @@ extern const AVFilter ff_af_apad; extern const AVFilter ff_af_aperms; extern const AVFilter ff_af_aphaser; extern const AVFilter ff_af_aphaseshift; +extern const AVFilter ff_af_apsnr; extern const AVFilter ff_af_apsyclip; extern const AVFilter ff_af_apulsator; extern const AVFilter ff_af_arealtime; extern const AVFilter ff_af_aresample; extern const AVFilter ff_af_areverse; +extern const AVFilter ff_af_arls; extern const AVFilter ff_af_arnndn; extern const AVFilter ff_af_asdr; extern const AVFilter ff_af_asegment; @@ -86,6 +89,7 @@ extern const AVFilter ff_af_asetrate; extern const AVFilter ff_af_asettb; extern const AVFilter ff_af_ashowinfo; extern const AVFilter ff_af_asidedata; +extern const AVFilter ff_af_asisdr; extern const AVFilter ff_af_asoftclip; extern const AVFilter ff_af_aspectralstats; extern const AVFilter ff_af_asplit; @@ -160,6 +164,7 @@ extern const AVFilter ff_af_volumedetect; extern const AVFilter ff_asrc_aevalsrc; extern const AVFilter ff_asrc_afdelaysrc; +extern const AVFilter ff_asrc_afireqsrc; extern const AVFilter ff_asrc_afirsrc; extern const AVFilter ff_asrc_anoisesrc; extern const AVFilter ff_asrc_anullsrc; @@ -195,7 +200,10 @@ extern const AVFilter ff_vf_bm3d; extern const AVFilter ff_vf_boxblur; extern const AVFilter ff_vf_boxblur_opencl; extern const AVFilter ff_vf_bwdif; +extern const AVFilter ff_vf_bwdif_cuda; +extern const AVFilter ff_vf_bwdif_vulkan; extern const AVFilter ff_vf_cas; +extern const AVFilter ff_vf_ccrepack; extern const AVFilter ff_vf_chromaber_vulkan; extern const AVFilter ff_vf_chromahold; extern const AVFilter ff_vf_chromakey; @@ -291,6 +299,7 @@ extern const AVFilter ff_vf_freezedetect; extern const AVFilter ff_vf_freezeframes; extern const AVFilter ff_vf_frei0r; extern const AVFilter ff_vf_fspp; +extern const AVFilter ff_vf_fsync; extern const AVFilter ff_vf_gblur; extern const AVFilter ff_vf_gblur_vulkan; extern const AVFilter ff_vf_geq; @@ -332,6 +341,7 @@ extern const AVFilter ff_vf_lenscorrection; extern const AVFilter ff_vf_lensfun; extern const AVFilter ff_vf_libplacebo; extern const AVFilter ff_vf_libvmaf; +extern const AVFilter ff_vf_libvmaf_cuda; extern const AVFilter ff_vf_limitdiff; extern const AVFilter ff_vf_limiter; extern const AVFilter ff_vf_loop; @@ -364,6 +374,7 @@ extern const AVFilter ff_vf_multiply; extern const AVFilter ff_vf_negate; extern const AVFilter ff_vf_nlmeans; extern const AVFilter ff_vf_nlmeans_opencl; +extern const AVFilter ff_vf_nlmeans_vulkan; extern const AVFilter ff_vf_nnedi; extern const AVFilter ff_vf_noformat; extern const AVFilter ff_vf_noise; @@ -401,6 +412,8 @@ extern const AVFilter ff_vf_pseudocolor; extern const AVFilter ff_vf_psnr; extern const AVFilter ff_vf_pullup; extern const AVFilter ff_vf_qp; +extern const AVFilter ff_vf_qrencode; +extern const AVFilter ff_vf_quirc; extern const AVFilter ff_vf_random; extern const AVFilter ff_vf_readeia608; extern const AVFilter ff_vf_readvitc; @@ -421,6 +434,7 @@ extern const AVFilter ff_vf_scale_cuda; extern const AVFilter ff_vf_scale_npp; extern const AVFilter ff_vf_scale_qsv; extern const AVFilter ff_vf_scale_vaapi; +extern const AVFilter ff_vf_scale_vt; extern const AVFilter ff_vf_scale_vulkan; extern const AVFilter ff_vf_scale2ref; extern const AVFilter ff_vf_scale2ref_npp; @@ -472,6 +486,7 @@ extern const AVFilter ff_vf_threshold; extern const AVFilter ff_vf_thumbnail; extern const AVFilter ff_vf_thumbnail_cuda; extern const AVFilter ff_vf_tile; +extern const AVFilter ff_vf_tiltandshift; extern const AVFilter ff_vf_tinterlace; extern const AVFilter ff_vf_tlut2; extern const AVFilter ff_vf_tmedian; @@ -485,6 +500,7 @@ extern const AVFilter ff_vf_transpose; extern const AVFilter ff_vf_transpose_npp; extern const AVFilter ff_vf_transpose_opencl; extern const AVFilter ff_vf_transpose_vaapi; +extern const AVFilter ff_vf_transpose_vt; extern const AVFilter ff_vf_transpose_vulkan; extern const AVFilter ff_vf_trim; extern const AVFilter ff_vf_unpremultiply; @@ -514,6 +530,7 @@ extern const AVFilter ff_vf_xbr; extern const AVFilter ff_vf_xcorrelate; extern const AVFilter ff_vf_xfade; extern const AVFilter ff_vf_xfade_opencl; +extern const AVFilter ff_vf_xfade_vulkan; extern const AVFilter ff_vf_xmedian; extern const AVFilter ff_vf_xstack; extern const AVFilter ff_vf_yadif; @@ -534,6 +551,7 @@ extern const AVFilter ff_vsrc_allrgb; extern const AVFilter ff_vsrc_allyuv; extern const AVFilter ff_vsrc_cellauto; extern const AVFilter ff_vsrc_color; +extern const AVFilter ff_vsrc_color_vulkan; extern const AVFilter ff_vsrc_colorchart; extern const AVFilter ff_vsrc_colorspectrum; extern const AVFilter ff_vsrc_coreimagesrc; @@ -546,6 +564,7 @@ extern const AVFilter ff_vsrc_mandelbrot; extern const AVFilter ff_vsrc_mptestsrc; extern const AVFilter ff_vsrc_nullsrc; extern const AVFilter ff_vsrc_openclsrc; +extern const AVFilter ff_vsrc_qrencodesrc; extern const AVFilter ff_vsrc_pal75bars; extern const AVFilter ff_vsrc_pal100bars; extern const AVFilter ff_vsrc_rgbtestsrc; @@ -555,6 +574,7 @@ extern const AVFilter ff_vsrc_smptehdbars; extern const AVFilter ff_vsrc_testsrc; extern const AVFilter ff_vsrc_testsrc2; extern const AVFilter ff_vsrc_yuvtestsrc; +extern const AVFilter ff_vsrc_zoneplate; extern const AVFilter ff_vsink_nullsink; @@ -591,8 +611,6 @@ extern const AVFilter ff_asrc_abuffer; extern const AVFilter ff_vsrc_buffer; extern const AVFilter ff_asink_abuffer; extern const AVFilter ff_vsink_buffer; -extern const AVFilter ff_af_afifo; -extern const AVFilter ff_vf_fifo; #include "libavfilter/filter_list.c" diff --git a/libavfilter/anlms_template.c b/libavfilter/anlms_template.c new file mode 100644 index 00000000000..b25df4fa18b --- /dev/null +++ b/libavfilter/anlms_template.c @@ -0,0 +1,141 @@ +/* + * 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 + */ + +#undef ONE +#undef ftype +#undef SAMPLE_FORMAT +#if DEPTH == 32 +#define SAMPLE_FORMAT float +#define ftype float +#define ONE 1.f +#else +#define SAMPLE_FORMAT double +#define ftype double +#define ONE 1.0 +#endif + +#define fn3(a,b) a##_##b +#define fn2(a,b) fn3(a,b) +#define fn(a) fn2(a, SAMPLE_FORMAT) + +#if DEPTH == 64 +static double scalarproduct_double(const double *v1, const double *v2, int len) +{ + double p = 0.0; + + for (int i = 0; i < len; i++) + p += v1[i] * v2[i]; + + return p; +} +#endif + +static ftype fn(fir_sample)(AudioNLMSContext *s, ftype sample, ftype *delay, + ftype *coeffs, ftype *tmp, int *offset) +{ + const int order = s->order; + ftype output; + + delay[*offset] = sample; + + memcpy(tmp, coeffs + order - *offset, order * sizeof(ftype)); + +#if DEPTH == 32 + output = s->fdsp->scalarproduct_float(delay, tmp, s->kernel_size); +#else + output = scalarproduct_double(delay, tmp, s->kernel_size); +#endif + + if (--(*offset) < 0) + *offset = order - 1; + + return output; +} + +static ftype fn(process_sample)(AudioNLMSContext *s, ftype input, ftype desired, + ftype *delay, ftype *coeffs, ftype *tmp, int *offsetp) +{ + const int order = s->order; + const ftype leakage = s->leakage; + const ftype mu = s->mu; + const ftype a = ONE - leakage; + ftype sum, output, e, norm, b; + int offset = *offsetp; + + delay[offset + order] = input; + + output = fn(fir_sample)(s, input, delay, coeffs, tmp, offsetp); + e = desired - output; + +#if DEPTH == 32 + sum = s->fdsp->scalarproduct_float(delay, delay, s->kernel_size); +#else + sum = scalarproduct_double(delay, delay, s->kernel_size); +#endif + norm = s->eps + sum; + b = mu * e / norm; + if (s->anlmf) + b *= e * e; + + memcpy(tmp, delay + offset, order * sizeof(ftype)); + +#if DEPTH == 32 + s->fdsp->vector_fmul_scalar(coeffs, coeffs, a, s->kernel_size); + s->fdsp->vector_fmac_scalar(coeffs, tmp, b, s->kernel_size); +#else + s->fdsp->vector_dmul_scalar(coeffs, coeffs, a, s->kernel_size); + s->fdsp->vector_dmac_scalar(coeffs, tmp, b, s->kernel_size); +#endif + + memcpy(coeffs + order, coeffs, order * sizeof(ftype)); + + switch (s->output_mode) { + case IN_MODE: output = input; break; + case DESIRED_MODE: output = desired; break; + case OUT_MODE: output = desired - output; break; + case NOISE_MODE: output = input - output; break; + case ERROR_MODE: break; + } + return output; +} + +static int fn(filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + AudioNLMSContext *s = ctx->priv; + AVFrame *out = arg; + const int start = (out->ch_layout.nb_channels * jobnr) / nb_jobs; + const int end = (out->ch_layout.nb_channels * (jobnr+1)) / nb_jobs; + + for (int c = start; c < end; c++) { + const ftype *input = (const ftype *)s->frame[0]->extended_data[c]; + const ftype *desired = (const ftype *)s->frame[1]->extended_data[c]; + ftype *delay = (ftype *)s->delay->extended_data[c]; + ftype *coeffs = (ftype *)s->coeffs->extended_data[c]; + ftype *tmp = (ftype *)s->tmp->extended_data[c]; + int *offset = (int *)s->offset->extended_data[c]; + ftype *output = (ftype *)out->extended_data[c]; + + for (int n = 0; n < out->nb_samples; n++) { + output[n] = fn(process_sample)(s, input[n], desired[n], delay, coeffs, tmp, offset); + if (ctx->is_disabled) + output[n] = input[n]; + } + } + + return 0; +} diff --git a/libavfilter/arls_template.c b/libavfilter/arls_template.c new file mode 100644 index 00000000000..d8b19d89a54 --- /dev/null +++ b/libavfilter/arls_template.c @@ -0,0 +1,164 @@ +/* + * 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 + */ + +#undef ZERO +#undef HALF +#undef ONE +#undef ftype +#undef SAMPLE_FORMAT +#if DEPTH == 32 +#define SAMPLE_FORMAT float +#define ftype float +#define ONE 1.f +#define HALF 0.5f +#define ZERO 0.f +#else +#define SAMPLE_FORMAT double +#define ftype double +#define ONE 1.0 +#define HALF 0.5 +#define ZERO 0.0 +#endif + +#define fn3(a,b) a##_##b +#define fn2(a,b) fn3(a,b) +#define fn(a) fn2(a, SAMPLE_FORMAT) + +#if DEPTH == 64 +static double scalarproduct_double(const double *v1, const double *v2, int len) +{ + double p = 0.0; + + for (int i = 0; i < len; i++) + p += v1[i] * v2[i]; + + return p; +} +#endif + +static ftype fn(fir_sample)(AudioRLSContext *s, ftype sample, ftype *delay, + ftype *coeffs, ftype *tmp, int *offset) +{ + const int order = s->order; + ftype output; + + delay[*offset] = sample; + + memcpy(tmp, coeffs + order - *offset, order * sizeof(ftype)); + +#if DEPTH == 32 + output = s->fdsp->scalarproduct_float(delay, tmp, s->kernel_size); +#else + output = scalarproduct_double(delay, tmp, s->kernel_size); +#endif + + if (--(*offset) < 0) + *offset = order - 1; + + return output; +} + +static ftype fn(process_sample)(AudioRLSContext *s, ftype input, ftype desired, int ch) +{ + ftype *coeffs = (ftype *)s->coeffs->extended_data[ch]; + ftype *delay = (ftype *)s->delay->extended_data[ch]; + ftype *gains = (ftype *)s->gains->extended_data[ch]; + ftype *tmp = (ftype *)s->tmp->extended_data[ch]; + ftype *u = (ftype *)s->u->extended_data[ch]; + ftype *p = (ftype *)s->p->extended_data[ch]; + ftype *dp = (ftype *)s->dp->extended_data[ch]; + int *offsetp = (int *)s->offset->extended_data[ch]; + const int kernel_size = s->kernel_size; + const int order = s->order; + const ftype lambda = s->lambda; + int offset = *offsetp; + ftype g = lambda; + ftype output, e; + + delay[offset + order] = input; + + output = fn(fir_sample)(s, input, delay, coeffs, tmp, offsetp); + e = desired - output; + + for (int i = 0, pos = offset; i < order; i++, pos++) { + const int ikernel_size = i * kernel_size; + + u[i] = ZERO; + for (int k = 0, pos = offset; k < order; k++, pos++) + u[i] += p[ikernel_size + k] * delay[pos]; + + g += u[i] * delay[pos]; + } + + g = ONE / g; + + for (int i = 0; i < order; i++) { + const int ikernel_size = i * kernel_size; + + gains[i] = u[i] * g; + coeffs[i] = coeffs[order + i] = coeffs[i] + gains[i] * e; + tmp[i] = ZERO; + for (int k = 0, pos = offset; k < order; k++, pos++) + tmp[i] += p[ikernel_size + k] * delay[pos]; + } + + for (int i = 0; i < order; i++) { + const int ikernel_size = i * kernel_size; + + for (int k = 0; k < order; k++) + dp[ikernel_size + k] = gains[i] * tmp[k]; + } + + for (int i = 0; i < order; i++) { + const int ikernel_size = i * kernel_size; + + for (int k = 0; k < order; k++) + p[ikernel_size + k] = (p[ikernel_size + k] - (dp[ikernel_size + k] + dp[kernel_size * k + i]) * HALF) * lambda; + } + + switch (s->output_mode) { + case IN_MODE: output = input; break; + case DESIRED_MODE: output = desired; break; + case OUT_MODE: output = desired - output; break; + case NOISE_MODE: output = input - output; break; + case ERROR_MODE: break; + } + return output; +} + +static int fn(filter_channels)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + AudioRLSContext *s = ctx->priv; + AVFrame *out = arg; + const int start = (out->ch_layout.nb_channels * jobnr) / nb_jobs; + const int end = (out->ch_layout.nb_channels * (jobnr+1)) / nb_jobs; + + for (int c = start; c < end; c++) { + const ftype *input = (const ftype *)s->frame[0]->extended_data[c]; + const ftype *desired = (const ftype *)s->frame[1]->extended_data[c]; + ftype *output = (ftype *)out->extended_data[c]; + + for (int n = 0; n < out->nb_samples; n++) { + output[n] = fn(process_sample)(s, input[n], desired[n], c); + if (ctx->is_disabled) + output[n] = input[n]; + } + } + + return 0; +} diff --git a/libavfilter/asrc_afdelaysrc.c b/libavfilter/asrc_afdelaysrc.c index 2fc57cee4d2..f3f0b080a07 100644 --- a/libavfilter/asrc_afdelaysrc.c +++ b/libavfilter/asrc_afdelaysrc.c @@ -25,6 +25,7 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" typedef struct AFDelaySrcContext { @@ -35,23 +36,10 @@ typedef struct AFDelaySrcContext { int nb_samples; int nb_taps; AVChannelLayout chlayout; - char *chlayout_str; int64_t pts; } AFDelaySrcContext; -static av_cold int init(AVFilterContext *ctx) -{ - AFDelaySrcContext *s = ctx->priv; - int ret; - - ret = ff_parse_channel_layout(&s->chlayout, NULL, s->chlayout_str, ctx); - if (ret < 0) - return ret; - - return 0; -} - static float sincf(float x) { if (x == 0.f) @@ -133,13 +121,6 @@ static const AVFilterPad afdelaysrc_outputs[] = { }, }; -static av_cold void uninit(AVFilterContext *ctx) -{ - AFDelaySrcContext *s = ctx->priv; - - av_channel_layout_uninit(&s->chlayout); -} - #define AF AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM #define OFFSET(x) offsetof(AFDelaySrcContext, x) @@ -152,8 +133,8 @@ static const AVOption afdelaysrc_options[] = { { "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64=1024}, 1, INT_MAX, AF }, { "taps", "set number of taps for delay filter", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=0}, 0, 32768, AF }, { "t", "set number of taps for delay filter", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=0}, 0, 32768, AF }, - { "channel_layout", "set channel layout", OFFSET(chlayout_str),AV_OPT_TYPE_STRING,{.str="stereo"},0, 0, AF }, - { "c", "set channel layout", OFFSET(chlayout_str),AV_OPT_TYPE_STRING,{.str="stereo"},0, 0, AF }, + { "channel_layout", "set channel layout", OFFSET(chlayout), AV_OPT_TYPE_CHLAYOUT,{.str="stereo"},0, 0, AF }, + { "c", "set channel layout", OFFSET(chlayout), AV_OPT_TYPE_CHLAYOUT,{.str="stereo"},0, 0, AF }, { NULL } }; @@ -164,9 +145,7 @@ const AVFilter ff_asrc_afdelaysrc = { .description = NULL_IF_CONFIG_SMALL("Generate a Fractional delay FIR coefficients."), .priv_size = sizeof(AFDelaySrcContext), .priv_class = &afdelaysrc_class, - .init = init, .activate = activate, - .uninit = uninit, .inputs = NULL, FILTER_OUTPUTS(afdelaysrc_outputs), FILTER_QUERY_FUNC(query_formats), diff --git a/libavfilter/asrc_afirsrc.c b/libavfilter/asrc_afirsrc.c index d2ea92c41c7..20cb84bc01a 100644 --- a/libavfilter/asrc_afirsrc.c +++ b/libavfilter/asrc_afirsrc.c @@ -18,13 +18,16 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/cpu.h" #include "libavutil/channel_layout.h" +#include "libavutil/ffmath.h" #include "libavutil/eval.h" #include "libavutil/opt.h" #include "libavutil/tx.h" #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "window_func.h" @@ -38,6 +41,9 @@ typedef struct AudioFIRSourceContext { int sample_rate; int nb_samples; int win_func; + int preset; + int interp; + int phaset; AVComplexFloat *complexf; float *freq; @@ -54,8 +60,8 @@ typedef struct AudioFIRSourceContext { float *win; int64_t pts; - AVTXContext *tx_ctx; - av_tx_fn tx_fn; + AVTXContext *tx_ctx, *itx_ctx; + av_tx_fn tx_fn, itx_fn; } AudioFIRSourceContext; #define OFFSET(x) offsetof(AudioFIRSourceContext, x) @@ -104,6 +110,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->phase); av_freep(&s->complexf); av_tx_uninit(&s->tx_ctx); + av_tx_uninit(&s->itx_ctx); } static av_cold int query_formats(AVFilterContext *ctx) @@ -131,7 +138,7 @@ static int parse_string(char *str, float **items, int *nb_items, int *items_size float *new_items; char *tail; - new_items = av_fast_realloc(NULL, items_size, 1 * sizeof(float)); + new_items = av_fast_realloc(NULL, items_size, sizeof(float)); if (!new_items) return AVERROR(ENOMEM); *items = new_items; @@ -142,7 +149,7 @@ static int parse_string(char *str, float **items, int *nb_items, int *items_size do { (*items)[(*nb_items)++] = av_strtod(tail, &tail); - new_items = av_fast_realloc(*items, items_size, (*nb_items + 1) * sizeof(float)); + new_items = av_fast_realloc(*items, items_size, (*nb_items + 2) * sizeof(float)); if (!new_items) return AVERROR(ENOMEM); *items = new_items; @@ -300,3 +307,284 @@ const AVFilter ff_asrc_afirsrc = { FILTER_QUERY_FUNC(query_formats), .priv_class = &afirsrc_class, }; + +#define DEFAULT_BANDS "25 40 63 100 160 250 400 630 1000 1600 2500 4000 6300 10000 16000 24000" + +typedef struct EqPreset { + char name[16]; + float gains[16]; +} EqPreset; + +static const EqPreset eq_presets[] = { + { "flat", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, + { "acoustic", { 5.0, 4.5, 4.0, 3.5, 1.5, 1.0, 1.5, 1.5, 2.0, 3.0, 3.5, 4.0, 3.7, 3.0, 3.0 } }, + { "bass", { 10.0, 8.8, 8.5, 6.5, 2.5, 1.5, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, + { "beats", { -5.5, -5.0, -4.5, -4.2, -3.5, -3.0, -1.9, 0, 0, 0, 0, 0, 0, 0, 0 } }, + { "classic", { -0.3, 0.3, -3.5, -9.0, -1.0, 0.0, 1.8, 2.1, 0.0, 0.0, 0.0, 4.4, 9.0, 9.0, 9.0 } }, + { "clear", { 3.5, 5.5, 6.5, 9.5, 8.0, 6.5, 3.5, 2.5, 1.3, 5.0, 7.0, 9.0, 10.0, 11.0, 9.0 } }, + { "deep bass", { 12.0, 8.0, 0.0, -6.7, -12.0, -9.0, -3.5, -3.5, -6.1, 0.0, -3.0, -5.0, 0.0, 1.2, 3.0 } }, + { "dubstep", { 12.0, 10.0, 0.5, -1.0, -3.0, -5.0, -5.0, -4.8, -4.5, -2.5, -1.0, 0.0, -2.5, -2.5, 0.0 } }, + { "electronic", { 4.0, 4.0, 3.5, 1.0, 0.0, -0.5, -2.0, 0.0, 2.0, 0.0, 0.0, 1.0, 3.0, 4.0, 4.5 } }, + { "hardstyle", { 6.1, 7.0, 12.0, 6.1, -5.0, -12.0, -2.5, 3.0, 6.5, 0.0, -2.2, -4.5, -6.1, -9.2, -10.0 } }, + { "hip-hop", { 4.5, 4.3, 4.0, 2.5, 1.5, 3.0, -1.0, -1.5, -1.5, 1.5, 0.0, -1.0, 0.0, 1.5, 3.0 } }, + { "jazz", { 0.0, 0.0, 0.0, 2.0, 4.0, 5.9, -5.9, -4.5, -2.5, 2.5, 1.0, -0.8, -0.8, -0.8, -0.8 } }, + { "metal", { 10.5, 10.5, 7.5, 0.0, 2.0, 5.5, 0.0, 0.0, 0.0, 6.1, 0.0, 0.0, 6.1, 10.0, 12.0 } }, + { "movie", { 3.0, 3.0, 6.1, 8.5, 9.0, 7.0, 6.1, 6.1, 5.0, 8.0, 3.5, 3.5, 8.0, 10.0, 8.0 } }, + { "pop", { 0.0, 0.0, 0.0, 0.0, 0.0, 1.3, 2.0, 2.5, 5.0, -1.5, -2.0, -3.0, -3.0, -3.0, -3.0 } }, + { "r&b", { 3.0, 3.0, 7.0, 6.1, 4.5, 1.5, -1.5, -2.0, -1.5, 2.0, 2.5, 3.0, 3.5, 3.8, 4.0 } }, + { "rock", { 0.0, 0.0, 0.0, 3.0, 3.0, -10.0, -4.0, -1.0, 0.8, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0 } }, + { "vocal booster", { -1.5, -2.0, -3.0, -3.0, -0.5, 1.5, 3.5, 3.5, 3.5, 3.0, 2.0, 1.5, 0.0, 0.0, -1.5 } }, +}; + +static const AVOption afireqsrc_options[] = { + { "preset","set equalizer preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=0}, -1, FF_ARRAY_ELEMS(eq_presets)-1, FLAGS, .unit = "preset" }, + { "p", "set equalizer preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=0}, -1, FF_ARRAY_ELEMS(eq_presets)-1, FLAGS, .unit = "preset" }, + { "custom", NULL, 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[ 0].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 0}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[ 1].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 1}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[ 2].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 2}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[ 3].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 3}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[ 4].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 4}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[ 5].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 5}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[ 6].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 6}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[ 7].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 7}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[ 8].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 8}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[ 9].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64= 9}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[10].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=10}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[11].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=11}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[12].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=12}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[13].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=13}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[14].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=14}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[15].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=15}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[16].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=16}, 0, 0, FLAGS, .unit = "preset" }, + { eq_presets[17].name, NULL, 0, AV_OPT_TYPE_CONST, {.i64=17}, 0, 0, FLAGS, .unit = "preset" }, + { "gains", "set gain values per band", OFFSET(magnitude_str), AV_OPT_TYPE_STRING, {.str="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"}, 0, 0, FLAGS }, + { "g", "set gain values per band", OFFSET(magnitude_str), AV_OPT_TYPE_STRING, {.str="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"}, 0, 0, FLAGS }, + { "bands", "set central frequency values per band", OFFSET(freq_points_str), AV_OPT_TYPE_STRING, {.str=DEFAULT_BANDS}, 0, 0, FLAGS }, + { "b", "set central frequency values per band", OFFSET(freq_points_str), AV_OPT_TYPE_STRING, {.str=DEFAULT_BANDS}, 0, 0, FLAGS }, + { "taps", "set number of taps", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=4096}, 16, UINT16_MAX, FLAGS }, + { "t", "set number of taps", OFFSET(nb_taps), AV_OPT_TYPE_INT, {.i64=4096}, 16, UINT16_MAX, FLAGS }, + { "sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT_MAX, FLAGS }, + { "r", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100}, 1, INT_MAX, FLAGS }, + { "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS }, + { "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS }, + { "interp","set the interpolation", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "interp" }, + { "i", "set the interpolation", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "interp" }, + { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "interp" }, + { "cubic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "interp" }, + { "phase","set the phase", OFFSET(phaset), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "phase" }, + { "h", "set the phase", OFFSET(phaset), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "phase" }, + { "linear", "linear phase", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "phase" }, + { "min", "minimum phase", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "phase" }, + {NULL} +}; + +AVFILTER_DEFINE_CLASS(afireqsrc); + +static void eq_interp(AVComplexFloat *complexf, + const float *freq, + const float *magnitude, + int m, int interp, int minterp, + const float factor) +{ + for (int i = 0; i < minterp; i++) { + for (int j = 0; j < m; j++) { + const float x = factor * i; + + if (x <= freq[j+1]) { + float g; + + if (interp == 0) { + const float d = freq[j+1] - freq[j]; + const float d0 = x - freq[j]; + const float d1 = freq[j+1] - x; + const float g0 = magnitude[j]; + const float g1 = magnitude[j+1]; + + if (d0 && d1) { + g = (d0 * g1 + d1 * g0) / d; + } else if (d0) { + g = g1; + } else { + g = g0; + } + } else { + if (x <= freq[j]) { + g = magnitude[j]; + } else { + float x1, x2, x3; + float a, b, c, d; + float m0, m1, m2, msum; + const float unit = freq[j+1] - freq[j]; + + m0 = j != 0 ? unit * (magnitude[j] - magnitude[j-1]) / (freq[j] - freq[j-1]) : 0; + m1 = magnitude[j+1] - magnitude[j]; + m2 = j != minterp - 1 ? unit * (magnitude[j+2] - magnitude[j+1]) / (freq[j+2] - freq[j+1]) : 0; + + msum = fabsf(m0) + fabsf(m1); + m0 = msum > 0.f ? (fabsf(m0) * m1 + fabsf(m1) * m0) / msum : 0.f; + msum = fabsf(m1) + fabsf(m2); + m1 = msum > 0.f ? (fabsf(m1) * m2 + fabsf(m2) * m1) / msum : 0.f; + + d = magnitude[j]; + c = m0; + b = 3.f * magnitude[j+1] - m1 - 2.f * c - 3.f * d; + a = magnitude[j+1] - b - c - d; + + x1 = (x - freq[j]) / unit; + x2 = x1 * x1; + x3 = x2 * x1; + + g = a * x3 + b * x2 + c * x1 + d; + } + } + + complexf[i].re = g; + complexf[i].im = 0; + complexf[minterp * 2 - i - 1].re = g; + complexf[minterp * 2 - i - 1].im = 0; + + break; + } + } + } +} + +static av_cold int config_eq_output(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + AudioFIRSourceContext *s = ctx->priv; + int fft_size, middle, asize, ret; + float scale, factor; + + s->nb_freq = s->nb_magnitude = 0; + if (s->preset < 0) { + ret = parse_string(s->freq_points_str, &s->freq, &s->nb_freq, &s->freq_size); + if (ret < 0) + return ret; + + ret = parse_string(s->magnitude_str, &s->magnitude, &s->nb_magnitude, &s->magnitude_size); + if (ret < 0) + return ret; + } else { + char *freq_str; + + s->nb_magnitude = FF_ARRAY_ELEMS(eq_presets[s->preset].gains); + + freq_str = av_strdup(DEFAULT_BANDS); + if (!freq_str) + return AVERROR(ENOMEM); + + ret = parse_string(freq_str, &s->freq, &s->nb_freq, &s->freq_size); + av_free(freq_str); + if (ret < 0) + return ret; + + s->magnitude = av_calloc(s->nb_magnitude + 1, sizeof(*s->magnitude)); + if (!s->magnitude) + return AVERROR(ENOMEM); + memcpy(s->magnitude, eq_presets[s->preset].gains, sizeof(*s->magnitude) * s->nb_magnitude); + } + + if (s->nb_freq != s->nb_magnitude || s->nb_freq < 2) { + av_log(ctx, AV_LOG_ERROR, "Number of bands and gains must be same and >= 2.\n"); + return AVERROR(EINVAL); + } + + s->freq[s->nb_freq] = outlink->sample_rate * 0.5f; + s->magnitude[s->nb_freq] = s->magnitude[s->nb_freq-1]; + + fft_size = s->nb_taps * 2; + factor = FFMIN(outlink->sample_rate * 0.5f, s->freq[s->nb_freq - 1]) / (float)fft_size; + asize = FFALIGN(fft_size, av_cpu_max_align()); + s->complexf = av_calloc(asize * 2, sizeof(*s->complexf)); + if (!s->complexf) + return AVERROR(ENOMEM); + + scale = 1.f; + ret = av_tx_init(&s->itx_ctx, &s->itx_fn, AV_TX_FLOAT_FFT, 1, fft_size, &scale, 0); + if (ret < 0) + return ret; + + s->taps = av_calloc(s->nb_taps, sizeof(*s->taps)); + if (!s->taps) + return AVERROR(ENOMEM); + + eq_interp(s->complexf, s->freq, s->magnitude, s->nb_freq, s->interp, s->nb_taps, factor); + + for (int i = 0; i < fft_size; i++) + s->complexf[i].re = ff_exp10f(s->complexf[i].re / 20.f); + + if (s->phaset) { + const float threshold = powf(10.f, -100.f / 20.f); + const float logt = logf(threshold); + + scale = 1.f; + ret = av_tx_init(&s->tx_ctx, &s->tx_fn, AV_TX_FLOAT_FFT, 0, fft_size, &scale, 0); + if (ret < 0) + return ret; + + for (int i = 0; i < fft_size; i++) + s->complexf[i].re = s->complexf[i].re < threshold ? logt : logf(s->complexf[i].re); + + s->itx_fn(s->itx_ctx, s->complexf + asize, s->complexf, sizeof(float)); + for (int i = 0; i < fft_size; i++) { + s->complexf[i + asize].re /= fft_size; + s->complexf[i + asize].im /= fft_size; + } + + for (int i = 1; i < s->nb_taps; i++) { + s->complexf[asize + i].re += s->complexf[asize + fft_size - i].re; + s->complexf[asize + i].im -= s->complexf[asize + fft_size - i].im; + s->complexf[asize + fft_size - i].re = 0.f; + s->complexf[asize + fft_size - i].im = 0.f; + } + s->complexf[asize + s->nb_taps - 1].im *= -1.f; + + s->tx_fn(s->tx_ctx, s->complexf, s->complexf + asize, sizeof(float)); + + for (int i = 0; i < fft_size; i++) { + float eR = expf(s->complexf[i].re); + + s->complexf[i].re = eR * cosf(s->complexf[i].im); + s->complexf[i].im = eR * sinf(s->complexf[i].im); + } + + s->itx_fn(s->itx_ctx, s->complexf + asize, s->complexf, sizeof(float)); + + for (int i = 0; i < s->nb_taps; i++) + s->taps[i] = s->complexf[i + asize].re / fft_size; + } else { + s->itx_fn(s->itx_ctx, s->complexf + asize, s->complexf, sizeof(float)); + + middle = s->nb_taps / 2; + for (int i = 0; i < middle; i++) { + s->taps[middle - i] = s->complexf[i + asize].re / fft_size; + s->taps[middle + i] = s->complexf[i + asize].re / fft_size; + } + } + + s->pts = 0; + + return 0; +} + +static const AVFilterPad afireqsrc_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + .config_props = config_eq_output, + }, +}; + +const AVFilter ff_asrc_afireqsrc = { + .name = "afireqsrc", + .description = NULL_IF_CONFIG_SMALL("Generate a FIR equalizer coefficients audio stream."), + .uninit = uninit, + .activate = activate, + .priv_size = sizeof(AudioFIRSourceContext), + .inputs = NULL, + FILTER_OUTPUTS(afireqsrc_outputs), + FILTER_QUERY_FUNC(query_formats), + .priv_class = &afireqsrc_class, +}; diff --git a/libavfilter/asrc_anoisesrc.c b/libavfilter/asrc_anoisesrc.c index 96a8fd63e9b..d98bc4fc8ae 100644 --- a/libavfilter/asrc_anoisesrc.c +++ b/libavfilter/asrc_anoisesrc.c @@ -23,6 +23,7 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "libavutil/lfg.h" #include "libavutil/random_seed.h" @@ -31,6 +32,7 @@ typedef struct ANoiseSrcContext { const AVClass *class; int sample_rate; double amplitude; + double density; int64_t duration; int color; int64_t seed; @@ -38,7 +40,7 @@ typedef struct ANoiseSrcContext { int64_t pts; int infinite; - double (*filter)(double white, double *buf, double half_amplitude); + double (*filter)(double white, double *buf); double buf[7]; AVLFG c; } ANoiseSrcContext; @@ -63,19 +65,20 @@ static const AVOption anoisesrc_options[] = { { "a", "set amplitude", OFFSET(amplitude), AV_OPT_TYPE_DOUBLE, {.dbl = 1.}, 0., 1., FLAGS }, { "duration", "set duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, INT64_MAX, FLAGS }, { "d", "set duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, INT64_MAX, FLAGS }, - { "color", "set noise color", OFFSET(color), AV_OPT_TYPE_INT, {.i64 = 0}, 0, NM_NB - 1, FLAGS, "color" }, - { "colour", "set noise color", OFFSET(color), AV_OPT_TYPE_INT, {.i64 = 0}, 0, NM_NB - 1, FLAGS, "color" }, - { "c", "set noise color", OFFSET(color), AV_OPT_TYPE_INT, {.i64 = 0}, 0, NM_NB - 1, FLAGS, "color" }, - { "white", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NM_WHITE}, 0, 0, FLAGS, "color" }, - { "pink", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NM_PINK}, 0, 0, FLAGS, "color" }, - { "brown", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NM_BROWN}, 0, 0, FLAGS, "color" }, - { "blue", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NM_BLUE}, 0, 0, FLAGS, "color" }, - { "violet", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NM_VIOLET}, 0, 0, FLAGS, "color" }, - { "velvet", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NM_VELVET}, 0, 0, FLAGS, "color" }, + { "color", "set noise color", OFFSET(color), AV_OPT_TYPE_INT, {.i64 = 0}, 0, NM_NB - 1, FLAGS, .unit = "color" }, + { "colour", "set noise color", OFFSET(color), AV_OPT_TYPE_INT, {.i64 = 0}, 0, NM_NB - 1, FLAGS, .unit = "color" }, + { "c", "set noise color", OFFSET(color), AV_OPT_TYPE_INT, {.i64 = 0}, 0, NM_NB - 1, FLAGS, .unit = "color" }, + { "white", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NM_WHITE}, 0, 0, FLAGS, .unit = "color" }, + { "pink", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NM_PINK}, 0, 0, FLAGS, .unit = "color" }, + { "brown", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NM_BROWN}, 0, 0, FLAGS, .unit = "color" }, + { "blue", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NM_BLUE}, 0, 0, FLAGS, .unit = "color" }, + { "violet", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NM_VIOLET}, 0, 0, FLAGS, .unit = "color" }, + { "velvet", 0, 0, AV_OPT_TYPE_CONST, {.i64 = NM_VELVET}, 0, 0, FLAGS, .unit = "color" }, { "seed", "set random seed", OFFSET(seed), AV_OPT_TYPE_INT64, {.i64 = -1}, -1, UINT_MAX, FLAGS }, { "s", "set random seed", OFFSET(seed), AV_OPT_TYPE_INT64, {.i64 = -1}, -1, UINT_MAX, FLAGS }, { "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS }, { "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, INT_MAX, FLAGS }, + { "density", "set density", OFFSET(density), AV_OPT_TYPE_DOUBLE, {.dbl = 0.05}, 0., 1., FLAGS }, {NULL} }; @@ -101,12 +104,12 @@ static av_cold int query_formats(AVFilterContext *ctx) return ff_set_common_samplerates_from_list(ctx, sample_rates); } -static double white_filter(double white, double *buf, double ha) +static double white_filter(double white, double *buf) { return white; } -static double pink_filter(double white, double *buf, double ha) +static double pink_filter(double white, double *buf) { double pink; @@ -122,7 +125,7 @@ static double pink_filter(double white, double *buf, double ha) return pink * 0.11; } -static double blue_filter(double white, double *buf, double ha) +static double blue_filter(double white, double *buf) { double blue; @@ -138,7 +141,7 @@ static double blue_filter(double white, double *buf, double ha) return blue * 0.11; } -static double brown_filter(double white, double *buf, double ha) +static double brown_filter(double white, double *buf) { double brown; @@ -147,7 +150,7 @@ static double brown_filter(double white, double *buf, double ha) return brown * 3.5; } -static double violet_filter(double white, double *buf, double ha) +static double violet_filter(double white, double *buf) { double violet; @@ -156,9 +159,10 @@ static double violet_filter(double white, double *buf, double ha) return violet * 3.5; } -static double velvet_filter(double white, double *buf, double ha) +static double velvet_filter(double white, double *buf) { - return 2. * ha * ((white > ha) - (white < -ha)); + double awhite = fabs(white); + return FFDIFFSIGN(white, 0.0) * buf[1] * (awhite < buf[0]); } static av_cold int config_props(AVFilterLink *outlink) @@ -180,7 +184,9 @@ static av_cold int config_props(AVFilterLink *outlink) case NM_BROWN: s->filter = brown_filter; break; case NM_BLUE: s->filter = blue_filter; break; case NM_VIOLET: s->filter = violet_filter; break; - case NM_VELVET: s->filter = velvet_filter; break; + case NM_VELVET: s->buf[0] = s->amplitude * s->density; + s->buf[1] = s->amplitude; + s->filter = velvet_filter; break; } return 0; @@ -213,7 +219,7 @@ static int activate(AVFilterContext *ctx) for (i = 0; i < nb_samples; i++) { double white; white = s->amplitude * ((2 * ((double) av_lfg_get(&s->c) / 0xffffffff)) - 1); - dst[i] = s->filter(white, s->buf, s->amplitude * 0.5); + dst[i] = s->filter(white, s->buf); } if (!s->infinite) diff --git a/libavfilter/asrc_anullsrc.c b/libavfilter/asrc_anullsrc.c index c071d8aa116..a48728eaf04 100644 --- a/libavfilter/asrc_anullsrc.c +++ b/libavfilter/asrc_anullsrc.c @@ -33,13 +33,12 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" typedef struct ANullContext { const AVClass *class; - char *channel_layout_str; AVChannelLayout ch_layout; - char *sample_rate_str; int sample_rate; int64_t duration; int nb_samples; ///< number of samples per requested frame @@ -50,10 +49,10 @@ typedef struct ANullContext { #define FLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM static const AVOption anullsrc_options[]= { - { "channel_layout", "set channel_layout", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, {.str = "stereo"}, 0, 0, FLAGS }, - { "cl", "set channel_layout", OFFSET(channel_layout_str), AV_OPT_TYPE_STRING, {.str = "stereo"}, 0, 0, FLAGS }, - { "sample_rate", "set sample rate", OFFSET(sample_rate_str) , AV_OPT_TYPE_STRING, {.str = "44100"}, 0, 0, FLAGS }, - { "r", "set sample rate", OFFSET(sample_rate_str) , AV_OPT_TYPE_STRING, {.str = "44100"}, 0, 0, FLAGS }, + { "channel_layout", "set channel_layout", OFFSET(ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str = "stereo"}, 0, 0, FLAGS }, + { "cl", "set channel_layout", OFFSET(ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str = "stereo"}, 0, 0, FLAGS }, + { "sample_rate", "set sample rate", OFFSET(sample_rate) , AV_OPT_TYPE_INT, {.i64 = 44100}, 1, INT_MAX, FLAGS }, + { "r", "set sample rate", OFFSET(sample_rate) , AV_OPT_TYPE_INT, {.i64 = 44100}, 1, INT_MAX, FLAGS }, { "nb_samples", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, UINT16_MAX, FLAGS }, { "n", "set the number of samples per requested frame", OFFSET(nb_samples), AV_OPT_TYPE_INT, {.i64 = 1024}, 1, UINT16_MAX, FLAGS }, { "duration", "set the audio duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS }, @@ -63,22 +62,6 @@ static const AVOption anullsrc_options[]= { AVFILTER_DEFINE_CLASS(anullsrc); -static av_cold int init(AVFilterContext *ctx) -{ - ANullContext *null = ctx->priv; - int ret; - - if ((ret = ff_parse_sample_rate(&null->sample_rate, - null->sample_rate_str, ctx)) < 0) - return ret; - - if ((ret = ff_parse_channel_layout(&null->ch_layout, NULL, - null->channel_layout_str, ctx)) < 0) - return ret; - - return 0; -} - static int query_formats(AVFilterContext *ctx) { ANullContext *null = ctx->priv; @@ -128,12 +111,6 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static av_cold void uninit(AVFilterContext *ctx) -{ - ANullContext *s = ctx->priv; - av_channel_layout_uninit(&s->ch_layout); -} - static const AVFilterPad avfilter_asrc_anullsrc_outputs[] = { { .name = "default", @@ -145,8 +122,6 @@ static const AVFilterPad avfilter_asrc_anullsrc_outputs[] = { const AVFilter ff_asrc_anullsrc = { .name = "anullsrc", .description = NULL_IF_CONFIG_SMALL("Null audio source, return empty audio frames."), - .init = init, - .uninit = uninit, .priv_size = sizeof(ANullContext), .inputs = NULL, FILTER_OUTPUTS(avfilter_asrc_anullsrc_outputs), diff --git a/libavfilter/asrc_flite.c b/libavfilter/asrc_flite.c index 5fab8086bac..6055db52b9c 100644 --- a/libavfilter/asrc_flite.c +++ b/libavfilter/asrc_flite.c @@ -24,11 +24,14 @@ */ #include +#include "libavutil/audio_fifo.h" +#include "libavutil/avstring.h" #include "libavutil/channel_layout.h" #include "libavutil/file.h" #include "libavutil/opt.h" #include "libavutil/thread.h" #include "avfilter.h" +#include "filters.h" #include "audio.h" #include "formats.h" #include "internal.h" @@ -38,11 +41,14 @@ typedef struct FliteContext { char *voice_str; char *textfile; char *text; - cst_wave *wave; - int16_t *wave_samples; - int wave_nb_samples; + char *text_p; + char *text_saveptr; + int nb_channels; + int sample_rate; + AVAudioFifo *fifo; int list_voices; cst_voice *voice; + cst_audio_streaming_info *asi; struct voice_entry *voice_entry; int64_t pts; int frame_nb_samples; ///< number of samples per frame @@ -139,10 +145,30 @@ static int select_voice(struct voice_entry **entry_ret, const char *voice_name, return AVERROR(EINVAL); } +static int audio_stream_chunk_by_word(const cst_wave *wave, int start, int size, + int last, cst_audio_streaming_info *asi) +{ + FliteContext *flite = asi->userdata; + void *const ptr[8] = { &wave->samples[start] }; + + flite->nb_channels = wave->num_channels; + flite->sample_rate = wave->sample_rate; + if (!flite->fifo) { + flite->fifo = av_audio_fifo_alloc(AV_SAMPLE_FMT_S16, flite->nb_channels, size); + if (!flite->fifo) + return CST_AUDIO_STREAM_STOP; + } + + av_audio_fifo_write(flite->fifo, ptr, size); + + return CST_AUDIO_STREAM_CONT; +} + static av_cold int init(AVFilterContext *ctx) { FliteContext *flite = ctx->priv; int ret = 0; + char *text; if (flite->list_voices) { list_voices(ctx, "\n"); @@ -196,10 +222,21 @@ static av_cold int init(AVFilterContext *ctx) return AVERROR(EINVAL); } - /* synth all the file data in block */ - flite->wave = flite_text_to_wave(flite->text, flite->voice); - flite->wave_samples = flite->wave->samples; - flite->wave_nb_samples = flite->wave->num_samples; + flite->asi = new_audio_streaming_info(); + if (!flite->asi) + return AVERROR_BUG; + + flite->asi->asc = audio_stream_chunk_by_word; + flite->asi->userdata = flite; + feat_set(flite->voice->features, "streaming_info", audio_streaming_info_val(flite->asi)); + + flite->text_p = flite->text; + if (!(text = av_strtok(flite->text_p, "\n", &flite->text_saveptr))) + return AVERROR(EINVAL); + flite->text_p = NULL; + + flite_text_to_speech(text, flite->voice, "none"); + return 0; } @@ -215,8 +252,7 @@ static av_cold void uninit(AVFilterContext *ctx) } pthread_mutex_unlock(&flite_mutex); } - delete_wave(flite->wave); - flite->wave = NULL; + av_audio_fifo_free(flite->fifo); } static int query_formats(AVFilterContext *ctx) @@ -229,13 +265,13 @@ static int query_formats(AVFilterContext *ctx) AVFilterFormats *sample_rates = NULL; AVChannelLayout chlayout = { 0 }; - av_channel_layout_default(&chlayout, flite->wave->num_channels); + av_channel_layout_default(&chlayout, flite->nb_channels); if ((ret = ff_add_channel_layout (&chlayouts , &chlayout )) < 0 || (ret = ff_set_common_channel_layouts (ctx , chlayouts )) < 0 || (ret = ff_add_format (&sample_formats, AV_SAMPLE_FMT_S16 )) < 0 || (ret = ff_set_common_formats (ctx , sample_formats )) < 0 || - (ret = ff_add_format (&sample_rates , flite->wave->sample_rate)) < 0 || + (ret = ff_add_format (&sample_rates , flite->sample_rate )) < 0 || (ret = ff_set_common_samplerates (ctx , sample_rates )) < 0) return ret; @@ -247,36 +283,50 @@ static int config_props(AVFilterLink *outlink) AVFilterContext *ctx = outlink->src; FliteContext *flite = ctx->priv; - outlink->sample_rate = flite->wave->sample_rate; - outlink->time_base = (AVRational){1, flite->wave->sample_rate}; + outlink->sample_rate = flite->sample_rate; + outlink->time_base = (AVRational){1, flite->sample_rate}; av_log(ctx, AV_LOG_VERBOSE, "voice:%s fmt:%s sample_rate:%d\n", flite->voice_str, av_get_sample_fmt_name(outlink->format), outlink->sample_rate); + return 0; } -static int request_frame(AVFilterLink *outlink) +static int activate(AVFilterContext *ctx) { + AVFilterLink *outlink = ctx->outputs[0]; + FliteContext *flite = ctx->priv; AVFrame *samplesref; - FliteContext *flite = outlink->src->priv; - int nb_samples = FFMIN(flite->wave_nb_samples, flite->frame_nb_samples); + int nb_samples; + + if (!ff_outlink_frame_wanted(outlink)) + return FFERROR_NOT_READY; + + nb_samples = FFMIN(av_audio_fifo_size(flite->fifo), flite->frame_nb_samples); + if (!nb_samples) { + char *text; - if (!nb_samples) - return AVERROR_EOF; + if (!(text = av_strtok(flite->text_p, "\n", &flite->text_saveptr))) { + ff_outlink_set_status(outlink, AVERROR_EOF, flite->pts); + return 0; + } + + flite_text_to_speech(text, flite->voice, "none"); + ff_filter_set_ready(ctx, 100); + return 0; + } samplesref = ff_get_audio_buffer(outlink, nb_samples); if (!samplesref) return AVERROR(ENOMEM); - memcpy(samplesref->data[0], flite->wave_samples, - nb_samples * flite->wave->num_channels * 2); + av_audio_fifo_read(flite->fifo, (void **)samplesref->extended_data, + nb_samples); + samplesref->pts = flite->pts; - samplesref->pkt_pos = -1; - samplesref->sample_rate = flite->wave->sample_rate; + samplesref->sample_rate = flite->sample_rate; flite->pts += nb_samples; - flite->wave_samples += nb_samples * flite->wave->num_channels; - flite->wave_nb_samples -= nb_samples; return ff_filter_frame(outlink, samplesref); } @@ -286,7 +336,6 @@ static const AVFilterPad flite_outputs[] = { .name = "default", .type = AVMEDIA_TYPE_AUDIO, .config_props = config_props, - .request_frame = request_frame, }, }; @@ -296,6 +345,7 @@ const AVFilter ff_asrc_flite = { .init = init, .uninit = uninit, .priv_size = sizeof(FliteContext), + .activate = activate, .inputs = NULL, FILTER_OUTPUTS(flite_outputs), FILTER_QUERY_FUNC(query_formats), diff --git a/libavfilter/asrc_hilbert.c b/libavfilter/asrc_hilbert.c index c8e84354674..98248e7e4e7 100644 --- a/libavfilter/asrc_hilbert.c +++ b/libavfilter/asrc_hilbert.c @@ -22,6 +22,7 @@ #include "libavutil/opt.h" #include "audio.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "filters.h" #include "window_func.h" diff --git a/libavfilter/asrc_sinc.c b/libavfilter/asrc_sinc.c index 258f7a139e8..6366fb802bb 100644 --- a/libavfilter/asrc_sinc.c +++ b/libavfilter/asrc_sinc.c @@ -27,6 +27,7 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" typedef struct SincContext { @@ -91,27 +92,12 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_samplerates_from_list(ctx, sample_rates); } -static float bessel_I_0(float x) -{ - float term = 1, sum = 1, last_sum, x2 = x / 2; - int i = 1; - - do { - float y = x2 / i++; - - last_sum = sum; - sum += term *= y * y; - } while (sum != last_sum); - - return sum; -} - static float *make_lpf(int num_taps, float Fc, float beta, float rho, float scale, int dc_norm) { int i, m = num_taps - 1; float *h = av_calloc(num_taps, sizeof(*h)), sum = 0; - float mult = scale / bessel_I_0(beta), mult1 = 1.f / (.5f * m + rho); + float mult = scale / av_bessel_i0(beta), mult1 = 1.f / (.5f * m + rho); if (!h) return NULL; @@ -121,7 +107,7 @@ static float *make_lpf(int num_taps, float Fc, float beta, float rho, for (i = 0; i <= m / 2; i++) { float z = i - .5f * m, x = z * M_PI, y = z * mult1; h[i] = x ? sinf(Fc * x) / x : Fc; - sum += h[i] *= bessel_I_0(beta * sqrtf(1.f - y * y)) * mult; + sum += h[i] *= av_bessel_i0(beta * sqrtf(1.f - y * y)) * mult; if (m - i != i) { h[m - i] = h[i]; sum += h[i]; @@ -216,7 +202,7 @@ static float safe_log(float x) static int fir_to_phase(SincContext *s, float **h, int *len, int *post_len, float phase) { float *pi_wraps, *work, phase1 = (phase > 50.f ? 100.f - phase : phase) / 50.f; - int i, work_len, begin, end, imp_peak = 0, peak = 0; + int i, work_len, begin, end, imp_peak = 0, peak = 0, ret; float imp_sum = 0, peak_imp_sum = 0, scale = 1.f; float prev_angle2 = 0, cum_2pi = 0, prev_angle1 = 0, cum_1pi = 0; @@ -232,12 +218,12 @@ static int fir_to_phase(SincContext *s, float **h, int *len, int *post_len, floa av_tx_uninit(&s->tx); av_tx_uninit(&s->itx); - av_tx_init(&s->tx, &s->tx_fn, AV_TX_FLOAT_RDFT, 0, work_len, &scale, AV_TX_INPLACE); - av_tx_init(&s->itx, &s->itx_fn, AV_TX_FLOAT_RDFT, 1, work_len, &scale, AV_TX_INPLACE); - if (!s->tx || !s->itx) { - av_free(work); - return AVERROR(ENOMEM); - } + ret = av_tx_init(&s->tx, &s->tx_fn, AV_TX_FLOAT_RDFT, 0, work_len, &scale, AV_TX_INPLACE); + if (ret < 0) + goto fail; + ret = av_tx_init(&s->itx, &s->itx_fn, AV_TX_FLOAT_RDFT, 1, work_len, &scale, AV_TX_INPLACE); + if (ret < 0) + goto fail; s->tx_fn(s->tx, work, work, sizeof(float)); /* Cepstral: */ @@ -329,9 +315,10 @@ static int fir_to_phase(SincContext *s, float **h, int *len, int *post_len, floa work_len, pi_wraps[work_len >> 1] / M_PI, peak, peak_imp_sum, imp_peak, work[imp_peak], *len, *post_len, 100.f - 100.f * *post_len / (*len - 1)); +fail: av_free(work); - return 0; + return ret; } static int config_output(AVFilterLink *outlink) diff --git a/libavfilter/asrc_sine.c b/libavfilter/asrc_sine.c index c0d8d2265b3..c576b57fec4 100644 --- a/libavfilter/asrc_sine.c +++ b/libavfilter/asrc_sine.c @@ -27,6 +27,7 @@ #include "audio.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" typedef struct SineContext { diff --git a/libavfilter/audio.c b/libavfilter/audio.c index 4995047249b..22f64d1733f 100644 --- a/libavfilter/audio.c +++ b/libavfilter/audio.c @@ -26,9 +26,17 @@ #include "audio.h" #include "avfilter.h" +#include "avfilter_internal.h" #include "framepool.h" #include "internal.h" +const AVFilterPad ff_audio_default_filterpad[1] = { + { + .name = "default", + .type = AVMEDIA_TYPE_AUDIO, + } +}; + AVFrame *ff_null_get_audio_buffer(AVFilterLink *link, int nb_samples) { return ff_get_audio_buffer(link->dst->outputs[0], nb_samples); @@ -37,20 +45,14 @@ AVFrame *ff_null_get_audio_buffer(AVFilterLink *link, int nb_samples) AVFrame *ff_default_get_audio_buffer(AVFilterLink *link, int nb_samples) { AVFrame *frame = NULL; + FilterLinkInternal *const li = ff_link_internal(link); int channels = link->ch_layout.nb_channels; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - int channel_layout_nb_channels = av_get_channel_layout_nb_channels(link->channel_layout); int align = av_cpu_max_align(); - av_assert0(channels == channel_layout_nb_channels || !channel_layout_nb_channels); -FF_ENABLE_DEPRECATION_WARNINGS -#endif - - if (!link->frame_pool) { - link->frame_pool = ff_frame_pool_audio_init(av_buffer_allocz, channels, - nb_samples, link->format, align); - if (!link->frame_pool) + if (!li->frame_pool) { + li->frame_pool = ff_frame_pool_audio_init(av_buffer_allocz, channels, + nb_samples, link->format, align); + if (!li->frame_pool) return NULL; } else { int pool_channels = 0; @@ -58,7 +60,7 @@ FF_ENABLE_DEPRECATION_WARNINGS int pool_align = 0; enum AVSampleFormat pool_format = AV_SAMPLE_FMT_NONE; - if (ff_frame_pool_get_audio_config(link->frame_pool, + if (ff_frame_pool_get_audio_config(li->frame_pool, &pool_channels, &pool_nb_samples, &pool_format, &pool_align) < 0) { return NULL; @@ -67,24 +69,19 @@ FF_ENABLE_DEPRECATION_WARNINGS if (pool_channels != channels || pool_nb_samples < nb_samples || pool_format != link->format || pool_align != align) { - ff_frame_pool_uninit((FFFramePool **)&link->frame_pool); - link->frame_pool = ff_frame_pool_audio_init(av_buffer_allocz, channels, - nb_samples, link->format, align); - if (!link->frame_pool) + ff_frame_pool_uninit(&li->frame_pool); + li->frame_pool = ff_frame_pool_audio_init(av_buffer_allocz, channels, + nb_samples, link->format, align); + if (!li->frame_pool) return NULL; } } - frame = ff_frame_pool_get(link->frame_pool); + frame = ff_frame_pool_get(li->frame_pool); if (!frame) return NULL; frame->nb_samples = nb_samples; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - frame->channel_layout = link->channel_layout; -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (link->ch_layout.order != AV_CHANNEL_ORDER_UNSPEC && av_channel_layout_copy(&frame->ch_layout, &link->ch_layout) < 0) { av_frame_free(&frame); diff --git a/libavfilter/audio.h b/libavfilter/audio.h index 90709a0ad0f..be90fa347bd 100644 --- a/libavfilter/audio.h +++ b/libavfilter/audio.h @@ -25,6 +25,12 @@ #include "avfilter.h" #include "internal.h" +/** + * An AVFilterPad array whose only entry has name "default" + * and is of type AVMEDIA_TYPE_AUDIO. + */ +extern const AVFilterPad ff_audio_default_filterpad[1]; + /** default handler for get_audio_buffer() for audio inputs */ AVFrame *ff_default_get_audio_buffer(AVFilterLink *link, int nb_samples); diff --git a/libavfilter/avf_abitscope.c b/libavfilter/avf_abitscope.c index 782d57e03a5..60dba21be18 100644 --- a/libavfilter/avf_abitscope.c +++ b/libavfilter/avf_abitscope.c @@ -56,10 +56,10 @@ static const AVOption abitscope_options[] = { { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="1024x256"}, 0, 0, FLAGS }, { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="1024x256"}, 0, 0, FLAGS }, { "colors", "set channels colors", OFFSET(colors), AV_OPT_TYPE_STRING, {.str = "red|green|blue|yellow|orange|lime|pink|magenta|brown" }, 0, 0, FLAGS }, - { "mode", "set output mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, FLAGS, "mode" }, - { "m", "set output mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, FLAGS, "mode" }, - { "bars", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "mode" }, - { "trace", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "mode" }, + { "mode", "set output mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, FLAGS, .unit = "mode" }, + { "m", "set output mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, FLAGS, .unit = "mode" }, + { "bars", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, .unit = "mode" }, + { "trace", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, .unit = "mode" }, { NULL } }; @@ -148,25 +148,29 @@ static int config_output(AVFilterLink *outlink) return 0; } +#define BITCOUNTER(type, depth, one) \ + memset(counter, 0, sizeof(s->counter)); \ + for (int i = 0; i < nb_samples; i++) { \ + const type x = in[i]; \ + for (int j = 0; j < depth && x; j++) \ + counter[j] += !!(x & (one << j)); \ + } + #define BARS(type, depth, one) \ for (int ch = 0; ch < inlink->ch_layout.nb_channels; ch++) { \ + const int nb_samples = insamples->nb_samples; \ const type *in = (const type *)insamples->extended_data[ch]; \ const int w = outpicref->width / inlink->ch_layout.nb_channels; \ const int h = outpicref->height / depth; \ const uint32_t color = AV_RN32(&s->fg[4 * ch]); \ + uint64_t *counter = s->counter; \ \ - memset(s->counter, 0, sizeof(s->counter)); \ - for (int i = 0; i < insamples->nb_samples; i++) { \ - for (int j = 0; j < depth; j++) { \ - if (in[i] & (one << j)) \ - s->counter[j]++; \ - } \ - } \ + BITCOUNTER(type, depth, one) \ \ for (int b = 0; b < depth; b++) { \ for (int j = 1; j < h - 1; j++) { \ uint8_t *dst = outpicref->data[0] + (b * h + j) * outpicref->linesize[0] + w * ch * 4; \ - const int ww = (s->counter[depth - b - 1] / (float)insamples->nb_samples) * (w - 1); \ + const int ww = (counter[depth - b - 1] / (float)nb_samples) * (w - 1); \ \ for (int i = 0; i < ww; i++) { \ AV_WN32(&dst[i * 4], color); \ @@ -177,25 +181,21 @@ static int config_output(AVFilterLink *outlink) #define DO_TRACE(type, depth, one) \ for (int ch = 0; ch < inlink->ch_layout.nb_channels; ch++) { \ + const int nb_samples = insamples->nb_samples; \ const int w = outpicref->width / inlink->ch_layout.nb_channels; \ const type *in = (const type *)insamples->extended_data[ch]; \ + uint64_t *counter = s->counter; \ const int wb = w / depth; \ int wv; \ \ - memset(s->counter, 0, sizeof(s->counter)); \ - for (int i = 0; i < insamples->nb_samples; i++) { \ - for (int j = 0; j < depth; j++) { \ - if (in[i] & (one << j)) \ - s->counter[j]++; \ - } \ - } \ + BITCOUNTER(type, depth, one) \ \ for (int b = 0; b < depth; b++) { \ uint8_t colors[4]; \ uint32_t color; \ uint8_t *dst = outpicref->data[0] + w * ch * 4 + wb * b * 4 + \ s->current_vpos * outpicref->linesize[0]; \ - wv = (s->counter[depth - b - 1] * 255) / insamples->nb_samples; \ + wv = (counter[depth - b - 1] * 255) / nb_samples; \ colors[0] = (wv * s->fg[ch * 4 + 0] + 127) / 255; \ colors[1] = (wv * s->fg[ch * 4 + 1] + 127) / 255; \ colors[2] = (wv * s->fg[ch * 4 + 2] + 127) / 255; \ diff --git a/libavfilter/avf_ahistogram.c b/libavfilter/avf_ahistogram.c index 06490192a57..f80f341e331 100644 --- a/libavfilter/avf_ahistogram.c +++ b/libavfilter/avf_ahistogram.c @@ -65,30 +65,30 @@ typedef struct AudioHistogramContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption ahistogram_options[] = { - { "dmode", "set method to display channels", OFFSET(dmode), AV_OPT_TYPE_INT, {.i64=SINGLE}, 0, NB_DMODES-1, FLAGS, "dmode" }, - { "single", "all channels use single histogram", 0, AV_OPT_TYPE_CONST, {.i64=SINGLE}, 0, 0, FLAGS, "dmode" }, - { "separate", "each channel have own histogram", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, "dmode" }, + { "dmode", "set method to display channels", OFFSET(dmode), AV_OPT_TYPE_INT, {.i64=SINGLE}, 0, NB_DMODES-1, FLAGS, .unit = "dmode" }, + { "single", "all channels use single histogram", 0, AV_OPT_TYPE_CONST, {.i64=SINGLE}, 0, 0, FLAGS, .unit = "dmode" }, + { "separate", "each channel have own histogram", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, .unit = "dmode" }, { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, FLAGS }, { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, FLAGS }, - { "scale", "set display scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=LOG}, LINEAR, NB_SCALES-1, FLAGS, "scale" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, FLAGS, "scale" }, - { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT}, 0, 0, FLAGS, "scale" }, - { "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=CBRT}, 0, 0, FLAGS, "scale" }, - { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "scale" }, - { "rlog", "reverse logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=RLOG}, 0, 0, FLAGS, "scale" }, - { "ascale", "set amplitude scale", OFFSET(ascale), AV_OPT_TYPE_INT, {.i64=ALOG}, LINEAR, NB_ASCALES-1, FLAGS, "ascale" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=ALOG}, 0, 0, FLAGS, "ascale" }, - { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=ALINEAR}, 0, 0, FLAGS, "ascale" }, + { "scale", "set display scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=LOG}, LINEAR, NB_SCALES-1, FLAGS, .unit = "scale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, FLAGS, .unit = "scale" }, + { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT}, 0, 0, FLAGS, .unit = "scale" }, + { "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=CBRT}, 0, 0, FLAGS, .unit = "scale" }, + { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, .unit = "scale" }, + { "rlog", "reverse logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=RLOG}, 0, 0, FLAGS, .unit = "scale" }, + { "ascale", "set amplitude scale", OFFSET(ascale), AV_OPT_TYPE_INT, {.i64=ALOG}, LINEAR, NB_ASCALES-1, FLAGS, .unit = "ascale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=ALOG}, 0, 0, FLAGS, .unit = "ascale" }, + { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=ALINEAR}, 0, 0, FLAGS, .unit = "ascale" }, { "acount", "how much frames to accumulate", OFFSET(count), AV_OPT_TYPE_INT, {.i64=1}, -1, 100, FLAGS }, { "rheight", "set histogram ratio of window height", OFFSET(phisto), AV_OPT_TYPE_FLOAT, {.dbl=0.10}, 0, 1, FLAGS }, - { "slide", "set sonogram sliding", OFFSET(slide), AV_OPT_TYPE_INT, {.i64=REPLACE}, 0, NB_SLIDES-1, FLAGS, "slide" }, - { "replace", "replace old rows with new", 0, AV_OPT_TYPE_CONST, {.i64=REPLACE}, 0, 0, FLAGS, "slide" }, - { "scroll", "scroll from top to bottom", 0, AV_OPT_TYPE_CONST, {.i64=SCROLL}, 0, 0, FLAGS, "slide" }, - { "hmode", "set histograms mode", OFFSET(hmode), AV_OPT_TYPE_INT, {.i64=ABS}, 0, NB_HMODES-1, FLAGS, "hmode" }, - { "abs", "use absolute samples", 0, AV_OPT_TYPE_CONST, {.i64=ABS}, 0, 0, FLAGS, "hmode" }, - { "sign", "use unchanged samples", 0, AV_OPT_TYPE_CONST, {.i64=SIGN},0, 0, FLAGS, "hmode" }, + { "slide", "set sonogram sliding", OFFSET(slide), AV_OPT_TYPE_INT, {.i64=REPLACE}, 0, NB_SLIDES-1, FLAGS, .unit = "slide" }, + { "replace", "replace old rows with new", 0, AV_OPT_TYPE_CONST, {.i64=REPLACE}, 0, 0, FLAGS, .unit = "slide" }, + { "scroll", "scroll from top to bottom", 0, AV_OPT_TYPE_CONST, {.i64=SCROLL}, 0, 0, FLAGS, .unit = "slide" }, + { "hmode", "set histograms mode", OFFSET(hmode), AV_OPT_TYPE_INT, {.i64=ABS}, 0, NB_HMODES-1, FLAGS, .unit = "hmode" }, + { "abs", "use absolute samples", 0, AV_OPT_TYPE_CONST, {.i64=ABS}, 0, 0, FLAGS, .unit = "hmode" }, + { "sign", "use unchanged samples", 0, AV_OPT_TYPE_CONST, {.i64=SIGN},0, 0, FLAGS, .unit = "hmode" }, { NULL } }; @@ -207,6 +207,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; AudioHistogramContext *s = ctx->priv; + const int nb_samples = in->nb_samples; const int H = s->histogram_h; const int w = s->w; int c, y, n, p, bin, ret; @@ -260,7 +261,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) const float *src = (const float *)in->extended_data[c]; uint64_t *achistogram = &s->achistogram[(s->dmode == SINGLE ? 0: c) * w]; - for (n = 0; n < in->nb_samples; n++) { + for (n = 0; n < nb_samples; n++) { bin = s->get_bin(src[n], w); achistogram[bin]++; @@ -270,7 +271,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) uint64_t *shistogram = &s->shistogram[(s->dmode == SINGLE ? 0: c) * w]; const float *src2 = (const float *)s->in[s->first]->extended_data[c]; - for (n = 0; n < in->nb_samples; n++) { + for (n = 0; n < nb_samples; n++) { bin = s->get_bin(src2[n], w); shistogram[bin]++; @@ -283,7 +284,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) const float *src = (const float *)in->extended_data[c]; uint64_t *achistogram = &s->achistogram[(s->dmode == SINGLE ? 0: c) * w]; - for (n = 0; n < in->nb_samples; n++) { + for (n = 0; n < nb_samples; n++) { bin = s->get_bin(src[n], w); achistogram[bin]++; @@ -293,7 +294,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) uint64_t *shistogram = &s->shistogram[(s->dmode == SINGLE ? 0: c) * w]; const float *src2 = (const float *)s->in[s->first]->extended_data[c]; - for (n = 0; n < in->nb_samples; n++) { + for (n = 0; n < nb_samples; n++) { bin = s->get_bin(src2[n], w); shistogram[bin]++; @@ -357,10 +358,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) h = aa * (H - 1); if (s->dmode == SINGLE) { - - for (y = H - h; y < H; y++) { - s->out->data[0][y * s->out->linesize[0] + n] = 255; - s->out->data[3][y * s->out->linesize[0] + n] = 255; + int start = H - h, end = H; + const int linesizey = s->out->linesize[0]; + const int linesizea = s->out->linesize[3]; + uint8_t *dsty = s->out->data[0] + start * linesizey; + uint8_t *dsta = s->out->data[3] + start * linesizea; + + for (y = start; y < end; y++, dsty += linesizey, dsta += linesizea) { + dsty[n] = 255; + dsta[n] = 255; } if (s->h - H > 0) { @@ -372,18 +378,32 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->out->data[3][s->ypos * s->out->linesize[3] + n] = 255; } } else if (s->dmode == SEPARATE) { + int start = H - h, end = H; float *out = &s->combine_buffer[3 * n]; + const int linesizey = s->out->linesize[0]; + const int linesizeu = s->out->linesize[1]; + const int linesizev = s->out->linesize[2]; + const int linesizea = s->out->linesize[3]; + uint8_t *dsty = s->out->data[0] + start * linesizey; + uint8_t *dstu = s->out->data[1] + start * linesizeu; + uint8_t *dstv = s->out->data[2] + start * linesizev; + uint8_t *dsta = s->out->data[3] + start * linesizea; int old; - old = s->out->data[0][(H - h) * s->out->linesize[0] + n]; - for (y = H - h; y < H; y++) { - if (s->out->data[0][y * s->out->linesize[0] + n] != old) + old = dsty[n]; + for (y = start; y < end; y++) { + if (dsty[n] != old) break; - old = s->out->data[0][y * s->out->linesize[0] + n]; - s->out->data[0][y * s->out->linesize[0] + n] = av_clip_uint8(yf); - s->out->data[1][y * s->out->linesize[1] + n] = av_clip_uint8(128.f+uf); - s->out->data[2][y * s->out->linesize[2] + n] = av_clip_uint8(128.f+vf); - s->out->data[3][y * s->out->linesize[3] + n] = 255; + old = dsty[n]; + dsty[n] = av_clip_uint8(yf); + dstu[n] = av_clip_uint8(128.f+uf); + dstv[n] = av_clip_uint8(128.f+vf); + dsta[n] = 255; + + dsty += linesizey; + dstu += linesizeu; + dstv += linesizev; + dsta += linesizea; } out[0] += aa * yf; diff --git a/libavfilter/avf_aphasemeter.c b/libavfilter/avf_aphasemeter.c index bf9f922639b..fac8d7c048f 100644 --- a/libavfilter/avf_aphasemeter.c +++ b/libavfilter/avf_aphasemeter.c @@ -38,7 +38,8 @@ typedef struct AudioPhaseMeterContext { const AVClass *class; - AVFrame *out; + AVFrame *out, *in; + int64_t last_pts; int do_video; int do_phasing_detection; int w, h; @@ -51,6 +52,7 @@ typedef struct AudioPhaseMeterContext { int is_out_phase; int start_mono_presence; int start_out_phase_presence; + int nb_samples; float tolerance; float angle; float phase; @@ -127,14 +129,10 @@ static int config_input(AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; AudioPhaseMeterContext *s = ctx->priv; - int nb_samples; s->duration = av_rescale(s->duration, inlink->sample_rate, AV_TIME_BASE); - if (s->do_video) { - nb_samples = FFMAX(1, av_rescale(inlink->sample_rate, s->frame_rate.den, s->frame_rate.num)); - inlink->min_samples = - inlink->max_samples = nb_samples; - } + if (s->do_video) + s->nb_samples = FFMAX(1, av_rescale(inlink->sample_rate, s->frame_rate.den, s->frame_rate.num)); return 0; } @@ -144,10 +142,13 @@ static int config_video_output(AVFilterLink *outlink) AVFilterContext *ctx = outlink->src; AudioPhaseMeterContext *s = ctx->priv; + s->last_pts = AV_NOPTS_VALUE; + outlink->w = s->w; outlink->h = s->h; outlink->sample_aspect_ratio = (AVRational){1,1}; outlink->frame_rate = s->frame_rate; + outlink->time_base = av_inv_q(outlink->frame_rate); if (!strcmp(s->mpc_str, "none")) s->draw_median_phase = 0; @@ -161,7 +162,7 @@ static int config_video_output(AVFilterLink *outlink) static inline int get_x(float phase, int w) { - return (phase + 1.) / 2. * (w - 1); + return (phase + 1.f) / 2.f * (w - 1.f); } static inline void add_metadata(AVFrame *insamples, const char *key, char *value) @@ -251,15 +252,16 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) int mono_measurement; int out_phase_measurement; float tolerance = 1.0f - s->tolerance; - float angle = cosf(s->angle/180.0f*M_PI); + float angle = cosf(s->angle/180.0f*M_PIf); + int64_t new_pts; if (s->do_video && (!s->out || s->out->width != outlink->w || s->out->height != outlink->h)) { av_frame_free(&s->out); s->out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!s->out) { - av_frame_free(&in); - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail; } out = s->out; @@ -267,10 +269,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) memset(out->data[0] + i * out->linesize[0], 0, outlink->w * 4); } else if (s->do_video) { ret = ff_inlink_make_frame_writable(outlink, &s->out); - if (ret < 0) { - av_frame_free(&in); - return ret; - } + if (ret < 0) + goto fail; out = s->out; for (i = outlink->h - 1; i >= 10; i--) memmove(out->data[0] + (i ) * out->linesize[0], @@ -328,18 +328,59 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) update_out_phase_detection(s, in, out_phase_measurement); } - if (s->do_video) { + if (s->do_video) + new_pts = av_rescale_q(in->pts, inlink->time_base, outlink->time_base); + if (s->do_video && new_pts != s->last_pts) { AVFrame *clone; - s->out->pts = in->pts; - s->out->duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); + s->out->pts = s->last_pts = new_pts; + s->out->duration = 1; clone = av_frame_clone(s->out); - if (!clone) - return AVERROR(ENOMEM); - ff_filter_frame(outlink, clone); + if (!clone) { + ret = AVERROR(ENOMEM); + goto fail; + } + ret = ff_filter_frame(outlink, clone); + if (ret < 0) + goto fail; } + s->in = NULL; return ff_filter_frame(aoutlink, in); +fail: + av_frame_free(&in); + s->in = NULL; + return ret; +} + +static int activate(AVFilterContext *ctx) +{ + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + AudioPhaseMeterContext *s = ctx->priv; + int ret; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + if (s->do_video) + FF_FILTER_FORWARD_STATUS_BACK(ctx->outputs[1], inlink); + + if (!s->in) { + if (s->nb_samples > 0) + ret = ff_inlink_consume_samples(inlink, s->nb_samples, s->nb_samples, &s->in); + else + ret = ff_inlink_consume_frame(inlink, &s->in); + if (ret < 0) + return ret; + if (ret > 0) + return filter_frame(inlink, s->in); + } + + FF_FILTER_FORWARD_STATUS_ALL(inlink, ctx); + FF_FILTER_FORWARD_WANTED(outlink, inlink); + if (s->do_video) + FF_FILTER_FORWARD_WANTED(ctx->outputs[1], inlink); + + return FFERROR_NOT_READY; } static av_cold void uninit(AVFilterContext *ctx) @@ -386,7 +427,6 @@ static const AVFilterPad inputs[] = { .name = "default", .type = AVMEDIA_TYPE_AUDIO, .config_props = config_input, - .filter_frame = filter_frame, }, }; @@ -397,6 +437,7 @@ const AVFilter ff_avf_aphasemeter = { .uninit = uninit, .priv_size = sizeof(AudioPhaseMeterContext), FILTER_INPUTS(inputs), + .activate = activate, .outputs = NULL, FILTER_QUERY_FUNC(query_formats), .priv_class = &aphasemeter_class, diff --git a/libavfilter/avf_avectorscope.c b/libavfilter/avf_avectorscope.c index 6e45fd95755..1b3461d91d5 100644 --- a/libavfilter/avf_avectorscope.c +++ b/libavfilter/avf_avectorscope.c @@ -79,11 +79,11 @@ typedef struct AudioVectorScopeContext { #define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption avectorscope_options[] = { - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=LISSAJOUS}, 0, MODE_NB-1, TFLAGS, "mode" }, - { "m", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=LISSAJOUS}, 0, MODE_NB-1, TFLAGS, "mode" }, - { "lissajous", "", 0, AV_OPT_TYPE_CONST, {.i64=LISSAJOUS}, 0, 0, TFLAGS, "mode" }, - { "lissajous_xy", "", 0, AV_OPT_TYPE_CONST, {.i64=LISSAJOUS_XY}, 0, 0, TFLAGS, "mode" }, - { "polar", "", 0, AV_OPT_TYPE_CONST, {.i64=POLAR}, 0, 0, TFLAGS, "mode" }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=LISSAJOUS}, 0, MODE_NB-1, TFLAGS, .unit = "mode" }, + { "m", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=LISSAJOUS}, 0, MODE_NB-1, TFLAGS, .unit = "mode" }, + { "lissajous", "", 0, AV_OPT_TYPE_CONST, {.i64=LISSAJOUS}, 0, 0, TFLAGS, .unit = "mode" }, + { "lissajous_xy", "", 0, AV_OPT_TYPE_CONST, {.i64=LISSAJOUS_XY}, 0, 0, TFLAGS, .unit = "mode" }, + { "polar", "", 0, AV_OPT_TYPE_CONST, {.i64=POLAR}, 0, 0, TFLAGS, .unit = "mode" }, { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="400x400"}, 0, 0, FLAGS }, @@ -97,21 +97,21 @@ static const AVOption avectorscope_options[] = { { "bf", "set blue fade", OFFSET(fade[2]), AV_OPT_TYPE_INT, {.i64=5}, 0, 255, TFLAGS }, { "af", "set alpha fade", OFFSET(fade[3]), AV_OPT_TYPE_INT, {.i64=5}, 0, 255, TFLAGS }, { "zoom", "set zoom factor", OFFSET(zoom), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 10, TFLAGS }, - { "draw", "set draw mode", OFFSET(draw), AV_OPT_TYPE_INT, {.i64=DOT}, 0, DRAW_NB-1, TFLAGS, "draw" }, - { "dot", "draw dots", 0, AV_OPT_TYPE_CONST, {.i64=DOT} , 0, 0, TFLAGS, "draw" }, - { "line", "draw lines", 0, AV_OPT_TYPE_CONST, {.i64=LINE}, 0, 0, TFLAGS, "draw" }, - { "aaline","draw anti-aliased lines", 0, AV_OPT_TYPE_CONST, {.i64=AALINE},0,0, TFLAGS, "draw" }, - { "scale", "set amplitude scale mode", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=LIN}, 0, SCALE_NB-1, TFLAGS, "scale" }, - { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LIN}, 0, 0, TFLAGS, "scale" }, - { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT}, 0, 0, TFLAGS, "scale" }, - { "cbrt", "cube root", 0, AV_OPT_TYPE_CONST, {.i64=CBRT}, 0, 0, TFLAGS, "scale" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, TFLAGS, "scale" }, + { "draw", "set draw mode", OFFSET(draw), AV_OPT_TYPE_INT, {.i64=DOT}, 0, DRAW_NB-1, TFLAGS, .unit = "draw" }, + { "dot", "draw dots", 0, AV_OPT_TYPE_CONST, {.i64=DOT} , 0, 0, TFLAGS, .unit = "draw" }, + { "line", "draw lines", 0, AV_OPT_TYPE_CONST, {.i64=LINE}, 0, 0, TFLAGS, .unit = "draw" }, + { "aaline","draw anti-aliased lines", 0, AV_OPT_TYPE_CONST, {.i64=AALINE},0,0, TFLAGS, .unit = "draw" }, + { "scale", "set amplitude scale mode", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=LIN}, 0, SCALE_NB-1, TFLAGS, .unit = "scale" }, + { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LIN}, 0, 0, TFLAGS, .unit = "scale" }, + { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT}, 0, 0, TFLAGS, .unit = "scale" }, + { "cbrt", "cube root", 0, AV_OPT_TYPE_CONST, {.i64=CBRT}, 0, 0, TFLAGS, .unit = "scale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, TFLAGS, .unit = "scale" }, { "swap", "swap x axis with y axis", OFFSET(swap), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, TFLAGS }, - { "mirror", "mirror axis", OFFSET(mirror), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, TFLAGS, "mirror" }, - { "none", "no mirror", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, TFLAGS, "mirror" }, - { "x", "mirror x", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, TFLAGS, "mirror" }, - { "y", "mirror y", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, TFLAGS, "mirror" }, - { "xy", "mirror both", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, TFLAGS, "mirror" }, + { "mirror", "mirror axis", OFFSET(mirror), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, TFLAGS, .unit = "mirror" }, + { "none", "no mirror", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, TFLAGS, .unit = "mirror" }, + { "x", "mirror x", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, TFLAGS, .unit = "mirror" }, + { "y", "mirror y", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, TFLAGS, .unit = "mirror" }, + { "xy", "mirror both", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, TFLAGS, .unit = "mirror" }, { NULL } }; diff --git a/libavfilter/avf_concat.c b/libavfilter/avf_concat.c index c85c17b51f0..33edd7a3943 100644 --- a/libavfilter/avf_concat.c +++ b/libavfilter/avf_concat.c @@ -28,6 +28,7 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "video.h" #include "audio.h" @@ -179,6 +180,7 @@ static int push_frame(AVFilterContext *ctx, unsigned in_no, AVFrame *buf) struct concat_in *in = &cat->in[in_no]; buf->pts = av_rescale_q(buf->pts, inlink->time_base, outlink->time_base); + buf->duration = av_rescale_q(buf->duration, inlink->time_base, outlink->time_base); in->pts = buf->pts; in->nb_frames++; /* add duration to input PTS */ diff --git a/libavfilter/avf_showcqt.c b/libavfilter/avf_showcqt.c index 76086477e93..676f77ecb2a 100644 --- a/libavfilter/avf_showcqt.c +++ b/libavfilter/avf_showcqt.c @@ -26,10 +26,14 @@ #include "libavutil/eval.h" #include "libavutil/pixdesc.h" #include "libavutil/time.h" +#include "audio.h" #include "avfilter.h" +#include "filters.h" +#include "formats.h" #include "internal.h" #include "lavfutils.h" #include "lswsutils.h" +#include "video.h" #if CONFIG_LIBFREETYPE #include @@ -51,8 +55,6 @@ "st(1, if(between(ld(0),0,1), 0.5-0.5*cos(2*PI*ld(0)), 0));" \ "r(1-ld(1)) + b(ld(1))" #define CSCHEME "1|0.5|0|0|0.5|1" -#define PTS_STEP 10 -#define PTS_TOLERANCE 1 #define OFFSET(x) offsetof(ShowCQTContext, x) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM) @@ -91,14 +93,14 @@ static const AVOption showcqt_options[] = { { "axisfile", "set axis image", OFFSET(axisfile), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, { "axis", "draw axis", OFFSET(axis), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS }, { "text", "draw axis", OFFSET(axis), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS }, - { "csp", "set color space", OFFSET(csp), AV_OPT_TYPE_INT, { .i64 = AVCOL_SPC_UNSPECIFIED }, 0, INT_MAX, FLAGS, "csp" }, - { "unspecified", "unspecified", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_UNSPECIFIED }, 0, 0, FLAGS, "csp" }, - { "bt709", "bt709", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_BT709 }, 0, 0, FLAGS, "csp" }, - { "fcc", "fcc", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_FCC }, 0, 0, FLAGS, "csp" }, - { "bt470bg", "bt470bg", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_BT470BG }, 0, 0, FLAGS, "csp" }, - { "smpte170m", "smpte170m", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_SMPTE170M }, 0, 0, FLAGS, "csp" }, - { "smpte240m", "smpte240m", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_SMPTE240M }, 0, 0, FLAGS, "csp" }, - { "bt2020ncl", "bt2020ncl", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_BT2020_NCL }, 0, 0, FLAGS, "csp" }, + { "csp", "set color space", OFFSET(csp), AV_OPT_TYPE_INT, { .i64 = AVCOL_SPC_UNSPECIFIED }, 0, INT_MAX, FLAGS, .unit = "csp" }, + { "unspecified", "unspecified", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_UNSPECIFIED }, 0, 0, FLAGS, .unit = "csp" }, + { "bt709", "bt709", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_BT709 }, 0, 0, FLAGS, .unit = "csp" }, + { "fcc", "fcc", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_FCC }, 0, 0, FLAGS, .unit = "csp" }, + { "bt470bg", "bt470bg", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_BT470BG }, 0, 0, FLAGS, .unit = "csp" }, + { "smpte170m", "smpte170m", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_SMPTE170M }, 0, 0, FLAGS, .unit = "csp" }, + { "smpte240m", "smpte240m", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_SMPTE240M }, 0, 0, FLAGS, .unit = "csp" }, + { "bt2020ncl", "bt2020ncl", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_BT2020_NCL }, 0, 0, FLAGS, .unit = "csp" }, { "cscheme", "set color scheme", OFFSET(cscheme), AV_OPT_TYPE_STRING, { .str = CSCHEME }, 0, 0, FLAGS }, { NULL } }; @@ -344,7 +346,7 @@ static int init_cqt(ShowCQTContext *s) } av_expr_free(expr); - av_log(s->ctx, AV_LOG_INFO, "nb_cqt_coeffs = %d.\n", nb_cqt_coeffs); + av_log(s->ctx, AV_LOG_VERBOSE, "nb_cqt_coeffs = %d.\n", nb_cqt_coeffs); return 0; error: @@ -1186,9 +1188,6 @@ static int plot_cqt(AVFilterContext *ctx, AVFrame **frameout) s->draw_sono(out, s->sono_frame, s->bar_h + s->axis_h, s->sono_idx); UPDATE_TIME(s->sono_time); } - out->pts = s->next_pts; - out->duration = PTS_STEP; - s->next_pts += PTS_STEP; } s->sono_count = (s->sono_count + 1) % s->count; if (s->sono_h) @@ -1366,8 +1365,8 @@ static int config_output(AVFilterLink *outlink) s->format = outlink->format; outlink->sample_aspect_ratio = av_make_q(1, 1); outlink->frame_rate = s->rate; - outlink->time_base = av_mul_q(av_inv_q(s->rate), av_make_q(1, PTS_STEP)); - av_log(ctx, AV_LOG_INFO, "video: %dx%d %s %d/%d fps, bar_h = %d, axis_h = %d, sono_h = %d.\n", + outlink->time_base = av_inv_q(s->rate); + av_log(ctx, AV_LOG_VERBOSE, "video: %dx%d %s %d/%d fps, bar_h = %d, axis_h = %d, sono_h = %d.\n", s->width, s->height, av_get_pix_fmt_name(s->format), s->rate.num, s->rate.den, s->bar_h, s->axis_h, s->sono_h); @@ -1380,7 +1379,7 @@ static int config_output(AVFilterLink *outlink) s->fft_bits = FFMAX(ceil(log2(inlink->sample_rate * s->timeclamp)), 4); s->fft_len = 1 << s->fft_bits; - av_log(ctx, AV_LOG_INFO, "fft_len = %d, cqt_len = %d.\n", s->fft_len, s->cqt_len); + av_log(ctx, AV_LOG_VERBOSE, "fft_len = %d, cqt_len = %d.\n", s->fft_len, s->cqt_len); ret = av_tx_init(&s->fft_ctx, &s->tx_fn, AV_TX_FLOAT_FFT, 0, s->fft_len, &scale, 0); s->fft_data = av_calloc(s->fft_len, sizeof(*s->fft_data)); @@ -1470,11 +1469,10 @@ static int config_output(AVFilterLink *outlink) s->step = (int)(s->step_frac.num / s->step_frac.den); s->step_frac.num %= s->step_frac.den; if (s->step_frac.num) { - av_log(ctx, AV_LOG_INFO, "audio: %d Hz, step = %d + %d/%d.\n", + av_log(ctx, AV_LOG_VERBOSE, "audio: %d Hz, step = %d + %d/%d.\n", inlink->sample_rate, s->step, s->step_frac.num, s->step_frac.den); - av_log(ctx, AV_LOG_WARNING, "fractional step.\n"); } else { - av_log(ctx, AV_LOG_INFO, "audio: %d Hz, step = %d.\n", + av_log(ctx, AV_LOG_VERBOSE, "audio: %d Hz, step = %d.\n", inlink->sample_rate, s->step); } @@ -1487,7 +1485,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; ShowCQTContext *s = ctx->priv; - int remaining, step, ret, x, i, j, m; + int remaining, step, ret, x, i, j, m, got_frame = 0; float *audio_data; AVFrame *out = NULL; @@ -1503,11 +1501,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) for (x = 0; x < (s->fft_len/2 + s->remaining_fill_max - step); x++) s->fft_data[x] = s->fft_data[x+step]; s->remaining_fill += step; + s->next_pts++; - if (out) + if (out) { + out->pts = s->next_pts; + out->duration = 1; return ff_filter_frame(outlink, out); + } } - return AVERROR_EOF; + return 0; } remaining = insamples->nb_samples; @@ -1528,16 +1530,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) } remaining -= s->remaining_fill; if (out) { - int64_t pts = av_rescale_q(insamples->pts, inlink->time_base, av_make_q(1, inlink->sample_rate)); - pts += insamples->nb_samples - remaining - s->remaining_fill_max; - pts = av_rescale_q(pts, av_make_q(1, inlink->sample_rate), outlink->time_base); - if (FFABS(pts - out->pts) > PTS_TOLERANCE) { - av_log(ctx, AV_LOG_DEBUG, "changing pts from %"PRId64" (%.3f) to %"PRId64" (%.3f).\n", - out->pts, out->pts * av_q2d(outlink->time_base), - pts, pts * av_q2d(outlink->time_base)); - out->pts = pts; - s->next_pts = pts + PTS_STEP; - } + int64_t pts = av_rescale_q(insamples->nb_samples - remaining - s->remaining_fill_max, + av_make_q(1, inlink->sample_rate), inlink->time_base); + out->pts = av_rescale_q(insamples->pts + pts, inlink->time_base, outlink->time_base); + out->duration = 1; + got_frame = 1; ret = ff_filter_frame(outlink, out); if (ret < 0) { av_frame_free(&insamples); @@ -1559,35 +1556,49 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) remaining = 0; } } + if (!got_frame) + ff_filter_set_ready(ctx, 100); av_frame_free(&insamples); return 0; } -static int request_frame(AVFilterLink *outlink) +static int activate(AVFilterContext *ctx) { - AVFilterLink *inlink = outlink->src->inputs[0]; - int ret; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + ShowCQTContext *s = ctx->priv; + int nb_samples, ret, status; + int64_t pts; + AVFrame *in; - ret = ff_request_frame(inlink); - if (ret == AVERROR_EOF) - ret = filter_frame(inlink, NULL); - return ret; -} + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); -static const AVFilterPad showcqt_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = filter_frame, - }, -}; + nb_samples = s->step + (s->step_frac.num + s->remaining_frac) / s->step_frac.den; + ret = ff_inlink_consume_samples(inlink, nb_samples, nb_samples, &in); + if (ret < 0) + return ret; + if (ret > 0) + return filter_frame(inlink, in); + + if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { + if (status == AVERROR_EOF) { + s->next_pts = av_rescale_q(pts, inlink->time_base, outlink->time_base); + ret = filter_frame(inlink, NULL); + ff_outlink_set_status(outlink, AVERROR_EOF, s->next_pts); + return ret; + } + } + + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} static const AVFilterPad showcqt_outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, .config_props = config_output, - .request_frame = request_frame, }, }; @@ -1595,9 +1606,10 @@ const AVFilter ff_avf_showcqt = { .name = "showcqt", .description = NULL_IF_CONFIG_SMALL("Convert input audio to a CQT (Constant/Clamped Q Transform) spectrum video output."), .init = init, + .activate = activate, .uninit = uninit, .priv_size = sizeof(ShowCQTContext), - FILTER_INPUTS(showcqt_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(showcqt_outputs), FILTER_QUERY_FUNC(query_formats), .priv_class = &showcqt_class, diff --git a/libavfilter/avf_showcwt.c b/libavfilter/avf_showcwt.c index ed1bc4c65ae..c498f0a7bfc 100644 --- a/libavfilter/avf_showcwt.c +++ b/libavfilter/avf_showcwt.c @@ -25,10 +25,12 @@ #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/channel_layout.h" +#include "libavutil/float_dsp.h" #include "libavutil/cpu.h" #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "audio.h" +#include "formats.h" #include "video.h" #include "avfilter.h" #include "filters.h" @@ -36,13 +38,26 @@ enum FrequencyScale { FSCALE_LINEAR, - FSCALE_LOG2, + FSCALE_LOG, FSCALE_BARK, FSCALE_MEL, FSCALE_ERBS, + FSCALE_SQRT, + FSCALE_CBRT, + FSCALE_QDRT, + FSCALE_FM, NB_FSCALE }; +enum IntensityScale { + ISCALE_LOG, + ISCALE_LINEAR, + ISCALE_SQRT, + ISCALE_CBRT, + ISCALE_QDRT, + NB_ISCALE +}; + enum DirectionMode { DIRECTION_LR, DIRECTION_RL, @@ -65,30 +80,28 @@ typedef struct ShowCWTContext { char *rate_str; AVRational auto_frame_rate; AVRational frame_rate; - AVTXContext **fft; - AVTXContext **ifft; - av_tx_fn tx_fn; - av_tx_fn itx_fn; - int fft_in_size; - int fft_out_size; - int ifft_in_size; - int ifft_out_size; + AVTXContext **fft, **ifft; + av_tx_fn tx_fn, itx_fn; + int fft_size, ifft_size; int pos; int64_t in_pts; int64_t old_pts; int64_t eof_pts; float *frequency_band; - AVFrame *kernel; + AVComplexFloat **kernel; unsigned *index; - int *kernel_start; - int *kernel_stop; - AVFrame *cache[2]; + int *kernel_start, *kernel_stop; + AVFrame *cache; AVFrame *outpicref; AVFrame *fft_in; AVFrame *fft_out; + AVFrame *dst_x; + AVFrame *src_x; AVFrame *ifft_in; AVFrame *ifft_out; AVFrame *ch_out; + AVFrame *over; + AVFrame *bh_out; int nb_threads; int nb_channels; int nb_consumed_samples; @@ -97,20 +110,23 @@ typedef struct ShowCWTContext { int slide; int new_frame; int direction; - int hop_size; - int hop_index; - int ihop_size; - int ihop_index; - int input_padding_size; - int input_sample_count; - int output_padding_size; - int output_sample_count; + int hop_size, ihop_size; + int hop_index, ihop_index; + int input_padding_size, output_padding_size; + int input_sample_count, output_sample_count; int frequency_band_count; float logarithmic_basis; + int intensity_scale; int frequency_scale; - float minimum_frequency; - float maximum_frequency; + float minimum_frequency, maximum_frequency; + float minimum_intensity, maximum_intensity; float deviation; + float bar_ratio; + int bar_size; + int sono_size; + float rotation; + + AVFloatDSPContext *fdsp; } ShowCWTContext; #define OFFSET(x) offsetof(ShowCWTContext, x) @@ -121,32 +137,46 @@ static const AVOption showcwt_options[] = { { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x512"}, 0, 0, FLAGS }, { "rate", "set video rate", OFFSET(rate_str), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0, FLAGS }, { "r", "set video rate", OFFSET(rate_str), AV_OPT_TYPE_STRING, {.str = "25"}, 0, 0, FLAGS }, - { "scale", "set frequency scale", OFFSET(frequency_scale), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_FSCALE-1, FLAGS, "scale" }, - { "linear", "linear", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_LINEAR}, 0, 0, FLAGS, "scale" }, - { "log2", "logarithmic", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_LOG2}, 0, 0, FLAGS, "scale" }, - { "bark", "bark", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_BARK}, 0, 0, FLAGS, "scale" }, - { "mel", "mel", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_MEL}, 0, 0, FLAGS, "scale" }, - { "erbs", "erbs", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_ERBS}, 0, 0, FLAGS, "scale" }, - { "min", "set minimum frequency", OFFSET(minimum_frequency), AV_OPT_TYPE_FLOAT, {.dbl = 20.}, 1, 2000, FLAGS }, - { "max", "set maximum frequency", OFFSET(maximum_frequency), AV_OPT_TYPE_FLOAT, {.dbl = 20000.}, 0, 192000, FLAGS }, + { "scale", "set frequency scale", OFFSET(frequency_scale), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_FSCALE-1, FLAGS, .unit = "scale" }, + { "linear", "linear", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_LINEAR}, 0, 0, FLAGS, .unit = "scale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_LOG}, 0, 0, FLAGS, .unit = "scale" }, + { "bark", "bark", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_BARK}, 0, 0, FLAGS, .unit = "scale" }, + { "mel", "mel", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_MEL}, 0, 0, FLAGS, .unit = "scale" }, + { "erbs", "erbs", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_ERBS}, 0, 0, FLAGS, .unit = "scale" }, + { "sqrt", "sqrt", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_SQRT}, 0, 0, FLAGS, .unit = "scale" }, + { "cbrt", "cbrt", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_CBRT}, 0, 0, FLAGS, .unit = "scale" }, + { "qdrt", "qdrt", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_QDRT}, 0, 0, FLAGS, .unit = "scale" }, + { "fm", "fm", 0, AV_OPT_TYPE_CONST,{.i64=FSCALE_FM}, 0, 0, FLAGS, .unit = "scale" }, + { "iscale", "set intensity scale", OFFSET(intensity_scale),AV_OPT_TYPE_INT, {.i64=0}, 0, NB_ISCALE-1, FLAGS, .unit = "iscale" }, + { "linear", "linear", 0, AV_OPT_TYPE_CONST,{.i64=ISCALE_LINEAR}, 0, 0, FLAGS, .unit = "iscale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST,{.i64=ISCALE_LOG}, 0, 0, FLAGS, .unit = "iscale" }, + { "sqrt", "sqrt", 0, AV_OPT_TYPE_CONST,{.i64=ISCALE_SQRT}, 0, 0, FLAGS, .unit = "iscale" }, + { "cbrt", "cbrt", 0, AV_OPT_TYPE_CONST,{.i64=ISCALE_CBRT}, 0, 0, FLAGS, .unit = "iscale" }, + { "qdrt", "qdrt", 0, AV_OPT_TYPE_CONST,{.i64=ISCALE_QDRT}, 0, 0, FLAGS, .unit = "iscale" }, + { "min", "set minimum frequency", OFFSET(minimum_frequency), AV_OPT_TYPE_FLOAT, {.dbl = 20.}, 1, 192000, FLAGS }, + { "max", "set maximum frequency", OFFSET(maximum_frequency), AV_OPT_TYPE_FLOAT, {.dbl = 20000.}, 1, 192000, FLAGS }, + { "imin", "set minimum intensity", OFFSET(minimum_intensity), AV_OPT_TYPE_FLOAT, {.dbl = 0.}, 0, 1, FLAGS }, + { "imax", "set maximum intensity", OFFSET(maximum_intensity), AV_OPT_TYPE_FLOAT, {.dbl = 1.}, 0, 1, FLAGS }, { "logb", "set logarithmic basis", OFFSET(logarithmic_basis), AV_OPT_TYPE_FLOAT, {.dbl = 0.0001}, 0, 1, FLAGS }, - { "deviation", "set frequency deviation", OFFSET(deviation), AV_OPT_TYPE_FLOAT, {.dbl = 1.}, 0, 10, FLAGS }, + { "deviation", "set frequency deviation", OFFSET(deviation), AV_OPT_TYPE_FLOAT, {.dbl = 1.}, 0, 100, FLAGS }, { "pps", "set pixels per second", OFFSET(pps), AV_OPT_TYPE_INT, {.i64 = 64}, 1, 1024, FLAGS }, - { "mode", "set output mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 4, FLAGS, "mode" }, - { "magnitude", "magnitude", 0, AV_OPT_TYPE_CONST,{.i64=0}, 0, 0, FLAGS, "mode" }, - { "phase", "phase", 0, AV_OPT_TYPE_CONST,{.i64=1}, 0, 0, FLAGS, "mode" }, - { "magphase", "magnitude+phase", 0, AV_OPT_TYPE_CONST,{.i64=2}, 0, 0, FLAGS, "mode" }, - { "channel", "color per channel", 0, AV_OPT_TYPE_CONST,{.i64=3}, 0, 0, FLAGS, "mode" }, - { "stereo", "stereo difference", 0, AV_OPT_TYPE_CONST,{.i64=4}, 0, 0, FLAGS, "mode" }, - { "slide", "set slide mode", OFFSET(slide), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_SLIDE-1, FLAGS, "slide" }, - { "replace", "replace", 0, AV_OPT_TYPE_CONST,{.i64=SLIDE_REPLACE},0, 0, FLAGS, "slide" }, - { "scroll", "scroll", 0, AV_OPT_TYPE_CONST,{.i64=SLIDE_SCROLL}, 0, 0, FLAGS, "slide" }, - { "frame", "frame", 0, AV_OPT_TYPE_CONST,{.i64=SLIDE_FRAME}, 0, 0, FLAGS, "slide" }, - { "direction", "set direction mode", OFFSET(direction), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_DIRECTION-1, FLAGS, "direction" }, - { "lr", "left to right", 0, AV_OPT_TYPE_CONST,{.i64=DIRECTION_LR}, 0, 0, FLAGS, "direction" }, - { "rl", "right to left", 0, AV_OPT_TYPE_CONST,{.i64=DIRECTION_RL}, 0, 0, FLAGS, "direction" }, - { "ud", "up to down", 0, AV_OPT_TYPE_CONST,{.i64=DIRECTION_UD}, 0, 0, FLAGS, "direction" }, - { "du", "down to up", 0, AV_OPT_TYPE_CONST,{.i64=DIRECTION_DU}, 0, 0, FLAGS, "direction" }, + { "mode", "set output mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 4, FLAGS, .unit = "mode" }, + { "magnitude", "magnitude", 0, AV_OPT_TYPE_CONST,{.i64=0}, 0, 0, FLAGS, .unit = "mode" }, + { "phase", "phase", 0, AV_OPT_TYPE_CONST,{.i64=1}, 0, 0, FLAGS, .unit = "mode" }, + { "magphase", "magnitude+phase", 0, AV_OPT_TYPE_CONST,{.i64=2}, 0, 0, FLAGS, .unit = "mode" }, + { "channel", "color per channel", 0, AV_OPT_TYPE_CONST,{.i64=3}, 0, 0, FLAGS, .unit = "mode" }, + { "stereo", "stereo difference", 0, AV_OPT_TYPE_CONST,{.i64=4}, 0, 0, FLAGS, .unit = "mode" }, + { "slide", "set slide mode", OFFSET(slide), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_SLIDE-1, FLAGS, .unit = "slide" }, + { "replace", "replace", 0, AV_OPT_TYPE_CONST,{.i64=SLIDE_REPLACE},0, 0, FLAGS, .unit = "slide" }, + { "scroll", "scroll", 0, AV_OPT_TYPE_CONST,{.i64=SLIDE_SCROLL}, 0, 0, FLAGS, .unit = "slide" }, + { "frame", "frame", 0, AV_OPT_TYPE_CONST,{.i64=SLIDE_FRAME}, 0, 0, FLAGS, .unit = "slide" }, + { "direction", "set direction mode", OFFSET(direction), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_DIRECTION-1, FLAGS, .unit = "direction" }, + { "lr", "left to right", 0, AV_OPT_TYPE_CONST,{.i64=DIRECTION_LR}, 0, 0, FLAGS, .unit = "direction" }, + { "rl", "right to left", 0, AV_OPT_TYPE_CONST,{.i64=DIRECTION_RL}, 0, 0, FLAGS, .unit = "direction" }, + { "ud", "up to down", 0, AV_OPT_TYPE_CONST,{.i64=DIRECTION_UD}, 0, 0, FLAGS, .unit = "direction" }, + { "du", "down to up", 0, AV_OPT_TYPE_CONST,{.i64=DIRECTION_DU}, 0, 0, FLAGS, .unit = "direction" }, + { "bar", "set bargraph ratio", OFFSET(bar_ratio), AV_OPT_TYPE_FLOAT, {.dbl = 0.}, 0, 1, FLAGS }, + { "rotation", "set color rotation", OFFSET(rotation), AV_OPT_TYPE_FLOAT, {.dbl = 0}, -1, 1, FLAGS }, { NULL } }; @@ -161,15 +191,17 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->kernel_stop); av_freep(&s->index); - av_frame_free(&s->kernel); - av_frame_free(&s->cache[0]); - av_frame_free(&s->cache[1]); + av_frame_free(&s->cache); av_frame_free(&s->outpicref); av_frame_free(&s->fft_in); av_frame_free(&s->fft_out); + av_frame_free(&s->dst_x); + av_frame_free(&s->src_x); av_frame_free(&s->ifft_in); av_frame_free(&s->ifft_out); av_frame_free(&s->ch_out); + av_frame_free(&s->over); + av_frame_free(&s->bh_out); if (s->fft) { for (int n = 0; n < s->nb_threads; n++) @@ -182,6 +214,14 @@ static av_cold void uninit(AVFilterContext *ctx) av_tx_uninit(&s->ifft[n]); av_freep(&s->ifft); } + + if (s->kernel) { + for (int n = 0; n < s->frequency_band_count; n++) + av_freep(&s->kernel[n]); + } + av_freep(&s->kernel); + + av_freep(&s->fdsp); } static int query_formats(AVFilterContext *ctx) @@ -213,19 +253,21 @@ static int query_formats(AVFilterContext *ctx) return 0; } -static void frequency_band(float *frequency_band, - int frequency_band_count, - float frequency_range, - float frequency_offset, - int frequency_scale, float deviation) +static float frequency_band(float *frequency_band, + int frequency_band_count, + float frequency_range, + float frequency_offset, + int frequency_scale, float deviation) { - deviation *= sqrtf(1.f / (4.f * M_PI)); // Heisenberg Gabor Limit + float ret = 0.f; + + deviation = sqrtf(deviation / (4.f * M_PI)); // Heisenberg Gabor Limit for (int y = 0; y < frequency_band_count; y++) { float frequency = frequency_range * (1.f - (float)y / frequency_band_count) + frequency_offset; float frequency_derivative = frequency_range / frequency_band_count; switch (frequency_scale) { - case FSCALE_LOG2: + case FSCALE_LOG: frequency = powf(2.f, frequency); frequency_derivative *= logf(2.f) * frequency; break; @@ -239,22 +281,66 @@ static void frequency_band(float *frequency_band, break; case FSCALE_ERBS: frequency = 676170.4f / (47.06538f - expf(frequency * 0.08950404f)) - 14678.49f; - frequency_derivative *= (frequency * frequency + 14990.4 * frequency + 4577850.f) / 160514.f; + frequency_derivative *= (frequency * frequency + 14990.4f * frequency + 4577850.f) / 160514.f; + break; + case FSCALE_SQRT: + frequency = frequency * frequency; + frequency_derivative *= 2.f * sqrtf(frequency); + break; + case FSCALE_CBRT: + frequency = frequency * frequency * frequency; + frequency_derivative *= 3.f * powf(frequency, 2.f / 3.f); + break; + case FSCALE_QDRT: + frequency = frequency * frequency * frequency * frequency; + frequency_derivative *= 4.f * powf(frequency, 3.f / 4.f); + break; + case FSCALE_FM: + frequency = 2.f * powf(frequency, 3.f / 2.f) / 3.f; + frequency_derivative *= sqrtf(frequency); break; } frequency_band[y*2 ] = frequency; frequency_band[y*2+1] = frequency_derivative * deviation; + + ret = 1.f / (frequency_derivative * deviation); } + + return ret; } -static float remap_log(float value, float log_factor) +static float remap_log(ShowCWTContext *s, float value, int iscale, float log_factor) { - float sign = (0 < value) - (value < 0); + const float max = s->maximum_intensity; + const float min = s->minimum_intensity; + float ret; - value = logf(value * sign) * log_factor; + value += min; - return 1.f - av_clipf(value, 0.f, 1.f); + switch (iscale) { + case ISCALE_LINEAR: + ret = max - expf(value / log_factor); + break; + case ISCALE_LOG: + value = logf(value) * log_factor; + ret = max - av_clipf(value, 0.f, 1.f); + break; + case ISCALE_SQRT: + value = max - expf(value / log_factor); + ret = sqrtf(value); + break; + case ISCALE_CBRT: + value = max - expf(value / log_factor); + ret = cbrtf(value); + break; + case ISCALE_QDRT: + value = max - expf(value / log_factor); + ret = powf(value, 0.25f); + break; + } + + return av_clipf(ret, 0.f, 1.f); } static int run_channel_cwt_prepare(AVFilterContext *ctx, void *arg, int jobnr, int ch) @@ -262,38 +348,106 @@ static int run_channel_cwt_prepare(AVFilterContext *ctx, void *arg, int jobnr, i ShowCWTContext *s = ctx->priv; const int hop_size = s->hop_size; AVFrame *fin = arg; - float *cache0 = (float *)s->cache[0]->extended_data[ch]; - float *cache = (float *)s->cache[1]->extended_data[ch]; + float *cache = (float *)s->cache->extended_data[ch]; AVComplexFloat *src = (AVComplexFloat *)s->fft_in->extended_data[ch]; AVComplexFloat *dst = (AVComplexFloat *)s->fft_out->extended_data[ch]; + const int offset = (s->input_padding_size - hop_size) >> 1; if (fin) { - const int offset = s->hop_index; const float *input = (const float *)fin->extended_data[ch]; + const int offset = s->hop_size - fin->nb_samples; - memcpy(&cache[offset], input, - fin->nb_samples * sizeof(float)); + memmove(cache, &cache[fin->nb_samples], offset * sizeof(float)); + memcpy(&cache[offset], input, fin->nb_samples * sizeof(float)); } - if (fin == NULL) { - memset(&cache[s->hop_index], 0, - (hop_size - s->hop_index) * sizeof(float)); - } else if (s->hop_index + fin->nb_samples < hop_size) { + if (fin && s->hop_index + fin->nb_samples < hop_size) return 0; - } - for (int n = 0; n < hop_size; n++) { - src[n].re = cache0[n]; - src[n].im = 0.f; - src[n + hop_size].re = cache[n]; - src[n + hop_size].im = 0.f; - } + memset(src, 0, sizeof(float) * s->fft_size); + for (int n = 0; n < hop_size; n++) + src[n+offset].re = cache[n]; s->tx_fn(s->fft[jobnr], dst, src, sizeof(*src)); return 0; } +#define DRAW_BAR_COLOR(x) \ +do { \ + if (Y <= ht) { \ + dstY[x] = 0; \ + dstU[x] = 128; \ + dstV[x] = 128; \ + } else { \ + float mul = (Y - ht) * bh[0]; \ + dstY[x] = av_clip_uint8(lrintf(Y * mul * 255.f)); \ + dstU[x] = av_clip_uint8(lrintf((U-0.5f) * 128.f + 128)); \ + dstV[x] = av_clip_uint8(lrintf((V-0.5f) * 128.f + 128)); \ + } \ +} while (0) + +static void draw_bar(ShowCWTContext *s, int y, + float Y, float U, float V) +{ + float *bh = ((float *)s->bh_out->extended_data[0]) + y; + const ptrdiff_t ylinesize = s->outpicref->linesize[0]; + const ptrdiff_t ulinesize = s->outpicref->linesize[1]; + const ptrdiff_t vlinesize = s->outpicref->linesize[2]; + const int direction = s->direction; + const int sono_size = s->sono_size; + const int bar_size = s->bar_size; + const float rcp_bar_h = 1.f / bar_size; + uint8_t *dstY, *dstU, *dstV; + const int w = s->w; + + bh[0] = 1.f / (Y + 0.0001f); + switch (direction) { + case DIRECTION_LR: + dstY = s->outpicref->data[0] + y * ylinesize; + dstU = s->outpicref->data[1] + y * ulinesize; + dstV = s->outpicref->data[2] + y * vlinesize; + for (int x = 0; x < bar_size; x++) { + float ht = (bar_size - x) * rcp_bar_h; + DRAW_BAR_COLOR(x); + } + break; + case DIRECTION_RL: + dstY = s->outpicref->data[0] + y * ylinesize; + dstU = s->outpicref->data[1] + y * ulinesize; + dstV = s->outpicref->data[2] + y * vlinesize; + for (int x = 0; x < bar_size; x++) { + float ht = x * rcp_bar_h; + DRAW_BAR_COLOR(w - bar_size + x); + } + break; + case DIRECTION_UD: + dstY = s->outpicref->data[0] + w - 1 - y; + dstU = s->outpicref->data[1] + w - 1 - y; + dstV = s->outpicref->data[2] + w - 1 - y; + for (int x = 0; x < bar_size; x++) { + float ht = (bar_size - x) * rcp_bar_h; + DRAW_BAR_COLOR(0); + dstY += ylinesize; + dstU += ulinesize; + dstV += vlinesize; + } + break; + case DIRECTION_DU: + dstY = s->outpicref->data[0] + w - 1 - y + ylinesize * sono_size; + dstU = s->outpicref->data[1] + w - 1 - y + ulinesize * sono_size; + dstV = s->outpicref->data[2] + w - 1 - y + vlinesize * sono_size; + for (int x = 0; x < bar_size; x++) { + float ht = x * rcp_bar_h; + DRAW_BAR_COLOR(0); + dstY += ylinesize; + dstU += ulinesize; + dstV += vlinesize; + } + break; + } +} + static int draw(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ShowCWTContext *s = ctx->priv; @@ -305,18 +459,26 @@ static int draw(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const int count = s->frequency_band_count; const int start = (count * jobnr) / nb_jobs; const int end = (count * (jobnr+1)) / nb_jobs; + const int nb_channels = s->nb_channels; + const int iscale = s->intensity_scale; const int ihop_index = s->ihop_index; const int ihop_size = s->ihop_size; + const float rotation = s->rotation; const int direction = s->direction; uint8_t *dstY, *dstU, *dstV, *dstA; + const int sono_size = s->sono_size; + const int bar_size = s->bar_size; const int mode = s->mode; const int w_1 = s->w - 1; const int x = s->pos; float Y, U, V; for (int y = start; y < end; y++) { - const AVComplexFloat *src = ((const AVComplexFloat *)s->ch_out->extended_data[0]) + - y * ihop_size + ihop_index; + const AVComplexFloat *src = ((const AVComplexFloat *)s->ch_out->extended_data[y]) + + 0 * ihop_size + ihop_index; + + if (sono_size <= 0) + goto skip; switch (direction) { case DIRECTION_LR: @@ -368,31 +530,42 @@ static int draw(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) if (dstA != NULL) dstA += x; } +skip: switch (mode) { case 4: { - const AVComplexFloat *src2 = ((const AVComplexFloat *)s->ch_out->extended_data[FFMIN(1, s->nb_channels - 1)]) + - y * ihop_size + ihop_index; + const AVComplexFloat *src2 = (nb_channels > 1) ? src + ihop_size: src; float z, u, v; z = hypotf(src[0].re + src2[0].re, src[0].im + src2[0].im); u = hypotf(src[0].re, src[0].im); v = hypotf(src2[0].re, src2[0].im); - z = remap_log(z, log_factor); - u = remap_log(u, log_factor); - v = remap_log(v, log_factor); + z = remap_log(s, z, iscale, log_factor); + u = remap_log(s, u, iscale, log_factor); + v = remap_log(s, v, iscale, log_factor); Y = z; - U = 0.5f + z * sinf((v - u) * M_PI_2); - V = 0.5f + z * sinf((u - v) * M_PI_2); + U = sinf((v - u) * M_PI_2); + V = sinf((u - v) * M_PI_2); - dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); - dstU[0] = av_clip_uint8(lrintf(U * 255.f)); - dstV[0] = av_clip_uint8(lrintf(V * 255.f)); - if (dstA) - dstA[0] = dstY[0]; + u = U * cosf(rotation * M_PI) - V * sinf(rotation * M_PI); + v = U * sinf(rotation * M_PI) + V * cosf(rotation * M_PI); + + U = 0.5f + 0.5f * z * u; + V = 0.5f + 0.5f * z * v; + + if (sono_size > 0) { + dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); + dstU[0] = av_clip_uint8(lrintf(U * 255.f)); + dstV[0] = av_clip_uint8(lrintf(V * 255.f)); + if (dstA) + dstA[0] = dstY[0]; + } + + if (bar_size > 0) + draw_bar(s, y, Y, U, V); } break; case 3: @@ -403,53 +576,72 @@ static int draw(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) Y = 0.f; U = V = 0.5f; for (int ch = 0; ch < nb_channels; ch++) { - const AVComplexFloat *src = ((const AVComplexFloat *)s->ch_out->extended_data[ch]) + - y * ihop_size + ihop_index; + const AVComplexFloat *srcn = src + ihop_size * ch; float z; - z = hypotf(src[0].re, src[0].im); - z = remap_log(z, log_factor); + z = hypotf(srcn[0].re, srcn[0].im); + z = remap_log(s, z, iscale, log_factor); Y += z * yf; - U += z * yf * sinf(2.f * M_PI * ch * yf); - V += z * yf * cosf(2.f * M_PI * ch * yf); + U += z * yf * sinf(2.f * M_PI * (ch * yf + rotation)); + V += z * yf * cosf(2.f * M_PI * (ch * yf + rotation)); } - dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); - dstU[0] = av_clip_uint8(lrintf(U * 255.f)); - dstV[0] = av_clip_uint8(lrintf(V * 255.f)); - if (dstA) - dstA[0] = dstY[0]; + if (sono_size > 0) { + dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); + dstU[0] = av_clip_uint8(lrintf(U * 255.f)); + dstV[0] = av_clip_uint8(lrintf(V * 255.f)); + if (dstA) + dstA[0] = dstY[0]; + } + + if (bar_size > 0) + draw_bar(s, y, Y, U, V); } break; case 2: Y = hypotf(src[0].re, src[0].im); - Y = remap_log(Y, log_factor); + Y = remap_log(s, Y, iscale, log_factor); U = atan2f(src[0].im, src[0].re); U = 0.5f + 0.5f * U * Y / M_PI; V = 1.f - U; - dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); - dstU[0] = av_clip_uint8(lrintf(U * 255.f)); - dstV[0] = av_clip_uint8(lrintf(V * 255.f)); - if (dstA) - dstA[0] = dstY[0]; + if (sono_size > 0) { + dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); + dstU[0] = av_clip_uint8(lrintf(U * 255.f)); + dstV[0] = av_clip_uint8(lrintf(V * 255.f)); + if (dstA) + dstA[0] = dstY[0]; + } + + if (bar_size > 0) + draw_bar(s, y, Y, U, V); break; case 1: Y = atan2f(src[0].im, src[0].re); Y = 0.5f + 0.5f * Y / M_PI; - dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); - if (dstA) - dstA[0] = dstY[0]; + if (sono_size > 0) { + dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); + if (dstA) + dstA[0] = dstY[0]; + } + + if (bar_size > 0) + draw_bar(s, y, Y, 0.5f, 0.5f); break; case 0: Y = hypotf(src[0].re, src[0].im); - Y = remap_log(Y, log_factor); + Y = remap_log(s, Y, iscale, log_factor); - dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); - if (dstA) - dstA[0] = dstY[0]; + if (sono_size > 0) { + dstY[0] = av_clip_uint8(lrintf(Y * 255.f)); + if (dstA) + dstA[0] = dstY[0]; + } + + if (bar_size > 0) + draw_bar(s, y, Y, 0.5f, 0.5f); break; } } @@ -461,82 +653,160 @@ static int run_channel_cwt(AVFilterContext *ctx, void *arg, int jobnr, int nb_jo { ShowCWTContext *s = ctx->priv; const int ch = *(int *)arg; - AVComplexFloat *dst = (AVComplexFloat *)s->fft_out->extended_data[ch]; - const int output_sample_count = s->output_sample_count; + const AVComplexFloat *fft_out = (const AVComplexFloat *)s->fft_out->extended_data[ch]; + AVComplexFloat *isrc = (AVComplexFloat *)s->ifft_in->extended_data[jobnr]; + AVComplexFloat *idst = (AVComplexFloat *)s->ifft_out->extended_data[jobnr]; + const int output_padding_size = s->output_padding_size; + const int input_padding_size = s->input_padding_size; + const float scale = 1.f / input_padding_size; const int ihop_size = s->ihop_size; - const int ioffset = (s->output_padding_size - ihop_size) >> 1; const int count = s->frequency_band_count; const int start = (count * jobnr) / nb_jobs; const int end = (count * (jobnr+1)) / nb_jobs; for (int y = start; y < end; y++) { - AVComplexFloat *isrc = (AVComplexFloat *)s->ifft_in->extended_data[y]; - AVComplexFloat *idst = (AVComplexFloat *)s->ifft_out->extended_data[y]; - AVComplexFloat *chout = ((AVComplexFloat *)s->ch_out->extended_data[ch]) + y * ihop_size; - const float *kernel = (const float *)s->kernel->extended_data[y]; + AVComplexFloat *chout = ((AVComplexFloat *)s->ch_out->extended_data[y]) + ch * ihop_size; + AVComplexFloat *over = ((AVComplexFloat *)s->over->extended_data[ch]) + y * ihop_size; + AVComplexFloat *dstx = (AVComplexFloat *)s->dst_x->extended_data[jobnr]; + AVComplexFloat *srcx = (AVComplexFloat *)s->src_x->extended_data[jobnr]; + const AVComplexFloat *kernel = s->kernel[y]; const unsigned *index = (const unsigned *)s->index; const int kernel_start = s->kernel_start[y]; const int kernel_stop = s->kernel_stop[y]; + const int kernel_range = kernel_stop - kernel_start + 1; + int offset; + + if (kernel_start >= 0) { + offset = 0; + memcpy(srcx, fft_out + kernel_start, sizeof(*fft_out) * kernel_range); + } else { + offset = -kernel_start; + memcpy(srcx+offset, fft_out, sizeof(*fft_out) * (kernel_range-offset)); + memcpy(srcx, fft_out+input_padding_size-offset, sizeof(*fft_out)*offset); + } + + s->fdsp->vector_fmul_scalar((float *)srcx, (const float *)srcx, scale, FFALIGN(kernel_range * 2, 4)); + s->fdsp->vector_fmul((float *)dstx, (const float *)srcx, + (const float *)kernel, FFALIGN(kernel_range * 2, 16)); - memset(isrc, 0, sizeof(*isrc) * output_sample_count); - for (int i = kernel_start; i < kernel_stop; i++) { - const unsigned n = index[i]; - const float ff = kernel[i]; + memset(isrc, 0, sizeof(*isrc) * output_padding_size); + if (offset == 0) { + const unsigned *kindex = index + kernel_start; + for (int i = 0; i < kernel_range; i++) { + const unsigned n = kindex[i]; + + isrc[n].re += dstx[i].re; + isrc[n].im += dstx[i].im; + } + } else { + for (int i = 0; i < kernel_range; i++) { + const unsigned n = (i-kernel_start) & (output_padding_size-1); - isrc[n].re += ff * dst[i].re; - isrc[n].im += ff * dst[i].im; + isrc[n].re += dstx[i].re; + isrc[n].im += dstx[i].im; + } } s->itx_fn(s->ifft[jobnr], idst, isrc, sizeof(*isrc)); - memcpy(chout, idst + ioffset, sizeof(*chout) * ihop_size); + memcpy(chout, idst, sizeof(*chout) * ihop_size); + for (int n = 0; n < ihop_size; n++) { + chout[n].re += over[n].re; + chout[n].im += over[n].im; + } + memcpy(over, idst + ihop_size, sizeof(*over) * ihop_size); } return 0; } -static void compute_kernel(AVFilterContext *ctx) +static int compute_kernel(AVFilterContext *ctx) { ShowCWTContext *s = ctx->priv; - const int size = s->input_sample_count; - const float scale_factor = 1.f/(float)size; + const int size = s->input_padding_size; const int output_sample_count = s->output_sample_count; const int fsize = s->frequency_band_count; + int *kernel_start = s->kernel_start; + int *kernel_stop = s->kernel_stop; unsigned *index = s->index; + int range_min = INT_MAX; + int range_max = 0, ret = 0; + float *tkernel; + + tkernel = av_malloc_array(size, sizeof(*tkernel)); + if (!tkernel) + return AVERROR(ENOMEM); for (int y = 0; y < fsize; y++) { - float *kernel = (float *)s->kernel->extended_data[y]; - int *kernel_start = s->kernel_start; - int *kernel_stop = s->kernel_stop; - float frequency = s->frequency_band[y*2]; - float deviation = 1.f / (s->frequency_band[y*2+1] * - output_sample_count); - - for (int n = 0; n < size; n++) { - float ff, f = fabsf(n-frequency); - - f = size - fabsf(f - size); - ff = expf(-f*f*deviation) * scale_factor; - kernel[n] = ff; + AVComplexFloat *kernel = s->kernel[y]; + int start = INT_MIN, stop = INT_MAX; + const float frequency = s->frequency_band[y*2]; + const float deviation = 1.f / (s->frequency_band[y*2+1] * + output_sample_count); + const int a = FFMAX(frequency-12.f*sqrtf(1.f/deviation)-0.5f, -size); + const int b = FFMIN(frequency+12.f*sqrtf(1.f/deviation)-0.5f, size+a); + const int range = -a; + + memset(tkernel, 0, size * sizeof(*tkernel)); + for (int n = a; n < b; n++) { + float ff, f = n+0.5f-frequency; + + ff = expf(-f*f*deviation); + tkernel[n+range] = ff; } - for (int n = 0; n < size; n++) { - if (kernel[n] != 0.f) { - kernel_start[y] = n; + for (int n = a; n < b; n++) { + if (tkernel[n+range] != 0.f) { + if (tkernel[n+range] > FLT_MIN) + av_log(ctx, AV_LOG_DEBUG, "out of range kernel %g\n", tkernel[n+range]); + start = n; break; } } - for (int n = 0; n < size; n++) { - if (kernel[size - n - 1] != 0.f) { - kernel_stop[y] = size - n; + for (int n = b; n >= a; n--) { + if (tkernel[n+range] != 0.f) { + if (tkernel[n+range] > FLT_MIN) + av_log(ctx, AV_LOG_DEBUG, "out of range kernel %g\n", tkernel[n+range]); + stop = n; break; } } + + if (start == INT_MIN || stop == INT_MAX) { + ret = AVERROR(EINVAL); + break; + } + + kernel_start[y] = start; + kernel_stop[y] = stop; + + kernel = av_calloc(FFALIGN(stop-start+1, 16), sizeof(*kernel)); + if (!kernel) { + ret = AVERROR(ENOMEM); + break; + } + + for (int n = 0; n <= stop - start; n++) { + kernel[n].re = tkernel[n+range+start]; + kernel[n].im = tkernel[n+range+start]; + } + + range_min = FFMIN(range_min, stop+1-start); + range_max = FFMAX(range_max, stop+1-start); + + s->kernel[y] = kernel; } for (int n = 0; n < size; n++) - index[n] = n % output_sample_count; + index[n] = n & (s->output_padding_size - 1); + + av_log(ctx, AV_LOG_DEBUG, "range_min: %d\n", range_min); + av_log(ctx, AV_LOG_DEBUG, "range_max: %d\n", range_max); + + av_freep(&tkernel); + + return ret; } static int config_output(AVFilterLink *outlink) @@ -544,48 +814,104 @@ static int config_output(AVFilterLink *outlink) AVFilterContext *ctx = outlink->src; AVFilterLink *inlink = ctx->inputs[0]; ShowCWTContext *s = ctx->priv; - float maximum_frequency = fminf(s->maximum_frequency, inlink->sample_rate * 0.5f); + const float limit_frequency = inlink->sample_rate * 0.5f; + float maximum_frequency = fminf(s->maximum_frequency, limit_frequency); float minimum_frequency = s->minimum_frequency; float scale = 1.f, factor; int ret; + if (minimum_frequency >= maximum_frequency) { + av_log(ctx, AV_LOG_ERROR, "min frequency (%f) >= (%f) max frequency\n", + minimum_frequency, maximum_frequency); + return AVERROR(EINVAL); + } + uninit(ctx); + s->fdsp = avpriv_float_dsp_alloc(0); + if (!s->fdsp) + return AVERROR(ENOMEM); + switch (s->direction) { case DIRECTION_LR: case DIRECTION_RL: + s->bar_size = s->w * s->bar_ratio; + s->sono_size = s->w - s->bar_size; s->frequency_band_count = s->h; break; case DIRECTION_UD: case DIRECTION_DU: + s->bar_size = s->h * s->bar_ratio; + s->sono_size = s->h - s->bar_size; s->frequency_band_count = s->w; break; } - s->new_frame = 1; + switch (s->frequency_scale) { + case FSCALE_LOG: + minimum_frequency = logf(minimum_frequency) / logf(2.f); + maximum_frequency = logf(maximum_frequency) / logf(2.f); + break; + case FSCALE_BARK: + minimum_frequency = 6.f * asinhf(minimum_frequency / 600.f); + maximum_frequency = 6.f * asinhf(maximum_frequency / 600.f); + break; + case FSCALE_MEL: + minimum_frequency = 2595.f * log10f(1.f + minimum_frequency / 700.f); + maximum_frequency = 2595.f * log10f(1.f + maximum_frequency / 700.f); + break; + case FSCALE_ERBS: + minimum_frequency = 11.17268f * logf(1.f + (46.06538f * minimum_frequency) / (minimum_frequency + 14678.49f)); + maximum_frequency = 11.17268f * logf(1.f + (46.06538f * maximum_frequency) / (maximum_frequency + 14678.49f)); + break; + case FSCALE_SQRT: + minimum_frequency = sqrtf(minimum_frequency); + maximum_frequency = sqrtf(maximum_frequency); + break; + case FSCALE_CBRT: + minimum_frequency = cbrtf(minimum_frequency); + maximum_frequency = cbrtf(maximum_frequency); + break; + case FSCALE_QDRT: + minimum_frequency = powf(minimum_frequency, 0.25f); + maximum_frequency = powf(maximum_frequency, 0.25f); + break; + case FSCALE_FM: + minimum_frequency = powf(9.f * (minimum_frequency * minimum_frequency) / 4.f, 1.f / 3.f); + maximum_frequency = powf(9.f * (maximum_frequency * maximum_frequency) / 4.f, 1.f / 3.f); + break; + } + + s->frequency_band = av_calloc(s->frequency_band_count, + sizeof(*s->frequency_band) * 2); + if (!s->frequency_band) + return AVERROR(ENOMEM); + + s->nb_consumed_samples = inlink->sample_rate * + frequency_band(s->frequency_band, + s->frequency_band_count, maximum_frequency - minimum_frequency, + minimum_frequency, s->frequency_scale, s->deviation); + s->nb_consumed_samples = FFMIN(s->nb_consumed_samples, 65536); + s->nb_threads = FFMIN(s->frequency_band_count, ff_filter_get_nb_threads(ctx)); s->nb_channels = inlink->ch_layout.nb_channels; s->old_pts = AV_NOPTS_VALUE; s->eof_pts = AV_NOPTS_VALUE; - s->nb_consumed_samples = 65536; - s->input_sample_count = s->nb_consumed_samples; - s->hop_size = s->nb_consumed_samples >> 1; - s->input_padding_size = 65536; - s->output_padding_size = FFMAX(16, s->input_padding_size * s->pps / inlink->sample_rate); + s->input_sample_count = 1 << (32 - ff_clz(s->nb_consumed_samples)); + s->input_padding_size = 1 << (32 - ff_clz(s->input_sample_count)); + s->output_sample_count = FFMAX(1, av_rescale(s->input_sample_count, s->pps, inlink->sample_rate)); + s->output_padding_size = 1 << (32 - ff_clz(s->output_sample_count)); + + s->hop_size = s->input_sample_count; + s->ihop_size = s->output_padding_size >> 1; outlink->w = s->w; outlink->h = s->h; outlink->sample_aspect_ratio = (AVRational){1,1}; - s->fft_in_size = FFALIGN(s->input_padding_size, av_cpu_max_align()); - s->fft_out_size = FFALIGN(s->input_padding_size, av_cpu_max_align()); - - s->output_sample_count = s->output_padding_size; - - s->ifft_in_size = FFALIGN(s->output_padding_size, av_cpu_max_align()); - s->ifft_out_size = FFALIGN(s->output_padding_size, av_cpu_max_align()); - s->ihop_size = s->output_padding_size >> 1; + s->fft_size = FFALIGN(s->input_padding_size, av_cpu_max_align()); + s->ifft_size = FFALIGN(s->output_padding_size, av_cpu_max_align()); s->fft = av_calloc(s->nb_threads, sizeof(*s->fft)); if (!s->fft) @@ -607,43 +933,58 @@ static int config_output(AVFilterLink *outlink) return ret; } - s->frequency_band = av_calloc(s->frequency_band_count, - sizeof(*s->frequency_band) * 2); s->outpicref = ff_get_video_buffer(outlink, outlink->w, outlink->h); - s->fft_in = ff_get_audio_buffer(inlink, s->fft_in_size * 2); - s->fft_out = ff_get_audio_buffer(inlink, s->fft_out_size * 2); - s->cache[0] = ff_get_audio_buffer(inlink, s->hop_size); - s->cache[1] = ff_get_audio_buffer(inlink, s->hop_size); - s->ch_out = ff_get_audio_buffer(inlink, s->frequency_band_count * 2 * s->ihop_size); + s->fft_in = ff_get_audio_buffer(inlink, s->fft_size * 2); + s->fft_out = ff_get_audio_buffer(inlink, s->fft_size * 2); + s->dst_x = av_frame_alloc(); + s->src_x = av_frame_alloc(); + s->kernel = av_calloc(s->frequency_band_count, sizeof(*s->kernel)); + s->cache = ff_get_audio_buffer(inlink, s->hop_size); + s->over = ff_get_audio_buffer(inlink, s->frequency_band_count * 2 * s->ihop_size); + s->bh_out = ff_get_audio_buffer(inlink, s->frequency_band_count); s->ifft_in = av_frame_alloc(); s->ifft_out = av_frame_alloc(); - s->kernel = av_frame_alloc(); + s->ch_out = av_frame_alloc(); s->index = av_calloc(s->input_padding_size, sizeof(*s->index)); s->kernel_start = av_calloc(s->frequency_band_count, sizeof(*s->kernel_start)); s->kernel_stop = av_calloc(s->frequency_band_count, sizeof(*s->kernel_stop)); - if (!s->outpicref || !s->fft_in || !s->fft_out || - !s->ifft_in || !s->ifft_out || !s->kernel_start || !s->kernel_stop || - !s->frequency_band || !s->kernel || !s->cache[0] || !s->cache[1] || !s->index) + if (!s->outpicref || !s->fft_in || !s->fft_out || !s->src_x || !s->dst_x || !s->over || + !s->ifft_in || !s->ifft_out || !s->kernel_start || !s->kernel_stop || !s->ch_out || + !s->cache || !s->index || !s->bh_out || !s->kernel) return AVERROR(ENOMEM); + s->ch_out->format = inlink->format; + s->ch_out->nb_samples = 2 * s->ihop_size * inlink->ch_layout.nb_channels; + s->ch_out->ch_layout.nb_channels = s->frequency_band_count; + ret = av_frame_get_buffer(s->ch_out, 0); + if (ret < 0) + return ret; + s->ifft_in->format = inlink->format; - s->ifft_in->nb_samples = s->ifft_in_size * 2; - s->ifft_in->ch_layout.nb_channels = s->frequency_band_count; + s->ifft_in->nb_samples = s->ifft_size * 2; + s->ifft_in->ch_layout.nb_channels = s->nb_threads; ret = av_frame_get_buffer(s->ifft_in, 0); if (ret < 0) return ret; s->ifft_out->format = inlink->format; - s->ifft_out->nb_samples = s->ifft_out_size * 2; - s->ifft_out->ch_layout.nb_channels = s->frequency_band_count; + s->ifft_out->nb_samples = s->ifft_size * 2; + s->ifft_out->ch_layout.nb_channels = s->nb_threads; ret = av_frame_get_buffer(s->ifft_out, 0); if (ret < 0) return ret; - s->kernel->format = inlink->format; - s->kernel->nb_samples = s->input_padding_size; - s->kernel->ch_layout.nb_channels = s->frequency_band_count; - ret = av_frame_get_buffer(s->kernel, 0); + s->src_x->format = inlink->format; + s->src_x->nb_samples = s->fft_size * 2; + s->src_x->ch_layout.nb_channels = s->nb_threads; + ret = av_frame_get_buffer(s->src_x, 0); + if (ret < 0) + return ret; + + s->dst_x->format = inlink->format; + s->dst_x->nb_samples = s->fft_size * 2; + s->dst_x->ch_layout.nb_channels = s->nb_threads; + ret = av_frame_get_buffer(s->dst_x, 0); if (ret < 0) return ret; @@ -659,61 +1000,46 @@ static int config_output(AVFilterLink *outlink) s->outpicref->color_range = AVCOL_RANGE_JPEG; - factor = s->nb_consumed_samples / (float)inlink->sample_rate; - minimum_frequency *= factor; - maximum_frequency *= factor; - - switch (s->frequency_scale) { - case FSCALE_LOG2: - minimum_frequency = logf(minimum_frequency) / logf(2.f); - maximum_frequency = logf(maximum_frequency) / logf(2.f); - break; - case FSCALE_BARK: - minimum_frequency = 6.f * asinhf(minimum_frequency / 600.f); - maximum_frequency = 6.f * asinhf(maximum_frequency / 600.f); - break; - case FSCALE_MEL: - minimum_frequency = 2595.f * log10f(1.f + minimum_frequency / 700.f); - maximum_frequency = 2595.f * log10f(1.f + maximum_frequency / 700.f); - break; - case FSCALE_ERBS: - minimum_frequency = 11.17268f * log(1.f + (46.06538f * minimum_frequency) / (minimum_frequency + 14678.49f)); - maximum_frequency = 11.17268f * log(1.f + (46.06538f * maximum_frequency) / (maximum_frequency + 14678.49f)); - break; + factor = s->input_padding_size / (float)inlink->sample_rate; + for (int n = 0; n < s->frequency_band_count; n++) { + s->frequency_band[2*n ] *= factor; + s->frequency_band[2*n+1] *= factor; } - frequency_band(s->frequency_band, - s->frequency_band_count, maximum_frequency - minimum_frequency, - minimum_frequency, s->frequency_scale, s->deviation); - + av_log(ctx, AV_LOG_DEBUG, "factor: %f\n", factor); + av_log(ctx, AV_LOG_DEBUG, "nb_consumed_samples: %d\n", s->nb_consumed_samples); + av_log(ctx, AV_LOG_DEBUG, "hop_size: %d\n", s->hop_size); + av_log(ctx, AV_LOG_DEBUG, "ihop_size: %d\n", s->ihop_size); av_log(ctx, AV_LOG_DEBUG, "input_sample_count: %d\n", s->input_sample_count); + av_log(ctx, AV_LOG_DEBUG, "input_padding_size: %d\n", s->input_padding_size); av_log(ctx, AV_LOG_DEBUG, "output_sample_count: %d\n", s->output_sample_count); + av_log(ctx, AV_LOG_DEBUG, "output_padding_size: %d\n", s->output_padding_size); switch (s->direction) { case DIRECTION_LR: - s->pos = 0; - break; - case DIRECTION_RL: - s->pos = s->w - 1; - break; case DIRECTION_UD: - s->pos = 0; + s->pos = s->bar_size; break; + case DIRECTION_RL: case DIRECTION_DU: - s->pos = s->h - 1; + s->pos = s->sono_size; break; } s->auto_frame_rate = av_make_q(inlink->sample_rate, s->hop_size); if (strcmp(s->rate_str, "auto")) { ret = av_parse_video_rate(&s->frame_rate, s->rate_str); + if (ret < 0) + return ret; } else { s->frame_rate = s->auto_frame_rate; } outlink->frame_rate = s->frame_rate; outlink->time_base = av_inv_q(outlink->frame_rate); - compute_kernel(ctx); + ret = compute_kernel(ctx); + if (ret < 0) + return ret; return 0; } @@ -733,7 +1059,7 @@ static int output_frame(AVFilterContext *ctx) for (int p = 0; p < nb_planes; p++) { ptrdiff_t linesize = s->outpicref->linesize[p]; - for (int y = s->h - 1; y > 0; y--) { + for (int y = s->h - 1; y > s->bar_size; y--) { uint8_t *dst = s->outpicref->data[p] + y * linesize; memmove(dst, dst - linesize, s->w); @@ -744,7 +1070,7 @@ static int output_frame(AVFilterContext *ctx) for (int p = 0; p < nb_planes; p++) { ptrdiff_t linesize = s->outpicref->linesize[p]; - for (int y = 0; y < s->h - 1; y++) { + for (int y = 0; y < s->sono_size; y++) { uint8_t *dst = s->outpicref->data[p] + y * linesize; memmove(dst, dst + linesize, s->w); @@ -764,28 +1090,28 @@ static int output_frame(AVFilterContext *ctx) case DIRECTION_LR: s->pos++; if (s->pos >= s->w) { - s->pos = 0; + s->pos = s->bar_size; s->new_frame = 1; } break; case DIRECTION_RL: s->pos--; if (s->pos < 0) { - s->pos = s->w - 1; + s->pos = s->sono_size; s->new_frame = 1; } break; case DIRECTION_UD: s->pos++; if (s->pos >= s->h) { - s->pos = 0; + s->pos = s->bar_size; s->new_frame = 1; } break; case DIRECTION_DU: s->pos--; if (s->pos < 0) { - s->pos = s->h - 1; + s->pos = s->sono_size; s->new_frame = 1; } break; @@ -795,13 +1121,11 @@ static int output_frame(AVFilterContext *ctx) switch (s->direction) { case DIRECTION_UD: case DIRECTION_LR: - s->pos = 0; + s->pos = s->bar_size; break; case DIRECTION_RL: - s->pos = s->w - 1; - break; case DIRECTION_DU: - s->pos = s->h - 1; + s->pos = s->sono_size; break; } break; @@ -867,14 +1191,16 @@ static int output_frame(AVFilterContext *ctx) if (s->slide != SLIDE_FRAME || s->new_frame == 1) { int64_t pts_offset = s->new_frame ? 0LL : av_rescale(s->ihop_index, s->hop_size, s->ihop_size); + const int offset = (s->input_padding_size - s->hop_size) >> 1; + pts_offset = av_rescale_q(pts_offset - offset, av_make_q(1, inlink->sample_rate), inlink->time_base); s->outpicref->pts = av_rescale_q(s->in_pts + pts_offset, inlink->time_base, outlink->time_base); s->outpicref->duration = 1; } s->ihop_index++; if (s->ihop_index >= s->ihop_size) - s->ihop_index = 0; + s->ihop_index = s->hop_index = 0; if (s->slide == SLIDE_FRAME && s->new_frame == 0) return 1; @@ -928,7 +1254,7 @@ static int activate(AVFilterContext *ctx) if (s->outpicref) { AVFrame *fin = NULL; - if (s->ihop_index == 0) { + if (s->hop_index < s->hop_size) { if (!s->eof) { ret = ff_inlink_consume_samples(inlink, 1, s->hop_size - s->hop_index, &fin); if (ret < 0) @@ -939,9 +1265,10 @@ static int activate(AVFilterContext *ctx) ff_filter_execute(ctx, run_channels_cwt_prepare, fin, NULL, FFMIN(s->nb_threads, s->nb_channels)); if (fin) { - if ((s->hop_index == 0 && s->slide != SLIDE_FRAME) || s->new_frame) { + if (s->hop_index == 0) { s->in_pts = fin->pts; - s->new_frame = 0; + if (s->old_pts == AV_NOPTS_VALUE) + s->old_pts = av_rescale_q(s->in_pts, inlink->time_base, outlink->time_base) - 1; } s->hop_index += fin->nb_samples; av_frame_free(&fin); @@ -952,11 +1279,6 @@ static int activate(AVFilterContext *ctx) } if (s->hop_index >= s->hop_size || s->ihop_index > 0) { - if (s->hop_index) { - FFSWAP(AVFrame *, s->cache[0], s->cache[1]); - s->hop_index = 0; - } - for (int ch = 0; ch < s->nb_channels && s->ihop_index == 0; ch++) { ff_filter_execute(ctx, run_channel_cwt, (void *)&ch, NULL, s->nb_threads); @@ -968,8 +1290,7 @@ static int activate(AVFilterContext *ctx) } } - if (s->eof && s->eof_pts != AV_NOPTS_VALUE && - (s->old_pts + 1 >= s->eof_pts || (s->slide == SLIDE_FRAME))) { + if (s->eof) { if (s->slide == SLIDE_FRAME) ret = output_frame(ctx); ff_outlink_set_status(outlink, AVERROR_EOF, s->eof_pts); @@ -999,13 +1320,6 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad showcwt_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad showcwt_outputs[] = { { .name = "default", @@ -1019,7 +1333,7 @@ const AVFilter ff_avf_showcwt = { .description = NULL_IF_CONFIG_SMALL("Convert input audio to a CWT (Continuous Wavelet Transform) spectrum video output."), .uninit = uninit, .priv_size = sizeof(ShowCWTContext), - FILTER_INPUTS(showcwt_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(showcwt_outputs), FILTER_QUERY_FUNC(query_formats), .activate = activate, diff --git a/libavfilter/avf_showfreqs.c b/libavfilter/avf_showfreqs.c index cf99b4331e7..e3c7ce96f87 100644 --- a/libavfilter/avf_showfreqs.c +++ b/libavfilter/avf_showfreqs.c @@ -30,6 +30,7 @@ #include "libavutil/parseutils.h" #include "audio.h" #include "filters.h" +#include "formats.h" #include "video.h" #include "avfilter.h" #include "internal.h" @@ -83,32 +84,32 @@ static const AVOption showfreqs_options[] = { { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "1024x512"}, 0, 0, FLAGS }, { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS }, { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS }, - { "mode", "set display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=BAR}, 0, NB_MODES-1, FLAGS, "mode" }, - { "line", "show lines", 0, AV_OPT_TYPE_CONST, {.i64=LINE}, 0, 0, FLAGS, "mode" }, - { "bar", "show bars", 0, AV_OPT_TYPE_CONST, {.i64=BAR}, 0, 0, FLAGS, "mode" }, - { "dot", "show dots", 0, AV_OPT_TYPE_CONST, {.i64=DOT}, 0, 0, FLAGS, "mode" }, - { "ascale", "set amplitude scale", OFFSET(ascale), AV_OPT_TYPE_INT, {.i64=AS_LOG}, 0, NB_ASCALES-1, FLAGS, "ascale" }, - { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=AS_LINEAR}, 0, 0, FLAGS, "ascale" }, - { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=AS_SQRT}, 0, 0, FLAGS, "ascale" }, - { "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=AS_CBRT}, 0, 0, FLAGS, "ascale" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=AS_LOG}, 0, 0, FLAGS, "ascale" }, - { "fscale", "set frequency scale", OFFSET(fscale), AV_OPT_TYPE_INT, {.i64=FS_LINEAR}, 0, NB_FSCALES-1, FLAGS, "fscale" }, - { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=FS_LINEAR}, 0, 0, FLAGS, "fscale" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=FS_LOG}, 0, 0, FLAGS, "fscale" }, - { "rlog", "reverse logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=FS_RLOG}, 0, 0, FLAGS, "fscale" }, + { "mode", "set display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=BAR}, 0, NB_MODES-1, FLAGS, .unit = "mode" }, + { "line", "show lines", 0, AV_OPT_TYPE_CONST, {.i64=LINE}, 0, 0, FLAGS, .unit = "mode" }, + { "bar", "show bars", 0, AV_OPT_TYPE_CONST, {.i64=BAR}, 0, 0, FLAGS, .unit = "mode" }, + { "dot", "show dots", 0, AV_OPT_TYPE_CONST, {.i64=DOT}, 0, 0, FLAGS, .unit = "mode" }, + { "ascale", "set amplitude scale", OFFSET(ascale), AV_OPT_TYPE_INT, {.i64=AS_LOG}, 0, NB_ASCALES-1, FLAGS, .unit = "ascale" }, + { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=AS_LINEAR}, 0, 0, FLAGS, .unit = "ascale" }, + { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=AS_SQRT}, 0, 0, FLAGS, .unit = "ascale" }, + { "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=AS_CBRT}, 0, 0, FLAGS, .unit = "ascale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=AS_LOG}, 0, 0, FLAGS, .unit = "ascale" }, + { "fscale", "set frequency scale", OFFSET(fscale), AV_OPT_TYPE_INT, {.i64=FS_LINEAR}, 0, NB_FSCALES-1, FLAGS, .unit = "fscale" }, + { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=FS_LINEAR}, 0, 0, FLAGS, .unit = "fscale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=FS_LOG}, 0, 0, FLAGS, .unit = "fscale" }, + { "rlog", "reverse logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=FS_RLOG}, 0, 0, FLAGS, .unit = "fscale" }, { "win_size", "set window size", OFFSET(fft_size), AV_OPT_TYPE_INT, {.i64=2048}, 16, 65536, FLAGS }, WIN_FUNC_OPTION("win_func", OFFSET(win_func), FLAGS, WFUNC_HANNING), { "overlap", "set window overlap", OFFSET(overlap), AV_OPT_TYPE_FLOAT, {.dbl=1.}, 0., 1., FLAGS }, { "averaging", "set time averaging", OFFSET(avg), AV_OPT_TYPE_INT, {.i64=1}, 0, INT32_MAX, FLAGS }, { "colors", "set channels colors", OFFSET(colors), AV_OPT_TYPE_STRING, {.str = "red|green|blue|yellow|orange|lime|pink|magenta|brown" }, 0, 0, FLAGS }, - { "cmode", "set channel mode", OFFSET(cmode), AV_OPT_TYPE_INT, {.i64=COMBINED}, 0, NB_CMODES-1, FLAGS, "cmode" }, - { "combined", "show all channels in same window", 0, AV_OPT_TYPE_CONST, {.i64=COMBINED}, 0, 0, FLAGS, "cmode" }, - { "separate", "show each channel in own window", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, "cmode" }, + { "cmode", "set channel mode", OFFSET(cmode), AV_OPT_TYPE_INT, {.i64=COMBINED}, 0, NB_CMODES-1, FLAGS, .unit = "cmode" }, + { "combined", "show all channels in same window", 0, AV_OPT_TYPE_CONST, {.i64=COMBINED}, 0, 0, FLAGS, .unit = "cmode" }, + { "separate", "show each channel in own window", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, .unit = "cmode" }, { "minamp", "set minimum amplitude", OFFSET(minamp), AV_OPT_TYPE_FLOAT, {.dbl=1e-6}, FLT_MIN, 1e-6, FLAGS }, - { "data", "set data mode", OFFSET(data_mode), AV_OPT_TYPE_INT, {.i64=MAGNITUDE}, 0, NB_DATA-1, FLAGS, "data" }, - { "magnitude", "show magnitude", 0, AV_OPT_TYPE_CONST, {.i64=MAGNITUDE}, 0, 0, FLAGS, "data" }, - { "phase", "show phase", 0, AV_OPT_TYPE_CONST, {.i64=PHASE}, 0, 0, FLAGS, "data" }, - { "delay", "show group delay",0, AV_OPT_TYPE_CONST, {.i64=DELAY}, 0, 0, FLAGS, "data" }, + { "data", "set data mode", OFFSET(data_mode), AV_OPT_TYPE_INT, {.i64=MAGNITUDE}, 0, NB_DATA-1, FLAGS, .unit = "data" }, + { "magnitude", "show magnitude", 0, AV_OPT_TYPE_CONST, {.i64=MAGNITUDE}, 0, 0, FLAGS, .unit = "data" }, + { "phase", "show phase", 0, AV_OPT_TYPE_CONST, {.i64=PHASE}, 0, 0, FLAGS, .unit = "data" }, + { "delay", "show group delay",0, AV_OPT_TYPE_CONST, {.i64=DELAY}, 0, 0, FLAGS, .unit = "data" }, { "channels", "set channels to draw", OFFSET(ch_layout_str), AV_OPT_TYPE_STRING, {.str="all"}, 0, 0, FLAGS }, { NULL } }; @@ -547,13 +548,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&s->window); } -static const AVFilterPad showfreqs_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad showfreqs_outputs[] = { { .name = "default", @@ -568,7 +562,7 @@ const AVFilter ff_avf_showfreqs = { .uninit = uninit, .priv_size = sizeof(ShowFreqsContext), .activate = activate, - FILTER_INPUTS(showfreqs_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(showfreqs_outputs), FILTER_QUERY_FUNC(query_formats), .priv_class = &showfreqs_class, diff --git a/libavfilter/avf_showspatial.c b/libavfilter/avf_showspatial.c index 54d7962ec4f..29b41bee850 100644 --- a/libavfilter/avf_showspatial.c +++ b/libavfilter/avf_showspatial.c @@ -28,6 +28,7 @@ #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "audio.h" +#include "formats.h" #include "video.h" #include "avfilter.h" #include "filters.h" @@ -315,13 +316,6 @@ static int spatial_activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad showspatial_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad showspatial_outputs[] = { { .name = "default", @@ -335,7 +329,7 @@ const AVFilter ff_avf_showspatial = { .description = NULL_IF_CONFIG_SMALL("Convert input audio to a spatial video output."), .uninit = uninit, .priv_size = sizeof(ShowSpatialContext), - FILTER_INPUTS(showspatial_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(showspatial_outputs), FILTER_QUERY_FUNC(query_formats), .activate = spatial_activate, diff --git a/libavfilter/avf_showspectrum.c b/libavfilter/avf_showspectrum.c index 4ce964706fa..5d76da3bfc6 100644 --- a/libavfilter/avf_showspectrum.c +++ b/libavfilter/avf_showspectrum.c @@ -40,6 +40,7 @@ #include "libavutil/parseutils.h" #include "libavutil/xga_font_data.h" #include "audio.h" +#include "formats.h" #include "video.h" #include "avfilter.h" #include "filters.h" @@ -109,6 +110,7 @@ typedef struct ShowSpectrumContext { float dmin, dmax; uint64_t samples; int (*plot_channel)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); + int eof; float opacity_factor; @@ -123,52 +125,52 @@ typedef struct ShowSpectrumContext { static const AVOption showspectrum_options[] = { { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x512"}, 0, 0, FLAGS }, { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x512"}, 0, 0, FLAGS }, - { "slide", "set sliding mode", OFFSET(sliding), AV_OPT_TYPE_INT, {.i64 = 0}, 0, NB_SLIDES-1, FLAGS, "slide" }, - { "replace", "replace old columns with new", 0, AV_OPT_TYPE_CONST, {.i64=REPLACE}, 0, 0, FLAGS, "slide" }, - { "scroll", "scroll from right to left", 0, AV_OPT_TYPE_CONST, {.i64=SCROLL}, 0, 0, FLAGS, "slide" }, - { "fullframe", "return full frames", 0, AV_OPT_TYPE_CONST, {.i64=FULLFRAME}, 0, 0, FLAGS, "slide" }, - { "rscroll", "scroll from left to right", 0, AV_OPT_TYPE_CONST, {.i64=RSCROLL}, 0, 0, FLAGS, "slide" }, - { "lreplace", "replace from right to left", 0, AV_OPT_TYPE_CONST, {.i64=LREPLACE}, 0, 0, FLAGS, "slide" }, - { "mode", "set channel display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=COMBINED}, COMBINED, NB_MODES-1, FLAGS, "mode" }, - { "combined", "combined mode", 0, AV_OPT_TYPE_CONST, {.i64=COMBINED}, 0, 0, FLAGS, "mode" }, - { "separate", "separate mode", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, "mode" }, - { "color", "set channel coloring", OFFSET(color_mode), AV_OPT_TYPE_INT, {.i64=CHANNEL}, CHANNEL, NB_CLMODES-1, FLAGS, "color" }, - { "channel", "separate color for each channel", 0, AV_OPT_TYPE_CONST, {.i64=CHANNEL}, 0, 0, FLAGS, "color" }, - { "intensity", "intensity based coloring", 0, AV_OPT_TYPE_CONST, {.i64=INTENSITY}, 0, 0, FLAGS, "color" }, - { "rainbow", "rainbow based coloring", 0, AV_OPT_TYPE_CONST, {.i64=RAINBOW}, 0, 0, FLAGS, "color" }, - { "moreland", "moreland based coloring", 0, AV_OPT_TYPE_CONST, {.i64=MORELAND}, 0, 0, FLAGS, "color" }, - { "nebulae", "nebulae based coloring", 0, AV_OPT_TYPE_CONST, {.i64=NEBULAE}, 0, 0, FLAGS, "color" }, - { "fire", "fire based coloring", 0, AV_OPT_TYPE_CONST, {.i64=FIRE}, 0, 0, FLAGS, "color" }, - { "fiery", "fiery based coloring", 0, AV_OPT_TYPE_CONST, {.i64=FIERY}, 0, 0, FLAGS, "color" }, - { "fruit", "fruit based coloring", 0, AV_OPT_TYPE_CONST, {.i64=FRUIT}, 0, 0, FLAGS, "color" }, - { "cool", "cool based coloring", 0, AV_OPT_TYPE_CONST, {.i64=COOL}, 0, 0, FLAGS, "color" }, - { "magma", "magma based coloring", 0, AV_OPT_TYPE_CONST, {.i64=MAGMA}, 0, 0, FLAGS, "color" }, - { "green", "green based coloring", 0, AV_OPT_TYPE_CONST, {.i64=GREEN}, 0, 0, FLAGS, "color" }, - { "viridis", "viridis based coloring", 0, AV_OPT_TYPE_CONST, {.i64=VIRIDIS}, 0, 0, FLAGS, "color" }, - { "plasma", "plasma based coloring", 0, AV_OPT_TYPE_CONST, {.i64=PLASMA}, 0, 0, FLAGS, "color" }, - { "cividis", "cividis based coloring", 0, AV_OPT_TYPE_CONST, {.i64=CIVIDIS}, 0, 0, FLAGS, "color" }, - { "terrain", "terrain based coloring", 0, AV_OPT_TYPE_CONST, {.i64=TERRAIN}, 0, 0, FLAGS, "color" }, - { "scale", "set display scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=SQRT}, LINEAR, NB_SCALES-1, FLAGS, "scale" }, - { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "scale" }, - { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT}, 0, 0, FLAGS, "scale" }, - { "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=CBRT}, 0, 0, FLAGS, "scale" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, FLAGS, "scale" }, - { "4thrt","4th root", 0, AV_OPT_TYPE_CONST, {.i64=FOURTHRT}, 0, 0, FLAGS, "scale" }, - { "5thrt","5th root", 0, AV_OPT_TYPE_CONST, {.i64=FIFTHRT}, 0, 0, FLAGS, "scale" }, - { "fscale", "set frequency scale", OFFSET(fscale), AV_OPT_TYPE_INT, {.i64=F_LINEAR}, 0, NB_FSCALES-1, FLAGS, "fscale" }, - { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=F_LINEAR}, 0, 0, FLAGS, "fscale" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=F_LOG}, 0, 0, FLAGS, "fscale" }, + { "slide", "set sliding mode", OFFSET(sliding), AV_OPT_TYPE_INT, {.i64 = 0}, 0, NB_SLIDES-1, FLAGS, .unit = "slide" }, + { "replace", "replace old columns with new", 0, AV_OPT_TYPE_CONST, {.i64=REPLACE}, 0, 0, FLAGS, .unit = "slide" }, + { "scroll", "scroll from right to left", 0, AV_OPT_TYPE_CONST, {.i64=SCROLL}, 0, 0, FLAGS, .unit = "slide" }, + { "fullframe", "return full frames", 0, AV_OPT_TYPE_CONST, {.i64=FULLFRAME}, 0, 0, FLAGS, .unit = "slide" }, + { "rscroll", "scroll from left to right", 0, AV_OPT_TYPE_CONST, {.i64=RSCROLL}, 0, 0, FLAGS, .unit = "slide" }, + { "lreplace", "replace from right to left", 0, AV_OPT_TYPE_CONST, {.i64=LREPLACE}, 0, 0, FLAGS, .unit = "slide" }, + { "mode", "set channel display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=COMBINED}, COMBINED, NB_MODES-1, FLAGS, .unit = "mode" }, + { "combined", "combined mode", 0, AV_OPT_TYPE_CONST, {.i64=COMBINED}, 0, 0, FLAGS, .unit = "mode" }, + { "separate", "separate mode", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, .unit = "mode" }, + { "color", "set channel coloring", OFFSET(color_mode), AV_OPT_TYPE_INT, {.i64=CHANNEL}, CHANNEL, NB_CLMODES-1, FLAGS, .unit = "color" }, + { "channel", "separate color for each channel", 0, AV_OPT_TYPE_CONST, {.i64=CHANNEL}, 0, 0, FLAGS, .unit = "color" }, + { "intensity", "intensity based coloring", 0, AV_OPT_TYPE_CONST, {.i64=INTENSITY}, 0, 0, FLAGS, .unit = "color" }, + { "rainbow", "rainbow based coloring", 0, AV_OPT_TYPE_CONST, {.i64=RAINBOW}, 0, 0, FLAGS, .unit = "color" }, + { "moreland", "moreland based coloring", 0, AV_OPT_TYPE_CONST, {.i64=MORELAND}, 0, 0, FLAGS, .unit = "color" }, + { "nebulae", "nebulae based coloring", 0, AV_OPT_TYPE_CONST, {.i64=NEBULAE}, 0, 0, FLAGS, .unit = "color" }, + { "fire", "fire based coloring", 0, AV_OPT_TYPE_CONST, {.i64=FIRE}, 0, 0, FLAGS, .unit = "color" }, + { "fiery", "fiery based coloring", 0, AV_OPT_TYPE_CONST, {.i64=FIERY}, 0, 0, FLAGS, .unit = "color" }, + { "fruit", "fruit based coloring", 0, AV_OPT_TYPE_CONST, {.i64=FRUIT}, 0, 0, FLAGS, .unit = "color" }, + { "cool", "cool based coloring", 0, AV_OPT_TYPE_CONST, {.i64=COOL}, 0, 0, FLAGS, .unit = "color" }, + { "magma", "magma based coloring", 0, AV_OPT_TYPE_CONST, {.i64=MAGMA}, 0, 0, FLAGS, .unit = "color" }, + { "green", "green based coloring", 0, AV_OPT_TYPE_CONST, {.i64=GREEN}, 0, 0, FLAGS, .unit = "color" }, + { "viridis", "viridis based coloring", 0, AV_OPT_TYPE_CONST, {.i64=VIRIDIS}, 0, 0, FLAGS, .unit = "color" }, + { "plasma", "plasma based coloring", 0, AV_OPT_TYPE_CONST, {.i64=PLASMA}, 0, 0, FLAGS, .unit = "color" }, + { "cividis", "cividis based coloring", 0, AV_OPT_TYPE_CONST, {.i64=CIVIDIS}, 0, 0, FLAGS, .unit = "color" }, + { "terrain", "terrain based coloring", 0, AV_OPT_TYPE_CONST, {.i64=TERRAIN}, 0, 0, FLAGS, .unit = "color" }, + { "scale", "set display scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=SQRT}, LINEAR, NB_SCALES-1, FLAGS, .unit = "scale" }, + { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, .unit = "scale" }, + { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT}, 0, 0, FLAGS, .unit = "scale" }, + { "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=CBRT}, 0, 0, FLAGS, .unit = "scale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, FLAGS, .unit = "scale" }, + { "4thrt","4th root", 0, AV_OPT_TYPE_CONST, {.i64=FOURTHRT}, 0, 0, FLAGS, .unit = "scale" }, + { "5thrt","5th root", 0, AV_OPT_TYPE_CONST, {.i64=FIFTHRT}, 0, 0, FLAGS, .unit = "scale" }, + { "fscale", "set frequency scale", OFFSET(fscale), AV_OPT_TYPE_INT, {.i64=F_LINEAR}, 0, NB_FSCALES-1, FLAGS, .unit = "fscale" }, + { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=F_LINEAR}, 0, 0, FLAGS, .unit = "fscale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=F_LOG}, 0, 0, FLAGS, .unit = "fscale" }, { "saturation", "color saturation multiplier", OFFSET(saturation), AV_OPT_TYPE_FLOAT, {.dbl = 1}, -10, 10, FLAGS }, WIN_FUNC_OPTION("win_func", OFFSET(win_func), FLAGS, WFUNC_HANNING), - { "orientation", "set orientation", OFFSET(orientation), AV_OPT_TYPE_INT, {.i64=VERTICAL}, 0, NB_ORIENTATIONS-1, FLAGS, "orientation" }, - { "vertical", NULL, 0, AV_OPT_TYPE_CONST, {.i64=VERTICAL}, 0, 0, FLAGS, "orientation" }, - { "horizontal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=HORIZONTAL}, 0, 0, FLAGS, "orientation" }, + { "orientation", "set orientation", OFFSET(orientation), AV_OPT_TYPE_INT, {.i64=VERTICAL}, 0, NB_ORIENTATIONS-1, FLAGS, .unit = "orientation" }, + { "vertical", NULL, 0, AV_OPT_TYPE_CONST, {.i64=VERTICAL}, 0, 0, FLAGS, .unit = "orientation" }, + { "horizontal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=HORIZONTAL}, 0, 0, FLAGS, .unit = "orientation" }, { "overlap", "set window overlap", OFFSET(overlap), AV_OPT_TYPE_FLOAT, {.dbl = 0}, 0, 1, FLAGS }, { "gain", "set scale gain", OFFSET(gain), AV_OPT_TYPE_FLOAT, {.dbl = 1}, 0, 128, FLAGS }, - { "data", "set data mode", OFFSET(data), AV_OPT_TYPE_INT, {.i64 = 0}, 0, NB_DMODES-1, FLAGS, "data" }, - { "magnitude", NULL, 0, AV_OPT_TYPE_CONST, {.i64=D_MAGNITUDE}, 0, 0, FLAGS, "data" }, - { "phase", NULL, 0, AV_OPT_TYPE_CONST, {.i64=D_PHASE}, 0, 0, FLAGS, "data" }, - { "uphase", NULL, 0, AV_OPT_TYPE_CONST, {.i64=D_UPHASE}, 0, 0, FLAGS, "data" }, + { "data", "set data mode", OFFSET(data), AV_OPT_TYPE_INT, {.i64 = 0}, 0, NB_DMODES-1, FLAGS, .unit = "data" }, + { "magnitude", NULL, 0, AV_OPT_TYPE_CONST, {.i64=D_MAGNITUDE}, 0, 0, FLAGS, .unit = "data" }, + { "phase", NULL, 0, AV_OPT_TYPE_CONST, {.i64=D_PHASE}, 0, 0, FLAGS, .unit = "data" }, + { "uphase", NULL, 0, AV_OPT_TYPE_CONST, {.i64=D_UPHASE}, 0, 0, FLAGS, .unit = "data" }, { "rotation", "color rotation", OFFSET(rotation), AV_OPT_TYPE_FLOAT, {.dbl = 0}, -1, 1, FLAGS }, { "start", "start frequency", OFFSET(start), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT32_MAX, FLAGS }, { "stop", "stop frequency", OFFSET(stop), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT32_MAX, FLAGS }, @@ -1292,6 +1294,8 @@ static int config_output(AVFilterLink *outlink) av_realloc_f(s->combine_buffer, s->w * 4, sizeof(*s->combine_buffer)); } + if (!s->combine_buffer) + return AVERROR(ENOMEM); av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d FFT window size:%d\n", s->w, s->h, s->win_size); @@ -1541,8 +1545,7 @@ static int plot_spectrum_column(AVFilterLink *inlink, AVFrame *insamples) if (!s->single_pic && (s->sliding != FULLFRAME || s->xpos == 0)) { if (s->old_pts < outpicref->pts || s->sliding == FULLFRAME || - (ff_outlink_get_status(inlink) == AVERROR_EOF && - ff_inlink_queued_samples(inlink) <= s->hop_size)) { + (s->eof && ff_inlink_queued_samples(inlink) <= s->hop_size)) { AVFrame *clone; if (s->legend) { @@ -1598,7 +1601,7 @@ static int activate(AVFilterContext *ctx) FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); - if (s->outpicref) { + if (s->outpicref && ff_inlink_queued_samples(inlink) > 0) { AVFrame *fin; ret = ff_inlink_consume_samples(inlink, s->hop_size, s->hop_size, &fin); @@ -1625,8 +1628,7 @@ static int activate(AVFilterContext *ctx) } } - if (ff_outlink_get_status(inlink) == AVERROR_EOF && - s->sliding == FULLFRAME && + if (s->eof && s->sliding == FULLFRAME && s->xpos > 0 && s->outpicref) { if (s->orientation == VERTICAL) { @@ -1654,11 +1656,15 @@ static int activate(AVFilterContext *ctx) return 0; } - if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { - if (status == AVERROR_EOF) { - ff_outlink_set_status(outlink, status, s->pts); - return 0; - } + if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) { + s->eof = status == AVERROR_EOF; + ff_filter_set_ready(ctx, 100); + return 0; + } + + if (s->eof) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); + return 0; } if (ff_inlink_queued_samples(inlink) >= s->hop_size) { @@ -1674,13 +1680,6 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad showspectrum_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad showspectrum_outputs[] = { { .name = "default", @@ -1694,7 +1693,7 @@ const AVFilter ff_avf_showspectrum = { .description = NULL_IF_CONFIG_SMALL("Convert input audio to a spectrum video output."), .uninit = uninit, .priv_size = sizeof(ShowSpectrumContext), - FILTER_INPUTS(showspectrum_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(showspectrum_outputs), FILTER_QUERY_FUNC(query_formats), .activate = activate, @@ -1708,40 +1707,40 @@ const AVFilter ff_avf_showspectrum = { static const AVOption showspectrumpic_options[] = { { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "4096x2048"}, 0, 0, FLAGS }, { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "4096x2048"}, 0, 0, FLAGS }, - { "mode", "set channel display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=COMBINED}, 0, NB_MODES-1, FLAGS, "mode" }, - { "combined", "combined mode", 0, AV_OPT_TYPE_CONST, {.i64=COMBINED}, 0, 0, FLAGS, "mode" }, - { "separate", "separate mode", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, "mode" }, - { "color", "set channel coloring", OFFSET(color_mode), AV_OPT_TYPE_INT, {.i64=INTENSITY}, 0, NB_CLMODES-1, FLAGS, "color" }, - { "channel", "separate color for each channel", 0, AV_OPT_TYPE_CONST, {.i64=CHANNEL}, 0, 0, FLAGS, "color" }, - { "intensity", "intensity based coloring", 0, AV_OPT_TYPE_CONST, {.i64=INTENSITY}, 0, 0, FLAGS, "color" }, - { "rainbow", "rainbow based coloring", 0, AV_OPT_TYPE_CONST, {.i64=RAINBOW}, 0, 0, FLAGS, "color" }, - { "moreland", "moreland based coloring", 0, AV_OPT_TYPE_CONST, {.i64=MORELAND}, 0, 0, FLAGS, "color" }, - { "nebulae", "nebulae based coloring", 0, AV_OPT_TYPE_CONST, {.i64=NEBULAE}, 0, 0, FLAGS, "color" }, - { "fire", "fire based coloring", 0, AV_OPT_TYPE_CONST, {.i64=FIRE}, 0, 0, FLAGS, "color" }, - { "fiery", "fiery based coloring", 0, AV_OPT_TYPE_CONST, {.i64=FIERY}, 0, 0, FLAGS, "color" }, - { "fruit", "fruit based coloring", 0, AV_OPT_TYPE_CONST, {.i64=FRUIT}, 0, 0, FLAGS, "color" }, - { "cool", "cool based coloring", 0, AV_OPT_TYPE_CONST, {.i64=COOL}, 0, 0, FLAGS, "color" }, - { "magma", "magma based coloring", 0, AV_OPT_TYPE_CONST, {.i64=MAGMA}, 0, 0, FLAGS, "color" }, - { "green", "green based coloring", 0, AV_OPT_TYPE_CONST, {.i64=GREEN}, 0, 0, FLAGS, "color" }, - { "viridis", "viridis based coloring", 0, AV_OPT_TYPE_CONST, {.i64=VIRIDIS}, 0, 0, FLAGS, "color" }, - { "plasma", "plasma based coloring", 0, AV_OPT_TYPE_CONST, {.i64=PLASMA}, 0, 0, FLAGS, "color" }, - { "cividis", "cividis based coloring", 0, AV_OPT_TYPE_CONST, {.i64=CIVIDIS}, 0, 0, FLAGS, "color" }, - { "terrain", "terrain based coloring", 0, AV_OPT_TYPE_CONST, {.i64=TERRAIN}, 0, 0, FLAGS, "color" }, - { "scale", "set display scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=LOG}, 0, NB_SCALES-1, FLAGS, "scale" }, - { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "scale" }, - { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT}, 0, 0, FLAGS, "scale" }, - { "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=CBRT}, 0, 0, FLAGS, "scale" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, FLAGS, "scale" }, - { "4thrt","4th root", 0, AV_OPT_TYPE_CONST, {.i64=FOURTHRT}, 0, 0, FLAGS, "scale" }, - { "5thrt","5th root", 0, AV_OPT_TYPE_CONST, {.i64=FIFTHRT}, 0, 0, FLAGS, "scale" }, - { "fscale", "set frequency scale", OFFSET(fscale), AV_OPT_TYPE_INT, {.i64=F_LINEAR}, 0, NB_FSCALES-1, FLAGS, "fscale" }, - { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=F_LINEAR}, 0, 0, FLAGS, "fscale" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=F_LOG}, 0, 0, FLAGS, "fscale" }, + { "mode", "set channel display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=COMBINED}, 0, NB_MODES-1, FLAGS, .unit = "mode" }, + { "combined", "combined mode", 0, AV_OPT_TYPE_CONST, {.i64=COMBINED}, 0, 0, FLAGS, .unit = "mode" }, + { "separate", "separate mode", 0, AV_OPT_TYPE_CONST, {.i64=SEPARATE}, 0, 0, FLAGS, .unit = "mode" }, + { "color", "set channel coloring", OFFSET(color_mode), AV_OPT_TYPE_INT, {.i64=INTENSITY}, 0, NB_CLMODES-1, FLAGS, .unit = "color" }, + { "channel", "separate color for each channel", 0, AV_OPT_TYPE_CONST, {.i64=CHANNEL}, 0, 0, FLAGS, .unit = "color" }, + { "intensity", "intensity based coloring", 0, AV_OPT_TYPE_CONST, {.i64=INTENSITY}, 0, 0, FLAGS, .unit = "color" }, + { "rainbow", "rainbow based coloring", 0, AV_OPT_TYPE_CONST, {.i64=RAINBOW}, 0, 0, FLAGS, .unit = "color" }, + { "moreland", "moreland based coloring", 0, AV_OPT_TYPE_CONST, {.i64=MORELAND}, 0, 0, FLAGS, .unit = "color" }, + { "nebulae", "nebulae based coloring", 0, AV_OPT_TYPE_CONST, {.i64=NEBULAE}, 0, 0, FLAGS, .unit = "color" }, + { "fire", "fire based coloring", 0, AV_OPT_TYPE_CONST, {.i64=FIRE}, 0, 0, FLAGS, .unit = "color" }, + { "fiery", "fiery based coloring", 0, AV_OPT_TYPE_CONST, {.i64=FIERY}, 0, 0, FLAGS, .unit = "color" }, + { "fruit", "fruit based coloring", 0, AV_OPT_TYPE_CONST, {.i64=FRUIT}, 0, 0, FLAGS, .unit = "color" }, + { "cool", "cool based coloring", 0, AV_OPT_TYPE_CONST, {.i64=COOL}, 0, 0, FLAGS, .unit = "color" }, + { "magma", "magma based coloring", 0, AV_OPT_TYPE_CONST, {.i64=MAGMA}, 0, 0, FLAGS, .unit = "color" }, + { "green", "green based coloring", 0, AV_OPT_TYPE_CONST, {.i64=GREEN}, 0, 0, FLAGS, .unit = "color" }, + { "viridis", "viridis based coloring", 0, AV_OPT_TYPE_CONST, {.i64=VIRIDIS}, 0, 0, FLAGS, .unit = "color" }, + { "plasma", "plasma based coloring", 0, AV_OPT_TYPE_CONST, {.i64=PLASMA}, 0, 0, FLAGS, .unit = "color" }, + { "cividis", "cividis based coloring", 0, AV_OPT_TYPE_CONST, {.i64=CIVIDIS}, 0, 0, FLAGS, .unit = "color" }, + { "terrain", "terrain based coloring", 0, AV_OPT_TYPE_CONST, {.i64=TERRAIN}, 0, 0, FLAGS, .unit = "color" }, + { "scale", "set display scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=LOG}, 0, NB_SCALES-1, FLAGS, .unit = "scale" }, + { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, .unit = "scale" }, + { "sqrt", "square root", 0, AV_OPT_TYPE_CONST, {.i64=SQRT}, 0, 0, FLAGS, .unit = "scale" }, + { "cbrt", "cubic root", 0, AV_OPT_TYPE_CONST, {.i64=CBRT}, 0, 0, FLAGS, .unit = "scale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, FLAGS, .unit = "scale" }, + { "4thrt","4th root", 0, AV_OPT_TYPE_CONST, {.i64=FOURTHRT}, 0, 0, FLAGS, .unit = "scale" }, + { "5thrt","5th root", 0, AV_OPT_TYPE_CONST, {.i64=FIFTHRT}, 0, 0, FLAGS, .unit = "scale" }, + { "fscale", "set frequency scale", OFFSET(fscale), AV_OPT_TYPE_INT, {.i64=F_LINEAR}, 0, NB_FSCALES-1, FLAGS, .unit = "fscale" }, + { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=F_LINEAR}, 0, 0, FLAGS, .unit = "fscale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=F_LOG}, 0, 0, FLAGS, .unit = "fscale" }, { "saturation", "color saturation multiplier", OFFSET(saturation), AV_OPT_TYPE_FLOAT, {.dbl = 1}, -10, 10, FLAGS }, WIN_FUNC_OPTION("win_func", OFFSET(win_func), FLAGS, WFUNC_HANNING), - { "orientation", "set orientation", OFFSET(orientation), AV_OPT_TYPE_INT, {.i64=VERTICAL}, 0, NB_ORIENTATIONS-1, FLAGS, "orientation" }, - { "vertical", NULL, 0, AV_OPT_TYPE_CONST, {.i64=VERTICAL}, 0, 0, FLAGS, "orientation" }, - { "horizontal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=HORIZONTAL}, 0, 0, FLAGS, "orientation" }, + { "orientation", "set orientation", OFFSET(orientation), AV_OPT_TYPE_INT, {.i64=VERTICAL}, 0, NB_ORIENTATIONS-1, FLAGS, .unit = "orientation" }, + { "vertical", NULL, 0, AV_OPT_TYPE_CONST, {.i64=VERTICAL}, 0, 0, FLAGS, .unit = "orientation" }, + { "horizontal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=HORIZONTAL}, 0, 0, FLAGS, .unit = "orientation" }, { "gain", "set scale gain", OFFSET(gain), AV_OPT_TYPE_FLOAT, {.dbl = 1}, 0, 128, FLAGS }, { "legend", "draw legend", OFFSET(legend), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, { "rotation", "color rotation", OFFSET(rotation), AV_OPT_TYPE_FLOAT, {.dbl = 0}, -1, 1, FLAGS }, @@ -1785,7 +1784,7 @@ static int showspectrumpic_request_frame(AVFilterLink *outlink) int acc_samples = 0; int dst_offset = 0; - while (nb_frame <= s->nb_frames) { + while (nb_frame < s->nb_frames) { AVFrame *cur_frame = s->frames[nb_frame]; int cur_frame_samples = cur_frame->nb_samples; int nb_samples = 0; diff --git a/libavfilter/avf_showvolume.c b/libavfilter/avf_showvolume.c index fa64d5237ae..f359e4d97ba 100644 --- a/libavfilter/avf_showvolume.c +++ b/libavfilter/avf_showvolume.c @@ -82,17 +82,17 @@ static const AVOption showvolume_options[] = { { "v", "display volume value", OFFSET(draw_volume), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, { "dm", "duration for max value display", OFFSET(draw_persistent_duration), AV_OPT_TYPE_DOUBLE, {.dbl=0.}, 0, 9000, FLAGS}, { "dmc","set color of the max value line", OFFSET(persistant_max_rgba), AV_OPT_TYPE_COLOR, {.str = "orange"}, 0, 0, FLAGS }, - { "o", "set orientation", OFFSET(orientation), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "orientation" }, - { "h", "horizontal", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "orientation" }, - { "v", "vertical", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "orientation" }, + { "o", "set orientation", OFFSET(orientation), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "orientation" }, + { "h", "horizontal", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "orientation" }, + { "v", "vertical", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "orientation" }, { "s", "set step size", OFFSET(step), AV_OPT_TYPE_INT, {.i64=0}, 0, 5, FLAGS }, { "p", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, 1, FLAGS }, - { "m", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "mode" }, - { "p", "peak", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" }, - { "r", "rms", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" }, - { "ds", "set display scale", OFFSET(display_scale), AV_OPT_TYPE_INT, {.i64=LINEAR}, LINEAR, NB_DISPLAY_SCALE - 1, FLAGS, "display_scale" }, - { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "display_scale" }, - { "log", "log", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, FLAGS, "display_scale" }, + { "m", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "mode" }, + { "p", "peak", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "mode" }, + { "r", "rms", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "mode" }, + { "ds", "set display scale", OFFSET(display_scale), AV_OPT_TYPE_INT, {.i64=LINEAR}, LINEAR, NB_DISPLAY_SCALE - 1, FLAGS, .unit = "display_scale" }, + { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, .unit = "display_scale" }, + { "log", "log", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, FLAGS, .unit = "display_scale" }, { NULL } }; @@ -370,20 +370,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) max = av_clipf(max, 0, 1); max_draw = calc_max_draw(s, outlink, max); - for (j = max_draw; j < s->w; j++) { + for (j = s->w - 1; j >= max_draw; j--) { uint8_t *dst = s->out->data[0] + j * s->out->linesize[0] + c * (s->b + s->h) * 4; for (k = 0; k < s->h; k++) { AV_WN32A(&dst[k * 4], lut[s->w - j - 1]); - if (j & step) - j += step; } - } - - if (s->h >= 8 && s->draw_text) { - int ret = av_channel_name(channel_name, sizeof(channel_name), av_channel_layout_channel_from_index(&insamples->ch_layout, c)); - if (ret < 0) - continue; - drawtext(s->out, c * (s->h + s->b) + (s->h - 10) / 2, outlink->h - 35, channel_name, 1); + if (j & step) + j -= step; } if (s->draw_persistent_duration > 0.) { @@ -415,13 +408,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) } } - if (s->h >= 8 && s->draw_text) { - int ret = av_channel_name(channel_name, sizeof(channel_name), av_channel_layout_channel_from_index(&insamples->ch_layout, c)); - if (ret < 0) - continue; - drawtext(s->out, 2, c * (s->h + s->b) + (s->h - 8) / 2, channel_name, 0); - } - if (s->draw_persistent_duration > 0.) { calc_persistent_max(s, max, c); max_draw = FFMAX(0, calc_max_draw(s, outlink, s->max_persistent[c]) - 1); @@ -440,6 +426,21 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) return ret; } + /* draw channel names */ + for (c = 0; c < inlink->ch_layout.nb_channels && s->h >= 10 && s->draw_text; c++) { + if (s->orientation) { /* vertical */ + int ret = av_channel_name(channel_name, sizeof(channel_name), av_channel_layout_channel_from_index(&inlink->ch_layout, c)); + if (ret < 0) + continue; + drawtext(out, c * (s->h + s->b) + (s->h - 10) / 2, outlink->h - 35, channel_name, 1); + } else { /* horizontal */ + int ret = av_channel_name(channel_name, sizeof(channel_name), av_channel_layout_channel_from_index(&inlink->ch_layout, c)); + if (ret < 0) + continue; + drawtext(out, 2, c * (s->h + s->b) + (s->h - 8) / 2, channel_name, 0); + } + } + /* draw volume level */ for (c = 0; c < inlink->ch_layout.nb_channels && s->h >= 8 && s->draw_volume; c++) { char buf[16]; diff --git a/libavfilter/avf_showwaves.c b/libavfilter/avf_showwaves.c index 76399ab13dd..f090d937148 100644 --- a/libavfilter/avf_showwaves.c +++ b/libavfilter/avf_showwaves.c @@ -28,6 +28,7 @@ #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/channel_layout.h" +#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "avfilter.h" @@ -77,10 +78,12 @@ typedef struct ShowWavesContext { char *colors; int buf_idx; int16_t *buf_idy; /* y coordinate of previous sample for each channel */ + int16_t *history; + int history_nb_samples; + int history_index; AVFrame *outpicref; - int n; + AVRational n, q, c; int pixstep; - int sample_count_mod; int mode; ///< ShowWavesMode int scale; ///< ShowWavesScale int draw_mode; ///< ShowWavesDrawMode @@ -106,12 +109,12 @@ typedef struct ShowWavesContext { static const AVOption showwaves_options[] = { { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS }, { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "600x240"}, 0, 0, FLAGS }, - { "mode", "select display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_POINT}, 0, MODE_NB-1, FLAGS, "mode"}, + { "mode", "select display mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_POINT}, 0, MODE_NB-1, .flags=FLAGS, .unit="mode"}, { "point", "draw a point for each sample", 0, AV_OPT_TYPE_CONST, {.i64=MODE_POINT}, .flags=FLAGS, .unit="mode"}, { "line", "draw a line for each sample", 0, AV_OPT_TYPE_CONST, {.i64=MODE_LINE}, .flags=FLAGS, .unit="mode"}, { "p2p", "draw a line between samples", 0, AV_OPT_TYPE_CONST, {.i64=MODE_P2P}, .flags=FLAGS, .unit="mode"}, { "cline", "draw a centered line for each sample", 0, AV_OPT_TYPE_CONST, {.i64=MODE_CENTERED_LINE}, .flags=FLAGS, .unit="mode"}, - { "n", "set how many samples to show in the same point", OFFSET(n), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS }, + { "n", "set how many samples to show in the same point", OFFSET(n), AV_OPT_TYPE_RATIONAL, {.i64 = 0}, 0, INT_MAX, FLAGS }, { "rate", "set video rate", OFFSET(rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS }, { "r", "set video rate", OFFSET(rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS }, { "split_channels", "draw channels separately", OFFSET(split_channels), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, @@ -135,6 +138,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&showwaves->outpicref); av_freep(&showwaves->buf_idy); + av_freep(&showwaves->history); av_freep(&showwaves->fg); if (showwaves->single_pic) { @@ -238,28 +242,26 @@ static void draw_sample_point_rgba_full(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - if (h >= 0 && h < height) { - buf[h * linesize + 0] = color[0]; - buf[h * linesize + 1] = color[1]; - buf[h * linesize + 2] = color[2]; - buf[h * linesize + 3] = color[3]; - } + uint32_t clr = AV_RN32(color); + if (h >= 0 && h < height) + AV_WN32(buf + h * linesize, clr); } static void draw_sample_line_rgba_scale(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - int k; int start = height/2; int end = av_clip(h, 0, height-1); + uint8_t *bufk; if (start > end) FFSWAP(int16_t, start, end); - for (k = start; k < end; k++) { - buf[k * linesize + 0] += color[0]; - buf[k * linesize + 1] += color[1]; - buf[k * linesize + 2] += color[2]; - buf[k * linesize + 3] += color[3]; + bufk = buf + start * linesize; + for (int k = start; k < end; k++, bufk += linesize) { + bufk[0] += color[0]; + bufk[1] += color[1]; + bufk[2] += color[2]; + bufk[3] += color[3]; } } @@ -267,24 +269,21 @@ static void draw_sample_line_rgba_full(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - int k; int start = height/2; int end = av_clip(h, 0, height-1); + uint32_t clr = AV_RN32(color); + uint8_t *bufk; if (start > end) FFSWAP(int16_t, start, end); - for (k = start; k < end; k++) { - buf[k * linesize + 0] = color[0]; - buf[k * linesize + 1] = color[1]; - buf[k * linesize + 2] = color[2]; - buf[k * linesize + 3] = color[3]; - } + bufk = buf + start * linesize; + for (int k = start; k < end; k++, bufk += linesize) + AV_WN32(bufk, clr); } static void draw_sample_p2p_rgba_scale(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - int k; if (h >= 0 && h < height) { buf[h * linesize + 0] += color[0]; buf[h * linesize + 1] += color[1]; @@ -292,14 +291,16 @@ static void draw_sample_p2p_rgba_scale(uint8_t *buf, int height, int linesize, buf[h * linesize + 3] += color[3]; if (*prev_y && h != *prev_y) { int start = *prev_y; + uint8_t *bufk; int end = av_clip(h, 0, height-1); if (start > end) FFSWAP(int16_t, start, end); - for (k = start + 1; k < end; k++) { - buf[k * linesize + 0] += color[0]; - buf[k * linesize + 1] += color[1]; - buf[k * linesize + 2] += color[2]; - buf[k * linesize + 3] += color[3]; + bufk = buf + (start + 1) * linesize; + for (int k = start + 1; k < end; k++, bufk += linesize) { + bufk[0] += color[0]; + bufk[1] += color[1]; + bufk[2] += color[2]; + bufk[3] += color[3]; } } } @@ -310,23 +311,18 @@ static void draw_sample_p2p_rgba_full(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - int k; + uint32_t clr = AV_RN32(color); if (h >= 0 && h < height) { - buf[h * linesize + 0] = color[0]; - buf[h * linesize + 1] = color[1]; - buf[h * linesize + 2] = color[2]; - buf[h * linesize + 3] = color[3]; + AV_WN32(buf + h * linesize, clr); if (*prev_y && h != *prev_y) { int start = *prev_y; + uint8_t *bufk; int end = av_clip(h, 0, height-1); if (start > end) FFSWAP(int16_t, start, end); - for (k = start + 1; k < end; k++) { - buf[k * linesize + 0] = color[0]; - buf[k * linesize + 1] = color[1]; - buf[k * linesize + 2] = color[2]; - buf[k * linesize + 3] = color[3]; - } + bufk = buf + (start + 1) * linesize; + for (int k = start + 1; k < end; k++, bufk += linesize) + AV_WN32(bufk, clr); } } *prev_y = h; @@ -336,29 +332,27 @@ static void draw_sample_cline_rgba_scale(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - int k; const int start = (height - h) / 2; const int end = start + h; - for (k = start; k < end; k++) { - buf[k * linesize + 0] += color[0]; - buf[k * linesize + 1] += color[1]; - buf[k * linesize + 2] += color[2]; - buf[k * linesize + 3] += color[3]; + uint8_t *bufk = buf + start * linesize; + for (int k = start; k < end; k++, bufk += linesize) { + bufk[0] += color[0]; + bufk[1] += color[1]; + bufk[2] += color[2]; + bufk[3] += color[3]; } } - static void draw_sample_cline_rgba_full(uint8_t *buf, int height, int linesize, + +static void draw_sample_cline_rgba_full(uint8_t *buf, int height, int linesize, int16_t *prev_y, const uint8_t color[4], int h) { - int k; + uint32_t clr = AV_RN32(color); const int start = (height - h) / 2; const int end = start + h; - for (k = start; k < end; k++) { - buf[k * linesize + 0] = color[0]; - buf[k * linesize + 1] = color[1]; - buf[k * linesize + 2] = color[2]; - buf[k * linesize + 3] = color[3]; - } + uint8_t *bufk = buf + start * linesize; + for (int k = start; k < end; k++, bufk += linesize) + AV_WN32(bufk, clr); } static void draw_sample_point_gray(uint8_t *buf, int height, int linesize, @@ -422,29 +416,44 @@ static int config_output(AVFilterLink *outlink) uint8_t x; int ch; - if (showwaves->single_pic) - showwaves->n = 1; + showwaves->q = av_make_q(0, 1); + showwaves->c = av_make_q(0, 1); - if (!showwaves->n) - showwaves->n = FFMAX(1, av_rescale_q(inlink->sample_rate, av_make_q(1, showwaves->w), showwaves->rate)); + if (showwaves->single_pic) { + showwaves->n = av_make_q(1, 1); + outlink->frame_rate = av_make_q(1, 1); + } else { + if (!showwaves->n.num || !showwaves->n.den) { + showwaves->n = av_mul_q(av_make_q(inlink->sample_rate, + showwaves->w), av_inv_q(showwaves->rate)); + outlink->frame_rate = showwaves->rate; + } else { + outlink->frame_rate = av_div_q(av_make_q(inlink->sample_rate, showwaves->w), showwaves->n); + } + } showwaves->buf_idx = 0; if (!FF_ALLOCZ_TYPED_ARRAY(showwaves->buf_idy, nb_channels)) { av_log(ctx, AV_LOG_ERROR, "Could not allocate showwaves buffer\n"); return AVERROR(ENOMEM); } + + showwaves->history_nb_samples = av_rescale(showwaves->w * nb_channels * 2, + showwaves->n.num, showwaves->n.den); + if (showwaves->history_nb_samples <= 0) + return AVERROR(EINVAL); + showwaves->history = av_calloc(showwaves->history_nb_samples, + sizeof(*showwaves->history)); + if (!showwaves->history) + return AVERROR(ENOMEM); + + outlink->time_base = av_inv_q(outlink->frame_rate); outlink->w = showwaves->w; outlink->h = showwaves->h; outlink->sample_aspect_ratio = (AVRational){1,1}; - if (showwaves->single_pic) - outlink->frame_rate = av_make_q(1, 1); - else - outlink->frame_rate = av_div_q((AVRational){inlink->sample_rate,showwaves->n}, - (AVRational){showwaves->w,1}); - - av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d r:%f n:%d\n", - showwaves->w, showwaves->h, av_q2d(outlink->frame_rate), showwaves->n); + av_log(ctx, AV_LOG_VERBOSE, "s:%dx%d r:%f n:%f\n", + showwaves->w, showwaves->h, av_q2d(outlink->frame_rate), av_q2d(showwaves->n)); switch (outlink->format) { case AV_PIX_FMT_GRAY8: @@ -524,7 +533,7 @@ static int config_output(AVFilterLink *outlink) if (showwaves->draw_mode == DRAW_SCALE) { /* multiplication factor, pre-computed to avoid in-loop divisions */ - x = 255 / ((showwaves->split_channels ? 1 : nb_channels) * showwaves->n); + x = (showwaves->n.den * 255) / ((showwaves->split_channels ? 1 : nb_channels) * showwaves->n.num); } else { x = 255; } @@ -551,18 +560,23 @@ static int config_output(AVFilterLink *outlink) return 0; } -inline static int push_frame(AVFilterLink *outlink) +inline static int push_frame(AVFilterLink *outlink, int i, int64_t pts) { AVFilterContext *ctx = outlink->src; AVFilterLink *inlink = ctx->inputs[0]; ShowWavesContext *showwaves = outlink->src->priv; int nb_channels = inlink->ch_layout.nb_channels; - int ret, i; + int ret; + + showwaves->outpicref->duration = 1; + showwaves->outpicref->pts = av_rescale_q(pts + i, + inlink->time_base, + outlink->time_base); ret = ff_filter_frame(outlink, showwaves->outpicref); showwaves->outpicref = NULL; showwaves->buf_idx = 0; - for (i = 0; i < nb_channels; i++) + for (int i = 0; i < nb_channels; i++) showwaves->buf_idy[i] = 0; return ret; } @@ -591,7 +605,7 @@ static int push_single_pic(AVFilterLink *outlink) av_log(ctx, AV_LOG_DEBUG, "Create frame averaging %"PRId64" samples per column\n", column_max_samples); - memset(sum, 0, nb_channels); + memset(sum, 0, nb_channels * sizeof(*sum)); for (node = showwaves->audio_frames; node; node = node->next) { int i; @@ -633,7 +647,7 @@ static int push_single_pic(AVFilterLink *outlink) } } - return push_frame(outlink); + return push_frame(outlink, 0, 0); } @@ -645,31 +659,23 @@ static int request_frame(AVFilterLink *outlink) ret = ff_request_frame(inlink); if (ret == AVERROR_EOF && showwaves->outpicref) { - if (showwaves->single_pic) - push_single_pic(outlink); - else - push_frame(outlink); + push_single_pic(outlink); } return ret; } -static int alloc_out_frame(ShowWavesContext *showwaves, const int16_t *p, - const AVFilterLink *inlink, AVFilterLink *outlink, - const AVFrame *in) +static int alloc_out_frame(ShowWavesContext *showwaves, + AVFilterLink *outlink) { if (!showwaves->outpicref) { - int j; AVFrame *out = showwaves->outpicref = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) return AVERROR(ENOMEM); out->width = outlink->w; out->height = outlink->h; - out->pts = in->pts + av_rescale_q((p - (int16_t *)in->data[0]) / inlink->ch_layout.nb_channels, - av_make_q(1, inlink->sample_rate), - outlink->time_base); - for (j = 0; j < outlink->h; j++) + for (int j = 0; j < outlink->h; j++) memset(out->data[0] + j*out->linesize[0], 0, outlink->w * showwaves->pixstep); } return 0; @@ -696,45 +702,67 @@ static int showwaves_filter_frame(AVFilterLink *inlink, AVFrame *insamples) ShowWavesContext *showwaves = ctx->priv; const int nb_samples = insamples->nb_samples; AVFrame *outpicref = showwaves->outpicref; - int16_t *p = (int16_t *)insamples->data[0]; - int nb_channels = inlink->ch_layout.nb_channels; - int i, j, ret = 0; + const int16_t *p = (const int16_t *)insamples->data[0]; + int16_t *history = showwaves->history; + const int nb_channels = inlink->ch_layout.nb_channels; + int i, j, ret = 0, linesize; const int pixstep = showwaves->pixstep; - const int n = showwaves->n; const int ch_height = showwaves->split_channels ? outlink->h / nb_channels : outlink->h; + const int history_nb_samples = showwaves->history_nb_samples; + const int split_channels = showwaves->split_channels; + const AVRational i_n = av_inv_q(showwaves->n); + const AVRational u_q = av_make_q(1, 1); + const AVRational z_q = av_make_q(0, 1); + int16_t *buf_idy = showwaves->buf_idy; + int idx = showwaves->history_index; + int buf_idx = showwaves->buf_idx; + const uint8_t *fg = showwaves->fg; + const int w = showwaves->w; + uint8_t *dst; + + for (int n = 0; n < nb_samples * nb_channels; n++) { + history[idx++] = p[n]; + if (idx >= history_nb_samples) + idx = 0; + } + showwaves->history_index = idx; - /* draw data in the buffer */ - for (i = 0; i < nb_samples; i++) { - - ret = alloc_out_frame(showwaves, p, inlink, outlink, insamples); - if (ret < 0) - goto end; - outpicref = showwaves->outpicref; + ret = alloc_out_frame(showwaves, outlink); + if (ret < 0) + goto end; + outpicref = showwaves->outpicref; + linesize = outpicref->linesize[0]; + /* draw data in the buffer */ + dst = outpicref->data[0]; + for (i = 0; i < history_nb_samples; i++) { for (j = 0; j < nb_channels; j++) { - uint8_t *buf = outpicref->data[0] + showwaves->buf_idx * pixstep; - const int linesize = outpicref->linesize[0]; + uint8_t *buf = dst + buf_idx * pixstep; int h; - if (showwaves->split_channels) + if (split_channels) buf += j*ch_height*linesize; - h = showwaves->get_h(*p++, ch_height); + h = showwaves->get_h(history[idx++], ch_height); + if (idx >= history_nb_samples) + idx = 0; showwaves->draw_sample(buf, ch_height, linesize, - &showwaves->buf_idy[j], &showwaves->fg[j * 4], h); + &buf_idy[j], &fg[j * 4], h); } - showwaves->sample_count_mod++; - if (showwaves->sample_count_mod == n) { - showwaves->sample_count_mod = 0; - showwaves->buf_idx++; + showwaves->c = av_add_q(showwaves->c, i_n); + if (av_cmp_q(showwaves->c, u_q) >= 0) { + showwaves->c = z_q; + buf_idx++; } - if (showwaves->buf_idx == showwaves->w || - (ff_outlink_get_status(inlink) && i == nb_samples - 1)) - if ((ret = push_frame(outlink)) < 0) - break; - outpicref = showwaves->outpicref; + if (buf_idx == w) + break; } + showwaves->buf_idx = buf_idx; + + if ((ret = push_frame(outlink, history_nb_samples - i - 1, insamples->pts)) < 0) + goto end; + outpicref = showwaves->outpicref; end: av_frame_free(&insamples); return ret; @@ -745,17 +773,22 @@ static int activate(AVFilterContext *ctx) AVFilterLink *inlink = ctx->inputs[0]; AVFilterLink *outlink = ctx->outputs[0]; ShowWavesContext *showwaves = ctx->priv; + AVRational q; AVFrame *in; - const int nb_samples = showwaves->n * outlink->w; + int nb_samples; int ret; FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + q = av_add_q(showwaves->q, av_mul_q(av_make_q(outlink->w, 1), showwaves->n)); + nb_samples = (q.num + (q.den / 2)) / q.den; ret = ff_inlink_consume_samples(inlink, nb_samples, nb_samples, &in); if (ret < 0) return ret; - if (ret > 0) + if (ret > 0) { + showwaves->q = av_sub_q(q, av_make_q(nb_samples, 1)); return showwaves_filter_frame(inlink, in); + } FF_FILTER_FORWARD_STATUS(inlink, outlink); FF_FILTER_FORWARD_WANTED(outlink, inlink); @@ -763,13 +796,6 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad showwaves_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad showwaves_outputs[] = { { .name = "default", @@ -784,7 +810,7 @@ const AVFilter ff_avf_showwaves = { .init = init, .uninit = uninit, .priv_size = sizeof(ShowWavesContext), - FILTER_INPUTS(showwaves_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), .activate = activate, FILTER_OUTPUTS(showwaves_outputs), FILTER_QUERY_FUNC(query_formats), @@ -838,13 +864,12 @@ static int showwavespic_filter_frame(AVFilterLink *inlink, AVFrame *insamples) AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; ShowWavesContext *showwaves = ctx->priv; - int16_t *p = (int16_t *)insamples->data[0]; int ret = 0; if (showwaves->single_pic) { struct frame_node *f; - ret = alloc_out_frame(showwaves, p, inlink, outlink, insamples); + ret = alloc_out_frame(showwaves, outlink); if (ret < 0) goto end; diff --git a/libavfilter/avfilter.c b/libavfilter/avfilter.c index ec83b691bf8..831871de90b 100644 --- a/libavfilter/avfilter.c +++ b/libavfilter/avfilter.c @@ -34,32 +34,32 @@ #include "libavutil/rational.h" #include "libavutil/samplefmt.h" -#define FF_INTERNAL_FIELDS 1 -#include "framequeue.h" - #include "audio.h" #include "avfilter.h" +#include "avfilter_internal.h" #include "filters.h" #include "formats.h" +#include "framequeue.h" #include "framepool.h" #include "internal.h" +#include "video.h" static void tlog_ref(void *ctx, AVFrame *ref, int end) { #ifdef TRACE ff_tlog(ctx, - "ref[%p buf:%p data:%p linesize[%d, %d, %d, %d] pts:%"PRId64" pos:%"PRId64, + "ref[%p buf:%p data:%p linesize[%d, %d, %d, %d] pts:%"PRId64, ref, ref->buf, ref->data[0], ref->linesize[0], ref->linesize[1], ref->linesize[2], ref->linesize[3], - ref->pts, ref->pkt_pos); + ref->pts); if (ref->width) { ff_tlog(ctx, " a:%d/%d s:%dx%d i:%c iskey:%d type:%c", ref->sample_aspect_ratio.num, ref->sample_aspect_ratio.den, ref->width, ref->height, - !ref->interlaced_frame ? 'P' : /* Progressive */ - ref->top_field_first ? 'T' : 'B', /* Top / Bottom */ - ref->key_frame, + !(ref->flags & AV_FRAME_FLAG_INTERLACED) ? 'P' : /* Progressive */ + (ref->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 'T' : 'B', /* Top / Bottom */ + !!(ref->flags & AV_FRAME_FLAG_KEY), av_get_picture_type_char(ref->pict_type)); } if (ref->nb_samples) { @@ -148,6 +148,7 @@ int ff_append_outpad_free_name(AVFilterContext *f, AVFilterPad *p) int avfilter_link(AVFilterContext *src, unsigned srcpad, AVFilterContext *dst, unsigned dstpad) { + FilterLinkInternal *li; AVFilterLink *link; av_assert0(src->graph); @@ -158,7 +159,7 @@ int avfilter_link(AVFilterContext *src, unsigned srcpad, src->outputs[srcpad] || dst->inputs[dstpad]) return AVERROR(EINVAL); - if (!src->internal->initialized || !dst->internal->initialized) { + if (!fffilterctx(src)->initialized || !fffilterctx(dst)->initialized) { av_log(src, AV_LOG_ERROR, "Filters must be initialized before linking.\n"); return AVERROR(EINVAL); } @@ -171,9 +172,10 @@ int avfilter_link(AVFilterContext *src, unsigned srcpad, return AVERROR(EINVAL); } - link = av_mallocz(sizeof(*link)); - if (!link) + li = av_mallocz(sizeof(*li)); + if (!li) return AVERROR(ENOMEM); + link = &li->l; src->outputs[srcpad] = dst->inputs[dstpad] = link; @@ -184,23 +186,51 @@ int avfilter_link(AVFilterContext *src, unsigned srcpad, link->type = src->output_pads[srcpad].type; av_assert0(AV_PIX_FMT_NONE == -1 && AV_SAMPLE_FMT_NONE == -1); link->format = -1; - ff_framequeue_init(&link->fifo, &src->graph->internal->frame_queues); + link->colorspace = AVCOL_SPC_UNSPECIFIED; + ff_framequeue_init(&li->fifo, &fffiltergraph(src->graph)->frame_queues); return 0; } -void avfilter_link_free(AVFilterLink **link) +static void link_free(AVFilterLink **link) { + FilterLinkInternal *li; + if (!*link) return; + li = ff_link_internal(*link); - ff_framequeue_free(&(*link)->fifo); - ff_frame_pool_uninit((FFFramePool**)&(*link)->frame_pool); + ff_framequeue_free(&li->fifo); + ff_frame_pool_uninit(&li->frame_pool); av_channel_layout_uninit(&(*link)->ch_layout); av_freep(link); } +#if FF_API_LINK_PUBLIC +void avfilter_link_free(AVFilterLink **link) +{ + link_free(link); +} +int avfilter_config_links(AVFilterContext *filter) +{ + return ff_filter_config_links(filter); +} +#endif + +static void update_link_current_pts(FilterLinkInternal *li, int64_t pts) +{ + AVFilterLink *const link = &li->l; + + if (pts == AV_NOPTS_VALUE) + return; + link->current_pts = pts; + link->current_pts_us = av_rescale_q(pts, link->time_base, AV_TIME_BASE_Q); + /* TODO use duration */ + if (link->graph && li->age_index >= 0) + ff_avfilter_graph_update_heap(link->graph, li); +} + void ff_filter_set_ready(AVFilterContext *filter, unsigned priority) { filter->ready = FFMAX(filter->ready, priority); @@ -214,31 +244,41 @@ static void filter_unblock(AVFilterContext *filter) { unsigned i; - for (i = 0; i < filter->nb_outputs; i++) - filter->outputs[i]->frame_blocked_in = 0; + for (i = 0; i < filter->nb_outputs; i++) { + FilterLinkInternal * const li = ff_link_internal(filter->outputs[i]); + li->frame_blocked_in = 0; + } } void ff_avfilter_link_set_in_status(AVFilterLink *link, int status, int64_t pts) { - if (link->status_in == status) + FilterLinkInternal * const li = ff_link_internal(link); + + if (li->status_in == status) return; - av_assert0(!link->status_in); - link->status_in = status; - link->status_in_pts = pts; + av_assert0(!li->status_in); + li->status_in = status; + li->status_in_pts = pts; link->frame_wanted_out = 0; - link->frame_blocked_in = 0; + li->frame_blocked_in = 0; filter_unblock(link->dst); ff_filter_set_ready(link->dst, 200); } -void ff_avfilter_link_set_out_status(AVFilterLink *link, int status, int64_t pts) +/** + * Set the status field of a link from the destination filter. + * The pts should probably be left unset (AV_NOPTS_VALUE). + */ +static void link_set_out_status(AVFilterLink *link, int status, int64_t pts) { + FilterLinkInternal * const li = ff_link_internal(link); + av_assert0(!link->frame_wanted_out); - av_assert0(!link->status_out); - link->status_out = status; + av_assert0(!li->status_out); + li->status_out = status; if (pts != AV_NOPTS_VALUE) - ff_update_link_current_pts(link, pts); + update_link_current_pts(li, pts); filter_unblock(link->dst); ff_filter_set_ready(link->src, 200); } @@ -270,6 +310,12 @@ int avfilter_insert_filter(AVFilterLink *link, AVFilterContext *filt, if (link->outcfg.formats) ff_formats_changeref(&link->outcfg.formats, &filt->outputs[filt_dstpad_idx]->outcfg.formats); + if (link->outcfg.color_spaces) + ff_formats_changeref(&link->outcfg.color_spaces, + &filt->outputs[filt_dstpad_idx]->outcfg.color_spaces); + if (link->outcfg.color_ranges) + ff_formats_changeref(&link->outcfg.color_ranges, + &filt->outputs[filt_dstpad_idx]->outcfg.color_ranges); if (link->outcfg.samplerates) ff_formats_changeref(&link->outcfg.samplerates, &filt->outputs[filt_dstpad_idx]->outcfg.samplerates); @@ -280,7 +326,7 @@ int avfilter_insert_filter(AVFilterLink *link, AVFilterContext *filt, return 0; } -int avfilter_config_links(AVFilterContext *filter) +int ff_filter_config_links(AVFilterContext *filter) { int (*config_link)(AVFilterLink *); unsigned i; @@ -289,6 +335,7 @@ int avfilter_config_links(AVFilterContext *filter) for (i = 0; i < filter->nb_inputs; i ++) { AVFilterLink *link = filter->inputs[i]; AVFilterLink *inlink; + FilterLinkInternal *li = ff_link_internal(link); if (!link) continue; if (!link->src || !link->dst) { @@ -301,16 +348,16 @@ int avfilter_config_links(AVFilterContext *filter) link->current_pts = link->current_pts_us = AV_NOPTS_VALUE; - switch (link->init_state) { + switch (li->init_state) { case AVLINK_INIT: continue; case AVLINK_STARTINIT: av_log(filter, AV_LOG_INFO, "circular filter chain detected\n"); return 0; case AVLINK_UNINIT: - link->init_state = AVLINK_STARTINIT; + li->init_state = AVLINK_STARTINIT; - if ((ret = avfilter_config_links(link->src)) < 0) + if ((ret = ff_filter_config_links(link->src)) < 0) return ret; if (!(config_link = link->srcpad->config_props)) { @@ -379,7 +426,7 @@ int avfilter_config_links(AVFilterContext *filter) return ret; } - link->init_state = AVLINK_INIT; + li->init_state = AVLINK_INIT; } } @@ -414,13 +461,15 @@ void ff_tlog_link(void *ctx, AVFilterLink *link, int end) int ff_request_frame(AVFilterLink *link) { + FilterLinkInternal * const li = ff_link_internal(link); + FF_TPRINTF_START(NULL, request_frame); ff_tlog_link(NULL, link, 1); av_assert1(!link->dst->filter->activate); - if (link->status_out) - return link->status_out; - if (link->status_in) { - if (ff_framequeue_queued_frames(&link->fifo)) { + if (li->status_out) + return li->status_out; + if (li->status_in) { + if (ff_framequeue_queued_frames(&li->fifo)) { av_assert1(!link->frame_wanted_out); av_assert1(link->dst->ready >= 300); return 0; @@ -428,8 +477,8 @@ int ff_request_frame(AVFilterLink *link) /* Acknowledge status change. Filters using ff_request_frame() will handle the change automatically. Filters can also check the status directly but none do yet. */ - ff_avfilter_link_set_out_status(link, link->status_in, link->status_in_pts); - return link->status_out; + link_set_out_status(link, li->status_in, li->status_in_pts); + return li->status_out; } } link->frame_wanted_out = 1; @@ -442,14 +491,18 @@ static int64_t guess_status_pts(AVFilterContext *ctx, int status, AVRational lin unsigned i; int64_t r = INT64_MAX; - for (i = 0; i < ctx->nb_inputs; i++) - if (ctx->inputs[i]->status_out == status) + for (i = 0; i < ctx->nb_inputs; i++) { + FilterLinkInternal * const li = ff_link_internal(ctx->inputs[i]); + if (li->status_out == status) r = FFMIN(r, av_rescale_q(ctx->inputs[i]->current_pts, ctx->inputs[i]->time_base, link_time_base)); + } if (r < INT64_MAX) return r; av_log(ctx, AV_LOG_WARNING, "EOF timestamp not reliable\n"); - for (i = 0; i < ctx->nb_inputs; i++) - r = FFMIN(r, av_rescale_q(ctx->inputs[i]->status_in_pts, ctx->inputs[i]->time_base, link_time_base)); + for (i = 0; i < ctx->nb_inputs; i++) { + FilterLinkInternal * const li = ff_link_internal(ctx->inputs[i]); + r = FFMIN(r, av_rescale_q(li->status_in_pts, ctx->inputs[i]->time_base, link_time_base)); + } if (r < INT64_MAX) return r; return AV_NOPTS_VALUE; @@ -457,17 +510,18 @@ static int64_t guess_status_pts(AVFilterContext *ctx, int status, AVRational lin static int ff_request_frame_to_filter(AVFilterLink *link) { + FilterLinkInternal * const li = ff_link_internal(link); int ret = -1; FF_TPRINTF_START(NULL, request_frame_to_filter); ff_tlog_link(NULL, link, 1); /* Assume the filter is blocked, let the method clear it if not */ - link->frame_blocked_in = 1; + li->frame_blocked_in = 1; if (link->srcpad->request_frame) ret = link->srcpad->request_frame(link); else if (link->src->inputs[0]) ret = ff_request_frame(link->src->inputs[0]); if (ret < 0) { - if (ret != AVERROR(EAGAIN) && ret != link->status_in) + if (ret != AVERROR(EAGAIN) && ret != li->status_in) ff_avfilter_link_set_in_status(link, ret, guess_status_pts(link->src, ret, link->time_base)); if (ret == AVERROR_EOF) ret = 0; @@ -478,7 +532,9 @@ static int ff_request_frame_to_filter(AVFilterLink *link) static const char *const var_names[] = { "t", "n", +#if FF_API_FRAME_PKT "pos", +#endif "w", "h", NULL @@ -487,7 +543,9 @@ static const char *const var_names[] = { enum { VAR_T, VAR_N, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_W, VAR_H, VAR_VARS_NB @@ -533,17 +591,6 @@ static int set_enable_expr(AVFilterContext *ctx, const char *expr) return 0; } -void ff_update_link_current_pts(AVFilterLink *link, int64_t pts) -{ - if (pts == AV_NOPTS_VALUE) - return; - link->current_pts = pts; - link->current_pts_us = av_rescale_q(pts, link->time_base, AV_TIME_BASE_Q); - /* TODO use duration */ - if (link->graph && link->age_index >= 0) - ff_avfilter_graph_update_heap(link->graph, link); -} - int avfilter_process_command(AVFilterContext *filter, const char *cmd, const char *arg, char *res, int res_len, int flags) { if(!strcmp(cmd, "ping")){ @@ -600,7 +647,7 @@ static const AVClass *filter_child_class_iterate(void **iter) #define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption avfilter_options[] = { { "thread_type", "Allowed thread types", OFFSET(thread_type), AV_OPT_TYPE_FLAGS, - { .i64 = AVFILTER_THREAD_SLICE }, 0, INT_MAX, FLAGS, "thread_type" }, + { .i64 = AVFILTER_THREAD_SLICE }, 0, INT_MAX, FLAGS, .unit = "thread_type" }, { "slice", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AVFILTER_THREAD_SLICE }, .flags = FLAGS, .unit = "thread_type" }, { "enable", "set enable expression", OFFSET(enable_str), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = TFLAGS }, { "threads", "Allowed number of threads", OFFSET(nb_threads), AV_OPT_TYPE_INT, @@ -635,15 +682,17 @@ static int default_execute(AVFilterContext *ctx, avfilter_action_func *func, voi AVFilterContext *ff_filter_alloc(const AVFilter *filter, const char *inst_name) { + FFFilterContext *ctx; AVFilterContext *ret; int preinited = 0; if (!filter) return NULL; - ret = av_mallocz(sizeof(AVFilterContext)); - if (!ret) + ctx = av_mallocz(sizeof(*ctx)); + if (!ctx) return NULL; + ret = &ctx->p; ret->av_class = &avfilter_class; ret->filter = filter; @@ -665,10 +714,7 @@ AVFilterContext *ff_filter_alloc(const AVFilter *filter, const char *inst_name) av_opt_set_defaults(ret->priv); } - ret->internal = av_mallocz(sizeof(*ret->internal)); - if (!ret->internal) - goto err; - ret->internal->execute = default_execute; + ctx->execute = default_execute; ret->nb_inputs = filter->nb_inputs; if (ret->nb_inputs ) { @@ -702,7 +748,6 @@ AVFilterContext *ff_filter_alloc(const AVFilter *filter, const char *inst_name) av_freep(&ret->output_pads); ret->nb_outputs = 0; av_freep(&ret->priv); - av_freep(&ret->internal); av_free(ret); return NULL; } @@ -721,11 +766,15 @@ static void free_link(AVFilterLink *link) ff_formats_unref(&link->incfg.formats); ff_formats_unref(&link->outcfg.formats); + ff_formats_unref(&link->incfg.color_spaces); + ff_formats_unref(&link->outcfg.color_spaces); + ff_formats_unref(&link->incfg.color_ranges); + ff_formats_unref(&link->outcfg.color_ranges); ff_formats_unref(&link->incfg.samplerates); ff_formats_unref(&link->outcfg.samplerates); ff_channel_layouts_unref(&link->incfg.channel_layouts); ff_channel_layouts_unref(&link->outcfg.channel_layouts); - avfilter_link_free(&link); + link_free(&link); } void avfilter_free(AVFilterContext *filter) @@ -770,7 +819,6 @@ void avfilter_free(AVFilterContext *filter) av_expr_free(filter->enable); filter->enable = NULL; av_freep(&filter->var_values); - av_freep(&filter->internal); av_free(filter); } @@ -854,9 +902,10 @@ int ff_filter_process_command(AVFilterContext *ctx, const char *cmd, int avfilter_init_dict(AVFilterContext *ctx, AVDictionary **options) { + FFFilterContext *ctxi = fffilterctx(ctx); int ret = 0; - if (ctx->internal->initialized) { + if (ctxi->initialized) { av_log(ctx, AV_LOG_ERROR, "Filter already initialized\n"); return AVERROR(EINVAL); } @@ -869,9 +918,9 @@ int avfilter_init_dict(AVFilterContext *ctx, AVDictionary **options) if (ctx->filter->flags & AVFILTER_FLAG_SLICE_THREADS && ctx->thread_type & ctx->graph->thread_type & AVFILTER_THREAD_SLICE && - ctx->graph->internal->thread_execute) { + fffiltergraph(ctx->graph)->thread_execute) { ctx->thread_type = AVFILTER_THREAD_SLICE; - ctx->internal->execute = ctx->graph->internal->thread_execute; + ctxi->execute = fffiltergraph(ctx->graph)->thread_execute; } else { ctx->thread_type = 0; } @@ -887,7 +936,7 @@ int avfilter_init_dict(AVFilterContext *ctx, AVDictionary **options) return ret; } - ctx->internal->initialized = 1; + ctxi->initialized = 1; return 0; } @@ -968,6 +1017,7 @@ static int ff_filter_frame_framed(AVFilterLink *link, AVFrame *frame) int ff_filter_frame(AVFilterLink *link, AVFrame *frame) { + FilterLinkInternal * const li = ff_link_internal(link); int ret; FF_TPRINTF_START(NULL, filter_frame); ff_tlog_link(NULL, link, 1); ff_tlog(NULL, " "); tlog_ref(NULL, frame, 1); @@ -978,10 +1028,12 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame) strcmp(link->dst->filter->name, "idet") && strcmp(link->dst->filter->name, "null") && strcmp(link->dst->filter->name, "scale")) { - av_assert1(frame->format == link->format); - av_assert1(frame->width == link->w); - av_assert1(frame->height == link->h); + av_assert1(frame->format == link->format); + av_assert1(frame->width == link->w); + av_assert1(frame->height == link->h); } + + frame->sample_aspect_ratio = link->sample_aspect_ratio; } else { if (frame->format != link->format) { av_log(link->dst, AV_LOG_ERROR, "Format change is not supported\n"); @@ -995,13 +1047,16 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame) av_log(link->dst, AV_LOG_ERROR, "Sample rate change is not supported\n"); goto error; } + + frame->duration = av_rescale_q(frame->nb_samples, (AVRational){ 1, frame->sample_rate }, + link->time_base); } - link->frame_blocked_in = link->frame_wanted_out = 0; + li->frame_blocked_in = link->frame_wanted_out = 0; link->frame_count_in++; link->sample_count_in += frame->nb_samples; filter_unblock(link->dst); - ret = ff_framequeue_add(&link->fifo, frame); + ret = ff_framequeue_add(&li->fifo, frame); if (ret < 0) { av_frame_free(&frame); return ret; @@ -1014,26 +1069,27 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame) return AVERROR_PATCHWELCOME; } -static int samples_ready(AVFilterLink *link, unsigned min) +static int samples_ready(FilterLinkInternal *link, unsigned min) { return ff_framequeue_queued_frames(&link->fifo) && (ff_framequeue_queued_samples(&link->fifo) >= min || link->status_in); } -static int take_samples(AVFilterLink *link, unsigned min, unsigned max, +static int take_samples(FilterLinkInternal *li, unsigned min, unsigned max, AVFrame **rframe) { + AVFilterLink *link = &li->l; AVFrame *frame0, *frame, *buf; unsigned nb_samples, nb_frames, i, p; int ret; /* Note: this function relies on no format changes and must only be called with enough samples. */ - av_assert1(samples_ready(link, link->min_samples)); - frame0 = frame = ff_framequeue_peek(&link->fifo, 0); - if (!link->fifo.samples_skipped && frame->nb_samples >= min && frame->nb_samples <= max) { - *rframe = ff_framequeue_take(&link->fifo); + av_assert1(samples_ready(li, link->min_samples)); + frame0 = frame = ff_framequeue_peek(&li->fifo, 0); + if (!li->fifo.samples_skipped && frame->nb_samples >= min && frame->nb_samples <= max) { + *rframe = ff_framequeue_take(&li->fifo); return 0; } nb_frames = 0; @@ -1046,9 +1102,9 @@ static int take_samples(AVFilterLink *link, unsigned min, unsigned max, } nb_samples += frame->nb_samples; nb_frames++; - if (nb_frames == ff_framequeue_queued_frames(&link->fifo)) + if (nb_frames == ff_framequeue_queued_frames(&li->fifo)) break; - frame = ff_framequeue_peek(&link->fifo, nb_frames); + frame = ff_framequeue_peek(&li->fifo, nb_frames); } buf = ff_get_audio_buffer(link, nb_samples); @@ -1062,7 +1118,7 @@ static int take_samples(AVFilterLink *link, unsigned min, unsigned max, p = 0; for (i = 0; i < nb_frames; i++) { - frame = ff_framequeue_take(&link->fifo); + frame = ff_framequeue_take(&li->fifo); av_samples_copy(buf->extended_data, frame->extended_data, p, 0, frame->nb_samples, link->ch_layout.nb_channels, link->format); p += frame->nb_samples; @@ -1070,10 +1126,10 @@ static int take_samples(AVFilterLink *link, unsigned min, unsigned max, } if (p < nb_samples) { unsigned n = nb_samples - p; - frame = ff_framequeue_peek(&link->fifo, 0); + frame = ff_framequeue_peek(&li->fifo, 0); av_samples_copy(buf->extended_data, frame->extended_data, p, 0, n, link->ch_layout.nb_channels, link->format); - ff_framequeue_skip_samples(&link->fifo, n, link->time_base); + ff_framequeue_skip_samples(&li->fifo, n, link->time_base); } *rframe = buf; @@ -1082,11 +1138,12 @@ static int take_samples(AVFilterLink *link, unsigned min, unsigned max, static int ff_filter_frame_to_filter(AVFilterLink *link) { + FilterLinkInternal * const li = ff_link_internal(link); AVFrame *frame = NULL; AVFilterContext *dst = link->dst; int ret; - av_assert1(ff_framequeue_queued_frames(&link->fifo)); + av_assert1(ff_framequeue_queued_frames(&li->fifo)); ret = link->min_samples ? ff_inlink_consume_samples(link, link->min_samples, link->max_samples, &frame) : ff_inlink_consume_frame(link, &frame); @@ -1102,8 +1159,8 @@ static int ff_filter_frame_to_filter(AVFilterLink *link) before the frame; ff_filter_frame_framed() will re-increment it. */ link->frame_count_out--; ret = ff_filter_frame_framed(link, frame); - if (ret < 0 && ret != link->status_out) { - ff_avfilter_link_set_out_status(link, ret, AV_NOPTS_VALUE); + if (ret < 0 && ret != li->status_out) { + link_set_out_status(link, ret, AV_NOPTS_VALUE); } else { /* Run once again, to see if several frames were available, or if the input status has also changed, or any other reason. */ @@ -1112,18 +1169,21 @@ static int ff_filter_frame_to_filter(AVFilterLink *link) return ret; } -static int forward_status_change(AVFilterContext *filter, AVFilterLink *in) +static int forward_status_change(AVFilterContext *filter, FilterLinkInternal *li_in) { + AVFilterLink *in = &li_in->l; unsigned out = 0, progress = 0; int ret; - av_assert0(!in->status_out); + av_assert0(!li_in->status_out); if (!filter->nb_outputs) { /* not necessary with the current API and sinks */ return 0; } - while (!in->status_out) { - if (!filter->outputs[out]->status_in) { + while (!li_in->status_out) { + FilterLinkInternal *li_out = ff_link_internal(filter->outputs[out]); + + if (!li_out->status_in) { progress++; ret = ff_request_frame_to_filter(filter->outputs[out]); if (ret < 0) @@ -1133,7 +1193,7 @@ static int forward_status_change(AVFilterContext *filter, AVFilterLink *in) if (!progress) { /* Every output already closed: input no longer interesting (example: overlay in shortest mode, other input closed). */ - ff_avfilter_link_set_out_status(in, in->status_in, in->status_in_pts); + link_set_out_status(in, li_in->status_in, li_in->status_in_pts); return 0; } progress = 0; @@ -1148,20 +1208,34 @@ static int ff_filter_activate_default(AVFilterContext *filter) { unsigned i; + for (i = 0; i < filter->nb_outputs; i++) { + FilterLinkInternal *li = ff_link_internal(filter->outputs[i]); + int ret = li->status_in; + + if (ret) { + for (int j = 0; j < filter->nb_inputs; j++) + ff_inlink_set_status(filter->inputs[j], ret); + return 0; + } + } + for (i = 0; i < filter->nb_inputs; i++) { - if (samples_ready(filter->inputs[i], filter->inputs[i]->min_samples)) { + if (samples_ready(ff_link_internal(filter->inputs[i]), + filter->inputs[i]->min_samples)) { return ff_filter_frame_to_filter(filter->inputs[i]); } } for (i = 0; i < filter->nb_inputs; i++) { - if (filter->inputs[i]->status_in && !filter->inputs[i]->status_out) { - av_assert1(!ff_framequeue_queued_frames(&filter->inputs[i]->fifo)); - return forward_status_change(filter, filter->inputs[i]); + FilterLinkInternal * const li = ff_link_internal(filter->inputs[i]); + if (li->status_in && !li->status_out) { + av_assert1(!ff_framequeue_queued_frames(&li->fifo)); + return forward_status_change(filter, li); } } for (i = 0; i < filter->nb_outputs; i++) { + FilterLinkInternal * const li = ff_link_internal(filter->outputs[i]); if (filter->outputs[i]->frame_wanted_out && - !filter->outputs[i]->frame_blocked_in) { + !li->frame_blocked_in) { return ff_request_frame_to_filter(filter->outputs[i]); } } @@ -1235,7 +1309,7 @@ static int ff_filter_activate_default(AVFilterContext *filter) change is considered having already happened. It is set by the destination filter using - ff_avfilter_link_set_out_status(). + link_set_out_status(). Filters are activated according to the ready field, set using the ff_filter_set_ready(). Eventually, a priority queue will be used. @@ -1317,65 +1391,73 @@ int ff_filter_activate(AVFilterContext *filter) int ff_inlink_acknowledge_status(AVFilterLink *link, int *rstatus, int64_t *rpts) { + FilterLinkInternal * const li = ff_link_internal(link); *rpts = link->current_pts; - if (ff_framequeue_queued_frames(&link->fifo)) + if (ff_framequeue_queued_frames(&li->fifo)) return *rstatus = 0; - if (link->status_out) - return *rstatus = link->status_out; - if (!link->status_in) + if (li->status_out) + return *rstatus = li->status_out; + if (!li->status_in) return *rstatus = 0; - *rstatus = link->status_out = link->status_in; - ff_update_link_current_pts(link, link->status_in_pts); + *rstatus = li->status_out = li->status_in; + update_link_current_pts(li, li->status_in_pts); *rpts = link->current_pts; return 1; } size_t ff_inlink_queued_frames(AVFilterLink *link) { - return ff_framequeue_queued_frames(&link->fifo); + FilterLinkInternal * const li = ff_link_internal(link); + return ff_framequeue_queued_frames(&li->fifo); } int ff_inlink_check_available_frame(AVFilterLink *link) { - return ff_framequeue_queued_frames(&link->fifo) > 0; + FilterLinkInternal * const li = ff_link_internal(link); + return ff_framequeue_queued_frames(&li->fifo) > 0; } int ff_inlink_queued_samples(AVFilterLink *link) { - return ff_framequeue_queued_samples(&link->fifo); + FilterLinkInternal * const li = ff_link_internal(link); + return ff_framequeue_queued_samples(&li->fifo); } int ff_inlink_check_available_samples(AVFilterLink *link, unsigned min) { - uint64_t samples = ff_framequeue_queued_samples(&link->fifo); + FilterLinkInternal * const li = ff_link_internal(link); + uint64_t samples = ff_framequeue_queued_samples(&li->fifo); av_assert1(min); - return samples >= min || (link->status_in && samples); + return samples >= min || (li->status_in && samples); } -static void consume_update(AVFilterLink *link, const AVFrame *frame) +static void consume_update(FilterLinkInternal *li, const AVFrame *frame) { - ff_update_link_current_pts(link, frame->pts); + AVFilterLink *const link = &li->l; + update_link_current_pts(li, frame->pts); ff_inlink_process_commands(link, frame); - link->dst->is_disabled = !ff_inlink_evaluate_timeline_at_frame(link, frame); + if (link == link->dst->inputs[0]) + link->dst->is_disabled = !ff_inlink_evaluate_timeline_at_frame(link, frame); link->frame_count_out++; link->sample_count_out += frame->nb_samples; } int ff_inlink_consume_frame(AVFilterLink *link, AVFrame **rframe) { + FilterLinkInternal * const li = ff_link_internal(link); AVFrame *frame; *rframe = NULL; if (!ff_inlink_check_available_frame(link)) return 0; - if (link->fifo.samples_skipped) { - frame = ff_framequeue_peek(&link->fifo, 0); + if (li->fifo.samples_skipped) { + frame = ff_framequeue_peek(&li->fifo, 0); return ff_inlink_consume_samples(link, frame->nb_samples, frame->nb_samples, rframe); } - frame = ff_framequeue_take(&link->fifo); - consume_update(link, frame); + frame = ff_framequeue_take(&li->fifo); + consume_update(li, frame); *rframe = frame; return 1; } @@ -1383,6 +1465,7 @@ int ff_inlink_consume_frame(AVFilterLink *link, AVFrame **rframe) int ff_inlink_consume_samples(AVFilterLink *link, unsigned min, unsigned max, AVFrame **rframe) { + FilterLinkInternal * const li = ff_link_internal(link); AVFrame *frame; int ret; @@ -1390,19 +1473,20 @@ int ff_inlink_consume_samples(AVFilterLink *link, unsigned min, unsigned max, *rframe = NULL; if (!ff_inlink_check_available_samples(link, min)) return 0; - if (link->status_in) - min = FFMIN(min, ff_framequeue_queued_samples(&link->fifo)); - ret = take_samples(link, min, max, &frame); + if (li->status_in) + min = FFMIN(min, ff_framequeue_queued_samples(&li->fifo)); + ret = take_samples(li, min, max, &frame); if (ret < 0) return ret; - consume_update(link, frame); + consume_update(li, frame); *rframe = frame; return 1; } AVFrame *ff_inlink_peek_frame(AVFilterLink *link, size_t idx) { - return ff_framequeue_peek(&link->fifo, idx); + FilterLinkInternal * const li = ff_link_internal(link); + return ff_framequeue_peek(&li->fifo, idx); } int ff_inlink_make_frame_writable(AVFilterLink *link, AVFrame **rframe) @@ -1464,7 +1548,11 @@ int ff_inlink_evaluate_timeline_at_frame(AVFilterLink *link, const AVFrame *fram { AVFilterContext *dstctx = link->dst; int64_t pts = frame->pts; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS int64_t pos = frame->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (!dstctx->enable_str) return 1; @@ -1473,44 +1561,50 @@ int ff_inlink_evaluate_timeline_at_frame(AVFilterLink *link, const AVFrame *fram dstctx->var_values[VAR_T] = pts == AV_NOPTS_VALUE ? NAN : pts * av_q2d(link->time_base); dstctx->var_values[VAR_W] = link->w; dstctx->var_values[VAR_H] = link->h; +#if FF_API_FRAME_PKT dstctx->var_values[VAR_POS] = pos == -1 ? NAN : pos; +#endif return fabs(av_expr_eval(dstctx->enable, dstctx->var_values, NULL)) >= 0.5; } void ff_inlink_request_frame(AVFilterLink *link) { - av_assert1(!link->status_in); - av_assert1(!link->status_out); + av_unused FilterLinkInternal *li = ff_link_internal(link); + av_assert1(!li->status_in); + av_assert1(!li->status_out); link->frame_wanted_out = 1; ff_filter_set_ready(link->src, 100); } void ff_inlink_set_status(AVFilterLink *link, int status) { - if (link->status_out) + FilterLinkInternal * const li = ff_link_internal(link); + if (li->status_out) return; link->frame_wanted_out = 0; - link->frame_blocked_in = 0; - ff_avfilter_link_set_out_status(link, status, AV_NOPTS_VALUE); - while (ff_framequeue_queued_frames(&link->fifo)) { - AVFrame *frame = ff_framequeue_take(&link->fifo); + li->frame_blocked_in = 0; + link_set_out_status(link, status, AV_NOPTS_VALUE); + while (ff_framequeue_queued_frames(&li->fifo)) { + AVFrame *frame = ff_framequeue_take(&li->fifo); av_frame_free(&frame); } - if (!link->status_in) - link->status_in = status; + if (!li->status_in) + li->status_in = status; } int ff_outlink_get_status(AVFilterLink *link) { - return link->status_in; + FilterLinkInternal * const li = ff_link_internal(link); + return li->status_in; } int ff_inoutlink_check_flow(AVFilterLink *inlink, AVFilterLink *outlink) { + FilterLinkInternal * const li_in = ff_link_internal(inlink); return ff_outlink_frame_wanted(outlink) || ff_inlink_check_available_frame(inlink) || - inlink->status_out; + li_in->status_out; } diff --git a/libavfilter/avfilter.h b/libavfilter/avfilter.h index d0d45844681..a34e61f23cc 100644 --- a/libavfilter/avfilter.h +++ b/libavfilter/avfilter.h @@ -131,6 +131,11 @@ enum AVMediaType avfilter_pad_get_type(const AVFilterPad *pads, int pad_idx); * received by the filter on one of its inputs. */ #define AVFILTER_FLAG_METADATA_ONLY (1 << 3) + +/** + * The filter can create hardware frames using AVFilterContext.hw_device_ctx. + */ +#define AVFILTER_FLAG_HWDEVICE (1 << 4) /** * Some filters support a generic "enable" expression option that can be used * to enable or disable a filter in the timeline. Filters supporting this @@ -296,6 +301,14 @@ typedef struct AVFilter { * @ref AVFilterFormatsConfig.formats "incfg.formats" * on every output link to a list of pixel/sample formats that the filter * supports on that link. + * For video links, this filter may also set + * @ref AVFilterFormatsConfig.color_spaces "incfg.color_spaces" + * / + * @ref AVFilterFormatsConfig.color_spaces "outcfg.color_spaces" + * and @ref AVFilterFormatsConfig.color_ranges "incfg.color_ranges" + * / + * @ref AVFilterFormatsConfig.color_ranges "outcfg.color_ranges" + * analogously. * For audio links, this filter must also set * @ref AVFilterFormatsConfig.samplerates "incfg.samplerates" * / @@ -317,6 +330,10 @@ typedef struct AVFilter { * to indicate that this filter supports each of these pixel formats, * provided that all inputs and outputs use the same pixel format. * + * In addition to that the generic code will mark all inputs + * and all outputs as supporting all color spaces and ranges, as + * long as all inputs and outputs use the same color space/range. + * * This list must never be NULL if the union is in this state. * The type of all inputs and outputs of filters using this must * be AVMEDIA_TYPE_VIDEO. @@ -386,8 +403,6 @@ unsigned avfilter_filter_pad_count(const AVFilter *filter, int is_output); */ #define AVFILTER_THREAD_SLICE (1 << 0) -typedef struct AVFilterInternal AVFilterInternal; - /** An instance of a filter */ struct AVFilterContext { const AVClass *av_class; ///< needed for av_log() and filters common options @@ -427,9 +442,11 @@ struct AVFilterContext { int thread_type; /** - * An opaque struct for libavfilter internal use. + * Max number of threads allowed in this filter instance. + * If <= 0, its value is ignored. + * Overrides global number of threads set per filter graph. */ - AVFilterInternal *internal; + int nb_threads; struct AVFilterCommand *command_queue; @@ -444,16 +461,13 @@ struct AVFilterContext { * in particular, a filter which consumes or processes hardware frames will * instead use the hw_frames_ctx field in AVFilterLink to carry the * hardware context information. + * + * May be set by the caller on filters flagged with AVFILTER_FLAG_HWDEVICE + * before initializing the filter with avfilter_init_str() or + * avfilter_init_dict(). */ AVBufferRef *hw_device_ctx; - /** - * Max number of threads allowed in this filter instance. - * If <= 0, its value is ignored. - * Overrides global number of threads set per filter graph. - */ - int nb_threads; - /** * Ready status of the filter. * A non-0 value means that the filter needs activating; @@ -505,6 +519,12 @@ typedef struct AVFilterFormatsConfig { */ AVFilterChannelLayouts *channel_layouts; + /** + * Lists of supported YUV color metadata, only for YUV video. + */ + AVFilterFormats *color_spaces; ///< AVColorSpace + AVFilterFormats *color_ranges; ///< AVColorRange + } AVFilterFormatsConfig; /** @@ -528,22 +548,25 @@ struct AVFilterLink { enum AVMediaType type; ///< filter media type + int format; ///< agreed upon media format + /* These parameters apply only to video */ int w; ///< agreed upon image width int h; ///< agreed upon image height AVRational sample_aspect_ratio; ///< agreed upon sample aspect ratio - /* These parameters apply only to audio */ -#if FF_API_OLD_CHANNEL_LAYOUT /** - * channel layout of current buffer (see libavutil/channel_layout.h) - * @deprecated use ch_layout + * For non-YUV links, these are respectively set to fallback values (as + * appropriate for that colorspace). + * + * Note: This includes grayscale formats, as these are currently treated + * as forced full range always. */ - attribute_deprecated - uint64_t channel_layout; -#endif - int sample_rate; ///< samples per second + enum AVColorSpace colorspace; ///< agreed upon YUV color space + enum AVColorRange color_range; ///< agreed upon YUV color range - int format; ///< agreed upon media format + /* These parameters apply only to audio */ + int sample_rate; ///< samples per second + AVChannelLayout ch_layout; ///< channel layout of current buffer (see libavutil/channel_layout.h) /** * Define the time base used by the PTS of the frames/samples @@ -554,8 +577,6 @@ struct AVFilterLink { */ AVRational time_base; - AVChannelLayout ch_layout; ///< channel layout of current buffer (see libavutil/channel_layout.h) - /***************************************************************** * All fields below this line are not part of the public API. They * may not be used outside of libavfilter and can be changed and @@ -574,13 +595,6 @@ struct AVFilterLink { */ AVFilterFormatsConfig outcfg; - /** stage of the initialization of the link properties (dimensions, etc) */ - enum { - AVLINK_UNINIT = 0, ///< not started - AVLINK_STARTINIT, ///< started, but incomplete - AVLINK_INIT ///< complete - } init_state; - /** * Graph the filter belongs to. */ @@ -598,11 +612,6 @@ struct AVFilterLink { */ int64_t current_pts_us; - /** - * Index in the age array. - */ - int age_index; - /** * Frame rate of the stream on the link, or 1/0 if unknown or variable; * if left to 0/0, will be automatically copied from the first input @@ -641,11 +650,6 @@ struct AVFilterLink { */ int64_t sample_count_in, sample_count_out; - /** - * A pointer to a FFFramePool struct. - */ - void *frame_pool; - /** * True if a frame is currently wanted on the output of this filter. * Set when ff_request_frame() is called by the output, @@ -658,51 +662,6 @@ struct AVFilterLink { * AVHWFramesContext describing the frames. */ AVBufferRef *hw_frames_ctx; - -#ifndef FF_INTERNAL_FIELDS - - /** - * Internal structure members. - * The fields below this limit are internal for libavfilter's use - * and must in no way be accessed by applications. - */ - char reserved[0xF000]; - -#else /* FF_INTERNAL_FIELDS */ - - /** - * Queue of frames waiting to be filtered. - */ - FFFrameQueue fifo; - - /** - * If set, the source filter can not generate a frame as is. - * The goal is to avoid repeatedly calling the request_frame() method on - * the same link. - */ - int frame_blocked_in; - - /** - * Link input status. - * If not zero, all attempts of filter_frame will fail with the - * corresponding code. - */ - int status_in; - - /** - * Timestamp of the input status change. - */ - int64_t status_in_pts; - - /** - * Link output status. - * If not zero, all attempts of request_frame will fail with the - * corresponding code. - */ - int status_out; - -#endif /* FF_INTERNAL_FIELDS */ - }; /** @@ -717,18 +676,19 @@ struct AVFilterLink { int avfilter_link(AVFilterContext *src, unsigned srcpad, AVFilterContext *dst, unsigned dstpad); +#if FF_API_LINK_PUBLIC /** - * Free the link in *link, and set its pointer to NULL. + * @deprecated this function should never be called by users */ +attribute_deprecated void avfilter_link_free(AVFilterLink **link); /** - * Negotiate the media format, dimensions, etc of all inputs to a filter. - * - * @param filter the filter to negotiate the properties for its inputs - * @return zero on successful negotiation + * @deprecated this function should never be called by users */ +attribute_deprecated int avfilter_config_links(AVFilterContext *filter); +#endif #define AVFILTER_CMD_FLAG_ONE 1 ///< Stop once a filter understood the command (for target=all for example), fast filters are favored automatically #define AVFILTER_CMD_FLAG_FAST 2 ///< Only execute command when its fast (like a video out that supports contrast adjustment in hw) @@ -821,8 +781,6 @@ int avfilter_insert_filter(AVFilterLink *link, AVFilterContext *filt, */ const AVClass *avfilter_get_class(void); -typedef struct AVFilterGraphInternal AVFilterGraphInternal; - /** * A function pointer passed to the @ref AVFilterGraph.execute callback to be * executed multiple times, possibly in parallel. @@ -880,11 +838,6 @@ typedef struct AVFilterGraph { */ int nb_threads; - /** - * Opaque object for libavfilter internal use. - */ - AVFilterGraphInternal *internal; - /** * Opaque user data. May be set by the caller to an arbitrary value, e.g. to * be used from callbacks like @ref AVFilterGraph.execute. @@ -907,18 +860,6 @@ typedef struct AVFilterGraph { avfilter_execute_func *execute; char *aresample_swr_opts; ///< swr options to use for the auto-inserted aresample filters, Access ONLY through AVOptions - - /** - * Private fields - * - * The following fields are for internal use only. - * Their type, offset, number and semantic can change without notice. - */ - - AVFilterLink **sink_links; - int sink_links_count; - - unsigned disable_auto_convert; } AVFilterGraph; /** diff --git a/libavfilter/avfilter_internal.h b/libavfilter/avfilter_internal.h new file mode 100644 index 00000000000..2c31c3e7dec --- /dev/null +++ b/libavfilter/avfilter_internal.h @@ -0,0 +1,156 @@ +/* + * 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 + */ + +/* + * APIs internal to the generic filter(graph) layer. + * + * MUST NOT be included by individual filters. + */ + +#ifndef AVFILTER_AVFILTER_INTERNAL_H +#define AVFILTER_AVFILTER_INTERNAL_H + +#include + +#include "avfilter.h" +#include "framequeue.h" + +typedef struct FilterLinkInternal { + AVFilterLink l; + + struct FFFramePool *frame_pool; + + /** + * Queue of frames waiting to be filtered. + */ + FFFrameQueue fifo; + + /** + * If set, the source filter can not generate a frame as is. + * The goal is to avoid repeatedly calling the request_frame() method on + * the same link. + */ + int frame_blocked_in; + + /** + * Link input status. + * If not zero, all attempts of filter_frame will fail with the + * corresponding code. + */ + int status_in; + + /** + * Timestamp of the input status change. + */ + int64_t status_in_pts; + + /** + * Link output status. + * If not zero, all attempts of request_frame will fail with the + * corresponding code. + */ + int status_out; + + /** + * Index in the age array. + */ + int age_index; + + /** stage of the initialization of the link properties (dimensions, etc) */ + enum { + AVLINK_UNINIT = 0, ///< not started + AVLINK_STARTINIT, ///< started, but incomplete + AVLINK_INIT ///< complete + } init_state; +} FilterLinkInternal; + +static inline FilterLinkInternal *ff_link_internal(AVFilterLink *link) +{ + return (FilterLinkInternal*)link; +} + +typedef struct AVFilterCommand { + double time; ///< time expressed in seconds + char *command; ///< command + char *arg; ///< optional argument for the command + int flags; + struct AVFilterCommand *next; +} AVFilterCommand; + +typedef struct FFFilterGraph { + /** + * The public AVFilterGraph. See avfilter.h for it. + */ + AVFilterGraph p; + + struct FilterLinkInternal **sink_links; + int sink_links_count; + + unsigned disable_auto_convert; + + void *thread; + avfilter_execute_func *thread_execute; + FFFrameQueueGlobal frame_queues; +} FFFilterGraph; + +static inline FFFilterGraph *fffiltergraph(AVFilterGraph *graph) +{ + return (FFFilterGraph*)graph; +} + +/** + * Update the position of a link in the age heap. + */ +void ff_avfilter_graph_update_heap(AVFilterGraph *graph, + struct FilterLinkInternal *li); + +/** + * Allocate a new filter context and return it. + * + * @param filter what filter to create an instance of + * @param inst_name name to give to the new filter context + * + * @return newly created filter context or NULL on failure + */ +AVFilterContext *ff_filter_alloc(const AVFilter *filter, const char *inst_name); + +/** + * Remove a filter from a graph; + */ +void ff_filter_graph_remove_filter(AVFilterGraph *graph, AVFilterContext *filter); + +int ff_filter_activate(AVFilterContext *filter); + +/** + * Parse filter options into a dictionary. + * + * @param logctx context for logging + * @param priv_class a filter's private class for shorthand options or NULL + * @param options dictionary to store parsed options in + * @param args options string to parse + * + * @return a non-negative number on success, a negative error code on failure + */ +int ff_filter_opt_parse(void *logctx, const AVClass *priv_class, + AVDictionary **options, const char *args); + +int ff_graph_thread_init(FFFilterGraph *graph); + +void ff_graph_thread_free(FFFilterGraph *graph); + +#endif /* AVFILTER_AVFILTER_INTERNAL_H */ diff --git a/libavfilter/avfiltergraph.c b/libavfilter/avfiltergraph.c index 53f468494d3..8e091d95e09 100644 --- a/libavfilter/avfiltergraph.c +++ b/libavfilter/avfiltergraph.c @@ -27,18 +27,18 @@ #include "libavutil/avassert.h" #include "libavutil/bprint.h" #include "libavutil/channel_layout.h" +#include "libavutil/hwcontext.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" -#define FF_INTERNAL_FIELDS 1 -#include "framequeue.h" #include "avfilter.h" +#include "avfilter_internal.h" #include "buffersink.h" #include "formats.h" +#include "framequeue.h" #include "internal.h" -#include "thread.h" #define OFFSET(x) offsetof(AVFilterGraph, x) #define F AV_OPT_FLAG_FILTERING_PARAM @@ -46,10 +46,10 @@ #define A AV_OPT_FLAG_AUDIO_PARAM static const AVOption filtergraph_options[] = { { "thread_type", "Allowed thread types", OFFSET(thread_type), AV_OPT_TYPE_FLAGS, - { .i64 = AVFILTER_THREAD_SLICE }, 0, INT_MAX, F|V|A, "thread_type" }, + { .i64 = AVFILTER_THREAD_SLICE }, 0, INT_MAX, F|V|A, .unit = "thread_type" }, { "slice", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AVFILTER_THREAD_SLICE }, .flags = F|V|A, .unit = "thread_type" }, { "threads", "Maximum number of threads", OFFSET(nb_threads), AV_OPT_TYPE_INT, - { .i64 = 0 }, 0, INT_MAX, F|V|A, "threads"}, + { .i64 = 0 }, 0, INT_MAX, F|V|A, .unit = "threads"}, {"auto", "autodetect a suitable number of threads to use", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, .flags = F|V|A, .unit = "threads"}, {"scale_sws_opts" , "default scale filter options" , OFFSET(scale_sws_opts) , AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, F|V }, @@ -67,33 +67,30 @@ static const AVClass filtergraph_class = { }; #if !HAVE_THREADS -void ff_graph_thread_free(AVFilterGraph *graph) +void ff_graph_thread_free(FFFilterGraph *graph) { } -int ff_graph_thread_init(AVFilterGraph *graph) +int ff_graph_thread_init(FFFilterGraph *graph) { - graph->thread_type = 0; - graph->nb_threads = 1; + graph->p.thread_type = 0; + graph->p.nb_threads = 1; return 0; } #endif AVFilterGraph *avfilter_graph_alloc(void) { - AVFilterGraph *ret = av_mallocz(sizeof(*ret)); - if (!ret) - return NULL; + FFFilterGraph *graph = av_mallocz(sizeof(*graph)); + AVFilterGraph *ret; - ret->internal = av_mallocz(sizeof(*ret->internal)); - if (!ret->internal) { - av_freep(&ret); + if (!graph) return NULL; - } + ret = &graph->p; ret->av_class = &filtergraph_class; av_opt_set_defaults(ret); - ff_framequeue_global_init(&ret->internal->frame_queues); + ff_framequeue_global_init(&graph->frame_queues); return ret; } @@ -116,23 +113,25 @@ void ff_filter_graph_remove_filter(AVFilterGraph *graph, AVFilterContext *filter } } -void avfilter_graph_free(AVFilterGraph **graph) +void avfilter_graph_free(AVFilterGraph **graphp) { - if (!*graph) + AVFilterGraph *graph = *graphp; + FFFilterGraph *graphi = fffiltergraph(graph); + + if (!graph) return; - while ((*graph)->nb_filters) - avfilter_free((*graph)->filters[0]); + while (graph->nb_filters) + avfilter_free(graph->filters[0]); - ff_graph_thread_free(*graph); + ff_graph_thread_free(graphi); - av_freep(&(*graph)->sink_links); + av_freep(&graphi->sink_links); - av_opt_free(*graph); + av_opt_free(graph); - av_freep(&(*graph)->filters); - av_freep(&(*graph)->internal); - av_freep(graph); + av_freep(&graph->filters); + av_freep(graphp); } int avfilter_graph_create_filter(AVFilterContext **filt_ctx, const AVFilter *filt, @@ -159,7 +158,7 @@ int avfilter_graph_create_filter(AVFilterContext **filt_ctx, const AVFilter *fil void avfilter_graph_set_auto_convert(AVFilterGraph *graph, unsigned flags) { - graph->disable_auto_convert = flags; + fffiltergraph(graph)->disable_auto_convert = flags; } AVFilterContext *avfilter_graph_alloc_filter(AVFilterGraph *graph, @@ -167,12 +166,13 @@ AVFilterContext *avfilter_graph_alloc_filter(AVFilterGraph *graph, const char *name) { AVFilterContext **filters, *s; + FFFilterGraph *graphi = fffiltergraph(graph); - if (graph->thread_type && !graph->internal->thread_execute) { + if (graph->thread_type && !graphi->thread_execute) { if (graph->execute) { - graph->internal->thread_execute = graph->execute; + graphi->thread_execute = graph->execute; } else { - int ret = ff_graph_thread_init(graph); + int ret = ff_graph_thread_init(graphi); if (ret < 0) { av_log(graph, AV_LOG_ERROR, "Error initializing threading: %s.\n", av_err2str(ret)); return NULL; @@ -251,7 +251,7 @@ static int graph_config_links(AVFilterGraph *graph, void *log_ctx) filt = graph->filters[i]; if (!filt->nb_outputs) { - if ((ret = avfilter_config_links(filt))) + if ((ret = ff_filter_config_links(filt))) return ret; } } @@ -298,7 +298,9 @@ static int filter_link_check_formats(void *log, AVFilterLink *link, AVFilterForm switch (link->type) { case AVMEDIA_TYPE_VIDEO: - if ((ret = ff_formats_check_pixel_formats(log, cfg->formats)) < 0) + if ((ret = ff_formats_check_pixel_formats(log, cfg->formats)) < 0 || + (ret = ff_formats_check_color_spaces(log, cfg->color_spaces)) < 0 || + (ret = ff_formats_check_color_ranges(log, cfg->color_ranges)) < 0) return ret; break; @@ -341,33 +343,21 @@ static int filter_check_formats(AVFilterContext *ctx) static int filter_query_formats(AVFilterContext *ctx) { int ret; - AVFilterFormats *formats; - AVFilterChannelLayouts *chlayouts; - enum AVMediaType type = ctx->inputs && ctx->inputs [0] ? ctx->inputs [0]->type : - ctx->outputs && ctx->outputs[0] ? ctx->outputs[0]->type : - AVMEDIA_TYPE_VIDEO; - - if ((ret = ctx->filter->formats.query_func(ctx)) < 0) { - if (ret != AVERROR(EAGAIN)) - av_log(ctx, AV_LOG_ERROR, "Query format failed for '%s': %s\n", - ctx->name, av_err2str(ret)); - return ret; - } - ret = filter_check_formats(ctx); - if (ret < 0) - return ret; - formats = ff_all_formats(type); - if ((ret = ff_set_common_formats(ctx, formats)) < 0) - return ret; - if (type == AVMEDIA_TYPE_AUDIO) { - if ((ret = ff_set_common_all_samplerates(ctx)) < 0) + if (ctx->filter->formats_state == FF_FILTER_FORMATS_QUERY_FUNC) { + if ((ret = ctx->filter->formats.query_func(ctx)) < 0) { + if (ret != AVERROR(EAGAIN)) + av_log(ctx, AV_LOG_ERROR, "Query format failed for '%s': %s\n", + ctx->name, av_err2str(ret)); return ret; - chlayouts = ff_all_channel_layouts(); - if ((ret = ff_set_common_channel_layouts(ctx, chlayouts)) < 0) + } + + ret = filter_check_formats(ctx); + if (ret < 0) return ret; } - return 0; + + return ff_default_query_formats(ctx); } static int formats_declared(AVFilterContext *f) @@ -377,6 +367,10 @@ static int formats_declared(AVFilterContext *f) for (i = 0; i < f->nb_inputs; i++) { if (!f->inputs[i]->outcfg.formats) return 0; + if (f->inputs[i]->type == AVMEDIA_TYPE_VIDEO && + !(f->inputs[i]->outcfg.color_ranges && + f->inputs[i]->outcfg.color_spaces)) + return 0; if (f->inputs[i]->type == AVMEDIA_TYPE_AUDIO && !(f->inputs[i]->outcfg.samplerates && f->inputs[i]->outcfg.channel_layouts)) @@ -385,6 +379,10 @@ static int formats_declared(AVFilterContext *f) for (i = 0; i < f->nb_outputs; i++) { if (!f->outputs[i]->incfg.formats) return 0; + if (f->outputs[i]->type == AVMEDIA_TYPE_VIDEO && + !(f->outputs[i]->incfg.color_ranges && + f->outputs[i]->incfg.color_spaces)) + return 0; if (f->outputs[i]->type == AVMEDIA_TYPE_AUDIO && !(f->outputs[i]->incfg.samplerates && f->outputs[i]->incfg.channel_layouts)) @@ -416,10 +414,7 @@ static int query_formats(AVFilterGraph *graph, void *log_ctx) AVFilterContext *f = graph->filters[i]; if (formats_declared(f)) continue; - if (f->filter->formats_state == FF_FILTER_FORMATS_QUERY_FUNC) - ret = filter_query_formats(f); - else - ret = ff_default_query_formats(f); + ret = filter_query_formats(f); if (ret < 0 && ret != AVERROR(EAGAIN)) return ret; /* note: EAGAIN could indicate a partial success, not counted yet */ @@ -441,7 +436,7 @@ static int query_formats(AVFilterGraph *graph, void *log_ctx) neg = ff_filter_get_negotiation(link); av_assert0(neg); - for (neg_step = 1; neg_step < neg->nb_mergers; neg_step++) { + for (neg_step = 0; neg_step < neg->nb_mergers; neg_step++) { const AVFilterFormatsMerger *m = &neg->mergers[neg_step]; void *a = FF_FIELD_AT(void *, m->offset, link->incfg); void *b = FF_FIELD_AT(void *, m->offset, link->outcfg); @@ -475,7 +470,7 @@ static int query_formats(AVFilterGraph *graph, void *log_ctx) char inst_name[30]; const char *opts; - if (graph->disable_auto_convert) { + if (fffiltergraph(graph)->disable_auto_convert) { av_log(log_ctx, AV_LOG_ERROR, "The filters '%s' and '%s' do not have a common format " "and automatic conversion is disabled.\n", @@ -508,7 +503,16 @@ static int query_formats(AVFilterGraph *graph, void *log_ctx) av_assert0( inlink->outcfg.formats->refcount > 0); av_assert0(outlink->incfg.formats->refcount > 0); av_assert0(outlink->outcfg.formats->refcount > 0); - if (outlink->type == AVMEDIA_TYPE_AUDIO) { + if (outlink->type == AVMEDIA_TYPE_VIDEO) { + av_assert0( inlink-> incfg.color_spaces->refcount > 0); + av_assert0( inlink->outcfg.color_spaces->refcount > 0); + av_assert0(outlink-> incfg.color_spaces->refcount > 0); + av_assert0(outlink->outcfg.color_spaces->refcount > 0); + av_assert0( inlink-> incfg.color_ranges->refcount > 0); + av_assert0( inlink->outcfg.color_ranges->refcount > 0); + av_assert0(outlink-> incfg.color_ranges->refcount > 0); + av_assert0(outlink->outcfg.color_ranges->refcount > 0); + } else if (outlink->type == AVMEDIA_TYPE_AUDIO) { av_assert0( inlink-> incfg.samplerates->refcount > 0); av_assert0( inlink->outcfg.samplerates->refcount > 0); av_assert0(outlink-> incfg.samplerates->refcount > 0); @@ -598,6 +602,30 @@ static enum AVSampleFormat find_best_sample_fmt_of_2(enum AVSampleFormat dst_fmt return score1 < score2 ? dst_fmt1 : dst_fmt2; } +int ff_fmt_is_regular_yuv(enum AVPixelFormat fmt) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + if (!desc) + return 0; + if (desc->nb_components < 3) + return 0; /* Grayscale is explicitly full-range in swscale */ + av_assert1(!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)); + if (desc->flags & (AV_PIX_FMT_FLAG_RGB | AV_PIX_FMT_FLAG_PAL | + AV_PIX_FMT_FLAG_XYZ | AV_PIX_FMT_FLAG_FLOAT)) + return 0; + + switch (fmt) { + case AV_PIX_FMT_YUVJ420P: + case AV_PIX_FMT_YUVJ422P: + case AV_PIX_FMT_YUVJ444P: + case AV_PIX_FMT_YUVJ440P: + case AV_PIX_FMT_YUVJ411P: + return 0; + default: + return 1; + } +} + static int pick_format(AVFilterLink *link, AVFilterLink *ref) { if (!link || !link->incfg.formats) @@ -636,7 +664,50 @@ static int pick_format(AVFilterLink *link, AVFilterLink *ref) link->incfg.formats->nb_formats = 1; link->format = link->incfg.formats->formats[0]; - if (link->type == AVMEDIA_TYPE_AUDIO) { + if (link->type == AVMEDIA_TYPE_VIDEO) { + enum AVPixelFormat swfmt = link->format; + if (av_pix_fmt_desc_get(swfmt)->flags & AV_PIX_FMT_FLAG_HWACCEL) { + // FIXME: this is a hack - we'd like to use the sw_format of + // link->hw_frames_ctx here, but it is not yet available. + // To make this work properly we will need to either reorder + // things so that it is available here or somehow negotiate + // sw_format separately. + swfmt = AV_PIX_FMT_YUV420P; + } + + if (!ff_fmt_is_regular_yuv(swfmt)) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(swfmt); + /* These fields are explicitly documented as affecting YUV only, + * so set them to sane values for other formats. */ + if (desc->flags & AV_PIX_FMT_FLAG_FLOAT) + link->color_range = AVCOL_RANGE_UNSPECIFIED; + else + link->color_range = AVCOL_RANGE_JPEG; + if (desc->flags & (AV_PIX_FMT_FLAG_RGB | AV_PIX_FMT_FLAG_XYZ)) { + link->colorspace = AVCOL_SPC_RGB; + } else { + link->colorspace = AVCOL_SPC_UNSPECIFIED; + } + } else { + if (!link->incfg.color_spaces->nb_formats) { + av_log(link->src, AV_LOG_ERROR, "Cannot select color space for" + " the link between filters %s and %s.\n", link->src->name, + link->dst->name); + return AVERROR(EINVAL); + } + link->incfg.color_spaces->nb_formats = 1; + link->colorspace = link->incfg.color_spaces->formats[0]; + + if (!link->incfg.color_ranges->nb_formats) { + av_log(link->src, AV_LOG_ERROR, "Cannot select color range for" + " the link between filters %s and %s.\n", link->src->name, + link->dst->name); + return AVERROR(EINVAL); + } + link->incfg.color_ranges->nb_formats = 1; + link->color_range = link->incfg.color_ranges->formats[0]; + } + } else if (link->type == AVMEDIA_TYPE_AUDIO) { int ret; if (!link->incfg.samplerates->nb_formats) { @@ -662,12 +733,6 @@ static int pick_format(AVFilterLink *link, AVFilterLink *ref) ret = av_channel_layout_copy(&link->ch_layout, &link->incfg.channel_layouts->channel_layouts[0]); if (ret < 0) return ret; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - link->channel_layout = link->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ? - link->ch_layout.u.mask : 0; -FF_ENABLE_DEPRECATION_WARNINGS -#endif } ff_formats_unref(&link->incfg.formats); @@ -676,6 +741,10 @@ FF_ENABLE_DEPRECATION_WARNINGS ff_formats_unref(&link->outcfg.samplerates); ff_channel_layouts_unref(&link->incfg.channel_layouts); ff_channel_layouts_unref(&link->outcfg.channel_layouts); + ff_formats_unref(&link->incfg.color_spaces); + ff_formats_unref(&link->outcfg.color_spaces); + ff_formats_unref(&link->incfg.color_ranges); + ff_formats_unref(&link->outcfg.color_ranges); return 0; } @@ -725,6 +794,10 @@ static int reduce_formats_on_filter(AVFilterContext *filter) nb_formats, ff_add_format); REDUCE_FORMATS(int, AVFilterFormats, samplerates, formats, nb_formats, ff_add_format); + REDUCE_FORMATS(int, AVFilterFormats, color_spaces, formats, + nb_formats, ff_add_format); + REDUCE_FORMATS(int, AVFilterFormats, color_ranges, formats, + nb_formats, ff_add_format); /* reduce channel layouts */ for (i = 0; i < filter->nb_inputs; i++) { @@ -748,8 +821,10 @@ static int reduce_formats_on_filter(AVFilterContext *filter) (KNOWN(fmt) || fmts->all_counts)) { /* Turn the infinite list into a singleton */ fmts->all_layouts = fmts->all_counts = 0; - if (ff_add_channel_layout(&outlink->incfg.channel_layouts, fmt) < 0) - ret = 1; + ret = ff_add_channel_layout(&outlink->incfg.channel_layouts, fmt); + if (ret < 0) + return ret; + ret = 1; break; } @@ -1128,17 +1203,17 @@ static int graph_config_pointers(AVFilterGraph *graph, void *log_ctx) unsigned i, j; int sink_links_count = 0, n = 0; AVFilterContext *f; - AVFilterLink **sinks; + FilterLinkInternal **sinks; for (i = 0; i < graph->nb_filters; i++) { f = graph->filters[i]; for (j = 0; j < f->nb_inputs; j++) { f->inputs[j]->graph = graph; - f->inputs[j]->age_index = -1; + ff_link_internal(f->inputs[j])->age_index = -1; } for (j = 0; j < f->nb_outputs; j++) { f->outputs[j]->graph = graph; - f->outputs[j]->age_index= -1; + ff_link_internal(f->outputs[j])->age_index = -1; } if (!f->nb_outputs) { if (f->nb_inputs > INT_MAX - sink_links_count) @@ -1153,14 +1228,15 @@ static int graph_config_pointers(AVFilterGraph *graph, void *log_ctx) f = graph->filters[i]; if (!f->nb_outputs) { for (j = 0; j < f->nb_inputs; j++) { - sinks[n] = f->inputs[j]; - f->inputs[j]->age_index = n++; + sinks[n] = ff_link_internal(f->inputs[j]); + sinks[n]->age_index = n; + n++; } } } av_assert0(n == sink_links_count); - graph->sink_links = sinks; - graph->sink_links_count = sink_links_count; + fffiltergraph(graph)->sink_links = sinks; + fffiltergraph(graph)->sink_links_count = sink_links_count; return 0; } @@ -1243,29 +1319,29 @@ int avfilter_graph_queue_command(AVFilterGraph *graph, const char *target, const return 0; } -static void heap_bubble_up(AVFilterGraph *graph, - AVFilterLink *link, int index) +static void heap_bubble_up(FFFilterGraph *graph, + FilterLinkInternal *li, int index) { - AVFilterLink **links = graph->sink_links; + FilterLinkInternal **links = graph->sink_links; av_assert0(index >= 0); while (index) { int parent = (index - 1) >> 1; - if (links[parent]->current_pts_us >= link->current_pts_us) + if (links[parent]->l.current_pts_us >= li->l.current_pts_us) break; links[index] = links[parent]; links[index]->age_index = index; index = parent; } - links[index] = link; - link->age_index = index; + links[index] = li; + li->age_index = index; } -static void heap_bubble_down(AVFilterGraph *graph, - AVFilterLink *link, int index) +static void heap_bubble_down(FFFilterGraph *graph, + FilterLinkInternal *li, int index) { - AVFilterLink **links = graph->sink_links; + FilterLinkInternal **links = graph->sink_links; av_assert0(index >= 0); @@ -1274,34 +1350,38 @@ static void heap_bubble_down(AVFilterGraph *graph, if (child >= graph->sink_links_count) break; if (child + 1 < graph->sink_links_count && - links[child + 1]->current_pts_us < links[child]->current_pts_us) + links[child + 1]->l.current_pts_us < links[child]->l.current_pts_us) child++; - if (link->current_pts_us < links[child]->current_pts_us) + if (li->l.current_pts_us < links[child]->l.current_pts_us) break; links[index] = links[child]; links[index]->age_index = index; index = child; } - links[index] = link; - link->age_index = index; + links[index] = li; + li->age_index = index; } -void ff_avfilter_graph_update_heap(AVFilterGraph *graph, AVFilterLink *link) +void ff_avfilter_graph_update_heap(AVFilterGraph *graph, FilterLinkInternal *li) { - heap_bubble_up (graph, link, link->age_index); - heap_bubble_down(graph, link, link->age_index); + FFFilterGraph *graphi = fffiltergraph(graph); + + heap_bubble_up (graphi, li, li->age_index); + heap_bubble_down(graphi, li, li->age_index); } int avfilter_graph_request_oldest(AVFilterGraph *graph) { - AVFilterLink *oldest = graph->sink_links[0]; + FFFilterGraph *graphi = fffiltergraph(graph); + FilterLinkInternal *oldesti = graphi->sink_links[0]; + AVFilterLink *oldest = &oldesti->l; int64_t frame_count; int r; - while (graph->sink_links_count) { - oldest = graph->sink_links[0]; + while (graphi->sink_links_count) { + oldesti = graphi->sink_links[0]; + oldest = &oldesti->l; if (oldest->dst->filter->activate) { - /* For now, buffersink is the only filter implementing activate. */ r = av_buffersink_get_frame_flags(oldest->dst, NULL, AV_BUFFERSINK_FLAG_PEEK); if (r != AVERROR_EOF) @@ -1315,22 +1395,22 @@ int avfilter_graph_request_oldest(AVFilterGraph *graph) oldest->dst->name, oldest->dstpad->name); /* EOF: remove the link from the heap */ - if (oldest->age_index < --graph->sink_links_count) - heap_bubble_down(graph, graph->sink_links[graph->sink_links_count], - oldest->age_index); - oldest->age_index = -1; + if (oldesti->age_index < --graphi->sink_links_count) + heap_bubble_down(graphi, graphi->sink_links[graphi->sink_links_count], + oldesti->age_index); + oldesti->age_index = -1; } - if (!graph->sink_links_count) + if (!graphi->sink_links_count) return AVERROR_EOF; av_assert1(!oldest->dst->filter->activate); - av_assert1(oldest->age_index >= 0); + av_assert1(oldesti->age_index >= 0); frame_count = oldest->frame_count_out; while (frame_count == oldest->frame_count_out) { r = ff_filter_graph_run_once(graph); if (r == AVERROR(EAGAIN) && - !oldest->frame_wanted_out && !oldest->frame_blocked_in && - !oldest->status_in) - ff_request_frame(oldest); + !oldest->frame_wanted_out && !oldesti->frame_blocked_in && + !oldesti->status_in) + (void)ff_request_frame(oldest); else if (r < 0) return r; } diff --git a/libavfilter/blend_modes.c b/libavfilter/blend_modes.c index 0ff96a5b03f..65c5e6f8909 100644 --- a/libavfilter/blend_modes.c +++ b/libavfilter/blend_modes.c @@ -21,7 +21,6 @@ #include "libavutil/common.h" #include "libavutil/intfloat.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "blend.h" @@ -94,8 +93,8 @@ static void fn0(NAME)(const uint8_t *_top, ptrdiff_t top_linesize, \ ptrdiff_t width, ptrdiff_t height, \ FilterParams *param, double *values, int starty) \ { \ - const PIXEL *top = (PIXEL *)_top; \ - const PIXEL *bottom = (PIXEL *)_bottom; \ + const PIXEL *top = (const PIXEL *)_top; \ + const PIXEL *bottom = (const PIXEL *)_bottom; \ PIXEL *dst = (PIXEL *)_dst; \ const float opacity = param->opacity; \ \ diff --git a/libavfilter/buffersink.c b/libavfilter/buffersink.c index 9ab83696ce1..e05bd0a5739 100644 --- a/libavfilter/buffersink.c +++ b/libavfilter/buffersink.c @@ -30,14 +30,15 @@ #include "libavutil/internal.h" #include "libavutil/opt.h" -#define FF_INTERNAL_FIELDS 1 -#include "framequeue.h" - #include "audio.h" #include "avfilter.h" +#include "avfilter_internal.h" #include "buffersink.h" #include "filters.h" +#include "formats.h" +#include "framequeue.h" #include "internal.h" +#include "video.h" typedef struct BufferSinkContext { const AVClass *class; @@ -46,16 +47,14 @@ typedef struct BufferSinkContext { /* only used for video */ enum AVPixelFormat *pixel_fmts; ///< list of accepted pixel formats int pixel_fmts_size; + enum AVColorSpace *color_spaces; ///< list of accepted color spaces + int color_spaces_size; + enum AVColorRange *color_ranges; ///< list of accepted color ranges + int color_ranges_size; /* only used for audio */ enum AVSampleFormat *sample_fmts; ///< list of accepted sample formats int sample_fmts_size; -#if FF_API_OLD_CHANNEL_LAYOUT - int64_t *channel_layouts; ///< list of accepted channel layouts - int channel_layouts_size; - int *channel_counts; ///< list of accepted channel counts - int channel_counts_size; -#endif char *channel_layouts_str; ///< list of accepted channel layouts int all_channel_counts; int *sample_rates; ///< list of accepted sample rates @@ -66,31 +65,6 @@ typedef struct BufferSinkContext { #define NB_ITEMS(list) (list ## _size / sizeof(*list)) -#if FF_API_OLD_CHANNEL_LAYOUT -static void cleanup_redundant_layouts(AVFilterContext *ctx) -{ - BufferSinkContext *buf = ctx->priv; - int nb_layouts = NB_ITEMS(buf->channel_layouts); - int nb_counts = NB_ITEMS(buf->channel_counts); - uint64_t counts = 0; - int i, lc, n; - - for (i = 0; i < nb_counts; i++) - if (buf->channel_counts[i] < 64) - counts |= (uint64_t)1 << buf->channel_counts[i]; - for (i = lc = 0; i < nb_layouts; i++) { - n = av_popcount64(buf->channel_layouts[i]); - if (n < 64 && (counts & ((uint64_t)1 << n))) - av_log(ctx, AV_LOG_WARNING, - "Removing channel layout 0x%"PRIx64", redundant with %d channels\n", - buf->channel_layouts[i], n); - else - buf->channel_layouts[lc++] = buf->channel_layouts[i]; - } - buf->channel_layouts_size = lc * sizeof(*buf->channel_layouts); -} -#endif - int attribute_align_arg av_buffersink_get_frame(AVFilterContext *ctx, AVFrame *frame) { return av_buffersink_get_frame_flags(ctx, frame, 0); @@ -162,12 +136,20 @@ static av_cold int common_init(AVFilterContext *ctx) return 0; } +static void uninit(AVFilterContext *ctx) +{ + BufferSinkContext *buf = ctx->priv; + + av_frame_free(&buf->peeked_frame); +} + static int activate(AVFilterContext *ctx) { BufferSinkContext *buf = ctx->priv; + FilterLinkInternal * const li = ff_link_internal(ctx->inputs[0]); if (buf->warning_limit && - ff_framequeue_queued_frames(&ctx->inputs[0]->fifo) >= buf->warning_limit) { + ff_framequeue_queued_frames(&li->fifo) >= buf->warning_limit) { av_log(ctx, AV_LOG_WARNING, "%d buffers queued in %s, something may be wrong.\n", buf->warning_limit, @@ -200,12 +182,9 @@ MAKE_AVFILTERLINK_ACCESSOR(AVRational , frame_rate ) MAKE_AVFILTERLINK_ACCESSOR(int , w ) MAKE_AVFILTERLINK_ACCESSOR(int , h ) MAKE_AVFILTERLINK_ACCESSOR(AVRational , sample_aspect_ratio) +MAKE_AVFILTERLINK_ACCESSOR(enum AVColorSpace, colorspace) +MAKE_AVFILTERLINK_ACCESSOR(enum AVColorRange, color_range) -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS -MAKE_AVFILTERLINK_ACCESSOR(uint64_t , channel_layout ) -FF_ENABLE_DEPRECATION_WARNINGS -#endif MAKE_AVFILTERLINK_ACCESSOR(int , sample_rate ) MAKE_AVFILTERLINK_ACCESSOR(AVBufferRef * , hw_frames_ctx ) @@ -239,19 +218,36 @@ int av_buffersink_get_ch_layout(const AVFilterContext *ctx, AVChannelLayout *out static int vsink_query_formats(AVFilterContext *ctx) { BufferSinkContext *buf = ctx->priv; - AVFilterFormats *formats = NULL; unsigned i; int ret; CHECK_LIST_SIZE(pixel_fmts) + CHECK_LIST_SIZE(color_spaces) + CHECK_LIST_SIZE(color_ranges) if (buf->pixel_fmts_size) { + AVFilterFormats *formats = NULL; for (i = 0; i < NB_ITEMS(buf->pixel_fmts); i++) if ((ret = ff_add_format(&formats, buf->pixel_fmts[i])) < 0) return ret; if ((ret = ff_set_common_formats(ctx, formats)) < 0) return ret; - } else { - if ((ret = ff_default_query_formats(ctx)) < 0) + } + + if (buf->color_spaces_size) { + AVFilterFormats *formats = NULL; + for (i = 0; i < NB_ITEMS(buf->color_spaces); i++) + if ((ret = ff_add_format(&formats, buf->color_spaces[i])) < 0) + return ret; + if ((ret = ff_set_common_color_spaces(ctx, formats)) < 0) + return ret; + } + + if (buf->color_ranges_size) { + AVFilterFormats *formats = NULL; + for (i = 0; i < NB_ITEMS(buf->color_ranges); i++) + if ((ret = ff_add_format(&formats, buf->color_ranges[i])) < 0) + return ret; + if ((ret = ff_set_common_color_ranges(ctx, formats)) < 0) return ret; } @@ -269,10 +265,6 @@ static int asink_query_formats(AVFilterContext *ctx) CHECK_LIST_SIZE(sample_fmts) CHECK_LIST_SIZE(sample_rates) -#if FF_API_OLD_CHANNEL_LAYOUT - CHECK_LIST_SIZE(channel_layouts) - CHECK_LIST_SIZE(channel_counts) -#endif if (buf->sample_fmts_size) { for (i = 0; i < NB_ITEMS(buf->sample_fmts); i++) @@ -282,32 +274,10 @@ static int asink_query_formats(AVFilterContext *ctx) return ret; } - if ( -#if FF_API_OLD_CHANNEL_LAYOUT - buf->channel_layouts_size || buf->channel_counts_size || -#endif - buf->channel_layouts_str || buf->all_channel_counts) { -#if FF_API_OLD_CHANNEL_LAYOUT - cleanup_redundant_layouts(ctx); - for (i = 0; i < NB_ITEMS(buf->channel_layouts); i++) - if ((ret = av_channel_layout_from_mask(&layout, buf->channel_layouts[i])) < 0 || - (ret = ff_add_channel_layout(&layouts, &layout)) < 0) - return ret; - for (i = 0; i < NB_ITEMS(buf->channel_counts); i++) { - layout = FF_COUNT2LAYOUT(buf->channel_counts[i]); - if ((ret = ff_add_channel_layout(&layouts, &layout)) < 0) - return ret; - } -#endif + if (buf->channel_layouts_str || buf->all_channel_counts) { if (buf->channel_layouts_str) { const char *cur = buf->channel_layouts_str; -#if FF_API_OLD_CHANNEL_LAYOUT - if (layouts) - av_log(ctx, AV_LOG_WARNING, - "Conflicting ch_layouts and list of channel_counts/channel_layouts. Ignoring the former\n"); - else -#endif while (cur) { char *next = strchr(cur, '|'); if (next) @@ -354,6 +324,8 @@ static int asink_query_formats(AVFilterContext *ctx) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption buffersink_options[] = { { "pix_fmts", "set the supported pixel formats", OFFSET(pixel_fmts), AV_OPT_TYPE_BINARY, .flags = FLAGS }, + { "color_spaces", "set the supported color spaces", OFFSET(color_spaces), AV_OPT_TYPE_BINARY, .flags = FLAGS }, + { "color_ranges", "set the supported color ranges", OFFSET(color_ranges), AV_OPT_TYPE_BINARY, .flags = FLAGS }, { NULL }, }; #undef FLAGS @@ -361,12 +333,6 @@ static const AVOption buffersink_options[] = { static const AVOption abuffersink_options[] = { { "sample_fmts", "set the supported sample formats", OFFSET(sample_fmts), AV_OPT_TYPE_BINARY, .flags = FLAGS }, { "sample_rates", "set the supported sample rates", OFFSET(sample_rates), AV_OPT_TYPE_BINARY, .flags = FLAGS }, -#if FF_API_OLD_CHANNEL_LAYOUT - { "channel_layouts", "set the supported channel layouts (deprecated, use ch_layouts)", - OFFSET(channel_layouts), AV_OPT_TYPE_BINARY, .flags = FLAGS | AV_OPT_FLAG_DEPRECATED }, - { "channel_counts", "set the supported channel counts (deprecated, use ch_layouts)", - OFFSET(channel_counts), AV_OPT_TYPE_BINARY, .flags = FLAGS | AV_OPT_FLAG_DEPRECATED }, -#endif { "ch_layouts", "set a '|'-separated list of supported channel layouts", OFFSET(channel_layouts_str), AV_OPT_TYPE_STRING, .flags = FLAGS }, { "all_channel_counts", "accept all channel counts", OFFSET(all_channel_counts), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, @@ -377,40 +343,28 @@ static const AVOption abuffersink_options[] = { AVFILTER_DEFINE_CLASS(buffersink); AVFILTER_DEFINE_CLASS(abuffersink); -static const AVFilterPad avfilter_vsink_buffer_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vsink_buffer = { .name = "buffersink", .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them available to the end of the filter graph."), .priv_size = sizeof(BufferSinkContext), .priv_class = &buffersink_class, .init = common_init, + .uninit = uninit, .activate = activate, - FILTER_INPUTS(avfilter_vsink_buffer_inputs), + FILTER_INPUTS(ff_video_default_filterpad), .outputs = NULL, FILTER_QUERY_FUNC(vsink_query_formats), }; -static const AVFilterPad avfilter_asink_abuffer_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_asink_abuffer = { .name = "abuffersink", .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them available to the end of the filter graph."), .priv_class = &abuffersink_class, .priv_size = sizeof(BufferSinkContext), .init = common_init, + .uninit = uninit, .activate = activate, - FILTER_INPUTS(avfilter_asink_abuffer_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), .outputs = NULL, FILTER_QUERY_FUNC(asink_query_formats), }; diff --git a/libavfilter/buffersink.h b/libavfilter/buffersink.h index 64e08de53ee..361d6036793 100644 --- a/libavfilter/buffersink.h +++ b/libavfilter/buffersink.h @@ -55,6 +55,8 @@ * The format can be constrained by setting options, using av_opt_set() and * related functions with the AV_OPT_SEARCH_CHILDREN flag. * - pix_fmts (int list), + * - color_spaces (int list), + * - color_ranges (int list), * - sample_fmts (int list), * - sample_rates (int list), * - ch_layouts (string), @@ -117,12 +119,10 @@ AVRational av_buffersink_get_frame_rate (const AVFilterContext *c int av_buffersink_get_w (const AVFilterContext *ctx); int av_buffersink_get_h (const AVFilterContext *ctx); AVRational av_buffersink_get_sample_aspect_ratio (const AVFilterContext *ctx); +enum AVColorSpace av_buffersink_get_colorspace (const AVFilterContext *ctx); +enum AVColorRange av_buffersink_get_color_range (const AVFilterContext *ctx); int av_buffersink_get_channels (const AVFilterContext *ctx); -#if FF_API_OLD_CHANNEL_LAYOUT -attribute_deprecated -uint64_t av_buffersink_get_channel_layout (const AVFilterContext *ctx); -#endif int av_buffersink_get_ch_layout (const AVFilterContext *ctx, AVChannelLayout *ch_layout); int av_buffersink_get_sample_rate (const AVFilterContext *ctx); diff --git a/libavfilter/buffersrc.c b/libavfilter/buffersrc.c index ba17450b937..fcae4f8e699 100644 --- a/libavfilter/buffersrc.c +++ b/libavfilter/buffersrc.c @@ -28,6 +28,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/common.h" #include "libavutil/frame.h" +#include "libavutil/hwcontext.h" #include "libavutil/imgutils.h" #include "libavutil/internal.h" #include "libavutil/opt.h" @@ -36,6 +37,7 @@ #include "audio.h" #include "avfilter.h" #include "buffersrc.h" +#include "filters.h" #include "formats.h" #include "internal.h" #include "video.h" @@ -47,8 +49,10 @@ typedef struct BufferSourceContext { unsigned nb_failed_requests; /* video only */ - int w, h; - enum AVPixelFormat pix_fmt; + int w, h, prev_w, prev_h; + enum AVPixelFormat pix_fmt, prev_pix_fmt; + enum AVColorSpace color_space, prev_color_space; + enum AVColorRange color_range, prev_color_range; AVRational pixel_aspect; AVBufferRef *hw_frames_ctx; @@ -61,13 +65,31 @@ typedef struct BufferSourceContext { AVChannelLayout ch_layout; int eof; + int64_t last_pts; + int link_delta, prev_delta; } BufferSourceContext; -#define CHECK_VIDEO_PARAM_CHANGE(s, c, width, height, format, pts)\ - if (c->w != width || c->h != height || c->pix_fmt != format) {\ - av_log(s, AV_LOG_INFO, "filter context - w: %d h: %d fmt: %d, incoming frame - w: %d h: %d fmt: %d pts_time: %s\n",\ - c->w, c->h, c->pix_fmt, width, height, format, av_ts2timestr(pts, &s->outputs[0]->time_base));\ - av_log(s, AV_LOG_WARNING, "Changing video frame properties on the fly is not supported by all filters.\n");\ +#define CHECK_VIDEO_PARAM_CHANGE(s, c, width, height, format, csp, range, pts)\ + c->link_delta = c->w != width || c->h != height || c->pix_fmt != format ||\ + c->color_space != csp || c->color_range != range;\ + c->prev_delta = c->prev_w != width || c->prev_h != height || c->prev_pix_fmt != format ||\ + c->prev_color_space != csp || c->prev_color_range != range;\ + if (c->link_delta) {\ + int loglevel = c->prev_delta ? AV_LOG_WARNING : AV_LOG_DEBUG;\ + av_log(s, loglevel, "Changing video frame properties on the fly is not supported by all filters.\n");\ + av_log(s, loglevel, "filter context - w: %d h: %d fmt: %d csp: %s range: %s, incoming frame - w: %d h: %d fmt: %d csp: %s range: %s pts_time: %s\n",\ + c->w, c->h, c->pix_fmt, av_color_space_name(c->color_space), av_color_range_name(c->color_range),\ + width, height, format, av_color_space_name(csp), av_color_range_name(range),\ + av_ts2timestr(pts, &s->outputs[0]->time_base));\ + }\ + if (c->prev_delta) {\ + if (!c->link_delta)\ + av_log(s, AV_LOG_VERBOSE, "video frame properties congruent with link at pts_time: %s\n", av_ts2timestr(pts, &s->outputs[0]->time_base));\ + c->prev_w = width;\ + c->prev_h = height;\ + c->prev_pix_fmt = format;\ + c->prev_color_space = csp;\ + c->prev_color_range = range;\ } #define CHECK_AUDIO_PARAM_CHANGE(s, c, srate, layout, format, pts)\ @@ -87,6 +109,8 @@ AVBufferSrcParameters *av_buffersrc_parameters_alloc(void) return NULL; par->format = -1; + par->color_range = AVCOL_RANGE_UNSPECIFIED; + par->color_space = AVCOL_SPC_UNSPECIFIED; return par; } @@ -101,12 +125,12 @@ int av_buffersrc_parameters_set(AVFilterContext *ctx, AVBufferSrcParameters *par switch (ctx->filter->outputs[0].type) { case AVMEDIA_TYPE_VIDEO: if (param->format != AV_PIX_FMT_NONE) { - s->pix_fmt = param->format; + s->pix_fmt = s->prev_pix_fmt = param->format; } if (param->width > 0) - s->w = param->width; + s->w = s->prev_w = param->width; if (param->height > 0) - s->h = param->height; + s->h = s->prev_h = param->height; if (param->sample_aspect_ratio.num > 0 && param->sample_aspect_ratio.den > 0) s->pixel_aspect = param->sample_aspect_ratio; if (param->frame_rate.num > 0 && param->frame_rate.den > 0) @@ -117,6 +141,10 @@ int av_buffersrc_parameters_set(AVFilterContext *ctx, AVBufferSrcParameters *par if (!s->hw_frames_ctx) return AVERROR(ENOMEM); } + if (param->color_space != AVCOL_SPC_UNSPECIFIED) + s->color_space = s->prev_color_space = param->color_space; + if (param->color_range != AVCOL_RANGE_UNSPECIFIED) + s->color_range = s->prev_color_range = param->color_range; break; case AVMEDIA_TYPE_AUDIO: if (param->format != AV_SAMPLE_FMT_NONE) { @@ -124,16 +152,6 @@ int av_buffersrc_parameters_set(AVFilterContext *ctx, AVBufferSrcParameters *par } if (param->sample_rate > 0) s->sample_rate = param->sample_rate; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - // if the old/new fields are set inconsistently, prefer the old ones - if (param->channel_layout && (param->ch_layout.order != AV_CHANNEL_ORDER_NATIVE || - param->ch_layout.u.mask != param->channel_layout)) { - av_channel_layout_uninit(&s->ch_layout); - av_channel_layout_from_mask(&s->ch_layout, param->channel_layout); -FF_ENABLE_DEPRECATION_WARNINGS - } else -#endif if (param->ch_layout.nb_channels) { int ret = av_channel_layout_copy(&s->ch_layout, ¶m->ch_layout); if (ret < 0) @@ -178,22 +196,14 @@ int attribute_align_arg av_buffersrc_add_frame_flags(AVFilterContext *ctx, AVFra AVFrame *copy; int refcounted, ret; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (frame && frame->channel_layout && - av_get_channel_layout_nb_channels(frame->channel_layout) != frame->channels) { - av_log(ctx, AV_LOG_ERROR, "Layout indicates a different number of channels than actually present\n"); - return AVERROR(EINVAL); - } -FF_ENABLE_DEPRECATION_WARNINGS -#endif - s->nb_failed_requests = 0; if (!frame) - return av_buffersrc_close(ctx, AV_NOPTS_VALUE, flags); + return av_buffersrc_close(ctx, s->last_pts, flags); if (s->eof) - return AVERROR(EINVAL); + return AVERROR_EOF; + + s->last_pts = frame->pts + frame->duration; refcounted = !!frame->buf[0]; @@ -202,17 +212,11 @@ FF_ENABLE_DEPRECATION_WARNINGS switch (ctx->outputs[0]->type) { case AVMEDIA_TYPE_VIDEO: CHECK_VIDEO_PARAM_CHANGE(ctx, s, frame->width, frame->height, - frame->format, frame->pts); + frame->format, frame->colorspace, + frame->color_range, frame->pts); break; case AVMEDIA_TYPE_AUDIO: /* For layouts unknown on input but known on link after negotiation. */ -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (!frame->channel_layout) - frame->channel_layout = s->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ? - s->ch_layout.u.mask : 0; -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (frame->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) { ret = av_channel_layout_copy(&frame->ch_layout, &s->ch_layout); if (ret < 0) @@ -227,26 +231,37 @@ FF_ENABLE_DEPRECATION_WARNINGS } - if (!(copy = av_frame_alloc())) - return AVERROR(ENOMEM); - if (refcounted && !(flags & AV_BUFFERSRC_FLAG_KEEP_REF)) { + if (!(copy = av_frame_alloc())) + return AVERROR(ENOMEM); av_frame_move_ref(copy, frame); } else { - ret = av_frame_ref(copy, frame); - if (ret < 0) { - av_frame_free(©); - return ret; - } + copy = av_frame_clone(frame); + if (!copy) + return AVERROR(ENOMEM); } -#if FF_API_PKT_DURATION +#if FF_API_INTERLACED_FRAME FF_DISABLE_DEPRECATION_WARNINGS - if (copy->pkt_duration && copy->pkt_duration != copy->duration) - copy->duration = copy->pkt_duration; + if (copy->interlaced_frame) + copy->flags |= AV_FRAME_FLAG_INTERLACED; + if (copy->top_field_first) + copy->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; FF_ENABLE_DEPRECATION_WARNINGS #endif +#if FF_API_FRAME_KEY +FF_DISABLE_DEPRECATION_WARNINGS + if (copy->key_frame) + copy->flags |= AV_FRAME_FLAG_KEY; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + if (copy->colorspace == AVCOL_SPC_UNSPECIFIED) + copy->colorspace = ctx->outputs[0]->colorspace; + if (copy->color_range == AVCOL_RANGE_UNSPECIFIED) + copy->color_range = ctx->outputs[0]->color_range; + ret = ff_filter_frame(ctx->outputs[0], copy); if (ret < 0) return ret; @@ -273,16 +288,24 @@ static av_cold int init_video(AVFilterContext *ctx) { BufferSourceContext *c = ctx->priv; - if (c->pix_fmt == AV_PIX_FMT_NONE || !c->w || !c->h || - av_q2d(c->time_base) <= 0) { - av_log(ctx, AV_LOG_ERROR, "Invalid parameters provided.\n"); + if (c->pix_fmt == AV_PIX_FMT_NONE) { + av_log(ctx, AV_LOG_ERROR, "Unspecified pixel format\n"); + return AVERROR(EINVAL); + } + if (c->w <= 0 || c->h <= 0) { + av_log(ctx, AV_LOG_ERROR, "Invalid size %dx%d\n", c->w, c->h); + return AVERROR(EINVAL); + } + if (av_q2d(c->time_base) <= 0) { + av_log(ctx, AV_LOG_ERROR, "Invalid time base %d/%d\n", c->time_base.num, c->time_base.den); return AVERROR(EINVAL); } - av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d pixfmt:%s tb:%d/%d fr:%d/%d sar:%d/%d\n", + av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d pixfmt:%s tb:%d/%d fr:%d/%d sar:%d/%d csp:%s range:%s\n", c->w, c->h, av_get_pix_fmt_name(c->pix_fmt), c->time_base.num, c->time_base.den, c->frame_rate.num, c->frame_rate.den, - c->pixel_aspect.num, c->pixel_aspect.den); + c->pixel_aspect.num, c->pixel_aspect.den, + av_color_space_name(c->color_space), av_color_range_name(c->color_range)); return 0; } @@ -305,6 +328,30 @@ static const AVOption buffer_options[] = { { "pixel_aspect", "sample aspect ratio", OFFSET(pixel_aspect), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, DBL_MAX, V }, { "time_base", NULL, OFFSET(time_base), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, DBL_MAX, V }, { "frame_rate", NULL, OFFSET(frame_rate), AV_OPT_TYPE_RATIONAL, { .dbl = 0 }, 0, DBL_MAX, V }, + { "colorspace", "select colorspace", OFFSET(color_space), AV_OPT_TYPE_INT, {.i64=AVCOL_SPC_UNSPECIFIED}, 0, AVCOL_SPC_NB-1, V, .unit = "colorspace"}, + { "gbr", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_RGB}, INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT709}, INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_UNSPECIFIED}, INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "fcc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_FCC}, INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT470BG}, INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE170M}, INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE240M}, INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "ycgco", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_YCGCO}, INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "bt2020nc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_NCL}, INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "bt2020c", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_CL}, INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "smpte2085", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE2085}, INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "chroma-derived-nc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_CHROMA_DERIVED_NCL},INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "chroma-derived-c", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_CHROMA_DERIVED_CL}, INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "ictcp", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_ICTCP}, INT_MIN, INT_MAX, V, .unit = "colorspace"}, + { "range", "select color range", OFFSET(color_range), AV_OPT_TYPE_INT, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, AVCOL_RANGE_NB-1, V, .unit = "range"}, + { "unspecified", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, V, .unit = "range"}, + { "unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, V, .unit = "range"}, + { "limited", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, V, .unit = "range"}, + { "tv", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, V, .unit = "range"}, + { "mpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, V, .unit = "range"}, + { "full", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, V, .unit = "range"}, + { "pc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, V, .unit = "range"}, + { "jpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, V, .unit = "range"}, { NULL }, }; @@ -338,22 +385,9 @@ static av_cold int init_audio(AVFilterContext *ctx) if (!s->ch_layout.nb_channels) { ret = av_channel_layout_from_string(&s->ch_layout, s->channel_layout_str); if (ret < 0) { -#if FF_API_OLD_CHANNEL_LAYOUT - uint64_t mask; -FF_DISABLE_DEPRECATION_WARNINGS - mask = av_get_channel_layout(s->channel_layout_str); - if (!mask) { -#endif - av_log(ctx, AV_LOG_ERROR, "Invalid channel layout %s.\n", - s->channel_layout_str); - return AVERROR(EINVAL); -#if FF_API_OLD_CHANNEL_LAYOUT - } -FF_ENABLE_DEPRECATION_WARNINGS - av_log(ctx, AV_LOG_WARNING, "Channel layout '%s' uses a deprecated syntax.\n", + av_log(ctx, AV_LOG_ERROR, "Invalid channel layout %s.\n", s->channel_layout_str); - av_channel_layout_from_mask(&s->ch_layout, mask); -#endif + return AVERROR(EINVAL); } } @@ -402,14 +436,41 @@ static int query_formats(AVFilterContext *ctx) AVFilterChannelLayouts *channel_layouts = NULL; AVFilterFormats *formats = NULL; AVFilterFormats *samplerates = NULL; + AVFilterFormats *color_spaces = NULL; + AVFilterFormats *color_ranges = NULL; int ret; switch (ctx->outputs[0]->type) { - case AVMEDIA_TYPE_VIDEO: + case AVMEDIA_TYPE_VIDEO: { + enum AVPixelFormat swfmt = c->pix_fmt; + if (av_pix_fmt_desc_get(swfmt)->flags & AV_PIX_FMT_FLAG_HWACCEL) { + if (!c->hw_frames_ctx) { + av_log(ctx, AV_LOG_ERROR, "Setting BufferSourceContext.pix_fmt " + "to a HW format requires hw_frames_ctx to be non-NULL!\n"); + return AVERROR(EINVAL); + } + swfmt = ((AVHWFramesContext *) c->hw_frames_ctx->data)->sw_format; + } if ((ret = ff_add_format (&formats, c->pix_fmt)) < 0 || (ret = ff_set_common_formats (ctx , formats )) < 0) return ret; + /* force specific colorspace/range downstream only for ordinary YUV */ + if (ff_fmt_is_regular_yuv(swfmt)) { + if ((ret = ff_add_format(&color_spaces, c->color_space)) < 0 || + (ret = ff_set_common_color_spaces(ctx, color_spaces)) < 0) + return ret; + if ((ret = ff_add_format(&color_ranges, c->color_range)) < 0) + return ret; + if (c->color_range == AVCOL_RANGE_UNSPECIFIED) { + /* allow implicitly promoting unspecified to mpeg */ + if ((ret = ff_add_format(&color_ranges, AVCOL_RANGE_MPEG)) < 0) + return ret; + } + if ((ret = ff_set_common_color_ranges(ctx, color_ranges)) < 0) + return ret; + } break; + } case AVMEDIA_TYPE_AUDIO: if ((ret = ff_add_format (&formats , c->sample_fmt )) < 0 || (ret = ff_set_common_formats (ctx , formats )) < 0 || @@ -446,7 +507,7 @@ static int config_props(AVFilterLink *link) } break; case AVMEDIA_TYPE_AUDIO: - if (!c->ch_layout.nb_channels) { + if (!c->ch_layout.nb_channels || c->ch_layout.order == AV_CHANNEL_ORDER_UNSPEC) { int ret = av_channel_layout_copy(&c->ch_layout, &link->ch_layout); if (ret < 0) return ret; @@ -461,21 +522,28 @@ static int config_props(AVFilterLink *link) return 0; } -static int request_frame(AVFilterLink *link) +static int activate(AVFilterContext *ctx) { - BufferSourceContext *c = link->src->priv; + AVFilterLink *outlink = ctx->outputs[0]; + BufferSourceContext *c = ctx->priv; - if (c->eof) - return AVERROR_EOF; + if (!c->eof && ff_outlink_get_status(outlink)) { + c->eof = 1; + return 0; + } + + if (c->eof) { + ff_outlink_set_status(outlink, AVERROR_EOF, c->last_pts); + return 0; + } c->nb_failed_requests++; - return AVERROR(EAGAIN); + return FFERROR_NOT_READY; } static const AVFilterPad avfilter_vsrc_buffer_outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, - .request_frame = request_frame, .config_props = config_props, }, }; @@ -484,7 +552,7 @@ const AVFilter ff_vsrc_buffer = { .name = "buffer", .description = NULL_IF_CONFIG_SMALL("Buffer video frames, and make them accessible to the filterchain."), .priv_size = sizeof(BufferSourceContext), - + .activate = activate, .init = init_video, .uninit = uninit, @@ -498,7 +566,6 @@ static const AVFilterPad avfilter_asrc_abuffer_outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .request_frame = request_frame, .config_props = config_props, }, }; @@ -507,7 +574,7 @@ const AVFilter ff_asrc_abuffer = { .name = "abuffer", .description = NULL_IF_CONFIG_SMALL("Buffer audio frames, and make them accessible to the filterchain."), .priv_size = sizeof(BufferSourceContext), - + .activate = activate, .init = init_audio, .uninit = uninit, diff --git a/libavfilter/buffersrc.h b/libavfilter/buffersrc.h index 3b248b37cd8..6f3344f4452 100644 --- a/libavfilter/buffersrc.h +++ b/libavfilter/buffersrc.h @@ -110,19 +110,16 @@ typedef struct AVBufferSrcParameters { */ int sample_rate; -#if FF_API_OLD_CHANNEL_LAYOUT /** * Audio only, the audio channel layout - * @deprecated use ch_layout */ - attribute_deprecated - uint64_t channel_layout; -#endif + AVChannelLayout ch_layout; /** - * Audio only, the audio channel layout + * Video only, the YUV colorspace and range. */ - AVChannelLayout ch_layout; + enum AVColorSpace color_space; + enum AVColorRange color_range; } AVBufferSrcParameters; /** diff --git a/libavfilter/bwdif.h b/libavfilter/bwdif.h deleted file mode 100644 index 889ff772edd..00000000000 --- a/libavfilter/bwdif.h +++ /dev/null @@ -1,42 +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 - */ - -#ifndef AVFILTER_BWDIF_H -#define AVFILTER_BWDIF_H - -#include "libavutil/pixdesc.h" -#include "avfilter.h" -#include "yadif.h" - -typedef struct BWDIFContext { - YADIFContext yadif; - - void (*filter_intra)(void *dst1, void *cur1, int w, int prefs, int mrefs, - int prefs3, int mrefs3, int parity, int clip_max); - void (*filter_line)(void *dst, void *prev, void *cur, void *next, - int w, int prefs, int mrefs, int prefs2, int mrefs2, - int prefs3, int mrefs3, int prefs4, int mrefs4, - int parity, int clip_max); - void (*filter_edge)(void *dst, void *prev, void *cur, void *next, - int w, int prefs, int mrefs, int prefs2, int mrefs2, - int parity, int clip_max, int spat); -} BWDIFContext; - -void ff_bwdif_init_x86(BWDIFContext *bwdif); - -#endif /* AVFILTER_BWDIF_H */ diff --git a/libavfilter/bwdifdsp.c b/libavfilter/bwdifdsp.c new file mode 100644 index 00000000000..e87fe414e04 --- /dev/null +++ b/libavfilter/bwdifdsp.c @@ -0,0 +1,226 @@ +/* + * BobWeaver Deinterlacing Filter DSP functions + * Copyright (C) 2016 Thomas Mundt + * + * Based on YADIF (Yet Another Deinterlacing Filter) + * Copyright (C) 2006-2011 Michael Niedermayer + * 2010 James Darnley + * + * With use of Weston 3 Field Deinterlacing Filter algorithm + * Copyright (C) 2012 British Broadcasting Corporation, All Rights Reserved + * Author of de-interlace algorithm: Jim Easterbrook for BBC R&D + * Based on the process described by Martin Weston for BBC R&D + * + * 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 +#include + +#include "config.h" + +#include "bwdifdsp.h" +#include "libavutil/attributes.h" +#include "libavutil/common.h" +#include "libavutil/macros.h" + +/* + * Filter coefficients coef_lf and coef_hf taken from BBC PH-2071 (Weston 3 Field Deinterlacer). + * Used when there is spatial and temporal interpolation. + * Filter coefficients coef_sp are used when there is spatial interpolation only. + * Adjusted for matching visual sharpness impression of spatial and temporal interpolation. + */ +static const uint16_t coef_lf[2] = { 4309, 213 }; +static const uint16_t coef_hf[3] = { 5570, 3801, 1016 }; +static const uint16_t coef_sp[2] = { 5077, 981 }; + + +#define FILTER_INTRA() \ + for (x = 0; x < w; x++) { \ + interpol = (coef_sp[0] * (cur[mrefs] + cur[prefs]) - coef_sp[1] * (cur[mrefs3] + cur[prefs3])) >> 13; \ + dst[0] = av_clip(interpol, 0, clip_max); \ + \ + dst++; \ + cur++; \ + } + +#define FILTER1() \ + for (x = 0; x < w; x++) { \ + int c = cur[mrefs]; \ + int d = (prev2[0] + next2[0]) >> 1; \ + int e = cur[prefs]; \ + int temporal_diff0 = FFABS(prev2[0] - next2[0]); \ + int temporal_diff1 =(FFABS(prev[mrefs] - c) + FFABS(prev[prefs] - e)) >> 1; \ + int temporal_diff2 =(FFABS(next[mrefs] - c) + FFABS(next[prefs] - e)) >> 1; \ + int diff = FFMAX3(temporal_diff0 >> 1, temporal_diff1, temporal_diff2); \ + \ + if (!diff) { \ + dst[0] = d; \ + } else { + +#define SPAT_CHECK() \ + int b = ((prev2[mrefs2] + next2[mrefs2]) >> 1) - c; \ + int f = ((prev2[prefs2] + next2[prefs2]) >> 1) - e; \ + int dc = d - c; \ + int de = d - e; \ + int max = FFMAX3(de, dc, FFMIN(b, f)); \ + int min = FFMIN3(de, dc, FFMAX(b, f)); \ + diff = FFMAX3(diff, min, -max); + +#define FILTER_LINE() \ + SPAT_CHECK() \ + if (FFABS(c - e) > temporal_diff0) { \ + interpol = (((coef_hf[0] * (prev2[0] + next2[0]) \ + - coef_hf[1] * (prev2[mrefs2] + next2[mrefs2] + prev2[prefs2] + next2[prefs2]) \ + + coef_hf[2] * (prev2[mrefs4] + next2[mrefs4] + prev2[prefs4] + next2[prefs4])) >> 2) \ + + coef_lf[0] * (c + e) - coef_lf[1] * (cur[mrefs3] + cur[prefs3])) >> 13; \ + } else { \ + interpol = (coef_sp[0] * (c + e) - coef_sp[1] * (cur[mrefs3] + cur[prefs3])) >> 13; \ + } + +#define FILTER_EDGE() \ + if (spat) { \ + SPAT_CHECK() \ + } \ + interpol = (c + e) >> 1; + +#define FILTER2() \ + if (interpol > d + diff) \ + interpol = d + diff; \ + else if (interpol < d - diff) \ + interpol = d - diff; \ + \ + dst[0] = av_clip(interpol, 0, clip_max); \ + } \ + \ + dst++; \ + cur++; \ + prev++; \ + next++; \ + prev2++; \ + next2++; \ + } + +void ff_bwdif_filter_intra_c(void *dst1, const void *cur1, int w, int prefs, int mrefs, + int prefs3, int mrefs3, int parity, int clip_max) +{ + uint8_t *dst = dst1; + const uint8_t *cur = cur1; + int interpol, x; + + FILTER_INTRA() +} + +void ff_bwdif_filter_line_c(void *dst1, const void *prev1, const void *cur1, const void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int prefs3, int mrefs3, int prefs4, int mrefs4, + int parity, int clip_max) +{ + uint8_t *dst = dst1; + const uint8_t *prev = prev1; + const uint8_t *cur = cur1; + const uint8_t *next = next1; + const uint8_t *prev2 = parity ? prev : cur ; + const uint8_t *next2 = parity ? cur : next; + int interpol, x; + + FILTER1() + FILTER_LINE() + FILTER2() +} + +void ff_bwdif_filter_edge_c(void *dst1, const void *prev1, const void *cur1, const void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int parity, int clip_max, int spat) +{ + uint8_t *dst = dst1; + const uint8_t *prev = prev1; + const uint8_t *cur = cur1; + const uint8_t *next = next1; + const uint8_t *prev2 = parity ? prev : cur ; + const uint8_t *next2 = parity ? cur : next; + int interpol, x; + + FILTER1() + FILTER_EDGE() + FILTER2() +} + +static void filter_intra_16bit(void *dst1, const void *cur1, int w, int prefs, int mrefs, + int prefs3, int mrefs3, int parity, int clip_max) +{ + uint16_t *dst = dst1; + const uint16_t *cur = cur1; + int interpol, x; + + FILTER_INTRA() +} + +static void filter_line_c_16bit(void *dst1, const void *prev1, const void *cur1, const void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int prefs3, int mrefs3, int prefs4, int mrefs4, + int parity, int clip_max) +{ + uint16_t *dst = dst1; + const uint16_t *prev = prev1; + const uint16_t *cur = cur1; + const uint16_t *next = next1; + const uint16_t *prev2 = parity ? prev : cur ; + const uint16_t *next2 = parity ? cur : next; + int interpol, x; + + FILTER1() + FILTER_LINE() + FILTER2() +} + +static void filter_edge_16bit(void *dst1, const void *prev1, const void *cur1, const void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int parity, int clip_max, int spat) +{ + uint16_t *dst = dst1; + const uint16_t *prev = prev1; + const uint16_t *cur = cur1; + const uint16_t *next = next1; + const uint16_t *prev2 = parity ? prev : cur ; + const uint16_t *next2 = parity ? cur : next; + int interpol, x; + + FILTER1() + FILTER_EDGE() + FILTER2() +} + +av_cold void ff_bwdif_init_filter_line(BWDIFDSPContext *s, int bit_depth) +{ + s->filter_line3 = 0; + if (bit_depth > 8) { + s->filter_intra = filter_intra_16bit; + s->filter_line = filter_line_c_16bit; + s->filter_edge = filter_edge_16bit; + } else { + s->filter_intra = ff_bwdif_filter_intra_c; + s->filter_line = ff_bwdif_filter_line_c; + s->filter_edge = ff_bwdif_filter_edge_c; + } + +#if ARCH_X86 + ff_bwdif_init_x86(s, bit_depth); +#elif ARCH_AARCH64 + ff_bwdif_init_aarch64(s, bit_depth); +#endif +} diff --git a/libavfilter/bwdifdsp.h b/libavfilter/bwdifdsp.h new file mode 100644 index 00000000000..4a350e5cccb --- /dev/null +++ b/libavfilter/bwdifdsp.h @@ -0,0 +1,85 @@ +/* + * 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 + */ + +#ifndef AVFILTER_BWDIFDSP_H +#define AVFILTER_BWDIFDSP_H + +#include +#include + +typedef struct BWDIFDSPContext { + void (*filter_intra)(void *dst1, const void *cur1, int w, int prefs, int mrefs, + int prefs3, int mrefs3, int parity, int clip_max); + void (*filter_line)(void *dst, const void *prev, const void *cur, const void *next, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int prefs3, int mrefs3, int prefs4, int mrefs4, + int parity, int clip_max); + void (*filter_edge)(void *dst, const void *prev, const void *cur, const void *next, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int parity, int clip_max, int spat); + void (*filter_line3)(void *dst, int dstride, + const void *prev, const void *cur, const void *next, int prefs, + int w, int parity, int clip_max); +} BWDIFDSPContext; + +void ff_bwdif_init_filter_line(BWDIFDSPContext *bwdif, int bit_depth); +void ff_bwdif_init_x86(BWDIFDSPContext *bwdif, int bit_depth); +void ff_bwdif_init_aarch64(BWDIFDSPContext *bwdif, int bit_depth); + +void ff_bwdif_filter_edge_c(void *dst1, const void *prev1, const void *cur1, const void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int parity, int clip_max, int spat); + +void ff_bwdif_filter_intra_c(void *dst1, const void *cur1, int w, int prefs, int mrefs, + int prefs3, int mrefs3, int parity, int clip_max); + +void ff_bwdif_filter_line_c(void *dst1, const void *prev1, const void *cur1, const void *next1, + int w, int prefs, int mrefs, int prefs2, int mrefs2, + int prefs3, int mrefs3, int prefs4, int mrefs4, + int parity, int clip_max); + +static inline +void ff_bwdif_filter_line3_c(void * dst1, int d_stride, + const void * prev1, const void * cur1, const void * next1, int s_stride, + int w, int parity, int clip_max) +{ + const int prefs = s_stride; + uint8_t * dst = dst1; + const uint8_t * prev = prev1; + const uint8_t * cur = cur1; + const uint8_t * next = next1; + + ff_bwdif_filter_line_c(dst, prev, cur, next, w, + prefs, -prefs, prefs * 2, - prefs * 2, prefs * 3, -prefs * 3, prefs * 4, -prefs * 4, parity, clip_max); +#define NEXT_LINE()\ + dst += d_stride; \ + prev += prefs; \ + cur += prefs; \ + next += prefs; + + NEXT_LINE(); + memcpy(dst, cur, w); + NEXT_LINE(); +#undef NEXT_LINE + ff_bwdif_filter_line_c(dst, prev, cur, next, w, + prefs, -prefs, prefs * 2, - prefs * 2, prefs * 3, -prefs * 3, prefs * 4, -prefs * 4, parity, clip_max); +} + + + +#endif /* AVFILTER_BWDIFDSP_H */ diff --git a/libavfilter/ccfifo.c b/libavfilter/ccfifo.c new file mode 100644 index 00000000000..d76dbff149e --- /dev/null +++ b/libavfilter/ccfifo.c @@ -0,0 +1,195 @@ +/* + * CEA-708 Closed Captioning FIFO + * Copyright (c) 2023 LTN Global Communications + * + * Author: Devin Heitmueller + * + * 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 "ccfifo.h" +#include "libavutil/fifo.h" + +#define MAX_CC_ELEMENTS 128 + +struct cc_lookup { + int num; + int den; + int cc_count; + int num_608; +}; + +const static struct cc_lookup cc_lookup_vals[] = { + { 15, 1, 40, 4 }, + { 24, 1, 25, 3 }, + { 24000, 1001, 25, 3 }, + { 30, 1, 20, 2 }, + { 30000, 1001, 20, 2}, + { 60, 1, 10, 1 }, + { 60000, 1001, 10, 1}, +}; + +void ff_ccfifo_uninit(CCFifo *ccf) +{ + av_fifo_freep2(&ccf->cc_608_fifo); + av_fifo_freep2(&ccf->cc_708_fifo); + memset(ccf, 0, sizeof(*ccf)); +} + +int ff_ccfifo_init(CCFifo *ccf, AVRational framerate, void *log_ctx) +{ + int i; + + memset(ccf, 0, sizeof(*ccf)); + ccf->log_ctx = log_ctx; + ccf->framerate = framerate; + + if (!(ccf->cc_708_fifo = av_fifo_alloc2(MAX_CC_ELEMENTS, CC_BYTES_PER_ENTRY, 0))) + goto error; + + if (!(ccf->cc_608_fifo = av_fifo_alloc2(MAX_CC_ELEMENTS, CC_BYTES_PER_ENTRY, 0))) + goto error; + + /* Based on the target FPS, figure out the expected cc_count and number of + 608 tuples per packet. See ANSI/CTA-708-E Sec 4.3.6.1. */ + for (i = 0; i < FF_ARRAY_ELEMS(cc_lookup_vals); i++) { + if (framerate.num == cc_lookup_vals[i].num && + framerate.den == cc_lookup_vals[i].den) { + ccf->expected_cc_count = cc_lookup_vals[i].cc_count; + ccf->expected_608 = cc_lookup_vals[i].num_608; + break; + } + } + + if (ccf->expected_608 == 0) { + /* We didn't find an output frame we support. We'll let the call succeed + and the FIFO to be allocated, but the extract/inject functions will simply + leave everything the way it is */ + ccf->passthrough = 1; + } + + return 0; + +error: + ff_ccfifo_uninit(ccf); + return AVERROR(ENOMEM); +} + +int ff_ccfifo_injectbytes(CCFifo *ccf, uint8_t *cc_data, size_t len) +{ + int cc_608_tuples = 0; + int cc_708_tuples = 0; + int cc_filled = 0; + + if (ccf->passthrough) { + return 0; + } + + if (len < ff_ccfifo_getoutputsize(ccf)) { + return AVERROR(EINVAL); + } + + /* Insert any available data from the 608 FIFO */ + if (ccf->expected_608 <= av_fifo_can_read(ccf->cc_608_fifo)) + cc_608_tuples = ccf->expected_608; + else + cc_608_tuples = av_fifo_can_read(ccf->cc_608_fifo); + av_fifo_read(ccf->cc_608_fifo, cc_data, cc_608_tuples); + cc_filled += cc_608_tuples; + + /* Insert any available data from the 708 FIFO */ + if ((ccf->expected_cc_count - cc_filled) <= av_fifo_can_read(ccf->cc_708_fifo)) + cc_708_tuples = ccf->expected_cc_count - cc_filled; + else + cc_708_tuples = av_fifo_can_read(ccf->cc_708_fifo); + av_fifo_read(ccf->cc_708_fifo, &cc_data[cc_filled * CC_BYTES_PER_ENTRY], cc_708_tuples); + cc_filled += cc_708_tuples; + + /* Insert 708 padding into any remaining fields */ + while (cc_filled < ccf->expected_cc_count) { + cc_data[cc_filled * CC_BYTES_PER_ENTRY] = 0xfa; + cc_data[cc_filled * CC_BYTES_PER_ENTRY + 1] = 0x00; + cc_data[cc_filled * CC_BYTES_PER_ENTRY + 2] = 0x00; + cc_filled++; + } + + return 0; +} + +int ff_ccfifo_inject(CCFifo *ccf, AVFrame *frame) +{ + AVFrameSideData *sd; + int ret; + + if (ccf->passthrough == 1 || ccf->cc_detected == 0) + return 0; + + sd = av_frame_new_side_data(frame, AV_FRAME_DATA_A53_CC, + ff_ccfifo_getoutputsize(ccf)); + if (sd) { + ret = ff_ccfifo_injectbytes(ccf, sd->data, sd->size); + if (ret < 0) { + av_frame_remove_side_data(frame, AV_FRAME_DATA_A53_CC); + return ret; + } + } + + return 0; +} + +int ff_ccfifo_extractbytes(CCFifo *ccf, uint8_t *cc_bytes, size_t len) +{ + int cc_count = len / CC_BYTES_PER_ENTRY; + + if (ccf->passthrough == 1) { + av_log_once(ccf->log_ctx, AV_LOG_WARNING, AV_LOG_DEBUG, &ccf->passthrough_warning, + "cc_fifo cannot transcode captions fps=%d/%d\n", + ccf->framerate.num, ccf->framerate.den); + return 0; + } + + ccf->cc_detected = 1; + + for (int i = 0; i < cc_count; i++) { + /* See ANSI/CTA-708-E Sec 4.3, Table 3 */ + uint8_t cc_valid = (cc_bytes[CC_BYTES_PER_ENTRY*i] & 0x04) >> 2; + uint8_t cc_type = cc_bytes[CC_BYTES_PER_ENTRY*i] & 0x03; + if (cc_type == 0x00 || cc_type == 0x01) { + av_fifo_write(ccf->cc_608_fifo, &cc_bytes[CC_BYTES_PER_ENTRY*i], 1); + } else if (cc_valid && (cc_type == 0x02 || cc_type == 0x03)) { + av_fifo_write(ccf->cc_708_fifo, &cc_bytes[CC_BYTES_PER_ENTRY*i], 1); + } + } + return 0; +} + +/* Read the A53 side data, discard padding, and put 608/708 into + queues so we can ensure they get into the output frames at + the correct rate... */ +int ff_ccfifo_extract(CCFifo *ccf, AVFrame *frame) +{ + AVFrameSideData *side_data = av_frame_get_side_data(frame, AV_FRAME_DATA_A53_CC); + if (side_data) { + ff_ccfifo_extractbytes(ccf, side_data->data, side_data->size); + + /* Remove the side data, as we will re-create it on the + output as needed */ + if (!ccf->passthrough) + av_frame_remove_side_data(frame, AV_FRAME_DATA_A53_CC); + } + return 0; +} diff --git a/libavfilter/ccfifo.h b/libavfilter/ccfifo.h new file mode 100644 index 00000000000..d3f8a52cc18 --- /dev/null +++ b/libavfilter/ccfifo.h @@ -0,0 +1,129 @@ +/* + * CEA-708 Closed Captioning FIFO + * Copyright (c) 2023 LTN Global Communications + * + * Author: Devin Heitmueller + * + * 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 + * CC FIFO Buffer + */ + +#ifndef AVFILTER_CCFIFO_H +#define AVFILTER_CCFIFO_H + +#include +#include + +#include "libavutil/frame.h" +#include "libavutil/rational.h" + +#define CC_BYTES_PER_ENTRY 3 + +typedef struct CCFifo { + struct AVFifo *cc_608_fifo; + struct AVFifo *cc_708_fifo; + AVRational framerate; + int expected_cc_count; + int expected_608; + int cc_detected; + int passthrough; + int passthrough_warning; + void *log_ctx; +} CCFifo; + +/** + * Initialize a CCFifo. + * + * @param framerate output framerate + * @param log_ctx used for any av_log() calls + * @return Zero on success, or negative AVERROR code on failure. + */ +int ff_ccfifo_init(CCFifo *ccf, AVRational framerate, void *log_ctx); + +/** + * Free all memory allocated in a CCFifo and clear the context. + * + * @param ccf Pointer to the CCFifo which should be uninitialized + */ +void ff_ccfifo_uninit(CCFifo *ccf); + +/** + * Extract CC data from an AVFrame + * + * Extract CC bytes from the AVFrame, insert them into our queue, and + * remove the side data from the AVFrame. The side data is removed + * as it will be re-inserted at the appropriate rate later in the + * filter. + * + * @param af CCFifo to write to + * @param frame AVFrame with the video frame to operate on + * @return Zero on success, or negative AVERROR + * code on failure. + */ +int ff_ccfifo_extract(CCFifo *ccf, AVFrame *frame); + +/** + *Just like ff_ccfifo_extract(), but takes the raw bytes instead of an AVFrame + */ +int ff_ccfifo_extractbytes(CCFifo *ccf, uint8_t *data, size_t len); + +/** + * Provide the size in bytes of an output buffer to allocate + * + * Ask for how many bytes the output will contain, so the caller can allocate + * an appropriately sized buffer and pass it to ff_ccfifo_injectbytes() + * + */ +static inline int ff_ccfifo_getoutputsize(const CCFifo *ccf) +{ + return ccf->expected_cc_count * CC_BYTES_PER_ENTRY; +} + + +/** + * Insert CC data from the FIFO into an AVFrame (as side data) + * + * Dequeue the appropriate number of CC tuples based on the + * frame rate, and insert them into the AVFrame + * + * @param af CCFifo to read from + * @param frame AVFrame with the video frame to operate on + * @return Zero on success, or negative AVERROR + * code on failure. + */ +int ff_ccfifo_inject(CCFifo *ccf, AVFrame *frame); + +/** + * Just like ff_ccfifo_inject(), but takes the raw bytes to insert the CC data + * int rather than an AVFrame + */ +int ff_ccfifo_injectbytes(CCFifo *ccf, uint8_t *data, size_t len); + +/** + * Returns 1 if captions have been found as a prior call + * to ff_ccfifo_extract() or ff_ccfifo_extractbytes() + */ +static inline int ff_ccfifo_ccdetected(const CCFifo *ccf) +{ + return ccf->cc_detected; +} + +#endif /* AVFILTER_CCFIFO_H */ diff --git a/libavfilter/convolution.h b/libavfilter/convolution.h index e44bfb5da80..ee7477ef89d 100644 --- a/libavfilter/convolution.h +++ b/libavfilter/convolution.h @@ -34,13 +34,14 @@ typedef struct ConvolutionContext { const AVClass *class; char *matrix_str[4]; - float rdiv[4]; + float user_rdiv[4]; float bias[4]; int mode[4]; float scale; float delta; int planes; + float rdiv[4]; int size[4]; int depth; int max; diff --git a/libavfilter/cuda/load_helper.h b/libavfilter/cuda/load_helper.h index 4ae78095c43..455bf36a238 100644 --- a/libavfilter/cuda/load_helper.h +++ b/libavfilter/cuda/load_helper.h @@ -20,7 +20,7 @@ #define AVFILTER_CUDA_LOAD_HELPER_H /** - * Loads a CUDA module and applies any decompression, if neccesary. + * Loads a CUDA module and applies any decompression, if necessary. */ int ff_cuda_load_module(void *avctx, AVCUDADeviceContext *hwctx, CUmodule *cu_module, const unsigned char *data, const unsigned int length); diff --git a/libavfilter/deshake.h b/libavfilter/deshake.h deleted file mode 100644 index 406cbab2f65..00000000000 --- a/libavfilter/deshake.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2013 Wei Gao - * Copyright (C) 2013 Lenny Wang - * - * 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 - */ - -#ifndef AVFILTER_DESHAKE_H -#define AVFILTER_DESHAKE_H - -#include "config.h" -#include "avfilter.h" -#include "transform.h" -#include "libavutil/pixelutils.h" - - -enum SearchMethod { - EXHAUSTIVE, ///< Search all possible positions - SMART_EXHAUSTIVE, ///< Search most possible positions (faster) - SEARCH_COUNT -}; - -typedef struct IntMotionVector { - int x; ///< Horizontal shift - int y; ///< Vertical shift -} IntMotionVector; - -typedef struct MotionVector { - double x; ///< Horizontal shift - double y; ///< Vertical shift -} MotionVector; - -typedef struct Transform { - MotionVector vec; ///< Motion vector - double angle; ///< Angle of rotation - double zoom; ///< Zoom percentage -} Transform; - -#define MAX_R 64 - -typedef struct DeshakeContext { - const AVClass *class; - int counts[2*MAX_R+1][2*MAX_R+1]; /// < Scratch buffer for motion search - double *angles; ///< Scratch buffer for block angles - unsigned angles_size; - AVFrame *ref; ///< Previous frame - int rx; ///< Maximum horizontal shift - int ry; ///< Maximum vertical shift - int edge; ///< Edge fill method - int blocksize; ///< Size of blocks to compare - int contrast; ///< Contrast threshold - int search; ///< Motion search method - av_pixelutils_sad_fn sad; ///< Sum of the absolute difference function - Transform last; ///< Transform from last frame - int refcount; ///< Number of reference frames (defines averaging window) - FILE *fp; - Transform avg; - int cw; ///< Crop motion search to this box - int ch; - int cx; - int cy; - char *filename; ///< Motion search detailed log filename - int opencl; - int (* transform)(AVFilterContext *ctx, int width, int height, int cw, int ch, - const float *matrix_y, const float *matrix_uv, enum InterpolateMethod interpolate, - enum FillMethod fill, AVFrame *in, AVFrame *out); -} DeshakeContext; - -#endif /* AVFILTER_DESHAKE_H */ diff --git a/libavfilter/dialoguenhance_template.c b/libavfilter/dialoguenhance_template.c new file mode 100644 index 00000000000..26650f7d8c9 --- /dev/null +++ b/libavfilter/dialoguenhance_template.c @@ -0,0 +1,274 @@ +/* + * 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/tx.h" +#include "avfilter.h" +#include "internal.h" +#include "audio.h" + +#undef ctype +#undef ftype +#undef SQRT +#undef HYPOT +#undef SAMPLE_FORMAT +#undef TX_TYPE +#undef ONE +#undef ZERO +#undef HALF +#undef SIN +#undef CLIP +#undef EPSILON +#if DEPTH == 32 +#define SAMPLE_FORMAT float +#define SQRT sqrtf +#define HYPOT hypotf +#define ctype AVComplexFloat +#define ftype float +#define TX_TYPE AV_TX_FLOAT_RDFT +#define ONE 1.f +#define ZERO 0.f +#define HALF 0.5f +#define SIN sinf +#define CLIP av_clipf +#define EPSILON FLT_EPSILON +#else +#define SAMPLE_FORMAT double +#define SQRT sqrt +#define HYPOT hypot +#define ctype AVComplexDouble +#define ftype double +#define TX_TYPE AV_TX_DOUBLE_RDFT +#define ONE 1.0 +#define ZERO 0.0 +#define HALF 0.5 +#define SIN sin +#define CLIP av_clipd +#define EPSILON DBL_EPSILON +#endif + +#define fn3(a,b) a##_##b +#define fn2(a,b) fn3(a,b) +#define fn(a) fn2(a, SAMPLE_FORMAT) + +static int fn(de_tx_init)(AVFilterContext *ctx) +{ + AudioDialogueEnhanceContext *s = ctx->priv; + ftype scale = ONE, iscale = ONE / (s->fft_size * 1.5f); + int ret; + + s->window = av_calloc(s->fft_size, sizeof(ftype)); + if (!s->window) + return AVERROR(ENOMEM); + fn(s->window) = s->window; + for (int n = 0; n < s->fft_size; n++) + fn(s->window)[n] = SIN(M_PI*n/(s->fft_size-1)); + + ret = av_tx_init(&s->tx_ctx[0], &s->tx_fn, TX_TYPE, 0, s->fft_size, &scale, 0); + if (ret < 0) + return ret; + + ret = av_tx_init(&s->tx_ctx[1], &s->tx_fn, TX_TYPE, 0, s->fft_size, &scale, 0); + if (ret < 0) + return ret; + + ret = av_tx_init(&s->itx_ctx, &s->itx_fn, TX_TYPE, 1, s->fft_size, &iscale, 0); + if (ret < 0) + return ret; + + return 0; +} + +static void fn(apply_window)(AudioDialogueEnhanceContext *s, + const ftype *in_frame, ftype *out_frame, const int add_to_out_frame) +{ + const ftype *window = fn(s->window); + const int fft_size = s->fft_size; + + if (add_to_out_frame) { + for (int i = 0; i < fft_size; i++) + out_frame[i] += in_frame[i] * window[i]; + } else { + for (int i = 0; i < fft_size; i++) + out_frame[i] = in_frame[i] * window[i]; + } +} + +static ftype fn(sqr)(ftype x) +{ + return x * x; +} + +static void fn(get_centere)(ctype *left, ctype *right, + ctype *center, int N) +{ + for (int i = 0; i < N; i++) { + const ftype l_re = left[i].re; + const ftype l_im = left[i].im; + const ftype r_re = right[i].re; + const ftype r_im = right[i].im; + const ftype a = HALF * (ONE - SQRT((fn(sqr)(l_re - r_re) + fn(sqr)(l_im - r_im))/ + (fn(sqr)(l_re + r_re) + fn(sqr)(l_im + r_im) + EPSILON))); + + center[i].re = a * (l_re + r_re); + center[i].im = a * (l_im + r_im); + } +} + +static ftype fn(flux)(ftype *curf, ftype *prevf, int N) +{ + ctype *cur = (ctype *)curf; + ctype *prev = (ctype *)prevf; + ftype sum = ZERO; + + for (int i = 0; i < N; i++) { + ftype c_re = cur[i].re; + ftype c_im = cur[i].im; + ftype p_re = prev[i].re; + ftype p_im = prev[i].im; + + sum += fn(sqr)(HYPOT(c_re, c_im) - HYPOT(p_re, p_im)); + } + + return sum; +} + +static ftype fn(fluxlr)(ftype *lf, ftype *lpf, + ftype *rf, ftype *rpf, + int N) +{ + ctype *l = (ctype *)lf; + ctype *lp = (ctype *)lpf; + ctype *r = (ctype *)rf; + ctype *rp = (ctype *)rpf; + ftype sum = ZERO; + + for (int i = 0; i < N; i++) { + ftype c_re = l[i].re - r[i].re; + ftype c_im = l[i].im - r[i].im; + ftype p_re = lp[i].re - rp[i].re; + ftype p_im = lp[i].im - rp[i].im; + + sum += fn(sqr)(HYPOT(c_re, c_im) - HYPOT(p_re, p_im)); + } + + return sum; +} + +static ftype fn(calc_vad)(ftype fc, ftype flr, ftype a) +{ + const ftype vad = a * (fc / (fc + flr) - HALF); + + return CLIP(vad, ZERO, ONE); +} + +static void fn(get_final)(ftype *c, ftype *l, + ftype *r, ftype vad, int N, + ftype original, ftype enhance) +{ + ctype *center = (ctype *)c; + ctype *left = (ctype *)l; + ctype *right = (ctype *)r; + + for (int i = 0; i < N; i++) { + ftype cP = fn(sqr)(center[i].re) + fn(sqr)(center[i].im); + ftype lrP = fn(sqr)(left[i].re - right[i].re) + fn(sqr)(left[i].im - right[i].im); + ftype G = cP / (cP + lrP + EPSILON); + ftype re, im; + + re = center[i].re * (original + vad * G * enhance); + im = center[i].im * (original + vad * G * enhance); + + center[i].re = re; + center[i].im = im; + } +} + +static int fn(de_stereo)(AVFilterContext *ctx, AVFrame *out) +{ + AudioDialogueEnhanceContext *s = ctx->priv; + ftype *center = (ftype *)s->center_frame->extended_data[0]; + ftype *center_prev = (ftype *)s->center_frame->extended_data[1]; + ftype *left_in = (ftype *)s->in_frame->extended_data[0]; + ftype *right_in = (ftype *)s->in_frame->extended_data[1]; + ftype *left_out = (ftype *)s->out_dist_frame->extended_data[0]; + ftype *right_out = (ftype *)s->out_dist_frame->extended_data[1]; + ftype *left_samples = (ftype *)s->in->extended_data[0]; + ftype *right_samples = (ftype *)s->in->extended_data[1]; + ftype *windowed_left = (ftype *)s->windowed_frame->extended_data[0]; + ftype *windowed_right = (ftype *)s->windowed_frame->extended_data[1]; + ftype *windowed_oleft = (ftype *)s->windowed_out->extended_data[0]; + ftype *windowed_oright = (ftype *)s->windowed_out->extended_data[1]; + ftype *windowed_pleft = (ftype *)s->windowed_prev->extended_data[0]; + ftype *windowed_pright = (ftype *)s->windowed_prev->extended_data[1]; + ftype *left_osamples = (ftype *)out->extended_data[0]; + ftype *right_osamples = (ftype *)out->extended_data[1]; + ftype *center_osamples = (ftype *)out->extended_data[2]; + const int overlap = s->overlap; + const int offset = s->fft_size - overlap; + const int nb_samples = FFMIN(overlap, s->in->nb_samples); + ftype vad; + + // shift in/out buffers + memmove(left_in, &left_in[overlap], offset * sizeof(ftype)); + memmove(right_in, &right_in[overlap], offset * sizeof(ftype)); + memmove(left_out, &left_out[overlap], offset * sizeof(ftype)); + memmove(right_out, &right_out[overlap], offset * sizeof(ftype)); + + memcpy(&left_in[offset], left_samples, nb_samples * sizeof(ftype)); + memcpy(&right_in[offset], right_samples, nb_samples * sizeof(ftype)); + memset(&left_out[offset], 0, overlap * sizeof(ftype)); + memset(&right_out[offset], 0, overlap * sizeof(ftype)); + + fn(apply_window)(s, left_in, windowed_left, 0); + fn(apply_window)(s, right_in, windowed_right, 0); + + s->tx_fn(s->tx_ctx[0], windowed_oleft, windowed_left, sizeof(ftype)); + s->tx_fn(s->tx_ctx[1], windowed_oright, windowed_right, sizeof(ftype)); + + fn(get_centere)((ctype *)windowed_oleft, + (ctype *)windowed_oright, + (ctype *)center, + s->fft_size / 2 + 1); + + vad = fn(calc_vad)(fn(flux)(center, center_prev, s->fft_size / 2 + 1), + fn(fluxlr)(windowed_oleft, windowed_pleft, + windowed_oright, windowed_pright, s->fft_size / 2 + 1), s->voice); + vad = vad * 0.1 + 0.9 * fn(s->prev_vad); + fn(s->prev_vad) = vad; + + memcpy(center_prev, center, s->fft_size * sizeof(ftype)); + memcpy(windowed_pleft, windowed_oleft, s->fft_size * sizeof(ftype)); + memcpy(windowed_pright, windowed_oright, s->fft_size * sizeof(ftype)); + + fn(get_final)(center, windowed_oleft, windowed_oright, vad, s->fft_size / 2 + 1, + s->original, s->enhance); + + s->itx_fn(s->itx_ctx, windowed_oleft, center, sizeof(ctype)); + + fn(apply_window)(s, windowed_oleft, left_out, 1); + + memcpy(left_osamples, left_in, overlap * sizeof(ftype)); + memcpy(right_osamples, right_in, overlap * sizeof(ftype)); + + if (ctx->is_disabled) + memset(center_osamples, 0, overlap * sizeof(ftype)); + else + memcpy(center_osamples, left_out, overlap * sizeof(ftype)); + + return 0; +} diff --git a/libavfilter/dnn/Makefile b/libavfilter/dnn/Makefile index 4cfbce0efc7..3d09927c98a 100644 --- a/libavfilter/dnn/Makefile +++ b/libavfilter/dnn/Makefile @@ -3,18 +3,9 @@ OBJS-$(CONFIG_DNN) += dnn/dnn_io_proc.o OBJS-$(CONFIG_DNN) += dnn/queue.o OBJS-$(CONFIG_DNN) += dnn/safe_queue.o OBJS-$(CONFIG_DNN) += dnn/dnn_backend_common.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layers.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_avgpool.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_dense.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_pad.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_conv2d.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_depth2space.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_maximum.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_mathbinary.o -OBJS-$(CONFIG_DNN) += dnn/dnn_backend_native_layer_mathunary.o DNN-OBJS-$(CONFIG_LIBTENSORFLOW) += dnn/dnn_backend_tf.o DNN-OBJS-$(CONFIG_LIBOPENVINO) += dnn/dnn_backend_openvino.o +DNN-OBJS-$(CONFIG_LIBTORCH) += dnn/dnn_backend_torch.o OBJS-$(CONFIG_DNN) += $(DNN-OBJS-yes) diff --git a/libavfilter/dnn/dnn_backend_common.c b/libavfilter/dnn/dnn_backend_common.c index 91a4a3c4bf4..632832ec366 100644 --- a/libavfilter/dnn/dnn_backend_common.c +++ b/libavfilter/dnn/dnn_backend_common.c @@ -43,13 +43,6 @@ int ff_check_exec_params(void *ctx, DNNBackendType backend, DNNFunctionType func return AVERROR(EINVAL); } - if (exec_params->nb_output != 1 && backend != DNN_TF) { - // currently, the filter does not need multiple outputs, - // so we just pending the support until we really need it. - avpriv_report_missing_feature(ctx, "multiple outputs"); - return AVERROR(ENOSYS); - } - return 0; } diff --git a/libavfilter/dnn/dnn_backend_native.c b/libavfilter/dnn/dnn_backend_native.c deleted file mode 100644 index b53799f04d2..00000000000 --- a/libavfilter/dnn/dnn_backend_native.c +++ /dev/null @@ -1,561 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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 - * DNN native backend implementation. - */ - -#include "dnn_backend_native.h" -#include "libavutil/avassert.h" -#include "dnn_backend_native_layer_conv2d.h" -#include "dnn_backend_native_layers.h" -#include "dnn_io_proc.h" -#include "dnn_backend_common.h" - -#define OFFSET(x) offsetof(NativeContext, x) -#define FLAGS AV_OPT_FLAG_FILTERING_PARAM -static const AVOption dnn_native_options[] = { - { "conv2d_threads", "threads num for conv2d layer", OFFSET(options.conv2d_threads), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS }, - { "async", "use DNN async inference", OFFSET(options.async), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, - { NULL }, -}; - -static const AVClass dnn_native_class = { - .class_name = "dnn_native", - .item_name = av_default_item_name, - .option = dnn_native_options, - .version = LIBAVUTIL_VERSION_INT, - .category = AV_CLASS_CATEGORY_FILTER, -}; - -static int execute_model_native(Queue *lltask_queue); - -static int extract_lltask_from_task(TaskItem *task, Queue *lltask_queue) -{ - NativeModel *native_model = task->model; - NativeContext *ctx = &native_model->ctx; - LastLevelTaskItem *lltask = av_malloc(sizeof(*lltask)); - - if (!lltask) { - av_log(ctx, AV_LOG_ERROR, "Unable to allocate space for LastLevelTaskItem\n"); - return AVERROR(ENOMEM); - } - task->inference_todo = 1; - task->inference_done = 0; - lltask->task = task; - - if (ff_queue_push_back(lltask_queue, lltask) < 0) { - av_log(ctx, AV_LOG_ERROR, "Failed to push back lltask_queue.\n"); - av_freep(&lltask); - return AVERROR(ENOMEM); - } - return 0; -} - -static int get_input_native(void *model, DNNData *input, const char *input_name) -{ - NativeModel *native_model = model; - NativeContext *ctx = &native_model->ctx; - - for (int i = 0; i < native_model->operands_num; ++i) { - DnnOperand *oprd = &native_model->operands[i]; - if (strcmp(oprd->name, input_name) == 0) { - if (oprd->type != DOT_INPUT) { - av_log(ctx, AV_LOG_ERROR, "Found \"%s\" in model, but it is not input node\n", input_name); - return AVERROR(EINVAL); - } - input->dt = oprd->data_type; - av_assert0(oprd->dims[0] == 1); - input->height = oprd->dims[1]; - input->width = oprd->dims[2]; - input->channels = oprd->dims[3]; - return 0; - } - } - - // do not find the input operand - av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", input_name); - return AVERROR(EINVAL); -} - -static int get_output_native(void *model, const char *input_name, int input_width, int input_height, - const char *output_name, int *output_width, int *output_height) -{ - int ret = 0; - NativeModel *native_model = model; - NativeContext *ctx = &native_model->ctx; - TaskItem task; - DNNExecBaseParams exec_params = { - .input_name = input_name, - .output_names = &output_name, - .nb_output = 1, - .in_frame = NULL, - .out_frame = NULL, - }; - - ret = ff_dnn_fill_gettingoutput_task(&task, &exec_params, native_model, input_height, input_width, ctx); - if (ret != 0) { - goto err; - } - - ret = extract_lltask_from_task(&task, native_model->lltask_queue); - if (ret != 0) { - av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n"); - goto err; - } - - ret = execute_model_native(native_model->lltask_queue); - *output_width = task.out_frame->width; - *output_height = task.out_frame->height; - -err: - av_frame_free(&task.out_frame); - av_frame_free(&task.in_frame); - return ret; -} - -// Loads model and its parameters that are stored in a binary file with following structure: -// layers_num,layer_type,layer_parameterss,layer_type,layer_parameters... -// For CONV layer: activation_function, input_num, output_num, kernel_size, kernel, biases -// For DEPTH_TO_SPACE layer: block_size -DNNModel *ff_dnn_load_model_native(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx) -{ -#define DNN_NATIVE_MAGIC "FFMPEGDNNNATIVE" - DNNModel *model = NULL; - // sizeof - 1 to skip the terminating '\0' which is not written in the file - char buf[sizeof(DNN_NATIVE_MAGIC) - 1]; - int version, header_size, major_version_expected = 1; - NativeModel *native_model = NULL; - AVIOContext *model_file_context; - int file_size, dnn_size, parsed_size; - int32_t layer; - DNNLayerType layer_type; - - if (avio_open(&model_file_context, model_filename, AVIO_FLAG_READ) < 0){ - return NULL; - } - file_size = avio_size(model_file_context); - - model = av_mallocz(sizeof(DNNModel)); - if (!model){ - goto fail; - } - - /** - * check file header with string and version - */ - if (avio_read(model_file_context, buf, sizeof(buf)) != sizeof(buf) || - memcmp(buf, DNN_NATIVE_MAGIC, sizeof(buf))) - goto fail; - dnn_size = sizeof(buf); - - version = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - if (version != major_version_expected) { - goto fail; - } - - // currently no need to check minor version - version = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - header_size = dnn_size; - - native_model = av_mallocz(sizeof(NativeModel)); - if (!native_model){ - goto fail; - } - model->model = native_model; - - native_model->ctx.class = &dnn_native_class; - model->options = options; - if (av_opt_set_from_string(&native_model->ctx, model->options, NULL, "=", "&") < 0) - goto fail; - native_model->model = model; - - if (native_model->ctx.options.async) { - av_log(&native_model->ctx, AV_LOG_WARNING, "Async not supported. Rolling back to sync\n"); - native_model->ctx.options.async = 0; - } - -#if !HAVE_PTHREAD_CANCEL - if (native_model->ctx.options.conv2d_threads > 1){ - av_log(&native_model->ctx, AV_LOG_WARNING, "'conv2d_threads' option was set but it is not supported " - "on this build (pthread support is required)\n"); - } -#endif - - avio_seek(model_file_context, file_size - 8, SEEK_SET); - native_model->layers_num = (int32_t)avio_rl32(model_file_context); - native_model->operands_num = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - avio_seek(model_file_context, header_size, SEEK_SET); - - native_model->layers = av_mallocz(native_model->layers_num * sizeof(Layer)); - if (!native_model->layers){ - goto fail; - } - - native_model->operands = av_mallocz(native_model->operands_num * sizeof(DnnOperand)); - if (!native_model->operands){ - goto fail; - } - - native_model->task_queue = ff_queue_create(); - if (!native_model->task_queue) { - goto fail; - } - - native_model->lltask_queue = ff_queue_create(); - if (!native_model->lltask_queue) { - goto fail; - } - - for (layer = 0; layer < native_model->layers_num; ++layer){ - layer_type = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - if (layer_type >= DLT_COUNT) { - goto fail; - } - - native_model->layers[layer].type = layer_type; - parsed_size = ff_layer_funcs[layer_type].pf_load(&native_model->layers[layer], model_file_context, file_size, native_model->operands_num); - if (!parsed_size) { - goto fail; - } - dnn_size += parsed_size; - } - - for (int32_t i = 0; i < native_model->operands_num; ++i){ - DnnOperand *oprd; - int32_t name_len; - int32_t operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - if (operand_index >= native_model->operands_num) { - goto fail; - } - - oprd = &native_model->operands[operand_index]; - name_len = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - avio_get_str(model_file_context, name_len, oprd->name, sizeof(oprd->name)); - dnn_size += name_len; - - oprd->type = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - oprd->data_type = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - for (int32_t dim = 0; dim < 4; ++dim) { - oprd->dims[dim] = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - } - if (oprd->type == DOT_INPUT && oprd->dims[0] != 1) - goto fail; - - oprd->isNHWC = 1; - } - - avio_closep(&model_file_context); - - if (dnn_size != file_size){ - ff_dnn_free_model_native(&model); - return NULL; - } - - model->get_input = &get_input_native; - model->get_output = &get_output_native; - model->filter_ctx = filter_ctx; - model->func_type = func_type; - - return model; - -fail: - ff_dnn_free_model_native(&model); - avio_closep(&model_file_context); - return NULL; -} - -static int execute_model_native(Queue *lltask_queue) -{ - NativeModel *native_model = NULL; - NativeContext *ctx = NULL; - int32_t layer; - DNNData input, output; - DnnOperand *oprd = NULL; - LastLevelTaskItem *lltask = NULL; - TaskItem *task = NULL; - int ret = 0; - - lltask = ff_queue_pop_front(lltask_queue); - if (!lltask) { - av_log(NULL, AV_LOG_ERROR, "Failed to get LastLevelTaskItem\n"); - ret = AVERROR(EINVAL); - goto err; - } - task = lltask->task; - native_model = task->model; - ctx = &native_model->ctx; - - if (native_model->layers_num <= 0 || native_model->operands_num <= 0) { - av_log(ctx, AV_LOG_ERROR, "No operands or layers in model\n"); - ret = AVERROR(EINVAL); - goto err; - } - - for (int i = 0; i < native_model->operands_num; ++i) { - oprd = &native_model->operands[i]; - if (strcmp(oprd->name, task->input_name) == 0) { - if (oprd->type != DOT_INPUT) { - av_log(ctx, AV_LOG_ERROR, "Found \"%s\" in model, but it is not input node\n", task->input_name); - ret = AVERROR(EINVAL); - goto err; - } - break; - } - oprd = NULL; - } - if (!oprd) { - av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model\n", task->input_name); - ret = AVERROR(EINVAL); - goto err; - } - - oprd->dims[1] = task->in_frame->height; - oprd->dims[2] = task->in_frame->width; - - av_freep(&oprd->data); - oprd->length = ff_calculate_operand_data_length(oprd); - if (oprd->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The input data length overflow\n"); - ret = AVERROR(EINVAL); - goto err; - } - oprd->data = av_malloc(oprd->length); - if (!oprd->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to malloc memory for input data\n"); - ret = AVERROR(ENOMEM); - goto err; - } - - input.height = oprd->dims[1]; - input.width = oprd->dims[2]; - input.channels = oprd->dims[3]; - input.data = oprd->data; - input.dt = oprd->data_type; - if (task->do_ioproc) { - if (native_model->model->frame_pre_proc != NULL) { - native_model->model->frame_pre_proc(task->in_frame, &input, native_model->model->filter_ctx); - } else { - ff_proc_from_frame_to_dnn(task->in_frame, &input, ctx); - } - } - - if (task->nb_output != 1) { - // currently, the filter does not need multiple outputs, - // so we just pending the support until we really need it. - avpriv_report_missing_feature(ctx, "multiple outputs"); - ret = AVERROR(ENOSYS); - goto err; - } - - for (layer = 0; layer < native_model->layers_num; ++layer){ - DNNLayerType layer_type = native_model->layers[layer].type; - ret = ff_layer_funcs[layer_type].pf_exec(native_model->operands, - native_model->layers[layer].input_operand_indexes, - native_model->layers[layer].output_operand_index, - native_model->layers[layer].params, - &native_model->ctx); - if (ret != 0) { - av_log(ctx, AV_LOG_ERROR, "Failed to execute model\n"); - goto err; - } - } - - for (uint32_t i = 0; i < task->nb_output; ++i) { - DnnOperand *oprd = NULL; - const char *output_name = task->output_names[i]; - for (int j = 0; j < native_model->operands_num; ++j) { - if (strcmp(native_model->operands[j].name, output_name) == 0) { - oprd = &native_model->operands[j]; - break; - } - } - - if (oprd == NULL) { - av_log(ctx, AV_LOG_ERROR, "Could not find output in model\n"); - ret = AVERROR(EINVAL); - goto err; - } - - output.data = oprd->data; - output.height = oprd->dims[1]; - output.width = oprd->dims[2]; - output.channels = oprd->dims[3]; - output.dt = oprd->data_type; - - if (task->do_ioproc) { - if (native_model->model->frame_post_proc != NULL) { - native_model->model->frame_post_proc(task->out_frame, &output, native_model->model->filter_ctx); - } else { - ff_proc_from_dnn_to_frame(task->out_frame, &output, ctx); - } - } else { - task->out_frame->width = output.width; - task->out_frame->height = output.height; - } - } - task->inference_done++; -err: - av_freep(&lltask); - return ret; -} - -int ff_dnn_execute_model_native(const DNNModel *model, DNNExecBaseParams *exec_params) -{ - NativeModel *native_model = model->model; - NativeContext *ctx = &native_model->ctx; - TaskItem *task; - int ret = 0; - - ret = ff_check_exec_params(ctx, DNN_NATIVE, model->func_type, exec_params); - if (ret != 0) { - return ret; - } - - task = av_malloc(sizeof(*task)); - if (!task) { - av_log(ctx, AV_LOG_ERROR, "unable to alloc memory for task item.\n"); - return AVERROR(ENOMEM); - } - - ret = ff_dnn_fill_task(task, exec_params, native_model, ctx->options.async, 1); - if (ret != 0) { - av_freep(&task); - return ret; - } - - if (ff_queue_push_back(native_model->task_queue, task) < 0) { - av_freep(&task); - av_log(ctx, AV_LOG_ERROR, "unable to push back task_queue.\n"); - return AVERROR(ENOMEM); - } - - ret = extract_lltask_from_task(task, native_model->lltask_queue); - if (ret != 0) { - av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n"); - return ret; - } - - return execute_model_native(native_model->lltask_queue); -} - -int ff_dnn_flush_native(const DNNModel *model) -{ - NativeModel *native_model = model->model; - - if (ff_queue_size(native_model->lltask_queue) == 0) { - // no pending task need to flush - return 0; - } - - // for now, use sync node with flush operation - // Switch to async when it is supported - return execute_model_native(native_model->lltask_queue); -} - -DNNAsyncStatusType ff_dnn_get_result_native(const DNNModel *model, AVFrame **in, AVFrame **out) -{ - NativeModel *native_model = model->model; - return ff_dnn_get_result_common(native_model->task_queue, in, out); -} - -int32_t ff_calculate_operand_dims_count(const DnnOperand *oprd) -{ - int32_t result = 1; - for (int i = 0; i < 4; ++i) - result *= oprd->dims[i]; - - return result; -} - -int32_t ff_calculate_operand_data_length(const DnnOperand* oprd) -{ - // currently, we just support DNN_FLOAT - uint64_t len = sizeof(float); - for (int i = 0; i < 4; i++) { - len *= oprd->dims[i]; - if (len > INT32_MAX) - return 0; - } - return len; -} - -void ff_dnn_free_model_native(DNNModel **model) -{ - NativeModel *native_model; - ConvolutionalParams *conv_params; - int32_t layer; - - if (*model) - { - if ((*model)->model) { - native_model = (*model)->model; - if (native_model->layers) { - for (layer = 0; layer < native_model->layers_num; ++layer){ - if (native_model->layers[layer].type == DLT_CONV2D){ - conv_params = (ConvolutionalParams *)native_model->layers[layer].params; - av_freep(&conv_params->kernel); - av_freep(&conv_params->biases); - } - av_freep(&native_model->layers[layer].params); - } - av_freep(&native_model->layers); - } - - if (native_model->operands) { - for (uint32_t operand = 0; operand < native_model->operands_num; ++operand) - av_freep(&native_model->operands[operand].data); - av_freep(&native_model->operands); - } - - while (ff_queue_size(native_model->lltask_queue) != 0) { - LastLevelTaskItem *item = ff_queue_pop_front(native_model->lltask_queue); - av_freep(&item); - } - ff_queue_destroy(native_model->lltask_queue); - - while (ff_queue_size(native_model->task_queue) != 0) { - TaskItem *item = ff_queue_pop_front(native_model->task_queue); - av_frame_free(&item->in_frame); - av_frame_free(&item->out_frame); - av_freep(&item); - } - ff_queue_destroy(native_model->task_queue); - - av_freep(&native_model); - } - av_freep(model); - } -} diff --git a/libavfilter/dnn/dnn_backend_native.h b/libavfilter/dnn/dnn_backend_native.h deleted file mode 100644 index 75bd9a44f77..00000000000 --- a/libavfilter/dnn/dnn_backend_native.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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 - * DNN inference functions interface for native backend. - */ - - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_H - -#include "../dnn_interface.h" -#include "libavformat/avio.h" -#include "libavutil/opt.h" -#include "queue.h" - -/** - * the enum value of DNNLayerType should not be changed, - * the same values are used in convert_from_tensorflow.py - * and, it is used to index the layer execution/load function pointer. - */ -typedef enum { - DLT_INPUT = 0, - DLT_CONV2D = 1, - DLT_DEPTH_TO_SPACE = 2, - DLT_MIRROR_PAD = 3, - DLT_MAXIMUM = 4, - DLT_MATH_BINARY = 5, - DLT_MATH_UNARY = 6, - DLT_AVG_POOL = 7, - DLT_DENSE = 8, - DLT_COUNT -} DNNLayerType; - -typedef enum {DOT_INPUT = 1, DOT_OUTPUT = 2, DOT_INTERMEDIATE = DOT_INPUT | DOT_OUTPUT} DNNOperandType; -typedef enum {VALID, SAME, SAME_CLAMP_TO_EDGE} DNNPaddingParam; -typedef enum {RELU, TANH, SIGMOID, NONE, LEAKY_RELU} DNNActivationFunc; - -typedef struct Layer{ - DNNLayerType type; - /** - * a layer can have multiple inputs and one output. - * 4 is just a big enough number for input operands (increase it if necessary), - * do not use 'int32_t *input_operand_indexes', so we don't worry about mem leaks. - */ - int32_t input_operand_indexes[4]; - int32_t output_operand_index; - void *params; -} Layer; - -typedef struct DnnOperand{ - /** - * there are two memory layouts, NHWC or NCHW, so we use dims, - * dims[0] is Number. - */ - int32_t dims[4]; - - /** - * input/output/intermediate operand of the network - */ - DNNOperandType type; - - /** - * support different kinds of data type such as float, half float, int8 etc, - * first support float now. - */ - DNNDataType data_type; - - /** - * NHWC if 1, otherwise NCHW. - * let's first support NHWC only, this flag is for extensive usage. - */ - int8_t isNHWC; - - /** - * to avoid possible memory leak, do not use char *name - */ - char name[128]; - - /** - * data pointer with data length in bytes. - * usedNumbersLeft is only valid for intermediate operand, - * it means how many layers still depend on this operand, - * todo: the memory can be reused when usedNumbersLeft is zero. - */ - void *data; - int32_t length; - int32_t usedNumbersLeft; -}DnnOperand; - -typedef struct InputParams{ - int height, width, channels; -} InputParams; - -typedef struct NativeOptions{ - uint8_t async; - uint32_t conv2d_threads; -} NativeOptions; - -typedef struct NativeContext { - const AVClass *class; - NativeOptions options; -} NativeContext; - -// Represents simple feed-forward convolutional network. -typedef struct NativeModel{ - NativeContext ctx; - DNNModel *model; - Layer *layers; - int32_t layers_num; - DnnOperand *operands; - int32_t operands_num; - Queue *task_queue; - Queue *lltask_queue; -} NativeModel; - -DNNModel *ff_dnn_load_model_native(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx); - -int ff_dnn_execute_model_native(const DNNModel *model, DNNExecBaseParams *exec_params); - -DNNAsyncStatusType ff_dnn_get_result_native(const DNNModel *model, AVFrame **in, AVFrame **out); - -int ff_dnn_flush_native(const DNNModel *model); - -void ff_dnn_free_model_native(DNNModel **model); - -// NOTE: User must check for error (return value <= 0) to handle -// case like integer overflow. -int32_t ff_calculate_operand_data_length(const DnnOperand *oprd); -int32_t ff_calculate_operand_dims_count(const DnnOperand *oprd); -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_avgpool.c b/libavfilter/dnn/dnn_backend_native_layer_avgpool.c deleted file mode 100644 index d6fcac8a355..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_avgpool.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - * DNN native backend implementation. - */ - -#include "libavutil/avassert.h" -#include "dnn_backend_native_layer_avgpool.h" - -int ff_dnn_load_layer_avg_pool(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - AvgPoolParams *avgpool_params; - int dnn_size = 0; - avgpool_params = av_malloc(sizeof(*avgpool_params)); - if(!avgpool_params) - return 0; - - avgpool_params->strides = (int32_t)avio_rl32(model_file_context); - avgpool_params->padding_method = (int32_t)avio_rl32(model_file_context); - avgpool_params->kernel_size = (int32_t)avio_rl32(model_file_context); - dnn_size += 12; - - if (dnn_size > file_size || avgpool_params->kernel_size <= 0 || avgpool_params->strides <=0){ - av_freep(&avgpool_params); - return 0; - } - - layer->params = avgpool_params; - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - return dnn_size; -} - -int ff_dnn_execute_layer_avg_pool(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - float *output; - int height_end, width_end, height_radius, width_radius, output_height, output_width, kernel_area; - int32_t input_operand_index = input_operand_indexes[0]; - int number = operands[input_operand_index].dims[0]; - int height = operands[input_operand_index].dims[1]; - int width = operands[input_operand_index].dims[2]; - int channel = operands[input_operand_index].dims[3]; - const float *input = operands[input_operand_index].data; - const AvgPoolParams *avgpool_params = parameters; - - int kernel_strides = avgpool_params->strides; - int src_linesize = width * channel; - DnnOperand *output_operand = &operands[output_operand_index]; - - /** - * When padding_method = SAME, the tensorflow will only padding the hald number of 0 pixels - * except the remainders. - * Eg: assuming the input height = 1080, the strides = 11, so the remainders = 1080 % 11 = 2 - * and if ksize = 5: it will fill (5 - 2) >> 1 = 1 line before the first line of input image, - * and 5 - 2 - 1 = 2 lines after the last line of input image. - * and if ksize = 7: it will fill (7 - 2) >> 1 = 2 lines before the first line of input image, - * and 7 - 2 - 2 = 3 lines after the last line of input image. - */ - if (avgpool_params->padding_method == SAME) { - height_end = height; - width_end = width; - height_radius = avgpool_params->kernel_size - ((height - 1) % kernel_strides + 1); - width_radius = avgpool_params->kernel_size - ((width - 1) % kernel_strides + 1); - height_radius = height_radius < 0 ? 0 : height_radius >> 1; - width_radius = width_radius < 0 ? 0 : width_radius >> 1; - output_height = ceil(height / (kernel_strides * 1.0)); - output_width = ceil(width / (kernel_strides * 1.0)); - } else { - av_assert0(avgpool_params->padding_method == VALID); - height_end = height - avgpool_params->kernel_size + 1; - width_end = width - avgpool_params->kernel_size + 1; - height_radius = 0; - width_radius = 0; - output_height = ceil((height - avgpool_params->kernel_size + 1) / (kernel_strides * 1.0)); - output_width = ceil((width - avgpool_params->kernel_size + 1) / (kernel_strides * 1.0)); - } - - output_operand->dims[0] = number; - output_operand->dims[1] = output_height; - output_operand->dims[2] = output_width; - // not support pooling in channel dimension now - output_operand->dims[3] = channel; - output_operand->data_type = operands[input_operand_index].data_type; - output_operand->length = ff_calculate_operand_data_length(output_operand); - if (output_operand->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output_operand->data = av_realloc(output_operand->data, output_operand->length); - if (!output_operand->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - output = output_operand->data; - - for (int y = 0; y < height_end; y += kernel_strides) { - for (int x = 0; x < width_end; x += kernel_strides) { - for (int n_channel = 0; n_channel < channel; ++n_channel) { - output[n_channel] = 0.0; - kernel_area = 0; - for (int kernel_y = 0; kernel_y < avgpool_params->kernel_size; ++kernel_y) { - for (int kernel_x = 0; kernel_x < avgpool_params->kernel_size; ++kernel_x) { - float input_pel; - int y_pos = y + (kernel_y - height_radius); - int x_pos = x + (kernel_x - width_radius); - if (x_pos < 0 || x_pos >= width || y_pos < 0 || y_pos >= height) { - input_pel = 0.0; - } else { - kernel_area++; - input_pel = input[y_pos * src_linesize + x_pos * channel + n_channel]; - } - output[n_channel] += input_pel; - } - } - output[n_channel] /= kernel_area; - } - output += channel; - } - } - - return 0; -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_avgpool.h b/libavfilter/dnn/dnn_backend_native_layer_avgpool.h deleted file mode 100644 index 118a160090f..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_avgpool.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - * DNN inference functions interface for native backend. - */ - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_AVGPOOL_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_AVGPOOL_H - -#include "dnn_backend_native.h" - -typedef struct AvgPoolParams{ - int32_t strides, kernel_size; - DNNPaddingParam padding_method; -} AvgPoolParams; - -/** - * @brief Load Average Pooling Layer. - * - * It assigns the Average Pooling layer with AvgPoolParams - * after parsing from the model file context. - * - * @param layer pointer to the DNN layer instance - * @param model_file_context pointer to model file context - * @param file_size model file size to check if data is read - * correctly from the model file - * @param operands_num operand count of the whole model to - * check if data is read correctly from the model file - * @return number of bytes read from the model file - * @retval 0 if out of memory or an error occurs - */ -int ff_dnn_load_layer_avg_pool(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); - -/** - * @brief Execute the Average Pooling Layer. - * Padding in channel dimensions is currently not supported. - * - * @param operands all operands for the model - * @param input_operand_indexes input operand indexes for this layer - * @param output_operand_index output operand index for this layer - * @param parameters average pooling parameters - * @param ctx pointer to Native model context for logging - * @retval 0 if the execution succeeds - * @retval AVERROR(ENOMEM) if memory allocation fails - * @retval AVERROR(EINVAL) for invalid arguments - */ -int ff_dnn_execute_layer_avg_pool(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); - -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_conv2d.c b/libavfilter/dnn/dnn_backend_native_layer_conv2d.c deleted file mode 100644 index 2ac37d8855d..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_conv2d.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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/avassert.h" -#include "libavutil/thread.h" -#include "libavutil/cpu.h" -#include "dnn_backend_native_layer_conv2d.h" - -#define CLAMP_TO_EDGE(x, w) ((x) < 0 ? 0 : ((x) >= (w) ? (w - 1) : (x))) - -//struct to pass parameters -typedef struct ThreadCommonParam{ - DnnOperand *operands; - const int32_t *input_operand_indexes; - int32_t output_operand_index; - const void *parameters; - NativeContext *ctx; - float *output_data; -} ThreadCommonParam; - -typedef struct ThreadParam{ - ThreadCommonParam *thread_common_param; - int thread_start, thread_end; -#if HAVE_PTHREAD_CANCEL - pthread_t thread; -#endif -} ThreadParam; - -int ff_dnn_load_layer_conv2d(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - ConvolutionalParams *conv_params; - int kernel_size; - int dnn_size = 0; - conv_params = av_malloc(sizeof(*conv_params)); - if (!conv_params) - return 0; - - conv_params->dilation = (int32_t)avio_rl32(model_file_context); - conv_params->padding_method = (int32_t)avio_rl32(model_file_context); - conv_params->activation = (int32_t)avio_rl32(model_file_context); - conv_params->input_num = (int32_t)avio_rl32(model_file_context); - conv_params->output_num = (int32_t)avio_rl32(model_file_context); - conv_params->kernel_size = (int32_t)avio_rl32(model_file_context); - conv_params->has_bias = (int32_t)avio_rl32(model_file_context); - dnn_size += 28; - - kernel_size = conv_params->input_num * conv_params->output_num * - conv_params->kernel_size * conv_params->kernel_size; - dnn_size += kernel_size * 4; - if (conv_params->has_bias) - dnn_size += conv_params->output_num * 4; - - if (dnn_size > file_size || conv_params->input_num <= 0 || - conv_params->output_num <= 0 || conv_params->kernel_size <= 0){ - av_freep(&conv_params); - return 0; - } - - conv_params->kernel = av_malloc_array(kernel_size, sizeof(*conv_params->kernel)); - if (!conv_params->kernel) { - av_freep(&conv_params); - return 0; - } - for (int i = 0; i < kernel_size; ++i) { - conv_params->kernel[i] = av_int2float(avio_rl32(model_file_context)); - } - - conv_params->biases = NULL; - if (conv_params->has_bias) { - conv_params->biases = av_malloc_array(conv_params->output_num, sizeof(*conv_params->biases)); - if (!conv_params->biases){ - av_freep(&conv_params->kernel); - av_freep(&conv_params); - return 0; - } - for (int i = 0; i < conv_params->output_num; ++i){ - conv_params->biases[i] = av_int2float(avio_rl32(model_file_context)); - } - } - - layer->params = conv_params; - - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - - return dnn_size; -} - -static void * dnn_execute_layer_conv2d_thread(void *threadarg) -{ - //pass parameters - ThreadParam *thread_param = threadarg; - ThreadCommonParam *thread_common_param = thread_param->thread_common_param; - DnnOperand *operands = thread_common_param->operands; - int32_t input_operand_index = thread_common_param->input_operand_indexes[0]; - int height = operands[input_operand_index].dims[1]; - int width = operands[input_operand_index].dims[2]; - int channel = operands[input_operand_index].dims[3]; - const float *input = operands[input_operand_index].data; - const ConvolutionalParams *conv_params = thread_common_param->parameters; - - int radius = conv_params->kernel_size >> 1; - int src_linesize = width * conv_params->input_num; - int filter_linesize = conv_params->kernel_size * conv_params->input_num; - int filter_size = conv_params->kernel_size * filter_linesize; - int pad_size = (conv_params->padding_method == VALID) ? (conv_params->kernel_size - 1) / 2 * conv_params->dilation : 0; - - float *output = thread_common_param->output_data; - output += (conv_params->output_num) * (width - 2 * pad_size) * (thread_param->thread_start - pad_size); - - av_assert0(channel == conv_params->input_num); - - for (int y = thread_param->thread_start; y < thread_param->thread_end; ++y) { - for (int x = pad_size; x < width - pad_size; ++x) { - for (int n_filter = 0; n_filter < conv_params->output_num; ++n_filter) { - if (conv_params->has_bias) - output[n_filter] = conv_params->biases[n_filter]; - else - output[n_filter] = 0.f; - - for (int ch = 0; ch < conv_params->input_num; ++ch) { - for (int kernel_y = 0; kernel_y < conv_params->kernel_size; ++kernel_y) { - for (int kernel_x = 0; kernel_x < conv_params->kernel_size; ++kernel_x) { - float input_pel; - if (conv_params->padding_method == SAME_CLAMP_TO_EDGE) { - int y_pos = CLAMP_TO_EDGE(y + (kernel_y - radius) * conv_params->dilation, height); - int x_pos = CLAMP_TO_EDGE(x + (kernel_x - radius) * conv_params->dilation, width); - input_pel = input[y_pos * src_linesize + x_pos * conv_params->input_num + ch]; - } else { - int y_pos = y + (kernel_y - radius) * conv_params->dilation; - int x_pos = x + (kernel_x - radius) * conv_params->dilation; - input_pel = (x_pos < 0 || x_pos >= width || y_pos < 0 || y_pos >= height) ? 0.0 : - input[y_pos * src_linesize + x_pos * conv_params->input_num + ch]; - } - - - output[n_filter] += input_pel * conv_params->kernel[n_filter * filter_size + kernel_y * filter_linesize + - kernel_x * conv_params->input_num + ch]; - } - } - } - switch (conv_params->activation){ - case RELU: - output[n_filter] = FFMAX(output[n_filter], 0.0); - break; - case TANH: - output[n_filter] = 2.0f / (1.0f + exp(-2.0f * output[n_filter])) - 1.0f; - break; - case SIGMOID: - output[n_filter] = 1.0f / (1.0f + exp(-output[n_filter])); - break; - case NONE: - break; - case LEAKY_RELU: - output[n_filter] = FFMAX(output[n_filter], 0.0) + 0.2 * FFMIN(output[n_filter], 0.0); - } - } - output += conv_params->output_num; - } - } - return NULL; -} - - -int ff_dnn_execute_layer_conv2d(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ -#if HAVE_PTHREAD_CANCEL - int thread_num = (ctx->options.conv2d_threads <= 0 || ctx->options.conv2d_threads > av_cpu_count()) - ? (av_cpu_count() + 1) : (ctx->options.conv2d_threads); - int ret = 0, thread_stride; - ThreadParam *thread_param; -#else - ThreadParam thread_param = { 0 }; -#endif - ThreadCommonParam thread_common_param; - const ConvolutionalParams *conv_params = parameters; - int height = operands[input_operand_indexes[0]].dims[1]; - int width = operands[input_operand_indexes[0]].dims[2]; - int pad_size = (conv_params->padding_method == VALID) ? (conv_params->kernel_size - 1) / 2 * conv_params->dilation : 0; - DnnOperand *output_operand = &operands[output_operand_index]; - void *tmp; - - output_operand->dims[0] = operands[input_operand_indexes[0]].dims[0]; - output_operand->dims[1] = height - pad_size * 2; - output_operand->dims[2] = width - pad_size * 2; - output_operand->dims[3] = conv_params->output_num; - output_operand->data_type = operands[input_operand_indexes[0]].data_type; - output_operand->length = ff_calculate_operand_data_length(output_operand); - if (output_operand->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - tmp = av_realloc(output_operand->data, output_operand->length); - if (!tmp) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - output_operand->data = tmp; - thread_common_param.output_data = output_operand->data; - thread_common_param.operands = operands; - thread_common_param.input_operand_indexes = input_operand_indexes; - thread_common_param.output_operand_index = output_operand_index; - thread_common_param.parameters = parameters; - thread_common_param.ctx = ctx; - -#if HAVE_PTHREAD_CANCEL - thread_param = av_malloc_array(thread_num, sizeof(*thread_param)); - if (!thread_param) - return AVERROR(ENOMEM); - thread_stride = (height - pad_size * 2) / thread_num; - //create threads - for (int i = 0; i < thread_num; i++){ - int thread_ret = 0; - thread_param[i].thread_common_param = &thread_common_param; - thread_param[i].thread_start = thread_stride * i + pad_size; - thread_param[i].thread_end = (i == thread_num - 1) ? (height - pad_size) : (thread_param[i].thread_start + thread_stride); - thread_ret = pthread_create(&thread_param[i].thread, NULL, - dnn_execute_layer_conv2d_thread, &thread_param[i]); - if (thread_ret) { - thread_num = i; - ret = AVERROR(thread_ret); - break; - } - } - - for (int i = 0; i < thread_num; i++){ - pthread_join(thread_param[i].thread, NULL); - } - - //release memory - av_freep(&thread_param); - - return ret; -#else - thread_param.thread_common_param = &thread_common_param; - thread_param.thread_start = pad_size; - thread_param.thread_end = height - pad_size; - dnn_execute_layer_conv2d_thread(&thread_param); - - return 0; -#endif -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_conv2d.h b/libavfilter/dnn/dnn_backend_native_layer_conv2d.h deleted file mode 100644 index f754a9ba185..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_conv2d.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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 - */ - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_CONV2D_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_CONV2D_H - -#include "dnn_backend_native.h" - - -typedef struct ConvolutionalParams{ - int32_t input_num, output_num, kernel_size; - DNNActivationFunc activation; - DNNPaddingParam padding_method; - int32_t dilation; - int32_t has_bias; - float *kernel; - float *biases; -} ConvolutionalParams; - -/** - * @brief Load the 2D Convolution Layer. - * - * It assigns the 2D convolution layer with ConvolutionalParams - * after parsing from the model file context. - * - * @param layer pointer to the DNN layer instance - * @param model_file_context pointer to model file context - * @param file_size model file size to check if data is read - * correctly from the model file - * @param operands_num operand count of the whole model to - * check if data is read correctly from the model file - * @return number of bytes read from the model file - * @retval 0 if out of memory or an error occurs - */ -int ff_dnn_load_layer_conv2d(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); - -/** - * @brief Execute the 2D Convolution Layer. - * - * @param operands all operands for the model - * @param input_operand_indexes input operand indexes for this layer - * @param output_operand_index output operand index for this layer - * @param parameters convolution parameters - * @param ctx pointer to Native model context for logging - * @retval 0 if the execution succeeds - * @retval AVERROR(ENOMEM) if memory allocation fails - * @retval AVERROR(EINVAL) for invalid arguments - */ -int ff_dnn_execute_layer_conv2d(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_dense.c b/libavfilter/dnn/dnn_backend_native_layer_dense.c deleted file mode 100644 index dff342c1f35..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_dense.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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/avassert.h" -#include "dnn_backend_native_layer_dense.h" - -int ff_dnn_load_layer_dense(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - DenseParams *dense_params; - int kernel_size; - int dnn_size = 0; - dense_params = av_malloc(sizeof(*dense_params)); - if (!dense_params) - return 0; - - dense_params->activation = (int32_t)avio_rl32(model_file_context); - dense_params->input_num = (int32_t)avio_rl32(model_file_context); - dense_params->output_num = (int32_t)avio_rl32(model_file_context); - dense_params->has_bias = (int32_t)avio_rl32(model_file_context); - dnn_size += 16; - - kernel_size = dense_params->input_num * dense_params->output_num; - dnn_size += kernel_size * 4; - if (dense_params->has_bias) - dnn_size += dense_params->output_num * 4; - - if (dnn_size > file_size || dense_params->input_num <= 0 || - dense_params->output_num <= 0){ - av_freep(&dense_params); - return 0; - } - - dense_params->kernel = av_malloc(kernel_size * sizeof(float)); - if (!dense_params->kernel) { - av_freep(&dense_params); - return 0; - } - for (int i = 0; i < kernel_size; ++i) { - dense_params->kernel[i] = av_int2float(avio_rl32(model_file_context)); - } - - dense_params->biases = NULL; - if (dense_params->has_bias) { - dense_params->biases = av_malloc(dense_params->output_num * sizeof(float)); - if (!dense_params->biases){ - av_freep(&dense_params->kernel); - av_freep(&dense_params); - return 0; - } - for (int i = 0; i < dense_params->output_num; ++i){ - dense_params->biases[i] = av_int2float(avio_rl32(model_file_context)); - } - } - - layer->params = dense_params; - - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - - return dnn_size; -} - -int ff_dnn_execute_layer_dense(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - float *output; - int32_t input_operand_index = input_operand_indexes[0]; - int number = operands[input_operand_index].dims[0]; - int height = operands[input_operand_index].dims[1]; - int width = operands[input_operand_index].dims[2]; - int channel = operands[input_operand_index].dims[3]; - const float *input = operands[input_operand_index].data; - const DenseParams *dense_params = parameters; - - int src_linesize = width * channel; - DnnOperand *output_operand = &operands[output_operand_index]; - output_operand->dims[0] = number; - output_operand->dims[1] = height; - output_operand->dims[2] = width; - output_operand->dims[3] = dense_params->output_num; - output_operand->data_type = operands[input_operand_index].data_type; - output_operand->length = ff_calculate_operand_data_length(output_operand); - if (output_operand->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output_operand->data = av_realloc(output_operand->data, output_operand->length); - if (!output_operand->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - output = output_operand->data; - - av_assert0(channel == dense_params->input_num); - - for (int y = 0; y < height; ++y) { - for (int x = 0; x < width; ++x) { - for (int n_filter = 0; n_filter < dense_params->output_num; ++n_filter) { - if (dense_params->has_bias) - output[n_filter] = dense_params->biases[n_filter]; - else - output[n_filter] = 0.f; - - for (int ch = 0; ch < dense_params->input_num; ++ch) { - float input_pel; - input_pel = input[y * src_linesize + x * dense_params->input_num + ch]; - output[n_filter] += input_pel * dense_params->kernel[n_filter*dense_params->input_num + ch]; - } - switch (dense_params->activation){ - case RELU: - output[n_filter] = FFMAX(output[n_filter], 0.0); - break; - case TANH: - output[n_filter] = 2.0f / (1.0f + exp(-2.0f * output[n_filter])) - 1.0f; - break; - case SIGMOID: - output[n_filter] = 1.0f / (1.0f + exp(-output[n_filter])); - break; - case NONE: - break; - case LEAKY_RELU: - output[n_filter] = FFMAX(output[n_filter], 0.0) + 0.2 * FFMIN(output[n_filter], 0.0); - } - } - output += dense_params->output_num; - } - } - return 0; -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_dense.h b/libavfilter/dnn/dnn_backend_native_layer_dense.h deleted file mode 100644 index 607fc3e684b..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_dense.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - */ - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_DENSE_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_DENSE_H - -#include "dnn_backend_native.h" - -typedef struct DenseParams{ - int32_t input_num, output_num; - DNNActivationFunc activation; - int32_t has_bias; - float *kernel; - float *biases; -} DenseParams; - -/** - * @brief Load the Densely-Connected Layer. - * - * It assigns the densely connected layer with DenseParams - * after parsing from the model file context. - * - * @param layer pointer to the DNN layer instance - * @param model_file_context pointer to model file context - * @param file_size model file size to check if data is read - * correctly from the model file - * @param operands_num operand count of the whole model to - * check if data is read correctly from the model file - * @return number of bytes read from the model file - * @retval 0 if out of memory or an error occurs - */ -int ff_dnn_load_layer_dense(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); - -/** - * @brief Execute the Densely-Connected Layer. - * - * @param operands all operands for the model - * @param input_operand_indexes input operand indexes for this layer - * @param output_operand_index output operand index for this layer - * @param parameters dense layer parameters - * @param ctx pointer to Native model context for logging - * @retval 0 if the execution succeeds - * @retval AVERROR(ENOMEM) if memory allocation fails - * @retval AVERROR(EINVAL) for invalid arguments - */ -int ff_dnn_execute_layer_dense(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_depth2space.c b/libavfilter/dnn/dnn_backend_native_layer_depth2space.c deleted file mode 100644 index 358ac3bcaa2..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_depth2space.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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 - * DNN native backend implementation. - */ - -#include "dnn_backend_native.h" -#include "dnn_backend_native_layer_depth2space.h" - -int ff_dnn_load_layer_depth2space(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - DepthToSpaceParams *params; - int dnn_size = 0; - params = av_malloc(sizeof(*params)); - if (!params) - return 0; - - params->block_size = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - layer->params = params; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - - return dnn_size; -} - -int ff_dnn_execute_layer_depth2space(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - float *output; - const DepthToSpaceParams *params = parameters; - int block_size = params->block_size; - int32_t input_operand_index = input_operand_indexes[0]; - int number = operands[input_operand_index].dims[0]; - int height = operands[input_operand_index].dims[1]; - int width = operands[input_operand_index].dims[2]; - int channels = operands[input_operand_index].dims[3]; - const float *input = operands[input_operand_index].data; - - int y, x, by, bx, ch; - int new_channels = channels / (block_size * block_size); - int output_linesize = width * channels; - int by_linesize = output_linesize / block_size; - int x_linesize = new_channels * block_size; - - DnnOperand *output_operand = &operands[output_operand_index]; - output_operand->dims[0] = number; - output_operand->dims[1] = height * block_size; - output_operand->dims[2] = width * block_size; - output_operand->dims[3] = new_channels; - output_operand->data_type = operands[input_operand_index].data_type; - output_operand->length = ff_calculate_operand_data_length(output_operand); - if (output_operand->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output_operand->data = av_realloc(output_operand->data, output_operand->length); - if (!output_operand->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - output = output_operand->data; - - for (y = 0; y < height; ++y){ - for (x = 0; x < width; ++x){ - for (by = 0; by < block_size; ++by){ - for (bx = 0; bx < block_size; ++bx){ - for (ch = 0; ch < new_channels; ++ch){ - output[by * by_linesize + x * x_linesize + bx * new_channels + ch] = input[ch]; - } - input += new_channels; - } - } - } - output += output_linesize; - } - return 0; -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_depth2space.h b/libavfilter/dnn/dnn_backend_native_layer_depth2space.h deleted file mode 100644 index aaf2df4c13e..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_depth2space.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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 - * DNN inference functions interface for native backend. - */ - - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_DEPTH2SPACE_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_DEPTH2SPACE_H - -#include "../dnn_interface.h" -#include "libavformat/avio.h" - -typedef struct DepthToSpaceParams{ - int block_size; -} DepthToSpaceParams; - -/** - * @brief Load the Depth to Space Layer. - * - * It assigns the depth to space layer with DepthToSpaceParams - * after parsing from the model file context. - * - * @param layer pointer to the DNN layer instance - * @param model_file_context pointer to model file context - * @param file_size model file size to check if data is read - * correctly from the model file - * @param operands_num operand count of the whole model to - * check if data is read correctly from the model file - * @return number of bytes read from the model file - * @retval 0 if an error occurs or out of memory - */ -int ff_dnn_load_layer_depth2space(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); - -/** - * @brief Execute the Depth to Space Layer. - * - * It rearranges the input data from depth into spatial - * form by applying Depth to Space transformation. - * - * @param operands all operands for the model - * @param input_operand_indexes input operand indexes for this layer - * @param output_operand_index output operand index for this layer - * @param parameters depth to space layer parameters - * @param ctx pointer to Native model context for logging - * @retval 0 if the execution succeeds - * @retval AVERROR(ENOMEM) if memory allocation fails - * @retval AVERROR(EINVAL) for invalid arguments - */ -int ff_dnn_execute_layer_depth2space(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); - -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_mathbinary.c b/libavfilter/dnn/dnn_backend_native_layer_mathbinary.c deleted file mode 100644 index 1a3fa3f132f..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_mathbinary.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - * DNN native backend implementation. - */ - -#include "dnn_backend_native.h" -#include "dnn_backend_native_layer_mathbinary.h" - -typedef float (*FunType)(float src0, float src1); - -static float sub(float src0, float src1) -{ - return src0 - src1; -} -static float add(float src0, float src1) -{ - return src0 + src1; -} -static float mul(float src0, float src1) -{ - return src0 * src1; -} -static float realdiv(float src0, float src1) -{ - return src0 / src1; -} -static float minimum(float src0, float src1) -{ - return FFMIN(src0, src1); -} -static float floormod(float src0, float src1) -{ - return (float)((int)(src0) % (int)(src1)); -} - -static void math_binary_commutative(FunType pfun, const DnnLayerMathBinaryParams *params, const DnnOperand *input, DnnOperand *output, DnnOperand *operands, const int32_t *input_operand_indexes) -{ - int dims_count; - const float *src; - float *dst; - dims_count = ff_calculate_operand_dims_count(output); - src = input->data; - dst = output->data; - if (params->input0_broadcast || params->input1_broadcast) { - for (int i = 0; i < dims_count; ++i) { - dst[i] = pfun(params->v, src[i]); - } - } else { - const DnnOperand *input1 = &operands[input_operand_indexes[1]]; - const float *src1 = input1->data; - for (int i = 0; i < dims_count; ++i) { - dst[i] = pfun(src[i], src1[i]); - } - } -} -static void math_binary_not_commutative(FunType pfun, const DnnLayerMathBinaryParams *params, const DnnOperand *input, DnnOperand *output, DnnOperand *operands, const int32_t *input_operand_indexes) -{ - int dims_count; - const float *src; - float *dst; - dims_count = ff_calculate_operand_dims_count(output); - src = input->data; - dst = output->data; - if (params->input0_broadcast) { - for (int i = 0; i < dims_count; ++i) { - dst[i] = pfun(params->v, src[i]); - } - } else if (params->input1_broadcast) { - for (int i = 0; i < dims_count; ++i) { - dst[i] = pfun(src[i], params->v); - } - } else { - const DnnOperand *input1 = &operands[input_operand_indexes[1]]; - const float *src1 = input1->data; - for (int i = 0; i < dims_count; ++i) { - dst[i] = pfun(src[i], src1[i]); - } - } -} -int ff_dnn_load_layer_math_binary(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - DnnLayerMathBinaryParams params = { 0 }; - int dnn_size = 0; - int input_index = 0; - - params.bin_op = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - params.input0_broadcast = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - if (params.input0_broadcast) { - params.v = av_int2float(avio_rl32(model_file_context)); - } else { - layer->input_operand_indexes[input_index] = (int32_t)avio_rl32(model_file_context); - if (layer->input_operand_indexes[input_index] >= operands_num) { - return 0; - } - input_index++; - } - dnn_size += 4; - - params.input1_broadcast = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - if (params.input1_broadcast) { - params.v = av_int2float(avio_rl32(model_file_context)); - } else { - layer->input_operand_indexes[input_index] = (int32_t)avio_rl32(model_file_context); - if (layer->input_operand_indexes[input_index] >= operands_num) { - return 0; - } - input_index++; - } - dnn_size += 4; - - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - - if (layer->output_operand_index >= operands_num) { - return 0; - } - layer->params = av_memdup(¶ms, sizeof(params)); - if (!layer->params) - return 0; - - return dnn_size; -} - -int ff_dnn_execute_layer_math_binary(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - const DnnOperand *input = &operands[input_operand_indexes[0]]; - DnnOperand *output = &operands[output_operand_index]; - const DnnLayerMathBinaryParams *params = parameters; - - for (int i = 0; i < 4; ++i) - output->dims[i] = input->dims[i]; - - output->data_type = input->data_type; - output->length = ff_calculate_operand_data_length(output); - if (output->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output->data = av_realloc(output->data, output->length); - if (!output->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - - switch (params->bin_op) { - case DMBO_SUB: - math_binary_not_commutative(sub, params, input, output, operands, input_operand_indexes); - return 0; - case DMBO_ADD: - math_binary_commutative(add, params, input, output, operands, input_operand_indexes); - return 0; - case DMBO_MUL: - math_binary_commutative(mul, params, input, output, operands, input_operand_indexes); - return 0; - case DMBO_REALDIV: - math_binary_not_commutative(realdiv, params, input, output, operands, input_operand_indexes); - return 0; - case DMBO_MINIMUM: - math_binary_commutative(minimum, params, input, output, operands, input_operand_indexes); - return 0; - case DMBO_FLOORMOD: - math_binary_not_commutative(floormod, params, input, output, operands, input_operand_indexes); - return 0; - default: - av_log(ctx, AV_LOG_ERROR, "Unmatch math binary operator\n"); - return AVERROR(EINVAL); - } -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_mathbinary.h b/libavfilter/dnn/dnn_backend_native_layer_mathbinary.h deleted file mode 100644 index eee294b00f9..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_mathbinary.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - * DNN inference functions interface for native backend. - */ - - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_MATHBINARY_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_MATHBINARY_H - -#include "libavformat/avio.h" -#include "dnn_backend_native.h" - -typedef enum { - DMBO_SUB = 0, - DMBO_ADD = 1, - DMBO_MUL = 2, - DMBO_REALDIV = 3, - DMBO_MINIMUM = 4, - DMBO_FLOORMOD = 5, - DMBO_COUNT -} DNNMathBinaryOperation; - -typedef struct DnnLayerMathBinaryParams{ - DNNMathBinaryOperation bin_op; - int input0_broadcast; - int input1_broadcast; - float v; -} DnnLayerMathBinaryParams; - -int ff_dnn_load_layer_math_binary(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); -int ff_dnn_execute_layer_math_binary(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); - -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_mathunary.c b/libavfilter/dnn/dnn_backend_native_layer_mathunary.c deleted file mode 100644 index e3c5106e5e4..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_mathunary.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - * DNN native backend implementation. - */ - -#include - -#include "dnn_backend_native.h" -#include "dnn_backend_native_layer_mathunary.h" - -int ff_dnn_load_layer_math_unary(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - DnnLayerMathUnaryParams *params; - int dnn_size = 0; - params = av_malloc(sizeof(*params)); - if(!params) - return 0; - - params->un_op = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - layer->params = params; - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - - return dnn_size; - -} - -int ff_dnn_execute_layer_math_unary(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - const DnnOperand *input = &operands[input_operand_indexes[0]]; - DnnOperand *output = &operands[output_operand_index]; - const DnnLayerMathUnaryParams *params = parameters; - int dims_count; - const float *src; - float *dst; - - for (int i = 0; i < 4; ++i) - output->dims[i] = input->dims[i]; - - output->data_type = input->data_type; - output->length = ff_calculate_operand_data_length(output); - if (output->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output->data = av_realloc(output->data, output->length); - if (!output->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - - dims_count = ff_calculate_operand_dims_count(output); - src = input->data; - dst = output->data; - - switch (params->un_op) { - case DMUO_ABS: - for (int i = 0; i < dims_count; ++i) - dst[i] = FFABS(src[i]); - return 0; - case DMUO_SIN: - for (int i = 0; i < dims_count; ++i) - dst[i] = sin(src[i]); - return 0; - case DMUO_COS: - for (int i = 0; i < dims_count; ++i) - dst[i] = cos(src[i]); - return 0; - case DMUO_TAN: - for (int i = 0; i < dims_count; ++i) - dst[i] = tan(src[i]); - return 0; - case DMUO_ASIN: - for (int i = 0; i < dims_count; ++i) - dst[i] = asin(src[i]); - return 0; - case DMUO_ACOS: - for (int i = 0; i < dims_count; ++i) - dst[i] = acos(src[i]); - return 0; - case DMUO_ATAN: - for (int i = 0; i < dims_count; ++i) - dst[i] = atan(src[i]); - return 0; - case DMUO_SINH: - for (int i = 0; i < dims_count; ++i) - dst[i] = sinh(src[i]); - return 0; - case DMUO_COSH: - for (int i = 0; i < dims_count; ++i) - dst[i] = cosh(src[i]); - return 0; - case DMUO_TANH: - for (int i = 0; i < dims_count; ++i) - dst[i] = tanh(src[i]); - return 0; - case DMUO_ASINH: - for (int i = 0; i < dims_count; ++i) - dst[i] = asinh(src[i]); - return 0; - case DMUO_ACOSH: - for (int i = 0; i < dims_count; ++i) - dst[i] = acosh(src[i]); - return 0; - case DMUO_ATANH: - for (int i = 0; i < dims_count; ++i) - dst[i] = atanh(src[i]); - return 0; - case DMUO_CEIL: - for (int i = 0; i < dims_count; ++i) - dst[i] = ceil(src[i]); - return 0; - case DMUO_FLOOR: - for (int i = 0; i < dims_count; ++i) - dst[i] = floor(src[i]); - return 0; - case DMUO_ROUND: - for (int i = 0; i < dims_count; ++i) - dst[i] = round(src[i]); - return 0; - case DMUO_EXP: - for (int i = 0; i < dims_count; ++i) - dst[i] = exp(src[i]); - return 0; - default: - av_log(ctx, AV_LOG_ERROR, "Unmatch math unary operator\n"); - return AVERROR(EINVAL); - } -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_mathunary.h b/libavfilter/dnn/dnn_backend_native_layer_mathunary.h deleted file mode 100644 index 806e73b29fa..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_mathunary.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 - * DNN inference functions interface for native backend. - */ - -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_MATHUNARY_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_MATHUNARY_H - -#include "libavformat/avio.h" -#include "dnn_backend_native.h" - -typedef enum { - DMUO_ABS = 0, - DMUO_SIN = 1, - DMUO_COS = 2, - DMUO_TAN = 3, - DMUO_ASIN = 4, - DMUO_ACOS = 5, - DMUO_ATAN = 6, - DMUO_SINH = 7, - DMUO_COSH = 8, - DMUO_TANH = 9, - DMUO_ASINH = 10, - DMUO_ACOSH = 11, - DMUO_ATANH = 12, - DMUO_CEIL = 13, - DMUO_FLOOR = 14, - DMUO_ROUND = 15, - DMUO_EXP = 16, - DMUO_COUNT -} DNNMathUnaryOperation; - -typedef struct DnnLayerMathUnaryParams{ - DNNMathUnaryOperation un_op; -} DnnLayerMathUnaryParams; - -/** - * @brief Load the Unary Math Layer. - * - * It assigns the unary math layer with DnnLayerMathUnaryParams - * after parsing from the model file context. - * - * @param layer pointer to the DNN layer instance - * @param model_file_context pointer to model file context - * @param file_size model file size to check if data is read - * correctly from the model file - * @param operands_num operand count of the whole model to - * check if data is read correctly from the model file - * @return number of bytes read from the model file - * @retval 0 if out of memory or an error occurs - */ -int ff_dnn_load_layer_math_unary(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); - -/** - * @brief Execute the Unary Math Layer. - * - * It applies the unary operator parsed while - * loading to the given input operands. - * - * @param operands all operands for the model - * @param input_operand_indexes input operand indexes for this layer - * @param output_operand_index output operand index for this layer - * @param parameters unary math layer parameters - * @param ctx pointer to Native model context for logging - * @retval 0 if the execution succeeds - * @retval AVERROR(ENOMEM) if memory allocation fails - * @retval AVERROR(EINVAL) for invalid arguments - */ -int ff_dnn_execute_layer_math_unary(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); - -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layer_maximum.c b/libavfilter/dnn/dnn_backend_native_layer_maximum.c deleted file mode 100644 index 667efaa3b80..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_maximum.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 - * DNN native backend implementation. - */ - -#include "dnn_backend_native.h" -#include "dnn_backend_native_layer_maximum.h" - -int ff_dnn_load_layer_maximum(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - DnnLayerMaximumParams *params; - int dnn_size = 0; - params = av_malloc(sizeof(*params)); - if (!params) - return 0; - - params->val.u32 = avio_rl32(model_file_context); - dnn_size += 4; - layer->params = params; - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - - return dnn_size; -} - -int ff_dnn_execute_layer_maximum(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - const DnnOperand *input = &operands[input_operand_indexes[0]]; - DnnOperand *output = &operands[output_operand_index]; - const DnnLayerMaximumParams *params = parameters; - int dims_count; - const float *src; - float *dst; - - for (int i = 0; i < 4; ++i) - output->dims[i] = input->dims[i]; - - output->data_type = input->data_type; - output->length = ff_calculate_operand_data_length(output); - if (output->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output->data = av_realloc(output->data, output->length); - if (!output->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - - dims_count = ff_calculate_operand_dims_count(output); - src = input->data; - dst = output->data; - for (int i = 0; i < dims_count; ++i) - dst[i] = FFMAX(src[i], params->val.y); - - return 0; -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_pad.c b/libavfilter/dnn/dnn_backend_native_layer_pad.c deleted file mode 100644 index e274fe12c60..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_pad.c +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 -#include "libavutil/avassert.h" -#include "dnn_backend_native_layer_pad.h" - -int ff_dnn_load_layer_pad(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num) -{ - LayerPadParams *params; - int dnn_size = 0; - params = av_malloc(sizeof(*params)); - if (!params) - return 0; - - params->mode = (int32_t)avio_rl32(model_file_context); - dnn_size += 4; - for (int i = 0; i < 4; ++i) { - params->paddings[i][0] = avio_rl32(model_file_context); - params->paddings[i][1] = avio_rl32(model_file_context); - dnn_size += 8; - } - layer->input_operand_indexes[0] = (int32_t)avio_rl32(model_file_context); - layer->output_operand_index = (int32_t)avio_rl32(model_file_context); - dnn_size += 8; - layer->params = params; - - if (layer->input_operand_indexes[0] >= operands_num || layer->output_operand_index >= operands_num) { - return 0; - } - - return dnn_size; -} - -static int before_get_buddy(int given, int paddings, LayerPadModeParam mode) -{ - if (mode == LPMP_SYMMETRIC) { - return (2 * paddings - 1 - given); - } else if (mode == LPMP_REFLECT) { - return (2 * paddings - given); - } else { - av_assert0(!"should not reach here"); - return 0; - } -} - -static int after_get_buddy(int given, int border, LayerPadModeParam mode) -{ - if (mode == LPMP_SYMMETRIC) { - int offset = given - border; - return (border - 1 - offset); - } else if (mode == LPMP_REFLECT) { - int offset = given - border; - return (border - 2 - offset); - } else { - av_assert0(!"should not reach here"); - return 0; - } -} - -int ff_dnn_execute_layer_pad(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx) -{ - int32_t before_paddings; - int32_t after_paddings; - float* output; - const LayerPadParams *params = parameters; - - // suppose format is - int32_t input_operand_index = input_operand_indexes[0]; - int number = operands[input_operand_index].dims[0]; - int height = operands[input_operand_index].dims[1]; - int width = operands[input_operand_index].dims[2]; - int channel = operands[input_operand_index].dims[3]; - const float *input = operands[input_operand_index].data; - - int new_number = number + params->paddings[0][0] + params->paddings[0][1]; - int new_height = height + params->paddings[1][0] + params->paddings[1][1]; - int new_width = width + params->paddings[2][0] + params->paddings[2][1]; - int new_channel = channel + params->paddings[3][0] + params->paddings[3][1]; - - int c_stride = channel; - int wc_stride = c_stride * width; - int hwc_stride = wc_stride * height; - - int new_c_stride = new_channel; - int new_wc_stride = new_c_stride * new_width; - int new_hwc_stride = new_wc_stride * new_height; - - DnnOperand *output_operand = &operands[output_operand_index]; - output_operand->dims[0] = new_number; - output_operand->dims[1] = new_height; - output_operand->dims[2] = new_width; - output_operand->dims[3] = new_channel; - output_operand->data_type = operands[input_operand_index].data_type; - output_operand->length = ff_calculate_operand_data_length(output_operand); - if (output_operand->length <= 0) { - av_log(ctx, AV_LOG_ERROR, "The output data length overflow\n"); - return AVERROR(EINVAL); - } - output_operand->data = av_realloc(output_operand->data, output_operand->length); - if (!output_operand->data) { - av_log(ctx, AV_LOG_ERROR, "Failed to reallocate memory for output\n"); - return AVERROR(ENOMEM); - } - output = output_operand->data; - - // copy the original data - for (int n = 0; n < number; n++) { - for (int h = 0; h < height; h++) { - for (int w = 0; w < width; w++) { - const float *src = input + n * hwc_stride + h * wc_stride + w * c_stride; - float *dst = output + (n + params->paddings[0][0]) * new_hwc_stride - + (h + params->paddings[1][0]) * new_wc_stride - + (w + params->paddings[2][0]) * new_c_stride - + params->paddings[3][0]; - memcpy(dst, src, channel * sizeof(float)); - } - } - } - - // handle the first dimension - before_paddings = params->paddings[0][0]; - after_paddings = params->paddings[0][1]; - for (int n = 0; n < before_paddings; n++) { - float *dst = output + n * new_hwc_stride; - if (params->mode == LPMP_CONSTANT) { - for (int i = 0; i < new_hwc_stride; i++) { - dst[i] = params->constant_values; - } - } - else { - int buddy = before_get_buddy(n, before_paddings, params->mode); - float *src = output + buddy * new_hwc_stride; - memcpy(dst, src, new_hwc_stride * sizeof(float)); - } - } - for (int n = 0; n < after_paddings; n++) { - int given = number + before_paddings + n; - float *dst = output + given * new_hwc_stride; - if (params->mode == LPMP_CONSTANT) { - for (int i = 0; i < new_hwc_stride; i++) { - dst[i] = params->constant_values; - } - } else { - int buddy = after_get_buddy(given, number + before_paddings, params->mode); - float *src = output + buddy * new_hwc_stride; - memcpy(dst, src, new_hwc_stride * sizeof(float)); - } - } - - // handle the second dimension - before_paddings = params->paddings[1][0]; - after_paddings = params->paddings[1][1]; - for (int n = 0; n < new_number; n++) { - float *start = output + n * new_hwc_stride; - for (int h = 0; h < before_paddings; h++) { - float *dst = start + h * new_wc_stride; - if (params->mode == LPMP_CONSTANT) { - for (int i = 0; i < new_wc_stride; i++) { - dst[i] = params->constant_values; - } - } else { - int buddy = before_get_buddy(h, before_paddings, params->mode); - float *src = start + buddy * new_wc_stride; - memcpy(dst, src, new_wc_stride * sizeof(float)); - } - } - for (int h = 0; h < after_paddings; h++) { - int given = height + before_paddings + h; - float *dst = start + given * new_wc_stride; - if (params->mode == LPMP_CONSTANT) { - for (int i = 0; i < new_wc_stride; i++) { - dst[i] = params->constant_values; - } - } else { - int buddy = after_get_buddy(given, height + before_paddings, params->mode); - float *src = start + buddy * new_wc_stride; - memcpy(dst, src, new_wc_stride * sizeof(float)); - } - } - } - - // handle the third dimension - before_paddings = params->paddings[2][0]; - after_paddings = params->paddings[2][1]; - for (int n = 0; n < new_number; n++) { - for (int h = 0; h < new_height; h++) { - float *start = output + n * new_hwc_stride + h * new_wc_stride; - for (int w = 0; w < before_paddings; w++) { - float *dst = start + w * new_c_stride; - if (params->mode == LPMP_CONSTANT) { - for (int i = 0; i < new_c_stride; i++) { - dst[i] = params->constant_values; - } - } else { - int buddy = before_get_buddy(w, before_paddings, params->mode); - float *src = start + buddy * new_c_stride; - memcpy(dst, src, new_c_stride * sizeof(float)); - } - } - for (int w = 0; w < after_paddings; w++) { - int given = width + before_paddings + w; - float *dst = start + given * new_c_stride; - if (params->mode == LPMP_CONSTANT) { - for (int i = 0; i < new_c_stride; i++) { - dst[i] = params->constant_values; - } - } else { - int buddy = after_get_buddy(given, width + before_paddings, params->mode); - float *src = start + buddy * new_c_stride; - memcpy(dst, src, new_c_stride * sizeof(float)); - } - } - } - } - - // handle the fourth dimension - before_paddings = params->paddings[3][0]; - after_paddings = params->paddings[3][1]; - for (int n = 0; n < new_number; n++) { - for (int h = 0; h < new_height; h++) { - for (int w = 0; w < new_width; w++) { - float *start = output + n * new_hwc_stride + h * new_wc_stride + w * new_c_stride; - for (int c = 0; c < before_paddings; c++) { - float *dst = start + c; - if (params->mode == LPMP_CONSTANT) { - *dst = params->constant_values; - } else { - int buddy = before_get_buddy(c, before_paddings, params->mode); - float *src = start + buddy; - *dst = *src; - } - } - for (int c = 0; c < after_paddings; c++) { - int given = channel + before_paddings + c; - float *dst = start + given; - if (params->mode == LPMP_CONSTANT) { - *dst = params->constant_values; - } else { - int buddy = after_get_buddy(given, channel + before_paddings, params->mode); - float *src = start + buddy; - *dst = *src; - } - } - } - } - } - - return 0; -} diff --git a/libavfilter/dnn/dnn_backend_native_layer_pad.h b/libavfilter/dnn/dnn_backend_native_layer_pad.h deleted file mode 100644 index 4f76c67c3f6..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layer_pad.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 - * layer pad (equivalent to tf.pad) for native backend. - */ -#ifndef AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_PAD_H -#define AVFILTER_DNN_DNN_BACKEND_NATIVE_LAYER_PAD_H - -#include -#include "dnn_backend_native.h" - -typedef enum {LPMP_CONSTANT, LPMP_REFLECT, LPMP_SYMMETRIC} LayerPadModeParam; - -typedef struct LayerPadParams{ - int32_t paddings[4][2]; - LayerPadModeParam mode; - float constant_values; -} LayerPadParams; - -int ff_dnn_load_layer_pad(Layer *layer, AVIOContext *model_file_context, int file_size, int operands_num); -int ff_dnn_execute_layer_pad(DnnOperand *operands, const int32_t *input_operand_indexes, - int32_t output_operand_index, const void *parameters, NativeContext *ctx); - -#endif diff --git a/libavfilter/dnn/dnn_backend_native_layers.c b/libavfilter/dnn/dnn_backend_native_layers.c deleted file mode 100644 index 492939fd366..00000000000 --- a/libavfilter/dnn/dnn_backend_native_layers.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 -#include "dnn_backend_native_layers.h" -#include "dnn_backend_native_layer_pad.h" -#include "dnn_backend_native_layer_conv2d.h" -#include "dnn_backend_native_layer_depth2space.h" -#include "dnn_backend_native_layer_maximum.h" -#include "dnn_backend_native_layer_mathbinary.h" -#include "dnn_backend_native_layer_mathunary.h" -#include "dnn_backend_native_layer_avgpool.h" -#include "dnn_backend_native_layer_dense.h" - -const LayerFunc ff_layer_funcs[DLT_COUNT] = { - {NULL, NULL}, - {ff_dnn_execute_layer_conv2d, ff_dnn_load_layer_conv2d}, - {ff_dnn_execute_layer_depth2space, ff_dnn_load_layer_depth2space}, - {ff_dnn_execute_layer_pad, ff_dnn_load_layer_pad}, - {ff_dnn_execute_layer_maximum, ff_dnn_load_layer_maximum}, - {ff_dnn_execute_layer_math_binary, ff_dnn_load_layer_math_binary}, - {ff_dnn_execute_layer_math_unary, ff_dnn_load_layer_math_unary}, - {ff_dnn_execute_layer_avg_pool, ff_dnn_load_layer_avg_pool}, - {ff_dnn_execute_layer_dense, ff_dnn_load_layer_dense}, -}; diff --git a/libavfilter/dnn/dnn_backend_openvino.c b/libavfilter/dnn/dnn_backend_openvino.c index b67f2883364..375643377f7 100644 --- a/libavfilter/dnn/dnn_backend_openvino.c +++ b/libavfilter/dnn/dnn_backend_openvino.c @@ -23,7 +23,6 @@ * DNN OpenVINO backend implementation. */ -#include "dnn_backend_openvino.h" #include "dnn_io_proc.h" #include "libavformat/avio.h" #include "libavutil/avassert.h" @@ -33,7 +32,11 @@ #include "libavutil/detection_bbox.h" #include "../internal.h" #include "safe_queue.h" +#if HAVE_OPENVINO2 +#include +#else #include +#endif #include "dnn_backend_common.h" typedef struct OVOptions{ @@ -42,6 +45,9 @@ typedef struct OVOptions{ uint8_t async; int batch_size; int input_resizable; + DNNLayout layout; + float scale; + float mean; } OVOptions; typedef struct OVContext { @@ -52,22 +58,39 @@ typedef struct OVContext { typedef struct OVModel{ OVContext ctx; DNNModel *model; +#if HAVE_OPENVINO2 + ov_core_t *core; + ov_model_t *ov_model; + ov_compiled_model_t *compiled_model; + ov_output_const_port_t* input_port; + ov_preprocess_input_info_t* input_info; + ov_output_const_port_t** output_ports; + ov_preprocess_output_info_t* output_info; + ov_preprocess_prepostprocessor_t* preprocess; +#else ie_core_t *core; ie_network_t *network; ie_executable_network_t *exe_network; + const char *all_input_names; + const char *all_output_names; +#endif SafeQueue *request_queue; // holds OVRequestItem Queue *task_queue; // holds TaskItem Queue *lltask_queue; // holds LastLevelTaskItem - const char *all_input_names; - const char *all_output_names; + int nb_outputs; } OVModel; // one request for one call to openvino typedef struct OVRequestItem { - ie_infer_request_t *infer_request; LastLevelTaskItem **lltasks; uint32_t lltask_count; +#if HAVE_OPENVINO2 + ov_infer_request_t *infer_request; + ov_callback_t callback; +#else ie_complete_call_back_t callback; + ie_infer_request_t *infer_request; +#endif } OVRequestItem; #define APPEND_STRING(generated_string, iterate_string) \ @@ -81,16 +104,72 @@ static const AVOption dnn_openvino_options[] = { DNN_BACKEND_COMMON_OPTIONS { "batch_size", "batch size per request", OFFSET(options.batch_size), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 1000, FLAGS}, { "input_resizable", "can input be resizable or not", OFFSET(options.input_resizable), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, + { "layout", "input layout of model", OFFSET(options.layout), AV_OPT_TYPE_INT, { .i64 = DL_NONE}, DL_NONE, DL_NHWC, FLAGS, .unit = "layout" }, + { "none", "none", 0, AV_OPT_TYPE_CONST, { .i64 = DL_NONE }, 0, 0, FLAGS, .unit = "layout"}, + { "nchw", "nchw", 0, AV_OPT_TYPE_CONST, { .i64 = DL_NCHW }, 0, 0, FLAGS, .unit = "layout"}, + { "nhwc", "nhwc", 0, AV_OPT_TYPE_CONST, { .i64 = DL_NHWC }, 0, 0, FLAGS, .unit = "layout"}, + { "scale", "Add scale preprocess operation. Divide each element of input by specified value.", OFFSET(options.scale), AV_OPT_TYPE_FLOAT, { .dbl = 0 }, INT_MIN, INT_MAX, FLAGS}, + { "mean", "Add mean preprocess operation. Subtract specified value from each element of input.", OFFSET(options.mean), AV_OPT_TYPE_FLOAT, { .dbl = 0 }, INT_MIN, INT_MAX, FLAGS}, { NULL } }; AVFILTER_DEFINE_CLASS(dnn_openvino); +#if HAVE_OPENVINO2 +static const struct { + ov_status_e status; + int av_err; + const char *desc; +} ov2_errors[] = { + { OK, 0, "success" }, + { GENERAL_ERROR, AVERROR_EXTERNAL, "general error" }, + { NOT_IMPLEMENTED, AVERROR(ENOSYS), "not implemented" }, + { NETWORK_NOT_LOADED, AVERROR_EXTERNAL, "network not loaded" }, + { PARAMETER_MISMATCH, AVERROR(EINVAL), "parameter mismatch" }, + { NOT_FOUND, AVERROR_EXTERNAL, "not found" }, + { OUT_OF_BOUNDS, AVERROR(EOVERFLOW), "out of bounds" }, + { UNEXPECTED, AVERROR_EXTERNAL, "unexpected" }, + { REQUEST_BUSY, AVERROR(EBUSY), "request busy" }, + { RESULT_NOT_READY, AVERROR(EBUSY), "result not ready" }, + { NOT_ALLOCATED, AVERROR(ENODATA), "not allocated" }, + { INFER_NOT_STARTED, AVERROR_EXTERNAL, "infer not started" }, + { NETWORK_NOT_READ, AVERROR_EXTERNAL, "network not read" }, + { INFER_CANCELLED, AVERROR(ECANCELED), "infer cancelled" }, + { INVALID_C_PARAM, AVERROR(EINVAL), "invalid C parameter" }, + { UNKNOWN_C_ERROR, AVERROR_UNKNOWN, "unknown C error" }, + { NOT_IMPLEMENT_C_METHOD, AVERROR(ENOSYS), "not implement C method" }, + { UNKNOW_EXCEPTION, AVERROR_UNKNOWN, "unknown exception" }, +}; + +static int ov2_map_error(ov_status_e status, const char **desc) +{ + int i; + for (i = 0; i < FF_ARRAY_ELEMS(ov2_errors); i++) { + if (ov2_errors[i].status == status) { + if (desc) + *desc = ov2_errors[i].desc; + return ov2_errors[i].av_err; + } + } + if (desc) + *desc = "unknown error"; + return AVERROR_UNKNOWN; +} +#endif + +#if HAVE_OPENVINO2 +static DNNDataType precision_to_datatype(ov_element_type_e precision) +#else static DNNDataType precision_to_datatype(precision_e precision) +#endif { switch (precision) { +#if HAVE_OPENVINO2 + case F32: +#else case FP32: +#endif return DNN_FLOAT; case U8: return DNN_UINT8; @@ -116,20 +195,69 @@ static int get_datatype_size(DNNDataType dt) static int fill_model_input_ov(OVModel *ov_model, OVRequestItem *request) { + DNNData input; + LastLevelTaskItem *lltask; + TaskItem *task; + OVContext *ctx = &ov_model->ctx; +#if HAVE_OPENVINO2 + int64_t* dims; + ov_status_e status; + ov_tensor_t* tensor = NULL; + ov_shape_t input_shape = {0}; + ov_element_type_e precision; + char *port_name; +#else dimensions_t dims; precision_e precision; ie_blob_buffer_t blob_buffer; - OVContext *ctx = &ov_model->ctx; IEStatusCode status; - DNNData input; ie_blob_t *input_blob = NULL; - LastLevelTaskItem *lltask; - TaskItem *task; +#endif + memset(&input, 0, sizeof(input)); lltask = ff_queue_peek_front(ov_model->lltask_queue); av_assert0(lltask); task = lltask->task; +#if HAVE_OPENVINO2 + if (ov_model->input_port) { + ov_output_const_port_free(ov_model->input_port); + ov_model->input_port = NULL; + } + if (task->input_name) + status = ov_model_const_input_by_name(ov_model->ov_model, task->input_name, &ov_model->input_port); + else + status = ov_model_const_input(ov_model->ov_model, &ov_model->input_port); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get input port shape.\n"); + return ov2_map_error(status, NULL); + } + status = ov_port_get_any_name(ov_model->input_port, &port_name); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get input port name.\n"); + return ov2_map_error(status, NULL); + } + av_log(ctx, AV_LOG_VERBOSE, "OpenVINO model input: %s\n", port_name); + ov_free(port_name); + port_name = NULL; + + status = ov_const_port_get_shape(ov_model->input_port, &input_shape); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get input port shape.\n"); + return ov2_map_error(status, NULL); + } + dims = input_shape.dims; + status = ov_port_get_element_type(ov_model->input_port, &precision); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get input port data type.\n"); + ov_shape_free(&input_shape); + return ov2_map_error(status, NULL); + } + for (int i = 0; i < input_shape.rank; i++) + input.dims[i] = dims[i]; + input.layout = DL_NHWC; + input.dt = precision_to_datatype(precision); +#else status = ie_infer_request_get_blob(request->infer_request, task->input_name, &input_blob); if (status != OK) { av_log(ctx, AV_LOG_ERROR, "Failed to get input blob with name %s\n", task->input_name); @@ -150,15 +278,18 @@ static int fill_model_input_ov(OVModel *ov_model, OVRequestItem *request) av_log(ctx, AV_LOG_ERROR, "Failed to get input blob buffer\n"); return DNN_GENERIC_ERROR; } - - input.height = dims.dims[2]; - input.width = dims.dims[3]; - input.channels = dims.dims[1]; + for (int i = 0; i < input_shape.rank; i++) + input.dims[i] = dims[i]; + input.layout = DL_NCHW; input.data = blob_buffer.buffer; input.dt = precision_to_datatype(precision); +#endif // all models in openvino open model zoo use BGR as input, // change to be an option when necessary. input.order = DCO_BGR; + // We use preprocess_steps to scale input data, so disable scale and mean here. + input.scale = 1; + input.mean = 0; for (int i = 0; i < ctx->options.batch_size; ++i) { lltask = ff_queue_pop_front(ov_model->lltask_queue); @@ -168,6 +299,26 @@ static int fill_model_input_ov(OVModel *ov_model, OVRequestItem *request) request->lltasks[i] = lltask; request->lltask_count = i + 1; task = lltask->task; +#if HAVE_OPENVINO2 + if (tensor) + ov_tensor_free(tensor); + status = ov_tensor_create(precision, input_shape, &tensor); + ov_shape_free(&input_shape); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to create tensor from host prt.\n"); + return ov2_map_error(status, NULL); + } + status = ov_tensor_data(tensor, &input.data); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get input data.\n"); + return ov2_map_error(status, NULL); + } + status = ov_infer_request_set_input_tensor(request->infer_request, tensor); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to Set an input tensor for the model.\n"); + return ov2_map_error(status, NULL); + } +#endif switch (ov_model->model->func_type) { case DFT_PROCESS_FRAME: if (task->do_ioproc) { @@ -188,29 +339,90 @@ static int fill_model_input_ov(OVModel *ov_model, OVRequestItem *request) av_assert0(!"should not reach here"); break; } - input.data = (uint8_t *)input.data - + input.width * input.height * input.channels * get_datatype_size(input.dt); + input.data = (uint8_t *)input.data + + input.dims[1] * input.dims[2] * input.dims[3] * get_datatype_size(input.dt); } +#if HAVE_OPENVINO2 + ov_tensor_free(tensor); +#else ie_blob_free(&input_blob); +#endif return 0; } static void infer_completion_callback(void *args) { - dimensions_t dims; - precision_e precision; - IEStatusCode status; OVRequestItem *request = args; LastLevelTaskItem *lltask = request->lltasks[0]; TaskItem *task = lltask->task; OVModel *ov_model = task->model; SafeQueue *requestq = ov_model->request_queue; + DNNData *outputs; + OVContext *ctx = &ov_model->ctx; +#if HAVE_OPENVINO2 + size_t* dims; + ov_status_e status; + ov_tensor_t *output_tensor; + ov_shape_t output_shape = {0}; + ov_element_type_e precision; + + outputs = av_calloc(ov_model->nb_outputs, sizeof(*outputs)); + if (!outputs) { + av_log(ctx, AV_LOG_ERROR, "Failed to alloc outputs."); + return; + } + + for (int i = 0; i < ov_model->nb_outputs; i++) { + status = ov_infer_request_get_tensor_by_const_port(request->infer_request, + ov_model->output_ports[i], + &output_tensor); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, + "Failed to get output tensor."); + goto end; + } + + status = ov_tensor_data(output_tensor, &outputs[i].data); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, + "Failed to get output data."); + goto end; + } + + status = ov_tensor_get_shape(output_tensor, &output_shape); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get output port shape.\n"); + goto end; + } + dims = output_shape.dims; + + status = ov_port_get_element_type(ov_model->output_ports[i], &precision); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get output port data type.\n"); + goto end; + } + outputs[i].dt = precision_to_datatype(precision); + outputs[i].layout = DL_NCHW; + outputs[i].dims[0] = 1; + outputs[i].dims[1] = output_shape.rank > 2 ? dims[output_shape.rank - 3] : 1; + outputs[i].dims[2] = output_shape.rank > 1 ? dims[output_shape.rank - 2] : 1; + outputs[i].dims[3] = output_shape.rank > 0 ? dims[output_shape.rank - 1] : 1; + av_assert0(request->lltask_count <= dims[0]); + outputs[i].layout = ctx->options.layout; + outputs[i].scale = ctx->options.scale; + outputs[i].mean = ctx->options.mean; + ov_shape_free(&output_shape); + ov_tensor_free(output_tensor); + output_tensor = NULL; + } +#else + IEStatusCode status; + dimensions_t dims; ie_blob_t *output_blob = NULL; ie_blob_buffer_t blob_buffer; + precision_e precision; DNNData output; - OVContext *ctx = &ov_model->ctx; - status = ie_infer_request_get_blob(request->infer_request, task->output_names[0], &output_blob); if (status != OK) { av_log(ctx, AV_LOG_ERROR, @@ -233,14 +445,18 @@ static void infer_completion_callback(void *args) av_log(ctx, AV_LOG_ERROR, "Failed to get dims or precision of output\n"); return; } - - output.channels = dims.dims[1]; - output.height = dims.dims[2]; - output.width = dims.dims[3]; - output.dt = precision_to_datatype(precision); output.data = blob_buffer.buffer; - + output.layout = DL_NCHW; + for (int i = 0; i < 4; i++) + output.dims[i] = dims.dims[i]; av_assert0(request->lltask_count <= dims.dims[0]); + output.dt = precision_to_datatype(precision); + output.layout = ctx->options.layout; + output.scale = ctx->options.scale; + output.mean = ctx->options.mean; + outputs = &output; +#endif + av_assert0(request->lltask_count >= 1); for (int i = 0; i < request->lltask_count; ++i) { task = request->lltasks[i]->task; @@ -249,28 +465,35 @@ static void infer_completion_callback(void *args) case DFT_PROCESS_FRAME: if (task->do_ioproc) { if (ov_model->model->frame_post_proc != NULL) { - ov_model->model->frame_post_proc(task->out_frame, &output, ov_model->model->filter_ctx); + ov_model->model->frame_post_proc(task->out_frame, outputs, ov_model->model->filter_ctx); } else { - ff_proc_from_dnn_to_frame(task->out_frame, &output, ctx); + ff_proc_from_dnn_to_frame(task->out_frame, outputs, ctx); } } else { - task->out_frame->width = output.width; - task->out_frame->height = output.height; + task->out_frame->width = + outputs[0].dims[dnn_get_width_idx_by_layout(outputs[0].layout)]; + task->out_frame->height = + outputs[0].dims[dnn_get_height_idx_by_layout(outputs[0].layout)]; } break; case DFT_ANALYTICS_DETECT: if (!ov_model->model->detect_post_proc) { av_log(ctx, AV_LOG_ERROR, "detect filter needs to provide post proc\n"); - return; + goto end; } - ov_model->model->detect_post_proc(task->in_frame, &output, 1, ov_model->model->filter_ctx); + ov_model->model->detect_post_proc(task->in_frame, outputs, + ov_model->nb_outputs, + ov_model->model->filter_ctx); break; case DFT_ANALYTICS_CLASSIFY: if (!ov_model->model->classify_post_proc) { av_log(ctx, AV_LOG_ERROR, "classify filter needs to provide post proc\n"); - return; + goto end; } - ov_model->model->classify_post_proc(task->in_frame, &output, request->lltasks[i]->bbox_index, ov_model->model->filter_ctx); + for (int output_i = 0; output_i < ov_model->nb_outputs; output_i++) + ov_model->model->classify_post_proc(task->in_frame, outputs, + request->lltasks[i]->bbox_index, + ov_model->model->filter_ctx); break; default: av_assert0(!"should not reach here"); @@ -279,34 +502,327 @@ static void infer_completion_callback(void *args) task->inference_done++; av_freep(&request->lltasks[i]); - output.data = (uint8_t *)output.data - + output.width * output.height * output.channels * get_datatype_size(output.dt); + for (int i = 0; i < ov_model->nb_outputs; i++) + outputs[i].data = (uint8_t *)outputs[i].data + + outputs[i].dims[1] * outputs[i].dims[2] * outputs[i].dims[3] * + get_datatype_size(outputs[i].dt); } +end: +#if HAVE_OPENVINO2 + av_freep(&outputs); + ov_shape_free(&output_shape); + if (output_tensor) + ov_tensor_free(output_tensor); +#else ie_blob_free(&output_blob); - +#endif request->lltask_count = 0; if (ff_safe_queue_push_back(requestq, request) < 0) { +#if HAVE_OPENVINO2 + ov_infer_request_free(request->infer_request); +#else ie_infer_request_free(&request->infer_request); +#endif av_freep(&request); av_log(ctx, AV_LOG_ERROR, "Failed to push back request_queue.\n"); return; } } -static int init_model_ov(OVModel *ov_model, const char *input_name, const char *output_name) +static void dnn_free_model_ov(DNNModel **model) +{ + OVModel *ov_model; + + if (!model || !*model) + return; + + ov_model = (*model)->model; + while (ff_safe_queue_size(ov_model->request_queue) != 0) { + OVRequestItem *item = ff_safe_queue_pop_front(ov_model->request_queue); + if (item && item->infer_request) { +#if HAVE_OPENVINO2 + ov_infer_request_free(item->infer_request); +#else + ie_infer_request_free(&item->infer_request); +#endif + } + av_freep(&item->lltasks); + av_freep(&item); + } + ff_safe_queue_destroy(ov_model->request_queue); + + while (ff_queue_size(ov_model->lltask_queue) != 0) { + LastLevelTaskItem *item = ff_queue_pop_front(ov_model->lltask_queue); + av_freep(&item); + } + ff_queue_destroy(ov_model->lltask_queue); + + while (ff_queue_size(ov_model->task_queue) != 0) { + TaskItem *item = ff_queue_pop_front(ov_model->task_queue); + av_frame_free(&item->in_frame); + av_frame_free(&item->out_frame); + av_freep(&item); + } + ff_queue_destroy(ov_model->task_queue); +#if HAVE_OPENVINO2 + if (ov_model->input_port) + ov_output_const_port_free(ov_model->input_port); + for (int i = 0; i < ov_model->nb_outputs; i++) + if (ov_model->output_ports[i]) + ov_output_const_port_free(ov_model->output_ports[i]); + av_freep(&ov_model->output_ports); + if (ov_model->preprocess) + ov_preprocess_prepostprocessor_free(ov_model->preprocess); + if (ov_model->compiled_model) + ov_compiled_model_free(ov_model->compiled_model); + if (ov_model->ov_model) + ov_model_free(ov_model->ov_model); + if (ov_model->core) + ov_core_free(ov_model->core); +#else + if (ov_model->exe_network) + ie_exec_network_free(&ov_model->exe_network); + if (ov_model->network) + ie_network_free(&ov_model->network); + if (ov_model->core) + ie_core_free(&ov_model->core); + av_free(ov_model->all_output_names); + av_free(ov_model->all_input_names); +#endif + av_opt_free(&ov_model->ctx); + av_freep(&ov_model); + av_freep(model); +} + + +static int init_model_ov(OVModel *ov_model, const char *input_name, const char **output_names, int nb_outputs) { int ret = 0; OVContext *ctx = &ov_model->ctx; +#if HAVE_OPENVINO2 + ov_status_e status; + ov_preprocess_input_tensor_info_t* input_tensor_info = NULL; + ov_preprocess_output_tensor_info_t* output_tensor_info = NULL; + ov_preprocess_input_model_info_t* input_model_info = NULL; + ov_model_t *tmp_ov_model; + ov_layout_t* NHWC_layout = NULL; + ov_layout_t* NCHW_layout = NULL; + const char* NHWC_desc = "NHWC"; + const char* NCHW_desc = "NCHW"; + const char* device = ctx->options.device_type; +#else IEStatusCode status; ie_available_devices_t a_dev; ie_config_t config = {NULL, NULL, NULL}; char *all_dev_names = NULL; - +#endif + // We scale pixel by default when do frame processing. + if (fabsf(ctx->options.scale) < 1e-6f) + ctx->options.scale = ov_model->model->func_type == DFT_PROCESS_FRAME ? 255 : 1; // batch size if (ctx->options.batch_size <= 0) { ctx->options.batch_size = 1; } +#if HAVE_OPENVINO2 + if (ctx->options.batch_size > 1) { + avpriv_report_missing_feature(ctx, "Do not support batch_size > 1 for now," + "change batch_size to 1.\n"); + ctx->options.batch_size = 1; + } + + status = ov_preprocess_prepostprocessor_create(ov_model->ov_model, &ov_model->preprocess); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to create preprocess for ov_model.\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + + if (input_name) + status = ov_preprocess_prepostprocessor_get_input_info_by_name(ov_model->preprocess, input_name, &ov_model->input_info); + else + status = ov_preprocess_prepostprocessor_get_input_info(ov_model->preprocess, &ov_model->input_info); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get input info from preprocess.\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + + status = ov_preprocess_input_info_get_tensor_info(ov_model->input_info, &input_tensor_info); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get tensor info from input.\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + + //set input layout + status = ov_layout_create(NHWC_desc, &NHWC_layout); + status |= ov_layout_create(NCHW_desc, &NCHW_layout); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to create layout for input.\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + + status = ov_preprocess_input_tensor_info_set_layout(input_tensor_info, NHWC_layout); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to set input tensor layout\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + + status = ov_preprocess_input_info_get_model_info(ov_model->input_info, &input_model_info); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get input model info\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + if (ctx->options.layout == DL_NCHW) + status = ov_preprocess_input_model_info_set_layout(input_model_info, NCHW_layout); + else if (ctx->options.layout == DL_NHWC) + status = ov_preprocess_input_model_info_set_layout(input_model_info, NHWC_layout); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get set input model layout\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + + status = ov_preprocess_input_tensor_info_set_element_type(input_tensor_info, U8); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to set input element type\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + + if (!nb_outputs) { + size_t output_size; + status = ov_model_outputs_size(ov_model->ov_model, &output_size); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get output size.\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + nb_outputs = output_size; + } + ov_model->nb_outputs = nb_outputs; + for (int i = 0; i < nb_outputs; i++) { + if (output_names) + status = ov_preprocess_prepostprocessor_get_output_info_by_name( + ov_model->preprocess, output_names[i], &ov_model->output_info); + else + status = ov_preprocess_prepostprocessor_get_output_info_by_index( + ov_model->preprocess, i, &ov_model->output_info); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get output info from preprocess.\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + status |= ov_preprocess_output_info_get_tensor_info(ov_model->output_info, &output_tensor_info); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get tensor info from input/output.\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + if (ov_model->model->func_type != DFT_PROCESS_FRAME) + status |= ov_preprocess_output_set_element_type(output_tensor_info, F32); + else if (fabsf(ctx->options.scale - 1) > 1e-6f || fabsf(ctx->options.mean) > 1e-6f) + status |= ov_preprocess_output_set_element_type(output_tensor_info, F32); + else + status |= ov_preprocess_output_set_element_type(output_tensor_info, U8); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to set output element type\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + ov_preprocess_output_tensor_info_free(output_tensor_info); + output_tensor_info = NULL; + ov_preprocess_output_info_free(ov_model->output_info); + ov_model->output_info = NULL; + } + // set preprocess steps. + if (fabsf(ctx->options.scale - 1) > 1e-6f || fabsf(ctx->options.mean) > 1e-6f) { + ov_preprocess_preprocess_steps_t* input_process_steps = NULL; + status = ov_preprocess_input_info_get_preprocess_steps(ov_model->input_info, &input_process_steps); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get preprocess steps\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + status = ov_preprocess_preprocess_steps_convert_element_type(input_process_steps, F32); + status |= ov_preprocess_preprocess_steps_mean(input_process_steps, ctx->options.mean); + status |= ov_preprocess_preprocess_steps_scale(input_process_steps, ctx->options.scale); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to set preprocess steps\n"); + ov_preprocess_preprocess_steps_free(input_process_steps); + input_process_steps = NULL; + ret = ov2_map_error(status, NULL); + goto err; + } + ov_preprocess_preprocess_steps_free(input_process_steps); + input_process_steps = NULL; + } + ov_preprocess_input_tensor_info_free(input_tensor_info); + input_tensor_info = NULL; + ov_preprocess_input_info_free(ov_model->input_info); + ov_model->input_info = NULL; + + //update model + if(ov_model->ov_model) + tmp_ov_model = ov_model->ov_model; + status = ov_preprocess_prepostprocessor_build(ov_model->preprocess, &ov_model->ov_model); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to update OV model\n"); + ov_model_free(tmp_ov_model); + tmp_ov_model = NULL; + ret = ov2_map_error(status, NULL); + goto err; + } + ov_model_free(tmp_ov_model); + //update output_port + if (!ov_model->output_ports) { + ov_model->output_ports = av_calloc(nb_outputs, sizeof(*ov_model->output_ports)); + if (!ov_model->output_ports) { + ret = AVERROR(ENOMEM); + goto err; + } + } else + for (int i = 0; i < nb_outputs; i++) { + ov_output_const_port_free(ov_model->output_ports[i]); + ov_model->output_ports[i] = NULL; + } + + for (int i = 0; i < nb_outputs; i++) { + char *port_name; + if (output_names) + status = ov_model_const_output_by_name(ov_model->ov_model, output_names[i], + &ov_model->output_ports[i]); + else + status = ov_model_const_output_by_index(ov_model->ov_model, i, + &ov_model->output_ports[i]); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get output port %s.\n", output_names[i]); + goto err; + } + status = ov_port_get_any_name(ov_model->output_ports[i], &port_name); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get output port name.\n"); + goto err; + } + av_log(ctx, AV_LOG_VERBOSE, "OpenVINO model outputs: %s\n", port_name); + ov_free(port_name); + port_name = NULL; + } + //compile network + status = ov_core_compile_model(ov_model->core, ov_model->ov_model, device, 0, &ov_model->compiled_model); + if (status != OK) { + ret = ov2_map_error(status, NULL); + goto err; + } + ov_preprocess_input_model_info_free(input_model_info); + input_model_info = NULL; + ov_layout_free(NCHW_layout); + ov_layout_free(NHWC_layout); +#else if (ctx->options.batch_size > 1) { input_shapes_t input_shapes; status = ie_network_get_input_shapes(ov_model->network, &input_shapes); @@ -341,13 +857,14 @@ static int init_model_ov(OVModel *ov_model, const char *input_name, const char * if (status != OK) { if (status == NOT_FOUND) { av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model, failed to set output layout as NHWC, "\ - "all output(s) are: \"%s\"\n", input_name, ov_model->all_output_names); + "all output(s) are: \"%s\"\n", output_name, ov_model->all_output_names); } else{ av_log(ctx, AV_LOG_ERROR, "Failed to set layout as NHWC for output %s\n", output_name); } ret = DNN_GENERIC_ERROR; goto err; } + ov_model->nb_outputs = 1; // all models in openvino open model zoo use BGR with range [0.0f, 255.0f] as input, // we don't have a AVPixelFormat to describe it, so we'll use AV_PIX_FMT_BGR24 and @@ -381,7 +898,7 @@ static int init_model_ov(OVModel *ov_model, const char *input_name, const char * ret = AVERROR(ENODEV); goto err; } - +#endif // create infer_requests for async execution if (ctx->options.nireq <= 0) { // the default value is a rough estimation @@ -401,7 +918,11 @@ static int init_model_ov(OVModel *ov_model, const char *input_name, const char * goto err; } +#if HAVE_OPENVINO2 + item->callback.callback_func = infer_completion_callback; +#else item->callback.completeCallBackFunc = infer_completion_callback; +#endif item->callback.args = item; if (ff_safe_queue_push_back(ov_model->request_queue, item) < 0) { av_freep(&item); @@ -409,11 +930,19 @@ static int init_model_ov(OVModel *ov_model, const char *input_name, const char * goto err; } +#if HAVE_OPENVINO2 + status = ov_compiled_model_create_infer_request(ov_model->compiled_model, &item->infer_request); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to Creates an inference request object.\n"); + goto err; + } +#else status = ie_exec_network_create_infer_request(ov_model->exe_network, &item->infer_request); if (status != OK) { ret = DNN_GENERIC_ERROR; goto err; } +#endif item->lltasks = av_malloc_array(ctx->options.batch_size, sizeof(*item->lltasks)); if (!item->lltasks) { @@ -438,13 +967,29 @@ static int init_model_ov(OVModel *ov_model, const char *input_name, const char * return 0; err: - ff_dnn_free_model_ov(&ov_model->model); +#if HAVE_OPENVINO2 + if (output_tensor_info) + ov_preprocess_output_tensor_info_free(output_tensor_info); + if (ov_model->output_info) + ov_preprocess_output_info_free(ov_model->output_info); + if (NCHW_layout) + ov_layout_free(NCHW_layout); + if (NHWC_layout) + ov_layout_free(NHWC_layout); + if (input_model_info) + ov_preprocess_input_model_info_free(input_model_info); +#endif + dnn_free_model_ov(&ov_model->model); return ret; } static int execute_model_ov(OVRequestItem *request, Queue *inferenceq) { +#if HAVE_OPENVINO2 + ov_status_e status; +#else IEStatusCode status; +#endif LastLevelTaskItem *lltask; int ret = 0; TaskItem *task; @@ -452,7 +997,11 @@ static int execute_model_ov(OVRequestItem *request, Queue *inferenceq) OVModel *ov_model; if (ff_queue_size(inferenceq) == 0) { +#if HAVE_OPENVINO2 + ov_infer_request_free(request->infer_request); +#else ie_infer_request_free(&request->infer_request); +#endif av_freep(&request); return 0; } @@ -462,11 +1011,39 @@ static int execute_model_ov(OVRequestItem *request, Queue *inferenceq) ov_model = task->model; ctx = &ov_model->ctx; + ret = fill_model_input_ov(ov_model, request); + if (ret != 0) { + goto err; + } + +#if HAVE_OPENVINO2 if (task->async) { - ret = fill_model_input_ov(ov_model, request); - if (ret != 0) { + status = ov_infer_request_set_callback(request->infer_request, &request->callback); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to set completion callback for inference\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + + status = ov_infer_request_start_async(request->infer_request); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to start async inference\n"); + ret = ov2_map_error(status, NULL); goto err; } + return 0; + } else { + status = ov_infer_request_infer(request->infer_request); + if (status != OK) { + av_log(NULL, AV_LOG_ERROR, "Failed to start synchronous model inference for OV2\n"); + ret = ov2_map_error(status, NULL); + goto err; + } + infer_completion_callback(request); + return (task->inference_done == task->inference_todo) ? 0 : DNN_GENERIC_ERROR; + } +#else + if (task->async) { status = ie_infer_set_completion_callback(request->infer_request, &request->callback); if (status != OK) { av_log(ctx, AV_LOG_ERROR, "Failed to set completion callback for inference\n"); @@ -481,10 +1058,6 @@ static int execute_model_ov(OVRequestItem *request, Queue *inferenceq) } return 0; } else { - ret = fill_model_input_ov(ov_model, request); - if (ret != 0) { - goto err; - } status = ie_infer_request_infer(request->infer_request); if (status != OK) { av_log(ctx, AV_LOG_ERROR, "Failed to start synchronous model inference\n"); @@ -494,9 +1067,14 @@ static int execute_model_ov(OVRequestItem *request, Queue *inferenceq) infer_completion_callback(request); return (task->inference_done == task->inference_todo) ? 0 : DNN_GENERIC_ERROR; } +#endif err: if (ff_safe_queue_push_back(ov_model->request_queue, request) < 0) { +#if HAVE_OPENVINO2 + ov_infer_request_free(request->infer_request); +#else ie_infer_request_free(&request->infer_request); +#endif av_freep(&request); } return ret; @@ -506,19 +1084,56 @@ static int get_input_ov(void *model, DNNData *input, const char *input_name) { OVModel *ov_model = model; OVContext *ctx = &ov_model->ctx; + int input_resizable = ctx->options.input_resizable; + +#if HAVE_OPENVINO2 + ov_shape_t input_shape = {0}; + ov_element_type_e precision; + ov_status_e status; + if (input_name) + status = ov_model_const_input_by_name(ov_model->ov_model, input_name, &ov_model->input_port); + else + status = ov_model_const_input(ov_model->ov_model, &ov_model->input_port); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get input port shape.\n"); + return ov2_map_error(status, NULL); + } + status = ov_port_get_element_type(ov_model->input_port, &precision); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get input port data type.\n"); + return ov2_map_error(status, NULL); + } + status = ov_const_port_get_shape(ov_model->input_port, &input_shape); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to get input port shape.\n"); + return ov2_map_error(status, NULL); + } + for (int i = 0; i < 4; i++) + input->dims[i] = input_shape.dims[i]; + if (input_resizable) { + input->dims[dnn_get_width_idx_by_layout(input->layout)] = -1; + input->dims[dnn_get_height_idx_by_layout(input->layout)] = -1; + } + + if (input_shape.dims[1] <= 3) // NCHW + input->layout = DL_NCHW; + else // NHWC + input->layout = DL_NHWC; + + input->dt = precision_to_datatype(precision); + ov_shape_free(&input_shape); + return 0; +#else char *model_input_name = NULL; IEStatusCode status; size_t model_input_count = 0; dimensions_t dims; precision_e precision; - int input_resizable = ctx->options.input_resizable; - status = ie_network_get_inputs_number(ov_model->network, &model_input_count); if (status != OK) { av_log(ctx, AV_LOG_ERROR, "Failed to get input count\n"); return DNN_GENERIC_ERROR; } - for (size_t i = 0; i < model_input_count; i++) { status = ie_network_get_input_name(ov_model->network, i, &model_input_name); if (status != OK) { @@ -534,9 +1149,18 @@ static int get_input_ov(void *model, DNNData *input, const char *input_name) return DNN_GENERIC_ERROR; } - input->channels = dims.dims[1]; - input->height = input_resizable ? -1 : dims.dims[2]; - input->width = input_resizable ? -1 : dims.dims[3]; + for (int i = 0; i < 4; i++) + input->dims[i] = input_shape.dims[i]; + if (input_resizable) { + input->dims[dnn_get_width_idx_by_layout(input->layout)] = -1; + input->dims[dnn_get_height_idx_by_layout(input->layout)] = -1; + } + + if (input_shape.dims[1] <= 3) // NCHW + input->layout = DL_NCHW; + else // NHWC + input->layout = DL_NHWC; + input->dt = precision_to_datatype(precision); return 0; } @@ -546,6 +1170,7 @@ static int get_input_ov(void *model, DNNData *input, const char *input_name) av_log(ctx, AV_LOG_ERROR, "Could not find \"%s\" in model, all input(s) are: \"%s\"\n", input_name, ov_model->all_input_names); return AVERROR(EINVAL); +#endif } static int contain_valid_detection_bbox(AVFrame *frame) @@ -654,16 +1279,23 @@ static int extract_lltask_from_task(DNNFunctionType func_type, TaskItem *task, Q static int get_output_ov(void *model, const char *input_name, int input_width, int input_height, const char *output_name, int *output_width, int *output_height) { +#if HAVE_OPENVINO2 + ov_dimension_t dims[4] = {{1, 1}, {1, 1}, {input_height, input_height}, {input_width, input_width}}; + ov_status_e status; + ov_shape_t input_shape = {0}; + ov_partial_shape_t partial_shape; +#else + IEStatusCode status; + input_shapes_t input_shapes; +#endif int ret; OVModel *ov_model = model; OVContext *ctx = &ov_model->ctx; TaskItem task; OVRequestItem *request; - IEStatusCode status; - input_shapes_t input_shapes; DNNExecBaseParams exec_params = { .input_name = input_name, - .output_names = &output_name, + .output_names = output_name ? &output_name : NULL, .nb_output = 1, .in_frame = NULL, .out_frame = NULL, @@ -674,6 +1306,38 @@ static int get_output_ov(void *model, const char *input_name, int input_width, i return AVERROR(EINVAL); } +#if HAVE_OPENVINO2 + if (ctx->options.input_resizable) { + status = ov_partial_shape_create(4, dims, &partial_shape); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to create partial shape.\n"); + return ov2_map_error(status, NULL); + } + status = ov_const_port_get_shape(ov_model->input_port, &input_shape); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to create shape for model input resize.\n"); + return ov2_map_error(status, NULL); + } + input_shape.dims[2] = input_height; + input_shape.dims[3] = input_width; + + status = ov_shape_to_partial_shape(input_shape, &partial_shape); + ov_shape_free(&input_shape); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to create partial shape for model input resize.\n"); + return ov2_map_error(status, NULL); + } + + status = ov_model_reshape_single_input(ov_model->ov_model, partial_shape); + ov_partial_shape_free(&partial_shape); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to reszie model input.\n"); + return ov2_map_error(status, NULL); + } + } + + if (!ov_model->compiled_model) { +#else if (ctx->options.input_resizable) { status = ie_network_get_input_shapes(ov_model->network, &input_shapes); input_shapes.shapes->shape.dims[2] = input_height; @@ -685,9 +1349,9 @@ static int get_output_ov(void *model, const char *input_name, int input_width, i return DNN_GENERIC_ERROR; } } - if (!ov_model->exe_network) { - ret = init_model_ov(ov_model, input_name, output_name); +#endif + ret = init_model_ov(ov_model, input_name, output_name ? &output_name : NULL, 1); if (ret != 0) { av_log(ctx, AV_LOG_ERROR, "Failed init OpenVINO exectuable network or inference request\n"); return ret; @@ -721,14 +1385,20 @@ static int get_output_ov(void *model, const char *input_name, int input_width, i return ret; } -DNNModel *ff_dnn_load_model_ov(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx) +static DNNModel *dnn_load_model_ov(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx) { DNNModel *model = NULL; OVModel *ov_model = NULL; OVContext *ctx = NULL; - IEStatusCode status; +#if HAVE_OPENVINO2 + ov_core_t* core = NULL; + ov_model_t* ovmodel = NULL; + ov_status_e status; +#else size_t node_count = 0; char *node_name = NULL; + IEStatusCode status; +#endif model = av_mallocz(sizeof(DNNModel)); if (!model){ @@ -744,8 +1414,6 @@ DNNModel *ff_dnn_load_model_ov(const char *model_filename, DNNFunctionType func_ ov_model->model = model; ov_model->ctx.class = &dnn_openvino_class; ctx = &ov_model->ctx; - ov_model->all_input_names = NULL; - ov_model->all_output_names = NULL; //parse options av_opt_set_defaults(ctx); @@ -754,6 +1422,31 @@ DNNModel *ff_dnn_load_model_ov(const char *model_filename, DNNFunctionType func_ goto err; } +#if HAVE_OPENVINO2 + status = ov_core_create(&core); + if (status != OK) { + goto err; + } + ov_model->core = core; + + status = ov_core_read_model(core, model_filename, NULL, &ovmodel); + if (status != OK) { + ov_version_t ver; + status = ov_get_openvino_version(&ver); + av_log(NULL, AV_LOG_ERROR, "Failed to read the network from model file %s,\n" + "Please check if the model version matches the runtime OpenVINO Version:\n", + model_filename); + if (status == OK) { + av_log(NULL, AV_LOG_ERROR, "BuildNumber: %s\n", ver.buildNumber); + } + ov_version_free(&ver); + goto err; + } + ov_model->ov_model = ovmodel; +#else + ov_model->all_input_names = NULL; + ov_model->all_output_names = NULL; + status = ie_core_create("", &ov_model->core); if (status != OK) goto err; @@ -782,6 +1475,7 @@ DNNModel *ff_dnn_load_model_ov(const char *model_filename, DNNFunctionType func_ goto err; } APPEND_STRING(ov_model->all_input_names, node_name) + ie_network_name_free(&node_name); } status = ie_network_get_outputs_number(ov_model->network, &node_count); if (status != OK) { @@ -795,7 +1489,9 @@ DNNModel *ff_dnn_load_model_ov(const char *model_filename, DNNFunctionType func_ goto err; } APPEND_STRING(ov_model->all_output_names, node_name) + ie_network_name_free(&node_name); } +#endif model->get_input = &get_input_ov; model->get_output = &get_output_ov; @@ -806,11 +1502,11 @@ DNNModel *ff_dnn_load_model_ov(const char *model_filename, DNNFunctionType func_ return model; err: - ff_dnn_free_model_ov(&model); + dnn_free_model_ov(&model); return NULL; } -int ff_dnn_execute_model_ov(const DNNModel *model, DNNExecBaseParams *exec_params) +static int dnn_execute_model_ov(const DNNModel *model, DNNExecBaseParams *exec_params) { OVModel *ov_model = model->model; OVContext *ctx = &ov_model->ctx; @@ -823,8 +1519,13 @@ int ff_dnn_execute_model_ov(const DNNModel *model, DNNExecBaseParams *exec_param return ret; } +#if HAVE_OPENVINO2 + if (!ov_model->compiled_model) { +#else if (!ov_model->exe_network) { - ret = init_model_ov(ov_model, exec_params->input_name, exec_params->output_names[0]); +#endif + ret = init_model_ov(ov_model, exec_params->input_name, + exec_params->output_names, exec_params->nb_output); if (ret != 0) { av_log(ctx, AV_LOG_ERROR, "Failed init OpenVINO exectuable network or inference request\n"); return ret; @@ -893,18 +1594,22 @@ int ff_dnn_execute_model_ov(const DNNModel *model, DNNExecBaseParams *exec_param } } -DNNAsyncStatusType ff_dnn_get_result_ov(const DNNModel *model, AVFrame **in, AVFrame **out) +static DNNAsyncStatusType dnn_get_result_ov(const DNNModel *model, AVFrame **in, AVFrame **out) { OVModel *ov_model = model->model; return ff_dnn_get_result_common(ov_model->task_queue, in, out); } -int ff_dnn_flush_ov(const DNNModel *model) +static int dnn_flush_ov(const DNNModel *model) { OVModel *ov_model = model->model; OVContext *ctx = &ov_model->ctx; OVRequestItem *request; +#if HAVE_OPENVINO2 + ov_status_e status; +#else IEStatusCode status; +#endif int ret; if (ff_queue_size(ov_model->lltask_queue) == 0) { @@ -923,6 +1628,13 @@ int ff_dnn_flush_ov(const DNNModel *model) av_log(ctx, AV_LOG_ERROR, "Failed to fill model input.\n"); return ret; } +#if HAVE_OPENVINO2 + status = ov_infer_request_infer(request->infer_request); + if (status != OK) { + av_log(ctx, AV_LOG_ERROR, "Failed to start sync inference for OV2\n"); + return ov2_map_error(status, NULL); + } +#else status = ie_infer_set_completion_callback(request->infer_request, &request->callback); if (status != OK) { av_log(ctx, AV_LOG_ERROR, "Failed to set completion callback for inference\n"); @@ -933,45 +1645,15 @@ int ff_dnn_flush_ov(const DNNModel *model) av_log(ctx, AV_LOG_ERROR, "Failed to start async inference\n"); return DNN_GENERIC_ERROR; } +#endif return 0; } -void ff_dnn_free_model_ov(DNNModel **model) -{ - if (*model){ - OVModel *ov_model = (*model)->model; - while (ff_safe_queue_size(ov_model->request_queue) != 0) { - OVRequestItem *item = ff_safe_queue_pop_front(ov_model->request_queue); - if (item && item->infer_request) { - ie_infer_request_free(&item->infer_request); - } - av_freep(&item->lltasks); - av_freep(&item); - } - ff_safe_queue_destroy(ov_model->request_queue); - - while (ff_queue_size(ov_model->lltask_queue) != 0) { - LastLevelTaskItem *item = ff_queue_pop_front(ov_model->lltask_queue); - av_freep(&item); - } - ff_queue_destroy(ov_model->lltask_queue); - - while (ff_queue_size(ov_model->task_queue) != 0) { - TaskItem *item = ff_queue_pop_front(ov_model->task_queue); - av_frame_free(&item->in_frame); - av_frame_free(&item->out_frame); - av_freep(&item); - } - ff_queue_destroy(ov_model->task_queue); - - if (ov_model->exe_network) - ie_exec_network_free(&ov_model->exe_network); - if (ov_model->network) - ie_network_free(&ov_model->network); - if (ov_model->core) - ie_core_free(&ov_model->core); - av_freep(&ov_model); - av_freep(model); - } -} +const DNNModule ff_dnn_backend_openvino = { + .load_model = dnn_load_model_ov, + .execute_model = dnn_execute_model_ov, + .get_result = dnn_get_result_ov, + .flush = dnn_flush_ov, + .free_model = dnn_free_model_ov, +}; diff --git a/libavfilter/dnn/dnn_backend_tf.c b/libavfilter/dnn/dnn_backend_tf.c index 3b5084b67b0..27c5178bb50 100644 --- a/libavfilter/dnn/dnn_backend_tf.c +++ b/libavfilter/dnn/dnn_backend_tf.c @@ -23,18 +23,13 @@ * DNN tensorflow backend implementation. */ -#include "dnn_backend_tf.h" -#include "dnn_backend_native.h" -#include "dnn_backend_native_layer_conv2d.h" -#include "dnn_backend_native_layer_depth2space.h" #include "libavformat/avio.h" #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/cpu.h" +#include "libavutil/opt.h" #include "libavcodec/defs.h" #include "../internal.h" -#include "dnn_backend_native_layer_pad.h" -#include "dnn_backend_native_layer_maximum.h" #include "dnn_io_proc.h" #include "dnn_backend_common.h" #include "safe_queue.h" @@ -175,10 +170,6 @@ static int tf_start_inference(void *args) request->status); if (TF_GetCode(request->status) != TF_OK) { av_log(&tf_model->ctx, AV_LOG_ERROR, "%s", TF_Message(request->status)); - tf_free_request(infer_request); - if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) { - destroy_request_item(&request); - } return DNN_GENERIC_ERROR; } return 0; @@ -260,7 +251,12 @@ static TF_Tensor *allocate_input_tensor(const DNNData *input) { TF_DataType dt; size_t size; - int64_t input_dims[] = {1, input->height, input->width, input->channels}; + int64_t input_dims[4] = { 0 }; + + input_dims[0] = 1; + input_dims[1] = input->dims[dnn_get_height_idx_by_layout(input->layout)]; + input_dims[2] = input->dims[dnn_get_width_idx_by_layout(input->layout)]; + input_dims[3] = input->dims[dnn_get_channel_idx_by_layout(input->layout)]; switch (input->dt) { case DNN_FLOAT: dt = TF_FLOAT; @@ -283,6 +279,7 @@ static int get_input_tf(void *model, DNNData *input, const char *input_name) TFModel *tf_model = model; TFContext *ctx = &tf_model->ctx; TF_Status *status; + TF_DataType dt; int64_t dims[4]; TF_Output tf_output; @@ -293,7 +290,18 @@ static int get_input_tf(void *model, DNNData *input, const char *input_name) } tf_output.index = 0; - input->dt = TF_OperationOutputType(tf_output); + dt = TF_OperationOutputType(tf_output); + switch (dt) { + case TF_FLOAT: + input->dt = DNN_FLOAT; + break; + case TF_UINT8: + input->dt = DNN_UINT8; + break; + default: + av_log(ctx, AV_LOG_ERROR, "Unsupported output type %d in model\n", dt); + return AVERROR(EINVAL); + } input->order = DCO_RGB; status = TF_NewStatus(); @@ -307,9 +315,9 @@ static int get_input_tf(void *model, DNNData *input, const char *input_name) // currently only NHWC is supported av_assert0(dims[0] == 1 || dims[0] == -1); - input->height = dims[1]; - input->width = dims[2]; - input->channels = dims[3]; + for (int i = 0; i < 4; i++) + input->dims[i] = dims[i]; + input->layout = DL_NHWC; return 0; } @@ -438,8 +446,6 @@ static int load_tf_model(TFModel *tf_model, const char *model_filename) TF_DeleteImportGraphDefOptions(graph_opts); TF_DeleteBuffer(graph_def); if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteGraph(tf_model->graph); - TF_DeleteStatus(tf_model->status); av_log(ctx, AV_LOG_ERROR, "Failed to import serialized graph to model graph\n"); av_freep(&sess_config); return DNN_GENERIC_ERROR; @@ -452,8 +458,6 @@ static int load_tf_model(TFModel *tf_model, const char *model_filename) TF_SetConfig(sess_opts, sess_config, sess_config_length,tf_model->status); av_freep(&sess_config); if (TF_GetCode(tf_model->status) != TF_OK) { - TF_DeleteGraph(tf_model->graph); - TF_DeleteStatus(tf_model->status); TF_DeleteSessionOptions(sess_opts); av_log(ctx, AV_LOG_ERROR, "Failed to set config for sess options with %s\n", tf_model->ctx.options.sess_config); @@ -465,8 +469,7 @@ static int load_tf_model(TFModel *tf_model, const char *model_filename) TF_DeleteSessionOptions(sess_opts); if (TF_GetCode(tf_model->status) != TF_OK) { - TF_DeleteGraph(tf_model->graph); - TF_DeleteStatus(tf_model->status); + av_freep(&sess_config); av_log(ctx, AV_LOG_ERROR, "Failed to create new session with model graph\n"); return DNN_GENERIC_ERROR; } @@ -479,9 +482,7 @@ static int load_tf_model(TFModel *tf_model, const char *model_filename) &init_op, 1, NULL, tf_model->status); if (TF_GetCode(tf_model->status) != TF_OK) { - TF_DeleteSession(tf_model->session, tf_model->status); - TF_DeleteGraph(tf_model->graph); - TF_DeleteStatus(tf_model->status); + av_freep(&sess_config); av_log(ctx, AV_LOG_ERROR, "Failed to run session when initializing\n"); return DNN_GENERIC_ERROR; } @@ -490,366 +491,48 @@ static int load_tf_model(TFModel *tf_model, const char *model_filename) return 0; } -#define NAME_BUFFER_SIZE 256 - -static int add_conv_layer(TFModel *tf_model, TF_Operation *transpose_op, TF_Operation **cur_op, - ConvolutionalParams* params, const int layer) +static void dnn_free_model_tf(DNNModel **model) { - TFContext *ctx = &tf_model->ctx; - TF_Operation *op; - TF_OperationDescription *op_desc; - TF_Output input; - int64_t strides[] = {1, 1, 1, 1}; - TF_Tensor *kernel_tensor = NULL, *biases_tensor = NULL; - int64_t dims[4]; - int dims_len; - char name_buffer[NAME_BUFFER_SIZE]; - int32_t size; - - size = params->input_num * params->output_num * params->kernel_size * params->kernel_size; - input.index = 0; - - snprintf(name_buffer, NAME_BUFFER_SIZE, "conv_kernel%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer); - TF_SetAttrType(op_desc, "dtype", TF_FLOAT); - dims[0] = params->output_num; - dims[1] = params->kernel_size; - dims[2] = params->kernel_size; - dims[3] = params->input_num; - dims_len = 4; - kernel_tensor = TF_AllocateTensor(TF_FLOAT, dims, dims_len, size * sizeof(float)); - memcpy(TF_TensorData(kernel_tensor), params->kernel, size * sizeof(float)); - TF_SetAttrTensor(op_desc, "value", kernel_tensor, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - - snprintf(name_buffer, NAME_BUFFER_SIZE, "transpose%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "Transpose", name_buffer); - input.oper = op; - TF_AddInput(op_desc, input); - input.oper = transpose_op; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - TF_SetAttrType(op_desc, "Tperm", TF_INT32); - op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - - snprintf(name_buffer, NAME_BUFFER_SIZE, "conv2d%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "Conv2D", name_buffer); - input.oper = *cur_op; - TF_AddInput(op_desc, input); - input.oper = op; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - TF_SetAttrIntList(op_desc, "strides", strides, 4); - TF_SetAttrString(op_desc, "padding", "VALID", 5); - *cur_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - - snprintf(name_buffer, NAME_BUFFER_SIZE, "conv_biases%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer); - TF_SetAttrType(op_desc, "dtype", TF_FLOAT); - dims[0] = params->output_num; - dims_len = 1; - biases_tensor = TF_AllocateTensor(TF_FLOAT, dims, dims_len, params->output_num * sizeof(float)); - memcpy(TF_TensorData(biases_tensor), params->biases, params->output_num * sizeof(float)); - TF_SetAttrTensor(op_desc, "value", biases_tensor, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - - snprintf(name_buffer, NAME_BUFFER_SIZE, "bias_add%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "BiasAdd", name_buffer); - input.oper = *cur_op; - TF_AddInput(op_desc, input); - input.oper = op; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - *cur_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - - snprintf(name_buffer, NAME_BUFFER_SIZE, "activation%d", layer); - switch (params->activation){ - case RELU: - op_desc = TF_NewOperation(tf_model->graph, "Relu", name_buffer); - break; - case TANH: - op_desc = TF_NewOperation(tf_model->graph, "Tanh", name_buffer); - break; - case SIGMOID: - op_desc = TF_NewOperation(tf_model->graph, "Sigmoid", name_buffer); - break; - default: - avpriv_report_missing_feature(ctx, "convolutional activation function %d", params->activation); - return AVERROR(ENOSYS); - } - input.oper = *cur_op; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - *cur_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - goto err; - } - - return 0; -err: - TF_DeleteTensor(kernel_tensor); - TF_DeleteTensor(biases_tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to add conv layer %d\n", layer); - return DNN_GENERIC_ERROR; -} - -static int add_depth_to_space_layer(TFModel *tf_model, TF_Operation **cur_op, - DepthToSpaceParams *params, const int layer) -{ - TFContext *ctx = &tf_model->ctx; - TF_OperationDescription *op_desc; - TF_Output input; - char name_buffer[NAME_BUFFER_SIZE]; - - snprintf(name_buffer, NAME_BUFFER_SIZE, "depth_to_space%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "DepthToSpace", name_buffer); - input.oper = *cur_op; - input.index = 0; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - TF_SetAttrInt(op_desc, "block_size", params->block_size); - *cur_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - av_log(ctx, AV_LOG_ERROR, "Failed to add depth_to_space to layer %d\n", layer); - return DNN_GENERIC_ERROR; - } - - return 0; -} - -static int add_pad_layer(TFModel *tf_model, TF_Operation **cur_op, - LayerPadParams *params, const int layer) -{ - TFContext *ctx = &tf_model->ctx; - TF_Operation *op; - TF_Tensor *tensor; - TF_OperationDescription *op_desc; - TF_Output input; - int32_t *pads; - int64_t pads_shape[] = {4, 2}; - - char name_buffer[NAME_BUFFER_SIZE]; - snprintf(name_buffer, NAME_BUFFER_SIZE, "pad%d", layer); - - op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer); - TF_SetAttrType(op_desc, "dtype", TF_INT32); - tensor = TF_AllocateTensor(TF_INT32, pads_shape, 2, 4 * 2 * sizeof(int32_t)); - pads = (int32_t *)TF_TensorData(tensor); - pads[0] = params->paddings[0][0]; - pads[1] = params->paddings[0][1]; - pads[2] = params->paddings[1][0]; - pads[3] = params->paddings[1][1]; - pads[4] = params->paddings[2][0]; - pads[5] = params->paddings[2][1]; - pads[6] = params->paddings[3][0]; - pads[7] = params->paddings[3][1]; - TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteTensor(tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to set value for pad of layer %d\n", layer); - return DNN_GENERIC_ERROR; - } - op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteTensor(tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to add pad to layer %d\n", layer); - return DNN_GENERIC_ERROR; - } - - op_desc = TF_NewOperation(tf_model->graph, "MirrorPad", "mirror_pad"); - input.oper = *cur_op; - input.index = 0; - TF_AddInput(op_desc, input); - input.oper = op; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - TF_SetAttrType(op_desc, "Tpaddings", TF_INT32); - TF_SetAttrString(op_desc, "mode", "SYMMETRIC", 9); - *cur_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteTensor(tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to add mirror_pad to layer %d\n", layer); - return DNN_GENERIC_ERROR; - } - - return 0; -} - -static int add_maximum_layer(TFModel *tf_model, TF_Operation **cur_op, - DnnLayerMaximumParams *params, const int layer) -{ - TFContext *ctx = &tf_model->ctx; - TF_Operation *op; - TF_Tensor *tensor; - TF_OperationDescription *op_desc; - TF_Output input; - float *y; - - char name_buffer[NAME_BUFFER_SIZE]; - snprintf(name_buffer, NAME_BUFFER_SIZE, "maximum/y%d", layer); - - op_desc = TF_NewOperation(tf_model->graph, "Const", name_buffer); - TF_SetAttrType(op_desc, "dtype", TF_FLOAT); - tensor = TF_AllocateTensor(TF_FLOAT, NULL, 0, TF_DataTypeSize(TF_FLOAT)); - y = (float *)TF_TensorData(tensor); - *y = params->val.y; - TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteTensor(tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to set value for maximum/y of layer %d", layer); - return DNN_GENERIC_ERROR; - } - op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteTensor(tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to add maximum/y to layer %d\n", layer); - return DNN_GENERIC_ERROR; - } - - snprintf(name_buffer, NAME_BUFFER_SIZE, "maximum%d", layer); - op_desc = TF_NewOperation(tf_model->graph, "Maximum", name_buffer); - input.oper = *cur_op; - input.index = 0; - TF_AddInput(op_desc, input); - input.oper = op; - TF_AddInput(op_desc, input); - TF_SetAttrType(op_desc, "T", TF_FLOAT); - *cur_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - TF_DeleteTensor(tensor); - av_log(ctx, AV_LOG_ERROR, "Failed to add maximum to layer %d\n", layer); - return DNN_GENERIC_ERROR; - } - - return 0; -} - -static int load_native_model(TFModel *tf_model, const char *model_filename) -{ - TFContext *ctx = &tf_model->ctx; - int32_t layer; - TF_OperationDescription *op_desc; - TF_Operation *op; - TF_Operation *transpose_op; - TF_Tensor *tensor = NULL; - TF_Output input; - int32_t *transpose_perm; - int64_t transpose_perm_shape[] = {4}; - int64_t input_shape[] = {1, -1, -1, -1}; - int layer_add_res; - DNNModel *model = NULL; - NativeModel *native_model; - - model = ff_dnn_load_model_native(model_filename, DFT_PROCESS_FRAME, NULL, NULL); - if (!model){ - av_log(ctx, AV_LOG_ERROR, "Failed to load native model\n"); - return AVERROR(EINVAL); - } - - native_model = model->model; - tf_model->graph = TF_NewGraph(); - tf_model->status = TF_NewStatus(); - -#define CLEANUP_ON_ERROR(tf_model) \ - { \ - TF_DeleteTensor(tensor); \ - TF_DeleteGraph(tf_model->graph); \ - TF_DeleteStatus(tf_model->status); \ - av_log(ctx, AV_LOG_ERROR, "Failed to set value or add operator to layer\n"); \ - return DNN_GENERIC_ERROR; \ - } - - op_desc = TF_NewOperation(tf_model->graph, "Placeholder", "x"); - TF_SetAttrType(op_desc, "dtype", TF_FLOAT); - TF_SetAttrShape(op_desc, "shape", input_shape, 4); - op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - CLEANUP_ON_ERROR(tf_model); - } + TFModel *tf_model; - op_desc = TF_NewOperation(tf_model->graph, "Const", "transpose_perm"); - TF_SetAttrType(op_desc, "dtype", TF_INT32); - tensor = TF_AllocateTensor(TF_INT32, transpose_perm_shape, 1, 4 * sizeof(int32_t)); - transpose_perm = (int32_t *)TF_TensorData(tensor); - transpose_perm[0] = 1; - transpose_perm[1] = 2; - transpose_perm[2] = 3; - transpose_perm[3] = 0; - TF_SetAttrTensor(op_desc, "value", tensor, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - CLEANUP_ON_ERROR(tf_model); - } - transpose_op = TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - CLEANUP_ON_ERROR(tf_model); - } + if (*model){ + tf_model = (*model)->model; + while (ff_safe_queue_size(tf_model->request_queue) != 0) { + TFRequestItem *item = ff_safe_queue_pop_front(tf_model->request_queue); + destroy_request_item(&item); + } + ff_safe_queue_destroy(tf_model->request_queue); - for (layer = 0; layer < native_model->layers_num; ++layer){ - switch (native_model->layers[layer].type){ - case DLT_INPUT: - layer_add_res = 0; - break; - case DLT_CONV2D: - layer_add_res = add_conv_layer(tf_model, transpose_op, &op, - (ConvolutionalParams *)native_model->layers[layer].params, layer); - break; - case DLT_DEPTH_TO_SPACE: - layer_add_res = add_depth_to_space_layer(tf_model, &op, - (DepthToSpaceParams *)native_model->layers[layer].params, layer); - break; - case DLT_MIRROR_PAD: - layer_add_res = add_pad_layer(tf_model, &op, - (LayerPadParams *)native_model->layers[layer].params, layer); - break; - case DLT_MAXIMUM: - layer_add_res = add_maximum_layer(tf_model, &op, - (DnnLayerMaximumParams *)native_model->layers[layer].params, layer); - break; - default: - CLEANUP_ON_ERROR(tf_model); + while (ff_queue_size(tf_model->lltask_queue) != 0) { + LastLevelTaskItem *item = ff_queue_pop_front(tf_model->lltask_queue); + av_freep(&item); } + ff_queue_destroy(tf_model->lltask_queue); - if (layer_add_res != 0){ - CLEANUP_ON_ERROR(tf_model); + while (ff_queue_size(tf_model->task_queue) != 0) { + TaskItem *item = ff_queue_pop_front(tf_model->task_queue); + av_frame_free(&item->in_frame); + av_frame_free(&item->out_frame); + av_freep(&item); } - } + ff_queue_destroy(tf_model->task_queue); - op_desc = TF_NewOperation(tf_model->graph, "Identity", "y"); - input.oper = op; - input.index = 0; - TF_AddInput(op_desc, input); - TF_FinishOperation(op_desc, tf_model->status); - if (TF_GetCode(tf_model->status) != TF_OK){ - CLEANUP_ON_ERROR(tf_model); + if (tf_model->graph){ + TF_DeleteGraph(tf_model->graph); + } + if (tf_model->session){ + TF_CloseSession(tf_model->session, tf_model->status); + TF_DeleteSession(tf_model->session, tf_model->status); + } + if (tf_model->status){ + TF_DeleteStatus(tf_model->status); + } + av_freep(&tf_model); + av_freep(model); } - - ff_dnn_free_model_native(&model); - - return 0; } -DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx) +static DNNModel *dnn_load_model_tf(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx) { DNNModel *model = NULL; TFModel *tf_model = NULL; @@ -865,6 +548,7 @@ DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_ av_freep(&model); return NULL; } + model->model = tf_model; tf_model->model = model; ctx = &tf_model->ctx; ctx->class = &dnn_tensorflow_class; @@ -877,9 +561,8 @@ DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_ } if (load_tf_model(tf_model, model_filename) != 0){ - if (load_native_model(tf_model, model_filename) != 0){ - goto err; - } + av_log(ctx, AV_LOG_ERROR, "Failed to load TensorFlow model: \"%s\"\n", model_filename); + goto err; } if (ctx->options.nireq <= 0) { @@ -931,7 +614,6 @@ DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_ goto err; } - model->model = tf_model; model->get_input = &get_input_tf; model->get_output = &get_output_tf; model->options = options; @@ -940,15 +622,15 @@ DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_ return model; err: - ff_dnn_free_model_tf(&model); + dnn_free_model_tf(&model); return NULL; } static int fill_model_input_tf(TFModel *tf_model, TFRequestItem *request) { - DNNData input; + DNNData input = { 0 }; LastLevelTaskItem *lltask; TaskItem *task; - TFInferRequest *infer_request; + TFInferRequest *infer_request = NULL; TFContext *ctx = &tf_model->ctx; int ret = 0; @@ -963,8 +645,8 @@ static int fill_model_input_tf(TFModel *tf_model, TFRequestItem *request) { } infer_request = request->infer_request; - input.height = task->in_frame->height; - input.width = task->in_frame->width; + input.dims[1] = task->in_frame->height; + input.dims[2] = task->in_frame->width; infer_request->tf_input = av_malloc(sizeof(TF_Output)); if (!infer_request->tf_input) { @@ -1047,18 +729,21 @@ static void infer_completion_callback(void *args) { TFModel *tf_model = task->model; TFContext *ctx = &tf_model->ctx; - outputs = av_malloc_array(task->nb_output, sizeof(*outputs)); + outputs = av_calloc(task->nb_output, sizeof(*outputs)); if (!outputs) { av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for *outputs\n"); goto err; } for (uint32_t i = 0; i < task->nb_output; ++i) { - outputs[i].height = TF_Dim(infer_request->output_tensors[i], 1); - outputs[i].width = TF_Dim(infer_request->output_tensors[i], 2); - outputs[i].channels = TF_Dim(infer_request->output_tensors[i], 3); + outputs[i].dims[dnn_get_height_idx_by_layout(outputs[i].layout)] = + TF_Dim(infer_request->output_tensors[i], 1); + outputs[i].dims[dnn_get_width_idx_by_layout(outputs[i].layout)] = + TF_Dim(infer_request->output_tensors[i], 2); + outputs[i].dims[dnn_get_channel_idx_by_layout(outputs[i].layout)] = + TF_Dim(infer_request->output_tensors[i], 3); outputs[i].data = TF_TensorData(infer_request->output_tensors[i]); - outputs[i].dt = TF_TensorType(infer_request->output_tensors[i]); + outputs[i].dt = (DNNDataType)TF_TensorType(infer_request->output_tensors[i]); } switch (tf_model->model->func_type) { case DFT_PROCESS_FRAME: @@ -1070,8 +755,10 @@ static void infer_completion_callback(void *args) { ff_proc_from_dnn_to_frame(task->out_frame, outputs, ctx); } } else { - task->out_frame->width = outputs[0].width; - task->out_frame->height = outputs[0].height; + task->out_frame->width = + outputs[0].dims[dnn_get_width_idx_by_layout(outputs[0].layout)]; + task->out_frame->height = + outputs[0].dims[dnn_get_height_idx_by_layout(outputs[0].layout)]; } break; case DFT_ANALYTICS_DETECT: @@ -1138,10 +825,11 @@ static int execute_model_tf(TFRequestItem *request, Queue *lltask_queue) if (ff_safe_queue_push_back(tf_model->request_queue, request) < 0) { destroy_request_item(&request); } + dnn_free_model_tf(&tf_model->model); return ret; } -int ff_dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_params) +static int dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_params) { TFModel *tf_model = model->model; TFContext *ctx = &tf_model->ctx; @@ -1162,6 +850,7 @@ int ff_dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_param ret = ff_dnn_fill_task(task, exec_params, tf_model, ctx->options.async, 1); if (ret != 0) { + av_log(ctx, AV_LOG_ERROR, "Fill task with invalid parameter(s).\n"); av_freep(&task); return ret; } @@ -1174,25 +863,27 @@ int ff_dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_param ret = extract_lltask_from_task(task, tf_model->lltask_queue); if (ret != 0) { + av_freep(&task); av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n"); return ret; } request = ff_safe_queue_pop_front(tf_model->request_queue); if (!request) { + av_freep(&task); av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n"); return AVERROR(EINVAL); } return execute_model_tf(request, tf_model->lltask_queue); } -DNNAsyncStatusType ff_dnn_get_result_tf(const DNNModel *model, AVFrame **in, AVFrame **out) +static DNNAsyncStatusType dnn_get_result_tf(const DNNModel *model, AVFrame **in, AVFrame **out) { TFModel *tf_model = model->model; return ff_dnn_get_result_common(tf_model->task_queue, in, out); } -int ff_dnn_flush_tf(const DNNModel *model) +static int dnn_flush_tf(const DNNModel *model) { TFModel *tf_model = model->model; TFContext *ctx = &tf_model->ctx; @@ -1222,43 +913,10 @@ int ff_dnn_flush_tf(const DNNModel *model) return ff_dnn_start_inference_async(ctx, &request->exec_module); } -void ff_dnn_free_model_tf(DNNModel **model) -{ - TFModel *tf_model; - - if (*model){ - tf_model = (*model)->model; - while (ff_safe_queue_size(tf_model->request_queue) != 0) { - TFRequestItem *item = ff_safe_queue_pop_front(tf_model->request_queue); - destroy_request_item(&item); - } - ff_safe_queue_destroy(tf_model->request_queue); - - while (ff_queue_size(tf_model->lltask_queue) != 0) { - LastLevelTaskItem *item = ff_queue_pop_front(tf_model->lltask_queue); - av_freep(&item); - } - ff_queue_destroy(tf_model->lltask_queue); - - while (ff_queue_size(tf_model->task_queue) != 0) { - TaskItem *item = ff_queue_pop_front(tf_model->task_queue); - av_frame_free(&item->in_frame); - av_frame_free(&item->out_frame); - av_freep(&item); - } - ff_queue_destroy(tf_model->task_queue); - - if (tf_model->graph){ - TF_DeleteGraph(tf_model->graph); - } - if (tf_model->session){ - TF_CloseSession(tf_model->session, tf_model->status); - TF_DeleteSession(tf_model->session, tf_model->status); - } - if (tf_model->status){ - TF_DeleteStatus(tf_model->status); - } - av_freep(&tf_model); - av_freep(model); - } -} +const DNNModule ff_dnn_backend_tf = { + .load_model = dnn_load_model_tf, + .execute_model = dnn_execute_model_tf, + .get_result = dnn_get_result_tf, + .flush = dnn_flush_tf, + .free_model = dnn_free_model_tf, +}; diff --git a/libavfilter/dnn/dnn_backend_tf.h b/libavfilter/dnn/dnn_backend_tf.h deleted file mode 100644 index 0b63a4b6d22..00000000000 --- a/libavfilter/dnn/dnn_backend_tf.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2018 Sergey Lavrushkin - * - * 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 - * DNN inference functions interface for TensorFlow backend. - */ - - -#ifndef AVFILTER_DNN_DNN_BACKEND_TF_H -#define AVFILTER_DNN_DNN_BACKEND_TF_H - -#include "../dnn_interface.h" - -DNNModel *ff_dnn_load_model_tf(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx); - -int ff_dnn_execute_model_tf(const DNNModel *model, DNNExecBaseParams *exec_params); -DNNAsyncStatusType ff_dnn_get_result_tf(const DNNModel *model, AVFrame **in, AVFrame **out); -int ff_dnn_flush_tf(const DNNModel *model); - -void ff_dnn_free_model_tf(DNNModel **model); - -#endif diff --git a/libavfilter/dnn/dnn_backend_torch.cpp b/libavfilter/dnn/dnn_backend_torch.cpp new file mode 100644 index 00000000000..fa9a2e6d996 --- /dev/null +++ b/libavfilter/dnn/dnn_backend_torch.cpp @@ -0,0 +1,597 @@ +/* + * Copyright (c) 2024 + * + * 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 + * DNN Torch backend implementation. + */ + +#include +#include + +extern "C" { +#include "../internal.h" +#include "dnn_io_proc.h" +#include "dnn_backend_common.h" +#include "libavutil/opt.h" +#include "queue.h" +#include "safe_queue.h" +} + +typedef struct THOptions{ + char *device_name; + int optimize; +} THOptions; + +typedef struct THContext { + const AVClass *c_class; + THOptions options; +} THContext; + +typedef struct THModel { + THContext ctx; + DNNModel *model; + torch::jit::Module *jit_model; + SafeQueue *request_queue; + Queue *task_queue; + Queue *lltask_queue; +} THModel; + +typedef struct THInferRequest { + torch::Tensor *output; + torch::Tensor *input_tensor; +} THInferRequest; + +typedef struct THRequestItem { + THInferRequest *infer_request; + LastLevelTaskItem *lltask; + DNNAsyncExecModule exec_module; +} THRequestItem; + + +#define OFFSET(x) offsetof(THContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM +static const AVOption dnn_th_options[] = { + { "device", "device to run model", OFFSET(options.device_name), AV_OPT_TYPE_STRING, { .str = "cpu" }, 0, 0, FLAGS }, + { "optimize", "turn on graph executor optimization", OFFSET(options.optimize), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS}, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(dnn_th); + +static int extract_lltask_from_task(TaskItem *task, Queue *lltask_queue) +{ + THModel *th_model = (THModel *)task->model; + THContext *ctx = &th_model->ctx; + LastLevelTaskItem *lltask = (LastLevelTaskItem *)av_malloc(sizeof(*lltask)); + if (!lltask) { + av_log(ctx, AV_LOG_ERROR, "Failed to allocate memory for LastLevelTaskItem\n"); + return AVERROR(ENOMEM); + } + task->inference_todo = 1; + task->inference_done = 0; + lltask->task = task; + if (ff_queue_push_back(lltask_queue, lltask) < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to push back lltask_queue.\n"); + av_freep(&lltask); + return AVERROR(ENOMEM); + } + return 0; +} + +static void th_free_request(THInferRequest *request) +{ + if (!request) + return; + if (request->output) { + delete(request->output); + request->output = NULL; + } + if (request->input_tensor) { + delete(request->input_tensor); + request->input_tensor = NULL; + } + return; +} + +static inline void destroy_request_item(THRequestItem **arg) +{ + THRequestItem *item; + if (!arg || !*arg) { + return; + } + item = *arg; + th_free_request(item->infer_request); + av_freep(&item->infer_request); + av_freep(&item->lltask); + ff_dnn_async_module_cleanup(&item->exec_module); + av_freep(arg); +} + +static void dnn_free_model_th(DNNModel **model) +{ + THModel *th_model; + if (!model || !*model) + return; + + th_model = (THModel *) (*model)->model; + while (ff_safe_queue_size(th_model->request_queue) != 0) { + THRequestItem *item = (THRequestItem *)ff_safe_queue_pop_front(th_model->request_queue); + destroy_request_item(&item); + } + ff_safe_queue_destroy(th_model->request_queue); + + while (ff_queue_size(th_model->lltask_queue) != 0) { + LastLevelTaskItem *item = (LastLevelTaskItem *)ff_queue_pop_front(th_model->lltask_queue); + av_freep(&item); + } + ff_queue_destroy(th_model->lltask_queue); + + while (ff_queue_size(th_model->task_queue) != 0) { + TaskItem *item = (TaskItem *)ff_queue_pop_front(th_model->task_queue); + av_frame_free(&item->in_frame); + av_frame_free(&item->out_frame); + av_freep(&item); + } + ff_queue_destroy(th_model->task_queue); + delete th_model->jit_model; + av_opt_free(&th_model->ctx); + av_freep(&th_model); + av_freep(model); +} + +static int get_input_th(void *model, DNNData *input, const char *input_name) +{ + input->dt = DNN_FLOAT; + input->order = DCO_RGB; + input->layout = DL_NCHW; + input->dims[0] = 1; + input->dims[1] = 3; + input->dims[2] = -1; + input->dims[3] = -1; + return 0; +} + +static void deleter(void *arg) +{ + av_freep(&arg); +} + +static int fill_model_input_th(THModel *th_model, THRequestItem *request) +{ + LastLevelTaskItem *lltask = NULL; + TaskItem *task = NULL; + THInferRequest *infer_request = NULL; + DNNData input = { 0 }; + THContext *ctx = &th_model->ctx; + int ret, width_idx, height_idx, channel_idx; + + lltask = (LastLevelTaskItem *)ff_queue_pop_front(th_model->lltask_queue); + if (!lltask) { + ret = AVERROR(EINVAL); + goto err; + } + request->lltask = lltask; + task = lltask->task; + infer_request = request->infer_request; + + ret = get_input_th(th_model, &input, NULL); + if ( ret != 0) { + goto err; + } + width_idx = dnn_get_width_idx_by_layout(input.layout); + height_idx = dnn_get_height_idx_by_layout(input.layout); + channel_idx = dnn_get_channel_idx_by_layout(input.layout); + input.dims[height_idx] = task->in_frame->height; + input.dims[width_idx] = task->in_frame->width; + input.data = av_malloc(input.dims[height_idx] * input.dims[width_idx] * + input.dims[channel_idx] * sizeof(float)); + if (!input.data) + return AVERROR(ENOMEM); + infer_request->input_tensor = new torch::Tensor(); + infer_request->output = new torch::Tensor(); + + switch (th_model->model->func_type) { + case DFT_PROCESS_FRAME: + input.scale = 255; + if (task->do_ioproc) { + if (th_model->model->frame_pre_proc != NULL) { + th_model->model->frame_pre_proc(task->in_frame, &input, th_model->model->filter_ctx); + } else { + ff_proc_from_frame_to_dnn(task->in_frame, &input, ctx); + } + } + break; + default: + avpriv_report_missing_feature(NULL, "model function type %d", th_model->model->func_type); + break; + } + *infer_request->input_tensor = torch::from_blob(input.data, + {1, input.dims[channel_idx], input.dims[height_idx], input.dims[width_idx]}, + deleter, torch::kFloat32); + return 0; + +err: + th_free_request(infer_request); + return ret; +} + +static int th_start_inference(void *args) +{ + THRequestItem *request = (THRequestItem *)args; + THInferRequest *infer_request = NULL; + LastLevelTaskItem *lltask = NULL; + TaskItem *task = NULL; + THModel *th_model = NULL; + THContext *ctx = NULL; + std::vector inputs; + torch::NoGradGuard no_grad; + + if (!request) { + av_log(NULL, AV_LOG_ERROR, "THRequestItem is NULL\n"); + return AVERROR(EINVAL); + } + infer_request = request->infer_request; + lltask = request->lltask; + task = lltask->task; + th_model = (THModel *)task->model; + ctx = &th_model->ctx; + + if (ctx->options.optimize) + torch::jit::setGraphExecutorOptimize(true); + else + torch::jit::setGraphExecutorOptimize(false); + + if (!infer_request->input_tensor || !infer_request->output) { + av_log(ctx, AV_LOG_ERROR, "input or output tensor is NULL\n"); + return DNN_GENERIC_ERROR; + } + inputs.push_back(*infer_request->input_tensor); + + *infer_request->output = th_model->jit_model->forward(inputs).toTensor(); + + return 0; +} + +static void infer_completion_callback(void *args) { + THRequestItem *request = (THRequestItem*)args; + LastLevelTaskItem *lltask = request->lltask; + TaskItem *task = lltask->task; + DNNData outputs = { 0 }; + THInferRequest *infer_request = request->infer_request; + THModel *th_model = (THModel *)task->model; + torch::Tensor *output = infer_request->output; + + c10::IntArrayRef sizes = output->sizes(); + outputs.order = DCO_RGB; + outputs.layout = DL_NCHW; + outputs.dt = DNN_FLOAT; + if (sizes.size() == 4) { + // 4 dimensions: [batch_size, channel, height, width] + // this format of data is normally used for video frame SR + outputs.dims[0] = sizes.at(0); // N + outputs.dims[1] = sizes.at(1); // C + outputs.dims[2] = sizes.at(2); // H + outputs.dims[3] = sizes.at(3); // W + } else { + avpriv_report_missing_feature(&th_model->ctx, "Support of this kind of model"); + goto err; + } + + switch (th_model->model->func_type) { + case DFT_PROCESS_FRAME: + if (task->do_ioproc) { + outputs.scale = 255; + outputs.data = output->data_ptr(); + if (th_model->model->frame_post_proc != NULL) { + th_model->model->frame_post_proc(task->out_frame, &outputs, th_model->model->filter_ctx); + } else { + ff_proc_from_dnn_to_frame(task->out_frame, &outputs, &th_model->ctx); + } + } else { + task->out_frame->width = outputs.dims[dnn_get_width_idx_by_layout(outputs.layout)]; + task->out_frame->height = outputs.dims[dnn_get_height_idx_by_layout(outputs.layout)]; + } + break; + default: + avpriv_report_missing_feature(&th_model->ctx, "model function type %d", th_model->model->func_type); + goto err; + } + task->inference_done++; + av_freep(&request->lltask); +err: + th_free_request(infer_request); + + if (ff_safe_queue_push_back(th_model->request_queue, request) < 0) { + destroy_request_item(&request); + av_log(&th_model->ctx, AV_LOG_ERROR, "Unable to push back request_queue when failed to start inference.\n"); + } +} + +static int execute_model_th(THRequestItem *request, Queue *lltask_queue) +{ + THModel *th_model = NULL; + LastLevelTaskItem *lltask; + TaskItem *task = NULL; + int ret = 0; + + if (ff_queue_size(lltask_queue) == 0) { + destroy_request_item(&request); + return 0; + } + + lltask = (LastLevelTaskItem *)ff_queue_peek_front(lltask_queue); + if (lltask == NULL) { + av_log(NULL, AV_LOG_ERROR, "Failed to get LastLevelTaskItem\n"); + ret = AVERROR(EINVAL); + goto err; + } + task = lltask->task; + th_model = (THModel *)task->model; + + ret = fill_model_input_th(th_model, request); + if ( ret != 0) { + goto err; + } + if (task->async) { + avpriv_report_missing_feature(&th_model->ctx, "LibTorch async"); + } else { + ret = th_start_inference((void *)(request)); + if (ret != 0) { + goto err; + } + infer_completion_callback(request); + return (task->inference_done == task->inference_todo) ? 0 : DNN_GENERIC_ERROR; + } + +err: + th_free_request(request->infer_request); + if (ff_safe_queue_push_back(th_model->request_queue, request) < 0) { + destroy_request_item(&request); + } + return ret; +} + +static int get_output_th(void *model, const char *input_name, int input_width, int input_height, + const char *output_name, int *output_width, int *output_height) +{ + int ret = 0; + THModel *th_model = (THModel*) model; + THContext *ctx = &th_model->ctx; + TaskItem task = { 0 }; + THRequestItem *request = NULL; + DNNExecBaseParams exec_params = { + .input_name = input_name, + .output_names = &output_name, + .nb_output = 1, + .in_frame = NULL, + .out_frame = NULL, + }; + ret = ff_dnn_fill_gettingoutput_task(&task, &exec_params, th_model, input_height, input_width, ctx); + if ( ret != 0) { + goto err; + } + + ret = extract_lltask_from_task(&task, th_model->lltask_queue); + if ( ret != 0) { + av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n"); + goto err; + } + + request = (THRequestItem*) ff_safe_queue_pop_front(th_model->request_queue); + if (!request) { + av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n"); + ret = AVERROR(EINVAL); + goto err; + } + + ret = execute_model_th(request, th_model->lltask_queue); + *output_width = task.out_frame->width; + *output_height = task.out_frame->height; + +err: + av_frame_free(&task.out_frame); + av_frame_free(&task.in_frame); + return ret; +} + +static THInferRequest *th_create_inference_request(void) +{ + THInferRequest *request = (THInferRequest *)av_malloc(sizeof(THInferRequest)); + if (!request) { + return NULL; + } + request->input_tensor = NULL; + request->output = NULL; + return request; +} + +static DNNModel *dnn_load_model_th(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx) +{ + DNNModel *model = NULL; + THModel *th_model = NULL; + THRequestItem *item = NULL; + THContext *ctx; + + model = (DNNModel *)av_mallocz(sizeof(DNNModel)); + if (!model) { + return NULL; + } + + th_model = (THModel *)av_mallocz(sizeof(THModel)); + if (!th_model) { + av_freep(&model); + return NULL; + } + th_model->model = model; + model->model = th_model; + th_model->ctx.c_class = &dnn_th_class; + ctx = &th_model->ctx; + //parse options + av_opt_set_defaults(ctx); + if (av_opt_set_from_string(ctx, options, NULL, "=", "&") < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to parse options \"%s\"\n", options); + return NULL; + } + + c10::Device device = c10::Device(ctx->options.device_name); + if (!device.is_cpu()) { + av_log(ctx, AV_LOG_ERROR, "Not supported device:\"%s\"\n", ctx->options.device_name); + goto fail; + } + + try { + th_model->jit_model = new torch::jit::Module; + (*th_model->jit_model) = torch::jit::load(model_filename); + } catch (const c10::Error& e) { + av_log(ctx, AV_LOG_ERROR, "Failed to load torch model\n"); + goto fail; + } + + th_model->request_queue = ff_safe_queue_create(); + if (!th_model->request_queue) { + goto fail; + } + + item = (THRequestItem *)av_mallocz(sizeof(THRequestItem)); + if (!item) { + goto fail; + } + item->lltask = NULL; + item->infer_request = th_create_inference_request(); + if (!item->infer_request) { + av_log(NULL, AV_LOG_ERROR, "Failed to allocate memory for Torch inference request\n"); + goto fail; + } + item->exec_module.start_inference = &th_start_inference; + item->exec_module.callback = &infer_completion_callback; + item->exec_module.args = item; + + if (ff_safe_queue_push_back(th_model->request_queue, item) < 0) { + goto fail; + } + item = NULL; + + th_model->task_queue = ff_queue_create(); + if (!th_model->task_queue) { + goto fail; + } + + th_model->lltask_queue = ff_queue_create(); + if (!th_model->lltask_queue) { + goto fail; + } + + model->get_input = &get_input_th; + model->get_output = &get_output_th; + model->options = NULL; + model->filter_ctx = filter_ctx; + model->func_type = func_type; + return model; + +fail: + if (item) { + destroy_request_item(&item); + av_freep(&item); + } + dnn_free_model_th(&model); + return NULL; +} + +static int dnn_execute_model_th(const DNNModel *model, DNNExecBaseParams *exec_params) +{ + THModel *th_model = (THModel *)model->model; + THContext *ctx = &th_model->ctx; + TaskItem *task; + THRequestItem *request; + int ret = 0; + + ret = ff_check_exec_params(ctx, DNN_TH, model->func_type, exec_params); + if (ret != 0) { + av_log(ctx, AV_LOG_ERROR, "exec parameter checking fail.\n"); + return ret; + } + + task = (TaskItem *)av_malloc(sizeof(TaskItem)); + if (!task) { + av_log(ctx, AV_LOG_ERROR, "unable to alloc memory for task item.\n"); + return AVERROR(ENOMEM); + } + + ret = ff_dnn_fill_task(task, exec_params, th_model, 0, 1); + if (ret != 0) { + av_freep(&task); + av_log(ctx, AV_LOG_ERROR, "unable to fill task.\n"); + return ret; + } + + ret = ff_queue_push_back(th_model->task_queue, task); + if (ret < 0) { + av_freep(&task); + av_log(ctx, AV_LOG_ERROR, "unable to push back task_queue.\n"); + return ret; + } + + ret = extract_lltask_from_task(task, th_model->lltask_queue); + if (ret != 0) { + av_log(ctx, AV_LOG_ERROR, "unable to extract last level task from task.\n"); + return ret; + } + + request = (THRequestItem *)ff_safe_queue_pop_front(th_model->request_queue); + if (!request) { + av_log(ctx, AV_LOG_ERROR, "unable to get infer request.\n"); + return AVERROR(EINVAL); + } + + return execute_model_th(request, th_model->lltask_queue); +} + +static DNNAsyncStatusType dnn_get_result_th(const DNNModel *model, AVFrame **in, AVFrame **out) +{ + THModel *th_model = (THModel *)model->model; + return ff_dnn_get_result_common(th_model->task_queue, in, out); +} + +static int dnn_flush_th(const DNNModel *model) +{ + THModel *th_model = (THModel *)model->model; + THRequestItem *request; + + if (ff_queue_size(th_model->lltask_queue) == 0) + // no pending task need to flush + return 0; + + request = (THRequestItem *)ff_safe_queue_pop_front(th_model->request_queue); + if (!request) { + av_log(&th_model->ctx, AV_LOG_ERROR, "unable to get infer request.\n"); + return AVERROR(EINVAL); + } + + return execute_model_th(request, th_model->lltask_queue); +} + +extern const DNNModule ff_dnn_backend_torch = { + .load_model = dnn_load_model_th, + .execute_model = dnn_execute_model_th, + .get_result = dnn_get_result_th, + .flush = dnn_flush_th, + .free_model = dnn_free_model_th, +}; diff --git a/libavfilter/dnn/dnn_interface.c b/libavfilter/dnn/dnn_interface.c index 554a36b0dc7..b9f71aea530 100644 --- a/libavfilter/dnn/dnn_interface.c +++ b/libavfilter/dnn/dnn_interface.c @@ -24,57 +24,31 @@ */ #include "../dnn_interface.h" -#include "dnn_backend_native.h" -#include "dnn_backend_tf.h" -#include "dnn_backend_openvino.h" #include "libavutil/mem.h" -DNNModule *ff_get_dnn_module(DNNBackendType backend_type) -{ - DNNModule *dnn_module; - - dnn_module = av_mallocz(sizeof(DNNModule)); - if(!dnn_module){ - return NULL; - } +extern const DNNModule ff_dnn_backend_openvino; +extern const DNNModule ff_dnn_backend_tf; +extern const DNNModule ff_dnn_backend_torch; +const DNNModule *ff_get_dnn_module(DNNBackendType backend_type, void *log_ctx) +{ switch(backend_type){ - case DNN_NATIVE: - dnn_module->load_model = &ff_dnn_load_model_native; - dnn_module->execute_model = &ff_dnn_execute_model_native; - dnn_module->get_result = &ff_dnn_get_result_native; - dnn_module->flush = &ff_dnn_flush_native; - dnn_module->free_model = &ff_dnn_free_model_native; - break; - case DNN_TF: #if (CONFIG_LIBTENSORFLOW == 1) - dnn_module->load_model = &ff_dnn_load_model_tf; - dnn_module->execute_model = &ff_dnn_execute_model_tf; - dnn_module->get_result = &ff_dnn_get_result_tf; - dnn_module->flush = &ff_dnn_flush_tf; - dnn_module->free_model = &ff_dnn_free_model_tf; - #else - av_freep(&dnn_module); - return NULL; + case DNN_TF: + return &ff_dnn_backend_tf; #endif - break; - case DNN_OV: #if (CONFIG_LIBOPENVINO == 1) - dnn_module->load_model = &ff_dnn_load_model_ov; - dnn_module->execute_model = &ff_dnn_execute_model_ov; - dnn_module->get_result = &ff_dnn_get_result_ov; - dnn_module->flush = &ff_dnn_flush_ov; - dnn_module->free_model = &ff_dnn_free_model_ov; - #else - av_freep(&dnn_module); - return NULL; + case DNN_OV: + return &ff_dnn_backend_openvino; + #endif + #if (CONFIG_LIBTORCH == 1) + case DNN_TH: + return &ff_dnn_backend_torch; #endif - break; default: - av_log(NULL, AV_LOG_ERROR, "Module backend_type is not native or tensorflow\n"); - av_freep(&dnn_module); + av_log(log_ctx, AV_LOG_ERROR, + "Module backend_type %d is not supported or enabled.\n", + backend_type); return NULL; } - - return dnn_module; } diff --git a/libavfilter/dnn/dnn_io_proc.c b/libavfilter/dnn/dnn_io_proc.c index 7961bf6b95e..e5d6edb301c 100644 --- a/libavfilter/dnn/dnn_io_proc.c +++ b/libavfilter/dnn/dnn_io_proc.c @@ -24,24 +24,67 @@ #include "libavutil/avassert.h" #include "libavutil/detection_bbox.h" +static int get_datatype_size(DNNDataType dt) +{ + switch (dt) + { + case DNN_FLOAT: + return sizeof(float); + case DNN_UINT8: + return sizeof(uint8_t); + default: + av_assert0(!"not supported yet."); + return 1; + } +} + int ff_proc_from_dnn_to_frame(AVFrame *frame, DNNData *output, void *log_ctx) { struct SwsContext *sws_ctx; + int ret = 0; + int linesize[4] = { 0 }; + void **dst_data = NULL; + void *middle_data = NULL; + uint8_t *planar_data[4] = { 0 }; + int plane_size = frame->width * frame->height * sizeof(uint8_t); + enum AVPixelFormat src_fmt = AV_PIX_FMT_NONE; + int src_datatype_size = get_datatype_size(output->dt); + int bytewidth = av_image_get_linesize(frame->format, frame->width, 0); if (bytewidth < 0) { return AVERROR(EINVAL); } - if (output->dt != DNN_FLOAT) { - avpriv_report_missing_feature(log_ctx, "data type rather than DNN_FLOAT"); + /* scale == 1 and mean == 0 and dt == UINT8: passthrough */ + if (fabsf(output->scale - 1) < 1e-6f && fabsf(output->mean) < 1e-6 && output->dt == DNN_UINT8) + src_fmt = AV_PIX_FMT_GRAY8; + /* (scale == 255 or scale == 0) and mean == 0 and dt == FLOAT: normalization */ + else if ((fabsf(output->scale - 255) < 1e-6f || fabsf(output->scale) < 1e-6f) && + fabsf(output->mean) < 1e-6 && output->dt == DNN_FLOAT) + src_fmt = AV_PIX_FMT_GRAYF32; + else { + av_log(log_ctx, AV_LOG_ERROR, "dnn_process output data doesn't type: UINT8 " + "scale: %f, mean: %f\n", output->scale, output->mean); return AVERROR(ENOSYS); } + dst_data = (void **)frame->data; + linesize[0] = frame->linesize[0]; + if (output->layout == DL_NCHW) { + middle_data = av_malloc(plane_size * output->dims[1]); + if (!middle_data) { + ret = AVERROR(ENOMEM); + goto err; + } + dst_data = &middle_data; + linesize[0] = frame->width * 3; + } + switch (frame->format) { case AV_PIX_FMT_RGB24: case AV_PIX_FMT_BGR24: sws_ctx = sws_getContext(frame->width * 3, frame->height, - AV_PIX_FMT_GRAYF32, + src_fmt, frame->width * 3, frame->height, AV_PIX_FMT_GRAY8, @@ -49,20 +92,54 @@ int ff_proc_from_dnn_to_frame(AVFrame *frame, DNNData *output, void *log_ctx) if (!sws_ctx) { av_log(log_ctx, AV_LOG_ERROR, "Impossible to create scale context for the conversion " "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n", - av_get_pix_fmt_name(AV_PIX_FMT_GRAYF32), frame->width * 3, frame->height, + av_get_pix_fmt_name(src_fmt), frame->width * 3, frame->height, av_get_pix_fmt_name(AV_PIX_FMT_GRAY8), frame->width * 3, frame->height); - return AVERROR(EINVAL); + ret = AVERROR(EINVAL); + goto err; } sws_scale(sws_ctx, (const uint8_t *[4]){(const uint8_t *)output->data, 0, 0, 0}, - (const int[4]){frame->width * 3 * sizeof(float), 0, 0, 0}, 0, frame->height, - (uint8_t * const*)frame->data, frame->linesize); + (const int[4]){frame->width * 3 * src_datatype_size, 0, 0, 0}, 0, frame->height, + (uint8_t * const*)dst_data, linesize); sws_freeContext(sws_ctx); - return 0; + // convert data from planar to packed + if (output->layout == DL_NCHW) { + sws_ctx = sws_getContext(frame->width, + frame->height, + AV_PIX_FMT_GBRP, + frame->width, + frame->height, + frame->format, + 0, NULL, NULL, NULL); + if (!sws_ctx) { + av_log(log_ctx, AV_LOG_ERROR, "Impossible to create scale context for the conversion " + "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n", + av_get_pix_fmt_name(AV_PIX_FMT_GBRP), frame->width, frame->height, + av_get_pix_fmt_name(frame->format),frame->width, frame->height); + ret = AVERROR(EINVAL); + goto err; + } + if (frame->format == AV_PIX_FMT_RGB24) { + planar_data[0] = (uint8_t *)middle_data + plane_size; + planar_data[1] = (uint8_t *)middle_data + plane_size * 2; + planar_data[2] = (uint8_t *)middle_data; + } else if (frame->format == AV_PIX_FMT_BGR24) { + planar_data[0] = (uint8_t *)middle_data + plane_size; + planar_data[1] = (uint8_t *)middle_data; + planar_data[2] = (uint8_t *)middle_data + plane_size * 2; + } + sws_scale(sws_ctx, (const uint8_t * const *)planar_data, + (const int [4]){frame->width * sizeof(uint8_t), + frame->width * sizeof(uint8_t), + frame->width * sizeof(uint8_t), 0}, + 0, frame->height, frame->data, frame->linesize); + sws_freeContext(sws_ctx); + } + break; case AV_PIX_FMT_GRAYF32: av_image_copy_plane(frame->data[0], frame->linesize[0], output->data, bytewidth, bytewidth, frame->height); - return 0; + break; case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUV422P: case AV_PIX_FMT_YUV444P: @@ -80,56 +157,122 @@ int ff_proc_from_dnn_to_frame(AVFrame *frame, DNNData *output, void *log_ctx) if (!sws_ctx) { av_log(log_ctx, AV_LOG_ERROR, "Impossible to create scale context for the conversion " "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n", - av_get_pix_fmt_name(AV_PIX_FMT_GRAYF32), frame->width, frame->height, + av_get_pix_fmt_name(src_fmt), frame->width, frame->height, av_get_pix_fmt_name(AV_PIX_FMT_GRAY8), frame->width, frame->height); - return AVERROR(EINVAL); + ret = AVERROR(EINVAL); + goto err; } sws_scale(sws_ctx, (const uint8_t *[4]){(const uint8_t *)output->data, 0, 0, 0}, - (const int[4]){frame->width * sizeof(float), 0, 0, 0}, 0, frame->height, + (const int[4]){frame->width * src_datatype_size, 0, 0, 0}, 0, frame->height, (uint8_t * const*)frame->data, frame->linesize); sws_freeContext(sws_ctx); - return 0; + break; default: avpriv_report_missing_feature(log_ctx, "%s", av_get_pix_fmt_name(frame->format)); - return AVERROR(ENOSYS); + ret = AVERROR(ENOSYS); + goto err; } - return 0; +err: + av_free(middle_data); + return ret; } int ff_proc_from_frame_to_dnn(AVFrame *frame, DNNData *input, void *log_ctx) { struct SwsContext *sws_ctx; + int ret = 0; + int linesize[4] = { 0 }; + void **src_data = NULL; + void *middle_data = NULL; + uint8_t *planar_data[4] = { 0 }; + int plane_size = frame->width * frame->height * sizeof(uint8_t); + enum AVPixelFormat dst_fmt = AV_PIX_FMT_NONE; + int dst_datatype_size = get_datatype_size(input->dt); int bytewidth = av_image_get_linesize(frame->format, frame->width, 0); if (bytewidth < 0) { return AVERROR(EINVAL); } - if (input->dt != DNN_FLOAT) { - avpriv_report_missing_feature(log_ctx, "data type rather than DNN_FLOAT"); + /* scale == 1 and mean == 0 and dt == UINT8: passthrough */ + if (fabsf(input->scale - 1) < 1e-6f && fabsf(input->mean) < 1e-6 && input->dt == DNN_UINT8) + dst_fmt = AV_PIX_FMT_GRAY8; + /* (scale == 255 or scale == 0) and mean == 0 and dt == FLOAT: normalization */ + else if ((fabsf(input->scale - 255) < 1e-6f || fabsf(input->scale) < 1e-6f) && + fabsf(input->mean) < 1e-6 && input->dt == DNN_FLOAT) + dst_fmt = AV_PIX_FMT_GRAYF32; + else { + av_log(log_ctx, AV_LOG_ERROR, "dnn_process input data doesn't support type: UINT8 " + "scale: %f, mean: %f\n", input->scale, input->mean); return AVERROR(ENOSYS); } + src_data = (void **)frame->data; + linesize[0] = frame->linesize[0]; + if (input->layout == DL_NCHW) { + middle_data = av_malloc(plane_size * input->dims[1]); + if (!middle_data) { + ret = AVERROR(ENOMEM); + goto err; + } + src_data = &middle_data; + linesize[0] = frame->width * 3; + } + switch (frame->format) { case AV_PIX_FMT_RGB24: case AV_PIX_FMT_BGR24: + // convert data from planar to packed + if (input->layout == DL_NCHW) { + sws_ctx = sws_getContext(frame->width, + frame->height, + frame->format, + frame->width, + frame->height, + AV_PIX_FMT_GBRP, + 0, NULL, NULL, NULL); + if (!sws_ctx) { + av_log(log_ctx, AV_LOG_ERROR, "Impossible to create scale context for the conversion " + "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n", + av_get_pix_fmt_name(frame->format), frame->width, frame->height, + av_get_pix_fmt_name(AV_PIX_FMT_GBRP),frame->width, frame->height); + ret = AVERROR(EINVAL); + goto err; + } + if (frame->format == AV_PIX_FMT_RGB24) { + planar_data[0] = (uint8_t *)middle_data + plane_size; + planar_data[1] = (uint8_t *)middle_data + plane_size * 2; + planar_data[2] = (uint8_t *)middle_data; + } else if (frame->format == AV_PIX_FMT_BGR24) { + planar_data[0] = (uint8_t *)middle_data + plane_size; + planar_data[1] = (uint8_t *)middle_data; + planar_data[2] = (uint8_t *)middle_data + plane_size * 2; + } + sws_scale(sws_ctx, (const uint8_t * const *)frame->data, + frame->linesize, 0, frame->height, planar_data, + (const int [4]){frame->width * sizeof(uint8_t), + frame->width * sizeof(uint8_t), + frame->width * sizeof(uint8_t), 0}); + sws_freeContext(sws_ctx); + } sws_ctx = sws_getContext(frame->width * 3, frame->height, AV_PIX_FMT_GRAY8, frame->width * 3, frame->height, - AV_PIX_FMT_GRAYF32, + dst_fmt, 0, NULL, NULL, NULL); if (!sws_ctx) { av_log(log_ctx, AV_LOG_ERROR, "Impossible to create scale context for the conversion " "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n", av_get_pix_fmt_name(AV_PIX_FMT_GRAY8), frame->width * 3, frame->height, - av_get_pix_fmt_name(AV_PIX_FMT_GRAYF32),frame->width * 3, frame->height); - return AVERROR(EINVAL); + av_get_pix_fmt_name(dst_fmt),frame->width * 3, frame->height); + ret = AVERROR(EINVAL); + goto err; } - sws_scale(sws_ctx, (const uint8_t **)frame->data, - frame->linesize, 0, frame->height, + sws_scale(sws_ctx, (const uint8_t **)src_data, + linesize, 0, frame->height, (uint8_t * const [4]){input->data, 0, 0, 0}, - (const int [4]){frame->width * 3 * sizeof(float), 0, 0, 0}); + (const int [4]){frame->width * 3 * dst_datatype_size, 0, 0, 0}); sws_freeContext(sws_ctx); break; case AV_PIX_FMT_GRAYF32: @@ -149,27 +292,30 @@ int ff_proc_from_frame_to_dnn(AVFrame *frame, DNNData *input, void *log_ctx) AV_PIX_FMT_GRAY8, frame->width, frame->height, - AV_PIX_FMT_GRAYF32, + dst_fmt, 0, NULL, NULL, NULL); if (!sws_ctx) { av_log(log_ctx, AV_LOG_ERROR, "Impossible to create scale context for the conversion " "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n", av_get_pix_fmt_name(AV_PIX_FMT_GRAY8), frame->width, frame->height, - av_get_pix_fmt_name(AV_PIX_FMT_GRAYF32),frame->width, frame->height); - return AVERROR(EINVAL); + av_get_pix_fmt_name(dst_fmt),frame->width, frame->height); + ret = AVERROR(EINVAL); + goto err; } sws_scale(sws_ctx, (const uint8_t **)frame->data, frame->linesize, 0, frame->height, (uint8_t * const [4]){input->data, 0, 0, 0}, - (const int [4]){frame->width * sizeof(float), 0, 0, 0}); + (const int [4]){frame->width * dst_datatype_size, 0, 0, 0}); sws_freeContext(sws_ctx); break; default: avpriv_report_missing_feature(log_ctx, "%s", av_get_pix_fmt_name(frame->format)); - return AVERROR(ENOSYS); + ret = AVERROR(ENOSYS); + goto err; } - - return 0; +err: + av_free(middle_data); + return ret; } static enum AVPixelFormat get_pixel_format(DNNData *data) @@ -200,11 +346,28 @@ int ff_frame_to_dnn_classify(AVFrame *frame, DNNData *input, uint32_t bbox_index int ret = 0; enum AVPixelFormat fmt; int left, top, width, height; + int width_idx, height_idx; const AVDetectionBBoxHeader *header; const AVDetectionBBox *bbox; AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DETECTION_BBOXES); av_assert0(sd); + /* (scale != 1 and scale != 0) or mean != 0 */ + if ((fabsf(input->scale - 1) > 1e-6f && fabsf(input->scale) > 1e-6f) || + fabsf(input->mean) > 1e-6f) { + av_log(log_ctx, AV_LOG_ERROR, "dnn_classify input data doesn't support " + "scale: %f, mean: %f\n", input->scale, input->mean); + return AVERROR(ENOSYS); + } + + if (input->layout == DL_NCHW) { + av_log(log_ctx, AV_LOG_ERROR, "dnn_classify input data doesn't support layout: NCHW\n"); + return AVERROR(ENOSYS); + } + + width_idx = dnn_get_width_idx_by_layout(input->layout); + height_idx = dnn_get_height_idx_by_layout(input->layout); + header = (const AVDetectionBBoxHeader *)sd->data; bbox = av_get_detection_bbox(header, bbox_index); @@ -215,17 +378,20 @@ int ff_frame_to_dnn_classify(AVFrame *frame, DNNData *input, uint32_t bbox_index fmt = get_pixel_format(input); sws_ctx = sws_getContext(width, height, frame->format, - input->width, input->height, fmt, + input->dims[width_idx], + input->dims[height_idx], fmt, SWS_FAST_BILINEAR, NULL, NULL, NULL); if (!sws_ctx) { av_log(log_ctx, AV_LOG_ERROR, "Failed to create scale context for the conversion " "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n", av_get_pix_fmt_name(frame->format), width, height, - av_get_pix_fmt_name(fmt), input->width, input->height); + av_get_pix_fmt_name(fmt), + input->dims[width_idx], + input->dims[height_idx]); return AVERROR(EINVAL); } - ret = av_image_fill_linesizes(linesizes, fmt, input->width); + ret = av_image_fill_linesizes(linesizes, fmt, input->dims[width_idx]); if (ret < 0) { av_log(log_ctx, AV_LOG_ERROR, "unable to get linesizes with av_image_fill_linesizes"); sws_freeContext(sws_ctx); @@ -255,20 +421,39 @@ int ff_frame_to_dnn_detect(AVFrame *frame, DNNData *input, void *log_ctx) { struct SwsContext *sws_ctx; int linesizes[4]; - int ret = 0; + int ret = 0, width_idx, height_idx; enum AVPixelFormat fmt = get_pixel_format(input); + + /* (scale != 1 and scale != 0) or mean != 0 */ + if ((fabsf(input->scale - 1) > 1e-6f && fabsf(input->scale) > 1e-6f) || + fabsf(input->mean) > 1e-6f) { + av_log(log_ctx, AV_LOG_ERROR, "dnn_detect input data doesn't support " + "scale: %f, mean: %f\n", input->scale, input->mean); + return AVERROR(ENOSYS); + } + + if (input->layout == DL_NCHW) { + av_log(log_ctx, AV_LOG_ERROR, "dnn_detect input data doesn't support layout: NCHW\n"); + return AVERROR(ENOSYS); + } + + width_idx = dnn_get_width_idx_by_layout(input->layout); + height_idx = dnn_get_height_idx_by_layout(input->layout); + sws_ctx = sws_getContext(frame->width, frame->height, frame->format, - input->width, input->height, fmt, + input->dims[width_idx], + input->dims[height_idx], fmt, SWS_FAST_BILINEAR, NULL, NULL, NULL); if (!sws_ctx) { av_log(log_ctx, AV_LOG_ERROR, "Impossible to create scale context for the conversion " "fmt:%s s:%dx%d -> fmt:%s s:%dx%d\n", av_get_pix_fmt_name(frame->format), frame->width, frame->height, - av_get_pix_fmt_name(fmt), input->width, input->height); + av_get_pix_fmt_name(fmt), input->dims[width_idx], + input->dims[height_idx]); return AVERROR(EINVAL); } - ret = av_image_fill_linesizes(linesizes, fmt, input->width); + ret = av_image_fill_linesizes(linesizes, fmt, input->dims[width_idx]); if (ret < 0) { av_log(log_ctx, AV_LOG_ERROR, "unable to get linesizes with av_image_fill_linesizes"); sws_freeContext(sws_ctx); diff --git a/libavfilter/dnn_filter_common.c b/libavfilter/dnn_filter_common.c index 5083e3de198..7d194c9adea 100644 --- a/libavfilter/dnn_filter_common.c +++ b/libavfilter/dnn_filter_common.c @@ -53,22 +53,34 @@ static char **separate_output_names(const char *expr, const char *val_sep, int * int ff_dnn_init(DnnContext *ctx, DNNFunctionType func_type, AVFilterContext *filter_ctx) { + DNNBackendType backend = ctx->backend_type; + if (!ctx->model_filename) { av_log(filter_ctx, AV_LOG_ERROR, "model file for network is not specified\n"); return AVERROR(EINVAL); } - if (!ctx->model_inputname) { - av_log(filter_ctx, AV_LOG_ERROR, "input name of the model network is not specified\n"); - return AVERROR(EINVAL); - } - ctx->model_outputnames = separate_output_names(ctx->model_outputnames_string, "&", &ctx->nb_outputs); - if (!ctx->model_outputnames) { - av_log(filter_ctx, AV_LOG_ERROR, "could not parse model output names\n"); - return AVERROR(EINVAL); + if (backend == DNN_TH) { + if (ctx->model_inputname) + av_log(filter_ctx, AV_LOG_WARNING, "LibTorch backend do not require inputname, "\ + "inputname will be ignored.\n"); + if (ctx->model_outputnames) + av_log(filter_ctx, AV_LOG_WARNING, "LibTorch backend do not require outputname(s), "\ + "all outputname(s) will be ignored.\n"); + ctx->nb_outputs = 1; + } else if (backend == DNN_TF) { + if (!ctx->model_inputname) { + av_log(filter_ctx, AV_LOG_ERROR, "input name of the model network is not specified\n"); + return AVERROR(EINVAL); + } + ctx->model_outputnames = separate_output_names(ctx->model_outputnames_string, "&", &ctx->nb_outputs); + if (!ctx->model_outputnames) { + av_log(filter_ctx, AV_LOG_ERROR, "could not parse model output names\n"); + return AVERROR(EINVAL); + } } - ctx->dnn_module = ff_get_dnn_module(ctx->backend_type); + ctx->dnn_module = ff_get_dnn_module(ctx->backend_type, filter_ctx); if (!ctx->dnn_module) { av_log(filter_ctx, AV_LOG_ERROR, "could not create DNN module for requested backend\n"); return AVERROR(ENOMEM); @@ -113,8 +125,10 @@ int ff_dnn_get_input(DnnContext *ctx, DNNData *input) int ff_dnn_get_output(DnnContext *ctx, int input_width, int input_height, int *output_width, int *output_height) { + char * output_name = ctx->model_outputnames && ctx->backend_type != DNN_TH ? + ctx->model_outputnames[0] : NULL; return ctx->model->get_output(ctx->model->model, ctx->model_inputname, input_width, input_height, - (const char *)ctx->model_outputnames[0], output_width, output_height); + (const char *)output_name, output_width, output_height); } int ff_dnn_execute_model(DnnContext *ctx, AVFrame *in_frame, AVFrame *out_frame) @@ -158,6 +172,11 @@ void ff_dnn_uninit(DnnContext *ctx) { if (ctx->dnn_module) { (ctx->dnn_module->free_model)(&ctx->model); - av_freep(&ctx->dnn_module); + } + if (ctx->model_outputnames) { + for (int i = 0; i < ctx->nb_outputs; i++) + av_free(ctx->model_outputnames[i]); + + av_freep(&ctx->model_outputnames); } } diff --git a/libavfilter/dnn_filter_common.h b/libavfilter/dnn_filter_common.h index bcdf37c8155..30871ee3819 100644 --- a/libavfilter/dnn_filter_common.h +++ b/libavfilter/dnn_filter_common.h @@ -36,7 +36,7 @@ typedef struct DnnContext { char **model_outputnames; uint32_t nb_outputs; - DNNModule *dnn_module; + const DNNModule *dnn_module; DNNModel *model; } DnnContext; diff --git a/libavfilter/dnn_interface.h b/libavfilter/dnn_interface.h index ef8d7ae66f7..63f492e690c 100644 --- a/libavfilter/dnn_interface.h +++ b/libavfilter/dnn_interface.h @@ -32,7 +32,7 @@ #define DNN_GENERIC_ERROR FFERRTAG('D','N','N','!') -typedef enum {DNN_NATIVE, DNN_TF, DNN_OV} DNNBackendType; +typedef enum {DNN_TF = 1, DNN_OV, DNN_TH} DNNBackendType; typedef enum {DNN_FLOAT = 1, DNN_UINT8 = 4} DNNDataType; @@ -56,12 +56,21 @@ typedef enum { DFT_ANALYTICS_CLASSIFY, // classify for each bounding box }DNNFunctionType; +typedef enum { + DL_NONE, + DL_NCHW, + DL_NHWC, +} DNNLayout; + typedef struct DNNData{ void *data; - int width, height, channels; + int dims[4]; // dt and order together decide the color format DNNDataType dt; DNNColorOrder order; + DNNLayout layout; + float scale; + float mean; } DNNData; typedef struct DNNExecBaseParams { @@ -123,6 +132,21 @@ typedef struct DNNModule{ } DNNModule; // Initializes DNNModule depending on chosen backend. -DNNModule *ff_get_dnn_module(DNNBackendType backend_type); +const DNNModule *ff_get_dnn_module(DNNBackendType backend_type, void *log_ctx); + +static inline int dnn_get_width_idx_by_layout(DNNLayout layout) +{ + return layout == DL_NHWC ? 2 : 3; +} + +static inline int dnn_get_height_idx_by_layout(DNNLayout layout) +{ + return layout == DL_NHWC ? 1 : 2; +} + +static inline int dnn_get_channel_idx_by_layout(DNNLayout layout) +{ + return layout == DL_NHWC ? 3 : 1; +} #endif diff --git a/libavfilter/drawutils.c b/libavfilter/drawutils.c index 1081938d867..95525d38b40 100644 --- a/libavfilter/drawutils.c +++ b/libavfilter/drawutils.c @@ -61,6 +61,7 @@ int ff_fill_rgba_map(uint8_t *rgba_map, enum AVPixelFormat pix_fmt) had0 |= pos == 0; rgba_map[i] = pos; + depthb = db; } if (desc->nb_components == 3) diff --git a/libavfilter/edge_template.c b/libavfilter/edge_template.c index af33c178af4..ce45e579db5 100644 --- a/libavfilter/edge_template.c +++ b/libavfilter/edge_template.c @@ -22,7 +22,6 @@ #include "libavutil/avassert.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -75,6 +74,7 @@ void fn(gaussian_blur)(int w, int h, uint8_t *dst, int dst_linesize, const uint8_t *src, int src_linesize, int src_stride) { + int j; pixel *srcp = (pixel *)src; pixel *dstp = (pixel *)dst; @@ -82,12 +82,17 @@ void fn(gaussian_blur)(int w, int h, src_linesize /= sizeof(pixel); dst_linesize /= sizeof(pixel); - memcpy(dstp, srcp, w*sizeof(pixel)); dstp += dst_linesize; srcp += src_linesize; - memcpy(dstp, srcp, w*sizeof(pixel)); dstp += dst_linesize; srcp += src_linesize; - for (int j = 2; j < h - 2; j++) { - dstp[0] = srcp[(0)*src_stride]; - dstp[1] = srcp[(1)*src_stride]; - for (int i = 2; i < w - 2; i++) { + for (j = 0; j < FFMIN(h, 2); j++) { + memcpy(dstp, srcp, w*sizeof(pixel)); + dstp += dst_linesize; + srcp += src_linesize; + } + + for (; j < h - 2; j++) { + int i; + for (i = 0; i < FFMIN(w, 2); i++) + dstp[i] = srcp[i*src_stride]; + for (; i < w - 2; i++) { /* Gaussian mask of size 5x5 with sigma = 1.4 */ dstp[i] = ((srcp[-2*src_linesize + (i-2)*src_stride] + srcp[2*src_linesize + (i-2)*src_stride]) * 2 + (srcp[-2*src_linesize + (i-1)*src_stride] + srcp[2*src_linesize + (i-1)*src_stride]) * 4 @@ -107,12 +112,15 @@ void fn(gaussian_blur)(int w, int h, + srcp[(i+1)*src_stride] * 12 + srcp[(i+2)*src_stride] * 5) / 159; } - dstp[w - 2] = srcp[(w - 2)*src_stride]; - dstp[w - 1] = srcp[(w - 1)*src_stride]; + for (; i < w; i++) + dstp[i] = srcp[i*src_stride]; dstp += dst_linesize; srcp += src_linesize; } - memcpy(dstp, srcp, w*sizeof(pixel)); dstp += dst_linesize; srcp += src_linesize; - memcpy(dstp, srcp, w*sizeof(pixel)); + for (; j < h; j++) { + memcpy(dstp, srcp, w*sizeof(pixel)); + dstp += dst_linesize; + srcp += src_linesize; + } } diff --git a/libavfilter/f_bench.c b/libavfilter/f_bench.c index 9b55194dbc5..a9bcd2149ca 100644 --- a/libavfilter/f_bench.c +++ b/libavfilter/f_bench.c @@ -20,9 +20,10 @@ #include "libavutil/opt.h" #include "libavutil/time.h" +#include "audio.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" enum BenchAction { ACTION_START, @@ -41,9 +42,9 @@ typedef struct BenchContext { #define OFFSET(x) offsetof(BenchContext, x) #define DEFINE_OPTIONS(filt_name, FLAGS) \ static const AVOption filt_name##_options[] = { \ - { "action", "set action", OFFSET(action), AV_OPT_TYPE_INT, {.i64=ACTION_START}, 0, NB_ACTION-1, FLAGS, "action" }, \ - { "start", "start timer", 0, AV_OPT_TYPE_CONST, {.i64=ACTION_START}, INT_MIN, INT_MAX, FLAGS, "action" }, \ - { "stop", "stop timer", 0, AV_OPT_TYPE_CONST, {.i64=ACTION_STOP}, INT_MIN, INT_MAX, FLAGS, "action" }, \ + { "action", "set action", OFFSET(action), AV_OPT_TYPE_INT, {.i64=ACTION_START}, 0, NB_ACTION-1, FLAGS, .unit = "action" }, \ + { "start", "start timer", 0, AV_OPT_TYPE_CONST, {.i64=ACTION_START}, INT_MIN, INT_MAX, FLAGS, .unit = "action" }, \ + { "stop", "stop timer", 0, AV_OPT_TYPE_CONST, {.i64=ACTION_STOP}, INT_MIN, INT_MAX, FLAGS, .unit = "action" }, \ { NULL } \ } @@ -100,20 +101,13 @@ static const AVFilterPad bench_inputs[] = { }, }; -static const AVFilterPad bench_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_bench = { .name = "bench", .description = NULL_IF_CONFIG_SMALL("Benchmark part of a filtergraph."), .priv_size = sizeof(BenchContext), .init = init, FILTER_INPUTS(bench_inputs), - FILTER_OUTPUTS(bench_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &bench_class, .flags = AVFILTER_FLAG_METADATA_ONLY, }; @@ -131,20 +125,13 @@ static const AVFilterPad abench_inputs[] = { }, }; -static const AVFilterPad abench_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_abench = { .name = "abench", .description = NULL_IF_CONFIG_SMALL("Benchmark part of a filtergraph."), .priv_size = sizeof(BenchContext), .init = init, FILTER_INPUTS(abench_inputs), - FILTER_OUTPUTS(abench_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), .priv_class = &abench_class, .flags = AVFILTER_FLAG_METADATA_ONLY, }; diff --git a/libavfilter/f_cue.c b/libavfilter/f_cue.c index 9a1b7c03b86..7748e8f1fd4 100644 --- a/libavfilter/f_cue.c +++ b/libavfilter/f_cue.c @@ -22,9 +22,11 @@ #include "libavutil/opt.h" #include "libavutil/time.h" +#include "audio.h" #include "avfilter.h" #include "filters.h" #include "internal.h" +#include "video.h" typedef struct CueContext { const AVClass *class; @@ -99,54 +101,26 @@ static const AVOption options[] = { AVFILTER_DEFINE_CLASS_EXT(cue_acue, "(a)cue", options); #if CONFIG_CUE_FILTER -static const AVFilterPad cue_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -static const AVFilterPad cue_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_cue = { .name = "cue", .description = NULL_IF_CONFIG_SMALL("Delay filtering to match a cue."), .priv_class = &cue_acue_class, .priv_size = sizeof(CueContext), - FILTER_INPUTS(cue_inputs), - FILTER_OUTPUTS(cue_outputs), + FILTER_INPUTS(ff_video_default_filterpad), + FILTER_OUTPUTS(ff_video_default_filterpad), .activate = activate, }; #endif /* CONFIG_CUE_FILTER */ #if CONFIG_ACUE_FILTER -static const AVFilterPad acue_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - -static const AVFilterPad acue_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_acue = { .name = "acue", .description = NULL_IF_CONFIG_SMALL("Delay filtering to match a cue."), .priv_class = &cue_acue_class, .priv_size = sizeof(CueContext), .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(acue_inputs), - FILTER_OUTPUTS(acue_outputs), + FILTER_INPUTS(ff_audio_default_filterpad), + FILTER_OUTPUTS(ff_audio_default_filterpad), .activate = activate, }; #endif /* CONFIG_ACUE_FILTER */ diff --git a/libavfilter/f_drawgraph.c b/libavfilter/f_drawgraph.c index d29a7fb60a2..fd8641ff759 100644 --- a/libavfilter/f_drawgraph.c +++ b/libavfilter/f_drawgraph.c @@ -69,16 +69,16 @@ static const AVOption drawgraph_options[] = { { "bg", "set background color", OFFSET(bg), AV_OPT_TYPE_COLOR, {.str="white"}, 0, 0, FLAGS }, { "min", "set minimal value", OFFSET(min), AV_OPT_TYPE_FLOAT, {.dbl=-1.}, INT_MIN, INT_MAX, FLAGS }, { "max", "set maximal value", OFFSET(max), AV_OPT_TYPE_FLOAT, {.dbl=1.}, INT_MIN, INT_MAX, FLAGS }, - { "mode", "set graph mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS, "mode" }, - {"bar", "draw bars", OFFSET(mode), AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode"}, - {"dot", "draw dots", OFFSET(mode), AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode"}, - {"line", "draw lines", OFFSET(mode), AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "mode"}, - { "slide", "set slide mode", OFFSET(slide), AV_OPT_TYPE_INT, {.i64=0}, 0, 4, FLAGS, "slide" }, - {"frame", "draw new frames", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "slide"}, - {"replace", "replace old columns with new", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "slide"}, - {"scroll", "scroll from right to left", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "slide"}, - {"rscroll", "scroll from left to right", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "slide"}, - {"picture", "display graph in single frame", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, "slide"}, + { "mode", "set graph mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS, .unit = "mode" }, + {"bar", "draw bars", OFFSET(mode), AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "mode"}, + {"dot", "draw dots", OFFSET(mode), AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "mode"}, + {"line", "draw lines", OFFSET(mode), AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "mode"}, + { "slide", "set slide mode", OFFSET(slide), AV_OPT_TYPE_INT, {.i64=0}, 0, 4, FLAGS, .unit = "slide" }, + {"frame", "draw new frames", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "slide"}, + {"replace", "replace old columns with new", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "slide"}, + {"scroll", "scroll from right to left", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "slide"}, + {"rscroll", "scroll from left to right", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, .unit = "slide"}, + {"picture", "display graph in single frame", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, .unit = "slide"}, { "size", "set graph size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="900x256"}, 0, 0, FLAGS }, { "s", "set graph size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="900x256"}, 0, 0, FLAGS }, { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, @@ -454,22 +454,22 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->values[3]); } -#if CONFIG_DRAWGRAPH_FILTER - -static const AVFilterPad drawgraph_inputs[] = { +static const AVFilterPad drawgraph_outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = filter_frame, + .config_props = config_output, + .request_frame = request_frame, }, }; -static const AVFilterPad drawgraph_outputs[] = { +#if CONFIG_DRAWGRAPH_FILTER + +static const AVFilterPad drawgraph_inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_output, - .request_frame = request_frame, + .filter_frame = filter_frame, }, }; @@ -497,15 +497,6 @@ static const AVFilterPad adrawgraph_inputs[] = { }, }; -static const AVFilterPad adrawgraph_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_output, - .request_frame = request_frame, - }, -}; - const AVFilter ff_avf_adrawgraph = { .name = "adrawgraph", .description = NULL_IF_CONFIG_SMALL("Draw a graph using input audio metadata."), @@ -514,7 +505,7 @@ const AVFilter ff_avf_adrawgraph = { .init = init, .uninit = uninit, FILTER_INPUTS(adrawgraph_inputs), - FILTER_OUTPUTS(adrawgraph_outputs), + FILTER_OUTPUTS(drawgraph_outputs), FILTER_QUERY_FUNC(query_formats), }; #endif // CONFIG_ADRAWGRAPH_FILTER diff --git a/libavfilter/f_ebur128.c b/libavfilter/f_ebur128.c index 38e7e0b2954..3a8dba5816a 100644 --- a/libavfilter/f_ebur128.c +++ b/libavfilter/f_ebur128.c @@ -26,6 +26,7 @@ * @todo implement start/stop/reset through filter command injection */ +#include #include #include "libavutil/avassert.h" @@ -42,6 +43,7 @@ #include "filters.h" #include "formats.h" #include "internal.h" +#include "video.h" #define ABS_THRES -70 ///< silence gate: we discard anything below this absolute (LUFS) threshold #define ABS_UP_THRES 10 ///< upper loud limit to consider (ABS_THRES being the minimum) @@ -80,7 +82,9 @@ typedef struct EBUR128Context { /* peak metering */ int peak_mode; ///< enabled peak modes + double true_peak; ///< global true peak double *true_peaks; ///< true peaks per channel + double sample_peak; ///< global sample peak double *sample_peaks; ///< sample peaks per channel double *true_peaks_per_frame; ///< true peaks in a frame per channel #if CONFIG_SWRESAMPLE @@ -159,32 +163,40 @@ enum { #define A AV_OPT_FLAG_AUDIO_PARAM #define V AV_OPT_FLAG_VIDEO_PARAM #define F AV_OPT_FLAG_FILTERING_PARAM +#define X AV_OPT_FLAG_EXPORT +#define R AV_OPT_FLAG_READONLY static const AVOption ebur128_options[] = { { "video", "set video output", OFFSET(do_video), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, V|F }, { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "640x480"}, 0, 0, V|F }, { "meter", "set scale meter (+9 to +18)", OFFSET(meter), AV_OPT_TYPE_INT, {.i64 = 9}, 9, 18, V|F }, - { "framelog", "force frame logging level", OFFSET(loglevel), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, A|V|F, "level" }, - { "quiet", "logging disabled", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_QUIET}, INT_MIN, INT_MAX, A|V|F, "level" }, - { "info", "information logging level", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_INFO}, INT_MIN, INT_MAX, A|V|F, "level" }, - { "verbose", "verbose logging level", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_VERBOSE}, INT_MIN, INT_MAX, A|V|F, "level" }, + { "framelog", "force frame logging level", OFFSET(loglevel), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, A|V|F, .unit = "level" }, + { "quiet", "logging disabled", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_QUIET}, INT_MIN, INT_MAX, A|V|F, .unit = "level" }, + { "info", "information logging level", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_INFO}, INT_MIN, INT_MAX, A|V|F, .unit = "level" }, + { "verbose", "verbose logging level", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_VERBOSE}, INT_MIN, INT_MAX, A|V|F, .unit = "level" }, { "metadata", "inject metadata in the filtergraph", OFFSET(metadata), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, A|V|F }, - { "peak", "set peak mode", OFFSET(peak_mode), AV_OPT_TYPE_FLAGS, {.i64 = PEAK_MODE_NONE}, 0, INT_MAX, A|F, "mode" }, - { "none", "disable any peak mode", 0, AV_OPT_TYPE_CONST, {.i64 = PEAK_MODE_NONE}, INT_MIN, INT_MAX, A|F, "mode" }, - { "sample", "enable peak-sample mode", 0, AV_OPT_TYPE_CONST, {.i64 = PEAK_MODE_SAMPLES_PEAKS}, INT_MIN, INT_MAX, A|F, "mode" }, - { "true", "enable true-peak mode", 0, AV_OPT_TYPE_CONST, {.i64 = PEAK_MODE_TRUE_PEAKS}, INT_MIN, INT_MAX, A|F, "mode" }, + { "peak", "set peak mode", OFFSET(peak_mode), AV_OPT_TYPE_FLAGS, {.i64 = PEAK_MODE_NONE}, 0, INT_MAX, A|F, .unit = "mode" }, + { "none", "disable any peak mode", 0, AV_OPT_TYPE_CONST, {.i64 = PEAK_MODE_NONE}, INT_MIN, INT_MAX, A|F, .unit = "mode" }, + { "sample", "enable peak-sample mode", 0, AV_OPT_TYPE_CONST, {.i64 = PEAK_MODE_SAMPLES_PEAKS}, INT_MIN, INT_MAX, A|F, .unit = "mode" }, + { "true", "enable true-peak mode", 0, AV_OPT_TYPE_CONST, {.i64 = PEAK_MODE_TRUE_PEAKS}, INT_MIN, INT_MAX, A|F, .unit = "mode" }, { "dualmono", "treat mono input files as dual-mono", OFFSET(dual_mono), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, A|F }, { "panlaw", "set a specific pan law for dual-mono files", OFFSET(pan_law), AV_OPT_TYPE_DOUBLE, {.dbl = -3.01029995663978}, -10.0, 0.0, A|F }, { "target", "set a specific target level in LUFS (-23 to 0)", OFFSET(target), AV_OPT_TYPE_INT, {.i64 = -23}, -23, 0, V|F }, - { "gauge", "set gauge display type", OFFSET(gauge_type), AV_OPT_TYPE_INT, {.i64 = 0 }, GAUGE_TYPE_MOMENTARY, GAUGE_TYPE_SHORTTERM, V|F, "gaugetype" }, - { "momentary", "display momentary value", 0, AV_OPT_TYPE_CONST, {.i64 = GAUGE_TYPE_MOMENTARY}, INT_MIN, INT_MAX, V|F, "gaugetype" }, - { "m", "display momentary value", 0, AV_OPT_TYPE_CONST, {.i64 = GAUGE_TYPE_MOMENTARY}, INT_MIN, INT_MAX, V|F, "gaugetype" }, - { "shortterm", "display short-term value", 0, AV_OPT_TYPE_CONST, {.i64 = GAUGE_TYPE_SHORTTERM}, INT_MIN, INT_MAX, V|F, "gaugetype" }, - { "s", "display short-term value", 0, AV_OPT_TYPE_CONST, {.i64 = GAUGE_TYPE_SHORTTERM}, INT_MIN, INT_MAX, V|F, "gaugetype" }, - { "scale", "sets display method for the stats", OFFSET(scale), AV_OPT_TYPE_INT, {.i64 = 0}, SCALE_TYPE_ABSOLUTE, SCALE_TYPE_RELATIVE, V|F, "scaletype" }, - { "absolute", "display absolute values (LUFS)", 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_TYPE_ABSOLUTE}, INT_MIN, INT_MAX, V|F, "scaletype" }, - { "LUFS", "display absolute values (LUFS)", 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_TYPE_ABSOLUTE}, INT_MIN, INT_MAX, V|F, "scaletype" }, - { "relative", "display values relative to target (LU)", 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_TYPE_RELATIVE}, INT_MIN, INT_MAX, V|F, "scaletype" }, - { "LU", "display values relative to target (LU)", 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_TYPE_RELATIVE}, INT_MIN, INT_MAX, V|F, "scaletype" }, + { "gauge", "set gauge display type", OFFSET(gauge_type), AV_OPT_TYPE_INT, {.i64 = 0 }, GAUGE_TYPE_MOMENTARY, GAUGE_TYPE_SHORTTERM, V|F, .unit = "gaugetype" }, + { "momentary", "display momentary value", 0, AV_OPT_TYPE_CONST, {.i64 = GAUGE_TYPE_MOMENTARY}, INT_MIN, INT_MAX, V|F, .unit = "gaugetype" }, + { "m", "display momentary value", 0, AV_OPT_TYPE_CONST, {.i64 = GAUGE_TYPE_MOMENTARY}, INT_MIN, INT_MAX, V|F, .unit = "gaugetype" }, + { "shortterm", "display short-term value", 0, AV_OPT_TYPE_CONST, {.i64 = GAUGE_TYPE_SHORTTERM}, INT_MIN, INT_MAX, V|F, .unit = "gaugetype" }, + { "s", "display short-term value", 0, AV_OPT_TYPE_CONST, {.i64 = GAUGE_TYPE_SHORTTERM}, INT_MIN, INT_MAX, V|F, .unit = "gaugetype" }, + { "scale", "sets display method for the stats", OFFSET(scale), AV_OPT_TYPE_INT, {.i64 = 0}, SCALE_TYPE_ABSOLUTE, SCALE_TYPE_RELATIVE, V|F, .unit = "scaletype" }, + { "absolute", "display absolute values (LUFS)", 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_TYPE_ABSOLUTE}, INT_MIN, INT_MAX, V|F, .unit = "scaletype" }, + { "LUFS", "display absolute values (LUFS)", 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_TYPE_ABSOLUTE}, INT_MIN, INT_MAX, V|F, .unit = "scaletype" }, + { "relative", "display values relative to target (LU)", 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_TYPE_RELATIVE}, INT_MIN, INT_MAX, V|F, .unit = "scaletype" }, + { "LU", "display values relative to target (LU)", 0, AV_OPT_TYPE_CONST, {.i64 = SCALE_TYPE_RELATIVE}, INT_MIN, INT_MAX, V|F, .unit = "scaletype" }, + { "integrated", "integrated loudness (LUFS)", OFFSET(integrated_loudness), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -DBL_MAX, DBL_MAX, A|F|X|R }, + { "range", "loudness range (LU)", OFFSET(loudness_range), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -DBL_MAX, DBL_MAX, A|F|X|R }, + { "lra_low", "LRA low (LUFS)", OFFSET(lra_low), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -DBL_MAX, DBL_MAX, A|F|X|R }, + { "lra_high", "LRA high (LUFS)", OFFSET(lra_high), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -DBL_MAX, DBL_MAX, A|F|X|R }, + { "sample_peak", "sample peak (dBFS)", OFFSET(sample_peak), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -DBL_MAX, DBL_MAX, A|F|X|R }, + { "true_peak", "true peak (dBFS)", OFFSET(true_peak), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, -DBL_MAX, DBL_MAX, A|F|X|R }, { NULL }, }; @@ -284,7 +296,6 @@ static int config_video_output(AVFilterLink *outlink) int i, x, y; uint8_t *p; AVFilterContext *ctx = outlink->src; - AVFilterLink *inlink = ctx->inputs[0]; EBUR128Context *ebur128 = ctx->priv; AVFrame *outpicref; @@ -297,8 +308,8 @@ static int config_video_output(AVFilterLink *outlink) outlink->w = ebur128->w; outlink->h = ebur128->h; outlink->sample_aspect_ratio = (AVRational){1,1}; - outlink->time_base = inlink->time_base; outlink->frame_rate = av_make_q(10, 1); + outlink->time_base = av_inv_q(outlink->frame_rate); #define PAD 8 @@ -419,7 +430,7 @@ static int config_audio_input(AVFilterLink *inlink) * can be more complex to integrate in the one-sample loop of * filter_frame()). */ if (ebur128->metadata || (ebur128->peak_mode & PEAK_MODE_TRUE_PEAKS)) - ebur128->nb_samples = inlink->sample_rate / 10; + ebur128->nb_samples = FFMAX(inlink->sample_rate / 10, 1); return 0; } @@ -701,6 +712,20 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) ebur128->i3000.cache[ch][bin_id_3000] = bin; } +#define FIND_PEAK(global, sp, ptype) do { \ + int ch; \ + double maxpeak; \ + maxpeak = 0.0; \ + if (ebur128->peak_mode & PEAK_MODE_ ## ptype ## _PEAKS) { \ + for (ch = 0; ch < ebur128->nb_channels; ch++) \ + maxpeak = FFMAX(maxpeak, sp[ch]); \ + global = DBFS(maxpeak); \ + } \ +} while (0) + + FIND_PEAK(ebur128->sample_peak, ebur128->sample_peaks, SAMPLES); + FIND_PEAK(ebur128->true_peak, ebur128->true_peaks, TRUE); + /* For integrated loudness, gating blocks are 400ms long with 75% * overlap (see BS.1770-2 p5), so a re-computation is needed each 100ms * (4800 samples at 48kHz). */ @@ -710,7 +735,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) AVFilterLink *outlink = ctx->outputs[0]; const int64_t pts = insamples->pts + av_rescale_q(idx_insample, (AVRational){ 1, inlink->sample_rate }, - outlink->time_base); + ctx->outputs[ebur128->do_video]->time_base); ebur128->sample_count = 0; @@ -862,7 +887,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *insamples) } /* set pts and push frame */ - pic->pts = pts; + pic->pts = av_rescale_q(pts, inlink->time_base, outlink->time_base); + pic->duration = 1; clone = av_frame_clone(pic); if (!clone) return AVERROR(ENOMEM); @@ -1023,7 +1049,6 @@ static int query_formats(AVFilterContext *ctx) static av_cold void uninit(AVFilterContext *ctx) { - int i; EBUR128Context *ebur128 = ctx->priv; /* dual-mono correction */ @@ -1034,6 +1059,7 @@ static av_cold void uninit(AVFilterContext *ctx) ebur128->lra_high -= ebur128->pan_law; } + if (ebur128->nb_channels > 0) { av_log(ctx, AV_LOG_INFO, "Summary:\n\n" " Integrated loudness:\n" " I: %5.1f LUFS\n" @@ -1047,22 +1073,17 @@ static av_cold void uninit(AVFilterContext *ctx) ebur128->loudness_range, ebur128->i3000.rel_threshold, ebur128->lra_low, ebur128->lra_high); -#define PRINT_PEAK_SUMMARY(str, sp, ptype) do { \ - int ch; \ - double maxpeak; \ - maxpeak = 0.0; \ +#define PRINT_PEAK_SUMMARY(str, value, ptype) do { \ if (ebur128->peak_mode & PEAK_MODE_ ## ptype ## _PEAKS) { \ - for (ch = 0; ch < ebur128->nb_channels; ch++) \ - maxpeak = FFMAX(maxpeak, sp[ch]); \ av_log(ctx, AV_LOG_INFO, "\n\n " str " peak:\n" \ - " Peak: %5.1f dBFS", \ - DBFS(maxpeak)); \ + " Peak: %5.1f dBFS", value); \ } \ } while (0) - PRINT_PEAK_SUMMARY("Sample", ebur128->sample_peaks, SAMPLES); - PRINT_PEAK_SUMMARY("True", ebur128->true_peaks, TRUE); + PRINT_PEAK_SUMMARY("Sample", ebur128->sample_peak, SAMPLES); + PRINT_PEAK_SUMMARY("True", ebur128->true_peak, TRUE); av_log(ctx, AV_LOG_INFO, "\n"); + } av_freep(&ebur128->y_line_ref); av_freep(&ebur128->x); @@ -1076,7 +1097,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&ebur128->i3000.sum); av_freep(&ebur128->i400.histogram); av_freep(&ebur128->i3000.histogram); - for (i = 0; i < ebur128->nb_channels; i++) { + for (int i = 0; i < ebur128->nb_channels; i++) { if (ebur128->i400.cache) av_freep(&ebur128->i400.cache[i]); if (ebur128->i3000.cache) diff --git a/libavfilter/f_graphmonitor.c b/libavfilter/f_graphmonitor.c index 016a707a27a..e0b20114e03 100644 --- a/libavfilter/f_graphmonitor.c +++ b/libavfilter/f_graphmonitor.c @@ -28,6 +28,7 @@ #include "libavutil/opt.h" #include "libavutil/timestamp.h" #include "libavutil/xga_font_data.h" +#include "audio.h" #include "avfilter.h" #include "filters.h" #include "formats.h" @@ -47,6 +48,8 @@ typedef struct GraphMonitorContext { int flags; AVRational frame_rate; + int eof; + int eof_frames; int64_t pts; int64_t next_pts; uint8_t white[4]; @@ -54,6 +57,7 @@ typedef struct GraphMonitorContext { uint8_t red[4]; uint8_t green[4]; uint8_t blue[4]; + uint8_t gray[4]; uint8_t bg[4]; CacheItem *cache; @@ -62,54 +66,72 @@ typedef struct GraphMonitorContext { } GraphMonitorContext; enum { - MODE_QUEUE = 1 << 0, - MODE_FCIN = 1 << 1, - MODE_FCOUT = 1 << 2, - MODE_PTS = 1 << 3, - MODE_TIME = 1 << 4, - MODE_TB = 1 << 5, - MODE_FMT = 1 << 6, - MODE_SIZE = 1 << 7, - MODE_RATE = 1 << 8, - MODE_EOF = 1 << 9, - MODE_SCIN = 1 << 10, - MODE_SCOUT = 1 << 11, - MODE_PTS_DELTA = 1 << 12, - MODE_TIME_DELTA = 1 << 13, - MODE_FC_DELTA = 1 << 14, - MODE_SC_DELTA = 1 << 15, + MODE_FULL = 0, + MODE_COMPACT = 1, + MODE_NOZERO = 2, + MODE_NOEOF = 4, + MODE_NODISABLED = 8, + MODE_MAX = 15 +}; + +enum { + FLAG_NONE = 0 << 0, + FLAG_QUEUE = 1 << 0, + FLAG_FCIN = 1 << 1, + FLAG_FCOUT = 1 << 2, + FLAG_PTS = 1 << 3, + FLAG_TIME = 1 << 4, + FLAG_TB = 1 << 5, + FLAG_FMT = 1 << 6, + FLAG_SIZE = 1 << 7, + FLAG_RATE = 1 << 8, + FLAG_EOF = 1 << 9, + FLAG_SCIN = 1 << 10, + FLAG_SCOUT = 1 << 11, + FLAG_PTS_DELTA = 1 << 12, + FLAG_TIME_DELTA = 1 << 13, + FLAG_FC_DELTA = 1 << 14, + FLAG_SC_DELTA = 1 << 15, + FLAG_DISABLED = 1 << 16, }; #define OFFSET(x) offsetof(GraphMonitorContext, x) #define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define VFR AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption graphmonitor_options[] = { { "size", "set monitor size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, VF }, { "s", "set monitor size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, VF }, - { "opacity", "set video opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=.9}, 0, 1, VF }, - { "o", "set video opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=.9}, 0, 1, VF }, - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, VF, "mode" }, - { "m", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, VF, "mode" }, - { "full", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VF, "mode" }, - { "compact", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, VF, "mode" }, - { "flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=MODE_QUEUE}, 0, INT_MAX, VF, "flags" }, - { "f", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=MODE_QUEUE}, 0, INT_MAX, VF, "flags" }, - { "queue", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_QUEUE}, 0, 0, VF, "flags" }, - { "frame_count_in", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FCOUT}, 0, 0, VF, "flags" }, - { "frame_count_out", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FCIN}, 0, 0, VF, "flags" }, - { "frame_count_delta",NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FC_DELTA},0, 0, VF, "flags" }, - { "pts", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_PTS}, 0, 0, VF, "flags" }, - { "pts_delta", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_PTS_DELTA},0,0, VF, "flags" }, - { "time", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_TIME}, 0, 0, VF, "flags" }, - { "time_delta", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_TIME_DELTA},0,0,VF, "flags" }, - { "timebase", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_TB}, 0, 0, VF, "flags" }, - { "format", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FMT}, 0, 0, VF, "flags" }, - { "size", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_SIZE}, 0, 0, VF, "flags" }, - { "rate", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_RATE}, 0, 0, VF, "flags" }, - { "eof", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_EOF}, 0, 0, VF, "flags" }, - { "sample_count_in", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_SCOUT}, 0, 0, VF, "flags" }, - { "sample_count_out", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_SCIN}, 0, 0, VF, "flags" }, - { "sample_count_delta",NULL,0, AV_OPT_TYPE_CONST, {.i64=MODE_SC_DELTA},0, 0, VF, "flags" }, + { "opacity", "set video opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=.9}, 0, 1, VFR }, + { "o", "set video opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=.9}, 0, 1, VFR }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, MODE_MAX, VFR, .unit = "mode" }, + { "m", "set mode", OFFSET(mode), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, MODE_MAX, VFR, .unit = "mode" }, + { "full", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_FULL}, 0, 0, VFR, .unit = "mode" }, + { "compact", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_COMPACT},0, 0, VFR, .unit = "mode" }, + { "nozero", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_NOZERO}, 0, 0, VFR, .unit = "mode" }, + { "noeof", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_NOEOF}, 0, 0, VFR, .unit = "mode" }, + { "nodisabled",NULL,0,AV_OPT_TYPE_CONST, {.i64=MODE_NODISABLED},0,0,VFR,.unit = "mode" }, + { "flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=FLAG_QUEUE}, 0, INT_MAX, VFR, .unit = "flags" }, + { "f", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=FLAG_QUEUE}, 0, INT_MAX, VFR, .unit = "flags" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_NONE}, 0, 0, VFR, .unit = "flags" }, + { "all", NULL, 0, AV_OPT_TYPE_CONST, {.i64=INT_MAX}, 0, 0, VFR, .unit = "flags" }, + { "queue", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_QUEUE}, 0, 0, VFR, .unit = "flags" }, + { "frame_count_in", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_FCOUT}, 0, 0, VFR, .unit = "flags" }, + { "frame_count_out", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_FCIN}, 0, 0, VFR, .unit = "flags" }, + { "frame_count_delta",NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_FC_DELTA},0, 0, VFR, .unit = "flags" }, + { "pts", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_PTS}, 0, 0, VFR, .unit = "flags" }, + { "pts_delta", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_PTS_DELTA},0,0, VFR, .unit = "flags" }, + { "time", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_TIME}, 0, 0, VFR, .unit = "flags" }, + { "time_delta", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_TIME_DELTA},0,0,VFR, .unit = "flags" }, + { "timebase", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_TB}, 0, 0, VFR, .unit = "flags" }, + { "format", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_FMT}, 0, 0, VFR, .unit = "flags" }, + { "size", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_SIZE}, 0, 0, VFR, .unit = "flags" }, + { "rate", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_RATE}, 0, 0, VFR, .unit = "flags" }, + { "eof", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_EOF}, 0, 0, VFR, .unit = "flags" }, + { "sample_count_in", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_SCOUT}, 0, 0, VFR, .unit = "flags" }, + { "sample_count_out", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_SCIN}, 0, 0, VFR, .unit = "flags" }, + { "sample_count_delta",NULL,0, AV_OPT_TYPE_CONST, {.i64=FLAG_SC_DELTA},0, 0, VFR, .unit = "flags" }, + { "disabled", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAG_DISABLED},0, 0, VFR, .unit = "flags" }, { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, VF }, { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, VF }, { NULL } @@ -145,14 +167,22 @@ static int query_formats(AVFilterContext *ctx) static void clear_image(GraphMonitorContext *s, AVFrame *out, AVFilterLink *outlink) { + const int h = out->height; + const int w = out->width; + uint8_t *dst = out->data[0]; int bg = AV_RN32(s->bg); - for (int i = 0; i < out->height; i++) - for (int j = 0; j < out->width; j++) - AV_WN32(out->data[0] + i * out->linesize[0] + j * 4, bg); + for (int j = 0; j < w; j++) + AV_WN32(dst + j * 4, bg); + dst += out->linesize[0]; + for (int i = 1; i < h; i++) { + memcpy(dst, out->data[0], w * 4); + dst += out->linesize[0]; + } } -static void drawtext(AVFrame *pic, int x, int y, const char *txt, uint8_t *color) +static void drawtext(AVFrame *pic, int x, int y, const char *txt, + const int len, uint8_t *color) { const uint8_t *font; int font_height; @@ -161,7 +191,7 @@ static void drawtext(AVFrame *pic, int x, int y, const char *txt, uint8_t *color font = avpriv_cga_font, font_height = 8; if (y + 8 >= pic->height || - x + strlen(txt) * 8 >= pic->width) + x + len * 8 >= pic->width) return; for (i = 0; txt[i]; i++) { @@ -182,6 +212,25 @@ static void drawtext(AVFrame *pic, int x, int y, const char *txt, uint8_t *color } } +static int filter_have_eof(AVFilterContext *filter) +{ + for (int j = 0; j < filter->nb_inputs; j++) { + AVFilterLink *l = filter->inputs[j]; + + if (!ff_outlink_get_status(l)) + return 0; + } + + for (int j = 0; j < filter->nb_outputs; j++) { + AVFilterLink *l = filter->outputs[j]; + + if (!ff_outlink_get_status(l)) + return 0; + } + + return 1; +} + static int filter_have_queued(AVFilterContext *filter) { for (int j = 0; j < filter->nb_inputs; j++) { @@ -203,7 +252,9 @@ static int filter_have_queued(AVFilterContext *filter) return 0; } -static int draw_items(AVFilterContext *ctx, AVFrame *out, +static int draw_items(AVFilterContext *ctx, + AVFilterContext *filter, + AVFrame *out, int xpos, int ypos, AVFilterLink *l, size_t frames) @@ -211,104 +262,112 @@ static int draw_items(AVFilterContext *ctx, AVFrame *out, GraphMonitorContext *s = ctx->priv; int64_t previous_pts_us = s->cache[s->cache_index].previous_pts_us; int64_t current_pts_us = l->current_pts_us; + const int flags = s->flags; + const int mode = s->mode; char buffer[1024] = { 0 }; + int len = 0; - if (s->flags & MODE_FMT) { + if (flags & FLAG_FMT) { if (l->type == AVMEDIA_TYPE_VIDEO) { - snprintf(buffer, sizeof(buffer)-1, " | format: %s", + len = snprintf(buffer, sizeof(buffer)-1, " | format: %s", av_get_pix_fmt_name(l->format)); } else if (l->type == AVMEDIA_TYPE_AUDIO) { - snprintf(buffer, sizeof(buffer)-1, " | format: %s", + len = snprintf(buffer, sizeof(buffer)-1, " | format: %s", av_get_sample_fmt_name(l->format)); } - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_SIZE) { + if (flags & FLAG_SIZE) { if (l->type == AVMEDIA_TYPE_VIDEO) { - snprintf(buffer, sizeof(buffer)-1, " | size: %dx%d", l->w, l->h); + len = snprintf(buffer, sizeof(buffer)-1, " | size: %dx%d", l->w, l->h); } else if (l->type == AVMEDIA_TYPE_AUDIO) { - snprintf(buffer, sizeof(buffer)-1, " | channels: %d", l->ch_layout.nb_channels); + len = snprintf(buffer, sizeof(buffer)-1, " | channels: %d", l->ch_layout.nb_channels); } - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_RATE) { + if (flags & FLAG_RATE) { if (l->type == AVMEDIA_TYPE_VIDEO) { - snprintf(buffer, sizeof(buffer)-1, " | fps: %d/%d", l->frame_rate.num, l->frame_rate.den); + len = snprintf(buffer, sizeof(buffer)-1, " | fps: %d/%d", l->frame_rate.num, l->frame_rate.den); } else if (l->type == AVMEDIA_TYPE_AUDIO) { - snprintf(buffer, sizeof(buffer)-1, " | samplerate: %d", l->sample_rate); + len = snprintf(buffer, sizeof(buffer)-1, " | samplerate: %d", l->sample_rate); } - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; + } + if (flags & FLAG_TB) { + len = snprintf(buffer, sizeof(buffer)-1, " | tb: %d/%d", l->time_base.num, l->time_base.den); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_TB) { - snprintf(buffer, sizeof(buffer)-1, " | tb: %d/%d", l->time_base.num, l->time_base.den); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_QUEUE) && (!(mode & MODE_NOZERO) || frames)) { + len = snprintf(buffer, sizeof(buffer)-1, " | queue: "); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; + len = snprintf(buffer, sizeof(buffer)-1, "%"SIZE_SPECIFIER, frames); + drawtext(out, xpos, ypos, buffer, len, frames > 0 ? frames >= 10 ? frames >= 50 ? s->red : s->yellow : s->green : s->white); + xpos += len * 8; } - if (s->flags & MODE_QUEUE) { - snprintf(buffer, sizeof(buffer)-1, " | queue: "); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; - snprintf(buffer, sizeof(buffer)-1, "%"SIZE_SPECIFIER, frames); - drawtext(out, xpos, ypos, buffer, frames > 0 ? frames >= 10 ? frames >= 50 ? s->red : s->yellow : s->green : s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_FCIN) && (!(mode & MODE_NOZERO) || l->frame_count_in)) { + len = snprintf(buffer, sizeof(buffer)-1, " | in: %"PRId64, l->frame_count_in); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_FCIN) { - snprintf(buffer, sizeof(buffer)-1, " | in: %"PRId64, l->frame_count_in); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_FCOUT) && (!(mode & MODE_NOZERO) || l->frame_count_out)) { + len = snprintf(buffer, sizeof(buffer)-1, " | out: %"PRId64, l->frame_count_out); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_FCOUT) { - snprintf(buffer, sizeof(buffer)-1, " | out: %"PRId64, l->frame_count_out); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_FC_DELTA) && (!(mode & MODE_NOZERO) || (l->frame_count_in - l->frame_count_out))) { + len = snprintf(buffer, sizeof(buffer)-1, " | delta: %"PRId64, l->frame_count_in - l->frame_count_out); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_FC_DELTA) { - snprintf(buffer, sizeof(buffer)-1, " | delta: %"PRId64, l->frame_count_in - l->frame_count_out); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_SCIN) && (!(mode & MODE_NOZERO) || l->sample_count_in)) { + len = snprintf(buffer, sizeof(buffer)-1, " | sin: %"PRId64, l->sample_count_in); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_SCIN) { - snprintf(buffer, sizeof(buffer)-1, " | sin: %"PRId64, l->sample_count_in); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_SCOUT) && (!(mode & MODE_NOZERO) || l->sample_count_out)) { + len = snprintf(buffer, sizeof(buffer)-1, " | sout: %"PRId64, l->sample_count_out); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_SCOUT) { - snprintf(buffer, sizeof(buffer)-1, " | sout: %"PRId64, l->sample_count_out); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_SC_DELTA) && (!(mode & MODE_NOZERO) || (l->sample_count_in - l->sample_count_out))) { + len = snprintf(buffer, sizeof(buffer)-1, " | sdelta: %"PRId64, l->sample_count_in - l->sample_count_out); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_SC_DELTA) { - snprintf(buffer, sizeof(buffer)-1, " | sdelta: %"PRId64, l->sample_count_in - l->sample_count_out); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_PTS) && (!(mode & MODE_NOZERO) || current_pts_us)) { + len = snprintf(buffer, sizeof(buffer)-1, " | pts: %s", av_ts2str(current_pts_us)); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_PTS) { - snprintf(buffer, sizeof(buffer)-1, " | pts: %s", av_ts2str(current_pts_us)); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_PTS_DELTA) && (!(mode & MODE_NOZERO) || (current_pts_us - previous_pts_us))) { + len = snprintf(buffer, sizeof(buffer)-1, " | pts_delta: %s", av_ts2str(current_pts_us - previous_pts_us)); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_PTS_DELTA) { - snprintf(buffer, sizeof(buffer)-1, " | pts_delta: %s", av_ts2str(current_pts_us - previous_pts_us)); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_TIME) && (!(mode & MODE_NOZERO) || current_pts_us)) { + len = snprintf(buffer, sizeof(buffer)-1, " | time: %s", av_ts2timestr(current_pts_us, &AV_TIME_BASE_Q)); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_TIME) { - snprintf(buffer, sizeof(buffer)-1, " | time: %s", av_ts2timestr(current_pts_us, &AV_TIME_BASE_Q)); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_TIME_DELTA) && (!(mode & MODE_NOZERO) || (current_pts_us - previous_pts_us))) { + len = snprintf(buffer, sizeof(buffer)-1, " | time_delta: %s", av_ts2timestr(current_pts_us - previous_pts_us, &AV_TIME_BASE_Q)); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; } - if (s->flags & MODE_TIME_DELTA) { - snprintf(buffer, sizeof(buffer)-1, " | time_delta: %s", av_ts2timestr(current_pts_us - previous_pts_us, &AV_TIME_BASE_Q)); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_EOF) && ff_outlink_get_status(l)) { + len = snprintf(buffer, sizeof(buffer)-1, " | eof"); + drawtext(out, xpos, ypos, buffer, len, s->blue); + xpos += len * 8; } - if (s->flags & MODE_EOF && ff_outlink_get_status(l)) { - snprintf(buffer, sizeof(buffer)-1, " | eof"); - drawtext(out, xpos, ypos, buffer, s->blue); - xpos += strlen(buffer) * 8; + if ((flags & FLAG_DISABLED) && filter->is_disabled) { + len = snprintf(buffer, sizeof(buffer)-1, " | off"); + drawtext(out, xpos, ypos, buffer, len, s->gray); + xpos += len * 8; } s->cache[s->cache_index].previous_pts_us = l->current_pts_us; @@ -329,43 +388,56 @@ static int create_frame(AVFilterContext *ctx, int64_t pts) { GraphMonitorContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; + int ret, len, xpos, ypos = 0; + char buffer[1024]; AVFrame *out; - int ret, xpos, ypos = 0; out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) return AVERROR(ENOMEM); + s->bg[3] = 255 * s->opacity; clear_image(s, out, outlink); s->cache_index = 0; for (int i = 0; i < ctx->graph->nb_filters; i++) { AVFilterContext *filter = ctx->graph->filters[i]; - char buffer[1024] = { 0 }; - if (s->mode && !filter_have_queued(filter)) + if ((s->mode & MODE_COMPACT) && !filter_have_queued(filter)) + continue; + + if ((s->mode & MODE_NOEOF) && filter_have_eof(filter)) + continue; + + if ((s->mode & MODE_NODISABLED) && filter->is_disabled) continue; xpos = 0; - drawtext(out, xpos, ypos, filter->name, s->white); - xpos += strlen(filter->name) * 8 + 10; - drawtext(out, xpos, ypos, filter->filter->name, s->white); + len = strlen(filter->name); + drawtext(out, xpos, ypos, filter->name, len, s->white); + xpos += len * 8 + 10; + len = strlen(filter->filter->name); + drawtext(out, xpos, ypos, filter->filter->name, len, s->white); ypos += 10; for (int j = 0; j < filter->nb_inputs; j++) { AVFilterLink *l = filter->inputs[j]; size_t frames = ff_inlink_queued_frames(l); - if (s->mode && !frames) + if ((s->mode & MODE_COMPACT) && !frames) + continue; + + if ((s->mode & MODE_NOEOF) && ff_outlink_get_status(l)) continue; xpos = 10; - snprintf(buffer, sizeof(buffer)-1, "in%d: ", j); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; - drawtext(out, xpos, ypos, l->src->name, s->white); - xpos += strlen(l->src->name) * 8 + 10; - ret = draw_items(ctx, out, xpos, ypos, l, frames); + len = snprintf(buffer, sizeof(buffer)-1, "in%d: ", j); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; + len = strlen(l->src->name); + drawtext(out, xpos, ypos, l->src->name, len, s->white); + xpos += len * 8 + 10; + ret = draw_items(ctx, filter, out, xpos, ypos, l, frames); if (ret < 0) goto error; ypos += 10; @@ -376,16 +448,20 @@ static int create_frame(AVFilterContext *ctx, int64_t pts) AVFilterLink *l = filter->outputs[j]; size_t frames = ff_inlink_queued_frames(l); - if (s->mode && !frames) + if ((s->mode & MODE_COMPACT) && !frames) + continue; + + if ((s->mode & MODE_NOEOF) && ff_outlink_get_status(l)) continue; xpos = 10; - snprintf(buffer, sizeof(buffer)-1, "out%d: ", j); - drawtext(out, xpos, ypos, buffer, s->white); - xpos += strlen(buffer) * 8; - drawtext(out, xpos, ypos, l->dst->name, s->white); - xpos += strlen(l->dst->name) * 8 + 10; - ret = draw_items(ctx, out, xpos, ypos, l, frames); + len = snprintf(buffer, sizeof(buffer)-1, "out%d: ", j); + drawtext(out, xpos, ypos, buffer, len, s->white); + xpos += len * 8; + len = strlen(l->dst->name); + drawtext(out, xpos, ypos, l->dst->name, len, s->white); + xpos += len * 8 + 10; + ret = draw_items(ctx, filter, out, xpos, ypos, l, frames); if (ret < 0) goto error; ypos += 10; @@ -394,7 +470,10 @@ static int create_frame(AVFilterContext *ctx, int64_t pts) } out->pts = pts; + out->duration = 1; s->pts = pts + 1; + if (s->eof_frames) + s->eof_frames = 0; return ff_filter_frame(outlink, out); error: av_frame_free(&out); @@ -407,10 +486,11 @@ static int activate(AVFilterContext *ctx) AVFilterLink *inlink = ctx->inputs[0]; AVFilterLink *outlink = ctx->outputs[0]; int64_t pts = AV_NOPTS_VALUE; + int status; FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); - if (ff_inlink_queued_frames(inlink)) { + if (!s->eof && ff_inlink_queued_frames(inlink)) { AVFrame *frame = NULL; int ret; @@ -428,13 +508,31 @@ static int activate(AVFilterContext *ctx) if (s->pts == AV_NOPTS_VALUE) s->pts = pts; s->next_pts = pts; + } else if (s->eof) { + s->next_pts = s->pts + 1; } - if (s->pts < s->next_pts && ff_outlink_frame_wanted(outlink)) + if (s->eof && s->eof_frames == 0) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->next_pts); + return 0; + } + + if (s->eof || (s->pts < s->next_pts && ff_outlink_frame_wanted(outlink))) return create_frame(ctx, s->pts); - FF_FILTER_FORWARD_STATUS(inlink, outlink); - FF_FILTER_FORWARD_WANTED(outlink, inlink); + if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) { + s->eof = 1; + s->eof_frames = 1; + ff_filter_set_ready(ctx, 100); + return 0; + } + + if (!s->eof) { + FF_FILTER_FORWARD_WANTED(outlink, inlink); + } else { + ff_filter_set_ready(ctx, 100); + return 0; + } return FFERROR_NOT_READY; } @@ -443,12 +541,12 @@ static int config_output(AVFilterLink *outlink) { GraphMonitorContext *s = outlink->src->priv; - s->bg[3] = 255 * s->opacity; s->white[0] = s->white[1] = s->white[2] = 255; s->yellow[0] = s->yellow[1] = 255; s->red[0] = 255; s->green[1] = 255; s->blue[2] = 255; + s->gray[0] = s->gray[1] = s->gray[2] = 128; s->pts = AV_NOPTS_VALUE; s->next_pts = AV_NOPTS_VALUE; outlink->w = s->w; @@ -470,15 +568,6 @@ static av_cold void uninit(AVFilterContext *ctx) AVFILTER_DEFINE_CLASS_EXT(graphmonitor, "(a)graphmonitor", graphmonitor_options); -#if CONFIG_GRAPHMONITOR_FILTER - -static const AVFilterPad graphmonitor_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad graphmonitor_outputs[] = { { .name = "default", @@ -487,6 +576,8 @@ static const AVFilterPad graphmonitor_outputs[] = { }, }; +#if CONFIG_GRAPHMONITOR_FILTER + const AVFilter ff_vf_graphmonitor = { .name = "graphmonitor", .description = NULL_IF_CONFIG_SMALL("Show various filtergraph stats."), @@ -495,30 +586,16 @@ const AVFilter ff_vf_graphmonitor = { .init = init, .uninit = uninit, .activate = activate, - FILTER_INPUTS(graphmonitor_inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(graphmonitor_outputs), FILTER_QUERY_FUNC(query_formats), + .process_command = ff_filter_process_command, }; #endif // CONFIG_GRAPHMONITOR_FILTER #if CONFIG_AGRAPHMONITOR_FILTER -static const AVFilterPad agraphmonitor_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - -static const AVFilterPad agraphmonitor_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_output, - }, -}; - const AVFilter ff_avf_agraphmonitor = { .name = "agraphmonitor", .description = NULL_IF_CONFIG_SMALL("Show various filtergraph stats."), @@ -527,8 +604,9 @@ const AVFilter ff_avf_agraphmonitor = { .init = init, .uninit = uninit, .activate = activate, - FILTER_INPUTS(agraphmonitor_inputs), - FILTER_OUTPUTS(agraphmonitor_outputs), + FILTER_INPUTS(ff_audio_default_filterpad), + FILTER_OUTPUTS(graphmonitor_outputs), FILTER_QUERY_FUNC(query_formats), + .process_command = ff_filter_process_command, }; #endif // CONFIG_AGRAPHMONITOR_FILTER diff --git a/libavfilter/f_interleave.c b/libavfilter/f_interleave.c index 74597a0ec61..93ad548826e 100644 --- a/libavfilter/f_interleave.c +++ b/libavfilter/f_interleave.c @@ -30,7 +30,6 @@ #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "filters.h" #include "internal.h" #include "audio.h" @@ -54,10 +53,10 @@ static const AVOption filt_name##_options[] = { \ { "nb_inputs", "set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64 = 2}, 1, INT_MAX, .flags = flags_ }, \ { "n", "set number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64 = 2}, 1, INT_MAX, .flags = flags_ }, \ { "duration", "how to determine the end-of-stream", \ - OFFSET(duration_mode), AV_OPT_TYPE_INT, { .i64 = DURATION_LONGEST }, 0, 2, flags_, "duration" }, \ - { "longest", "Duration of longest input", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_LONGEST }, 0, 0, flags_, "duration" }, \ - { "shortest", "Duration of shortest input", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_SHORTEST }, 0, 0, flags_, "duration" }, \ - { "first", "Duration of first input", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_FIRST }, 0, 0, flags_, "duration" }, \ + OFFSET(duration_mode), AV_OPT_TYPE_INT, { .i64 = DURATION_LONGEST }, 0, 2, flags_, .unit = "duration" }, \ + { "longest", "Duration of longest input", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_LONGEST }, 0, 0, flags_, .unit = "duration" }, \ + { "shortest", "Duration of shortest input", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_SHORTEST }, 0, 0, flags_, .unit = "duration" }, \ + { "first", "Duration of first input", 0, AV_OPT_TYPE_CONST, { .i64 = DURATION_FIRST }, 0, 0, flags_, .unit = "duration" }, \ { NULL } \ } diff --git a/libavfilter/f_latency.c b/libavfilter/f_latency.c index f04fd726f23..a39c3c7d242 100644 --- a/libavfilter/f_latency.c +++ b/libavfilter/f_latency.c @@ -20,11 +20,11 @@ #include "config_components.h" -#include "libavutil/opt.h" +#include "audio.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" +#include "video.h" typedef struct LatencyContext { int64_t min_latency; @@ -99,20 +99,6 @@ static av_cold void uninit(AVFilterContext *ctx) #if CONFIG_LATENCY_FILTER -static const AVFilterPad latency_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -static const AVFilterPad latency_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_latency = { .name = "latency", .description = NULL_IF_CONFIG_SMALL("Report video filtering latency."), @@ -122,28 +108,14 @@ const AVFilter ff_vf_latency = { .activate = activate, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(latency_inputs), - FILTER_OUTPUTS(latency_outputs), + FILTER_INPUTS(ff_video_default_filterpad), + FILTER_OUTPUTS(ff_video_default_filterpad), }; #endif // CONFIG_LATENCY_FILTER #if CONFIG_ALATENCY_FILTER -static const AVFilterPad alatency_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - -static const AVFilterPad alatency_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_alatency = { .name = "alatency", .description = NULL_IF_CONFIG_SMALL("Report audio filtering latency."), @@ -152,7 +124,7 @@ const AVFilter ff_af_alatency = { .uninit = uninit, .activate = activate, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, - FILTER_INPUTS(alatency_inputs), - FILTER_OUTPUTS(alatency_outputs), + FILTER_INPUTS(ff_audio_default_filterpad), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; #endif // CONFIG_ALATENCY_FILTER diff --git a/libavfilter/f_loop.c b/libavfilter/f_loop.c index d217efe2fd0..0b08a2ead39 100644 --- a/libavfilter/f_loop.c +++ b/libavfilter/f_loop.c @@ -21,13 +21,11 @@ #include "config_components.h" #include "libavutil/audio_fifo.h" -#include "libavutil/fifo.h" #include "libavutil/internal.h" #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" #include "filters.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -39,7 +37,7 @@ typedef struct LoopContext { AVFrame **frames; int nb_frames; int current_frame; - int64_t start_pts; + int64_t time_pts; int64_t duration; int64_t current_sample; int64_t nb_samples; @@ -49,7 +47,10 @@ typedef struct LoopContext { int eof; int64_t size; int64_t start; + int64_t time; int64_t pts; + int64_t pts_offset; + int64_t eof_pts; } LoopContext; #define AFLAGS AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM @@ -65,6 +66,17 @@ static void check_size(AVFilterContext *ctx) ctx->input_pads[0].type == AVMEDIA_TYPE_VIDEO ? "frames" : "samples"); } +static void update_time(AVFilterContext *ctx, AVRational tb) +{ + LoopContext *s = ctx->priv; + + if (s->time != INT64_MAX) { + int64_t time_pts = av_rescale_q(s->time, AV_TIME_BASE_Q, tb); + if (s->time_pts == AV_NOPTS_VALUE || time_pts < s->time_pts) + s->time_pts = time_pts; + } +} + #if CONFIG_ALOOP_FILTER static int aconfig_input(AVFilterLink *inlink) @@ -72,6 +84,8 @@ static int aconfig_input(AVFilterLink *inlink) AVFilterContext *ctx = inlink->dst; LoopContext *s = ctx->priv; + s->time_pts = AV_NOPTS_VALUE; + s->fifo = av_audio_fifo_alloc(inlink->format, inlink->ch_layout.nb_channels, 8192); s->left = av_audio_fifo_alloc(inlink->format, inlink->ch_layout.nb_channels, 8192); if (!s->fifo || !s->left) @@ -117,7 +131,6 @@ static int push_samples(AVFilterContext *ctx, int nb_samples) return ret; if (s->current_sample >= s->nb_samples) { - s->duration = s->pts; s->current_sample = 0; if (s->loop > 0) @@ -135,11 +148,17 @@ static int afilter_frame(AVFilterLink *inlink, AVFrame *frame) LoopContext *s = ctx->priv; int ret = 0; - if (s->ignored_samples + frame->nb_samples > s->start && s->size > 0 && s->loop != 0) { + if (((s->start >= 0 && s->ignored_samples + frame->nb_samples > s->start) || + (s->time_pts != AV_NOPTS_VALUE && + frame->pts >= s->time_pts)) && + s->size > 0 && s->loop != 0) { if (s->nb_samples < s->size) { int written = FFMIN(frame->nb_samples, s->size - s->nb_samples); int drain = 0; + if (s->start < 0) + s->start = inlink->sample_count_out - written; + ret = av_audio_fifo_write(s->fifo, (void **)frame->extended_data, written); if (ret < 0) return ret; @@ -221,19 +240,24 @@ static int aactivate(AVFilterContext *ctx) LoopContext *s = ctx->priv; AVFrame *frame = NULL; int ret, status; - int64_t pts; FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + update_time(ctx, inlink->time_base); + if (!s->eof && (s->nb_samples < s->size || !s->loop || !s->size)) { - ret = ff_inlink_consume_frame(inlink, &frame); + const int in_nb_samples = FFMIN(1024, s->size - s->nb_samples); + if (in_nb_samples == 0) + ret = ff_inlink_consume_frame(inlink, &frame); + else + ret = ff_inlink_consume_samples(inlink, in_nb_samples, in_nb_samples, &frame); if (ret < 0) return ret; if (ret > 0) return afilter_frame(inlink, frame); } - if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) { + if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &s->eof_pts)) { if (status == AVERROR_EOF) { s->size = s->nb_samples; s->eof = 1; @@ -241,7 +265,7 @@ static int aactivate(AVFilterContext *ctx) } if (s->eof && (!s->loop || !s->size)) { - ff_outlink_set_status(outlink, AVERROR_EOF, s->duration); + ff_outlink_set_status(outlink, AVERROR_EOF, s->eof_pts + s->pts_offset); return 0; } @@ -259,7 +283,8 @@ static int aactivate(AVFilterContext *ctx) static const AVOption aloop_options[] = { { "loop", "number of loops", OFFSET(loop), AV_OPT_TYPE_INT, {.i64 = 0 }, -1, INT_MAX, AFLAGS }, { "size", "max number of samples to loop", OFFSET(size), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT32_MAX, AFLAGS }, - { "start", "set the loop start sample", OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, AFLAGS }, + { "start", "set the loop start sample", OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0 }, -1, INT64_MAX, AFLAGS }, + { "time", "set the loop start time", OFFSET(time), AV_OPT_TYPE_DURATION, {.i64=INT64_MAX}, INT64_MIN, INT64_MAX, AFLAGS }, { NULL } }; @@ -273,13 +298,6 @@ static const AVFilterPad ainputs[] = { }, }; -static const AVFilterPad aoutputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_aloop = { .name = "aloop", .description = NULL_IF_CONFIG_SMALL("Loop audio samples."), @@ -288,7 +306,7 @@ const AVFilter ff_af_aloop = { .activate = aactivate, .uninit = auninit, FILTER_INPUTS(ainputs), - FILTER_OUTPUTS(aoutputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; #endif /* CONFIG_ALOOP_FILTER */ @@ -298,6 +316,8 @@ static av_cold int init(AVFilterContext *ctx) { LoopContext *s = ctx->priv; + s->time_pts = AV_NOPTS_VALUE; + s->frames = av_calloc(s->size, sizeof(*s->frames)); if (!s->frames) return AVERROR(ENOMEM); @@ -307,14 +327,19 @@ static av_cold int init(AVFilterContext *ctx) return 0; } -static av_cold void uninit(AVFilterContext *ctx) +static void free_frames(AVFilterContext *ctx) { LoopContext *s = ctx->priv; - int i; - for (i = 0; i < s->nb_frames; i++) + for (int i = 0; i < s->nb_frames; i++) av_frame_free(&s->frames[i]); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + LoopContext *s = ctx->priv; + free_frames(ctx); av_freep(&s->frames); s->nb_frames = 0; } @@ -323,35 +348,24 @@ static int push_frame(AVFilterContext *ctx) { AVFilterLink *outlink = ctx->outputs[0]; LoopContext *s = ctx->priv; - int64_t pts, duration; + AVFrame *out; int ret; - AVFrame *out = av_frame_clone(s->frames[s->current_frame]); - + out = av_frame_clone(s->frames[s->current_frame]); if (!out) return AVERROR(ENOMEM); - out->pts += s->duration - s->start_pts; -#if FF_API_PKT_DURATION -FF_DISABLE_DEPRECATION_WARNINGS - if (out->pkt_duration) - duration = out->pkt_duration; - else -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (out->duration) - duration = out->duration; - else - duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); - pts = out->pts + duration; + out->pts += s->pts_offset; ret = ff_filter_frame(outlink, out); s->current_frame++; if (s->current_frame >= s->nb_frames) { - s->duration = pts; s->current_frame = 0; + s->pts_offset += s->duration; if (s->loop > 0) s->loop--; + if (s->loop == 0) + free_frames(ctx); } return ret; @@ -365,35 +379,30 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) int64_t duration; int ret = 0; - if (inlink->frame_count_out >= s->start && s->size > 0 && s->loop != 0) { + if (((s->start >= 0 && inlink->frame_count_out >= s->start) || + (s->time_pts != AV_NOPTS_VALUE && + frame->pts >= s->time_pts)) && + s->size > 0 && s->loop != 0) { if (s->nb_frames < s->size) { - if (!s->nb_frames) - s->start_pts = frame->pts; s->frames[s->nb_frames] = av_frame_clone(frame); if (!s->frames[s->nb_frames]) { av_frame_free(&frame); return AVERROR(ENOMEM); } s->nb_frames++; -#if FF_API_PKT_DURATION -FF_DISABLE_DEPRECATION_WARNINGS - if (frame->pkt_duration) - duration = frame->pkt_duration; - else -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (frame->duration) duration = frame->duration; else duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); - s->duration = frame->pts + duration; + s->duration += duration; + s->pts_offset = s->duration; ret = ff_filter_frame(outlink, frame); } else { av_frame_free(&frame); ret = push_frame(ctx); } } else { - frame->pts += s->duration; + frame->pts += s->pts_offset - s->duration; ret = ff_filter_frame(outlink, frame); } @@ -407,9 +416,15 @@ static int activate(AVFilterContext *ctx) LoopContext *s = ctx->priv; AVFrame *frame = NULL; int ret, status; - int64_t pts; - FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + ret = ff_outlink_get_status(outlink); + if (ret) { + ff_inlink_set_status(inlink, ret); + free_frames(ctx); + return 0; + } + + update_time(ctx, inlink->time_base); if (!s->eof && (s->nb_frames < s->size || !s->loop || !s->size)) { ret = ff_inlink_consume_frame(inlink, &frame); @@ -419,7 +434,7 @@ static int activate(AVFilterContext *ctx) return filter_frame(inlink, frame); } - if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &pts)) { + if (!s->eof && ff_inlink_acknowledge_status(inlink, &status, &s->eof_pts)) { if (status == AVERROR_EOF) { s->size = s->nb_frames; s->eof = 1; @@ -427,7 +442,8 @@ static int activate(AVFilterContext *ctx) } if (s->eof && (!s->loop || !s->size)) { - ff_outlink_set_status(outlink, AVERROR_EOF, s->duration); + ff_outlink_set_status(outlink, AVERROR_EOF, s->eof_pts + s->pts_offset); + free_frames(ctx); return 0; } @@ -445,26 +461,13 @@ static int activate(AVFilterContext *ctx) static const AVOption loop_options[] = { { "loop", "number of loops", OFFSET(loop), AV_OPT_TYPE_INT, {.i64 = 0 }, -1, INT_MAX, VFLAGS }, { "size", "max number of frames to loop", OFFSET(size), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT16_MAX, VFLAGS }, - { "start", "set the loop start frame", OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, VFLAGS }, + { "start", "set the loop start frame", OFFSET(start), AV_OPT_TYPE_INT64, {.i64 = 0 }, -1, INT64_MAX, VFLAGS }, + { "time", "set the loop start time", OFFSET(time), AV_OPT_TYPE_DURATION, {.i64=INT64_MAX}, INT64_MIN, INT64_MAX, VFLAGS }, { NULL } }; AVFILTER_DEFINE_CLASS(loop); -static const AVFilterPad inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_loop = { .name = "loop", .description = NULL_IF_CONFIG_SMALL("Loop video frames."), @@ -473,7 +476,7 @@ const AVFilter ff_vf_loop = { .init = init, .uninit = uninit, .activate = activate, - FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_INPUTS(ff_video_default_filterpad), + FILTER_OUTPUTS(ff_video_default_filterpad), }; #endif /* CONFIG_LOOP_FILTER */ diff --git a/libavfilter/f_metadata.c b/libavfilter/f_metadata.c index 4b7cfb0cb74..b6d548612bf 100644 --- a/libavfilter/f_metadata.c +++ b/libavfilter/f_metadata.c @@ -36,7 +36,6 @@ #include "libavformat/avio.h" #include "avfilter.h" #include "audio.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -101,22 +100,22 @@ typedef struct MetadataContext { #define OFFSET(x) offsetof(MetadataContext, x) #define DEFINE_OPTIONS(filt_name, FLAGS) \ static const AVOption filt_name##_options[] = { \ - { "mode", "set a mode of operation", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, METADATA_NB-1, FLAGS, "mode" }, \ - { "select", "select frame", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_SELECT }, 0, 0, FLAGS, "mode" }, \ - { "add", "add new metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_ADD }, 0, 0, FLAGS, "mode" }, \ - { "modify", "modify metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_MODIFY }, 0, 0, FLAGS, "mode" }, \ - { "delete", "delete metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_DELETE }, 0, 0, FLAGS, "mode" }, \ - { "print", "print metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_PRINT }, 0, 0, FLAGS, "mode" }, \ + { "mode", "set a mode of operation", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, METADATA_NB-1, FLAGS, .unit = "mode" }, \ + { "select", "select frame", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_SELECT }, 0, 0, FLAGS, .unit = "mode" }, \ + { "add", "add new metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_ADD }, 0, 0, FLAGS, .unit = "mode" }, \ + { "modify", "modify metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_MODIFY }, 0, 0, FLAGS, .unit = "mode" }, \ + { "delete", "delete metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_DELETE }, 0, 0, FLAGS, .unit = "mode" }, \ + { "print", "print metadata", 0, AV_OPT_TYPE_CONST, {.i64 = METADATA_PRINT }, 0, 0, FLAGS, .unit = "mode" }, \ { "key", "set metadata key", OFFSET(key), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \ { "value", "set metadata value", OFFSET(value), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \ - { "function", "function for comparing values", OFFSET(function), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, METADATAF_NB-1, FLAGS, "function" }, \ - { "same_str", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_SAME_STR }, 0, 3, FLAGS, "function" }, \ - { "starts_with", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_STARTS_WITH }, 0, 0, FLAGS, "function" }, \ - { "less", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_LESS }, 0, 3, FLAGS, "function" }, \ - { "equal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EQUAL }, 0, 3, FLAGS, "function" }, \ - { "greater", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_GREATER }, 0, 3, FLAGS, "function" }, \ - { "expr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EXPR }, 0, 3, FLAGS, "function" }, \ - { "ends_with", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_ENDS_WITH }, 0, 0, FLAGS, "function" }, \ + { "function", "function for comparing values", OFFSET(function), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, METADATAF_NB-1, FLAGS, .unit = "function" }, \ + { "same_str", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_SAME_STR }, 0, 3, FLAGS, .unit = "function" }, \ + { "starts_with", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_STARTS_WITH }, 0, 0, FLAGS, .unit = "function" }, \ + { "less", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_LESS }, 0, 3, FLAGS, .unit = "function" }, \ + { "equal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EQUAL }, 0, 3, FLAGS, .unit = "function" }, \ + { "greater", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_GREATER }, 0, 3, FLAGS, .unit = "function" }, \ + { "expr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_EXPR }, 0, 3, FLAGS, .unit = "function" }, \ + { "ends_with", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = METADATAF_ENDS_WITH }, 0, 0, FLAGS, .unit = "function" }, \ { "expr", "set expression for expr function", OFFSET(expr_str), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, FLAGS }, \ { "file", "set file where to print metadata information", OFFSET(file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, \ { "direct", "reduce buffering when printing to user-set file or pipe", OFFSET(direct), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, \ @@ -377,13 +376,6 @@ static const AVFilterPad ainputs[] = { }, }; -static const AVFilterPad aoutputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_ametadata = { .name = "ametadata", .description = NULL_IF_CONFIG_SMALL("Manipulate audio frame metadata."), @@ -392,7 +384,7 @@ const AVFilter ff_af_ametadata = { .init = init, .uninit = uninit, FILTER_INPUTS(ainputs), - FILTER_OUTPUTS(aoutputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, }; @@ -411,13 +403,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_metadata = { .name = "metadata", .description = NULL_IF_CONFIG_SMALL("Manipulate video frame metadata."), @@ -426,7 +411,7 @@ const AVFilter ff_vf_metadata = { .init = init, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, }; diff --git a/libavfilter/f_perms.c b/libavfilter/f_perms.c index 021652cfe81..0e8c208272f 100644 --- a/libavfilter/f_perms.c +++ b/libavfilter/f_perms.c @@ -48,12 +48,12 @@ typedef struct PermsContext { #define TFLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_RUNTIME_PARAM static const AVOption options[] = { - { "mode", "select permissions mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_NONE}, MODE_NONE, NB_MODES-1, TFLAGS, "mode" }, - { "none", "do nothing", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_NONE}, 0, 0, TFLAGS, "mode" }, - { "ro", "set all output frames read-only", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_RO}, 0, 0, TFLAGS, "mode" }, - { "rw", "set all output frames writable", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_RW}, 0, 0, TFLAGS, "mode" }, - { "toggle", "switch permissions", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_TOGGLE}, 0, 0, TFLAGS, "mode" }, - { "random", "set permissions randomly", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_RANDOM}, 0, 0, TFLAGS, "mode" }, + { "mode", "select permissions mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_NONE}, MODE_NONE, NB_MODES-1, TFLAGS, .unit = "mode" }, + { "none", "do nothing", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_NONE}, 0, 0, TFLAGS, .unit = "mode" }, + { "ro", "set all output frames read-only", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_RO}, 0, 0, TFLAGS, .unit = "mode" }, + { "rw", "set all output frames writable", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_RW}, 0, 0, TFLAGS, .unit = "mode" }, + { "toggle", "switch permissions", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_TOGGLE}, 0, 0, TFLAGS, .unit = "mode" }, + { "random", "set permissions randomly", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_RANDOM}, 0, 0, TFLAGS, .unit = "mode" }, { "seed", "set the seed for the random mode", OFFSET(random_seed), AV_OPT_TYPE_INT64, {.i64 = -1}, -1, UINT32_MAX, FLAGS }, { NULL } }; @@ -125,13 +125,6 @@ static const AVFilterPad aperms_inputs[] = { }, }; -static const AVFilterPad aperms_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_aperms = { .name = "aperms", .description = NULL_IF_CONFIG_SMALL("Set permissions for the output audio frame."), @@ -139,7 +132,7 @@ const AVFilter ff_af_aperms = { .init = init, .priv_size = sizeof(PermsContext), FILTER_INPUTS(aperms_inputs), - FILTER_OUTPUTS(aperms_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, .process_command = ff_filter_process_command, @@ -156,20 +149,13 @@ static const AVFilterPad perms_inputs[] = { }, }; -static const AVFilterPad perms_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_perms = { .name = "perms", .description = NULL_IF_CONFIG_SMALL("Set permissions for the output video frame."), .init = init, .priv_size = sizeof(PermsContext), FILTER_INPUTS(perms_inputs), - FILTER_OUTPUTS(perms_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &perms_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, diff --git a/libavfilter/f_realtime.c b/libavfilter/f_realtime.c index ef713474eaf..83793bbe150 100644 --- a/libavfilter/f_realtime.c +++ b/libavfilter/f_realtime.c @@ -22,8 +22,10 @@ #include "libavutil/opt.h" #include "libavutil/time.h" +#include "audio.h" #include "avfilter.h" #include "internal.h" +#include "video.h" #include typedef struct RealtimeContext { @@ -85,13 +87,6 @@ static const AVFilterPad avfilter_vf_realtime_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_realtime_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_realtime = { .name = "realtime", .description = NULL_IF_CONFIG_SMALL("Slow down filtering to match realtime."), @@ -99,7 +94,7 @@ const AVFilter ff_vf_realtime = { .priv_class = &realtime_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(avfilter_vf_realtime_inputs), - FILTER_OUTPUTS(avfilter_vf_realtime_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .process_command = ff_filter_process_command, }; #endif /* CONFIG_REALTIME_FILTER */ @@ -114,13 +109,6 @@ static const AVFilterPad arealtime_inputs[] = { }, }; -static const AVFilterPad arealtime_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_arealtime = { .name = "arealtime", .description = NULL_IF_CONFIG_SMALL("Slow down filtering to match realtime."), @@ -128,7 +116,7 @@ const AVFilter ff_af_arealtime = { .priv_size = sizeof(RealtimeContext), .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(arealtime_inputs), - FILTER_OUTPUTS(arealtime_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), .process_command = ff_filter_process_command, }; #endif /* CONFIG_AREALTIME_FILTER */ diff --git a/libavfilter/f_reverse.c b/libavfilter/f_reverse.c index 0ab2a3ab293..744f4a3cc83 100644 --- a/libavfilter/f_reverse.c +++ b/libavfilter/f_reverse.c @@ -20,11 +20,8 @@ #include "config_components.h" -#include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" #define DEFAULT_LENGTH 300 @@ -269,7 +266,8 @@ static int areverse_request_frame(AVFilterLink *outlink) AVFrame *out = s->frames[s->nb_frames - 1]; out->duration = s->duration[s->flush_idx]; out->pts = s->pts[s->flush_idx++] - s->nb_samples; - s->nb_samples += s->pts[s->flush_idx] - s->pts[s->flush_idx - 1] - out->nb_samples; + if (s->nb_frames > 1) + s->nb_samples += s->pts[s->flush_idx] - s->pts[s->flush_idx - 1] - out->nb_samples; if (av_sample_fmt_is_planar(out->format)) reverse_samples_planar(out); diff --git a/libavfilter/f_select.c b/libavfilter/f_select.c index 1cfe2d59e5e..9b330a06737 100644 --- a/libavfilter/f_select.c +++ b/libavfilter/f_select.c @@ -82,7 +82,9 @@ static const char *const var_names[] = { "prev_selected_n", ///< number of the last selected frame "key", ///< tell if the frame is a key frame +#if FF_API_FRAME_PKT "pos", ///< original position in the file of the frame +#endif "scene", @@ -134,7 +136,9 @@ enum var_name { VAR_PREV_SELECTED_N, VAR_KEY, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_SCENE, @@ -229,6 +233,7 @@ static int config_input(AVFilterLink *inlink) select->var_values[VAR_TB] = av_q2d(inlink->time_base); + select->var_values[VAR_PREV_SELECTED_N] = NAN; select->var_values[VAR_PREV_PTS] = NAN; select->var_values[VAR_PREV_SELECTED_PTS] = NAN; select->var_values[VAR_PREV_SELECTED_T] = NAN; @@ -292,7 +297,6 @@ static double get_scene_score(AVFilterContext *ctx, AVFrame *frame) count += select->width[plane] * select->height[plane]; } - emms_c(); mafd = (double)sad / count / (1ULL << (select->bitdepth - 8)); diff = fabs(mafd - select->prev_mafd); ret = av_clipf(FFMIN(mafd, diff) / 100., 0, 1); @@ -339,8 +343,12 @@ static void select_frame(AVFilterContext *ctx, AVFrame *frame) select->var_values[VAR_N ] = inlink->frame_count_out; select->var_values[VAR_PTS] = TS2D(frame->pts); select->var_values[VAR_T ] = TS2D(frame->pts) * av_q2d(inlink->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS select->var_values[VAR_POS] = frame->pkt_pos == -1 ? NAN : frame->pkt_pos; - select->var_values[VAR_KEY] = frame->key_frame; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + select->var_values[VAR_KEY] = !!(frame->flags & AV_FRAME_FLAG_KEY); select->var_values[VAR_CONCATDEC_SELECT] = get_concatdec_select(frame, av_rescale_q(frame->pts, inlink->time_base, AV_TIME_BASE_Q)); switch (inlink->type) { @@ -350,8 +358,8 @@ static void select_frame(AVFilterContext *ctx, AVFrame *frame) case AVMEDIA_TYPE_VIDEO: select->var_values[VAR_INTERLACE_TYPE] = - !frame->interlaced_frame ? INTERLACE_TYPE_P : - frame->top_field_first ? INTERLACE_TYPE_T : INTERLACE_TYPE_B; + !(frame->flags & AV_FRAME_FLAG_INTERLACED) ? INTERLACE_TYPE_P : + (frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? INTERLACE_TYPE_T : INTERLACE_TYPE_B; select->var_values[VAR_PICT_TYPE] = frame->pict_type; if (select->do_scene_detect) { char buf[32]; @@ -369,13 +377,13 @@ static void select_frame(AVFilterContext *ctx, AVFrame *frame) select->var_values[VAR_N], select->var_values[VAR_PTS], select->var_values[VAR_T], - frame->key_frame); + !!(frame->flags & AV_FRAME_FLAG_KEY)); switch (inlink->type) { case AVMEDIA_TYPE_VIDEO: av_log(inlink->dst, AV_LOG_DEBUG, " interlace_type:%c pict_type:%c scene:%f", - (!frame->interlaced_frame) ? 'P' : - frame->top_field_first ? 'T' : 'B', + !(frame->flags & AV_FRAME_FLAG_INTERLACED) ? 'P' : + (frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 'T' : 'B', av_get_picture_type_char(frame->pict_type), select->var_values[VAR_SCENE]); break; diff --git a/libavfilter/f_sendcmd.c b/libavfilter/f_sendcmd.c index 857b8413131..922ce78287d 100644 --- a/libavfilter/f_sendcmd.c +++ b/libavfilter/f_sendcmd.c @@ -43,7 +43,9 @@ static const char *const var_names[] = { "N", /* frame number */ "T", /* frame time in seconds */ +#if FF_API_FRAME_PKT "POS", /* original position in the file of the frame */ +#endif "PTS", /* frame pts */ "TS", /* interval start time in seconds */ "TE", /* interval end time in seconds */ @@ -56,7 +58,9 @@ static const char *const var_names[] = { enum var_name { VAR_N, VAR_T, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_PTS, VAR_TS, VAR_TE, @@ -531,7 +535,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *ref) double current = TS2T(ref->pts, inlink->time_base); var_values[VAR_N] = inlink->frame_count_in; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS var_values[VAR_POS] = ref->pkt_pos == -1 ? NAN : ref->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif var_values[VAR_PTS] = TS2D(ref->pts); var_values[VAR_T] = current; var_values[VAR_TS] = start; @@ -592,13 +600,6 @@ static const AVFilterPad sendcmd_inputs[] = { }, }; -static const AVFilterPad sendcmd_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_sendcmd = { .name = "sendcmd", .description = NULL_IF_CONFIG_SMALL("Send commands to filters."), @@ -607,7 +608,7 @@ const AVFilter ff_vf_sendcmd = { .priv_size = sizeof(SendCmdContext), .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(sendcmd_inputs), - FILTER_OUTPUTS(sendcmd_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &sendcmd_class, }; @@ -623,13 +624,6 @@ static const AVFilterPad asendcmd_inputs[] = { }, }; -static const AVFilterPad asendcmd_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asendcmd = { .name = "asendcmd", .description = NULL_IF_CONFIG_SMALL("Send commands to filters."), @@ -639,7 +633,7 @@ const AVFilter ff_af_asendcmd = { .priv_size = sizeof(SendCmdContext), .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(asendcmd_inputs), - FILTER_OUTPUTS(asendcmd_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; #endif diff --git a/libavfilter/f_sidedata.c b/libavfilter/f_sidedata.c index 82bbaa0e7e0..fe9607ed521 100644 --- a/libavfilter/f_sidedata.c +++ b/libavfilter/f_sidedata.c @@ -27,9 +27,10 @@ #include "libavutil/internal.h" #include "libavutil/frame.h" #include "libavutil/opt.h" +#include "audio.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" enum SideDataMode { SIDEDATA_SELECT, @@ -47,31 +48,31 @@ typedef struct SideDataContext { #define OFFSET(x) offsetof(SideDataContext, x) #define DEFINE_OPTIONS(filt_name, FLAGS) \ static const AVOption filt_name##_options[] = { \ - { "mode", "set a mode of operation", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, SIDEDATA_NB-1, FLAGS, "mode" }, \ - { "select", "select frame", 0, AV_OPT_TYPE_CONST, {.i64 = SIDEDATA_SELECT }, 0, 0, FLAGS, "mode" }, \ - { "delete", "delete side data", 0, AV_OPT_TYPE_CONST, {.i64 = SIDEDATA_DELETE }, 0, 0, FLAGS, "mode" }, \ - { "type", "set side data type", OFFSET(type), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, FLAGS, "type" }, \ - { "PANSCAN", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_PANSCAN }, 0, 0, FLAGS, "type" }, \ - { "A53_CC", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_A53_CC }, 0, 0, FLAGS, "type" }, \ - { "STEREO3D", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_STEREO3D }, 0, 0, FLAGS, "type" }, \ - { "MATRIXENCODING", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_MATRIXENCODING }, 0, 0, FLAGS, "type" }, \ - { "DOWNMIX_INFO", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_DOWNMIX_INFO }, 0, 0, FLAGS, "type" }, \ - { "REPLAYGAIN", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_REPLAYGAIN }, 0, 0, FLAGS, "type" }, \ - { "DISPLAYMATRIX", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_DISPLAYMATRIX }, 0, 0, FLAGS, "type" }, \ - { "AFD", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_AFD }, 0, 0, FLAGS, "type" }, \ - { "MOTION_VECTORS", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_MOTION_VECTORS }, 0, 0, FLAGS, "type" }, \ - { "SKIP_SAMPLES", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_SKIP_SAMPLES }, 0, 0, FLAGS, "type" }, \ - { "AUDIO_SERVICE_TYPE", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_AUDIO_SERVICE_TYPE }, 0, 0, FLAGS, "type" }, \ - { "MASTERING_DISPLAY_METADATA", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_MASTERING_DISPLAY_METADATA }, 0, 0, FLAGS, "type" }, \ - { "GOP_TIMECODE", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_GOP_TIMECODE }, 0, 0, FLAGS, "type" }, \ - { "SPHERICAL", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_SPHERICAL }, 0, 0, FLAGS, "type" }, \ - { "CONTENT_LIGHT_LEVEL", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_CONTENT_LIGHT_LEVEL }, 0, 0, FLAGS, "type" }, \ - { "ICC_PROFILE", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_ICC_PROFILE }, 0, 0, FLAGS, "type" }, \ - { "S12M_TIMECOD", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_S12M_TIMECODE }, 0, 0, FLAGS, "type" }, \ - { "DYNAMIC_HDR_PLUS", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_DYNAMIC_HDR_PLUS }, 0, 0, FLAGS, "type" }, \ - { "REGIONS_OF_INTEREST", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_REGIONS_OF_INTEREST }, 0, 0, FLAGS, "type" }, \ - { "DETECTION_BOUNDING_BOXES", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_DETECTION_BBOXES }, 0, 0, FLAGS, "type" }, \ - { "SEI_UNREGISTERED", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_SEI_UNREGISTERED }, 0, 0, FLAGS, "type" }, \ + { "mode", "set a mode of operation", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, SIDEDATA_NB-1, FLAGS, .unit = "mode" }, \ + { "select", "select frame", 0, AV_OPT_TYPE_CONST, {.i64 = SIDEDATA_SELECT }, 0, 0, FLAGS, .unit = "mode" }, \ + { "delete", "delete side data", 0, AV_OPT_TYPE_CONST, {.i64 = SIDEDATA_DELETE }, 0, 0, FLAGS, .unit = "mode" }, \ + { "type", "set side data type", OFFSET(type), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, FLAGS, .unit = "type" }, \ + { "PANSCAN", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_PANSCAN }, 0, 0, FLAGS, .unit = "type" }, \ + { "A53_CC", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_A53_CC }, 0, 0, FLAGS, .unit = "type" }, \ + { "STEREO3D", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_STEREO3D }, 0, 0, FLAGS, .unit = "type" }, \ + { "MATRIXENCODING", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_MATRIXENCODING }, 0, 0, FLAGS, .unit = "type" }, \ + { "DOWNMIX_INFO", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_DOWNMIX_INFO }, 0, 0, FLAGS, .unit = "type" }, \ + { "REPLAYGAIN", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_REPLAYGAIN }, 0, 0, FLAGS, .unit = "type" }, \ + { "DISPLAYMATRIX", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_DISPLAYMATRIX }, 0, 0, FLAGS, .unit = "type" }, \ + { "AFD", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_AFD }, 0, 0, FLAGS, .unit = "type" }, \ + { "MOTION_VECTORS", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_MOTION_VECTORS }, 0, 0, FLAGS, .unit = "type" }, \ + { "SKIP_SAMPLES", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_SKIP_SAMPLES }, 0, 0, FLAGS, .unit = "type" }, \ + { "AUDIO_SERVICE_TYPE", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_AUDIO_SERVICE_TYPE }, 0, 0, FLAGS, .unit = "type" }, \ + { "MASTERING_DISPLAY_METADATA", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_MASTERING_DISPLAY_METADATA }, 0, 0, FLAGS, .unit = "type" }, \ + { "GOP_TIMECODE", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_GOP_TIMECODE }, 0, 0, FLAGS, .unit = "type" }, \ + { "SPHERICAL", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_SPHERICAL }, 0, 0, FLAGS, .unit = "type" }, \ + { "CONTENT_LIGHT_LEVEL", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_CONTENT_LIGHT_LEVEL }, 0, 0, FLAGS, .unit = "type" }, \ + { "ICC_PROFILE", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_ICC_PROFILE }, 0, 0, FLAGS, .unit = "type" }, \ + { "S12M_TIMECOD", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_S12M_TIMECODE }, 0, 0, FLAGS, .unit = "type" }, \ + { "DYNAMIC_HDR_PLUS", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_DYNAMIC_HDR_PLUS }, 0, 0, FLAGS, .unit = "type" }, \ + { "REGIONS_OF_INTEREST", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_REGIONS_OF_INTEREST }, 0, 0, FLAGS, .unit = "type" }, \ + { "DETECTION_BOUNDING_BOXES", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_DETECTION_BBOXES }, 0, 0, FLAGS, .unit = "type" }, \ + { "SEI_UNREGISTERED", "", 0, AV_OPT_TYPE_CONST, {.i64 = AV_FRAME_DATA_SEI_UNREGISTERED }, 0, 0, FLAGS, .unit = "type" }, \ { NULL } \ } @@ -134,13 +135,6 @@ static const AVFilterPad ainputs[] = { }, }; -static const AVFilterPad aoutputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asidedata = { .name = "asidedata", .description = NULL_IF_CONFIG_SMALL("Manipulate audio frame side data."), @@ -148,7 +142,7 @@ const AVFilter ff_af_asidedata = { .priv_class = &asidedata_class, .init = init, FILTER_INPUTS(ainputs), - FILTER_OUTPUTS(aoutputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, }; @@ -167,13 +161,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_sidedata = { .name = "sidedata", .description = NULL_IF_CONFIG_SMALL("Manipulate video frame side data."), @@ -181,7 +168,7 @@ const AVFilter ff_vf_sidedata = { .priv_class = &sidedata_class, .init = init, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, }; diff --git a/libavfilter/f_streamselect.c b/libavfilter/f_streamselect.c index 1328a842f90..5fbb13a3238 100644 --- a/libavfilter/f_streamselect.c +++ b/libavfilter/f_streamselect.c @@ -119,11 +119,6 @@ static int config_output(AVFilterLink *outlink) case AVMEDIA_TYPE_AUDIO: outlink->sample_rate = inlink->sample_rate; outlink->ch_layout.nb_channels = inlink->ch_layout.nb_channels; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - outlink->channel_layout = inlink->channel_layout; -FF_ENABLE_DEPRECATION_WARNINGS -#endif break; } @@ -250,7 +245,7 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar if (ret < 0) return ret; - return avfilter_config_links(ctx); + return ff_filter_config_links(ctx); } return AVERROR(ENOSYS); } diff --git a/libavfilter/f_zmq.c b/libavfilter/f_zmq.c index 156670dad43..3829b55224f 100644 --- a/libavfilter/f_zmq.c +++ b/libavfilter/f_zmq.c @@ -217,13 +217,6 @@ static const AVFilterPad zmq_inputs[] = { }, }; -static const AVFilterPad zmq_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_zmq = { .name = "zmq", .description = NULL_IF_CONFIG_SMALL("Receive commands through ZMQ and broker them to filters."), @@ -231,7 +224,7 @@ const AVFilter ff_vf_zmq = { .uninit = uninit, .priv_size = sizeof(ZMQContext), FILTER_INPUTS(zmq_inputs), - FILTER_OUTPUTS(zmq_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &zmq_class, }; @@ -247,13 +240,6 @@ static const AVFilterPad azmq_inputs[] = { }, }; -static const AVFilterPad azmq_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_azmq = { .name = "azmq", .description = NULL_IF_CONFIG_SMALL("Receive commands through ZMQ and broker them to filters."), @@ -262,7 +248,7 @@ const AVFilter ff_af_azmq = { .uninit = uninit, .priv_size = sizeof(ZMQContext), FILTER_INPUTS(azmq_inputs), - FILTER_OUTPUTS(azmq_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; #endif diff --git a/libavfilter/fifo.c b/libavfilter/fifo.c deleted file mode 100644 index 1c7be88ae15..00000000000 --- a/libavfilter/fifo.c +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2007 Bobby Bingham - * - * 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 - * FIFO buffering filter - */ - -#include "libavutil/common.h" -#include "libavutil/mathematics.h" - -#include "audio.h" -#include "avfilter.h" -#include "internal.h" - -typedef struct Buf { - AVFrame *frame; - struct Buf *next; -} Buf; - -typedef struct FifoContext { - Buf root; - Buf *last; ///< last buffered frame - - /** - * When a specific number of output samples is requested, the partial - * buffer is stored here - */ - AVFrame *out; - int allocated_samples; ///< number of samples out was allocated for -} FifoContext; - -static av_cold int init(AVFilterContext *ctx) -{ - FifoContext *s = ctx->priv; - s->last = &s->root; - - return 0; -} - -static av_cold void uninit(AVFilterContext *ctx) -{ - FifoContext *s = ctx->priv; - Buf *buf, *tmp; - - for (buf = s->root.next; buf; buf = tmp) { - tmp = buf->next; - av_frame_free(&buf->frame); - av_free(buf); - } - - av_frame_free(&s->out); -} - -static int add_to_queue(AVFilterLink *inlink, AVFrame *frame) -{ - FifoContext *s = inlink->dst->priv; - - s->last->next = av_mallocz(sizeof(Buf)); - if (!s->last->next) { - av_frame_free(&frame); - return AVERROR(ENOMEM); - } - - s->last = s->last->next; - s->last->frame = frame; - - return 0; -} - -static void queue_pop(FifoContext *s) -{ - Buf *tmp = s->root.next->next; - if (s->last == s->root.next) - s->last = &s->root; - av_freep(&s->root.next); - s->root.next = tmp; -} - -static int request_frame(AVFilterLink *outlink) -{ - FifoContext *s = outlink->src->priv; - int ret = 0; - - if (!s->root.next) { - if ((ret = ff_request_frame(outlink->src->inputs[0])) < 0) - return ret; - if (!s->root.next) - return 0; - } - ret = ff_filter_frame(outlink, s->root.next->frame); - queue_pop(s); - return ret; -} - -static const AVFilterPad avfilter_vf_fifo_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = add_to_queue, - }, -}; - -static const AVFilterPad avfilter_vf_fifo_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .request_frame = request_frame, - }, -}; - -const AVFilter ff_vf_fifo = { - .name = "fifo", - .description = NULL_IF_CONFIG_SMALL("Buffer input images and send them when they are requested."), - .init = init, - .uninit = uninit, - .priv_size = sizeof(FifoContext), - .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_vf_fifo_inputs), - FILTER_OUTPUTS(avfilter_vf_fifo_outputs), -}; - -static const AVFilterPad avfilter_af_afifo_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = add_to_queue, - }, -}; - -static const AVFilterPad avfilter_af_afifo_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - .request_frame = request_frame, - }, -}; - -const AVFilter ff_af_afifo = { - .name = "afifo", - .description = NULL_IF_CONFIG_SMALL("Buffer input frames and send them when they are requested."), - .init = init, - .uninit = uninit, - .priv_size = sizeof(FifoContext), - .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_af_afifo_inputs), - FILTER_OUTPUTS(avfilter_af_afifo_outputs), -}; diff --git a/libavfilter/formats.c b/libavfilter/formats.c index e8c2888c0c0..d3f4c7f09c6 100644 --- a/libavfilter/formats.c +++ b/libavfilter/formats.c @@ -209,11 +209,10 @@ static int merge_samplerates(void *a, void *b) /** * See merge_pix_fmts(). */ -static int merge_channel_layouts(void *va, void *vb) +static int merge_channel_layouts_internal(AVFilterChannelLayouts *a, + AVFilterChannelLayouts *b, int check) { - AVFilterChannelLayouts *a = va; - AVFilterChannelLayouts *b = vb; - AVChannelLayout *channel_layouts; + AVChannelLayout *channel_layouts = NULL; unsigned a_all = a->all_layouts + a->all_counts; unsigned b_all = b->all_layouts + b->all_counts; int ret_max, ret_nb = 0, i, j, round; @@ -231,8 +230,11 @@ static int merge_channel_layouts(void *va, void *vb) if (a_all == 1 && !b_all) { /* keep only known layouts in b; works also for b_all = 1 */ for (i = j = 0; i < b->nb_channel_layouts; i++) - if (KNOWN(&b->channel_layouts[i]) && i != j++) + if (KNOWN(&b->channel_layouts[i]) && i != j++) { + if (check) + return 1; av_channel_layout_copy(&b->channel_layouts[j], &b->channel_layouts[i]); + } /* Not optimal: the unknown layouts of b may become known after another merge. */ if (!j) @@ -244,7 +246,7 @@ static int merge_channel_layouts(void *va, void *vb) } ret_max = a->nb_channel_layouts + b->nb_channel_layouts; - if (!(channel_layouts = av_calloc(ret_max, sizeof(*channel_layouts)))) + if (!check && !(channel_layouts = av_calloc(ret_max, sizeof(*channel_layouts)))) return AVERROR(ENOMEM); /* a[known] intersect b[known] */ @@ -253,6 +255,8 @@ static int merge_channel_layouts(void *va, void *vb) continue; for (j = 0; j < b->nb_channel_layouts; j++) { if (!av_channel_layout_compare(&a->channel_layouts[i], &b->channel_layouts[j])) { + if (check) + return 1; av_channel_layout_copy(&channel_layouts[ret_nb++], &a->channel_layouts[i]); av_channel_layout_uninit(&a->channel_layouts[i]); av_channel_layout_uninit(&b->channel_layouts[j]); @@ -269,8 +273,11 @@ static int merge_channel_layouts(void *va, void *vb) continue; bfmt = FF_COUNT2LAYOUT(fmt->nb_channels); for (j = 0; j < b->nb_channel_layouts; j++) - if (!av_channel_layout_compare(&b->channel_layouts[j], &bfmt)) + if (!av_channel_layout_compare(&b->channel_layouts[j], &bfmt)) { + if (check) + return 1; av_channel_layout_copy(&channel_layouts[ret_nb++], fmt); + } } /* 1st round: swap to prepare 2nd round; 2nd round: put it back */ FFSWAP(AVFilterChannelLayouts *, a, b); @@ -280,8 +287,11 @@ static int merge_channel_layouts(void *va, void *vb) if (KNOWN(&a->channel_layouts[i])) continue; for (j = 0; j < b->nb_channel_layouts; j++) - if (!av_channel_layout_compare(&a->channel_layouts[i], &b->channel_layouts[j])) + if (!av_channel_layout_compare(&a->channel_layouts[i], &b->channel_layouts[j])) { + if (check) + return 1; av_channel_layout_copy(&channel_layouts[ret_nb++], &a->channel_layouts[i]); + } } if (!ret_nb) { @@ -300,19 +310,64 @@ static int merge_channel_layouts(void *va, void *vb) return 1; } +static int can_merge_channel_layouts(const void *a, const void *b) +{ + return merge_channel_layouts_internal((AVFilterChannelLayouts *)a, + (AVFilterChannelLayouts *)b, 1); +} + +static int merge_channel_layouts(void *a, void *b) +{ + return merge_channel_layouts_internal(a, b, 0); +} + +static int merge_generic_internal(AVFilterFormats *a, + AVFilterFormats *b, int check) +{ + av_assert2(check || (a->refcount && b->refcount)); + + if (a == b) + return 1; + + MERGE_FORMATS(a, b, formats, nb_formats, AVFilterFormats, check, 0); + + return 1; +} + +static int can_merge_generic(const void *a, const void *b) +{ + return merge_generic_internal((AVFilterFormats *)a, + (AVFilterFormats *)b, 1); +} + +static int merge_generic(void *a, void *b) +{ + return merge_generic_internal(a, b, 0); +} + static const AVFilterFormatsMerger mergers_video[] = { { .offset = offsetof(AVFilterFormatsConfig, formats), .merge = merge_pix_fmts, .can_merge = can_merge_pix_fmts, }, + { + .offset = offsetof(AVFilterFormatsConfig, color_spaces), + .merge = merge_generic, + .can_merge = can_merge_generic, + }, + { + .offset = offsetof(AVFilterFormatsConfig, color_ranges), + .merge = merge_generic, + .can_merge = can_merge_generic, + }, }; static const AVFilterFormatsMerger mergers_audio[] = { { .offset = offsetof(AVFilterFormatsConfig, channel_layouts), .merge = merge_channel_layouts, - .can_merge = NULL, + .can_merge = can_merge_channel_layouts, }, { .offset = offsetof(AVFilterFormatsConfig, samplerates), @@ -573,6 +628,33 @@ AVFilterChannelLayouts *ff_all_channel_counts(void) return ret; } +AVFilterFormats *ff_all_color_spaces(void) +{ + AVFilterFormats *ret = NULL; + if (ff_add_format(&ret, AVCOL_SPC_UNSPECIFIED) < 0) + return NULL; + for (int csp = 0; csp < AVCOL_SPC_NB; csp++) { + if (csp == AVCOL_SPC_RESERVED || + csp == AVCOL_SPC_UNSPECIFIED) + continue; + if (ff_add_format(&ret, csp) < 0) + return NULL; + } + + return ret; +} + +AVFilterFormats *ff_all_color_ranges(void) +{ + AVFilterFormats *ret = NULL; + for (int range = 0; range < AVCOL_RANGE_NB; range++) { + if (ff_add_format(&ret, range) < 0) + return NULL; + } + + return ret; +} + #define FORMATS_REF(f, ref, unref_fn) \ void *tmp; \ \ @@ -742,6 +824,42 @@ int ff_set_common_all_samplerates(AVFilterContext *ctx) return ff_set_common_samplerates(ctx, ff_all_samplerates()); } +int ff_set_common_color_spaces(AVFilterContext *ctx, + AVFilterFormats *color_spaces) +{ + SET_COMMON_FORMATS(ctx, color_spaces, AVMEDIA_TYPE_VIDEO, + ff_formats_ref, ff_formats_unref); +} + +int ff_set_common_color_spaces_from_list(AVFilterContext *ctx, + const int *color_ranges) +{ + return ff_set_common_color_spaces(ctx, ff_make_format_list(color_ranges)); +} + +int ff_set_common_all_color_spaces(AVFilterContext *ctx) +{ + return ff_set_common_color_spaces(ctx, ff_all_color_spaces()); +} + +int ff_set_common_color_ranges(AVFilterContext *ctx, + AVFilterFormats *color_ranges) +{ + SET_COMMON_FORMATS(ctx, color_ranges, AVMEDIA_TYPE_VIDEO, + ff_formats_ref, ff_formats_unref); +} + +int ff_set_common_color_ranges_from_list(AVFilterContext *ctx, + const int *color_ranges) +{ + return ff_set_common_color_ranges(ctx, ff_make_format_list(color_ranges)); +} + +int ff_set_common_all_color_ranges(AVFilterContext *ctx) +{ + return ff_set_common_color_ranges(ctx, ff_all_color_ranges()); +} + /** * A helper for query_formats() which sets all links to the same list of * formats. If there are no links hooked to this filter, the list of formats is @@ -787,16 +905,25 @@ int ff_default_query_formats(AVFilterContext *ctx) /* Intended fallthrough */ case FF_FILTER_FORMATS_PASSTHROUGH: case FF_FILTER_FORMATS_QUERY_FUNC: - type = ctx->nb_inputs ? ctx->inputs [0]->type : - ctx->nb_outputs ? ctx->outputs[0]->type : AVMEDIA_TYPE_VIDEO; - formats = ff_all_formats(type); + type = AVMEDIA_TYPE_UNKNOWN; + formats = ff_all_formats(ctx->nb_inputs ? ctx->inputs [0]->type : + ctx->nb_outputs ? ctx->outputs[0]->type : + AVMEDIA_TYPE_VIDEO); break; } ret = ff_set_common_formats(ctx, formats); if (ret < 0) return ret; - if (type == AVMEDIA_TYPE_AUDIO) { + if (type != AVMEDIA_TYPE_AUDIO) { + ret = ff_set_common_all_color_spaces(ctx); + if (ret < 0) + return ret; + ret = ff_set_common_all_color_ranges(ctx); + if (ret < 0) + return ret; + } + if (type != AVMEDIA_TYPE_VIDEO) { ret = ff_set_common_all_channel_counts(ctx); if (ret < 0) return ret; @@ -845,24 +972,8 @@ int ff_parse_channel_layout(AVChannelLayout *ret, int *nret, const char *arg, res = av_channel_layout_from_string(&chlayout, arg); if (res < 0) { -#if FF_API_OLD_CHANNEL_LAYOUT - int64_t mask; - int nb_channels; -FF_DISABLE_DEPRECATION_WARNINGS - if (av_get_extended_channel_layout(arg, &mask, &nb_channels) < 0) { -#endif - av_log(log_ctx, AV_LOG_ERROR, "Invalid channel layout '%s'\n", arg); - return AVERROR(EINVAL); -#if FF_API_OLD_CHANNEL_LAYOUT - } -FF_ENABLE_DEPRECATION_WARNINGS - av_log(log_ctx, AV_LOG_WARNING, "Channel layout '%s' uses a deprecated syntax.\n", - arg); - if (mask) - av_channel_layout_from_mask(&chlayout, mask); - else - chlayout = (AVChannelLayout) { .order = AV_CHANNEL_ORDER_UNSPEC, .nb_channels = nb_channels }; -#endif + av_log(log_ctx, AV_LOG_ERROR, "Invalid channel layout '%s'\n", arg); + return AVERROR(EINVAL); } if (chlayout.order == AV_CHANNEL_ORDER_UNSPEC && !nret) { @@ -914,6 +1025,22 @@ int ff_formats_check_sample_rates(void *log, const AVFilterFormats *fmts) return check_list(log, "sample rate", fmts); } +int ff_formats_check_color_spaces(void *log, const AVFilterFormats *fmts) +{ + for (int i = 0; fmts && i < fmts->nb_formats; i++) { + if (fmts->formats[i] == AVCOL_SPC_RESERVED) { + av_log(log, AV_LOG_ERROR, "Invalid color range\n"); + return AVERROR(EINVAL); + } + } + return check_list(log, "color space", fmts); +} + +int ff_formats_check_color_ranges(void *log, const AVFilterFormats *fmts) +{ + return check_list(log, "color range", fmts); +} + static int layouts_compatible(const AVChannelLayout *a, const AVChannelLayout *b) { return !av_channel_layout_compare(a, b) || diff --git a/libavfilter/formats.h b/libavfilter/formats.h index 22224dce2d7..82b3af4be1b 100644 --- a/libavfilter/formats.h +++ b/libavfilter/formats.h @@ -130,6 +130,20 @@ AVFilterChannelLayouts *ff_all_channel_counts(void); av_warn_unused_result AVFilterChannelLayouts *ff_make_channel_layout_list(const AVChannelLayout *fmts); +/** + * Construct an AVFilterFormats representing all possible color spaces. + * + * Note: This list does not include AVCOL_SPC_RESERVED. + */ +av_warn_unused_result +AVFilterFormats *ff_all_color_spaces(void); + +/** + * Construct an AVFilterFormats representing all possible color ranges. + */ +av_warn_unused_result +AVFilterFormats *ff_all_color_ranges(void); + /** * Helpers for query_formats() which set all free audio links to the same list * of channel layouts/sample rates. If there are no links hooked to this list, @@ -165,6 +179,38 @@ int ff_set_common_samplerates_from_list(AVFilterContext *ctx, av_warn_unused_result int ff_set_common_all_samplerates(AVFilterContext *ctx); +av_warn_unused_result +int ff_set_common_color_spaces(AVFilterContext *ctx, + AVFilterFormats *color_spaces); +/** + * Equivalent to ff_set_common_color_spaces(ctx, ff_make_format_list(color_spaces)) + */ +av_warn_unused_result +int ff_set_common_color_spaces_from_list(AVFilterContext *ctx, + const int *color_spaces); + +/** + * Equivalent to ff_set_common_color_spaces(ctx, ff_all_color_spaces()) + */ +av_warn_unused_result +int ff_set_common_all_color_spaces(AVFilterContext *ctx); + +av_warn_unused_result +int ff_set_common_color_ranges(AVFilterContext *ctx, + AVFilterFormats *color_ranges); +/** + * Equivalent to ff_set_common_color_ranges(ctx, ff_make_format_list(color_ranges)) + */ +av_warn_unused_result +int ff_set_common_color_ranges_from_list(AVFilterContext *ctx, + const int *color_ranges); + +/** + * Equivalent to ff_set_common_color_ranges(ctx, ff_all_color_ranges()) + */ +av_warn_unused_result +int ff_set_common_all_color_ranges(AVFilterContext *ctx); + /** * A helper for query_formats() which sets all links to the same list of * formats. If there are no links hooked to this filter, the list of formats is @@ -198,6 +244,10 @@ void ff_channel_layouts_unref(AVFilterChannelLayouts **ref); void ff_channel_layouts_changeref(AVFilterChannelLayouts **oldref, AVFilterChannelLayouts **newref); +/** + * Sets all remaining unset filter lists for all inputs/outputs to their + * corresponding `ff_all_*()` lists. + */ av_warn_unused_result int ff_default_query_formats(AVFilterContext *ctx); @@ -324,6 +374,14 @@ int ff_formats_check_sample_rates(void *log, const AVFilterFormats *fmts); */ int ff_formats_check_channel_layouts(void *log, const AVFilterChannelLayouts *fmts); +/** + * Check that fmts is a valid formats list for YUV colorspace metadata. + * + * In particular, check for duplicates. + */ +int ff_formats_check_color_spaces(void *log, const AVFilterFormats *fmts); +int ff_formats_check_color_ranges(void *log, const AVFilterFormats *fmts); + typedef struct AVFilterFormatMerger { unsigned offset; int (*merge)(void *a, void *b); diff --git a/libavfilter/framepool.c b/libavfilter/framepool.c index 04045890553..841caa04605 100644 --- a/libavfilter/framepool.c +++ b/libavfilter/framepool.c @@ -234,11 +234,6 @@ AVFrame *ff_frame_pool_get(FFFramePool *pool) break; case AVMEDIA_TYPE_AUDIO: frame->nb_samples = pool->nb_samples; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - frame->channels = pool->channels; -FF_ENABLE_DEPRECATION_WARNINGS -#endif frame->ch_layout.nb_channels = pool->channels; frame->format = pool->format; frame->linesize[0] = pool->linesize[0]; diff --git a/libavfilter/framequeue.c b/libavfilter/framequeue.c index 383c195b855..ace0dad6897 100644 --- a/libavfilter/framequeue.c +++ b/libavfilter/framequeue.c @@ -79,9 +79,8 @@ int ff_framequeue_add(FFFrameQueue *fq, AVFrame *frame) FFFrameBucket *nq = av_realloc_array(fq->queue, na, sizeof(*nq)); if (!nq) return AVERROR(ENOMEM); - if (fq->tail + fq->queued > fq->allocated) - memmove(nq + fq->allocated, nq, - (fq->tail + fq->queued - fq->allocated) * sizeof(*nq)); + if (fq->tail) + memmove(nq + fq->allocated, nq, fq->tail * sizeof(*nq)); fq->queue = nq; fq->allocated = na; } diff --git a/libavfilter/framesync.c b/libavfilter/framesync.c index 6cb4b21fed8..cf2f355c899 100644 --- a/libavfilter/framesync.c +++ b/libavfilter/framesync.c @@ -36,19 +36,19 @@ static const char *framesync_name(void *ptr) static const AVOption framesync_options[] = { { "eof_action", "Action to take when encountering EOF from secondary input ", OFFSET(opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT }, - EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, "eof_action" }, - { "repeat", "Repeat the previous frame.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, "eof_action" }, - { "endall", "End both streams.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, "eof_action" }, - { "pass", "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, .flags = FLAGS, "eof_action" }, + EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, .unit = "eof_action" }, + { "repeat", "Repeat the previous frame.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, .unit = "eof_action" }, + { "endall", "End both streams.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, .unit = "eof_action" }, + { "pass", "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, .flags = FLAGS, .unit = "eof_action" }, { "shortest", "force termination when the shortest input terminates", OFFSET(opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "repeatlast", "extend last frame of secondary streams beyond EOF", OFFSET(opt_repeatlast), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS }, { "ts_sync_mode", "How strictly to sync streams based on secondary input timestamps", OFFSET(opt_ts_sync_mode), AV_OPT_TYPE_INT, { .i64 = TS_DEFAULT }, - TS_DEFAULT, TS_NEAREST, .flags = FLAGS, "ts_sync_mode" }, + TS_DEFAULT, TS_NEAREST, .flags = FLAGS, .unit = "ts_sync_mode" }, { "default", "Frame from secondary input with the nearest lower or equal timestamp to the primary input frame", - 0, AV_OPT_TYPE_CONST, { .i64 = TS_DEFAULT }, .flags = FLAGS, "ts_sync_mode" }, + 0, AV_OPT_TYPE_CONST, { .i64 = TS_DEFAULT }, .flags = FLAGS, .unit = "ts_sync_mode" }, { "nearest", "Frame from secondary input with the absolute nearest timestamp to the primary input frame", - 0, AV_OPT_TYPE_CONST, { .i64 = TS_NEAREST }, .flags = FLAGS, "ts_sync_mode" }, + 0, AV_OPT_TYPE_CONST, { .i64 = TS_NEAREST }, .flags = FLAGS, .unit = "ts_sync_mode" }, { NULL } }; static const AVClass framesync_class = { @@ -354,8 +354,11 @@ static int consume_from_fifos(FFFrameSync *fs) int ff_framesync_activate(FFFrameSync *fs) { + AVFilterContext *ctx = fs->parent; int ret; + FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[0], ctx); + ret = framesync_advance(fs); if (ret < 0) return ret; diff --git a/libavfilter/graphdump.c b/libavfilter/graphdump.c index 80f7bf6c981..df79903c528 100644 --- a/libavfilter/graphdump.c +++ b/libavfilter/graphdump.c @@ -30,7 +30,6 @@ static int print_link_prop(AVBPrint *buf, AVFilterLink *link) { const char *format; - char layout[128]; AVBPrint dummy_buffer; if (!buf) { @@ -50,8 +49,7 @@ static int print_link_prop(AVBPrint *buf, AVFilterLink *link) format = av_x_if_null(av_get_sample_fmt_name(link->format), "?"); av_bprintf(buf, "[%dHz %s:", (int)link->sample_rate, format); - av_channel_layout_describe(&link->ch_layout, layout, sizeof(layout)); - av_bprintf(buf, "%s", layout); + av_channel_layout_describe_bprint(&link->ch_layout, buf); av_bprint_chars(buf, ']', 1); break; diff --git a/libavfilter/graphparser.c b/libavfilter/graphparser.c index 4347131fad1..5d6dcdb9d31 100644 --- a/libavfilter/graphparser.c +++ b/libavfilter/graphparser.c @@ -29,6 +29,7 @@ #include "libavutil/opt.h" #include "avfilter.h" +#include "avfilter_internal.h" #include "internal.h" #define WHITESPACES " \n\t\r" @@ -475,7 +476,7 @@ int avfilter_graph_segment_parse(AVFilterGraph *graph, const char *graph_str, graph_str += strspn(graph_str, WHITESPACES); - ret = parse_sws_flags(&graph_str, &seg->scale_sws_opts, &graph); + ret = parse_sws_flags(&graph_str, &seg->scale_sws_opts, graph); if (ret < 0) goto fail; @@ -626,7 +627,7 @@ int avfilter_graph_segment_init(AVFilterGraphSegment *seg, int flags) if (p->filter_name) return fail_creation_pending(seg, p->filter_name, __func__); - if (!p->filter || p->filter->internal->initialized) + if (!p->filter || fffilterctx(p->filter)->initialized) continue; ret = avfilter_init_dict(p->filter, NULL); @@ -851,6 +852,32 @@ int avfilter_graph_segment_link(AVFilterGraphSegment *seg, int flags, return ret; } +// print an error message if some options were not found +static void log_unknown_opt(const AVFilterGraphSegment *seg) +{ + for (size_t i = 0; i < seg->nb_chains; i++) { + const AVFilterChain *ch = seg->chains[i]; + + for (size_t j = 0; j < ch->nb_filters; j++) { + const AVFilterParams *p = ch->filters[j]; + const AVDictionaryEntry *e; + + if (!p->filter) + continue; + + e = av_dict_iterate(p->opts, NULL); + + if (e) { + av_log(p->filter, AV_LOG_ERROR, + "Could not set non-existent option '%s' to value '%s'\n", + e->key, e->value); + return; + } + } + } + +} + int avfilter_graph_segment_apply(AVFilterGraphSegment *seg, int flags, AVFilterInOut **inputs, AVFilterInOut **outputs) @@ -868,6 +895,8 @@ int avfilter_graph_segment_apply(AVFilterGraphSegment *seg, int flags, ret = avfilter_graph_segment_apply_opts(seg, 0); if (ret < 0) { + if (ret == AVERROR_OPTION_NOT_FOUND) + log_unknown_opt(seg); av_log(seg->graph, AV_LOG_ERROR, "Error applying filter options\n"); return ret; } @@ -909,8 +938,11 @@ int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters, goto end; ret = avfilter_graph_segment_apply_opts(seg, 0); - if (ret < 0) + if (ret < 0) { + if (ret == AVERROR_OPTION_NOT_FOUND) + log_unknown_opt(seg); goto end; + } ret = avfilter_graph_segment_init(seg, 0); if (ret < 0) @@ -988,6 +1020,9 @@ int avfilter_graph_parse_ptr(AVFilterGraph *graph, const char *filters, avfilter_graph_segment_free(&seg); if (ret < 0) { + av_log(graph, AV_LOG_ERROR, "Error processing filtergraph: %s\n", + av_err2str(ret)); + while (graph->nb_filters) avfilter_free(graph->filters[0]); av_freep(&graph->filters); diff --git a/libavfilter/internal.h b/libavfilter/internal.h index 8b232a8d8f9..000f94cb164 100644 --- a/libavfilter/internal.h +++ b/libavfilter/internal.h @@ -26,22 +26,6 @@ #include "libavutil/internal.h" #include "avfilter.h" -#include "formats.h" -#include "framequeue.h" -#include "video.h" - -typedef struct AVFilterCommand { - double time; ///< time expressed in seconds - char *command; ///< command - char *arg; ///< optional argument for the command - int flags; - struct AVFilterCommand *next; -} AVFilterCommand; - -/** - * Update the position of a link in the age heap. - */ -void ff_avfilter_graph_update_heap(AVFilterGraph *graph, AVFilterLink *link); /** * A filter pad used for either input or output. @@ -129,24 +113,28 @@ struct AVFilterPad { int (*config_props)(AVFilterLink *link); }; -struct AVFilterGraphInternal { - void *thread; - avfilter_execute_func *thread_execute; - FFFrameQueueGlobal frame_queues; -}; +typedef struct FFFilterContext { + /** + * The public AVFilterContext. See avfilter.h for it. + */ + AVFilterContext p; -struct AVFilterInternal { avfilter_execute_func *execute; // 1 when avfilter_init_*() was successfully called on this filter // 0 otherwise int initialized; -}; +} FFFilterContext; + +static inline FFFilterContext *fffilterctx(AVFilterContext *ctx) +{ + return (FFFilterContext*)ctx; +} static av_always_inline int ff_filter_execute(AVFilterContext *ctx, avfilter_action_func *func, void *arg, int *ret, int nb_jobs) { - return ctx->internal->execute(ctx, func, arg, ret, nb_jobs); + return fffilterctx(ctx)->execute(ctx, func, arg, ret, nb_jobs); } enum FilterFormatsState { @@ -205,6 +193,12 @@ enum FilterFormatsState { */ int ff_fmt_is_in(int fmt, const int *fmts); +/** + * Returns true if a pixel format is "regular YUV", which includes all pixel + * formats that are affected by YUV colorspace negotiation. + */ +int ff_fmt_is_regular_yuv(enum AVPixelFormat fmt); + /* Functions to parse audio format arguments */ /** @@ -243,8 +237,6 @@ av_warn_unused_result int ff_parse_channel_layout(AVChannelLayout *ret, int *nret, const char *arg, void *log_ctx); -void ff_update_link_current_pts(AVFilterLink *link, int64_t pts); - /** * Set the status field of a link from the source filter. * The pts should reflect the timestamp of the status change, @@ -255,10 +247,12 @@ void ff_update_link_current_pts(AVFilterLink *link, int64_t pts); void ff_avfilter_link_set_in_status(AVFilterLink *link, int status, int64_t pts); /** - * Set the status field of a link from the destination filter. - * The pts should probably be left unset (AV_NOPTS_VALUE). + * Negotiate the media format, dimensions, etc of all inputs to a filter. + * + * @param filter the filter to negotiate the properties for its inputs + * @return zero on successful negotiation */ -void ff_avfilter_link_set_out_status(AVFilterLink *link, int status, int64_t pts); +int ff_filter_config_links(AVFilterContext *filter); #define D2TS(d) (isnan(d) ? AV_NOPTS_VALUE : (int64_t)(d)) #define TS2D(ts) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)) @@ -350,23 +344,6 @@ int ff_request_frame(AVFilterLink *link); */ int ff_filter_frame(AVFilterLink *link, AVFrame *frame); -/** - * Allocate a new filter context and return it. - * - * @param filter what filter to create an instance of - * @param inst_name name to give to the new filter context - * - * @return newly created filter context or NULL on failure - */ -AVFilterContext *ff_filter_alloc(const AVFilter *filter, const char *inst_name); - -int ff_filter_activate(AVFilterContext *filter); - -/** - * Remove a filter from a graph; - */ -void ff_filter_graph_remove_filter(AVFilterGraph *graph, AVFilterContext *filter); - /** * The filter is aware of hardware frames, and any hardware frame context * should not be automatically propagated through it. @@ -409,17 +386,4 @@ int ff_filter_process_command(AVFilterContext *ctx, const char *cmd, int ff_filter_init_hw_frames(AVFilterContext *avctx, AVFilterLink *link, int default_pool_size); -/** - * Parse filter options into a dictionary. - * - * @param logctx context for logging - * @param priv_class a filter's private class for shorthand options or NULL - * @param options dictionary to store parsed options in - * @param args options string to parse - * - * @return a non-negative number on success, a negative error code on failure - */ -int ff_filter_opt_parse(void *logctx, const AVClass *priv_class, - AVDictionary **options, const char *args); - #endif /* AVFILTER_INTERNAL_H */ diff --git a/libavfilter/lavfutils.c b/libavfilter/lavfutils.c index 9aa781ef7ba..6130f21e7f8 100644 --- a/libavfilter/lavfutils.c +++ b/libavfilter/lavfutils.c @@ -117,7 +117,8 @@ int ff_load_image(uint8_t *data[4], int linesize[4], goto end; ret = 0; - av_image_copy(data, linesize, (const uint8_t **)frame->data, frame->linesize, *pix_fmt, *w, *h); + av_image_copy2(data, linesize, frame->data, frame->linesize, + *pix_fmt, *w, *h); end: avcodec_free_context(&codec_ctx); diff --git a/libavfilter/median_template.c b/libavfilter/median_template.c index bb7646e0eb1..760a0278c94 100644 --- a/libavfilter/median_template.c +++ b/libavfilter/median_template.c @@ -22,7 +22,6 @@ #include "libavutil/avassert.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/opencl.c b/libavfilter/opencl.c index 5d1e297af8a..48752e95305 100644 --- a/libavfilter/opencl.c +++ b/libavfilter/opencl.c @@ -23,7 +23,6 @@ #include "libavutil/mem.h" #include "libavutil/pixdesc.h" -#include "formats.h" #include "opencl.h" static int opencl_filter_set_device(AVFilterContext *avctx, diff --git a/libavfilter/opencl/deshake.cl b/libavfilter/opencl/deshake.cl index fef2681dc69..f2a7c7221d3 100644 --- a/libavfilter/opencl/deshake.cl +++ b/libavfilter/opencl/deshake.cl @@ -231,7 +231,7 @@ __kernel void harris_response( {-1, -2, -1} }; - // 8 x 8 local work + 3 pixels around each side (needed to accomodate for the + // 8 x 8 local work + 3 pixels around each side (needed to accommodate for the // block size radius of 2) __local float grayscale_data[196]; diff --git a/libavfilter/opencl_source.h b/libavfilter/opencl_source.h index 9eac2dc516a..b6930fb686d 100644 --- a/libavfilter/opencl_source.h +++ b/libavfilter/opencl_source.h @@ -19,19 +19,19 @@ #ifndef AVFILTER_OPENCL_SOURCE_H #define AVFILTER_OPENCL_SOURCE_H -extern const char *ff_opencl_source_avgblur; -extern const char *ff_opencl_source_colorkey; -extern const char *ff_opencl_source_colorspace_common; -extern const char *ff_opencl_source_convolution; -extern const char *ff_opencl_source_deshake; -extern const char *ff_opencl_source_neighbor; -extern const char *ff_opencl_source_nlmeans; -extern const char *ff_opencl_source_overlay; -extern const char *ff_opencl_source_pad; -extern const char *ff_opencl_source_remap; -extern const char *ff_opencl_source_tonemap; -extern const char *ff_opencl_source_transpose; -extern const char *ff_opencl_source_unsharp; -extern const char *ff_opencl_source_xfade; +extern const char *ff_source_avgblur_cl; +extern const char *ff_source_colorkey_cl; +extern const char *ff_source_colorspace_common_cl; +extern const char *ff_source_convolution_cl; +extern const char *ff_source_deshake_cl; +extern const char *ff_source_neighbor_cl; +extern const char *ff_source_nlmeans_cl; +extern const char *ff_source_overlay_cl; +extern const char *ff_source_pad_cl; +extern const char *ff_source_remap_cl; +extern const char *ff_source_tonemap_cl; +extern const char *ff_source_transpose_cl; +extern const char *ff_source_unsharp_cl; +extern const char *ff_source_xfade_cl; #endif /* AVFILTER_OPENCL_SOURCE_H */ diff --git a/libavfilter/phase_template.c b/libavfilter/phase_template.c index 84506702342..c25bc884153 100644 --- a/libavfilter/phase_template.c +++ b/libavfilter/phase_template.c @@ -20,7 +20,6 @@ #include "libavutil/avassert.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -57,10 +56,10 @@ static enum PhaseMode fn(analyze_plane)(void *ctx, enum PhaseMode mode, AVFrame double bdiff, tdiff, pdiff; if (mode == AUTO) { - mode = new->interlaced_frame ? new->top_field_first ? + mode = (new->flags & AV_FRAME_FLAG_INTERLACED) ? (new->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? TOP_FIRST : BOTTOM_FIRST : PROGRESSIVE; } else if (mode == AUTO_ANALYZE) { - mode = new->interlaced_frame ? new->top_field_first ? + mode = (new->flags & AV_FRAME_FLAG_INTERLACED) ? (new->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? TOP_FIRST_ANALYZE : BOTTOM_FIRST_ANALYZE : FULL_ANALYZE; } diff --git a/libavfilter/pthread.c b/libavfilter/pthread.c index 1a063d3cc0f..06590fe65a2 100644 --- a/libavfilter/pthread.c +++ b/libavfilter/pthread.c @@ -29,8 +29,7 @@ #include "libavutil/slicethread.h" #include "avfilter.h" -#include "internal.h" -#include "thread.h" +#include "avfilter_internal.h" typedef struct ThreadContext { AVFilterGraph *graph; @@ -59,7 +58,7 @@ static void slice_thread_uninit(ThreadContext *c) static int thread_execute(AVFilterContext *ctx, avfilter_action_func *func, void *arg, int *ret, int nb_jobs) { - ThreadContext *c = ctx->graph->internal->thread; + ThreadContext *c = fffiltergraph(ctx->graph)->thread; if (nb_jobs <= 0) return 0; @@ -80,8 +79,9 @@ static int thread_init_internal(ThreadContext *c, int nb_threads) return FFMAX(nb_threads, 1); } -int ff_graph_thread_init(AVFilterGraph *graph) +int ff_graph_thread_init(FFFilterGraph *graphi) { + AVFilterGraph *graph = &graphi->p; int ret; if (graph->nb_threads == 1) { @@ -89,27 +89,27 @@ int ff_graph_thread_init(AVFilterGraph *graph) return 0; } - graph->internal->thread = av_mallocz(sizeof(ThreadContext)); - if (!graph->internal->thread) + graphi->thread = av_mallocz(sizeof(ThreadContext)); + if (!graphi->thread) return AVERROR(ENOMEM); - ret = thread_init_internal(graph->internal->thread, graph->nb_threads); + ret = thread_init_internal(graphi->thread, graph->nb_threads); if (ret <= 1) { - av_freep(&graph->internal->thread); + av_freep(&graphi->thread); graph->thread_type = 0; graph->nb_threads = 1; return (ret < 0) ? ret : 0; } graph->nb_threads = ret; - graph->internal->thread_execute = thread_execute; + graphi->thread_execute = thread_execute; return 0; } -void ff_graph_thread_free(AVFilterGraph *graph) +void ff_graph_thread_free(FFFilterGraph *graph) { - if (graph->internal->thread) - slice_thread_uninit(graph->internal->thread); - av_freep(&graph->internal->thread); + if (graph->thread) + slice_thread_uninit(graph->thread); + av_freep(&graph->thread); } diff --git a/libavfilter/qrencode.c b/libavfilter/qrencode.c new file mode 100644 index 00000000000..b084bf1d1f4 --- /dev/null +++ b/libavfilter/qrencode.c @@ -0,0 +1,822 @@ +/* + * 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 QR encoder source and filter. + * + * A QR code (quick-response code) is a type of two-dimensional matrix + * barcode, invented in 1994, by Japanese company Denso Wave for + * labelling automobile parts. + * + * This source uses the libqrencode library to generate QR code: + * https://fukuchi.org/works/qrencode/ + */ + +//#define DEBUG + +#include "config_components.h" + +#include "libavutil/internal.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/lfg.h" +#include "libavutil/random_seed.h" + +#include "avfilter.h" +#include "drawutils.h" +#include "internal.h" +#include "formats.h" +#include "textutils.h" +#include "video.h" +#include "libswscale/swscale.h" + +#include + +enum var_name { + VAR_dar, + VAR_duration, + VAR_hsub, VAR_vsub, + VAR_main_h, VAR_H, + VAR_main_w, VAR_W, + VAR_n, + VAR_pict_type, + VAR_qr_w, VAR_w, + VAR_rendered_padded_qr_w, VAR_Q, + VAR_rendered_qr_w, VAR_q, + VAR_sar, + VAR_t, + VAR_x, + VAR_y, + VAR_VARS_NB +}; + +static const char *const var_names[] = { + "dar", + "duration", + "hsub", "vsub", + "main_h", "H", ///< height of the input video + "main_w", "W", ///< width of the input video + "n", ///< number of frame + "pict_type", + "qr_w", "w", ///< width of the QR code + "rendered_padded_qr_w", "Q", ///< width of the rendered QR code + "rendered_qr_w", "q", ///< width of the rendered QR code + "sar", + "t", ///< timestamp expressed in seconds + "x", + "y", + NULL +}; + +#define V(name_) qr->var_values[VAR_##name_] + +enum Expansion { + EXPANSION_NONE, + EXPANSION_NORMAL +}; + +typedef struct QREncodeContext { + const AVClass *class; + + char is_source; + char *x_expr; + char *y_expr; + AVExpr *x_pexpr, *y_pexpr; + + char *rendered_qrcode_width_expr; + char *rendered_padded_qrcode_width_expr; + AVExpr *rendered_qrcode_width_pexpr, *rendered_padded_qrcode_width_pexpr; + + int rendered_qrcode_width; + int rendered_padded_qrcode_width; + + unsigned char *text; + char *textfile; + uint64_t pts; + + int level; + char case_sensitive; + + uint8_t foreground_color[4]; + uint8_t background_color[4]; + + FFDrawContext draw; + FFDrawColor draw_foreground_color; ///< foreground color + FFDrawColor draw_background_color; ///< background color + + /* these are only used when nothing must be encoded */ + FFDrawContext draw0; + FFDrawColor draw0_background_color; ///< background color + + uint8_t *qrcode_data[4]; + int qrcode_linesize[4]; + uint8_t *qrcode_mask_data[4]; + int qrcode_mask_linesize[4]; + + /* only used for filter to contain scaled image to blend on top of input */ + uint8_t *rendered_qrcode_data[4]; + int rendered_qrcode_linesize[4]; + + int qrcode_width; + int padded_qrcode_width; + + AVRational frame_rate; + + int expansion; ///< expansion mode to use for the text + FFExpandTextContext expand_text; ///< expand text in case expansion is enabled + AVBPrint expanded_text; ///< used to contain the expanded text + + double var_values[VAR_VARS_NB]; + AVLFG lfg; ///< random generator + AVDictionary *metadata; +} QREncodeContext; + +#define OFFSET(x) offsetof(QREncodeContext, x) +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +#define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM + +#define COMMON_OPTIONS \ + { "qrcode_width", "set rendered QR code width expression", OFFSET(rendered_qrcode_width_expr), AV_OPT_TYPE_STRING, {.str = "64"}, 0, INT_MAX, FLAGS }, \ + { "q", "set rendered QR code width expression", OFFSET(rendered_qrcode_width_expr), AV_OPT_TYPE_STRING, {.str = "64"}, 0, INT_MAX, FLAGS }, \ + { "padded_qrcode_width", "set rendered padded QR code width expression", OFFSET(rendered_padded_qrcode_width_expr), AV_OPT_TYPE_STRING, {.str = "q"}, 0, INT_MAX, FLAGS }, \ + { "Q", "set rendered padded QR code width expression", OFFSET(rendered_padded_qrcode_width_expr), AV_OPT_TYPE_STRING, {.str = "q"}, 0, INT_MAX, FLAGS }, \ + { "case_sensitive", "generate code which is case sensitive", OFFSET(case_sensitive), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, \ + { "cs", "generate code which is case sensitive", OFFSET(case_sensitive), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, \ + \ + { "level", "error correction level, lowest is L", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = AVCOL_SPC_UNSPECIFIED }, 0, QR_ECLEVEL_H, .flags = FLAGS, .unit = "level"}, \ + { "l", "error correction level, lowest is L", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = AVCOL_SPC_UNSPECIFIED }, 0, QR_ECLEVEL_H, .flags = FLAGS, .unit = "level"}, \ + { "L", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QR_ECLEVEL_L }, 0, 0, FLAGS, .unit = "level" }, \ + { "M", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QR_ECLEVEL_M }, 0, 0, FLAGS, .unit = "level" }, \ + { "Q", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QR_ECLEVEL_Q }, 0, 0, FLAGS, .unit = "level" }, \ + { "H", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QR_ECLEVEL_H }, 0, 0, FLAGS, .unit = "level" }, \ + \ + {"expansion", "set the expansion mode", OFFSET(expansion), AV_OPT_TYPE_INT, {.i64=EXPANSION_NORMAL}, 0, 2, FLAGS, .unit = "expansion"}, \ + {"none", "set no expansion", OFFSET(expansion), AV_OPT_TYPE_CONST, {.i64 = EXPANSION_NONE}, 0, 0, FLAGS, .unit = "expansion"}, \ + {"normal", "set normal expansion", OFFSET(expansion), AV_OPT_TYPE_CONST, {.i64 = EXPANSION_NORMAL}, 0, 0, FLAGS, .unit = "expansion"}, \ + \ + { "foreground_color", "set QR foreground color", OFFSET(foreground_color), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS }, \ + { "fc", "set QR foreground color", OFFSET(foreground_color), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS }, \ + { "background_color", "set QR background color", OFFSET(background_color), AV_OPT_TYPE_COLOR, {.str = "white"}, 0, 0, FLAGS }, \ + { "bc", "set QR background color", OFFSET(background_color), AV_OPT_TYPE_COLOR, {.str = "white"}, 0, 0, FLAGS }, \ + \ + {"text", "set text to encode", OFFSET(text), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS}, \ + {"textfile", "set text file to encode", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS}, \ + +static const char *const fun2_names[] = { + "rand" +}; + +static double drand(void *opaque, double min, double max) +{ + return min + (max-min) / UINT_MAX * av_lfg_get(opaque); +} + +static const ff_eval_func2 fun2[] = { + drand, + NULL +}; + +static int func_pts(void *ctx, AVBPrint *bp, const char *function_name, + unsigned argc, char **argv) +{ + QREncodeContext *qr = ((AVFilterContext *)ctx)->priv; + const char *fmt; + const char *strftime_fmt = NULL; + const char *delta = NULL; + double t = qr->var_values[VAR_t]; + + // argv: pts, FMT, [DELTA, strftime_fmt] + + fmt = argc >= 1 ? argv[0] : "flt"; + if (argc >= 2) { + delta = argv[1]; + } + if (argc >= 3) { + strftime_fmt = argv[2]; + } + + return ff_print_pts(ctx, bp, t, delta, fmt, strftime_fmt); +} + +static int func_frame_num(void *ctx, AVBPrint *bp, const char *function_name, + unsigned argc, char **argv) +{ + QREncodeContext *qr = ((AVFilterContext *)ctx)->priv; + + av_bprintf(bp, "%d", (int)V(n)); + return 0; +} + +static int func_strftime(void *ctx, AVBPrint *bp, const char *function_name, + unsigned argc, char **argv) +{ + const char *strftime_fmt = argc ? argv[0] : NULL; + + return ff_print_time(ctx, bp, strftime_fmt, !strcmp(function_name, "localtime")); +} + +static int func_frame_metadata(void *ctx, AVBPrint *bp, const char *function_name, + unsigned argc, char **argv) +{ + QREncodeContext *qr = ((AVFilterContext *)ctx)->priv; + AVDictionaryEntry *e = av_dict_get(qr->metadata, argv[0], NULL, 0); + + if (e && e->value) + av_bprintf(bp, "%s", e->value); + else if (argc >= 2) + av_bprintf(bp, "%s", argv[1]); + + return 0; +} + +static int func_eval_expr(void *ctx, AVBPrint *bp, const char *function_name, + unsigned argc, char **argv) +{ + QREncodeContext *qr = ((AVFilterContext *)ctx)->priv; + + return ff_print_eval_expr(ctx, bp, argv[0], + fun2_names, fun2, + var_names, qr->var_values, &qr->lfg); +} + +static int func_eval_expr_formatted(void *ctx, AVBPrint *bp, const char *function_name, + unsigned argc, char **argv) +{ + QREncodeContext *qr = ((AVFilterContext *)ctx)->priv; + int ret; + int positions = -1; + + /* + * argv[0] expression to be converted to `int` + * argv[1] format: 'x', 'X', 'd' or 'u' + * argv[2] positions printed (optional) + */ + + if (argc == 3) { + ret = sscanf(argv[2], "%u", &positions); + if (ret != 1) { + av_log(ctx, AV_LOG_ERROR, "expr_int_format(): Invalid number of positions" + " to print: '%s'\n", argv[2]); + return AVERROR(EINVAL); + } + } + + return ff_print_formatted_eval_expr(ctx, bp, argv[0], + fun2_names, fun2, + var_names, qr->var_values, + &qr->lfg, + argv[1][0], positions); +} + +static FFExpandTextFunction expand_text_functions[] = { + { "expr", 1, 1, func_eval_expr }, + { "e", 1, 1, func_eval_expr }, + { "expr_formatted", 2, 3, func_eval_expr_formatted }, + { "ef", 2, 3, func_eval_expr_formatted }, + { "metadata", 1, 2, func_frame_metadata }, + { "frame_num", 0, 0, func_frame_num }, + { "n", 0, 0, func_frame_num }, + { "gmtime", 0, 1, func_strftime }, + { "localtime", 0, 1, func_strftime }, + { "pts", 0, 3, func_pts } +}; + +static av_cold int init(AVFilterContext *ctx) +{ + QREncodeContext *qr = ctx->priv; + int ret; + + av_lfg_init(&qr->lfg, av_get_random_seed()); + + qr->qrcode_width = -1; + qr->rendered_padded_qrcode_width = -1; + + if (qr->textfile) { + if (qr->text) { + av_log(ctx, AV_LOG_ERROR, + "Both text and text file provided. Please provide only one\n"); + return AVERROR(EINVAL); + } + if ((ret = ff_load_textfile(ctx, (const char *)qr->textfile, &(qr->text), NULL)) < 0) + return ret; + } + + qr->expand_text = (FFExpandTextContext) { + .log_ctx = ctx, + .functions = expand_text_functions, + .functions_nb = FF_ARRAY_ELEMS(expand_text_functions) + }; + + av_bprint_init(&qr->expanded_text, 0, AV_BPRINT_SIZE_UNLIMITED); + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + QREncodeContext *qr = ctx->priv; + + av_expr_free(qr->x_pexpr); + av_expr_free(qr->y_pexpr); + + av_bprint_finalize(&qr->expanded_text, NULL); + + av_freep(&qr->qrcode_data[0]); + av_freep(&qr->rendered_qrcode_data[0]); + av_freep(&qr->qrcode_mask_data[0]); +} + +#ifdef DEBUG +static void show_qrcode(AVFilterContext *ctx, const QRcode *qrcode) +{ + int i, j; + char *line = av_malloc(qrcode->width + 1); + const char *p = qrcode->data; + + if (!line) + return; + for (i = 0; i < qrcode->width; i++) { + for (j = 0; j < qrcode->width; j++) + line[j] = (*p++)&1 ? '@' : ' '; + line[j] = 0; + av_log(ctx, AV_LOG_DEBUG, "%3d: %s\n", i, line); + } + av_free(line); +} +#endif + +static int draw_qrcode(AVFilterContext *ctx, AVFrame *frame) +{ + QREncodeContext *qr = ctx->priv; + struct SwsContext *sws = NULL; + QRcode *qrcode = NULL; + int i, j; + char qrcode_width_changed; + int ret; + int offset; + uint8_t *srcp; + uint8_t *dstp0, *dstp; + + av_bprint_clear(&qr->expanded_text); + + switch (qr->expansion) { + case EXPANSION_NONE: + av_bprintf(&qr->expanded_text, "%s", qr->text); + break; + case EXPANSION_NORMAL: + if ((ret = ff_expand_text(&qr->expand_text, qr->text, &qr->expanded_text)) < 0) + return ret; + break; + } + + if (!qr->expanded_text.str || qr->expanded_text.str[0] == 0) { + if (qr->is_source) { + ff_fill_rectangle(&qr->draw0, &qr->draw0_background_color, + frame->data, frame->linesize, + 0, 0, qr->rendered_padded_qrcode_width, qr->rendered_padded_qrcode_width); + } + + return 0; + } + + av_log(ctx, AV_LOG_DEBUG, "Encoding string '%s'\n", qr->expanded_text.str); + qrcode = QRcode_encodeString(qr->expanded_text.str, 1, qr->level, QR_MODE_8, + qr->case_sensitive); + if (!qrcode) { + ret = AVERROR(errno); + av_log(ctx, AV_LOG_ERROR, + "Failed to encode string with error \'%s\'\n", av_err2str(ret)); + goto end; + } + + av_log(ctx, AV_LOG_DEBUG, + "Encoded QR with width:%d version:%d\n", qrcode->width, qrcode->version); +#ifdef DEBUG + show_qrcode(ctx, (const QRcode *)qrcode); +#endif + + qrcode_width_changed = qr->qrcode_width != qrcode->width; + qr->qrcode_width = qrcode->width; + + // realloc mask if needed + if (qrcode_width_changed) { + av_freep(&qr->qrcode_mask_data[0]); + ret = av_image_alloc(qr->qrcode_mask_data, qr->qrcode_mask_linesize, + qrcode->width, qrcode->width, + AV_PIX_FMT_GRAY8, 16); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, + "Failed to allocate image for QR code with width %d\n", qrcode->width); + goto end; + } + } + + /* fill mask */ + dstp0 = qr->qrcode_mask_data[0]; + srcp = qrcode->data; + + for (i = 0; i < qrcode->width; i++) { + dstp = dstp0; + for (j = 0; j < qrcode->width; j++) + *dstp++ = (*srcp++ & 1) ? 255 : 0; + dstp0 += qr->qrcode_mask_linesize[0]; + } + + if (qr->is_source) { + if (qrcode_width_changed) { + /* realloc padded image */ + + // compute virtual non-rendered padded size + // Q/q = W/w + qr->padded_qrcode_width = + ((double)qr->rendered_padded_qrcode_width / qr->rendered_qrcode_width) * qrcode->width; + + av_freep(&qr->qrcode_data[0]); + ret = av_image_alloc(qr->qrcode_data, qr->qrcode_linesize, + qr->padded_qrcode_width, qr->padded_qrcode_width, + AV_PIX_FMT_ARGB, 16); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, + "Failed to allocate image for QR code with width %d\n", + qr->padded_qrcode_width); + goto end; + } + } + + /* fill padding */ + ff_fill_rectangle(&qr->draw, &qr->draw_background_color, + qr->qrcode_data, qr->qrcode_linesize, + 0, 0, qr->padded_qrcode_width, qr->padded_qrcode_width); + + /* blend mask */ + offset = (qr->padded_qrcode_width - qr->qrcode_width) / 2; + ff_blend_mask(&qr->draw, &qr->draw_foreground_color, + qr->qrcode_data, qr->qrcode_linesize, + qr->padded_qrcode_width, qr->padded_qrcode_width, + qr->qrcode_mask_data[0], qr->qrcode_mask_linesize[0], qrcode->width, qrcode->width, + 3, 0, offset, offset); + + /* scale padded QR over the frame */ + sws = sws_alloc_context(); + if (!sws) { + ret = AVERROR(ENOMEM); + goto end; + } + + av_opt_set_int(sws, "srcw", qr->padded_qrcode_width, 0); + av_opt_set_int(sws, "srch", qr->padded_qrcode_width, 0); + av_opt_set_int(sws, "src_format", AV_PIX_FMT_ARGB, 0); + av_opt_set_int(sws, "dstw", qr->rendered_padded_qrcode_width, 0); + av_opt_set_int(sws, "dsth", qr->rendered_padded_qrcode_width, 0); + av_opt_set_int(sws, "dst_format", frame->format, 0); + av_opt_set_int(sws, "sws_flags", SWS_POINT, 0); + + if ((ret = sws_init_context(sws, NULL, NULL)) < 0) + goto end; + + sws_scale(sws, + (const uint8_t *const *)&qr->qrcode_data, qr->qrcode_linesize, + 0, qr->padded_qrcode_width, + frame->data, frame->linesize); + } else { +#define EVAL_EXPR(name_) \ + av_expr_eval(qr->name_##_pexpr, qr->var_values, &qr->lfg); + + V(qr_w) = V(w) = qrcode->width; + + V(rendered_qr_w) = V(q) = EVAL_EXPR(rendered_qrcode_width); + V(rendered_padded_qr_w) = V(Q) = EVAL_EXPR(rendered_padded_qrcode_width); + /* It is necessary if q is expressed from Q */ + V(rendered_qr_w) = V(q) = EVAL_EXPR(rendered_qrcode_width); + + V(x) = EVAL_EXPR(x); + V(y) = EVAL_EXPR(y); + /* It is necessary if x is expressed from y */ + V(x) = EVAL_EXPR(x); + + av_log(ctx, AV_LOG_DEBUG, + "Rendering QR code with values n:%d w:%d q:%d Q:%d x:%d y:%d t:%f\n", + (int)V(n), (int)V(w), (int)V(q), (int)V(Q), (int)V(x), (int)V(y), V(t)); + + /* blend rectangle over the target */ + ff_blend_rectangle(&qr->draw, &qr->draw_background_color, + frame->data, frame->linesize, frame->width, frame->height, + V(x), V(y), V(Q), V(Q)); + + if (V(q) != qr->rendered_qrcode_width) { + av_freep(&qr->rendered_qrcode_data[0]); + qr->rendered_qrcode_width = V(q); + + ret = av_image_alloc(qr->rendered_qrcode_data, qr->rendered_qrcode_linesize, + qr->rendered_qrcode_width, qr->rendered_qrcode_width, + AV_PIX_FMT_GRAY8, 16); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, + "Failed to allocate image for rendered QR code with width %d\n", + qr->rendered_qrcode_width); + goto end; + } + } + + /* scale mask */ + sws = sws_alloc_context(); + if (!sws) { + ret = AVERROR(ENOMEM); + goto end; + } + + av_opt_set_int(sws, "srcw", qr->qrcode_width, 0); + av_opt_set_int(sws, "srch", qr->qrcode_width, 0); + av_opt_set_int(sws, "src_format", AV_PIX_FMT_GRAY8, 0); + av_opt_set_int(sws, "dstw", qr->rendered_qrcode_width, 0); + av_opt_set_int(sws, "dsth", qr->rendered_qrcode_width, 0); + av_opt_set_int(sws, "dst_format", AV_PIX_FMT_GRAY8, 0); + av_opt_set_int(sws, "sws_flags", SWS_POINT, 0); + + if ((ret = sws_init_context(sws, NULL, NULL)) < 0) + goto end; + + sws_scale(sws, + (const uint8_t *const *)&qr->qrcode_mask_data, qr->qrcode_mask_linesize, + 0, qr->qrcode_width, + qr->rendered_qrcode_data, qr->rendered_qrcode_linesize); + + /* blend mask over the input frame */ + offset = (V(Q) - V(q)) / 2; + ff_blend_mask(&qr->draw, &qr->draw_foreground_color, + frame->data, frame->linesize, frame->width, frame->height, + qr->rendered_qrcode_data[0], qr->rendered_qrcode_linesize[0], + qr->rendered_qrcode_width, qr->rendered_qrcode_width, + 3, 0, V(x) + offset, V(y) + offset); + } + +end: + sws_freeContext(sws); + QRcode_free(qrcode); + + return ret; +} + +#if CONFIG_QRENCODESRC_FILTER + +static const AVOption qrencodesrc_options[] = { + COMMON_OPTIONS + { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS }, + { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(qrencodesrc); + +static int qrencodesrc_config_props(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + QREncodeContext *qr = ctx->priv; + int ret; + + qr->is_source = 1; + V(x) = V(y) = 0; + +#define PARSE_AND_EVAL_EXPR(var_name_, expr_name_) \ + ret = av_expr_parse_and_eval(&qr->var_values[VAR_##var_name_], \ + qr->expr_name_##_expr, \ + var_names, qr->var_values, \ + NULL, NULL, \ + fun2_names, fun2, \ + &qr->lfg, 0, ctx); \ + if (ret < 0) { \ + av_log(ctx, AV_LOG_ERROR, \ + "Could not evaluate expression '%s'\n", \ + qr->expr_name_##_expr); \ + return ret; \ + } + + /* undefined for the source */ + V(main_w) = V(W) = NAN; + V(main_h) = V(H) = NAN; + V(x) = V(y) = V(t) = V(n) = NAN; + V(dar) = V(sar) = 1.0; + + PARSE_AND_EVAL_EXPR(rendered_qr_w, rendered_qrcode_width); + V(q) = V(rendered_qr_w); + PARSE_AND_EVAL_EXPR(rendered_padded_qr_w, rendered_padded_qrcode_width); + V(Q) = V(rendered_padded_qr_w); + PARSE_AND_EVAL_EXPR(rendered_qr_w, rendered_qrcode_width); + V(q) = V(rendered_qr_w); + + qr->rendered_qrcode_width = V(rendered_qr_w); + qr->rendered_padded_qrcode_width = V(rendered_padded_qr_w); + + av_log(ctx, AV_LOG_VERBOSE, + "q:%d Q:%d case_sensitive:%d level:%d\n", + (int)qr->rendered_qrcode_width, (int)qr->rendered_padded_qrcode_width, + qr->case_sensitive, qr->level); + + if (qr->rendered_padded_qrcode_width < qr->rendered_qrcode_width) { + av_log(ctx, AV_LOG_ERROR, + "Resulting padded QR code width (%d) is lesser than the QR code width (%d)\n", + qr->rendered_padded_qrcode_width, qr->rendered_qrcode_width); + return AVERROR(EINVAL); + } + + ff_draw_init(&qr->draw, AV_PIX_FMT_ARGB, FF_DRAW_PROCESS_ALPHA); + ff_draw_color(&qr->draw, &qr->draw_foreground_color, (const uint8_t *)&qr->foreground_color); + ff_draw_color(&qr->draw, &qr->draw_background_color, (const uint8_t *)&qr->background_color); + + ff_draw_init2(&qr->draw0, outlink->format, outlink->colorspace, outlink->color_range, FF_DRAW_PROCESS_ALPHA); + ff_draw_color(&qr->draw0, &qr->draw0_background_color, (const uint8_t *)&qr->background_color); + + outlink->w = qr->rendered_padded_qrcode_width; + outlink->h = qr->rendered_padded_qrcode_width; + outlink->time_base = av_inv_q(qr->frame_rate); + outlink->frame_rate = qr->frame_rate; + + return 0; +} + +static int request_frame(AVFilterLink *outlink) +{ + AVFilterContext *ctx = (AVFilterContext *)outlink->src; + QREncodeContext *qr = ctx->priv; + AVFrame *frame = + ff_get_video_buffer(outlink, qr->rendered_padded_qrcode_width, qr->rendered_padded_qrcode_width); + int ret; + + if (!frame) + return AVERROR(ENOMEM); + frame->sample_aspect_ratio = (AVRational) {1, 1}; + V(n) = frame->pts = qr->pts++; + V(t) = qr->pts * av_q2d(outlink->time_base); + + if ((ret = draw_qrcode(ctx, frame)) < 0) + return ret; + + return ff_filter_frame(outlink, frame); +} + +static int qrencodesrc_query_formats(AVFilterContext *ctx) +{ + enum AVPixelFormat pix_fmt; + FFDrawContext draw; + AVFilterFormats *fmts = NULL; + int ret; + + // this is needed to support both the no-draw and draw cases + // for the no-draw case we use FFDrawContext to write on the input picture ref + for (pix_fmt = 0; av_pix_fmt_desc_get(pix_fmt); pix_fmt++) + if (ff_draw_init(&draw, pix_fmt, 0) >= 0 && + sws_isSupportedOutput(pix_fmt) && + (ret = ff_add_format(&fmts, pix_fmt)) < 0) + return ret; + + return ff_set_common_formats(ctx, fmts); +} + +static const AVFilterPad qrencodesrc_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = request_frame, + .config_props = qrencodesrc_config_props, + } +}; + +const AVFilter ff_vsrc_qrencodesrc = { + .name = "qrencodesrc", + .description = NULL_IF_CONFIG_SMALL("Generate a QR code."), + .priv_size = sizeof(QREncodeContext), + .priv_class = &qrencodesrc_class, + .init = init, + .uninit = uninit, + .inputs = NULL, + FILTER_OUTPUTS(qrencodesrc_outputs), + FILTER_QUERY_FUNC(qrencodesrc_query_formats), +}; + +#endif // CONFIG_QRENCODESRC_FILTER + +#if CONFIG_QRENCODE_FILTER + +static const AVOption qrencode_options[] = { + COMMON_OPTIONS + {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, TFLAGS}, + {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, TFLAGS}, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(qrencode); + +static int qrencode_config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + QREncodeContext *qr = ctx->priv; + char *expr; + int ret; + + qr->is_source = 0; + + ff_draw_init2(&qr->draw, inlink->format, inlink->colorspace, inlink->color_range, + FF_DRAW_PROCESS_ALPHA); + + V(W) = V(main_w) = inlink->w; + V(H) = V(main_h) = inlink->h; + V(sar) = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1; + V(dar) = (double)inlink->w / inlink->h * V(sar); + V(hsub) = 1 << qr->draw.hsub_max; + V(vsub) = 1 << qr->draw.vsub_max; + V(t) = NAN; + V(x) = V(y) = NAN; + + qr->x_pexpr = qr->y_pexpr = NULL; + qr->x_pexpr = qr->y_pexpr = NULL; + +#define PARSE_EXPR(name_) \ + ret = av_expr_parse(&qr->name_##_pexpr, expr = qr->name_##_expr, var_names, \ + NULL, NULL, fun2_names, fun2, 0, ctx); \ + if (ret < 0) { \ + av_log(ctx, AV_LOG_ERROR, \ + "Could not to parse expression '%s' for '%s'\n", \ + expr, #name_); \ + return AVERROR(EINVAL); \ + } + + PARSE_EXPR(x); + PARSE_EXPR(y); + PARSE_EXPR(rendered_qrcode_width); + PARSE_EXPR(rendered_padded_qrcode_width); + + ff_draw_init2(&qr->draw, inlink->format, inlink->colorspace, inlink->color_range, + FF_DRAW_PROCESS_ALPHA); + ff_draw_color(&qr->draw, &qr->draw_foreground_color, (const uint8_t *)&qr->foreground_color); + ff_draw_color(&qr->draw, &qr->draw_background_color, (const uint8_t *)&qr->background_color); + + qr->rendered_qrcode_width = -1; + + return 0; +} + +static int qrencode_query_formats(AVFilterContext *ctx) +{ + return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + QREncodeContext *qr = ctx->priv; + int ret; + + V(n) = inlink->frame_count_out; + V(t) = frame->pts == AV_NOPTS_VALUE ? + NAN : frame->pts * av_q2d(inlink->time_base); + V(pict_type) = frame->pict_type; + V(duration) = frame->duration * av_q2d(inlink->time_base); + + qr->metadata = frame->metadata; + + if ((ret = draw_qrcode(ctx, frame)) < 0) + return ret; + + return ff_filter_frame(outlink, frame); +} + +static const AVFilterPad avfilter_vf_qrencode_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE, + .filter_frame = filter_frame, + .config_props = qrencode_config_input, + }, +}; + +const AVFilter ff_vf_qrencode = { + .name = "qrencode", + .description = NULL_IF_CONFIG_SMALL("Draw a QR code on top of video frames."), + .priv_size = sizeof(QREncodeContext), + .priv_class = &qrencode_class, + .init = init, + .uninit = uninit, + FILTER_INPUTS(avfilter_vf_qrencode_inputs), + FILTER_OUTPUTS(ff_video_default_filterpad), + FILTER_QUERY_FUNC(qrencode_query_formats), + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, +}; + +#endif // CONFIG_QRENCODE_FILTER diff --git a/libavfilter/qsvvpp.c b/libavfilter/qsvvpp.c index 54e7284234b..5cdba7d54ab 100644 --- a/libavfilter/qsvvpp.c +++ b/libavfilter/qsvvpp.c @@ -460,8 +460,8 @@ static QSVFrame *submit_frame(QSVVPPContext *s, AVFilterLink *inlink, AVFrame *p inlink->time_base, default_tb); qsv_frame->surface.Info.PicStruct = - !qsv_frame->frame->interlaced_frame ? MFX_PICSTRUCT_PROGRESSIVE : - (qsv_frame->frame->top_field_first ? MFX_PICSTRUCT_FIELD_TFF : + !(qsv_frame->frame->flags & AV_FRAME_FLAG_INTERLACED) ? MFX_PICSTRUCT_PROGRESSIVE : + ((qsv_frame->frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? MFX_PICSTRUCT_FIELD_TFF : MFX_PICSTRUCT_FIELD_BFF); if (qsv_frame->frame->repeat_pict == 1) qsv_frame->surface.Info.PicStruct |= MFX_PICSTRUCT_FIELD_REPEATED; @@ -474,7 +474,7 @@ static QSVFrame *submit_frame(QSVVPPContext *s, AVFilterLink *inlink, AVFrame *p } /* get the output surface */ -static QSVFrame *query_frame(QSVVPPContext *s, AVFilterLink *outlink) +static QSVFrame *query_frame(QSVVPPContext *s, AVFilterLink *outlink, const AVFrame *in) { AVFilterContext *ctx = outlink->src; QSVFrame *out_frame; @@ -493,6 +493,12 @@ static QSVFrame *query_frame(QSVVPPContext *s, AVFilterLink *outlink) if (!out_frame->frame) return NULL; + ret = av_frame_copy_props(out_frame->frame, in); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to copy metadata fields from src to dst.\n"); + return NULL; + } + ret = av_hwframe_get_buffer(outlink->hw_frames_ctx, out_frame->frame, 0); if (ret < 0) { av_log(ctx, AV_LOG_ERROR, "Can't allocate a surface.\n"); @@ -509,6 +515,12 @@ static QSVFrame *query_frame(QSVVPPContext *s, AVFilterLink *outlink) if (!out_frame->frame) return NULL; + ret = av_frame_copy_props(out_frame->frame, in); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to copy metadata fields from src to dst.\n"); + return NULL; + } + ret = map_frame_to_surface(out_frame->frame, &out_frame->surface); if (ret < 0) @@ -524,6 +536,25 @@ static QSVFrame *query_frame(QSVVPPContext *s, AVFilterLink *outlink) out_frame->frame->height = outlink->h; out_frame->surface.Info = s->vpp_param.vpp.Out; + for (int i = 0; i < s->vpp_param.NumExtParam; i++) { + mfxExtBuffer *extbuf = s->vpp_param.ExtParam[i]; + + if (extbuf->BufferId == MFX_EXTBUFF_VPP_DEINTERLACING) { +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + out_frame->frame->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out_frame->frame->flags &= ~AV_FRAME_FLAG_INTERLACED; + break; + } + } + + out_frame->surface.Info.PicStruct = + !(out_frame->frame->flags & AV_FRAME_FLAG_INTERLACED) ? MFX_PICSTRUCT_PROGRESSIVE : + ((out_frame->frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? MFX_PICSTRUCT_FIELD_TFF : + MFX_PICSTRUCT_FIELD_BFF); + return out_frame; } @@ -655,6 +686,12 @@ static int init_vpp_session(AVFilterContext *avctx, QSVVPPContext *s) if (ret) return ret; + ret = MFXQueryVersion(s->session, &s->ver); + if (ret != MFX_ERR_NONE) { + av_log(avctx, AV_LOG_ERROR, "Error querying the runtime version\n"); + return AVERROR_UNKNOWN; + } + if (handle) { ret = MFXVideoCORE_SetHandle(s->session, handle_type, handle); if (ret != MFX_ERR_NONE) @@ -699,6 +736,11 @@ static int init_vpp_session(AVFilterContext *avctx, QSVVPPContext *s) return 0; } +static int set_frame_ext_params_null(AVFilterContext *ctx, const AVFrame *in, AVFrame *out, QSVVPPFrameParam *fp) +{ + return 0; +} + int ff_qsvvpp_init(AVFilterContext *avctx, QSVVPPParam *param) { int i; @@ -710,6 +752,10 @@ int ff_qsvvpp_init(AVFilterContext *avctx, QSVVPPParam *param) s->filter_frame = ff_filter_frame; s->out_sw_format = param->out_sw_format; + s->set_frame_ext_params = param->set_frame_ext_params; + if (!s->set_frame_ext_params) + s->set_frame_ext_params = set_frame_ext_params_null; + /* create the vpp session */ ret = init_vpp_session(avctx, s); if (ret < 0) @@ -749,28 +795,39 @@ int ff_qsvvpp_init(AVFilterContext *avctx, QSVVPPParam *param) goto failed; } + s->nb_seq_buffers = param->num_ext_buf; #if QSV_HAVE_OPAQUE - if (IS_OPAQUE_MEMORY(s->in_mem_mode) || IS_OPAQUE_MEMORY(s->out_mem_mode)) { - s->nb_ext_buffers = param->num_ext_buf + 1; + if (IS_OPAQUE_MEMORY(s->in_mem_mode) || IS_OPAQUE_MEMORY(s->out_mem_mode)) + s->nb_seq_buffers++; +#endif + + if (s->nb_seq_buffers) { + s->seq_buffers = av_calloc(s->nb_seq_buffers, sizeof(*s->seq_buffers)); + if (!s->seq_buffers) { + ret = AVERROR(ENOMEM); + goto failed; + } + + for (i = 0; i < param->num_ext_buf; i++) + s->seq_buffers[i] = param->ext_buf[i]; + +#if QSV_HAVE_OPAQUE + if (IS_OPAQUE_MEMORY(s->in_mem_mode) || IS_OPAQUE_MEMORY(s->out_mem_mode)) + s->seq_buffers[i] = (mfxExtBuffer *)&s->opaque_alloc; +#endif + + s->nb_ext_buffers = s->nb_seq_buffers; s->ext_buffers = av_calloc(s->nb_ext_buffers, sizeof(*s->ext_buffers)); if (!s->ext_buffers) { ret = AVERROR(ENOMEM); goto failed; } - s->ext_buffers[0] = (mfxExtBuffer *)&s->opaque_alloc; - for (i = 1; i < param->num_ext_buf; i++) - s->ext_buffers[i] = param->ext_buf[i - 1]; - s->vpp_param.ExtParam = s->ext_buffers; - s->vpp_param.NumExtParam = s->nb_ext_buffers; - } else { - s->vpp_param.NumExtParam = param->num_ext_buf; - s->vpp_param.ExtParam = param->ext_buf; + memcpy(s->ext_buffers, s->seq_buffers, s->nb_seq_buffers * sizeof(*s->seq_buffers)); } -#else - s->vpp_param.NumExtParam = param->num_ext_buf; - s->vpp_param.ExtParam = param->ext_buf; -#endif + + s->vpp_param.ExtParam = s->ext_buffers; + s->vpp_param.NumExtParam = s->nb_ext_buffers; s->got_frame = 0; @@ -805,12 +862,14 @@ int ff_qsvvpp_init(AVFilterContext *avctx, QSVVPPParam *param) ff_qsvvpp_print_iopattern(avctx, s->vpp_param.IOPattern & 0x0F, "VPP"); /* Print output memory mode */ ff_qsvvpp_print_iopattern(avctx, s->vpp_param.IOPattern & 0xF0, "VPP"); - ret = MFXVideoVPP_Init(s->session, &s->vpp_param); + + /* Validate VPP params, but don't initial VPP session here */ + ret = MFXVideoVPP_Query(s->session, &s->vpp_param, &s->vpp_param); if (ret < 0) { - ret = ff_qsvvpp_print_error(avctx, ret, "Failed to create a qsvvpp"); + ret = ff_qsvvpp_print_error(avctx, ret, "Error querying VPP params"); goto failed; } else if (ret > 0) - ff_qsvvpp_print_warning(avctx, ret, "Warning When creating qsvvpp"); + ff_qsvvpp_print_warning(avctx, ret, "Warning When querying VPP params"); return 0; @@ -820,6 +879,60 @@ int ff_qsvvpp_init(AVFilterContext *avctx, QSVVPPParam *param) return ret; } +static int qsvvpp_init_vpp_session(AVFilterContext *avctx, QSVVPPContext *s, const QSVFrame *in, QSVFrame *out) +{ + int ret; + mfxExtBuffer *ext_param[QSVVPP_MAX_FRAME_EXTBUFS]; + QSVVPPFrameParam fp = { 0, ext_param }; + + ret = s->set_frame_ext_params(avctx, in->frame, out->frame, &fp); + if (ret) + return ret; + + if (fp.num_ext_buf) { + av_freep(&s->ext_buffers); + s->nb_ext_buffers = s->nb_seq_buffers + fp.num_ext_buf; + + s->ext_buffers = av_calloc(s->nb_ext_buffers, sizeof(*s->ext_buffers)); + if (!s->ext_buffers) + return AVERROR(ENOMEM); + + memcpy(&s->ext_buffers[0], s->seq_buffers, s->nb_seq_buffers * sizeof(*s->seq_buffers)); + memcpy(&s->ext_buffers[s->nb_seq_buffers], fp.ext_buf, fp.num_ext_buf * sizeof(*fp.ext_buf)); + s->vpp_param.ExtParam = s->ext_buffers; + s->vpp_param.NumExtParam = s->nb_ext_buffers; + } + + if (!s->vpp_initted) { + s->vpp_param.vpp.In.PicStruct = in->surface.Info.PicStruct; + s->vpp_param.vpp.Out.PicStruct = out->surface.Info.PicStruct; + + /* Query VPP params again, including params for frame */ + ret = MFXVideoVPP_Query(s->session, &s->vpp_param, &s->vpp_param); + if (ret < 0) + return ff_qsvvpp_print_error(avctx, ret, "Error querying VPP params"); + else if (ret > 0) + ff_qsvvpp_print_warning(avctx, ret, "Warning When querying VPP params"); + + ret = MFXVideoVPP_Init(s->session, &s->vpp_param); + if (ret < 0) + return ff_qsvvpp_print_error(avctx, ret, "Failed to create a qsvvpp"); + else if (ret > 0) + ff_qsvvpp_print_warning(avctx, ret, "Warning When creating qsvvpp"); + + s->vpp_initted = 1; + } else if (fp.num_ext_buf) { + ret = MFXVideoVPP_Reset(s->session, &s->vpp_param); + if (ret < 0) { + ret = ff_qsvvpp_print_error(avctx, ret, "Failed to reset session for qsvvpp"); + return ret; + } else if (ret > 0) + ff_qsvvpp_print_warning(avctx, ret, "Warning When resetting session for qsvvpp"); + } + + return 0; +} + int ff_qsvvpp_close(AVFilterContext *avctx) { QSVVPPContext *s = avctx->priv; @@ -828,6 +941,7 @@ int ff_qsvvpp_close(AVFilterContext *avctx) MFXVideoVPP_Close(s->session); MFXClose(s->session); s->session = NULL; + s->vpp_initted = 0; } /* release all the resources */ @@ -835,9 +949,8 @@ int ff_qsvvpp_close(AVFilterContext *avctx) clear_frame_list(&s->out_frame_list); av_freep(&s->surface_ptrs_in); av_freep(&s->surface_ptrs_out); -#if QSV_HAVE_OPAQUE + av_freep(&s->seq_buffers); av_freep(&s->ext_buffers); -#endif av_freep(&s->frame_infos); av_fifo_freep2(&s->async_fifo); @@ -878,12 +991,16 @@ int ff_qsvvpp_filter_frame(QSVVPPContext *s, AVFilterLink *inlink, AVFrame *picr } do { - out_frame = query_frame(s, outlink); + out_frame = query_frame(s, outlink, in_frame->frame); if (!out_frame) { av_log(ctx, AV_LOG_ERROR, "Failed to query an output frame.\n"); return AVERROR(ENOMEM); } + ret = qsvvpp_init_vpp_session(ctx, s, in_frame, out_frame); + if (ret) + return ret; + do { ret = MFXVideoVPP_RunFrameVPPAsync(s->session, &in_frame->surface, &out_frame->surface, NULL, &sync); diff --git a/libavfilter/qsvvpp.h b/libavfilter/qsvvpp.h index 3b321937440..4eea7a46c75 100644 --- a/libavfilter/qsvvpp.h +++ b/libavfilter/qsvvpp.h @@ -52,11 +52,20 @@ typedef struct QSVFrame { int queued; } QSVFrame; +#define QSVVPP_MAX_FRAME_EXTBUFS 8 + +typedef struct QSVVPPFrameParam { + /* To fill with MFX enhanced filter configurations */ + int num_ext_buf; + mfxExtBuffer **ext_buf; +} QSVVPPFrameParam; + typedef struct QSVVPPContext { const AVClass *class; mfxSession session; int (*filter_frame) (AVFilterLink *outlink, AVFrame *frame); /**< callback */ + int (*set_frame_ext_params)(AVFilterContext *ctx, const AVFrame *in, AVFrame *out, QSVVPPFrameParam *fp); /**< callbak */ enum AVPixelFormat out_sw_format; /**< Real output format */ mfxVideoParam vpp_param; mfxFrameInfo *frame_infos; /**< frame info for each input */ @@ -74,15 +83,23 @@ typedef struct QSVVPPContext { #if QSV_HAVE_OPAQUE /** MFXVPP extern parameters */ mfxExtOpaqueSurfaceAlloc opaque_alloc; +#endif + /** store sequence parameters */ + mfxExtBuffer **seq_buffers; + int nb_seq_buffers; + + /** store all parameters for vpp execution, including parameters per frame */ mfxExtBuffer **ext_buffers; int nb_ext_buffers; -#endif int got_frame; int async_depth; int eof; /** order with frame_out, sync */ AVFifo *async_fifo; + + mfxVersion ver; + int vpp_initted; } QSVVPPContext; typedef struct QSVVPPCrop { @@ -93,6 +110,7 @@ typedef struct QSVVPPCrop { typedef struct QSVVPPParam { /* default is ff_filter_frame */ int (*filter_frame)(AVFilterLink *outlink, AVFrame *frame); + int (*set_frame_ext_params)(AVFilterContext *ctx, const AVFrame *in, AVFrame *out, QSVVPPFrameParam *fp); /**< callbak */ /* To fill with MFX enhanced filter configurations */ int num_ext_buf; diff --git a/libavfilter/riscv/Makefile b/libavfilter/riscv/Makefile new file mode 100644 index 00000000000..0b968a9c0df --- /dev/null +++ b/libavfilter/riscv/Makefile @@ -0,0 +1,2 @@ +OBJS += riscv/af_afir_init.o +RVV-OBJS += riscv/af_afir_rvv.o diff --git a/libavutil/bfin/bswap.h b/libavfilter/riscv/af_afir_init.c similarity index 62% rename from libavutil/bfin/bswap.h rename to libavfilter/riscv/af_afir_init.c index 363ed40bc5f..52aa18c1266 100644 --- a/libavutil/bfin/bswap.h +++ b/libavfilter/riscv/af_afir_init.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Marc Hoffman + * Copyright (c) 2023 Institue of Software Chinese Academy of Sciences (ISCAS). * * This file is part of FFmpeg. * @@ -18,28 +18,25 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -/** - * @file - * byte swapping routines - */ - -#ifndef AVUTIL_BFIN_BSWAP_H -#define AVUTIL_BFIN_BSWAP_H - #include + #include "config.h" #include "libavutil/attributes.h" +#include "libavutil/cpu.h" +#include "libavfilter/af_afirdsp.h" -#define av_bswap32 av_bswap32 -static av_always_inline av_const uint32_t av_bswap32(uint32_t x) +void ff_fcmul_add_rvv(float *sum, const float *t, const float *c, + ptrdiff_t len); + +av_cold void ff_afir_init_riscv(AudioFIRDSPContext *s) { - unsigned tmp; - __asm__("%1 = %0 >> 8 (V); \n\t" - "%0 = %0 << 8 (V); \n\t" - "%0 = %0 | %1; \n\t" - "%0 = PACK(%0.L, %0.H); \n\t" - : "+d"(x), "=&d"(tmp)); - return x; -} +#if HAVE_RVV + int flags = av_get_cpu_flags(); -#endif /* AVUTIL_BFIN_BSWAP_H */ + if (flags & AV_CPU_FLAG_RVV_F64) { + if (flags & AV_CPU_FLAG_RVB_ADDR) { + s->fcmul_add = ff_fcmul_add_rvv; + } + } +#endif +} diff --git a/libavfilter/riscv/af_afir_rvv.S b/libavfilter/riscv/af_afir_rvv.S new file mode 100644 index 00000000000..04ec2e50d8c --- /dev/null +++ b/libavfilter/riscv/af_afir_rvv.S @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Institue of Software Chinese Academy of Sciences (ISCAS). + * + * 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/riscv/asm.S" + +// void ff_fcmul_add(float *sum, const float *t, const float *c, int len) +func ff_fcmul_add_rvv, zve64f + li t1, 32 +1: + vsetvli t0, a3, e32, m4, ta, ma + vle64.v v24, (a0) + sub a3, a3, t0 + vnsrl.wx v16, v24, zero + vnsrl.wx v20, v24, t1 + vle64.v v24, (a1) + sh3add a1, t0, a1 + vnsrl.wx v0, v24, zero + vnsrl.wx v4, v24, t1 + vle64.v v24, (a2) + sh3add a2, t0, a2 + vnsrl.wx v8, v24, zero + vnsrl.wx v12, v24, t1 + vfmacc.vv v16, v0, v8 + vfmacc.vv v20, v4, v8 + vfnmsac.vv v16, v4, v12 + vfmacc.vv v20, v0, v12 + vsseg2e32.v v16, (a0) + sh3add a0, t0, a0 + bgtz a3, 1b + + flw fa0, 0(a1) + flw fa1, 0(a2) + flw fa2, 0(a0) + fmadd.s fa2, fa0, fa1, fa2 + fsw fa2, 0(a0) + + ret +endfunc diff --git a/libavfilter/scale_eval.c b/libavfilter/scale_eval.c index 75ed503f15f..dc8d522b1e3 100644 --- a/libavfilter/scale_eval.c +++ b/libavfilter/scale_eval.c @@ -114,7 +114,7 @@ int ff_scale_adjust_dimensions(AVFilterLink *inlink, int *ret_w, int *ret_h, int force_original_aspect_ratio, int force_divisible_by) { - int w, h; + int64_t w, h; int factor_w, factor_h; w = *ret_w; @@ -149,9 +149,9 @@ int ff_scale_adjust_dimensions(AVFilterLink *inlink, * unless force_divisible_by is defined as well */ if (force_original_aspect_ratio) { // Including force_divisible_by here rounds to the nearest multiple of it. - int tmp_w = av_rescale(h, inlink->w, inlink->h * (int64_t)force_divisible_by) + int64_t tmp_w = av_rescale(h, inlink->w, inlink->h * (int64_t)force_divisible_by) * force_divisible_by; - int tmp_h = av_rescale(w, inlink->h, inlink->w * (int64_t)force_divisible_by) + int64_t tmp_h = av_rescale(w, inlink->h, inlink->w * (int64_t)force_divisible_by) * force_divisible_by; if (force_original_aspect_ratio == 1) { @@ -173,6 +173,9 @@ int ff_scale_adjust_dimensions(AVFilterLink *inlink, } } + if ((int32_t)w != w || (int32_t)h != h) + return AVERROR(EINVAL); + *ret_w = w; *ret_h = h; diff --git a/libavfilter/scale_eval.h b/libavfilter/scale_eval.h index 2eb6970aad4..b4895284043 100644 --- a/libavfilter/scale_eval.h +++ b/libavfilter/scale_eval.h @@ -41,7 +41,7 @@ int ff_scale_eval_dimensions(void *ctx, * force_original_aspect_ratio is set. force_divisible_by is used only when * force_original_aspect_ratio is set and must be at least 1. * - * Returns 0. + * Returns negative error code on error or non negative on success */ int ff_scale_adjust_dimensions(AVFilterLink *inlink, int *ret_w, int *ret_h, diff --git a/libavfilter/setpts.c b/libavfilter/setpts.c index 171fae88c0c..60cf2b642e0 100644 --- a/libavfilter/setpts.c +++ b/libavfilter/setpts.c @@ -45,7 +45,9 @@ static const char *const var_names[] = { "N", ///< frame / sample number (starting at zero) "NB_CONSUMED_SAMPLES", ///< number of samples consumed by the filter (only audio) "NB_SAMPLES", ///< number of samples in the current frame (only audio) +#if FF_API_FRAME_PKT "POS", ///< original position in the file of the frame +#endif "PREV_INPTS", ///< previous input PTS "PREV_INT", ///< previous input time in seconds "PREV_OUTPTS", ///< previous output PTS @@ -61,6 +63,7 @@ static const char *const var_names[] = { "S", // Number of samples in the current frame "SR", // Audio sample rate "FR", ///< defined only for constant frame-rate video + "T_CHANGE", ///< time of first frame after latest command was applied NULL }; @@ -70,7 +73,9 @@ enum var_name { VAR_N, VAR_NB_CONSUMED_SAMPLES, VAR_NB_SAMPLES, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_PREV_INPTS, VAR_PREV_INT, VAR_PREV_OUTPTS, @@ -86,7 +91,8 @@ enum var_name { VAR_S, VAR_SR, VAR_FR, - VAR_VARS_NB + VAR_T_CHANGE, + VAR_VARS_NB, }; typedef struct SetPTSContext { @@ -97,6 +103,9 @@ typedef struct SetPTSContext { enum AVMediaType type; } SetPTSContext; +#define V(name_) \ + setpts->var_values[VAR_##name_] + static av_cold int init(AVFilterContext *ctx) { SetPTSContext *setpts = ctx->priv; @@ -108,14 +117,15 @@ static av_cold int init(AVFilterContext *ctx) return ret; } - setpts->var_values[VAR_N] = 0.0; - setpts->var_values[VAR_S] = 0.0; - setpts->var_values[VAR_PREV_INPTS] = NAN; - setpts->var_values[VAR_PREV_INT] = NAN; - setpts->var_values[VAR_PREV_OUTPTS] = NAN; - setpts->var_values[VAR_PREV_OUTT] = NAN; - setpts->var_values[VAR_STARTPTS] = NAN; - setpts->var_values[VAR_STARTT] = NAN; + V(N) = 0.0; + V(S) = 0.0; + V(PREV_INPTS) = NAN; + V(PREV_INT) = NAN; + V(PREV_OUTPTS) = NAN; + V(PREV_OUTT) = NAN; + V(STARTPTS) = NAN; + V(STARTT) = NAN; + V(T_CHANGE) = NAN; return 0; } @@ -125,22 +135,25 @@ static int config_input(AVFilterLink *inlink) SetPTSContext *setpts = ctx->priv; setpts->type = inlink->type; - setpts->var_values[VAR_TB] = av_q2d(inlink->time_base); - setpts->var_values[VAR_RTCSTART] = av_gettime(); + V(TB) = av_q2d(inlink->time_base); + V(RTCSTART) = av_gettime(); - setpts->var_values[VAR_SR] = - setpts->var_values[VAR_SAMPLE_RATE] = + V(SR) = V(SAMPLE_RATE) = setpts->type == AVMEDIA_TYPE_AUDIO ? inlink->sample_rate : NAN; - setpts->var_values[VAR_FRAME_RATE] = - setpts->var_values[VAR_FR] = inlink->frame_rate.num && - inlink->frame_rate.den ? - av_q2d(inlink->frame_rate) : NAN; + V(FRAME_RATE) = V(FR) = + inlink->frame_rate.num && inlink->frame_rate.den ? + av_q2d(inlink->frame_rate) : NAN; av_log(inlink->src, AV_LOG_VERBOSE, "TB:%f FRAME_RATE:%f SAMPLE_RATE:%f\n", - setpts->var_values[VAR_TB], - setpts->var_values[VAR_FRAME_RATE], - setpts->var_values[VAR_SAMPLE_RATE]); + V(TB), V(FRAME_RATE), V(SAMPLE_RATE)); + return 0; +} + +static int config_output_video(AVFilterLink *outlink) +{ + outlink->frame_rate = (AVRational){ 1, 0 }; + return 0; } @@ -155,21 +168,28 @@ static inline char *double2int64str(char *buf, double v) static double eval_pts(SetPTSContext *setpts, AVFilterLink *inlink, AVFrame *frame, int64_t pts) { - if (isnan(setpts->var_values[VAR_STARTPTS])) { - setpts->var_values[VAR_STARTPTS] = TS2D(pts); - setpts->var_values[VAR_STARTT ] = TS2T(pts, inlink->time_base); + if (isnan(V(STARTPTS))) { + V(STARTPTS) = TS2D(pts); + V(STARTT ) = TS2T(pts, inlink->time_base); + } + if (isnan(V(T_CHANGE))) { + V(T_CHANGE) = TS2T(pts, inlink->time_base); } - setpts->var_values[VAR_PTS ] = TS2D(pts); - setpts->var_values[VAR_T ] = TS2T(pts, inlink->time_base); - setpts->var_values[VAR_POS ] = !frame || frame->pkt_pos == -1 ? NAN : frame->pkt_pos; - setpts->var_values[VAR_RTCTIME ] = av_gettime(); + V(PTS ) = TS2D(pts); + V(T ) = TS2T(pts, inlink->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + V(POS ) = !frame || frame->pkt_pos == -1 ? NAN : frame->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + V(RTCTIME ) = av_gettime(); if (frame) { if (inlink->type == AVMEDIA_TYPE_VIDEO) { - setpts->var_values[VAR_INTERLACED] = frame->interlaced_frame; + V(INTERLACED) = !!(frame->flags & AV_FRAME_FLAG_INTERLACED); } else if (inlink->type == AVMEDIA_TYPE_AUDIO) { - setpts->var_values[VAR_S] = frame->nb_samples; - setpts->var_values[VAR_NB_SAMPLES] = frame->nb_samples; + V(S) = frame->nb_samples; + V(NB_SAMPLES) = frame->nb_samples; } } @@ -185,38 +205,36 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) d = eval_pts(setpts, inlink, frame, frame->pts); frame->pts = D2TS(d); + frame->duration = 0; av_log(inlink->dst, AV_LOG_TRACE, - "N:%"PRId64" PTS:%s T:%f POS:%s", - (int64_t)setpts->var_values[VAR_N], - d2istr(setpts->var_values[VAR_PTS]), - setpts->var_values[VAR_T], - d2istr(setpts->var_values[VAR_POS])); + "N:%"PRId64" PTS:%s T:%f", + (int64_t)V(N), d2istr(V(PTS)), V(T)); switch (inlink->type) { case AVMEDIA_TYPE_VIDEO: av_log(inlink->dst, AV_LOG_TRACE, " INTERLACED:%"PRId64, - (int64_t)setpts->var_values[VAR_INTERLACED]); + (int64_t)V(INTERLACED)); break; case AVMEDIA_TYPE_AUDIO: av_log(inlink->dst, AV_LOG_TRACE, " NB_SAMPLES:%"PRId64" NB_CONSUMED_SAMPLES:%"PRId64, - (int64_t)setpts->var_values[VAR_NB_SAMPLES], - (int64_t)setpts->var_values[VAR_NB_CONSUMED_SAMPLES]); + (int64_t)V(NB_SAMPLES), + (int64_t)V(NB_CONSUMED_SAMPLES)); break; } av_log(inlink->dst, AV_LOG_TRACE, " -> PTS:%s T:%f\n", d2istr(d), TS2T(d, inlink->time_base)); if (inlink->type == AVMEDIA_TYPE_VIDEO) { - setpts->var_values[VAR_N] += 1.0; + V(N) += 1.0; } else { - setpts->var_values[VAR_N] += frame->nb_samples; + V(N) += frame->nb_samples; } - setpts->var_values[VAR_PREV_INPTS ] = TS2D(in_pts); - setpts->var_values[VAR_PREV_INT ] = TS2T(in_pts, inlink->time_base); - setpts->var_values[VAR_PREV_OUTPTS] = TS2D(frame->pts); - setpts->var_values[VAR_PREV_OUTT] = TS2T(frame->pts, inlink->time_base); + V(PREV_INPTS ) = TS2D(in_pts); + V(PREV_INT ) = TS2T(in_pts, inlink->time_base); + V(PREV_OUTPTS) = TS2D(frame->pts); + V(PREV_OUTT) = TS2T(frame->pts, inlink->time_base); if (setpts->type == AVMEDIA_TYPE_AUDIO) { - setpts->var_values[VAR_NB_CONSUMED_SAMPLES] += frame->nb_samples; + V(NB_CONSUMED_SAMPLES) += frame->nb_samples; } return ff_filter_frame(inlink->dst->outputs[0], frame); } @@ -242,11 +260,8 @@ static int activate(AVFilterContext *ctx) if (ff_inlink_acknowledge_status(inlink, &status, &pts)) { double d = eval_pts(setpts, inlink, NULL, pts); - av_log(ctx, AV_LOG_TRACE, "N:EOF PTS:%s T:%f POS:%s -> PTS:%s T:%f\n", - d2istr(setpts->var_values[VAR_PTS]), - setpts->var_values[VAR_T], - d2istr(setpts->var_values[VAR_POS]), - d2istr(d), TS2T(d, inlink->time_base)); + av_log(ctx, AV_LOG_TRACE, "N:EOF PTS:%s T:%f -> PTS:%s T:%f\n", + d2istr(V(PTS)), V(T), d2istr(d), TS2T(d, inlink->time_base)); ff_outlink_set_status(outlink, status, D2TS(d)); return 0; } @@ -263,14 +278,46 @@ static av_cold void uninit(AVFilterContext *ctx) setpts->expr = NULL; } +static int process_command(AVFilterContext *ctx, const char *cmd, const char *arg, + char *res, int res_len, int flags) +{ + SetPTSContext *setpts = ctx->priv; + AVExpr *new_expr; + int ret; + + ret = ff_filter_process_command(ctx, cmd, arg, res, res_len, flags); + + if (ret < 0) + return ret; + + if (!strcmp(cmd, "expr")) { + ret = av_expr_parse(&new_expr, arg, var_names, NULL, NULL, NULL, NULL, 0, ctx); + // Only free and replace previous expression if new one succeeds, + // otherwise defensively keep everything intact even if reporting an error. + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Error while parsing expression '%s'\n", arg); + } else { + av_expr_free(setpts->expr); + setpts->expr = new_expr; + V(T_CHANGE) = NAN; + } + } else { + ret = AVERROR(EINVAL); + } + + return ret; +} +#undef V + #define OFFSET(x) offsetof(SetPTSContext, x) #define V AV_OPT_FLAG_VIDEO_PARAM #define A AV_OPT_FLAG_AUDIO_PARAM +#define R AV_OPT_FLAG_RUNTIME_PARAM #define F AV_OPT_FLAG_FILTERING_PARAM #if CONFIG_SETPTS_FILTER static const AVOption setpts_options[] = { - { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = V|F }, + { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = V|F|R }, { NULL } }; AVFILTER_DEFINE_CLASS(setpts); @@ -283,33 +330,35 @@ static const AVFilterPad avfilter_vf_setpts_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_setpts_outputs[] = { +static const AVFilterPad outputs_video[] = { { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output_video, }, }; const AVFilter ff_vf_setpts = { - .name = "setpts", - .description = NULL_IF_CONFIG_SMALL("Set PTS for the output video frame."), - .init = init, - .activate = activate, - .uninit = uninit, - .flags = AVFILTER_FLAG_METADATA_ONLY, + .name = "setpts", + .description = NULL_IF_CONFIG_SMALL("Set PTS for the output video frame."), + .init = init, + .activate = activate, + .uninit = uninit, + .process_command = process_command, + .flags = AVFILTER_FLAG_METADATA_ONLY, .priv_size = sizeof(SetPTSContext), .priv_class = &setpts_class, FILTER_INPUTS(avfilter_vf_setpts_inputs), - FILTER_OUTPUTS(avfilter_vf_setpts_outputs), + FILTER_OUTPUTS(outputs_video), }; #endif /* CONFIG_SETPTS_FILTER */ #if CONFIG_ASETPTS_FILTER static const AVOption asetpts_options[] = { - { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = A|F }, + { "expr", "Expression determining the frame timestamp", OFFSET(expr_str), AV_OPT_TYPE_STRING, { .str = "PTS" }, .flags = A|F|R }, { NULL } }; AVFILTER_DEFINE_CLASS(asetpts); @@ -322,23 +371,17 @@ static const AVFilterPad asetpts_inputs[] = { }, }; -static const AVFilterPad asetpts_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asetpts = { - .name = "asetpts", - .description = NULL_IF_CONFIG_SMALL("Set PTS for the output audio frame."), - .init = init, - .activate = activate, - .uninit = uninit, - .priv_size = sizeof(SetPTSContext), - .priv_class = &asetpts_class, - .flags = AVFILTER_FLAG_METADATA_ONLY, + .name = "asetpts", + .description = NULL_IF_CONFIG_SMALL("Set PTS for the output audio frame."), + .init = init, + .activate = activate, + .uninit = uninit, + .process_command = process_command, + .priv_size = sizeof(SetPTSContext), + .priv_class = &asetpts_class, + .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(asetpts_inputs), - FILTER_OUTPUTS(asetpts_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; #endif /* CONFIG_ASETPTS_FILTER */ diff --git a/libavfilter/settb.c b/libavfilter/settb.c index 23cb02689ba..ba58abd9e9d 100644 --- a/libavfilter/settb.c +++ b/libavfilter/settb.c @@ -165,13 +165,6 @@ static int activate(AVFilterContext *ctx) DEFINE_OPTIONS(settb, VIDEO); AVFILTER_DEFINE_CLASS(settb); -static const AVFilterPad avfilter_vf_settb_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad avfilter_vf_settb_outputs[] = { { .name = "default", @@ -185,7 +178,7 @@ const AVFilter ff_vf_settb = { .description = NULL_IF_CONFIG_SMALL("Set timebase for the video output link."), .priv_size = sizeof(SetTBContext), .priv_class = &settb_class, - FILTER_INPUTS(avfilter_vf_settb_inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(avfilter_vf_settb_outputs), .activate = activate, .flags = AVFILTER_FLAG_METADATA_ONLY, @@ -197,13 +190,6 @@ const AVFilter ff_vf_settb = { DEFINE_OPTIONS(asettb, AUDIO); AVFILTER_DEFINE_CLASS(asettb); -static const AVFilterPad avfilter_af_asettb_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - static const AVFilterPad avfilter_af_asettb_outputs[] = { { .name = "default", @@ -216,7 +202,7 @@ const AVFilter ff_af_asettb = { .name = "asettb", .description = NULL_IF_CONFIG_SMALL("Set timebase for the audio output link."), .priv_size = sizeof(SetTBContext), - FILTER_INPUTS(avfilter_af_asettb_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), FILTER_OUTPUTS(avfilter_af_asettb_outputs), .priv_class = &asettb_class, .activate = activate, diff --git a/libavfilter/signature_lookup.c b/libavfilter/signature_lookup.c index 86dd0c66754..ad59106cf0e 100644 --- a/libavfilter/signature_lookup.c +++ b/libavfilter/signature_lookup.c @@ -37,6 +37,16 @@ #define STATUS_END_REACHED 1 #define STATUS_BEGIN_REACHED 2 +static void sll_free(MatchingInfo **sll) +{ + while (*sll) { + MatchingInfo *tmp = *sll; + *sll = tmp->next; + tmp->next = NULL; + av_free(tmp); + } +} + static void fill_l1distlut(uint8_t lut[]) { int i, j, tmp_i, tmp_j,count; @@ -177,7 +187,7 @@ static MatchingInfo* get_matching_parameters(AVFilterContext *ctx, SignatureCont size_t i, j, k, l, hmax = 0, score; int framerate, offset, l1dist; double m; - MatchingInfo *cands = NULL, *c = NULL; + MatchingInfo cands = { 0 }, *c = &cands; struct { uint8_t size; @@ -195,11 +205,17 @@ static MatchingInfo* get_matching_parameters(AVFilterContext *ctx, SignatureCont } hspace_elem; /* houghspace */ - hspace_elem** hspace = av_malloc_array(MAX_FRAMERATE, sizeof(hspace_elem *)); + hspace_elem **hspace = av_malloc(MAX_FRAMERATE * sizeof(*hspace)); + hspace_elem *hspaces; + if (!hspace) + return NULL; /* initialize houghspace */ + hspaces = av_malloc((2 * HOUGH_MAX_OFFSET + 1) * sizeof(*hspaces) * MAX_FRAMERATE); + if (!hspaces) + goto error; for (i = 0; i < MAX_FRAMERATE; i++) { - hspace[i] = av_malloc_array(2 * HOUGH_MAX_OFFSET + 1, sizeof(hspace_elem)); + hspace[i] = hspaces + i * (2 * HOUGH_MAX_OFFSET + 1); for (j = 0; j < 2 * HOUGH_MAX_OFFSET + 1; j++) { hspace[i][j].score = 0; hspace[i][j].dist = 99999; @@ -279,16 +295,11 @@ static MatchingInfo* get_matching_parameters(AVFilterContext *ctx, SignatureCont for (i = 0; i < MAX_FRAMERATE; i++) { for (j = 0; j < HOUGH_MAX_OFFSET; j++) { if (hmax < hspace[i][j].score) { - if (c == NULL) { - c = av_malloc(sizeof(MatchingInfo)); - if (!c) - av_log(ctx, AV_LOG_FATAL, "Could not allocate memory"); - cands = c; - } else { - c->next = av_malloc(sizeof(MatchingInfo)); - if (!c->next) - av_log(ctx, AV_LOG_FATAL, "Could not allocate memory"); - c = c->next; + c->next = av_malloc(sizeof(MatchingInfo)); + c = c->next; + if (!c) { + sll_free(&cands.next); + goto error; } c->framerateratio = (i+1.0) / 30; c->score = hspace[i][j].score; @@ -305,11 +316,10 @@ static MatchingInfo* get_matching_parameters(AVFilterContext *ctx, SignatureCont } } } - for (i = 0; i < MAX_FRAMERATE; i++) { - av_freep(&hspace[i]); - } + error: av_freep(&hspace); - return cands; + av_free(hspaces); + return cands.next; } static int iterate_frame(double frr, FineSignature **a, FineSignature **b, int fcount, int *bcount, int dir) @@ -437,14 +447,14 @@ static MatchingInfo evaluate_parameters(AVFilterContext *ctx, SignatureContext * } if (tolerancecount > 2) { - a = aprev; - b = bprev; if (dir == DIR_NEXT) { /* turn around */ a = infos->first; b = infos->second; dir = DIR_PREV; } else { + a = aprev; + b = bprev; break; } } @@ -485,10 +495,10 @@ static MatchingInfo evaluate_parameters(AVFilterContext *ctx, SignatureContext * continue; /* matching sequence is too short */ if ((double) goodfcount / (double) fcount < sc->thit) continue; - if ((double) goodfcount*0.5 < FFMAX(gooda, goodb)) + if ((double) goodfcount*0.5 <= FFMAX(gooda, goodb)) continue; - meandist = (double) goodfcount / (double) distsum; + meandist = (double) distsum / (double) goodfcount; if (meandist < minmeandist || status == (STATUS_END_REACHED | STATUS_BEGIN_REACHED) || @@ -520,16 +530,6 @@ static MatchingInfo evaluate_parameters(AVFilterContext *ctx, SignatureContext * return bestmatch; } -static void sll_free(MatchingInfo *sll) -{ - void *tmp; - while (sll) { - tmp = sll; - sll = sll->next; - av_freep(&tmp); - } -} - static MatchingInfo lookup_signatures(AVFilterContext *ctx, SignatureContext *sc, StreamContext *first, StreamContext *second, int mode) { CoarseSignature *cs, *cs2; @@ -572,7 +572,7 @@ static MatchingInfo lookup_signatures(AVFilterContext *ctx, SignatureContext *sc "ratio %f, offset %d, score %d, %d frames matching\n", bestmatch.first->index, bestmatch.second->index, bestmatch.framerateratio, bestmatch.offset, bestmatch.score, bestmatch.matchframes); - sll_free(infos); + sll_free(&infos); } } while (find_next_coarsecandidate(sc, second->coarsesiglist, &cs, &cs2, 0) && !bestmatch.whole); return bestmatch; diff --git a/libavfilter/silenceremove_template.c b/libavfilter/silenceremove_template.c new file mode 100644 index 00000000000..2f34fb5958b --- /dev/null +++ b/libavfilter/silenceremove_template.c @@ -0,0 +1,446 @@ +/* + * 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 + */ + +#undef ftype +#undef FABS +#undef FMAX +#undef SAMPLE_FORMAT +#undef SQRT +#undef ZERO +#undef ONE +#undef TMIN +#if DEPTH == 32 +#define SAMPLE_FORMAT flt +#define SQRT sqrtf +#define FMAX fmaxf +#define FABS fabsf +#define ftype float +#define ZERO 0.f +#define ONE 1.f +#define TMIN -FLT_MAX +#else +#define SAMPLE_FORMAT dbl +#define SQRT sqrt +#define FMAX fmax +#define FABS fabs +#define ftype double +#define ZERO 0.0 +#define ONE 1.0 +#define TMIN -DBL_MAX +#endif + +#define fn3(a,b) a##_##b +#define fn2(a,b) fn3(a,b) +#define fn(a) fn2(a, SAMPLE_FORMAT) + +static void fn(flush)(ftype *dst, const ftype *src, int src_pos, + int nb_channels, int count, int src_nb_samples, + int *out_nb_samples) +{ + int oidx, out_count = count; + int sidx = src_pos; + + if (count <= 0) + return; + + oidx = *out_nb_samples + out_count - 1; + *out_nb_samples += out_count; + while (out_count-- > 0) { + const int spos = sidx * nb_channels; + const int opos = oidx * nb_channels; + + for (int ch = 0; ch < nb_channels; ch++) + dst[opos + ch] = src[spos + ch]; + + oidx--; + sidx--; + if (sidx < 0) + sidx = src_nb_samples - 1; + } +} + +static void fn(queue_sample)(AVFilterContext *ctx, + const ftype *src, + ftype *queue, + int *queue_pos, + int *queue_size, + int *window_pos, + int *window_size, + const int nb_channels, + const int nb_samples, + const int window_nb_samples) +{ + const int pos = *queue_pos * nb_channels; + + for (int ch = 0; ch < nb_channels; ch++) + queue[pos + ch] = src[ch]; + + (*queue_pos)++; + if (*queue_pos >= nb_samples) + *queue_pos = 0; + + if (*queue_size < nb_samples) + (*queue_size)++; + + if (*window_size < window_nb_samples) + (*window_size)++; + + (*window_pos)++; + if (*window_pos >= window_nb_samples) + *window_pos = 0; +} + +static ftype fn(compute_avg)(ftype *cache, ftype x, ftype px, + int window_size, int *unused, int *unused2) +{ + ftype r; + + cache[0] += FABS(x); + cache[0] -= FABS(px); + cache[0] = r = FMAX(cache[0], ZERO); + + return r / window_size; +} + +#define PEAKS(empty_value,op,sample, psample)\ + if (!empty && psample == ss[front]) { \ + ss[front] = empty_value; \ + if (back != front) { \ + front--; \ + if (front < 0) \ + front = n - 1; \ + } \ + empty = front == back; \ + } \ + \ + if (!empty && sample op ss[front]) { \ + while (1) { \ + ss[front] = empty_value; \ + if (back == front) { \ + empty = 1; \ + break; \ + } \ + front--; \ + if (front < 0) \ + front = n - 1; \ + } \ + } \ + \ + while (!empty && sample op ss[back]) { \ + ss[back] = empty_value; \ + if (back == front) { \ + empty = 1; \ + break; \ + } \ + back++; \ + if (back >= n) \ + back = 0; \ + } \ + \ + if (!empty) { \ + back--; \ + if (back < 0) \ + back = n - 1; \ + } + +static ftype fn(compute_median)(ftype *ss, ftype x, ftype px, + int n, int *ffront, int *bback) +{ + ftype r, ax = FABS(x); + int front = *ffront; + int back = *bback; + int empty = front == back && ss[front] == -ONE; + int idx; + + PEAKS(-ONE, >, ax, FABS(px)) + + ss[back] = ax; + idx = (back <= front) ? back + (front - back + 1) / 2 : back + (n + front - back + 1) / 2; + if (idx >= n) + idx -= n; + av_assert2(idx >= 0 && idx < n); + r = ss[idx]; + + *ffront = front; + *bback = back; + + return r; +} + +static ftype fn(compute_peak)(ftype *ss, ftype x, ftype px, + int n, int *ffront, int *bback) +{ + ftype r, ax = FABS(x); + int front = *ffront; + int back = *bback; + int empty = front == back && ss[front] == ZERO; + + PEAKS(ZERO, >=, ax, FABS(px)) + + ss[back] = ax; + r = ss[front]; + + *ffront = front; + *bback = back; + + return r; +} + +static ftype fn(compute_ptp)(ftype *ss, ftype x, ftype px, + int n, int *ffront, int *bback) +{ + int front = *ffront; + int back = *bback; + int empty = front == back && ss[front] == TMIN; + ftype r, max, min; + + PEAKS(TMIN, >=, x, px) + + ss[back] = x; + max = ss[front]; + min = x; + r = FABS(min) + FABS(max - min); + + *ffront = front; + *bback = back; + + return r; +} + +static ftype fn(compute_rms)(ftype *cache, ftype x, ftype px, + int window_size, int *unused, int *unused2) +{ + ftype r; + + cache[0] += x * x; + cache[0] -= px * px; + cache[0] = r = FMAX(cache[0], ZERO); + + return SQRT(r / window_size); +} + +static ftype fn(compute_dev)(ftype *ss, ftype x, ftype px, + int n, int *unused, int *unused2) +{ + ftype r; + + ss[0] += x; + ss[0] -= px; + + ss[1] += x * x; + ss[1] -= px * px; + ss[1] = FMAX(ss[1], ZERO); + + r = FMAX(ss[1] - ss[0] * ss[0] / n, ZERO) / n; + + return SQRT(r); +} + +static void fn(filter_start)(AVFilterContext *ctx, + const ftype *src, ftype *dst, + int *nb_out_samples, + const int nb_channels) +{ + SilenceRemoveContext *s = ctx->priv; + const int start_periods = s->start_periods; + int out_nb_samples = *nb_out_samples; + const int start_window_nb_samples = s->start_window->nb_samples; + const int start_nb_samples = s->start_queuef->nb_samples; + const int start_wpos = s->start_window_pos * nb_channels; + const int start_pos = s->start_queue_pos * nb_channels; + ftype *startw = (ftype *)s->start_window->data[0]; + ftype *start = (ftype *)s->start_queuef->data[0]; + const ftype start_threshold = s->start_threshold; + const int start_mode = s->start_mode; + int start_thres = (start_mode == T_ANY) ? 0 : 1; + const int start_duration = s->start_duration; + ftype *start_cache = (ftype *)s->start_cache; + const int start_silence = s->start_silence; + int window_size = start_window_nb_samples; + const int cache_size = s->cache_size; + int *front = s->start_front; + int *back = s->start_back; + + fn(queue_sample)(ctx, src, start, + &s->start_queue_pos, + &s->start_queue_size, + &s->start_window_pos, + &s->start_window_size, + nb_channels, + start_nb_samples, + start_window_nb_samples); + + if (s->start_found_periods < 0) + goto skip; + + if (s->detection != D_PEAK && s->detection != D_MEDIAN && + s->detection != D_PTP) + window_size = s->start_window_size; + + for (int ch = 0; ch < nb_channels; ch++) { + ftype start_sample = start[start_pos + ch]; + ftype start_ow = startw[start_wpos + ch]; + ftype tstart; + + tstart = fn(s->compute)(start_cache + ch * cache_size, + start_sample, + start_ow, + window_size, + front + ch, + back + ch); + + startw[start_wpos + ch] = start_sample; + + if (start_mode == T_ANY) { + start_thres |= tstart > start_threshold; + } else { + start_thres &= tstart > start_threshold; + } + } + + if (s->start_found_periods >= 0) { + if (start_silence > 0) { + s->start_silence_count++; + if (s->start_silence_count > start_silence) + s->start_silence_count = start_silence; + } + + s->start_sample_count += start_thres; + } + + if (s->start_sample_count > start_duration) { + s->start_found_periods++; + if (s->start_found_periods >= start_periods) { + if (!ctx->is_disabled) + fn(flush)(dst, start, s->start_queue_pos, nb_channels, + s->start_silence_count, start_nb_samples, + &out_nb_samples); + s->start_silence_count = 0; + s->start_found_periods = -1; + } + + s->start_sample_count = 0; + } + +skip: + if (s->start_found_periods < 0 || ctx->is_disabled) { + const int dst_pos = out_nb_samples * nb_channels; + for (int ch = 0; ch < nb_channels; ch++) + dst[dst_pos + ch] = start[start_pos + ch]; + out_nb_samples++; + } + + *nb_out_samples = out_nb_samples; +} + +static void fn(filter_stop)(AVFilterContext *ctx, + const ftype *src, ftype *dst, + int *nb_out_samples, + const int nb_channels) +{ + SilenceRemoveContext *s = ctx->priv; + const int stop_periods = s->stop_periods; + int out_nb_samples = *nb_out_samples; + const int stop_window_nb_samples = s->stop_window->nb_samples; + const int stop_nb_samples = s->stop_queuef->nb_samples; + const int stop_wpos = s->stop_window_pos * nb_channels; + const int stop_pos = s->stop_queue_pos * nb_channels; + ftype *stopw = (ftype *)s->stop_window->data[0]; + const ftype stop_threshold = s->stop_threshold; + ftype *stop = (ftype *)s->stop_queuef->data[0]; + const int stop_mode = s->stop_mode; + int stop_thres = (stop_mode == T_ANY) ? 0 : 1; + const int stop_duration = s->stop_duration; + ftype *stop_cache = (ftype *)s->stop_cache; + const int stop_silence = s->stop_silence; + int window_size = stop_window_nb_samples; + const int cache_size = s->cache_size; + const int restart = s->restart; + int *front = s->stop_front; + int *back = s->stop_back; + + fn(queue_sample)(ctx, src, stop, + &s->stop_queue_pos, + &s->stop_queue_size, + &s->stop_window_pos, + &s->stop_window_size, + nb_channels, + stop_nb_samples, + stop_window_nb_samples); + + if (s->detection != D_PEAK && s->detection != D_MEDIAN && + s->detection != D_PTP) + window_size = s->stop_window_size; + + for (int ch = 0; ch < nb_channels; ch++) { + ftype stop_sample = stop[stop_pos + ch]; + ftype stop_ow = stopw[stop_wpos + ch]; + ftype tstop; + + tstop = fn(s->compute)(stop_cache + ch * cache_size, + stop_sample, + stop_ow, + window_size, + front + ch, + back + ch); + + stopw[stop_wpos + ch] = stop_sample; + + if (stop_mode == T_ANY) { + stop_thres |= tstop <= stop_threshold; + } else { + stop_thres &= tstop <= stop_threshold; + } + } + + s->found_nonsilence = FFMAX(s->found_nonsilence, !stop_thres); + if (restart && !stop_thres) + s->stop_found_periods = 0; + + if (s->stop_found_periods >= 0 || ctx->is_disabled) { + if (s->found_nonsilence) { + s->stop_sample_count += stop_thres; + s->stop_sample_count *= stop_thres; + } + } else if (s->stop_silence_count > 0) { + const int dst_pos = out_nb_samples * nb_channels; + for (int ch = 0; ch < nb_channels; ch++) + dst[dst_pos + ch] = stop[stop_pos + ch]; + s->stop_silence_count--; + out_nb_samples++; + } + + if (s->stop_sample_count > stop_duration) { + s->stop_found_periods++; + if (s->stop_found_periods >= stop_periods) { + s->stop_found_periods = -1; + s->stop_silence_count = stop_silence; + } + + s->stop_sample_count = 0; + } + + if (s->stop_found_periods >= 0 || ctx->is_disabled) { + const int dst_pos = out_nb_samples * nb_channels; + for (int ch = 0; ch < nb_channels; ch++) + dst[dst_pos + ch] = stop[stop_pos + ch]; + out_nb_samples++; + } + + *nb_out_samples = out_nb_samples; +} diff --git a/libavfilter/split.c b/libavfilter/split.c index 98b51f976ec..7935f2d518b 100644 --- a/libavfilter/split.c +++ b/libavfilter/split.c @@ -28,13 +28,11 @@ #include "libavutil/attributes.h" #include "libavutil/avstring.h" #include "libavutil/internal.h" -#include "libavutil/mem.h" #include "libavutil/opt.h" #include "avfilter.h" #include "audio.h" #include "filters.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -67,11 +65,15 @@ static int activate(AVFilterContext *ctx) { AVFilterLink *inlink = ctx->inputs[0]; AVFrame *in; - int status, ret; + int status, ret, nb_eofs = 0; int64_t pts; - for (int i = 0; i < ctx->nb_outputs; i++) { - FF_FILTER_FORWARD_STATUS_BACK_ALL(ctx->outputs[i], ctx); + for (int i = 0; i < ctx->nb_outputs; i++) + nb_eofs += ff_outlink_get_status(ctx->outputs[i]) == AVERROR_EOF; + + if (nb_eofs == ctx->nb_outputs) { + ff_inlink_set_status(inlink, AVERROR_EOF); + return 0; } ret = ff_inlink_consume_frame(inlink, &in); @@ -130,13 +132,6 @@ static const AVOption options[] = { AVFILTER_DEFINE_CLASS_EXT(split, "(a)split", options); -static const AVFilterPad avfilter_vf_split_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_split = { .name = "split", .description = NULL_IF_CONFIG_SMALL("Pass on the input to N video outputs."), @@ -144,18 +139,11 @@ const AVFilter ff_vf_split = { .priv_class = &split_class, .init = split_init, .activate = activate, - FILTER_INPUTS(avfilter_vf_split_inputs), + FILTER_INPUTS(ff_video_default_filterpad), .outputs = NULL, .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS | AVFILTER_FLAG_METADATA_ONLY, }; -static const AVFilterPad avfilter_af_asplit_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_asplit = { .name = "asplit", .description = NULL_IF_CONFIG_SMALL("Pass on the audio input to N audio outputs."), @@ -163,7 +151,7 @@ const AVFilter ff_af_asplit = { .priv_size = sizeof(SplitContext), .init = split_init, .activate = activate, - FILTER_INPUTS(avfilter_af_asplit_inputs), + FILTER_INPUTS(ff_audio_default_filterpad), .outputs = NULL, .flags = AVFILTER_FLAG_DYNAMIC_OUTPUTS | AVFILTER_FLAG_METADATA_ONLY, }; diff --git a/libavfilter/src_avsynctest.c b/libavfilter/src_avsynctest.c index 78e4a2ba506..9fd0b590c1e 100644 --- a/libavfilter/src_avsynctest.c +++ b/libavfilter/src_avsynctest.c @@ -67,6 +67,7 @@ typedef struct AVSyncTestContext { #define OFFSET(x) offsetof(AVSyncTestContext, x) #define A AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_FILTERING_PARAM #define V AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define R AV_OPT_FLAG_RUNTIME_PARAM static const AVOption avsynctest_options[] = { {"size", "set frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, V }, @@ -75,14 +76,14 @@ static const AVOption avsynctest_options[] = { {"fr", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="30"}, 0,INT_MAX, V }, {"samplerate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100},8000,384000, A }, {"sr", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64=44100},8000,384000, A }, - {"amplitude", "set beep amplitude", OFFSET(amplitude), AV_OPT_TYPE_FLOAT, {.dbl=.7}, 0., 1., A }, - {"a", "set beep amplitude", OFFSET(amplitude), AV_OPT_TYPE_FLOAT, {.dbl=.7}, 0., 1., A }, + {"amplitude", "set beep amplitude", OFFSET(amplitude), AV_OPT_TYPE_FLOAT, {.dbl=.7}, 0., 1., A|R }, + {"a", "set beep amplitude", OFFSET(amplitude), AV_OPT_TYPE_FLOAT, {.dbl=.7}, 0., 1., A|R }, {"period", "set beep period", OFFSET(period), AV_OPT_TYPE_INT, {.i64=3}, 1, 99., A }, {"p", "set beep period", OFFSET(period), AV_OPT_TYPE_INT, {.i64=3}, 1, 99., A }, - {"delay", "set flash delay", OFFSET(delay), AV_OPT_TYPE_INT, {.i64=0}, -30, 30, V }, - {"dl", "set flash delay", OFFSET(delay), AV_OPT_TYPE_INT, {.i64=0}, -30, 30, V }, - {"cycle", "set delay cycle", OFFSET(cycle), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, V }, - {"c", "set delay cycle", OFFSET(cycle), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, V }, + {"delay", "set flash delay", OFFSET(delay), AV_OPT_TYPE_INT, {.i64=0}, -30, 30, V|R }, + {"dl", "set flash delay", OFFSET(delay), AV_OPT_TYPE_INT, {.i64=0}, -30, 30, V|R }, + {"cycle", "set delay cycle", OFFSET(cycle), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, V|R }, + {"c", "set delay cycle", OFFSET(cycle), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, V|R }, {"duration", "set duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT64_MAX, V|A }, {"d", "set duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT64_MAX, V|A }, {"fg", "set foreground color", OFFSET(rgba[0]), AV_OPT_TYPE_COLOR, {.str="white"}, 0, 0, V }, @@ -159,7 +160,7 @@ static av_cold int config_props(AVFilterLink *outlink) s->dir = 1; s->prev_intpart = INT64_MIN; - ff_draw_init(&s->draw, outlink->format, 0); + ff_draw_init2(&s->draw, outlink->format, outlink->colorspace, outlink->color_range, 0); ff_draw_color(&s->draw, &s->fg, s->rgba[0]); ff_draw_color(&s->draw, &s->bg, s->rgba[1]); @@ -170,7 +171,7 @@ static av_cold int config_props(AVFilterLink *outlink) #define FPI 0x8000 -static int32_t sin32(int32_t x, int shift) +static int32_t sin32(int32_t x, AVRational scale) { const double pi = M_PI; const int32_t a = ((2.0 * pi) * (1 << 24)); @@ -194,7 +195,8 @@ static int32_t sin32(int32_t x, int shift) result = a + t2; result *= x; result += (1U << 31); - result >>= (32 - shift); + result >>= 17; + result = av_rescale(result, scale.num, scale.den); return result; } @@ -203,7 +205,7 @@ static int audio_frame(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; AVSyncTestContext *s = ctx->priv; - const int a = lrintf(s->amplitude * 15); + const AVRational a = av_d2q(s->amplitude, 32768); int64_t duration[2]; int64_t delta; AVFrame *out; @@ -277,6 +279,9 @@ static int video_frame(AVFilterLink *outlink) int64_t delta, temp, intpart; AVFrame *out; + if (!s->cycle) + s->vdelay = av_make_q(s->delay, 1); + delta = av_rescale_q(s->apts, s->frame_rate, av_make_q(s->sample_rate, 1)) - s->vpts; if (delta < 0) return 1; @@ -400,4 +405,5 @@ const AVFilter ff_avsrc_avsynctest = { .activate = activate, FILTER_OUTPUTS(avsynctest_outputs), FILTER_QUERY_FUNC(query_formats), + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/src_movie.c b/libavfilter/src_movie.c index 5937613d131..e50ebc99dce 100644 --- a/libavfilter/src_movie.c +++ b/libavfilter/src_movie.c @@ -23,7 +23,6 @@ * @file * movie video source * - * @todo use direct rendering (no allocation of a new frame) * @todo support a PTS correction mechanism */ @@ -46,15 +45,19 @@ #include "audio.h" #include "avfilter.h" +#include "filters.h" #include "formats.h" #include "internal.h" #include "video.h" typedef struct MovieStream { + AVFilterLink *link; AVStream *st; AVCodecContext *codec_ctx; int64_t discontinuity_threshold; int64_t last_pts; + AVFrame *frame; + int eof; } MovieStream; typedef struct MovieContext { @@ -71,8 +74,10 @@ typedef struct MovieContext { int64_t ts_offset; int dec_threads; + AVPacket *pkt; AVFormatContext *format_ctx; + int eof; int max_stream_index; /**< max stream # actually used for output */ MovieStream *st; /**< array of all streams, one per output */ int *out_index; /**< stream number -> output number map, or -1 */ @@ -100,7 +105,6 @@ static const AVOption movie_options[]= { }; static int movie_config_output_props(AVFilterLink *outlink); -static int movie_request_frame(AVFilterLink *outlink); static AVStream *find_stream(void *log, AVFormatContext *avf, const char *spec) { @@ -156,6 +160,56 @@ static AVStream *find_stream(void *log, AVFormatContext *avf, const char *spec) return found; } +static int get_buffer(AVCodecContext *avctx, AVFrame *frame, int flags) +{ + int linesize_align[AV_NUM_DATA_POINTERS]; + MovieStream *st = avctx->opaque; + AVFilterLink *outlink = st->link; + int w, h, ow, oh, copy = 0; + AVFrame *new; + + h = oh = frame->height; + w = ow = frame->width; + + copy = frame->format != outlink->format; + switch (avctx->codec_type) { + case AVMEDIA_TYPE_VIDEO: + if (w != outlink->w || h != outlink->h) + copy |= 1; + break; + case AVMEDIA_TYPE_AUDIO: + if (outlink->sample_rate != frame->sample_rate || + av_channel_layout_compare(&outlink->ch_layout, &frame->ch_layout)) + copy |= 1; + break; + } + + if (copy || !(avctx->codec->capabilities & AV_CODEC_CAP_DR1)) + return avcodec_default_get_buffer2(avctx, frame, flags); + + switch (avctx->codec_type) { + case AVMEDIA_TYPE_VIDEO: + avcodec_align_dimensions2(avctx, &w, &h, linesize_align); + new = ff_default_get_video_buffer(outlink, w, h); + break; + case AVMEDIA_TYPE_AUDIO: + new = ff_default_get_audio_buffer(outlink, frame->nb_samples); + break; + default: + return -1; + } + + av_frame_copy_props(new, frame); + av_frame_unref(frame); + av_frame_move_ref(frame, new); + av_frame_free(&new); + + frame->width = ow; + frame->height = oh; + + return 0; +} + static int open_stream(AVFilterContext *ctx, MovieStream *st, int dec_threads) { const AVCodec *codec; @@ -171,6 +225,8 @@ static int open_stream(AVFilterContext *ctx, MovieStream *st, int dec_threads) if (!st->codec_ctx) return AVERROR(ENOMEM); + st->codec_ctx->opaque = st; + st->codec_ctx->get_buffer2 = get_buffer; ret = avcodec_parameters_to_context(st->codec_ctx, st->st->codecpar); if (ret < 0) return ret; @@ -279,6 +335,9 @@ static av_cold int movie_common_init(AVFilterContext *ctx) for (i = 0; i < movie->format_ctx->nb_streams; i++) movie->format_ctx->streams[i]->discard = AVDISCARD_ALL; + movie->pkt = av_packet_alloc(); + if (!movie->pkt) + return AVERROR(ENOMEM); movie->st = av_calloc(nb_streams, sizeof(*movie->st)); if (!movie->st) return AVERROR(ENOMEM); @@ -296,6 +355,10 @@ static av_cold int movie_common_init(AVFilterContext *ctx) movie->max_stream_index = FFMAX(movie->max_stream_index, st->index); movie->st[i].discontinuity_threshold = av_rescale_q(movie->discontinuity_threshold, AV_TIME_BASE_Q, st->time_base); + + movie->st[i].frame = av_frame_alloc(); + if (!movie->st[i].frame) + return AVERROR(ENOMEM); } if (av_strtok(NULL, "+", &cursor)) return AVERROR_BUG; @@ -314,7 +377,6 @@ static av_cold int movie_common_init(AVFilterContext *ctx) if (!pad.name) return AVERROR(ENOMEM); pad.config_props = movie_config_output_props; - pad.request_frame = movie_request_frame; if ((ret = ff_append_outpad_free_name(ctx, &pad)) < 0) return ret; if ( movie->st[i].st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && @@ -343,7 +405,9 @@ static av_cold void movie_uninit(AVFilterContext *ctx) for (i = 0; i < ctx->nb_outputs; i++) { if (movie->st[i].st) avcodec_free_context(&movie->st[i].codec_ctx); + av_frame_free(&movie->st[i].frame); } + av_packet_free(&movie->pkt); av_freep(&movie->st); av_freep(&movie->out_index); if (movie->format_ctx) @@ -406,33 +470,9 @@ static int movie_config_output_props(AVFilterLink *outlink) break; } - return 0; -} + st->link = outlink; -static char *describe_frame_to_str(char *dst, size_t dst_size, - AVFrame *frame, enum AVMediaType frame_type, - AVFilterLink *link) -{ - switch (frame_type) { - case AVMEDIA_TYPE_VIDEO: - snprintf(dst, dst_size, - "video pts:%s time:%s size:%dx%d aspect:%d/%d", - av_ts2str(frame->pts), av_ts2timestr(frame->pts, &link->time_base), - frame->width, frame->height, - frame->sample_aspect_ratio.num, - frame->sample_aspect_ratio.den); - break; - case AVMEDIA_TYPE_AUDIO: - snprintf(dst, dst_size, - "audio pts:%s time:%s samples:%d", - av_ts2str(frame->pts), av_ts2timestr(frame->pts, &link->time_base), - frame->nb_samples); - break; - default: - snprintf(dst, dst_size, "%s BUG", av_get_media_type_string(frame_type)); - break; - } - return dst; + return 0; } static int rewind_file(AVFilterContext *ctx) @@ -456,145 +496,137 @@ static int rewind_file(AVFilterContext *ctx) return 0; } -static int movie_decode_packet(AVFilterContext *ctx) +static int flush_decoder(AVFilterContext *ctx, int i) { MovieContext *movie = ctx->priv; - AVPacket pkt = { 0 }; - int pkt_out_id, ret; - - /* read a new packet from input stream */ - ret = av_read_frame(movie->format_ctx, &pkt); - if (ret == AVERROR_EOF) { - /* EOF -> set all decoders for flushing */ - for (int i = 0; i < ctx->nb_outputs; i++) { - ret = avcodec_send_packet(movie->st[i].codec_ctx, NULL); - if (ret < 0 && ret != AVERROR_EOF) - return ret; - } + AVCodecContext *dec = movie->st[i].codec_ctx; - return 0; - } else if (ret < 0) - return ret; - - /* send the packet to its decoder, if any */ - pkt_out_id = pkt.stream_index > movie->max_stream_index ? -1 : - movie->out_index[pkt.stream_index]; - if (pkt_out_id >= 0) - ret = avcodec_send_packet(movie->st[pkt_out_id].codec_ctx, &pkt); - av_packet_unref(&pkt); - - return ret; + return avcodec_send_packet(dec, NULL); } -/** - * Try to push a frame to the requested output. - * - * @param ctx filter context - * @param out_id number of output where a frame is wanted; - * @return 0 if a frame was pushed on the requested output, - * AVERROR(EAGAIN) if the decoder requires more input - * AVERROR(EOF) if the decoder has been completely flushed - * <0 AVERROR code - */ -static int movie_push_frame(AVFilterContext *ctx, unsigned out_id) +static int decode_packet(AVFilterContext *ctx, int i) { - MovieContext *movie = ctx->priv; - MovieStream *st = &movie->st[out_id]; - AVFilterLink *outlink = ctx->outputs[out_id]; - AVFrame *frame; - int ret; - - frame = av_frame_alloc(); - if (!frame) - return AVERROR(ENOMEM); - - ret = avcodec_receive_frame(st->codec_ctx, frame); - if (ret < 0) { - if (ret != AVERROR_EOF && ret != AVERROR(EAGAIN)) - av_log(ctx, AV_LOG_WARNING, "Decode error: %s\n", av_err2str(ret)); - - av_frame_free(&frame); - return ret; + AVFilterLink *outlink = ctx->outputs[i]; + MovieContext *movie = ctx->priv; + MovieStream *st = &movie->st[i]; + AVCodecContext *dec = movie->st[i].codec_ctx; + AVFrame *frame = movie->st[i].frame; + AVPacket *pkt = movie->pkt; + int ret = 0; + + // submit the packet to the decoder + if (!movie->eof) { + ret = avcodec_send_packet(dec, pkt); + if (ret < 0) + return ret; } - frame->pts = frame->best_effort_timestamp; - if (frame->pts != AV_NOPTS_VALUE) { - if (movie->ts_offset) - frame->pts += av_rescale_q_rnd(movie->ts_offset, AV_TIME_BASE_Q, outlink->time_base, AV_ROUND_UP); - if (st->discontinuity_threshold) { - if (st->last_pts != AV_NOPTS_VALUE) { - int64_t diff = frame->pts - st->last_pts; - if (diff < 0 || diff > st->discontinuity_threshold) { - av_log(ctx, AV_LOG_VERBOSE, "Discontinuity in stream:%d diff:%"PRId64"\n", out_id, diff); - movie->ts_offset += av_rescale_q_rnd(-diff, outlink->time_base, AV_TIME_BASE_Q, AV_ROUND_UP); - frame->pts -= diff; + // get all the available frames from the decoder + if (ret >= 0) { + ret = avcodec_receive_frame(dec, frame); + if (ret < 0) { + // those two return values are special and mean there is no output + // frame available, but there were no errors during decoding + if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) + return 0; + return ret; + } + + frame->pts = frame->best_effort_timestamp; + if (frame->pts != AV_NOPTS_VALUE) { + if (movie->ts_offset) + frame->pts += av_rescale_q_rnd(movie->ts_offset, AV_TIME_BASE_Q, outlink->time_base, AV_ROUND_UP); + if (st->discontinuity_threshold) { + if (st->last_pts != AV_NOPTS_VALUE) { + int64_t diff = frame->pts - st->last_pts; + if (diff < 0 || diff > st->discontinuity_threshold) { + av_log(ctx, AV_LOG_VERBOSE, "Discontinuity in stream:%d diff:%"PRId64"\n", i, diff); + movie->ts_offset += av_rescale_q_rnd(-diff, outlink->time_base, AV_TIME_BASE_Q, AV_ROUND_UP); + frame->pts -= diff; + } } } + st->last_pts = frame->pts; } - st->last_pts = frame->pts; - } - ff_dlog(ctx, "movie_push_frame(): file:'%s' %s\n", movie->file_name, - describe_frame_to_str((char[1024]){0}, 1024, frame, - st->st->codecpar->codec_type, outlink)); - - if (st->st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { - if (frame->format != outlink->format) { - av_log(ctx, AV_LOG_ERROR, "Format changed %s -> %s, discarding frame\n", - av_get_pix_fmt_name(outlink->format), - av_get_pix_fmt_name(frame->format) - ); - av_frame_free(&frame); - return 0; - } + ret = ff_filter_frame(outlink, av_frame_clone(frame)); + if (ret < 0) + return ret; + if (ret == 0) + return 1; } - ret = ff_filter_frame(outlink, frame); - if (ret < 0) - return ret; return 0; } -static int movie_request_frame(AVFilterLink *outlink) +static int activate(AVFilterContext *ctx) { - AVFilterContext *ctx = outlink->src; - MovieContext *movie = ctx->priv; - unsigned out_id = FF_OUTLINK_IDX(outlink); + MovieContext *movie = ctx->priv; + int wanted = 0, ret; - while (1) { - int got_eagain = 0, got_eof = 0; - int ret = 0; + for (int i = 0; i < ctx->nb_outputs; i++) { + if (ff_outlink_frame_wanted(ctx->outputs[i])) + wanted++; + } - /* check all decoders for available output */ - for (int i = 0; i < ctx->nb_outputs; i++) { - ret = movie_push_frame(ctx, i); - if (ret == AVERROR(EAGAIN)) - got_eagain++; - else if (ret == AVERROR_EOF) - got_eof++; - else if (ret < 0) - return ret; - else if (i == out_id) - return 0; + if (wanted == 0) + return FFERROR_NOT_READY; + + if (!movie->eof) { + ret = av_read_frame(movie->format_ctx, movie->pkt); + if (ret < 0) { + movie->eof = 1; + for (int i = 0; i < ctx->nb_outputs; i++) + flush_decoder(ctx, i); + ff_filter_set_ready(ctx, 100); + return 0; + } else { + int pkt_out_id = movie->pkt->stream_index > movie->max_stream_index ? -1 : + movie->out_index[movie->pkt->stream_index]; + + if (pkt_out_id >= 0) { + ret = decode_packet(ctx, pkt_out_id); + } + av_packet_unref(movie->pkt); + ff_filter_set_ready(ctx, 100); + return (ret <= 0) ? ret : 0; } + } else { + int nb_eofs = 0; - if (got_eagain) { - /* all decoders require more input -> read a new packet */ - ret = movie_decode_packet(ctx); + for (int i = 0; i < ctx->nb_outputs; i++) { + if (!movie->st[i].eof) { + ret = decode_packet(ctx, i); + if (ret <= 0) + movie->st[i].eof = 1; + } + nb_eofs += movie->st[i].eof == 1; + } + if (nb_eofs == ctx->nb_outputs && movie->loop_count != 1) { + ret = rewind_file(ctx); if (ret < 0) return ret; - } else if (got_eof) { - /* all decoders flushed */ - if (movie->loop_count != 1) { - ret = rewind_file(ctx); - if (ret < 0) - return ret; - movie->loop_count -= movie->loop_count > 1; - av_log(ctx, AV_LOG_VERBOSE, "Stream finished, looping.\n"); - continue; + movie->loop_count -= movie->loop_count > 1; + av_log(ctx, AV_LOG_VERBOSE, "Stream finished, looping.\n"); + ff_filter_set_ready(ctx, 100); + for (int i = 0; i < ctx->nb_outputs; i++) + movie->st[i].eof = 0; + movie->eof = 0; + return 0; + } else { + for (int i = 0; i < ctx->nb_outputs; i++) { + if (movie->st[i].eof) { + ff_outlink_set_status(ctx->outputs[i], AVERROR_EOF, movie->st[i].last_pts); + nb_eofs++; + } } - return AVERROR_EOF; } + + if (nb_eofs < ctx->nb_outputs) + ff_filter_set_ready(ctx, 100); + return 0; } + + return FFERROR_NOT_READY; } static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, @@ -649,6 +681,7 @@ const AVFilter ff_avsrc_movie = { .priv_size = sizeof(MovieContext), .priv_class = &movie_class, .init = movie_common_init, + .activate = activate, .uninit = movie_uninit, FILTER_QUERY_FUNC(movie_query_formats), @@ -668,6 +701,7 @@ const AVFilter ff_avsrc_amovie = { .priv_class = &movie_class, .priv_size = sizeof(MovieContext), .init = movie_common_init, + .activate = activate, .uninit = movie_uninit, FILTER_QUERY_FUNC(movie_query_formats), diff --git a/libavfilter/stack_internal.c b/libavfilter/stack_internal.c index 0697ebdd12c..1ee20d66cf2 100644 --- a/libavfilter/stack_internal.c +++ b/libavfilter/stack_internal.c @@ -333,7 +333,7 @@ static const AVFilterPad stack_outputs[] = { { NULL } \ } -#define DEFINE_STACK_FILTER(category, api, capi) \ +#define DEFINE_STACK_FILTER(category, api, capi, filter_flags) \ static const AVClass category##_##api##_class = { \ .class_name = #category "_" #api, \ .item_name = av_default_item_name, \ @@ -351,5 +351,5 @@ static const AVFilterPad stack_outputs[] = { FILTER_QUERY_FUNC(api##_stack_query_formats), \ FILTER_OUTPUTS(stack_outputs), \ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, \ - .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, \ + .flags = AVFILTER_FLAG_DYNAMIC_INPUTS | filter_flags, \ } diff --git a/libavfilter/tests/dnn-layer-avgpool.c b/libavfilter/tests/dnn-layer-avgpool.c deleted file mode 100644 index 4a925ea22af..00000000000 --- a/libavfilter/tests/dnn-layer-avgpool.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 -#include "libavfilter/dnn/dnn_backend_native_layer_avgpool.h" - -#define EPSON 0.00001 - -static int test_with_same(void) -{ - // the input data and expected data are generated with below python code. - /* - import tensorflow as tf - import numpy as np - - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.layers.average_pooling2d(x, pool_size=[2,2], strides=[1,1], padding='VALID') - data = np.random.rand(1, 5, 6, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - - output = sess.run(y, feed_dict={x: data}) - - print("input:") - print(data.shape) - print(list(data.flatten())) - - print("output:") - print(output.shape) - print(list(output.flatten())) - */ - - AvgPoolParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*5*6*3] = { - 0.7461309859908424, 0.7567538372797069, 0.07662743569678687, 0.8882112610336333, 0.9720443314026668, 0.3337200343220823, 0.4421032129780248, - 0.14940809044964876, 0.6773177061961277, 0.9778844630669781, 0.6522650522626998, 0.0317651530878591, 0.31259897552911364, 0.6235936821891896, - 0.40016094349542775, 0.4599222930032276, 0.7893807222960093, 0.8475986363538283, 0.5058802717647394, 0.7827005363222633, 0.3032188123727916, - 0.8983728631302361, 0.20622408444965523, 0.22966072303869878, 0.09535751273161308, 0.8760709100995375, 0.9982324154558745, 0.7904595468621013, - 0.13883671508879347, 0.9332751439533138, 0.0010861680752152214, 0.3607210449251048, 0.6600652759586171, 0.7629572058138805, 0.29441975810476106, - 0.2683471432889405, 0.22574580829831536, 0.8893251976212904, 0.3907737043801005, 0.6421829842863968, 0.6670373870457297, 0.9383850793160277, - 0.4120458907436003, 0.3589847212711481, 0.48047736550128983, 0.6428192648418949, 0.0313661686292348, 0.429357100401472, 0.5123413386514056, - 0.8492446404097114, 0.9045286128486804, 0.8123708563814285, 0.3943245008451698, 0.9576713003177785, 0.5985610965938726, 0.9350833279543561, - 0.8010079897491659, 0.45882114217642866, 0.35275037908941487, 0.4555844661432271, 0.12352455940255314, 0.37801756635035544, 0.2824056214573083, - 0.6229462823245029, 0.7235305681391472, 0.5408259266122064, 0.12142224381781208, 0.34431198802873686, 0.7112823816321276, 0.6307144385115417, - 0.8136734589018082, 0.842095618140585, 0.8602767724004784, 0.6649236853766185, 0.5184782829419623, 0.9119607270982825, 0.3084111974561645, - 0.39460705638161364, 0.17710447526170836, 0.1715485945814199, 0.17277563576521882, 0.40188232428735704, 0.22847985411491878, 0.4135361701550696, - 0.24621846601980057, 0.6576588108454774, 0.6063336087333997, 0.6452342242996931, 0.7071689702737508, 0.1973416063225648 - }; - float expected_output[] = { - 0.75964886, 0.6794307, 0.23580676, 0.5810112, 0.5509369, 0.55973274, 0.5764512, 0.45414522, 0.6601476, 0.52050734, 0.44385415, - 0.50631666, 0.38414115, 0.5170288, 0.544043, 0.61143976, 0.5419003, 0.5579729, 0.5680455, 0.6363218, 0.4655096, 0.51198983, - 0.5270792, 0.66168886, 0.48517057, 0.3513146, 0.7103355, 0.48667657, 0.34504217, 0.7318065, 0.5221889, 0.4746775, 0.69765306, - 0.78766406, 0.34437215, 0.6130092, 0.48132777, 0.7110491, 0.6464378, 0.40914366, 0.4391975, 0.5392131, 0.45033398, 0.37297475, - 0.43326652, 0.4748823, 0.48711336, 0.64649844, 0.51921225, 0.60038865, 0.8538945, 0.7215426, 0.60399896, 0.89988345, 0.707405, - 0.5652921, 0.54241943, 0.41785273, 0.30268195, 0.3263432, 0.3313644, 0.37539417, 0.35238582, 0.34811732, 0.48849532, 0.56799453, - 0.41089734, 0.63070333, 0.5892633, 0.6379743, 0.7604212, 0.5197186, 0.88611877, 0.48666745, 0.45654267, 0.5445326, 0.2399799, - 0.28369135, 0.28949338, 0.20001422, 0.2931559, 0.3240504, 0.44306934, 0.5099349, 0.44572634, 0.68241394, 0.40183762, 0.6452342, - 0.707169, 0.1973416 - }; - float *output; - - params.strides = 1; - params.kernel_size = 2; - params.padding_method = SAME; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 5; - operands[0].dims[2] = 6; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_avg_pool(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); ++i) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -static int test_with_valid(void) -{ - // the input data and expected data are generated with below python code. - /* - import tensorflow as tf - import numpy as np - - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.layers.average_pooling2d(x, pool_size=[2,2], strides=[1,1], padding='VALID') - data = np.random.rand(1, 5, 6, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - - output = sess.run(y, feed_dict={x: data}) - - print("input:") - print(data.shape) - print(list(data.flatten())) - - print("output:") - print(output.shape) - print(list(output.flatten())) - */ - - AvgPoolParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*5*6*3] = { - 0.5046741692941682, 0.9273653202485155, 0.8193878359859937, 0.1904059431360905, 0.8664919633253656, 0.7484625128286059, 0.984534184632278, - 0.31900804890072254, 0.3259426099940872, 0.05388974903570376, 0.7356610151331133, 0.46710858713311965, 0.718553768817036, 0.062478421853278676, - 0.7813224786584609, 0.4826837517658389, 0.9748095400220147, 0.8078547703898341, 0.11976750668368585, 0.8713586777195065, 0.41447321551284355, - 0.9818788239089807, 0.4335715767584073, 0.4059793452147419, 0.3677205907204525, 0.47919995923571, 0.8341395256258882, 0.7059726374074609, - 0.5478504551919791, 0.8622900484790175, 0.8343709722511167, 0.05089827275068537, 0.6465283980840416, 0.544539116066677, 0.39812057257884337, - 0.9578115576866337, 0.25012888117580145, 0.579333516024662, 0.5556732133051457, 0.6119862111181243, 0.0018736758772316398, 0.9795490254040474, - 0.4488085008883018, 0.28947489777011737, 0.4834108668633247, 0.9280490084385024, 0.9895821458049648, 0.31777618554697606, 0.42679693258977847, - 0.74447844466923, 0.9752225305081498, 0.17564130841849335, 0.22382692067314292, 0.009602884447469373, 0.5144884415025782, 0.031622570708844555, - 0.8277532752502512, 0.4111593210409763, 0.5272084646575664, 0.28856508082905297, 0.11317726946036655, 0.7203328275540273, 0.8310055019972384, - 0.8535951508685228, 0.40230347305233227, 0.2819703265132867, 0.6243143957791139, 0.7512463693822311, 0.7523056340495644, 0.8838077258040928, - 0.5472240664033092, 0.2550538284454935, 0.5560317774456567, 0.8966847087518931, 0.6728358284165321, 0.30361297147530875, 0.464343925441822, - 0.34507695659461224, 0.6333175615390685, 0.26661369038523497, 0.9926748632253231, 0.9994267301382666, 0.8684917986974414, 0.3598754806113009, - 0.49550268625464666, 0.03652458679973214, 0.13469081713137177, 0.4579424049273835, 0.48641107969110353, 0.9670250266945365 - }; - float expected_output[1*4*5*3] = { - 0.44918162, 0.7746969, 0.5970757, 0.63113487, 0.5245679, 0.578631, 0.52802926, 0.52042985, 0.6223702, 0.57819676, 0.34922206, - 0.6893124, 0.64503694, 0.37157673, 0.7983793, 0.49094033, 0.47153437, 0.5889187, 0.6025985, 0.30103004, 0.6757697, 0.6126377, - 0.5765268, 0.62440413, 0.7237974, 0.5832023, 0.7004543, 0.49533707, 0.35433105, 0.6472913, 0.44694072, 0.28500956, 0.6628852, - 0.39628282, 0.38472247, 0.6456326, 0.58590746, 0.60042334, 0.47854072, 0.7081889, 0.7219026, 0.5818187, 0.5276401, 0.56669396, - 0.49804622, 0.4463231, 0.4799649, 0.5335578, 0.36531678, 0.4946247, 0.6143306, 0.6498792, 0.5644355, 0.6163815, 0.7432098, - 0.5146416, 0.38221055, 0.6153918, 0.45535153, 0.5272688 - }; - float *output; - - params.strides = 1; - params.kernel_size = 2; - params.padding_method = VALID; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 5; - operands[0].dims[2] = 6; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_avg_pool(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); ++i) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -int main(int argc, char **argv) -{ - if (test_with_same()) - return 1; - if (test_with_valid()) - return 1; - - return 0; -} diff --git a/libavfilter/tests/dnn-layer-conv2d.c b/libavfilter/tests/dnn-layer-conv2d.c deleted file mode 100644 index 5ee60eeaf0d..00000000000 --- a/libavfilter/tests/dnn-layer-conv2d.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native_layer_conv2d.h" - -#define EPSON 0.00001 - -static int test_with_same_dilate(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.layers.conv2d(x, 2, 3, activation=tf.nn.tanh, padding='same', dilation_rate=(2, 2), bias_initializer=tf.keras.initializers.he_normal()) - data = np.random.rand(1, 5, 6, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - - weights = dict([(var.name, sess.run(var)) for var in tf.trainable_variables()]) - kernel = weights['conv2d/kernel:0'] - kernel = np.transpose(kernel, [3, 0, 1, 2]) - print("kernel:") - print(kernel.shape) - print(list(kernel.flatten())) - - bias = weights['conv2d/bias:0'] - print("bias:") - print(bias.shape) - print(list(bias.flatten())) - - output = sess.run(y, feed_dict={x: data}) - - print("input:") - print(data.shape) - print(list(data.flatten())) - - print("output:") - print(output.shape) - print(list(output.flatten())) - */ - - ConvolutionalParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*5*6*3] = { - 0.7012556460308194, 0.4233847954643357, 0.19515900664313612, 0.16343083004926495, 0.5758261611052848, 0.9510767434014871, 0.11014085055947687, - 0.906327053637727, 0.8136794715542507, 0.45371764543639526, 0.5768443343523952, 0.19543668786046986, 0.15648326047898609, 0.2099500241141279, - 0.17658777090552413, 0.059335724777169196, 0.1729991838469117, 0.8150514704819208, 0.4435535466703049, 0.3752188477566878, 0.749936650421431, - 0.6823494635284907, 0.10776389679424747, 0.34247481674596836, 0.5147867256244629, 0.9063709728129032, 0.12423605800856818, 0.6064872945412728, - 0.5891681538551459, 0.9865836236466314, 0.9002163879294677, 0.003968273184274618, 0.8628374809643967, 0.1327176268279583, 0.8449799925703798, - 0.1937671869354366, 0.41524410152707425, 0.02038786604756837, 0.49792466069597496, 0.8881874553848784, 0.9683921035597336, 0.4122972568010813, - 0.843553550993252, 0.9588482762501964, 0.5190350762645546, 0.4283584264145317, 0.09781496073714646, 0.9501058833776156, 0.8665541760152776, - 0.31669272550095806, 0.07133074675453632, 0.606438007334886, 0.7007157020538224, 0.4827996264130444, 0.5167615606392761, 0.6385043039312651, - 0.23069664707810555, 0.058233497329354456, 0.06323892961591071, 0.24816458893245974, 0.8646369065257812, 0.24742185893094837, 0.09991225948167437, - 0.625700606979606, 0.7678541502111257, 0.6215834594679912, 0.5623003956582483, 0.07389123942681242, 0.7659100715711249, 0.486061471642225, - 0.9947455699829012, 0.9094911797643259, 0.7644355876253265, 0.05384315321492239, 0.13565394382783613, 0.9810628204953316, 0.007386389078887889, - 0.226182754156241, 0.2609021390764772, 0.24182802076928933, 0.13264782451941648, 0.2035816485767682, 0.005504188177612557, 0.7014619934040155, - 0.956215988391991, 0.5670398541013633, 0.9809764721750784, 0.6886338100487461, 0.5758152317218274, 0.7137823176776179 - }; - float expected_output[1*5*6*2] = { - -0.9480655, -0.7169147, -0.9404794, -0.5567385, -0.8991124, -0.8306558, -0.94487447, -0.8932543, -0.88238764, -0.7301602, - -0.8974813, -0.7026703, -0.8858988, -0.53203243, -0.92881465, -0.5648504, -0.8871471, -0.7000097, -0.91754407, -0.79684794, - -0.760465, -0.117928326, -0.88302773, -0.8975289, -0.70615053, 0.19231977, -0.8318776, -0.386184, -0.80698484, -0.8556624, - -0.7336671, -0.6168619, -0.7658234, -0.63449603, -0.73314047, -0.87502456, -0.58158904, -0.4184259, -0.52618927, -0.13613208, - -0.5093187, -0.21027721, -0.39455596, -0.44507834, -0.22269244, -0.73400885, -0.77655095, -0.74408925, -0.57313335, -0.15333457, - -0.74620694, -0.34858236, -0.42586932, -0.5240488, 0.1634339, -0.2447881, -0.57927346, -0.62732303, -0.82287043, -0.8474058 - }; - float *output; - float kernel[2*3*3*3] = { - 0.26025516, 0.16536498, -0.24351254, 0.33892477, -0.34005195, 0.35202783, 0.34056443, 0.01422739, 0.13799345, 0.29489166, - 0.2781723, 0.178585, 0.22122234, 0.044115514, 0.13134438, 0.31705368, 0.22527462, -0.021323413, 0.115134746, -0.18216397, - -0.21197563, -0.027848959, -0.01704529, -0.12401503, -0.23415318, -0.12661739, -0.35338148, 0.20049328, -0.076153606, - -0.23642601, -0.3125769, -0.025851756, -0.30006272, 0.050762743, 0.32003498, 0.3052225, -0.0017385483, 0.25337684, -0.25664508, - 0.27846587, -0.3112659, 0.2066065, 0.31499845, 0.113178134, 0.09449363, -0.11828774, -0.12671001, -0.36259216, 0.2710235, - -0.19676702, 0.023612618, -0.2596915, -0.34949252, -0.108270735 - }; - float bias[2] = { -1.6574852, -0.72915393 }; - - NativeContext ctx; - ctx.class = NULL; - ctx.options.conv2d_threads = 1; - - params.activation = TANH; - params.has_bias = 1; - params.biases = bias; - params.dilation = 2; - params.input_num = 3; - params.kernel = kernel; - params.kernel_size = 3; - params.output_num = 2; - params.padding_method = SAME; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 5; - operands[0].dims[2] = 6; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_conv2d(operands, input_indexes, 1, ¶ms, &ctx); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -static int test_with_valid(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.layers.conv2d(x, 2, 3, activation=tf.nn.tanh, padding='valid', bias_initializer=tf.keras.initializers.he_normal()) - data = np.random.rand(1, 5, 6, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - - weights = dict([(var.name, sess.run(var)) for var in tf.trainable_variables()]) - kernel = weights['conv2d/kernel:0'] - kernel = np.transpose(kernel, [3, 0, 1, 2]) - print("kernel:") - print(kernel.shape) - print(list(kernel.flatten())) - - bias = weights['conv2d/bias:0'] - print("bias:") - print(bias.shape) - print(list(bias.flatten())) - - output = sess.run(y, feed_dict={x: data}) - - print("input:") - print(data.shape) - print(list(data.flatten())) - - print("output:") - print(output.shape) - print(list(output.flatten())) - */ - - ConvolutionalParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*5*6*3] = { - 0.26126657468269665, 0.42762216215337556, 0.7466274030131497, 0.802550266787863, 0.3709323443076644, 0.5919817068197668, 0.49274512279324967, - 0.7170132295090351, 0.0911793215410649, 0.5134213878288361, 0.670132600785118, 0.49417034512633484, 0.03887389460089885, 0.436785102836845, - 0.1490231658611978, 0.6413606121498127, 0.8595987991375995, 0.9132593077586231, 0.7075959004873255, 0.17754995944845464, 0.5212507214937141, - 0.35379732738215475, 0.25205107358505296, 0.3928792840544273, 0.09485294189485782, 0.8685115437448666, 0.6489046799288605, 0.509253797582924, - 0.8993255536791972, 0.18740056466602373, 0.34237617336313986, 0.3871438962989183, 0.1488532571774911, 0.5187002331293636, 0.8137098818752955, - 0.521761863717401, 0.4622312310118274, 0.29038411334638825, 0.16194915718170566, 0.5175999923925211, 0.8852230040101133, 0.0218263385047206, - 0.08482355352852367, 0.3463638568376264, 0.28627127120619733, 0.9553293378948409, 0.4803391055970835, 0.841635695030805, 0.3556828280031952, - 0.06778527221541808, 0.28193560357091596, 0.8399957619031576, 0.03305536359456385, 0.6625039162109645, 0.9300552020023897, 0.8551529138204146, - 0.6133216915522418, 0.222427800857393, 0.1315422686800336, 0.6189144989185527, 0.5346184916866876, 0.8348888624532548, 0.6544834567840291, - 0.2844062293389934, 0.28780026600883324, 0.5372272015684924, 0.6250226011503823, 0.28119106062279453, 0.49655812908420094, 0.6451488959145951, - 0.7362580606834843, 0.44815578616664087, 0.6454760235835586, 0.6794062414265861, 0.045378883014935756, 0.9008388543865096, 0.7949752851269782, - 0.4179928876222264, 0.28733419007048644, 0.996902319501908, 0.5690851338677467, 0.9511814013279738, 0.025323788678181636, 0.5594359732604794, - 0.1213732595086251, 0.7172624313368294, 0.6759328959074691, 0.07252138454885071, 0.17557735158403442, 0.5988895455048769 - }; - float expected_output[1*3*4*2] = { - -0.556947, -0.42143887, -0.092070885, 0.27404794, -0.41886684, 0.0862887, -0.25001016, -0.342721, 0.020730592, 0.04016919, -0.69839877, - -0.06136704, 0.14186388, -0.11655602, -0.23489095, -0.3845829, -0.19017771, 0.1595885, -0.18308741, -0.3071209, -0.5848686, -0.22509028, - -0.6023201, -0.14448485 - }; - float *output; - float kernel[2*3*3*3] = { - -0.25291282, 0.22402048, 0.028642118, -0.14615723, -0.27362752, -0.34801802, -0.2759148, 0.19594926, -0.25029412, 0.34606284, 0.10376671, - -0.1015394, 0.23616093, 0.2134214, 0.35285157, 0.05893758, 0.0024731457, -0.17143056, 0.35758412, 0.2186206, -0.28384736, -0.21206513, - -0.20871592, 0.27070445, 0.25878823, 0.11136332, -0.33737376, 0.08353335, -0.34290665, 0.041805506, -0.09738535, 0.3284936, -0.16838405, - -0.032494456, -0.29193437, 0.033259362, -0.09272635, -0.2802651, -0.28648436, 0.3542878, 0.2432127, -0.24551713, 0.27813476, 0.21024024, - -0.013690501, -0.1350077, -0.07826337, -0.34563828, 0.3220685, -0.07571727, 0.19420576, 0.20783454, 0.18738335, 0.16672492 - }; - float bias[2] = { -0.4773722, -0.19620377 }; - - NativeContext ctx; - ctx.class = NULL; - ctx.options.conv2d_threads = 1; - - params.activation = TANH; - params.has_bias = 1; - params.biases = bias; - params.dilation = 1; - params.input_num = 3; - params.kernel = kernel; - params.kernel_size = 3; - params.output_num = 2; - params.padding_method = VALID; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 5; - operands[0].dims[2] = 6; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_conv2d(operands, input_indexes, 1, ¶ms, &ctx); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -int main(int argc, char **argv) -{ - if (test_with_valid()) - return 1; - if (test_with_same_dilate()) - return 1; - - return 0; -} diff --git a/libavfilter/tests/dnn-layer-dense.c b/libavfilter/tests/dnn-layer-dense.c deleted file mode 100644 index 696f7505e51..00000000000 --- a/libavfilter/tests/dnn-layer-dense.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native_layer_dense.h" - -#define EPSON 0.00001 - -static int test(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.layers.dense(input_x, 3, activation=tf.nn.sigmoid, bias_initializer=tf.keras.initializers.he_normal()) - data = np.random.rand(1, 5, 6, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - - weights = dict([(var.name, sess.run(var)) for var in tf.trainable_variables()]) - kernel = weights['dense/kernel:0'] - kernel = np.transpose(kernel, [1, 0]) - print("kernel:") - print(kernel.shape) - print(list(kernel.flatten())) - - bias = weights['dense/bias:0'] - print("bias:") - print(bias.shape) - print(list(bias.flatten())) - - output = sess.run(y, feed_dict={x: data}) - - print("input:") - print(data.shape) - print(list(data.flatten())) - - print("output:") - print(output.shape) - print(list(output.flatten())) - */ - - DenseParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*5*6*3] = { - 0.5552418686576308, 0.20653189262022464, 0.31115120939398877, 0.5897014433221428, 0.37340078861060655, 0.6470921693941893, 0.8039950367872679, 0.8762700891949274, - 0.6556655583829558, 0.5911096107039339, 0.18640250865290997, 0.2803248779238966, 0.31586613136402053, 0.9447300740056483, 0.9443980824873418, 0.8158851991115941, - 0.5631010340387631, 0.9407402251929046, 0.6485434876551682, 0.5631376966470001, 0.17581924875609634, 0.7033802439103178, 0.04802402495561675, 0.9183681450194972, - 0.46059317944364, 0.07964160481596883, 0.871787076270302, 0.973743142324361, 0.15923146943258415, 0.8212946080584571, 0.5415954459227064, 0.9552813822803975, - 0.4908552668172057, 0.33723691635292274, 0.46588057864910026, 0.8994239961321776, 0.09845220457674186, 0.1713400292123486, 0.39570294912818826, 0.08018956486392803, - 0.5290478278169032, 0.7141906125920976, 0.0320878067840098, 0.6412406575332606, 0.0075712007102423096, 0.7150828462386156, 0.1311989216968138, 0.4706847944253756, - 0.5447610794883336, 0.3430923933318001, 0.536082357943209, 0.4371629342483694, 0.40227962985019927, 0.3553806249465469, 0.031806622424259245, 0.7053916426174, - 0.3261570237309813, 0.419500213292063, 0.3155691223480851, 0.05664028113178088, 0.3636491555914486, 0.8502419746667123, 0.9836596530684955, 0.1628681802975801, - 0.09410832912479894, 0.28407218939480294, 0.7983417928813697, 0.24132158596506748, 0.8154729498062224, 0.29173768373895637, 0.13407102008052096, 0.18705786678800385, - 0.7167943621295573, 0.09222004247174376, 0.2319220738766018, 0.17708964382285064, 0.1391440370249517, 0.3254088083499256, 0.4013916894718289, 0.4819742663322323, - 0.15080103744648077, 0.9302407847555013, 0.9397597961319524, 0.5719200825550793, 0.9538938024682824, 0.9583882089203861, 0.5168861091262276, 0.1926396841842669, - 0.6781176744337578, 0.719366447288566 - }; - float expected_output[1*5*6*3] = { - -0.3921688, -0.9243112, -0.29659146, -0.64000785, -0.9466343, -0.62125254, -0.71759033, -0.9171336, -0.735589, -0.34365994, - -0.92100817, -0.23903961, -0.8962277, -0.9521279, -0.90962386, -0.7488303, -0.9563761, -0.7701762, -0.40800542, -0.87684774, - -0.3339763, -0.6354543, -0.97068924, -0.6246325, -0.6992075, -0.9706726, -0.6818918, -0.51864433, -0.9592881, -0.51187396, - -0.7423632, -0.89911884, -0.7457824, -0.82009757, -0.96402895, -0.8235518, -0.61980766, -0.94494647, -0.5410502, -0.8281218, - -0.95508635, -0.8201453, -0.5937325, -0.8679507, -0.500767, -0.39430764, -0.93967676, -0.32183182, -0.58913624, -0.939717, - -0.55179894, -0.55004454, -0.9214453, -0.4889004, -0.75294703, -0.9118363, -0.7200309, -0.3248641, -0.8878874, -0.18977344, - -0.8873837, -0.9571257, -0.90145934, -0.50521654, -0.93739635, -0.39051685, -0.61143184, -0.9591179, -0.605999, -0.40008977, - -0.92219675, -0.26732883, -0.19607787, -0.9172511, -0.07068595, -0.5409857, -0.9387041, -0.44181606, -0.4705004, -0.8899935, - -0.37997037, -0.66105115, -0.89754754, -0.68141997, -0.6324047, -0.886776, -0.65066385, -0.8334821, -0.94801456, -0.83297 - }; - float *output; - float kernel[3*3] = { - 0.56611896, -0.5144603, -0.82600045, 0.19219112, 0.3835776, -0.7475352, 0.5209291, -0.6301091, -0.99442935}; - float bias[3] = {-0.3654299, -1.5711838, -0.15546428}; - - params.activation = TANH; - params.has_bias = 1; - params.biases = bias; - params.input_num = 3; - params.kernel = kernel; - params.output_num = 3; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 5; - operands[0].dims[2] = 6; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_dense(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -int main(int argc, char **argv) -{ - if (test()) - return 1; - - return 0; -} diff --git a/libavfilter/tests/dnn-layer-depth2space.c b/libavfilter/tests/dnn-layer-depth2space.c deleted file mode 100644 index 958247e6753..00000000000 --- a/libavfilter/tests/dnn-layer-depth2space.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native.h" -#include "libavfilter/dnn/dnn_backend_native_layer_depth2space.h" - -#define EPSON 0.00001 - -static int test(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[1, None, None, 4]) - y = tf.depth_to_space(x, 2) - data = np.random.rand(1, 5, 3, 4); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - - output = sess.run(y, feed_dict={x: data}) - - print("input:") - print(data.shape) - print(list(data.flatten())) - - print("output:") - print(output.shape) - print(list(output.flatten())) - */ - - DepthToSpaceParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*5*3*4] = { - 0.09771065121566602, 0.6336807372403175, 0.5142416549709786, 0.8027206567330333, 0.2154276025069397, 0.12112878462616772, 0.913936596765778, - 0.38881443647542646, 0.5850447615898835, 0.9311499327398275, 0.3613660929428246, 0.5420722002125493, 0.6002131190230359, 0.44800665702299525, - 0.7271322557896777, 0.3869293511885826, 0.5144404769364138, 0.6910844856987723, 0.6142102742269762, 0.6249991371621018, 0.45663376215836626, - 0.19523477129943423, 0.2483895888532045, 0.64326768256278, 0.5485877602998981, 0.45442067849873546, 0.529374943304256, 0.30439850391811885, - 0.11961343361340993, 0.2909643484561082, 0.9810970344127848, 0.8886928489786549, 0.6112237084436409, 0.8852482695156674, 0.9110868043114374, - 0.21242780027585217, 0.7101536973207572, 0.9709717457443375, 0.2702666770969332, 0.7718295953780221, 0.3957005164588574, 0.24383544252475453, - 0.040143453532367035, 0.26358051835323115, 0.013130251443791319, 0.3016550481482074, 0.03582340459943956, 0.718025513612361, 0.09844204177633753, - 0.04433767496953056, 0.6221895044119757, 0.6190414032940228, 0.8963550834625371, 0.5642449700064629, 0.2482982014723497, 0.17824909294583013, - 0.024401882408643272, 0.21742800875253465, 0.6794724473181843, 0.4814830479242237 - }; - float expected_output[1*10*6*1] = { - 0.097710654, 0.63368076, 0.2154276, 0.12112878, 0.58504474, 0.93114996, 0.51424164, 0.80272067, 0.9139366, 0.38881445, - 0.3613661, 0.5420722, 0.6002131, 0.44800666, 0.5144405, 0.6910845, 0.45663378, 0.19523478, 0.72713226, 0.38692936, - 0.61421025, 0.62499917, 0.24838959, 0.6432677, 0.54858774, 0.4544207, 0.11961343, 0.29096434, 0.6112237, 0.88524824, - 0.52937496, 0.3043985, 0.98109704, 0.88869286, 0.9110868, 0.2124278, 0.7101537, 0.97097176, 0.3957005, 0.24383545, - 0.013130251, 0.30165505, 0.27026668, 0.7718296, 0.040143453, 0.26358053, 0.035823405, 0.7180255, 0.09844204, - 0.044337675, 0.8963551, 0.564245, 0.024401883, 0.21742801, 0.6221895, 0.6190414, 0.2482982, 0.17824909, 0.67947245, 0.48148304 - }; - float *output; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 5; - operands[0].dims[2] = 3; - operands[0].dims[3] = 4; - operands[1].data = NULL; - - input_indexes[0] = 0; - params.block_size = 2; - ff_dnn_execute_layer_depth2space(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -int main(int argc, char **argv) -{ - return test(); -} diff --git a/libavfilter/tests/dnn-layer-mathbinary.c b/libavfilter/tests/dnn-layer-mathbinary.c deleted file mode 100644 index 2e41dc1ae75..00000000000 --- a/libavfilter/tests/dnn-layer-mathbinary.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native_layer_mathbinary.h" -#include "libavutil/avassert.h" - -#define EPSON 0.00005 - -static float get_expected(float f1, float f2, DNNMathBinaryOperation op) -{ - switch (op) - { - case DMBO_SUB: - return f1 - f2; - case DMBO_ADD: - return f1 + f2; - case DMBO_MUL: - return f1 * f2; - case DMBO_REALDIV: - return f1 / f2; - case DMBO_MINIMUM: - return (f1 < f2) ? f1 : f2; - case DMBO_FLOORMOD: - return (float)((int)(f1) % (int)(f2)); - default: - av_assert0(!"not supported yet"); - return 0.f; - } -} - -static int test_broadcast_input0(DNNMathBinaryOperation op) -{ - DnnLayerMathBinaryParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*1*2*3] = { - -3, 2.5, 2, -2.1, 7.8, 100 - }; - float *output; - - params.bin_op = op; - params.input0_broadcast = 1; - params.input1_broadcast = 0; - params.v = 7.28; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 1; - operands[0].dims[2] = 2; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_math_binary(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(input) / sizeof(float); i++) { - float expected_output = get_expected(params.v, input[i], op); - if (fabs(output[i] - expected_output) > EPSON) { - printf("op %d, at index %d, output: %f, expected_output: %f (%s:%d)\n", - op, i, output[i], expected_output, __FILE__, __LINE__); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -static int test_broadcast_input1(DNNMathBinaryOperation op) -{ - DnnLayerMathBinaryParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*1*2*3] = { - -3, 2.5, 2, -2.1, 7.8, 100 - }; - float *output; - - params.bin_op = op; - params.input0_broadcast = 0; - params.input1_broadcast = 1; - params.v = 7.28; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 1; - operands[0].dims[2] = 2; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_math_binary(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(input) / sizeof(float); i++) { - float expected_output = get_expected(input[i], params.v, op); - if (fabs(output[i] - expected_output) > EPSON) { - printf("op %d, at index %d, output: %f, expected_output: %f (%s:%d)\n", - op, i, output[i], expected_output, __FILE__, __LINE__); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -static int test_no_broadcast(DNNMathBinaryOperation op) -{ - DnnLayerMathBinaryParams params; - DnnOperand operands[3]; - int32_t input_indexes[2]; - float input0[1*1*2*3] = { - -3, 2.5, 2, -2.1, 7.8, 100 - }; - float input1[1*1*2*3] = { - -1, 2, 3, -21, 8, 10.0 - }; - float *output; - - params.bin_op = op; - params.input0_broadcast = 0; - params.input1_broadcast = 0; - - operands[0].data = input0; - operands[0].dims[0] = 1; - operands[0].dims[1] = 1; - operands[0].dims[2] = 2; - operands[0].dims[3] = 3; - operands[1].data = input1; - operands[1].dims[0] = 1; - operands[1].dims[1] = 1; - operands[1].dims[2] = 2; - operands[1].dims[3] = 3; - operands[2].data = NULL; - - input_indexes[0] = 0; - input_indexes[1] = 1; - ff_dnn_execute_layer_math_binary(operands, input_indexes, 2, ¶ms, NULL); - - output = operands[2].data; - for (int i = 0; i < sizeof(input0) / sizeof(float); i++) { - float expected_output = get_expected(input0[i], input1[i], op); - if (fabs(output[i] - expected_output) > EPSON) { - printf("op %d, at index %d, output: %f, expected_output: %f (%s:%d)\n", - op, i, output[i], expected_output, __FILE__, __LINE__); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -static int test(DNNMathBinaryOperation op) -{ - if (test_broadcast_input0(op)) - return 1; - - if (test_broadcast_input1(op)) - return 1; - - if (test_no_broadcast(op)) - return 1; - - return 0; -} - -int main(int argc, char **argv) -{ - if (test(DMBO_SUB)) - return 1; - - if (test(DMBO_ADD)) - return 1; - - if (test(DMBO_MUL)) - return 1; - - if (test(DMBO_REALDIV)) - return 1; - - if (test(DMBO_MINIMUM)) - return 1; - - if (test(DMBO_FLOORMOD)) - return 1; - - return 0; -} diff --git a/libavfilter/tests/dnn-layer-mathunary.c b/libavfilter/tests/dnn-layer-mathunary.c deleted file mode 100644 index 0f84c12960b..00000000000 --- a/libavfilter/tests/dnn-layer-mathunary.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2020 - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native_layer_mathunary.h" -#include "libavutil/avassert.h" - -#define EPS 0.00001 - -static float get_expected(float f, DNNMathUnaryOperation op) -{ - switch (op) - { - case DMUO_ABS: - return (f >= 0) ? f : -f; - case DMUO_SIN: - return sin(f); - case DMUO_COS: - return cos(f); - case DMUO_TAN: - return tan(f); - case DMUO_ASIN: - return asin(f); - case DMUO_ACOS: - return acos(f); - case DMUO_ATAN: - return atan(f); - case DMUO_SINH: - return sinh(f); - case DMUO_COSH: - return cosh(f); - case DMUO_TANH: - return tanh(f); - case DMUO_ASINH: - return asinh(f); - case DMUO_ACOSH: - return acosh(f); - case DMUO_ATANH: - return atanh(f); - case DMUO_CEIL: - return ceil(f); - case DMUO_FLOOR: - return floor(f); - case DMUO_ROUND: - return round(f); - case DMUO_EXP: - return exp(f); - default: - av_assert0(!"not supported yet"); - return 0.f; - } -} - -static int test(DNNMathUnaryOperation op) -{ - DnnLayerMathUnaryParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*1*3*3] = { - 0.1, 0.5, 0.75, -3, 2.5, 2, -2.1, 7.8, 100}; - float *output; - - params.un_op = op; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 1; - operands[0].dims[2] = 3; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_math_unary(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(input) / sizeof(float); ++i) { - float expected_output = get_expected(input[i], op); - int output_nan = isnan(output[i]); - int expected_nan = isnan(expected_output); - if ((!output_nan && !expected_nan && fabs(output[i] - expected_output) > EPS) || - (output_nan && !expected_nan) || (!output_nan && expected_nan)) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; -} - -int main(int agrc, char **argv) -{ - if (test(DMUO_ABS)) - return 1; - if (test(DMUO_SIN)) - return 1; - if (test(DMUO_COS)) - return 1; - if (test(DMUO_TAN)) - return 1; - if (test(DMUO_ASIN)) - return 1; - if (test(DMUO_ACOS)) - return 1; - if (test(DMUO_ATAN)) - return 1; - if (test(DMUO_SINH)) - return 1; - if (test(DMUO_COSH)) - return 1; - if (test(DMUO_TANH)) - return 1; - if (test(DMUO_ASINH)) - return 1; - if (test(DMUO_ACOSH)) - return 1; - if (test(DMUO_ATANH)) - return 1; - if (test(DMUO_CEIL)) - return 1; - if (test(DMUO_FLOOR)) - return 1; - if (test(DMUO_ROUND)) - return 1; - if (test(DMUO_EXP)) - return 1; - return 0; -} diff --git a/libavfilter/tests/dnn-layer-maximum.c b/libavfilter/tests/dnn-layer-maximum.c deleted file mode 100644 index bf22f3719fd..00000000000 --- a/libavfilter/tests/dnn-layer-maximum.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native_layer_maximum.h" - -#define EPSON 0.00001 - -static int test(void) -{ - DnnLayerMaximumParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*1*2*3] = { - -3, 2.5, 2, -2.1, 7.8, 100 - }; - float *output; - - params.val.y = 2.3; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 1; - operands[0].dims[2] = 2; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_maximum(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(input) / sizeof(float); i++) { - float expected_output = input[i] > params.val.y ? input[i] : params.val.y; - if (fabs(output[i] - expected_output) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; - -} - -int main(int argc, char **argv) -{ - if (test()) - return 1; - - return 0; -} diff --git a/libavfilter/tests/dnn-layer-pad.c b/libavfilter/tests/dnn-layer-pad.c deleted file mode 100644 index a8443ce3be0..00000000000 --- a/libavfilter/tests/dnn-layer-pad.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2019 Guo Yejun - * - * 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 -#include -#include -#include "libavfilter/dnn/dnn_backend_native_layer_pad.h" - -#define EPSON 0.00001 - -static int test_with_mode_symmetric(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.pad(x, [[0, 0], [2, 3], [3, 2], [0, 0]], 'SYMMETRIC') - data = np.arange(48).reshape(1, 4, 4, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - output = sess.run(y, feed_dict={x: data}) - - print(list(data.flatten())) - print(list(output.flatten())) - print(data.shape) - print(output.shape) - */ - - LayerPadParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*4*4*3] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 - }; - float expected_output[1*9*9*3] = { - 18.0, 19.0, 20.0, 15.0, 16.0, 17.0, 12.0, 13.0, 14.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 21.0, 22.0, 23.0, 18.0, 19.0, 20.0, 6.0, 7.0, 8.0, 3.0, - 4.0, 5.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 9.0, 10.0, 11.0, 6.0, 7.0, 8.0, 6.0, 7.0, 8.0, 3.0, 4.0, 5.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 3.0, - 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 9.0, 10.0, 11.0, 6.0, 7.0, 8.0, 18.0, 19.0, 20.0, 15.0, 16.0, 17.0, 12.0, 13.0, 14.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, - 21.0, 22.0, 23.0, 21.0, 22.0, 23.0, 18.0, 19.0, 20.0, 30.0, 31.0, 32.0, 27.0, 28.0, 29.0, 24.0, 25.0, 26.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0, 35.0, 33.0, - 34.0, 35.0, 30.0, 31.0, 32.0, 42.0, 43.0, 44.0, 39.0, 40.0, 41.0, 36.0, 37.0, 38.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 45.0, 46.0, 47.0, 42.0, 43.0, - 44.0, 42.0, 43.0, 44.0, 39.0, 40.0, 41.0, 36.0, 37.0, 38.0, 36.0, 37.0, 38.0, 39.0, 40.0, 41.0, 42.0, 43.0, 44.0, 45.0, 46.0, 47.0, 45.0, 46.0, 47.0, 42.0, 43.0, 44.0, 30.0, 31.0, 32.0, - 27.0, 28.0, 29.0, 24.0, 25.0, 26.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0, 35.0, 33.0, 34.0, 35.0, 30.0, 31.0, 32.0, 18.0, 19.0, 20.0, 15.0, 16.0, 17.0, 12.0, - 13.0, 14.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 21.0, 22.0, 23.0, 18.0, 19.0, 20.0 - }; - float *output; - - params.mode = LPMP_SYMMETRIC; - params.paddings[0][0] = 0; - params.paddings[0][1] = 0; - params.paddings[1][0] = 2; - params.paddings[1][1] = 3; - params.paddings[2][0] = 3; - params.paddings[2][1] = 2; - params.paddings[3][0] = 0; - params.paddings[3][1] = 0; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 4; - operands[0].dims[2] = 4; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_pad(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; - -} - -static int test_with_mode_reflect(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[3, None, None, 3]) - y = tf.pad(x, [[1, 2], [0, 0], [0, 0], [0, 0]], 'REFLECT') - data = np.arange(36).reshape(3, 2, 2, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - output = sess.run(y, feed_dict={x: data}) - - print(list(data.flatten())) - print(list(output.flatten())) - print(data.shape) - print(output.shape) - */ - - LayerPadParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[3*2*2*3] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35 - }; - float expected_output[6*2*2*3] = { - 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, - 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0, 32.0, 33.0, 34.0, - 35.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0 - }; - float *output; - - params.mode = LPMP_REFLECT; - params.paddings[0][0] = 1; - params.paddings[0][1] = 2; - params.paddings[1][0] = 0; - params.paddings[1][1] = 0; - params.paddings[2][0] = 0; - params.paddings[2][1] = 0; - params.paddings[3][0] = 0; - params.paddings[3][1] = 0; - - operands[0].data = input; - operands[0].dims[0] = 3; - operands[0].dims[1] = 2; - operands[0].dims[2] = 2; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_pad(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; - -} - -static int test_with_mode_constant(void) -{ - // the input data and expected data are generated with below python code. - /* - x = tf.placeholder(tf.float32, shape=[1, None, None, 3]) - y = tf.pad(x, [[0, 0], [1, 0], [0, 0], [1, 2]], 'CONSTANT', constant_values=728) - data = np.arange(12).reshape(1, 2, 2, 3); - - sess=tf.Session() - sess.run(tf.global_variables_initializer()) - output = sess.run(y, feed_dict={x: data}) - - print(list(data.flatten())) - print(list(output.flatten())) - print(data.shape) - print(output.shape) - */ - - LayerPadParams params; - DnnOperand operands[2]; - int32_t input_indexes[1]; - float input[1*2*2*3] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 - }; - float expected_output[1*3*2*6] = { - 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, 728.0, - 728.0, 728.0, 0.0, 1.0, 2.0, 728.0, 728.0, 728.0, 3.0, 4.0, 5.0, 728.0, 728.0, - 728.0, 6.0, 7.0, 8.0, 728.0, 728.0, 728.0, 9.0, 10.0, 11.0, 728.0, 728.0 - }; - float *output; - - params.mode = LPMP_CONSTANT; - params.constant_values = 728; - params.paddings[0][0] = 0; - params.paddings[0][1] = 0; - params.paddings[1][0] = 1; - params.paddings[1][1] = 0; - params.paddings[2][0] = 0; - params.paddings[2][1] = 0; - params.paddings[3][0] = 1; - params.paddings[3][1] = 2; - - operands[0].data = input; - operands[0].dims[0] = 1; - operands[0].dims[1] = 2; - operands[0].dims[2] = 2; - operands[0].dims[3] = 3; - operands[1].data = NULL; - - input_indexes[0] = 0; - ff_dnn_execute_layer_pad(operands, input_indexes, 1, ¶ms, NULL); - - output = operands[1].data; - for (int i = 0; i < sizeof(expected_output) / sizeof(float); i++) { - if (fabs(output[i] - expected_output[i]) > EPSON) { - printf("at index %d, output: %f, expected_output: %f\n", i, output[i], expected_output[i]); - av_freep(&output); - return 1; - } - } - - av_freep(&output); - return 0; - -} - -int main(int argc, char **argv) -{ - if (test_with_mode_symmetric()) - return 1; - - if (test_with_mode_reflect()) - return 1; - - if (test_with_mode_constant()) - return 1; -} diff --git a/libavfilter/tests/filtfmts.c b/libavfilter/tests/filtfmts.c index 909c1e8dc96..194792078dc 100644 --- a/libavfilter/tests/filtfmts.c +++ b/libavfilter/tests/filtfmts.c @@ -25,11 +25,10 @@ #include "libavutil/pixdesc.h" #include "libavutil/samplefmt.h" -#define FF_INTERNAL_FIELDS 1 -#include "libavfilter/framequeue.h" - #include "libavfilter/avfilter.h" +#include "libavfilter/avfilter_internal.h" #include "libavfilter/formats.h" +#include "libavfilter/framequeue.h" #include "libavfilter/internal.h" static void print_formats_internal(AVFilterLink **links, const AVFilterPad *pads, @@ -123,7 +122,7 @@ int main(int argc, char **argv) /* create a link for each of the input pads */ for (i = 0; i < filter_ctx->nb_inputs; i++) { - AVFilterLink *link = av_mallocz(sizeof(AVFilterLink)); + AVFilterLink *link = av_mallocz(sizeof(FilterLinkInternal)); if (!link) { fprintf(stderr, "Unable to allocate memory for filter input link\n"); ret = 1; @@ -133,7 +132,7 @@ int main(int argc, char **argv) filter_ctx->inputs[i] = link; } for (i = 0; i < filter_ctx->nb_outputs; i++) { - AVFilterLink *link = av_mallocz(sizeof(AVFilterLink)); + AVFilterLink *link = av_mallocz(sizeof(FilterLinkInternal)); if (!link) { fprintf(stderr, "Unable to allocate memory for filter output link\n"); ret = 1; diff --git a/libavfilter/tests/formats.c b/libavfilter/tests/formats.c index 2bdceb0d215..ed1de24a4c2 100644 --- a/libavfilter/tests/formats.c +++ b/libavfilter/tests/formats.c @@ -118,9 +118,6 @@ int main(void) "65C", "5.1", "stereo", - "1+1+1+1", - "1c+1c+1c+1c", - "2c+1c", "0x3", }; diff --git a/libavfilter/textutils.c b/libavfilter/textutils.c new file mode 100644 index 00000000000..ef658d04a28 --- /dev/null +++ b/libavfilter/textutils.c @@ -0,0 +1,382 @@ +/* + * 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 + * text expansion utilities + */ + +#include +#include +#include + +#include "textutils.h" +#include "libavutil/avutil.h" +#include "libavutil/error.h" +#include "libavutil/file.h" +#include "libavutil/time.h" + +static int ff_expand_text_function_internal(FFExpandTextContext *expand_text, AVBPrint *bp, + char *name, unsigned argc, char **argv) +{ + void *log_ctx = expand_text->log_ctx; + FFExpandTextFunction *functions = expand_text->functions; + unsigned i; + + for (i = 0; i < expand_text->functions_nb; i++) { + if (strcmp(name, functions[i].name)) + continue; + if (argc < functions[i].argc_min) { + av_log(log_ctx, AV_LOG_ERROR, "%%{%s} requires at least %d arguments\n", + name, functions[i].argc_min); + return AVERROR(EINVAL); + } + if (argc > functions[i].argc_max) { + av_log(log_ctx, AV_LOG_ERROR, "%%{%s} requires at most %d arguments\n", + name, functions[i].argc_max); + return AVERROR(EINVAL); + } + break; + } + if (i >= expand_text->functions_nb) { + av_log(log_ctx, AV_LOG_ERROR, "%%{%s} is not known\n", name); + return AVERROR(EINVAL); + } + + return functions[i].func(log_ctx, bp, name, argc, argv); +} + +/** + * Expand text template pointed to by *rtext. + * + * Expand text template defined in text using the logic defined in a text + * expander object. + * + * This function expects the text to be in the format %{FUNCTION_NAME[:PARAMS]}, + * where PARAMS is a sequence of strings separated by : and represents the function + * arguments to use for the function evaluation. + * + * @param text_expander TextExpander object used to expand the text + * @param bp BPrint object where the expanded text is written to + * @param rtext pointer to pointer to the text to expand, it is updated to point + * to the next part of the template to process + * @return negative value corresponding to an AVERROR error code in case of + * errors, a non-negative value otherwise + */ +static int ff_expand_text_function(FFExpandTextContext *expand_text, AVBPrint *bp, char **rtext) +{ + void *log_ctx = expand_text->log_ctx; + const char *text = *rtext; + char *argv[16] = { NULL }; + unsigned argc = 0, i; + int ret; + + if (*text != '{') { + av_log(log_ctx, AV_LOG_ERROR, "Stray %% near '%s'\n", text); + return AVERROR(EINVAL); + } + text++; + while (1) { + if (!(argv[argc++] = av_get_token(&text, ":}"))) { + ret = AVERROR(ENOMEM); + goto end; + } + if (!*text) { + av_log(log_ctx, AV_LOG_ERROR, "Unterminated %%{} near '%s'\n", *rtext); + ret = AVERROR(EINVAL); + goto end; + } + if (argc == FF_ARRAY_ELEMS(argv)) + av_freep(&argv[--argc]); /* error will be caught later */ + if (*text == '}') + break; + text++; + } + + if ((ret = ff_expand_text_function_internal(expand_text, bp, argv[0], argc - 1, argv + 1)) < 0) + goto end; + ret = 0; + *rtext = (char *)text + 1; + +end: + for (i = 0; i < argc; i++) + av_freep(&argv[i]); + return ret; +} + +int ff_expand_text(FFExpandTextContext *expand_text, char *text, AVBPrint *bp) +{ + int ret; + + av_bprint_clear(bp); + if (!text) + return 0; + + while (*text) { + if (*text == '\\' && text[1]) { + av_bprint_chars(bp, text[1], 1); + text += 2; + } else if (*text == '%') { + text++; + if ((ret = ff_expand_text_function(expand_text, bp, &text)) < 0) + return ret; + } else { + av_bprint_chars(bp, *text, 1); + text++; + } + } + if (!av_bprint_is_complete(bp)) + return AVERROR(ENOMEM); + return 0; +} + +int ff_print_pts(void *log_ctx, AVBPrint *bp, double pts, const char *delta, + const char *fmt, const char *strftime_fmt) +{ + int ret; + + if (delta) { + int64_t delta_i; + if ((ret = av_parse_time(&delta_i, delta, 1)) < 0) { + av_log(log_ctx, AV_LOG_ERROR, "Invalid delta '%s'\n", delta); + return ret; + } + pts += (double)delta_i / AV_TIME_BASE; + } + + if (!strcmp(fmt, "flt")) { + av_bprintf(bp, "%.6f", pts); + } else if (!strcmp(fmt, "hms") || + !strcmp(fmt, "hms24hh")) { + if (isnan(pts)) { + av_bprintf(bp, " ??:??:??.???"); + } else { + int64_t ms = llrint(pts * 1000); + char sign = ' '; + if (ms < 0) { + sign = '-'; + ms = -ms; + } + if (!strcmp(fmt, "hms24hh")) { + /* wrap around 24 hours */ + ms %= 24 * 60 * 60 * 1000; + } + av_bprintf(bp, "%c%02d:%02d:%02d.%03d", sign, + (int)(ms / (60 * 60 * 1000)), + (int)(ms / (60 * 1000)) % 60, + (int)(ms / 1000) % 60, + (int)(ms % 1000)); + } + } else if (!strcmp(fmt, "localtime") || + !strcmp(fmt, "gmtime")) { + struct tm tm; + time_t ms = (time_t)pts; + if (!strcmp(fmt, "localtime")) + localtime_r(&ms, &tm); + else + gmtime_r(&ms, &tm); + av_bprint_strftime(bp, av_x_if_null(strftime_fmt, "%Y-%m-%d %H:%M:%S"), &tm); + } else { + av_log(log_ctx, AV_LOG_ERROR, "Invalid format '%s'\n", fmt); + return AVERROR(EINVAL); + } + return 0; +} + +int ff_print_time(void *log_ctx, AVBPrint *bp, + const char *strftime_fmt, char localtime) +{ + const char *fmt = av_x_if_null(strftime_fmt, "%Y-%m-%d %H:%M:%S"); + const char *fmt_begin = fmt; + int64_t unow; + time_t now; + struct tm tm; + const char *begin; + const char *tmp; + int len; + int div; + AVBPrint fmt_bp; + + av_bprint_init(&fmt_bp, 0, AV_BPRINT_SIZE_UNLIMITED); + + unow = av_gettime(); + now = unow / 1000000; + if (localtime) + localtime_r(&now, &tm); + else + tm = *gmtime_r(&now, &tm); + + // manually parse format for %N (fractional seconds) + begin = fmt; + while ((begin = strchr(begin, '%'))) { + tmp = begin + 1; + len = 0; + + // skip escaped "%%" + if (*tmp == '%') { + begin = tmp + 1; + continue; + } + + // count digits between % and possible N + while (*tmp != '\0' && av_isdigit((int)*tmp)) { + len++; + tmp++; + } + + // N encountered, insert time + if (*tmp == 'N') { + int num_digits = 3; // default show millisecond [1,6] + + // if digit given, expect [1,6], warn & clamp otherwise + if (len == 1) { + num_digits = av_clip(*(begin + 1) - '0', 1, 6); + } else if (len > 1) { + av_log(log_ctx, AV_LOG_WARNING, "Invalid number of decimals for %%N, using default of %i\n", num_digits); + } + + len += 2; // add % and N to get length of string part + + div = pow(10, 6 - num_digits); + + av_bprintf(&fmt_bp, "%.*s%0*d", (int)(begin - fmt_begin), fmt_begin, num_digits, (int)(unow % 1000000) / div); + + begin += len; + fmt_begin = begin; + + continue; + } + + begin = tmp; + } + + av_bprintf(&fmt_bp, "%s", fmt_begin); + if (!av_bprint_is_complete(&fmt_bp)) { + av_log(log_ctx, AV_LOG_WARNING, "Format string truncated at %u/%u.", fmt_bp.size, fmt_bp.len); + } + + av_bprint_strftime(bp, fmt_bp.str, &tm); + + av_bprint_finalize(&fmt_bp, NULL); + + return 0; +} + +int ff_print_eval_expr(void *log_ctx, AVBPrint *bp, + const char *expr, + const char * const *fun_names, const ff_eval_func2 *fun_values, + const char * const *var_names, const double *var_values, + void *eval_ctx) +{ + double res; + int ret; + + ret = av_expr_parse_and_eval(&res, expr, var_names, var_values, + NULL, NULL, fun_names, fun_values, + eval_ctx, 0, log_ctx); + if (ret < 0) + av_log(log_ctx, AV_LOG_ERROR, + "Text expansion expression '%s' is not valid\n", + expr); + else + av_bprintf(bp, "%f", res); + + return ret; +} + +int ff_print_formatted_eval_expr(void *log_ctx, AVBPrint *bp, + const char *expr, + const char * const *fun_names, const ff_eval_func2 *fun_values, + const char * const *var_names, const double *var_values, + void *eval_ctx, + const char format, int positions) +{ + double res; + int intval; + int ret; + char fmt_str[30] = "%"; + + ret = av_expr_parse_and_eval(&res, expr, var_names, var_values, + NULL, NULL, fun_names, fun_values, + eval_ctx, 0, log_ctx); + if (ret < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "Text expansion expression '%s' is not valid\n", + expr); + return ret; + } + + if (!strchr("xXdu", format)) { + av_log(log_ctx, AV_LOG_ERROR, "Invalid format '%c' specified," + " allowed values: 'x', 'X', 'd', 'u'\n", format); + return AVERROR(EINVAL); + } + + feclearexcept(FE_ALL_EXCEPT); + intval = res; +#if defined(FE_INVALID) && defined(FE_OVERFLOW) && defined(FE_UNDERFLOW) + if ((ret = fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW))) { + av_log(log_ctx, AV_LOG_ERROR, "Conversion of floating-point result to int failed. Control register: 0x%08x. Conversion result: %d\n", ret, intval); + return AVERROR(EINVAL); + } +#endif + + if (positions >= 0) + av_strlcatf(fmt_str, sizeof(fmt_str), "0%u", positions); + av_strlcatf(fmt_str, sizeof(fmt_str), "%c", format); + + av_log(log_ctx, AV_LOG_DEBUG, "Formatting value %f (expr '%s') with spec '%s'\n", + res, expr, fmt_str); + + av_bprintf(bp, fmt_str, intval); + + return 0; +} + + +int ff_load_textfile(void *log_ctx, const char *textfile, + unsigned char **text, size_t *text_size) +{ + int err; + uint8_t *textbuf; + uint8_t *tmp; + size_t textbuf_size; + + if ((err = av_file_map(textfile, &textbuf, &textbuf_size, 0, log_ctx)) < 0) { + av_log(log_ctx, AV_LOG_ERROR, + "The text file '%s' could not be read or is empty\n", + textfile); + return err; + } + + if (textbuf_size > 0 && ff_is_newline(textbuf[textbuf_size - 1])) + textbuf_size--; + if (textbuf_size > SIZE_MAX - 1 || !(tmp = av_realloc(*text, textbuf_size + 1))) { + av_file_unmap(textbuf, textbuf_size); + return AVERROR(ENOMEM); + } + *text = tmp; + memcpy(*text, textbuf, textbuf_size); + (*text)[textbuf_size] = 0; + if (text_size) + *text_size = textbuf_size; + av_file_unmap(textbuf, textbuf_size); + + return 0; +} + diff --git a/libavfilter/textutils.h b/libavfilter/textutils.h new file mode 100644 index 00000000000..7fa856c6811 --- /dev/null +++ b/libavfilter/textutils.h @@ -0,0 +1,229 @@ +/* + * 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 + * text utilities + */ + +#ifndef AVFILTER_TEXTUTILS_H +#define AVFILTER_TEXTUTILS_H + +#include "libavutil/bprint.h" +#include "libavutil/eval.h" +#include "libavutil/log.h" +#include "libavutil/parseutils.h" + +/** + * Function used to expand a template sequence in the format + * %{FUNCTION_NAME[:PARAMS]}, defined in the TextExpander object. + */ +typedef struct FFExpandTextFunction { + /** + * name of the function + */ + const char *name; + + /** + * minimum and maximum number of arguments accepted by the + * function in the PARAMS + */ + unsigned argc_min, argc_max; + + /** + * actual function used to perform the expansion + */ + int (*func)(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **args); +} FFExpandTextFunction; + +/** + * Text expander context, used to encapsulate the logic to expand a + * given text template. + * + * A backslash character @samp{\} in a text template, followed by any + * character, always expands to the second character. + * Sequences of the form %{FUNCTION_NAME[:PARAMS]} are expanded using a + * function defined in the object. The text between the braces is a + * function name, possibly followed by arguments separated by ':'. If + * the arguments contain special characters or delimiters (':' or + * '}'), they should be escaped. + */ +typedef struct FFExpandTextContext { + /** + * log context to pass to the function, used for logging and for + * accessing the context for the function + */ + void *log_ctx; + + /** + * list of functions to use to expand sequences in the format + * FUNCTION_NAME{PARAMS} + */ + FFExpandTextFunction *functions; + + /** + * number of functions + */ + unsigned int functions_nb; +} FFExpandTextContext; + +/** + * Expand text template. + * + * Expand text template defined in text using the logic defined in a text + * expander object. + * + * @param expand_text text expansion context used to expand the text + * @param text template text to expand + * @param bp BPrint object where the expanded text is written to + * @return negative value corresponding to an AVERROR error code in case of + * errors, a non-negative value otherwise + */ +int ff_expand_text(FFExpandTextContext *expand_text, char *text, AVBPrint *bp); + +/** + * Print PTS representation to an AVBPrint object. + * + * @param log_ctx pointer to av_log object + * @param bp AVBPrint object where the PTS textual representation is written to + * @param pts PTS value expressed as a double to represent + * @param delta delta time parsed by av_parse_time(), added to the PTS + * @param fmt string representing the format to use for printing, can be + * "flt" - use a float representation with 6 decimal digits, + * "hms" - use HH:MM:SS.MMM format, + * "hms24hh" - same as "hms" but wraps the hours in 24hh format + * (so that it is expressed in the range 00-23), + * "localtime" or "gmtime" - expand the PTS according to the + * @code{strftime()} function rules, using either the corresponding + * @code{localtime()} or @code{gmtime()} time + * @param strftime_fmt: @code{strftime()} format to use to represent the PTS in + * case the format "localtime" or "gmtime" was selected, if not specified + * defaults to "%Y-%m-%d %H:%M:%S" + * @return negative value corresponding to an AVERROR error code in case of + * errors, a non-negative value otherwise + */ +int ff_print_pts(void *log_ctx, AVBPrint *bp, double pts, const char *delta, + const char *fmt, const char *strftime_fmt); + +/** + * Print time representation to an AVBPrint object. + * + * @param log_ctx pointer to av_log object + * @param bp AVBPrint object where the time textual representation is written to + * @param strftime_fmt: strftime() format to use to represent the time in case + * if not specified defaults to "%Y-%m-%d %H:%M:%S". The format string is + * extended to support the %[1-6]N after %S which prints fractions of the + * second with optionally specified number of digits, if not specified + * defaults to 3. + * @param localtime use local time to compute the time if non-zero, otherwise + * use UTC + * @return negative value corresponding to an AVERROR error code in case of + * errors, a non-negative value otherwise + */ +int ff_print_time(void *log_ctx, AVBPrint *bp, const char *strftime_fmt, char localtime); + +typedef double (*ff_eval_func2)(void *, double a, double b); + +/** + * Evaluate and print expression to an AVBprint object. + * The output is written as a double representation. + * + * This is a wrapper around av_expr_parse_and_eval() and following the + * same rules. + * + * @param log_ctx pointer to av_log object + * @param bp AVBPrint object where the evaluated expression is written to + * @param expr the expression to be evaluated + * @param fun_names names of the ff_eval_func2 functions used to evaluate the expression + * @param fun_values values of the ff_eval_func2 functions used to evaluate the expression + * @param var_names names of the variables used in the expression + * @param var_values values of the variables used in the expression + * @param eval_ctx evaluation context to be passed to some functions + * + * @return negative value corresponding to an AVERROR error code in case of + * errors, a non-negative value otherwise + */ +int ff_print_eval_expr(void *log_ctx, AVBPrint *bp, + const char *expr, + const char * const *fun_names, const ff_eval_func2 *fun_values, + const char * const *var_names, const double *var_values, + void *eval_ctx); + +/** + * Evaluate and print expression to an AVBprint object, using the + * specified format. + * + * This is a wrapper around av_expr_parse_and_eval() and following the + * same rules. + * + * The format is specified as a printf format character, optionally + * preceded by the positions numbers for zero-padding. + * + * The following formats are accepted: + * - x: use lowercase hexadecimal representation + * - X: use uppercase hexadecimal representation + * - d: use decimal representation + * - u: use unsigned decimal representation + * + * @param log_ctx pointer to av_log object + * @param bp AVBPrint object where the evaluated expression is written to + * @param expr the expression to be evaluated + * @param fun_names names of the ff_eval_func2 functions used to evaluate the expression + * @param fun_values values of the ff_eval_func2 functions used to evaluate the expression + * @param var_names names of the variables used in the expression + * @param var_values values of the variables used in the expression + * @param eval_ctx evaluation context to be passed to some functions + * @param format a character representing the format, to be chosen in xXdu + * @param positions final size of the value representation with 0-padding + * @return negative value corresponding to an AVERROR error code in case of + * errors, a non-negative value otherwise + */ +int ff_print_formatted_eval_expr(void *log_ctx, AVBPrint *bp, + const char *expr, + const char * const *fun_names, const ff_eval_func2 *fun_values, + const char * const *var_names, const double *var_values, + void *eval_ctx, + const char format, int positions); + +/** + * Check if the character is a newline. + * + * @param c character to check + * @return non-negative value in case c is a newline, 0 otherwise + */ +static inline int ff_is_newline(uint32_t c) +{ + return c == '\n' || c == '\r' || c == '\f' || c == '\v'; +} + +/** + * Load text file into the buffer pointed by text. + * + * @param log_ctx pointer to av_log object + * @param textfile filename containing the text to load + * @param text pointer to the text buffer where the loaded text will be + * loaded + * @param text_size pointer to the value to set with the loaded text data, + * including the terminating 0 character + * @return negative value corresponding to an AVERROR error code in case of + * errors, a non-negative value otherwise + */ +int ff_load_textfile(void *log_ctx, const char *textfile, + unsigned char **text, size_t *text_size); + +#endif /* AVFILTER_TEXTUTILS__H */ diff --git a/libavfilter/tinterlace.h b/libavfilter/tinterlace.h index 37b6c10c08c..f9b3e630b8e 100644 --- a/libavfilter/tinterlace.h +++ b/libavfilter/tinterlace.h @@ -32,6 +32,7 @@ #include "libavutil/pixdesc.h" #include "drawutils.h" #include "avfilter.h" +#include "ccfifo.h" #define TINTERLACE_FLAG_VLPF 01 #define TINTERLACE_FLAG_CVLPF 2 @@ -77,6 +78,7 @@ typedef struct TInterlaceContext { const AVPixFmtDescriptor *csp; void (*lowpass_line)(uint8_t *dstp, ptrdiff_t width, const uint8_t *srcp, ptrdiff_t mref, ptrdiff_t pref, int clip_max); + CCFifo cc_fifo; } TInterlaceContext; void ff_tinterlace_init_x86(TInterlaceContext *interlace); diff --git a/libavfilter/trim.c b/libavfilter/trim.c index ee6e821cd24..4c1a2b4f483 100644 --- a/libavfilter/trim.c +++ b/libavfilter/trim.c @@ -31,6 +31,8 @@ #include "audio.h" #include "avfilter.h" #include "internal.h" +#include "filters.h" +#include "video.h" typedef struct TrimContext { const AVClass *class; @@ -68,6 +70,8 @@ typedef struct TrimContext { int64_t next_pts; int eof; + + int (*filter_frame)(AVFilterLink *inlink, AVFrame *frame); } TrimContext; static av_cold int init(AVFilterContext *ctx) @@ -79,47 +83,6 @@ static av_cold int init(AVFilterContext *ctx) return 0; } -static int config_input(AVFilterLink *inlink) -{ - AVFilterContext *ctx = inlink->dst; - TrimContext *s = ctx->priv; - AVRational tb = (inlink->type == AVMEDIA_TYPE_VIDEO) ? - inlink->time_base : (AVRational){ 1, inlink->sample_rate }; - - if (s->start_time != INT64_MAX) { - int64_t start_pts = av_rescale_q(s->start_time, AV_TIME_BASE_Q, tb); - if (s->start_pts == AV_NOPTS_VALUE || start_pts < s->start_pts) - s->start_pts = start_pts; - } - if (s->end_time != INT64_MAX) { - int64_t end_pts = av_rescale_q(s->end_time, AV_TIME_BASE_Q, tb); - if (s->end_pts == AV_NOPTS_VALUE || end_pts > s->end_pts) - s->end_pts = end_pts; - } - if (s->duration) - s->duration_tb = av_rescale_q(s->duration, AV_TIME_BASE_Q, tb); - - return 0; -} - -#define OFFSET(x) offsetof(TrimContext, x) -#define COMMON_OPTS \ - { "start", "Timestamp of the first frame that " \ - "should be passed", OFFSET(start_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ - { "starti", "Timestamp of the first frame that " \ - "should be passed", OFFSET(start_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ - { "end", "Timestamp of the first frame that " \ - "should be dropped again", OFFSET(end_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ - { "endi", "Timestamp of the first frame that " \ - "should be dropped again", OFFSET(end_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ - { "start_pts", "Timestamp of the first frame that should be " \ - " passed", OFFSET(start_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \ - { "end_pts", "Timestamp of the first frame that should be " \ - "dropped again", OFFSET(end_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \ - { "duration", "Maximum duration of the output", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS }, \ - { "durationi", "Maximum duration of the output", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS }, - - #if CONFIG_TRIM_FILTER static int trim_filter_frame(AVFilterLink *inlink, AVFrame *frame) { @@ -161,7 +124,8 @@ static int trim_filter_frame(AVFilterLink *inlink, AVFrame *frame) if (drop) { s->eof = 1; - ff_avfilter_link_set_out_status(inlink, AVERROR_EOF, AV_NOPTS_VALUE); + ff_inlink_set_status(inlink, AVERROR_EOF); + ff_outlink_set_status(ctx->outputs[0], AVERROR_EOF, frame->pts); goto drop; } } @@ -171,49 +135,12 @@ static int trim_filter_frame(AVFilterLink *inlink, AVFrame *frame) return ff_filter_frame(ctx->outputs[0], frame); drop: + if (!s->eof) + ff_filter_set_ready(ctx, 100); s->nb_frames++; av_frame_free(&frame); return 0; } - -#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM -static const AVOption trim_options[] = { - COMMON_OPTS - { "start_frame", "Number of the first frame that should be passed " - "to the output", OFFSET(start_frame), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, - { "end_frame", "Number of the first frame that should be dropped " - "again", OFFSET(end_frame), AV_OPT_TYPE_INT64, { .i64 = INT64_MAX }, 0, INT64_MAX, FLAGS }, - { NULL } -}; -#undef FLAGS - -AVFILTER_DEFINE_CLASS(trim); - -static const AVFilterPad trim_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = trim_filter_frame, - .config_props = config_input, - }, -}; - -static const AVFilterPad trim_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -const AVFilter ff_vf_trim = { - .name = "trim", - .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."), - .init = init, - .priv_size = sizeof(TrimContext), - .priv_class = &trim_class, - FILTER_INPUTS(trim_inputs), - FILTER_OUTPUTS(trim_outputs), -}; #endif // CONFIG_TRIM_FILTER #if CONFIG_ATRIM_FILTER @@ -290,7 +217,8 @@ static int atrim_filter_frame(AVFilterLink *inlink, AVFrame *frame) if (drop) { s->eof = 1; - ff_avfilter_link_set_out_status(inlink, AVERROR_EOF, AV_NOPTS_VALUE); + ff_inlink_set_status(inlink, AVERROR_EOF); + ff_outlink_set_status(ctx->outputs[0], AVERROR_EOF, frame->pts); goto drop; } } @@ -324,10 +252,124 @@ static int atrim_filter_frame(AVFilterLink *inlink, AVFrame *frame) return ff_filter_frame(ctx->outputs[0], frame); drop: + if (!s->eof) + ff_filter_set_ready(ctx, 100); s->nb_samples += frame->nb_samples; av_frame_free(&frame); return 0; } +#endif // CONFIG_ATRIM_FILTER + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + TrimContext *s = ctx->priv; + AVRational tb = (inlink->type == AVMEDIA_TYPE_VIDEO) ? + inlink->time_base : (AVRational){ 1, inlink->sample_rate }; + +#if CONFIG_TRIM_FILTER + if (inlink->type == AVMEDIA_TYPE_VIDEO) + s->filter_frame = trim_filter_frame; +#endif +#if CONFIG_ATRIM_FILTER + if (inlink->type == AVMEDIA_TYPE_AUDIO) + s->filter_frame = atrim_filter_frame; +#endif + if (s->start_time != INT64_MAX) { + int64_t start_pts = av_rescale_q(s->start_time, AV_TIME_BASE_Q, tb); + if (s->start_pts == AV_NOPTS_VALUE || start_pts < s->start_pts) + s->start_pts = start_pts; + } + if (s->end_time != INT64_MAX) { + int64_t end_pts = av_rescale_q(s->end_time, AV_TIME_BASE_Q, tb); + if (s->end_pts == AV_NOPTS_VALUE || end_pts > s->end_pts) + s->end_pts = end_pts; + } + if (s->duration) + s->duration_tb = av_rescale_q(s->duration, AV_TIME_BASE_Q, tb); + + return 0; +} + +static int activate(AVFilterContext *ctx) +{ + TrimContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + if (!s->eof && ff_inlink_queued_frames(inlink)) { + AVFrame *frame = NULL; + int ret; + + ret = ff_inlink_consume_frame(inlink, &frame); + if (ret < 0) + return ret; + if (ret > 0) + return s->filter_frame(inlink, frame); + } + + FF_FILTER_FORWARD_STATUS(inlink, outlink); + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} + +#define OFFSET(x) offsetof(TrimContext, x) +#define COMMON_OPTS \ + { "start", "Timestamp of the first frame that " \ + "should be passed", OFFSET(start_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ + { "starti", "Timestamp of the first frame that " \ + "should be passed", OFFSET(start_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ + { "end", "Timestamp of the first frame that " \ + "should be dropped again", OFFSET(end_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ + { "endi", "Timestamp of the first frame that " \ + "should be dropped again", OFFSET(end_time), AV_OPT_TYPE_DURATION, { .i64 = INT64_MAX }, INT64_MIN, INT64_MAX, FLAGS }, \ + { "start_pts", "Timestamp of the first frame that should be " \ + " passed", OFFSET(start_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \ + { "end_pts", "Timestamp of the first frame that should be " \ + "dropped again", OFFSET(end_pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, FLAGS }, \ + { "duration", "Maximum duration of the output", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS }, \ + { "durationi", "Maximum duration of the output", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT64_MAX, FLAGS }, + + +#if CONFIG_TRIM_FILTER + +#define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM +static const AVOption trim_options[] = { + COMMON_OPTS + { "start_frame", "Number of the first frame that should be passed " + "to the output", OFFSET(start_frame), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, + { "end_frame", "Number of the first frame that should be dropped " + "again", OFFSET(end_frame), AV_OPT_TYPE_INT64, { .i64 = INT64_MAX }, 0, INT64_MAX, FLAGS }, + { NULL } +}; +#undef FLAGS + +AVFILTER_DEFINE_CLASS(trim); + +static const AVFilterPad trim_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, + }, +}; + +const AVFilter ff_vf_trim = { + .name = "trim", + .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."), + .init = init, + .activate = activate, + .priv_size = sizeof(TrimContext), + .priv_class = &trim_class, + FILTER_INPUTS(trim_inputs), + FILTER_OUTPUTS(ff_video_default_filterpad), +}; +#endif // CONFIG_TRIM_FILTER + +#if CONFIG_ATRIM_FILTER #define FLAGS AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_FILTERING_PARAM static const AVOption atrim_options[] = { @@ -346,26 +388,19 @@ static const AVFilterPad atrim_inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_AUDIO, - .filter_frame = atrim_filter_frame, .config_props = config_input, }, }; -static const AVFilterPad atrim_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_AUDIO, - }, -}; - const AVFilter ff_af_atrim = { .name = "atrim", .description = NULL_IF_CONFIG_SMALL("Pick one continuous section from the input, drop the rest."), .init = init, + .activate = activate, .priv_size = sizeof(TrimContext), .priv_class = &atrim_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(atrim_inputs), - FILTER_OUTPUTS(atrim_outputs), + FILTER_OUTPUTS(ff_audio_default_filterpad), }; #endif // CONFIG_ATRIM_FILTER diff --git a/libavfilter/v360.h b/libavfilter/v360.h index 5d797ab828b..b3660c234ca 100644 --- a/libavfilter/v360.h +++ b/libavfilter/v360.h @@ -160,14 +160,14 @@ typedef struct V360Context { int in_width, in_height; int out_width, out_height; - int pr_width[4], pr_height[4]; + int pr_width[AV_VIDEO_MAX_PLANES], pr_height[AV_VIDEO_MAX_PLANES]; - int in_offset_w[4], in_offset_h[4]; - int out_offset_w[4], out_offset_h[4]; + int in_offset_w[AV_VIDEO_MAX_PLANES], in_offset_h[AV_VIDEO_MAX_PLANES]; + int out_offset_w[AV_VIDEO_MAX_PLANES], out_offset_h[AV_VIDEO_MAX_PLANES]; - int planewidth[4], planeheight[4]; - int inplanewidth[4], inplaneheight[4]; - int uv_linesize[4]; + int planewidth[AV_VIDEO_MAX_PLANES], planeheight[AV_VIDEO_MAX_PLANES]; + int inplanewidth[AV_VIDEO_MAX_PLANES], inplaneheight[AV_VIDEO_MAX_PLANES]; + int uv_linesize[AV_VIDEO_MAX_PLANES]; int nb_planes; int nb_allocated; int elements; @@ -176,7 +176,7 @@ typedef struct V360Context { int nb_threads; SliceXYRemap *slice_remap; - unsigned map[4]; + unsigned map[AV_VIDEO_MAX_PLANES]; int (*in_transform)(const struct V360Context *s, const float *vec, int width, int height, diff --git a/libavfilter/vaapi_vpp.c b/libavfilter/vaapi_vpp.c index a323dab8b8a..59961bfa4a5 100644 --- a/libavfilter/vaapi_vpp.c +++ b/libavfilter/vaapi_vpp.c @@ -38,6 +38,10 @@ int ff_vaapi_vpp_query_formats(AVFilterContext *avctx) &avctx->outputs[0]->incfg.formats)) < 0) return err; + if ((err = ff_set_common_all_color_spaces(avctx)) < 0 || + (err = ff_set_common_all_color_ranges(avctx)) < 0) + return err; + return 0; } @@ -95,6 +99,7 @@ int ff_vaapi_vpp_config_input(AVFilterLink *inlink) int ff_vaapi_vpp_config_output(AVFilterLink *outlink) { AVFilterContext *avctx = outlink->src; + AVFilterLink *inlink = avctx->inputs[0]; VAAPIVPPContext *ctx = avctx->priv; AVVAAPIHWConfig *hwconfig = NULL; AVHWFramesConstraints *constraints = NULL; @@ -111,6 +116,17 @@ int ff_vaapi_vpp_config_output(AVFilterLink *outlink) if (!ctx->output_height) ctx->output_height = avctx->inputs[0]->h; + outlink->w = ctx->output_width; + outlink->h = ctx->output_height; + + if (ctx->passthrough) { + if (inlink->hw_frames_ctx) + outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx); + av_log(ctx, AV_LOG_VERBOSE, "Using VAAPI filter passthrough mode.\n"); + + return 0; + } + av_assert0(ctx->input_frames); ctx->device_ref = av_buffer_ref(ctx->input_frames->device_ref); if (!ctx->device_ref) { @@ -214,9 +230,6 @@ int ff_vaapi_vpp_config_output(AVFilterLink *outlink) return AVERROR(EIO); } - outlink->w = ctx->output_width; - outlink->h = ctx->output_height; - if (ctx->build_filter_params) { err = ctx->build_filter_params(avctx); if (err < 0) @@ -518,7 +531,6 @@ int ff_vaapi_vpp_init_params(AVFilterContext *avctx, AVFrame *output_frame) { VAAPIVPPContext *ctx = avctx->priv; - VASurfaceID input_surface; int err; ctx->input_region = (VARectangle) { @@ -534,10 +546,8 @@ int ff_vaapi_vpp_init_params(AVFilterContext *avctx, output_frame->crop_left = 0; output_frame->crop_right = 0; - input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3], - *params = (VAProcPipelineParameterBuffer) { - .surface = input_surface, + .surface = ff_vaapi_vpp_get_surface_id(input_frame), .surface_region = &ctx->input_region, .output_region = NULL, .output_background_color = VAAPI_VPP_BACKGROUND_BLACK, @@ -557,6 +567,10 @@ int ff_vaapi_vpp_init_params(AVFilterContext *avctx, if (err < 0) return err; + av_log(avctx, AV_LOG_DEBUG, "Filter frame from surface %#x to %#x.\n", + ff_vaapi_vpp_get_surface_id(input_frame), + ff_vaapi_vpp_get_surface_id(output_frame)); + return 0; } @@ -623,7 +637,6 @@ int ff_vaapi_vpp_render_pictures(AVFilterContext *avctx, AVFrame *output_frame) { VAAPIVPPContext *ctx = avctx->priv; - VASurfaceID output_surface; VABufferID *params_ids; VAStatus vas; int err; @@ -635,10 +648,8 @@ int ff_vaapi_vpp_render_pictures(AVFilterContext *avctx, for (int i = 0; i < cout; i++) params_ids[i] = VA_INVALID_ID; - output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3]; - vas = vaBeginPicture(ctx->hwctx->display, - ctx->va_context, output_surface); + ctx->va_context, ff_vaapi_vpp_get_surface_id(output_frame)); if (vas != VA_STATUS_SUCCESS) { av_log(avctx, AV_LOG_ERROR, "Failed to attach new picture: " "%d (%s).\n", vas, vaErrorStr(vas)); diff --git a/libavfilter/vaapi_vpp.h b/libavfilter/vaapi_vpp.h index ead07036dce..6764ab0c390 100644 --- a/libavfilter/vaapi_vpp.h +++ b/libavfilter/vaapi_vpp.h @@ -27,6 +27,11 @@ #include "avfilter.h" +static inline VASurfaceID ff_vaapi_vpp_get_surface_id(const AVFrame *frame) +{ + return (uintptr_t)frame->data[3]; +} + // ARGB black, for VAProcPipelineParameterBuffer.output_background_color. #define VAAPI_VPP_BACKGROUND_BLACK 0xff000000 @@ -51,6 +56,8 @@ typedef struct VAAPIVPPContext { VABufferID filter_buffers[VAProcFilterCount]; int nb_filter_buffers; + int passthrough; + int (*build_filter_params)(AVFilterContext *avctx); void (*pipeline_uninit)(AVFilterContext *avctx); diff --git a/libavfilter/vaf_spectrumsynth.c b/libavfilter/vaf_spectrumsynth.c index 896eba558ec..3a9aacd2b54 100644 --- a/libavfilter/vaf_spectrumsynth.c +++ b/libavfilter/vaf_spectrumsynth.c @@ -78,19 +78,19 @@ typedef struct SpectrumSynthContext { static const AVOption spectrumsynth_options[] = { { "sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.i64 = 44100}, 15, INT_MAX, A }, { "channels", "set channels", OFFSET(channels), AV_OPT_TYPE_INT, {.i64 = 1}, 1, 8, A }, - { "scale", "set input amplitude scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64 = LOG}, 0, NB_SCALES-1, V, "scale" }, - { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, V, "scale" }, - { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, V, "scale" }, - { "slide", "set input sliding mode", OFFSET(sliding), AV_OPT_TYPE_INT, {.i64 = FULLFRAME}, 0, NB_SLIDES-1, V, "slide" }, - { "replace", "consume old columns with new", 0, AV_OPT_TYPE_CONST, {.i64=REPLACE}, 0, 0, V, "slide" }, - { "scroll", "consume only most right column", 0, AV_OPT_TYPE_CONST, {.i64=SCROLL}, 0, 0, V, "slide" }, - { "fullframe", "consume full frames", 0, AV_OPT_TYPE_CONST, {.i64=FULLFRAME}, 0, 0, V, "slide" }, - { "rscroll", "consume only most left column", 0, AV_OPT_TYPE_CONST, {.i64=RSCROLL}, 0, 0, V, "slide" }, + { "scale", "set input amplitude scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64 = LOG}, 0, NB_SCALES-1, V, .unit = "scale" }, + { "lin", "linear", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, V, .unit = "scale" }, + { "log", "logarithmic", 0, AV_OPT_TYPE_CONST, {.i64=LOG}, 0, 0, V, .unit = "scale" }, + { "slide", "set input sliding mode", OFFSET(sliding), AV_OPT_TYPE_INT, {.i64 = FULLFRAME}, 0, NB_SLIDES-1, V, .unit = "slide" }, + { "replace", "consume old columns with new", 0, AV_OPT_TYPE_CONST, {.i64=REPLACE}, 0, 0, V, .unit = "slide" }, + { "scroll", "consume only most right column", 0, AV_OPT_TYPE_CONST, {.i64=SCROLL}, 0, 0, V, .unit = "slide" }, + { "fullframe", "consume full frames", 0, AV_OPT_TYPE_CONST, {.i64=FULLFRAME}, 0, 0, V, .unit = "slide" }, + { "rscroll", "consume only most left column", 0, AV_OPT_TYPE_CONST, {.i64=RSCROLL}, 0, 0, V, .unit = "slide" }, WIN_FUNC_OPTION("win_func", OFFSET(win_func), A, 0), { "overlap", "set window overlap", OFFSET(overlap), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 1, A }, - { "orientation", "set orientation", OFFSET(orientation), AV_OPT_TYPE_INT, {.i64=VERTICAL}, 0, NB_ORIENTATIONS-1, V, "orientation" }, - { "vertical", NULL, 0, AV_OPT_TYPE_CONST, {.i64=VERTICAL}, 0, 0, V, "orientation" }, - { "horizontal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=HORIZONTAL}, 0, 0, V, "orientation" }, + { "orientation", "set orientation", OFFSET(orientation), AV_OPT_TYPE_INT, {.i64=VERTICAL}, 0, NB_ORIENTATIONS-1, V, .unit = "orientation" }, + { "vertical", NULL, 0, AV_OPT_TYPE_CONST, {.i64=VERTICAL}, 0, 0, V, .unit = "orientation" }, + { "horizontal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=HORIZONTAL}, 0, 0, V, .unit = "orientation" }, { NULL } }; diff --git a/libavfilter/version.h b/libavfilter/version.h index 7e0eb9af97d..1e884d9b44c 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -31,7 +31,7 @@ #include "version_major.h" -#define LIBAVFILTER_VERSION_MINOR 3 +#define LIBAVFILTER_VERSION_MINOR 1 #define LIBAVFILTER_VERSION_MICRO 100 diff --git a/libavfilter/version_major.h b/libavfilter/version_major.h index 899dfdb27db..c5e660eedaa 100644 --- a/libavfilter/version_major.h +++ b/libavfilter/version_major.h @@ -27,7 +27,7 @@ * Libavfilter version macros */ -#define LIBAVFILTER_VERSION_MAJOR 9 +#define LIBAVFILTER_VERSION_MAJOR 10 /** * FF_API_* defines may be placed below to indicate public API that will be @@ -35,4 +35,6 @@ * the public API and may change, break or disappear at any time. */ +#define FF_API_LINK_PUBLIC (LIBAVFILTER_VERSION_MAJOR < 11) + #endif /* AVFILTER_VERSION_MAJOR_H */ diff --git a/libavfilter/vf_addroi.c b/libavfilter/vf_addroi.c index dd3daeda81b..e7ad916214b 100644 --- a/libavfilter/vf_addroi.c +++ b/libavfilter/vf_addroi.c @@ -21,6 +21,7 @@ #include "libavutil/opt.h" #include "avfilter.h" #include "internal.h" +#include "video.h" enum { X, Y, W, H, @@ -246,13 +247,6 @@ static const AVFilterPad addroi_inputs[] = { }, }; -static const AVFilterPad addroi_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_addroi = { .name = "addroi", .description = NULL_IF_CONFIG_SMALL("Add region of interest to frame."), @@ -265,5 +259,5 @@ const AVFilter ff_vf_addroi = { .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(addroi_inputs), - FILTER_OUTPUTS(addroi_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_alphamerge.c b/libavfilter/vf_alphamerge.c index 4bbc06da36a..a5f5baf77e7 100644 --- a/libavfilter/vf_alphamerge.c +++ b/libavfilter/vf_alphamerge.c @@ -60,6 +60,12 @@ static int do_alphamerge(FFFrameSync *fs) if (!alpha_buf) return ff_filter_frame(ctx->outputs[0], main_buf); + if (alpha_buf->color_range == AVCOL_RANGE_MPEG) { + av_log(ctx, AV_LOG_WARNING, "alpha plane color range tagged as %s, " + "output will be wrong!\n", + av_color_range_name(alpha_buf->color_range)); + } + if (s->is_packed_rgb) { int x, y; uint8_t *pin, *pout; diff --git a/libavfilter/vf_amplify.c b/libavfilter/vf_amplify.c index 0891d264134..0ebca60ea28 100644 --- a/libavfilter/vf_amplify.c +++ b/libavfilter/vf_amplify.c @@ -19,12 +19,10 @@ */ #include "libavutil/imgutils.h" -#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_aspect.c b/libavfilter/vf_aspect.c index 0a10a5a1769..129ae19d6d2 100644 --- a/libavfilter/vf_aspect.c +++ b/libavfilter/vf_aspect.c @@ -41,7 +41,8 @@ static const char *const var_names[] = { "w", "h", - "a", "dar", + "a", + "dar", "sar", "hsub", "vsub", @@ -51,7 +52,8 @@ static const char *const var_names[] = { enum var_name { VAR_W, VAR_H, - VAR_A, VAR_DAR, + VAR_A, + VAR_DAR, VAR_SAR, VAR_HSUB, VAR_VSUB, @@ -105,8 +107,8 @@ static int get_aspect_ratio(AVFilterLink *inlink, AVRational *aspect_ratio) /* evaluate new aspect ratio*/ ret = av_expr_parse_and_eval(&res, s->ratio_expr, - var_names, var_values, - NULL, NULL, NULL, NULL, NULL, 0, ctx); + var_names, var_values, + NULL, NULL, NULL, NULL, NULL, 0, ctx); if (ret < 0) { ret = av_parse_ratio(aspect_ratio, s->ratio_expr, s->max, 0, ctx); } else @@ -125,6 +127,14 @@ static int get_aspect_ratio(AVFilterLink *inlink, AVRational *aspect_ratio) return 0; } +static const AVFilterPad aspect_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + }, +}; + #if CONFIG_SETDAR_FILTER static int setdar_config_props(AVFilterLink *outlink) @@ -169,14 +179,6 @@ static const AVOption setdar_options[] = { AVFILTER_DEFINE_CLASS(setdar); -static const AVFilterPad avfilter_vf_setdar_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = filter_frame, - }, -}; - static const AVFilterPad avfilter_vf_setdar_outputs[] = { { .name = "default", @@ -191,7 +193,7 @@ const AVFilter ff_vf_setdar = { .priv_size = sizeof(AspectContext), .priv_class = &setdar_class, .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_vf_setdar_inputs), + FILTER_INPUTS(aspect_inputs), FILTER_OUTPUTS(avfilter_vf_setdar_outputs), }; @@ -232,14 +234,6 @@ static const AVOption setsar_options[] = { AVFILTER_DEFINE_CLASS(setsar); -static const AVFilterPad avfilter_vf_setsar_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = filter_frame, - }, -}; - static const AVFilterPad avfilter_vf_setsar_outputs[] = { { .name = "default", @@ -254,7 +248,7 @@ const AVFilter ff_vf_setsar = { .priv_size = sizeof(AspectContext), .priv_class = &setsar_class, .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_vf_setsar_inputs), + FILTER_INPUTS(aspect_inputs), FILTER_OUTPUTS(avfilter_vf_setsar_outputs), }; diff --git a/libavfilter/vf_atadenoise.c b/libavfilter/vf_atadenoise.c index 2178c62faf5..da132db1b6f 100644 --- a/libavfilter/vf_atadenoise.c +++ b/libavfilter/vf_atadenoise.c @@ -34,7 +34,6 @@ #include "bufferqueue.h" #include "atadenoise.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -79,9 +78,9 @@ static const AVOption atadenoise_options[] = { { "2b", "set threshold B for 3rd plane", OFFSET(fthrb[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.04}, 0, 5.0, FLAGS }, { "s", "set how many frames to use", OFFSET(size), AV_OPT_TYPE_INT, {.i64=9}, 5, SIZE, VF }, { "p", "set what planes to filter", OFFSET(planes), AV_OPT_TYPE_FLAGS, {.i64=7}, 0, 15, FLAGS }, - { "a", "set variant of algorithm", OFFSET(algorithm),AV_OPT_TYPE_INT, {.i64=PARALLEL}, 0, NB_ATAA-1, FLAGS, "a" }, - { "p", "parallel", 0, AV_OPT_TYPE_CONST, {.i64=PARALLEL}, 0, 0, FLAGS, "a" }, - { "s", "serial", 0, AV_OPT_TYPE_CONST, {.i64=SERIAL}, 0, 0, FLAGS, "a" }, + { "a", "set variant of algorithm", OFFSET(algorithm),AV_OPT_TYPE_INT, {.i64=PARALLEL}, 0, NB_ATAA-1, FLAGS, .unit = "a" }, + { "p", "parallel", 0, AV_OPT_TYPE_CONST, {.i64=PARALLEL}, 0, 0, FLAGS, .unit = "a" }, + { "s", "serial", 0, AV_OPT_TYPE_CONST, {.i64=SERIAL}, 0, 0, FLAGS, .unit = "a" }, { "0s", "set sigma for 1st plane", OFFSET(sigma[0]), AV_OPT_TYPE_FLOAT, {.dbl=INT16_MAX}, 0, INT16_MAX, FLAGS }, { "1s", "set sigma for 2nd plane", OFFSET(sigma[1]), AV_OPT_TYPE_FLOAT, {.dbl=INT16_MAX}, 0, INT16_MAX, FLAGS }, { "2s", "set sigma for 3rd plane", OFFSET(sigma[2]), AV_OPT_TYPE_FLOAT, {.dbl=INT16_MAX}, 0, INT16_MAX, FLAGS }, diff --git a/libavfilter/vf_avgblur.c b/libavfilter/vf_avgblur.c index bd4471cb8e7..d1ce029b5cd 100644 --- a/libavfilter/vf_avgblur.c +++ b/libavfilter/vf_avgblur.c @@ -25,7 +25,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -288,7 +287,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) const int width = s->planewidth[plane]; if (!(s->planes & (1 << plane))) { - if (out != in) + if (out->data[plane] != in->data[plane]) av_image_copy_plane(out->data[plane], out->linesize[plane], in->data[plane], in->linesize[plane], width * ((s->depth + 7) / 8), height); @@ -334,13 +333,6 @@ static const AVFilterPad avgblur_inputs[] = { }, }; -static const AVFilterPad avgblur_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_avgblur = { .name = "avgblur", .description = NULL_IF_CONFIG_SMALL("Apply Average Blur filter."), @@ -348,7 +340,7 @@ const AVFilter ff_vf_avgblur = { .priv_class = &avgblur_class, .uninit = uninit, FILTER_INPUTS(avgblur_inputs), - FILTER_OUTPUTS(avgblur_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = process_command, diff --git a/libavfilter/vf_avgblur_opencl.c b/libavfilter/vf_avgblur_opencl.c index 7c0578b694b..c00d2f6363c 100644 --- a/libavfilter/vf_avgblur_opencl.c +++ b/libavfilter/vf_avgblur_opencl.c @@ -59,7 +59,7 @@ static int avgblur_opencl_init(AVFilterContext *avctx) cl_int cle; int err; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_avgblur, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_avgblur_cl, 1); if (err < 0) goto fail; @@ -390,6 +390,7 @@ const AVFilter ff_vf_boxblur_opencl = { FILTER_OUTPUTS(avgblur_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif /* CONFIG_BOXBLUR_OPENCL_FILTER */ diff --git a/libavfilter/vf_avgblur_vulkan.c b/libavfilter/vf_avgblur_vulkan.c index d118ce802ca..6bc1b616a6e 100644 --- a/libavfilter/vf_avgblur_vulkan.c +++ b/libavfilter/vf_avgblur_vulkan.c @@ -1,4 +1,6 @@ /* + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -19,23 +21,25 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" - -#define CGS 32 +#include "video.h" typedef struct AvgBlurVulkanContext { FFVulkanContext vkctx; int initialized; + FFVkExecPool e; FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl_hor; - FFVulkanPipeline *pl_ver; + VkSampler sampler; + FFVulkanPipeline pl; + FFVkSPIRVShader shd; - /* Shader updators, must be in the main filter struct */ - VkDescriptorImageInfo input_images[3]; - VkDescriptorImageInfo tmp_images[3]; - VkDescriptorImageInfo output_images[3]; + /* Push constants / options */ + struct { + float filter_norm[4]; + int32_t filter_len[2]; + } opts; int size_x; int size_y; @@ -43,46 +47,53 @@ typedef struct AvgBlurVulkanContext { } AvgBlurVulkanContext; static const char blur_kernel[] = { - C(0, shared vec4 cache[DIR(gl_WorkGroupSize) + FILTER_RADIUS*2 + 1]; ) - C(0, ) - C(0, void distort(const ivec2 pos, const int idx) ) - C(0, { ) - C(1, const uint cp = DIR(gl_LocalInvocationID) + FILTER_RADIUS; ) - C(0, ) - C(1, cache[cp] = texture(input_img[idx], pos); ) - C(0, ) - C(1, const ivec2 loc_l = pos - INC(FILTER_RADIUS); ) - C(1, cache[cp - FILTER_RADIUS] = texture(input_img[idx], loc_l); ) - C(0, ) - C(1, const ivec2 loc_h = pos + INC(DIR(gl_WorkGroupSize)); ) - C(1, cache[cp + DIR(gl_WorkGroupSize)] = texture(input_img[idx], loc_h); ) - C(0, ) - C(1, barrier(); ) - C(0, ) - C(1, vec4 sum = vec4(0); ) - C(1, for (int p = -FILTER_RADIUS; p <= FILTER_RADIUS; p++) ) - C(2, sum += cache[cp + p]; ) - C(0, ) - C(1, sum /= vec4(FILTER_RADIUS*2 + 1); ) - C(1, imageStore(output_img[idx], pos, sum); ) - C(0, } ) + C(0, void distort(const ivec2 pos, const int idx) ) + C(0, { ) + C(1, vec4 sum = vec4(0); ) + C(1, for (int y = -filter_len.y; y <= filter_len.y; y++) ) + C(1, for (int x = -filter_len.x; x <= filter_len.x; x++) ) + C(2, sum += texture(input_img[idx], pos + ivec2(x, y)); ) + C(0, ) + C(1, imageStore(output_img[idx], pos, sum * filter_norm); ) + C(0, } ) }; static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { int err; - FFVkSPIRVShader *shd; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; AvgBlurVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } - FFVulkanDescriptorSetBinding desc_i[2] = { + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_LINEAR)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "avgblur_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + shd = &s->shd; + + ff_vk_shader_set_compute_sizes(shd, 32, 1, 1); + + desc = (FFVulkanDescriptorSetBinding []) { { .name = "input_img", .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .dimensions = 2, .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), }, { .name = "output_img", @@ -95,244 +106,66 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) }, }; - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 2, 0, 0)); - desc_i[0].sampler = ff_vk_init_sampler(vkctx, 1, VK_FILTER_LINEAR); - if (!desc_i[0].sampler) - return AVERROR_EXTERNAL; + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, vec4 filter_norm; ); + GLSLC(1, ivec2 filter_len; ); + GLSLC(0, }; ); + GLSLC(0, ); - { /* Create shader for the horizontal pass */ - desc_i[0].updater = s->input_images; - desc_i[1].updater = s->tmp_images; - - s->pl_hor = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl_hor) - return AVERROR(ENOMEM); - - shd = ff_vk_init_shader(s->pl_hor, "avgblur_compute_hor", - VK_SHADER_STAGE_COMPUTE_BIT); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, (int [3]){ CGS, 1, 1 }); - - RET(ff_vk_add_descriptor_set(vkctx, s->pl_hor, shd, desc_i, FF_ARRAY_ELEMS(desc_i), 0)); - - GLSLF(0, #define FILTER_RADIUS (%i) ,s->size_x - 1); - GLSLC(0, #define INC(x) (ivec2(x, 0)) ); - GLSLC(0, #define DIR(var) (var.x) ); - GLSLD( blur_kernel ); - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - for (int i = 0; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_img[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - if (s->planes & (1 << i)) { - GLSLF(2, distort(pos, %i); ,i); - } else { - GLSLF(2, vec4 res = texture(input_img[%i], pos); ,i); - GLSLF(2, imageStore(output_img[%i], pos, res); ,i); - } - GLSLC(1, } ); - } - GLSLC(0, } ); - - RET(ff_vk_compile_shader(vkctx, shd, "main")); - - RET(ff_vk_init_pipeline_layout(vkctx, s->pl_hor)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl_hor)); - } + ff_vk_add_push_constant(&s->pl, 0, sizeof(s->opts), + VK_SHADER_STAGE_COMPUTE_BIT); - { /* Create shader for the vertical pass */ - desc_i[0].updater = s->tmp_images; - desc_i[1].updater = s->output_images; - - s->pl_ver = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl_ver) - return AVERROR(ENOMEM); - - shd = ff_vk_init_shader(s->pl_ver, "avgblur_compute_ver", - VK_SHADER_STAGE_COMPUTE_BIT); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, (int [3]){ 1, CGS, 1 }); - - RET(ff_vk_add_descriptor_set(vkctx, s->pl_ver, shd, desc_i, FF_ARRAY_ELEMS(desc_i), 0)); - - GLSLF(0, #define FILTER_RADIUS (%i) ,s->size_y - 1); - GLSLC(0, #define INC(x) (ivec2(0, x)) ); - GLSLC(0, #define DIR(var) (var.y) ); - GLSLD( blur_kernel ); - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - for (int i = 0; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_img[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - if (s->planes & (1 << i)) { - GLSLF(2, distort(pos, %i); ,i); - } else { - GLSLF(2, vec4 res = texture(input_img[%i], pos); ,i); - GLSLF(2, imageStore(output_img[%i], pos, res); ,i); - } - GLSLC(1, } ); + GLSLD( blur_kernel ); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 size; ); + GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + for (int i = 0; i < planes; i++) { + GLSLC(0, ); + GLSLF(1, size = imageSize(output_img[%i]); ,i); + GLSLC(1, if (!IS_WITHIN(pos, size)) ); + GLSLC(2, return; ); + if (s->planes & (1 << i)) { + GLSLF(1, distort(pos, %i); ,i); + } else { + GLSLF(1, vec4 res = texture(input_img[%i], pos); ,i); + GLSLF(1, imageStore(output_img[%i], pos, res); ,i); } - GLSLC(0, } ); - - RET(ff_vk_compile_shader(vkctx, shd, "main")); - - RET(ff_vk_init_pipeline_layout(vkctx, s->pl_ver)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl_ver)); } + GLSLC(0, } ); + + RET(spv->compile_shader(spv, ctx, &s->shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, &s->shd, spv_data, spv_len, "main")); - /* Execution context */ - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, &s->shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); s->initialized = 1; + s->opts.filter_len[0] = s->size_x - 1; + s->opts.filter_len[1] = s->size_y - 1; - return 0; + s->opts.filter_norm[0] = s->opts.filter_len[0]*2 + 1; + s->opts.filter_norm[0] = 1.0/(s->opts.filter_norm[0]*s->opts.filter_norm[0]); + s->opts.filter_norm[1] = s->opts.filter_norm[0]; + s->opts.filter_norm[2] = s->opts.filter_norm[0]; + s->opts.filter_norm[3] = s->opts.filter_norm[0]; fail: - return err; -} - -static int process_frames(AVFilterContext *avctx, AVFrame *out_f, AVFrame *tmp_f, AVFrame *in_f) -{ - int err; - VkCommandBuffer cmd_buf; - AvgBlurVulkanContext *s = avctx->priv; - FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &vkctx->vkfn; - AVVkFrame *in = (AVVkFrame *)in_f->data[0]; - AVVkFrame *tmp = (AVVkFrame *)tmp_f->data[0]; - AVVkFrame *out = (AVVkFrame *)out_f->data[0]; - - const VkFormat *input_formats = av_vkfmt_from_pixfmt(s->vkctx.input_format); - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - /* Update descriptors and init the exec context */ - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->input_images[i].imageView, in->img[i], - input_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->tmp_images[i].imageView, tmp->img[i], - output_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->tmp_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } - - ff_vk_update_descriptor_set(vkctx, s->pl_hor, 0); - ff_vk_update_descriptor_set(vkctx, s->pl_ver, 0); - - for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier bar[] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = in->layout[i], - .newLayout = s->input_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = in->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT, - .oldLayout = tmp->layout[i], - .newLayout = s->tmp_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = tmp->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(bar), bar); - - in->layout[i] = bar[0].newLayout; - in->access[i] = bar[0].dstAccessMask; - - tmp->layout[i] = bar[1].newLayout; - tmp->access[i] = bar[1].dstAccessMask; - - out->layout[i] = bar[2].newLayout; - out->access[i] = bar[2].dstAccessMask; - } - - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl_hor); - - vk->CmdDispatch(cmd_buf, FFALIGN(s->vkctx.output_width, CGS)/CGS, - s->vkctx.output_height, 1); - - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl_ver); - - vk->CmdDispatch(cmd_buf, s->vkctx.output_width, - FFALIGN(s->vkctx.output_height, CGS)/CGS, 1); - - ff_vk_add_exec_dep(vkctx, s->exec, in_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, out_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(vkctx,s->exec); - if (err) - return err; - - ff_vk_qf_rotate(&s->qf); + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); return err; - -fail: - ff_vk_discard_exec_deps(s->exec); - return err; } static int avgblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) { int err; - AVFrame *tmp = NULL, *out = NULL; + AVFrame *out = NULL; AVFilterContext *ctx = link->dst; AvgBlurVulkanContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; @@ -343,29 +176,22 @@ static int avgblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) goto fail; } - tmp = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!tmp) { - err = AVERROR(ENOMEM); - goto fail; - } - if (!s->initialized) RET(init_filter(ctx, in)); - RET(process_frames(ctx, out, tmp, in)); + RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, + out, in, s->sampler, &s->opts, sizeof(s->opts))); err = av_frame_copy_props(out, in); if (err < 0) goto fail; av_frame_free(&in); - av_frame_free(&tmp); return ff_filter_frame(outlink, out); fail: av_frame_free(&in); - av_frame_free(&tmp); av_frame_free(&out); return err; } @@ -373,6 +199,16 @@ static int avgblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) static void avgblur_vulkan_uninit(AVFilterContext *avctx) { AvgBlurVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); ff_vk_uninit(&s->vkctx); @@ -382,9 +218,9 @@ static void avgblur_vulkan_uninit(AVFilterContext *avctx) #define OFFSET(x) offsetof(AvgBlurVulkanContext, x) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) static const AVOption avgblur_vulkan_options[] = { - { "sizeX", "Set horizontal radius", OFFSET(size_x), AV_OPT_TYPE_INT, {.i64 = 3}, 1, 32, .flags = FLAGS }, + { "sizeX", "Set horizontal radius", OFFSET(size_x), AV_OPT_TYPE_INT, { .i64 = 3 }, 1, 32, .flags = FLAGS }, + { "sizeY", "Set vertical radius", OFFSET(size_y), AV_OPT_TYPE_INT, { .i64 = 3 }, 1, 32, .flags = FLAGS }, { "planes", "Set planes to filter (bitmask)", OFFSET(planes), AV_OPT_TYPE_INT, {.i64 = 0xF}, 0, 0xF, .flags = FLAGS }, - { "sizeY", "Set vertical radius", OFFSET(size_y), AV_OPT_TYPE_INT, {.i64 = 3}, 1, 32, .flags = FLAGS }, { NULL }, }; @@ -418,4 +254,5 @@ const AVFilter ff_vf_avgblur_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &avgblur_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_backgroundkey.c b/libavfilter/vf_backgroundkey.c index 8c7c289d0bd..26fb08bf869 100644 --- a/libavfilter/vf_backgroundkey.c +++ b/libavfilter/vf_backgroundkey.c @@ -17,9 +17,8 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_bilateral.c b/libavfilter/vf_bilateral.c index 41dc38d6f23..fe16419026d 100644 --- a/libavfilter/vf_bilateral.c +++ b/libavfilter/vf_bilateral.c @@ -25,7 +25,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -498,13 +497,6 @@ static const AVFilterPad bilateral_inputs[] = { }, }; -static const AVFilterPad bilateral_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_bilateral = { .name = "bilateral", .description = NULL_IF_CONFIG_SMALL("Apply Bilateral filter."), @@ -512,7 +504,7 @@ const AVFilter ff_vf_bilateral = { .priv_class = &bilateral_class, .uninit = uninit, FILTER_INPUTS(bilateral_inputs), - FILTER_OUTPUTS(bilateral_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_bilateral_cuda.c b/libavfilter/vf_bilateral_cuda.c index 7a271f1ce08..ba008b517af 100644 --- a/libavfilter/vf_bilateral_cuda.c +++ b/libavfilter/vf_bilateral_cuda.c @@ -20,9 +20,7 @@ #include #include -#include -#include "libavutil/avstring.h" #include "libavutil/common.h" #include "libavutil/hwcontext.h" #include "libavutil/hwcontext_cuda_internal.h" @@ -32,9 +30,7 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" #include "cuda/load_helper.h" diff --git a/libavfilter/vf_bitplanenoise.c b/libavfilter/vf_bitplanenoise.c index 5b5c429369f..32235ff2301 100644 --- a/libavfilter/vf_bitplanenoise.c +++ b/libavfilter/vf_bitplanenoise.c @@ -19,9 +19,8 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -197,19 +196,12 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_bitplanenoise = { .name = "bitplanenoise", .description = NULL_IF_CONFIG_SMALL("Measure bit plane noise."), .priv_size = sizeof(BPNContext), FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixfmts), .priv_class = &bitplanenoise_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_blackdetect.c b/libavfilter/vf_blackdetect.c index c9372481694..55033ba5ea3 100644 --- a/libavfilter/vf_blackdetect.c +++ b/libavfilter/vf_blackdetect.c @@ -30,6 +30,7 @@ #include "libavutil/timestamp.h" #include "avfilter.h" #include "internal.h" +#include "video.h" typedef struct BlackDetectContext { const AVClass *class; @@ -241,19 +242,12 @@ static const AVFilterPad blackdetect_inputs[] = { }, }; -static const AVFilterPad blackdetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_blackdetect = { .name = "blackdetect", .description = NULL_IF_CONFIG_SMALL("Detect video intervals that are (almost) black."), .priv_size = sizeof(BlackDetectContext), FILTER_INPUTS(blackdetect_inputs), - FILTER_OUTPUTS(blackdetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .uninit = uninit, .priv_class = &blackdetect_class, diff --git a/libavfilter/vf_blackframe.c b/libavfilter/vf_blackframe.c index e5e185dbea9..10062c995c6 100644 --- a/libavfilter/vf_blackframe.c +++ b/libavfilter/vf_blackframe.c @@ -33,7 +33,6 @@ #include "libavutil/internal.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -72,7 +71,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) p += frame->linesize[0]; } - if (frame->key_frame) + if (frame->flags & AV_FRAME_FLAG_KEY) s->last_keyframe = s->frame; pblack = s->nblack * 100 / (inlink->w * inlink->h); @@ -115,13 +114,6 @@ static const AVFilterPad avfilter_vf_blackframe_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_blackframe_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - const AVFilter ff_vf_blackframe = { .name = "blackframe", .description = NULL_IF_CONFIG_SMALL("Detect frames that are (almost) black."), @@ -129,6 +121,6 @@ const AVFilter ff_vf_blackframe = { .priv_class = &blackframe_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(avfilter_vf_blackframe_inputs), - FILTER_OUTPUTS(avfilter_vf_blackframe_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/vf_blend.c b/libavfilter/vf_blend.c index dfe2b8b174c..74bc5989d99 100644 --- a/libavfilter/vf_blend.c +++ b/libavfilter/vf_blend.c @@ -64,53 +64,53 @@ typedef struct ThreadData { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption blend_options[] = { - { "c0_mode", "set component #0 blend mode", OFFSET(params[0].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode" }, - { "c1_mode", "set component #1 blend mode", OFFSET(params[1].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode" }, - { "c2_mode", "set component #2 blend mode", OFFSET(params[2].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode" }, - { "c3_mode", "set component #3 blend mode", OFFSET(params[3].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, "mode" }, - { "all_mode", "set blend mode for all components", OFFSET(all_mode), AV_OPT_TYPE_INT, {.i64=-1},-1, BLEND_NB-1, FLAGS, "mode" }, - { "addition", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_ADDITION}, 0, 0, FLAGS, "mode" }, - { "addition128","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GRAINMERGE}, 0, 0, FLAGS, "mode" }, - { "grainmerge", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GRAINMERGE}, 0, 0, FLAGS, "mode" }, - { "and", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AND}, 0, 0, FLAGS, "mode" }, - { "average", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AVERAGE}, 0, 0, FLAGS, "mode" }, - { "burn", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_BURN}, 0, 0, FLAGS, "mode" }, - { "darken", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DARKEN}, 0, 0, FLAGS, "mode" }, - { "difference", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIFFERENCE}, 0, 0, FLAGS, "mode" }, - { "difference128", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GRAINEXTRACT}, 0, 0, FLAGS, "mode" }, - { "grainextract", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GRAINEXTRACT}, 0, 0, FLAGS, "mode" }, - { "divide", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIVIDE}, 0, 0, FLAGS, "mode" }, - { "dodge", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DODGE}, 0, 0, FLAGS, "mode" }, - { "exclusion", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_EXCLUSION}, 0, 0, FLAGS, "mode" }, - { "extremity", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_EXTREMITY}, 0, 0, FLAGS, "mode" }, - { "freeze", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_FREEZE}, 0, 0, FLAGS, "mode" }, - { "glow", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GLOW}, 0, 0, FLAGS, "mode" }, - { "hardlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDLIGHT}, 0, 0, FLAGS, "mode" }, - { "hardmix", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDMIX}, 0, 0, FLAGS, "mode" }, - { "heat", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HEAT}, 0, 0, FLAGS, "mode" }, - { "lighten", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_LIGHTEN}, 0, 0, FLAGS, "mode" }, - { "linearlight","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_LINEARLIGHT},0, 0, FLAGS, "mode" }, - { "multiply", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_MULTIPLY}, 0, 0, FLAGS, "mode" }, - { "multiply128","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_MULTIPLY128},0, 0, FLAGS, "mode" }, - { "negation", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NEGATION}, 0, 0, FLAGS, "mode" }, - { "normal", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NORMAL}, 0, 0, FLAGS, "mode" }, - { "or", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OR}, 0, 0, FLAGS, "mode" }, - { "overlay", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OVERLAY}, 0, 0, FLAGS, "mode" }, - { "phoenix", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PHOENIX}, 0, 0, FLAGS, "mode" }, - { "pinlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PINLIGHT}, 0, 0, FLAGS, "mode" }, - { "reflect", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_REFLECT}, 0, 0, FLAGS, "mode" }, - { "screen", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SCREEN}, 0, 0, FLAGS, "mode" }, - { "softlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SOFTLIGHT}, 0, 0, FLAGS, "mode" }, - { "subtract", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SUBTRACT}, 0, 0, FLAGS, "mode" }, - { "vividlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_VIVIDLIGHT}, 0, 0, FLAGS, "mode" }, - { "xor", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_XOR}, 0, 0, FLAGS, "mode" }, - { "softdifference","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SOFTDIFFERENCE}, 0, 0, FLAGS, "mode" }, - { "geometric", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GEOMETRIC}, 0, 0, FLAGS, "mode" }, - { "harmonic", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARMONIC}, 0, 0, FLAGS, "mode" }, - { "bleach", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_BLEACH}, 0, 0, FLAGS, "mode" }, - { "stain", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_STAIN}, 0, 0, FLAGS, "mode" }, - { "interpolate","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_INTERPOLATE},0, 0, FLAGS, "mode" }, - { "hardoverlay","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDOVERLAY},0, 0, FLAGS, "mode" }, + { "c0_mode", "set component #0 blend mode", OFFSET(params[0].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, .unit = "mode" }, + { "c1_mode", "set component #1 blend mode", OFFSET(params[1].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, .unit = "mode" }, + { "c2_mode", "set component #2 blend mode", OFFSET(params[2].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, .unit = "mode" }, + { "c3_mode", "set component #3 blend mode", OFFSET(params[3].mode), AV_OPT_TYPE_INT, {.i64=0}, 0, BLEND_NB-1, FLAGS, .unit = "mode" }, + { "all_mode", "set blend mode for all components", OFFSET(all_mode), AV_OPT_TYPE_INT, {.i64=-1},-1, BLEND_NB-1, FLAGS, .unit = "mode" }, + { "addition", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_ADDITION}, 0, 0, FLAGS, .unit = "mode" }, + { "addition128","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GRAINMERGE}, 0, 0, FLAGS, .unit = "mode" }, + { "grainmerge", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GRAINMERGE}, 0, 0, FLAGS, .unit = "mode" }, + { "and", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AND}, 0, 0, FLAGS, .unit = "mode" }, + { "average", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_AVERAGE}, 0, 0, FLAGS, .unit = "mode" }, + { "burn", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_BURN}, 0, 0, FLAGS, .unit = "mode" }, + { "darken", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DARKEN}, 0, 0, FLAGS, .unit = "mode" }, + { "difference", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIFFERENCE}, 0, 0, FLAGS, .unit = "mode" }, + { "difference128", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GRAINEXTRACT}, 0, 0, FLAGS, .unit = "mode" }, + { "grainextract", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GRAINEXTRACT}, 0, 0, FLAGS, .unit = "mode" }, + { "divide", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DIVIDE}, 0, 0, FLAGS, .unit = "mode" }, + { "dodge", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_DODGE}, 0, 0, FLAGS, .unit = "mode" }, + { "exclusion", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_EXCLUSION}, 0, 0, FLAGS, .unit = "mode" }, + { "extremity", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_EXTREMITY}, 0, 0, FLAGS, .unit = "mode" }, + { "freeze", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_FREEZE}, 0, 0, FLAGS, .unit = "mode" }, + { "glow", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GLOW}, 0, 0, FLAGS, .unit = "mode" }, + { "hardlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDLIGHT}, 0, 0, FLAGS, .unit = "mode" }, + { "hardmix", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDMIX}, 0, 0, FLAGS, .unit = "mode" }, + { "heat", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HEAT}, 0, 0, FLAGS, .unit = "mode" }, + { "lighten", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_LIGHTEN}, 0, 0, FLAGS, .unit = "mode" }, + { "linearlight","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_LINEARLIGHT},0, 0, FLAGS, .unit = "mode" }, + { "multiply", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_MULTIPLY}, 0, 0, FLAGS, .unit = "mode" }, + { "multiply128","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_MULTIPLY128},0, 0, FLAGS, .unit = "mode" }, + { "negation", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NEGATION}, 0, 0, FLAGS, .unit = "mode" }, + { "normal", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_NORMAL}, 0, 0, FLAGS, .unit = "mode" }, + { "or", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OR}, 0, 0, FLAGS, .unit = "mode" }, + { "overlay", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_OVERLAY}, 0, 0, FLAGS, .unit = "mode" }, + { "phoenix", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PHOENIX}, 0, 0, FLAGS, .unit = "mode" }, + { "pinlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_PINLIGHT}, 0, 0, FLAGS, .unit = "mode" }, + { "reflect", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_REFLECT}, 0, 0, FLAGS, .unit = "mode" }, + { "screen", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SCREEN}, 0, 0, FLAGS, .unit = "mode" }, + { "softlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SOFTLIGHT}, 0, 0, FLAGS, .unit = "mode" }, + { "subtract", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SUBTRACT}, 0, 0, FLAGS, .unit = "mode" }, + { "vividlight", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_VIVIDLIGHT}, 0, 0, FLAGS, .unit = "mode" }, + { "xor", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_XOR}, 0, 0, FLAGS, .unit = "mode" }, + { "softdifference","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_SOFTDIFFERENCE}, 0, 0, FLAGS, .unit = "mode" }, + { "geometric", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_GEOMETRIC}, 0, 0, FLAGS, .unit = "mode" }, + { "harmonic", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARMONIC}, 0, 0, FLAGS, .unit = "mode" }, + { "bleach", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_BLEACH}, 0, 0, FLAGS, .unit = "mode" }, + { "stain", "", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_STAIN}, 0, 0, FLAGS, .unit = "mode" }, + { "interpolate","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_INTERPOLATE},0, 0, FLAGS, .unit = "mode" }, + { "hardoverlay","", 0, AV_OPT_TYPE_CONST, {.i64=BLEND_HARDOVERLAY},0, 0, FLAGS, .unit = "mode" }, { "c0_expr", "set color component #0 expression", OFFSET(params[0].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, { "c1_expr", "set color component #1 expression", OFFSET(params[1].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, { "c2_expr", "set color component #2 expression", OFFSET(params[2].expr_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, @@ -133,8 +133,8 @@ static void blend_expr_## name(const uint8_t *_top, ptrdiff_t top_linesize, ptrdiff_t width, ptrdiff_t height, \ FilterParams *param, double *values, int starty) \ { \ - const type *top = (type*)_top; \ - const type *bottom = (type*)_bottom; \ + const type *top = (const type*)_top; \ + const type *bottom = (const type*)_bottom; \ type *dst = (type*)_dst; \ AVExpr *e = param->e; \ int y, x; \ @@ -378,6 +378,14 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar return config_params(ctx); } +static const AVFilterPad blend_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, +}; + #if CONFIG_BLEND_FILTER static int activate(AVFilterContext *ctx) @@ -396,14 +404,6 @@ static const AVFilterPad blend_inputs[] = { }, }; -static const AVFilterPad blend_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_output, - }, -}; - const AVFilter ff_vf_blend = { .name = "blend", .description = NULL_IF_CONFIG_SMALL("Blend two video frames into each other."), @@ -455,14 +455,6 @@ static const AVFilterPad tblend_inputs[] = { }, }; -static const AVFilterPad tblend_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_output, - }, -}; - const AVFilter ff_vf_tblend = { .name = "tblend", .description = NULL_IF_CONFIG_SMALL("Blend successive frames."), @@ -471,7 +463,7 @@ const AVFilter ff_vf_tblend = { .init = init, .uninit = uninit, FILTER_INPUTS(tblend_inputs), - FILTER_OUTPUTS(tblend_outputs), + FILTER_OUTPUTS(blend_outputs), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_blend_init.h b/libavfilter/vf_blend_init.h index f531338a545..d24f1780328 100644 --- a/libavfilter/vf_blend_init.h +++ b/libavfilter/vf_blend_init.h @@ -82,8 +82,8 @@ static void blend_normal_##name(const uint8_t *_top, ptrdiff_t top_linesize, ptrdiff_t width, ptrdiff_t height, \ FilterParams *param, double *values, int starty) \ { \ - const type *top = (type*)_top; \ - const type *bottom = (type*)_bottom; \ + const type *top = (const type*)_top; \ + const type *bottom = (const type*)_bottom; \ type *dst = (type*)_dst; \ const float opacity = param->opacity; \ \ diff --git a/libavfilter/vf_blend_vulkan.c b/libavfilter/vf_blend_vulkan.c index fcc21cbc8d5..417be766b80 100644 --- a/libavfilter/vf_blend_vulkan.c +++ b/libavfilter/vf_blend_vulkan.c @@ -1,5 +1,7 @@ /* * copyright (c) 2021-2022 Wu Jianhua + * Copyright (c) Lynne + * * The blend modes are based on the blend.c. * * This file is part of FFmpeg. @@ -22,11 +24,11 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" #include "framesync.h" #include "blend.h" - -#define CGS 32 +#include "video.h" #define IN_TOP 0 #define IN_BOTTOM 1 @@ -40,20 +42,18 @@ typedef struct FilterParamsVulkan { typedef struct BlendVulkanContext { FFVulkanContext vkctx; - FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl; FFFrameSync fs; - VkDescriptorImageInfo top_images[3]; - VkDescriptorImageInfo bottom_images[3]; - VkDescriptorImageInfo output_images[3]; + int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + FFVkSPIRVShader shd; + VkSampler sampler; FilterParamsVulkan params[4]; double all_opacity; enum BlendMode all_mode; - - int initialized; } BlendVulkanContext; #define DEFINE_BLEND_MODE(MODE, EXPR) \ @@ -125,223 +125,103 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar static av_cold int init_filter(AVFilterContext *avctx) { int err = 0; - FFVkSampler *sampler; - FFVkSPIRVShader *shd; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; BlendVulkanContext *s = avctx->priv; FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - - sampler = ff_vk_init_sampler(vkctx, 1, VK_FILTER_LINEAR); - if (!sampler) + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(avctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); return AVERROR_EXTERNAL; - - s->pl = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl) - return AVERROR(ENOMEM); - - { - FFVulkanDescriptorSetBinding image_descs[] = { - { - .name = "top_images", - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->top_images, - .sampler = sampler, - }, - { - .name = "bottom_images", - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->bottom_images, - .sampler = sampler, - }, - { - .name = "output_images", - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), - .mem_quali = "writeonly", - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->output_images, - }, - }; - - shd = ff_vk_init_shader(s->pl, "blend_compute", image_descs[0].stages); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, (int [3]){ CGS, CGS, 1 }); - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, image_descs, FF_ARRAY_ELEMS(image_descs), 0)); - - for (int i = 0, j = 0; i < planes; i++) { - for (j = 0; j < i; j++) - if (s->params[i].blend_func == s->params[j].blend_func) - break; - /* note: the bracket is needed, for GLSLD is a macro with multiple statements. */ - if (j == i) { - GLSLD(s->params[i].blend_func); - } - } - - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - for (int i = 0; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_images[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - GLSLF(2, const vec4 top = texture(top_images[%i], pos); ,i); - GLSLF(2, const vec4 bottom = texture(bottom_images[%i], pos); ,i); - GLSLF(2, const float opacity = %f; ,s->params[i].opacity); - GLSLF(2, vec4 dst = %s(top, bottom, opacity); ,s->params[i].blend); - GLSLC(0, ); - GLSLF(2, imageStore(output_images[%i], pos, dst); ,i); - GLSLC(1, } ); - } - GLSLC(0, } ); - - RET(ff_vk_compile_shader(vkctx, shd, "main")); - RET(ff_vk_init_pipeline_layout(vkctx, s->pl)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl)); } - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); - - s->initialized = 1; - -fail: - return err; -} - -static int process_frames(AVFilterContext *avctx, AVFrame *out_frame, AVFrame *top_frame, AVFrame *bottom_frame) -{ - int err = 0; - VkCommandBuffer cmd_buf; - BlendVulkanContext *s = avctx->priv; - FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &s->vkctx.vkfn; - const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - AVVkFrame *out = (AVVkFrame *)out_frame->data[0]; - AVVkFrame *top = (AVVkFrame *)top_frame->data[0]; - AVVkFrame *bottom = (AVVkFrame *)bottom_frame->data[0]; - - AVHWFramesContext *top_fc = (AVHWFramesContext*)top_frame->hw_frames_ctx->data; - AVHWFramesContext *bottom_fc = (AVHWFramesContext*)bottom_frame->hw_frames_ctx->data; - - const VkFormat *top_formats = av_vkfmt_from_pixfmt(top_fc->sw_format); - const VkFormat *bottom_formats = av_vkfmt_from_pixfmt(bottom_fc->sw_format); - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->top_images[i].imageView, top->img[i], - top_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->bottom_images[i].imageView, bottom->img[i], - bottom_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->top_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->bottom_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_NEAREST)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "blend_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); + + desc = (FFVulkanDescriptorSetBinding []) { + { + .name = "top_images", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "bottom_images", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "output_images", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 3, 0, 0)); + + for (int i = 0, j = 0; i < planes; i++) { + for (j = 0; j < i; j++) + if (s->params[i].blend_func == s->params[j].blend_func) + break; + /* note: the bracket is needed, for GLSLD is a macro with multiple statements. */ + if (j == i) { + GLSLD(s->params[i].blend_func); + } } - ff_vk_update_descriptor_set(vkctx, s->pl, 0); - + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 size; ); + GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier barriers[] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = top->layout[i], - .newLayout = s->top_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = top->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = bottom->layout[i], - .newLayout = s->bottom_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = bottom->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(barriers), barriers); - - top->layout[i] = barriers[0].newLayout; - top->access[i] = barriers[0].dstAccessMask; - - bottom->layout[i] = barriers[1].newLayout; - bottom->access[i] = barriers[1].dstAccessMask; - - out->layout[i] = barriers[2].newLayout; - out->access[i] = barriers[2].dstAccessMask; + GLSLC(0, ); + GLSLF(1, size = imageSize(output_images[%i]); ,i); + GLSLC(1, if (IS_WITHIN(pos, size)) { ); + GLSLF(2, const vec4 top = texture(top_images[%i], pos); ,i); + GLSLF(2, const vec4 bottom = texture(bottom_images[%i], pos); ,i); + GLSLF(2, const float opacity = %f; ,s->params[i].opacity); + GLSLF(2, vec4 dst = %s(top, bottom, opacity); ,s->params[i].blend); + GLSLC(0, ); + GLSLF(2, imageStore(output_images[%i], pos, dst); ,i); + GLSLC(1, } ); } + GLSLC(0, } ); - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl); - vk->CmdDispatch(cmd_buf, FFALIGN(s->vkctx.output_width, CGS) / CGS, - FFALIGN(s->vkctx.output_height, CGS) / CGS, 1); - - ff_vk_add_exec_dep(vkctx, s->exec, top_frame, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, bottom_frame, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, out_frame, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(vkctx, s->exec); - if (err) - return err; + RET(spv->compile_shader(spv, avctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); - ff_vk_qf_rotate(&s->qf); + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); - return 0; + s->initialized = 1; fail: - ff_vk_discard_exec_deps(s->exec); + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); + return err; } @@ -370,12 +250,15 @@ static int blend_frame(FFFrameSync *fs) if (top_fc->sw_format != bottom_fc->sw_format) { av_log(avctx, AV_LOG_ERROR, "Currently the sw format of the bottom video need to match the top!\n"); - return AVERROR(EINVAL); + err = AVERROR(EINVAL); + goto fail; } RET(init_filter(avctx)); } - RET(process_frames(avctx, out, top, bottom)); + RET(ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->pl, + out, (AVFrame *[]){ top, bottom }, 2, + s->sampler, NULL, 0)); return ff_filter_frame(outlink, out); @@ -396,10 +279,19 @@ static av_cold int init(AVFilterContext *avctx) static av_cold void uninit(AVFilterContext *avctx) { BlendVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; - ff_framesync_uninit(&s->fs); + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); ff_vk_uninit(&s->vkctx); + ff_framesync_uninit(&s->fs); s->initialized = 0; } @@ -447,13 +339,13 @@ static int activate(AVFilterContext *avctx) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) static const AVOption blend_vulkan_options[] = { - { "c0_mode", "set component #0 blend mode", OFFSET(params[0].mode), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, BLEND_NB - 1, FLAGS, "mode" }, - { "c1_mode", "set component #1 blend mode", OFFSET(params[1].mode), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, BLEND_NB - 1, FLAGS, "mode" }, - { "c2_mode", "set component #2 blend mode", OFFSET(params[2].mode), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, BLEND_NB - 1, FLAGS, "mode" }, - { "c3_mode", "set component #3 blend mode", OFFSET(params[3].mode), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, BLEND_NB - 1, FLAGS, "mode" }, - { "all_mode", "set blend mode for all components", OFFSET(all_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, BLEND_NB - 1, FLAGS, "mode" }, - { "normal", "", 0, AV_OPT_TYPE_CONST, { .i64 = BLEND_NORMAL }, 0, 0, FLAGS, "mode" }, - { "multiply", "", 0, AV_OPT_TYPE_CONST, { .i64 = BLEND_MULTIPLY }, 0, 0, FLAGS, "mode" }, + { "c0_mode", "set component #0 blend mode", OFFSET(params[0].mode), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, BLEND_NB - 1, FLAGS, .unit = "mode" }, + { "c1_mode", "set component #1 blend mode", OFFSET(params[1].mode), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, BLEND_NB - 1, FLAGS, .unit = "mode" }, + { "c2_mode", "set component #2 blend mode", OFFSET(params[2].mode), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, BLEND_NB - 1, FLAGS, .unit = "mode" }, + { "c3_mode", "set component #3 blend mode", OFFSET(params[3].mode), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, BLEND_NB - 1, FLAGS, .unit = "mode" }, + { "all_mode", "set blend mode for all components", OFFSET(all_mode), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, BLEND_NB - 1, FLAGS, .unit = "mode" }, + { "normal", "", 0, AV_OPT_TYPE_CONST, { .i64 = BLEND_NORMAL }, 0, 0, FLAGS, .unit = "mode" }, + { "multiply", "", 0, AV_OPT_TYPE_CONST, { .i64 = BLEND_MULTIPLY }, 0, 0, FLAGS, .unit = "mode" }, { "c0_opacity", "set color component #0 opacity", OFFSET(params[0].opacity), AV_OPT_TYPE_DOUBLE, { .dbl = 1 }, 0, 1, FLAGS }, { "c1_opacity", "set color component #1 opacity", OFFSET(params[1].opacity), AV_OPT_TYPE_DOUBLE, { .dbl = 1 }, 0, 1, FLAGS }, @@ -500,5 +392,6 @@ const AVFilter ff_vf_blend_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &blend_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, .process_command = &process_command, }; diff --git a/libavfilter/vf_blockdetect.c b/libavfilter/vf_blockdetect.c index 27283590be1..d787aff5e45 100644 --- a/libavfilter/vf_blockdetect.c +++ b/libavfilter/vf_blockdetect.c @@ -32,6 +32,7 @@ #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "internal.h" +#include "video.h" typedef struct BLKContext { const AVClass *class; @@ -272,13 +273,6 @@ static const AVFilterPad blockdetect_inputs[] = { }, }; -static const AVFilterPad blockdetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_blockdetect = { .name = "blockdetect", .description = NULL_IF_CONFIG_SMALL("Blockdetect filter."), @@ -286,7 +280,7 @@ const AVFilter ff_vf_blockdetect = { .uninit = blockdetect_uninit, FILTER_PIXFMTS_ARRAY(pix_fmts), FILTER_INPUTS(blockdetect_inputs), - FILTER_OUTPUTS(blockdetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &blockdetect_class, .flags = AVFILTER_FLAG_METADATA_ONLY, }; diff --git a/libavfilter/vf_blurdetect.c b/libavfilter/vf_blurdetect.c index db06efcce74..354a6b6100f 100644 --- a/libavfilter/vf_blurdetect.c +++ b/libavfilter/vf_blurdetect.c @@ -35,6 +35,7 @@ #include "libavutil/qsort.h" #include "internal.h" #include "edge_common.h" +#include "video.h" static int comp(const float *a,const float *b) { @@ -356,13 +357,6 @@ static const AVFilterPad blurdetect_inputs[] = { }, }; -static const AVFilterPad blurdetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_blurdetect = { .name = "blurdetect", .description = NULL_IF_CONFIG_SMALL("Blurdetect filter."), @@ -371,7 +365,7 @@ const AVFilter ff_vf_blurdetect = { .uninit = blurdetect_uninit, FILTER_PIXFMTS_ARRAY(pix_fmts), FILTER_INPUTS(blurdetect_inputs), - FILTER_OUTPUTS(blurdetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &blurdetect_class, .flags = AVFILTER_FLAG_METADATA_ONLY, }; diff --git a/libavfilter/vf_bm3d.c b/libavfilter/vf_bm3d.c index 14f94cf5358..11d373c6431 100644 --- a/libavfilter/vf_bm3d.c +++ b/libavfilter/vf_bm3d.c @@ -38,7 +38,6 @@ #include "libavutil/tx.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "framesync.h" #include "internal.h" #include "video.h" @@ -150,11 +149,11 @@ static const AVOption bm3d_options[] = { { "hdthr", "set hard threshold for 3D transfer domain", OFFSET(hard_threshold), AV_OPT_TYPE_FLOAT, {.dbl=2.7}, 0, INT32_MAX, FLAGS }, { "estim", "set filtering estimation mode", - OFFSET(mode), AV_OPT_TYPE_INT, {.i64=BASIC}, 0, NB_MODES-1, FLAGS, "mode" }, + OFFSET(mode), AV_OPT_TYPE_INT, {.i64=BASIC}, 0, NB_MODES-1, FLAGS, .unit = "mode" }, { "basic", "basic estimate", - 0, AV_OPT_TYPE_CONST, {.i64=BASIC}, 0, 0, FLAGS, "mode" }, + 0, AV_OPT_TYPE_CONST, {.i64=BASIC}, 0, 0, FLAGS, .unit = "mode" }, { "final", "final estimate", - 0, AV_OPT_TYPE_CONST, {.i64=FINAL}, 0, 0, FLAGS, "mode" }, + 0, AV_OPT_TYPE_CONST, {.i64=FINAL}, 0, 0, FLAGS, .unit = "mode" }, { "ref", "have reference stream", OFFSET(ref), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { "planes", "set planes to filter", @@ -274,7 +273,7 @@ static void do_block_matching_multi(BM3DContext *s, const uint8_t *src, int src_ int r_y, int r_x, int plane, int jobnr) { SliceContext *sc = &s->slices[jobnr]; - double MSE2SSE = s->group_size * s->block_size * s->block_size * src_range * src_range / (s->max * s->max); + double MSE2SSE = s->group_size * s->block_size * s->block_size * src_range * src_range / (double)(s->max * s->max); double distMul = 1. / MSE2SSE; double th_sse = th_mse * MSE2SSE; int index = sc->nb_match_blocks; diff --git a/libavfilter/vf_boxblur.c b/libavfilter/vf_boxblur.c index e13c2472b37..60375463a61 100644 --- a/libavfilter/vf_boxblur.c +++ b/libavfilter/vf_boxblur.c @@ -295,13 +295,6 @@ static const AVFilterPad avfilter_vf_boxblur_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_boxblur_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_boxblur = { .name = "boxblur", .description = NULL_IF_CONFIG_SMALL("Blur the input."), @@ -309,7 +302,7 @@ const AVFilter ff_vf_boxblur = { .priv_class = &boxblur_class, .uninit = uninit, FILTER_INPUTS(avfilter_vf_boxblur_inputs), - FILTER_OUTPUTS(avfilter_vf_boxblur_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_bwdif.c b/libavfilter/vf_bwdif.c index 65c617ebb33..b8759d8243a 100644 --- a/libavfilter/vf_bwdif.c +++ b/libavfilter/vf_bwdif.c @@ -31,22 +31,16 @@ #include "libavutil/common.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" -#include "libavutil/imgutils.h" #include "avfilter.h" -#include "formats.h" +#include "bwdifdsp.h" +#include "ccfifo.h" #include "internal.h" -#include "video.h" -#include "bwdif.h" +#include "yadif.h" -/* - * Filter coefficients coef_lf and coef_hf taken from BBC PH-2071 (Weston 3 Field Deinterlacer). - * Used when there is spatial and temporal interpolation. - * Filter coefficients coef_sp are used when there is spatial interpolation only. - * Adjusted for matching visual sharpness impression of spatial and temporal interpolation. - */ -static const uint16_t coef_lf[2] = { 4309, 213 }; -static const uint16_t coef_hf[3] = { 5570, 3801, 1016 }; -static const uint16_t coef_sp[2] = { 5077, 981 }; +typedef struct BWDIFContext { + YADIFContext yadif; + BWDIFDSPContext dsp; +} BWDIFContext; typedef struct ThreadData { AVFrame *frame; @@ -56,160 +50,11 @@ typedef struct ThreadData { int tff; } ThreadData; -#define FILTER_INTRA() \ - for (x = 0; x < w; x++) { \ - interpol = (coef_sp[0] * (cur[mrefs] + cur[prefs]) - coef_sp[1] * (cur[mrefs3] + cur[prefs3])) >> 13; \ - dst[0] = av_clip(interpol, 0, clip_max); \ - \ - dst++; \ - cur++; \ - } - -#define FILTER1() \ - for (x = 0; x < w; x++) { \ - int c = cur[mrefs]; \ - int d = (prev2[0] + next2[0]) >> 1; \ - int e = cur[prefs]; \ - int temporal_diff0 = FFABS(prev2[0] - next2[0]); \ - int temporal_diff1 =(FFABS(prev[mrefs] - c) + FFABS(prev[prefs] - e)) >> 1; \ - int temporal_diff2 =(FFABS(next[mrefs] - c) + FFABS(next[prefs] - e)) >> 1; \ - int diff = FFMAX3(temporal_diff0 >> 1, temporal_diff1, temporal_diff2); \ - \ - if (!diff) { \ - dst[0] = d; \ - } else { - -#define SPAT_CHECK() \ - int b = ((prev2[mrefs2] + next2[mrefs2]) >> 1) - c; \ - int f = ((prev2[prefs2] + next2[prefs2]) >> 1) - e; \ - int dc = d - c; \ - int de = d - e; \ - int max = FFMAX3(de, dc, FFMIN(b, f)); \ - int min = FFMIN3(de, dc, FFMAX(b, f)); \ - diff = FFMAX3(diff, min, -max); - -#define FILTER_LINE() \ - SPAT_CHECK() \ - if (FFABS(c - e) > temporal_diff0) { \ - interpol = (((coef_hf[0] * (prev2[0] + next2[0]) \ - - coef_hf[1] * (prev2[mrefs2] + next2[mrefs2] + prev2[prefs2] + next2[prefs2]) \ - + coef_hf[2] * (prev2[mrefs4] + next2[mrefs4] + prev2[prefs4] + next2[prefs4])) >> 2) \ - + coef_lf[0] * (c + e) - coef_lf[1] * (cur[mrefs3] + cur[prefs3])) >> 13; \ - } else { \ - interpol = (coef_sp[0] * (c + e) - coef_sp[1] * (cur[mrefs3] + cur[prefs3])) >> 13; \ - } - -#define FILTER_EDGE() \ - if (spat) { \ - SPAT_CHECK() \ - } \ - interpol = (c + e) >> 1; - -#define FILTER2() \ - if (interpol > d + diff) \ - interpol = d + diff; \ - else if (interpol < d - diff) \ - interpol = d - diff; \ - \ - dst[0] = av_clip(interpol, 0, clip_max); \ - } \ - \ - dst++; \ - cur++; \ - prev++; \ - next++; \ - prev2++; \ - next2++; \ - } - -static void filter_intra(void *dst1, void *cur1, int w, int prefs, int mrefs, - int prefs3, int mrefs3, int parity, int clip_max) -{ - uint8_t *dst = dst1; - uint8_t *cur = cur1; - int interpol, x; - - FILTER_INTRA() -} - -static void filter_line_c(void *dst1, void *prev1, void *cur1, void *next1, - int w, int prefs, int mrefs, int prefs2, int mrefs2, - int prefs3, int mrefs3, int prefs4, int mrefs4, - int parity, int clip_max) -{ - uint8_t *dst = dst1; - uint8_t *prev = prev1; - uint8_t *cur = cur1; - uint8_t *next = next1; - uint8_t *prev2 = parity ? prev : cur ; - uint8_t *next2 = parity ? cur : next; - int interpol, x; - - FILTER1() - FILTER_LINE() - FILTER2() -} - -static void filter_edge(void *dst1, void *prev1, void *cur1, void *next1, - int w, int prefs, int mrefs, int prefs2, int mrefs2, - int parity, int clip_max, int spat) -{ - uint8_t *dst = dst1; - uint8_t *prev = prev1; - uint8_t *cur = cur1; - uint8_t *next = next1; - uint8_t *prev2 = parity ? prev : cur ; - uint8_t *next2 = parity ? cur : next; - int interpol, x; - - FILTER1() - FILTER_EDGE() - FILTER2() -} - -static void filter_intra_16bit(void *dst1, void *cur1, int w, int prefs, int mrefs, - int prefs3, int mrefs3, int parity, int clip_max) -{ - uint16_t *dst = dst1; - uint16_t *cur = cur1; - int interpol, x; - - FILTER_INTRA() -} - -static void filter_line_c_16bit(void *dst1, void *prev1, void *cur1, void *next1, - int w, int prefs, int mrefs, int prefs2, int mrefs2, - int prefs3, int mrefs3, int prefs4, int mrefs4, - int parity, int clip_max) -{ - uint16_t *dst = dst1; - uint16_t *prev = prev1; - uint16_t *cur = cur1; - uint16_t *next = next1; - uint16_t *prev2 = parity ? prev : cur ; - uint16_t *next2 = parity ? cur : next; - int interpol, x; - - FILTER1() - FILTER_LINE() - FILTER2() -} - -static void filter_edge_16bit(void *dst1, void *prev1, void *cur1, void *next1, - int w, int prefs, int mrefs, int prefs2, int mrefs2, - int parity, int clip_max, int spat) +// Round job start line down to multiple of 4 so that if filter_line3 exists +// and the frame is a multiple of 4 high then filter_line will never be called +static inline int job_start(const int jobnr, const int nb_jobs, const int h) { - uint16_t *dst = dst1; - uint16_t *prev = prev1; - uint16_t *cur = cur1; - uint16_t *next = next1; - uint16_t *prev2 = parity ? prev : cur ; - uint16_t *next2 = parity ? cur : next; - int interpol, x; - - FILTER1() - FILTER_EDGE() - FILTER2() + return jobnr >= nb_jobs ? h : ((h * jobnr) / nb_jobs) & ~3; } static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) @@ -221,8 +66,8 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) int clip_max = (1 << (yadif->csp->comp[td->plane].depth)) - 1; int df = (yadif->csp->comp[td->plane].depth + 7) / 8; int refs = linesize / df; - int slice_start = (td->h * jobnr ) / nb_jobs; - int slice_end = (td->h * (jobnr+1)) / nb_jobs; + int slice_start = job_start(jobnr, nb_jobs, td->h); + int slice_end = job_start(jobnr + 1, nb_jobs, td->h); int y; for (y = slice_start; y < slice_end; y++) { @@ -232,20 +77,25 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) uint8_t *next = &yadif->next->data[td->plane][y * linesize]; uint8_t *dst = &td->frame->data[td->plane][y * td->frame->linesize[td->plane]]; if (yadif->current_field == YADIF_FIELD_END) { - s->filter_intra(dst, cur, td->w, (y + df) < td->h ? refs : -refs, + s->dsp.filter_intra(dst, cur, td->w, (y + df) < td->h ? refs : -refs, y > (df - 1) ? -refs : refs, (y + 3*df) < td->h ? 3 * refs : -refs, y > (3*df - 1) ? -3 * refs : refs, td->parity ^ td->tff, clip_max); } else if ((y < 4) || ((y + 5) > td->h)) { - s->filter_edge(dst, prev, cur, next, td->w, + s->dsp.filter_edge(dst, prev, cur, next, td->w, (y + df) < td->h ? refs : -refs, y > (df - 1) ? -refs : refs, refs << 1, -(refs << 1), td->parity ^ td->tff, clip_max, (y < 2) || ((y + 3) > td->h) ? 0 : 1); + } else if (s->dsp.filter_line3 && y + 2 < slice_end && y + 6 < td->h) { + s->dsp.filter_line3(dst, td->frame->linesize[td->plane], + prev, cur, next, linesize, td->w, + td->parity ^ td->tff, clip_max); + y += 2; } else { - s->filter_line(dst, prev, cur, next, td->w, + s->dsp.filter_line(dst, prev, cur, next, td->w, refs, -refs, refs << 1, -(refs << 1), 3 * refs, -3 * refs, refs << 2, -(refs << 2), td->parity ^ td->tff, clip_max); @@ -280,23 +130,11 @@ static void filter(AVFilterContext *ctx, AVFrame *dstpic, td.plane = i; ff_filter_execute(ctx, filter_slice, &td, NULL, - FFMIN(h, ff_filter_get_nb_threads(ctx))); + FFMIN((h+3)/4, ff_filter_get_nb_threads(ctx))); } if (yadif->current_field == YADIF_FIELD_END) { yadif->current_field = YADIF_FIELD_NORMAL; } - - emms_c(); -} - -static av_cold void uninit(AVFilterContext *ctx) -{ - BWDIFContext *bwdif = ctx->priv; - YADIFContext *yadif = &bwdif->yadif; - - av_frame_free(&yadif->prev); - av_frame_free(&yadif->cur ); - av_frame_free(&yadif->next); } static const enum AVPixelFormat pix_fmts[] = { @@ -325,34 +163,21 @@ static int config_props(AVFilterLink *link) AVFilterContext *ctx = link->src; BWDIFContext *s = link->src->priv; YADIFContext *yadif = &s->yadif; + int ret; - link->time_base = av_mul_q(ctx->inputs[0]->time_base, (AVRational){1, 2}); - link->w = link->src->inputs[0]->w; - link->h = link->src->inputs[0]->h; - - if(yadif->mode&1) - link->frame_rate = av_mul_q(link->src->inputs[0]->frame_rate, (AVRational){2,1}); - - if (link->w < 3 || link->h < 4) { - av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or 4 lines is not supported\n"); + ret = ff_yadif_config_output_common(link); + if (ret < 0) return AVERROR(EINVAL); - } yadif->csp = av_pix_fmt_desc_get(link->format); yadif->filter = filter; - if (yadif->csp->comp[0].depth > 8) { - s->filter_intra = filter_intra_16bit; - s->filter_line = filter_line_c_16bit; - s->filter_edge = filter_edge_16bit; - } else { - s->filter_intra = filter_intra; - s->filter_line = filter_line_c; - s->filter_edge = filter_edge; + + if (AV_CEIL_RSHIFT(link->w, yadif->csp->log2_chroma_w) < 3 || AV_CEIL_RSHIFT(link->h, yadif->csp->log2_chroma_h) < 4) { + av_log(ctx, AV_LOG_ERROR, "Video with planes less than 3 columns or 4 lines is not supported\n"); + return AVERROR(EINVAL); } -#if ARCH_X86 - ff_bwdif_init_x86(s); -#endif + ff_bwdif_init_filter_line(&s->dsp, yadif->csp->comp[0].depth); return 0; } @@ -361,19 +186,19 @@ static int config_props(AVFilterLink *link) #define OFFSET(x) offsetof(YADIFContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM -#define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, unit } +#define CONST(name, help, val, u) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, .unit = u } static const AVOption bwdif_options[] = { - { "mode", "specify the interlacing mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=YADIF_MODE_SEND_FIELD}, 0, 1, FLAGS, "mode"}, + { "mode", "specify the interlacing mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=YADIF_MODE_SEND_FIELD}, 0, 1, FLAGS, .unit = "mode"}, CONST("send_frame", "send one frame for each frame", YADIF_MODE_SEND_FRAME, "mode"), CONST("send_field", "send one frame for each field", YADIF_MODE_SEND_FIELD, "mode"), - { "parity", "specify the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=YADIF_PARITY_AUTO}, -1, 1, FLAGS, "parity" }, + { "parity", "specify the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=YADIF_PARITY_AUTO}, -1, 1, FLAGS, .unit = "parity" }, CONST("tff", "assume top field first", YADIF_PARITY_TFF, "parity"), CONST("bff", "assume bottom field first", YADIF_PARITY_BFF, "parity"), CONST("auto", "auto detect parity", YADIF_PARITY_AUTO, "parity"), - { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=YADIF_DEINT_ALL}, 0, 1, FLAGS, "deint" }, + { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=YADIF_DEINT_ALL}, 0, 1, FLAGS, .unit = "deint" }, CONST("all", "deinterlace all frames", YADIF_DEINT_ALL, "deint"), CONST("interlaced", "only deinterlace frames marked as interlaced", YADIF_DEINT_INTERLACED, "deint"), @@ -404,7 +229,7 @@ const AVFilter ff_vf_bwdif = { .description = NULL_IF_CONFIG_SMALL("Deinterlace the input image."), .priv_size = sizeof(BWDIFContext), .priv_class = &bwdif_class, - .uninit = uninit, + .uninit = ff_yadif_uninit, FILTER_INPUTS(avfilter_vf_bwdif_inputs), FILTER_OUTPUTS(avfilter_vf_bwdif_outputs), FILTER_PIXFMTS_ARRAY(pix_fmts), diff --git a/libavfilter/vf_bwdif_cuda.c b/libavfilter/vf_bwdif_cuda.c new file mode 100644 index 00000000000..8c37dc8800a --- /dev/null +++ b/libavfilter/vf_bwdif_cuda.c @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2019 Philip Langdale + * + * 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/avassert.h" +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_cuda_internal.h" +#include "libavutil/cuda_check.h" +#include "internal.h" +#include "yadif.h" + +#include "cuda/load_helper.h" + +extern const unsigned char ff_vf_bwdif_cuda_ptx_data[]; +extern const unsigned int ff_vf_bwdif_cuda_ptx_len; + +typedef struct DeintCUDAContext { + YADIFContext yadif; + + AVCUDADeviceContext *hwctx; + AVBufferRef *device_ref; + AVBufferRef *input_frames_ref; + AVHWFramesContext *input_frames; + + CUmodule cu_module; + CUfunction cu_func_uchar; + CUfunction cu_func_uchar2; + CUfunction cu_func_ushort; + CUfunction cu_func_ushort2; +} DeintCUDAContext; + +#define DIV_UP(a, b) ( ((a) + (b) - 1) / (b) ) +#define ALIGN_UP(a, b) (((a) + (b) - 1) & ~((b) - 1)) +#define BLOCKX 32 +#define BLOCKY 16 + +#define CHECK_CU(x) FF_CUDA_CHECK_DL(ctx, s->hwctx->internal->cuda_dl, x) + +static CUresult call_kernel(AVFilterContext *ctx, CUfunction func, + CUdeviceptr prev, CUdeviceptr cur, CUdeviceptr next, + CUarray_format format, int channels, + int src_width, // Width is pixels per channel + int src_height, // Height is pixels per channel + int src_pitch, // Pitch is bytes + CUdeviceptr dst, + int dst_width, // Width is pixels per channel + int dst_height, // Height is pixels per channel + int dst_pitch, // Pitch is pixels per channel + int parity, int tff, int clip_max) +{ + DeintCUDAContext *s = ctx->priv; + YADIFContext *y = &s->yadif; + CudaFunctions *cu = s->hwctx->internal->cuda_dl; + CUtexObject tex_prev = 0, tex_cur = 0, tex_next = 0; + int is_field_end = y->current_field == YADIF_FIELD_END; + int ret; + + void *args[] = { &dst, &tex_prev, &tex_cur, &tex_next, + &dst_width, &dst_height, &dst_pitch, + &src_width, &src_height, &parity, &tff, + &is_field_end, &clip_max }; + + CUDA_TEXTURE_DESC tex_desc = { + .filterMode = CU_TR_FILTER_MODE_POINT, + .flags = CU_TRSF_READ_AS_INTEGER, + }; + + CUDA_RESOURCE_DESC res_desc = { + .resType = CU_RESOURCE_TYPE_PITCH2D, + .res.pitch2D.format = format, + .res.pitch2D.numChannels = channels, + .res.pitch2D.width = src_width, + .res.pitch2D.height = src_height, + .res.pitch2D.pitchInBytes = src_pitch, + }; + + res_desc.res.pitch2D.devPtr = (CUdeviceptr)prev; + ret = CHECK_CU(cu->cuTexObjectCreate(&tex_prev, &res_desc, &tex_desc, NULL)); + if (ret < 0) + goto exit; + + res_desc.res.pitch2D.devPtr = (CUdeviceptr)cur; + ret = CHECK_CU(cu->cuTexObjectCreate(&tex_cur, &res_desc, &tex_desc, NULL)); + if (ret < 0) + goto exit; + + res_desc.res.pitch2D.devPtr = (CUdeviceptr)next; + ret = CHECK_CU(cu->cuTexObjectCreate(&tex_next, &res_desc, &tex_desc, NULL)); + if (ret < 0) + goto exit; + + ret = CHECK_CU(cu->cuLaunchKernel(func, + DIV_UP(dst_width, BLOCKX), DIV_UP(dst_height, BLOCKY), 1, + BLOCKX, BLOCKY, 1, + 0, s->hwctx->stream, args, NULL)); + +exit: + if (tex_prev) + CHECK_CU(cu->cuTexObjectDestroy(tex_prev)); + if (tex_cur) + CHECK_CU(cu->cuTexObjectDestroy(tex_cur)); + if (tex_next) + CHECK_CU(cu->cuTexObjectDestroy(tex_next)); + + return ret; +} + +static void filter(AVFilterContext *ctx, AVFrame *dst, + int parity, int tff) +{ + DeintCUDAContext *s = ctx->priv; + YADIFContext *y = &s->yadif; + CudaFunctions *cu = s->hwctx->internal->cuda_dl; + CUcontext dummy; + int i, ret; + + ret = CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx)); + if (ret < 0) + return; + + for (i = 0; i < y->csp->nb_components; i++) { + CUfunction func; + CUarray_format format; + int pixel_size, channels, clip_max; + const AVComponentDescriptor *comp = &y->csp->comp[i]; + + if (comp->plane < i) { + // We process planes as a whole, so don't reprocess + // them for additional components + continue; + } + + pixel_size = (comp->depth + comp->shift) / 8; + channels = comp->step / pixel_size; + if (pixel_size > 2 || channels > 2) { + av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format: %s\n", y->csp->name); + goto exit; + } + switch (pixel_size) { + case 1: + func = channels == 1 ? s->cu_func_uchar : s->cu_func_uchar2; + format = CU_AD_FORMAT_UNSIGNED_INT8; + break; + case 2: + func = channels == 1 ? s->cu_func_ushort : s->cu_func_ushort2; + format = CU_AD_FORMAT_UNSIGNED_INT16; + break; + default: + av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format: %s\n", y->csp->name); + goto exit; + } + + clip_max = (1 << (comp->depth + comp->shift)) - 1; + + av_log(ctx, AV_LOG_TRACE, + "Deinterlacing plane %d: pixel_size: %d channels: %d\n", + comp->plane, pixel_size, channels); + call_kernel(ctx, func, + (CUdeviceptr)y->prev->data[i], + (CUdeviceptr)y->cur->data[i], + (CUdeviceptr)y->next->data[i], + format, channels, + AV_CEIL_RSHIFT(y->cur->width, i ? y->csp->log2_chroma_w : 0), + AV_CEIL_RSHIFT(y->cur->height, i ? y->csp->log2_chroma_h : 0), + y->cur->linesize[i], + (CUdeviceptr)dst->data[i], + AV_CEIL_RSHIFT(dst->width, i ? y->csp->log2_chroma_w : 0), + AV_CEIL_RSHIFT(dst->height, i ? y->csp->log2_chroma_h : 0), + dst->linesize[i] / comp->step, + parity, tff, clip_max); + } + + if (y->current_field == YADIF_FIELD_END) { + y->current_field = YADIF_FIELD_NORMAL; + } + +exit: + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + return; +} + +static av_cold void deint_cuda_uninit(AVFilterContext *ctx) +{ + CUcontext dummy; + DeintCUDAContext *s = ctx->priv; + + if (s->hwctx && s->cu_module) { + CudaFunctions *cu = s->hwctx->internal->cuda_dl; + CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx)); + CHECK_CU(cu->cuModuleUnload(s->cu_module)); + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + } + + ff_yadif_uninit(ctx); + + av_buffer_unref(&s->device_ref); + s->hwctx = NULL; + av_buffer_unref(&s->input_frames_ref); + s->input_frames = NULL; +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + DeintCUDAContext *s = ctx->priv; + + if (!inlink->hw_frames_ctx) { + av_log(ctx, AV_LOG_ERROR, "A hardware frames reference is " + "required to associate the processing device.\n"); + return AVERROR(EINVAL); + } + + s->input_frames_ref = av_buffer_ref(inlink->hw_frames_ctx); + if (!s->input_frames_ref) { + av_log(ctx, AV_LOG_ERROR, "A input frames reference create " + "failed.\n"); + return AVERROR(ENOMEM); + } + s->input_frames = (AVHWFramesContext*)s->input_frames_ref->data; + + return 0; +} + +static int config_output(AVFilterLink *link) +{ + AVHWFramesContext *output_frames; + AVFilterContext *ctx = link->src; + DeintCUDAContext *s = ctx->priv; + YADIFContext *y = &s->yadif; + CudaFunctions *cu; + int ret = 0; + CUcontext dummy; + + av_assert0(s->input_frames); + s->device_ref = av_buffer_ref(s->input_frames->device_ref); + if (!s->device_ref) { + av_log(ctx, AV_LOG_ERROR, "A device reference create " + "failed.\n"); + return AVERROR(ENOMEM); + } + s->hwctx = ((AVHWDeviceContext*)s->device_ref->data)->hwctx; + cu = s->hwctx->internal->cuda_dl; + + link->hw_frames_ctx = av_hwframe_ctx_alloc(s->device_ref); + if (!link->hw_frames_ctx) { + av_log(ctx, AV_LOG_ERROR, "Failed to create HW frame context " + "for output.\n"); + ret = AVERROR(ENOMEM); + goto exit; + } + + output_frames = (AVHWFramesContext*)link->hw_frames_ctx->data; + + output_frames->format = AV_PIX_FMT_CUDA; + output_frames->sw_format = s->input_frames->sw_format; + output_frames->width = ctx->inputs[0]->w; + output_frames->height = ctx->inputs[0]->h; + + output_frames->initial_pool_size = 4; + + ret = ff_filter_init_hw_frames(ctx, link, 10); + if (ret < 0) + goto exit; + + ret = av_hwframe_ctx_init(link->hw_frames_ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to initialise CUDA frame " + "context for output: %d\n", ret); + goto exit; + } + + ret = ff_yadif_config_output_common(link); + if (ret < 0) + goto exit; + + y->csp = av_pix_fmt_desc_get(output_frames->sw_format); + y->filter = filter; + + if (AV_CEIL_RSHIFT(link->w, y->csp->log2_chroma_w) < 3 || AV_CEIL_RSHIFT(link->h, y->csp->log2_chroma_h) < 3) { + av_log(ctx, AV_LOG_ERROR, "Video with planes less than 3 columns or lines is not supported\n"); + ret = AVERROR(EINVAL); + goto exit; + } + + ret = CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx)); + if (ret < 0) + goto exit; + + ret = ff_cuda_load_module(ctx, s->hwctx, &s->cu_module, ff_vf_bwdif_cuda_ptx_data, ff_vf_bwdif_cuda_ptx_len); + if (ret < 0) + goto exit; + + ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_uchar, s->cu_module, "bwdif_uchar")); + if (ret < 0) + goto exit; + + ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_uchar2, s->cu_module, "bwdif_uchar2")); + if (ret < 0) + goto exit; + + ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_ushort, s->cu_module, "bwdif_ushort")); + if (ret < 0) + goto exit; + + ret = CHECK_CU(cu->cuModuleGetFunction(&s->cu_func_ushort2, s->cu_module, "bwdif_ushort2")); + if (ret < 0) + goto exit; + +exit: + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + + return ret; +} + +static const AVClass bwdif_cuda_class = { + .class_name = "bwdif_cuda", + .item_name = av_default_item_name, + .option = ff_yadif_options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_FILTER, +}; + +static const AVFilterPad deint_cuda_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = ff_yadif_filter_frame, + .config_props = config_input, + }, +}; + +static const AVFilterPad deint_cuda_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = ff_yadif_request_frame, + .config_props = config_output, + }, +}; + +const AVFilter ff_vf_bwdif_cuda = { + .name = "bwdif_cuda", + .description = NULL_IF_CONFIG_SMALL("Deinterlace CUDA frames"), + .priv_size = sizeof(DeintCUDAContext), + .priv_class = &bwdif_cuda_class, + .uninit = deint_cuda_uninit, + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_CUDA), + FILTER_INPUTS(deint_cuda_inputs), + FILTER_OUTPUTS(deint_cuda_outputs), + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_bwdif_cuda.cu b/libavfilter/vf_bwdif_cuda.cu new file mode 100644 index 00000000000..3d4c29d8c39 --- /dev/null +++ b/libavfilter/vf_bwdif_cuda.cu @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2019 Philip Langdale + * + * 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 + */ + +__device__ static const int coef_lf[2] = { 4309, 213 }; +__device__ static const int coef_hf[3] = { 5570, 3801, 1016 }; +__device__ static const int coef_sp[2] = { 5077, 981 }; + +template +__inline__ __device__ T max3(T a, T b, T c) +{ + T x = max(a, b); + return max(x, c); +} + +template +__inline__ __device__ T min3(T a, T b, T c) +{ + T x = min(a, b); + return min(x, c); +} + +template +__inline__ __device__ T clip(T a, T min, T max) +{ + if (a < min) { + return min; + } else if (a > max) { + return max; + } else { + return a; + } +} + +template +__inline__ __device__ T filter_intra(T cur_prefs3, T cur_prefs, + T cur_mrefs, T cur_mrefs3, + int clip_max) +{ + int final = (coef_sp[0] * (cur_mrefs + cur_prefs) - + coef_sp[1] * (cur_mrefs3 + cur_prefs3)) >> 13; + return clip(final, 0, clip_max); +} + +template +__inline__ __device__ T filter(T cur_prefs3, T cur_prefs, T cur_mrefs, T cur_mrefs3, + T prev2_prefs4, T prev2_prefs2, T prev2_0, T prev2_mrefs2, T prev2_mrefs4, + T prev_prefs, T prev_mrefs, T next_prefs, T next_mrefs, + T next2_prefs4, T next2_prefs2, T next2_0, T next2_mrefs2, T next2_mrefs4, + int clip_max) +{ + T final; + + int c = cur_mrefs; + int d = (prev2_0 + next2_0) >> 1; + int e = cur_prefs; + + int temporal_diff0 = abs(prev2_0 - next2_0); + int temporal_diff1 = (abs(prev_mrefs - c) + abs(prev_prefs - e)) >> 1; + int temporal_diff2 = (abs(next_mrefs - c) + abs(next_prefs - e)) >> 1; + int diff = max3(temporal_diff0 >> 1, temporal_diff1, temporal_diff2); + + if (!diff) { + final = d; + } else { + int b = ((prev2_mrefs2 + next2_mrefs2) >> 1) - c; + int f = ((prev2_prefs2 + next2_prefs2) >> 1) - e; + int dc = d - c; + int de = d - e; + int mmax = max3(de, dc, min(b, f)); + int mmin = min3(de, dc, max(b, f)); + diff = max3(diff, mmin, -mmax); + + int interpol; + if (abs(c - e) > temporal_diff0) { + interpol = (((coef_hf[0] * (prev2_0 + next2_0) + - coef_hf[1] * (prev2_mrefs2 + next2_mrefs2 + prev2_prefs2 + next2_prefs2) + + coef_hf[2] * (prev2_mrefs4 + next2_mrefs4 + prev2_prefs4 + next2_mrefs4)) >> 2) + + coef_lf[0] * (c + e) - coef_lf[1] * (cur_mrefs3 + cur_prefs3)) >> 13; + } else { + interpol = (coef_sp[0] * (c + e) - coef_sp[1] * (cur_mrefs3 + cur_prefs3)) >> 13; + } + + if (interpol > d + diff) { + interpol = d + diff; + } else if (interpol < d - diff) { + interpol = d - diff; + } + final = clip(interpol, 0, clip_max); + } + + return final; +} + +template +__inline__ __device__ void bwdif_single(T *dst, + cudaTextureObject_t prev, + cudaTextureObject_t cur, + cudaTextureObject_t next, + int dst_width, int dst_height, int dst_pitch, + int src_width, int src_height, + int parity, int tff, + int is_field_end, int clip_max) +{ + // Identify location + int xo = blockIdx.x * blockDim.x + threadIdx.x; + int yo = blockIdx.y * blockDim.y + threadIdx.y; + + if (xo >= dst_width || yo >= dst_height) { + return; + } + + // Don't modify the primary field + if (yo % 2 == parity) { + dst[yo*dst_pitch+xo] = tex2D(cur, xo, yo); + return; + } + + T cur_prefs3 = tex2D(cur, xo, yo + 3); + T cur_prefs = tex2D(cur, xo, yo + 1); + T cur_mrefs = tex2D(cur, xo, yo - 1); + T cur_mrefs3 = tex2D(cur, xo, yo - 3); + + if (is_field_end) { + dst[yo*dst_pitch+xo] = + filter_intra(cur_prefs3, cur_prefs, cur_mrefs, cur_mrefs3, clip_max); + return; + } + + // Calculate temporal prediction + int is_second_field = !(parity ^ tff); + + cudaTextureObject_t prev2 = prev; + cudaTextureObject_t prev1 = is_second_field ? cur : prev; + cudaTextureObject_t next1 = is_second_field ? next : cur; + cudaTextureObject_t next2 = next; + + T prev2_prefs4 = tex2D(prev2, xo, yo + 4); + T prev2_prefs2 = tex2D(prev2, xo, yo + 2); + T prev2_0 = tex2D(prev2, xo, yo + 0); + T prev2_mrefs2 = tex2D(prev2, xo, yo - 2); + T prev2_mrefs4 = tex2D(prev2, xo, yo - 4); + T prev_prefs = tex2D(prev1, xo, yo + 1); + T prev_mrefs = tex2D(prev1, xo, yo - 1); + T next_prefs = tex2D(next1, xo, yo + 1); + T next_mrefs = tex2D(next1, xo, yo - 1); + T next2_prefs4 = tex2D(next2, xo, yo + 4); + T next2_prefs2 = tex2D(next2, xo, yo + 2); + T next2_0 = tex2D(next2, xo, yo + 0); + T next2_mrefs2 = tex2D(next2, xo, yo - 2); + T next2_mrefs4 = tex2D(next2, xo, yo - 4); + + dst[yo*dst_pitch+xo] = filter(cur_prefs3, cur_prefs, cur_mrefs, cur_mrefs3, + prev2_prefs4, prev2_prefs2, prev2_0, prev2_mrefs2, prev2_mrefs4, + prev_prefs, prev_mrefs, next_prefs, next_mrefs, + next2_prefs4, next2_prefs2, next2_0, next2_mrefs2, next2_mrefs4, + clip_max); +} + +template +__inline__ __device__ void bwdif_double(T *dst, + cudaTextureObject_t prev, + cudaTextureObject_t cur, + cudaTextureObject_t next, + int dst_width, int dst_height, int dst_pitch, + int src_width, int src_height, + int parity, int tff, + int is_field_end, int clip_max) +{ + int xo = blockIdx.x * blockDim.x + threadIdx.x; + int yo = blockIdx.y * blockDim.y + threadIdx.y; + + if (xo >= dst_width || yo >= dst_height) { + return; + } + + if (yo % 2 == parity) { + // Don't modify the primary field + dst[yo*dst_pitch+xo] = tex2D(cur, xo, yo); + return; + } + + T cur_prefs3 = tex2D(cur, xo, yo + 3); + T cur_prefs = tex2D(cur, xo, yo + 1); + T cur_mrefs = tex2D(cur, xo, yo - 1); + T cur_mrefs3 = tex2D(cur, xo, yo - 3); + + if (is_field_end) { + T final; + final.x = filter_intra(cur_prefs3.x, cur_prefs.x, cur_mrefs.x, cur_mrefs3.x, + clip_max); + final.y = filter_intra(cur_prefs3.y, cur_prefs.y, cur_mrefs.y, cur_mrefs3.y, + clip_max); + dst[yo*dst_pitch+xo] = final; + return; + } + + int is_second_field = !(parity ^ tff); + + cudaTextureObject_t prev2 = prev; + cudaTextureObject_t prev1 = is_second_field ? cur : prev; + cudaTextureObject_t next1 = is_second_field ? next : cur; + cudaTextureObject_t next2 = next; + + T prev2_prefs4 = tex2D(prev2, xo, yo + 4); + T prev2_prefs2 = tex2D(prev2, xo, yo + 2); + T prev2_0 = tex2D(prev2, xo, yo + 0); + T prev2_mrefs2 = tex2D(prev2, xo, yo - 2); + T prev2_mrefs4 = tex2D(prev2, xo, yo - 4); + T prev_prefs = tex2D(prev1, xo, yo + 1); + T prev_mrefs = tex2D(prev1, xo, yo - 1); + T next_prefs = tex2D(next1, xo, yo + 1); + T next_mrefs = tex2D(next1, xo, yo - 1); + T next2_prefs4 = tex2D(next2, xo, yo + 4); + T next2_prefs2 = tex2D(next2, xo, yo + 2); + T next2_0 = tex2D(next2, xo, yo + 0); + T next2_mrefs2 = tex2D(next2, xo, yo - 2); + T next2_mrefs4 = tex2D(next2, xo, yo - 4); + + T final; + final.x = filter(cur_prefs3.x, cur_prefs.x, cur_mrefs.x, cur_mrefs3.x, + prev2_prefs4.x, prev2_prefs2.x, prev2_0.x, prev2_mrefs2.x, prev2_mrefs4.x, + prev_prefs.x, prev_mrefs.x, next_prefs.x, next_mrefs.x, + next2_prefs4.x, next2_prefs2.x, next2_0.x, next2_mrefs2.x, next2_mrefs4.x, + clip_max); + final.y = filter(cur_prefs3.y, cur_prefs.y, cur_mrefs.y, cur_mrefs3.y, + prev2_prefs4.y, prev2_prefs2.y, prev2_0.y, prev2_mrefs2.y, prev2_mrefs4.y, + prev_prefs.y, prev_mrefs.y, next_prefs.y, next_mrefs.y, + next2_prefs4.y, next2_prefs2.y, next2_0.y, next2_mrefs2.y, next2_mrefs4.y, + clip_max); + + dst[yo*dst_pitch+xo] = final; +} + +extern "C" { + +__global__ void bwdif_uchar(unsigned char *dst, + cudaTextureObject_t prev, + cudaTextureObject_t cur, + cudaTextureObject_t next, + int dst_width, int dst_height, int dst_pitch, + int src_width, int src_height, + int parity, int tff, int is_field_end, int clip_max) +{ + bwdif_single(dst, prev, cur, next, + dst_width, dst_height, dst_pitch, + src_width, src_height, + parity, tff, is_field_end, clip_max); +} + +__global__ void bwdif_ushort(unsigned short *dst, + cudaTextureObject_t prev, + cudaTextureObject_t cur, + cudaTextureObject_t next, + int dst_width, int dst_height, int dst_pitch, + int src_width, int src_height, + int parity, int tff, int is_field_end, int clip_max) +{ + bwdif_single(dst, prev, cur, next, + dst_width, dst_height, dst_pitch, + src_width, src_height, + parity, tff, is_field_end, clip_max); +} + +__global__ void bwdif_uchar2(uchar2 *dst, + cudaTextureObject_t prev, + cudaTextureObject_t cur, + cudaTextureObject_t next, + int dst_width, int dst_height, int dst_pitch, + int src_width, int src_height, + int parity, int tff, int is_field_end, int clip_max) +{ + bwdif_double(dst, prev, cur, next, + dst_width, dst_height, dst_pitch, + src_width, src_height, + parity, tff, is_field_end, clip_max); +} + +__global__ void bwdif_ushort2(ushort2 *dst, + cudaTextureObject_t prev, + cudaTextureObject_t cur, + cudaTextureObject_t next, + int dst_width, int dst_height, int dst_pitch, + int src_width, int src_height, + int parity, int tff, int is_field_end, int clip_max) +{ + bwdif_double(dst, prev, cur, next, + dst_width, dst_height, dst_pitch, + src_width, src_height, + parity, tff, is_field_end, clip_max); +} + +} /* extern "C" */ diff --git a/libavfilter/vf_bwdif_vulkan.c b/libavfilter/vf_bwdif_vulkan.c new file mode 100644 index 00000000000..57711fb672f --- /dev/null +++ b/libavfilter/vf_bwdif_vulkan.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) Lynne + * Copyright (C) 2018 Philip Langdale + * Copyright (C) 2016 Thomas Mundt + * + * 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/random_seed.h" +#include "libavutil/opt.h" +#include "vulkan_filter.h" +#include "vulkan_spirv.h" +#include "yadif.h" +#include "internal.h" + +typedef struct BWDIFVulkanContext { + YADIFContext yadif; + FFVulkanContext vkctx; + + int initialized; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + VkSampler sampler; + FFVulkanPipeline pl; + FFVkSPIRVShader shd; +} BWDIFVulkanContext; + +typedef struct BWDIFParameters { + int parity; + int tff; + int current_field; +} BWDIFParameters; + +static const char filter_fn[] = { + "const vec4 coef_lf[2] = { vec4(4309), vec4(213), };\n" + "const vec4 coef_hf[3] = { vec4(5570), vec4(3801), vec4(1016) };\n" + "const vec4 coef_sp[2] = { vec4(5077), vec4(981), };\n" + C(0, ) + C(0, vec4 process_intra(vec4 cur[4]) ) + C(0, { ) + C(1, return (coef_sp[0]*(cur[1] + cur[2]) - coef_sp[1]*(cur[0] + cur[3])) / (1 << 13); ) + C(0, } ) + C(0, ) + C(0, vec4 process_line(vec4 prev2[5], vec4 prev1[2], vec4 cur[4], vec4 next1[2], vec4 next2[5]) ) + C(0, { ) + C(1, vec4 fc = cur[1]; ) + C(1, vec4 fe = cur[2]; ) + C(1, vec4 fs = prev2[2] + next2[2]; ) + C(1, vec4 fd = fs / 2; ) + C(0, ) + C(1, vec4 temp_diff[3]; ) + C(1, temp_diff[0] = abs(prev2[2] - next2[2]); ) + C(1, temp_diff[1] = (abs(prev1[0] - fc) + abs(prev1[1] - fe)) / 2; ) + C(1, temp_diff[1] = (abs(next1[0] - fc) + abs(next1[1] - fe)) / 2; ) + C(1, vec4 diff = max(temp_diff[0] / 2, max(temp_diff[1], temp_diff[2])); ) + C(1, bvec4 diff_mask = equal(diff, vec4(0)); ) + C(0, ) + C(1, vec4 fbs = prev2[1] + next2[1]; ) + C(1, vec4 ffs = prev2[3] + next2[3]; ) + C(1, vec4 fb = (fbs / 2) - fc; ) + C(1, vec4 ff = (ffs / 2) - fe; ) + C(1, vec4 dc = fd - fc; ) + C(1, vec4 de = fd - fe; ) + C(1, vec4 mmax = max(de, max(dc, min(fb, ff))); ) + C(1, vec4 mmin = min(de, min(dc, max(fb, ff))); ) + C(1, diff = max(diff, max(mmin, -mmax)); ) + C(0, ) +" vec4 interpolate_all = (((coef_hf[0]*(fs) - coef_hf[1]*(fbs + ffs) +\n" +" coef_hf[2]*(prev2[0] + next2[0] + prev2[4] + next2[4])) / 4) +\n" +" coef_lf[0]*(fc + fe) - coef_lf[1]*(cur[0] + cur[3])) / (1 << 13);\n" +" vec4 interpolate_cur = (coef_sp[0]*(fc + fe) - coef_sp[1]*(cur[0] + cur[3])) / (1 << 13);\n" + C(0, ) + C(1, bvec4 interpolate_cnd1 = greaterThan(abs(fc - fe), temp_diff[0]); ) + C(1, vec4 interpol = mix(interpolate_cur, interpolate_all, interpolate_cnd1); ) + C(1, interpol = clamp(interpol, fd - diff, fd + diff); ) + C(1, return mix(interpol, fd, diff_mask); ) + C(0, } ) +}; + +static av_cold int init_filter(AVFilterContext *ctx) +{ + int err; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; + BWDIFVulkanContext *s = ctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_NEAREST)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "bwdif_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + shd = &s->shd; + + ff_vk_shader_set_compute_sizes(shd, 1, 64, 1); + + desc = (FFVulkanDescriptorSetBinding []) { + { + .name = "prev", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "cur", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "next", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "dst", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 4, 0, 0)); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, int parity; ); + GLSLC(1, int tff; ); + GLSLC(1, int current_field; ); + GLSLC(0, }; ); + + ff_vk_add_push_constant(&s->pl, 0, sizeof(BWDIFParameters), + VK_SHADER_STAGE_COMPUTE_BIT); + + GLSLD( filter_fn ); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, vec4 res; ); + GLSLC(1, ivec2 size; ); + GLSLC(1, vec4 dcur[4]; ); + GLSLC(1, vec4 prev1[2]; ); + GLSLC(1, vec4 next1[2]; ); + GLSLC(1, vec4 prev2[5]; ); + GLSLC(1, vec4 next2[5]; ); + GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + GLSLC(1, bool filter_field = ((pos.y ^ parity) & 1) == 1; ); + GLSLF(1, bool is_intra = filter_field && (current_field == %i); ,YADIF_FIELD_END); + GLSLC(1, bool field_parity = (parity ^ tff) != 0; ); + GLSLC(0, ); + + for (int i = 0; i < planes; i++) { + GLSLC(0, ); + GLSLF(1, size = imageSize(dst[%i]); ,i); + GLSLC(1, if (!IS_WITHIN(pos, size)) { ); + GLSLC(2, return; ); + GLSLC(1, } else if (is_intra) { ); + GLSLF(2, dcur[0] = texture(cur[%i], pos - ivec2(0, 3)); ,i); + GLSLF(2, dcur[1] = texture(cur[%i], pos - ivec2(0, 1)); ,i); + GLSLF(2, dcur[2] = texture(cur[%i], pos + ivec2(0, 1)); ,i); + GLSLF(2, dcur[3] = texture(cur[%i], pos + ivec2(0, 3)); ,i); + GLSLC(0, ); + GLSLC(2, res = process_intra(dcur); ); + GLSLF(2, imageStore(dst[%i], pos, res); ,i); + GLSLC(1, } else if (filter_field) { ); + GLSLF(2, dcur[0] = texture(cur[%i], pos - ivec2(0, 3)); ,i); + GLSLF(2, dcur[1] = texture(cur[%i], pos - ivec2(0, 1)); ,i); + GLSLF(2, dcur[2] = texture(cur[%i], pos + ivec2(0, 1)); ,i); + GLSLF(2, dcur[3] = texture(cur[%i], pos + ivec2(0, 3)); ,i); + GLSLC(0, ); + GLSLF(2, prev1[0] = texture(prev[%i], pos - ivec2(0, 1)); ,i); + GLSLF(2, prev1[1] = texture(prev[%i], pos + ivec2(0, 1)); ,i); + GLSLC(0, ); + GLSLF(2, next1[0] = texture(next[%i], pos - ivec2(0, 1)); ,i); + GLSLF(2, next1[1] = texture(next[%i], pos + ivec2(0, 1)); ,i); + GLSLC(0, ); + GLSLC(2, if (field_parity) { ); + GLSLF(3, prev2[0] = texture(prev[%i], pos - ivec2(0, 4)); ,i); + GLSLF(3, prev2[1] = texture(prev[%i], pos - ivec2(0, 2)); ,i); + GLSLF(3, prev2[2] = texture(prev[%i], pos); ,i); + GLSLF(3, prev2[3] = texture(prev[%i], pos + ivec2(0, 2)); ,i); + GLSLF(3, prev2[4] = texture(prev[%i], pos + ivec2(0, 4)); ,i); + GLSLC(0, ); + GLSLF(3, next2[0] = texture(cur[%i], pos - ivec2(0, 4)); ,i); + GLSLF(3, next2[1] = texture(cur[%i], pos - ivec2(0, 2)); ,i); + GLSLF(3, next2[2] = texture(cur[%i], pos); ,i); + GLSLF(3, next2[3] = texture(cur[%i], pos + ivec2(0, 2)); ,i); + GLSLF(3, next2[4] = texture(cur[%i], pos + ivec2(0, 4)); ,i); + GLSLC(2, } else { ); + GLSLF(3, prev2[0] = texture(cur[%i], pos - ivec2(0, 4)); ,i); + GLSLF(3, prev2[1] = texture(cur[%i], pos - ivec2(0, 2)); ,i); + GLSLF(3, prev2[2] = texture(cur[%i], pos); ,i); + GLSLF(3, prev2[3] = texture(cur[%i], pos + ivec2(0, 2)); ,i); + GLSLF(3, prev2[4] = texture(cur[%i], pos + ivec2(0, 4)); ,i); + GLSLC(0, ); + GLSLF(3, next2[0] = texture(next[%i], pos - ivec2(0, 4)); ,i); + GLSLF(3, next2[1] = texture(next[%i], pos - ivec2(0, 2)); ,i); + GLSLF(3, next2[2] = texture(next[%i], pos); ,i); + GLSLF(3, next2[3] = texture(next[%i], pos + ivec2(0, 2)); ,i); + GLSLF(3, next2[4] = texture(next[%i], pos + ivec2(0, 4)); ,i); + GLSLC(2, } ); + GLSLC(0, ); + GLSLC(2, res = process_line(prev2, prev1, dcur, next1, next2); ); + GLSLF(2, imageStore(dst[%i], pos, res); ,i); + GLSLC(1, } else { ); + GLSLF(2, res = texture(cur[%i], pos); ,i); + GLSLF(2, imageStore(dst[%i], pos, res); ,i); + GLSLC(1, } ); + } + + GLSLC(0, } ); + + RET(spv->compile_shader(spv, ctx, &s->shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, &s->shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, &s->shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); + + s->initialized = 1; + +fail: + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); + + return err; +} + +static void bwdif_vulkan_filter_frame(AVFilterContext *ctx, AVFrame *dst, + int parity, int tff) +{ + BWDIFVulkanContext *s = ctx->priv; + YADIFContext *y = &s->yadif; + BWDIFParameters params = { + .parity = parity, + .tff = tff, + .current_field = y->current_field, + }; + + ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->pl, dst, + (AVFrame *[]){ y->prev, y->cur, y->next }, 3, + s->sampler, ¶ms, sizeof(params)); + + if (y->current_field == YADIF_FIELD_END) + y->current_field = YADIF_FIELD_NORMAL; +} + +static void bwdif_vulkan_uninit(AVFilterContext *avctx) +{ + BWDIFVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); + + ff_vk_uninit(&s->vkctx); + + ff_yadif_uninit(avctx); + + s->initialized = 0; +} + +static int bwdif_vulkan_config_input(AVFilterLink *inlink) +{ + AVHWFramesContext *input_frames; + AVFilterContext *avctx = inlink->dst; + BWDIFVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + + if (!inlink->hw_frames_ctx) { + av_log(inlink->dst, AV_LOG_ERROR, "Vulkan filtering requires a " + "hardware frames context on the input.\n"); + return AVERROR(EINVAL); + } + + input_frames = (AVHWFramesContext *)inlink->hw_frames_ctx->data; + if (input_frames->format != AV_PIX_FMT_VULKAN) + return AVERROR(EINVAL); + + /* Extract the device and default output format from the first input. */ + if (avctx->inputs[0] != inlink) + return 0; + + /* Save the ref, without reffing it */ + vkctx->input_frames_ref = inlink->hw_frames_ctx; + + /* Defaults */ + vkctx->output_format = input_frames->sw_format; + vkctx->output_width = inlink->w; + vkctx->output_height = inlink->h; + + return 0; +} + +static int bwdif_vulkan_config_output(AVFilterLink *outlink) +{ + int err; + AVFilterContext *avctx = outlink->src; + BWDIFVulkanContext *s = avctx->priv; + YADIFContext *y = &s->yadif; + FFVulkanContext *vkctx = &s->vkctx; + + av_buffer_unref(&outlink->hw_frames_ctx); + + err = ff_vk_filter_init_context(avctx, vkctx, vkctx->input_frames_ref, + vkctx->output_width, vkctx->output_height, + vkctx->output_format); + if (err < 0) + return err; + + /* For logging */ + vkctx->class = y->class; + + outlink->hw_frames_ctx = av_buffer_ref(vkctx->frames_ref); + if (!outlink->hw_frames_ctx) + return AVERROR(ENOMEM); + + err = ff_yadif_config_output_common(outlink); + if (err < 0) + return err; + + y->csp = av_pix_fmt_desc_get(vkctx->frames->sw_format); + y->filter = bwdif_vulkan_filter_frame; + + if (AV_CEIL_RSHIFT(outlink->w, y->csp->log2_chroma_w) < 4 || AV_CEIL_RSHIFT(outlink->h, y->csp->log2_chroma_h) < 4) { + av_log(avctx, AV_LOG_ERROR, "Video with planes less than 4 columns or lines is not supported\n"); + return AVERROR(EINVAL); + } + + return init_filter(avctx); +} + +static const AVClass bwdif_vulkan_class = { + .class_name = "bwdif_vulkan", + .item_name = av_default_item_name, + .option = ff_yadif_options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_FILTER, +}; + +static const AVFilterPad bwdif_vulkan_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = ff_yadif_filter_frame, + .config_props = &bwdif_vulkan_config_input, + }, +}; + +static const AVFilterPad bwdif_vulkan_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .request_frame = ff_yadif_request_frame, + .config_props = &bwdif_vulkan_config_output, + }, +}; + +const AVFilter ff_vf_bwdif_vulkan = { + .name = "bwdif_vulkan", + .description = NULL_IF_CONFIG_SMALL("Deinterlace Vulkan frames via bwdif"), + .priv_size = sizeof(BWDIFVulkanContext), + .init = &ff_vk_filter_init, + .uninit = &bwdif_vulkan_uninit, + FILTER_INPUTS(bwdif_vulkan_inputs), + FILTER_OUTPUTS(bwdif_vulkan_outputs), + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), + .priv_class = &bwdif_vulkan_class, + .flags = AVFILTER_FLAG_HWDEVICE | + AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_cas.c b/libavfilter/vf_cas.c index c45529cef7e..5fa5055d761 100644 --- a/libavfilter/vf_cas.c +++ b/libavfilter/vf_cas.c @@ -19,7 +19,6 @@ #include "libavutil/opt.h" #include "libavutil/imgutils.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -255,13 +254,6 @@ static const AVFilterPad cas_inputs[] = { }, }; -static const AVFilterPad cas_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(CASContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -279,7 +271,7 @@ const AVFilter ff_vf_cas = { .priv_size = sizeof(CASContext), .priv_class = &cas_class, FILTER_INPUTS(cas_inputs), - FILTER_OUTPUTS(cas_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_ccrepack.c b/libavfilter/vf_ccrepack.c new file mode 100644 index 00000000000..5213eab82b3 --- /dev/null +++ b/libavfilter/vf_ccrepack.c @@ -0,0 +1,97 @@ +/* + * CEA-708 Closed Caption Repacker + * Copyright (c) 2023 LTN Global Communications + * + * Author: Devin Heitmueller + * + * 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 + */ + +/* + * Repackage CEA-708 arrays, which deals with incorrect cc_count for a given + * output framerate, and incorrect 708 padding. + * + * See CEA CEA-10-A "EIA-708-B Implementation Guidance", Section 26.5 + * "Grouping DTVCC Data Within user_data() Structure" + */ + +#include "avfilter.h" +#include "internal.h" +#include "ccfifo.h" +#include "video.h" +#include "libavutil/opt.h" + +typedef struct CCRepackContext +{ + const AVClass *class; + CCFifo cc_fifo; +} CCRepackContext; + +static const AVOption ccrepack_options[] = { + { NULL } +}; + +AVFILTER_DEFINE_CLASS(ccrepack); + +static int config_input(AVFilterLink *link) +{ + CCRepackContext *ctx = link->dst->priv; + + int ret = ff_ccfifo_init(&ctx->cc_fifo, link->frame_rate, ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); + return ret; + } + + return 0; +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + CCRepackContext *ctx = inlink->dst->priv; + AVFilterLink *outlink = inlink->dst->outputs[0]; + + ff_ccfifo_extract(&ctx->cc_fifo, frame); + ff_ccfifo_inject(&ctx->cc_fifo, frame); + + return ff_filter_frame(outlink, frame); +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + CCRepackContext *s = ctx->priv; + ff_ccfifo_uninit(&s->cc_fifo); +} + +static const AVFilterPad avfilter_vf_ccrepack_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_input, + }, +}; + +const AVFilter ff_vf_ccrepack = { + .name = "ccrepack", + .description = NULL_IF_CONFIG_SMALL("Repack CEA-708 closed caption metadata"), + .uninit = uninit, + .priv_size = sizeof(CCRepackContext), + .priv_class = &ccrepack_class, + FILTER_INPUTS(avfilter_vf_ccrepack_inputs), + FILTER_OUTPUTS(ff_video_default_filterpad), +}; diff --git a/libavfilter/vf_chromaber_vulkan.c b/libavfilter/vf_chromaber_vulkan.c index b9423e417e3..0b96a7400fe 100644 --- a/libavfilter/vf_chromaber_vulkan.c +++ b/libavfilter/vf_chromaber_vulkan.c @@ -1,4 +1,6 @@ /* + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -19,21 +21,19 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" - -#define CGROUPS (int [3]){ 32, 32, 1 } +#include "video.h" typedef struct ChromaticAberrationVulkanContext { FFVulkanContext vkctx; int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl; - - /* Shader updators, must be in the main filter struct */ - VkDescriptorImageInfo input_images[3]; - VkDescriptorImageInfo output_images[3]; + FFVkSPIRVShader shd; + VkSampler sampler; /* Push constants / options */ struct { @@ -59,7 +59,7 @@ static const char distort_chroma_kernel[] = { C(0, { ) C(1, vec2 p = ((vec2(pos)/vec2(size)) - 0.5f)*2.0f; ) C(1, float d = sqrt(p.x*p.x + p.y*p.y); ) - C(1, p *= d / (d* dist); ) + C(1, p *= d / (d*dist); ) C(1, vec4 res = texture(input_img[idx], (p/2.0f) + 0.5f); ) C(1, imageStore(output_img[idx], pos, res); ) C(0, } ) @@ -68,205 +68,100 @@ static const char distort_chroma_kernel[] = { static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { int err; - FFVkSampler *sampler; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; ChromaticAberrationVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - - /* Create a sampler */ - sampler = ff_vk_init_sampler(vkctx, 0, VK_FILTER_LINEAR); - if (!sampler) - return AVERROR_EXTERNAL; - - s->pl = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl) - return AVERROR(ENOMEM); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; /* Normalize options */ s->opts.dist[0] = (s->opts.dist[0] / 100.0f) + 1.0f; s->opts.dist[1] = (s->opts.dist[1] / 100.0f) + 1.0f; - { /* Create the shader */ - FFVulkanDescriptorSetBinding desc_i[2] = { - { - .name = "input_img", - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->input_images, - .sampler = sampler, - }, - { - .name = "output_img", - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), - .mem_quali = "writeonly", - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->output_images, - }, - }; - - FFVkSPIRVShader *shd = ff_vk_init_shader(s->pl, "chromaber_compute", - VK_SHADER_STAGE_COMPUTE_BIT); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, CGROUPS); - - GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); - GLSLC(1, vec2 dist; ); - GLSLC(0, }; ); - GLSLC(0, ); - - ff_vk_add_push_constant(s->pl, 0, sizeof(s->opts), - VK_SHADER_STAGE_COMPUTE_BIT); - - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, desc_i, FF_ARRAY_ELEMS(desc_i), 0)); /* set 0 */ - - GLSLD( distort_chroma_kernel ); - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - if (planes == 1) { - GLSLC(1, distort_rgb(imageSize(output_img[0]), pos); ); - } else { - GLSLC(1, ivec2 size = imageSize(output_img[0]); ); - GLSLC(1, vec2 npos = vec2(pos)/vec2(size); ); - GLSLC(1, vec4 res = texture(input_img[0], npos); ); - GLSLC(1, imageStore(output_img[0], pos, res); ); - for (int i = 1; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_img[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - GLSLF(2, distort_chroma(%i, size, pos); ,i); - GLSLC(1, } else { ); - GLSLC(2, npos = vec2(pos)/vec2(size); ); - GLSLF(2, res = texture(input_img[%i], npos); ,i); - GLSLF(2, imageStore(output_img[%i], pos, res); ,i); - GLSLC(1, } ); - } - } - GLSLC(0, } ); + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } - RET(ff_vk_compile_shader(vkctx, shd, "main")); + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 0, VK_FILTER_LINEAR)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "chromaber_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, vec2 dist; ); + GLSLC(0, }; ); + GLSLC(0, ); + + ff_vk_add_push_constant(&s->pl, 0, sizeof(s->opts), + VK_SHADER_STAGE_COMPUTE_BIT); + + desc = (FFVulkanDescriptorSetBinding []) { + { + .name = "input_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "output_img", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 2, 0, 0)); + + GLSLD( distort_chroma_kernel ); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + if (planes == 1) { + GLSLC(1, distort_rgb(imageSize(output_img[0]), pos); ); + } else { + GLSLC(1, ivec2 size = imageSize(output_img[0]); ); + GLSLC(1, vec2 npos = vec2(pos)/vec2(size); ); + GLSLC(1, vec4 res = texture(input_img[0], npos); ); + GLSLC(1, imageStore(output_img[0], pos, res); ); + for (int i = 1; i < planes; i++) { + GLSLC(0, ); + GLSLF(1, size = imageSize(output_img[%i]); ,i); + GLSLC(1, if (!IS_WITHIN(pos, size)) ); + GLSLC(2, return; ); + GLSLF(1, distort_chroma(%i, size, pos); ,i); + } } + GLSLC(0, } ); - RET(ff_vk_init_pipeline_layout(vkctx, s->pl)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl)); + RET(spv->compile_shader(spv, ctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); - /* Execution context */ - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); s->initialized = 1; - return 0; - fail: - return err; -} + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); -static int process_frames(AVFilterContext *avctx, AVFrame *out_f, AVFrame *in_f) -{ - int err = 0; - VkCommandBuffer cmd_buf; - ChromaticAberrationVulkanContext *s = avctx->priv; - FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &vkctx->vkfn; - AVVkFrame *in = (AVVkFrame *)in_f->data[0]; - AVVkFrame *out = (AVVkFrame *)out_f->data[0]; - int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - const VkFormat *input_formats = av_vkfmt_from_pixfmt(s->vkctx.input_format); - const VkFormat *ouput_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - /* Update descriptors and init the exec context */ - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->input_images[i].imageView, in->img[i], - input_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - ouput_formats[i], - ff_comp_identity_map)); - - s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } - - ff_vk_update_descriptor_set(vkctx, s->pl, 0); - - for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier bar[2] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = in->layout[i], - .newLayout = s->input_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = in->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(bar), bar); - - in->layout[i] = bar[0].newLayout; - in->access[i] = bar[0].dstAccessMask; - - out->layout[i] = bar[1].newLayout; - out->access[i] = bar[1].dstAccessMask; - } - - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl); - - ff_vk_update_push_exec(vkctx, s->exec, VK_SHADER_STAGE_COMPUTE_BIT, - 0, sizeof(s->opts), &s->opts); - - vk->CmdDispatch(cmd_buf, - FFALIGN(s->vkctx.output_width, CGROUPS[0])/CGROUPS[0], - FFALIGN(s->vkctx.output_height, CGROUPS[1])/CGROUPS[1], 1); - - ff_vk_add_exec_dep(vkctx, s->exec, in_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, out_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(vkctx, s->exec); - if (err) - return err; - - ff_vk_qf_rotate(&s->qf); - - return err; - -fail: - ff_vk_discard_exec_deps(s->exec); return err; } @@ -286,7 +181,8 @@ static int chromaber_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) if (!s->initialized) RET(init_filter(ctx, in)); - RET(process_frames(ctx, out, in)); + RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, out, in, + s->sampler, &s->opts, sizeof(s->opts))); err = av_frame_copy_props(out, in); if (err < 0) @@ -305,6 +201,16 @@ static int chromaber_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) static void chromaber_vulkan_uninit(AVFilterContext *avctx) { ChromaticAberrationVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); ff_vk_uninit(&s->vkctx); @@ -349,4 +255,5 @@ const AVFilter ff_vf_chromaber_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &chromaber_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_chromakey.c b/libavfilter/vf_chromakey.c index fdd610cb19a..9c0918bb4d2 100644 --- a/libavfilter/vf_chromakey.c +++ b/libavfilter/vf_chromakey.c @@ -19,12 +19,10 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" typedef struct ChromakeyContext { const AVClass *class; @@ -325,7 +323,7 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar return config_output(ctx->outputs[0]); } -static const AVFilterPad chromakey_inputs[] = { +static const AVFilterPad inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, @@ -335,7 +333,7 @@ static const AVFilterPad chromakey_inputs[] = { }, }; -static const AVFilterPad chromakey_outputs[] = { +static const AVFilterPad outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, @@ -372,8 +370,8 @@ const AVFilter ff_vf_chromakey = { .description = NULL_IF_CONFIG_SMALL("Turns a certain color into transparency. Operates on YUV colors."), .priv_size = sizeof(ChromakeyContext), .priv_class = &chromakey_class, - FILTER_INPUTS(chromakey_inputs), - FILTER_OUTPUTS(chromakey_outputs), + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), FILTER_PIXFMTS_ARRAY(chromakey_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, @@ -387,24 +385,6 @@ static const AVOption chromahold_options[] = { { NULL } }; -static const AVFilterPad chromahold_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE, - .filter_frame = filter_frame, - .config_props = config_input, - }, -}; - -static const AVFilterPad chromahold_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_output, - }, -}; - static const enum AVPixelFormat hold_pixel_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, @@ -431,8 +411,8 @@ const AVFilter ff_vf_chromahold = { .description = NULL_IF_CONFIG_SMALL("Turns a certain color range into gray."), .priv_size = sizeof(ChromakeyContext), .priv_class = &chromahold_class, - FILTER_INPUTS(chromahold_inputs), - FILTER_OUTPUTS(chromahold_outputs), + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), FILTER_PIXFMTS_ARRAY(hold_pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_chromakey_cuda.c b/libavfilter/vf_chromakey_cuda.c index 113484279ce..ac644caea7f 100644 --- a/libavfilter/vf_chromakey_cuda.c +++ b/libavfilter/vf_chromakey_cuda.c @@ -20,9 +20,7 @@ #include #include -#include -#include "libavutil/avstring.h" #include "libavutil/common.h" #include "libavutil/hwcontext.h" #include "libavutil/hwcontext_cuda_internal.h" @@ -32,9 +30,7 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" #include "cuda/load_helper.h" static const enum AVPixelFormat supported_formats[] = { diff --git a/libavfilter/vf_chromanr.c b/libavfilter/vf_chromanr.c index 36c29ed8cfc..ff773133117 100644 --- a/libavfilter/vf_chromanr.c +++ b/libavfilter/vf_chromanr.c @@ -18,13 +18,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/avstring.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -160,7 +158,7 @@ static int distance ## _slice##name(AVFilterContext *ctx, void *arg, su += U; \ sv += V; \ cn++; \ - } else if (fun(cyY, cuU, cvV) < thres) { \ + } else if (!extra && fun(cyY, cuU, cvV) < thres) { \ su += U; \ sv += V; \ cn++; \ @@ -212,7 +210,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->thres_u = s->threshold_u * (1 << (s->depth - 8)); s->thres_v = s->threshold_v * (1 << (s->depth - 8)); - if (s->thres_y < 200.f || s->thres_u < 200.f || s->thres_v < 200.f) { + if (s->threshold_y < 200.f || s->threshold_u < 200.f || s->threshold_v < 200.f) { switch (s->distance) { case 0: s->filter_slice = s->depth <= 8 ? manhattan_e_slice8 : manhattan_e_slice16; @@ -274,9 +272,9 @@ static const AVOption chromanr_options[] = { { "threy", "set y threshold", OFFSET(threshold_y), AV_OPT_TYPE_FLOAT, {.dbl=200},1, 200, VF }, { "threu", "set u threshold", OFFSET(threshold_u), AV_OPT_TYPE_FLOAT, {.dbl=200},1, 200, VF }, { "threv", "set v threshold", OFFSET(threshold_v), AV_OPT_TYPE_FLOAT, {.dbl=200},1, 200, VF }, - { "distance", "set distance type", OFFSET(distance), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, VF, "distance" }, - { "manhattan", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VF, "distance" }, - { "euclidean", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, VF, "distance" }, + { "distance", "set distance type", OFFSET(distance), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, VF, .unit = "distance" }, + { "manhattan", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VF, .unit = "distance" }, + { "euclidean", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, VF, .unit = "distance" }, { NULL } }; @@ -289,13 +287,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - AVFILTER_DEFINE_CLASS(chromanr); const AVFilter ff_vf_chromanr = { @@ -303,7 +294,7 @@ const AVFilter ff_vf_chromanr = { .description = NULL_IF_CONFIG_SMALL("Reduce chrominance noise."), .priv_size = sizeof(ChromaNRContext), .priv_class = &chromanr_class, - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_INPUTS(inputs), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_chromashift.c b/libavfilter/vf_chromashift.c index 2ddfa617263..6c929472a79 100644 --- a/libavfilter/vf_chromashift.c +++ b/libavfilter/vf_chromashift.c @@ -18,15 +18,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/avstring.h" -#include "libavutil/eval.h" #include "libavutil/imgutils.h" -#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -367,9 +363,9 @@ static const AVOption chromashift_options[] = { { "cbv", "shift chroma-blue vertically", OFFSET(cbv), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VFR }, { "crh", "shift chroma-red horizontally", OFFSET(crh), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VFR }, { "crv", "shift chroma-red vertically", OFFSET(crv), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VFR }, - { "edge", "set edge operation", OFFSET(edge), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags = VFR, "edge" }, - { "smear", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = VFR, "edge" }, - { "wrap", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = VFR, "edge" }, + { "edge", "set edge operation", OFFSET(edge), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags = VFR, .unit = "edge" }, + { "smear", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = VFR, .unit = "edge" }, + { "wrap", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = VFR, .unit = "edge" }, { NULL }, }; @@ -382,13 +378,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const enum AVPixelFormat yuv_pix_fmts[] = { AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUVJ422P,AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ411P, @@ -411,7 +400,7 @@ const AVFilter ff_vf_chromashift = { .description = NULL_IF_CONFIG_SMALL("Shift chroma."), .priv_size = sizeof(ChromaShiftContext), .priv_class = &chromashift_class, - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_INPUTS(inputs), FILTER_PIXFMTS_ARRAY(yuv_pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, @@ -435,9 +424,9 @@ static const AVOption rgbashift_options[] = { { "bv", "shift blue vertically", OFFSET(bv), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VFR }, { "ah", "shift alpha horizontally", OFFSET(ah), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VFR }, { "av", "shift alpha vertically", OFFSET(av), AV_OPT_TYPE_INT, {.i64=0}, -255, 255, .flags = VFR }, - { "edge", "set edge operation", OFFSET(edge), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags = VFR, "edge" }, - { "smear", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = VFR, "edge" }, - { "wrap", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = VFR, "edge" }, + { "edge", "set edge operation", OFFSET(edge), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags = VFR, .unit = "edge" }, + { "smear", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = VFR, .unit = "edge" }, + { "wrap", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = VFR, .unit = "edge" }, { NULL }, }; @@ -448,7 +437,7 @@ const AVFilter ff_vf_rgbashift = { .description = NULL_IF_CONFIG_SMALL("Shift RGBA."), .priv_size = sizeof(ChromaShiftContext), .priv_class = &rgbashift_class, - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_INPUTS(inputs), FILTER_PIXFMTS_ARRAY(rgb_pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_ciescope.c b/libavfilter/vf_ciescope.c index eebb3af2812..d5f33a530e8 100644 --- a/libavfilter/vf_ciescope.c +++ b/libavfilter/vf_ciescope.c @@ -77,40 +77,40 @@ typedef struct CiescopeContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption ciescope_options[] = { - { "system", "set color system", OFFSET(color_system), AV_OPT_TYPE_INT, {.i64=Rec709system}, 0, NB_CS-1, FLAGS, "system" }, - { "ntsc", "NTSC 1953 Y'I'O' (ITU-R BT.470 System M)", 0, AV_OPT_TYPE_CONST, {.i64=NTSCsystem}, 0, 0, FLAGS, "system" }, - { "470m", "NTSC 1953 Y'I'O' (ITU-R BT.470 System M)", 0, AV_OPT_TYPE_CONST, {.i64=NTSCsystem}, 0, 0, FLAGS, "system" }, - { "ebu", "EBU Y'U'V' (PAL/SECAM) (ITU-R BT.470 System B, G)", 0, AV_OPT_TYPE_CONST, {.i64=EBUsystem}, 0, 0, FLAGS, "system" }, - { "470bg", "EBU Y'U'V' (PAL/SECAM) (ITU-R BT.470 System B, G)", 0, AV_OPT_TYPE_CONST, {.i64=EBUsystem}, 0, 0, FLAGS, "system" }, - { "smpte", "SMPTE-C RGB", 0, AV_OPT_TYPE_CONST, {.i64=SMPTEsystem}, 0, 0, FLAGS, "system" }, - { "240m", "SMPTE-240M Y'PbPr", 0, AV_OPT_TYPE_CONST, {.i64=SMPTE240Msystem},0, 0, FLAGS, "system" }, - { "apple", "Apple RGB", 0, AV_OPT_TYPE_CONST, {.i64=APPLEsystem}, 0, 0, FLAGS, "system" }, - { "widergb", "Adobe Wide Gamut RGB", 0, AV_OPT_TYPE_CONST, {.i64=wRGBsystem}, 0, 0, FLAGS, "system" }, - { "cie1931", "CIE 1931 RGB", 0, AV_OPT_TYPE_CONST, {.i64=CIE1931system}, 0, 0, FLAGS, "system" }, - { "hdtv", "ITU.BT-709 Y'CbCr", 0, AV_OPT_TYPE_CONST, {.i64=Rec709system}, 0, 0, FLAGS, "system" }, - { "rec709", "ITU.BT-709 Y'CbCr", 0, AV_OPT_TYPE_CONST, {.i64=Rec709system}, 0, 0, FLAGS, "system" }, - { "uhdtv", "ITU-R.BT-2020", 0, AV_OPT_TYPE_CONST, {.i64=Rec2020system}, 0, 0, FLAGS, "system" }, - { "rec2020", "ITU-R.BT-2020", 0, AV_OPT_TYPE_CONST, {.i64=Rec2020system}, 0, 0, FLAGS, "system" }, - { "dcip3", "DCI-P3", 0, AV_OPT_TYPE_CONST, {.i64=DCIP3}, 0, 0, FLAGS, "system" }, - { "cie", "set cie system", OFFSET(cie), AV_OPT_TYPE_INT, {.i64=XYY}, 0, NB_CIE-1, FLAGS, "cie" }, - { "xyy", "CIE 1931 xyY", 0, AV_OPT_TYPE_CONST, {.i64=XYY}, 0, 0, FLAGS, "cie" }, - { "ucs", "CIE 1960 UCS", 0, AV_OPT_TYPE_CONST, {.i64=UCS}, 0, 0, FLAGS, "cie" }, - { "luv", "CIE 1976 Luv", 0, AV_OPT_TYPE_CONST, {.i64=LUV}, 0, 0, FLAGS, "cie" }, - { "gamuts", "set what gamuts to draw", OFFSET(gamuts), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 0xFFF, FLAGS, "gamuts" }, - { "ntsc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1<height, s->vsub); uint8_t *pu = frame->data[1]; uint8_t *pv = frame->data[2]; - const int lzu = frame->linesize[1]; - const int lzv = frame->linesize[2]; + const ptrdiff_t lzu = frame->linesize[1]; + const ptrdiff_t lzv = frame->linesize[2]; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { @@ -262,7 +260,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_VIDEO_ENC_PARAMS); if (sd) { AVVideoEncParams *par = (AVVideoEncParams*)sd->data; - const int stride = frame->linesize[0]; + const ptrdiff_t stride = frame->linesize[0]; if (par->nb_blocks) { for (int block_idx = 0; block_idx < par->nb_blocks; block_idx++) { @@ -334,19 +332,12 @@ static const AVFilterPad codecview_inputs[] = { }, }; -static const AVFilterPad codecview_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_codecview = { .name = "codecview", .description = NULL_IF_CONFIG_SMALL("Visualize information about some codecs."), .priv_size = sizeof(CodecViewContext), FILTER_INPUTS(codecview_inputs), - FILTER_OUTPUTS(codecview_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), // TODO: we can probably add way more pixel formats without any other // changes; anything with 8-bit luma in first plane should be working FILTER_SINGLE_PIXFMT(AV_PIX_FMT_YUV420P), diff --git a/libavfilter/vf_colorbalance.c b/libavfilter/vf_colorbalance.c index 0e626681b60..676e74c7705 100644 --- a/libavfilter/vf_colorbalance.c +++ b/libavfilter/vf_colorbalance.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_colorchannelmixer.c b/libavfilter/vf_colorchannelmixer.c index 88dd7451dcf..8402b578581 100644 --- a/libavfilter/vf_colorchannelmixer.c +++ b/libavfilter/vf_colorchannelmixer.c @@ -24,7 +24,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "preserve_color.h" @@ -101,14 +100,14 @@ static const AVOption colorchannelmixer_options[] = { { "ag", "set the green gain for the alpha channel", OFFSET(ag), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, { "ab", "set the blue gain for the alpha channel", OFFSET(ab), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -2, 2, FLAGS }, { "aa", "set the alpha gain for the alpha channel", OFFSET(aa), AV_OPT_TYPE_DOUBLE, {.dbl=1}, -2, 2, FLAGS }, - { "pc", "set the preserve color mode", OFFSET(preserve_color), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_PRESERVE-1, FLAGS, "preserve" }, - { "none", "disabled", 0, AV_OPT_TYPE_CONST, {.i64=P_NONE}, 0, 0, FLAGS, "preserve" }, - { "lum", "luminance", 0, AV_OPT_TYPE_CONST, {.i64=P_LUM}, 0, 0, FLAGS, "preserve" }, - { "max", "max", 0, AV_OPT_TYPE_CONST, {.i64=P_MAX}, 0, 0, FLAGS, "preserve" }, - { "avg", "average", 0, AV_OPT_TYPE_CONST, {.i64=P_AVG}, 0, 0, FLAGS, "preserve" }, - { "sum", "sum", 0, AV_OPT_TYPE_CONST, {.i64=P_SUM}, 0, 0, FLAGS, "preserve" }, - { "nrm", "norm", 0, AV_OPT_TYPE_CONST, {.i64=P_NRM}, 0, 0, FLAGS, "preserve" }, - { "pwr", "power", 0, AV_OPT_TYPE_CONST, {.i64=P_PWR}, 0, 0, FLAGS, "preserve" }, + { "pc", "set the preserve color mode", OFFSET(preserve_color), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_PRESERVE-1, FLAGS, .unit = "preserve" }, + { "none", "disabled", 0, AV_OPT_TYPE_CONST, {.i64=P_NONE}, 0, 0, FLAGS, .unit = "preserve" }, + { "lum", "luminance", 0, AV_OPT_TYPE_CONST, {.i64=P_LUM}, 0, 0, FLAGS, .unit = "preserve" }, + { "max", "max", 0, AV_OPT_TYPE_CONST, {.i64=P_MAX}, 0, 0, FLAGS, .unit = "preserve" }, + { "avg", "average", 0, AV_OPT_TYPE_CONST, {.i64=P_AVG}, 0, 0, FLAGS, .unit = "preserve" }, + { "sum", "sum", 0, AV_OPT_TYPE_CONST, {.i64=P_SUM}, 0, 0, FLAGS, .unit = "preserve" }, + { "nrm", "norm", 0, AV_OPT_TYPE_CONST, {.i64=P_NRM}, 0, 0, FLAGS, .unit = "preserve" }, + { "pwr", "power", 0, AV_OPT_TYPE_CONST, {.i64=P_PWR}, 0, 0, FLAGS, .unit = "preserve" }, { "pa", "set the preserve color amount", OFFSET(preserve_amount), AV_OPT_TYPE_DOUBLE, {.dbl=0}, 0, 1, FLAGS }, { NULL } }; diff --git a/libavfilter/vf_colorconstancy.c b/libavfilter/vf_colorconstancy.c index db7e20df530..3d4d433cc19 100644 --- a/libavfilter/vf_colorconstancy.c +++ b/libavfilter/vf_colorconstancy.c @@ -28,14 +28,10 @@ * J. van de Weijer, Th. Gevers, A. Gijsenij "Edge-Based Color Constancy". */ -#include "config_components.h" - #include "libavutil/imgutils.h" #include "libavutil/opt.h" -#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -719,15 +715,6 @@ static const AVFilterPad colorconstancy_inputs[] = { }, }; -static const AVFilterPad colorconstancy_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -#if CONFIG_GREYEDGE_FILTER - static const AVOption greyedge_options[] = { { "difford", "set differentiation order", OFFSET(difford), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, FLAGS }, { "minknorm", "set Minkowski norm", OFFSET(minknorm), AV_OPT_TYPE_INT, {.i64=1}, 0, 20, FLAGS }, @@ -744,11 +731,9 @@ const AVFilter ff_vf_greyedge = { .priv_class = &greyedge_class, .uninit = uninit, FILTER_INPUTS(colorconstancy_inputs), - FILTER_OUTPUTS(colorconstancy_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), // TODO: support more formats // FIXME: error when saving to .jpg FILTER_SINGLE_PIXFMT(AV_PIX_FMT_GBRP), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, }; - -#endif /* CONFIG_GREY_EDGE_FILTER */ diff --git a/libavfilter/vf_colorcontrast.c b/libavfilter/vf_colorcontrast.c index 7561d21259e..b086de71e5c 100644 --- a/libavfilter/vf_colorcontrast.c +++ b/libavfilter/vf_colorcontrast.c @@ -21,10 +21,9 @@ #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -97,9 +96,9 @@ static int colorcontrast_slice8(AVFilterContext *ctx, void *arg, int jobnr, int const int height = frame->height; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int glinesize = frame->linesize[0]; - const int blinesize = frame->linesize[1]; - const int rlinesize = frame->linesize[2]; + const ptrdiff_t glinesize = frame->linesize[0]; + const ptrdiff_t blinesize = frame->linesize[1]; + const ptrdiff_t rlinesize = frame->linesize[2]; uint8_t *gptr = frame->data[0] + slice_start * glinesize; uint8_t *bptr = frame->data[1] + slice_start * blinesize; uint8_t *rptr = frame->data[2] + slice_start * rlinesize; @@ -151,9 +150,9 @@ static int colorcontrast_slice16(AVFilterContext *ctx, void *arg, int jobnr, int const int height = frame->height; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int glinesize = frame->linesize[0] / 2; - const int blinesize = frame->linesize[1] / 2; - const int rlinesize = frame->linesize[2] / 2; + const ptrdiff_t glinesize = frame->linesize[0] / 2; + const ptrdiff_t blinesize = frame->linesize[1] / 2; + const ptrdiff_t rlinesize = frame->linesize[2] / 2; uint16_t *gptr = (uint16_t *)frame->data[0] + slice_start * glinesize; uint16_t *bptr = (uint16_t *)frame->data[1] + slice_start * blinesize; uint16_t *rptr = (uint16_t *)frame->data[2] + slice_start * rlinesize; @@ -204,7 +203,7 @@ static int colorcontrast_slice8p(AVFilterContext *ctx, void *arg, int jobnr, int const int height = frame->height; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int linesize = frame->linesize[0]; + const ptrdiff_t linesize = frame->linesize[0]; const uint8_t roffset = s->rgba_map[R]; const uint8_t goffset = s->rgba_map[G]; const uint8_t boffset = s->rgba_map[B]; @@ -256,7 +255,7 @@ static int colorcontrast_slice16p(AVFilterContext *ctx, void *arg, int jobnr, in const int height = frame->height; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int linesize = frame->linesize[0] / 2; + const ptrdiff_t linesize = frame->linesize[0] / 2; const uint8_t roffset = s->rgba_map[R]; const uint8_t goffset = s->rgba_map[G]; const uint8_t boffset = s->rgba_map[B]; @@ -359,13 +358,6 @@ static const AVFilterPad colorcontrast_inputs[] = { }, }; -static const AVFilterPad colorcontrast_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(ColorContrastContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -388,7 +380,7 @@ const AVFilter ff_vf_colorcontrast = { .priv_size = sizeof(ColorContrastContext), .priv_class = &colorcontrast_class, FILTER_INPUTS(colorcontrast_inputs), - FILTER_OUTPUTS(colorcontrast_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_colorcorrect.c b/libavfilter/vf_colorcorrect.c index ee97b62b0e2..3433b5c4c3f 100644 --- a/libavfilter/vf_colorcorrect.c +++ b/libavfilter/vf_colorcorrect.c @@ -21,9 +21,8 @@ #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -70,8 +69,8 @@ static int average_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_job const int height = s->planeheight[1]; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ulinesize = frame->linesize[1]; - const int vlinesize = frame->linesize[2]; + const ptrdiff_t ulinesize = frame->linesize[1]; + const ptrdiff_t vlinesize = frame->linesize[2]; const uint8_t *uptr = (const uint8_t *)frame->data[1] + slice_start * ulinesize; const uint8_t *vptr = (const uint8_t *)frame->data[2] + slice_start * vlinesize; int sum_u = 0, sum_v = 0; @@ -101,8 +100,8 @@ static int average_slice16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jo const int height = s->planeheight[1]; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ulinesize = frame->linesize[1] / 2; - const int vlinesize = frame->linesize[2] / 2; + const ptrdiff_t ulinesize = frame->linesize[1] / 2; + const ptrdiff_t vlinesize = frame->linesize[2] / 2; const uint16_t *uptr = (const uint16_t *)frame->data[1] + slice_start * ulinesize; const uint16_t *vptr = (const uint16_t *)frame->data[2] + slice_start * vlinesize; int64_t sum_u = 0, sum_v = 0; @@ -132,8 +131,8 @@ static int minmax_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs const int height = s->planeheight[1]; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ulinesize = frame->linesize[1]; - const int vlinesize = frame->linesize[2]; + const ptrdiff_t ulinesize = frame->linesize[1]; + const ptrdiff_t vlinesize = frame->linesize[2]; const uint8_t *uptr = (const uint8_t *)frame->data[1] + slice_start * ulinesize; const uint8_t *vptr = (const uint8_t *)frame->data[2] + slice_start * vlinesize; int min_u = 255, min_v = 255; @@ -168,8 +167,8 @@ static int minmax_slice16(AVFilterContext *ctx, void *arg, int jobnr, int nb_job const int height = s->planeheight[1]; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ulinesize = frame->linesize[1] / 2; - const int vlinesize = frame->linesize[2] / 2; + const ptrdiff_t ulinesize = frame->linesize[1] / 2; + const ptrdiff_t vlinesize = frame->linesize[2] / 2; const uint16_t *uptr = (const uint16_t *)frame->data[1] + slice_start * ulinesize; const uint16_t *vptr = (const uint16_t *)frame->data[2] + slice_start * vlinesize; int min_u = INT_MAX, min_v = INT_MAX; @@ -202,8 +201,8 @@ static int median_8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const float imax = s->imax; const int width = s->planewidth[1]; const int height = s->planeheight[1]; - const int ulinesize = frame->linesize[1]; - const int vlinesize = frame->linesize[2]; + const ptrdiff_t ulinesize = frame->linesize[1]; + const ptrdiff_t vlinesize = frame->linesize[2]; const uint8_t *uptr = (const uint8_t *)frame->data[1]; const uint8_t *vptr = (const uint8_t *)frame->data[2]; unsigned *uhistogram = s->uhistogram; @@ -256,8 +255,8 @@ static int median_16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const float imax = s->imax; const int width = s->planewidth[1]; const int height = s->planeheight[1]; - const int ulinesize = frame->linesize[1] / 2; - const int vlinesize = frame->linesize[2] / 2; + const ptrdiff_t ulinesize = frame->linesize[1] / 2; + const ptrdiff_t vlinesize = frame->linesize[2] / 2; const uint16_t *uptr = (const uint16_t *)frame->data[1]; const uint16_t *vptr = (const uint16_t *)frame->data[2]; unsigned *uhistogram = s->uhistogram; @@ -324,9 +323,9 @@ static int colorcorrect_slice8(AVFilterContext *ctx, void *arg, int jobnr, int n const int height = s->planeheight[1]; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ylinesize = frame->linesize[0]; - const int ulinesize = frame->linesize[1]; - const int vlinesize = frame->linesize[2]; + const ptrdiff_t ylinesize = frame->linesize[0]; + const ptrdiff_t ulinesize = frame->linesize[1]; + const ptrdiff_t vlinesize = frame->linesize[2]; uint8_t *yptr = frame->data[0] + slice_start * chroma_h * ylinesize; uint8_t *uptr = frame->data[1] + slice_start * ulinesize; uint8_t *vptr = frame->data[2] + slice_start * vlinesize; @@ -365,9 +364,9 @@ static int colorcorrect_slice16(AVFilterContext *ctx, void *arg, int jobnr, int const int height = s->planeheight[1]; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ylinesize = frame->linesize[0] / 2; - const int ulinesize = frame->linesize[1] / 2; - const int vlinesize = frame->linesize[2] / 2; + const ptrdiff_t ylinesize = frame->linesize[0] / 2; + const ptrdiff_t ulinesize = frame->linesize[1] / 2; + const ptrdiff_t vlinesize = frame->linesize[2] / 2; uint16_t *yptr = (uint16_t *)frame->data[0] + slice_start * chroma_h * ylinesize; uint16_t *uptr = (uint16_t *)frame->data[1] + slice_start * ulinesize; uint16_t *vptr = (uint16_t *)frame->data[2] + slice_start * vlinesize; @@ -498,6 +497,8 @@ static av_cold void uninit(AVFilterContext *ctx) ColorCorrectContext *s = ctx->priv; av_freep(&s->analyzeret); + av_freep(&s->uhistogram); + av_freep(&s->vhistogram); } static const AVFilterPad colorcorrect_inputs[] = { @@ -510,13 +511,6 @@ static const AVFilterPad colorcorrect_inputs[] = { }, }; -static const AVFilterPad colorcorrect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(ColorCorrectContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -526,11 +520,11 @@ static const AVOption colorcorrect_options[] = { { "rh", "set the red highlight spot", OFFSET(rh), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, VF }, { "bh", "set the blue highlight spot", OFFSET(bh), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, VF }, { "saturation", "set the amount of saturation", OFFSET(saturation), AV_OPT_TYPE_FLOAT, {.dbl=1}, -3, 3, VF }, - { "analyze", "set the analyze mode", OFFSET(analyze), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_ANALYZE-1, VF, "analyze" }, - { "manual", "manually set options", 0, AV_OPT_TYPE_CONST, {.i64=MANUAL}, 0, 0, VF, "analyze" }, - { "average", "use average pixels", 0, AV_OPT_TYPE_CONST, {.i64=AVERAGE}, 0, 0, VF, "analyze" }, - { "minmax", "use minmax pixels", 0, AV_OPT_TYPE_CONST, {.i64=MINMAX}, 0, 0, VF, "analyze" }, - { "median", "use median pixels", 0, AV_OPT_TYPE_CONST, {.i64=MEDIAN}, 0, 0, VF, "analyze" }, + { "analyze", "set the analyze mode", OFFSET(analyze), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_ANALYZE-1, VF, .unit = "analyze" }, + { "manual", "manually set options", 0, AV_OPT_TYPE_CONST, {.i64=MANUAL}, 0, 0, VF, .unit = "analyze" }, + { "average", "use average pixels", 0, AV_OPT_TYPE_CONST, {.i64=AVERAGE}, 0, 0, VF, .unit = "analyze" }, + { "minmax", "use minmax pixels", 0, AV_OPT_TYPE_CONST, {.i64=MINMAX}, 0, 0, VF, .unit = "analyze" }, + { "median", "use median pixels", 0, AV_OPT_TYPE_CONST, {.i64=MEDIAN}, 0, 0, VF, .unit = "analyze" }, { NULL } }; @@ -543,7 +537,7 @@ const AVFilter ff_vf_colorcorrect = { .priv_class = &colorcorrect_class, .uninit = uninit, FILTER_INPUTS(colorcorrect_inputs), - FILTER_OUTPUTS(colorcorrect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_colorize.c b/libavfilter/vf_colorize.c index ba7b80dae77..e6c563e3e2e 100644 --- a/libavfilter/vf_colorize.c +++ b/libavfilter/vf_colorize.c @@ -17,9 +17,8 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -53,7 +52,7 @@ static int colorizey_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_j const int height = s->planeheight[0]; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ylinesize = frame->linesize[0]; + const ptrdiff_t ylinesize = frame->linesize[0]; uint8_t *yptr = frame->data[0] + slice_start * ylinesize; const int yv = s->c[0]; const float mix = s->mix; @@ -76,7 +75,7 @@ static int colorizey_slice16(AVFilterContext *ctx, void *arg, int jobnr, int nb_ const int height = s->planeheight[0]; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ylinesize = frame->linesize[0] / 2; + const ptrdiff_t ylinesize = frame->linesize[0] / 2; uint16_t *yptr = (uint16_t *)frame->data[0] + slice_start * ylinesize; const int yv = s->c[0]; const float mix = s->mix; @@ -99,8 +98,8 @@ static int colorize_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jo const int height = s->planeheight[1]; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ulinesize = frame->linesize[1]; - const int vlinesize = frame->linesize[2]; + const ptrdiff_t ulinesize = frame->linesize[1]; + const ptrdiff_t vlinesize = frame->linesize[2]; uint8_t *uptr = frame->data[1] + slice_start * ulinesize; uint8_t *vptr = frame->data[2] + slice_start * vlinesize; const int u = s->c[1]; @@ -127,8 +126,8 @@ static int colorize_slice16(AVFilterContext *ctx, void *arg, int jobnr, int nb_j const int height = s->planeheight[1]; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ulinesize = frame->linesize[1] / 2; - const int vlinesize = frame->linesize[2] / 2; + const ptrdiff_t ulinesize = frame->linesize[1] / 2; + const ptrdiff_t vlinesize = frame->linesize[2] / 2; uint16_t *uptr = (uint16_t *)frame->data[1] + slice_start * ulinesize; uint16_t *vptr = (uint16_t *)frame->data[2] + slice_start * vlinesize; const int u = s->c[1]; @@ -260,13 +259,6 @@ static const AVFilterPad colorize_inputs[] = { }, }; -static const AVFilterPad colorize_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(ColorizeContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -286,7 +278,7 @@ const AVFilter ff_vf_colorize = { .priv_size = sizeof(ColorizeContext), .priv_class = &colorize_class, FILTER_INPUTS(colorize_inputs), - FILTER_OUTPUTS(colorize_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_colorkey.c b/libavfilter/vf_colorkey.c index 9979f4ad0da..58dd513b31e 100644 --- a/libavfilter/vf_colorkey.c +++ b/libavfilter/vf_colorkey.c @@ -21,12 +21,10 @@ #include "config_components.h" #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" -#include "video.h" typedef struct ColorkeyContext { const AVClass *class; diff --git a/libavfilter/vf_colorkey_opencl.c b/libavfilter/vf_colorkey_opencl.c index 84be1999a89..72a36df2d33 100644 --- a/libavfilter/vf_colorkey_opencl.c +++ b/libavfilter/vf_colorkey_opencl.c @@ -17,9 +17,7 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "opencl.h" #include "opencl_source.h" @@ -52,7 +50,7 @@ static int colorkey_opencl_init(AVFilterContext *avctx) cl_int cle; int err; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_colorkey, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_colorkey_cl, 1); if (err < 0) goto fail; @@ -238,5 +236,6 @@ const AVFilter ff_vf_colorkey_opencl = { FILTER_INPUTS(colorkey_opencl_inputs), FILTER_OUTPUTS(colorkey_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_colorlevels.c b/libavfilter/vf_colorlevels.c index 44ae6cffbf2..6f54628ec52 100644 --- a/libavfilter/vf_colorlevels.c +++ b/libavfilter/vf_colorlevels.c @@ -18,12 +18,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "preserve_color.h" @@ -74,14 +72,14 @@ static const AVOption colorlevels_options[] = { { "gomax", "set output green white point", OFFSET(range[G].out_max), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS }, { "bomax", "set output blue white point", OFFSET(range[B].out_max), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS }, { "aomax", "set output alpha white point", OFFSET(range[A].out_max), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0, 1, FLAGS }, - { "preserve", "set preserve color mode", OFFSET(preserve_color), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_PRESERVE-1, FLAGS, "preserve" }, - { "none", "disabled", 0, AV_OPT_TYPE_CONST, {.i64=P_NONE}, 0, 0, FLAGS, "preserve" }, - { "lum", "luminance", 0, AV_OPT_TYPE_CONST, {.i64=P_LUM}, 0, 0, FLAGS, "preserve" }, - { "max", "max", 0, AV_OPT_TYPE_CONST, {.i64=P_MAX}, 0, 0, FLAGS, "preserve" }, - { "avg", "average", 0, AV_OPT_TYPE_CONST, {.i64=P_AVG}, 0, 0, FLAGS, "preserve" }, - { "sum", "sum", 0, AV_OPT_TYPE_CONST, {.i64=P_SUM}, 0, 0, FLAGS, "preserve" }, - { "nrm", "norm", 0, AV_OPT_TYPE_CONST, {.i64=P_NRM}, 0, 0, FLAGS, "preserve" }, - { "pwr", "power", 0, AV_OPT_TYPE_CONST, {.i64=P_PWR}, 0, 0, FLAGS, "preserve" }, + { "preserve", "set preserve color mode", OFFSET(preserve_color), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_PRESERVE-1, FLAGS, .unit = "preserve" }, + { "none", "disabled", 0, AV_OPT_TYPE_CONST, {.i64=P_NONE}, 0, 0, FLAGS, .unit = "preserve" }, + { "lum", "luminance", 0, AV_OPT_TYPE_CONST, {.i64=P_LUM}, 0, 0, FLAGS, .unit = "preserve" }, + { "max", "max", 0, AV_OPT_TYPE_CONST, {.i64=P_MAX}, 0, 0, FLAGS, .unit = "preserve" }, + { "avg", "average", 0, AV_OPT_TYPE_CONST, {.i64=P_AVG}, 0, 0, FLAGS, .unit = "preserve" }, + { "sum", "sum", 0, AV_OPT_TYPE_CONST, {.i64=P_SUM}, 0, 0, FLAGS, .unit = "preserve" }, + { "nrm", "norm", 0, AV_OPT_TYPE_CONST, {.i64=P_NRM}, 0, 0, FLAGS, .unit = "preserve" }, + { "pwr", "power", 0, AV_OPT_TYPE_CONST, {.i64=P_PWR}, 0, 0, FLAGS, .unit = "preserve" }, { NULL } }; @@ -559,20 +557,13 @@ static const AVFilterPad colorlevels_inputs[] = { }, }; -static const AVFilterPad colorlevels_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_colorlevels = { .name = "colorlevels", .description = NULL_IF_CONFIG_SMALL("Adjust the color levels."), .priv_size = sizeof(ColorLevelsContext), .priv_class = &colorlevels_class, FILTER_INPUTS(colorlevels_inputs), - FILTER_OUTPUTS(colorlevels_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_0RGB, AV_PIX_FMT_0BGR, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_RGB0, AV_PIX_FMT_BGR0, diff --git a/libavfilter/vf_colormap.c b/libavfilter/vf_colormap.c index 106333ced8c..31f33e7ebd3 100644 --- a/libavfilter/vf_colormap.c +++ b/libavfilter/vf_colormap.c @@ -70,12 +70,12 @@ typedef struct ColorMapContext { static const AVOption colormap_options[] = { { "patch_size", "set patch size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str = "64x64"}, 0, 0, FLAGS }, { "nb_patches", "set number of patches", OFFSET(size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, MAX_SIZE, FLAGS }, - { "type", "set the target type used", OFFSET(target_type), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "type" }, - { "relative", "the target colors are relative", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 1, FLAGS, "type" }, - { "absolute", "the target colors are absolute", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 1, FLAGS, "type" }, - { "kernel", "set the kernel used for measuring color difference", OFFSET(kernel_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_KERNELS-1, FLAGS, "kernel" }, - { "euclidean", "square root of sum of squared differences", 0, AV_OPT_TYPE_CONST, {.i64=EUCLIDEAN}, 0, 0, FLAGS, "kernel" }, - { "weuclidean", "weighted square root of sum of squared differences",0, AV_OPT_TYPE_CONST, {.i64=WEUCLIDEAN}, 0, 0, FLAGS, "kernel" }, + { "type", "set the target type used", OFFSET(target_type), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "type" }, + { "relative", "the target colors are relative", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 1, FLAGS, .unit = "type" }, + { "absolute", "the target colors are absolute", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 1, FLAGS, .unit = "type" }, + { "kernel", "set the kernel used for measuring color difference", OFFSET(kernel_type), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_KERNELS-1, FLAGS, .unit = "kernel" }, + { "euclidean", "square root of sum of squared differences", 0, AV_OPT_TYPE_CONST, {.i64=EUCLIDEAN}, 0, 0, FLAGS, .unit = "kernel" }, + { "weuclidean", "weighted square root of sum of squared differences",0, AV_OPT_TYPE_CONST, {.i64=WEUCLIDEAN}, 0, 0, FLAGS, .unit = "kernel" }, { NULL } }; diff --git a/libavfilter/vf_colormatrix.c b/libavfilter/vf_colormatrix.c index bee80c69cc8..81254b0463a 100644 --- a/libavfilter/vf_colormatrix.c +++ b/libavfilter/vf_colormatrix.c @@ -30,12 +30,10 @@ #include #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" -#include "libavutil/avstring.h" #define NS(n) ((n) < 0 ? (int)((n)*65536.0-0.5+DBL_EPSILON) : (int)((n)*65536.0+0.5)) #define CB(n) av_clip_uint8(n) @@ -483,20 +481,13 @@ static const AVFilterPad colormatrix_inputs[] = { }, }; -static const AVFilterPad colormatrix_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_colormatrix = { .name = "colormatrix", .description = NULL_IF_CONFIG_SMALL("Convert color matrix."), .priv_size = sizeof(ColorMatrixContext), .init = init, FILTER_INPUTS(colormatrix_inputs), - FILTER_OUTPUTS(colormatrix_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, diff --git a/libavfilter/vf_colorspace.c b/libavfilter/vf_colorspace.c index 21916b7b7af..f367ce17c68 100644 --- a/libavfilter/vf_colorspace.c +++ b/libavfilter/vf_colorspace.c @@ -400,7 +400,7 @@ static int create_filtergraph(AVFilterContext *ctx, ColorSpaceContext *s = ctx->priv; const AVPixFmtDescriptor *in_desc = av_pix_fmt_desc_get(in->format); const AVPixFmtDescriptor *out_desc = av_pix_fmt_desc_get(out->format); - int emms = 0, m, n, o, res, fmt_identical, redo_yuv2rgb = 0, redo_rgb2yuv = 0; + int m, n, o, res, fmt_identical, redo_yuv2rgb = 0, redo_rgb2yuv = 0; #define supported_depth(d) ((d) == 8 || (d) == 10 || (d) == 12) #define supported_subsampling(lcw, lch) \ @@ -494,7 +494,6 @@ static int create_filtergraph(AVFilterContext *ctx, s->lrgb2lrgb_coeffs[m][n][o] = s->lrgb2lrgb_coeffs[m][n][0]; } - emms = 1; } } @@ -542,7 +541,6 @@ static int create_filtergraph(AVFilterContext *ctx, res = fill_gamma_table(s); if (res < 0) return res; - emms = 1; } if (!s->in_lumacoef) { @@ -572,15 +570,15 @@ static int create_filtergraph(AVFilterContext *ctx, if (s->out_csp == AVCOL_SPC_UNSPECIFIED) { if (s->user_all == CS_UNSPECIFIED) { av_log(ctx, AV_LOG_ERROR, - "Please specify output transfer characteristics\n"); + "Please specify output colorspace\n"); } else { av_log(ctx, AV_LOG_ERROR, "Unsupported output color property %d\n", s->user_all); } } else { av_log(ctx, AV_LOG_ERROR, - "Unsupported output transfer characteristics %d (%s)\n", - s->out_csp, av_color_space_name(s->out_csp)); + "Unsupported output colorspace %d (%s)\n", s->out_csp, + av_color_space_name(s->out_csp)); } return AVERROR(EINVAL); } @@ -625,7 +623,6 @@ static int create_filtergraph(AVFilterContext *ctx, av_assert2(s->yuv2rgb_coeffs[0][0][0] == s->yuv2rgb_coeffs[2][0][0]); s->yuv2rgb = s->dsp.yuv2rgb[(in_desc->comp[0].depth - 8) >> 1] [in_desc->log2_chroma_h + in_desc->log2_chroma_w]; - emms = 1; } if (redo_rgb2yuv) { @@ -656,7 +653,6 @@ static int create_filtergraph(AVFilterContext *ctx, [out_desc->log2_chroma_h + out_desc->log2_chroma_w]; s->rgb2yuv_fsb = s->dsp.rgb2yuv_fsb[(out_desc->comp[0].depth - 8) >> 1] [out_desc->log2_chroma_h + out_desc->log2_chroma_w]; - emms = 1; } if (s->yuv2yuv_fastmode && (redo_yuv2rgb || redo_rgb2yuv)) { @@ -683,9 +679,6 @@ static int create_filtergraph(AVFilterContext *ctx, } } - if (emms) - emms_c(); - return 0; } @@ -885,12 +878,12 @@ static int config_props(AVFilterLink *outlink) #define OFFSET(x) offsetof(ColorSpaceContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM -#define ENUM(x, y, z) { x, "", 0, AV_OPT_TYPE_CONST, { .i64 = y }, INT_MIN, INT_MAX, FLAGS, z } +#define ENUM(x, y, z) { x, "", 0, AV_OPT_TYPE_CONST, { .i64 = y }, INT_MIN, INT_MAX, FLAGS, .unit = z } static const AVOption colorspace_options[] = { { "all", "Set all color properties together", OFFSET(user_all), AV_OPT_TYPE_INT, { .i64 = CS_UNSPECIFIED }, - CS_UNSPECIFIED, CS_NB - 1, FLAGS, "all" }, + CS_UNSPECIFIED, CS_NB - 1, FLAGS, .unit = "all" }, ENUM("bt470m", CS_BT470M, "all"), ENUM("bt470bg", CS_BT470BG, "all"), ENUM("bt601-6-525", CS_BT601_6_525, "all"), @@ -902,7 +895,7 @@ static const AVOption colorspace_options[] = { { "space", "Output colorspace", OFFSET(user_csp), AV_OPT_TYPE_INT, { .i64 = AVCOL_SPC_UNSPECIFIED }, - AVCOL_SPC_RGB, AVCOL_SPC_NB - 1, FLAGS, "csp"}, + AVCOL_SPC_RGB, AVCOL_SPC_NB - 1, FLAGS, .unit = "csp"}, ENUM("bt709", AVCOL_SPC_BT709, "csp"), ENUM("fcc", AVCOL_SPC_FCC, "csp"), ENUM("bt470bg", AVCOL_SPC_BT470BG, "csp"), @@ -915,7 +908,7 @@ static const AVOption colorspace_options[] = { { "range", "Output color range", OFFSET(user_rng), AV_OPT_TYPE_INT, { .i64 = AVCOL_RANGE_UNSPECIFIED }, - AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_NB - 1, FLAGS, "rng" }, + AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_NB - 1, FLAGS, .unit = "rng" }, ENUM("tv", AVCOL_RANGE_MPEG, "rng"), ENUM("mpeg", AVCOL_RANGE_MPEG, "rng"), ENUM("pc", AVCOL_RANGE_JPEG, "rng"), @@ -923,7 +916,7 @@ static const AVOption colorspace_options[] = { { "primaries", "Output color primaries", OFFSET(user_prm), AV_OPT_TYPE_INT, { .i64 = AVCOL_PRI_UNSPECIFIED }, - AVCOL_PRI_RESERVED0, AVCOL_PRI_NB - 1, FLAGS, "prm" }, + AVCOL_PRI_RESERVED0, AVCOL_PRI_NB - 1, FLAGS, .unit = "prm" }, ENUM("bt709", AVCOL_PRI_BT709, "prm"), ENUM("bt470m", AVCOL_PRI_BT470M, "prm"), ENUM("bt470bg", AVCOL_PRI_BT470BG, "prm"), @@ -939,7 +932,7 @@ static const AVOption colorspace_options[] = { { "trc", "Output transfer characteristics", OFFSET(user_trc), AV_OPT_TYPE_INT, { .i64 = AVCOL_TRC_UNSPECIFIED }, - AVCOL_TRC_RESERVED0, AVCOL_TRC_NB - 1, FLAGS, "trc" }, + AVCOL_TRC_RESERVED0, AVCOL_TRC_NB - 1, FLAGS, .unit = "trc" }, ENUM("bt709", AVCOL_TRC_BT709, "trc"), ENUM("bt470m", AVCOL_TRC_GAMMA22, "trc"), ENUM("gamma22", AVCOL_TRC_GAMMA22, "trc"), @@ -957,7 +950,7 @@ static const AVOption colorspace_options[] = { { "format", "Output pixel format", OFFSET(user_format), AV_OPT_TYPE_INT, { .i64 = AV_PIX_FMT_NONE }, - AV_PIX_FMT_NONE, AV_PIX_FMT_GBRAP12LE, FLAGS, "fmt" }, + AV_PIX_FMT_NONE, AV_PIX_FMT_GBRAP12LE, FLAGS, .unit = "fmt" }, ENUM("yuv420p", AV_PIX_FMT_YUV420P, "fmt"), ENUM("yuv420p10", AV_PIX_FMT_YUV420P10, "fmt"), ENUM("yuv420p12", AV_PIX_FMT_YUV420P12, "fmt"), @@ -974,32 +967,32 @@ static const AVOption colorspace_options[] = { { "dither", "Dithering mode", OFFSET(dither), AV_OPT_TYPE_INT, { .i64 = DITHER_NONE }, - DITHER_NONE, DITHER_NB - 1, FLAGS, "dither" }, + DITHER_NONE, DITHER_NB - 1, FLAGS, .unit = "dither" }, ENUM("none", DITHER_NONE, "dither"), ENUM("fsb", DITHER_FSB, "dither"), { "wpadapt", "Whitepoint adaptation method", OFFSET(wp_adapt), AV_OPT_TYPE_INT, { .i64 = WP_ADAPT_BRADFORD }, - WP_ADAPT_BRADFORD, NB_WP_ADAPT - 1, FLAGS, "wpadapt" }, + WP_ADAPT_BRADFORD, NB_WP_ADAPT - 1, FLAGS, .unit = "wpadapt" }, ENUM("bradford", WP_ADAPT_BRADFORD, "wpadapt"), ENUM("vonkries", WP_ADAPT_VON_KRIES, "wpadapt"), ENUM("identity", WP_ADAPT_IDENTITY, "wpadapt"), { "iall", "Set all input color properties together", OFFSET(user_iall), AV_OPT_TYPE_INT, { .i64 = CS_UNSPECIFIED }, - CS_UNSPECIFIED, CS_NB - 1, FLAGS, "all" }, + CS_UNSPECIFIED, CS_NB - 1, FLAGS, .unit = "all" }, { "ispace", "Input colorspace", OFFSET(user_icsp), AV_OPT_TYPE_INT, { .i64 = AVCOL_SPC_UNSPECIFIED }, - AVCOL_PRI_RESERVED0, AVCOL_PRI_NB - 1, FLAGS, "csp" }, + AVCOL_PRI_RESERVED0, AVCOL_PRI_NB - 1, FLAGS, .unit = "csp" }, { "irange", "Input color range", OFFSET(user_irng), AV_OPT_TYPE_INT, { .i64 = AVCOL_RANGE_UNSPECIFIED }, - AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_NB - 1, FLAGS, "rng" }, + AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_NB - 1, FLAGS, .unit = "rng" }, { "iprimaries", "Input color primaries", OFFSET(user_iprm), AV_OPT_TYPE_INT, { .i64 = AVCOL_PRI_UNSPECIFIED }, - AVCOL_PRI_RESERVED0, AVCOL_PRI_NB - 1, FLAGS, "prm" }, + AVCOL_PRI_RESERVED0, AVCOL_PRI_NB - 1, FLAGS, .unit = "prm" }, { "itrc", "Input transfer characteristics", OFFSET(user_itrc), AV_OPT_TYPE_INT, { .i64 = AVCOL_TRC_UNSPECIFIED }, - AVCOL_TRC_RESERVED0, AVCOL_TRC_NB - 1, FLAGS, "trc" }, + AVCOL_TRC_RESERVED0, AVCOL_TRC_NB - 1, FLAGS, .unit = "trc" }, { NULL } }; diff --git a/libavfilter/vf_colorspace_cuda.c b/libavfilter/vf_colorspace_cuda.c index 07d4edd0d87..5ad81e959c3 100644 --- a/libavfilter/vf_colorspace_cuda.c +++ b/libavfilter/vf_colorspace_cuda.c @@ -22,7 +22,6 @@ #include -#include "libavutil/avstring.h" #include "libavutil/common.h" #include "libavutil/cuda_check.h" #include "libavutil/hwcontext.h" @@ -32,10 +31,7 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "scale_eval.h" -#include "video.h" #include "cuda/load_helper.h" @@ -386,11 +382,11 @@ static int cudacolorspace_filter_frame(AVFilterLink* link, AVFrame* in) #define OFFSET(x) offsetof(CUDAColorspaceContext, x) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) static const AVOption options[] = { - {"range", "Output video range", OFFSET(range), AV_OPT_TYPE_INT, { .i64 = AVCOL_RANGE_UNSPECIFIED }, AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_NB - 1, FLAGS, "range"}, - {"tv", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range"}, - {"mpeg", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range"}, - {"pc", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range"}, - {"jpeg", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range"}, + {"range", "Output video range", OFFSET(range), AV_OPT_TYPE_INT, { .i64 = AVCOL_RANGE_UNSPECIFIED }, AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_NB - 1, FLAGS, .unit = "range"}, + {"tv", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range"}, + {"mpeg", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range"}, + {"pc", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range"}, + {"jpeg", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range"}, {NULL}, }; diff --git a/libavfilter/vf_colortemperature.c b/libavfilter/vf_colortemperature.c index e6ac5f95c71..b06b04e704e 100644 --- a/libavfilter/vf_colortemperature.c +++ b/libavfilter/vf_colortemperature.c @@ -21,10 +21,9 @@ #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -112,9 +111,9 @@ static int temperature_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb const float *color = s->color; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int glinesize = frame->linesize[0]; - const int blinesize = frame->linesize[1]; - const int rlinesize = frame->linesize[2]; + const ptrdiff_t glinesize = frame->linesize[0]; + const ptrdiff_t blinesize = frame->linesize[1]; + const ptrdiff_t rlinesize = frame->linesize[2]; uint8_t *gptr = frame->data[0] + slice_start * glinesize; uint8_t *bptr = frame->data[1] + slice_start * blinesize; uint8_t *rptr = frame->data[2] + slice_start * rlinesize; @@ -154,9 +153,9 @@ static int temperature_slice16(AVFilterContext *ctx, void *arg, int jobnr, int n const float *color = s->color; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int glinesize = frame->linesize[0] / sizeof(uint16_t); - const int blinesize = frame->linesize[1] / sizeof(uint16_t); - const int rlinesize = frame->linesize[2] / sizeof(uint16_t); + const ptrdiff_t glinesize = frame->linesize[0] / sizeof(uint16_t); + const ptrdiff_t blinesize = frame->linesize[1] / sizeof(uint16_t); + const ptrdiff_t rlinesize = frame->linesize[2] / sizeof(uint16_t); uint16_t *gptr = (uint16_t *)frame->data[0] + slice_start * glinesize; uint16_t *bptr = (uint16_t *)frame->data[1] + slice_start * blinesize; uint16_t *rptr = (uint16_t *)frame->data[2] + slice_start * rlinesize; @@ -184,6 +183,47 @@ static int temperature_slice16(AVFilterContext *ctx, void *arg, int jobnr, int n return 0; } +static int temperature_slice32(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + ColorTemperatureContext *s = ctx->priv; + AVFrame *frame = arg; + const int width = frame->width; + const int height = frame->height; + const float preserve = s->preserve; + const float mix = s->mix; + const float *color = s->color; + const int slice_start = (height * jobnr) / nb_jobs; + const int slice_end = (height * (jobnr + 1)) / nb_jobs; + const ptrdiff_t glinesize = frame->linesize[0] / sizeof(float); + const ptrdiff_t blinesize = frame->linesize[1] / sizeof(float); + const ptrdiff_t rlinesize = frame->linesize[2] / sizeof(float); + float *gptr = (float *)frame->data[0] + slice_start * glinesize; + float *bptr = (float *)frame->data[1] + slice_start * blinesize; + float *rptr = (float *)frame->data[2] + slice_start * rlinesize; + + for (int y = slice_start; y < slice_end; y++) { + for (int x = 0; x < width; x++) { + float g = gptr[x]; + float b = bptr[x]; + float r = rptr[x]; + float nr, ng, nb; + float l0, l1, l; + + PROCESS() + + gptr[x] = ng; + bptr[x] = nb; + rptr[x] = nr; + } + + gptr += glinesize; + bptr += blinesize; + rptr += rlinesize; + } + + return 0; +} + static int temperature_slice8p(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { ColorTemperatureContext *s = ctx->priv; @@ -199,7 +239,7 @@ static int temperature_slice8p(AVFilterContext *ctx, void *arg, int jobnr, int n const uint8_t boffset = s->rgba_map[B]; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int linesize = frame->linesize[0]; + const ptrdiff_t linesize = frame->linesize[0]; uint8_t *ptr = frame->data[0] + slice_start * linesize; for (int y = slice_start; y < slice_end; y++) { @@ -239,7 +279,7 @@ static int temperature_slice16p(AVFilterContext *ctx, void *arg, int jobnr, int const uint8_t boffset = s->rgba_map[B]; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int linesize = frame->linesize[0] / sizeof(uint16_t); + const ptrdiff_t linesize = frame->linesize[0] / sizeof(uint16_t); uint16_t *ptr = (uint16_t *)frame->data[0] + slice_start * linesize; for (int y = slice_start; y < slice_end; y++) { @@ -286,6 +326,7 @@ static const enum AVPixelFormat pixel_fmts[] = { AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, + AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32, AV_PIX_FMT_RGB48, AV_PIX_FMT_BGR48, AV_PIX_FMT_RGBA64, AV_PIX_FMT_BGRA64, AV_PIX_FMT_NONE @@ -309,6 +350,8 @@ static av_cold int config_input(AVFilterLink *inlink) s->do_slice = s->depth <= 8 ? temperature_slice8 : temperature_slice16; if (!planar) s->do_slice = s->depth <= 8 ? temperature_slice8p : temperature_slice16p; + if (s->depth == 32) + s->do_slice = temperature_slice32; ff_fill_rgba_map(s->rgba_map, inlink->format); @@ -325,13 +368,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(ColorTemperatureContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -350,7 +386,7 @@ const AVFilter ff_vf_colortemperature = { .priv_size = sizeof(ColorTemperatureContext), .priv_class = &colortemperature_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_convolution.c b/libavfilter/vf_convolution.c index 7762fa2a053..88b89289a9e 100644 --- a/libavfilter/vf_convolution.c +++ b/libavfilter/vf_convolution.c @@ -29,7 +29,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "convolution.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -41,21 +40,21 @@ static const AVOption convolution_options[] = { { "1m", "set matrix for 2nd plane", OFFSET(matrix_str[1]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS }, { "2m", "set matrix for 3rd plane", OFFSET(matrix_str[2]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS }, { "3m", "set matrix for 4th plane", OFFSET(matrix_str[3]), AV_OPT_TYPE_STRING, {.str="0 0 0 0 1 0 0 0 0"}, 0, 0, FLAGS }, - { "0rdiv", "set rdiv for 1st plane", OFFSET(rdiv[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, - { "1rdiv", "set rdiv for 2nd plane", OFFSET(rdiv[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, - { "2rdiv", "set rdiv for 3rd plane", OFFSET(rdiv[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, - { "3rdiv", "set rdiv for 4th plane", OFFSET(rdiv[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, + { "0rdiv", "set rdiv for 1st plane", OFFSET(user_rdiv[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, + { "1rdiv", "set rdiv for 2nd plane", OFFSET(user_rdiv[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, + { "2rdiv", "set rdiv for 3rd plane", OFFSET(user_rdiv[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, + { "3rdiv", "set rdiv for 4th plane", OFFSET(user_rdiv[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, { "0bias", "set bias for 1st plane", OFFSET(bias[0]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, { "1bias", "set bias for 2nd plane", OFFSET(bias[1]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, { "2bias", "set bias for 3rd plane", OFFSET(bias[2]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, { "3bias", "set bias for 4th plane", OFFSET(bias[3]), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, INT_MAX, FLAGS}, - { "0mode", "set matrix mode for 1st plane", OFFSET(mode[0]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" }, - { "1mode", "set matrix mode for 2nd plane", OFFSET(mode[1]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" }, - { "2mode", "set matrix mode for 3rd plane", OFFSET(mode[2]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" }, - { "3mode", "set matrix mode for 4th plane", OFFSET(mode[3]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, "mode" }, - { "square", "square matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_SQUARE}, 0, 0, FLAGS, "mode" }, - { "row", "single row matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_ROW} , 0, 0, FLAGS, "mode" }, - { "column", "single column matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_COLUMN}, 0, 0, FLAGS, "mode" }, + { "0mode", "set matrix mode for 1st plane", OFFSET(mode[0]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, .unit = "mode" }, + { "1mode", "set matrix mode for 2nd plane", OFFSET(mode[1]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, .unit = "mode" }, + { "2mode", "set matrix mode for 3rd plane", OFFSET(mode[2]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, .unit = "mode" }, + { "3mode", "set matrix mode for 4th plane", OFFSET(mode[3]), AV_OPT_TYPE_INT, {.i64=MATRIX_SQUARE}, 0, MATRIX_NBMODES-1, FLAGS, .unit = "mode" }, + { "square", "square matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_SQUARE}, 0, 0, FLAGS, .unit = "mode" }, + { "row", "single row matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_ROW} , 0, 0, FLAGS, .unit = "mode" }, + { "column", "single column matrix", 0, AV_OPT_TYPE_CONST, {.i64=MATRIX_COLUMN}, 0, 0, FLAGS, .unit = "mode" }, { NULL } }; @@ -675,7 +674,7 @@ static int param_init(AVFilterContext *ctx) p = orig = av_strdup(s->matrix_str[i]); if (p) { s->matrix_length[i] = 0; - s->rdiv[i] = 0.f; + s->rdiv[i] = s->user_rdiv[i]; sum = 0.f; while (s->matrix_length[i] < 49) { @@ -873,13 +872,6 @@ static const AVFilterPad convolution_inputs[] = { }, }; -static const AVFilterPad convolution_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #if CONFIG_CONVOLUTION_FILTER const AVFilter ff_vf_convolution = { @@ -888,7 +880,7 @@ const AVFilter ff_vf_convolution = { .priv_size = sizeof(ConvolutionContext), .priv_class = &convolution_class, FILTER_INPUTS(convolution_inputs), - FILTER_OUTPUTS(convolution_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, @@ -914,7 +906,7 @@ const AVFilter ff_vf_prewitt = { .priv_size = sizeof(ConvolutionContext), .priv_class = &common_class, FILTER_INPUTS(convolution_inputs), - FILTER_OUTPUTS(convolution_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, @@ -930,7 +922,7 @@ const AVFilter ff_vf_sobel = { .priv_size = sizeof(ConvolutionContext), .priv_class = &common_class, FILTER_INPUTS(convolution_inputs), - FILTER_OUTPUTS(convolution_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, @@ -946,7 +938,7 @@ const AVFilter ff_vf_roberts = { .priv_size = sizeof(ConvolutionContext), .priv_class = &common_class, FILTER_INPUTS(convolution_inputs), - FILTER_OUTPUTS(convolution_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, @@ -962,7 +954,7 @@ const AVFilter ff_vf_kirsch = { .priv_size = sizeof(ConvolutionContext), .priv_class = &common_class, FILTER_INPUTS(convolution_inputs), - FILTER_OUTPUTS(convolution_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, @@ -978,7 +970,7 @@ const AVFilter ff_vf_scharr = { .priv_size = sizeof(ConvolutionContext), .priv_class = &common_class, FILTER_INPUTS(convolution_inputs), - FILTER_OUTPUTS(convolution_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_convolution_opencl.c b/libavfilter/vf_convolution_opencl.c index de3d38b553b..40938436f2b 100644 --- a/libavfilter/vf_convolution_opencl.c +++ b/libavfilter/vf_convolution_opencl.c @@ -20,6 +20,7 @@ #include "config_components.h" +#include "libavutil/avassert.h" #include "libavutil/common.h" #include "libavutil/imgutils.h" #include "libavutil/mem.h" @@ -62,7 +63,7 @@ static int convolution_opencl_init(AVFilterContext *avctx) cl_int cle; int err; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_convolution, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_convolution_cl, 1); if (err < 0) goto fail; @@ -80,6 +81,8 @@ static int convolution_opencl_init(AVFilterContext *avctx) kernel_name = "prewitt_global"; } else if (!strcmp(avctx->filter->name, "roberts_opencl")){ kernel_name = "roberts_global"; + } else { + av_assert0(0); } ctx->kernel = clCreateKernel(ctx->ocf.program, kernel_name, &cle); CL_FAIL_ON_ERROR(AVERROR(EIO), "Failed to create " @@ -373,6 +376,7 @@ const AVFilter ff_vf_convolution_opencl = { FILTER_OUTPUTS(convolution_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif /* CONFIG_CONVOLUTION_OPENCL_FILTER */ @@ -399,6 +403,7 @@ const AVFilter ff_vf_sobel_opencl = { FILTER_OUTPUTS(convolution_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif /* CONFIG_SOBEL_OPENCL_FILTER */ @@ -425,6 +430,7 @@ const AVFilter ff_vf_prewitt_opencl = { FILTER_OUTPUTS(convolution_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif /* CONFIG_PREWITT_OPENCL_FILTER */ @@ -451,6 +457,7 @@ const AVFilter ff_vf_roberts_opencl = { FILTER_OUTPUTS(convolution_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif /* CONFIG_ROBERTS_OPENCL_FILTER */ diff --git a/libavfilter/vf_convolve.c b/libavfilter/vf_convolve.c index d9f4c4bd772..95d17435970 100644 --- a/libavfilter/vf_convolve.c +++ b/libavfilter/vf_convolve.c @@ -22,16 +22,13 @@ #include -#include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/tx.h" #include "avfilter.h" -#include "formats.h" #include "framesync.h" #include "internal.h" -#include "video.h" #define MAX_THREADS 16 @@ -86,9 +83,9 @@ typedef struct ConvolveContext { static const AVOption convolve_options[] = { { "planes", "set planes to convolve", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=7}, 0, 15, FLAGS }, - { "impulse", "when to process impulses", OFFSET(impulse), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "impulse" }, - { "first", "process only first impulse, ignore rest", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "impulse" }, - { "all", "process all impulses", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "impulse" }, + { "impulse", "when to process impulses", OFFSET(impulse), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "impulse" }, + { "first", "process only first impulse, ignore rest", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "impulse" }, + { "all", "process all impulses", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "impulse" }, { "noise", "set noise", OFFSET(noise), AV_OPT_TYPE_FLOAT, {.dbl=0.0000001}, 0, 1, FLAGS }, { NULL }, }; @@ -895,9 +892,9 @@ const AVFilter ff_vf_convolve = { static const AVOption deconvolve_options[] = { { "planes", "set planes to deconvolve", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=7}, 0, 15, FLAGS }, - { "impulse", "when to process impulses", OFFSET(impulse), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "impulse" }, - { "first", "process only first impulse, ignore rest", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "impulse" }, - { "all", "process all impulses", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "impulse" }, + { "impulse", "when to process impulses", OFFSET(impulse), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "impulse" }, + { "first", "process only first impulse, ignore rest", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "impulse" }, + { "all", "process all impulses", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "impulse" }, { "noise", "set noise", OFFSET(noise), AV_OPT_TYPE_FLOAT, {.dbl=0.0000001}, 0, 1, FLAGS }, { NULL }, }; @@ -925,9 +922,9 @@ const AVFilter ff_vf_deconvolve = { static const AVOption xcorrelate_options[] = { { "planes", "set planes to cross-correlate", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=7}, 0, 15, FLAGS }, - { "secondary", "when to process secondary frame", OFFSET(impulse), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "impulse" }, - { "first", "process only first secondary frame, ignore rest", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "impulse" }, - { "all", "process all secondary frames", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "impulse" }, + { "secondary", "when to process secondary frame", OFFSET(impulse), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "impulse" }, + { "first", "process only first secondary frame, ignore rest", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "impulse" }, + { "all", "process all secondary frames", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "impulse" }, { NULL }, }; @@ -958,13 +955,7 @@ static const AVFilterPad xcorrelate_inputs[] = { }, }; -static const AVFilterPad xcorrelate_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_output, - }, -}; +#define xcorrelate_outputs convolve_outputs const AVFilter ff_vf_xcorrelate = { .name = "xcorrelate", diff --git a/libavfilter/vf_copy.c b/libavfilter/vf_copy.c index 2fbced354f5..52ac9fb0ec0 100644 --- a/libavfilter/vf_copy.c +++ b/libavfilter/vf_copy.c @@ -24,6 +24,7 @@ #include "libavutil/imgutils.h" #include "libavutil/internal.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "video.h" @@ -65,18 +66,11 @@ static const AVFilterPad avfilter_vf_copy_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_copy_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_copy = { .name = "copy", .description = NULL_IF_CONFIG_SMALL("Copy the input video unchanged to the output."), .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(avfilter_vf_copy_inputs), - FILTER_OUTPUTS(avfilter_vf_copy_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/vf_coreimage.m b/libavfilter/vf_coreimage.m index b1959861ded..979eab3b01a 100644 --- a/libavfilter/vf_coreimage.m +++ b/libavfilter/vf_coreimage.m @@ -301,8 +301,14 @@ static int request_frame(AVFilterLink *link) frame->pts = ctx->pts; frame->duration = 1; +#if FF_API_FRAME_KEY frame->key_frame = 1; +#endif + frame->flags |= AV_FRAME_FLAG_KEY; +#if FF_API_INTERLACED_FRAME frame->interlaced_frame = 0; +#endif + frame->flags &= ~AV_FRAME_FLAG_INTERLACED; frame->pict_type = AV_PICTURE_TYPE_I; frame->sample_aspect_ratio = ctx->sar; diff --git a/libavfilter/vf_corr.c b/libavfilter/vf_corr.c index 65d8b09767c..7e0f81921f4 100644 --- a/libavfilter/vf_corr.c +++ b/libavfilter/vf_corr.c @@ -26,27 +26,43 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "framesync.h" #include "internal.h" -#include "video.h" + +typedef struct Sums { + uint64_t s[2]; +} Sums; + +typedef struct QSums { + float s[3]; +} QSums; typedef struct CorrContext { const AVClass *class; FFFrameSync fs; double score, min_score, max_score, score_comp[4]; uint64_t nb_frames; + int nb_threads; int is_rgb; uint8_t rgba_map[4]; int max[4]; char comps[4]; + float mean[4][2]; + Sums *sums; + QSums *qsums; int nb_components; int planewidth[4]; int planeheight[4]; - int (*filter_slice)(AVFilterContext *ctx, void *arg, - int jobnr, int nb_jobs); + int (*sum_slice)(AVFilterContext *ctx, void *arg, + int jobnr, int nb_jobs); + int (*corr_slice)(AVFilterContext *ctx, void *arg, + int jobnr, int nb_jobs); } CorrContext; +typedef struct ThreadData { + AVFrame *master, *ref; +} ThreadData; + #define OFFSET(x) offsetof(CorrContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM @@ -68,27 +84,31 @@ static void set_meta(AVFilterContext *ctx, } } -#define CORR(type, name) \ -static void f##name(AVFilterContext *ctx, AVFrame *master, \ - AVFrame *ref, double *comp_score) \ +#define SUM(type, name) \ +static int sum_##name(AVFilterContext *ctx, void *arg, \ + int jobnr, int nb_jobs) \ { \ CorrContext *s = ctx->priv; \ + ThreadData *td = arg; \ + AVFrame *master = td->master; \ + AVFrame *ref = td->ref; \ \ for (int c = 0; c < s->nb_components; c++) { \ const ptrdiff_t linesize1 = master->linesize[c] / \ sizeof(type); \ const ptrdiff_t linesize2 = ref->linesize[c] / \ sizeof(type); \ - const type *src1 = (const type *)master->data[c]; \ - const type *src2 = (const type *)ref->data[c]; \ const int h = s->planeheight[c]; \ const int w = s->planewidth[c]; \ - const float scale = 1.f / s->max[c]; \ + const int slice_start = (h * jobnr) / nb_jobs; \ + const int slice_end = (h * (jobnr+1)) / nb_jobs; \ + const type *src1 = (const type *)master->data[c] + \ + linesize1 * slice_start; \ + const type *src2 = (const type *)ref->data[c] + \ + linesize2 * slice_start; \ uint64_t sum1 = 0, sum2 = 0; \ - float sum12, sum1q, sum2q; \ - float sumq, mean1, mean2; \ \ - for (int y = 0; y < h; y++) { \ + for (int y = slice_start; y < slice_end; y++) { \ for (int x = 0; x < w; x++) { \ sum1 += src1[x]; \ sum2 += src2[x]; \ @@ -98,17 +118,47 @@ static void f##name(AVFilterContext *ctx, AVFrame *master, \ src2 += linesize2; \ } \ \ - mean1 = scale * (sum1 /(double)(w * h)); \ - mean2 = scale * (sum2 /(double)(w * h)); \ + s->sums[jobnr * s->nb_components + c].s[0] = sum1; \ + s->sums[jobnr * s->nb_components + c].s[1] = sum2; \ + } \ \ - src1 = (const type *)master->data[c]; \ - src2 = (const type *)ref->data[c]; \ + return 0; \ +} + +SUM(uint8_t, slice8) +SUM(uint16_t, slice16) + +#define CORR(type, name) \ +static int corr_##name(AVFilterContext *ctx, void *arg, \ + int jobnr, int nb_jobs) \ +{ \ + CorrContext *s = ctx->priv; \ + ThreadData *td = arg; \ + AVFrame *master = td->master; \ + AVFrame *ref = td->ref; \ + \ + for (int c = 0; c < s->nb_components; c++) { \ + const ptrdiff_t linesize1 = master->linesize[c] / \ + sizeof(type); \ + const ptrdiff_t linesize2 = ref->linesize[c] / \ + sizeof(type); \ + const type *src1 = (const type *)master->data[c]; \ + const type *src2 = (const type *)ref->data[c]; \ + const int h = s->planeheight[c]; \ + const int w = s->planewidth[c]; \ + const int slice_start = (h * jobnr) / nb_jobs; \ + const int slice_end = (h * (jobnr+1)) / nb_jobs; \ + const float scale = 1.f / s->max[c]; \ + const float mean1 = s->mean[c][0]; \ + const float mean2 = s->mean[c][1]; \ + float sum12 = 0.f, sum1q = 0.f, sum2q = 0.f; \ \ - sum12 = 0.f; \ - sum1q = 0.f; \ - sum2q = 0.f; \ + src1 = (const type *)master->data[c] + \ + slice_start * linesize1; \ + src2 = (const type *)ref->data[c] + \ + slice_start * linesize2; \ \ - for (int y = 0; y < h; y++) { \ + for (int y = slice_start; y < slice_end; y++) { \ for (int x = 0; x < w; x++) { \ const float f1 = scale * src1[x] - mean1; \ const float f2 = scale * src2[x] - mean2; \ @@ -122,17 +172,16 @@ static void f##name(AVFilterContext *ctx, AVFrame *master, \ src2 += linesize2; \ } \ \ - sumq = sqrtf(sum1q * sum2q); \ - if (sumq > 0.f) { \ - comp_score[c] = av_clipf(sum12 / sumq,-1.f,1.f); \ - } else { \ - comp_score[c] = sum1q == sum2q ? 1.f : 0.f; \ - } \ + s->qsums[jobnr * s->nb_components + c].s[0] = sum12; \ + s->qsums[jobnr * s->nb_components + c].s[1] = sum1q; \ + s->qsums[jobnr * s->nb_components + c].s[2] = sum2q; \ } \ + \ + return 0; \ } -CORR(uint8_t, corr8) -CORR(uint16_t, corr16) +CORR(uint8_t, slice8) +CORR(uint16_t, slice16) static int do_corr(FFFrameSync *fs) { @@ -141,6 +190,7 @@ static int do_corr(FFFrameSync *fs) AVFrame *master, *ref; double comp_score[4], score = 0.; AVDictionary **metadata; + ThreadData td; int ret; ret = ff_framesync_dualinput_get(fs, &master, &ref); @@ -150,10 +200,42 @@ static int do_corr(FFFrameSync *fs) return ff_filter_frame(ctx->outputs[0], master); metadata = &master->metadata; - if (s->max[0] > 255) { - fcorr16(ctx, master, ref, comp_score); - } else { - fcorr8(ctx, master, ref, comp_score); + td.master = master; + td.ref = ref; + ff_filter_execute(ctx, s->sum_slice, &td, NULL, + FFMIN(s->planeheight[1], s->nb_threads)); + + for (int c = 0; c < s->nb_components; c++) { + const double scale = 1.f / s->max[c]; + uint64_t sum1 = 0, sum2 = 0; + + for (int n = 0; n < s->nb_threads; n++) { + sum1 += s->sums[n * s->nb_components + c].s[0]; + sum2 += s->sums[n * s->nb_components + c].s[1]; + } + + s->mean[c][0] = scale * (sum1 /(double)(s->planewidth[c] * s->planeheight[c])); + s->mean[c][1] = scale * (sum2 /(double)(s->planewidth[c] * s->planeheight[c])); + } + + ff_filter_execute(ctx, s->corr_slice, &td, NULL, + FFMIN(s->planeheight[1], s->nb_threads)); + + for (int c = 0; c < s->nb_components; c++) { + double sumq, sum12 = 0.0, sum1q = 0.0, sum2q = 0.0; + + for (int n = 0; n < s->nb_threads; n++) { + sum12 += s->qsums[n * s->nb_components + c].s[0]; + sum1q += s->qsums[n * s->nb_components + c].s[1]; + sum2q += s->qsums[n * s->nb_components + c].s[2]; + } + + sumq = sqrt(sum1q * sum2q); + if (sumq > 0.0) { + comp_score[c] = av_clipd(sum12 / sumq,-1.0,1.0); + } else { + comp_score[c] = 0.f; + } } for (int c = 0; c < s->nb_components; c++) @@ -207,6 +289,7 @@ static int config_input_ref(AVFilterLink *inlink) AVFilterContext *ctx = inlink->dst; CorrContext *s = ctx->priv; + s->nb_threads = ff_filter_get_nb_threads(ctx); s->nb_components = desc->nb_components; if (ctx->inputs[0]->w != ctx->inputs[1]->w || ctx->inputs[0]->h != ctx->inputs[1]->h) { @@ -225,6 +308,11 @@ static int config_input_ref(AVFilterLink *inlink) s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, desc->log2_chroma_w); s->planewidth[0] = s->planewidth[3] = inlink->w; + s->sums = av_calloc(s->nb_threads * s->nb_components, sizeof(*s->sums)); + s->qsums = av_calloc(s->nb_threads * s->nb_components, sizeof(*s->qsums)); + if (!s->qsums || !s->sums) + return AVERROR(ENOMEM); + s->min_score = +INFINITY; s->max_score = -INFINITY; @@ -233,6 +321,9 @@ static int config_input_ref(AVFilterLink *inlink) s->max[2] = (1 << desc->comp[2].depth) - 1; s->max[3] = (1 << desc->comp[3].depth) - 1; + s->sum_slice = desc->comp[0].depth > 8 ? sum_slice16 : sum_slice8; + s->corr_slice = desc->comp[0].depth > 8 ? corr_slice16 : corr_slice8; + return 0; } @@ -293,6 +384,8 @@ static av_cold void uninit(AVFilterContext *ctx) } ff_framesync_uninit(&s->fs); + av_freep(&s->qsums); + av_freep(&s->sums); } static const AVFilterPad corr_inputs[] = { @@ -334,5 +427,6 @@ const AVFilter ff_vf_corr = { FILTER_OUTPUTS(corr_outputs), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | + AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_METADATA_ONLY, }; diff --git a/libavfilter/vf_cover_rect.c b/libavfilter/vf_cover_rect.c index 642747a3516..a4321fdb13c 100644 --- a/libavfilter/vf_cover_rect.c +++ b/libavfilter/vf_cover_rect.c @@ -26,6 +26,7 @@ #include "libavutil/opt.h" #include "filters.h" #include "internal.h" +#include "video.h" #include "lavfutils.h" @@ -47,9 +48,9 @@ typedef struct CoverContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption cover_rect_options[] = { { "cover", "cover bitmap filename", OFFSET(cover_filename), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS }, - { "mode", "set removal mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_BLUR}, 0, NB_MODES - 1, FLAGS, "mode" }, - { "cover", "cover area with bitmap", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_COVER}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "blur", "blur area", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_BLUR}, INT_MIN, INT_MAX, FLAGS, "mode" }, + { "mode", "set removal mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_BLUR}, 0, NB_MODES - 1, FLAGS, .unit = "mode" }, + { "cover", "cover area with bitmap", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_COVER}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "blur", "blur area", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_BLUR}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, { NULL } }; @@ -232,13 +233,6 @@ static const AVFilterPad cover_rect_inputs[] = { }, }; -static const AVFilterPad cover_rect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_cover_rect = { .name = "cover_rect", .description = NULL_IF_CONFIG_SMALL("Find and cover a user specified object."), @@ -246,7 +240,7 @@ const AVFilter ff_vf_cover_rect = { .init = init, .uninit = uninit, FILTER_INPUTS(cover_rect_inputs), - FILTER_OUTPUTS(cover_rect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P), .priv_class = &cover_rect_class, }; diff --git a/libavfilter/vf_crop.c b/libavfilter/vf_crop.c index c7cbfa51ef9..6361209941a 100644 --- a/libavfilter/vf_crop.c +++ b/libavfilter/vf_crop.c @@ -50,7 +50,9 @@ static const char *const var_names[] = { "x", "y", "n", ///< number of frame +#if FF_API_FRAME_PKT "pos", ///< position in the file +#endif "t", ///< timestamp expressed in seconds NULL }; @@ -68,7 +70,9 @@ enum var_name { VAR_X, VAR_Y, VAR_N, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_T, VAR_VARS_NB }; @@ -145,7 +149,9 @@ static int config_input(AVFilterLink *link) s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = NAN; s->var_values[VAR_N] = 0; s->var_values[VAR_T] = NAN; +#if FF_API_FRAME_PKT s->var_values[VAR_POS] = NAN; +#endif av_image_fill_max_pixsteps(s->max_step, NULL, pix_desc); @@ -257,8 +263,12 @@ static int filter_frame(AVFilterLink *link, AVFrame *frame) s->var_values[VAR_N] = link->frame_count_out; s->var_values[VAR_T] = frame->pts == AV_NOPTS_VALUE ? NAN : frame->pts * av_q2d(link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS s->var_values[VAR_POS] = frame->pkt_pos == -1 ? NAN : frame->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif s->var_values[VAR_X] = av_expr_eval(s->x_pexpr, s->var_values, NULL); s->var_values[VAR_Y] = av_expr_eval(s->y_pexpr, s->var_values, NULL); /* It is necessary if x is expressed from y */ @@ -280,8 +290,8 @@ static int filter_frame(AVFilterLink *link, AVFrame *frame) s->y &= ~((1 << s->vsub) - 1); } - av_log(ctx, AV_LOG_TRACE, "n:%d t:%f pos:%f x:%d y:%d x+w:%d y+h:%d\n", - (int)s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS], + av_log(ctx, AV_LOG_TRACE, "n:%d t:%f x:%d y:%d x+w:%d y+h:%d\n", + (int)s->var_values[VAR_N], s->var_values[VAR_T], s->x, s->y, s->x+s->w, s->y+s->h); if (desc->flags & AV_PIX_FMT_FLAG_HWACCEL) { diff --git a/libavfilter/vf_cropdetect.c b/libavfilter/vf_cropdetect.c index 588a38fed81..a1cfc465d8d 100644 --- a/libavfilter/vf_cropdetect.c +++ b/libavfilter/vf_cropdetect.c @@ -30,7 +30,6 @@ #include "libavutil/qsort.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "edge_common.h" @@ -475,9 +474,9 @@ static const AVOption cropdetect_options[] = { { "skip", "Number of initial frames to skip", OFFSET(skip), AV_OPT_TYPE_INT, { .i64 = 2 }, 0, INT_MAX, FLAGS }, { "reset_count", "Recalculate the crop area after this many frames",OFFSET(reset_count),AV_OPT_TYPE_INT,{ .i64 = 0 }, 0, INT_MAX, FLAGS }, { "max_outliers", "Threshold count of outliers", OFFSET(max_outliers),AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_BLACK}, 0, MODE_NB-1, FLAGS, "mode" }, - { "black", "detect black pixels surrounding the video", 0, AV_OPT_TYPE_CONST, {.i64=MODE_BLACK}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "mvedges", "detect motion and edged surrounding the video", 0, AV_OPT_TYPE_CONST, {.i64=MODE_MV_EDGES}, INT_MIN, INT_MAX, FLAGS, "mode" }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_BLACK}, 0, MODE_NB-1, FLAGS, .unit = "mode" }, + { "black", "detect black pixels surrounding the video", 0, AV_OPT_TYPE_CONST, {.i64=MODE_BLACK}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "mvedges", "detect motion and edged surrounding the video", 0, AV_OPT_TYPE_CONST, {.i64=MODE_MV_EDGES}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, { "high", "Set high threshold for edge detection", OFFSET(high), AV_OPT_TYPE_FLOAT, {.dbl=25/255.}, 0, 1, FLAGS }, { "low", "Set low threshold for edge detection", OFFSET(low), AV_OPT_TYPE_FLOAT, {.dbl=15/255.}, 0, 1, FLAGS }, { "mv_threshold", "motion vector threshold when estimating video window size", OFFSET(mv_threshold), AV_OPT_TYPE_INT, {.i64=8}, 0, 100, FLAGS}, @@ -495,13 +494,6 @@ static const AVFilterPad avfilter_vf_cropdetect_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_cropdetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - const AVFilter ff_vf_cropdetect = { .name = "cropdetect", .description = NULL_IF_CONFIG_SMALL("Auto-detect crop size."), @@ -510,7 +502,7 @@ const AVFilter ff_vf_cropdetect = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_cropdetect_inputs), - FILTER_OUTPUTS(avfilter_vf_cropdetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, .process_command = process_command, diff --git a/libavfilter/vf_curves.c b/libavfilter/vf_curves.c index 838942d745b..55c0a1236e5 100644 --- a/libavfilter/vf_curves.c +++ b/libavfilter/vf_curves.c @@ -91,18 +91,18 @@ typedef struct ThreadData { #define OFFSET(x) offsetof(CurvesContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption curves_options[] = { - { "preset", "select a color curves preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=PRESET_NONE}, PRESET_NONE, NB_PRESETS-1, FLAGS, "preset_name" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NONE}, 0, 0, FLAGS, "preset_name" }, - { "color_negative", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_COLOR_NEGATIVE}, 0, 0, FLAGS, "preset_name" }, - { "cross_process", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_CROSS_PROCESS}, 0, 0, FLAGS, "preset_name" }, - { "darker", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_DARKER}, 0, 0, FLAGS, "preset_name" }, - { "increase_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_INCREASE_CONTRAST}, 0, 0, FLAGS, "preset_name" }, - { "lighter", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_LIGHTER}, 0, 0, FLAGS, "preset_name" }, - { "linear_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_LINEAR_CONTRAST}, 0, 0, FLAGS, "preset_name" }, - { "medium_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_MEDIUM_CONTRAST}, 0, 0, FLAGS, "preset_name" }, - { "negative", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NEGATIVE}, 0, 0, FLAGS, "preset_name" }, - { "strong_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_STRONG_CONTRAST}, 0, 0, FLAGS, "preset_name" }, - { "vintage", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_VINTAGE}, 0, 0, FLAGS, "preset_name" }, + { "preset", "select a color curves preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=PRESET_NONE}, PRESET_NONE, NB_PRESETS-1, FLAGS, .unit = "preset_name" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NONE}, 0, 0, FLAGS, .unit = "preset_name" }, + { "color_negative", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_COLOR_NEGATIVE}, 0, 0, FLAGS, .unit = "preset_name" }, + { "cross_process", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_CROSS_PROCESS}, 0, 0, FLAGS, .unit = "preset_name" }, + { "darker", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_DARKER}, 0, 0, FLAGS, .unit = "preset_name" }, + { "increase_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_INCREASE_CONTRAST}, 0, 0, FLAGS, .unit = "preset_name" }, + { "lighter", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_LIGHTER}, 0, 0, FLAGS, .unit = "preset_name" }, + { "linear_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_LINEAR_CONTRAST}, 0, 0, FLAGS, .unit = "preset_name" }, + { "medium_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_MEDIUM_CONTRAST}, 0, 0, FLAGS, .unit = "preset_name" }, + { "negative", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NEGATIVE}, 0, 0, FLAGS, .unit = "preset_name" }, + { "strong_contrast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_STRONG_CONTRAST}, 0, 0, FLAGS, .unit = "preset_name" }, + { "vintage", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_VINTAGE}, 0, 0, FLAGS, .unit = "preset_name" }, { "master","set master points coordinates",OFFSET(comp_points_str[NB_COMP]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, { "m", "set master points coordinates",OFFSET(comp_points_str[NB_COMP]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, { "red", "set red points coordinates", OFFSET(comp_points_str[0]), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, @@ -114,9 +114,9 @@ static const AVOption curves_options[] = { { "all", "set points coordinates for all components", OFFSET(comp_points_str_all), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, { "psfile", "set Photoshop curves file name", OFFSET(psfile), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, { "plot", "save Gnuplot script of the curves in specified file", OFFSET(plot_filename), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, - { "interp", "specify the kind of interpolation", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=INTERP_NATURAL}, INTERP_NATURAL, NB_INTERPS-1, FLAGS, "interp_name" }, - { "natural", "natural cubic spline", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_NATURAL}, 0, 0, FLAGS, "interp_name" }, - { "pchip", "monotonically cubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_PCHIP}, 0, 0, FLAGS, "interp_name" }, + { "interp", "specify the kind of interpolation", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=INTERP_NATURAL}, INTERP_NATURAL, NB_INTERPS-1, FLAGS, .unit = "interp_name" }, + { "natural", "natural cubic spline", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_NATURAL}, 0, 0, FLAGS, .unit = "interp_name" }, + { "pchip", "monotonically cubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_PCHIP}, 0, 0, FLAGS, .unit = "interp_name" }, { NULL } }; @@ -1005,13 +1005,6 @@ static const AVFilterPad curves_inputs[] = { }, }; -static const AVFilterPad curves_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_curves = { .name = "curves", .description = NULL_IF_CONFIG_SMALL("Adjust components curves."), @@ -1019,7 +1012,7 @@ const AVFilter ff_vf_curves = { .init = curves_init, .uninit = curves_uninit, FILTER_INPUTS(curves_inputs), - FILTER_OUTPUTS(curves_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, diff --git a/libavfilter/vf_datascope.c b/libavfilter/vf_datascope.c index d4f441c0ab6..fedd57b8381 100644 --- a/libavfilter/vf_datascope.c +++ b/libavfilter/vf_datascope.c @@ -62,15 +62,15 @@ static const AVOption datascope_options[] = { { "s", "set output size", OFFSET(ow), AV_OPT_TYPE_IMAGE_SIZE, {.str="hd720"}, 0, 0, FLAGS }, { "x", "set x offset", OFFSET(x), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGSR }, { "y", "set y offset", OFFSET(y), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGSR }, - { "mode", "set scope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGSR, "mode" }, - { "mono", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGSR, "mode" }, - { "color", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGSR, "mode" }, - { "color2", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGSR, "mode" }, + { "mode", "set scope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGSR, .unit = "mode" }, + { "mono", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGSR, .unit = "mode" }, + { "color", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGSR, .unit = "mode" }, + { "color2", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGSR, .unit = "mode" }, { "axis", "draw column/row numbers", OFFSET(axis), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGSR }, { "opacity", "set background opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, FLAGSR }, - { "format", "set display number format", OFFSET(dformat), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGSR, "format" }, - { "hex", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGSR, "format" }, - { "dec", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGSR, "format" }, + { "format", "set display number format", OFFSET(dformat), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGSR, .unit = "format" }, + { "hex", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGSR, .unit = "format" }, + { "dec", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGSR, .unit = "format" }, { "components", "set components to display", OFFSET(components), AV_OPT_TYPE_INT, {.i64=15}, 1, 15, FLAGSR }, { NULL } }; @@ -383,7 +383,7 @@ static int config_input(AVFilterLink *inlink) uint8_t alpha = s->opacity * 255; s->nb_planes = av_pix_fmt_count_planes(inlink->format); - ff_draw_init(&s->draw, inlink->format, 0); + ff_draw_init2(&s->draw, inlink->format, inlink->colorspace, inlink->color_range, 0); ff_draw_color(&s->draw, &s->white, (uint8_t[]){ 255, 255, 255, 255} ); ff_draw_color(&s->draw, &s->black, (uint8_t[]){ 0, 0, 0, alpha} ); ff_draw_color(&s->draw, &s->yellow, (uint8_t[]){ 255, 255, 0, 255} ); @@ -728,20 +728,13 @@ static const AVFilterPad pixscope_inputs[] = { }, }; -static const AVFilterPad pixscope_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_pixscope = { .name = "pixscope", .description = NULL_IF_CONFIG_SMALL("Pixel data analysis."), .priv_size = sizeof(PixscopeContext), .priv_class = &pixscope_class, FILTER_INPUTS(pixscope_inputs), - FILTER_OUTPUTS(pixscope_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = pixscope_process_command, @@ -1132,13 +1125,6 @@ static const AVFilterPad oscilloscope_inputs[] = { }, }; -static const AVFilterPad oscilloscope_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_oscilloscope = { .name = "oscilloscope", .description = NULL_IF_CONFIG_SMALL("2D Video Oscilloscope."), @@ -1146,7 +1132,7 @@ const AVFilter ff_vf_oscilloscope = { .priv_class = &oscilloscope_class, .uninit = oscilloscope_uninit, FILTER_INPUTS(oscilloscope_inputs), - FILTER_OUTPUTS(oscilloscope_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = oscilloscope_process_command, diff --git a/libavfilter/vf_dblur.c b/libavfilter/vf_dblur.c index 7dcb873ba8c..3110d192718 100644 --- a/libavfilter/vf_dblur.c +++ b/libavfilter/vf_dblur.c @@ -22,7 +22,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -299,13 +298,6 @@ static const AVFilterPad dblur_inputs[] = { }, }; -static const AVFilterPad dblur_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_dblur = { .name = "dblur", .description = NULL_IF_CONFIG_SMALL("Apply Directional Blur filter."), @@ -313,7 +305,7 @@ const AVFilter ff_vf_dblur = { .priv_class = &dblur_class, .uninit = uninit, FILTER_INPUTS(dblur_inputs), - FILTER_OUTPUTS(dblur_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_dctdnoiz.c b/libavfilter/vf_dctdnoiz.c index f1cad94ff1f..ab96dbe15e1 100644 --- a/libavfilter/vf_dctdnoiz.c +++ b/libavfilter/vf_dctdnoiz.c @@ -33,6 +33,7 @@ #include "libavutil/mem_internal.h" #include "libavutil/opt.h" #include "internal.h" +#include "video.h" static const char *const var_names[] = { "c", NULL }; enum { VAR_C, VAR_VARS_NB }; @@ -809,13 +810,6 @@ static const AVFilterPad dctdnoiz_inputs[] = { }, }; -static const AVFilterPad dctdnoiz_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_dctdnoiz = { .name = "dctdnoiz", .description = NULL_IF_CONFIG_SMALL("Denoise frames using 2D DCT."), @@ -823,7 +817,7 @@ const AVFilter ff_vf_dctdnoiz = { .init = init, .uninit = uninit, FILTER_INPUTS(dctdnoiz_inputs), - FILTER_OUTPUTS(dctdnoiz_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &dctdnoiz_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_deband.c b/libavfilter/vf_deband.c index ec91cb15486..177d888bed4 100644 --- a/libavfilter/vf_deband.c +++ b/libavfilter/vf_deband.c @@ -24,6 +24,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "video.h" @@ -458,13 +459,6 @@ static const AVFilterPad avfilter_vf_deband_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_deband_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_deband = { .name = "deband", .description = NULL_IF_CONFIG_SMALL("Debands video."), @@ -472,7 +466,7 @@ const AVFilter ff_vf_deband = { .priv_class = &deband_class, .uninit = uninit, FILTER_INPUTS(avfilter_vf_deband_inputs), - FILTER_OUTPUTS(avfilter_vf_deband_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_deblock.c b/libavfilter/vf_deblock.c index 770fce0eab2..7e4b1799d3b 100644 --- a/libavfilter/vf_deblock.c +++ b/libavfilter/vf_deblock.c @@ -27,7 +27,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -382,9 +381,9 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption deblock_options[] = { - { "filter", "set type of filter", OFFSET(filter), AV_OPT_TYPE_INT, {.i64=STRONG},0, 1, FLAGS, "filter" }, - { "weak", 0, 0, AV_OPT_TYPE_CONST, {.i64=WEAK}, 0, 0, FLAGS, "filter" }, - { "strong", 0, 0, AV_OPT_TYPE_CONST, {.i64=STRONG},0, 0, FLAGS, "filter" }, + { "filter", "set type of filter", OFFSET(filter), AV_OPT_TYPE_INT, {.i64=STRONG},0, 1, FLAGS, .unit = "filter" }, + { "weak", 0, 0, AV_OPT_TYPE_CONST, {.i64=WEAK}, 0, 0, FLAGS, .unit = "filter" }, + { "strong", 0, 0, AV_OPT_TYPE_CONST, {.i64=STRONG},0, 0, FLAGS, .unit = "filter" }, { "block", "set size of block", OFFSET(block), AV_OPT_TYPE_INT, {.i64=8}, 4, 512, FLAGS }, { "alpha", "set 1st detection threshold", OFFSET(alpha), AV_OPT_TYPE_FLOAT, {.dbl=.098}, 0, 1, FLAGS }, { "beta", "set 2nd detection threshold", OFFSET(beta), AV_OPT_TYPE_FLOAT, {.dbl=.05}, 0, 1, FLAGS }, diff --git a/libavfilter/vf_dedot.c b/libavfilter/vf_dedot.c index 6ca47c262a0..acdb03c7060 100644 --- a/libavfilter/vf_dedot.c +++ b/libavfilter/vf_dedot.c @@ -18,13 +18,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -113,12 +111,12 @@ static int dedotcrawl##name(AVFilterContext *ctx, void *arg, \ for (int y = slice_start; y < slice_end; y++) { \ for (int x = 1; x < s->planewidth[0] - 1; x++) { \ int above = src[x - src_linesize]; \ - int bellow = src[x + src_linesize]; \ + int below = src[x + src_linesize]; \ int cur = src[x]; \ int left = src[x - 1]; \ int right = src[x + 1]; \ \ - if (FFABS(above + bellow - 2 * cur) <= luma2d && \ + if (FFABS(above + below - 2 * cur) <= luma2d && \ FFABS(left + right - 2 * cur) <= luma2d) \ continue; \ \ @@ -365,9 +363,9 @@ static av_cold void uninit(AVFilterContext *ctx) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM static const AVOption dedot_options[] = { - { "m", "set filtering mode", OFFSET( m), AV_OPT_TYPE_FLAGS, {.i64=3}, 0, 3, FLAGS, "m" }, - { "dotcrawl", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "m" }, - { "rainbows", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "m" }, + { "m", "set filtering mode", OFFSET( m), AV_OPT_TYPE_FLAGS, {.i64=3}, 0, 3, FLAGS, .unit = "m" }, + { "dotcrawl", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "m" }, + { "rainbows", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "m" }, { "lt", "set spatial luma threshold", OFFSET(lt), AV_OPT_TYPE_FLOAT, {.dbl=.079}, 0, 1, FLAGS }, { "tl", "set tolerance for temporal luma", OFFSET(tl), AV_OPT_TYPE_FLOAT, {.dbl=.079}, 0, 1, FLAGS }, { "tc", "set tolerance for chroma temporal variation", OFFSET(tc), AV_OPT_TYPE_FLOAT, {.dbl=.058}, 0, 1, FLAGS }, @@ -375,13 +373,6 @@ static const AVOption dedot_options[] = { { NULL }, }; -static const AVFilterPad inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad outputs[] = { { .name = "default", @@ -399,7 +390,7 @@ const AVFilter ff_vf_dedot = { .priv_class = &dedot_class, .activate = activate, .uninit = uninit, - FILTER_INPUTS(inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(outputs), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_deflicker.c b/libavfilter/vf_deflicker.c index c01c057e0e8..f84fe5eafb0 100644 --- a/libavfilter/vf_deflicker.c +++ b/libavfilter/vf_deflicker.c @@ -27,7 +27,6 @@ #define FF_BUFQUEUE_SIZE 129 #include "bufferqueue.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -76,15 +75,15 @@ typedef struct DeflickerContext { static const AVOption deflicker_options[] = { { "size", "set how many frames to use", OFFSET(size), AV_OPT_TYPE_INT, {.i64=5}, 2, SIZE, FLAGS }, { "s", "set how many frames to use", OFFSET(size), AV_OPT_TYPE_INT, {.i64=5}, 2, SIZE, FLAGS }, - { "mode", "set how to smooth luminance", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_SMOOTH_MODE-1, FLAGS, "mode" }, - { "m", "set how to smooth luminance", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_SMOOTH_MODE-1, FLAGS, "mode" }, - { "am", "arithmetic mean", 0, AV_OPT_TYPE_CONST, {.i64=ARITHMETIC_MEAN}, 0, 0, FLAGS, "mode" }, - { "gm", "geometric mean", 0, AV_OPT_TYPE_CONST, {.i64=GEOMETRIC_MEAN}, 0, 0, FLAGS, "mode" }, - { "hm", "harmonic mean", 0, AV_OPT_TYPE_CONST, {.i64=HARMONIC_MEAN}, 0, 0, FLAGS, "mode" }, - { "qm", "quadratic mean", 0, AV_OPT_TYPE_CONST, {.i64=QUADRATIC_MEAN}, 0, 0, FLAGS, "mode" }, - { "cm", "cubic mean", 0, AV_OPT_TYPE_CONST, {.i64=CUBIC_MEAN}, 0, 0, FLAGS, "mode" }, - { "pm", "power mean", 0, AV_OPT_TYPE_CONST, {.i64=POWER_MEAN}, 0, 0, FLAGS, "mode" }, - { "median", "median", 0, AV_OPT_TYPE_CONST, {.i64=MEDIAN}, 0, 0, FLAGS, "mode" }, + { "mode", "set how to smooth luminance", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_SMOOTH_MODE-1, FLAGS, .unit = "mode" }, + { "m", "set how to smooth luminance", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_SMOOTH_MODE-1, FLAGS, .unit = "mode" }, + { "am", "arithmetic mean", 0, AV_OPT_TYPE_CONST, {.i64=ARITHMETIC_MEAN}, 0, 0, FLAGS, .unit = "mode" }, + { "gm", "geometric mean", 0, AV_OPT_TYPE_CONST, {.i64=GEOMETRIC_MEAN}, 0, 0, FLAGS, .unit = "mode" }, + { "hm", "harmonic mean", 0, AV_OPT_TYPE_CONST, {.i64=HARMONIC_MEAN}, 0, 0, FLAGS, .unit = "mode" }, + { "qm", "quadratic mean", 0, AV_OPT_TYPE_CONST, {.i64=QUADRATIC_MEAN}, 0, 0, FLAGS, .unit = "mode" }, + { "cm", "cubic mean", 0, AV_OPT_TYPE_CONST, {.i64=CUBIC_MEAN}, 0, 0, FLAGS, .unit = "mode" }, + { "pm", "power mean", 0, AV_OPT_TYPE_CONST, {.i64=POWER_MEAN}, 0, 0, FLAGS, .unit = "mode" }, + { "median", "median", 0, AV_OPT_TYPE_CONST, {.i64=MEDIAN}, 0, 0, FLAGS, .unit = "mode" }, { "bypass", "leave frames unchanged", OFFSET(bypass), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { NULL } }; diff --git a/libavfilter/vf_deinterlace_vaapi.c b/libavfilter/vf_deinterlace_vaapi.c index 13045610343..dbaba26ab48 100644 --- a/libavfilter/vf_deinterlace_vaapi.c +++ b/libavfilter/vf_deinterlace_vaapi.c @@ -23,7 +23,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "vaapi_vpp.h" @@ -252,7 +251,7 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) if (err < 0) goto fail; - if (!ctx->auto_enable || input_frame->interlaced_frame) { + if (!ctx->auto_enable || (input_frame->flags & AV_FRAME_FLAG_INTERLACED)) { vas = vaMapBuffer(vpp_ctx->hwctx->display, vpp_ctx->filter_buffers[0], &filter_params_addr); if (vas != VA_STATUS_SUCCESS) { @@ -263,7 +262,7 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) } filter_params = filter_params_addr; filter_params->flags = 0; - if (input_frame->top_field_first) { + if (input_frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) { filter_params->flags |= field ? VA_DEINTERLACING_BOTTOM_FIELD : 0; } else { filter_params->flags |= VA_DEINTERLACING_BOTTOM_FIELD_FIRST; @@ -303,7 +302,12 @@ static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) output_frame->pts = input_frame->pts + ctx->frame_queue[current_frame_index + 1]->pts; } +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS output_frame->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + output_frame->flags &= ~AV_FRAME_FLAG_INTERLACED; av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n", av_get_pix_fmt_name(output_frame->format), @@ -361,24 +365,24 @@ static av_cold int deint_vaapi_init(AVFilterContext *avctx) static const AVOption deint_vaapi_options[] = { { "mode", "Deinterlacing mode", OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = VAProcDeinterlacingNone }, - VAProcDeinterlacingNone, VAProcDeinterlacingCount - 1, FLAGS, "mode" }, + VAProcDeinterlacingNone, VAProcDeinterlacingCount - 1, FLAGS, .unit = "mode" }, { "default", "Use the highest-numbered (and therefore possibly most advanced) deinterlacing algorithm", - 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingNone }, 0, 0, FLAGS, "mode" }, + 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingNone }, 0, 0, FLAGS, .unit = "mode" }, { "bob", "Use the bob deinterlacing algorithm", - 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingBob }, 0, 0, FLAGS, "mode" }, + 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingBob }, 0, 0, FLAGS, .unit = "mode" }, { "weave", "Use the weave deinterlacing algorithm", - 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingWeave }, 0, 0, FLAGS, "mode" }, + 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingWeave }, 0, 0, FLAGS, .unit = "mode" }, { "motion_adaptive", "Use the motion adaptive deinterlacing algorithm", - 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingMotionAdaptive }, 0, 0, FLAGS, "mode" }, + 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingMotionAdaptive }, 0, 0, FLAGS, .unit = "mode" }, { "motion_compensated", "Use the motion compensated deinterlacing algorithm", - 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingMotionCompensated }, 0, 0, FLAGS, "mode" }, + 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingMotionCompensated }, 0, 0, FLAGS, .unit = "mode" }, { "rate", "Generate output at frame rate or field rate", - OFFSET(field_rate), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 2, FLAGS, "rate" }, + OFFSET(field_rate), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 2, FLAGS, .unit = "rate" }, { "frame", "Output at frame rate (one frame of output for each field-pair)", - 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "rate" }, + 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, .unit = "rate" }, { "field", "Output at field rate (one frame of output for each field)", - 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, "rate" }, + 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, .unit = "rate" }, { "auto", "Only deinterlace fields, passing frames through unchanged", OFFSET(auto_enable), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS }, diff --git a/libavfilter/vf_delogo.c b/libavfilter/vf_delogo.c index cc71f25c597..c049f273b05 100644 --- a/libavfilter/vf_delogo.c +++ b/libavfilter/vf_delogo.c @@ -33,7 +33,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/eval.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" static const char * const var_names[] = { @@ -381,13 +380,6 @@ static const AVFilterPad avfilter_vf_delogo_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_delogo_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_delogo = { .name = "delogo", .description = NULL_IF_CONFIG_SMALL("Remove logo from input video."), @@ -396,7 +388,7 @@ const AVFilter ff_vf_delogo = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_delogo_inputs), - FILTER_OUTPUTS(avfilter_vf_delogo_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_derain.c b/libavfilter/vf_derain.c index 86e9eb8752b..c8848dd7bab 100644 --- a/libavfilter/vf_derain.c +++ b/libavfilter/vf_derain.c @@ -24,12 +24,11 @@ * http://openaccess.thecvf.com/content_ECCV_2018/html/Xia_Li_Recurrent_Squeeze-and-Excitation_Context_ECCV_2018_paper.html */ -#include "libavformat/avio.h" #include "libavutil/opt.h" #include "avfilter.h" #include "dnn_filter_common.h" -#include "formats.h" #include "internal.h" +#include "video.h" typedef struct DRContext { const AVClass *class; @@ -40,13 +39,12 @@ typedef struct DRContext { #define OFFSET(x) offsetof(DRContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM static const AVOption derain_options[] = { - { "filter_type", "filter type(derain/dehaze)", OFFSET(filter_type), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, "type" }, - { "derain", "derain filter flag", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "type" }, - { "dehaze", "dehaze filter flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "type" }, - { "dnn_backend", "DNN backend", OFFSET(dnnctx.backend_type), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, "backend" }, - { "native", "native backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "backend" }, + { "filter_type", "filter type(derain/dehaze)", OFFSET(filter_type), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, .unit = "type" }, + { "derain", "derain filter flag", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, .unit = "type" }, + { "dehaze", "dehaze filter flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, .unit = "type" }, + { "dnn_backend", "DNN backend", OFFSET(dnnctx.backend_type), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS, .unit = "backend" }, #if (CONFIG_LIBTENSORFLOW == 1) - { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "backend" }, + { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, .unit = "backend" }, #endif { "model", "path to model file", OFFSET(dnnctx.model_filename), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, { "input", "input name of the model", OFFSET(dnnctx.model_inputname), AV_OPT_TYPE_STRING, { .str = "x" }, 0, 0, FLAGS }, @@ -111,13 +109,6 @@ static const AVFilterPad derain_inputs[] = { }, }; -static const AVFilterPad derain_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_derain = { .name = "derain", .description = NULL_IF_CONFIG_SMALL("Apply derain filter to the input."), @@ -125,7 +116,7 @@ const AVFilter ff_vf_derain = { .init = init, .uninit = uninit, FILTER_INPUTS(derain_inputs), - FILTER_OUTPUTS(derain_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_RGB24), .priv_class = &derain_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_deshake.c b/libavfilter/vf_deshake.c index 142f88541d2..107b78a7d1c 100644 --- a/libavfilter/vf_deshake.c +++ b/libavfilter/vf_deshake.c @@ -51,15 +51,68 @@ #include "avfilter.h" #include "internal.h" +#include "transform.h" #include "video.h" #include "libavutil/common.h" #include "libavutil/file_open.h" #include "libavutil/mem.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" +#include "libavutil/pixelutils.h" #include "libavutil/qsort.h" -#include "deshake.h" + +enum SearchMethod { + EXHAUSTIVE, ///< Search all possible positions + SMART_EXHAUSTIVE, ///< Search most possible positions (faster) + SEARCH_COUNT +}; + +typedef struct IntMotionVector { + int x; ///< Horizontal shift + int y; ///< Vertical shift +} IntMotionVector; + +typedef struct MotionVector { + double x; ///< Horizontal shift + double y; ///< Vertical shift +} MotionVector; + +typedef struct Transform { + MotionVector vec; ///< Motion vector + double angle; ///< Angle of rotation + double zoom; ///< Zoom percentage +} Transform; + +#define MAX_R 64 + +typedef struct DeshakeContext { + const AVClass *class; + int counts[2*MAX_R+1][2*MAX_R+1]; ///< Scratch buffer for motion search + double *angles; ///< Scratch buffer for block angles + unsigned angles_size; + AVFrame *ref; ///< Previous frame + int rx; ///< Maximum horizontal shift + int ry; ///< Maximum vertical shift + int edge; ///< Edge fill method + int blocksize; ///< Size of blocks to compare + int contrast; ///< Contrast threshold + int search; ///< Motion search method + av_pixelutils_sad_fn sad; ///< Sum of the absolute difference function + Transform last; ///< Transform from last frame + int refcount; ///< Number of reference frames (defines averaging window) + FILE *fp; + Transform avg; + int cw; ///< Crop motion search to this box + int ch; + int cx; + int cy; + char *filename; ///< Motion search detailed log filename + int opencl; + int (* transform)(AVFilterContext *ctx, int width, int height, int cw, int ch, + const float *matrix_y, const float *matrix_uv, enum InterpolateMethod interpolate, + enum FillMethod fill, AVFrame *in, AVFrame *out); +} DeshakeContext; #define OFFSET(x) offsetof(DeshakeContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM @@ -71,16 +124,16 @@ static const AVOption deshake_options[] = { { "h", "set height for the rectangular search area", OFFSET(ch), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, .flags = FLAGS }, { "rx", "set x for the rectangular search area", OFFSET(rx), AV_OPT_TYPE_INT, {.i64=16}, 0, MAX_R, .flags = FLAGS }, { "ry", "set y for the rectangular search area", OFFSET(ry), AV_OPT_TYPE_INT, {.i64=16}, 0, MAX_R, .flags = FLAGS }, - { "edge", "set edge mode", OFFSET(edge), AV_OPT_TYPE_INT, {.i64=FILL_MIRROR}, FILL_BLANK, FILL_COUNT-1, FLAGS, "edge"}, - { "blank", "fill zeroes at blank locations", 0, AV_OPT_TYPE_CONST, {.i64=FILL_BLANK}, INT_MIN, INT_MAX, FLAGS, "edge" }, - { "original", "original image at blank locations", 0, AV_OPT_TYPE_CONST, {.i64=FILL_ORIGINAL}, INT_MIN, INT_MAX, FLAGS, "edge" }, - { "clamp", "extruded edge value at blank locations", 0, AV_OPT_TYPE_CONST, {.i64=FILL_CLAMP}, INT_MIN, INT_MAX, FLAGS, "edge" }, - { "mirror", "mirrored edge at blank locations", 0, AV_OPT_TYPE_CONST, {.i64=FILL_MIRROR}, INT_MIN, INT_MAX, FLAGS, "edge" }, + { "edge", "set edge mode", OFFSET(edge), AV_OPT_TYPE_INT, {.i64=FILL_MIRROR}, FILL_BLANK, FILL_COUNT-1, FLAGS, .unit = "edge"}, + { "blank", "fill zeroes at blank locations", 0, AV_OPT_TYPE_CONST, {.i64=FILL_BLANK}, INT_MIN, INT_MAX, FLAGS, .unit = "edge" }, + { "original", "original image at blank locations", 0, AV_OPT_TYPE_CONST, {.i64=FILL_ORIGINAL}, INT_MIN, INT_MAX, FLAGS, .unit = "edge" }, + { "clamp", "extruded edge value at blank locations", 0, AV_OPT_TYPE_CONST, {.i64=FILL_CLAMP}, INT_MIN, INT_MAX, FLAGS, .unit = "edge" }, + { "mirror", "mirrored edge at blank locations", 0, AV_OPT_TYPE_CONST, {.i64=FILL_MIRROR}, INT_MIN, INT_MAX, FLAGS, .unit = "edge" }, { "blocksize", "set motion search blocksize", OFFSET(blocksize), AV_OPT_TYPE_INT, {.i64=8}, 4, 128, .flags = FLAGS }, { "contrast", "set contrast threshold for blocks", OFFSET(contrast), AV_OPT_TYPE_INT, {.i64=125}, 1, 255, .flags = FLAGS }, - { "search", "set search strategy", OFFSET(search), AV_OPT_TYPE_INT, {.i64=EXHAUSTIVE}, EXHAUSTIVE, SEARCH_COUNT-1, FLAGS, "smode" }, - { "exhaustive", "exhaustive search", 0, AV_OPT_TYPE_CONST, {.i64=EXHAUSTIVE}, INT_MIN, INT_MAX, FLAGS, "smode" }, - { "less", "less exhaustive search", 0, AV_OPT_TYPE_CONST, {.i64=SMART_EXHAUSTIVE}, INT_MIN, INT_MAX, FLAGS, "smode" }, + { "search", "set search strategy", OFFSET(search), AV_OPT_TYPE_INT, {.i64=EXHAUSTIVE}, EXHAUSTIVE, SEARCH_COUNT-1, FLAGS, .unit = "smode" }, + { "exhaustive", "exhaustive search", 0, AV_OPT_TYPE_CONST, {.i64=EXHAUSTIVE}, INT_MIN, INT_MAX, FLAGS, .unit = "smode" }, + { "less", "less exhaustive search", 0, AV_OPT_TYPE_CONST, {.i64=SMART_EXHAUSTIVE}, INT_MIN, INT_MAX, FLAGS, .unit = "smode" }, { "filename", "set motion search detailed log file name", OFFSET(filename), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, { "opencl", "ignored", OFFSET(opencl), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, .flags = FLAGS }, { NULL } @@ -177,7 +230,6 @@ static void find_block_motion(DeshakeContext *deshake, uint8_t *src1, mv->x = -1; mv->y = -1; } - emms_c(); //av_log(NULL, AV_LOG_ERROR, "%d\n", smallest); //av_log(NULL, AV_LOG_ERROR, "Final: (%d, %d) = %d x %d\n", cx, cy, mv->x, mv->y); } @@ -535,13 +587,6 @@ static const AVFilterPad deshake_inputs[] = { }, }; -static const AVFilterPad deshake_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_deshake = { .name = "deshake", .description = NULL_IF_CONFIG_SMALL("Stabilize shaky video."), @@ -549,7 +594,7 @@ const AVFilter ff_vf_deshake = { .init = init, .uninit = uninit, FILTER_INPUTS(deshake_inputs), - FILTER_OUTPUTS(deshake_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &deshake_class, }; diff --git a/libavfilter/vf_deshake_opencl.c b/libavfilter/vf_deshake_opencl.c index d488da7fa0c..128545a982e 100644 --- a/libavfilter/vf_deshake_opencl.c +++ b/libavfilter/vf_deshake_opencl.c @@ -48,17 +48,16 @@ #include #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" #include "libavutil/mem.h" #include "libavutil/fifo.h" #include "libavutil/common.h" #include "libavutil/avassert.h" +#include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" #include "avfilter.h" #include "framequeue.h" #include "filters.h" #include "transform.h" -#include "formats.h" #include "internal.h" #include "opencl.h" #include "opencl_source.h" @@ -704,7 +703,7 @@ static int minimize_error( total_err += deshake_ctx->ransac_err[j]; } - if (total_err < best_err) { + if (i == 0 || total_err < best_err) { for (int mi = 0; mi < 6; ++mi) { best_model[mi] = model[mi]; } @@ -1251,7 +1250,7 @@ static int deshake_opencl_init(AVFilterContext *avctx) } ctx->sw_format = hw_frames_ctx->sw_format; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_deshake, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_deshake_cl, 1); if (err < 0) goto fail; @@ -1413,13 +1412,6 @@ static int filter_frame(AVFilterLink *link, AVFrame *input_frame) &debug_matches, 1); } -#if FF_API_PKT_DURATION -FF_DISABLE_DEPRECATION_WARNINGS - if (input_frame->pkt_duration) { - duration = input_frame->pkt_duration; - } else -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (input_frame->duration) { duration = input_frame->duration; } else { @@ -2169,5 +2161,6 @@ const AVFilter ff_vf_deshake_opencl = { FILTER_INPUTS(deshake_opencl_inputs), FILTER_OUTPUTS(deshake_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_despill.c b/libavfilter/vf_despill.c index 483d9c85600..7e8ccf7fe8f 100644 --- a/libavfilter/vf_despill.c +++ b/libavfilter/vf_despill.c @@ -19,11 +19,9 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" typedef struct DespillContext { const AVClass *class; @@ -143,9 +141,9 @@ static const AVFilterPad despill_outputs[] = { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption despill_options[] = { - { "type", "set the screen type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "type" }, - { "green", "greenscreen", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "type" }, - { "blue", "bluescreen", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "type" }, + { "type", "set the screen type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "type" }, + { "green", "greenscreen", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "type" }, + { "blue", "bluescreen", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "type" }, { "mix", "set the spillmap mix", OFFSET(spillmix), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS }, { "expand", "set the spillmap expand", OFFSET(spillexpand), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, 1, FLAGS }, { "red", "set red scale", OFFSET(redscale), AV_OPT_TYPE_FLOAT, {.dbl=0}, -100, 100, FLAGS }, diff --git a/libavfilter/vf_detelecine.c b/libavfilter/vf_detelecine.c index e1f0587b6a7..255126da4ee 100644 --- a/libavfilter/vf_detelecine.c +++ b/libavfilter/vf_detelecine.c @@ -58,11 +58,11 @@ typedef struct DetelecineContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption detelecine_options[] = { - {"first_field", "select first field", OFFSET(first_field), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "field"}, - {"top", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"}, - {"t", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"}, - {"bottom", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"}, - {"b", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"}, + {"first_field", "select first field", OFFSET(first_field), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "field"}, + {"top", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "field"}, + {"t", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "field"}, + {"bottom", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "field"}, + {"b", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "field"}, {"pattern", "pattern that describe for how many fields a frame is to be displayed", OFFSET(pattern), AV_OPT_TYPE_STRING, {.str="23"}, 0, 0, FLAGS}, {"start_frame", "position of first frame with respect to the pattern if stream is cut", OFFSET(start_frame), AV_OPT_TYPE_INT, {.i64=0}, 0, 13, FLAGS}, {NULL} diff --git a/libavfilter/vf_displace.c b/libavfilter/vf_displace.c index 11909949b16..93de62fb5cf 100644 --- a/libavfilter/vf_displace.c +++ b/libavfilter/vf_displace.c @@ -18,11 +18,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/imgutils.h" #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "framesync.h" #include "internal.h" #include "video.h" @@ -45,19 +43,18 @@ typedef struct DisplaceContext { uint8_t blank[4]; FFFrameSync fs; - void (*displace)(struct DisplaceContext *s, const AVFrame *in, - const AVFrame *xpic, const AVFrame *ypic, AVFrame *out); + int (*displace_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); } DisplaceContext; #define OFFSET(x) offsetof(DisplaceContext, x) -#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption displace_options[] = { - { "edge", "set edge mode", OFFSET(edge), AV_OPT_TYPE_INT, {.i64=EDGE_SMEAR}, 0, EDGE_NB-1, FLAGS, "edge" }, - { "blank", "", 0, AV_OPT_TYPE_CONST, {.i64=EDGE_BLANK}, 0, 0, FLAGS, "edge" }, - { "smear", "", 0, AV_OPT_TYPE_CONST, {.i64=EDGE_SMEAR}, 0, 0, FLAGS, "edge" }, - { "wrap" , "", 0, AV_OPT_TYPE_CONST, {.i64=EDGE_WRAP}, 0, 0, FLAGS, "edge" }, - { "mirror" , "", 0, AV_OPT_TYPE_CONST, {.i64=EDGE_MIRROR}, 0, 0, FLAGS, "edge" }, + { "edge", "set edge mode", OFFSET(edge), AV_OPT_TYPE_INT, {.i64=EDGE_SMEAR}, 0, EDGE_NB-1, FLAGS, .unit = "edge" }, + { "blank", "", 0, AV_OPT_TYPE_CONST, {.i64=EDGE_BLANK}, 0, 0, FLAGS, .unit = "edge" }, + { "smear", "", 0, AV_OPT_TYPE_CONST, {.i64=EDGE_SMEAR}, 0, 0, FLAGS, .unit = "edge" }, + { "wrap" , "", 0, AV_OPT_TYPE_CONST, {.i64=EDGE_WRAP}, 0, 0, FLAGS, .unit = "edge" }, + { "mirror" , "", 0, AV_OPT_TYPE_CONST, {.i64=EDGE_MIRROR}, 0, 0, FLAGS, .unit = "edge" }, { NULL } }; @@ -76,29 +73,38 @@ static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE }; -static void displace_planar(DisplaceContext *s, const AVFrame *in, - const AVFrame *xpic, const AVFrame *ypic, - AVFrame *out) +typedef struct ThreadData { + AVFrame *in, *xin, *yin, *out; +} ThreadData; + +static int displace_planar(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { - int plane, x, y; + DisplaceContext *s = ctx->priv; + const ThreadData *td = arg; + const AVFrame *in = td->in; + const AVFrame *xin = td->xin; + const AVFrame *yin = td->yin; + const AVFrame *out = td->out; - for (plane = 0; plane < s->nb_planes; plane++) { + for (int plane = 0; plane < s->nb_planes; plane++) { const int h = s->height[plane]; const int w = s->width[plane]; + const int slice_start = (h * jobnr ) / nb_jobs; + const int slice_end = (h * (jobnr+1)) / nb_jobs; const int dlinesize = out->linesize[plane]; const int slinesize = in->linesize[plane]; - const int xlinesize = xpic->linesize[plane]; - const int ylinesize = ypic->linesize[plane]; + const int xlinesize = xin->linesize[plane]; + const int ylinesize = yin->linesize[plane]; const uint8_t *src = in->data[plane]; - const uint8_t *ysrc = ypic->data[plane]; - const uint8_t *xsrc = xpic->data[plane]; - uint8_t *dst = out->data[plane]; + const uint8_t *ysrc = yin->data[plane] + slice_start * ylinesize; + const uint8_t *xsrc = xin->data[plane] + slice_start * xlinesize; + uint8_t *dst = out->data[plane] + slice_start * dlinesize; const uint8_t blank = s->blank[plane]; - for (y = 0; y < h; y++) { + for (int y = slice_start; y < slice_end; y++) { switch (s->edge) { case EDGE_BLANK: - for (x = 0; x < w; x++) { + for (int x = 0; x < w; x++) { int Y = y + ysrc[x] - 128; int X = x + xsrc[x] - 128; @@ -109,14 +115,14 @@ static void displace_planar(DisplaceContext *s, const AVFrame *in, } break; case EDGE_SMEAR: - for (x = 0; x < w; x++) { + for (int x = 0; x < w; x++) { int Y = av_clip(y + ysrc[x] - 128, 0, h - 1); int X = av_clip(x + xsrc[x] - 128, 0, w - 1); dst[x] = src[Y * slinesize + X]; } break; case EDGE_WRAP: - for (x = 0; x < w; x++) { + for (int x = 0; x < w; x++) { int Y = (y + ysrc[x] - 128) % h; int X = (x + xsrc[x] - 128) % w; @@ -128,7 +134,7 @@ static void displace_planar(DisplaceContext *s, const AVFrame *in, } break; case EDGE_MIRROR: - for (x = 0; x < w; x++) { + for (int x = 0; x < w; x++) { int Y = y + ysrc[x] - 128; int X = x + xsrc[x] - 128; @@ -150,31 +156,37 @@ static void displace_planar(DisplaceContext *s, const AVFrame *in, dst += dlinesize; } } + return 0; } -static void displace_packed(DisplaceContext *s, const AVFrame *in, - const AVFrame *xpic, const AVFrame *ypic, - AVFrame *out) +static int displace_packed(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) { + DisplaceContext *s = ctx->priv; + const ThreadData *td = arg; + const AVFrame *in = td->in; + const AVFrame *xin = td->xin; + const AVFrame *yin = td->yin; + const AVFrame *out = td->out; const int step = s->step; const int h = s->height[0]; const int w = s->width[0]; + const int slice_start = (h * jobnr ) / nb_jobs; + const int slice_end = (h * (jobnr+1)) / nb_jobs; const int dlinesize = out->linesize[0]; const int slinesize = in->linesize[0]; - const int xlinesize = xpic->linesize[0]; - const int ylinesize = ypic->linesize[0]; + const int xlinesize = xin->linesize[0]; + const int ylinesize = yin->linesize[0]; const uint8_t *src = in->data[0]; - const uint8_t *ysrc = ypic->data[0]; - const uint8_t *xsrc = xpic->data[0]; + const uint8_t *ysrc = yin->data[0] + slice_start * ylinesize; + const uint8_t *xsrc = xin->data[0] + slice_start * xlinesize; + uint8_t *dst = out->data[0] + slice_start * dlinesize; const uint8_t *blank = s->blank; - uint8_t *dst = out->data[0]; - int c, x, y; - for (y = 0; y < h; y++) { + for (int y = slice_start; y < slice_end; y++) { switch (s->edge) { case EDGE_BLANK: - for (x = 0; x < w; x++) { - for (c = 0; c < s->nb_components; c++) { + for (int x = 0; x < w; x++) { + for (int c = 0; c < s->nb_components; c++) { int Y = y + (ysrc[x * step + c] - 128); int X = x + (xsrc[x * step + c] - 128); @@ -186,8 +198,8 @@ static void displace_packed(DisplaceContext *s, const AVFrame *in, } break; case EDGE_SMEAR: - for (x = 0; x < w; x++) { - for (c = 0; c < s->nb_components; c++) { + for (int x = 0; x < w; x++) { + for (int c = 0; c < s->nb_components; c++) { int Y = av_clip(y + (ysrc[x * step + c] - 128), 0, h - 1); int X = av_clip(x + (xsrc[x * step + c] - 128), 0, w - 1); @@ -196,8 +208,8 @@ static void displace_packed(DisplaceContext *s, const AVFrame *in, } break; case EDGE_WRAP: - for (x = 0; x < w; x++) { - for (c = 0; c < s->nb_components; c++) { + for (int x = 0; x < w; x++) { + for (int c = 0; c < s->nb_components; c++) { int Y = (y + (ysrc[x * step + c] - 128)) % h; int X = (x + (xsrc[x * step + c] - 128)) % w; @@ -210,8 +222,8 @@ static void displace_packed(DisplaceContext *s, const AVFrame *in, } break; case EDGE_MIRROR: - for (x = 0; x < w; x++) { - for (c = 0; c < s->nb_components; c++) { + for (int x = 0; x < w; x++) { + for (int c = 0; c < s->nb_components; c++) { int Y = y + ysrc[x * step + c] - 128; int X = x + xsrc[x * step + c] - 128; @@ -233,6 +245,7 @@ static void displace_packed(DisplaceContext *s, const AVFrame *in, xsrc += xlinesize; dst += dlinesize; } + return 0; } static int process_frame(FFFrameSync *fs) @@ -240,12 +253,12 @@ static int process_frame(FFFrameSync *fs) AVFilterContext *ctx = fs->parent; DisplaceContext *s = fs->opaque; AVFilterLink *outlink = ctx->outputs[0]; - AVFrame *out, *in, *xpic, *ypic; + AVFrame *out, *in, *xin, *yin; int ret; - if ((ret = ff_framesync_get_frame(&s->fs, 0, &in, 0)) < 0 || - (ret = ff_framesync_get_frame(&s->fs, 1, &xpic, 0)) < 0 || - (ret = ff_framesync_get_frame(&s->fs, 2, &ypic, 0)) < 0) + if ((ret = ff_framesync_get_frame(&s->fs, 0, &in, 0)) < 0 || + (ret = ff_framesync_get_frame(&s->fs, 1, &xin, 0)) < 0 || + (ret = ff_framesync_get_frame(&s->fs, 2, &yin, 0)) < 0) return ret; if (ctx->is_disabled) { @@ -253,12 +266,19 @@ static int process_frame(FFFrameSync *fs) if (!out) return AVERROR(ENOMEM); } else { + ThreadData td; + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) return AVERROR(ENOMEM); av_frame_copy_props(out, in); - s->displace(s, in, xpic, ypic, out); + td.in = in; + td.xin = xin; + td.yin = yin; + td.out = out; + ff_filter_execute(ctx, s->displace_slice, &td, NULL, + FFMIN(outlink->h, ff_filter_get_nb_threads(ctx))); } out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base); @@ -276,9 +296,9 @@ static int config_input(AVFilterLink *inlink) s->nb_components = desc->nb_components; if (s->nb_planes > 1 || s->nb_components == 1) - s->displace = displace_planar; + s->displace_slice = displace_planar; else - s->displace = displace_packed; + s->displace_slice = displace_packed; if (!(desc->flags & AV_PIX_FMT_FLAG_RGB)) { s->blank[1] = s->blank[2] = 128; @@ -398,5 +418,7 @@ const AVFilter ff_vf_displace = { FILTER_OUTPUTS(displace_outputs), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &displace_class, - .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | + AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_dnn_classify.c b/libavfilter/vf_dnn_classify.c index d242aebcfbb..11389c029db 100644 --- a/libavfilter/vf_dnn_classify.c +++ b/libavfilter/vf_dnn_classify.c @@ -26,6 +26,7 @@ #include "filters.h" #include "dnn_filter_common.h" #include "internal.h" +#include "video.h" #include "libavutil/time.h" #include "libavutil/avstring.h" #include "libavutil/detection_bbox.h" @@ -44,9 +45,9 @@ typedef struct DnnClassifyContext { #define OFFSET2(x) offsetof(DnnClassifyContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM static const AVOption dnn_classify_options[] = { - { "dnn_backend", "DNN backend", OFFSET(backend_type), AV_OPT_TYPE_INT, { .i64 = 2 }, INT_MIN, INT_MAX, FLAGS, "backend" }, + { "dnn_backend", "DNN backend", OFFSET(backend_type), AV_OPT_TYPE_INT, { .i64 = DNN_OV }, INT_MIN, INT_MAX, FLAGS, .unit = "backend" }, #if (CONFIG_LIBOPENVINO == 1) - { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, "backend" }, + { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_OV }, 0, 0, FLAGS, .unit = "backend" }, #endif DNN_COMMON_OPTIONS { "confidence", "threshold of confidence", OFFSET2(confidence), AV_OPT_TYPE_FLOAT, { .dbl = 0.5 }, 0, 1, FLAGS}, @@ -67,8 +68,8 @@ static int dnn_classify_post_proc(AVFrame *frame, DNNData *output, uint32_t bbox uint32_t label_id; float confidence; AVFrameSideData *sd; - - if (output->channels <= 0) { + int output_size = output->dims[3] * output->dims[2] * output->dims[1]; + if (output_size <= 0) { return -1; } @@ -87,7 +88,7 @@ static int dnn_classify_post_proc(AVFrame *frame, DNNData *output, uint32_t bbox classifications = output->data; label_id = 0; confidence= classifications[0]; - for (int i = 1; i < output->channels; i++) { + for (int i = 1; i < output_size; i++) { if (classifications[i] > confidence) { label_id = i; confidence= classifications[i]; @@ -293,28 +294,14 @@ static av_cold void dnn_classify_uninit(AVFilterContext *context) free_classify_labels(ctx); } -static const AVFilterPad dnn_classify_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -static const AVFilterPad dnn_classify_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_dnn_classify = { .name = "dnn_classify", .description = NULL_IF_CONFIG_SMALL("Apply DNN classify filter to the input."), .priv_size = sizeof(DnnClassifyContext), .init = dnn_classify_init, .uninit = dnn_classify_uninit, - FILTER_INPUTS(dnn_classify_inputs), - FILTER_OUTPUTS(dnn_classify_outputs), + FILTER_INPUTS(ff_video_default_filterpad), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &dnn_classify_class, .activate = dnn_classify_activate, diff --git a/libavfilter/vf_dnn_detect.c b/libavfilter/vf_dnn_detect.c index 7e133f6af55..8f505735272 100644 --- a/libavfilter/vf_dnn_detect.c +++ b/libavfilter/vf_dnn_detect.c @@ -26,9 +26,18 @@ #include "filters.h" #include "dnn_filter_common.h" #include "internal.h" +#include "video.h" #include "libavutil/time.h" #include "libavutil/avstring.h" #include "libavutil/detection_bbox.h" +#include "libavutil/fifo.h" + +typedef enum { + DDMT_SSD, + DDMT_YOLOV1V2, + DDMT_YOLOV3, + DDMT_YOLOV4 +} DNNDetectionModelType; typedef struct DnnDetectContext { const AVClass *class; @@ -37,47 +46,361 @@ typedef struct DnnDetectContext { char *labels_filename; char **labels; int label_count; + DNNDetectionModelType model_type; + int cell_w; + int cell_h; + int nb_classes; + AVFifo *bboxes_fifo; + int scale_width; + int scale_height; + char *anchors_str; + float *anchors; + int nb_anchor; } DnnDetectContext; #define OFFSET(x) offsetof(DnnDetectContext, dnnctx.x) #define OFFSET2(x) offsetof(DnnDetectContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM static const AVOption dnn_detect_options[] = { - { "dnn_backend", "DNN backend", OFFSET(backend_type), AV_OPT_TYPE_INT, { .i64 = 2 }, INT_MIN, INT_MAX, FLAGS, "backend" }, + { "dnn_backend", "DNN backend", OFFSET(backend_type), AV_OPT_TYPE_INT, { .i64 = DNN_OV }, INT_MIN, INT_MAX, FLAGS, .unit = "backend" }, #if (CONFIG_LIBTENSORFLOW == 1) - { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "backend" }, + { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_TF }, 0, 0, FLAGS, .unit = "backend" }, #endif #if (CONFIG_LIBOPENVINO == 1) - { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, "backend" }, + { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_OV }, 0, 0, FLAGS, .unit = "backend" }, #endif DNN_COMMON_OPTIONS { "confidence", "threshold of confidence", OFFSET2(confidence), AV_OPT_TYPE_FLOAT, { .dbl = 0.5 }, 0, 1, FLAGS}, { "labels", "path to labels file", OFFSET2(labels_filename), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, + { "model_type", "DNN detection model type", OFFSET2(model_type), AV_OPT_TYPE_INT, { .i64 = DDMT_SSD }, INT_MIN, INT_MAX, FLAGS, .unit = "model_type" }, + { "ssd", "output shape [1, 1, N, 7]", 0, AV_OPT_TYPE_CONST, { .i64 = DDMT_SSD }, 0, 0, FLAGS, .unit = "model_type" }, + { "yolo", "output shape [1, N*Cx*Cy*DetectionBox]", 0, AV_OPT_TYPE_CONST, { .i64 = DDMT_YOLOV1V2 }, 0, 0, FLAGS, .unit = "model_type" }, + { "yolov3", "outputs shape [1, N*D, Cx, Cy]", 0, AV_OPT_TYPE_CONST, { .i64 = DDMT_YOLOV3 }, 0, 0, FLAGS, .unit = "model_type" }, + { "yolov4", "outputs shape [1, N*D, Cx, Cy]", 0, AV_OPT_TYPE_CONST, { .i64 = DDMT_YOLOV4 }, 0, 0, FLAGS, .unit = "model_type" }, + { "cell_w", "cell width", OFFSET2(cell_w), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INTMAX_MAX, FLAGS }, + { "cell_h", "cell height", OFFSET2(cell_h), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INTMAX_MAX, FLAGS }, + { "nb_classes", "The number of class", OFFSET2(nb_classes), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INTMAX_MAX, FLAGS }, + { "anchors", "anchors, splited by '&'", OFFSET2(anchors_str), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS }, { NULL } }; AVFILTER_DEFINE_CLASS(dnn_detect); -static int dnn_detect_post_proc_ov(AVFrame *frame, DNNData *output, AVFilterContext *filter_ctx) +static inline float sigmoid(float x) { + return 1.f / (1.f + exp(-x)); +} + +static inline float linear(float x) { + return x; +} + +static int dnn_detect_get_label_id(int nb_classes, int cell_size, float *label_data) +{ + float max_prob = 0; + int label_id = 0; + for (int i = 0; i < nb_classes; i++) { + if (label_data[i * cell_size] > max_prob) { + max_prob = label_data[i * cell_size]; + label_id = i; + } + } + return label_id; +} + +static int dnn_detect_parse_anchors(char *anchors_str, float **anchors) +{ + char *saveptr = NULL, *token; + float *anchors_buf; + int nb_anchor = 0, i = 0; + while(anchors_str[i] != '\0') { + if(anchors_str[i] == '&') + nb_anchor++; + i++; + } + nb_anchor++; + anchors_buf = av_mallocz(nb_anchor * sizeof(**anchors)); + if (!anchors_buf) { + return 0; + } + for (int i = 0; i < nb_anchor; i++) { + token = av_strtok(anchors_str, "&", &saveptr); + if (!token) { + av_freep(&anchors_buf); + return 0; + } + anchors_buf[i] = strtof(token, NULL); + anchors_str = NULL; + } + *anchors = anchors_buf; + return nb_anchor; +} + +/* Calculate Intersection Over Union */ +static float dnn_detect_IOU(AVDetectionBBox *bbox1, AVDetectionBBox *bbox2) +{ + float overlapping_width = FFMIN(bbox1->x + bbox1->w, bbox2->x + bbox2->w) - FFMAX(bbox1->x, bbox2->x); + float overlapping_height = FFMIN(bbox1->y + bbox1->h, bbox2->y + bbox2->h) - FFMAX(bbox1->y, bbox2->y); + float intersection_area = + (overlapping_width < 0 || overlapping_height < 0) ? 0 : overlapping_height * overlapping_width; + float union_area = bbox1->w * bbox1->h + bbox2->w * bbox2->h - intersection_area; + return intersection_area / union_area; +} + +static int dnn_detect_parse_yolo_output(AVFrame *frame, DNNData *output, int output_index, + AVFilterContext *filter_ctx) +{ + DnnDetectContext *ctx = filter_ctx->priv; + float conf_threshold = ctx->confidence; + int detection_boxes, box_size; + int cell_w = 0, cell_h = 0, scale_w = 0, scale_h = 0; + int nb_classes = ctx->nb_classes; + float *output_data = output[output_index].data; + float *anchors = ctx->anchors; + AVDetectionBBox *bbox; + float (*post_process_raw_data)(float x) = linear; + int is_NHWC = 0; + + if (ctx->model_type == DDMT_YOLOV1V2) { + cell_w = ctx->cell_w; + cell_h = ctx->cell_h; + scale_w = cell_w; + scale_h = cell_h; + } else { + if (output[output_index].dims[2] != output[output_index].dims[3] && + output[output_index].dims[2] == output[output_index].dims[1]) { + is_NHWC = 1; + cell_w = output[output_index].dims[2]; + cell_h = output[output_index].dims[1]; + } else { + cell_w = output[output_index].dims[3]; + cell_h = output[output_index].dims[2]; + } + scale_w = ctx->scale_width; + scale_h = ctx->scale_height; + } + box_size = nb_classes + 5; + + switch (ctx->model_type) { + case DDMT_YOLOV1V2: + case DDMT_YOLOV3: + post_process_raw_data = linear; + break; + case DDMT_YOLOV4: + post_process_raw_data = sigmoid; + break; + } + + if (!cell_h || !cell_w) { + av_log(filter_ctx, AV_LOG_ERROR, "cell_w and cell_h are detected\n"); + return AVERROR(EINVAL); + } + + if (!nb_classes) { + av_log(filter_ctx, AV_LOG_ERROR, "nb_classes is not set\n"); + return AVERROR(EINVAL); + } + + if (!anchors) { + av_log(filter_ctx, AV_LOG_ERROR, "anchors is not set\n"); + return AVERROR(EINVAL); + } + + if (output[output_index].dims[1] * output[output_index].dims[2] * + output[output_index].dims[3] % (box_size * cell_w * cell_h)) { + av_log(filter_ctx, AV_LOG_ERROR, "wrong cell_w, cell_h or nb_classes\n"); + return AVERROR(EINVAL); + } + detection_boxes = output[output_index].dims[1] * + output[output_index].dims[2] * + output[output_index].dims[3] / box_size / cell_w / cell_h; + + anchors = anchors + (detection_boxes * output_index * 2); + /** + * find all candidate bbox + * yolo output can be reshaped to [B, N*D, Cx, Cy] + * Detection box 'D' has format [`x`, `y`, `h`, `w`, `box_score`, `class_no_1`, ...,] + **/ + for (int box_id = 0; box_id < detection_boxes; box_id++) { + for (int cx = 0; cx < cell_w; cx++) + for (int cy = 0; cy < cell_h; cy++) { + float x, y, w, h, conf; + float *detection_boxes_data; + int label_id; + + if (is_NHWC) { + detection_boxes_data = output_data + + ((cy * cell_w + cx) * detection_boxes + box_id) * box_size; + conf = post_process_raw_data(detection_boxes_data[4]); + } else { + detection_boxes_data = output_data + box_id * box_size * cell_w * cell_h; + conf = post_process_raw_data( + detection_boxes_data[cy * cell_w + cx + 4 * cell_w * cell_h]); + } + + if (is_NHWC) { + x = post_process_raw_data(detection_boxes_data[0]); + y = post_process_raw_data(detection_boxes_data[1]); + w = detection_boxes_data[2]; + h = detection_boxes_data[3]; + label_id = dnn_detect_get_label_id(ctx->nb_classes, 1, detection_boxes_data + 5); + conf = conf * post_process_raw_data(detection_boxes_data[label_id + 5]); + } else { + x = post_process_raw_data(detection_boxes_data[cy * cell_w + cx]); + y = post_process_raw_data(detection_boxes_data[cy * cell_w + cx + cell_w * cell_h]); + w = detection_boxes_data[cy * cell_w + cx + 2 * cell_w * cell_h]; + h = detection_boxes_data[cy * cell_w + cx + 3 * cell_w * cell_h]; + label_id = dnn_detect_get_label_id(ctx->nb_classes, cell_w * cell_h, + detection_boxes_data + cy * cell_w + cx + 5 * cell_w * cell_h); + conf = conf * post_process_raw_data( + detection_boxes_data[cy * cell_w + cx + (label_id + 5) * cell_w * cell_h]); + } + if (conf < conf_threshold) { + continue; + } + + bbox = av_mallocz(sizeof(*bbox)); + if (!bbox) + return AVERROR(ENOMEM); + + bbox->w = exp(w) * anchors[box_id * 2] * frame->width / scale_w; + bbox->h = exp(h) * anchors[box_id * 2 + 1] * frame->height / scale_h; + bbox->x = (cx + x) / cell_w * frame->width - bbox->w / 2; + bbox->y = (cy + y) / cell_h * frame->height - bbox->h / 2; + bbox->detect_confidence = av_make_q((int)(conf * 10000), 10000); + if (ctx->labels && label_id < ctx->label_count) { + av_strlcpy(bbox->detect_label, ctx->labels[label_id], sizeof(bbox->detect_label)); + } else { + snprintf(bbox->detect_label, sizeof(bbox->detect_label), "%d", label_id); + } + + if (av_fifo_write(ctx->bboxes_fifo, &bbox, 1) < 0) { + av_freep(&bbox); + return AVERROR(ENOMEM); + } + bbox = NULL; + } + } + return 0; +} + +static int dnn_detect_fill_side_data(AVFrame *frame, AVFilterContext *filter_ctx) { DnnDetectContext *ctx = filter_ctx->priv; float conf_threshold = ctx->confidence; - int proposal_count = output->height; - int detect_size = output->width; - float *detections = output->data; - int nb_bboxes = 0; - AVFrameSideData *sd; AVDetectionBBox *bbox; + int nb_bboxes = 0; AVDetectionBBoxHeader *header; + if (av_fifo_can_read(ctx->bboxes_fifo) == 0) { + av_log(filter_ctx, AV_LOG_VERBOSE, "nothing detected in this frame.\n"); + return 0; + } - sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DETECTION_BBOXES); - if (sd) { - av_log(filter_ctx, AV_LOG_ERROR, "already have bounding boxes in side data.\n"); - return -1; + /* remove overlap bboxes */ + for (int i = 0; i < av_fifo_can_read(ctx->bboxes_fifo); i++){ + av_fifo_peek(ctx->bboxes_fifo, &bbox, 1, i); + for (int j = 0; j < av_fifo_can_read(ctx->bboxes_fifo); j++) { + AVDetectionBBox *overlap_bbox; + av_fifo_peek(ctx->bboxes_fifo, &overlap_bbox, 1, j); + if (!strcmp(bbox->detect_label, overlap_bbox->detect_label) && + av_cmp_q(bbox->detect_confidence, overlap_bbox->detect_confidence) < 0 && + dnn_detect_IOU(bbox, overlap_bbox) >= conf_threshold) { + bbox->classify_count = -1; // bad result + nb_bboxes++; + break; + } + } + } + nb_bboxes = av_fifo_can_read(ctx->bboxes_fifo) - nb_bboxes; + header = av_detection_bbox_create_side_data(frame, nb_bboxes); + if (!header) { + av_log(filter_ctx, AV_LOG_ERROR, "failed to create side data with %d bounding boxes\n", nb_bboxes); + return -1; + } + av_strlcpy(header->source, ctx->dnnctx.model_filename, sizeof(header->source)); + + while(av_fifo_can_read(ctx->bboxes_fifo)) { + AVDetectionBBox *candidate_bbox; + av_fifo_read(ctx->bboxes_fifo, &candidate_bbox, 1); + + if (nb_bboxes > 0 && candidate_bbox->classify_count != -1) { + bbox = av_get_detection_bbox(header, header->nb_bboxes - nb_bboxes); + memcpy(bbox, candidate_bbox, sizeof(*bbox)); + nb_bboxes--; + } + av_freep(&candidate_bbox); + } + return 0; +} + +static int dnn_detect_post_proc_yolo(AVFrame *frame, DNNData *output, AVFilterContext *filter_ctx) +{ + int ret = 0; + ret = dnn_detect_parse_yolo_output(frame, output, 0, filter_ctx); + if (ret < 0) + return ret; + ret = dnn_detect_fill_side_data(frame, filter_ctx); + if (ret < 0) + return ret; + return 0; +} + +static int dnn_detect_post_proc_yolov3(AVFrame *frame, DNNData *output, + AVFilterContext *filter_ctx, int nb_outputs) +{ + int ret = 0; + for (int i = 0; i < nb_outputs; i++) { + ret = dnn_detect_parse_yolo_output(frame, output, i, filter_ctx); + if (ret < 0) + return ret; } + ret = dnn_detect_fill_side_data(frame, filter_ctx); + if (ret < 0) + return ret; + return 0; +} + +static int dnn_detect_post_proc_ssd(AVFrame *frame, DNNData *output, int nb_outputs, + AVFilterContext *filter_ctx) +{ + DnnDetectContext *ctx = filter_ctx->priv; + float conf_threshold = ctx->confidence; + int proposal_count = 0; + int detect_size = 0; + float *detections = NULL, *labels = NULL; + int nb_bboxes = 0; + AVDetectionBBoxHeader *header; + AVDetectionBBox *bbox; + int scale_w = ctx->scale_width; + int scale_h = ctx->scale_height; + + if (nb_outputs == 1 && output->dims[3] == 7) { + proposal_count = output->dims[2]; + detect_size = output->dims[3]; + detections = output->data; + } else if (nb_outputs == 2 && output[0].dims[3] == 5) { + proposal_count = output[0].dims[2]; + detect_size = output[0].dims[3]; + detections = output[0].data; + labels = output[1].data; + } else if (nb_outputs == 2 && output[1].dims[3] == 5) { + proposal_count = output[1].dims[2]; + detect_size = output[1].dims[3]; + detections = output[1].data; + labels = output[0].data; + } else { + av_log(filter_ctx, AV_LOG_ERROR, "Model output shape doesn't match ssd requirement.\n"); + return AVERROR(EINVAL); + } + + if (proposal_count == 0) + return 0; for (int i = 0; i < proposal_count; ++i) { - float conf = detections[i * detect_size + 2]; + float conf; + if (nb_outputs == 1) + conf = detections[i * detect_size + 2]; + else + conf = detections[i * detect_size + 4]; if (conf < conf_threshold) { continue; } @@ -99,19 +422,30 @@ static int dnn_detect_post_proc_ov(AVFrame *frame, DNNData *output, AVFilterCont for (int i = 0; i < proposal_count; ++i) { int av_unused image_id = (int)detections[i * detect_size + 0]; - int label_id = (int)detections[i * detect_size + 1]; - float conf = detections[i * detect_size + 2]; - float x0 = detections[i * detect_size + 3]; - float y0 = detections[i * detect_size + 4]; - float x1 = detections[i * detect_size + 5]; - float y1 = detections[i * detect_size + 6]; - - bbox = av_get_detection_bbox(header, i); + int label_id; + float conf, x0, y0, x1, y1; + + if (nb_outputs == 1) { + label_id = (int)detections[i * detect_size + 1]; + conf = detections[i * detect_size + 2]; + x0 = detections[i * detect_size + 3]; + y0 = detections[i * detect_size + 4]; + x1 = detections[i * detect_size + 5]; + y1 = detections[i * detect_size + 6]; + } else { + label_id = (int)labels[i]; + x0 = detections[i * detect_size] / scale_w; + y0 = detections[i * detect_size + 1] / scale_h; + x1 = detections[i * detect_size + 2] / scale_w; + y1 = detections[i * detect_size + 3] / scale_h; + conf = detections[i * detect_size + 4]; + } if (conf < conf_threshold) { continue; } + bbox = av_get_detection_bbox(header, header->nb_bboxes - nb_bboxes); bbox->x = (int)(x0 * frame->width); bbox->w = (int)(x1 * frame->width) - bbox->x; bbox->y = (int)(y0 * frame->height); @@ -131,7 +465,40 @@ static int dnn_detect_post_proc_ov(AVFrame *frame, DNNData *output, AVFilterCont break; } } + return 0; +} + +static int dnn_detect_post_proc_ov(AVFrame *frame, DNNData *output, int nb_outputs, + AVFilterContext *filter_ctx) +{ + AVFrameSideData *sd; + DnnDetectContext *ctx = filter_ctx->priv; + int ret = 0; + + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_DETECTION_BBOXES); + if (sd) { + av_log(filter_ctx, AV_LOG_ERROR, "already have bounding boxes in side data.\n"); + return -1; + } + switch (ctx->model_type) { + case DDMT_SSD: + ret = dnn_detect_post_proc_ssd(frame, output, nb_outputs, filter_ctx); + if (ret < 0) + return ret; + break; + case DDMT_YOLOV1V2: + ret = dnn_detect_post_proc_yolo(frame, output, filter_ctx); + if (ret < 0) + return ret; + break; + case DDMT_YOLOV3: + case DDMT_YOLOV4: + ret = dnn_detect_post_proc_yolov3(frame, output, filter_ctx, nb_outputs); + if (ret < 0) + return ret; + break; + } return 0; } @@ -216,7 +583,7 @@ static int dnn_detect_post_proc(AVFrame *frame, DNNData *output, uint32_t nb, AV DnnContext *dnn_ctx = &ctx->dnnctx; switch (dnn_ctx->backend_type) { case DNN_OV: - return dnn_detect_post_proc_ov(frame, output, filter_ctx); + return dnn_detect_post_proc_ov(frame, output, nb, filter_ctx); case DNN_TF: return dnn_detect_post_proc_tf(frame, output, filter_ctx); default: @@ -303,11 +670,6 @@ static int check_output_nb(DnnDetectContext *ctx, DNNBackendType backend_type, i } return 0; case DNN_OV: - if (output_nb != 1) { - av_log(ctx, AV_LOG_ERROR, "Dnn detect filter with openvino backend needs 1 output only, \ - but get %d instead\n", output_nb); - return AVERROR(EINVAL); - } return 0; default: avpriv_report_missing_feature(ctx, "Dnn detect filter does not support current backend\n"); @@ -328,11 +690,22 @@ static av_cold int dnn_detect_init(AVFilterContext *context) ret = check_output_nb(ctx, dnn_ctx->backend_type, dnn_ctx->nb_outputs); if (ret < 0) return ret; + ctx->bboxes_fifo = av_fifo_alloc2(1, sizeof(AVDetectionBBox *), AV_FIFO_FLAG_AUTO_GROW); + if (!ctx->bboxes_fifo) + return AVERROR(ENOMEM); ff_dnn_set_detect_post_proc(&ctx->dnnctx, dnn_detect_post_proc); if (ctx->labels_filename) { return read_detect_label_file(context); } + if (ctx->anchors_str) { + ret = dnn_detect_parse_anchors(ctx->anchors_str, &ctx->anchors); + if (!ctx->anchors) { + av_log(context, AV_LOG_ERROR, "failed to parse anchors_str\n"); + return AVERROR(EINVAL); + } + ctx->nb_anchor = ret; + } return 0; } @@ -432,21 +805,44 @@ static int dnn_detect_activate(AVFilterContext *filter_ctx) static av_cold void dnn_detect_uninit(AVFilterContext *context) { DnnDetectContext *ctx = context->priv; + AVDetectionBBox *bbox; ff_dnn_uninit(&ctx->dnnctx); + while(av_fifo_can_read(ctx->bboxes_fifo)) { + av_fifo_read(ctx->bboxes_fifo, &bbox, 1); + av_freep(&bbox); + } + av_fifo_freep2(&ctx->bboxes_fifo); + av_freep(&ctx->anchors); free_detect_labels(ctx); } +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *context = inlink->dst; + DnnDetectContext *ctx = context->priv; + DNNData model_input; + int ret, width_idx, height_idx; + + ret = ff_dnn_get_input(&ctx->dnnctx, &model_input); + if (ret != 0) { + av_log(ctx, AV_LOG_ERROR, "could not get input from the model\n"); + return ret; + } + width_idx = dnn_get_width_idx_by_layout(model_input.layout); + height_idx = dnn_get_height_idx_by_layout(model_input.layout); + ctx->scale_width = model_input.dims[width_idx] == -1 ? inlink->w : + model_input.dims[width_idx]; + ctx->scale_height = model_input.dims[height_idx] == -1 ? inlink->h : + model_input.dims[height_idx]; + + return 0; +} + static const AVFilterPad dnn_detect_inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -static const AVFilterPad dnn_detect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_input, }, }; @@ -457,7 +853,7 @@ const AVFilter ff_vf_dnn_detect = { .init = dnn_detect_init, .uninit = dnn_detect_uninit, FILTER_INPUTS(dnn_detect_inputs), - FILTER_OUTPUTS(dnn_detect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &dnn_detect_class, .activate = dnn_detect_activate, diff --git a/libavfilter/vf_dnn_processing.c b/libavfilter/vf_dnn_processing.c index 4462915073d..fdac31665e3 100644 --- a/libavfilter/vf_dnn_processing.c +++ b/libavfilter/vf_dnn_processing.c @@ -23,15 +23,14 @@ * implementing a generic image processing filter using deep learning networks. */ -#include "libavformat/avio.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/avassert.h" #include "libavutil/imgutils.h" #include "filters.h" #include "dnn_filter_common.h" -#include "formats.h" #include "internal.h" +#include "video.h" #include "libswscale/swscale.h" #include "libavutil/time.h" @@ -45,13 +44,15 @@ typedef struct DnnProcessingContext { #define OFFSET(x) offsetof(DnnProcessingContext, dnnctx.x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM static const AVOption dnn_processing_options[] = { - { "dnn_backend", "DNN backend", OFFSET(backend_type), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS, "backend" }, - { "native", "native backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "backend" }, + { "dnn_backend", "DNN backend", OFFSET(backend_type), AV_OPT_TYPE_INT, { .i64 = DNN_TF }, INT_MIN, INT_MAX, FLAGS, .unit = "backend" }, #if (CONFIG_LIBTENSORFLOW == 1) - { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "backend" }, + { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_TF }, 0, 0, FLAGS, .unit = "backend" }, #endif #if (CONFIG_LIBOPENVINO == 1) - { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, "backend" }, + { "openvino", "openvino backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_OV }, 0, 0, FLAGS, .unit = "backend" }, +#endif +#if (CONFIG_LIBTORCH == 1) + { "torch", "torch backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = DNN_TH }, 0, 0, FLAGS, "backend" }, #endif DNN_COMMON_OPTIONS { NULL } @@ -79,22 +80,29 @@ static const enum AVPixelFormat pix_fmts[] = { "the frame's format %s does not match " \ "the model input channel %d\n", \ av_get_pix_fmt_name(fmt), \ - model_input->channels); + model_input->dims[dnn_get_channel_idx_by_layout(model_input->layout)]); static int check_modelinput_inlink(const DNNData *model_input, const AVFilterLink *inlink) { AVFilterContext *ctx = inlink->dst; enum AVPixelFormat fmt = inlink->format; + int width_idx, height_idx; + width_idx = dnn_get_width_idx_by_layout(model_input->layout); + height_idx = dnn_get_height_idx_by_layout(model_input->layout); // the design is to add explicit scale filter before this filter - if (model_input->height != -1 && model_input->height != inlink->h) { + if (model_input->dims[height_idx] != -1 && + model_input->dims[height_idx] != inlink->h) { av_log(ctx, AV_LOG_ERROR, "the model requires frame height %d but got %d\n", - model_input->height, inlink->h); + model_input->dims[height_idx], + inlink->h); return AVERROR(EIO); } - if (model_input->width != -1 && model_input->width != inlink->w) { + if (model_input->dims[width_idx] != -1 && + model_input->dims[width_idx] != inlink->w) { av_log(ctx, AV_LOG_ERROR, "the model requires frame width %d but got %d\n", - model_input->width, inlink->w); + model_input->dims[width_idx], + inlink->w); return AVERROR(EIO); } if (model_input->dt != DNN_FLOAT) { @@ -105,7 +113,7 @@ static int check_modelinput_inlink(const DNNData *model_input, const AVFilterLin switch (fmt) { case AV_PIX_FMT_RGB24: case AV_PIX_FMT_BGR24: - if (model_input->channels != 3) { + if (model_input->dims[dnn_get_channel_idx_by_layout(model_input->layout)] != 3) { LOG_FORMAT_CHANNEL_MISMATCH(); return AVERROR(EIO); } @@ -118,7 +126,7 @@ static int check_modelinput_inlink(const DNNData *model_input, const AVFilterLin case AV_PIX_FMT_YUV410P: case AV_PIX_FMT_YUV411P: case AV_PIX_FMT_NV12: - if (model_input->channels != 1) { + if (model_input->dims[dnn_get_channel_idx_by_layout(model_input->layout)] != 1) { LOG_FORMAT_CHANNEL_MISMATCH(); return AVERROR(EIO); } diff --git a/libavfilter/vf_drawbox.c b/libavfilter/vf_drawbox.c index 64eeeece121..27739dc89f3 100644 --- a/libavfilter/vf_drawbox.c +++ b/libavfilter/vf_drawbox.c @@ -36,7 +36,6 @@ #include "libavutil/detection_bbox.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -471,13 +470,6 @@ static const AVFilterPad drawbox_inputs[] = { }, }; -static const AVFilterPad drawbox_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_drawbox = { .name = "drawbox", .description = NULL_IF_CONFIG_SMALL("Draw a colored box on the input video."), @@ -485,7 +477,7 @@ const AVFilter ff_vf_drawbox = { .priv_class = &drawbox_class, .init = init, FILTER_INPUTS(drawbox_inputs), - FILTER_OUTPUTS(drawbox_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, @@ -553,13 +545,6 @@ static const AVFilterPad drawgrid_inputs[] = { }, }; -static const AVFilterPad drawgrid_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_drawgrid = { .name = "drawgrid", .description = NULL_IF_CONFIG_SMALL("Draw a colored grid on the input video."), @@ -567,7 +552,7 @@ const AVFilter ff_vf_drawgrid = { .priv_class = &drawgrid_class, .init = init, FILTER_INPUTS(drawgrid_inputs), - FILTER_OUTPUTS(drawgrid_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = process_command, diff --git a/libavfilter/vf_drawtext.c b/libavfilter/vf_drawtext.c index 50012bb258a..72629df2dcc 100644 --- a/libavfilter/vf_drawtext.c +++ b/libavfilter/vf_drawtext.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2023 Francesco Carusi * Copyright (c) 2011 Stefano Sabatini * Copyright (c) 2010 S.N. Hemanth Meenakshisundaram * Copyright (c) 2003 Gustavo Sverzut Barbieri @@ -46,7 +47,6 @@ #include "libavutil/avstring.h" #include "libavutil/bprint.h" #include "libavutil/common.h" -#include "libavutil/file.h" #include "libavutil/eval.h" #include "libavutil/opt.h" #include "libavutil/random_seed.h" @@ -61,6 +61,7 @@ #include "drawutils.h" #include "formats.h" #include "internal.h" +#include "textutils.h" #include "video.h" #if CONFIG_LIBFRIBIDI @@ -72,16 +73,26 @@ #include FT_GLYPH_H #include FT_STROKER_H +#include +#include + +// Ceiling operation for positive integers division +#define POS_CEIL(x, y) ((x)/(y) + ((x)%(y) != 0)) + static const char *const var_names[] = { "dar", "hsub", "vsub", - "line_h", "lh", ///< line height, same as max_glyph_h + "line_h", "lh", ///< line height "main_h", "h", "H", ///< height of the input video "main_w", "w", "W", ///< width of the input video - "max_glyph_a", "ascent", ///< max glyph ascent - "max_glyph_d", "descent", ///< min glyph descent + "max_glyph_a", "ascent", ///< max glyph ascender + "max_glyph_d", "descent", ///< min glyph descender "max_glyph_h", ///< max glyph height "max_glyph_w", ///< max glyph width + "font_a", ///< font-defined ascent + "font_d", ///< font-defined descent + "top_a", ///< max glyph ascender of the top line + "bottom_d", ///< max glyph descender of the bottom line "n", ///< number of frame "sar", "t", ///< timestamp expressed in seconds @@ -90,11 +101,12 @@ static const char *const var_names[] = { "x", "y", "pict_type", +#if FF_API_FRAME_PKT "pkt_pos", -#if FF_API_PKT_DURATION - "pkt_duration", #endif +#if FF_API_FRAME_PKT "pkt_size", +#endif "duration", NULL }; @@ -125,6 +137,10 @@ enum var_name { VAR_MAX_GLYPH_D, VAR_DESCENT, VAR_MAX_GLYPH_H, VAR_MAX_GLYPH_W, + VAR_FONT_A, + VAR_FONT_D, + VAR_TOP_A, + VAR_BOTTOM_D, VAR_N, VAR_SAR, VAR_T, @@ -133,11 +149,12 @@ enum var_name { VAR_X, VAR_Y, VAR_PICT_TYPE, +#if FF_API_FRAME_PKT VAR_PKT_POS, -#if FF_API_PKT_DURATION - VAR_PKT_DURATION, #endif +#if FF_API_FRAME_PKT VAR_PKT_SIZE, +#endif VAR_DURATION, VAR_VARS_NB }; @@ -148,12 +165,92 @@ enum expansion_mode { EXP_STRFTIME, }; +enum y_alignment { + YA_TEXT, + YA_BASELINE, + YA_FONT, +}; + +enum text_alignment { + TA_LEFT = (1 << 0), + TA_RIGHT = (1 << 1), + TA_TOP = (1 << 2), + TA_BOTTOM = (1 << 3), +}; + +typedef struct HarfbuzzData { + hb_buffer_t* buf; + hb_font_t* font; + unsigned int glyph_count; + hb_glyph_info_t* glyph_info; + hb_glyph_position_t* glyph_pos; +} HarfbuzzData; + +/** Information about a single glyph in a text line */ +typedef struct GlyphInfo { + uint32_t code; ///< the glyph code point + int x; ///< the x position of the glyph + int y; ///< the y position of the glyph + int shift_x64; ///< the horizontal shift of the glyph in 26.6 units + int shift_y64; ///< the vertical shift of the glyph in 26.6 units +} GlyphInfo; + +/** Information about a single line of text */ +typedef struct TextLine { + int offset_left64; ///< offset between the origin and + /// the leftmost pixel of the first glyph + int offset_right64; ///< maximum offset between the origin and + /// the rightmost pixel of the last glyph + int width64; ///< width of the line + HarfbuzzData hb_data; ///< libharfbuzz data of this text line + GlyphInfo* glyphs; ///< array of glyphs in this text line + int cluster_offset; ///< the offset at which this line begins +} TextLine; + +/** A glyph as loaded and rendered using libfreetype */ +typedef struct Glyph { + FT_Glyph glyph; + FT_Glyph border_glyph; + uint32_t code; + unsigned int fontsize; + /** Glyph bitmaps with 1/4 pixel precision in both directions */ + FT_BitmapGlyph bglyph[16]; + /** Outlined glyph bitmaps with 1/4 pixel precision in both directions */ + FT_BitmapGlyph border_bglyph[16]; + FT_BBox bbox; +} Glyph; + +/** Global text metrics */ +typedef struct TextMetrics { + int offset_top64; ///< ascender amount of the first line (in 26.6 units) + int offset_bottom64; ///< descender amount of the last line (in 26.6 units) + int offset_left64; ///< maximum offset between the origin and + /// the leftmost pixel of the first glyph + /// of each line (in 26.6 units) + int offset_right64; ///< maximum offset between the origin and + /// the rightmost pixel of the last glyph + /// of each line (in 26.6 units) + int line_height64; ///< the font-defined line height + int width; ///< width of the longest line - ceil(width64/64) + int height; ///< total height of the text - ceil(height64/64) + + int min_y64; ///< minimum value of bbox.yMin among glyphs (in 26.6 units) + int max_y64; ///< maximum value of bbox.yMax among glyphs (in 26.6 units) + int min_x64; ///< minimum value of bbox.xMin among glyphs (in 26.6 units) + int max_x64; ///< maximum value of bbox.xMax among glyphs (in 26.6 units) + + // Position of the background box (without borders) + int rect_x; ///< x position of the box + int rect_y; ///< y position of the box +} TextMetrics; + typedef struct DrawTextContext { const AVClass *class; int exp_mode; ///< expansion mode to use for the text + FFExpandTextContext expand_text; ///< expand text in case exp_mode == NORMAL int reinit; ///< tells if the filter is being reinited #if CONFIG_LIBFONTCONFIG - uint8_t *font; ///< font to be used + uint8_t *font; ///< font to be used #endif uint8_t *fontfile; ///< font to be used uint8_t *text; ///< text to be drawn @@ -161,11 +258,9 @@ typedef struct DrawTextContext { uint8_t *fontcolor_expr; ///< fontcolor expression to evaluate AVBPrint expanded_fontcolor; ///< used to contain the expanded fontcolor spec int ft_load_flags; ///< flags used for loading fonts, see FT_LOAD_* - FT_Vector *positions; ///< positions for each element in the text - size_t nb_positions; ///< number of elements of positions array char *textfile; ///< file with text to be drawn - int x; ///< x position to start drawing text - int y; ///< y position to start drawing text + double x; ///< x position to start drawing text + double y; ///< y position to start drawing text int max_glyph_w; ///< max glyph width int max_glyph_h; ///< max glyph height int shadowx, shadowy; @@ -177,8 +272,14 @@ typedef struct DrawTextContext { int line_spacing; ///< lines spacing in pixels short int draw_box; ///< draw box around text - true or false - int boxborderw; ///< box border width - int use_kerning; ///< font kerning is used - true/false + char *boxborderw; ///< box border width (padding) + /// allowed formats: "all", "vert|oriz", "top|right|bottom|left" + int bb_top; ///< the size of the top box border + int bb_right; ///< the size of the right box border + int bb_bottom; ///< the size of the bottom box border + int bb_left; ///< the size of the left box border + int box_width; ///< the width of box + int box_height; ///< the height of box int tabsize; ///< tab size int fix_bounds; ///< do we let it go out of frame bounds - t/f @@ -213,57 +314,89 @@ typedef struct DrawTextContext { int text_shaping; ///< 1 to shape the text before drawing it #endif AVDictionary *metadata; + + int boxw; ///< the value of the boxw parameter + int boxh; ///< the value of the boxh parameter + int text_align; ///< the horizontal and vertical text alignment + int y_align; ///< the value of the y_align parameter + + TextLine *lines; ///< computed information about text lines + int line_count; ///< the number of text lines + uint32_t *tab_clusters; ///< the position of tab characters in the text + int tab_count; ///< the number of tab characters + int blank_advance64; ///< the size of the space character + int tab_warning_printed; ///< ensure the tab warning to be printed only once } DrawTextContext; #define OFFSET(x) offsetof(DrawTextContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM +#define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption drawtext_options[]= { - {"fontfile", "set font file", OFFSET(fontfile), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, - {"text", "set text", OFFSET(text), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, - {"textfile", "set text file", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, - {"fontcolor", "set foreground color", OFFSET(fontcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, FLAGS}, + {"fontfile", "set font file", OFFSET(fontfile), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, + {"text", "set text", OFFSET(text), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, TFLAGS}, + {"textfile", "set text file", OFFSET(textfile), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, + {"fontcolor", "set foreground color", OFFSET(fontcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, TFLAGS}, {"fontcolor_expr", "set foreground color expression", OFFSET(fontcolor_expr), AV_OPT_TYPE_STRING, {.str=""}, 0, 0, FLAGS}, - {"boxcolor", "set box color", OFFSET(boxcolor.rgba), AV_OPT_TYPE_COLOR, {.str="white"}, 0, 0, FLAGS}, - {"bordercolor", "set border color", OFFSET(bordercolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, FLAGS}, - {"shadowcolor", "set shadow color", OFFSET(shadowcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, FLAGS}, - {"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 , FLAGS}, - {"boxborderw", "set box border width", OFFSET(boxborderw), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS}, - {"line_spacing", "set line spacing in pixels", OFFSET(line_spacing), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX,FLAGS}, - {"fontsize", "set font size", OFFSET(fontsize_expr), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0 , FLAGS}, - {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS}, - {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, FLAGS}, - {"shadowx", "set shadow x offset", OFFSET(shadowx), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS}, - {"shadowy", "set shadow y offset", OFFSET(shadowy), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS}, - {"borderw", "set border width", OFFSET(borderw), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX , FLAGS}, - {"tabsize", "set tab size", OFFSET(tabsize), AV_OPT_TYPE_INT, {.i64=4}, 0, INT_MAX , FLAGS}, - {"basetime", "set base time", OFFSET(basetime), AV_OPT_TYPE_INT64, {.i64=AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX , FLAGS}, + {"boxcolor", "set box color", OFFSET(boxcolor.rgba), AV_OPT_TYPE_COLOR, {.str="white"}, 0, 0, TFLAGS}, + {"bordercolor", "set border color", OFFSET(bordercolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, TFLAGS}, + {"shadowcolor", "set shadow color", OFFSET(shadowcolor.rgba), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, TFLAGS}, + {"box", "set box", OFFSET(draw_box), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, TFLAGS}, + {"boxborderw", "set box borders width", OFFSET(boxborderw), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, TFLAGS}, + {"line_spacing", "set line spacing in pixels", OFFSET(line_spacing), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, TFLAGS}, + {"fontsize", "set font size", OFFSET(fontsize_expr), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, TFLAGS}, + {"text_align", "set text alignment", OFFSET(text_align), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, (TA_LEFT|TA_RIGHT|TA_TOP|TA_BOTTOM), TFLAGS, .unit = "text_align"}, + { "left", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_LEFT }, .flags = TFLAGS, .unit = "text_align" }, + { "L", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_LEFT }, .flags = TFLAGS, .unit = "text_align" }, + { "right", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_RIGHT }, .flags = TFLAGS, .unit = "text_align" }, + { "R", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_RIGHT }, .flags = TFLAGS, .unit = "text_align" }, + { "center", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = (TA_LEFT|TA_RIGHT) }, .flags = TFLAGS, .unit = "text_align" }, + { "C", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = (TA_LEFT|TA_RIGHT) }, .flags = TFLAGS, .unit = "text_align" }, + { "top", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_TOP }, .flags = TFLAGS, .unit = "text_align" }, + { "T", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_TOP }, .flags = TFLAGS, .unit = "text_align" }, + { "bottom", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_BOTTOM }, .flags = TFLAGS, .unit = "text_align" }, + { "B", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = TA_BOTTOM }, .flags = TFLAGS, .unit = "text_align" }, + { "middle", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = (TA_TOP|TA_BOTTOM) }, .flags = TFLAGS, .unit = "text_align" }, + { "M", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = (TA_TOP|TA_BOTTOM) }, .flags = TFLAGS, .unit = "text_align" }, + {"x", "set x expression", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, TFLAGS}, + {"y", "set y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str="0"}, 0, 0, TFLAGS}, + {"boxw", "set box width", OFFSET(boxw), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, TFLAGS}, + {"boxh", "set box height", OFFSET(boxh), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, TFLAGS}, + {"shadowx", "set shadow x offset", OFFSET(shadowx), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, TFLAGS}, + {"shadowy", "set shadow y offset", OFFSET(shadowy), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, TFLAGS}, + {"borderw", "set border width", OFFSET(borderw), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, TFLAGS}, + {"tabsize", "set tab size", OFFSET(tabsize), AV_OPT_TYPE_INT, {.i64=4}, 0, INT_MAX, TFLAGS}, + {"basetime", "set base time", OFFSET(basetime), AV_OPT_TYPE_INT64, {.i64=AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX, FLAGS}, #if CONFIG_LIBFONTCONFIG { "font", "Font name", OFFSET(font), AV_OPT_TYPE_STRING, { .str = "Sans" }, .flags = FLAGS }, #endif - {"expansion", "set the expansion mode", OFFSET(exp_mode), AV_OPT_TYPE_INT, {.i64=EXP_NORMAL}, 0, 2, FLAGS, "expansion"}, - {"none", "set no expansion", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_NONE}, 0, 0, FLAGS, "expansion"}, - {"normal", "set normal expansion", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_NORMAL}, 0, 0, FLAGS, "expansion"}, - {"strftime", "set strftime expansion (deprecated)", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_STRFTIME}, 0, 0, FLAGS, "expansion"}, + {"expansion", "set the expansion mode", OFFSET(exp_mode), AV_OPT_TYPE_INT, {.i64=EXP_NORMAL}, 0, 2, FLAGS, .unit = "expansion"}, + {"none", "set no expansion", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_NONE}, 0, 0, FLAGS, .unit = "expansion"}, + {"normal", "set normal expansion", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_NORMAL}, 0, 0, FLAGS, .unit = "expansion"}, + {"strftime", "set strftime expansion (deprecated)", OFFSET(exp_mode), AV_OPT_TYPE_CONST, {.i64=EXP_STRFTIME}, 0, 0, FLAGS, .unit = "expansion"}, + {"y_align", "set the y alignment", OFFSET(y_align), AV_OPT_TYPE_INT, {.i64=YA_TEXT}, 0, 2, TFLAGS, .unit = "y_align"}, + {"text", "y is referred to the top of the first text line", OFFSET(y_align), AV_OPT_TYPE_CONST, {.i64=YA_TEXT}, 0, 0, FLAGS, .unit = "y_align"}, + {"baseline", "y is referred to the baseline of the first line", OFFSET(y_align), AV_OPT_TYPE_CONST, {.i64=YA_BASELINE}, 0, 0, FLAGS, .unit = "y_align"}, + {"font", "y is referred to the font defined line metrics", OFFSET(y_align), AV_OPT_TYPE_CONST, {.i64=YA_FONT}, 0, 0, FLAGS, .unit = "y_align"}, {"timecode", "set initial timecode", OFFSET(tc_opt_string), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS}, - {"tc24hmax", "set 24 hours max (timecode only)", OFFSET(tc24hmax), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, - {"timecode_rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS}, - {"r", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS}, - {"rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS}, - {"reload", "reload text file at specified frame interval", OFFSET(reload), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS}, - { "alpha", "apply alpha while rendering", OFFSET(a_expr), AV_OPT_TYPE_STRING, { .str = "1" }, .flags = FLAGS }, - {"fix_bounds", "check and fix text coords to avoid clipping", OFFSET(fix_bounds), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, - {"start_number", "start frame number for n/frame_num variable", OFFSET(start_number), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS}, - {"text_source", "the source of text", OFFSET(text_source_string), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS }, + {"tc24hmax", "set 24 hours max (timecode only)", OFFSET(tc24hmax), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"timecode_rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS}, + {"r", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS}, + {"rate", "set rate (timecode only)", OFFSET(tc_rate), AV_OPT_TYPE_RATIONAL, {.dbl=0}, 0, INT_MAX, FLAGS}, + {"reload", "reload text file at specified frame interval", OFFSET(reload), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS}, + {"alpha", "apply alpha while rendering", OFFSET(a_expr), AV_OPT_TYPE_STRING, {.str = "1"}, .flags = TFLAGS}, + {"fix_bounds", "check and fix text coords to avoid clipping", OFFSET(fix_bounds), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, + {"start_number", "start frame number for n/frame_num variable", OFFSET(start_number), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS}, + {"text_source", "the source of text", OFFSET(text_source_string), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS }, #if CONFIG_LIBFRIBIDI {"text_shaping", "attempt to shape text before drawing", OFFSET(text_shaping), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS}, #endif /* FT_LOAD_* flags */ - { "ft_load_flags", "set font loading flags for libfreetype", OFFSET(ft_load_flags), AV_OPT_TYPE_FLAGS, { .i64 = FT_LOAD_DEFAULT }, 0, INT_MAX, FLAGS, "ft_load_flags" }, + { "ft_load_flags", "set font loading flags for libfreetype", OFFSET(ft_load_flags), AV_OPT_TYPE_FLAGS, { .i64 = FT_LOAD_DEFAULT }, 0, INT_MAX, FLAGS, .unit = "ft_load_flags" }, { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_DEFAULT }, .flags = FLAGS, .unit = "ft_load_flags" }, { "no_scale", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_SCALE }, .flags = FLAGS, .unit = "ft_load_flags" }, { "no_hinting", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = FT_LOAD_NO_HINTING }, .flags = FLAGS, .unit = "ft_load_flags" }, @@ -297,99 +430,15 @@ static const struct ft_error { #define FT_ERRMSG(e) ft_errors[e].err_msg -typedef struct Glyph { - FT_Glyph glyph; - FT_Glyph border_glyph; - uint32_t code; - unsigned int fontsize; - FT_Bitmap bitmap; ///< array holding bitmaps of font - FT_Bitmap border_bitmap; ///< array holding bitmaps of font border - FT_BBox bbox; - int advance; - int bitmap_left; - int bitmap_top; -} Glyph; - static int glyph_cmp(const void *key, const void *b) { const Glyph *a = key, *bb = b; int64_t diff = (int64_t)a->code - (int64_t)bb->code; if (diff != 0) - return diff > 0 ? 1 : -1; + return diff > 0 ? 1 : -1; else - return FFDIFFSIGN((int64_t)a->fontsize, (int64_t)bb->fontsize); -} - -/** - * Load glyphs corresponding to the UTF-32 codepoint code. - */ -static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code) -{ - DrawTextContext *s = ctx->priv; - FT_BitmapGlyph bitmapglyph; - Glyph *glyph; - struct AVTreeNode *node = NULL; - int ret; - - /* load glyph into s->face->glyph */ - if (FT_Load_Char(s->face, code, s->ft_load_flags)) - return AVERROR(EINVAL); - - glyph = av_mallocz(sizeof(*glyph)); - if (!glyph) { - ret = AVERROR(ENOMEM); - goto error; - } - glyph->code = code; - glyph->fontsize = s->fontsize; - - if (FT_Get_Glyph(s->face->glyph, &glyph->glyph)) { - ret = AVERROR(EINVAL); - goto error; - } - if (s->borderw) { - glyph->border_glyph = glyph->glyph; - if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0) || - FT_Glyph_To_Bitmap(&glyph->border_glyph, FT_RENDER_MODE_NORMAL, 0, 1)) { - ret = AVERROR_EXTERNAL; - goto error; - } - bitmapglyph = (FT_BitmapGlyph) glyph->border_glyph; - glyph->border_bitmap = bitmapglyph->bitmap; - } - if (FT_Glyph_To_Bitmap(&glyph->glyph, FT_RENDER_MODE_NORMAL, 0, 1)) { - ret = AVERROR_EXTERNAL; - goto error; - } - bitmapglyph = (FT_BitmapGlyph) glyph->glyph; - - glyph->bitmap = bitmapglyph->bitmap; - glyph->bitmap_left = bitmapglyph->left; - glyph->bitmap_top = bitmapglyph->top; - glyph->advance = s->face->glyph->advance.x >> 6; - - /* measure text height to calculate text_height (or the maximum text height) */ - FT_Glyph_Get_CBox(glyph->glyph, ft_glyph_bbox_pixels, &glyph->bbox); - - /* cache the newly created glyph */ - if (!(node = av_tree_node_alloc())) { - ret = AVERROR(ENOMEM); - goto error; - } - av_tree_insert(&s->glyphs, glyph, glyph_cmp, &node); - - if (glyph_ptr) - *glyph_ptr = glyph; - return 0; - -error: - if (glyph) - av_freep(&glyph->glyph); - - av_freep(&glyph); - av_freep(&node); - return ret; + return FFDIFFSIGN((int64_t)a->fontsize, (int64_t)bb->fontsize); } static av_cold int set_fontsize(AVFilterContext *ctx, unsigned int fontsize) @@ -439,7 +488,6 @@ static av_cold int update_fontsize(AVFilterContext *ctx) return err; size = av_expr_eval(s->fontsize_pexpr, s->var_values, &s->prng); - if (!isnan(size)) { roundedsize = round(size); // test for overflow before cast @@ -447,7 +495,6 @@ static av_cold int update_fontsize(AVFilterContext *ctx) av_log(ctx, AV_LOG_ERROR, "fontsize overflow\n"); return AVERROR(EINVAL); } - fontsize = roundedsize; } } @@ -548,7 +595,7 @@ static int load_font_fontconfig(AVFilterContext *ctx) goto fail; } - av_log(ctx, AV_LOG_INFO, "Using \"%s\"\n", filename); + av_log(ctx, AV_LOG_VERBOSE, "Using \"%s\"\n", filename); if (parse_err) s->default_fontsize = size + 0.5; @@ -579,40 +626,6 @@ static int load_font(AVFilterContext *ctx) return err; } -static inline int is_newline(uint32_t c) -{ - return c == '\n' || c == '\r' || c == '\f' || c == '\v'; -} - -static int load_textfile(AVFilterContext *ctx) -{ - DrawTextContext *s = ctx->priv; - int err; - uint8_t *textbuf; - uint8_t *tmp; - size_t textbuf_size; - - if ((err = av_file_map(s->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) { - av_log(ctx, AV_LOG_ERROR, - "The text file '%s' could not be read or is empty\n", - s->textfile); - return err; - } - - if (textbuf_size > 0 && is_newline(textbuf[textbuf_size - 1])) - textbuf_size--; - if (textbuf_size > SIZE_MAX - 1 || !(tmp = av_realloc(s->text, textbuf_size + 1))) { - av_file_unmap(textbuf, textbuf_size); - return AVERROR(ENOMEM); - } - s->text = tmp; - memcpy(s->text, textbuf, textbuf_size); - s->text[textbuf_size] = 0; - av_file_unmap(textbuf, textbuf_size); - - return 0; -} - #if CONFIG_LIBFRIBIDI static int shape_text(AVFilterContext *ctx) { @@ -665,7 +678,7 @@ static int shape_text(AVFilterContext *ctx) fribidi_shape(flags, embedding_levels, len, ar_props, unicodestr); for (line_end = 0, line_start = 0; line_end < len; line_end++) { - if (is_newline(unicodestr[line_end]) || line_end == len - 1) { + if (ff_is_newline(unicodestr[line_end]) || line_end == len - 1) { if (!fribidi_reorder_line(flags, bidi_types, line_end - line_start + 1, line_start, direction, embedding_levels, unicodestr, @@ -690,6 +703,7 @@ static int shape_text(AVFilterContext *ctx) s->text = tmp; len = fribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8, unicodestr, len, s->text); + ret = 0; out: @@ -711,11 +725,248 @@ static enum AVFrameSideDataType text_source_string_parse(const char *text_source } } +static inline int get_subpixel_idx(int shift_x64, int shift_y64) +{ + int idx = (shift_x64 >> 2) + (shift_y64 >> 4); + return idx; +} + +// Loads and (optionally) renders a glyph +static int load_glyph(AVFilterContext *ctx, Glyph **glyph_ptr, uint32_t code, int8_t shift_x64, int8_t shift_y64) +{ + DrawTextContext *s = ctx->priv; + Glyph dummy = { 0 }; + Glyph *glyph; + FT_Vector shift; + struct AVTreeNode *node = NULL; + int ret = 0; + + /* get glyph */ + dummy.code = code; + dummy.fontsize = s->fontsize; + glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL); + if (!glyph) { + if (FT_Load_Glyph(s->face, code, s->ft_load_flags)) { + return AVERROR(EINVAL); + } + glyph = av_mallocz(sizeof(*glyph)); + if (!glyph) { + ret = AVERROR(ENOMEM); + goto error; + } + glyph->code = code; + glyph->fontsize = s->fontsize; + if (FT_Get_Glyph(s->face->glyph, &glyph->glyph)) { + ret = AVERROR(EINVAL); + goto error; + } + if (s->borderw) { + glyph->border_glyph = glyph->glyph; + if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0)) { + ret = AVERROR_EXTERNAL; + goto error; + } + } + /* measure text height to calculate text_height (or the maximum text height) */ + FT_Glyph_Get_CBox(glyph->glyph, FT_GLYPH_BBOX_SUBPIXELS, &glyph->bbox); + + /* cache the newly created glyph */ + if (!(node = av_tree_node_alloc())) { + ret = AVERROR(ENOMEM); + goto error; + } + av_tree_insert(&s->glyphs, glyph, glyph_cmp, &node); + } else { + if (s->borderw && !glyph->border_glyph) { + glyph->border_glyph = glyph->glyph; + if (FT_Glyph_StrokeBorder(&glyph->border_glyph, s->stroker, 0, 0)) { + ret = AVERROR_EXTERNAL; + goto error; + } + } + } + + // Check if a bitmap is needed + if (shift_x64 >= 0 && shift_y64 >= 0) { + // Get the bitmap subpixel index (0 -> 15) + int idx = get_subpixel_idx(shift_x64, shift_y64); + shift.x = shift_x64; + shift.y = shift_y64; + + if (!glyph->bglyph[idx]) { + FT_Glyph tmp_glyph = glyph->glyph; + if (FT_Glyph_To_Bitmap(&tmp_glyph, FT_RENDER_MODE_NORMAL, &shift, 0)) { + ret = AVERROR_EXTERNAL; + goto error; + } + glyph->bglyph[idx] = (FT_BitmapGlyph)tmp_glyph; + if (glyph->bglyph[idx]->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { + av_log(ctx, AV_LOG_ERROR, "Monocromatic (1bpp) fonts are not supported.\n"); + ret = AVERROR(EINVAL); + goto error; + } + } + if (s->borderw && !glyph->border_bglyph[idx]) { + FT_Glyph tmp_glyph = glyph->border_glyph; + if (FT_Glyph_To_Bitmap(&tmp_glyph, FT_RENDER_MODE_NORMAL, &shift, 0)) { + ret = AVERROR_EXTERNAL; + goto error; + } + glyph->border_bglyph[idx] = (FT_BitmapGlyph)tmp_glyph; + } + } + if (glyph_ptr) { + *glyph_ptr = glyph; + } + return 0; + +error: + if (glyph && glyph->glyph) + FT_Done_Glyph(glyph->glyph); + + av_freep(&glyph); + av_freep(&node); + return ret; +} + +// Convert a string formatted as "n1|n2|...|nN" into an integer array +static int string_to_array(const char *source, int *result, int result_size) +{ + int counter = 0, size = strlen(source) + 1; + char *saveptr, *curval, *dup = av_malloc(size); + if (!dup) + return 0; + av_strlcpy(dup, source, size); + if (result_size > 0 && (curval = av_strtok(dup, "|", &saveptr))) { + do { + result[counter++] = atoi(curval); + } while ((curval = av_strtok(NULL, "|", &saveptr)) && counter < result_size); + } + av_free(dup); + return counter; +} + +static int func_pict_type(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv) +{ + DrawTextContext *s = ((AVFilterContext *)ctx)->priv; + + av_bprintf(bp, "%c", av_get_picture_type_char(s->var_values[VAR_PICT_TYPE])); + return 0; +} + +static int func_pts(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv) +{ + DrawTextContext *s = ((AVFilterContext *)ctx)->priv; + const char *fmt; + const char *strftime_fmt = NULL; + const char *delta = NULL; + double pts = s->var_values[VAR_T]; + + // argv: pts, FMT, [DELTA, 24HH | strftime_fmt] + + fmt = argc >= 1 ? argv[0] : "flt"; + if (argc >= 2) { + delta = argv[1]; + } + if (argc >= 3) { + if (!strcmp(fmt, "hms")) { + if (!strcmp(argv[2], "24HH")) { + av_log(ctx, AV_LOG_WARNING, "pts third argument 24HH is deprected, use pts:hms24hh instead\n"); + fmt = "hms24"; + } else { + av_log(ctx, AV_LOG_ERROR, "Invalid argument '%s', '24HH' was expected\n", argv[2]); + return AVERROR(EINVAL); + } + } else { + strftime_fmt = argv[2]; + } + } + + return ff_print_pts(ctx, bp, pts, delta, fmt, strftime_fmt); +} + +static int func_frame_num(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv) +{ + DrawTextContext *s = ((AVFilterContext *)ctx)->priv; + + av_bprintf(bp, "%d", (int)s->var_values[VAR_N]); + return 0; +} + +static int func_metadata(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv) +{ + DrawTextContext *s = ((AVFilterContext *)ctx)->priv; + AVDictionaryEntry *e = av_dict_get(s->metadata, argv[0], NULL, 0); + + if (e && e->value) + av_bprintf(bp, "%s", e->value); + else if (argc >= 2) + av_bprintf(bp, "%s", argv[1]); + return 0; +} + +static int func_strftime(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv) +{ + const char *strftime_fmt = argc ? argv[0] : NULL; + + return ff_print_time(ctx, bp, strftime_fmt, !strcmp(function_name, "localtime")); +} + +static int func_eval_expr(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv) +{ + DrawTextContext *s = ((AVFilterContext *)ctx)->priv; + + return ff_print_eval_expr(ctx, bp, argv[0], + fun2_names, fun2, + var_names, s->var_values, &s->prng); +} + +static int func_eval_expr_int_format(void *ctx, AVBPrint *bp, const char *function_name, unsigned argc, char **argv) +{ + DrawTextContext *s = ((AVFilterContext *)ctx)->priv; + int ret; + int positions = -1; + + /* + * argv[0] expression to be converted to `int` + * argv[1] format: 'x', 'X', 'd' or 'u' + * argv[2] positions printed (optional) + */ + + if (argc == 3) { + ret = sscanf(argv[2], "%u", &positions); + if (ret != 1) { + av_log(ctx, AV_LOG_ERROR, "expr_int_format(): Invalid number of positions" + " to print: '%s'\n", argv[2]); + return AVERROR(EINVAL); + } + } + + return ff_print_formatted_eval_expr(ctx, bp, argv[0], + fun2_names, fun2, + var_names, s->var_values, + &s->prng, + argv[1][0], positions); +} + +static FFExpandTextFunction expand_text_functions[] = { + { "e", 1, 1, func_eval_expr }, + { "eif", 2, 3, func_eval_expr_int_format }, + { "expr", 1, 1, func_eval_expr }, + { "expr_int_format", 2, 3, func_eval_expr_int_format }, + { "frame_num", 0, 0, func_frame_num }, + { "gmtime", 0, 1, func_strftime }, + { "localtime", 0, 1, func_strftime }, + { "metadata", 1, 2, func_metadata }, + { "n", 0, 0, func_frame_num }, + { "pict_type", 0, 0, func_pict_type }, + { "pts", 0, 3, func_pts } +}; + static av_cold int init(AVFilterContext *ctx) { int err; DrawTextContext *s = ctx->priv; - Glyph *glyph; av_expr_free(s->fontsize_pexpr); s->fontsize_pexpr = NULL; @@ -734,7 +985,7 @@ static av_cold int init(AVFilterContext *ctx) "Both text and text file provided. Please provide only one\n"); return AVERROR(EINVAL); } - if ((err = load_textfile(ctx)) < 0) + if ((err = ff_load_textfile(ctx, (const char *)s->textfile, &s->text, NULL)) < 0) return err; } @@ -777,6 +1028,12 @@ static av_cold int init(AVFilterContext *ctx) return AVERROR(EINVAL); } + s->expand_text = (FFExpandTextContext) { + .log_ctx = ctx, + .functions = expand_text_functions, + .functions_nb = FF_ARRAY_ELEMS(expand_text_functions) + }; + #if CONFIG_LIBFRIBIDI if (s->text_shaping) if ((err = shape_text(ctx)) < 0) @@ -795,26 +1052,19 @@ static av_cold int init(AVFilterContext *ctx) if ((err = update_fontsize(ctx)) < 0) return err; + // Always init the stroker, may be needed if borderw is set via command + if (FT_Stroker_New(s->library, &s->stroker)) { + av_log(ctx, AV_LOG_ERROR, "Could not init FT stroker\n"); + return AVERROR_EXTERNAL; + } + if (s->borderw) { - if (FT_Stroker_New(s->library, &s->stroker)) { - av_log(ctx, AV_LOG_ERROR, "Coult not init FT stroker\n"); - return AVERROR_EXTERNAL; - } FT_Stroker_Set(s->stroker, s->borderw << 6, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); } - s->use_kerning = FT_HAS_KERNING(s->face); - /* load the fallback glyph with code 0 */ - load_glyph(ctx, NULL, 0); - - /* set the tabsize in pixels */ - if ((err = load_glyph(ctx, &glyph, ' ')) < 0) { - av_log(ctx, AV_LOG_ERROR, "Could not set tabsize.\n"); - return err; - } - s->tabsize *= glyph->advance; + load_glyph(ctx, NULL, 0, 0, 0); if (s->exp_mode == EXP_STRFTIME && (strchr(s->text, '%') || strchr(s->text, '\\'))) @@ -831,12 +1081,37 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); } +static int glyph_enu_border_free(void *opaque, void *elem) +{ + Glyph *glyph = elem; + + if (glyph->border_glyph != NULL) { + for (int t = 0; t < 16; ++t) { + if (glyph->border_bglyph[t] != NULL) { + FT_Done_Glyph((FT_Glyph)glyph->border_bglyph[t]); + glyph->border_bglyph[t] = NULL; + } + } + FT_Done_Glyph(glyph->border_glyph); + glyph->border_glyph = NULL; + } + return 0; +} + static int glyph_enu_free(void *opaque, void *elem) { Glyph *glyph = elem; FT_Done_Glyph(glyph->glyph); FT_Done_Glyph(glyph->border_glyph); + for (int t = 0; t < 16; ++t) { + if (glyph->bglyph[t] != NULL) { + FT_Done_Glyph((FT_Glyph)glyph->bglyph[t]); + } + if (glyph->border_bglyph[t] != NULL) { + FT_Done_Glyph((FT_Glyph)glyph->border_bglyph[t]); + } + } av_free(elem); return 0; } @@ -852,9 +1127,6 @@ static av_cold void uninit(AVFilterContext *ctx) s->x_pexpr = s->y_pexpr = s->a_pexpr = s->fontsize_pexpr = NULL; - av_freep(&s->positions); - s->nb_positions = 0; - av_tree_enumerate(s->glyphs, NULL, NULL, glyph_enu_free); av_tree_destroy(s->glyphs); s->glyphs = NULL; @@ -874,21 +1146,21 @@ static int config_input(AVFilterLink *inlink) char *expr; int ret; - ff_draw_init(&s->dc, inlink->format, FF_DRAW_PROCESS_ALPHA); + ff_draw_init2(&s->dc, inlink->format, inlink->colorspace, inlink->color_range, FF_DRAW_PROCESS_ALPHA); ff_draw_color(&s->dc, &s->fontcolor, s->fontcolor.rgba); ff_draw_color(&s->dc, &s->shadowcolor, s->shadowcolor.rgba); ff_draw_color(&s->dc, &s->bordercolor, s->bordercolor.rgba); ff_draw_color(&s->dc, &s->boxcolor, s->boxcolor.rgba); - s->var_values[VAR_w] = s->var_values[VAR_W] = s->var_values[VAR_MAIN_W] = inlink->w; - s->var_values[VAR_h] = s->var_values[VAR_H] = s->var_values[VAR_MAIN_H] = inlink->h; - s->var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1; - s->var_values[VAR_DAR] = (double)inlink->w / inlink->h * s->var_values[VAR_SAR]; - s->var_values[VAR_HSUB] = 1 << s->dc.hsub_max; - s->var_values[VAR_VSUB] = 1 << s->dc.vsub_max; - s->var_values[VAR_X] = NAN; - s->var_values[VAR_Y] = NAN; - s->var_values[VAR_T] = NAN; + s->var_values[VAR_w] = s->var_values[VAR_W] = s->var_values[VAR_MAIN_W] = inlink->w; + s->var_values[VAR_h] = s->var_values[VAR_H] = s->var_values[VAR_MAIN_H] = inlink->h; + s->var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? av_q2d(inlink->sample_aspect_ratio) : 1; + s->var_values[VAR_DAR] = (double)inlink->w / inlink->h * s->var_values[VAR_SAR]; + s->var_values[VAR_HSUB] = 1 << s->dc.hsub_max; + s->var_values[VAR_VSUB] = 1 << s->dc.vsub_max; + s->var_values[VAR_X] = NAN; + s->var_values[VAR_Y] = NAN; + s->var_values[VAR_T] = NAN; av_lfg_init(&s->prng, av_get_random_seed()); @@ -948,8 +1220,23 @@ static int command(AVFilterContext *ctx, const char *cmd, const char *arg, char ctx->priv = new; return config_input(ctx->inputs[0]); - } else - return AVERROR(ENOSYS); + } else { + int old_borderw = old->borderw; + if ((ret = ff_filter_process_command(ctx, cmd, arg, res, res_len, flags)) < 0) { + return ret; + } + if (old->borderw != old_borderw) { + FT_Stroker_Set(old->stroker, old->borderw << 6, FT_STROKER_LINECAP_ROUND, + FT_STROKER_LINEJOIN_ROUND, 0); + // Dispose the old border glyphs + av_tree_enumerate(old->glyphs, NULL, NULL, glyph_enu_border_free); + } else if (strcmp(cmd, "fontsize") == 0) { + av_expr_free(old->fontsize_pexpr); + old->fontsize_pexpr = NULL; + old->blank_advance64 = 0; + } + return config_input(ctx->inputs[0]); + } fail: av_log(ctx, AV_LOG_ERROR, "Failed to process command. Continuing with existing parameters.\n"); @@ -957,452 +1244,318 @@ static int command(AVFilterContext *ctx, const char *cmd, const char *arg, char return ret; } -static int func_pict_type(AVFilterContext *ctx, AVBPrint *bp, - char *fct, unsigned argc, char **argv, int tag) -{ - DrawTextContext *s = ctx->priv; - - av_bprintf(bp, "%c", av_get_picture_type_char(s->var_values[VAR_PICT_TYPE])); - return 0; -} - -static int func_pts(AVFilterContext *ctx, AVBPrint *bp, - char *fct, unsigned argc, char **argv, int tag) +static void update_color_with_alpha(DrawTextContext *s, FFDrawColor *color, const FFDrawColor incolor) { - DrawTextContext *s = ctx->priv; - const char *fmt; - double pts = s->var_values[VAR_T]; - int ret; - - fmt = argc >= 1 ? argv[0] : "flt"; - if (argc >= 2) { - int64_t delta; - if ((ret = av_parse_time(&delta, argv[1], 1)) < 0) { - av_log(ctx, AV_LOG_ERROR, "Invalid delta '%s'\n", argv[1]); - return ret; - } - pts += (double)delta / AV_TIME_BASE; - } - if (!strcmp(fmt, "flt")) { - av_bprintf(bp, "%.6f", pts); - } else if (!strcmp(fmt, "hms")) { - if (isnan(pts)) { - av_bprintf(bp, " ??:??:??.???"); - } else { - int64_t ms = llrint(pts * 1000); - char sign = ' '; - if (ms < 0) { - sign = '-'; - ms = -ms; - } - if (argc >= 3) { - if (!strcmp(argv[2], "24HH")) { - ms %= 24 * 60 * 60 * 1000; - } else { - av_log(ctx, AV_LOG_ERROR, "Invalid argument '%s'\n", argv[2]); - return AVERROR(EINVAL); - } - } - av_bprintf(bp, "%c%02d:%02d:%02d.%03d", sign, - (int)(ms / (60 * 60 * 1000)), - (int)(ms / (60 * 1000)) % 60, - (int)(ms / 1000) % 60, - (int)(ms % 1000)); - } - } else if (!strcmp(fmt, "localtime") || - !strcmp(fmt, "gmtime")) { - struct tm tm; - time_t ms = (time_t)pts; - const char *timefmt = argc >= 3 ? argv[2] : "%Y-%m-%d %H:%M:%S"; - if (!strcmp(fmt, "localtime")) - localtime_r(&ms, &tm); - else - gmtime_r(&ms, &tm); - av_bprint_strftime(bp, timefmt, &tm); - } else { - av_log(ctx, AV_LOG_ERROR, "Invalid format '%s'\n", fmt); - return AVERROR(EINVAL); - } - return 0; + *color = incolor; + color->rgba[3] = (color->rgba[3] * s->alpha) / 255; + ff_draw_color(&s->dc, color, color->rgba); } -static int func_frame_num(AVFilterContext *ctx, AVBPrint *bp, - char *fct, unsigned argc, char **argv, int tag) +static void update_alpha(DrawTextContext *s) { - DrawTextContext *s = ctx->priv; - - av_bprintf(bp, "%d", (int)s->var_values[VAR_N]); - return 0; -} + double alpha = av_expr_eval(s->a_pexpr, s->var_values, &s->prng); -static int func_metadata(AVFilterContext *ctx, AVBPrint *bp, - char *fct, unsigned argc, char **argv, int tag) -{ - DrawTextContext *s = ctx->priv; - AVDictionaryEntry *e = av_dict_get(s->metadata, argv[0], NULL, 0); + if (isnan(alpha)) + return; - if (e && e->value) - av_bprintf(bp, "%s", e->value); - else if (argc >= 2) - av_bprintf(bp, "%s", argv[1]); - return 0; + if (alpha >= 1.0) + s->alpha = 255; + else if (alpha <= 0) + s->alpha = 0; + else + s->alpha = 256 * alpha; } -static int func_strftime(AVFilterContext *ctx, AVBPrint *bp, - char *fct, unsigned argc, char **argv, int tag) +static int draw_glyphs(DrawTextContext *s, AVFrame *frame, + FFDrawColor *color, + TextMetrics *metrics, + int x, int y, int borderw) { - const char *fmt = argc ? argv[0] : "%Y-%m-%d %H:%M:%S"; - const char *fmt_begin = fmt; - int64_t unow; - time_t now; - struct tm tm; - const char *begin; - const char *tmp; - int len; - int div; - AVBPrint fmt_bp; - - av_bprint_init(&fmt_bp, 0, AV_BPRINT_SIZE_UNLIMITED); - - unow = av_gettime(); - now = unow / 1000000; - if (tag == 'L' || tag == 'm') - localtime_r(&now, &tm); - else - tm = *gmtime_r(&now, &tm); - - // manually parse format for %N (fractional seconds) - begin = fmt; - while ((begin = strchr(begin, '%'))) { - tmp = begin + 1; - len = 0; - - // skip escaped "%%" - if (*tmp == '%') { - begin = tmp + 1; - continue; - } - - // count digits between % and possible N - while (*tmp != '\0' && av_isdigit((int)*tmp)) { - len++; - tmp++; - } + int g, l, x1, y1, w1, h1, idx; + int dx = 0, dy = 0, pdx = 0; + GlyphInfo *info; + Glyph dummy = { 0 }, *glyph; + FT_Bitmap bitmap; + FT_BitmapGlyph b_glyph; + uint8_t j_left = 0, j_right = 0, j_top = 0, j_bottom = 0; + int line_w, offset_y = 0; + int clip_x = 0, clip_y = 0; + + j_left = !!(s->text_align & TA_LEFT); + j_right = !!(s->text_align & TA_RIGHT); + j_top = !!(s->text_align & TA_TOP); + j_bottom = !!(s->text_align & TA_BOTTOM); + + if (j_top && j_bottom) { + offset_y = (s->box_height - metrics->height) / 2; + } else if (j_bottom) { + offset_y = s->box_height - metrics->height; + } - // N encountered, insert time - if (*tmp == 'N') { - int num_digits = 3; // default show millisecond [1,6] + if ((!j_left || j_right) && !s->tab_warning_printed && s->tab_count > 0) { + s->tab_warning_printed = 1; + av_log(s, AV_LOG_WARNING, "Tab characters are only supported with left horizontal alignment\n"); + } - // if digit given, expect [1,6], warn & clamp otherwise - if (len == 1) { - num_digits = av_clip(*(begin + 1) - '0', 1, 6); - } else if (len > 1) { - av_log(ctx, AV_LOG_WARNING, "Invalid number of decimals for %%N, using default of %i\n", num_digits); + clip_x = FFMIN(metrics->rect_x + s->box_width + s->bb_right, frame->width); + clip_y = FFMIN(metrics->rect_y + s->box_height + s->bb_bottom, frame->height); + + for (l = 0; l < s->line_count; ++l) { + TextLine *line = &s->lines[l]; + line_w = POS_CEIL(line->width64, 64); + for (g = 0; g < line->hb_data.glyph_count; ++g) { + info = &line->glyphs[g]; + dummy.fontsize = s->fontsize; + dummy.code = info->code; + glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL); + if (!glyph) { + return AVERROR(EINVAL); } - len += 2; // add % and N to get length of string part + idx = get_subpixel_idx(info->shift_x64, info->shift_y64); + b_glyph = borderw ? glyph->border_bglyph[idx] : glyph->bglyph[idx]; + bitmap = b_glyph->bitmap; + x1 = x + info->x + b_glyph->left; + y1 = y + info->y - b_glyph->top + offset_y; + w1 = bitmap.width; + h1 = bitmap.rows; + + if (j_left && j_right) { + x1 += (s->box_width - line_w) / 2; + } else if (j_right) { + x1 += s->box_width - line_w; + } - div = pow(10, 6 - num_digits); + // Offset of the glyph's bitmap in the visible region + dx = dy = 0; + if (x1 < metrics->rect_x - s->bb_left) { + dx = metrics->rect_x - s->bb_left - x1; + x1 = metrics->rect_x - s->bb_left; + } + if (y1 < metrics->rect_y - s->bb_top) { + dy = metrics->rect_y - s->bb_top - y1; + y1 = metrics->rect_y - s->bb_top; + } - av_bprintf(&fmt_bp, "%.*s%0*d", (int)(begin - fmt_begin), fmt_begin, num_digits, (int)(unow % 1000000) / div); + // check if the glyph is empty or out of the clipping region + if (dx >= w1 || dy >= h1 || x1 >= clip_x || y1 >= clip_y) { + continue; + } - begin += len; - fmt_begin = begin; + pdx = dx + dy * bitmap.pitch; + w1 = FFMIN(clip_x - x1, w1 - dx); + h1 = FFMIN(clip_y - y1, h1 - dy); - continue; + ff_blend_mask(&s->dc, color, frame->data, frame->linesize, clip_x, clip_y, + bitmap.buffer + pdx, bitmap.pitch, w1, h1, 3, 0, x1, y1); } - - begin = tmp; } - av_bprintf(&fmt_bp, "%s", fmt_begin); - if (!av_bprint_is_complete(&fmt_bp)) { - av_log(ctx, AV_LOG_WARNING, "Format string truncated at %u/%u.", fmt_bp.size, fmt_bp.len); - } - - av_bprint_strftime(bp, fmt_bp.str, &tm); - - av_bprint_finalize(&fmt_bp, NULL); - return 0; } -static int func_eval_expr(AVFilterContext *ctx, AVBPrint *bp, - char *fct, unsigned argc, char **argv, int tag) +// Shapes a line of text using libharfbuzz +static int shape_text_hb(DrawTextContext *s, HarfbuzzData* hb, const char* text, int textLen) { - DrawTextContext *s = ctx->priv; - double res; - int ret; - - ret = av_expr_parse_and_eval(&res, argv[0], var_names, s->var_values, - NULL, NULL, fun2_names, fun2, - &s->prng, 0, ctx); - if (ret < 0) - av_log(ctx, AV_LOG_ERROR, - "Expression '%s' for the expr text expansion function is not valid\n", - argv[0]); - else - av_bprintf(bp, "%f", res); - - return ret; -} - -static int func_eval_expr_int_format(AVFilterContext *ctx, AVBPrint *bp, - char *fct, unsigned argc, char **argv, int tag) -{ - DrawTextContext *s = ctx->priv; - double res; - int intval; - int ret; - unsigned int positions = 0; - char fmt_str[30] = "%"; - - /* - * argv[0] expression to be converted to `int` - * argv[1] format: 'x', 'X', 'd' or 'u' - * argv[2] positions printed (optional) - */ - - ret = av_expr_parse_and_eval(&res, argv[0], var_names, s->var_values, - NULL, NULL, fun2_names, fun2, - &s->prng, 0, ctx); - if (ret < 0) { - av_log(ctx, AV_LOG_ERROR, - "Expression '%s' for the expr text expansion function is not valid\n", - argv[0]); - return ret; - } - - if (!strchr("xXdu", argv[1][0])) { - av_log(ctx, AV_LOG_ERROR, "Invalid format '%c' specified," - " allowed values: 'x', 'X', 'd', 'u'\n", argv[1][0]); - return AVERROR(EINVAL); - } - - if (argc == 3) { - ret = sscanf(argv[2], "%u", &positions); - if (ret != 1) { - av_log(ctx, AV_LOG_ERROR, "expr_int_format(): Invalid number of positions" - " to print: '%s'\n", argv[2]); - return AVERROR(EINVAL); - } + hb->buf = hb_buffer_create(); + if(!hb_buffer_allocation_successful(hb->buf)) { + return AVERROR(ENOMEM); } - - feclearexcept(FE_ALL_EXCEPT); - intval = res; -#if defined(FE_INVALID) && defined(FE_OVERFLOW) && defined(FE_UNDERFLOW) - if ((ret = fetestexcept(FE_INVALID|FE_OVERFLOW|FE_UNDERFLOW))) { - av_log(ctx, AV_LOG_ERROR, "Conversion of floating-point result to int failed. Control register: 0x%08x. Conversion result: %d\n", ret, intval); - return AVERROR(EINVAL); + hb_buffer_set_direction(hb->buf, HB_DIRECTION_LTR); + hb_buffer_set_script(hb->buf, HB_SCRIPT_LATIN); + hb_buffer_set_language(hb->buf, hb_language_from_string("en", -1)); + hb_buffer_guess_segment_properties(hb->buf); + hb->font = hb_ft_font_create(s->face, NULL); + if(hb->font == NULL) { + return AVERROR(ENOMEM); } -#endif - - if (argc == 3) - av_strlcatf(fmt_str, sizeof(fmt_str), "0%u", positions); - av_strlcatf(fmt_str, sizeof(fmt_str), "%c", argv[1][0]); - - av_log(ctx, AV_LOG_DEBUG, "Formatting value %f (expr '%s') with spec '%s'\n", - res, argv[0], fmt_str); - - av_bprintf(bp, fmt_str, intval); + hb_ft_font_set_funcs(hb->font); + hb_buffer_add_utf8(hb->buf, text, textLen, 0, -1); + hb_shape(hb->font, hb->buf, NULL, 0); + hb->glyph_info = hb_buffer_get_glyph_infos(hb->buf, &hb->glyph_count); + hb->glyph_pos = hb_buffer_get_glyph_positions(hb->buf, &hb->glyph_count); return 0; } -static const struct drawtext_function { - const char *name; - unsigned argc_min, argc_max; - int tag; /**< opaque argument to func */ - int (*func)(AVFilterContext *, AVBPrint *, char *, unsigned, char **, int); -} functions[] = { - { "expr", 1, 1, 0, func_eval_expr }, - { "e", 1, 1, 0, func_eval_expr }, - { "expr_int_format", 2, 3, 0, func_eval_expr_int_format }, - { "eif", 2, 3, 0, func_eval_expr_int_format }, - { "pict_type", 0, 0, 0, func_pict_type }, - { "pts", 0, 3, 0, func_pts }, - { "gmtime", 0, 1, 'G', func_strftime }, - { "localtime", 0, 1, 'L', func_strftime }, - { "frame_num", 0, 0, 0, func_frame_num }, - { "n", 0, 0, 0, func_frame_num }, - { "metadata", 1, 2, 0, func_metadata }, -}; - -static int eval_function(AVFilterContext *ctx, AVBPrint *bp, char *fct, - unsigned argc, char **argv) +static void hb_destroy(HarfbuzzData *hb) { - unsigned i; - - for (i = 0; i < FF_ARRAY_ELEMS(functions); i++) { - if (strcmp(fct, functions[i].name)) - continue; - if (argc < functions[i].argc_min) { - av_log(ctx, AV_LOG_ERROR, "%%{%s} requires at least %d arguments\n", - fct, functions[i].argc_min); - return AVERROR(EINVAL); - } - if (argc > functions[i].argc_max) { - av_log(ctx, AV_LOG_ERROR, "%%{%s} requires at most %d arguments\n", - fct, functions[i].argc_max); - return AVERROR(EINVAL); - } - break; - } - if (i >= FF_ARRAY_ELEMS(functions)) { - av_log(ctx, AV_LOG_ERROR, "%%{%s} is not known\n", fct); - return AVERROR(EINVAL); - } - return functions[i].func(ctx, bp, fct, argc, argv, functions[i].tag); + hb_buffer_destroy(hb->buf); + hb_font_destroy(hb->font); + hb->buf = NULL; + hb->font = NULL; + hb->glyph_info = NULL; + hb->glyph_pos = NULL; } -static int expand_function(AVFilterContext *ctx, AVBPrint *bp, char **rtext) +static int measure_text(AVFilterContext *ctx, TextMetrics *metrics) { - const char *text = *rtext; - char *argv[16] = { NULL }; - unsigned argc = 0, i; - int ret; + DrawTextContext *s = ctx->priv; + char *text = s->expanded_text.str; + char *textdup = NULL, *start = NULL; + int num_chars = 0; + int width64 = 0, w64 = 0; + int cur_min_y64 = 0, first_max_y64 = -32000; + int first_min_x64 = 32000, last_max_x64 = -32000; + int min_y64 = 32000, max_y64 = -32000, min_x64 = 32000, max_x64 = -32000; + int line_count = 0; + uint32_t code = 0; + Glyph *glyph = NULL; - if (*text != '{') { - av_log(ctx, AV_LOG_ERROR, "Stray %% near '%s'\n", text); - return AVERROR(EINVAL); - } - text++; - while (1) { - if (!(argv[argc++] = av_get_token(&text, ":}"))) { - ret = AVERROR(ENOMEM); - goto end; - } - if (!*text) { - av_log(ctx, AV_LOG_ERROR, "Unterminated %%{} near '%s'\n", *rtext); - ret = AVERROR(EINVAL); - goto end; + int i, tab_idx = 0, last_tab_idx = 0, line_offset = 0; + char* p; + int ret = 0; + + // Count the lines and the tab characters + s->tab_count = 0; + for (i = 0, p = text; 1; i++) { + GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed;); +continue_on_failed: + if (ff_is_newline(code) || code == 0) { + ++line_count; + if (code == 0) { + break; + } + } else if (code == '\t') { + ++s->tab_count; } - if (argc == FF_ARRAY_ELEMS(argv)) - av_freep(&argv[--argc]); /* error will be caught later */ - if (*text == '}') - break; - text++; } - if ((ret = eval_function(ctx, bp, argv[0], argc - 1, argv + 1)) < 0) - goto end; - ret = 0; - *rtext = (char *)text + 1; - -end: - for (i = 0; i < argc; i++) - av_freep(&argv[i]); - return ret; -} - -static int expand_text(AVFilterContext *ctx, char *text, AVBPrint *bp) -{ - int ret; - - av_bprint_clear(bp); - while (*text) { - if (*text == '\\' && text[1]) { - av_bprint_chars(bp, text[1], 1); - text += 2; - } else if (*text == '%') { - text++; - if ((ret = expand_function(ctx, bp, &text)) < 0) - return ret; - } else { - av_bprint_chars(bp, *text, 1); - text++; + // Evaluate the width of the space character if needed to replace tabs + if (s->tab_count > 0 && !s->blank_advance64) { + HarfbuzzData hb_data; + ret = shape_text_hb(s, &hb_data, " ", 1); + if(ret != 0) { + goto done; } + s->blank_advance64 = hb_data.glyph_pos[0].x_advance; + hb_destroy(&hb_data); } - if (!av_bprint_is_complete(bp)) - return AVERROR(ENOMEM); - return 0; -} - -static int draw_glyphs(DrawTextContext *s, AVFrame *frame, - int width, int height, - FFDrawColor *color, - int x, int y, int borderw) -{ - char *text = s->expanded_text.str; - uint32_t code = 0; - int i, x1, y1; - uint8_t *p; - Glyph *glyph = NULL; - - for (i = 0, p = text; *p; i++) { - FT_Bitmap bitmap; - Glyph dummy = { 0 }; - GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_invalid;); -continue_on_invalid: - - /* skip new line chars, just go to new line */ - if (code == '\n' || code == '\r' || code == '\t') - continue; - - dummy.code = code; - dummy.fontsize = s->fontsize; - glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL); - - bitmap = borderw ? glyph->border_bitmap : glyph->bitmap; - - if (glyph->bitmap.pixel_mode != FT_PIXEL_MODE_MONO && - glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) - return AVERROR(EINVAL); - x1 = s->positions[i].x+s->x+x - borderw; - y1 = s->positions[i].y+s->y+y - borderw; + s->line_count = line_count; + s->lines = av_mallocz(line_count * sizeof(TextLine)); + s->tab_clusters = av_mallocz(s->tab_count * sizeof(uint32_t)); + for (i = 0; i < s->tab_count; ++i) { + s->tab_clusters[i] = -1; + } - ff_blend_mask(&s->dc, color, - frame->data, frame->linesize, width, height, - bitmap.buffer, bitmap.pitch, - bitmap.width, bitmap.rows, - bitmap.pixel_mode == FT_PIXEL_MODE_MONO ? 0 : 3, - 0, x1, y1); + start = textdup = av_strdup(text); + if (textdup == NULL) { + ret = AVERROR(ENOMEM); + goto done; } + line_count = 0; + for (i = 0, p = textdup; 1; i++) { + if (*p == '\t') { + s->tab_clusters[tab_idx++] = i; + *p = ' '; + } + GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_failed2;); +continue_on_failed2: + if (ff_is_newline(code) || code == 0) { + TextLine *cur_line = &s->lines[line_count]; + HarfbuzzData *hb = &cur_line->hb_data; + cur_line->cluster_offset = line_offset; + ret = shape_text_hb(s, hb, start, num_chars); + if (ret != 0) { + goto done; + } + w64 = 0; + cur_min_y64 = 32000; + for (int t = 0; t < hb->glyph_count; ++t) { + uint8_t is_tab = last_tab_idx < s->tab_count && + hb->glyph_info[t].cluster == s->tab_clusters[last_tab_idx] - line_offset; + if (is_tab) { + ++last_tab_idx; + } + ret = load_glyph(ctx, &glyph, hb->glyph_info[t].codepoint, -1, -1); + if (ret != 0) { + goto done; + } + if (line_count == 0) { + first_max_y64 = FFMAX(glyph->bbox.yMax, first_max_y64); + } + if (t == 0) { + cur_line->offset_left64 = glyph->bbox.xMin; + first_min_x64 = FFMIN(glyph->bbox.xMin, first_min_x64); + } + if (t == hb->glyph_count - 1) { + // The following code measures the width of the line up to the last + // character's horizontal advance + int last_char_width = hb->glyph_pos[t].x_advance; + + // The following code measures the width of the line up to the rightmost + // visible pixel of the last character + // int last_char_width = glyph->bbox.xMax; + + w64 += last_char_width; + last_max_x64 = FFMAX(last_char_width, last_max_x64); + cur_line->offset_right64 = last_char_width; + } else { + if (is_tab) { + int size = s->blank_advance64 * s->tabsize; + w64 = (w64 / size + 1) * size; + } else { + w64 += hb->glyph_pos[t].x_advance; + } + } + cur_min_y64 = FFMIN(glyph->bbox.yMin, cur_min_y64); + min_y64 = FFMIN(glyph->bbox.yMin, min_y64); + max_y64 = FFMAX(glyph->bbox.yMax, max_y64); + min_x64 = FFMIN(glyph->bbox.xMin, min_x64); + max_x64 = FFMAX(glyph->bbox.xMax, max_x64); + } - return 0; -} + cur_line->width64 = w64; + av_log(s, AV_LOG_DEBUG, " Line: %d -- glyphs count: %d - width64: %d - offset_left64: %d - offset_right64: %d)\n", + line_count, hb->glyph_count, cur_line->width64, cur_line->offset_left64, cur_line->offset_right64); -static void update_color_with_alpha(DrawTextContext *s, FFDrawColor *color, const FFDrawColor incolor) -{ - *color = incolor; - color->rgba[3] = (color->rgba[3] * s->alpha) / 255; - ff_draw_color(&s->dc, color, color->rgba); -} + if (w64 > width64) { + width64 = w64; + } + num_chars = -1; + start = p; + ++line_count; + line_offset = i + 1; + } -static void update_alpha(DrawTextContext *s) -{ - double alpha = av_expr_eval(s->a_pexpr, s->var_values, &s->prng); + if (code == 0) break; + ++num_chars; + } - if (isnan(alpha)) - return; + metrics->line_height64 = s->face->size->metrics.height; - if (alpha >= 1.0) - s->alpha = 255; - else if (alpha <= 0) - s->alpha = 0; - else - s->alpha = 256 * alpha; + metrics->width = POS_CEIL(width64, 64); + if (s->y_align == YA_FONT) { + metrics->height = POS_CEIL(metrics->line_height64 * line_count, 64); + } else { + int height64 = (metrics->line_height64 + s->line_spacing * 64) * + (FFMAX(0, line_count - 1)) + first_max_y64 - cur_min_y64; + metrics->height = POS_CEIL(height64, 64); + } + metrics->offset_top64 = first_max_y64; + metrics->offset_right64 = last_max_x64; + metrics->offset_bottom64 = cur_min_y64; + metrics->offset_left64 = first_min_x64; + metrics->min_x64 = min_x64; + metrics->min_y64 = min_y64; + metrics->max_x64 = max_x64; + metrics->max_y64 = max_y64; + +done: + av_free(textdup); + return ret; } -static int draw_text(AVFilterContext *ctx, AVFrame *frame, - int width, int height) +static int draw_text(AVFilterContext *ctx, AVFrame *frame) { DrawTextContext *s = ctx->priv; AVFilterLink *inlink = ctx->inputs[0]; - - uint32_t code = 0, prev_code = 0; - int x = 0, y = 0, i = 0, ret; - int max_text_line_w = 0, len; - int box_w, box_h; - char *text; - uint8_t *p; - int y_min = 32000, y_max = -32000; - int x_min = 32000, x_max = -32000; - FT_Vector delta; - Glyph *glyph = NULL, *prev_glyph = NULL; - Glyph dummy = { 0 }; + int x = 0, y = 0, ret; + int shift_x64, shift_y64; + int x64, y64; + Glyph *glyph = NULL; time_t now = time(0); struct tm ltime; @@ -1413,9 +1566,17 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, FFDrawColor bordercolor; FFDrawColor boxcolor; + int width = frame->width; + int height = frame->height; + int rec_x = 0, rec_y = 0, rec_width = 0, rec_height = 0; + int is_outside = 0; + int last_tab_idx = 0; + + TextMetrics metrics; + av_bprint_clear(bp); - if(s->basetime != AV_NOPTS_VALUE) + if (s->basetime != AV_NOPTS_VALUE) now= frame->pts*av_q2d(ctx->inputs[0]->time_base) + s->basetime/1000000; switch (s->exp_mode) { @@ -1423,7 +1584,7 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, av_bprintf(bp, "%s", s->text); break; case EXP_NORMAL: - if ((ret = expand_text(ctx, s->text, &s->expanded_text)) < 0) + if ((ret = ff_expand_text(&s->expand_text, s->text, &s->expanded_text)) < 0) return ret; break; case EXP_STRFTIME: @@ -1441,18 +1602,11 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, if (!av_bprint_is_complete(bp)) return AVERROR(ENOMEM); - text = s->expanded_text.str; - if ((len = s->expanded_text.len) > s->nb_positions) { - if (!(s->positions = - av_realloc(s->positions, len*sizeof(*s->positions)))) - return AVERROR(ENOMEM); - s->nb_positions = len; - } if (s->fontcolor_expr[0]) { /* If expression is set, evaluate and replace the static value */ av_bprint_clear(&s->expanded_fontcolor); - if ((ret = expand_text(ctx, s->fontcolor_expr, &s->expanded_fontcolor)) < 0) + if ((ret = ff_expand_text(&s->expand_text, s->fontcolor_expr, &s->expanded_fontcolor)) < 0) return ret; if (!av_bprint_is_complete(&s->expanded_fontcolor)) return AVERROR(ENOMEM); @@ -1463,85 +1617,30 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, ff_draw_color(&s->dc, &s->fontcolor, s->fontcolor.rgba); } - x = 0; - y = 0; - - if ((ret = update_fontsize(ctx)) < 0) + if ((ret = update_fontsize(ctx)) < 0) { return ret; - - /* load and cache glyphs */ - for (i = 0, p = text; *p; i++) { - GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_invalid;); -continue_on_invalid: - - /* get glyph */ - dummy.code = code; - dummy.fontsize = s->fontsize; - glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL); - if (!glyph) { - ret = load_glyph(ctx, &glyph, code); - if (ret < 0) - return ret; - } - - y_min = FFMIN(glyph->bbox.yMin, y_min); - y_max = FFMAX(glyph->bbox.yMax, y_max); - x_min = FFMIN(glyph->bbox.xMin, x_min); - x_max = FFMAX(glyph->bbox.xMax, x_max); } - s->max_glyph_h = y_max - y_min; - s->max_glyph_w = x_max - x_min; - - /* compute and save position for each glyph */ - glyph = NULL; - for (i = 0, p = text; *p; i++) { - GET_UTF8(code, *p ? *p++ : 0, code = 0xfffd; goto continue_on_invalid2;); -continue_on_invalid2: - - /* skip the \n in the sequence \r\n */ - if (prev_code == '\r' && code == '\n') - continue; - prev_code = code; - if (is_newline(code)) { - - max_text_line_w = FFMAX(max_text_line_w, x); - y += s->max_glyph_h + s->line_spacing; - x = 0; - continue; - } - - /* get glyph */ - prev_glyph = glyph; - dummy.code = code; - dummy.fontsize = s->fontsize; - glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL); - - /* kerning */ - if (s->use_kerning && prev_glyph && glyph->code) { - FT_Get_Kerning(s->face, prev_glyph->code, glyph->code, - ft_kerning_default, &delta); - x += delta.x >> 6; - } - - /* save position */ - s->positions[i].x = x + glyph->bitmap_left; - s->positions[i].y = y - glyph->bitmap_top + y_max; - if (code == '\t') x = (x / s->tabsize + 1)*s->tabsize; - else x += glyph->advance; + if ((ret = measure_text(ctx, &metrics)) < 0) { + return ret; } - max_text_line_w = FFMAX(x, max_text_line_w); + s->max_glyph_h = POS_CEIL(metrics.max_y64 - metrics.min_y64, 64); + s->max_glyph_w = POS_CEIL(metrics.max_x64 - metrics.min_x64, 64); - s->var_values[VAR_TW] = s->var_values[VAR_TEXT_W] = max_text_line_w; - s->var_values[VAR_TH] = s->var_values[VAR_TEXT_H] = y + s->max_glyph_h; + s->var_values[VAR_TW] = s->var_values[VAR_TEXT_W] = metrics.width; + s->var_values[VAR_TH] = s->var_values[VAR_TEXT_H] = metrics.height; s->var_values[VAR_MAX_GLYPH_W] = s->max_glyph_w; s->var_values[VAR_MAX_GLYPH_H] = s->max_glyph_h; - s->var_values[VAR_MAX_GLYPH_A] = s->var_values[VAR_ASCENT ] = y_max; - s->var_values[VAR_MAX_GLYPH_D] = s->var_values[VAR_DESCENT] = y_min; + s->var_values[VAR_MAX_GLYPH_A] = s->var_values[VAR_ASCENT] = POS_CEIL(metrics.max_y64, 64); + s->var_values[VAR_FONT_A] = s->face->size->metrics.ascender / 64; + s->var_values[VAR_MAX_GLYPH_D] = s->var_values[VAR_DESCENT] = POS_CEIL(metrics.min_y64, 64); + s->var_values[VAR_FONT_D] = -s->face->size->metrics.descender / 64; - s->var_values[VAR_LINE_H] = s->var_values[VAR_LH] = s->max_glyph_h; + s->var_values[VAR_TOP_A] = POS_CEIL(metrics.offset_top64, 64); + s->var_values[VAR_BOTTOM_D] = -POS_CEIL(metrics.offset_bottom64, 64); + s->var_values[VAR_LINE_H] = s->var_values[VAR_LH] = metrics.line_height64 / 64.; if (s->text_source == AV_FRAME_DATA_DETECTION_BBOXES) { s->var_values[VAR_X] = s->x; @@ -1559,56 +1658,169 @@ static int draw_text(AVFilterContext *ctx, AVFrame *frame, update_color_with_alpha(s, &bordercolor, s->bordercolor); update_color_with_alpha(s, &boxcolor , s->boxcolor ); - box_w = max_text_line_w; - box_h = y + s->max_glyph_h; + if (s->draw_box && s->boxborderw) { + int bbsize[4]; + int count; + count = string_to_array(s->boxborderw, bbsize, 4); + if (count == 1) { + s->bb_top = s->bb_right = s->bb_bottom = s->bb_left = bbsize[0]; + } else if (count == 2) { + s->bb_top = s->bb_bottom = bbsize[0]; + s->bb_right = s->bb_left = bbsize[1]; + } else if (count == 3) { + s->bb_top = bbsize[0]; + s->bb_right = s->bb_left = bbsize[1]; + s->bb_bottom = bbsize[2]; + } else if (count == 4) { + s->bb_top = bbsize[0]; + s->bb_right = bbsize[1]; + s->bb_bottom = bbsize[2]; + s->bb_left = bbsize[3]; + } + } else { + s->bb_top = s->bb_right = s->bb_bottom = s->bb_left = 0; + } if (s->fix_bounds) { - /* calculate footprint of text effects */ - int boxoffset = s->draw_box ? FFMAX(s->boxborderw, 0) : 0; int borderoffset = s->borderw ? FFMAX(s->borderw, 0) : 0; - int offsetleft = FFMAX3(boxoffset, borderoffset, + int offsetleft = FFMAX3(FFMAX(s->bb_left, 0), borderoffset, (s->shadowx < 0 ? FFABS(s->shadowx) : 0)); - int offsettop = FFMAX3(boxoffset, borderoffset, + int offsettop = FFMAX3(FFMAX(s->bb_top, 0), borderoffset, (s->shadowy < 0 ? FFABS(s->shadowy) : 0)); - - int offsetright = FFMAX3(boxoffset, borderoffset, + int offsetright = FFMAX3(FFMAX(s->bb_right, 0), borderoffset, (s->shadowx > 0 ? s->shadowx : 0)); - int offsetbottom = FFMAX3(boxoffset, borderoffset, + int offsetbottom = FFMAX3(FFMAX(s->bb_bottom, 0), borderoffset, (s->shadowy > 0 ? s->shadowy : 0)); - if (s->x - offsetleft < 0) s->x = offsetleft; if (s->y - offsettop < 0) s->y = offsettop; - if (s->x + box_w + offsetright > width) - s->x = FFMAX(width - box_w - offsetright, 0); - if (s->y + box_h + offsetbottom > height) - s->y = FFMAX(height - box_h - offsetbottom, 0); + if (s->x + metrics.width + offsetright > width) + s->x = FFMAX(width - metrics.width - offsetright, 0); + if (s->y + metrics.height + offsetbottom > height) + s->y = FFMAX(height - metrics.height - offsetbottom, 0); + } + + x = 0; + y = 0; + x64 = (int)(s->x * 64.); + if (s->y_align == YA_FONT) { + y64 = (int)(s->y * 64. + s->face->size->metrics.ascender); + } else if (s->y_align == YA_BASELINE) { + y64 = (int)(s->y * 64.); + } else { + y64 = (int)(s->y * 64. + metrics.offset_top64); } - /* draw box */ - if (s->draw_box) - ff_blend_rectangle(&s->dc, &boxcolor, - frame->data, frame->linesize, width, height, - s->x - s->boxborderw, s->y - s->boxborderw, - box_w + s->boxborderw * 2, box_h + s->boxborderw * 2); + for (int l = 0; l < s->line_count; ++l) { + TextLine *line = &s->lines[l]; + HarfbuzzData *hb = &line->hb_data; + line->glyphs = av_mallocz(hb->glyph_count * sizeof(GlyphInfo)); + + for (int t = 0; t < hb->glyph_count; ++t) { + GlyphInfo *g_info = &line->glyphs[t]; + uint8_t is_tab = last_tab_idx < s->tab_count && + hb->glyph_info[t].cluster == s->tab_clusters[last_tab_idx] - line->cluster_offset; + int true_x, true_y; + if (is_tab) { + ++last_tab_idx; + } + true_x = x + hb->glyph_pos[t].x_offset; + true_y = y + hb->glyph_pos[t].y_offset; + shift_x64 = (((x64 + true_x) >> 4) & 0b0011) << 4; + shift_y64 = ((4 - (((y64 + true_y) >> 4) & 0b0011)) & 0b0011) << 4; - if (s->shadowx || s->shadowy) { - if ((ret = draw_glyphs(s, frame, width, height, - &shadowcolor, s->shadowx, s->shadowy, 0)) < 0) - return ret; + ret = load_glyph(ctx, &glyph, hb->glyph_info[t].codepoint, shift_x64, shift_y64); + if (ret != 0) { + return ret; + } + g_info->code = hb->glyph_info[t].codepoint; + g_info->x = (x64 + true_x) >> 6; + g_info->y = ((y64 + true_y) >> 6) + (shift_y64 > 0 ? 1 : 0); + g_info->shift_x64 = shift_x64; + g_info->shift_y64 = shift_y64; + + if (!is_tab) { + x += hb->glyph_pos[t].x_advance; + } else { + int size = s->blank_advance64 * s->tabsize; + x = (x / size + 1) * size; + } + y += hb->glyph_pos[t].y_advance; + } + + y += metrics.line_height64 + s->line_spacing * 64; + x = 0; } - if (s->borderw) { - if ((ret = draw_glyphs(s, frame, width, height, - &bordercolor, 0, 0, s->borderw)) < 0) + metrics.rect_x = s->x; + if (s->y_align == YA_BASELINE) { + metrics.rect_y = s->y - metrics.offset_top64 / 64; + } else { + metrics.rect_y = s->y; + } + + s->box_width = s->boxw == 0 ? metrics.width : s->boxw; + s->box_height = s->boxh == 0 ? metrics.height : s->boxh; + + if (!s->draw_box) { + // Create a border for the clipping region to take into account subpixel + // errors in text measurement and effects. + int borderoffset = s->borderw ? FFMAX(s->borderw, 0) : 0; + s->bb_left = borderoffset + (s->shadowx < 0 ? FFABS(s->shadowx) : 0) + 1; + s->bb_top = borderoffset + (s->shadowy < 0 ? FFABS(s->shadowy) : 0) + 1; + s->bb_right = borderoffset + (s->shadowx > 0 ? s->shadowx : 0) + 1; + s->bb_bottom = borderoffset + (s->shadowy > 0 ? s->shadowy : 0) + 1; + } + + /* Check if the whole box is out of the frame */ + is_outside = metrics.rect_x - s->bb_left >= width || + metrics.rect_y - s->bb_top >= height || + metrics.rect_x + s->box_width + s->bb_right <= 0 || + metrics.rect_y + s->box_height + s->bb_bottom <= 0; + + if (!is_outside) { + /* draw box */ + if (s->draw_box) { + rec_x = metrics.rect_x - s->bb_left; + rec_y = metrics.rect_y - s->bb_top; + rec_width = s->box_width + s->bb_right + s->bb_left; + rec_height = s->box_height + s->bb_bottom + s->bb_top; + ff_blend_rectangle(&s->dc, &boxcolor, + frame->data, frame->linesize, width, height, + rec_x, rec_y, rec_width, rec_height); + } + + if (s->shadowx || s->shadowy) { + if ((ret = draw_glyphs(s, frame, &shadowcolor, &metrics, + s->shadowx, s->shadowy, s->borderw)) < 0) { + return ret; + } + } + + if (s->borderw) { + if ((ret = draw_glyphs(s, frame, &bordercolor, &metrics, + 0, 0, s->borderw)) < 0) { + return ret; + } + } + + if ((ret = draw_glyphs(s, frame, &fontcolor, &metrics, 0, + 0, 0)) < 0) { return ret; + } } - if ((ret = draw_glyphs(s, frame, width, height, - &fontcolor, 0, 0, 0)) < 0) - return ret; + + // FREE data structures + for (int l = 0; l < s->line_count; ++l) { + TextLine *line = &s->lines[l]; + av_freep(&line->glyphs); + hb_destroy(&line->hb_data); + } + av_freep(&s->lines); + av_freep(&s->tab_clusters); return 0; } @@ -1636,7 +1848,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) } if (s->reload && !(inlink->frame_count_out % s->reload)) { - if ((ret = load_textfile(ctx)) < 0) { + if ((ret = ff_load_textfile(ctx, (const char *)s->textfile, &s->text, NULL)) < 0) { av_frame_free(&frame); return ret; } @@ -1654,18 +1866,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) NAN : frame->pts * av_q2d(inlink->time_base); s->var_values[VAR_PICT_TYPE] = frame->pict_type; - s->var_values[VAR_PKT_POS] = frame->pkt_pos; -#if FF_API_PKT_DURATION +#if FF_API_FRAME_PKT FF_DISABLE_DEPRECATION_WARNINGS - s->var_values[VAR_PKT_DURATION] = frame->pkt_duration * av_q2d(inlink->time_base); - - if (frame->pkt_duration) - s->var_values[VAR_DURATION] = frame->pkt_duration * av_q2d(inlink->time_base); - else + s->var_values[VAR_PKT_POS] = frame->pkt_pos; + s->var_values[VAR_PKT_SIZE] = frame->pkt_size; FF_ENABLE_DEPRECATION_WARNINGS #endif s->var_values[VAR_DURATION] = frame->duration * av_q2d(inlink->time_base); - s->var_values[VAR_PKT_SIZE] = frame->pkt_size; s->metadata = frame->metadata; @@ -1680,14 +1887,9 @@ FF_ENABLE_DEPRECATION_WARNINGS s->x = bbox->x; s->y = bbox->y - s->fontsize; } - draw_text(ctx, frame, frame->width, frame->height); + draw_text(ctx, frame); } - av_log(ctx, AV_LOG_DEBUG, "n:%d t:%f text_w:%d text_h:%d x:%d y:%d\n", - (int)s->var_values[VAR_N], s->var_values[VAR_T], - (int)s->var_values[VAR_TEXT_W], (int)s->var_values[VAR_TEXT_H], - s->x, s->y); - return ff_filter_frame(outlink, frame); } @@ -1701,13 +1903,6 @@ static const AVFilterPad avfilter_vf_drawtext_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_drawtext_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_drawtext = { .name = "drawtext", .description = NULL_IF_CONFIG_SMALL("Draw text on top of video frames using libfreetype library."), @@ -1716,7 +1911,7 @@ const AVFilter ff_vf_drawtext = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_drawtext_inputs), - FILTER_OUTPUTS(avfilter_vf_drawtext_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .process_command = command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_edgedetect.c b/libavfilter/vf_edgedetect.c index 603f06f1419..38c3503ce6e 100644 --- a/libavfilter/vf_edgedetect.c +++ b/libavfilter/vf_edgedetect.c @@ -71,17 +71,17 @@ typedef struct EdgeDetectContext { static const AVOption edgedetect_options[] = { { "high", "set high threshold", OFFSET(high), AV_OPT_TYPE_DOUBLE, {.dbl=50/255.}, 0, 1, FLAGS }, { "low", "set low threshold", OFFSET(low), AV_OPT_TYPE_DOUBLE, {.dbl=20/255.}, 0, 1, FLAGS }, - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_WIRES}, 0, NB_MODE-1, FLAGS, "mode" }, - { "wires", "white/gray wires on black", 0, AV_OPT_TYPE_CONST, {.i64=MODE_WIRES}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "colormix", "mix colors", 0, AV_OPT_TYPE_CONST, {.i64=MODE_COLORMIX}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "canny", "detect edges on planes", 0, AV_OPT_TYPE_CONST, {.i64=MODE_CANNY}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "planes", "set planes to filter", OFFSET(filter_planes), AV_OPT_TYPE_FLAGS, {.i64=7}, 1, 0x7, FLAGS, "flags" }, - { "y", "filter luma plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_Y}, 0, 0, FLAGS, "flags" }, - { "u", "filter u plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_U}, 0, 0, FLAGS, "flags" }, - { "v", "filter v plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_V}, 0, 0, FLAGS, "flags" }, - { "r", "filter red plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_R}, 0, 0, FLAGS, "flags" }, - { "g", "filter green plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_G}, 0, 0, FLAGS, "flags" }, - { "b", "filter blue plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_B}, 0, 0, FLAGS, "flags" }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_WIRES}, 0, NB_MODE-1, FLAGS, .unit = "mode" }, + { "wires", "white/gray wires on black", 0, AV_OPT_TYPE_CONST, {.i64=MODE_WIRES}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "colormix", "mix colors", 0, AV_OPT_TYPE_CONST, {.i64=MODE_COLORMIX}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "canny", "detect edges on planes", 0, AV_OPT_TYPE_CONST, {.i64=MODE_CANNY}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "planes", "set planes to filter", OFFSET(filter_planes), AV_OPT_TYPE_FLAGS, {.i64=7}, 1, 0x7, FLAGS, .unit = "flags" }, + { "y", "filter luma plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_Y}, 0, 0, FLAGS, .unit = "flags" }, + { "u", "filter u plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_U}, 0, 0, FLAGS, .unit = "flags" }, + { "v", "filter v plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_V}, 0, 0, FLAGS, .unit = "flags" }, + { "r", "filter red plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_R}, 0, 0, FLAGS, .unit = "flags" }, + { "g", "filter green plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_G}, 0, 0, FLAGS, .unit = "flags" }, + { "b", "filter blue plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_B}, 0, 0, FLAGS, .unit = "flags" }, { NULL } }; @@ -249,13 +249,6 @@ static const AVFilterPad edgedetect_inputs[] = { }, }; -static const AVFilterPad edgedetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_edgedetect = { .name = "edgedetect", .description = NULL_IF_CONFIG_SMALL("Detect and draw edge."), @@ -263,7 +256,7 @@ const AVFilter ff_vf_edgedetect = { .init = init, .uninit = uninit, FILTER_INPUTS(edgedetect_inputs), - FILTER_OUTPUTS(edgedetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .priv_class = &edgedetect_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_elbg.c b/libavfilter/vf_elbg.c index d1166714fc4..863366ccc26 100644 --- a/libavfilter/vf_elbg.c +++ b/libavfilter/vf_elbg.c @@ -30,6 +30,7 @@ #include "avfilter.h" #include "drawutils.h" +#include "formats.h" #include "internal.h" #include "video.h" @@ -193,7 +194,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) p0 = (uint8_t *)out->data[0]; for (i = 0; i < elbg->codebook_length; i++) { - const int al = elbg->use_alpha ? elbg->codebook[i*4+3] : 0xff; + const unsigned al = elbg->use_alpha ? elbg->codebook[i*4+3] : 0xff; pal[i] = al << 24 | (elbg->codebook[i*4+2] << 16) | (elbg->codebook[i*4+1] << 8) | @@ -253,13 +254,6 @@ static const AVFilterPad elbg_inputs[] = { }, }; -static const AVFilterPad elbg_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_elbg = { .name = "elbg", .description = NULL_IF_CONFIG_SMALL("Apply posterize effect, using the ELBG algorithm."), @@ -268,6 +262,6 @@ const AVFilter ff_vf_elbg = { .init = init, .uninit = uninit, FILTER_INPUTS(elbg_inputs), - FILTER_OUTPUTS(elbg_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/vf_entropy.c b/libavfilter/vf_entropy.c index 893d07d8e6d..81772e6c97a 100644 --- a/libavfilter/vf_entropy.c +++ b/libavfilter/vf_entropy.c @@ -18,12 +18,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -45,9 +43,9 @@ typedef struct EntropyContext { #define OFFSET(x) offsetof(EntropyContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption entropy_options[] = { - { "mode", "set kind of histogram entropy measurement", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "mode" }, - { "normal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" }, - { "diff", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" }, + { "mode", "set kind of histogram entropy measurement", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "mode" }, + { "normal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "mode" }, + { "diff", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "mode" }, { NULL } }; @@ -176,20 +174,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_entropy = { .name = "entropy", .description = NULL_IF_CONFIG_SMALL("Measure video frames entropy."), .priv_size = sizeof(EntropyContext), .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixfmts), .priv_class = &entropy_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_METADATA_ONLY, diff --git a/libavfilter/vf_epx.c b/libavfilter/vf_epx.c index d706229ab85..bae1b898bc7 100644 --- a/libavfilter/vf_epx.c +++ b/libavfilter/vf_epx.c @@ -19,6 +19,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "internal.h" +#include "video.h" typedef struct EPXContext { const AVClass *class; diff --git a/libavfilter/vf_eq.c b/libavfilter/vf_eq.c index 46636dd29d3..30ff9769400 100644 --- a/libavfilter/vf_eq.c +++ b/libavfilter/vf_eq.c @@ -27,12 +27,13 @@ * very simple video equalizer */ -#include "libavfilter/internal.h" #include "libavutil/common.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" +#include "internal.h" #include "vf_eq.h" +#include "video.h" static void create_lut(EQParameters *param) { @@ -221,7 +222,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) AVFilterLink *outlink = inlink->dst->outputs[0]; EQContext *eq = ctx->priv; AVFrame *out; - int64_t pos = in->pkt_pos; const AVPixFmtDescriptor *desc; int i; @@ -235,7 +235,14 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) desc = av_pix_fmt_desc_get(inlink->format); eq->var_values[VAR_N] = inlink->frame_count_out; - eq->var_values[VAR_POS] = pos == -1 ? NAN : pos; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + { + int64_t pos = in->pkt_pos; + eq->var_values[VAR_POS] = pos == -1 ? NAN : pos; + } +FF_ENABLE_DEPRECATION_WARNINGS +#endif eq->var_values[VAR_T] = TS2T(in->pts, inlink->time_base); if (eq->eval_mode == EVAL_MODE_FRAME) { @@ -307,13 +314,6 @@ static const AVFilterPad eq_inputs[] = { }, }; -static const AVFilterPad eq_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(EQContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM #define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -334,7 +334,7 @@ static const AVOption eq_options[] = { OFFSET(gamma_b_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, 0, 0, TFLAGS }, { "gamma_weight", "set the gamma weight which reduces the effect of gamma on bright areas", OFFSET(gamma_weight_expr), AV_OPT_TYPE_STRING, {.str = "1.0"}, 0, 0, TFLAGS }, - { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" }, + { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, .unit = "eval" }, { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, { "frame", "eval expressions per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, { NULL } @@ -348,7 +348,7 @@ const AVFilter ff_vf_eq = { .priv_size = sizeof(EQContext), .priv_class = &eq_class, FILTER_INPUTS(eq_inputs), - FILTER_OUTPUTS(eq_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts_eq), .process_command = process_command, .init = initialize, diff --git a/libavfilter/vf_eq.h b/libavfilter/vf_eq.h index fc8d4a711a8..50850ea42c3 100644 --- a/libavfilter/vf_eq.h +++ b/libavfilter/vf_eq.h @@ -30,7 +30,9 @@ static const char *const var_names[] = { "n", // frame count +#if FF_API_FRAME_PKT "pos", // frame position +#endif "r", // frame rate "t", // timestamp expressed in seconds NULL @@ -38,7 +40,9 @@ static const char *const var_names[] = { enum var_name { VAR_N, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_R, VAR_T, VAR_NB diff --git a/libavfilter/vf_estdif.c b/libavfilter/vf_estdif.c index 0164f4638ae..b785c290ffa 100644 --- a/libavfilter/vf_estdif.c +++ b/libavfilter/vf_estdif.c @@ -23,7 +23,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -35,9 +34,9 @@ typedef struct ESTDIFContext { int deint; ///< which frames to deinterlace int rslope; ///< best edge slope search radius int redge; ///< best edge match search radius - float ecost; ///< edge cost for edge matching - float mcost; ///< middle cost for edge matching - float dcost; ///< distance cost for edge matching + int ecost; ///< edge cost for edge matching + int mcost; ///< middle cost for edge matching + int dcost; ///< distance cost for edge matching int interp; ///< type of interpolation int linesize[4]; ///< bytes of pixel data per line for each plane int planewidth[4]; ///< width of each plane @@ -79,25 +78,25 @@ typedef struct ESTDIFContext { #define OFFSET(x) offsetof(ESTDIFContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM -#define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, unit } +#define CONST(name, help, val, u) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, .unit = u } static const AVOption estdif_options[] = { - { "mode", "specify the mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "mode" }, + { "mode", "specify the mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "mode" }, CONST("frame", "send one frame for each frame", 0, "mode"), CONST("field", "send one frame for each field", 1, "mode"), - { "parity", "specify the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=-1}, -1, 1, FLAGS, "parity" }, + { "parity", "specify the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=-1}, -1, 1, FLAGS, .unit = "parity" }, CONST("tff", "assume top field first", 0, "parity"), CONST("bff", "assume bottom field first", 1, "parity"), CONST("auto", "auto detect parity", -1, "parity"), - { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "deint" }, + { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "deint" }, CONST("all", "deinterlace all frames", 0, "deint"), CONST("interlaced", "only deinterlace frames marked as interlaced", 1, "deint"), - { "rslope", "specify the search radius for edge slope tracing", OFFSET(rslope), AV_OPT_TYPE_INT, {.i64=1}, 1, MAX_R, FLAGS, }, - { "redge", "specify the search radius for best edge matching", OFFSET(redge), AV_OPT_TYPE_INT, {.i64=2}, 0, MAX_R, FLAGS, }, - { "ecost", "specify the edge cost for edge matching", OFFSET(ecost), AV_OPT_TYPE_FLOAT,{.dbl=1},0,9,FLAGS, }, - { "mcost", "specify the middle cost for edge matching", OFFSET(mcost), AV_OPT_TYPE_FLOAT,{.dbl=0.5}, 0, 1, FLAGS, }, - { "dcost", "specify the distance cost for edge matching", OFFSET(dcost), AV_OPT_TYPE_FLOAT,{.dbl=0.5}, 0, 1, FLAGS, }, - { "interp", "specify the type of interpolation", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, FLAGS, "interp" }, + { "rslope", "specify the search radius for edge slope tracing", OFFSET(rslope), AV_OPT_TYPE_INT, {.i64=1}, 1, MAX_R, FLAGS }, + { "redge", "specify the search radius for best edge matching", OFFSET(redge), AV_OPT_TYPE_INT, {.i64=2}, 0, MAX_R, FLAGS }, + { "ecost", "specify the edge cost for edge matching", OFFSET(ecost), AV_OPT_TYPE_INT, {.i64=2}, 0, 50, FLAGS }, + { "mcost", "specify the middle cost for edge matching", OFFSET(mcost), AV_OPT_TYPE_INT, {.i64=1}, 0, 50, FLAGS }, + { "dcost", "specify the distance cost for edge matching", OFFSET(dcost), AV_OPT_TYPE_INT, {.i64=1}, 0, 50, FLAGS }, + { "interp", "specify the type of interpolation", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, FLAGS, .unit = "interp" }, CONST("2p", "two-point interpolation", 0, "interp"), CONST("4p", "four-point interpolation", 1, "interp"), CONST("6p", "six-point interpolation", 2, "interp"), @@ -265,12 +264,13 @@ static void interpolate_##ss(ESTDIFContext *s, uint8_t *ddst, \ const type *const next2_line = (const type *const)nnext2_line; \ const type *const next3_line = (const type *const)nnext3_line; \ const int interp = s->interp; \ - const int ecost = s->ecost * 32.f; \ - const int dcost = s->dcost * s->max; \ - const int end = width - 1; \ - const atype mcost = s->mcost * s->redge * 4.f; \ + const int ecost = s->ecost; \ + const int dcost = s->dcost; \ + const int mcost = s->mcost; \ atype sd[S], sD[S], di = 0; \ + const int end = width - 1; \ atype dmin = amax; \ + int id = 0, iD = 0; \ int k = *K; \ \ for (int i = -rslope; i <= rslope && abs(k) > rslope; i++) { \ @@ -288,7 +288,11 @@ static void interpolate_##ss(ESTDIFContext *s, uint8_t *ddst, \ sD[i + rslope] += mcost * cost_##ss(prev_line, next_line, end, x, i);\ sD[i + rslope] += dcost * abs(i); \ \ - dmin = FFMIN(sD[i + rslope], dmin); \ + if (dmin > sD[i + rslope]) { \ + dmin = sD[i + rslope]; \ + di = 1; \ + iD = i; \ + } \ } \ \ for (int i = -rslope; i <= rslope; i++) { \ @@ -306,23 +310,14 @@ static void interpolate_##ss(ESTDIFContext *s, uint8_t *ddst, \ sd[i + rslope] += mcost * cost_##ss(prev_line, next_line, end, x, k+i);\ sd[i + rslope] += dcost * abs(k + i); \ \ - dmin = FFMIN(sd[i + rslope], dmin); \ - } \ - \ - for (int i = -rslope; i <= rslope && abs(k) > rslope; i++) { \ - if (dmin == sD[i + rslope]) { \ - di = 1; \ - k = i; \ - break; \ + if (dmin > sd[i + rslope]) { \ + dmin = sd[i + rslope]; \ + di = 0; \ + id = i; \ } \ } \ \ - for (int i = -rslope; i <= rslope && !di; i++) { \ - if (dmin == sd[i + rslope]) { \ - k += i; \ - break; \ - } \ - } \ + k = di ? iD : k + id; \ \ dst[x] = s->mid_##ss[interp](prev_line, next_line, \ prev2_line, next2_line, \ @@ -345,8 +340,8 @@ static int deinterlace_slice(AVFilterContext *ctx, void *arg, const int rslope = s->rslope; const int redge = s->redge; const int depth = s->depth; - const int interlaced = in->interlaced_frame; - const int tff = (s->field == (s->parity == -1 ? interlaced ? in->top_field_first : 1 : + const int interlaced = !!(in->flags & AV_FRAME_FLAG_INTERLACED); + const int tff = (s->field == (s->parity == -1 ? interlaced ? !!(in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 1 : s->parity ^ 1)); for (int plane = 0; plane < s->nb_planes; plane++) { @@ -443,7 +438,12 @@ static int filter(AVFilterContext *ctx, AVFrame *in, int64_t pts, int64_t durati if (!out) return AVERROR(ENOMEM); av_frame_copy_props(out, in); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags &= ~AV_FRAME_FLAG_INTERLACED; out->pts = pts; out->duration = duration; @@ -502,7 +502,7 @@ static int config_input(AVFilterLink *inlink) return 0; } - if ((s->deint && !s->prev->interlaced_frame) || ctx->is_disabled) { + if ((s->deint && !(s->prev->flags & AV_FRAME_FLAG_INTERLACED)) || ctx->is_disabled) { s->prev->pts *= 2; s->prev->duration *= 2; ret = ff_filter_frame(ctx->outputs[0], s->prev); diff --git a/libavfilter/vf_exposure.c b/libavfilter/vf_exposure.c index ff2f1565341..926d784a813 100644 --- a/libavfilter/vf_exposure.c +++ b/libavfilter/vf_exposure.c @@ -21,9 +21,7 @@ #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -133,13 +131,6 @@ static const AVFilterPad exposure_inputs[] = { }, }; -static const AVFilterPad exposure_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(ExposureContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -157,7 +148,7 @@ const AVFilter ff_vf_exposure = { .priv_size = sizeof(ExposureContext), .priv_class = &exposure_class, FILTER_INPUTS(exposure_inputs), - FILTER_OUTPUTS(exposure_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_extractplanes.c b/libavfilter/vf_extractplanes.c index 08737d64156..8f1c9cca402 100644 --- a/libavfilter/vf_extractplanes.c +++ b/libavfilter/vf_extractplanes.c @@ -28,7 +28,9 @@ #include "avfilter.h" #include "drawutils.h" #include "filters.h" +#include "formats.h" #include "internal.h" +#include "video.h" #define PLANE_R 0x01 #define PLANE_G 0x02 @@ -51,14 +53,14 @@ typedef struct ExtractPlanesContext { #define OFFSET(x) offsetof(ExtractPlanesContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption extractplanes_options[] = { - { "planes", "set planes", OFFSET(requested_planes), AV_OPT_TYPE_FLAGS, {.i64=1}, 1, 0xff, FLAGS, "flags"}, - { "y", "set luma plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_Y}, 0, 0, FLAGS, "flags"}, - { "u", "set u plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_U}, 0, 0, FLAGS, "flags"}, - { "v", "set v plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_V}, 0, 0, FLAGS, "flags"}, - { "r", "set red plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_R}, 0, 0, FLAGS, "flags"}, - { "g", "set green plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_G}, 0, 0, FLAGS, "flags"}, - { "b", "set blue plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_B}, 0, 0, FLAGS, "flags"}, - { "a", "set alpha plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_A}, 0, 0, FLAGS, "flags"}, + { "planes", "set planes", OFFSET(requested_planes), AV_OPT_TYPE_FLAGS, {.i64=1}, 1, 0xff, FLAGS, .unit = "flags"}, + { "y", "set luma plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_Y}, 0, 0, FLAGS, .unit = "flags"}, + { "u", "set u plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_U}, 0, 0, FLAGS, .unit = "flags"}, + { "v", "set v plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_V}, 0, 0, FLAGS, .unit = "flags"}, + { "r", "set red plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_R}, 0, 0, FLAGS, .unit = "flags"}, + { "g", "set green plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_G}, 0, 0, FLAGS, .unit = "flags"}, + { "b", "set blue plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_B}, 0, 0, FLAGS, .unit = "flags"}, + { "a", "set alpha plane", 0, AV_OPT_TYPE_CONST, {.i64=PLANE_A}, 0, 0, FLAGS, .unit = "flags"}, { NULL } }; @@ -117,7 +119,7 @@ AVFILTER_DEFINE_CLASS(extractplanes); AV_PIX_FMT_YUVA422P9##suf, \ AV_PIX_FMT_YUVA444P9##suf, \ AV_PIX_FMT_GBRP9##suf, \ - AV_PIX_FMT_GBRP14##suf, \ + AV_PIX_FMT_GBRP14##suf, AV_PIX_FMT_GBRAP14##suf, \ AV_PIX_FMT_YUV420P14##suf, \ AV_PIX_FMT_YUV422P14##suf, \ AV_PIX_FMT_YUV444P14##suf @@ -310,6 +312,8 @@ static int extract_plane(AVFilterLink *outlink, AVFrame *frame) if (!out) return AVERROR(ENOMEM); av_frame_copy_props(out, frame); + if (idx == 3 /* alpha */) + out->color_range = AVCOL_RANGE_JPEG; if (s->is_packed) { extract_from_packed(out->data[0], out->linesize[0], diff --git a/libavfilter/vf_fade.c b/libavfilter/vf_fade.c index 868e7cc16a6..cd64a82f25e 100644 --- a/libavfilter/vf_fade.c +++ b/libavfilter/vf_fade.c @@ -519,8 +519,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM static const AVOption fade_options[] = { - { "type", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, { .i64 = FADE_IN }, FADE_IN, FADE_OUT, FLAGS, "type" }, - { "t", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, { .i64 = FADE_IN }, FADE_IN, FADE_OUT, FLAGS, "type" }, + { "type", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, { .i64 = FADE_IN }, FADE_IN, FADE_OUT, FLAGS, .unit = "type" }, + { "t", "set the fade direction", OFFSET(type), AV_OPT_TYPE_INT, { .i64 = FADE_IN }, FADE_IN, FADE_OUT, FLAGS, .unit = "type" }, { "in", "fade-in", 0, AV_OPT_TYPE_CONST, { .i64 = FADE_IN }, .flags = FLAGS, .unit = "type" }, { "out", "fade-out", 0, AV_OPT_TYPE_CONST, { .i64 = FADE_OUT }, .flags = FLAGS, .unit = "type" }, { "start_frame", "Number of the first frame to which to apply the effect.", @@ -557,13 +557,6 @@ static const AVFilterPad avfilter_vf_fade_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_fade_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_fade = { .name = "fade", .description = NULL_IF_CONFIG_SMALL("Fade in/out input video."), @@ -571,7 +564,7 @@ const AVFilter ff_vf_fade = { .priv_size = sizeof(FadeContext), .priv_class = &fade_class, FILTER_INPUTS(avfilter_vf_fade_inputs), - FILTER_OUTPUTS(avfilter_vf_fade_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_feedback.c b/libavfilter/vf_feedback.c index 15cbf95c54d..2fb7d480572 100644 --- a/libavfilter/vf_feedback.c +++ b/libavfilter/vf_feedback.c @@ -27,6 +27,7 @@ #include "libavutil/internal.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" #include "video.h" @@ -163,11 +164,11 @@ static int activate(AVFilterContext *ctx) src->data[0] + y * src->linesize[0], src->width * s->max_step[0]); } - for (int i = 1; i < 3; i ++) { + for (int i = 1; i < 3; i++) { if (dst->data[i]) { for (int y = 0; y < src->height; y++) { - memmove(dst->data[i] + ((s->y + y) >> s->vsub) * dst->linesize[i] + ((s->x * s->max_step[i]) >> s->hsub), - src->data[i] + (y >> s->vsub) * src->linesize[i], (src->width * s->max_step[i]) >> s->hsub); + memmove(dst->data[i] + ((s->y + y) >> s->vsub) * dst->linesize[i] + (s->x >> s->hsub) * s->max_step[i], + src->data[i] + (y >> s->vsub) * src->linesize[i], (src->width >> s->hsub) * s->max_step[i]); } } } @@ -184,13 +185,16 @@ static int activate(AVFilterContext *ctx) return ret; } - if (!s->feed) { + if (!s->feed || ctx->is_disabled) { AVFrame *in = NULL; ret = ff_inlink_consume_frame(ctx->inputs[0], &in); if (ret < 0) return ret; + if (ret > 0 && ctx->is_disabled) + return ff_filter_frame(ctx->outputs[0], in); + if (ret > 0) { AVFrame *frame; @@ -213,7 +217,7 @@ static int activate(AVFilterContext *ctx) for (int i = 1; i < 3; i ++) { if (frame->data[i]) { frame->data[i] += (s->y >> s->vsub) * frame->linesize[i]; - frame->data[i] += (s->x * s->max_step[i]) >> s->hsub; + frame->data[i] += (s->x >> s->hsub) * s->max_step[i]; } } @@ -238,10 +242,11 @@ static int activate(AVFilterContext *ctx) return 0; } - if (!s->feed) { + if (!s->feed || ctx->is_disabled) { if (ff_outlink_frame_wanted(ctx->outputs[0])) { ff_inlink_request_frame(ctx->inputs[0]); - ff_inlink_request_frame(ctx->inputs[1]); + if (!ctx->is_disabled) + ff_inlink_request_frame(ctx->inputs[1]); return 0; } } @@ -329,5 +334,6 @@ const AVFilter ff_vf_feedback = { FILTER_INPUTS(inputs), FILTER_OUTPUTS(outputs), FILTER_QUERY_FUNC(query_formats), + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_fftdnoiz.c b/libavfilter/vf_fftdnoiz.c index 17dd4f1e778..93fc23bc40b 100644 --- a/libavfilter/vf_fftdnoiz.c +++ b/libavfilter/vf_fftdnoiz.c @@ -24,6 +24,7 @@ #include "libavutil/pixdesc.h" #include "libavutil/tx.h" #include "internal.h" +#include "video.h" #include "window_func.h" #define MAX_BLOCK 256 @@ -94,11 +95,11 @@ static const AVOption fftdnoiz_options[] = { { "overlap", "set block overlap", OFFSET(overlap), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0.2, 0.8, .flags = FLAGS }, { "method", "set method of denoising", - OFFSET(method), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags = TFLAGS, "method" }, + OFFSET(method), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags = TFLAGS, .unit = "method" }, { "wiener", "wiener method", - 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = TFLAGS, "method" }, + 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags = TFLAGS, .unit = "method" }, { "hard", "hard thresholding", - 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = TFLAGS, "method" }, + 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags = TFLAGS, .unit = "method" }, { "prev", "set number of previous frames for temporal denoising", OFFSET(nb_prev), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, .flags = FLAGS }, { "next", "set number of next frames for temporal denoising", diff --git a/libavfilter/vf_fftfilt.c b/libavfilter/vf_fftfilt.c index d2a6db24c8c..634d2a4911e 100644 --- a/libavfilter/vf_fftfilt.c +++ b/libavfilter/vf_fftfilt.c @@ -24,7 +24,8 @@ * FFT domain filtering. */ -#include "libavfilter/internal.h" +#include "internal.h" +#include "video.h" #include "libavutil/common.h" #include "libavutil/cpu.h" #include "libavutil/imgutils.h" @@ -95,7 +96,7 @@ static const AVOption fftfilt_options[] = { { "weight_Y", "set luminance expression in Y plane", OFFSET(weight_str[Y]), AV_OPT_TYPE_STRING, {.str = "1"}, 0, 0, FLAGS }, { "weight_U", "set chrominance expression in U plane", OFFSET(weight_str[U]), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, { "weight_V", "set chrominance expression in V plane", OFFSET(weight_str[V]), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, - { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" }, + { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, .unit = "eval" }, { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, { "frame", "eval expressions per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, {NULL}, @@ -592,20 +593,13 @@ static const AVFilterPad fftfilt_inputs[] = { }, }; -static const AVFilterPad fftfilt_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_fftfilt = { .name = "fftfilt", .description = NULL_IF_CONFIG_SMALL("Apply arbitrary expressions to pixels in frequency domain."), .priv_size = sizeof(FFTFILTContext), .priv_class = &fftfilt_class, FILTER_INPUTS(fftfilt_inputs), - FILTER_OUTPUTS(fftfilt_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts_fftfilt), .init = initialize, .uninit = uninit, diff --git a/libavfilter/vf_field.c b/libavfilter/vf_field.c index 8d06ffe6636..525f1da4a09 100644 --- a/libavfilter/vf_field.c +++ b/libavfilter/vf_field.c @@ -41,9 +41,9 @@ typedef struct FieldContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption field_options[] = { - {"type", "set field type (top or bottom)", OFFSET(type), AV_OPT_TYPE_INT, {.i64=FIELD_TYPE_TOP}, 0, 1, FLAGS, "field_type" }, - {"top", "select top field", 0, AV_OPT_TYPE_CONST, {.i64=FIELD_TYPE_TOP}, INT_MIN, INT_MAX, FLAGS, "field_type"}, - {"bottom", "select bottom field", 0, AV_OPT_TYPE_CONST, {.i64=FIELD_TYPE_BOTTOM}, INT_MIN, INT_MAX, FLAGS, "field_type"}, + {"type", "set field type (top or bottom)", OFFSET(type), AV_OPT_TYPE_INT, {.i64=FIELD_TYPE_TOP}, 0, 1, FLAGS, .unit = "field_type" }, + {"top", "select top field", 0, AV_OPT_TYPE_CONST, {.i64=FIELD_TYPE_TOP}, INT_MIN, INT_MAX, FLAGS, .unit = "field_type"}, + {"bottom", "select bottom field", 0, AV_OPT_TYPE_CONST, {.i64=FIELD_TYPE_BOTTOM}, INT_MIN, INT_MAX, FLAGS, .unit = "field_type"}, {NULL} }; @@ -73,7 +73,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) int i; inpicref->height = outlink->h; +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS inpicref->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + inpicref->flags &= ~AV_FRAME_FLAG_INTERLACED; for (i = 0; i < field->nb_planes; i++) { if (field->type == FIELD_TYPE_BOTTOM) diff --git a/libavfilter/vf_fieldhint.c b/libavfilter/vf_fieldhint.c index 4af9e269255..8d0e7157498 100644 --- a/libavfilter/vf_fieldhint.c +++ b/libavfilter/vf_fieldhint.c @@ -25,6 +25,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "video.h" @@ -56,10 +57,10 @@ typedef struct FieldHintContext { static const AVOption fieldhint_options[] = { { "hint", "set hint file", OFFSET(hint_file_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, - { "mode", "set hint mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_HINTS-1, FLAGS, "mode" }, - { "absolute", 0, 0, AV_OPT_TYPE_CONST, {.i64=ABSOLUTE_HINT}, 0, 0, FLAGS, "mode" }, - { "relative", 0, 0, AV_OPT_TYPE_CONST, {.i64=RELATIVE_HINT}, 0, 0, FLAGS, "mode" }, - { "pattern", 0, 0, AV_OPT_TYPE_CONST, {.i64=PATTERN_HINT}, 0, 0, FLAGS, "mode" }, + { "mode", "set hint mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_HINTS-1, FLAGS, .unit = "mode" }, + { "absolute", 0, 0, AV_OPT_TYPE_CONST, {.i64=ABSOLUTE_HINT}, 0, 0, FLAGS, .unit = "mode" }, + { "relative", 0, 0, AV_OPT_TYPE_CONST, {.i64=RELATIVE_HINT}, 0, 0, FLAGS, .unit = "mode" }, + { "pattern", 0, 0, AV_OPT_TYPE_CONST, {.i64=PATTERN_HINT}, 0, 0, FLAGS, .unit = "mode" }, { NULL } }; @@ -217,10 +218,20 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) switch (hint) { case '+': +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags |= AV_FRAME_FLAG_INTERLACED; break; case '-': +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags &= ~AV_FRAME_FLAG_INTERLACED; break; case '=': break; diff --git a/libavfilter/vf_fieldmatch.c b/libavfilter/vf_fieldmatch.c index bf946beec90..bcf0ba49e2f 100644 --- a/libavfilter/vf_fieldmatch.c +++ b/libavfilter/vf_fieldmatch.c @@ -38,7 +38,9 @@ #include "libavutil/timestamp.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "internal.h" +#include "video.h" #define INPUT_MAIN 0 #define INPUT_CLEANSRC 1 @@ -115,34 +117,34 @@ typedef struct FieldMatchContext { #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM static const AVOption fieldmatch_options[] = { - { "order", "specify the assumed field order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=FM_PARITY_AUTO}, -1, 1, FLAGS, "order" }, - { "auto", "auto detect parity", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_AUTO}, INT_MIN, INT_MAX, FLAGS, "order" }, - { "bff", "assume bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_BOTTOM}, INT_MIN, INT_MAX, FLAGS, "order" }, - { "tff", "assume top field first", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_TOP}, INT_MIN, INT_MAX, FLAGS, "order" }, - { "mode", "set the matching mode or strategy to use", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_PC_N}, MODE_PC, NB_MODE-1, FLAGS, "mode" }, - { "pc", "2-way match (p/c)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PC}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "pc_n", "2-way match + 3rd match on combed (p/c + u)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PC_N}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "pc_u", "2-way match + 3rd match (same order) on combed (p/c + u)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PC_U}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "pc_n_ub", "2-way match + 3rd match on combed + 4th/5th matches if still combed (p/c + u + u/b)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PC_N_UB}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "pcn", "3-way match (p/c/n)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PCN}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "pcn_ub", "3-way match + 4th/5th matches on combed (p/c/n + u/b)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PCN_UB}, INT_MIN, INT_MAX, FLAGS, "mode" }, + { "order", "specify the assumed field order", OFFSET(order), AV_OPT_TYPE_INT, {.i64=FM_PARITY_AUTO}, -1, 1, FLAGS, .unit = "order" }, + { "auto", "auto detect parity", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_AUTO}, INT_MIN, INT_MAX, FLAGS, .unit = "order" }, + { "bff", "assume bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_BOTTOM}, INT_MIN, INT_MAX, FLAGS, .unit = "order" }, + { "tff", "assume top field first", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_TOP}, INT_MIN, INT_MAX, FLAGS, .unit = "order" }, + { "mode", "set the matching mode or strategy to use", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_PC_N}, MODE_PC, NB_MODE-1, FLAGS, .unit = "mode" }, + { "pc", "2-way match (p/c)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PC}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "pc_n", "2-way match + 3rd match on combed (p/c + u)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PC_N}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "pc_u", "2-way match + 3rd match (same order) on combed (p/c + u)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PC_U}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "pc_n_ub", "2-way match + 3rd match on combed + 4th/5th matches if still combed (p/c + u + u/b)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PC_N_UB}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "pcn", "3-way match (p/c/n)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PCN}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "pcn_ub", "3-way match + 4th/5th matches on combed (p/c/n + u/b)", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PCN_UB}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, { "ppsrc", "mark main input as a pre-processed input and activate clean source input stream", OFFSET(ppsrc), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, - { "field", "set the field to match from", OFFSET(field), AV_OPT_TYPE_INT, {.i64=FM_PARITY_AUTO}, -1, 1, FLAGS, "field" }, - { "auto", "automatic (same value as 'order')", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_AUTO}, INT_MIN, INT_MAX, FLAGS, "field" }, - { "bottom", "bottom field", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_BOTTOM}, INT_MIN, INT_MAX, FLAGS, "field" }, - { "top", "top field", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_TOP}, INT_MIN, INT_MAX, FLAGS, "field" }, + { "field", "set the field to match from", OFFSET(field), AV_OPT_TYPE_INT, {.i64=FM_PARITY_AUTO}, -1, 1, FLAGS, .unit = "field" }, + { "auto", "automatic (same value as 'order')", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_AUTO}, INT_MIN, INT_MAX, FLAGS, .unit = "field" }, + { "bottom", "bottom field", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_BOTTOM}, INT_MIN, INT_MAX, FLAGS, .unit = "field" }, + { "top", "top field", 0, AV_OPT_TYPE_CONST, {.i64=FM_PARITY_TOP}, INT_MIN, INT_MAX, FLAGS, .unit = "field" }, { "mchroma", "set whether or not chroma is included during the match comparisons", OFFSET(mchroma), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, { "y0", "define an exclusion band which excludes the lines between y0 and y1 from the field matching decision", OFFSET(y0), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, { "y1", "define an exclusion band which excludes the lines between y0 and y1 from the field matching decision", OFFSET(y1), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, { "scthresh", "set scene change detection threshold", OFFSET(scthresh_flt), AV_OPT_TYPE_DOUBLE, {.dbl=12}, 0, 100, FLAGS }, - { "combmatch", "set combmatching mode", OFFSET(combmatch), AV_OPT_TYPE_INT, {.i64=COMBMATCH_SC}, COMBMATCH_NONE, NB_COMBMATCH-1, FLAGS, "combmatching" }, - { "none", "disable combmatching", 0, AV_OPT_TYPE_CONST, {.i64=COMBMATCH_NONE}, INT_MIN, INT_MAX, FLAGS, "combmatching" }, - { "sc", "enable combmatching only on scene change", 0, AV_OPT_TYPE_CONST, {.i64=COMBMATCH_SC}, INT_MIN, INT_MAX, FLAGS, "combmatching" }, - { "full", "enable combmatching all the time", 0, AV_OPT_TYPE_CONST, {.i64=COMBMATCH_FULL}, INT_MIN, INT_MAX, FLAGS, "combmatching" }, - { "combdbg", "enable comb debug", OFFSET(combdbg), AV_OPT_TYPE_INT, {.i64=COMBDBG_NONE}, COMBDBG_NONE, NB_COMBDBG-1, FLAGS, "dbglvl" }, - { "none", "no forced calculation", 0, AV_OPT_TYPE_CONST, {.i64=COMBDBG_NONE}, INT_MIN, INT_MAX, FLAGS, "dbglvl" }, - { "pcn", "calculate p/c/n", 0, AV_OPT_TYPE_CONST, {.i64=COMBDBG_PCN}, INT_MIN, INT_MAX, FLAGS, "dbglvl" }, - { "pcnub", "calculate p/c/n/u/b", 0, AV_OPT_TYPE_CONST, {.i64=COMBDBG_PCNUB}, INT_MIN, INT_MAX, FLAGS, "dbglvl" }, + { "combmatch", "set combmatching mode", OFFSET(combmatch), AV_OPT_TYPE_INT, {.i64=COMBMATCH_SC}, COMBMATCH_NONE, NB_COMBMATCH-1, FLAGS, .unit = "combmatching" }, + { "none", "disable combmatching", 0, AV_OPT_TYPE_CONST, {.i64=COMBMATCH_NONE}, INT_MIN, INT_MAX, FLAGS, .unit = "combmatching" }, + { "sc", "enable combmatching only on scene change", 0, AV_OPT_TYPE_CONST, {.i64=COMBMATCH_SC}, INT_MIN, INT_MAX, FLAGS, .unit = "combmatching" }, + { "full", "enable combmatching all the time", 0, AV_OPT_TYPE_CONST, {.i64=COMBMATCH_FULL}, INT_MIN, INT_MAX, FLAGS, .unit = "combmatching" }, + { "combdbg", "enable comb debug", OFFSET(combdbg), AV_OPT_TYPE_INT, {.i64=COMBDBG_NONE}, COMBDBG_NONE, NB_COMBDBG-1, FLAGS, .unit = "dbglvl" }, + { "none", "no forced calculation", 0, AV_OPT_TYPE_CONST, {.i64=COMBDBG_NONE}, INT_MIN, INT_MAX, FLAGS, .unit = "dbglvl" }, + { "pcn", "calculate p/c/n", 0, AV_OPT_TYPE_CONST, {.i64=COMBDBG_PCN}, INT_MIN, INT_MAX, FLAGS, .unit = "dbglvl" }, + { "pcnub", "calculate p/c/n/u/b", 0, AV_OPT_TYPE_CONST, {.i64=COMBDBG_PCNUB}, INT_MIN, INT_MAX, FLAGS, .unit = "dbglvl" }, { "cthresh", "set the area combing threshold used for combed frame detection", OFFSET(cthresh), AV_OPT_TYPE_INT, {.i64= 9}, -1, 0xff, FLAGS }, { "chroma", "set whether or not chroma is considered in the combed frame decision", OFFSET(chroma), AV_OPT_TYPE_BOOL,{.i64= 0}, 0, 1, FLAGS }, { "blockx", "set the x-axis size of the window used during combed frame detection", OFFSET(blockx), AV_OPT_TYPE_INT, {.i64=16}, 4, 1<<9, FLAGS }, @@ -714,7 +716,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) in = fm->src; /* parity */ - order = fm->order != FM_PARITY_AUTO ? fm->order : (in->interlaced_frame ? in->top_field_first : 1); + order = fm->order != FM_PARITY_AUTO ? fm->order : ((in->flags & AV_FRAME_FLAG_INTERLACED) ? + !!(in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 1); field = fm->field != FM_PARITY_AUTO ? fm->field : order; av_assert0(order == 0 || order == 1 || field == 0 || field == 1); fxo = field ^ order ? fxo1m : fxo0m; @@ -819,16 +822,30 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) /* mark the frame we are unable to match properly as interlaced so a proper * de-interlacer can take the relay */ +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS dst->interlaced_frame = interlaced_frame; - if (dst->interlaced_frame) { +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (interlaced_frame) { + dst->flags |= AV_FRAME_FLAG_INTERLACED; av_log(ctx, AV_LOG_WARNING, "Frame #%"PRId64" at %s is still interlaced\n", outlink->frame_count_in, av_ts2timestr(in->pts, &inlink->time_base)); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS dst->top_field_first = field; - } +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (field) + dst->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + dst->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + } else + dst->flags &= ~AV_FRAME_FLAG_INTERLACED; av_log(ctx, AV_LOG_DEBUG, "SC:%d | COMBS: %3d %3d %3d %3d %3d (combpel=%d)" " match=%d combed=%s\n", sc, combs[0], combs[1], combs[2], combs[3], combs[4], - fm->combpel, match, dst->interlaced_frame ? "YES" : "NO"); + fm->combpel, match, (dst->flags & AV_FRAME_FLAG_INTERLACED) ? "YES" : "NO"); fail: for (i = 0; i < FF_ARRAY_ELEMS(gen_frames); i++) diff --git a/libavfilter/vf_fieldorder.c b/libavfilter/vf_fieldorder.c index 52b4b3d8aa0..21d62b93f19 100644 --- a/libavfilter/vf_fieldorder.c +++ b/libavfilter/vf_fieldorder.c @@ -76,11 +76,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) uint8_t *dst, *src; AVFrame *out; - if (!frame->interlaced_frame || - frame->top_field_first == s->dst_tff) { + if (!(frame->flags & AV_FRAME_FLAG_INTERLACED) || + !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) == s->dst_tff) { av_log(ctx, AV_LOG_VERBOSE, "Skipping %s.\n", - frame->interlaced_frame ? + (frame->flags & AV_FRAME_FLAG_INTERLACED) ? "frame with same field order" : "progressive frame"); return ff_filter_frame(outlink, frame); } @@ -140,7 +140,15 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) } } } +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->top_field_first = s->dst_tff; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (s->dst_tff) + out->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + out->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; if (frame != out) av_frame_free(&frame); @@ -151,7 +159,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption fieldorder_options[] = { - { "order", "output field order", OFFSET(dst_tff), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS, "order" }, + { "order", "output field order", OFFSET(dst_tff), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS, .unit = "order" }, { "bff", "bottom field first", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, .flags=FLAGS, .unit = "order" }, { "tff", "top field first", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, .flags=FLAGS, .unit = "order" }, { NULL } @@ -168,20 +176,13 @@ static const AVFilterPad avfilter_vf_fieldorder_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_fieldorder_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_fieldorder = { .name = "fieldorder", .description = NULL_IF_CONFIG_SMALL("Set the field order."), .priv_size = sizeof(FieldOrderContext), .priv_class = &fieldorder_class, FILTER_INPUTS(avfilter_vf_fieldorder_inputs), - FILTER_OUTPUTS(avfilter_vf_fieldorder_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_fillborders.c b/libavfilter/vf_fillborders.c index 83f206aeb16..2778cb486b6 100644 --- a/libavfilter/vf_fillborders.c +++ b/libavfilter/vf_fillborders.c @@ -25,7 +25,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -83,7 +82,7 @@ static void smear_borders8(FillBordersContext *s, AVFrame *frame) for (p = 0; p < s->nb_planes; p++) { uint8_t *ptr = frame->data[p]; - int linesize = frame->linesize[p]; + ptrdiff_t linesize = frame->linesize[p]; for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { memset(ptr + y * linesize, @@ -113,7 +112,7 @@ static void smear_borders16(FillBordersContext *s, AVFrame *frame) for (p = 0; p < s->nb_planes; p++) { uint16_t *ptr = (uint16_t *)frame->data[p]; - int linesize = frame->linesize[p] / 2; + ptrdiff_t linesize = frame->linesize[p] / 2; for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { for (x = 0; x < s->borders[p].left; x++) { @@ -145,7 +144,7 @@ static void mirror_borders8(FillBordersContext *s, AVFrame *frame) for (p = 0; p < s->nb_planes; p++) { uint8_t *ptr = frame->data[p]; - int linesize = frame->linesize[p]; + ptrdiff_t linesize = frame->linesize[p]; for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { for (x = 0; x < s->borders[p].left; x++) { @@ -178,7 +177,7 @@ static void mirror_borders16(FillBordersContext *s, AVFrame *frame) for (p = 0; p < s->nb_planes; p++) { uint16_t *ptr = (uint16_t *)frame->data[p]; - int linesize = frame->linesize[p] / 2; + ptrdiff_t linesize = frame->linesize[p] / 2; for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { for (x = 0; x < s->borders[p].left; x++) { @@ -212,7 +211,7 @@ static void fixed_borders8(FillBordersContext *s, AVFrame *frame) for (p = 0; p < s->nb_planes; p++) { uint8_t *ptr = frame->data[p]; uint8_t fill = s->fill[p]; - int linesize = frame->linesize[p]; + ptrdiff_t linesize = frame->linesize[p]; for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { memset(ptr + y * linesize, fill, s->borders[p].left); @@ -237,7 +236,7 @@ static void fixed_borders16(FillBordersContext *s, AVFrame *frame) for (p = 0; p < s->nb_planes; p++) { uint16_t *ptr = (uint16_t *)frame->data[p]; uint16_t fill = s->fill[p] << (s->depth - 8); - int linesize = frame->linesize[p] / 2; + ptrdiff_t linesize = frame->linesize[p] / 2; for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { for (x = 0; x < s->borders[p].left; x++) { @@ -269,7 +268,7 @@ static void reflect_borders8(FillBordersContext *s, AVFrame *frame) for (p = 0; p < s->nb_planes; p++) { uint8_t *ptr = frame->data[p]; - int linesize = frame->linesize[p]; + ptrdiff_t linesize = frame->linesize[p]; for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { for (x = 0; x < s->borders[p].left; x++) { @@ -302,7 +301,7 @@ static void reflect_borders16(FillBordersContext *s, AVFrame *frame) for (p = 0; p < s->nb_planes; p++) { uint16_t *ptr = (uint16_t *)frame->data[p]; - int linesize = frame->linesize[p] / 2; + ptrdiff_t linesize = frame->linesize[p] / 2; for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { for (x = 0; x < s->borders[p].left; x++) { @@ -335,7 +334,7 @@ static void wrap_borders8(FillBordersContext *s, AVFrame *frame) for (p = 0; p < s->nb_planes; p++) { uint8_t *ptr = frame->data[p]; - int linesize = frame->linesize[p]; + ptrdiff_t linesize = frame->linesize[p]; for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { for (x = 0; x < s->borders[p].left; x++) { @@ -368,7 +367,7 @@ static void wrap_borders16(FillBordersContext *s, AVFrame *frame) for (p = 0; p < s->nb_planes; p++) { uint16_t *ptr = (uint16_t *)frame->data[p]; - int linesize = frame->linesize[p] / 2; + ptrdiff_t linesize = frame->linesize[p] / 2; for (y = s->borders[p].top; y < s->planeheight[p] - s->borders[p].bottom; y++) { for (x = 0; x < s->borders[p].left; x++) { @@ -412,7 +411,7 @@ static void fade_borders8(FillBordersContext *s, AVFrame *frame) for (p = 0; p < s->nb_planes; p++) { uint8_t *ptr = frame->data[p]; const uint8_t fill = s->fill[p]; - const int linesize = frame->linesize[p]; + const ptrdiff_t linesize = frame->linesize[p]; const int start_left = s->borders[p].left; const int start_right = s->planewidth[p] - s->borders[p].right; const int start_top = s->borders[p].top; @@ -454,7 +453,7 @@ static void fade_borders16(FillBordersContext *s, AVFrame *frame) for (p = 0; p < s->nb_planes; p++) { uint16_t *ptr = (uint16_t *)frame->data[p]; const uint16_t fill = s->fill[p] << (depth - 8); - const int linesize = frame->linesize[p] / 2; + const ptrdiff_t linesize = frame->linesize[p] / 2; const int start_left = s->borders[p].left; const int start_right = s->planewidth[p] - s->borders[p].right; const int start_top = s->borders[p].top; @@ -492,7 +491,7 @@ static void margins_borders8(FillBordersContext *s, AVFrame *frame) { for (int p = 0; p < s->nb_planes; p++) { uint8_t *ptr = (uint8_t *)frame->data[p]; - const int linesize = frame->linesize[p]; + const ptrdiff_t linesize = frame->linesize[p]; const int left = s->borders[p].left; const int right = s->borders[p].right; const int top = s->borders[p].top; @@ -537,7 +536,7 @@ static void margins_borders16(FillBordersContext *s, AVFrame *frame) { for (int p = 0; p < s->nb_planes; p++) { uint16_t *ptr = (uint16_t *)frame->data[p]; - const int linesize = frame->linesize[p] / 2; + const ptrdiff_t linesize = frame->linesize[p] / 2; const int left = s->borders[p].left; const int right = s->borders[p].right; const int top = s->borders[p].top; @@ -683,14 +682,14 @@ static const AVOption fillborders_options[] = { { "right", "set the right fill border", OFFSET(right), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, { "top", "set the top fill border", OFFSET(top), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, { "bottom", "set the bottom fill border", OFFSET(bottom), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, - { "mode", "set the fill borders mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=FM_SMEAR}, 0, FM_NB_MODES-1, FLAGS, "mode" }, - { "smear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_SMEAR}, 0, 0, FLAGS, "mode" }, - { "mirror", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_MIRROR}, 0, 0, FLAGS, "mode" }, - { "fixed", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_FIXED}, 0, 0, FLAGS, "mode" }, - { "reflect",NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_REFLECT},0, 0, FLAGS, "mode" }, - { "wrap", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_WRAP}, 0, 0, FLAGS, "mode" }, - { "fade", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_FADE}, 0, 0, FLAGS, "mode" }, - { "margins",NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_MARGINS},0, 0, FLAGS, "mode" }, + { "mode", "set the fill borders mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=FM_SMEAR}, 0, FM_NB_MODES-1, FLAGS, .unit = "mode" }, + { "smear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_SMEAR}, 0, 0, FLAGS, .unit = "mode" }, + { "mirror", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_MIRROR}, 0, 0, FLAGS, .unit = "mode" }, + { "fixed", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_FIXED}, 0, 0, FLAGS, .unit = "mode" }, + { "reflect",NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_REFLECT},0, 0, FLAGS, .unit = "mode" }, + { "wrap", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_WRAP}, 0, 0, FLAGS, .unit = "mode" }, + { "fade", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_FADE}, 0, 0, FLAGS, .unit = "mode" }, + { "margins",NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_MARGINS},0, 0, FLAGS, .unit = "mode" }, { "color", "set the color for the fixed/fade mode", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str = "black"}, .flags = FLAGS }, { NULL } }; @@ -707,20 +706,13 @@ static const AVFilterPad fillborders_inputs[] = { }, }; -static const AVFilterPad fillborders_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_fillborders = { .name = "fillborders", .description = NULL_IF_CONFIG_SMALL("Fill borders of the input video."), .priv_size = sizeof(FillBordersContext), .priv_class = &fillborders_class, FILTER_INPUTS(fillborders_inputs), - FILTER_OUTPUTS(fillborders_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = process_command, diff --git a/libavfilter/vf_find_rect.c b/libavfilter/vf_find_rect.c index a536d669d10..9f4ee1e32fb 100644 --- a/libavfilter/vf_find_rect.c +++ b/libavfilter/vf_find_rect.c @@ -25,6 +25,7 @@ #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "internal.h" +#include "video.h" #include "lavfutils.h" @@ -281,13 +282,6 @@ static const AVFilterPad foc_inputs[] = { }, }; -static const AVFilterPad foc_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_find_rect = { .name = "find_rect", .description = NULL_IF_CONFIG_SMALL("Find a user specified object."), @@ -296,7 +290,7 @@ const AVFilter ff_vf_find_rect = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(foc_inputs), - FILTER_OUTPUTS(foc_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P), .priv_class = &find_rect_class, }; diff --git a/libavfilter/vf_flip_vulkan.c b/libavfilter/vf_flip_vulkan.c index 0223786ef1f..ecd2567ebc2 100644 --- a/libavfilter/vf_flip_vulkan.c +++ b/libavfilter/vf_flip_vulkan.c @@ -1,5 +1,7 @@ /* * copyright (c) 2021 Wu Jianhua + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -20,9 +22,9 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" - -#define CGS 32 +#include "video.h" enum FlipType { FLIP_VERTICAL, @@ -32,32 +34,50 @@ enum FlipType { typedef struct FlipVulkanContext { FFVulkanContext vkctx; - FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl; - - VkDescriptorImageInfo input_images[3]; - VkDescriptorImageInfo output_images[3]; int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + FFVkSPIRVShader shd; + VkSampler sampler; } FlipVulkanContext; static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in, enum FlipType type) { int err = 0; - FFVkSPIRVShader *shd; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; FlipVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_LINEAR)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "flip_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); - FFVulkanDescriptorSetBinding image_descs[] = { + desc = (FFVulkanDescriptorSetBinding []) { { .name = "input_image", .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .dimensions = 2, .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->input_images, + .samplers = DUP_SAMPLER(s->sampler), }, { .name = "output_image", @@ -67,167 +87,75 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in, enum FlipType .dimensions = 2, .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->output_images, }, }; - image_descs[0].sampler = ff_vk_init_sampler(vkctx, 1, VK_FILTER_LINEAR); - if (!image_descs[0].sampler) - return AVERROR_EXTERNAL; + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 2, 0, 0)); - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - - { - s->pl = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl) - return AVERROR(ENOMEM); - - shd = ff_vk_init_shader(s->pl, "flip_compute", image_descs[0].stages); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, (int [3]){ CGS, 1, 1 }); - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, image_descs, FF_ARRAY_ELEMS(image_descs), 0)); - - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - for (int i = 0; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_image[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - switch (type) - { - case FLIP_HORIZONTAL: - GLSLF(2, vec4 res = texture(input_image[%i], ivec2(size.x - pos.x, pos.y)); ,i); - break; - case FLIP_VERTICAL: - GLSLF(2, vec4 res = texture(input_image[%i], ivec2(pos.x, size.y - pos.y)); ,i); - break; - case FLIP_BOTH: - GLSLF(2, vec4 res = texture(input_image[%i], ivec2(size.xy - pos.xy));, i); - break; - default: - GLSLF(2, vec4 res = texture(input_image[%i], pos); ,i); - break; - } - GLSLF(2, imageStore(output_image[%i], pos, res); ,i); - GLSLC(1, } ); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 size; ); + GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + for (int i = 0; i < planes; i++) { + GLSLC(0, ); + GLSLF(1, size = imageSize(output_image[%i]); ,i); + GLSLC(1, if (IS_WITHIN(pos, size)) { ); + switch (type) + { + case FLIP_HORIZONTAL: + GLSLF(2, vec4 res = texture(input_image[%i], ivec2(size.x - pos.x, pos.y)); ,i); + break; + case FLIP_VERTICAL: + GLSLF(2, vec4 res = texture(input_image[%i], ivec2(pos.x, size.y - pos.y)); ,i); + break; + case FLIP_BOTH: + GLSLF(2, vec4 res = texture(input_image[%i], ivec2(size.xy - pos.xy));, i); + break; + default: + GLSLF(2, vec4 res = texture(input_image[%i], pos); ,i); + break; } - GLSLC(0, } ); - - RET(ff_vk_compile_shader(vkctx, shd, "main")); - RET(ff_vk_init_pipeline_layout(vkctx, s->pl)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl)); + GLSLF(2, imageStore(output_image[%i], pos, res); ,i); + GLSLC(1, } ); } + GLSLC(0, } ); + + RET(spv->compile_shader(spv, ctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); s->initialized = 1; fail: + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); + return err; } static av_cold void flip_vulkan_uninit(AVFilterContext *avctx) { FlipVulkanContext *s = avctx->priv; - ff_vk_uninit(&s->vkctx); - s->initialized = 0; -} - -static int process_frames(AVFilterContext *avctx, AVFrame *outframe, AVFrame *inframe) -{ - int err = 0; - VkCommandBuffer cmd_buf; - FlipVulkanContext *s = avctx->priv; FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &s->vkctx.vkfn; - AVVkFrame *in = (AVVkFrame *)inframe->data[0]; - AVVkFrame *out = (AVVkFrame *)outframe->data[0]; - const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - const VkFormat *input_formats = av_vkfmt_from_pixfmt(s->vkctx.input_format); - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->input_images[i].imageView, in->img[i], - input_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } + FFVulkanFunctions *vk = &vkctx->vkfn; - ff_vk_update_descriptor_set(vkctx, s->pl, 0); + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); - for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier barriers[] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = in->layout[i], - .newLayout = s->input_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = in->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(barriers), barriers); - - in->layout[i] = barriers[0].newLayout; - in->access[i] = barriers[0].dstAccessMask; - - out->layout[i] = barriers[1].newLayout; - out->access[i] = barriers[1].dstAccessMask; - } - - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl); - vk->CmdDispatch(cmd_buf, FFALIGN(s->vkctx.output_width, CGS)/CGS, - s->vkctx.output_height, 1); - - ff_vk_add_exec_dep(vkctx, s->exec, inframe, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, outframe, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(vkctx, s->exec); - if (err) - return err; + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); - ff_vk_qf_rotate(&s->qf); + ff_vk_uninit(&s->vkctx); - return 0; -fail: - ff_vk_discard_exec_deps(s->exec); - return err; + s->initialized = 0; } static int filter_frame(AVFilterLink *link, AVFrame *in, enum FlipType type) @@ -247,7 +175,8 @@ static int filter_frame(AVFilterLink *link, AVFrame *in, enum FlipType type) if (!s->initialized) RET(init_filter(ctx, in, type)); - RET(process_frames(ctx, out, in)); + RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, out, in, + s->sampler, NULL, 0)); RET(av_frame_copy_props(out, in)); @@ -366,4 +295,5 @@ const AVFilter ff_vf_flip_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &flip_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_floodfill.c b/libavfilter/vf_floodfill.c index 212255a784d..d5fa42651ac 100644 --- a/libavfilter/vf_floodfill.c +++ b/libavfilter/vf_floodfill.c @@ -19,11 +19,10 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -385,13 +384,6 @@ static const AVFilterPad floodfill_inputs[] = { }, }; -static const AVFilterPad floodfill_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(FloodfillContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM @@ -418,7 +410,7 @@ const AVFilter ff_vf_floodfill = { .priv_class = &floodfill_class, .uninit = uninit, FILTER_INPUTS(floodfill_inputs), - FILTER_OUTPUTS(floodfill_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_format.c b/libavfilter/vf_format.c index 24b1c9ca618..3a353bbb0d7 100644 --- a/libavfilter/vf_format.c +++ b/libavfilter/vf_format.c @@ -40,113 +40,125 @@ typedef struct FormatContext { const AVClass *class; char *pix_fmts; + char *csps; + char *ranges; - /** - * pix_fmts parsed into AVPixelFormats and terminated with - * AV_PIX_FMT_NONE - */ - enum AVPixelFormat *formats; + AVFilterFormats *formats; ///< parsed from `pix_fmts` + AVFilterFormats *color_spaces; ///< parsed from `csps` + AVFilterFormats *color_ranges; ///< parsed from `ranges` } FormatContext; static av_cold void uninit(AVFilterContext *ctx) { FormatContext *s = ctx->priv; - av_freep(&s->formats); + ff_formats_unref(&s->formats); + ff_formats_unref(&s->color_spaces); + ff_formats_unref(&s->color_ranges); +} + +static av_cold int invert_formats(AVFilterFormats **fmts, + AVFilterFormats *allfmts) +{ + if (!allfmts) + return AVERROR(ENOMEM); + if (!*fmts) { + /* empty fmt list means no restriction, regardless of filter type */ + ff_formats_unref(&allfmts); + return 0; + } + + for (int i = 0; i < allfmts->nb_formats; i++) { + for (int j = 0; j < (*fmts)->nb_formats; j++) { + if (allfmts->formats[i] == (*fmts)->formats[j]) { + /* format is forbidden, remove it from allfmts list */ + memmove(&allfmts->formats[i], &allfmts->formats[i+1], + (allfmts->nb_formats - (i+1)) * sizeof(*allfmts->formats)); + allfmts->nb_formats--; + i--; /* repeat loop with same idx */ + break; + } + } + } + + ff_formats_unref(fmts); + *fmts = allfmts; + return 0; } static av_cold int init(AVFilterContext *ctx) { FormatContext *s = ctx->priv; - char *cur, *sep; - int nb_formats = 1; - int i; + enum AVPixelFormat pix_fmt; int ret; - if (!s->pix_fmts) { - av_log(ctx, AV_LOG_ERROR, "Empty output format string.\n"); - return AVERROR(EINVAL); + for (char *sep, *cur = s->pix_fmts; cur; cur = sep) { + sep = strchr(cur, '|'); + if (sep && *sep) + *sep++ = 0; + if ((ret = ff_parse_pixel_format(&pix_fmt, cur, ctx)) < 0 || + (ret = ff_add_format(&s->formats, pix_fmt)) < 0) + return ret; } - /* count the formats */ - cur = s->pix_fmts; - while ((cur = strchr(cur, '|'))) { - nb_formats++; - if (*cur) - cur++; + for (char *sep, *cur = s->csps; cur; cur = sep) { + sep = strchr(cur, '|'); + if (sep && *sep) + *sep++ = 0; + if ((ret = av_color_space_from_name(cur)) < 0 || + (ret = ff_add_format(&s->color_spaces, ret)) < 0) + return ret; } - s->formats = av_malloc_array(nb_formats + 1, sizeof(*s->formats)); - if (!s->formats) - return AVERROR(ENOMEM); - - /* parse the list of formats */ - cur = s->pix_fmts; - for (i = 0; i < nb_formats; i++) { + for (char *sep, *cur = s->ranges; cur; cur = sep) { sep = strchr(cur, '|'); - if (sep) + if (sep && *sep) *sep++ = 0; - - if ((ret = ff_parse_pixel_format(&s->formats[i], cur, ctx)) < 0) + if ((ret = av_color_range_from_name(cur)) < 0 || + (ret = ff_add_format(&s->color_ranges, ret)) < 0) return ret; - - cur = sep; } - s->formats[nb_formats] = AV_PIX_FMT_NONE; if (!strcmp(ctx->filter->name, "noformat")) { - const AVPixFmtDescriptor *desc = NULL; - enum AVPixelFormat *formats_allowed; - int nb_formats_lavu = 0, nb_formats_allowed = 0; - - /* count the formats known to lavu */ - while ((desc = av_pix_fmt_desc_next(desc))) - nb_formats_lavu++; - - formats_allowed = av_malloc_array(nb_formats_lavu + 1, sizeof(*formats_allowed)); - if (!formats_allowed) - return AVERROR(ENOMEM); - - /* for each format known to lavu, check if it's in the list of - * forbidden formats */ - while ((desc = av_pix_fmt_desc_next(desc))) { - enum AVPixelFormat pix_fmt = av_pix_fmt_desc_get_id(desc); - - for (i = 0; i < nb_formats; i++) { - if (s->formats[i] == pix_fmt) - break; - } - if (i < nb_formats) - continue; - - formats_allowed[nb_formats_allowed++] = pix_fmt; - } - formats_allowed[nb_formats_allowed] = AV_PIX_FMT_NONE; - av_freep(&s->formats); - s->formats = formats_allowed; + if ((ret = invert_formats(&s->formats, ff_all_formats(AVMEDIA_TYPE_VIDEO))) < 0 || + (ret = invert_formats(&s->color_spaces, ff_all_color_spaces())) < 0 || + (ret = invert_formats(&s->color_ranges, ff_all_color_ranges())) < 0) + return ret; } + /* hold on to a ref for the lifetime of the filter */ + if (s->formats && (ret = ff_formats_ref(s->formats, &s->formats)) < 0 || + s->color_spaces && (ret = ff_formats_ref(s->color_spaces, &s->color_spaces)) < 0 || + s->color_ranges && (ret = ff_formats_ref(s->color_ranges, &s->color_ranges)) < 0) + return ret; + return 0; } static int query_formats(AVFilterContext *ctx) { FormatContext *s = ctx->priv; + int ret; - return ff_set_common_formats_from_list(ctx, s->formats); + if (s->formats && (ret = ff_set_common_formats(ctx, s->formats)) < 0 || + s->color_spaces && (ret = ff_set_common_color_spaces(ctx, s->color_spaces)) < 0 || + s->color_ranges && (ret = ff_set_common_color_ranges(ctx, s->color_ranges)) < 0) + return ret; + + return 0; } #define OFFSET(x) offsetof(FormatContext, x) static const AVOption options[] = { { "pix_fmts", "A '|'-separated list of pixel formats", OFFSET(pix_fmts), AV_OPT_TYPE_STRING, .flags = AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM }, + { "color_spaces", "A '|'-separated list of color spaces", OFFSET(csps), AV_OPT_TYPE_STRING, .flags = AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM }, + { "color_ranges", "A '|'-separated list of color ranges", OFFSET(ranges), AV_OPT_TYPE_STRING, .flags = AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM }, { NULL } }; AVFILTER_DEFINE_CLASS_EXT(format, "(no)format", options); -#if CONFIG_FORMAT_FILTER - -static const AVFilterPad avfilter_vf_format_inputs[] = { +static const AVFilterPad inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, @@ -154,13 +166,7 @@ static const AVFilterPad avfilter_vf_format_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_format_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - +#if CONFIG_FORMAT_FILTER const AVFilter ff_vf_format = { .name = "format", .description = NULL_IF_CONFIG_SMALL("Convert the input video to one of the specified pixel formats."), @@ -173,30 +179,14 @@ const AVFilter ff_vf_format = { .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_vf_format_inputs), - FILTER_OUTPUTS(avfilter_vf_format_outputs), + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; #endif /* CONFIG_FORMAT_FILTER */ #if CONFIG_NOFORMAT_FILTER - -static const AVFilterPad avfilter_vf_noformat_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .get_buffer.video = ff_null_get_video_buffer, - }, -}; - -static const AVFilterPad avfilter_vf_noformat_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - const AVFilter ff_vf_noformat = { .name = "noformat", .description = NULL_IF_CONFIG_SMALL("Force libavfilter not to use any of the specified pixel formats for the input to the next filter."), @@ -209,8 +199,8 @@ const AVFilter ff_vf_noformat = { .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_vf_noformat_inputs), - FILTER_OUTPUTS(avfilter_vf_noformat_outputs), + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/vf_fps.c b/libavfilter/vf_fps.c index 051d278f54d..02636d0f787 100644 --- a/libavfilter/vf_fps.c +++ b/libavfilter/vf_fps.c @@ -34,8 +34,10 @@ #include "libavutil/mathematics.h" #include "libavutil/opt.h" #include "avfilter.h" +#include "ccfifo.h" #include "filters.h" #include "internal.h" +#include "video.h" enum EOFAction { EOF_ACTION_ROUND, @@ -85,6 +87,7 @@ typedef struct FPSContext { AVFrame *frames[2]; ///< buffered frames int frames_count; ///< number of buffered frames + CCFifo cc_fifo; ///< closed captions int64_t next_pts; ///< pts of the next frame to output @@ -102,15 +105,15 @@ typedef struct FPSContext { static const AVOption fps_options[] = { { "fps", "A string describing desired output framerate", OFFSET(framerate), AV_OPT_TYPE_STRING, { .str = "25" }, 0, 0, V|F }, { "start_time", "Assume the first PTS should be this value.", OFFSET(start_time), AV_OPT_TYPE_DOUBLE, { .dbl = DBL_MAX}, -DBL_MAX, DBL_MAX, V|F }, - { "round", "set rounding method for timestamps", OFFSET(rounding), AV_OPT_TYPE_INT, { .i64 = AV_ROUND_NEAR_INF }, 0, 5, V|F, "round" }, - { "zero", "round towards 0", 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_ZERO }, 0, 0, V|F, "round" }, - { "inf", "round away from 0", 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_INF }, 0, 0, V|F, "round" }, - { "down", "round towards -infty", 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_DOWN }, 0, 0, V|F, "round" }, - { "up", "round towards +infty", 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_UP }, 0, 0, V|F, "round" }, - { "near", "round to nearest", 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_NEAR_INF }, 0, 0, V|F, "round" }, - { "eof_action", "action performed for last frame", OFFSET(eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_ROUND }, 0, EOF_ACTION_NB-1, V|F, "eof_action" }, - { "round", "round similar to other frames", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ROUND }, 0, 0, V|F, "eof_action" }, - { "pass", "pass through last frame", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, 0, 0, V|F, "eof_action" }, + { "round", "set rounding method for timestamps", OFFSET(rounding), AV_OPT_TYPE_INT, { .i64 = AV_ROUND_NEAR_INF }, 0, 5, V|F, .unit = "round" }, + { "zero", "round towards 0", 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_ZERO }, 0, 0, V|F, .unit = "round" }, + { "inf", "round away from 0", 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_INF }, 0, 0, V|F, .unit = "round" }, + { "down", "round towards -infty", 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_DOWN }, 0, 0, V|F, .unit = "round" }, + { "up", "round towards +infty", 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_UP }, 0, 0, V|F, .unit = "round" }, + { "near", "round to nearest", 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_NEAR_INF }, 0, 0, V|F, .unit = "round" }, + { "eof_action", "action performed for last frame", OFFSET(eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_ROUND }, 0, EOF_ACTION_NB-1, V|F, .unit = "eof_action" }, + { "round", "round similar to other frames", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ROUND }, 0, 0, V|F, .unit = "eof_action" }, + { "pass", "pass through last frame", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, 0, 0, V|F, .unit = "eof_action" }, { NULL } }; @@ -165,6 +168,7 @@ static av_cold void uninit(AVFilterContext *ctx) frame = shift_frame(ctx, s); av_frame_free(&frame); } + ff_ccfifo_uninit(&s->cc_fifo); av_log(ctx, AV_LOG_VERBOSE, "%d frames in, %d frames out; %d frames dropped, " "%d frames duplicated.\n", s->frames_in, s->frames_out, s->drop, s->dup); @@ -210,6 +214,12 @@ static int config_props(AVFilterLink* outlink) s->in_pts_off, s->out_pts_off, s->start_time); } + ret = ff_ccfifo_init(&s->cc_fifo, outlink->frame_rate, ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); + return ret; + } + av_log(ctx, AV_LOG_VERBOSE, "fps=%d/%d\n", outlink->frame_rate.num, outlink->frame_rate.den); return 0; @@ -242,6 +252,7 @@ static int read_frame(AVFilterContext *ctx, FPSContext *s, AVFilterLink *inlink, av_log(ctx, AV_LOG_DEBUG, "Read frame with in pts %"PRId64", out pts %"PRId64"\n", in_pts, frame->pts); + ff_ccfifo_extract(&s->cc_fifo, frame); s->frames[s->frames_count++] = frame; s->frames_in++; @@ -289,7 +300,7 @@ static int write_frame(AVFilterContext *ctx, FPSContext *s, AVFilterLink *outlin if (!frame) return AVERROR(ENOMEM); // Make sure Closed Captions will not be duplicated - av_frame_remove_side_data(s->frames[0], AV_FRAME_DATA_A53_CC); + ff_ccfifo_inject(&s->cc_fifo, frame); frame->pts = s->next_pts++; frame->duration = 1; @@ -366,13 +377,6 @@ static int activate(AVFilterContext *ctx) return FFERROR_NOT_READY; } -static const AVFilterPad avfilter_vf_fps_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad avfilter_vf_fps_outputs[] = { { .name = "default", @@ -390,6 +394,6 @@ const AVFilter ff_vf_fps = { .priv_class = &fps_class, .activate = activate, .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_vf_fps_inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(avfilter_vf_fps_outputs), }; diff --git a/libavfilter/vf_framepack.c b/libavfilter/vf_framepack.c index 6ea88df8b28..3b915ddddf9 100644 --- a/libavfilter/vf_framepack.c +++ b/libavfilter/vf_framepack.c @@ -34,7 +34,6 @@ #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -142,7 +141,7 @@ static int config_output(AVFilterLink *outlink) height *= 2; break; default: - av_log(ctx, AV_LOG_ERROR, "Unknown packing mode."); + av_log(ctx, AV_LOG_ERROR, "Unknown packing mode.\n"); return AVERROR_INVALIDDATA; } @@ -234,23 +233,20 @@ static void horizontal_frame_pack(AVFilterLink *outlink, } } else { for (i = 0; i < 2; i++) { + const AVFrame *const input_view = s->input_views[i]; const int psize = 1 + (s->depth > 8); - const uint8_t *src[4]; uint8_t *dst[4]; - int sub_w = psize * s->input_views[i]->width >> s->pix_desc->log2_chroma_w; + int sub_w = psize * input_view->width >> s->pix_desc->log2_chroma_w; - src[0] = s->input_views[i]->data[0]; - src[1] = s->input_views[i]->data[1]; - src[2] = s->input_views[i]->data[2]; - - dst[0] = out->data[0] + i * s->input_views[i]->width * psize; + dst[0] = out->data[0] + i * input_view->width * psize; dst[1] = out->data[1] + i * sub_w; dst[2] = out->data[2] + i * sub_w; - av_image_copy(dst, out->linesize, src, s->input_views[i]->linesize, - s->input_views[i]->format, - s->input_views[i]->width, - s->input_views[i]->height); + av_image_copy2(dst, out->linesize, + input_view->data, input_view->linesize, + input_view->format, + input_view->width, + input_view->height); } } } @@ -264,17 +260,13 @@ static void vertical_frame_pack(AVFilterLink *outlink, int i; for (i = 0; i < 2; i++) { - const uint8_t *src[4]; + const AVFrame *const input_view = s->input_views[i]; uint8_t *dst[4]; int linesizes[4]; - int sub_h = s->input_views[i]->height >> s->pix_desc->log2_chroma_h; - - src[0] = s->input_views[i]->data[0]; - src[1] = s->input_views[i]->data[1]; - src[2] = s->input_views[i]->data[2]; + int sub_h = input_view->height >> s->pix_desc->log2_chroma_h; dst[0] = out->data[0] + i * out->linesize[0] * - (interleaved + s->input_views[i]->height * (1 - interleaved)); + (interleaved + input_view->height * (1 - interleaved)); dst[1] = out->data[1] + i * out->linesize[1] * (interleaved + sub_h * (1 - interleaved)); dst[2] = out->data[2] + i * out->linesize[2] * @@ -287,10 +279,11 @@ static void vertical_frame_pack(AVFilterLink *outlink, linesizes[2] = out->linesize[2] + interleaved * out->linesize[2]; - av_image_copy(dst, linesizes, src, s->input_views[i]->linesize, - s->input_views[i]->format, - s->input_views[i]->width, - s->input_views[i]->height); + av_image_copy2(dst, linesizes, + input_view->data, input_view->linesize, + input_view->format, + input_view->width, + input_view->height); } } @@ -405,14 +398,12 @@ static int activate(AVFilterContext *ctx) FF_FILTER_FORWARD_STATUS(ctx->inputs[1], outlink); if (ff_outlink_frame_wanted(ctx->outputs[0]) && - !ff_outlink_get_status(ctx->inputs[0]) && !s->input_views[0]) { ff_inlink_request_frame(ctx->inputs[0]); return 0; } if (ff_outlink_frame_wanted(ctx->outputs[0]) && - !ff_outlink_get_status(ctx->inputs[1]) && !s->input_views[1]) { ff_inlink_request_frame(ctx->inputs[1]); return 0; diff --git a/libavfilter/vf_framerate.c b/libavfilter/vf_framerate.c index 49bf6cdfff2..0c31f77cc4b 100644 --- a/libavfilter/vf_framerate.c +++ b/libavfilter/vf_framerate.c @@ -53,9 +53,9 @@ static const AVOption framerate_options[] = { {"interp_end", "point to end linear interpolation", OFFSET(interp_end), AV_OPT_TYPE_INT, {.i64=240}, 0, 255, V|F }, {"scene", "scene change level", OFFSET(scene_score), AV_OPT_TYPE_DOUBLE, {.dbl=8.2}, 0, 100., V|F }, - {"flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=1}, 0, INT_MAX, V|F, "flags" }, - {"scene_change_detect", "enable scene change detection", 0, AV_OPT_TYPE_CONST, {.i64=FRAMERATE_FLAG_SCD}, INT_MIN, INT_MAX, V|F, "flags" }, - {"scd", "enable scene change detection", 0, AV_OPT_TYPE_CONST, {.i64=FRAMERATE_FLAG_SCD}, INT_MIN, INT_MAX, V|F, "flags" }, + {"flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=1}, 0, INT_MAX, V|F, .unit = "flags" }, + {"scene_change_detect", "enable scene change detection", 0, AV_OPT_TYPE_CONST, {.i64=FRAMERATE_FLAG_SCD}, INT_MIN, INT_MAX, V|F, .unit = "flags" }, + {"scd", "enable scene change detection", 0, AV_OPT_TYPE_CONST, {.i64=FRAMERATE_FLAG_SCD}, INT_MIN, INT_MAX, V|F, .unit = "flags" }, {NULL} }; @@ -76,7 +76,6 @@ static double get_scene_score(AVFilterContext *ctx, AVFrame *crnt, AVFrame *next ff_dlog(ctx, "get_scene_score() process\n"); s->sad(crnt->data[0], crnt->linesize[0], next->data[0], next->linesize[0], crnt->width, crnt->height, &sad); - emms_c(); mafd = (double)sad * 100.0 / (crnt->width * crnt->height) / (1 << s->bitdepth); diff = fabs(mafd - s->prev_mafd); ret = av_clipf(FFMIN(mafd, diff), 0, 100.0); @@ -318,7 +317,7 @@ static int activate(AVFilterContext *ctx) return ret; if (inpicref) { - if (inpicref->interlaced_frame) + if (inpicref->flags & AV_FRAME_FLAG_INTERLACED) av_log(ctx, AV_LOG_WARNING, "Interlaced frame found - the output will not be correct.\n"); if (inpicref->pts == AV_NOPTS_VALUE) { diff --git a/libavfilter/vf_freezedetect.c b/libavfilter/vf_freezedetect.c index 31a80a6dc09..18d392d9ae5 100644 --- a/libavfilter/vf_freezedetect.c +++ b/libavfilter/vf_freezedetect.c @@ -29,6 +29,7 @@ #include "avfilter.h" #include "filters.h" #include "scene_sad.h" +#include "video.h" typedef struct FreezeDetectContext { const AVClass *class; @@ -130,7 +131,6 @@ static int is_frozen(FreezeDetectContext *s, AVFrame *reference, AVFrame *frame) count += s->width[plane] * s->height[plane]; } } - emms_c(); mafd = (double)sad / count / (1ULL << s->bitdepth); return (mafd <= s->noise); } @@ -204,13 +204,6 @@ static const AVFilterPad freezedetect_inputs[] = { }, }; -static const AVFilterPad freezedetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_freezedetect = { .name = "freezedetect", .description = NULL_IF_CONFIG_SMALL("Detects frozen video input."), @@ -219,7 +212,7 @@ const AVFilter ff_vf_freezedetect = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(freezedetect_inputs), - FILTER_OUTPUTS(freezedetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .activate = activate, }; diff --git a/libavfilter/vf_frei0r.c b/libavfilter/vf_frei0r.c index 59873dc502e..7dccd5946f7 100644 --- a/libavfilter/vf_frei0r.c +++ b/libavfilter/vf_frei0r.c @@ -423,13 +423,6 @@ static const AVFilterPad avfilter_vf_frei0r_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_frei0r_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_frei0r = { .name = "frei0r", .description = NULL_IF_CONFIG_SMALL("Apply a frei0r effect."), @@ -438,7 +431,7 @@ const AVFilter ff_vf_frei0r = { .priv_size = sizeof(Frei0rContext), .priv_class = &frei0r_class, FILTER_INPUTS(avfilter_vf_frei0r_inputs), - FILTER_OUTPUTS(avfilter_vf_frei0r_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .process_command = process_command, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_fspp.c b/libavfilter/vf_fspp.c index 3e04fd01b94..8f443901177 100644 --- a/libavfilter/vf_fspp.c +++ b/libavfilter/vf_fspp.c @@ -35,6 +35,7 @@ * project, and ported by Arwa Arif for FFmpeg. */ +#include "libavutil/emms.h" #include "libavutil/imgutils.h" #include "libavutil/mem_internal.h" #include "libavutil/opt.h" @@ -42,6 +43,7 @@ #include "internal.h" #include "qp_table.h" #include "vf_fspp.h" +#include "video.h" #define OFFSET(x) offsetof(FSPPContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM @@ -651,20 +653,13 @@ static const AVFilterPad fspp_inputs[] = { }, }; -static const AVFilterPad fspp_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_fspp = { .name = "fspp", .description = NULL_IF_CONFIG_SMALL("Apply Fast Simple Post-processing filter."), .priv_size = sizeof(FSPPContext), .uninit = uninit, FILTER_INPUTS(fspp_inputs), - FILTER_OUTPUTS(fspp_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &fspp_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/vf_fsync.c b/libavfilter/vf_fsync.c new file mode 100644 index 00000000000..a18f1a009b5 --- /dev/null +++ b/libavfilter/vf_fsync.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2023 Thilo Borgmann + * + * 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 + * Filter for syncing video frames from external source + * + * @author Thilo Borgmann + */ + +#include "libavutil/avstring.h" +#include "libavutil/error.h" +#include "libavutil/opt.h" +#include "libavformat/avio.h" +#include "video.h" +#include "filters.h" + +#define BUF_SIZE 256 + +typedef struct FsyncContext { + const AVClass *class; + AVIOContext *avio_ctx; // reading the map file + AVFrame *last_frame; // buffering the last frame for duplicating eventually + char *filename; // user-specified map file + char *buf; // line buffer for the map file + char *cur; // current position in the line buffer + char *end; // end pointer of the line buffer + int64_t ptsi; // input pts to map to [0-N] output pts + int64_t pts; // output pts + int tb_num; // output timebase num + int tb_den; // output timebase den +} FsyncContext; + +#define OFFSET(x) offsetof(FsyncContext, x) + +static const AVOption fsync_options[] = { + { "file", "set the file name to use for frame sync", OFFSET(filename), AV_OPT_TYPE_STRING, { .str = "" }, .flags= AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM }, + { "f", "set the file name to use for frame sync", OFFSET(filename), AV_OPT_TYPE_STRING, { .str = "" }, .flags= AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM }, + { NULL } +}; + +/** + * Fills the buffer from cur to end, add \0 at EOF + */ +static int buf_fill(FsyncContext *ctx) +{ + int ret; + int num = ctx->end - ctx->cur; + + ret = avio_read(ctx->avio_ctx, ctx->cur, num); + if (ret < 0) + return ret; + if (ret < num) { + *(ctx->cur + ret) = '\0'; + } + + return ret; +} + +/** + * Copies cur to end to the beginning and fills the rest + */ +static int buf_reload(FsyncContext *ctx) +{ + int i, ret; + int num = ctx->end - ctx->cur; + + for (i = 0; i < num; i++) { + ctx->buf[i] = *ctx->cur++; + } + + ctx->cur = ctx->buf + i; + ret = buf_fill(ctx); + if (ret < 0) + return ret; + ctx->cur = ctx->buf; + + return ret; +} + +/** + * Skip from cur over eol + */ +static void buf_skip_eol(FsyncContext *ctx) +{ + char *i; + for (i = ctx->cur; i < ctx->end; i++) { + if (*i != '\n')// && *i != '\r') + break; + } + ctx->cur = i; +} + +/** + * Get number of bytes from cur until eol + * + * @return >= 0 in case of success, + * -1 in case there is no line ending before end of buffer + */ +static int buf_get_line_count(FsyncContext *ctx) +{ + int ret = 0; + char *i; + for (i = ctx->cur; i < ctx->end; i++, ret++) { + if (*i == '\0' || *i == '\n') + return ret; + } + + return -1; +} + +/** + * Get number of bytes from cur to '\0' + */ +static int buf_get_zero(FsyncContext *ctx) +{ + return av_strnlen(ctx->cur, ctx->end - ctx->cur); +} + +static int activate(AVFilterContext *ctx) +{ + FsyncContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + AVFilterLink *outlink = ctx->outputs[0]; + + int ret, line_count; + AVFrame *frame; + + FF_FILTER_FORWARD_STATUS_BACK(outlink, inlink); + + buf_skip_eol(s); + line_count = buf_get_line_count(s); + if (line_count < 0) { + line_count = buf_reload(s); + if (line_count < 0) + return line_count; + line_count = buf_get_line_count(s); + if (line_count < 0) + return line_count; + } + + if (avio_feof(s->avio_ctx) && buf_get_zero(s) < 3) { + av_log(ctx, AV_LOG_DEBUG, "End of file. To zero = %i\n", buf_get_zero(s)); + goto end; + } + + if (s->last_frame) { + ret = av_sscanf(s->cur, "%"PRId64" %"PRId64" %d/%d", &s->ptsi, &s->pts, &s->tb_num, &s->tb_den); + if (ret != 4) { + av_log(ctx, AV_LOG_ERROR, "Unexpected format found (%i / 4).\n", ret); + ff_outlink_set_status(outlink, AVERROR_INVALIDDATA, AV_NOPTS_VALUE); + return AVERROR_INVALIDDATA; + } + + av_log(ctx, AV_LOG_DEBUG, "frame %"PRId64" ", s->last_frame->pts); + + if (s->last_frame->pts >= s->ptsi) { + av_log(ctx, AV_LOG_DEBUG, ">= %"PRId64": DUP LAST with pts = %"PRId64"\n", s->ptsi, s->pts); + + // clone frame + frame = av_frame_clone(s->last_frame); + if (!frame) { + ff_outlink_set_status(outlink, AVERROR(ENOMEM), AV_NOPTS_VALUE); + return AVERROR(ENOMEM); + } + + // set output pts and timebase + frame->pts = s->pts; + frame->time_base = av_make_q((int)s->tb_num, (int)s->tb_den); + + // advance cur to eol, skip over eol in the next call + s->cur += line_count; + + // call again + if (ff_inoutlink_check_flow(inlink, outlink)) + ff_filter_set_ready(ctx, 100); + + // filter frame + return ff_filter_frame(outlink, frame); + } else if (s->last_frame->pts < s->ptsi) { + av_log(ctx, AV_LOG_DEBUG, "< %"PRId64": DROP\n", s->ptsi); + av_frame_free(&s->last_frame); + + // call again + if (ff_inoutlink_check_flow(inlink, outlink)) + ff_filter_set_ready(ctx, 100); + + return 0; + } + } + +end: + if (s->last_frame) + av_frame_free(&s->last_frame); + + ret = ff_inlink_consume_frame(inlink, &s->last_frame); + if (ret < 0) + return ret; + + FF_FILTER_FORWARD_STATUS(inlink, outlink); + FF_FILTER_FORWARD_WANTED(outlink, inlink); + + return FFERROR_NOT_READY; +} + +static int fsync_config_props(AVFilterLink* outlink) +{ + AVFilterContext *ctx = outlink->src; + FsyncContext *s = ctx->priv; + int ret; + + // read first line to get output timebase + ret = av_sscanf(s->cur, "%"PRId64" %"PRId64" %d/%d", &s->ptsi, &s->pts, &s->tb_num, &s->tb_den); + if (ret != 4) { + av_log(ctx, AV_LOG_ERROR, "Unexpected format found (%i of 4).\n", ret); + ff_outlink_set_status(outlink, AVERROR_INVALIDDATA, AV_NOPTS_VALUE); + return AVERROR_INVALIDDATA; + } + + outlink->frame_rate = av_make_q(1, 0); // unknown or dynamic + outlink->time_base = av_make_q(s->tb_num, s->tb_den); + + return 0; +} + +static av_cold int fsync_init(AVFilterContext *ctx) +{ + FsyncContext *s = ctx->priv; + int ret; + + av_log(ctx, AV_LOG_DEBUG, "filename: %s\n", s->filename); + + s->buf = av_malloc(BUF_SIZE + 1); + if (!s->buf) + return AVERROR(ENOMEM); + + ret = avio_open(&s->avio_ctx, s->filename, AVIO_FLAG_READ); + if (ret < 0) + return ret; + + s->cur = s->buf; + s->end = s->buf + BUF_SIZE; + s->buf[BUF_SIZE] = '\0'; + + ret = buf_fill(s); + if (ret < 0) + return ret; + + + return 0; +} + +static av_cold void fsync_uninit(AVFilterContext *ctx) +{ + FsyncContext *s = ctx->priv; + + avio_closep(&s->avio_ctx); + av_freep(&s->buf); + av_frame_free(&s->last_frame); +} + +AVFILTER_DEFINE_CLASS(fsync); + +static const AVFilterPad fsync_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = fsync_config_props, + }, +}; + +const AVFilter ff_vf_fsync = { + .name = "fsync", + .description = NULL_IF_CONFIG_SMALL("Synchronize video frames from external source."), + .init = fsync_init, + .uninit = fsync_uninit, + .priv_size = sizeof(FsyncContext), + .priv_class = &fsync_class, + .activate = activate, + .formats_state = FF_FILTER_FORMATS_PASSTHROUGH, + FILTER_INPUTS(ff_video_default_filterpad), + FILTER_OUTPUTS(fsync_outputs), + .flags = AVFILTER_FLAG_METADATA_ONLY, +}; diff --git a/libavfilter/vf_gblur.c b/libavfilter/vf_gblur.c index ca1dcb3dab1..6ce2c84736c 100644 --- a/libavfilter/vf_gblur.c +++ b/libavfilter/vf_gblur.c @@ -31,7 +31,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "gblur.h" #include "internal.h" #include "vf_gblur_init.h" @@ -74,7 +73,6 @@ static int filter_horizontally(AVFilterContext *ctx, void *arg, int jobnr, int n s->horiz_slice(buffer + width * slice_start, width, slice_end - slice_start, steps, nu, boundaryscale, localbuf); - emms_c(); return 0; } @@ -315,13 +313,6 @@ static const AVFilterPad gblur_inputs[] = { }, }; -static const AVFilterPad gblur_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_gblur = { .name = "gblur", .description = NULL_IF_CONFIG_SMALL("Apply Gaussian Blur filter."), @@ -329,7 +320,7 @@ const AVFilter ff_vf_gblur = { .priv_class = &gblur_class, .uninit = uninit, FILTER_INPUTS(gblur_inputs), - FILTER_OUTPUTS(gblur_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_gblur_vulkan.c b/libavfilter/vf_gblur_vulkan.c index d61f3c778c0..bac05b87b89 100644 --- a/libavfilter/vf_gblur_vulkan.c +++ b/libavfilter/vf_gblur_vulkan.c @@ -1,5 +1,7 @@ /* * copyright (c) 2021-2022 Wu Jianhua + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -20,33 +22,32 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" +#include "video.h" #define CGS 32 #define GBLUR_MAX_KERNEL_SIZE 127 typedef struct GBlurVulkanContext { FFVulkanContext vkctx; - FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl_hor; - FFVulkanPipeline *pl_ver; - FFVkBuffer params_buf_hor; - FFVkBuffer params_buf_ver; - - VkDescriptorImageInfo input_images[3]; - VkDescriptorImageInfo tmp_images[3]; - VkDescriptorImageInfo output_images[3]; - VkDescriptorBufferInfo params_desc_hor; - VkDescriptorBufferInfo params_desc_ver; int initialized; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + VkSampler sampler; + FFVulkanPipeline pl_hor; + FFVkSPIRVShader shd_hor; + FFVkBuffer params_hor; + FFVulkanPipeline pl_ver; + FFVkSPIRVShader shd_ver; + FFVkBuffer params_ver; + int size; int sizeV; int planes; float sigma; float sigmaV; - AVFrame *tmpframe; } GBlurVulkanContext; static const char gblur_func[] = { @@ -118,16 +119,17 @@ static av_cold void init_gaussian_params(GBlurVulkanContext *s) s->sizeV = s->size; else init_kernel_size(s, &s->sizeV); - - s->tmpframe = NULL; } -static int init_gblur_pipeline(GBlurVulkanContext *s, FFVulkanPipeline *pl, FFVkSPIRVShader *shd, - FFVkBuffer *params_buf, VkDescriptorBufferInfo *params_desc, - int ksize, float sigma) +static int init_gblur_pipeline(GBlurVulkanContext *s, FFVulkanPipeline *pl, + FFVkSPIRVShader *shd, FFVkBuffer *params_buf, + int ksize, float sigma, FFVkSPIRVCompiler *spv) { int err = 0; uint8_t *kernel_mapped; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); @@ -137,7 +139,6 @@ static int init_gblur_pipeline(GBlurVulkanContext *s, FFVulkanPipeline *pl, FFVk .mem_quali = "readonly", .mem_layout = "std430", .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = NULL, .buf_content = NULL, }; @@ -145,10 +146,9 @@ static int init_gblur_pipeline(GBlurVulkanContext *s, FFVulkanPipeline *pl, FFVk if (!kernel_def) return AVERROR(ENOMEM); - buf_desc.updater = params_desc; buf_desc.buf_content = kernel_def; - RET(ff_vk_add_descriptor_set(&s->vkctx, pl, shd, &buf_desc, 1, 0)); + RET(ff_vk_pipeline_descriptor_set_add(&s->vkctx, pl, shd, &buf_desc, 1, 1, 0)); GLSLD( gblur_func ); GLSLC(0, void main() ); @@ -157,38 +157,43 @@ static int init_gblur_pipeline(GBlurVulkanContext *s, FFVulkanPipeline *pl, FFVk GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); for (int i = 0; i < planes; i++) { GLSLC(0, ); - GLSLF(1, size = imageSize(output_images[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); + GLSLF(1, size = imageSize(output_images[%i]); ,i); + GLSLC(1, if (!IS_WITHIN(pos, size)) ); + GLSLC(2, return; ); if (s->planes & (1 << i)) { - GLSLF(2, gblur(pos, %i); ,i); + GLSLF(1, gblur(pos, %i); ,i); } else { - GLSLF(2, vec4 res = texture(input_images[%i], pos); ,i); - GLSLF(2, imageStore(output_images[%i], pos, res); ,i); + GLSLF(1, vec4 res = texture(input_images[%i], pos); ,i); + GLSLF(1, imageStore(output_images[%i], pos, res); ,i); } - GLSLC(1, } ); } GLSLC(0, } ); - RET(ff_vk_compile_shader(&s->vkctx, shd, "main")); + RET(spv->compile_shader(spv, s, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(&s->vkctx, shd, spv_data, spv_len, "main")); - RET(ff_vk_init_pipeline_layout(&s->vkctx, pl)); - RET(ff_vk_init_compute_pipeline(&s->vkctx, pl)); + RET(ff_vk_init_compute_pipeline(&s->vkctx, pl, shd)); + RET(ff_vk_exec_pipeline_register(&s->vkctx, &s->e, pl)); - RET(ff_vk_create_buf(&s->vkctx, params_buf, sizeof(float) * ksize, - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)); - RET(ff_vk_map_buffers(&s->vkctx, params_buf, &kernel_mapped, 1, 0)); + RET(ff_vk_create_buf(&s->vkctx, params_buf, sizeof(float) * ksize, NULL, NULL, + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)); + RET(ff_vk_map_buffer(&s->vkctx, params_buf, &kernel_mapped, 0)); init_gaussian_kernel((float *)kernel_mapped, sigma, ksize); - RET(ff_vk_unmap_buffers(&s->vkctx, params_buf, 1, 1)); - - params_desc->buffer = params_buf->buf; - params_desc->range = VK_WHOLE_SIZE; + RET(ff_vk_unmap_buffer(&s->vkctx, params_buf, 1)); - ff_vk_update_descriptor_set(&s->vkctx, pl, 1); + RET(ff_vk_set_descriptor_buffer(&s->vkctx, pl, NULL, 1, 0, 0, + params_buf->address, params_buf->size, + VK_FORMAT_UNDEFINED)); fail: av_free(kernel_def); + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); return err; } @@ -196,16 +201,35 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { int err = 0; GBlurVulkanContext *s = ctx->priv; - FFVkSPIRVShader *shd; + FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - FFVulkanDescriptorSetBinding image_descs[] = { + FFVkSPIRVShader *shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_LINEAR)); + RET(ff_vk_shader_init(&s->pl_hor, &s->shd_hor, "gblur_hor_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + RET(ff_vk_shader_init(&s->pl_ver, &s->shd_ver, "gblur_ver_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + desc = (FFVulkanDescriptorSetBinding []) { { .name = "input_images", .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .dimensions = 2, .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), }, { .name = "output_images", @@ -218,215 +242,64 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) }, }; - image_descs[0].sampler = ff_vk_init_sampler(&s->vkctx, 1, VK_FILTER_LINEAR); - if (!image_descs[0].sampler) - return AVERROR_EXTERNAL; - init_gaussian_params(s); - ff_vk_qf_init(&s->vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - { - /* Create shader for the horizontal pass */ - image_descs[0].updater = s->input_images; - image_descs[1].updater = s->tmp_images; - - s->pl_hor = ff_vk_create_pipeline(&s->vkctx, &s->qf); - if (!s->pl_hor) { - err = AVERROR(ENOMEM); - goto fail; - } - - shd = ff_vk_init_shader(s->pl_hor, "gblur_compute_hor", image_descs[0].stages); - if (!shd) { - err = AVERROR(ENOMEM); - goto fail; - } + shd = &s->shd_hor; + ff_vk_shader_set_compute_sizes(shd, 32, 1, 1); - ff_vk_set_compute_shader_sizes(shd, (int [3]){ CGS, 1, 1 }); - RET(ff_vk_add_descriptor_set(&s->vkctx, s->pl_hor, shd, image_descs, FF_ARRAY_ELEMS(image_descs), 0)); + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl_hor, shd, desc, 2, 0, 0)); GLSLC(0, #define OFFSET (vec2(i, 0.0))); - RET(init_gblur_pipeline(s, s->pl_hor, shd, &s->params_buf_hor, &s->params_desc_hor, - s->size, s->sigma)); + RET(init_gblur_pipeline(s, &s->pl_hor, shd, &s->params_hor, s->size, s->sigma, spv)); } { - /* Create shader for the vertical pass */ - image_descs[0].updater = s->tmp_images; - image_descs[1].updater = s->output_images; - - s->pl_ver = ff_vk_create_pipeline(&s->vkctx, &s->qf); - if (!s->pl_ver) { - err = AVERROR(ENOMEM); - goto fail; - } + shd = &s->shd_ver; + ff_vk_shader_set_compute_sizes(shd, 1, 32, 1); - shd = ff_vk_init_shader(s->pl_ver, "gblur_compute_ver", image_descs[0].stages); - if (!shd) { - err = AVERROR(ENOMEM); - goto fail; - } - - ff_vk_set_compute_shader_sizes(shd, (int [3]){ 1, CGS, 1 }); - RET(ff_vk_add_descriptor_set(&s->vkctx, s->pl_ver, shd, image_descs, FF_ARRAY_ELEMS(image_descs), 0)); + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl_ver, shd, desc, 2, 0, 0)); GLSLC(0, #define OFFSET (vec2(0.0, i))); - RET(init_gblur_pipeline(s, s->pl_ver, shd, &s->params_buf_ver, &s->params_desc_ver, - s->sizeV, s->sigmaV)); + RET(init_gblur_pipeline(s, &s->pl_ver, shd, &s->params_ver, s->sizeV, s->sigmaV, spv)); } - RET(ff_vk_create_exec_ctx(&s->vkctx, &s->exec, &s->qf)); - s->initialized = 1; fail: + if (spv) + spv->uninit(&spv); + return err; } static av_cold void gblur_vulkan_uninit(AVFilterContext *avctx) { GBlurVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; - av_frame_free(&s->tmpframe); + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl_hor); + ff_vk_pipeline_free(vkctx, &s->pl_ver); + ff_vk_shader_free(vkctx, &s->shd_hor); + ff_vk_shader_free(vkctx, &s->shd_ver); + ff_vk_free_buf(vkctx, &s->params_hor); + ff_vk_free_buf(vkctx, &s->params_ver); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); - ff_vk_free_buf(&s->vkctx, &s->params_buf_hor); - ff_vk_free_buf(&s->vkctx, &s->params_buf_ver); ff_vk_uninit(&s->vkctx); s->initialized = 0; } -static int process_frames(AVFilterContext *avctx, AVFrame *outframe, AVFrame *inframe) -{ - int err; - VkCommandBuffer cmd_buf; - GBlurVulkanContext *s = avctx->priv; - FFVulkanFunctions *vk = &s->vkctx.vkfn; - - const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - AVVkFrame *in = (AVVkFrame *)inframe->data[0]; - AVVkFrame *out = (AVVkFrame *)outframe->data[0]; - AVVkFrame *tmp = (AVVkFrame *)s->tmpframe->data[0]; - - const VkFormat *input_formats = av_vkfmt_from_pixfmt(s->vkctx.input_format); - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - ff_vk_start_exec_recording(&s->vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(&s->vkctx, s->exec, &s->input_images[i].imageView, - in->img[i], - input_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(&s->vkctx, s->exec, &s->tmp_images[i].imageView, - tmp->img[i], - output_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(&s->vkctx, s->exec, &s->output_images[i].imageView, - out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->tmp_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } - - ff_vk_update_descriptor_set(&s->vkctx, s->pl_hor, 0); - ff_vk_update_descriptor_set(&s->vkctx, s->pl_ver, 0); - - for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier barriers[] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = in->layout[i], - .newLayout = s->input_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = in->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT, - .oldLayout = tmp->layout[i], - .newLayout = s->tmp_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = tmp->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(barriers), barriers); - - in->layout[i] = barriers[0].newLayout; - in->access[i] = barriers[0].dstAccessMask; - - tmp->layout[i] = barriers[1].newLayout; - tmp->access[i] = barriers[1].dstAccessMask; - - out->layout[i] = barriers[2].newLayout; - out->access[i] = barriers[2].dstAccessMask; - } - - ff_vk_bind_pipeline_exec(&s->vkctx, s->exec, s->pl_hor); - - vk->CmdDispatch(cmd_buf, FFALIGN(s->vkctx.output_width, CGS)/CGS, - s->vkctx.output_height, 1); - - ff_vk_bind_pipeline_exec(&s->vkctx, s->exec, s->pl_ver); - - vk->CmdDispatch(cmd_buf,s->vkctx.output_width, - FFALIGN(s->vkctx.output_height, CGS)/CGS, 1); - - ff_vk_add_exec_dep(&s->vkctx, s->exec, inframe, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(&s->vkctx, s->exec, outframe, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(&s->vkctx, s->exec); - if (err) - return err; - - ff_vk_qf_rotate(&s->qf); - - return 0; - -fail: - ff_vk_discard_exec_deps(s->exec); - return err; -} - static int gblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) { int err; - AVFrame *out = NULL; + AVFrame *tmp = NULL, *out = NULL; AVFilterContext *ctx = link->dst; GBlurVulkanContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; @@ -437,28 +310,32 @@ static int gblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) goto fail; } - if (!s->initialized) { - RET(init_filter(ctx, in)); - s->tmpframe = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!s->tmpframe) { - err = AVERROR(ENOMEM); - goto fail; - } + tmp = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!tmp) { + err = AVERROR(ENOMEM); + goto fail; } - RET(process_frames(ctx, out, in)); + if (!s->initialized) + RET(init_filter(ctx, in)); - RET(av_frame_copy_props(out, in)); + RET(ff_vk_filter_process_2pass(&s->vkctx, &s->e, + (FFVulkanPipeline *[2]){ &s->pl_hor, &s->pl_ver }, + out, tmp, in, s->sampler, NULL, 0)); + + err = av_frame_copy_props(out, in); + if (err < 0) + goto fail; av_frame_free(&in); + av_frame_free(&tmp); return ff_filter_frame(outlink, out); fail: av_frame_free(&in); + av_frame_free(&tmp); av_frame_free(&out); - av_frame_free(&s->tmpframe); - return err; } @@ -503,4 +380,5 @@ const AVFilter ff_vf_gblur_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &gblur_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_geq.c b/libavfilter/vf_geq.c index 2b539802787..ff243ad843f 100644 --- a/libavfilter/vf_geq.c +++ b/libavfilter/vf_geq.c @@ -31,7 +31,9 @@ #include "libavutil/eval.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" +#include "formats.h" #include "internal.h" +#include "video.h" #define MAX_NB_THREADS 32 #define NB_PLANES 4 @@ -84,12 +86,12 @@ static const AVOption geq_options[] = { { "g", "set green expression", OFFSET(expr_str[G]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, { "blue_expr", "set blue expression", OFFSET(expr_str[B]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, { "b", "set blue expression", OFFSET(expr_str[B]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, - { "interpolation","set interpolation method", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERP_BILINEAR}, 0, NB_INTERP-1, FLAGS, "interp" }, - { "i", "set interpolation method", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERP_BILINEAR}, 0, NB_INTERP-1, FLAGS, "interp" }, - { "nearest", "nearest interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_NEAREST}, 0, 0, FLAGS, "interp" }, - { "n", "nearest interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_NEAREST}, 0, 0, FLAGS, "interp" }, - { "bilinear", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_BILINEAR}, 0, 0, FLAGS, "interp" }, - { "b", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_BILINEAR}, 0, 0, FLAGS, "interp" }, + { "interpolation","set interpolation method", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERP_BILINEAR}, 0, NB_INTERP-1, FLAGS, .unit = "interp" }, + { "i", "set interpolation method", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERP_BILINEAR}, 0, NB_INTERP-1, FLAGS, .unit = "interp" }, + { "nearest", "nearest interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_NEAREST}, 0, 0, FLAGS, .unit = "interp" }, + { "n", "nearest interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_NEAREST}, 0, 0, FLAGS, .unit = "interp" }, + { "bilinear", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_BILINEAR}, 0, 0, FLAGS, .unit = "interp" }, + { "b", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERP_BILINEAR}, 0, 0, FLAGS, .unit = "interp" }, {NULL}, }; @@ -515,13 +517,6 @@ static const AVFilterPad geq_inputs[] = { }, }; -static const AVFilterPad geq_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_geq = { .name = "geq", .description = NULL_IF_CONFIG_SMALL("Apply generic equation to each pixel."), @@ -529,7 +524,7 @@ const AVFilter ff_vf_geq = { .init = geq_init, .uninit = geq_uninit, FILTER_INPUTS(geq_inputs), - FILTER_OUTPUTS(geq_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(geq_query_formats), .priv_class = &geq_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_gradfun.c b/libavfilter/vf_gradfun.c index 71a5f9c787d..e8d9cae8286 100644 --- a/libavfilter/vf_gradfun.c +++ b/libavfilter/vf_gradfun.c @@ -32,13 +32,13 @@ * Dither it back to 8bit. */ +#include "libavutil/emms.h" #include "libavutil/imgutils.h" #include "libavutil/common.h" #include "libavutil/mem_internal.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "gradfun.h" #include "internal.h" #include "video.h" @@ -92,7 +92,7 @@ static void filter(GradFunContext *ctx, uint8_t *dst, const uint8_t *src, int wi for (y = 0; y < r; y++) ctx->blur_line(dc, buf + y * bstride, buf + (y - 1) * bstride, src + 2 * y * src_linesize, src_linesize, width / 2); for (;;) { - if (y < height - r) { + if (y + 1 < height - r) { int mod = ((y + r) / 2) % r; uint16_t *buf0 = buf + mod * bstride; uint16_t *buf1 = buf + (mod ? mod - 1 : r - 1) * bstride; @@ -236,13 +236,6 @@ static const AVFilterPad avfilter_vf_gradfun_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_gradfun_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_gradfun = { .name = "gradfun", .description = NULL_IF_CONFIG_SMALL("Debands video quickly using gradients."), @@ -251,7 +244,7 @@ const AVFilter ff_vf_gradfun = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_gradfun_inputs), - FILTER_OUTPUTS(avfilter_vf_gradfun_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_grayworld.c b/libavfilter/vf_grayworld.c index f20412cb104..e9c959416e1 100644 --- a/libavfilter/vf_grayworld.c +++ b/libavfilter/vf_grayworld.c @@ -27,10 +27,8 @@ #include "libavutil/imgutils.h" #include "libavutil/opt.h" -#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -308,20 +306,13 @@ static const AVFilterPad grayworld_inputs[] = { } }; -static const AVFilterPad grayworld_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - } -}; - const AVFilter ff_vf_grayworld = { .name = "grayworld", .description = NULL_IF_CONFIG_SMALL("Adjust white balance using LAB gray world algorithm"), .priv_size = sizeof(GrayWorldContext), .priv_class = &grayworld_class, FILTER_INPUTS(grayworld_inputs), - FILTER_OUTPUTS(grayworld_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .uninit = uninit, diff --git a/libavfilter/vf_guided.c b/libavfilter/vf_guided.c index 58a1ff5a264..e5c534a6ae1 100644 --- a/libavfilter/vf_guided.c +++ b/libavfilter/vf_guided.c @@ -23,7 +23,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "framesync.h" #include "internal.h" #include "video.h" @@ -83,13 +82,13 @@ typedef struct GuidedContext { static const AVOption guided_options[] = { { "radius", "set the box radius", OFFSET(radius), AV_OPT_TYPE_INT, {.i64 = 3 }, 1, 20, TFLAGS }, { "eps", "set the regularization parameter (with square)", OFFSET(eps), AV_OPT_TYPE_FLOAT, {.dbl = 0.01 }, 0.0, 1, TFLAGS }, - { "mode", "set filtering mode (0: basic mode; 1: fast mode)", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = BASIC}, BASIC, NB_MODES - 1, TFLAGS, "mode" }, - { "basic", "basic guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = BASIC}, 0, 0, TFLAGS, "mode" }, - { "fast", "fast guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = FAST }, 0, 0, TFLAGS, "mode" }, + { "mode", "set filtering mode (0: basic mode; 1: fast mode)", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = BASIC}, BASIC, NB_MODES - 1, TFLAGS, .unit = "mode" }, + { "basic", "basic guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = BASIC}, 0, 0, TFLAGS, .unit = "mode" }, + { "fast", "fast guided filter", 0, AV_OPT_TYPE_CONST, {.i64 = FAST }, 0, 0, TFLAGS, .unit = "mode" }, { "sub", "subsampling ratio for fast mode", OFFSET(sub), AV_OPT_TYPE_INT, {.i64 = 4 }, 2, 64, TFLAGS }, - { "guidance", "set guidance mode (0: off mode; 1: on mode)", OFFSET(guidance), AV_OPT_TYPE_INT, {.i64 = OFF }, OFF, NB_GUIDANCE_MODES - 1, FLAGS, "guidance" }, - { "off", "only one input is enabled", 0, AV_OPT_TYPE_CONST, {.i64 = OFF }, 0, 0, FLAGS, "guidance" }, - { "on", "two inputs are required", 0, AV_OPT_TYPE_CONST, {.i64 = ON }, 0, 0, FLAGS, "guidance" }, + { "guidance", "set guidance mode (0: off mode; 1: on mode)", OFFSET(guidance), AV_OPT_TYPE_INT, {.i64 = OFF }, OFF, NB_GUIDANCE_MODES - 1, FLAGS, .unit = "guidance" }, + { "off", "only one input is enabled", 0, AV_OPT_TYPE_CONST, {.i64 = OFF }, 0, 0, FLAGS, .unit = "guidance" }, + { "on", "two inputs are required", 0, AV_OPT_TYPE_CONST, {.i64 = ON }, 0, 0, FLAGS, .unit = "guidance" }, { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64 = 1 }, 0, 0xF, TFLAGS }, { NULL } }; diff --git a/libavfilter/vf_hflip.c b/libavfilter/vf_hflip.c index 8517b87889c..09f4e08ea33 100644 --- a/libavfilter/vf_hflip.c +++ b/libavfilter/vf_hflip.c @@ -151,20 +151,13 @@ static const AVFilterPad avfilter_vf_hflip_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_hflip_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_hflip = { .name = "hflip", .description = NULL_IF_CONFIG_SMALL("Horizontally flip the input video."), .priv_size = sizeof(FlipContext), .priv_class = &hflip_class, FILTER_INPUTS(avfilter_vf_hflip_inputs), - FILTER_OUTPUTS(avfilter_vf_hflip_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SLICE_THREADS | AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_histeq.c b/libavfilter/vf_histeq.c index 3fee4c9480c..24c95a0249f 100644 --- a/libavfilter/vf_histeq.c +++ b/libavfilter/vf_histeq.c @@ -34,7 +34,6 @@ #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -68,12 +67,12 @@ typedef struct HisteqContext { #define OFFSET(x) offsetof(HisteqContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM -#define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, unit } +#define CONST(name, help, val, u) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, .unit = u } static const AVOption histeq_options[] = { { "strength", "set the strength", OFFSET(strength), AV_OPT_TYPE_FLOAT, {.dbl=0.2}, 0, 1, FLAGS }, { "intensity", "set the intensity", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0.21}, 0, 1, FLAGS }, - { "antibanding", "set the antibanding level", OFFSET(antibanding), AV_OPT_TYPE_INT, {.i64=HISTEQ_ANTIBANDING_NONE}, 0, HISTEQ_ANTIBANDING_NB-1, FLAGS, "antibanding" }, + { "antibanding", "set the antibanding level", OFFSET(antibanding), AV_OPT_TYPE_INT, {.i64=HISTEQ_ANTIBANDING_NONE}, 0, HISTEQ_ANTIBANDING_NB-1, FLAGS, .unit = "antibanding" }, CONST("none", "apply no antibanding", HISTEQ_ANTIBANDING_NONE, "antibanding"), CONST("weak", "apply weak antibanding", HISTEQ_ANTIBANDING_WEAK, "antibanding"), CONST("strong", "apply strong antibanding", HISTEQ_ANTIBANDING_STRONG, "antibanding"), @@ -254,20 +253,13 @@ static const AVFilterPad histeq_inputs[] = { }, }; -static const AVFilterPad histeq_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_histeq = { .name = "histeq", .description = NULL_IF_CONFIG_SMALL("Apply global color histogram equalization."), .priv_size = sizeof(HisteqContext), .init = init, FILTER_INPUTS(histeq_inputs), - FILTER_OUTPUTS(histeq_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &histeq_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_histogram.c b/libavfilter/vf_histogram.c index 83477692cd5..7da6d5d7279 100644 --- a/libavfilter/vf_histogram.c +++ b/libavfilter/vf_histogram.c @@ -67,15 +67,15 @@ typedef struct HistogramContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM #define COMMON_OPTIONS \ - { "display_mode", "set display mode", OFFSET(display_mode), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS, "display_mode"}, \ - { "d", "set display mode", OFFSET(display_mode), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS, "display_mode"}, \ - { "overlay", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "display_mode" }, \ - { "parade", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "display_mode" }, \ - { "stack", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "display_mode" }, \ - { "levels_mode", "set levels mode", OFFSET(levels_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "levels_mode"}, \ - { "m", "set levels mode", OFFSET(levels_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "levels_mode"}, \ - { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "levels_mode" }, \ - { "logarithmic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "levels_mode" }, \ + { "display_mode", "set display mode", OFFSET(display_mode), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS, .unit = "display_mode"}, \ + { "d", "set display mode", OFFSET(display_mode), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS, .unit = "display_mode"}, \ + { "overlay", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "display_mode" }, \ + { "parade", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "display_mode" }, \ + { "stack", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "display_mode" }, \ + { "levels_mode", "set levels mode", OFFSET(levels_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "levels_mode"}, \ + { "m", "set levels mode", OFFSET(levels_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "levels_mode"}, \ + { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "levels_mode" }, \ + { "logarithmic", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "levels_mode" }, \ { "components", "set color components to display", OFFSET(components), AV_OPT_TYPE_INT, {.i64=7}, 1, 15, FLAGS}, \ { "c", "set color components to display", OFFSET(components), AV_OPT_TYPE_INT, {.i64=7}, 1, 15, FLAGS}, @@ -87,18 +87,18 @@ static const AVOption histogram_options[] = { { "f", "set foreground opacity", OFFSET(fgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.7}, 0, 1, FLAGS}, { "bgopacity", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS}, { "b", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.5}, 0, 1, FLAGS}, - { "colors_mode", "set colors mode", OFFSET(colors_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 9, FLAGS, "colors_mode"}, - { "l", "set colors mode", OFFSET(colors_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 9, FLAGS, "colors_mode"}, - { "whiteonblack", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "colors_mode" }, - { "blackonwhite", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "colors_mode" }, - { "whiteongray", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "colors_mode" }, - { "blackongray", NULL, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "colors_mode" }, - { "coloronblack", NULL, 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, "colors_mode" }, - { "coloronwhite", NULL, 0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, FLAGS, "colors_mode" }, - { "colorongray" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, FLAGS, "colors_mode" }, - { "blackoncolor", NULL, 0, AV_OPT_TYPE_CONST, {.i64=7}, 0, 0, FLAGS, "colors_mode" }, - { "whiteoncolor", NULL, 0, AV_OPT_TYPE_CONST, {.i64=8}, 0, 0, FLAGS, "colors_mode" }, - { "grayoncolor" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=9}, 0, 0, FLAGS, "colors_mode" }, + { "colors_mode", "set colors mode", OFFSET(colors_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 9, FLAGS, .unit = "colors_mode"}, + { "l", "set colors mode", OFFSET(colors_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 9, FLAGS, .unit = "colors_mode"}, + { "whiteonblack", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "colors_mode" }, + { "blackonwhite", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "colors_mode" }, + { "whiteongray", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "colors_mode" }, + { "blackongray", NULL, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, .unit = "colors_mode" }, + { "coloronblack", NULL, 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, .unit = "colors_mode" }, + { "coloronwhite", NULL, 0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, FLAGS, .unit = "colors_mode" }, + { "colorongray" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, FLAGS, .unit = "colors_mode" }, + { "blackoncolor", NULL, 0, AV_OPT_TYPE_CONST, {.i64=7}, 0, 0, FLAGS, .unit = "colors_mode" }, + { "whiteoncolor", NULL, 0, AV_OPT_TYPE_CONST, {.i64=8}, 0, 0, FLAGS, .unit = "colors_mode" }, + { "grayoncolor" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=9}, 0, 0, FLAGS, .unit = "colors_mode" }, { NULL } }; @@ -667,12 +667,12 @@ static const AVOption thistogram_options[] = { { "e", "display envelope", OFFSET(envelope), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { "ecolor", "set envelope color", OFFSET(envelope_rgba), AV_OPT_TYPE_COLOR, {.str="gold"}, 0, 0, FLAGS }, { "ec", "set envelope color", OFFSET(envelope_rgba), AV_OPT_TYPE_COLOR, {.str="gold"}, 0, 0, FLAGS }, - { "slide", "set slide mode", OFFSET(slide), AV_OPT_TYPE_INT, {.i64=1}, 0, 4, FLAGS, "slide" }, - {"frame", "draw new frames", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "slide"}, - {"replace", "replace old columns with new", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "slide"}, - {"scroll", "scroll from right to left", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "slide"}, - {"rscroll", "scroll from left to right", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "slide"}, - {"picture", "display graph in single frame", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, "slide"}, + { "slide", "set slide mode", OFFSET(slide), AV_OPT_TYPE_INT, {.i64=1}, 0, 4, FLAGS, .unit = "slide" }, + {"frame", "draw new frames", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "slide"}, + {"replace", "replace old columns with new", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "slide"}, + {"scroll", "scroll from right to left", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "slide"}, + {"rscroll", "scroll from left to right", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, .unit = "slide"}, + {"picture", "display graph in single frame", OFFSET(slide), AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, FLAGS, .unit = "slide"}, { NULL } }; diff --git a/libavfilter/vf_hqdn3d.c b/libavfilter/vf_hqdn3d.c index c796ea9ab4f..d95ae8f898b 100644 --- a/libavfilter/vf_hqdn3d.c +++ b/libavfilter/vf_hqdn3d.c @@ -31,12 +31,12 @@ #include "config.h" #include "libavutil/attributes.h" #include "libavutil/common.h" +#include "libavutil/emms.h" #include "libavutil/pixdesc.h" #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "vf_hqdn3d.h" @@ -383,13 +383,6 @@ static const AVFilterPad avfilter_vf_hqdn3d_inputs[] = { }; -static const AVFilterPad avfilter_vf_hqdn3d_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - const AVFilter ff_vf_hqdn3d = { .name = "hqdn3d", .description = NULL_IF_CONFIG_SMALL("Apply a High Quality 3D Denoiser."), @@ -398,7 +391,7 @@ const AVFilter ff_vf_hqdn3d = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_hqdn3d_inputs), - FILTER_OUTPUTS(avfilter_vf_hqdn3d_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_hqx.c b/libavfilter/vf_hqx.c index bc70d984e98..4a8ed184f49 100644 --- a/libavfilter/vf_hqx.c +++ b/libavfilter/vf_hqx.c @@ -31,6 +31,7 @@ #include "libavutil/avassert.h" #include "libavutil/pixdesc.h" #include "internal.h" +#include "video.h" typedef int (*hqxfunc_t)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); diff --git a/libavfilter/vf_hsvkey.c b/libavfilter/vf_hsvkey.c index 8963bcf1f83..0bd8cace37c 100644 --- a/libavfilter/vf_hsvkey.c +++ b/libavfilter/vf_hsvkey.c @@ -21,12 +21,10 @@ #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" typedef struct HSVKeyContext { const AVClass *class; @@ -267,7 +265,7 @@ static const enum AVPixelFormat key_pixel_fmts[] = { AV_PIX_FMT_NONE }; -static const AVFilterPad hsvkey_inputs[] = { +static const AVFilterPad inputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, @@ -277,7 +275,7 @@ static const AVFilterPad hsvkey_inputs[] = { }, }; -static const AVFilterPad hsvkey_outputs[] = { +static const AVFilterPad outputs[] = { { .name = "default", .type = AVMEDIA_TYPE_VIDEO, @@ -304,8 +302,8 @@ const AVFilter ff_vf_hsvkey = { .description = NULL_IF_CONFIG_SMALL("Turns a certain HSV range into transparency. Operates on YUV colors."), .priv_size = sizeof(HSVKeyContext), .priv_class = &hsvkey_class, - FILTER_INPUTS(hsvkey_inputs), - FILTER_OUTPUTS(hsvkey_outputs), + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), FILTER_PIXFMTS_ARRAY(key_pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, @@ -339,24 +337,6 @@ static const AVOption hsvhold_options[] = { { NULL } }; -static const AVFilterPad hsvhold_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE, - .filter_frame = filter_frame, - .config_props = config_input, - }, -}; - -static const AVFilterPad hsvhold_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_output, - }, -}; - AVFILTER_DEFINE_CLASS(hsvhold); const AVFilter ff_vf_hsvhold = { @@ -364,8 +344,8 @@ const AVFilter ff_vf_hsvhold = { .description = NULL_IF_CONFIG_SMALL("Turns a certain HSV range into gray."), .priv_size = sizeof(HSVKeyContext), .priv_class = &hsvhold_class, - FILTER_INPUTS(hsvhold_inputs), - FILTER_OUTPUTS(hsvhold_outputs), + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(outputs), FILTER_PIXFMTS_ARRAY(hold_pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_hue.c b/libavfilter/vf_hue.c index 644c99b1b1a..8fea2128fa2 100644 --- a/libavfilter/vf_hue.c +++ b/libavfilter/vf_hue.c @@ -32,7 +32,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -500,13 +499,6 @@ static const AVFilterPad hue_inputs[] = { }, }; -static const AVFilterPad hue_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_hue = { .name = "hue", .description = NULL_IF_CONFIG_SMALL("Adjust the hue and saturation of the input video."), @@ -515,7 +507,7 @@ const AVFilter ff_vf_hue = { .uninit = uninit, .process_command = process_command, FILTER_INPUTS(hue_inputs), - FILTER_OUTPUTS(hue_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &hue_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_huesaturation.c b/libavfilter/vf_huesaturation.c index d4e3fea1c51..bea13deca90 100644 --- a/libavfilter/vf_huesaturation.c +++ b/libavfilter/vf_huesaturation.c @@ -17,10 +17,9 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -106,7 +105,7 @@ static int do_slice_##name##_##xall(AVFilterContext *ctx, \ const int process_h = frame->height; \ const int slice_start = (process_h * jobnr ) / nb_jobs; \ const int slice_end = (process_h * (jobnr+1)) / nb_jobs; \ - const int linesize = frame->linesize[0] / sizeof(type); \ + const ptrdiff_t linesize = frame->linesize[0] / sizeof(type); \ type *row = (type *)frame->data[0] + linesize * slice_start; \ const uint8_t offset_r = s->rgba_map[R]; \ const uint8_t offset_g = s->rgba_map[G]; \ @@ -434,13 +433,6 @@ static const AVFilterPad huesaturation_inputs[] = { }, }; -static const AVFilterPad huesaturation_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(HueSaturationContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -448,14 +440,14 @@ static const AVOption huesaturation_options[] = { { "hue", "set the hue shift", OFFSET(hue), AV_OPT_TYPE_FLOAT, {.dbl=0},-180, 180, VF }, { "saturation", "set the saturation shift", OFFSET(saturation), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, VF }, { "intensity", "set the intensity shift", OFFSET(intensity), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, VF }, - { "colors", "set colors range", OFFSET(colors), AV_OPT_TYPE_FLAGS, {.i64=ALL}, 0,ALL,VF, "colors" }, - { "r", "set reds", 0, AV_OPT_TYPE_CONST, {.i64=RED}, 0, 0, VF, "colors" }, - { "y", "set yellows", 0, AV_OPT_TYPE_CONST, {.i64=YELLOW}, 0, 0, VF, "colors" }, - { "g", "set greens", 0, AV_OPT_TYPE_CONST, {.i64=GREEN}, 0, 0, VF, "colors" }, - { "c", "set cyans", 0, AV_OPT_TYPE_CONST, {.i64=CYAN}, 0, 0, VF, "colors" }, - { "b", "set blues", 0, AV_OPT_TYPE_CONST, {.i64=BLUE}, 0, 0, VF, "colors" }, - { "m", "set magentas", 0, AV_OPT_TYPE_CONST, {.i64=MAGENTA}, 0, 0, VF, "colors" }, - { "a", "set all colors", 0, AV_OPT_TYPE_CONST, {.i64=ALL}, 0, 0, VF, "colors" }, + { "colors", "set colors range", OFFSET(colors), AV_OPT_TYPE_FLAGS, {.i64=ALL}, 0,ALL,VF, .unit = "colors" }, + { "r", "set reds", 0, AV_OPT_TYPE_CONST, {.i64=RED}, 0, 0, VF, .unit = "colors" }, + { "y", "set yellows", 0, AV_OPT_TYPE_CONST, {.i64=YELLOW}, 0, 0, VF, .unit = "colors" }, + { "g", "set greens", 0, AV_OPT_TYPE_CONST, {.i64=GREEN}, 0, 0, VF, .unit = "colors" }, + { "c", "set cyans", 0, AV_OPT_TYPE_CONST, {.i64=CYAN}, 0, 0, VF, .unit = "colors" }, + { "b", "set blues", 0, AV_OPT_TYPE_CONST, {.i64=BLUE}, 0, 0, VF, .unit = "colors" }, + { "m", "set magentas", 0, AV_OPT_TYPE_CONST, {.i64=MAGENTA}, 0, 0, VF, .unit = "colors" }, + { "a", "set all colors", 0, AV_OPT_TYPE_CONST, {.i64=ALL}, 0, 0, VF, .unit = "colors" }, { "strength", "set the filtering strength", OFFSET(strength), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0,100,VF }, { "rw", "set the red weight", OFFSET(rlw), AV_OPT_TYPE_FLOAT, {.dbl=.333}, 0, 1, VF }, { "gw", "set the green weight", OFFSET(glw), AV_OPT_TYPE_FLOAT, {.dbl=.334}, 0, 1, VF }, @@ -472,7 +464,7 @@ const AVFilter ff_vf_huesaturation = { .priv_size = sizeof(HueSaturationContext), .priv_class = &huesaturation_class, FILTER_INPUTS(huesaturation_inputs), - FILTER_OUTPUTS(huesaturation_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_hwmap.c b/libavfilter/vf_hwmap.c index 2e03dfc1fec..3f37dab2c89 100644 --- a/libavfilter/vf_hwmap.c +++ b/libavfilter/vf_hwmap.c @@ -373,20 +373,20 @@ static const AVOption hwmap_options[] = { { "mode", "Frame mapping mode", OFFSET(mode), AV_OPT_TYPE_FLAGS, { .i64 = AV_HWFRAME_MAP_READ | AV_HWFRAME_MAP_WRITE }, - 0, INT_MAX, FLAGS, "mode" }, + 0, INT_MAX, FLAGS, .unit = "mode" }, { "read", "Mapping should be readable", 0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_READ }, - INT_MIN, INT_MAX, FLAGS, "mode" }, + INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, { "write", "Mapping should be writeable", 0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_WRITE }, - INT_MIN, INT_MAX, FLAGS, "mode" }, + INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, { "overwrite", "Mapping will always overwrite the entire frame", 0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_OVERWRITE }, - INT_MIN, INT_MAX, FLAGS, "mode" }, + INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, { "direct", "Mapping should not involve any copying", 0, AV_OPT_TYPE_CONST, { .i64 = AV_HWFRAME_MAP_DIRECT }, - INT_MIN, INT_MAX, FLAGS, "mode" }, + INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, { "derive_device", "Derive a new device of this type", OFFSET(derive_device_type), AV_OPT_TYPE_STRING, @@ -427,4 +427,5 @@ const AVFilter ff_vf_hwmap = { FILTER_OUTPUTS(hwmap_outputs), FILTER_QUERY_FUNC(hwmap_query_formats), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_hwupload.c b/libavfilter/vf_hwupload.c index dbc41734ccc..ef61bb41375 100644 --- a/libavfilter/vf_hwupload.c +++ b/libavfilter/vf_hwupload.c @@ -258,4 +258,5 @@ const AVFilter ff_vf_hwupload = { FILTER_OUTPUTS(hwupload_outputs), FILTER_QUERY_FUNC(hwupload_query_formats), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_hysteresis.c b/libavfilter/vf_hysteresis.c index a274ec49626..d2fd3011b8f 100644 --- a/libavfilter/vf_hysteresis.c +++ b/libavfilter/vf_hysteresis.c @@ -23,7 +23,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" diff --git a/libavfilter/vf_iccdetect.c b/libavfilter/vf_iccdetect.c index 7c08a2baaa1..16eacbbb560 100644 --- a/libavfilter/vf_iccdetect.c +++ b/libavfilter/vf_iccdetect.c @@ -31,6 +31,7 @@ #include "avfilter.h" #include "fflcms2.h" #include "internal.h" +#include "video.h" typedef struct IccDetectContext { const AVClass *class; @@ -92,7 +93,9 @@ static int iccdetect_filter_frame(AVFilterLink *inlink, AVFrame *frame) if (!profile) return AVERROR_INVALIDDATA; - ret = ff_icc_profile_read_primaries(&s->icc, profile, &coeffs); + ret = ff_icc_profile_sanitize(&s->icc, profile); + if (!ret) + ret = ff_icc_profile_read_primaries(&s->icc, profile, &coeffs); if (!ret) ret = ff_icc_profile_detect_transfer(&s->icc, profile, &s->profile_trc); cmsCloseProfile(profile); @@ -123,13 +126,6 @@ static const AVFilterPad iccdetect_inputs[] = { }, }; -static const AVFilterPad iccdetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_iccdetect = { .name = "iccdetect", .description = NULL_IF_CONFIG_SMALL("Detect and parse ICC profiles."), @@ -139,5 +135,5 @@ const AVFilter ff_vf_iccdetect = { .init = &iccdetect_init, .uninit = &iccdetect_uninit, FILTER_INPUTS(iccdetect_inputs), - FILTER_OUTPUTS(iccdetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_iccgen.c b/libavfilter/vf_iccgen.c index 67d02f5bc20..aae7ae5d268 100644 --- a/libavfilter/vf_iccgen.c +++ b/libavfilter/vf_iccgen.c @@ -30,6 +30,7 @@ #include "avfilter.h" #include "fflcms2.h" #include "internal.h" +#include "video.h" typedef struct IccGenContext { const AVClass *class; @@ -48,35 +49,35 @@ typedef struct IccGenContext { #define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM static const AVOption iccgen_options[] = { - {"color_primaries", "select color primaries", OFFSET(color_prim), AV_OPT_TYPE_INT, {.i64=0}, 0, AVCOL_PRI_NB-1, VF, "color_primaries"}, - {"auto", "infer based on frame", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VF, "color_primaries"}, - {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT709}, 0, 0, VF, "color_primaries"}, - {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470M}, 0, 0, VF, "color_primaries"}, - {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470BG}, 0, 0, VF, "color_primaries"}, - {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE170M}, 0, 0, VF, "color_primaries"}, - {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE240M}, 0, 0, VF, "color_primaries"}, - {"film", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_FILM}, 0, 0, VF, "color_primaries"}, - {"bt2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT2020}, 0, 0, VF, "color_primaries"}, - {"smpte428", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE428}, 0, 0, VF, "color_primaries"}, - {"smpte431", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE431}, 0, 0, VF, "color_primaries"}, - {"smpte432", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE432}, 0, 0, VF, "color_primaries"}, - {"jedec-p22", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_JEDEC_P22}, 0, 0, VF, "color_primaries"}, - {"ebu3213", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_EBU3213}, 0, 0, VF, "color_primaries"}, - {"color_trc", "select color transfer", OFFSET(color_trc), AV_OPT_TYPE_INT, {.i64=0}, 0, AVCOL_TRC_NB-1, VF, "color_trc"}, - {"auto", "infer based on frame", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VF, "color_trc"}, - {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT709}, 0, 0, VF, "color_trc"}, - {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA22}, 0, 0, VF, "color_trc"}, - {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA28}, 0, 0, VF, "color_trc"}, - {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE170M}, 0, 0, VF, "color_trc"}, - {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE240M}, 0, 0, VF, "color_trc"}, - {"linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_LINEAR}, 0, 0, VF, "color_trc"}, - {"iec61966-2-4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_4}, 0, 0, VF, "color_trc"}, - {"bt1361e", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT1361_ECG}, 0, 0, VF, "color_trc"}, - {"iec61966-2-1", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_1}, 0, 0, VF, "color_trc"}, - {"bt2020-10", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_10}, 0, 0, VF, "color_trc"}, - {"bt2020-12", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_12}, 0, 0, VF, "color_trc"}, - {"smpte2084", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE2084}, 0, 0, VF, "color_trc"}, - {"arib-std-b67", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_ARIB_STD_B67}, 0, 0, VF, "color_trc"}, + {"color_primaries", "select color primaries", OFFSET(color_prim), AV_OPT_TYPE_INT, {.i64=0}, 0, AVCOL_PRI_NB-1, VF, .unit = "color_primaries"}, + {"auto", "infer based on frame", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VF, .unit = "color_primaries"}, + {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT709}, 0, 0, VF, .unit = "color_primaries"}, + {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470M}, 0, 0, VF, .unit = "color_primaries"}, + {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470BG}, 0, 0, VF, .unit = "color_primaries"}, + {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE170M}, 0, 0, VF, .unit = "color_primaries"}, + {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE240M}, 0, 0, VF, .unit = "color_primaries"}, + {"film", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_FILM}, 0, 0, VF, .unit = "color_primaries"}, + {"bt2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT2020}, 0, 0, VF, .unit = "color_primaries"}, + {"smpte428", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE428}, 0, 0, VF, .unit = "color_primaries"}, + {"smpte431", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE431}, 0, 0, VF, .unit = "color_primaries"}, + {"smpte432", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE432}, 0, 0, VF, .unit = "color_primaries"}, + {"jedec-p22", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_JEDEC_P22}, 0, 0, VF, .unit = "color_primaries"}, + {"ebu3213", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_EBU3213}, 0, 0, VF, .unit = "color_primaries"}, + {"color_trc", "select color transfer", OFFSET(color_trc), AV_OPT_TYPE_INT, {.i64=0}, 0, AVCOL_TRC_NB-1, VF, .unit = "color_trc"}, + {"auto", "infer based on frame", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VF, .unit = "color_trc"}, + {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT709}, 0, 0, VF, .unit = "color_trc"}, + {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA22}, 0, 0, VF, .unit = "color_trc"}, + {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA28}, 0, 0, VF, .unit = "color_trc"}, + {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE170M}, 0, 0, VF, .unit = "color_trc"}, + {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE240M}, 0, 0, VF, .unit = "color_trc"}, + {"linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_LINEAR}, 0, 0, VF, .unit = "color_trc"}, + {"iec61966-2-4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_4}, 0, 0, VF, .unit = "color_trc"}, + {"bt1361e", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT1361_ECG}, 0, 0, VF, .unit = "color_trc"}, + {"iec61966-2-1", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_1}, 0, 0, VF, .unit = "color_trc"}, + {"bt2020-10", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_10}, 0, 0, VF, .unit = "color_trc"}, + {"bt2020-12", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_12}, 0, 0, VF, .unit = "color_trc"}, + {"smpte2084", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE2084}, 0, 0, VF, .unit = "color_trc"}, + {"arib-std-b67", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_ARIB_STD_B67}, 0, 0, VF, .unit = "color_trc"}, { "force", "overwrite existing ICC profile", OFFSET(force), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, VF }, { NULL } }; @@ -161,13 +162,6 @@ static const AVFilterPad iccgen_inputs[] = { }, }; -static const AVFilterPad iccgen_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_iccgen = { .name = "iccgen", .description = NULL_IF_CONFIG_SMALL("Generate and attach ICC profiles."), @@ -177,5 +171,5 @@ const AVFilter ff_vf_iccgen = { .init = &iccgen_init, .uninit = &iccgen_uninit, FILTER_INPUTS(iccgen_inputs), - FILTER_OUTPUTS(iccgen_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_identity.c b/libavfilter/vf_identity.c index 84def7959be..d3a5ec14f6d 100644 --- a/libavfilter/vf_identity.c +++ b/libavfilter/vf_identity.c @@ -30,10 +30,8 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "framesync.h" #include "internal.h" -#include "video.h" #include "scene_sad.h" typedef struct IdentityContext { diff --git a/libavfilter/vf_idet.c b/libavfilter/vf_idet.c index 83d992add12..abd375230aa 100644 --- a/libavfilter/vf_idet.c +++ b/libavfilter/vf_idet.c @@ -183,13 +183,29 @@ static void filter(AVFilterContext *ctx) } if (idet->last_type == TFF){ +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS idet->cur->top_field_first = 1; idet->cur->interlaced_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + idet->cur->flags |= (AV_FRAME_FLAG_INTERLACED | AV_FRAME_FLAG_TOP_FIELD_FIRST); }else if(idet->last_type == BFF){ +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS idet->cur->top_field_first = 0; idet->cur->interlaced_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + idet->cur->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + idet->cur->flags |= AV_FRAME_FLAG_INTERLACED; }else if(idet->last_type == PROGRESSIVE){ +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS idet->cur->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + idet->cur->flags &= ~AV_FRAME_FLAG_INTERLACED; } for(i=0; i<3; i++) @@ -238,13 +254,19 @@ static int filter_frame(AVFilterLink *link, AVFrame *picref) // initial frame(s) and not interlaced, just pass through for // the analyze_interlaced_flag mode if (idet->analyze_interlaced_flag && - !picref->interlaced_frame && + !(picref->flags & AV_FRAME_FLAG_INTERLACED) && !idet->next) { return ff_filter_frame(ctx->outputs[0], picref); } if (idet->analyze_interlaced_flag_done) { - if (picref->interlaced_frame && idet->interlaced_flag_accuracy < 0) + if ((picref->flags & AV_FRAME_FLAG_INTERLACED) && idet->interlaced_flag_accuracy < 0) { +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS picref->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + picref->flags &= ~AV_FRAME_FLAG_INTERLACED; + } return ff_filter_frame(ctx->outputs[0], picref); } @@ -282,8 +304,13 @@ static int filter_frame(AVFilterLink *link, AVFrame *picref) } if (idet->analyze_interlaced_flag) { - if (idet->cur->interlaced_frame) { + if (idet->cur->flags & AV_FRAME_FLAG_INTERLACED) { +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS idet->cur->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + idet->cur->flags &= ~AV_FRAME_FLAG_INTERLACED; filter(ctx); if (idet->last_type == PROGRESSIVE) { idet->interlaced_flag_accuracy --; @@ -295,8 +322,14 @@ static int filter_frame(AVFilterLink *link, AVFrame *picref) if (idet->analyze_interlaced_flag == 1) { ff_filter_frame(ctx->outputs[0], av_frame_clone(idet->cur)); - if (idet->next->interlaced_frame && idet->interlaced_flag_accuracy < 0) + if ((idet->next->flags & AV_FRAME_FLAG_INTERLACED) && idet->interlaced_flag_accuracy < 0) { +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS idet->next->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + idet->next->flags &= ~AV_FRAME_FLAG_INTERLACED; + } idet->analyze_interlaced_flag_done = 1; av_log(ctx, AV_LOG_INFO, "Final flag accuracy %d\n", idet->interlaced_flag_accuracy); return ff_filter_frame(ctx->outputs[0], av_frame_clone(idet->next)); diff --git a/libavfilter/vf_il.c b/libavfilter/vf_il.c index e1c380a2cf6..5eaa40a6f8c 100644 --- a/libavfilter/vf_il.c +++ b/libavfilter/vf_il.c @@ -28,7 +28,9 @@ #include "libavutil/imgutils.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" +#include "video.h" enum FilterMode { MODE_NONE, @@ -49,27 +51,27 @@ typedef struct IlContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption il_options[] = { - {"luma_mode", "select luma mode", OFFSET(luma_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, "luma_mode"}, - {"l", "select luma mode", OFFSET(luma_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, "luma_mode"}, - {"none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_NONE}, 0, 0, FLAGS, "luma_mode"}, - {"interleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, "luma_mode"}, - {"i", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, "luma_mode"}, - {"deinterleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, "luma_mode"}, - {"d", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, "luma_mode"}, - {"chroma_mode", "select chroma mode", OFFSET(chroma_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, "chroma_mode"}, - {"c", "select chroma mode", OFFSET(chroma_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, "chroma_mode"}, - {"none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_NONE}, 0, 0, FLAGS, "chroma_mode"}, - {"interleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, "chroma_mode"}, - {"i", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, "chroma_mode"}, - {"deinterleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, "chroma_mode"}, - {"d", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, "chroma_mode"}, - {"alpha_mode", "select alpha mode", OFFSET(alpha_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, "alpha_mode"}, - {"a", "select alpha mode", OFFSET(alpha_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, "alpha_mode"}, - {"none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_NONE}, 0, 0, FLAGS, "alpha_mode"}, - {"interleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, "alpha_mode"}, - {"i", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, "alpha_mode"}, - {"deinterleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, "alpha_mode"}, - {"d", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, "alpha_mode"}, + {"luma_mode", "select luma mode", OFFSET(luma_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, .unit = "luma_mode"}, + {"l", "select luma mode", OFFSET(luma_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, .unit = "luma_mode"}, + {"none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_NONE}, 0, 0, FLAGS, .unit = "luma_mode"}, + {"interleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, .unit = "luma_mode"}, + {"i", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, .unit = "luma_mode"}, + {"deinterleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, .unit = "luma_mode"}, + {"d", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, .unit = "luma_mode"}, + {"chroma_mode", "select chroma mode", OFFSET(chroma_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, .unit = "chroma_mode"}, + {"c", "select chroma mode", OFFSET(chroma_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, .unit = "chroma_mode"}, + {"none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_NONE}, 0, 0, FLAGS, .unit = "chroma_mode"}, + {"interleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, .unit = "chroma_mode"}, + {"i", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, .unit = "chroma_mode"}, + {"deinterleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, .unit = "chroma_mode"}, + {"d", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, .unit = "chroma_mode"}, + {"alpha_mode", "select alpha mode", OFFSET(alpha_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, .unit = "alpha_mode"}, + {"a", "select alpha mode", OFFSET(alpha_mode), AV_OPT_TYPE_INT, {.i64=MODE_NONE}, MODE_NONE, MODE_DEINTERLEAVE, FLAGS, .unit = "alpha_mode"}, + {"none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_NONE}, 0, 0, FLAGS, .unit = "alpha_mode"}, + {"interleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, .unit = "alpha_mode"}, + {"i", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE}, 0, 0, FLAGS, .unit = "alpha_mode"}, + {"deinterleave", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, .unit = "alpha_mode"}, + {"d", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MODE_DEINTERLEAVE}, 0, 0, FLAGS, .unit = "alpha_mode"}, {"luma_swap", "swap luma fields", OFFSET(luma_swap), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"ls", "swap luma fields", OFFSET(luma_swap), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, {"chroma_swap", "swap chroma fields", OFFSET(chroma_swap), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, @@ -183,19 +185,12 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_il = { .name = "il", .description = NULL_IF_CONFIG_SMALL("Deinterleave or interleave fields."), .priv_size = sizeof(IlContext), FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .priv_class = &il_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_kerndeint.c b/libavfilter/vf_kerndeint.c index dd320fbebfa..bbad3f9bfc9 100644 --- a/libavfilter/vf_kerndeint.c +++ b/libavfilter/vf_kerndeint.c @@ -32,8 +32,8 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" typedef struct KerndeintContext { const AVClass *class; @@ -141,7 +141,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) return AVERROR(ENOMEM); } av_frame_copy_props(outpic, inpic); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS outpic->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + outpic->flags &= ~AV_FRAME_FLAG_INTERLACED; for (plane = 0; plane < 4 && inpic->data[plane] && inpic->linesize[plane]; plane++) { h = plane == 0 ? inlink->h : AV_CEIL_RSHIFT(inlink->h, kerndeint->vsub); @@ -289,13 +294,6 @@ static const AVFilterPad kerndeint_inputs[] = { }, }; -static const AVFilterPad kerndeint_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_kerndeint = { .name = "kerndeint", @@ -304,6 +302,6 @@ const AVFilter ff_vf_kerndeint = { .priv_class = &kerndeint_class, .uninit = uninit, FILTER_INPUTS(kerndeint_inputs), - FILTER_OUTPUTS(kerndeint_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/vf_lagfun.c b/libavfilter/vf_lagfun.c index 352f08512f7..20cdd9fa659 100644 --- a/libavfilter/vf_lagfun.c +++ b/libavfilter/vf_lagfun.c @@ -19,12 +19,10 @@ */ #include "libavutil/imgutils.h" -#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_lenscorrection.c b/libavfilter/vf_lenscorrection.c index 413eabefba9..aa545f71c63 100644 --- a/libavfilter/vf_lenscorrection.c +++ b/libavfilter/vf_lenscorrection.c @@ -59,9 +59,9 @@ static const AVOption lenscorrection_options[] = { { "cy", "set relative center y", OFFSET(cy), AV_OPT_TYPE_DOUBLE, {.dbl=0.5}, 0, 1, .flags=FLAGS }, { "k1", "set quadratic distortion factor", OFFSET(k1), AV_OPT_TYPE_DOUBLE, {.dbl=0.0}, -1, 1, .flags=FLAGS }, { "k2", "set double quadratic distortion factor", OFFSET(k2), AV_OPT_TYPE_DOUBLE, {.dbl=0.0}, -1, 1, .flags=FLAGS }, - { "i", "set interpolation type", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=0}, 0, 64, .flags=FLAGS, "i" }, - { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=0},0, 0, .flags=FLAGS, "i" }, - { "bilinear", "bilinear", 0, AV_OPT_TYPE_CONST, {.i64=1},0, 0, .flags=FLAGS, "i" }, + { "i", "set interpolation type", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=0}, 0, 64, .flags=FLAGS, .unit = "i" }, + { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=0},0, 0, .flags=FLAGS, .unit = "i" }, + { "bilinear", "bilinear", 0, AV_OPT_TYPE_CONST, {.i64=1},0, 0, .flags=FLAGS, .unit = "i" }, { "fc", "set the color of the unmapped pixels", OFFSET(fill_rgba), AV_OPT_TYPE_COLOR, {.str="black@0"}, .flags = FLAGS }, { NULL } }; diff --git a/libavfilter/vf_lensfun.c b/libavfilter/vf_lensfun.c index f544af773e5..3d7e6cd6b32 100644 --- a/libavfilter/vf_lensfun.c +++ b/libavfilter/vf_lensfun.c @@ -28,12 +28,9 @@ #include #include -#include "libavutil/imgutils.h" #include "libavutil/opt.h" -#include "libswscale/swscale.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -99,32 +96,32 @@ static const AVOption lensfun_options[] = { { "model", "set camera model", OFFSET(model), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, { "lens_model", "set lens model", OFFSET(lens_model), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, { "db_path", "set path to database", OFFSET(db_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=GEOMETRY_DISTORTION}, 0, VIGNETTING | GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION, FLAGS, "mode" }, - { "vignetting", "fix lens vignetting", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING}, 0, 0, FLAGS, "mode" }, - { "geometry", "correct geometry distortion", 0, AV_OPT_TYPE_CONST, {.i64=GEOMETRY_DISTORTION}, 0, 0, FLAGS, "mode" }, - { "subpixel", "fix chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" }, - { "vig_geo", "fix lens vignetting and correct geometry distortion", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | GEOMETRY_DISTORTION}, 0, 0, FLAGS, "mode" }, - { "vig_subpixel", "fix lens vignetting and chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" }, - { "distortion", "correct geometry distortion and chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" }, - { "all", NULL, 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, "mode" }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=GEOMETRY_DISTORTION}, 0, VIGNETTING | GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION, FLAGS, .unit = "mode" }, + { "vignetting", "fix lens vignetting", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING}, 0, 0, FLAGS, .unit = "mode" }, + { "geometry", "correct geometry distortion", 0, AV_OPT_TYPE_CONST, {.i64=GEOMETRY_DISTORTION}, 0, 0, FLAGS, .unit = "mode" }, + { "subpixel", "fix chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=SUBPIXEL_DISTORTION}, 0, 0, FLAGS, .unit = "mode" }, + { "vig_geo", "fix lens vignetting and correct geometry distortion", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | GEOMETRY_DISTORTION}, 0, 0, FLAGS, .unit = "mode" }, + { "vig_subpixel", "fix lens vignetting and chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, .unit = "mode" }, + { "distortion", "correct geometry distortion and chromatic aberrations", 0, AV_OPT_TYPE_CONST, {.i64=GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, .unit = "mode" }, + { "all", NULL, 0, AV_OPT_TYPE_CONST, {.i64=VIGNETTING | GEOMETRY_DISTORTION | SUBPIXEL_DISTORTION}, 0, 0, FLAGS, .unit = "mode" }, { "focal_length", "focal length of video (zoom; constant for the duration of the use of this filter)", OFFSET(focal_length), AV_OPT_TYPE_FLOAT, {.dbl=18}, 0.0, DBL_MAX, FLAGS }, { "aperture", "aperture (constant for the duration of the use of this filter)", OFFSET(aperture), AV_OPT_TYPE_FLOAT, {.dbl=3.5}, 0.0, DBL_MAX, FLAGS }, { "focus_distance", "focus distance (constant for the duration of the use of this filter)", OFFSET(focus_distance), AV_OPT_TYPE_FLOAT, {.dbl=1000.0f}, 0.0, DBL_MAX, FLAGS }, { "scale", "scale factor applied after corrections (0.0 means automatic scaling)", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, DBL_MAX, FLAGS }, - { "target_geometry", "target geometry of the lens correction (only when geometry correction is enabled)", OFFSET(target_geometry), AV_OPT_TYPE_INT, {.i64=LF_RECTILINEAR}, 0, INT_MAX, FLAGS, "lens_geometry" }, - { "rectilinear", "rectilinear lens (default)", 0, AV_OPT_TYPE_CONST, {.i64=LF_RECTILINEAR}, 0, 0, FLAGS, "lens_geometry" }, - { "fisheye", "fisheye lens", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE}, 0, 0, FLAGS, "lens_geometry" }, - { "panoramic", "panoramic (cylindrical)", 0, AV_OPT_TYPE_CONST, {.i64=LF_PANORAMIC}, 0, 0, FLAGS, "lens_geometry" }, - { "equirectangular", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=LF_EQUIRECTANGULAR}, 0, 0, FLAGS, "lens_geometry" }, - { "fisheye_orthographic", "orthographic fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_ORTHOGRAPHIC}, 0, 0, FLAGS, "lens_geometry" }, - { "fisheye_stereographic", "stereographic fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_STEREOGRAPHIC}, 0, 0, FLAGS, "lens_geometry" }, - { "fisheye_equisolid", "equisolid fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_EQUISOLID}, 0, 0, FLAGS, "lens_geometry" }, - { "fisheye_thoby", "fisheye as measured by thoby", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_THOBY}, 0, 0, FLAGS, "lens_geometry" }, + { "target_geometry", "target geometry of the lens correction (only when geometry correction is enabled)", OFFSET(target_geometry), AV_OPT_TYPE_INT, {.i64=LF_RECTILINEAR}, 0, INT_MAX, FLAGS, .unit = "lens_geometry" }, + { "rectilinear", "rectilinear lens (default)", 0, AV_OPT_TYPE_CONST, {.i64=LF_RECTILINEAR}, 0, 0, FLAGS, .unit = "lens_geometry" }, + { "fisheye", "fisheye lens", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE}, 0, 0, FLAGS, .unit = "lens_geometry" }, + { "panoramic", "panoramic (cylindrical)", 0, AV_OPT_TYPE_CONST, {.i64=LF_PANORAMIC}, 0, 0, FLAGS, .unit = "lens_geometry" }, + { "equirectangular", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=LF_EQUIRECTANGULAR}, 0, 0, FLAGS, .unit = "lens_geometry" }, + { "fisheye_orthographic", "orthographic fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_ORTHOGRAPHIC}, 0, 0, FLAGS, .unit = "lens_geometry" }, + { "fisheye_stereographic", "stereographic fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_STEREOGRAPHIC}, 0, 0, FLAGS, .unit = "lens_geometry" }, + { "fisheye_equisolid", "equisolid fisheye", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_EQUISOLID}, 0, 0, FLAGS, .unit = "lens_geometry" }, + { "fisheye_thoby", "fisheye as measured by thoby", 0, AV_OPT_TYPE_CONST, {.i64=LF_FISHEYE_THOBY}, 0, 0, FLAGS, .unit = "lens_geometry" }, { "reverse", "Does reverse correction (regular image to lens distorted)", OFFSET(reverse), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, - { "interpolation", "Type of interpolation", OFFSET(interpolation_type), AV_OPT_TYPE_INT, {.i64=LINEAR}, 0, LANCZOS, FLAGS, "interpolation" }, - { "nearest", NULL, 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interpolation" }, - { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "interpolation" }, - { "lanczos", NULL, 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interpolation" }, + { "interpolation", "Type of interpolation", OFFSET(interpolation_type), AV_OPT_TYPE_INT, {.i64=LINEAR}, 0, LANCZOS, FLAGS, .unit = "interpolation" }, + { "nearest", NULL, 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, .unit = "interpolation" }, + { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, .unit = "interpolation" }, + { "lanczos", NULL, 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, .unit = "interpolation" }, { NULL } }; @@ -522,13 +519,6 @@ static const AVFilterPad lensfun_inputs[] = { }, }; -static const AVFilterPad lensfun_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_lensfun = { .name = "lensfun", .description = NULL_IF_CONFIG_SMALL("Apply correction to an image based on info derived from the lensfun database."), @@ -536,7 +526,7 @@ const AVFilter ff_vf_lensfun = { .init = init, .uninit = uninit, FILTER_INPUTS(lensfun_inputs), - FILTER_OUTPUTS(lensfun_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_RGB24), .priv_class = &lensfun_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_libplacebo.c b/libavfilter/vf_libplacebo.c index 7cd495de261..f0c34bd3bd1 100644 --- a/libavfilter/vf_libplacebo.c +++ b/libavfilter/vf_libplacebo.c @@ -16,16 +16,51 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" +#include "libavutil/eval.h" +#include "libavutil/fifo.h" #include "libavutil/file.h" #include "libavutil/opt.h" +#include "libavutil/parseutils.h" +#include "formats.h" #include "internal.h" +#include "filters.h" +#include "video.h" #include "vulkan_filter.h" #include "scale_eval.h" #include #include +#include #include +/* Backwards compatibility with older libplacebo */ +#if PL_API_VER < 276 +static inline AVFrame *pl_get_mapped_avframe(const struct pl_frame *frame) +{ + return frame->user_data; +} +#endif + +#if PL_API_VER >= 309 +#include +#else +typedef struct pl_options_t { + // Backwards compatibility shim of this struct + struct pl_render_params params; + struct pl_deband_params deband_params; + struct pl_sigmoid_params sigmoid_params; + struct pl_color_adjustment color_adjustment; + struct pl_peak_detect_params peak_detect_params; + struct pl_color_map_params color_map_params; + struct pl_dither_params dither_params; + struct pl_cone_params cone_params; +} *pl_options; + +#define pl_options_alloc(log) av_mallocz(sizeof(struct pl_options_t)) +#define pl_options_free(ptr) av_freep(ptr) +#endif + enum { TONE_MAP_AUTO, TONE_MAP_CLIP, @@ -42,23 +77,78 @@ enum { TONE_MAP_COUNT, }; -static const struct pl_tone_map_function * const tonemapping_funcs[TONE_MAP_COUNT] = { - [TONE_MAP_AUTO] = &pl_tone_map_auto, - [TONE_MAP_CLIP] = &pl_tone_map_clip, -#if PL_API_VER >= 246 - [TONE_MAP_ST2094_40] = &pl_tone_map_st2094_40, - [TONE_MAP_ST2094_10] = &pl_tone_map_st2094_10, -#endif - [TONE_MAP_BT2390] = &pl_tone_map_bt2390, - [TONE_MAP_BT2446A] = &pl_tone_map_bt2446a, - [TONE_MAP_SPLINE] = &pl_tone_map_spline, - [TONE_MAP_REINHARD] = &pl_tone_map_reinhard, - [TONE_MAP_MOBIUS] = &pl_tone_map_mobius, - [TONE_MAP_HABLE] = &pl_tone_map_hable, - [TONE_MAP_GAMMA] = &pl_tone_map_gamma, - [TONE_MAP_LINEAR] = &pl_tone_map_linear, +enum { + GAMUT_MAP_CLIP, + GAMUT_MAP_PERCEPTUAL, + GAMUT_MAP_RELATIVE, + GAMUT_MAP_SATURATION, + GAMUT_MAP_ABSOLUTE, + GAMUT_MAP_DESATURATE, + GAMUT_MAP_DARKEN, + GAMUT_MAP_HIGHLIGHT, + GAMUT_MAP_LINEAR, + GAMUT_MAP_COUNT, }; +static const char *const var_names[] = { + "in_idx", "idx",///< index of input + "in_w", "iw", ///< width of the input video frame + "in_h", "ih", ///< height of the input video frame + "out_w", "ow", ///< width of the output video frame + "out_h", "oh", ///< height of the output video frame + "crop_w", "cw", ///< evaluated input crop width + "crop_h", "ch", ///< evaluated input crop height + "pos_w", "pw", ///< evaluated output placement width + "pos_h", "ph", ///< evaluated output placement height + "a", ///< iw/ih + "sar", ///< input pixel aspect ratio + "dar", ///< output pixel aspect ratio + "hsub", ///< input horizontal subsampling factor + "vsub", ///< input vertical subsampling factor + "ohsub", ///< output horizontal subsampling factor + "ovsub", ///< output vertical subsampling factor + "in_t", "t", ///< input frame pts + "out_t", "ot", ///< output frame pts + "n", ///< number of frame + NULL, +}; + +enum var_name { + VAR_IN_IDX, VAR_IDX, + VAR_IN_W, VAR_IW, + VAR_IN_H, VAR_IH, + VAR_OUT_W, VAR_OW, + VAR_OUT_H, VAR_OH, + VAR_CROP_W, VAR_CW, + VAR_CROP_H, VAR_CH, + VAR_POS_W, VAR_PW, + VAR_POS_H, VAR_PH, + VAR_A, + VAR_SAR, + VAR_DAR, + VAR_HSUB, + VAR_VSUB, + VAR_OHSUB, + VAR_OVSUB, + VAR_IN_T, VAR_T, + VAR_OUT_T, VAR_OT, + VAR_N, + VAR_VARS_NB +}; + +/* per-input dynamic filter state */ +typedef struct LibplaceboInput { + int idx; + pl_renderer renderer; + pl_queue queue; + enum pl_queue_status qstatus; + struct pl_frame_mix mix; ///< temporary storage + AVFilterLink *link; + AVFifo *out_pts; ///< timestamps of wanted output frames + int64_t status_pts; + int status; +} LibplaceboInput; + typedef struct LibplaceboContext { /* lavfi vulkan*/ FFVulkanContext vkctx; @@ -67,16 +157,32 @@ typedef struct LibplaceboContext { pl_log log; pl_vulkan vulkan; pl_gpu gpu; - pl_renderer renderer; - pl_tex tex[8]; + pl_tex tex[4]; + + /* input state */ + LibplaceboInput *inputs; + int nb_inputs; + int64_t status_pts; ///< tracks status of most recently used input + int status; /* settings */ char *out_format_string; enum AVPixelFormat out_format; + char *fillcolor; + double var_values[VAR_VARS_NB]; char *w_expr; char *h_expr; - AVRational target_sar; + char *fps_string; + AVRational fps; ///< parsed FPS, or 0/0 for "none" + char *crop_x_expr, *crop_y_expr; + char *crop_w_expr, *crop_h_expr; + char *pos_x_expr, *pos_y_expr; + char *pos_w_expr, *pos_h_expr; + // Parsed expressions for input/output crop + AVExpr *crop_x_pexpr, *crop_y_pexpr, *crop_w_pexpr, *crop_h_pexpr; + AVExpr *pos_x_pexpr, *pos_y_pexpr, *pos_w_pexpr, *pos_h_pexpr; float pad_crop_ratio; + float corner_rounding; int force_original_aspect_ratio; int force_divisible_by; int normalize_sar; @@ -86,10 +192,13 @@ typedef struct LibplaceboContext { int color_range; int color_primaries; int color_trc; + AVDictionary *extra_opts; /* pl_render_params */ + pl_options opts; char *upscaler; char *downscaler; + char *frame_mixer; int lut_entries; float antiringing; int sigmoid; @@ -97,7 +206,6 @@ typedef struct LibplaceboContext { float polar_cutoff; int disable_linear; int disable_builtin; - int force_icc_lut; int force_dither; int disable_fbos; @@ -121,24 +229,18 @@ typedef struct LibplaceboContext { float min_peak; float scene_low; float scene_high; - float overshoot; + float percentile; /* pl_color_map_params */ - int intent; int gamut_mode; int tonemapping; float tonemapping_param; - int tonemapping_mode; int inverse_tonemapping; - float crosstalk; int tonemapping_lut_size; - /* for backwards compatibility */ - float desat_str; - float desat_exp; - int gamut_warning; - int gamut_clipping; + float contrast_recovery; + float contrast_smoothness; - /* pl_dither_params */ + /* pl_dither_params */ int dithering; int dither_lut_size; int dither_temporal; @@ -184,34 +286,69 @@ static void pl_av_log(void *log_ctx, enum pl_log_level level, const char *msg) av_log(log_ctx, av_lev, "%s\n", msg); } -static int parse_shader(AVFilterContext *avctx, const void *shader, size_t len) -{ - LibplaceboContext *s = avctx->priv; - const struct pl_hook *hook; +static const struct pl_tone_map_function *get_tonemapping_func(int tm) { + switch (tm) { + case TONE_MAP_AUTO: return &pl_tone_map_auto; + case TONE_MAP_CLIP: return &pl_tone_map_clip; +#if PL_API_VER >= 246 + case TONE_MAP_ST2094_40: return &pl_tone_map_st2094_40; + case TONE_MAP_ST2094_10: return &pl_tone_map_st2094_10; +#endif + case TONE_MAP_BT2390: return &pl_tone_map_bt2390; + case TONE_MAP_BT2446A: return &pl_tone_map_bt2446a; + case TONE_MAP_SPLINE: return &pl_tone_map_spline; + case TONE_MAP_REINHARD: return &pl_tone_map_reinhard; + case TONE_MAP_MOBIUS: return &pl_tone_map_mobius; + case TONE_MAP_HABLE: return &pl_tone_map_hable; + case TONE_MAP_GAMMA: return &pl_tone_map_gamma; + case TONE_MAP_LINEAR: return &pl_tone_map_linear; + default: av_assert0(0); + } +} - hook = pl_mpv_user_shader_parse(s->gpu, shader, len); - if (!hook) { - av_log(s, AV_LOG_ERROR, "Failed parsing custom shader!\n"); - return AVERROR(EINVAL); +static void set_gamut_mode(struct pl_color_map_params *p, int gamut_mode) +{ + switch (gamut_mode) { +#if PL_API_VER >= 269 + case GAMUT_MAP_CLIP: p->gamut_mapping = &pl_gamut_map_clip; return; + case GAMUT_MAP_PERCEPTUAL: p->gamut_mapping = &pl_gamut_map_perceptual; return; + case GAMUT_MAP_RELATIVE: p->gamut_mapping = &pl_gamut_map_relative; return; + case GAMUT_MAP_SATURATION: p->gamut_mapping = &pl_gamut_map_saturation; return; + case GAMUT_MAP_ABSOLUTE: p->gamut_mapping = &pl_gamut_map_absolute; return; + case GAMUT_MAP_DESATURATE: p->gamut_mapping = &pl_gamut_map_desaturate; return; + case GAMUT_MAP_DARKEN: p->gamut_mapping = &pl_gamut_map_darken; return; + case GAMUT_MAP_HIGHLIGHT: p->gamut_mapping = &pl_gamut_map_highlight; return; + case GAMUT_MAP_LINEAR: p->gamut_mapping = &pl_gamut_map_linear; return; +#else + case GAMUT_MAP_RELATIVE: p->intent = PL_INTENT_RELATIVE_COLORIMETRIC; return; + case GAMUT_MAP_SATURATION: p->intent = PL_INTENT_SATURATION; return; + case GAMUT_MAP_ABSOLUTE: p->intent = PL_INTENT_ABSOLUTE_COLORIMETRIC; return; + case GAMUT_MAP_DESATURATE: p->gamut_mode = PL_GAMUT_DESATURATE; return; + case GAMUT_MAP_DARKEN: p->gamut_mode = PL_GAMUT_DARKEN; return; + case GAMUT_MAP_HIGHLIGHT: p->gamut_mode = PL_GAMUT_WARN; return; + /* Use defaults for all other cases */ + default: return; +#endif } - s->hooks[s->num_hooks++] = hook; - return 0; -} + av_assert0(0); +}; static int find_scaler(AVFilterContext *avctx, const struct pl_filter_config **opt, - const char *name) + const char *name, int frame_mixing) { - const struct pl_filter_preset *preset; + const struct pl_filter_preset *preset, *presets_avail; + presets_avail = frame_mixing ? pl_frame_mixers : pl_scale_filters; + if (!strcmp(name, "help")) { av_log(avctx, AV_LOG_INFO, "Available scaler presets:\n"); - for (preset = pl_scale_filters; preset->name; preset++) + for (preset = presets_avail; preset->name; preset++) av_log(avctx, AV_LOG_INFO, " %s\n", preset->name); return AVERROR_EXIT; } - for (preset = pl_scale_filters; preset->name; preset++) { + for (preset = presets_avail; preset->name; preset++) { if (!strcmp(name, preset->name)) { *opt = preset->filter; return 0; @@ -222,10 +359,144 @@ static int find_scaler(AVFilterContext *avctx, return AVERROR(EINVAL); } +static int update_settings(AVFilterContext *ctx) +{ + int err = 0; + LibplaceboContext *s = ctx->priv; + AVDictionaryEntry *e = NULL; + pl_options opts = s->opts; + int gamut_mode = s->gamut_mode; + uint8_t color_rgba[4]; + + RET(av_parse_color(color_rgba, s->fillcolor, -1, s)); + + opts->deband_params = *pl_deband_params( + .iterations = s->deband_iterations, + .threshold = s->deband_threshold, + .radius = s->deband_radius, + .grain = s->deband_grain, + ); + + opts->sigmoid_params = pl_sigmoid_default_params; + + opts->color_adjustment = (struct pl_color_adjustment) { + .brightness = s->brightness, + .contrast = s->contrast, + .saturation = s->saturation, + .hue = s->hue, + .gamma = s->gamma, + }; + + opts->peak_detect_params = *pl_peak_detect_params( + .smoothing_period = s->smoothing, + .minimum_peak = s->min_peak, + .scene_threshold_low = s->scene_low, + .scene_threshold_high = s->scene_high, +#if PL_API_VER >= 263 + .percentile = s->percentile, +#endif + ); + + opts->color_map_params = *pl_color_map_params( + .tone_mapping_function = get_tonemapping_func(s->tonemapping), + .tone_mapping_param = s->tonemapping_param, + .inverse_tone_mapping = s->inverse_tonemapping, + .lut_size = s->tonemapping_lut_size, +#if PL_API_VER >= 285 + .contrast_recovery = s->contrast_recovery, + .contrast_smoothness = s->contrast_smoothness, +#endif + ); + + set_gamut_mode(&opts->color_map_params, gamut_mode); + + opts->dither_params = *pl_dither_params( + .method = s->dithering, + .lut_size = s->dither_lut_size, + .temporal = s->dither_temporal, + ); + + opts->cone_params = *pl_cone_params( + .cones = s->cones, + .strength = s->cone_str, + ); + + opts->params = *pl_render_params( + .lut_entries = s->lut_entries, + .antiringing_strength = s->antiringing, + .background_transparency = 1.0f - (float) color_rgba[3] / UINT8_MAX, + .background_color = { + (float) color_rgba[0] / UINT8_MAX, + (float) color_rgba[1] / UINT8_MAX, + (float) color_rgba[2] / UINT8_MAX, + }, +#if PL_API_VER >= 277 + .corner_rounding = s->corner_rounding, +#endif + + .deband_params = s->deband ? &opts->deband_params : NULL, + .sigmoid_params = s->sigmoid ? &opts->sigmoid_params : NULL, + .color_adjustment = &opts->color_adjustment, + .peak_detect_params = s->peakdetect ? &opts->peak_detect_params : NULL, + .color_map_params = &opts->color_map_params, + .dither_params = s->dithering >= 0 ? &opts->dither_params : NULL, + .cone_params = s->cones ? &opts->cone_params : NULL, + + .hooks = s->hooks, + .num_hooks = s->num_hooks, + + .skip_anti_aliasing = s->skip_aa, + .polar_cutoff = s->polar_cutoff, + .disable_linear_scaling = s->disable_linear, + .disable_builtin_scalers = s->disable_builtin, + .force_dither = s->force_dither, + .disable_fbos = s->disable_fbos, + ); + + RET(find_scaler(ctx, &opts->params.upscaler, s->upscaler, 0)); + RET(find_scaler(ctx, &opts->params.downscaler, s->downscaler, 0)); + RET(find_scaler(ctx, &opts->params.frame_mixer, s->frame_mixer, 1)); + +#if PL_API_VER >= 309 + while ((e = av_dict_get(s->extra_opts, "", e, AV_DICT_IGNORE_SUFFIX))) { + if (!pl_options_set_str(s->opts, e->key, e->value)) { + err = AVERROR(EINVAL); + goto fail; + } + } +#else + (void) e; + if (av_dict_count(s->extra_opts) > 0) + av_log(s, AV_LOG_WARNING, "extra_opts requires libplacebo >= 6.309!\n"); +#endif + + return 0; + +fail: + return err; +} + +static int parse_shader(AVFilterContext *avctx, const void *shader, size_t len) +{ + LibplaceboContext *s = avctx->priv; + const struct pl_hook *hook; + + hook = pl_mpv_user_shader_parse(s->gpu, shader, len); + if (!hook) { + av_log(s, AV_LOG_ERROR, "Failed parsing custom shader!\n"); + return AVERROR(EINVAL); + } + + s->hooks[s->num_hooks++] = hook; + return update_settings(avctx); +} + static void libplacebo_uninit(AVFilterContext *avctx); +static int libplacebo_config_input(AVFilterLink *inlink); static int libplacebo_init(AVFilterContext *avctx) { + int err = 0; LibplaceboContext *s = avctx->priv; /* Create libplacebo log context */ @@ -238,6 +509,12 @@ static int libplacebo_init(AVFilterContext *avctx) if (!s->log) return AVERROR(ENOMEM); + s->opts = pl_options_alloc(s->log); + if (!s->opts) { + libplacebo_uninit(avctx); + return AVERROR(ENOMEM); + } + if (s->out_format_string) { s->out_format = av_get_pix_fmt(s->out_format_string); if (s->out_format == AV_PIX_FMT_NONE) { @@ -250,67 +527,141 @@ static int libplacebo_init(AVFilterContext *avctx) s->out_format = AV_PIX_FMT_NONE; } + for (int i = 0; i < s->nb_inputs; i++) { + AVFilterPad pad = { + .name = av_asprintf("input%d", i), + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &libplacebo_config_input, + }; + if (!pad.name) + return AVERROR(ENOMEM); + RET(ff_append_inpad_free_name(avctx, &pad)); + } + + RET(update_settings(avctx)); + RET(av_expr_parse(&s->crop_x_pexpr, s->crop_x_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->crop_y_pexpr, s->crop_y_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->crop_w_pexpr, s->crop_w_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->crop_h_pexpr, s->crop_h_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->pos_x_pexpr, s->pos_x_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->pos_y_pexpr, s->pos_y_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->pos_w_pexpr, s->pos_w_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + RET(av_expr_parse(&s->pos_h_pexpr, s->pos_h_expr, var_names, + NULL, NULL, NULL, NULL, 0, s)); + + if (strcmp(s->fps_string, "none") != 0) + RET(av_parse_video_rate(&s->fps, s->fps_string)); + /* Note: s->vulkan etc. are initialized later, when hwctx is available */ return 0; + +fail: + return err; } -static int init_vulkan(AVFilterContext *avctx) +#if PL_API_VER >= 278 +static void lock_queue(void *priv, uint32_t qf, uint32_t qidx) +{ + AVHWDeviceContext *avhwctx = priv; + const AVVulkanDeviceContext *hwctx = avhwctx->hwctx; + hwctx->lock_queue(avhwctx, qf, qidx); +} + +static void unlock_queue(void *priv, uint32_t qf, uint32_t qidx) +{ + AVHWDeviceContext *avhwctx = priv; + const AVVulkanDeviceContext *hwctx = avhwctx->hwctx; + hwctx->unlock_queue(avhwctx, qf, qidx); +} +#endif + +static int input_init(AVFilterContext *avctx, AVFilterLink *link, + LibplaceboInput *input, int idx) +{ + LibplaceboContext *s = avctx->priv; + + input->out_pts = av_fifo_alloc2(1, sizeof(int64_t), AV_FIFO_FLAG_AUTO_GROW); + if (!input->out_pts) + return AVERROR(ENOMEM); + input->queue = pl_queue_create(s->gpu); + input->renderer = pl_renderer_create(s->log, s->gpu); + input->link = link; + input->idx = idx; + + return 0; +} + +static void input_uninit(LibplaceboInput *input) +{ + pl_renderer_destroy(&input->renderer); + pl_queue_destroy(&input->queue); + av_fifo_freep2(&input->out_pts); +} + +static int init_vulkan(AVFilterContext *avctx, const AVVulkanDeviceContext *hwctx) { int err = 0; LibplaceboContext *s = avctx->priv; - const AVHWDeviceContext *avhwctx; - const AVVulkanDeviceContext *hwctx; uint8_t *buf = NULL; size_t buf_len; - if (!avctx->hw_device_ctx) { - av_log(s, AV_LOG_ERROR, "Missing vulkan hwdevice for vf_libplacebo.\n"); - return AVERROR(EINVAL); - } - - 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)); - return AVERROR(EINVAL); + if (hwctx) { +#if PL_API_VER >= 278 + /* Import libavfilter vulkan context into libplacebo */ + s->vulkan = pl_vulkan_import(s->log, pl_vulkan_import_params( + .instance = hwctx->inst, + .get_proc_addr = hwctx->get_proc_addr, + .phys_device = hwctx->phys_dev, + .device = hwctx->act_dev, + .extensions = hwctx->enabled_dev_extensions, + .num_extensions = hwctx->nb_enabled_dev_extensions, + .features = &hwctx->device_features, + .lock_queue = lock_queue, + .unlock_queue = unlock_queue, + .queue_ctx = avctx->hw_device_ctx->data, + .queue_graphics = { + .index = hwctx->queue_family_index, + .count = hwctx->nb_graphics_queues, + }, + .queue_compute = { + .index = hwctx->queue_family_comp_index, + .count = hwctx->nb_comp_queues, + }, + .queue_transfer = { + .index = hwctx->queue_family_tx_index, + .count = hwctx->nb_tx_queues, + }, + /* This is the highest version created by hwcontext_vulkan.c */ + .max_api_version = VK_API_VERSION_1_3, + )); +#else + av_log(s, AV_LOG_ERROR, "libplacebo version %s too old to import " + "Vulkan device, remove it or upgrade libplacebo to >= 5.278\n", + PL_VERSION); + err = AVERROR_EXTERNAL; + goto fail; +#endif + } else { + s->vulkan = pl_vulkan_create(s->log, pl_vulkan_params( + .queue_count = 0, /* enable all queues for parallelization */ + )); } - hwctx = avhwctx->hwctx; - - /* Import libavfilter vulkan context into libplacebo */ - s->vulkan = pl_vulkan_import(s->log, pl_vulkan_import_params( - .instance = hwctx->inst, - .get_proc_addr = hwctx->get_proc_addr, - .phys_device = hwctx->phys_dev, - .device = hwctx->act_dev, - .extensions = hwctx->enabled_dev_extensions, - .num_extensions = hwctx->nb_enabled_dev_extensions, - .features = &hwctx->device_features, - .queue_graphics = { - .index = hwctx->queue_family_index, - .count = hwctx->nb_graphics_queues, - }, - .queue_compute = { - .index = hwctx->queue_family_comp_index, - .count = hwctx->nb_comp_queues, - }, - .queue_transfer = { - .index = hwctx->queue_family_tx_index, - .count = hwctx->nb_tx_queues, - }, - /* This is the highest version created by hwcontext_vulkan.c */ - .max_api_version = VK_API_VERSION_1_2, - )); - if (!s->vulkan) { - av_log(s, AV_LOG_ERROR, "Failed importing vulkan device to libplacebo!\n"); + av_log(s, AV_LOG_ERROR, "Failed %s Vulkan device!\n", + hwctx ? "importing" : "creating"); err = AVERROR_EXTERNAL; goto fail; } - /* Create the renderer */ s->gpu = s->vulkan->gpu; - s->renderer = pl_renderer_create(s->log, s->gpu); /* Parse the user shaders, if requested */ if (s->shader_bin_len) @@ -321,6 +672,13 @@ static int init_vulkan(AVFilterContext *avctx) RET(parse_shader(avctx, buf, buf_len)); } + /* Initialize inputs */ + s->inputs = av_calloc(s->nb_inputs, sizeof(*s->inputs)); + if (!s->inputs) + return AVERROR(ENOMEM); + for (int i = 0; i < s->nb_inputs; i++) + RET(input_init(avctx, avctx->inputs[i], &s->inputs[i], i)); + /* fall through */ fail: if (buf) @@ -336,201 +694,170 @@ static void libplacebo_uninit(AVFilterContext *avctx) pl_tex_destroy(s->gpu, &s->tex[i]); for (int i = 0; i < s->num_hooks; i++) pl_mpv_user_shader_destroy(&s->hooks[i]); - pl_renderer_destroy(&s->renderer); + if (s->inputs) { + for (int i = 0; i < s->nb_inputs; i++) + input_uninit(&s->inputs[i]); + av_freep(&s->inputs); + } + + pl_options_free(&s->opts); pl_vulkan_destroy(&s->vulkan); pl_log_destroy(&s->log); ff_vk_uninit(&s->vkctx); s->gpu = NULL; + + av_expr_free(s->crop_x_pexpr); + av_expr_free(s->crop_y_pexpr); + av_expr_free(s->crop_w_pexpr); + av_expr_free(s->crop_h_pexpr); + av_expr_free(s->pos_x_pexpr); + av_expr_free(s->pos_y_pexpr); + av_expr_free(s->pos_w_pexpr); + av_expr_free(s->pos_h_pexpr); } -static int process_frames(AVFilterContext *avctx, AVFrame *out, AVFrame *in) +static int libplacebo_process_command(AVFilterContext *ctx, const char *cmd, + const char *arg, char *res, int res_len, + int flags) { - int err = 0, ok; - LibplaceboContext *s = avctx->priv; - struct pl_render_params params; - enum pl_tone_map_mode tonemapping_mode = s->tonemapping_mode; - const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(out->format); - enum pl_gamut_mode gamut_mode = s->gamut_mode; - struct pl_frame image, target; - ok = pl_map_avframe_ex(s->gpu, &image, pl_avframe_params( - .frame = in, - .tex = s->tex, - .map_dovi = s->apply_dovi, - )); - - if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) { - ok &= pl_map_avframe_ex(s->gpu, &target, pl_avframe_params( - .frame = out, - .map_dovi = false, - )); - } else { - ok &= pl_frame_recreate_from_avframe(s->gpu, &target, s->tex + 4, out); - } - - if (!ok) { - err = AVERROR_EXTERNAL; - goto fail; - } + int err = 0; + RET(ff_filter_process_command(ctx, cmd, arg, res, res_len, flags)); + RET(update_settings(ctx)); + return 0; - if (!s->apply_filmgrain) - image.film_grain.type = PL_FILM_GRAIN_NONE; +fail: + return err; +} - if (s->target_sar.num) { - float aspect = pl_rect2df_aspect(&target.crop) * av_q2d(s->target_sar); - pl_rect2df_aspect_set(&target.crop, aspect, s->pad_crop_ratio); +static const AVFrame *ref_frame(const struct pl_frame_mix *mix) +{ + for (int i = 0; i < mix->num_frames; i++) { + if (i+1 == mix->num_frames || mix->timestamps[i+1] > 0) + return pl_get_mapped_avframe(mix->frames[i]); } + return NULL; +} - /* backwards compatibility with older API */ - if (!tonemapping_mode && (s->desat_str >= 0.0f || s->desat_exp >= 0.0f)) { - float str = s->desat_str < 0.0f ? 0.9f : s->desat_str; - float exp = s->desat_exp < 0.0f ? 0.2f : s->desat_exp; - if (str >= 0.9f && exp <= 0.1f) { - tonemapping_mode = PL_TONE_MAP_RGB; - } else if (str > 0.1f) { - tonemapping_mode = PL_TONE_MAP_HYBRID; - } else { - tonemapping_mode = PL_TONE_MAP_LUMA; +static void update_crops(AVFilterContext *ctx, LibplaceboInput *in, + struct pl_frame *target, double target_pts) +{ + LibplaceboContext *s = ctx->priv; + const AVFrame *ref = ref_frame(&in->mix); + + for (int i = 0; i < in->mix.num_frames; i++) { + // Mutate the `pl_frame.crop` fields in-place. This is fine because we + // own the entire pl_queue, and hence, the pointed-at frames. + struct pl_frame *image = (struct pl_frame *) in->mix.frames[i]; + const AVFrame *src = pl_get_mapped_avframe(image); + double image_pts = src->pts * av_q2d(in->link->time_base); + + /* Update dynamic variables */ + s->var_values[VAR_IN_IDX] = s->var_values[VAR_IDX] = in->idx; + s->var_values[VAR_IN_W] = s->var_values[VAR_IW] = in->link->w; + s->var_values[VAR_IN_H] = s->var_values[VAR_IH] = in->link->h; + s->var_values[VAR_A] = (double) in->link->w / in->link->h; + s->var_values[VAR_SAR] = in->link->sample_aspect_ratio.num ? + av_q2d(in->link->sample_aspect_ratio) : 1.0; + s->var_values[VAR_IN_T] = s->var_values[VAR_T] = image_pts; + s->var_values[VAR_OUT_T] = s->var_values[VAR_OT] = target_pts; + s->var_values[VAR_N] = ctx->outputs[0]->frame_count_out; + + /* Clear these explicitly to avoid leaking previous frames' state */ + s->var_values[VAR_CROP_W] = s->var_values[VAR_CW] = NAN; + s->var_values[VAR_CROP_H] = s->var_values[VAR_CH] = NAN; + s->var_values[VAR_POS_W] = s->var_values[VAR_PW] = NAN; + s->var_values[VAR_POS_H] = s->var_values[VAR_PH] = NAN; + + /* Compute dimensions first and placement second */ + s->var_values[VAR_CROP_W] = s->var_values[VAR_CW] = + av_expr_eval(s->crop_w_pexpr, s->var_values, NULL); + s->var_values[VAR_CROP_H] = s->var_values[VAR_CH] = + av_expr_eval(s->crop_h_pexpr, s->var_values, NULL); + s->var_values[VAR_CROP_W] = s->var_values[VAR_CW] = + av_expr_eval(s->crop_w_pexpr, s->var_values, NULL); + s->var_values[VAR_POS_W] = s->var_values[VAR_PW] = + av_expr_eval(s->pos_w_pexpr, s->var_values, NULL); + s->var_values[VAR_POS_H] = s->var_values[VAR_PH] = + av_expr_eval(s->pos_h_pexpr, s->var_values, NULL); + s->var_values[VAR_POS_W] = s->var_values[VAR_PW] = + av_expr_eval(s->pos_w_pexpr, s->var_values, NULL); + + image->crop.x0 = av_expr_eval(s->crop_x_pexpr, s->var_values, NULL); + image->crop.y0 = av_expr_eval(s->crop_y_pexpr, s->var_values, NULL); + image->crop.x1 = image->crop.x0 + s->var_values[VAR_CROP_W]; + image->crop.y1 = image->crop.y0 + s->var_values[VAR_CROP_H]; + + if (src == ref) { + /* Only update the target crop once, for the 'reference' frame */ + target->crop.x0 = av_expr_eval(s->pos_x_pexpr, s->var_values, NULL); + target->crop.y0 = av_expr_eval(s->pos_y_pexpr, s->var_values, NULL); + target->crop.x1 = target->crop.x0 + s->var_values[VAR_POS_W]; + target->crop.y1 = target->crop.y0 + s->var_values[VAR_POS_H]; + if (s->normalize_sar) { + float aspect = pl_rect2df_aspect(&image->crop); + aspect *= av_q2d(in->link->sample_aspect_ratio); + pl_rect2df_aspect_set(&target->crop, aspect, s->pad_crop_ratio); + } } } - - if (s->gamut_warning) - gamut_mode = PL_GAMUT_WARN; - if (s->gamut_clipping) - gamut_mode = PL_GAMUT_DESATURATE; - - /* Update render params */ - params = (struct pl_render_params) { - PL_RENDER_DEFAULTS - .lut_entries = s->lut_entries, - .antiringing_strength = s->antiringing, - - .deband_params = !s->deband ? NULL : pl_deband_params( - .iterations = s->deband_iterations, - .threshold = s->deband_threshold, - .radius = s->deband_radius, - .grain = s->deband_grain, - ), - - .sigmoid_params = s->sigmoid ? &pl_sigmoid_default_params : NULL, - - .color_adjustment = &(struct pl_color_adjustment) { - .brightness = s->brightness, - .contrast = s->contrast, - .saturation = s->saturation, - .hue = s->hue, - .gamma = s->gamma, - }, - - .peak_detect_params = !s->peakdetect ? NULL : pl_peak_detect_params( - .smoothing_period = s->smoothing, - .minimum_peak = s->min_peak, - .scene_threshold_low = s->scene_low, - .scene_threshold_high = s->scene_high, - .overshoot_margin = s->overshoot, - ), - - .color_map_params = pl_color_map_params( - .intent = s->intent, - .gamut_mode = gamut_mode, - .tone_mapping_function = tonemapping_funcs[s->tonemapping], - .tone_mapping_param = s->tonemapping_param, - .tone_mapping_mode = tonemapping_mode, - .inverse_tone_mapping = s->inverse_tonemapping, - .tone_mapping_crosstalk = s->crosstalk, - .lut_size = s->tonemapping_lut_size, - ), - - .dither_params = s->dithering < 0 ? NULL : pl_dither_params( - .method = s->dithering, - .lut_size = s->dither_lut_size, - .temporal = s->dither_temporal, - ), - - .cone_params = !s->cones ? NULL : pl_cone_params( - .cones = s->cones, - .strength = s->cone_str, - ), - - .hooks = s->hooks, - .num_hooks = s->num_hooks, - - .skip_anti_aliasing = s->skip_aa, - .polar_cutoff = s->polar_cutoff, - .disable_linear_scaling = s->disable_linear, - .disable_builtin_scalers = s->disable_builtin, - .force_icc_lut = s->force_icc_lut, - .force_dither = s->force_dither, - .disable_fbos = s->disable_fbos, - }; - - RET(find_scaler(avctx, ¶ms.upscaler, s->upscaler)); - RET(find_scaler(avctx, ¶ms.downscaler, s->downscaler)); - - pl_render_image(s->renderer, &image, &target, ¶ms); - pl_unmap_avframe(s->gpu, &image); - - if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) { - pl_unmap_avframe(s->gpu, &target); - } else if (!pl_download_avframe(s->gpu, &target, out)) { - err = AVERROR_EXTERNAL; - goto fail; - } - - /* Flush the command queues for performance */ - pl_gpu_flush(s->gpu); - return 0; - -fail: - pl_unmap_avframe(s->gpu, &image); - pl_unmap_avframe(s->gpu, &target); - return err; } -static int filter_frame(AVFilterLink *link, AVFrame *in) +/* Construct and emit an output frame for a given timestamp */ +static int output_frame(AVFilterContext *ctx, int64_t pts) { - int err, changed_csp; - AVFilterContext *ctx = link->dst; + int err = 0, ok, changed_csp; LibplaceboContext *s = ctx->priv; + pl_options opts = s->opts; AVFilterLink *outlink = ctx->outputs[0]; - - AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); - if (!out) { - err = AVERROR(ENOMEM); - goto fail; + const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(outlink->format); + struct pl_frame target; + const AVFrame *ref = NULL; + AVFrame *out; + + /* Use the first active input as metadata reference */ + for (int i = 0; i < s->nb_inputs; i++) { + const LibplaceboInput *in = &s->inputs[i]; + if (in->qstatus == PL_QUEUE_OK && (ref = ref_frame(&in->mix))) + break; } + if (!ref) + return 0; - pl_log_level_update(s->log, get_log_level()); + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) + return AVERROR(ENOMEM); - RET(av_frame_copy_props(out, in)); + RET(av_frame_copy_props(out, ref)); + out->pts = pts; out->width = outlink->w; out->height = outlink->h; + out->colorspace = outlink->colorspace; + out->color_range = outlink->color_range; + if (s->fps.num) + out->duration = 1; - if (s->apply_dovi && av_frame_get_side_data(in, AV_FRAME_DATA_DOVI_METADATA)) { + if (s->apply_dovi && av_frame_get_side_data(ref, AV_FRAME_DATA_DOVI_METADATA)) { /* Output of dovi reshaping is always BT.2020+PQ, so infer the correct * output colorspace defaults */ - out->colorspace = AVCOL_SPC_BT2020_NCL; out->color_primaries = AVCOL_PRI_BT2020; out->color_trc = AVCOL_TRC_SMPTE2084; } - if (s->colorspace >= 0) - out->colorspace = s->colorspace; - if (s->color_range >= 0) - out->color_range = s->color_range; if (s->color_trc >= 0) out->color_trc = s->color_trc; if (s->color_primaries >= 0) out->color_primaries = s->color_primaries; - changed_csp = in->colorspace != out->colorspace || - in->color_range != out->color_range || - in->color_trc != out->color_trc || - in->color_primaries != out->color_primaries; + changed_csp = ref->colorspace != out->colorspace || + ref->color_range != out->color_range || + ref->color_trc != out->color_trc || + ref->color_primaries != out->color_primaries; /* Strip side data if no longer relevant */ if (changed_csp) { av_frame_remove_side_data(out, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); av_frame_remove_side_data(out, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + av_frame_remove_side_data(out, AV_FRAME_DATA_ICC_PROFILE); } if (s->apply_dovi || changed_csp) { av_frame_remove_side_data(out, AV_FRAME_DATA_DOVI_RPU_BUFFER); @@ -539,26 +866,227 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) if (s->apply_filmgrain) av_frame_remove_side_data(out, AV_FRAME_DATA_FILM_GRAIN_PARAMS); - RET(process_frames(ctx, out, in)); + /* Map, render and unmap output frame */ + if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) { + ok = pl_map_avframe_ex(s->gpu, &target, pl_avframe_params( + .frame = out, + .map_dovi = false, + )); + } else { + ok = pl_frame_recreate_from_avframe(s->gpu, &target, s->tex, out); + } + if (!ok) { + err = AVERROR_EXTERNAL; + goto fail; + } - av_frame_free(&in); + /* Draw first frame opaque, others with blending */ + opts->params.skip_target_clearing = false; + opts->params.blend_params = NULL; + for (int i = 0; i < s->nb_inputs; i++) { + LibplaceboInput *in = &s->inputs[i]; + int high_fps = av_cmp_q(in->link->frame_rate, outlink->frame_rate) >= 0; + if (in->qstatus != PL_QUEUE_OK) + continue; + opts->params.skip_caching_single_frame = high_fps; + update_crops(ctx, in, &target, out->pts * av_q2d(outlink->time_base)); + pl_render_image_mix(in->renderer, &in->mix, &target, &opts->params); + opts->params.skip_target_clearing = true; + opts->params.blend_params = &pl_alpha_overlay; + } + if (outdesc->flags & AV_PIX_FMT_FLAG_HWACCEL) { + pl_unmap_avframe(s->gpu, &target); + } else if (!pl_download_avframe(s->gpu, &target, out)) { + err = AVERROR_EXTERNAL; + goto fail; + } return ff_filter_frame(outlink, out); fail: - av_frame_free(&in); av_frame_free(&out); return err; } +static bool map_frame(pl_gpu gpu, pl_tex *tex, + const struct pl_source_frame *src, + struct pl_frame *out) +{ + AVFrame *avframe = src->frame_data; + LibplaceboContext *s = avframe->opaque; + bool ok = pl_map_avframe_ex(gpu, out, pl_avframe_params( + .frame = avframe, + .tex = tex, + .map_dovi = s->apply_dovi, + )); + + if (!s->apply_filmgrain) + out->film_grain.type = PL_FILM_GRAIN_NONE; + + av_frame_free(&avframe); + return ok; +} + +static void unmap_frame(pl_gpu gpu, struct pl_frame *frame, + const struct pl_source_frame *src) +{ + pl_unmap_avframe(gpu, frame); +} + +static void discard_frame(const struct pl_source_frame *src) +{ + AVFrame *avframe = src->frame_data; + av_frame_free(&avframe); +} + +static int handle_input(AVFilterContext *ctx, LibplaceboInput *input) +{ + int ret, status; + LibplaceboContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + AVFrame *in; + int64_t pts; + + while ((ret = ff_inlink_consume_frame(input->link, &in)) > 0) { + in->opaque = s; + pl_queue_push(input->queue, &(struct pl_source_frame) { + .pts = in->pts * av_q2d(input->link->time_base), + .duration = in->duration * av_q2d(input->link->time_base), + .first_field = pl_field_from_avframe(in), + .frame_data = in, + .map = map_frame, + .unmap = unmap_frame, + .discard = discard_frame, + }); + + if (!s->fps.num) { + /* Internally queue an output frame for the same PTS */ + pts = av_rescale_q(in->pts, input->link->time_base, outlink->time_base); + av_fifo_write(input->out_pts, &pts, 1); + } + } + + if (ret < 0) + return ret; + + if (!input->status && ff_inlink_acknowledge_status(input->link, &status, &pts)) { + pts = av_rescale_q_rnd(pts, input->link->time_base, outlink->time_base, + AV_ROUND_UP); + pl_queue_push(input->queue, NULL); /* Signal EOF to pl_queue */ + input->status = status; + input->status_pts = pts; + if (!s->status || pts >= s->status_pts) { + /* Also propagate to output unless overwritten by later status change */ + s->status = status; + s->status_pts = pts; + } + } + + return 0; +} + +static void drain_input_pts(LibplaceboInput *in, int64_t until) +{ + int64_t pts; + while (av_fifo_peek(in->out_pts, &pts, 1, 0) >= 0 && pts <= until) + av_fifo_drain2(in->out_pts, 1); +} + +static int libplacebo_activate(AVFilterContext *ctx) +{ + int ret, ok = 0, retry = 0; + LibplaceboContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + int64_t pts, out_pts; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); + pl_log_level_update(s->log, get_log_level()); + + for (int i = 0; i < s->nb_inputs; i++) { + if ((ret = handle_input(ctx, &s->inputs[i])) < 0) + return ret; + } + + if (ff_outlink_frame_wanted(outlink)) { + if (s->fps.num) { + out_pts = outlink->frame_count_out; + } else { + /* Determine the PTS of the next frame from any active input */ + out_pts = INT64_MAX; + for (int i = 0; i < s->nb_inputs; i++) { + LibplaceboInput *in = &s->inputs[i]; + if (av_fifo_peek(in->out_pts, &pts, 1, 0) >= 0) { + out_pts = FFMIN(out_pts, pts); + } else if (!in->status) { + ff_inlink_request_frame(in->link); + retry = true; + } + } + + if (retry) /* some inputs are incomplete */ + return 0; + } + + /* Update all input queues to the chosen out_pts */ + for (int i = 0; i < s->nb_inputs; i++) { + LibplaceboInput *in = &s->inputs[i]; + if (in->status && out_pts >= in->status_pts) { + in->qstatus = PL_QUEUE_EOF; + continue; + } + + in->qstatus = pl_queue_update(in->queue, &in->mix, pl_queue_params( + .pts = out_pts * av_q2d(outlink->time_base), + .radius = pl_frame_mix_radius(&s->opts->params), + .vsync_duration = av_q2d(av_inv_q(outlink->frame_rate)), + )); + + switch (in->qstatus) { + case PL_QUEUE_MORE: + ff_inlink_request_frame(in->link); + retry = true; + break; + case PL_QUEUE_OK: + ok = true; + break; + case PL_QUEUE_ERR: + return AVERROR_EXTERNAL; + } + } + + if (retry) { + return 0; + } else if (ok) { + /* Got any valid frame mixes, drain PTS queue and render output */ + for (int i = 0; i < s->nb_inputs; i++) + drain_input_pts(&s->inputs[i], out_pts); + return output_frame(ctx, out_pts); + } else if (s->status) { + ff_outlink_set_status(outlink, s->status, s->status_pts); + return 0; + } + + return AVERROR_BUG; + } + + return FFERROR_NOT_READY; +} + static int libplacebo_query_format(AVFilterContext *ctx) { int err; LibplaceboContext *s = ctx->priv; + const AVVulkanDeviceContext *vkhwctx = NULL; const AVPixFmtDescriptor *desc = NULL; AVFilterFormats *infmts = NULL, *outfmts = NULL; - RET(init_vulkan(ctx)); + if (ctx->hw_device_ctx) { + const AVHWDeviceContext *avhwctx = (void *) ctx->hw_device_ctx->data; + if (avhwctx->type == AV_HWDEVICE_TYPE_VULKAN) + vkhwctx = avhwctx->hwctx; + } + + RET(init_vulkan(ctx, vkhwctx)); while ((desc = av_pix_fmt_desc_next(desc))) { enum AVPixelFormat pixfmt = av_pix_fmt_desc_get_id(desc); @@ -570,6 +1098,11 @@ static int libplacebo_query_format(AVFilterContext *ctx) continue; #endif + if (pixfmt == AV_PIX_FMT_VULKAN) { + if (!vkhwctx || vkhwctx->act_dev != s->vulkan->device) + continue; + } + if (!pl_test_pixfmt(s->gpu, pixfmt)) continue; @@ -590,6 +1123,11 @@ static int libplacebo_query_format(AVFilterContext *ctx) } } +#if PL_API_VER >= 293 + if (!pl_test_pixfmt_caps(s->gpu, pixfmt, PL_FMT_CAP_RENDERABLE)) + continue; +#endif + RET(ff_add_format(&outfmts, pixfmt)); } @@ -602,8 +1140,21 @@ static int libplacebo_query_format(AVFilterContext *ctx) goto fail; } - RET(ff_formats_ref(infmts, &ctx->inputs[0]->outcfg.formats)); + for (int i = 0; i < s->nb_inputs; i++) + RET(ff_formats_ref(infmts, &ctx->inputs[i]->outcfg.formats)); RET(ff_formats_ref(outfmts, &ctx->outputs[0]->incfg.formats)); + + /* Set colorspace properties */ + RET(ff_formats_ref(ff_all_color_spaces(), &ctx->inputs[0]->outcfg.color_spaces)); + RET(ff_formats_ref(ff_all_color_ranges(), &ctx->inputs[0]->outcfg.color_ranges)); + + outfmts = s->colorspace > 0 ? ff_make_formats_list_singleton(s->colorspace) + : ff_all_color_spaces(); + RET(ff_formats_ref(outfmts, &ctx->outputs[0]->incfg.color_spaces)); + + outfmts = s->color_range > 0 ? ff_make_formats_list_singleton(s->color_range) + : ff_all_color_ranges(); + RET(ff_formats_ref(outfmts, &ctx->outputs[0]->incfg.color_ranges)); return 0; fail: @@ -628,16 +1179,23 @@ static int libplacebo_config_input(AVFilterLink *inlink) return 0; } +static inline AVRational max_q(AVRational a, AVRational b) +{ + return av_cmp_q(a, b) < 0 ? b : a; +} + static int libplacebo_config_output(AVFilterLink *outlink) { int err; AVFilterContext *avctx = outlink->src; LibplaceboContext *s = avctx->priv; AVFilterLink *inlink = outlink->src->inputs[0]; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + const AVPixFmtDescriptor *out_desc = av_pix_fmt_desc_get(outlink->format); AVHWFramesContext *hwfc; AVVulkanFramesContext *vkfc; - AVRational scale_sar; + /* Frame dimensions */ RET(ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink, outlink, &outlink->w, &outlink->h)); @@ -645,22 +1203,43 @@ static int libplacebo_config_output(AVFilterLink *outlink) s->force_original_aspect_ratio, s->force_divisible_by); - scale_sar = (AVRational){outlink->h * inlink->w, outlink->w * inlink->h}; - if (inlink->sample_aspect_ratio.num) - scale_sar = av_mul_q(scale_sar, inlink->sample_aspect_ratio); - - if (s->normalize_sar) { - /* Apply all SAR during scaling, so we don't need to set the out SAR */ + if (s->normalize_sar || s->nb_inputs > 1) { + /* SAR is normalized, or we have multiple inputs, set out to 1:1 */ outlink->sample_aspect_ratio = (AVRational){ 1, 1 }; - s->target_sar = scale_sar; } else { /* This is consistent with other scale_* filters, which only * set the outlink SAR to be equal to the scale SAR iff the input SAR * was set to something nonzero */ if (inlink->sample_aspect_ratio.num) - outlink->sample_aspect_ratio = scale_sar; + outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; + } + + /* Frame rate */ + if (s->fps.num) { + outlink->frame_rate = s->fps; + outlink->time_base = av_inv_q(s->fps); + } else { + outlink->frame_rate = avctx->inputs[0]->frame_rate; + outlink->time_base = avctx->inputs[0]->time_base; + for (int i = 1; i < s->nb_inputs; i++) { + outlink->frame_rate = max_q(outlink->frame_rate, + avctx->inputs[i]->frame_rate); + outlink->time_base = av_gcd_q(outlink->time_base, + avctx->inputs[i]->time_base, + AV_TIME_BASE / 2, AV_TIME_BASE_Q); + } } + /* Static variables */ + s->var_values[VAR_OUT_W] = s->var_values[VAR_OW] = outlink->w; + s->var_values[VAR_OUT_H] = s->var_values[VAR_OH] = outlink->h; + s->var_values[VAR_DAR] = outlink->sample_aspect_ratio.num ? + av_q2d(outlink->sample_aspect_ratio) : 1.0; + s->var_values[VAR_HSUB] = 1 << desc->log2_chroma_w; + s->var_values[VAR_VSUB] = 1 << desc->log2_chroma_h; + s->var_values[VAR_OHSUB] = 1 << out_desc->log2_chroma_w; + s->var_values[VAR_OVSUB] = 1 << out_desc->log2_chroma_h; + if (outlink->format != AV_PIX_FMT_VULKAN) return 0; @@ -688,76 +1267,90 @@ static int libplacebo_config_output(AVFilterLink *outlink) #define DYNAMIC (STATIC | AV_OPT_FLAG_RUNTIME_PARAM) static const AVOption libplacebo_options[] = { - { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = STATIC }, - { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = STATIC }, + { "inputs", "Number of inputs", OFFSET(nb_inputs), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, .flags = STATIC }, + { "w", "Output video frame width", OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = STATIC }, + { "h", "Output video frame height", OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = STATIC }, + { "fps", "Output video frame rate", OFFSET(fps_string), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = STATIC }, + { "crop_x", "Input video crop x", OFFSET(crop_x_expr), AV_OPT_TYPE_STRING, {.str = "(iw-cw)/2"}, .flags = DYNAMIC }, + { "crop_y", "Input video crop y", OFFSET(crop_y_expr), AV_OPT_TYPE_STRING, {.str = "(ih-ch)/2"}, .flags = DYNAMIC }, + { "crop_w", "Input video crop w", OFFSET(crop_w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = DYNAMIC }, + { "crop_h", "Input video crop h", OFFSET(crop_h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = DYNAMIC }, + { "pos_x", "Output video placement x", OFFSET(pos_x_expr), AV_OPT_TYPE_STRING, {.str = "(ow-pw)/2"}, .flags = DYNAMIC }, + { "pos_y", "Output video placement y", OFFSET(pos_y_expr), AV_OPT_TYPE_STRING, {.str = "(oh-ph)/2"}, .flags = DYNAMIC }, + { "pos_w", "Output video placement w", OFFSET(pos_w_expr), AV_OPT_TYPE_STRING, {.str = "ow"}, .flags = DYNAMIC }, + { "pos_h", "Output video placement h", OFFSET(pos_h_expr), AV_OPT_TYPE_STRING, {.str = "oh"}, .flags = DYNAMIC }, { "format", "Output video format", OFFSET(out_format_string), AV_OPT_TYPE_STRING, .flags = STATIC }, - { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, STATIC, "force_oar" }, - { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, STATIC, "force_oar" }, - { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, STATIC, "force_oar" }, - { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, STATIC, "force_oar" }, + { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, STATIC, .unit = "force_oar" }, + { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, STATIC, .unit = "force_oar" }, + { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, STATIC, .unit = "force_oar" }, + { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, STATIC, .unit = "force_oar" }, { "force_divisible_by", "enforce that the output resolution is divisible by a defined integer when force_original_aspect_ratio is used", OFFSET(force_divisible_by), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 256, STATIC }, - { "normalize_sar", "force SAR normalization to 1:1", OFFSET(normalize_sar), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, STATIC }, + { "normalize_sar", "force SAR normalization to 1:1 by adjusting pos_x/y/w/h", OFFSET(normalize_sar), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, STATIC }, { "pad_crop_ratio", "ratio between padding and cropping when normalizing SAR (0=pad, 1=crop)", OFFSET(pad_crop_ratio), AV_OPT_TYPE_FLOAT, {.dbl=0.0}, 0.0, 1.0, DYNAMIC }, - - {"colorspace", "select colorspace", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_SPC_NB-1, DYNAMIC, "colorspace"}, - {"auto", "keep the same colorspace", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, STATIC, "colorspace"}, - {"gbr", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_RGB}, INT_MIN, INT_MAX, STATIC, "colorspace"}, - {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT709}, INT_MIN, INT_MAX, STATIC, "colorspace"}, - {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_UNSPECIFIED}, INT_MIN, INT_MAX, STATIC, "colorspace"}, - {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT470BG}, INT_MIN, INT_MAX, STATIC, "colorspace"}, - {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE170M}, INT_MIN, INT_MAX, STATIC, "colorspace"}, - {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE240M}, INT_MIN, INT_MAX, STATIC, "colorspace"}, - {"ycgco", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_YCGCO}, INT_MIN, INT_MAX, STATIC, "colorspace"}, - {"bt2020nc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_NCL}, INT_MIN, INT_MAX, STATIC, "colorspace"}, - {"bt2020c", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_CL}, INT_MIN, INT_MAX, STATIC, "colorspace"}, - {"ictcp", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_ICTCP}, INT_MIN, INT_MAX, STATIC, "colorspace"}, - - {"range", "select color range", OFFSET(color_range), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_RANGE_NB-1, DYNAMIC, "range"}, - {"auto", "keep the same color range", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, STATIC, "range"}, - {"unspecified", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, STATIC, "range"}, - {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, STATIC, "range"}, - {"limited", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, STATIC, "range"}, - {"tv", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, STATIC, "range"}, - {"mpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, STATIC, "range"}, - {"full", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, STATIC, "range"}, - {"pc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, STATIC, "range"}, - {"jpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, STATIC, "range"}, - - {"color_primaries", "select color primaries", OFFSET(color_primaries), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_PRI_NB-1, DYNAMIC, "color_primaries"}, - {"auto", "keep the same color primaries", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT709}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_UNSPECIFIED}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470M}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470BG}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE170M}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE240M}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - {"film", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_FILM}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - {"bt2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT2020}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - {"smpte428", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE428}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - {"smpte431", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE431}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - {"smpte432", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE432}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - {"jedec-p22", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_JEDEC_P22}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - {"ebu3213", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_EBU3213}, INT_MIN, INT_MAX, STATIC, "color_primaries"}, - - {"color_trc", "select color transfer", OFFSET(color_trc), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_TRC_NB-1, DYNAMIC, "color_trc"}, - {"auto", "keep the same color transfer", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT709}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_UNSPECIFIED}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA22}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA28}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE170M}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE240M}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_LINEAR}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"iec61966-2-4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_4}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"bt1361e", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT1361_ECG}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"iec61966-2-1", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_1}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"bt2020-10", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_10}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"bt2020-12", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_12}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"smpte2084", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE2084}, INT_MIN, INT_MAX, STATIC, "color_trc"}, - {"arib-std-b67", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_ARIB_STD_B67}, INT_MIN, INT_MAX, STATIC, "color_trc"}, + { "fillcolor", "Background fill color", OFFSET(fillcolor), AV_OPT_TYPE_STRING, {.str = "black"}, .flags = DYNAMIC }, + { "corner_rounding", "Corner rounding radius", OFFSET(corner_rounding), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, 0.0, 1.0, .flags = DYNAMIC }, + { "extra_opts", "Pass extra libplacebo-specific options using a :-separated list of key=value pairs", OFFSET(extra_opts), AV_OPT_TYPE_DICT, .flags = DYNAMIC }, + + {"colorspace", "select colorspace", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_SPC_NB-1, DYNAMIC, .unit = "colorspace"}, + {"auto", "keep the same colorspace", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, STATIC, .unit = "colorspace"}, + {"gbr", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_RGB}, INT_MIN, INT_MAX, STATIC, .unit = "colorspace"}, + {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT709}, INT_MIN, INT_MAX, STATIC, .unit = "colorspace"}, + {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_UNSPECIFIED}, INT_MIN, INT_MAX, STATIC, .unit = "colorspace"}, + {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT470BG}, INT_MIN, INT_MAX, STATIC, .unit = "colorspace"}, + {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE170M}, INT_MIN, INT_MAX, STATIC, .unit = "colorspace"}, + {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE240M}, INT_MIN, INT_MAX, STATIC, .unit = "colorspace"}, + {"ycgco", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_YCGCO}, INT_MIN, INT_MAX, STATIC, .unit = "colorspace"}, + {"bt2020nc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_NCL}, INT_MIN, INT_MAX, STATIC, .unit = "colorspace"}, + {"bt2020c", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_CL}, INT_MIN, INT_MAX, STATIC, .unit = "colorspace"}, + {"ictcp", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_ICTCP}, INT_MIN, INT_MAX, STATIC, .unit = "colorspace"}, + + {"range", "select color range", OFFSET(color_range), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_RANGE_NB-1, DYNAMIC, .unit = "range"}, + {"auto", "keep the same color range", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, STATIC, .unit = "range"}, + {"unspecified", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, STATIC, .unit = "range"}, + {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, STATIC, .unit = "range"}, + {"limited", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, STATIC, .unit = "range"}, + {"tv", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, STATIC, .unit = "range"}, + {"mpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, STATIC, .unit = "range"}, + {"full", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, STATIC, .unit = "range"}, + {"pc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, STATIC, .unit = "range"}, + {"jpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, STATIC, .unit = "range"}, + + {"color_primaries", "select color primaries", OFFSET(color_primaries), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_PRI_NB-1, DYNAMIC, .unit = "color_primaries"}, + {"auto", "keep the same color primaries", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT709}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_UNSPECIFIED}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470M}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470BG}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE170M}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE240M}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + {"film", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_FILM}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + {"bt2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT2020}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + {"smpte428", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE428}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + {"smpte431", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE431}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + {"smpte432", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE432}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + {"jedec-p22", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_JEDEC_P22}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + {"ebu3213", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_EBU3213}, INT_MIN, INT_MAX, STATIC, .unit = "color_primaries"}, + + {"color_trc", "select color transfer", OFFSET(color_trc), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_TRC_NB-1, DYNAMIC, .unit = "color_trc"}, + {"auto", "keep the same color transfer", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT709}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_UNSPECIFIED}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA22}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA28}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE170M}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE240M}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_LINEAR}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"iec61966-2-4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_4}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"bt1361e", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT1361_ECG}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"iec61966-2-1", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_1}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"bt2020-10", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_10}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"bt2020-12", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_12}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"smpte2084", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE2084}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, + {"arib-std-b67", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_ARIB_STD_B67}, INT_MIN, INT_MAX, STATIC, .unit = "color_trc"}, { "upscaler", "Upscaler function", OFFSET(upscaler), AV_OPT_TYPE_STRING, {.str = "spline36"}, .flags = DYNAMIC }, { "downscaler", "Downscaler function", OFFSET(downscaler), AV_OPT_TYPE_STRING, {.str = "mitchell"}, .flags = DYNAMIC }, + { "frame_mixer", "Frame mixing function", OFFSET(frame_mixer), AV_OPT_TYPE_STRING, {.str = "none"}, .flags = DYNAMIC }, { "lut_entries", "Number of scaler LUT entries", OFFSET(lut_entries), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 256, DYNAMIC }, { "antiringing", "Antiringing strength (for non-EWA filters)", OFFSET(antiringing), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, 0.0, 1.0, DYNAMIC }, { "sigmoid", "Enable sigmoid upscaling", OFFSET(sigmoid), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, DYNAMIC }, @@ -781,73 +1374,62 @@ static const AVOption libplacebo_options[] = { { "minimum_peak", "Peak detection minimum peak", OFFSET(min_peak), AV_OPT_TYPE_FLOAT, {.dbl = 1.0}, 0.0, 100.0, DYNAMIC }, { "scene_threshold_low", "Scene change low threshold", OFFSET(scene_low), AV_OPT_TYPE_FLOAT, {.dbl = 5.5}, -1.0, 100.0, DYNAMIC }, { "scene_threshold_high", "Scene change high threshold", OFFSET(scene_high), AV_OPT_TYPE_FLOAT, {.dbl = 10.0}, -1.0, 100.0, DYNAMIC }, - { "overshoot", "Tone-mapping overshoot margin", OFFSET(overshoot), AV_OPT_TYPE_FLOAT, {.dbl = 0.05}, 0.0, 1.0, DYNAMIC }, - - { "intent", "Rendering intent", OFFSET(intent), AV_OPT_TYPE_INT, {.i64 = PL_INTENT_RELATIVE_COLORIMETRIC}, 0, 3, DYNAMIC, "intent" }, - { "perceptual", "Perceptual", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_PERCEPTUAL}, 0, 0, STATIC, "intent" }, - { "relative", "Relative colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_RELATIVE_COLORIMETRIC}, 0, 0, STATIC, "intent" }, - { "absolute", "Absolute colorimetric", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_ABSOLUTE_COLORIMETRIC}, 0, 0, STATIC, "intent" }, - { "saturation", "Saturation mapping", 0, AV_OPT_TYPE_CONST, {.i64 = PL_INTENT_SATURATION}, 0, 0, STATIC, "intent" }, - { "gamut_mode", "Gamut-mapping mode", OFFSET(gamut_mode), AV_OPT_TYPE_INT, {.i64 = PL_GAMUT_CLIP}, 0, PL_GAMUT_MODE_COUNT - 1, DYNAMIC, "gamut_mode" }, - { "clip", "Hard-clip gamut boundary", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_CLIP}, 0, 0, STATIC, "gamut_mode" }, - { "warn", "Highlight out-of-gamut colors", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_WARN}, 0, 0, STATIC, "gamut_mode" }, - { "darken", "Darken image to fit gamut", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_DARKEN}, 0, 0, STATIC, "gamut_mode" }, - { "desaturate", "Colorimetrically desaturate colors", 0, AV_OPT_TYPE_CONST, {.i64 = PL_GAMUT_DESATURATE}, 0, 0, STATIC, "gamut_mode" }, - { "tonemapping", "Tone-mapping algorithm", OFFSET(tonemapping), AV_OPT_TYPE_INT, {.i64 = TONE_MAP_AUTO}, 0, TONE_MAP_COUNT - 1, DYNAMIC, "tonemap" }, - { "auto", "Automatic selection", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_AUTO}, 0, 0, STATIC, "tonemap" }, - { "clip", "No tone mapping (clip", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_CLIP}, 0, 0, STATIC, "tonemap" }, + { "percentile", "Peak detection percentile", OFFSET(percentile), AV_OPT_TYPE_FLOAT, {.dbl = 99.995}, 0.0, 100.0, DYNAMIC }, + + { "gamut_mode", "Gamut-mapping mode", OFFSET(gamut_mode), AV_OPT_TYPE_INT, {.i64 = GAMUT_MAP_PERCEPTUAL}, 0, GAMUT_MAP_COUNT - 1, DYNAMIC, .unit = "gamut_mode" }, + { "clip", "Hard-clip (RGB per-channel)", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_CLIP}, 0, 0, STATIC, .unit = "gamut_mode" }, + { "perceptual", "Colorimetric soft clipping", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_PERCEPTUAL}, 0, 0, STATIC, .unit = "gamut_mode" }, + { "relative", "Relative colorimetric clipping", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_RELATIVE}, 0, 0, STATIC, .unit = "gamut_mode" }, + { "saturation", "Saturation mapping (RGB -> RGB)", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_SATURATION}, 0, 0, STATIC, .unit = "gamut_mode" }, + { "absolute", "Absolute colorimetric clipping", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_ABSOLUTE}, 0, 0, STATIC, .unit = "gamut_mode" }, + { "desaturate", "Colorimetrically desaturate colors towards white", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_DESATURATE}, 0, 0, STATIC, .unit = "gamut_mode" }, + { "darken", "Colorimetric clip with bias towards darkening image to fit gamut", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_DARKEN}, 0, 0, STATIC, .unit = "gamut_mode" }, + { "warn", "Highlight out-of-gamut colors", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_HIGHLIGHT}, 0, 0, STATIC, .unit = "gamut_mode" }, + { "linear", "Linearly reduce chromaticity to fit gamut", 0, AV_OPT_TYPE_CONST, {.i64 = GAMUT_MAP_LINEAR}, 0, 0, STATIC, .unit = "gamut_mode" }, + { "tonemapping", "Tone-mapping algorithm", OFFSET(tonemapping), AV_OPT_TYPE_INT, {.i64 = TONE_MAP_AUTO}, 0, TONE_MAP_COUNT - 1, DYNAMIC, .unit = "tonemap" }, + { "auto", "Automatic selection", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_AUTO}, 0, 0, STATIC, .unit = "tonemap" }, + { "clip", "No tone mapping (clip", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_CLIP}, 0, 0, STATIC, .unit = "tonemap" }, #if PL_API_VER >= 246 - { "st2094-40", "SMPTE ST 2094-40", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_ST2094_40}, 0, 0, STATIC, "tonemap" }, - { "st2094-10", "SMPTE ST 2094-10", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_ST2094_10}, 0, 0, STATIC, "tonemap" }, + { "st2094-40", "SMPTE ST 2094-40", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_ST2094_40}, 0, 0, STATIC, .unit = "tonemap" }, + { "st2094-10", "SMPTE ST 2094-10", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_ST2094_10}, 0, 0, STATIC, .unit = "tonemap" }, #endif - { "bt.2390", "ITU-R BT.2390 EETF", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_BT2390}, 0, 0, STATIC, "tonemap" }, - { "bt.2446a", "ITU-R BT.2446 Method A", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_BT2446A}, 0, 0, STATIC, "tonemap" }, - { "spline", "Single-pivot polynomial spline", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_SPLINE}, 0, 0, STATIC, "tonemap" }, - { "reinhard", "Reinhard", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_REINHARD}, 0, 0, STATIC, "tonemap" }, - { "mobius", "Mobius", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_MOBIUS}, 0, 0, STATIC, "tonemap" }, - { "hable", "Filmic tone-mapping (Hable)", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_HABLE}, 0, 0, STATIC, "tonemap" }, - { "gamma", "Gamma function with knee", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_GAMMA}, 0, 0, STATIC, "tonemap" }, - { "linear", "Perceptually linear stretch", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_LINEAR}, 0, 0, STATIC, "tonemap" }, + { "bt.2390", "ITU-R BT.2390 EETF", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_BT2390}, 0, 0, STATIC, .unit = "tonemap" }, + { "bt.2446a", "ITU-R BT.2446 Method A", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_BT2446A}, 0, 0, STATIC, .unit = "tonemap" }, + { "spline", "Single-pivot polynomial spline", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_SPLINE}, 0, 0, STATIC, .unit = "tonemap" }, + { "reinhard", "Reinhard", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_REINHARD}, 0, 0, STATIC, .unit = "tonemap" }, + { "mobius", "Mobius", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_MOBIUS}, 0, 0, STATIC, .unit = "tonemap" }, + { "hable", "Filmic tone-mapping (Hable)", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_HABLE}, 0, 0, STATIC, .unit = "tonemap" }, + { "gamma", "Gamma function with knee", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_GAMMA}, 0, 0, STATIC, .unit = "tonemap" }, + { "linear", "Perceptually linear stretch", 0, AV_OPT_TYPE_CONST, {.i64 = TONE_MAP_LINEAR}, 0, 0, STATIC, .unit = "tonemap" }, { "tonemapping_param", "Tunable parameter for some tone-mapping functions", OFFSET(tonemapping_param), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, 0.0, 100.0, .flags = DYNAMIC }, - { "tonemapping_mode", "Tone-mapping mode", OFFSET(tonemapping_mode), AV_OPT_TYPE_INT, {.i64 = PL_TONE_MAP_AUTO}, 0, PL_TONE_MAP_MODE_COUNT - 1, DYNAMIC, "tonemap_mode" }, - { "auto", "Automatic selection", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_AUTO}, 0, 0, STATIC, "tonemap_mode" }, - { "rgb", "Per-channel (RGB)", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_RGB}, 0, 0, STATIC, "tonemap_mode" }, - { "max", "Maximum component", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_MAX}, 0, 0, STATIC, "tonemap_mode" }, - { "hybrid", "Hybrid of Luma/RGB", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_HYBRID}, 0, 0, STATIC, "tonemap_mode" }, - { "luma", "Luminance", 0, AV_OPT_TYPE_CONST, {.i64 = PL_TONE_MAP_LUMA}, 0, 0, STATIC, "tonemap_mode" }, { "inverse_tonemapping", "Inverse tone mapping (range expansion)", OFFSET(inverse_tonemapping), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, - { "tonemapping_crosstalk", "Crosstalk factor for tone-mapping", OFFSET(crosstalk), AV_OPT_TYPE_FLOAT, {.dbl = 0.04}, 0.0, 0.30, DYNAMIC }, { "tonemapping_lut_size", "Tone-mapping LUT size", OFFSET(tonemapping_lut_size), AV_OPT_TYPE_INT, {.i64 = 256}, 2, 1024, DYNAMIC }, - /* deprecated options for backwards compatibility, defaulting to -1 to not override the new defaults */ - { "desaturation_strength", "Desaturation strength", OFFSET(desat_str), AV_OPT_TYPE_FLOAT, {.dbl = -1.0}, -1.0, 1.0, DYNAMIC | AV_OPT_FLAG_DEPRECATED }, - { "desaturation_exponent", "Desaturation exponent", OFFSET(desat_exp), AV_OPT_TYPE_FLOAT, {.dbl = -1.0}, -1.0, 10.0, DYNAMIC | AV_OPT_FLAG_DEPRECATED }, - { "gamut_warning", "Highlight out-of-gamut colors", OFFSET(gamut_warning), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC | AV_OPT_FLAG_DEPRECATED }, - { "gamut_clipping", "Enable colorimetric gamut clipping", OFFSET(gamut_clipping), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC | AV_OPT_FLAG_DEPRECATED }, - - { "dithering", "Dither method to use", OFFSET(dithering), AV_OPT_TYPE_INT, {.i64 = PL_DITHER_BLUE_NOISE}, -1, PL_DITHER_METHOD_COUNT - 1, DYNAMIC, "dither" }, - { "none", "Disable dithering", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, STATIC, "dither" }, - { "blue", "Blue noise", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DITHER_BLUE_NOISE}, 0, 0, STATIC, "dither" }, - { "ordered", "Ordered LUT", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DITHER_ORDERED_LUT}, 0, 0, STATIC, "dither" }, - { "ordered_fixed", "Fixed function ordered", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DITHER_ORDERED_FIXED}, 0, 0, STATIC, "dither" }, - { "white", "White noise", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DITHER_WHITE_NOISE}, 0, 0, STATIC, "dither" }, + { "contrast_recovery", "HDR contrast recovery strength", OFFSET(contrast_recovery), AV_OPT_TYPE_FLOAT, {.dbl = 0.30}, 0.0, 3.0, DYNAMIC }, + { "contrast_smoothness", "HDR contrast recovery smoothness", OFFSET(contrast_smoothness), AV_OPT_TYPE_FLOAT, {.dbl = 3.50}, 1.0, 32.0, DYNAMIC }, + + { "dithering", "Dither method to use", OFFSET(dithering), AV_OPT_TYPE_INT, {.i64 = PL_DITHER_BLUE_NOISE}, -1, PL_DITHER_METHOD_COUNT - 1, DYNAMIC, .unit = "dither" }, + { "none", "Disable dithering", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, STATIC, .unit = "dither" }, + { "blue", "Blue noise", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DITHER_BLUE_NOISE}, 0, 0, STATIC, .unit = "dither" }, + { "ordered", "Ordered LUT", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DITHER_ORDERED_LUT}, 0, 0, STATIC, .unit = "dither" }, + { "ordered_fixed", "Fixed function ordered", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DITHER_ORDERED_FIXED}, 0, 0, STATIC, .unit = "dither" }, + { "white", "White noise", 0, AV_OPT_TYPE_CONST, {.i64 = PL_DITHER_WHITE_NOISE}, 0, 0, STATIC, .unit = "dither" }, { "dither_lut_size", "Dithering LUT size", OFFSET(dither_lut_size), AV_OPT_TYPE_INT, {.i64 = 6}, 1, 8, STATIC }, { "dither_temporal", "Enable temporal dithering", OFFSET(dither_temporal), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, - { "cones", "Colorblindness adaptation model", OFFSET(cones), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, PL_CONE_LMS, DYNAMIC, "cone" }, - { "l", "L cone", 0, AV_OPT_TYPE_CONST, {.i64 = PL_CONE_L}, 0, 0, STATIC, "cone" }, - { "m", "M cone", 0, AV_OPT_TYPE_CONST, {.i64 = PL_CONE_M}, 0, 0, STATIC, "cone" }, - { "s", "S cone", 0, AV_OPT_TYPE_CONST, {.i64 = PL_CONE_S}, 0, 0, STATIC, "cone" }, + { "cones", "Colorblindness adaptation model", OFFSET(cones), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, PL_CONE_LMS, DYNAMIC, .unit = "cone" }, + { "l", "L cone", 0, AV_OPT_TYPE_CONST, {.i64 = PL_CONE_L}, 0, 0, STATIC, .unit = "cone" }, + { "m", "M cone", 0, AV_OPT_TYPE_CONST, {.i64 = PL_CONE_M}, 0, 0, STATIC, .unit = "cone" }, + { "s", "S cone", 0, AV_OPT_TYPE_CONST, {.i64 = PL_CONE_S}, 0, 0, STATIC, .unit = "cone" }, { "cone-strength", "Colorblindness adaptation strength", OFFSET(cone_str), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, 0.0, 10.0, DYNAMIC }, { "custom_shader_path", "Path to custom user shader (mpv .hook format)", OFFSET(shader_path), AV_OPT_TYPE_STRING, .flags = STATIC }, { "custom_shader_bin", "Custom user shader as binary (mpv .hook format)", OFFSET(shader_bin), AV_OPT_TYPE_BINARY, .flags = STATIC }, /* Performance/quality tradeoff options */ - { "skip_aa", "Skip anti-aliasing", OFFSET(skip_aa), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 0, DYNAMIC }, + { "skip_aa", "Skip anti-aliasing", OFFSET(skip_aa), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, { "polar_cutoff", "Polar LUT cutoff", OFFSET(polar_cutoff), AV_OPT_TYPE_FLOAT, {.dbl = 0}, 0.0, 1.0, DYNAMIC }, { "disable_linear", "Disable linear scaling", OFFSET(disable_linear), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, { "disable_builtin", "Disable built-in scalers", OFFSET(disable_builtin), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, - { "force_icc_lut", "Force the use of a full ICC 3DLUT for color mapping", OFFSET(force_icc_lut), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, { "force_dither", "Force dithering", OFFSET(force_dither), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, { "disable_fbos", "Force-disable FBOs", OFFSET(disable_fbos), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DYNAMIC }, { NULL }, @@ -855,15 +1437,6 @@ static const AVOption libplacebo_options[] = { AVFILTER_DEFINE_CLASS(libplacebo); -static const AVFilterPad libplacebo_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .filter_frame = &filter_frame, - .config_props = &libplacebo_config_input, - }, -}; - static const AVFilterPad libplacebo_outputs[] = { { .name = "default", @@ -878,10 +1451,11 @@ const AVFilter ff_vf_libplacebo = { .priv_size = sizeof(LibplaceboContext), .init = &libplacebo_init, .uninit = &libplacebo_uninit, - .process_command = &ff_filter_process_command, - FILTER_INPUTS(libplacebo_inputs), + .activate = &libplacebo_activate, + .process_command = &libplacebo_process_command, FILTER_OUTPUTS(libplacebo_outputs), FILTER_QUERY_FUNC(libplacebo_query_format), .priv_class = &libplacebo_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE | AVFILTER_FLAG_DYNAMIC_INPUTS, }; diff --git a/libavfilter/vf_libvmaf.c b/libavfilter/vf_libvmaf.c index 2586f37d99c..180ada040fa 100644 --- a/libavfilter/vf_libvmaf.c +++ b/libavfilter/vf_libvmaf.c @@ -24,6 +24,8 @@ * Calculate the VMAF between two input videos. */ +#include "config_components.h" + #include #include "libavutil/avstring.h" @@ -36,21 +38,21 @@ #include "internal.h" #include "video.h" +#if CONFIG_LIBVMAF_CUDA_FILTER +#include + +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_cuda_internal.h" +#endif + typedef struct LIBVMAFContext { const AVClass *class; FFFrameSync fs; - char *model_path; char *log_path; char *log_fmt; - int enable_transform; - int phone_model; - int psnr; - int ssim; - int ms_ssim; char *pool; int n_threads; int n_subsample; - int enable_conf_interval; char *model_cfg; char *feature_cfg; VmafContext *vmaf; @@ -58,24 +60,20 @@ typedef struct LIBVMAFContext { unsigned model_cnt; unsigned frame_cnt; unsigned bpc; +#if CONFIG_LIBVMAF_CUDA_FILTER + VmafCudaState *cu_state; +#endif } LIBVMAFContext; #define OFFSET(x) offsetof(LIBVMAFContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption libvmaf_options[] = { - {"model_path", "use model='path=...'.", OFFSET(model_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED}, {"log_path", "Set the file path to be used to write log.", OFFSET(log_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS}, {"log_fmt", "Set the format of the log (csv, json, xml, or sub).", OFFSET(log_fmt), AV_OPT_TYPE_STRING, {.str="xml"}, 0, 1, FLAGS}, - {"enable_transform", "use model='enable_transform=true'.", OFFSET(enable_transform), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED}, - {"phone_model", "use model='enable_transform=true'.", OFFSET(phone_model), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED}, - {"psnr", "use feature='name=psnr'.", OFFSET(psnr), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED}, - {"ssim", "use feature='name=float_ssim'.", OFFSET(ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED}, - {"ms_ssim", "use feature='name=float_ms_ssim'.", OFFSET(ms_ssim), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED}, {"pool", "Set the pool method to be used for computing vmaf.", OFFSET(pool), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS}, {"n_threads", "Set number of threads to be used when computing vmaf.", OFFSET(n_threads), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT_MAX, FLAGS}, {"n_subsample", "Set interval for frame subsampling used when computing vmaf.", OFFSET(n_subsample), AV_OPT_TYPE_INT, {.i64=1}, 1, UINT_MAX, FLAGS}, - {"enable_conf_interval", "model='enable_conf_interval=true'.", OFFSET(enable_conf_interval), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS|AV_OPT_FLAG_DEPRECATED}, {"model", "Set the model to be used for computing vmaf.", OFFSET(model_cfg), AV_OPT_TYPE_STRING, {.str="version=vmaf_v0.6.1"}, 0, 1, FLAGS}, {"feature", "Set the feature to be used for computing vmaf.", OFFSET(feature_cfg), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS}, { NULL } @@ -141,6 +139,13 @@ static int do_vmaf(FFFrameSync *fs) if (ctx->is_disabled || !ref) return ff_filter_frame(ctx->outputs[0], dist); + if (dist->color_range != ref->color_range) { + av_log(ctx, AV_LOG_WARNING, "distorted and reference " + "frames use different color ranges (%s != %s)\n", + av_color_range_name(dist->color_range), + av_color_range_name(ref->color_range)); + } + err = copy_picture_data(ref, &pic_ref, s->bpc); if (err) { av_log(s, AV_LOG_ERROR, "problem during vmaf_picture_alloc.\n"); @@ -163,7 +168,6 @@ static int do_vmaf(FFFrameSync *fs) return ff_filter_frame(ctx->outputs[0], dist); } - static AVDictionary **delimited_dict_parse(char *str, unsigned *cnt) { AVDictionary **dict = NULL; @@ -239,7 +243,7 @@ static int parse_features(AVFilterContext *ctx) const AVDictionaryEntry *e = NULL; while (e = av_dict_iterate(dict[i], e)) { - if (av_stristr(e->key, "name")) { + if (!strcmp(e->key, "name")) { feature_name = e->value; continue; } @@ -300,29 +304,29 @@ static int parse_models(AVFilterContext *ctx) char *path = NULL; while (e = av_dict_iterate(dict[i], e)) { - if (av_stristr(e->key, "disable_clip")) { - model_cfg.flags |= av_stristr(e->value, "true") ? + if (!strcmp(e->key, "disable_clip")) { + model_cfg.flags |= !strcmp(e->value, "true") ? VMAF_MODEL_FLAG_DISABLE_CLIP : 0; continue; } - if (av_stristr(e->key, "enable_transform")) { - model_cfg.flags |= av_stristr(e->value, "true") ? + if (!strcmp(e->key, "enable_transform")) { + model_cfg.flags |= !strcmp(e->value, "true") ? VMAF_MODEL_FLAG_ENABLE_TRANSFORM : 0; continue; } - if (av_stristr(e->key, "name")) { + if (!strcmp(e->key, "name")) { model_cfg.name = e->value; continue; } - if (av_stristr(e->key, "version")) { + if (!strcmp(e->key, "version")) { version = e->value; continue; } - if (av_stristr(e->key, "path")) { + if (!strcmp(e->key, "path")) { path = e->value; continue; } @@ -421,92 +425,6 @@ static enum VmafLogLevel log_level_map(int log_level) } } -static int parse_deprecated_options(AVFilterContext *ctx) -{ - LIBVMAFContext *s = ctx->priv; - VmafModel *model = NULL; - VmafModelCollection *model_collection = NULL; - enum VmafModelFlags flags = VMAF_MODEL_FLAGS_DEFAULT; - int err = 0; - - VmafModelConfig model_cfg = { - .name = "vmaf", - .flags = flags, - }; - - if (s->enable_transform || s->phone_model) - flags |= VMAF_MODEL_FLAG_ENABLE_TRANSFORM; - - if (!s->model_path) - goto extra_metrics_only; - - if (s->enable_conf_interval) { - err = vmaf_model_collection_load_from_path(&model, &model_collection, - &model_cfg, s->model_path); - if (err) { - av_log(ctx, AV_LOG_ERROR, - "problem loading model file: %s\n", s->model_path); - goto exit; - } - - err = vmaf_use_features_from_model_collection(s->vmaf, model_collection); - if (err) { - av_log(ctx, AV_LOG_ERROR, - "problem loading feature extractors from model file: %s\n", - s->model_path); - goto exit; - } - } else { - err = vmaf_model_load_from_path(&model, &model_cfg, s->model_path); - if (err) { - av_log(ctx, AV_LOG_ERROR, - "problem loading model file: %s\n", s->model_path); - goto exit; - } - err = vmaf_use_features_from_model(s->vmaf, model); - if (err) { - av_log(ctx, AV_LOG_ERROR, - "problem loading feature extractors from model file: %s\n", - s->model_path); - goto exit; - } - } - -extra_metrics_only: - if (s->psnr) { - VmafFeatureDictionary *d = NULL; - vmaf_feature_dictionary_set(&d, "enable_chroma", "false"); - - err = vmaf_use_feature(s->vmaf, "psnr", d); - if (err) { - av_log(ctx, AV_LOG_ERROR, - "problem loading feature extractor: psnr\n"); - goto exit; - } - } - - if (s->ssim) { - err = vmaf_use_feature(s->vmaf, "float_ssim", NULL); - if (err) { - av_log(ctx, AV_LOG_ERROR, - "problem loading feature extractor: ssim\n"); - goto exit; - } - } - - if (s->ms_ssim) { - err = vmaf_use_feature(s->vmaf, "float_ms_ssim", NULL); - if (err) { - av_log(ctx, AV_LOG_ERROR, - "problem loading feature extractor: ms_ssim\n"); - goto exit; - } - } - -exit: - return err; -} - static av_cold int init(AVFilterContext *ctx) { LIBVMAFContext *s = ctx->priv; @@ -522,10 +440,6 @@ static av_cold int init(AVFilterContext *ctx) if (err) return AVERROR(EINVAL); - err = parse_deprecated_options(ctx); - if (err) - return err; - err = parse_models(ctx); if (err) return err; @@ -541,6 +455,8 @@ static av_cold int init(AVFilterContext *ctx) static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV444P10LE, AV_PIX_FMT_YUV422P10LE, AV_PIX_FMT_YUV420P10LE, + AV_PIX_FMT_YUV444P12LE, AV_PIX_FMT_YUV422P12LE, AV_PIX_FMT_YUV420P12LE, + AV_PIX_FMT_YUV444P16LE, AV_PIX_FMT_YUV422P16LE, AV_PIX_FMT_YUV420P16LE, AV_PIX_FMT_NONE }; @@ -605,13 +521,13 @@ static int activate(AVFilterContext *ctx) static enum VmafOutputFormat log_fmt_map(const char *log_fmt) { if (log_fmt) { - if (av_stristr(log_fmt, "xml")) + if (!strcmp(log_fmt, "xml")) return VMAF_OUTPUT_FORMAT_XML; - if (av_stristr(log_fmt, "json")) + if (!strcmp(log_fmt, "json")) return VMAF_OUTPUT_FORMAT_JSON; - if (av_stristr(log_fmt, "csv")) + if (!strcmp(log_fmt, "csv")) return VMAF_OUTPUT_FORMAT_CSV; - if (av_stristr(log_fmt, "sub")) + if (!strcmp(log_fmt, "sub")) return VMAF_OUTPUT_FORMAT_SUB; } @@ -621,11 +537,11 @@ static enum VmafOutputFormat log_fmt_map(const char *log_fmt) static enum VmafPoolingMethod pool_method_map(const char *pool_method) { if (pool_method) { - if (av_stristr(pool_method, "min")) + if (!strcmp(pool_method, "min")) return VMAF_POOL_METHOD_MIN; - if (av_stristr(pool_method, "mean")) + if (!strcmp(pool_method, "mean")) return VMAF_POOL_METHOD_MEAN; - if (av_stristr(pool_method, "harmonic_mean")) + if (!strcmp(pool_method, "harmonic_mean")) return VMAF_POOL_METHOD_HARMONIC_MEAN; } @@ -682,7 +598,8 @@ static const AVFilterPad libvmaf_inputs[] = { { .name = "main", .type = AVMEDIA_TYPE_VIDEO, - },{ + }, + { .name = "reference", .type = AVMEDIA_TYPE_VIDEO, .config_props = config_input_ref, @@ -710,3 +627,197 @@ const AVFilter ff_vf_libvmaf = { FILTER_OUTPUTS(libvmaf_outputs), FILTER_PIXFMTS_ARRAY(pix_fmts), }; + +#if CONFIG_LIBVMAF_CUDA_FILTER +static const enum AVPixelFormat supported_formats[] = { + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_YUV444P16, +}; + +static int format_is_supported(enum AVPixelFormat fmt) +{ + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) + if (supported_formats[i] == fmt) + return 1; + return 0; +} + +static int config_props_cuda(AVFilterLink *outlink) +{ + int err; + AVFilterContext *ctx = outlink->src; + LIBVMAFContext *s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + AVHWFramesContext *frames_ctx = (AVHWFramesContext*) inlink->hw_frames_ctx->data; + AVCUDADeviceContext *device_hwctx = frames_ctx->device_ctx->hwctx; + CUcontext cu_ctx = device_hwctx->cuda_ctx; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frames_ctx->sw_format); + + VmafConfiguration cfg = { + .log_level = log_level_map(av_log_get_level()), + .n_subsample = s->n_subsample, + .n_threads = s->n_threads, + }; + + VmafCudaPictureConfiguration cuda_pic_cfg = { + .pic_params = { + .bpc = desc->comp[0].depth, + .w = inlink->w, + .h = inlink->h, + .pix_fmt = pix_fmt_map(frames_ctx->sw_format), + }, + .pic_prealloc_method = VMAF_CUDA_PICTURE_PREALLOCATION_METHOD_DEVICE, + }; + + VmafCudaConfiguration cuda_cfg = { + .cu_ctx = cu_ctx, + }; + + if (!format_is_supported(frames_ctx->sw_format)) { + av_log(s, AV_LOG_ERROR, + "Unsupported input format: %s\n", desc->name); + return AVERROR(EINVAL); + } + + err = vmaf_init(&s->vmaf, cfg); + if (err) + return AVERROR(EINVAL); + + err = vmaf_cuda_state_init(&s->cu_state, cuda_cfg); + if (err) + return AVERROR(EINVAL); + + err = vmaf_cuda_import_state(s->vmaf, s->cu_state); + if (err) + return AVERROR(EINVAL); + + err = vmaf_cuda_preallocate_pictures(s->vmaf, cuda_pic_cfg); + if (err < 0) + return err; + + err = parse_models(ctx); + if (err) + return err; + + err = parse_features(ctx); + if (err) + return err; + + return config_output(outlink); +} + +static int copy_picture_data_cuda(VmafContext* vmaf, + AVCUDADeviceContext* device_hwctx, + AVFrame* src, VmafPicture* dst, + enum AVPixelFormat pix_fmt) +{ + const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(pix_fmt); + CudaFunctions *cu = device_hwctx->internal->cuda_dl; + + CUDA_MEMCPY2D m = { + .srcMemoryType = CU_MEMORYTYPE_DEVICE, + .dstMemoryType = CU_MEMORYTYPE_DEVICE, + }; + + int err = vmaf_cuda_fetch_preallocated_picture(vmaf, dst); + if (err) + return AVERROR(ENOMEM); + + err = cu->cuCtxPushCurrent(device_hwctx->cuda_ctx); + if (err) + return AVERROR_EXTERNAL; + + for (unsigned i = 0; i < pix_desc->nb_components; i++) { + m.srcDevice = (CUdeviceptr) src->data[i]; + m.srcPitch = src->linesize[i]; + m.dstDevice = (CUdeviceptr) dst->data[i]; + m.dstPitch = dst->stride[i]; + m.WidthInBytes = dst->w[i] * ((dst->bpc + 7) / 8); + m.Height = dst->h[i]; + + err = cu->cuMemcpy2D(&m); + if (err) + return AVERROR_EXTERNAL; + break; + } + + err = cu->cuCtxPopCurrent(NULL); + if (err) + return AVERROR_EXTERNAL; + + return 0; +} + +static int do_vmaf_cuda(FFFrameSync* fs) +{ + AVFilterContext* ctx = fs->parent; + LIBVMAFContext* s = ctx->priv; + AVFilterLink *inlink = ctx->inputs[0]; + AVHWFramesContext *frames_ctx = (AVHWFramesContext*) inlink->hw_frames_ctx->data; + AVCUDADeviceContext *device_hwctx = frames_ctx->device_ctx->hwctx; + VmafPicture pic_ref, pic_dist; + AVFrame *ref, *dist; + + int err = 0; + + err = ff_framesync_dualinput_get(fs, &dist, &ref); + if (err < 0) + return err; + if (ctx->is_disabled || !ref) + return ff_filter_frame(ctx->outputs[0], dist); + + err = copy_picture_data_cuda(s->vmaf, device_hwctx, ref, &pic_ref, + frames_ctx->sw_format); + if (err) { + av_log(s, AV_LOG_ERROR, "problem during copy_picture_data_cuda.\n"); + return AVERROR(ENOMEM); + } + + err = copy_picture_data_cuda(s->vmaf, device_hwctx, dist, &pic_dist, + frames_ctx->sw_format); + if (err) { + av_log(s, AV_LOG_ERROR, "problem during copy_picture_data_cuda.\n"); + return AVERROR(ENOMEM); + } + + err = vmaf_read_pictures(s->vmaf, &pic_ref, &pic_dist, s->frame_cnt++); + if (err) { + av_log(s, AV_LOG_ERROR, "problem during vmaf_read_pictures.\n"); + return AVERROR(EINVAL); + } + + return ff_filter_frame(ctx->outputs[0], dist); +} + +static av_cold int init_cuda(AVFilterContext *ctx) +{ + LIBVMAFContext *s = ctx->priv; + s->fs.on_event = do_vmaf_cuda; + return 0; +} + +static const AVFilterPad libvmaf_outputs_cuda[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_props_cuda, + }, +}; + +const AVFilter ff_vf_libvmaf_cuda = { + .name = "libvmaf_cuda", + .description = NULL_IF_CONFIG_SMALL("Calculate the VMAF between two video streams."), + .preinit = libvmaf_framesync_preinit, + .init = init_cuda, + .uninit = uninit, + .activate = activate, + .priv_size = sizeof(LIBVMAFContext), + .priv_class = &libvmaf_class, + FILTER_INPUTS(libvmaf_inputs), + FILTER_OUTPUTS(libvmaf_outputs_cuda), + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_CUDA), + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; +#endif diff --git a/libavfilter/vf_limitdiff.c b/libavfilter/vf_limitdiff.c index d2688c39f42..1e903d45a83 100644 --- a/libavfilter/vf_limitdiff.c +++ b/libavfilter/vf_limitdiff.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" diff --git a/libavfilter/vf_limiter.c b/libavfilter/vf_limiter.c index 67b734229fd..f67f590d600 100644 --- a/libavfilter/vf_limiter.c +++ b/libavfilter/vf_limiter.c @@ -22,7 +22,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "limiter.h" #include "video.h" @@ -231,13 +230,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_limiter = { .name = "limiter", .description = NULL_IF_CONFIG_SMALL("Limit pixels components to the specified range."), @@ -245,7 +237,7 @@ const AVFilter ff_vf_limiter = { .priv_class = &limiter_class, .init = init, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_lumakey.c b/libavfilter/vf_lumakey.c index af1d85ec544..d426a5b67a8 100644 --- a/libavfilter/vf_lumakey.c +++ b/libavfilter/vf_lumakey.c @@ -19,9 +19,8 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -173,13 +172,6 @@ static const AVFilterPad lumakey_inputs[] = { }, }; -static const AVFilterPad lumakey_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(LumakeyContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -198,7 +190,7 @@ const AVFilter ff_vf_lumakey = { .priv_size = sizeof(LumakeyContext), .priv_class = &lumakey_class, FILTER_INPUTS(lumakey_inputs), - FILTER_OUTPUTS(lumakey_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_lut.c b/libavfilter/vf_lut.c index 621291cdade..1a6ca065461 100644 --- a/libavfilter/vf_lut.c +++ b/libavfilter/vf_lut.c @@ -582,11 +582,6 @@ static const AVFilterPad inputs[] = { .config_props = config_props, }, }; -static const AVFilterPad outputs[] = { - { .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; #define DEFINE_LUT_FILTER(name_, description_, priv_class_) \ const AVFilter ff_vf_##name_ = { \ @@ -597,7 +592,7 @@ static const AVFilterPad outputs[] = { .init = name_##_init, \ .uninit = uninit, \ FILTER_INPUTS(inputs), \ - FILTER_OUTPUTS(outputs), \ + FILTER_OUTPUTS(ff_video_default_filterpad), \ FILTER_QUERY_FUNC(query_formats), \ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | \ AVFILTER_FLAG_SLICE_THREADS, \ diff --git a/libavfilter/vf_lut3d.c b/libavfilter/vf_lut3d.c index 1ca448fcb3b..a03fdb33aed 100644 --- a/libavfilter/vf_lut3d.c +++ b/libavfilter/vf_lut3d.c @@ -47,12 +47,12 @@ #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM #define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM #define COMMON_OPTIONS \ - { "interp", "select interpolation mode", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERPOLATE_TETRAHEDRAL}, 0, NB_INTERP_MODE-1, TFLAGS, "interp_mode" }, \ - { "nearest", "use values from the nearest defined points", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_NEAREST}, 0, 0, TFLAGS, "interp_mode" }, \ - { "trilinear", "interpolate values using the 8 points defining a cube", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TRILINEAR}, 0, 0, TFLAGS, "interp_mode" }, \ - { "tetrahedral", "interpolate values using a tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TETRAHEDRAL}, 0, 0, TFLAGS, "interp_mode" }, \ - { "pyramid", "interpolate values using a pyramid", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_PYRAMID}, 0, 0, TFLAGS, "interp_mode" }, \ - { "prism", "interpolate values using a prism", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_PRISM}, 0, 0, TFLAGS, "interp_mode" }, \ + { "interp", "select interpolation mode", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERPOLATE_TETRAHEDRAL}, 0, NB_INTERP_MODE-1, TFLAGS, .unit = "interp_mode" }, \ + { "nearest", "use values from the nearest defined points", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_NEAREST}, 0, 0, TFLAGS, .unit = "interp_mode" }, \ + { "trilinear", "interpolate values using the 8 points defining a cube", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TRILINEAR}, 0, 0, TFLAGS, .unit = "interp_mode" }, \ + { "tetrahedral", "interpolate values using a tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_TETRAHEDRAL}, 0, 0, TFLAGS, .unit = "interp_mode" }, \ + { "pyramid", "interpolate values using a pyramid", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_PYRAMID}, 0, 0, TFLAGS, .unit = "interp_mode" }, \ + { "prism", "interpolate values using a prism", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_PRISM}, 0, 0, TFLAGS, .unit = "interp_mode" }, \ { NULL } #define EXPONENT_MASK 0x7F800000 @@ -702,7 +702,8 @@ static int parse_cube(AVFilterContext *ctx, FILE *f) else if (!strncmp(line + 7, "MAX ", 4)) vals = max; if (!vals) return AVERROR_INVALIDDATA; - av_sscanf(line + 11, "%f %f %f", vals, vals + 1, vals + 2); + if (av_sscanf(line + 11, "%f %f %f", vals, vals + 1, vals + 2) != 3) + return AVERROR_INVALIDDATA; av_log(ctx, AV_LOG_DEBUG, "min: %f %f %f | max: %f %f %f\n", min[0], min[1], min[2], max[0], max[1], max[2]); goto try_again; @@ -1217,9 +1218,9 @@ static const AVOption lut3d_haldclut_options[] = { { "file", "set 3D LUT file name", OFFSET(file), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = FLAGS }, #endif #if CONFIG_HALDCLUT_FILTER - { "clut", "when to process CLUT", OFFSET(clut), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, .flags = TFLAGS, "clut" }, - { "first", "process only first CLUT, ignore rest", 0, AV_OPT_TYPE_CONST, {.i64=0}, .flags = TFLAGS, "clut" }, - { "all", "process all CLUTs", 0, AV_OPT_TYPE_CONST, {.i64=1}, .flags = TFLAGS, "clut" }, + { "clut", "when to process CLUT", OFFSET(clut), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, .flags = TFLAGS, .unit = "clut" }, + { "first", "process only first CLUT, ignore rest", 0, AV_OPT_TYPE_CONST, {.i64=0}, .flags = TFLAGS, .unit = "clut" }, + { "all", "process all CLUTs", 0, AV_OPT_TYPE_CONST, {.i64=1}, .flags = TFLAGS, .unit = "clut" }, #endif COMMON_OPTIONS }; @@ -1301,13 +1302,6 @@ static const AVFilterPad lut3d_inputs[] = { }, }; -static const AVFilterPad lut3d_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_lut3d = { .name = "lut3d", .description = NULL_IF_CONFIG_SMALL("Adjust colors using a 3D LUT."), @@ -1315,7 +1309,7 @@ const AVFilter ff_vf_lut3d = { .init = lut3d_init, .uninit = lut3d_uninit, FILTER_INPUTS(lut3d_inputs), - FILTER_OUTPUTS(lut3d_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &lut3d_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, @@ -1328,7 +1322,7 @@ const AVFilter ff_vf_lut3d = { static void update_clut_packed(LUT3DContext *lut3d, const AVFrame *frame) { const uint8_t *data = frame->data[0]; - const int linesize = frame->linesize[0]; + const ptrdiff_t linesize = frame->linesize[0]; const int w = lut3d->clut_width; const int step = lut3d->clut_step; const uint8_t *rgba_map = lut3d->clut_rgba_map; @@ -1367,9 +1361,9 @@ static void update_clut_planar(LUT3DContext *lut3d, const AVFrame *frame) const uint8_t *datag = frame->data[0]; const uint8_t *datab = frame->data[1]; const uint8_t *datar = frame->data[2]; - const int glinesize = frame->linesize[0]; - const int blinesize = frame->linesize[1]; - const int rlinesize = frame->linesize[2]; + const ptrdiff_t glinesize = frame->linesize[0]; + const ptrdiff_t blinesize = frame->linesize[1]; + const ptrdiff_t rlinesize = frame->linesize[2]; const int w = lut3d->clut_width; const int level = lut3d->lutsize; const int level2 = lut3d->lutsize2; @@ -1414,9 +1408,9 @@ static void update_clut_float(LUT3DContext *lut3d, const AVFrame *frame) const uint8_t *datag = frame->data[0]; const uint8_t *datab = frame->data[1]; const uint8_t *datar = frame->data[2]; - const int glinesize = frame->linesize[0]; - const int blinesize = frame->linesize[1]; - const int rlinesize = frame->linesize[2]; + const ptrdiff_t glinesize = frame->linesize[0]; + const ptrdiff_t blinesize = frame->linesize[1]; + const ptrdiff_t rlinesize = frame->linesize[2]; const int w = lut3d->clut_width; const int level = lut3d->lutsize; const int level2 = lut3d->lutsize2; @@ -1740,12 +1734,14 @@ static int parse_cube_1d(AVFilterContext *ctx, FILE *f) else if (!strncmp(line + 7, "MAX ", 4)) vals = max; if (!vals) return AVERROR_INVALIDDATA; - av_sscanf(line + 11, "%f %f %f", vals, vals + 1, vals + 2); + if (av_sscanf(line + 11, "%f %f %f", vals, vals + 1, vals + 2) != 3) + return AVERROR_INVALIDDATA; av_log(ctx, AV_LOG_DEBUG, "min: %f %f %f | max: %f %f %f\n", min[0], min[1], min[2], max[0], max[1], max[2]); goto try_again; } else if (!strncmp(line, "LUT_1D_INPUT_RANGE ", 19)) { - av_sscanf(line + 19, "%f %f", min, max); + if (av_sscanf(line + 19, "%f %f", min, max) != 2) + return AVERROR_INVALIDDATA; min[1] = min[2] = min[0]; max[1] = max[2] = max[0]; goto try_again; @@ -1769,12 +1765,12 @@ static int parse_cube_1d(AVFilterContext *ctx, FILE *f) static const AVOption lut1d_options[] = { { "file", "set 1D LUT file name", OFFSET(file), AV_OPT_TYPE_STRING, {.str=NULL}, .flags = TFLAGS }, - { "interp", "select interpolation mode", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERPOLATE_1D_LINEAR}, 0, NB_INTERP_1D_MODE-1, TFLAGS, "interp_mode" }, - { "nearest", "use values from the nearest defined points", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_NEAREST}, 0, 0, TFLAGS, "interp_mode" }, - { "linear", "use values from the linear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_LINEAR}, 0, 0, TFLAGS, "interp_mode" }, - { "cosine", "use values from the cosine interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_COSINE}, 0, 0, TFLAGS, "interp_mode" }, - { "cubic", "use values from the cubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_CUBIC}, 0, 0, TFLAGS, "interp_mode" }, - { "spline", "use values from the spline interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_SPLINE}, 0, 0, TFLAGS, "interp_mode" }, + { "interp", "select interpolation mode", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=INTERPOLATE_1D_LINEAR}, 0, NB_INTERP_1D_MODE-1, TFLAGS, .unit = "interp_mode" }, + { "nearest", "use values from the nearest defined points", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_NEAREST}, 0, 0, TFLAGS, .unit = "interp_mode" }, + { "linear", "use values from the linear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_LINEAR}, 0, 0, TFLAGS, .unit = "interp_mode" }, + { "cosine", "use values from the cosine interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_COSINE}, 0, 0, TFLAGS, .unit = "interp_mode" }, + { "cubic", "use values from the cubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_CUBIC}, 0, 0, TFLAGS, .unit = "interp_mode" }, + { "spline", "use values from the spline interpolation", 0, AV_OPT_TYPE_CONST, {.i64=INTERPOLATE_1D_SPLINE}, 0, 0, TFLAGS, .unit = "interp_mode" }, { NULL } }; @@ -2232,20 +2228,13 @@ static const AVFilterPad lut1d_inputs[] = { }, }; -static const AVFilterPad lut1d_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_lut1d = { .name = "lut1d", .description = NULL_IF_CONFIG_SMALL("Adjust colors using a 1D LUT."), .priv_size = sizeof(LUT1DContext), .init = lut1d_init, FILTER_INPUTS(lut1d_inputs), - FILTER_OUTPUTS(lut1d_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &lut1d_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_maskedclamp.c b/libavfilter/vf_maskedclamp.c index 72f98703a5e..e6fbb1a6d58 100644 --- a/libavfilter/vf_maskedclamp.c +++ b/libavfilter/vf_maskedclamp.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" diff --git a/libavfilter/vf_maskedmerge.c b/libavfilter/vf_maskedmerge.c index 7adb8097607..4ca0c571c8e 100644 --- a/libavfilter/vf_maskedmerge.c +++ b/libavfilter/vf_maskedmerge.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "maskedmerge.h" diff --git a/libavfilter/vf_maskedminmax.c b/libavfilter/vf_maskedminmax.c index c7a05b42d91..b1c309cc7d9 100644 --- a/libavfilter/vf_maskedminmax.c +++ b/libavfilter/vf_maskedminmax.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" diff --git a/libavfilter/vf_maskedthreshold.c b/libavfilter/vf_maskedthreshold.c index 53731eefc98..e78e11810e0 100644 --- a/libavfilter/vf_maskedthreshold.c +++ b/libavfilter/vf_maskedthreshold.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" @@ -54,9 +53,9 @@ typedef struct ThreadData { static const AVOption maskedthreshold_options[] = { { "threshold", "set threshold", OFFSET(threshold), AV_OPT_TYPE_INT, {.i64=1}, 0, UINT16_MAX, TFLAGS }, { "planes", "set planes", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=0xF}, 0, 0xF, TFLAGS }, - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "mode" }, - { "abs", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" }, - { "diff", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "mode" }, + { "abs", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "mode" }, + { "diff", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "mode" }, { NULL } }; diff --git a/libavfilter/vf_maskfun.c b/libavfilter/vf_maskfun.c index 253e5e40f7a..1ac152fc8b8 100644 --- a/libavfilter/vf_maskfun.c +++ b/libavfilter/vf_maskfun.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -317,20 +316,13 @@ static const AVFilterPad maskfun_inputs[] = { }, }; -static const AVFilterPad maskfun_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_maskfun = { .name = "maskfun", .description = NULL_IF_CONFIG_SMALL("Create Mask."), .priv_size = sizeof(MaskFunContext), .uninit = uninit, FILTER_INPUTS(maskfun_inputs), - FILTER_OUTPUTS(maskfun_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &maskfun_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_mcdeint.c b/libavfilter/vf_mcdeint.c index e747521c0a8..76ab59912e1 100644 --- a/libavfilter/vf_mcdeint.c +++ b/libavfilter/vf_mcdeint.c @@ -50,11 +50,10 @@ */ #include "libavutil/opt.h" -#include "libavutil/pixdesc.h" #include "libavcodec/avcodec.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" enum MCDeintMode { MODE_FAST = 0, @@ -75,12 +74,13 @@ typedef struct MCDeintContext { int parity; ///< MCDeintParity int qp; AVPacket *pkt; + AVFrame *frame_dec; AVCodecContext *enc_ctx; } MCDeintContext; #define OFFSET(x) offsetof(MCDeintContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM -#define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, unit } +#define CONST(name, help, val, u) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, .unit = u } static const AVOption mcdeint_options[] = { { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_FAST}, 0, MODE_NB-1, FLAGS, .unit="mode" }, @@ -89,7 +89,7 @@ static const AVOption mcdeint_options[] = { CONST("slow", NULL, MODE_SLOW, "mode"), CONST("extra_slow", NULL, MODE_EXTRA_SLOW, "mode"), - { "parity", "set the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=PARITY_BFF}, -1, 1, FLAGS, "parity" }, + { "parity", "set the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=PARITY_BFF}, -1, 1, FLAGS, .unit = "parity" }, CONST("tff", "assume top field first", PARITY_TFF, "parity"), CONST("bff", "assume bottom field first", PARITY_BFF, "parity"), @@ -116,6 +116,9 @@ static int config_props(AVFilterLink *inlink) mcdeint->pkt = av_packet_alloc(); if (!mcdeint->pkt) return AVERROR(ENOMEM); + mcdeint->frame_dec = av_frame_alloc(); + if (!mcdeint->frame_dec) + return AVERROR(ENOMEM); mcdeint->enc_ctx = avcodec_alloc_context3(enc); if (!mcdeint->enc_ctx) return AVERROR(ENOMEM); @@ -126,7 +129,7 @@ static int config_props(AVFilterLink *inlink) enc_ctx->gop_size = INT_MAX; enc_ctx->max_b_frames = 0; enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P; - enc_ctx->flags = AV_CODEC_FLAG_QSCALE | AV_CODEC_FLAG_LOW_DELAY; + enc_ctx->flags = AV_CODEC_FLAG_QSCALE | AV_CODEC_FLAG_LOW_DELAY | AV_CODEC_FLAG_RECON_FRAME; enc_ctx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; enc_ctx->global_quality = 1; enc_ctx->me_cmp = enc_ctx->me_sub_cmp = FF_CMP_SAD; @@ -160,15 +163,16 @@ static av_cold void uninit(AVFilterContext *ctx) av_packet_free(&mcdeint->pkt); avcodec_free_context(&mcdeint->enc_ctx); + av_frame_free(&mcdeint->frame_dec); } static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) { MCDeintContext *mcdeint = inlink->dst->priv; AVFilterLink *outlink = inlink->dst->outputs[0]; - AVFrame *outpic, *frame_dec; + AVFrame *outpic, *frame_dec = mcdeint->frame_dec; AVPacket *pkt = mcdeint->pkt; - int x, y, i, ret, got_frame = 0; + int x, y, i, ret; outpic = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!outpic) { @@ -178,11 +182,22 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpic) av_frame_copy_props(outpic, inpic); inpic->quality = mcdeint->qp * FF_QP2LAMBDA; - ret = avcodec_encode_video2(mcdeint->enc_ctx, pkt, inpic, &got_frame); - if (ret < 0) + ret = avcodec_send_frame(mcdeint->enc_ctx, inpic); + if (ret < 0) { + av_log(mcdeint->enc_ctx, AV_LOG_ERROR, "Error sending a frame for encoding\n"); goto end; - - frame_dec = mcdeint->enc_ctx->coded_frame; + } + ret = avcodec_receive_packet(mcdeint->enc_ctx, pkt); + if (ret < 0) { + av_log(mcdeint->enc_ctx, AV_LOG_ERROR, "Error receiving a packet from encoding\n"); + goto end; + } + av_packet_unref(pkt); + ret = avcodec_receive_frame(mcdeint->enc_ctx, frame_dec); + if (ret < 0) { + av_log(mcdeint->enc_ctx, AV_LOG_ERROR, "Error receiving a frame from encoding\n"); + goto end; + } for (i = 0; i < 3; i++) { int is_chroma = !!i; @@ -284,20 +299,13 @@ static const AVFilterPad mcdeint_inputs[] = { }, }; -static const AVFilterPad mcdeint_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_mcdeint = { .name = "mcdeint", .description = NULL_IF_CONFIG_SMALL("Apply motion compensating deinterlacing."), .priv_size = sizeof(MCDeintContext), .uninit = uninit, FILTER_INPUTS(mcdeint_inputs), - FILTER_OUTPUTS(mcdeint_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_YUV420P), .priv_class = &mcdeint_class, }; diff --git a/libavfilter/vf_median.c b/libavfilter/vf_median.c index 11eccf608c6..57514f92890 100644 --- a/libavfilter/vf_median.c +++ b/libavfilter/vf_median.c @@ -24,7 +24,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "median.h" #include "video.h" @@ -270,13 +269,6 @@ static const AVFilterPad median_inputs[] = { }, }; -static const AVFilterPad median_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_median = { .name = "median", .description = NULL_IF_CONFIG_SMALL("Apply Median filter."), @@ -284,7 +276,7 @@ const AVFilter ff_vf_median = { .priv_class = &median_class, .uninit = uninit, FILTER_INPUTS(median_inputs), - FILTER_OUTPUTS(median_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_mergeplanes.c b/libavfilter/vf_mergeplanes.c index 23b9f3d62f1..91bc0d2c553 100644 --- a/libavfilter/vf_mergeplanes.c +++ b/libavfilter/vf_mergeplanes.c @@ -24,8 +24,10 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "framesync.h" +#include "video.h" typedef struct Mapping { int input; @@ -48,6 +50,7 @@ typedef struct MergePlanesContext { int planewidth[4]; int planeheight[4]; Mapping map[4]; + const AVPixFmtDescriptor *indesc[4]; const AVPixFmtDescriptor *outdesc; FFFrameSync fs; @@ -171,7 +174,8 @@ static int process_frame(FFFrameSync *fs) av_image_copy_plane(out->data[i], out->linesize[i], in[input]->data[plane], in[input]->linesize[plane], - s->planewidth[i], s->planeheight[i]); + s->planewidth[i] * ((s->indesc[input]->comp[plane].depth + 7) / 8), + s->planeheight[i]); } return ff_filter_frame(outlink, out); @@ -199,9 +203,9 @@ static int config_output(AVFilterLink *outlink) outlink->sample_aspect_ratio = ctx->inputs[0]->sample_aspect_ratio; s->planewidth[1] = - s->planewidth[2] = AV_CEIL_RSHIFT(((s->outdesc->comp[1].depth > 8) + 1) * outlink->w, s->outdesc->log2_chroma_w); + s->planewidth[2] = AV_CEIL_RSHIFT(outlink->w, s->outdesc->log2_chroma_w); s->planewidth[0] = - s->planewidth[3] = ((s->outdesc->comp[0].depth > 8) + 1) * outlink->w; + s->planewidth[3] = outlink->w; s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(outlink->h, s->outdesc->log2_chroma_h); s->planeheight[0] = @@ -210,8 +214,7 @@ static int config_output(AVFilterLink *outlink) for (i = 0; i < s->nb_inputs; i++) { InputParam *inputp = &inputsp[i]; AVFilterLink *inlink = ctx->inputs[i]; - const AVPixFmtDescriptor *indesc = av_pix_fmt_desc_get(inlink->format); - int j; + s->indesc[i] = av_pix_fmt_desc_get(inlink->format); if (outlink->sample_aspect_ratio.num != inlink->sample_aspect_ratio.num || outlink->sample_aspect_ratio.den != inlink->sample_aspect_ratio.den) { @@ -227,17 +230,17 @@ static int config_output(AVFilterLink *outlink) } inputp->planewidth[1] = - inputp->planewidth[2] = AV_CEIL_RSHIFT(((indesc->comp[1].depth > 8) + 1) * inlink->w, indesc->log2_chroma_w); + inputp->planewidth[2] = AV_CEIL_RSHIFT(inlink->w, s->indesc[i]->log2_chroma_w); inputp->planewidth[0] = - inputp->planewidth[3] = ((indesc->comp[0].depth > 8) + 1) * inlink->w; + inputp->planewidth[3] = inlink->w; inputp->planeheight[1] = - inputp->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, indesc->log2_chroma_h); + inputp->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, s->indesc[i]->log2_chroma_h); inputp->planeheight[0] = inputp->planeheight[3] = inlink->h; inputp->nb_planes = av_pix_fmt_count_planes(inlink->format); - for (j = 0; j < inputp->nb_planes; j++) - inputp->depth[j] = indesc->comp[j].depth; + for (int j = 0; j < inputp->nb_planes; j++) + inputp->depth[j] = s->indesc[i]->comp[j].depth; in[i].time_base = inlink->time_base; in[i].sync = 1; diff --git a/libavfilter/vf_mestimate.c b/libavfilter/vf_mestimate.c index eff58f612e4..d551dab8529 100644 --- a/libavfilter/vf_mestimate.c +++ b/libavfilter/vf_mestimate.c @@ -21,12 +21,9 @@ #include "motion_estimation.h" #include "libavcodec/mathops.h" #include "libavutil/common.h" -#include "libavutil/imgutils.h" #include "libavutil/opt.h" -#include "libavutil/pixdesc.h" #include "libavutil/motion_vector.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -47,10 +44,10 @@ typedef struct MEContext { #define OFFSET(x) offsetof(MEContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM -#define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, unit } +#define CONST(name, help, val, u) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, .unit = u } static const AVOption mestimate_options[] = { - { "method", "motion estimation method", OFFSET(method), AV_OPT_TYPE_INT, {.i64 = AV_ME_METHOD_ESA}, AV_ME_METHOD_ESA, AV_ME_METHOD_UMH, FLAGS, "method" }, + { "method", "motion estimation method", OFFSET(method), AV_OPT_TYPE_INT, {.i64 = AV_ME_METHOD_ESA}, AV_ME_METHOD_ESA, AV_ME_METHOD_UMH, FLAGS, .unit = "method" }, CONST("esa", "exhaustive search", AV_ME_METHOD_ESA, "method"), CONST("tss", "three step search", AV_ME_METHOD_TSS, "method"), CONST("tdls", "two dimensional logarithmic search", AV_ME_METHOD_TDLS, "method"), @@ -350,13 +347,6 @@ static const AVFilterPad mestimate_inputs[] = { }, }; -static const AVFilterPad mestimate_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_mestimate = { .name = "mestimate", .description = NULL_IF_CONFIG_SMALL("Generate motion vectors."), @@ -365,6 +355,6 @@ const AVFilter ff_vf_mestimate = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(mestimate_inputs), - FILTER_OUTPUTS(mestimate_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/vf_midequalizer.c b/libavfilter/vf_midequalizer.c index 8c0e02d888e..fae2b7ef193 100644 --- a/libavfilter/vf_midequalizer.c +++ b/libavfilter/vf_midequalizer.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" diff --git a/libavfilter/vf_minterpolate.c b/libavfilter/vf_minterpolate.c index f2de61ca39a..171f865b0b8 100644 --- a/libavfilter/vf_minterpolate.c +++ b/libavfilter/vf_minterpolate.c @@ -22,11 +22,9 @@ #include "motion_estimation.h" #include "libavcodec/mathops.h" #include "libavutil/common.h" -#include "libavutil/motion_vector.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "scene_sad.h" @@ -199,21 +197,21 @@ typedef struct MIContext { #define OFFSET(x) offsetof(MIContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM -#define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, unit } +#define CONST(name, help, val, u) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, .unit = u } static const AVOption minterpolate_options[] = { { "fps", "output's frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str = "60"}, 0, INT_MAX, FLAGS }, - { "mi_mode", "motion interpolation mode", OFFSET(mi_mode), AV_OPT_TYPE_INT, {.i64 = MI_MODE_MCI}, MI_MODE_DUP, MI_MODE_MCI, FLAGS, "mi_mode" }, + { "mi_mode", "motion interpolation mode", OFFSET(mi_mode), AV_OPT_TYPE_INT, {.i64 = MI_MODE_MCI}, MI_MODE_DUP, MI_MODE_MCI, FLAGS, .unit = "mi_mode" }, CONST("dup", "duplicate frames", MI_MODE_DUP, "mi_mode"), CONST("blend", "blend frames", MI_MODE_BLEND, "mi_mode"), CONST("mci", "motion compensated interpolation", MI_MODE_MCI, "mi_mode"), - { "mc_mode", "motion compensation mode", OFFSET(mc_mode), AV_OPT_TYPE_INT, {.i64 = MC_MODE_OBMC}, MC_MODE_OBMC, MC_MODE_AOBMC, FLAGS, "mc_mode" }, + { "mc_mode", "motion compensation mode", OFFSET(mc_mode), AV_OPT_TYPE_INT, {.i64 = MC_MODE_OBMC}, MC_MODE_OBMC, MC_MODE_AOBMC, FLAGS, .unit = "mc_mode" }, CONST("obmc", "overlapped block motion compensation", MC_MODE_OBMC, "mc_mode"), CONST("aobmc", "adaptive overlapped block motion compensation", MC_MODE_AOBMC, "mc_mode"), - { "me_mode", "motion estimation mode", OFFSET(me_mode), AV_OPT_TYPE_INT, {.i64 = ME_MODE_BILAT}, ME_MODE_BIDIR, ME_MODE_BILAT, FLAGS, "me_mode" }, + { "me_mode", "motion estimation mode", OFFSET(me_mode), AV_OPT_TYPE_INT, {.i64 = ME_MODE_BILAT}, ME_MODE_BIDIR, ME_MODE_BILAT, FLAGS, .unit = "me_mode" }, CONST("bidir", "bidirectional motion estimation", ME_MODE_BIDIR, "me_mode"), CONST("bilat", "bilateral motion estimation", ME_MODE_BILAT, "me_mode"), - { "me", "motion estimation method", OFFSET(me_method), AV_OPT_TYPE_INT, {.i64 = AV_ME_METHOD_EPZS}, AV_ME_METHOD_ESA, AV_ME_METHOD_UMH, FLAGS, "me" }, + { "me", "motion estimation method", OFFSET(me_method), AV_OPT_TYPE_INT, {.i64 = AV_ME_METHOD_EPZS}, AV_ME_METHOD_ESA, AV_ME_METHOD_UMH, FLAGS, .unit = "me" }, CONST("esa", "exhaustive search", AV_ME_METHOD_ESA, "me"), CONST("tss", "three step search", AV_ME_METHOD_TSS, "me"), CONST("tdls", "two dimensional logarithmic search", AV_ME_METHOD_TDLS, "me"), @@ -226,7 +224,7 @@ static const AVOption minterpolate_options[] = { { "mb_size", "macroblock size", OFFSET(mb_size), AV_OPT_TYPE_INT, {.i64 = 16}, 4, 16, FLAGS }, { "search_param", "search parameter", OFFSET(search_param), AV_OPT_TYPE_INT, {.i64 = 32}, 4, INT_MAX, FLAGS }, { "vsbmc", "variable-size block motion compensation", OFFSET(vsbmc), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS }, - { "scd", "scene change detection method", OFFSET(scd_method), AV_OPT_TYPE_INT, {.i64 = SCD_METHOD_FDIFF}, SCD_METHOD_NONE, SCD_METHOD_FDIFF, FLAGS, "scene" }, + { "scd", "scene change detection method", OFFSET(scd_method), AV_OPT_TYPE_INT, {.i64 = SCD_METHOD_FDIFF}, SCD_METHOD_NONE, SCD_METHOD_FDIFF, FLAGS, .unit = "scene" }, CONST("none", "disable detection", SCD_METHOD_NONE, "scene"), CONST("fdiff", "frame difference", SCD_METHOD_FDIFF, "scene"), { "scd_threshold", "scene change threshold", OFFSET(scd_threshold), AV_OPT_TYPE_DOUBLE, {.dbl = 10.}, 0, 100.0, FLAGS }, @@ -827,7 +825,6 @@ static int detect_scene_change(AVFilterContext *ctx) double ret = 0, mafd, diff; uint64_t sad; mi_ctx->sad(p1, linesize1, p2, linesize2, input->w, input->h, &sad); - emms_c(); mafd = (double) sad * 100.0 / (input->h * input->w) / (1 << mi_ctx->bitdepth); diff = fabs(mafd - mi_ctx->prev_mafd); ret = av_clipf(FFMIN(mafd, diff), 0, 100.0); @@ -1078,8 +1075,13 @@ static void interpolate(AVFilterLink *inlink, AVFrame *avf_out) pts = av_rescale(avf_out->pts, (int64_t) ALPHA_MAX * outlink->time_base.num * inlink->time_base.den, (int64_t) outlink->time_base.den * inlink->time_base.num); - alpha = (pts - mi_ctx->frames[1].avf->pts * ALPHA_MAX) / (mi_ctx->frames[2].avf->pts - mi_ctx->frames[1].avf->pts); - alpha = av_clip(alpha, 0, ALPHA_MAX); + if (mi_ctx->frames[2].avf->pts > mi_ctx->frames[1].avf->pts) { + alpha = (pts - mi_ctx->frames[1].avf->pts * ALPHA_MAX) / (mi_ctx->frames[2].avf->pts - mi_ctx->frames[1].avf->pts); + alpha = av_clip(alpha, 0, ALPHA_MAX); + } else { + av_log(ctx, AV_LOG_DEBUG, "duplicate input PTS detected\n"); + alpha = 0; + } if (alpha == 0 || alpha == ALPHA_MAX) { av_frame_copy(avf_out, alpha ? mi_ctx->frames[2].avf : mi_ctx->frames[1].avf); diff --git a/libavfilter/vf_misc_vaapi.c b/libavfilter/vf_misc_vaapi.c index db3e69679af..d68e18b52b2 100644 --- a/libavfilter/vf_misc_vaapi.c +++ b/libavfilter/vf_misc_vaapi.c @@ -21,9 +21,9 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "vaapi_vpp.h" +#include "video.h" // Denoise min/max/default Values #define DENOISE_MIN 0 @@ -131,6 +131,9 @@ static int misc_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) av_get_pix_fmt_name(input_frame->format), input_frame->width, input_frame->height, input_frame->pts); + if (vpp_ctx->passthrough) + return ff_filter_frame(outlink, input_frame); + if (vpp_ctx->va_context == VA_INVALID_ID) return AVERROR(EINVAL); @@ -176,11 +179,14 @@ static int misc_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) static av_cold int denoise_vaapi_init(AVFilterContext *avctx) { VAAPIVPPContext *vpp_ctx = avctx->priv; + DenoiseVAAPIContext *ctx = avctx->priv; ff_vaapi_vpp_ctx_init(avctx); vpp_ctx->pipeline_uninit = ff_vaapi_vpp_pipeline_uninit; vpp_ctx->build_filter_params = denoise_vaapi_build_filter_params; vpp_ctx->output_format = AV_PIX_FMT_NONE; + if (ctx->denoise == DENOISE_DEFAULT) + vpp_ctx->passthrough = 1; return 0; } @@ -188,11 +194,14 @@ static av_cold int denoise_vaapi_init(AVFilterContext *avctx) static av_cold int sharpness_vaapi_init(AVFilterContext *avctx) { VAAPIVPPContext *vpp_ctx = avctx->priv; + SharpnessVAAPIContext *ctx = avctx->priv; ff_vaapi_vpp_ctx_init(avctx); vpp_ctx->pipeline_uninit = ff_vaapi_vpp_pipeline_uninit; vpp_ctx->build_filter_params = sharpness_vaapi_build_filter_params; vpp_ctx->output_format = AV_PIX_FMT_NONE; + if (ctx->sharpness == SHARPNESS_DEFAULT) + vpp_ctx->passthrough = 1; return 0; } diff --git a/libavfilter/vf_mix.c b/libavfilter/vf_mix.c index 64d07bbbbb7..10da1cadd3c 100644 --- a/libavfilter/vf_mix.c +++ b/libavfilter/vf_mix.c @@ -43,8 +43,10 @@ typedef struct MixContext { float scale; float wfactor; + int fast; int tmix; int nb_frames; + int nb_unique_frames; int depth; int max; @@ -53,6 +55,8 @@ typedef struct MixContext { int linesizes[4]; int height[4]; + uint8_t *sum[4]; + uint8_t **data; int *linesize; @@ -81,6 +85,7 @@ static int parse_weights(AVFilterContext *ctx) char *p, *arg, *saveptr = NULL; int i, last = 0; + s->fast = 1; s->wfactor = 0.f; p = s->weights_str; for (i = 0; i < s->nb_inputs; i++) { @@ -93,6 +98,8 @@ static int parse_weights(AVFilterContext *ctx) return AVERROR(EINVAL); } s->wfactor += s->weights[i]; + if (i > 0) + s->fast &= s->weights[i] == s->weights[0]; last = i; } @@ -103,6 +110,8 @@ static int parse_weights(AVFilterContext *ctx) if (s->scale == 0) { s->wfactor = 1 / s->wfactor; } else { + if (s->scale != 1.f / s->wfactor) + s->fast = 0; s->wfactor = s->scale; } @@ -145,13 +154,52 @@ typedef struct ThreadData { AVFrame **in, *out; } ThreadData; +#define FAST_TMIX_SLICE(type, stype, round) \ + for (int p = 0; p < s->nb_planes; p++) { \ + const int slice_start = (s->height[p] * jobnr) / nb_jobs; \ + const int slice_end = (s->height[p] * (jobnr+1)) / nb_jobs; \ + const int width = s->linesizes[p] / sizeof(type); \ + stype *sum = (stype *)(s->sum[p] + slice_start * s->linesizes[p] * 2); \ + type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ + const ptrdiff_t sum_linesize = (s->linesizes[p] * 2) / sizeof(stype); \ + const ptrdiff_t dst_linesize = out->linesize[p] / sizeof(type); \ + const int idx = FFMAX(0, nb_inputs - nb_unique); \ + const ptrdiff_t src_linesize[2] = { in[idx]->linesize[p], \ + in[nb_inputs-1]->linesize[p] }; \ + const type *src[2]; \ + \ + if (!((1 << p) & s->planes)) { \ + av_image_copy_plane((uint8_t *)dst, out->linesize[p], \ + in[0]->data[p] + slice_start * in[0]->linesize[p], \ + in[0]->linesize[p], \ + s->linesizes[p], slice_end - slice_start); \ + continue; \ + } \ + \ + src[0] = (const type *)(in[idx]->data[p] + slice_start*src_linesize[0]); \ + src[1] = (const type *)(in[nb_inputs-1]->data[p] + slice_start * src_linesize[1]); \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + for (int x = 0; x < width; x++) { \ + sum[x] += src[1][x] * (1 + (nb_inputs - 1) * (idx == (nb_inputs - 1))); \ + dst[x] = (sum[x] + (round)) / nb_inputs; \ + sum[x] -= src[0][x]; \ + } \ + \ + dst += dst_linesize; \ + sum += sum_linesize; \ + src[0] += src_linesize[0] / sizeof(type); \ + src[1] += src_linesize[1] / sizeof(type); \ + } \ + } + #define MIX_SLICE(type, fun, clip) \ for (int p = 0; p < s->nb_planes; p++) { \ const int slice_start = (s->height[p] * jobnr) / nb_jobs; \ const int slice_end = (s->height[p] * (jobnr+1)) / nb_jobs; \ const int width = s->linesizes[p] / sizeof(type); \ type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ - ptrdiff_t dst_linesize = out->linesize[p] / sizeof(type); \ + const ptrdiff_t dst_linesize = out->linesize[p] / sizeof(type); \ \ if (!((1 << p) & s->planes)) { \ av_image_copy_plane((uint8_t *)dst, out->linesize[p], \ @@ -161,27 +209,27 @@ typedef struct ThreadData { continue; \ } \ \ - for (int i = 0; i < s->nb_inputs; i++) \ + for (int i = 0; i < nb_inputs; i++) \ linesize[i] = in[i]->linesize[p]; \ \ - for (int i = 0; i < s->nb_inputs; i++) \ + for (int i = 0; i < nb_inputs; i++) \ srcf[i] = in[i]->data[p] + slice_start * linesize[i]; \ \ for (int y = slice_start; y < slice_end; y++) { \ for (int x = 0; x < width; x++) { \ float val = 0.f; \ \ - for (int i = 0; i < s->nb_inputs; i++) { \ + for (int i = 0; i < nb_inputs; i++) { \ float src = *(type *)(srcf[i] + x * sizeof(type)); \ \ val += src * weights[i]; \ } \ \ - dst[x] = clip(fun(val * s->wfactor), 0, s->max); \ + dst[x] = clip(fun(val * wfactor), 0, max); \ } \ \ dst += dst_linesize; \ - for (int i = 0; i < s->nb_inputs; i++) \ + for (int i = 0; i < nb_inputs; i++) \ srcf[i] += linesize[i]; \ } \ } @@ -200,6 +248,22 @@ static int mix_frames(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const float *weights = s->weights; uint8_t **srcf = s->data + jobnr * s->nb_inputs; int *linesize = s->linesize + jobnr * s->nb_inputs; + const int nb_unique = s->nb_unique_frames; + const int nb_inputs = s->nb_inputs; + const float wfactor = s->wfactor; + const int max = s->max; + + if (s->tmix && s->fast) { + if (s->depth <= 8) { + FAST_TMIX_SLICE(uint8_t, uint16_t, nb_inputs >> 1) + } else if (s->depth <= 16) { + FAST_TMIX_SLICE(uint16_t, uint32_t, nb_inputs >> 1) + } else { + FAST_TMIX_SLICE(float, float, 0.f) + } + + return 0; + } if (s->depth <= 8) { MIX_SLICE(uint8_t, lrintf, CLIP8) @@ -291,8 +355,14 @@ static int config_output(AVFilterLink *outlink) if (!s->linesize) return AVERROR(ENOMEM); - if (s->tmix) + if (s->tmix) { + for (int p = 0; p < s->nb_planes; p++) { + s->sum[p] = av_calloc(s->linesizes[p], s->height[p] * sizeof(*s->sum) * 2); + if (!s->sum[p]) + return AVERROR(ENOMEM); + } return 0; + } outlink->w = width; outlink->h = height; @@ -332,6 +402,8 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&s->linesize); if (s->tmix) { + for (i = 0; i < 4; i++) + av_freep(&s->sum[i]); for (i = 0; i < s->nb_frames && s->frames; i++) av_frame_free(&s->frames[i]); } @@ -365,10 +437,10 @@ static const AVOption mix_options[] = { { "weights", "set weight for each input", OFFSET(weights_str), AV_OPT_TYPE_STRING, {.str="1 1"}, 0, 0, .flags = TFLAGS }, { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, INT16_MAX, .flags = TFLAGS }, { "planes", "set what planes to filter", OFFSET(planes), AV_OPT_TYPE_FLAGS, {.i64=15}, 0, 15, .flags = TFLAGS }, - { "duration", "how to determine end of stream", OFFSET(duration), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, .flags = FLAGS, "duration" }, - { "longest", "Duration of longest input", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "duration" }, - { "shortest", "Duration of shortest input", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "duration" }, - { "first", "Duration of first input", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "duration" }, + { "duration", "how to determine end of stream", OFFSET(duration), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, .flags = FLAGS, .unit = "duration" }, + { "longest", "Duration of longest input", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "duration" }, + { "shortest", "Duration of shortest input", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "duration" }, + { "first", "Duration of first input", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "duration" }, { NULL }, }; @@ -415,6 +487,7 @@ static int tmix_filter_frame(AVFilterLink *inlink, AVFrame *in) if (s->nb_frames < s->nb_inputs) { s->frames[s->nb_frames] = in; s->nb_frames++; + s->nb_unique_frames++; while (s->nb_frames < s->nb_inputs) { s->frames[s->nb_frames] = av_frame_clone(s->frames[s->nb_frames - 1]); if (!s->frames[s->nb_frames]) @@ -422,6 +495,7 @@ static int tmix_filter_frame(AVFilterLink *inlink, AVFrame *in) s->nb_frames++; } } else { + s->nb_unique_frames = FFMIN(s->nb_unique_frames + 1, s->nb_inputs); av_frame_free(&s->frames[0]); memmove(&s->frames[0], &s->frames[1], sizeof(*s->frames) * (s->nb_inputs - 1)); s->frames[s->nb_inputs - 1] = in; diff --git a/libavfilter/vf_monochrome.c b/libavfilter/vf_monochrome.c index c77c3b8f194..05c001707ae 100644 --- a/libavfilter/vf_monochrome.c +++ b/libavfilter/vf_monochrome.c @@ -21,9 +21,8 @@ #include #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -90,9 +89,9 @@ static int monochrome_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_ const int height = frame->height; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ylinesize = frame->linesize[0]; - const int ulinesize = frame->linesize[1]; - const int vlinesize = frame->linesize[2]; + const ptrdiff_t ylinesize = frame->linesize[0]; + const ptrdiff_t ulinesize = frame->linesize[1]; + const ptrdiff_t vlinesize = frame->linesize[2]; uint8_t *yptr = frame->data[0] + slice_start * ylinesize; const float ihigh = 1.f - s->high; const float size = 1.f / s->size; @@ -129,9 +128,9 @@ static int monochrome_slice16(AVFilterContext *ctx, void *arg, int jobnr, int nb const int height = frame->height; const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ylinesize = frame->linesize[0] / 2; - const int ulinesize = frame->linesize[1] / 2; - const int vlinesize = frame->linesize[2] / 2; + const ptrdiff_t ylinesize = frame->linesize[0] / 2; + const ptrdiff_t ulinesize = frame->linesize[1] / 2; + const ptrdiff_t vlinesize = frame->linesize[2] / 2; uint16_t *yptr = (uint16_t *)frame->data[0] + slice_start * ylinesize; const float ihigh = 1.f - s->high; const float size = 1.f / s->size; @@ -167,8 +166,8 @@ static int clear_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const int height = AV_CEIL_RSHIFT(frame->height, subh); const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ulinesize = frame->linesize[1]; - const int vlinesize = frame->linesize[2]; + const ptrdiff_t ulinesize = frame->linesize[1]; + const ptrdiff_t vlinesize = frame->linesize[2]; for (int y = slice_start; y < slice_end; y++) { uint8_t *uptr = frame->data[1] + y * ulinesize; @@ -193,8 +192,8 @@ static int clear_slice16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs const int height = AV_CEIL_RSHIFT(frame->height, subh); const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int ulinesize = frame->linesize[1] / 2; - const int vlinesize = frame->linesize[2] / 2; + const ptrdiff_t ulinesize = frame->linesize[1] / 2; + const ptrdiff_t vlinesize = frame->linesize[2] / 2; for (int y = slice_start; y < slice_end; y++) { uint16_t *uptr = (uint16_t *)frame->data[1] + y * ulinesize; @@ -268,13 +267,6 @@ static const AVFilterPad monochrome_inputs[] = { }, }; -static const AVFilterPad monochrome_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(MonochromeContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -294,7 +286,7 @@ const AVFilter ff_vf_monochrome = { .priv_size = sizeof(MonochromeContext), .priv_class = &monochrome_class, FILTER_INPUTS(monochrome_inputs), - FILTER_OUTPUTS(monochrome_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_morpho.c b/libavfilter/vf_morpho.c index f91957ab810..35d688a8492 100644 --- a/libavfilter/vf_morpho.c +++ b/libavfilter/vf_morpho.c @@ -29,7 +29,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "framesync.h" #include "internal.h" #include "video.h" @@ -95,6 +94,8 @@ typedef struct chord_set { unsigned nb_elements; } chord_set; +#define MAX_THREADS 64 + typedef struct MorphoContext { const AVClass *class; FFFrameSync fs; @@ -102,7 +103,7 @@ typedef struct MorphoContext { chord_set SE[4]; IPlane SEimg[4]; IPlane g[4], f[4], h[4]; - LUT Ty[2][4]; + LUT Ty[MAX_THREADS][2][4]; int mode; int planes; @@ -127,18 +128,18 @@ typedef struct MorphoContext { #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_RUNTIME_PARAM static const AVOption morpho_options[] = { - { "mode", "set morphological transform", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_MODES-1, FLAGS, "mode" }, - { "erode", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ERODE}, 0, 0, FLAGS, "mode" }, - { "dilate", NULL, 0, AV_OPT_TYPE_CONST, {.i64=DILATE}, 0, 0, FLAGS, "mode" }, - { "open", NULL, 0, AV_OPT_TYPE_CONST, {.i64=OPEN}, 0, 0, FLAGS, "mode" }, - { "close", NULL, 0, AV_OPT_TYPE_CONST, {.i64=CLOSE}, 0, 0, FLAGS, "mode" }, - { "gradient",NULL, 0, AV_OPT_TYPE_CONST, {.i64=GRADIENT},0, 0, FLAGS, "mode" }, - { "tophat",NULL, 0, AV_OPT_TYPE_CONST, {.i64=TOPHAT}, 0, 0, FLAGS, "mode" }, - { "blackhat",NULL, 0, AV_OPT_TYPE_CONST, {.i64=BLACKHAT},0, 0, FLAGS, "mode" }, + { "mode", "set morphological transform", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_MODES-1, FLAGS, .unit = "mode" }, + { "erode", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ERODE}, 0, 0, FLAGS, .unit = "mode" }, + { "dilate", NULL, 0, AV_OPT_TYPE_CONST, {.i64=DILATE}, 0, 0, FLAGS, .unit = "mode" }, + { "open", NULL, 0, AV_OPT_TYPE_CONST, {.i64=OPEN}, 0, 0, FLAGS, .unit = "mode" }, + { "close", NULL, 0, AV_OPT_TYPE_CONST, {.i64=CLOSE}, 0, 0, FLAGS, .unit = "mode" }, + { "gradient",NULL, 0, AV_OPT_TYPE_CONST, {.i64=GRADIENT},0, 0, FLAGS, .unit = "mode" }, + { "tophat",NULL, 0, AV_OPT_TYPE_CONST, {.i64=TOPHAT}, 0, 0, FLAGS, .unit = "mode" }, + { "blackhat",NULL, 0, AV_OPT_TYPE_CONST, {.i64=BLACKHAT},0, 0, FLAGS, .unit = "mode" }, { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=7}, 0, 15, FLAGS }, - { "structure", "when to process structures", OFFSET(structures), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "str" }, - { "first", "process only first structure, ignore rest", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "str" }, - { "all", "process all structure", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "str" }, + { "structure", "when to process structures", OFFSET(structures), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "str" }, + { "first", "process only first structure, ignore rest", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "str" }, + { "all", "process all structure", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "str" }, { NULL } }; @@ -318,7 +319,7 @@ static void free_lut(LUT *table) } static int alloc_lut_if_necessary(LUT *Ty, IPlane *f, chord_set *SE, - int y, int num, enum MorphModes mode) + int num, enum MorphModes mode) { if (!Ty->arr || Ty->I != SE->Lnum || Ty->X != f->w || @@ -387,7 +388,7 @@ static void update_min_lut(IPlane *f, LUT *Ty, chord_set *SE, int y, int tid, in static int compute_min_lut(LUT *Ty, IPlane *f, chord_set *SE, int y, int num) { - int ret = alloc_lut_if_necessary(Ty, f, SE, y, num, ERODE); + int ret = alloc_lut_if_necessary(Ty, f, SE, num, ERODE); if (ret < 0) return ret; @@ -428,7 +429,7 @@ static void update_max_lut(IPlane *f, LUT *Ty, chord_set *SE, int y, int tid, in static int compute_max_lut(LUT *Ty, IPlane *f, chord_set *SE, int y, int num) { - int ret = alloc_lut_if_necessary(Ty, f, SE, y, num, DILATE); + int ret = alloc_lut_if_necessary(Ty, f, SE, num, DILATE); if (ret < 0) return ret; @@ -460,14 +461,14 @@ static void line_erode(IPlane *g, LUT *Ty, chord_set *SE, int y, int tid) } } -static int dilate(IPlane *g, IPlane *f, chord_set *SE, LUT *Ty) +static int dilate(IPlane *g, IPlane *f, chord_set *SE, LUT *Ty, int y0, int y1) { - int ret = compute_max_lut(Ty, f, SE, 0, 1); + int ret = compute_max_lut(Ty, f, SE, y0, 1); if (ret < 0) return ret; - line_dilate(g, Ty, SE, 0, 0); - for (int y = 1; y < f->h; y++) { + line_dilate(g, Ty, SE, y0, 0); + for (int y = y0 + 1; y < y1; y++) { update_max_lut(f, Ty, SE, y, 0, 1); line_dilate(g, Ty, SE, y, 0); } @@ -475,14 +476,14 @@ static int dilate(IPlane *g, IPlane *f, chord_set *SE, LUT *Ty) return 0; } -static int erode(IPlane *g, IPlane *f, chord_set *SE, LUT *Ty) +static int erode(IPlane *g, IPlane *f, chord_set *SE, LUT *Ty, int y0, int y1) { - int ret = compute_min_lut(Ty, f, SE, 0, 1); + int ret = compute_min_lut(Ty, f, SE, y0, 1); if (ret < 0) return ret; - line_erode(g, Ty, SE, 0, 0); - for (int y = 1; y < f->h; y++) { + line_erode(g, Ty, SE, y0, 0); + for (int y = y0 + 1; y < y1; y++) { update_min_lut(f, Ty, SE, y, 0, 1); line_erode(g, Ty, SE, y, 0); } @@ -490,15 +491,15 @@ static int erode(IPlane *g, IPlane *f, chord_set *SE, LUT *Ty) return 0; } -static void difference(IPlane *g, IPlane *f) +static void difference(IPlane *g, IPlane *f, int y0, int y1) { - for (int y = 0; y < f->h; y++) + for (int y = y0; y < y1; y++) f->diff_in_place(g->img[y], f->img[y], f->w); } -static void difference2(IPlane *g, IPlane *f) +static void difference2(IPlane *g, IPlane *f, int y0, int y1) { - for (int y = 0; y < f->h; y++) + for (int y = y0; y < y1; y++) f->diff_rin_place(g->img[y], f->img[y], f->w); } @@ -785,12 +786,133 @@ static int activate(AVFilterContext *ctx) return ff_framesync_activate(&s->fs); } +typedef struct ThreadData { + AVFrame *in, *out; +} ThreadData; + +static int morpho_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + MorphoContext *s = ctx->priv; + ThreadData *td = arg; + AVFrame *out = td->out; + AVFrame *in = td->in; + int ret; + + for (int p = 0; p < s->nb_planes; p++) { + const int width = s->planewidth[p]; + const int height = s->planeheight[p]; + const int y0 = (height * jobnr ) / nb_jobs; + const int y1 = (height * (jobnr+1)) / nb_jobs; + const int depth = s->depth; + + if (ctx->is_disabled || !(s->planes & (1 << p))) { +copy: + av_image_copy_plane(out->data[p] + y0 * out->linesize[p], + out->linesize[p], + in->data[p] + y0 * in->linesize[p], + in->linesize[p], + width * ((depth + 7) / 8), + y1 - y0); + continue; + } + + if (s->SE[p].minX == INT16_MAX || + s->SE[p].minY == INT16_MAX || + s->SE[p].maxX == INT16_MIN || + s->SE[p].maxY == INT16_MIN) + goto copy; + + switch (s->mode) { + case ERODE: + ret = erode(&s->g[p], &s->f[p], &s->SE[p], &s->Ty[jobnr][0][p], y0, y1); + break; + case DILATE: + case GRADIENT: + ret = dilate(&s->g[p], &s->f[p], &s->SE[p], &s->Ty[jobnr][0][p], y0, y1); + break; + case OPEN: + case TOPHAT: + ret = erode(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[jobnr][0][p], y0, y1); + break; + case CLOSE: + case BLACKHAT: + ret = dilate(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[jobnr][0][p], y0, y1); + break; + default: + av_assert0(0); + } + + if (ret < 0) + return ret; + } + + return 0; +} + +static int morpho_sliceX(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) +{ + MorphoContext *s = ctx->priv; + int ret; + + for (int p = 0; p < s->nb_planes; p++) { + const int height = s->planeheight[p]; + const int y0 = (height * jobnr ) / nb_jobs; + const int y1 = (height * (jobnr+1)) / nb_jobs; + + if (ctx->is_disabled || !(s->planes & (1 << p))) { +copy: + continue; + } + + if (s->SE[p].minX == INT16_MAX || + s->SE[p].minY == INT16_MAX || + s->SE[p].maxX == INT16_MIN || + s->SE[p].maxY == INT16_MIN) + goto copy; + + switch (s->mode) { + case OPEN: + ret = dilate(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[jobnr][1][p], y0, y1); + break; + case CLOSE: + ret = erode(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[jobnr][1][p], y0, y1); + break; + case GRADIENT: + ret = erode(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[jobnr][1][p], y0, y1); + if (ret < 0) + break; + difference(&s->g[p], &s->h[p], y0, y1); + break; + case TOPHAT: + ret = dilate(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[jobnr][1][p], y0, y1); + if (ret < 0) + break; + difference2(&s->g[p], &s->f[p], y0, y1); + break; + case BLACKHAT: + ret = erode(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[jobnr][1][p], y0, y1); + if (ret < 0) + break; + difference(&s->g[p], &s->f[p], y0, y1); + break; + default: + av_assert0(0); + } + + if (ret < 0) + return ret; + } + + return 0; +} + static int do_morpho(FFFrameSync *fs) { AVFilterContext *ctx = fs->parent; AVFilterLink *outlink = ctx->outputs[0]; MorphoContext *s = ctx->priv; AVFrame *in = NULL, *structurepic = NULL; + ThreadData td; AVFrame *out; int ret; @@ -808,30 +930,19 @@ static int do_morpho(FFFrameSync *fs) av_frame_copy_props(out, in); for (int p = 0; p < s->nb_planes; p++) { - const uint8_t *src = in->data[p]; - int src_linesize = in->linesize[p]; const uint8_t *ssrc = structurepic->data[p]; const int ssrc_linesize = structurepic->linesize[p]; - uint8_t *dst = out->data[p]; - int dst_linesize = out->linesize[p]; const int swidth = s->splanewidth[p]; const int sheight = s->splaneheight[p]; + const uint8_t *src = in->data[p]; + int src_linesize = in->linesize[p]; + uint8_t *dst = out->data[p]; + int dst_linesize = out->linesize[p]; const int width = s->planewidth[p]; const int height = s->planeheight[p]; const int depth = s->depth; int type_size = s->type_size; - if (ctx->is_disabled || !(s->planes & (1 << p))) { -copy: - av_image_copy_plane(out->data[p] + 0 * out->linesize[p], - out->linesize[p], - in->data[p] + 0 * in->linesize[p], - in->linesize[p], - width * ((s->depth + 7) / 8), - height); - continue; - } - if (!s->got_structure[p] || s->structures) { free_chord_set(&s->SE[p]); @@ -844,12 +955,6 @@ static int do_morpho(FFFrameSync *fs) s->got_structure[p] = 1; } - if (s->SE[p].minX == INT16_MAX || - s->SE[p].minY == INT16_MAX || - s->SE[p].maxX == INT16_MIN || - s->SE[p].maxY == INT16_MIN) - goto copy; - ret = read_iplane(&s->f[p], src, src_linesize, width, height, 1, type_size, depth); if (ret < 0) goto fail; @@ -859,74 +964,29 @@ static int do_morpho(FFFrameSync *fs) goto fail; switch (s->mode) { - case ERODE: - ret = erode(&s->g[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - break; - case DILATE: - ret = dilate(&s->g[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - break; case OPEN: - ret = read_iplane(&s->h[p], s->temp->data[p], s->temp->linesize[p], width, height, 1, type_size, depth); - if (ret < 0) - break; - ret = erode(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - if (ret < 0) - break; - ret = dilate(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[1][p]); - break; case CLOSE: - ret = read_iplane(&s->h[p], s->temp->data[p], s->temp->linesize[p], width, height, 1, type_size, depth); - if (ret < 0) - break; - ret = dilate(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - if (ret < 0) - break; - ret = erode(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[1][p]); - break; case GRADIENT: - ret = read_iplane(&s->h[p], s->temp->data[p], s->temp->linesize[p], width, height, 1, type_size, depth); - if (ret < 0) - break; - ret = dilate(&s->g[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - if (ret < 0) - break; - ret = erode(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[1][p]); - if (ret < 0) - break; - difference(&s->g[p], &s->h[p]); - break; case TOPHAT: - ret = read_iplane(&s->h[p], s->temp->data[p], s->temp->linesize[p], width, height, 1, type_size, depth); - if (ret < 0) - break; - ret = erode(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - if (ret < 0) - break; - ret = dilate(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[1][p]); - if (ret < 0) - break; - difference2(&s->g[p], &s->f[p]); - break; case BLACKHAT: ret = read_iplane(&s->h[p], s->temp->data[p], s->temp->linesize[p], width, height, 1, type_size, depth); - if (ret < 0) - break; - ret = dilate(&s->h[p], &s->f[p], &s->SE[p], &s->Ty[0][p]); - if (ret < 0) - break; - ret = erode(&s->g[p], &s->h[p], &s->SE[p], &s->Ty[1][p]); - if (ret < 0) - break; - difference(&s->g[p], &s->f[p]); break; - default: - av_assert0(0); } if (ret < 0) goto fail; } + td.in = in; td.out = out; + ret = ff_filter_execute(ctx, morpho_slice, &td, NULL, + FFMIN3(s->planeheight[1], s->planeheight[2], + FFMIN(MAX_THREADS, ff_filter_get_nb_threads(ctx)))); + if (ret == 0 && (s->mode != ERODE && s->mode != DILATE)) { + ff_filter_execute(ctx, morpho_sliceX, NULL, NULL, + FFMIN3(s->planeheight[1], s->planeheight[2], + FFMIN(MAX_THREADS, ff_filter_get_nb_threads(ctx)))); + } + av_frame_free(&in); out->pts = av_rescale_q(s->fs.pts, s->fs.time_base, outlink->time_base); return ff_filter_frame(outlink, out); @@ -979,8 +1039,10 @@ static av_cold void uninit(AVFilterContext *ctx) free_iplane(&s->g[p]); free_iplane(&s->h[p]); free_chord_set(&s->SE[p]); - free_lut(&s->Ty[0][p]); - free_lut(&s->Ty[1][p]); + for (int n = 0; n < MAX_THREADS; n++) { + free_lut(&s->Ty[n][0][p]); + free_lut(&s->Ty[n][1][p]); + } } ff_framesync_uninit(&s->fs); @@ -1022,6 +1084,7 @@ const AVFilter ff_vf_morpho = { FILTER_INPUTS(morpho_inputs), FILTER_OUTPUTS(morpho_outputs), FILTER_PIXFMTS_ARRAY(pix_fmts), - .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | + AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vf_mpdecimate.c b/libavfilter/vf_mpdecimate.c index 71f673cb646..dab8941e46a 100644 --- a/libavfilter/vf_mpdecimate.c +++ b/libavfilter/vf_mpdecimate.c @@ -30,7 +30,6 @@ #include "libavutil/timestamp.h" #include "avfilter.h" #include "internal.h" -#include "formats.h" #include "video.h" typedef struct DecimateContext { @@ -46,6 +45,9 @@ typedef struct DecimateContext { int drop_count; ///< if positive: number of frames sequentially dropped ///< if negative: number of sequential frames which were not dropped + int max_keep_count; ///< number of similar frames to ignore before to start dropping them + int keep_count; ///< number of similar frames already ignored + int hsub, vsub; ///< chroma subsampling values AVFrame *ref; ///< reference picture av_pixelutils_sad_fn sad; ///< sum of absolute difference function @@ -57,6 +59,8 @@ typedef struct DecimateContext { static const AVOption mpdecimate_options[] = { { "max", "set the maximum number of consecutive dropped frames (positive), or the minimum interval between dropped frames (negative)", OFFSET(max_drop_count), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGS }, + { "keep", "set the number of similar consecutive frames to be kept before starting to drop similar frames", + OFFSET(max_keep_count), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS }, { "hi", "set high dropping threshold", OFFSET(hi), AV_OPT_TYPE_INT, {.i64=64*12}, INT_MIN, INT_MAX, FLAGS }, { "lo", "set low dropping threshold", OFFSET(lo), AV_OPT_TYPE_INT, {.i64=64*5}, INT_MIN, INT_MAX, FLAGS }, { "frac", "set fraction dropping threshold", OFFSET(frac), AV_OPT_TYPE_FLOAT, {.dbl=0.33}, 0, 1, FLAGS }, @@ -112,6 +116,12 @@ static int decimate_frame(AVFilterContext *ctx, DecimateContext *decimate = ctx->priv; int plane; + if (decimate->max_keep_count > 0 && + decimate->keep_count > -1 && + decimate->keep_count < decimate->max_keep_count) { + decimate->keep_count++; + return 0; + } if (decimate->max_drop_count > 0 && decimate->drop_count >= decimate->max_drop_count) return 0; @@ -131,13 +141,10 @@ static int decimate_frame(AVFilterContext *ctx, cur->data[plane], cur->linesize[plane], ref->data[plane], ref->linesize[plane], AV_CEIL_RSHIFT(ref->width, hsub), - AV_CEIL_RSHIFT(ref->height, vsub))) { - emms_c(); + AV_CEIL_RSHIFT(ref->height, vsub))) return 0; - } } - emms_c(); return 1; } @@ -196,20 +203,24 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *cur) if (decimate->ref && decimate_frame(inlink->dst, cur, decimate->ref)) { decimate->drop_count = FFMAX(1, decimate->drop_count+1); + decimate->keep_count = -1; // do not keep any more frames until non-similar frames are detected } else { av_frame_free(&decimate->ref); decimate->ref = cur; decimate->drop_count = FFMIN(-1, decimate->drop_count-1); + if (decimate->keep_count < 0) // re-enable counting similiar frames to ignore before dropping + decimate->keep_count = 0; if ((ret = ff_filter_frame(outlink, av_frame_clone(cur))) < 0) return ret; } av_log(inlink->dst, AV_LOG_DEBUG, - "%s pts:%s pts_time:%s drop_count:%d\n", + "%s pts:%s pts_time:%s drop_count:%d keep_count:%d\n", decimate->drop_count > 0 ? "drop" : "keep", av_ts2str(cur->pts), av_ts2timestr(cur->pts, &inlink->time_base), - decimate->drop_count); + decimate->drop_count, + decimate->keep_count); if (decimate->drop_count > 0) av_frame_free(&cur); @@ -226,13 +237,6 @@ static const AVFilterPad mpdecimate_inputs[] = { }, }; -static const AVFilterPad mpdecimate_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_mpdecimate = { .name = "mpdecimate", .description = NULL_IF_CONFIG_SMALL("Remove near-duplicate frames."), @@ -241,6 +245,6 @@ const AVFilter ff_vf_mpdecimate = { .priv_size = sizeof(DecimateContext), .priv_class = &mpdecimate_class, FILTER_INPUTS(mpdecimate_inputs), - FILTER_OUTPUTS(mpdecimate_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/vf_multiply.c b/libavfilter/vf_multiply.c index 979b885eb19..54fbeff4831 100644 --- a/libavfilter/vf_multiply.c +++ b/libavfilter/vf_multiply.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "framesync.h" diff --git a/libavfilter/vf_negate.c b/libavfilter/vf_negate.c index d782e63d3a2..40c0c2608b7 100644 --- a/libavfilter/vf_negate.c +++ b/libavfilter/vf_negate.c @@ -16,14 +16,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/attributes.h" #include "libavutil/common.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -64,14 +62,14 @@ typedef struct NegateContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption negate_options[] = { - { "components", "set components to negate", OFFSET(requested_components), AV_OPT_TYPE_FLAGS, {.i64=0x77}, 1, 0xff, FLAGS, "flags"}, - { "y", "set luma component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_Y}, 0, 0, FLAGS, "flags"}, - { "u", "set u component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_U}, 0, 0, FLAGS, "flags"}, - { "v", "set v component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_V}, 0, 0, FLAGS, "flags"}, - { "r", "set red component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_R}, 0, 0, FLAGS, "flags"}, - { "g", "set green component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_G}, 0, 0, FLAGS, "flags"}, - { "b", "set blue component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_B}, 0, 0, FLAGS, "flags"}, - { "a", "set alpha component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_A}, 0, 0, FLAGS, "flags"}, + { "components", "set components to negate", OFFSET(requested_components), AV_OPT_TYPE_FLAGS, {.i64=0x77}, 1, 0xff, FLAGS, .unit = "flags"}, + { "y", "set luma component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_Y}, 0, 0, FLAGS, .unit = "flags"}, + { "u", "set u component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_U}, 0, 0, FLAGS, .unit = "flags"}, + { "v", "set v component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_V}, 0, 0, FLAGS, .unit = "flags"}, + { "r", "set red component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_R}, 0, 0, FLAGS, .unit = "flags"}, + { "g", "set green component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_G}, 0, 0, FLAGS, .unit = "flags"}, + { "b", "set blue component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_B}, 0, 0, FLAGS, .unit = "flags"}, + { "a", "set alpha component", 0, AV_OPT_TYPE_CONST, {.i64=COMP_A}, 0, 0, FLAGS, .unit = "flags"}, { "negate_alpha", NULL, OFFSET(negate_alpha), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { NULL } }; @@ -89,6 +87,8 @@ static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUV420P12, AV_PIX_FMT_YUV440P12, AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV444P16, AV_PIX_FMT_YUV422P16, AV_PIX_FMT_YUV420P16, + AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA420P10, + AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_ARGB, AV_PIX_FMT_RGBA, AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA, @@ -97,7 +97,7 @@ static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_BGR48, AV_PIX_FMT_BGRA64, AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, - AV_PIX_FMT_GBRAP10, + AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP14, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, @@ -355,20 +355,13 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_negate = { .name = "negate", .description = NULL_IF_CONFIG_SMALL("Negate input video."), .priv_size = sizeof(NegateContext), .priv_class = &negate_class, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_neighbor.c b/libavfilter/vf_neighbor.c index d6638779e59..915347d6ba9 100644 --- a/libavfilter/vf_neighbor.c +++ b/libavfilter/vf_neighbor.c @@ -26,7 +26,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -341,13 +340,6 @@ static const AVFilterPad neighbor_inputs[] = { }, }; -static const AVFilterPad neighbor_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(NContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -358,7 +350,7 @@ const AVFilter ff_vf_##name_ = { \ .priv_class = &priv_class_##_class, \ .priv_size = sizeof(NContext), \ FILTER_INPUTS(neighbor_inputs), \ - FILTER_OUTPUTS(neighbor_outputs), \ + FILTER_OUTPUTS(ff_video_default_filterpad), \ FILTER_PIXFMTS_ARRAY(pix_fmts), \ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC| \ AVFILTER_FLAG_SLICE_THREADS, \ diff --git a/libavfilter/vf_neighbor_opencl.c b/libavfilter/vf_neighbor_opencl.c index 3b83cee3da6..b2939f841a2 100644 --- a/libavfilter/vf_neighbor_opencl.c +++ b/libavfilter/vf_neighbor_opencl.c @@ -55,7 +55,7 @@ static int neighbor_opencl_init(AVFilterContext *avctx) cl_int cle; int err; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_neighbor, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_neighbor_cl, 1); if (err < 0) goto fail; @@ -312,6 +312,7 @@ const AVFilter ff_vf_dilation_opencl = { FILTER_OUTPUTS(neighbor_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif /* CONFIG_DILATION_OPENCL_FILTER */ diff --git a/libavfilter/vf_nlmeans.c b/libavfilter/vf_nlmeans.c index 2fc3adacca1..dc935538f05 100644 --- a/libavfilter/vf_nlmeans.c +++ b/libavfilter/vf_nlmeans.c @@ -33,7 +33,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "vf_nlmeans.h" #include "vf_nlmeans_init.h" @@ -472,13 +471,6 @@ static const AVFilterPad nlmeans_inputs[] = { }, }; -static const AVFilterPad nlmeans_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_nlmeans = { .name = "nlmeans", .description = NULL_IF_CONFIG_SMALL("Non-local means denoiser."), @@ -486,7 +478,7 @@ const AVFilter ff_vf_nlmeans = { .init = init, .uninit = uninit, FILTER_INPUTS(nlmeans_inputs), - FILTER_OUTPUTS(nlmeans_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &nlmeans_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_nlmeans_opencl.c b/libavfilter/vf_nlmeans_opencl.c index c0ac8bf95dd..5149be02ca4 100644 --- a/libavfilter/vf_nlmeans_opencl.c +++ b/libavfilter/vf_nlmeans_opencl.c @@ -98,7 +98,7 @@ static int nlmeans_opencl_init(AVFilterContext *avctx, int width, int height) if (!ctx->patch_size_uv) ctx->patch_size_uv = ctx->patch_size; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_nlmeans, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_nlmeans_cl, 1); if (err < 0) goto fail; @@ -438,4 +438,5 @@ const AVFilter ff_vf_nlmeans_opencl = { FILTER_OUTPUTS(nlmeans_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_nlmeans_vulkan.c b/libavfilter/vf_nlmeans_vulkan.c new file mode 100644 index 00000000000..fac38d16f47 --- /dev/null +++ b/libavfilter/vf_nlmeans_vulkan.c @@ -0,0 +1,1113 @@ +/* + * Copyright (c) Lynne + * + * 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/random_seed.h" +#include "libavutil/opt.h" +#include "vulkan_filter.h" +#include "vulkan_spirv.h" +#include "internal.h" +#include "video.h" + +#define TYPE_NAME "vec4" +#define TYPE_ELEMS 4 +#define TYPE_SIZE (TYPE_ELEMS*4) + +typedef struct NLMeansVulkanContext { + FFVulkanContext vkctx; + + int initialized; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + VkSampler sampler; + + AVBufferPool *integral_buf_pool; + AVBufferPool *ws_buf_pool; + + FFVkBuffer xyoffsets_buf; + + int pl_weights_rows; + FFVulkanPipeline pl_weights; + FFVkSPIRVShader shd_weights; + + FFVulkanPipeline pl_denoise; + FFVkSPIRVShader shd_denoise; + + int *xoffsets; + int *yoffsets; + int nb_offsets; + float strength[4]; + int patch[4]; + + struct nlmeans_opts { + int r; + double s; + double sc[4]; + int p; + int pc[4]; + int t; + } opts; +} NLMeansVulkanContext; + +extern const char *ff_source_prefix_sum_comp; + +static void insert_first(FFVkSPIRVShader *shd, int r, const char *off, int horiz, int plane, int comp) +{ + GLSLF(4, s1 = texture(input_img[%i], pos + ivec2(%i + %s, %i + %s))[%i]; + ,plane, horiz ? r : 0, horiz ? off : "0", !horiz ? r : 0, !horiz ? off : "0", comp); + + GLSLF(4, s2[0] = texture(input_img[%i], pos + offs[0] + ivec2(%i + %s, %i + %s))[%i]; + ,plane, horiz ? r : 0, horiz ? off : "0", !horiz ? r : 0, !horiz ? off : "0", comp); + GLSLF(4, s2[1] = texture(input_img[%i], pos + offs[1] + ivec2(%i + %s, %i + %s))[%i]; + ,plane, horiz ? r : 0, horiz ? off : "0", !horiz ? r : 0, !horiz ? off : "0", comp); + GLSLF(4, s2[2] = texture(input_img[%i], pos + offs[2] + ivec2(%i + %s, %i + %s))[%i]; + ,plane, horiz ? r : 0, horiz ? off : "0", !horiz ? r : 0, !horiz ? off : "0", comp); + GLSLF(4, s2[3] = texture(input_img[%i], pos + offs[3] + ivec2(%i + %s, %i + %s))[%i]; + ,plane, horiz ? r : 0, horiz ? off : "0", !horiz ? r : 0, !horiz ? off : "0", comp); + + GLSLC(4, s2 = (s1 - s2) * (s1 - s2); ); +} + +static void insert_horizontal_pass(FFVkSPIRVShader *shd, int nb_rows, int first, int plane, int comp) +{ + GLSLF(1, pos.y = int(gl_GlobalInvocationID.x) * %i; ,nb_rows); + if (!first) + GLSLC(1, barrier(); ); + GLSLC(0, ); + GLSLF(1, if (pos.y < height[%i]) { ,plane); + GLSLC(2, #pragma unroll(1) ); + GLSLF(2, for (r = 0; r < %i; r++) { ,nb_rows); + GLSLC(3, prefix_sum = DTYPE(0); ); + GLSLC(3, offset = int_stride * uint64_t(pos.y + r); ); + GLSLC(3, dst = DataBuffer(uint64_t(integral_data) + offset); ); + GLSLC(0, ); + GLSLF(3, for (pos.x = 0; pos.x < width[%i]; pos.x++) { ,plane); + if (first) + insert_first(shd, 0, "r", 0, plane, comp); + else + GLSLC(4, s2 = dst.v[pos.x]; ); + GLSLC(4, dst.v[pos.x] = s2 + prefix_sum; ); + GLSLC(4, prefix_sum += s2; ); + GLSLC(3, } ); + GLSLC(2, } ); + GLSLC(1, } ); + GLSLC(0, ); +} + +static void insert_vertical_pass(FFVkSPIRVShader *shd, int nb_rows, int first, int plane, int comp) +{ + GLSLF(1, pos.x = int(gl_GlobalInvocationID.x) * %i; ,nb_rows); + GLSLC(1, #pragma unroll(1) ); + GLSLF(1, for (r = 0; r < %i; r++) ,nb_rows); + GLSLC(2, psum[r] = DTYPE(0); ); + GLSLC(0, ); + if (!first) + GLSLC(1, barrier(); ); + GLSLC(0, ); + GLSLF(1, if (pos.x < width[%i]) { ,plane); + GLSLF(2, for (pos.y = 0; pos.y < height[%i]; pos.y++) { ,plane); + GLSLC(3, offset = int_stride * uint64_t(pos.y); ); + GLSLC(3, dst = DataBuffer(uint64_t(integral_data) + offset); ); + GLSLC(0, ); + GLSLC(3, #pragma unroll(1) ); + GLSLF(3, for (r = 0; r < %i; r++) { ,nb_rows); + if (first) + insert_first(shd, 0, "r", 1, plane, comp); + else + GLSLC(4, s2 = dst.v[pos.x + r]; ); + GLSLC(4, dst.v[pos.x + r] = s2 + psum[r]; ); + GLSLC(4, psum[r] += s2; ); + GLSLC(3, } ); + GLSLC(2, } ); + GLSLC(1, } ); + GLSLC(0, ); +} + +static void insert_weights_pass(FFVkSPIRVShader *shd, int nb_rows, int vert, + int t, int dst_comp, int plane, int comp) +{ + GLSLF(1, p = patch_size[%i]; ,dst_comp); + GLSLC(0, ); + GLSLC(1, barrier(); ); + GLSLC(0, ); + if (!vert) { + GLSLF(1, for (pos.y = 0; pos.y < height[%i]; pos.y++) { ,plane); + GLSLF(2, if (gl_GlobalInvocationID.x*%i >= width[%i]) ,nb_rows, plane); + GLSLC(3, break; ); + GLSLF(2, for (r = 0; r < %i; r++) { ,nb_rows); + GLSLF(3, pos.x = int(gl_GlobalInvocationID.x) * %i + r; ,nb_rows); + } else { + GLSLF(1, for (pos.x = 0; pos.x < width[%i]; pos.x++) { ,plane); + GLSLF(2, if (gl_GlobalInvocationID.x*%i >= height[%i]) ,nb_rows, plane); + GLSLC(3, break; ); + GLSLF(2, for (r = 0; r < %i; r++) { ,nb_rows); + GLSLF(3, pos.y = int(gl_GlobalInvocationID.x) * %i + r; ,nb_rows); + } + GLSLC(0, ); + GLSLC(3, a = DTYPE(0); ); + GLSLC(3, b = DTYPE(0); ); + GLSLC(3, c = DTYPE(0); ); + GLSLC(3, d = DTYPE(0); ); + GLSLC(0, ); + GLSLC(3, lt = ((pos.x - p) < 0) || ((pos.y - p) < 0); ); + GLSLC(0, ); + GLSLF(3, src[0] = texture(input_img[%i], pos + offs[0])[%i]; ,plane, comp); + GLSLF(3, src[1] = texture(input_img[%i], pos + offs[1])[%i]; ,plane, comp); + GLSLF(3, src[2] = texture(input_img[%i], pos + offs[2])[%i]; ,plane, comp); + GLSLF(3, src[3] = texture(input_img[%i], pos + offs[3])[%i]; ,plane, comp); + GLSLC(0, ); + GLSLC(3, if (lt == false) { ); + GLSLC(3, offset = int_stride * uint64_t(pos.y - p); ); + GLSLC(3, dst = DataBuffer(uint64_t(integral_data) + offset); ); + GLSLC(4, a = dst.v[pos.x - p]; ); + GLSLC(4, c = dst.v[pos.x + p]; ); + GLSLC(3, offset = int_stride * uint64_t(pos.y + p); ); + GLSLC(3, dst = DataBuffer(uint64_t(integral_data) + offset); ); + GLSLC(4, b = dst.v[pos.x - p]; ); + GLSLC(4, d = dst.v[pos.x + p]; ); + GLSLC(3, } ); + GLSLC(0, ); + GLSLC(3, patch_diff = d + a - b - c; ); + GLSLF(3, w = exp(patch_diff * strength[%i]); ,dst_comp); + GLSLC(3, w_sum = w[0] + w[1] + w[2] + w[3]; ); + GLSLC(3, sum = dot(w, src*255); ); + GLSLC(0, ); + if (t > 1) { + GLSLF(3, atomicAdd(weights_%i[pos.y*ws_stride[%i] + pos.x], w_sum); ,dst_comp, dst_comp); + GLSLF(3, atomicAdd(sums_%i[pos.y*ws_stride[%i] + pos.x], sum); ,dst_comp, dst_comp); + } else { + GLSLF(3, weights_%i[pos.y*ws_stride[%i] + pos.x] += w_sum; ,dst_comp, dst_comp); + GLSLF(3, sums_%i[pos.y*ws_stride[%i] + pos.x] += sum; ,dst_comp, dst_comp); + } + GLSLC(2, } ); + GLSLC(1, } ); +} + +typedef struct HorizontalPushData { + uint32_t width[4]; + uint32_t height[4]; + uint32_t ws_stride[4]; + int32_t patch_size[4]; + float strength[4]; + VkDeviceAddress integral_base; + uint64_t integral_size; + uint64_t int_stride; + uint32_t xyoffs_start; +} HorizontalPushData; + +static av_cold int init_weights_pipeline(FFVulkanContext *vkctx, FFVkExecPool *exec, + FFVulkanPipeline *pl, FFVkSPIRVShader *shd, + VkSampler sampler, FFVkSPIRVCompiler *spv, + int width, int height, int t, + const AVPixFmtDescriptor *desc, + int planes, int *nb_rows) +{ + int err; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; + FFVulkanDescriptorSetBinding *desc_set; + int max_dim = FFMAX(width, height); + uint32_t max_wg = vkctx->props.properties.limits.maxComputeWorkGroupSize[0]; + int wg_size, wg_rows; + + /* Round the max workgroup size to the previous power of two */ + wg_size = max_wg; + wg_rows = 1; + + if (max_wg > max_dim) { + wg_size = max_dim; + } else if (max_wg < max_dim) { + /* Make it fit */ + while (wg_size*wg_rows < max_dim) + wg_rows++; + } + + RET(ff_vk_shader_init(pl, shd, "nlmeans_weights", VK_SHADER_STAGE_COMPUTE_BIT, 0)); + ff_vk_shader_set_compute_sizes(shd, wg_size, 1, 1); + *nb_rows = wg_rows; + + if (t > 1) + GLSLC(0, #extension GL_EXT_shader_atomic_float : require ); + GLSLC(0, #extension GL_ARB_gpu_shader_int64 : require ); + GLSLC(0, ); + GLSLF(0, #define DTYPE %s ,TYPE_NAME); + GLSLF(0, #define T_ALIGN %i ,TYPE_SIZE); + GLSLC(0, ); + GLSLC(0, layout(buffer_reference, buffer_reference_align = T_ALIGN) buffer DataBuffer { ); + GLSLC(1, DTYPE v[]; ); + GLSLC(0, }; ); + GLSLC(0, ); + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, uvec4 width; ); + GLSLC(1, uvec4 height; ); + GLSLC(1, uvec4 ws_stride; ); + GLSLC(1, ivec4 patch_size; ); + GLSLC(1, vec4 strength; ); + GLSLC(1, DataBuffer integral_base; ); + GLSLC(1, uint64_t integral_size; ); + GLSLC(1, uint64_t int_stride; ); + GLSLC(1, uint xyoffs_start; ); + GLSLC(0, }; ); + GLSLC(0, ); + + ff_vk_add_push_constant(pl, 0, sizeof(HorizontalPushData), VK_SHADER_STAGE_COMPUTE_BIT); + + desc_set = (FFVulkanDescriptorSetBinding []) { + { + .name = "input_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(sampler), + }, + { + .name = "weights_buffer_0", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_0[];", + }, + { + .name = "sums_buffer_0", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_0[];", + }, + { + .name = "weights_buffer_1", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_1[];", + }, + { + .name = "sums_buffer_1", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_1[];", + }, + { + .name = "weights_buffer_2", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_2[];", + }, + { + .name = "sums_buffer_2", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_2[];", + }, + { + .name = "weights_buffer_3", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_3[];", + }, + { + .name = "sums_buffer_3", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_3[];", + }, + }; + RET(ff_vk_pipeline_descriptor_set_add(vkctx, pl, shd, desc_set, 1 + 2*desc->nb_components, 0, 0)); + + desc_set = (FFVulkanDescriptorSetBinding []) { + { + .name = "xyoffsets_buffer", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "ivec2 xyoffsets[];", + }, + }; + RET(ff_vk_pipeline_descriptor_set_add(vkctx, pl, shd, desc_set, 1, 1, 0)); + + GLSLC(0, ); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, uint64_t offset; ); + GLSLC(1, DataBuffer dst; ); + GLSLC(1, float s1; ); + GLSLC(1, DTYPE s2; ); + GLSLC(1, DTYPE prefix_sum; ); + GLSLF(1, DTYPE psum[%i]; ,*nb_rows); + GLSLC(1, int r; ); + GLSLC(1, ivec2 pos; ); + GLSLC(1, int p; ); + GLSLC(0, ); + GLSLC(1, DataBuffer integral_data; ); + GLSLF(1, ivec2 offs[%i]; ,TYPE_ELEMS); + GLSLC(0, ); + GLSLC(1, int invoc_idx = int(gl_WorkGroupID.z); ); + GLSLC(0, ); + GLSLC(1, offset = integral_size * invoc_idx; ); + GLSLC(1, integral_data = DataBuffer(uint64_t(integral_base) + offset); ); + for (int i = 0; i < TYPE_ELEMS; i++) + GLSLF(1, offs[%i] = xyoffsets[xyoffs_start + %i*invoc_idx + %i]; ,i,TYPE_ELEMS,i); + GLSLC(0, ); + GLSLC(1, DTYPE a; ); + GLSLC(1, DTYPE b; ); + GLSLC(1, DTYPE c; ); + GLSLC(1, DTYPE d; ); + GLSLC(0, ); + GLSLC(1, DTYPE patch_diff; ); + if (TYPE_ELEMS == 4) { + GLSLC(1, vec4 src; ); + GLSLC(1, vec4 w; ); + } else { + GLSLC(1, vec4 src[4]; ); + GLSLC(1, vec4 w[4]; ); + } + GLSLC(1, float w_sum; ); + GLSLC(1, float sum; ); + GLSLC(0, ); + GLSLC(1, bool lt; ); + GLSLC(1, bool gt; ); + GLSLC(0, ); + + for (int i = 0; i < desc->nb_components; i++) { + int off = desc->comp[i].offset / (FFALIGN(desc->comp[i].depth, 8)/8); + if (width >= height) { + insert_horizontal_pass(shd, *nb_rows, 1, desc->comp[i].plane, off); + insert_vertical_pass(shd, *nb_rows, 0, desc->comp[i].plane, off); + insert_weights_pass(shd, *nb_rows, 0, t, i, desc->comp[i].plane, off); + } else { + insert_vertical_pass(shd, *nb_rows, 1, desc->comp[i].plane, off); + insert_horizontal_pass(shd, *nb_rows, 0, desc->comp[i].plane, off); + insert_weights_pass(shd, *nb_rows, 1, t, i, desc->comp[i].plane, off); + } + } + + GLSLC(0, } ); + + RET(spv->compile_shader(spv, vkctx, shd, &spv_data, &spv_len, "main", &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, exec, pl)); + +fail: + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + + return err; +} + +typedef struct DenoisePushData { + uint32_t ws_stride[4]; +} DenoisePushData; + +static av_cold int init_denoise_pipeline(FFVulkanContext *vkctx, FFVkExecPool *exec, + FFVulkanPipeline *pl, FFVkSPIRVShader *shd, + VkSampler sampler, FFVkSPIRVCompiler *spv, + const AVPixFmtDescriptor *desc, int planes) +{ + int err; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; + FFVulkanDescriptorSetBinding *desc_set; + + RET(ff_vk_shader_init(pl, shd, "nlmeans_denoise", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(shd, 32, 32, 1); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, uvec4 ws_stride; ); + GLSLC(0, }; ); + + ff_vk_add_push_constant(pl, 0, sizeof(DenoisePushData), VK_SHADER_STAGE_COMPUTE_BIT); + + desc_set = (FFVulkanDescriptorSetBinding []) { + { + .name = "input_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(sampler), + }, + { + .name = "output_img", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(vkctx->output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + { + .name = "weights_buffer_0", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_0[];", + }, + { + .name = "sums_buffer_0", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_0[];", + }, + { + .name = "weights_buffer_1", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_1[];", + }, + { + .name = "sums_buffer_1", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_1[];", + }, + { + .name = "weights_buffer_2", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_2[];", + }, + { + .name = "sums_buffer_2", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_2[];", + }, + { + .name = "weights_buffer_3", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float weights_3[];", + }, + { + .name = "sums_buffer_3", + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .mem_quali = "readonly", + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .buf_content = "float sums_3[];", + }, + }; + RET(ff_vk_pipeline_descriptor_set_add(vkctx, pl, shd, desc_set, 2 + 2*desc->nb_components, 0, 0)); + + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 size; ); + GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + GLSLC(1, const uint plane = uint(gl_WorkGroupID.z); ); + GLSLC(0, ); + GLSLC(1, float w_sum; ); + GLSLC(1, float sum; ); + GLSLC(1, vec4 src; ); + GLSLC(1, vec4 r; ); + GLSLC(0, ); + GLSLC(1, size = imageSize(output_img[plane]); ); + GLSLC(1, if (!IS_WITHIN(pos, size)) ); + GLSLC(2, return; ); + GLSLC(0, ); + GLSLC(1, src = texture(input_img[plane], pos); ); + GLSLC(0, ); + for (int c = 0; c < desc->nb_components; c++) { + int off = desc->comp[c].offset / (FFALIGN(desc->comp[c].depth, 8)/8); + GLSLF(1, if (plane == %i) { ,desc->comp[c].plane); + GLSLF(2, w_sum = weights_%i[pos.y*ws_stride[%i] + pos.x]; ,c, c); + GLSLF(2, sum = sums_%i[pos.y*ws_stride[%i] + pos.x]; ,c, c); + GLSLF(2, r[%i] = (sum + src[%i]*255) / (1.0 + w_sum) / 255; ,off, off); + GLSLC(1, } ); + GLSLC(0, ); + } + GLSLC(1, imageStore(output_img[plane], pos, r); ); + GLSLC(0, } ); + + RET(spv->compile_shader(spv, vkctx, shd, &spv_data, &spv_len, "main", &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, exec, pl)); + +fail: + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + + return err; +} + +static av_cold int init_filter(AVFilterContext *ctx) +{ + int rad, err; + int xcnt = 0, ycnt = 0; + NLMeansVulkanContext *s = ctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVCompiler *spv; + int *offsets_buf; + int offsets_dispatched = 0, nb_dispatches = 0; + + const AVPixFmtDescriptor *desc; + desc = av_pix_fmt_desc_get(vkctx->output_format); + if (!desc) + return AVERROR(EINVAL); + + if (!(s->opts.r & 1)) { + s->opts.r |= 1; + av_log(ctx, AV_LOG_WARNING, "Research size should be odd, setting to %i", + s->opts.r); + } + + if (!(s->opts.p & 1)) { + s->opts.p |= 1; + av_log(ctx, AV_LOG_WARNING, "Patch size should be odd, setting to %i", + s->opts.p); + } + + for (int i = 0; i < 4; i++) { + double str = (s->opts.sc[i] > 1.0) ? s->opts.sc[i] : s->opts.s; + int ps = (s->opts.pc[i] ? s->opts.pc[i] : s->opts.p); + str = 10.0f*str; + str *= -str; + str = 255.0*255.0 / str; + s->strength[i] = str; + if (!(ps & 1)) { + ps |= 1; + av_log(ctx, AV_LOG_WARNING, "Patch size should be odd, setting to %i", + ps); + } + s->patch[i] = ps / 2; + } + + rad = s->opts.r/2; + s->nb_offsets = (2*rad + 1)*(2*rad + 1) - 1; + s->xoffsets = av_malloc(s->nb_offsets*sizeof(*s->xoffsets)); + s->yoffsets = av_malloc(s->nb_offsets*sizeof(*s->yoffsets)); + s->nb_offsets = 0; + + for (int x = -rad; x <= rad; x++) { + for (int y = -rad; y <= rad; y++) { + if (!x && !y) + continue; + + s->xoffsets[xcnt++] = x; + s->yoffsets[ycnt++] = y; + s->nb_offsets++; + } + } + + RET(ff_vk_create_buf(&s->vkctx, &s->xyoffsets_buf, 2*s->nb_offsets*sizeof(int32_t), NULL, NULL, + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)); + RET(ff_vk_map_buffer(&s->vkctx, &s->xyoffsets_buf, (uint8_t **)&offsets_buf, 0)); + + for (int i = 0; i < 2*s->nb_offsets; i += 2) { + offsets_buf[i + 0] = s->xoffsets[i >> 1]; + offsets_buf[i + 1] = s->yoffsets[i >> 1]; + } + + RET(ff_vk_unmap_buffer(&s->vkctx, &s->xyoffsets_buf, 1)); + + s->opts.t = FFMIN(s->opts.t, (FFALIGN(s->nb_offsets, TYPE_ELEMS) / TYPE_ELEMS)); + if (!vkctx->atomic_float_feats.shaderBufferFloat32AtomicAdd) { + av_log(ctx, AV_LOG_WARNING, "Device doesn't support atomic float adds, " + "disabling dispatch parallelism\n"); + s->opts.t = 1; + } + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, 1, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_NEAREST)); + + RET(init_weights_pipeline(vkctx, &s->e, &s->pl_weights, &s->shd_weights, s->sampler, + spv, s->vkctx.output_width, s->vkctx.output_height, + s->opts.t, desc, planes, &s->pl_weights_rows)); + + RET(init_denoise_pipeline(vkctx, &s->e, &s->pl_denoise, &s->shd_denoise, s->sampler, + spv, desc, planes)); + + RET(ff_vk_set_descriptor_buffer(&s->vkctx, &s->pl_weights, NULL, 1, 0, 0, + s->xyoffsets_buf.address, s->xyoffsets_buf.size, + VK_FORMAT_UNDEFINED)); + + do { + int wg_invoc = FFMIN((s->nb_offsets - offsets_dispatched)/TYPE_ELEMS, s->opts.t); + wg_invoc = FFMIN(wg_invoc, vkctx->props.properties.limits.maxComputeWorkGroupCount[2]); + offsets_dispatched += wg_invoc * TYPE_ELEMS; + nb_dispatches++; + } while (offsets_dispatched < s->nb_offsets); + + av_log(ctx, AV_LOG_VERBOSE, "Filter initialized, %i x/y offsets, %i dispatches\n", + s->nb_offsets, nb_dispatches); + + s->initialized = 1; + +fail: + if (spv) + spv->uninit(&spv); + + return err; +} + +static int denoise_pass(NLMeansVulkanContext *s, FFVkExecContext *exec, + FFVkBuffer *ws_vk, uint32_t ws_stride[4]) +{ + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + VkBufferMemoryBarrier2 buf_bar[8]; + int nb_buf_bar = 0; + + /* Denoise pass pipeline */ + ff_vk_exec_bind_pipeline(vkctx, exec, &s->pl_denoise); + + /* Push data */ + ff_vk_update_push_exec(vkctx, exec, &s->pl_denoise, VK_SHADER_STAGE_COMPUTE_BIT, + 0, sizeof(DenoisePushData), &(DenoisePushData) { + { ws_stride[0], ws_stride[1], ws_stride[2], ws_stride[3] }, + }); + + buf_bar[nb_buf_bar++] = (VkBufferMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .srcStageMask = ws_vk->stage, + .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .srcAccessMask = ws_vk->access, + .dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_READ_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = ws_vk->buf, + .size = ws_vk->size, + .offset = 0, + }; + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pBufferMemoryBarriers = buf_bar, + .bufferMemoryBarrierCount = nb_buf_bar, + }); + ws_vk->stage = buf_bar[0].dstStageMask; + ws_vk->access = buf_bar[0].dstAccessMask; + + /* End of denoise pass */ + vk->CmdDispatch(exec->buf, + FFALIGN(vkctx->output_width, s->pl_denoise.wg_size[0])/s->pl_denoise.wg_size[0], + FFALIGN(vkctx->output_height, s->pl_denoise.wg_size[1])/s->pl_denoise.wg_size[1], + av_pix_fmt_count_planes(s->vkctx.output_format)); + + return 0; +} + +static int nlmeans_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) +{ + int err; + AVFrame *out = NULL; + AVFilterContext *ctx = link->dst; + NLMeansVulkanContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + const AVPixFmtDescriptor *desc; + int plane_widths[4]; + int plane_heights[4]; + + int offsets_dispatched = 0; + + /* Integral */ + AVBufferRef *integral_buf = NULL; + FFVkBuffer *integral_vk; + size_t int_stride; + size_t int_size; + + /* Weights/sums */ + AVBufferRef *ws_buf = NULL; + FFVkBuffer *ws_vk; + VkDeviceAddress weights_addr[4]; + VkDeviceAddress sums_addr[4]; + uint32_t ws_stride[4]; + size_t ws_size[4]; + size_t ws_total_size = 0; + + FFVkExecContext *exec; + VkImageView in_views[AV_NUM_DATA_POINTERS]; + VkImageView out_views[AV_NUM_DATA_POINTERS]; + VkImageMemoryBarrier2 img_bar[8]; + int nb_img_bar = 0; + VkBufferMemoryBarrier2 buf_bar[8]; + int nb_buf_bar = 0; + + if (!s->initialized) + RET(init_filter(ctx)); + + desc = av_pix_fmt_desc_get(vkctx->output_format); + if (!desc) + return AVERROR(EINVAL); + + /* Integral image */ + int_stride = s->pl_weights.wg_size[0]*s->pl_weights_rows*TYPE_SIZE; + int_size = s->pl_weights.wg_size[0]*s->pl_weights_rows*int_stride; + + /* Plane dimensions */ + for (int i = 0; i < desc->nb_components; i++) { + plane_widths[i] = !i || (i == 3) ? vkctx->output_width : AV_CEIL_RSHIFT(vkctx->output_width, desc->log2_chroma_w); + plane_heights[i] = !i || (i == 3) ? vkctx->output_height : AV_CEIL_RSHIFT(vkctx->output_height, desc->log2_chroma_w); + plane_widths[i] = FFALIGN(plane_widths[i], s->pl_denoise.wg_size[0]); + plane_heights[i] = FFALIGN(plane_heights[i], s->pl_denoise.wg_size[1]); + + ws_stride[i] = plane_widths[i]; + ws_size[i] = ws_stride[i] * plane_heights[i] * sizeof(float); + ws_total_size += ws_size[i]; + } + + /* Buffers */ + err = ff_vk_get_pooled_buffer(&s->vkctx, &s->integral_buf_pool, &integral_buf, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + NULL, + s->opts.t * int_size, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (err < 0) + return err; + integral_vk = (FFVkBuffer *)integral_buf->data; + + err = ff_vk_get_pooled_buffer(&s->vkctx, &s->ws_buf_pool, &ws_buf, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT | + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + NULL, + ws_total_size * 2, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + if (err < 0) + return err; + ws_vk = (FFVkBuffer *)ws_buf->data; + + weights_addr[0] = ws_vk->address; + sums_addr[0] = ws_vk->address + ws_total_size; + for (int i = 1; i < desc->nb_components; i++) { + weights_addr[i] = weights_addr[i - 1] + ws_size[i - 1]; + sums_addr[i] = sums_addr[i - 1] + ws_size[i - 1]; + } + + /* Output frame */ + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + err = AVERROR(ENOMEM); + goto fail; + } + + /* Execution context */ + exec = ff_vk_exec_get(&s->e); + ff_vk_exec_start(vkctx, exec); + + /* Dependencies */ + RET(ff_vk_exec_add_dep_frame(vkctx, exec, in, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_exec_add_dep_frame(vkctx, exec, out, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + + RET(ff_vk_exec_add_dep_buf(vkctx, exec, &integral_buf, 1, 0)); + integral_buf = NULL; + + RET(ff_vk_exec_add_dep_buf(vkctx, exec, &ws_buf, 1, 0)); + ws_buf = NULL; + + /* Input frame prep */ + RET(ff_vk_create_imageviews(vkctx, exec, in_views, in)); + ff_vk_update_descriptor_img_array(vkctx, &s->pl_weights, exec, in, in_views, 0, 0, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + s->sampler); + ff_vk_frame_barrier(vkctx, exec, in, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED); + + /* Output frame prep */ + RET(ff_vk_create_imageviews(vkctx, exec, out_views, out)); + ff_vk_frame_barrier(vkctx, exec, out, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED); + + nb_buf_bar = 0; + buf_bar[nb_buf_bar++] = (VkBufferMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .srcStageMask = ws_vk->stage, + .dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT, + .srcAccessMask = ws_vk->access, + .dstAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = ws_vk->buf, + .size = ws_vk->size, + .offset = 0, + }; + buf_bar[nb_buf_bar++] = (VkBufferMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .srcStageMask = integral_vk->stage, + .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .srcAccessMask = integral_vk->access, + .dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_READ_BIT | + VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = integral_vk->buf, + .size = integral_vk->size, + .offset = 0, + }; + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pImageMemoryBarriers = img_bar, + .imageMemoryBarrierCount = nb_img_bar, + .pBufferMemoryBarriers = buf_bar, + .bufferMemoryBarrierCount = nb_buf_bar, + }); + ws_vk->stage = buf_bar[0].dstStageMask; + ws_vk->access = buf_bar[0].dstAccessMask; + integral_vk->stage = buf_bar[1].dstStageMask; + integral_vk->access = buf_bar[1].dstAccessMask; + + /* Buffer zeroing */ + vk->CmdFillBuffer(exec->buf, ws_vk->buf, 0, ws_vk->size, 0x0); + + nb_buf_bar = 0; + buf_bar[nb_buf_bar++] = (VkBufferMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .srcStageMask = ws_vk->stage, + .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .srcAccessMask = ws_vk->access, + .dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_READ_BIT | + VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = ws_vk->buf, + .size = ws_vk->size, + .offset = 0, + }; + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pBufferMemoryBarriers = buf_bar, + .bufferMemoryBarrierCount = nb_buf_bar, + }); + ws_vk->stage = buf_bar[0].dstStageMask; + ws_vk->access = buf_bar[0].dstAccessMask; + + /* Update weights descriptors */ + ff_vk_update_descriptor_img_array(vkctx, &s->pl_weights, exec, in, in_views, 0, 0, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + s->sampler); + for (int i = 0; i < desc->nb_components; i++) { + RET(ff_vk_set_descriptor_buffer(&s->vkctx, &s->pl_weights, exec, 0, 1 + i*2 + 0, 0, + weights_addr[i], ws_size[i], + VK_FORMAT_UNDEFINED)); + RET(ff_vk_set_descriptor_buffer(&s->vkctx, &s->pl_weights, exec, 0, 1 + i*2 + 1, 0, + sums_addr[i], ws_size[i], + VK_FORMAT_UNDEFINED)); + } + + /* Update denoise descriptors */ + ff_vk_update_descriptor_img_array(vkctx, &s->pl_denoise, exec, in, in_views, 0, 0, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + s->sampler); + ff_vk_update_descriptor_img_array(vkctx, &s->pl_denoise, exec, out, out_views, 0, 1, + VK_IMAGE_LAYOUT_GENERAL, s->sampler); + for (int i = 0; i < desc->nb_components; i++) { + RET(ff_vk_set_descriptor_buffer(&s->vkctx, &s->pl_denoise, exec, 0, 2 + i*2 + 0, 0, + weights_addr[i], ws_size[i], + VK_FORMAT_UNDEFINED)); + RET(ff_vk_set_descriptor_buffer(&s->vkctx, &s->pl_denoise, exec, 0, 2 + i*2 + 1, 0, + sums_addr[i], ws_size[i], + VK_FORMAT_UNDEFINED)); + } + + /* Weights pipeline */ + ff_vk_exec_bind_pipeline(vkctx, exec, &s->pl_weights); + + do { + int wg_invoc; + HorizontalPushData pd = { + { plane_widths[0], plane_widths[1], plane_widths[2], plane_widths[3] }, + { plane_heights[0], plane_heights[1], plane_heights[2], plane_heights[3] }, + { ws_stride[0], ws_stride[1], ws_stride[2], ws_stride[3] }, + { s->patch[0], s->patch[1], s->patch[2], s->patch[3] }, + { s->strength[0], s->strength[1], s->strength[2], s->strength[2], }, + integral_vk->address, + (uint64_t)int_size, + (uint64_t)int_stride, + offsets_dispatched, + }; + + if (offsets_dispatched) { + nb_buf_bar = 0; + buf_bar[nb_buf_bar++] = (VkBufferMemoryBarrier2) { + .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2, + .srcStageMask = integral_vk->stage, + .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + .srcAccessMask = integral_vk->access, + .dstAccessMask = VK_ACCESS_2_SHADER_STORAGE_READ_BIT | + VK_ACCESS_2_SHADER_STORAGE_WRITE_BIT, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .buffer = integral_vk->buf, + .size = integral_vk->size, + .offset = 0, + }; + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pBufferMemoryBarriers = buf_bar, + .bufferMemoryBarrierCount = nb_buf_bar, + }); + integral_vk->stage = buf_bar[1].dstStageMask; + integral_vk->access = buf_bar[1].dstAccessMask; + } + + /* Push data */ + ff_vk_update_push_exec(vkctx, exec, &s->pl_weights, VK_SHADER_STAGE_COMPUTE_BIT, + 0, sizeof(pd), &pd); + + wg_invoc = FFMIN((s->nb_offsets - offsets_dispatched)/TYPE_ELEMS, s->opts.t); + wg_invoc = FFMIN(wg_invoc, vkctx->props.properties.limits.maxComputeWorkGroupCount[2]); + + /* End of horizontal pass */ + vk->CmdDispatch(exec->buf, 1, 1, wg_invoc); + + offsets_dispatched += wg_invoc * TYPE_ELEMS; + } while (offsets_dispatched < s->nb_offsets); + + RET(denoise_pass(s, exec, ws_vk, ws_stride)); + + err = ff_vk_exec_submit(vkctx, exec); + if (err < 0) + return err; + + err = av_frame_copy_props(out, in); + if (err < 0) + goto fail; + + av_frame_free(&in); + + return ff_filter_frame(outlink, out); + +fail: + av_buffer_unref(&integral_buf); + av_buffer_unref(&ws_buf); + av_frame_free(&in); + av_frame_free(&out); + return err; +} + +static void nlmeans_vulkan_uninit(AVFilterContext *avctx) +{ + NLMeansVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl_weights); + ff_vk_shader_free(vkctx, &s->shd_weights); + ff_vk_pipeline_free(vkctx, &s->pl_denoise); + ff_vk_shader_free(vkctx, &s->shd_denoise); + + av_buffer_pool_uninit(&s->integral_buf_pool); + av_buffer_pool_uninit(&s->ws_buf_pool); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); + + ff_vk_uninit(&s->vkctx); + + av_freep(&s->xoffsets); + av_freep(&s->yoffsets); + + s->initialized = 0; +} + +#define OFFSET(x) offsetof(NLMeansVulkanContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) +static const AVOption nlmeans_vulkan_options[] = { + { "s", "denoising strength for all components", OFFSET(opts.s), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 1.0, 100.0, FLAGS }, + { "p", "patch size for all components", OFFSET(opts.p), AV_OPT_TYPE_INT, { .i64 = 3*2+1 }, 0, 99, FLAGS }, + { "r", "research window radius", OFFSET(opts.r), AV_OPT_TYPE_INT, { .i64 = 7*2+1 }, 0, 99, FLAGS }, + { "t", "parallelism", OFFSET(opts.t), AV_OPT_TYPE_INT, { .i64 = 36 }, 1, 168, FLAGS }, + + { "s1", "denoising strength for component 1", OFFSET(opts.sc[0]), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 1.0, 100.0, FLAGS }, + { "s2", "denoising strength for component 2", OFFSET(opts.sc[1]), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 1.0, 100.0, FLAGS }, + { "s3", "denoising strength for component 3", OFFSET(opts.sc[2]), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 1.0, 100.0, FLAGS }, + { "s4", "denoising strength for component 4", OFFSET(opts.sc[3]), AV_OPT_TYPE_DOUBLE, { .dbl = 1.0 }, 1.0, 100.0, FLAGS }, + + { "p1", "patch size for component 1", OFFSET(opts.pc[0]), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 99, FLAGS }, + { "p2", "patch size for component 2", OFFSET(opts.pc[1]), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 99, FLAGS }, + { "p3", "patch size for component 3", OFFSET(opts.pc[2]), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 99, FLAGS }, + { "p4", "patch size for component 4", OFFSET(opts.pc[3]), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 99, FLAGS }, + + { NULL } +}; + +AVFILTER_DEFINE_CLASS(nlmeans_vulkan); + +static const AVFilterPad nlmeans_vulkan_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = &nlmeans_vulkan_filter_frame, + .config_props = &ff_vk_filter_config_input, + }, +}; + +static const AVFilterPad nlmeans_vulkan_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &ff_vk_filter_config_output, + }, +}; + +const AVFilter ff_vf_nlmeans_vulkan = { + .name = "nlmeans_vulkan", + .description = NULL_IF_CONFIG_SMALL("Non-local means denoiser (Vulkan)"), + .priv_size = sizeof(NLMeansVulkanContext), + .init = &ff_vk_filter_init, + .uninit = &nlmeans_vulkan_uninit, + FILTER_INPUTS(nlmeans_vulkan_inputs), + FILTER_OUTPUTS(nlmeans_vulkan_outputs), + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), + .priv_class = &nlmeans_vulkan_class, + .flags = AVFILTER_FLAG_HWDEVICE, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_nnedi.c b/libavfilter/vf_nnedi.c index 63b83e5efdc..436d7a339eb 100644 --- a/libavfilter/vf_nnedi.c +++ b/libavfilter/vf_nnedi.c @@ -119,45 +119,45 @@ typedef struct NNEDIContext { static const AVOption nnedi_options[] = { {"weights", "set weights file", OFFSET(weights_file), AV_OPT_TYPE_STRING, {.str="nnedi3_weights.bin"}, 0, 0, FLAGS }, - {"deint", "set which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, RFLAGS, "deint" }, - {"all", "deinterlace all frames", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, "deint" }, - {"interlaced", "only deinterlace frames marked as interlaced", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, "deint" }, - {"field", "set mode of operation", OFFSET(field), AV_OPT_TYPE_INT, {.i64=-1}, -2, 3, RFLAGS, "field" }, - {"af", "use frame flags, both fields", 0, AV_OPT_TYPE_CONST, {.i64=-2}, 0, 0, RFLAGS, "field" }, - {"a", "use frame flags, single field", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, RFLAGS, "field" }, - {"t", "use top field only", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, "field" }, - {"b", "use bottom field only", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, "field" }, - {"tf", "use both fields, top first", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, RFLAGS, "field" }, - {"bf", "use both fields, bottom first", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, RFLAGS, "field" }, + {"deint", "set which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, RFLAGS, .unit = "deint" }, + {"all", "deinterlace all frames", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, .unit = "deint" }, + {"interlaced", "only deinterlace frames marked as interlaced", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, .unit = "deint" }, + {"field", "set mode of operation", OFFSET(field), AV_OPT_TYPE_INT, {.i64=-1}, -2, 3, RFLAGS, .unit = "field" }, + {"af", "use frame flags, both fields", 0, AV_OPT_TYPE_CONST, {.i64=-2}, 0, 0, RFLAGS, .unit = "field" }, + {"a", "use frame flags, single field", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, RFLAGS, .unit = "field" }, + {"t", "use top field only", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, .unit = "field" }, + {"b", "use bottom field only", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, .unit = "field" }, + {"tf", "use both fields, top first", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, RFLAGS, .unit = "field" }, + {"bf", "use both fields, bottom first", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, RFLAGS, .unit = "field" }, {"planes", "set which planes to process", OFFSET(process_plane), AV_OPT_TYPE_INT, {.i64=7}, 0, 15, RFLAGS }, - {"nsize", "set size of local neighborhood around each pixel, used by the predictor neural network", OFFSET(nsize), AV_OPT_TYPE_INT, {.i64=6}, 0, 6, RFLAGS, "nsize" }, - {"s8x6", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, "nsize" }, - {"s16x6", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, "nsize" }, - {"s32x6", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, RFLAGS, "nsize" }, - {"s48x6", NULL, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, RFLAGS, "nsize" }, - {"s8x4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, RFLAGS, "nsize" }, - {"s16x4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, RFLAGS, "nsize" }, - {"s32x4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, RFLAGS, "nsize" }, - {"nns", "set number of neurons in predictor neural network", OFFSET(nnsparam), AV_OPT_TYPE_INT, {.i64=1}, 0, 4, RFLAGS, "nns" }, - {"n16", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, "nns" }, - {"n32", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, "nns" }, - {"n64", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, RFLAGS, "nns" }, - {"n128", NULL, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, RFLAGS, "nns" }, - {"n256", NULL, 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, RFLAGS, "nns" }, - {"qual", "set quality", OFFSET(qual), AV_OPT_TYPE_INT, {.i64=1}, 1, 2, RFLAGS, "qual" }, - {"fast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, "qual" }, - {"slow", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, RFLAGS, "qual" }, - {"etype", "set which set of weights to use in the predictor", OFFSET(etype), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, RFLAGS, "etype" }, - {"a", "weights trained to minimize absolute error", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, "etype" }, - {"abs","weights trained to minimize absolute error", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, "etype" }, - {"s", "weights trained to minimize squared error", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, "etype" }, - {"mse","weights trained to minimize squared error", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, "etype" }, - {"pscrn", "set prescreening", OFFSET(pscrn), AV_OPT_TYPE_INT, {.i64=2}, 0, 4, RFLAGS, "pscrn" }, - {"none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, "pscrn" }, - {"original", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, "pscrn" }, - {"new", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, RFLAGS, "pscrn" }, - {"new2", NULL, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, RFLAGS, "pscrn" }, - {"new3", NULL, 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, RFLAGS, "pscrn" }, + {"nsize", "set size of local neighborhood around each pixel, used by the predictor neural network", OFFSET(nsize), AV_OPT_TYPE_INT, {.i64=6}, 0, 6, RFLAGS, .unit = "nsize" }, + {"s8x6", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, .unit = "nsize" }, + {"s16x6", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, .unit = "nsize" }, + {"s32x6", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, RFLAGS, .unit = "nsize" }, + {"s48x6", NULL, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, RFLAGS, .unit = "nsize" }, + {"s8x4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, RFLAGS, .unit = "nsize" }, + {"s16x4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=5}, 0, 0, RFLAGS, .unit = "nsize" }, + {"s32x4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=6}, 0, 0, RFLAGS, .unit = "nsize" }, + {"nns", "set number of neurons in predictor neural network", OFFSET(nnsparam), AV_OPT_TYPE_INT, {.i64=1}, 0, 4, RFLAGS, .unit = "nns" }, + {"n16", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, .unit = "nns" }, + {"n32", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, .unit = "nns" }, + {"n64", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, RFLAGS, .unit = "nns" }, + {"n128", NULL, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, RFLAGS, .unit = "nns" }, + {"n256", NULL, 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, RFLAGS, .unit = "nns" }, + {"qual", "set quality", OFFSET(qual), AV_OPT_TYPE_INT, {.i64=1}, 1, 2, RFLAGS, .unit = "qual" }, + {"fast", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, .unit = "qual" }, + {"slow", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, RFLAGS, .unit = "qual" }, + {"etype", "set which set of weights to use in the predictor", OFFSET(etype), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, RFLAGS, .unit = "etype" }, + {"a", "weights trained to minimize absolute error", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, .unit = "etype" }, + {"abs","weights trained to minimize absolute error", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, .unit = "etype" }, + {"s", "weights trained to minimize squared error", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, .unit = "etype" }, + {"mse","weights trained to minimize squared error", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, .unit = "etype" }, + {"pscrn", "set prescreening", OFFSET(pscrn), AV_OPT_TYPE_INT, {.i64=2}, 0, 4, RFLAGS, .unit = "pscrn" }, + {"none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, RFLAGS, .unit = "pscrn" }, + {"original", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, RFLAGS, .unit = "pscrn" }, + {"new", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, RFLAGS, .unit = "pscrn" }, + {"new2", NULL, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, RFLAGS, .unit = "pscrn" }, + {"new3", NULL, 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, RFLAGS, .unit = "pscrn" }, { NULL } }; @@ -540,8 +540,8 @@ static int filter_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const float in_scale = s->in_scale; const float out_scale = s->out_scale; const int depth = s->depth; - const int interlaced = in->interlaced_frame; - const int tff = s->field_n == (s->field < 0 ? interlaced ? in->top_field_first : 1 : + const int interlaced = !!(in->flags & AV_FRAME_FLAG_INTERLACED); + const int tff = s->field_n == (s->field < 0 ? interlaced ? (in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 1 : (s->field & 1) ^ 1); @@ -665,7 +665,12 @@ static int get_frame(AVFilterContext *ctx, int is_second) if (!dst) return AVERROR(ENOMEM); av_frame_copy_props(dst, s->prev); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS dst->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + dst->flags &= ~AV_FRAME_FLAG_INTERLACED; dst->pts = s->pts; ff_filter_execute(ctx, filter_slice, dst, NULL, @@ -688,7 +693,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) return 0; } - if ((s->deint && !s->prev->interlaced_frame) || ctx->is_disabled) { + if ((s->deint && !(s->prev->flags & AV_FRAME_FLAG_INTERLACED)) || ctx->is_disabled) { s->prev->pts *= 2; ret = ff_filter_frame(ctx->outputs[0], s->prev); s->prev = in; diff --git a/libavfilter/vf_noise.c b/libavfilter/vf_noise.c index 8ed12f74098..af33a73efaa 100644 --- a/libavfilter/vf_noise.c +++ b/libavfilter/vf_noise.c @@ -24,6 +24,7 @@ * noise generator */ +#include "libavutil/emms.h" #include "libavutil/opt.h" #include "libavutil/imgutils.h" #include "libavutil/lfg.h" @@ -46,12 +47,12 @@ typedef struct ThreadData { {#name"_seed", "set component #"#x" noise seed", OFFSET(param.seed), AV_OPT_TYPE_INT, {.i64=-1}, -1, INT_MAX, FLAGS}, \ {#name"_strength", "set component #"#x" strength", OFFSET(param.strength), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS}, \ {#name"s", "set component #"#x" strength", OFFSET(param.strength), AV_OPT_TYPE_INT, {.i64=0}, 0, 100, FLAGS}, \ - {#name"_flags", "set component #"#x" flags", OFFSET(param.flags), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 31, FLAGS, #name"_flags"}, \ - {#name"f", "set component #"#x" flags", OFFSET(param.flags), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 31, FLAGS, #name"_flags"}, \ - {"a", "averaged noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_AVERAGED}, 0, 0, FLAGS, #name"_flags"}, \ - {"p", "(semi)regular pattern", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_PATTERN}, 0, 0, FLAGS, #name"_flags"}, \ - {"t", "temporal noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_TEMPORAL}, 0, 0, FLAGS, #name"_flags"}, \ - {"u", "uniform noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_UNIFORM}, 0, 0, FLAGS, #name"_flags"}, + {#name"_flags", "set component #"#x" flags", OFFSET(param.flags), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 31, FLAGS, .unit = #name"_flags"}, \ + {#name"f", "set component #"#x" flags", OFFSET(param.flags), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, 31, FLAGS, .unit = #name"_flags"}, \ + {"a", "averaged noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_AVERAGED}, 0, 0, FLAGS, .unit = #name"_flags"}, \ + {"p", "(semi)regular pattern", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_PATTERN}, 0, 0, FLAGS, .unit = #name"_flags"}, \ + {"t", "temporal noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_TEMPORAL}, 0, 0, FLAGS, .unit = #name"_flags"}, \ + {"u", "uniform noise", 0, AV_OPT_TYPE_CONST, {.i64=NOISE_UNIFORM}, 0, 0, FLAGS, .unit = #name"_flags"}, static const AVOption noise_options[] = { NOISE_PARAMS(all, 0, all) @@ -330,13 +331,6 @@ static const AVFilterPad noise_inputs[] = { }, }; -static const AVFilterPad noise_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_noise = { .name = "noise", .description = NULL_IF_CONFIG_SMALL("Add noise."), @@ -344,7 +338,7 @@ const AVFilter ff_vf_noise = { .init = init, .uninit = uninit, FILTER_INPUTS(noise_inputs), - FILTER_OUTPUTS(noise_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .priv_class = &noise_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_normalize.c b/libavfilter/vf_normalize.c index 43ed3c67b3f..d81b2d64572 100644 --- a/libavfilter/vf_normalize.c +++ b/libavfilter/vf_normalize.c @@ -72,13 +72,11 @@ * over-processed look. The default is full strength. */ -#include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -511,13 +509,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_normalize = { .name = "normalize", .description = NULL_IF_CONFIG_SMALL("Normalize RGB video."), @@ -525,7 +516,7 @@ const AVFilter ff_vf_normalize = { .priv_class = &normalize_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_null.c b/libavfilter/vf_null.c index fa80e2a1026..1502774f988 100644 --- a/libavfilter/vf_null.c +++ b/libavfilter/vf_null.c @@ -26,24 +26,10 @@ #include "internal.h" #include "video.h" -static const AVFilterPad avfilter_vf_null_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - -static const AVFilterPad avfilter_vf_null_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_null = { .name = "null", .description = NULL_IF_CONFIG_SMALL("Pass the source unchanged to the output."), .flags = AVFILTER_FLAG_METADATA_ONLY, - FILTER_INPUTS(avfilter_vf_null_inputs), - FILTER_OUTPUTS(avfilter_vf_null_outputs), + FILTER_INPUTS(ff_video_default_filterpad), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_ocr.c b/libavfilter/vf_ocr.c index 1648afa0f9b..3cece91edf5 100644 --- a/libavfilter/vf_ocr.c +++ b/libavfilter/vf_ocr.c @@ -22,7 +22,6 @@ #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -129,13 +128,6 @@ static const AVFilterPad ocr_inputs[] = { }, }; -static const AVFilterPad ocr_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_ocr = { .name = "ocr", .description = NULL_IF_CONFIG_SMALL("Optical Character Recognition."), @@ -145,6 +137,6 @@ const AVFilter ff_vf_ocr = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(ocr_inputs), - FILTER_OUTPUTS(ocr_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/vf_overlay.c b/libavfilter/vf_overlay.c index e201e07c159..349b8d588c0 100644 --- a/libavfilter/vf_overlay.c +++ b/libavfilter/vf_overlay.c @@ -55,7 +55,9 @@ static const char *const var_names[] = { "x", "y", "n", ///< number of frame +#if FF_API_FRAME_PKT "pos", ///< position in the file +#endif "t", ///< timestamp expressed in seconds NULL }; @@ -154,7 +156,7 @@ static int process_command(AVFilterContext *ctx, const char *cmd, const char *ar static const enum AVPixelFormat alpha_pix_fmts[] = { AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, - AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, + AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_ARGB, AV_PIX_FMT_ABGR, AV_PIX_FMT_RGBA, AV_PIX_FMT_BGRA, AV_PIX_FMT_GBRAP, AV_PIX_FMT_NONE }; @@ -202,6 +204,13 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUVA444P, AV_PIX_FMT_NONE }; + static const enum AVPixelFormat main_pix_fmts_yuv444p10[] = { + AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_NONE + }; + static const enum AVPixelFormat overlay_pix_fmts_yuv444p10[] = { + AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_NONE + }; + static const enum AVPixelFormat main_pix_fmts_gbrp[] = { AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_NONE }; @@ -246,6 +255,10 @@ static int query_formats(AVFilterContext *ctx) main_formats = main_pix_fmts_yuv444; overlay_formats = overlay_pix_fmts_yuv444; break; + case OVERLAY_FORMAT_YUV444P10: + main_formats = main_pix_fmts_yuv444p10; + overlay_formats = overlay_pix_fmts_yuv444p10; + break; case OVERLAY_FORMAT_RGB: main_formats = main_pix_fmts_rgb; overlay_formats = overlay_pix_fmts_rgb; @@ -290,7 +303,9 @@ static int config_input_overlay(AVFilterLink *inlink) s->var_values[VAR_Y] = NAN; s->var_values[VAR_N] = 0; s->var_values[VAR_T] = NAN; +#if FF_API_FRAME_PKT s->var_values[VAR_POS] = NAN; +#endif if ((ret = set_expr(&s->x_pexpr, s->x_expr, "x", ctx)) < 0 || (ret = set_expr(&s->y_pexpr, s->y_expr, "y", ctx)) < 0) @@ -675,197 +690,59 @@ static av_always_inline void blend_slice_planar_rgb(AVFilterContext *ctx, alpha_composite_8_8bits(src, dst, src_w, src_h, dst_w, dst_h, x, y, jobnr, nb_jobs); } -static int blend_slice_yuv420(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 1, 0, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuva420(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 1, 1, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuv420p10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_16_10bits(ctx, td->dst, td->src, 1, 1, 0, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuva420p10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_16_10bits(ctx, td->dst, td->src, 1, 1, 1, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuv422p10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_16_10bits(ctx, td->dst, td->src, 1, 0, 0, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuva422p10(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_16_10bits(ctx, td->dst, td->src, 1, 0, 1, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuv422(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 0, 0, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuva422(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 0, 1, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuv444(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 0, 0, 0, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuva444(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 0, 0, 1, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_gbrp(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_planar_rgb(ctx, td->dst, td->src, 0, 0, 0, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_gbrap(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_planar_rgb(ctx, td->dst, td->src, 0, 0, 1, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuv420_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 1, 0, s->x, s->y, 0, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuva420_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 1, 1, s->x, s->y, 0, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuv422_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 0, 0, s->x, s->y, 0, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuva422_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 1, 0, 1, s->x, s->y, 0, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuv444_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 0, 0, 0, s->x, s->y, 0, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_yuva444_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_yuv_8_8bits(ctx, td->dst, td->src, 0, 0, 1, s->x, s->y, 0, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_gbrp_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_planar_rgb(ctx, td->dst, td->src, 0, 0, 0, s->x, s->y, 0, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_gbrap_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_planar_rgb(ctx, td->dst, td->src, 0, 0, 1, s->x, s->y, 0, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_rgb(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_packed_rgb(ctx, td->dst, td->src, 0, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_rgba(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_packed_rgb(ctx, td->dst, td->src, 1, s->x, s->y, 1, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_rgb_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_packed_rgb(ctx, td->dst, td->src, 0, s->x, s->y, 0, jobnr, nb_jobs); - return 0; -} - -static int blend_slice_rgba_pm(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) -{ - OverlayContext *s = ctx->priv; - ThreadData *td = arg; - blend_slice_packed_rgb(ctx, td->dst, td->src, 1, s->x, s->y, 0, jobnr, nb_jobs); - return 0; -} +#define DEFINE_BLEND_SLICE_PLANAR_FMT(format_, blend_slice_fn_suffix_, hsub_, vsub_, main_has_alpha_, direct_) \ +static int blend_slice_##format_(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \ +{ \ + OverlayContext *s = ctx->priv; \ + ThreadData *td = arg; \ + blend_slice_##blend_slice_fn_suffix_(ctx, td->dst, td->src, \ + hsub_, vsub_, main_has_alpha_, \ + s->x, s->y, direct_, \ + jobnr, nb_jobs); \ + return 0; \ +} + +// FMT FN H V A D +DEFINE_BLEND_SLICE_PLANAR_FMT(yuv420, yuv_8_8bits, 1, 1, 0, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuva420, yuv_8_8bits, 1, 1, 1, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuv420p10, yuv_16_10bits, 1, 1, 0, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuva420p10, yuv_16_10bits, 1, 1, 1, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuv422p10, yuv_16_10bits, 1, 0, 0, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuva422p10, yuv_16_10bits, 1, 0, 1, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuv422, yuv_8_8bits, 1, 0, 0, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuva422, yuv_8_8bits, 1, 0, 1, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuv444, yuv_8_8bits, 0, 0, 0, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuva444, yuv_8_8bits, 0, 0, 1, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuv444p10, yuv_16_10bits, 0, 0, 0, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuva444p10, yuv_16_10bits, 0, 0, 1, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(gbrp, planar_rgb, 0, 0, 0, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(gbrap, planar_rgb, 0, 0, 1, 1); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuv420_pm, yuv_8_8bits, 1, 1, 0, 0); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuva420_pm, yuv_8_8bits, 1, 1, 1, 0); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuv422_pm, yuv_8_8bits, 1, 0, 0, 0); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuva422_pm, yuv_8_8bits, 1, 0, 1, 0); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuv444_pm, yuv_8_8bits, 0, 0, 0, 0); +DEFINE_BLEND_SLICE_PLANAR_FMT(yuva444_pm, yuv_8_8bits, 0, 0, 1, 0); +DEFINE_BLEND_SLICE_PLANAR_FMT(gbrp_pm, planar_rgb, 0, 0, 0, 0); +DEFINE_BLEND_SLICE_PLANAR_FMT(gbrap_pm, planar_rgb, 0, 0, 1, 0); + +#define DEFINE_BLEND_SLICE_PACKED_FMT(format_, blend_slice_fn_suffix_, main_has_alpha_, direct_) \ +static int blend_slice_##format_(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) \ +{ \ + OverlayContext *s = ctx->priv; \ + ThreadData *td = arg; \ + blend_slice_packed_##blend_slice_fn_suffix_(ctx, td->dst, td->src, \ + main_has_alpha_, \ + s->x, s->y, direct_, \ + jobnr, nb_jobs); \ + return 0; \ +} + +// FMT FN A D +DEFINE_BLEND_SLICE_PACKED_FMT(rgb, rgb, 0, 1); +DEFINE_BLEND_SLICE_PACKED_FMT(rgba, rgb, 1, 1); +DEFINE_BLEND_SLICE_PACKED_FMT(rgb_pm, rgb, 0, 0); +DEFINE_BLEND_SLICE_PACKED_FMT(rgba_pm, rgb, 1, 0); static int config_input_main(AVFilterLink *inlink) { @@ -898,6 +775,9 @@ static int config_input_main(AVFilterLink *inlink) case OVERLAY_FORMAT_YUV444: s->blend_slice = s->main_has_alpha ? blend_slice_yuva444 : blend_slice_yuv444; break; + case OVERLAY_FORMAT_YUV444P10: + s->blend_slice = s->main_has_alpha ? blend_slice_yuva444p10 : blend_slice_yuv444p10; + break; case OVERLAY_FORMAT_RGB: s->blend_slice = s->main_has_alpha ? blend_slice_rgba : blend_slice_rgb; break; @@ -921,6 +801,9 @@ static int config_input_main(AVFilterLink *inlink) case AV_PIX_FMT_YUVA444P: s->blend_slice = blend_slice_yuva444; break; + case AV_PIX_FMT_YUVA444P10: + s->blend_slice = blend_slice_yuva444p10; + break; case AV_PIX_FMT_ARGB: case AV_PIX_FMT_RGBA: case AV_PIX_FMT_BGRA: @@ -1007,12 +890,18 @@ static int do_blend(FFFrameSync *fs) return ff_filter_frame(ctx->outputs[0], mainpic); if (s->eval_mode == EVAL_MODE_FRAME) { - int64_t pos = mainpic->pkt_pos; s->var_values[VAR_N] = inlink->frame_count_out; s->var_values[VAR_T] = mainpic->pts == AV_NOPTS_VALUE ? NAN : mainpic->pts * av_q2d(inlink->time_base); - s->var_values[VAR_POS] = pos == -1 ? NAN : pos; +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + { + int64_t pos = mainpic->pkt_pos; + s->var_values[VAR_POS] = pos == -1 ? NAN : pos; + } +FF_ENABLE_DEPRECATION_WARNINGS +#endif s->var_values[VAR_OVERLAY_W] = s->var_values[VAR_OW] = second->width; s->var_values[VAR_OVERLAY_H] = s->var_values[VAR_OH] = second->height; @@ -1020,8 +909,8 @@ static int do_blend(FFFrameSync *fs) s->var_values[VAR_MAIN_H ] = s->var_values[VAR_MH] = mainpic->height; eval_expr(ctx); - av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d\n", - s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_POS], + av_log(ctx, AV_LOG_DEBUG, "n:%f t:%f x:%f xi:%d y:%f yi:%d\n", + s->var_values[VAR_N], s->var_values[VAR_T], s->var_values[VAR_X], s->x, s->var_values[VAR_Y], s->y); } @@ -1060,25 +949,26 @@ static const AVOption overlay_options[] = { { "y", "set the y expression", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, FLAGS }, { "eof_action", "Action to take when encountering EOF from secondary input ", OFFSET(fs.opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT }, - EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, "eof_action" }, - { "repeat", "Repeat the previous frame.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, "eof_action" }, - { "endall", "End both streams.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, "eof_action" }, - { "pass", "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, .flags = FLAGS, "eof_action" }, - { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_FRAME}, 0, EVAL_MODE_NB-1, FLAGS, "eval" }, + EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, .unit = "eof_action" }, + { "repeat", "Repeat the previous frame.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, .unit = "eof_action" }, + { "endall", "End both streams.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, .unit = "eof_action" }, + { "pass", "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, .flags = FLAGS, .unit = "eof_action" }, + { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_FRAME}, 0, EVAL_MODE_NB-1, FLAGS, .unit = "eval" }, { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, { "frame", "eval expressions per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, { "shortest", "force termination when the shortest input terminates", OFFSET(fs.opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, - { "format", "set output format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=OVERLAY_FORMAT_YUV420}, 0, OVERLAY_FORMAT_NB-1, FLAGS, "format" }, + { "format", "set output format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=OVERLAY_FORMAT_YUV420}, 0, OVERLAY_FORMAT_NB-1, FLAGS, .unit = "format" }, { "yuv420", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV420}, .flags = FLAGS, .unit = "format" }, { "yuv420p10", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV420P10}, .flags = FLAGS, .unit = "format" }, { "yuv422", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV422}, .flags = FLAGS, .unit = "format" }, { "yuv422p10", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV422P10}, .flags = FLAGS, .unit = "format" }, { "yuv444", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV444}, .flags = FLAGS, .unit = "format" }, + { "yuv444p10", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_YUV444P10}, .flags = FLAGS, .unit = "format" }, { "rgb", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_RGB}, .flags = FLAGS, .unit = "format" }, { "gbrp", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_GBRP}, .flags = FLAGS, .unit = "format" }, { "auto", "", 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY_FORMAT_AUTO}, .flags = FLAGS, .unit = "format" }, { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(fs.opt_repeatlast), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, - { "alpha", "alpha format", OFFSET(alpha_format), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "alpha_format" }, + { "alpha", "alpha format", OFFSET(alpha_format), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "alpha_format" }, { "straight", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, .flags = FLAGS, .unit = "alpha_format" }, { "premultiplied", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, .flags = FLAGS, .unit = "alpha_format" }, { NULL } diff --git a/libavfilter/vf_overlay.h b/libavfilter/vf_overlay.h index 30a1a7371ca..59749648c32 100644 --- a/libavfilter/vf_overlay.h +++ b/libavfilter/vf_overlay.h @@ -34,7 +34,9 @@ enum var_name { VAR_X, VAR_Y, VAR_N, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_T, VAR_VARS_NB }; @@ -45,6 +47,7 @@ enum OverlayFormat { OVERLAY_FORMAT_YUV422, OVERLAY_FORMAT_YUV422P10, OVERLAY_FORMAT_YUV444, + OVERLAY_FORMAT_YUV444P10, OVERLAY_FORMAT_RGB, OVERLAY_FORMAT_GBRP, OVERLAY_FORMAT_AUTO, diff --git a/libavfilter/vf_overlay_cuda.c b/libavfilter/vf_overlay_cuda.c index b2cbb9c625d..77c7f9b4e7a 100644 --- a/libavfilter/vf_overlay_cuda.c +++ b/libavfilter/vf_overlay_cuda.c @@ -68,7 +68,9 @@ enum var_name { VAR_X, VAR_Y, VAR_N, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_T, VAR_VARS_NB }; @@ -87,7 +89,9 @@ static const char *const var_names[] = { "x", "y", "n", ///< number of frame +#if FF_API_FRAME_PKT "pos", ///< position in the file +#endif "t", ///< timestamp expressed in seconds NULL }; @@ -238,8 +242,6 @@ static int overlay_cuda_blend(FFFrameSync *fs) AVFrame *input_main, *input_overlay; - int pos = 0; - ctx->cu_ctx = cuda_ctx; // read main and overlay frames from inputs @@ -268,11 +270,19 @@ static int overlay_cuda_blend(FFFrameSync *fs) } if (ctx->eval_mode == EVAL_MODE_FRAME) { - pos = input_main->pkt_pos; ctx->var_values[VAR_N] = inlink->frame_count_out; ctx->var_values[VAR_T] = input_main->pts == AV_NOPTS_VALUE ? NAN : input_main->pts * av_q2d(inlink->time_base); - ctx->var_values[VAR_POS] = pos == -1 ? NAN : pos; + +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS + { + int64_t pos = input_main->pkt_pos; + ctx->var_values[VAR_POS] = pos == -1 ? NAN : pos; + } +FF_ENABLE_DEPRECATION_WARNINGS +#endif + ctx->var_values[VAR_OVERLAY_W] = ctx->var_values[VAR_OW] = input_overlay->width; ctx->var_values[VAR_OVERLAY_H] = ctx->var_values[VAR_OH] = input_overlay->height; ctx->var_values[VAR_MAIN_W ] = ctx->var_values[VAR_MW] = input_main->width; @@ -280,8 +290,8 @@ static int overlay_cuda_blend(FFFrameSync *fs) eval_expr(avctx); - av_log(avctx, AV_LOG_DEBUG, "n:%f t:%f pos:%f x:%f xi:%d y:%f yi:%d\n", - ctx->var_values[VAR_N], ctx->var_values[VAR_T], ctx->var_values[VAR_POS], + av_log(avctx, AV_LOG_DEBUG, "n:%f t:%f x:%f xi:%d y:%f yi:%d\n", + ctx->var_values[VAR_N], ctx->var_values[VAR_T], ctx->var_values[VAR_X], ctx->x_position, ctx->var_values[VAR_Y], ctx->y_position); } @@ -355,7 +365,9 @@ static int config_input_overlay(AVFilterLink *inlink) s->var_values[VAR_Y] = NAN; s->var_values[VAR_N] = 0; s->var_values[VAR_T] = NAN; +#if FF_API_FRAME_PKT s->var_values[VAR_POS] = NAN; +#endif if ((ret = set_expr(&s->x_pexpr, s->x_expr, "x", ctx)) < 0 || (ret = set_expr(&s->y_pexpr, s->y_expr, "y", ctx)) < 0) @@ -530,11 +542,11 @@ static const AVOption overlay_cuda_options[] = { { "y", "set the y expression of overlay", OFFSET(y_expr), AV_OPT_TYPE_STRING, { .str = "0" }, 0, 0, FLAGS }, { "eof_action", "Action to take when encountering EOF from secondary input ", OFFSET(fs.opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT }, - EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, "eof_action" }, - { "repeat", "Repeat the previous frame.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, "eof_action" }, - { "endall", "End both streams.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, "eof_action" }, - { "pass", "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, .flags = FLAGS, "eof_action" }, - { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, { .i64 = EVAL_MODE_FRAME }, 0, EVAL_MODE_NB - 1, FLAGS, "eval" }, + EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, .unit = "eof_action" }, + { "repeat", "Repeat the previous frame.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, .unit = "eof_action" }, + { "endall", "End both streams.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, .unit = "eof_action" }, + { "pass", "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, .flags = FLAGS, .unit = "eof_action" }, + { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, { .i64 = EVAL_MODE_FRAME }, 0, EVAL_MODE_NB - 1, FLAGS, .unit = "eval" }, { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, { .i64=EVAL_MODE_INIT }, .flags = FLAGS, .unit = "eval" }, { "frame", "eval expressions per-frame", 0, AV_OPT_TYPE_CONST, { .i64=EVAL_MODE_FRAME }, .flags = FLAGS, .unit = "eval" }, { "shortest", "force termination when the shortest input terminates", OFFSET(fs.opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, diff --git a/libavfilter/vf_overlay_opencl.c b/libavfilter/vf_overlay_opencl.c index 1e3ad903e11..9beb09f05a1 100644 --- a/libavfilter/vf_overlay_opencl.c +++ b/libavfilter/vf_overlay_opencl.c @@ -51,7 +51,7 @@ static int overlay_opencl_load(AVFilterContext *avctx, { OverlayOpenCLContext *ctx = avctx->priv; cl_int cle; - const char *source = ff_opencl_source_overlay; + const char *source = ff_source_overlay_cl; const char *kernel; const AVPixFmtDescriptor *main_desc, *overlay_desc; int err, i, main_planes, overlay_planes; @@ -322,4 +322,5 @@ const AVFilter ff_vf_overlay_opencl = { FILTER_OUTPUTS(overlay_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_overlay_qsv.c b/libavfilter/vf_overlay_qsv.c index 5bec7dd414c..19aaf03c17d 100644 --- a/libavfilter/vf_overlay_qsv.c +++ b/libavfilter/vf_overlay_qsv.c @@ -89,10 +89,10 @@ static const AVOption overlay_qsv_options[] = { { "alpha", "Overlay global alpha", OFFSET(overlay_alpha), AV_OPT_TYPE_INT, { .i64 = 255}, 0, 255, .flags = FLAGS}, { "eof_action", "Action to take when encountering EOF from secondary input ", OFFSET(fs.opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT }, - EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, "eof_action" }, - { "repeat", "Repeat the previous frame.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, "eof_action" }, - { "endall", "End both streams.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, "eof_action" }, - { "pass", "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, .flags = FLAGS, "eof_action" }, + EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, .unit = "eof_action" }, + { "repeat", "Repeat the previous frame.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, .unit = "eof_action" }, + { "endall", "End both streams.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, .unit = "eof_action" }, + { "pass", "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, .flags = FLAGS, .unit = "eof_action" }, { "shortest", "force termination when the shortest input terminates", OFFSET(fs.opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(fs.opt_repeatlast), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, { NULL } @@ -429,4 +429,5 @@ const AVFilter ff_vf_overlay_qsv = { FILTER_QUERY_FUNC(overlay_qsv_query_formats), .priv_class = &overlay_qsv_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_overlay_vaapi.c b/libavfilter/vf_overlay_vaapi.c index 16d1e0e2113..9b1e12bdc80 100644 --- a/libavfilter/vf_overlay_vaapi.c +++ b/libavfilter/vf_overlay_vaapi.c @@ -17,16 +17,14 @@ */ #include -#include "libavutil/avassert.h" -#include "libavutil/mem.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "framesync.h" -#include "formats.h" #include "internal.h" #include "vaapi_vpp.h" +#include "video.h" #include "libavutil/eval.h" enum var_name { @@ -237,6 +235,7 @@ static int overlay_vaapi_blend(FFFrameSync *fs) params[1].blend_state = &blend_state; params[1].surface = (VASurfaceID)(uintptr_t)input_overlay->data[3]; + params[1].surface_region = NULL; params[1].output_region = &overlay_region; } @@ -382,10 +381,10 @@ static const AVOption overlay_vaapi_options[] = { { "alpha", "Overlay global alpha", OFFSET(alpha), AV_OPT_TYPE_FLOAT, { .dbl = 1.0 }, 0.0, 1.0, .flags = FLAGS }, { "eof_action", "Action to take when encountering EOF from secondary input ", OFFSET(fs.opt_eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_REPEAT }, - EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, "eof_action" }, - { "repeat", "Repeat the previous frame.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, "eof_action" }, - { "endall", "End both streams.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, "eof_action" }, - { "pass", "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, .flags = FLAGS, "eof_action" }, + EOF_ACTION_REPEAT, EOF_ACTION_PASS, .flags = FLAGS, .unit = "eof_action" }, + { "repeat", "Repeat the previous frame.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_REPEAT }, .flags = FLAGS, .unit = "eof_action" }, + { "endall", "End both streams.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ENDALL }, .flags = FLAGS, .unit = "eof_action" }, + { "pass", "Pass through the main input.", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, .flags = FLAGS, .unit = "eof_action" }, { "shortest", "force termination when the shortest input terminates", OFFSET(fs.opt_shortest), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "repeatlast", "repeat overlay of the last overlay frame", OFFSET(fs.opt_repeatlast), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS }, { NULL }, diff --git a/libavfilter/vf_overlay_vulkan.c b/libavfilter/vf_overlay_vulkan.c index e87ee830007..c09de24142f 100644 --- a/libavfilter/vf_overlay_vulkan.c +++ b/libavfilter/vf_overlay_vulkan.c @@ -1,4 +1,6 @@ /* + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -19,26 +21,27 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" #include "framesync.h" - -#define CGROUPS (int [3]){ 32, 32, 1 } +#include "video.h" typedef struct OverlayVulkanContext { FFVulkanContext vkctx; + FFFrameSync fs; int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl; - FFFrameSync fs; - FFVkBuffer params_buf; + FFVkSPIRVShader shd; + VkSampler sampler; - /* Shader updators, must be in the main filter struct */ - VkDescriptorImageInfo main_images[3]; - VkDescriptorImageInfo overlay_images[3]; - VkDescriptorImageInfo output_images[3]; - VkDescriptorBufferInfo params_desc; + /* Push constants / options */ + struct { + int32_t o_offset[2*3]; + int32_t o_size[2*3]; + } opts; int overlay_x; int overlay_y; @@ -80,279 +83,114 @@ static const char overlay_alpha[] = { static av_cold int init_filter(AVFilterContext *ctx) { int err; - FFVkSampler *sampler; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; OverlayVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - - sampler = ff_vk_init_sampler(vkctx, 1, VK_FILTER_NEAREST); - if (!sampler) + const int ialpha = av_pix_fmt_desc_get(s->vkctx.input_format)->flags & AV_PIX_FMT_FLAG_ALPHA; + const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(s->vkctx.output_format); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); return AVERROR_EXTERNAL; - - s->pl = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl) - return AVERROR(ENOMEM); - - { /* Create the shader */ - const int ialpha = av_pix_fmt_desc_get(s->vkctx.input_format)->flags & AV_PIX_FMT_FLAG_ALPHA; - - FFVulkanDescriptorSetBinding desc_i[3] = { - { - .name = "main_img", - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->main_images, - .sampler = sampler, - }, - { - .name = "overlay_img", - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->overlay_images, - .sampler = sampler, - }, - { - .name = "output_img", - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), - .mem_quali = "writeonly", - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->output_images, - }, - }; - - FFVulkanDescriptorSetBinding desc_b = { - .name = "params", - .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .mem_quali = "readonly", - .mem_layout = "std430", - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = &s->params_desc, - .buf_content = "ivec2 o_offset[3], o_size[3];", - }; - - FFVkSPIRVShader *shd = ff_vk_init_shader(s->pl, "overlay_compute", - VK_SHADER_STAGE_COMPUTE_BIT); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, CGROUPS); - - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, desc_i, FF_ARRAY_ELEMS(desc_i), 0)); /* set 0 */ - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, &desc_b, 1, 0)); /* set 1 */ - - GLSLD( overlay_noalpha ); - GLSLD( overlay_alpha ); - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - GLSLF(1, int planes = %i; ,planes); - GLSLC(1, for (int i = 0; i < planes; i++) { ); - if (ialpha) - GLSLC(2, overlay_alpha_opaque(i, pos); ); - else - GLSLC(2, overlay_noalpha(i, pos); ); - GLSLC(1, } ); - GLSLC(0, } ); - - RET(ff_vk_compile_shader(vkctx, shd, "main")); - } - - RET(ff_vk_init_pipeline_layout(vkctx, s->pl)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl)); - - { /* Create and update buffer */ - const AVPixFmtDescriptor *desc; - - /* NOTE: std430 requires the same identical struct layout, padding and - * alignment as C, so we're allowed to do this, as this will map - * exactly to what the shader recieves */ - struct { - int32_t o_offset[2*3]; - int32_t o_size[2*3]; - } *par; - - err = ff_vk_create_buf(vkctx, &s->params_buf, - sizeof(*par), - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); - if (err) - return err; - - err = ff_vk_map_buffers(vkctx, &s->params_buf, (uint8_t **)&par, 1, 0); - if (err) - return err; - - desc = av_pix_fmt_desc_get(s->vkctx.output_format); - - par->o_offset[0] = s->overlay_x; - par->o_offset[1] = s->overlay_y; - par->o_offset[2] = par->o_offset[0] >> desc->log2_chroma_w; - par->o_offset[3] = par->o_offset[1] >> desc->log2_chroma_h; - par->o_offset[4] = par->o_offset[0] >> desc->log2_chroma_w; - par->o_offset[5] = par->o_offset[1] >> desc->log2_chroma_h; - - par->o_size[0] = s->overlay_w; - par->o_size[1] = s->overlay_h; - par->o_size[2] = par->o_size[0] >> desc->log2_chroma_w; - par->o_size[3] = par->o_size[1] >> desc->log2_chroma_h; - par->o_size[4] = par->o_size[0] >> desc->log2_chroma_w; - par->o_size[5] = par->o_size[1] >> desc->log2_chroma_h; - - err = ff_vk_unmap_buffers(vkctx, &s->params_buf, 1, 1); - if (err) - return err; - - s->params_desc.buffer = s->params_buf.buf; - s->params_desc.range = VK_WHOLE_SIZE; - - ff_vk_update_descriptor_set(vkctx, s->pl, 1); } - /* Execution context */ - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_NEAREST)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "overlay_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, ivec2 o_offset[3]; ); + GLSLC(1, ivec2 o_size[3]; ); + GLSLC(0, }; ); + GLSLC(0, ); + + ff_vk_add_push_constant(&s->pl, 0, sizeof(s->opts), + VK_SHADER_STAGE_COMPUTE_BIT); + + desc = (FFVulkanDescriptorSetBinding []) { + { + .name = "main_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "overlay_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "output_img", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 3, 0, 0)); + + GLSLD( overlay_noalpha ); + GLSLD( overlay_alpha ); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + GLSLF(1, int planes = %i; ,planes); + GLSLC(1, for (int i = 0; i < planes; i++) { ); + if (ialpha) + GLSLC(2, overlay_alpha_opaque(i, pos); ); + else + GLSLC(2, overlay_noalpha(i, pos); ); + GLSLC(1, } ); + GLSLC(0, } ); + + RET(spv->compile_shader(spv, ctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); + + s->opts.o_offset[0] = s->overlay_x; + s->opts.o_offset[1] = s->overlay_y; + s->opts.o_offset[2] = s->opts.o_offset[0] >> pix_desc->log2_chroma_w; + s->opts.o_offset[3] = s->opts.o_offset[1] >> pix_desc->log2_chroma_h; + s->opts.o_offset[4] = s->opts.o_offset[0] >> pix_desc->log2_chroma_w; + s->opts.o_offset[5] = s->opts.o_offset[1] >> pix_desc->log2_chroma_h; + + s->opts.o_size[0] = s->overlay_w; + s->opts.o_size[1] = s->overlay_h; + s->opts.o_size[2] = s->opts.o_size[0] >> pix_desc->log2_chroma_w; + s->opts.o_size[3] = s->opts.o_size[1] >> pix_desc->log2_chroma_h; + s->opts.o_size[4] = s->opts.o_size[0] >> pix_desc->log2_chroma_w; + s->opts.o_size[5] = s->opts.o_size[1] >> pix_desc->log2_chroma_h; s->initialized = 1; - return 0; - fail: - return err; -} - -static int process_frames(AVFilterContext *avctx, AVFrame *out_f, - AVFrame *main_f, AVFrame *overlay_f) -{ - int err; - VkCommandBuffer cmd_buf; - OverlayVulkanContext *s = avctx->priv; - FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &vkctx->vkfn; - int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - AVVkFrame *out = (AVVkFrame *)out_f->data[0]; - AVVkFrame *main = (AVVkFrame *)main_f->data[0]; - AVVkFrame *overlay = (AVVkFrame *)overlay_f->data[0]; - - AVHWFramesContext *main_fc = (AVHWFramesContext*)main_f->hw_frames_ctx->data; - AVHWFramesContext *overlay_fc = (AVHWFramesContext*)overlay_f->hw_frames_ctx->data; - - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - const VkFormat *main_sw_formats = av_vkfmt_from_pixfmt(main_fc->sw_format); - const VkFormat *overlay_sw_formats = av_vkfmt_from_pixfmt(overlay_fc->sw_format); - - /* Update descriptors and init the exec context */ - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->main_images[i].imageView, main->img[i], - main_sw_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->overlay_images[i].imageView, overlay->img[i], - overlay_sw_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->main_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->overlay_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } - - ff_vk_update_descriptor_set(vkctx, s->pl, 0); - - for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier bar[3] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = main->layout[i], - .newLayout = s->main_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = main->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = overlay->layout[i], - .newLayout = s->overlay_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = overlay->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(bar), bar); - - main->layout[i] = bar[0].newLayout; - main->access[i] = bar[0].dstAccessMask; - - overlay->layout[i] = bar[1].newLayout; - overlay->access[i] = bar[1].dstAccessMask; - - out->layout[i] = bar[2].newLayout; - out->access[i] = bar[2].dstAccessMask; - } - - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl); - - vk->CmdDispatch(cmd_buf, - FFALIGN(s->vkctx.output_width, CGROUPS[0])/CGROUPS[0], - FFALIGN(s->vkctx.output_height, CGROUPS[1])/CGROUPS[1], 1); - - ff_vk_add_exec_dep(vkctx, s->exec, main_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, overlay_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, out_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(vkctx, s->exec); - if (err) - return err; + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); - ff_vk_qf_rotate(&s->qf); - - return err; - -fail: - ff_vk_discard_exec_deps(s->exec); return err; } @@ -394,7 +232,9 @@ static int overlay_vulkan_blend(FFFrameSync *fs) goto fail; } - RET(process_frames(ctx, out, input_main, input_overlay)); + RET(ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->pl, + out, (AVFrame *[]){ input_main, input_overlay }, 2, + s->sampler, &s->opts, sizeof(s->opts))); err = av_frame_copy_props(out, input_main); if (err < 0) @@ -443,8 +283,17 @@ static av_cold int overlay_vulkan_init(AVFilterContext *avctx) static void overlay_vulkan_uninit(AVFilterContext *avctx) { OverlayVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); - ff_vk_free_buf(&s->vkctx, &s->params_buf); ff_vk_uninit(&s->vkctx); ff_framesync_uninit(&s->fs); @@ -494,4 +343,5 @@ const AVFilter ff_vf_overlay_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &overlay_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_owdenoise.c b/libavfilter/vf_owdenoise.c index bb99e8f33c4..fad85bfa8ef 100644 --- a/libavfilter/vf_owdenoise.c +++ b/libavfilter/vf_owdenoise.c @@ -39,6 +39,7 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "internal.h" +#include "video.h" typedef struct OWDenoiseContext { const AVClass *class; @@ -354,20 +355,13 @@ static const AVFilterPad owdenoise_inputs[] = { }, }; -static const AVFilterPad owdenoise_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_owdenoise = { .name = "owdenoise", .description = NULL_IF_CONFIG_SMALL("Denoise using wavelets."), .priv_size = sizeof(OWDenoiseContext), .uninit = uninit, FILTER_INPUTS(owdenoise_inputs), - FILTER_OUTPUTS(owdenoise_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &owdenoise_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_pad.c b/libavfilter/vf_pad.c index e52f7284d4a..2cced46f296 100644 --- a/libavfilter/vf_pad.c +++ b/libavfilter/vf_pad.c @@ -111,7 +111,7 @@ static int config_input(AVFilterLink *inlink) double var_values[VARS_NB], res; char *expr; - ff_draw_init(&s->draw, inlink->format, 0); + ff_draw_init2(&s->draw, inlink->format, inlink->colorspace, inlink->color_range, 0); ff_draw_color(&s->draw, &s->color, s->rgba_color); var_values[VAR_IN_W] = var_values[VAR_IW] = inlink->w; @@ -424,7 +424,7 @@ static const AVOption pad_options[] = { { "x", "set the x offset expression for the input image position", OFFSET(x_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, FLAGS }, { "y", "set the y offset expression for the input image position", OFFSET(y_expr), AV_OPT_TYPE_STRING, {.str = "0"}, 0, 0, FLAGS }, { "color", "set the color of the padded area border", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str = "black"}, .flags = FLAGS }, - { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" }, + { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, .unit = "eval" }, { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, { "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, { "aspect", "pad to fit an aspect instead of a resolution", OFFSET(aspect), AV_OPT_TYPE_RATIONAL, {.dbl = 0}, 0, DBL_MAX, FLAGS }, diff --git a/libavfilter/vf_pad_opencl.c b/libavfilter/vf_pad_opencl.c index 728e850f720..7c163a1d0e4 100644 --- a/libavfilter/vf_pad_opencl.c +++ b/libavfilter/vf_pad_opencl.c @@ -19,10 +19,9 @@ #include "libavutil/colorspace.h" #include "libavutil/eval.h" #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "opencl.h" #include "opencl_source.h" @@ -93,7 +92,7 @@ static int pad_opencl_init(AVFilterContext *avctx, AVFrame *input_frame) ctx->hsub = desc->log2_chroma_w; ctx->vsub = desc->log2_chroma_h; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_pad, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_pad_cl, 1); if (err < 0) goto fail; @@ -391,5 +390,6 @@ const AVFilter ff_vf_pad_opencl = { FILTER_INPUTS(pad_opencl_inputs), FILTER_OUTPUTS(pad_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), - .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_palettegen.c b/libavfilter/vf_palettegen.c index 4b69d3c63b2..dcd4e99afff 100644 --- a/libavfilter/vf_palettegen.c +++ b/libavfilter/vf_palettegen.c @@ -29,8 +29,10 @@ #include "libavutil/opt.h" #include "libavutil/intreadwrite.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "palette.h" +#include "video.h" /* Reference a color and how much it's used */ struct color_ref { @@ -88,10 +90,10 @@ static const AVOption palettegen_options[] = { { "max_colors", "set the maximum number of colors to use in the palette", OFFSET(max_colors), AV_OPT_TYPE_INT, {.i64=256}, 2, 256, FLAGS }, { "reserve_transparent", "reserve a palette entry for transparency", OFFSET(reserve_transparent), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, { "transparency_color", "set a background color for transparency", OFFSET(transparency_color), AV_OPT_TYPE_COLOR, {.str="lime"}, 0, 0, FLAGS }, - { "stats_mode", "set statistics mode", OFFSET(stats_mode), AV_OPT_TYPE_INT, {.i64=STATS_MODE_ALL_FRAMES}, 0, NB_STATS_MODE-1, FLAGS, "mode" }, - { "full", "compute full frame histograms", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_ALL_FRAMES}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "diff", "compute histograms only for the part that differs from previous frame", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_DIFF_FRAMES}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "single", "compute new histogram for each frame", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_SINGLE_FRAMES}, INT_MIN, INT_MAX, FLAGS, "mode" }, + { "stats_mode", "set statistics mode", OFFSET(stats_mode), AV_OPT_TYPE_INT, {.i64=STATS_MODE_ALL_FRAMES}, 0, NB_STATS_MODE-1, FLAGS, .unit = "mode" }, + { "full", "compute full frame histograms", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_ALL_FRAMES}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "diff", "compute histograms only for the part that differs from previous frame", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_DIFF_FRAMES}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "single", "compute new histogram for each frame", 0, AV_OPT_TYPE_CONST, {.i64=STATS_MODE_SINGLE_FRAMES}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, { NULL } }; diff --git a/libavfilter/vf_paletteuse.c b/libavfilter/vf_paletteuse.c index 5fa7a605cef..8394f62f0f5 100644 --- a/libavfilter/vf_paletteuse.c +++ b/libavfilter/vf_paletteuse.c @@ -31,9 +31,11 @@ #include "libavutil/qsort.h" #include "avfilter.h" #include "filters.h" +#include "formats.h" #include "framesync.h" #include "internal.h" #include "palette.h" +#include "video.h" enum dithering_mode { DITHERING_NONE, @@ -110,18 +112,18 @@ typedef struct PaletteUseContext { #define OFFSET(x) offsetof(PaletteUseContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption paletteuse_options[] = { - { "dither", "select dithering mode", OFFSET(dither), AV_OPT_TYPE_INT, {.i64=DITHERING_SIERRA2_4A}, 0, NB_DITHERING-1, FLAGS, "dithering_mode" }, - { "bayer", "ordered 8x8 bayer dithering (deterministic)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_BAYER}, INT_MIN, INT_MAX, FLAGS, "dithering_mode" }, - { "heckbert", "dithering as defined by Paul Heckbert in 1982 (simple error diffusion)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_HECKBERT}, INT_MIN, INT_MAX, FLAGS, "dithering_mode" }, - { "floyd_steinberg", "Floyd and Steingberg dithering (error diffusion)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_FLOYD_STEINBERG}, INT_MIN, INT_MAX, FLAGS, "dithering_mode" }, - { "sierra2", "Frankie Sierra dithering v2 (error diffusion)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_SIERRA2}, INT_MIN, INT_MAX, FLAGS, "dithering_mode" }, - { "sierra2_4a", "Frankie Sierra dithering v2 \"Lite\" (error diffusion)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_SIERRA2_4A}, INT_MIN, INT_MAX, FLAGS, "dithering_mode" }, - { "sierra3", "Frankie Sierra dithering v3 (error diffusion)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_SIERRA3}, INT_MIN, INT_MAX, FLAGS, "dithering_mode" }, - { "burkes", "Burkes dithering (error diffusion)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_BURKES}, INT_MIN, INT_MAX, FLAGS, "dithering_mode" }, - { "atkinson", "Atkinson dithering by Bill Atkinson at Apple Computer (error diffusion)",0, AV_OPT_TYPE_CONST, {.i64=DITHERING_ATKINSON}, INT_MIN, INT_MAX, FLAGS, "dithering_mode" }, + { "dither", "select dithering mode", OFFSET(dither), AV_OPT_TYPE_INT, {.i64=DITHERING_SIERRA2_4A}, 0, NB_DITHERING-1, FLAGS, .unit = "dithering_mode" }, + { "bayer", "ordered 8x8 bayer dithering (deterministic)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_BAYER}, INT_MIN, INT_MAX, FLAGS, .unit = "dithering_mode" }, + { "heckbert", "dithering as defined by Paul Heckbert in 1982 (simple error diffusion)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_HECKBERT}, INT_MIN, INT_MAX, FLAGS, .unit = "dithering_mode" }, + { "floyd_steinberg", "Floyd and Steingberg dithering (error diffusion)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_FLOYD_STEINBERG}, INT_MIN, INT_MAX, FLAGS, .unit = "dithering_mode" }, + { "sierra2", "Frankie Sierra dithering v2 (error diffusion)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_SIERRA2}, INT_MIN, INT_MAX, FLAGS, .unit = "dithering_mode" }, + { "sierra2_4a", "Frankie Sierra dithering v2 \"Lite\" (error diffusion)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_SIERRA2_4A}, INT_MIN, INT_MAX, FLAGS, .unit = "dithering_mode" }, + { "sierra3", "Frankie Sierra dithering v3 (error diffusion)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_SIERRA3}, INT_MIN, INT_MAX, FLAGS, .unit = "dithering_mode" }, + { "burkes", "Burkes dithering (error diffusion)", 0, AV_OPT_TYPE_CONST, {.i64=DITHERING_BURKES}, INT_MIN, INT_MAX, FLAGS, .unit = "dithering_mode" }, + { "atkinson", "Atkinson dithering by Bill Atkinson at Apple Computer (error diffusion)",0, AV_OPT_TYPE_CONST, {.i64=DITHERING_ATKINSON}, INT_MIN, INT_MAX, FLAGS, .unit = "dithering_mode" }, { "bayer_scale", "set scale for bayer dithering", OFFSET(bayer_scale), AV_OPT_TYPE_INT, {.i64=2}, 0, 5, FLAGS }, - { "diff_mode", "set frame difference mode", OFFSET(diff_mode), AV_OPT_TYPE_INT, {.i64=DIFF_MODE_NONE}, 0, NB_DIFF_MODE-1, FLAGS, "diff_mode" }, - { "rectangle", "process smallest different rectangle", 0, AV_OPT_TYPE_CONST, {.i64=DIFF_MODE_RECTANGLE}, INT_MIN, INT_MAX, FLAGS, "diff_mode" }, + { "diff_mode", "set frame difference mode", OFFSET(diff_mode), AV_OPT_TYPE_INT, {.i64=DIFF_MODE_NONE}, 0, NB_DIFF_MODE-1, FLAGS, .unit = "diff_mode" }, + { "rectangle", "process smallest different rectangle", 0, AV_OPT_TYPE_CONST, {.i64=DIFF_MODE_RECTANGLE}, INT_MIN, INT_MAX, FLAGS, .unit = "diff_mode" }, { "new", "take new palette for each output frame", OFFSET(new), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS }, { "alpha_threshold", "set the alpha threshold for transparency", OFFSET(trans_thresh), AV_OPT_TYPE_INT, {.i64=128}, 0, 255, FLAGS }, @@ -779,9 +781,8 @@ static int apply_palette(AVFilterLink *inlink, AVFrame *in, AVFrame **outf) set_processing_window(s->diff_mode, s->last_in, in, s->last_out, out, &x, &y, &w, &h); - av_frame_unref(s->last_in); av_frame_unref(s->last_out); - if ((ret = av_frame_ref(s->last_in, in)) < 0 || + if ((ret = av_frame_replace(s->last_in, in)) < 0 || (ret = av_frame_ref(s->last_out, out)) < 0 || (ret = ff_inlink_make_frame_writable(inlink, &s->last_in)) < 0) { av_frame_free(&out); @@ -844,7 +845,7 @@ static void load_palette(PaletteUseContext *s, const AVFrame *palette_frame) { int i, x, y; const uint32_t *p = (const uint32_t *)palette_frame->data[0]; - const int p_linesize = palette_frame->linesize[0] >> 2; + const ptrdiff_t p_linesize = palette_frame->linesize[0] >> 2; s->transparency_index = -1; diff --git a/libavfilter/vf_perspective.c b/libavfilter/vf_perspective.c index da720dcb545..0206c75b6d0 100644 --- a/libavfilter/vf_perspective.c +++ b/libavfilter/vf_perspective.c @@ -25,7 +25,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -77,15 +76,15 @@ static const AVOption perspective_options[] = { { "y2", "set bottom left y coordinate", OFFSET(expr_str[2][1]), AV_OPT_TYPE_STRING, {.str="H"}, 0, 0, FLAGS }, { "x3", "set bottom right x coordinate", OFFSET(expr_str[3][0]), AV_OPT_TYPE_STRING, {.str="W"}, 0, 0, FLAGS }, { "y3", "set bottom right y coordinate", OFFSET(expr_str[3][1]), AV_OPT_TYPE_STRING, {.str="H"}, 0, 0, FLAGS }, - { "interpolation", "set interpolation", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=LINEAR}, 0, 1, FLAGS, "interpolation" }, - { "linear", "", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, "interpolation" }, - { "cubic", "", 0, AV_OPT_TYPE_CONST, {.i64=CUBIC}, 0, 0, FLAGS, "interpolation" }, - { "sense", "specify the sense of the coordinates", OFFSET(sense), AV_OPT_TYPE_INT, {.i64=PERSPECTIVE_SENSE_SOURCE}, 0, 1, FLAGS, "sense"}, + { "interpolation", "set interpolation", OFFSET(interpolation), AV_OPT_TYPE_INT, {.i64=LINEAR}, 0, 1, FLAGS, .unit = "interpolation" }, + { "linear", "", 0, AV_OPT_TYPE_CONST, {.i64=LINEAR}, 0, 0, FLAGS, .unit = "interpolation" }, + { "cubic", "", 0, AV_OPT_TYPE_CONST, {.i64=CUBIC}, 0, 0, FLAGS, .unit = "interpolation" }, + { "sense", "specify the sense of the coordinates", OFFSET(sense), AV_OPT_TYPE_INT, {.i64=PERSPECTIVE_SENSE_SOURCE}, 0, 1, FLAGS, .unit = "sense"}, { "source", "specify locations in source to send to corners in destination", - 0, AV_OPT_TYPE_CONST, {.i64=PERSPECTIVE_SENSE_SOURCE}, 0, 0, FLAGS, "sense"}, + 0, AV_OPT_TYPE_CONST, {.i64=PERSPECTIVE_SENSE_SOURCE}, 0, 0, FLAGS, .unit = "sense"}, { "destination", "specify locations in destination to send corners of source", - 0, AV_OPT_TYPE_CONST, {.i64=PERSPECTIVE_SENSE_DESTINATION}, 0, 0, FLAGS, "sense"}, - { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" }, + 0, AV_OPT_TYPE_CONST, {.i64=PERSPECTIVE_SENSE_DESTINATION}, 0, 0, FLAGS, .unit = "sense"}, + { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, .unit = "eval" }, { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, { "frame", "eval expressions per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, @@ -495,13 +494,6 @@ static const AVFilterPad perspective_inputs[] = { }, }; -static const AVFilterPad perspective_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_perspective = { .name = "perspective", .description = NULL_IF_CONFIG_SMALL("Correct the perspective of video."), @@ -509,7 +501,7 @@ const AVFilter ff_vf_perspective = { .init = init, .uninit = uninit, FILTER_INPUTS(perspective_inputs), - FILTER_OUTPUTS(perspective_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &perspective_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_phase.c b/libavfilter/vf_phase.c index 1cb71e0e432..4fd6d2b6e59 100644 --- a/libavfilter/vf_phase.c +++ b/libavfilter/vf_phase.c @@ -23,7 +23,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -75,10 +74,10 @@ typedef struct PhaseContext { #define OFFSET(x) offsetof(PhaseContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM -#define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, unit } +#define CONST(name, help, val, u) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, .unit = u } static const AVOption phase_options[] = { - { "mode", "set phase mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=AUTO_ANALYZE}, PROGRESSIVE, AUTO_ANALYZE, FLAGS, "mode" }, + { "mode", "set phase mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=AUTO_ANALYZE}, PROGRESSIVE, AUTO_ANALYZE, FLAGS, .unit = "mode" }, CONST("p", "progressive", PROGRESSIVE, "mode"), CONST("t", "top first", TOP_FIRST, "mode"), CONST("b", "bottom first", BOTTOM_FIRST, "mode"), @@ -218,13 +217,6 @@ static const AVFilterPad phase_inputs[] = { }, }; -static const AVFilterPad phase_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_phase = { .name = "phase", .description = NULL_IF_CONFIG_SMALL("Phase shift fields."), @@ -232,7 +224,7 @@ const AVFilter ff_vf_phase = { .priv_class = &phase_class, .uninit = uninit, FILTER_INPUTS(phase_inputs), - FILTER_OUTPUTS(phase_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_photosensitivity.c b/libavfilter/vf_photosensitivity.c index e05d4d02621..e7920b32425 100644 --- a/libavfilter/vf_photosensitivity.c +++ b/libavfilter/vf_photosensitivity.c @@ -20,13 +20,10 @@ #include -#include "libavutil/imgutils.h" #include "libavutil/opt.h" -#include "libavutil/pixdesc.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -310,13 +307,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_photosensitivity = { .name = "photosensitivity", .description = NULL_IF_CONFIG_SMALL("Filter out photosensitive epilepsy seizure-inducing flashes."), @@ -324,6 +314,6 @@ const AVFilter ff_vf_photosensitivity = { .priv_class = &photosensitivity_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24), }; diff --git a/libavfilter/vf_pixdesctest.c b/libavfilter/vf_pixdesctest.c index 12815aea9c4..c2638d058f3 100644 --- a/libavfilter/vf_pixdesctest.c +++ b/libavfilter/vf_pixdesctest.c @@ -115,18 +115,11 @@ static const AVFilterPad avfilter_vf_pixdesctest_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_pixdesctest_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_pixdesctest = { .name = "pixdesctest", .description = NULL_IF_CONFIG_SMALL("Test pixel format definitions."), .priv_size = sizeof(PixdescTestContext), .uninit = uninit, FILTER_INPUTS(avfilter_vf_pixdesctest_inputs), - FILTER_OUTPUTS(avfilter_vf_pixdesctest_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_pixelize.c b/libavfilter/vf_pixelize.c index a4365159863..4eb236f1219 100644 --- a/libavfilter/vf_pixelize.c +++ b/libavfilter/vf_pixelize.c @@ -310,11 +310,11 @@ static const AVOption pixelize_options[] = { { "w", "set block width", OFFSET(block_w[0]), AV_OPT_TYPE_INT, {.i64=16}, 1, 1024, FLAGS }, { "height", "set block height", OFFSET(block_h[0]), AV_OPT_TYPE_INT, {.i64=16}, 1, 1024, FLAGS }, { "h", "set block height", OFFSET(block_h[0]), AV_OPT_TYPE_INT, {.i64=16}, 1, 1024, FLAGS }, - { "mode", "set the pixelize mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, PIXELIZE_MODES-1, FLAGS, "mode" }, - { "m", "set the pixelize mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, PIXELIZE_MODES-1, FLAGS, "mode" }, - { "avg", "average", 0, AV_OPT_TYPE_CONST, {.i64=PIXELIZE_AVG}, 0, 0, FLAGS, "mode" }, - { "min", "minimum", 0, AV_OPT_TYPE_CONST, {.i64=PIXELIZE_MIN}, 0, 0, FLAGS, "mode" }, - { "max", "maximum", 0, AV_OPT_TYPE_CONST, {.i64=PIXELIZE_MAX}, 0, 0, FLAGS, "mode" }, + { "mode", "set the pixelize mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, PIXELIZE_MODES-1, FLAGS, .unit = "mode" }, + { "m", "set the pixelize mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, PIXELIZE_MODES-1, FLAGS, .unit = "mode" }, + { "avg", "average", 0, AV_OPT_TYPE_CONST, {.i64=PIXELIZE_AVG}, 0, 0, FLAGS, .unit = "mode" }, + { "min", "minimum", 0, AV_OPT_TYPE_CONST, {.i64=PIXELIZE_MIN}, 0, 0, FLAGS, .unit = "mode" }, + { "max", "maximum", 0, AV_OPT_TYPE_CONST, {.i64=PIXELIZE_MAX}, 0, 0, FLAGS, .unit = "mode" }, { "planes", "set what planes to filter", OFFSET(planes), AV_OPT_TYPE_FLAGS, {.i64=15}, 0, 15, FLAGS }, { "p", "set what planes to filter", OFFSET(planes), AV_OPT_TYPE_FLAGS, {.i64=15}, 0, 15, FLAGS }, { NULL }, diff --git a/libavfilter/vf_pp.c b/libavfilter/vf_pp.c index 13a013a12da..aa37325a243 100644 --- a/libavfilter/vf_pp.c +++ b/libavfilter/vf_pp.c @@ -29,6 +29,7 @@ #include "internal.h" #include "qp_table.h" +#include "video.h" #include "libpostproc/postprocess.h" @@ -174,13 +175,6 @@ static const AVFilterPad pp_inputs[] = { }, }; -static const AVFilterPad pp_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_pp = { .name = "pp", .description = NULL_IF_CONFIG_SMALL("Filter video using libpostproc."), @@ -188,7 +182,7 @@ const AVFilter ff_vf_pp = { .init = pp_init, .uninit = pp_uninit, FILTER_INPUTS(pp_inputs), - FILTER_OUTPUTS(pp_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .process_command = pp_process_command, .priv_class = &pp_class, diff --git a/libavfilter/vf_pp7.c b/libavfilter/vf_pp7.c index b7c7cf9dde5..c4f1b200865 100644 --- a/libavfilter/vf_pp7.c +++ b/libavfilter/vf_pp7.c @@ -27,6 +27,7 @@ * project, and ported by Arwa Arif for FFmpeg. */ +#include "libavutil/emms.h" #include "libavutil/imgutils.h" #include "libavutil/mem_internal.h" #include "libavutil/opt.h" @@ -34,6 +35,7 @@ #include "internal.h" #include "qp_table.h" #include "vf_pp7.h" +#include "video.h" enum mode { MODE_HARD, @@ -45,10 +47,10 @@ enum mode { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption pp7_options[] = { { "qp", "force a constant quantizer parameter", OFFSET(qp), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 64, FLAGS }, - { "mode", "set thresholding mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_MEDIUM}, 0, 2, FLAGS, "mode" }, - { "hard", "hard thresholding", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_HARD}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "soft", "soft thresholding", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_SOFT}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "medium", "medium thresholding", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_MEDIUM}, INT_MIN, INT_MAX, FLAGS, "mode" }, + { "mode", "set thresholding mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_MEDIUM}, 0, 2, FLAGS, .unit = "mode" }, + { "hard", "hard thresholding", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_HARD}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "soft", "soft thresholding", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_SOFT}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "medium", "medium thresholding", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_MEDIUM}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, { NULL } }; @@ -385,20 +387,13 @@ static const AVFilterPad pp7_inputs[] = { }, }; -static const AVFilterPad pp7_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_pp7 = { .name = "pp7", .description = NULL_IF_CONFIG_SMALL("Apply Postprocessing 7 filter."), .priv_size = sizeof(PP7Context), .uninit = uninit, FILTER_INPUTS(pp7_inputs), - FILTER_OUTPUTS(pp7_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &pp7_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, diff --git a/libavfilter/vf_premultiply.c b/libavfilter/vf_premultiply.c index e077d46a9a0..5e97c2000f2 100644 --- a/libavfilter/vf_premultiply.c +++ b/libavfilter/vf_premultiply.c @@ -36,8 +36,8 @@ typedef struct ThreadData { typedef struct PreMultiplyContext { const AVClass *class; - int width[4], height[4]; - int linesize[4]; + int width[AV_VIDEO_MAX_PLANES], height[AV_VIDEO_MAX_PLANES]; + int linesize[AV_VIDEO_MAX_PLANES]; int nb_planes; int planes; int inverse; @@ -45,7 +45,7 @@ typedef struct PreMultiplyContext { int half, depth, offset, max; FFFrameSync fs; - void (*premultiply[4])(const uint8_t *msrc, const uint8_t *asrc, + void (*premultiply[AV_VIDEO_MAX_PLANES])(const uint8_t *msrc, const uint8_t *asrc, uint8_t *dst, ptrdiff_t mlinesize, ptrdiff_t alinesize, ptrdiff_t dlinesize, diff --git a/libavfilter/vf_procamp_vaapi.c b/libavfilter/vf_procamp_vaapi.c index 4a3b9d0766f..b535a36d33e 100644 --- a/libavfilter/vf_procamp_vaapi.c +++ b/libavfilter/vf_procamp_vaapi.c @@ -21,9 +21,9 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "vaapi_vpp.h" +#include "video.h" // ProcAmp Min/Max/Default Values #define BRIGHTNESS_MIN -100.0F @@ -136,6 +136,9 @@ static int procamp_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame av_get_pix_fmt_name(input_frame->format), input_frame->width, input_frame->height, input_frame->pts); + if (vpp_ctx->passthrough) + return ff_filter_frame(outlink, input_frame); + if (vpp_ctx->va_context == VA_INVALID_ID) return AVERROR(EINVAL); @@ -179,11 +182,18 @@ static int procamp_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame static av_cold int procamp_vaapi_init(AVFilterContext *avctx) { VAAPIVPPContext *vpp_ctx = avctx->priv; + ProcampVAAPIContext *ctx = avctx->priv; + float eps = 1.0e-10f; ff_vaapi_vpp_ctx_init(avctx); vpp_ctx->pipeline_uninit = ff_vaapi_vpp_pipeline_uninit; vpp_ctx->build_filter_params = procamp_vaapi_build_filter_params; vpp_ctx->output_format = AV_PIX_FMT_NONE; + if (fabs(ctx->saturation - SATURATION_DEFAULT) < eps && + fabs(ctx->bright - BRIGHTNESS_DEFAULT) < eps && + fabs(ctx->contrast - CONTRAST_DEFAULT) < eps && + fabs(ctx->hue - HUE_DEFAULT) < eps) + vpp_ctx->passthrough = 1; return 0; } diff --git a/libavfilter/vf_program_opencl.c b/libavfilter/vf_program_opencl.c index 55c1e6547c4..8a4881b38e4 100644 --- a/libavfilter/vf_program_opencl.c +++ b/libavfilter/vf_program_opencl.c @@ -362,7 +362,8 @@ const AVFilter ff_vf_program_opencl = { .description = NULL_IF_CONFIG_SMALL("Filter video using an OpenCL program"), .priv_size = sizeof(ProgramOpenCLContext), .priv_class = &program_opencl_class, - .flags = AVFILTER_FLAG_DYNAMIC_INPUTS, + .flags = AVFILTER_FLAG_DYNAMIC_INPUTS | + AVFILTER_FLAG_HWDEVICE, .preinit = &program_opencl_framesync_preinit, .init = &program_opencl_init, .uninit = &program_opencl_uninit, @@ -421,6 +422,7 @@ const AVFilter ff_vsrc_openclsrc = { FILTER_OUTPUTS(openclsrc_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; #endif diff --git a/libavfilter/vf_pseudocolor.c b/libavfilter/vf_pseudocolor.c index 89dc2f0901f..cfdfac7842e 100644 --- a/libavfilter/vf_pseudocolor.c +++ b/libavfilter/vf_pseudocolor.c @@ -25,7 +25,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -68,6 +67,12 @@ enum Curves { CIVIDIS, SOLAR, SPECTRAL, + COOL, + HEAT, + FIERY, + BLUES, + GREEN, + HELIX, NB_CURVES, }; @@ -87,6 +92,12 @@ enum Presets { PRESET_PREFERRED, PRESET_TOTAL, PRESET_SPECTRAL, + PRESET_COOL, + PRESET_HEAT, + PRESET_FIERY, + PRESET_BLUES, + PRESET_GREEN, + PRESET_HELIX, NB_PRESETS, }; @@ -96,6 +107,7 @@ typedef struct Curve { double coef[3][8]; double offset[3]; curve_fun fun[3]; + int yuv; } Curve; typedef struct Fill { @@ -137,6 +149,46 @@ static double solarfun(double x) return 0.5 * sin(x) + 0.5; } +static double coolfunu(double x) +{ + return 0.25 * sin(2.0 * x * M_PI - M_PI) + 0.5; +} + +static double coolfunv(double x) +{ + return 0.25 * sin(2.0 * x * M_PI) + 0.5; +} + +static double heatfunu(double x) +{ + return 0.25 * cos(2.0 * x * M_PI + M_PI) + 0.75; +} + +static double heatfunv(double x) +{ + return 0.25 * sin(2.0 * x * M_PI) + 0.5; +} + +static double fieryfunu(double x) +{ + return 0.75 - 0.25 * cos(2.0 * x * M_PI); +} + +static double fieryfunv(double x) +{ + return 0.25 + 0.25 * cos(2.0 * x * M_PI); +} + +static double helixfunu(double x) +{ + return 0.5 + 0.15 * sin(5.0 * x * M_PI + M_PI); +} + +static double helixfunv(double x) +{ + return 0.5 + 0.15 * cos(6.0 * x * M_PI + M_PI_2); +} + static const Curve curves[] = { [MAGMA] = {{ @@ -181,6 +233,54 @@ static const Curve curves[] = { 1.2526e-15, -1.2203e-12, 4.7013e-10, -8.9360e-08, 8.3839e-06, -3.6642e-04, 1.4784e-02, -9.8075e-03 }, { 1.4755e-15, -1.6765e-12, 7.3188e-10, -1.5522e-07, 1.6406e-05, -7.7883e-04, 1.4502e-02, 2.1597e-01 }, }, .fun = { limit, limit, limit }, }, + [COOL] = {{ + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + }, + .offset = { 0., 0., 0 }, + .yuv = 1, + .fun = { coolfunu, limit, coolfunv }, }, + [HEAT] = {{ + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + }, + .offset = { 0., 0., 0 }, + .yuv = 1, + .fun = { heatfunu, limit, heatfunv }, }, + [FIERY] = {{ + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + }, + .offset = { 0., 0., 0 }, + .yuv = 1, + .fun = { fieryfunu, limit, fieryfunv }, }, + [BLUES] = {{ + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + }, + .offset = { 0., 0., 0 }, + .yuv = 1, + .fun = { fieryfunv, limit, fieryfunu }, }, + [GREEN] = {{ + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + }, + .offset = { 0., 0., 0 }, + .yuv = 1, + .fun = { fieryfunv, limit, fieryfunv }, }, + [HELIX] = {{ + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + { 0, 0, 0, 0, 0, 0, 1./256, 0 }, + }, + .offset = { 0., 0., 0 }, + .yuv = 1, + .fun = { helixfunu, limit, helixfunv }, }, }; static const Preset presets[] = @@ -200,6 +300,12 @@ static const Preset presets[] = [PRESET_HIGHLIGHTS] = { 3, highlights_range, NULL, highlights_fills }, [PRESET_SOLAR] = { 1, &full_range, &curves[SOLAR], NULL }, [PRESET_SPECTRAL]= { 1, &full_range, &curves[SPECTRAL],NULL }, + [PRESET_COOL] = { 1, &full_range, &curves[COOL], NULL }, + [PRESET_HEAT] = { 1, &full_range, &curves[HEAT], NULL }, + [PRESET_FIERY] = { 1, &full_range, &curves[FIERY], NULL }, + [PRESET_BLUES] = { 1, &full_range, &curves[BLUES], NULL }, + [PRESET_GREEN] = { 1, &full_range, &curves[GREEN], NULL }, + [PRESET_HELIX] = { 1, &full_range, &curves[HELIX], NULL }, }; typedef struct PseudoColorContext { @@ -237,24 +343,30 @@ static const AVOption pseudocolor_options[] = { { "c3", "set component #3 expression", OFFSET(comp_expr_str[3]), AV_OPT_TYPE_STRING, {.str="val"}, .flags = FLAGS }, { "index", "set component as base", OFFSET(index), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, .flags = FLAGS }, { "i", "set component as base", OFFSET(index), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, .flags = FLAGS }, - { "preset", "set preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=-1},-1, NB_PRESETS-1, .flags = FLAGS, "preset" }, - { "p", "set preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=-1},-1, NB_PRESETS-1, .flags = FLAGS, "preset" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=-1}, .flags = FLAGS, "preset" }, - { "magma", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_MAGMA}, .flags = FLAGS, "preset" }, - { "inferno", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_INFERNO}, .flags = FLAGS, "preset" }, - { "plasma", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_PLASMA}, .flags = FLAGS, "preset" }, - { "viridis", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_VIRIDIS}, .flags = FLAGS, "preset" }, - { "turbo", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_TURBO}, .flags = FLAGS, "preset" }, - { "cividis", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_CIVIDIS}, .flags = FLAGS, "preset" }, - { "range1", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_RANGE1}, .flags = FLAGS, "preset" }, - { "range2", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_RANGE2}, .flags = FLAGS, "preset" }, - { "shadows", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_SHADOWS}, .flags = FLAGS, "preset" }, - { "highlights", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_HIGHLIGHTS},.flags=FLAGS, "preset" }, - { "solar", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_SOLAR}, .flags=FLAGS, "preset" }, - { "nominal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NOMINAL}, .flags=FLAGS, "preset" }, - { "preferred", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_PREFERRED},.flags=FLAGS,"preset" }, - { "total", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_TOTAL}, .flags=FLAGS, "preset" }, - { "spectral", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_SPECTRAL},.flags = FLAGS, "preset" }, + { "preset", "set preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=-1},-1, NB_PRESETS-1, .flags = FLAGS, .unit = "preset" }, + { "p", "set preset", OFFSET(preset), AV_OPT_TYPE_INT, {.i64=-1},-1, NB_PRESETS-1, .flags = FLAGS, .unit = "preset" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=-1}, .flags = FLAGS, .unit = "preset" }, + { "magma", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_MAGMA}, .flags = FLAGS, .unit = "preset" }, + { "inferno", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_INFERNO}, .flags = FLAGS, .unit = "preset" }, + { "plasma", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_PLASMA}, .flags = FLAGS, .unit = "preset" }, + { "viridis", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_VIRIDIS}, .flags = FLAGS, .unit = "preset" }, + { "turbo", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_TURBO}, .flags = FLAGS, .unit = "preset" }, + { "cividis", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_CIVIDIS}, .flags = FLAGS, .unit = "preset" }, + { "range1", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_RANGE1}, .flags = FLAGS, .unit = "preset" }, + { "range2", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_RANGE2}, .flags = FLAGS, .unit = "preset" }, + { "shadows", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_SHADOWS}, .flags = FLAGS, .unit = "preset" }, + { "highlights", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_HIGHLIGHTS},.flags=FLAGS, .unit = "preset" }, + { "solar", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_SOLAR}, .flags=FLAGS, .unit = "preset" }, + { "nominal", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_NOMINAL}, .flags=FLAGS, .unit = "preset" }, + { "preferred", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_PREFERRED},.flags=FLAGS, .unit = "preset" }, + { "total", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_TOTAL}, .flags=FLAGS, .unit = "preset" }, + { "spectral", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_SPECTRAL},.flags = FLAGS, .unit = "preset" }, + { "cool", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_COOL}, .flags = FLAGS, .unit = "preset" }, + { "heat", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_HEAT}, .flags = FLAGS, .unit = "preset" }, + { "fiery", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_FIERY}, .flags = FLAGS, .unit = "preset" }, + { "blues", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_BLUES}, .flags = FLAGS, .unit = "preset" }, + { "green", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_GREEN}, .flags = FLAGS, .unit = "preset" }, + { "helix", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PRESET_HELIX}, .flags = FLAGS, .unit = "preset" }, { "opacity", "set pseudocolor opacity",OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 1, .flags = FLAGS }, { NULL } }; @@ -272,8 +384,8 @@ static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUV420P12, - AV_PIX_FMT_YUV422P12, - AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_YUV422P12, AV_PIX_FMT_YUVA422P12, + AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUV420P14, AV_PIX_FMT_YUV422P14, AV_PIX_FMT_YUV444P14, @@ -283,7 +395,7 @@ static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, - AV_PIX_FMT_GBRP14, + AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRAP14, AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_NONE }; @@ -571,6 +683,19 @@ static void pseudocolor_filter_16_11d(int max, int width, int height, ((0.50000*224.0/255.0) * r1 - (0.45415*224.0/255.0) * g1 - \ (0.04585*224.0/255.0) * b1 + max * 0.5) +#define Wr 0.2126 +#define Wb 0.0722 +#define Wg (1 - Wr - Wb) +#define Umax 0.436 +#define Vmax 0.615 + +#define YUV_BT709_TO_R(y, u, v, max) \ + ((y + v * (1 - Wr) / Vmax) * max) +#define YUV_BT709_TO_G(y, u, v, max) \ + ((y - (u * Wb * (1 - Wb) / (Umax * Wg)) - (v * Wr * (1 - Wr) / (Vmax * Wg))) * max) +#define YUV_BT709_TO_B(y, u, v, max) \ + ((y + u * (1 - Wb) / Umax) * max) + static double poly_eval(const double *const poly, double x, curve_fun fun) { double res = 0.; @@ -696,11 +821,17 @@ static int config_input(AVFilterLink *inlink) const double lf = i / (double)s->max * 256.; double r, g, b; - g = poly_eval(curve.coef[1], lf + curve.offset[1], curve.fun[1]) * s->max; - b = poly_eval(curve.coef[2], lf + curve.offset[2], curve.fun[2]) * s->max; - r = poly_eval(curve.coef[0], lf + curve.offset[0], curve.fun[0]) * s->max; + g = poly_eval(curve.coef[1], lf + curve.offset[1], curve.fun[1]); + b = poly_eval(curve.coef[2], lf + curve.offset[2], curve.fun[2]); + r = poly_eval(curve.coef[0], lf + curve.offset[0], curve.fun[0]); + + if (!curve.yuv || !rgb) { + g *= s->max; + b *= s->max; + r *= s->max; + } - if (!rgb) { + if (!rgb && !curve.yuv) { double y = RGB_TO_Y_BT709(r, g, b); double u = RGB_TO_U_BT709(r, g, b, s->max); double v = RGB_TO_V_BT709(r, g, b, s->max); @@ -708,6 +839,14 @@ static int config_input(AVFilterLink *inlink) r = v; g = y; b = u; + } else if (rgb && curve.yuv) { + double y = g; + double u = b - 0.5; + double v = r - 0.5; + + r = av_clipd(YUV_BT709_TO_R(y, u, v, s->max), 0, s->max); + g = av_clipd(YUV_BT709_TO_G(y, u, v, s->max), 0, s->max); + b = av_clipd(YUV_BT709_TO_B(y, u, v, s->max), 0, s->max); } s->lut[0][i] = g; @@ -762,6 +901,7 @@ static int config_input(AVFilterLink *inlink) case AV_PIX_FMT_YUV444P10: case AV_PIX_FMT_YUVA444P10: case AV_PIX_FMT_YUV444P12: + case AV_PIX_FMT_YUVA444P12: case AV_PIX_FMT_YUV444P14: case AV_PIX_FMT_YUV444P16: case AV_PIX_FMT_YUVA444P16: @@ -772,6 +912,7 @@ static int config_input(AVFilterLink *inlink) case AV_PIX_FMT_GBRP16: case AV_PIX_FMT_GBRAP10: case AV_PIX_FMT_GBRAP12: + case AV_PIX_FMT_GBRAP14: case AV_PIX_FMT_GBRAP16: case AV_PIX_FMT_GRAY9: case AV_PIX_FMT_GRAY10: @@ -785,6 +926,7 @@ static int config_input(AVFilterLink *inlink) case AV_PIX_FMT_YUV422P10: case AV_PIX_FMT_YUVA422P10: case AV_PIX_FMT_YUV422P12: + case AV_PIX_FMT_YUVA422P12: case AV_PIX_FMT_YUV422P14: case AV_PIX_FMT_YUV422P16: case AV_PIX_FMT_YUVA422P16: @@ -900,13 +1042,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static av_cold void uninit(AVFilterContext *ctx) { PseudoColorContext *s = ctx->priv; @@ -927,7 +1062,7 @@ const AVFilter ff_vf_pseudocolor = { .priv_class = &pseudocolor_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = process_command, diff --git a/libavfilter/vf_psnr.c b/libavfilter/vf_psnr.c index 15cde7e8c8a..058a8932f47 100644 --- a/libavfilter/vf_psnr.c +++ b/libavfilter/vf_psnr.c @@ -188,6 +188,13 @@ static int do_psnr(FFFrameSync *fs) td.planeheight[c] = s->planeheight[c]; } + if (master->color_range != ref->color_range) { + av_log(ctx, AV_LOG_WARNING, "master and reference " + "frames use different color ranges (%s != %s)\n", + av_color_range_name(master->color_range), + av_color_range_name(ref->color_range)); + } + ff_filter_execute(ctx, compute_images_mse, &td, NULL, FFMIN(s->planeheight[1], s->nb_threads)); diff --git a/libavfilter/vf_pullup.c b/libavfilter/vf_pullup.c index 054e3f90a91..231f09620ba 100644 --- a/libavfilter/vf_pullup.c +++ b/libavfilter/vf_pullup.c @@ -19,11 +19,11 @@ */ #include "libavutil/avassert.h" +#include "libavutil/emms.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "vf_pullup.h" @@ -43,10 +43,10 @@ static const AVOption pullup_options[] = { { "jt", "set top junk size", OFFSET(junk_top), AV_OPT_TYPE_INT, {.i64=4}, 1, INT_MAX, FLAGS }, { "jb", "set bottom junk size", OFFSET(junk_bottom), AV_OPT_TYPE_INT, {.i64=4}, 1, INT_MAX, FLAGS }, { "sb", "set strict breaks", OFFSET(strict_breaks), AV_OPT_TYPE_BOOL,{.i64=0},-1, 1, FLAGS }, - { "mp", "set metric plane", OFFSET(metric_plane), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "mp" }, - { "y", "luma", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mp" }, - { "u", "chroma blue", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mp" }, - { "v", "chroma red", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "mp" }, + { "mp", "set metric plane", OFFSET(metric_plane), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, .unit = "mp" }, + { "y", "luma", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "mp" }, + { "u", "chroma blue", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "mp" }, + { "v", "chroma red", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "mp" }, { NULL } }; @@ -666,11 +666,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) goto end; } - av_image_copy(b->planes, s->planewidth, - (const uint8_t**)in->data, in->linesize, - inlink->format, inlink->w, inlink->h); + av_image_copy2(b->planes, s->planewidth, + in->data, in->linesize, + inlink->format, inlink->w, inlink->h); - p = in->interlaced_frame ? !in->top_field_first : 0; + p = (in->flags & AV_FRAME_FLAG_INTERLACED) ? + !(in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 0; pullup_submit_field(s, b, p ); pullup_submit_field(s, b, p^1); @@ -713,9 +714,9 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) } av_frame_copy_props(out, in); - av_image_copy(out->data, out->linesize, - (const uint8_t**)f->buffer->planes, s->planewidth, - inlink->format, inlink->w, inlink->h); + av_image_copy2(out->data, out->linesize, + f->buffer->planes, s->planewidth, + inlink->format, inlink->w, inlink->h); ret = ff_filter_frame(outlink, out); pullup_release_frame(f); @@ -748,13 +749,6 @@ static const AVFilterPad pullup_inputs[] = { }, }; -static const AVFilterPad pullup_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_pullup = { .name = "pullup", .description = NULL_IF_CONFIG_SMALL("Pullup from field sequence to frames."), @@ -762,6 +756,6 @@ const AVFilter ff_vf_pullup = { .priv_class = &pullup_class, .uninit = uninit, FILTER_INPUTS(pullup_inputs), - FILTER_OUTPUTS(pullup_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/vf_qp.c b/libavfilter/vf_qp.c index a771b51ae14..37e575447dd 100644 --- a/libavfilter/vf_qp.c +++ b/libavfilter/vf_qp.c @@ -20,13 +20,10 @@ #include #include "libavutil/eval.h" -#include "libavutil/imgutils.h" -#include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "libavutil/video_enc_params.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -184,19 +181,12 @@ static const AVFilterPad qp_inputs[] = { }, }; -static const AVFilterPad qp_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_qp = { .name = "qp", .description = NULL_IF_CONFIG_SMALL("Change video quantization parameters."), .priv_size = sizeof(QPContext), FILTER_INPUTS(qp_inputs), - FILTER_OUTPUTS(qp_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .priv_class = &qp_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_METADATA_ONLY, diff --git a/libavfilter/vf_quirc.c b/libavfilter/vf_quirc.c new file mode 100644 index 00000000000..62eb29b7cef --- /dev/null +++ b/libavfilter/vf_quirc.c @@ -0,0 +1,183 @@ +/* + * 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 QR decoder video filter + * + * Use libquirc library to decode the content of QR codes, and put the decoded + * content to metadata. See: + * https://github.com/dlbeer/quirc + */ + +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "avfilter.h" +#include "formats.h" +#include "video.h" +#include + +typedef struct QuircContext { + const AVClass *class; + + struct quirc *quirc; +} QuircContext; + +static av_cold int init(AVFilterContext *ctx) +{ + QuircContext *quirc = ctx->priv; + + quirc->quirc = quirc_new(); + if (!quirc->quirc) { + return AVERROR(ENOMEM); + } + + return 0; +} + +static av_cold void uninit(AVFilterContext *ctx) +{ + QuircContext *quirc = ctx->priv; + + quirc_destroy(quirc->quirc); +} + +static int config_input(AVFilterLink *inlink) +{ + AVFilterContext *ctx = inlink->dst; + QuircContext *quirc = ctx->priv; + int err; + + err = quirc_resize(quirc->quirc, inlink->w, inlink->h); + if (err == -1) { + return AVERROR(ENOMEM); + } + + return 0; +} + +static int query_formats(AVFilterContext *ctx) +{ + static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_GRAY8, + AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, + AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_NV12, AV_PIX_FMT_NV21, + AV_PIX_FMT_YUVJ411P, AV_PIX_FMT_YUVJ420P, + AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, + AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_NONE + }; + + return ff_set_common_formats_from_list(ctx, pix_fmts); +} + +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *ctx = inlink->dst; + AVFilterLink *outlink = ctx->outputs[0]; + QuircContext *quirc = ctx->priv; + int codes_count; + uint8_t *image; + + /* copy input image to quirc buffer */ + image = quirc_begin(quirc->quirc, NULL, NULL); + av_image_copy_plane(image, inlink->w, + frame->data[0], frame->linesize[0], inlink->w, inlink->h); + + quirc_end(quirc->quirc); + + codes_count = quirc_count(quirc->quirc); + av_log(ctx, AV_LOG_VERBOSE, + "Found count %d codes in image #%ld\n", codes_count, inlink->frame_count_out); + + if (codes_count) { + int i, j; + AVDictionary **metadata = &frame->metadata; + + av_dict_set_int(metadata, "lavfi.quirc.count", codes_count, 0); + + for (i = 0; i < codes_count; i++) { + struct quirc_code code; + struct quirc_data data; + quirc_decode_error_t err; + char metadata_key[64]; + + quirc_extract(quirc->quirc, i, &code); + + err = quirc_decode(&code, &data); + if (err) { + av_log(ctx, AV_LOG_WARNING, + "Failed to decode image: %s\n", quirc_strerror(err)); + continue; + } + + for (j = 0; j < 4; j++) { + struct quirc_point corner = code.corners[j]; + +#define SET_CORNER_METADATA(key_, value_) \ + snprintf(metadata_key, sizeof(metadata_key)-1, \ + "lavfi.quirc.%d.corner.%d." #key_, i, j); \ + av_dict_set_int(metadata, metadata_key, value_, 0) + + SET_CORNER_METADATA(x, corner.x); + SET_CORNER_METADATA(y, corner.y); + } + + snprintf(metadata_key, sizeof(metadata_key)-1, "lavfi.quirc.%d.payload", i); \ + av_dict_set(metadata, metadata_key, data.payload, 0); + + av_log(ctx, AV_LOG_INFO, + "Found QR code at position %d,%d - %d,%d with payload: %s\n", + code.corners[0].x, code.corners[0].y, + code.corners[3].x, code.corners[3].y, data.payload); + } + } + + return ff_filter_frame(outlink, frame); +} + +static const AVClass quirc_class = { + .class_name = "quirc", + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_FILTER +}; + +static const AVFilterPad inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + .config_props = config_input + }, +}; + +const AVFilter ff_vf_quirc = { + .name = "quirc", + .description = NULL_IF_CONFIG_SMALL("Decode and show QR codes content."), + .priv_size = sizeof(QuircContext), + .priv_class = &quirc_class, + .init = init, + .uninit = uninit, + FILTER_INPUTS(inputs), + FILTER_OUTPUTS(ff_video_default_filterpad), + FILTER_QUERY_FUNC(query_formats), + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | + AVFILTER_FLAG_METADATA_ONLY, +}; + diff --git a/libavfilter/vf_random.c b/libavfilter/vf_random.c index cb4b3d3827d..8c7cf8b8066 100644 --- a/libavfilter/vf_random.c +++ b/libavfilter/vf_random.c @@ -22,9 +22,7 @@ #include "libavutil/opt.h" #include "libavutil/random_seed.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" #define MAX_FRAMES 512 diff --git a/libavfilter/vf_readeia608.c b/libavfilter/vf_readeia608.c index d85012564fc..50bf915765c 100644 --- a/libavfilter/vf_readeia608.c +++ b/libavfilter/vf_readeia608.c @@ -29,10 +29,8 @@ #include "libavutil/internal.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" -#include "libavutil/timestamp.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -539,20 +537,13 @@ static const AVFilterPad readeia608_inputs[] = { }, }; -static const AVFilterPad readeia608_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_readeia608 = { .name = "readeia608", .description = NULL_IF_CONFIG_SMALL("Read EIA-608 Closed Caption codes from input video and write them to frame metadata."), .priv_size = sizeof(ReadEIA608Context), .priv_class = &readeia608_class, FILTER_INPUTS(readeia608_inputs), - FILTER_OUTPUTS(readeia608_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .uninit = uninit, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | diff --git a/libavfilter/vf_readvitc.c b/libavfilter/vf_readvitc.c index d0fba234f74..cb63a4d127f 100644 --- a/libavfilter/vf_readvitc.c +++ b/libavfilter/vf_readvitc.c @@ -27,11 +27,10 @@ #include "libavutil/common.h" #include "libavutil/internal.h" #include "libavutil/opt.h" -#include "libavutil/pixdesc.h" #include "libavutil/timecode.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" #define LINE_DATA_SIZE 9 @@ -230,13 +229,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_readvitc = { .name = "readvitc", .description = NULL_IF_CONFIG_SMALL("Read vertical interval timecode and write it to frame metadata."), @@ -244,7 +236,7 @@ const AVFilter ff_vf_readvitc = { .priv_class = &readvitc_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .init = init, }; diff --git a/libavfilter/vf_remap.c b/libavfilter/vf_remap.c index f536580a906..7cc56fa5f0c 100644 --- a/libavfilter/vf_remap.c +++ b/libavfilter/vf_remap.c @@ -66,7 +66,7 @@ typedef struct RemapContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption remap_options[] = { - { "format", "set output format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "format" }, + { "format", "set output format", OFFSET(format), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "format" }, { "color", "", 0, AV_OPT_TYPE_CONST, {.i64=0}, .flags = FLAGS, .unit = "format" }, { "gray", "", 0, AV_OPT_TYPE_CONST, {.i64=1}, .flags = FLAGS, .unit = "format" }, { "fill", "set the color of the unmapped pixels", OFFSET(fill_rgba), AV_OPT_TYPE_COLOR, {.str="black"}, .flags = FLAGS }, diff --git a/libavfilter/vf_remap_opencl.c b/libavfilter/vf_remap_opencl.c index f3f84bde64a..8da48096de9 100644 --- a/libavfilter/vf_remap_opencl.c +++ b/libavfilter/vf_remap_opencl.c @@ -19,12 +19,10 @@ */ #include "libavutil/colorspace.h" -#include "libavutil/imgutils.h" #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "framesync.h" #include "internal.h" #include "opencl.h" @@ -50,9 +48,9 @@ typedef struct RemapOpenCLContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption remap_opencl_options[] = { - { "interp", "set interpolation method", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "interp" }, - { "near", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "interp" }, - { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "interp" }, + { "interp", "set interpolation method", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "interp" }, + { "near", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "interp" }, + { "linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "interp" }, { "fill", "set the color of the unmapped pixels", OFFSET(fill_rgba), AV_OPT_TYPE_COLOR, {.str="black"}, .flags = FLAGS }, { NULL } }; @@ -73,7 +71,7 @@ static int remap_opencl_load(AVFilterContext *avctx, { RemapOpenCLContext *ctx = avctx->priv; cl_int cle; - const char *source = ff_opencl_source_remap; + const char *source = ff_source_remap_cl; const char *kernel = kernels[ctx->interp]; const AVPixFmtDescriptor *main_desc; int err, main_planes; @@ -351,4 +349,5 @@ const AVFilter ff_vf_remap_opencl = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .priv_class = &remap_opencl_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_removegrain.c b/libavfilter/vf_removegrain.c index 0d4b070cd56..6e6e99198c9 100644 --- a/libavfilter/vf_removegrain.c +++ b/libavfilter/vf_removegrain.c @@ -26,7 +26,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/qsort.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "removegrain.h" #include "video.h" @@ -632,19 +631,12 @@ static const AVFilterPad removegrain_inputs[] = { }, }; -static const AVFilterPad removegrain_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_removegrain = { .name = "removegrain", .description = NULL_IF_CONFIG_SMALL("Remove grain."), .priv_size = sizeof(RemoveGrainContext), FILTER_INPUTS(removegrain_inputs), - FILTER_OUTPUTS(removegrain_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &removegrain_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_removelogo.c b/libavfilter/vf_removelogo.c index c323b5eff38..b2930543094 100644 --- a/libavfilter/vf_removelogo.c +++ b/libavfilter/vf_removelogo.c @@ -72,7 +72,6 @@ #include "libavutil/imgutils.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "bbox.h" @@ -555,13 +554,6 @@ static const AVFilterPad removelogo_inputs[] = { }, }; -static const AVFilterPad removelogo_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_removelogo = { .name = "removelogo", .description = NULL_IF_CONFIG_SMALL("Remove a TV logo based on a mask image."), @@ -569,7 +561,7 @@ const AVFilter ff_vf_removelogo = { .init = init, .uninit = uninit, FILTER_INPUTS(removelogo_inputs), - FILTER_OUTPUTS(removelogo_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_YUV420P), .priv_class = &removelogo_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_repeatfields.c b/libavfilter/vf_repeatfields.c index 4d31f3b4f50..bf0edb5440b 100644 --- a/libavfilter/vf_repeatfields.c +++ b/libavfilter/vf_repeatfields.c @@ -22,6 +22,7 @@ #include "avfilter.h" #include "filters.h" #include "internal.h" +#include "video.h" typedef struct RepeatFieldsContext { const AVClass *class; @@ -93,11 +94,12 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) s->frame->pts = AV_NOPTS_VALUE; } - if ((state == 0 && !in->top_field_first) || - (state == 1 && in->top_field_first)) { + if ((state == 0 && !(in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)) || + (state == 1 && (in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST))) { av_log(ctx, AV_LOG_WARNING, "Unexpected field flags: " "state=%d top_field_first=%d repeat_first_field=%d\n", - state, in->top_field_first, in->repeat_pict); + state, !!(in->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST), + in->repeat_pict); state ^= 1; } @@ -181,19 +183,12 @@ static const AVFilterPad repeatfields_inputs[] = { }, }; -static const AVFilterPad repeatfields_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_repeatfields = { .name = "repeatfields", .description = NULL_IF_CONFIG_SMALL("Hard repeat fields based on MPEG repeat field flag."), .priv_size = sizeof(RepeatFieldsContext), .uninit = uninit, FILTER_INPUTS(repeatfields_inputs), - FILTER_OUTPUTS(repeatfields_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts_eq), }; diff --git a/libavfilter/vf_rotate.c b/libavfilter/vf_rotate.c index 96c250a4596..3e65f265527 100644 --- a/libavfilter/vf_rotate.c +++ b/libavfilter/vf_rotate.c @@ -288,7 +288,9 @@ static int config_props(AVFilterLink *outlink) double res; char *expr; - ff_draw_init(&rot->draw, inlink->format, 0); + ret = ff_draw_init2(&rot->draw, inlink->format, inlink->colorspace, inlink->color_range, 0); + if (ret < 0) + return ret; ff_draw_color(&rot->draw, &rot->color, rot->fillcolor); rot->hsub = pixdesc->log2_chroma_w; diff --git a/libavfilter/vf_sab.c b/libavfilter/vf_sab.c index a70d309633c..5e0687c9a20 100644 --- a/libavfilter/vf_sab.c +++ b/libavfilter/vf_sab.c @@ -28,8 +28,8 @@ #include "libswscale/swscale.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" typedef struct FilterParam { float radius; @@ -307,13 +307,6 @@ static const AVFilterPad sab_inputs[] = { }, }; -static const AVFilterPad sab_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_sab = { .name = "sab", .description = NULL_IF_CONFIG_SMALL("Apply shape adaptive blur."), @@ -321,7 +314,7 @@ const AVFilter ff_vf_sab = { .init = init, .uninit = uninit, FILTER_INPUTS(sab_inputs), - FILTER_OUTPUTS(sab_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &sab_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_scale.c b/libavfilter/vf_scale.c index 85047e35248..960ce42b547 100644 --- a/libavfilter/vf_scale.c +++ b/libavfilter/vf_scale.c @@ -56,7 +56,9 @@ static const char *const var_names[] = { "ovsub", "n", "t", +#if FF_API_FRAME_PKT "pos", +#endif "main_w", "main_h", "main_a", @@ -84,7 +86,9 @@ enum var_name { VAR_OVSUB, VAR_N, VAR_T, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_S2R_MAIN_W, VAR_S2R_MAIN_H, VAR_S2R_MAIN_A, @@ -135,11 +139,10 @@ typedef struct ScaleContext { char *flags_str; - char *in_color_matrix; - char *out_color_matrix; + int in_color_matrix; + int out_color_matrix; int in_range; - int in_frame_range; int out_range; int out_h_chr_pos; @@ -205,7 +208,9 @@ static int check_exprs(AVFilterContext *ctx) if (scale->eval_mode == EVAL_MODE_INIT && (vars_w[VAR_N] || vars_h[VAR_N] || vars_w[VAR_T] || vars_h[VAR_T] || +#if FF_API_FRAME_PKT vars_w[VAR_POS] || vars_h[VAR_POS] || +#endif vars_w[VAR_S2R_MAIN_N] || vars_h[VAR_S2R_MAIN_N] || vars_w[VAR_S2R_MAIN_T] || vars_h[VAR_S2R_MAIN_T] || vars_w[VAR_S2R_MAIN_POS] || vars_h[VAR_S2R_MAIN_POS]) ) { @@ -286,6 +291,18 @@ static av_cold int preinit(AVFilterContext *ctx) return 0; } +static const int sws_colorspaces[] = { + AVCOL_SPC_UNSPECIFIED, + AVCOL_SPC_RGB, + AVCOL_SPC_BT709, + AVCOL_SPC_BT470BG, + AVCOL_SPC_SMPTE170M, + AVCOL_SPC_FCC, + AVCOL_SPC_SMPTE240M, + AVCOL_SPC_BT2020_NCL, + -1 +}; + static av_cold int init(AVFilterContext *ctx) { ScaleContext *scale = ctx->priv; @@ -326,6 +343,19 @@ static av_cold int init(AVFilterContext *ctx) if (ret < 0) return ret; + if (scale->in_color_matrix != -1 && + !ff_fmt_is_in(scale->in_color_matrix, sws_colorspaces)) { + av_log(ctx, AV_LOG_ERROR, "Unsupported input color matrix '%s'\n", + av_color_space_name(scale->in_color_matrix)); + return AVERROR(EINVAL); + } + + if (!ff_fmt_is_in(scale->out_color_matrix, sws_colorspaces)) { + av_log(ctx, AV_LOG_ERROR, "Unsupported output color matrix '%s'\n", + av_color_space_name(scale->out_color_matrix)); + return AVERROR(EINVAL); + } + av_log(ctx, AV_LOG_VERBOSE, "w:%s h:%s flags:'%s' interl:%d\n", scale->w_expr, scale->h_expr, (char *)av_x_if_null(scale->flags_str, ""), scale->interlaced); @@ -350,8 +380,6 @@ static av_cold int init(AVFilterContext *ctx) if (!threads) av_opt_set_int(scale->sws_opts, "threads", ff_filter_get_nb_threads(ctx), 0); - scale->in_frame_range = AVCOL_RANGE_UNSPECIFIED; - return 0; } @@ -370,6 +398,7 @@ static av_cold void uninit(AVFilterContext *ctx) static int query_formats(AVFilterContext *ctx) { + ScaleContext *scale = ctx->priv; AVFilterFormats *formats; const AVPixFmtDescriptor *desc; enum AVPixelFormat pix_fmt; @@ -401,31 +430,29 @@ static int query_formats(AVFilterContext *ctx) if ((ret = ff_formats_ref(formats, &ctx->outputs[0]->incfg.formats)) < 0) return ret; - return 0; -} + /* accept all supported inputs, even if user overrides their properties */ + if ((ret = ff_formats_ref(ff_make_format_list(sws_colorspaces), + &ctx->inputs[0]->outcfg.color_spaces)) < 0) + return ret; -static const int *parse_yuv_type(const char *s, enum AVColorSpace colorspace) -{ - if (!s) - s = "bt601"; - - if (s && strstr(s, "bt709")) { - colorspace = AVCOL_SPC_BT709; - } else if (s && strstr(s, "fcc")) { - colorspace = AVCOL_SPC_FCC; - } else if (s && strstr(s, "smpte240m")) { - colorspace = AVCOL_SPC_SMPTE240M; - } else if (s && (strstr(s, "bt601") || strstr(s, "bt470") || strstr(s, "smpte170m"))) { - colorspace = AVCOL_SPC_BT470BG; - } else if (s && strstr(s, "bt2020")) { - colorspace = AVCOL_SPC_BT2020_NCL; - } + if ((ret = ff_formats_ref(ff_all_color_ranges(), + &ctx->inputs[0]->outcfg.color_ranges)) < 0) + return ret; - if (colorspace < 1 || colorspace > 10 || colorspace == 8) { - colorspace = AVCOL_SPC_BT470BG; - } + /* propagate output properties if overridden */ + formats = scale->out_color_matrix != AVCOL_SPC_UNSPECIFIED + ? ff_make_formats_list_singleton(scale->out_color_matrix) + : ff_make_format_list(sws_colorspaces); + if ((ret = ff_formats_ref(formats, &ctx->outputs[0]->incfg.color_spaces)) < 0) + return ret; + + formats = scale->out_range != AVCOL_RANGE_UNSPECIFIED + ? ff_make_formats_list_singleton(scale->out_range) + : ff_all_color_ranges(); + if ((ret = ff_formats_ref(formats, &ctx->outputs[0]->incfg.color_ranges)) < 0) + return ret; - return sws_getCoefficients(colorspace); + return 0; } static int scale_eval_dimensions(AVFilterContext *ctx) @@ -512,8 +539,10 @@ static int config_props(AVFilterLink *outlink) outlink->src->inputs[0]; enum AVPixelFormat outfmt = outlink->format; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); + const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(outfmt); ScaleContext *scale = ctx->priv; uint8_t *flags_val = NULL; + int in_range, in_colorspace; int ret; if ((ret = scale_eval_dimensions(ctx)) < 0) @@ -522,10 +551,13 @@ static int config_props(AVFilterLink *outlink) outlink->w = scale->w; outlink->h = scale->h; - ff_scale_adjust_dimensions(inlink, &outlink->w, &outlink->h, + ret = ff_scale_adjust_dimensions(inlink, &outlink->w, &outlink->h, scale->force_original_aspect_ratio, scale->force_divisible_by); + if (ret < 0) + goto fail; + if (outlink->w > INT_MAX || outlink->h > INT_MAX || (outlink->h * inlink->w) > INT_MAX || @@ -538,6 +570,14 @@ static int config_props(AVFilterLink *outlink) if (outfmt == AV_PIX_FMT_PAL8) outfmt = AV_PIX_FMT_BGR8; scale->output_is_pal = av_pix_fmt_desc_get(outfmt)->flags & AV_PIX_FMT_FLAG_PAL; + in_range = scale->in_range; + if (in_range == AVCOL_RANGE_UNSPECIFIED) + in_range = inlink0->color_range; + + in_colorspace = scale->in_color_matrix; + if (in_colorspace == -1 /* auto */) + in_colorspace = inlink0->colorspace; + if (scale->sws) sws_freeContext(scale->sws); if (scale->isws[0]) @@ -547,8 +587,8 @@ static int config_props(AVFilterLink *outlink) scale->isws[0] = scale->isws[1] = scale->sws = NULL; if (inlink0->w == outlink->w && inlink0->h == outlink->h && - !scale->out_color_matrix && - scale->in_range == scale->out_range && + in_range == outlink->color_range && + in_colorspace == outlink->colorspace && inlink0->format == outlink->format) ; else { @@ -557,6 +597,8 @@ static int config_props(AVFilterLink *outlink) for (i = 0; i < 3; i++) { int in_v_chr_pos = scale->in_v_chr_pos, out_v_chr_pos = scale->out_v_chr_pos; + int in_full, out_full, brightness, contrast, saturation; + const int *inv_table, *table; struct SwsContext *const s = sws_alloc_context(); if (!s) return AVERROR(ENOMEM); @@ -572,24 +614,22 @@ static int config_props(AVFilterLink *outlink) av_opt_set_int(s, "dstw", outlink->w, 0); av_opt_set_int(s, "dsth", outlink->h >> !!i, 0); av_opt_set_int(s, "dst_format", outfmt, 0); - if (scale->in_range != AVCOL_RANGE_UNSPECIFIED) - av_opt_set_int(s, "src_range", - scale->in_range == AVCOL_RANGE_JPEG, 0); - else if (scale->in_frame_range != AVCOL_RANGE_UNSPECIFIED) + if (in_range != AVCOL_RANGE_UNSPECIFIED) av_opt_set_int(s, "src_range", - scale->in_frame_range == AVCOL_RANGE_JPEG, 0); - if (scale->out_range != AVCOL_RANGE_UNSPECIFIED) + in_range == AVCOL_RANGE_JPEG, 0); + if (outlink->color_range != AVCOL_RANGE_UNSPECIFIED) av_opt_set_int(s, "dst_range", - scale->out_range == AVCOL_RANGE_JPEG, 0); + outlink->color_range == AVCOL_RANGE_JPEG, 0); - /* Override YUV420P default settings to have the correct (MPEG-2) chroma positions - * MPEG-2 chroma positions are used by convention - * XXX: support other 4:2:0 pixel formats */ - if (inlink0->format == AV_PIX_FMT_YUV420P && scale->in_v_chr_pos == -513) { + /* Override chroma location default settings to have the correct + * chroma positions. MPEG chroma positions are used by convention. + * Note that this works for both MPEG-1/JPEG and MPEG-2/4 chroma + * locations, since they share a vertical alignment */ + if (desc->log2_chroma_h == 1 && scale->in_v_chr_pos == -513) { in_v_chr_pos = (i == 0) ? 128 : (i == 1) ? 64 : 192; } - if (outlink->format == AV_PIX_FMT_YUV420P && scale->out_v_chr_pos == -513) { + if (outdesc->log2_chroma_h == 1 && scale->out_v_chr_pos == -513) { out_v_chr_pos = (i == 0) ? 128 : (i == 1) ? 64 : 192; } @@ -600,6 +640,24 @@ static int config_props(AVFilterLink *outlink) if ((ret = sws_init_context(s, NULL, NULL)) < 0) return ret; + + sws_getColorspaceDetails(s, (int **)&inv_table, &in_full, + (int **)&table, &out_full, + &brightness, &contrast, &saturation); + + if (scale->in_color_matrix == -1 /* auto */) + inv_table = sws_getCoefficients(inlink0->colorspace); + else if (scale->in_color_matrix != AVCOL_SPC_UNSPECIFIED) + inv_table = sws_getCoefficients(scale->in_color_matrix); + if (outlink->colorspace != AVCOL_SPC_UNSPECIFIED) + table = sws_getCoefficients(outlink->colorspace); + else if (scale->in_color_matrix != AVCOL_SPC_UNSPECIFIED) + table = inv_table; + + sws_setColorspaceDetails(s, inv_table, in_full, + table, out_full, + brightness, contrast, saturation); + if (!scale->interlaced) break; } @@ -613,10 +671,12 @@ static int config_props(AVFilterLink *outlink) if (scale->sws) av_opt_get(scale->sws, "sws_flags", 0, &flags_val); - av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d fmt:%s sar:%d/%d -> w:%d h:%d fmt:%s sar:%d/%d flags:%s\n", + av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d fmt:%s csp:%s range:%s sar:%d/%d -> w:%d h:%d fmt:%s csp:%s range:%s sar:%d/%d flags:%s\n", inlink ->w, inlink ->h, av_get_pix_fmt_name( inlink->format), + av_color_space_name(inlink->colorspace), av_color_range_name(inlink->color_range), inlink->sample_aspect_ratio.num, inlink->sample_aspect_ratio.den, outlink->w, outlink->h, av_get_pix_fmt_name(outlink->format), + av_color_space_name(outlink->colorspace), av_color_range_name(outlink->color_range), outlink->sample_aspect_ratio.num, outlink->sample_aspect_ratio.den, flags_val); av_freep(&flags_val); @@ -636,6 +696,8 @@ static int config_props_ref(AVFilterLink *outlink) outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; outlink->time_base = inlink->time_base; outlink->frame_rate = inlink->frame_rate; + outlink->colorspace = inlink->colorspace; + outlink->color_range = inlink->color_range; return 0; } @@ -709,7 +771,6 @@ static int scale_frame(AVFilterLink *link, AVFrame *in, AVFrame **frame_out) const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(link->format); char buf[32]; int ret; - int in_range; int frame_changed; *frame_out = NULL; @@ -720,14 +781,9 @@ static int scale_frame(AVFilterLink *link, AVFrame *in, AVFrame **frame_out) in->height != link->h || in->format != link->format || in->sample_aspect_ratio.den != link->sample_aspect_ratio.den || - in->sample_aspect_ratio.num != link->sample_aspect_ratio.num; - - if (in->color_range != AVCOL_RANGE_UNSPECIFIED && - scale->in_range == AVCOL_RANGE_UNSPECIFIED && - in->color_range != scale->in_frame_range) { - scale->in_frame_range = in->color_range; - frame_changed = 1; - } + in->sample_aspect_ratio.num != link->sample_aspect_ratio.num || + in->colorspace != link->colorspace || + in->color_range != link->color_range; if (scale->eval_mode == EVAL_MODE_FRAME || frame_changed) { unsigned vars_w[VARS_NB] = { 0 }, vars_h[VARS_NB] = { 0 }; @@ -738,8 +794,16 @@ static int scale_frame(AVFilterLink *link, AVFrame *in, AVFrame **frame_out) if (scale->eval_mode == EVAL_MODE_FRAME && !frame_changed && ctx->filter != &ff_vf_scale2ref && - !(vars_w[VAR_N] || vars_w[VAR_T] || vars_w[VAR_POS]) && - !(vars_h[VAR_N] || vars_h[VAR_T] || vars_h[VAR_POS]) && + !(vars_w[VAR_N] || vars_w[VAR_T] +#if FF_API_FRAME_PKT + || vars_w[VAR_POS] +#endif + ) && + !(vars_h[VAR_N] || vars_h[VAR_T] +#if FF_API_FRAME_PKT + || vars_h[VAR_POS] +#endif + ) && scale->w && scale->h) goto scale; @@ -761,16 +825,26 @@ static int scale_frame(AVFilterLink *link, AVFrame *in, AVFrame **frame_out) if (ctx->filter == &ff_vf_scale2ref) { scale->var_values[VAR_S2R_MAIN_N] = link->frame_count_out; scale->var_values[VAR_S2R_MAIN_T] = TS2T(in->pts, link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS scale->var_values[VAR_S2R_MAIN_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else { scale->var_values[VAR_N] = link->frame_count_out; scale->var_values[VAR_T] = TS2T(in->pts, link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS scale->var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } - link->dst->inputs[0]->format = in->format; - link->dst->inputs[0]->w = in->width; - link->dst->inputs[0]->h = in->height; + link->dst->inputs[0]->format = in->format; + link->dst->inputs[0]->w = in->width; + link->dst->inputs[0]->h = in->height; + link->dst->inputs[0]->colorspace = in->colorspace; + link->dst->inputs[0]->color_range = in->color_range; link->dst->inputs[0]->sample_aspect_ratio.den = in->sample_aspect_ratio.den; link->dst->inputs[0]->sample_aspect_ratio.num = in->sample_aspect_ratio.num; @@ -798,71 +872,19 @@ static int scale_frame(AVFilterLink *link, AVFrame *in, AVFrame **frame_out) av_frame_copy_props(out, in); out->width = outlink->w; out->height = outlink->h; - - // Sanity checks: - // 1. If the output is RGB, set the matrix coefficients to RGB. - // 2. If the output is not RGB and we've got the RGB/XYZ (identity) - // matrix configured, unset the matrix. - // In theory these should be in swscale itself as the AVFrame - // based API gets in, so that not every swscale API user has - // to go through duplicating such sanity checks. - if (av_pix_fmt_desc_get(out->format)->flags & AV_PIX_FMT_FLAG_RGB) - out->colorspace = AVCOL_SPC_RGB; - else if (out->colorspace == AVCOL_SPC_RGB) - out->colorspace = AVCOL_SPC_UNSPECIFIED; + out->color_range = outlink->color_range; + out->colorspace = outlink->colorspace; if (scale->output_is_pal) avpriv_set_systematic_pal2((uint32_t*)out->data[1], outlink->format == AV_PIX_FMT_PAL8 ? AV_PIX_FMT_BGR8 : outlink->format); - in_range = in->color_range; - - if ( scale->in_color_matrix - || scale->out_color_matrix - || scale-> in_range != AVCOL_RANGE_UNSPECIFIED - || in_range != AVCOL_RANGE_UNSPECIFIED - || scale->out_range != AVCOL_RANGE_UNSPECIFIED) { - int in_full, out_full, brightness, contrast, saturation; - const int *inv_table, *table; - - sws_getColorspaceDetails(scale->sws, (int **)&inv_table, &in_full, - (int **)&table, &out_full, - &brightness, &contrast, &saturation); - - if (scale->in_color_matrix) - inv_table = parse_yuv_type(scale->in_color_matrix, in->colorspace); - if (scale->out_color_matrix) - table = parse_yuv_type(scale->out_color_matrix, AVCOL_SPC_UNSPECIFIED); - else if (scale->in_color_matrix) - table = inv_table; - - if (scale-> in_range != AVCOL_RANGE_UNSPECIFIED) - in_full = (scale-> in_range == AVCOL_RANGE_JPEG); - else if (in_range != AVCOL_RANGE_UNSPECIFIED) - in_full = (in_range == AVCOL_RANGE_JPEG); - if (scale->out_range != AVCOL_RANGE_UNSPECIFIED) - out_full = (scale->out_range == AVCOL_RANGE_JPEG); - - sws_setColorspaceDetails(scale->sws, inv_table, in_full, - table, out_full, - brightness, contrast, saturation); - if (scale->isws[0]) - sws_setColorspaceDetails(scale->isws[0], inv_table, in_full, - table, out_full, - brightness, contrast, saturation); - if (scale->isws[1]) - sws_setColorspaceDetails(scale->isws[1], inv_table, in_full, - table, out_full, - brightness, contrast, saturation); - - out->color_range = out_full ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; - } - 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); - if (scale->interlaced>0 || (scale->interlaced<0 && in->interlaced_frame)) { + if (scale->interlaced>0 || (scale->interlaced<0 && + (in->flags & AV_FRAME_FLAG_INTERLACED))) { ret = scale_field(scale, out, in, 0); if (ret >= 0) ret = scale_field(scale, out, in, 1); @@ -900,7 +922,9 @@ static int filter_frame_ref(AVFilterLink *link, AVFrame *in) in->height != link->h || in->format != link->format || in->sample_aspect_ratio.den != link->sample_aspect_ratio.den || - in->sample_aspect_ratio.num != link->sample_aspect_ratio.num; + in->sample_aspect_ratio.num != link->sample_aspect_ratio.num || + in->colorspace != link->colorspace || + in->color_range != link->color_range; if (frame_changed) { link->format = in->format; @@ -908,6 +932,8 @@ static int filter_frame_ref(AVFilterLink *link, AVFrame *in) link->h = in->height; link->sample_aspect_ratio.num = in->sample_aspect_ratio.num; link->sample_aspect_ratio.den = in->sample_aspect_ratio.den; + link->colorspace = in->colorspace; + link->color_range = in->color_range; config_props_ref(outlink); } @@ -915,7 +941,11 @@ static int filter_frame_ref(AVFilterLink *link, AVFrame *in) if (scale->eval_mode == EVAL_MODE_FRAME) { scale->var_values[VAR_N] = link->frame_count_out; scale->var_values[VAR_T] = TS2T(in->pts, link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS scale->var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } return ff_filter_frame(outlink, in); @@ -974,38 +1004,38 @@ static const AVOption scale_options[] = { { "interl", "set interlacing", OFFSET(interlaced), AV_OPT_TYPE_BOOL, {.i64 = 0 }, -1, 1, FLAGS }, { "size", "set video size", OFFSET(size_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, FLAGS }, { "s", "set video size", OFFSET(size_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, FLAGS }, - { "in_color_matrix", "set input YCbCr type", OFFSET(in_color_matrix), AV_OPT_TYPE_STRING, { .str = "auto" }, .flags = FLAGS, "color" }, - { "out_color_matrix", "set output YCbCr type", OFFSET(out_color_matrix), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS, "color"}, - { "auto", NULL, 0, AV_OPT_TYPE_CONST, { .str = "auto" }, 0, 0, FLAGS, "color" }, - { "bt601", NULL, 0, AV_OPT_TYPE_CONST, { .str = "bt601" }, 0, 0, FLAGS, "color" }, - { "bt470", NULL, 0, AV_OPT_TYPE_CONST, { .str = "bt470" }, 0, 0, FLAGS, "color" }, - { "smpte170m", NULL, 0, AV_OPT_TYPE_CONST, { .str = "smpte170m" }, 0, 0, FLAGS, "color" }, - { "bt709", NULL, 0, AV_OPT_TYPE_CONST, { .str = "bt709" }, 0, 0, FLAGS, "color" }, - { "fcc", NULL, 0, AV_OPT_TYPE_CONST, { .str = "fcc" }, 0, 0, FLAGS, "color" }, - { "smpte240m", NULL, 0, AV_OPT_TYPE_CONST, { .str = "smpte240m" }, 0, 0, FLAGS, "color" }, - { "bt2020", NULL, 0, AV_OPT_TYPE_CONST, { .str = "bt2020" }, 0, 0, FLAGS, "color" }, - { "in_range", "set input color range", OFFSET( in_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, 2, FLAGS, "range" }, - { "out_range", "set output color range", OFFSET(out_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, 2, FLAGS, "range" }, - { "auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, 0, FLAGS, "range" }, - { "unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, 0, FLAGS, "range" }, - { "full", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range" }, - { "limited",NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG}, 0, 0, FLAGS, "range" }, - { "jpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range" }, - { "mpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG}, 0, 0, FLAGS, "range" }, - { "tv", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG}, 0, 0, FLAGS, "range" }, - { "pc", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range" }, + { "in_color_matrix", "set input YCbCr type", OFFSET(in_color_matrix), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, AVCOL_SPC_NB-1, .flags = FLAGS, .unit = "color" }, + { "out_color_matrix", "set output YCbCr type", OFFSET(out_color_matrix), AV_OPT_TYPE_INT, { .i64 = AVCOL_SPC_UNSPECIFIED }, 0, AVCOL_SPC_NB-1, .flags = FLAGS, .unit = "color"}, + { "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, 0, 0, FLAGS, .unit = "color" }, + { "bt601", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_BT470BG }, 0, 0, FLAGS, .unit = "color" }, + { "bt470", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_BT470BG }, 0, 0, FLAGS, .unit = "color" }, + { "smpte170m", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_BT470BG }, 0, 0, FLAGS, .unit = "color" }, + { "bt709", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_BT709 }, 0, 0, FLAGS, .unit = "color" }, + { "fcc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_FCC }, 0, 0, FLAGS, .unit = "color" }, + { "smpte240m", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_SMPTE240M }, 0, 0, FLAGS, .unit = "color" }, + { "bt2020", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_SPC_BT2020_NCL }, 0, 0, FLAGS, .unit = "color" }, + { "in_range", "set input color range", OFFSET( in_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, 2, FLAGS, .unit = "range" }, + { "out_range", "set output color range", OFFSET(out_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, 2, FLAGS, .unit = "range" }, + { "auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, 0, FLAGS, .unit = "range" }, + { "unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_UNSPECIFIED }, 0, 0, FLAGS, .unit = "range" }, + { "full", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG}, 0, 0, FLAGS, .unit = "range" }, + { "limited",NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG}, 0, 0, FLAGS, .unit = "range" }, + { "jpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG}, 0, 0, FLAGS, .unit = "range" }, + { "mpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG}, 0, 0, FLAGS, .unit = "range" }, + { "tv", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG}, 0, 0, FLAGS, .unit = "range" }, + { "pc", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG}, 0, 0, FLAGS, .unit = "range" }, { "in_v_chr_pos", "input vertical chroma position in luma grid/256" , OFFSET(in_v_chr_pos), AV_OPT_TYPE_INT, { .i64 = -513}, -513, 512, FLAGS }, { "in_h_chr_pos", "input horizontal chroma position in luma grid/256", OFFSET(in_h_chr_pos), AV_OPT_TYPE_INT, { .i64 = -513}, -513, 512, FLAGS }, { "out_v_chr_pos", "output vertical chroma position in luma grid/256" , OFFSET(out_v_chr_pos), AV_OPT_TYPE_INT, { .i64 = -513}, -513, 512, FLAGS }, { "out_h_chr_pos", "output horizontal chroma position in luma grid/256", OFFSET(out_h_chr_pos), AV_OPT_TYPE_INT, { .i64 = -513}, -513, 512, FLAGS }, - { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, "force_oar" }, - { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" }, - { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" }, - { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" }, + { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, .unit = "force_oar" }, + { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, .unit = "force_oar" }, + { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, .unit = "force_oar" }, + { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, .unit = "force_oar" }, { "force_divisible_by", "enforce that the output resolution is divisible by a defined integer when force_original_aspect_ratio is used", OFFSET(force_divisible_by), AV_OPT_TYPE_INT, { .i64 = 1}, 1, 256, FLAGS }, { "param0", "Scaler param 0", OFFSET(param[0]), AV_OPT_TYPE_DOUBLE, { .dbl = DBL_MAX }, -DBL_MAX, DBL_MAX, FLAGS }, { "param1", "Scaler param 1", OFFSET(param[1]), AV_OPT_TYPE_DOUBLE, { .dbl = DBL_MAX }, -DBL_MAX, DBL_MAX, FLAGS }, - { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" }, + { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, .unit = "eval" }, { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, { "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, { NULL } diff --git a/libavfilter/vf_scale_cuda.c b/libavfilter/vf_scale_cuda.c index 1c99befec8e..5571a52b1e0 100644 --- a/libavfilter/vf_scale_cuda.c +++ b/libavfilter/vf_scale_cuda.c @@ -22,9 +22,7 @@ #include #include -#include -#include "libavutil/avstring.h" #include "libavutil/common.h" #include "libavutil/hwcontext.h" #include "libavutil/hwcontext_cuda_internal.h" @@ -34,7 +32,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "scale_eval.h" #include "video.h" @@ -51,6 +48,8 @@ static const enum AVPixelFormat supported_formats[] = { AV_PIX_FMT_YUV444P16, AV_PIX_FMT_0RGB32, AV_PIX_FMT_0BGR32, + AV_PIX_FMT_RGB32, + AV_PIX_FMT_BGR32, }; #define DIV_UP(a, b) ( ((a) + (b) - 1) / (b) ) @@ -586,18 +585,18 @@ static AVFrame *cudascale_get_video_buffer(AVFilterLink *inlink, int w, int h) 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 }, - { "interp_algo", "Interpolation algorithm used for resizing", OFFSET(interp_algo), AV_OPT_TYPE_INT, { .i64 = INTERP_ALGO_DEFAULT }, 0, INTERP_ALGO_COUNT - 1, FLAGS, "interp_algo" }, - { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, { .i64 = INTERP_ALGO_NEAREST }, 0, 0, FLAGS, "interp_algo" }, - { "bilinear", "bilinear", 0, AV_OPT_TYPE_CONST, { .i64 = INTERP_ALGO_BILINEAR }, 0, 0, FLAGS, "interp_algo" }, - { "bicubic", "bicubic", 0, AV_OPT_TYPE_CONST, { .i64 = INTERP_ALGO_BICUBIC }, 0, 0, FLAGS, "interp_algo" }, - { "lanczos", "lanczos", 0, AV_OPT_TYPE_CONST, { .i64 = INTERP_ALGO_LANCZOS }, 0, 0, FLAGS, "interp_algo" }, + { "interp_algo", "Interpolation algorithm used for resizing", OFFSET(interp_algo), AV_OPT_TYPE_INT, { .i64 = INTERP_ALGO_DEFAULT }, 0, INTERP_ALGO_COUNT - 1, FLAGS, .unit = "interp_algo" }, + { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, { .i64 = INTERP_ALGO_NEAREST }, 0, 0, FLAGS, .unit = "interp_algo" }, + { "bilinear", "bilinear", 0, AV_OPT_TYPE_CONST, { .i64 = INTERP_ALGO_BILINEAR }, 0, 0, FLAGS, .unit = "interp_algo" }, + { "bicubic", "bicubic", 0, AV_OPT_TYPE_CONST, { .i64 = INTERP_ALGO_BICUBIC }, 0, 0, FLAGS, .unit = "interp_algo" }, + { "lanczos", "lanczos", 0, AV_OPT_TYPE_CONST, { .i64 = INTERP_ALGO_LANCZOS }, 0, 0, FLAGS, .unit = "interp_algo" }, { "format", "Output video pixel format", OFFSET(format), AV_OPT_TYPE_PIXEL_FMT, { .i64 = AV_PIX_FMT_NONE }, INT_MIN, INT_MAX, .flags=FLAGS }, { "passthrough", "Do not process frames at all if parameters match", OFFSET(passthrough), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS }, { "param", "Algorithm-Specific parameter", OFFSET(param), AV_OPT_TYPE_FLOAT, { .dbl = SCALE_CUDA_PARAM_DEFAULT }, -FLT_MAX, FLT_MAX, FLAGS }, - { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, FLAGS, "force_oar" }, - { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" }, - { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" }, - { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" }, + { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, FLAGS, .unit = "force_oar" }, + { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, .unit = "force_oar" }, + { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, .unit = "force_oar" }, + { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, .unit = "force_oar" }, { "force_divisible_by", "enforce that the output resolution is divisible by a defined integer when force_original_aspect_ratio is used", OFFSET(force_divisible_by), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 256, FLAGS }, { NULL }, }; diff --git a/libavfilter/vf_scale_cuda.cu b/libavfilter/vf_scale_cuda.cu index c9c6cafdb6e..de06ba94331 100644 --- a/libavfilter/vf_scale_cuda.cu +++ b/libavfilter/vf_scale_cuda.cu @@ -853,9 +853,67 @@ struct Convert_yuv444p16le_yuv444p16le } }; -// bgr0->X - -struct Convert_bgr0_bgr0 +#define DEF_CONVERT_IDENTITY(fmt1, fmt2)\ + \ +struct Convert_##fmt1##_##fmt2 \ +{ \ + static const int in_bit_depth = 8; \ + typedef uchar4 in_T; \ + typedef uchar in_T_uv; \ + typedef uchar4 out_T; \ + typedef uchar out_T_uv; \ + \ + DEF_F(Convert, out_T) \ + { \ + DEFAULT_DST(0) = SUB_F(y, 0); \ + } \ + \ + DEF_F(Convert_uv, out_T_uv) \ + { \ + } \ +}; \ + +#define DEF_CONVERT_REORDER(fmt1, fmt2) \ + \ +struct Convert_##fmt1##_##fmt2 \ +{ \ + static const int in_bit_depth = 8; \ + typedef uchar4 in_T; \ + typedef uchar in_T_uv; \ + typedef uchar4 out_T; \ + typedef uchar out_T_uv; \ + \ + DEF_F(Convert, out_T) \ + { \ + uchar4 res = SUB_F(y, 0); \ + DEFAULT_DST(0) = make_uchar4( \ + res.z, \ + res.y, \ + res.x, \ + res.w \ + ); \ + } \ + \ + DEF_F(Convert_uv, out_T_uv) \ + { \ + } \ +}; \ + +#define DEF_CONVERT_RGB(fmt1, fmt2) \ + \ +DEF_CONVERT_IDENTITY(fmt1, fmt1) \ +DEF_CONVERT_REORDER (fmt1, fmt2) \ +DEF_CONVERT_REORDER (fmt2, fmt1) \ +DEF_CONVERT_IDENTITY(fmt2, fmt2) + +DEF_CONVERT_RGB(rgb0, bgr0) +DEF_CONVERT_RGB(rgba, bgra) +DEF_CONVERT_IDENTITY(rgba, rgb0) +DEF_CONVERT_IDENTITY(bgra, bgr0) +DEF_CONVERT_REORDER(rgba, bgr0) +DEF_CONVERT_REORDER(bgra, rgb0) + +struct Convert_bgr0_bgra { static const int in_bit_depth = 8; typedef uchar4 in_T; @@ -865,7 +923,13 @@ struct Convert_bgr0_bgr0 DEF_F(Convert, out_T) { - DEFAULT_DST(0) = SUB_F(y, 0); + uchar4 res = SUB_F(y, 0); + DEFAULT_DST(0) = make_uchar4( + res.x, + res.y, + res.z, + 1 + ); } DEF_F(Convert_uv, out_T_uv) @@ -873,7 +937,7 @@ struct Convert_bgr0_bgr0 } }; -struct Convert_bgr0_rgb0 +struct Convert_bgr0_rgba { static const int in_bit_depth = 8; typedef uchar4 in_T; @@ -888,7 +952,7 @@ struct Convert_bgr0_rgb0 res.z, res.y, res.x, - res.w + 1 ); } @@ -897,9 +961,7 @@ struct Convert_bgr0_rgb0 } }; -// rgb0->X - -struct Convert_rgb0_bgr0 +struct Convert_rgb0_bgra { static const int in_bit_depth = 8; typedef uchar4 in_T; @@ -914,7 +976,7 @@ struct Convert_rgb0_bgr0 res.z, res.y, res.x, - res.w + 1 ); } @@ -923,7 +985,7 @@ struct Convert_rgb0_bgr0 } }; -struct Convert_rgb0_rgb0 +struct Convert_rgb0_rgba { static const int in_bit_depth = 8; typedef uchar4 in_T; @@ -933,7 +995,13 @@ struct Convert_rgb0_rgb0 DEF_F(Convert, out_T) { - DEFAULT_DST(0) = SUB_F(y, 0); + uchar4 res = SUB_F(y, 0); + DEFAULT_DST(0) = make_uchar4( + res.x, + res.y, + res.z, + 1 + ); } DEF_F(Convert_uv, out_T_uv) @@ -1117,6 +1185,12 @@ extern "C" { NEAREST_KERNEL_RAW(p016le_ ## C) \ NEAREST_KERNEL_RAW(yuv444p16le_ ## C) +#define NEAREST_KERNELS_RGB(C) \ + NEAREST_KERNEL_RAW(rgb0_ ## C) \ + NEAREST_KERNEL_RAW(bgr0_ ## C) \ + NEAREST_KERNEL_RAW(rgba_ ## C) \ + NEAREST_KERNEL_RAW(bgra_ ## C) \ + NEAREST_KERNELS(yuv420p) NEAREST_KERNELS(nv12) NEAREST_KERNELS(yuv444p) @@ -1124,11 +1198,10 @@ NEAREST_KERNELS(p010le) NEAREST_KERNELS(p016le) NEAREST_KERNELS(yuv444p16le) -NEAREST_KERNEL_RAW(bgr0_bgr0) -NEAREST_KERNEL_RAW(rgb0_rgb0) -NEAREST_KERNEL_RAW(bgr0_rgb0) -NEAREST_KERNEL_RAW(rgb0_bgr0) - +NEAREST_KERNELS_RGB(rgb0) +NEAREST_KERNELS_RGB(bgr0) +NEAREST_KERNELS_RGB(rgba) +NEAREST_KERNELS_RGB(bgra) #define BILINEAR_KERNEL(C, S) \ __global__ void Subsample_Bilinear_##C##S( \ @@ -1152,6 +1225,12 @@ NEAREST_KERNEL_RAW(rgb0_bgr0) BILINEAR_KERNEL_RAW(p016le_ ## C) \ BILINEAR_KERNEL_RAW(yuv444p16le_ ## C) +#define BILINEAR_KERNELS_RGB(C) \ + BILINEAR_KERNEL_RAW(rgb0_ ## C) \ + BILINEAR_KERNEL_RAW(bgr0_ ## C) \ + BILINEAR_KERNEL_RAW(rgba_ ## C) \ + BILINEAR_KERNEL_RAW(bgra_ ## C) + BILINEAR_KERNELS(yuv420p) BILINEAR_KERNELS(nv12) BILINEAR_KERNELS(yuv444p) @@ -1159,10 +1238,10 @@ BILINEAR_KERNELS(p010le) BILINEAR_KERNELS(p016le) BILINEAR_KERNELS(yuv444p16le) -BILINEAR_KERNEL_RAW(bgr0_bgr0) -BILINEAR_KERNEL_RAW(rgb0_rgb0) -BILINEAR_KERNEL_RAW(bgr0_rgb0) -BILINEAR_KERNEL_RAW(rgb0_bgr0) +BILINEAR_KERNELS_RGB(rgb0) +BILINEAR_KERNELS_RGB(bgr0) +BILINEAR_KERNELS_RGB(rgba) +BILINEAR_KERNELS_RGB(bgra) #define BICUBIC_KERNEL(C, S) \ __global__ void Subsample_Bicubic_##C##S( \ @@ -1186,6 +1265,12 @@ BILINEAR_KERNEL_RAW(rgb0_bgr0) BICUBIC_KERNEL_RAW(p016le_ ## C) \ BICUBIC_KERNEL_RAW(yuv444p16le_ ## C) +#define BICUBIC_KERNELS_RGB(C) \ + BICUBIC_KERNEL_RAW(rgb0_ ## C) \ + BICUBIC_KERNEL_RAW(bgr0_ ## C) \ + BICUBIC_KERNEL_RAW(rgba_ ## C) \ + BICUBIC_KERNEL_RAW(bgra_ ## C) + BICUBIC_KERNELS(yuv420p) BICUBIC_KERNELS(nv12) BICUBIC_KERNELS(yuv444p) @@ -1193,11 +1278,10 @@ BICUBIC_KERNELS(p010le) BICUBIC_KERNELS(p016le) BICUBIC_KERNELS(yuv444p16le) -BICUBIC_KERNEL_RAW(bgr0_bgr0) -BICUBIC_KERNEL_RAW(rgb0_rgb0) -BICUBIC_KERNEL_RAW(bgr0_rgb0) -BICUBIC_KERNEL_RAW(rgb0_bgr0) - +BICUBIC_KERNELS_RGB(rgb0) +BICUBIC_KERNELS_RGB(bgr0) +BICUBIC_KERNELS_RGB(rgba) +BICUBIC_KERNELS_RGB(bgra) #define LANCZOS_KERNEL(C, S) \ __global__ void Subsample_Lanczos_##C##S( \ @@ -1221,6 +1305,12 @@ BICUBIC_KERNEL_RAW(rgb0_bgr0) LANCZOS_KERNEL_RAW(p016le_ ## C) \ LANCZOS_KERNEL_RAW(yuv444p16le_ ## C) +#define LANCZOS_KERNELS_RGB(C) \ + LANCZOS_KERNEL_RAW(rgb0_ ## C) \ + LANCZOS_KERNEL_RAW(bgr0_ ## C) \ + LANCZOS_KERNEL_RAW(rgba_ ## C) \ + LANCZOS_KERNEL_RAW(bgra_ ## C) + LANCZOS_KERNELS(yuv420p) LANCZOS_KERNELS(nv12) LANCZOS_KERNELS(yuv444p) @@ -1228,9 +1318,8 @@ LANCZOS_KERNELS(p010le) LANCZOS_KERNELS(p016le) LANCZOS_KERNELS(yuv444p16le) -LANCZOS_KERNEL_RAW(bgr0_bgr0) -LANCZOS_KERNEL_RAW(rgb0_rgb0) -LANCZOS_KERNEL_RAW(bgr0_rgb0) -LANCZOS_KERNEL_RAW(rgb0_bgr0) - +LANCZOS_KERNELS_RGB(rgb0) +LANCZOS_KERNELS_RGB(bgr0) +LANCZOS_KERNELS_RGB(rgba) +LANCZOS_KERNELS_RGB(bgra) } diff --git a/libavfilter/vf_scale_npp.c b/libavfilter/vf_scale_npp.c index 6ce82e53027..9e55fe69951 100644 --- a/libavfilter/vf_scale_npp.c +++ b/libavfilter/vf_scale_npp.c @@ -84,7 +84,9 @@ static const char *const var_names[] = { "dar", "n", "t", +#if FF_API_FRAME_PKT "pos", +#endif "main_w", "main_h", "main_a", @@ -92,7 +94,9 @@ static const char *const var_names[] = { "main_dar", "mdar", "main_n", "main_t", +#if FF_API_FRAME_PKT "main_pos", +#endif NULL }; @@ -106,7 +110,9 @@ enum var_name { VAR_DAR, VAR_N, VAR_T, +#if FF_API_FRAME_PKT VAR_POS, +#endif VAR_S2R_MAIN_W, VAR_S2R_MAIN_H, VAR_S2R_MAIN_A, @@ -114,7 +120,9 @@ enum var_name { VAR_S2R_MAIN_DAR, VAR_S2R_MDAR, VAR_S2R_MAIN_N, VAR_S2R_MAIN_T, +#if FF_API_FRAME_PKT VAR_S2R_MAIN_POS, +#endif VARS_NB }; @@ -204,8 +212,11 @@ static int check_exprs(AVFilterContext* ctx) vars_w[VAR_S2R_MAIN_DAR] || vars_h[VAR_S2R_MAIN_DAR] || vars_w[VAR_S2R_MDAR] || vars_h[VAR_S2R_MDAR] || vars_w[VAR_S2R_MAIN_N] || vars_h[VAR_S2R_MAIN_N] || - vars_w[VAR_S2R_MAIN_T] || vars_h[VAR_S2R_MAIN_T] || - vars_w[VAR_S2R_MAIN_POS] || vars_h[VAR_S2R_MAIN_POS])) { + vars_w[VAR_S2R_MAIN_T] || vars_h[VAR_S2R_MAIN_T] +#if FF_API_FRAME_PKT + || vars_w[VAR_S2R_MAIN_POS] || vars_h[VAR_S2R_MAIN_POS] +#endif + )) { av_log(ctx, AV_LOG_ERROR, "Expressions with scale2ref_npp variables are not valid in scale_npp filter.\n"); return AVERROR(EINVAL); } @@ -213,11 +224,16 @@ static int check_exprs(AVFilterContext* ctx) if (scale->eval_mode == EVAL_MODE_INIT && (vars_w[VAR_N] || vars_h[VAR_N] || vars_w[VAR_T] || vars_h[VAR_T] || +#if FF_API_FRAME_PKT vars_w[VAR_POS] || vars_h[VAR_POS] || +#endif vars_w[VAR_S2R_MAIN_N] || vars_h[VAR_S2R_MAIN_N] || - vars_w[VAR_S2R_MAIN_T] || vars_h[VAR_S2R_MAIN_T] || - vars_w[VAR_S2R_MAIN_POS] || vars_h[VAR_S2R_MAIN_POS]) ) { - av_log(ctx, AV_LOG_ERROR, "Expressions with frame variables 'n', 't', 'pos' are not valid in init eval_mode.\n"); + vars_w[VAR_S2R_MAIN_T] || vars_h[VAR_S2R_MAIN_T] +#if FF_API_FRAME_PKT + || vars_w[VAR_S2R_MAIN_POS] || vars_h[VAR_S2R_MAIN_POS] +#endif + ) ) { + av_log(ctx, AV_LOG_ERROR, "Expressions with frame variables 'n', 't', are not valid in init eval_mode.\n"); return AVERROR(EINVAL); } @@ -790,9 +806,16 @@ static int nppscale_scale(AVFilterLink *link, AVFrame *out, AVFrame *in) av_expr_count_vars(s->h_pexpr, vars_h, VARS_NB); if (s->eval_mode == EVAL_MODE_FRAME && !frame_changed && ctx->filter != &ff_vf_scale2ref_npp && - !(vars_w[VAR_N] || vars_w[VAR_T] || vars_w[VAR_POS]) && - !(vars_h[VAR_N] || vars_h[VAR_T] || vars_h[VAR_POS]) && - s->w && s->h) + !(vars_w[VAR_N] || vars_w[VAR_T] +#if FF_API_FRAME_PKT + || vars_w[VAR_POS] +#endif + ) && + !(vars_h[VAR_N] || vars_h[VAR_T] +#if FF_API_FRAME_PKT + || vars_h[VAR_POS] +#endif + ) && s->w && s->h) goto scale; if (s->eval_mode == EVAL_MODE_INIT) { @@ -813,11 +836,19 @@ static int nppscale_scale(AVFilterLink *link, AVFrame *out, AVFrame *in) if (ctx->filter == &ff_vf_scale2ref_npp) { s->var_values[VAR_S2R_MAIN_N] = link->frame_count_out; s->var_values[VAR_S2R_MAIN_T] = TS2T(in->pts, link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS s->var_values[VAR_S2R_MAIN_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } else { s->var_values[VAR_N] = link->frame_count_out; s->var_values[VAR_T] = TS2T(in->pts, link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS s->var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } link->format = in->format; @@ -932,7 +963,11 @@ static int nppscale_filter_frame_ref(AVFilterLink *link, AVFrame *in) if (scale->eval_mode == EVAL_MODE_FRAME) { scale->var_values[VAR_N] = link->frame_count_out; scale->var_values[VAR_T] = TS2T(in->pts, link->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS scale->var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif } return ff_filter_frame(outlink, in); @@ -956,23 +991,23 @@ static const AVOption options[] = { { "format", "Output pixel format", OFFSET(format_str), AV_OPT_TYPE_STRING, { .str = "same" }, .flags = FLAGS }, { "s", "Output video size", OFFSET(size_str), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, - { "interp_algo", "Interpolation algorithm used for resizing", OFFSET(interp_algo), AV_OPT_TYPE_INT, { .i64 = NPPI_INTER_CUBIC }, 0, INT_MAX, FLAGS, "interp_algo" }, - { "nn", "nearest neighbour", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_NN }, 0, 0, FLAGS, "interp_algo" }, - { "linear", "linear", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_LINEAR }, 0, 0, FLAGS, "interp_algo" }, - { "cubic", "cubic", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_CUBIC }, 0, 0, FLAGS, "interp_algo" }, - { "cubic2p_bspline", "2-parameter cubic (B=1, C=0)", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_CUBIC2P_BSPLINE }, 0, 0, FLAGS, "interp_algo" }, - { "cubic2p_catmullrom", "2-parameter cubic (B=0, C=1/2)", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_CUBIC2P_CATMULLROM }, 0, 0, FLAGS, "interp_algo" }, - { "cubic2p_b05c03", "2-parameter cubic (B=1/2, C=3/10)", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_CUBIC2P_B05C03 }, 0, 0, FLAGS, "interp_algo" }, - { "super", "supersampling", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_SUPER }, 0, 0, FLAGS, "interp_algo" }, - { "lanczos", "Lanczos", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_LANCZOS }, 0, 0, FLAGS, "interp_algo" }, - { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, FLAGS, "force_oar" }, - { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" }, - { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" }, - { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" }, + { "interp_algo", "Interpolation algorithm used for resizing", OFFSET(interp_algo), AV_OPT_TYPE_INT, { .i64 = NPPI_INTER_CUBIC }, 0, INT_MAX, FLAGS, .unit = "interp_algo" }, + { "nn", "nearest neighbour", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_NN }, 0, 0, FLAGS, .unit = "interp_algo" }, + { "linear", "linear", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_LINEAR }, 0, 0, FLAGS, .unit = "interp_algo" }, + { "cubic", "cubic", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_CUBIC }, 0, 0, FLAGS, .unit = "interp_algo" }, + { "cubic2p_bspline", "2-parameter cubic (B=1, C=0)", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_CUBIC2P_BSPLINE }, 0, 0, FLAGS, .unit = "interp_algo" }, + { "cubic2p_catmullrom", "2-parameter cubic (B=0, C=1/2)", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_CUBIC2P_CATMULLROM }, 0, 0, FLAGS, .unit = "interp_algo" }, + { "cubic2p_b05c03", "2-parameter cubic (B=1/2, C=3/10)", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_CUBIC2P_B05C03 }, 0, 0, FLAGS, .unit = "interp_algo" }, + { "super", "supersampling", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_SUPER }, 0, 0, FLAGS, .unit = "interp_algo" }, + { "lanczos", "Lanczos", 0, AV_OPT_TYPE_CONST, { .i64 = NPPI_INTER_LANCZOS }, 0, 0, FLAGS, .unit = "interp_algo" }, + { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, FLAGS, .unit = "force_oar" }, + { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, .unit = "force_oar" }, + { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, .unit = "force_oar" }, + { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, .unit = "force_oar" }, { "force_divisible_by", "enforce that the output resolution is divisible by a defined integer when force_original_aspect_ratio is used", OFFSET(force_divisible_by), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 256, FLAGS }, - { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, { .i64 = EVAL_MODE_INIT }, 0, EVAL_MODE_NB-1, FLAGS, "eval" }, - { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, { .i64 = EVAL_MODE_INIT }, 0, 0, FLAGS, "eval" }, - { "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, { .i64 = EVAL_MODE_FRAME }, 0, 0, FLAGS, "eval" }, + { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, { .i64 = EVAL_MODE_INIT }, 0, EVAL_MODE_NB-1, FLAGS, .unit = "eval" }, + { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, { .i64 = EVAL_MODE_INIT }, 0, 0, FLAGS, .unit = "eval" }, + { "frame", "eval expressions during initialization and per-frame", 0, AV_OPT_TYPE_CONST, { .i64 = EVAL_MODE_FRAME }, 0, 0, FLAGS, .unit = "eval" }, { NULL }, }; diff --git a/libavfilter/vf_scale_vaapi.c b/libavfilter/vf_scale_vaapi.c index a371077ee02..5f20b8a43c6 100644 --- a/libavfilter/vf_scale_vaapi.c +++ b/libavfilter/vf_scale_vaapi.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "scale_eval.h" #include "video.h" @@ -85,6 +84,16 @@ static int scale_vaapi_config_output(AVFilterLink *outlink) ff_scale_adjust_dimensions(inlink, &vpp_ctx->output_width, &vpp_ctx->output_height, ctx->force_original_aspect_ratio, ctx->force_divisible_by); + if (inlink->w == vpp_ctx->output_width && inlink->h == vpp_ctx->output_height && + (vpp_ctx->input_frames->sw_format == vpp_ctx->output_format || + vpp_ctx->output_format == AV_PIX_FMT_NONE) && + ctx->colour_primaries == AVCOL_PRI_UNSPECIFIED && + ctx->colour_transfer == AVCOL_TRC_UNSPECIFIED && + ctx->colour_matrix == AVCOL_SPC_UNSPECIFIED && + ctx->colour_range == AVCOL_RANGE_UNSPECIFIED && + ctx->chroma_location == AVCHROMA_LOC_UNSPECIFIED) + vpp_ctx->passthrough = 1; + err = ff_vaapi_vpp_config_output(outlink); if (err < 0) return err; @@ -111,6 +120,9 @@ static int scale_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame) av_get_pix_fmt_name(input_frame->format), input_frame->width, input_frame->height, input_frame->pts); + if (vpp_ctx->passthrough) + return ff_filter_frame(outlink, input_frame); + if (vpp_ctx->va_context == VA_INVALID_ID) return AVERROR(EINVAL); @@ -213,34 +225,34 @@ static const AVOption scale_vaapi_options[] = { OFFSET(output_format_string), AV_OPT_TYPE_STRING, .flags = FLAGS }, { "mode", "Scaling mode", OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = VA_FILTER_SCALING_HQ }, - 0, VA_FILTER_SCALING_NL_ANAMORPHIC, FLAGS, "mode" }, + 0, VA_FILTER_SCALING_NL_ANAMORPHIC, FLAGS, .unit = "mode" }, { "default", "Use the default (depend on the driver) scaling algorithm", - 0, AV_OPT_TYPE_CONST, { .i64 = VA_FILTER_SCALING_DEFAULT }, 0, 0, FLAGS, "mode" }, + 0, AV_OPT_TYPE_CONST, { .i64 = VA_FILTER_SCALING_DEFAULT }, 0, 0, FLAGS, .unit = "mode" }, { "fast", "Use fast scaling algorithm", - 0, AV_OPT_TYPE_CONST, { .i64 = VA_FILTER_SCALING_FAST }, 0, 0, FLAGS, "mode" }, + 0, AV_OPT_TYPE_CONST, { .i64 = VA_FILTER_SCALING_FAST }, 0, 0, FLAGS, .unit = "mode" }, { "hq", "Use high quality scaling algorithm", - 0, AV_OPT_TYPE_CONST, { .i64 = VA_FILTER_SCALING_HQ }, 0, 0, FLAGS, "mode" }, + 0, AV_OPT_TYPE_CONST, { .i64 = VA_FILTER_SCALING_HQ }, 0, 0, FLAGS, .unit = "mode" }, { "nl_anamorphic", "Use nolinear anamorphic scaling algorithm", - 0, AV_OPT_TYPE_CONST, { .i64 = VA_FILTER_SCALING_NL_ANAMORPHIC }, 0, 0, FLAGS, "mode" }, + 0, AV_OPT_TYPE_CONST, { .i64 = VA_FILTER_SCALING_NL_ANAMORPHIC }, 0, 0, FLAGS, .unit = "mode" }, // These colour properties match the ones of the same name in vf_scale. { "out_color_matrix", "Output colour matrix coefficient set", OFFSET(colour_matrix_string), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, { "out_range", "Output colour range", OFFSET(colour_range), AV_OPT_TYPE_INT, { .i64 = AVCOL_RANGE_UNSPECIFIED }, - AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_JPEG, FLAGS, "range" }, + AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_JPEG, FLAGS, .unit = "range" }, { "full", "Full range", - 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" }, { "limited", "Limited range", - 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" }, { "jpeg", "Full range", - 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" }, { "mpeg", "Limited range", - 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" }, { "tv", "Limited range", - 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" }, { "pc", "Full range", - 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" }, // These colour properties are new here. { "out_color_primaries", "Output colour primaries", OFFSET(colour_primaries_string), AV_OPT_TYPE_STRING, @@ -251,10 +263,10 @@ static const AVOption scale_vaapi_options[] = { { "out_chroma_location", "Output chroma sample location", OFFSET(chroma_location_string), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, - { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, "force_oar" }, - { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, "force_oar" }, - { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, "force_oar" }, - { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, "force_oar" }, + { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, .unit = "force_oar" }, + { "disable", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 0, FLAGS, .unit = "force_oar" }, + { "decrease", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 0, FLAGS, .unit = "force_oar" }, + { "increase", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 0, FLAGS, .unit = "force_oar" }, { "force_divisible_by", "enforce that the output resolution is divisible by a defined integer when force_original_aspect_ratio is used", OFFSET(force_divisible_by), AV_OPT_TYPE_INT, { .i64 = 1}, 1, 256, FLAGS }, { NULL }, diff --git a/libavfilter/vf_scale_vt.c b/libavfilter/vf_scale_vt.c new file mode 100644 index 00000000000..af4a8b32c6a --- /dev/null +++ b/libavfilter/vf_scale_vt.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2023 Zhao Zhili + * + * 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 + +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_videotoolbox.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "internal.h" +#include "scale_eval.h" +#include "video.h" + +typedef struct ScaleVtContext { + AVClass *class; + + VTPixelTransferSessionRef transfer; + int output_width; + int output_height; + char *w_expr; + char *h_expr; + + enum AVColorPrimaries colour_primaries; + enum AVColorTransferCharacteristic colour_transfer; + enum AVColorSpace colour_matrix; + char *colour_primaries_string; + char *colour_transfer_string; + char *colour_matrix_string; +} ScaleVtContext; + +static av_cold int scale_vt_init(AVFilterContext *avctx) +{ + ScaleVtContext *s = avctx->priv; + int ret; + CFStringRef value; + + ret = VTPixelTransferSessionCreate(kCFAllocatorDefault, &s->transfer); + if (ret != noErr) { + av_log(avctx, AV_LOG_ERROR, "transfer session create failed, %d\n", ret); + return AVERROR_EXTERNAL; + } + +#define STRING_OPTION(var_name, func_name, default_value) \ + do { \ + if (s->var_name##_string) { \ + int var = av_##func_name##_from_name(s->var_name##_string); \ + if (var < 0) { \ + av_log(avctx, AV_LOG_ERROR, "Invalid %s.\n", #var_name); \ + return AVERROR(EINVAL); \ + } \ + s->var_name = var; \ + } else { \ + s->var_name = default_value; \ + } \ + } while (0) + + STRING_OPTION(colour_primaries, color_primaries, AVCOL_PRI_UNSPECIFIED); + STRING_OPTION(colour_transfer, color_transfer, AVCOL_TRC_UNSPECIFIED); + STRING_OPTION(colour_matrix, color_space, AVCOL_SPC_UNSPECIFIED); + + if (s->colour_primaries != AVCOL_PRI_UNSPECIFIED) { + value = av_map_videotoolbox_color_primaries_from_av(s->colour_primaries); + if (!value) { + av_log(avctx, AV_LOG_ERROR, + "Doesn't support converting to colour primaries %s\n", + s->colour_primaries_string); + return AVERROR(ENOTSUP); + } + VTSessionSetProperty(s->transfer, kVTPixelTransferPropertyKey_DestinationColorPrimaries, value); + } + + if (s->colour_transfer != AVCOL_TRC_UNSPECIFIED) { + value = av_map_videotoolbox_color_trc_from_av(s->colour_transfer); + if (!value) { + av_log(avctx, AV_LOG_ERROR, + "Doesn't support converting to trc %s\n", + s->colour_transfer_string); + return AVERROR(ENOTSUP); + } + VTSessionSetProperty(s->transfer, kVTPixelTransferPropertyKey_DestinationTransferFunction, value); + } + + if (s->colour_matrix != AVCOL_SPC_UNSPECIFIED) { + value = av_map_videotoolbox_color_matrix_from_av(s->colour_matrix); + if (!value) { + av_log(avctx, AV_LOG_ERROR, + "Doesn't support converting to colorspace %s\n", + s->colour_matrix_string); + return AVERROR(ENOTSUP); + } + VTSessionSetProperty(s->transfer, kVTPixelTransferPropertyKey_DestinationYCbCrMatrix, value); + } + + return 0; +} + +static av_cold void scale_vt_uninit(AVFilterContext *avctx) +{ + ScaleVtContext *s = avctx->priv; + + if (s->transfer) { + VTPixelTransferSessionInvalidate(s->transfer); + CFRelease(s->transfer); + s->transfer = NULL; + } +} + +static int scale_vt_filter_frame(AVFilterLink *link, AVFrame *in) +{ + int ret; + AVFilterContext *ctx = link->dst; + ScaleVtContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + CVPixelBufferRef src; + CVPixelBufferRef dst; + + AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + ret = AVERROR(ENOMEM); + goto fail; + } + + ret = av_frame_copy_props(out, in); + if (ret < 0) + goto fail; + + 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); + if (s->colour_primaries != AVCOL_PRI_UNSPECIFIED) + out->color_primaries = s->colour_primaries; + if (s->colour_transfer != AVCOL_TRC_UNSPECIFIED) + out->color_trc = s->colour_transfer; + if (s->colour_matrix != AVCOL_SPC_UNSPECIFIED) + out->colorspace = s->colour_matrix; + + src = (CVPixelBufferRef)in->data[3]; + dst = (CVPixelBufferRef)out->data[3]; + ret = VTPixelTransferSessionTransferImage(s->transfer, src, dst); + if (ret != noErr) { + av_log(ctx, AV_LOG_ERROR, "transfer image failed, %d\n", ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + av_frame_free(&in); + + return ff_filter_frame(outlink, out); + +fail: + av_frame_free(&in); + av_frame_free(&out); + return ret; +} + +static int scale_vt_config_output(AVFilterLink *outlink) +{ + int err; + AVFilterContext *avctx = outlink->src; + ScaleVtContext *s = avctx->priv; + AVFilterLink *inlink = outlink->src->inputs[0]; + AVHWFramesContext *hw_frame_ctx_in; + AVHWFramesContext *hw_frame_ctx_out; + + err = ff_scale_eval_dimensions(s, s->w_expr, s->h_expr, inlink, outlink, + &s->output_width, + &s->output_height); + if (err < 0) + return err; + + outlink->w = s->output_width; + outlink->h = s->output_height; + + if (inlink->sample_aspect_ratio.num) { + AVRational r = {outlink->h * inlink->w, outlink->w * inlink->h}; + outlink->sample_aspect_ratio = av_mul_q(r, inlink->sample_aspect_ratio); + } else { + outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; + } + + hw_frame_ctx_in = (AVHWFramesContext *)inlink->hw_frames_ctx->data; + + av_buffer_unref(&outlink->hw_frames_ctx); + outlink->hw_frames_ctx = av_hwframe_ctx_alloc(hw_frame_ctx_in->device_ref); + hw_frame_ctx_out = (AVHWFramesContext *)outlink->hw_frames_ctx->data; + hw_frame_ctx_out->format = AV_PIX_FMT_VIDEOTOOLBOX; + hw_frame_ctx_out->sw_format = hw_frame_ctx_in->sw_format; + hw_frame_ctx_out->width = outlink->w; + hw_frame_ctx_out->height = outlink->h; + + err = ff_filter_init_hw_frames(avctx, outlink, 1); + if (err < 0) + return err; + + err = av_hwframe_ctx_init(outlink->hw_frames_ctx); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, + "Failed to init videotoolbox frame context, %s\n", + av_err2str(err)); + return err; + } + + return 0; +} + +#define OFFSET(x) offsetof(ScaleVtContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) +static const AVOption scale_vt_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 }, + { "color_matrix", "Output colour matrix coefficient set", + OFFSET(colour_matrix_string), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + { "color_primaries", "Output colour primaries", + OFFSET(colour_primaries_string), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + { "color_transfer", "Output colour transfer characteristics", + OFFSET(colour_transfer_string), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(scale_vt); + +static const AVFilterPad scale_vt_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = &scale_vt_filter_frame, + }, +}; + +static const AVFilterPad scale_vt_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &scale_vt_config_output, + }, +}; + +const AVFilter ff_vf_scale_vt = { + .name = "scale_vt", + .description = NULL_IF_CONFIG_SMALL("Scale Videotoolbox frames"), + .priv_size = sizeof(ScaleVtContext), + .init = scale_vt_init, + .uninit = scale_vt_uninit, + FILTER_INPUTS(scale_vt_inputs), + FILTER_OUTPUTS(scale_vt_outputs), + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VIDEOTOOLBOX), + .priv_class = &scale_vt_class, + .flags = AVFILTER_FLAG_HWDEVICE, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_scale_vulkan.c b/libavfilter/vf_scale_vulkan.c index c1404208962..7210509de32 100644 --- a/libavfilter/vf_scale_vulkan.c +++ b/libavfilter/vf_scale_vulkan.c @@ -1,4 +1,6 @@ /* + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -19,11 +21,11 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "scale_eval.h" #include "internal.h" #include "colorspace.h" - -#define CGROUPS (int [3]){ 32, 32, 1 } +#include "video.h" enum ScalerFunc { F_BILINEAR = 0, @@ -35,15 +37,17 @@ enum ScalerFunc { typedef struct ScaleVulkanContext { FFVulkanContext vkctx; + int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl; - FFVkBuffer params_buf; + FFVkSPIRVShader shd; + VkSampler sampler; - /* Shader updators, must be in the main filter struct */ - VkDescriptorImageInfo input_images[3]; - VkDescriptorImageInfo output_images[3]; - VkDescriptorBufferInfo params_desc; + /* Push constants / options */ + struct { + float yuv_matrix[4][4]; + } opts; char *out_format_string; char *w_expr; @@ -51,8 +55,6 @@ typedef struct ScaleVulkanContext { enum ScalerFunc scaler; enum AVColorRange out_range; - - int initialized; } ScaleVulkanContext; static const char scale_bilinear[] = { @@ -110,10 +112,15 @@ static const char write_444[] = { static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { int err; - FFVkSampler *sampler; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; VkFilter sampler_mode; ScaleVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; int crop_x = in->crop_left; int crop_y = in->crop_top; @@ -121,8 +128,6 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) int crop_h = in->height - (in->crop_top + in->crop_bottom); int in_planes = av_pix_fmt_count_planes(s->vkctx.input_format); - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - switch (s->scaler) { case F_NEAREST: sampler_mode = VK_FILTER_NEAREST; @@ -132,264 +137,132 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) break; }; - /* Create a sampler */ - sampler = ff_vk_init_sampler(vkctx, 0, sampler_mode); - if (!sampler) + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); return AVERROR_EXTERNAL; + } - s->pl = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl) - return AVERROR(ENOMEM); - - { /* Create the shader */ - FFVulkanDescriptorSetBinding desc_i[2] = { - { - .name = "input_img", - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dimensions = 2, - .elems = in_planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->input_images, - .sampler = sampler, - }, - { - .name = "output_img", - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), - .mem_quali = "writeonly", - .dimensions = 2, - .elems = av_pix_fmt_count_planes(s->vkctx.output_format), - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->output_images, - }, - }; - - FFVulkanDescriptorSetBinding desc_b = { - .name = "params", - .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .mem_quali = "readonly", - .mem_layout = "std430", - .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = &s->params_desc, - .buf_content = "mat4 yuv_matrix;", - }; - - FFVkSPIRVShader *shd = ff_vk_init_shader(s->pl, "scale_compute", - VK_SHADER_STAGE_COMPUTE_BIT); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, CGROUPS); - - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, desc_i, FF_ARRAY_ELEMS(desc_i), 0)); /* set 0 */ - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, &desc_b, 1, 0)); /* set 1 */ - - GLSLD( scale_bilinear ); - - if (s->vkctx.output_format != s->vkctx.input_format) { - GLSLD( rgb2yuv ); - } + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 0, sampler_mode)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "scale_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, mat4 yuv_matrix; ); + GLSLC(0, }; ); + GLSLC(0, ); + + ff_vk_add_push_constant(&s->pl, 0, sizeof(s->opts), + VK_SHADER_STAGE_COMPUTE_BIT); + + desc = (FFVulkanDescriptorSetBinding []) { + { + .name = "input_img", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = in_planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "output_img", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = av_pix_fmt_count_planes(s->vkctx.output_format), + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; - switch (s->vkctx.output_format) { - case AV_PIX_FMT_NV12: GLSLD(write_nv12); break; - case AV_PIX_FMT_YUV420P: GLSLD( write_420); break; - case AV_PIX_FMT_YUV444P: GLSLD( write_444); break; - default: break; - } + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 2, 0, 0)); - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - GLSLF(1, vec2 in_d = vec2(%i, %i); ,in->width, in->height); - GLSLF(1, vec2 c_r = vec2(%i, %i) / in_d; ,crop_w, crop_h); - GLSLF(1, vec2 c_o = vec2(%i, %i) / in_d; ,crop_x,crop_y); - GLSLC(0, ); - - if (s->vkctx.output_format == s->vkctx.input_format) { - for (int i = 0; i < desc_i[1].elems; i++) { - GLSLF(1, size = imageSize(output_img[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - switch (s->scaler) { - case F_NEAREST: - case F_BILINEAR: - GLSLF(2, vec4 res = scale_bilinear(%i, pos, c_r, c_o); ,i); - GLSLF(2, imageStore(output_img[%i], pos, res); ,i); - break; - }; - GLSLC(1, } ); - } - } else { - GLSLC(1, vec4 res = scale_bilinear(0, pos, c_r, c_o); ); - GLSLF(1, res = rgb2yuv(res, %i); ,s->out_range == AVCOL_RANGE_JPEG); - switch (s->vkctx.output_format) { - case AV_PIX_FMT_NV12: GLSLC(1, write_nv12(res, pos); ); break; - case AV_PIX_FMT_YUV420P: GLSLC(1, write_420(res, pos); ); break; - case AV_PIX_FMT_YUV444P: GLSLC(1, write_444(res, pos); ); break; - default: return AVERROR(EINVAL); - } - } + GLSLD( scale_bilinear ); + + if (s->vkctx.output_format != s->vkctx.input_format) { + GLSLD( rgb2yuv ); + } - GLSLC(0, } ); + switch (s->vkctx.output_format) { + case AV_PIX_FMT_NV12: GLSLD(write_nv12); break; + case AV_PIX_FMT_YUV420P: GLSLD( write_420); break; + case AV_PIX_FMT_YUV444P: GLSLD( write_444); break; + default: break; + } - RET(ff_vk_compile_shader(vkctx, shd, "main")); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 size; ); + GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + GLSLF(1, vec2 in_d = vec2(%i, %i); ,in->width, in->height); + GLSLF(1, vec2 c_r = vec2(%i, %i) / in_d; ,crop_w, crop_h); + GLSLF(1, vec2 c_o = vec2(%i, %i) / in_d; ,crop_x,crop_y); + GLSLC(0, ); + + if (s->vkctx.output_format == s->vkctx.input_format) { + for (int i = 0; i < desc[1].elems; i++) { + GLSLF(1, size = imageSize(output_img[%i]); ,i); + GLSLC(1, if (IS_WITHIN(pos, size)) { ); + switch (s->scaler) { + case F_NEAREST: + case F_BILINEAR: + GLSLF(2, vec4 res = scale_bilinear(%i, pos, c_r, c_o); ,i); + GLSLF(2, imageStore(output_img[%i], pos, res); ,i); + break; + }; + GLSLC(1, } ); + } + } else { + GLSLC(1, vec4 res = scale_bilinear(0, pos, c_r, c_o); ); + GLSLF(1, res = rgb2yuv(res, %i); ,s->out_range == AVCOL_RANGE_JPEG); + switch (s->vkctx.output_format) { + case AV_PIX_FMT_NV12: GLSLC(1, write_nv12(res, pos); ); break; + case AV_PIX_FMT_YUV420P: GLSLC(1, write_420(res, pos); ); break; + case AV_PIX_FMT_YUV444P: GLSLC(1, write_444(res, pos); ); break; + default: return AVERROR(EINVAL); + } } - RET(ff_vk_init_pipeline_layout(vkctx, s->pl)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl)); + GLSLC(0, } ); if (s->vkctx.output_format != s->vkctx.input_format) { const AVLumaCoefficients *lcoeffs; double tmp_mat[3][3]; - struct { - float yuv_matrix[4][4]; - } *par; - lcoeffs = av_csp_luma_coeffs_from_avcsp(in->colorspace); if (!lcoeffs) { av_log(ctx, AV_LOG_ERROR, "Unsupported colorspace\n"); return AVERROR(EINVAL); } - RET(ff_vk_create_buf(vkctx, &s->params_buf, - sizeof(*par), - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)); - - RET(ff_vk_map_buffers(vkctx, &s->params_buf, (uint8_t **)&par, 1, 0)); - ff_fill_rgb2yuv_table(lcoeffs, tmp_mat); - memset(par, 0, sizeof(*par)); - for (int y = 0; y < 3; y++) for (int x = 0; x < 3; x++) - par->yuv_matrix[x][y] = tmp_mat[x][y]; - - par->yuv_matrix[3][3] = 1.0; - - RET(ff_vk_unmap_buffers(vkctx, &s->params_buf, 1, 1)); - - s->params_desc.buffer = s->params_buf.buf; - s->params_desc.range = VK_WHOLE_SIZE; - - ff_vk_update_descriptor_set(vkctx, s->pl, 1); + s->opts.yuv_matrix[x][y] = tmp_mat[x][y]; + s->opts.yuv_matrix[3][3] = 1.0; } - /* Execution context */ - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); + RET(spv->compile_shader(spv, ctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); - s->initialized = 1; + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); - return 0; + s->initialized = 1; fail: - return err; -} - -static int process_frames(AVFilterContext *avctx, AVFrame *out_f, AVFrame *in_f) -{ - int err = 0; - VkCommandBuffer cmd_buf; - ScaleVulkanContext *s = avctx->priv; - FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &vkctx->vkfn; - AVVkFrame *in = (AVVkFrame *)in_f->data[0]; - AVVkFrame *out = (AVVkFrame *)out_f->data[0]; - VkImageMemoryBarrier barriers[AV_NUM_DATA_POINTERS*2]; - int barrier_count = 0; - const int planes = av_pix_fmt_count_planes(s->vkctx.input_format); - const VkFormat *input_formats = av_vkfmt_from_pixfmt(s->vkctx.input_format); - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - /* Update descriptors and init the exec context */ - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->input_images[i].imageView, in->img[i], - input_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } - - ff_vk_update_descriptor_set(vkctx, s->pl, 0); - - for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier bar = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = in->layout[i], - .newLayout = s->input_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = in->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }; - - memcpy(&barriers[barrier_count++], &bar, sizeof(VkImageMemoryBarrier)); - - in->layout[i] = bar.newLayout; - in->access[i] = bar.dstAccessMask; - } - - for (int i = 0; i < av_pix_fmt_count_planes(s->vkctx.output_format); i++) { - VkImageMemoryBarrier bar = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }; - - memcpy(&barriers[barrier_count++], &bar, sizeof(VkImageMemoryBarrier)); - - out->layout[i] = bar.newLayout; - out->access[i] = bar.dstAccessMask; - } - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, barrier_count, barriers); - - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl); - - vk->CmdDispatch(cmd_buf, - FFALIGN(vkctx->output_width, CGROUPS[0])/CGROUPS[0], - FFALIGN(vkctx->output_height, CGROUPS[1])/CGROUPS[1], 1); - - ff_vk_add_exec_dep(vkctx, s->exec, in_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, out_f, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - - err = ff_vk_submit_exec_queue(vkctx, s->exec); - if (err) - return err; - - ff_vk_qf_rotate(&s->qf); + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); return err; - -fail: - ff_vk_discard_exec_deps(s->exec); - return err; } static int scale_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) @@ -408,7 +281,8 @@ static int scale_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) if (!s->initialized) RET(init_filter(ctx, in)); - RET(process_frames(ctx, out, in)); + RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, out, in, + s->sampler, &s->opts, sizeof(s->opts))); err = av_frame_copy_props(out, in); if (err < 0) @@ -475,8 +349,17 @@ static int scale_vulkan_config_output(AVFilterLink *outlink) static void scale_vulkan_uninit(AVFilterContext *avctx) { ScaleVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); - ff_vk_free_buf(&s->vkctx, &s->params_buf); ff_vk_uninit(&s->vkctx); s->initialized = 0; @@ -487,17 +370,17 @@ static void scale_vulkan_uninit(AVFilterContext *avctx) static const AVOption scale_vulkan_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 }, - { "scaler", "Scaler function", OFFSET(scaler), AV_OPT_TYPE_INT, {.i64 = F_BILINEAR}, 0, F_NB, .flags = FLAGS, "scaler" }, - { "bilinear", "Bilinear interpolation (fastest)", 0, AV_OPT_TYPE_CONST, {.i64 = F_BILINEAR}, 0, 0, .flags = FLAGS, "scaler" }, - { "nearest", "Nearest (useful for pixel art)", 0, AV_OPT_TYPE_CONST, {.i64 = F_NEAREST}, 0, 0, .flags = FLAGS, "scaler" }, + { "scaler", "Scaler function", OFFSET(scaler), AV_OPT_TYPE_INT, {.i64 = F_BILINEAR}, 0, F_NB, .flags = FLAGS, .unit = "scaler" }, + { "bilinear", "Bilinear interpolation (fastest)", 0, AV_OPT_TYPE_CONST, {.i64 = F_BILINEAR}, 0, 0, .flags = FLAGS, .unit = "scaler" }, + { "nearest", "Nearest (useful for pixel art)", 0, AV_OPT_TYPE_CONST, {.i64 = F_NEAREST}, 0, 0, .flags = FLAGS, .unit = "scaler" }, { "format", "Output video format (software format of hardware frames)", OFFSET(out_format_string), AV_OPT_TYPE_STRING, .flags = FLAGS }, - { "out_range", "Output colour range (from 0 to 2) (default 0)", OFFSET(out_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED}, AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_JPEG, .flags = FLAGS, "range" }, - { "full", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, - { "limited", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, - { "jpeg", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, - { "mpeg", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, - { "tv", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, - { "pc", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, + { "out_range", "Output colour range (from 0 to 2) (default 0)", OFFSET(out_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED}, AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_JPEG, .flags = FLAGS, .unit = "range" }, + { "full", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" }, + { "limited", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" }, + { "jpeg", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" }, + { "mpeg", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" }, + { "tv", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" }, + { "pc", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" }, { NULL }, }; @@ -531,4 +414,5 @@ const AVFilter ff_vf_scale_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &scale_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_scdet.c b/libavfilter/vf_scdet.c index 8dcce5ada64..15399cfebf0 100644 --- a/libavfilter/vf_scdet.c +++ b/libavfilter/vf_scdet.c @@ -29,6 +29,7 @@ #include "avfilter.h" #include "filters.h" #include "scene_sad.h" +#include "video.h" typedef struct SCDetContext { const AVClass *class; @@ -125,7 +126,6 @@ static double get_scene_score(AVFilterContext *ctx, AVFrame *frame) count += s->width[plane] * s->height[plane]; } - emms_c(); mafd = (double)sad * 100. / count / (1ULL << s->bitdepth); diff = fabs(mafd - s->prev_mafd); ret = av_clipf(FFMIN(mafd, diff), 0, 100.); @@ -193,13 +193,6 @@ static const AVFilterPad scdet_inputs[] = { }, }; -static const AVFilterPad scdet_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_scdet = { .name = "scdet", .description = NULL_IF_CONFIG_SMALL("Detect video scene change"), @@ -208,7 +201,7 @@ const AVFilter ff_vf_scdet = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(scdet_inputs), - FILTER_OUTPUTS(scdet_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .activate = activate, }; diff --git a/libavfilter/vf_scroll.c b/libavfilter/vf_scroll.c index 107686cb02b..eebf12e902b 100644 --- a/libavfilter/vf_scroll.c +++ b/libavfilter/vf_scroll.c @@ -22,8 +22,8 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" typedef struct ScrollContext { const AVClass *class; @@ -194,20 +194,13 @@ static const AVFilterPad scroll_inputs[] = { }, }; -static const AVFilterPad scroll_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_scroll = { .name = "scroll", .description = NULL_IF_CONFIG_SMALL("Scroll input video."), .priv_size = sizeof(ScrollContext), .priv_class = &scroll_class, FILTER_INPUTS(scroll_inputs), - FILTER_OUTPUTS(scroll_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_selectivecolor.c b/libavfilter/vf_selectivecolor.c index ea4b5dfee25..eea79b7815c 100644 --- a/libavfilter/vf_selectivecolor.c +++ b/libavfilter/vf_selectivecolor.c @@ -32,7 +32,6 @@ #include "libavcodec/mathops.h" // for mid_pred(), which is a macro so no link dependency #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -96,9 +95,9 @@ typedef struct SelectiveColorContext { { color_name"s", "adjust "color_name" regions", OFFSET(opt_cmyk_adjust[range]), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS } static const AVOption selectivecolor_options[] = { - { "correction_method", "select correction method", OFFSET(correction_method), AV_OPT_TYPE_INT, {.i64 = CORRECTION_METHOD_ABSOLUTE}, 0, NB_CORRECTION_METHODS-1, FLAGS, "correction_method" }, - { "absolute", NULL, 0, AV_OPT_TYPE_CONST, {.i64=CORRECTION_METHOD_ABSOLUTE}, INT_MIN, INT_MAX, FLAGS, "correction_method" }, - { "relative", NULL, 0, AV_OPT_TYPE_CONST, {.i64=CORRECTION_METHOD_RELATIVE}, INT_MIN, INT_MAX, FLAGS, "correction_method" }, + { "correction_method", "select correction method", OFFSET(correction_method), AV_OPT_TYPE_INT, {.i64 = CORRECTION_METHOD_ABSOLUTE}, 0, NB_CORRECTION_METHODS-1, FLAGS, .unit = "correction_method" }, + { "absolute", NULL, 0, AV_OPT_TYPE_CONST, {.i64=CORRECTION_METHOD_ABSOLUTE}, INT_MIN, INT_MAX, FLAGS, .unit = "correction_method" }, + { "relative", NULL, 0, AV_OPT_TYPE_CONST, {.i64=CORRECTION_METHOD_RELATIVE}, INT_MIN, INT_MAX, FLAGS, .unit = "correction_method" }, RANGE_OPTION("red", RANGE_REDS), RANGE_OPTION("yellow", RANGE_YELLOWS), RANGE_OPTION("green", RANGE_GREENS), @@ -474,19 +473,12 @@ static const AVFilterPad selectivecolor_inputs[] = { }, }; -static const AVFilterPad selectivecolor_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_selectivecolor = { .name = "selectivecolor", .description = NULL_IF_CONFIG_SMALL("Apply CMYK adjustments to specific color ranges."), .priv_size = sizeof(SelectiveColorContext), FILTER_INPUTS(selectivecolor_inputs), - FILTER_OUTPUTS(selectivecolor_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &selectivecolor_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, diff --git a/libavfilter/vf_separatefields.c b/libavfilter/vf_separatefields.c index 7db64c54798..b7ddb263770 100644 --- a/libavfilter/vf_separatefields.c +++ b/libavfilter/vf_separatefields.c @@ -22,6 +22,7 @@ #include "avfilter.h" #include "filters.h" #include "internal.h" +#include "video.h" typedef struct SeparateFieldsContext { int nb_planes; @@ -70,14 +71,19 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) int ret; inpicref->height = outlink->h; +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS inpicref->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + inpicref->flags &= ~AV_FRAME_FLAG_INTERLACED; if (!s->second) { goto clone; } else { AVFrame *second = s->second; - extract_field(second, s->nb_planes, second->top_field_first); + extract_field(second, s->nb_planes, !!(second->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)); if (second->pts != AV_NOPTS_VALUE && inpicref->pts != AV_NOPTS_VALUE) @@ -94,7 +100,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) return AVERROR(ENOMEM); } - extract_field(inpicref, s->nb_planes, !inpicref->top_field_first); + extract_field(inpicref, s->nb_planes, !(inpicref->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)); if (inpicref->pts != AV_NOPTS_VALUE) inpicref->pts *= 2; @@ -110,7 +116,7 @@ static int flush_frame(AVFilterLink *outlink, int64_t pts, int64_t *out_pts) if (s->second) { *out_pts = s->second->pts += pts; - extract_field(s->second, s->nb_planes, s->second->top_field_first); + extract_field(s->second, s->nb_planes, !!(s->second->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST)); ret = ff_filter_frame(outlink, s->second); s->second = NULL; } @@ -156,13 +162,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&s->second); } -static const AVFilterPad separatefields_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad separatefields_outputs[] = { { .name = "default", @@ -177,6 +176,6 @@ const AVFilter ff_vf_separatefields = { .priv_size = sizeof(SeparateFieldsContext), .activate = activate, .uninit = uninit, - FILTER_INPUTS(separatefields_inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(separatefields_outputs), }; diff --git a/libavfilter/vf_setparams.c b/libavfilter/vf_setparams.c index 95a2d15c02b..c96f4d314b0 100644 --- a/libavfilter/vf_setparams.c +++ b/libavfilter/vf_setparams.c @@ -23,6 +23,7 @@ #include "libavutil/pixfmt.h" #include "libavutil/opt.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "video.h" @@ -46,80 +47,103 @@ typedef struct SetParamsContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption setparams_options[] = { - {"field_mode", "select interlace mode", OFFSET(field_mode), AV_OPT_TYPE_INT, {.i64=MODE_AUTO}, -1, MODE_PROG, FLAGS, "mode"}, - {"auto", "keep the same input field", 0, AV_OPT_TYPE_CONST, {.i64=MODE_AUTO}, INT_MIN, INT_MAX, FLAGS, "mode"}, - {"bff", "mark as bottom-field-first", 0, AV_OPT_TYPE_CONST, {.i64=MODE_BFF}, INT_MIN, INT_MAX, FLAGS, "mode"}, - {"tff", "mark as top-field-first", 0, AV_OPT_TYPE_CONST, {.i64=MODE_TFF}, INT_MIN, INT_MAX, FLAGS, "mode"}, - {"prog", "mark as progressive", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PROG}, INT_MIN, INT_MAX, FLAGS, "mode"}, - - {"range", "select color range", OFFSET(color_range), AV_OPT_TYPE_INT, {.i64=-1},-1, AVCOL_RANGE_NB-1, FLAGS, "range"}, - {"auto", "keep the same color range", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, FLAGS, "range"}, - {"unspecified", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, FLAGS, "range"}, - {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, FLAGS, "range"}, - {"limited", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, FLAGS, "range"}, - {"tv", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, FLAGS, "range"}, - {"mpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, FLAGS, "range"}, - {"full", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range"}, - {"pc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range"}, - {"jpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range"}, - - {"color_primaries", "select color primaries", OFFSET(color_primaries), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_PRI_NB-1, FLAGS, "color_primaries"}, - {"auto", "keep the same color primaries", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT709}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_UNSPECIFIED}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470M}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470BG}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE170M}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE240M}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"film", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_FILM}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"bt2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT2020}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"smpte428", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE428}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"smpte431", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE431}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"smpte432", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE432}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"jedec-p22", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_JEDEC_P22}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - {"ebu3213", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_EBU3213}, INT_MIN, INT_MAX, FLAGS, "color_primaries"}, - - {"color_trc", "select color transfer", OFFSET(color_trc), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_TRC_NB-1, FLAGS, "color_trc"}, - {"auto", "keep the same color transfer", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT709}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_UNSPECIFIED}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA22}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA28}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE170M}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE240M}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_LINEAR}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"log100", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_LOG}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"log316", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_LOG_SQRT}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"iec61966-2-4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_4}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"bt1361e", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT1361_ECG}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"iec61966-2-1", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_1}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"bt2020-10", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_10}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"bt2020-12", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_12}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"smpte2084", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE2084}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"smpte428", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE428}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - {"arib-std-b67", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_ARIB_STD_B67}, INT_MIN, INT_MAX, FLAGS, "color_trc"}, - - {"colorspace", "select colorspace", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_SPC_NB-1, FLAGS, "colorspace"}, - {"auto", "keep the same colorspace", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"gbr", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_RGB}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT709}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_UNSPECIFIED}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"fcc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_FCC}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT470BG}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE170M}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE240M}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"ycgco", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_YCGCO}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"bt2020nc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_NCL}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"bt2020c", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_CL}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"smpte2085", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE2085}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"chroma-derived-nc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_CHROMA_DERIVED_NCL},INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"chroma-derived-c", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_CHROMA_DERIVED_CL}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, - {"ictcp", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_ICTCP}, INT_MIN, INT_MAX, FLAGS, "colorspace"}, + {"field_mode", "select interlace mode", OFFSET(field_mode), AV_OPT_TYPE_INT, {.i64=MODE_AUTO}, -1, MODE_PROG, FLAGS, .unit = "mode"}, + {"auto", "keep the same input field", 0, AV_OPT_TYPE_CONST, {.i64=MODE_AUTO}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + {"bff", "mark as bottom-field-first", 0, AV_OPT_TYPE_CONST, {.i64=MODE_BFF}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + {"tff", "mark as top-field-first", 0, AV_OPT_TYPE_CONST, {.i64=MODE_TFF}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + {"prog", "mark as progressive", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PROG}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + + {"range", "select color range", OFFSET(color_range), AV_OPT_TYPE_INT, {.i64=-1},-1, AVCOL_RANGE_NB-1, FLAGS, .unit = "range"}, + {"auto", "keep the same color range", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, FLAGS, .unit = "range"}, + {"unspecified", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, FLAGS, .unit = "range"}, + {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, FLAGS, .unit = "range"}, + {"limited", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, FLAGS, .unit = "range"}, + {"tv", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, FLAGS, .unit = "range"}, + {"mpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, FLAGS, .unit = "range"}, + {"full", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, FLAGS, .unit = "range"}, + {"pc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, FLAGS, .unit = "range"}, + {"jpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, FLAGS, .unit = "range"}, + + {"color_primaries", "select color primaries", OFFSET(color_primaries), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_PRI_NB-1, FLAGS, .unit = "color_primaries"}, + {"auto", "keep the same color primaries", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT709}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_UNSPECIFIED}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470M}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470BG}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE170M}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE240M}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"film", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_FILM}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"bt2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT2020}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"smpte428", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE428}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"smpte431", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE431}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"smpte432", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE432}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"jedec-p22", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_JEDEC_P22}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + {"ebu3213", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_EBU3213}, INT_MIN, INT_MAX, FLAGS, .unit = "color_primaries"}, + + {"color_trc", "select color transfer", OFFSET(color_trc), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_TRC_NB-1, FLAGS, .unit = "color_trc"}, + {"auto", "keep the same color transfer", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT709}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_UNSPECIFIED}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA22}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA28}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE170M}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE240M}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_LINEAR}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"log100", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_LOG}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"log316", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_LOG_SQRT}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"iec61966-2-4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_4}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"bt1361e", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT1361_ECG}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"iec61966-2-1", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_1}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"bt2020-10", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_10}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"bt2020-12", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_12}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"smpte2084", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE2084}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"smpte428", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE428}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + {"arib-std-b67", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_ARIB_STD_B67}, INT_MIN, INT_MAX, FLAGS, .unit = "color_trc"}, + + {"colorspace", "select colorspace", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64=-1}, -1, AVCOL_SPC_NB-1, FLAGS, .unit = "colorspace"}, + {"auto", "keep the same colorspace", 0, AV_OPT_TYPE_CONST, {.i64=-1}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"gbr", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_RGB}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT709}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_UNSPECIFIED}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"fcc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_FCC}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT470BG}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE170M}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE240M}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"ycgco", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_YCGCO}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"bt2020nc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_NCL}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"bt2020c", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_BT2020_CL}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"smpte2085", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_SMPTE2085}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"chroma-derived-nc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_CHROMA_DERIVED_NCL},INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"chroma-derived-c", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_CHROMA_DERIVED_CL}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, + {"ictcp", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_SPC_ICTCP}, INT_MIN, INT_MAX, FLAGS, .unit = "colorspace"}, {NULL} }; AVFILTER_DEFINE_CLASS(setparams); +static int query_formats(AVFilterContext *ctx) +{ + SetParamsContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + int ret; + + if (s->colorspace >= 0) { + ret = ff_formats_ref(ff_make_formats_list_singleton(s->colorspace), + &outlink->incfg.color_spaces); + if (ret < 0) + return ret; + } + + if (s->color_range >= 0) { + ret = ff_formats_ref(ff_make_formats_list_singleton(s->color_range), + &outlink->incfg.color_ranges); + if (ret < 0) + return ret; + } + + return 0; +} + static int filter_frame(AVFilterLink *inlink, AVFrame *frame) { AVFilterContext *ctx = inlink->dst; @@ -127,10 +151,24 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) /* set field */ if (s->field_mode == MODE_PROG) { +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS frame->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + frame->flags &= ~AV_FRAME_FLAG_INTERLACED; } else if (s->field_mode != MODE_AUTO) { +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS frame->interlaced_frame = 1; frame->top_field_first = s->field_mode; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + frame->flags |= AV_FRAME_FLAG_INTERLACED; + if (s->field_mode) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + frame->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; } /* set range */ @@ -155,13 +193,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_setparams = { .name = "setparams", .description = NULL_IF_CONFIG_SMALL("Force field, or color property for the output video frame."), @@ -169,22 +200,23 @@ const AVFilter ff_vf_setparams = { .priv_class = &setparams_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), + FILTER_QUERY_FUNC(query_formats), }; #if CONFIG_SETRANGE_FILTER static const AVOption setrange_options[] = { - {"range", "select color range", OFFSET(color_range), AV_OPT_TYPE_INT, {.i64=-1},-1, AVCOL_RANGE_NB-1, FLAGS, "range"}, - {"auto", "keep the same color range", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, FLAGS, "range"}, - {"unspecified", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, FLAGS, "range"}, - {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, FLAGS, "range"}, - {"limited", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, FLAGS, "range"}, - {"tv", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, FLAGS, "range"}, - {"mpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, FLAGS, "range"}, - {"full", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range"}, - {"pc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range"}, - {"jpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range"}, + {"range", "select color range", OFFSET(color_range), AV_OPT_TYPE_INT, {.i64=-1},-1, AVCOL_RANGE_NB-1, FLAGS, .unit = "range"}, + {"auto", "keep the same color range", 0, AV_OPT_TYPE_CONST, {.i64=-1}, 0, 0, FLAGS, .unit = "range"}, + {"unspecified", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, FLAGS, .unit = "range"}, + {"unknown", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_UNSPECIFIED}, 0, 0, FLAGS, .unit = "range"}, + {"limited", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, FLAGS, .unit = "range"}, + {"tv", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, FLAGS, .unit = "range"}, + {"mpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_MPEG}, 0, 0, FLAGS, .unit = "range"}, + {"full", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, FLAGS, .unit = "range"}, + {"pc", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, FLAGS, .unit = "range"}, + {"jpeg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_RANGE_JPEG}, 0, 0, FLAGS, .unit = "range"}, {NULL} }; @@ -209,17 +241,18 @@ const AVFilter ff_vf_setrange = { .priv_class = &setrange_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), + FILTER_QUERY_FUNC(query_formats), }; #endif /* CONFIG_SETRANGE_FILTER */ #if CONFIG_SETFIELD_FILTER static const AVOption setfield_options[] = { - {"mode", "select interlace mode", OFFSET(field_mode), AV_OPT_TYPE_INT, {.i64=MODE_AUTO}, -1, MODE_PROG, FLAGS, "mode"}, - {"auto", "keep the same input field", 0, AV_OPT_TYPE_CONST, {.i64=MODE_AUTO}, INT_MIN, INT_MAX, FLAGS, "mode"}, - {"bff", "mark as bottom-field-first", 0, AV_OPT_TYPE_CONST, {.i64=MODE_BFF}, INT_MIN, INT_MAX, FLAGS, "mode"}, - {"tff", "mark as top-field-first", 0, AV_OPT_TYPE_CONST, {.i64=MODE_TFF}, INT_MIN, INT_MAX, FLAGS, "mode"}, - {"prog", "mark as progressive", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PROG}, INT_MIN, INT_MAX, FLAGS, "mode"}, + {"mode", "select interlace mode", OFFSET(field_mode), AV_OPT_TYPE_INT, {.i64=MODE_AUTO}, -1, MODE_PROG, FLAGS, .unit = "mode"}, + {"auto", "keep the same input field", 0, AV_OPT_TYPE_CONST, {.i64=MODE_AUTO}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + {"bff", "mark as bottom-field-first", 0, AV_OPT_TYPE_CONST, {.i64=MODE_BFF}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + {"tff", "mark as top-field-first", 0, AV_OPT_TYPE_CONST, {.i64=MODE_TFF}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + {"prog", "mark as progressive", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PROG}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, {NULL} }; @@ -244,6 +277,6 @@ const AVFilter ff_vf_setfield = { .priv_class = &setfield_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; #endif /* CONFIG_SETFIELD_FILTER */ diff --git a/libavfilter/vf_sharpen_npp.c b/libavfilter/vf_sharpen_npp.c index 0a2c5c458be..73c77dd5df9 100644 --- a/libavfilter/vf_sharpen_npp.c +++ b/libavfilter/vf_sharpen_npp.c @@ -228,8 +228,8 @@ static int nppsharpen_filter_frame(AVFilterLink* link, AVFrame* in) #define OFFSET(x) offsetof(NPPSharpenContext, x) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) static const AVOption options[] = { - { "border_type", "Type of operation to be performed on image border", OFFSET(border_type), AV_OPT_TYPE_INT, { .i64 = NPP_BORDER_REPLICATE }, NPP_BORDER_REPLICATE, NPP_BORDER_REPLICATE, FLAGS, "border_type" }, - { "replicate", "replicate pixels", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_BORDER_REPLICATE }, 0, 0, FLAGS, "border_type" }, + { "border_type", "Type of operation to be performed on image border", OFFSET(border_type), AV_OPT_TYPE_INT, { .i64 = NPP_BORDER_REPLICATE }, NPP_BORDER_REPLICATE, NPP_BORDER_REPLICATE, FLAGS, .unit = "border_type" }, + { "replicate", "replicate pixels", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_BORDER_REPLICATE }, 0, 0, FLAGS, .unit = "border_type" }, {NULL}, }; diff --git a/libavfilter/vf_shear.c b/libavfilter/vf_shear.c index 760caa50110..5008db3f46f 100644 --- a/libavfilter/vf_shear.c +++ b/libavfilter/vf_shear.c @@ -64,9 +64,9 @@ static const AVOption shear_options[] = { { "shy", "set y shear factor", OFFSET(shy), AV_OPT_TYPE_FLOAT, {.dbl=0.}, -2, 2, .flags=FLAGS }, { "fillcolor", "set background fill color", OFFSET(fillcolor_str), AV_OPT_TYPE_STRING, {.str="black"}, 0, 0, .flags=FLAGS }, { "c", "set background fill color", OFFSET(fillcolor_str), AV_OPT_TYPE_STRING, {.str="black"}, 0, 0, .flags=FLAGS }, - { "interp", "set interpolation", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, .flags=FLAGS, "interp" }, - { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags=FLAGS, "interp" }, - { "bilinear", "bilinear", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags=FLAGS, "interp" }, + { "interp", "set interpolation", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, .flags=FLAGS, .unit = "interp" }, + { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, .flags=FLAGS, .unit = "interp" }, + { "bilinear", "bilinear", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, .flags=FLAGS, .unit = "interp" }, { NULL } }; @@ -260,7 +260,7 @@ static int config_output(AVFilterLink *outlink) s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(ctx->inputs[0]->h, desc->log2_chroma_h); s->planeheight[0] = s->planeheight[3] = ctx->inputs[0]->h; - ff_draw_init(&s->draw, outlink->format, 0); + ff_draw_init2(&s->draw, outlink->format, outlink->colorspace, outlink->color_range, 0); ff_draw_color(&s->draw, &s->color, s->fillcolor); s->filter_slice[0] = s->depth <= 8 ? filter_slice_nn8 : filter_slice_nn16; diff --git a/libavfilter/vf_showinfo.c b/libavfilter/vf_showinfo.c index e55625b3382..28d8ea76e9b 100644 --- a/libavfilter/vf_showinfo.c +++ b/libavfilter/vf_showinfo.c @@ -22,6 +22,7 @@ * filter for showing textual video frame information */ +#include #include #include "libavutil/bswap.h" @@ -52,6 +53,7 @@ typedef struct ShowInfoContext { const AVClass *class; int calculate_checksums; + int udu_sei_as_ascii; } ShowInfoContext; #define OFFSET(x) offsetof(ShowInfoContext, x) @@ -59,6 +61,8 @@ typedef struct ShowInfoContext { static const AVOption showinfo_options[] = { { "checksum", "calculate checksums", OFFSET(calculate_checksums), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, VF }, + { "udu_sei_as_ascii", "try to print user data unregistered SEI as ascii character when possible", + OFFSET(udu_sei_as_ascii), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VF }, { NULL } }; @@ -354,19 +358,20 @@ static void dump_dynamic_hdr_vivid(AVFilterContext *ctx, AVFrameSideData *sd) av_log(ctx, AV_LOG_INFO, "3Spline_enable_flag[%d][%d]: %d, ", w, i, tm_params->three_Spline_enable_flag); if (tm_params->three_Spline_enable_flag) { - av_log(ctx, AV_LOG_INFO, "3Spline_TH_mode[%d][%d]: %d, ", w, i, tm_params->three_Spline_TH_mode); - for (int j = 0; j < tm_params->three_Spline_num; j++) { - av_log(ctx, AV_LOG_INFO, "3Spline_TH_enable_MB[%d][%d][%d]: %.4f, ", - w, i, j, av_q2d(tm_params->three_Spline_TH_enable_MB)); + const AVHDRVivid3SplineParams *three_spline = &tm_params->three_spline[j]; + av_log(ctx, AV_LOG_INFO, "3Spline_TH_mode[%d][%d]: %d, ", w, i, three_spline->th_mode); + if (three_spline->th_mode == 0 || three_spline->th_mode == 2) + av_log(ctx, AV_LOG_INFO, "3Spline_TH_enable_MB[%d][%d][%d]: %.4f, ", + w, i, j, av_q2d(three_spline->th_enable_mb)); av_log(ctx, AV_LOG_INFO, "3Spline_TH_enable[%d][%d][%d]: %.4f, ", - w, i, j, av_q2d(tm_params->three_Spline_TH_enable)); + w, i, j, av_q2d(three_spline->th_enable)); av_log(ctx, AV_LOG_INFO, "3Spline_TH_Delta1[%d][%d][%d]: %.4f, ", - w, i, j, av_q2d(tm_params->three_Spline_TH_Delta1)); + w, i, j, av_q2d(three_spline->th_delta1)); av_log(ctx, AV_LOG_INFO, "3Spline_TH_Delta2[%d][%d][%d]: %.4f, ", - w, i, j, av_q2d(tm_params->three_Spline_TH_Delta2)); + w, i, j, av_q2d(three_spline->th_delta2)); av_log(ctx, AV_LOG_INFO, "3Spline_enable_Strength[%d][%d][%d]: %.4f, ", - w, i, j, av_q2d(tm_params->three_Spline_enable_Strength)); + w, i, j, av_q2d(three_spline->enable_strength)); } } } @@ -417,6 +422,7 @@ static void dump_video_enc_params(AVFilterContext *ctx, const AVFrameSideData *s static void dump_sei_unregistered_metadata(AVFilterContext *ctx, const AVFrameSideData *sd) { const uint8_t *user_data = sd->data; + ShowInfoContext *s = ctx->priv; if (sd->size < AV_UUID_LEN) { av_log(ctx, AV_LOG_ERROR, "invalid data(%"SIZE_SPECIFIER" < " @@ -427,8 +433,13 @@ static void dump_sei_unregistered_metadata(AVFilterContext *ctx, const AVFrameSi av_log(ctx, AV_LOG_INFO, "UUID=" AV_PRI_UUID "\n", AV_UUID_ARG(user_data)); av_log(ctx, AV_LOG_INFO, "User Data="); - for (size_t i = 16; i < sd->size; i++) - av_log(ctx, AV_LOG_INFO, "%02x", user_data[i]); + for (size_t i = 16; i < sd->size; i++) { + const char *format = "%02x"; + + if (s->udu_sei_as_ascii) + format = isprint(user_data[i]) ? "%c" : "\\x%02x"; + av_log(ctx, AV_LOG_INFO, format, user_data[i]); + } av_log(ctx, AV_LOG_INFO, "\n"); } @@ -441,6 +452,11 @@ static void dump_sei_film_grain_params_metadata(AVFilterContext *ctx, const AVFr [AV_FILM_GRAIN_PARAMS_H274] = "h274", }; + const char *color_range_str = av_color_range_name(fgp->color_range); + const char *color_primaries_str = av_color_primaries_name(fgp->color_primaries); + const char *color_trc_str = av_color_transfer_name(fgp->color_trc); + const char *colorspace_str = av_color_space_name(fgp->color_space); + if (fgp->type >= FF_ARRAY_ELEMS(film_grain_type_names)) { av_log(ctx, AV_LOG_ERROR, "invalid data\n"); return; @@ -448,25 +464,62 @@ static void dump_sei_film_grain_params_metadata(AVFilterContext *ctx, const AVFr av_log(ctx, AV_LOG_INFO, "type %s; ", film_grain_type_names[fgp->type]); av_log(ctx, AV_LOG_INFO, "seed=%"PRIu64"; ", fgp->seed); + av_log(ctx, AV_LOG_INFO, "width=%d; ", fgp->width); + av_log(ctx, AV_LOG_INFO, "height=%d; ", fgp->height); + av_log(ctx, AV_LOG_INFO, "subsampling_x=%d; ", fgp->subsampling_x); + av_log(ctx, AV_LOG_INFO, "subsampling_y=%d; ", fgp->subsampling_y); + av_log(ctx, AV_LOG_INFO, "color_range=%s; ", color_range_str ? color_range_str : "unknown"); + av_log(ctx, AV_LOG_INFO, "color_primaries=%s; ", color_primaries_str ? color_primaries_str : "unknown"); + av_log(ctx, AV_LOG_INFO, "color_trc=%s; ", color_trc_str ? color_trc_str : "unknown"); + av_log(ctx, AV_LOG_INFO, "color_space=%s; ", colorspace_str ? colorspace_str : "unknown"); + av_log(ctx, AV_LOG_INFO, "bit_depth_luma=%d; ", fgp->bit_depth_luma); + av_log(ctx, AV_LOG_INFO, "bit_depth_chroma=%d; ", fgp->bit_depth_chroma); switch (fgp->type) { case AV_FILM_GRAIN_PARAMS_NONE: - case AV_FILM_GRAIN_PARAMS_AV1: - return; + break; + case AV_FILM_GRAIN_PARAMS_AV1: { + const AVFilmGrainAOMParams *aom = &fgp->codec.aom; + const int num_ar_coeffs_y = 2 * aom->ar_coeff_lag * (aom->ar_coeff_lag + 1); + const int num_ar_coeffs_uv = num_ar_coeffs_y + !!aom->num_y_points; + av_log(ctx, AV_LOG_INFO, "y_points={ "); + for (int i = 0; i < aom->num_y_points; i++) + av_log(ctx, AV_LOG_INFO, "(%d,%d) ", aom->y_points[i][0], aom->y_points[i][1]); + av_log(ctx, AV_LOG_INFO, "}; chroma_scaling_from_luma=%d; ", aom->chroma_scaling_from_luma); + for (int uv = 0; uv < 2; uv++) { + av_log(ctx, AV_LOG_INFO, "uv_points[%d]={ ", uv); + for (int i = 0; i < aom->num_uv_points[uv]; i++) + av_log(ctx, AV_LOG_INFO, "(%d,%d) ", aom->uv_points[uv][i][0], aom->uv_points[uv][i][1]); + av_log(ctx, AV_LOG_INFO, "}; "); + } + av_log(ctx, AV_LOG_INFO, "scaling_shift=%d; ", aom->scaling_shift); + av_log(ctx, AV_LOG_INFO, "ar_coeff_lag=%d; ", aom->ar_coeff_lag); + if (num_ar_coeffs_y) { + av_log(ctx, AV_LOG_INFO, "ar_coeffs_y={ "); + for (int i = 0; i < num_ar_coeffs_y; i++) + av_log(ctx, AV_LOG_INFO, "%d ", aom->ar_coeffs_y[i]); + av_log(ctx, AV_LOG_INFO, "}; "); + } + for (int uv = 0; num_ar_coeffs_uv && uv < 2; uv++) { + av_log(ctx, AV_LOG_INFO, "ar_coeffs_uv[%d]={ ", uv); + for (int i = 0; i < num_ar_coeffs_uv; i++) + av_log(ctx, AV_LOG_INFO, "%d ", aom->ar_coeffs_uv[uv][i]); + av_log(ctx, AV_LOG_INFO, "}; "); + } + av_log(ctx, AV_LOG_INFO, "ar_coeff_shift=%d; ", aom->ar_coeff_shift); + av_log(ctx, AV_LOG_INFO, "grain_scale_shift=%d; ", aom->grain_scale_shift); + for (int uv = 0; uv < 2; uv++) { + av_log(ctx, AV_LOG_INFO, "uv_mult[%d] = %d; ", uv, aom->uv_mult[uv]); + av_log(ctx, AV_LOG_INFO, "uv_mult_luma[%d] = %d; ", uv, aom->uv_mult_luma[uv]); + av_log(ctx, AV_LOG_INFO, "uv_offset[%d] = %d; ", uv, aom->uv_offset[uv]); + } + av_log(ctx, AV_LOG_INFO, "overlap_flag=%d; ", aom->overlap_flag); + av_log(ctx, AV_LOG_INFO, "limit_output_range=%d; ", aom->limit_output_range); + break; + } case AV_FILM_GRAIN_PARAMS_H274: { const AVFilmGrainH274Params *h274 = &fgp->codec.h274; - const char *color_range_str = av_color_range_name(h274->color_range); - const char *color_primaries_str = av_color_primaries_name(h274->color_primaries); - const char *color_trc_str = av_color_transfer_name(h274->color_trc); - const char *colorspace_str = av_color_space_name(h274->color_space); - av_log(ctx, AV_LOG_INFO, "model_id=%d; ", h274->model_id); - av_log(ctx, AV_LOG_INFO, "bit_depth_luma=%d; ", h274->bit_depth_luma); - av_log(ctx, AV_LOG_INFO, "bit_depth_chroma=%d; ", h274->bit_depth_chroma); - av_log(ctx, AV_LOG_INFO, "color_range=%s; ", color_range_str ? color_range_str : "unknown"); - av_log(ctx, AV_LOG_INFO, "color_primaries=%s; ", color_primaries_str ? color_primaries_str : "unknown"); - av_log(ctx, AV_LOG_INFO, "color_trc=%s; ", color_trc_str ? color_trc_str : "unknown"); - av_log(ctx, AV_LOG_INFO, "color_space=%s; ", colorspace_str ? colorspace_str : "unknown"); av_log(ctx, AV_LOG_INFO, "blending_mode_id=%d; ", h274->blending_mode_id); av_log(ctx, AV_LOG_INFO, "log2_scale_factor=%d; ", h274->log2_scale_factor); @@ -713,18 +766,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) av_log(ctx, AV_LOG_INFO, "n:%4"PRId64" pts:%7s pts_time:%-7s duration:%7"PRId64 - " duration_time:%-7s pos:%9"PRId64" " - "fmt:%s sar:%d/%d s:%dx%d i:%c iskey:%d type:%c ", + " duration_time:%-7s " + "fmt:%s cl:%s sar:%d/%d s:%dx%d i:%c iskey:%d type:%c ", inlink->frame_count_out, av_ts2str(frame->pts), av_ts2timestr(frame->pts, &inlink->time_base), frame->duration, av_ts2timestr(frame->duration, &inlink->time_base), - frame->pkt_pos, - desc->name, + desc->name, av_chroma_location_name(frame->chroma_location), frame->sample_aspect_ratio.num, frame->sample_aspect_ratio.den, frame->width, frame->height, - !frame->interlaced_frame ? 'P' : /* Progressive */ - frame->top_field_first ? 'T' : 'B', /* Top / Bottom */ - frame->key_frame, + !(frame->flags & AV_FRAME_FLAG_INTERLACED) ? 'P' : /* Progressive */ + (frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 'T' : 'B', /* Top / Bottom */ + !!(frame->flags & AV_FRAME_FLAG_KEY), av_get_picture_type_char(frame->pict_type)); if (s->calculate_checksums) { diff --git a/libavfilter/vf_shuffleframes.c b/libavfilter/vf_shuffleframes.c index 14b90e64461..cf8f197e27d 100644 --- a/libavfilter/vf_shuffleframes.c +++ b/libavfilter/vf_shuffleframes.c @@ -146,13 +146,6 @@ static const AVFilterPad shuffleframes_inputs[] = { }, }; -static const AVFilterPad shuffleframes_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_shuffleframes = { .name = "shuffleframes", .description = NULL_IF_CONFIG_SMALL("Shuffle video frames."), @@ -161,6 +154,6 @@ const AVFilter ff_vf_shuffleframes = { .init = init, .uninit = uninit, FILTER_INPUTS(shuffleframes_inputs), - FILTER_OUTPUTS(shuffleframes_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_shufflepixels.c b/libavfilter/vf_shufflepixels.c index e7bd738ee98..d3e1af087d4 100644 --- a/libavfilter/vf_shufflepixels.c +++ b/libavfilter/vf_shufflepixels.c @@ -404,15 +404,15 @@ static av_cold void uninit(AVFilterContext *ctx) #define OFFSET(x) offsetof(ShufflePixelsContext, x) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) static const AVOption shufflepixels_options[] = { - { "direction", "set shuffle direction", OFFSET(direction), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "dir" }, - { "d", "set shuffle direction", OFFSET(direction), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "dir" }, - { "forward", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "dir" }, - { "inverse", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "dir" }, - { "mode", "set shuffle mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "mode" }, - { "m", "set shuffle mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "mode" }, - { "horizontal", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" }, - { "vertical", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" }, - { "block", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "mode" }, + { "direction", "set shuffle direction", OFFSET(direction), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "dir" }, + { "d", "set shuffle direction", OFFSET(direction), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "dir" }, + { "forward", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "dir" }, + { "inverse", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "dir" }, + { "mode", "set shuffle mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, .unit = "mode" }, + { "m", "set shuffle mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, .unit = "mode" }, + { "horizontal", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "mode" }, + { "vertical", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "mode" }, + { "block", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "mode" }, { "width", "set block width", OFFSET(block_w), AV_OPT_TYPE_INT, {.i64=10}, 1, 8000, FLAGS }, { "w", "set block width", OFFSET(block_w), AV_OPT_TYPE_INT, {.i64=10}, 1, 8000, FLAGS }, { "height", "set block height", OFFSET(block_h), AV_OPT_TYPE_INT, {.i64=10}, 1, 8000, FLAGS }, diff --git a/libavfilter/vf_shuffleplanes.c b/libavfilter/vf_shuffleplanes.c index b2f64ad0763..fb2f85cf550 100644 --- a/libavfilter/vf_shuffleplanes.c +++ b/libavfilter/vf_shuffleplanes.c @@ -24,6 +24,7 @@ #include "libavutil/pixfmt.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" #include "video.h" @@ -153,20 +154,13 @@ static const AVFilterPad shuffleplanes_inputs[] = { }, }; -static const AVFilterPad shuffleplanes_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_shuffleplanes = { .name = "shuffleplanes", .description = NULL_IF_CONFIG_SMALL("Shuffle video planes."), .priv_size = sizeof(ShufflePlanesContext), .priv_class = &shuffleplanes_class, FILTER_INPUTS(shuffleplanes_inputs), - FILTER_OUTPUTS(shuffleplanes_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_signalstats.c b/libavfilter/vf_signalstats.c index b4d10292960..49bd9aa5095 100644 --- a/libavfilter/vf_signalstats.c +++ b/libavfilter/vf_signalstats.c @@ -72,14 +72,14 @@ typedef struct ThreadDataHueSatMetrics { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption signalstats_options[] = { - {"stat", "set statistics filters", OFFSET(filters), AV_OPT_TYPE_FLAGS, {.i64=0}, 0, INT_MAX, FLAGS, "filters"}, - {"tout", "analyze pixels for temporal outliers", 0, AV_OPT_TYPE_CONST, {.i64=1<elem_count, sizeof(int64_t)); + elemsignature = av_malloc_array(elemcat->elem_count, 2 * sizeof(int64_t)); if (!elemsignature) return AVERROR(ENOMEM); - sortsignature = av_malloc_array(elemcat->elem_count, sizeof(int64_t)); - if (!sortsignature) { - av_freep(&elemsignature); - return AVERROR(ENOMEM); - } + sortsignature = elemsignature + elemcat->elem_count; for (j = 0; j < elemcat->elem_count; j++) { blocksum = 0; @@ -307,7 +303,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref) f++; } av_freep(&elemsignature); - av_freep(&sortsignature); } /* confidence */ @@ -384,6 +379,9 @@ static int xml_export(AVFilterContext *ctx, StreamContext *sc, const char* filen FILE* f; unsigned int pot3[5] = { 3*3*3*3, 3*3*3, 3*3, 3, 1 }; + if (!sc->coarseend->last) + return AVERROR(EINVAL); // No frames ? + f = avpriv_fopen_utf8(filename, "w"); if (!f) { int err = AVERROR(EINVAL); diff --git a/libavfilter/vf_siti.c b/libavfilter/vf_siti.c index 738affa5e84..1b6ff32d6e2 100644 --- a/libavfilter/vf_siti.c +++ b/libavfilter/vf_siti.c @@ -31,7 +31,6 @@ #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -328,13 +327,6 @@ static const AVFilterPad avfilter_vf_siti_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_siti_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - const AVFilter ff_vf_siti = { .name = "siti", .description = NULL_IF_CONFIG_SMALL("Calculate spatial information (SI) and temporal information (TI)."), @@ -345,5 +337,5 @@ const AVFilter ff_vf_siti = { .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_PIXFMTS_ARRAY(pix_fmts), FILTER_INPUTS(avfilter_vf_siti_inputs), - FILTER_OUTPUTS(avfilter_vf_siti_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_smartblur.c b/libavfilter/vf_smartblur.c index 48c28d2760c..ae0ec05b2d4 100644 --- a/libavfilter/vf_smartblur.c +++ b/libavfilter/vf_smartblur.c @@ -30,8 +30,8 @@ #include "libswscale/swscale.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" #define RADIUS_MIN 0.1 #define RADIUS_MAX 5.0 @@ -136,7 +136,7 @@ static int alloc_sws_context(FilterParam *f, int width, int height, unsigned int vec->coeff[vec->length / 2] += 1.0 - f->strength; sws_filter.lumH = sws_filter.lumV = vec; sws_filter.chrH = sws_filter.chrV = NULL; - f->filter_context = sws_getCachedContext(NULL, + f->filter_context = sws_getCachedContext(f->filter_context, width, height, AV_PIX_FMT_GRAY8, width, height, AV_PIX_FMT_GRAY8, flags, &sws_filter, NULL, NULL); @@ -274,13 +274,6 @@ static const AVFilterPad smartblur_inputs[] = { }, }; -static const AVFilterPad smartblur_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_smartblur = { .name = "smartblur", .description = NULL_IF_CONFIG_SMALL("Blur the input video without impacting the outlines."), @@ -288,7 +281,7 @@ const AVFilter ff_vf_smartblur = { .init = init, .uninit = uninit, FILTER_INPUTS(smartblur_inputs), - FILTER_OUTPUTS(smartblur_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &smartblur_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_spp.c b/libavfilter/vf_spp.c index 8442bcc5974..5c6495612b8 100644 --- a/libavfilter/vf_spp.c +++ b/libavfilter/vf_spp.c @@ -31,6 +31,7 @@ * ported by Clément Bœsch for FFmpeg. */ +#include "libavutil/emms.h" #include "libavutil/imgutils.h" #include "libavutil/mem_internal.h" #include "libavutil/opt.h" @@ -38,6 +39,7 @@ #include "internal.h" #include "qp_table.h" #include "vf_spp.h" +#include "video.h" enum mode { MODE_HARD, @@ -64,9 +66,9 @@ static void *child_next(void *obj, void *prev) static const AVOption spp_options[] = { { "quality", "set quality", OFFSET(log2_count), AV_OPT_TYPE_INT, {.i64 = 3}, 0, MAX_LEVEL, TFLAGS }, { "qp", "force a constant quantizer parameter", OFFSET(qp), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 63, FLAGS }, - { "mode", "set thresholding mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_HARD}, 0, NB_MODES - 1, FLAGS, "mode" }, - { "hard", "hard thresholding", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_HARD}, INT_MIN, INT_MAX, FLAGS, "mode" }, - { "soft", "soft thresholding", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_SOFT}, INT_MIN, INT_MAX, FLAGS, "mode" }, + { "mode", "set thresholding mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_HARD}, 0, NB_MODES - 1, FLAGS, .unit = "mode" }, + { "hard", "hard thresholding", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_HARD}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, + { "soft", "soft thresholding", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_SOFT}, INT_MIN, INT_MAX, FLAGS, .unit = "mode" }, { "use_bframe_qp", "use B-frames' QP", OFFSET(use_bframe_qp), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, { NULL } }; @@ -484,13 +486,6 @@ static const AVFilterPad spp_inputs[] = { }, }; -static const AVFilterPad spp_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_spp = { .name = "spp", .description = NULL_IF_CONFIG_SMALL("Apply a simple post processing filter."), @@ -498,7 +493,7 @@ const AVFilter ff_vf_spp = { .preinit = preinit, .uninit = uninit, FILTER_INPUTS(spp_inputs), - FILTER_OUTPUTS(spp_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .process_command = process_command, .priv_class = &spp_class, diff --git a/libavfilter/vf_sr.c b/libavfilter/vf_sr.c index cb24c096ced..60683b52090 100644 --- a/libavfilter/vf_sr.c +++ b/libavfilter/vf_sr.c @@ -26,11 +26,10 @@ */ #include "avfilter.h" -#include "formats.h" #include "internal.h" +#include "video.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" -#include "libavformat/avio.h" #include "libswscale/swscale.h" #include "dnn_filter_common.h" @@ -46,10 +45,9 @@ typedef struct SRContext { #define OFFSET(x) offsetof(SRContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM static const AVOption sr_options[] = { - { "dnn_backend", "DNN backend used for model execution", OFFSET(dnnctx.backend_type), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, "backend" }, - { "native", "native backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "backend" }, + { "dnn_backend", "DNN backend used for model execution", OFFSET(dnnctx.backend_type), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS, .unit = "backend" }, #if (CONFIG_LIBTENSORFLOW == 1) - { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "backend" }, + { "tensorflow", "tensorflow backend flag", 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, .unit = "backend" }, #endif { "scale_factor", "scale factor for SRCNN model", OFFSET(scale_factor), AV_OPT_TYPE_INT, { .i64 = 2 }, 2, 4, FLAGS }, { "model", "path to model file specifying network architecture and its parameters", OFFSET(dnnctx.model_filename), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, diff --git a/libavfilter/vf_ssim.c b/libavfilter/vf_ssim.c index 1933b9b82da..53eb72fe6a3 100644 --- a/libavfilter/vf_ssim.c +++ b/libavfilter/vf_ssim.c @@ -358,6 +358,13 @@ static int do_ssim(FFFrameSync *fs) td.planeheight[n] = s->planeheight[n]; } + if (master->color_range != ref->color_range) { + av_log(ctx, AV_LOG_WARNING, "master and reference " + "frames use different color ranges (%s != %s)\n", + av_color_range_name(master->color_range), + av_color_range_name(ref->color_range)); + } + ff_filter_execute(ctx, s->ssim_plane, &td, NULL, FFMIN((s->planeheight[1] + 3) >> 2, s->nb_threads)); diff --git a/libavfilter/vf_ssim360.c b/libavfilter/vf_ssim360.c index 3eb8e43bbc8..0f5b11c7230 100644 --- a/libavfilter/vf_ssim360.c +++ b/libavfilter/vf_ssim360.c @@ -50,9 +50,7 @@ #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" -#include "video.h" #include "framesync.h" #define RIGHT 0 @@ -221,30 +219,30 @@ static const AVOption ssim360_options[] = { { "ref_projection", "projection of the reference video", OFFSET(ref_projection), AV_OPT_TYPE_INT, {.i64 = PROJECTION_EQUIRECT}, - 0, PROJECTION_N - 1, .flags = FLAGS, "projection" }, + 0, PROJECTION_N - 1, .flags = FLAGS, .unit = "projection" }, - { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_EQUIRECT}, 0, 0, FLAGS, "projection" }, - { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_EQUIRECT}, 0, 0, FLAGS, "projection" }, - { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_CUBEMAP32}, 0, 0, FLAGS, "projection" }, - { "c2x3", "cubemap 2x3", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_CUBEMAP23}, 0, 0, FLAGS, "projection" }, - { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_BARREL}, 0, 0, FLAGS, "projection" }, - { "barrelsplit", "barrel split facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_BARREL_SPLIT}, 0, 0, FLAGS, "projection" }, + { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_EQUIRECT}, 0, 0, FLAGS, .unit = "projection" }, + { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_EQUIRECT}, 0, 0, FLAGS, .unit = "projection" }, + { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_CUBEMAP32}, 0, 0, FLAGS, .unit = "projection" }, + { "c2x3", "cubemap 2x3", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_CUBEMAP23}, 0, 0, FLAGS, .unit = "projection" }, + { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_BARREL}, 0, 0, FLAGS, .unit = "projection" }, + { "barrelsplit", "barrel split facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64 = PROJECTION_BARREL_SPLIT}, 0, 0, FLAGS, .unit = "projection" }, { "main_projection", "projection of the main video", OFFSET(main_projection), AV_OPT_TYPE_INT, {.i64 = PROJECTION_N}, - 0, PROJECTION_N, .flags = FLAGS, "projection" }, + 0, PROJECTION_N, .flags = FLAGS, .unit = "projection" }, { "ref_stereo", "stereo format of the reference video", OFFSET(ref_stereo_format), AV_OPT_TYPE_INT, {.i64 = STEREO_FORMAT_MONO}, - 0, STEREO_FORMAT_N - 1, .flags = FLAGS, "stereo_format" }, + 0, STEREO_FORMAT_N - 1, .flags = FLAGS, .unit = "stereo_format" }, - { "mono", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = STEREO_FORMAT_MONO }, 0, 0, FLAGS, "stereo_format" }, - { "tb", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = STEREO_FORMAT_TB }, 0, 0, FLAGS, "stereo_format" }, - { "lr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = STEREO_FORMAT_LR }, 0, 0, FLAGS, "stereo_format" }, + { "mono", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = STEREO_FORMAT_MONO }, 0, 0, FLAGS, .unit = "stereo_format" }, + { "tb", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = STEREO_FORMAT_TB }, 0, 0, FLAGS, .unit = "stereo_format" }, + { "lr", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = STEREO_FORMAT_LR }, 0, 0, FLAGS, .unit = "stereo_format" }, { "main_stereo", "stereo format of main video", OFFSET(main_stereo_format), AV_OPT_TYPE_INT, {.i64 = STEREO_FORMAT_N}, - 0, STEREO_FORMAT_N, .flags = FLAGS, "stereo_format" }, + 0, STEREO_FORMAT_N, .flags = FLAGS, .unit = "stereo_format" }, { "ref_pad", "Expansion (padding) coefficient for each cube face of the reference video", @@ -1274,10 +1272,6 @@ static int parse_heatmaps(void *logctx, HeatmapList **proot, ret = AVERROR(ENOMEM); goto fail; } - if (!line) { - av_freep(&line); - break; - } // first value is frame id av_strtok(line, ",", &saveptr); @@ -1624,7 +1618,7 @@ static int config_output(AVFilterLink *outlink) memset(s->ssim360_percentile_sum, 0, sizeof(s->ssim360_percentile_sum)); for (int i = 0; i < s->nb_components; i++) { - s->ssim360_hist[i] = av_calloc(SSIM360_HIST_SIZE, sizeof(*s->ssim360_hist)); + FF_ALLOCZ_TYPED_ARRAY(s->ssim360_hist[i], SSIM360_HIST_SIZE); if (!s->ssim360_hist[i]) return AVERROR(ENOMEM); } diff --git a/libavfilter/vf_stack.c b/libavfilter/vf_stack.c index 331dc7b3e3a..2bb3d9b1d25 100644 --- a/libavfilter/vf_stack.c +++ b/libavfilter/vf_stack.c @@ -304,7 +304,8 @@ static int config_output(AVFilterLink *outlink) int inw, inh, size; if (s->fillcolor_enable) { - ff_draw_init(&s->draw, ctx->inputs[0]->format, 0); + const AVFilterLink *inlink = ctx->inputs[0]; + ff_draw_init2(&s->draw, inlink->format, inlink->colorspace, inlink->color_range, 0); ff_draw_color(&s->draw, &s->color, s->fillcolor); } diff --git a/libavfilter/vf_stack_qsv.c b/libavfilter/vf_stack_qsv.c index 9eb0748bd61..3e6aefe44bf 100644 --- a/libavfilter/vf_stack_qsv.c +++ b/libavfilter/vf_stack_qsv.c @@ -235,20 +235,20 @@ static int qsv_stack_query_formats(AVFilterContext *ctx) #if CONFIG_HSTACK_QSV_FILTER DEFINE_HSTACK_OPTIONS(qsv); -DEFINE_STACK_FILTER(hstack, qsv, "Quick Sync Video"); +DEFINE_STACK_FILTER(hstack, qsv, "Quick Sync Video", AVFILTER_FLAG_HWDEVICE); #endif #if CONFIG_VSTACK_QSV_FILTER DEFINE_VSTACK_OPTIONS(qsv); -DEFINE_STACK_FILTER(vstack, qsv, "Quick Sync Video"); +DEFINE_STACK_FILTER(vstack, qsv, "Quick Sync Video", AVFILTER_FLAG_HWDEVICE); #endif #if CONFIG_XSTACK_QSV_FILTER DEFINE_XSTACK_OPTIONS(qsv); -DEFINE_STACK_FILTER(xstack, qsv, "Quick Sync Video"); +DEFINE_STACK_FILTER(xstack, qsv, "Quick Sync Video", AVFILTER_FLAG_HWDEVICE); #endif diff --git a/libavfilter/vf_stack_vaapi.c b/libavfilter/vf_stack_vaapi.c index 26dbe3f7aa7..8e9471e6d7b 100644 --- a/libavfilter/vf_stack_vaapi.c +++ b/libavfilter/vf_stack_vaapi.c @@ -234,20 +234,20 @@ static int vaapi_stack_query_formats(AVFilterContext *avctx) #if CONFIG_HSTACK_VAAPI_FILTER DEFINE_HSTACK_OPTIONS(vaapi); -DEFINE_STACK_FILTER(hstack, vaapi, "VA-API"); +DEFINE_STACK_FILTER(hstack, vaapi, "VA-API", 0); #endif #if CONFIG_VSTACK_VAAPI_FILTER DEFINE_VSTACK_OPTIONS(vaapi); -DEFINE_STACK_FILTER(vstack, vaapi, "VA-API"); +DEFINE_STACK_FILTER(vstack, vaapi, "VA-API", 0); #endif #if CONFIG_XSTACK_VAAPI_FILTER DEFINE_XSTACK_OPTIONS(vaapi); -DEFINE_STACK_FILTER(xstack, vaapi, "VA-API"); +DEFINE_STACK_FILTER(xstack, vaapi, "VA-API", 0); #endif diff --git a/libavfilter/vf_stereo3d.c b/libavfilter/vf_stereo3d.c index 71041d2fee7..6bd4158f6dd 100644 --- a/libavfilter/vf_stereo3d.c +++ b/libavfilter/vf_stereo3d.c @@ -158,63 +158,63 @@ typedef struct Stereo3DContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption stereo3d_options[] = { - { "in", "set input format", OFFSET(in.format), AV_OPT_TYPE_INT, {.i64=SIDE_BY_SIDE_LR}, INTERLEAVE_ROWS_LR, STEREO_CODE_COUNT-1, FLAGS, "in"}, - { "ab2l", "above below half height left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_LR}, 0, 0, FLAGS, "in" }, - { "tb2l", "above below half height left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_LR}, 0, 0, FLAGS, "in" }, - { "ab2r", "above below half height right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_RL}, 0, 0, FLAGS, "in" }, - { "tb2r", "above below half height right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_RL}, 0, 0, FLAGS, "in" }, - { "abl", "above below left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_LR}, 0, 0, FLAGS, "in" }, - { "tbl", "above below left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_LR}, 0, 0, FLAGS, "in" }, - { "abr", "above below right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_RL}, 0, 0, FLAGS, "in" }, - { "tbr", "above below right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_RL}, 0, 0, FLAGS, "in" }, - { "al", "alternating frames left first", 0, AV_OPT_TYPE_CONST, {.i64=ALTERNATING_LR}, 0, 0, FLAGS, "in" }, - { "ar", "alternating frames right first", 0, AV_OPT_TYPE_CONST, {.i64=ALTERNATING_RL}, 0, 0, FLAGS, "in" }, - { "sbs2l", "side by side half width left first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_2_LR}, 0, 0, FLAGS, "in" }, - { "sbs2r", "side by side half width right first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_2_RL}, 0, 0, FLAGS, "in" }, - { "sbsl", "side by side left first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_LR}, 0, 0, FLAGS, "in" }, - { "sbsr", "side by side right first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_RL}, 0, 0, FLAGS, "in" }, - { "irl", "interleave rows left first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_ROWS_LR}, 0, 0, FLAGS, "in" }, - { "irr", "interleave rows right first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_ROWS_RL}, 0, 0, FLAGS, "in" }, - { "icl", "interleave columns left first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_COLS_LR}, 0, 0, FLAGS, "in" }, - { "icr", "interleave columns right first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_COLS_RL}, 0, 0, FLAGS, "in" }, - { "out", "set output format", OFFSET(out.format), AV_OPT_TYPE_INT, {.i64=ANAGLYPH_RC_DUBOIS}, 0, STEREO_CODE_COUNT-1, FLAGS, "out"}, - { "ab2l", "above below half height left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_LR}, 0, 0, FLAGS, "out" }, - { "tb2l", "above below half height left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_LR}, 0, 0, FLAGS, "out" }, - { "ab2r", "above below half height right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_RL}, 0, 0, FLAGS, "out" }, - { "tb2r", "above below half height right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_RL}, 0, 0, FLAGS, "out" }, - { "abl", "above below left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_LR}, 0, 0, FLAGS, "out" }, - { "tbl", "above below left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_LR}, 0, 0, FLAGS, "out" }, - { "abr", "above below right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_RL}, 0, 0, FLAGS, "out" }, - { "tbr", "above below right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_RL}, 0, 0, FLAGS, "out" }, - { "agmc", "anaglyph green magenta color", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_GM_COLOR}, 0, 0, FLAGS, "out" }, - { "agmd", "anaglyph green magenta dubois", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_GM_DUBOIS}, 0, 0, FLAGS, "out" }, - { "agmg", "anaglyph green magenta gray", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_GM_GRAY}, 0, 0, FLAGS, "out" }, - { "agmh", "anaglyph green magenta half color", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_GM_HALF}, 0, 0, FLAGS, "out" }, - { "al", "alternating frames left first", 0, AV_OPT_TYPE_CONST, {.i64=ALTERNATING_LR}, 0, 0, FLAGS, "out" }, - { "ar", "alternating frames right first", 0, AV_OPT_TYPE_CONST, {.i64=ALTERNATING_RL}, 0, 0, FLAGS, "out" }, - { "arbg", "anaglyph red blue gray", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_RB_GRAY}, 0, 0, FLAGS, "out" }, - { "arcc", "anaglyph red cyan color", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_RC_COLOR}, 0, 0, FLAGS, "out" }, - { "arcd", "anaglyph red cyan dubois", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_RC_DUBOIS}, 0, 0, FLAGS, "out" }, - { "arcg", "anaglyph red cyan gray", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_RC_GRAY}, 0, 0, FLAGS, "out" }, - { "arch", "anaglyph red cyan half color", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_RC_HALF}, 0, 0, FLAGS, "out" }, - { "argg", "anaglyph red green gray", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_RG_GRAY}, 0, 0, FLAGS, "out" }, - { "aybc", "anaglyph yellow blue color", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_YB_COLOR}, 0, 0, FLAGS, "out" }, - { "aybd", "anaglyph yellow blue dubois", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_YB_DUBOIS}, 0, 0, FLAGS, "out" }, - { "aybg", "anaglyph yellow blue gray", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_YB_GRAY}, 0, 0, FLAGS, "out" }, - { "aybh", "anaglyph yellow blue half color", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_YB_HALF}, 0, 0, FLAGS, "out" }, - { "irl", "interleave rows left first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_ROWS_LR}, 0, 0, FLAGS, "out" }, - { "irr", "interleave rows right first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_ROWS_RL}, 0, 0, FLAGS, "out" }, - { "ml", "mono left", 0, AV_OPT_TYPE_CONST, {.i64=MONO_L}, 0, 0, FLAGS, "out" }, - { "mr", "mono right", 0, AV_OPT_TYPE_CONST, {.i64=MONO_R}, 0, 0, FLAGS, "out" }, - { "sbs2l", "side by side half width left first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_2_LR}, 0, 0, FLAGS, "out" }, - { "sbs2r", "side by side half width right first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_2_RL}, 0, 0, FLAGS, "out" }, - { "sbsl", "side by side left first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_LR}, 0, 0, FLAGS, "out" }, - { "sbsr", "side by side right first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_RL}, 0, 0, FLAGS, "out" }, - { "chl", "checkerboard left first", 0, AV_OPT_TYPE_CONST, {.i64=CHECKERBOARD_LR}, 0, 0, FLAGS, "out" }, - { "chr", "checkerboard right first", 0, AV_OPT_TYPE_CONST, {.i64=CHECKERBOARD_RL}, 0, 0, FLAGS, "out" }, - { "icl", "interleave columns left first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_COLS_LR}, 0, 0, FLAGS, "out" }, - { "icr", "interleave columns right first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_COLS_RL}, 0, 0, FLAGS, "out" }, - { "hdmi", "HDMI frame pack", 0, AV_OPT_TYPE_CONST, {.i64=HDMI}, 0, 0, FLAGS, "out" }, + { "in", "set input format", OFFSET(in.format), AV_OPT_TYPE_INT, {.i64=SIDE_BY_SIDE_LR}, INTERLEAVE_ROWS_LR, STEREO_CODE_COUNT-1, FLAGS, .unit = "in"}, + { "ab2l", "above below half height left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_LR}, 0, 0, FLAGS, .unit = "in" }, + { "tb2l", "above below half height left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_LR}, 0, 0, FLAGS, .unit = "in" }, + { "ab2r", "above below half height right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_RL}, 0, 0, FLAGS, .unit = "in" }, + { "tb2r", "above below half height right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_RL}, 0, 0, FLAGS, .unit = "in" }, + { "abl", "above below left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_LR}, 0, 0, FLAGS, .unit = "in" }, + { "tbl", "above below left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_LR}, 0, 0, FLAGS, .unit = "in" }, + { "abr", "above below right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_RL}, 0, 0, FLAGS, .unit = "in" }, + { "tbr", "above below right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_RL}, 0, 0, FLAGS, .unit = "in" }, + { "al", "alternating frames left first", 0, AV_OPT_TYPE_CONST, {.i64=ALTERNATING_LR}, 0, 0, FLAGS, .unit = "in" }, + { "ar", "alternating frames right first", 0, AV_OPT_TYPE_CONST, {.i64=ALTERNATING_RL}, 0, 0, FLAGS, .unit = "in" }, + { "sbs2l", "side by side half width left first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_2_LR}, 0, 0, FLAGS, .unit = "in" }, + { "sbs2r", "side by side half width right first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_2_RL}, 0, 0, FLAGS, .unit = "in" }, + { "sbsl", "side by side left first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_LR}, 0, 0, FLAGS, .unit = "in" }, + { "sbsr", "side by side right first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_RL}, 0, 0, FLAGS, .unit = "in" }, + { "irl", "interleave rows left first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_ROWS_LR}, 0, 0, FLAGS, .unit = "in" }, + { "irr", "interleave rows right first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_ROWS_RL}, 0, 0, FLAGS, .unit = "in" }, + { "icl", "interleave columns left first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_COLS_LR}, 0, 0, FLAGS, .unit = "in" }, + { "icr", "interleave columns right first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_COLS_RL}, 0, 0, FLAGS, .unit = "in" }, + { "out", "set output format", OFFSET(out.format), AV_OPT_TYPE_INT, {.i64=ANAGLYPH_RC_DUBOIS}, 0, STEREO_CODE_COUNT-1, FLAGS, .unit = "out"}, + { "ab2l", "above below half height left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_LR}, 0, 0, FLAGS, .unit = "out" }, + { "tb2l", "above below half height left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_LR}, 0, 0, FLAGS, .unit = "out" }, + { "ab2r", "above below half height right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_RL}, 0, 0, FLAGS, .unit = "out" }, + { "tb2r", "above below half height right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_2_RL}, 0, 0, FLAGS, .unit = "out" }, + { "abl", "above below left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_LR}, 0, 0, FLAGS, .unit = "out" }, + { "tbl", "above below left first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_LR}, 0, 0, FLAGS, .unit = "out" }, + { "abr", "above below right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_RL}, 0, 0, FLAGS, .unit = "out" }, + { "tbr", "above below right first", 0, AV_OPT_TYPE_CONST, {.i64=ABOVE_BELOW_RL}, 0, 0, FLAGS, .unit = "out" }, + { "agmc", "anaglyph green magenta color", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_GM_COLOR}, 0, 0, FLAGS, .unit = "out" }, + { "agmd", "anaglyph green magenta dubois", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_GM_DUBOIS}, 0, 0, FLAGS, .unit = "out" }, + { "agmg", "anaglyph green magenta gray", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_GM_GRAY}, 0, 0, FLAGS, .unit = "out" }, + { "agmh", "anaglyph green magenta half color", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_GM_HALF}, 0, 0, FLAGS, .unit = "out" }, + { "al", "alternating frames left first", 0, AV_OPT_TYPE_CONST, {.i64=ALTERNATING_LR}, 0, 0, FLAGS, .unit = "out" }, + { "ar", "alternating frames right first", 0, AV_OPT_TYPE_CONST, {.i64=ALTERNATING_RL}, 0, 0, FLAGS, .unit = "out" }, + { "arbg", "anaglyph red blue gray", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_RB_GRAY}, 0, 0, FLAGS, .unit = "out" }, + { "arcc", "anaglyph red cyan color", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_RC_COLOR}, 0, 0, FLAGS, .unit = "out" }, + { "arcd", "anaglyph red cyan dubois", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_RC_DUBOIS}, 0, 0, FLAGS, .unit = "out" }, + { "arcg", "anaglyph red cyan gray", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_RC_GRAY}, 0, 0, FLAGS, .unit = "out" }, + { "arch", "anaglyph red cyan half color", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_RC_HALF}, 0, 0, FLAGS, .unit = "out" }, + { "argg", "anaglyph red green gray", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_RG_GRAY}, 0, 0, FLAGS, .unit = "out" }, + { "aybc", "anaglyph yellow blue color", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_YB_COLOR}, 0, 0, FLAGS, .unit = "out" }, + { "aybd", "anaglyph yellow blue dubois", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_YB_DUBOIS}, 0, 0, FLAGS, .unit = "out" }, + { "aybg", "anaglyph yellow blue gray", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_YB_GRAY}, 0, 0, FLAGS, .unit = "out" }, + { "aybh", "anaglyph yellow blue half color", 0, AV_OPT_TYPE_CONST, {.i64=ANAGLYPH_YB_HALF}, 0, 0, FLAGS, .unit = "out" }, + { "irl", "interleave rows left first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_ROWS_LR}, 0, 0, FLAGS, .unit = "out" }, + { "irr", "interleave rows right first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_ROWS_RL}, 0, 0, FLAGS, .unit = "out" }, + { "ml", "mono left", 0, AV_OPT_TYPE_CONST, {.i64=MONO_L}, 0, 0, FLAGS, .unit = "out" }, + { "mr", "mono right", 0, AV_OPT_TYPE_CONST, {.i64=MONO_R}, 0, 0, FLAGS, .unit = "out" }, + { "sbs2l", "side by side half width left first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_2_LR}, 0, 0, FLAGS, .unit = "out" }, + { "sbs2r", "side by side half width right first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_2_RL}, 0, 0, FLAGS, .unit = "out" }, + { "sbsl", "side by side left first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_LR}, 0, 0, FLAGS, .unit = "out" }, + { "sbsr", "side by side right first", 0, AV_OPT_TYPE_CONST, {.i64=SIDE_BY_SIDE_RL}, 0, 0, FLAGS, .unit = "out" }, + { "chl", "checkerboard left first", 0, AV_OPT_TYPE_CONST, {.i64=CHECKERBOARD_LR}, 0, 0, FLAGS, .unit = "out" }, + { "chr", "checkerboard right first", 0, AV_OPT_TYPE_CONST, {.i64=CHECKERBOARD_RL}, 0, 0, FLAGS, .unit = "out" }, + { "icl", "interleave columns left first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_COLS_LR}, 0, 0, FLAGS, .unit = "out" }, + { "icr", "interleave columns right first", 0, AV_OPT_TYPE_CONST, {.i64=INTERLEAVE_COLS_RL}, 0, 0, FLAGS, .unit = "out" }, + { "hdmi", "HDMI frame pack", 0, AV_OPT_TYPE_CONST, {.i64=HDMI}, 0, 0, FLAGS, .unit = "out" }, { NULL } }; diff --git a/libavfilter/vf_subtitles.c b/libavfilter/vf_subtitles.c index 82e140e9863..a6338f3daf2 100644 --- a/libavfilter/vf_subtitles.c +++ b/libavfilter/vf_subtitles.c @@ -33,6 +33,7 @@ #include "config_components.h" #if CONFIG_SUBTITLES_FILTER # include "libavcodec/avcodec.h" +# include "libavcodec/codec_desc.h" # include "libavformat/avformat.h" #endif #include "libavutil/avstring.h" @@ -45,6 +46,8 @@ #include "formats.h" #include "video.h" +#define FF_ASS_FEATURE_WRAP_UNICODE (LIBASS_VERSION >= 0x01600010) + typedef struct AssContext { const AVClass *class; ASS_Library *library; @@ -61,6 +64,7 @@ typedef struct AssContext { int original_w, original_h; int shaping; FFDrawContext draw; + int wrap_unicode; } AssContext; #define OFFSET(x) offsetof(AssContext, x) @@ -144,7 +148,8 @@ static int config_input(AVFilterLink *inlink) { AssContext *ass = inlink->dst->priv; - ff_draw_init(&ass->draw, inlink->format, ass->alpha ? FF_DRAW_PROCESS_ALPHA : 0); + ff_draw_init2(&ass->draw, inlink->format, inlink->colorspace, inlink->color_range, + ass->alpha ? FF_DRAW_PROCESS_ALPHA : 0); ass_set_frame_size (ass->renderer, inlink->w, inlink->h); if (ass->original_w && ass->original_h) { @@ -209,21 +214,14 @@ static const AVFilterPad ass_inputs[] = { }, }; -static const AVFilterPad ass_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #if CONFIG_ASS_FILTER static const AVOption ass_options[] = { COMMON_OPTIONS - {"shaping", "set shaping engine", OFFSET(shaping), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, FLAGS, "shaping_mode"}, - {"auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, INT_MIN, INT_MAX, FLAGS, "shaping_mode"}, - {"simple", "simple shaping", 0, AV_OPT_TYPE_CONST, {.i64 = ASS_SHAPING_SIMPLE}, INT_MIN, INT_MAX, FLAGS, "shaping_mode"}, - {"complex", "complex shaping", 0, AV_OPT_TYPE_CONST, {.i64 = ASS_SHAPING_COMPLEX}, INT_MIN, INT_MAX, FLAGS, "shaping_mode"}, + {"shaping", "set shaping engine", OFFSET(shaping), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 1, FLAGS, .unit = "shaping_mode"}, + {"auto", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, INT_MIN, INT_MAX, FLAGS, .unit = "shaping_mode"}, + {"simple", "simple shaping", 0, AV_OPT_TYPE_CONST, {.i64 = ASS_SHAPING_SIMPLE}, INT_MIN, INT_MAX, FLAGS, .unit = "shaping_mode"}, + {"complex", "complex shaping", 0, AV_OPT_TYPE_CONST, {.i64 = ASS_SHAPING_COMPLEX}, INT_MIN, INT_MAX, FLAGS, .unit = "shaping_mode"}, {NULL}, }; @@ -257,7 +255,7 @@ const AVFilter ff_vf_ass = { .init = init_ass, .uninit = uninit, FILTER_INPUTS(ass_inputs), - FILTER_OUTPUTS(ass_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .priv_class = &ass_class, }; @@ -271,6 +269,9 @@ static const AVOption subtitles_options[] = { {"stream_index", "set stream index", OFFSET(stream_index), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS}, {"si", "set stream index", OFFSET(stream_index), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS}, {"force_style", "force subtitle style", OFFSET(force_style), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS}, +#if FF_ASS_FEATURE_WRAP_UNICODE + {"wrap_unicode", "break lines according to the Unicode Line Breaking Algorithm", OFFSET(wrap_unicode), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, FLAGS }, +#endif {NULL}, }; @@ -432,6 +433,18 @@ static av_cold int init_subtitles(AVFilterContext *ctx) if (ret < 0) goto end; +#if FF_ASS_FEATURE_WRAP_UNICODE + /* Don't overwrite wrap automatically for native ASS */ + if (ass->wrap_unicode == -1) + ass->wrap_unicode = st->codecpar->codec_id != AV_CODEC_ID_ASS; + if (ass->wrap_unicode) { + ret = ass_track_set_feature(ass->track, ASS_FEATURE_WRAP_UNICODE, 1); + if (ret < 0) + av_log(ctx, AV_LOG_WARNING, + "libass wasn't built with ASS_FEATURE_WRAP_UNICODE support\n"); + } +#endif + if (ass->force_style) { char **list = NULL; char *temp = NULL; @@ -497,7 +510,7 @@ const AVFilter ff_vf_subtitles = { .init = init_subtitles, .uninit = uninit, FILTER_INPUTS(ass_inputs), - FILTER_OUTPUTS(ass_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .priv_class = &subtitles_class, }; diff --git a/libavfilter/vf_super2xsai.c b/libavfilter/vf_super2xsai.c index d932e2ba4b4..65144cbf1b0 100644 --- a/libavfilter/vf_super2xsai.c +++ b/libavfilter/vf_super2xsai.c @@ -29,7 +29,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/intreadwrite.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_swaprect.c b/libavfilter/vf_swaprect.c index b76e3bb99dc..119fd692fa7 100644 --- a/libavfilter/vf_swaprect.c +++ b/libavfilter/vf_swaprect.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/eval.h" #include "libavutil/imgutils.h" @@ -64,8 +65,16 @@ static int query_formats(AVFilterContext *ctx) return ff_set_common_formats(ctx, ff_formats_pixdesc_filter(0, reject_flags)); } -static const char *const var_names[] = { "w", "h", "a", "n", "t", "pos", "sar", "dar", NULL }; -enum { VAR_W, VAR_H, VAR_A, VAR_N, VAR_T, VAR_POS, VAR_SAR, VAR_DAR, VAR_VARS_NB }; +static const char *const var_names[] = { "w", "h", "a", "n", "t", +#if FF_API_FRAME_PKT + "pos", +#endif + "sar", "dar", NULL }; +enum { VAR_W, VAR_H, VAR_A, VAR_N, VAR_T, +#if FF_API_FRAME_PKT + VAR_POS, +#endif + VAR_SAR, VAR_DAR, VAR_VARS_NB }; static int filter_frame(AVFilterLink *inlink, AVFrame *in) { @@ -90,7 +99,11 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) var_values[VAR_DAR] = var_values[VAR_A] * var_values[VAR_SAR]; var_values[VAR_N] = inlink->frame_count_out; var_values[VAR_T] = in->pts == AV_NOPTS_VALUE ? NAN : in->pts * av_q2d(inlink->time_base); +#if FF_API_FRAME_PKT +FF_DISABLE_DEPRECATION_WARNINGS var_values[VAR_POS] = in->pkt_pos == -1 ? NAN : in->pkt_pos; +FF_ENABLE_DEPRECATION_WARNINGS +#endif ret = av_expr_parse_and_eval(&dw, s->w, var_names, &var_values[0], @@ -137,10 +150,10 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) w = dw; h = dh; x1[0] = dx1; y1[0] = dy1; x2[0] = dx2; y2[0] = dy2; x1[0] = av_clip(x1[0], 0, inlink->w - 1); - y1[0] = av_clip(y1[0], 0, inlink->w - 1); + y1[0] = av_clip(y1[0], 0, inlink->h - 1); x2[0] = av_clip(x2[0], 0, inlink->w - 1); - y2[0] = av_clip(y2[0], 0, inlink->w - 1); + y2[0] = av_clip(y2[0], 0, inlink->h - 1); ah[1] = ah[2] = AV_CEIL_RSHIFT(h, s->desc->log2_chroma_h); ah[0] = ah[3] = h; @@ -160,16 +173,20 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) lw[1] = lw[2] = AV_CEIL_RSHIFT(inlink->w, s->desc->log2_chroma_w); lw[0] = lw[3] = inlink->w; - x1[1] = x1[2] = AV_CEIL_RSHIFT(x1[0], s->desc->log2_chroma_w); + x1[1] = x1[2] = (x1[0] >> s->desc->log2_chroma_w); x1[0] = x1[3] = x1[0]; - y1[1] = y1[2] = AV_CEIL_RSHIFT(y1[0], s->desc->log2_chroma_h); + y1[1] = y1[2] = (y1[0] >> s->desc->log2_chroma_h); y1[0] = y1[3] = y1[0]; - x2[1] = x2[2] = AV_CEIL_RSHIFT(x2[0], s->desc->log2_chroma_w); + x2[1] = x2[2] = (x2[0] >> s->desc->log2_chroma_w); x2[0] = x2[3] = x2[0]; - y2[1] = y2[2] = AV_CEIL_RSHIFT(y2[0], s->desc->log2_chroma_h); + y2[1] = y2[2] = (y2[0] >> s->desc->log2_chroma_h); y2[0] = y2[3] = y2[0]; + + av_assert0(FFMAX(x1[1], x2[1]) + pw[1] <= lw[1]); + av_assert0(FFMAX(y1[1], y2[1]) + ph[1] <= lh[1]); + for (p = 0; p < s->nb_planes; p++) { if (ph[p] == ah[p] && pw[p] == aw[p]) { uint8_t *src = in->data[p] + y1[p] * in->linesize[p] + x1[p] * s->pixsteps[p]; @@ -225,13 +242,6 @@ static const AVFilterPad inputs[] = { }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_swaprect = { .name = "swaprect", .description = NULL_IF_CONFIG_SMALL("Swap 2 rectangular objects in video."), @@ -239,7 +249,7 @@ const AVFilter ff_vf_swaprect = { .priv_class = &swaprect_class, .uninit = uninit, FILTER_INPUTS(inputs), - FILTER_OUTPUTS(outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_swapuv.c b/libavfilter/vf_swapuv.c index 44520282621..df04631d200 100644 --- a/libavfilter/vf_swapuv.c +++ b/libavfilter/vf_swapuv.c @@ -101,20 +101,13 @@ static const AVFilterPad swapuv_inputs[] = { }, }; -static const AVFilterPad swapuv_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_swapuv = { .name = "swapuv", .description = NULL_IF_CONFIG_SMALL("Swap U and V components."), .priv_size = sizeof(SwapUVContext), .priv_class = &swapuv_class, FILTER_INPUTS(swapuv_inputs), - FILTER_OUTPUTS(swapuv_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_telecine.c b/libavfilter/vf_telecine.c index 227de6f733c..44ef2b74d99 100644 --- a/libavfilter/vf_telecine.c +++ b/libavfilter/vf_telecine.c @@ -58,11 +58,11 @@ typedef struct TelecineContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption telecine_options[] = { - {"first_field", "select first field", OFFSET(first_field), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "field"}, - {"top", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"}, - {"t", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"}, - {"bottom", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"}, - {"b", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"}, + {"first_field", "select first field", OFFSET(first_field), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "field"}, + {"top", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "field"}, + {"t", "select top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "field"}, + {"bottom", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "field"}, + {"b", "select bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "field"}, {"pattern", "pattern that describe for how many fields a frame is to be displayed", OFFSET(pattern), AV_OPT_TYPE_STRING, {.str="23"}, 0, 0, FLAGS}, {NULL} }; @@ -204,8 +204,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) s->stride[i], (s->planeheight[i] - !s->first_field + 1) / 2); } +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS s->frame[nout]->interlaced_frame = 1; s->frame[nout]->top_field_first = !s->first_field; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + s->frame[nout]->flags |= AV_FRAME_FLAG_INTERLACED; + if (s->first_field) + s->frame[nout]->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + s->frame[nout]->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; nout++; len--; s->occupied = 0; @@ -223,8 +232,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) inpicref->data[i], inpicref->linesize[i], s->stride[i], s->planeheight[i]); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS s->frame[nout]->interlaced_frame = inpicref->interlaced_frame; s->frame[nout]->top_field_first = inpicref->top_field_first; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + s->frame[nout]->flags |= (inpicref->flags & (AV_FRAME_FLAG_INTERLACED | AV_FRAME_FLAG_TOP_FIELD_FIRST)); nout++; len -= 2; } @@ -241,8 +255,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) for (i = 0; i < nout; i++) { AVFrame *frame = av_frame_clone(s->frame[i]); - int interlaced = frame ? frame->interlaced_frame : 0; - int tff = frame ? frame->top_field_first : 0; + int interlaced = frame ? !!(frame->flags & AV_FRAME_FLAG_INTERLACED) : 0; + int tff = frame ? !!(frame->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 0; if (!frame) { av_frame_free(&inpicref); @@ -250,8 +264,20 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *inpicref) } av_frame_copy_props(frame, inpicref); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS frame->interlaced_frame = interlaced; frame->top_field_first = tff; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + if (interlaced) + frame->flags |= AV_FRAME_FLAG_INTERLACED; + else + frame->flags &= ~AV_FRAME_FLAG_INTERLACED; + if (tff) + frame->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + frame->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; frame->pts = ((s->start_time == AV_NOPTS_VALUE) ? 0 : s->start_time) + av_rescale(outlink->frame_count_in, s->ts_unit.num, s->ts_unit.den); diff --git a/libavfilter/vf_thumbnail.c b/libavfilter/vf_thumbnail.c index 4d6b0aef04a..0b335c2a71a 100644 --- a/libavfilter/vf_thumbnail.c +++ b/libavfilter/vf_thumbnail.c @@ -59,10 +59,10 @@ typedef struct ThumbContext { static const AVOption thumbnail_options[] = { { "n", "set the frames batch size", OFFSET(n_frames), AV_OPT_TYPE_INT, {.i64=100}, 2, INT_MAX, FLAGS }, - { "log", "force stats logging level", OFFSET(loglevel), AV_OPT_TYPE_INT, {.i64 = AV_LOG_INFO}, INT_MIN, INT_MAX, FLAGS, "level" }, - { "quiet", "logging disabled", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_QUIET}, 0, 0, FLAGS, "level" }, - { "info", "information logging level", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_INFO}, 0, 0, FLAGS, "level" }, - { "verbose", "verbose logging level", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_VERBOSE}, 0, 0, FLAGS, "level" }, + { "log", "force stats logging level", OFFSET(loglevel), AV_OPT_TYPE_INT, {.i64 = AV_LOG_INFO}, INT_MIN, INT_MAX, FLAGS, .unit = "level" }, + { "quiet", "logging disabled", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_QUIET}, 0, 0, FLAGS, .unit = "level" }, + { "info", "information logging level", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_INFO}, 0, 0, FLAGS, .unit = "level" }, + { "verbose", "verbose logging level", 0, AV_OPT_TYPE_CONST, {.i64 = AV_LOG_VERBOSE}, 0, 0, FLAGS, .unit = "level" }, { NULL } }; diff --git a/libavfilter/vf_thumbnail_cuda.c b/libavfilter/vf_thumbnail_cuda.c index c8dd9051237..40a3b75dd06 100644 --- a/libavfilter/vf_thumbnail_cuda.c +++ b/libavfilter/vf_thumbnail_cuda.c @@ -290,7 +290,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) hist[i] = 4 * hist[i]; } - CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + ret = CHECK_CU(cu->cuCtxPopCurrent(&dummy)); if (ret < 0) return ret; diff --git a/libavfilter/vf_tile.c b/libavfilter/vf_tile.c index ca2397fb181..b45e739bb62 100644 --- a/libavfilter/vf_tile.c +++ b/libavfilter/vf_tile.c @@ -139,7 +139,7 @@ static int config_props(AVFilterLink *outlink) outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; outlink->frame_rate = av_mul_q(inlink->frame_rate, av_make_q(1, tile->nb_frames - tile->overlap)); - ff_draw_init(&tile->draw, inlink->format, 0); + ff_draw_init2(&tile->draw, inlink->format, inlink->colorspace, inlink->color_range, 0); ff_draw_color(&tile->draw, &tile->blank, tile->rgba_color); return 0; diff --git a/libavfilter/vf_tiltandshift.c b/libavfilter/vf_tiltandshift.c new file mode 100644 index 00000000000..0149cd44d53 --- /dev/null +++ b/libavfilter/vf_tiltandshift.c @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2014 Vittorio Giovara + * + * 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 vf_tiltandshift.c + * Simple time and space inverter. + */ + +#include + +#include "libavutil/common.h" +#include "libavutil/imgutils.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "libavutil/rational.h" + +#include "avfilter.h" +#include "formats.h" +#include "internal.h" +#include "video.h" + +enum PaddingOption { + TILT_NONE, + TILT_FRAME, + TILT_BLACK, + TILT_OPT_MAX, +}; + +typedef struct TiltandshiftContext { + const AVClass *class; + + /* set when all input frames have been processed and we have to + * empty buffers, pad and then return */ + int eof_recv; + + /* live or static sliding */ + int tilt; + + /* initial or final actions to perform (pad/hold a frame/black/nothing) */ + enum PaddingOption start; + enum PaddingOption end; + + /* columns to hold or pad at the beginning or at the end (respectively) */ + int hold; + int pad; + + /* buffers for black columns */ + uint8_t *black_buffers[4]; + int black_linesizes[4]; + + /* list containing all input frames */ + size_t input_size; + AVFrame *input; + AVFrame *prev; + + const AVPixFmtDescriptor *desc; +} TiltandshiftContext; + +static int list_add_frame(TiltandshiftContext *s, AVFrame *frame) +{ + if (s->input == NULL) { + s->input = frame; + } else { + AVFrame *head = s->input; + while (head->opaque) + head = head->opaque; + head->opaque = frame; + } + s->input_size++; + return 0; +} + +static void list_remove_head(TiltandshiftContext *s) +{ + AVFrame *head = s->input; + if (head) { + s->input = head->opaque; + av_frame_free(&head); + } + s->input_size--; +} + +static const enum AVPixelFormat pix_fmts[] = { + AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_YUV410P, + AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, + AV_PIX_FMT_YUVJ440P, + AV_PIX_FMT_NONE +}; + +static av_cold void uninit(AVFilterContext *ctx) +{ + TiltandshiftContext *s = ctx->priv; + while (s->input) + list_remove_head(s); + av_freep(&s->black_buffers); +} + +static int config_props(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + TiltandshiftContext *s = ctx->priv; + + outlink->w = ctx->inputs[0]->w; + outlink->h = ctx->inputs[0]->h; + outlink->format = ctx->inputs[0]->format; + + // when we have to pad black or a frame at the start, skip navigating + // the list and use either the frame or black for the requested value + if (s->start != TILT_NONE && !s->hold) + s->hold = outlink->w; + + // Init black buffers if we pad with black at the start or at the end. + // For the end, we always have to init on NONE and BLACK because we never + // know if there are going to be enough input frames to fill an output one. + if (s->start == TILT_BLACK || s->end != TILT_FRAME) { + int i, j, ret; + uint8_t black_data[] = { 0x10, 0x80, 0x80, 0x10 }; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); + if (!desc) + return AVERROR_BUG; + + if (outlink->format == AV_PIX_FMT_YUVJ420P || + outlink->format == AV_PIX_FMT_YUVJ422P || + outlink->format == AV_PIX_FMT_YUVJ444P || + outlink->format == AV_PIX_FMT_YUVJ440P || + outlink->color_range == AVCOL_RANGE_JPEG) + black_data[0] = black_data[3] = 0; + + ret = av_image_alloc(s->black_buffers, s->black_linesizes, 1, + outlink->h, outlink->format, 1); + if (ret < 0) + return ret; + + for (i = 0; i < FFMIN(desc->nb_components, 4); i++) + for (j = 0; j < (!i ? outlink->h + : -((-outlink->h) >> desc->log2_chroma_h)); j++) + memset(s->black_buffers[i] + j * s->black_linesizes[i], + black_data[i], 1); + + av_log(ctx, AV_LOG_VERBOSE, "Padding buffers initialized.\n"); + } + + s->desc = av_pix_fmt_desc_get(outlink->format); + if (!s->desc) + return AVERROR_BUG; + + return 0; +} + + +static void copy_column(AVFilterLink *outlink, + uint8_t *dst_data[4], int dst_linesizes[4], + const uint8_t *src_data[4], const int src_linesizes[4], + int ncol, int tilt) +{ + AVFilterContext *ctx = outlink->src; + TiltandshiftContext *s = ctx->priv; + uint8_t *dst[4]; + const uint8_t *src[4]; + + dst[0] = dst_data[0] + ncol; + dst[1] = dst_data[1] + (ncol >> s->desc->log2_chroma_w); + dst[2] = dst_data[2] + (ncol >> s->desc->log2_chroma_w); + + if (!tilt) + ncol = 0; + src[0] = src_data[0] + ncol; + src[1] = src_data[1] + (ncol >> s->desc->log2_chroma_w); + src[2] = src_data[2] + (ncol >> s->desc->log2_chroma_w); + + av_image_copy(dst, dst_linesizes, src, src_linesizes, outlink->format, 1, outlink->h); +} + +static int output_frame(AVFilterLink *outlink) +{ + TiltandshiftContext *s = outlink->src->priv; + AVFrame *head; + int ret; + + int ncol = 0; + AVFrame *dst = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!dst) + return AVERROR(ENOMEM); + + // in case we have to do any initial black padding + if (s->start == TILT_BLACK) { + for ( ; ncol < s->hold; ncol++) + copy_column(outlink, dst->data, dst->linesize, + (const uint8_t **)s->black_buffers, s->black_linesizes, + ncol, 0); + } + + head = s->input; + // copy a column from each input frame + for ( ; ncol < s->input_size; ncol++) { + AVFrame *src = head; + + copy_column(outlink, dst->data, dst->linesize, + (const uint8_t **)src->data, src->linesize, + ncol, s->tilt); + + // keep track of the last known frame in case we need it below + s->prev = head; + // advance to the next frame unless we have to hold it + if (s->hold <= ncol) + head = head->opaque; + } + + // pad any remaining space with black or last frame + if (s->end == TILT_FRAME) { + for ( ; ncol < outlink->w; ncol++) + copy_column(outlink, dst->data, dst->linesize, + (const uint8_t **)s->prev->data, + s->prev->linesize, ncol, 1); + } else { // TILT_BLACK and TILT_NONE + for ( ; ncol < outlink->w; ncol++) + copy_column(outlink, dst->data, dst->linesize, + (const uint8_t **)s->black_buffers, s->black_linesizes, + ncol, 0); + } + + // set correct timestamps and props as long as there is proper input + ret = av_frame_copy_props(dst, s->input); + if (ret < 0) { + av_frame_free(&dst); + return ret; + } + + // discard frame at the top of the list since it has been fully processed + list_remove_head(s); + // and it is safe to reduce the hold value (even if unused) + s->hold--; + + // output + return ff_filter_frame(outlink, dst); +} + +// This function just polls for new frames and queues them on a list +static int filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterLink *outlink = inlink->dst->outputs[0]; + AVFilterContext *ctx = outlink->src; + TiltandshiftContext *s = inlink->dst->priv; + + int ret = list_add_frame(s, frame); + if (ret < 0) { + return ret; + } + + // load up enough frames to fill a frame and keep the queue filled on subsequent + // calls, until we receive EOF, and then we either pad or end + if (!s->eof_recv && s->input_size < outlink->w - s->pad) { + av_log(ctx, AV_LOG_DEBUG, "Not enough frames in the list (%zu/%d), waiting for more.\n", s->input_size, outlink->w - s->pad); + return 0; + } + + return output_frame(outlink); +} + +static int request_frame(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + TiltandshiftContext *s = ctx->priv; + int ret; + + // signal job finished when list is empty or when padding is either + // limited or disabled and eof was received + if ((s->input_size <= 0 || s->input_size == outlink->w - s->pad || s->end == TILT_NONE) && s->eof_recv) { + return AVERROR_EOF; + } + + ret = ff_request_frame(ctx->inputs[0]); + if (ret == AVERROR_EOF) { + s->eof_recv = 1; + } else if (ret < 0) { + return ret; + } + + if (s->eof_recv) { + while (s->input_size) { + av_log(ctx, AV_LOG_DEBUG, "Emptying buffers (%zu/%d).\n", s->input_size, outlink->w - s->pad); + ret = output_frame(outlink); + if (ret < 0) { + return ret; + } + } + } + + return 0; +} + +#define OFFSET(x) offsetof(TiltandshiftContext, x) +#define V AV_OPT_FLAG_VIDEO_PARAM +static const AVOption tiltandshift_options[] = { + { "tilt", "Tilt the video horizontally while shifting", OFFSET(tilt), AV_OPT_TYPE_INT, + { .i64 = 1 }, 0, 1, .flags = V, .unit = "tilt" }, + + { "start", "Action at the start of input", OFFSET(start), AV_OPT_TYPE_INT, + { .i64 = TILT_NONE }, 0, TILT_OPT_MAX, .flags = V, .unit = "start" }, + { "none", "Start immediately (default)", 0, AV_OPT_TYPE_CONST, + { .i64 = TILT_NONE }, INT_MIN, INT_MAX, .flags = V, .unit = "start" }, + { "frame", "Use the first frames", 0, AV_OPT_TYPE_CONST, + { .i64 = TILT_FRAME }, INT_MIN, INT_MAX, .flags = V, .unit = "start" }, + { "black", "Fill with black", 0, AV_OPT_TYPE_CONST, + { .i64 = TILT_BLACK }, INT_MIN, INT_MAX, .flags = V, .unit = "start" }, + + { "end", "Action at the end of input", OFFSET(end), AV_OPT_TYPE_INT, + { .i64 = TILT_NONE }, 0, TILT_OPT_MAX, .flags = V, .unit = "end" }, + { "none", "Do not pad at the end (default)", 0, AV_OPT_TYPE_CONST, + { .i64 = TILT_NONE }, INT_MIN, INT_MAX, .flags = V, .unit = "end" }, + { "frame", "Use the last frame", 0, AV_OPT_TYPE_CONST, + { .i64 = TILT_FRAME }, INT_MIN, INT_MAX, .flags = V, .unit = "end" }, + { "black", "Fill with black", 0, AV_OPT_TYPE_CONST, + { .i64 = TILT_BLACK }, INT_MIN, INT_MAX, .flags = V, .unit = "end" }, + + { "hold", "Number of columns to hold at the start of the video", OFFSET(hold), AV_OPT_TYPE_INT, + { .i64 = 0 }, 0, INT_MAX, .flags = V, .unit = "hold" }, + { "pad", "Number of columns to pad at the end of the video", OFFSET(pad), AV_OPT_TYPE_INT, + { .i64 = 0 }, 0, INT_MAX, .flags = V, .unit = "pad" }, + + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(tiltandshift); + +static const AVFilterPad tiltandshift_inputs[] = { + { + .name = "in", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = filter_frame, + }, +}; + +static const AVFilterPad tiltandshift_outputs[] = { + { + .name = "out", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_props, + .request_frame = request_frame, + }, +}; + +const AVFilter ff_vf_tiltandshift = { + .name = "tiltandshift", + .description = NULL_IF_CONFIG_SMALL("Generate a tilt-and-shift'd video."), + .priv_size = sizeof(TiltandshiftContext), + .priv_class = &tiltandshift_class, + .uninit = uninit, + FILTER_INPUTS(tiltandshift_inputs), + FILTER_OUTPUTS(tiltandshift_outputs), + FILTER_PIXFMTS_ARRAY(pix_fmts), +}; diff --git a/libavfilter/vf_tinterlace.c b/libavfilter/vf_tinterlace.c index 032629279ad..c916e583753 100644 --- a/libavfilter/vf_tinterlace.c +++ b/libavfilter/vf_tinterlace.c @@ -32,28 +32,29 @@ #include "avfilter.h" #include "internal.h" #include "tinterlace.h" +#include "video.h" #define OFFSET(x) offsetof(TInterlaceContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption tinterlace_options[] = { - {"mode", "select interlace mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_MERGE}, 0, MODE_NB-1, FLAGS, "mode"}, - {"merge", "merge fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE}, INT_MIN, INT_MAX, FLAGS, "mode"}, - {"drop_even", "drop even fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_EVEN}, INT_MIN, INT_MAX, FLAGS, "mode"}, - {"drop_odd", "drop odd fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_ODD}, INT_MIN, INT_MAX, FLAGS, "mode"}, - {"pad", "pad alternate lines with black", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PAD}, INT_MIN, INT_MAX, FLAGS, "mode"}, - {"interleave_top", "interleave top and bottom fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_TOP}, INT_MIN, INT_MAX, FLAGS, "mode"}, - {"interleave_bottom", "interleave bottom and top fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_BOTTOM}, INT_MIN, INT_MAX, FLAGS, "mode"}, - {"interlacex2", "interlace fields from two consecutive frames", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLACEX2}, INT_MIN, INT_MAX, FLAGS, "mode"}, - {"mergex2", "merge fields keeping same frame rate", 0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGEX2}, INT_MIN, INT_MAX, FLAGS, "mode"}, - - {"flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, INT_MAX, 0, "flags" }, - {"low_pass_filter", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags" }, - {"vlpf", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, "flags" }, - {"complex_filter", "enable complex vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, "flags" }, - {"cvlpf", "enable complex vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, "flags" }, - {"exact_tb", "force a timebase which can represent timestamps exactly", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_EXACT_TB}, INT_MIN, INT_MAX, FLAGS, "flags" }, - {"bypass_il", "bypass already interlaced frames", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_BYPASS_IL}, INT_MIN, INT_MAX, FLAGS, "flags" }, + {"mode", "select interlace mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=MODE_MERGE}, 0, MODE_NB-1, FLAGS, .unit = "mode"}, + {"merge", "merge fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGE}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + {"drop_even", "drop even fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_EVEN}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + {"drop_odd", "drop odd fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_DROP_ODD}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + {"pad", "pad alternate lines with black", 0, AV_OPT_TYPE_CONST, {.i64=MODE_PAD}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + {"interleave_top", "interleave top and bottom fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_TOP}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + {"interleave_bottom", "interleave bottom and top fields", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLEAVE_BOTTOM}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + {"interlacex2", "interlace fields from two consecutive frames", 0, AV_OPT_TYPE_CONST, {.i64=MODE_INTERLACEX2}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + {"mergex2", "merge fields keeping same frame rate", 0, AV_OPT_TYPE_CONST, {.i64=MODE_MERGEX2}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + + {"flags", "set flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, 0, INT_MAX, 0, .unit = "flags" }, + {"low_pass_filter", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, .unit = "flags" }, + {"vlpf", "enable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_VLPF}, INT_MIN, INT_MAX, FLAGS, .unit = "flags" }, + {"complex_filter", "enable complex vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, .unit = "flags" }, + {"cvlpf", "enable complex vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_CVLPF},INT_MIN, INT_MAX, FLAGS, .unit = "flags" }, + {"exact_tb", "force a timebase which can represent timestamps exactly", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_EXACT_TB}, INT_MIN, INT_MAX, FLAGS, .unit = "flags" }, + {"bypass_il", "bypass already interlaced frames", 0, AV_OPT_TYPE_CONST, {.i64 = TINTERLACE_FLAG_BYPASS_IL}, INT_MIN, INT_MAX, FLAGS, .unit = "flags" }, {NULL} }; @@ -61,13 +62,13 @@ static const AVOption tinterlace_options[] = { AVFILTER_DEFINE_CLASS(tinterlace); static const AVOption interlace_options[] = { - { "scan", "scanning mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_TFF}, 0, 1, FLAGS, "mode"}, + { "scan", "scanning mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64 = MODE_TFF}, 0, 1, FLAGS, .unit = "mode"}, { "tff", "top field first", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_TFF}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, { "bff", "bottom field first", 0, AV_OPT_TYPE_CONST, {.i64 = MODE_BFF}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, - { "lowpass", "set vertical low-pass filter", OFFSET(lowpass), AV_OPT_TYPE_INT, {.i64 = VLPF_LIN}, 0, 2, FLAGS, "lowpass" }, - { "off", "disable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = VLPF_OFF}, INT_MIN, INT_MAX, FLAGS, "lowpass" }, - { "linear", "linear vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = VLPF_LIN}, INT_MIN, INT_MAX, FLAGS, "lowpass" }, - { "complex", "complex vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = VLPF_CMP}, INT_MIN, INT_MAX, FLAGS, "lowpass" }, + { "lowpass", "set vertical low-pass filter", OFFSET(lowpass), AV_OPT_TYPE_INT, {.i64 = VLPF_LIN}, 0, 2, FLAGS, .unit = "lowpass" }, + { "off", "disable vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = VLPF_OFF}, INT_MIN, INT_MAX, FLAGS, .unit = "lowpass" }, + { "linear", "linear vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = VLPF_LIN}, INT_MIN, INT_MAX, FLAGS, .unit = "lowpass" }, + { "complex", "complex vertical low-pass filter", 0, AV_OPT_TYPE_CONST, {.i64 = VLPF_CMP}, INT_MIN, INT_MAX, FLAGS, .unit = "lowpass" }, { NULL } }; @@ -203,6 +204,7 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&tinterlace->next); av_freep(&tinterlace->black_data[0][0]); av_freep(&tinterlace->black_data[1][0]); + ff_ccfifo_uninit(&tinterlace->cc_fifo); } static int config_out_props(AVFilterLink *outlink) @@ -211,7 +213,7 @@ static int config_out_props(AVFilterLink *outlink) AVFilterLink *inlink = outlink->src->inputs[0]; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); TInterlaceContext *tinterlace = ctx->priv; - int i; + int ret, i; tinterlace->vsub = desc->log2_chroma_h; outlink->w = inlink->w; @@ -223,8 +225,7 @@ static int config_out_props(AVFilterLink *outlink) if (tinterlace->mode == MODE_PAD) { uint8_t black[4] = { 0, 0, 0, 16 }; - int ret; - ff_draw_init(&tinterlace->draw, outlink->format, 0); + ff_draw_init2(&tinterlace->draw, outlink->format, outlink->colorspace, outlink->color_range, 0); ff_draw_color(&tinterlace->draw, &tinterlace->color, black); /* limited range */ if (!ff_fmt_is_in(outlink->format, full_scale_yuvj_pix_fmts)) { @@ -291,6 +292,12 @@ static int config_out_props(AVFilterLink *outlink) #endif } + ret = ff_ccfifo_init(&tinterlace->cc_fifo, outlink->frame_rate, ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); + return ret; + } + av_log(ctx, AV_LOG_VERBOSE, "mode:%d filter:%s h:%d -> h:%d\n", tinterlace->mode, (tinterlace->flags & TINTERLACE_FLAG_CVLPF) ? "complex" : (tinterlace->flags & TINTERLACE_FLAG_VLPF) ? "linear" : "off", @@ -375,6 +382,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref) tinterlace->cur = tinterlace->next; tinterlace->next = picref; + ff_ccfifo_extract(&tinterlace->cc_fifo, picref); + cur = tinterlace->cur; next = tinterlace->next; /* we need at least two frames */ @@ -391,8 +400,13 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref) return AVERROR(ENOMEM); av_frame_copy_props(out, cur); out->height = outlink->h; +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 1; out->top_field_first = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags |= AV_FRAME_FLAG_INTERLACED | AV_FRAME_FLAG_TOP_FIELD_FIRST; out->sample_aspect_ratio = av_mul_q(cur->sample_aspect_ratio, av_make_q(2, 1)); /* write odd frame lines into the upper field of the new frame */ @@ -444,13 +458,14 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref) * halving the frame rate and preserving image height */ case MODE_INTERLEAVE_TOP: /* top field first */ case MODE_INTERLEAVE_BOTTOM: /* bottom field first */ - if ((tinterlace->flags & TINTERLACE_FLAG_BYPASS_IL) && cur->interlaced_frame) { + if ((tinterlace->flags & TINTERLACE_FLAG_BYPASS_IL) && (cur->flags & AV_FRAME_FLAG_INTERLACED)) { av_log(ctx, AV_LOG_WARNING, "video is already interlaced, adjusting framerate only\n"); out = av_frame_clone(cur); if (!out) return AVERROR(ENOMEM); out->pts /= 2; // adjust pts to new framerate + ff_ccfifo_inject(&tinterlace->cc_fifo, out); ret = ff_filter_frame(outlink, out); return ret; } @@ -459,8 +474,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref) if (!out) return AVERROR(ENOMEM); av_frame_copy_props(out, cur); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 1; out->top_field_first = tff; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags |= AV_FRAME_FLAG_INTERLACED; + if (tff) + out->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + out->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; /* copy upper/lower field from cur */ copy_picture_field(tinterlace, out->data, out->linesize, @@ -481,22 +505,37 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref) out = av_frame_clone(cur); if (!out) return AVERROR(ENOMEM); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags |= AV_FRAME_FLAG_INTERLACED; if (cur->pts != AV_NOPTS_VALUE) out->pts = cur->pts*2; out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base); + ff_ccfifo_inject(&tinterlace->cc_fifo, out); if ((ret = ff_filter_frame(outlink, out)) < 0) return ret; /* output mix of current and next frame */ - tff = next->top_field_first; + tff = !!(next->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST); out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) return AVERROR(ENOMEM); av_frame_copy_props(out, next); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 1; out->top_field_first = !tff; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags |= AV_FRAME_FLAG_INTERLACED; + if (tff) + out->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + out->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; if (next->pts != AV_NOPTS_VALUE && cur->pts != AV_NOPTS_VALUE) out->pts = cur->pts + next->pts; @@ -521,6 +560,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *picref) out->pts = av_rescale_q(out->pts, tinterlace->preout_time_base, outlink->time_base); out->duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); + ff_ccfifo_inject(&tinterlace->cc_fifo, out); ret = ff_filter_frame(outlink, out); return ret; diff --git a/libavfilter/vf_tmidequalizer.c b/libavfilter/vf_tmidequalizer.c index b5d497b637c..650aa36636d 100644 --- a/libavfilter/vf_tmidequalizer.c +++ b/libavfilter/vf_tmidequalizer.c @@ -22,7 +22,6 @@ #include "libavutil/pixdesc.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" diff --git a/libavfilter/vf_tonemap.c b/libavfilter/vf_tonemap.c index d1087e6bd95..8029f696631 100644 --- a/libavfilter/vf_tonemap.c +++ b/libavfilter/vf_tonemap.c @@ -25,7 +25,6 @@ #include #include -#include #include "libavutil/csp.h" #include "libavutil/imgutils.h" @@ -36,7 +35,6 @@ #include "avfilter.h" #include "colorspace.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -283,14 +281,14 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) #define OFFSET(x) offsetof(TonemapContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM static const AVOption tonemap_options[] = { - { "tonemap", "tonemap algorithm selection", OFFSET(tonemap), AV_OPT_TYPE_INT, {.i64 = TONEMAP_NONE}, TONEMAP_NONE, TONEMAP_MAX - 1, FLAGS, "tonemap" }, - { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_NONE}, 0, 0, FLAGS, "tonemap" }, - { "linear", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_LINEAR}, 0, 0, FLAGS, "tonemap" }, - { "gamma", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_GAMMA}, 0, 0, FLAGS, "tonemap" }, - { "clip", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_CLIP}, 0, 0, FLAGS, "tonemap" }, - { "reinhard", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_REINHARD}, 0, 0, FLAGS, "tonemap" }, - { "hable", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_HABLE}, 0, 0, FLAGS, "tonemap" }, - { "mobius", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_MOBIUS}, 0, 0, FLAGS, "tonemap" }, + { "tonemap", "tonemap algorithm selection", OFFSET(tonemap), AV_OPT_TYPE_INT, {.i64 = TONEMAP_NONE}, TONEMAP_NONE, TONEMAP_MAX - 1, FLAGS, .unit = "tonemap" }, + { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_NONE}, 0, 0, FLAGS, .unit = "tonemap" }, + { "linear", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_LINEAR}, 0, 0, FLAGS, .unit = "tonemap" }, + { "gamma", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_GAMMA}, 0, 0, FLAGS, .unit = "tonemap" }, + { "clip", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_CLIP}, 0, 0, FLAGS, .unit = "tonemap" }, + { "reinhard", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_REINHARD}, 0, 0, FLAGS, .unit = "tonemap" }, + { "hable", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_HABLE}, 0, 0, FLAGS, .unit = "tonemap" }, + { "mobius", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_MOBIUS}, 0, 0, FLAGS, .unit = "tonemap" }, { "param", "tonemap parameter", OFFSET(param), AV_OPT_TYPE_DOUBLE, {.dbl = NAN}, DBL_MIN, DBL_MAX, FLAGS }, { "desat", "desaturation strength", OFFSET(desat), AV_OPT_TYPE_DOUBLE, {.dbl = 2}, 0, DBL_MAX, FLAGS }, { "peak", "signal peak override", OFFSET(peak), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, 0, DBL_MAX, FLAGS }, @@ -307,13 +305,6 @@ static const AVFilterPad tonemap_inputs[] = { }, }; -static const AVFilterPad tonemap_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_tonemap = { .name = "tonemap", .description = NULL_IF_CONFIG_SMALL("Conversion to/from different dynamic ranges."), @@ -321,7 +312,7 @@ const AVFilter ff_vf_tonemap = { .priv_size = sizeof(TonemapContext), .priv_class = &tonemap_class, FILTER_INPUTS(tonemap_inputs), - FILTER_OUTPUTS(tonemap_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS(AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32), .flags = AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/vf_tonemap_opencl.c b/libavfilter/vf_tonemap_opencl.c index f6ebb694a81..a2a27307b48 100644 --- a/libavfilter/vf_tonemap_opencl.c +++ b/libavfilter/vf_tonemap_opencl.c @@ -240,8 +240,8 @@ static int tonemap_opencl_init(AVFilterContext *avctx) av_log(avctx, AV_LOG_DEBUG, "Generated OpenCL header:\n%s\n", header.str); opencl_sources[0] = header.str; - opencl_sources[1] = ff_opencl_source_tonemap; - opencl_sources[2] = ff_opencl_source_colorspace_common; + opencl_sources[1] = ff_source_tonemap_cl; + opencl_sources[2] = ff_source_colorspace_common_cl; err = ff_opencl_filter_load_program(avctx, opencl_sources, OPENCL_SOURCE_NB); av_bprint_finalize(&header, NULL); @@ -483,33 +483,33 @@ static av_cold void tonemap_opencl_uninit(AVFilterContext *avctx) #define OFFSET(x) offsetof(TonemapOpenCLContext, x) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) static const AVOption tonemap_opencl_options[] = { - { "tonemap", "tonemap algorithm selection", OFFSET(tonemap), AV_OPT_TYPE_INT, {.i64 = TONEMAP_NONE}, TONEMAP_NONE, TONEMAP_MAX - 1, FLAGS, "tonemap" }, - { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_NONE}, 0, 0, FLAGS, "tonemap" }, - { "linear", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_LINEAR}, 0, 0, FLAGS, "tonemap" }, - { "gamma", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_GAMMA}, 0, 0, FLAGS, "tonemap" }, - { "clip", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_CLIP}, 0, 0, FLAGS, "tonemap" }, - { "reinhard", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_REINHARD}, 0, 0, FLAGS, "tonemap" }, - { "hable", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_HABLE}, 0, 0, FLAGS, "tonemap" }, - { "mobius", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_MOBIUS}, 0, 0, FLAGS, "tonemap" }, - { "transfer", "set transfer characteristic", OFFSET(trc), AV_OPT_TYPE_INT, {.i64 = AVCOL_TRC_BT709}, -1, INT_MAX, FLAGS, "transfer" }, - { "t", "set transfer characteristic", OFFSET(trc), AV_OPT_TYPE_INT, {.i64 = AVCOL_TRC_BT709}, -1, INT_MAX, FLAGS, "transfer" }, - { "bt709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT709}, 0, 0, FLAGS, "transfer" }, - { "bt2020", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_10}, 0, 0, FLAGS, "transfer" }, - { "matrix", "set colorspace matrix", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "matrix" }, - { "m", "set colorspace matrix", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "matrix" }, - { "bt709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT709}, 0, 0, FLAGS, "matrix" }, - { "bt2020", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT2020_NCL}, 0, 0, FLAGS, "matrix" }, - { "primaries", "set color primaries", OFFSET(primaries), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "primaries" }, - { "p", "set color primaries", OFFSET(primaries), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "primaries" }, - { "bt709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_BT709}, 0, 0, FLAGS, "primaries" }, - { "bt2020", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_BT2020}, 0, 0, FLAGS, "primaries" }, - { "range", "set color range", OFFSET(range), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "range" }, - { "r", "set color range", OFFSET(range), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "range" }, - { "tv", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG}, 0, 0, FLAGS, "range" }, - { "pc", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range" }, - { "limited", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG}, 0, 0, FLAGS, "range" }, - { "full", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG}, 0, 0, FLAGS, "range" }, - { "format", "output pixel format", OFFSET(format), AV_OPT_TYPE_PIXEL_FMT, {.i64 = AV_PIX_FMT_NONE}, AV_PIX_FMT_NONE, INT_MAX, FLAGS, "fmt" }, + { "tonemap", "tonemap algorithm selection", OFFSET(tonemap), AV_OPT_TYPE_INT, {.i64 = TONEMAP_NONE}, TONEMAP_NONE, TONEMAP_MAX - 1, FLAGS, .unit = "tonemap" }, + { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_NONE}, 0, 0, FLAGS, .unit = "tonemap" }, + { "linear", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_LINEAR}, 0, 0, FLAGS, .unit = "tonemap" }, + { "gamma", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_GAMMA}, 0, 0, FLAGS, .unit = "tonemap" }, + { "clip", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_CLIP}, 0, 0, FLAGS, .unit = "tonemap" }, + { "reinhard", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_REINHARD}, 0, 0, FLAGS, .unit = "tonemap" }, + { "hable", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_HABLE}, 0, 0, FLAGS, .unit = "tonemap" }, + { "mobius", 0, 0, AV_OPT_TYPE_CONST, {.i64 = TONEMAP_MOBIUS}, 0, 0, FLAGS, .unit = "tonemap" }, + { "transfer", "set transfer characteristic", OFFSET(trc), AV_OPT_TYPE_INT, {.i64 = AVCOL_TRC_BT709}, -1, INT_MAX, FLAGS, .unit = "transfer" }, + { "t", "set transfer characteristic", OFFSET(trc), AV_OPT_TYPE_INT, {.i64 = AVCOL_TRC_BT709}, -1, INT_MAX, FLAGS, .unit = "transfer" }, + { "bt709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT709}, 0, 0, FLAGS, .unit = "transfer" }, + { "bt2020", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_TRC_BT2020_10}, 0, 0, FLAGS, .unit = "transfer" }, + { "matrix", "set colorspace matrix", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "matrix" }, + { "m", "set colorspace matrix", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "matrix" }, + { "bt709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT709}, 0, 0, FLAGS, .unit = "matrix" }, + { "bt2020", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_SPC_BT2020_NCL}, 0, 0, FLAGS, .unit = "matrix" }, + { "primaries", "set color primaries", OFFSET(primaries), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "primaries" }, + { "p", "set color primaries", OFFSET(primaries), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "primaries" }, + { "bt709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_BT709}, 0, 0, FLAGS, .unit = "primaries" }, + { "bt2020", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_PRI_BT2020}, 0, 0, FLAGS, .unit = "primaries" }, + { "range", "set color range", OFFSET(range), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "range" }, + { "r", "set color range", OFFSET(range), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "range" }, + { "tv", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG}, 0, 0, FLAGS, .unit = "range" }, + { "pc", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG}, 0, 0, FLAGS, .unit = "range" }, + { "limited", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_MPEG}, 0, 0, FLAGS, .unit = "range" }, + { "full", 0, 0, AV_OPT_TYPE_CONST, {.i64 = AVCOL_RANGE_JPEG}, 0, 0, FLAGS, .unit = "range" }, + { "format", "output pixel format", OFFSET(format), AV_OPT_TYPE_PIXEL_FMT, {.i64 = AV_PIX_FMT_NONE}, AV_PIX_FMT_NONE, INT_MAX, FLAGS, .unit = "fmt" }, { "peak", "signal peak override", OFFSET(peak), AV_OPT_TYPE_DOUBLE, {.dbl = 0}, 0, DBL_MAX, FLAGS }, { "param", "tonemap parameter", OFFSET(param), AV_OPT_TYPE_DOUBLE, {.dbl = NAN}, DBL_MIN, DBL_MAX, FLAGS }, { "desat", "desaturation parameter", OFFSET(desat_param), AV_OPT_TYPE_DOUBLE, {.dbl = 0.5}, 0, DBL_MAX, FLAGS }, @@ -547,4 +547,5 @@ const AVFilter ff_vf_tonemap_opencl = { FILTER_OUTPUTS(tonemap_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_tonemap_vaapi.c b/libavfilter/vf_tonemap_vaapi.c index cd2f4c21950..0b767202d2b 100644 --- a/libavfilter/vf_tonemap_vaapi.c +++ b/libavfilter/vf_tonemap_vaapi.c @@ -22,9 +22,9 @@ #include "libavutil/mastering_display_metadata.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "vaapi_vpp.h" +#include "video.h" typedef struct HDRVAAPIContext { VAAPIVPPContext vpp_ctx; // must be the first field @@ -309,6 +309,9 @@ static int tonemap_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame av_get_pix_fmt_name(output_frame->format), output_frame->width, output_frame->height, output_frame->pts); + av_frame_remove_side_data(output_frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + av_frame_remove_side_data(output_frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + return ff_filter_frame(outlink, output_frame); fail: @@ -328,14 +331,6 @@ static av_cold int tonemap_vaapi_init(AVFilterContext *avctx) if (ctx->output_format_string) { vpp_ctx->output_format = av_get_pix_fmt(ctx->output_format_string); - switch (vpp_ctx->output_format) { - case AV_PIX_FMT_NV12: - case AV_PIX_FMT_P010: - break; - default: - av_log(avctx, AV_LOG_ERROR, "Invalid output format.\n"); - return AVERROR(EINVAL); - } } else { vpp_ctx->output_format = AV_PIX_FMT_NV12; av_log(avctx, AV_LOG_WARNING, "Output format not set, use default format NV12\n"); @@ -364,25 +359,25 @@ static av_cold int tonemap_vaapi_init(AVFilterContext *avctx) #define OFFSET(x) offsetof(HDRVAAPIContext, x) #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM) static const AVOption tonemap_vaapi_options[] = { - { "format", "Output pixel format set", OFFSET(output_format_string), AV_OPT_TYPE_STRING, .flags = FLAGS, "format" }, + { "format", "Output pixel format set", OFFSET(output_format_string), AV_OPT_TYPE_STRING, .flags = FLAGS, .unit = "format" }, { "matrix", "Output color matrix coefficient set", OFFSET(color_matrix_string), AV_OPT_TYPE_STRING, - { .str = NULL }, .flags = FLAGS, "matrix" }, + { .str = NULL }, .flags = FLAGS, .unit = "matrix" }, { "m", "Output color matrix coefficient set", OFFSET(color_matrix_string), AV_OPT_TYPE_STRING, - { .str = NULL }, .flags = FLAGS, "matrix" }, + { .str = NULL }, .flags = FLAGS, .unit = "matrix" }, { "primaries", "Output color primaries set", OFFSET(color_primaries_string), AV_OPT_TYPE_STRING, - { .str = NULL }, .flags = FLAGS, "primaries" }, + { .str = NULL }, .flags = FLAGS, .unit = "primaries" }, { "p", "Output color primaries set", OFFSET(color_primaries_string), AV_OPT_TYPE_STRING, - { .str = NULL }, .flags = FLAGS, "primaries" }, + { .str = NULL }, .flags = FLAGS, .unit = "primaries" }, { "transfer", "Output color transfer characteristics set", OFFSET(color_transfer_string), AV_OPT_TYPE_STRING, - { .str = NULL }, .flags = FLAGS, "transfer" }, + { .str = NULL }, .flags = FLAGS, .unit = "transfer" }, { "t", "Output color transfer characteristics set", OFFSET(color_transfer_string), AV_OPT_TYPE_STRING, - { .str = NULL }, .flags = FLAGS, "transfer" }, + { .str = NULL }, .flags = FLAGS, .unit = "transfer" }, { NULL } }; diff --git a/libavfilter/vf_tpad.c b/libavfilter/vf_tpad.c index f0c065f0c37..72d0bf338fe 100644 --- a/libavfilter/vf_tpad.c +++ b/libavfilter/vf_tpad.c @@ -26,6 +26,13 @@ #include "internal.h" #include "formats.h" #include "drawutils.h" +#include "video.h" + +enum PadMode { + MODE_ADD = 0, + MODE_CLONE, + NB_MODE +}; typedef struct TPadContext { const AVClass *class; @@ -51,10 +58,10 @@ typedef struct TPadContext { static const AVOption tpad_options[] = { { "start", "set the number of frames to delay input", OFFSET(pad_start), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, VF }, { "stop", "set the number of frames to add after input finished", OFFSET(pad_stop), AV_OPT_TYPE_INT, {.i64=0}, -1, INT_MAX, VF }, - { "start_mode", "set the mode of added frames to start", OFFSET(start_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, VF, "mode" }, - { "add", "add solid-color frames", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VF, "mode" }, - { "clone", "clone first/last frame", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, VF, "mode" }, - { "stop_mode", "set the mode of added frames to end", OFFSET(stop_mode), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, VF, "mode" }, + { "start_mode", "set the mode of added frames to start", OFFSET(start_mode), AV_OPT_TYPE_INT, {.i64=MODE_ADD}, 0, NB_MODE-1, VF, .unit = "mode" }, + { "add", "add solid-color frames", 0, AV_OPT_TYPE_CONST, {.i64=MODE_ADD}, 0, 0, VF, .unit = "mode" }, + { "clone", "clone first/last frame", 0, AV_OPT_TYPE_CONST, {.i64=MODE_CLONE}, 0, 0, VF, .unit = "mode" }, + { "stop_mode", "set the mode of added frames to end", OFFSET(stop_mode), AV_OPT_TYPE_INT, {.i64=MODE_ADD}, 0, NB_MODE-1, VF, .unit = "mode" }, { "start_duration", "set the duration to delay input", OFFSET(start_duration), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT64_MAX, VF }, { "stop_duration", "set the duration to pad input", OFFSET(stop_duration), AV_OPT_TYPE_DURATION, {.i64=0}, 0, INT64_MAX, VF }, { "color", "set the color of the added frames", OFFSET(rgba_color), AV_OPT_TYPE_COLOR, {.str="black"}, 0, 0, VF }, @@ -63,9 +70,20 @@ static const AVOption tpad_options[] = { AVFILTER_DEFINE_CLASS(tpad); +static int needs_drawing(const TPadContext *s) { + return ( + (s->stop_mode == MODE_ADD && (s->pad_stop != 0 || s->stop_duration != 0)) || + (s->start_mode == MODE_ADD && (s->pad_start != 0 || s->start_duration != 0)) + ); +} + static int query_formats(AVFilterContext *ctx) { - return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); + TPadContext *s = ctx->priv; + if (needs_drawing(s)) + return ff_set_common_formats(ctx, ff_draw_supported_pixel_formats(0)); + + return ff_set_common_formats(ctx, ff_all_formats(AVMEDIA_TYPE_VIDEO)); } static int activate(AVFilterContext *ctx) @@ -91,7 +109,7 @@ static int activate(AVFilterContext *ctx) } } - if (s->start_mode == 0 && s->pad_start > 0 && ff_outlink_frame_wanted(outlink)) { + if (s->start_mode == MODE_ADD && s->pad_start > 0 && ff_outlink_frame_wanted(outlink)) { frame = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!frame) return AVERROR(ENOMEM); @@ -106,7 +124,7 @@ static int activate(AVFilterContext *ctx) return ff_filter_frame(outlink, frame); } - if (s->start_mode == 1 && s->pad_start > 0) { + if (s->start_mode == MODE_CLONE && s->pad_start > 0) { if (s->eof) { ff_outlink_set_status(outlink, AVERROR_EOF, 0); return 0; @@ -133,7 +151,7 @@ static int activate(AVFilterContext *ctx) if (ret < 0) return ret; if (ret > 0) { - if (s->stop_mode == 1 && s->pad_stop != 0) { + if (s->stop_mode == MODE_CLONE && s->pad_stop != 0) { av_frame_free(&s->cache_stop); s->cache_stop = av_frame_clone(frame); } @@ -147,14 +165,14 @@ static int activate(AVFilterContext *ctx) ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); return 0; } - if (s->stop_mode == 0) { + if (s->stop_mode == MODE_ADD) { frame = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!frame) return AVERROR(ENOMEM); ff_fill_rectangle(&s->draw, &s->color, frame->data, frame->linesize, 0, 0, frame->width, frame->height); - } else if (s->stop_mode == 1) { + } else if (s->stop_mode == MODE_CLONE) { if (!s->cache_stop) { s->pad_stop = 0; ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); @@ -184,8 +202,10 @@ static int config_input(AVFilterLink *inlink) AVFilterContext *ctx = inlink->dst; TPadContext *s = ctx->priv; - ff_draw_init(&s->draw, inlink->format, 0); - ff_draw_color(&s->draw, &s->color, s->rgba_color); + if (needs_drawing(s)) { + ff_draw_init2(&s->draw, inlink->format, inlink->colorspace, inlink->color_range, 0); + ff_draw_color(&s->draw, &s->color, s->rgba_color); + } if (s->start_duration) s->pad_start = av_rescale_q(s->start_duration, inlink->frame_rate, av_inv_q(AV_TIME_BASE_Q)); @@ -210,13 +230,6 @@ static const AVFilterPad tpad_inputs[] = { }, }; -static const AVFilterPad tpad_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_tpad = { .name = "tpad", .description = NULL_IF_CONFIG_SMALL("Temporarily pad video frames."), @@ -225,6 +238,6 @@ const AVFilter ff_vf_tpad = { .activate = activate, .uninit = uninit, FILTER_INPUTS(tpad_inputs), - FILTER_OUTPUTS(tpad_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/vf_transpose.c b/libavfilter/vf_transpose.c index 469e66729fd..bfd6f45fbd0 100644 --- a/libavfilter/vf_transpose.c +++ b/libavfilter/vf_transpose.c @@ -371,17 +371,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption transpose_options[] = { - { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 7, FLAGS, "dir" }, + { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 7, FLAGS, .unit = "dir" }, { "cclock_flip", "rotate counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "dir" }, { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit = "dir" }, { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit = "dir" }, { "clock_flip", "rotate clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .flags=FLAGS, .unit = "dir" }, { "passthrough", "do not apply transposition if the input matches the specified geometry", - OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, "passthrough" }, - { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, - { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, - { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, + OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, .unit = "passthrough" }, + { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, + { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, + { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, { NULL } }; diff --git a/libavfilter/vf_transpose_npp.c b/libavfilter/vf_transpose_npp.c index 047c200096c..96c008cda96 100644 --- a/libavfilter/vf_transpose_npp.c +++ b/libavfilter/vf_transpose_npp.c @@ -300,7 +300,7 @@ static int npptranspose_rotate(AVFilterContext *ctx, NPPTransposeStageContext *s // nppRotate uses 0,0 as the rotation point // need to shift the image accordingly after rotation - // need to substract 1 to get the correct coordinates + // need to subtract 1 to get the correct coordinates double angle = s->dir == NPP_TRANSPOSE_CLOCK ? -90.0 : s->dir == NPP_TRANSPOSE_CCLOCK ? 90.0 : 180.0; int shiftw = (s->dir == NPP_TRANSPOSE_CLOCK || s->dir == NPP_TRANSPOSE_CLOCK_FLIP) ? ow - 1 : 0; int shifth = (s->dir == NPP_TRANSPOSE_CCLOCK || s->dir == NPP_TRANSPOSE_CLOCK_FLIP) ? oh - 1 : 0; @@ -426,15 +426,15 @@ static int npptranspose_filter_frame(AVFilterLink *link, AVFrame *in) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM) static const AVOption options[] = { - { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = NPP_TRANSPOSE_CCLOCK_FLIP }, 0, 3, FLAGS, "dir" }, - { "cclock_flip", "rotate counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_CCLOCK_FLIP }, 0, 0, FLAGS, "dir" }, - { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_CLOCK }, 0, 0, FLAGS, "dir" }, - { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_CCLOCK }, 0, 0, FLAGS, "dir" }, - { "clock_flip", "rotate clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_CLOCK_FLIP }, 0, 0, FLAGS, "dir" }, - { "passthrough", "do not apply transposition if the input matches the specified geometry", OFFSET(passthrough), AV_OPT_TYPE_INT, { .i64 = NPP_TRANSPOSE_PT_TYPE_NONE }, 0, 2, FLAGS, "passthrough" }, - { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_PT_TYPE_NONE }, 0, 0, FLAGS, "passthrough" }, - { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_PT_TYPE_LANDSCAPE }, 0, 0, FLAGS, "passthrough" }, - { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_PT_TYPE_PORTRAIT }, 0, 0, FLAGS, "passthrough" }, + { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = NPP_TRANSPOSE_CCLOCK_FLIP }, 0, 3, FLAGS, .unit = "dir" }, + { "cclock_flip", "rotate counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_CCLOCK_FLIP }, 0, 0, FLAGS, .unit = "dir" }, + { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_CLOCK }, 0, 0, FLAGS, .unit = "dir" }, + { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_CCLOCK }, 0, 0, FLAGS, .unit = "dir" }, + { "clock_flip", "rotate clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_CLOCK_FLIP }, 0, 0, FLAGS, .unit = "dir" }, + { "passthrough", "do not apply transposition if the input matches the specified geometry", OFFSET(passthrough), AV_OPT_TYPE_INT, { .i64 = NPP_TRANSPOSE_PT_TYPE_NONE }, 0, 2, FLAGS, .unit = "passthrough" }, + { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_PT_TYPE_NONE }, 0, 0, FLAGS, .unit = "passthrough" }, + { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_PT_TYPE_LANDSCAPE }, 0, 0, FLAGS, .unit = "passthrough" }, + { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, { .i64 = NPP_TRANSPOSE_PT_TYPE_PORTRAIT }, 0, 0, FLAGS, .unit = "passthrough" }, { NULL }, }; diff --git a/libavfilter/vf_transpose_opencl.c b/libavfilter/vf_transpose_opencl.c index c6e8bd2f531..262dec0a8ab 100644 --- a/libavfilter/vf_transpose_opencl.c +++ b/libavfilter/vf_transpose_opencl.c @@ -44,7 +44,7 @@ static int transpose_opencl_init(AVFilterContext *avctx) cl_int cle; int err; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_transpose, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_transpose_cl, 1); if (err < 0) goto fail; @@ -235,17 +235,17 @@ static av_cold void transpose_opencl_uninit(AVFilterContext *avctx) #define OFFSET(x) offsetof(TransposeOpenCLContext, x) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) static const AVOption transpose_opencl_options[] = { - { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 3, FLAGS, "dir" }, + { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 3, FLAGS, .unit = "dir" }, { "cclock_flip", "rotate counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "dir" }, { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit = "dir" }, { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit = "dir" }, { "clock_flip", "rotate clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .flags=FLAGS, .unit = "dir" }, { "passthrough", "do not apply transposition if the input matches the specified geometry", - OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, "passthrough" }, - { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, - { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, - { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, + OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, .unit = "passthrough" }, + { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, + { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, + { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, { NULL } }; @@ -281,4 +281,5 @@ const AVFilter ff_vf_transpose_opencl = { FILTER_OUTPUTS(transpose_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_transpose_vaapi.c b/libavfilter/vf_transpose_vaapi.c index 5f1829dd053..165a97de303 100644 --- a/libavfilter/vf_transpose_vaapi.c +++ b/libavfilter/vf_transpose_vaapi.c @@ -21,10 +21,10 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "transpose.h" #include "vaapi_vpp.h" +#include "video.h" typedef struct TransposeVAAPIContext { VAAPIVPPContext vpp_ctx; // must be the first field @@ -231,7 +231,7 @@ static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h) #define OFFSET(x) offsetof(TransposeVAAPIContext, x) #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM) static const AVOption transpose_vaapi_options[] = { - { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 6, FLAGS, "dir" }, + { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 6, FLAGS, .unit = "dir" }, { "cclock_flip", "rotate counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "dir" }, { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit = "dir" }, { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit = "dir" }, @@ -241,10 +241,10 @@ static const AVOption transpose_vaapi_options[] = { { "vflip", "flip vertically", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_VFLIP }, .flags=FLAGS, .unit = "dir" }, { "passthrough", "do not apply transposition if the input matches the specified geometry", - OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, "passthrough" }, - { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, - { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, - { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, + OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, .unit = "passthrough" }, + { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, + { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, + { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, { NULL } }; diff --git a/libavfilter/vf_transpose_vt.c b/libavfilter/vf_transpose_vt.c new file mode 100644 index 00000000000..e0f35fdb5ab --- /dev/null +++ b/libavfilter/vf_transpose_vt.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2023 Zhao Zhili + * + * 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 + +#include "libavutil/hwcontext.h" +#include "libavutil/hwcontext_videotoolbox.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "internal.h" +#include "transpose.h" +#include "video.h" + +typedef struct TransposeVtContext { + AVClass *class; + + VTPixelRotationSessionRef session; + int dir; + int passthrough; +} TransposeVtContext; + +static av_cold int transpose_vt_init(AVFilterContext *avctx) +{ + TransposeVtContext *s = avctx->priv; + int ret; + + ret = VTPixelRotationSessionCreate(kCFAllocatorDefault, &s->session); + if (ret != noErr) { + av_log(avctx, AV_LOG_ERROR, "Rotation session create failed, %d\n", ret); + return AVERROR_EXTERNAL; + } + + return 0; +} + +static av_cold void transpose_vt_uninit(AVFilterContext *avctx) +{ + TransposeVtContext *s = avctx->priv; + + if (s->session) { + VTPixelRotationSessionInvalidate(s->session); + CFRelease(s->session); + s->session = NULL; + } +} + +static int transpose_vt_filter_frame(AVFilterLink *link, AVFrame *in) +{ + int ret; + AVFilterContext *ctx = link->dst; + TransposeVtContext *s = ctx->priv; + AVFilterLink *outlink = ctx->outputs[0]; + CVPixelBufferRef src; + CVPixelBufferRef dst; + AVFrame *out; + + if (s->passthrough) + return ff_filter_frame(outlink, in); + + out = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!out) { + ret = AVERROR(ENOMEM); + goto fail; + } + + ret = av_frame_copy_props(out, in); + if (ret < 0) + goto fail; + + src = (CVPixelBufferRef)in->data[3]; + dst = (CVPixelBufferRef)out->data[3]; + ret = VTPixelRotationSessionRotateImage(s->session, src, dst); + if (ret != noErr) { + av_log(ctx, AV_LOG_ERROR, "transfer image failed, %d\n", ret); + ret = AVERROR_EXTERNAL; + goto fail; + } + + av_frame_free(&in); + + return ff_filter_frame(outlink, out); + +fail: + av_frame_free(&in); + av_frame_free(&out); + return ret; +} + +static int transpose_vt_recreate_hw_ctx(AVFilterLink *outlink) +{ + AVFilterContext *avctx = outlink->src; + AVFilterLink *inlink = outlink->src->inputs[0]; + AVHWFramesContext *hw_frame_ctx_in; + AVHWFramesContext *hw_frame_ctx_out; + int err; + + av_buffer_unref(&outlink->hw_frames_ctx); + + hw_frame_ctx_in = (AVHWFramesContext *)inlink->hw_frames_ctx->data; + outlink->hw_frames_ctx = av_hwframe_ctx_alloc(hw_frame_ctx_in->device_ref); + hw_frame_ctx_out = (AVHWFramesContext *)outlink->hw_frames_ctx->data; + hw_frame_ctx_out->format = AV_PIX_FMT_VIDEOTOOLBOX; + hw_frame_ctx_out->sw_format = hw_frame_ctx_in->sw_format; + hw_frame_ctx_out->width = outlink->w; + hw_frame_ctx_out->height = outlink->h; + + err = ff_filter_init_hw_frames(avctx, outlink, 1); + if (err < 0) + return err; + + err = av_hwframe_ctx_init(outlink->hw_frames_ctx); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, + "Failed to init videotoolbox frame context, %s\n", + av_err2str(err)); + return err; + } + + return 0; +} + +static int transpose_vt_config_output(AVFilterLink *outlink) +{ + int err; + AVFilterContext *avctx = outlink->src; + TransposeVtContext *s = avctx->priv; + AVFilterLink *inlink = outlink->src->inputs[0]; + CFStringRef rotation = kVTRotation_0; + CFBooleanRef vflip = kCFBooleanFalse; + CFBooleanRef hflip = kCFBooleanFalse; + int swap_w_h = 0; + + av_buffer_unref(&outlink->hw_frames_ctx); + outlink->hw_frames_ctx = av_buffer_ref(inlink->hw_frames_ctx); + + if ((inlink->w >= inlink->h && s->passthrough == TRANSPOSE_PT_TYPE_LANDSCAPE) || + (inlink->w <= inlink->h && s->passthrough == TRANSPOSE_PT_TYPE_PORTRAIT)) { + av_log(avctx, AV_LOG_VERBOSE, + "w:%d h:%d -> w:%d h:%d (passthrough mode)\n", + inlink->w, inlink->h, inlink->w, inlink->h); + return 0; + } + + s->passthrough = TRANSPOSE_PT_TYPE_NONE; + + switch (s->dir) { + case TRANSPOSE_CCLOCK_FLIP: + rotation = kVTRotation_CCW90; + vflip = kCFBooleanTrue; + swap_w_h = 1; + break; + case TRANSPOSE_CCLOCK: + rotation = kVTRotation_CCW90; + swap_w_h = 1; + break; + case TRANSPOSE_CLOCK: + rotation = kVTRotation_CW90; + swap_w_h = 1; + break; + case TRANSPOSE_CLOCK_FLIP: + rotation = kVTRotation_CW90; + vflip = kCFBooleanTrue; + swap_w_h = 1; + break; + case TRANSPOSE_REVERSAL: + rotation = kVTRotation_180; + break; + case TRANSPOSE_HFLIP: + hflip = kCFBooleanTrue; + break; + case TRANSPOSE_VFLIP: + vflip = kCFBooleanTrue; + break; + default: + av_log(avctx, AV_LOG_ERROR, "Failed to set direction to %d\n", s->dir); + return AVERROR(EINVAL); + } + + err = VTSessionSetProperty(s->session, kVTPixelRotationPropertyKey_Rotation, + rotation); + if (err != noErr) { + av_log(avctx, AV_LOG_ERROR, "Set rotation property failed, %d\n", err); + return AVERROR_EXTERNAL; + } + err = VTSessionSetProperty(s->session, kVTPixelRotationPropertyKey_FlipVerticalOrientation, + vflip); + if (err != noErr) { + av_log(avctx, AV_LOG_ERROR, "Set vertical flip property failed, %d\n", err); + return AVERROR_EXTERNAL; + } + err = VTSessionSetProperty(s->session, kVTPixelRotationPropertyKey_FlipHorizontalOrientation, + hflip); + if (err != noErr) { + av_log(avctx, AV_LOG_ERROR, "Set horizontal flip property failed, %d\n", err); + return AVERROR_EXTERNAL; + } + + if (!swap_w_h) + return 0; + + outlink->w = inlink->h; + outlink->h = inlink->w; + return transpose_vt_recreate_hw_ctx(outlink); +} + +#define OFFSET(x) offsetof(TransposeVtContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) +static const AVOption transpose_vt_options[] = { + { "dir", "set transpose direction", + OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 6, FLAGS, .unit = "dir" }, + { "cclock_flip", "rotate counter-clockwise with vertical flip", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "dir" }, + { "clock", "rotate clockwise", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit = "dir" }, + { "cclock", "rotate counter-clockwise", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit = "dir" }, + { "clock_flip", "rotate clockwise with vertical flip", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .flags=FLAGS, .unit = "dir" }, + { "reversal", "rotate by half-turn", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_REVERSAL }, .flags=FLAGS, .unit = "dir" }, + { "hflip", "flip horizontally", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_HFLIP }, .flags=FLAGS, .unit = "dir" }, + { "vflip", "flip vertically", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_VFLIP }, .flags=FLAGS, .unit = "dir" }, + + { "passthrough", "do not apply transposition if the input matches the specified geometry", + OFFSET(passthrough), AV_OPT_TYPE_INT, { .i64=TRANSPOSE_PT_TYPE_NONE }, 0, INT_MAX, FLAGS, .unit = "passthrough" }, + { "none", "always apply transposition", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_PT_TYPE_NONE }, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, + { "portrait", "preserve portrait geometry", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_PT_TYPE_PORTRAIT }, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, + { "landscape", "preserve landscape geometry", + 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_PT_TYPE_LANDSCAPE }, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, + + { NULL } +}; + +AVFILTER_DEFINE_CLASS(transpose_vt); + +static const AVFilterPad transpose_vt_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = &transpose_vt_filter_frame, + }, +}; + +static const AVFilterPad transpose_vt_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &transpose_vt_config_output, + }, +}; + +const AVFilter ff_vf_transpose_vt = { + .name = "transpose_vt", + .description = NULL_IF_CONFIG_SMALL("Transpose Videotoolbox frames"), + .priv_size = sizeof(TransposeVtContext), + .init = transpose_vt_init, + .uninit = transpose_vt_uninit, + FILTER_INPUTS(transpose_vt_inputs), + FILTER_OUTPUTS(transpose_vt_outputs), + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VIDEOTOOLBOX), + .priv_class = &transpose_vt_class, + .flags = AVFILTER_FLAG_HWDEVICE, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vf_transpose_vulkan.c b/libavfilter/vf_transpose_vulkan.c index 30d052e08cc..263a934dc53 100644 --- a/libavfilter/vf_transpose_vulkan.c +++ b/libavfilter/vf_transpose_vulkan.c @@ -1,5 +1,7 @@ /* * copyright (c) 2021 Wu Jianhua + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -20,41 +22,61 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" #include "vulkan_filter.h" +#include "vulkan_spirv.h" #include "internal.h" #include "transpose.h" - -#define CGS 32 +#include "video.h" typedef struct TransposeVulkanContext { FFVulkanContext vkctx; - FFVkQueueFamilyCtx qf; - FFVkExecContext *exec; - FFVulkanPipeline *pl; - VkDescriptorImageInfo input_images[3]; - VkDescriptorImageInfo output_images[3]; + int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + FFVkSPIRVShader shd; + VkSampler sampler; int dir; int passthrough; - int initialized; } TransposeVulkanContext; static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { - int err = 0; - FFVkSPIRVShader *shd; + int err; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; TransposeVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_LINEAR)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "transpose_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 1, 1); - FFVulkanDescriptorSetBinding image_descs[] = { + desc = (FFVulkanDescriptorSetBinding []) { { .name = "input_images", .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .dimensions = 2, .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->input_images, + .samplers = DUP_SAMPLER(s->sampler), }, { .name = "output_images", @@ -64,154 +86,47 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) .dimensions = 2, .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .updater = s->output_images, }, }; - image_descs[0].sampler = ff_vk_init_sampler(vkctx, 1, VK_FILTER_LINEAR); - if (!image_descs[0].sampler) - return AVERROR_EXTERNAL; - - ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT, 0); - - { - s->pl = ff_vk_create_pipeline(vkctx, &s->qf); - if (!s->pl) - return AVERROR(ENOMEM); - - shd = ff_vk_init_shader(s->pl, "transpose_compute", image_descs[0].stages); - if (!shd) - return AVERROR(ENOMEM); - - ff_vk_set_compute_shader_sizes(shd, (int [3]){ CGS, 1, 1 }); - RET(ff_vk_add_descriptor_set(vkctx, s->pl, shd, image_descs, FF_ARRAY_ELEMS(image_descs), 0)); - - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - for (int i = 0; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_images[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - if (s->dir == TRANSPOSE_CCLOCK) - GLSLF(2, vec4 res = texture(input_images[%i], ivec2(size.y - pos.y, pos.x)); ,i); - else if (s->dir == TRANSPOSE_CLOCK_FLIP || s->dir == TRANSPOSE_CLOCK) { - GLSLF(2, vec4 res = texture(input_images[%i], ivec2(size.yx - pos.yx)); ,i); - if (s->dir == TRANSPOSE_CLOCK) - GLSLC(2, pos = ivec2(pos.x, size.y - pos.y); ); - } else - GLSLF(2, vec4 res = texture(input_images[%i], pos.yx); ,i); - GLSLF(2, imageStore(output_images[%i], pos, res); ,i); - GLSLC(1, } ); - } - GLSLC(0, } ); - - RET(ff_vk_compile_shader(vkctx, shd, "main")); - RET(ff_vk_init_pipeline_layout(vkctx, s->pl)); - RET(ff_vk_init_compute_pipeline(vkctx, s->pl)); - } - - RET(ff_vk_create_exec_ctx(vkctx, &s->exec, &s->qf)); - s->initialized = 1; - -fail: - return err; -} - -static int process_frames(AVFilterContext *avctx, AVFrame *outframe, AVFrame *inframe) -{ - int err = 0; - VkCommandBuffer cmd_buf; - TransposeVulkanContext *s = avctx->priv; - FFVulkanContext *vkctx = &s->vkctx; - FFVulkanFunctions *vk = &s->vkctx.vkfn; - const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - - AVVkFrame *in = (AVVkFrame *)inframe->data[0]; - AVVkFrame *out = (AVVkFrame *)outframe->data[0]; - - const VkFormat *input_formats = av_vkfmt_from_pixfmt(s->vkctx.input_format); - const VkFormat *output_formats = av_vkfmt_from_pixfmt(s->vkctx.output_format); - - ff_vk_start_exec_recording(vkctx, s->exec); - cmd_buf = ff_vk_get_exec_buf(s->exec); - - for (int i = 0; i < planes; i++) { - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->input_images[i].imageView, in->img[i], - input_formats[i], - ff_comp_identity_map)); - - RET(ff_vk_create_imageview(vkctx, s->exec, - &s->output_images[i].imageView, out->img[i], - output_formats[i], - ff_comp_identity_map)); - - s->input_images[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - s->output_images[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; - } - - ff_vk_update_descriptor_set(vkctx, s->pl, 0); + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 2, 0, 0)); + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 size; ); + GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); for (int i = 0; i < planes; i++) { - VkImageMemoryBarrier barriers[] = { - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT, - .oldLayout = in->layout[i], - .newLayout = s->input_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = in->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .oldLayout = out->layout[i], - .newLayout = s->output_images[i].imageLayout, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = out->img[i], - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - }, - }; - - vk->CmdPipelineBarrier(cmd_buf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, - 0, NULL, 0, NULL, FF_ARRAY_ELEMS(barriers), barriers); - - in->layout[i] = barriers[0].newLayout; - in->access[i] = barriers[0].dstAccessMask; - - out->layout[i] = barriers[1].newLayout; - out->access[i] = barriers[1].dstAccessMask; + GLSLC(0, ); + GLSLF(1, size = imageSize(output_images[%i]); ,i); + GLSLC(1, if (IS_WITHIN(pos, size)) { ); + if (s->dir == TRANSPOSE_CCLOCK) + GLSLF(2, vec4 res = texture(input_images[%i], ivec2(size.y - pos.y, pos.x)); ,i); + else if (s->dir == TRANSPOSE_CLOCK_FLIP || s->dir == TRANSPOSE_CLOCK) { + GLSLF(2, vec4 res = texture(input_images[%i], ivec2(size.yx - pos.yx)); ,i); + if (s->dir == TRANSPOSE_CLOCK) + GLSLC(2, pos = ivec2(pos.x, size.y - pos.y); ); + } else + GLSLF(2, vec4 res = texture(input_images[%i], pos.yx); ,i); + GLSLF(2, imageStore(output_images[%i], pos, res); ,i); + GLSLC(1, } ); } + GLSLC(0, } ); - ff_vk_bind_pipeline_exec(vkctx, s->exec, s->pl); - vk->CmdDispatch(cmd_buf, FFALIGN(s->vkctx.output_width, CGS)/CGS, - s->vkctx.output_height, 1); - - ff_vk_add_exec_dep(vkctx, s->exec, inframe, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - ff_vk_add_exec_dep(vkctx, s->exec, outframe, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); + RET(spv->compile_shader(spv, ctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); - err = ff_vk_submit_exec_queue(vkctx, s->exec); - if (err) - return err; + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); - ff_vk_qf_rotate(&s->qf); - - return 0; + s->initialized = 1; fail: - ff_vk_discard_exec_deps(s->exec); + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); + return err; } @@ -235,7 +150,8 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) if (!s->initialized) RET(init_filter(ctx, in)); - RET(process_frames(ctx, out, in)); + RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, out, in, + s->sampler, NULL, 0)); RET(av_frame_copy_props(out, in)); @@ -259,6 +175,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) static av_cold void transpose_vulkan_uninit(AVFilterContext *avctx) { TransposeVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); + ff_vk_uninit(&s->vkctx); s->initialized = 0; @@ -298,17 +225,17 @@ static int config_props_output(AVFilterLink *outlink) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) static const AVOption transpose_vulkan_options[] = { - { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 7, FLAGS, "dir" }, + { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 7, FLAGS, .unit = "dir" }, { "cclock_flip", "rotate counter-clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK_FLIP }, .flags=FLAGS, .unit = "dir" }, { "clock", "rotate clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK }, .flags=FLAGS, .unit = "dir" }, { "cclock", "rotate counter-clockwise", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CCLOCK }, .flags=FLAGS, .unit = "dir" }, { "clock_flip", "rotate clockwise with vertical flip", 0, AV_OPT_TYPE_CONST, { .i64 = TRANSPOSE_CLOCK_FLIP }, .flags=FLAGS, .unit = "dir" }, { "passthrough", "do not apply transposition if the input matches the specified geometry", - OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, "passthrough" }, - { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, - { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, - { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, "passthrough" }, + OFFSET(passthrough), AV_OPT_TYPE_INT, {.i64=TRANSPOSE_PT_TYPE_NONE}, 0, INT_MAX, FLAGS, .unit = "passthrough" }, + { "none", "always apply transposition", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_NONE}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, + { "portrait", "preserve portrait geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_PORTRAIT}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, + { "landscape", "preserve landscape geometry", 0, AV_OPT_TYPE_CONST, {.i64=TRANSPOSE_PT_TYPE_LANDSCAPE}, INT_MIN, INT_MAX, FLAGS, .unit = "passthrough" }, { NULL } }; @@ -343,4 +270,5 @@ const AVFilter ff_vf_transpose_vulkan = { FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), .priv_class = &transpose_vulkan_class, .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_unsharp.c b/libavfilter/vf_unsharp.c index e88e732c9ed..2705ac52703 100644 --- a/libavfilter/vf_unsharp.c +++ b/libavfilter/vf_unsharp.c @@ -37,7 +37,6 @@ */ #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "libavutil/common.h" @@ -171,7 +170,10 @@ static int apply_unsharp_c(AVFilterContext *ctx, AVFrame *in, AVFrame *out) return 0; } -static void set_filter_param(UnsharpFilterParam *fp, int msize_x, int msize_y, float amount) +#define MAX_SCALEBITS 25 + +static int set_filter_param(AVFilterContext *ctx, const char *name, const char *short_name, + UnsharpFilterParam *fp, int msize_x, int msize_y, float amount) { fp->msize_x = msize_x; fp->msize_y = msize_y; @@ -181,20 +183,31 @@ static void set_filter_param(UnsharpFilterParam *fp, int msize_x, int msize_y, f fp->steps_y = msize_y / 2; fp->scalebits = (fp->steps_x + fp->steps_y) * 2; fp->halfscale = 1 << (fp->scalebits - 1); + + if (fp->scalebits > MAX_SCALEBITS) { + av_log(ctx, AV_LOG_ERROR, "%s matrix size (%sx/2+%sy/2)*2=%d greater than maximum value %d\n", + name, short_name, short_name, fp->scalebits, MAX_SCALEBITS); + return AVERROR(EINVAL); + } + + return 0; } static av_cold int init(AVFilterContext *ctx) { UnsharpContext *s = ctx->priv; + int ret; - set_filter_param(&s->luma, s->lmsize_x, s->lmsize_y, s->lamount); - set_filter_param(&s->chroma, s->cmsize_x, s->cmsize_y, s->camount); - set_filter_param(&s->alpha, s->amsize_x, s->amsize_y, s->aamount); +#define SET_FILTER_PARAM(name_, short_) \ + ret = set_filter_param(ctx, #name_, #short_, &s->name_, \ + s->short_##msize_x, s->short_##msize_y, s->short_##amount); \ + if (ret < 0) \ + return ret; \ + + SET_FILTER_PARAM(luma, l); + SET_FILTER_PARAM(chroma, c); + SET_FILTER_PARAM(alpha, a); - if (s->luma.scalebits >= 26 || s->chroma.scalebits >= 26 || s->alpha.scalebits >= 26) { - av_log(ctx, AV_LOG_ERROR, "luma or chroma or alpha matrix size too big\n"); - return AVERROR(EINVAL); - } s->apply_unsharp = apply_unsharp_c; return 0; } @@ -352,13 +365,6 @@ static const AVFilterPad avfilter_vf_unsharp_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_unsharp_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_unsharp = { .name = "unsharp", .description = NULL_IF_CONFIG_SMALL("Sharpen or blur the input video."), @@ -367,7 +373,7 @@ const AVFilter ff_vf_unsharp = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_unsharp_inputs), - FILTER_OUTPUTS(avfilter_vf_unsharp_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/vf_unsharp_opencl.c b/libavfilter/vf_unsharp_opencl.c index 53e310de099..09398464ca3 100644 --- a/libavfilter/vf_unsharp_opencl.c +++ b/libavfilter/vf_unsharp_opencl.c @@ -69,7 +69,7 @@ static int unsharp_opencl_init(AVFilterContext *avctx) cl_int cle; int err; - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_unsharp, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_unsharp_cl, 1); if (err < 0) goto fail; @@ -407,4 +407,5 @@ const AVFilter ff_vf_unsharp_opencl = { FILTER_OUTPUTS(unsharp_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_untile.c b/libavfilter/vf_untile.c index c8dafd1e60b..f32f3e186be 100644 --- a/libavfilter/vf_untile.c +++ b/libavfilter/vf_untile.c @@ -24,6 +24,7 @@ #include "avfilter.h" #include "formats.h" #include "filters.h" +#include "video.h" typedef struct UntileContext { const AVClass *class; @@ -162,13 +163,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&s->frame); } -static const AVFilterPad untile_inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad untile_outputs[] = { { .name = "default", @@ -184,7 +178,7 @@ const AVFilter ff_vf_untile = { .uninit = uninit, .activate = activate, .priv_size = sizeof(UntileContext), - FILTER_INPUTS(untile_inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(untile_outputs), FILTER_QUERY_FUNC(query_formats), .priv_class = &untile_class, diff --git a/libavfilter/vf_uspp.c b/libavfilter/vf_uspp.c index 051de007716..10c8aaeb6c9 100644 --- a/libavfilter/vf_uspp.c +++ b/libavfilter/vf_uspp.c @@ -37,6 +37,7 @@ #include "internal.h" #include "qp_table.h" #include "avfilter.h" +#include "video.h" #define MAX_LEVEL 8 /* quality levels */ #define BLOCK 16 @@ -44,8 +45,10 @@ typedef struct USPPContext { const AVClass *av_class; int log2_count; + int count; int hsub, vsub; int qp; + char *codec_name; enum AVVideoEncParamsType qscale_type; int temp_stride[3]; uint8_t *src[3]; @@ -53,12 +56,14 @@ typedef struct USPPContext { int outbuf_size; uint8_t *outbuf; AVCodecContext *avctx_enc[BLOCK*BLOCK]; - AVPacket *pkt; - AVFrame *frame; - AVFrame *frame_dec; + AVCodecContext *avctx_dec[BLOCK*BLOCK]; + AVPacket *pkt [BLOCK*BLOCK]; + AVFrame *frame [BLOCK*BLOCK]; + AVFrame *frame_dec [BLOCK*BLOCK]; int8_t *non_b_qp_table; int non_b_qp_stride; int use_bframe_qp; + int quality; } USPPContext; #define OFFSET(x) offsetof(USPPContext, x) @@ -67,6 +72,7 @@ static const AVOption uspp_options[] = { { "quality", "set quality", OFFSET(log2_count), AV_OPT_TYPE_INT, {.i64 = 3}, 0, MAX_LEVEL, FLAGS }, { "qp", "force a constant quantizer parameter", OFFSET(qp), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 63, FLAGS }, { "use_bframe_qp", "use B-frames' QP", OFFSET(use_bframe_qp), AV_OPT_TYPE_BOOL,{.i64 = 0}, 0, 1, FLAGS }, + { "codec", "Codec name", OFFSET(codec_name), AV_OPT_TYPE_STRING, {.str = "snow"}, 0, 0, FLAGS }, { NULL } }; @@ -185,13 +191,96 @@ static void store_slice_c(uint8_t *dst, const uint16_t *src, } } -static void filter(USPPContext *p, uint8_t *dst[3], uint8_t *src[3], +static int filter_1phase(AVFilterContext *ctx, void *arg, int i, int nb_jobs) +{ + USPPContext *p = ctx->priv; + int ret, x, y; + int width = ctx->inputs[0]->w; + int height = ctx->inputs[0]->h; + + const int x1 = offset[i+nb_jobs-1][0]; + const int y1 = offset[i+nb_jobs-1][1]; + const int x1c = x1 >> p->hsub; + const int y1c = y1 >> p->vsub; + const int BLOCKc = BLOCK >> p->hsub; + int offset; + AVPacket *pkt = p->pkt[i]; + + av_packet_unref(pkt); + pkt->data = p->outbuf; + pkt->size = p->outbuf_size; + + p->frame[i]->linesize[0] = p->temp_stride[0]; + p->frame[i]->linesize[1] = p->temp_stride[1]; + p->frame[i]->linesize[2] = p->temp_stride[2]; + p->frame[i]->height = height + BLOCK; + p->frame[i]->width = width + BLOCK; + p->frame[i]->data[0] = p->src[0] + x1 + y1 * p->frame[i]->linesize[0]; + p->frame[i]->data[1] = p->src[1] + x1c + y1c * p->frame[i]->linesize[1]; + p->frame[i]->data[2] = p->src[2] + x1c + y1c * p->frame[i]->linesize[2]; + p->frame[i]->format = p->avctx_enc[i]->pix_fmt; + p->frame[i]->quality = p->quality; + + ret = avcodec_send_frame(p->avctx_enc[i], p->frame[i]); + if (ret < 0) { + av_log(p->avctx_enc[i], AV_LOG_ERROR, "Error sending a frame for encoding\n"); + return ret; + } + ret = avcodec_receive_packet(p->avctx_enc[i], pkt); + if (ret < 0) { + av_log(p->avctx_enc[i], AV_LOG_ERROR, "Error receiving a packet from encoding\n"); + return ret; + } + + if (p->avctx_enc[i]->flags & AV_CODEC_FLAG_RECON_FRAME) { + av_packet_unref(pkt); + ret = avcodec_receive_frame(p->avctx_enc[i], p->frame_dec[i]); + if (ret < 0) { + av_log(p->avctx_dec[i], AV_LOG_ERROR, "Error receiving a frame from encoding\n"); + return ret; + } + } else { + ret = avcodec_send_packet(p->avctx_dec[i], pkt); + av_packet_unref(pkt); + if (ret < 0) { + av_log(p->avctx_dec[i], AV_LOG_ERROR, "Error sending a packet for decoding\n"); + return ret; + } + ret = avcodec_receive_frame(p->avctx_dec[i], p->frame_dec[i]); + if (ret < 0) { + av_log(p->avctx_dec[i], AV_LOG_ERROR, "Error receiving a frame from decoding\n"); + return ret; + } + } + + offset = (BLOCK-x1) + (BLOCK-y1) * p->frame_dec[i]->linesize[0]; + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + p->temp[0][x + y * p->temp_stride[0]] += p->frame_dec[i]->data[0][x + y * p->frame_dec[i]->linesize[0] + offset]; + + + if (!p->frame_dec[i]->data[2] || !p->temp[2]) + return 0; + + offset = (BLOCKc-x1c) + (BLOCKc-y1c) * p->frame_dec[i]->linesize[1]; + + for (y = 0; y < AV_CEIL_RSHIFT(height, p->vsub); y++) { + for (x = 0; x < AV_CEIL_RSHIFT(width, p->hsub); x++) { + p->temp[1][x + y * p->temp_stride[1]] += p->frame_dec[i]->data[1][x + y * p->frame_dec[i]->linesize[1] + offset]; + p->temp[2][x + y * p->temp_stride[2]] += p->frame_dec[i]->data[2][x + y * p->frame_dec[i]->linesize[2] + offset]; + } + } + + return 0; +} + +static void filter(AVFilterContext *ctx, uint8_t *dst[3], uint8_t *src[3], int dst_stride[3], int src_stride[3], int width, int height, uint8_t *qp_store, int qp_stride) { + USPPContext *p = ctx->priv; int x, y, i, j; - const int count = 1<log2_count; - int ret; for (i = 0; i < 3; i++) { int is_chroma = !!i; @@ -216,12 +305,11 @@ static void filter(USPPContext *p, uint8_t *dst[3], uint8_t *src[3], memcpy(p->src[i] + (h+block +y) * stride, p->src[i] + (h-y+block-1) * stride, stride); } - p->frame->linesize[i] = stride; memset(p->temp[i], 0, (h + 2 * block) * stride * sizeof(int16_t)); } if (p->qp) - p->frame->quality = p->qp * FF_QP2LAMBDA; + p->quality = p->qp * FF_QP2LAMBDA; else { int qpsum=0; int qpcount = (height>>4) * (height>>4); @@ -230,58 +318,11 @@ static void filter(USPPContext *p, uint8_t *dst[3], uint8_t *src[3], for (x = 0; x < (width>>4); x++) qpsum += qp_store[x + y * qp_stride]; } - p->frame->quality = ff_norm_qscale((qpsum + qpcount/2) / qpcount, p->qscale_type) * FF_QP2LAMBDA; + p->quality = ff_norm_qscale((qpsum + qpcount/2) / qpcount, p->qscale_type) * FF_QP2LAMBDA; } // init per MB qscale stuff FIXME - p->frame->height = height + BLOCK; - p->frame->width = width + BLOCK; - - for (i = 0; i < count; i++) { - const int x1 = offset[i+count-1][0]; - const int y1 = offset[i+count-1][1]; - const int x1c = x1 >> p->hsub; - const int y1c = y1 >> p->vsub; - const int BLOCKc = BLOCK >> p->hsub; - int offset; - AVPacket *pkt = p->pkt; - int got_pkt_ptr; - av_packet_unref(pkt); - pkt->data = p->outbuf; - pkt->size = p->outbuf_size; - - p->frame->data[0] = p->src[0] + x1 + y1 * p->frame->linesize[0]; - p->frame->data[1] = p->src[1] + x1c + y1c * p->frame->linesize[1]; - p->frame->data[2] = p->src[2] + x1c + y1c * p->frame->linesize[2]; - p->frame->format = p->avctx_enc[i]->pix_fmt; - - ret = avcodec_encode_video2(p->avctx_enc[i], pkt, p->frame, &got_pkt_ptr); - if (ret < 0) { - av_log(p->avctx_enc[i], AV_LOG_ERROR, "Encoding failed\n"); - continue; - } - av_packet_unref(pkt); - - p->frame_dec = p->avctx_enc[i]->coded_frame; - - offset = (BLOCK-x1) + (BLOCK-y1) * p->frame_dec->linesize[0]; - - for (y = 0; y < height; y++) - for (x = 0; x < width; x++) - p->temp[0][x + y * p->temp_stride[0]] += p->frame_dec->data[0][x + y * p->frame_dec->linesize[0] + offset]; - - if (!src[2] || !dst[2]) - continue; - - offset = (BLOCKc-x1c) + (BLOCKc-y1c) * p->frame_dec->linesize[1]; - - for (y = 0; y < AV_CEIL_RSHIFT(height, p->vsub); y++) { - for (x = 0; x < AV_CEIL_RSHIFT(width, p->hsub); x++) { - p->temp[1][x + y * p->temp_stride[1]] += p->frame_dec->data[1][x + y * p->frame_dec->linesize[1] + offset]; - p->temp[2][x + y * p->temp_stride[2]] += p->frame_dec->data[2][x + y * p->frame_dec->linesize[2] + offset]; - } - } - } + ff_filter_execute(ctx, filter_1phase, NULL, NULL, p->count); for (j = 0; j < 3; j++) { int is_chroma = !!j; @@ -313,15 +354,20 @@ static int config_input(AVFilterLink *inlink) const int width = inlink->w; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); int i; - - const AVCodec *enc = avcodec_find_encoder(AV_CODEC_ID_SNOW); + const AVCodec *enc = avcodec_find_encoder_by_name(uspp->codec_name); + const AVCodec *dec = avcodec_find_decoder_by_name(uspp->codec_name); if (!enc) { - av_log(ctx, AV_LOG_ERROR, "SNOW encoder not found.\n"); + av_log(ctx, AV_LOG_ERROR, "encoder %s not found.\n", uspp->codec_name); + return AVERROR(EINVAL); + } + if (!dec) { + av_log(ctx, AV_LOG_ERROR, "decoder %s not found.\n", uspp->codec_name); return AVERROR(EINVAL); } uspp->hsub = desc->log2_chroma_w; uspp->vsub = desc->log2_chroma_h; + uspp->count = 1<log2_count; for (i = 0; i < 3; i++) { int is_chroma = !!i; @@ -340,8 +386,8 @@ static int config_input(AVFilterLink *inlink) return AVERROR(ENOMEM); } - for (i = 0; i < (1<log2_count); i++) { - AVCodecContext *avctx_enc; + for (i = 0; i < uspp->count; i++) { + AVCodecContext *avctx_enc, *avctx_dec; AVDictionary *opts = NULL; int ret; @@ -356,21 +402,41 @@ static int config_input(AVFilterLink *inlink) avctx_enc->max_b_frames = 0; avctx_enc->pix_fmt = inlink->format; avctx_enc->flags = AV_CODEC_FLAG_QSCALE | AV_CODEC_FLAG_LOW_DELAY; + if (enc->capabilities & AV_CODEC_CAP_ENCODER_RECON_FRAME) { + avctx_enc->flags |= AV_CODEC_FLAG_RECON_FRAME; + av_dict_set(&opts, "no_bitstream", "1", 0); + } avctx_enc->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL; avctx_enc->global_quality = 123; - av_dict_set(&opts, "no_bitstream", "1", 0); + avctx_enc->thread_count = 1; // We do threading in the filter with muiltiple codecs ret = avcodec_open2(avctx_enc, enc, &opts); av_dict_free(&opts); if (ret < 0) return ret; av_assert0(avctx_enc->codec); + + + if (!(enc->capabilities & AV_CODEC_CAP_ENCODER_RECON_FRAME)) { + if (!(uspp->avctx_dec[i] = avcodec_alloc_context3(NULL))) + return AVERROR(ENOMEM); + avctx_dec = uspp->avctx_dec[i]; + avctx_dec->width = avctx_enc->width; + avctx_dec->height = avctx_enc->height; + avctx_dec->thread_count = 1; + ret = avcodec_open2(avctx_dec, dec, NULL); + if (ret < 0) + return ret; + } + + if (!(uspp->frame[i] = av_frame_alloc())) + return AVERROR(ENOMEM); + if (!(uspp->frame_dec[i] = av_frame_alloc())) + return AVERROR(ENOMEM); + if (!(uspp->pkt[i] = av_packet_alloc())) + return AVERROR(ENOMEM); } uspp->outbuf_size = (width + BLOCK) * (height + BLOCK) * 10; - if (!(uspp->frame = av_frame_alloc())) - return AVERROR(ENOMEM); - if (!(uspp->pkt = av_packet_alloc())) - return AVERROR(ENOMEM); if (!(uspp->outbuf = av_malloc(uspp->outbuf_size))) return AVERROR(ENOMEM); @@ -432,7 +498,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) out->height = in->height; } - filter(uspp, out->data, in->data, out->linesize, in->linesize, + filter(ctx, out->data, in->data, out->linesize, in->linesize, inlink->w, inlink->h, qp_table, qp_stride); } } @@ -460,13 +526,16 @@ static av_cold void uninit(AVFilterContext *ctx) av_freep(&uspp->src[i]); } - for (i = 0; i < (1 << uspp->log2_count); i++) + for (i = 0; i < uspp->count; i++) { avcodec_free_context(&uspp->avctx_enc[i]); + avcodec_free_context(&uspp->avctx_dec[i]); + av_frame_free(&uspp->frame[i]); + av_frame_free(&uspp->frame_dec[i]); + av_packet_free(&uspp->pkt[i]); + } av_freep(&uspp->non_b_qp_table); av_freep(&uspp->outbuf); - av_packet_free(&uspp->pkt); - av_frame_free(&uspp->frame); } static const AVFilterPad uspp_inputs[] = { @@ -478,21 +547,14 @@ static const AVFilterPad uspp_inputs[] = { }, }; -static const AVFilterPad uspp_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_uspp = { .name = "uspp", .description = NULL_IF_CONFIG_SMALL("Apply Ultra Simple / Slow Post-processing filter."), .priv_size = sizeof(USPPContext), .uninit = uninit, FILTER_INPUTS(uspp_inputs), - FILTER_OUTPUTS(uspp_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &uspp_class, - .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL, + .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_INTERNAL | AVFILTER_FLAG_SLICE_THREADS, }; diff --git a/libavfilter/vf_v360.c b/libavfilter/vf_v360.c index 2ac9b688dcb..942b47d7a4c 100644 --- a/libavfilter/vf_v360.c +++ b/libavfilter/vf_v360.c @@ -55,118 +55,118 @@ typedef struct ThreadData { #define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption v360_options[] = { - { "input", "set input projection", OFFSET(in), AV_OPT_TYPE_INT, {.i64=EQUIRECTANGULAR}, 0, NB_PROJECTIONS-1, FLAGS, "in" }, - { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "in" }, - { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "in" }, - { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, "in" }, - { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "in" }, - { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "in" }, - { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "in" }, - { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" }, - {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" }, - { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "in" }, - { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" }, - { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "in" }, - { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "in" }, - { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "in" }, - { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, "in" }, - { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, "in" }, - { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, "in" }, - {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, "in" }, - { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "in" }, - { "pannini", "pannini", 0, AV_OPT_TYPE_CONST, {.i64=PANNINI}, 0, 0, FLAGS, "in" }, - {"cylindrical", "cylindrical", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICAL}, 0, 0, FLAGS, "in" }, - {"tetrahedron", "tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=TETRAHEDRON}, 0, 0, FLAGS, "in" }, - {"barrelsplit", "barrel split facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL_SPLIT}, 0, 0, FLAGS, "in" }, - { "tsp", "truncated square pyramid", 0, AV_OPT_TYPE_CONST, {.i64=TSPYRAMID}, 0, 0, FLAGS, "in" }, - { "hequirect", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, "in" }, - { "he", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, "in" }, - { "equisolid", "equisolid", 0, AV_OPT_TYPE_CONST, {.i64=EQUISOLID}, 0, 0, FLAGS, "in" }, - { "og", "orthographic", 0, AV_OPT_TYPE_CONST, {.i64=ORTHOGRAPHIC}, 0, 0, FLAGS, "in" }, - {"octahedron", "octahedron", 0, AV_OPT_TYPE_CONST, {.i64=OCTAHEDRON}, 0, 0, FLAGS, "in" }, - {"cylindricalea", "cylindrical equal area", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICALEA}, 0, 0, FLAGS, "in" }, - { "output", "set output projection", OFFSET(out), AV_OPT_TYPE_INT, {.i64=CUBEMAP_3_2}, 0, NB_PROJECTIONS-1, FLAGS, "out" }, - { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" }, - { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, "out" }, - { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, "out" }, - { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, "out" }, - { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, "out" }, - { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, "out" }, - { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" }, - {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" }, - { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "out" }, - { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "out" }, - { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, "out" }, - { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, "out" }, - { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, "out" }, - { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, "out" }, - { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, "out" }, - { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, "out" }, - {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, "out" }, - { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, "out" }, - { "pannini", "pannini", 0, AV_OPT_TYPE_CONST, {.i64=PANNINI}, 0, 0, FLAGS, "out" }, - {"cylindrical", "cylindrical", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICAL}, 0, 0, FLAGS, "out" }, - {"perspective", "perspective", 0, AV_OPT_TYPE_CONST, {.i64=PERSPECTIVE}, 0, 0, FLAGS, "out" }, - {"tetrahedron", "tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=TETRAHEDRON}, 0, 0, FLAGS, "out" }, - {"barrelsplit", "barrel split facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL_SPLIT}, 0, 0, FLAGS, "out" }, - { "tsp", "truncated square pyramid", 0, AV_OPT_TYPE_CONST, {.i64=TSPYRAMID}, 0, 0, FLAGS, "out" }, - { "hequirect", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, "out" }, - { "he", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, "out" }, - { "equisolid", "equisolid", 0, AV_OPT_TYPE_CONST, {.i64=EQUISOLID}, 0, 0, FLAGS, "out" }, - { "og", "orthographic", 0, AV_OPT_TYPE_CONST, {.i64=ORTHOGRAPHIC}, 0, 0, FLAGS, "out" }, - {"octahedron", "octahedron", 0, AV_OPT_TYPE_CONST, {.i64=OCTAHEDRON}, 0, 0, FLAGS, "out" }, - {"cylindricalea", "cylindrical equal area", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICALEA}, 0, 0, FLAGS, "out" }, - { "interp", "set interpolation method", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=BILINEAR}, 0, NB_INTERP_METHODS-1, FLAGS, "interp" }, - { "near", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" }, - { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, "interp" }, - { "line", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, "interp" }, - { "linear", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, "interp" }, - { "lagrange9", "lagrange9 interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LAGRANGE9}, 0, 0, FLAGS, "interp" }, - { "cube", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, "interp" }, - { "cubic", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, "interp" }, - { "lanc", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" }, - { "lanczos", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, "interp" }, - { "sp16", "spline16 interpolation", 0, AV_OPT_TYPE_CONST, {.i64=SPLINE16}, 0, 0, FLAGS, "interp" }, - { "spline16", "spline16 interpolation", 0, AV_OPT_TYPE_CONST, {.i64=SPLINE16}, 0, 0, FLAGS, "interp" }, - { "gauss", "gaussian interpolation", 0, AV_OPT_TYPE_CONST, {.i64=GAUSSIAN}, 0, 0, FLAGS, "interp" }, - { "gaussian", "gaussian interpolation", 0, AV_OPT_TYPE_CONST, {.i64=GAUSSIAN}, 0, 0, FLAGS, "interp" }, - { "mitchell", "mitchell interpolation", 0, AV_OPT_TYPE_CONST, {.i64=MITCHELL}, 0, 0, FLAGS, "interp" }, - { "w", "output width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "w"}, - { "h", "output height", OFFSET(height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, "h"}, - { "in_stereo", "input stereo format", OFFSET(in_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" }, - {"out_stereo", "output stereo format", OFFSET(out_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, "stereo" }, - { "2d", "2d mono", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_2D}, 0, 0, FLAGS, "stereo" }, - { "sbs", "side by side", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_SBS}, 0, 0, FLAGS, "stereo" }, - { "tb", "top bottom", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_TB}, 0, 0, FLAGS, "stereo" }, - { "in_forder", "input cubemap face order", OFFSET(in_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "in_forder"}, - {"out_forder", "output cubemap face order", OFFSET(out_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, "out_forder"}, - { "in_frot", "input cubemap face rotation", OFFSET(in_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "in_frot"}, - { "out_frot", "output cubemap face rotation",OFFSET(out_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, "out_frot"}, - { "in_pad", "percent input cubemap pads", OFFSET(in_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 0.1,TFLAGS, "in_pad"}, - { "out_pad", "percent output cubemap pads", OFFSET(out_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 0.1,TFLAGS, "out_pad"}, - { "fin_pad", "fixed input cubemap pads", OFFSET(fin_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100,TFLAGS, "fin_pad"}, - { "fout_pad", "fixed output cubemap pads", OFFSET(fout_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100,TFLAGS, "fout_pad"}, - { "yaw", "yaw rotation", OFFSET(yaw), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f,TFLAGS, "yaw"}, - { "pitch", "pitch rotation", OFFSET(pitch), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f,TFLAGS, "pitch"}, - { "roll", "roll rotation", OFFSET(roll), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f,TFLAGS, "roll"}, - { "rorder", "rotation order", OFFSET(rorder), AV_OPT_TYPE_STRING, {.str="ypr"}, 0, 0,TFLAGS, "rorder"}, - { "h_fov", "output horizontal field of view",OFFSET(h_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, "h_fov"}, - { "v_fov", "output vertical field of view", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, "v_fov"}, - { "d_fov", "output diagonal field of view", OFFSET(d_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, "d_fov"}, - { "h_flip", "flip out video horizontally", OFFSET(h_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, "h_flip"}, - { "v_flip", "flip out video vertically", OFFSET(v_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, "v_flip"}, - { "d_flip", "flip out video indepth", OFFSET(d_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, "d_flip"}, - { "ih_flip", "flip in video horizontally", OFFSET(ih_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, "ih_flip"}, - { "iv_flip", "flip in video vertically", OFFSET(iv_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, "iv_flip"}, - { "in_trans", "transpose video input", OFFSET(in_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "in_transpose"}, - { "out_trans", "transpose video output", OFFSET(out_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "out_transpose"}, - { "ih_fov", "input horizontal field of view",OFFSET(ih_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, "ih_fov"}, - { "iv_fov", "input vertical field of view", OFFSET(iv_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, "iv_fov"}, - { "id_fov", "input diagonal field of view", OFFSET(id_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, "id_fov"}, - { "h_offset", "output horizontal off-axis offset",OFFSET(h_offset), AV_OPT_TYPE_FLOAT,{.dbl=0.f}, -1.f, 1.f,TFLAGS, "h_offset"}, - { "v_offset", "output vertical off-axis offset", OFFSET(v_offset), AV_OPT_TYPE_FLOAT,{.dbl=0.f}, -1.f, 1.f,TFLAGS, "v_offset"}, - {"alpha_mask", "build mask in alpha plane", OFFSET(alpha), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, "alpha"}, - { "reset_rot", "reset rotation", OFFSET(reset_rot), AV_OPT_TYPE_BOOL, {.i64=0}, -1, 1,TFLAGS, "reset_rot"}, + { "input", "set input projection", OFFSET(in), AV_OPT_TYPE_INT, {.i64=EQUIRECTANGULAR}, 0, NB_PROJECTIONS-1, FLAGS, .unit = "in" }, + { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, .unit = "in" }, + { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, .unit = "in" }, + { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, .unit = "in" }, + { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, .unit = "in" }, + { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, .unit = "in" }, + { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, .unit = "in" }, + { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, .unit = "in" }, + {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, .unit = "in" }, + { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, .unit = "in" }, + { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, .unit = "in" }, + { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, .unit = "in" }, + { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, .unit = "in" }, + { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, .unit = "in" }, + { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, .unit = "in" }, + { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, .unit = "in" }, + { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, .unit = "in" }, + {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, .unit = "in" }, + { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, .unit = "in" }, + { "pannini", "pannini", 0, AV_OPT_TYPE_CONST, {.i64=PANNINI}, 0, 0, FLAGS, .unit = "in" }, + {"cylindrical", "cylindrical", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICAL}, 0, 0, FLAGS, .unit = "in" }, + {"tetrahedron", "tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=TETRAHEDRON}, 0, 0, FLAGS, .unit = "in" }, + {"barrelsplit", "barrel split facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL_SPLIT}, 0, 0, FLAGS, .unit = "in" }, + { "tsp", "truncated square pyramid", 0, AV_OPT_TYPE_CONST, {.i64=TSPYRAMID}, 0, 0, FLAGS, .unit = "in" }, + { "hequirect", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, .unit = "in" }, + { "he", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, .unit = "in" }, + { "equisolid", "equisolid", 0, AV_OPT_TYPE_CONST, {.i64=EQUISOLID}, 0, 0, FLAGS, .unit = "in" }, + { "og", "orthographic", 0, AV_OPT_TYPE_CONST, {.i64=ORTHOGRAPHIC}, 0, 0, FLAGS, .unit = "in" }, + {"octahedron", "octahedron", 0, AV_OPT_TYPE_CONST, {.i64=OCTAHEDRON}, 0, 0, FLAGS, .unit = "in" }, + {"cylindricalea", "cylindrical equal area", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICALEA}, 0, 0, FLAGS, .unit = "in" }, + { "output", "set output projection", OFFSET(out), AV_OPT_TYPE_INT, {.i64=CUBEMAP_3_2}, 0, NB_PROJECTIONS-1, FLAGS, .unit = "out" }, + { "e", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, .unit = "out" }, + { "equirect", "equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=EQUIRECTANGULAR}, 0, 0, FLAGS, .unit = "out" }, + { "c3x2", "cubemap 3x2", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_3_2}, 0, 0, FLAGS, .unit = "out" }, + { "c6x1", "cubemap 6x1", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_6_1}, 0, 0, FLAGS, .unit = "out" }, + { "eac", "equi-angular cubemap", 0, AV_OPT_TYPE_CONST, {.i64=EQUIANGULAR}, 0, 0, FLAGS, .unit = "out" }, + { "dfisheye", "dual fisheye", 0, AV_OPT_TYPE_CONST, {.i64=DUAL_FISHEYE}, 0, 0, FLAGS, .unit = "out" }, + { "flat", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, .unit = "out" }, + {"rectilinear", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, .unit = "out" }, + { "gnomonic", "regular video", 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, .unit = "out" }, + { "barrel", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, .unit = "out" }, + { "fb", "barrel facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL}, 0, 0, FLAGS, .unit = "out" }, + { "c1x6", "cubemap 1x6", 0, AV_OPT_TYPE_CONST, {.i64=CUBEMAP_1_6}, 0, 0, FLAGS, .unit = "out" }, + { "sg", "stereographic", 0, AV_OPT_TYPE_CONST, {.i64=STEREOGRAPHIC}, 0, 0, FLAGS, .unit = "out" }, + { "mercator", "mercator", 0, AV_OPT_TYPE_CONST, {.i64=MERCATOR}, 0, 0, FLAGS, .unit = "out" }, + { "ball", "ball", 0, AV_OPT_TYPE_CONST, {.i64=BALL}, 0, 0, FLAGS, .unit = "out" }, + { "hammer", "hammer", 0, AV_OPT_TYPE_CONST, {.i64=HAMMER}, 0, 0, FLAGS, .unit = "out" }, + {"sinusoidal", "sinusoidal", 0, AV_OPT_TYPE_CONST, {.i64=SINUSOIDAL}, 0, 0, FLAGS, .unit = "out" }, + { "fisheye", "fisheye", 0, AV_OPT_TYPE_CONST, {.i64=FISHEYE}, 0, 0, FLAGS, .unit = "out" }, + { "pannini", "pannini", 0, AV_OPT_TYPE_CONST, {.i64=PANNINI}, 0, 0, FLAGS, .unit = "out" }, + {"cylindrical", "cylindrical", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICAL}, 0, 0, FLAGS, .unit = "out" }, + {"perspective", "perspective", 0, AV_OPT_TYPE_CONST, {.i64=PERSPECTIVE}, 0, 0, FLAGS, .unit = "out" }, + {"tetrahedron", "tetrahedron", 0, AV_OPT_TYPE_CONST, {.i64=TETRAHEDRON}, 0, 0, FLAGS, .unit = "out" }, + {"barrelsplit", "barrel split facebook's 360 format", 0, AV_OPT_TYPE_CONST, {.i64=BARREL_SPLIT}, 0, 0, FLAGS, .unit = "out" }, + { "tsp", "truncated square pyramid", 0, AV_OPT_TYPE_CONST, {.i64=TSPYRAMID}, 0, 0, FLAGS, .unit = "out" }, + { "hequirect", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, .unit = "out" }, + { "he", "half equirectangular", 0, AV_OPT_TYPE_CONST, {.i64=HEQUIRECTANGULAR},0, 0, FLAGS, .unit = "out" }, + { "equisolid", "equisolid", 0, AV_OPT_TYPE_CONST, {.i64=EQUISOLID}, 0, 0, FLAGS, .unit = "out" }, + { "og", "orthographic", 0, AV_OPT_TYPE_CONST, {.i64=ORTHOGRAPHIC}, 0, 0, FLAGS, .unit = "out" }, + {"octahedron", "octahedron", 0, AV_OPT_TYPE_CONST, {.i64=OCTAHEDRON}, 0, 0, FLAGS, .unit = "out" }, + {"cylindricalea", "cylindrical equal area", 0, AV_OPT_TYPE_CONST, {.i64=CYLINDRICALEA}, 0, 0, FLAGS, .unit = "out" }, + { "interp", "set interpolation method", OFFSET(interp), AV_OPT_TYPE_INT, {.i64=BILINEAR}, 0, NB_INTERP_METHODS-1, FLAGS, .unit = "interp" }, + { "near", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, .unit = "interp" }, + { "nearest", "nearest neighbour", 0, AV_OPT_TYPE_CONST, {.i64=NEAREST}, 0, 0, FLAGS, .unit = "interp" }, + { "line", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, .unit = "interp" }, + { "linear", "bilinear interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BILINEAR}, 0, 0, FLAGS, .unit = "interp" }, + { "lagrange9", "lagrange9 interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LAGRANGE9}, 0, 0, FLAGS, .unit = "interp" }, + { "cube", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, .unit = "interp" }, + { "cubic", "bicubic interpolation", 0, AV_OPT_TYPE_CONST, {.i64=BICUBIC}, 0, 0, FLAGS, .unit = "interp" }, + { "lanc", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, .unit = "interp" }, + { "lanczos", "lanczos interpolation", 0, AV_OPT_TYPE_CONST, {.i64=LANCZOS}, 0, 0, FLAGS, .unit = "interp" }, + { "sp16", "spline16 interpolation", 0, AV_OPT_TYPE_CONST, {.i64=SPLINE16}, 0, 0, FLAGS, .unit = "interp" }, + { "spline16", "spline16 interpolation", 0, AV_OPT_TYPE_CONST, {.i64=SPLINE16}, 0, 0, FLAGS, .unit = "interp" }, + { "gauss", "gaussian interpolation", 0, AV_OPT_TYPE_CONST, {.i64=GAUSSIAN}, 0, 0, FLAGS, .unit = "interp" }, + { "gaussian", "gaussian interpolation", 0, AV_OPT_TYPE_CONST, {.i64=GAUSSIAN}, 0, 0, FLAGS, .unit = "interp" }, + { "mitchell", "mitchell interpolation", 0, AV_OPT_TYPE_CONST, {.i64=MITCHELL}, 0, 0, FLAGS, .unit = "interp" }, + { "w", "output width", OFFSET(width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, .unit = "w"}, + { "h", "output height", OFFSET(height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT16_MAX, FLAGS, .unit = "h"}, + { "in_stereo", "input stereo format", OFFSET(in_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, .unit = "stereo" }, + {"out_stereo", "output stereo format", OFFSET(out_stereo), AV_OPT_TYPE_INT, {.i64=STEREO_2D}, 0, NB_STEREO_FMTS-1, FLAGS, .unit = "stereo" }, + { "2d", "2d mono", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_2D}, 0, 0, FLAGS, .unit = "stereo" }, + { "sbs", "side by side", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_SBS}, 0, 0, FLAGS, .unit = "stereo" }, + { "tb", "top bottom", 0, AV_OPT_TYPE_CONST, {.i64=STEREO_TB}, 0, 0, FLAGS, .unit = "stereo" }, + { "in_forder", "input cubemap face order", OFFSET(in_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, .unit = "in_forder"}, + {"out_forder", "output cubemap face order", OFFSET(out_forder), AV_OPT_TYPE_STRING, {.str="rludfb"}, 0, NB_DIRECTIONS-1, FLAGS, .unit = "out_forder"}, + { "in_frot", "input cubemap face rotation", OFFSET(in_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, .unit = "in_frot"}, + { "out_frot", "output cubemap face rotation",OFFSET(out_frot), AV_OPT_TYPE_STRING, {.str="000000"}, 0, NB_DIRECTIONS-1, FLAGS, .unit = "out_frot"}, + { "in_pad", "percent input cubemap pads", OFFSET(in_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 0.1,TFLAGS, .unit = "in_pad"}, + { "out_pad", "percent output cubemap pads", OFFSET(out_pad), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 0.1,TFLAGS, .unit = "out_pad"}, + { "fin_pad", "fixed input cubemap pads", OFFSET(fin_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100,TFLAGS, .unit = "fin_pad"}, + { "fout_pad", "fixed output cubemap pads", OFFSET(fout_pad), AV_OPT_TYPE_INT, {.i64=0}, 0, 100,TFLAGS, .unit = "fout_pad"}, + { "yaw", "yaw rotation", OFFSET(yaw), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f,TFLAGS, .unit = "yaw"}, + { "pitch", "pitch rotation", OFFSET(pitch), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f,TFLAGS, .unit = "pitch"}, + { "roll", "roll rotation", OFFSET(roll), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, -180.f, 180.f,TFLAGS, .unit = "roll"}, + { "rorder", "rotation order", OFFSET(rorder), AV_OPT_TYPE_STRING, {.str="ypr"}, 0, 0,TFLAGS, .unit = "rorder"}, + { "h_fov", "output horizontal field of view",OFFSET(h_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, .unit = "h_fov"}, + { "v_fov", "output vertical field of view", OFFSET(v_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, .unit = "v_fov"}, + { "d_fov", "output diagonal field of view", OFFSET(d_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, .unit = "d_fov"}, + { "h_flip", "flip out video horizontally", OFFSET(h_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, .unit = "h_flip"}, + { "v_flip", "flip out video vertically", OFFSET(v_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, .unit = "v_flip"}, + { "d_flip", "flip out video indepth", OFFSET(d_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, .unit = "d_flip"}, + { "ih_flip", "flip in video horizontally", OFFSET(ih_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, .unit = "ih_flip"}, + { "iv_flip", "flip in video vertically", OFFSET(iv_flip), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1,TFLAGS, .unit = "iv_flip"}, + { "in_trans", "transpose video input", OFFSET(in_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, .unit = "in_transpose"}, + { "out_trans", "transpose video output", OFFSET(out_transpose), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, .unit = "out_transpose"}, + { "ih_fov", "input horizontal field of view",OFFSET(ih_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, .unit = "ih_fov"}, + { "iv_fov", "input vertical field of view", OFFSET(iv_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, .unit = "iv_fov"}, + { "id_fov", "input diagonal field of view", OFFSET(id_fov), AV_OPT_TYPE_FLOAT, {.dbl=0.f}, 0.f, 360.f,TFLAGS, .unit = "id_fov"}, + { "h_offset", "output horizontal off-axis offset",OFFSET(h_offset), AV_OPT_TYPE_FLOAT,{.dbl=0.f}, -1.f, 1.f,TFLAGS, .unit = "h_offset"}, + { "v_offset", "output vertical off-axis offset", OFFSET(v_offset), AV_OPT_TYPE_FLOAT,{.dbl=0.f}, -1.f, 1.f,TFLAGS, .unit = "v_offset"}, + {"alpha_mask", "build mask in alpha plane", OFFSET(alpha), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS, .unit = "alpha"}, + { "reset_rot", "reset rotation", OFFSET(reset_rot), AV_OPT_TYPE_BOOL, {.i64=0}, -1, 1,TFLAGS, .unit = "reset_rot"}, { NULL } }; diff --git a/libavfilter/vf_vaguedenoiser.c b/libavfilter/vf_vaguedenoiser.c index fb66f0dea1c..19deaa25e29 100644 --- a/libavfilter/vf_vaguedenoiser.c +++ b/libavfilter/vf_vaguedenoiser.c @@ -24,11 +24,9 @@ #include "libavutil/attributes.h" #include "libavutil/common.h" #include "libavutil/pixdesc.h" -#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -68,16 +66,16 @@ typedef struct VagueDenoiserContext { #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM static const AVOption vaguedenoiser_options[] = { { "threshold", "set filtering strength", OFFSET(threshold), AV_OPT_TYPE_FLOAT, {.dbl=2.}, 0,DBL_MAX, FLAGS }, - { "method", "set filtering method", OFFSET(method), AV_OPT_TYPE_INT, {.i64=2 }, 0, 2, FLAGS, "method" }, - { "hard", "hard thresholding", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "method" }, - { "soft", "soft thresholding", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "method" }, - { "garrote", "garrote thresholding", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "method" }, + { "method", "set filtering method", OFFSET(method), AV_OPT_TYPE_INT, {.i64=2 }, 0, 2, FLAGS, .unit = "method" }, + { "hard", "hard thresholding", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "method" }, + { "soft", "soft thresholding", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "method" }, + { "garrote", "garrote thresholding", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "method" }, { "nsteps", "set number of steps", OFFSET(nsteps), AV_OPT_TYPE_INT, {.i64=6 }, 1, 32, FLAGS }, { "percent", "set percent of full denoising", OFFSET(percent),AV_OPT_TYPE_FLOAT, {.dbl=85}, 0,100, FLAGS }, { "planes", "set planes to filter", OFFSET(planes), AV_OPT_TYPE_INT, {.i64=15 }, 0, 15, FLAGS }, - { "type", "set threshold type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0 }, 0, 1, FLAGS, "type" }, - { "universal", "universal (VisuShrink)", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "type" }, - { "bayes", "bayes (BayesShrink)", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "type" }, + { "type", "set threshold type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0 }, 0, 1, FLAGS, .unit = "type" }, + { "universal", "universal (VisuShrink)", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "type" }, + { "bayes", "bayes (BayesShrink)", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "type" }, { NULL } }; @@ -594,13 +592,6 @@ static const AVFilterPad vaguedenoiser_inputs[] = { }; -static const AVFilterPad vaguedenoiser_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO - }, -}; - const AVFilter ff_vf_vaguedenoiser = { .name = "vaguedenoiser", .description = NULL_IF_CONFIG_SMALL("Apply a Wavelet based Denoiser."), @@ -609,7 +600,7 @@ const AVFilter ff_vf_vaguedenoiser = { .init = init, .uninit = uninit, FILTER_INPUTS(vaguedenoiser_inputs), - FILTER_OUTPUTS(vaguedenoiser_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_varblur.c b/libavfilter/vf_varblur.c index 3c020a8c3ec..6ebb9c06637 100644 --- a/libavfilter/vf_varblur.c +++ b/libavfilter/vf_varblur.c @@ -22,7 +22,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "framesync.h" #include "internal.h" #include "video.h" @@ -39,13 +38,14 @@ typedef struct VarBlurContext { int planewidth[4]; int planeheight[4]; - AVFrame *sat; + uint8_t *sat[4]; + int sat_linesize[4]; int nb_planes; void (*compute_sat)(const uint8_t *ssrc, int linesize, int w, int h, - const uint8_t *dstp, + uint8_t *dstp, int dst_linesize); int (*blur_plane)(AVFilterContext *ctx, @@ -90,6 +90,7 @@ static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY9, AV_PIX_FMT_GRAY10, AV_PIX_FMT_GRAY12, AV_PIX_FMT_GRAY14, AV_PIX_FMT_GRAY16, + AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32, AV_PIX_FMT_NONE }; @@ -97,14 +98,14 @@ static const enum AVPixelFormat pix_fmts[] = { static void compute_sat##depth(const uint8_t *ssrc, \ int linesize, \ int w, int h, \ - const uint8_t *dstp, \ + uint8_t *dstp, \ int dst_linesize) \ { \ const type *src = (const type *)ssrc; \ stype *dst = (stype *)dstp; \ \ linesize /= (depth / 8); \ - dst_linesize /= (depth / 2); \ + dst_linesize /= sizeof(stype); \ dst += dst_linesize; \ \ for (int y = 0; y < h; y++) { \ @@ -122,6 +123,7 @@ static void compute_sat##depth(const uint8_t *ssrc, \ COMPUTE_SAT(uint8_t, uint32_t, 8) COMPUTE_SAT(uint16_t, uint64_t, 16) +COMPUTE_SAT(float, double, 32) typedef struct ThreadData { AVFrame *in, *out, *radius; @@ -144,12 +146,12 @@ static int blur_plane##bits(AVFilterContext *ctx, \ int slice_start, int slice_end) \ { \ VarBlurContext *s = ctx->priv; \ - const int ddepth = s->depth; \ + const int ddepth = (bits == 32) ? 1 : s->depth; \ const int dst_linesize = ddst_linesize / (bits / 8); \ - const int ptr_linesize = pptr_linesize / (bits / 2); \ + const int ptr_linesize = pptr_linesize / sizeof(stype); \ const int rptr_linesize = rrptr_linesize / (bits / 8); \ - const type *rptr = (const type *)rrptr + slice_start * rptr_linesize; \ - type *dst = (type *)ddst + slice_start * dst_linesize; \ + const type *rptr = ((const type *)rrptr) + slice_start * rptr_linesize; \ + type *dst = ((type *)ddst) + slice_start * dst_linesize; \ const stype *ptr = (stype *)pptr; \ const float minr = 2.f * s->min_radius + 1.f; \ const float maxr = 2.f * s->max_radius + 1.f; \ @@ -182,7 +184,10 @@ static int blur_plane##bits(AVFilterContext *ctx, \ stype p0 = (br + tl - bl - tr) / div; \ stype n0 = (nbr + ntl - nbl - ntr) / ndiv; \ \ - dst[x] = av_clip_uintp2_c(lrintf( \ + if (bits == 32) \ + dst[x] = lerpf(p0, n0, factor); \ + else \ + dst[x] = av_clip_uintp2_c(lrintf( \ lerpf(p0, n0, factor)), \ ddepth); \ } \ @@ -196,6 +201,7 @@ static int blur_plane##bits(AVFilterContext *ctx, \ BLUR_PLANE(uint8_t, uint32_t, 8) BLUR_PLANE(uint16_t, uint64_t, 16) +BLUR_PLANE(float, double, 32) static int blur_planes(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) @@ -215,8 +221,8 @@ static int blur_planes(AVFilterContext *ctx, void *arg, const int dst_linesize = out->linesize[plane]; const uint8_t *rptr = radius->data[plane]; const int rptr_linesize = radius->linesize[plane]; - uint8_t *ptr = s->sat->data[plane]; - const int ptr_linesize = s->sat->linesize[plane]; + uint8_t *ptr = s->sat[plane]; + const int ptr_linesize = s->sat_linesize[plane]; const uint8_t *src = in->data[plane]; uint8_t *dst = out->data[plane]; @@ -263,8 +269,8 @@ static int blur_frame(AVFilterContext *ctx, AVFrame *in, AVFrame *radius) const int height = s->planeheight[plane]; const int width = s->planewidth[plane]; const int linesize = in->linesize[plane]; - uint8_t *ptr = s->sat->data[plane]; - const int ptr_linesize = s->sat->linesize[plane]; + uint8_t *ptr = s->sat[plane]; + const int ptr_linesize = s->sat_linesize[plane]; const uint8_t *src = in->data[plane]; if (!(s->planes & (1 << plane))) @@ -333,8 +339,8 @@ static int config_output(AVFilterLink *outlink) outlink->frame_rate = inlink->frame_rate; s->depth = desc->comp[0].depth; - s->blur_plane = s->depth <= 8 ? blur_plane8 : blur_plane16; - s->compute_sat = s->depth <= 8 ? compute_sat8 : compute_sat16; + s->blur_plane = s->depth <= 8 ? blur_plane8 : s->depth <= 16 ? blur_plane16 : blur_plane32; + s->compute_sat = s->depth <= 8 ? compute_sat8 : s->depth <= 16 ? compute_sat16 : compute_sat32; s->planewidth[1] = s->planewidth[2] = AV_CEIL_RSHIFT(outlink->w, desc->log2_chroma_w); s->planewidth[0] = s->planewidth[3] = outlink->w; @@ -343,9 +349,12 @@ static int config_output(AVFilterLink *outlink) s->nb_planes = av_pix_fmt_count_planes(outlink->format); - s->sat = ff_get_video_buffer(outlink, (outlink->w + 1) * 4 * ((s->depth + 7) / 8), outlink->h + 1); - if (!s->sat) - return AVERROR(ENOMEM); + for (int p = 0; p < s->nb_planes; p++) { + s->sat_linesize[p] = (outlink->w + 1) * (4 + 4 * (s->depth > 8)); + s->sat[p] = av_calloc(s->sat_linesize[p], outlink->h + 1); + if (!s->sat[p]) + return AVERROR(ENOMEM); + } s->fs.on_event = varblur_frame; if ((ret = ff_framesync_init_dualinput(&s->fs, ctx)) < 0) @@ -362,7 +371,8 @@ static av_cold void uninit(AVFilterContext *ctx) VarBlurContext *s = ctx->priv; ff_framesync_uninit(&s->fs); - av_frame_free(&s->sat); + for (int p = 0; p < 4; p++) + av_freep(&s->sat[p]); } static const AVFilterPad varblur_inputs[] = { diff --git a/libavfilter/vf_vectorscope.c b/libavfilter/vf_vectorscope.c index 6a45b114638..735cf35a852 100644 --- a/libavfilter/vf_vectorscope.c +++ b/libavfilter/vf_vectorscope.c @@ -88,49 +88,49 @@ typedef struct VectorscopeContext { #define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption vectorscope_options[] = { - { "mode", "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, "mode"}, - { "m", "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, "mode"}, - { "gray", 0, 0, AV_OPT_TYPE_CONST, {.i64=TINT}, 0, 0, FLAGS, "mode" }, - { "tint", 0, 0, AV_OPT_TYPE_CONST, {.i64=TINT}, 0, 0, FLAGS, "mode" }, - { "color", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR}, 0, 0, FLAGS, "mode" }, - { "color2", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR2}, 0, 0, FLAGS, "mode" }, - { "color3", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR3}, 0, 0, FLAGS, "mode" }, - { "color4", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR4}, 0, 0, FLAGS, "mode" }, - { "color5", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR5}, 0, 0, FLAGS, "mode" }, + { "mode", "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, .unit = "mode"}, + { "m", "set vectorscope mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=0}, 0, MODE_NB-1, FLAGS, .unit = "mode"}, + { "gray", 0, 0, AV_OPT_TYPE_CONST, {.i64=TINT}, 0, 0, FLAGS, .unit = "mode" }, + { "tint", 0, 0, AV_OPT_TYPE_CONST, {.i64=TINT}, 0, 0, FLAGS, .unit = "mode" }, + { "color", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR}, 0, 0, FLAGS, .unit = "mode" }, + { "color2", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR2}, 0, 0, FLAGS, .unit = "mode" }, + { "color3", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR3}, 0, 0, FLAGS, .unit = "mode" }, + { "color4", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR4}, 0, 0, FLAGS, .unit = "mode" }, + { "color5", 0, 0, AV_OPT_TYPE_CONST, {.i64=COLOR5}, 0, 0, FLAGS, .unit = "mode" }, { "x", "set color component on X axis", OFFSET(x), AV_OPT_TYPE_INT, {.i64=1}, 0, 2, FLAGS}, { "y", "set color component on Y axis", OFFSET(y), AV_OPT_TYPE_INT, {.i64=2}, 0, 2, FLAGS}, { "intensity", "set intensity", OFFSET(fintensity), AV_OPT_TYPE_FLOAT, {.dbl=0.004}, 0, 1, TFLAGS}, { "i", "set intensity", OFFSET(fintensity), AV_OPT_TYPE_FLOAT, {.dbl=0.004}, 0, 1, TFLAGS}, - { "envelope", "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, TFLAGS, "envelope"}, - { "e", "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, TFLAGS, "envelope"}, - { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, TFLAGS, "envelope" }, - { "instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, TFLAGS, "envelope" }, - { "peak", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, TFLAGS, "envelope" }, - { "peak+instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, TFLAGS, "envelope" }, - { "graticule", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=GRAT_NONE}, 0, NB_GRATICULES-1, FLAGS, "graticule"}, - { "g", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=GRAT_NONE}, 0, NB_GRATICULES-1, FLAGS, "graticule"}, - { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_NONE}, 0, 0, FLAGS, "graticule" }, - { "green", 0, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_GREEN}, 0, 0, FLAGS, "graticule" }, - { "color", 0, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_COLOR}, 0, 0, FLAGS, "graticule" }, - { "invert", 0, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_INVERT},0, 0, FLAGS, "graticule" }, + { "envelope", "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, TFLAGS, .unit = "envelope"}, + { "e", "set envelope", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, TFLAGS, .unit = "envelope"}, + { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, TFLAGS, .unit = "envelope" }, + { "instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, TFLAGS, .unit = "envelope" }, + { "peak", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, TFLAGS, .unit = "envelope" }, + { "peak+instant", 0, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, TFLAGS, .unit = "envelope" }, + { "graticule", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=GRAT_NONE}, 0, NB_GRATICULES-1, FLAGS, .unit = "graticule"}, + { "g", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=GRAT_NONE}, 0, NB_GRATICULES-1, FLAGS, .unit = "graticule"}, + { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_NONE}, 0, 0, FLAGS, .unit = "graticule" }, + { "green", 0, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_GREEN}, 0, 0, FLAGS, .unit = "graticule" }, + { "color", 0, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_COLOR}, 0, 0, FLAGS, .unit = "graticule" }, + { "invert", 0, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_INVERT},0, 0, FLAGS, .unit = "graticule" }, { "opacity", "set graticule opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, TFLAGS}, { "o", "set graticule opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, TFLAGS}, - { "flags", "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=4}, 0, 7, TFLAGS, "flags"}, - { "f", "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=4}, 0, 7, TFLAGS, "flags"}, - { "white", "draw white point", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, TFLAGS, "flags" }, - { "black", "draw black point", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, TFLAGS, "flags" }, - { "name", "draw point name", 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, TFLAGS, "flags" }, + { "flags", "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=4}, 0, 7, TFLAGS, .unit = "flags"}, + { "f", "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=4}, 0, 7, TFLAGS, .unit = "flags"}, + { "white", "draw white point", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, TFLAGS, .unit = "flags" }, + { "black", "draw black point", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, TFLAGS, .unit = "flags" }, + { "name", "draw point name", 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, TFLAGS, .unit = "flags" }, { "bgopacity", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.3}, 0, 1, TFLAGS}, { "b", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.3}, 0, 1, TFLAGS}, { "lthreshold", "set low threshold", OFFSET(lthreshold), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, 1, FLAGS}, { "l", "set low threshold", OFFSET(lthreshold), AV_OPT_TYPE_FLOAT, {.dbl=0}, 0, 1, FLAGS}, { "hthreshold", "set high threshold", OFFSET(hthreshold), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 1, FLAGS}, { "h", "set high threshold", OFFSET(hthreshold), AV_OPT_TYPE_FLOAT, {.dbl=1}, 0, 1, FLAGS}, - { "colorspace", "set colorspace", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "colorspace"}, - { "c", "set colorspace", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "colorspace"}, - { "auto", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "colorspace" }, - { "601", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "colorspace" }, - { "709", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "colorspace" }, + { "colorspace", "set colorspace", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, .unit = "colorspace"}, + { "c", "set colorspace", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, .unit = "colorspace"}, + { "auto", 0, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "colorspace" }, + { "601", 0, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "colorspace" }, + { "709", 0, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, .unit = "colorspace" }, { "tint0", "set 1st tint", OFFSET(ftint[0]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, TFLAGS}, { "t0", "set 1st tint", OFFSET(ftint[0]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, TFLAGS}, { "tint1", "set 2nd tint", OFFSET(ftint[1]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, TFLAGS}, diff --git a/libavfilter/vf_vflip.c b/libavfilter/vf_vflip.c index 0d624512f9f..8d6724ed370 100644 --- a/libavfilter/vf_vflip.c +++ b/libavfilter/vf_vflip.c @@ -135,19 +135,12 @@ static const AVFilterPad avfilter_vf_vflip_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_vflip_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_vflip = { .name = "vflip", .description = NULL_IF_CONFIG_SMALL("Flip the input video vertically."), .priv_size = sizeof(FlipContext), .priv_class = &vflip_class, FILTER_INPUTS(avfilter_vf_vflip_inputs), - FILTER_OUTPUTS(avfilter_vf_vflip_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, }; diff --git a/libavfilter/vf_vfrdet.c b/libavfilter/vf_vfrdet.c index 0ca221b5b86..8d641dd3688 100644 --- a/libavfilter/vf_vfrdet.c +++ b/libavfilter/vf_vfrdet.c @@ -21,6 +21,7 @@ #include "libavutil/common.h" #include "libavutil/opt.h" #include "internal.h" +#include "video.h" typedef struct VFRDETContext { const AVClass *class; @@ -95,13 +96,6 @@ static const AVFilterPad vfrdet_inputs[] = { }, }; -static const AVFilterPad vfrdet_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_vfrdet = { .name = "vfrdet", .description = NULL_IF_CONFIG_SMALL("Variable frame rate detect filter."), @@ -110,5 +104,5 @@ const AVFilter ff_vf_vfrdet = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(vfrdet_inputs), - FILTER_OUTPUTS(vfrdet_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), }; diff --git a/libavfilter/vf_vibrance.c b/libavfilter/vf_vibrance.c index 329b37bdc62..e1d6e64aeb0 100644 --- a/libavfilter/vf_vibrance.c +++ b/libavfilter/vf_vibrance.c @@ -19,10 +19,9 @@ */ #include "libavutil/opt.h" -#include "libavutil/imgutils.h" +#include "libavutil/pixdesc.h" #include "avfilter.h" #include "drawutils.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -78,14 +77,14 @@ static int vibrance_slice8(AVFilterContext *avctx, void *arg, int jobnr, int nb_ const float srintensity = alternate * FFSIGN(rintensity); const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int glinesize = frame->linesize[0]; - const int blinesize = frame->linesize[1]; - const int rlinesize = frame->linesize[2]; - const int alinesize = frame->linesize[3]; - const int gslinesize = in->linesize[0]; - const int bslinesize = in->linesize[1]; - const int rslinesize = in->linesize[2]; - const int aslinesize = in->linesize[3]; + const ptrdiff_t glinesize = frame->linesize[0]; + const ptrdiff_t blinesize = frame->linesize[1]; + const ptrdiff_t rlinesize = frame->linesize[2]; + const ptrdiff_t alinesize = frame->linesize[3]; + const ptrdiff_t gslinesize = in->linesize[0]; + const ptrdiff_t bslinesize = in->linesize[1]; + const ptrdiff_t rslinesize = in->linesize[2]; + const ptrdiff_t aslinesize = in->linesize[3]; const uint8_t *gsrc = in->data[0] + slice_start * glinesize; const uint8_t *bsrc = in->data[1] + slice_start * blinesize; const uint8_t *rsrc = in->data[2] + slice_start * rlinesize; @@ -155,14 +154,14 @@ static int vibrance_slice16(AVFilterContext *avctx, void *arg, int jobnr, int nb const float srintensity = alternate * FFSIGN(rintensity); const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int gslinesize = in->linesize[0] / 2; - const int bslinesize = in->linesize[1] / 2; - const int rslinesize = in->linesize[2] / 2; - const int aslinesize = in->linesize[3] / 2; - const int glinesize = frame->linesize[0] / 2; - const int blinesize = frame->linesize[1] / 2; - const int rlinesize = frame->linesize[2] / 2; - const int alinesize = frame->linesize[3] / 2; + const ptrdiff_t gslinesize = in->linesize[0] / 2; + const ptrdiff_t bslinesize = in->linesize[1] / 2; + const ptrdiff_t rslinesize = in->linesize[2] / 2; + const ptrdiff_t aslinesize = in->linesize[3] / 2; + const ptrdiff_t glinesize = frame->linesize[0] / 2; + const ptrdiff_t blinesize = frame->linesize[1] / 2; + const ptrdiff_t rlinesize = frame->linesize[2] / 2; + const ptrdiff_t alinesize = frame->linesize[3] / 2; const uint16_t *gsrc = (const uint16_t *)in->data[0] + slice_start * gslinesize; const uint16_t *bsrc = (const uint16_t *)in->data[1] + slice_start * bslinesize; const uint16_t *rsrc = (const uint16_t *)in->data[2] + slice_start * rslinesize; @@ -235,8 +234,8 @@ static int vibrance_slice8p(AVFilterContext *avctx, void *arg, int jobnr, int nb const float srintensity = alternate * FFSIGN(rintensity); const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int linesize = frame->linesize[0]; - const int slinesize = in->linesize[0]; + const ptrdiff_t linesize = frame->linesize[0]; + const ptrdiff_t slinesize = in->linesize[0]; const uint8_t *src = in->data[0] + slice_start * slinesize; uint8_t *ptr = frame->data[0] + slice_start * linesize; @@ -301,8 +300,8 @@ static int vibrance_slice16p(AVFilterContext *avctx, void *arg, int jobnr, int n const float srintensity = alternate * FFSIGN(rintensity); const int slice_start = (height * jobnr) / nb_jobs; const int slice_end = (height * (jobnr + 1)) / nb_jobs; - const int linesize = frame->linesize[0] / 2; - const int slinesize = in->linesize[0] / 2; + const ptrdiff_t linesize = frame->linesize[0] / 2; + const ptrdiff_t slinesize = in->linesize[0] / 2; const uint16_t *src = (const uint16_t *)in->data[0] + slice_start * slinesize; uint16_t *ptr = (uint16_t *)frame->data[0] + slice_start * linesize; @@ -416,13 +415,6 @@ static const AVFilterPad vibrance_inputs[] = { }, }; -static const AVFilterPad vibrance_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(VibranceContext, x) #define VF AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -446,7 +438,7 @@ const AVFilter ff_vf_vibrance = { .priv_size = sizeof(VibranceContext), .priv_class = &vibrance_class, FILTER_INPUTS(vibrance_inputs), - FILTER_OUTPUTS(vibrance_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pixel_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_vidstabdetect.c b/libavfilter/vf_vidstabdetect.c index b27b1e40a67..1e1946a45d5 100644 --- a/libavfilter/vf_vidstabdetect.c +++ b/libavfilter/vf_vidstabdetect.c @@ -29,6 +29,7 @@ #include "avfilter.h" #include "filters.h" #include "internal.h" +#include "video.h" #include "vidstabutils.h" @@ -39,6 +40,7 @@ typedef struct StabData { VSMotionDetectConfig conf; char *result; + int fileformat; FILE *f; } StabData; @@ -57,6 +59,11 @@ static const AVOption vidstabdetect_options[] = { {"show", "0: draw nothing; 1,2: show fields and transforms", OFFSETC(show), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 2, FLAGS}, {"tripod", "virtual tripod mode (if >0): motion is compared to a reference" " reference frame (frame # is the value)", OFFSETC(virtualTripod), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS}, +#ifdef LIBVIDSTAB_FILE_FORMAT_VERSION + { "fileformat", "transforms data file format", OFFSET(fileformat), AV_OPT_TYPE_INT, {.i64 = BINARY_SERIALIZATION_MODE}, ASCII_SERIALIZATION_MODE, BINARY_SERIALIZATION_MODE, FLAGS, .unit = "file_format"}, + { "ascii", "ASCII text", 0, AV_OPT_TYPE_CONST, {.i64 = ASCII_SERIALIZATION_MODE }, 0, 0, FLAGS, .unit = "file_format"}, + { "binary", "binary", 0, AV_OPT_TYPE_CONST, {.i64 = BINARY_SERIALIZATION_MODE}, 0, 0, FLAGS, .unit = "file_format"}, +#endif {NULL} }; @@ -93,6 +100,13 @@ static int config_input(AVFilterLink *inlink) VSFrameInfo fi; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); int is_planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR; + const char *file_mode = "w"; + +#ifdef LIBVIDSTAB_FILE_FORMAT_VERSION + md->serializationMode = s->fileformat; + if (s->fileformat == BINARY_SERIALIZATION_MODE) + file_mode = "wb"; +#endif vsFrameInfoInit(&fi, inlink->w, inlink->h, ff_av2vs_pixfmt(ctx, inlink->format)); @@ -128,7 +142,7 @@ static int config_input(AVFilterLink *inlink) av_log(ctx, AV_LOG_INFO, " show = %d\n", s->conf.show); av_log(ctx, AV_LOG_INFO, " result = %s\n", s->result); - s->f = avpriv_fopen_utf8(s->result, "w"); + s->f = avpriv_fopen_utf8(s->result, file_mode); if (s->f == NULL) { av_log(ctx, AV_LOG_ERROR, "cannot open transform file %s\n", s->result); return AVERROR(EINVAL); @@ -166,7 +180,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) } if (vsMotionDetection(md, &localmotions, &frame) != VS_OK) { av_log(ctx, AV_LOG_ERROR, "motion detection failed"); - return AVERROR(AVERROR_EXTERNAL); + return AVERROR_EXTERNAL; } else { if (vsWriteToFile(md, s->f, &localmotions) != VS_OK) { int ret = AVERROR(errno); @@ -188,13 +202,6 @@ static const AVFilterPad avfilter_vf_vidstabdetect_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_vidstabdetect_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_vidstabdetect = { .name = "vidstabdetect", .description = NULL_IF_CONFIG_SMALL("Extract relative transformations, " @@ -205,7 +212,7 @@ const AVFilter ff_vf_vidstabdetect = { .uninit = uninit, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(avfilter_vf_vidstabdetect_inputs), - FILTER_OUTPUTS(avfilter_vf_vidstabdetect_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(ff_vidstab_pix_fmts), .priv_class = &vidstabdetect_class, }; diff --git a/libavfilter/vf_vidstabtransform.c b/libavfilter/vf_vidstabtransform.c index 1914d7b3486..dd86c4d819b 100644 --- a/libavfilter/vf_vidstabtransform.c +++ b/libavfilter/vf_vidstabtransform.c @@ -28,6 +28,7 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "internal.h" +#include "video.h" #include "vidstabutils.h" @@ -54,13 +55,13 @@ static const AVOption vidstabtransform_options[] = { AV_OPT_TYPE_INT, {.i64 = 15}, 0, 1000, FLAGS}, {"optalgo", "set camera path optimization algo", OFFSETC(camPathAlgo), - AV_OPT_TYPE_INT, {.i64 = VSOptimalL1}, VSOptimalL1, VSAvg, FLAGS, "optalgo"}, + AV_OPT_TYPE_INT, {.i64 = VSOptimalL1}, VSOptimalL1, VSAvg, FLAGS, .unit = "optalgo"}, { "opt", "global optimization", 0, // from version 1.0 on - AV_OPT_TYPE_CONST, {.i64 = VSOptimalL1 }, 0, 0, FLAGS, "optalgo"}, + AV_OPT_TYPE_CONST, {.i64 = VSOptimalL1 }, 0, 0, FLAGS, .unit = "optalgo"}, { "gauss", "gaussian kernel", 0, - AV_OPT_TYPE_CONST, {.i64 = VSGaussian }, 0, 0, FLAGS, "optalgo"}, + AV_OPT_TYPE_CONST, {.i64 = VSGaussian }, 0, 0, FLAGS, .unit = "optalgo"}, { "avg", "simple averaging on motion", 0, - AV_OPT_TYPE_CONST, {.i64 = VSAvg }, 0, 0, FLAGS, "optalgo"}, + AV_OPT_TYPE_CONST, {.i64 = VSAvg }, 0, 0, FLAGS, .unit = "optalgo"}, {"maxshift", "set maximal number of pixels to translate image", OFFSETC(maxShift), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 500, FLAGS}, @@ -68,11 +69,11 @@ static const AVOption vidstabtransform_options[] = { AV_OPT_TYPE_DOUBLE, {.dbl = -1.0}, -1.0, 3.14, FLAGS}, {"crop", "set cropping mode", OFFSETC(crop), - AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS, "crop"}, + AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS, .unit = "crop"}, { "keep", "keep border", 0, - AV_OPT_TYPE_CONST, {.i64 = VSKeepBorder }, 0, 0, FLAGS, "crop"}, + AV_OPT_TYPE_CONST, {.i64 = VSKeepBorder }, 0, 0, FLAGS, .unit = "crop"}, { "black", "black border", 0, - AV_OPT_TYPE_CONST, {.i64 = VSCropBorder }, 0, 0, FLAGS, "crop"}, + AV_OPT_TYPE_CONST, {.i64 = VSCropBorder }, 0, 0, FLAGS, .unit = "crop"}, {"invert", "invert transforms", OFFSETC(invert), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS}, @@ -86,15 +87,15 @@ static const AVOption vidstabtransform_options[] = { AV_OPT_TYPE_DOUBLE, {.dbl = 0.25}, 0, 5, FLAGS}, {"interpol", "set type of interpolation", OFFSETC(interpolType), - AV_OPT_TYPE_INT, {.i64 = 2}, 0, 3, FLAGS, "interpol"}, + AV_OPT_TYPE_INT, {.i64 = 2}, 0, 3, FLAGS, .unit = "interpol"}, { "no", "no interpolation", 0, - AV_OPT_TYPE_CONST, {.i64 = VS_Zero }, 0, 0, FLAGS, "interpol"}, + AV_OPT_TYPE_CONST, {.i64 = VS_Zero }, 0, 0, FLAGS, .unit = "interpol"}, { "linear", "linear (horizontal)", 0, - AV_OPT_TYPE_CONST, {.i64 = VS_Linear }, 0, 0, FLAGS, "interpol"}, + AV_OPT_TYPE_CONST, {.i64 = VS_Linear }, 0, 0, FLAGS, .unit = "interpol"}, { "bilinear","bi-linear", 0, - AV_OPT_TYPE_CONST, {.i64 = VS_BiLinear},0, 0, FLAGS, "interpol"}, + AV_OPT_TYPE_CONST, {.i64 = VS_BiLinear},0, 0, FLAGS, .unit = "interpol"}, { "bicubic", "bi-cubic", 0, - AV_OPT_TYPE_CONST, {.i64 = VS_BiCubic },0, 0, FLAGS, "interpol"}, + AV_OPT_TYPE_CONST, {.i64 = VS_BiCubic },0, 0, FLAGS, .unit = "interpol"}, {"tripod", "enable virtual tripod mode (same as relative=0:smoothing=0)", OFFSET(tripod), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS}, @@ -192,7 +193,7 @@ static int config_input(AVFilterLink *inlink) av_log(ctx, AV_LOG_INFO, " zoomspeed = %g\n", tc->conf.zoomSpeed); av_log(ctx, AV_LOG_INFO, " interpol = %s\n", getInterpolationTypeName(tc->conf.interpolType)); - f = avpriv_fopen_utf8(tc->input, "r"); + f = avpriv_fopen_utf8(tc->input, "rb"); if (!f) { int ret = AVERROR(errno); av_log(ctx, AV_LOG_ERROR, "cannot open input file %s\n", tc->input); @@ -282,13 +283,6 @@ static const AVFilterPad avfilter_vf_vidstabtransform_inputs[] = { }, }; -static const AVFilterPad avfilter_vf_vidstabtransform_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_vidstabtransform = { .name = "vidstabtransform", .description = NULL_IF_CONFIG_SMALL("Transform the frames, " @@ -298,7 +292,7 @@ const AVFilter ff_vf_vidstabtransform = { .init = init, .uninit = uninit, FILTER_INPUTS(avfilter_vf_vidstabtransform_inputs), - FILTER_OUTPUTS(avfilter_vf_vidstabtransform_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(ff_vidstab_pix_fmts), .priv_class = &vidstabtransform_class, }; diff --git a/libavfilter/vf_vif.c b/libavfilter/vf_vif.c index 1aea4a73c64..a927abaf6f0 100644 --- a/libavfilter/vf_vif.c +++ b/libavfilter/vf_vif.c @@ -27,15 +27,11 @@ #include -#include "libavutil/avstring.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "framesync.h" -#include "drawutils.h" -#include "formats.h" #include "internal.h" -#include "video.h" #define NUM_DATA_BUFS 13 @@ -305,8 +301,8 @@ static int compute_vif2(AVFilterContext *ctx, float *main_sq_filt = data_buf[11]; float *ref_main_filt = data_buf[12]; - float *curr_ref_scale = (float *)ref; - float *curr_main_scale = (float *)main; + const float *curr_ref_scale = ref; + const float *curr_main_scale = main; int curr_ref_stride = ref_stride; int curr_main_stride = main_stride; diff --git a/libavfilter/vf_vignette.c b/libavfilter/vf_vignette.c index 56764b49d11..b329c510aba 100644 --- a/libavfilter/vf_vignette.c +++ b/libavfilter/vf_vignette.c @@ -24,7 +24,6 @@ #include "libavutil/eval.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" @@ -83,12 +82,12 @@ static const AVOption vignette_options[] = { { "a", "set lens angle", OFFSET(angle_expr), AV_OPT_TYPE_STRING, {.str="PI/5"}, .flags = FLAGS }, { "x0", "set circle center position on x-axis", OFFSET(x0_expr), AV_OPT_TYPE_STRING, {.str="w/2"}, .flags = FLAGS }, { "y0", "set circle center position on y-axis", OFFSET(y0_expr), AV_OPT_TYPE_STRING, {.str="h/2"}, .flags = FLAGS }, - { "mode", "set forward/backward mode", OFFSET(backward), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS, "mode" }, - { "forward", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, FLAGS, "mode"}, - { "backward", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1}, INT_MIN, INT_MAX, FLAGS, "mode"}, - { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, "eval" }, - { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, - { "frame", "eval expressions for each frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, + { "mode", "set forward/backward mode", OFFSET(backward), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, FLAGS, .unit = "mode" }, + { "forward", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + { "backward", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = 1}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + { "eval", "specify when to evaluate expressions", OFFSET(eval_mode), AV_OPT_TYPE_INT, {.i64 = EVAL_MODE_INIT}, 0, EVAL_MODE_NB-1, FLAGS, .unit = "eval" }, + { "init", "eval expressions once during initialization", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_INIT}, .flags = FLAGS, .unit = "eval" }, + { "frame", "eval expressions for each frame", 0, AV_OPT_TYPE_CONST, {.i64=EVAL_MODE_FRAME}, .flags = FLAGS, .unit = "eval" }, { "dither", "set dithering", OFFSET(do_dither), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, { "aspect", "set aspect ratio", OFFSET(aspect), AV_OPT_TYPE_RATIONAL, {.dbl = 1}, 0, DBL_MAX, .flags = FLAGS }, { NULL } @@ -325,13 +324,6 @@ static const AVFilterPad vignette_inputs[] = { }, }; -static const AVFilterPad vignette_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_vignette = { .name = "vignette", .description = NULL_IF_CONFIG_SMALL("Make or reverse a vignette effect."), @@ -339,7 +331,7 @@ const AVFilter ff_vf_vignette = { .init = init, .uninit = uninit, FILTER_INPUTS(vignette_inputs), - FILTER_OUTPUTS(vignette_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .priv_class = &vignette_class, .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC, diff --git a/libavfilter/vf_vmafmotion.c b/libavfilter/vf_vmafmotion.c index 137afd92453..022816e896f 100644 --- a/libavfilter/vf_vmafmotion.c +++ b/libavfilter/vf_vmafmotion.c @@ -30,6 +30,7 @@ #include "avfilter.h" #include "formats.h" #include "internal.h" +#include "video.h" #include "vmaf_motion.h" #define BIT_SHIFT 15 @@ -350,13 +351,6 @@ static const AVFilterPad vmafmotion_inputs[] = { }, }; -static const AVFilterPad vmafmotion_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - const AVFilter ff_vf_vmafmotion = { .name = "vmafmotion", .description = NULL_IF_CONFIG_SMALL("Calculate the VMAF Motion score."), @@ -366,6 +360,6 @@ const AVFilter ff_vf_vmafmotion = { .priv_class = &vmafmotion_class, .flags = AVFILTER_FLAG_METADATA_ONLY, FILTER_INPUTS(vmafmotion_inputs), - FILTER_OUTPUTS(vmafmotion_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_QUERY_FUNC(query_formats), }; diff --git a/libavfilter/vf_vpp_qsv.c b/libavfilter/vf_vpp_qsv.c index b4dd7a41913..598c85be091 100644 --- a/libavfilter/vf_vpp_qsv.c +++ b/libavfilter/vf_vpp_qsv.c @@ -31,6 +31,7 @@ #include "libavutil/hwcontext_qsv.h" #include "libavutil/pixdesc.h" #include "libavutil/mathematics.h" +#include "libavutil/mastering_display_metadata.h" #include "formats.h" #include "internal.h" @@ -58,6 +59,15 @@ typedef struct VPPContext{ mfxExtVPPRotation rotation_conf; mfxExtVPPMirroring mirroring_conf; mfxExtVPPScaling scale_conf; +#if QSV_ONEVPL + /** Video signal info attached on the input frame */ + mfxExtVideoSignalInfo invsi_conf; + /** Video signal info attached on the output frame */ + mfxExtVideoSignalInfo outvsi_conf; + /** HDR parameters attached on the input frame */ + mfxExtMasteringDisplayColourVolume mdcv_conf; + mfxExtContentLightLevelInfo clli_conf; +#endif /** * New dimensions. Special values are: @@ -100,8 +110,19 @@ typedef struct VPPContext{ char *ow, *oh; char *output_format_str; + /** The color properties for output */ + char *color_primaries_str; + char *color_transfer_str; + char *color_matrix_str; + + int color_range; + enum AVColorPrimaries color_primaries; + enum AVColorTransferCharacteristic color_transfer; + enum AVColorSpace color_matrix; + int has_passthrough; /* apply pass through mode if possible */ int field_rate; /* Generate output at frame rate or field rate for deinterlace mode, 0: frame, 1: field */ + int tonemap; /* 1: perform tonemapping if the input has HDR metadata, 0: always disable tonemapping */ } VPPContext; static const char *const var_names[] = { @@ -227,6 +248,11 @@ static av_cold int vpp_preinit(AVFilterContext *ctx) vpp->contrast = 1.0; vpp->transpose = -1; + vpp->color_range = AVCOL_RANGE_UNSPECIFIED; + vpp->color_primaries = AVCOL_PRI_UNSPECIFIED; + vpp->color_transfer = AVCOL_TRC_UNSPECIFIED; + vpp->color_matrix = AVCOL_SPC_UNSPECIFIED; + vpp->has_passthrough = 1; return 0; @@ -246,6 +272,24 @@ static av_cold int vpp_init(AVFilterContext *ctx) } } +#define STRING_OPTION(var_name, func_name, default_value) do { \ + if (vpp->var_name ## _str) { \ + int var = av_ ## func_name ## _from_name(vpp->var_name ## _str); \ + if (var < 0) { \ + av_log(ctx, AV_LOG_ERROR, "Invalid %s.\n", #var_name); \ + return AVERROR(EINVAL); \ + } \ + vpp->var_name = var; \ + } else { \ + vpp->var_name = default_value; \ + } \ + } while (0) + + STRING_OPTION(color_primaries, color_primaries, AVCOL_PRI_UNSPECIFIED); + STRING_OPTION(color_transfer, color_transfer, AVCOL_TRC_UNSPECIFIED); + STRING_OPTION(color_matrix, color_space, AVCOL_SPC_UNSPECIFIED); + +#undef STRING_OPTION return 0; } @@ -344,6 +388,140 @@ static mfxStatus get_mfx_version(const AVFilterContext *ctx, mfxVersion *mfx_ver return MFXQueryVersion(device_hwctx->session, mfx_version); } +static int vpp_set_frame_ext_params(AVFilterContext *ctx, const AVFrame *in, AVFrame *out, QSVVPPFrameParam *fp) +{ +#if QSV_ONEVPL + VPPContext *vpp = ctx->priv; + QSVVPPContext *qsvvpp = &vpp->qsv; + mfxExtVideoSignalInfo invsi_conf, outvsi_conf; + mfxExtMasteringDisplayColourVolume mdcv_conf; + mfxExtContentLightLevelInfo clli_conf; + AVFrameSideData *sd; + int tm = 0; + + fp->num_ext_buf = 0; + + if (!in || !out || + !QSV_RUNTIME_VERSION_ATLEAST(qsvvpp->ver, 2, 0)) + return 0; + + memset(&invsi_conf, 0, sizeof(mfxExtVideoSignalInfo)); + invsi_conf.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO_IN; + invsi_conf.Header.BufferSz = sizeof(mfxExtVideoSignalInfo); + invsi_conf.VideoFullRange = (in->color_range == AVCOL_RANGE_JPEG); + invsi_conf.ColourPrimaries = (in->color_primaries == AVCOL_PRI_UNSPECIFIED) ? AVCOL_PRI_BT709 : in->color_primaries; + invsi_conf.TransferCharacteristics = (in->color_trc == AVCOL_TRC_UNSPECIFIED) ? AVCOL_TRC_BT709 : in->color_trc; + invsi_conf.MatrixCoefficients = (in->colorspace == AVCOL_SPC_UNSPECIFIED) ? AVCOL_SPC_BT709 : in->colorspace; + invsi_conf.ColourDescriptionPresent = 1; + + memset(&mdcv_conf, 0, sizeof(mfxExtMasteringDisplayColourVolume)); + sd = av_frame_get_side_data(in, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + if (vpp->tonemap && sd) { + AVMasteringDisplayMetadata *mdm = (AVMasteringDisplayMetadata *)sd->data; + + if (mdm->has_primaries && mdm->has_luminance) { + const int mapping[3] = {1, 2, 0}; + const int chroma_den = 50000; + const int luma_den = 10000; + int i; + + mdcv_conf.Header.BufferId = MFX_EXTBUFF_MASTERING_DISPLAY_COLOUR_VOLUME_IN; + mdcv_conf.Header.BufferSz = sizeof(mfxExtMasteringDisplayColourVolume); + + for (i = 0; i < 3; i++) { + const int j = mapping[i]; + + mdcv_conf.DisplayPrimariesX[i] = + FFMIN(lrint(chroma_den * + av_q2d(mdm->display_primaries[j][0])), + chroma_den); + mdcv_conf.DisplayPrimariesY[i] = + FFMIN(lrint(chroma_den * + av_q2d(mdm->display_primaries[j][1])), + chroma_den); + } + + mdcv_conf.WhitePointX = + FFMIN(lrint(chroma_den * av_q2d(mdm->white_point[0])), + chroma_den); + mdcv_conf.WhitePointY = + FFMIN(lrint(chroma_den * av_q2d(mdm->white_point[1])), + chroma_den); + + /* MaxDisplayMasteringLuminance is in the unit of 1 nits however + * MinDisplayMasteringLuminance is in the unit of 0.0001 nits + */ + mdcv_conf.MaxDisplayMasteringLuminance = + lrint(av_q2d(mdm->max_luminance)); + mdcv_conf.MinDisplayMasteringLuminance = + lrint(luma_den * av_q2d(mdm->min_luminance)); + tm = 1; + } + } + + memset(&clli_conf, 0, sizeof(mfxExtContentLightLevelInfo)); + sd = av_frame_get_side_data(in, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + if (vpp->tonemap && sd) { + AVContentLightMetadata *clm = (AVContentLightMetadata *)sd->data; + + clli_conf.Header.BufferId = MFX_EXTBUFF_CONTENT_LIGHT_LEVEL_INFO; + clli_conf.Header.BufferSz = sizeof(mfxExtContentLightLevelInfo); + clli_conf.MaxContentLightLevel = FFMIN(clm->MaxCLL, 65535); + clli_conf.MaxPicAverageLightLevel = FFMIN(clm->MaxFALL, 65535); + tm = 1; + } + + if (tm) { + av_frame_remove_side_data(out, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + av_frame_remove_side_data(out, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + + out->color_primaries = AVCOL_PRI_BT709; + out->color_trc = AVCOL_TRC_BT709; + out->colorspace = AVCOL_SPC_BT709; + out->color_range = AVCOL_RANGE_MPEG; + } + + if (vpp->color_range != AVCOL_RANGE_UNSPECIFIED) + out->color_range = vpp->color_range; + if (vpp->color_primaries != AVCOL_PRI_UNSPECIFIED) + out->color_primaries = vpp->color_primaries; + if (vpp->color_transfer != AVCOL_TRC_UNSPECIFIED) + out->color_trc = vpp->color_transfer; + if (vpp->color_matrix != AVCOL_SPC_UNSPECIFIED) + out->colorspace = vpp->color_matrix; + + memset(&outvsi_conf, 0, sizeof(mfxExtVideoSignalInfo)); + outvsi_conf.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO_OUT; + outvsi_conf.Header.BufferSz = sizeof(mfxExtVideoSignalInfo); + outvsi_conf.VideoFullRange = (out->color_range == AVCOL_RANGE_JPEG); + outvsi_conf.ColourPrimaries = (out->color_primaries == AVCOL_PRI_UNSPECIFIED) ? AVCOL_PRI_BT709 : out->color_primaries; + outvsi_conf.TransferCharacteristics = (out->color_trc == AVCOL_TRC_UNSPECIFIED) ? AVCOL_TRC_BT709 : out->color_trc; + outvsi_conf.MatrixCoefficients = (out->colorspace == AVCOL_SPC_UNSPECIFIED) ? AVCOL_SPC_BT709 : out->colorspace; + outvsi_conf.ColourDescriptionPresent = 1; + + if (memcmp(&vpp->invsi_conf, &invsi_conf, sizeof(mfxExtVideoSignalInfo)) || + memcmp(&vpp->mdcv_conf, &mdcv_conf, sizeof(mfxExtMasteringDisplayColourVolume)) || + memcmp(&vpp->clli_conf, &clli_conf, sizeof(mfxExtContentLightLevelInfo)) || + memcmp(&vpp->outvsi_conf, &outvsi_conf, sizeof(mfxExtVideoSignalInfo))) { + vpp->invsi_conf = invsi_conf; + fp->ext_buf[fp->num_ext_buf++] = (mfxExtBuffer*)&vpp->invsi_conf; + + vpp->outvsi_conf = outvsi_conf; + fp->ext_buf[fp->num_ext_buf++] = (mfxExtBuffer*)&vpp->outvsi_conf; + + vpp->mdcv_conf = mdcv_conf; + if (mdcv_conf.Header.BufferId) + fp->ext_buf[fp->num_ext_buf++] = (mfxExtBuffer*)&vpp->mdcv_conf; + + vpp->clli_conf = clli_conf; + if (clli_conf.Header.BufferId) + fp->ext_buf[fp->num_ext_buf++] = (mfxExtBuffer*)&vpp->clli_conf; + } +#endif + + return 0; +} + static int config_output(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; @@ -358,9 +536,13 @@ static int config_output(AVFilterLink *outlink) outlink->w = vpp->out_width; outlink->h = vpp->out_height; outlink->frame_rate = vpp->framerate; - outlink->time_base = av_inv_q(vpp->framerate); + if (vpp->framerate.num == 0 || vpp->framerate.den == 0) + outlink->time_base = inlink->time_base; + else + outlink->time_base = av_inv_q(vpp->framerate); param.filter_frame = NULL; + param.set_frame_ext_params = vpp_set_frame_ext_params; param.num_ext_buf = 0; param.ext_buf = ext_buf; @@ -524,6 +706,11 @@ static int config_output(AVFilterLink *outlink) if (vpp->use_frc || vpp->use_crop || vpp->deinterlace || vpp->denoise || vpp->detail || vpp->procamp || vpp->rotate || vpp->hflip || inlink->w != outlink->w || inlink->h != outlink->h || in_format != vpp->out_format || + vpp->color_range != AVCOL_RANGE_UNSPECIFIED || + vpp->color_primaries != AVCOL_PRI_UNSPECIFIED || + vpp->color_transfer != AVCOL_TRC_UNSPECIFIED || + vpp->color_matrix != AVCOL_SPC_UNSPECIFIED || + vpp->tonemap || !vpp->has_passthrough) return ff_qsvvpp_init(ctx, ¶m); else { @@ -582,6 +769,11 @@ static int activate(AVFilterContext *ctx) if (in->pts != AV_NOPTS_VALUE) in->pts = av_rescale_q(in->pts, inlink->time_base, outlink->time_base); + if (outlink->frame_rate.num && outlink->frame_rate.den) + in->duration = av_rescale_q(1, av_inv_q(outlink->frame_rate), outlink->time_base); + else + in->duration = 0; + ret = ff_filter_frame(outlink, in); if (ret < 0) return ret; @@ -649,14 +841,15 @@ const AVFilter ff_vf_##sn##_qsv = { \ fmts, \ .activate = activate, \ .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, \ + .flags = AVFILTER_FLAG_HWDEVICE, \ }; #if CONFIG_VPP_QSV_FILTER 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" }, + { "deinterlace", "deinterlace mode: 0=off, 1=bob, 2=advanced", OFFSET(deinterlace), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, MFX_DEINTERLACING_ADVANCED, .flags = FLAGS, .unit = "deinterlace" }, + { "bob", "Bob deinterlace mode.", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_BOB }, .flags = FLAGS, .unit = "deinterlace" }, + { "advanced", "Advanced deinterlace mode. ", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_DEINTERLACING_ADVANCED }, .flags = FLAGS, .unit = "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 }, @@ -667,7 +860,7 @@ static const AVOption vpp_options[] = { { "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"}, + { "transpose", "set transpose direction", OFFSET(transpose), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 6, FLAGS, .unit = "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" }, @@ -686,34 +879,59 @@ static const AVOption vpp_options[] = { { "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 }, + { "async_depth", "Internal parallelization depth, the higher the value the higher the latency.", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = 4 }, 0, INT_MAX, .flags = FLAGS }, #if QSV_ONEVPL - { "scale_mode", "scaling & format conversion mode (mode compute(3), vd(4) and ve(5) are only available on some platforms)", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 5, .flags = FLAGS, "scale mode" }, + { "scale_mode", "scaling & format conversion mode (mode compute(3), vd(4) and ve(5) are only available on some platforms)", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 5, .flags = FLAGS, .unit = "scale mode" }, #else - { "scale_mode", "scaling & format conversion mode", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT }, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY, .flags = FLAGS, "scale mode" }, + { "scale_mode", "scaling & format conversion mode", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT }, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY, .flags = FLAGS, .unit = "scale mode" }, #endif - { "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"}, + { "auto", "auto mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_DEFAULT}, INT_MIN, INT_MAX, FLAGS, .unit = "scale mode"}, + { "low_power", "low power mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_LOWPOWER}, INT_MIN, INT_MAX, FLAGS, .unit = "scale mode"}, + { "hq", "high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_QUALITY}, INT_MIN, INT_MAX, FLAGS, .unit = "scale mode"}, #if QSV_ONEVPL - { "compute", "compute", 0, AV_OPT_TYPE_CONST, { .i64 = 3}, INT_MIN, INT_MAX, FLAGS, "scale mode"}, - { "vd", "vd", 0, AV_OPT_TYPE_CONST, { .i64 = 4}, INT_MIN, INT_MAX, FLAGS, "scale mode"}, - { "ve", "ve", 0, AV_OPT_TYPE_CONST, { .i64 = 5}, INT_MIN, INT_MAX, FLAGS, "scale mode"}, + { "compute", "compute", 0, AV_OPT_TYPE_CONST, { .i64 = 3}, INT_MIN, INT_MAX, FLAGS, .unit = "scale mode"}, + { "vd", "vd", 0, AV_OPT_TYPE_CONST, { .i64 = 4}, INT_MIN, INT_MAX, FLAGS, .unit = "scale mode"}, + { "ve", "ve", 0, AV_OPT_TYPE_CONST, { .i64 = 5}, INT_MIN, INT_MAX, FLAGS, .unit = "scale mode"}, #endif { "rate", "Generate output at frame rate or field rate, available only for deinterlace mode", - OFFSET(field_rate), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, "rate" }, + OFFSET(field_rate), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS, .unit = "rate" }, { "frame", "Output at frame rate (one frame of output for each field-pair)", - 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, "rate" }, + 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, 0, FLAGS, .unit = "rate" }, { "field", "Output at field rate (one frame of output for each field)", - 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "rate" }, + 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, .unit = "rate" }, + + { "out_range", "Output color range", + OFFSET(color_range), AV_OPT_TYPE_INT, { .i64 = AVCOL_RANGE_UNSPECIFIED }, + AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_JPEG, FLAGS, .unit = "range" }, + { "full", "Full range", + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" }, + { "limited", "Limited range", + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" }, + { "jpeg", "Full range", + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" }, + { "mpeg", "Limited range", + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" }, + { "tv", "Limited range", + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" }, + { "pc", "Full range", + 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" }, + { "out_color_matrix", "Output color matrix coefficient set", + OFFSET(color_matrix_str), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + { "out_color_primaries", "Output color primaries", + OFFSET(color_primaries_str), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + { "out_color_transfer", "Output color transfer characteristics", + OFFSET(color_transfer_str), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, + + {"tonemap", "Perform tonemapping (0=disable tonemapping, 1=perform tonemapping if the input has HDR metadata)", OFFSET(tonemap), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, .flags = FLAGS}, { NULL } }; static int vpp_query_formats(AVFilterContext *ctx) { - int ret; + VPPContext *vpp = ctx->priv; + int ret, i = 0; static const enum AVPixelFormat in_pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NV12, @@ -726,17 +944,25 @@ static int vpp_query_formats(AVFilterContext *ctx) 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 - }; + static enum AVPixelFormat out_pix_fmts[4]; ret = ff_formats_ref(ff_make_format_list(in_pix_fmts), &ctx->inputs[0]->outcfg.formats); if (ret < 0) return ret; + + /* User specifies the output format */ + if (vpp->out_format == AV_PIX_FMT_NV12 || + vpp->out_format == AV_PIX_FMT_P010) + out_pix_fmts[i++] = vpp->out_format; + else { + out_pix_fmts[i++] = AV_PIX_FMT_NV12; + out_pix_fmts[i++] = AV_PIX_FMT_P010; + } + + out_pix_fmts[i++] = AV_PIX_FMT_QSV; + out_pix_fmts[i++] = AV_PIX_FMT_NONE; + return ff_formats_ref(ff_make_format_list(out_pix_fmts), &ctx->outputs[0]->incfg.formats); } @@ -753,16 +979,16 @@ static const AVOption qsvscale_options[] = { { "format", "Output pixel format", OFFSET(output_format_str), AV_OPT_TYPE_STRING, { .str = "same" }, .flags = FLAGS }, #if QSV_ONEVPL - { "mode", "scaling & format conversion mode (mode compute(3), vd(4) and ve(5) are only available on some platforms)", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 5, FLAGS, "mode"}, + { "mode", "scaling & format conversion mode (mode compute(3), vd(4) and ve(5) are only available on some platforms)", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 5, FLAGS, .unit = "mode"}, #else - { "mode", "scaling & format conversion mode", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT}, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY, FLAGS, "mode"}, + { "mode", "scaling & format conversion mode", OFFSET(scale_mode), AV_OPT_TYPE_INT, { .i64 = MFX_SCALING_MODE_DEFAULT}, MFX_SCALING_MODE_DEFAULT, MFX_SCALING_MODE_QUALITY, FLAGS, .unit = "mode"}, #endif - { "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"}, + { "low_power", "low power mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_LOWPOWER}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + { "hq", "high quality mode", 0, AV_OPT_TYPE_CONST, { .i64 = MFX_SCALING_MODE_QUALITY}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, #if QSV_ONEVPL - { "compute", "compute", 0, AV_OPT_TYPE_CONST, { .i64 = 3}, INT_MIN, INT_MAX, FLAGS, "mode"}, - { "vd", "vd", 0, AV_OPT_TYPE_CONST, { .i64 = 4}, INT_MIN, INT_MAX, FLAGS, "mode"}, - { "ve", "ve", 0, AV_OPT_TYPE_CONST, { .i64 = 5}, INT_MIN, INT_MAX, FLAGS, "mode"}, + { "compute", "compute", 0, AV_OPT_TYPE_CONST, { .i64 = 3}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + { "vd", "vd", 0, AV_OPT_TYPE_CONST, { .i64 = 4}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, + { "ve", "ve", 0, AV_OPT_TYPE_CONST, { .i64 = 5}, INT_MIN, INT_MAX, FLAGS, .unit = "mode"}, #endif { NULL }, @@ -785,9 +1011,9 @@ DEFINE_QSV_FILTER(qsvscale, scale, "scaling and format conversion", FILTER_SINGL #if CONFIG_DEINTERLACE_QSV_FILTER static const AVOption qsvdeint_options[] = { - { "mode", "set deinterlace mode", OFFSET(deinterlace), AV_OPT_TYPE_INT, {.i64 = MFX_DEINTERLACING_ADVANCED}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED, FLAGS, "mode"}, - { "bob", "bob algorithm", 0, AV_OPT_TYPE_CONST, {.i64 = MFX_DEINTERLACING_BOB}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED, FLAGS, "mode"}, - { "advanced", "Motion adaptive algorithm", 0, AV_OPT_TYPE_CONST, {.i64 = MFX_DEINTERLACING_ADVANCED}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED, FLAGS, "mode"}, + { "mode", "set deinterlace mode", OFFSET(deinterlace), AV_OPT_TYPE_INT, {.i64 = MFX_DEINTERLACING_ADVANCED}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED, FLAGS, .unit = "mode"}, + { "bob", "bob algorithm", 0, AV_OPT_TYPE_CONST, {.i64 = MFX_DEINTERLACING_BOB}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED, FLAGS, .unit = "mode"}, + { "advanced", "Motion adaptive algorithm", 0, AV_OPT_TYPE_CONST, {.i64 = MFX_DEINTERLACING_ADVANCED}, MFX_DEINTERLACING_BOB, MFX_DEINTERLACING_ADVANCED, FLAGS, .unit = "mode"}, { NULL }, }; diff --git a/libavfilter/vf_w3fdif.c b/libavfilter/vf_w3fdif.c index 512c8070c7f..6814905a4fa 100644 --- a/libavfilter/vf_w3fdif.c +++ b/libavfilter/vf_w3fdif.c @@ -26,7 +26,6 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "w3fdif.h" @@ -52,20 +51,20 @@ typedef struct W3FDIFContext { #define OFFSET(x) offsetof(W3FDIFContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM -#define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, unit } +#define CONST(name, help, val, u) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, 0, 0, FLAGS, .unit = u } static const AVOption w3fdif_options[] = { - { "filter", "specify the filter", OFFSET(filter), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "filter" }, + { "filter", "specify the filter", OFFSET(filter), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "filter" }, CONST("simple", NULL, 0, "filter"), CONST("complex", NULL, 1, "filter"), - { "mode", "specify the interlacing mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "mode"}, + { "mode", "specify the interlacing mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "mode"}, CONST("frame", "send one frame for each frame", 0, "mode"), CONST("field", "send one frame for each field", 1, "mode"), - { "parity", "specify the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=-1}, -1, 1, FLAGS, "parity" }, + { "parity", "specify the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=-1}, -1, 1, FLAGS, .unit = "parity" }, CONST("tff", "assume top field first", 0, "parity"), CONST("bff", "assume bottom field first", 1, "parity"), CONST("auto", "auto detect parity", -1, "parity"), - { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "deint" }, + { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "deint" }, CONST("all", "deinterlace all frames", 0, "deint"), CONST("interlaced", "only deinterlace frames marked as interlaced", 1, "deint"), { NULL } @@ -379,8 +378,8 @@ static int deinterlace_plane_slice(AVFilterContext *ctx, void *arg, const int start = (height * jobnr) / nb_jobs; const int end = (height * (jobnr+1)) / nb_jobs; const int max = s->max; - const int interlaced = cur->interlaced_frame; - const int tff = s->field == (s->parity == -1 ? interlaced ? cur->top_field_first : 1 : + const int interlaced = !!(cur->flags & AV_FRAME_FLAG_INTERLACED); + const int tff = s->field == (s->parity == -1 ? interlaced ? !!(cur->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 1 : s->parity ^ 1); int j, y_in, y_out; @@ -486,7 +485,12 @@ static int filter(AVFilterContext *ctx, int is_second) if (!out) return AVERROR(ENOMEM); av_frame_copy_props(out, s->cur); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags &= ~AV_FRAME_FLAG_INTERLACED; if (!is_second) { if (out->pts != AV_NOPTS_VALUE) @@ -533,7 +537,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) if (!s->prev) return 0; - if ((s->deint && !s->cur->interlaced_frame) || ctx->is_disabled) { + if ((s->deint && !(s->cur->flags & AV_FRAME_FLAG_INTERLACED)) || ctx->is_disabled) { AVFrame *out = av_frame_clone(s->cur); if (!out) return AVERROR(ENOMEM); diff --git a/libavfilter/vf_waveform.c b/libavfilter/vf_waveform.c index 37eb0679d0d..e004ea84f12 100644 --- a/libavfilter/vf_waveform.c +++ b/libavfilter/vf_waveform.c @@ -120,6 +120,7 @@ typedef struct WaveformContext { float ftint[2]; int tint[2]; int fitmode; + int input; int (*waveform_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); @@ -138,64 +139,67 @@ typedef struct WaveformContext { #define TFLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption waveform_options[] = { - { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "mode" }, - { "m", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, "mode" }, - { "row", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "mode" }, - { "column", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "mode" }, + { "mode", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "mode" }, + { "m", "set mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "mode" }, + { "row", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "mode" }, + { "column", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "mode" }, { "intensity", "set intensity", OFFSET(fintensity), AV_OPT_TYPE_FLOAT, {.dbl=0.04}, 0, 1, TFLAGS }, { "i", "set intensity", OFFSET(fintensity), AV_OPT_TYPE_FLOAT, {.dbl=0.04}, 0, 1, TFLAGS }, { "mirror", "set mirroring", OFFSET(mirror), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, { "r", "set mirroring", OFFSET(mirror), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, FLAGS }, - { "display", "set display mode", OFFSET(display), AV_OPT_TYPE_INT, {.i64=STACK}, 0, NB_DISPLAYS-1, FLAGS, "display" }, - { "d", "set display mode", OFFSET(display), AV_OPT_TYPE_INT, {.i64=STACK}, 0, NB_DISPLAYS-1, FLAGS, "display" }, - { "overlay", NULL, 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY}, 0, 0, FLAGS, "display" }, - { "stack", NULL, 0, AV_OPT_TYPE_CONST, {.i64=STACK}, 0, 0, FLAGS, "display" }, - { "parade", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PARADE}, 0, 0, FLAGS, "display" }, + { "display", "set display mode", OFFSET(display), AV_OPT_TYPE_INT, {.i64=STACK}, 0, NB_DISPLAYS-1, FLAGS, .unit = "display" }, + { "d", "set display mode", OFFSET(display), AV_OPT_TYPE_INT, {.i64=STACK}, 0, NB_DISPLAYS-1, FLAGS, .unit = "display" }, + { "overlay", NULL, 0, AV_OPT_TYPE_CONST, {.i64=OVERLAY}, 0, 0, FLAGS, .unit = "display" }, + { "stack", NULL, 0, AV_OPT_TYPE_CONST, {.i64=STACK}, 0, 0, FLAGS, .unit = "display" }, + { "parade", NULL, 0, AV_OPT_TYPE_CONST, {.i64=PARADE}, 0, 0, FLAGS, .unit = "display" }, { "components", "set components to display", OFFSET(pcomp), AV_OPT_TYPE_INT, {.i64=1}, 1, 15, FLAGS }, { "c", "set components to display", OFFSET(pcomp), AV_OPT_TYPE_INT, {.i64=1}, 1, 15, FLAGS }, - { "envelope", "set envelope to display", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, TFLAGS, "envelope" }, - { "e", "set envelope to display", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, TFLAGS, "envelope" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, TFLAGS, "envelope" }, - { "instant", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, TFLAGS, "envelope" }, - { "peak", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, TFLAGS, "envelope" }, - { "peak+instant", NULL, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, TFLAGS, "envelope" }, - { "filter", "set filter", OFFSET(filter), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_FILTERS-1, FLAGS, "filter" }, - { "f", "set filter", OFFSET(filter), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_FILTERS-1, FLAGS, "filter" }, - { "lowpass", NULL, 0, AV_OPT_TYPE_CONST, {.i64=LOWPASS}, 0, 0, FLAGS, "filter" }, - { "flat" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, "filter" }, - { "aflat" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=AFLAT}, 0, 0, FLAGS, "filter" }, - { "chroma", NULL, 0, AV_OPT_TYPE_CONST, {.i64=CHROMA}, 0, 0, FLAGS, "filter" }, - { "color", NULL, 0, AV_OPT_TYPE_CONST, {.i64=COLOR}, 0, 0, FLAGS, "filter" }, - { "acolor", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ACOLOR}, 0, 0, FLAGS, "filter" }, - { "xflat", NULL, 0, AV_OPT_TYPE_CONST, {.i64=XFLAT}, 0, 0, FLAGS, "filter" }, - { "yflat", NULL, 0, AV_OPT_TYPE_CONST, {.i64=YFLAT}, 0, 0, FLAGS, "filter" }, - { "graticule", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_GRATICULES-1, FLAGS, "graticule" }, - { "g", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_GRATICULES-1, FLAGS, "graticule" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_NONE}, 0, 0, FLAGS, "graticule" }, - { "green", NULL, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_GREEN}, 0, 0, FLAGS, "graticule" }, - { "orange", NULL, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_ORANGE}, 0, 0, FLAGS, "graticule" }, - { "invert", NULL, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_INVERT}, 0, 0, FLAGS, "graticule" }, + { "envelope", "set envelope to display", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, TFLAGS, .unit = "envelope" }, + { "e", "set envelope to display", OFFSET(envelope), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, TFLAGS, .unit = "envelope" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, TFLAGS, .unit = "envelope" }, + { "instant", NULL, 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, TFLAGS, .unit = "envelope" }, + { "peak", NULL, 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, TFLAGS, .unit = "envelope" }, + { "peak+instant", NULL, 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, TFLAGS, .unit = "envelope" }, + { "filter", "set filter", OFFSET(filter), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_FILTERS-1, FLAGS, .unit = "filter" }, + { "f", "set filter", OFFSET(filter), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_FILTERS-1, FLAGS, .unit = "filter" }, + { "lowpass", NULL, 0, AV_OPT_TYPE_CONST, {.i64=LOWPASS}, 0, 0, FLAGS, .unit = "filter" }, + { "flat" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=FLAT}, 0, 0, FLAGS, .unit = "filter" }, + { "aflat" , NULL, 0, AV_OPT_TYPE_CONST, {.i64=AFLAT}, 0, 0, FLAGS, .unit = "filter" }, + { "chroma", NULL, 0, AV_OPT_TYPE_CONST, {.i64=CHROMA}, 0, 0, FLAGS, .unit = "filter" }, + { "color", NULL, 0, AV_OPT_TYPE_CONST, {.i64=COLOR}, 0, 0, FLAGS, .unit = "filter" }, + { "acolor", NULL, 0, AV_OPT_TYPE_CONST, {.i64=ACOLOR}, 0, 0, FLAGS, .unit = "filter" }, + { "xflat", NULL, 0, AV_OPT_TYPE_CONST, {.i64=XFLAT}, 0, 0, FLAGS, .unit = "filter" }, + { "yflat", NULL, 0, AV_OPT_TYPE_CONST, {.i64=YFLAT}, 0, 0, FLAGS, .unit = "filter" }, + { "graticule", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_GRATICULES-1, FLAGS, .unit = "graticule" }, + { "g", "set graticule", OFFSET(graticule), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_GRATICULES-1, FLAGS, .unit = "graticule" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_NONE}, 0, 0, FLAGS, .unit = "graticule" }, + { "green", NULL, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_GREEN}, 0, 0, FLAGS, .unit = "graticule" }, + { "orange", NULL, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_ORANGE}, 0, 0, FLAGS, .unit = "graticule" }, + { "invert", NULL, 0, AV_OPT_TYPE_CONST, {.i64=GRAT_INVERT}, 0, 0, FLAGS, .unit = "graticule" }, { "opacity", "set graticule opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, TFLAGS }, { "o", "set graticule opacity", OFFSET(opacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, TFLAGS }, - { "flags", "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=1}, 0, 3, TFLAGS, "flags" }, - { "fl", "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=1}, 0, 3, TFLAGS, "flags" }, - { "numbers", "draw numbers", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, TFLAGS, "flags" }, - { "dots", "draw dots instead of lines", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, TFLAGS, "flags" }, - { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_SCALES-1, FLAGS, "scale" }, - { "s", "set scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_SCALES-1, FLAGS, "scale" }, - { "digital", NULL, 0, AV_OPT_TYPE_CONST, {.i64=DIGITAL}, 0, 0, FLAGS, "scale" }, - { "millivolts", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MILLIVOLTS}, 0, 0, FLAGS, "scale" }, - { "ire", NULL, 0, AV_OPT_TYPE_CONST, {.i64=IRE}, 0, 0, FLAGS, "scale" }, + { "flags", "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=1}, 0, 3, TFLAGS, .unit = "flags" }, + { "fl", "set graticule flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64=1}, 0, 3, TFLAGS, .unit = "flags" }, + { "numbers", "draw numbers", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, TFLAGS, .unit = "flags" }, + { "dots", "draw dots instead of lines", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, TFLAGS, .unit = "flags" }, + { "scale", "set scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_SCALES-1, FLAGS, .unit = "scale" }, + { "s", "set scale", OFFSET(scale), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_SCALES-1, FLAGS, .unit = "scale" }, + { "digital", NULL, 0, AV_OPT_TYPE_CONST, {.i64=DIGITAL}, 0, 0, FLAGS, .unit = "scale" }, + { "millivolts", NULL, 0, AV_OPT_TYPE_CONST, {.i64=MILLIVOLTS}, 0, 0, FLAGS, .unit = "scale" }, + { "ire", NULL, 0, AV_OPT_TYPE_CONST, {.i64=IRE}, 0, 0, FLAGS, .unit = "scale" }, { "bgopacity", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, TFLAGS }, { "b", "set background opacity", OFFSET(bgopacity), AV_OPT_TYPE_FLOAT, {.dbl=0.75}, 0, 1, TFLAGS }, { "tint0", "set 1st tint", OFFSET(ftint[0]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, TFLAGS}, { "t0", "set 1st tint", OFFSET(ftint[0]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, TFLAGS}, { "tint1", "set 2nd tint", OFFSET(ftint[1]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, TFLAGS}, { "t1", "set 2nd tint", OFFSET(ftint[1]), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, TFLAGS}, - { "fitmode", "set fit mode", OFFSET(fitmode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_FITMODES-1, FLAGS, "fitmode" }, - { "fm", "set fit mode", OFFSET(fitmode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_FITMODES-1, FLAGS, "fitmode" }, - { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_NONE}, 0, 0, FLAGS, "fitmode" }, - { "size", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_SIZE}, 0, 0, FLAGS, "fitmode" }, + { "fitmode", "set fit mode", OFFSET(fitmode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_FITMODES-1, FLAGS, .unit = "fitmode" }, + { "fm", "set fit mode", OFFSET(fitmode), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_FITMODES-1, FLAGS, .unit = "fitmode" }, + { "none", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_NONE}, 0, 0, FLAGS, .unit = "fitmode" }, + { "size", NULL, 0, AV_OPT_TYPE_CONST, {.i64=FM_SIZE}, 0, 0, FLAGS, .unit = "fitmode" }, + { "input", "set input formats selection", OFFSET(input), AV_OPT_TYPE_INT, {.i64=1}, 0, 1, FLAGS, .unit = "input" }, + { "all", "try to select from all available formats", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "input" }, + { "first", "pick first available format", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "input" }, { NULL } }; @@ -356,7 +360,7 @@ static int query_formats(AVFilterContext *ctx) depth2 = desc2->comp[0].depth; if (ncomp != ncomp2 || depth != depth2) return AVERROR(EAGAIN); - for (i = 1; i < avff->nb_formats; i++) { + for (i = 1; i < avff->nb_formats && !s->input; i++) { desc = av_pix_fmt_desc_get(avff->formats[i]); if (rgb != (desc->flags & AV_PIX_FMT_FLAG_RGB) || depth != desc->comp[0].depth) @@ -745,7 +749,7 @@ static av_always_inline void lowpass16(WaveformContext *s, dst_data += dst_linesize * step; } - if (s->display != OVERLAY && column && !s->rgb) { + if (s->display != OVERLAY && column && !s->rgb && out->data[1] && out->data[2]) { const int mult = s->max / 256; const int bg = s->bg_color[0] * mult; const int t0 = s->tint[0]; @@ -769,7 +773,7 @@ static av_always_inline void lowpass16(WaveformContext *s, dst0 += dst_linesize; dst1 += dst_linesize; } - } else if (s->display != OVERLAY && !s->rgb) { + } else if (s->display != OVERLAY && !s->rgb && out->data[1] && out->data[2]) { const int mult = s->max / 256; const int bg = s->bg_color[0] * mult; const int t0 = s->tint[0]; diff --git a/libavfilter/vf_weave.c b/libavfilter/vf_weave.c index 2bd3994e5ee..f0e8b0927a7 100644 --- a/libavfilter/vf_weave.c +++ b/libavfilter/vf_weave.c @@ -22,7 +22,9 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "formats.h" #include "internal.h" +#include "video.h" typedef struct WeaveContext { const AVClass *class; @@ -30,6 +32,7 @@ typedef struct WeaveContext { int double_weave; int nb_planes; int planeheight[4]; + int outheight[4]; int linesize[4]; AVFrame *prev; @@ -39,11 +42,11 @@ typedef struct WeaveContext { #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM static const AVOption weave_options[] = { - { "first_field", "set first field", OFFSET(first_field), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "field"}, - { "top", "set top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"}, - { "t", "set top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "field"}, - { "bottom", "set bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"}, - { "b", "set bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "field"}, + { "first_field", "set first field", OFFSET(first_field), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "field"}, + { "top", "set top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "field"}, + { "t", "set top field first", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "field"}, + { "bottom", "set bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "field"}, + { "b", "set bottom field first", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "field"}, { NULL } }; @@ -79,6 +82,9 @@ static int config_props_output(AVFilterLink *outlink) s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h); s->planeheight[0] = s->planeheight[3] = inlink->h; + s->outheight[1] = s->outheight[2] = AV_CEIL_RSHIFT(2*inlink->h, desc->log2_chroma_h); + s->outheight[0] = s->outheight[3] = 2*inlink->h; + s->nb_planes = av_pix_fmt_count_planes(inlink->format); return 0; @@ -104,19 +110,20 @@ static int weave_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) const int height = s->planeheight[i]; const int start = (height * jobnr) / nb_jobs; const int end = (height * (jobnr+1)) / nb_jobs; + const int compensation = 2*end > s->outheight[i]; av_image_copy_plane(out->data[i] + out->linesize[i] * field1 + out->linesize[i] * start * 2, out->linesize[i] * 2, in->data[i] + start * in->linesize[i], in->linesize[i], - s->linesize[i], end - start); + s->linesize[i], end - start - compensation * field1); av_image_copy_plane(out->data[i] + out->linesize[i] * field2 + out->linesize[i] * start * 2, out->linesize[i] * 2, s->prev->data[i] + start * s->prev->linesize[i], s->prev->linesize[i], - s->linesize[i], end - start); + s->linesize[i], end - start - compensation * field2); } return 0; @@ -148,8 +155,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) FFMIN(s->planeheight[1], ff_filter_get_nb_threads(ctx))); out->pts = s->double_weave ? s->prev->pts : in->pts / 2; +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS out->interlaced_frame = 1; out->top_field_first = !s->first_field; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + out->flags |= AV_FRAME_FLAG_INTERLACED; + if (s->first_field) + out->flags &= ~AV_FRAME_FLAG_TOP_FIELD_FIRST; + else + out->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST; if (!s->double_weave) av_frame_free(&in); diff --git a/libavfilter/vf_xbr.c b/libavfilter/vf_xbr.c index 6ae5310c9f4..1750da043e0 100644 --- a/libavfilter/vf_xbr.c +++ b/libavfilter/vf_xbr.c @@ -31,6 +31,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "internal.h" +#include "video.h" #define LB_MASK 0x00FEFEFE #define RED_BLUE_MASK 0x00FF00FF diff --git a/libavfilter/vf_xfade.c b/libavfilter/vf_xfade.c index 9f669273657..f61c7083dca 100644 --- a/libavfilter/vf_xfade.c +++ b/libavfilter/vf_xfade.c @@ -18,12 +18,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/imgutils.h" #include "libavutil/eval.h" #include "libavutil/opt.h" +#include "libavutil/pixdesc.h" #include "libavutil/pixfmt.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "filters.h" #include "video.h" @@ -76,6 +75,18 @@ enum XFadeTransitions { ZOOMIN, FADEFAST, FADESLOW, + HLWIND, + HRWIND, + VUWIND, + VDWIND, + COVERLEFT, + COVERRIGHT, + COVERUP, + COVERDOWN, + REVEALLEFT, + REVEALRIGHT, + REVEALUP, + REVEALDOWN, NB_TRANSITIONS, }; @@ -91,14 +102,23 @@ typedef struct XFadeContext { int depth; int is_rgb; + // PTS when the fade should start (in first inputs timebase) + int64_t start_pts; + + // PTS offset between first and second input + int64_t inputs_offset_pts; + + // Duration of the transition int64_t duration_pts; - int64_t offset_pts; - int64_t first_pts; - int64_t last_pts; + + // Current PTS of the first input int64_t pts; - int xfade_is_over; - int need_second; - int eof[2]; + + // If frames are currently just passed through unmodified, + // like before and after the actual transition. + int passthrough; + + int status[2]; AVFrame *xf[2]; int max_value; uint16_t black[4]; @@ -149,54 +169,66 @@ static av_cold void uninit(AVFilterContext *ctx) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) static const AVOption xfade_options[] = { - { "transition", "set cross fade transition", OFFSET(transition), AV_OPT_TYPE_INT, {.i64=FADE}, -1, NB_TRANSITIONS-1, FLAGS, "transition" }, - { "custom", "custom transition", 0, AV_OPT_TYPE_CONST, {.i64=CUSTOM}, 0, 0, FLAGS, "transition" }, - { "fade", "fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADE}, 0, 0, FLAGS, "transition" }, - { "wipeleft", "wipe left transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPELEFT}, 0, 0, FLAGS, "transition" }, - { "wiperight", "wipe right transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPERIGHT}, 0, 0, FLAGS, "transition" }, - { "wipeup", "wipe up transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEUP}, 0, 0, FLAGS, "transition" }, - { "wipedown", "wipe down transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEDOWN}, 0, 0, FLAGS, "transition" }, - { "slideleft", "slide left transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDELEFT}, 0, 0, FLAGS, "transition" }, - { "slideright", "slide right transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDERIGHT}, 0, 0, FLAGS, "transition" }, - { "slideup", "slide up transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEUP}, 0, 0, FLAGS, "transition" }, - { "slidedown", "slide down transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEDOWN}, 0, 0, FLAGS, "transition" }, - { "circlecrop", "circle crop transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLECROP}, 0, 0, FLAGS, "transition" }, - { "rectcrop", "rect crop transition", 0, AV_OPT_TYPE_CONST, {.i64=RECTCROP}, 0, 0, FLAGS, "transition" }, - { "distance", "distance transition", 0, AV_OPT_TYPE_CONST, {.i64=DISTANCE}, 0, 0, FLAGS, "transition" }, - { "fadeblack", "fadeblack transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEBLACK}, 0, 0, FLAGS, "transition" }, - { "fadewhite", "fadewhite transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEWHITE}, 0, 0, FLAGS, "transition" }, - { "radial", "radial transition", 0, AV_OPT_TYPE_CONST, {.i64=RADIAL}, 0, 0, FLAGS, "transition" }, - { "smoothleft", "smoothleft transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHLEFT}, 0, 0, FLAGS, "transition" }, - { "smoothright","smoothright transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHRIGHT},0, 0, FLAGS, "transition" }, - { "smoothup", "smoothup transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHUP}, 0, 0, FLAGS, "transition" }, - { "smoothdown", "smoothdown transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHDOWN}, 0, 0, FLAGS, "transition" }, - { "circleopen", "circleopen transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLEOPEN}, 0, 0, FLAGS, "transition" }, - { "circleclose","circleclose transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLECLOSE},0, 0, FLAGS, "transition" }, - { "vertopen", "vert open transition", 0, AV_OPT_TYPE_CONST, {.i64=VERTOPEN}, 0, 0, FLAGS, "transition" }, - { "vertclose", "vert close transition", 0, AV_OPT_TYPE_CONST, {.i64=VERTCLOSE}, 0, 0, FLAGS, "transition" }, - { "horzopen", "horz open transition", 0, AV_OPT_TYPE_CONST, {.i64=HORZOPEN}, 0, 0, FLAGS, "transition" }, - { "horzclose", "horz close transition", 0, AV_OPT_TYPE_CONST, {.i64=HORZCLOSE}, 0, 0, FLAGS, "transition" }, - { "dissolve", "dissolve transition", 0, AV_OPT_TYPE_CONST, {.i64=DISSOLVE}, 0, 0, FLAGS, "transition" }, - { "pixelize", "pixelize transition", 0, AV_OPT_TYPE_CONST, {.i64=PIXELIZE}, 0, 0, FLAGS, "transition" }, - { "diagtl", "diag tl transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGTL}, 0, 0, FLAGS, "transition" }, - { "diagtr", "diag tr transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGTR}, 0, 0, FLAGS, "transition" }, - { "diagbl", "diag bl transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGBL}, 0, 0, FLAGS, "transition" }, - { "diagbr", "diag br transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGBR}, 0, 0, FLAGS, "transition" }, - { "hlslice", "hl slice transition", 0, AV_OPT_TYPE_CONST, {.i64=HLSLICE}, 0, 0, FLAGS, "transition" }, - { "hrslice", "hr slice transition", 0, AV_OPT_TYPE_CONST, {.i64=HRSLICE}, 0, 0, FLAGS, "transition" }, - { "vuslice", "vu slice transition", 0, AV_OPT_TYPE_CONST, {.i64=VUSLICE}, 0, 0, FLAGS, "transition" }, - { "vdslice", "vd slice transition", 0, AV_OPT_TYPE_CONST, {.i64=VDSLICE}, 0, 0, FLAGS, "transition" }, - { "hblur", "hblur transition", 0, AV_OPT_TYPE_CONST, {.i64=HBLUR}, 0, 0, FLAGS, "transition" }, - { "fadegrays", "fadegrays transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEGRAYS}, 0, 0, FLAGS, "transition" }, - { "wipetl", "wipe tl transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPETL}, 0, 0, FLAGS, "transition" }, - { "wipetr", "wipe tr transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPETR}, 0, 0, FLAGS, "transition" }, - { "wipebl", "wipe bl transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEBL}, 0, 0, FLAGS, "transition" }, - { "wipebr", "wipe br transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEBR}, 0, 0, FLAGS, "transition" }, - { "squeezeh", "squeeze h transition", 0, AV_OPT_TYPE_CONST, {.i64=SQUEEZEH}, 0, 0, FLAGS, "transition" }, - { "squeezev", "squeeze v transition", 0, AV_OPT_TYPE_CONST, {.i64=SQUEEZEV}, 0, 0, FLAGS, "transition" }, - { "zoomin", "zoom in transition", 0, AV_OPT_TYPE_CONST, {.i64=ZOOMIN}, 0, 0, FLAGS, "transition" }, - { "fadefast", "fast fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEFAST}, 0, 0, FLAGS, "transition" }, - { "fadeslow", "slow fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADESLOW}, 0, 0, FLAGS, "transition" }, + { "transition", "set cross fade transition", OFFSET(transition), AV_OPT_TYPE_INT, {.i64=FADE}, -1, NB_TRANSITIONS-1, FLAGS, .unit = "transition" }, + { "custom", "custom transition", 0, AV_OPT_TYPE_CONST, {.i64=CUSTOM}, 0, 0, FLAGS, .unit = "transition" }, + { "fade", "fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADE}, 0, 0, FLAGS, .unit = "transition" }, + { "wipeleft", "wipe left transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPELEFT}, 0, 0, FLAGS, .unit = "transition" }, + { "wiperight", "wipe right transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPERIGHT}, 0, 0, FLAGS, .unit = "transition" }, + { "wipeup", "wipe up transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEUP}, 0, 0, FLAGS, .unit = "transition" }, + { "wipedown", "wipe down transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEDOWN}, 0, 0, FLAGS, .unit = "transition" }, + { "slideleft", "slide left transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDELEFT}, 0, 0, FLAGS, .unit = "transition" }, + { "slideright", "slide right transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDERIGHT}, 0, 0, FLAGS, .unit = "transition" }, + { "slideup", "slide up transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEUP}, 0, 0, FLAGS, .unit = "transition" }, + { "slidedown", "slide down transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEDOWN}, 0, 0, FLAGS, .unit = "transition" }, + { "circlecrop", "circle crop transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLECROP}, 0, 0, FLAGS, .unit = "transition" }, + { "rectcrop", "rect crop transition", 0, AV_OPT_TYPE_CONST, {.i64=RECTCROP}, 0, 0, FLAGS, .unit = "transition" }, + { "distance", "distance transition", 0, AV_OPT_TYPE_CONST, {.i64=DISTANCE}, 0, 0, FLAGS, .unit = "transition" }, + { "fadeblack", "fadeblack transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEBLACK}, 0, 0, FLAGS, .unit = "transition" }, + { "fadewhite", "fadewhite transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEWHITE}, 0, 0, FLAGS, .unit = "transition" }, + { "radial", "radial transition", 0, AV_OPT_TYPE_CONST, {.i64=RADIAL}, 0, 0, FLAGS, .unit = "transition" }, + { "smoothleft", "smoothleft transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHLEFT}, 0, 0, FLAGS, .unit = "transition" }, + { "smoothright","smoothright transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHRIGHT},0, 0, FLAGS, .unit = "transition" }, + { "smoothup", "smoothup transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHUP}, 0, 0, FLAGS, .unit = "transition" }, + { "smoothdown", "smoothdown transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHDOWN}, 0, 0, FLAGS, .unit = "transition" }, + { "circleopen", "circleopen transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLEOPEN}, 0, 0, FLAGS, .unit = "transition" }, + { "circleclose","circleclose transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLECLOSE},0, 0, FLAGS, .unit = "transition" }, + { "vertopen", "vert open transition", 0, AV_OPT_TYPE_CONST, {.i64=VERTOPEN}, 0, 0, FLAGS, .unit = "transition" }, + { "vertclose", "vert close transition", 0, AV_OPT_TYPE_CONST, {.i64=VERTCLOSE}, 0, 0, FLAGS, .unit = "transition" }, + { "horzopen", "horz open transition", 0, AV_OPT_TYPE_CONST, {.i64=HORZOPEN}, 0, 0, FLAGS, .unit = "transition" }, + { "horzclose", "horz close transition", 0, AV_OPT_TYPE_CONST, {.i64=HORZCLOSE}, 0, 0, FLAGS, .unit = "transition" }, + { "dissolve", "dissolve transition", 0, AV_OPT_TYPE_CONST, {.i64=DISSOLVE}, 0, 0, FLAGS, .unit = "transition" }, + { "pixelize", "pixelize transition", 0, AV_OPT_TYPE_CONST, {.i64=PIXELIZE}, 0, 0, FLAGS, .unit = "transition" }, + { "diagtl", "diag tl transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGTL}, 0, 0, FLAGS, .unit = "transition" }, + { "diagtr", "diag tr transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGTR}, 0, 0, FLAGS, .unit = "transition" }, + { "diagbl", "diag bl transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGBL}, 0, 0, FLAGS, .unit = "transition" }, + { "diagbr", "diag br transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGBR}, 0, 0, FLAGS, .unit = "transition" }, + { "hlslice", "hl slice transition", 0, AV_OPT_TYPE_CONST, {.i64=HLSLICE}, 0, 0, FLAGS, .unit = "transition" }, + { "hrslice", "hr slice transition", 0, AV_OPT_TYPE_CONST, {.i64=HRSLICE}, 0, 0, FLAGS, .unit = "transition" }, + { "vuslice", "vu slice transition", 0, AV_OPT_TYPE_CONST, {.i64=VUSLICE}, 0, 0, FLAGS, .unit = "transition" }, + { "vdslice", "vd slice transition", 0, AV_OPT_TYPE_CONST, {.i64=VDSLICE}, 0, 0, FLAGS, .unit = "transition" }, + { "hblur", "hblur transition", 0, AV_OPT_TYPE_CONST, {.i64=HBLUR}, 0, 0, FLAGS, .unit = "transition" }, + { "fadegrays", "fadegrays transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEGRAYS}, 0, 0, FLAGS, .unit = "transition" }, + { "wipetl", "wipe tl transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPETL}, 0, 0, FLAGS, .unit = "transition" }, + { "wipetr", "wipe tr transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPETR}, 0, 0, FLAGS, .unit = "transition" }, + { "wipebl", "wipe bl transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEBL}, 0, 0, FLAGS, .unit = "transition" }, + { "wipebr", "wipe br transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEBR}, 0, 0, FLAGS, .unit = "transition" }, + { "squeezeh", "squeeze h transition", 0, AV_OPT_TYPE_CONST, {.i64=SQUEEZEH}, 0, 0, FLAGS, .unit = "transition" }, + { "squeezev", "squeeze v transition", 0, AV_OPT_TYPE_CONST, {.i64=SQUEEZEV}, 0, 0, FLAGS, .unit = "transition" }, + { "zoomin", "zoom in transition", 0, AV_OPT_TYPE_CONST, {.i64=ZOOMIN}, 0, 0, FLAGS, .unit = "transition" }, + { "fadefast", "fast fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEFAST}, 0, 0, FLAGS, .unit = "transition" }, + { "fadeslow", "slow fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADESLOW}, 0, 0, FLAGS, .unit = "transition" }, + { "hlwind", "hl wind transition", 0, AV_OPT_TYPE_CONST, {.i64=HLWIND}, 0, 0, FLAGS, .unit = "transition" }, + { "hrwind", "hr wind transition", 0, AV_OPT_TYPE_CONST, {.i64=HRWIND}, 0, 0, FLAGS, .unit = "transition" }, + { "vuwind", "vu wind transition", 0, AV_OPT_TYPE_CONST, {.i64=VUWIND}, 0, 0, FLAGS, .unit = "transition" }, + { "vdwind", "vd wind transition", 0, AV_OPT_TYPE_CONST, {.i64=VDWIND}, 0, 0, FLAGS, .unit = "transition" }, + { "coverleft", "cover left transition", 0, AV_OPT_TYPE_CONST, {.i64=COVERLEFT}, 0, 0, FLAGS, .unit = "transition" }, + { "coverright", "cover right transition", 0, AV_OPT_TYPE_CONST, {.i64=COVERRIGHT}, 0, 0, FLAGS, .unit = "transition" }, + { "coverup", "cover up transition", 0, AV_OPT_TYPE_CONST, {.i64=COVERUP}, 0, 0, FLAGS, .unit = "transition" }, + { "coverdown", "cover down transition", 0, AV_OPT_TYPE_CONST, {.i64=COVERDOWN}, 0, 0, FLAGS, .unit = "transition" }, + { "revealleft", "reveal left transition", 0, AV_OPT_TYPE_CONST, {.i64=REVEALLEFT}, 0, 0, FLAGS, .unit = "transition" }, + { "revealright","reveal right transition",0, AV_OPT_TYPE_CONST, {.i64=REVEALRIGHT},0, 0, FLAGS, .unit = "transition" }, + { "revealup", "reveal up transition", 0, AV_OPT_TYPE_CONST, {.i64=REVEALUP}, 0, 0, FLAGS, .unit = "transition" }, + { "revealdown", "reveal down transition", 0, AV_OPT_TYPE_CONST, {.i64=REVEALDOWN}, 0, 0, FLAGS, .unit = "transition" }, { "duration", "set cross fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=1000000}, 0, 60000000, FLAGS }, { "offset", "set cross fade start relative to first input stream", OFFSET(offset), AV_OPT_TYPE_DURATION, {.i64=0}, INT64_MIN, INT64_MAX, FLAGS }, { "expr", "set expression for custom transition", OFFSET(custom_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS }, @@ -213,9 +245,10 @@ static void custom##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ \ double values[VAR_VARS_NB]; \ - values[VAR_W] = out->width; \ + values[VAR_W] = width; \ values[VAR_H] = out->height; \ values[VAR_PROGRESS] = progress; \ \ @@ -228,7 +261,7 @@ static void custom##name##_transition(AVFilterContext *ctx, \ for (int y = 0; y < height; y++) { \ values[VAR_Y] = slice_start + y; \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ values[VAR_X] = x; \ values[VAR_A] = xf0[x]; \ values[VAR_B] = xf1[x]; \ @@ -272,6 +305,7 @@ static void fade##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ \ for (int p = 0; p < s->nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \ @@ -279,7 +313,7 @@ static void fade##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = mix(xf0[x], xf1[x], progress); \ } \ \ @@ -301,7 +335,8 @@ static void wipeleft##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ - const int z = out->width * progress; \ + const int width = out->width; \ + const int z = width * progress; \ \ for (int p = 0; p < s->nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \ @@ -309,7 +344,7 @@ static void wipeleft##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = x > z ? xf1[x] : xf0[x]; \ } \ \ @@ -331,7 +366,8 @@ static void wiperight##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ - const int z = out->width * (1.f - progress); \ + const int width = out->width; \ + const int z = width * (1.f - progress); \ \ for (int p = 0; p < s->nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \ @@ -339,7 +375,7 @@ static void wiperight##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = x > z ? xf0[x] : xf1[x]; \ } \ \ @@ -361,6 +397,7 @@ static void wipeup##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const int z = out->height * progress; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -369,7 +406,7 @@ static void wipeup##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = slice_start + y > z ? xf1[x] : xf0[x]; \ } \ \ @@ -391,6 +428,7 @@ static void wipedown##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const int z = out->height * (1.f - progress); \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -399,7 +437,7 @@ static void wipedown##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = slice_start + y > z ? xf0[x] : xf1[x]; \ } \ \ @@ -463,7 +501,7 @@ static void slideright##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ const int zx = z + x; \ const int zz = zx % width + width * (zx < 0); \ dst[x] = (zx >= 0) && (zx < width) ? xf1[zz] : xf0[zz]; \ @@ -487,6 +525,7 @@ static void slideup##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = out->height; \ + const int width = out->width; \ const int z = -progress * height; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -498,7 +537,7 @@ static void slideup##name##_transition(AVFilterContext *ctx, const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \ \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = (zy >= 0) && (zy < height) ? xf1[x] : xf0[x]; \ } \ \ @@ -518,6 +557,7 @@ static void slidedown##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = out->height; \ + const int width = out->width; \ const int z = progress * height; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -529,7 +569,7 @@ static void slidedown##name##_transition(AVFilterContext *ctx, const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \ \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = (zy >= 0) && (zy < height) ? xf1[x] : xf0[x]; \ } \ \ @@ -652,6 +692,7 @@ static void fadeblack##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const float phase = 0.2f; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -661,7 +702,7 @@ static void fadeblack##name##_transition(AVFilterContext *ctx, const int bg = s->black[p]; \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = mix(mix(xf0[x], bg, smoothstep(1.f-phase, 1.f, progress)), \ mix(bg, xf1[x], smoothstep(phase, 1.f, progress)), \ progress); \ @@ -685,6 +726,7 @@ static void fadewhite##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const float phase = 0.2f; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -694,7 +736,7 @@ static void fadewhite##name##_transition(AVFilterContext *ctx, const int bg = s->white[p]; \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = mix(mix(xf0[x], bg, smoothstep(1.f-phase, 1.f, progress)), \ mix(bg, xf1[x], smoothstep(phase, 1.f, progress)), \ progress); \ @@ -940,13 +982,14 @@ static void vertclose##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w2 = out->width / 2; \ \ for (int y = slice_start; y < slice_end; y++) { \ for (int x = 0; x < width; x++) { \ const float smooth = 1.f + fabsf((x - w2) / w2) - progress * 2.f; \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -967,13 +1010,14 @@ static void horzopen##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float h2 = out->height / 2; \ \ for (int y = slice_start; y < slice_end; y++) { \ const float smooth = 2.f - fabsf((y - h2) / h2) - progress * 2.f; \ for (int x = 0; x < width; x++) { \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -994,13 +1038,14 @@ static void horzclose##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float h2 = out->height / 2; \ \ for (int y = slice_start; y < slice_end; y++) { \ const float smooth = 1.f + fabsf((y - h2) / h2) - progress * 2.f; \ for (int x = 0; x < width; x++) { \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1028,12 +1073,13 @@ static void dissolve##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ \ for (int y = slice_start; y < slice_end; y++) { \ for (int x = 0; x < width; x++) { \ const float smooth = frand(x, y) * 2.f + progress * 2.f - 1.5f; \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1054,6 +1100,7 @@ static void pixelize##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int w = out->width; \ const int h = out->height; \ const float d = fminf(progress, 1.f - progress); \ @@ -1065,7 +1112,7 @@ static void pixelize##name##_transition(AVFilterContext *ctx, for (int x = 0; x < w; x++) { \ int sx = dist > 0.f ? FFMIN((floorf(x / sqx) + .5f) * sqx, w - 1) : x; \ int sy = dist > 0.f ? FFMIN((floorf(y / sqy) + .5f) * sqy, h - 1) : y; \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + sy * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + sy * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1086,6 +1133,7 @@ static void diagtl##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w = width; \ const float h = out->height; \ @@ -1094,7 +1142,7 @@ static void diagtl##name##_transition(AVFilterContext *ctx, for (int x = 0; x < width; x++) { \ const float smooth = 1.f + x / w * y / h - progress * 2.f; \ \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1115,6 +1163,7 @@ static void diagtr##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w = width; \ const float h = out->height; \ @@ -1123,7 +1172,7 @@ static void diagtr##name##_transition(AVFilterContext *ctx, for (int x = 0; x < width; x++) { \ const float smooth = 1.f + (w - 1 - x) / w * y / h - progress * 2.f; \ \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1144,6 +1193,7 @@ static void diagbl##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w = width; \ const float h = out->height; \ @@ -1152,7 +1202,7 @@ static void diagbl##name##_transition(AVFilterContext *ctx, for (int x = 0; x < width; x++) { \ const float smooth = 1.f + x / w * (h - 1 - y) / h - progress * 2.f; \ \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1173,6 +1223,7 @@ static void diagbr##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w = width; \ const float h = out->height; \ @@ -1182,7 +1233,7 @@ static void diagbr##name##_transition(AVFilterContext *ctx, const float smooth = 1.f + (w - 1 - x) / w * (h - 1 - y) / h - \ progress * 2.f; \ \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1203,6 +1254,7 @@ static void hlslice##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w = width; \ \ @@ -1211,7 +1263,7 @@ static void hlslice##name##_transition(AVFilterContext *ctx, const float smooth = smoothstep(-0.5f, 0.f, x / w - progress * 1.5f); \ const float ss = smooth <= fract(10.f * x / w) ? 0.f : 1.f; \ \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1232,6 +1284,7 @@ static void hrslice##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float w = width; \ \ @@ -1241,7 +1294,7 @@ static void hrslice##name##_transition(AVFilterContext *ctx, const float smooth = smoothstep(-0.5f, 0.f, xx - progress * 1.5f); \ const float ss = smooth <= fract(10.f * xx) ? 0.f : 1.f; \ \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1262,6 +1315,7 @@ static void vuslice##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float h = out->height; \ \ @@ -1270,7 +1324,7 @@ static void vuslice##name##_transition(AVFilterContext *ctx, const float ss = smooth <= fract(10.f * y / h) ? 0.f : 1.f; \ \ for (int x = 0; x < width; x++) { \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1291,6 +1345,7 @@ static void vdslice##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float h = out->height; \ \ @@ -1300,7 +1355,7 @@ static void vdslice##name##_transition(AVFilterContext *ctx, const float ss = smooth <= fract(10.f * yy) ? 0.f : 1.f; \ \ for (int x = 0; x < width; x++) { \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1321,12 +1376,13 @@ static void hblur##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ + const int nb_planes = s->nb_planes; \ const int width = out->width; \ const float prog = progress <= 0.5f ? progress * 2.f : (1.f - progress) * 2.f; \ const int size = 1 + (width / 2) * prog; \ \ for (int y = slice_start; y < slice_end; y++) { \ - for (int p = 0; p < s->nb_planes; p++) { \ + for (int p = 0; p < nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ @@ -1374,6 +1430,7 @@ static void fadegrays##name##_transition(AVFilterContext *ctx, for (int x = 0; x < width; x++) { \ int bg[2][4]; \ if (is_rgb) { \ + bg[0][0] = bg[1][0] = 0; \ for (int p = 0; p < s->nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + \ y * a->linesize[p]); \ @@ -1435,6 +1492,7 @@ static void wipetl##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const int zw = out->width * progress; \ const int zh = out->height * progress; \ \ @@ -1444,7 +1502,7 @@ static void wipetl##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = slice_start + y <= zh && \ x <= zw ? xf0[x] : xf1[x]; \ } \ @@ -1467,7 +1525,8 @@ static void wipetr##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ - const int zw = out->width * (1.f - progress); \ + const int width = out->width; \ + const int zw = width * (1.f - progress); \ const int zh = out->height * progress; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -1476,7 +1535,7 @@ static void wipetr##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = slice_start + y <= zh && \ x > zw ? xf0[x] : xf1[x]; \ } \ @@ -1499,7 +1558,8 @@ static void wipebl##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ - const int zw = out->width * progress; \ + const int width = out->width; \ + const int zw = width * progress; \ const int zh = out->height * (1.f - progress); \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -1508,7 +1568,7 @@ static void wipebl##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = slice_start + y > zh && \ x <= zw ? xf0[x] : xf1[x]; \ } \ @@ -1532,7 +1592,8 @@ static void wipebr##name##_transition(AVFilterContext *ctx, XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ const int zh = out->height * (1.f - progress); \ - const int zw = out->width * (1.f - progress); \ + const int width = out->width; \ + const int zw = width * (1.f - progress); \ \ for (int p = 0; p < s->nb_planes; p++) { \ const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \ @@ -1540,7 +1601,7 @@ static void wipebr##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = slice_start + y > zh && \ x > zw ? xf0[x] : xf1[x]; \ } \ @@ -1564,6 +1625,7 @@ static void squeezeh##name##_transition(AVFilterContext *ctx, XFadeContext *s = ctx->priv; \ const float h = out->height; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ \ for (int p = 0; p < s->nb_planes; p++) { \ const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \ @@ -1573,13 +1635,13 @@ static void squeezeh##name##_transition(AVFilterContext *ctx, const float z = .5f + ((slice_start + y) / h - .5f) / progress; \ \ if (z < 0.f || z > 1.f) { \ - for (int x = 0; x < out->width; x++) \ + for (int x = 0; x < width; x++) \ dst[x] = xf1[x]; \ } else { \ const int yy = lrintf(z * (h - 1.f)); \ const type *xf0 = (const type *)(a->data[p] + yy * a->linesize[p]); \ \ - for (int x = 0; x < out->width; x++) \ + for (int x = 0; x < width; x++) \ dst[x] = xf0[x]; \ } \ \ @@ -1599,7 +1661,8 @@ static void squeezev##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ - const float w = out->width; \ + const int width = out->width; \ + const float w = width; \ const int height = slice_end - slice_start; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -1608,7 +1671,7 @@ static void squeezev##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ const float z = .5f + (x / w - .5f) / progress; \ \ if (z < 0.f || z > 1.f) { \ @@ -1643,7 +1706,8 @@ static void zoomin##name##_transition(AVFilterContext *ctx, int slice_start, int slice_end, int jobnr) \ { \ XFadeContext *s = ctx->priv; \ - const float w = out->width; \ + const int width = out->width; \ + const float w = width; \ const float h = out->height; \ const float zf = smoothstep(0.5f, 1.f, progress); \ \ @@ -1653,7 +1717,7 @@ static void zoomin##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = slice_start; y < slice_end; y++) { \ - for (int x = 0; x < w; x++) { \ + for (int x = 0; x < width; x++) { \ float zv, u, v; \ int iu, iv; \ \ @@ -1682,6 +1746,7 @@ static void fadefast##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const float imax = 1.f / s->max_value; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -1690,7 +1755,7 @@ static void fadefast##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = mix(xf0[x], xf1[x], powf(progress, 1.f + \ logf(1.f+FFABS(xf0[x]-xf1[x])*imax)\ )); \ @@ -1714,6 +1779,7 @@ static void fadeslow##name##_transition(AVFilterContext *ctx, { \ XFadeContext *s = ctx->priv; \ const int height = slice_end - slice_start; \ + const int width = out->width; \ const float imax = 1.f / s->max_value; \ \ for (int p = 0; p < s->nb_planes; p++) { \ @@ -1722,7 +1788,7 @@ static void fadeslow##name##_transition(AVFilterContext *ctx, type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ \ for (int y = 0; y < height; y++) { \ - for (int x = 0; x < out->width; x++) { \ + for (int x = 0; x < width; x++) { \ dst[x] = mix(xf0[x], xf1[x], powf(progress, 1.f + \ logf(2.f-FFABS(xf0[x]-xf1[x])*imax)\ )); \ @@ -1738,6 +1804,204 @@ static void fadeslow##name##_transition(AVFilterContext *ctx, FADESLOW_TRANSITION(8, uint8_t, 1) FADESLOW_TRANSITION(16, uint16_t, 2) +#define HWIND_TRANSITION(name, z, type, div, expr) \ +static void h##z##wind##name##_transition(AVFilterContext *ctx, \ + const AVFrame *a, const AVFrame *b, AVFrame *out, \ + float progress, \ + int slice_start, int slice_end, int jobnr) \ +{ \ + XFadeContext *s = ctx->priv; \ + const int width = out->width; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const float r = frand(0, y); \ + for (int x = 0; x < width; x++) { \ + const float fx = expr x / (float)width; \ + for (int p = 0; p < s->nb_planes; p++) { \ + const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ + type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ + \ + dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f,-0.2f, fx * (1.f - 0.2f)\ + + 0.2f * r - (1.f - progress)\ + * (1.f + 0.2f))); \ + } \ + } \ + } \ +} + +HWIND_TRANSITION(8, l, uint8_t, 1, 1.f - ) +HWIND_TRANSITION(16, l, uint16_t, 2, 1.f - ) +HWIND_TRANSITION(8, r, uint8_t, 1, ) +HWIND_TRANSITION(16, r, uint16_t, 2, ) + +#define VWIND_TRANSITION(name, z, type, div, expr) \ +static void v##z##wind##name##_transition(AVFilterContext *ctx, \ + const AVFrame *a, const AVFrame *b, AVFrame *out, \ + float progress, \ + int slice_start, int slice_end, int jobnr) \ +{ \ + XFadeContext *s = ctx->priv; \ + const int width = out->width; \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const float fy = expr y / (float)out->height; \ + for (int x = 0; x < width; x++) { \ + const float r = frand(x, 0); \ + for (int p = 0; p < s->nb_planes; p++) { \ + const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ + type *dst = (type *)(out->data[p] + y * out->linesize[p]); \ + \ + dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f,-0.2f, fy * (1.f - 0.2f) \ + + 0.2f * r - (1.f - progress)\ + * (1.f + 0.2f))); \ + } \ + } \ + } \ +} + +VWIND_TRANSITION(8, u, uint8_t, 1, 1.f - ) +VWIND_TRANSITION(16, u, uint16_t, 2, 1.f - ) +VWIND_TRANSITION(8, d, uint8_t, 1, ) +VWIND_TRANSITION(16, d, uint16_t, 2, ) + +#define COVERH_TRANSITION(dir, name, type, div, expr) \ +static void cover##dir##name##_transition(AVFilterContext *ctx, \ + const AVFrame *a, const AVFrame *b, AVFrame *out, \ + float progress, \ + int slice_start, int slice_end, int jobnr) \ +{ \ + XFadeContext *s = ctx->priv; \ + const int height = slice_end - slice_start; \ + const int width = out->width; \ + const int z = (expr progress) * width; \ + \ + for (int p = 0; p < s->nb_planes; p++) { \ + const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \ + type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ + \ + for (int y = 0; y < height; y++) { \ + for (int x = 0; x < width; x++) { \ + const int zx = z + x; \ + const int zz = zx % width + width * (zx < 0); \ + dst[x] = (zx >= 0) && (zx < width) ? xf1[zz] : xf0[x]; \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +COVERH_TRANSITION(left, 8, uint8_t, 1, -) +COVERH_TRANSITION(left, 16, uint16_t, 2, -) +COVERH_TRANSITION(right, 8, uint8_t, 1, ) +COVERH_TRANSITION(right, 16, uint16_t, 2, ) + +#define COVERV_TRANSITION(dir, name, type, div, expr) \ +static void cover##dir##name##_transition(AVFilterContext *ctx, \ + const AVFrame *a, const AVFrame *b, AVFrame *out, \ + float progress, \ + int slice_start, int slice_end, int jobnr) \ +{ \ + XFadeContext *s = ctx->priv; \ + const int height = out->height; \ + const int width = out->width; \ + const int z = (expr progress) * height; \ + \ + for (int p = 0; p < s->nb_planes; p++) { \ + type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const int zy = z + y; \ + const int zz = zy % height + height * (zy < 0); \ + const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \ + \ + for (int x = 0; x < width; x++) \ + dst[x] = (zy >= 0) && (zy < height) ? xf1[x] : xf0[x]; \ + \ + dst += out->linesize[p] / div; \ + } \ + } \ +} + +COVERV_TRANSITION(up, 8, uint8_t, 1, -) +COVERV_TRANSITION(up, 16, uint16_t, 2, -) +COVERV_TRANSITION(down, 8, uint8_t, 1, ) +COVERV_TRANSITION(down, 16, uint16_t, 2, ) + +#define REVEALH_TRANSITION(dir, name, type, div, expr) \ +static void reveal##dir##name##_transition(AVFilterContext *ctx, \ + const AVFrame *a, const AVFrame *b, AVFrame *out, \ + float progress, \ + int slice_start, int slice_end, int jobnr) \ +{ \ + XFadeContext *s = ctx->priv; \ + const int height = slice_end - slice_start; \ + const int width = out->width; \ + const int z = (expr progress) * width; \ + \ + for (int p = 0; p < s->nb_planes; p++) { \ + const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \ + type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ + \ + for (int y = 0; y < height; y++) { \ + for (int x = 0; x < width; x++) { \ + const int zx = z + x; \ + const int zz = zx % width + width * (zx < 0); \ + dst[x] = (zx >= 0) && (zx < width) ? xf1[x] : xf0[zz]; \ + } \ + \ + dst += out->linesize[p] / div; \ + xf0 += a->linesize[p] / div; \ + xf1 += b->linesize[p] / div; \ + } \ + } \ +} + +REVEALH_TRANSITION(left, 8, uint8_t, 1, -) +REVEALH_TRANSITION(left, 16, uint16_t, 2, -) +REVEALH_TRANSITION(right, 8, uint8_t, 1, ) +REVEALH_TRANSITION(right, 16, uint16_t, 2, ) + +#define REVEALV_TRANSITION(dir, name, type, div, expr) \ +static void reveal##dir##name##_transition(AVFilterContext *ctx, \ + const AVFrame *a, const AVFrame *b, AVFrame *out, \ + float progress, \ + int slice_start, int slice_end, int jobnr) \ +{ \ + XFadeContext *s = ctx->priv; \ + const int height = out->height; \ + const int width = out->width; \ + const int z = (expr progress) * height; \ + \ + for (int p = 0; p < s->nb_planes; p++) { \ + type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \ + \ + for (int y = slice_start; y < slice_end; y++) { \ + const int zy = z + y; \ + const int zz = zy % height + height * (zy < 0); \ + const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \ + const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \ + \ + for (int x = 0; x < width; x++) \ + dst[x] = (zy >= 0) && (zy < height) ? xf1[x] : xf0[x]; \ + \ + dst += out->linesize[p] / div; \ + } \ + } \ +} + +REVEALV_TRANSITION(up, 8, uint8_t, 1, -) +REVEALV_TRANSITION(up, 16, uint16_t, 2, -) +REVEALV_TRANSITION(down, 8, uint8_t, 1, ) +REVEALV_TRANSITION(down, 16, uint16_t, 2, ) + static inline double getpix(void *priv, double x, double y, int plane, int nb) { XFadeContext *s = priv; @@ -1831,12 +2095,10 @@ static int config_output(AVFilterLink *outlink) s->white[0] = s->white[3] = s->max_value; s->white[1] = s->white[2] = s->is_rgb ? s->max_value : s->max_value / 2; - s->first_pts = s->last_pts = s->pts = AV_NOPTS_VALUE; + s->start_pts = s->inputs_offset_pts = AV_NOPTS_VALUE; if (s->duration) s->duration_pts = av_rescale_q(s->duration, AV_TIME_BASE_Q, outlink->time_base); - if (s->offset) - s->offset_pts = av_rescale_q(s->offset, AV_TIME_BASE_Q, outlink->time_base); switch (s->transition) { case CUSTOM: s->transitionf = s->depth <= 8 ? custom8_transition : custom16_transition; break; @@ -1886,6 +2148,18 @@ static int config_output(AVFilterLink *outlink) case ZOOMIN: s->transitionf = s->depth <= 8 ? zoomin8_transition : zoomin16_transition; break; case FADEFAST: s->transitionf = s->depth <= 8 ? fadefast8_transition : fadefast16_transition; break; case FADESLOW: s->transitionf = s->depth <= 8 ? fadeslow8_transition : fadeslow16_transition; break; + case HLWIND: s->transitionf = s->depth <= 8 ? hlwind8_transition : hlwind16_transition; break; + case HRWIND: s->transitionf = s->depth <= 8 ? hrwind8_transition : hrwind16_transition; break; + case VUWIND: s->transitionf = s->depth <= 8 ? vuwind8_transition : vuwind16_transition; break; + case VDWIND: s->transitionf = s->depth <= 8 ? vdwind8_transition : vdwind16_transition; break; + case COVERLEFT: s->transitionf = s->depth <= 8 ? coverleft8_transition : coverleft16_transition; break; + case COVERRIGHT: s->transitionf = s->depth <= 8 ? coverright8_transition : coverright16_transition; break; + case COVERUP: s->transitionf = s->depth <= 8 ? coverup8_transition : coverup16_transition; break; + case COVERDOWN: s->transitionf = s->depth <= 8 ? coverdown8_transition : coverdown16_transition; break; + case REVEALLEFT: s->transitionf = s->depth <= 8 ? revealleft8_transition : revealleft16_transition; break; + case REVEALRIGHT:s->transitionf = s->depth <= 8 ? revealright8_transition: revealright16_transition;break; + case REVEALUP: s->transitionf = s->depth <= 8 ? revealup8_transition : revealup16_transition; break; + case REVEALDOWN: s->transitionf = s->depth <= 8 ? revealdown8_transition : revealdown16_transition; break; default: return AVERROR_BUG; } @@ -1929,7 +2203,7 @@ static int xfade_frame(AVFilterContext *ctx, AVFrame *a, AVFrame *b) { XFadeContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; - float progress = av_clipf(1.f - ((float)(s->pts - s->first_pts - s->offset_pts) / s->duration_pts), 0.f, 1.f); + float progress = av_clipf(1.f - ((float)(s->pts - s->start_pts) / s->duration_pts), 0.f, 1.f); ThreadData td; AVFrame *out; @@ -1947,109 +2221,162 @@ static int xfade_frame(AVFilterContext *ctx, AVFrame *a, AVFrame *b) return ff_filter_frame(outlink, out); } -static int xfade_activate(AVFilterContext *ctx) +static int forward_frame(XFadeContext *s, + AVFilterLink *inlink, AVFilterLink *outlink) { - XFadeContext *s = ctx->priv; - AVFilterLink *outlink = ctx->outputs[0]; - AVFrame *in = NULL; + int64_t status_pts; int ret = 0, status; - int64_t pts; + AVFrame *frame = NULL; - FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx); + ret = ff_inlink_consume_frame(inlink, &frame); + if (ret < 0) + return ret; - if (s->xfade_is_over) { - if (!s->eof[0]) { - ret = ff_inlink_consume_frame(ctx->inputs[0], &in); - if (ret > 0) - av_frame_free(&in); - } - ret = ff_inlink_consume_frame(ctx->inputs[1], &in); - if (ret < 0) { - return ret; - } else if (ret > 0) { - in->pts = (in->pts - s->last_pts) + s->pts; - return ff_filter_frame(outlink, in); - } else if (ff_inlink_acknowledge_status(ctx->inputs[1], &status, &pts)) { - ff_outlink_set_status(outlink, status, s->pts); - return 0; - } else if (!ret) { - if (ff_outlink_frame_wanted(outlink)) - ff_inlink_request_frame(ctx->inputs[1]); - return 0; - } + if (ret > 0) { + // If we do not have an offset yet, it's because we + // never got a first input. Just offset to 0 + if (s->inputs_offset_pts == AV_NOPTS_VALUE) + s->inputs_offset_pts = -frame->pts; + + // We got a frame, nothing to do other than adjusting the timestamp + frame->pts += s->inputs_offset_pts; + return ff_filter_frame(outlink, frame); + } + + // Forward status with our timestamp + if (ff_inlink_acknowledge_status(inlink, &status, &status_pts)) { + if (s->inputs_offset_pts == AV_NOPTS_VALUE) + s->inputs_offset_pts = -status_pts; + + ff_outlink_set_status(outlink, status, status_pts + s->inputs_offset_pts); + return 0; } - if (ff_inlink_queued_frames(ctx->inputs[0]) > 0) { - s->xf[0] = ff_inlink_peek_frame(ctx->inputs[0], 0); - if (s->xf[0]) { - if (s->first_pts == AV_NOPTS_VALUE) { - s->first_pts = s->xf[0]->pts; + // No frame available, request one if needed + if (ff_outlink_frame_wanted(outlink)) + ff_inlink_request_frame(inlink); + + return 0; +} + +static int xfade_activate(AVFilterContext *avctx) +{ + XFadeContext *s = avctx->priv; + AVFilterLink *in_a = avctx->inputs[0]; + AVFilterLink *in_b = avctx->inputs[1]; + AVFilterLink *outlink = avctx->outputs[0]; + int64_t status_pts; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, avctx); + + // Check if we already transitioned or first input ended prematurely, + // in which case just forward the frames from second input with adjusted + // timestamps until EOF. + if (s->status[0] && !s->status[1]) + return forward_frame(s, in_b, outlink); + + // We did not finish transitioning yet and the first stream + // did not end either, so check if there are more frames to consume. + if (ff_inlink_check_available_frame(in_a)) { + AVFrame *peeked_frame = ff_inlink_peek_frame(in_a, 0); + s->pts = peeked_frame->pts; + + if (s->start_pts == AV_NOPTS_VALUE) + s->start_pts = + s->pts + av_rescale_q(s->offset, AV_TIME_BASE_Q, in_a->time_base); + + // Check if we are not yet transitioning, in which case + // just request and forward the input frame. + if (s->start_pts > s->pts) { + int ret; + s->passthrough = 1; + ret = ff_inlink_consume_frame(in_a, &s->xf[0]); + if (ret < 0) + return ret; + return ff_filter_frame(outlink, s->xf[0]); + } + s->passthrough = 0; + + // We are transitioning, so we need a frame from second input + if (ff_inlink_check_available_frame(in_b)) { + int ret; + ret = ff_inlink_consume_frame(avctx->inputs[0], &s->xf[0]); + if (ret < 0) + return ret; + ret = ff_inlink_consume_frame(avctx->inputs[1], &s->xf[1]); + if (ret < 0) { + av_frame_free(&s->xf[0]); + return ret; } - s->pts = s->xf[0]->pts; - if (s->first_pts + s->offset_pts > s->xf[0]->pts) { - s->xf[0] = NULL; - s->need_second = 0; - ff_inlink_consume_frame(ctx->inputs[0], &in); - return ff_filter_frame(outlink, in); + + // Calculate PTS offset to first input + if (s->inputs_offset_pts == AV_NOPTS_VALUE) + s->inputs_offset_pts = s->pts - s->xf[1]->pts; + + // Check if we finished transitioning, in which case we + // report back EOF to first input as it is no longer needed. + if (s->pts - s->start_pts > s->duration_pts) { + s->status[0] = AVERROR_EOF; + ff_inlink_set_status(in_a, AVERROR_EOF); + s->passthrough = 1; } + ret = xfade_frame(avctx, s->xf[0], s->xf[1]); + av_frame_free(&s->xf[0]); + av_frame_free(&s->xf[1]); + return ret; + } - s->need_second = 1; + // We did not get a frame from second input, check its status. + if (ff_inlink_acknowledge_status(in_b, &s->status[1], &status_pts)) { + // We should transition, but second input is EOF so just report EOF output now. + ff_outlink_set_status(outlink, s->status[1], s->pts); + return 0; } - } - if (s->xf[0] && ff_inlink_queued_frames(ctx->inputs[1]) > 0) { - ff_inlink_consume_frame(ctx->inputs[0], &s->xf[0]); - ff_inlink_consume_frame(ctx->inputs[1], &s->xf[1]); - - s->last_pts = s->xf[1]->pts; - s->pts = s->xf[0]->pts; - if (s->xf[0]->pts - (s->first_pts + s->offset_pts) > s->duration_pts) - s->xfade_is_over = 1; - ret = xfade_frame(ctx, s->xf[0], s->xf[1]); - av_frame_free(&s->xf[0]); - av_frame_free(&s->xf[1]); - return ret; + // We did not get a frame for second input but no EOF either, so just request more. + if (ff_outlink_frame_wanted(outlink)) { + ff_inlink_request_frame(in_b); + return 0; + } } - if (ff_inlink_queued_frames(ctx->inputs[0]) > 0 && - ff_inlink_queued_frames(ctx->inputs[1]) > 0) { - ff_filter_set_ready(ctx, 100); + // We did not get a frame from first input, check its status. + if (ff_inlink_acknowledge_status(in_a, &s->status[0], &status_pts)) { + // No more frames from first input, do not report EOF though, we will just + // forward the second input frames in the next activate calls. + s->passthrough = 1; + ff_filter_set_ready(avctx, 100); return 0; } + // We have no frames yet from first input and no EOF, so request some. if (ff_outlink_frame_wanted(outlink)) { - if (!s->eof[0] && ff_outlink_get_status(ctx->inputs[0])) { - s->eof[0] = 1; - s->xfade_is_over = 1; - } - if (!s->eof[1] && ff_outlink_get_status(ctx->inputs[1])) { - s->eof[1] = 1; - } - if (!s->eof[0] && !s->xf[0] && ff_inlink_queued_frames(ctx->inputs[0]) == 0) - ff_inlink_request_frame(ctx->inputs[0]); - if (!s->eof[1] && (s->need_second || s->eof[0]) && ff_inlink_queued_frames(ctx->inputs[1]) == 0) - ff_inlink_request_frame(ctx->inputs[1]); - if (s->eof[0] && s->eof[1] && ( - ff_inlink_queued_frames(ctx->inputs[0]) <= 0 && - ff_inlink_queued_frames(ctx->inputs[1]) <= 0)) { - ff_outlink_set_status(outlink, AVERROR_EOF, AV_NOPTS_VALUE); - } else if (s->xfade_is_over) { - ff_filter_set_ready(ctx, 100); - } + ff_inlink_request_frame(in_a); return 0; } return FFERROR_NOT_READY; } +static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h) +{ + XFadeContext *s = inlink->dst->priv; + + return s->passthrough ? + ff_null_get_video_buffer (inlink, w, h) : + ff_default_get_video_buffer(inlink, w, h); +} + static const AVFilterPad xfade_inputs[] = { { .name = "main", .type = AVMEDIA_TYPE_VIDEO, + .get_buffer.video = get_video_buffer, }, { .name = "xfade", .type = AVMEDIA_TYPE_VIDEO, + .get_buffer.video = get_video_buffer, }, }; diff --git a/libavfilter/vf_xfade_opencl.c b/libavfilter/vf_xfade_opencl.c index cca4980e012..2368c046b4d 100644 --- a/libavfilter/vf_xfade_opencl.c +++ b/libavfilter/vf_xfade_opencl.c @@ -93,7 +93,7 @@ static int xfade_opencl_load(AVFilterContext *avctx, if (ctx->transition == CUSTOM) { err = ff_opencl_filter_load_program_from_file(avctx, ctx->source_file); } else { - err = ff_opencl_filter_load_program(avctx, &ff_opencl_source_xfade, 1); + err = ff_opencl_filter_load_program(avctx, &ff_source_xfade_cl, 1); } if (err < 0) return err; @@ -378,17 +378,17 @@ static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h) #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) static const AVOption xfade_opencl_options[] = { - { "transition", "set cross fade transition", OFFSET(transition), AV_OPT_TYPE_INT, {.i64=1}, 0, NB_TRANSITIONS-1, FLAGS, "transition" }, - { "custom", "custom transition", 0, AV_OPT_TYPE_CONST, {.i64=CUSTOM}, 0, 0, FLAGS, "transition" }, - { "fade", "fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADE}, 0, 0, FLAGS, "transition" }, - { "wipeleft", "wipe left transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPELEFT}, 0, 0, FLAGS, "transition" }, - { "wiperight", "wipe right transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPERIGHT}, 0, 0, FLAGS, "transition" }, - { "wipeup", "wipe up transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEUP}, 0, 0, FLAGS, "transition" }, - { "wipedown", "wipe down transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEDOWN}, 0, 0, FLAGS, "transition" }, - { "slideleft", "slide left transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDELEFT}, 0, 0, FLAGS, "transition" }, - { "slideright", "slide right transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDERIGHT}, 0, 0, FLAGS, "transition" }, - { "slideup", "slide up transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEUP}, 0, 0, FLAGS, "transition" }, - { "slidedown", "slide down transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEDOWN}, 0, 0, FLAGS, "transition" }, + { "transition", "set cross fade transition", OFFSET(transition), AV_OPT_TYPE_INT, {.i64=1}, 0, NB_TRANSITIONS-1, FLAGS, .unit = "transition" }, + { "custom", "custom transition", 0, AV_OPT_TYPE_CONST, {.i64=CUSTOM}, 0, 0, FLAGS, .unit = "transition" }, + { "fade", "fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADE}, 0, 0, FLAGS, .unit = "transition" }, + { "wipeleft", "wipe left transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPELEFT}, 0, 0, FLAGS, .unit = "transition" }, + { "wiperight", "wipe right transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPERIGHT}, 0, 0, FLAGS, .unit = "transition" }, + { "wipeup", "wipe up transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEUP}, 0, 0, FLAGS, .unit = "transition" }, + { "wipedown", "wipe down transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEDOWN}, 0, 0, FLAGS, .unit = "transition" }, + { "slideleft", "slide left transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDELEFT}, 0, 0, FLAGS, .unit = "transition" }, + { "slideright", "slide right transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDERIGHT}, 0, 0, FLAGS, .unit = "transition" }, + { "slideup", "slide up transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEUP}, 0, 0, FLAGS, .unit = "transition" }, + { "slidedown", "slide down transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEDOWN}, 0, 0, FLAGS, .unit = "transition" }, { "source", "set OpenCL program source file for custom transition", OFFSET(source_file), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS }, { "kernel", "set kernel name in program file for custom transition", OFFSET(kernel_name), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = FLAGS }, { "duration", "set cross fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=1000000}, 0, 60000000, FLAGS }, @@ -433,4 +433,5 @@ const AVFilter ff_vf_xfade_opencl = { FILTER_OUTPUTS(xfade_opencl_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_OPENCL), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vf_xfade_vulkan.c b/libavfilter/vf_xfade_vulkan.c new file mode 100644 index 00000000000..be041eaef46 --- /dev/null +++ b/libavfilter/vf_xfade_vulkan.c @@ -0,0 +1,720 @@ +/* + * 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/avassert.h" +#include "libavutil/random_seed.h" +#include "libavutil/opt.h" +#include "vulkan_filter.h" +#include "vulkan_spirv.h" +#include "filters.h" +#include "internal.h" +#include "video.h" + +#define IN_A 0 +#define IN_B 1 +#define IN_NB 2 + +typedef struct XFadeParameters { + float progress; +} XFadeParameters; + +typedef struct XFadeVulkanContext { + FFVulkanContext vkctx; + + int transition; + int64_t duration; + int64_t offset; + + int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + FFVkSPIRVShader shd; + VkSampler sampler; + + // PTS when the fade should start (in IN_A timebase) + int64_t start_pts; + + // PTS offset between IN_A and IN_B + int64_t inputs_offset_pts; + + // Duration of the transition + int64_t duration_pts; + + // Current PTS of the first input (IN_A) + int64_t pts; + + // If frames are currently just passed through + // unmodified, like before and after the actual + // transition. + int passthrough; + + int status[IN_NB]; +} XFadeVulkanContext; + +enum XFadeTransitions { + FADE, + WIPELEFT, + WIPERIGHT, + WIPEUP, + WIPEDOWN, + SLIDEDOWN, + SLIDEUP, + SLIDELEFT, + SLIDERIGHT, + CIRCLEOPEN, + CIRCLECLOSE, + DISSOLVE, + PIXELIZE, + WIPETL, + WIPETR, + WIPEBL, + WIPEBR, + NB_TRANSITIONS, +}; + +static const char transition_fade[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, mix(a, b, progress)); ) + C(0, } ) +}; + +static const char transition_wipeleft[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, ivec2 size = imageSize(output_images[idx]); ) + C(1, int s = int(size.x * (1.0 - progress)); ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, pos.x > s ? b : a); ) + C(0, } ) +}; + +static const char transition_wiperight[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, ivec2 size = imageSize(output_images[idx]); ) + C(1, int s = int(size.x * progress); ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, pos.x > s ? a : b); ) + C(0, } ) +}; + +static const char transition_wipeup[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, ivec2 size = imageSize(output_images[idx]); ) + C(1, int s = int(size.y * (1.0 - progress)); ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, pos.y > s ? b : a); ) + C(0, } ) +}; + +static const char transition_wipedown[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, ivec2 size = imageSize(output_images[idx]); ) + C(1, int s = int(size.y * progress); ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, pos.y > s ? a : b); ) + C(0, } ) +}; + +#define SHADER_SLIDE_COMMON \ + C(0, void slide(int idx, ivec2 pos, float progress, ivec2 direction) ) \ + C(0, { ) \ + C(1, ivec2 size = imageSize(output_images[idx]); ) \ + C(1, ivec2 pi = ivec2(progress * size); ) \ + C(1, ivec2 p = pos + pi * direction; ) \ + C(1, ivec2 f = p % size; ) \ + C(1, f = f + size * ivec2(f.x < 0, f.y < 0); ) \ + C(1, vec4 a = texture(a_images[idx], f); ) \ + C(1, vec4 b = texture(b_images[idx], f); ) \ + C(1, vec4 r = (p.y >= 0 && p.x >= 0 && size.y > p.y && size.x > p.x) ? a : b; ) \ + C(1, imageStore(output_images[idx], pos, r); ) \ + C(0, } ) + +static const char transition_slidedown[] = { + SHADER_SLIDE_COMMON + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, slide(idx, pos, progress, ivec2(0, -1)); ) + C(0, } ) +}; + +static const char transition_slideup[] = { + SHADER_SLIDE_COMMON + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, slide(idx, pos, progress, ivec2(0, +1)); ) + C(0, } ) +}; + +static const char transition_slideleft[] = { + SHADER_SLIDE_COMMON + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, slide(idx, pos, progress, ivec2(+1, 0)); ) + C(0, } ) +}; + +static const char transition_slideright[] = { + SHADER_SLIDE_COMMON + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, slide(idx, pos, progress, ivec2(-1, 0)); ) + C(0, } ) +}; + +#define SHADER_CIRCLE_COMMON \ + C(0, void circle(int idx, ivec2 pos, float progress, bool open) ) \ + C(0, { ) \ + C(1, const ivec2 half_size = imageSize(output_images[idx]) / 2; ) \ + C(1, const float z = dot(half_size, half_size); ) \ + C(1, float p = ((open ? (1.0 - progress) : progress) - 0.5) * 3.0; ) \ + C(1, ivec2 dsize = pos - half_size; ) \ + C(1, float sm = dot(dsize, dsize) / z + p; ) \ + C(1, vec4 a = texture(a_images[idx], pos); ) \ + C(1, vec4 b = texture(b_images[idx], pos); ) \ + C(1, imageStore(output_images[idx], pos, \ + mix(open ? b : a, open ? a : b, \ + smoothstep(0.f, 1.f, sm))); ) \ + C(0, } ) + +static const char transition_circleopen[] = { + SHADER_CIRCLE_COMMON + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, circle(idx, pos, progress, true); ) + C(0, } ) +}; + +static const char transition_circleclose[] = { + SHADER_CIRCLE_COMMON + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, circle(idx, pos, progress, false); ) + C(0, } ) +}; + +#define SHADER_FRAND_FUNC \ + C(0, float frand(vec2 v) ) \ + C(0, { ) \ + C(1, return fract(sin(dot(v, vec2(12.9898, 78.233))) * 43758.545); ) \ + C(0, } ) + +static const char transition_dissolve[] = { + SHADER_FRAND_FUNC + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, float sm = frand(pos) * 2.0 + (1.0 - progress) * 2.0 - 1.5; ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, sm >= 0.5 ? a : b); ) + C(0, } ) +}; + +static const char transition_pixelize[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, ivec2 size = imageSize(output_images[idx]); ) + C(1, float d = min(progress, 1.0 - progress); ) + C(1, float dist = ceil(d * 50.0) / 50.0; ) + C(1, float sq = 2.0 * dist * min(size.x, size.y) / 20.0; ) + C(1, float sx = dist > 0.0 ? min((floor(pos.x / sq) + 0.5) * sq, size.x - 1) : pos.x; ) + C(1, float sy = dist > 0.0 ? min((floor(pos.y / sq) + 0.5) * sq, size.y - 1) : pos.y; ) + C(1, vec4 a = texture(a_images[idx], vec2(sx, sy)); ) + C(1, vec4 b = texture(b_images[idx], vec2(sx, sy)); ) + C(1, imageStore(output_images[idx], pos, mix(a, b, progress)); ) + C(0, } ) +}; + +static const char transition_wipetl[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, ivec2 size = imageSize(output_images[idx]); ) + C(1, float zw = size.x * (1.0 - progress); ) + C(1, float zh = size.y * (1.0 - progress); ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, (pos.y <= zh && pos.x <= zw) ? a : b); ) + C(0, } ) +}; + +static const char transition_wipetr[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, ivec2 size = imageSize(output_images[idx]); ) + C(1, float zw = size.x * (progress); ) + C(1, float zh = size.y * (1.0 - progress); ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, (pos.y <= zh && pos.x > zw) ? a : b); ) + C(0, } ) +}; + +static const char transition_wipebl[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, ivec2 size = imageSize(output_images[idx]); ) + C(1, float zw = size.x * (1.0 - progress); ) + C(1, float zh = size.y * (progress); ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, (pos.y > zh && pos.x <= zw) ? a : b); ) + C(0, } ) +}; + +static const char transition_wipebr[] = { + C(0, void transition(int idx, ivec2 pos, float progress) ) + C(0, { ) + C(1, ivec2 size = imageSize(output_images[idx]); ) + C(1, float zw = size.x * (progress); ) + C(1, float zh = size.y * (progress); ) + C(1, vec4 a = texture(a_images[idx], pos); ) + C(1, vec4 b = texture(b_images[idx], pos); ) + C(1, imageStore(output_images[idx], pos, (pos.y > zh && pos.x > zw) ? a : b); ) + C(0, } ) +}; + +static const char* transitions_map[NB_TRANSITIONS] = { + [FADE] = transition_fade, + [WIPELEFT] = transition_wipeleft, + [WIPERIGHT] = transition_wiperight, + [WIPEUP] = transition_wipeup, + [WIPEDOWN] = transition_wipedown, + [SLIDEDOWN] = transition_slidedown, + [SLIDEUP] = transition_slideup, + [SLIDELEFT] = transition_slideleft, + [SLIDERIGHT] = transition_slideright, + [CIRCLEOPEN] = transition_circleopen, + [CIRCLECLOSE] = transition_circleclose, + [DISSOLVE] = transition_dissolve, + [PIXELIZE] = transition_pixelize, + [WIPETL] = transition_wipetl, + [WIPETR] = transition_wipetr, + [WIPEBL] = transition_wipebl, + [WIPEBR] = transition_wipebr, +}; + +static av_cold int init_vulkan(AVFilterContext *avctx) +{ + int err = 0; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; + XFadeVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc; + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(avctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_init_sampler(vkctx, &s->sampler, 1, VK_FILTER_NEAREST)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "xfade_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); + + desc = (FFVulkanDescriptorSetBinding []) { + { + .name = "a_images", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "b_images", + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .samplers = DUP_SAMPLER(s->sampler), + }, + { + .name = "output_images", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc, 3, 0, 0)); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, float progress; ); + GLSLC(0, }; ); + + ff_vk_add_push_constant(&s->pl, 0, sizeof(XFadeParameters), + VK_SHADER_STAGE_COMPUTE_BIT); + + // Add the right transition type function to the shader + GLSLD(transitions_map[s->transition]); + + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + GLSLF(1, int planes = %i; ,planes); + GLSLC(1, for (int i = 0; i < planes; i++) { ); + GLSLC(2, transition(i, pos, progress); ); + GLSLC(1, } ); + GLSLC(0, } ); + + RET(spv->compile_shader(spv, avctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); + + s->initialized = 1; + +fail: + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); + + return err; +} + +static int xfade_frame(AVFilterContext *avctx, AVFrame *frame_a, AVFrame *frame_b) +{ + int err; + AVFilterLink *outlink = avctx->outputs[0]; + XFadeVulkanContext *s = avctx->priv; + float progress; + + AVFrame *output = ff_get_video_buffer(outlink, outlink->w, outlink->h); + if (!output) { + err = AVERROR(ENOMEM); + goto fail; + } + + if (!s->initialized) { + AVHWFramesContext *a_fc = (AVHWFramesContext*)frame_a->hw_frames_ctx->data; + AVHWFramesContext *b_fc = (AVHWFramesContext*)frame_b->hw_frames_ctx->data; + if (a_fc->sw_format != b_fc->sw_format) { + av_log(avctx, AV_LOG_ERROR, + "Currently the sw format of the first input needs to match the second!\n"); + return AVERROR(EINVAL); + } + RET(init_vulkan(avctx)); + } + + RET(av_frame_copy_props(output, frame_a)); + output->pts = s->pts; + + progress = av_clipf((float)(s->pts - s->start_pts) / s->duration_pts, + 0.f, 1.f); + + RET(ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->pl, output, + (AVFrame *[]){ frame_a, frame_b }, 2, s->sampler, + &(XFadeParameters){ progress }, sizeof(XFadeParameters))); + + return ff_filter_frame(outlink, output); + +fail: + av_frame_free(&output); + return err; +} + +static int config_props_output(AVFilterLink *outlink) +{ + int err; + AVFilterContext *avctx = outlink->src; + XFadeVulkanContext *s = avctx->priv; + AVFilterLink *inlink_a = avctx->inputs[IN_A]; + AVFilterLink *inlink_b = avctx->inputs[IN_B]; + + if (inlink_a->w != inlink_b->w || inlink_a->h != inlink_b->h) { + av_log(avctx, AV_LOG_ERROR, "First input link %s parameters " + "(size %dx%d) do not match the corresponding " + "second input link %s parameters (size %dx%d)\n", + avctx->input_pads[IN_A].name, inlink_a->w, inlink_a->h, + avctx->input_pads[IN_B].name, inlink_b->w, inlink_b->h); + return AVERROR(EINVAL); + } + + if (inlink_a->time_base.num != inlink_b->time_base.num || + inlink_a->time_base.den != inlink_b->time_base.den) { + av_log(avctx, AV_LOG_ERROR, "First input link %s timebase " + "(%d/%d) does not match the corresponding " + "second input link %s timebase (%d/%d)\n", + avctx->input_pads[IN_A].name, inlink_a->time_base.num, inlink_a->time_base.den, + avctx->input_pads[IN_B].name, inlink_b->time_base.num, inlink_b->time_base.den); + return AVERROR(EINVAL); + } + + s->start_pts = s->inputs_offset_pts = AV_NOPTS_VALUE; + + outlink->time_base = inlink_a->time_base; + outlink->frame_rate = inlink_a->frame_rate; + outlink->sample_aspect_ratio = inlink_a->sample_aspect_ratio; + + if (s->duration) + s->duration_pts = av_rescale_q(s->duration, AV_TIME_BASE_Q, inlink_a->time_base); + RET(ff_vk_filter_config_output(outlink)); + +fail: + return err; +} + +static int forward_frame(XFadeVulkanContext *s, + AVFilterLink *inlink, AVFilterLink *outlink) +{ + int64_t status_pts; + int ret = 0, status; + AVFrame *frame = NULL; + + ret = ff_inlink_consume_frame(inlink, &frame); + if (ret < 0) + return ret; + + if (ret > 0) { + // If we do not have an offset yet, it's because we + // never got a first input. Just offset to 0 + if (s->inputs_offset_pts == AV_NOPTS_VALUE) + s->inputs_offset_pts = -frame->pts; + + // We got a frame, nothing to do other than adjusting the timestamp + frame->pts += s->inputs_offset_pts; + return ff_filter_frame(outlink, frame); + } + + // Forward status with our timestamp + if (ff_inlink_acknowledge_status(inlink, &status, &status_pts)) { + if (s->inputs_offset_pts == AV_NOPTS_VALUE) + s->inputs_offset_pts = -status_pts; + + ff_outlink_set_status(outlink, status, status_pts + s->inputs_offset_pts); + return 0; + } + + // No frame available, request one if needed + if (ff_outlink_frame_wanted(outlink)) + ff_inlink_request_frame(inlink); + + return 0; +} + +static int activate(AVFilterContext *avctx) +{ + XFadeVulkanContext *s = avctx->priv; + AVFilterLink *in_a = avctx->inputs[IN_A]; + AVFilterLink *in_b = avctx->inputs[IN_B]; + AVFilterLink *outlink = avctx->outputs[0]; + int64_t status_pts; + + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, avctx); + + // Check if we already transitioned or IN_A ended prematurely, + // in which case just forward the frames from IN_B with adjusted + // timestamps until EOF. + if (s->status[IN_A] && !s->status[IN_B]) + return forward_frame(s, in_b, outlink); + + // We did not finish transitioning yet and the first stream + // did not end either, so check if there are more frames to consume. + if (ff_inlink_check_available_frame(in_a)) { + AVFrame *peeked_frame = ff_inlink_peek_frame(in_a, 0); + s->pts = peeked_frame->pts; + + if (s->start_pts == AV_NOPTS_VALUE) + s->start_pts = + s->pts + av_rescale_q(s->offset, AV_TIME_BASE_Q, in_a->time_base); + + // Check if we are not yet transitioning, in which case + // just request and forward the input frame. + if (s->start_pts > s->pts) { + AVFrame *frame_a = NULL; + s->passthrough = 1; + ff_inlink_consume_frame(in_a, &frame_a); + return ff_filter_frame(outlink, frame_a); + } + s->passthrough = 0; + + // We are transitioning, so we need a frame from IN_B + if (ff_inlink_check_available_frame(in_b)) { + int ret; + AVFrame *frame_a = NULL, *frame_b = NULL; + ff_inlink_consume_frame(avctx->inputs[IN_A], &frame_a); + ff_inlink_consume_frame(avctx->inputs[IN_B], &frame_b); + + // Calculate PTS offset to first input + if (s->inputs_offset_pts == AV_NOPTS_VALUE) + s->inputs_offset_pts = s->pts - frame_b->pts; + + // Check if we finished transitioning, in which case we + // report back EOF to IN_A as it is no longer needed. + if (s->pts - s->start_pts > s->duration_pts) { + s->status[IN_A] = AVERROR_EOF; + ff_inlink_set_status(in_a, AVERROR_EOF); + s->passthrough = 1; + } + ret = xfade_frame(avctx, frame_a, frame_b); + av_frame_free(&frame_a); + av_frame_free(&frame_b); + return ret; + } + + // We did not get a frame from IN_B, check its status. + if (ff_inlink_acknowledge_status(in_b, &s->status[IN_B], &status_pts)) { + // We should transition, but IN_B is EOF so just report EOF output now. + ff_outlink_set_status(outlink, s->status[IN_B], s->pts); + return 0; + } + + // We did not get a frame for IN_B but no EOF either, so just request more. + if (ff_outlink_frame_wanted(outlink)) { + ff_inlink_request_frame(in_b); + return 0; + } + } + + // We did not get a frame from IN_A, check its status. + if (ff_inlink_acknowledge_status(in_a, &s->status[IN_A], &status_pts)) { + // No more frames from IN_A, do not report EOF though, we will just + // forward the IN_B frames in the next activate calls. + s->passthrough = 1; + ff_filter_set_ready(avctx, 100); + return 0; + } + + // We have no frames yet from IN_A and no EOF, so request some. + if (ff_outlink_frame_wanted(outlink)) { + ff_inlink_request_frame(in_a); + return 0; + } + + return FFERROR_NOT_READY; +} + +static av_cold void uninit(AVFilterContext *avctx) +{ + XFadeVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + FFVulkanFunctions *vk = &vkctx->vkfn; + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + if (s->sampler) + vk->DestroySampler(vkctx->hwctx->act_dev, s->sampler, + vkctx->hwctx->alloc); + + ff_vk_uninit(&s->vkctx); + + s->initialized = 0; +} + +static AVFrame *get_video_buffer(AVFilterLink *inlink, int w, int h) +{ + XFadeVulkanContext *s = inlink->dst->priv; + + return s->passthrough ? + ff_null_get_video_buffer (inlink, w, h) : + ff_default_get_video_buffer(inlink, w, h); +} + +#define OFFSET(x) offsetof(XFadeVulkanContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) + +static const AVOption xfade_vulkan_options[] = { + { "transition", "set cross fade transition", OFFSET(transition), AV_OPT_TYPE_INT, {.i64=FADE}, 0, NB_TRANSITIONS-1, FLAGS, .unit = "transition" }, + { "fade", "fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADE}, 0, 0, FLAGS, .unit = "transition" }, + { "wipeleft", "wipe left transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPELEFT}, 0, 0, FLAGS, .unit = "transition" }, + { "wiperight", "wipe right transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPERIGHT}, 0, 0, FLAGS, .unit = "transition" }, + { "wipeup", "wipe up transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEUP}, 0, 0, FLAGS, .unit = "transition" }, + { "wipedown", "wipe down transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEDOWN}, 0, 0, FLAGS, .unit = "transition" }, + { "slidedown", "slide down transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEDOWN}, 0, 0, FLAGS, .unit = "transition" }, + { "slideup", "slide up transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEUP}, 0, 0, FLAGS, .unit = "transition" }, + { "slideleft", "slide left transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDELEFT}, 0, 0, FLAGS, .unit = "transition" }, + { "slideright", "slide right transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDERIGHT}, 0, 0, FLAGS, .unit = "transition" }, + { "circleopen", "circleopen transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLEOPEN}, 0, 0, FLAGS, .unit = "transition" }, + { "circleclose", "circleclose transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLECLOSE}, 0, 0, FLAGS, .unit = "transition" }, + { "dissolve", "dissolve transition", 0, AV_OPT_TYPE_CONST, {.i64=DISSOLVE}, 0, 0, FLAGS, .unit = "transition" }, + { "pixelize", "pixelize transition", 0, AV_OPT_TYPE_CONST, {.i64=PIXELIZE}, 0, 0, FLAGS, .unit = "transition" }, + { "wipetl", "wipe top left transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPETL}, 0, 0, FLAGS, .unit = "transition" }, + { "wipetr", "wipe top right transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPETR}, 0, 0, FLAGS, .unit = "transition" }, + { "wipebl", "wipe bottom left transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEBL}, 0, 0, FLAGS, .unit = "transition" }, + { "wipebr", "wipe bottom right transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEBR}, 0, 0, FLAGS, .unit = "transition" }, + { "duration", "set cross fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=1000000}, 0, 60000000, FLAGS }, + { "offset", "set cross fade start relative to first input stream", OFFSET(offset), AV_OPT_TYPE_DURATION, {.i64=0}, INT64_MIN, INT64_MAX, FLAGS }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(xfade_vulkan); + +static const AVFilterPad xfade_vulkan_inputs[] = { + { + .name = "main", + .type = AVMEDIA_TYPE_VIDEO, + .get_buffer.video = &get_video_buffer, + .config_props = &ff_vk_filter_config_input, + }, + { + .name = "xfade", + .type = AVMEDIA_TYPE_VIDEO, + .get_buffer.video = &get_video_buffer, + .config_props = &ff_vk_filter_config_input, + }, +}; + +static const AVFilterPad xfade_vulkan_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = &config_props_output, + }, +}; + +const AVFilter ff_vf_xfade_vulkan = { + .name = "xfade_vulkan", + .description = NULL_IF_CONFIG_SMALL("Cross fade one video with another video."), + .priv_size = sizeof(XFadeVulkanContext), + .init = &ff_vk_filter_init, + .uninit = &uninit, + .activate = &activate, + FILTER_INPUTS(xfade_vulkan_inputs), + FILTER_OUTPUTS(xfade_vulkan_outputs), + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), + .priv_class = &xfade_vulkan_class, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, +}; diff --git a/libavfilter/vf_xmedian.c b/libavfilter/vf_xmedian.c index 7f7f3de12a8..ebcbea97ed7 100644 --- a/libavfilter/vf_xmedian.c +++ b/libavfilter/vf_xmedian.c @@ -22,13 +22,11 @@ #include "libavutil/avstring.h" #include "libavutil/imgutils.h" -#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "libavutil/qsort.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" #include "framesync.h" #include "video.h" @@ -329,6 +327,14 @@ static int activate(AVFilterContext *ctx) return ff_framesync_activate(&s->fs); } +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_output, + }, +}; + #if CONFIG_XMEDIAN_FILTER static av_cold int xmedian_init(AVFilterContext *ctx) { @@ -365,14 +371,6 @@ static const AVOption xmedian_options[] = { { NULL }, }; -static const AVFilterPad outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_output, - }, -}; - FRAMESYNC_DEFINE_CLASS(xmedian, XMedianContext, fs); const AVFilter ff_vf_xmedian = { @@ -450,14 +448,6 @@ static const AVFilterPad tmedian_inputs[] = { }, }; -static const AVFilterPad tmedian_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_output, - }, -}; - AVFILTER_DEFINE_CLASS(tmedian); const AVFilter ff_vf_tmedian = { @@ -466,7 +456,7 @@ const AVFilter ff_vf_tmedian = { .priv_size = sizeof(XMedianContext), .priv_class = &tmedian_class, FILTER_INPUTS(tmedian_inputs), - FILTER_OUTPUTS(tmedian_outputs), + FILTER_OUTPUTS(outputs), FILTER_PIXFMTS_ARRAY(pixel_fmts), .init = init, .uninit = uninit, diff --git a/libavfilter/vf_yadif.c b/libavfilter/vf_yadif.c index 1be02de1a94..aa5ca4a8892 100644 --- a/libavfilter/vf_yadif.c +++ b/libavfilter/vf_yadif.c @@ -21,11 +21,8 @@ #include "libavutil/common.h" #include "libavutil/pixdesc.h" -#include "libavutil/imgutils.h" #include "avfilter.h" -#include "formats.h" #include "internal.h" -#include "video.h" #include "yadif.h" typedef struct ThreadData { @@ -254,15 +251,6 @@ static void filter(AVFilterContext *ctx, AVFrame *dstpic, } } -static av_cold void uninit(AVFilterContext *ctx) -{ - YADIFContext *yadif = ctx->priv; - - av_frame_free(&yadif->prev); - av_frame_free(&yadif->cur ); - av_frame_free(&yadif->next); -} - static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV440P, @@ -285,19 +273,11 @@ static int config_output(AVFilterLink *outlink) { AVFilterContext *ctx = outlink->src; YADIFContext *s = ctx->priv; + int ret; - outlink->time_base = av_mul_q(ctx->inputs[0]->time_base, (AVRational){1, 2}); - outlink->w = ctx->inputs[0]->w; - outlink->h = ctx->inputs[0]->h; - - if(s->mode & 1) - outlink->frame_rate = av_mul_q(ctx->inputs[0]->frame_rate, - (AVRational){2, 1}); - - if (outlink->w < 3 || outlink->h < 3) { - av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or lines is not supported\n"); - return AVERROR(EINVAL); - } + ret = ff_yadif_config_output_common(outlink); + if (ret < 0) + return ret; s->csp = av_pix_fmt_desc_get(outlink->format); s->filter = filter; @@ -347,7 +327,7 @@ const AVFilter ff_vf_yadif = { .description = NULL_IF_CONFIG_SMALL("Deinterlace the input image."), .priv_size = sizeof(YADIFContext), .priv_class = &yadif_class, - .uninit = uninit, + .uninit = ff_yadif_uninit, FILTER_INPUTS(avfilter_vf_yadif_inputs), FILTER_OUTPUTS(avfilter_vf_yadif_outputs), FILTER_PIXFMTS_ARRAY(pix_fmts), diff --git a/libavfilter/vf_yadif_cuda.c b/libavfilter/vf_yadif_cuda.c index 685b8a20355..79080e71c2d 100644 --- a/libavfilter/vf_yadif_cuda.c +++ b/libavfilter/vf_yadif_cuda.c @@ -38,8 +38,6 @@ typedef struct DeintCUDAContext { AVBufferRef *input_frames_ref; AVHWFramesContext *input_frames; - CUcontext cu_ctx; - CUstream stream; CUmodule cu_module; CUfunction cu_func_uchar; CUfunction cu_func_uchar2; @@ -109,7 +107,7 @@ static CUresult call_kernel(AVFilterContext *ctx, CUfunction func, ret = CHECK_CU(cu->cuLaunchKernel(func, DIV_UP(dst_width, BLOCKX), DIV_UP(dst_height, BLOCKY), 1, BLOCKX, BLOCKY, 1, - 0, s->stream, args, NULL)); + 0, s->hwctx->stream, args, NULL)); exit: if (tex_prev) @@ -131,7 +129,7 @@ static void filter(AVFilterContext *ctx, AVFrame *dst, CUcontext dummy; int i, ret; - ret = CHECK_CU(cu->cuCtxPushCurrent(s->cu_ctx)); + ret = CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx)); if (ret < 0) return; @@ -193,18 +191,15 @@ static av_cold void deint_cuda_uninit(AVFilterContext *ctx) { CUcontext dummy; DeintCUDAContext *s = ctx->priv; - YADIFContext *y = &s->yadif; if (s->hwctx && s->cu_module) { CudaFunctions *cu = s->hwctx->internal->cuda_dl; - CHECK_CU(cu->cuCtxPushCurrent(s->cu_ctx)); + CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx)); CHECK_CU(cu->cuModuleUnload(s->cu_module)); CHECK_CU(cu->cuCtxPopCurrent(&dummy)); } - av_frame_free(&y->prev); - av_frame_free(&y->cur); - av_frame_free(&y->next); + ff_yadif_uninit(ctx); av_buffer_unref(&s->device_ref); s->hwctx = NULL; @@ -252,8 +247,6 @@ static int config_output(AVFilterLink *link) return AVERROR(ENOMEM); } s->hwctx = ((AVHWDeviceContext*)s->device_ref->data)->hwctx; - s->cu_ctx = s->hwctx->cuda_ctx; - s->stream = s->hwctx->stream; cu = s->hwctx->internal->cuda_dl; link->hw_frames_ctx = av_hwframe_ctx_alloc(s->device_ref); @@ -284,24 +277,14 @@ static int config_output(AVFilterLink *link) goto exit; } - link->time_base = av_mul_q(ctx->inputs[0]->time_base, (AVRational){1, 2}); - link->w = ctx->inputs[0]->w; - link->h = ctx->inputs[0]->h; - - if(y->mode & 1) - link->frame_rate = av_mul_q(ctx->inputs[0]->frame_rate, - (AVRational){2, 1}); - - if (link->w < 3 || link->h < 3) { - av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or lines is not supported\n"); - ret = AVERROR(EINVAL); + ret = ff_yadif_config_output_common(link); + if (ret < 0) goto exit; - } y->csp = av_pix_fmt_desc_get(output_frames->sw_format); y->filter = filter; - ret = CHECK_CU(cu->cuCtxPushCurrent(s->cu_ctx)); + ret = CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx)); if (ret < 0) goto exit; diff --git a/libavfilter/vf_yadif_videotoolbox.m b/libavfilter/vf_yadif_videotoolbox.m index 69186c22542..c47d3edfb85 100644 --- a/libavfilter/vf_yadif_videotoolbox.m +++ b/libavfilter/vf_yadif_videotoolbox.m @@ -174,9 +174,7 @@ static av_cold void do_uninit(AVFilterContext *ctx) API_AVAILABLE(macos(10.11), YADIFVTContext *s = ctx->priv; YADIFContext *y = &s->yadif; - av_frame_free(&y->prev); - av_frame_free(&y->cur); - av_frame_free(&y->next); + ff_yadif_uninit(ctx); av_buffer_unref(&s->device_ref); av_buffer_unref(&s->input_frames_ref); @@ -363,20 +361,9 @@ static int do_config_output(AVFilterLink *link) API_AVAILABLE(macos(10.11), ios( goto exit; } - link->time_base.num = ctx->inputs[0]->time_base.num; - link->time_base.den = ctx->inputs[0]->time_base.den * 2; - link->w = ctx->inputs[0]->w; - link->h = ctx->inputs[0]->h; - - if(y->mode & 1) - link->frame_rate = av_mul_q(ctx->inputs[0]->frame_rate, - (AVRational){2, 1}); - - if (link->w < 3 || link->h < 3) { - av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or lines is not supported\n"); - ret = AVERROR(EINVAL); + ret = ff_yadif_config_output_common(link); + if (ret < 0) goto exit; - } y->csp = av_pix_fmt_desc_get(output_frames->sw_format); y->filter = filter; @@ -401,18 +388,18 @@ static int config_output(AVFilterLink *link) static const AVOption yadif_videotoolbox_options[] = { #define OFFSET(x) offsetof(YADIFContext, x) - { "mode", "specify the interlacing mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=YADIF_MODE_SEND_FRAME}, 0, 3, FLAGS, "mode"}, + { "mode", "specify the interlacing mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=YADIF_MODE_SEND_FRAME}, 0, 3, FLAGS, .unit = "mode"}, CONST("send_frame", "send one frame for each frame", YADIF_MODE_SEND_FRAME, "mode"), CONST("send_field", "send one frame for each field", YADIF_MODE_SEND_FIELD, "mode"), CONST("send_frame_nospatial", "send one frame for each frame, but skip spatial interlacing check", YADIF_MODE_SEND_FRAME_NOSPATIAL, "mode"), CONST("send_field_nospatial", "send one frame for each field, but skip spatial interlacing check", YADIF_MODE_SEND_FIELD_NOSPATIAL, "mode"), - { "parity", "specify the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=YADIF_PARITY_AUTO}, -1, 1, FLAGS, "parity" }, + { "parity", "specify the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=YADIF_PARITY_AUTO}, -1, 1, FLAGS, .unit = "parity" }, CONST("tff", "assume top field first", YADIF_PARITY_TFF, "parity"), CONST("bff", "assume bottom field first", YADIF_PARITY_BFF, "parity"), CONST("auto", "auto detect parity", YADIF_PARITY_AUTO, "parity"), - { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=YADIF_DEINT_ALL}, 0, 1, FLAGS, "deint" }, + { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=YADIF_DEINT_ALL}, 0, 1, FLAGS, .unit = "deint" }, CONST("all", "deinterlace all frames", YADIF_DEINT_ALL, "deint"), CONST("interlaced", "only deinterlace frames marked as interlaced", YADIF_DEINT_INTERLACED, "deint"), #undef OFFSET diff --git a/libavfilter/vf_yaepblur.c b/libavfilter/vf_yaepblur.c index 28a74a85a0c..b39738b577a 100644 --- a/libavfilter/vf_yaepblur.c +++ b/libavfilter/vf_yaepblur.c @@ -31,6 +31,7 @@ #include "libavutil/imgutils.h" #include "avfilter.h" #include "internal.h" +#include "video.h" typedef struct YAEPContext { const AVClass *class; @@ -309,13 +310,6 @@ static const AVFilterPad yaep_inputs[] = { }, }; -static const AVFilterPad yaep_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - #define OFFSET(x) offsetof(YAEPContext, x) #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM @@ -338,7 +332,7 @@ const AVFilter ff_vf_yaepblur = { .priv_class = &yaepblur_class, .uninit = uninit, FILTER_INPUTS(yaep_inputs), - FILTER_OUTPUTS(yaep_outputs), + FILTER_OUTPUTS(ff_video_default_filterpad), FILTER_PIXFMTS_ARRAY(pix_fmts), .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, .process_command = ff_filter_process_command, diff --git a/libavfilter/vf_zoompan.c b/libavfilter/vf_zoompan.c index cca979e01ac..e729bda56de 100644 --- a/libavfilter/vf_zoompan.c +++ b/libavfilter/vf_zoompan.c @@ -23,7 +23,6 @@ #include "libavutil/pixdesc.h" #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "internal.h" #include "video.h" #include "libswscale/swscale.h" @@ -351,13 +350,6 @@ static av_cold void uninit(AVFilterContext *ctx) av_frame_free(&s->in); } -static const AVFilterPad inputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - }, -}; - static const AVFilterPad outputs[] = { { .name = "default", @@ -374,7 +366,7 @@ const AVFilter ff_vf_zoompan = { .init = init, .uninit = uninit, .activate = activate, - FILTER_INPUTS(inputs), + FILTER_INPUTS(ff_video_default_filterpad), FILTER_OUTPUTS(outputs), FILTER_PIXFMTS_ARRAY(pix_fmts), }; diff --git a/libavfilter/vf_zscale.c b/libavfilter/vf_zscale.c index 999147b5870..ad8fdb6eb75 100644 --- a/libavfilter/vf_zscale.c +++ b/libavfilter/vf_zscale.c @@ -132,12 +132,6 @@ typedef struct ZScaleContext { zimg_graph_builder_params alpha_params, params; zimg_graph_builder_params alpha_params_tmp, params_tmp; zimg_filter_graph *alpha_graph[MAX_THREADS], *graph[MAX_THREADS]; - - enum AVColorSpace in_colorspace, out_colorspace; - enum AVColorTransferCharacteristic in_trc, out_trc; - enum AVColorPrimaries in_primaries, out_primaries; - enum AVColorRange in_range, out_range; - enum AVChromaLocation in_chromal, out_chromal; } ZScaleContext; typedef struct ThreadData { @@ -193,8 +187,12 @@ static av_cold int init(AVFilterContext *ctx) return 0; } +static enum AVColorRange convert_range_from_zimg(enum zimg_pixel_range_e color_range); + static int query_formats(AVFilterContext *ctx) { + ZScaleContext *s = ctx->priv; + AVFilterFormats *formats; static const enum AVPixelFormat pixel_fmts[] = { AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, @@ -210,10 +208,11 @@ static int query_formats(AVFilterContext *ctx) AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10, + AV_PIX_FMT_YUVA444P12, AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP9, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRP14, AV_PIX_FMT_GBRP16, - AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP16, + AV_PIX_FMT_GBRAP, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP14, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GBRPF32, AV_PIX_FMT_GBRAPF32, AV_PIX_FMT_NONE }; @@ -222,7 +221,27 @@ static int query_formats(AVFilterContext *ctx) ret = ff_formats_ref(ff_make_format_list(pixel_fmts), &ctx->inputs[0]->outcfg.formats); if (ret < 0) return ret; - return ff_formats_ref(ff_make_format_list(pixel_fmts), &ctx->outputs[0]->incfg.formats); + ret = ff_formats_ref(ff_make_format_list(pixel_fmts), &ctx->outputs[0]->incfg.formats); + if (ret < 0) + return ret; + + if ((ret = ff_formats_ref(ff_all_color_spaces(), &ctx->inputs[0]->outcfg.color_spaces)) < 0 || + (ret = ff_formats_ref(ff_all_color_ranges(), &ctx->inputs[0]->outcfg.color_ranges)) < 0) + return ret; + + formats = s->colorspace != ZIMG_MATRIX_UNSPECIFIED && s->colorspace > 0 + ? ff_make_formats_list_singleton(s->colorspace) + : ff_all_color_spaces(); + if ((ret = ff_formats_ref(formats, &ctx->outputs[0]->incfg.color_spaces)) < 0) + return ret; + + formats = s->range != -1 + ? ff_make_formats_list_singleton(convert_range_from_zimg(s->range)) + : ff_all_color_ranges(); + if ((ret = ff_formats_ref(formats, &ctx->outputs[0]->incfg.color_ranges)) < 0) + return ret; + + return 0; } static void slice_params(ZScaleContext *s, int out_h, int in_h) @@ -683,15 +702,9 @@ static int realign_frame(const AVPixFmtDescriptor *desc, AVFrame **frame, int ne static void update_output_color_information(ZScaleContext *s, AVFrame *frame) { - if (s->colorspace != -1) - frame->colorspace = (int)s->dst_format.matrix_coefficients; - if (s->primaries != -1) frame->color_primaries = (int)s->dst_format.color_primaries; - if (s->range != -1) - frame->color_range = convert_range_from_zimg(s->dst_format.pixel_range); - if (s->trc != -1) frame->color_trc = (int)s->dst_format.transfer_characteristics; @@ -780,6 +793,8 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) if ((link->format != outlink->format) || (link->w != outlink->w) || (link->h != outlink->h) || + (link->colorspace != outlink->colorspace) || + (link->color_range != outlink->color_range) || s->first_time || (s->src_format.chroma_location != s->dst_format.chroma_location) || (s->src_format.color_family !=s->dst_format.color_family) || @@ -801,6 +816,8 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) goto fail; av_frame_copy_props(out, in); + out->colorspace = outlink->colorspace; + out->color_range = outlink->color_range; if ((ret = realign_frame(desc, &in, 1)) < 0) goto fail; @@ -810,20 +827,13 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) snprintf(buf, sizeof(buf)-1, "%d", outlink->h); av_opt_set(s, "h", buf, 0); - link->dst->inputs[0]->format = in->format; - link->dst->inputs[0]->w = in->width; - link->dst->inputs[0]->h = in->height; + link->dst->inputs[0]->format = in->format; + link->dst->inputs[0]->w = in->width; + link->dst->inputs[0]->h = in->height; + link->dst->inputs[0]->colorspace = in->colorspace; + link->dst->inputs[0]->color_range = in->color_range; s->nb_threads = av_clip(FFMIN(ff_filter_get_nb_threads(ctx), FFMIN(link->h, outlink->h) / MIN_TILESIZE), 1, MAX_THREADS); - s->in_colorspace = in->colorspace; - s->in_trc = in->color_trc; - s->in_primaries = in->color_primaries; - s->in_range = in->color_range; - s->out_colorspace = out->colorspace; - s->out_trc = out->color_trc; - s->out_primaries = out->color_primaries; - s->out_range = out->color_range; - slice_params(s, out->height, in->height); zimg_image_format_default(&s->src_format, ZIMG_API_VERSION); @@ -900,14 +910,22 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) int x, y; if (odesc->flags & AV_PIX_FMT_FLAG_FLOAT) { for (y = 0; y < out->height; y++) { + const ptrdiff_t row = y * out->linesize[3]; for (x = 0; x < out->width; x++) { - AV_WN32(out->data[3] + x * odesc->comp[3].step + y * out->linesize[3], + AV_WN32(out->data[3] + x * odesc->comp[3].step + row, av_float2int(1.0f)); } } - } else { + } else if (s->dst_format.depth == 8) { for (y = 0; y < outlink->h; y++) memset(out->data[3] + y * out->linesize[3], 0xff, outlink->w); + } else { + const uint16_t max = (1 << s->dst_format.depth) - 1; + for (y = 0; y < outlink->h; y++) { + const ptrdiff_t row = y * out->linesize[3]; + for (x = 0; x < out->width; x++) + AV_WN16(out->data[3] + x * odesc->comp[3].step + row, max); + } } } } else { @@ -976,116 +994,116 @@ static const AVOption zscale_options[] = { { "height", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, .flags = TFLAGS }, { "size", "set video size", OFFSET(size_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, { "s", "set video size", OFFSET(size_str), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, - { "dither", "set dither type", OFFSET(dither), AV_OPT_TYPE_INT, {.i64 = 0}, 0, ZIMG_DITHER_ERROR_DIFFUSION, FLAGS, "dither" }, - { "d", "set dither type", OFFSET(dither), AV_OPT_TYPE_INT, {.i64 = 0}, 0, ZIMG_DITHER_ERROR_DIFFUSION, FLAGS, "dither" }, - { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_DITHER_NONE}, 0, 0, FLAGS, "dither" }, - { "ordered", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_DITHER_ORDERED}, 0, 0, FLAGS, "dither" }, - { "random", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_DITHER_RANDOM}, 0, 0, FLAGS, "dither" }, - { "error_diffusion", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_DITHER_ERROR_DIFFUSION}, 0, 0, FLAGS, "dither" }, - { "filter", "set filter type", OFFSET(filter), AV_OPT_TYPE_INT, {.i64 = ZIMG_RESIZE_BILINEAR}, 0, ZIMG_RESIZE_LANCZOS, FLAGS, "filter" }, - { "f", "set filter type", OFFSET(filter), AV_OPT_TYPE_INT, {.i64 = ZIMG_RESIZE_BILINEAR}, 0, ZIMG_RESIZE_LANCZOS, FLAGS, "filter" }, - { "point", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RESIZE_POINT}, 0, 0, FLAGS, "filter" }, - { "bilinear", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RESIZE_BILINEAR}, 0, 0, FLAGS, "filter" }, - { "bicubic", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RESIZE_BICUBIC}, 0, 0, FLAGS, "filter" }, - { "spline16", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RESIZE_SPLINE16}, 0, 0, FLAGS, "filter" }, - { "spline36", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RESIZE_SPLINE36}, 0, 0, FLAGS, "filter" }, - { "lanczos", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RESIZE_LANCZOS}, 0, 0, FLAGS, "filter" }, - { "out_range", "set color range", OFFSET(range), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_RANGE_FULL, FLAGS, "range" }, - { "range", "set color range", OFFSET(range), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_RANGE_FULL, FLAGS, "range" }, - { "r", "set color range", OFFSET(range), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_RANGE_FULL, FLAGS, "range" }, - { "input", 0, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, "range" }, - { "limited", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RANGE_LIMITED}, 0, 0, FLAGS, "range" }, - { "full", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RANGE_FULL}, 0, 0, FLAGS, "range" }, - { "unknown", 0, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, "range" }, - { "tv", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RANGE_LIMITED}, 0, 0, FLAGS, "range" }, - { "pc", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RANGE_FULL}, 0, 0, FLAGS, "range" }, - { "primaries", "set color primaries", OFFSET(primaries), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "primaries" }, - { "p", "set color primaries", OFFSET(primaries), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "primaries" }, - { "input", 0, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, "primaries" }, - { "709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_709}, 0, 0, FLAGS, "primaries" }, - { "unspecified", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_UNSPECIFIED}, 0, 0, FLAGS, "primaries" }, - { "170m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_170M}, 0, 0, FLAGS, "primaries" }, - { "240m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_240M}, 0, 0, FLAGS, "primaries" }, - { "2020", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_2020}, 0, 0, FLAGS, "primaries" }, - { "unknown", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_UNSPECIFIED}, 0, 0, FLAGS, "primaries" }, - { "bt709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_709}, 0, 0, FLAGS, "primaries" }, - { "bt470m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_470_M}, 0, 0, FLAGS, "primaries" }, - { "bt470bg", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_470_BG}, 0, 0, FLAGS, "primaries" }, - { "smpte170m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_170M}, 0, 0, FLAGS, "primaries" }, - { "smpte240m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_240M}, 0, 0, FLAGS, "primaries" }, - { "film", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_FILM}, 0, 0, FLAGS, "primaries" }, - { "bt2020", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_2020}, 0, 0, FLAGS, "primaries" }, - { "smpte428", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_ST428}, 0, 0, FLAGS, "primaries" }, - { "smpte431", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_ST431_2}, 0, 0, FLAGS, "primaries" }, - { "smpte432", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_ST432_1}, 0, 0, FLAGS, "primaries" }, - { "jedec-p22", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_EBU3213_E}, 0, 0, FLAGS, "primaries" }, - { "ebu3213", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_EBU3213_E}, 0, 0, FLAGS, "primaries" }, - { "transfer", "set transfer characteristic", OFFSET(trc), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "transfer" }, - { "t", "set transfer characteristic", OFFSET(trc), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "transfer" }, - { "input", 0, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, "transfer" }, - { "709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_709}, 0, 0, FLAGS, "transfer" }, - { "unspecified", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_UNSPECIFIED}, 0, 0, FLAGS, "transfer" }, - { "601", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_601}, 0, 0, FLAGS, "transfer" }, - { "linear", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_LINEAR}, 0, 0, FLAGS, "transfer" }, - { "2020_10", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_2020_10}, 0, 0, FLAGS, "transfer" }, - { "2020_12", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_2020_12}, 0, 0, FLAGS, "transfer" }, - { "unknown", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_UNSPECIFIED}, 0, 0, FLAGS, "transfer" }, - { "bt470m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_470_M}, 0, 0, FLAGS, "transfer" }, - { "bt470bg", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_470_BG}, 0, 0, FLAGS, "transfer" }, - { "smpte170m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_601}, 0, 0, FLAGS, "transfer" }, - { "smpte240m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_240M}, 0, 0, FLAGS, "transfer" }, - { "bt709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_709}, 0, 0, FLAGS, "transfer" }, - { "linear", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_LINEAR}, 0, 0, FLAGS, "transfer" }, - { "log100", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_LOG_100}, 0, 0, FLAGS, "transfer" }, - { "log316", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_LOG_316}, 0, 0, FLAGS, "transfer" }, - { "bt2020-10", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_2020_10}, 0, 0, FLAGS, "transfer" }, - { "bt2020-12", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_2020_12}, 0, 0, FLAGS, "transfer" }, - { "smpte2084", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_ST2084}, 0, 0, FLAGS, "transfer" }, - { "iec61966-2-4", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_IEC_61966_2_4},0, 0, FLAGS, "transfer" }, - { "iec61966-2-1", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_IEC_61966_2_1},0, 0, FLAGS, "transfer" }, - { "arib-std-b67", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_ARIB_B67}, 0, 0, FLAGS, "transfer" }, - { "matrix", "set colorspace matrix", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "matrix" }, - { "m", "set colorspace matrix", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "matrix" }, - { "input", 0, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, "matrix" }, - { "709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_709}, 0, 0, FLAGS, "matrix" }, - { "unspecified", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_UNSPECIFIED}, 0, 0, FLAGS, "matrix" }, - { "470bg", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_470BG}, 0, 0, FLAGS, "matrix" }, - { "170m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_170M}, 0, 0, FLAGS, "matrix" }, - { "2020_ncl", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_2020_NCL}, 0, 0, FLAGS, "matrix" }, - { "2020_cl", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_2020_CL}, 0, 0, FLAGS, "matrix" }, - { "unknown", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_UNSPECIFIED}, 0, 0, FLAGS, "matrix" }, - { "gbr", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_RGB}, 0, 0, FLAGS, "matrix" }, - { "bt709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_709}, 0, 0, FLAGS, "matrix" }, - { "fcc", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_FCC}, 0, 0, FLAGS, "matrix" }, - { "bt470bg", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_470BG}, 0, 0, FLAGS, "matrix" }, - { "smpte170m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_170M}, 0, 0, FLAGS, "matrix" }, - { "smpte240m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_240M}, 0, 0, FLAGS, "matrix" }, - { "ycgco", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_YCGCO}, 0, 0, FLAGS, "matrix" }, - { "bt2020nc", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_2020_NCL}, 0, 0, FLAGS, "matrix" }, - { "bt2020c", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_2020_CL}, 0, 0, FLAGS, "matrix" }, - { "chroma-derived-nc",0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_CHROMATICITY_DERIVED_NCL}, 0, 0, FLAGS, "matrix" }, - { "chroma-derived-c", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_CHROMATICITY_DERIVED_CL}, 0, 0, FLAGS, "matrix" }, - { "ictcp", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_ICTCP}, 0, 0, FLAGS, "matrix" }, - { "in_range", "set input color range", OFFSET(range_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_RANGE_FULL, FLAGS, "range" }, - { "rangein", "set input color range", OFFSET(range_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_RANGE_FULL, FLAGS, "range" }, - { "rin", "set input color range", OFFSET(range_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_RANGE_FULL, FLAGS, "range" }, - { "primariesin", "set input color primaries", OFFSET(primaries_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "primaries" }, - { "pin", "set input color primaries", OFFSET(primaries_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "primaries" }, - { "transferin", "set input transfer characteristic", OFFSET(trc_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "transfer" }, - { "tin", "set input transfer characteristic", OFFSET(trc_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "transfer" }, - { "matrixin", "set input colorspace matrix", OFFSET(colorspace_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "matrix" }, - { "min", "set input colorspace matrix", OFFSET(colorspace_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, "matrix" }, - { "chromal", "set output chroma location", OFFSET(chromal), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_CHROMA_BOTTOM, FLAGS, "chroma" }, - { "c", "set output chroma location", OFFSET(chromal), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_CHROMA_BOTTOM, FLAGS, "chroma" }, - { "input", 0, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, "chroma" }, - { "left", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_CHROMA_LEFT}, 0, 0, FLAGS, "chroma" }, - { "center", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_CHROMA_CENTER}, 0, 0, FLAGS, "chroma" }, - { "topleft", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_CHROMA_TOP_LEFT}, 0, 0, FLAGS, "chroma" }, - { "top", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_CHROMA_TOP}, 0, 0, FLAGS, "chroma" }, - { "bottomleft",0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_CHROMA_BOTTOM_LEFT}, 0, 0, FLAGS, "chroma" }, - { "bottom", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_CHROMA_BOTTOM}, 0, 0, FLAGS, "chroma" }, - { "chromalin", "set input chroma location", OFFSET(chromal_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_CHROMA_BOTTOM, FLAGS, "chroma" }, - { "cin", "set input chroma location", OFFSET(chromal_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_CHROMA_BOTTOM, FLAGS, "chroma" }, + { "dither", "set dither type", OFFSET(dither), AV_OPT_TYPE_INT, {.i64 = 0}, 0, ZIMG_DITHER_ERROR_DIFFUSION, FLAGS, .unit = "dither" }, + { "d", "set dither type", OFFSET(dither), AV_OPT_TYPE_INT, {.i64 = 0}, 0, ZIMG_DITHER_ERROR_DIFFUSION, FLAGS, .unit = "dither" }, + { "none", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_DITHER_NONE}, 0, 0, FLAGS, .unit = "dither" }, + { "ordered", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_DITHER_ORDERED}, 0, 0, FLAGS, .unit = "dither" }, + { "random", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_DITHER_RANDOM}, 0, 0, FLAGS, .unit = "dither" }, + { "error_diffusion", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_DITHER_ERROR_DIFFUSION}, 0, 0, FLAGS, .unit = "dither" }, + { "filter", "set filter type", OFFSET(filter), AV_OPT_TYPE_INT, {.i64 = ZIMG_RESIZE_BILINEAR}, 0, ZIMG_RESIZE_LANCZOS, FLAGS, .unit = "filter" }, + { "f", "set filter type", OFFSET(filter), AV_OPT_TYPE_INT, {.i64 = ZIMG_RESIZE_BILINEAR}, 0, ZIMG_RESIZE_LANCZOS, FLAGS, .unit = "filter" }, + { "point", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RESIZE_POINT}, 0, 0, FLAGS, .unit = "filter" }, + { "bilinear", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RESIZE_BILINEAR}, 0, 0, FLAGS, .unit = "filter" }, + { "bicubic", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RESIZE_BICUBIC}, 0, 0, FLAGS, .unit = "filter" }, + { "spline16", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RESIZE_SPLINE16}, 0, 0, FLAGS, .unit = "filter" }, + { "spline36", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RESIZE_SPLINE36}, 0, 0, FLAGS, .unit = "filter" }, + { "lanczos", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RESIZE_LANCZOS}, 0, 0, FLAGS, .unit = "filter" }, + { "out_range", "set color range", OFFSET(range), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_RANGE_FULL, FLAGS, .unit = "range" }, + { "range", "set color range", OFFSET(range), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_RANGE_FULL, FLAGS, .unit = "range" }, + { "r", "set color range", OFFSET(range), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_RANGE_FULL, FLAGS, .unit = "range" }, + { "input", 0, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, .unit = "range" }, + { "limited", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RANGE_LIMITED}, 0, 0, FLAGS, .unit = "range" }, + { "full", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RANGE_FULL}, 0, 0, FLAGS, .unit = "range" }, + { "unknown", 0, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, .unit = "range" }, + { "tv", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RANGE_LIMITED}, 0, 0, FLAGS, .unit = "range" }, + { "pc", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_RANGE_FULL}, 0, 0, FLAGS, .unit = "range" }, + { "primaries", "set color primaries", OFFSET(primaries), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "primaries" }, + { "p", "set color primaries", OFFSET(primaries), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "primaries" }, + { "input", 0, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, .unit = "primaries" }, + { "709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_709}, 0, 0, FLAGS, .unit = "primaries" }, + { "unspecified", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_UNSPECIFIED}, 0, 0, FLAGS, .unit = "primaries" }, + { "170m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_170M}, 0, 0, FLAGS, .unit = "primaries" }, + { "240m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_240M}, 0, 0, FLAGS, .unit = "primaries" }, + { "2020", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_2020}, 0, 0, FLAGS, .unit = "primaries" }, + { "unknown", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_UNSPECIFIED}, 0, 0, FLAGS, .unit = "primaries" }, + { "bt709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_709}, 0, 0, FLAGS, .unit = "primaries" }, + { "bt470m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_470_M}, 0, 0, FLAGS, .unit = "primaries" }, + { "bt470bg", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_470_BG}, 0, 0, FLAGS, .unit = "primaries" }, + { "smpte170m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_170M}, 0, 0, FLAGS, .unit = "primaries" }, + { "smpte240m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_240M}, 0, 0, FLAGS, .unit = "primaries" }, + { "film", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_FILM}, 0, 0, FLAGS, .unit = "primaries" }, + { "bt2020", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_2020}, 0, 0, FLAGS, .unit = "primaries" }, + { "smpte428", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_ST428}, 0, 0, FLAGS, .unit = "primaries" }, + { "smpte431", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_ST431_2}, 0, 0, FLAGS, .unit = "primaries" }, + { "smpte432", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_ST432_1}, 0, 0, FLAGS, .unit = "primaries" }, + { "jedec-p22", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_EBU3213_E}, 0, 0, FLAGS, .unit = "primaries" }, + { "ebu3213", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_PRIMARIES_EBU3213_E}, 0, 0, FLAGS, .unit = "primaries" }, + { "transfer", "set transfer characteristic", OFFSET(trc), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "transfer" }, + { "t", "set transfer characteristic", OFFSET(trc), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "transfer" }, + { "input", 0, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, .unit = "transfer" }, + { "709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_709}, 0, 0, FLAGS, .unit = "transfer" }, + { "unspecified", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_UNSPECIFIED}, 0, 0, FLAGS, .unit = "transfer" }, + { "601", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_601}, 0, 0, FLAGS, .unit = "transfer" }, + { "linear", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_LINEAR}, 0, 0, FLAGS, .unit = "transfer" }, + { "2020_10", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_2020_10}, 0, 0, FLAGS, .unit = "transfer" }, + { "2020_12", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_2020_12}, 0, 0, FLAGS, .unit = "transfer" }, + { "unknown", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_UNSPECIFIED}, 0, 0, FLAGS, .unit = "transfer" }, + { "bt470m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_470_M}, 0, 0, FLAGS, .unit = "transfer" }, + { "bt470bg", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_470_BG}, 0, 0, FLAGS, .unit = "transfer" }, + { "smpte170m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_601}, 0, 0, FLAGS, .unit = "transfer" }, + { "smpte240m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_240M}, 0, 0, FLAGS, .unit = "transfer" }, + { "bt709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_709}, 0, 0, FLAGS, .unit = "transfer" }, + { "linear", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_LINEAR}, 0, 0, FLAGS, .unit = "transfer" }, + { "log100", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_LOG_100}, 0, 0, FLAGS, .unit = "transfer" }, + { "log316", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_LOG_316}, 0, 0, FLAGS, .unit = "transfer" }, + { "bt2020-10", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_2020_10}, 0, 0, FLAGS, .unit = "transfer" }, + { "bt2020-12", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_2020_12}, 0, 0, FLAGS, .unit = "transfer" }, + { "smpte2084", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_ST2084}, 0, 0, FLAGS, .unit = "transfer" }, + { "iec61966-2-4", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_IEC_61966_2_4},0, 0, FLAGS, .unit = "transfer" }, + { "iec61966-2-1", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_IEC_61966_2_1},0, 0, FLAGS, .unit = "transfer" }, + { "arib-std-b67", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_TRANSFER_ARIB_B67}, 0, 0, FLAGS, .unit = "transfer" }, + { "matrix", "set colorspace matrix", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "matrix" }, + { "m", "set colorspace matrix", OFFSET(colorspace), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "matrix" }, + { "input", 0, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, .unit = "matrix" }, + { "709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_709}, 0, 0, FLAGS, .unit = "matrix" }, + { "unspecified", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_UNSPECIFIED}, 0, 0, FLAGS, .unit = "matrix" }, + { "470bg", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_470BG}, 0, 0, FLAGS, .unit = "matrix" }, + { "170m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_170M}, 0, 0, FLAGS, .unit = "matrix" }, + { "2020_ncl", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_2020_NCL}, 0, 0, FLAGS, .unit = "matrix" }, + { "2020_cl", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_2020_CL}, 0, 0, FLAGS, .unit = "matrix" }, + { "unknown", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_UNSPECIFIED}, 0, 0, FLAGS, .unit = "matrix" }, + { "gbr", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_RGB}, 0, 0, FLAGS, .unit = "matrix" }, + { "bt709", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_709}, 0, 0, FLAGS, .unit = "matrix" }, + { "fcc", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_FCC}, 0, 0, FLAGS, .unit = "matrix" }, + { "bt470bg", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_470BG}, 0, 0, FLAGS, .unit = "matrix" }, + { "smpte170m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_170M}, 0, 0, FLAGS, .unit = "matrix" }, + { "smpte240m", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_240M}, 0, 0, FLAGS, .unit = "matrix" }, + { "ycgco", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_YCGCO}, 0, 0, FLAGS, .unit = "matrix" }, + { "bt2020nc", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_2020_NCL}, 0, 0, FLAGS, .unit = "matrix" }, + { "bt2020c", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_2020_CL}, 0, 0, FLAGS, .unit = "matrix" }, + { "chroma-derived-nc",0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_CHROMATICITY_DERIVED_NCL}, 0, 0, FLAGS, .unit = "matrix" }, + { "chroma-derived-c", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_CHROMATICITY_DERIVED_CL}, 0, 0, FLAGS, .unit = "matrix" }, + { "ictcp", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_MATRIX_ICTCP}, 0, 0, FLAGS, .unit = "matrix" }, + { "in_range", "set input color range", OFFSET(range_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_RANGE_FULL, FLAGS, .unit = "range" }, + { "rangein", "set input color range", OFFSET(range_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_RANGE_FULL, FLAGS, .unit = "range" }, + { "rin", "set input color range", OFFSET(range_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_RANGE_FULL, FLAGS, .unit = "range" }, + { "primariesin", "set input color primaries", OFFSET(primaries_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "primaries" }, + { "pin", "set input color primaries", OFFSET(primaries_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "primaries" }, + { "transferin", "set input transfer characteristic", OFFSET(trc_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "transfer" }, + { "tin", "set input transfer characteristic", OFFSET(trc_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "transfer" }, + { "matrixin", "set input colorspace matrix", OFFSET(colorspace_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "matrix" }, + { "min", "set input colorspace matrix", OFFSET(colorspace_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, FLAGS, .unit = "matrix" }, + { "chromal", "set output chroma location", OFFSET(chromal), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_CHROMA_BOTTOM, FLAGS, .unit = "chroma" }, + { "c", "set output chroma location", OFFSET(chromal), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_CHROMA_BOTTOM, FLAGS, .unit = "chroma" }, + { "input", 0, 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, .unit = "chroma" }, + { "left", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_CHROMA_LEFT}, 0, 0, FLAGS, .unit = "chroma" }, + { "center", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_CHROMA_CENTER}, 0, 0, FLAGS, .unit = "chroma" }, + { "topleft", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_CHROMA_TOP_LEFT}, 0, 0, FLAGS, .unit = "chroma" }, + { "top", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_CHROMA_TOP}, 0, 0, FLAGS, .unit = "chroma" }, + { "bottomleft",0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_CHROMA_BOTTOM_LEFT}, 0, 0, FLAGS, .unit = "chroma" }, + { "bottom", 0, 0, AV_OPT_TYPE_CONST, {.i64 = ZIMG_CHROMA_BOTTOM}, 0, 0, FLAGS, .unit = "chroma" }, + { "chromalin", "set input chroma location", OFFSET(chromal_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_CHROMA_BOTTOM, FLAGS, .unit = "chroma" }, + { "cin", "set input chroma location", OFFSET(chromal_in), AV_OPT_TYPE_INT, {.i64 = -1}, -1, ZIMG_CHROMA_BOTTOM, FLAGS, .unit = "chroma" }, { "npl", "set nominal peak luminance", OFFSET(nominal_peak_luminance), AV_OPT_TYPE_DOUBLE, {.dbl = NAN}, 0, DBL_MAX, FLAGS }, { "agamma", "allow approximate gamma", OFFSET(approximate_gamma), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS }, { "param_a", "parameter A, which is parameter \"b\" for bicubic, " diff --git a/libavfilter/video.c b/libavfilter/video.c index 7683ef6fd45..bbd11938356 100644 --- a/libavfilter/video.c +++ b/libavfilter/video.c @@ -29,10 +29,18 @@ #include "libavutil/imgutils.h" #include "avfilter.h" +#include "avfilter_internal.h" #include "framepool.h" #include "internal.h" #include "video.h" +const AVFilterPad ff_video_default_filterpad[1] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + } +}; + AVFrame *ff_null_get_video_buffer(AVFilterLink *link, int w, int h) { return ff_get_video_buffer(link->dst->outputs[0], w, h); @@ -40,6 +48,7 @@ AVFrame *ff_null_get_video_buffer(AVFilterLink *link, int w, int h) AVFrame *ff_default_get_video_buffer2(AVFilterLink *link, int w, int h, int align) { + FilterLinkInternal *const li = ff_link_internal(link); AVFrame *frame = NULL; int pool_width = 0; int pool_height = 0; @@ -61,13 +70,13 @@ AVFrame *ff_default_get_video_buffer2(AVFilterLink *link, int w, int h, int alig return frame; } - if (!link->frame_pool) { - link->frame_pool = ff_frame_pool_video_init(av_buffer_allocz, w, h, - link->format, align); - if (!link->frame_pool) + if (!li->frame_pool) { + li->frame_pool = ff_frame_pool_video_init(av_buffer_allocz, w, h, + link->format, align); + if (!li->frame_pool) return NULL; } else { - if (ff_frame_pool_get_video_config(link->frame_pool, + if (ff_frame_pool_get_video_config(li->frame_pool, &pool_width, &pool_height, &pool_format, &pool_align) < 0) { return NULL; @@ -76,19 +85,21 @@ AVFrame *ff_default_get_video_buffer2(AVFilterLink *link, int w, int h, int alig if (pool_width != w || pool_height != h || pool_format != link->format || pool_align != align) { - ff_frame_pool_uninit((FFFramePool **)&link->frame_pool); - link->frame_pool = ff_frame_pool_video_init(av_buffer_allocz, w, h, - link->format, align); - if (!link->frame_pool) + ff_frame_pool_uninit(&li->frame_pool); + li->frame_pool = ff_frame_pool_video_init(av_buffer_allocz, w, h, + link->format, align); + if (!li->frame_pool) return NULL; } } - frame = ff_frame_pool_get(link->frame_pool); + frame = ff_frame_pool_get(li->frame_pool); if (!frame) return NULL; frame->sample_aspect_ratio = link->sample_aspect_ratio; + frame->colorspace = link->colorspace; + frame->color_range = link->color_range; return frame; } diff --git a/libavfilter/video.h b/libavfilter/video.h index f37bab9d031..81331c3199f 100644 --- a/libavfilter/video.h +++ b/libavfilter/video.h @@ -22,6 +22,13 @@ #define AVFILTER_VIDEO_H #include "avfilter.h" +#include "internal.h" + +/** + * An AVFilterPad array whose only entry has name "default" + * and is of type AVMEDIA_TYPE_VIDEO. + */ +extern const AVFilterPad ff_video_default_filterpad[1]; AVFrame *ff_default_get_video_buffer(AVFilterLink *link, int w, int h); AVFrame *ff_default_get_video_buffer2(AVFilterLink *link, int w, int h, int align); diff --git a/libavfilter/vsrc_cellauto.c b/libavfilter/vsrc_cellauto.c index 8c682a70523..b469f1bf208 100644 --- a/libavfilter/vsrc_cellauto.c +++ b/libavfilter/vsrc_cellauto.c @@ -29,12 +29,10 @@ #include "libavutil/internal.h" #include "libavutil/lfg.h" #include "libavutil/opt.h" -#include "libavutil/parseutils.h" #include "libavutil/random_seed.h" #include "libavutil/avstring.h" #include "avfilter.h" #include "internal.h" -#include "formats.h" #include "video.h" typedef struct CellAutoContext { diff --git a/libavfilter/vsrc_ddagrab.c b/libavfilter/vsrc_ddagrab.c index c89282afae4..018f46ed946 100644 --- a/libavfilter/vsrc_ddagrab.c +++ b/libavfilter/vsrc_ddagrab.c @@ -22,7 +22,6 @@ #undef _WIN32_WINNT #define _WIN32_WINNT 0x0A00 #endif -#define WIN32_LEAN_AND_MEAN #include @@ -44,7 +43,6 @@ #include "compat/w32dlfcn.h" #include "avfilter.h" #include "internal.h" -#include "formats.h" #include "video.h" #include "vsrc_ddagrab_shaders.h" @@ -70,7 +68,9 @@ typedef struct DdagrabContext { int mouse_x, mouse_y; ID3D11Texture2D *mouse_texture; - ID3D11ShaderResourceView* mouse_resource_view ; + ID3D11ShaderResourceView* mouse_resource_view; + ID3D11Texture2D *mouse_xor_texture; + ID3D11ShaderResourceView* mouse_xor_resource_view; AVRational time_base; int64_t time_frame; @@ -82,6 +82,7 @@ typedef struct DdagrabContext { int raw_height; ID3D11Texture2D *probed_texture; + ID3D11Texture2D *buffer_texture; ID3D11VertexShader *vertex_shader; ID3D11InputLayout *input_layout; @@ -89,6 +90,7 @@ typedef struct DdagrabContext { ID3D11Buffer *const_buffer; ID3D11SamplerState *sampler_state; ID3D11BlendState *blend_state; + ID3D11BlendState *blend_state_xor; int output_idx; int draw_mouse; @@ -100,6 +102,7 @@ typedef struct DdagrabContext { int out_fmt; int allow_fallback; int force_fmt; + int dup_frames; } DdagrabContext; #define OFFSET(x) offsetof(DdagrabContext, x) @@ -111,18 +114,20 @@ static const AVOption ddagrab_options[] = { { "video_size", "set video frame size", OFFSET(width), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, 0, FLAGS }, { "offset_x", "capture area x offset", OFFSET(offset_x), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS }, { "offset_y", "capture area y offset", OFFSET(offset_y), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS }, - { "output_fmt", "desired output format", OFFSET(out_fmt), AV_OPT_TYPE_INT, { .i64 = DXGI_FORMAT_B8G8R8A8_UNORM }, 0, INT_MAX, FLAGS, "output_fmt" }, - { "auto", "let dda pick its preferred format", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, INT_MAX, FLAGS, "output_fmt" }, - { "8bit", "only output default 8 Bit format", 0, AV_OPT_TYPE_CONST, { .i64 = DXGI_FORMAT_B8G8R8A8_UNORM }, 0, INT_MAX, FLAGS, "output_fmt" }, - { "bgra", "only output 8 Bit BGRA", 0, AV_OPT_TYPE_CONST, { .i64 = DXGI_FORMAT_B8G8R8A8_UNORM }, 0, INT_MAX, FLAGS, "output_fmt" }, - { "10bit", "only output default 10 Bit format", 0, AV_OPT_TYPE_CONST, { .i64 = DXGI_FORMAT_R10G10B10A2_UNORM }, 0, INT_MAX, FLAGS, "output_fmt" }, - { "x2bgr10", "only output 10 Bit X2BGR10", 0, AV_OPT_TYPE_CONST, { .i64 = DXGI_FORMAT_R10G10B10A2_UNORM }, 0, INT_MAX, FLAGS, "output_fmt" }, - { "16bit", "only output default 16 Bit format", 0, AV_OPT_TYPE_CONST, { .i64 = DXGI_FORMAT_R16G16B16A16_FLOAT },0, INT_MAX, FLAGS, "output_fmt" }, - { "rgbaf16", "only output 16 Bit RGBAF16", 0, AV_OPT_TYPE_CONST, { .i64 = DXGI_FORMAT_R16G16B16A16_FLOAT },0, INT_MAX, FLAGS, "output_fmt" }, + { "output_fmt", "desired output format", OFFSET(out_fmt), AV_OPT_TYPE_INT, { .i64 = DXGI_FORMAT_B8G8R8A8_UNORM }, 0, INT_MAX, FLAGS, .unit = "output_fmt" }, + { "auto", "let dda pick its preferred format", 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, 0, INT_MAX, FLAGS, .unit = "output_fmt" }, + { "8bit", "only output default 8 Bit format", 0, AV_OPT_TYPE_CONST, { .i64 = DXGI_FORMAT_B8G8R8A8_UNORM }, 0, INT_MAX, FLAGS, .unit = "output_fmt" }, + { "bgra", "only output 8 Bit BGRA", 0, AV_OPT_TYPE_CONST, { .i64 = DXGI_FORMAT_B8G8R8A8_UNORM }, 0, INT_MAX, FLAGS, .unit = "output_fmt" }, + { "10bit", "only output default 10 Bit format", 0, AV_OPT_TYPE_CONST, { .i64 = DXGI_FORMAT_R10G10B10A2_UNORM }, 0, INT_MAX, FLAGS, .unit = "output_fmt" }, + { "x2bgr10", "only output 10 Bit X2BGR10", 0, AV_OPT_TYPE_CONST, { .i64 = DXGI_FORMAT_R10G10B10A2_UNORM }, 0, INT_MAX, FLAGS, .unit = "output_fmt" }, + { "16bit", "only output default 16 Bit format", 0, AV_OPT_TYPE_CONST, { .i64 = DXGI_FORMAT_R16G16B16A16_FLOAT },0, INT_MAX, FLAGS, .unit = "output_fmt" }, + { "rgbaf16", "only output 16 Bit RGBAF16", 0, AV_OPT_TYPE_CONST, { .i64 = DXGI_FORMAT_R16G16B16A16_FLOAT },0, INT_MAX, FLAGS, .unit = "output_fmt" }, { "allow_fallback", "don't error on fallback to default 8 Bit format", OFFSET(allow_fallback), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "force_fmt", "exclude BGRA from format list (experimental, discouraged by Microsoft)", OFFSET(force_fmt), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, + { "dup_frames", "duplicate frames to maintain framerate", + OFFSET(dup_frames), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS }, { NULL } }; @@ -142,6 +147,7 @@ static av_cold void ddagrab_uninit(AVFilterContext *avctx) DdagrabContext *dda = avctx->priv; release_resource(&dda->blend_state); + release_resource(&dda->blend_state_xor); release_resource(&dda->sampler_state); release_resource(&dda->pixel_shader); release_resource(&dda->input_layout); @@ -149,10 +155,13 @@ static av_cold void ddagrab_uninit(AVFilterContext *avctx) release_resource(&dda->const_buffer); release_resource(&dda->probed_texture); + release_resource(&dda->buffer_texture); release_resource(&dda->dxgi_outdupl); release_resource(&dda->mouse_resource_view); release_resource(&dda->mouse_texture); + release_resource(&dda->mouse_xor_resource_view); + release_resource(&dda->mouse_xor_texture); av_frame_free(&dda->last_frame); av_buffer_unref(&dda->frames_ref); @@ -414,6 +423,16 @@ static av_cold int init_render_resources(AVFilterContext *avctx) return AVERROR_EXTERNAL; } + blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_INV_DEST_COLOR; + blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_COLOR; + hr = ID3D11Device_CreateBlendState(dev, + &blend_desc, + &dda->blend_state_xor); + if (FAILED(hr)) { + av_log(avctx, AV_LOG_ERROR, "CreateBlendState (xor) failed: %lx\n", hr); + return AVERROR_EXTERNAL; + } + return 0; } @@ -472,7 +491,7 @@ static int create_d3d11_pointer_tex(AVFilterContext *avctx, } hr = ID3D11Device_CreateShaderResourceView(dda->device_hwctx->device, - (ID3D11Resource*)dda->mouse_texture, + (ID3D11Resource*)*out_tex, &resource_desc, res_view); if (FAILED(hr)) { @@ -484,50 +503,102 @@ static int create_d3d11_pointer_tex(AVFilterContext *avctx, return 0; } -static uint8_t *convert_mono_buffer(uint8_t *input, int *_width, int *_height, int *_pitch) +static int convert_mono_buffer(uint8_t *input, uint8_t **rgba_out, uint8_t **xor_out, int *_width, int *_height, int *_pitch) { int width = *_width, height = *_height, pitch = *_pitch; int real_height = height / 2; + int size = real_height * pitch; + uint8_t *output = av_malloc(real_height * width * 4); + uint8_t *output_xor = av_malloc(real_height * width * 4); + int y, x; - if (!output) - return NULL; + if (!output || !output_xor) { + av_free(output); + av_free(output_xor); + return AVERROR(ENOMEM); + } - // This simulates drawing the cursor on a full black surface - // i.e. ignore the AND mask, turn XOR mask into all 4 color channels for (y = 0; y < real_height; y++) { for (x = 0; x < width; x++) { - int v = input[(real_height + y) * pitch + (x / 8)]; - v = (v >> (7 - (x % 8))) & 1; - memset(&output[4 * ((y*width) + x)], v ? 0xFF : 0, 4); + int in_pos = (y * pitch) + (x / 8); + int out_pos = 4 * ((y * width) + x); + int and_val = (input[in_pos] >> (7 - (x % 8))) & 1; + int xor_val = (input[in_pos + size] >> (7 - (x % 8))) & 1; + + if (!and_val && !xor_val) { + // solid black + memset(&output[out_pos], 0, 4); + output[out_pos + 3] = 0xFF; + + // transparent + memset(&output_xor[out_pos], 0, 4); + } else if (and_val && !xor_val) { + // transparent + memset(&output[out_pos], 0, 4); + + // transparent + memset(&output_xor[out_pos], 0, 4); + } else if (!and_val && xor_val) { + // solid white + memset(&output[out_pos], 0xFF, 4); + + // transparent + memset(&output_xor[out_pos], 0, 4); + } else if (and_val && xor_val) { + // transparent + memset(&output[out_pos], 0, 4); + + // solid white -> invert color + memset(&output_xor[out_pos], 0xFF, 4); + } } } *_pitch = width * 4; *_height = real_height; + *rgba_out = output; + *xor_out = output_xor; - return output; + return 0; } -static void fixup_color_mask(uint8_t *buf, int width, int height, int pitch) +static int fixup_color_mask(uint8_t *input, uint8_t **rgba_out, uint8_t **xor_out, int width, int height, int pitch) { + int size = height * pitch; + uint8_t *output = av_malloc(size); + uint8_t *output_xor = av_malloc(size); int x, y; - // There is no good way to replicate XOR'ig parts of the texture with the screen - // best effort is rendering the non-masked parts, and make the rest transparent + + if (!output || !output_xor) { + av_free(output); + av_free(output_xor); + return AVERROR(ENOMEM); + } + + memcpy(output, input, size); + memcpy(output_xor, input, size); + for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { int pos = (y*pitch) + (4*x) + 3; - buf[pos] = buf[pos] ? 0 : 0xFF; + output[pos] = input[pos] ? 0 : 0xFF; + output_xor[pos] = input[pos] ? 0xFF : 0; } } + + *rgba_out = output; + *xor_out = output_xor; + + return 0; } static int update_mouse_pointer(AVFilterContext *avctx, DXGI_OUTDUPL_FRAME_INFO *frame_info) { DdagrabContext *dda = avctx->priv; HRESULT hr; - int ret; + int ret, ret2; if (frame_info->LastMouseUpdateTime.QuadPart == 0) return 0; @@ -557,6 +628,7 @@ static int update_mouse_pointer(AVFilterContext *avctx, DXGI_OUTDUPL_FRAME_INFO if (frame_info->PointerShapeBufferSize) { UINT size = frame_info->PointerShapeBufferSize; DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info; + uint8_t *rgba_buf = NULL, *rgb_xor_buf = NULL; uint8_t *buf = av_malloc(size); if (!buf) return AVERROR(ENOMEM); @@ -573,26 +645,37 @@ static int update_mouse_pointer(AVFilterContext *avctx, DXGI_OUTDUPL_FRAME_INFO } if (shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME) { - uint8_t *new_buf = convert_mono_buffer(buf, &shape_info.Width, &shape_info.Height, &shape_info.Pitch); - av_free(buf); - if (!new_buf) - return AVERROR(ENOMEM); - buf = new_buf; + ret = convert_mono_buffer(buf, &rgba_buf, &rgb_xor_buf, &shape_info.Width, &shape_info.Height, &shape_info.Pitch); + av_freep(&buf); + if (ret < 0) + return ret; } else if (shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR) { - fixup_color_mask(buf, shape_info.Width, shape_info.Height, shape_info.Pitch); - } else if (shape_info.Type != DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) { + ret = fixup_color_mask(buf, &rgba_buf, &rgb_xor_buf, shape_info.Width, shape_info.Height, shape_info.Pitch); + av_freep(&buf); + if (ret < 0) + return ret; + } else if (shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR) { + rgba_buf = buf; + buf = NULL; + } else { av_log(avctx, AV_LOG_WARNING, "Unsupported pointer shape type: %d\n", (int)shape_info.Type); - av_free(buf); + av_freep(&buf); return 0; } release_resource(&dda->mouse_resource_view); release_resource(&dda->mouse_texture); + release_resource(&dda->mouse_xor_resource_view); + release_resource(&dda->mouse_xor_texture); - ret = create_d3d11_pointer_tex(avctx, buf, &shape_info, &dda->mouse_texture, &dda->mouse_resource_view); - av_freep(&buf); + ret = create_d3d11_pointer_tex(avctx, rgba_buf, &shape_info, &dda->mouse_texture, &dda->mouse_resource_view); + ret2 = rgb_xor_buf ? create_d3d11_pointer_tex(avctx, rgb_xor_buf, &shape_info, &dda->mouse_xor_texture, &dda->mouse_xor_resource_view) : 0; + av_freep(&rgba_buf); + av_freep(&rgb_xor_buf); if (ret < 0) return ret; + if (ret2 < 0) + return ret2; av_log(avctx, AV_LOG_VERBOSE, "Updated pointer shape texture\n"); } @@ -626,9 +709,29 @@ static int next_frame_internal(AVFilterContext *avctx, ID3D11Texture2D **desktop goto error; } - if (need_frame && (!frame_info.LastPresentTime.QuadPart || !frame_info.AccumulatedFrames)) { - ret = AVERROR(EAGAIN); - goto error; + if (!frame_info.LastPresentTime.QuadPart || !frame_info.AccumulatedFrames) { + if (need_frame) { + ret = AVERROR(EAGAIN); + goto error; + } + + // Unforunately, we can't rely on the desktop_resource's format in this case. + // The API might even return it in with a format that was not in the initial + // list of supported formats, and it can change/flicker randomly. + // To work around this, return an internal copy of the last valid texture we got. + release_resource(&desktop_resource); + + // The initial probing should make this impossible. + if (!dda->buffer_texture) { + av_log(avctx, AV_LOG_ERROR, "No buffer texture while operating!\n"); + ret = AVERROR_BUG; + goto error; + } + + av_log(avctx, AV_LOG_TRACE, "Returning internal buffer for a frame!\n"); + ID3D11Texture2D_AddRef(dda->buffer_texture); + *desktop_texture = dda->buffer_texture; + return 0; } hr = IDXGIResource_QueryInterface(desktop_resource, &IID_ID3D11Texture2D, (void**)desktop_texture); @@ -639,6 +742,27 @@ static int next_frame_internal(AVFilterContext *avctx, ID3D11Texture2D **desktop goto error; } + if (!dda->buffer_texture) { + D3D11_TEXTURE2D_DESC desc; + ID3D11Texture2D_GetDesc(*desktop_texture, &desc); + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = 0; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + + hr = ID3D11Device_CreateTexture2D(dda->device_hwctx->device, &desc, NULL, &dda->buffer_texture); + if (FAILED(hr)) { + release_resource(desktop_texture); + av_log(avctx, AV_LOG_ERROR, "Failed creating internal buffer texture.\n"); + ret = AVERROR(ENOMEM); + goto error; + } + } + + ID3D11DeviceContext_CopyResource(dda->device_hwctx->device_context, + (ID3D11Resource*)dda->buffer_texture, + (ID3D11Resource*)*desktop_texture); + return 0; error: @@ -936,6 +1060,13 @@ static int draw_mouse_pointer(AVFilterContext *avctx, AVFrame *frame) ID3D11DeviceContext_Draw(devctx, num_vertices, 0); + if (dda->mouse_xor_resource_view) { + ID3D11DeviceContext_PSSetShaderResources(devctx, 0, 1, &dda->mouse_xor_resource_view); + ID3D11DeviceContext_OMSetBlendState(devctx, dda->blend_state_xor, NULL, 0xFFFFFFFF); + + ID3D11DeviceContext_Draw(devctx, num_vertices, 0); + } + end: release_resource(&mouse_vertex_buffer); release_resource(&target_view); @@ -982,7 +1113,9 @@ static int ddagrab_request_frame(AVFilterLink *outlink) now -= dda->first_pts; if (!dda->probed_texture) { - ret = next_frame_internal(avctx, &cur_texture, 0); + do { + ret = next_frame_internal(avctx, &cur_texture, 0); + } while (ret == AVERROR(EAGAIN) && !dda->dup_frames); } else { cur_texture = dda->probed_texture; dda->probed_texture = NULL; @@ -1018,7 +1151,7 @@ static int ddagrab_request_frame(AVFilterLink *outlink) if (desc.Format != dda->raw_format || (int)desc.Width != dda->raw_width || (int)desc.Height != dda->raw_height) { - av_log(avctx, AV_LOG_ERROR, "Output parameters changed!"); + av_log(avctx, AV_LOG_ERROR, "Output parameters changed!\n"); ret = AVERROR_OUTPUT_CHANGED; goto fail; } @@ -1078,8 +1211,7 @@ static int ddagrab_request_frame(AVFilterLink *outlink) goto fail; } - av_frame_unref(dda->last_frame); - ret = av_frame_ref(dda->last_frame, frame); + ret = av_frame_replace(dda->last_frame, frame); if (ret < 0) return ret; @@ -1120,4 +1252,5 @@ const AVFilter ff_vsrc_ddagrab = { FILTER_OUTPUTS(ddagrab_outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_D3D11), .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, + .flags = AVFILTER_FLAG_HWDEVICE, }; diff --git a/libavfilter/vsrc_gradients.c b/libavfilter/vsrc_gradients.c index 3c524b92423..567a4a311dc 100644 --- a/libavfilter/vsrc_gradients.c +++ b/libavfilter/vsrc_gradients.c @@ -20,13 +20,10 @@ #include "avfilter.h" #include "filters.h" -#include "formats.h" #include "video.h" #include "internal.h" #include "libavutil/imgutils.h" -#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" -#include "libavutil/parseutils.h" #include "libavutil/lfg.h" #include "libavutil/random_seed.h" #include @@ -40,6 +37,7 @@ typedef struct GradientsContext { int64_t pts; int64_t duration; ///< duration expressed in microseconds float speed; + float angle; uint8_t color_rgba[8][4]; float color_rgbaf[8][4]; @@ -55,6 +53,7 @@ typedef struct GradientsContext { #define OFFSET(x) offsetof(GradientsContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM +#define VFT AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_RUNTIME_PARAM static const AVOption gradients_options[] = { {"size", "set frame size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, {.str="640x480"}, 0, 0, FLAGS }, @@ -78,13 +77,14 @@ static const AVOption gradients_options[] = { {"seed", "set the seed", OFFSET(seed), AV_OPT_TYPE_INT64, {.i64=-1}, -1, UINT32_MAX, FLAGS }, {"duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=-1}, -1, INT64_MAX, FLAGS }, {"d", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=-1}, -1, INT64_MAX, FLAGS }, - {"speed", "set gradients rotation speed", OFFSET(speed), AV_OPT_TYPE_FLOAT,{.dbl=0.01}, 0.00001, 1, FLAGS }, - {"type", "set gradient type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "type" }, - {"t", "set gradient type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 3, FLAGS, "type" }, - {"linear", "set gradient type", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "type" }, - {"radial", "set gradient type", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "type" }, - {"circular", "set gradient type", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, FLAGS, "type" }, - {"spiral", "set gradient type", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, FLAGS, "type" }, + {"speed", "set gradients rotation speed", OFFSET(speed), AV_OPT_TYPE_FLOAT,{.dbl=0.01}, 0, 1, VFT }, + {"type", "set gradient type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 4, VFT, .unit = "type" }, + {"t", "set gradient type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 4, VFT, .unit = "type" }, + { "linear", "set linear gradient", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VFT, .unit = "type" }, + { "radial", "set radial gradient", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, VFT, .unit = "type" }, + { "circular", "set circular gradient", 0, AV_OPT_TYPE_CONST, {.i64=2}, 0, 0, VFT, .unit = "type" }, + { "spiral", "set spiral gradient", 0, AV_OPT_TYPE_CONST, {.i64=3}, 0, 0, VFT, .unit = "type" }, + { "square", "set square gradient", 0, AV_OPT_TYPE_CONST, {.i64=4}, 0, 0, VFT, .unit = "type" }, {NULL}, }; @@ -111,13 +111,13 @@ static uint64_t lerp_color16(uint8_t c0[4], uint8_t c1[4], float x) { const float y = 1.f - x; - return (llrintf((c0[0] * y + c1[0] * x) * 256)) << 0 | - (llrintf((c0[1] * y + c1[1] * x) * 256)) << 16 | - (llrintf((c0[2] * y + c1[2] * x) * 256)) << 32 | - (llrintf((c0[3] * y + c1[3] * x) * 256)) << 48; + return ((uint64_t)llrintf((c0[0] * y + c1[0] * x) * 256)) << 0 | + ((uint64_t)llrintf((c0[1] * y + c1[1] * x) * 256)) << 16 | + ((uint64_t)llrintf((c0[2] * y + c1[2] * x) * 256)) << 32 | + ((uint64_t)llrintf((c0[3] * y + c1[3] * x) * 256)) << 48; } -static uint32_t lerp_colors(uint8_t arr[3][4], int nb_colors, int nb_wrap_colors, float step) +static uint32_t lerp_colors(uint8_t arr[8][4], int nb_colors, int nb_wrap_colors, float step) { float scl; int i, j; @@ -140,7 +140,7 @@ static uint32_t lerp_colors(uint8_t arr[3][4], int nb_colors, int nb_wrap_colors return lerp_color(arr[i], arr[j], scl - i); } -static uint64_t lerp_colors16(uint8_t arr[3][4], int nb_colors, int nb_wrap_colors, float step) +static uint64_t lerp_colors16(uint8_t arr[8][4], int nb_colors, int nb_wrap_colors, float step) { float scl; int i, j; @@ -163,7 +163,7 @@ static uint64_t lerp_colors16(uint8_t arr[3][4], int nb_colors, int nb_wrap_colo return lerp_color16(arr[i], arr[j], scl - i); } -static void lerp_colors32(float arr[3][4], int nb_colors, +static void lerp_colors32(float arr[8][4], int nb_colors, int nb_wrap_colors, float step, float *r, float *g, float *b, float *a) { @@ -187,12 +187,12 @@ static void lerp_colors32(float arr[3][4], int nb_colors, scl = step * (nb_wrap_colors - 1); i = floorf(scl); - x = scl - i; j = i + 1; if (i >= nb_colors - 1) { i = nb_colors - 1; j = 0; } + x = scl - i; *r = lerpf(arr[i][0], arr[j][0], x); *g = lerpf(arr[i][1], arr[j][1], x); @@ -222,6 +222,9 @@ static float project(float origin_x, float origin_y, case 3: od_s_q = M_PI * 2.f; break; + case 4: + od_s_q = fmaxf(fabsf(od_x), fabsf(od_y)); + break; } switch (type) { @@ -237,6 +240,9 @@ static float project(float origin_x, float origin_y, case 3: op_x_od = fmodf(atan2f(op_x, op_y) + M_PI + point_x / fmaxf(origin_x, dest_x), 2.f * M_PI); break; + case 4: + op_x_od = fmaxf(fabsf(op_x), fabsf(op_y)); + break; } // Normalize and clamp range. @@ -251,13 +257,14 @@ static int draw_gradients_slice(AVFilterContext *ctx, void *arg, int job, int nb const int height = frame->height; const int start = (height * job ) / nb_jobs; const int end = (height * (job+1)) / nb_jobs; - const int linesize = frame->linesize[0] / 4; + const ptrdiff_t linesize = frame->linesize[0] / 4; uint32_t *dst = (uint32_t *)frame->data[0] + start * linesize; + const int type = s->type; for (int y = start; y < end; y++) { for (int x = 0; x < width; x++) { - float factor = project(s->fx0, s->fy0, s->fx1, s->fy1, x, y, s->type); - dst[x] = lerp_colors(s->color_rgba, s->nb_colors, s->nb_colors + (s->type >= 2), factor); + float factor = project(s->fx0, s->fy0, s->fx1, s->fy1, x, y, type); + dst[x] = lerp_colors(s->color_rgba, s->nb_colors, s->nb_colors + (type >= 2 && type <= 3), factor); } dst += linesize; @@ -274,13 +281,14 @@ static int draw_gradients_slice16(AVFilterContext *ctx, void *arg, int job, int const int height = frame->height; const int start = (height * job ) / nb_jobs; const int end = (height * (job+1)) / nb_jobs; - const int linesize = frame->linesize[0] / 8; + const ptrdiff_t linesize = frame->linesize[0] / 8; uint64_t *dst = (uint64_t *)frame->data[0] + start * linesize; + const int type = s->type; for (int y = start; y < end; y++) { for (int x = 0; x < width; x++) { - float factor = project(s->fx0, s->fy0, s->fx1, s->fy1, x, y, s->type); - dst[x] = lerp_colors16(s->color_rgba, s->nb_colors, s->nb_colors + s->type >= 2, factor); + float factor = project(s->fx0, s->fy0, s->fx1, s->fy1, x, y, type); + dst[x] = lerp_colors16(s->color_rgba, s->nb_colors, s->nb_colors + (type >= 2 && type <= 3), factor); } dst += linesize; @@ -297,19 +305,20 @@ static int draw_gradients_slice32_planar(AVFilterContext *ctx, void *arg, int jo const int height = frame->height; const int start = (height * job ) / nb_jobs; const int end = (height * (job+1)) / nb_jobs; - const int linesize_g = frame->linesize[0] / 4; - const int linesize_b = frame->linesize[1] / 4; - const int linesize_r = frame->linesize[2] / 4; - const int linesize_a = frame->linesize[3] / 4; + const ptrdiff_t linesize_g = frame->linesize[0] / 4; + const ptrdiff_t linesize_b = frame->linesize[1] / 4; + const ptrdiff_t linesize_r = frame->linesize[2] / 4; + const ptrdiff_t linesize_a = frame->linesize[3] / 4; float *dst_g = (float *)frame->data[0] + start * linesize_g; - float *dst_b = (float *)frame->data[0] + start * linesize_b; - float *dst_r = (float *)frame->data[0] + start * linesize_r; - float *dst_a = (float *)frame->data[0] + start * linesize_a; + float *dst_b = (float *)frame->data[1] + start * linesize_b; + float *dst_r = (float *)frame->data[2] + start * linesize_r; + float *dst_a = (float *)frame->data[3] + start * linesize_a; + const int type = s->type; for (int y = start; y < end; y++) { for (int x = 0; x < width; x++) { - float factor = project(s->fx0, s->fy0, s->fx1, s->fy1, x, y, s->type); - lerp_colors32(s->color_rgbaf, s->nb_colors, s->nb_colors + s->type >= 2 ,factor, + float factor = project(s->fx0, s->fy0, s->fx1, s->fy1, x, y, type); + lerp_colors32(s->color_rgbaf, s->nb_colors, s->nb_colors + (type >= 2 && type <= 3), factor, &dst_r[x], &dst_g[x], &dst_b[x], &dst_a[x]); } @@ -384,10 +393,12 @@ static int activate(AVFilterContext *ctx) if (ff_outlink_frame_wanted(outlink)) { AVFrame *frame = ff_get_video_buffer(outlink, s->w, s->h); - float angle = fmodf(s->pts * s->speed, 2.f * M_PI); + float angle = fmodf(s->angle, 2.f * M_PI); const float w2 = s->w / 2.f; const float h2 = s->h / 2.f; + s->angle = angle + s->speed; + s->fx0 = (s->x0 - w2) * cosf(angle) - (s->y0 - h2) * sinf(angle) + w2; s->fy0 = (s->x0 - w2) * sinf(angle) + (s->y0 - h2) * cosf(angle) + h2; @@ -397,8 +408,19 @@ static int activate(AVFilterContext *ctx) if (!frame) return AVERROR(ENOMEM); +#if FF_API_FRAME_KEY +FF_DISABLE_DEPRECATION_WARNINGS frame->key_frame = 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + frame->flags |= AV_FRAME_FLAG_KEY; +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS frame->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + frame->flags &= ~AV_FRAME_FLAG_INTERLACED; frame->pict_type = AV_PICTURE_TYPE_I; frame->sample_aspect_ratio = (AVRational) {1, 1}; frame->pts = s->pts++; @@ -431,4 +453,5 @@ const AVFilter ff_vsrc_gradients = { FILTER_PIXFMTS(AV_PIX_FMT_RGBA, AV_PIX_FMT_RGBA64, AV_PIX_FMT_GBRAPF32), .activate = activate, .flags = AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, }; diff --git a/libavfilter/vsrc_mandelbrot.c b/libavfilter/vsrc_mandelbrot.c index aa63f9edd1f..8eede773779 100644 --- a/libavfilter/vsrc_mandelbrot.c +++ b/libavfilter/vsrc_mandelbrot.c @@ -27,12 +27,10 @@ */ #include "avfilter.h" -#include "formats.h" #include "video.h" #include "internal.h" #include "libavutil/imgutils.h" #include "libavutil/opt.h" -#include "libavutil/parseutils.h" #include #include @@ -102,17 +100,17 @@ static const AVOption mandelbrot_options[] = { {"morphyf", "set morph y frequency", OFFSET(morphyf), AV_OPT_TYPE_DOUBLE, {.dbl=0.0123}, -FLT_MAX, FLT_MAX, FLAGS }, {"morphamp", "set morph amplitude", OFFSET(morphamp), AV_OPT_TYPE_DOUBLE, {.dbl=0}, -FLT_MAX, FLT_MAX, FLAGS }, - {"outer", "set outer coloring mode", OFFSET(outer), AV_OPT_TYPE_INT, {.i64=NORMALIZED_ITERATION_COUNT}, 0, INT_MAX, FLAGS, "outer" }, - {"iteration_count", "set iteration count mode", 0, AV_OPT_TYPE_CONST, {.i64=ITERATION_COUNT}, INT_MIN, INT_MAX, FLAGS, "outer" }, - {"normalized_iteration_count", "set normalized iteration count mode", 0, AV_OPT_TYPE_CONST, {.i64=NORMALIZED_ITERATION_COUNT}, INT_MIN, INT_MAX, FLAGS, "outer" }, - {"white", "set white mode", 0, AV_OPT_TYPE_CONST, {.i64=WHITE}, INT_MIN, INT_MAX, FLAGS, "outer" }, - {"outz", "set outz mode", 0, AV_OPT_TYPE_CONST, {.i64=OUTZ}, INT_MIN, INT_MAX, FLAGS, "outer" }, - - {"inner", "set inner coloring mode", OFFSET(inner), AV_OPT_TYPE_INT, {.i64=MINCOL}, 0, INT_MAX, FLAGS, "inner" }, - {"black", "set black mode", 0, AV_OPT_TYPE_CONST, {.i64=BLACK}, INT_MIN, INT_MAX, FLAGS, "inner"}, - {"period", "set period mode", 0, AV_OPT_TYPE_CONST, {.i64=PERIOD}, INT_MIN, INT_MAX, FLAGS, "inner"}, - {"convergence", "show time until convergence", 0, AV_OPT_TYPE_CONST, {.i64=CONVTIME}, INT_MIN, INT_MAX, FLAGS, "inner"}, - {"mincol", "color based on point closest to the origin of the iterations", 0, AV_OPT_TYPE_CONST, {.i64=MINCOL}, INT_MIN, INT_MAX, FLAGS, "inner"}, + {"outer", "set outer coloring mode", OFFSET(outer), AV_OPT_TYPE_INT, {.i64=NORMALIZED_ITERATION_COUNT}, 0, INT_MAX, FLAGS, .unit = "outer" }, + {"iteration_count", "set iteration count mode", 0, AV_OPT_TYPE_CONST, {.i64=ITERATION_COUNT}, INT_MIN, INT_MAX, FLAGS, .unit = "outer" }, + {"normalized_iteration_count", "set normalized iteration count mode", 0, AV_OPT_TYPE_CONST, {.i64=NORMALIZED_ITERATION_COUNT}, INT_MIN, INT_MAX, FLAGS, .unit = "outer" }, + {"white", "set white mode", 0, AV_OPT_TYPE_CONST, {.i64=WHITE}, INT_MIN, INT_MAX, FLAGS, .unit = "outer" }, + {"outz", "set outz mode", 0, AV_OPT_TYPE_CONST, {.i64=OUTZ}, INT_MIN, INT_MAX, FLAGS, .unit = "outer" }, + + {"inner", "set inner coloring mode", OFFSET(inner), AV_OPT_TYPE_INT, {.i64=MINCOL}, 0, INT_MAX, FLAGS, .unit = "inner" }, + {"black", "set black mode", 0, AV_OPT_TYPE_CONST, {.i64=BLACK}, INT_MIN, INT_MAX, FLAGS, .unit = "inner"}, + {"period", "set period mode", 0, AV_OPT_TYPE_CONST, {.i64=PERIOD}, INT_MIN, INT_MAX, FLAGS, .unit = "inner"}, + {"convergence", "show time until convergence", 0, AV_OPT_TYPE_CONST, {.i64=CONVTIME}, INT_MIN, INT_MAX, FLAGS, .unit = "inner"}, + {"mincol", "color based on point closest to the origin of the iterations", 0, AV_OPT_TYPE_CONST, {.i64=MINCOL}, INT_MIN, INT_MAX, FLAGS, .unit = "inner"}, {NULL}, }; diff --git a/libavfilter/vsrc_mptestsrc.c b/libavfilter/vsrc_mptestsrc.c index 55b723af004..0395af35e8a 100644 --- a/libavfilter/vsrc_mptestsrc.c +++ b/libavfilter/vsrc_mptestsrc.c @@ -23,13 +23,10 @@ * MP test source, ported from MPlayer libmpcodecs/vf_test.c */ -#include "libavutil/avstring.h" #include "libavutil/opt.h" -#include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" #include "avfilter.h" #include "internal.h" -#include "formats.h" #include "video.h" #define WIDTH 512 @@ -67,19 +64,19 @@ static const AVOption mptestsrc_options[]= { { "duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS }, { "d", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = -1}, -1, INT64_MAX, FLAGS }, - { "test", "set test to perform", OFFSET(test), AV_OPT_TYPE_INT, {.i64=TEST_ALL}, 0, INT_MAX, FLAGS, "test" }, - { "t", "set test to perform", OFFSET(test), AV_OPT_TYPE_INT, {.i64=TEST_ALL}, 0, INT_MAX, FLAGS, "test" }, - { "dc_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_DC_LUMA}, INT_MIN, INT_MAX, FLAGS, "test" }, - { "dc_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_DC_CHROMA}, INT_MIN, INT_MAX, FLAGS, "test" }, - { "freq_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_FREQ_LUMA}, INT_MIN, INT_MAX, FLAGS, "test" }, - { "freq_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_FREQ_CHROMA}, INT_MIN, INT_MAX, FLAGS, "test" }, - { "amp_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_AMP_LUMA}, INT_MIN, INT_MAX, FLAGS, "test" }, - { "amp_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_AMP_CHROMA}, INT_MIN, INT_MAX, FLAGS, "test" }, - { "cbp", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_CBP}, INT_MIN, INT_MAX, FLAGS, "test" }, - { "mv", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_MV}, INT_MIN, INT_MAX, FLAGS, "test" }, - { "ring1", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_RING1}, INT_MIN, INT_MAX, FLAGS, "test" }, - { "ring2", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_RING2}, INT_MIN, INT_MAX, FLAGS, "test" }, - { "all", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_ALL}, INT_MIN, INT_MAX, FLAGS, "test" }, + { "test", "set test to perform", OFFSET(test), AV_OPT_TYPE_INT, {.i64=TEST_ALL}, 0, INT_MAX, FLAGS, .unit = "test" }, + { "t", "set test to perform", OFFSET(test), AV_OPT_TYPE_INT, {.i64=TEST_ALL}, 0, INT_MAX, FLAGS, .unit = "test" }, + { "dc_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_DC_LUMA}, INT_MIN, INT_MAX, FLAGS, .unit = "test" }, + { "dc_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_DC_CHROMA}, INT_MIN, INT_MAX, FLAGS, .unit = "test" }, + { "freq_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_FREQ_LUMA}, INT_MIN, INT_MAX, FLAGS, .unit = "test" }, + { "freq_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_FREQ_CHROMA}, INT_MIN, INT_MAX, FLAGS, .unit = "test" }, + { "amp_luma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_AMP_LUMA}, INT_MIN, INT_MAX, FLAGS, .unit = "test" }, + { "amp_chroma", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_AMP_CHROMA}, INT_MIN, INT_MAX, FLAGS, .unit = "test" }, + { "cbp", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_CBP}, INT_MIN, INT_MAX, FLAGS, .unit = "test" }, + { "mv", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_MV}, INT_MIN, INT_MAX, FLAGS, .unit = "test" }, + { "ring1", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_RING1}, INT_MIN, INT_MAX, FLAGS, .unit = "test" }, + { "ring2", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_RING2}, INT_MIN, INT_MAX, FLAGS, .unit = "test" }, + { "all", "", 0, AV_OPT_TYPE_CONST, {.i64=TEST_ALL}, INT_MIN, INT_MAX, FLAGS, .unit = "test" }, { "max_frames", "Set the maximum number of frames generated for each test", OFFSET(max_frames), AV_OPT_TYPE_INT64, {.i64 = 30}, 1, INT64_MAX, FLAGS }, { "m", "Set the maximum number of frames generated for each test", OFFSET(max_frames), diff --git a/libavfilter/vsrc_sierpinski.c b/libavfilter/vsrc_sierpinski.c index 72b28b1bd79..2f31081901b 100644 --- a/libavfilter/vsrc_sierpinski.c +++ b/libavfilter/vsrc_sierpinski.c @@ -24,13 +24,11 @@ */ #include "avfilter.h" -#include "formats.h" #include "video.h" #include "internal.h" #include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" -#include "libavutil/parseutils.h" #include "libavutil/lfg.h" #include "libavutil/random_seed.h" #include @@ -63,9 +61,9 @@ static const AVOption sierpinski_options[] = { {"r", "set frame rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, {"seed", "set the seed", OFFSET(seed), AV_OPT_TYPE_INT64, {.i64=-1}, -1, UINT32_MAX, FLAGS }, {"jump", "set the jump", OFFSET(jump), AV_OPT_TYPE_INT, {.i64=100}, 1, 10000, FLAGS }, - {"type","set fractal type",OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "type" }, - {"carpet", "sierpinski carpet", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, "type" }, - {"triangle", "sierpinski triangle", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, "type" }, + {"type","set fractal type",OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "type" }, + {"carpet", "sierpinski carpet", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, FLAGS, .unit = "type" }, + {"triangle", "sierpinski triangle", 0, AV_OPT_TYPE_CONST, {.i64=1}, 0, 0, FLAGS, .unit = "type" }, {NULL}, }; diff --git a/libavfilter/vsrc_testsrc.c b/libavfilter/vsrc_testsrc.c index 9760e5fc805..55c7e30ed69 100644 --- a/libavfilter/vsrc_testsrc.c +++ b/libavfilter/vsrc_testsrc.c @@ -88,6 +88,15 @@ typedef struct TestSourceContext { /* only used by haldclut */ int level; + + /* only used by zoneplate */ + int k0, kx, ky, kt; + int kxt, kyt, kxy; + int kx2, ky2, kt2; + int xo, yo, to, kU, kV; + int lut_precision; + uint8_t *lut; + int (*fill_slice_fn)(AVFilterContext *ctx, void *arg, int job, int nb_jobs); } TestSourceContext; #define OFFSET(x) offsetof(TestSourceContext, x) @@ -135,6 +144,7 @@ static av_cold void uninit(AVFilterContext *ctx) TestSourceContext *test = ctx->priv; av_frame_free(&test->picref); + av_freep(&test->lut); } static int config_props(AVFilterLink *outlink) @@ -150,6 +160,14 @@ static int config_props(AVFilterLink *outlink) return 0; } +static const AVFilterPad outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = config_props, + }, +}; + static int activate(AVFilterContext *ctx) { AVFilterLink *outlink = ctx->outputs[0]; @@ -184,8 +202,13 @@ static int activate(AVFilterContext *ctx) return AVERROR(ENOMEM); frame->pts = test->pts; frame->duration = 1; - frame->key_frame = 1; + frame->flags |= AV_FRAME_FLAG_KEY; +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS frame->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + frame->flags &= ~AV_FRAME_FLAG_INTERLACED; frame->pict_type = AV_PICTURE_TYPE_I; frame->sample_aspect_ratio = test->sar; if (!test->draw_once) @@ -235,7 +258,8 @@ static int color_config_props(AVFilterLink *inlink) TestSourceContext *test = ctx->priv; int ret; - ff_draw_init(&test->draw, inlink->format, 0); + ff_draw_init2(&test->draw, inlink->format, inlink->colorspace, + inlink->color_range, 0); ff_draw_color(&test->draw, &test->color, test->color_rgba); test->w = ff_draw_round_to_sub(&test->draw, 0, -1, test->w); @@ -307,8 +331,8 @@ static void haldclutsrc_fill_picture(AVFilterContext *ctx, AVFrame *frame) float scale; const int w = frame->width; const int h = frame->height; - const uint8_t *data = frame->data[0]; - const int linesize = frame->linesize[0]; + uint8_t *data = frame->data[0]; + const ptrdiff_t linesize = frame->linesize[0]; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); const int depth = desc->comp[0].depth; const int planar = desc->flags & AV_PIX_FMT_FLAG_PLANAR; @@ -445,14 +469,6 @@ static av_cold int nullsrc_init(AVFilterContext *ctx) return init(ctx); } -static const AVFilterPad nullsrc_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_props, - }, -}; - const AVFilter ff_vsrc_nullsrc = { .name = "nullsrc", .description = NULL_IF_CONFIG_SMALL("Null video source, return unprocessed video frames."), @@ -462,7 +478,7 @@ const AVFilter ff_vsrc_nullsrc = { .activate = activate, .priv_size = sizeof(TestSourceContext), .inputs = NULL, - FILTER_OUTPUTS(nullsrc_outputs), + FILTER_OUTPUTS(outputs), }; #endif /* CONFIG_NULLSRC_FILTER */ @@ -490,7 +506,7 @@ AVFILTER_DEFINE_CLASS(testsrc); * @param w width of the rectangle to draw, expressed as a number of segment_width units * @param h height of the rectangle to draw, expressed as a number of segment_width units */ -static void draw_rectangle(unsigned val, uint8_t *dst, int dst_linesize, int segment_width, +static void draw_rectangle(unsigned val, uint8_t *dst, ptrdiff_t dst_linesize, int segment_width, int x, int y, int w, int h) { int i; @@ -505,7 +521,7 @@ static void draw_rectangle(unsigned val, uint8_t *dst, int dst_linesize, int seg } } -static void draw_digit(int digit, uint8_t *dst, int dst_linesize, +static void draw_digit(int digit, uint8_t *dst, ptrdiff_t dst_linesize, int segment_width) { #define TOP_HBAR 1 @@ -668,14 +684,6 @@ static av_cold int test_init(AVFilterContext *ctx) return init(ctx); } -static const AVFilterPad avfilter_vsrc_testsrc_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_props, - }, -}; - const AVFilter ff_vsrc_testsrc = { .name = "testsrc", .description = NULL_IF_CONFIG_SMALL("Generate test pattern."), @@ -685,7 +693,7 @@ const AVFilter ff_vsrc_testsrc = { .uninit = uninit, .activate = activate, .inputs = NULL, - FILTER_OUTPUTS(avfilter_vsrc_testsrc_outputs), + FILTER_OUTPUTS(outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_RGB24), }; @@ -932,7 +940,8 @@ static int test2_config_props(AVFilterLink *inlink) AVFilterContext *ctx = inlink->src; TestSourceContext *s = ctx->priv; - av_assert0(ff_draw_init(&s->draw, inlink->format, 0) >= 0); + av_assert0(ff_draw_init2(&s->draw, inlink->format, inlink->colorspace, + inlink->color_range, 0) >= 0); s->w = ff_draw_round_to_sub(&s->draw, 0, -1, s->w); s->h = ff_draw_round_to_sub(&s->draw, 1, -1, s->h); if (av_image_check_size(s->w, s->h, 0, ctx) < 0) @@ -984,7 +993,7 @@ static void rgbtest_put_pixel(uint8_t *dstp[4], int dst_linesizep[4], uint8_t rgba_map[4]) { uint8_t *dst = dstp[0]; - int dst_linesize = dst_linesizep[0]; + ptrdiff_t dst_linesize = dst_linesizep[0]; uint32_t v; uint8_t *p; uint16_t *p16; @@ -1011,7 +1020,7 @@ static void rgbtest_put_pixel(uint8_t *dstp[4], int dst_linesizep[4], AV_WL32(p, v); break; case AV_PIX_FMT_GBRP: - p = dstp[0] + x + y * dst_linesizep[0]; + p = dstp[0] + x + y * dst_linesize; p[0] = g; p = dstp[1] + x + y * dst_linesizep[1]; p[0] = b; @@ -1140,9 +1149,9 @@ static void yuvtest_fill_picture8(AVFilterContext *ctx, AVFrame *frame) uint8_t *ydst = frame->data[0]; uint8_t *udst = frame->data[1]; uint8_t *vdst = frame->data[2]; - int ylinesize = frame->linesize[0]; - int ulinesize = frame->linesize[1]; - int vlinesize = frame->linesize[2]; + ptrdiff_t ylinesize = frame->linesize[0]; + ptrdiff_t ulinesize = frame->linesize[1]; + ptrdiff_t vlinesize = frame->linesize[2]; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { @@ -1197,9 +1206,9 @@ static void yuvtest_fill_picture16(AVFilterContext *ctx, AVFrame *frame) uint16_t *ydst = (uint16_t *)frame->data[0]; uint16_t *udst = (uint16_t *)frame->data[1]; uint16_t *vdst = (uint16_t *)frame->data[2]; - int ylinesize = frame->linesize[0] / 2; - int ulinesize = frame->linesize[1] / 2; - int vlinesize = frame->linesize[2] / 2; + ptrdiff_t ylinesize = frame->linesize[0] / 2; + ptrdiff_t ulinesize = frame->linesize[1] / 2; + ptrdiff_t vlinesize = frame->linesize[2] / 2; for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { @@ -1374,7 +1383,7 @@ static void draw_bar(TestSourceContext *test, const uint8_t color[4], for (plane = 0; frame->data[plane]; plane++) { const int c = color[plane]; - const int linesize = frame->linesize[plane]; + const ptrdiff_t linesize = frame->linesize[plane]; int i, px, py, pw, ph; if (plane == 1 || plane == 2) { @@ -1404,13 +1413,23 @@ static const enum AVPixelFormat smptebars_pix_fmts[] = { AV_PIX_FMT_NONE, }; -static const AVFilterPad smptebars_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_props, - }, -}; +static int smptebars_query_formats(AVFilterContext *ctx) +{ + enum AVColorSpace csp; + int ret; + + if (!strcmp(ctx->name, "smptehdbars")) { + csp = AVCOL_SPC_BT709; + } else { + csp = AVCOL_SPC_BT470BG; + } + + if ((ret = ff_set_common_color_spaces(ctx, ff_make_formats_list_singleton(csp)))) + return ret; + if ((ret = ff_set_common_color_ranges(ctx, ff_make_formats_list_singleton(AVCOL_RANGE_MPEG)))) + return ret; + return ff_set_common_formats_from_list(ctx, smptebars_pix_fmts); +} AVFILTER_DEFINE_CLASS_EXT(palbars, "pal(75|100)bars", options); @@ -1422,9 +1441,6 @@ static void pal75bars_fill_picture(AVFilterContext *ctx, AVFrame *picref) int r_w, i, x = 0; const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(picref->format); - picref->color_range = AVCOL_RANGE_MPEG; - picref->colorspace = AVCOL_SPC_BT470BG; - r_w = FFALIGN((test->w + 7) / 8, 1 << pixdesc->log2_chroma_w); draw_bar(test, white, x, 0, r_w, test->h, picref); @@ -1454,8 +1470,8 @@ const AVFilter ff_vsrc_pal75bars = { .uninit = uninit, .activate = activate, .inputs = NULL, - FILTER_OUTPUTS(smptebars_outputs), - FILTER_PIXFMTS_ARRAY(smptebars_pix_fmts), + FILTER_OUTPUTS(outputs), + FILTER_QUERY_FUNC(smptebars_query_formats), }; #endif /* CONFIG_PAL75BARS_FILTER */ @@ -1468,9 +1484,6 @@ static void pal100bars_fill_picture(AVFilterContext *ctx, AVFrame *picref) int r_w, i, x = 0; const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(picref->format); - picref->color_range = AVCOL_RANGE_MPEG; - picref->colorspace = AVCOL_SPC_BT470BG; - r_w = FFALIGN((test->w + 7) / 8, 1 << pixdesc->log2_chroma_w); for (i = 0; i < 7; i++) { @@ -1498,8 +1511,8 @@ const AVFilter ff_vsrc_pal100bars = { .uninit = uninit, .activate = activate, .inputs = NULL, - FILTER_OUTPUTS(smptebars_outputs), - FILTER_PIXFMTS_ARRAY(smptebars_pix_fmts), + FILTER_OUTPUTS(outputs), + FILTER_QUERY_FUNC(smptebars_query_formats), }; #endif /* CONFIG_PAL100BARS_FILTER */ @@ -1514,8 +1527,6 @@ static void smptebars_fill_picture(AVFilterContext *ctx, AVFrame *picref) int r_w, r_h, w_h, p_w, p_h, i, tmp, x = 0; const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(picref->format); - picref->colorspace = AVCOL_SPC_BT470BG; - r_w = FFALIGN((test->w + 6) / 7, 1 << pixdesc->log2_chroma_w); r_h = FFALIGN(test->h * 2 / 3, 1 << pixdesc->log2_chroma_h); w_h = FFALIGN(test->h * 3 / 4 - r_h, 1 << pixdesc->log2_chroma_h); @@ -1565,8 +1576,8 @@ const AVFilter ff_vsrc_smptebars = { .uninit = uninit, .activate = activate, .inputs = NULL, - FILTER_OUTPUTS(smptebars_outputs), - FILTER_PIXFMTS_ARRAY(smptebars_pix_fmts), + FILTER_OUTPUTS(outputs), + FILTER_QUERY_FUNC(smptebars_query_formats), }; #endif /* CONFIG_SMPTEBARS_FILTER */ @@ -1579,8 +1590,6 @@ static void smptehdbars_fill_picture(AVFilterContext *ctx, AVFrame *picref) int d_w, r_w, r_h, l_w, i, tmp, x = 0, y = 0; const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(picref->format); - picref->colorspace = AVCOL_SPC_BT709; - d_w = FFALIGN(test->w / 8, 1 << pixdesc->log2_chroma_w); r_h = FFALIGN(test->h * 7 / 12, 1 << pixdesc->log2_chroma_h); draw_bar(test, gray40, x, 0, d_w, r_h, picref); @@ -1668,8 +1677,8 @@ const AVFilter ff_vsrc_smptehdbars = { .uninit = uninit, .activate = activate, .inputs = NULL, - FILTER_OUTPUTS(smptebars_outputs), - FILTER_PIXFMTS_ARRAY(smptebars_pix_fmts), + FILTER_OUTPUTS(outputs), + FILTER_QUERY_FUNC(smptebars_query_formats), }; #endif /* CONFIG_SMPTEHDBARS_FILTER */ @@ -1682,9 +1691,9 @@ AVFILTER_DEFINE_CLASS_EXT(allyuv_allrgb, "allyuv/allrgb", static void allyuv_fill_picture(AVFilterContext *ctx, AVFrame *frame) { - const int ys = frame->linesize[0]; - const int us = frame->linesize[1]; - const int vs = frame->linesize[2]; + const ptrdiff_t ys = frame->linesize[0]; + const ptrdiff_t us = frame->linesize[1]; + const ptrdiff_t vs = frame->linesize[2]; int x, y, j; for (y = 0; y < 4096; y++) { @@ -1715,14 +1724,6 @@ static av_cold int allyuv_init(AVFilterContext *ctx) return init(ctx); } -static const AVFilterPad avfilter_vsrc_allyuv_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_props, - }, -}; - const AVFilter ff_vsrc_allyuv = { .name = "allyuv", .description = NULL_IF_CONFIG_SMALL("Generate all yuv colors."), @@ -1732,7 +1733,7 @@ const AVFilter ff_vsrc_allyuv = { .uninit = uninit, .activate = activate, .inputs = NULL, - FILTER_OUTPUTS(avfilter_vsrc_allyuv_outputs), + FILTER_OUTPUTS(outputs), FILTER_PIXFMTS(AV_PIX_FMT_YUV444P, AV_PIX_FMT_GBRP), }; @@ -1743,7 +1744,7 @@ const AVFilter ff_vsrc_allyuv = { static void allrgb_fill_picture(AVFilterContext *ctx, AVFrame *frame) { unsigned x, y; - const int linesize = frame->linesize[0]; + const ptrdiff_t linesize = frame->linesize[0]; uint8_t *line = frame->data[0]; for (y = 0; y < 4096; y++) { @@ -1803,10 +1804,10 @@ const AVFilter ff_vsrc_allrgb = { static const AVOption colorspectrum_options[] = { COMMON_OPTIONS - { "type", "set the color spectrum type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, "type" }, - { "black","fade to black", 0, AV_OPT_TYPE_CONST,{.i64=0},0, 0, FLAGS, "type" }, - { "white","fade to white", 0, AV_OPT_TYPE_CONST,{.i64=1},0, 0, FLAGS, "type" }, - { "all", "white to black", 0, AV_OPT_TYPE_CONST,{.i64=2},0, 0, FLAGS, "type" }, + { "type", "set the color spectrum type", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 2, FLAGS, .unit = "type" }, + { "black","fade to black", 0, AV_OPT_TYPE_CONST,{.i64=0},0, 0, FLAGS, .unit = "type" }, + { "white","fade to white", 0, AV_OPT_TYPE_CONST,{.i64=1},0, 0, FLAGS, .unit = "type" }, + { "all", "white to black", 0, AV_OPT_TYPE_CONST,{.i64=2},0, 0, FLAGS, .unit = "type" }, { NULL } }; @@ -1865,14 +1866,6 @@ static av_cold int colorspectrum_init(AVFilterContext *ctx) return init(ctx); } -static const AVFilterPad avfilter_vsrc_colorspectrum_outputs[] = { - { - .name = "default", - .type = AVMEDIA_TYPE_VIDEO, - .config_props = config_props, - }, -}; - const AVFilter ff_vsrc_colorspectrum = { .name = "colorspectrum", .description = NULL_IF_CONFIG_SMALL("Generate colors spectrum."), @@ -1882,7 +1875,7 @@ const AVFilter ff_vsrc_colorspectrum = { .uninit = uninit, .activate = activate, .inputs = NULL, - FILTER_OUTPUTS(avfilter_vsrc_colorspectrum_outputs), + FILTER_OUTPUTS(outputs), FILTER_SINGLE_PIXFMT(AV_PIX_FMT_GBRPF32), }; @@ -1893,9 +1886,9 @@ const AVFilter ff_vsrc_colorspectrum = { static const AVOption colorchart_options[] = { COMMON_OPTIONS_NOSIZE { "patch_size", "set the single patch size", OFFSET(pw), AV_OPT_TYPE_IMAGE_SIZE, {.str="64x64"}, 0, 0, FLAGS }, - { "preset", "set the color checker chart preset", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, "preset" }, - { "reference", "reference", 0, AV_OPT_TYPE_CONST,{.i64=0}, 0, 0, FLAGS, "preset" }, - { "skintones", "skintones", 0, AV_OPT_TYPE_CONST,{.i64=1}, 0, 0, FLAGS, "preset" }, + { "preset", "set the color checker chart preset", OFFSET(type), AV_OPT_TYPE_INT, {.i64=0}, 0, 1, FLAGS, .unit = "preset" }, + { "reference", "reference", 0, AV_OPT_TYPE_CONST,{.i64=0}, 0, 0, FLAGS, .unit = "preset" }, + { "skintones", "skintones", 0, AV_OPT_TYPE_CONST,{.i64=1}, 0, 0, FLAGS, .unit = "preset" }, { NULL } }; @@ -1976,7 +1969,8 @@ static int colorchart_config_props(AVFilterLink *inlink) AVFilterContext *ctx = inlink->src; TestSourceContext *s = ctx->priv; - av_assert0(ff_draw_init(&s->draw, inlink->format, 0) >= 0); + av_assert0(ff_draw_init2(&s->draw, inlink->format, inlink->colorspace, + inlink->color_range, 0) >= 0); if (av_image_check_size(s->w, s->h, 0, ctx) < 0) return AVERROR(EINVAL); return config_props(inlink); @@ -1989,7 +1983,7 @@ static void colorchart_fill_picture(AVFilterContext *ctx, AVFrame *frame) const int w = colorchart_presets[preset].w; const int h = colorchart_presets[preset].h; const int pw = test->pw; - const int ph = test->pw; + const int ph = test->ph; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { @@ -2039,3 +2033,198 @@ const AVFilter ff_vsrc_colorchart = { }; #endif /* CONFIG_COLORCHART_FILTER */ + +#if CONFIG_ZONEPLATE_FILTER + +static const AVOption zoneplate_options[] = { + COMMON_OPTIONS + { "precision", "set LUT precision", OFFSET(lut_precision), AV_OPT_TYPE_INT, {.i64=10}, 4, 16, FLAGS }, + { "xo", "set X-axis offset", OFFSET(xo), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "yo", "set Y-axis offset", OFFSET(yo), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "to", "set T-axis offset", OFFSET(to), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "k0", "set 0-order phase", OFFSET(k0), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kx", "set 1-order X-axis phase", OFFSET(kx), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "ky", "set 1-order Y-axis phase", OFFSET(ky), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kt", "set 1-order T-axis phase", OFFSET(kt), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kxt", "set X-axis*T-axis product phase", OFFSET(kxt), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kyt", "set Y-axis*T-axis product phase", OFFSET(kyt), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kxy", "set X-axis*Y-axis product phase", OFFSET(kxy), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kx2", "set 2-order X-axis phase", OFFSET(kx2), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "ky2", "set 2-order Y-axis phase", OFFSET(ky2), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kt2", "set 2-order T-axis phase", OFFSET(kt2), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "ku", "set 0-order U-color phase", OFFSET(kU), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { "kv", "set 0-order V-color phase", OFFSET(kV), AV_OPT_TYPE_INT, {.i64=0}, INT_MIN, INT_MAX, FLAGSR }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(zoneplate); + +#define ZONEPLATE_SLICE(name, type) \ +static int zoneplate_fill_slice_##name(AVFilterContext *ctx, \ + void *arg, int job, \ + int nb_jobs) \ +{ \ + TestSourceContext *test = ctx->priv; \ + AVFrame *frame = arg; \ + const int w = frame->width; \ + const int h = frame->height; \ + const int kxt = test->kxt, kyt = test->kyt, kx2 = test->kx2; \ + const int t = test->pts + test->to, k0 = test->k0; \ + const int kt = test->kt, kt2 = test->kt2, ky2 = test->ky2; \ + const int ky = test->ky, kx = test->kx, kxy = test->kxy; \ + const int lut_mask = (1 << test->lut_precision) - 1; \ + const int nkt2t = kt2 * t * t, nktt = kt * t; \ + const int start = (h * job ) / nb_jobs; \ + const int end = (h * (job+1)) / nb_jobs; \ + const ptrdiff_t ylinesize = frame->linesize[0] / sizeof(type); \ + const ptrdiff_t ulinesize = frame->linesize[1] / sizeof(type); \ + const ptrdiff_t vlinesize = frame->linesize[2] / sizeof(type); \ + const int xreset = -(w / 2) - test->xo; \ + const int yreset = -(h / 2) - test->yo + start; \ + const int kU = test->kU, kV = test->kV; \ + const int skxy = 0xffff / (w / 2); \ + const int skx2 = 0xffff / w; \ + const int dkxt = kxt * t; \ + type *ydst = ((type *)frame->data[0]) + start * ylinesize; \ + type *udst = ((type *)frame->data[1]) + start * ulinesize; \ + type *vdst = ((type *)frame->data[2]) + start * vlinesize; \ + const type *lut = (const type *)test->lut; \ + int akx, akxt, aky, akyt; \ + \ + aky = start * ky; \ + akyt = start * kyt * t; \ + \ + for (int j = start, y = yreset; j < end; j++, y++) { \ + const int dkxy = kxy * y * skxy; \ + const int nky2kt2 = (ky2 * y * y) / h + (nkt2t >> 1); \ + int akxy = dkxy * xreset; \ + \ + akx = 0; \ + akxt = 0; \ + aky += ky; \ + akyt += kyt * t; \ + \ + for (int i = 0, x = xreset; i < w; i++, x++) { \ + int phase = k0, uphase = kU, vphase = kV; \ + \ + akx += kx; \ + phase += akx + aky + nktt; \ + \ + akxt += dkxt; \ + akxy += dkxy; \ + phase += akxt + akyt; \ + phase += akxy >> 16; \ + phase += ((kx2 * x * x * skx2) >> 16) + nky2kt2; \ + uphase += phase; \ + vphase += phase; \ + \ + ydst[i] = lut[phase & lut_mask]; \ + udst[i] = lut[uphase & lut_mask]; \ + vdst[i] = lut[vphase & lut_mask]; \ + } \ + \ + ydst += ylinesize; \ + udst += ulinesize; \ + vdst += vlinesize; \ + } \ + \ + return 0; \ +} + +ZONEPLATE_SLICE( 8, uint8_t) +ZONEPLATE_SLICE( 9, uint16_t) +ZONEPLATE_SLICE(10, uint16_t) +ZONEPLATE_SLICE(12, uint16_t) +ZONEPLATE_SLICE(14, uint16_t) +ZONEPLATE_SLICE(16, uint16_t) + +static void zoneplate_fill_picture(AVFilterContext *ctx, AVFrame *frame) +{ + TestSourceContext *test = ctx->priv; + ff_filter_execute(ctx, test->fill_slice_fn, frame, NULL, + FFMIN(frame->height, ff_filter_get_nb_threads(ctx))); +} + +static int zoneplate_config_props(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + TestSourceContext *test = ctx->priv; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format); + const int lut_size = 1 << test->lut_precision; + const int depth = desc->comp[0].depth; + uint16_t *lut16; + uint8_t *lut8; + + if (av_image_check_size(test->w, test->h, 0, ctx) < 0) + return AVERROR(EINVAL); + + test->lut = av_calloc(lut_size, sizeof(*test->lut) * ((depth + 7) / 8)); + if (!test->lut) + return AVERROR(ENOMEM); + + lut8 = test->lut; + lut16 = (uint16_t *)test->lut; + switch (depth) { + case 8: + for (int i = 0; i < lut_size; i++) + lut8[i] = lrintf(255.f * (0.5f + 0.5f * sinf((2.f * M_PI * i) / lut_size))); + break; + default: + for (int i = 0; i < lut_size; i++) + lut16[i] = lrintf(((1 << depth) - 1) * (0.5f + 0.5f * sinf((2.f * M_PI * i) / lut_size))); + break; + } + + test->draw_once = 0; + test->fill_picture_fn = zoneplate_fill_picture; + + switch (depth) { + case 8: test->fill_slice_fn = zoneplate_fill_slice_8; break; + case 9: test->fill_slice_fn = zoneplate_fill_slice_9; break; + case 10: test->fill_slice_fn = zoneplate_fill_slice_10; break; + case 12: test->fill_slice_fn = zoneplate_fill_slice_12; break; + case 14: test->fill_slice_fn = zoneplate_fill_slice_14; break; + case 16: test->fill_slice_fn = zoneplate_fill_slice_16; break; + } + return config_props(outlink); +} + +static const enum AVPixelFormat zoneplate_pix_fmts[] = { + AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV444P9, + AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV444P12, + AV_PIX_FMT_YUV444P14, AV_PIX_FMT_YUV444P16, + AV_PIX_FMT_NONE, +}; + +static int zoneplate_query_formats(AVFilterContext *ctx) +{ + int ret; + if ((ret = ff_set_common_color_ranges(ctx, ff_make_formats_list_singleton(AVCOL_RANGE_JPEG)))) + return ret; + return ff_set_common_formats_from_list(ctx, zoneplate_pix_fmts); +} + +static const AVFilterPad avfilter_vsrc_zoneplate_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = zoneplate_config_props, + }, +}; + +const AVFilter ff_vsrc_zoneplate = { + .name = "zoneplate", + .description = NULL_IF_CONFIG_SMALL("Generate zone-plate."), + .priv_size = sizeof(TestSourceContext), + .priv_class = &zoneplate_class, + .init = init, + .uninit = uninit, + .activate = activate, + .inputs = NULL, + FILTER_OUTPUTS(avfilter_vsrc_zoneplate_outputs), + FILTER_QUERY_FUNC(zoneplate_query_formats), + .flags = AVFILTER_FLAG_SLICE_THREADS, + .process_command = ff_filter_process_command, +}; + +#endif /* CONFIG_ZONEPLATE_FILTER */ diff --git a/libavfilter/vsrc_testsrc_vulkan.c b/libavfilter/vsrc_testsrc_vulkan.c new file mode 100644 index 00000000000..480b23ac9ff --- /dev/null +++ b/libavfilter/vsrc_testsrc_vulkan.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) Lynne + * + * 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/random_seed.h" +#include "libavutil/csp.h" +#include "libavutil/opt.h" +#include "vulkan_filter.h" +#include "vulkan_spirv.h" +#include "internal.h" +#include "filters.h" +#include "colorspace.h" +#include "video.h" + +enum TestSrcVulkanMode { + TESTSRC_COLOR, +}; + +typedef struct TestSrcVulkanPushData { + float color_comp[4]; +} TestSrcVulkanPushData; + +typedef struct TestSrcVulkanContext { + FFVulkanContext vkctx; + + int initialized; + FFVulkanPipeline pl; + FFVkExecPool e; + FFVkQueueFamilyCtx qf; + FFVkSPIRVShader shd; + + /* Only used by color_vulkan */ + uint8_t color_rgba[4]; + + TestSrcVulkanPushData opts; + + int w, h; + int pw, ph; + char *out_format_string; + enum AVColorRange out_range; + unsigned int nb_frame; + AVRational time_base, frame_rate; + int64_t pts; + int64_t duration; ///< duration expressed in microseconds + AVRational sar; ///< sample aspect ratio + int draw_once; ///< draw only the first frame, always put out the same picture + int draw_once_reset; ///< draw only the first frame or in case of reset + AVFrame *picref; ///< cached reference containing the painted picture +} TestSrcVulkanContext; + +static av_cold int init_filter(AVFilterContext *ctx, enum TestSrcVulkanMode mode) +{ + int err; + uint8_t *spv_data; + size_t spv_len; + void *spv_opaque = NULL; + TestSrcVulkanContext *s = ctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + FFVkSPIRVShader *shd = &s->shd; + FFVkSPIRVCompiler *spv; + FFVulkanDescriptorSetBinding *desc_set; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(s->vkctx.output_format); + + spv = ff_vk_spirv_init(); + if (!spv) { + av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); + return AVERROR_EXTERNAL; + } + + ff_vk_qf_init(vkctx, &s->qf, VK_QUEUE_COMPUTE_BIT); + RET(ff_vk_exec_pool_init(vkctx, &s->qf, &s->e, s->qf.nb_queues*4, 0, 0, 0, NULL)); + RET(ff_vk_shader_init(&s->pl, &s->shd, "testsrc_compute", + VK_SHADER_STAGE_COMPUTE_BIT, 0)); + + ff_vk_shader_set_compute_sizes(&s->shd, 32, 32, 1); + + GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); + GLSLC(1, vec4 color_comp; ); + GLSLC(0, }; ); + GLSLC(0, ); + + ff_vk_add_push_constant(&s->pl, 0, sizeof(s->opts), + VK_SHADER_STAGE_COMPUTE_BIT); + + desc_set = (FFVulkanDescriptorSetBinding []) { + { + .name = "output_img", + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format), + .mem_quali = "writeonly", + .dimensions = 2, + .elems = planes, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + }, + }; + + RET(ff_vk_pipeline_descriptor_set_add(vkctx, &s->pl, shd, desc_set, 1, 0, 0)); + + GLSLC(0, void main() ); + GLSLC(0, { ); + GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); + if (mode == TESTSRC_COLOR) { + double rgb2yuv[3][3]; + double rgbad[4]; + double yuvad[4]; + + enum AVColorSpace csp; + const AVLumaCoefficients *luma = NULL; + + s->draw_once = 1; + + if (desc->flags & AV_PIX_FMT_FLAG_RGB) + csp = AVCOL_SPC_RGB; + else + csp = AVCOL_SPC_SMPTE170M; + + if (!(desc->flags & AV_PIX_FMT_FLAG_RGB) && !(luma = av_csp_luma_coeffs_from_avcsp(csp))) + return AVERROR(EINVAL); + else if (!(desc->flags & AV_PIX_FMT_FLAG_RGB)) + ff_fill_rgb2yuv_table(luma, rgb2yuv); + + for (int i = 0; i < 4; i++) + rgbad[i] = s->color_rgba[i] / 255.0; + + if (!(desc->flags & AV_PIX_FMT_FLAG_RGB)) + ff_matrix_mul_3x3_vec(yuvad, rgbad, rgb2yuv); + else + memcpy(yuvad, rgbad, sizeof(rgbad)); + + yuvad[3] = rgbad[3]; + + if (!(desc->flags & AV_PIX_FMT_FLAG_RGB)) { + for (int i = 0; i < 3; i++) { + int chroma = (!(desc->flags & AV_PIX_FMT_FLAG_RGB) && i > 0); + if (s->out_range == AVCOL_RANGE_MPEG) { + yuvad[i] *= (chroma ? 224.0 : 219.0) / 255.0; + yuvad[i] += (chroma ? 128.0 : 16.0) / 255.0; + } else if (chroma) { + yuvad[i] += 0.5; + } + } + } + + /* Ensure we place the alpha appropriately for gray formats */ + if (desc->nb_components <= 2) + yuvad[1] = yuvad[3]; + + for (int i = 0; i < 4; i++) + s->opts.color_comp[i] = yuvad[i]; + + GLSLC(1, vec4 r; ); + GLSLC(0, ); + for (int i = 0, c_off = 0; i < planes; i++) { + for (int c = 0; c < desc->nb_components; c++) { + if (desc->comp[c].plane == i) { + int off = desc->comp[c].offset / (FFALIGN(desc->comp[c].depth, 8)/8); + GLSLF(1, r[%i] = color_comp[%i]; ,off, c_off++); + } + } + GLSLF(1, imageStore(output_img[%i], pos, r); ,i); + GLSLC(0, ); + } + } + GLSLC(0, } ); + + RET(spv->compile_shader(spv, ctx, shd, &spv_data, &spv_len, "main", + &spv_opaque)); + RET(ff_vk_shader_create(vkctx, shd, spv_data, spv_len, "main")); + + RET(ff_vk_init_compute_pipeline(vkctx, &s->pl, shd)); + RET(ff_vk_exec_pipeline_register(vkctx, &s->e, &s->pl)); + + s->initialized = 1; + +fail: + if (spv_opaque) + spv->free_shader(spv, &spv_opaque); + if (spv) + spv->uninit(&spv); + + return err; +} + +static int testsrc_vulkan_activate(AVFilterContext *ctx) +{ + int err; + AVFilterLink *outlink = ctx->outputs[0]; + TestSrcVulkanContext *s = ctx->priv; + AVFrame *frame; + + if (!s->initialized) { + enum TestSrcVulkanMode mode = TESTSRC_COLOR; + err = init_filter(ctx, mode); + if (err < 0) + return err; + } + + if (!ff_outlink_frame_wanted(outlink)) + return FFERROR_NOT_READY; + if (s->duration >= 0 && + av_rescale_q(s->pts, s->time_base, AV_TIME_BASE_Q) >= s->duration) { + ff_outlink_set_status(outlink, AVERROR_EOF, s->pts); + return 0; + } + + if (s->draw_once) { + if (s->draw_once_reset) { + av_frame_free(&s->picref); + s->draw_once_reset = 0; + } + if (!s->picref) { + s->picref = ff_get_video_buffer(outlink, s->w, s->h); + if (!s->picref) + return AVERROR(ENOMEM); + + err = ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, s->picref, NULL, + VK_NULL_HANDLE, &s->opts, sizeof(s->opts)); + if (err < 0) + return err; + } + frame = av_frame_clone(s->picref); + } else { + frame = ff_get_video_buffer(outlink, s->w, s->h); + } + + if (!frame) + return AVERROR(ENOMEM); + + frame->pts = s->pts; + frame->duration = 1; + frame->flags = AV_FRAME_FLAG_KEY; + frame->pict_type = AV_PICTURE_TYPE_I; + frame->sample_aspect_ratio = s->sar; + if (!s->draw_once) { + err = ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->pl, frame, NULL, + VK_NULL_HANDLE, &s->opts, sizeof(s->opts)); + if (err < 0) { + av_frame_free(&frame); + return err; + } + } + + s->pts++; + s->nb_frame++; + + return ff_filter_frame(outlink, frame); +} + +static int testsrc_vulkan_config_props(AVFilterLink *outlink) +{ + int err; + TestSrcVulkanContext *s = outlink->src->priv; + FFVulkanContext *vkctx = &s->vkctx; + + if (!s->out_format_string) { + vkctx->output_format = AV_PIX_FMT_YUV444P; + } else { + vkctx->output_format = av_get_pix_fmt(s->out_format_string); + if (vkctx->output_format == AV_PIX_FMT_NONE) { + av_log(vkctx, AV_LOG_ERROR, "Invalid output format.\n"); + return AVERROR(EINVAL); + } + } + + err = ff_vk_filter_init_context(outlink->src, vkctx, NULL, + s->w, s->h, vkctx->output_format); + if (err < 0) + return err; + + outlink->hw_frames_ctx = av_buffer_ref(vkctx->frames_ref); + if (!outlink->hw_frames_ctx) + return AVERROR(ENOMEM); + + s->time_base = av_inv_q(s->frame_rate); + s->nb_frame = 0; + s->pts = 0; + + s->vkctx.output_width = s->w; + s->vkctx.output_height = s->h; + outlink->w = s->w; + outlink->h = s->h; + outlink->sample_aspect_ratio = s->sar; + outlink->frame_rate = s->frame_rate; + outlink->time_base = s->time_base; + + return 0; +} + +static void testsrc_vulkan_uninit(AVFilterContext *avctx) +{ + TestSrcVulkanContext *s = avctx->priv; + FFVulkanContext *vkctx = &s->vkctx; + + av_frame_free(&s->picref); + + ff_vk_exec_pool_free(vkctx, &s->e); + ff_vk_pipeline_free(vkctx, &s->pl); + ff_vk_shader_free(vkctx, &s->shd); + + ff_vk_uninit(&s->vkctx); + + s->initialized = 0; +} + +#define OFFSET(x) offsetof(TestSrcVulkanContext, x) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) + +#define COMMON_OPTS \ + { "size", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, { .str = "1920x1080" }, 0, 0, FLAGS }, \ + { "s", "set video size", OFFSET(w), AV_OPT_TYPE_IMAGE_SIZE, { .str = "1920x1080" }, 0, 0, FLAGS }, \ + \ + { "rate", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "60" }, 0, INT_MAX, FLAGS }, \ + { "r", "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, { .str = "60" }, 0, INT_MAX, FLAGS }, \ + \ + { "duration", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, \ + { "d", "set video duration", OFFSET(duration), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT64_MAX, FLAGS }, \ + \ + { "sar", "set video sample aspect ratio", OFFSET(sar), AV_OPT_TYPE_RATIONAL, { .dbl = 1 }, 0, INT_MAX, FLAGS }, \ + \ + { "format", "Output video format (software format of hardware frames)", OFFSET(out_format_string), AV_OPT_TYPE_STRING, .flags = FLAGS }, + +static const AVOption color_vulkan_options[] = { + { "color", "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS }, + { "c", "set color", OFFSET(color_rgba), AV_OPT_TYPE_COLOR, {.str = "black"}, 0, 0, FLAGS }, + COMMON_OPTS + { "out_range", "Output colour range (from 0 to 2) (default 0)", OFFSET(out_range), AV_OPT_TYPE_INT, {.i64 = AVCOL_RANGE_UNSPECIFIED}, AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_JPEG, .flags = FLAGS, .unit = "range" }, + { "full", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" }, + { "limited", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" }, + { "jpeg", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" }, + { "mpeg", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" }, + { "tv", "Limited range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, .unit = "range" }, + { "pc", "Full range", 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, .unit = "range" }, + { NULL }, +}; + +AVFILTER_DEFINE_CLASS(color_vulkan); + +static const AVFilterPad testsrc_vulkan_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .config_props = testsrc_vulkan_config_props, + }, +}; + +const AVFilter ff_vsrc_color_vulkan = { + .name = "color_vulkan", + .description = NULL_IF_CONFIG_SMALL("Generate a constant color (Vulkan)"), + .priv_size = sizeof(TestSrcVulkanContext), + .init = &ff_vk_filter_init, + .uninit = &testsrc_vulkan_uninit, + .inputs = NULL, + .flags = AVFILTER_FLAG_HWDEVICE, + .activate = testsrc_vulkan_activate, + FILTER_OUTPUTS(testsrc_vulkan_outputs), + FILTER_SINGLE_PIXFMT(AV_PIX_FMT_VULKAN), + .priv_class = &color_vulkan_class, + .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, +}; diff --git a/libavfilter/vulkan_filter.c b/libavfilter/vulkan_filter.c index e22541bd23a..cef42eeb4de 100644 --- a/libavfilter/vulkan_filter.c +++ b/libavfilter/vulkan_filter.c @@ -1,4 +1,6 @@ /* + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -17,108 +19,194 @@ */ #include "vulkan_filter.h" +#include "libavutil/vulkan_loader.h" -static int vulkan_filter_set_device(AVFilterContext *avctx, - AVBufferRef *device) +int ff_vk_filter_init_context(AVFilterContext *avctx, FFVulkanContext *s, + AVBufferRef *frames_ref, + int width, int height, enum AVPixelFormat sw_format) { - FFVulkanContext *s = avctx->priv; + int err; + AVHWFramesContext *frames_ctx; + AVHWDeviceContext *device_ctx; + AVVulkanFramesContext *vk_frames; + AVVulkanDeviceContext *vk_dev; + AVBufferRef *device_ref = avctx->hw_device_ctx; + + /* Check if context is reusable as-is */ + if (frames_ref) { + int no_storage = 0; + FFVulkanFunctions *vk; + const VkFormat *sub = av_vkfmt_from_pixfmt(sw_format); + + frames_ctx = (AVHWFramesContext *)frames_ref->data; + device_ctx = (AVHWDeviceContext *)frames_ctx->device_ref->data; + vk_frames = frames_ctx->hwctx; + vk_dev = device_ctx->hwctx; + + /* Width and height mismatch */ + if (width != frames_ctx->width || + height != frames_ctx->height) + goto skip; + + /* Format mismatch */ + if (sw_format != frames_ctx->sw_format) + goto skip; + + /* Unusual tiling mismatch. Don't let linear through either. */ + if (vk_frames->tiling != VK_IMAGE_TILING_OPTIMAL) + goto skip; + + /* Usage mismatch */ + if ((vk_frames->usage & (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT)) != + (VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT)) + goto skip; + + s->extensions = ff_vk_extensions_to_mask(vk_dev->enabled_dev_extensions, + vk_dev->nb_enabled_dev_extensions); + err = ff_vk_load_functions(device_ctx, &s->vkfn, s->extensions, 1, 1); + if (err < 0) + return err; + vk = &s->vkfn; + + /* Check if the subformats can do storage */ + for (int i = 0; sub[i] != VK_FORMAT_UNDEFINED; i++) { + VkFormatProperties2 prop = { + .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, + }; + vk->GetPhysicalDeviceFormatProperties2(vk_dev->phys_dev, sub[i], + &prop); + + if (vk_frames->tiling == VK_IMAGE_TILING_LINEAR) { + no_storage |= !(prop.formatProperties.linearTilingFeatures & + VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT); + } else { + no_storage |= !(prop.formatProperties.optimalTilingFeatures & + VK_FORMAT_FEATURE_2_STORAGE_IMAGE_BIT); + } + } - av_buffer_unref(&s->device_ref); + /* Check if it's usable */ + if (no_storage) { +skip: + device_ref = frames_ctx->device_ref; + frames_ref = NULL; + } else { + frames_ref = av_buffer_ref(frames_ref); + if (!frames_ref) + return AVERROR(ENOMEM); + } + } - s->device_ref = av_buffer_ref(device); - if (!s->device_ref) - return AVERROR(ENOMEM); + if (!frames_ref) { + if (!device_ref) { + av_log(avctx, AV_LOG_ERROR, + "Vulkan filtering requires a device context!\n"); + return AVERROR(EINVAL); + } - s->device = (AVHWDeviceContext*)s->device_ref->data; - s->hwctx = s->device->hwctx; + frames_ref = av_hwframe_ctx_alloc(device_ref); - return 0; -} + frames_ctx = (AVHWFramesContext *)frames_ref->data; + frames_ctx->format = AV_PIX_FMT_VULKAN; + frames_ctx->sw_format = sw_format; + frames_ctx->width = width; + frames_ctx->height = height; -static int vulkan_filter_set_frames(AVFilterContext *avctx, - AVBufferRef *frames) -{ - FFVulkanContext *s = avctx->priv; + vk_frames = frames_ctx->hwctx; + vk_frames->tiling = VK_IMAGE_TILING_OPTIMAL; + vk_frames->usage = VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_STORAGE_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT; - av_buffer_unref(&s->frames_ref); + err = av_hwframe_ctx_init(frames_ref); + if (err < 0) { + av_buffer_unref(&frames_ref); + return err; + } - s->frames_ref = av_buffer_ref(frames); - if (!s->frames_ref) - return AVERROR(ENOMEM); + device_ctx = (AVHWDeviceContext *)frames_ctx->device_ref->data; + vk_dev = device_ctx->hwctx; + } - return 0; + s->extensions = ff_vk_extensions_to_mask(vk_dev->enabled_dev_extensions, + vk_dev->nb_enabled_dev_extensions); + + /** + * libplacebo does not use descriptor buffers. + */ + if (!(s->extensions & FF_VK_EXT_DESCRIPTOR_BUFFER) && + strcmp(avctx->filter->name, "libplacebo")) { + av_log(avctx, AV_LOG_ERROR, "Vulkan filtering requires that " + "the %s extension is supported!\n", + VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME); + av_buffer_unref(&frames_ref); + return AVERROR(EINVAL); + } + + err = ff_vk_load_functions(device_ctx, &s->vkfn, s->extensions, 1, 1); + if (err < 0) { + av_buffer_unref(&frames_ref); + return err; + } + + s->frames_ref = frames_ref; + s->frames = frames_ctx; + s->hwfc = vk_frames; + s->device = device_ctx; + s->hwctx = device_ctx->hwctx; + + err = ff_vk_load_props(s); + if (err < 0) + av_buffer_unref(&s->frames_ref); + + return err; } int ff_vk_filter_config_input(AVFilterLink *inlink) { - int err; - AVFilterContext *avctx = inlink->dst; - FFVulkanContext *s = avctx->priv; - FFVulkanFunctions *vk = &s->vkfn; AVHWFramesContext *input_frames; + AVFilterContext *avctx = inlink->dst; + FFVulkanContext *s = inlink->dst->priv; if (!inlink->hw_frames_ctx) { - av_log(avctx, AV_LOG_ERROR, "Vulkan filtering requires a " + av_log(inlink->dst, AV_LOG_ERROR, "Vulkan filtering requires a " "hardware frames context on the input.\n"); return AVERROR(EINVAL); } - /* Extract the device and default output format from the first input. */ - if (avctx->inputs[0] != inlink) - return 0; - input_frames = (AVHWFramesContext *)inlink->hw_frames_ctx->data; if (input_frames->format != AV_PIX_FMT_VULKAN) return AVERROR(EINVAL); - err = vulkan_filter_set_device(avctx, input_frames->device_ref); - if (err < 0) - return err; - err = vulkan_filter_set_frames(avctx, inlink->hw_frames_ctx); - if (err < 0) - return err; - - s->extensions = ff_vk_extensions_to_mask(s->hwctx->enabled_dev_extensions, - s->hwctx->nb_enabled_dev_extensions); - - err = ff_vk_load_functions(s->device, &s->vkfn, s->extensions, 1, 1); - if (err < 0) - return err; + /* Extract the device and default output format from the first input. */ + if (avctx->inputs[0] != inlink) + return 0; - vk->GetPhysicalDeviceProperties(s->hwctx->phys_dev, &s->props); - vk->GetPhysicalDeviceMemoryProperties(s->hwctx->phys_dev, &s->mprops); + /* Save the ref, without reffing it */ + s->input_frames_ref = inlink->hw_frames_ctx; - /* Default output parameters match input parameters. */ + /* Defaults */ s->input_format = input_frames->sw_format; - if (s->output_format == AV_PIX_FMT_NONE) - s->output_format = input_frames->sw_format; - if (!s->output_width) - s->output_width = inlink->w; - if (!s->output_height) - s->output_height = inlink->h; + s->output_format = input_frames->sw_format; + s->output_width = inlink->w; + s->output_height = inlink->h; return 0; } -int ff_vk_filter_config_output_inplace(AVFilterLink *outlink) +int ff_vk_filter_config_output(AVFilterLink *outlink) { int err; - AVFilterContext *avctx = outlink->src; - FFVulkanContext *s = avctx->priv; + FFVulkanContext *s = outlink->src->priv; av_buffer_unref(&outlink->hw_frames_ctx); - if (!s->device_ref) { - if (!avctx->hw_device_ctx) { - av_log(avctx, AV_LOG_ERROR, "Vulkan filtering requires a " - "Vulkan device.\n"); - return AVERROR(EINVAL); - } - - err = vulkan_filter_set_device(avctx, avctx->hw_device_ctx); - if (err < 0) - return err; - } + err = ff_vk_filter_init_context(outlink->src, s, s->input_frames_ref, + s->output_width, s->output_height, + s->output_format); + if (err < 0) + return err; outlink->hw_frames_ctx = av_buffer_ref(s->frames_ref); if (!outlink->hw_frames_ctx) @@ -127,65 +215,246 @@ int ff_vk_filter_config_output_inplace(AVFilterLink *outlink) outlink->w = s->output_width; outlink->h = s->output_height; - return 0; + return err; } -int ff_vk_filter_config_output(AVFilterLink *outlink) +int ff_vk_filter_init(AVFilterContext *avctx) { - int err; - AVFilterContext *avctx = outlink->src; FFVulkanContext *s = avctx->priv; - AVBufferRef *output_frames_ref; - AVHWFramesContext *output_frames; - av_buffer_unref(&outlink->hw_frames_ctx); - - if (!s->device_ref) { - if (!avctx->hw_device_ctx) { - av_log(avctx, AV_LOG_ERROR, "Vulkan filtering requires a " - "Vulkan device.\n"); - return AVERROR(EINVAL); - } + s->output_format = AV_PIX_FMT_NONE; - err = vulkan_filter_set_device(avctx, avctx->hw_device_ctx); - if (err < 0) - return err; - } + return 0; +} - output_frames_ref = av_hwframe_ctx_alloc(s->device_ref); - if (!output_frames_ref) { - err = AVERROR(ENOMEM); - goto fail; +int ff_vk_filter_process_simple(FFVulkanContext *vkctx, FFVkExecPool *e, + FFVulkanPipeline *pl, AVFrame *out_f, AVFrame *in_f, + VkSampler sampler, void *push_src, size_t push_size) +{ + int err = 0; + FFVulkanFunctions *vk = &vkctx->vkfn; + VkImageView in_views[AV_NUM_DATA_POINTERS]; + VkImageView out_views[AV_NUM_DATA_POINTERS]; + VkImageMemoryBarrier2 img_bar[37]; + int nb_img_bar = 0; + + /* Update descriptors and init the exec context */ + FFVkExecContext *exec = ff_vk_exec_get(e); + ff_vk_exec_start(vkctx, exec); + + ff_vk_exec_bind_pipeline(vkctx, exec, pl); + + if (push_src) + ff_vk_update_push_exec(vkctx, exec, pl, VK_SHADER_STAGE_COMPUTE_BIT, + 0, push_size, push_src); + + if (in_f) { + RET(ff_vk_exec_add_dep_frame(vkctx, exec, in_f, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_create_imageviews(vkctx, exec, in_views, in_f)); + ff_vk_update_descriptor_img_array(vkctx, pl, exec, in_f, in_views, 0, 0, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + sampler); + ff_vk_frame_barrier(vkctx, exec, in_f, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED); } - output_frames = (AVHWFramesContext*)output_frames_ref->data; - output_frames->format = AV_PIX_FMT_VULKAN; - output_frames->sw_format = s->output_format; - output_frames->width = s->output_width; - output_frames->height = s->output_height; + RET(ff_vk_exec_add_dep_frame(vkctx, exec, out_f, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_create_imageviews(vkctx, exec, out_views, out_f)); + ff_vk_update_descriptor_img_array(vkctx, pl, exec, out_f, out_views, 0, !!in_f, + VK_IMAGE_LAYOUT_GENERAL, + VK_NULL_HANDLE); + ff_vk_frame_barrier(vkctx, exec, out_f, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED); + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pImageMemoryBarriers = img_bar, + .imageMemoryBarrierCount = nb_img_bar, + }); + + vk->CmdDispatch(exec->buf, + FFALIGN(vkctx->output_width, pl->wg_size[0])/pl->wg_size[0], + FFALIGN(vkctx->output_height, pl->wg_size[1])/pl->wg_size[1], + pl->wg_size[2]); + + return ff_vk_exec_submit(vkctx, exec); +fail: + ff_vk_exec_discard_deps(vkctx, exec); + return err; +} - err = av_hwframe_ctx_init(output_frames_ref); - if (err < 0) { - av_log(avctx, AV_LOG_ERROR, "Failed to initialise output " - "frames: %d.\n", err); - goto fail; +int ff_vk_filter_process_2pass(FFVulkanContext *vkctx, FFVkExecPool *e, + FFVulkanPipeline *pls[2], + AVFrame *out, AVFrame *tmp, AVFrame *in, + VkSampler sampler, void *push_src, size_t push_size) +{ + int err = 0; + FFVulkanFunctions *vk = &vkctx->vkfn; + VkImageView in_views[AV_NUM_DATA_POINTERS]; + VkImageView tmp_views[AV_NUM_DATA_POINTERS]; + VkImageView out_views[AV_NUM_DATA_POINTERS]; + VkImageMemoryBarrier2 img_bar[37]; + int nb_img_bar = 0; + + /* Update descriptors and init the exec context */ + FFVkExecContext *exec = ff_vk_exec_get(e); + ff_vk_exec_start(vkctx, exec); + + RET(ff_vk_exec_add_dep_frame(vkctx, exec, in, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_exec_add_dep_frame(vkctx, exec, tmp, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_exec_add_dep_frame(vkctx, exec, out, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + + RET(ff_vk_create_imageviews(vkctx, exec, in_views, in)); + RET(ff_vk_create_imageviews(vkctx, exec, tmp_views, tmp)); + RET(ff_vk_create_imageviews(vkctx, exec, out_views, out)); + + ff_vk_frame_barrier(vkctx, exec, in, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED); + ff_vk_frame_barrier(vkctx, exec, tmp, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED); + ff_vk_frame_barrier(vkctx, exec, out, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED); + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pImageMemoryBarriers = img_bar, + .imageMemoryBarrierCount = nb_img_bar, + }); + + for (int i = 0; i < 2; i++) { + FFVulkanPipeline *pl = pls[i]; + AVFrame *src_f = !i ? in : tmp; + AVFrame *dst_f = !i ? tmp : out; + VkImageView *src_views = !i ? in_views : tmp_views; + VkImageView *dst_views = !i ? tmp_views : out_views; + + ff_vk_exec_bind_pipeline(vkctx, exec, pl); + + if (push_src) + ff_vk_update_push_exec(vkctx, exec, pl, VK_SHADER_STAGE_COMPUTE_BIT, + 0, push_size, push_src); + + ff_vk_update_descriptor_img_array(vkctx, pl, exec, src_f, src_views, 0, 0, + !i ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : + VK_IMAGE_LAYOUT_GENERAL, + sampler); + ff_vk_update_descriptor_img_array(vkctx, pl, exec, dst_f, dst_views, 0, 1, + VK_IMAGE_LAYOUT_GENERAL, + VK_NULL_HANDLE); + + vk->CmdDispatch(exec->buf, + FFALIGN(vkctx->output_width, pl->wg_size[0])/pl->wg_size[0], + FFALIGN(vkctx->output_height, pl->wg_size[1])/pl->wg_size[1], + pl->wg_size[2]); } - outlink->hw_frames_ctx = output_frames_ref; - outlink->w = s->output_width; - outlink->h = s->output_height; - - return 0; + return ff_vk_exec_submit(vkctx, exec); fail: - av_buffer_unref(&output_frames_ref); + ff_vk_exec_discard_deps(vkctx, exec); return err; } -int ff_vk_filter_init(AVFilterContext *avctx) +int ff_vk_filter_process_Nin(FFVulkanContext *vkctx, FFVkExecPool *e, + FFVulkanPipeline *pl, + AVFrame *out, AVFrame *in[], int nb_in, + VkSampler sampler, void *push_src, size_t push_size) { - FFVulkanContext *s = avctx->priv; - - s->output_format = AV_PIX_FMT_NONE; + int err = 0; + FFVulkanFunctions *vk = &vkctx->vkfn; + VkImageView in_views[16][AV_NUM_DATA_POINTERS]; + VkImageView out_views[AV_NUM_DATA_POINTERS]; + VkImageMemoryBarrier2 img_bar[128]; + int nb_img_bar = 0; + + /* Update descriptors and init the exec context */ + FFVkExecContext *exec = ff_vk_exec_get(e); + ff_vk_exec_start(vkctx, exec); + + /* Inputs */ + for (int i = 0; i < nb_in; i++) { + RET(ff_vk_exec_add_dep_frame(vkctx, exec, in[i], + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_create_imageviews(vkctx, exec, in_views[i], in[i])); + + ff_vk_frame_barrier(vkctx, exec, in[i], img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_QUEUE_FAMILY_IGNORED); + } - return 0; + /* Output */ + RET(ff_vk_exec_add_dep_frame(vkctx, exec, out, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT)); + RET(ff_vk_create_imageviews(vkctx, exec, out_views, out)); + ff_vk_frame_barrier(vkctx, exec, out, img_bar, &nb_img_bar, + VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + VK_ACCESS_SHADER_WRITE_BIT, + VK_IMAGE_LAYOUT_GENERAL, + VK_QUEUE_FAMILY_IGNORED); + + vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pImageMemoryBarriers = img_bar, + .imageMemoryBarrierCount = nb_img_bar, + }); + + ff_vk_exec_bind_pipeline(vkctx, exec, pl); + + if (push_src) + ff_vk_update_push_exec(vkctx, exec, pl, VK_SHADER_STAGE_COMPUTE_BIT, + 0, push_size, push_src); + + for (int i = 0; i < nb_in; i++) + ff_vk_update_descriptor_img_array(vkctx, pl, exec, in[i], in_views[i], 0, i, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + sampler); + + ff_vk_update_descriptor_img_array(vkctx, pl, exec, out, out_views, 0, nb_in, + VK_IMAGE_LAYOUT_GENERAL, + VK_NULL_HANDLE); + + vk->CmdDispatch(exec->buf, + FFALIGN(vkctx->output_width, pl->wg_size[0])/pl->wg_size[0], + FFALIGN(vkctx->output_height, pl->wg_size[1])/pl->wg_size[1], + pl->wg_size[2]); + + return ff_vk_exec_submit(vkctx, exec); +fail: + ff_vk_exec_discard_deps(vkctx, exec); + return err; } diff --git a/libavfilter/vulkan_filter.h b/libavfilter/vulkan_filter.h index bfdb9b2d7d8..d2c14601d94 100644 --- a/libavfilter/vulkan_filter.h +++ b/libavfilter/vulkan_filter.h @@ -1,4 +1,6 @@ /* + * Copyright (c) Lynne + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -26,9 +28,38 @@ /** * General lavfi IO functions */ -int ff_vk_filter_init (AVFilterContext *avctx); -int ff_vk_filter_config_input (AVFilterLink *inlink); -int ff_vk_filter_config_output (AVFilterLink *outlink); -int ff_vk_filter_config_output_inplace(AVFilterLink *outlink); +int ff_vk_filter_init (AVFilterContext *avctx); +int ff_vk_filter_config_input (AVFilterLink *inlink); +int ff_vk_filter_config_output(AVFilterLink *outlink); + +/** + * Can be called manually, if not using ff_vk_filter_config_output. + */ +int ff_vk_filter_init_context(AVFilterContext *avctx, FFVulkanContext *s, + AVBufferRef *frames_ref, + int width, int height, enum AVPixelFormat sw_format); + +/** + * Submit a compute shader with a zero/one input and single out for execution. + */ +int ff_vk_filter_process_simple(FFVulkanContext *vkctx, FFVkExecPool *e, + FFVulkanPipeline *pl, AVFrame *out_f, AVFrame *in_f, + VkSampler sampler, void *push_src, size_t push_size); + +/** + * Submit a compute shader with a single in and single out with 2 stages. + */ +int ff_vk_filter_process_2pass(FFVulkanContext *vkctx, FFVkExecPool *e, + FFVulkanPipeline *pls[2], + AVFrame *out, AVFrame *tmp, AVFrame *in, + VkSampler sampler, void *push_src, size_t push_size); + +/** + * Up to 16 inputs, one output + */ +int ff_vk_filter_process_Nin(FFVulkanContext *vkctx, FFVkExecPool *e, + FFVulkanPipeline *pl, + AVFrame *out, AVFrame *in[], int nb_in, + VkSampler sampler, void *push_src, size_t push_size); #endif /* AVFILTER_VULKAN_FILTER_H */ diff --git a/libavutil/vulkan_glslang.c b/libavfilter/vulkan_glslang.c similarity index 95% rename from libavutil/vulkan_glslang.c rename to libavfilter/vulkan_glslang.c index e7785f6d405..845a530ee0d 100644 --- a/libavutil/vulkan_glslang.c +++ b/libavfilter/vulkan_glslang.c @@ -21,8 +21,9 @@ #include #include -#include "mem.h" -#include "avassert.h" +#include "vulkan_spirv.h" +#include "libavutil/mem.h" +#include "libavutil/avassert.h" static pthread_mutex_t glslc_mutex = PTHREAD_MUTEX_INITIALIZER; static int glslc_refcount = 0; @@ -176,11 +177,13 @@ static int glslc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, av_assert0(glslc_refcount); + *opaque = NULL; + if (!(glslc_shader = glslang_shader_create(&glslc_input))) return AVERROR(ENOMEM); if (!glslang_shader_preprocess(glslc_shader, &glslc_input)) { - ff_vk_print_shader(avctx, shd, AV_LOG_WARNING); + ff_vk_shader_print(avctx, shd, AV_LOG_WARNING); av_log(avctx, AV_LOG_ERROR, "Unable to preprocess shader: %s (%s)!\n", glslang_shader_get_info_log(glslc_shader), glslang_shader_get_info_debug_log(glslc_shader)); @@ -189,7 +192,7 @@ static int glslc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, } if (!glslang_shader_parse(glslc_shader, &glslc_input)) { - ff_vk_print_shader(avctx, shd, AV_LOG_WARNING); + ff_vk_shader_print(avctx, shd, AV_LOG_WARNING); av_log(avctx, AV_LOG_ERROR, "Unable to parse shader: %s (%s)!\n", glslang_shader_get_info_log(glslc_shader), glslang_shader_get_info_debug_log(glslc_shader)); @@ -206,7 +209,7 @@ static int glslc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, if (!glslang_program_link(glslc_program, GLSLANG_MSG_SPV_RULES_BIT | GLSLANG_MSG_VULKAN_RULES_BIT)) { - ff_vk_print_shader(avctx, shd, AV_LOG_WARNING); + ff_vk_shader_print(avctx, shd, AV_LOG_WARNING); av_log(avctx, AV_LOG_ERROR, "Unable to link shader: %s (%s)!\n", glslang_program_get_info_log(glslc_program), glslang_program_get_info_debug_log(glslc_program)); @@ -219,10 +222,10 @@ static int glslc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, messages = glslang_program_SPIRV_get_messages(glslc_program); if (messages) { - ff_vk_print_shader(avctx, shd, AV_LOG_WARNING); + ff_vk_shader_print(avctx, shd, AV_LOG_WARNING); av_log(avctx, AV_LOG_WARNING, "%s\n", messages); } else { - ff_vk_print_shader(avctx, shd, AV_LOG_VERBOSE); + ff_vk_shader_print(avctx, shd, AV_LOG_VERBOSE); } glslang_shader_delete(glslc_shader); @@ -257,7 +260,7 @@ static void glslc_uninit(FFVkSPIRVCompiler **ctx) av_freep(ctx); } -static FFVkSPIRVCompiler *ff_vk_glslang_init(void) +FFVkSPIRVCompiler *ff_vk_glslang_init(void) { FFVkSPIRVCompiler *ret = av_mallocz(sizeof(*ret)); if (!ret) diff --git a/libavutil/vulkan_shaderc.c b/libavfilter/vulkan_shaderc.c similarity index 96% rename from libavutil/vulkan_shaderc.c rename to libavfilter/vulkan_shaderc.c index bd40edf1876..38be1030ad2 100644 --- a/libavutil/vulkan_shaderc.c +++ b/libavfilter/vulkan_shaderc.c @@ -18,7 +18,8 @@ #include -#include "mem.h" +#include "libavutil/mem.h" +#include "vulkan_spirv.h" static int shdc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, FFVkSPIRVShader *shd, uint8_t **data, @@ -43,6 +44,7 @@ static int shdc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, }; shaderc_compile_options_t opts = shaderc_compile_options_initialize(); + *opaque = NULL; if (!opts) return AVERROR(ENOMEM); @@ -65,7 +67,7 @@ static int shdc_shader_compile(FFVkSPIRVCompiler *ctx, void *avctx, loglevel = err ? AV_LOG_ERROR : warn ? AV_LOG_WARNING : AV_LOG_VERBOSE; - ff_vk_print_shader(avctx, shd, loglevel); + ff_vk_shader_print(avctx, shd, loglevel); if (message && (err || warn)) av_log(avctx, loglevel, "%s\n", message); status = ret < FF_ARRAY_ELEMS(shdc_result) ? shdc_result[ret] : "unknown"; @@ -104,7 +106,7 @@ static void shdc_uninit(FFVkSPIRVCompiler **ctx) av_freep(ctx); } -static FFVkSPIRVCompiler *ff_vk_shaderc_init(void) +FFVkSPIRVCompiler *ff_vk_shaderc_init(void) { FFVkSPIRVCompiler *ret = av_mallocz(sizeof(*ret)); if (!ret) diff --git a/libavfilter/vulkan_spirv.h b/libavfilter/vulkan_spirv.h new file mode 100644 index 00000000000..5638cd9696a --- /dev/null +++ b/libavfilter/vulkan_spirv.h @@ -0,0 +1,45 @@ +/* + * 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 + */ + +#ifndef AVFILTER_VULKAN_SPIRV_H +#define AVFILTER_VULKAN_SPIRV_H + +#include "libavutil/vulkan.h" + +#include "vulkan.h" +#include "config.h" + +typedef struct FFVkSPIRVCompiler { + void *priv; + int (*compile_shader)(struct FFVkSPIRVCompiler *ctx, void *avctx, + struct FFVkSPIRVShader *shd, uint8_t **data, + size_t *size, const char *entrypoint, void **opaque); + void (*free_shader)(struct FFVkSPIRVCompiler *ctx, void **opaque); + void (*uninit)(struct FFVkSPIRVCompiler **ctx); +} FFVkSPIRVCompiler; + +#if CONFIG_LIBGLSLANG +FFVkSPIRVCompiler *ff_vk_glslang_init(void); +#define ff_vk_spirv_init ff_vk_glslang_init +#endif +#if CONFIG_LIBSHADERC +FFVkSPIRVCompiler *ff_vk_shaderc_init(void); +#define ff_vk_spirv_init ff_vk_shaderc_init +#endif + +#endif /* AVFILTER_VULKAN_H */ diff --git a/libavfilter/window_func.h b/libavfilter/window_func.h index 02b5def9dd1..d4e39c3efed 100644 --- a/libavfilter/window_func.h +++ b/libavfilter/window_func.h @@ -35,43 +35,30 @@ enum WindowFunc { WFUNC_RECT, WFUNC_HANNING, WFUNC_HAMMING, WFUNC_BLACKMAN, NB_WFUNC }; #define WIN_FUNC_OPTION(win_func_opt_name, win_func_offset, flag, default_window_func) \ - { win_func_opt_name, "set window function", win_func_offset, AV_OPT_TYPE_INT, {.i64 = default_window_func}, 0, NB_WFUNC-1, flag, "win_func" }, \ - { "rect", "Rectangular", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_RECT}, 0, 0, flag, "win_func" }, \ - { "bartlett", "Bartlett", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BARTLETT}, 0, 0, flag, "win_func" }, \ - { "hann", "Hann", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING}, 0, 0, flag, "win_func" }, \ - { "hanning", "Hanning", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING}, 0, 0, flag, "win_func" }, \ - { "hamming", "Hamming", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HAMMING}, 0, 0, flag, "win_func" }, \ - { "blackman", "Blackman", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BLACKMAN}, 0, 0, flag, "win_func" }, \ - { "welch", "Welch", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_WELCH}, 0, 0, flag, "win_func" }, \ - { "flattop", "Flat-top", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_FLATTOP}, 0, 0, flag, "win_func" }, \ - { "bharris", "Blackman-Harris", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHARRIS}, 0, 0, flag, "win_func" }, \ - { "bnuttall", "Blackman-Nuttall", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BNUTTALL}, 0, 0, flag, "win_func" }, \ - { "bhann", "Bartlett-Hann", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHANN}, 0, 0, flag, "win_func" }, \ - { "sine", "Sine", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_SINE}, 0, 0, flag, "win_func" }, \ - { "nuttall", "Nuttall", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_NUTTALL}, 0, 0, flag, "win_func" }, \ - { "lanczos", "Lanczos", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_LANCZOS}, 0, 0, flag, "win_func" }, \ - { "gauss", "Gauss", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_GAUSS}, 0, 0, flag, "win_func" }, \ - { "tukey", "Tukey", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_TUKEY}, 0, 0, flag, "win_func" }, \ - { "dolph", "Dolph-Chebyshev", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_DOLPH}, 0, 0, flag, "win_func" }, \ - { "cauchy", "Cauchy", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_CAUCHY}, 0, 0, flag, "win_func" }, \ - { "parzen", "Parzen", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_PARZEN}, 0, 0, flag, "win_func" }, \ - { "poisson", "Poisson", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_POISSON}, 0, 0, flag, "win_func" }, \ - { "bohman", "Bohman", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BOHMAN}, 0, 0, flag, "win_func" }, \ - { "kaiser", "Kaiser", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_KAISER}, 0, 0, flag, "win_func" } + { win_func_opt_name, "set window function", win_func_offset, AV_OPT_TYPE_INT, {.i64 = default_window_func}, 0, NB_WFUNC-1, flag, .unit = "win_func" }, \ + { "rect", "Rectangular", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_RECT}, 0, 0, flag, .unit = "win_func" }, \ + { "bartlett", "Bartlett", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BARTLETT}, 0, 0, flag, .unit = "win_func" }, \ + { "hann", "Hann", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING}, 0, 0, flag, .unit = "win_func" }, \ + { "hanning", "Hanning", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HANNING}, 0, 0, flag, .unit = "win_func" }, \ + { "hamming", "Hamming", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_HAMMING}, 0, 0, flag, .unit = "win_func" }, \ + { "blackman", "Blackman", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BLACKMAN}, 0, 0, flag, .unit = "win_func" }, \ + { "welch", "Welch", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_WELCH}, 0, 0, flag, .unit = "win_func" }, \ + { "flattop", "Flat-top", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_FLATTOP}, 0, 0, flag, .unit = "win_func" }, \ + { "bharris", "Blackman-Harris", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHARRIS}, 0, 0, flag, .unit = "win_func" }, \ + { "bnuttall", "Blackman-Nuttall", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BNUTTALL}, 0, 0, flag, .unit = "win_func" }, \ + { "bhann", "Bartlett-Hann", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BHANN}, 0, 0, flag, .unit = "win_func" }, \ + { "sine", "Sine", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_SINE}, 0, 0, flag, .unit = "win_func" }, \ + { "nuttall", "Nuttall", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_NUTTALL}, 0, 0, flag, .unit = "win_func" }, \ + { "lanczos", "Lanczos", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_LANCZOS}, 0, 0, flag, .unit = "win_func" }, \ + { "gauss", "Gauss", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_GAUSS}, 0, 0, flag, .unit = "win_func" }, \ + { "tukey", "Tukey", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_TUKEY}, 0, 0, flag, .unit = "win_func" }, \ + { "dolph", "Dolph-Chebyshev", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_DOLPH}, 0, 0, flag, .unit = "win_func" }, \ + { "cauchy", "Cauchy", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_CAUCHY}, 0, 0, flag, .unit = "win_func" }, \ + { "parzen", "Parzen", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_PARZEN}, 0, 0, flag, .unit = "win_func" }, \ + { "poisson", "Poisson", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_POISSON}, 0, 0, flag, .unit = "win_func" }, \ + { "bohman", "Bohman", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_BOHMAN}, 0, 0, flag, .unit = "win_func" }, \ + { "kaiser", "Kaiser", 0, AV_OPT_TYPE_CONST, {.i64=WFUNC_KAISER}, 0, 0, flag, .unit = "win_func" } -static inline double get_i0(double x) -{ - double y = 1.0, prev = 1.0, i = 1.0; - - while (fabs(prev) > 1e-20) { - double summand = prev * x * x / (4 * i * i); - y += summand; - prev = summand; - i++; - } - - return y; -} static inline void generate_window_func(float *lut, int N, int win_func, float *overlap) @@ -232,13 +219,15 @@ static inline void generate_window_func(float *lut, int N, int win_func, *overlap = 0.75; break; case WFUNC_KAISER: + { + double scale = 1.0 / av_bessel_i0(12.); for (n = 0; n < N; n++) { double x = 2.0 / (double)(N - 1); - - lut[n] = get_i0(12. * sqrt(1. - SQR(n * x - 1.))) / get_i0(12.); + lut[n] = av_bessel_i0(12. * sqrt(1. - SQR(n * x - 1.))) * scale; } *overlap = 0.75; break; + } default: av_assert0(0); } diff --git a/libavfilter/x86/af_afir.asm b/libavfilter/x86/af_afir.asm index 2cc09709a27..ed0276c7b95 100644 --- a/libavfilter/x86/af_afir.asm +++ b/libavfilter/x86/af_afir.asm @@ -67,3 +67,30 @@ INIT_XMM sse3 FCMUL_ADD INIT_YMM avx FCMUL_ADD + +%if HAVE_FMA3_EXTERNAL +INIT_YMM fma3 +cglobal fcmul_add, 4,4,4, sum, t, c, len + shl lend, 3 + add tq, lenq + add cq, lenq + add sumq, lenq + neg lenq +.loop: + movaps m0, [tq + lenq] + movaps m1, [cq + lenq] + vpermilps m3, m0, 177 + vpermilps m2, m1, 160 + vpermilps m1, m1, 245 + mulps m1, m1, m3 + vfmaddsub132ps m0, m1, m2 + addps m0, m0, [sumq + lenq] + movaps [sumq + lenq], m0 + add lenq, mmsize + jl .loop + movss xm0, [tq + lenq] + mulss xm0, [cq + lenq] + addss xm0, [sumq + lenq] + movss [sumq + lenq], xm0 + RET +%endif diff --git a/libavfilter/x86/af_afir_init.c b/libavfilter/x86/af_afir_init.c index e53817b9c0d..d573acf10b2 100644 --- a/libavfilter/x86/af_afir_init.c +++ b/libavfilter/x86/af_afir_init.c @@ -26,6 +26,8 @@ void ff_fcmul_add_sse3(float *sum, const float *t, const float *c, ptrdiff_t len); void ff_fcmul_add_avx(float *sum, const float *t, const float *c, ptrdiff_t len); +void ff_fcmul_add_fma3(float *sum, const float *t, const float *c, + ptrdiff_t len); av_cold void ff_afir_init_x86(AudioFIRDSPContext *s) { @@ -37,4 +39,7 @@ av_cold void ff_afir_init_x86(AudioFIRDSPContext *s) if (EXTERNAL_AVX_FAST(cpu_flags)) { s->fcmul_add = ff_fcmul_add_avx; } + if (EXTERNAL_FMA3_FAST(cpu_flags)) { + s->fcmul_add = ff_fcmul_add_fma3; + } } diff --git a/libavfilter/x86/vf_bwdif.asm b/libavfilter/x86/vf_bwdif.asm index 0b453da53b3..c93b41ec488 100644 --- a/libavfilter/x86/vf_bwdif.asm +++ b/libavfilter/x86/vf_bwdif.asm @@ -26,18 +26,22 @@ %include "libavutil/x86/x86util.asm" -SECTION_RODATA +SECTION_RODATA 32 -pw_coefhf: times 4 dw 1016, 5570 -pw_coefhf1: times 8 dw -3801 -pw_coefsp: times 4 dw 5077, -981 -pw_splfdif: times 4 dw -768, 768 +pw_coefhf: times 8 dw 1016, 5570 +pw_coefhf1: times 16 dw -3801 +pw_coefsp: times 8 dw 5077, -981 +pw_splfdif: times 8 dw -768, 768 SECTION .text %macro LOAD8 2 + %if mmsize == 32 + pmovzxbw %1, %2 + %else movh %1, %2 punpcklbw %1, m7 + %endif %endmacro %macro LOAD12 2 @@ -45,8 +49,14 @@ SECTION .text %endmacro %macro DISP8 0 + %if mmsize == 32 + vextracti128 xm1, m2, 1 + packuswb xm2, xm1 + movu [dstq], xm2 + %else packuswb m2, m2 movh [dstq], m2 + %endif %endmacro %macro DISP12 0 @@ -244,8 +254,12 @@ cglobal bwdif_filter_line_12bit, 4, 9, 13, 0, dst, prev, cur, next, w, \ prefs, mrefs, prefs2, mrefs2, \ prefs3, mrefs3, prefs4, \ mrefs4, parity, clip_max + %if mmsize == 32 + vpbroadcastw m12, WORD clip_maxm + %else movd m12, DWORD clip_maxm SPLATW m12, m12, 0 + %endif %else cglobal bwdif_filter_line_12bit, 4, 6, 8, 80, dst, prev, cur, next, w, \ prefs, mrefs, prefs2, mrefs2, \ @@ -264,3 +278,8 @@ INIT_XMM ssse3 BWDIF INIT_XMM sse2 BWDIF + +%if HAVE_AVX2_EXTERNAL && ARCH_X86_64 +INIT_YMM avx2 +BWDIF +%endif diff --git a/libavfilter/x86/vf_bwdif_init.c b/libavfilter/x86/vf_bwdif_init.c index e24e5cd9b1c..69a70e3293e 100644 --- a/libavfilter/x86/vf_bwdif_init.c +++ b/libavfilter/x86/vf_bwdif_init.c @@ -22,41 +22,51 @@ #include "libavutil/cpu.h" #include "libavutil/x86/asm.h" #include "libavutil/x86/cpu.h" -#include "libavfilter/bwdif.h" +#include "libavfilter/bwdifdsp.h" -void ff_bwdif_filter_line_sse2(void *dst, void *prev, void *cur, void *next, +void ff_bwdif_filter_line_sse2(void *dst, const void *prev, const void *cur, const void *next, int w, int prefs, int mrefs, int prefs2, int mrefs2, int prefs3, int mrefs3, int prefs4, int mrefs4, int parity, int clip_max); -void ff_bwdif_filter_line_ssse3(void *dst, void *prev, void *cur, void *next, +void ff_bwdif_filter_line_ssse3(void *dst, const void *prev, const void *cur, const void *next, int w, int prefs, int mrefs, int prefs2, int mrefs2, int prefs3, int mrefs3, int prefs4, int mrefs4, int parity, int clip_max); +void ff_bwdif_filter_line_avx2(void *dst, const void *prev, const void *cur, const void *next, + int w, int prefs, int mrefs, int prefs2, + int mrefs2, int prefs3, int mrefs3, int prefs4, + int mrefs4, int parity, int clip_max); -void ff_bwdif_filter_line_12bit_sse2(void *dst, void *prev, void *cur, void *next, +void ff_bwdif_filter_line_12bit_sse2(void *dst, const void *prev, const void *cur, const void *next, int w, int prefs, int mrefs, int prefs2, int mrefs2, int prefs3, int mrefs3, int prefs4, int mrefs4, int parity, int clip_max); -void ff_bwdif_filter_line_12bit_ssse3(void *dst, void *prev, void *cur, void *next, +void ff_bwdif_filter_line_12bit_ssse3(void *dst, const void *prev, const void *cur, const void *next, int w, int prefs, int mrefs, int prefs2, int mrefs2, int prefs3, int mrefs3, int prefs4, int mrefs4, int parity, int clip_max); +void ff_bwdif_filter_line_12bit_avx2(void *dst, const void *prev, const void *cur, const void *next, + int w, int prefs, int mrefs, int prefs2, + int mrefs2, int prefs3, int mrefs3, int prefs4, + int mrefs4, int parity, int clip_max); -av_cold void ff_bwdif_init_x86(BWDIFContext *bwdif) +av_cold void ff_bwdif_init_x86(BWDIFDSPContext *bwdif, int bit_depth) { - YADIFContext *yadif = &bwdif->yadif; int cpu_flags = av_get_cpu_flags(); - int bit_depth = (!yadif->csp) ? 8 : yadif->csp->comp[0].depth; if (bit_depth <= 8) { if (EXTERNAL_SSE2(cpu_flags)) bwdif->filter_line = ff_bwdif_filter_line_sse2; if (EXTERNAL_SSSE3(cpu_flags)) bwdif->filter_line = ff_bwdif_filter_line_ssse3; + if (ARCH_X86_64 && EXTERNAL_AVX2_FAST(cpu_flags)) + bwdif->filter_line = ff_bwdif_filter_line_avx2; } else if (bit_depth <= 12) { if (EXTERNAL_SSE2(cpu_flags)) bwdif->filter_line = ff_bwdif_filter_line_12bit_sse2; if (EXTERNAL_SSSE3(cpu_flags)) bwdif->filter_line = ff_bwdif_filter_line_12bit_ssse3; + if (ARCH_X86_64 && EXTERNAL_AVX2_FAST(cpu_flags)) + bwdif->filter_line = ff_bwdif_filter_line_12bit_avx2; } } diff --git a/libavfilter/x86/vf_overlay.asm b/libavfilter/x86/vf_overlay.asm index 14ec60ca341..8eecbbd2b20 100644 --- a/libavfilter/x86/vf_overlay.asm +++ b/libavfilter/x86/vf_overlay.asm @@ -33,7 +33,7 @@ pw_257: times 8 dw 257 SECTION .text INIT_XMM sse4 -cglobal overlay_row_44, 5, 7, 6, 0, d, da, s, a, w, r, x +cglobal overlay_row_44, 5, 7, 6, 0, dst, da, s, a, w, r, x xor xq, xq movsxdifnidn wq, wd mov rq, wq @@ -47,7 +47,7 @@ cglobal overlay_row_44, 5, 7, 6, 0, d, da, s, a, w, r, x .loop: pmovzxbw m0, [sq+xq] pmovzxbw m2, [aq+xq] - pmovzxbw m1, [dq+xq] + pmovzxbw m1, [dstq+xq] pmullw m0, m2 pxor m2, m3 pmullw m1, m2 @@ -55,7 +55,7 @@ cglobal overlay_row_44, 5, 7, 6, 0, d, da, s, a, w, r, x paddw m0, m1 pmulhuw m0, m5 packuswb m0, m0 - movq [dq+xq], m0 + movq [dstq+xq], m0 add xq, mmsize/2 cmp xq, wq jl .loop @@ -65,7 +65,7 @@ cglobal overlay_row_44, 5, 7, 6, 0, d, da, s, a, w, r, x RET INIT_XMM sse4 -cglobal overlay_row_22, 5, 7, 6, 0, d, da, s, a, w, r, x +cglobal overlay_row_22, 5, 7, 6, 0, dst, da, s, a, w, r, x xor xq, xq movsxdifnidn wq, wd sub wq, 1 @@ -85,7 +85,7 @@ cglobal overlay_row_22, 5, 7, 6, 0, d, da, s, a, w, r, x pavgw m2, m1 pavgw m2, m1 psrlw m2, 8 - pmovzxbw m1, [dq+xq] + pmovzxbw m1, [dstq+xq] pmullw m0, m2 pxor m2, m3 pmullw m1, m2 @@ -93,7 +93,7 @@ cglobal overlay_row_22, 5, 7, 6, 0, d, da, s, a, w, r, x paddw m0, m1 pmulhuw m0, m5 packuswb m0, m0 - movq [dq+xq], m0 + movq [dstq+xq], m0 add xq, mmsize/2 cmp xq, wq jl .loop @@ -103,7 +103,7 @@ cglobal overlay_row_22, 5, 7, 6, 0, d, da, s, a, w, r, x RET INIT_XMM sse4 -cglobal overlay_row_20, 6, 7, 7, 0, d, da, s, a, w, r, x +cglobal overlay_row_20, 6, 7, 7, 0, dst, da, s, a, w, r, x mov daq, aq add daq, rmp xor xq, xq @@ -126,7 +126,7 @@ cglobal overlay_row_20, 6, 7, 7, 0, d, da, s, a, w, r, x pmaddubsw m1, m6 paddw m2, m1 psrlw m2, 2 - pmovzxbw m1, [dq+xq] + pmovzxbw m1, [dstq+xq] pmullw m0, m2 pxor m2, m3 pmullw m1, m2 @@ -134,7 +134,7 @@ cglobal overlay_row_20, 6, 7, 7, 0, d, da, s, a, w, r, x paddw m0, m1 pmulhuw m0, m5 packuswb m0, m0 - movq [dq+xq], m0 + movq [dstq+xq], m0 add xq, mmsize/2 cmp xq, wq jl .loop diff --git a/libavfilter/x86/vf_ssim.asm b/libavfilter/x86/vf_ssim.asm index 78809305dec..e3e0c8104b9 100644 --- a/libavfilter/x86/vf_ssim.asm +++ b/libavfilter/x86/vf_ssim.asm @@ -228,25 +228,22 @@ cglobal ssim_end_line, 3, 3, 7, sum0, sum1, w ; subpd the ones we added too much test wd, wd - jz .end + jz .end add wd, 4 - test wd, 3 - jz .skip3 - test wd, 2 - jz .skip2 - test wd, 1 - jz .skip1 -.skip3: + cmp wd, 1 + jz .skip3 + cmp wd, 2 + jz .skip2 +.skip1: ; 3 valid => skip 1 invalid psrldq m5, 8 subpd m6, m5 - jmp .end -.skip2: - psrldq m5, 8 + jmp .end +.skip2: ; 2 valid => skip 2 invalid subpd m6, m5 + jmp .end +.skip3: ; 1 valid => skip 3 invalid + psrldq m3, 8 subpd m0, m3 - jmp .end -.skip1: - psrldq m3, 16 subpd m6, m5 .end: diff --git a/libavfilter/yadif.h b/libavfilter/yadif.h index c928911b357..888ba12365c 100644 --- a/libavfilter/yadif.h +++ b/libavfilter/yadif.h @@ -22,6 +22,7 @@ #include "libavutil/opt.h" #include "libavutil/pixdesc.h" #include "avfilter.h" +#include "ccfifo.h" enum YADIFMode { YADIF_MODE_SEND_FRAME = 0, ///< send 1 frame for each frame @@ -76,6 +77,7 @@ typedef struct YADIFContext { int eof; uint8_t *temp_line; int temp_line_size; + CCFifo cc_fifo; /* * An algorithm that treats first and/or last fields in a sequence @@ -84,6 +86,8 @@ typedef struct YADIFContext { * the first field. */ int current_field; ///< YADIFCurrentField + + int pts_multiplier; } YADIFContext; void ff_yadif_init_x86(YADIFContext *yadif); @@ -92,6 +96,10 @@ int ff_yadif_filter_frame(AVFilterLink *link, AVFrame *frame); int ff_yadif_request_frame(AVFilterLink *link); +int ff_yadif_config_output_common(AVFilterLink *outlink); + +void ff_yadif_uninit(AVFilterContext *ctx); + extern const AVOption ff_yadif_options[]; #endif /* AVFILTER_YADIF_H */ diff --git a/libavfilter/yadif_common.c b/libavfilter/yadif_common.c index a10cf7a17fd..35be87e8d57 100644 --- a/libavfilter/yadif_common.c +++ b/libavfilter/yadif_common.c @@ -22,6 +22,7 @@ #include "libavutil/avassert.h" #include "libavutil/imgutils.h" #include "internal.h" +#include "video.h" #include "yadif.h" static int return_frame(AVFilterContext *ctx, int is_second) @@ -31,8 +32,8 @@ static int return_frame(AVFilterContext *ctx, int is_second) int tff, ret; if (yadif->parity == -1) { - tff = yadif->cur->interlaced_frame ? - yadif->cur->top_field_first : 1; + tff = (yadif->cur->flags & AV_FRAME_FLAG_INTERLACED) ? + !!(yadif->cur->flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) : 1; } else { tff = yadif->parity ^ 1; } @@ -43,7 +44,12 @@ static int return_frame(AVFilterContext *ctx, int is_second) return AVERROR(ENOMEM); av_frame_copy_props(yadif->out, yadif->cur); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS yadif->out->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + yadif->out->flags &= ~AV_FRAME_FLAG_INTERLACED; if (yadif->current_field == YADIF_FIELD_BACK_END) yadif->current_field = YADIF_FIELD_END; } @@ -56,10 +62,16 @@ static int return_frame(AVFilterContext *ctx, int is_second) if (next_pts != AV_NOPTS_VALUE && cur_pts != AV_NOPTS_VALUE) { yadif->out->pts = cur_pts + next_pts; + if (yadif->pts_multiplier == 1) { + yadif->out->pts >>= 1; + yadif->out->duration >>= 1; + } } else { yadif->out->pts = AV_NOPTS_VALUE; } } + + ff_ccfifo_inject(&yadif->cc_fifo, yadif->out); ret = ff_filter_frame(ctx->outputs[0], yadif->out); yadif->frame_pending = (yadif->mode&1) && !is_second; @@ -81,9 +93,9 @@ static void fixstride(AVFilterLink *link, AVFrame *f) if(!dst) return; av_frame_copy_props(dst, f); - av_image_copy(dst->data, dst->linesize, - (const uint8_t **)f->data, f->linesize, - dst->format, dst->width, dst->height); + av_image_copy2(dst->data, dst->linesize, + f->data, f->linesize, + dst->format, dst->width, dst->height); av_frame_unref(f); av_frame_move_ref(f, dst); av_frame_free(&dst); @@ -96,6 +108,8 @@ int ff_yadif_filter_frame(AVFilterLink *link, AVFrame *frame) av_assert0(frame); + ff_ccfifo_extract(&yadif->cc_fifo, frame); + if (yadif->frame_pending) return_frame(ctx, 1); @@ -128,18 +142,20 @@ int ff_yadif_filter_frame(AVFilterLink *link, AVFrame *frame) if (!yadif->prev) return 0; - if ((yadif->deint && !yadif->cur->interlaced_frame) || + if ((yadif->deint && !(yadif->cur->flags & AV_FRAME_FLAG_INTERLACED)) || ctx->is_disabled || - (yadif->deint && !yadif->prev->interlaced_frame && yadif->prev->repeat_pict) || - (yadif->deint && !yadif->next->interlaced_frame && yadif->next->repeat_pict) + (yadif->deint && !(yadif->prev->flags & AV_FRAME_FLAG_INTERLACED) && yadif->prev->repeat_pict) || + (yadif->deint && !(yadif->next->flags & AV_FRAME_FLAG_INTERLACED) && yadif->next->repeat_pict) ) { yadif->out = av_frame_clone(yadif->cur); if (!yadif->out) return AVERROR(ENOMEM); + ff_ccfifo_inject(&yadif->cc_fifo, yadif->out); av_frame_free(&yadif->prev); if (yadif->out->pts != AV_NOPTS_VALUE) - yadif->out->pts *= 2; + yadif->out->pts *= yadif->pts_multiplier; + yadif->out->duration *= yadif->pts_multiplier; return ff_filter_frame(ctx->outputs[0], yadif->out); } @@ -148,10 +164,19 @@ int ff_yadif_filter_frame(AVFilterLink *link, AVFrame *frame) return AVERROR(ENOMEM); av_frame_copy_props(yadif->out, yadif->cur); +#if FF_API_INTERLACED_FRAME +FF_DISABLE_DEPRECATION_WARNINGS yadif->out->interlaced_frame = 0; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + yadif->out->flags &= ~AV_FRAME_FLAG_INTERLACED; if (yadif->out->pts != AV_NOPTS_VALUE) - yadif->out->pts *= 2; + yadif->out->pts *= yadif->pts_multiplier; + if (!(yadif->mode & 1)) + yadif->out->duration *= yadif->pts_multiplier; + else if (yadif->pts_multiplier == 1) + yadif->out->duration >>= 1; return return_frame(ctx, 0); } @@ -190,24 +215,72 @@ int ff_yadif_request_frame(AVFilterLink *link) return 0; } +int ff_yadif_config_output_common(AVFilterLink *outlink) +{ + AVFilterContext *ctx = outlink->src; + YADIFContext *yadif = ctx->priv; + AVRational tb = ctx->inputs[0]->time_base; + int ret; + + if (av_reduce(&outlink->time_base.num, &outlink->time_base.den, tb.num, tb.den * 2LL, INT_MAX)) { + yadif->pts_multiplier = 2; + } else { + av_log(ctx, AV_LOG_WARNING, "Cannot use exact output timebase\n"); + outlink->time_base = tb; + yadif->pts_multiplier = 1; + } + + outlink->w = ctx->inputs[0]->w; + outlink->h = ctx->inputs[0]->h; + + if (outlink->w < 3 || outlink->h < 3) { + av_log(ctx, AV_LOG_ERROR, "Video of less than 3 columns or lines is not supported\n"); + return AVERROR(EINVAL); + } + + if(yadif->mode & 1) + outlink->frame_rate = av_mul_q(ctx->inputs[0]->frame_rate, + (AVRational){2, 1}); + else + outlink->frame_rate = ctx->inputs[0]->frame_rate; + + ret = ff_ccfifo_init(&yadif->cc_fifo, outlink->frame_rate, ctx); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failure to setup CC FIFO queue\n"); + return ret; + } + + return 0; +} + +void ff_yadif_uninit(AVFilterContext *ctx) +{ + YADIFContext *yadif = ctx->priv; + + av_frame_free(&yadif->prev); + av_frame_free(&yadif->cur ); + av_frame_free(&yadif->next); + ff_ccfifo_uninit(&yadif->cc_fifo); +} + #define OFFSET(x) offsetof(YADIFContext, x) #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM -#define CONST(name, help, val, unit) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, unit } +#define CONST(name, help, val, u) { name, help, 0, AV_OPT_TYPE_CONST, {.i64=val}, INT_MIN, INT_MAX, FLAGS, .unit = u } const AVOption ff_yadif_options[] = { - { "mode", "specify the interlacing mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=YADIF_MODE_SEND_FRAME}, 0, 3, FLAGS, "mode"}, + { "mode", "specify the interlacing mode", OFFSET(mode), AV_OPT_TYPE_INT, {.i64=YADIF_MODE_SEND_FRAME}, 0, 3, FLAGS, .unit = "mode"}, CONST("send_frame", "send one frame for each frame", YADIF_MODE_SEND_FRAME, "mode"), CONST("send_field", "send one frame for each field", YADIF_MODE_SEND_FIELD, "mode"), CONST("send_frame_nospatial", "send one frame for each frame, but skip spatial interlacing check", YADIF_MODE_SEND_FRAME_NOSPATIAL, "mode"), CONST("send_field_nospatial", "send one frame for each field, but skip spatial interlacing check", YADIF_MODE_SEND_FIELD_NOSPATIAL, "mode"), - { "parity", "specify the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=YADIF_PARITY_AUTO}, -1, 1, FLAGS, "parity" }, + { "parity", "specify the assumed picture field parity", OFFSET(parity), AV_OPT_TYPE_INT, {.i64=YADIF_PARITY_AUTO}, -1, 1, FLAGS, .unit = "parity" }, CONST("tff", "assume top field first", YADIF_PARITY_TFF, "parity"), CONST("bff", "assume bottom field first", YADIF_PARITY_BFF, "parity"), CONST("auto", "auto detect parity", YADIF_PARITY_AUTO, "parity"), - { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=YADIF_DEINT_ALL}, 0, 1, FLAGS, "deint" }, + { "deint", "specify which frames to deinterlace", OFFSET(deint), AV_OPT_TYPE_INT, {.i64=YADIF_DEINT_ALL}, 0, 1, FLAGS, .unit = "deint" }, CONST("all", "deinterlace all frames", YADIF_DEINT_ALL, "deint"), CONST("interlaced", "only deinterlace frames marked as interlaced", YADIF_DEINT_INTERLACED, "deint"), diff --git a/libavformat/3dostr.c b/libavformat/3dostr.c index fea12d03e6d..78b8205904d 100644 --- a/libavformat/3dostr.c +++ b/libavformat/3dostr.c @@ -21,6 +21,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" static int threedostr_probe(const AVProbeData *p) @@ -191,12 +192,12 @@ static int threedostr_read_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; } -const AVInputFormat ff_threedostr_demuxer = { - .name = "3dostr", - .long_name = NULL_IF_CONFIG_SMALL("3DO STR"), +const FFInputFormat ff_threedostr_demuxer = { + .p.name = "3dostr", + .p.long_name = NULL_IF_CONFIG_SMALL("3DO STR"), + .p.extensions = "str", + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = threedostr_probe, .read_header = threedostr_read_header, .read_packet = threedostr_read_packet, - .extensions = "str", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/4xm.c b/libavformat/4xm.c index fdf6e4b84b5..516c7866bd5 100644 --- a/libavformat/4xm.c +++ b/libavformat/4xm.c @@ -31,6 +31,7 @@ #include "libavutil/intfloat.h" #include "libavcodec/internal.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define RIFF_TAG MKTAG('R', 'I', 'F', 'F') @@ -396,11 +397,11 @@ static int fourxm_read_close(AVFormatContext *s) return 0; } -const AVInputFormat ff_fourxm_demuxer = { - .name = "4xm", - .long_name = NULL_IF_CONFIG_SMALL("4X Technologies"), +const FFInputFormat ff_fourxm_demuxer = { + .p.name = "4xm", + .p.long_name = NULL_IF_CONFIG_SMALL("4X Technologies"), .priv_data_size = sizeof(FourxmDemuxContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = fourxm_probe, .read_header = fourxm_read_header, .read_packet = fourxm_read_packet, diff --git a/libavformat/Makefile b/libavformat/Makefile index 47bbbbfb2a9..ae86954e7c0 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -35,6 +35,8 @@ OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o # subsystems OBJS-$(CONFIG_ISO_MEDIA) += isom.o +OBJS-$(CONFIG_IAMFDEC) += iamf_reader.o iamf_parse.o iamf.o +OBJS-$(CONFIG_IAMFENC) += iamf_writer.o iamf.o OBJS-$(CONFIG_NETWORK) += network.o OBJS-$(CONFIG_RIFFDEC) += riffdec.o OBJS-$(CONFIG_RIFFENC) += riffenc.o @@ -77,6 +79,8 @@ OBJS-$(CONFIG_AAC_DEMUXER) += aacdec.o apetag.o img2.o rawdec.o OBJS-$(CONFIG_AAX_DEMUXER) += aaxdec.o OBJS-$(CONFIG_AC3_DEMUXER) += ac3dec.o rawdec.o OBJS-$(CONFIG_AC3_MUXER) += rawenc.o +OBJS-$(CONFIG_AC4_DEMUXER) += ac4dec.o +OBJS-$(CONFIG_AC4_MUXER) += ac4enc.o OBJS-$(CONFIG_ACE_DEMUXER) += acedec.o OBJS-$(CONFIG_ACM_DEMUXER) += acm.o rawdec.o OBJS-$(CONFIG_ACT_DEMUXER) += act.o @@ -87,7 +91,8 @@ OBJS-$(CONFIG_ADTS_MUXER) += adtsenc.o apetag.o img2.o \ id3v2enc.o OBJS-$(CONFIG_ADX_DEMUXER) += adxdec.o OBJS-$(CONFIG_ADX_MUXER) += rawenc.o -OBJS-$(CONFIG_AEA_DEMUXER) += aea.o pcm.o +OBJS-$(CONFIG_AEA_DEMUXER) += aeadec.o pcm.o +OBJS-$(CONFIG_AEA_MUXER) += aeaenc.o rawenc.o OBJS-$(CONFIG_AFC_DEMUXER) += afc.o OBJS-$(CONFIG_AIFF_DEMUXER) += aiffdec.o aiff.o pcm.o \ mov_chan.o replaygain.o @@ -190,6 +195,7 @@ OBJS-$(CONFIG_DTS_MUXER) += rawenc.o OBJS-$(CONFIG_DV_MUXER) += dvenc.o OBJS-$(CONFIG_DVBSUB_DEMUXER) += dvbsub.o rawdec.o OBJS-$(CONFIG_DVBTXT_DEMUXER) += dvbtxt.o rawdec.o +OBJS-$(CONFIG_DVDVIDEO_DEMUXER) += dvdvideodec.o dvdclut.o OBJS-$(CONFIG_DXA_DEMUXER) += dxa.o OBJS-$(CONFIG_EA_CDATA_DEMUXER) += eacdata.o OBJS-$(CONFIG_EA_DEMUXER) += electronicarts.o @@ -199,7 +205,6 @@ OBJS-$(CONFIG_EPAF_DEMUXER) += epafdec.o pcm.o OBJS-$(CONFIG_FFMETADATA_DEMUXER) += ffmetadec.o OBJS-$(CONFIG_FFMETADATA_MUXER) += ffmetaenc.o OBJS-$(CONFIG_FIFO_MUXER) += fifo.o -OBJS-$(CONFIG_FIFO_TEST_MUXER) += fifo_test.o OBJS-$(CONFIG_FILMSTRIP_DEMUXER) += filmstripdec.o OBJS-$(CONFIG_FILMSTRIP_MUXER) += filmstripenc.o rawenc.o OBJS-$(CONFIG_FITS_DEMUXER) += fitsdec.o @@ -214,7 +219,7 @@ OBJS-$(CONFIG_FLAC_MUXER) += flacenc.o flacenc_header.o \ OBJS-$(CONFIG_FLIC_DEMUXER) += flic.o OBJS-$(CONFIG_FLV_DEMUXER) += flvdec.o OBJS-$(CONFIG_LIVE_FLV_DEMUXER) += flvdec.o -OBJS-$(CONFIG_FLV_MUXER) += flvenc.o avc.o +OBJS-$(CONFIG_FLV_MUXER) += flvenc.o avc.o hevc.o av1.o vpcc.o OBJS-$(CONFIG_FOURXM_DEMUXER) += 4xm.o OBJS-$(CONFIG_FRAMECRC_MUXER) += framecrcenc.o framehash.o OBJS-$(CONFIG_FRAMEHASH_MUXER) += hashenc.o framehash.o @@ -251,9 +256,13 @@ OBJS-$(CONFIG_HCOM_DEMUXER) += hcom.o pcm.o OBJS-$(CONFIG_HDS_MUXER) += hdsenc.o OBJS-$(CONFIG_HEVC_DEMUXER) += hevcdec.o rawdec.o OBJS-$(CONFIG_HEVC_MUXER) += rawenc.o +OBJS-$(CONFIG_EVC_DEMUXER) += evcdec.o rawdec.o +OBJS-$(CONFIG_EVC_MUXER) += rawenc.o OBJS-$(CONFIG_HLS_DEMUXER) += hls.o hls_sample_encryption.o OBJS-$(CONFIG_HLS_MUXER) += hlsenc.o hlsplaylist.o avc.o OBJS-$(CONFIG_HNM_DEMUXER) += hnm.o +OBJS-$(CONFIG_IAMF_DEMUXER) += iamfdec.o +OBJS-$(CONFIG_IAMF_MUXER) += iamfenc.o OBJS-$(CONFIG_ICO_DEMUXER) += icodec.o OBJS-$(CONFIG_ICO_MUXER) += icoenc.o OBJS-$(CONFIG_IDCIN_DEMUXER) += idcin.o @@ -279,7 +288,7 @@ OBJS-$(CONFIG_IMAGE_HDR_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_J2K_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_JPEG_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_JPEGLS_PIPE_DEMUXER) += img2dec.o img2.o -OBJS-$(CONFIG_IMAGE_JPEGXL_PIPE_DEMUXER) += img2dec.o img2.o jpegxl_probe.o +OBJS-$(CONFIG_IMAGE_JPEGXL_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_PAM_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_PBM_PIPE_DEMUXER) += img2dec.o img2.o OBJS-$(CONFIG_IMAGE_PCX_PIPE_DEMUXER) += img2dec.o img2.o @@ -316,6 +325,7 @@ OBJS-$(CONFIG_IVF_MUXER) += ivfenc.o OBJS-$(CONFIG_IVR_DEMUXER) += rmdec.o rm.o rmsipr.o OBJS-$(CONFIG_JACOSUB_DEMUXER) += jacosubdec.o subtitles.o OBJS-$(CONFIG_JACOSUB_MUXER) += jacosubenc.o rawenc.o +OBJS-$(CONFIG_JPEGXL_ANIM_DEMUXER) += jpegxl_anim_dec.o OBJS-$(CONFIG_JV_DEMUXER) += jvdec.o OBJS-$(CONFIG_KUX_DEMUXER) += flvdec.o OBJS-$(CONFIG_KVAG_DEMUXER) += kvag.o @@ -336,7 +346,7 @@ OBJS-$(CONFIG_MATROSKA_DEMUXER) += matroskadec.o matroska.o \ oggparsevorbis.o vorbiscomment.o \ qtpalette.o replaygain.o dovi_isom.o OBJS-$(CONFIG_MATROSKA_MUXER) += matroskaenc.o matroska.o \ - av1.o avc.o hevc.o \ + av1.o avc.o hevc.o vvc.o\ flacenc_header.o avlanguage.o \ vorbiscomment.o wv.o dovi_isom.o OBJS-$(CONFIG_MCA_DEMUXER) += mca.o @@ -358,10 +368,10 @@ OBJS-$(CONFIG_MODS_DEMUXER) += mods.o OBJS-$(CONFIG_MOFLEX_DEMUXER) += moflex.o OBJS-$(CONFIG_MOV_DEMUXER) += mov.o mov_chan.o mov_esds.o \ qtpalette.o replaygain.o dovi_isom.o -OBJS-$(CONFIG_MOV_MUXER) += movenc.o av1.o avc.o hevc.o vpcc.o \ +OBJS-$(CONFIG_MOV_MUXER) += movenc.o av1.o avc.o hevc.o vvc.o vpcc.o \ movenchint.o mov_chan.o rtp.o \ movenccenc.o movenc_ttml.o rawutils.o \ - dovi_isom.o + dovi_isom.o evc.o OBJS-$(CONFIG_MP2_MUXER) += rawenc.o OBJS-$(CONFIG_MP3_DEMUXER) += mp3dec.o replaygain.o OBJS-$(CONFIG_MP3_MUXER) += mp3enc.o rawenc.o id3v2enc.o @@ -428,6 +438,7 @@ OBJS-$(CONFIG_OMA_DEMUXER) += omadec.o pcm.o oma.o OBJS-$(CONFIG_OMA_MUXER) += omaenc.o rawenc.o oma.o id3v2enc.o OBJS-$(CONFIG_OPUS_MUXER) += oggenc.o \ vorbiscomment.o +OBJS-$(CONFIG_OSQ_DEMUXER) += osq.o rawdec.o OBJS-$(CONFIG_PAF_DEMUXER) += paf.o OBJS-$(CONFIG_PCM_ALAW_DEMUXER) += pcmdec.o pcm.o OBJS-$(CONFIG_PCM_ALAW_MUXER) += pcmenc.o rawenc.o @@ -471,15 +482,18 @@ OBJS-$(CONFIG_PCM_U8_DEMUXER) += pcmdec.o pcm.o OBJS-$(CONFIG_PCM_U8_MUXER) += pcmenc.o rawenc.o OBJS-$(CONFIG_PCM_VIDC_DEMUXER) += pcmdec.o pcm.o OBJS-$(CONFIG_PCM_VIDC_MUXER) += pcmenc.o rawenc.o +OBJS-$(CONFIG_PDV_DEMUXER) += pdvdec.o OBJS-$(CONFIG_PJS_DEMUXER) += pjsdec.o subtitles.o OBJS-$(CONFIG_PMP_DEMUXER) += pmpdec.o OBJS-$(CONFIG_PP_BNK_DEMUXER) += pp_bnk.o OBJS-$(CONFIG_PVA_DEMUXER) += pva.o OBJS-$(CONFIG_PVF_DEMUXER) += pvfdec.o pcm.o OBJS-$(CONFIG_QCP_DEMUXER) += qcp.o +OBJS-$(CONFIG_QOA_DEMUXER) += qoadec.o OBJS-$(CONFIG_R3D_DEMUXER) += r3d.o OBJS-$(CONFIG_RAWVIDEO_DEMUXER) += rawvideodec.o OBJS-$(CONFIG_RAWVIDEO_MUXER) += rawenc.o +OBJS-$(CONFIG_RCWT_MUXER) += rcwtenc.o subtitles.o OBJS-$(CONFIG_REALTEXT_DEMUXER) += realtextdec.o subtitles.o OBJS-$(CONFIG_REDSPARK_DEMUXER) += redspark.o OBJS-$(CONFIG_RKA_DEMUXER) += rka.o apetag.o img2.o @@ -509,7 +523,7 @@ OBJS-$(CONFIG_RTP_MUXER) += rtp.o \ rtpenc_vp8.o \ rtpenc_vp9.o \ rtpenc_xiph.o \ - avc.o hevc.o + avc.o hevc.o vvc.o OBJS-$(CONFIG_RTSP_DEMUXER) += rtsp.o rtspdec.o httpauth.o \ urldecode.o OBJS-$(CONFIG_RTSP_MUXER) += rtsp.o rtspenc.o httpauth.o \ @@ -581,6 +595,7 @@ OBJS-$(CONFIG_TTY_DEMUXER) += tty.o sauce.o OBJS-$(CONFIG_TY_DEMUXER) += ty.o OBJS-$(CONFIG_TXD_DEMUXER) += txd.o OBJS-$(CONFIG_UNCODEDFRAMECRC_MUXER) += uncodedframecrcenc.o framehash.o +OBJS-$(CONFIG_USM_DEMUXER) += usmdec.o OBJS-$(CONFIG_V210_DEMUXER) += rawvideodec.o OBJS-$(CONFIG_V210X_DEMUXER) += rawvideodec.o OBJS-$(CONFIG_VAG_DEMUXER) += vag.o @@ -597,6 +612,8 @@ OBJS-$(CONFIG_VOC_MUXER) += vocenc.o voc.o OBJS-$(CONFIG_VPK_DEMUXER) += vpk.o OBJS-$(CONFIG_VPLAYER_DEMUXER) += vplayerdec.o subtitles.o OBJS-$(CONFIG_VQF_DEMUXER) += vqf.o +OBJS-$(CONFIG_VVC_DEMUXER) += vvcdec.o rawdec.o +OBJS-$(CONFIG_VVC_MUXER) += rawenc.o OBJS-$(CONFIG_W64_DEMUXER) += wavdec.o w64.o pcm.o OBJS-$(CONFIG_W64_MUXER) += wavenc.o w64.o OBJS-$(CONFIG_WADY_DEMUXER) += wady.o pcm.o @@ -641,6 +658,7 @@ OBJS-$(CONFIG_LIBOPENMPT_DEMUXER) += libopenmpt.o OBJS-$(CONFIG_VAPOURSYNTH_DEMUXER) += vapoursynth.o # protocols I/O +OBJS-$(CONFIG_ANDROID_CONTENT_PROTOCOL) += file.o OBJS-$(CONFIG_ASYNC_PROTOCOL) += async.o OBJS-$(CONFIG_APPLEHTTP_PROTOCOL) += hlsproto.o OBJS-$(CONFIG_BLURAY_PROTOCOL) += bluray.o @@ -700,19 +718,23 @@ OBJS-$(CONFIG_LIBRTMPS_PROTOCOL) += librtmp.o OBJS-$(CONFIG_LIBRTMPT_PROTOCOL) += librtmp.o OBJS-$(CONFIG_LIBRTMPTE_PROTOCOL) += librtmp.o OBJS-$(CONFIG_LIBSMBCLIENT_PROTOCOL) += libsmbclient.o -OBJS-$(CONFIG_LIBSRT_PROTOCOL) += libsrt.o +OBJS-$(CONFIG_LIBSRT_PROTOCOL) += libsrt.o urldecode.o OBJS-$(CONFIG_LIBSSH_PROTOCOL) += libssh.o OBJS-$(CONFIG_LIBZMQ_PROTOCOL) += libzmq.o # Objects duplicated from other libraries for shared builds -SHLIBOBJS += log2_tab.o to_upper4.o +SHLIBOBJS += log2_tab.o to_upper4.o bitstream.o SHLIBOBJS-$(CONFIG_ISO_MEDIA) += mpegaudiotabs.o SHLIBOBJS-$(CONFIG_FLV_MUXER) += mpeg4audio_sample_rates.o SHLIBOBJS-$(CONFIG_HLS_DEMUXER) += ac3_channel_layout_tab.o +SHLIBOBJS-$(CONFIG_IMAGE_JPEGXL_PIPE_DEMUXER) += jpegxl_parse.o +SHLIBOBJS-$(CONFIG_JNI) += ffjni.o +SHLIBOBJS-$(CONFIG_JPEGXL_ANIM_DEMUXER) += jpegxl_parse.o SHLIBOBJS-$(CONFIG_MATROSKA_DEMUXER) += mpeg4audio_sample_rates.o SHLIBOBJS-$(CONFIG_MOV_DEMUXER) += ac3_channel_layout_tab.o SHLIBOBJS-$(CONFIG_MP3_MUXER) += mpegaudiotabs.o -SHLIBOBJS-$(CONFIG_MXF_MUXER) += golomb_tab.o +SHLIBOBJS-$(CONFIG_MXF_MUXER) += golomb_tab.o \ + rangecoder_dec.o SHLIBOBJS-$(CONFIG_NUT_MUXER) += mpegaudiotabs.o SHLIBOBJS-$(CONFIG_RTPDEC) += jpegtables.o SHLIBOBJS-$(CONFIG_RTP_MUXER) += golomb_tab.o jpegtables.o \ diff --git a/libavformat/a64.c b/libavformat/a64.c index 23b20fc8b79..6e722c7e9fe 100644 --- a/libavformat/a64.c +++ b/libavformat/a64.c @@ -65,6 +65,9 @@ const FFOutputFormat ff_a64_muxer = { .p.long_name = NULL_IF_CONFIG_SMALL("a64 - video for Commodore 64"), .p.extensions = "a64, A64", .p.video_codec = AV_CODEC_ID_A64_MULTI, + .p.audio_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH, .write_header = a64_write_header, .write_packet = ff_raw_write_packet, }; diff --git a/libavformat/aacdec.c b/libavformat/aacdec.c index a4766409041..e267886e1a0 100644 --- a/libavformat/aacdec.c +++ b/libavformat/aacdec.c @@ -24,6 +24,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "avio_internal.h" +#include "demux.h" #include "internal.h" #include "id3v1.h" #include "id3v2.h" @@ -113,7 +114,7 @@ static int adts_aac_read_header(AVFormatContext *s) return AVERROR(ENOMEM); st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - st->codecpar->codec_id = s->iformat->raw_codec_id; + st->codecpar->codec_id = AV_CODEC_ID_AAC; ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; ff_id3v1_read(s); @@ -146,7 +147,7 @@ static int handle_id3(AVFormatContext *s, AVPacket *pkt) return ret; } - ffio_init_context(&pb, pkt->data, pkt->size, 0, NULL, NULL, NULL, NULL); + ffio_init_read_context(&pb, pkt->data, pkt->size); ff_id3v2_read_dict(&pb.pub, &metadata, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); if ((ret = ff_id3v2_parse_priv_dict(&metadata, id3v2_extra_meta)) < 0) goto error; @@ -208,14 +209,14 @@ static int adts_aac_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_aac_demuxer = { - .name = "aac", - .long_name = NULL_IF_CONFIG_SMALL("raw ADTS AAC (Advanced Audio Coding)"), +const FFInputFormat ff_aac_demuxer = { + .p.name = "aac", + .p.long_name = NULL_IF_CONFIG_SMALL("raw ADTS AAC (Advanced Audio Coding)"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "aac", + .p.mime_type = "audio/aac,audio/aacp,audio/x-aac", .read_probe = adts_aac_probe, .read_header = adts_aac_read_header, .read_packet = adts_aac_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "aac", - .mime_type = "audio/aac,audio/aacp,audio/x-aac", .raw_codec_id = AV_CODEC_ID_AAC, }; diff --git a/libavformat/aadec.c b/libavformat/aadec.c index e7b048b1f96..c39fb51a8d6 100644 --- a/libavformat/aadec.c +++ b/libavformat/aadec.c @@ -371,17 +371,17 @@ static const AVClass aa_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_aa_demuxer = { - .name = "aa", - .long_name = NULL_IF_CONFIG_SMALL("Audible AA format files"), - .priv_class = &aa_class, +const FFInputFormat ff_aa_demuxer = { + .p.name = "aa", + .p.long_name = NULL_IF_CONFIG_SMALL("Audible AA format files"), + .p.priv_class = &aa_class, + .p.extensions = "aa", + .p.flags = AVFMT_NO_BYTE_SEEK | AVFMT_NOGENSEARCH, .priv_data_size = sizeof(AADemuxContext), - .extensions = "aa", .read_probe = aa_probe, .read_header = aa_read_header, .read_packet = aa_read_packet, .read_seek = aa_read_seek, .read_close = aa_read_close, - .flags = AVFMT_NO_BYTE_SEEK | AVFMT_NOGENSEARCH, - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, }; diff --git a/libavformat/aaxdec.c b/libavformat/aaxdec.c index 4e352f8ce37..830ae5d89e4 100644 --- a/libavformat/aaxdec.c +++ b/libavformat/aaxdec.c @@ -383,15 +383,15 @@ static int aax_read_close(AVFormatContext *s) return 0; } -const AVInputFormat ff_aax_demuxer = { - .name = "aax", - .long_name = NULL_IF_CONFIG_SMALL("CRI AAX"), +const FFInputFormat ff_aax_demuxer = { + .p.name = "aax", + .p.long_name = NULL_IF_CONFIG_SMALL("CRI AAX"), + .p.extensions = "aax", + .p.flags = AVFMT_GENERIC_INDEX, .priv_data_size = sizeof(AAXContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = aax_probe, .read_header = aax_read_header, .read_packet = aax_read_packet, .read_close = aax_read_close, - .extensions = "aax", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/ac3dec.c b/libavformat/ac3dec.c index 989d126a81b..0b1557d68c5 100644 --- a/libavformat/ac3dec.c +++ b/libavformat/ac3dec.c @@ -25,6 +25,7 @@ #include "libavutil/crc.h" #include "libavcodec/ac3_parser.h" #include "avformat.h" +#include "demux.h" #include "rawdec.h" static int ac3_eac3_probe(const AVProbeData *p, enum AVCodecID expected_codec_id) @@ -104,17 +105,17 @@ static int ac3_probe(const AVProbeData *p) return ac3_eac3_probe(p, AV_CODEC_ID_AC3); } -const AVInputFormat ff_ac3_demuxer = { - .name = "ac3", - .long_name = NULL_IF_CONFIG_SMALL("raw AC-3"), +const FFInputFormat ff_ac3_demuxer = { + .p.name = "ac3", + .p.long_name = NULL_IF_CONFIG_SMALL("raw AC-3"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "ac3", + .p.priv_class = &ff_raw_demuxer_class, .read_probe = ac3_probe, .read_header = ff_raw_audio_read_header, .read_packet = ff_raw_read_partial_packet, - .flags= AVFMT_GENERIC_INDEX, - .extensions = "ac3", .raw_codec_id = AV_CODEC_ID_AC3, .priv_data_size = sizeof(FFRawDemuxerContext), - .priv_class = &ff_raw_demuxer_class, }; #endif @@ -124,16 +125,16 @@ static int eac3_probe(const AVProbeData *p) return ac3_eac3_probe(p, AV_CODEC_ID_EAC3); } -const AVInputFormat ff_eac3_demuxer = { - .name = "eac3", - .long_name = NULL_IF_CONFIG_SMALL("raw E-AC-3"), +const FFInputFormat ff_eac3_demuxer = { + .p.name = "eac3", + .p.long_name = NULL_IF_CONFIG_SMALL("raw E-AC-3"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "eac3,ec3", + .p.priv_class = &ff_raw_demuxer_class, .read_probe = eac3_probe, .read_header = ff_raw_audio_read_header, .read_packet = ff_raw_read_partial_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "eac3,ec3", .raw_codec_id = AV_CODEC_ID_EAC3, .priv_data_size = sizeof(FFRawDemuxerContext), - .priv_class = &ff_raw_demuxer_class, }; #endif diff --git a/libavformat/ac4dec.c b/libavformat/ac4dec.c new file mode 100644 index 00000000000..dc6638de3a4 --- /dev/null +++ b/libavformat/ac4dec.c @@ -0,0 +1,107 @@ +/* + * RAW AC-4 demuxer + * Copyright (c) 2019 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/avassert.h" +#include "libavutil/crc.h" +#include "avformat.h" +#include "demux.h" +#include "rawdec.h" + +static int ac4_probe(const AVProbeData *p) +{ + const uint8_t *buf = p->buf; + int left = p->buf_size; + int max_frames = 0; + + while (left > 7) { + int size; + + if (buf[0] == 0xAC && + (buf[1] == 0x40 || + buf[1] == 0x41)) { + size = (buf[2] << 8) | buf[3]; + if (size == 0xFFFF) + size = 3 + ((buf[4] << 16) | (buf[5] << 8) | buf[6]); + size += 4; + if (buf[1] == 0x41) + size += 2; + if (left < size) + break; + max_frames++; + left -= size; + buf += size; + } else { + break; + } + } + + return FFMIN(AVPROBE_SCORE_MAX, max_frames * 7); +} + +static int ac4_read_header(AVFormatContext *s) +{ + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->codec_id = AV_CODEC_ID_AC4; + + return 0; +} + +static int ac4_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + int64_t pos; + uint16_t sync; + int ret, size; + + if (avio_feof(s->pb)) + return AVERROR_EOF; + + pos = avio_tell(s->pb); + sync = avio_rb16(pb); + size = avio_rb16(pb); + if (size == 0xffff) + size = avio_rb24(pb); + + ret = av_get_packet(pb, pkt, size); + pkt->pos = pos; + pkt->stream_index = 0; + + if (sync == 0xAC41) + avio_skip(pb, 2); + + return ret; +} + +const FFInputFormat ff_ac4_demuxer = { + .p.name = "ac4", + .p.long_name = NULL_IF_CONFIG_SMALL("raw AC-4"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "ac4", + .read_probe = ac4_probe, + .read_header = ac4_read_header, + .read_packet = ac4_read_packet, +}; diff --git a/libavformat/ac4enc.c b/libavformat/ac4enc.c new file mode 100644 index 00000000000..0505b051474 --- /dev/null +++ b/libavformat/ac4enc.c @@ -0,0 +1,91 @@ +/* + * Raw AC-4 muxer + * + * 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 "libavcodec/codec_id.h" +#include "libavcodec/packet.h" +#include "libavutil/crc.h" +#include "libavutil/opt.h" +#include "avformat.h" +#include "mux.h" + +typedef struct AC4Context { + AVClass *class; + int write_crc; +} AC4Context; + +static int ac4_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + AC4Context *ac4 = s->priv_data; + AVIOContext *pb = s->pb; + + if (!pkt->size) + return 0; + + if (ac4->write_crc) + avio_wb16(pb, 0xAC41); + else + avio_wb16(pb, 0xAC40); + + if (pkt->size >= 0xffff) { + avio_wb16(pb, 0xffff); + avio_wb24(pb, pkt->size); + } else { + avio_wb16(pb, pkt->size); + } + + avio_write(pb, pkt->data, pkt->size); + + if (ac4->write_crc) { + uint16_t crc = av_crc(av_crc_get_table(AV_CRC_16_ANSI), 0, pkt->data, pkt->size); + avio_wl16(pb, crc); + } + + return 0; +} + +#define ENC AV_OPT_FLAG_ENCODING_PARAM +#define OFFSET(obj) offsetof(AC4Context, obj) +static const AVOption ac4_options[] = { + { "write_crc", "enable checksum", OFFSET(write_crc), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, ENC}, + { NULL }, +}; + +static const AVClass ac4_muxer_class = { + .class_name = "AC4 muxer", + .item_name = av_default_item_name, + .option = ac4_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFOutputFormat ff_ac4_muxer = { + .p.name = "ac4", + .p.long_name = NULL_IF_CONFIG_SMALL("raw AC-4"), + .p.mime_type = "audio/ac4", + .p.extensions = "ac4", + .priv_data_size = sizeof(AC4Context), + .p.audio_codec = AV_CODEC_ID_AC4, + .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, + .write_packet = ac4_write_packet, + .p.priv_class = &ac4_muxer_class, + .p.flags = AVFMT_NOTIMESTAMPS, +}; diff --git a/libavformat/acedec.c b/libavformat/acedec.c index 6322af31f3c..5ab6de02f4f 100644 --- a/libavformat/acedec.c +++ b/libavformat/acedec.c @@ -21,6 +21,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" static int ace_probe(const AVProbeData *p) @@ -104,11 +105,11 @@ static int ace_read_packet(AVFormatContext *s, AVPacket *pkt) return av_get_packet(s->pb, pkt, par->block_align); } -const AVInputFormat ff_ace_demuxer = { - .name = "ace", - .long_name = NULL_IF_CONFIG_SMALL("tri-Ace Audio Container"), +const FFInputFormat ff_ace_demuxer = { + .p.name = "ace", + .p.long_name = NULL_IF_CONFIG_SMALL("tri-Ace Audio Container"), + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = ace_probe, .read_header = ace_read_header, .read_packet = ace_read_packet, - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/acm.c b/libavformat/acm.c index 28a040330f6..b9126ef8246 100644 --- a/libavformat/acm.c +++ b/libavformat/acm.c @@ -61,15 +61,15 @@ static int acm_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_acm_demuxer = { - .name = "acm", - .long_name = NULL_IF_CONFIG_SMALL("Interplay ACM"), +const FFInputFormat ff_acm_demuxer = { + .p.name = "acm", + .p.long_name = NULL_IF_CONFIG_SMALL("Interplay ACM"), + .p.flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS, + .p.extensions = "acm", + .p.priv_class = &ff_raw_demuxer_class, .read_probe = acm_probe, .read_header = acm_read_header, .read_packet = ff_raw_read_partial_packet, - .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS, - .extensions = "acm", .raw_codec_id = AV_CODEC_ID_INTERPLAY_ACM, .priv_data_size = sizeof(FFRawDemuxerContext), - .priv_class = &ff_raw_demuxer_class, }; diff --git a/libavformat/act.c b/libavformat/act.c index da73fcceca0..255568dd3cc 100644 --- a/libavformat/act.c +++ b/libavformat/act.c @@ -22,6 +22,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "avio_internal.h" +#include "demux.h" #include "riff.h" #include "internal.h" @@ -198,9 +199,9 @@ static int read_packet(AVFormatContext *s, return ret; } -const AVInputFormat ff_act_demuxer = { - .name = "act", - .long_name = "ACT Voice file format", +const FFInputFormat ff_act_demuxer = { + .p.name = "act", + .p.long_name = "ACT Voice file format", .priv_data_size = sizeof(ACTContext), .read_probe = probe, .read_header = read_header, diff --git a/libavformat/adp.c b/libavformat/adp.c index c9a5a045070..2e69c7a199a 100644 --- a/libavformat/adp.c +++ b/libavformat/adp.c @@ -22,6 +22,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" static int adp_probe(const AVProbeData *p) @@ -82,11 +83,11 @@ static int adp_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_adp_demuxer = { - .name = "adp", - .long_name = NULL_IF_CONFIG_SMALL("ADP"), +const FFInputFormat ff_adp_demuxer = { + .p.name = "adp", + .p.long_name = NULL_IF_CONFIG_SMALL("ADP"), + .p.extensions = "adp,dtk", .read_probe = adp_probe, .read_header = adp_read_header, .read_packet = adp_read_packet, - .extensions = "adp,dtk", }; diff --git a/libavformat/ads.c b/libavformat/ads.c index c19498490d1..ea812487657 100644 --- a/libavformat/ads.c +++ b/libavformat/ads.c @@ -21,6 +21,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" +#include "demux.h" #include "internal.h" static int ads_probe(const AVProbeData *p) @@ -80,11 +81,11 @@ static int ads_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_ads_demuxer = { - .name = "ads", - .long_name = NULL_IF_CONFIG_SMALL("Sony PS2 ADS"), +const FFInputFormat ff_ads_demuxer = { + .p.name = "ads", + .p.long_name = NULL_IF_CONFIG_SMALL("Sony PS2 ADS"), + .p.extensions = "ads,ss2", .read_probe = ads_probe, .read_header = ads_read_header, .read_packet = ads_read_packet, - .extensions = "ads,ss2", }; diff --git a/libavformat/adtsenc.c b/libavformat/adtsenc.c index b5e9640544b..0671224fc20 100644 --- a/libavformat/adtsenc.c +++ b/libavformat/adtsenc.c @@ -106,10 +106,6 @@ static int adts_init(AVFormatContext *s) ADTSContext *adts = s->priv_data; AVCodecParameters *par = s->streams[0]->codecpar; - if (par->codec_id != AV_CODEC_ID_AAC) { - av_log(s, AV_LOG_ERROR, "Only AAC streams can be muxed by the ADTS muxer\n"); - return AVERROR(EINVAL); - } if (par->extradata_size > 0) return adts_decode_extradata(s, adts, par->extradata, par->extradata_size); @@ -127,14 +123,14 @@ static int adts_write_header(AVFormatContext *s) return 0; } -static int adts_write_frame_header(ADTSContext *ctx, +static int adts_write_frame_header(AVFormatContext *s, ADTSContext *ctx, uint8_t *buf, int size, int pce_size) { PutBitContext pb; unsigned full_frame_size = (unsigned)ADTS_HEADER_SIZE + size + pce_size; if (full_frame_size > ADTS_MAX_FRAME_BYTES) { - av_log(NULL, AV_LOG_ERROR, "ADTS frame size too large: %u (max %d)\n", + av_log(s, AV_LOG_ERROR, "frame size too large: %u (max %d)\n", full_frame_size, ADTS_MAX_FRAME_BYTES); return AVERROR_INVALIDDATA; } @@ -192,7 +188,7 @@ static int adts_write_packet(AVFormatContext *s, AVPacket *pkt) } } if (adts->write_adts) { - int err = adts_write_frame_header(adts, buf, pkt->size, + int err = adts_write_frame_header(s, adts, buf, pkt->size, adts->pce_size); if (err < 0) return err; @@ -241,6 +237,9 @@ const FFOutputFormat ff_adts_muxer = { .priv_data_size = sizeof(ADTSContext), .p.audio_codec = AV_CODEC_ID_AAC, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .init = adts_init, .write_header = adts_write_header, .write_packet = adts_write_packet, diff --git a/libavformat/adxdec.c b/libavformat/adxdec.c index 982e8254268..0909884f197 100644 --- a/libavformat/adxdec.c +++ b/libavformat/adxdec.c @@ -57,11 +57,6 @@ static int adx_read_packet(AVFormatContext *s, AVPacket *pkt) if (avio_feof(s->pb)) return AVERROR_EOF; - if (par->ch_layout.nb_channels <= 0) { - av_log(s, AV_LOG_ERROR, "invalid number of channels %d\n", par->ch_layout.nb_channels); - return AVERROR_INVALIDDATA; - } - size = BLOCK_SIZE * par->ch_layout.nb_channels; pkt->pos = avio_tell(s->pb); @@ -125,7 +120,7 @@ static int adx_read_header(AVFormatContext *s) par->ch_layout.nb_channels = channels; par->codec_type = AVMEDIA_TYPE_AUDIO; - par->codec_id = s->iformat->raw_codec_id; + par->codec_id = AV_CODEC_ID_ADPCM_ADX; par->bit_rate = (int64_t)par->sample_rate * par->ch_layout.nb_channels * BLOCK_SIZE * 8LL / BLOCK_SAMPLES; avpriv_set_pts_info(st, 64, BLOCK_SAMPLES, par->sample_rate); @@ -133,14 +128,14 @@ static int adx_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_adx_demuxer = { - .name = "adx", - .long_name = NULL_IF_CONFIG_SMALL("CRI ADX"), +const FFInputFormat ff_adx_demuxer = { + .p.name = "adx", + .p.long_name = NULL_IF_CONFIG_SMALL("CRI ADX"), + .p.extensions = "adx", + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = adx_probe, .priv_data_size = sizeof(ADXDemuxerContext), .read_header = adx_read_header, .read_packet = adx_read_packet, - .extensions = "adx", .raw_codec_id = AV_CODEC_ID_ADPCM_ADX, - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/aea.c b/libavformat/aeadec.c similarity index 65% rename from libavformat/aea.c rename to libavformat/aeadec.c index f4b39e4f9ec..be18e7b7256 100644 --- a/libavformat/aea.c +++ b/libavformat/aeadec.c @@ -23,38 +23,37 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "avio_internal.h" +#include "demux.h" +#include "internal.h" #include "pcm.h" -#define AT1_SU_SIZE 212 +#define AT1_SU_SIZE 212 static int aea_read_probe(const AVProbeData *p) { - if (p->buf_size <= 2048+212) + if (p->buf_size <= 2048+AT1_SU_SIZE) return 0; /* Magic is '00 08 00 00' in little-endian*/ if (AV_RL32(p->buf)==0x800) { - int ch, i; + int ch, block_size, score = 0; ch = p->buf[264]; if (ch != 1 && ch != 2) return 0; + block_size = ch * AT1_SU_SIZE; /* Check so that the redundant bsm bytes and info bytes are valid * the block size mode bytes have to be the same * the info bytes have to be the same */ - for (i = 2048; i + 211 < p->buf_size; i+= 212) { - int bsm_s, bsm_e, inb_s, inb_e; - bsm_s = p->buf[0]; - inb_s = p->buf[1]; - inb_e = p->buf[210]; - bsm_e = p->buf[211]; - - if (bsm_s != bsm_e || inb_s != inb_e) + for (int i = 2048 + block_size; i + block_size <= p->buf_size; i += block_size) { + if (AV_RN16(p->buf+i) != AV_RN16(p->buf+i+AT1_SU_SIZE)) return 0; + score++; } - return AVPROBE_SCORE_MAX / 4 + 1; + return FFMIN(AVPROBE_SCORE_MAX / 4 + score, AVPROBE_SCORE_MAX); } return 0; } @@ -62,12 +61,20 @@ static int aea_read_probe(const AVProbeData *p) static int aea_read_header(AVFormatContext *s) { AVStream *st = avformat_new_stream(s, NULL); - int channels; + char title[256 + 1]; + int channels, ret; if (!st) return AVERROR(ENOMEM); - /* Parse the amount of channels and skip to pos 2048(0x800) */ - avio_skip(s->pb, 264); + /* Read the title, parse the number of channels and skip to pos 2048(0x800) */ + avio_rl32(s->pb); // magic + ret = ffio_read_size(s->pb, title, sizeof(title) - 1); + if (ret < 0) + return ret; + title[sizeof(title) - 1] = '\0'; + if (title[0] != '\0') + av_dict_set(&st->metadata, "title", title, 0); + avio_rl32(s->pb); // Block count channels = avio_r8(s->pb); avio_skip(s->pb, 1783); @@ -75,7 +82,7 @@ static int aea_read_header(AVFormatContext *s) st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; st->codecpar->codec_id = AV_CODEC_ID_ATRAC1; st->codecpar->sample_rate = 44100; - st->codecpar->bit_rate = 292000; + st->codecpar->bit_rate = 146000 * channels; if (channels != 1 && channels != 2) { av_log(s, AV_LOG_ERROR, "Channels %d not supported!\n", channels); @@ -85,27 +92,22 @@ static int aea_read_header(AVFormatContext *s) av_channel_layout_default(&st->codecpar->ch_layout, channels); st->codecpar->block_align = AT1_SU_SIZE * st->codecpar->ch_layout.nb_channels; + avpriv_set_pts_info(st, 64, 1, 44100); return 0; } static int aea_read_packet(AVFormatContext *s, AVPacket *pkt) { - int ret = av_get_packet(s->pb, pkt, s->streams[0]->codecpar->block_align); - - pkt->stream_index = 0; - if (ret <= 0) - return AVERROR(EIO); - - return ret; + return av_get_packet(s->pb, pkt, s->streams[0]->codecpar->block_align); } -const AVInputFormat ff_aea_demuxer = { - .name = "aea", - .long_name = NULL_IF_CONFIG_SMALL("MD STUDIO audio"), +const FFInputFormat ff_aea_demuxer = { + .p.name = "aea", + .p.long_name = NULL_IF_CONFIG_SMALL("MD STUDIO audio"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "aea", .read_probe = aea_read_probe, .read_header = aea_read_header, .read_packet = aea_read_packet, .read_seek = ff_pcm_read_seek, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "aea", }; diff --git a/libavformat/aeaenc.c b/libavformat/aeaenc.c new file mode 100644 index 00000000000..f7969526f4a --- /dev/null +++ b/libavformat/aeaenc.c @@ -0,0 +1,108 @@ +/* + * MD STUDIO audio muxer + * + * Copyright (c) 2024 asivery + * + * 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 "avformat.h" +#include "avio_internal.h" +#include "rawenc.h" +#include "mux.h" + +static int aea_write_header(AVFormatContext *s) +{ + const AVDictionaryEntry *title_entry; + size_t title_length = 0; + AVStream *st = s->streams[0]; + + if (st->codecpar->ch_layout.nb_channels != 1 && st->codecpar->ch_layout.nb_channels != 2) { + av_log(s, AV_LOG_ERROR, "Only maximum 2 channels are supported in the audio" + " stream, %d channels were found.\n", st->codecpar->ch_layout.nb_channels); + return AVERROR(EINVAL); + } + + if (st->codecpar->sample_rate != 44100) { + av_log(s, AV_LOG_ERROR, "Invalid sample rate (%d) AEA only supports 44.1kHz.\n", st->codecpar->sample_rate); + return AVERROR(EINVAL); + } + + /* Write magic */ + avio_wl32(s->pb, 2048); + + /* Write AEA title */ + title_entry = av_dict_get(st->metadata, "title", NULL, 0); + if (title_entry) { + const char *title_contents = title_entry->value; + title_length = strlen(title_contents); + if (title_length > 256) { + av_log(s, AV_LOG_WARNING, "Title too long, truncated to 256 bytes.\n"); + title_length = 256; + } + avio_write(s->pb, title_contents, title_length); + } + + ffio_fill(s->pb, 0, 256 - title_length); + + /* Write number of frames (zero at header-writing time, will seek later), number of channels */ + avio_wl32(s->pb, 0); + avio_w8(s->pb, st->codecpar->ch_layout.nb_channels); + avio_w8(s->pb, 0); + + /* Pad the header to 2048 bytes */ + ffio_fill(s->pb, 0, 1782); + + return 0; +} + +static int aea_write_trailer(struct AVFormatContext *s) +{ + int64_t total_blocks; + AVIOContext *pb = s->pb; + AVStream *st = s->streams[0]; + if (pb->seekable & AVIO_SEEKABLE_NORMAL) { + /* Seek to rewrite the block count. */ + avio_seek(pb, 260, SEEK_SET); + total_blocks = st->nb_frames * st->codecpar->ch_layout.nb_channels; + if (total_blocks > UINT32_MAX) { + av_log(s, AV_LOG_WARNING, "Too many frames in the file to properly encode the header (%"PRId64")." + " Block count in the header will be truncated.\n", total_blocks); + total_blocks = UINT32_MAX; + } + avio_wl32(pb, total_blocks); + } else { + av_log(s, AV_LOG_WARNING, "Unable to rewrite AEA header.\n"); + } + + return 0; +} + +const FFOutputFormat ff_aea_muxer = { + .p.name = "aea", + .p.long_name = NULL_IF_CONFIG_SMALL("MD STUDIO audio"), + .p.extensions = "aea", + .p.audio_codec = AV_CODEC_ID_ATRAC1, + .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, + + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, + .write_header = aea_write_header, + .write_packet = ff_raw_write_packet, + .write_trailer = aea_write_trailer, +}; diff --git a/libavformat/afc.c b/libavformat/afc.c index 898c7d03eb7..3113554c0f5 100644 --- a/libavformat/afc.c +++ b/libavformat/afc.c @@ -21,6 +21,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct AFCDemuxContext { @@ -68,12 +69,12 @@ static int afc_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_afc_demuxer = { - .name = "afc", - .long_name = NULL_IF_CONFIG_SMALL("AFC"), +const FFInputFormat ff_afc_demuxer = { + .p.name = "afc", + .p.long_name = NULL_IF_CONFIG_SMALL("AFC"), + .p.extensions = "afc", + .p.flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK, .priv_data_size = sizeof(AFCDemuxContext), .read_header = afc_read_header, .read_packet = afc_read_packet, - .extensions = "afc", - .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK, }; diff --git a/libavformat/aiffdec.c b/libavformat/aiffdec.c index 1cde12c1931..fc01ffcbf15 100644 --- a/libavformat/aiffdec.c +++ b/libavformat/aiffdec.c @@ -106,6 +106,8 @@ static int get_aiff_header(AVFormatContext *s, int64_t size, size++; par->codec_type = AVMEDIA_TYPE_AUDIO; channels = avio_rb16(pb); + if (par->ch_layout.nb_channels && par->ch_layout.nb_channels != channels) + return AVERROR_INVALIDDATA; par->ch_layout.nb_channels = channels; num_frames = avio_rb32(pb); par->bits_per_coded_sample = avio_rb16(pb); @@ -202,8 +204,8 @@ static int get_aiff_header(AVFormatContext *s, int64_t size, static int aiff_probe(const AVProbeData *p) { /* check file header */ - if (p->buf[0] == 'F' && p->buf[1] == 'O' && - p->buf[2] == 'R' && p->buf[3] == 'M' && + if (AV_RL32(p->buf) == MKTAG('F', 'O', 'R', 'M') && + AV_RB32(p->buf + 4) >= 4 && p->buf[8] == 'A' && p->buf[9] == 'I' && p->buf[10] == 'F' && (p->buf[11] == 'F' || p->buf[11] == 'C')) return AVPROBE_SCORE_MAX; @@ -433,13 +435,13 @@ static int aiff_read_packet(AVFormatContext *s, return 0; } -const AVInputFormat ff_aiff_demuxer = { - .name = "aiff", - .long_name = NULL_IF_CONFIG_SMALL("Audio IFF"), +const FFInputFormat ff_aiff_demuxer = { + .p.name = "aiff", + .p.long_name = NULL_IF_CONFIG_SMALL("Audio IFF"), + .p.codec_tag = ff_aiff_codec_tags_list, .priv_data_size = sizeof(AIFFInputContext), .read_probe = aiff_probe, .read_header = aiff_read_header, .read_packet = aiff_read_packet, .read_seek = ff_pcm_read_seek, - .codec_tag = ff_aiff_codec_tags_list, }; diff --git a/libavformat/aiffenc.c b/libavformat/aiffenc.c index 11a5b18d573..2cd11194095 100644 --- a/libavformat/aiffenc.c +++ b/libavformat/aiffenc.c @@ -54,7 +54,7 @@ static int put_id3v2_tags(AVFormatContext *s, AIFFOutputContext *aiff) if (!s->metadata && !s->nb_chapters && !list_entry) return 0; - avio_wl32(pb, MKTAG('I', 'D', '3', ' ')); + avio_wb32(pb, MKBETAG('I', 'D', '3', ' ')); avio_wb32(pb, 0); pos = avio_tell(pb); @@ -87,13 +87,15 @@ static void put_meta(AVFormatContext *s, const char *key, uint32_t id) AVIOContext *pb = s->pb; if (tag = av_dict_get(s->metadata, key, NULL, 0)) { - int size = strlen(tag->value); + size_t size = strlen(tag->value); - avio_wl32(pb, id); - avio_wb32(pb, FFALIGN(size, 2)); + // AIFF tags are zero-padded to an even length. + // So simply copy the terminating \0 if the length is odd. + size = FFALIGN(size, 2); + + avio_wb32(pb, id); + avio_wb32(pb, size); avio_write(pb, tag->value, size); - if (size & 1) - avio_w8(pb, 0); } } @@ -151,10 +153,10 @@ static int aiff_write_header(AVFormatContext *s) ff_mov_write_chan(pb, par->ch_layout.u.mask); } - put_meta(s, "title", MKTAG('N', 'A', 'M', 'E')); - put_meta(s, "author", MKTAG('A', 'U', 'T', 'H')); - put_meta(s, "copyright", MKTAG('(', 'c', ')', ' ')); - put_meta(s, "comment", MKTAG('A', 'N', 'N', 'O')); + put_meta(s, "title", MKBETAG('N', 'A', 'M', 'E')); + put_meta(s, "author", MKBETAG('A', 'U', 'T', 'H')); + put_meta(s, "copyright", MKBETAG('(', 'c', ')', ' ')); + put_meta(s, "comment", MKBETAG('A', 'N', 'N', 'O')); /* Common chunk */ ffio_wfourcc(pb, "COMM"); diff --git a/libavformat/aixdec.c b/libavformat/aixdec.c index f7d8e17acda..edecbac5b39 100644 --- a/libavformat/aixdec.c +++ b/libavformat/aixdec.c @@ -130,12 +130,12 @@ static int aix_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_aix_demuxer = { - .name = "aix", - .long_name = NULL_IF_CONFIG_SMALL("CRI AIX"), +const FFInputFormat ff_aix_demuxer = { + .p.name = "aix", + .p.long_name = NULL_IF_CONFIG_SMALL("CRI AIX"), + .p.extensions= "aix", + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = aix_probe, .read_header = aix_read_header, .read_packet = aix_read_packet, - .extensions = "aix", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/allformats.c b/libavformat/allformats.c index cb5b69e9cd6..e15d0fa6d72 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -25,261 +25,270 @@ #include "libavformat/internal.h" #include "avformat.h" +#include "demux.h" #include "mux.h" /* (de)muxers */ extern const FFOutputFormat ff_a64_muxer; -extern const AVInputFormat ff_aa_demuxer; -extern const AVInputFormat ff_aac_demuxer; -extern const AVInputFormat ff_aax_demuxer; -extern const AVInputFormat ff_ac3_demuxer; +extern const FFInputFormat ff_aa_demuxer; +extern const FFInputFormat ff_aac_demuxer; +extern const FFInputFormat ff_aax_demuxer; +extern const FFInputFormat ff_ac3_demuxer; extern const FFOutputFormat ff_ac3_muxer; -extern const AVInputFormat ff_ace_demuxer; -extern const AVInputFormat ff_acm_demuxer; -extern const AVInputFormat ff_act_demuxer; -extern const AVInputFormat ff_adf_demuxer; -extern const AVInputFormat ff_adp_demuxer; -extern const AVInputFormat ff_ads_demuxer; +extern const FFInputFormat ff_ac4_demuxer; +extern const FFOutputFormat ff_ac4_muxer; +extern const FFInputFormat ff_ace_demuxer; +extern const FFInputFormat ff_acm_demuxer; +extern const FFInputFormat ff_act_demuxer; +extern const FFInputFormat ff_adf_demuxer; +extern const FFInputFormat ff_adp_demuxer; +extern const FFInputFormat ff_ads_demuxer; extern const FFOutputFormat ff_adts_muxer; -extern const AVInputFormat ff_adx_demuxer; +extern const FFInputFormat ff_adx_demuxer; extern const FFOutputFormat ff_adx_muxer; -extern const AVInputFormat ff_aea_demuxer; -extern const AVInputFormat ff_afc_demuxer; -extern const AVInputFormat ff_aiff_demuxer; +extern const FFInputFormat ff_aea_demuxer; +extern const FFOutputFormat ff_aea_muxer; +extern const FFInputFormat ff_afc_demuxer; +extern const FFInputFormat ff_aiff_demuxer; extern const FFOutputFormat ff_aiff_muxer; -extern const AVInputFormat ff_aix_demuxer; -extern const AVInputFormat ff_alp_demuxer; +extern const FFInputFormat ff_aix_demuxer; +extern const FFInputFormat ff_alp_demuxer; extern const FFOutputFormat ff_alp_muxer; -extern const AVInputFormat ff_amr_demuxer; +extern const FFInputFormat ff_amr_demuxer; extern const FFOutputFormat ff_amr_muxer; -extern const AVInputFormat ff_amrnb_demuxer; -extern const AVInputFormat ff_amrwb_demuxer; +extern const FFInputFormat ff_amrnb_demuxer; +extern const FFInputFormat ff_amrwb_demuxer; extern const FFOutputFormat ff_amv_muxer; -extern const AVInputFormat ff_anm_demuxer; -extern const AVInputFormat ff_apac_demuxer; -extern const AVInputFormat ff_apc_demuxer; -extern const AVInputFormat ff_ape_demuxer; -extern const AVInputFormat ff_apm_demuxer; +extern const FFInputFormat ff_anm_demuxer; +extern const FFInputFormat ff_apac_demuxer; +extern const FFInputFormat ff_apc_demuxer; +extern const FFInputFormat ff_ape_demuxer; +extern const FFInputFormat ff_apm_demuxer; extern const FFOutputFormat ff_apm_muxer; -extern const AVInputFormat ff_apng_demuxer; +extern const FFInputFormat ff_apng_demuxer; extern const FFOutputFormat ff_apng_muxer; -extern const AVInputFormat ff_aptx_demuxer; +extern const FFInputFormat ff_aptx_demuxer; extern const FFOutputFormat ff_aptx_muxer; -extern const AVInputFormat ff_aptx_hd_demuxer; +extern const FFInputFormat ff_aptx_hd_demuxer; extern const FFOutputFormat ff_aptx_hd_muxer; -extern const AVInputFormat ff_aqtitle_demuxer; -extern const AVInputFormat ff_argo_asf_demuxer; +extern const FFInputFormat ff_aqtitle_demuxer; +extern const FFInputFormat ff_argo_asf_demuxer; extern const FFOutputFormat ff_argo_asf_muxer; -extern const AVInputFormat ff_argo_brp_demuxer; -extern const AVInputFormat ff_argo_cvg_demuxer; +extern const FFInputFormat ff_argo_brp_demuxer; +extern const FFInputFormat ff_argo_cvg_demuxer; extern const FFOutputFormat ff_argo_cvg_muxer; -extern const AVInputFormat ff_asf_demuxer; +extern const FFInputFormat ff_asf_demuxer; extern const FFOutputFormat ff_asf_muxer; -extern const AVInputFormat ff_asf_o_demuxer; -extern const AVInputFormat ff_ass_demuxer; +extern const FFInputFormat ff_asf_o_demuxer; +extern const FFInputFormat ff_ass_demuxer; extern const FFOutputFormat ff_ass_muxer; -extern const AVInputFormat ff_ast_demuxer; +extern const FFInputFormat ff_ast_demuxer; extern const FFOutputFormat ff_ast_muxer; extern const FFOutputFormat ff_asf_stream_muxer; -extern const AVInputFormat ff_au_demuxer; +extern const FFInputFormat ff_au_demuxer; extern const FFOutputFormat ff_au_muxer; -extern const AVInputFormat ff_av1_demuxer; -extern const AVInputFormat ff_avi_demuxer; +extern const FFInputFormat ff_av1_demuxer; +extern const FFInputFormat ff_avi_demuxer; extern const FFOutputFormat ff_avi_muxer; extern const FFOutputFormat ff_avif_muxer; -extern const AVInputFormat ff_avisynth_demuxer; +extern const FFInputFormat ff_avisynth_demuxer; extern const FFOutputFormat ff_avm2_muxer; -extern const AVInputFormat ff_avr_demuxer; -extern const AVInputFormat ff_avs_demuxer; -extern const AVInputFormat ff_avs2_demuxer; +extern const FFInputFormat ff_avr_demuxer; +extern const FFInputFormat ff_avs_demuxer; +extern const FFInputFormat ff_avs2_demuxer; extern const FFOutputFormat ff_avs2_muxer; -extern const AVInputFormat ff_avs3_demuxer; +extern const FFInputFormat ff_avs3_demuxer; extern const FFOutputFormat ff_avs3_muxer; -extern const AVInputFormat ff_bethsoftvid_demuxer; -extern const AVInputFormat ff_bfi_demuxer; -extern const AVInputFormat ff_bintext_demuxer; -extern const AVInputFormat ff_bink_demuxer; -extern const AVInputFormat ff_binka_demuxer; -extern const AVInputFormat ff_bit_demuxer; +extern const FFInputFormat ff_bethsoftvid_demuxer; +extern const FFInputFormat ff_bfi_demuxer; +extern const FFInputFormat ff_bintext_demuxer; +extern const FFInputFormat ff_bink_demuxer; +extern const FFInputFormat ff_binka_demuxer; +extern const FFInputFormat ff_bit_demuxer; extern const FFOutputFormat ff_bit_muxer; -extern const AVInputFormat ff_bitpacked_demuxer; -extern const AVInputFormat ff_bmv_demuxer; -extern const AVInputFormat ff_bfstm_demuxer; -extern const AVInputFormat ff_brstm_demuxer; -extern const AVInputFormat ff_boa_demuxer; -extern const AVInputFormat ff_bonk_demuxer; -extern const AVInputFormat ff_c93_demuxer; -extern const AVInputFormat ff_caf_demuxer; +extern const FFInputFormat ff_bitpacked_demuxer; +extern const FFInputFormat ff_bmv_demuxer; +extern const FFInputFormat ff_bfstm_demuxer; +extern const FFInputFormat ff_brstm_demuxer; +extern const FFInputFormat ff_boa_demuxer; +extern const FFInputFormat ff_bonk_demuxer; +extern const FFInputFormat ff_c93_demuxer; +extern const FFInputFormat ff_caf_demuxer; extern const FFOutputFormat ff_caf_muxer; -extern const AVInputFormat ff_cavsvideo_demuxer; +extern const FFInputFormat ff_cavsvideo_demuxer; extern const FFOutputFormat ff_cavsvideo_muxer; -extern const AVInputFormat ff_cdg_demuxer; -extern const AVInputFormat ff_cdxl_demuxer; -extern const AVInputFormat ff_cine_demuxer; -extern const AVInputFormat ff_codec2_demuxer; +extern const FFInputFormat ff_cdg_demuxer; +extern const FFInputFormat ff_cdxl_demuxer; +extern const FFInputFormat ff_cine_demuxer; +extern const FFInputFormat ff_codec2_demuxer; extern const FFOutputFormat ff_codec2_muxer; -extern const AVInputFormat ff_codec2raw_demuxer; +extern const FFInputFormat ff_codec2raw_demuxer; extern const FFOutputFormat ff_codec2raw_muxer; -extern const AVInputFormat ff_concat_demuxer; +extern const FFInputFormat ff_concat_demuxer; extern const FFOutputFormat ff_crc_muxer; -extern const AVInputFormat ff_dash_demuxer; +extern const FFInputFormat ff_dash_demuxer; extern const FFOutputFormat ff_dash_muxer; -extern const AVInputFormat ff_data_demuxer; +extern const FFInputFormat ff_data_demuxer; extern const FFOutputFormat ff_data_muxer; -extern const AVInputFormat ff_daud_demuxer; +extern const FFInputFormat ff_daud_demuxer; extern const FFOutputFormat ff_daud_muxer; -extern const AVInputFormat ff_dcstr_demuxer; -extern const AVInputFormat ff_derf_demuxer; -extern const AVInputFormat ff_dfa_demuxer; -extern const AVInputFormat ff_dfpwm_demuxer; +extern const FFInputFormat ff_dcstr_demuxer; +extern const FFInputFormat ff_derf_demuxer; +extern const FFInputFormat ff_dfa_demuxer; +extern const FFInputFormat ff_dfpwm_demuxer; extern const FFOutputFormat ff_dfpwm_muxer; -extern const AVInputFormat ff_dhav_demuxer; -extern const AVInputFormat ff_dirac_demuxer; +extern const FFInputFormat ff_dhav_demuxer; +extern const FFInputFormat ff_dirac_demuxer; extern const FFOutputFormat ff_dirac_muxer; -extern const AVInputFormat ff_dnxhd_demuxer; +extern const FFInputFormat ff_dnxhd_demuxer; extern const FFOutputFormat ff_dnxhd_muxer; -extern const AVInputFormat ff_dsf_demuxer; -extern const AVInputFormat ff_dsicin_demuxer; -extern const AVInputFormat ff_dss_demuxer; -extern const AVInputFormat ff_dts_demuxer; +extern const FFInputFormat ff_dsf_demuxer; +extern const FFInputFormat ff_dsicin_demuxer; +extern const FFInputFormat ff_dss_demuxer; +extern const FFInputFormat ff_dts_demuxer; extern const FFOutputFormat ff_dts_muxer; -extern const AVInputFormat ff_dtshd_demuxer; -extern const AVInputFormat ff_dv_demuxer; +extern const FFInputFormat ff_dtshd_demuxer; +extern const FFInputFormat ff_dv_demuxer; extern const FFOutputFormat ff_dv_muxer; -extern const AVInputFormat ff_dvbsub_demuxer; -extern const AVInputFormat ff_dvbtxt_demuxer; -extern const AVInputFormat ff_dxa_demuxer; -extern const AVInputFormat ff_ea_demuxer; -extern const AVInputFormat ff_ea_cdata_demuxer; -extern const AVInputFormat ff_eac3_demuxer; +extern const FFInputFormat ff_dvbsub_demuxer; +extern const FFInputFormat ff_dvbtxt_demuxer; +extern const FFInputFormat ff_dvdvideo_demuxer; +extern const FFInputFormat ff_dxa_demuxer; +extern const FFInputFormat ff_ea_demuxer; +extern const FFInputFormat ff_ea_cdata_demuxer; +extern const FFInputFormat ff_eac3_demuxer; extern const FFOutputFormat ff_eac3_muxer; -extern const AVInputFormat ff_epaf_demuxer; +extern const FFInputFormat ff_epaf_demuxer; +extern const FFInputFormat ff_evc_demuxer; +extern const FFOutputFormat ff_evc_muxer; extern const FFOutputFormat ff_f4v_muxer; -extern const AVInputFormat ff_ffmetadata_demuxer; +extern const FFInputFormat ff_ffmetadata_demuxer; extern const FFOutputFormat ff_ffmetadata_muxer; extern const FFOutputFormat ff_fifo_muxer; -extern const FFOutputFormat ff_fifo_test_muxer; -extern const AVInputFormat ff_filmstrip_demuxer; +extern const FFInputFormat ff_filmstrip_demuxer; extern const FFOutputFormat ff_filmstrip_muxer; -extern const AVInputFormat ff_fits_demuxer; +extern const FFInputFormat ff_fits_demuxer; extern const FFOutputFormat ff_fits_muxer; -extern const AVInputFormat ff_flac_demuxer; +extern const FFInputFormat ff_flac_demuxer; extern const FFOutputFormat ff_flac_muxer; -extern const AVInputFormat ff_flic_demuxer; -extern const AVInputFormat ff_flv_demuxer; +extern const FFInputFormat ff_flic_demuxer; +extern const FFInputFormat ff_flv_demuxer; extern const FFOutputFormat ff_flv_muxer; -extern const AVInputFormat ff_live_flv_demuxer; -extern const AVInputFormat ff_fourxm_demuxer; +extern const FFInputFormat ff_live_flv_demuxer; +extern const FFInputFormat ff_fourxm_demuxer; extern const FFOutputFormat ff_framecrc_muxer; extern const FFOutputFormat ff_framehash_muxer; extern const FFOutputFormat ff_framemd5_muxer; -extern const AVInputFormat ff_frm_demuxer; -extern const AVInputFormat ff_fsb_demuxer; -extern const AVInputFormat ff_fwse_demuxer; -extern const AVInputFormat ff_g722_demuxer; +extern const FFInputFormat ff_frm_demuxer; +extern const FFInputFormat ff_fsb_demuxer; +extern const FFInputFormat ff_fwse_demuxer; +extern const FFInputFormat ff_g722_demuxer; extern const FFOutputFormat ff_g722_muxer; -extern const AVInputFormat ff_g723_1_demuxer; +extern const FFInputFormat ff_g723_1_demuxer; extern const FFOutputFormat ff_g723_1_muxer; -extern const AVInputFormat ff_g726_demuxer; +extern const FFInputFormat ff_g726_demuxer; extern const FFOutputFormat ff_g726_muxer; -extern const AVInputFormat ff_g726le_demuxer; +extern const FFInputFormat ff_g726le_demuxer; extern const FFOutputFormat ff_g726le_muxer; -extern const AVInputFormat ff_g729_demuxer; -extern const AVInputFormat ff_gdv_demuxer; -extern const AVInputFormat ff_genh_demuxer; -extern const AVInputFormat ff_gif_demuxer; +extern const FFInputFormat ff_g729_demuxer; +extern const FFInputFormat ff_gdv_demuxer; +extern const FFInputFormat ff_genh_demuxer; +extern const FFInputFormat ff_gif_demuxer; extern const FFOutputFormat ff_gif_muxer; -extern const AVInputFormat ff_gsm_demuxer; +extern const FFInputFormat ff_gsm_demuxer; extern const FFOutputFormat ff_gsm_muxer; -extern const AVInputFormat ff_gxf_demuxer; +extern const FFInputFormat ff_gxf_demuxer; extern const FFOutputFormat ff_gxf_muxer; -extern const AVInputFormat ff_h261_demuxer; +extern const FFInputFormat ff_h261_demuxer; extern const FFOutputFormat ff_h261_muxer; -extern const AVInputFormat ff_h263_demuxer; +extern const FFInputFormat ff_h263_demuxer; extern const FFOutputFormat ff_h263_muxer; -extern const AVInputFormat ff_h264_demuxer; +extern const FFInputFormat ff_h264_demuxer; extern const FFOutputFormat ff_h264_muxer; extern const FFOutputFormat ff_hash_muxer; -extern const AVInputFormat ff_hca_demuxer; -extern const AVInputFormat ff_hcom_demuxer; +extern const FFInputFormat ff_hca_demuxer; +extern const FFInputFormat ff_hcom_demuxer; extern const FFOutputFormat ff_hds_muxer; -extern const AVInputFormat ff_hevc_demuxer; +extern const FFInputFormat ff_hevc_demuxer; extern const FFOutputFormat ff_hevc_muxer; -extern const AVInputFormat ff_hls_demuxer; +extern const FFInputFormat ff_hls_demuxer; extern const FFOutputFormat ff_hls_muxer; -extern const AVInputFormat ff_hnm_demuxer; -extern const AVInputFormat ff_ico_demuxer; +extern const FFInputFormat ff_hnm_demuxer; +extern const FFInputFormat ff_iamf_demuxer; +extern const FFOutputFormat ff_iamf_muxer; +extern const FFInputFormat ff_ico_demuxer; extern const FFOutputFormat ff_ico_muxer; -extern const AVInputFormat ff_idcin_demuxer; -extern const AVInputFormat ff_idf_demuxer; -extern const AVInputFormat ff_iff_demuxer; -extern const AVInputFormat ff_ifv_demuxer; -extern const AVInputFormat ff_ilbc_demuxer; +extern const FFInputFormat ff_idcin_demuxer; +extern const FFInputFormat ff_idf_demuxer; +extern const FFInputFormat ff_iff_demuxer; +extern const FFInputFormat ff_ifv_demuxer; +extern const FFInputFormat ff_ilbc_demuxer; extern const FFOutputFormat ff_ilbc_muxer; -extern const AVInputFormat ff_image2_demuxer; +extern const FFInputFormat ff_image2_demuxer; extern const FFOutputFormat ff_image2_muxer; -extern const AVInputFormat ff_image2pipe_demuxer; +extern const FFInputFormat ff_image2pipe_demuxer; extern const FFOutputFormat ff_image2pipe_muxer; -extern const AVInputFormat ff_image2_alias_pix_demuxer; -extern const AVInputFormat ff_image2_brender_pix_demuxer; -extern const AVInputFormat ff_imf_demuxer; -extern const AVInputFormat ff_ingenient_demuxer; -extern const AVInputFormat ff_ipmovie_demuxer; +extern const FFInputFormat ff_image2_alias_pix_demuxer; +extern const FFInputFormat ff_image2_brender_pix_demuxer; +extern const FFInputFormat ff_imf_demuxer; +extern const FFInputFormat ff_ingenient_demuxer; +extern const FFInputFormat ff_ipmovie_demuxer; extern const FFOutputFormat ff_ipod_muxer; -extern const AVInputFormat ff_ipu_demuxer; -extern const AVInputFormat ff_ircam_demuxer; +extern const FFInputFormat ff_ipu_demuxer; +extern const FFInputFormat ff_ircam_demuxer; extern const FFOutputFormat ff_ircam_muxer; extern const FFOutputFormat ff_ismv_muxer; -extern const AVInputFormat ff_iss_demuxer; -extern const AVInputFormat ff_iv8_demuxer; -extern const AVInputFormat ff_ivf_demuxer; +extern const FFInputFormat ff_iss_demuxer; +extern const FFInputFormat ff_iv8_demuxer; +extern const FFInputFormat ff_ivf_demuxer; extern const FFOutputFormat ff_ivf_muxer; -extern const AVInputFormat ff_ivr_demuxer; -extern const AVInputFormat ff_jacosub_demuxer; +extern const FFInputFormat ff_ivr_demuxer; +extern const FFInputFormat ff_jacosub_demuxer; extern const FFOutputFormat ff_jacosub_muxer; -extern const AVInputFormat ff_jv_demuxer; -extern const AVInputFormat ff_kux_demuxer; -extern const AVInputFormat ff_kvag_demuxer; +extern const FFInputFormat ff_jv_demuxer; +extern const FFInputFormat ff_jpegxl_anim_demuxer; +extern const FFInputFormat ff_kux_demuxer; +extern const FFInputFormat ff_kvag_demuxer; extern const FFOutputFormat ff_kvag_muxer; -extern const AVInputFormat ff_laf_demuxer; +extern const FFInputFormat ff_laf_demuxer; extern const FFOutputFormat ff_latm_muxer; -extern const AVInputFormat ff_lmlm4_demuxer; -extern const AVInputFormat ff_loas_demuxer; -extern const AVInputFormat ff_luodat_demuxer; -extern const AVInputFormat ff_lrc_demuxer; +extern const FFInputFormat ff_lmlm4_demuxer; +extern const FFInputFormat ff_loas_demuxer; +extern const FFInputFormat ff_luodat_demuxer; +extern const FFInputFormat ff_lrc_demuxer; extern const FFOutputFormat ff_lrc_muxer; -extern const AVInputFormat ff_lvf_demuxer; -extern const AVInputFormat ff_lxf_demuxer; -extern const AVInputFormat ff_m4v_demuxer; +extern const FFInputFormat ff_lvf_demuxer; +extern const FFInputFormat ff_lxf_demuxer; +extern const FFInputFormat ff_m4v_demuxer; extern const FFOutputFormat ff_m4v_muxer; -extern const AVInputFormat ff_mca_demuxer; -extern const AVInputFormat ff_mcc_demuxer; +extern const FFInputFormat ff_mca_demuxer; +extern const FFInputFormat ff_mcc_demuxer; extern const FFOutputFormat ff_md5_muxer; -extern const AVInputFormat ff_matroska_demuxer; +extern const FFInputFormat ff_matroska_demuxer; extern const FFOutputFormat ff_matroska_muxer; extern const FFOutputFormat ff_matroska_audio_muxer; -extern const AVInputFormat ff_mgsts_demuxer; -extern const AVInputFormat ff_microdvd_demuxer; +extern const FFInputFormat ff_mgsts_demuxer; +extern const FFInputFormat ff_microdvd_demuxer; extern const FFOutputFormat ff_microdvd_muxer; -extern const AVInputFormat ff_mjpeg_demuxer; +extern const FFInputFormat ff_mjpeg_demuxer; extern const FFOutputFormat ff_mjpeg_muxer; -extern const AVInputFormat ff_mjpeg_2000_demuxer; -extern const AVInputFormat ff_mlp_demuxer; +extern const FFInputFormat ff_mjpeg_2000_demuxer; +extern const FFInputFormat ff_mlp_demuxer; extern const FFOutputFormat ff_mlp_muxer; -extern const AVInputFormat ff_mlv_demuxer; -extern const AVInputFormat ff_mm_demuxer; -extern const AVInputFormat ff_mmf_demuxer; +extern const FFInputFormat ff_mlv_demuxer; +extern const FFInputFormat ff_mm_demuxer; +extern const FFInputFormat ff_mmf_demuxer; extern const FFOutputFormat ff_mmf_muxer; -extern const AVInputFormat ff_mods_demuxer; -extern const AVInputFormat ff_moflex_demuxer; -extern const AVInputFormat ff_mov_demuxer; +extern const FFInputFormat ff_mods_demuxer; +extern const FFInputFormat ff_moflex_demuxer; +extern const FFInputFormat ff_mov_demuxer; extern const FFOutputFormat ff_mov_muxer; extern const FFOutputFormat ff_mp2_muxer; -extern const AVInputFormat ff_mp3_demuxer; +extern const FFInputFormat ff_mp3_demuxer; extern const FFOutputFormat ff_mp3_muxer; extern const FFOutputFormat ff_mp4_muxer; -extern const AVInputFormat ff_mpc_demuxer; -extern const AVInputFormat ff_mpc8_demuxer; +extern const FFInputFormat ff_mpc_demuxer; +extern const FFInputFormat ff_mpc8_demuxer; extern const FFOutputFormat ff_mpeg1system_muxer; extern const FFOutputFormat ff_mpeg1vcd_muxer; extern const FFOutputFormat ff_mpeg1video_muxer; @@ -287,275 +296,282 @@ extern const FFOutputFormat ff_mpeg2dvd_muxer; extern const FFOutputFormat ff_mpeg2svcd_muxer; extern const FFOutputFormat ff_mpeg2video_muxer; extern const FFOutputFormat ff_mpeg2vob_muxer; -extern const AVInputFormat ff_mpegps_demuxer; -extern const AVInputFormat ff_mpegts_demuxer; +extern const FFInputFormat ff_mpegps_demuxer; +extern const FFInputFormat ff_mpegts_demuxer; extern const FFOutputFormat ff_mpegts_muxer; -extern const AVInputFormat ff_mpegtsraw_demuxer; -extern const AVInputFormat ff_mpegvideo_demuxer; -extern const AVInputFormat ff_mpjpeg_demuxer; +extern const FFInputFormat ff_mpegtsraw_demuxer; +extern const FFInputFormat ff_mpegvideo_demuxer; +extern const FFInputFormat ff_mpjpeg_demuxer; extern const FFOutputFormat ff_mpjpeg_muxer; -extern const AVInputFormat ff_mpl2_demuxer; -extern const AVInputFormat ff_mpsub_demuxer; -extern const AVInputFormat ff_msf_demuxer; -extern const AVInputFormat ff_msnwc_tcp_demuxer; -extern const AVInputFormat ff_msp_demuxer; -extern const AVInputFormat ff_mtaf_demuxer; -extern const AVInputFormat ff_mtv_demuxer; -extern const AVInputFormat ff_musx_demuxer; -extern const AVInputFormat ff_mv_demuxer; -extern const AVInputFormat ff_mvi_demuxer; -extern const AVInputFormat ff_mxf_demuxer; +extern const FFInputFormat ff_mpl2_demuxer; +extern const FFInputFormat ff_mpsub_demuxer; +extern const FFInputFormat ff_msf_demuxer; +extern const FFInputFormat ff_msnwc_tcp_demuxer; +extern const FFInputFormat ff_msp_demuxer; +extern const FFInputFormat ff_mtaf_demuxer; +extern const FFInputFormat ff_mtv_demuxer; +extern const FFInputFormat ff_musx_demuxer; +extern const FFInputFormat ff_mv_demuxer; +extern const FFInputFormat ff_mvi_demuxer; +extern const FFInputFormat ff_mxf_demuxer; extern const FFOutputFormat ff_mxf_muxer; extern const FFOutputFormat ff_mxf_d10_muxer; extern const FFOutputFormat ff_mxf_opatom_muxer; -extern const AVInputFormat ff_mxg_demuxer; -extern const AVInputFormat ff_nc_demuxer; -extern const AVInputFormat ff_nistsphere_demuxer; -extern const AVInputFormat ff_nsp_demuxer; -extern const AVInputFormat ff_nsv_demuxer; +extern const FFInputFormat ff_mxg_demuxer; +extern const FFInputFormat ff_nc_demuxer; +extern const FFInputFormat ff_nistsphere_demuxer; +extern const FFInputFormat ff_nsp_demuxer; +extern const FFInputFormat ff_nsv_demuxer; extern const FFOutputFormat ff_null_muxer; -extern const AVInputFormat ff_nut_demuxer; +extern const FFInputFormat ff_nut_demuxer; extern const FFOutputFormat ff_nut_muxer; -extern const AVInputFormat ff_nuv_demuxer; -extern const AVInputFormat ff_obu_demuxer; +extern const FFInputFormat ff_nuv_demuxer; +extern const FFInputFormat ff_obu_demuxer; extern const FFOutputFormat ff_obu_muxer; extern const FFOutputFormat ff_oga_muxer; -extern const AVInputFormat ff_ogg_demuxer; +extern const FFInputFormat ff_ogg_demuxer; extern const FFOutputFormat ff_ogg_muxer; extern const FFOutputFormat ff_ogv_muxer; -extern const AVInputFormat ff_oma_demuxer; +extern const FFInputFormat ff_oma_demuxer; extern const FFOutputFormat ff_oma_muxer; extern const FFOutputFormat ff_opus_muxer; -extern const AVInputFormat ff_paf_demuxer; -extern const AVInputFormat ff_pcm_alaw_demuxer; +extern const FFInputFormat ff_osq_demuxer; +extern const FFInputFormat ff_paf_demuxer; +extern const FFInputFormat ff_pcm_alaw_demuxer; extern const FFOutputFormat ff_pcm_alaw_muxer; -extern const AVInputFormat ff_pcm_mulaw_demuxer; +extern const FFInputFormat ff_pcm_mulaw_demuxer; extern const FFOutputFormat ff_pcm_mulaw_muxer; -extern const AVInputFormat ff_pcm_vidc_demuxer; +extern const FFInputFormat ff_pcm_vidc_demuxer; extern const FFOutputFormat ff_pcm_vidc_muxer; -extern const AVInputFormat ff_pcm_f64be_demuxer; +extern const FFInputFormat ff_pcm_f64be_demuxer; extern const FFOutputFormat ff_pcm_f64be_muxer; -extern const AVInputFormat ff_pcm_f64le_demuxer; +extern const FFInputFormat ff_pcm_f64le_demuxer; extern const FFOutputFormat ff_pcm_f64le_muxer; -extern const AVInputFormat ff_pcm_f32be_demuxer; +extern const FFInputFormat ff_pcm_f32be_demuxer; extern const FFOutputFormat ff_pcm_f32be_muxer; -extern const AVInputFormat ff_pcm_f32le_demuxer; +extern const FFInputFormat ff_pcm_f32le_demuxer; extern const FFOutputFormat ff_pcm_f32le_muxer; -extern const AVInputFormat ff_pcm_s32be_demuxer; +extern const FFInputFormat ff_pcm_s32be_demuxer; extern const FFOutputFormat ff_pcm_s32be_muxer; -extern const AVInputFormat ff_pcm_s32le_demuxer; +extern const FFInputFormat ff_pcm_s32le_demuxer; extern const FFOutputFormat ff_pcm_s32le_muxer; -extern const AVInputFormat ff_pcm_s24be_demuxer; +extern const FFInputFormat ff_pcm_s24be_demuxer; extern const FFOutputFormat ff_pcm_s24be_muxer; -extern const AVInputFormat ff_pcm_s24le_demuxer; +extern const FFInputFormat ff_pcm_s24le_demuxer; extern const FFOutputFormat ff_pcm_s24le_muxer; -extern const AVInputFormat ff_pcm_s16be_demuxer; +extern const FFInputFormat ff_pcm_s16be_demuxer; extern const FFOutputFormat ff_pcm_s16be_muxer; -extern const AVInputFormat ff_pcm_s16le_demuxer; +extern const FFInputFormat ff_pcm_s16le_demuxer; extern const FFOutputFormat ff_pcm_s16le_muxer; -extern const AVInputFormat ff_pcm_s8_demuxer; +extern const FFInputFormat ff_pcm_s8_demuxer; extern const FFOutputFormat ff_pcm_s8_muxer; -extern const AVInputFormat ff_pcm_u32be_demuxer; +extern const FFInputFormat ff_pcm_u32be_demuxer; extern const FFOutputFormat ff_pcm_u32be_muxer; -extern const AVInputFormat ff_pcm_u32le_demuxer; +extern const FFInputFormat ff_pcm_u32le_demuxer; extern const FFOutputFormat ff_pcm_u32le_muxer; -extern const AVInputFormat ff_pcm_u24be_demuxer; +extern const FFInputFormat ff_pcm_u24be_demuxer; extern const FFOutputFormat ff_pcm_u24be_muxer; -extern const AVInputFormat ff_pcm_u24le_demuxer; +extern const FFInputFormat ff_pcm_u24le_demuxer; extern const FFOutputFormat ff_pcm_u24le_muxer; -extern const AVInputFormat ff_pcm_u16be_demuxer; +extern const FFInputFormat ff_pcm_u16be_demuxer; extern const FFOutputFormat ff_pcm_u16be_muxer; -extern const AVInputFormat ff_pcm_u16le_demuxer; +extern const FFInputFormat ff_pcm_u16le_demuxer; extern const FFOutputFormat ff_pcm_u16le_muxer; -extern const AVInputFormat ff_pcm_u8_demuxer; +extern const FFInputFormat ff_pcm_u8_demuxer; extern const FFOutputFormat ff_pcm_u8_muxer; -extern const AVInputFormat ff_pjs_demuxer; -extern const AVInputFormat ff_pmp_demuxer; -extern const AVInputFormat ff_pp_bnk_demuxer; +extern const FFInputFormat ff_pdv_demuxer; +extern const FFInputFormat ff_pjs_demuxer; +extern const FFInputFormat ff_pmp_demuxer; +extern const FFInputFormat ff_pp_bnk_demuxer; extern const FFOutputFormat ff_psp_muxer; -extern const AVInputFormat ff_pva_demuxer; -extern const AVInputFormat ff_pvf_demuxer; -extern const AVInputFormat ff_qcp_demuxer; -extern const AVInputFormat ff_r3d_demuxer; -extern const AVInputFormat ff_rawvideo_demuxer; +extern const FFInputFormat ff_pva_demuxer; +extern const FFInputFormat ff_pvf_demuxer; +extern const FFInputFormat ff_qcp_demuxer; +extern const FFInputFormat ff_qoa_demuxer; +extern const FFInputFormat ff_r3d_demuxer; +extern const FFInputFormat ff_rawvideo_demuxer; extern const FFOutputFormat ff_rawvideo_muxer; -extern const AVInputFormat ff_realtext_demuxer; -extern const AVInputFormat ff_redspark_demuxer; -extern const AVInputFormat ff_rka_demuxer; -extern const AVInputFormat ff_rl2_demuxer; -extern const AVInputFormat ff_rm_demuxer; +extern const FFOutputFormat ff_rcwt_muxer; +extern const FFInputFormat ff_realtext_demuxer; +extern const FFInputFormat ff_redspark_demuxer; +extern const FFInputFormat ff_rka_demuxer; +extern const FFInputFormat ff_rl2_demuxer; +extern const FFInputFormat ff_rm_demuxer; extern const FFOutputFormat ff_rm_muxer; -extern const AVInputFormat ff_roq_demuxer; +extern const FFInputFormat ff_roq_demuxer; extern const FFOutputFormat ff_roq_muxer; -extern const AVInputFormat ff_rpl_demuxer; -extern const AVInputFormat ff_rsd_demuxer; -extern const AVInputFormat ff_rso_demuxer; +extern const FFInputFormat ff_rpl_demuxer; +extern const FFInputFormat ff_rsd_demuxer; +extern const FFInputFormat ff_rso_demuxer; extern const FFOutputFormat ff_rso_muxer; -extern const AVInputFormat ff_rtp_demuxer; +extern const FFInputFormat ff_rtp_demuxer; extern const FFOutputFormat ff_rtp_muxer; extern const FFOutputFormat ff_rtp_mpegts_muxer; -extern const AVInputFormat ff_rtsp_demuxer; +extern const FFInputFormat ff_rtsp_demuxer; extern const FFOutputFormat ff_rtsp_muxer; -extern const AVInputFormat ff_s337m_demuxer; -extern const AVInputFormat ff_sami_demuxer; -extern const AVInputFormat ff_sap_demuxer; +extern const FFInputFormat ff_s337m_demuxer; +extern const FFInputFormat ff_sami_demuxer; +extern const FFInputFormat ff_sap_demuxer; extern const FFOutputFormat ff_sap_muxer; -extern const AVInputFormat ff_sbc_demuxer; +extern const FFInputFormat ff_sbc_demuxer; extern const FFOutputFormat ff_sbc_muxer; -extern const AVInputFormat ff_sbg_demuxer; -extern const AVInputFormat ff_scc_demuxer; +extern const FFInputFormat ff_sbg_demuxer; +extern const FFInputFormat ff_scc_demuxer; extern const FFOutputFormat ff_scc_muxer; -extern const AVInputFormat ff_scd_demuxer; -extern const AVInputFormat ff_sdns_demuxer; -extern const AVInputFormat ff_sdp_demuxer; -extern const AVInputFormat ff_sdr2_demuxer; -extern const AVInputFormat ff_sds_demuxer; -extern const AVInputFormat ff_sdx_demuxer; -extern const AVInputFormat ff_segafilm_demuxer; +extern const FFInputFormat ff_scd_demuxer; +extern const FFInputFormat ff_sdns_demuxer; +extern const FFInputFormat ff_sdp_demuxer; +extern const FFInputFormat ff_sdr2_demuxer; +extern const FFInputFormat ff_sds_demuxer; +extern const FFInputFormat ff_sdx_demuxer; +extern const FFInputFormat ff_segafilm_demuxer; extern const FFOutputFormat ff_segafilm_muxer; extern const FFOutputFormat ff_segment_muxer; extern const FFOutputFormat ff_stream_segment_muxer; -extern const AVInputFormat ff_ser_demuxer; -extern const AVInputFormat ff_sga_demuxer; -extern const AVInputFormat ff_shorten_demuxer; -extern const AVInputFormat ff_siff_demuxer; -extern const AVInputFormat ff_simbiosis_imx_demuxer; -extern const AVInputFormat ff_sln_demuxer; -extern const AVInputFormat ff_smacker_demuxer; -extern const AVInputFormat ff_smjpeg_demuxer; +extern const FFInputFormat ff_ser_demuxer; +extern const FFInputFormat ff_sga_demuxer; +extern const FFInputFormat ff_shorten_demuxer; +extern const FFInputFormat ff_siff_demuxer; +extern const FFInputFormat ff_simbiosis_imx_demuxer; +extern const FFInputFormat ff_sln_demuxer; +extern const FFInputFormat ff_smacker_demuxer; +extern const FFInputFormat ff_smjpeg_demuxer; extern const FFOutputFormat ff_smjpeg_muxer; extern const FFOutputFormat ff_smoothstreaming_muxer; -extern const AVInputFormat ff_smush_demuxer; -extern const AVInputFormat ff_sol_demuxer; -extern const AVInputFormat ff_sox_demuxer; +extern const FFInputFormat ff_smush_demuxer; +extern const FFInputFormat ff_sol_demuxer; +extern const FFInputFormat ff_sox_demuxer; extern const FFOutputFormat ff_sox_muxer; extern const FFOutputFormat ff_spx_muxer; -extern const AVInputFormat ff_spdif_demuxer; +extern const FFInputFormat ff_spdif_demuxer; extern const FFOutputFormat ff_spdif_muxer; -extern const AVInputFormat ff_srt_demuxer; +extern const FFInputFormat ff_srt_demuxer; extern const FFOutputFormat ff_srt_muxer; -extern const AVInputFormat ff_str_demuxer; -extern const AVInputFormat ff_stl_demuxer; +extern const FFInputFormat ff_str_demuxer; +extern const FFInputFormat ff_stl_demuxer; extern const FFOutputFormat ff_streamhash_muxer; -extern const AVInputFormat ff_subviewer1_demuxer; -extern const AVInputFormat ff_subviewer_demuxer; -extern const AVInputFormat ff_sup_demuxer; +extern const FFInputFormat ff_subviewer1_demuxer; +extern const FFInputFormat ff_subviewer_demuxer; +extern const FFInputFormat ff_sup_demuxer; extern const FFOutputFormat ff_sup_muxer; -extern const AVInputFormat ff_svag_demuxer; -extern const AVInputFormat ff_svs_demuxer; -extern const AVInputFormat ff_swf_demuxer; +extern const FFInputFormat ff_svag_demuxer; +extern const FFInputFormat ff_svs_demuxer; +extern const FFInputFormat ff_swf_demuxer; extern const FFOutputFormat ff_swf_muxer; -extern const AVInputFormat ff_tak_demuxer; +extern const FFInputFormat ff_tak_demuxer; extern const FFOutputFormat ff_tee_muxer; -extern const AVInputFormat ff_tedcaptions_demuxer; +extern const FFInputFormat ff_tedcaptions_demuxer; extern const FFOutputFormat ff_tg2_muxer; extern const FFOutputFormat ff_tgp_muxer; -extern const AVInputFormat ff_thp_demuxer; -extern const AVInputFormat ff_threedostr_demuxer; -extern const AVInputFormat ff_tiertexseq_demuxer; +extern const FFInputFormat ff_thp_demuxer; +extern const FFInputFormat ff_threedostr_demuxer; +extern const FFInputFormat ff_tiertexseq_demuxer; extern const FFOutputFormat ff_mkvtimestamp_v2_muxer; -extern const AVInputFormat ff_tmv_demuxer; -extern const AVInputFormat ff_truehd_demuxer; +extern const FFInputFormat ff_tmv_demuxer; +extern const FFInputFormat ff_truehd_demuxer; extern const FFOutputFormat ff_truehd_muxer; -extern const AVInputFormat ff_tta_demuxer; +extern const FFInputFormat ff_tta_demuxer; extern const FFOutputFormat ff_tta_muxer; extern const FFOutputFormat ff_ttml_muxer; -extern const AVInputFormat ff_txd_demuxer; -extern const AVInputFormat ff_tty_demuxer; -extern const AVInputFormat ff_ty_demuxer; +extern const FFInputFormat ff_txd_demuxer; +extern const FFInputFormat ff_tty_demuxer; +extern const FFInputFormat ff_ty_demuxer; extern const FFOutputFormat ff_uncodedframecrc_muxer; -extern const AVInputFormat ff_v210_demuxer; -extern const AVInputFormat ff_v210x_demuxer; -extern const AVInputFormat ff_vag_demuxer; -extern const AVInputFormat ff_vc1_demuxer; +extern const FFInputFormat ff_usm_demuxer; +extern const FFInputFormat ff_v210_demuxer; +extern const FFInputFormat ff_v210x_demuxer; +extern const FFInputFormat ff_vag_demuxer; +extern const FFInputFormat ff_vc1_demuxer; extern const FFOutputFormat ff_vc1_muxer; -extern const AVInputFormat ff_vc1t_demuxer; +extern const FFInputFormat ff_vc1t_demuxer; extern const FFOutputFormat ff_vc1t_muxer; -extern const AVInputFormat ff_vividas_demuxer; -extern const AVInputFormat ff_vivo_demuxer; -extern const AVInputFormat ff_vmd_demuxer; -extern const AVInputFormat ff_vobsub_demuxer; -extern const AVInputFormat ff_voc_demuxer; +extern const FFInputFormat ff_vividas_demuxer; +extern const FFInputFormat ff_vivo_demuxer; +extern const FFInputFormat ff_vmd_demuxer; +extern const FFInputFormat ff_vobsub_demuxer; +extern const FFInputFormat ff_voc_demuxer; extern const FFOutputFormat ff_voc_muxer; -extern const AVInputFormat ff_vpk_demuxer; -extern const AVInputFormat ff_vplayer_demuxer; -extern const AVInputFormat ff_vqf_demuxer; -extern const AVInputFormat ff_w64_demuxer; +extern const FFInputFormat ff_vpk_demuxer; +extern const FFInputFormat ff_vplayer_demuxer; +extern const FFInputFormat ff_vqf_demuxer; +extern const FFInputFormat ff_vvc_demuxer; +extern const FFOutputFormat ff_vvc_muxer; +extern const FFInputFormat ff_w64_demuxer; extern const FFOutputFormat ff_w64_muxer; -extern const AVInputFormat ff_wady_demuxer; -extern const AVInputFormat ff_wavarc_demuxer; -extern const AVInputFormat ff_wav_demuxer; +extern const FFInputFormat ff_wady_demuxer; +extern const FFInputFormat ff_wavarc_demuxer; +extern const FFInputFormat ff_wav_demuxer; extern const FFOutputFormat ff_wav_muxer; -extern const AVInputFormat ff_wc3_demuxer; +extern const FFInputFormat ff_wc3_demuxer; extern const FFOutputFormat ff_webm_muxer; -extern const AVInputFormat ff_webm_dash_manifest_demuxer; +extern const FFInputFormat ff_webm_dash_manifest_demuxer; extern const FFOutputFormat ff_webm_dash_manifest_muxer; extern const FFOutputFormat ff_webm_chunk_muxer; extern const FFOutputFormat ff_webp_muxer; -extern const AVInputFormat ff_webvtt_demuxer; +extern const FFInputFormat ff_webvtt_demuxer; extern const FFOutputFormat ff_webvtt_muxer; -extern const AVInputFormat ff_wsaud_demuxer; +extern const FFInputFormat ff_wsaud_demuxer; extern const FFOutputFormat ff_wsaud_muxer; -extern const AVInputFormat ff_wsd_demuxer; -extern const AVInputFormat ff_wsvqa_demuxer; -extern const AVInputFormat ff_wtv_demuxer; +extern const FFInputFormat ff_wsd_demuxer; +extern const FFInputFormat ff_wsvqa_demuxer; +extern const FFInputFormat ff_wtv_demuxer; extern const FFOutputFormat ff_wtv_muxer; -extern const AVInputFormat ff_wve_demuxer; -extern const AVInputFormat ff_wv_demuxer; +extern const FFInputFormat ff_wve_demuxer; +extern const FFInputFormat ff_wv_demuxer; extern const FFOutputFormat 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; -extern const AVInputFormat ff_yop_demuxer; -extern const AVInputFormat ff_yuv4mpegpipe_demuxer; +extern const FFInputFormat ff_xa_demuxer; +extern const FFInputFormat ff_xbin_demuxer; +extern const FFInputFormat ff_xmd_demuxer; +extern const FFInputFormat ff_xmv_demuxer; +extern const FFInputFormat ff_xvag_demuxer; +extern const FFInputFormat ff_xwma_demuxer; +extern const FFInputFormat ff_yop_demuxer; +extern const FFInputFormat ff_yuv4mpegpipe_demuxer; extern const FFOutputFormat ff_yuv4mpegpipe_muxer; /* image demuxers */ -extern const AVInputFormat ff_image_bmp_pipe_demuxer; -extern const AVInputFormat ff_image_cri_pipe_demuxer; -extern const AVInputFormat ff_image_dds_pipe_demuxer; -extern const AVInputFormat ff_image_dpx_pipe_demuxer; -extern const AVInputFormat ff_image_exr_pipe_demuxer; -extern const AVInputFormat ff_image_gem_pipe_demuxer; -extern const AVInputFormat ff_image_gif_pipe_demuxer; -extern const AVInputFormat ff_image_hdr_pipe_demuxer; -extern const AVInputFormat ff_image_j2k_pipe_demuxer; -extern const AVInputFormat ff_image_jpeg_pipe_demuxer; -extern const AVInputFormat ff_image_jpegls_pipe_demuxer; -extern const AVInputFormat ff_image_jpegxl_pipe_demuxer; -extern const AVInputFormat ff_image_pam_pipe_demuxer; -extern const AVInputFormat ff_image_pbm_pipe_demuxer; -extern const AVInputFormat ff_image_pcx_pipe_demuxer; -extern const AVInputFormat ff_image_pfm_pipe_demuxer; -extern const AVInputFormat ff_image_pgmyuv_pipe_demuxer; -extern const AVInputFormat ff_image_pgm_pipe_demuxer; -extern const AVInputFormat ff_image_pgx_pipe_demuxer; -extern const AVInputFormat ff_image_phm_pipe_demuxer; -extern const AVInputFormat ff_image_photocd_pipe_demuxer; -extern const AVInputFormat ff_image_pictor_pipe_demuxer; -extern const AVInputFormat ff_image_png_pipe_demuxer; -extern const AVInputFormat ff_image_ppm_pipe_demuxer; -extern const AVInputFormat ff_image_psd_pipe_demuxer; -extern const AVInputFormat ff_image_qdraw_pipe_demuxer; -extern const AVInputFormat ff_image_qoi_pipe_demuxer; -extern const AVInputFormat ff_image_sgi_pipe_demuxer; -extern const AVInputFormat ff_image_svg_pipe_demuxer; -extern const AVInputFormat ff_image_sunrast_pipe_demuxer; -extern const AVInputFormat ff_image_tiff_pipe_demuxer; -extern const AVInputFormat ff_image_vbn_pipe_demuxer; -extern const AVInputFormat ff_image_webp_pipe_demuxer; -extern const AVInputFormat ff_image_xbm_pipe_demuxer; -extern const AVInputFormat ff_image_xpm_pipe_demuxer; -extern const AVInputFormat ff_image_xwd_pipe_demuxer; +extern const FFInputFormat ff_image_bmp_pipe_demuxer; +extern const FFInputFormat ff_image_cri_pipe_demuxer; +extern const FFInputFormat ff_image_dds_pipe_demuxer; +extern const FFInputFormat ff_image_dpx_pipe_demuxer; +extern const FFInputFormat ff_image_exr_pipe_demuxer; +extern const FFInputFormat ff_image_gem_pipe_demuxer; +extern const FFInputFormat ff_image_gif_pipe_demuxer; +extern const FFInputFormat ff_image_hdr_pipe_demuxer; +extern const FFInputFormat ff_image_j2k_pipe_demuxer; +extern const FFInputFormat ff_image_jpeg_pipe_demuxer; +extern const FFInputFormat ff_image_jpegls_pipe_demuxer; +extern const FFInputFormat ff_image_jpegxl_pipe_demuxer; +extern const FFInputFormat ff_image_pam_pipe_demuxer; +extern const FFInputFormat ff_image_pbm_pipe_demuxer; +extern const FFInputFormat ff_image_pcx_pipe_demuxer; +extern const FFInputFormat ff_image_pfm_pipe_demuxer; +extern const FFInputFormat ff_image_pgmyuv_pipe_demuxer; +extern const FFInputFormat ff_image_pgm_pipe_demuxer; +extern const FFInputFormat ff_image_pgx_pipe_demuxer; +extern const FFInputFormat ff_image_phm_pipe_demuxer; +extern const FFInputFormat ff_image_photocd_pipe_demuxer; +extern const FFInputFormat ff_image_pictor_pipe_demuxer; +extern const FFInputFormat ff_image_png_pipe_demuxer; +extern const FFInputFormat ff_image_ppm_pipe_demuxer; +extern const FFInputFormat ff_image_psd_pipe_demuxer; +extern const FFInputFormat ff_image_qdraw_pipe_demuxer; +extern const FFInputFormat ff_image_qoi_pipe_demuxer; +extern const FFInputFormat ff_image_sgi_pipe_demuxer; +extern const FFInputFormat ff_image_svg_pipe_demuxer; +extern const FFInputFormat ff_image_sunrast_pipe_demuxer; +extern const FFInputFormat ff_image_tiff_pipe_demuxer; +extern const FFInputFormat ff_image_vbn_pipe_demuxer; +extern const FFInputFormat ff_image_webp_pipe_demuxer; +extern const FFInputFormat ff_image_xbm_pipe_demuxer; +extern const FFInputFormat ff_image_xpm_pipe_demuxer; +extern const FFInputFormat ff_image_xwd_pipe_demuxer; /* external libraries */ extern const FFOutputFormat ff_chromaprint_muxer; -extern const AVInputFormat ff_libgme_demuxer; -extern const AVInputFormat ff_libmodplug_demuxer; -extern const AVInputFormat ff_libopenmpt_demuxer; -extern const AVInputFormat ff_vapoursynth_demuxer; +extern const FFInputFormat ff_libgme_demuxer; +extern const FFInputFormat ff_libmodplug_demuxer; +extern const FFInputFormat ff_libopenmpt_demuxer; +extern const FFInputFormat ff_vapoursynth_demuxer; #include "libavformat/muxer_list.c" #include "libavformat/demuxer_list.c" @@ -588,22 +604,24 @@ const AVInputFormat *av_demuxer_iterate(void **opaque) { static const uintptr_t size = sizeof(demuxer_list)/sizeof(demuxer_list[0]) - 1; uintptr_t i = (uintptr_t)*opaque; - const AVInputFormat *f = NULL; + const FFInputFormat *f = NULL; uintptr_t tmp; if (i < size) { f = demuxer_list[i]; } else if (tmp = atomic_load_explicit(&indev_list_intptr, memory_order_relaxed)) { - const AVInputFormat *const *indev_list = (const AVInputFormat *const *)tmp; + const FFInputFormat *const *indev_list = (const FFInputFormat *const *)tmp; f = indev_list[i - size]; } - if (f) + if (f) { *opaque = (void*)(i + 1); - return f; + return &f->p; + } + return NULL; } -void avpriv_register_devices(const FFOutputFormat * const o[], const AVInputFormat * const i[]) +void avpriv_register_devices(const FFOutputFormat * const o[], const FFInputFormat * const i[]) { atomic_store_explicit(&outdev_list_intptr, (uintptr_t)o, memory_order_relaxed); atomic_store_explicit(&indev_list_intptr, (uintptr_t)i, memory_order_relaxed); diff --git a/libavformat/alp.c b/libavformat/alp.c index 8c6066a59c6..ad8e160223a 100644 --- a/libavformat/alp.c +++ b/libavformat/alp.c @@ -24,6 +24,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "mux.h" #include "rawenc.h" @@ -163,9 +164,9 @@ static int alp_seek(AVFormatContext *s, int stream_index, return avio_seek(s->pb, hdr->header_size + 8, SEEK_SET); } -const AVInputFormat ff_alp_demuxer = { - .name = "alp", - .long_name = NULL_IF_CONFIG_SMALL("LEGO Racers ALP"), +const FFInputFormat ff_alp_demuxer = { + .p.name = "alp", + .p.long_name = NULL_IF_CONFIG_SMALL("LEGO Racers ALP"), .priv_data_size = sizeof(ALPHeader), .read_probe = alp_probe, .read_header = alp_read_header, @@ -188,19 +189,8 @@ static int alp_write_init(AVFormatContext *s) alp->type = ALP_TYPE_TUN; } - if (s->nb_streams != 1) { - av_log(s, AV_LOG_ERROR, "Too many streams\n"); - return AVERROR(EINVAL); - } - par = s->streams[0]->codecpar; - if (par->codec_id != AV_CODEC_ID_ADPCM_IMA_ALP) { - av_log(s, AV_LOG_ERROR, "%s codec not supported\n", - avcodec_get_name(par->codec_id)); - return AVERROR(EINVAL); - } - if (par->ch_layout.nb_channels > 2) { av_log(s, AV_LOG_ERROR, "A maximum of 2 channels are supported\n"); return AVERROR(EINVAL); @@ -297,7 +287,10 @@ const FFOutputFormat ff_alp_muxer = { .p.extensions = "tun,pcm", .p.audio_codec = AV_CODEC_ID_ADPCM_IMA_ALP, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, .p.priv_class = &alp_muxer_class, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .init = alp_write_init, .write_header = alp_write_header, .write_packet = ff_raw_write_packet, diff --git a/libavformat/amr.c b/libavformat/amr.c index b6615d8295c..0db0a8d26aa 100644 --- a/libavformat/amr.c +++ b/libavformat/amr.c @@ -29,6 +29,7 @@ Write and read amr data according to RFC3267, http://www.ietf.org/rfc/rfc3267.tx #include "libavutil/intreadwrite.h" #include "avformat.h" #include "avio_internal.h" +#include "demux.h" #include "internal.h" #include "mux.h" #include "rawdec.h" @@ -50,23 +51,6 @@ static const uint8_t amrwb_packed_size[16] = { 18, 24, 33, 37, 41, 47, 51, 59, 61, 6, 1, 1, 1, 1, 1, 1 }; -#if CONFIG_AMR_MUXER -static int amr_write_header(AVFormatContext *s) -{ - AVIOContext *pb = s->pb; - AVCodecParameters *par = s->streams[0]->codecpar; - - if (par->codec_id == AV_CODEC_ID_AMR_NB) { - avio_write(pb, AMR_header, sizeof(AMR_header)); /* magic number */ - } else if (par->codec_id == AV_CODEC_ID_AMR_WB) { - avio_write(pb, AMRWB_header, sizeof(AMRWB_header)); /* magic number */ - } else { - return -1; - } - return 0; -} -#endif /* CONFIG_AMR_MUXER */ - #if CONFIG_AMR_DEMUXER static int amr_probe(const AVProbeData *p) { @@ -140,15 +124,15 @@ static int amr_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_amr_demuxer = { - .name = "amr", - .long_name = NULL_IF_CONFIG_SMALL("3GPP AMR"), +const FFInputFormat ff_amr_demuxer = { + .p.name = "amr", + .p.long_name = NULL_IF_CONFIG_SMALL("3GPP AMR"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.priv_class = &ff_raw_demuxer_class, .priv_data_size = sizeof(AMRContext), .read_probe = amr_probe, .read_header = amr_read_header, .read_packet = ff_raw_read_partial_packet, - .flags = AVFMT_GENERIC_INDEX, - .priv_class = &ff_raw_demuxer_class, }; #endif @@ -197,15 +181,15 @@ static int amrnb_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_amrnb_demuxer = { - .name = "amrnb", - .long_name = NULL_IF_CONFIG_SMALL("raw AMR-NB"), +const FFInputFormat ff_amrnb_demuxer = { + .p.name = "amrnb", + .p.long_name = NULL_IF_CONFIG_SMALL("raw AMR-NB"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.priv_class = &ff_raw_demuxer_class, .priv_data_size = sizeof(AMRContext), .read_probe = amrnb_probe, .read_header = amrnb_read_header, .read_packet = ff_raw_read_partial_packet, - .flags = AVFMT_GENERIC_INDEX, - .priv_class = &ff_raw_demuxer_class, }; #endif @@ -254,19 +238,34 @@ static int amrwb_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_amrwb_demuxer = { - .name = "amrwb", - .long_name = NULL_IF_CONFIG_SMALL("raw AMR-WB"), +const FFInputFormat ff_amrwb_demuxer = { + .p.name = "amrwb", + .p.long_name = NULL_IF_CONFIG_SMALL("raw AMR-WB"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.priv_class = &ff_raw_demuxer_class, .priv_data_size = sizeof(AMRContext), .read_probe = amrwb_probe, .read_header = amrwb_read_header, .read_packet = ff_raw_read_partial_packet, - .flags = AVFMT_GENERIC_INDEX, - .priv_class = &ff_raw_demuxer_class, }; #endif #if CONFIG_AMR_MUXER +static int amr_write_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVCodecParameters *par = s->streams[0]->codecpar; + + if (par->codec_id == AV_CODEC_ID_AMR_NB) { + avio_write(pb, AMR_header, sizeof(AMR_header)); /* magic number */ + } else if (par->codec_id == AV_CODEC_ID_AMR_WB) { + avio_write(pb, AMRWB_header, sizeof(AMRWB_header)); /* magic number */ + } else { + return -1; + } + return 0; +} + const FFOutputFormat ff_amr_muxer = { .p.name = "amr", .p.long_name = NULL_IF_CONFIG_SMALL("3GPP AMR"), @@ -274,7 +273,9 @@ const FFOutputFormat ff_amr_muxer = { .p.extensions = "amr", .p.audio_codec = AV_CODEC_ID_AMR_NB, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, .p.flags = AVFMT_NOTIMESTAMPS, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH, .write_header = amr_write_header, .write_packet = ff_raw_write_packet, }; diff --git a/libavformat/amvenc.c b/libavformat/amvenc.c index e1b1ffd42e2..5ff4f69cfa5 100644 --- a/libavformat/amvenc.c +++ b/libavformat/amvenc.c @@ -113,11 +113,7 @@ static av_cold int amv_init(AVFormatContext *s) return AVERROR(EINVAL); } - if (ast->codecpar->codec_id != AV_CODEC_ID_ADPCM_IMA_AMV) { - av_log(s, AV_LOG_ERROR, "Second AMV stream must be %s\n", - avcodec_get_name(AV_CODEC_ID_ADPCM_IMA_AMV)); - return AVERROR(EINVAL); - } + av_assert1(ast->codecpar->codec_id == AV_CODEC_ID_ADPCM_IMA_AMV); /* These files are broken-enough as they are. They shouldn't be streamed. */ if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) { @@ -410,6 +406,9 @@ const FFOutputFormat ff_amv_muxer = { .priv_data_size = sizeof(AMVContext), .p.audio_codec = AV_CODEC_ID_ADPCM_IMA_AMV, .p.video_codec = AV_CODEC_ID_AMV, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .init = amv_init, .deinit = amv_deinit, .write_header = amv_write_header, diff --git a/libavformat/anm.c b/libavformat/anm.c index 7feba4ed1e7..789780d606f 100644 --- a/libavformat/anm.c +++ b/libavformat/anm.c @@ -172,7 +172,7 @@ static int read_packet(AVFormatContext *s, int tmp, record_size; if (avio_feof(s->pb)) - return AVERROR(EIO); + return AVERROR_EOF; if (anm->page < 0) return anm->page; @@ -215,9 +215,9 @@ static int read_packet(AVFormatContext *s, return 0; } -const AVInputFormat ff_anm_demuxer = { - .name = "anm", - .long_name = NULL_IF_CONFIG_SMALL("Deluxe Paint Animation"), +const FFInputFormat ff_anm_demuxer = { + .p.name = "anm", + .p.long_name = NULL_IF_CONFIG_SMALL("Deluxe Paint Animation"), .priv_data_size = sizeof(AnmDemuxContext), .read_probe = probe, .read_header = read_header, diff --git a/libavformat/apac.c b/libavformat/apac.c index 18970e19dd8..139035ca13b 100644 --- a/libavformat/apac.c +++ b/libavformat/apac.c @@ -72,15 +72,15 @@ static int apac_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_apac_demuxer = { - .name = "apac", - .long_name = NULL_IF_CONFIG_SMALL("raw APAC"), +const FFInputFormat ff_apac_demuxer = { + .p.name = "apac", + .p.long_name = NULL_IF_CONFIG_SMALL("raw APAC"), + .p.extensions = "apc", + .p.flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS, + .p.priv_class = &ff_raw_demuxer_class, .read_probe = apac_probe, .read_header = apac_read_header, .read_packet = ff_raw_read_partial_packet, - .extensions = "apc", - .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS, .raw_codec_id = AV_CODEC_ID_APAC, .priv_data_size = sizeof(FFRawDemuxerContext), - .priv_class = &ff_raw_demuxer_class, }; diff --git a/libavformat/apc.c b/libavformat/apc.c index b8b18c966cf..d24f57d021e 100644 --- a/libavformat/apc.c +++ b/libavformat/apc.c @@ -79,9 +79,9 @@ static int apc_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVInputFormat ff_apc_demuxer = { - .name = "apc", - .long_name = NULL_IF_CONFIG_SMALL("CRYO APC"), +const FFInputFormat ff_apc_demuxer = { + .p.name = "apc", + .p.long_name = NULL_IF_CONFIG_SMALL("CRYO APC"), .read_probe = apc_probe, .read_header = apc_read_header, .read_packet = apc_read_packet, diff --git a/libavformat/ape.c b/libavformat/ape.c index 92e9ac7cb1c..c664cd60fe5 100644 --- a/libavformat/ape.c +++ b/libavformat/ape.c @@ -24,6 +24,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "apetag.h" @@ -290,7 +291,7 @@ static int ape_read_header(AVFormatContext * s) final_size -= final_size & 3; } if (file_size <= 0 || final_size <= 0) - final_size = ape->finalframeblocks * 8; + final_size = ape->finalframeblocks * 8LL; ape->frames[ape->totalframes - 1].size = final_size; for (i = 0; i < ape->totalframes; i++) { @@ -444,15 +445,15 @@ static int ape_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp return 0; } -const AVInputFormat ff_ape_demuxer = { - .name = "ape", - .long_name = NULL_IF_CONFIG_SMALL("Monkey's Audio"), +const FFInputFormat ff_ape_demuxer = { + .p.name = "ape", + .p.long_name = NULL_IF_CONFIG_SMALL("Monkey's Audio"), + .p.extensions = "ape,apl,mac", .priv_data_size = sizeof(APEContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = ape_probe, .read_header = ape_read_header, .read_packet = ape_read_packet, .read_close = ape_read_close, .read_seek = ape_read_seek, - .extensions = "ape,apl,mac", }; diff --git a/libavformat/apm.c b/libavformat/apm.c index ccb8e22437e..98c5439738f 100644 --- a/libavformat/apm.c +++ b/libavformat/apm.c @@ -23,6 +23,7 @@ #include "config_components.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "mux.h" #include "rawenc.h" @@ -202,9 +203,9 @@ static int apm_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVInputFormat ff_apm_demuxer = { - .name = "apm", - .long_name = NULL_IF_CONFIG_SMALL("Ubisoft Rayman 2 APM"), +const FFInputFormat ff_apm_demuxer = { + .p.name = "apm", + .p.long_name = NULL_IF_CONFIG_SMALL("Ubisoft Rayman 2 APM"), .read_probe = apm_probe, .read_header = apm_read_header, .read_packet = apm_read_packet @@ -214,20 +215,7 @@ const AVInputFormat ff_apm_demuxer = { #if CONFIG_APM_MUXER static int apm_write_init(AVFormatContext *s) { - AVCodecParameters *par; - - if (s->nb_streams != 1) { - av_log(s, AV_LOG_ERROR, "APM files have exactly one stream\n"); - return AVERROR(EINVAL); - } - - par = s->streams[0]->codecpar; - - if (par->codec_id != AV_CODEC_ID_ADPCM_IMA_APM) { - av_log(s, AV_LOG_ERROR, "%s codec not supported\n", - avcodec_get_name(par->codec_id)); - return AVERROR(EINVAL); - } + AVCodecParameters *par = s->streams[0]->codecpar; if (par->ch_layout.nb_channels > 2) { av_log(s, AV_LOG_ERROR, "APM files only support up to 2 channels\n"); @@ -310,6 +298,9 @@ const FFOutputFormat ff_apm_muxer = { .p.extensions = "apm", .p.audio_codec = AV_CODEC_ID_ADPCM_IMA_APM, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .init = apm_write_init, .write_header = apm_write_header, .write_packet = ff_raw_write_packet, diff --git a/libavformat/apngdec.c b/libavformat/apngdec.c index 47cdbfcbfb7..4690283337d 100644 --- a/libavformat/apngdec.c +++ b/libavformat/apngdec.c @@ -28,6 +28,7 @@ #include "avformat.h" #include "avio_internal.h" +#include "demux.h" #include "internal.h" #include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" @@ -274,7 +275,7 @@ static int decode_fctl_chunk(AVFormatContext *s, APNGDemuxContext *ctx, AVPacket "delay_den: %"PRIu16", " "dispose_op: %d, " "blend_op: %d\n", - __FUNCTION__, + __func__, sequence_number, width, height, @@ -421,13 +422,13 @@ static const AVClass demuxer_class = { .category = AV_CLASS_CATEGORY_DEMUXER, }; -const AVInputFormat ff_apng_demuxer = { - .name = "apng", - .long_name = NULL_IF_CONFIG_SMALL("Animated Portable Network Graphics"), +const FFInputFormat ff_apng_demuxer = { + .p.name = "apng", + .p.long_name = NULL_IF_CONFIG_SMALL("Animated Portable Network Graphics"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.priv_class = &demuxer_class, .priv_data_size = sizeof(APNGDemuxContext), .read_probe = apng_probe, .read_header = apng_read_header, .read_packet = apng_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .priv_class = &demuxer_class, }; diff --git a/libavformat/apngenc.c b/libavformat/apngenc.c index a0af916f141..4d0438f8248 100644 --- a/libavformat/apngenc.c +++ b/libavformat/apngenc.c @@ -84,14 +84,6 @@ static int apng_write_header(AVFormatContext *format_context) APNGMuxContext *apng = format_context->priv_data; AVCodecParameters *par = format_context->streams[0]->codecpar; - if (format_context->nb_streams != 1 || - format_context->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO || - format_context->streams[0]->codecpar->codec_id != AV_CODEC_ID_APNG) { - av_log(format_context, AV_LOG_ERROR, - "APNG muxer supports only a single video APNG stream.\n"); - return AVERROR(EINVAL); - } - if (apng->last_delay.num > UINT16_MAX || apng->last_delay.den > UINT16_MAX) { av_reduce(&apng->last_delay.num, &apng->last_delay.den, apng->last_delay.num, apng->last_delay.den, UINT16_MAX); @@ -315,6 +307,9 @@ const FFOutputFormat ff_apng_muxer = { .priv_data_size = sizeof(APNGMuxContext), .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_APNG, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_header = apng_write_header, .write_packet = apng_write_packet, .write_trailer = apng_write_trailer, diff --git a/libavformat/aptxdec.c b/libavformat/aptxdec.c index 0637a8afde2..2fca45a71ed 100644 --- a/libavformat/aptxdec.c +++ b/libavformat/aptxdec.c @@ -24,6 +24,7 @@ #include "libavutil/opt.h" #include "avformat.h" +#include "demux.h" #define APTX_BLOCK_SIZE 4 #define APTX_PACKET_SIZE (256*APTX_BLOCK_SIZE) @@ -101,27 +102,27 @@ static const AVClass aptx_demuxer_class = { }; #if CONFIG_APTX_DEMUXER -const AVInputFormat ff_aptx_demuxer = { - .name = "aptx", - .long_name = NULL_IF_CONFIG_SMALL("raw aptX"), - .extensions = "aptx", +const FFInputFormat ff_aptx_demuxer = { + .p.name = "aptx", + .p.long_name = NULL_IF_CONFIG_SMALL("raw aptX"), + .p.extensions = "aptx", + .p.flags = AVFMT_GENERIC_INDEX, + .p.priv_class = &aptx_demuxer_class, .priv_data_size = sizeof(AptXDemuxerContext), .read_header = aptx_read_header, .read_packet = aptx_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .priv_class = &aptx_demuxer_class, }; #endif #if CONFIG_APTX_HD_DEMUXER -const AVInputFormat ff_aptx_hd_demuxer = { - .name = "aptx_hd", - .long_name = NULL_IF_CONFIG_SMALL("raw aptX HD"), - .extensions = "aptxhd", +const FFInputFormat ff_aptx_hd_demuxer = { + .p.name = "aptx_hd", + .p.long_name = NULL_IF_CONFIG_SMALL("raw aptX HD"), + .p.extensions = "aptxhd", + .p.flags = AVFMT_GENERIC_INDEX, + .p.priv_class = &aptx_demuxer_class, .priv_data_size = sizeof(AptXDemuxerContext), .read_header = aptx_hd_read_header, .read_packet = aptx_hd_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .priv_class = &aptx_demuxer_class, }; #endif diff --git a/libavformat/aqtitledec.c b/libavformat/aqtitledec.c index 6c14b23862b..5d41a2278d8 100644 --- a/libavformat/aqtitledec.c +++ b/libavformat/aqtitledec.c @@ -27,6 +27,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" #include "subtitles.h" #include "libavutil/opt.h" @@ -135,16 +136,16 @@ static const AVClass aqt_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_aqtitle_demuxer = { - .name = "aqtitle", - .long_name = NULL_IF_CONFIG_SMALL("AQTitle subtitles"), +const FFInputFormat ff_aqtitle_demuxer = { + .p.name = "aqtitle", + .p.long_name = NULL_IF_CONFIG_SMALL("AQTitle subtitles"), + .p.extensions = "aqt", + .p.priv_class = &aqt_class, .priv_data_size = sizeof(AQTitleContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = aqt_probe, .read_header = aqt_read_header, .read_packet = aqt_read_packet, .read_seek2 = aqt_read_seek, .read_close = aqt_read_close, - .extensions = "aqt", - .priv_class = &aqt_class, }; diff --git a/libavformat/argo_asf.c b/libavformat/argo_asf.c index 5f38b68b6ac..e08f029f80c 100644 --- a/libavformat/argo_asf.c +++ b/libavformat/argo_asf.c @@ -24,6 +24,7 @@ #include "libavutil/avstring.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "mux.h" #include "libavutil/channel_layout.h" @@ -258,7 +259,7 @@ static int argo_asf_seek(AVFormatContext *s, int stream_index, return -1; offset = asf->fhdr.chunk_offset + ASF_CHUNK_HEADER_SIZE + - (block * st->codecpar->block_align); + block * (int64_t)st->codecpar->block_align; if ((offset = avio_seek(s->pb, offset, SEEK_SET)) < 0) return offset; @@ -272,9 +273,9 @@ static int argo_asf_seek(AVFormatContext *s, int stream_index, * - Argonaut Sound File? * - Audio Stream File? */ -const AVInputFormat ff_argo_asf_demuxer = { - .name = "argo_asf", - .long_name = NULL_IF_CONFIG_SMALL("Argonaut Games ASF"), +const FFInputFormat ff_argo_asf_demuxer = { + .p.name = "argo_asf", + .p.long_name = NULL_IF_CONFIG_SMALL("Argonaut Games ASF"), .priv_data_size = sizeof(ArgoASFDemuxContext), .read_probe = argo_asf_probe, .read_header = argo_asf_read_header, @@ -287,20 +288,7 @@ const AVInputFormat ff_argo_asf_demuxer = { static int argo_asf_write_init(AVFormatContext *s) { ArgoASFMuxContext *ctx = s->priv_data; - const AVCodecParameters *par; - - if (s->nb_streams != 1) { - av_log(s, AV_LOG_ERROR, "ASF files have exactly one stream\n"); - return AVERROR(EINVAL); - } - - par = s->streams[0]->codecpar; - - if (par->codec_id != AV_CODEC_ID_ADPCM_ARGO) { - av_log(s, AV_LOG_ERROR, "%s codec not supported\n", - avcodec_get_name(par->codec_id)); - return AVERROR(EINVAL); - } + const AVCodecParameters *par = s->streams[0]->codecpar; if (ctx->version_major == 1 && ctx->version_minor == 1 && par->sample_rate != 22050) { av_log(s, AV_LOG_ERROR, "ASF v1.1 files only support a sample rate of 22050\n"); @@ -480,7 +468,10 @@ const FFOutputFormat ff_argo_asf_muxer = { */ .p.audio_codec = AV_CODEC_ID_ADPCM_ARGO, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, .p.priv_class = &argo_asf_muxer_class, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .init = argo_asf_write_init, .write_header = argo_asf_write_header, .write_packet = argo_asf_write_packet, diff --git a/libavformat/argo_brp.c b/libavformat/argo_brp.c index 2ccdbd3e5bc..f88def37313 100644 --- a/libavformat/argo_brp.c +++ b/libavformat/argo_brp.c @@ -21,6 +21,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" #include "libavutil/intreadwrite.h" #include "libavutil/avassert.h" @@ -413,9 +414,9 @@ static int argo_brp_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVInputFormat ff_argo_brp_demuxer = { - .name = "argo_brp", - .long_name = NULL_IF_CONFIG_SMALL("Argonaut Games BRP"), +const FFInputFormat ff_argo_brp_demuxer = { + .p.name = "argo_brp", + .p.long_name = NULL_IF_CONFIG_SMALL("Argonaut Games BRP"), .priv_data_size = sizeof(ArgoBRPDemuxContext), .read_probe = argo_brp_probe, .read_header = argo_brp_read_header, diff --git a/libavformat/argo_cvg.c b/libavformat/argo_cvg.c index 2c74200b7d8..03ae6fa59e9 100644 --- a/libavformat/argo_cvg.c +++ b/libavformat/argo_cvg.c @@ -25,6 +25,7 @@ #include "libavutil/avstring.h" #include "libavutil/channel_layout.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "mux.h" #include "libavutil/opt.h" @@ -46,13 +47,6 @@ typedef struct ArgoCVGHeader { uint32_t reverb; /*< Reverb flag. */ } ArgoCVGHeader; -typedef struct ArgoCVGOverride { - const char *name; - ArgoCVGHeader header; - uint32_t checksum; - int sample_rate; -} ArgoCVGOverride; - typedef struct ArgoCVGDemuxContext { ArgoCVGHeader header; uint32_t checksum; @@ -71,12 +65,33 @@ typedef struct ArgoCVGMuxContext { #if CONFIG_ARGO_CVG_DEMUXER /* "Special" files that are played at a different rate. */ +// FILE(NAME, SIZE, LOOP, REVERB, CHECKSUM, SAMPLE_RATE) +#define OVERRIDE_FILES(FILE) \ + FILE(CRYS, 23592, 0, 1, 2495499, 88200) /* Beta */ \ + FILE(REDCRY88, 38280, 0, 1, 4134848, 88200) /* Beta */ \ + FILE(DANLOOP1, 54744, 1, 0, 5684641, 37800) /* Beta */ \ + FILE(PICKUP88, 12904, 0, 1, 1348091, 48000) /* Beta */ \ + FILE(SELECT1, 5080, 0, 1, 549987, 44100) /* Beta */ \ + +#define MAX_FILENAME_SIZE(NAME, SIZE, LOOP, REVERB, CHECKSUM, SAMPLE_RATE) \ + MAX_SIZE_BEFORE_ ## NAME, \ + MAX_SIZE_UNTIL_ ## NAME ## _MINUS1 = FFMAX(sizeof(#NAME ".CVG"), MAX_SIZE_BEFORE_ ## NAME) - 1, +enum { + OVERRIDE_FILES(MAX_FILENAME_SIZE) + MAX_OVERRIDE_FILENAME_SIZE +}; + +typedef struct ArgoCVGOverride { + const char name[MAX_OVERRIDE_FILENAME_SIZE]; + ArgoCVGHeader header; + uint32_t checksum; + int sample_rate; +} ArgoCVGOverride; + +#define FILE(NAME, SIZE, LOOP, REVERB, CHECKSUM, SAMPLE_RATE) \ + { #NAME ".CVG", { SIZE, LOOP, REVERB }, CHECKSUM, SAMPLE_RATE }, static const ArgoCVGOverride overrides[] = { - { "CRYS.CVG", { 23592, 0, 1 }, 2495499, 88200 }, /* Beta */ - { "REDCRY88.CVG", { 38280, 0, 1 }, 4134848, 88200 }, /* Beta */ - { "DANLOOP1.CVG", { 54744, 1, 0 }, 5684641, 37800 }, /* Beta */ - { "PICKUP88.CVG", { 12904, 0, 1 }, 1348091, 48000 }, /* Beta */ - { "SELECT1.CVG", { 5080, 0, 1 }, 549987, 44100 }, /* Beta */ + OVERRIDE_FILES(FILE) }; static int argo_cvg_probe(const AVProbeData *p) @@ -253,9 +268,9 @@ static int argo_cvg_seek(AVFormatContext *s, int stream_index, return 0; } -const AVInputFormat ff_argo_cvg_demuxer = { - .name = "argo_cvg", - .long_name = NULL_IF_CONFIG_SMALL("Argonaut Games CVG"), +const FFInputFormat ff_argo_cvg_demuxer = { + .p.name = "argo_cvg", + .p.long_name = NULL_IF_CONFIG_SMALL("Argonaut Games CVG"), .priv_data_size = sizeof(ArgoCVGDemuxContext), .read_probe = argo_cvg_probe, .read_header = argo_cvg_read_header, @@ -268,20 +283,7 @@ const AVInputFormat ff_argo_cvg_demuxer = { static int argo_cvg_write_init(AVFormatContext *s) { ArgoCVGMuxContext *ctx = s->priv_data; - const AVCodecParameters *par; - - if (s->nb_streams != 1) { - av_log(s, AV_LOG_ERROR, "CVG files have exactly one stream\n"); - return AVERROR(EINVAL); - } - - par = s->streams[0]->codecpar; - - if (par->codec_id != AV_CODEC_ID_ADPCM_PSX) { - av_log(s, AV_LOG_ERROR, "%s codec not supported\n", - avcodec_get_name(par->codec_id)); - return AVERROR(EINVAL); - } + const AVCodecParameters *par = s->streams[0]->codecpar; if (par->ch_layout.nb_channels != 1) { av_log(s, AV_LOG_ERROR, "CVG files only support 1 channel\n"); @@ -407,7 +409,10 @@ const FFOutputFormat ff_argo_cvg_muxer = { .p.extensions = "cvg", .p.audio_codec = AV_CODEC_ID_ADPCM_PSX, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, .p.priv_class = &argo_cvg_muxer_class, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .init = argo_cvg_write_init, .write_header = argo_cvg_write_header, .write_packet = argo_cvg_write_packet, diff --git a/libavformat/asfdec_f.c b/libavformat/asfdec_f.c index aa7aaa6ab6c..8daae216a60 100644 --- a/libavformat/asfdec_f.c +++ b/libavformat/asfdec_f.c @@ -445,6 +445,8 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size) st->codecpar->codec_tag = tag1; st->codecpar->codec_id = ff_codec_get_id(ff_codec_bmp_tags, tag1); + if (!st->codecpar->codec_id) + st->codecpar->codec_id = ff_codec_get_id(ff_codec_bmp_tags_unofficial, tag1); if (tag1 == MKTAG('D', 'V', 'R', ' ')) { sti->need_parsing = AVSTREAM_PARSE_FULL; /* issue658 contains wrong w/h and MS even puts a fake seq header @@ -458,7 +460,9 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size) if (st->codecpar->codec_id == AV_CODEC_ID_H264) sti->need_parsing = AVSTREAM_PARSE_FULL_ONCE; if (st->codecpar->codec_id == AV_CODEC_ID_MPEG4) - sti->need_parsing = AVSTREAM_PARSE_FULL_ONCE; + sti->need_parsing = AVSTREAM_PARSE_FULL; + if (st->codecpar->codec_id == AV_CODEC_ID_HEVC) + sti->need_parsing = AVSTREAM_PARSE_FULL; } pos2 = avio_tell(pb); avio_skip(pb, size - (pos2 - pos1 + 24)); @@ -670,7 +674,7 @@ static int asf_read_marker(AVFormatContext *s) avio_rl64(pb); // offset, 8 bytes pres_time = avio_rl64(pb); // presentation time - pres_time = av_sat_sub64(pres_time, asf->hdr.preroll * 10000); + pres_time = av_sat_sub64(pres_time, asf->hdr.preroll * 10000LL); avio_rl16(pb); // entry length avio_rl32(pb); // send time avio_rl32(pb); // flags @@ -1613,9 +1617,11 @@ static int asf_read_seek(AVFormatContext *s, int stream_index, return 0; } -const AVInputFormat ff_asf_demuxer = { - .name = "asf", - .long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), +const FFInputFormat ff_asf_demuxer = { + .p.name = "asf", + .p.long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), + .p.flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH, + .p.priv_class = &asf_class, .priv_data_size = sizeof(ASFContext), .read_probe = asf_probe, .read_header = asf_read_header, @@ -1623,6 +1629,4 @@ const AVInputFormat ff_asf_demuxer = { .read_close = asf_read_close, .read_seek = asf_read_seek, .read_timestamp = asf_read_pts, - .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH, - .priv_class = &asf_class, }; diff --git a/libavformat/asfdec_o.c b/libavformat/asfdec_o.c index 2b407c016f2..8ec1c809432 100644 --- a/libavformat/asfdec_o.c +++ b/libavformat/asfdec_o.c @@ -19,6 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include "libavutil/attributes.h" #include "libavutil/common.h" #include "libavutil/dict.h" @@ -864,6 +866,9 @@ static int asf_read_simple_index(AVFormatContext *s, const GUIDParseTable *g) int64_t offset; uint64_t size = avio_rl64(pb); + if (size < 24) + return AVERROR_INVALIDDATA; + // simple index objects should be ordered by stream number, this loop tries to find // the first not indexed video stream for (i = 0; i < asf->nb_streams; i++) { @@ -1672,9 +1677,10 @@ static int asf_read_header(AVFormatContext *s) return ret; } -const AVInputFormat ff_asf_o_demuxer = { - .name = "asf_o", - .long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), +const FFInputFormat ff_asf_o_demuxer = { + .p.name = "asf_o", + .p.long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), + .p.flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH, .priv_data_size = sizeof(ASFContext), .read_probe = asf_probe, .read_header = asf_read_header, @@ -1682,5 +1688,4 @@ const AVInputFormat ff_asf_o_demuxer = { .read_close = asf_read_close, .read_timestamp = asf_read_timestamp, .read_seek = asf_read_seek, - .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH, }; diff --git a/libavformat/asfenc.c b/libavformat/asfenc.c index 244c7e7a271..2f2d9b1babd 100644 --- a/libavformat/asfenc.c +++ b/libavformat/asfenc.c @@ -773,8 +773,7 @@ static int asf_write_header(AVFormatContext *s) asf->packet_nb_payloads = 0; asf->packet_timestamp_start = -1; asf->packet_timestamp_end = -1; - ffio_init_context(&asf->pb, asf->packet_buf, s->packet_size, 1, - NULL, NULL, NULL, NULL); + ffio_init_write_context(&asf->pb, asf->packet_buf, s->packet_size); if (s->avoid_negative_ts < 0) s->avoid_negative_ts = 1; @@ -866,8 +865,7 @@ static void flush_packet(AVFormatContext *s) asf->packet_nb_payloads = 0; asf->packet_timestamp_start = -1; asf->packet_timestamp_end = -1; - ffio_init_context(&asf->pb, asf->packet_buf, s->packet_size, 1, - NULL, NULL, NULL, NULL); + ffio_init_write_context(&asf->pb, asf->packet_buf, s->packet_size); } static void put_payload_header(AVFormatContext *s, ASFStream *stream, diff --git a/libavformat/assdec.c b/libavformat/assdec.c index 0915f6fafd7..3a8353f4d3a 100644 --- a/libavformat/assdec.c +++ b/libavformat/assdec.c @@ -23,6 +23,7 @@ #include #include "avformat.h" +#include "demux.h" #include "internal.h" #include "subtitles.h" #include "libavutil/bprint.h" @@ -73,6 +74,8 @@ static int read_dialogue(ASSContext *ass, AVBPrint *dst, const uint8_t *p, av_bprint_clear(dst); av_bprintf(dst, "%u,%d,%s", ass->readorder++, layer, p + pos); + if (!av_bprint_is_complete(dst)) + return AVERROR(ENOMEM); /* right strip the buffer */ while (dst->len > 0 && @@ -135,7 +138,7 @@ static int ass_read_header(AVFormatContext *s) av_bprintf(&header, "%s", line.str); continue; } - sub = ff_subtitles_queue_insert(&ass->q, rline.str, rline.len, 0); + sub = ff_subtitles_queue_insert_bprint(&ass->q, &rline, 0); if (!sub) { res = AVERROR(ENOMEM); goto end; @@ -158,10 +161,10 @@ static int ass_read_header(AVFormatContext *s) return res; } -const AVInputFormat ff_ass_demuxer = { - .name = "ass", - .long_name = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"), - .flags_internal = FF_FMT_INIT_CLEANUP, +const FFInputFormat ff_ass_demuxer = { + .p.name = "ass", + .p.long_name = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"), + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .priv_data_size = sizeof(ASSContext), .read_probe = ass_probe, .read_header = ass_read_header, diff --git a/libavformat/assenc.c b/libavformat/assenc.c index 85a1e533715..7b474a60053 100644 --- a/libavformat/assenc.c +++ b/libavformat/assenc.c @@ -21,6 +21,7 @@ #include "libavutil/avstring.h" #include "avformat.h" +#include "avio_internal.h" #include "internal.h" #include "mux.h" @@ -49,10 +50,6 @@ static int write_header(AVFormatContext *s) ASSContext *ass = s->priv_data; AVCodecParameters *par = s->streams[0]->codecpar; - if (s->nb_streams != 1 || par->codec_id != AV_CODEC_ID_ASS) { - av_log(s, AV_LOG_ERROR, "Exactly one ASS/SSA stream is needed.\n"); - return AVERROR(EINVAL); - } avpriv_set_pts_info(s->streams[0], 64, 1, 100); if (par->extradata_size > 0) { size_t header_size = par->extradata_size; @@ -63,19 +60,18 @@ static int write_header(AVFormatContext *s) if (trailer) trailer = strstr(trailer, "\n"); - if (trailer++) { - header_size = (trailer - par->extradata); + if (trailer) { + header_size = (++trailer - par->extradata); ass->trailer_size = par->extradata_size - header_size; if (ass->trailer_size) ass->trailer = trailer; } - avio_write(s->pb, par->extradata, header_size); - if (par->extradata[header_size - 1] != '\n') - avio_write(s->pb, "\r\n", 2); + ffio_write_lines(s->pb, par->extradata, header_size, NULL); + ass->ssa_mode = !strstr(par->extradata, "\n[V4+ Styles]"); if (!strstr(par->extradata, "\n[Events]")) - avio_printf(s->pb, "[Events]\r\nFormat: %s, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n", + avio_printf(s->pb, "[Events]\nFormat: %s, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n", ass->ssa_mode ? "Marked" : "Layer"); } @@ -95,7 +91,7 @@ static void purge_dialogues(AVFormatContext *s, int force) ass->expected_readorder, dialogue->readorder); ass->expected_readorder = dialogue->readorder; } - avio_print(s->pb, "Dialogue: ", dialogue->line, "\r\n"); + avio_print(s->pb, "Dialogue: ", dialogue->line, "\n"); if (dialogue == ass->last_added_dialogue) ass->last_added_dialogue = next; av_freep(&dialogue->line); @@ -157,6 +153,7 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) ASSContext *ass = s->priv_data; long int layer; + int text_len; char *p = pkt->data; int64_t start = pkt->pts; int64_t end = start + pkt->duration; @@ -187,9 +184,13 @@ static int write_packet(AVFormatContext *s, AVPacket *pkt) if (hh1 > 9) hh1 = 9, mm1 = 59, ss1 = 59, ms1 = 99; if (hh2 > 9) hh2 = 9, mm2 = 59, ss2 = 59, ms2 = 99; - dialogue->line = av_asprintf("%s%ld,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%s", + text_len = strlen(p); + while (text_len > 0 && p[text_len - 1] == '\r' || p[text_len - 1] == '\n') + text_len--; + + dialogue->line = av_asprintf("%s%ld,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%.*s", ass->ssa_mode ? "Marked=" : "", - layer, hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, p); + layer, hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, text_len, p); if (!dialogue->line) { av_free(dialogue); return AVERROR(ENOMEM); @@ -207,7 +208,7 @@ static int write_trailer(AVFormatContext *s) purge_dialogues(s, 1); if (ass->trailer) { - avio_write(s->pb, ass->trailer, ass->trailer_size); + ffio_write_lines(s->pb, ass->trailer, ass->trailer_size, NULL); } return 0; @@ -232,8 +233,12 @@ const FFOutputFormat ff_ass_muxer = { .p.long_name = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"), .p.mime_type = "text/x-ass", .p.extensions = "ass,ssa", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_NONE, .p.subtitle_codec = AV_CODEC_ID_ASS, .p.flags = AVFMT_GLOBALHEADER | AVFMT_NOTIMESTAMPS | AVFMT_TS_NONSTRICT, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .p.priv_class = &ass_class, .priv_data_size = sizeof(ASSContext), .write_header = write_header, diff --git a/libavformat/astdec.c b/libavformat/astdec.c index f812f6437cf..7185e27fd40 100644 --- a/libavformat/astdec.c +++ b/libavformat/astdec.c @@ -22,6 +22,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "ast.h" @@ -111,13 +112,13 @@ static int ast_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_ast_demuxer = { - .name = "ast", - .long_name = NULL_IF_CONFIG_SMALL("AST (Audio Stream)"), +const FFInputFormat ff_ast_demuxer = { + .p.name = "ast", + .p.long_name = NULL_IF_CONFIG_SMALL("AST (Audio Stream)"), + .p.extensions = "ast", + .p.flags = AVFMT_GENERIC_INDEX, + .p.codec_tag = ff_ast_codec_tags_list, .read_probe = ast_probe, .read_header = ast_read_header, .read_packet = ast_read_packet, - .extensions = "ast", - .flags = AVFMT_GENERIC_INDEX, - .codec_tag = ff_ast_codec_tags_list, }; diff --git a/libavformat/astenc.c b/libavformat/astenc.c index 9dd388040f9..1fa06a2da87 100644 --- a/libavformat/astenc.c +++ b/libavformat/astenc.c @@ -49,16 +49,9 @@ static int ast_write_header(AVFormatContext *s) { ASTMuxContext *ast = s->priv_data; AVIOContext *pb = s->pb; - AVCodecParameters *par; + AVCodecParameters *par = s->streams[0]->codecpar; unsigned int codec_tag; - if (s->nb_streams == 1) { - par = s->streams[0]->codecpar; - } else { - av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); - return AVERROR(EINVAL); - } - if (par->codec_id == AV_CODEC_ID_ADPCM_AFC) { av_log(s, AV_LOG_ERROR, "muxing ADPCM AFC is not implemented\n"); return AVERROR_PATCHWELCOME; @@ -143,14 +136,16 @@ static int ast_write_trailer(AVFormatContext *s) /* Loopstart if provided */ if (ast->loopstart > 0) { - if (ast->loopstart >= samples) { - av_log(s, AV_LOG_WARNING, "Loopstart value is out of range and will be ignored\n"); - ast->loopstart = -1; - avio_skip(pb, 4); - } else - avio_wb32(pb, ast->loopstart); - } else + if (ast->loopstart >= samples) { + av_log(s, AV_LOG_WARNING, "Loopstart value is out of range and will be ignored\n"); + ast->loopstart = -1; + avio_skip(pb, 4); + } else { + avio_wb32(pb, ast->loopstart); + } + } else { avio_skip(pb, 4); + } /* Loopend if provided. Otherwise number of samples again */ if (ast->loopend && ast->loopstart >= 0) { @@ -202,6 +197,8 @@ const FFOutputFormat ff_ast_muxer = { .priv_data_size = sizeof(ASTMuxContext), .p.audio_codec = AV_CODEC_ID_PCM_S16BE_PLANAR, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH, .write_header = ast_write_header, .write_packet = ast_write_packet, .write_trailer = ast_write_trailer, diff --git a/libavformat/au.c b/libavformat/au.c index 3bf21502584..98f1a5b507a 100644 --- a/libavformat/au.c +++ b/libavformat/au.c @@ -30,7 +30,9 @@ #include "config_components.h" #include "libavutil/bprint.h" +#include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "avio_internal.h" #include "mux.h" @@ -63,11 +65,15 @@ static const AVCodecTag *const au_codec_tags[] = { codec_au_tags, NULL }; static int au_probe(const AVProbeData *p) { - if (p->buf[0] == '.' && p->buf[1] == 's' && - p->buf[2] == 'n' && p->buf[3] == 'd') - return AVPROBE_SCORE_MAX; - else + if (p->buf_size < 24 || + AV_RL32(p->buf) != MKTAG('.', 's', 'n', 'd') || + AV_RN32(p->buf+4) == 0 || + AV_RN32(p->buf+8) == 0 || + AV_RN32(p->buf+12) == 0 || + AV_RN32(p->buf+16) == 0 || + AV_RN32(p->buf+20) == 0) return 0; + return AVPROBE_SCORE_MAX; } static int au_read_annotation(AVFormatContext *s, int size) @@ -230,14 +236,14 @@ static int au_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_au_demuxer = { - .name = "au", - .long_name = NULL_IF_CONFIG_SMALL("Sun AU"), +const FFInputFormat ff_au_demuxer = { + .p.name = "au", + .p.long_name = NULL_IF_CONFIG_SMALL("Sun AU"), + .p.codec_tag = au_codec_tags, .read_probe = au_probe, .read_header = au_read_header, .read_packet = ff_pcm_read_packet, .read_seek = ff_pcm_read_seek, - .codec_tag = au_codec_tags, }; #endif /* CONFIG_AU_DEMUXER */ @@ -285,11 +291,6 @@ static int au_write_header(AVFormatContext *s) AVCodecParameters *par = s->streams[0]->codecpar; AVBPrint annotations; - if (s->nb_streams != 1) { - av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); - return AVERROR(EINVAL); - } - par->codec_tag = ff_codec_get_tag(codec_au_tags, par->codec_id); if (!par->codec_tag) { av_log(s, AV_LOG_ERROR, "unsupported codec\n"); @@ -340,7 +341,9 @@ const FFOutputFormat ff_au_muxer = { .p.codec_tag = au_codec_tags, .p.audio_codec = AV_CODEC_ID_PCM_S16BE, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, .p.flags = AVFMT_NOTIMESTAMPS, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH, .priv_data_size = sizeof(AUContext), .write_header = au_write_header, .write_packet = ff_raw_write_packet, diff --git a/libavformat/av1.c b/libavformat/av1.c index b6eaf506278..cb86e66d09f 100644 --- a/libavformat/av1.c +++ b/libavformat/av1.c @@ -23,7 +23,7 @@ #include "libavutil/mem.h" #include "libavcodec/av1.h" #include "libavcodec/av1_parse.h" -#include "libavcodec/avcodec.h" +#include "libavcodec/defs.h" #include "libavcodec/put_bits.h" #include "av1.h" #include "avio.h" @@ -107,7 +107,7 @@ int ff_av1_filter_obus_buf(const uint8_t *in, uint8_t **out, if (!buf) return AVERROR(ENOMEM); - ffio_init_context(&pb, buf, len, 1, NULL, NULL, NULL, NULL); + ffio_init_write_context(&pb, buf, len); ret = av1_filter_obus(&pb.pub, in, *size, NULL); av_assert1(ret == len); @@ -141,12 +141,12 @@ static int parse_color_config(AV1SequenceParameters *seq_params, GetBitContext * { int twelve_bit = 0; int high_bitdepth = get_bits1(gb); - if (seq_params->profile == FF_PROFILE_AV1_PROFESSIONAL && high_bitdepth) + if (seq_params->profile == AV_PROFILE_AV1_PROFESSIONAL && high_bitdepth) twelve_bit = get_bits1(gb); seq_params->bitdepth = 8 + (high_bitdepth * 2) + (twelve_bit * 2); - if (seq_params->profile == FF_PROFILE_AV1_HIGH) + if (seq_params->profile == AV_PROFILE_AV1_HIGH) seq_params->monochrome = 0; else seq_params->monochrome = get_bits1(gb); @@ -176,10 +176,10 @@ static int parse_color_config(AV1SequenceParameters *seq_params, GetBitContext * } else { seq_params->color_range = get_bits1(gb); - if (seq_params->profile == FF_PROFILE_AV1_MAIN) { + if (seq_params->profile == AV_PROFILE_AV1_MAIN) { seq_params->chroma_subsampling_x = 1; seq_params->chroma_subsampling_y = 1; - } else if (seq_params->profile == FF_PROFILE_AV1_HIGH) { + } else if (seq_params->profile == AV_PROFILE_AV1_HIGH) { seq_params->chroma_subsampling_x = 0; seq_params->chroma_subsampling_y = 0; } else { diff --git a/libavformat/av1dec.c b/libavformat/av1dec.c index d4b430af7e1..3363003b181 100644 --- a/libavformat/av1dec.c +++ b/libavformat/av1dec.c @@ -23,11 +23,11 @@ #include "libavutil/common.h" #include "libavutil/opt.h" -#include "libavcodec/avcodec.h" #include "libavcodec/av1_parse.h" #include "libavcodec/bsf.h" #include "avformat.h" #include "avio_internal.h" +#include "demux.h" #include "internal.h" typedef struct AV1DemuxContext { @@ -80,7 +80,7 @@ static int av1_read_header(AVFormatContext *s) st->codecpar->codec_id = AV_CODEC_ID_AV1; sti->need_parsing = AVSTREAM_PARSE_HEADERS; - sti->avctx->framerate = c->framerate; + st->avg_frame_rate = c->framerate; // taken from rawvideo demuxers avpriv_set_pts_info(st, 64, 1, 1200000); @@ -124,13 +124,16 @@ static const AVClass av1_demuxer_class = { #if CONFIG_AV1_DEMUXER -static int leb(AVIOContext *pb, uint32_t *len) { +static int leb(AVIOContext *pb, uint32_t *len, int eof) { int more, i = 0; - uint8_t byte; *len = 0; do { unsigned bits; - byte = avio_r8(pb); + int byte = avio_r8(pb); + if (pb->error) + return pb->error; + if (pb->eof_reached) + return (eof && !i) ? AVERROR_EOF : AVERROR_INVALIDDATA; more = byte & 0x80; bits = byte & 0x7f; if (i <= 3 || (i == 4 && bits < (1 << 4))) @@ -139,8 +142,6 @@ static int leb(AVIOContext *pb, uint32_t *len) { return AVERROR_INVALIDDATA; if (++i == 8 && more) return AVERROR_INVALIDDATA; - if (pb->eof_reached || pb->error) - return pb->error ? pb->error : AVERROR(EIO); } while (more); return i; } @@ -167,18 +168,17 @@ static int annexb_probe(const AVProbeData *p) int seq = 0; int ret, type, cnt = 0; - ffio_init_context(&ctx, p->buf, p->buf_size, 0, - NULL, NULL, NULL, NULL); + ffio_init_read_context(&ctx, p->buf, p->buf_size); - ret = leb(pb, &temporal_unit_size); + ret = leb(pb, &temporal_unit_size, 1); if (ret < 0) return 0; cnt += ret; - ret = leb(pb, &frame_unit_size); + ret = leb(pb, &frame_unit_size, 0); if (ret < 0 || ((int64_t)frame_unit_size + ret) > temporal_unit_size) return 0; cnt += ret; - ret = leb(pb, &obu_unit_size); + ret = leb(pb, &obu_unit_size, 0); if (ret < 0 || ((int64_t)obu_unit_size + ret) >= frame_unit_size) return 0; cnt += ret; @@ -196,7 +196,7 @@ static int annexb_probe(const AVProbeData *p) cnt += obu_unit_size; do { - ret = leb(pb, &obu_unit_size); + ret = leb(pb, &obu_unit_size, 0); if (ret < 0 || ((int64_t)obu_unit_size + ret) > frame_unit_size) return 0; cnt += ret; @@ -229,31 +229,36 @@ static int annexb_read_packet(AVFormatContext *s, AVPacket *pkt) retry: if (avio_feof(s->pb)) { if (c->temporal_unit_size || c->frame_unit_size) - return AVERROR(EIO); + return AVERROR_INVALIDDATA; goto end; } if (!c->temporal_unit_size) { - len = leb(s->pb, &c->temporal_unit_size); - if (len < 0) return AVERROR_INVALIDDATA; + len = leb(s->pb, &c->temporal_unit_size, 1); + if (len == AVERROR_EOF) goto end; + else if (len < 0) return len; } if (!c->frame_unit_size) { - len = leb(s->pb, &c->frame_unit_size); - if (len < 0 || ((int64_t)c->frame_unit_size + len) > c->temporal_unit_size) + len = leb(s->pb, &c->frame_unit_size, 0); + if (len < 0) + return len; + if (((int64_t)c->frame_unit_size + len) > c->temporal_unit_size) return AVERROR_INVALIDDATA; c->temporal_unit_size -= len; } - len = leb(s->pb, &obu_unit_size); - if (len < 0 || ((int64_t)obu_unit_size + len) > c->frame_unit_size) + len = leb(s->pb, &obu_unit_size, 0); + if (len < 0) + return len; + if (((int64_t)obu_unit_size + len) > c->frame_unit_size) return AVERROR_INVALIDDATA; ret = av_get_packet(s->pb, pkt, obu_unit_size); if (ret < 0) return ret; if (ret != obu_unit_size) - return AVERROR(EIO); + return AVERROR_INVALIDDATA; c->temporal_unit_size -= obu_unit_size + len; c->frame_unit_size -= obu_unit_size + len; @@ -277,18 +282,18 @@ static int annexb_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_av1_demuxer = { - .name = "av1", - .long_name = NULL_IF_CONFIG_SMALL("AV1 Annex B"), +const FFInputFormat ff_av1_demuxer = { + .p.name = "av1", + .p.long_name = NULL_IF_CONFIG_SMALL("AV1 Annex B"), + .p.extensions = "obu", + .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS, + .p.priv_class = &av1_demuxer_class, .priv_data_size = sizeof(AV1DemuxContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = annexb_probe, .read_header = av1_read_header, .read_packet = annexb_read_packet, .read_close = av1_read_close, - .extensions = "obu", - .flags = AVFMT_GENERIC_INDEX, - .priv_class = &av1_demuxer_class, }; #endif @@ -321,7 +326,7 @@ static int read_obu_with_size(const uint8_t *buf, int buf_size, int64_t *obu_siz skip_bits(&gb, 3); // extension_header_reserved_3bits } - *obu_size = leb128(&gb); + *obu_size = get_leb128(&gb); if (*obu_size > INT_MAX) return AVERROR_INVALIDDATA; @@ -422,17 +427,17 @@ static int obu_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_obu_demuxer = { - .name = "obu", - .long_name = NULL_IF_CONFIG_SMALL("AV1 low overhead OBU"), +const FFInputFormat ff_obu_demuxer = { + .p.name = "obu", + .p.long_name = NULL_IF_CONFIG_SMALL("AV1 low overhead OBU"), + .p.extensions = "obu", + .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS, + .p.priv_class = &av1_demuxer_class, .priv_data_size = sizeof(AV1DemuxContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = obu_probe, .read_header = av1_read_header, .read_packet = obu_read_packet, .read_close = av1_read_close, - .extensions = "obu", - .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK, - .priv_class = &av1_demuxer_class, }; #endif diff --git a/libavformat/avformat.c b/libavformat/avformat.c index 708d90b38cc..140fb5b6aa2 100644 --- a/libavformat/avformat.c +++ b/libavformat/avformat.c @@ -23,12 +23,15 @@ #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/channel_layout.h" +#include "libavutil/frame.h" +#include "libavutil/iamf.h" #include "libavutil/intreadwrite.h" #include "libavutil/mem.h" #include "libavutil/opt.h" #include "libavutil/pixfmt.h" #include "libavutil/samplefmt.h" #include "libavcodec/avcodec.h" +#include "libavcodec/codec.h" #include "libavcodec/bsf.h" #include "libavcodec/codec_desc.h" #include "libavcodec/packet_internal.h" @@ -46,9 +49,13 @@ void ff_free_stream(AVStream **pst) if (!st) return; +#if FF_API_AVSTREAM_SIDE_DATA +FF_DISABLE_DEPRECATION_WARNINGS for (int i = 0; i < st->nb_side_data; i++) av_freep(&st->side_data[i].data); av_freep(&st->side_data); +FF_ENABLE_DEPRECATION_WARNINGS +#endif if (st->attached_pic.data) av_packet_unref(&st->attached_pic); @@ -56,7 +63,6 @@ void ff_free_stream(AVStream **pst) av_parser_close(sti->parser); avcodec_free_context(&sti->avctx); av_bsf_free(&sti->bsfc); - av_freep(&sti->priv_pts); av_freep(&sti->index_entries); av_freep(&sti->probe_data.buf); @@ -74,6 +80,37 @@ void ff_free_stream(AVStream **pst) av_freep(pst); } +void ff_free_stream_group(AVStreamGroup **pstg) +{ + AVStreamGroup *stg = *pstg; + + if (!stg) + return; + + av_freep(&stg->streams); + av_dict_free(&stg->metadata); + av_freep(&stg->priv_data); + switch (stg->type) { + case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT: { + av_iamf_audio_element_free(&stg->params.iamf_audio_element); + break; + } + case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: { + av_iamf_mix_presentation_free(&stg->params.iamf_mix_presentation); + break; + } + case AV_STREAM_GROUP_PARAMS_TILE_GRID: + av_opt_free(stg->params.tile_grid); + av_freep(&stg->params.tile_grid->offsets); + av_freep(&stg->params.tile_grid); + break; + default: + break; + } + + av_freep(pstg); +} + void ff_remove_stream(AVFormatContext *s, AVStream *st) { av_assert0(s->nb_streams>0); @@ -82,6 +119,14 @@ void ff_remove_stream(AVFormatContext *s, AVStream *st) ff_free_stream(&s->streams[ --s->nb_streams ]); } +void ff_remove_stream_group(AVFormatContext *s, AVStreamGroup *stg) +{ + av_assert0(s->nb_stream_groups > 0); + av_assert0(s->stream_groups[ s->nb_stream_groups - 1 ] == stg); + + ff_free_stream_group(&s->stream_groups[ --s->nb_stream_groups ]); +} + /* XXX: suppress the packet queue */ void ff_flush_packet_queue(AVFormatContext *s) { @@ -112,6 +157,9 @@ void avformat_free_context(AVFormatContext *s) for (unsigned i = 0; i < s->nb_streams; i++) ff_free_stream(&s->streams[i]); + for (unsigned i = 0; i < s->nb_stream_groups; i++) + ff_free_stream_group(&s->stream_groups[i]); + s->nb_stream_groups = 0; s->nb_streams = 0; for (unsigned i = 0; i < s->nb_programs; i++) { @@ -133,11 +181,14 @@ void avformat_free_context(AVFormatContext *s) av_packet_free(&si->pkt); av_packet_free(&si->parse_pkt); av_freep(&s->streams); + av_freep(&s->stream_groups); ff_flush_packet_queue(s); av_freep(&s->url); av_free(s); } +#if FF_API_AVSTREAM_SIDE_DATA +FF_DISABLE_DEPRECATION_WARNINGS uint8_t *av_stream_get_side_data(const AVStream *st, enum AVPacketSideDataType type, size_t *size) { @@ -205,36 +256,8 @@ uint8_t *av_stream_new_side_data(AVStream *st, enum AVPacketSideDataType type, return data; } - -int ff_stream_side_data_copy(AVStream *dst, const AVStream *src) -{ - /* Free existing side data*/ - for (int i = 0; i < dst->nb_side_data; i++) - av_free(dst->side_data[i].data); - av_freep(&dst->side_data); - dst->nb_side_data = 0; - - /* Copy side data if present */ - if (src->nb_side_data) { - dst->side_data = av_calloc(src->nb_side_data, - sizeof(*dst->side_data)); - if (!dst->side_data) - return AVERROR(ENOMEM); - dst->nb_side_data = src->nb_side_data; - - for (int i = 0; i < src->nb_side_data; i++) { - uint8_t *data = av_memdup(src->side_data[i].data, - src->side_data[i].size); - if (!data) - return AVERROR(ENOMEM); - dst->side_data[i].type = src->side_data[i].type; - dst->side_data[i].size = src->side_data[i].size; - dst->side_data[i].data = data; - } - } - - return 0; -} +FF_ENABLE_DEPRECATION_WARNINGS +#endif /** * Copy all stream parameters from source to destination stream, with the @@ -270,10 +293,6 @@ static int stream_params_copy(AVStream *dst, const AVStream *src) if (ret < 0) return ret; - ret = ff_stream_side_data_copy(dst, src); - if (ret < 0) - return ret; - av_packet_unref(&dst->attached_pic); if (src->attached_pic.data) { ret = av_packet_ref(&dst->attached_pic, &src->attached_pic); @@ -302,6 +321,16 @@ AVStream *ff_stream_clone(AVFormatContext *dst_ctx, const AVStream *src) return st; } +const char *avformat_stream_group_name(enum AVStreamGroupParamsType type) +{ + switch(type) { + case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT: return "IAMF Audio Element"; + case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: return "IAMF Mix Presentation"; + case AV_STREAM_GROUP_PARAMS_TILE_GRID: return "Tile Grid"; + } + return NULL; +} + AVProgram *av_new_program(AVFormatContext *ac, int id) { AVProgram *program = NULL; @@ -488,7 +517,7 @@ int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type, */ static int match_stream_specifier(const AVFormatContext *s, const AVStream *st, const char *spec, const char **indexptr, - const AVProgram **p) + const AVStreamGroup **g, const AVProgram **p) { int match = 1; /* Stores if the specifier matches so far. */ while (*spec) { @@ -517,6 +546,46 @@ static int match_stream_specifier(const AVFormatContext *s, const AVStream *st, match = 0; if (nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC)) match = 0; + } else if (*spec == 'g' && *(spec + 1) == ':') { + int64_t group_idx = -1, group_id = -1; + int found = 0; + char *endptr; + spec += 2; + if (*spec == '#' || (*spec == 'i' && *(spec + 1) == ':')) { + spec += 1 + (*spec == 'i'); + group_id = strtol(spec, &endptr, 0); + if (spec == endptr || (*endptr && *endptr++ != ':')) + return AVERROR(EINVAL); + spec = endptr; + } else { + group_idx = strtol(spec, &endptr, 0); + /* Disallow empty id and make sure that if we are not at the end, then another specifier must follow. */ + if (spec == endptr || (*endptr && *endptr++ != ':')) + return AVERROR(EINVAL); + spec = endptr; + } + if (match) { + if (group_id > 0) { + for (unsigned i = 0; i < s->nb_stream_groups; i++) { + if (group_id == s->stream_groups[i]->id) { + group_idx = i; + break; + } + } + } + if (group_idx < 0 || group_idx >= s->nb_stream_groups) + return AVERROR(EINVAL); + for (unsigned j = 0; j < s->stream_groups[group_idx]->nb_streams; j++) { + if (st->index == s->stream_groups[group_idx]->streams[j]->index) { + found = 1; + if (g) + *g = s->stream_groups[group_idx]; + break; + } + } + } + if (!found) + match = 0; } else if (*spec == 'p' && *(spec + 1) == ':') { int prog_id; int found = 0; @@ -615,10 +684,11 @@ int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st, int ret, index; char *endptr; const char *indexptr = NULL; + const AVStreamGroup *g = NULL; const AVProgram *p = NULL; int nb_streams; - ret = match_stream_specifier(s, st, spec, &indexptr, &p); + ret = match_stream_specifier(s, st, spec, &indexptr, &g, &p); if (ret < 0) goto error; @@ -636,10 +706,11 @@ int avformat_match_stream_specifier(AVFormatContext *s, AVStream *st, return (index == st->index); /* If we requested a matching stream index, we have to ensure st is that. */ - nb_streams = p ? p->nb_stream_indexes : s->nb_streams; + nb_streams = g ? g->nb_streams : (p ? p->nb_stream_indexes : s->nb_streams); for (int i = 0; i < nb_streams && index >= 0; i++) { - const AVStream *candidate = s->streams[p ? p->stream_index[i] : i]; - ret = match_stream_specifier(s, candidate, spec, NULL, NULL); + unsigned idx = g ? g->streams[i]->index : (p ? p->stream_index[i] : i); + const AVStream *candidate = s->streams[idx]; + ret = match_stream_specifier(s, candidate, spec, NULL, NULL, NULL); if (ret < 0) goto error; if (ret > 0 && index-- == 0 && st == candidate) @@ -679,8 +750,7 @@ AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *strea AVRational av_guess_frame_rate(AVFormatContext *format, AVStream *st, AVFrame *frame) { AVRational fr = st->r_frame_rate; - AVCodecContext *const avctx = ffstream(st)->avctx; - AVRational codec_fr = avctx->framerate; + const AVCodecDescriptor *desc = cffstream(st)->codec_desc; AVRational avg_fr = st->avg_frame_rate; if (avg_fr.num > 0 && avg_fr.den > 0 && fr.num > 0 && fr.den > 0 && @@ -688,7 +758,10 @@ AVRational av_guess_frame_rate(AVFormatContext *format, AVStream *st, AVFrame *f fr = avg_fr; } - if (avctx->ticks_per_frame > 1) { + if (desc && (desc->props & AV_CODEC_PROP_FIELDS)) { + const AVCodecContext *const avctx = ffstream(st)->avctx; + AVRational codec_fr = avctx->framerate; + if ( codec_fr.num > 0 && codec_fr.den > 0 && (fr.num == 0 || av_q2d(codec_fr) < av_q2d(fr)*0.7 && fabs(1.0 - av_q2d(av_div_q(avg_fr, fr))) > 0.1)) fr = codec_fr; @@ -701,14 +774,21 @@ int avformat_transfer_internal_stream_timing_info(const AVOutputFormat *ofmt, AVStream *ost, const AVStream *ist, enum AVTimebaseSource copy_tb) { + const AVCodecDescriptor *desc = cffstream(ist)->codec_desc; const AVCodecContext *const dec_ctx = cffstream(ist)->avctx; - AVCodecContext *const enc_ctx = ffstream(ost)->avctx; - AVRational dec_ctx_tb = dec_ctx->framerate.num ? av_inv_q(av_mul_q(dec_ctx->framerate, - (AVRational){dec_ctx->ticks_per_frame, 1})) + + AVRational mul = (AVRational){ desc && (desc->props & AV_CODEC_PROP_FIELDS) ? 2 : 1, 1 }; + AVRational dec_ctx_framerate = dec_ctx ? dec_ctx->framerate : (AVRational){ 0, 0 }; + AVRational dec_ctx_tb = dec_ctx_framerate.num ? av_inv_q(av_mul_q(dec_ctx_framerate, mul)) : (ist->codecpar->codec_type == AVMEDIA_TYPE_AUDIO ? (AVRational){0, 1} : ist->time_base); + AVRational enc_tb = ist->time_base; +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + int ticks_per_frame = dec_ctx ? dec_ctx->ticks_per_frame : 1; +FF_ENABLE_DEPRECATION_WARNINGS +#endif - enc_ctx->time_base = ist->time_base; /* * Avi is a special case here because it supports variable fps but * having the fps and timebase differe significantly adds quite some @@ -722,50 +802,52 @@ int avformat_transfer_internal_stream_timing_info(const AVOutputFormat *ofmt, && 0.5/av_q2d(ist->r_frame_rate) > av_q2d(dec_ctx_tb) && av_q2d(ist->time_base) < 1.0/500 && av_q2d(dec_ctx_tb) < 1.0/500 || copy_tb == AVFMT_TBCF_R_FRAMERATE) { - enc_ctx->time_base.num = ist->r_frame_rate.den; - enc_ctx->time_base.den = 2*ist->r_frame_rate.num; - enc_ctx->ticks_per_frame = 2; + enc_tb.num = ist->r_frame_rate.den; + enc_tb.den = 2*ist->r_frame_rate.num; } else #endif - if (copy_tb == AVFMT_TBCF_AUTO && dec_ctx->framerate.num && - av_q2d(av_inv_q(dec_ctx->framerate)) > 2*av_q2d(ist->time_base) + if (copy_tb == AVFMT_TBCF_AUTO && dec_ctx_framerate.num && + av_q2d(av_inv_q(dec_ctx_framerate)) > 2*av_q2d(ist->time_base) && av_q2d(ist->time_base) < 1.0/500 || (copy_tb == AVFMT_TBCF_DECODER && - (dec_ctx->framerate.num || ist->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))) { - enc_ctx->time_base = dec_ctx_tb; - enc_ctx->time_base.num *= dec_ctx->ticks_per_frame; - enc_ctx->time_base.den *= 2; - enc_ctx->ticks_per_frame = 2; + (dec_ctx_framerate.num || ist->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))) { + enc_tb = dec_ctx_tb; + enc_tb.den *= 2; +#if FF_API_TICKS_PER_FRAME + enc_tb.num *= ticks_per_frame; +#endif } } else if (!(ofmt->flags & AVFMT_VARIABLE_FPS) && !av_match_name(ofmt->name, "mov,mp4,3gp,3g2,psp,ipod,ismv,f4v")) { - if (copy_tb == AVFMT_TBCF_AUTO && dec_ctx->framerate.num - && av_q2d(av_inv_q(dec_ctx->framerate)) > av_q2d(ist->time_base) + if (copy_tb == AVFMT_TBCF_AUTO && dec_ctx_framerate.num + && av_q2d(av_inv_q(dec_ctx_framerate)) > av_q2d(ist->time_base) && av_q2d(ist->time_base) < 1.0/500 || (copy_tb == AVFMT_TBCF_DECODER && - (dec_ctx->framerate.num || ist->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))) { - enc_ctx->time_base = dec_ctx_tb; - enc_ctx->time_base.num *= dec_ctx->ticks_per_frame; + (dec_ctx_framerate.num || ist->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))) { + enc_tb = dec_ctx_tb; +#if FF_API_TICKS_PER_FRAME + enc_tb.num *= ticks_per_frame; +#endif } } - if ((enc_ctx->codec_tag == AV_RL32("tmcd") || ost->codecpar->codec_tag == AV_RL32("tmcd")) + if (ost->codecpar->codec_tag == AV_RL32("tmcd") && dec_ctx_tb.num < dec_ctx_tb.den && dec_ctx_tb.num > 0 && 121LL*dec_ctx_tb.num > dec_ctx_tb.den) { - enc_ctx->time_base = dec_ctx_tb; + enc_tb = dec_ctx_tb; } - av_reduce(&enc_ctx->time_base.num, &enc_ctx->time_base.den, - enc_ctx->time_base.num, enc_ctx->time_base.den, INT_MAX); + av_reduce(&ffstream(ost)->transferred_mux_tb.num, + &ffstream(ost)->transferred_mux_tb.den, + enc_tb.num, enc_tb.den, INT_MAX); return 0; } AVRational av_stream_get_codec_timebase(const AVStream *st) { - // See avformat_transfer_internal_stream_timing_info() TODO. - return cffstream(st)->avctx->time_base; + return cffstream(st)->avctx ? cffstream(st)->avctx->time_base : cffstream(st)->transferred_mux_tb; } void avpriv_set_pts_info(AVStream *st, int pts_wrap_bits, @@ -790,7 +872,8 @@ void avpriv_set_pts_info(AVStream *st, int pts_wrap_bits, return; } st->time_base = new_tb; - sti->avctx->pkt_timebase = new_tb; + if (sti->avctx) + sti->avctx->pkt_timebase = new_tb; st->pts_wrap_bits = pts_wrap_bits; } @@ -814,20 +897,28 @@ const AVCodec *ff_find_decoder(AVFormatContext *s, const AVStream *st, int ff_copy_whiteblacklists(AVFormatContext *dst, const AVFormatContext *src) { +#define OFF(field) offsetof(AVFormatContext, field) + static const unsigned offsets[] = { + OFF(codec_whitelist), OFF(format_whitelist), + OFF(protocol_whitelist), OFF(protocol_blacklist), + }; +#undef OFF av_assert0(!dst->codec_whitelist && !dst->format_whitelist && !dst->protocol_whitelist && !dst->protocol_blacklist); - dst-> codec_whitelist = av_strdup(src->codec_whitelist); - dst->format_whitelist = av_strdup(src->format_whitelist); - dst->protocol_whitelist = av_strdup(src->protocol_whitelist); - dst->protocol_blacklist = av_strdup(src->protocol_blacklist); - if ( (src-> codec_whitelist && !dst-> codec_whitelist) - || (src-> format_whitelist && !dst-> format_whitelist) - || (src->protocol_whitelist && !dst->protocol_whitelist) - || (src->protocol_blacklist && !dst->protocol_blacklist)) { - av_log(dst, AV_LOG_ERROR, "Failed to duplicate black/whitelist\n"); - return AVERROR(ENOMEM); + for (unsigned i = 0; i < FF_ARRAY_ELEMS(offsets); i++) { + const char *src_str = *(char *const*)((const char*)src + offsets[i]); + + if (src_str) { + char *dst_str = av_strdup(src_str); + if (!dst_str) { + av_log(dst, AV_LOG_ERROR, "Failed to duplicate black/whitelist\n"); + return AVERROR(ENOMEM); + } + + *(char **)((char*)dst + offsets[i]) = dst_str; + } } return 0; } @@ -853,18 +944,8 @@ void ff_format_set_url(AVFormatContext *s, char *url) int ff_format_io_close(AVFormatContext *s, AVIOContext **pb) { int ret = 0; - if (*pb) { -#if FF_API_AVFORMAT_IO_CLOSE -FF_DISABLE_DEPRECATION_WARNINGS - if (s->io_close == ff_format_io_close_default || s->io_close == NULL) -#endif - ret = s->io_close2(s, *pb); -#if FF_API_AVFORMAT_IO_CLOSE - else - s->io_close(s, *pb); -FF_ENABLE_DEPRECATION_WARNINGS -#endif - } + if (*pb) + ret = s->io_close2(s, *pb); *pb = NULL; return ret; } diff --git a/libavformat/avformat.h b/libavformat/avformat.h index 1916aa2dc55..de403976760 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -307,10 +307,8 @@ * @} */ -#include #include /* FILE */ -#include "libavcodec/codec.h" #include "libavcodec/codec_par.h" #include "libavcodec/defs.h" #include "libavcodec/packet.h" @@ -325,11 +323,13 @@ * to avoid unnecessary rebuilds. When included externally, keep including * the full version information. */ #include "libavformat/version.h" + +#include "libavutil/frame.h" +#include "libavcodec/codec.h" #endif struct AVFormatContext; - -struct AVDeviceInfoList; +struct AVFrame; /** * @defgroup metadata_api Public Metadata API @@ -485,7 +485,9 @@ typedef struct AVProbeData { #define AVFMT_NOBINSEARCH 0x2000 /**< Format does not allow to fall back on binary search via read_timestamp */ #define AVFMT_NOGENSEARCH 0x4000 /**< Format does not allow to fall back on generic search */ #define AVFMT_NO_BYTE_SEEK 0x8000 /**< Format does not allow seeking by bytes */ -#define AVFMT_ALLOW_FLUSH 0x10000 /**< Format allows flushing. If not set, the muxer will not receive a NULL packet in the write_packet function. */ +#if FF_API_ALLOW_FLUSH +#define AVFMT_ALLOW_FLUSH 0x10000 /**< @deprecated: Just send a NULL packet if you want to flush a muxer. */ +#endif #define AVFMT_TS_NONSTRICT 0x20000 /**< Format does not require strictly increasing timestamps, but they must still be monotonic */ @@ -521,7 +523,7 @@ typedef struct AVOutputFormat { /** * can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, * AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_VARIABLE_FPS, - * AVFMT_NODIMENSIONS, AVFMT_NOSTREAMS, AVFMT_ALLOW_FLUSH, + * AVFMT_NODIMENSIONS, AVFMT_NOSTREAMS, * AVFMT_TS_NONSTRICT, AVFMT_TS_NEGATIVE */ int flags; @@ -581,103 +583,6 @@ typedef struct AVInputFormat { * @see av_probe_input_format2 */ const char *mime_type; - - /***************************************************************** - * No fields below this line are part of the public API. They - * may not be used outside of libavformat and can be changed and - * removed at will. - * New public fields should be added right above. - ***************************************************************** - */ - /** - * Raw demuxers store their codec ID here. - */ - int raw_codec_id; - - /** - * Size of private data so that it can be allocated in the wrapper. - */ - int priv_data_size; - - /** - * Internal flags. See FF_FMT_FLAG_* in internal.h. - */ - int flags_internal; - - /** - * Tell if a given file has a chance of being parsed as this format. - * The buffer provided is guaranteed to be AVPROBE_PADDING_SIZE bytes - * big so you do not have to check for that unless you need more. - */ - int (*read_probe)(const AVProbeData *); - - /** - * Read the format header and initialize the AVFormatContext - * structure. Return 0 if OK. 'avformat_new_stream' should be - * called to create new streams. - */ - int (*read_header)(struct AVFormatContext *); - - /** - * Read one packet and put it in 'pkt'. pts and flags are also - * set. 'avformat_new_stream' can be called only if the flag - * AVFMTCTX_NOHEADER is used and only in the calling thread (not in a - * background thread). - * @return 0 on success, < 0 on error. - * Upon returning an error, pkt must be unreferenced by the caller. - */ - int (*read_packet)(struct AVFormatContext *, AVPacket *pkt); - - /** - * Close the stream. The AVFormatContext and AVStreams are not - * freed by this function - */ - int (*read_close)(struct AVFormatContext *); - - /** - * Seek to a given timestamp relative to the frames in - * stream component stream_index. - * @param stream_index Must not be -1. - * @param flags Selects which direction should be preferred if no exact - * match is available. - * @return >= 0 on success (but not necessarily the new offset) - */ - int (*read_seek)(struct AVFormatContext *, - int stream_index, int64_t timestamp, int flags); - - /** - * Get the next timestamp in stream[stream_index].time_base units. - * @return the timestamp or AV_NOPTS_VALUE if an error occurred - */ - int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index, - int64_t *pos, int64_t pos_limit); - - /** - * Start/resume playing - only meaningful if using a network-based format - * (RTSP). - */ - int (*read_play)(struct AVFormatContext *); - - /** - * Pause playing - only meaningful if using a network-based format - * (RTSP). - */ - int (*read_pause)(struct AVFormatContext *); - - /** - * Seek to timestamp ts. - * Seeking will be done so that the point from which all active streams - * can be presented successfully will be closest to ts and within min/max_ts. - * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL. - */ - int (*read_seek2)(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags); - - /** - * Returns device list with it properties. - * @see avdevice_list_devices() for more details. - */ - int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list); - } AVInputFormat; /** * @} @@ -799,9 +704,9 @@ typedef struct AVIndexEntry { */ #define AV_DISPOSITION_METADATA (1 << 18) /** - * The audio stream is intended to be mixed with another stream before - * presentation. - * Corresponds to mix_type=0 in mpegts. + * The stream is intended to be mixed with another stream before presentation. + * Used for example to signal the stream contains an image part of a HEIF grid, + * or for mix_type=0 in mpegts. */ #define AV_DISPOSITION_DEPENDENT (1 << 19) /** @@ -937,6 +842,7 @@ typedef struct AVStream { */ AVPacket attached_pic; +#if FF_API_AVSTREAM_SIDE_DATA /** * An array of side data that applies to the whole stream (i.e. the * container does not allow it to change between packets). @@ -953,13 +859,20 @@ typedef struct AVStream { * * Freed by libavformat in avformat_free_context(). * - * @see av_format_inject_global_side_data() + * @deprecated use AVStream's @ref AVCodecParameters.coded_side_data + * "codecpar side data". */ + attribute_deprecated AVPacketSideData *side_data; /** * The number of elements in the AVStream.side_data array. + * + * @deprecated use AVStream's @ref AVCodecParameters.nb_coded_side_data + * "codecpar side data". */ + attribute_deprecated int nb_side_data; +#endif /** * Flags indicating events happening on the stream, a combination of @@ -1007,17 +920,255 @@ typedef struct AVStream { int pts_wrap_bits; } AVStream; -struct AVCodecParserContext *av_stream_get_parser(const AVStream *s); - -#if FF_API_GET_END_PTS /** - * Returns the pts of the last muxed packet + its duration + * AVStreamGroupTileGrid holds information on how to combine several + * independent images on a single canvas for presentation. + * + * The output should be a @ref AVStreamGroupTileGrid.background "background" + * colored @ref AVStreamGroupTileGrid.coded_width "coded_width" x + * @ref AVStreamGroupTileGrid.coded_height "coded_height" canvas where a + * @ref AVStreamGroupTileGrid.nb_tiles "nb_tiles" amount of tiles are placed in + * the order they appear in the @ref AVStreamGroupTileGrid.offsets "offsets" + * array, at the exact offset described for them. In particular, if two or more + * tiles overlap, the image with higher index in the + * @ref AVStreamGroupTileGrid.offsets "offsets" array takes priority. + * Note that a single image may be used multiple times, i.e. multiple entries + * in @ref AVStreamGroupTileGrid.offsets "offsets" may have the same value of + * idx. + * + * The following is an example of a simple grid with 3 rows and 4 columns: + * + * +---+---+---+---+ + * | 0 | 1 | 2 | 3 | + * +---+---+---+---+ + * | 4 | 5 | 6 | 7 | + * +---+---+---+---+ + * | 8 | 9 |10 |11 | + * +---+---+---+---+ + * + * Assuming all tiles have a dimension of 512x512, the + * @ref AVStreamGroupTileGrid.offsets "offset" of the topleft pixel of + * the first @ref AVStreamGroup.streams "stream" in the group is "0,0", the + * @ref AVStreamGroupTileGrid.offsets "offset" of the topleft pixel of + * the second @ref AVStreamGroup.streams "stream" in the group is "512,0", the + * @ref AVStreamGroupTileGrid.offsets "offset" of the topleft pixel of + * the fifth @ref AVStreamGroup.streams "stream" in the group is "0,512", the + * @ref AVStreamGroupTileGrid.offsets "offset", of the topleft pixel of + * the sixth @ref AVStreamGroup.streams "stream" in the group is "512,512", + * etc. * - * the retuned value is undefined when used with a demuxer. - */ -attribute_deprecated -int64_t av_stream_get_end_pts(const AVStream *st); -#endif + * The following is an example of a canvas with overlaping tiles: + * + * +-----------+ + * | %%%%% | + * |***%%3%%@@@| + * |**0%%%%%2@@| + * |***##1@@@@@| + * | ##### | + * +-----------+ + * + * Assuming a canvas with size 1024x1024 and all tiles with a dimension of + * 512x512, a possible @ref AVStreamGroupTileGrid.offsets "offset" for the + * topleft pixel of the first @ref AVStreamGroup.streams "stream" in the group + * would be 0x256, the @ref AVStreamGroupTileGrid.offsets "offset" for the + * topleft pixel of the second @ref AVStreamGroup.streams "stream" in the group + * would be 256x512, the @ref AVStreamGroupTileGrid.offsets "offset" for the + * topleft pixel of the third @ref AVStreamGroup.streams "stream" in the group + * would be 512x256, and the @ref AVStreamGroupTileGrid.offsets "offset" for + * the topleft pixel of the fourth @ref AVStreamGroup.streams "stream" in the + * group would be 256x0. + * + * sizeof(AVStreamGroupTileGrid) is not a part of the ABI and may only be + * allocated by avformat_stream_group_create(). + */ +typedef struct AVStreamGroupTileGrid { + const AVClass *av_class; + + /** + * Amount of tiles in the grid. + * + * Must be > 0. + */ + unsigned int nb_tiles; + + /** + * Width of the canvas. + * + * Must be > 0. + */ + int coded_width; + /** + * Width of the canvas. + * + * Must be > 0. + */ + int coded_height; + + /** + * An @ref nb_tiles sized array of offsets in pixels from the topleft edge + * of the canvas, indicating where each stream should be placed. + * It must be allocated with the av_malloc() family of functions. + * + * - demuxing: set by libavformat, must not be modified by the caller. + * - muxing: set by the caller before avformat_write_header(). + * + * Freed by libavformat in avformat_free_context(). + */ + struct { + /** + * Index of the stream in the group this tile references. + * + * Must be < @ref AVStreamGroup.nb_streams "nb_streams". + */ + unsigned int idx; + /** + * Offset in pixels from the left edge of the canvas where the tile + * should be placed. + */ + int horizontal; + /** + * Offset in pixels from the top edge of the canvas where the tile + * should be placed. + */ + int vertical; + } *offsets; + + /** + * The pixel value per channel in RGBA format used if no pixel of any tile + * is located at a particular pixel location. + * + * @see av_image_fill_color(). + * @see av_parse_color(). + */ + uint8_t background[4]; + + /** + * Offset in pixels from the left edge of the canvas where the actual image + * meant for presentation starts. + * + * This field must be >= 0 and < @ref coded_width. + */ + int horizontal_offset; + /** + * Offset in pixels from the top edge of the canvas where the actual image + * meant for presentation starts. + * + * This field must be >= 0 and < @ref coded_height. + */ + int vertical_offset; + + /** + * Width of the final image for presentation. + * + * Must be > 0 and <= (@ref coded_width - @ref horizontal_offset). + * When it's not equal to (@ref coded_width - @ref horizontal_offset), the + * result of (@ref coded_width - width - @ref horizontal_offset) is the + * amount amount of pixels to be cropped from the right edge of the + * final image before presentation. + */ + int width; + /** + * Height of the final image for presentation. + * + * Must be > 0 and <= (@ref coded_height - @ref vertical_offset). + * When it's not equal to (@ref coded_height - @ref vertical_offset), the + * result of (@ref coded_height - height - @ref vertical_offset) is the + * amount amount of pixels to be cropped from the bottom edge of the + * final image before presentation. + */ + int height; +} AVStreamGroupTileGrid; + +enum AVStreamGroupParamsType { + AV_STREAM_GROUP_PARAMS_NONE, + AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT, + AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION, + AV_STREAM_GROUP_PARAMS_TILE_GRID, +}; + +struct AVIAMFAudioElement; +struct AVIAMFMixPresentation; + +typedef struct AVStreamGroup { + /** + * A class for @ref avoptions. Set by avformat_stream_group_create(). + */ + const AVClass *av_class; + + void *priv_data; + + /** + * Group index in AVFormatContext. + */ + unsigned int index; + + /** + * Group type-specific group ID. + * + * decoding: set by libavformat + * encoding: may set by the user + */ + int64_t id; + + /** + * Group type + * + * decoding: set by libavformat on group creation + * encoding: set by avformat_stream_group_create() + */ + enum AVStreamGroupParamsType type; + + /** + * Group type-specific parameters + */ + union { + struct AVIAMFAudioElement *iamf_audio_element; + struct AVIAMFMixPresentation *iamf_mix_presentation; + struct AVStreamGroupTileGrid *tile_grid; + } params; + + /** + * Metadata that applies to the whole group. + * + * - demuxing: set by libavformat on group creation + * - muxing: may be set by the caller before avformat_write_header() + * + * Freed by libavformat in avformat_free_context(). + */ + AVDictionary *metadata; + + /** + * Number of elements in AVStreamGroup.streams. + * + * Set by avformat_stream_group_add_stream() must not be modified by any other code. + */ + unsigned int nb_streams; + + /** + * A list of streams in the group. New entries are created with + * avformat_stream_group_add_stream(). + * + * - demuxing: entries are created by libavformat on group creation. + * If AVFMTCTX_NOHEADER is set in ctx_flags, then new entries may also + * appear in av_read_frame(). + * - muxing: entries are created by the user before avformat_write_header(). + * + * Freed by libavformat in avformat_free_context(). + */ + AVStream **streams; + + /** + * Stream group disposition - a combination of AV_DISPOSITION_* flags. + * This field currently applies to all defined AVStreamGroupParamsType. + * + * - demuxing: set by libavformat when creating the group or in + * avformat_find_stream_info(). + * - muxing: may be set by the caller before avformat_write_header(). + */ + int disposition; +} AVStreamGroup; + +struct AVCodecParserContext *av_stream_get_parser(const AVStream *s); #define AV_PROGRAM_RUNNING 1 @@ -1173,6 +1324,39 @@ typedef struct AVFormatContext { */ AVStream **streams; + /** + * Number of elements in AVFormatContext.stream_groups. + * + * Set by avformat_stream_group_create(), must not be modified by any other code. + */ + unsigned int nb_stream_groups; + /** + * A list of all stream groups in the file. New groups are created with + * avformat_stream_group_create(), and filled with avformat_stream_group_add_stream(). + * + * - demuxing: groups may be created by libavformat in avformat_open_input(). + * If AVFMTCTX_NOHEADER is set in ctx_flags, then new groups may also + * appear in av_read_frame(). + * - muxing: groups may be created by the user before avformat_write_header(). + * + * Freed by libavformat in avformat_free_context(). + */ + AVStreamGroup **stream_groups; + + /** + * Number of chapters in AVChapter array. + * When muxing, chapters are normally written in the file header, + * so nb_chapters should normally be initialized before write_header + * is called. Some muxers (e.g. mov and mkv) can also write chapters + * in the trailer. To write chapters in the trailer, nb_chapters + * must be zero when write_header is called and non-zero when + * write_trailer is called. + * - muxing: set by user + * - demuxing: set by libavformat + */ + unsigned int nb_chapters; + AVChapter **chapters; + /** * input or output URL. Unlike the old filename field, this field has no * length restriction. @@ -1241,7 +1425,9 @@ typedef struct AVFormatContext { #define AVFMT_FLAG_BITEXACT 0x0400 #define AVFMT_FLAG_SORT_DTS 0x10000 ///< try to interleave outputted packets by dts (using this flag can slow demuxing down) #define AVFMT_FLAG_FAST_SEEK 0x80000 ///< Enable fast, but inaccurate seeks for some formats +#if FF_API_LAVF_SHORTEST #define AVFMT_FLAG_SHORTEST 0x100000 ///< Stop muxing when the shortest stream stops. +#endif #define AVFMT_FLAG_AUTO_BSF 0x200000 ///< Add bitstream filters as requested by the muxer /** @@ -1290,36 +1476,10 @@ typedef struct AVFormatContext { enum AVCodecID subtitle_codec_id; /** - * Maximum amount of memory in bytes to use for the index of each stream. - * If the index exceeds this size, entries will be discarded as - * needed to maintain a smaller size. This can lead to slower or less - * accurate seeking (depends on demuxer). - * Demuxers for which a full in-memory index is mandatory will ignore - * this. - * - muxing: unused - * - demuxing: set by user - */ - unsigned int max_index_size; - - /** - * Maximum amount of memory in bytes to use for buffering frames - * obtained from realtime capture devices. - */ - unsigned int max_picture_buffer; - - /** - * Number of chapters in AVChapter array. - * When muxing, chapters are normally written in the file header, - * so nb_chapters should normally be initialized before write_header - * is called. Some muxers (e.g. mov and mkv) can also write chapters - * in the trailer. To write chapters in the trailer, nb_chapters - * must be zero when write_header is called and non-zero when - * write_trailer is called. - * - muxing: set by user - * - demuxing: set by libavformat + * Forced Data codec_id. + * Demuxing: Set by user. */ - unsigned int nb_chapters; - AVChapter **chapters; + enum AVCodecID data_codec_id; /** * Metadata that applies to the whole file. @@ -1375,6 +1535,31 @@ typedef struct AVFormatContext { int debug; #define FF_FDEBUG_TS 0x0001 + /** + * The maximum number of streams. + * - encoding: unused + * - decoding: set by user + */ + int max_streams; + + /** + * Maximum amount of memory in bytes to use for the index of each stream. + * If the index exceeds this size, entries will be discarded as + * needed to maintain a smaller size. This can lead to slower or less + * accurate seeking (depends on demuxer). + * Demuxers for which a full in-memory index is mandatory will ignore + * this. + * - muxing: unused + * - demuxing: set by user + */ + unsigned int max_index_size; + + /** + * Maximum amount of memory in bytes to use for buffering frames + * obtained from realtime capture devices. + */ + unsigned int max_picture_buffer; + /** * Maximum buffering duration for interleaving. * @@ -1393,6 +1578,35 @@ typedef struct AVFormatContext { */ int64_t max_interleave_delta; + /** + * Maximum number of packets to read while waiting for the first timestamp. + * Decoding only. + */ + int max_ts_probe; + + /** + * Max chunk time in microseconds. + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user + * - decoding: unused + */ + int max_chunk_duration; + + /** + * Max chunk size in bytes + * Note, not all formats support this and unpredictable things may happen if it is used when not supported. + * - encoding: Set by user + * - decoding: unused + */ + int max_chunk_size; + + /** + * Maximum number of packets that can be probed + * - encoding: unused + * - decoding: set by user + */ + int max_probe_packets; + /** * Allow non-standard and experimental extension * @see AVCodecContext.strict_std_compliance @@ -1419,11 +1633,6 @@ typedef struct AVFormatContext { */ #define AVFMT_EVENT_FLAG_METADATA_UPDATED 0x0001 - /** - * Maximum number of packets to read while waiting for the first timestamp. - * Decoding only. - */ - int max_ts_probe; /** * Avoid negative timestamps during muxing. @@ -1438,12 +1647,6 @@ typedef struct AVFormatContext { #define AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE 1 ///< Shift timestamps so they are non negative #define AVFMT_AVOID_NEG_TS_MAKE_ZERO 2 ///< Shift timestamps so that they start at 0 - /** - * Transport stream id. - * This will be moved into demuxer private options. Thus no API/ABI compatibility - */ - int ts_id; - /** * Audio preload in microseconds. * Note, not all formats support this and unpredictable things may happen if it is used when not supported. @@ -1452,22 +1655,6 @@ typedef struct AVFormatContext { */ int audio_preload; - /** - * Max chunk time in microseconds. - * Note, not all formats support this and unpredictable things may happen if it is used when not supported. - * - encoding: Set by user - * - decoding: unused - */ - int max_chunk_duration; - - /** - * Max chunk size in bytes - * Note, not all formats support this and unpredictable things may happen if it is used when not supported. - * - encoding: Set by user - * - decoding: unused - */ - int max_chunk_size; - /** * forces the use of wallclock timestamps as pts/dts of packets * This has undefined results in the presence of B frames. @@ -1476,6 +1663,13 @@ typedef struct AVFormatContext { */ int use_wallclock_as_timestamps; + /** + * Skip duration calcuation in estimate_timings_from_pts. + * - encoding: unused + * - decoding: set by user + */ + int skip_estimate_duration_from_pts; + /** * avio flags, used to force AVIO_FLAG_DIRECT. * - encoding: unused @@ -1555,6 +1749,20 @@ typedef struct AVFormatContext { */ char *format_whitelist; + /** + * ',' separated list of allowed protocols. + * - encoding: unused + * - decoding: set by user + */ + char *protocol_whitelist; + + /** + * ',' separated list of disallowed protocols. + * - encoding: unused + * - decoding: set by user + */ + char *protocol_blacklist; + /** * IO repositioned flag. * This is set by avformat when the underlaying IO context read pointer @@ -1569,7 +1777,7 @@ typedef struct AVFormatContext { * the same codec_id. * Demuxing: Set by user */ - const AVCodec *video_codec; + const struct AVCodec *video_codec; /** * Forced audio codec. @@ -1577,7 +1785,7 @@ typedef struct AVFormatContext { * the same codec_id. * Demuxing: Set by user */ - const AVCodec *audio_codec; + const struct AVCodec *audio_codec; /** * Forced subtitle codec. @@ -1585,7 +1793,7 @@ typedef struct AVFormatContext { * the same codec_id. * Demuxing: Set by user */ - const AVCodec *subtitle_codec; + const struct AVCodec *subtitle_codec; /** * Forced data codec. @@ -1593,12 +1801,12 @@ typedef struct AVFormatContext { * the same codec_id. * Demuxing: Set by user */ - const AVCodec *data_codec; + const struct AVCodec *data_codec; /** * Number of bytes to be written as padding in a metadata header. * Demuxing: Unused. - * Muxing: Set by user via av_format_set_metadata_header_padding. + * Muxing: Set by user. */ int metadata_header_padding; @@ -1627,19 +1835,6 @@ typedef struct AVFormatContext { */ uint8_t *dump_separator; - /** - * Forced Data codec_id. - * Demuxing: Set by user. - */ - enum AVCodecID data_codec_id; - - /** - * ',' separated list of allowed protocols. - * - encoding: unused - * - decoding: set by user - */ - char *protocol_whitelist; - /** * A callback for opening new IO streams. * @@ -1663,44 +1858,6 @@ typedef struct AVFormatContext { int (*io_open)(struct AVFormatContext *s, AVIOContext **pb, const char *url, int flags, AVDictionary **options); -#if FF_API_AVFORMAT_IO_CLOSE - /** - * A callback for closing the streams opened with AVFormatContext.io_open(). - * - * @deprecated use io_close2 - */ - attribute_deprecated - void (*io_close)(struct AVFormatContext *s, AVIOContext *pb); -#endif - - /** - * ',' separated list of disallowed protocols. - * - encoding: unused - * - decoding: set by user - */ - char *protocol_blacklist; - - /** - * The maximum number of streams. - * - encoding: unused - * - decoding: set by user - */ - int max_streams; - - /** - * Skip duration calcuation in estimate_timings_from_pts. - * - encoding: unused - * - decoding: set by user - */ - int skip_estimate_duration_from_pts; - - /** - * Maximum number of packets that can be probed - * - encoding: unused - * - decoding: set by user - */ - int max_probe_packets; - /** * A callback for closing the streams opened with AVFormatContext.io_open(). * @@ -1718,15 +1875,25 @@ typedef struct AVFormatContext { /** * This function will cause global side data to be injected in the next packet * of each stream as well as after any subsequent seek. + * + * @note global side data is always available in every AVStream's + * @ref AVCodecParameters.coded_side_data "codecpar side data" array, and + * in a @ref AVCodecContext.coded_side_data "decoder's side data" array if + * initialized with said stream's codecpar. + * @see av_packet_side_data_get() */ void av_format_inject_global_side_data(AVFormatContext *s); +#if FF_API_GET_DUR_ESTIMATE_METHOD /** * Returns the method used to set ctx->duration. * * @return AVFMT_DURATION_FROM_PTS, AVFMT_DURATION_FROM_STREAM, or AVFMT_DURATION_FROM_BITRATE. + * @deprecated duration_estimation_method is public and can be read directly. */ +attribute_deprecated enum AVDurationEstimationMethod av_fmt_ctx_get_duration_estimation_method(const AVFormatContext* ctx); +#endif /** * @defgroup lavf_core Core functions @@ -1825,6 +1992,42 @@ const AVClass *avformat_get_class(void); */ const AVClass *av_stream_get_class(void); +/** + * Get the AVClass for AVStreamGroup. It can be used in combination with + * AV_OPT_SEARCH_FAKE_OBJ for examining options. + * + * @see av_opt_find(). + */ +const AVClass *av_stream_group_get_class(void); + +/** + * @return a string identifying the stream group type, or NULL if unknown + */ +const char *avformat_stream_group_name(enum AVStreamGroupParamsType type); + +/** + * Add a new empty stream group to a media file. + * + * When demuxing, it may be called by the demuxer in read_header(). If the + * flag AVFMTCTX_NOHEADER is set in s.ctx_flags, then it may also + * be called in read_packet(). + * + * When muxing, may be called by the user before avformat_write_header(). + * + * User is required to call avformat_free_context() to clean up the allocation + * by avformat_stream_group_create(). + * + * New streams can be added to the group with avformat_stream_group_add_stream(). + * + * @param s media file handle + * + * @return newly created group or NULL on error. + * @see avformat_new_stream, avformat_stream_group_add_stream. + */ +AVStreamGroup *avformat_stream_group_create(AVFormatContext *s, + enum AVStreamGroupParamsType type, + AVDictionary **options); + /** * Add a new stream to a media file. * @@ -1842,8 +2045,34 @@ const AVClass *av_stream_get_class(void); * * @return newly created stream or NULL on error. */ -AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c); +AVStream *avformat_new_stream(AVFormatContext *s, const struct AVCodec *c); + +/** + * Add an already allocated stream to a stream group. + * + * When demuxing, it may be called by the demuxer in read_header(). If the + * flag AVFMTCTX_NOHEADER is set in s.ctx_flags, then it may also + * be called in read_packet(). + * + * When muxing, may be called by the user before avformat_write_header() after + * having allocated a new group with avformat_stream_group_create() and stream with + * avformat_new_stream(). + * + * User is required to call avformat_free_context() to clean up the allocation + * by avformat_stream_group_add_stream(). + * + * @param stg stream group belonging to a media file. + * @param st stream in the media file to add to the group. + * + * @retval 0 success + * @retval AVERROR(EEXIST) the stream was already in the group + * @retval "another negative error code" legitimate errors + * + * @see avformat_new_stream, avformat_stream_group_create. + */ +int avformat_stream_group_add_stream(AVStreamGroup *stg, AVStream *st); +#if FF_API_AVSTREAM_SIDE_DATA /** * Wrap an existing array as stream side data. * @@ -1856,7 +2085,10 @@ AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c); * * @return zero on success, a negative AVERROR code on failure. On failure, * the stream is unchanged and the data remains owned by the caller. + * @deprecated use av_packet_side_data_add() with the stream's + * @ref AVCodecParameters.coded_side_data "codecpar side data" */ +attribute_deprecated int av_stream_add_side_data(AVStream *st, enum AVPacketSideDataType type, uint8_t *data, size_t size); @@ -1868,7 +2100,10 @@ int av_stream_add_side_data(AVStream *st, enum AVPacketSideDataType type, * @param size side information size * * @return pointer to fresh allocated data or NULL otherwise + * @deprecated use av_packet_side_data_new() with the stream's + * @ref AVCodecParameters.coded_side_data "codecpar side data" */ +attribute_deprecated uint8_t *av_stream_new_side_data(AVStream *stream, enum AVPacketSideDataType type, size_t size); /** @@ -1880,9 +2115,13 @@ uint8_t *av_stream_new_side_data(AVStream *stream, * or to zero if the desired side data is not present. * * @return pointer to data if present or NULL otherwise + * @deprecated use av_packet_side_data_get() with the stream's + * @ref AVCodecParameters.coded_side_data "codecpar side data" */ +attribute_deprecated uint8_t *av_stream_get_side_data(const AVStream *stream, enum AVPacketSideDataType type, size_t *size); +#endif AVProgram *av_new_program(AVFormatContext *s, int id); @@ -2076,7 +2315,7 @@ int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type, int wanted_stream_nb, int related_stream, - const AVCodec **decoder_ret, + const struct AVCodec **decoder_ret, int flags); /** @@ -2352,7 +2591,7 @@ int av_interleaved_write_frame(AVFormatContext *s, AVPacket *pkt); * See av_interleaved_write_uncoded_frame() for details. */ int av_write_uncoded_frame(AVFormatContext *s, int stream_index, - AVFrame *frame); + struct AVFrame *frame); /** * Write an uncoded frame to an output media file. @@ -2371,7 +2610,7 @@ int av_write_uncoded_frame(AVFormatContext *s, int stream_index, * @return >=0 for success, a negative code on error */ int av_interleaved_write_uncoded_frame(AVFormatContext *s, int stream_index, - AVFrame *frame); + struct AVFrame *frame); /** * Test whether a muxer supports uncoded frame. @@ -2759,7 +2998,8 @@ const struct AVCodecTag *avformat_get_mov_audio_tags(void); * @param frame the frame with the aspect ratio to be determined * @return the guessed (valid) sample_aspect_ratio, 0/1 if no idea */ -AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *stream, AVFrame *frame); +AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *stream, + struct AVFrame *frame); /** * Guess the frame rate, based on both the container and codec information. @@ -2769,7 +3009,8 @@ AVRational av_guess_sample_aspect_ratio(AVFormatContext *format, AVStream *strea * @param frame the frame for which the frame rate should be determined, may be NULL * @return the guessed (valid) frame rate, 0/1 if no idea */ -AVRational av_guess_frame_rate(AVFormatContext *ctx, AVStream *stream, AVFrame *frame); +AVRational av_guess_frame_rate(AVFormatContext *ctx, AVStream *stream, + struct AVFrame *frame); /** * Check if the stream st contained in s is matched by the stream specifier diff --git a/libavformat/avidec.c b/libavformat/avidec.c index 7a3fad6392b..2c99dbb88aa 100644 --- a/libavformat/avidec.c +++ b/libavformat/avidec.c @@ -27,6 +27,7 @@ #include "libavutil/avstring.h" #include "libavutil/opt.h" #include "libavutil/dict.h" +#include "libavutil/integer.h" #include "libavutil/internal.h" #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" @@ -476,7 +477,7 @@ static int calculate_bitrate(AVFormatContext *s) AVStream *st = s->streams[i]; FFStream *const sti = ffstream(st); int64_t duration; - int64_t bitrate; + AVInteger bitrate_i, den_i, num_i; for (j = 0; j < sti->nb_index_entries; j++) len += sti->index_entries[j].size; @@ -484,9 +485,14 @@ static int calculate_bitrate(AVFormatContext *s) if (sti->nb_index_entries < 2 || st->codecpar->bit_rate > 0) continue; duration = sti->index_entries[j-1].timestamp - sti->index_entries[0].timestamp; - bitrate = av_rescale(8*len, st->time_base.den, duration * st->time_base.num); - if (bitrate > 0) { - st->codecpar->bit_rate = bitrate; + den_i = av_mul_i(av_int2i(duration), av_int2i(st->time_base.num)); + num_i = av_add_i(av_mul_i(av_int2i(8*len), av_int2i(st->time_base.den)), av_shr_i(den_i, 1)); + bitrate_i = av_div_i(num_i, den_i); + if (av_cmp_i(bitrate_i, av_int2i(INT64_MAX)) <= 0) { + int64_t bitrate = av_i2int(bitrate_i); + if (bitrate > 0) { + st->codecpar->bit_rate = bitrate; + } } } return 1; @@ -1696,7 +1702,7 @@ static int check_stream_max_drift(AVFormatContext *s) int *idx = av_calloc(s->nb_streams, sizeof(*idx)); if (!idx) return AVERROR(ENOMEM); - for (min_pos = pos = 0; min_pos != INT64_MAX; pos = min_pos + 1LU) { + for (min_pos = pos = 0; min_pos != INT64_MAX; pos = min_pos + 1ULL) { int64_t max_dts = INT64_MIN / 2; int64_t min_dts = INT64_MAX / 2; int64_t max_buffer = 0; @@ -1869,13 +1875,20 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, st = s->streams[stream_index]; sti = ffstream(st); ast = st->priv_data; - index = av_index_search_timestamp(st, - timestamp * FFMAX(ast->sample_size, 1), - flags); + + if (avi->dv_demux) { + // index entries are in the AVI scale/rate timebase, which does + // not match DV demuxer's stream timebase + timestamp = av_rescale_q(timestamp, st->time_base, + (AVRational){ ast->scale, ast->rate }); + } else + timestamp *= FFMAX(ast->sample_size, 1); + + index = av_index_search_timestamp(st, timestamp, flags); if (index < 0) { if (sti->nb_index_entries > 0) av_log(s, AV_LOG_DEBUG, "Failed to find timestamp %"PRId64 " in index %"PRId64 " .. %"PRId64 "\n", - timestamp * FFMAX(ast->sample_size, 1), + timestamp, sti->index_entries[0].timestamp, sti->index_entries[sti->nb_index_entries - 1].timestamp); return AVERROR_INVALIDDATA; @@ -1883,7 +1896,7 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, /* find the position */ pos = sti->index_entries[index].pos; - timestamp = sti->index_entries[index].timestamp / FFMAX(ast->sample_size, 1); + timestamp = sti->index_entries[index].timestamp; av_log(s, AV_LOG_TRACE, "XX %"PRId64" %d %"PRId64"\n", timestamp, index, sti->index_entries[index].timestamp); @@ -1898,11 +1911,14 @@ static int avi_read_seek(AVFormatContext *s, int stream_index, /* Feed the DV video stream version of the timestamp to the */ /* DV demux so it can synthesize correct timestamps. */ - ff_dv_offset_reset(avi->dv_demux, timestamp); + ff_dv_ts_reset(avi->dv_demux, + av_rescale_q(timestamp, (AVRational){ ast->scale, ast->rate }, + st->time_base)); avi->stream_index = -1; return 0; } + timestamp /= FFMAX(ast->sample_size, 1); pos_min = pos; for (i = 0; i < s->nb_streams; i++) { @@ -2000,16 +2016,16 @@ static int avi_probe(const AVProbeData *p) return 0; } -const AVInputFormat ff_avi_demuxer = { - .name = "avi", - .long_name = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"), +const FFInputFormat ff_avi_demuxer = { + .p.name = "avi", + .p.long_name = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"), + .p.extensions = "avi", + .p.priv_class = &demuxer_class, .priv_data_size = sizeof(AVIContext), - .flags_internal = FF_FMT_INIT_CLEANUP, - .extensions = "avi", + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = avi_probe, .read_header = avi_read_header, .read_packet = avi_read_packet, .read_close = avi_read_close, .read_seek = avi_read_seek, - .priv_class = &demuxer_class, }; diff --git a/libavformat/avio.c b/libavformat/avio.c index ab1c19a58d0..0503b227866 100644 --- a/libavformat/avio.c +++ b/libavformat/avio.c @@ -24,14 +24,16 @@ #include "libavutil/opt.h" #include "libavutil/time.h" #include "libavutil/avassert.h" +#include "avio_internal.h" #include "os_support.h" -#include "avformat.h" #include "internal.h" #if CONFIG_NETWORK #include "network.h" #endif #include "url.h" +#define IO_BUFFER_SIZE 32768 + /** @name Logging context. */ /*@{*/ static const char *urlcontext_to_name(void *ptr) @@ -61,7 +63,7 @@ static const AVOption options[] = { { NULL } }; -const AVClass ffurl_context_class = { +static const AVClass url_context_class = { .class_name = "URLContext", .item_name = urlcontext_to_name, .option = options, @@ -71,6 +73,47 @@ const AVClass ffurl_context_class = { }; /*@}*/ +static void *avio_child_next(void *obj, void *prev) +{ + AVIOContext *s = obj; + return prev ? NULL : s->opaque; +} + +static const AVClass *child_class_iterate(void **iter) +{ + const AVClass *c = *iter ? NULL : &url_context_class; + *iter = (void*)(uintptr_t)c; + return c; +} + +#define AVIOOFFSET(x) offsetof(AVIOContext,x) +#define E AV_OPT_FLAG_ENCODING_PARAM +#define D AV_OPT_FLAG_DECODING_PARAM +static const AVOption avio_options[] = { + {"protocol_whitelist", "List of protocols that are allowed to be used", AVIOOFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, + { NULL }, +}; + +const AVClass ff_avio_class = { + .class_name = "AVIOContext", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, + .option = avio_options, + .child_next = avio_child_next, + .child_class_iterate = child_class_iterate, +}; + +URLContext *ffio_geturlcontext(AVIOContext *s) +{ + if (!s) + return NULL; + + if (s->opaque && s->read_packet == ffurl_read2) + return s->opaque; + else + return NULL; +} + static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up, const char *filename, int flags, const AVIOInterruptCB *int_cb) @@ -97,7 +140,7 @@ static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up, err = AVERROR(ENOMEM); goto fail; } - uc->av_class = &ffurl_context_class; + uc->av_class = &url_context_class; uc->filename = (char *)&uc[1]; strcpy(uc->filename, filename); uc->prot = up; @@ -126,10 +169,7 @@ static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up, while(ret >= 0 && (key= strchr(p, sep)) && ppriv_data, p, key+1, 0); + ret = av_opt_set(uc->priv_data, p, key+1, 0); if (ret == AVERROR_OPTION_NOT_FOUND) av_log(uc, AV_LOG_ERROR, "Key '%s' not found.\n", p); *val= *key= sep; @@ -137,8 +177,6 @@ static int url_alloc_for_protocol(URLContext **puc, const URLProtocol *up, } if(ret<0 || p!=key){ av_log(uc, AV_LOG_ERROR, "Error parsing options string %s\n", start); - av_freep(&uc->priv_data); - av_freep(&uc); err = AVERROR(EINVAL); goto fail; } @@ -231,6 +269,17 @@ int ffurl_accept(URLContext *s, URLContext **c) return AVERROR(EBADF); } +int avio_accept(AVIOContext *s, AVIOContext **c) +{ + int ret; + URLContext *sc = s->opaque; + URLContext *cc = NULL; + ret = ffurl_accept(sc, &cc); + if (ret < 0) + return ret; + return ffio_fdopen(c, cc); +} + int ffurl_handshake(URLContext *c) { int ret; @@ -243,6 +292,12 @@ int ffurl_handshake(URLContext *c) return 0; } +int avio_handshake(AVIOContext *c) +{ + URLContext *cc = c->opaque; + return ffurl_handshake(cc); +} + #define URL_SCHEME_CHARS \ "abcdefghijklmnopqrstuvwxyz" \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ @@ -353,11 +408,101 @@ int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, return ret; } +int ffio_fdopen(AVIOContext **sp, URLContext *h) +{ + AVIOContext *s; + uint8_t *buffer = NULL; + int buffer_size, max_packet_size; + + max_packet_size = h->max_packet_size; + if (max_packet_size) { + buffer_size = max_packet_size; /* no need to bufferize more than one packet */ + } else { + buffer_size = IO_BUFFER_SIZE; + } + if (!(h->flags & AVIO_FLAG_WRITE) && h->is_streamed) { + if (buffer_size > INT_MAX/2) + return AVERROR(EINVAL); + buffer_size *= 2; + } + buffer = av_malloc(buffer_size); + if (!buffer) + return AVERROR(ENOMEM); + + *sp = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h, + ffurl_read2, ffurl_write2, ffurl_seek2); + if (!*sp) { + av_freep(&buffer); + return AVERROR(ENOMEM); + } + s = *sp; + if (h->protocol_whitelist) { + s->protocol_whitelist = av_strdup(h->protocol_whitelist); + if (!s->protocol_whitelist) { + avio_closep(sp); + return AVERROR(ENOMEM); + } + } + if (h->protocol_blacklist) { + s->protocol_blacklist = av_strdup(h->protocol_blacklist); + if (!s->protocol_blacklist) { + avio_closep(sp); + return AVERROR(ENOMEM); + } + } + s->direct = h->flags & AVIO_FLAG_DIRECT; + + s->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL; + s->max_packet_size = max_packet_size; + s->min_packet_size = h->min_packet_size; + if(h->prot) { + s->read_pause = h->prot->url_read_pause; + s->read_seek = h->prot->url_read_seek; + + if (h->prot->url_read_seek) + s->seekable |= AVIO_SEEKABLE_TIME; + } + ((FFIOContext*)s)->short_seek_get = ffurl_get_short_seek; + s->av_class = &ff_avio_class; + return 0; +} + +int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options, + const char *whitelist, const char *blacklist) +{ + URLContext *h; + int err; + + *s = NULL; + + err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL); + if (err < 0) + return err; + err = ffio_fdopen(s, h); + if (err < 0) { + ffurl_close(h); + return err; + } + return 0; +} + +int avio_open2(AVIOContext **s, const char *filename, int flags, + const AVIOInterruptCB *int_cb, AVDictionary **options) +{ + return ffio_open_whitelist(s, filename, flags, int_cb, options, NULL, NULL); +} + +int avio_open(AVIOContext **s, const char *filename, int flags) +{ + return avio_open2(s, filename, flags, NULL, NULL); +} + + static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf, + const uint8_t *cbuf, int size, int size_min, - int (*transfer_func)(URLContext *h, - uint8_t *buf, - int size)) + int read) { int ret, len; int fast_retries = 5; @@ -367,7 +512,8 @@ static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf, while (len < size_min) { if (ff_check_interrupt(&h->interrupt_callback)) return AVERROR_EXIT; - ret = transfer_func(h, buf + len, size - len); + ret = read ? h->prot->url_read (h, buf + len, size - len): + h->prot->url_write(h, cbuf + len, size - len); if (ret == AVERROR(EINTR)) continue; if (h->flags & AVIO_FLAG_NONBLOCK) @@ -398,35 +544,38 @@ static inline int retry_transfer_wrapper(URLContext *h, uint8_t *buf, return len; } -int ffurl_read(URLContext *h, unsigned char *buf, int size) +int ffurl_read2(void *urlcontext, uint8_t *buf, int size) { + URLContext *h = urlcontext; + if (!(h->flags & AVIO_FLAG_READ)) return AVERROR(EIO); - return retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read); + return retry_transfer_wrapper(h, buf, NULL, size, 1, 1); } int ffurl_read_complete(URLContext *h, unsigned char *buf, int size) { if (!(h->flags & AVIO_FLAG_READ)) return AVERROR(EIO); - return retry_transfer_wrapper(h, buf, size, size, h->prot->url_read); + return retry_transfer_wrapper(h, buf, NULL, size, size, 1); } -int ffurl_write(URLContext *h, const unsigned char *buf, int size) +int ffurl_write2(void *urlcontext, const uint8_t *buf, int size) { + URLContext *h = urlcontext; + if (!(h->flags & AVIO_FLAG_WRITE)) return AVERROR(EIO); /* avoid sending too big packets */ if (h->max_packet_size && size > h->max_packet_size) return AVERROR(EIO); - return retry_transfer_wrapper(h, (unsigned char *)buf, size, size, - (int (*)(struct URLContext *, uint8_t *, int)) - h->prot->url_write); + return retry_transfer_wrapper(h, NULL, buf, size, size, 0); } -int64_t ffurl_seek(URLContext *h, int64_t pos, int whence) +int64_t ffurl_seek2(void *urlcontext, int64_t pos, int whence) { + URLContext *h = urlcontext; int64_t ret; if (!h->prot->url_seek) @@ -463,6 +612,46 @@ int ffurl_close(URLContext *h) return ffurl_closep(&h); } +int avio_close(AVIOContext *s) +{ + FFIOContext *const ctx = ffiocontext(s); + URLContext *h; + int ret, error; + + if (!s) + return 0; + + avio_flush(s); + h = s->opaque; + s->opaque = NULL; + + av_freep(&s->buffer); + if (s->write_flag) + av_log(s, AV_LOG_VERBOSE, + "Statistics: %"PRId64" bytes written, %d seeks, %d writeouts\n", + ctx->bytes_written, ctx->seek_count, ctx->writeout_count); + else + av_log(s, AV_LOG_VERBOSE, "Statistics: %"PRId64" bytes read, %d seeks\n", + ctx->bytes_read, ctx->seek_count); + av_opt_free(s); + + error = s->error; + avio_context_free(&s); + + ret = ffurl_close(h); + if (ret < 0) + return ret; + + return error; +} + +int avio_closep(AVIOContext **s) +{ + int ret = avio_close(*s); + *s = NULL; + return ret; +} + const char *avio_find_protocol_name(const char *url) { @@ -528,11 +717,9 @@ int ffurl_delete(const char *url) return ret; } -#if !FF_API_AVIODIRCONTEXT struct AVIODirContext { struct URLContext *url_context; }; -#endif int avio_open_dir(AVIODirContext **s, const char *url, AVDictionary **options) { @@ -647,8 +834,10 @@ int ffurl_get_multi_file_handle(URLContext *h, int **handles, int *numhandles) return h->prot->url_get_multi_file_handle(h, handles, numhandles); } -int ffurl_get_short_seek(URLContext *h) +int ffurl_get_short_seek(void *urlcontext) { + URLContext *h = urlcontext; + if (!h || !h->prot || !h->prot->url_get_short_seek) return AVERROR(ENOSYS); return h->prot->url_get_short_seek(h); diff --git a/libavformat/avio.h b/libavformat/avio.h index 5f13e0622d3..ebf611187dc 100644 --- a/libavformat/avio.h +++ b/libavformat/avio.h @@ -101,13 +101,7 @@ typedef struct AVIODirEntry { int64_t filemode; /**< Unix file mode, -1 if unknown. */ } AVIODirEntry; -#if FF_API_AVIODIRCONTEXT -typedef struct AVIODirContext { - struct URLContext *url_context; -} AVIODirContext; -#else typedef struct AVIODirContext AVIODirContext; -#endif /** * Different data types that can be returned via the AVIO @@ -238,7 +232,7 @@ typedef struct AVIOContext { void *opaque; /**< A private pointer, passed to the read/write/seek/... functions. */ int (*read_packet)(void *opaque, uint8_t *buf, int buf_size); - int (*write_packet)(void *opaque, uint8_t *buf, int buf_size); + int (*write_packet)(void *opaque, const uint8_t *buf, int buf_size); int64_t (*seek)(void *opaque, int64_t offset, int whence); int64_t pos; /**< position in the file of the current buffer */ int eof_reached; /**< true if was unable to read due to error or eof */ @@ -286,7 +280,7 @@ typedef struct AVIOContext { /** * A callback that is used instead of write_packet. */ - int (*write_data_type)(void *opaque, uint8_t *buf, int buf_size, + int (*write_data_type)(void *opaque, const uint8_t *buf, int buf_size, enum AVIODataMarkerType type, int64_t time); /** * If set, don't call write_data_type separately for AVIO_DATA_MARKER_BOUNDARY_POINT, @@ -407,7 +401,7 @@ AVIOContext *avio_alloc_context( int write_flag, void *opaque, int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), - int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), + int (*write_packet)(void *opaque, const uint8_t *buf, int buf_size), int64_t (*seek)(void *opaque, int64_t offset, int whence)); /** @@ -531,7 +525,7 @@ int avio_printf(AVIOContext *s, const char *fmt, ...) av_printf_format(2, 3); * Usually you don't need to use this function directly but its macro wrapper, * avio_print. */ -void avio_print_string_array(AVIOContext *s, const char *strings[]); +void avio_print_string_array(AVIOContext *s, const char * const strings[]); /** * Write strings (const char *) to the context. diff --git a/libavformat/avio_internal.h b/libavformat/avio_internal.h index 1f5e3d474b8..7d4756db0c6 100644 --- a/libavformat/avio_internal.h +++ b/libavformat/avio_internal.h @@ -20,7 +20,6 @@ #define AVFORMAT_AVIO_INTERNAL_H #include "avio.h" -#include "url.h" #include "libavutil/log.h" @@ -90,9 +89,18 @@ void ffio_init_context(FFIOContext *s, int write_flag, void *opaque, int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), - int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), + int (*write_packet)(void *opaque, const uint8_t *buf, int buf_size), int64_t (*seek)(void *opaque, int64_t offset, int whence)); +/** + * Wrap a buffer in an AVIOContext for reading. + */ +void ffio_init_read_context(FFIOContext *s, const uint8_t *buffer, int buffer_size); + +/** + * Wrap a buffer in an AVIOContext for writing. + */ +void ffio_init_write_context(FFIOContext *s, uint8_t *buffer, int buffer_size); /** * Read size bytes from AVIOContext, returning a pointer. @@ -134,6 +142,27 @@ int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char **buf, int buf_siz uint64_t ffio_read_varlen(AVIOContext *bc); +/** + * Read a unsigned integer coded as a variable number of up to eight + * little-endian bytes, where the MSB in a byte signals another byte + * must be read. + * All coded bytes are read, but values > UINT_MAX are truncated. + */ +unsigned int ffio_read_leb(AVIOContext *s); + +void ffio_write_leb(AVIOContext *s, unsigned val); + +/** + * Write a sequence of text lines, converting line endings. + * All input line endings (LF, CRLF, CR) are converted to the configured line ending. + * @param s The AVIOContext to write to + * @param buf The buffer to write + * @param size The size of the buffer, or <0 to use the full length of a null-terminated string + * @param ending The line ending sequence to convert to, or NULL for \n + */ +void ffio_write_lines(AVIOContext *s, const unsigned char *buf, int size, + const unsigned char *ending); + /** * Read size bytes from AVIOContext into buf. * Check that exactly size bytes have been read. @@ -185,6 +214,14 @@ unsigned long ff_crcA001_update(unsigned long checksum, const uint8_t *buf, */ int ffio_open_dyn_packet_buf(AVIOContext **s, int max_packet_size); +/** + * Return the URLContext associated with the AVIOContext + * + * @param s IO context + * @return pointer to URLContext or NULL. + */ +struct URLContext *ffio_geturlcontext(AVIOContext *s); + /** * Create and initialize a AVIOContext for accessing the * resource referenced by the URLContext h. @@ -196,15 +233,7 @@ int ffio_open_dyn_packet_buf(AVIOContext **s, int max_packet_size); * @return >= 0 in case of success, a negative value corresponding to an * AVERROR code in case of failure */ -int ffio_fdopen(AVIOContext **s, URLContext *h); - -/** - * Return the URLContext associated with the AVIOContext - * - * @param s IO context - * @return pointer to URLContext or NULL. - */ -URLContext *ffio_geturlcontext(AVIOContext *s); +int ffio_fdopen(AVIOContext **s, struct URLContext *h); /** diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 4ad734a3c3e..3609960907b 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -22,7 +22,6 @@ #include "libavutil/bprint.h" #include "libavutil/crc.h" #include "libavutil/dict.h" -#include "libavutil/internal.h" #include "libavutil/intreadwrite.h" #include "libavutil/log.h" #include "libavutil/opt.h" @@ -31,7 +30,6 @@ #include "avio.h" #include "avio_internal.h" #include "internal.h" -#include "url.h" #include #define IO_BUFFER_SIZE 32768 @@ -43,36 +41,6 @@ */ #define SHORT_SEEK_THRESHOLD 32768 -static void *ff_avio_child_next(void *obj, void *prev) -{ - AVIOContext *s = obj; - return prev ? NULL : s->opaque; -} - -static const AVClass *child_class_iterate(void **iter) -{ - const AVClass *c = *iter ? NULL : &ffurl_context_class; - *iter = (void*)(uintptr_t)c; - return c; -} - -#define OFFSET(x) offsetof(AVIOContext,x) -#define E AV_OPT_FLAG_ENCODING_PARAM -#define D AV_OPT_FLAG_DECODING_PARAM -static const AVOption ff_avio_options[] = { - {"protocol_whitelist", "List of protocols that are allowed to be used", OFFSET(protocol_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, - { NULL }, -}; - -const AVClass ff_avio_class = { - .class_name = "AVIOContext", - .item_name = av_default_item_name, - .version = LIBAVUTIL_VERSION_INT, - .option = ff_avio_options, - .child_next = ff_avio_child_next, - .child_class_iterate = child_class_iterate, -}; - static void fill_buffer(AVIOContext *s); static int url_resetbuf(AVIOContext *s, int flags); /** @warning must be called before any I/O */ @@ -84,7 +52,7 @@ void ffio_init_context(FFIOContext *ctx, int write_flag, void *opaque, int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), - int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), + int (*write_packet)(void *opaque, const uint8_t *buf, int buf_size), int64_t (*seek)(void *opaque, int64_t offset, int whence)) { AVIOContext *const s = &ctx->pub; @@ -127,13 +95,23 @@ void ffio_init_context(FFIOContext *ctx, ctx->short_seek_get = NULL; } +void ffio_init_read_context(FFIOContext *s, const uint8_t *buffer, int buffer_size) +{ + ffio_init_context(s, (unsigned char*)buffer, buffer_size, 0, NULL, NULL, NULL, NULL); +} + +void ffio_init_write_context(FFIOContext *s, uint8_t *buffer, int buffer_size) +{ + ffio_init_context(s, buffer, buffer_size, 1, NULL, NULL, NULL, NULL); +} + AVIOContext *avio_alloc_context( unsigned char *buffer, int buffer_size, int write_flag, void *opaque, int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), - int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), + int (*write_packet)(void *opaque, const uint8_t *buf, int buf_size), int64_t (*seek)(void *opaque, int64_t offset, int whence)) { FFIOContext *s = av_malloc(sizeof(*s)); @@ -155,12 +133,12 @@ static void writeout(AVIOContext *s, const uint8_t *data, int len) if (!s->error) { int ret = 0; if (s->write_data_type) - ret = s->write_data_type(s->opaque, (uint8_t *)data, + ret = s->write_data_type(s->opaque, data, len, ctx->current_type, ctx->last_time); else if (s->write_packet) - ret = s->write_packet(s->opaque, (uint8_t *)data, len); + ret = s->write_packet(s->opaque, data, len); if (ret < 0) { s->error = ret; } else { @@ -945,71 +923,68 @@ uint64_t ffio_read_varlen(AVIOContext *bc){ return val; } -int ffio_fdopen(AVIOContext **s, URLContext *h) +unsigned int ffio_read_leb(AVIOContext *s) { + int more, i = 0; + unsigned leb = 0; + + do { + int byte = avio_r8(s); + unsigned bits = byte & 0x7f; + more = byte & 0x80; + if (i <= 4) + leb |= bits << (i * 7); + if (++i == 8) + break; + } while (more); + + return leb; +} + +void ffio_write_leb(AVIOContext *s, unsigned val) { - uint8_t *buffer = NULL; - int buffer_size, max_packet_size; + int len; + uint8_t byte; - max_packet_size = h->max_packet_size; - if (max_packet_size) { - buffer_size = max_packet_size; /* no need to bufferize more than one packet */ - } else { - buffer_size = IO_BUFFER_SIZE; - } - if (!(h->flags & AVIO_FLAG_WRITE) && h->is_streamed) { - if (buffer_size > INT_MAX/2) - return AVERROR(EINVAL); - buffer_size *= 2; - } - buffer = av_malloc(buffer_size); - if (!buffer) - return AVERROR(ENOMEM); + len = (av_log2(val) + 7) / 7; - *s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h, - (int (*)(void *, uint8_t *, int)) ffurl_read, - (int (*)(void *, uint8_t *, int)) ffurl_write, - (int64_t (*)(void *, int64_t, int))ffurl_seek); - if (!*s) { - av_freep(&buffer); - return AVERROR(ENOMEM); - } - (*s)->protocol_whitelist = av_strdup(h->protocol_whitelist); - if (!(*s)->protocol_whitelist && h->protocol_whitelist) { - avio_closep(s); - return AVERROR(ENOMEM); - } - (*s)->protocol_blacklist = av_strdup(h->protocol_blacklist); - if (!(*s)->protocol_blacklist && h->protocol_blacklist) { - avio_closep(s); - return AVERROR(ENOMEM); - } - (*s)->direct = h->flags & AVIO_FLAG_DIRECT; + for (int i = 0; i < len; i++) { + byte = val >> (7 * i) & 0x7f; + if (i < len - 1) + byte |= 0x80; - (*s)->seekable = h->is_streamed ? 0 : AVIO_SEEKABLE_NORMAL; - (*s)->max_packet_size = max_packet_size; - (*s)->min_packet_size = h->min_packet_size; - if(h->prot) { - (*s)->read_pause = (int (*)(void *, int))h->prot->url_read_pause; - (*s)->read_seek = - (int64_t (*)(void *, int, int64_t, int))h->prot->url_read_seek; - - if (h->prot->url_read_seek) - (*s)->seekable |= AVIO_SEEKABLE_TIME; + avio_w8(s, byte); } - ((FFIOContext*)(*s))->short_seek_get = (int (*)(void *))ffurl_get_short_seek; - (*s)->av_class = &ff_avio_class; - return 0; } -URLContext* ffio_geturlcontext(AVIOContext *s) +void ffio_write_lines(AVIOContext *s, const unsigned char *buf, int size, + const unsigned char *ending) { - if (!s) - return NULL; + int ending_len = ending ? strlen(ending) : 1; + if (!ending) + ending = "\n"; + if (size < 0) + size = strlen(buf); - if (s->opaque && s->read_packet == (int (*)(void *, uint8_t *, int))ffurl_read) - return s->opaque; - else - return NULL; + while (size > 0) { + size_t len = 0; + char last = 0; + for (; len < size; len++) { + last = buf[len]; + if (last == '\r' || last == '\n') + break; + } + + avio_write(s, buf, len); + avio_write(s, ending, ending_len); + + buf += len + 1; + size -= len + 1; + + if (size > 0 && last == '\r' && buf[0] == '\n') { + buf++; + size--; + } + } } int ffio_copy_url_options(AVIOContext* pb, AVDictionary** avio_opts) @@ -1212,78 +1187,6 @@ int ffio_rewind_with_probe_data(AVIOContext *s, unsigned char **bufp, int buf_si return 0; } -int avio_open(AVIOContext **s, const char *filename, int flags) -{ - return avio_open2(s, filename, flags, NULL, NULL); -} - -int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags, - const AVIOInterruptCB *int_cb, AVDictionary **options, - const char *whitelist, const char *blacklist - ) -{ - URLContext *h; - int err; - - *s = NULL; - - err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL); - if (err < 0) - return err; - err = ffio_fdopen(s, h); - if (err < 0) { - ffurl_close(h); - return err; - } - return 0; -} - -int avio_open2(AVIOContext **s, const char *filename, int flags, - const AVIOInterruptCB *int_cb, AVDictionary **options) -{ - return ffio_open_whitelist(s, filename, flags, int_cb, options, NULL, NULL); -} - -int avio_close(AVIOContext *s) -{ - FFIOContext *const ctx = ffiocontext(s); - URLContext *h; - int ret, error; - - if (!s) - return 0; - - avio_flush(s); - h = s->opaque; - s->opaque = NULL; - - av_freep(&s->buffer); - if (s->write_flag) - av_log(s, AV_LOG_VERBOSE, - "Statistics: %"PRId64" bytes written, %d seeks, %d writeouts\n", - ctx->bytes_written, ctx->seek_count, ctx->writeout_count); - else - av_log(s, AV_LOG_VERBOSE, "Statistics: %"PRId64" bytes read, %d seeks\n", - ctx->bytes_read, ctx->seek_count); - av_opt_free(s); - - error = s->error; - avio_context_free(&s); - - ret = ffurl_close(h); - if (ret < 0) - return ret; - - return error; -} - -int avio_closep(AVIOContext **s) -{ - int ret = avio_close(*s); - *s = NULL; - return ret; -} - int avio_vprintf(AVIOContext *s, const char *fmt, va_list ap) { AVBPrint bp; @@ -1312,7 +1215,7 @@ int avio_printf(AVIOContext *s, const char *fmt, ...) return ret; } -void avio_print_string_array(AVIOContext *s, const char *strings[]) +void avio_print_string_array(AVIOContext *s, const char *const strings[]) { for(; *strings; strings++) avio_write(s, (const unsigned char *)*strings, strlen(*strings)); @@ -1362,23 +1265,6 @@ int avio_read_to_bprint(AVIOContext *h, AVBPrint *pb, size_t max_size) return 0; } -int avio_accept(AVIOContext *s, AVIOContext **c) -{ - int ret; - URLContext *sc = s->opaque; - URLContext *cc = NULL; - ret = ffurl_accept(sc, &cc); - if (ret < 0) - return ret; - return ffio_fdopen(c, cc); -} - -int avio_handshake(AVIOContext *c) -{ - URLContext *cc = c->opaque; - return ffurl_handshake(cc); -} - /* output in a dynamic buffer */ typedef struct DynBuffer { @@ -1388,7 +1274,7 @@ typedef struct DynBuffer { uint8_t io_buffer[1]; } DynBuffer; -static int dyn_buf_write(void *opaque, uint8_t *buf, int buf_size) +static int dyn_buf_write(void *opaque, const uint8_t *buf, int buf_size) { DynBuffer *d = opaque; unsigned new_size; @@ -1420,7 +1306,7 @@ static int dyn_buf_write(void *opaque, uint8_t *buf, int buf_size) return buf_size; } -static int dyn_packet_buf_write(void *opaque, uint8_t *buf, int buf_size) +static int dyn_packet_buf_write(void *opaque, const uint8_t *buf, int buf_size) { unsigned char buf1[4]; int ret; @@ -1557,7 +1443,7 @@ void ffio_free_dyn_buf(AVIOContext **s) avio_context_free(s); } -static int null_buf_write(void *opaque, uint8_t *buf, int buf_size) +static int null_buf_write(void *opaque, const uint8_t *buf, int buf_size) { DynBuffer *d = opaque; diff --git a/libavformat/avisynth.c b/libavformat/avisynth.c index b426ac343e6..e85b9ae4887 100644 --- a/libavformat/avisynth.c +++ b/libavformat/avisynth.c @@ -26,6 +26,7 @@ #include "libavcodec/internal.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "config.h" @@ -62,6 +63,7 @@ typedef struct AviSynthLibrary { AVSC_DECLARE_FUNC(avs_create_script_environment); AVSC_DECLARE_FUNC(avs_delete_script_environment); AVSC_DECLARE_FUNC(avs_get_audio); + AVSC_DECLARE_FUNC(avs_get_channel_mask); AVSC_DECLARE_FUNC(avs_get_error); AVSC_DECLARE_FUNC(avs_get_frame); AVSC_DECLARE_FUNC(avs_get_version); @@ -157,6 +159,7 @@ static av_cold int avisynth_load_library(void) LOAD_AVS_FUNC(avs_create_script_environment, 0); LOAD_AVS_FUNC(avs_delete_script_environment, 0); LOAD_AVS_FUNC(avs_get_audio, 0); + LOAD_AVS_FUNC(avs_get_channel_mask, 1); LOAD_AVS_FUNC(avs_get_error, 1); // New to AviSynth 2.6 LOAD_AVS_FUNC(avs_get_frame, 0); LOAD_AVS_FUNC(avs_get_version, 0); @@ -793,6 +796,10 @@ static int avisynth_create_stream_audio(AVFormatContext *s, AVStream *st) st->duration = avs->vi->num_audio_samples; avpriv_set_pts_info(st, 64, 1, avs->vi->audio_samples_per_second); + if (avs_library.avs_get_version(avs->clip) >= 10) + av_channel_layout_from_mask(&st->codecpar->ch_layout, + avs_library.avs_get_channel_mask(avs->vi)); + switch (avs->vi->sample_type) { case AVS_SAMPLE_INT8: st->codecpar->codec_id = AV_CODEC_ID_PCM_U8; @@ -1173,14 +1180,14 @@ static int avisynth_read_seek(AVFormatContext *s, int stream_index, AVISYNTH_FRAMEPROP_MATRIX | AVISYNTH_FRAMEPROP_CHROMA_LOCATION #define OFFSET(x) offsetof(AviSynthContext, x) static const AVOption avisynth_options[] = { - { "avisynth_flags", "set flags related to reading frame properties from script (AviSynth+ v3.7.1 or higher)", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = AVISYNTH_FRAMEPROP_DEFAULT}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM, "flags" }, - { "field_order", "read field order", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_FIELD_ORDER}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, "flags" }, - { "range", "read color range", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_RANGE}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, "flags" }, - { "primaries", "read color primaries", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_PRIMARIES}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, "flags" }, - { "transfer", "read color transfer characteristics", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_TRANSFER}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, "flags" }, - { "matrix", "read matrix coefficients", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_MATRIX}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, "flags" }, - { "chroma_location", "read chroma location", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_CHROMA_LOCATION}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, "flags" }, - { "sar", "read sample aspect ratio", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_SAR}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, "flags" }, + { "avisynth_flags", "set flags related to reading frame properties from script (AviSynth+ v3.7.1 or higher)", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = AVISYNTH_FRAMEPROP_DEFAULT}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM, .unit = "flags" }, + { "field_order", "read field order", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_FIELD_ORDER}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, .unit = "flags" }, + { "range", "read color range", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_RANGE}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, .unit = "flags" }, + { "primaries", "read color primaries", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_PRIMARIES}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, .unit = "flags" }, + { "transfer", "read color transfer characteristics", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_TRANSFER}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, .unit = "flags" }, + { "matrix", "read matrix coefficients", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_MATRIX}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, .unit = "flags" }, + { "chroma_location", "read chroma location", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_CHROMA_LOCATION}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, .unit = "flags" }, + { "sar", "read sample aspect ratio", 0, AV_OPT_TYPE_CONST, {.i64 = AVISYNTH_FRAMEPROP_SAR}, 0, 1, AV_OPT_FLAG_DECODING_PARAM, .unit = "flags" }, { NULL }, }; @@ -1191,14 +1198,14 @@ static const AVClass avisynth_demuxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_avisynth_demuxer = { - .name = "avisynth", - .long_name = NULL_IF_CONFIG_SMALL("AviSynth script"), +const FFInputFormat ff_avisynth_demuxer = { + .p.name = "avisynth", + .p.long_name = NULL_IF_CONFIG_SMALL("AviSynth script"), + .p.extensions = "avs", + .p.priv_class = &avisynth_demuxer_class, .priv_data_size = sizeof(AviSynthContext), .read_header = avisynth_read_header, .read_packet = avisynth_read_packet, .read_close = avisynth_read_close, .read_seek = avisynth_read_seek, - .extensions = "avs", - .priv_class = &avisynth_demuxer_class, }; diff --git a/libavformat/avlanguage.c b/libavformat/avlanguage.c index 782a58adb2b..202d9aa8355 100644 --- a/libavformat/avlanguage.c +++ b/libavformat/avlanguage.c @@ -29,7 +29,7 @@ typedef struct LangEntry { uint16_t next_equivalent; } LangEntry; -static const uint16_t lang_table_counts[] = { 484, 20, 184 }; +static const uint16_t lang_table_counts[] = { 484, 20, 190 }; static const uint16_t lang_table_offsets[] = { 0, 484, 504 }; static const LangEntry lang_table[] = { @@ -539,7 +539,7 @@ static const LangEntry lang_table[] = { /*0501*/ { "slk", 647 }, /*0502*/ { "sqi", 652 }, /*0503*/ { "zho", 686 }, - /*----- AV_LANG_ISO639_1 entries (184) -----*/ + /*----- AV_LANG_ISO639_1 entries (190) -----*/ /*0504*/ { "aa" , 0 }, /*0505*/ { "ab" , 1 }, /*0506*/ { "ae" , 33 }, @@ -724,6 +724,12 @@ static const LangEntry lang_table[] = { /*0685*/ { "za" , 478 }, /*0686*/ { "zh" , 78 }, /*0687*/ { "zu" , 480 }, + /*0688*/ { "in" , 195 }, /* deprecated */ + /*0689*/ { "iw" , 172 }, /* deprecated */ + /*0690*/ { "ji" , 472 }, /* deprecated */ + /*0691*/ { "jw" , 202 }, /* deprecated */ + /*0692*/ { "mo" , 358 }, /* deprecated */ + /*0693*/ { "sh" , 693 }, /* deprecated (no equivalent) */ { "", 0 } }; diff --git a/libavformat/avr.c b/libavformat/avr.c index dce977b6ac1..261edef4b5f 100644 --- a/libavformat/avr.c +++ b/libavformat/avr.c @@ -21,6 +21,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "pcm.h" @@ -48,8 +49,7 @@ static int avr_read_header(AVFormatContext *s) st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - avio_skip(s->pb, 4); // magic - avio_skip(s->pb, 8); // sample_name + avio_skip(s->pb, 4 /* magic */ + 8 /* sample_name */); chan = avio_rb16(s->pb); if (!chan) { @@ -65,18 +65,13 @@ static int avr_read_header(AVFormatContext *s) sign = avio_rb16(s->pb); - avio_skip(s->pb, 2); // loop - avio_skip(s->pb, 2); // midi - avio_skip(s->pb, 1); // replay speed + avio_skip(s->pb, 2 /* loop */ + 2 /* midi */ + 1 /* replay speed */); st->codecpar->sample_rate = avio_rb24(s->pb); if (st->codecpar->sample_rate == 0) return AVERROR_INVALIDDATA; - avio_skip(s->pb, 4 * 3); - avio_skip(s->pb, 2 * 3); - avio_skip(s->pb, 20); - avio_skip(s->pb, 64); + avio_skip(s->pb, 4 * 3 + 2 * 3 + 20 + 64); st->codecpar->codec_id = ff_get_pcm_codec_id(bps, 0, 1, sign); if (st->codecpar->codec_id == AV_CODEC_ID_NONE) { @@ -90,13 +85,13 @@ static int avr_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_avr_demuxer = { - .name = "avr", - .long_name = NULL_IF_CONFIG_SMALL("AVR (Audio Visual Research)"), +const FFInputFormat ff_avr_demuxer = { + .p.name = "avr", + .p.long_name = NULL_IF_CONFIG_SMALL("AVR (Audio Visual Research)"), + .p.extensions = "avr", + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = avr_probe, .read_header = avr_read_header, .read_packet = ff_pcm_read_packet, .read_seek = ff_pcm_read_seek, - .extensions = "avr", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/avs.c b/libavformat/avs.c index 19f03731573..3cd814836bc 100644 --- a/libavformat/avs.c +++ b/libavformat/avs.c @@ -26,6 +26,7 @@ */ #include "avformat.h" +#include "demux.h" #include "voc.h" @@ -228,9 +229,9 @@ static int avs_read_packet(AVFormatContext * s, AVPacket * pkt) } } -const AVInputFormat ff_avs_demuxer = { - .name = "avs", - .long_name = NULL_IF_CONFIG_SMALL("Argonaut Games Creature Shock"), +const FFInputFormat ff_avs_demuxer = { + .p.name = "avs", + .p.long_name = NULL_IF_CONFIG_SMALL("Argonaut Games Creature Shock"), .priv_data_size = sizeof(AvsFormat), .read_probe = avs_probe, .read_header = avs_read_header, diff --git a/libavformat/bethsoftvid.c b/libavformat/bethsoftvid.c index cfb7d573320..bdf1bdc6c04 100644 --- a/libavformat/bethsoftvid.c +++ b/libavformat/bethsoftvid.c @@ -31,6 +31,7 @@ #include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "libavcodec/bethsoftvideo.h" @@ -290,9 +291,9 @@ static int vid_read_packet(AVFormatContext *s, } } -const AVInputFormat ff_bethsoftvid_demuxer = { - .name = "bethsoftvid", - .long_name = NULL_IF_CONFIG_SMALL("Bethesda Softworks VID"), +const FFInputFormat ff_bethsoftvid_demuxer = { + .p.name = "bethsoftvid", + .p.long_name = NULL_IF_CONFIG_SMALL("Bethesda Softworks VID"), .priv_data_size = sizeof(BVID_DemuxContext), .read_probe = vid_probe, .read_header = vid_read_header, diff --git a/libavformat/bfi.c b/libavformat/bfi.c index 6bcd3cd4003..06bf5d2c176 100644 --- a/libavformat/bfi.c +++ b/libavformat/bfi.c @@ -176,9 +176,9 @@ static int bfi_read_packet(AVFormatContext * s, AVPacket * pkt) return ret; } -const AVInputFormat ff_bfi_demuxer = { - .name = "bfi", - .long_name = NULL_IF_CONFIG_SMALL("Brute Force & Ignorance"), +const FFInputFormat ff_bfi_demuxer = { + .p.name = "bfi", + .p.long_name = NULL_IF_CONFIG_SMALL("Brute Force & Ignorance"), .priv_data_size = sizeof(BFIContext), .read_probe = bfi_probe, .read_header = bfi_read_header, diff --git a/libavformat/bink.c b/libavformat/bink.c index f4079dfb1d3..0632d390a2a 100644 --- a/libavformat/bink.c +++ b/libavformat/bink.c @@ -324,13 +324,13 @@ static int read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, in return 0; } -const AVInputFormat ff_bink_demuxer = { - .name = "bink", - .long_name = NULL_IF_CONFIG_SMALL("Bink"), +const FFInputFormat ff_bink_demuxer = { + .p.name = "bink", + .p.long_name = NULL_IF_CONFIG_SMALL("Bink"), + .p.flags = AVFMT_SHOW_IDS, .priv_data_size = sizeof(BinkDemuxContext), .read_probe = probe, .read_header = read_header, .read_packet = read_packet, .read_seek = read_seek, - .flags = AVFMT_SHOW_IDS, }; diff --git a/libavformat/binka.c b/libavformat/binka.c index 00703ad015c..cc5f2555cae 100644 --- a/libavformat/binka.c +++ b/libavformat/binka.c @@ -20,6 +20,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" static int binka_probe(const AVProbeData *p) @@ -89,12 +90,12 @@ static int binka_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVInputFormat ff_binka_demuxer = { - .name = "binka", - .long_name = NULL_IF_CONFIG_SMALL("Bink Audio"), +const FFInputFormat ff_binka_demuxer = { + .p.name = "binka", + .p.long_name = NULL_IF_CONFIG_SMALL("Bink Audio"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "binka", .read_probe = binka_probe, .read_header = binka_read_header, .read_packet = binka_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "binka", }; diff --git a/libavformat/bintext.c b/libavformat/bintext.c index b6f14a03e5a..c96c14ccd9d 100644 --- a/libavformat/bintext.c +++ b/libavformat/bintext.c @@ -36,6 +36,7 @@ #include "libavutil/opt.h" #include "libavutil/parseutils.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "sauce.h" #include "libavcodec/bintext.h" @@ -92,9 +93,12 @@ static int next_tag_read(AVFormatContext *avctx, uint64_t *fsize) AVIOContext *pb = avctx->pb; char buf[36]; int len; - uint64_t start_pos = avio_size(pb) - 256; + int64_t start_pos = avio_size(pb); - avio_seek(pb, start_pos, SEEK_SET); + if (start_pos < 256) + return AVERROR_INVALIDDATA; + + avio_seek(pb, start_pos - 256, SEEK_SET); if (avio_read(pb, buf, sizeof(next_magic)) != sizeof(next_magic)) return -1; if (memcmp(buf, next_magic, sizeof(next_magic))) @@ -244,7 +248,10 @@ static int xbin_read_header(AVFormatContext *s) return AVERROR(EIO); if (pb->seekable & AVIO_SEEKABLE_NORMAL) { - bin->fsize = avio_size(pb) - 9 - st->codecpar->extradata_size; + int64_t fsize = avio_size(pb); + if (fsize < 9 + st->codecpar->extradata_size) + return 0; + bin->fsize = fsize - 9 - st->codecpar->extradata_size; ff_sauce_read(s, &bin->fsize, NULL, 0); avio_seek(pb, 9 + st->codecpar->extradata_size, SEEK_SET); } @@ -284,7 +291,10 @@ static int adf_read_header(AVFormatContext *s) if (pb->seekable & AVIO_SEEKABLE_NORMAL) { int got_width = 0; - bin->fsize = avio_size(pb) - 1 - 192 - 4096; + int64_t fsize = avio_size(pb); + if (fsize < 1 + 192 + 4096) + return 0; + bin->fsize = fsize - 1 - 192 - 4096; st->codecpar->width = 80<<3; ff_sauce_read(s, &bin->fsize, &got_width, 0); if (st->codecpar->width < 8) @@ -317,6 +327,7 @@ static int idf_read_header(AVFormatContext *s) AVIOContext *pb = s->pb; AVStream *st; int got_width = 0, ret; + int64_t fsize; if (!(pb->seekable & AVIO_SEEKABLE_NORMAL)) return AVERROR(EIO); @@ -331,14 +342,18 @@ static int idf_read_header(AVFormatContext *s) st->codecpar->extradata[0] = 16; st->codecpar->extradata[1] = BINTEXT_PALETTE|BINTEXT_FONT; - avio_seek(pb, avio_size(pb) - 4096 - 48, SEEK_SET); + fsize = avio_size(pb); + if (fsize < 12 + 4096 + 48) + return AVERROR_INVALIDDATA; + bin->fsize = fsize - 12 - 4096 - 48; + + avio_seek(pb, bin->fsize + 12, SEEK_SET); if (avio_read(pb, st->codecpar->extradata + 2 + 48, 4096) < 0) return AVERROR(EIO); if (avio_read(pb, st->codecpar->extradata + 2, 48) < 0) return AVERROR(EIO); - bin->fsize = avio_size(pb) - 12 - 4096 - 48; ff_sauce_read(s, &bin->fsize, &got_width, 0); if (st->codecpar->width < 8) return AVERROR_INVALIDDATA; @@ -388,50 +403,50 @@ static const AVOption options[] = { }} #if CONFIG_BINTEXT_DEMUXER -const AVInputFormat ff_bintext_demuxer = { - .name = "bin", - .long_name = NULL_IF_CONFIG_SMALL("Binary text"), +const FFInputFormat ff_bintext_demuxer = { + .p.name = "bin", + .p.long_name = NULL_IF_CONFIG_SMALL("Binary text"), + .p.priv_class = CLASS("Binary text demuxer"), .priv_data_size = sizeof(BinDemuxContext), .read_probe = bin_probe, .read_header = bintext_read_header, .read_packet = read_packet, - .priv_class = CLASS("Binary text demuxer"), }; #endif #if CONFIG_XBIN_DEMUXER -const AVInputFormat ff_xbin_demuxer = { - .name = "xbin", - .long_name = NULL_IF_CONFIG_SMALL("eXtended BINary text (XBIN)"), +const FFInputFormat ff_xbin_demuxer = { + .p.name = "xbin", + .p.long_name = NULL_IF_CONFIG_SMALL("eXtended BINary text (XBIN)"), + .p.priv_class = CLASS("eXtended BINary text (XBIN) demuxer"), .priv_data_size = sizeof(BinDemuxContext), .read_probe = xbin_probe, .read_header = xbin_read_header, .read_packet = read_packet, - .priv_class = CLASS("eXtended BINary text (XBIN) demuxer"), }; #endif #if CONFIG_ADF_DEMUXER -const AVInputFormat ff_adf_demuxer = { - .name = "adf", - .long_name = NULL_IF_CONFIG_SMALL("Artworx Data Format"), +const FFInputFormat ff_adf_demuxer = { + .p.name = "adf", + .p.long_name = NULL_IF_CONFIG_SMALL("Artworx Data Format"), + .p.extensions = "adf", + .p.priv_class = CLASS("Artworx Data Format demuxer"), .priv_data_size = sizeof(BinDemuxContext), .read_header = adf_read_header, .read_packet = read_packet, - .extensions = "adf", - .priv_class = CLASS("Artworx Data Format demuxer"), }; #endif #if CONFIG_IDF_DEMUXER -const AVInputFormat ff_idf_demuxer = { - .name = "idf", - .long_name = NULL_IF_CONFIG_SMALL("iCE Draw File"), +const FFInputFormat ff_idf_demuxer = { + .p.name = "idf", + .p.long_name = NULL_IF_CONFIG_SMALL("iCE Draw File"), + .p.extensions = "idf", + .p.priv_class = CLASS("iCE Draw File demuxer"), .priv_data_size = sizeof(BinDemuxContext), .read_probe = idf_probe, .read_header = idf_read_header, .read_packet = read_packet, - .extensions = "idf", - .priv_class = CLASS("iCE Draw File demuxer"), }; #endif diff --git a/libavformat/bit.c b/libavformat/bit.c index c3b9cf4d3dc..5c3eb31c571 100644 --- a/libavformat/bit.c +++ b/libavformat/bit.c @@ -22,6 +22,7 @@ #include "config_components.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "mux.h" #include "libavcodec/get_bits.h" @@ -113,22 +114,22 @@ static int read_packet(AVFormatContext *s, return 0; } -const AVInputFormat ff_bit_demuxer = { - .name = "bit", - .long_name = NULL_IF_CONFIG_SMALL("G.729 BIT file format"), +const FFInputFormat ff_bit_demuxer = { + .p.name = "bit", + .p.long_name = NULL_IF_CONFIG_SMALL("G.729 BIT file format"), + .p.extensions = "bit", .read_probe = probe, .read_header = read_header, .read_packet = read_packet, - .extensions = "bit", }; #endif #if CONFIG_BIT_MUXER -static int write_header(AVFormatContext *s) +static av_cold int init(AVFormatContext *s) { AVCodecParameters *par = s->streams[0]->codecpar; - if ((par->codec_id != AV_CODEC_ID_G729) || par->ch_layout.nb_channels != 1) { + if (par->ch_layout.nb_channels != 1) { av_log(s, AV_LOG_ERROR, "only codec g729 with 1 channel is supported by this format\n"); return AVERROR(EINVAL); @@ -166,7 +167,10 @@ const FFOutputFormat ff_bit_muxer = { .p.extensions = "bit", .p.audio_codec = AV_CODEC_ID_G729, .p.video_codec = AV_CODEC_ID_NONE, - .write_header = write_header, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, + .init = init, .write_packet = write_packet, }; #endif diff --git a/libavformat/bitstream.c b/libavformat/bitstream.c new file mode 100644 index 00000000000..2afda37c30b --- /dev/null +++ b/libavformat/bitstream.c @@ -0,0 +1 @@ +#include "libavcodec/bitstream.c" diff --git a/libavformat/bluray.c b/libavformat/bluray.c index 635c4f1b876..1845551c34c 100644 --- a/libavformat/bluray.c +++ b/libavformat/bluray.c @@ -23,7 +23,6 @@ #include #include "libavutil/avstring.h" -#include "libavformat/avformat.h" #include "libavformat/url.h" #include "libavutil/opt.h" diff --git a/libavformat/bmv.c b/libavformat/bmv.c index e1f667076e3..b2980cf582c 100644 --- a/libavformat/bmv.c +++ b/libavformat/bmv.c @@ -21,6 +21,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" +#include "demux.h" #include "internal.h" enum BMVFlags { @@ -124,12 +125,12 @@ static int bmv_read_close(AVFormatContext *s) return 0; } -const AVInputFormat ff_bmv_demuxer = { - .name = "bmv", - .long_name = NULL_IF_CONFIG_SMALL("Discworld II BMV"), +const FFInputFormat ff_bmv_demuxer = { + .p.name = "bmv", + .p.long_name = NULL_IF_CONFIG_SMALL("Discworld II BMV"), + .p.extensions = "bmv", .priv_data_size = sizeof(BMVContext), .read_header = bmv_read_header, .read_packet = bmv_read_packet, .read_close = bmv_read_close, - .extensions = "bmv", }; diff --git a/libavformat/boadec.c b/libavformat/boadec.c index 02763142fbb..d70966c97f1 100644 --- a/libavformat/boadec.c +++ b/libavformat/boadec.c @@ -22,6 +22,7 @@ #include "libavutil/intreadwrite.h" #include "libavcodec/internal.h" #include "avformat.h" +#include "demux.h" #include "internal.h" static int probe(const AVProbeData *p) @@ -78,11 +79,11 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) return av_get_packet(s->pb, pkt, st->codecpar->block_align); } -const AVInputFormat ff_boa_demuxer = { - .name = "boa", - .long_name = NULL_IF_CONFIG_SMALL("Black Ops Audio"), +const FFInputFormat ff_boa_demuxer = { + .p.name = "boa", + .p.long_name = NULL_IF_CONFIG_SMALL("Black Ops Audio"), + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = probe, .read_header = read_header, .read_packet = read_packet, - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/bonk.c b/libavformat/bonk.c index bd99c553e70..44de8e2087b 100644 --- a/libavformat/bonk.c +++ b/libavformat/bonk.c @@ -103,15 +103,15 @@ static int bonk_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_bonk_demuxer = { - .name = "bonk", - .long_name = NULL_IF_CONFIG_SMALL("raw Bonk"), +const FFInputFormat ff_bonk_demuxer = { + .p.name = "bonk", + .p.long_name = NULL_IF_CONFIG_SMALL("raw Bonk"), + .p.extensions = "bonk", + .p.flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS, + .p.priv_class = &ff_raw_demuxer_class, .read_probe = bonk_probe, .read_header = bonk_read_header, .read_packet = ff_raw_read_partial_packet, - .extensions = "bonk", - .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS, .raw_codec_id = AV_CODEC_ID_BONK, .priv_data_size = sizeof(FFRawDemuxerContext), - .priv_class = &ff_raw_demuxer_class, }; diff --git a/libavformat/brstm.c b/libavformat/brstm.c index 628c556e667..8b0ba3af67f 100644 --- a/libavformat/brstm.c +++ b/libavformat/brstm.c @@ -467,28 +467,28 @@ static int read_seek(AVFormatContext *s, int stream_index, return 0; } -const AVInputFormat ff_brstm_demuxer = { - .name = "brstm", - .long_name = NULL_IF_CONFIG_SMALL("BRSTM (Binary Revolution Stream)"), +const FFInputFormat ff_brstm_demuxer = { + .p.name = "brstm", + .p.long_name = NULL_IF_CONFIG_SMALL("BRSTM (Binary Revolution Stream)"), + .p.extensions = "brstm", .priv_data_size = sizeof(BRSTMDemuxContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = probe, .read_header = read_header, .read_packet = read_packet, .read_close = read_close, .read_seek = read_seek, - .extensions = "brstm", }; -const AVInputFormat ff_bfstm_demuxer = { - .name = "bfstm", - .long_name = NULL_IF_CONFIG_SMALL("BFSTM (Binary Cafe Stream)"), +const FFInputFormat ff_bfstm_demuxer = { + .p.name = "bfstm", + .p.long_name = NULL_IF_CONFIG_SMALL("BFSTM (Binary Cafe Stream)"), + .p.extensions = "bfstm,bcstm", .priv_data_size = sizeof(BRSTMDemuxContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = probe_bfstm, .read_header = read_header, .read_packet = read_packet, .read_close = read_close, .read_seek = read_seek, - .extensions = "bfstm,bcstm", }; diff --git a/libavformat/c93.c b/libavformat/c93.c index 9ecf1427a9e..933fe4a99e2 100644 --- a/libavformat/c93.c +++ b/libavformat/c93.c @@ -20,6 +20,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" #include "voc.h" #include "libavutil/intreadwrite.h" @@ -185,9 +186,9 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVInputFormat ff_c93_demuxer = { - .name = "c93", - .long_name = NULL_IF_CONFIG_SMALL("Interplay C93"), +const FFInputFormat ff_c93_demuxer = { + .p.name = "c93", + .p.long_name = NULL_IF_CONFIG_SMALL("Interplay C93"), .priv_data_size = sizeof(C93DemuxContext), .read_probe = probe, .read_header = read_header, diff --git a/libavformat/cafdec.c b/libavformat/cafdec.c index e0a9031cb80..07a2939a7a6 100644 --- a/libavformat/cafdec.c +++ b/libavformat/cafdec.c @@ -52,9 +52,15 @@ typedef struct CafContext { static int probe(const AVProbeData *p) { - if (AV_RB32(p->buf) == MKBETAG('c','a','f','f') && AV_RB16(&p->buf[4]) == 1) - return AVPROBE_SCORE_MAX; - return 0; + if (AV_RB32(p->buf) != MKBETAG('c','a','f','f')) + return 0; + if (AV_RB16(&p->buf[4]) != 1) + return 0; + if (AV_RB32(p->buf + 8) != MKBETAG('d','e','s','c')) + return 0; + if (AV_RB64(p->buf + 12) != 32) + return 0; + return AVPROBE_SCORE_MAX; } /** Read audio description chunk */ @@ -265,7 +271,7 @@ static int read_pakt_chunk(AVFormatContext *s, int64_t size) } } - if (avio_tell(pb) - ccount > size) { + if (avio_tell(pb) - ccount > size || size > INT64_MAX - ccount) { av_log(s, AV_LOG_ERROR, "error reading packet table\n"); return AVERROR_INVALIDDATA; } @@ -337,6 +343,9 @@ static int read_header(AVFormatContext *s) avio_skip(pb, 4); /* edit count */ caf->data_start = avio_tell(pb); caf->data_size = size < 0 ? -1 : size - 4; + if (caf->data_start < 0 || caf->data_size > INT64_MAX - caf->data_start) + return AVERROR_INVALIDDATA; + if (caf->data_size > 0 && (pb->seekable & AVIO_SEEKABLE_NORMAL)) avio_skip(pb, caf->data_size); found_data = 1; @@ -506,13 +515,13 @@ static int read_seek(AVFormatContext *s, int stream_index, return 0; } -const AVInputFormat ff_caf_demuxer = { - .name = "caf", - .long_name = NULL_IF_CONFIG_SMALL("Apple CAF (Core Audio Format)"), +const FFInputFormat ff_caf_demuxer = { + .p.name = "caf", + .p.long_name = NULL_IF_CONFIG_SMALL("Apple CAF (Core Audio Format)"), + .p.codec_tag = ff_caf_codec_tags_list, .priv_data_size = sizeof(CafContext), .read_probe = probe, .read_header = read_header, .read_packet = read_packet, .read_seek = read_seek, - .codec_tag = ff_caf_codec_tags_list, }; diff --git a/libavformat/cafenc.c b/libavformat/cafenc.c index 67be59806c0..426bc052099 100644 --- a/libavformat/cafenc.c +++ b/libavformat/cafenc.c @@ -118,11 +118,6 @@ static int caf_write_header(AVFormatContext *s) int64_t chunk_size = 0; int frame_size = par->frame_size, sample_rate = par->sample_rate; - if (s->nb_streams != 1) { - av_log(s, AV_LOG_ERROR, "CAF files have exactly one stream\n"); - return AVERROR(EINVAL); - } - switch (par->codec_id) { case AV_CODEC_ID_AAC: av_log(s, AV_LOG_ERROR, "muxing codec currently unsupported\n"); @@ -284,6 +279,8 @@ const FFOutputFormat ff_caf_muxer = { .priv_data_size = sizeof(CAFContext), .p.audio_codec = AV_CODEC_ID_PCM_S16BE, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH, .write_header = caf_write_header, .write_packet = caf_write_packet, .write_trailer = caf_write_trailer, diff --git a/libavformat/cdg.c b/libavformat/cdg.c index f5982859114..43d919e302e 100644 --- a/libavformat/cdg.c +++ b/libavformat/cdg.c @@ -20,6 +20,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" #define CDG_PACKET_SIZE 24 @@ -45,7 +46,7 @@ static int read_probe(const AVProbeData *p) static int read_header(AVFormatContext *s) { AVStream *vst; - int ret; + int64_t ret; vst = avformat_new_stream(s, NULL); if (!vst) @@ -83,12 +84,12 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_cdg_demuxer = { - .name = "cdg", - .long_name = NULL_IF_CONFIG_SMALL("CD Graphics"), +const FFInputFormat ff_cdg_demuxer = { + .p.name = "cdg", + .p.long_name = NULL_IF_CONFIG_SMALL("CD Graphics"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "cdg", .read_probe = read_probe, .read_header = read_header, .read_packet = read_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "cdg", }; diff --git a/libavformat/cdxl.c b/libavformat/cdxl.c index 065148360ef..0ed426d55a8 100644 --- a/libavformat/cdxl.c +++ b/libavformat/cdxl.c @@ -24,6 +24,7 @@ #include "libavutil/parseutils.h" #include "libavutil/opt.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define CDXL_HEADER_SIZE 32 @@ -257,15 +258,15 @@ static const AVClass cdxl_demuxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_cdxl_demuxer = { - .name = "cdxl", - .long_name = NULL_IF_CONFIG_SMALL("Commodore CDXL video"), +const FFInputFormat ff_cdxl_demuxer = { + .p.name = "cdxl", + .p.long_name = NULL_IF_CONFIG_SMALL("Commodore CDXL video"), + .p.priv_class = &cdxl_demuxer_class, + .p.extensions = "cdxl,xl", + .p.flags = AVFMT_GENERIC_INDEX, .priv_data_size = sizeof(CDXLDemuxContext), - .priv_class = &cdxl_demuxer_class, .read_probe = cdxl_read_probe, .read_header = cdxl_read_header, .read_packet = cdxl_read_packet, .read_seek = read_seek, - .extensions = "cdxl,xl", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/chromaprint.c b/libavformat/chromaprint.c index 9e5fd780c1e..1cdca47ea5a 100644 --- a/libavformat/chromaprint.c +++ b/libavformat/chromaprint.c @@ -58,7 +58,7 @@ static void deinit(AVFormatContext *s) } } -static int write_header(AVFormatContext *s) +static av_cold int init(AVFormatContext *s) { ChromaprintMuxContext *cpr = s->priv_data; AVStream *st; @@ -85,11 +85,6 @@ static int write_header(AVFormatContext *s) #endif } - if (s->nb_streams != 1) { - av_log(s, AV_LOG_ERROR, "Only one stream is supported\n"); - return AVERROR(EINVAL); - } - st = s->streams[0]; if (st->codecpar->ch_layout.nb_channels > 2) { @@ -163,10 +158,10 @@ static int write_trailer(AVFormatContext *s) static const AVOption options[] = { { "silence_threshold", "threshold for detecting silence", OFFSET(silence_threshold), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 32767, FLAGS }, { "algorithm", "version of the fingerprint algorithm", OFFSET(algorithm), AV_OPT_TYPE_INT, { .i64 = CHROMAPRINT_ALGORITHM_DEFAULT }, CHROMAPRINT_ALGORITHM_TEST1, INT_MAX, FLAGS }, - { "fp_format", "fingerprint format to write", OFFSET(fp_format), AV_OPT_TYPE_INT, { .i64 = FINGERPRINT_BASE64 }, FINGERPRINT_RAW, FINGERPRINT_BASE64, FLAGS, "fp_format" }, - { "raw", "binary raw fingerprint", 0, AV_OPT_TYPE_CONST, {.i64 = FINGERPRINT_RAW }, INT_MIN, INT_MAX, FLAGS, "fp_format"}, - { "compressed", "binary compressed fingerprint", 0, AV_OPT_TYPE_CONST, {.i64 = FINGERPRINT_COMPRESSED }, INT_MIN, INT_MAX, FLAGS, "fp_format"}, - { "base64", "Base64 compressed fingerprint", 0, AV_OPT_TYPE_CONST, {.i64 = FINGERPRINT_BASE64 }, INT_MIN, INT_MAX, FLAGS, "fp_format"}, + { "fp_format", "fingerprint format to write", OFFSET(fp_format), AV_OPT_TYPE_INT, { .i64 = FINGERPRINT_BASE64 }, FINGERPRINT_RAW, FINGERPRINT_BASE64, FLAGS, .unit = "fp_format" }, + { "raw", "binary raw fingerprint", 0, AV_OPT_TYPE_CONST, {.i64 = FINGERPRINT_RAW }, INT_MIN, INT_MAX, FLAGS, .unit = "fp_format"}, + { "compressed", "binary compressed fingerprint", 0, AV_OPT_TYPE_CONST, {.i64 = FINGERPRINT_COMPRESSED }, INT_MIN, INT_MAX, FLAGS, .unit = "fp_format"}, + { "base64", "Base64 compressed fingerprint", 0, AV_OPT_TYPE_CONST, {.i64 = FINGERPRINT_BASE64 }, INT_MIN, INT_MAX, FLAGS, .unit = "fp_format"}, { NULL }, }; @@ -182,7 +177,11 @@ const FFOutputFormat ff_chromaprint_muxer = { .p.long_name = NULL_IF_CONFIG_SMALL("Chromaprint"), .priv_data_size = sizeof(ChromaprintMuxContext), .p.audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), - .write_header = write_header, + .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, + .init = init, .write_packet = write_packet, .write_trailer = write_trailer, .deinit = deinit, diff --git a/libavformat/cinedec.c b/libavformat/cinedec.c index e8d9657ee14..9ddfc90b478 100644 --- a/libavformat/cinedec.c +++ b/libavformat/cinedec.c @@ -29,6 +29,7 @@ #include "libavcodec/bmp.h" #include "libavutil/intfloat.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct { @@ -336,9 +337,9 @@ static int cine_read_seek(AVFormatContext *avctx, int stream_index, int64_t time return 0; } -const AVInputFormat ff_cine_demuxer = { - .name = "cine", - .long_name = NULL_IF_CONFIG_SMALL("Phantom Cine"), +const FFInputFormat ff_cine_demuxer = { + .p.name = "cine", + .p.long_name = NULL_IF_CONFIG_SMALL("Phantom Cine"), .priv_data_size = sizeof(CineDemuxContext), .read_probe = cine_read_probe, .read_header = cine_read_header, diff --git a/libavformat/codec2.c b/libavformat/codec2.c index f0f7b892533..dcc3ed9e590 100644 --- a/libavformat/codec2.c +++ b/libavformat/codec2.c @@ -27,6 +27,7 @@ #include "libavutil/opt.h" #include "avio_internal.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "mux.h" #include "rawenc.h" @@ -213,14 +214,7 @@ static int codec2_read_packet(AVFormatContext *s, AVPacket *pkt) static int codec2_write_header(AVFormatContext *s) { - AVStream *st; - - if (s->nb_streams != 1 || s->streams[0]->codecpar->codec_id != AV_CODEC_ID_CODEC2) { - av_log(s, AV_LOG_ERROR, ".c2 files must have exactly one codec2 stream\n"); - return AVERROR(EINVAL); - } - - st = s->streams[0]; + AVStream *st = s->streams[0]; if (st->codecpar->extradata_size != CODEC2_EXTRADATA_SIZE) { av_log(s, AV_LOG_ERROR, ".c2 files require exactly %i bytes of extradata (got %i)\n", @@ -294,18 +288,18 @@ static const AVClass codec2raw_demux_class = { }; #if CONFIG_CODEC2_DEMUXER -const AVInputFormat ff_codec2_demuxer = { - .name = "codec2", - .long_name = NULL_IF_CONFIG_SMALL("codec2 .c2 demuxer"), +const FFInputFormat ff_codec2_demuxer = { + .p.name = "codec2", + .p.long_name = NULL_IF_CONFIG_SMALL("codec2 .c2 demuxer"), + .p.extensions = "c2", + .p.flags = AVFMT_GENERIC_INDEX, + .p.priv_class = &codec2_demux_class, .priv_data_size = sizeof(Codec2Context), - .extensions = "c2", .read_probe = codec2_probe, .read_header = codec2_read_header, .read_packet = codec2_read_packet, .read_seek = ff_pcm_read_seek, - .flags = AVFMT_GENERIC_INDEX, .raw_codec_id = AV_CODEC_ID_CODEC2, - .priv_class = &codec2_demux_class, }; #endif @@ -316,23 +310,25 @@ const FFOutputFormat ff_codec2_muxer = { .p.extensions = "c2", .p.audio_codec = AV_CODEC_ID_CODEC2, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, .p.flags = AVFMT_NOTIMESTAMPS, - .priv_data_size = sizeof(Codec2Context), + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_header = codec2_write_header, .write_packet = ff_raw_write_packet, }; #endif #if CONFIG_CODEC2RAW_DEMUXER -const AVInputFormat ff_codec2raw_demuxer = { - .name = "codec2raw", - .long_name = NULL_IF_CONFIG_SMALL("raw codec2 demuxer"), +const FFInputFormat ff_codec2raw_demuxer = { + .p.name = "codec2raw", + .p.long_name = NULL_IF_CONFIG_SMALL("raw codec2 demuxer"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.priv_class = &codec2raw_demux_class, .priv_data_size = sizeof(Codec2Context), .read_header = codec2raw_read_header, .read_packet = codec2_read_packet, .read_seek = ff_pcm_read_seek, - .flags = AVFMT_GENERIC_INDEX, .raw_codec_id = AV_CODEC_ID_CODEC2, - .priv_class = &codec2raw_demux_class, }; #endif diff --git a/libavformat/concat.c b/libavformat/concat.c index 825e43a7fad..e1d57de557a 100644 --- a/libavformat/concat.c +++ b/libavformat/concat.c @@ -21,13 +21,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include "config_components.h" #include "libavutil/avstring.h" #include "libavutil/bprint.h" +#include "libavutil/error.h" #include "libavutil/mem.h" -#include "avformat.h" #include "avio_internal.h" #include "url.h" diff --git a/libavformat/concatdec.c b/libavformat/concatdec.c index 114b6c6564c..93cab01ce05 100644 --- a/libavformat/concatdec.c +++ b/libavformat/concatdec.c @@ -194,7 +194,6 @@ static int copy_stream_props(AVStream *st, AVStream *source_st) avpriv_set_pts_info(st, 64, source_st->time_base.num, source_st->time_base.den); av_dict_copy(&st->metadata, source_st->metadata, 0); - ff_stream_side_data_copy(st, source_st); return 0; } @@ -324,7 +323,7 @@ static int64_t get_best_effort_duration(ConcatFile *file, AVFormatContext *avf) if (file->user_duration != AV_NOPTS_VALUE) return file->user_duration; if (file->outpoint != AV_NOPTS_VALUE) - return file->outpoint - file->file_inpoint; + return av_sat_sub64(file->outpoint, file->file_inpoint); if (avf->duration > 0) return avf->duration - (file->file_inpoint - file->file_start_time); if (file->next_dts != AV_NOPTS_VALUE) @@ -639,6 +638,17 @@ static int concat_parse_script(AVFormatContext *avf) } } + if (!file) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + if (file->inpoint != AV_NOPTS_VALUE && file->outpoint != AV_NOPTS_VALUE) { + if (file->inpoint > file->outpoint || + file->outpoint - (uint64_t)file->inpoint > INT64_MAX) + ret = AVERROR_INVALIDDATA; + } + fail: for (arg = 0; arg < MAX_ARGS; arg++) av_freep(&arg_str[arg]); @@ -674,6 +684,8 @@ static int concat_read_header(AVFormatContext *avf) cat->files[i].user_duration = cat->files[i].outpoint - cat->files[i].inpoint; } cat->files[i].duration = cat->files[i].user_duration; + if (time + (uint64_t)cat->files[i].user_duration > INT64_MAX) + return AVERROR_INVALIDDATA; time += cat->files[i].user_duration; } if (i == cat->nb_files) { @@ -937,15 +949,15 @@ static const AVClass concat_class = { }; -const AVInputFormat ff_concat_demuxer = { - .name = "concat", - .long_name = NULL_IF_CONFIG_SMALL("Virtual concatenation script"), +const FFInputFormat ff_concat_demuxer = { + .p.name = "concat", + .p.long_name = NULL_IF_CONFIG_SMALL("Virtual concatenation script"), + .p.priv_class = &concat_class, .priv_data_size = sizeof(ConcatContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = concat_probe, .read_header = concat_read_header, .read_packet = concat_read_packet, .read_close = concat_read_close, .read_seek2 = concat_seek, - .priv_class = &concat_class, }; diff --git a/libavformat/crypto.c b/libavformat/crypto.c index 1d4514e0f27..4393fb63992 100644 --- a/libavformat/crypto.c +++ b/libavformat/crypto.c @@ -19,11 +19,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "avformat.h" #include "libavutil/aes.h" #include "libavutil/avstring.h" #include "libavutil/opt.h" -#include "internal.h" #include "url.h" // encourage reads of 4096 bytes - 1 block is always retained. @@ -256,7 +254,7 @@ static int64_t crypto_seek(URLContext *h, int64_t pos, int whence) newpos = ffurl_seek( c->hd, pos, AVSEEK_SIZE ); if (newpos < 0) { av_log(h, AV_LOG_ERROR, - "Crypto: seek_end - can't get file size (pos=%lld)\r\n", (long long int)pos); + "Crypto: seek_end - can't get file size (pos=%"PRId64")\r\n", pos); return newpos; } pos = newpos - pos; diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c index 29d4680c68d..63070b77be2 100644 --- a/libavformat/dashdec.c +++ b/libavformat/dashdec.c @@ -20,6 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include +#include #include "libavutil/bprint.h" #include "libavutil/opt.h" #include "libavutil/time.h" @@ -28,6 +29,7 @@ #include "avio_internal.h" #include "dash.h" #include "demux.h" +#include "url.h" #define INITIAL_BUFFER_SIZE 32768 @@ -1952,17 +1954,6 @@ static int open_demux_for_component(AVFormatContext *s, struct representation *p // copy disposition st->disposition = ist->disposition; - - // copy side data - for (int i = 0; i < ist->nb_side_data; i++) { - const AVPacketSideData *sd_src = &ist->side_data[i]; - uint8_t *dst_data; - - dst_data = av_stream_new_side_data(st, sd_src->type, sd_src->size); - if (!dst_data) - return AVERROR(ENOMEM); - memcpy(dst_data, sd_src->data, sd_src->size); - } } return 0; @@ -2364,16 +2355,16 @@ static const AVClass dash_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_dash_demuxer = { - .name = "dash", - .long_name = NULL_IF_CONFIG_SMALL("Dynamic Adaptive Streaming over HTTP"), - .priv_class = &dash_class, +const FFInputFormat ff_dash_demuxer = { + .p.name = "dash", + .p.long_name = NULL_IF_CONFIG_SMALL("Dynamic Adaptive Streaming over HTTP"), + .p.priv_class = &dash_class, + .p.flags = AVFMT_NO_BYTE_SEEK, .priv_data_size = sizeof(DASHContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = dash_probe, .read_header = dash_read_header, .read_packet = dash_read_packet, .read_close = dash_close, .read_seek = dash_read_seek, - .flags = AVFMT_NO_BYTE_SEEK, }; diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 17fe5f430c6..5cb38c4c316 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -22,6 +22,7 @@ #include "config.h" #include "config_components.h" +#include #if HAVE_UNISTD_H #include #endif @@ -204,26 +205,16 @@ typedef struct DASHContext { int64_t update_period; } DASHContext; -static struct codec_string { - int id; - const char *str; +static const struct codec_string { + enum AVCodecID id; + const char str[8]; } codecs[] = { { AV_CODEC_ID_VP8, "vp8" }, { AV_CODEC_ID_VP9, "vp9" }, { AV_CODEC_ID_VORBIS, "vorbis" }, { AV_CODEC_ID_OPUS, "opus" }, { AV_CODEC_ID_FLAC, "flac" }, - { 0, NULL } -}; - -static struct format_string { - SegmentType segment_type; - const char *str; -} formats[] = { - { SEGMENT_TYPE_AUTO, "auto" }, - { SEGMENT_TYPE_MP4, "mp4" }, - { SEGMENT_TYPE_WEBM, "webm" }, - { 0, NULL } + { AV_CODEC_ID_NONE } }; static int dashenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, @@ -264,11 +255,12 @@ static void dashenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filenam } } -static const char *get_format_str(SegmentType segment_type) { - int i; - for (i = 0; i < SEGMENT_TYPE_NB; i++) - if (formats[i].segment_type == segment_type) - return formats[i].str; +static const char *get_format_str(SegmentType segment_type) +{ + switch (segment_type) { + case SEGMENT_TYPE_MP4: return "mp4"; + case SEGMENT_TYPE_WEBM: return "webm"; + } return NULL; } @@ -359,7 +351,7 @@ static void set_codec_str(AVFormatContext *s, AVCodecParameters *par, int i; // common Webm codecs are not part of RFC 6381 - for (i = 0; codecs[i].id; i++) + for (i = 0; codecs[i].id != AV_CODEC_ID_NONE; i++) if (codecs[i].id == par->codec_id) { if (codecs[i].id == AV_CODEC_ID_VP9) { set_vp9_codec_str(s, par, frame_rate, str, size); @@ -1291,8 +1283,9 @@ static int write_manifest(AVFormatContext *s, int final) if (os->segment_type != SEGMENT_TYPE_MP4) continue; get_hls_playlist_name(playlist_file, sizeof(playlist_file), NULL, i); - ff_hls_write_audio_rendition(c->m3u8_out, (char *)audio_group, - playlist_file, NULL, i, is_default); + ff_hls_write_audio_rendition(c->m3u8_out, audio_group, + playlist_file, NULL, i, is_default, + s->streams[i]->codecpar->ch_layout.nb_channels); max_audio_bitrate = FFMAX(st->codecpar->bit_rate + os->muxer_overhead, max_audio_bitrate); if (!av_strnstr(audio_codec_str, os->codec_str, sizeof(audio_codec_str))) { @@ -1308,7 +1301,7 @@ static int write_manifest(AVFormatContext *s, int final) char codec_str[128]; AVStream *st = s->streams[i]; OutputStream *os = &c->streams[i]; - char *agroup = NULL; + const char *agroup = NULL; int stream_bitrate = os->muxer_overhead; if (os->bit_rate > 0) stream_bitrate += os->bit_rate; @@ -1322,7 +1315,7 @@ static int write_manifest(AVFormatContext *s, int final) continue; av_strlcpy(codec_str, os->codec_str, sizeof(codec_str)); if (max_audio_bitrate) { - agroup = (char *)audio_group; + agroup = audio_group; stream_bitrate += max_audio_bitrate; av_strlcat(codec_str, ",", sizeof(codec_str)); av_strlcat(codec_str, audio_codec_str, sizeof(codec_str)); @@ -1461,7 +1454,7 @@ static int dash_init(AVFormatContext *s) } if (av_cmp_q(c->max_playback_rate, c->min_playback_rate) < 0) { - av_log(s, AV_LOG_WARNING, "Minimum playback rate value is higer than the Maximum. Both will be ignored\n"); + av_log(s, AV_LOG_WARNING, "Minimum playback rate value is higher than the Maximum. Both will be ignored\n"); c->min_playback_rate = c->max_playback_rate = (AVRational) {1, 1}; } @@ -1551,11 +1544,6 @@ static int dash_init(AVFormatContext *s) return AVERROR_MUXER_NOT_FOUND; ctx->interrupt_callback = s->interrupt_callback; ctx->opaque = s->opaque; -#if FF_API_AVFORMAT_IO_CLOSE -FF_DISABLE_DEPRECATION_WARNINGS - ctx->io_close = s->io_close; -FF_ENABLE_DEPRECATION_WARNINGS -#endif ctx->io_close2 = s->io_close2; ctx->io_open = s->io_open; ctx->strict_std_compliance = s->strict_std_compliance; @@ -1754,7 +1742,7 @@ static int dash_write_header(AVFormatContext *s) (ret = flush_init_segment(s, os)) < 0) return ret; } - return ret; + return 0; } static int add_segment(OutputStream *os, const char *file, @@ -2369,50 +2357,50 @@ static int dash_check_bitstream(AVFormatContext *s, AVStream *st, #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { { "adaptation_sets", "Adaptation sets. Syntax: id=0,streams=0,1,2 id=1,streams=3,4 and so on", OFFSET(adaptation_sets), AV_OPT_TYPE_STRING, { 0 }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, - { "window_size", "number of segments kept in the manifest", OFFSET(window_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, E }, + { "dash_segment_type", "set dash segment files type", OFFSET(segment_type_option), AV_OPT_TYPE_INT, {.i64 = SEGMENT_TYPE_AUTO }, 0, SEGMENT_TYPE_NB - 1, E, .unit = "segment_type"}, + { "auto", "select segment file format based on codec", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_AUTO }, 0, UINT_MAX, E, .unit = "segment_type"}, + { "mp4", "make segment file in ISOBMFF format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MP4 }, 0, UINT_MAX, E, .unit = "segment_type"}, + { "webm", "make segment file in WebM format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_WEBM }, 0, UINT_MAX, E, .unit = "segment_type"}, { "extra_window_size", "number of segments kept outside of the manifest before removing from disk", OFFSET(extra_window_size), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX, E }, - { "seg_duration", "segment duration (in seconds, fractional value can be set)", OFFSET(seg_duration), AV_OPT_TYPE_DURATION, { .i64 = 5000000 }, 0, INT_MAX, E }, - { "frag_duration", "fragment duration (in seconds, fractional value can be set)", OFFSET(frag_duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT_MAX, E }, - { "frag_type", "set type of interval for fragments", OFFSET(frag_type), AV_OPT_TYPE_INT, {.i64 = FRAG_TYPE_NONE }, 0, FRAG_TYPE_NB - 1, E, "frag_type"}, - { "none", "one fragment per segment", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_NONE }, 0, UINT_MAX, E, "frag_type"}, - { "every_frame", "fragment at every frame", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_EVERY_FRAME }, 0, UINT_MAX, E, "frag_type"}, - { "duration", "fragment at specific time intervals", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_DURATION }, 0, UINT_MAX, E, "frag_type"}, - { "pframes", "fragment at keyframes and following P-Frame reordering (Video only, experimental)", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_PFRAMES }, 0, UINT_MAX, E, "frag_type"}, - { "remove_at_exit", "remove all segments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, - { "use_template", "Use SegmentTemplate instead of SegmentList", OFFSET(use_template), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E }, - { "use_timeline", "Use SegmentTimeline in SegmentTemplate", OFFSET(use_timeline), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E }, - { "single_file", "Store all segments in one file, accessed using byte ranges", OFFSET(single_file), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, - { "single_file_name", "DASH-templated name to be used for baseURL. Implies storing all segments in one file, accessed using byte ranges", OFFSET(single_file_name), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E }, - { "init_seg_name", "DASH-templated name to used for the initialization segment", OFFSET(init_seg_name), AV_OPT_TYPE_STRING, {.str = "init-stream$RepresentationID$.$ext$"}, 0, 0, E }, - { "media_seg_name", "DASH-templated name to used for the media segments", OFFSET(media_seg_name), AV_OPT_TYPE_STRING, {.str = "chunk-stream$RepresentationID$-$Number%05d$.$ext$"}, 0, 0, E }, - { "utc_timing_url", "URL of the page that will return the UTC timestamp in ISO format", OFFSET(utc_timing_url), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E }, - { "method", "set the HTTP method", OFFSET(method), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, - { "http_user_agent", "override User-Agent field in HTTP header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, - { "http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, - { "hls_playlist", "Generate HLS playlist files(master.m3u8, media_%d.m3u8)", OFFSET(hls_playlist), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, - { "hls_master_name", "HLS master playlist name", OFFSET(hls_master_name), AV_OPT_TYPE_STRING, {.str = "master.m3u8"}, 0, 0, E }, - { "streaming", "Enable/Disable streaming mode of output. Each frame will be moof fragment", OFFSET(streaming), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, - { "timeout", "set timeout for socket I/O operations", OFFSET(timeout), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX, .flags = E }, - { "index_correction", "Enable/Disable segment index correction logic", OFFSET(index_correction), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, { "format_options","set list of options for the container format (mp4/webm) used for dash", OFFSET(format_options), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, E}, + { "frag_duration", "fragment duration (in seconds, fractional value can be set)", OFFSET(frag_duration), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT_MAX, E }, + { "frag_type", "set type of interval for fragments", OFFSET(frag_type), AV_OPT_TYPE_INT, {.i64 = FRAG_TYPE_NONE }, 0, FRAG_TYPE_NB - 1, E, .unit = "frag_type"}, + { "none", "one fragment per segment", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_NONE }, 0, UINT_MAX, E, .unit = "frag_type"}, + { "every_frame", "fragment at every frame", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_EVERY_FRAME }, 0, UINT_MAX, E, .unit = "frag_type"}, + { "duration", "fragment at specific time intervals", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_DURATION }, 0, UINT_MAX, E, .unit = "frag_type"}, + { "pframes", "fragment at keyframes and following P-Frame reordering (Video only, experimental)", 0, AV_OPT_TYPE_CONST, {.i64 = FRAG_TYPE_PFRAMES }, 0, UINT_MAX, E, .unit = "frag_type"}, { "global_sidx", "Write global SIDX atom. Applicable only for single file, mp4 output, non-streaming mode", OFFSET(global_sidx), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, - { "dash_segment_type", "set dash segment files type", OFFSET(segment_type_option), AV_OPT_TYPE_INT, {.i64 = SEGMENT_TYPE_AUTO }, 0, SEGMENT_TYPE_NB - 1, E, "segment_type"}, - { "auto", "select segment file format based on codec", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_AUTO }, 0, UINT_MAX, E, "segment_type"}, - { "mp4", "make segment file in ISOBMFF format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MP4 }, 0, UINT_MAX, E, "segment_type"}, - { "webm", "make segment file in WebM format", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_WEBM }, 0, UINT_MAX, E, "segment_type"}, + { "hls_master_name", "HLS master playlist name", OFFSET(hls_master_name), AV_OPT_TYPE_STRING, {.str = "master.m3u8"}, 0, 0, E }, + { "hls_playlist", "Generate HLS playlist files(master.m3u8, media_%d.m3u8)", OFFSET(hls_playlist), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, + { "http_opts", "HTTP protocol options", OFFSET(http_opts), AV_OPT_TYPE_DICT, { .str = NULL }, 0, 0, E }, + { "http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, + { "http_user_agent", "override User-Agent field in HTTP header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, { "ignore_io_errors", "Ignore IO errors during open and write. Useful for long-duration runs with network output", OFFSET(ignore_io_errors), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, - { "lhls", "Enable Low-latency HLS(Experimental). Adds #EXT-X-PREFETCH tag with current segment's URI", OFFSET(lhls), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, + { "index_correction", "Enable/Disable segment index correction logic", OFFSET(index_correction), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, + { "init_seg_name", "DASH-templated name to used for the initialization segment", OFFSET(init_seg_name), AV_OPT_TYPE_STRING, {.str = "init-stream$RepresentationID$.$ext$"}, 0, 0, E }, { "ldash", "Enable Low-latency dash. Constrains the value of a few elements", OFFSET(ldash), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, + { "lhls", "Enable Low-latency HLS(Experimental). Adds #EXT-X-PREFETCH tag with current segment's URI", OFFSET(lhls), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, { "master_m3u8_publish_rate", "Publish master playlist every after this many segment intervals", OFFSET(master_publish_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, UINT_MAX, E}, - { "write_prft", "Write producer reference time element", OFFSET(write_prft), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, E}, - { "mpd_profile", "Set profiles. Elements and values used in the manifest may be constrained by them", OFFSET(profile), AV_OPT_TYPE_FLAGS, {.i64 = MPD_PROFILE_DASH }, 0, UINT_MAX, E, "mpd_profile"}, - { "dash", "MPEG-DASH ISO Base media file format live profile", 0, AV_OPT_TYPE_CONST, {.i64 = MPD_PROFILE_DASH }, 0, UINT_MAX, E, "mpd_profile"}, - { "dvb_dash", "DVB-DASH profile", 0, AV_OPT_TYPE_CONST, {.i64 = MPD_PROFILE_DVB }, 0, UINT_MAX, E, "mpd_profile"}, - { "http_opts", "HTTP protocol options", OFFSET(http_opts), AV_OPT_TYPE_DICT, { .str = NULL }, 0, 0, E }, - { "target_latency", "Set desired target latency for Low-latency dash", OFFSET(target_latency), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT_MAX, E }, - { "min_playback_rate", "Set desired minimum playback rate", OFFSET(min_playback_rate), AV_OPT_TYPE_RATIONAL, { .dbl = 1.0 }, 0.5, 1.5, E }, { "max_playback_rate", "Set desired maximum playback rate", OFFSET(max_playback_rate), AV_OPT_TYPE_RATIONAL, { .dbl = 1.0 }, 0.5, 1.5, E }, + { "media_seg_name", "DASH-templated name to used for the media segments", OFFSET(media_seg_name), AV_OPT_TYPE_STRING, {.str = "chunk-stream$RepresentationID$-$Number%05d$.$ext$"}, 0, 0, E }, + { "method", "set the HTTP method", OFFSET(method), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, + { "min_playback_rate", "Set desired minimum playback rate", OFFSET(min_playback_rate), AV_OPT_TYPE_RATIONAL, { .dbl = 1.0 }, 0.5, 1.5, E }, + { "mpd_profile", "Set profiles. Elements and values used in the manifest may be constrained by them", OFFSET(profile), AV_OPT_TYPE_FLAGS, {.i64 = MPD_PROFILE_DASH }, 0, UINT_MAX, E, .unit = "mpd_profile"}, + { "dash", "MPEG-DASH ISO Base media file format live profile", 0, AV_OPT_TYPE_CONST, {.i64 = MPD_PROFILE_DASH }, 0, UINT_MAX, E, .unit = "mpd_profile"}, + { "dvb_dash", "DVB-DASH profile", 0, AV_OPT_TYPE_CONST, {.i64 = MPD_PROFILE_DVB }, 0, UINT_MAX, E, .unit = "mpd_profile"}, + { "remove_at_exit", "remove all segments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, + { "seg_duration", "segment duration (in seconds, fractional value can be set)", OFFSET(seg_duration), AV_OPT_TYPE_DURATION, { .i64 = 5000000 }, 0, INT_MAX, E }, + { "single_file", "Store all segments in one file, accessed using byte ranges", OFFSET(single_file), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, + { "single_file_name", "DASH-templated name to be used for baseURL. Implies storing all segments in one file, accessed using byte ranges", OFFSET(single_file_name), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E }, + { "streaming", "Enable/Disable streaming mode of output. Each frame will be moof fragment", OFFSET(streaming), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, + { "target_latency", "Set desired target latency for Low-latency dash", OFFSET(target_latency), AV_OPT_TYPE_DURATION, { .i64 = 0 }, 0, INT_MAX, E }, + { "timeout", "set timeout for socket I/O operations", OFFSET(timeout), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX, .flags = E }, { "update_period", "Set the mpd update interval", OFFSET(update_period), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E}, + { "use_template", "Use SegmentTemplate instead of SegmentList", OFFSET(use_template), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E }, + { "use_timeline", "Use SegmentTimeline in SegmentTemplate", OFFSET(use_timeline), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, E }, + { "utc_timing_url", "URL of the page that will return the UTC timestamp in ISO format", OFFSET(utc_timing_url), AV_OPT_TYPE_STRING, { 0 }, 0, 0, E }, + { "window_size", "number of segments kept in the manifest", OFFSET(window_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, E }, + { "write_prft", "Write producer reference time element", OFFSET(write_prft), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, E}, { NULL }, }; diff --git a/libavformat/dauddec.c b/libavformat/dauddec.c index dbbd39a3b40..7631cd70658 100644 --- a/libavformat/dauddec.c +++ b/libavformat/dauddec.c @@ -21,6 +21,8 @@ #include "libavutil/channel_layout.h" #include "avformat.h" +#include "demux.h" +#include "internal.h" static int daud_header(AVFormatContext *s) { AVStream *st = avformat_new_stream(s, NULL); @@ -34,6 +36,9 @@ static int daud_header(AVFormatContext *s) { st->codecpar->bit_rate = 3 * 6 * 96000 * 8; st->codecpar->block_align = 3 * 6; st->codecpar->bits_per_coded_sample = 24; + + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); + return 0; } @@ -41,7 +46,7 @@ static int daud_packet(AVFormatContext *s, AVPacket *pkt) { AVIOContext *pb = s->pb; int ret, size; if (avio_feof(pb)) - return AVERROR(EIO); + return AVERROR_EOF; size = avio_rb16(pb); avio_rb16(pb); // unknown ret = av_get_packet(pb, pkt, size); @@ -49,10 +54,10 @@ static int daud_packet(AVFormatContext *s, AVPacket *pkt) { return ret; } -const AVInputFormat ff_daud_demuxer = { - .name = "daud", - .long_name = NULL_IF_CONFIG_SMALL("D-Cinema audio"), +const FFInputFormat ff_daud_demuxer = { + .p.name = "daud", + .p.long_name = NULL_IF_CONFIG_SMALL("D-Cinema audio"), + .p.extensions = "302,daud", .read_header = daud_header, .read_packet = daud_packet, - .extensions = "302,daud", }; diff --git a/libavformat/daudenc.c b/libavformat/daudenc.c index 2d84b16650e..5c8db5104ee 100644 --- a/libavformat/daudenc.c +++ b/libavformat/daudenc.c @@ -25,18 +25,31 @@ static int daud_init(struct AVFormatContext *s) { AVCodecParameters *par = s->streams[0]->codecpar; - if (par->ch_layout.nb_channels != 6 || par->sample_rate != 96000) + int ret; + + if (par->ch_layout.nb_channels != 6) { + av_log(s, AV_LOG_ERROR, + "Invalid number of channels %d, must be exactly 6\n", + par->ch_layout.nb_channels); + return AVERROR(EINVAL); + } + + if (par->sample_rate != 96000) { + av_log(s, AV_LOG_ERROR, + "Invalid sample rate %d, must be 96000\n", + par->sample_rate); return AVERROR(EINVAL); + } + + ret = ff_stream_add_bitstream_filter(s->streams[0], "pcm_rechunk", "n=2000:pad=0"); + if (ret < 0) + return ret; + return 0; } static int daud_write_packet(struct AVFormatContext *s, AVPacket *pkt) { - if (pkt->size > 65535) { - av_log(s, AV_LOG_ERROR, - "Packet size too large for s302m. (%d > 65535)\n", pkt->size); - return AVERROR_INVALIDDATA; - } avio_wb16(s->pb, pkt->size); avio_wb16(s->pb, 0x8010); // unknown avio_write(s->pb, pkt->data, pkt->size); @@ -49,7 +62,10 @@ const FFOutputFormat ff_daud_muxer = { .p.extensions = "302", .p.audio_codec = AV_CODEC_ID_PCM_S24DAUD, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, .p.flags = AVFMT_NOTIMESTAMPS, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .init = daud_init, .write_packet = daud_write_packet, }; diff --git a/libavformat/dcstr.c b/libavformat/dcstr.c index 286ec92df3e..3badb7d4c89 100644 --- a/libavformat/dcstr.c +++ b/libavformat/dcstr.c @@ -20,6 +20,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" static int dcstr_probe(const AVProbeData *p) @@ -80,12 +81,12 @@ static int dcstr_read_packet(AVFormatContext *s, AVPacket *pkt) return av_get_packet(s->pb, pkt, par->block_align); } -const AVInputFormat ff_dcstr_demuxer = { - .name = "dcstr", - .long_name = NULL_IF_CONFIG_SMALL("Sega DC STR"), +const FFInputFormat ff_dcstr_demuxer = { + .p.name = "dcstr", + .p.long_name = NULL_IF_CONFIG_SMALL("Sega DC STR"), + .p.extensions = "str", + .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK | AVFMT_NOBINSEARCH, .read_probe = dcstr_probe, .read_header = dcstr_read_header, .read_packet = dcstr_read_packet, - .extensions = "str", - .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK | AVFMT_NOBINSEARCH, }; diff --git a/libavformat/demux.c b/libavformat/demux.c index b19ab86d08d..5027b84206c 100644 --- a/libavformat/demux.c +++ b/libavformat/demux.c @@ -36,6 +36,7 @@ #include "libavcodec/avcodec.h" #include "libavcodec/bsf.h" +#include "libavcodec/codec_desc.h" #include "libavcodec/internal.h" #include "libavcodec/packet_internal.h" #include "libavcodec/raw.h" @@ -120,6 +121,8 @@ static int set_codec_from_probe_data(AVFormatContext *s, AVStream *st, { "mp3", AV_CODEC_ID_MP3, AVMEDIA_TYPE_AUDIO }, { "mpegvideo", AV_CODEC_ID_MPEG2VIDEO, AVMEDIA_TYPE_VIDEO }, { "truehd", AV_CODEC_ID_TRUEHD, AVMEDIA_TYPE_AUDIO }, + { "evc", AV_CODEC_ID_EVC, AVMEDIA_TYPE_VIDEO }, + { "vvc", AV_CODEC_ID_VVC, AVMEDIA_TYPE_VIDEO }, { 0 } }; int score; @@ -196,23 +199,13 @@ static int update_stream_avctx(AVFormatContext *s) sti->parser = NULL; } -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (st->codecpar->ch_layout.nb_channels && - !st->codecpar->channels) { - st->codecpar->channels = st->codecpar->ch_layout.nb_channels; - st->codecpar->channel_layout = st->codecpar->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ? - st->codecpar->ch_layout.u.mask : 0; - - } -FF_ENABLE_DEPRECATION_WARNINGS -#endif - /* update internal codec context, for the parser */ ret = avcodec_parameters_to_context(sti->avctx, st->codecpar); if (ret < 0) return ret; + sti->codec_desc = avcodec_descriptor_get(sti->avctx->codec_id); + sti->need_context_update = 0; } return 0; @@ -290,8 +283,8 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, s->duration = s->start_time = AV_NOPTS_VALUE; /* Allocate private data. */ - if (s->iformat->priv_data_size > 0) { - if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) { + if (ffifmt(s->iformat)->priv_data_size > 0) { + if (!(s->priv_data = av_mallocz(ffifmt(s->iformat)->priv_data_size))) { ret = AVERROR(ENOMEM); goto fail; } @@ -307,9 +300,9 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, if (s->pb) ff_id3v2_read_dict(s->pb, &si->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta); - if (s->iformat->read_header) - if ((ret = s->iformat->read_header(s)) < 0) { - if (s->iformat->flags_internal & FF_FMT_INIT_CLEANUP) + if (ffifmt(s->iformat)->read_header) + if ((ret = ffifmt(s->iformat)->read_header(s)) < 0) { + if (ffifmt(s->iformat)->flags_internal & FF_INFMT_FLAG_INIT_CLEANUP) goto close; goto fail; } @@ -354,8 +347,8 @@ int avformat_open_input(AVFormatContext **ps, const char *filename, return 0; close: - if (s->iformat->read_close) - s->iformat->read_close(s); + if (ffifmt(s->iformat)->read_close) + ffifmt(s->iformat)->read_close(s); fail: ff_id3v2_free_extra_meta(&id3v2_extra_meta); av_dict_free(&tmp); @@ -382,8 +375,8 @@ void avformat_close_input(AVFormatContext **ps) pb = NULL; if (s->iformat) - if (s->iformat->read_close) - s->iformat->read_close(s); + if (ffifmt(s->iformat)->read_close) + ffifmt(s->iformat)->read_close(s); avformat_free_context(s); @@ -535,6 +528,90 @@ static int update_wrap_reference(AVFormatContext *s, AVStream *st, int stream_in return 1; } +static void update_timestamps(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + FFStream *const sti = ffstream(st); + + if (update_wrap_reference(s, st, pkt->stream_index, pkt) && sti->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) { + // correct first time stamps to negative values + if (!is_relative(sti->first_dts)) + sti->first_dts = wrap_timestamp(st, sti->first_dts); + if (!is_relative(st->start_time)) + st->start_time = wrap_timestamp(st, st->start_time); + if (!is_relative(sti->cur_dts)) + sti->cur_dts = wrap_timestamp(st, sti->cur_dts); + } + + pkt->dts = wrap_timestamp(st, pkt->dts); + pkt->pts = wrap_timestamp(st, pkt->pts); + + force_codec_ids(s, st); + + /* TODO: audio: time filter; video: frame reordering (pts != dts) */ + if (s->use_wallclock_as_timestamps) + pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base); +} + +/** + * Handle a new packet and either return it directly if possible and + * allow_passthrough is true or queue the packet (or drop the packet + * if corrupt). + * + * @return < 0 on error, 0 if the packet was passed through, + * 1 if it was queued or dropped + */ +static int handle_new_packet(AVFormatContext *s, AVPacket *pkt, int allow_passthrough) +{ + FFFormatContext *const si = ffformatcontext(s); + AVStream *st; + FFStream *sti; + int err; + + av_assert0(pkt->stream_index < (unsigned)s->nb_streams && + "Invalid stream index.\n"); + + if (pkt->flags & AV_PKT_FLAG_CORRUPT) { + av_log(s, AV_LOG_WARNING, + "Packet corrupt (stream = %d, dts = %s)%s.\n", + pkt->stream_index, av_ts2str(pkt->dts), + s->flags & AVFMT_FLAG_DISCARD_CORRUPT ? ", dropping it" : ""); + if (s->flags & AVFMT_FLAG_DISCARD_CORRUPT) { + av_packet_unref(pkt); + return 1; + } + } + + st = s->streams[pkt->stream_index]; + sti = ffstream(st); + + update_timestamps(s, st, pkt); + + if (sti->request_probe <= 0 && allow_passthrough && !si->raw_packet_buffer.head) + return 0; + + err = avpriv_packet_list_put(&si->raw_packet_buffer, pkt, NULL, 0); + if (err < 0) { + av_packet_unref(pkt); + return err; + } + + pkt = &si->raw_packet_buffer.tail->pkt; + si->raw_packet_buffer_size += pkt->size; + + err = probe_codec(s, st, pkt); + if (err < 0) + return err; + + return 1; +} + +int ff_buffer_packet(AVFormatContext *s, AVPacket *pkt) +{ + int err = handle_new_packet(s, pkt, 0); + + return err < 0 ? err : 0; +} + int ff_read_packet(AVFormatContext *s, AVPacket *pkt) { FFFormatContext *const si = ffformatcontext(s); @@ -552,9 +629,6 @@ FF_ENABLE_DEPRECATION_WARNINGS for (;;) { PacketListEntry *pktl = si->raw_packet_buffer.head; - AVStream *st; - FFStream *sti; - const AVPacket *pkt1; if (pktl) { AVStream *const st = s->streams[pktl->pkt.stream_index]; @@ -568,7 +642,7 @@ FF_ENABLE_DEPRECATION_WARNINGS } } - err = s->iformat->read_packet(s, pkt); + err = ffifmt(s->iformat)->read_packet(s, pkt); if (err < 0) { av_packet_unref(pkt); @@ -596,56 +670,8 @@ FF_ENABLE_DEPRECATION_WARNINGS return err; } - if (pkt->flags & AV_PKT_FLAG_CORRUPT) { - av_log(s, AV_LOG_WARNING, - "Packet corrupt (stream = %d, dts = %s)", - pkt->stream_index, av_ts2str(pkt->dts)); - if (s->flags & AVFMT_FLAG_DISCARD_CORRUPT) { - av_log(s, AV_LOG_WARNING, ", dropping it.\n"); - av_packet_unref(pkt); - continue; - } - av_log(s, AV_LOG_WARNING, ".\n"); - } - - av_assert0(pkt->stream_index < (unsigned)s->nb_streams && - "Invalid stream index.\n"); - - st = s->streams[pkt->stream_index]; - sti = ffstream(st); - - if (update_wrap_reference(s, st, pkt->stream_index, pkt) && sti->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) { - // correct first time stamps to negative values - if (!is_relative(sti->first_dts)) - sti->first_dts = wrap_timestamp(st, sti->first_dts); - if (!is_relative(st->start_time)) - st->start_time = wrap_timestamp(st, st->start_time); - if (!is_relative(sti->cur_dts)) - sti->cur_dts = wrap_timestamp(st, sti->cur_dts); - } - - pkt->dts = wrap_timestamp(st, pkt->dts); - pkt->pts = wrap_timestamp(st, pkt->pts); - - force_codec_ids(s, st); - - /* TODO: audio: time filter; video: frame reordering (pts != dts) */ - if (s->use_wallclock_as_timestamps) - pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base); - - if (!pktl && sti->request_probe <= 0) - return 0; - - err = avpriv_packet_list_put(&si->raw_packet_buffer, - pkt, NULL, 0); - if (err < 0) { - av_packet_unref(pkt); - return err; - } - pkt1 = &si->raw_packet_buffer.tail->pkt; - si->raw_packet_buffer_size += pkt1->size; - - if ((err = probe_codec(s, st, pkt1)) < 0) + err = handle_new_packet(s, pkt, 1); + if (err <= 0) /* Error or passthrough */ return err; } } @@ -668,14 +694,20 @@ static void compute_frame_duration(AVFormatContext *s, int *pnum, int *pden, if (st->r_frame_rate.num && (!pc || !codec_framerate.num)) { *pnum = st->r_frame_rate.den; *pden = st->r_frame_rate.num; + } else if ((s->iformat->flags & AVFMT_NOTIMESTAMPS) && + !codec_framerate.num && + st->avg_frame_rate.num && st->avg_frame_rate.den) { + *pnum = st->avg_frame_rate.den; + *pden = st->avg_frame_rate.num; } else if (st->time_base.num * 1000LL > st->time_base.den) { *pnum = st->time_base.num; *pden = st->time_base.den; } else if (codec_framerate.den * 1000LL > codec_framerate.num) { - av_assert0(sti->avctx->ticks_per_frame); + int ticks_per_frame = (sti->codec_desc && + (sti->codec_desc->props & AV_CODEC_PROP_FIELDS)) ? 2 : 1; av_reduce(pnum, pden, codec_framerate.den, - codec_framerate.num * (int64_t)sti->avctx->ticks_per_frame, + codec_framerate.num * (int64_t)ticks_per_frame, INT_MAX); if (pc && pc->repeat_pict) { @@ -687,7 +719,8 @@ static void compute_frame_duration(AVFormatContext *s, int *pnum, int *pden, /* If this codec can be interlaced or progressive then we need * a parser to compute duration of a packet. Thus if we have * no parser in such case leave duration undefined. */ - if (sti->avctx->ticks_per_frame > 1 && !pc) + if (sti->codec_desc && + (sti->codec_desc->props & AV_CODEC_PROP_FIELDS) && !pc) *pnum = *pden = 0; } break; @@ -743,7 +776,8 @@ static int64_t select_from_pts_buffer(AVStream *st, int64_t *pts_buffer, int64_t { FFStream *const sti = ffstream(st); int onein_oneout = st->codecpar->codec_id != AV_CODEC_ID_H264 && - st->codecpar->codec_id != AV_CODEC_ID_HEVC; + st->codecpar->codec_id != AV_CODEC_ID_HEVC && + st->codecpar->codec_id != AV_CODEC_ID_VVC; if (!onein_oneout) { int delay = sti->avctx->has_b_frames; @@ -933,7 +967,8 @@ static void compute_pkt_fields(AVFormatContext *s, AVStream *st, int64_t offset; AVRational duration; int onein_oneout = st->codecpar->codec_id != AV_CODEC_ID_H264 && - st->codecpar->codec_id != AV_CODEC_ID_HEVC; + st->codecpar->codec_id != AV_CODEC_ID_HEVC && + st->codecpar->codec_id != AV_CODEC_ID_VVC; if (s->flags & AVFMT_FLAG_NOFILLIN) return; @@ -1186,6 +1221,11 @@ static int parse_packet(AVFormatContext *s, AVPacket *pkt, st->time_base, AV_ROUND_DOWN); } + } else if (st->codecpar->codec_id == AV_CODEC_ID_GIF) { + if (st->time_base.num > 0 && st->time_base.den > 0 && + sti->parser->duration) { + out_pkt->duration = sti->parser->duration; + } } out_pkt->stream_index = st->index; @@ -1231,6 +1271,53 @@ static int64_t ts_to_samples(AVStream *st, int64_t ts) return av_rescale(ts, st->time_base.num * st->codecpar->sample_rate, st->time_base.den); } +static int codec_close(FFStream *sti) +{ + AVCodecContext *avctx_new = NULL; + AVCodecParameters *par_tmp = NULL; + int ret; + + avctx_new = avcodec_alloc_context3(sti->avctx->codec); + if (!avctx_new) { + ret = AVERROR(ENOMEM); + goto fail; + } + + par_tmp = avcodec_parameters_alloc(); + if (!par_tmp) { + ret = AVERROR(ENOMEM); + goto fail; + } + + ret = avcodec_parameters_from_context(par_tmp, sti->avctx); + if (ret < 0) + goto fail; + + ret = avcodec_parameters_to_context(avctx_new, par_tmp); + if (ret < 0) + goto fail; + + avctx_new->pkt_timebase = sti->avctx->pkt_timebase; + +#if FF_API_TICKS_PER_FRAME +FF_DISABLE_DEPRECATION_WARNINGS + avctx_new->ticks_per_frame = sti->avctx->ticks_per_frame; +FF_ENABLE_DEPRECATION_WARNINGS +#endif + + avcodec_free_context(&sti->avctx); + sti->avctx = avctx_new; + + avctx_new = NULL; + ret = 0; + +fail: + avcodec_free_context(&avctx_new); + avcodec_parameters_free(&par_tmp); + + return ret; +} + static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) { FFFormatContext *const si = ffformatcontext(s); @@ -1267,8 +1354,10 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) if (sti->need_context_update) { if (avcodec_is_open(sti->avctx)) { av_log(s, AV_LOG_DEBUG, "Demuxer context update while decoder is open, closing and trying to re-open\n"); - avcodec_close(sti->avctx); + ret = codec_close(sti); sti->info->found_decoder = 0; + if (ret < 0) + return ret; } /* close parser, because it depends on the codec */ @@ -1283,6 +1372,8 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) return ret; } + sti->codec_desc = avcodec_descriptor_get(sti->avctx->codec_id); + sti->need_context_update = 0; } @@ -1335,13 +1426,6 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) return ret; st->codecpar->sample_rate = sti->avctx->sample_rate; st->codecpar->bit_rate = sti->avctx->bit_rate; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - st->codecpar->channels = sti->avctx->ch_layout.nb_channels; - st->codecpar->channel_layout = sti->avctx->ch_layout.order == AV_CHANNEL_ORDER_NATIVE ? - sti->avctx->ch_layout.u.mask : 0; -FF_ENABLE_DEPRECATION_WARNINGS -#endif ret = av_channel_layout_copy(&st->codecpar->ch_layout, &sti->avctx->ch_layout); if (ret < 0) return ret; @@ -1388,9 +1472,10 @@ FF_ENABLE_DEPRECATION_WARNINGS sti->skip_samples = 0; } +#if FF_API_AVSTREAM_SIDE_DATA if (sti->inject_global_side_data) { - for (int i = 0; i < st->nb_side_data; i++) { - const AVPacketSideData *const src_sd = &st->side_data[i]; + for (int i = 0; i < st->codecpar->nb_coded_side_data; i++) { + const AVPacketSideData *const src_sd = &st->codecpar->coded_side_data[i]; uint8_t *dst_data; if (av_packet_get_side_data(pkt, src_sd->type, NULL)) @@ -1406,6 +1491,7 @@ FF_ENABLE_DEPRECATION_WARNINGS } sti->inject_global_side_data = 0; } +#endif } if (!si->metafree) { @@ -2159,9 +2245,10 @@ static int get_std_framerate(int i) static int tb_unreliable(AVFormatContext *ic, AVStream *st) { FFStream *const sti = ffstream(st); + const AVCodecDescriptor *desc = sti->codec_desc; AVCodecContext *c = sti->avctx; - AVRational time_base = c->framerate.num ? av_inv_q(av_mul_q(c->framerate, - (AVRational){c->ticks_per_frame, 1})) + AVRational mul = (AVRational){ desc && (desc->props & AV_CODEC_PROP_FIELDS) ? 2 : 1, 1 }; + AVRational time_base = c->framerate.num ? av_inv_q(av_mul_q(c->framerate, mul)) /* NOHEADER check added to not break existing behavior */ : (((ic->ctx_flags & AVFMTCTX_NOHEADER) || st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) ? (AVRational){0, 1} @@ -2409,23 +2496,10 @@ static int extract_extradata(FFFormatContext *si, AVStream *st, const AVPacket * return 0; } -static int add_coded_side_data(AVStream *st, AVCodecContext *avctx) -{ - for (int i = 0; i < avctx->nb_coded_side_data; i++) { - const AVPacketSideData *const sd_src = &avctx->coded_side_data[i]; - uint8_t *dst_data; - dst_data = av_stream_new_side_data(st, sd_src->type, sd_src->size); - if (!dst_data) - return AVERROR(ENOMEM); - memcpy(dst_data, sd_src->data, sd_src->size); - } - return 0; -} - int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) { FFFormatContext *const si = ffformatcontext(ic); - int count = 0, ret = 0; + int count = 0, ret = 0, err; int64_t read_size; AVPacket *pkt1 = si->pkt; int64_t old_offset = avio_tell(ic->pb); @@ -2511,7 +2585,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) if (codec && !avctx->codec) if (avcodec_open2(avctx, codec, options ? &options[i] : &thread_opt) < 0) av_log(ic, AV_LOG_WARNING, - "Failed to open codec in %s\n",__FUNCTION__); + "Failed to open codec in %s\n", __func__); } if (!options) av_dict_free(&thread_opt); @@ -2569,7 +2643,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) extract_extradata_check(st)) break; if (sti->first_dts == AV_NOPTS_VALUE && - !(ic->iformat->flags & AVFMT_NOTIMESTAMPS) && + (!(ic->iformat->flags & AVFMT_NOTIMESTAMPS) || sti->need_parsing == AVSTREAM_PARSE_FULL_RAW) && sti->codec_info_nb_frames < ((st->disposition & AV_DISPOSITION_ATTACHED_PIC) ? 1 : ic->max_ts_probe) && (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)) @@ -2713,13 +2787,14 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) break; } if (pkt->duration > 0) { + const int fields = sti->codec_desc && (sti->codec_desc->props & AV_CODEC_PROP_FIELDS); if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE && pkt->pts != AV_NOPTS_VALUE && st->start_time != AV_NOPTS_VALUE && pkt->pts >= st->start_time && (uint64_t)pkt->pts - st->start_time < INT64_MAX ) { sti->info->codec_info_duration = FFMIN(pkt->pts - st->start_time, sti->info->codec_info_duration + pkt->duration); } else sti->info->codec_info_duration += pkt->duration; - sti->info->codec_info_duration_fields += sti->parser && sti->need_parsing && avctx->ticks_per_frame == 2 + sti->info->codec_info_duration_fields += sti->parser && sti->need_parsing && fields ? sti->parser->repeat_pict + 1 : 2; } } @@ -2767,7 +2842,7 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) av_dict_set(&opts, "codec_whitelist", ic->codec_whitelist, 0); if (avcodec_open2(avctx, codec, (options && stream_index < orig_nb_streams) ? &options[stream_index] : &opts) < 0) av_log(ic, AV_LOG_WARNING, - "Failed to open codec in %s\n",__FUNCTION__); + "Failed to open codec in %s\n", __func__); av_dict_free(&opts); } } @@ -2859,20 +2934,18 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) best_fps, 12 * 1001, INT_MAX); } if (!st->r_frame_rate.num) { - AVRational time_base = avctx->framerate.num ? av_inv_q(av_mul_q(avctx->framerate, - (AVRational){avctx->ticks_per_frame, 1})) - /* NOHEADER check added to not break existing behavior */ - : ((ic->ctx_flags & AVFMTCTX_NOHEADER) ? (AVRational){0, 1} - : st->time_base); - if ( time_base.den * (int64_t) st->time_base.num - <= time_base.num * (uint64_t)avctx->ticks_per_frame * st->time_base.den) { - av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den, - time_base.den, (int64_t)time_base.num * avctx->ticks_per_frame, INT_MAX); + const AVCodecDescriptor *desc = sti->codec_desc; + AVRational mul = (AVRational){ desc && (desc->props & AV_CODEC_PROP_FIELDS) ? 2 : 1, 1 }; + AVRational fr = av_mul_q(avctx->framerate, mul); + + if (fr.num && fr.den && av_cmp_q(st->time_base, av_inv_q(fr)) <= 0) { + st->r_frame_rate = fr; } else { st->r_frame_rate.num = st->time_base.den; st->r_frame_rate.den = st->time_base.num; } } + st->codecpar->framerate = avctx->framerate; if (sti->display_aspect_ratio.num && sti->display_aspect_ratio.den) { AVRational hw_ratio = { avctx->height, avctx->width }; st->sample_aspect_ratio = av_mul_q(sti->display_aspect_ratio, @@ -2937,9 +3010,11 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) } } - ret = compute_chapters_end(ic); - if (ret < 0) + err = compute_chapters_end(ic); + if (err < 0) { + ret = err; goto find_stream_info_err; + } /* update the stream parameters from the internal codec contexts */ for (unsigned i = 0; i < ic->nb_streams; i++) { @@ -2950,26 +3025,69 @@ int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options) ret = avcodec_parameters_from_context(st->codecpar, sti->avctx); if (ret < 0) goto find_stream_info_err; - ret = add_coded_side_data(st, sti->avctx); - if (ret < 0) - goto find_stream_info_err; + + if (sti->avctx->rc_buffer_size > 0 || sti->avctx->rc_max_rate > 0 || + sti->avctx->rc_min_rate) { + size_t cpb_size; + AVCPBProperties *props = av_cpb_properties_alloc(&cpb_size); + if (props) { + if (sti->avctx->rc_buffer_size > 0) + props->buffer_size = sti->avctx->rc_buffer_size; + if (sti->avctx->rc_min_rate > 0) + props->min_bitrate = sti->avctx->rc_min_rate; + if (sti->avctx->rc_max_rate > 0) + props->max_bitrate = sti->avctx->rc_max_rate; + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, + &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_CPB_PROPERTIES, + (uint8_t *)props, cpb_size, 0)) + av_free(props); + } + } } sti->avctx_inited = 0; +#if FF_API_AVSTREAM_SIDE_DATA +FF_DISABLE_DEPRECATION_WARNINGS + if (st->codecpar->nb_coded_side_data > 0) { + av_assert0(!st->side_data && !st->nb_side_data); + st->side_data = av_calloc(st->codecpar->nb_coded_side_data, sizeof(*st->side_data)); + if (!st->side_data) { + ret = AVERROR(ENOMEM); + goto find_stream_info_err; + } + + for (int j = 0; j < st->codecpar->nb_coded_side_data; j++) { + uint8_t *data = av_memdup(st->codecpar->coded_side_data[j].data, + st->codecpar->coded_side_data[j].size); + if (!data) { + ret = AVERROR(ENOMEM); + goto find_stream_info_err; + } + st->side_data[j].type = st->codecpar->coded_side_data[j].type; + st->side_data[j].size = st->codecpar->coded_side_data[j].size; + st->side_data[j].data = data; + st->nb_side_data++; + } + } +FF_ENABLE_DEPRECATION_WARNINGS +#endif } find_stream_info_err: for (unsigned i = 0; i < ic->nb_streams; i++) { AVStream *const st = ic->streams[i]; FFStream *const sti = ffstream(st); + int err; + if (sti->info) { av_freep(&sti->info->duration_error); av_freep(&sti->info); } - avcodec_close(sti->avctx); - // FIXME: avcodec_close() frees AVOption settable fields which includes ch_layout, - // so we need to restore it. - av_channel_layout_copy(&sti->avctx->ch_layout, &st->codecpar->ch_layout); + + err = codec_close(sti); + if (err < 0 && ret >= 0) + ret = err; av_bsf_free(&sti->extract_extradata.bsf); } if (ic->pb) { diff --git a/libavformat/demux.h b/libavformat/demux.h index 1f57e062f61..9c760956622 100644 --- a/libavformat/demux.h +++ b/libavformat/demux.h @@ -26,6 +26,115 @@ #include "libavcodec/packet.h" #include "avformat.h" +struct AVDeviceInfoList; + +/** + * For an FFInputFormat with this flag set read_close() needs to be called + * by the caller upon read_header() failure. + */ +#define FF_INFMT_FLAG_INIT_CLEANUP (1 << 0) + +typedef struct FFInputFormat { + /** + * The public AVInputFormat. See avformat.h for it. + */ + AVInputFormat p; + + /** + * Raw demuxers store their codec ID here. + */ + enum AVCodecID raw_codec_id; + + /** + * Size of private data so that it can be allocated in the wrapper. + */ + int priv_data_size; + + /** + * Internal flags. See FF_INFMT_FLAG_* above and FF_FMT_FLAG_* in internal.h. + */ + int flags_internal; + + /** + * Tell if a given file has a chance of being parsed as this format. + * The buffer provided is guaranteed to be AVPROBE_PADDING_SIZE bytes + * big so you do not have to check for that unless you need more. + */ + int (*read_probe)(const AVProbeData *); + + /** + * Read the format header and initialize the AVFormatContext + * structure. Return 0 if OK. 'avformat_new_stream' should be + * called to create new streams. + */ + int (*read_header)(struct AVFormatContext *); + + /** + * Read one packet and put it in 'pkt'. pts and flags are also + * set. 'avformat_new_stream' can be called only if the flag + * AVFMTCTX_NOHEADER is used and only in the calling thread (not in a + * background thread). + * @return 0 on success, < 0 on error. + * Upon returning an error, pkt must be unreferenced by the caller. + */ + int (*read_packet)(struct AVFormatContext *, AVPacket *pkt); + + /** + * Close the stream. The AVFormatContext and AVStreams are not + * freed by this function + */ + int (*read_close)(struct AVFormatContext *); + + /** + * Seek to a given timestamp relative to the frames in + * stream component stream_index. + * @param stream_index Must not be -1. + * @param flags Selects which direction should be preferred if no exact + * match is available. + * @return >= 0 on success (but not necessarily the new offset) + */ + int (*read_seek)(struct AVFormatContext *, + int stream_index, int64_t timestamp, int flags); + + /** + * Get the next timestamp in stream[stream_index].time_base units. + * @return the timestamp or AV_NOPTS_VALUE if an error occurred + */ + int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index, + int64_t *pos, int64_t pos_limit); + + /** + * Start/resume playing - only meaningful if using a network-based format + * (RTSP). + */ + int (*read_play)(struct AVFormatContext *); + + /** + * Pause playing - only meaningful if using a network-based format + * (RTSP). + */ + int (*read_pause)(struct AVFormatContext *); + + /** + * Seek to timestamp ts. + * Seeking will be done so that the point from which all active streams + * can be presented successfully will be closest to ts and within min/max_ts. + * Active streams are all streams that have AVStream.discard < AVDISCARD_ALL. + */ + int (*read_seek2)(struct AVFormatContext *s, int stream_index, int64_t min_ts, int64_t ts, int64_t max_ts, int flags); + + /** + * Returns device list with it properties. + * @see avdevice_list_devices() for more details. + */ + int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list); +} FFInputFormat; + +static inline const FFInputFormat *ffifmt(const AVInputFormat *fmt) +{ + return (const FFInputFormat*)fmt; +} + #define MAX_STD_TIMEBASES (30*12+30+3+6) typedef struct FFStreamInfo { int64_t last_dts; @@ -90,7 +199,7 @@ void ff_read_frame_flush(AVFormatContext *s); /** * Perform a binary search using av_index_search_timestamp() and - * AVInputFormat.read_timestamp(). + * FFInputFormat.read_timestamp(). * * @param target_ts target timestamp in the time base of the given stream * @param stream_index stream number @@ -166,10 +275,10 @@ void ff_rfps_calculate(AVFormatContext *ic); * rounded to the nearest integer and halfway cases away from zero, and can * therefore fall outside of the output interval. * - * Useful to simplify the rescaling of the arguments of AVInputFormat::read_seek2() + * Useful to simplify the rescaling of the arguments of FFInputFormat::read_seek2() * * @param[in] tb_in Timebase of the input `min_ts`, `ts` and `max_ts` - * @param[in] tb_out Timebase of the ouput `min_ts`, `ts` and `max_ts` + * @param[in] tb_out Timebase of the output `min_ts`, `ts` and `max_ts` * @param[in,out] min_ts Lower bound of the interval * @param[in,out] ts Timestamp * @param[in,out] max_ts Upper bound of the interval @@ -238,4 +347,6 @@ int ff_get_extradata(void *logctx, AVCodecParameters *par, AVIOContext *pb, int */ int ff_find_stream_index(const AVFormatContext *s, int id); +int ff_buffer_packet(AVFormatContext *s, AVPacket *pkt); + #endif /* AVFORMAT_DEMUX_H */ diff --git a/libavformat/demux_utils.c b/libavformat/demux_utils.c index 56cc6e15d8d..171a07107b4 100644 --- a/libavformat/demux_utils.c +++ b/libavformat/demux_utils.c @@ -158,18 +158,6 @@ int ff_add_param_change(AVPacket *pkt, int32_t channels, if (!pkt) return AVERROR(EINVAL); -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (channels) { - size += 4; - flags |= AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT; - } - if (channel_layout) { - size += 8; - flags |= AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT; - } -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (sample_rate) { size += 4; flags |= AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE; @@ -182,14 +170,6 @@ FF_ENABLE_DEPRECATION_WARNINGS if (!data) return AVERROR(ENOMEM); bytestream_put_le32(&data, flags); -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (channels) - bytestream_put_le32(&data, channels); - if (channel_layout) - bytestream_put_le64(&data, channel_layout); -FF_ENABLE_DEPRECATION_WARNINGS -#endif if (sample_rate) bytestream_put_le32(&data, sample_rate); if (width || height) { @@ -201,8 +181,8 @@ FF_ENABLE_DEPRECATION_WARNINGS int av_read_play(AVFormatContext *s) { - if (s->iformat->read_play) - return s->iformat->read_play(s); + if (ffifmt(s->iformat)->read_play) + return ffifmt(s->iformat)->read_play(s); if (s->pb) return avio_pause(s->pb, 0); return AVERROR(ENOSYS); @@ -210,8 +190,8 @@ int av_read_play(AVFormatContext *s) int av_read_pause(AVFormatContext *s) { - if (s->iformat->read_pause) - return s->iformat->read_pause(s); + if (ffifmt(s->iformat)->read_pause) + return ffifmt(s->iformat)->read_pause(s); if (s->pb) return avio_pause(s->pb, 1); return AVERROR(ENOSYS); diff --git a/libavformat/derf.c b/libavformat/derf.c index 9da7fc4f19e..f0077e9f06a 100644 --- a/libavformat/derf.c +++ b/libavformat/derf.c @@ -22,6 +22,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "pcm.h" @@ -67,12 +68,12 @@ static int derf_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_derf_demuxer = { - .name = "derf", - .long_name = NULL_IF_CONFIG_SMALL("Xilam DERF"), +const FFInputFormat ff_derf_demuxer = { + .p.name = "derf", + .p.long_name = NULL_IF_CONFIG_SMALL("Xilam DERF"), + .p.extensions = "adp", .read_probe = derf_probe, .read_header = derf_read_header, .read_packet = ff_pcm_read_packet, .read_seek = ff_pcm_read_seek, - .extensions = "adp", }; diff --git a/libavformat/dfa.c b/libavformat/dfa.c index 9808c9b617c..1d78c348b1a 100644 --- a/libavformat/dfa.c +++ b/libavformat/dfa.c @@ -23,6 +23,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" static int dfa_probe(const AVProbeData *p) @@ -120,11 +121,11 @@ static int dfa_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVInputFormat ff_dfa_demuxer = { - .name = "dfa", - .long_name = NULL_IF_CONFIG_SMALL("Chronomaster DFA"), +const FFInputFormat ff_dfa_demuxer = { + .p.name = "dfa", + .p.long_name = NULL_IF_CONFIG_SMALL("Chronomaster DFA"), + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = dfa_probe, .read_header = dfa_read_header, .read_packet = dfa_read_packet, - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/dfpwmdec.c b/libavformat/dfpwmdec.c index 685b95148cc..52ed33b8cfb 100644 --- a/libavformat/dfpwmdec.c +++ b/libavformat/dfpwmdec.c @@ -22,6 +22,7 @@ #include "libavutil/avstring.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "pcm.h" #include "libavutil/log.h" @@ -31,9 +32,6 @@ typedef struct DFPWMAudioDemuxerContext { AVClass *class; int sample_rate; -#if FF_API_OLD_CHANNEL_LAYOUT - int channels; -#endif AVChannelLayout ch_layout; } DFPWMAudioDemuxerContext; @@ -50,18 +48,11 @@ static int dfpwm_read_header(AVFormatContext *s) par = st->codecpar; par->codec_type = AVMEDIA_TYPE_AUDIO; - par->codec_id = s->iformat->raw_codec_id; + par->codec_id = AV_CODEC_ID_DFPWM; par->sample_rate = s1->sample_rate; -#if FF_API_OLD_CHANNEL_LAYOUT - if (s1->ch_layout.nb_channels) { -#endif ret = av_channel_layout_copy(&par->ch_layout, &s1->ch_layout); if (ret < 0) return ret; -#if FF_API_OLD_CHANNEL_LAYOUT - } else - par->ch_layout.nb_channels = s1->channels; -#endif par->bits_per_coded_sample = 1; par->block_align = 1; @@ -71,13 +62,8 @@ static int dfpwm_read_header(AVFormatContext *s) static const AVOption dfpwm_options[] = { { "sample_rate", "", offsetof(DFPWMAudioDemuxerContext, sample_rate), AV_OPT_TYPE_INT, {.i64 = 48000}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, -#if FF_API_OLD_CHANNEL_LAYOUT - { "channels", "", offsetof(DFPWMAudioDemuxerContext, channels), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_DEPRECATED }, - { "ch_layout", "", offsetof(DFPWMAudioDemuxerContext, ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, -#else { "ch_layout", "", offsetof(DFPWMAudioDemuxerContext, ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str = "mono"}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, -#endif - { NULL }, +{ NULL }, }; static const AVClass dfpwm_demuxer_class = { .class_name = "dfpwm demuxer", @@ -86,15 +72,15 @@ static const AVClass dfpwm_demuxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_dfpwm_demuxer = { - .name = "dfpwm", - .long_name = NULL_IF_CONFIG_SMALL("raw DFPWM1a"), +const FFInputFormat ff_dfpwm_demuxer = { + .p.name = "dfpwm", + .p.long_name = NULL_IF_CONFIG_SMALL("raw DFPWM1a"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "dfpwm", + .p.priv_class = &dfpwm_demuxer_class, .priv_data_size = sizeof(DFPWMAudioDemuxerContext), .read_header = dfpwm_read_header, .read_packet = ff_pcm_read_packet, .read_seek = ff_pcm_read_seek, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "dfpwm", .raw_codec_id = AV_CODEC_ID_DFPWM, - .priv_class = &dfpwm_demuxer_class, }; diff --git a/libavformat/dhav.c b/libavformat/dhav.c index 4e720f2a26c..997875eff61 100644 --- a/libavformat/dhav.c +++ b/libavformat/dhav.c @@ -20,9 +20,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include "libavutil/parseutils.h" #include "avio_internal.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct DHAVContext { @@ -460,14 +463,14 @@ static int dhav_read_seek(AVFormatContext *s, int stream_index, return 0; } -const AVInputFormat ff_dhav_demuxer = { - .name = "dhav", - .long_name = NULL_IF_CONFIG_SMALL("Video DAV"), +const FFInputFormat ff_dhav_demuxer = { + .p.name = "dhav", + .p.long_name = NULL_IF_CONFIG_SMALL("Video DAV"), + .p.extensions = "dav", + .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK | AVFMT_TS_DISCONT | AVFMT_TS_NONSTRICT | AVFMT_SEEK_TO_PTS, .priv_data_size = sizeof(DHAVContext), .read_probe = dhav_probe, .read_header = dhav_read_header, .read_packet = dhav_read_packet, .read_seek = dhav_read_seek, - .extensions = "dav", - .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK | AVFMT_TS_DISCONT | AVFMT_TS_NONSTRICT | AVFMT_SEEK_TO_PTS, }; diff --git a/libavformat/dovi_isom.c b/libavformat/dovi_isom.c index 76681b94513..ef7ab1b6d9b 100644 --- a/libavformat/dovi_isom.c +++ b/libavformat/dovi_isom.c @@ -28,12 +28,12 @@ #include "avformat.h" #include "dovi_isom.h" -int ff_isom_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const uint8_t *buf_ptr, uint64_t size) +int ff_isom_parse_dvcc_dvvc(void *logctx, AVStream *st, + const uint8_t *buf_ptr, uint64_t size) { uint32_t buf; AVDOVIDecoderConfigurationRecord *dovi; size_t dovi_size; - int ret; if (size > (1 << 30) || size < 4) return AVERROR_INVALIDDATA; @@ -63,14 +63,13 @@ int ff_isom_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const uint8_t *buf dovi->dv_bl_signal_compatibility_id = 0; } - ret = av_stream_add_side_data(st, AV_PKT_DATA_DOVI_CONF, - (uint8_t *)dovi, dovi_size); - if (ret < 0) { + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_DOVI_CONF, (uint8_t *)dovi, dovi_size, 0)) { av_free(dovi); - return ret; + return AVERROR(ENOMEM); } - av_log(s, AV_LOG_TRACE, "DOVI in dvcC/dvvC/dvwC box, version: %d.%d, profile: %d, level: %d, " + av_log(logctx, AV_LOG_TRACE, "DOVI in dvcC/dvvC/dvwC box, version: %d.%d, profile: %d, level: %d, " "rpu flag: %d, el flag: %d, bl flag: %d, compatibility id: %d\n", dovi->dv_version_major, dovi->dv_version_minor, dovi->dv_profile, dovi->dv_level, @@ -82,8 +81,8 @@ int ff_isom_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const uint8_t *buf return 0; } -void ff_isom_put_dvcc_dvvc(AVFormatContext *s, uint8_t out[ISOM_DVCC_DVVC_SIZE], - AVDOVIDecoderConfigurationRecord *dovi) +void ff_isom_put_dvcc_dvvc(void *logctx, uint8_t out[ISOM_DVCC_DVVC_SIZE], + const AVDOVIDecoderConfigurationRecord *dovi) { PutBitContext pb; @@ -106,7 +105,8 @@ void ff_isom_put_dvcc_dvvc(AVFormatContext *s, uint8_t out[ISOM_DVCC_DVVC_SIZE], flush_put_bits(&pb); - av_log(s, AV_LOG_DEBUG, "DOVI in %s box, version: %d.%d, profile: %d, level: %d, " + av_log(logctx, AV_LOG_DEBUG, + "DOVI in %s box, version: %d.%d, profile: %d, level: %d, " "rpu flag: %d, el flag: %d, bl flag: %d, compatibility id: %d\n", dovi->dv_profile > 10 ? "dvwC" : (dovi->dv_profile > 7 ? "dvvC" : "dvcC"), dovi->dv_version_major, dovi->dv_version_minor, diff --git a/libavformat/dovi_isom.h b/libavformat/dovi_isom.h index 15261643191..1221a527935 100644 --- a/libavformat/dovi_isom.h +++ b/libavformat/dovi_isom.h @@ -28,8 +28,9 @@ #define ISOM_DVCC_DVVC_SIZE 24 -int ff_isom_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const uint8_t *buf_ptr, uint64_t size); -void ff_isom_put_dvcc_dvvc(AVFormatContext *s, uint8_t out[ISOM_DVCC_DVVC_SIZE], - AVDOVIDecoderConfigurationRecord *dovi); +int ff_isom_parse_dvcc_dvvc(void *logctx, AVStream *st, + const uint8_t *buf_ptr, uint64_t size); +void ff_isom_put_dvcc_dvvc(void *logctx, uint8_t out[ISOM_DVCC_DVVC_SIZE], + const AVDOVIDecoderConfigurationRecord *dovi); #endif /* AVFORMAT_DOVI_ISOM_H */ diff --git a/libavformat/dsfdec.c b/libavformat/dsfdec.c index 3d3a82956e4..17e109e3453 100644 --- a/libavformat/dsfdec.c +++ b/libavformat/dsfdec.c @@ -22,6 +22,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "id3v2.h" @@ -209,12 +210,12 @@ static int dsf_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVInputFormat ff_dsf_demuxer = { - .name = "dsf", - .long_name = NULL_IF_CONFIG_SMALL("DSD Stream File (DSF)"), +const FFInputFormat ff_dsf_demuxer = { + .p.name = "dsf", + .p.long_name = NULL_IF_CONFIG_SMALL("DSD Stream File (DSF)"), + .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK, .priv_data_size = sizeof(DSFContext), .read_probe = dsf_probe, .read_header = dsf_read_header, .read_packet = dsf_read_packet, - .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK, }; diff --git a/libavformat/dsicin.c b/libavformat/dsicin.c index 13ee9f87bba..6eff38e010a 100644 --- a/libavformat/dsicin.c +++ b/libavformat/dsicin.c @@ -27,6 +27,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "avio_internal.h" @@ -227,9 +228,9 @@ static int cin_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVInputFormat ff_dsicin_demuxer = { - .name = "dsicin", - .long_name = NULL_IF_CONFIG_SMALL("Delphine Software International CIN"), +const FFInputFormat ff_dsicin_demuxer = { + .p.name = "dsicin", + .p.long_name = NULL_IF_CONFIG_SMALL("Delphine Software International CIN"), .priv_data_size = sizeof(CinDemuxContext), .read_probe = cin_probe, .read_header = cin_read_header, diff --git a/libavformat/dss.c b/libavformat/dss.c index d619ea00d72..510b1bd60c6 100644 --- a/libavformat/dss.c +++ b/libavformat/dss.c @@ -23,6 +23,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define DSS_HEAD_OFFSET_AUTHOR 0xc @@ -353,13 +354,13 @@ static int dss_read_seek(AVFormatContext *s, int stream_index, } -const AVInputFormat ff_dss_demuxer = { - .name = "dss", - .long_name = NULL_IF_CONFIG_SMALL("Digital Speech Standard (DSS)"), +const FFInputFormat ff_dss_demuxer = { + .p.name = "dss", + .p.long_name = NULL_IF_CONFIG_SMALL("Digital Speech Standard (DSS)"), + .p.extensions = "dss", .priv_data_size = sizeof(DSSDemuxContext), .read_probe = dss_probe, .read_header = dss_read_header, .read_packet = dss_read_packet, .read_seek = dss_read_seek, - .extensions = "dss" }; diff --git a/libavformat/dtsdec.c b/libavformat/dtsdec.c index ceedb2eb49b..38ba3e73d46 100644 --- a/libavformat/dtsdec.c +++ b/libavformat/dtsdec.c @@ -27,6 +27,7 @@ #include "libavcodec/get_bits.h" #include "avformat.h" +#include "demux.h" #include "rawdec.h" static int dts_probe(const AVProbeData *p) @@ -132,15 +133,15 @@ static int dts_probe(const AVProbeData *p) return 0; } -const AVInputFormat ff_dts_demuxer = { - .name = "dts", - .long_name = NULL_IF_CONFIG_SMALL("raw DTS"), +const FFInputFormat ff_dts_demuxer = { + .p.name = "dts", + .p.long_name = NULL_IF_CONFIG_SMALL("raw DTS"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "dts", + .p.priv_class = &ff_raw_demuxer_class, .read_probe = dts_probe, .read_header = ff_raw_audio_read_header, .read_packet = ff_raw_read_partial_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "dts", .raw_codec_id = AV_CODEC_ID_DTS, .priv_data_size = sizeof(FFRawDemuxerContext), - .priv_class = &ff_raw_demuxer_class, }; diff --git a/libavformat/dtshddec.c b/libavformat/dtshddec.c index a3dea0668fe..9939724ac70 100644 --- a/libavformat/dtshddec.c +++ b/libavformat/dtshddec.c @@ -23,6 +23,7 @@ #include "libavutil/dict.h" #include "libavcodec/dca.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define AUPR_HDR 0x415550522D484452 @@ -162,14 +163,14 @@ static int raw_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_dtshd_demuxer = { - .name = "dtshd", - .long_name = NULL_IF_CONFIG_SMALL("raw DTS-HD"), +const FFInputFormat ff_dtshd_demuxer = { + .p.name = "dtshd", + .p.long_name = NULL_IF_CONFIG_SMALL("raw DTS-HD"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "dtshd", .priv_data_size = sizeof(DTSHDDemuxContext), .read_probe = dtshd_probe, .read_header = dtshd_read_header, .read_packet = raw_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "dtshd", .raw_codec_id = AV_CODEC_ID_DTS, }; diff --git a/libavformat/dump.c b/libavformat/dump.c index d31e4c2ec68..ca47d733f70 100644 --- a/libavformat/dump.c +++ b/libavformat/dump.c @@ -22,11 +22,14 @@ #include #include +#include "libavutil/avstring.h" #include "libavutil/channel_layout.h" #include "libavutil/display.h" +#include "libavutil/iamf.h" #include "libavutil/intreadwrite.h" #include "libavutil/log.h" #include "libavutil/mastering_display_metadata.h" +#include "libavutil/ambient_viewing_environment.h" #include "libavutil/dovi_meta.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" @@ -121,53 +124,59 @@ void av_pkt_dump_log2(void *avcl, int level, const AVPacket *pkt, int dump_paylo } -static void print_fps(double d, const char *postfix) +static void print_fps(double d, const char *postfix, int log_level) { uint64_t v = lrintf(d * 100); if (!v) - av_log(NULL, AV_LOG_INFO, "%1.4f %s", d, postfix); + av_log(NULL, log_level, "%1.4f %s", d, postfix); else if (v % 100) - av_log(NULL, AV_LOG_INFO, "%3.2f %s", d, postfix); + av_log(NULL, log_level, "%3.2f %s", d, postfix); else if (v % (100 * 1000)) - av_log(NULL, AV_LOG_INFO, "%1.0f %s", d, postfix); + av_log(NULL, log_level, "%1.0f %s", d, postfix); else - av_log(NULL, AV_LOG_INFO, "%1.0fk %s", d / 1000, postfix); + av_log(NULL, log_level, "%1.0fk %s", d / 1000, postfix); } -static void dump_metadata(void *ctx, const AVDictionary *m, const char *indent) +static void dump_dictionary(void *ctx, const AVDictionary *m, + const char *name, const char *indent, + int log_level) { - if (m && !(av_dict_count(m) == 1 && av_dict_get(m, "language", NULL, 0))) { - const AVDictionaryEntry *tag = NULL; - - av_log(ctx, AV_LOG_INFO, "%sMetadata:\n", indent); - while ((tag = av_dict_iterate(m, tag))) - if (strcmp("language", tag->key)) { - const char *p = tag->value; - av_log(ctx, AV_LOG_INFO, - "%s %-16s: ", indent, tag->key); - while (*p) { - size_t len = strcspn(p, "\x8\xa\xb\xc\xd"); - av_log(ctx, AV_LOG_INFO, "%.*s", (int)(FFMIN(255, len)), p); - p += len; - if (*p == 0xd) av_log(ctx, AV_LOG_INFO, " "); - if (*p == 0xa) av_log(ctx, AV_LOG_INFO, "\n%s %-16s: ", indent, ""); - if (*p) p++; - } - av_log(ctx, AV_LOG_INFO, "\n"); + const AVDictionaryEntry *tag = NULL; + + if (!m) + return; + + av_log(ctx, log_level, "%s%s:\n", indent, name); + while ((tag = av_dict_iterate(m, tag))) + if (strcmp("language", tag->key)) { + const char *p = tag->value; + av_log(ctx, log_level, + "%s %-16s: ", indent, tag->key); + while (*p) { + size_t len = strcspn(p, "\x8\xa\xb\xc\xd"); + av_log(ctx, log_level, "%.*s", (int)(FFMIN(255, len)), p); + p += len; + if (*p == 0xd) av_log(ctx, log_level, " "); + if (*p == 0xa) av_log(ctx, log_level, "\n%s %-16s: ", indent, ""); + if (*p) p++; } - } + av_log(ctx, log_level, "\n"); + } +} + +static void dump_metadata(void *ctx, const AVDictionary *m, const char *indent, + int log_level) +{ + if (m && !(av_dict_count(m) == 1 && av_dict_get(m, "language", NULL, 0))) + dump_dictionary(ctx, m, "Metadata", indent, log_level); } /* param change side data*/ -static void dump_paramchange(void *ctx, const AVPacketSideData *sd) +static void dump_paramchange(void *ctx, const AVPacketSideData *sd, int log_level) { int size = sd->size; const uint8_t *data = sd->data; uint32_t flags, sample_rate, width, height; -#if FF_API_OLD_CHANNEL_LAYOUT - uint32_t channels; - uint64_t layout; -#endif if (!data || sd->size < 4) goto fail; @@ -176,34 +185,13 @@ static void dump_paramchange(void *ctx, const AVPacketSideData *sd) data += 4; size -= 4; -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT) { - if (size < 4) - goto fail; - channels = AV_RL32(data); - data += 4; - size -= 4; - av_log(ctx, AV_LOG_INFO, "channel count %"PRIu32", ", channels); - } - if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT) { - if (size < 8) - goto fail; - layout = AV_RL64(data); - data += 8; - size -= 8; - av_log(ctx, AV_LOG_INFO, - "channel layout: %s, ", av_get_channel_name(layout)); - } -FF_ENABLE_DEPRECATION_WARNINGS -#endif /* FF_API_OLD_CHANNEL_LAYOUT */ if (flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE) { if (size < 4) goto fail; sample_rate = AV_RL32(data); data += 4; size -= 4; - av_log(ctx, AV_LOG_INFO, "sample_rate %"PRIu32", ", sample_rate); + av_log(ctx, log_level, "sample_rate %"PRIu32", ", sample_rate); } if (flags & AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS) { if (size < 8) @@ -214,7 +202,7 @@ FF_ENABLE_DEPRECATION_WARNINGS height = AV_RL32(data); data += 4; size -= 4; - av_log(ctx, AV_LOG_INFO, "width %"PRIu32" height %"PRIu32, width, height); + av_log(ctx, log_level, "width %"PRIu32" height %"PRIu32, width, height); } return; @@ -223,27 +211,27 @@ FF_ENABLE_DEPRECATION_WARNINGS } /* replaygain side data*/ -static void print_gain(void *ctx, const char *str, int32_t gain) +static void print_gain(void *ctx, const char *str, int32_t gain, int log_level) { - av_log(ctx, AV_LOG_INFO, "%s - ", str); + av_log(ctx, log_level, "%s - ", str); if (gain == INT32_MIN) - av_log(ctx, AV_LOG_INFO, "unknown"); + av_log(ctx, log_level, "unknown"); else - av_log(ctx, AV_LOG_INFO, "%f", gain / 100000.0f); - av_log(ctx, AV_LOG_INFO, ", "); + av_log(ctx, log_level, "%f", gain / 100000.0f); + av_log(ctx, log_level, ", "); } -static void print_peak(void *ctx, const char *str, uint32_t peak) +static void print_peak(void *ctx, const char *str, uint32_t peak, int log_level) { - av_log(ctx, AV_LOG_INFO, "%s - ", str); + av_log(ctx, log_level, "%s - ", str); if (!peak) - av_log(ctx, AV_LOG_INFO, "unknown"); + av_log(ctx, log_level, "unknown"); else - av_log(ctx, AV_LOG_INFO, "%f", (float) peak / UINT32_MAX); - av_log(ctx, AV_LOG_INFO, ", "); + av_log(ctx, log_level, "%f", (float) peak / UINT32_MAX); + av_log(ctx, log_level, ", "); } -static void dump_replaygain(void *ctx, const AVPacketSideData *sd) +static void dump_replaygain(void *ctx, const AVPacketSideData *sd, int log_level) { const AVReplayGain *rg; @@ -253,13 +241,13 @@ static void dump_replaygain(void *ctx, const AVPacketSideData *sd) } rg = (const AVReplayGain *)sd->data; - print_gain(ctx, "track gain", rg->track_gain); - print_peak(ctx, "track peak", rg->track_peak); - print_gain(ctx, "album gain", rg->album_gain); - print_peak(ctx, "album peak", rg->album_peak); + print_gain(ctx, "track gain", rg->track_gain, log_level); + print_peak(ctx, "track peak", rg->track_peak, log_level); + print_gain(ctx, "album gain", rg->album_gain, log_level); + print_peak(ctx, "album peak", rg->album_peak, log_level); } -static void dump_stereo3d(void *ctx, const AVPacketSideData *sd) +static void dump_stereo3d(void *ctx, const AVPacketSideData *sd, int log_level) { const AVStereo3D *stereo; @@ -270,13 +258,13 @@ static void dump_stereo3d(void *ctx, const AVPacketSideData *sd) stereo = (const AVStereo3D *)sd->data; - av_log(ctx, AV_LOG_INFO, "%s", av_stereo3d_type_name(stereo->type)); + av_log(ctx, log_level, "%s", av_stereo3d_type_name(stereo->type)); if (stereo->flags & AV_STEREO3D_FLAG_INVERT) - av_log(ctx, AV_LOG_INFO, " (inverted)"); + av_log(ctx, log_level, " (inverted)"); } -static void dump_audioservicetype(void *ctx, const AVPacketSideData *sd) +static void dump_audioservicetype(void *ctx, const AVPacketSideData *sd, int log_level) { const enum AVAudioServiceType *ast = (const enum AVAudioServiceType *)sd->data; @@ -287,31 +275,31 @@ static void dump_audioservicetype(void *ctx, const AVPacketSideData *sd) switch (*ast) { case AV_AUDIO_SERVICE_TYPE_MAIN: - av_log(ctx, AV_LOG_INFO, "main"); + av_log(ctx, log_level, "main"); break; case AV_AUDIO_SERVICE_TYPE_EFFECTS: - av_log(ctx, AV_LOG_INFO, "effects"); + av_log(ctx, log_level, "effects"); break; case AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED: - av_log(ctx, AV_LOG_INFO, "visually impaired"); + av_log(ctx, log_level, "visually impaired"); break; case AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED: - av_log(ctx, AV_LOG_INFO, "hearing impaired"); + av_log(ctx, log_level, "hearing impaired"); break; case AV_AUDIO_SERVICE_TYPE_DIALOGUE: - av_log(ctx, AV_LOG_INFO, "dialogue"); + av_log(ctx, log_level, "dialogue"); break; case AV_AUDIO_SERVICE_TYPE_COMMENTARY: - av_log(ctx, AV_LOG_INFO, "commentary"); + av_log(ctx, log_level, "commentary"); break; case AV_AUDIO_SERVICE_TYPE_EMERGENCY: - av_log(ctx, AV_LOG_INFO, "emergency"); + av_log(ctx, log_level, "emergency"); break; case AV_AUDIO_SERVICE_TYPE_VOICE_OVER: - av_log(ctx, AV_LOG_INFO, "voice over"); + av_log(ctx, log_level, "voice over"); break; case AV_AUDIO_SERVICE_TYPE_KARAOKE: - av_log(ctx, AV_LOG_INFO, "karaoke"); + av_log(ctx, log_level, "karaoke"); break; default: av_log(ctx, AV_LOG_WARNING, "unknown"); @@ -319,7 +307,7 @@ static void dump_audioservicetype(void *ctx, const AVPacketSideData *sd) } } -static void dump_cpb(void *ctx, const AVPacketSideData *sd) +static void dump_cpb(void *ctx, const AVPacketSideData *sd, int log_level) { const AVCPBProperties *cpb = (const AVCPBProperties *)sd->data; @@ -328,21 +316,22 @@ static void dump_cpb(void *ctx, const AVPacketSideData *sd) return; } - av_log(ctx, AV_LOG_INFO, + av_log(ctx, log_level, "bitrate max/min/avg: %"PRId64"/%"PRId64"/%"PRId64" buffer size: %"PRId64" ", cpb->max_bitrate, cpb->min_bitrate, cpb->avg_bitrate, cpb->buffer_size); if (cpb->vbv_delay == UINT64_MAX) - av_log(ctx, AV_LOG_INFO, "vbv_delay: N/A"); + av_log(ctx, log_level, "vbv_delay: N/A"); else - av_log(ctx, AV_LOG_INFO, "vbv_delay: %"PRIu64"", cpb->vbv_delay); + av_log(ctx, log_level, "vbv_delay: %"PRIu64"", cpb->vbv_delay); } -static void dump_mastering_display_metadata(void *ctx, const AVPacketSideData *sd) +static void dump_mastering_display_metadata(void *ctx, const AVPacketSideData *sd, + int log_level) { const AVMasteringDisplayMetadata *metadata = (const AVMasteringDisplayMetadata *)sd->data; - av_log(ctx, AV_LOG_INFO, "Mastering Display Metadata, " + av_log(ctx, log_level, "Mastering Display Metadata, " "has_primaries:%d has_luminance:%d " "r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f) " "min_luminance=%f, max_luminance=%f", @@ -357,17 +346,29 @@ static void dump_mastering_display_metadata(void *ctx, const AVPacketSideData *s av_q2d(metadata->min_luminance), av_q2d(metadata->max_luminance)); } -static void dump_content_light_metadata(void *ctx, const AVPacketSideData *sd) +static void dump_content_light_metadata(void *ctx, const AVPacketSideData *sd, + int log_level) { const AVContentLightMetadata *metadata = (const AVContentLightMetadata *)sd->data; - av_log(ctx, AV_LOG_INFO, "Content Light Level Metadata, " + av_log(ctx, log_level, "Content Light Level Metadata, " "MaxCLL=%d, MaxFALL=%d", metadata->MaxCLL, metadata->MaxFALL); } +static void dump_ambient_viewing_environment_metadata(void *ctx, const AVPacketSideData *sd) +{ + const AVAmbientViewingEnvironment *ambient = + (const AVAmbientViewingEnvironment *)sd->data; + av_log(ctx, AV_LOG_INFO, "Ambient Viewing Environment, " + "ambient_illuminance=%f, ambient_light_x=%f, ambient_light_y=%f", + av_q2d(ambient->ambient_illuminance), + av_q2d(ambient->ambient_light_x), + av_q2d(ambient->ambient_light_y)); +} + static void dump_spherical(void *ctx, const AVCodecParameters *par, - const AVPacketSideData *sd) + const AVPacketSideData *sd, int log_level) { const AVSphericalMapping *spherical = (const AVSphericalMapping *)sd->data; double yaw, pitch, roll; @@ -377,31 +378,32 @@ static void dump_spherical(void *ctx, const AVCodecParameters *par, return; } - av_log(ctx, AV_LOG_INFO, "%s ", av_spherical_projection_name(spherical->projection)); + av_log(ctx, log_level, "%s ", av_spherical_projection_name(spherical->projection)); yaw = ((double)spherical->yaw) / (1 << 16); pitch = ((double)spherical->pitch) / (1 << 16); roll = ((double)spherical->roll) / (1 << 16); - av_log(ctx, AV_LOG_INFO, "(%f/%f/%f) ", yaw, pitch, roll); + av_log(ctx, log_level, "(%f/%f/%f) ", yaw, pitch, roll); if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) { size_t l, t, r, b; av_spherical_tile_bounds(spherical, par->width, par->height, &l, &t, &r, &b); - av_log(ctx, AV_LOG_INFO, + av_log(ctx, log_level, "[%"SIZE_SPECIFIER", %"SIZE_SPECIFIER", %"SIZE_SPECIFIER", %"SIZE_SPECIFIER"] ", l, t, r, b); } else if (spherical->projection == AV_SPHERICAL_CUBEMAP) { - av_log(ctx, AV_LOG_INFO, "[pad %"PRIu32"] ", spherical->padding); + av_log(ctx, log_level, "[pad %"PRIu32"] ", spherical->padding); } } -static void dump_dovi_conf(void *ctx, const AVPacketSideData *sd) +static void dump_dovi_conf(void *ctx, const AVPacketSideData *sd, + int log_level) { const AVDOVIDecoderConfigurationRecord *dovi = (const AVDOVIDecoderConfigurationRecord *)sd->data; - av_log(ctx, AV_LOG_INFO, "version: %d.%d, profile: %d, level: %d, " + av_log(ctx, log_level, "version: %d.%d, profile: %d, level: %d, " "rpu flag: %d, el flag: %d, bl flag: %d, compatibility id: %d", dovi->dv_version_major, dovi->dv_version_minor, dovi->dv_profile, dovi->dv_level, @@ -411,7 +413,8 @@ static void dump_dovi_conf(void *ctx, const AVPacketSideData *sd) dovi->dv_bl_signal_compatibility_id); } -static void dump_s12m_timecode(void *ctx, const AVStream *st, const AVPacketSideData *sd) +static void dump_s12m_timecode(void *ctx, const AVStream *st, const AVPacketSideData *sd, + int log_level) { const uint32_t *tc = (const uint32_t *)sd->data; @@ -423,93 +426,138 @@ static void dump_s12m_timecode(void *ctx, const AVStream *st, const AVPacketSide for (int j = 1; j <= tc[0]; j++) { char tcbuf[AV_TIMECODE_STR_SIZE]; av_timecode_make_smpte_tc_string2(tcbuf, st->avg_frame_rate, tc[j], 0, 0); - av_log(ctx, AV_LOG_INFO, "timecode - %s%s", tcbuf, j != tc[0] ? ", " : ""); + av_log(ctx, log_level, "timecode - %s%s", tcbuf, j != tc[0] ? ", " : ""); } } -static void dump_sidedata(void *ctx, const AVStream *st, const char *indent) +static void dump_sidedata(void *ctx, const AVStream *st, const char *indent, + int log_level) { int i; - if (st->nb_side_data) - av_log(ctx, AV_LOG_INFO, "%sSide data:\n", indent); + if (st->codecpar->nb_coded_side_data) + av_log(ctx, log_level, "%sSide data:\n", indent); - for (i = 0; i < st->nb_side_data; i++) { - const AVPacketSideData *sd = &st->side_data[i]; - av_log(ctx, AV_LOG_INFO, "%s ", indent); + for (i = 0; i < st->codecpar->nb_coded_side_data; i++) { + const AVPacketSideData *sd = &st->codecpar->coded_side_data[i]; + av_log(ctx, log_level, "%s ", indent); switch (sd->type) { case AV_PKT_DATA_PALETTE: - av_log(ctx, AV_LOG_INFO, "palette"); + av_log(ctx, log_level, "palette"); break; case AV_PKT_DATA_NEW_EXTRADATA: - av_log(ctx, AV_LOG_INFO, "new extradata"); + av_log(ctx, log_level, "new extradata"); break; case AV_PKT_DATA_PARAM_CHANGE: - av_log(ctx, AV_LOG_INFO, "paramchange: "); - dump_paramchange(ctx, sd); + av_log(ctx, log_level, "paramchange: "); + dump_paramchange(ctx, sd, log_level); break; case AV_PKT_DATA_H263_MB_INFO: - av_log(ctx, AV_LOG_INFO, "H.263 macroblock info"); + av_log(ctx, log_level, "H.263 macroblock info"); break; case AV_PKT_DATA_REPLAYGAIN: - av_log(ctx, AV_LOG_INFO, "replaygain: "); - dump_replaygain(ctx, sd); + av_log(ctx, log_level, "replaygain: "); + dump_replaygain(ctx, sd, log_level); break; case AV_PKT_DATA_DISPLAYMATRIX: - av_log(ctx, AV_LOG_INFO, "displaymatrix: rotation of %.2f degrees", + av_log(ctx, log_level, "displaymatrix: rotation of %.2f degrees", av_display_rotation_get((const int32_t *)sd->data)); break; case AV_PKT_DATA_STEREO3D: - av_log(ctx, AV_LOG_INFO, "stereo3d: "); - dump_stereo3d(ctx, sd); + av_log(ctx, log_level, "stereo3d: "); + dump_stereo3d(ctx, sd, log_level); break; case AV_PKT_DATA_AUDIO_SERVICE_TYPE: - av_log(ctx, AV_LOG_INFO, "audio service type: "); - dump_audioservicetype(ctx, sd); + av_log(ctx, log_level, "audio service type: "); + dump_audioservicetype(ctx, sd, log_level); break; case AV_PKT_DATA_QUALITY_STATS: - av_log(ctx, AV_LOG_INFO, "quality factor: %"PRId32", pict_type: %c", + av_log(ctx, log_level, "quality factor: %"PRId32", pict_type: %c", AV_RL32(sd->data), av_get_picture_type_char(sd->data[4])); break; case AV_PKT_DATA_CPB_PROPERTIES: - av_log(ctx, AV_LOG_INFO, "cpb: "); - dump_cpb(ctx, sd); + av_log(ctx, log_level, "cpb: "); + dump_cpb(ctx, sd, log_level); break; case AV_PKT_DATA_MASTERING_DISPLAY_METADATA: - dump_mastering_display_metadata(ctx, sd); + dump_mastering_display_metadata(ctx, sd, log_level); break; case AV_PKT_DATA_SPHERICAL: - av_log(ctx, AV_LOG_INFO, "spherical: "); - dump_spherical(ctx, st->codecpar, sd); + av_log(ctx, log_level, "spherical: "); + dump_spherical(ctx, st->codecpar, sd, log_level); break; case AV_PKT_DATA_CONTENT_LIGHT_LEVEL: - dump_content_light_metadata(ctx, sd); + dump_content_light_metadata(ctx, sd, log_level); break; case AV_PKT_DATA_ICC_PROFILE: - av_log(ctx, AV_LOG_INFO, "ICC Profile"); + av_log(ctx, log_level, "ICC Profile"); break; case AV_PKT_DATA_DOVI_CONF: - av_log(ctx, AV_LOG_INFO, "DOVI configuration record: "); - dump_dovi_conf(ctx, sd); + av_log(ctx, log_level, "DOVI configuration record: "); + dump_dovi_conf(ctx, sd, log_level); break; case AV_PKT_DATA_S12M_TIMECODE: - av_log(ctx, AV_LOG_INFO, "SMPTE ST 12-1:2014: "); - dump_s12m_timecode(ctx, st, sd); + av_log(ctx, log_level, "SMPTE ST 12-1:2014: "); + dump_s12m_timecode(ctx, st, sd, log_level); + break; + case AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT: + dump_ambient_viewing_environment_metadata(ctx, sd); break; default: - av_log(ctx, AV_LOG_INFO, "unknown side data type %d " + av_log(ctx, log_level, "unknown side data type %d " "(%"SIZE_SPECIFIER" bytes)", sd->type, sd->size); break; } - av_log(ctx, AV_LOG_INFO, "\n"); + av_log(ctx, log_level, "\n"); } } +static void dump_disposition(int disposition, int log_level) +{ + if (disposition & AV_DISPOSITION_DEFAULT) + av_log(NULL, log_level, " (default)"); + if (disposition & AV_DISPOSITION_DUB) + av_log(NULL, log_level, " (dub)"); + if (disposition & AV_DISPOSITION_ORIGINAL) + av_log(NULL, log_level, " (original)"); + if (disposition & AV_DISPOSITION_COMMENT) + av_log(NULL, log_level, " (comment)"); + if (disposition & AV_DISPOSITION_LYRICS) + av_log(NULL, log_level, " (lyrics)"); + if (disposition & AV_DISPOSITION_KARAOKE) + av_log(NULL, log_level, " (karaoke)"); + if (disposition & AV_DISPOSITION_FORCED) + av_log(NULL, log_level, " (forced)"); + if (disposition & AV_DISPOSITION_HEARING_IMPAIRED) + av_log(NULL, log_level, " (hearing impaired)"); + if (disposition & AV_DISPOSITION_VISUAL_IMPAIRED) + av_log(NULL, log_level, " (visual impaired)"); + if (disposition & AV_DISPOSITION_CLEAN_EFFECTS) + av_log(NULL, log_level, " (clean effects)"); + if (disposition & AV_DISPOSITION_ATTACHED_PIC) + av_log(NULL, log_level, " (attached pic)"); + if (disposition & AV_DISPOSITION_TIMED_THUMBNAILS) + av_log(NULL, log_level, " (timed thumbnails)"); + if (disposition & AV_DISPOSITION_CAPTIONS) + av_log(NULL, log_level, " (captions)"); + if (disposition & AV_DISPOSITION_DESCRIPTIONS) + av_log(NULL, log_level, " (descriptions)"); + if (disposition & AV_DISPOSITION_METADATA) + av_log(NULL, log_level, " (metadata)"); + if (disposition & AV_DISPOSITION_DEPENDENT) + av_log(NULL, log_level, " (dependent)"); + if (disposition & AV_DISPOSITION_STILL_IMAGE) + av_log(NULL, log_level, " (still image)"); + if (disposition & AV_DISPOSITION_NON_DIEGETIC) + av_log(NULL, log_level, " (non-diegetic)"); +} + /* "user interface" functions */ static void dump_stream_format(const AVFormatContext *ic, int i, - int index, int is_output) + int group_index, int index, int is_output, + int log_level) { char buf[256]; int flags = (is_output ? ic->oformat->flags : ic->iformat->flags); @@ -517,6 +565,8 @@ static void dump_stream_format(const AVFormatContext *ic, int i, const FFStream *const sti = cffstream(st); const AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0); const char *separator = ic->dump_separator; + const char *group_indent = group_index >= 0 ? " " : ""; + const char *extra_indent = group_index >= 0 ? " " : " "; AVCodecContext *avctx; int ret; @@ -531,29 +581,32 @@ static void dump_stream_format(const AVFormatContext *ic, int i, } // Fields which are missing from AVCodecParameters need to be taken from the AVCodecContext - avctx->properties = sti->avctx->properties; - avctx->codec = sti->avctx->codec; - avctx->qmin = sti->avctx->qmin; - avctx->qmax = sti->avctx->qmax; - avctx->coded_width = sti->avctx->coded_width; - avctx->coded_height = sti->avctx->coded_height; + if (sti->avctx) { + avctx->properties = sti->avctx->properties; + avctx->codec = sti->avctx->codec; + avctx->qmin = sti->avctx->qmin; + avctx->qmax = sti->avctx->qmax; + avctx->coded_width = sti->avctx->coded_width; + avctx->coded_height = sti->avctx->coded_height; + } if (separator) av_opt_set(avctx, "dump_separator", separator, 0); avcodec_string(buf, sizeof(buf), avctx, is_output); avcodec_free_context(&avctx); - av_log(NULL, AV_LOG_INFO, " Stream #%d:%d", index, i); + av_log(NULL, log_level, "%s Stream #%d", group_indent, index); + av_log(NULL, log_level, ":%d", i); /* the pid is an important information, so we display it */ /* XXX: add a generic system */ if (flags & AVFMT_SHOW_IDS) - av_log(NULL, AV_LOG_INFO, "[0x%x]", st->id); + av_log(NULL, log_level, "[0x%x]", st->id); if (lang) - av_log(NULL, AV_LOG_INFO, "(%s)", lang->value); + av_log(NULL, log_level, "(%s)", lang->value); av_log(NULL, AV_LOG_DEBUG, ", %d, %d/%d", sti->codec_info_nb_frames, st->time_base.num, st->time_base.den); - av_log(NULL, AV_LOG_INFO, ": %s", buf); + av_log(NULL, log_level, ": %s", buf); if (st->sample_aspect_ratio.num && av_cmp_q(st->sample_aspect_ratio, st->codecpar->sample_aspect_ratio)) { @@ -562,7 +615,7 @@ static void dump_stream_format(const AVFormatContext *ic, int i, st->codecpar->width * (int64_t)st->sample_aspect_ratio.num, st->codecpar->height * (int64_t)st->sample_aspect_ratio.den, 1024 * 1024); - av_log(NULL, AV_LOG_INFO, ", SAR %d:%d DAR %d:%d", + av_log(NULL, log_level, ", SAR %d:%d DAR %d:%d", st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, display_aspect_ratio.num, display_aspect_ratio.den); } @@ -573,57 +626,135 @@ static void dump_stream_format(const AVFormatContext *ic, int i, int tbn = st->time_base.den && st->time_base.num; if (fps || tbr || tbn) - av_log(NULL, AV_LOG_INFO, "%s", separator); + av_log(NULL, log_level, "%s", separator); if (fps) - print_fps(av_q2d(st->avg_frame_rate), tbr || tbn ? "fps, " : "fps"); + print_fps(av_q2d(st->avg_frame_rate), tbr || tbn ? "fps, " : "fps", log_level); if (tbr) - print_fps(av_q2d(st->r_frame_rate), tbn ? "tbr, " : "tbr"); + print_fps(av_q2d(st->r_frame_rate), tbn ? "tbr, " : "tbr", log_level); if (tbn) - print_fps(1 / av_q2d(st->time_base), "tbn"); + print_fps(1 / av_q2d(st->time_base), "tbn", log_level); } - if (st->disposition & AV_DISPOSITION_DEFAULT) - av_log(NULL, AV_LOG_INFO, " (default)"); - if (st->disposition & AV_DISPOSITION_DUB) - av_log(NULL, AV_LOG_INFO, " (dub)"); - if (st->disposition & AV_DISPOSITION_ORIGINAL) - av_log(NULL, AV_LOG_INFO, " (original)"); - if (st->disposition & AV_DISPOSITION_COMMENT) - av_log(NULL, AV_LOG_INFO, " (comment)"); - if (st->disposition & AV_DISPOSITION_LYRICS) - av_log(NULL, AV_LOG_INFO, " (lyrics)"); - if (st->disposition & AV_DISPOSITION_KARAOKE) - av_log(NULL, AV_LOG_INFO, " (karaoke)"); - if (st->disposition & AV_DISPOSITION_FORCED) - av_log(NULL, AV_LOG_INFO, " (forced)"); - if (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED) - av_log(NULL, AV_LOG_INFO, " (hearing impaired)"); - if (st->disposition & AV_DISPOSITION_VISUAL_IMPAIRED) - av_log(NULL, AV_LOG_INFO, " (visual impaired)"); - if (st->disposition & AV_DISPOSITION_CLEAN_EFFECTS) - av_log(NULL, AV_LOG_INFO, " (clean effects)"); - if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) - av_log(NULL, AV_LOG_INFO, " (attached pic)"); - if (st->disposition & AV_DISPOSITION_TIMED_THUMBNAILS) - av_log(NULL, AV_LOG_INFO, " (timed thumbnails)"); - if (st->disposition & AV_DISPOSITION_CAPTIONS) - av_log(NULL, AV_LOG_INFO, " (captions)"); - if (st->disposition & AV_DISPOSITION_DESCRIPTIONS) - av_log(NULL, AV_LOG_INFO, " (descriptions)"); - if (st->disposition & AV_DISPOSITION_METADATA) - av_log(NULL, AV_LOG_INFO, " (metadata)"); - if (st->disposition & AV_DISPOSITION_DEPENDENT) - av_log(NULL, AV_LOG_INFO, " (dependent)"); - if (st->disposition & AV_DISPOSITION_STILL_IMAGE) - av_log(NULL, AV_LOG_INFO, " (still image)"); - if (st->disposition & AV_DISPOSITION_NON_DIEGETIC) - av_log(NULL, AV_LOG_INFO, " (non-diegetic)"); - av_log(NULL, AV_LOG_INFO, "\n"); - - dump_metadata(NULL, st->metadata, " "); - - dump_sidedata(NULL, st, " "); + dump_disposition(st->disposition, log_level); + av_log(NULL, log_level, "\n"); + + dump_metadata(NULL, st->metadata, extra_indent, log_level); + + dump_sidedata(NULL, st, extra_indent, log_level); +} + +static void dump_stream_group(const AVFormatContext *ic, uint8_t *printed, + int i, int index, int is_output) +{ + const AVStreamGroup *stg = ic->stream_groups[i]; + int flags = (is_output ? ic->oformat->flags : ic->iformat->flags); + char buf[512]; + int ret; + + av_log(NULL, AV_LOG_INFO, " Stream group #%d:%d", index, i); + if (flags & AVFMT_SHOW_IDS) + av_log(NULL, AV_LOG_INFO, "[0x%"PRIx64"]", stg->id); + av_log(NULL, AV_LOG_INFO, ":"); + + switch (stg->type) { + case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT: { + const AVIAMFAudioElement *audio_element = stg->params.iamf_audio_element; + av_log(NULL, AV_LOG_INFO, " IAMF Audio Element:"); + dump_disposition(stg->disposition, AV_LOG_INFO); + av_log(NULL, AV_LOG_INFO, "\n"); + dump_metadata(NULL, stg->metadata, " ", AV_LOG_INFO); + for (int j = 0; j < audio_element->nb_layers; j++) { + const AVIAMFLayer *layer = audio_element->layers[j]; + int channel_count = layer->ch_layout.nb_channels; + av_log(NULL, AV_LOG_INFO, " Layer %d:", j); + ret = av_channel_layout_describe(&layer->ch_layout, buf, sizeof(buf)); + if (ret >= 0) + av_log(NULL, AV_LOG_INFO, " %s", buf); + av_log(NULL, AV_LOG_INFO, "\n"); + for (int k = 0; channel_count > 0 && k < stg->nb_streams; k++) { + AVStream *st = stg->streams[k]; + dump_stream_format(ic, st->index, i, index, is_output, AV_LOG_VERBOSE); + printed[st->index] = 1; + channel_count -= st->codecpar->ch_layout.nb_channels; + } + } + break; + } + case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: { + const AVIAMFMixPresentation *mix_presentation = stg->params.iamf_mix_presentation; + av_log(NULL, AV_LOG_INFO, " IAMF Mix Presentation:"); + dump_disposition(stg->disposition, AV_LOG_INFO); + av_log(NULL, AV_LOG_INFO, "\n"); + dump_metadata(NULL, stg->metadata, " ", AV_LOG_INFO); + dump_dictionary(NULL, mix_presentation->annotations, "Annotations", " ", AV_LOG_INFO); + for (int j = 0; j < mix_presentation->nb_submixes; j++) { + AVIAMFSubmix *sub_mix = mix_presentation->submixes[j]; + av_log(NULL, AV_LOG_INFO, " Submix %d:\n", j); + for (int k = 0; k < sub_mix->nb_elements; k++) { + const AVIAMFSubmixElement *submix_element = sub_mix->elements[k]; + const AVStreamGroup *audio_element = NULL; + for (int l = 0; l < ic->nb_stream_groups; l++) + if (ic->stream_groups[l]->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT && + ic->stream_groups[l]->id == submix_element->audio_element_id) { + audio_element = ic->stream_groups[l]; + break; + } + if (audio_element) { + av_log(NULL, AV_LOG_INFO, " IAMF Audio Element #%d:%d", + index, audio_element->index); + if (flags & AVFMT_SHOW_IDS) + av_log(NULL, AV_LOG_INFO, "[0x%"PRIx64"]", audio_element->id); + av_log(NULL, AV_LOG_INFO, "\n"); + dump_dictionary(NULL, submix_element->annotations, "Annotations", " ", AV_LOG_INFO); + } + } + for (int k = 0; k < sub_mix->nb_layouts; k++) { + const AVIAMFSubmixLayout *submix_layout = sub_mix->layouts[k]; + av_log(NULL, AV_LOG_INFO, " Layout #%d:", k); + if (submix_layout->layout_type == 2) { + ret = av_channel_layout_describe(&submix_layout->sound_system, buf, sizeof(buf)); + if (ret >= 0) + av_log(NULL, AV_LOG_INFO, " %s", buf); + } else if (submix_layout->layout_type == 3) + av_log(NULL, AV_LOG_INFO, " Binaural"); + av_log(NULL, AV_LOG_INFO, "\n"); + } + } + break; + } + case AV_STREAM_GROUP_PARAMS_TILE_GRID: { + const AVStreamGroupTileGrid *tile_grid = stg->params.tile_grid; + AVCodecContext *avctx = avcodec_alloc_context3(NULL); + const char *ptr = NULL; + av_log(NULL, AV_LOG_INFO, " Tile Grid:"); + if (avctx && stg->nb_streams && !avcodec_parameters_to_context(avctx, stg->streams[0]->codecpar)) { + avctx->width = tile_grid->width; + avctx->height = tile_grid->height; + avctx->coded_width = tile_grid->coded_width; + avctx->coded_height = tile_grid->coded_height; + if (ic->dump_separator) + av_opt_set(avctx, "dump_separator", ic->dump_separator, 0); + buf[0] = 0; + avcodec_string(buf, sizeof(buf), avctx, is_output); + ptr = av_stristr(buf, " "); + } + avcodec_free_context(&avctx); + if (ptr) + av_log(NULL, AV_LOG_INFO, "%s", ptr); + dump_disposition(stg->disposition, AV_LOG_INFO); + av_log(NULL, AV_LOG_INFO, "\n"); + dump_metadata(NULL, stg->metadata, " ", AV_LOG_INFO); + for (int i = 0; i < stg->nb_streams; i++) { + const AVStream *st = stg->streams[i]; + dump_stream_format(ic, st->index, i, index, is_output, AV_LOG_VERBOSE); + printed[st->index] = 1; + } + break; + } + default: + break; + } } void av_dump_format(AVFormatContext *ic, int index, @@ -639,7 +770,7 @@ void av_dump_format(AVFormatContext *ic, int index, index, is_output ? ic->oformat->name : ic->iformat->name, is_output ? "to" : "from", url); - dump_metadata(NULL, ic->metadata, " "); + dump_metadata(NULL, ic->metadata, " ", AV_LOG_INFO); if (!is_output) { av_log(NULL, AV_LOG_INFO, " Duration: "); @@ -685,7 +816,7 @@ void av_dump_format(AVFormatContext *ic, int index, av_log(NULL, AV_LOG_INFO, "end %f\n", ch->end * av_q2d(ch->time_base)); - dump_metadata(NULL, ch->metadata, " "); + dump_metadata(NULL, ch->metadata, " ", AV_LOG_INFO); } if (ic->nb_programs) { @@ -696,10 +827,10 @@ void av_dump_format(AVFormatContext *ic, int index, "name", NULL, 0); av_log(NULL, AV_LOG_INFO, " Program %d %s\n", program->id, name ? name->value : ""); - dump_metadata(NULL, program->metadata, " "); + dump_metadata(NULL, program->metadata, " ", AV_LOG_INFO); for (k = 0; k < program->nb_stream_indexes; k++) { dump_stream_format(ic, program->stream_index[k], - index, is_output); + -1, index, is_output, AV_LOG_INFO); printed[program->stream_index[k]] = 1; } total += program->nb_stream_indexes; @@ -708,9 +839,12 @@ void av_dump_format(AVFormatContext *ic, int index, av_log(NULL, AV_LOG_INFO, " No Program\n"); } + for (i = 0; i < ic->nb_stream_groups; i++) + dump_stream_group(ic, printed, i, index, is_output); + for (i = 0; i < ic->nb_streams; i++) if (!printed[i]) - dump_stream_format(ic, i, index, is_output); + dump_stream_format(ic, i, -1, index, is_output, AV_LOG_INFO); av_free(printed); } diff --git a/libavformat/dv.c b/libavformat/dv.c index ffed1a7a90e..c0d3343e376 100644 --- a/libavformat/dv.c +++ b/libavformat/dv.c @@ -69,6 +69,9 @@ struct DVDemuxContext { uint8_t audio_buf[4][8192]; int ach; int frames; + + int64_t next_pts_video; + int64_t next_pts_audio; }; static inline uint16_t dv_audio_12to16(uint16_t sample) @@ -280,7 +283,7 @@ static int dv_extract_audio_info(DVDemuxContext *c, const uint8_t *frame) if (!c->ast[i]) return AVERROR(ENOMEM); - avpriv_set_pts_info(c->ast[i], 64, c->sys->time_base.num, c->sys->time_base.den); + avpriv_set_pts_info(c->ast[i], 64, 1, DV_TIMESCALE_AUDIO); c->ast[i]->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; c->ast[i]->codecpar->codec_id = AV_CODEC_ID_PCM_S16LE; c->ast[i]->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO; @@ -314,8 +317,6 @@ static int dv_extract_video_info(DVDemuxContext *c, const uint8_t *frame) par = c->vst->codecpar; - avpriv_set_pts_info(c->vst, 64, c->sys->time_base.num, - c->sys->time_base.den); c->vst->avg_frame_rate = av_inv_q(c->vst->time_base); /* finding out SAR is a little bit messy */ @@ -357,9 +358,10 @@ static int dv_init_demux(AVFormatContext *s, DVDemuxContext *c) c->fctx = s; c->vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; c->vst->codecpar->codec_id = AV_CODEC_ID_DVVIDEO; - c->vst->codecpar->bit_rate = 25000000; c->vst->start_time = 0; + avpriv_set_pts_info(c->vst, 64, 1, DV_TIMESCALE_VIDEO); + /* Audio streams are added later as they are encountered. */ s->ctx_flags |= AVFMTCTX_NOHEADER; @@ -419,6 +421,7 @@ int avpriv_dv_get_packet(DVDemuxContext *c, AVPacket *pkt) int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt, uint8_t *buf, int buf_size, int64_t pos) { + int64_t pts, duration; int size, i; uint8_t *ppcm[5] = { 0 }; @@ -434,11 +437,31 @@ int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt, if (size < 0) return size; + if (c->ach) { + int64_t next_pts_video = av_rescale_q(c->next_pts_video, c->vst->time_base, + c->ast[0]->time_base); + + duration = av_rescale_q(size / 4, + (AVRational){ 1, c->audio_pkt[0].sample_rate }, + c->ast[0]->time_base); + + // if audio timestamps are more than one frame away from video, + // assume desync happened (e.g. due to dropped audio frames) and + // resynchronize + pts = (FFABS(next_pts_video - c->next_pts_audio) >= duration) ? + next_pts_video : c->next_pts_audio; + + c->next_pts_audio = pts + duration; + } + for (i = 0; i < c->ach; i++) { - c->audio_pkt[i].pos = pos; - c->audio_pkt[i].size = size; - c->audio_pkt[i].pts = (c->sys->height == 720) ? (c->frames & ~1) : c->frames; - c->audio_pkt[i].duration = 1; + DVPacket *dpkt = &c->audio_pkt[i]; + + dpkt->pos = pos; + dpkt->size = size; + dpkt->pts = pts; + dpkt->duration = duration; + ppcm[i] = c->audio_buf[i]; } if (c->ach) @@ -463,7 +486,10 @@ int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt, pkt->size = size; pkt->flags |= AV_PKT_FLAG_KEY; pkt->stream_index = c->vst->index; - pkt->pts = c->frames; + pkt->pts = c->next_pts_video; + pkt->duration = av_rescale_q(1, c->sys->time_base, c->vst->time_base); + + c->next_pts_video += pkt->duration; } c->frames++; @@ -472,28 +498,36 @@ int avpriv_dv_produce_packet(DVDemuxContext *c, AVPacket *pkt, } static int64_t dv_frame_offset(AVFormatContext *s, DVDemuxContext *c, - int64_t timestamp, int flags) + int64_t *timestamp) { // FIXME: sys may be wrong if last dv_read_packet() failed (buffer is junk) FFFormatContext *const si = ffformatcontext(s); const int frame_size = c->sys->frame_size; + int64_t frame_count = av_rescale_q(*timestamp, c->vst->time_base, c->sys->time_base); int64_t offset; int64_t size = avio_size(s->pb) - si->data_offset; int64_t max_offset = ((size - 1) / frame_size) * frame_size; - offset = frame_size * timestamp; + offset = frame_size * frame_count; if (size >= 0 && offset > max_offset) offset = max_offset; else if (offset < 0) offset = 0; + *timestamp = av_rescale_q(offset / frame_size, c->sys->time_base, c->vst->time_base); + return offset + si->data_offset; } -void ff_dv_offset_reset(DVDemuxContext *c, int64_t frame_offset) +void ff_dv_ts_reset(DVDemuxContext *c, int64_t ts) { - c->frames = frame_offset; + c->frames = !c->sys ? 0 : + av_rescale_q(ts, c->vst->time_base, c->sys->time_base); + c->next_pts_video = ts; + c->next_pts_audio = (!c->sys || !c->ast[0]) ? AV_NOPTS_VALUE : + av_rescale_q(ts, c->vst->time_base, c->ast[0]->time_base); + c->audio_pkt[0].size = c->audio_pkt[1].size = 0; c->audio_pkt[2].size = c->audio_pkt[3].size = 0; } @@ -618,12 +652,19 @@ static int dv_read_seek(AVFormatContext *s, int stream_index, { RawDVContext *r = s->priv_data; DVDemuxContext *c = &r->dv_demux; - int64_t offset = dv_frame_offset(s, c, timestamp, flags); + int64_t offset; + + // seek using the video stream + if (stream_index != c->vst->index) + timestamp = av_rescale_q(timestamp, s->streams[stream_index]->time_base, + c->vst->time_base); + + offset = dv_frame_offset(s, c, ×tamp); if (avio_seek(s->pb, offset, SEEK_SET) < 0) return -1; - ff_dv_offset_reset(c, offset / c->sys->frame_size); + ff_dv_ts_reset(c, timestamp); return 0; } @@ -669,15 +710,15 @@ static int dv_probe(const AVProbeData *p) return 0; } -const AVInputFormat ff_dv_demuxer = { - .name = "dv", - .long_name = NULL_IF_CONFIG_SMALL("DV (Digital Video)"), +const FFInputFormat ff_dv_demuxer = { + .p.name = "dv", + .p.long_name = NULL_IF_CONFIG_SMALL("DV (Digital Video)"), + .p.extensions = "dv,dif", .priv_data_size = sizeof(RawDVContext), .read_probe = dv_probe, .read_header = dv_read_header, .read_packet = dv_read_packet, .read_seek = dv_read_seek, - .extensions = "dv,dif", }; #else // CONFIG_DV_DEMUXER diff --git a/libavformat/dv.h b/libavformat/dv.h index efced6ccf04..d21ea19e024 100644 --- a/libavformat/dv.h +++ b/libavformat/dv.h @@ -34,6 +34,6 @@ typedef struct DVDemuxContext DVDemuxContext; DVDemuxContext* avpriv_dv_init_demux(AVFormatContext* s); int avpriv_dv_get_packet(DVDemuxContext*, AVPacket *); int avpriv_dv_produce_packet(DVDemuxContext*, AVPacket*, uint8_t*, int, int64_t); -void ff_dv_offset_reset(DVDemuxContext *c, int64_t frame_offset); +void ff_dv_ts_reset(DVDemuxContext *c, int64_t ts_video); #endif /* AVFORMAT_DV_H */ diff --git a/libavformat/dvdclut.c b/libavformat/dvdclut.c new file mode 100644 index 00000000000..cd4b103e4b5 --- /dev/null +++ b/libavformat/dvdclut.c @@ -0,0 +1,75 @@ +/* + * DVD-Video subpicture CLUT (Color Lookup Table) utilities + * + * 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/bprint.h" +#include "libavutil/colorspace.h" +#include "libavutil/common.h" + +#include "dvdclut.h" +#include "internal.h" + +int ff_dvdclut_palette_extradata_cat(const uint32_t *clut, + const size_t clut_size, + AVCodecParameters *par) +{ + AVBPrint bp; + + if (clut_size != FF_DVDCLUT_CLUT_SIZE) + return AVERROR(EINVAL); + + av_bprint_init(&bp, 0, FF_DVDCLUT_EXTRADATA_SIZE); + + av_bprintf(&bp, "palette: "); + + for (int i = 0; i < FF_DVDCLUT_CLUT_LEN; i++) + av_bprintf(&bp, "%06"PRIx32"%s", clut[i], + i != (FF_DVDCLUT_CLUT_LEN - 1) ? ", " : ""); + + av_bprintf(&bp, "\n"); + + return ff_bprint_to_codecpar_extradata(par, &bp); +} + +int ff_dvdclut_yuv_to_rgb(uint32_t *clut, const size_t clut_size) +{ + int y, cb, cr; + uint8_t r, g, b; + int r_add, g_add, b_add; + + if (clut_size != FF_DVDCLUT_CLUT_SIZE) + return AVERROR(EINVAL); + + for (int i = 0; i < FF_DVDCLUT_CLUT_LEN; i++) { + y = (clut[i] >> 16) & 0xFF; + cr = (clut[i] >> 8) & 0xFF; + cb = clut[i] & 0xFF; + + YUV_TO_RGB1_CCIR(cb, cr); + + y = (y - 16) * FIX(255.0 / 219.0); + r = av_clip_uint8((y + r_add - 1024) >> SCALEBITS); + g = av_clip_uint8((y + g_add - 1024) >> SCALEBITS); + b = av_clip_uint8((y + b_add - 1024) >> SCALEBITS); + + clut[i] = (r << 16) | (g << 8) | b; + } + + return 0; +} diff --git a/libavfilter/dnn/dnn_backend_openvino.h b/libavformat/dvdclut.h similarity index 53% rename from libavfilter/dnn/dnn_backend_openvino.h rename to libavformat/dvdclut.h index 304bc96b998..41cea7e2c9d 100644 --- a/libavfilter/dnn/dnn_backend_openvino.h +++ b/libavformat/dvdclut.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 + * DVD-Video subpicture CLUT (Color Lookup Table) utilities * * This file is part of FFmpeg. * @@ -18,23 +18,20 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -/** - * @file - * DNN inference functions interface for OpenVINO backend. - */ - - -#ifndef AVFILTER_DNN_DNN_BACKEND_OPENVINO_H -#define AVFILTER_DNN_DNN_BACKEND_OPENVINO_H +#ifndef AVFORMAT_DVDCLUT_H +#define AVFORMAT_DVDCLUT_H -#include "../dnn_interface.h" +#include "libavcodec/codec_par.h" -DNNModel *ff_dnn_load_model_ov(const char *model_filename, DNNFunctionType func_type, const char *options, AVFilterContext *filter_ctx); +/* ("palette: ") + ("rrggbb, "*15) + ("rrggbb") + \n + \0 */ +#define FF_DVDCLUT_EXTRADATA_SIZE (9 + (8 * 15) + 6 + 1 + 1) +#define FF_DVDCLUT_CLUT_LEN 16 +#define FF_DVDCLUT_CLUT_SIZE FF_DVDCLUT_CLUT_LEN * sizeof(uint32_t) -int ff_dnn_execute_model_ov(const DNNModel *model, DNNExecBaseParams *exec_params); -DNNAsyncStatusType ff_dnn_get_result_ov(const DNNModel *model, AVFrame **in, AVFrame **out); -int ff_dnn_flush_ov(const DNNModel *model); +int ff_dvdclut_palette_extradata_cat(const uint32_t *clut, + const size_t clut_size, + AVCodecParameters *par); -void ff_dnn_free_model_ov(DNNModel **model); +int ff_dvdclut_yuv_to_rgb(uint32_t *clut, const size_t clut_size); -#endif +#endif /* AVFORMAT_DVDCLUT_H */ diff --git a/libavformat/dvdvideodec.c b/libavformat/dvdvideodec.c new file mode 100644 index 00000000000..c94e7f7fe6b --- /dev/null +++ b/libavformat/dvdvideodec.c @@ -0,0 +1,1714 @@ +/* + * DVD-Video demuxer, powered by libdvdnav and libdvdread + * + * 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 + */ + +/* + * See doc/demuxers.texi for a high-level overview. + * + * The tactical approach is as follows: + * 1) Open the volume with dvdread + * 2) Analyze the user-requested title and PGC coordinates in the IFO structures + * 3) Request playback at the coordinates and chosen angle with dvdnav + * 5) Begin the playback (reading and demuxing) of MPEG-PS blocks + * 6) End playback if navigation goes backwards, to a menu, or a different PGC or angle + * 7) Close the dvdnav VM, and free dvdread's IFO structures + */ + +#include + +#include +#include +#include +#include +#include + +#include "libavutil/avstring.h" +#include "libavutil/avutil.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" +#include "libavutil/samplefmt.h" +#include "libavutil/time.h" +#include "libavutil/timestamp.h" + +#include "avformat.h" +#include "avio_internal.h" +#include "avlanguage.h" +#include "demux.h" +#include "dvdclut.h" +#include "internal.h" +#include "url.h" + +#define DVDVIDEO_MAX_PS_SEARCH_BLOCKS 128 +#define DVDVIDEO_BLOCK_SIZE 2048 +#define DVDVIDEO_TIME_BASE_Q (AVRational) { 1, 90000 } +#define DVDVIDEO_PTS_WRAP_BITS 64 /* VOBUs use 32 (PES allows 33) */ +#define DVDVIDEO_LIBDVDX_LOG_BUFFER_SIZE 1024 + +#define PCI_START_BYTE 45 /* complement dvdread's DSI_START_BYTE */ +static const uint8_t dvdvideo_nav_header[4] = { 0x00, 0x00, 0x01, 0xBF }; + +enum DVDVideoSubpictureViewport { + DVDVIDEO_SUBP_VIEWPORT_FULLSCREEN, + DVDVIDEO_SUBP_VIEWPORT_WIDESCREEN, + DVDVIDEO_SUBP_VIEWPORT_LETTERBOX, + DVDVIDEO_SUBP_VIEWPORT_PANSCAN +}; +static const char dvdvideo_subp_viewport_labels[4][13] = { + "Fullscreen", "Widescreen", "Letterbox", "Pan and Scan" +}; + +typedef struct DVDVideoVTSVideoStreamEntry { + int startcode; + enum AVCodecID codec_id; + int width; + int height; + AVRational dar; + AVRational framerate; + int has_cc; +} DVDVideoVTSVideoStreamEntry; + +typedef struct DVDVideoPGCAudioStreamEntry { + int startcode; + enum AVCodecID codec_id; + int sample_fmt; + int sample_rate; + int bit_depth; + int nb_channels; + AVChannelLayout ch_layout; + int disposition; + const char *lang_iso; +} DVDVideoPGCAudioStreamEntry; + +typedef struct DVDVideoPGCSubtitleStreamEntry { + int startcode; + enum DVDVideoSubpictureViewport viewport; + int disposition; + uint32_t clut[FF_DVDCLUT_CLUT_LEN]; + const char *lang_iso; +} DVDVideoPGCSubtitleStreamEntry; + +typedef struct DVDVideoPlaybackState { + int celln; /* ID of the active cell */ + int entry_pgn; /* ID of the PG we are starting in */ + int in_pgc; /* if our navigator is in the PGC */ + int in_ps; /* if our navigator is in the program stream */ + int in_vts; /* if our navigator is in the VTS */ + int64_t nav_pts; /* PTS according to IFO, not frame-accurate */ + int nb_cells_played; /* number of cells played back so far */ + uint64_t pgc_duration_est; /* estimated duration as reported by IFO */ + uint64_t pgc_elapsed; /* the elapsed time of the PGC, cell-relative */ + int pgc_nb_pg_est; /* number of PGs as reported by IFOs */ + int pgcn; /* ID of the PGC we are playing */ + int pgn; /* ID of the PG we are in now */ + int ptt; /* ID of the chapter we are in now */ + int64_t ts_offset; /* PTS discontinuity offset (ex. VOB change) */ + uint32_t vobu_duration; /* duration of the current VOBU */ + uint32_t vobu_e_ptm; /* end PTM of the current VOBU */ + int vtsn; /* ID of the active VTS (video title set) */ + uint64_t *pgc_pg_times_est; /* PG start times as reported by IFO */ + pgc_t *pgc; /* handle to the active PGC */ + dvdnav_t *dvdnav; /* handle to the dvdnav VM */ + + /* the following fields are only used for menu playback */ + int celln_start; /* starting cell number */ + int celln_end; /* ending cell number */ + int sector_offset; /* current sector relative to the current VOB */ + uint32_t sector_end; /* end sector relative to the current VOBU */ + uint32_t vobu_next; /* the next VOBU pointer */ + uint32_t vobu_remaining; /* remaining blocks for current VOBU */ + dvd_file_t *vob_file; /* handle to the menu VOB (VMG or VTS) */ +} DVDVideoPlaybackState; + +typedef struct DVDVideoDemuxContext { + const AVClass *class; + + /* options */ + int opt_angle; /* the user-provided angle number (1-indexed) */ + int opt_chapter_end; /* the user-provided exit PTT (0 for last) */ + int opt_chapter_start; /* the user-provided entry PTT (1-indexed) */ + int opt_menu; /* demux menu domain instead of title domain */ + int opt_menu_lu; /* the menu language unit (logical grouping) */ + int opt_menu_vts; /* the menu VTS, or 0 for VMG (main menu) */ + int opt_pg; /* the user-provided PG number (1-indexed) */ + int opt_pgc; /* the user-provided PGC number (1-indexed) */ + int opt_preindex; /* pre-indexing mode (2-pass read) */ + int opt_region; /* the user-provided region digit */ + int opt_title; /* the user-provided title number (1-indexed) */ + int opt_trim; /* trim padding cells at beginning */ + + /* subdemux */ + AVFormatContext *mpeg_ctx; /* context for inner demuxer */ + uint8_t *mpeg_buf; /* buffer for inner demuxer */ + FFIOContext mpeg_pb; /* buffer context for inner demuxer */ + + /* volume */ + dvd_reader_t *dvdread; /* handle to libdvdread */ + ifo_handle_t *vmg_ifo; /* handle to the VMG (VIDEO_TS.IFO) */ + ifo_handle_t *vts_ifo; /* handle to the active VTS (VTS_nn_n.IFO) */ + + /* playback control */ + int64_t first_pts; /* the PTS of the first video keyframe */ + int play_end; /* signal EOF to the parent demuxer */ + DVDVideoPlaybackState play_state; /* the active playback state */ + int play_started; /* signal that playback has started */ + int segment_started; /* signal that subdemuxer is on a segment */ +} DVDVideoDemuxContext; + +static void dvdvideo_libdvdread_log(void *opaque, dvd_logger_level_t level, + const char *msg, va_list msg_va) +{ + AVFormatContext *s = opaque; + char msg_buf[DVDVIDEO_LIBDVDX_LOG_BUFFER_SIZE]; + int lavu_level = AV_LOG_DEBUG; + + vsnprintf(msg_buf, sizeof(msg_buf), msg, msg_va); + + if (level == DVD_LOGGER_LEVEL_ERROR) + lavu_level = AV_LOG_ERROR; + else if (level == DVD_LOGGER_LEVEL_WARN) + lavu_level = AV_LOG_WARNING; + + av_log(s, lavu_level, "libdvdread: %s\n", msg_buf); +} + +static void dvdvideo_libdvdnav_log(void *opaque, dvdnav_logger_level_t level, + const char *msg, va_list msg_va) +{ + AVFormatContext *s = opaque; + char msg_buf[DVDVIDEO_LIBDVDX_LOG_BUFFER_SIZE]; + int lavu_level = AV_LOG_DEBUG; + + vsnprintf(msg_buf, sizeof(msg_buf), msg, msg_va); + + if (level == DVDNAV_LOGGER_LEVEL_ERROR) + lavu_level = AV_LOG_ERROR; + /* some discs have invalid language codes set for menus, which throws noisy warnings */ + else if (level == DVDNAV_LOGGER_LEVEL_WARN && !av_strstart(msg, "Language", NULL)) + lavu_level = AV_LOG_WARNING; + + av_log(s, lavu_level, "libdvdnav: %s\n", msg_buf); +} + +static void dvdvideo_ifo_close(AVFormatContext *s) +{ + DVDVideoDemuxContext *c = s->priv_data; + + if (c->vts_ifo) + ifoClose(c->vts_ifo); + + if (c->vmg_ifo) + ifoClose(c->vmg_ifo); + + if (c->dvdread) + DVDClose(c->dvdread); +} + +static int dvdvideo_ifo_open(AVFormatContext *s) +{ + DVDVideoDemuxContext *c = s->priv_data; + + dvd_logger_cb dvdread_log_cb; + title_info_t title_info; + + dvdread_log_cb = (dvd_logger_cb) { .pf_log = dvdvideo_libdvdread_log }; + c->dvdread = DVDOpen2(s, &dvdread_log_cb, s->url); + + if (!c->dvdread) { + av_log(s, AV_LOG_ERROR, "Unable to open the DVD-Video structure\n"); + + return AVERROR_EXTERNAL; + } + + if (!(c->vmg_ifo = ifoOpen(c->dvdread, 0))) { + av_log(s, AV_LOG_ERROR, "Unable to open the VMG (VIDEO_TS.IFO)\n"); + + return AVERROR_EXTERNAL; + } + + if (c->opt_menu) { + if (c->opt_menu_vts > 0 && !(c->vts_ifo = ifoOpen(c->dvdread, c->opt_menu_vts))) { + av_log(s, AV_LOG_ERROR, "Unable to open IFO structure for VTS %d\n", c->opt_menu_vts); + + return AVERROR_EXTERNAL; + } + + return 0; + } + + if (c->opt_title > c->vmg_ifo->tt_srpt->nr_of_srpts) { + av_log(s, AV_LOG_ERROR, "Title %d not found\n", c->opt_title); + + return AVERROR_STREAM_NOT_FOUND; + } + + title_info = c->vmg_ifo->tt_srpt->title[c->opt_title - 1]; + if (c->opt_angle > title_info.nr_of_angles) { + av_log(s, AV_LOG_ERROR, "Angle %d not found\n", c->opt_angle); + + return AVERROR_STREAM_NOT_FOUND; + } + + if (title_info.nr_of_ptts < 1) { + av_log(s, AV_LOG_ERROR, "Title %d has invalid headers (no PTTs found)\n", c->opt_title); + + return AVERROR_INVALIDDATA; + } + + if (c->opt_chapter_start > title_info.nr_of_ptts || + (c->opt_chapter_end > 0 && c->opt_chapter_end > title_info.nr_of_ptts)) { + av_log(s, AV_LOG_ERROR, "Chapter (PTT) range [%d, %d] is invalid\n", + c->opt_chapter_start, c->opt_chapter_end); + + return AVERROR_INVALIDDATA; + } + + if (!(c->vts_ifo = ifoOpen(c->dvdread, title_info.title_set_nr))) { + av_log(s, AV_LOG_ERROR, "Unable to process IFO structure for VTS %d\n", + title_info.title_set_nr); + + return AVERROR_EXTERNAL; + } + + if (title_info.vts_ttn < 1 || + title_info.vts_ttn > 99 || + title_info.vts_ttn > c->vts_ifo->vts_ptt_srpt->nr_of_srpts || + c->vts_ifo->vtsi_mat->nr_of_vts_audio_streams > 8 || + c->vts_ifo->vtsi_mat->nr_of_vts_subp_streams > 32) { + + av_log(s, AV_LOG_ERROR, "Title %d has invalid headers in VTS\n", c->opt_title); + return AVERROR_INVALIDDATA; + } + + return 0; +} + +static int dvdvideo_is_cell_promising(AVFormatContext *s, pgc_t *pgc, int celln) +{ + dvd_time_t cell_duration = pgc->cell_playback[celln - 1].playback_time; + + return cell_duration.second >= 1 || cell_duration.minute >= 1 || cell_duration.hour >= 1; +} + +static int dvdvideo_is_pgc_promising(AVFormatContext *s, pgc_t *pgc) +{ + for (int i = 1; i <= pgc->nr_of_cells; i++) + if (dvdvideo_is_cell_promising(s, pgc, i)) + return 1; + + return 0; +} + +static void dvdvideo_menu_close(AVFormatContext *s, DVDVideoPlaybackState *state) +{ + if (state->vob_file) + DVDCloseFile(state->vob_file); +} + +static int dvdvideo_menu_open(AVFormatContext *s, DVDVideoPlaybackState *state) +{ + DVDVideoDemuxContext *c = s->priv_data; + pgci_ut_t *pgci_ut; + + pgci_ut = c->opt_menu_vts ? c->vts_ifo->pgci_ut : c->vmg_ifo->pgci_ut; + if (!pgci_ut) { + av_log(s, AV_LOG_ERROR, "Invalid PGC table for menu [LU %d, PGC %d]\n", + c->opt_menu_lu, c->opt_pgc); + + return AVERROR_INVALIDDATA; + } + + if (c->opt_pgc < 1 || + c->opt_menu_lu < 1 || + c->opt_menu_lu > pgci_ut->nr_of_lus || + c->opt_pgc > pgci_ut->lu[c->opt_menu_lu - 1].pgcit->nr_of_pgci_srp) { + + av_log(s, AV_LOG_ERROR, "Menu [LU %d, PGC %d] not found\n", c->opt_menu_lu, c->opt_pgc); + + return AVERROR(EINVAL); + } + + /* make sure the PGC is valid */ + state->pgcn = c->opt_pgc - 1; + state->pgc = pgci_ut->lu[c->opt_menu_lu - 1].pgcit->pgci_srp[c->opt_pgc - 1].pgc; + if (!state->pgc || !state->pgc->program_map || !state->pgc->cell_playback) { + av_log(s, AV_LOG_ERROR, "Invalid PGC structure for menu [LU %d, PGC %d]\n", + c->opt_menu_lu, c->opt_pgc); + + return AVERROR_INVALIDDATA; + } + + /* make sure the PG is valid */ + state->entry_pgn = c->opt_pg; + if (state->entry_pgn < 1 || state->entry_pgn > state->pgc->nr_of_programs) { + av_log(s, AV_LOG_ERROR, "Entry PG %d not found\n", state->entry_pgn); + + return AVERROR(EINVAL); + } + + /* make sure the program map isn't leading us to nowhere */ + state->celln_start = state->pgc->program_map[state->entry_pgn - 1]; + state->celln_end = state->pgc->nr_of_cells; + state->celln = state->celln_start; + if (state->celln_start > state->pgc->nr_of_cells) { + av_log(s, AV_LOG_ERROR, "Invalid PGC structure: program map points to unknown cell\n"); + + return AVERROR_INVALIDDATA; + } + + state->sector_end = state->pgc->cell_playback[state->celln - 1].last_sector; + state->vobu_next = state->pgc->cell_playback[state->celln - 1].first_sector; + state->sector_offset = state->vobu_next; + + if (c->opt_menu_vts > 0) + state->in_vts = 1; + + if (!(state->vob_file = DVDOpenFile(c->dvdread, c->opt_menu_vts, DVD_READ_MENU_VOBS))) { + av_log(s, AV_LOG_ERROR, !c->opt_menu_vts ? + "Unable to open main menu VOB (VIDEO_TS.VOB)\n" : + "Unable to open menu VOBs for VTS %d\n", c->opt_menu_vts); + + return AVERROR_EXTERNAL; + } + + return 0; +} + +static int dvdvideo_menu_next_ps_block(AVFormatContext *s, DVDVideoPlaybackState *state, + uint8_t *buf, int buf_size, + void (*flush_cb)(AVFormatContext *s)) +{ + int64_t blocks_read = 0; + uint8_t read_buf[DVDVIDEO_BLOCK_SIZE] = {0}; + pci_t pci = (pci_t) {0}; + dsi_t dsi = (dsi_t) {0}; + + if (buf_size != DVDVIDEO_BLOCK_SIZE) { + av_log(s, AV_LOG_ERROR, "Invalid buffer size (expected=%d actual=%d)\n", + DVDVIDEO_BLOCK_SIZE, buf_size); + + return AVERROR(EINVAL); + } + + /* we were at the end of a vobu, so now go to the next one or EOF */ + if (!state->vobu_remaining && state->in_pgc) { + if (state->vobu_next == SRI_END_OF_CELL) { + if (state->celln == state->celln_end && state->sector_offset > state->sector_end) + return AVERROR_EOF; + + state->celln++; + state->sector_offset = state->pgc->cell_playback[state->celln - 1].first_sector; + state->sector_end = state->pgc->cell_playback[state->celln - 1].last_sector; + } else { + state->sector_offset = state->vobu_next; + } + } + + /* continue reading the VOBU */ + av_log(s, AV_LOG_TRACE, "reading block at offset %d\n", state->sector_offset); + + blocks_read = DVDReadBlocks(state->vob_file, state->sector_offset, 1, read_buf); + if (blocks_read != 1) { + av_log(s, AV_LOG_ERROR, "Unable to read VOB block: offset=%d blocks_read=%" PRId64 "\n", + state->sector_offset, blocks_read); + + return AVERROR_INVALIDDATA; + } + + /* we are at the start of a VOBU, so we are expecting a NAV packet */ + if (!state->vobu_remaining) { + if (!memcmp(&read_buf[PCI_START_BYTE - 4], dvdvideo_nav_header, 4) || + !memcmp(&read_buf[DSI_START_BYTE - 4], dvdvideo_nav_header, 4) || + read_buf[PCI_START_BYTE - 1] != 0x00 || + read_buf[DSI_START_BYTE - 1] != 0x01) { + + av_log(s, AV_LOG_ERROR, "Invalid NAV packet at offset %d: PCI or DSI header mismatch\n", + state->sector_offset); + + return AVERROR_INVALIDDATA; + } + + navRead_PCI(&pci, &read_buf[PCI_START_BYTE]); + navRead_DSI(&dsi, &read_buf[DSI_START_BYTE]); + + if (!pci.pci_gi.vobu_s_ptm || + !pci.pci_gi.vobu_e_ptm || + pci.pci_gi.vobu_s_ptm > pci.pci_gi.vobu_e_ptm) { + + av_log(s, AV_LOG_ERROR, "Invalid NAV packet at offset %d: PCI header is invalid\n", + state->sector_offset); + + return AVERROR_INVALIDDATA; + } + + state->vobu_remaining = dsi.dsi_gi.vobu_ea; + state->vobu_next = dsi.vobu_sri.next_vobu == SRI_END_OF_CELL ? SRI_END_OF_CELL : + dsi.dsi_gi.nv_pck_lbn + (dsi.vobu_sri.next_vobu & 0x7FFFFFFF); + state->sector_offset++; + + if (state->in_pgc) { + if (state->vobu_e_ptm != pci.pci_gi.vobu_s_ptm) { + if (flush_cb) + flush_cb(s); + + state->ts_offset += state->vobu_e_ptm - pci.pci_gi.vobu_s_ptm; + } + } else { + state->in_pgc = 1; + state->in_ps = 1; + } + + state->vobu_e_ptm = pci.pci_gi.vobu_e_ptm; + + av_log(s, AV_LOG_DEBUG, "NAV packet: sector=%d " + "vobu_s_ptm=%d vobu_e_ptm=%d ts_offset=%" PRId64 "\n", + dsi.dsi_gi.nv_pck_lbn, + pci.pci_gi.vobu_s_ptm, pci.pci_gi.vobu_e_ptm, state->ts_offset); + + return FFERROR_REDO; + } + + /* we are in the middle of a VOBU, so pass on the PS packet */ + memcpy(buf, &read_buf, DVDVIDEO_BLOCK_SIZE); + state->sector_offset++; + state->vobu_remaining--; + + return DVDVIDEO_BLOCK_SIZE; +} + +static void dvdvideo_play_close(AVFormatContext *s, DVDVideoPlaybackState *state) +{ + if (!state->dvdnav) + return; + + /* not allocated by av_malloc() */ + if (state->pgc_pg_times_est) + free(state->pgc_pg_times_est); + + if (dvdnav_close(state->dvdnav) != DVDNAV_STATUS_OK) + av_log(s, AV_LOG_ERROR, "Unable to close dvdnav successfully, dvdnav error: %s\n", + dvdnav_err_to_string(state->dvdnav)); +} + +static int dvdvideo_play_open(AVFormatContext *s, DVDVideoPlaybackState *state) +{ + DVDVideoDemuxContext *c = s->priv_data; + + dvdnav_logger_cb dvdnav_log_cb; + dvdnav_status_t dvdnav_open_status; + int32_t disc_region_mask; + int32_t player_region_mask; + int cur_title, cur_pgcn, cur_pgn; + pgc_t *pgc; + + dvdnav_log_cb = (dvdnav_logger_cb) { .pf_log = dvdvideo_libdvdnav_log }; + dvdnav_open_status = dvdnav_open2(&state->dvdnav, s, &dvdnav_log_cb, s->url); + + if (!state->dvdnav || + dvdnav_open_status != DVDNAV_STATUS_OK || + dvdnav_set_readahead_flag(state->dvdnav, 0) != DVDNAV_STATUS_OK || + dvdnav_set_PGC_positioning_flag(state->dvdnav, 1) != DVDNAV_STATUS_OK || + dvdnav_get_region_mask(state->dvdnav, &disc_region_mask) != DVDNAV_STATUS_OK) { + + av_log(s, AV_LOG_ERROR, "Unable to open the DVD for playback\n"); + goto end_dvdnav_error; + } + + player_region_mask = c->opt_region > 0 ? (1 << (c->opt_region - 1)) : disc_region_mask; + if (dvdnav_set_region_mask(state->dvdnav, player_region_mask) != DVDNAV_STATUS_OK) { + av_log(s, AV_LOG_ERROR, "Unable to set the playback region code %d\n", c->opt_region); + + goto end_dvdnav_error; + } + + if (c->opt_pgc > 0 && c->opt_pg > 0) { + if (dvdnav_program_play(state->dvdnav, c->opt_title, c->opt_pgc, c->opt_pg) != DVDNAV_STATUS_OK) { + av_log(s, AV_LOG_ERROR, "Unable to start playback at title %d, PGC %d, PG %d\n", + c->opt_title, c->opt_pgc, c->opt_pg); + + goto end_dvdnav_error; + } + + state->pgcn = c->opt_pgc; + state->entry_pgn = c->opt_pg; + } else { + if (dvdnav_part_play(state->dvdnav, c->opt_title, c->opt_chapter_start) != DVDNAV_STATUS_OK || + dvdnav_current_title_program(state->dvdnav, &cur_title, &cur_pgcn, &cur_pgn) != DVDNAV_STATUS_OK) { + + av_log(s, AV_LOG_ERROR, "Unable to start playback at title %d, chapter (PTT) %d\n", + c->opt_title, c->opt_chapter_start); + goto end_dvdnav_error; + } + + state->pgcn = cur_pgcn; + state->entry_pgn = cur_pgn; + } + + pgc = c->vts_ifo->vts_pgcit->pgci_srp[state->pgcn - 1].pgc; + + if (pgc->pg_playback_mode != 0) { + av_log(s, AV_LOG_ERROR, "Non-sequential PGCs, such as shuffles, are not supported\n"); + + return AVERROR_PATCHWELCOME; + } + + if (c->opt_trim && !dvdvideo_is_pgc_promising(s, pgc)) { + av_log(s, AV_LOG_ERROR, "Title %d, PGC %d looks empty (may consist of padding cells), " + "if you want to try anyway, disable the -trim option\n", + c->opt_title, state->pgcn); + + return AVERROR_INVALIDDATA; + } + + if (dvdnav_angle_change(state->dvdnav, c->opt_angle) != DVDNAV_STATUS_OK) { + av_log(s, AV_LOG_ERROR, "Unable to start playback at angle %d\n", c->opt_angle); + + goto end_dvdnav_error; + } + + /* dvdnav_describe_title_chapters() performs several validations on the title structure */ + /* take advantage of this side effect to increase chances of a safe navigation path */ + state->pgc_nb_pg_est = dvdnav_describe_title_chapters(state->dvdnav, c->opt_title, + &state->pgc_pg_times_est, + &state->pgc_duration_est); + + /* dvdnav returning 0 PGs is documented as an error condition */ + if (!state->pgc_nb_pg_est) { + av_log(s, AV_LOG_ERROR, "Unable to read chapter information for title %d\n", c->opt_title); + + goto end_dvdnav_error; + } + + state->nav_pts = dvdnav_get_current_time(state->dvdnav); + state->vtsn = c->vmg_ifo->tt_srpt->title[c->opt_title - 1].title_set_nr; + state->pgc = pgc; + + return 0; + +end_dvdnav_error: + if (state->dvdnav) + av_log(s, AV_LOG_ERROR, "dvdnav error: %s\n", dvdnav_err_to_string(state->dvdnav)); + else + av_log(s, AV_LOG_ERROR, "dvdnav could not be initialized\n"); + + return AVERROR_EXTERNAL; +} + +static int dvdvideo_play_next_ps_block(AVFormatContext *s, DVDVideoPlaybackState *state, + uint8_t *buf, int buf_size, + int *p_nav_event, + void (*flush_cb)(AVFormatContext *s)) +{ + DVDVideoDemuxContext *c = s->priv_data; + + uint8_t nav_buf[DVDVIDEO_BLOCK_SIZE] = {0}; + int nav_event; + int nav_len; + + dvdnav_vts_change_event_t *e_vts; + dvdnav_cell_change_event_t *e_cell; + int cur_title, cur_pgcn, cur_pgn, cur_angle, cur_title_unused, cur_ptt, cur_nb_angles; + int is_cell_promising = 0; + pci_t *e_pci; + dsi_t *e_dsi; + + if (buf_size != DVDVIDEO_BLOCK_SIZE) { + av_log(s, AV_LOG_ERROR, "Invalid buffer size (expected=%d actual=%d)\n", + DVDVIDEO_BLOCK_SIZE, buf_size); + + return AVERROR(EINVAL); + } + + for (int i = 0; i < DVDVIDEO_MAX_PS_SEARCH_BLOCKS; i++) { + if (ff_check_interrupt(&s->interrupt_callback)) + return AVERROR_EXIT; + + if (dvdnav_get_next_block(state->dvdnav, nav_buf, &nav_event, &nav_len) != DVDNAV_STATUS_OK) { + av_log(s, AV_LOG_ERROR, "Unable to read next block of PGC\n"); + + goto end_dvdnav_error; + } + + /* STOP event can come at any time and should be honored */ + if (nav_event == DVDNAV_STOP) + return AVERROR_EOF; + + if (nav_len > DVDVIDEO_BLOCK_SIZE) { + av_log(s, AV_LOG_ERROR, "Invalid block size (expected<=%d actual=%d)\n", + DVDVIDEO_BLOCK_SIZE, nav_len); + + return AVERROR_INVALIDDATA; + } + + if (dvdnav_current_title_info(state->dvdnav, &cur_title, &cur_ptt) != DVDNAV_STATUS_OK) { + av_log(s, AV_LOG_ERROR, "Unable to determine current title coordinates\n"); + + goto end_dvdnav_error; + } + + /* we somehow navigated to a menu */ + if (cur_title == 0 || !dvdnav_is_domain_vts(state->dvdnav)) + return AVERROR_EOF; + + if (dvdnav_current_title_program(state->dvdnav, &cur_title_unused, &cur_pgcn, &cur_pgn) != DVDNAV_STATUS_OK) { + av_log(s, AV_LOG_ERROR, "Unable to determine current PGC coordinates\n"); + + goto end_dvdnav_error; + } + + /* we somehow left the PGC */ + if (state->in_pgc && cur_pgcn != state->pgcn) + return AVERROR_EOF; + + if (dvdnav_get_angle_info(state->dvdnav, &cur_angle, &cur_nb_angles) != DVDNAV_STATUS_OK) { + av_log(s, AV_LOG_ERROR, "Unable to determine current video angle\n"); + + goto end_dvdnav_error; + } + + av_log(s, nav_event == DVDNAV_BLOCK_OK ? AV_LOG_TRACE : AV_LOG_DEBUG, + "new block: i=%d nav_event=%d nav_len=%d cur_title=%d " + "cur_ptt=%d cur_angle=%d cur_celln=%d cur_pgcn=%d cur_pgn=%d " + "play_in_vts=%d play_in_pgc=%d play_in_ps=%d\n", + i, nav_event, nav_len, cur_title, + cur_ptt, cur_angle, state->celln, cur_pgcn, cur_pgn, + state->in_vts, state->in_pgc, state->in_ps); + + switch (nav_event) { + case DVDNAV_VTS_CHANGE: + if (state->in_vts) + return AVERROR_EOF; + + e_vts = (dvdnav_vts_change_event_t *) nav_buf; + + if (e_vts->new_vtsN == state->vtsn && e_vts->new_domain == DVD_DOMAIN_VTSTitle) + state->in_vts = 1; + + continue; + case DVDNAV_CELL_CHANGE: + if (!state->in_vts) + continue; + + e_cell = (dvdnav_cell_change_event_t *) nav_buf; + is_cell_promising = !c->opt_trim || dvdvideo_is_cell_promising(s, state->pgc, e_cell->cellN); + + av_log(s, AV_LOG_DEBUG, "new cell: prev=%d new=%d promising=%d\n", + state->celln, e_cell->cellN, is_cell_promising); + + if (!state->in_ps && !state->in_pgc) { + if (cur_title == c->opt_title && + (c->opt_pgc || cur_ptt == c->opt_chapter_start) && + cur_pgcn == state->pgcn && + cur_pgn == state->entry_pgn && + is_cell_promising) { + + state->in_pgc = 1; + } + + if (c->opt_trim && !is_cell_promising) + av_log(s, AV_LOG_INFO, "Skipping padding cell #%d\n", e_cell->cellN); + } else if (state->celln >= e_cell->cellN || state->pgn > cur_pgn) { + return AVERROR_EOF; + } + + state->celln = e_cell->cellN; + state->ptt = cur_ptt; + state->pgn = cur_pgn; + state->nb_cells_played++; + + continue; + case DVDNAV_NAV_PACKET: + if (!state->in_pgc) + continue; + + if ((state->ptt > 0 && state->ptt > cur_ptt) || + (c->opt_chapter_end > 0 && cur_ptt > c->opt_chapter_end)) { + return AVERROR_EOF; + } + + e_pci = dvdnav_get_current_nav_pci(state->dvdnav); + e_dsi = dvdnav_get_current_nav_dsi(state->dvdnav); + + if (e_pci == NULL || e_dsi == NULL || + e_pci->pci_gi.vobu_s_ptm > e_pci->pci_gi.vobu_e_ptm) { + + av_log(s, AV_LOG_ERROR, "Invalid NAV packet\n"); + return AVERROR_INVALIDDATA; + } + + state->vobu_duration = e_pci->pci_gi.vobu_e_ptm - e_pci->pci_gi.vobu_s_ptm; + state->pgc_elapsed += state->vobu_duration; + state->nav_pts = dvdnav_get_current_time(state->dvdnav); + state->ptt = cur_ptt; + state->pgn = cur_pgn; + + av_log(s, AV_LOG_DEBUG, + "NAV packet: s_ptm=%d e_ptm=%d " + "scr=%d lbn=%d vobu_duration=%d nav_pts=%" PRId64 "\n", + e_pci->pci_gi.vobu_s_ptm, e_pci->pci_gi.vobu_e_ptm, + e_dsi->dsi_gi.nv_pck_scr, + e_pci->pci_gi.nv_pck_lbn, state->vobu_duration, state->nav_pts); + + if (!state->in_ps) { + av_log(s, AV_LOG_DEBUG, "navigation: locked to program stream\n"); + + state->in_ps = 1; + } else { + if (state->vobu_e_ptm != e_pci->pci_gi.vobu_s_ptm) { + if (flush_cb) + flush_cb(s); + + state->ts_offset += state->vobu_e_ptm - e_pci->pci_gi.vobu_s_ptm; + } + } + + state->vobu_e_ptm = e_pci->pci_gi.vobu_e_ptm; + + (*p_nav_event) = nav_event; + + return nav_len; + case DVDNAV_BLOCK_OK: + if (!state->in_ps) { + if (state->in_pgc) + i = 0; /* necessary in case we are skipping junk cells at the beginning */ + continue; + } + + if (nav_len != DVDVIDEO_BLOCK_SIZE) { + av_log(s, AV_LOG_ERROR, "Invalid MPEG block size (expected=%d actual=%d)\n", + DVDVIDEO_BLOCK_SIZE, nav_len); + + return AVERROR_INVALIDDATA; + } + + if (cur_angle != c->opt_angle) { + av_log(s, AV_LOG_ERROR, "Unexpected angle change (expected=%d new=%d)\n", + c->opt_angle, cur_angle); + + return AVERROR_INPUT_CHANGED; + } + + memcpy(buf, &nav_buf, nav_len); + + if (state->pgn != cur_pgn) + av_log(s, AV_LOG_WARNING, "Unexpected PG change (expected=%d actual=%d); " + "this could be due to a missed NAV packet\n", + state->pgn, cur_pgn); + + (*p_nav_event) = nav_event; + + return nav_len; + case DVDNAV_STILL_FRAME: + case DVDNAV_WAIT: + case DVDNAV_HOP_CHANNEL: + case DVDNAV_HIGHLIGHT: + if (state->in_ps) + return AVERROR_EOF; + + if (nav_event == DVDNAV_STILL_FRAME) { + if (dvdnav_still_skip(state->dvdnav) != DVDNAV_STATUS_OK) { + av_log(s, AV_LOG_ERROR, "Unable to skip still image\n"); + + goto end_dvdnav_error; + } + } + + if (nav_event == DVDNAV_WAIT) { + if (dvdnav_wait_skip(state->dvdnav) != DVDNAV_STATUS_OK) { + av_log(s, AV_LOG_ERROR, "Unable to skip WAIT event\n"); + + goto end_dvdnav_error; + } + } + + continue; + default: + continue; + } + } + + av_log(s, AV_LOG_ERROR, "Unable to find next program stream block\n"); + + return AVERROR_INVALIDDATA; + +end_dvdnav_error: + av_log(s, AV_LOG_ERROR, "dvdnav error (title=%d pgc=%d pg=%d cell=%d): %s\n", + cur_title, cur_pgcn, cur_pgn, state->celln, + dvdnav_err_to_string(state->dvdnav)); + + return AVERROR_EXTERNAL; +} + +static int dvdvideo_chapters_setup_simple(AVFormatContext *s) +{ + DVDVideoDemuxContext *c = s->priv_data; + + uint64_t time_prev = 0; + int64_t total_duration = 0; + + int chapter_start = c->opt_chapter_start; + int chapter_end = c->opt_chapter_end > 0 ? c->opt_chapter_end : c->play_state.pgc_nb_pg_est - 1; + + /* dvdnav_describe_title_chapters() describes PGs rather than PTTs, so validate our range */ + if (chapter_start == chapter_end || + c->play_state.pgc_nb_pg_est == 1 || + chapter_start > c->play_state.pgc_nb_pg_est || + chapter_end > c->play_state.pgc_nb_pg_est) { + + s->duration = av_rescale_q(c->play_state.pgc_duration_est, + DVDVIDEO_TIME_BASE_Q, AV_TIME_BASE_Q); + return 0; + } + + for (int i = chapter_start - 1; i < chapter_end; i++) { + uint64_t time_effective = c->play_state.pgc_pg_times_est[i] - c->play_state.nav_pts; + + if (!avpriv_new_chapter(s, i, DVDVIDEO_TIME_BASE_Q, time_prev, time_effective, NULL)) + return AVERROR(ENOMEM); + + time_prev = time_effective; + total_duration = time_effective; + } + + if (c->opt_chapter_start == 1 && c->opt_chapter_end == 0) + s->duration = av_rescale_q(c->play_state.pgc_duration_est, + DVDVIDEO_TIME_BASE_Q, AV_TIME_BASE_Q); + else + s->duration = av_rescale_q(total_duration, + DVDVIDEO_TIME_BASE_Q, AV_TIME_BASE_Q); + + return 0; +} + +static int dvdvideo_chapters_setup_preindex(AVFormatContext *s) +{ + DVDVideoDemuxContext *c = s->priv_data; + + int ret = 0, interrupt = 0; + int nb_chapters = 0, last_ptt = c->opt_chapter_start; + uint64_t cur_chapter_offset = 0, cur_chapter_duration = 0; + DVDVideoPlaybackState state = {0}; + + uint8_t nav_buf[DVDVIDEO_BLOCK_SIZE]; + int nav_event; + + if (c->opt_chapter_start == c->opt_chapter_end) + return ret; + + if ((ret = dvdvideo_play_open(s, &state)) < 0) + return ret; + + if (state.pgc->nr_of_programs == 1) + goto end_close; + + av_log(s, AV_LOG_INFO, + "Indexing chapter markers, this will take a long time. Please wait...\n"); + + while (!(interrupt = ff_check_interrupt(&s->interrupt_callback))) { + ret = dvdvideo_play_next_ps_block(s, &state, nav_buf, DVDVIDEO_BLOCK_SIZE, + &nav_event, NULL); + if (ret < 0 && ret != AVERROR_EOF) + goto end_close; + + if (nav_event != DVDNAV_NAV_PACKET && ret != AVERROR_EOF) + continue; + + if (state.ptt == last_ptt) { + cur_chapter_duration += state.vobu_duration; + /* ensure we add the last chapter */ + if (ret != AVERROR_EOF) + continue; + } + + if (!avpriv_new_chapter(s, nb_chapters, DVDVIDEO_TIME_BASE_Q, cur_chapter_offset, + cur_chapter_offset + cur_chapter_duration, NULL)) { + ret = AVERROR(ENOMEM); + goto end_close; + } + + nb_chapters++; + cur_chapter_offset += cur_chapter_duration; + cur_chapter_duration = state.vobu_duration; + last_ptt = state.ptt; + + if (ret == AVERROR_EOF) + break; + } + + if (interrupt) { + ret = AVERROR_EXIT; + goto end_close; + } + + if (ret < 0 && ret != AVERROR_EOF) + goto end_close; + + s->duration = av_rescale_q(state.pgc_elapsed, DVDVIDEO_TIME_BASE_Q, AV_TIME_BASE_Q); + + av_log(s, AV_LOG_INFO, "Chapter marker indexing complete\n"); + ret = 0; + +end_close: + dvdvideo_play_close(s, &state); + + return ret; +} + +static int dvdvideo_video_stream_analyze(AVFormatContext *s, video_attr_t video_attr, + DVDVideoVTSVideoStreamEntry *entry) +{ + AVRational framerate; + int height = 0; + int width = 0; + int is_pal = video_attr.video_format == 1; + + framerate = is_pal ? (AVRational) { 25, 1 } : (AVRational) { 30000, 1001 }; + height = is_pal ? 576 : 480; + + if (height > 0) { + switch (video_attr.picture_size) { + case 0: /* D1 */ + width = 720; + break; + case 1: /* 4CIF */ + width = 704; + break; + case 2: /* Half D1 */ + width = 352; + break; + case 3: /* CIF */ + width = 352; + height /= 2; + break; + } + } + + if (!width || !height) { + av_log(s, AV_LOG_ERROR, "Invalid video stream parameters in the IFO headers, " + "this could be an authoring error or empty title " + "(video_format=%d picture_size=%d)\n", + video_attr.video_format, video_attr.picture_size); + + return AVERROR_INVALIDDATA; + } + + entry->startcode = 0x1E0; + entry->codec_id = !video_attr.mpeg_version ? AV_CODEC_ID_MPEG1VIDEO : AV_CODEC_ID_MPEG2VIDEO; + entry->width = width; + entry->height = height; + entry->dar = video_attr.display_aspect_ratio ? (AVRational) { 16, 9 } : (AVRational) { 4, 3 }; + entry->framerate = framerate; + entry->has_cc = !is_pal && (video_attr.line21_cc_1 || video_attr.line21_cc_2); + + return 0; +} + +static int dvdvideo_video_stream_add(AVFormatContext *s, + DVDVideoVTSVideoStreamEntry *entry, + enum AVStreamParseType need_parsing) +{ + AVStream *st; + FFStream *sti; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->id = entry->startcode; + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + st->codecpar->codec_id = entry->codec_id; + st->codecpar->width = entry->width; + st->codecpar->height = entry->height; + st->codecpar->format = AV_PIX_FMT_YUV420P; + st->codecpar->color_range = AVCOL_RANGE_MPEG; + +#if FF_API_R_FRAME_RATE + st->r_frame_rate = entry->framerate; +#endif + st->avg_frame_rate = entry->framerate; + + sti = ffstream(st); + sti->request_probe = 0; + sti->need_parsing = need_parsing; + sti->display_aspect_ratio = entry->dar; + + avpriv_set_pts_info(st, DVDVIDEO_PTS_WRAP_BITS, + DVDVIDEO_TIME_BASE_Q.num, DVDVIDEO_TIME_BASE_Q.den); + + return 0; +} + +static int dvdvideo_video_stream_setup(AVFormatContext *s) +{ + DVDVideoDemuxContext *c = s->priv_data; + + int ret = 0; + DVDVideoVTSVideoStreamEntry entry = {0}; + video_attr_t video_attr; + + if (c->opt_menu) + video_attr = !c->opt_menu_vts ? c->vmg_ifo->vmgi_mat->vmgm_video_attr : + c->vts_ifo->vtsi_mat->vtsm_video_attr; + else + video_attr = c->vts_ifo->vtsi_mat->vts_video_attr; + + if ((ret = dvdvideo_video_stream_analyze(s, video_attr, &entry)) < 0 || + (ret = dvdvideo_video_stream_add(s, &entry, AVSTREAM_PARSE_HEADERS)) < 0) { + + av_log(s, AV_LOG_ERROR, "Unable to add video stream\n"); + return ret; + } + + return 0; +} + +static int dvdvideo_audio_stream_analyze(AVFormatContext *s, audio_attr_t audio_attr, + uint16_t audio_control, DVDVideoPGCAudioStreamEntry *entry) +{ + int startcode = 0; + enum AVCodecID codec_id = AV_CODEC_ID_NONE; + int sample_fmt = AV_SAMPLE_FMT_NONE; + int sample_rate = 0; + int bit_depth = 0; + int nb_channels = 0; + AVChannelLayout ch_layout = (AVChannelLayout) {0}; + char lang_dvd[3] = {0}; + + int position = (audio_control & 0x7F00) >> 8; + + /* XXX(PATCHWELCOME): SDDS is not supported due to lack of sample material */ + switch (audio_attr.audio_format) { + case 0: /* AC3 */ + codec_id = AV_CODEC_ID_AC3; + sample_fmt = AV_SAMPLE_FMT_FLTP; + sample_rate = 48000; + startcode = 0x80 + position; + break; + case 2: /* MP1 */ + codec_id = AV_CODEC_ID_MP1; + sample_fmt = audio_attr.quantization ? AV_SAMPLE_FMT_S32 : AV_SAMPLE_FMT_S16; + sample_rate = 48000; + bit_depth = audio_attr.quantization ? 20 : 16; + startcode = 0x1C0 + position; + break; + case 3: /* MP2 */ + codec_id = AV_CODEC_ID_MP2; + sample_fmt = audio_attr.quantization ? AV_SAMPLE_FMT_S32 : AV_SAMPLE_FMT_S16; + sample_rate = 48000; + bit_depth = audio_attr.quantization ? 20 : 16; + startcode = 0x1C0 + position; + break; + case 4: /* DVD PCM */ + codec_id = AV_CODEC_ID_PCM_DVD; + sample_fmt = audio_attr.quantization ? AV_SAMPLE_FMT_S32 : AV_SAMPLE_FMT_S16; + sample_rate = audio_attr.sample_frequency ? 96000 : 48000; + bit_depth = audio_attr.quantization == 2 ? 24 : (audio_attr.quantization ? 20 : 16); + startcode = 0xA0 + position; + break; + case 6: /* DCA */ + codec_id = AV_CODEC_ID_DTS; + sample_fmt = AV_SAMPLE_FMT_FLTP; + sample_rate = 48000; + bit_depth = audio_attr.quantization == 2 ? 24 : (audio_attr.quantization ? 20 : 16); + startcode = 0x88 + position; + break; + } + + nb_channels = audio_attr.channels + 1; + + if (codec_id == AV_CODEC_ID_NONE || + startcode == 0 || + sample_fmt == AV_SAMPLE_FMT_NONE || + sample_rate == 0 || + nb_channels == 0) { + + av_log(s, AV_LOG_ERROR, "Invalid audio stream parameters in the IFO headers, " + "this could be an authoring error or dummy title " + "(stream position %d in IFO)\n", position); + return AVERROR_INVALIDDATA; + } + + if (nb_channels == 1) + ch_layout = (AVChannelLayout) AV_CHANNEL_LAYOUT_MONO; + else if (nb_channels == 2) + ch_layout = (AVChannelLayout) AV_CHANNEL_LAYOUT_STEREO; + else if (nb_channels == 6) + ch_layout = (AVChannelLayout) AV_CHANNEL_LAYOUT_5POINT1; + else if (nb_channels == 7) + ch_layout = (AVChannelLayout) AV_CHANNEL_LAYOUT_6POINT1; + else if (nb_channels == 8) + ch_layout = (AVChannelLayout) AV_CHANNEL_LAYOUT_7POINT1; + + /* XXX(PATCHWELCOME): IFO structures have metadata on karaoke tracks for additional features */ + if (audio_attr.application_mode == 1) { + entry->disposition |= AV_DISPOSITION_KARAOKE; + + av_log(s, AV_LOG_WARNING, "Extended karaoke metadata is not supported at this time " + "(stream id=%d)\n", startcode); + } + + if (audio_attr.code_extension == 2) + entry->disposition |= AV_DISPOSITION_VISUAL_IMPAIRED; + if (audio_attr.code_extension == 3 || audio_attr.code_extension == 4) + entry->disposition |= AV_DISPOSITION_COMMENT; + + AV_WB16(lang_dvd, audio_attr.lang_code); + + entry->startcode = startcode; + entry->codec_id = codec_id; + entry->sample_rate = sample_rate; + entry->bit_depth = bit_depth; + entry->nb_channels = nb_channels; + entry->ch_layout = ch_layout; + entry->lang_iso = ff_convert_lang_to(lang_dvd, AV_LANG_ISO639_2_BIBL); + + return 0; +} + +static int dvdvideo_audio_stream_add(AVFormatContext *s, DVDVideoPGCAudioStreamEntry *entry, + enum AVStreamParseType need_parsing) +{ + AVStream *st; + FFStream *sti; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->id = entry->startcode; + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->codec_id = entry->codec_id; + st->codecpar->format = entry->sample_fmt; + st->codecpar->sample_rate = entry->sample_rate; + st->codecpar->bits_per_coded_sample = entry->bit_depth; + st->codecpar->bits_per_raw_sample = entry->bit_depth; + st->codecpar->ch_layout = entry->ch_layout; + st->codecpar->ch_layout.nb_channels = entry->nb_channels; + st->disposition = entry->disposition; + + if (entry->lang_iso) + av_dict_set(&st->metadata, "language", entry->lang_iso, 0); + + sti = ffstream(st); + sti->request_probe = 0; + sti->need_parsing = need_parsing; + + avpriv_set_pts_info(st, DVDVIDEO_PTS_WRAP_BITS, + DVDVIDEO_TIME_BASE_Q.num, DVDVIDEO_TIME_BASE_Q.den); + + return 0; +} + +static int dvdvideo_audio_stream_add_all(AVFormatContext *s) +{ + DVDVideoDemuxContext *c = s->priv_data; + + int ret = 0; + int nb_streams; + + if (c->opt_menu) + nb_streams = !c->opt_menu_vts ? c->vmg_ifo->vmgi_mat->nr_of_vmgm_audio_streams : + c->vts_ifo->vtsi_mat->nr_of_vtsm_audio_streams; + else + nb_streams = c->vts_ifo->vtsi_mat->nr_of_vts_audio_streams; + + for (int i = 0; i < nb_streams; i++) { + DVDVideoPGCAudioStreamEntry entry = {0}; + audio_attr_t audio_attr; + + if (c->opt_menu) + audio_attr = !c->opt_menu_vts ? c->vmg_ifo->vmgi_mat->vmgm_audio_attr : + c->vts_ifo->vtsi_mat->vtsm_audio_attr; + else + audio_attr = c->vts_ifo->vtsi_mat->vts_audio_attr[i]; + + if (!(c->play_state.pgc->audio_control[i] & 0x8000)) + continue; + + if ((ret = dvdvideo_audio_stream_analyze(s, audio_attr, c->play_state.pgc->audio_control[i], + &entry)) < 0) + goto break_error; + + /* IFO structures can declare duplicate entries for the same startcode */ + for (int j = 0; j < s->nb_streams; j++) + if (s->streams[j]->id == entry.startcode) + continue; + + if ((ret = dvdvideo_audio_stream_add(s, &entry, AVSTREAM_PARSE_HEADERS)) < 0) + goto break_error; + + continue; + +break_error: + av_log(s, AV_LOG_ERROR, "Unable to add audio stream at position %d\n", i); + return ret; + } + + return 0; +} + +static int dvdvideo_subp_stream_analyze(AVFormatContext *s, uint32_t offset, subp_attr_t subp_attr, + DVDVideoPGCSubtitleStreamEntry *entry) +{ + DVDVideoDemuxContext *c = s->priv_data; + + char lang_dvd[3] = {0}; + + entry->startcode = 0x20 + (offset & 0x1F); + + if (subp_attr.lang_extension == 9) + entry->disposition |= AV_DISPOSITION_FORCED; + + memcpy(&entry->clut, c->play_state.pgc->palette, FF_DVDCLUT_CLUT_SIZE); + + /* dvdsub palettes currently have no colorspace tagging and all muxers only support RGB */ + /* this is not a lossless conversion, but no use cases are supported for the original YUV */ + ff_dvdclut_yuv_to_rgb(entry->clut, FF_DVDCLUT_CLUT_SIZE); + + AV_WB16(lang_dvd, subp_attr.lang_code); + entry->lang_iso = ff_convert_lang_to(lang_dvd, AV_LANG_ISO639_2_BIBL); + + return 0; +} + +static int dvdvideo_subp_stream_add(AVFormatContext *s, DVDVideoPGCSubtitleStreamEntry *entry, + enum AVStreamParseType need_parsing) +{ + AVStream *st; + FFStream *sti; + int ret; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->id = entry->startcode; + st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codecpar->codec_id = AV_CODEC_ID_DVD_SUBTITLE; + + if ((ret = ff_dvdclut_palette_extradata_cat(entry->clut, FF_DVDCLUT_CLUT_SIZE, st->codecpar)) < 0) + return ret; + + if (entry->lang_iso) + av_dict_set(&st->metadata, "language", entry->lang_iso, 0); + + av_dict_set(&st->metadata, "VIEWPORT", dvdvideo_subp_viewport_labels[entry->viewport], 0); + + st->disposition = entry->disposition; + + sti = ffstream(st); + sti->request_probe = 0; + sti->need_parsing = need_parsing; + + avpriv_set_pts_info(st, DVDVIDEO_PTS_WRAP_BITS, + DVDVIDEO_TIME_BASE_Q.num, DVDVIDEO_TIME_BASE_Q.den); + + return 0; +} + +static int dvdvideo_subp_stream_add_internal(AVFormatContext *s, uint32_t offset, + subp_attr_t subp_attr, + enum DVDVideoSubpictureViewport viewport) +{ + int ret = 0; + DVDVideoPGCSubtitleStreamEntry entry = {0}; + + entry.viewport = viewport; + + if ((ret = dvdvideo_subp_stream_analyze(s, offset, subp_attr, &entry)) < 0) + goto end_error; + + /* IFO structures can declare duplicate entries for the same startcode */ + for (int i = 0; i < s->nb_streams; i++) + if (s->streams[i]->id == entry.startcode) + return 0; + + if ((ret = dvdvideo_subp_stream_add(s, &entry, AVSTREAM_PARSE_HEADERS)) < 0) + goto end_error; + + return 0; + +end_error: + av_log(s, AV_LOG_ERROR, "Unable to add subtitle stream\n"); + return ret; +} + +static int dvdvideo_subp_stream_add_all(AVFormatContext *s) +{ + DVDVideoDemuxContext *c = s->priv_data; + + int nb_streams; + + if (c->opt_menu) + nb_streams = !c->opt_menu_vts ? c->vmg_ifo->vmgi_mat->nr_of_vmgm_subp_streams : + c->vts_ifo->vtsi_mat->nr_of_vtsm_subp_streams; + else + nb_streams = c->vts_ifo->vtsi_mat->nr_of_vts_subp_streams; + + + for (int i = 0; i < nb_streams; i++) { + int ret = 0; + uint32_t subp_control; + subp_attr_t subp_attr; + video_attr_t video_attr; + + subp_control = c->play_state.pgc->subp_control[i]; + if (!(subp_control & 0x80000000)) + continue; + + /* there can be several presentations for one SPU */ + /* the DAR check is flexible in order to support weird authoring */ + if (c->opt_menu) { + video_attr = !c->opt_menu_vts ? c->vmg_ifo->vmgi_mat->vmgm_video_attr : + c->vts_ifo->vtsi_mat->vtsm_video_attr; + + subp_attr = !c->opt_menu_vts ? c->vmg_ifo->vmgi_mat->vmgm_subp_attr : + c->vts_ifo->vtsi_mat->vtsm_subp_attr; + } else { + video_attr = c->vts_ifo->vtsi_mat->vts_video_attr; + subp_attr = c->vts_ifo->vtsi_mat->vts_subp_attr[i]; + } + + /* 4:3 */ + if (!video_attr.display_aspect_ratio) { + if ((ret = dvdvideo_subp_stream_add_internal(s, subp_control >> 24, subp_attr, + DVDVIDEO_SUBP_VIEWPORT_FULLSCREEN)) < 0) + return ret; + + continue; + } + + /* 16:9 */ + if (( ret = dvdvideo_subp_stream_add_internal(s, subp_control >> 16, subp_attr, + DVDVIDEO_SUBP_VIEWPORT_WIDESCREEN)) < 0) + return ret; + + /* 16:9 letterbox */ + if (video_attr.permitted_df == 2 || video_attr.permitted_df == 0) + if ((ret = dvdvideo_subp_stream_add_internal(s, subp_control >> 8, subp_attr, + DVDVIDEO_SUBP_VIEWPORT_LETTERBOX)) < 0) + return ret; + + /* 16:9 pan-and-scan */ + if (video_attr.permitted_df == 1 || video_attr.permitted_df == 0) + if ((ret = dvdvideo_subp_stream_add_internal(s, subp_control, subp_attr, + DVDVIDEO_SUBP_VIEWPORT_PANSCAN)) < 0) + return ret; + } + + return 0; +} + +static void dvdvideo_subdemux_flush(AVFormatContext *s) +{ + DVDVideoDemuxContext *c = s->priv_data; + + if (!c->segment_started) + return; + + av_log(s, AV_LOG_DEBUG, "flushing sub-demuxer\n"); + avio_flush(&c->mpeg_pb.pub); + ff_read_frame_flush(c->mpeg_ctx); + c->segment_started = 0; +} + +static int dvdvideo_subdemux_read_data(void *opaque, uint8_t *buf, int buf_size) +{ + AVFormatContext *s = opaque; + DVDVideoDemuxContext *c = s->priv_data; + + int ret = 0; + int nav_event; + + if (c->play_end) + return AVERROR_EOF; + + if (c->opt_menu) + ret = dvdvideo_menu_next_ps_block(s, &c->play_state, buf, buf_size, + dvdvideo_subdemux_flush); + else + ret = dvdvideo_play_next_ps_block(opaque, &c->play_state, buf, buf_size, + &nav_event, dvdvideo_subdemux_flush); + + if (ret == AVERROR_EOF) { + c->mpeg_pb.pub.eof_reached = 1; + c->play_end = 1; + + return AVERROR_EOF; + } + + if (ret >= 0 && nav_event == DVDNAV_NAV_PACKET) + return FFERROR_REDO; + + return ret; +} + +static void dvdvideo_subdemux_close(AVFormatContext *s) +{ + DVDVideoDemuxContext *c = s->priv_data; + + av_freep(&c->mpeg_pb.pub.buffer); + avformat_close_input(&c->mpeg_ctx); +} + +static int dvdvideo_subdemux_open(AVFormatContext *s) +{ + DVDVideoDemuxContext *c = s->priv_data; + extern const FFInputFormat ff_mpegps_demuxer; + int ret = 0; + + if (!(c->mpeg_buf = av_mallocz(DVDVIDEO_BLOCK_SIZE))) + return AVERROR(ENOMEM); + + ffio_init_context(&c->mpeg_pb, c->mpeg_buf, DVDVIDEO_BLOCK_SIZE, 0, s, + dvdvideo_subdemux_read_data, NULL, NULL); + c->mpeg_pb.pub.seekable = 0; + + if (!(c->mpeg_ctx = avformat_alloc_context())) + return AVERROR(ENOMEM); + + if ((ret = ff_copy_whiteblacklists(c->mpeg_ctx, s)) < 0) { + avformat_free_context(c->mpeg_ctx); + c->mpeg_ctx = NULL; + + return ret; + } + + c->mpeg_ctx->flags = AVFMT_FLAG_CUSTOM_IO | AVFMT_FLAG_GENPTS; + c->mpeg_ctx->ctx_flags |= AVFMTCTX_UNSEEKABLE; + c->mpeg_ctx->probesize = 0; + c->mpeg_ctx->max_analyze_duration = 0; + c->mpeg_ctx->interrupt_callback = s->interrupt_callback; + c->mpeg_ctx->pb = &c->mpeg_pb.pub; + c->mpeg_ctx->correct_ts_overflow = 0; + c->mpeg_ctx->io_open = NULL; + + return avformat_open_input(&c->mpeg_ctx, "", &ff_mpegps_demuxer.p, NULL); +} + +static int dvdvideo_read_header(AVFormatContext *s) +{ + DVDVideoDemuxContext *c = s->priv_data; + + int ret = 0; + + if (c->opt_menu) { + if (c->opt_region || + c->opt_title > 1 || + c->opt_preindex || + c->opt_chapter_start > 1 || + c->opt_chapter_end > 0) { + av_log(s, AV_LOG_ERROR, "-menu is not compatible with the -region, -title, " + "-preindex, or -chapter_start/-chapter_end options\n"); + return AVERROR(EINVAL); + } + + if (!c->opt_pgc) { + av_log(s, AV_LOG_ERROR, "If -menu is enabled, -pgc must be set to a non-zero value\n"); + + return AVERROR(EINVAL); + } + + if (!c->opt_menu_lu) { + av_log(s, AV_LOG_INFO, "Defaulting to menu language unit #1. " + "This is not always desirable, validation suggested.\n"); + + c->opt_menu_lu = 1; + } + + if (!c->opt_pg) { + av_log(s, AV_LOG_INFO, "Defaulting to menu PG #1. " + "This is not always desirable, validation suggested.\n"); + + c->opt_pg = 1; + } + + if ((ret = dvdvideo_ifo_open(s)) < 0 || + (ret = dvdvideo_menu_open(s, &c->play_state)) < 0 || + (ret = dvdvideo_subdemux_open(s)) < 0 || + (ret = dvdvideo_video_stream_setup(s)) < 0 || + (ret = dvdvideo_audio_stream_add_all(s)) < 0) + return ret; + + return 0; + } + + if (c->opt_title == 0) { + av_log(s, AV_LOG_INFO, "Defaulting to title #1. " + "This is not always the main feature, validation suggested.\n"); + + c->opt_title = 1; + } + + if (c->opt_pgc) { + if (c->opt_pg == 0) { + av_log(s, AV_LOG_ERROR, "Invalid coordinates. If -pgc is set, -pg must be set too.\n"); + + return AVERROR(EINVAL); + } else if (c->opt_chapter_start > 1 || c->opt_chapter_end > 0 || c->opt_preindex) { + av_log(s, AV_LOG_ERROR, "-pgc is not compatible with the -preindex or " + "-chapter_start/-chapter_end options\n"); + return AVERROR(EINVAL); + } + } + + if ((ret = dvdvideo_ifo_open(s)) < 0) + return ret; + + if (!c->opt_pgc && c->opt_preindex && (ret = dvdvideo_chapters_setup_preindex(s)) < 0) + return ret; + + if ((ret = dvdvideo_play_open(s, &c->play_state)) < 0 || + (ret = dvdvideo_subdemux_open(s)) < 0 || + (ret = dvdvideo_video_stream_setup(s)) < 0 || + (ret = dvdvideo_audio_stream_add_all(s)) < 0 || + (ret = dvdvideo_subp_stream_add_all(s)) < 0) + return ret; + + if (!c->opt_pgc && !c->opt_preindex) + return dvdvideo_chapters_setup_simple(s); + + return 0; +} + +static int dvdvideo_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + DVDVideoDemuxContext *c = s->priv_data; + + int ret; + enum AVMediaType st_type; + int found_stream = 0; + + if (c->play_end) + return AVERROR_EOF; + + ret = av_read_frame(c->mpeg_ctx, pkt); + + if (ret < 0) + return ret; + + if (!c->segment_started) + c->segment_started = 1; + + st_type = c->mpeg_ctx->streams[pkt->stream_index]->codecpar->codec_type; + + /* map the subdemuxer stream to the parent demuxer's stream (by startcode) */ + for (int i = 0; i < s->nb_streams; i++) { + if (s->streams[i]->id == c->mpeg_ctx->streams[pkt->stream_index]->id) { + pkt->stream_index = s->streams[i]->index; + found_stream = 1; + break; + } + } + + if (!found_stream) { + av_log(s, AV_LOG_DEBUG, "discarding frame with stream that was not in IFO headers " + "(stream id=%d)\n", c->mpeg_ctx->streams[pkt->stream_index]->id); + + return FFERROR_REDO; + } + + if (pkt->pts != AV_NOPTS_VALUE && pkt->dts != AV_NOPTS_VALUE) { + if (!c->play_started) { + /* try to start at the beginning of a GOP */ + if (st_type != AVMEDIA_TYPE_VIDEO || !(pkt->flags & AV_PKT_FLAG_KEY)) { + av_log(s, AV_LOG_VERBOSE, "Discarding packet which is not a video keyframe or " + "with unset PTS/DTS at start\n"); + return FFERROR_REDO; + } + + c->first_pts = pkt->pts; + c->play_started = 1; + } + + pkt->pts += c->play_state.ts_offset - c->first_pts; + pkt->dts += c->play_state.ts_offset - c->first_pts; + + if (pkt->pts < 0) { + av_log(s, AV_LOG_VERBOSE, "Discarding packet with negative PTS (st=%d pts=%" PRId64 "), " + "this is OK at start of playback\n", + pkt->stream_index, pkt->pts); + + return FFERROR_REDO; + } + } else { + av_log(s, AV_LOG_WARNING, "Unset PTS or DTS @ st=%d pts=%" PRId64 " dts=%" PRId64 "\n", + pkt->stream_index, pkt->pts, pkt->dts); + } + + av_log(s, AV_LOG_TRACE, "st=%d pts=%" PRId64 " dts=%" PRId64 " " + "ts_offset=%" PRId64 " first_pts=%" PRId64 "\n", + pkt->stream_index, pkt->pts, pkt->dts, + c->play_state.ts_offset, c->first_pts); + + return c->play_end ? AVERROR_EOF : 0; +} + +static int dvdvideo_close(AVFormatContext *s) +{ + DVDVideoDemuxContext *c = s->priv_data; + + dvdvideo_subdemux_close(s); + + if (c->opt_menu) + dvdvideo_menu_close(s, &c->play_state); + else + dvdvideo_play_close(s, &c->play_state); + + dvdvideo_ifo_close(s); + + return 0; +} + +#define OFFSET(x) offsetof(DVDVideoDemuxContext, x) +static const AVOption dvdvideo_options[] = { + {"angle", "playback angle number", OFFSET(opt_angle), AV_OPT_TYPE_INT, { .i64=1 }, 1, 9, AV_OPT_FLAG_DECODING_PARAM }, + {"chapter_end", "exit chapter (PTT) number (0=end)", OFFSET(opt_chapter_end), AV_OPT_TYPE_INT, { .i64=0 }, 0, 99, AV_OPT_FLAG_DECODING_PARAM }, + {"chapter_start", "entry chapter (PTT) number", OFFSET(opt_chapter_start), AV_OPT_TYPE_INT, { .i64=1 }, 1, 99, AV_OPT_FLAG_DECODING_PARAM }, + {"menu", "demux menu domain", OFFSET(opt_menu), AV_OPT_TYPE_BOOL, { .i64=0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + {"menu_lu", "menu language unit (0=auto)", OFFSET(opt_menu_lu), AV_OPT_TYPE_INT, { .i64=0 }, 0, 99, AV_OPT_FLAG_DECODING_PARAM }, + {"menu_vts", "menu VTS (0=VMG main menu)", OFFSET(opt_menu_vts), AV_OPT_TYPE_INT, { .i64=0 }, 0, 99, AV_OPT_FLAG_DECODING_PARAM }, + {"pg", "entry PG number (0=auto)", OFFSET(opt_pg), AV_OPT_TYPE_INT, { .i64=0 }, 0, 255, AV_OPT_FLAG_DECODING_PARAM }, + {"pgc", "entry PGC number (0=auto)", OFFSET(opt_pgc), AV_OPT_TYPE_INT, { .i64=0 }, 0, 999, AV_OPT_FLAG_DECODING_PARAM }, + {"preindex", "enable for accurate chapter markers, slow (2-pass read)", OFFSET(opt_preindex), AV_OPT_TYPE_BOOL, { .i64=0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + {"region", "playback region number (0=free)", OFFSET(opt_region), AV_OPT_TYPE_INT, { .i64=0 }, 0, 8, AV_OPT_FLAG_DECODING_PARAM }, + {"title", "title number (0=auto)", OFFSET(opt_title), AV_OPT_TYPE_INT, { .i64=0 }, 0, 99, AV_OPT_FLAG_DECODING_PARAM }, + {"trim", "trim padding cells from start", OFFSET(opt_trim), AV_OPT_TYPE_BOOL, { .i64=1 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, + {NULL} +}; + +static const AVClass dvdvideo_class = { + .class_name = "DVD-Video demuxer", + .item_name = av_default_item_name, + .option = dvdvideo_options, + .version = LIBAVUTIL_VERSION_INT +}; + +const FFInputFormat ff_dvdvideo_demuxer = { + .p.name = "dvdvideo", + .p.long_name = NULL_IF_CONFIG_SMALL("DVD-Video"), + .p.priv_class = &dvdvideo_class, + .p.flags = AVFMT_NOFILE | AVFMT_SHOW_IDS | AVFMT_TS_DISCONT | + AVFMT_NO_BYTE_SEEK | AVFMT_NOGENSEARCH | AVFMT_NOBINSEARCH, + .priv_data_size = sizeof(DVDVideoDemuxContext), + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, + .read_close = dvdvideo_close, + .read_header = dvdvideo_read_header, + .read_packet = dvdvideo_read_packet +}; diff --git a/libavformat/dvenc.c b/libavformat/dvenc.c index 29d2dc47acf..0e9a6cfbb1e 100644 --- a/libavformat/dvenc.c +++ b/libavformat/dvenc.c @@ -39,6 +39,7 @@ #include "libavutil/fifo.h" #include "libavutil/mathematics.h" #include "libavutil/intreadwrite.h" +#include "libavutil/pixdesc.h" #include "libavutil/timecode.h" #define MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32-bit audio @@ -308,62 +309,107 @@ static int dv_assemble_frame(AVFormatContext *s, return 0; } -static DVMuxContext* dv_init_mux(AVFormatContext* s) +static int dv_init_mux(AVFormatContext* s) { DVMuxContext *c = s->priv_data; AVStream *vst = NULL; int i; - /* we support at most 1 video and 2 audio streams */ - if (s->nb_streams > 5) - return NULL; + if (s->nb_streams > 5) { + av_log(s, AV_LOG_ERROR, + "Invalid number of streams %d, the muxer supports at most 1 video channel and 4 audio channels.\n", + s->nb_streams); + return AVERROR_INVALIDDATA; + } /* We have to sort out where audio and where video stream is */ for (i=0; inb_streams; i++) { AVStream *st = s->streams[i]; switch (st->codecpar->codec_type) { case AVMEDIA_TYPE_VIDEO: - if (vst) return NULL; - if (st->codecpar->codec_id != AV_CODEC_ID_DVVIDEO) - goto bail_out; + if (vst) { + av_log(s, AV_LOG_ERROR, + "More than one video stream found, only one is accepted.\n"); + return AVERROR_INVALIDDATA; + } + if (st->codecpar->codec_id != AV_CODEC_ID_DVVIDEO) { + av_log(s, AV_LOG_ERROR, + "Invalid codec for video stream, only DVVIDEO is supported.\n"); + return AVERROR_INVALIDDATA; + } vst = st; break; case AVMEDIA_TYPE_AUDIO: - if (c->n_ast > 1) return NULL; + if (c->n_ast > 1) { + av_log(s, AV_LOG_ERROR, + "More than two audio streams found, at most 2 are accepted.\n"); + return AVERROR_INVALIDDATA; + } /* Some checks -- DV format is very picky about its incoming streams */ - if(st->codecpar->codec_id != AV_CODEC_ID_PCM_S16LE || - st->codecpar->ch_layout.nb_channels != 2) - goto bail_out; + if (st->codecpar->codec_id != AV_CODEC_ID_PCM_S16LE) { + av_log(s, AV_LOG_ERROR, + "Invalid codec for stream %d, only PCM_S16LE is supported\n.", i); + return AVERROR_INVALIDDATA; + } + if (st->codecpar->ch_layout.nb_channels != 2) { + av_log(s, AV_LOG_ERROR, + "Invalid number of audio channels %d for stream %d, only 2 channels are supported\n.", + st->codecpar->ch_layout.nb_channels, i); + return AVERROR_INVALIDDATA; + } if (st->codecpar->sample_rate != 48000 && st->codecpar->sample_rate != 44100 && - st->codecpar->sample_rate != 32000 ) - goto bail_out; + st->codecpar->sample_rate != 32000) { + av_log(s, AV_LOG_ERROR, + "Invalid audio sample rate %d for stream %d, only 32000, 44100, and 48000 are supported.\n", + st->codecpar->sample_rate, i); + return AVERROR_INVALIDDATA; + } c->ast[c->n_ast++] = st; break; default: - goto bail_out; + av_log(s, AV_LOG_ERROR, + "Invalid media type for stream %d, only audio and video are supported.\n", i); + return AVERROR_INVALIDDATA; } } - if (!vst) - goto bail_out; + if (!vst) { + av_log(s, AV_LOG_ERROR, + "Missing video stream, must be present\n"); + return AVERROR_INVALIDDATA; + } c->sys = av_dv_codec_profile2(vst->codecpar->width, vst->codecpar->height, vst->codecpar->format, vst->time_base); - if (!c->sys) - goto bail_out; + if (!c->sys) { + av_log(s, AV_LOG_ERROR, + "Could not find a valid video profile for size:%dx%d format:%s tb:%d%d\n", + vst->codecpar->width, vst->codecpar->height, + av_get_pix_fmt_name(vst->codecpar->format), + vst->time_base.num, vst->time_base.den); + return AVERROR_INVALIDDATA; + } if ((c->sys->time_base.den != 25 && c->sys->time_base.den != 50) || c->sys->time_base.num != 1) { - if (c->ast[0] && c->ast[0]->codecpar->sample_rate != 48000) - goto bail_out; - if (c->ast[1] && c->ast[1]->codecpar->sample_rate != 48000) - goto bail_out; + for (i = 0; i < 2; i++) { + if (c->ast[i] && c->ast[i]->codecpar->sample_rate != 48000) { + av_log(s, AV_LOG_ERROR, + "Invalid sample rate %d for audio stream #%d for this video profile, must be 48000.\n", + c->ast[i]->codecpar->sample_rate, i); + return AVERROR_INVALIDDATA; + } + } } - if (((c->n_ast > 1) && (c->sys->n_difchan < 2)) || - ((c->n_ast > 2) && (c->sys->n_difchan < 4))) { - /* only 2 stereo pairs allowed in 50Mbps mode */ - goto bail_out; + for (i = 1; i < 2; i++) { + if (((c->n_ast > i) && (c->sys->n_difchan < (2 * i)))) { + const char *mode = c->n_ast > 1 ? "50Mps" : "25Mps"; + av_log(s, AV_LOG_ERROR, + "Invalid number of channels %d, only %d stereo pairs is allowed in %s mode.\n", + c->n_ast, i, mode); + return AVERROR_INVALIDDATA; + } } /* Ok, everything seems to be in working order */ @@ -376,14 +422,12 @@ static DVMuxContext* dv_init_mux(AVFormatContext* s) if (!c->ast[i]) continue; c->audio_data[i] = av_fifo_alloc2(100 * MAX_AUDIO_FRAME_SIZE, 1, 0); - if (!c->audio_data[i]) - goto bail_out; + if (!c->audio_data[i]) { + return AVERROR(ENOMEM); + } } - return c; - -bail_out: - return NULL; + return 0; } static int dv_write_header(AVFormatContext *s) @@ -392,12 +436,12 @@ static int dv_write_header(AVFormatContext *s) DVMuxContext *dvc = s->priv_data; AVDictionaryEntry *tcr = av_dict_get(s->metadata, "timecode", NULL, 0); - if (!dv_init_mux(s)) { + if (dv_init_mux(s) < 0) { av_log(s, AV_LOG_ERROR, "Can't initialize DV format!\n" - "Make sure that you supply exactly two streams:\n" - " video: 25fps or 29.97fps, audio: 2ch/48|44|32kHz/PCM\n" + "Make sure that you supply at least two streams:\n" + " video: 25fps or 29.97fps, audio: 2ch/48|44.1|32kHz/PCM\n" " (50Mbps allows an optional second audio stream)\n"); - return -1; + return AVERROR_INVALIDDATA; } rate.num = dvc->sys->ltc_divisor; rate.den = 1; diff --git a/libavformat/dxa.c b/libavformat/dxa.c index 474b85270ae..813e665a27a 100644 --- a/libavformat/dxa.c +++ b/libavformat/dxa.c @@ -23,6 +23,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "riff.h" @@ -122,7 +123,7 @@ static int dxa_read_header(AVFormatContext *s) if(ast->codecpar->block_align) { if (c->bpc > INT_MAX - ast->codecpar->block_align + 1) return AVERROR_INVALIDDATA; - c->bpc = ((c->bpc + ast->codecpar->block_align - 1) / ast->codecpar->block_align) * ast->codecpar->block_align; + c->bpc = ((c->bpc - 1 + ast->codecpar->block_align) / ast->codecpar->block_align) * ast->codecpar->block_align; } c->bytes_left = fsize; c->wavpos = avio_tell(pb); @@ -229,9 +230,9 @@ static int dxa_read_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; } -const AVInputFormat ff_dxa_demuxer = { - .name = "dxa", - .long_name = NULL_IF_CONFIG_SMALL("DXA"), +const FFInputFormat ff_dxa_demuxer = { + .p.name = "dxa", + .p.long_name = NULL_IF_CONFIG_SMALL("DXA"), .priv_data_size = sizeof(DXAContext), .read_probe = dxa_probe, .read_header = dxa_read_header, diff --git a/libavformat/eacdata.c b/libavformat/eacdata.c index ebc98d274fc..381f93dc71c 100644 --- a/libavformat/eacdata.c +++ b/libavformat/eacdata.c @@ -30,6 +30,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "libavutil/channel_layout.h" @@ -103,12 +104,12 @@ static int cdata_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVInputFormat ff_ea_cdata_demuxer = { - .name = "ea_cdata", - .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts cdata"), +const FFInputFormat ff_ea_cdata_demuxer = { + .p.name = "ea_cdata", + .p.long_name = NULL_IF_CONFIG_SMALL("Electronic Arts cdata"), + .p.extensions = "cdata", .priv_data_size = sizeof(CdataDemuxContext), .read_probe = cdata_probe, .read_header = cdata_read_header, .read_packet = cdata_read_packet, - .extensions = "cdata", }; diff --git a/libavformat/electronicarts.c b/libavformat/electronicarts.c index e7f574aede8..f7f6fd4cab9 100644 --- a/libavformat/electronicarts.c +++ b/libavformat/electronicarts.c @@ -30,6 +30,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define SCHl_TAG MKTAG('S', 'C', 'H', 'l') @@ -783,12 +784,12 @@ static const AVClass ea_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_ea_demuxer = { - .name = "ea", - .long_name = NULL_IF_CONFIG_SMALL("Electronic Arts Multimedia"), +const FFInputFormat ff_ea_demuxer = { + .p.name = "ea", + .p.long_name = NULL_IF_CONFIG_SMALL("Electronic Arts Multimedia"), + .p.priv_class = &ea_class, .priv_data_size = sizeof(EaDemuxContext), .read_probe = ea_probe, .read_header = ea_read_header, .read_packet = ea_read_packet, - .priv_class = &ea_class, }; diff --git a/libavformat/epafdec.c b/libavformat/epafdec.c index a132360ebfa..f2701b60ca0 100644 --- a/libavformat/epafdec.c +++ b/libavformat/epafdec.c @@ -22,6 +22,7 @@ #include "libavutil/intreadwrite.h" #include "libavcodec/internal.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "pcm.h" @@ -94,13 +95,13 @@ static int epaf_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_epaf_demuxer = { - .name = "epaf", - .long_name = NULL_IF_CONFIG_SMALL("Ensoniq Paris Audio File"), +const FFInputFormat ff_epaf_demuxer = { + .p.name = "epaf", + .p.long_name = NULL_IF_CONFIG_SMALL("Ensoniq Paris Audio File"), + .p.extensions = "paf,fap", + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = epaf_probe, .read_header = epaf_read_header, .read_packet = ff_pcm_read_packet, .read_seek = ff_pcm_read_seek, - .extensions = "paf,fap", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/evc.c b/libavformat/evc.c new file mode 100644 index 00000000000..95f07266b03 --- /dev/null +++ b/libavformat/evc.c @@ -0,0 +1,388 @@ +/* + * EVC helper functions for muxers + * Copyright (c) 2022 Dawid Kozinski + * + * 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/intreadwrite.h" +#include "libavcodec/get_bits.h" +#include "libavcodec/golomb.h" +#include "libavcodec/evc.h" +#include "avformat.h" +#include "avio.h" +#include "evc.h" +#include "avio_internal.h" + +// @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.1 +enum { + SPS_INDEX, + PPS_INDEX, + APS_INDEX, + SEI_INDEX, + NB_ARRAYS +}; + +// @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.3 +typedef struct EVCNALUnitArray { + uint8_t array_completeness; // when equal to 1 indicates that all NAL units of the given type are in the following array + uint8_t NAL_unit_type; // indicates the type of the NAL units in the following array + uint16_t numNalus; // indicates the number of NAL units of the indicated type + uint16_t *nalUnitLength; // indicates the length in bytes of the NAL unit + uint8_t **nalUnit; // contains an SPS, PPS, APS or a SEI NAL unit, as specified in ISO/IEC 23094-1 +} EVCNALUnitArray; + +/** + * @brief Specifies the decoder configuration information for ISO/IEC 23094-1 video content. + * @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.2 + * Carriage of network abstraction layer (NAL) unit structured video in the ISO base media file format + */ +typedef struct EVCDecoderConfigurationRecord { + uint8_t configurationVersion; // 8 bits + uint8_t profile_idc; // 8 bits + uint8_t level_idc; // 8 bits + uint32_t toolset_idc_h; // 32 bits + uint32_t toolset_idc_l; // 32 bits + uint8_t chroma_format_idc; // 2 bits + uint8_t bit_depth_luma_minus8; // 3 bits + uint8_t bit_depth_chroma_minus8; // 3 bits + uint16_t pic_width_in_luma_samples; // 16 bits + uint16_t pic_height_in_luma_samples; // 16 bits + uint8_t reserved; // 6 bits '000000'b + uint8_t lengthSizeMinusOne; // 2 bits + uint8_t num_of_arrays; // 8 bits + EVCNALUnitArray arrays[NB_ARRAYS]; +} EVCDecoderConfigurationRecord; + +typedef struct NALU { + int offset; + uint32_t size; +} NALU; + +typedef struct NALUList { + NALU *nalus; + unsigned nalus_array_size; + unsigned nb_nalus; ///< valid entries in nalus +} NALUList; + +// @see ISO_IEC_23094-1 (7.3.2.1 SPS RBSP syntax) +static int evcc_parse_sps(const uint8_t *bs, int bs_size, EVCDecoderConfigurationRecord *evcc) +{ + GetBitContext gb; + unsigned sps_seq_parameter_set_id; + int ret; + + bs += EVC_NALU_HEADER_SIZE; + bs_size -= EVC_NALU_HEADER_SIZE; + + ret = init_get_bits8(&gb, bs, bs_size); + if (ret < 0) + return ret; + + sps_seq_parameter_set_id = get_ue_golomb_long(&gb); + + if (sps_seq_parameter_set_id >= EVC_MAX_SPS_COUNT) + return AVERROR_INVALIDDATA; + + // the Baseline profile is indicated by profile_idc eqal to 0 + // the Main profile is indicated by profile_idc eqal to 1 + evcc->profile_idc = get_bits(&gb, 8); + + evcc->level_idc = get_bits(&gb, 8); + + evcc->toolset_idc_h = get_bits_long(&gb, 32); + evcc->toolset_idc_l = get_bits_long(&gb, 32); + + // 0 - monochrome + // 1 - 4:2:0 + // 2 - 4:2:2 + // 3 - 4:4:4 + evcc->chroma_format_idc = get_ue_golomb_long(&gb); + if (evcc->chroma_format_idc > 3) + return AVERROR_INVALIDDATA; + + evcc->pic_width_in_luma_samples = get_ue_golomb_long(&gb); + evcc->pic_height_in_luma_samples = get_ue_golomb_long(&gb); + + evcc->bit_depth_luma_minus8 = get_ue_golomb_long(&gb); + evcc->bit_depth_chroma_minus8 = get_ue_golomb_long(&gb); + // EVCDecoderConfigurationRecord can't store values > 7. Limit it to bit depth 14. + if (evcc->bit_depth_luma_minus8 > 6 || evcc->bit_depth_chroma_minus8 > 6) + return AVERROR_INVALIDDATA; + + return 0; +} + +// @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.3 +static int evcc_array_add_nal_unit(const uint8_t *nal_buf, uint32_t nal_size, + uint8_t nal_type, int ps_array_completeness, + EVCNALUnitArray *array) +{ + int ret; + uint16_t numNalus = array->numNalus; + + ret = av_reallocp_array(&array->nalUnit, numNalus + 1, sizeof(uint8_t *)); + if (ret < 0) + return ret; + + ret = av_reallocp_array(&array->nalUnitLength, numNalus + 1, sizeof(uint16_t)); + if (ret < 0) + return ret; + + array->nalUnit [numNalus] = (uint8_t *)nal_buf; + array->nalUnitLength[numNalus] = nal_size; + array->NAL_unit_type = nal_type; + array->numNalus++; + + /* + * When the sample entry name is 'evc1', the default and mandatory value of + * array_completeness is 1 for arrays of all types of parameter sets, and 0 + * for all other arrays. + */ + if (nal_type == EVC_SPS_NUT || nal_type == EVC_PPS_NUT || nal_type == EVC_APS_NUT) + array->array_completeness = ps_array_completeness; + + return 0; +} + +static void evcc_init(EVCDecoderConfigurationRecord *evcc) +{ + memset(evcc, 0, sizeof(EVCDecoderConfigurationRecord)); + evcc->configurationVersion = 1; + evcc->lengthSizeMinusOne = 3; // 4 bytes +} + +static void evcc_close(EVCDecoderConfigurationRecord *evcc) +{ + for (unsigned i = 0; i < FF_ARRAY_ELEMS(evcc->arrays); i++) { + EVCNALUnitArray *const array = &evcc->arrays[i]; + array->numNalus = 0; + av_freep(&array->nalUnit); + av_freep(&array->nalUnitLength); + } +} + +static int evcc_write(AVIOContext *pb, EVCDecoderConfigurationRecord *evcc) +{ + uint16_t sps_count; + + av_log(NULL, AV_LOG_TRACE, "configurationVersion: %"PRIu8"\n", + evcc->configurationVersion); + av_log(NULL, AV_LOG_TRACE, "profile_idc: %"PRIu8"\n", + evcc->profile_idc); + av_log(NULL, AV_LOG_TRACE, "level_idc: %"PRIu8"\n", + evcc->level_idc); + av_log(NULL, AV_LOG_TRACE, "toolset_idc_h: %"PRIu32"\n", + evcc->toolset_idc_h); + av_log(NULL, AV_LOG_TRACE, "toolset_idc_l: %"PRIu32"\n", + evcc->toolset_idc_l); + av_log(NULL, AV_LOG_TRACE, "chroma_format_idc: %"PRIu8"\n", + evcc->chroma_format_idc); + av_log(NULL, AV_LOG_TRACE, "bit_depth_luma_minus8: %"PRIu8"\n", + evcc->bit_depth_luma_minus8); + av_log(NULL, AV_LOG_TRACE, "bit_depth_chroma_minus8: %"PRIu8"\n", + evcc->bit_depth_chroma_minus8); + av_log(NULL, AV_LOG_TRACE, "pic_width_in_luma_samples: %"PRIu16"\n", + evcc->pic_width_in_luma_samples); + av_log(NULL, AV_LOG_TRACE, "pic_height_in_luma_samples: %"PRIu16"\n", + evcc->pic_height_in_luma_samples); + av_log(NULL, AV_LOG_TRACE, "lengthSizeMinusOne: %"PRIu8"\n", + evcc->lengthSizeMinusOne); + av_log(NULL, AV_LOG_TRACE, "num_of_arrays: %"PRIu8"\n", + evcc->num_of_arrays); + for (unsigned i = 0; i < FF_ARRAY_ELEMS(evcc->arrays); i++) { + const EVCNALUnitArray *const array = &evcc->arrays[i]; + + if(array->numNalus == 0) + continue; + + av_log(NULL, AV_LOG_TRACE, "array_completeness[%"PRIu8"]: %"PRIu8"\n", + i, array->array_completeness); + av_log(NULL, AV_LOG_TRACE, "NAL_unit_type[%"PRIu8"]: %"PRIu8"\n", + i, array->NAL_unit_type); + av_log(NULL, AV_LOG_TRACE, "numNalus[%"PRIu8"]: %"PRIu16"\n", + i, array->numNalus); + for ( unsigned j = 0; j < array->numNalus; j++) + av_log(NULL, AV_LOG_TRACE, + "nalUnitLength[%"PRIu8"][%"PRIu16"]: %"PRIu16"\n", + i, j, array->nalUnitLength[j]); + } + + /* + * We need at least one SPS. + */ + sps_count = evcc->arrays[SPS_INDEX].numNalus; + if (!sps_count || sps_count > EVC_MAX_SPS_COUNT) + return AVERROR_INVALIDDATA; + + /* unsigned int(8) configurationVersion = 1; */ + avio_w8(pb, evcc->configurationVersion); + + /* unsigned int(8) profile_idc */ + avio_w8(pb, evcc->profile_idc); + + /* unsigned int(8) profile_idc */ + avio_w8(pb, evcc->level_idc); + + /* unsigned int(32) toolset_idc_h */ + avio_wb32(pb, evcc->toolset_idc_h); + + /* unsigned int(32) toolset_idc_l */ + avio_wb32(pb, evcc->toolset_idc_l); + + /* + * unsigned int(2) chroma_format_idc; + * unsigned int(3) bit_depth_luma_minus8; + * unsigned int(3) bit_depth_chroma_minus8; + */ + avio_w8(pb, evcc->chroma_format_idc << 6 | + evcc->bit_depth_luma_minus8 << 3 | + evcc->bit_depth_chroma_minus8); + + /* unsigned int(16) pic_width_in_luma_samples; */ + avio_wb16(pb, evcc->pic_width_in_luma_samples); + + /* unsigned int(16) pic_width_in_luma_samples; */ + avio_wb16(pb, evcc->pic_height_in_luma_samples); + + /* + * bit(6) reserved = '111111'b; + * unsigned int(2) chromaFormat; + */ + avio_w8(pb, evcc->lengthSizeMinusOne | 0xfc); + + /* unsigned int(8) numOfArrays; */ + avio_w8(pb, evcc->num_of_arrays); + + for (unsigned i = 0; i < FF_ARRAY_ELEMS(evcc->arrays); i++) { + const EVCNALUnitArray *const array = &evcc->arrays[i]; + + if (!array->numNalus) + continue; + + /* + * bit(1) array_completeness; + * unsigned int(1) reserved = 0; + * unsigned int(6) NAL_unit_type; + */ + avio_w8(pb, array->array_completeness << 7 | + array->NAL_unit_type & 0x3f); + + /* unsigned int(16) numNalus; */ + avio_wb16(pb, array->numNalus); + + for (unsigned j = 0; j < array->numNalus; j++) { + /* unsigned int(16) nalUnitLength; */ + avio_wb16(pb, array->nalUnitLength[j]); + + /* bit(8*nalUnitLength) nalUnit; */ + avio_write(pb, array->nalUnit[j], + array->nalUnitLength[j]); + } + } + + return 0; +} + +int ff_isom_write_evcc(AVIOContext *pb, const uint8_t *data, + int size, int ps_array_completeness) +{ + EVCDecoderConfigurationRecord evcc; + int nalu_type; + size_t nalu_size; + int bytes_to_read = size; + unsigned array_index; + + int ret = 0; + + if (size < 8) { + /* We can't write a valid evcC from the provided data */ + return AVERROR_INVALIDDATA; + } else if (*data == 1) { + /* Data is already evcC-formatted */ + avio_write(pb, data, size); + return 0; + } + + evcc_init(&evcc); + + while (bytes_to_read > EVC_NALU_LENGTH_PREFIX_SIZE) { + nalu_size = evc_read_nal_unit_length(data, EVC_NALU_LENGTH_PREFIX_SIZE); + if (nalu_size == 0) break; + + data += EVC_NALU_LENGTH_PREFIX_SIZE; + bytes_to_read -= EVC_NALU_LENGTH_PREFIX_SIZE; + + if (bytes_to_read < nalu_size) break; + + nalu_type = evc_get_nalu_type(data, bytes_to_read); + if (nalu_type < EVC_NOIDR_NUT || nalu_type > EVC_UNSPEC_NUT62) { + ret = AVERROR_INVALIDDATA; + goto end; + } + + // @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.3 + // NAL_unit_type indicates the type of the NAL units in the following array (which shall be all of that type); + // - it takes a value as defined in ISO/IEC 23094-1; + // - it is restricted to take one of the values indicating a SPS, PPS, APS, or SEI NAL unit. + switch (nalu_type) { + case EVC_SPS_NUT: + array_index = SPS_INDEX; + break; + case EVC_PPS_NUT: + array_index = PPS_INDEX; + break; + case EVC_APS_NUT: + array_index = APS_INDEX; + break; + case EVC_SEI_NUT: + array_index = SEI_INDEX; + break; + default: + array_index = -1; + break; + } + + if( (array_index == SPS_INDEX) || + (array_index == PPS_INDEX) || + (array_index == APS_INDEX) || + (array_index == SEI_INDEX) ) { + + ret = evcc_array_add_nal_unit(data, nalu_size, nalu_type, ps_array_completeness, &(evcc.arrays[array_index])); + + if (ret < 0) + goto end; + if (evcc.arrays[array_index].numNalus == 1) + evcc.num_of_arrays++; + + if(nalu_type == EVC_SPS_NUT) { + ret = evcc_parse_sps(data, nalu_size, &evcc); + if (ret < 0) + goto end; + } + } + + data += nalu_size; + bytes_to_read -= nalu_size; + } + + ret = evcc_write(pb, &evcc); + +end: + evcc_close(&evcc); + return ret; +} diff --git a/libavformat/evc.h b/libavformat/evc.h new file mode 100644 index 00000000000..f30831257d1 --- /dev/null +++ b/libavformat/evc.h @@ -0,0 +1,69 @@ +/* + * EVC helper functions for muxers + * Copyright (c) 2022 Dawid Kozinski + * + * 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 + */ + +#ifndef AVFORMAT_EVC_H +#define AVFORMAT_EVC_H + +#include + +#include "libavutil/intreadwrite.h" +#include "libavutil/rational.h" +#include "libavcodec/evc.h" +#include "avio.h" + +static inline int evc_get_nalu_type(const uint8_t *p, int bits_size) +{ + int unit_type_plus1 = 0; + + if (bits_size >= EVC_NALU_HEADER_SIZE) { + // forbidden_zero_bit + if ((p[0] & 0x80) != 0) // Cannot get bitstream information. Malformed bitstream. + return -1; + + // nal_unit_type + unit_type_plus1 = (p[0] >> 1) & 0x3F; + } + + return unit_type_plus1 - 1; +} + +static inline uint32_t evc_read_nal_unit_length(const uint8_t *bits, int bits_size) +{ + if (bits_size >= EVC_NALU_LENGTH_PREFIX_SIZE) + return AV_RB32(bits); + + return 0; +} + +/** + * Writes EVC sample metadata to the provided AVIOContext. + * + * @param pb pointer to the AVIOContext where the evc sample metadata shall be written + * @param buf input data buffer + * @param size size in bytes of the input data buffer + * @param ps_array_completeness @see ISO/IEC 14496-15:2021 Coding of audio-visual objects - Part 15: section 12.3.3.3 + * + * @return 0 in case of success, a negative error code in case of failure + */ +int ff_isom_write_evcc(AVIOContext *pb, const uint8_t *data, + int size, int ps_array_completeness); + +#endif // AVFORMAT_EVC_H diff --git a/libavformat/evcdec.c b/libavformat/evcdec.c new file mode 100644 index 00000000000..9e735fab55b --- /dev/null +++ b/libavformat/evcdec.c @@ -0,0 +1,218 @@ +/* + * RAW EVC video demuxer + * + * Copyright (c) 2021 Dawid Kozinski + * + * 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 "libavcodec/evc.h" +#include "libavcodec/bsf.h" + +#include "libavutil/opt.h" + +#include "avformat.h" +#include "avio_internal.h" +#include "demux.h" +#include "evc.h" +#include "internal.h" + + +#define RAW_PACKET_SIZE 1024 + +typedef struct EVCDemuxContext { + const AVClass *class; + AVRational framerate; + + AVBSFContext *bsf; + +} EVCDemuxContext; + +#define DEC AV_OPT_FLAG_DECODING_PARAM +#define OFFSET(x) offsetof(EVCDemuxContext, x) +static const AVOption evc_options[] = { + { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, {.str = "25"}, 0, INT_MAX, DEC}, + { NULL }, +}; +#undef OFFSET + +static const AVClass evc_demuxer_class = { + .class_name = "EVC Annex B demuxer", + .item_name = av_default_item_name, + .option = evc_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +static int annexb_probe(const AVProbeData *p) +{ + int nalu_type; + size_t nalu_size; + int got_sps = 0, got_pps = 0, got_idr = 0, got_nonidr = 0; + const unsigned char *bits = p->buf; + int bytes_to_read = p->buf_size; + + while (bytes_to_read > EVC_NALU_LENGTH_PREFIX_SIZE) { + + nalu_size = evc_read_nal_unit_length(bits, EVC_NALU_LENGTH_PREFIX_SIZE); + if (nalu_size == 0) break; + + bits += EVC_NALU_LENGTH_PREFIX_SIZE; + bytes_to_read -= EVC_NALU_LENGTH_PREFIX_SIZE; + + if(bytes_to_read < nalu_size) break; + + nalu_type = evc_get_nalu_type(bits, bytes_to_read); + + if (nalu_type == EVC_SPS_NUT) + got_sps++; + else if (nalu_type == EVC_PPS_NUT) + got_pps++; + else if (nalu_type == EVC_IDR_NUT ) + got_idr++; + else if (nalu_type == EVC_NOIDR_NUT) + got_nonidr++; + + bits += nalu_size; + bytes_to_read -= nalu_size; + } + + if (got_sps && got_pps && (got_idr || got_nonidr > 3)) + return AVPROBE_SCORE_EXTENSION + 1; // 1 more than .mpg + + return 0; +} + +static int evc_read_header(AVFormatContext *s) +{ + AVStream *st; + FFStream *sti; + const AVBitStreamFilter *filter = av_bsf_get_by_name("evc_frame_merge"); + EVCDemuxContext *c = s->priv_data; + int ret = 0; + + st = avformat_new_stream(s, NULL); + if (!st) { + ret = AVERROR(ENOMEM); + goto fail; + } + sti = ffstream(st); + + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + st->codecpar->codec_id = AV_CODEC_ID_EVC; + + // This causes sending to the parser full frames, not chunks of data + // The flag PARSER_FLAG_COMPLETE_FRAMES will be set in demux.c (demux.c: 1316) + sti->need_parsing = AVSTREAM_PARSE_HEADERS; + + st->avg_frame_rate = c->framerate; + + // taken from rawvideo demuxers + avpriv_set_pts_info(st, 64, 1, 1200000); + + ret = av_bsf_alloc(filter, &c->bsf); + if (ret < 0) + return ret; + + ret = avcodec_parameters_copy(c->bsf->par_in, st->codecpar); + if (ret < 0) + return ret; + + ret = av_bsf_init(c->bsf); + if (ret < 0) + return ret; + +fail: + return ret; +} + +static int evc_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret; + uint32_t nalu_size; + int au_end_found = 0; + EVCDemuxContext *const c = s->priv_data; + + while(!au_end_found) { + uint8_t buf[EVC_NALU_LENGTH_PREFIX_SIZE]; + + if (avio_feof(s->pb)) + goto end; + + ret = ffio_ensure_seekback(s->pb, EVC_NALU_LENGTH_PREFIX_SIZE); + if (ret < 0) + return ret; + + ret = avio_read(s->pb, buf, EVC_NALU_LENGTH_PREFIX_SIZE); + if (ret < 0) + return ret; + if (ret != EVC_NALU_LENGTH_PREFIX_SIZE) + return AVERROR_INVALIDDATA; + + nalu_size = evc_read_nal_unit_length(buf, EVC_NALU_LENGTH_PREFIX_SIZE); + if (!nalu_size || nalu_size > INT_MAX) + return AVERROR_INVALIDDATA; + + avio_seek(s->pb, -EVC_NALU_LENGTH_PREFIX_SIZE, SEEK_CUR); + + ret = av_get_packet(s->pb, pkt, nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE); + if (ret < 0) + return ret; + if (ret != (nalu_size + EVC_NALU_LENGTH_PREFIX_SIZE)) + return AVERROR_INVALIDDATA; + +end: + ret = av_bsf_send_packet(c->bsf, pkt); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Failed to send packet to " + "evc_frame_merge filter\n"); + return ret; + } + + ret = av_bsf_receive_packet(c->bsf, pkt); + if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) + av_log(s, AV_LOG_ERROR, "evc_frame_merge filter failed to " + "send output packet\n"); + + if (ret != AVERROR(EAGAIN)) + au_end_found = 1; + } + + return ret; +} + +static int evc_read_close(AVFormatContext *s) +{ + EVCDemuxContext *const c = s->priv_data; + + av_bsf_free(&c->bsf); + return 0; +} + +const FFInputFormat ff_evc_demuxer = { + .p.name = "evc", + .p.long_name = NULL_IF_CONFIG_SMALL("EVC Annex B"), + .p.extensions = "evc", + .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS, + .p.priv_class = &evc_demuxer_class, + .read_probe = annexb_probe, + .read_header = evc_read_header, // annexb_read_header + .read_packet = evc_read_packet, // annexb_read_packet + .read_close = evc_read_close, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, + .raw_codec_id = AV_CODEC_ID_EVC, + .priv_data_size = sizeof(EVCDemuxContext), +}; diff --git a/libavcodec/tests/fft-fixed32.c b/libavformat/ffjni.c similarity index 85% rename from libavcodec/tests/fft-fixed32.c rename to libavformat/ffjni.c index 3c50bf1dc19..2b1483cf42c 100644 --- a/libavcodec/tests/fft-fixed32.c +++ b/libavformat/ffjni.c @@ -1,4 +1,8 @@ /* + * JNI utility functions - included stub + * + * Copyright (c) 2024 Leo Izen + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -16,6 +20,4 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#define FFT_FLOAT 0 -#define AVFFT 0 -#include "fft.c" +#include "libavcodec/ffjni.c" diff --git a/libavformat/ffmetadec.c b/libavformat/ffmetadec.c index 90f2e2b861d..ab62b7006e0 100644 --- a/libavformat/ffmetadec.c +++ b/libavformat/ffmetadec.c @@ -222,9 +222,9 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; } -const AVInputFormat ff_ffmetadata_demuxer = { - .name = "ffmetadata", - .long_name = NULL_IF_CONFIG_SMALL("FFmpeg metadata in text"), +const FFInputFormat ff_ffmetadata_demuxer = { + .p.name = "ffmetadata", + .p.long_name = NULL_IF_CONFIG_SMALL("FFmpeg metadata in text"), .read_probe = probe, .read_header = read_header, .read_packet = read_packet, diff --git a/libavformat/fifo.c b/libavformat/fifo.c index 9a3a23729c9..23e4149ad60 100644 --- a/libavformat/fifo.c +++ b/libavformat/fifo.c @@ -90,7 +90,7 @@ typedef struct FifoThreadContext { /* Timestamp of last failure. * This is either pts in case stream time is used, - * or microseconds as returned by av_getttime_relative() */ + * or microseconds as returned by av_gettime_relative() */ int64_t last_recovery_ts; /* Number of current recovery process @@ -501,11 +501,6 @@ static int fifo_mux_init(AVFormatContext *avf, const AVOutputFormat *oformat, if (ret < 0) return ret; avf2->opaque = avf->opaque; -#if FF_API_AVFORMAT_IO_CLOSE -FF_DISABLE_DEPRECATION_WARNINGS - avf2->io_close = avf->io_close; -FF_ENABLE_DEPRECATION_WARNINGS -#endif avf2->io_close2 = avf->io_close2; avf2->io_open = avf->io_open; avf2->flags = avf->flags; @@ -533,6 +528,13 @@ static int fifo_init(AVFormatContext *avf) atomic_init(&fifo->queue_duration, 0); fifo->last_sent_dts = AV_NOPTS_VALUE; +#ifdef FIFO_TEST + /* This exists for the fifo_muxer test tool. */ + if (fifo->format && !strcmp(fifo->format, "fifo_test")) { + extern const FFOutputFormat ff_fifo_test_muxer; + oformat = &ff_fifo_test_muxer.p; + } else +#endif oformat = av_guess_format(fifo->format, avf->url, NULL); if (!oformat) { ret = AVERROR_MUXER_NOT_FOUND; @@ -668,36 +670,36 @@ static void fifo_deinit(AVFormatContext *avf) #define OFFSET(x) offsetof(FifoContext, x) static const AVOption options[] = { - {"fifo_format", "Target muxer", OFFSET(format), - AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM}, - - {"queue_size", "Size of fifo queue", OFFSET(queue_size), - AV_OPT_TYPE_INT, {.i64 = FIFO_DEFAULT_QUEUE_SIZE}, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, - - {"format_opts", "Options to be passed to underlying muxer", OFFSET(format_options), - AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM}, + {"attempt_recovery", "Attempt recovery in case of failure", OFFSET(attempt_recovery), + AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, {"drop_pkts_on_overflow", "Drop packets on fifo queue overflow not to block encoder", OFFSET(drop_pkts_on_overflow), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, - {"restart_with_keyframe", "Wait for keyframe when restarting output", OFFSET(restart_with_keyframe), - AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, + {"fifo_format", "Target muxer", OFFSET(format), + AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM}, - {"attempt_recovery", "Attempt recovery in case of failure", OFFSET(attempt_recovery), - AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, + {"format_opts", "Options to be passed to underlying muxer", OFFSET(format_options), + AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM}, {"max_recovery_attempts", "Maximal number of recovery attempts", OFFSET(max_recovery_attempts), AV_OPT_TYPE_INT, {.i64 = FIFO_DEFAULT_MAX_RECOVERY_ATTEMPTS}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, - {"recovery_wait_time", "Waiting time between recovery attempts", OFFSET(recovery_wait_time), - AV_OPT_TYPE_DURATION, {.i64 = FIFO_DEFAULT_RECOVERY_WAIT_TIME_USEC}, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + {"queue_size", "Size of fifo queue", OFFSET(queue_size), + AV_OPT_TYPE_INT, {.i64 = FIFO_DEFAULT_QUEUE_SIZE}, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, {"recovery_wait_streamtime", "Use stream time instead of real time while waiting for recovery", OFFSET(recovery_wait_streamtime), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, + {"recovery_wait_time", "Waiting time between recovery attempts", OFFSET(recovery_wait_time), + AV_OPT_TYPE_DURATION, {.i64 = FIFO_DEFAULT_RECOVERY_WAIT_TIME_USEC}, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + {"recover_any_error", "Attempt recovery regardless of type of the error", OFFSET(recover_any_error), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, + {"restart_with_keyframe", "Wait for keyframe when restarting output", OFFSET(restart_with_keyframe), + AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, + {"timeshift", "Delay fifo output", OFFSET(timeshift), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM}, @@ -715,11 +717,16 @@ const FFOutputFormat ff_fifo_muxer = { .p.name = "fifo", .p.long_name = NULL_IF_CONFIG_SMALL("FIFO queue pseudo-muxer"), .p.priv_class = &fifo_muxer_class, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, +#else + .p.flags = AVFMT_NOFILE | AVFMT_TS_NEGATIVE, +#endif .priv_data_size = sizeof(FifoContext), .init = fifo_init, .write_header = fifo_write_header, .write_packet = fifo_write_packet, .write_trailer = fifo_write_trailer, .deinit = fifo_deinit, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; diff --git a/libavformat/fifo_test.c b/libavformat/fifo_test.c deleted file mode 100644 index 0f12d88b0fd..00000000000 --- a/libavformat/fifo_test.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * FIFO test pseudo-muxer - * Copyright (c) 2016 Jan Sebechlebsky - * - * 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 - -#include "libavutil/opt.h" -#include "libavutil/time.h" - -#include "avformat.h" -#include "mux.h" -#include "url.h" - -/* Implementation of mock muxer to simulate real muxer failures */ - -#define MAX_TST_PACKETS 128 -#define SLEEPTIME_50_MS 50000 -#define SLEEPTIME_10_MS 10000 - -/* Implementation of mock muxer to simulate real muxer failures */ - -/* This is structure of data sent in packets to - * failing muxer */ -typedef struct FailingMuxerPacketData { - int ret; /* return value of write_packet call*/ - int recover_after; /* set ret to zero after this number of recovery attempts */ - unsigned sleep_time; /* sleep for this long in write_packet to simulate long I/O operation */ -} FailingMuxerPacketData; - - -typedef struct FailingMuxerContext { - AVClass *class; - int write_header_ret; - int write_trailer_ret; - /* If non-zero, summary of processed packets will be printed in deinit */ - int print_deinit_summary; - - int flush_count; - int pts_written[MAX_TST_PACKETS]; - int pts_written_nr; -} FailingMuxerContext; - -static int failing_write_header(AVFormatContext *avf) -{ - FailingMuxerContext *ctx = avf->priv_data; - return ctx->write_header_ret; -} - -static int failing_write_packet(AVFormatContext *avf, AVPacket *pkt) -{ - FailingMuxerContext *ctx = avf->priv_data; - int ret = 0; - if (!pkt) { - ctx->flush_count++; - } else { - FailingMuxerPacketData *data = (FailingMuxerPacketData*) pkt->data; - - if (!data->recover_after) { - data->ret = 0; - } else { - data->recover_after--; - } - - ret = data->ret; - - if (data->sleep_time) { - int64_t slept = 0; - while (slept < data->sleep_time) { - if (ff_check_interrupt(&avf->interrupt_callback)) - return AVERROR_EXIT; - av_usleep(SLEEPTIME_10_MS); - slept += SLEEPTIME_10_MS; - } - } - - if (!ret) { - ctx->pts_written[ctx->pts_written_nr++] = pkt->pts; - av_packet_unref(pkt); - } - } - return ret; -} - -static int failing_write_trailer(AVFormatContext *avf) -{ - FailingMuxerContext *ctx = avf->priv_data; - return ctx->write_trailer_ret; -} - -static void failing_deinit(AVFormatContext *avf) -{ - int i; - FailingMuxerContext *ctx = avf->priv_data; - - if (!ctx->print_deinit_summary) - return; - - printf("flush count: %d\n", ctx->flush_count); - printf("pts seen nr: %d\n", ctx->pts_written_nr); - printf("pts seen: "); - for (i = 0; i < ctx->pts_written_nr; ++i ) { - printf(i ? ",%d" : "%d", ctx->pts_written[i]); - } - printf("\n"); -} -#define OFFSET(x) offsetof(FailingMuxerContext, x) -static const AVOption options[] = { - {"write_header_ret", "write_header() return value", OFFSET(write_header_ret), - AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, - {"write_trailer_ret", "write_trailer() return value", OFFSET(write_trailer_ret), - AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, - {"print_deinit_summary", "print summary when deinitializing muxer", OFFSET(print_deinit_summary), - AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, - {NULL} - }; - -static const AVClass failing_muxer_class = { - .class_name = "Fifo test muxer", - .item_name = av_default_item_name, - .option = options, - .version = LIBAVUTIL_VERSION_INT, -}; - -const FFOutputFormat ff_fifo_test_muxer = { - .p.name = "fifo_test", - .p.long_name = NULL_IF_CONFIG_SMALL("Fifo test muxer"), - .priv_data_size = sizeof(FailingMuxerContext), - .write_header = failing_write_header, - .write_packet = failing_write_packet, - .write_trailer = failing_write_trailer, - .deinit = failing_deinit, - .p.priv_class = &failing_muxer_class, - .p.flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, -}; - diff --git a/libavformat/file.c b/libavformat/file.c index cbdf48de0ac..1f853e0e178 100644 --- a/libavformat/file.c +++ b/libavformat/file.c @@ -98,6 +98,7 @@ typedef struct FileContext { #if HAVE_DIRENT_H DIR *dir; #endif + int64_t initial_pos; } FileContext; static const AVOption file_options[] = { @@ -218,7 +219,12 @@ static int fd_dup(URLContext *h, int oldfd) static int file_close(URLContext *h) { FileContext *c = h->priv_data; - int ret = close(c->fd); + int ret; + + if (c->initial_pos >= 0 && !h->is_streamed) + lseek(c->fd, c->initial_pos, SEEK_SET); + + ret = close(c->fd); return (ret == -1) ? AVERROR(errno) : 0; } @@ -286,6 +292,7 @@ static int file_open(URLContext *h, const char *filename, int flags) av_strstart(filename, "file:", &filename); + c->initial_pos = -1; if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) { access = O_CREAT | O_RDWR; if (c->trunc) @@ -494,6 +501,11 @@ static int fd_open(URLContext *h, const char *filename, int flags) if (c->fd == -1) return AVERROR(errno); + if (h->is_streamed) + c->initial_pos = -1; + else + c->initial_pos = lseek(c->fd, 0, SEEK_CUR); + return 0; } @@ -512,3 +524,157 @@ const URLProtocol ff_fd_protocol = { }; #endif /* CONFIG_FD_PROTOCOL */ + +#if CONFIG_ANDROID_CONTENT_PROTOCOL +#include +#include "libavcodec/ffjni.h" +#include "libavcodec/jni.h" + +typedef struct JFields { + jclass uri_class; + jmethodID parse_id; + + jclass context_class; + jmethodID get_content_resolver_id; + + jclass content_resolver_class; + jmethodID open_file_descriptor_id; + + jclass parcel_file_descriptor_class; + jmethodID detach_fd_id; +} JFields; + +#define OFFSET(x) offsetof(JFields, x) +static const struct FFJniField jfields_mapping[] = { + { "android/net/Uri", NULL, NULL, FF_JNI_CLASS, OFFSET(uri_class), 1 }, + { "android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", FF_JNI_STATIC_METHOD, OFFSET(parse_id), 1 }, + + { "android/content/Context", NULL, NULL, FF_JNI_CLASS, OFFSET(context_class), 1 }, + { "android/content/Context", "getContentResolver", "()Landroid/content/ContentResolver;", FF_JNI_METHOD, OFFSET(get_content_resolver_id), 1 }, + + { "android/content/ContentResolver", NULL, NULL, FF_JNI_CLASS, OFFSET(content_resolver_class), 1 }, + { "android/content/ContentResolver", "openFileDescriptor", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", FF_JNI_METHOD, OFFSET(open_file_descriptor_id), 1 }, + + { "android/os/ParcelFileDescriptor", NULL, NULL, FF_JNI_CLASS, OFFSET(parcel_file_descriptor_class), 1 }, + { "android/os/ParcelFileDescriptor", "detachFd", "()I", FF_JNI_METHOD, OFFSET(detach_fd_id), 1 }, + + { NULL } +}; +#undef OFFSET + +static int android_content_open(URLContext *h, const char *filename, int flags) +{ + FileContext *c = h->priv_data; + int fd, ret; + struct stat st; + const char *mode_str = "r"; + + JNIEnv *env; + JFields jfields = { 0 }; + jobject application_context = NULL; + jobject url = NULL; + jobject mode = NULL; + jobject uri = NULL; + jobject content_resolver = NULL; + jobject parcel_file_descriptor = NULL; + + env = ff_jni_get_env(c); + if (!env) { + return AVERROR(EINVAL); + } + + ret = ff_jni_init_jfields(env, &jfields, jfields_mapping, 0, c); + if (ret < 0) { + av_log(c, AV_LOG_ERROR, "failed to initialize jni fields\n"); + return ret; + } + + application_context = av_jni_get_android_app_ctx(); + if (!application_context) { + av_log(c, AV_LOG_ERROR, "application context is not set\n"); + ret = AVERROR_EXTERNAL; + goto done; + } + + url = ff_jni_utf_chars_to_jstring(env, filename, c); + if (!url) { + ret = AVERROR_EXTERNAL; + goto done; + } + + if (flags & AVIO_FLAG_WRITE && flags & AVIO_FLAG_READ) + mode_str = "rw"; + else if (flags & AVIO_FLAG_WRITE) + mode_str = "w"; + + mode = ff_jni_utf_chars_to_jstring(env, mode_str, c); + if (!mode) { + ret = AVERROR_EXTERNAL; + goto done; + } + + uri = (*env)->CallStaticObjectMethod(env, jfields.uri_class, jfields.parse_id, url); + ret = ff_jni_exception_check(env, 1, c); + if (ret < 0) + goto done; + + content_resolver = (*env)->CallObjectMethod(env, application_context, jfields.get_content_resolver_id); + ret = ff_jni_exception_check(env, 1, c); + if (ret < 0) + goto done; + + parcel_file_descriptor = (*env)->CallObjectMethod(env, content_resolver, jfields.open_file_descriptor_id, uri, mode); + ret = ff_jni_exception_check(env, 1, c); + if (ret < 0) + goto done; + + fd = (*env)->CallIntMethod(env, parcel_file_descriptor, jfields.detach_fd_id); + ret = ff_jni_exception_check(env, 1, c); + if (ret < 0) + goto done; + + if (fstat(fd, &st) < 0) { + close(fd); + return AVERROR(errno); + } + + c->fd = fd; + h->is_streamed = !(S_ISREG(st.st_mode) || S_ISBLK(st.st_mode)); + +done: + (*env)->DeleteLocalRef(env, url); + (*env)->DeleteLocalRef(env, mode); + (*env)->DeleteLocalRef(env, uri); + (*env)->DeleteLocalRef(env, content_resolver); + (*env)->DeleteLocalRef(env, parcel_file_descriptor); + ff_jni_reset_jfields(env, &jfields, jfields_mapping, 0, c); + + return ret; +} + +static const AVOption android_content_options[] = { + { "blocksize", "set I/O operation maximum block size", offsetof(FileContext, blocksize), AV_OPT_TYPE_INT, { .i64 = INT_MAX }, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { NULL } +}; + +static const AVClass android_content_class = { + .class_name = "android_content", + .item_name = av_default_item_name, + .option = android_content_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const URLProtocol ff_android_content_protocol = { + .name = "content", + .url_open = android_content_open, + .url_read = file_read, + .url_write = file_write, + .url_seek = file_seek, + .url_close = file_close, + .url_get_file_handle = file_get_handle, + .url_check = NULL, + .priv_data_size = sizeof(FileContext), + .priv_data_class = &android_content_class, +}; + +#endif /* CONFIG_ANDROID_CONTENT_PROTOCOL */ diff --git a/libavformat/filmstripdec.c b/libavformat/filmstripdec.c index 2b6ba63fcf0..5ce0af234ca 100644 --- a/libavformat/filmstripdec.c +++ b/libavformat/filmstripdec.c @@ -27,6 +27,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/imgutils.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define RAND_TAG MKBETAG('R','a','n','d') @@ -86,7 +87,7 @@ static int read_packet(AVFormatContext *s, AVStream *st = s->streams[0]; if (avio_feof(s->pb)) - return AVERROR(EIO); + return AVERROR_EOF; pkt->dts = avio_tell(s->pb) / (st->codecpar->width * (int64_t)(st->codecpar->height + film->leading) * 4); pkt->size = av_get_packet(s->pb, pkt, st->codecpar->width * st->codecpar->height * 4); avio_skip(s->pb, st->codecpar->width * (int64_t) film->leading * 4); @@ -104,12 +105,12 @@ static int read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, in return 0; } -const AVInputFormat ff_filmstrip_demuxer = { - .name = "filmstrip", - .long_name = NULL_IF_CONFIG_SMALL("Adobe Filmstrip"), +const FFInputFormat ff_filmstrip_demuxer = { + .p.name = "filmstrip", + .p.long_name = NULL_IF_CONFIG_SMALL("Adobe Filmstrip"), + .p.extensions = "flm", .priv_data_size = sizeof(FilmstripDemuxContext), .read_header = read_header, .read_packet = read_packet, .read_seek = read_seek, - .extensions = "flm", }; diff --git a/libavformat/filmstripenc.c b/libavformat/filmstripenc.c index 9033dba6925..b5d9179ff38 100644 --- a/libavformat/filmstripenc.c +++ b/libavformat/filmstripenc.c @@ -32,7 +32,7 @@ #define RAND_TAG MKBETAG('R','a','n','d') -static int write_header(AVFormatContext *s) +static av_cold int init(AVFormatContext *s) { if (s->streams[0]->codecpar->format != AV_PIX_FMT_RGBA) { av_log(s, AV_LOG_ERROR, "only AV_PIX_FMT_RGBA is supported\n"); @@ -66,7 +66,10 @@ const FFOutputFormat ff_filmstrip_muxer = { .p.extensions = "flm", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_RAWVIDEO, - .write_header = write_header, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, + .init = init, .write_packet = ff_raw_write_packet, .write_trailer = write_trailer, }; diff --git a/libavformat/fitsdec.c b/libavformat/fitsdec.c index 54412c60ff8..6771dda327c 100644 --- a/libavformat/fitsdec.c +++ b/libavformat/fitsdec.c @@ -24,12 +24,10 @@ * FITS demuxer. */ -#include "libavutil/avassert.h" -#include "libavutil/intreadwrite.h" +#include "demux.h" #include "internal.h" #include "libavutil/opt.h" #include "libavcodec/fits.h" -#include "libavutil/bprint.h" #define FITS_BLOCK_SIZE 2880 @@ -37,7 +35,6 @@ typedef struct FITSContext { const AVClass *class; AVRational framerate; int first_image; - int64_t pts; } FITSContext; static int fits_probe(const AVProbeData *p) @@ -61,7 +58,6 @@ static int fits_read_header(AVFormatContext *s) st->codecpar->codec_id = AV_CODEC_ID_FITS; avpriv_set_pts_info(st, 64, fits->framerate.den, fits->framerate.num); - fits->pts = 0; fits->first_image = 1; return 0; } @@ -72,31 +68,31 @@ static int fits_read_header(AVFormatContext *s) * @param s pointer to AVFormat Context * @param fits pointer to FITSContext * @param header pointer to FITSHeader - * @param avbuf pointer to AVBPrint to store the header + * @param pkt pointer to AVPacket to store the header * @param data_size to store the size of data part - * @return 1 if image found, 0 if any other extension and AVERROR_INVALIDDATA otherwise + * @return 1 if image found, 0 if any other extension and AVERROR code otherwise */ -static int64_t is_image(AVFormatContext *s, FITSContext *fits, FITSHeader *header, - AVBPrint *avbuf, uint64_t *data_size) +static int is_image(AVFormatContext *s, FITSContext *fits, FITSHeader *header, + AVPacket *pkt, uint64_t *data_size) { int i, ret, image = 0; - char buf[FITS_BLOCK_SIZE] = { 0 }; - int64_t buf_size = 0, size = 0, t; + int64_t size = 0, t; do { - ret = avio_read(s->pb, buf, FITS_BLOCK_SIZE); + const uint8_t *buf, *buf_end; + ret = av_append_packet(s->pb, pkt, FITS_BLOCK_SIZE); if (ret < 0) { return ret; } else if (ret < FITS_BLOCK_SIZE) { return AVERROR_INVALIDDATA; } - av_bprint_append_data(avbuf, buf, FITS_BLOCK_SIZE); ret = 0; - buf_size = 0; - while(!ret && buf_size < FITS_BLOCK_SIZE) { - ret = avpriv_fits_header_parse_line(s, header, buf + buf_size, NULL); - buf_size += 80; + buf_end = pkt->data + pkt->size; + buf = buf_end - FITS_BLOCK_SIZE; + while(!ret && buf < buf_end) { + ret = avpriv_fits_header_parse_line(s, header, buf, NULL); + buf += 80; } } while (!ret); if (ret < 0) @@ -143,12 +139,10 @@ static int64_t is_image(AVFormatContext *s, FITSContext *fits, FITSHeader *heade static int fits_read_packet(AVFormatContext *s, AVPacket *pkt) { - int64_t pos, ret; uint64_t size; FITSContext *fits = s->priv_data; FITSHeader header; - AVBPrint avbuf; - char *buf; + int ret; if (fits->first_image) { avpriv_fits_header_init(&header, STATE_SIMPLE); @@ -156,59 +150,32 @@ static int fits_read_packet(AVFormatContext *s, AVPacket *pkt) avpriv_fits_header_init(&header, STATE_XTENSION); } - av_bprint_init(&avbuf, FITS_BLOCK_SIZE, AV_BPRINT_SIZE_UNLIMITED); - while ((ret = is_image(s, fits, &header, &avbuf, &size)) == 0) { - av_bprint_finalize(&avbuf, NULL); - pos = avio_skip(s->pb, size); + while ((ret = is_image(s, fits, &header, pkt, &size)) == 0) { + int64_t pos = avio_skip(s->pb, size); if (pos < 0) return pos; - av_bprint_init(&avbuf, FITS_BLOCK_SIZE, AV_BPRINT_SIZE_UNLIMITED); avpriv_fits_header_init(&header, STATE_XTENSION); + av_packet_unref(pkt); } if (ret < 0) - goto fail; - - if (!av_bprint_is_complete(&avbuf)) { - ret = AVERROR(ENOMEM); - goto fail; - } - - av_assert0(avbuf.len <= INT64_MAX && size <= INT64_MAX); - if (avbuf.len + size > INT_MAX - 80) { - ret = AVERROR_INVALIDDATA; - goto fail; - } - // Header is sent with the first line removed... - ret = av_new_packet(pkt, avbuf.len - 80 + size); - if (ret < 0) - goto fail; + return ret; pkt->stream_index = 0; - pkt->flags |= AV_PKT_FLAG_KEY; + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->duration = 1; + // Header is sent with the first line removed... + pkt->data += 80; + pkt->size -= 80; - ret = av_bprint_finalize(&avbuf, &buf); - if (ret < 0) { - return ret; - } + if (size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE - pkt->size) + return AVERROR(ERANGE); - memcpy(pkt->data, buf + 80, avbuf.len - 80); - pkt->size = avbuf.len - 80; - av_freep(&buf); - ret = avio_read(s->pb, pkt->data + pkt->size, size); - if (ret < 0) { + ret = av_append_packet(s->pb, pkt, size); + if (ret < 0) return ret; - } - - pkt->size += ret; - pkt->pts = fits->pts; - fits->pts++; return 0; - -fail: - av_bprint_finalize(&avbuf, NULL); - return ret; } static const AVOption fits_options[] = { @@ -221,15 +188,16 @@ static const AVClass fits_demuxer_class = { .item_name = av_default_item_name, .option = fits_options, .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DEMUXER, }; -const AVInputFormat ff_fits_demuxer = { - .name = "fits", - .long_name = NULL_IF_CONFIG_SMALL("Flexible Image Transport System"), +const FFInputFormat ff_fits_demuxer = { + .p.name = "fits", + .p.long_name = NULL_IF_CONFIG_SMALL("Flexible Image Transport System"), + .p.priv_class = &fits_demuxer_class, + .p.flags = AVFMT_NOTIMESTAMPS, .priv_data_size = sizeof(FITSContext), .read_probe = fits_probe, .read_header = fits_read_header, .read_packet = fits_read_packet, - .priv_class = &fits_demuxer_class, - .raw_codec_id = AV_CODEC_ID_FITS, }; diff --git a/libavformat/fitsenc.c b/libavformat/fitsenc.c index 1df7e2bcf2b..a8efe93d3ce 100644 --- a/libavformat/fitsenc.c +++ b/libavformat/fitsenc.c @@ -84,48 +84,48 @@ static int write_image_header(AVFormatContext *s) float datamax, datamin; switch (encctx->format) { - case AV_PIX_FMT_GRAY8: - bitpix = 8; - naxis = 2; - datamin = 0; - datamax = 255; - break; - case AV_PIX_FMT_GRAY16BE: - bitpix = 16; - naxis = 2; - bzero = 32768; - datamin = 0; - datamax = 65535; - break; - case AV_PIX_FMT_GBRP: - case AV_PIX_FMT_GBRAP: - bitpix = 8; - naxis = 3; - rgb = 1; - if (encctx->format == AV_PIX_FMT_GBRP) { - naxis3 = 3; - } else { - naxis3 = 4; - } - datamin = 0; - datamax = 255; - break; - case AV_PIX_FMT_GBRP16BE: - case AV_PIX_FMT_GBRAP16BE: - bitpix = 16; - naxis = 3; - rgb = 1; - if (encctx->format == AV_PIX_FMT_GBRP16BE) { - naxis3 = 3; - } else { - naxis3 = 4; - } - bzero = 32768; - datamin = 0; - datamax = 65535; - break; - default: - return AVERROR(EINVAL); + case AV_PIX_FMT_GRAY8: + bitpix = 8; + naxis = 2; + datamin = 0; + datamax = 255; + break; + case AV_PIX_FMT_GRAY16BE: + bitpix = 16; + naxis = 2; + bzero = 32768; + datamin = 0; + datamax = 65535; + break; + case AV_PIX_FMT_GBRP: + case AV_PIX_FMT_GBRAP: + bitpix = 8; + naxis = 3; + rgb = 1; + if (encctx->format == AV_PIX_FMT_GBRP) { + naxis3 = 3; + } else { + naxis3 = 4; + } + datamin = 0; + datamax = 255; + break; + case AV_PIX_FMT_GBRP16BE: + case AV_PIX_FMT_GBRAP16BE: + bitpix = 16; + naxis = 3; + rgb = 1; + if (encctx->format == AV_PIX_FMT_GBRP16BE) { + naxis3 = 3; + } else { + naxis3 = 4; + } + bzero = 32768; + datamin = 0; + datamax = 65535; + break; + default: + return AVERROR(EINVAL); } if (fitsctx->first_image) { @@ -198,7 +198,11 @@ const FFOutputFormat ff_fits_muxer = { .p.extensions = "fits", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_FITS, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .priv_data_size = sizeof(FITSContext), - .write_header = fits_write_header, - .write_packet = fits_write_packet, + .write_header = fits_write_header, + .write_packet = fits_write_packet, + .p.flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/flacdec.c b/libavformat/flacdec.c index b58ec039636..5fe835cd05e 100644 --- a/libavformat/flacdec.c +++ b/libavformat/flacdec.c @@ -36,6 +36,8 @@ typedef struct FLACDecContext { FFRawDemuxerContext rawctx; int found_seektable; + + AVCodecContext *parser_dec; } FLACDecContext; static void reset_index_position(int64_t metadata_head_size, AVStream *st) @@ -46,11 +48,16 @@ static void reset_index_position(int64_t metadata_head_size, AVStream *st) sti->index_entries[i].pos += metadata_head_size; } +static const uint16_t sr_table[16] = { + 0, 1764, 3528, 3840, 160, 320, 441, 480, 640, 882, 960, 1920, 0, 0, 0, 0 +}; + static int flac_read_header(AVFormatContext *s) { int ret, metadata_last=0, metadata_type, metadata_size, found_streaminfo=0; uint8_t header[4]; uint8_t *buffer=NULL; + uint32_t marker; FLACDecContext *flac = s->priv_data; AVStream *st = avformat_new_stream(s, NULL); if (!st) @@ -61,7 +68,11 @@ static int flac_read_header(AVFormatContext *s) /* the parameters will be extracted from the compressed bitstream */ /* if fLaC marker is not found, assume there is no header */ - if (avio_rl32(s->pb) != MKTAG('f','L','a','C')) { + marker = avio_rl32(s->pb); + if (marker != MKTAG('f','L','a','C')) { + const int sample_rate = 50 * sr_table[(marker >> 16) & 0xF]; + if (sample_rate) + avpriv_set_pts_info(st, 64, 1, sample_rate); avio_seek(s->pb, -4, SEEK_CUR); return 0; } @@ -69,7 +80,7 @@ static int flac_read_header(AVFormatContext *s) /* process metadata blocks */ while (!avio_feof(s->pb) && !metadata_last) { if (avio_read(s->pb, header, 4) != 4) - return AVERROR(AVERROR_INVALIDDATA); + return AVERROR_INVALIDDATA; flac_parse_block_header(header, &metadata_last, &metadata_type, &metadata_size); switch (metadata_type) { @@ -260,6 +271,7 @@ static int flac_probe(const AVProbeData *p) static av_unused int64_t flac_read_timestamp(AVFormatContext *s, int stream_index, int64_t *ppos, int64_t pos_limit) { + FLACDecContext *flac = s->priv_data; FFFormatContext *const si = ffformatcontext(s); AVPacket *const pkt = si->parse_pkt; AVStream *st = s->streams[stream_index]; @@ -270,10 +282,19 @@ static av_unused int64_t flac_read_timestamp(AVFormatContext *s, int stream_inde if (avio_seek(s->pb, *ppos, SEEK_SET) < 0) return AV_NOPTS_VALUE; + if (!flac->parser_dec) { + flac->parser_dec = avcodec_alloc_context3(NULL); + if (!flac->parser_dec) + return AV_NOPTS_VALUE; + + ret = avcodec_parameters_to_context(flac->parser_dec, st->codecpar); + if (ret < 0) + return ret; + } + parser = av_parser_init(st->codecpar->codec_id); - if (!parser){ + if (!parser) return AV_NOPTS_VALUE; - } parser->flags |= PARSER_FLAG_USE_CODEC_TS; for (;;){ @@ -289,7 +310,7 @@ static av_unused int64_t flac_read_timestamp(AVFormatContext *s, int stream_inde av_assert1(!pkt->size); } } - av_parser_parse2(parser, ffstream(st)->avctx, + av_parser_parse2(parser, flac->parser_dec, &data, &size, pkt->data, pkt->size, pkt->pts, pkt->dts, *ppos); @@ -309,6 +330,15 @@ static av_unused int64_t flac_read_timestamp(AVFormatContext *s, int stream_inde return pts; } +static int flac_close(AVFormatContext *s) +{ + FLACDecContext *flac = s->priv_data; + + avcodec_free_context(&flac->parser_dec); + + return 0; +} + static int flac_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { AVStream *const st = s->streams[0]; FFStream *const sti = ffstream(st); @@ -333,17 +363,18 @@ static int flac_seek(AVFormatContext *s, int stream_index, int64_t timestamp, in return -1; } -const AVInputFormat ff_flac_demuxer = { - .name = "flac", - .long_name = NULL_IF_CONFIG_SMALL("raw FLAC"), +const FFInputFormat ff_flac_demuxer = { + .p.name = "flac", + .p.long_name = NULL_IF_CONFIG_SMALL("raw FLAC"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "flac", + .p.priv_class = &ff_raw_demuxer_class, .read_probe = flac_probe, .read_header = flac_read_header, + .read_close = flac_close, .read_packet = ff_raw_read_partial_packet, .read_seek = flac_seek, .read_timestamp = flac_read_timestamp, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "flac", .raw_codec_id = AV_CODEC_ID_FLAC, .priv_data_size = sizeof(FLACDecContext), - .priv_class = &ff_raw_demuxer_class, }; diff --git a/libavformat/flic.c b/libavformat/flic.c index 222452eac8e..41dfb4f39ef 100644 --- a/libavformat/flic.c +++ b/libavformat/flic.c @@ -34,6 +34,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define FLIC_FILE_MAGIC_1 0xAF11 @@ -285,9 +286,9 @@ static int flic_read_seek(AVFormatContext *s, int stream_index, return 0; } -const AVInputFormat ff_flic_demuxer = { - .name = "flic", - .long_name = NULL_IF_CONFIG_SMALL("FLI/FLC/FLX animation"), +const FFInputFormat ff_flic_demuxer = { + .p.name = "flic", + .p.long_name = NULL_IF_CONFIG_SMALL("FLI/FLC/FLX animation"), .priv_data_size = sizeof(FlicDemuxContext), .read_probe = flic_probe, .read_header = flic_read_header, diff --git a/libavformat/flv.h b/libavformat/flv.h index 3571b90279c..f710963b920 100644 --- a/libavformat/flv.h +++ b/libavformat/flv.h @@ -35,6 +35,12 @@ #define FLV_VIDEO_FRAMETYPE_OFFSET 4 +/* Extended VideoTagHeader + * defined in reference link: + * https://github.com/veovera/enhanced-rtmp/blob/main/enhanced-rtmp-v1.pdf + * */ +#define FLV_IS_EX_HEADER 0x80 + /* bitmasks to isolate specific values */ #define FLV_AUDIO_CHANNEL_MASK 0x01 #define FLV_AUDIO_SAMPLESIZE_MASK 0x02 @@ -42,7 +48,7 @@ #define FLV_AUDIO_CODECID_MASK 0xf0 #define FLV_VIDEO_CODECID_MASK 0x0f -#define FLV_VIDEO_FRAMETYPE_MASK 0xf0 +#define FLV_VIDEO_FRAMETYPE_MASK 0x70 #define AMF_END_OF_OBJECT 0x09 @@ -112,6 +118,15 @@ enum { FLV_CODECID_MPEG4 = 9, }; +enum { + PacketTypeSequenceStart = 0, + PacketTypeCodedFrames = 1, + PacketTypeSequenceEnd = 2, + PacketTypeCodedFramesX = 3, + PacketTypeMetadata = 4, + PacketTypeMPEG2TSSequenceStart = 5, +}; + enum { FLV_FRAME_KEY = 1 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< key frame (for AVC, a seekable frame) FLV_FRAME_INTER = 2 << FLV_VIDEO_FRAMETYPE_OFFSET, ///< inter frame (for AVC, a non-seekable frame) diff --git a/libavformat/flvdec.c b/libavformat/flvdec.c index d83edff727c..892371fcd09 100644 --- a/libavformat/flvdec.c +++ b/libavformat/flvdec.c @@ -33,6 +33,7 @@ #include "libavutil/internal.h" #include "libavutil/intfloat.h" #include "libavutil/intreadwrite.h" +#include "libavutil/mastering_display_metadata.h" #include "libavutil/mathematics.h" #include "avformat.h" #include "demux.h" @@ -45,6 +46,28 @@ #define MAX_DEPTH 16 ///< arbitrary limit to prevent unbounded recursion +typedef struct FLVMasteringMeta { + double r_x; + double r_y; + double g_x; + double g_y; + double b_x; + double b_y; + double white_x; + double white_y; + double max_luminance; + double min_luminance; +} FLVMasteringMeta; + +typedef struct FLVMetaVideoColor { + uint64_t matrix_coefficients; + uint64_t transfer_characteristics; + uint64_t primaries; + uint64_t max_cll; + uint64_t max_fall; + FLVMasteringMeta mastering_meta; +} FLVMetaVideoColor; + typedef struct FLVContext { const AVClass *class; ///< Class for private options. int trust_metadata; ///< configure streams according onMetaData @@ -79,6 +102,9 @@ typedef struct FLVContext { int64_t last_ts; int64_t time_offset; int64_t time_pos; + + FLVMetaVideoColor *metaVideoColor; + int meta_color_info_flag; } FLVContext; /* AMF date type */ @@ -302,14 +328,18 @@ static void flv_set_audio_codec(AVFormatContext *s, AVStream *astream, } } -static int flv_same_video_codec(AVCodecParameters *vpar, int flags) +static int flv_same_video_codec(AVCodecParameters *vpar, uint32_t flv_codecid) { - int flv_codecid = flags & FLV_VIDEO_CODECID_MASK; - if (!vpar->codec_id && !vpar->codec_tag) return 1; switch (flv_codecid) { + case MKBETAG('h', 'v', 'c', '1'): + return vpar->codec_id == AV_CODEC_ID_HEVC; + case MKBETAG('a', 'v', '0', '1'): + return vpar->codec_id == AV_CODEC_ID_AV1; + case MKBETAG('v', 'p', '0', '9'): + return vpar->codec_id == AV_CODEC_ID_VP9; case FLV_CODECID_H263: return vpar->codec_id == AV_CODEC_ID_FLV1; case FLV_CODECID_SCREEN: @@ -328,13 +358,26 @@ static int flv_same_video_codec(AVCodecParameters *vpar, int flags) } static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, - int flv_codecid, int read) + uint32_t flv_codecid, int read) { FFStream *const vstreami = ffstream(vstream); int ret = 0; AVCodecParameters *par = vstream->codecpar; enum AVCodecID old_codec_id = vstream->codecpar->codec_id; + switch (flv_codecid) { + case MKBETAG('h', 'v', 'c', '1'): + par->codec_id = AV_CODEC_ID_HEVC; + vstreami->need_parsing = AVSTREAM_PARSE_HEADERS; + break; + case MKBETAG('a', 'v', '0', '1'): + par->codec_id = AV_CODEC_ID_AV1; + vstreami->need_parsing = AVSTREAM_PARSE_HEADERS; + break; + case MKBETAG('v', 'p', '0', '9'): + par->codec_id = AV_CODEC_ID_VP9; + vstreami->need_parsing = AVSTREAM_PARSE_HEADERS; + break; case FLV_CODECID_H263: par->codec_id = AV_CODEC_ID_FLV1; break; @@ -366,11 +409,9 @@ static int flv_set_video_codec(AVFormatContext *s, AVStream *vstream, case FLV_CODECID_H264: par->codec_id = AV_CODEC_ID_H264; vstreami->need_parsing = AVSTREAM_PARSE_HEADERS; - ret = 3; // not 4, reading packet type will consume one byte break; case FLV_CODECID_MPEG4: par->codec_id = AV_CODEC_ID_MPEG4; - ret = 3; break; default: avpriv_request_sample(s, "Video codec (%x)", flv_codecid); @@ -508,6 +549,7 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream, FLVContext *flv = s->priv_data; AVIOContext *ioc; AMFDataType amf_type; + FLVMetaVideoColor *meta_video_color = flv->metaVideoColor; char str_val[1024]; double num_val; amf_date date; @@ -611,12 +653,7 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream, else if (!strcmp(key, "audiodatarate") && 0 <= (int)(num_val * 1024.0)) flv->audio_bit_rate = num_val * 1024.0; - else if (!strcmp(key, "datastream")) { - AVStream *st = create_stream(s, AVMEDIA_TYPE_SUBTITLE); - if (!st) - return AVERROR(ENOMEM); - st->codecpar->codec_id = AV_CODEC_ID_TEXT; - } else if (!strcmp(key, "framerate")) { + else if (!strcmp(key, "framerate")) { flv->framerate = av_d2q(num_val, 1000); if (vstream) vstream->avg_frame_rate = flv->framerate; @@ -638,6 +675,11 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream, vpar->width = num_val; } else if (!strcmp(key, "height") && vpar) { vpar->height = num_val; + } else if (!strcmp(key, "datastream")) { + AVStream *st = create_stream(s, AVMEDIA_TYPE_SUBTITLE); + if (!st) + return AVERROR(ENOMEM); + st->codecpar->codec_id = AV_CODEC_ID_TEXT; } } } @@ -656,6 +698,43 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream, } } + if (meta_video_color) { + if (amf_type == AMF_DATA_TYPE_NUMBER || + amf_type == AMF_DATA_TYPE_BOOL) { + if (!strcmp(key, "colorPrimaries")) { + meta_video_color->primaries = num_val; + } else if (!strcmp(key, "transferCharacteristics")) { + meta_video_color->transfer_characteristics = num_val; + } else if (!strcmp(key, "matrixCoefficients")) { + meta_video_color->matrix_coefficients = num_val; + } else if (!strcmp(key, "maxFall")) { + meta_video_color->max_fall = num_val; + } else if (!strcmp(key, "maxCLL")) { + meta_video_color->max_cll = num_val; + } else if (!strcmp(key, "redX")) { + meta_video_color->mastering_meta.r_x = num_val; + } else if (!strcmp(key, "redY")) { + meta_video_color->mastering_meta.r_y = num_val; + } else if (!strcmp(key, "greenX")) { + meta_video_color->mastering_meta.g_x = num_val; + } else if (!strcmp(key, "greenY")) { + meta_video_color->mastering_meta.g_y = num_val; + } else if (!strcmp(key, "blueX")) { + meta_video_color->mastering_meta.b_x = num_val; + } else if (!strcmp(key, "blueY")) { + meta_video_color->mastering_meta.b_y = num_val; + } else if (!strcmp(key, "whitePointX")) { + meta_video_color->mastering_meta.white_x = num_val; + } else if (!strcmp(key, "whitePointY")) { + meta_video_color->mastering_meta.white_y = num_val; + } else if (!strcmp(key, "maxLuminance")) { + meta_video_color->mastering_meta.max_luminance = num_val; + } else if (!strcmp(key, "minLuminance")) { + meta_video_color->mastering_meta.min_luminance = num_val; + } + } + } + if (amf_type == AMF_DATA_TYPE_OBJECT && s->nb_streams == 1 && ((!apar && !strcmp(key, "audiocodecid")) || (!vpar && !strcmp(key, "videocodecid")))) @@ -808,6 +887,7 @@ static int flv_read_close(AVFormatContext *s) av_freep(&flv->new_extradata[i]); av_freep(&flv->keyframe_times); av_freep(&flv->keyframe_filepositions); + av_freep(&flv->metaVideoColor); return 0; } @@ -1012,6 +1092,103 @@ static int resync(AVFormatContext *s) return AVERROR_EOF; } +static int flv_parse_video_color_info(AVFormatContext *s, AVStream *st, int64_t next_pos) +{ + FLVContext *flv = s->priv_data; + AMFDataType type; + AVIOContext *ioc; + char buffer[32]; + ioc = s->pb; + + // first object needs to be "colorInfo" string + type = avio_r8(ioc); + if (type != AMF_DATA_TYPE_STRING || + amf_get_string(ioc, buffer, sizeof(buffer)) < 0) + return TYPE_UNKNOWN; + + if (strcmp(buffer, "colorInfo")) { + av_log(s, AV_LOG_DEBUG, "Unknown type %s\n", buffer); + return TYPE_UNKNOWN; + } + + if (!(flv->metaVideoColor = av_mallocz(sizeof(FLVMetaVideoColor)))) { + return AVERROR(ENOMEM); + } + flv->meta_color_info_flag = 1; + amf_parse_object(s, NULL, NULL, buffer, next_pos, 0); // parse metadata + return 0; +} + +static int flv_update_video_color_info(AVFormatContext *s, AVStream *st) +{ + FLVContext *flv = s->priv_data; + const FLVMetaVideoColor* meta_video_color = flv->metaVideoColor; + const FLVMasteringMeta *mastering_meta = &meta_video_color->mastering_meta; + + int has_mastering_primaries, has_mastering_luminance; + // Mastering primaries are CIE 1931 coords, and must be > 0. + has_mastering_primaries = + mastering_meta->r_x > 0 && mastering_meta->r_y > 0 && + mastering_meta->g_x > 0 && mastering_meta->g_y > 0 && + mastering_meta->b_x > 0 && mastering_meta->b_y > 0 && + mastering_meta->white_x > 0 && mastering_meta->white_y > 0; + has_mastering_luminance = mastering_meta->max_luminance > 0 && mastering_meta->min_luminance > 0; + + if (meta_video_color->matrix_coefficients != AVCOL_SPC_RESERVED) + st->codecpar->color_space = meta_video_color->matrix_coefficients; + if (meta_video_color->primaries != AVCOL_PRI_RESERVED && + meta_video_color->primaries != AVCOL_PRI_RESERVED0) + st->codecpar->color_primaries = meta_video_color->primaries; + if (meta_video_color->transfer_characteristics != AVCOL_TRC_RESERVED && + meta_video_color->transfer_characteristics != AVCOL_TRC_RESERVED0) + st->codecpar->color_trc = meta_video_color->transfer_characteristics; + + if (meta_video_color->max_cll && meta_video_color->max_fall) { + size_t size = 0; + AVContentLightMetadata *metadata = av_content_light_metadata_alloc(&size); + if (!metadata) + return AVERROR(ENOMEM); + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_CONTENT_LIGHT_LEVEL, metadata, size, 0)) { + av_freep(&metadata); + return AVERROR(ENOMEM); + } + metadata->MaxCLL = meta_video_color->max_cll; + metadata->MaxFALL = meta_video_color->max_fall; + } + + if (has_mastering_primaries || has_mastering_luminance) { + AVMasteringDisplayMetadata *metadata; + AVPacketSideData *sd = av_packet_side_data_new(&st->codecpar->coded_side_data, + &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_MASTERING_DISPLAY_METADATA, + sizeof(AVMasteringDisplayMetadata), 0); + if (!sd) + return AVERROR(ENOMEM); + metadata = (AVMasteringDisplayMetadata*)sd->data; + memset(metadata, 0, sizeof(AVMasteringDisplayMetadata)); + // hdrCll + if (has_mastering_luminance) { + metadata->max_luminance = av_d2q(mastering_meta->max_luminance, INT_MAX); + metadata->min_luminance = av_d2q(mastering_meta->min_luminance, INT_MAX); + metadata->has_luminance = 1; + } + // hdrMdcv + if (has_mastering_primaries) { + metadata->display_primaries[0][0] = av_d2q(mastering_meta->r_x, INT_MAX); + metadata->display_primaries[0][1] = av_d2q(mastering_meta->r_y, INT_MAX); + metadata->display_primaries[1][0] = av_d2q(mastering_meta->g_x, INT_MAX); + metadata->display_primaries[1][1] = av_d2q(mastering_meta->g_y, INT_MAX); + metadata->display_primaries[2][0] = av_d2q(mastering_meta->b_x, INT_MAX); + metadata->display_primaries[2][1] = av_d2q(mastering_meta->b_y, INT_MAX); + metadata->white_point[0] = av_d2q(mastering_meta->white_x, INT_MAX); + metadata->white_point[1] = av_d2q(mastering_meta->white_y, INT_MAX); + metadata->has_primaries = 1; + } + } + return 0; +} + static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) { FLVContext *flv = s->priv_data; @@ -1025,6 +1202,8 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) AVStream *st = NULL; int last = -1; int orig_size; + int enhanced_flv = 0; + uint32_t video_codec_id = 0; retry: /* pkt size is repeated at end. skip it */ @@ -1071,9 +1250,28 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) } else if (type == FLV_TAG_TYPE_VIDEO) { stream_type = FLV_STREAM_TYPE_VIDEO; flags = avio_r8(s->pb); + video_codec_id = flags & FLV_VIDEO_CODECID_MASK; + /* + * Reference Enhancing FLV 2023-03-v1.0.0-B.8 + * https://github.com/veovera/enhanced-rtmp/blob/main/enhanced-rtmp-v1.pdf + * */ + enhanced_flv = (flags >> 7) & 1; size--; - if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD) + if (enhanced_flv) { + video_codec_id = avio_rb32(s->pb); + size -= 4; + } + + if (enhanced_flv && stream_type == FLV_STREAM_TYPE_VIDEO && (flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD) { + int pkt_type = flags & 0x0F; + if (pkt_type == PacketTypeMetadata) { + int ret = flv_parse_video_color_info(s, st, next); + av_log(s, AV_LOG_DEBUG, "enhanced flv parse metadata ret %d and skip\n", ret); + } goto skip; + } else if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD) { + goto skip; + } } else if (type == FLV_TAG_TYPE_META) { stream_type=FLV_STREAM_TYPE_SUBTITLE; if (size > 13 + 1 + 4) { // Header-type metadata stuff @@ -1129,7 +1327,7 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) break; } else if (stream_type == FLV_STREAM_TYPE_VIDEO) { if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && - (s->video_codec_id || flv_same_video_codec(st->codecpar, flags))) + (s->video_codec_id || flv_same_video_codec(st->codecpar, video_codec_id))) break; } else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) { if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) @@ -1230,7 +1428,7 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) avcodec_parameters_free(&par); } } else if (stream_type == FLV_STREAM_TYPE_VIDEO) { - int ret = flv_set_video_codec(s, st, flags & FLV_VIDEO_CODECID_MASK, 1); + int ret = flv_set_video_codec(s, st, video_codec_id, 1); if (ret < 0) return ret; size -= ret; @@ -1242,16 +1440,30 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) if (st->codecpar->codec_id == AV_CODEC_ID_AAC || st->codecpar->codec_id == AV_CODEC_ID_H264 || - st->codecpar->codec_id == AV_CODEC_ID_MPEG4) { - int type = avio_r8(s->pb); - size--; + st->codecpar->codec_id == AV_CODEC_ID_MPEG4 || + st->codecpar->codec_id == AV_CODEC_ID_HEVC || + st->codecpar->codec_id == AV_CODEC_ID_AV1 || + st->codecpar->codec_id == AV_CODEC_ID_VP9) { + int type = 0; + if (enhanced_flv && stream_type == FLV_STREAM_TYPE_VIDEO) { + type = flags & 0x0F; + } else { + type = avio_r8(s->pb); + size--; + } if (size < 0) { ret = AVERROR_INVALIDDATA; goto leave; } - if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4) { + if (enhanced_flv && stream_type == FLV_STREAM_TYPE_VIDEO && flv->meta_color_info_flag) { + flv_update_video_color_info(s, st); // update av packet side data + flv->meta_color_info_flag = 0; + } + + if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4 || + (st->codecpar->codec_id == AV_CODEC_ID_HEVC && type == PacketTypeCodedFrames)) { // sign extension int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000; pts = av_sat_add64(dts, cts); @@ -1265,9 +1477,11 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt) "invalid timestamps %"PRId64" %"PRId64"\n", dts, pts); dts = pts = AV_NOPTS_VALUE; } + size -= 3; } if (type == 0 && (!st->codecpar->extradata || st->codecpar->codec_id == AV_CODEC_ID_AAC || - st->codecpar->codec_id == AV_CODEC_ID_H264)) { + st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_HEVC || + st->codecpar->codec_id == AV_CODEC_ID_AV1 || st->codecpar->codec_id == AV_CODEC_ID_VP9)) { AVDictionaryEntry *t; if (st->codecpar->extradata) { @@ -1373,42 +1587,42 @@ static const AVClass flv_kux_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_flv_demuxer = { - .name = "flv", - .long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"), +const FFInputFormat ff_flv_demuxer = { + .p.name = "flv", + .p.long_name = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"), + .p.extensions = "flv", + .p.priv_class = &flv_kux_class, .priv_data_size = sizeof(FLVContext), .read_probe = flv_probe, .read_header = flv_read_header, .read_packet = flv_read_packet, .read_seek = flv_read_seek, .read_close = flv_read_close, - .extensions = "flv", - .priv_class = &flv_kux_class, }; -const AVInputFormat ff_live_flv_demuxer = { - .name = "live_flv", - .long_name = NULL_IF_CONFIG_SMALL("live RTMP FLV (Flash Video)"), +const FFInputFormat ff_live_flv_demuxer = { + .p.name = "live_flv", + .p.long_name = NULL_IF_CONFIG_SMALL("live RTMP FLV (Flash Video)"), + .p.extensions = "flv", + .p.priv_class = &flv_kux_class, + .p.flags = AVFMT_TS_DISCONT, .priv_data_size = sizeof(FLVContext), .read_probe = live_flv_probe, .read_header = flv_read_header, .read_packet = flv_read_packet, .read_seek = flv_read_seek, .read_close = flv_read_close, - .extensions = "flv", - .priv_class = &flv_kux_class, - .flags = AVFMT_TS_DISCONT }; -const AVInputFormat ff_kux_demuxer = { - .name = "kux", - .long_name = NULL_IF_CONFIG_SMALL("KUX (YouKu)"), +const FFInputFormat ff_kux_demuxer = { + .p.name = "kux", + .p.long_name = NULL_IF_CONFIG_SMALL("KUX (YouKu)"), + .p.extensions = "kux", + .p.priv_class = &flv_kux_class, .priv_data_size = sizeof(FLVContext), .read_probe = kux_probe, .read_header = flv_read_header, .read_packet = flv_read_packet, .read_seek = flv_read_seek, .read_close = flv_read_close, - .extensions = "kux", - .priv_class = &flv_kux_class, }; diff --git a/libavformat/flvenc.c b/libavformat/flvenc.c index 64ea554dada..a7d4fa46a26 100644 --- a/libavformat/flvenc.c +++ b/libavformat/flvenc.c @@ -23,11 +23,15 @@ #include "libavutil/dict.h" #include "libavutil/intfloat.h" #include "libavutil/avassert.h" +#include "libavutil/mastering_display_metadata.h" #include "libavutil/mathematics.h" #include "libavcodec/codec_desc.h" #include "libavcodec/mpeg4audio.h" #include "avio.h" #include "avc.h" +#include "av1.h" +#include "vpcc.h" +#include "hevc.h" #include "avformat.h" #include "flv.h" #include "internal.h" @@ -46,6 +50,9 @@ static const AVCodecTag flv_video_codec_ids[] = { { AV_CODEC_ID_VP6, FLV_CODECID_VP6 }, { AV_CODEC_ID_VP6A, FLV_CODECID_VP6A }, { AV_CODEC_ID_H264, FLV_CODECID_H264 }, + { AV_CODEC_ID_HEVC, MKBETAG('h', 'v', 'c', '1') }, + { AV_CODEC_ID_AV1, MKBETAG('a', 'v', '0', '1') }, + { AV_CODEC_ID_VP9, MKBETAG('v', 'p', '0', '9') }, { AV_CODEC_ID_NONE, 0 } }; @@ -117,12 +124,10 @@ typedef struct FLVContext { AVCodecParameters *data_par; int flags; + int64_t last_ts[FLV_STREAM_TYPE_NB]; + int metadata_pkt_written; } FLVContext; -typedef struct FLVStreamContext { - int64_t last_ts; ///< last timestamp for each stream -} FLVStreamContext; - static int get_audio_flags(AVFormatContext *s, AVCodecParameters *par) { int flags = (par->bits_per_coded_sample == 16) ? FLV_SAMPLESSIZE_16BIT @@ -205,10 +210,10 @@ static int get_audio_flags(AVFormatContext *s, AVCodecParameters *par) flags |= FLV_CODECID_NELLYMOSER | FLV_SAMPLESSIZE_16BIT; break; case AV_CODEC_ID_PCM_MULAW: - flags = FLV_CODECID_PCM_MULAW | FLV_SAMPLERATE_SPECIAL | FLV_SAMPLESSIZE_16BIT; + flags |= FLV_CODECID_PCM_MULAW | FLV_SAMPLESSIZE_16BIT; break; case AV_CODEC_ID_PCM_ALAW: - flags = FLV_CODECID_PCM_ALAW | FLV_SAMPLERATE_SPECIAL | FLV_SAMPLESSIZE_16BIT; + flags |= FLV_CODECID_PCM_ALAW | FLV_SAMPLESSIZE_16BIT; break; case 0: flags |= par->codec_tag << 4; @@ -226,6 +231,9 @@ static void put_amf_string(AVIOContext *pb, const char *str) { size_t len = strlen(str); avio_wb16(pb, len); + // Avoid avio_write() if put_amf_string(pb, "") is inlined. + if (av_builtin_constant_p(len == 0) && len == 0) + return; avio_write(pb, str, len); } @@ -475,6 +483,142 @@ static void write_metadata(AVFormatContext *s, unsigned int ts) avio_wb32(pb, flv->metadata_totalsize + 11); } +static void flv_write_metadata_packet(AVFormatContext *s, AVCodecParameters *par, unsigned int ts) +{ + AVIOContext *pb = s->pb; + FLVContext *flv = s->priv_data; + AVContentLightMetadata *lightMetadata = NULL; + AVMasteringDisplayMetadata *displayMetadata = NULL; + int64_t metadata_size_pos = 0; + int64_t total_size = 0; + const AVPacketSideData *side_data = NULL; + + if (flv->metadata_pkt_written) return; + if (par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_AV1 || + par->codec_id == AV_CODEC_ID_VP9) { + int flags_size = 5; + side_data = av_packet_side_data_get(par->coded_side_data, par->nb_coded_side_data, + AV_PKT_DATA_CONTENT_LIGHT_LEVEL); + if (side_data) + lightMetadata = (AVContentLightMetadata *)side_data->data; + + side_data = av_packet_side_data_get(par->coded_side_data, par->nb_coded_side_data, + AV_PKT_DATA_MASTERING_DISPLAY_METADATA); + if (side_data) + displayMetadata = (AVMasteringDisplayMetadata *)side_data->data; + + /* + * Reference Enhancing FLV + * https://github.com/veovera/enhanced-rtmp/blob/main/enhanced-rtmp.pdf + * */ + avio_w8(pb, FLV_TAG_TYPE_VIDEO); //write video tag type + metadata_size_pos = avio_tell(pb); + avio_wb24(pb, 0 + flags_size); + put_timestamp(pb, ts); //ts = pkt->dts, gen + avio_wb24(pb, flv->reserved); + + if (par->codec_id == AV_CODEC_ID_HEVC) { + avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeMetadata| FLV_FRAME_VIDEO_INFO_CMD); // ExVideoTagHeader mode with PacketTypeMetadata + avio_write(pb, "hvc1", 4); + } else if (par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) { + avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeMetadata| FLV_FRAME_VIDEO_INFO_CMD); + avio_write(pb, par->codec_id == AV_CODEC_ID_AV1 ? "av01" : "vp09", 4); + } + + avio_w8(pb, AMF_DATA_TYPE_STRING); + put_amf_string(pb, "colorInfo"); + + avio_w8(pb, AMF_DATA_TYPE_OBJECT); + + put_amf_string(pb, "colorConfig"); // colorConfig + + avio_w8(pb, AMF_DATA_TYPE_OBJECT); + + if (par->color_trc != AVCOL_TRC_UNSPECIFIED && + par->color_trc < AVCOL_TRC_NB) { + put_amf_string(pb, "transferCharacteristics"); // color_trc + put_amf_double(pb, par->color_trc); + } + + if (par->color_space != AVCOL_SPC_UNSPECIFIED && + par->color_space < AVCOL_SPC_NB) { + put_amf_string(pb, "matrixCoefficients"); // colorspace + put_amf_double(pb, par->color_space); + } + + if (par->color_primaries != AVCOL_PRI_UNSPECIFIED && + par->color_primaries < AVCOL_PRI_NB) { + put_amf_string(pb, "colorPrimaries"); // color_primaries + put_amf_double(pb, par->color_primaries); + } + + put_amf_string(pb, ""); + avio_w8(pb, AMF_END_OF_OBJECT); + + if (lightMetadata) { + put_amf_string(pb, "hdrCll"); + avio_w8(pb, AMF_DATA_TYPE_OBJECT); + + put_amf_string(pb, "maxFall"); + put_amf_double(pb, lightMetadata->MaxFALL); + + put_amf_string(pb, "maxCLL"); + put_amf_double(pb, lightMetadata->MaxCLL); + + put_amf_string(pb, ""); + avio_w8(pb, AMF_END_OF_OBJECT); + } + + if (displayMetadata && (displayMetadata->has_primaries || displayMetadata->has_luminance)) { + put_amf_string(pb, "hdrMdcv"); + avio_w8(pb, AMF_DATA_TYPE_OBJECT); + if (displayMetadata->has_primaries) { + put_amf_string(pb, "redX"); + put_amf_double(pb, av_q2d(displayMetadata->display_primaries[0][0])); + + put_amf_string(pb, "redY"); + put_amf_double(pb, av_q2d(displayMetadata->display_primaries[0][1])); + + put_amf_string(pb, "greenX"); + put_amf_double(pb, av_q2d(displayMetadata->display_primaries[1][0])); + + put_amf_string(pb, "greenY"); + put_amf_double(pb, av_q2d(displayMetadata->display_primaries[1][1])); + + put_amf_string(pb, "blueX"); + put_amf_double(pb, av_q2d(displayMetadata->display_primaries[2][0])); + + put_amf_string(pb, "blueY"); + put_amf_double(pb, av_q2d(displayMetadata->display_primaries[2][1])); + + put_amf_string(pb, "whitePointX"); + put_amf_double(pb, av_q2d(displayMetadata->white_point[0])); + + put_amf_string(pb, "whitePointY"); + put_amf_double(pb, av_q2d(displayMetadata->white_point[1])); + } + if (displayMetadata->has_luminance) { + put_amf_string(pb, "maxLuminance"); + put_amf_double(pb, av_q2d(displayMetadata->max_luminance)); + + put_amf_string(pb, "minLuminance"); + put_amf_double(pb, av_q2d(displayMetadata->min_luminance)); + } + put_amf_string(pb, ""); + avio_w8(pb, AMF_END_OF_OBJECT); + } + put_amf_string(pb, ""); + avio_w8(pb, AMF_END_OF_OBJECT); + + total_size = avio_tell(pb) - metadata_size_pos - 10; + avio_seek(pb, metadata_size_pos, SEEK_SET); + avio_wb24(pb, total_size); + avio_skip(pb, total_size + 10 - 3); + avio_wb32(pb, total_size + 11); // previous tag size + flv->metadata_pkt_written = 1; + } +} + static int unsupported_codec(AVFormatContext *s, const char* type, int codec_id) { @@ -492,7 +636,8 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i FLVContext *flv = s->priv_data; if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264 - || par->codec_id == AV_CODEC_ID_MPEG4) { + || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC + || par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) { int64_t pos; avio_w8(pb, par->codec_type == AVMEDIA_TYPE_VIDEO ? @@ -508,18 +653,18 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i if (!par->extradata_size && (flv->flags & FLV_AAC_SEQ_HEADER_DETECT)) { PutBitContext pbc; int samplerate_index; - int channels = flv->audio_par->ch_layout.nb_channels - - (flv->audio_par->ch_layout.nb_channels == 8 ? 1 : 0); + int channels = par->ch_layout.nb_channels + - (par->ch_layout.nb_channels == 8 ? 1 : 0); uint8_t data[2]; for (samplerate_index = 0; samplerate_index < 16; samplerate_index++) - if (flv->audio_par->sample_rate + if (par->sample_rate == ff_mpeg4audio_sample_rates[samplerate_index]) break; init_put_bits(&pbc, data, sizeof(data)); - put_bits(&pbc, 5, flv->audio_par->profile + 1); //profile + put_bits(&pbc, 5, par->profile + 1); //profile put_bits(&pbc, 4, samplerate_index); //sample rate index put_bits(&pbc, 4, channels); put_bits(&pbc, 1, 0); //frame length - 1024 samples @@ -535,10 +680,26 @@ static void flv_write_codec_header(AVFormatContext* s, AVCodecParameters* par, i } avio_write(pb, par->extradata, par->extradata_size); } else { - avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags - avio_w8(pb, 0); // AVC sequence header - avio_wb24(pb, 0); // composition time - ff_isom_write_avcc(pb, par->extradata, par->extradata_size); + if (par->codec_id == AV_CODEC_ID_HEVC) { + avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeSequenceStart | FLV_FRAME_KEY); // ExVideoTagHeader mode with PacketTypeSequenceStart + avio_write(pb, "hvc1", 4); + } else if (par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) { + avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeSequenceStart | FLV_FRAME_KEY); + avio_write(pb, par->codec_id == AV_CODEC_ID_AV1 ? "av01" : "vp09", 4); + } else { + avio_w8(pb, par->codec_tag | FLV_FRAME_KEY); // flags + avio_w8(pb, 0); // AVC sequence header + avio_wb24(pb, 0); // composition time + } + + if (par->codec_id == AV_CODEC_ID_HEVC) + ff_isom_write_hvcc(pb, par->extradata, par->extradata_size, 0); + else if (par->codec_id == AV_CODEC_ID_AV1) + ff_isom_write_av1c(pb, par->extradata, par->extradata_size, 1); + else if (par->codec_id == AV_CODEC_ID_VP9) + ff_isom_write_vpcc(s, pb, par->extradata, par->extradata_size, par); + else + ff_isom_write_avcc(pb, par->extradata, par->extradata_size); } data_size = avio_tell(pb) - pos; avio_seek(pb, -data_size - 10, SEEK_CUR); @@ -609,9 +770,15 @@ static int flv_init(struct AVFormatContext *s) int i; FLVContext *flv = s->priv_data; + if (s->nb_streams > FLV_STREAM_TYPE_NB) { + av_log(s, AV_LOG_ERROR, "invalid number of streams %d\n", + s->nb_streams); + return AVERROR(EINVAL); + } + for (i = 0; i < s->nb_streams; i++) { AVCodecParameters *par = s->streams[i]->codecpar; - FLVStreamContext *sc; + switch (par->codec_type) { case AVMEDIA_TYPE_VIDEO: if (s->streams[i]->avg_frame_rate.den && @@ -675,12 +842,7 @@ static int flv_init(struct AVFormatContext *s) return AVERROR(EINVAL); } avpriv_set_pts_info(s->streams[i], 32, 1, 1000); /* 32 bit pts in ms */ - - sc = av_mallocz(sizeof(FLVStreamContext)); - if (!sc) - return AVERROR(ENOMEM); - s->streams[i]->priv_data = sc; - sc->last_ts = -1; + flv->last_ts[i] = -1; } flv->delay = AV_NOPTS_VALUE; @@ -783,10 +945,9 @@ static int flv_write_trailer(AVFormatContext *s) /* Add EOS tag */ for (i = 0; i < s->nb_streams; i++) { AVCodecParameters *par = s->streams[i]->codecpar; - FLVStreamContext *sc = s->streams[i]->priv_data; if (par->codec_type == AVMEDIA_TYPE_VIDEO && (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4)) - put_eos_tag(pb, sc->last_ts, par->codec_id); + put_eos_tag(pb, flv->last_ts[i], par->codec_id); } } @@ -821,10 +982,10 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) AVIOContext *pb = s->pb; AVCodecParameters *par = s->streams[pkt->stream_index]->codecpar; FLVContext *flv = s->priv_data; - FLVStreamContext *sc = s->streams[pkt->stream_index]->priv_data; unsigned ts; int size = pkt->size; uint8_t *data = NULL; + uint8_t frametype = pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; int flags = -1, flags_size, ret = 0; int64_t cur_offset = avio_tell(pb); @@ -836,13 +997,19 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A || par->codec_id == AV_CODEC_ID_VP6 || par->codec_id == AV_CODEC_ID_AAC) flags_size = 2; - else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) + else if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4 || + par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_AV1 || + par->codec_id == AV_CODEC_ID_VP9) flags_size = 5; else flags_size = 1; + if (par->codec_id == AV_CODEC_ID_HEVC && pkt->pts != pkt->dts) + flags_size += 3; + if (par->codec_id == AV_CODEC_ID_AAC || par->codec_id == AV_CODEC_ID_H264 - || par->codec_id == AV_CODEC_ID_MPEG4) { + || par->codec_id == AV_CODEC_ID_MPEG4 || par->codec_id == AV_CODEC_ID_HEVC + || par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) { size_t side_size; uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size); if (side && side_size > 0 && (side_size != par->extradata_size || memcmp(side, par->extradata, side_size))) { @@ -852,6 +1019,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) memcpy(par->extradata, side, side_size); flv_write_codec_header(s, par, pkt->dts); } + flv_write_metadata_packet(s, par, pkt->dts); } if (flv->delay == AV_NOPTS_VALUE) @@ -862,7 +1030,9 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) "Packets are not in the proper order with respect to DTS\n"); return AVERROR(EINVAL); } - if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4) { + if (par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_MPEG4 || + par->codec_id == AV_CODEC_ID_HEVC || par->codec_id == AV_CODEC_ID_AV1 || + par->codec_id == AV_CODEC_ID_VP9) { if (pkt->pts == AV_NOPTS_VALUE) { av_log(s, AV_LOG_ERROR, "Packet is missing PTS\n"); return AVERROR(EINVAL); @@ -885,7 +1055,7 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) flags = ff_codec_get_tag(flv_video_codec_ids, par->codec_id); - flags |= pkt->flags & AV_PKT_FLAG_KEY ? FLV_FRAME_KEY : FLV_FRAME_INTER; + flags |= frametype; break; case AVMEDIA_TYPE_AUDIO: flags = get_audio_flags(s, par); @@ -907,6 +1077,10 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1) if ((ret = ff_avc_parse_nal_units_buf(pkt->data, &data, &size)) < 0) return ret; + } else if (par->codec_id == AV_CODEC_ID_HEVC) { + if (par->extradata_size > 0 && *(uint8_t*)par->extradata != 1) + if ((ret = ff_hevc_annexb2mp4_buf(pkt->data, &data, &size, 0, NULL)) < 0) + return ret; } else if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { if (!s->streams[pkt->stream_index]->nb_frames) { @@ -919,13 +1093,13 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) } /* check Speex packet duration */ - if (par->codec_id == AV_CODEC_ID_SPEEX && ts - sc->last_ts > 160) + if (par->codec_id == AV_CODEC_ID_SPEEX && ts - flv->last_ts[pkt->stream_index] > 160) av_log(s, AV_LOG_WARNING, "Warning: Speex stream has more than " "8 frames per packet. Adobe Flash " "Player cannot handle this!\n"); - if (sc->last_ts < ts) - sc->last_ts = ts; + if (flv->last_ts[pkt->stream_index] < ts) + flv->last_ts[pkt->stream_index] = ts; if (size + flags_size >= 1<<24) { av_log(s, AV_LOG_ERROR, "Too large packet with size %u >= %u\n", @@ -968,7 +1142,18 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) avio_wb32(pb, data_size + 11); } else { av_assert1(flags>=0); - avio_w8(pb,flags); + if (par->codec_id == AV_CODEC_ID_HEVC) { + int pkttype = (pkt->pts != pkt->dts) ? PacketTypeCodedFrames : PacketTypeCodedFramesX; + avio_w8(pb, FLV_IS_EX_HEADER | pkttype | frametype); // ExVideoTagHeader mode with PacketTypeCodedFrames(X) + avio_write(pb, "hvc1", 4); + if (pkttype == PacketTypeCodedFrames) + avio_wb24(pb, pkt->pts - pkt->dts); + } else if (par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP9) { + avio_w8(pb, FLV_IS_EX_HEADER | PacketTypeCodedFrames | frametype); + avio_write(pb, par->codec_id == AV_CODEC_ID_AV1 ? "av01" : "vp09", 4); + } else { + avio_w8(pb, flags); + } if (par->codec_id == AV_CODEC_ID_VP6) avio_w8(pb,0); if (par->codec_id == AV_CODEC_ID_VP6F || par->codec_id == AV_CODEC_ID_VP6A) { @@ -1023,13 +1208,17 @@ static int flv_write_packet(AVFormatContext *s, AVPacket *pkt) static int flv_check_bitstream(AVFormatContext *s, AVStream *st, const AVPacket *pkt) { - int ret = 1; - if (st->codecpar->codec_id == AV_CODEC_ID_AAC) { if (pkt->size > 2 && (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) - ret = ff_stream_add_bitstream_filter(st, "aac_adtstoasc", NULL); + return ff_stream_add_bitstream_filter(st, "aac_adtstoasc", NULL); } - return ret; + if (!st->codecpar->extradata_size && + (st->codecpar->codec_id == AV_CODEC_ID_H264 || + st->codecpar->codec_id == AV_CODEC_ID_HEVC || + st->codecpar->codec_id == AV_CODEC_ID_AV1 || + st->codecpar->codec_id == AV_CODEC_ID_MPEG4)) + return ff_stream_add_bitstream_filter(st, "extract_extradata", NULL); + return 1; } static void flv_deinit(AVFormatContext *s) @@ -1047,12 +1236,12 @@ static void flv_deinit(AVFormatContext *s) } static const AVOption options[] = { - { "flvflags", "FLV muxer flags", offsetof(FLVContext, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" }, - { "aac_seq_header_detect", "Put AAC sequence header based on stream data", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_AAC_SEQ_HEADER_DETECT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" }, - { "no_sequence_end", "disable sequence end for FLV", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_NO_SEQUENCE_END}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" }, - { "no_metadata", "disable metadata for FLV", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_NO_METADATA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" }, - { "no_duration_filesize", "disable duration and filesize zero value metadata for FLV", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_NO_DURATION_FILESIZE}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" }, - { "add_keyframe_index", "Add keyframe index metadata", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_ADD_KEYFRAME_INDEX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "flvflags" }, + { "flvflags", "FLV muxer flags", offsetof(FLVContext, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "flvflags" }, + { "aac_seq_header_detect", "Put AAC sequence header based on stream data", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_AAC_SEQ_HEADER_DETECT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "flvflags" }, + { "no_sequence_end", "disable sequence end for FLV", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_NO_SEQUENCE_END}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "flvflags" }, + { "no_metadata", "disable metadata for FLV", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_NO_METADATA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "flvflags" }, + { "no_duration_filesize", "disable duration and filesize zero value metadata for FLV", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_NO_DURATION_FILESIZE}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "flvflags" }, + { "add_keyframe_index", "Add keyframe index metadata", 0, AV_OPT_TYPE_CONST, {.i64 = FLV_ADD_KEYFRAME_INDEX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "flvflags" }, { NULL }, }; diff --git a/libavformat/format.c b/libavformat/format.c index 52b814e67de..0cdfd85c222 100644 --- a/libavformat/format.c +++ b/libavformat/format.c @@ -28,8 +28,10 @@ #include "avio_internal.h" #include "avformat.h" +#include "demux.h" #include "id3v2.h" #include "internal.h" +#include "url.h" /** @@ -50,6 +52,31 @@ int av_match_ext(const char *filename, const char *extensions) return 0; } +int ff_match_url_ext(const char *url, const char *extensions) +{ + const char *ext; + URLComponents uc; + int ret; + char scratchpad[128]; + + if (!url) + return 0; + + ret = ff_url_decompose(&uc, url, NULL); + if (ret < 0 || !URL_COMPONENT_HAVE(uc, scheme)) + return ret; + for (ext = uc.query; *ext != '.' && ext > uc.path; ext--) + ; + + if (*ext != '.') + return 0; + if (uc.query - ext > sizeof(scratchpad)) + return AVERROR(ENOMEM); //not enough memory in our scratchpad + av_strlcpy(scratchpad, ext + 1, uc.query - ext); + + return av_match_name(scratchpad, extensions); +} + const AVOutputFormat *av_guess_format(const char *short_name, const char *filename, const char *mime_type) { @@ -163,8 +190,8 @@ const AVInputFormat *av_probe_input_format3(const AVProbeData *pd, if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2")) continue; score = 0; - if (fmt1->read_probe) { - score = fmt1->read_probe(&lpd); + if (ffifmt(fmt1)->read_probe) { + score = ffifmt(fmt1)->read_probe(&lpd); if (score) av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d\n", fmt1->name, score, lpd.buf_size); if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) { diff --git a/libavformat/frmdec.c b/libavformat/frmdec.c index e6c1179dcd3..412430e4fcf 100644 --- a/libavformat/frmdec.c +++ b/libavformat/frmdec.c @@ -27,6 +27,7 @@ #include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" static const enum AVPixelFormat frm_pix_fmt_tags[] = { AV_PIX_FMT_RGB555, @@ -102,10 +103,10 @@ static int frm_read_packet(AVFormatContext *avctx, AVPacket *pkt) return 0; } -const AVInputFormat ff_frm_demuxer = { - .name = "frm", +const FFInputFormat ff_frm_demuxer = { + .p.name = "frm", + .p.long_name = NULL_IF_CONFIG_SMALL("Megalux Frame"), .priv_data_size = sizeof(FrmContext), - .long_name = NULL_IF_CONFIG_SMALL("Megalux Frame"), .read_probe = frm_read_probe, .read_header = frm_read_header, .read_packet = frm_read_packet, diff --git a/libavformat/fsb.c b/libavformat/fsb.c index 12b67f631c9..0febeffd56b 100644 --- a/libavformat/fsb.c +++ b/libavformat/fsb.c @@ -23,6 +23,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "avio.h" +#include "demux.h" #include "internal.h" static int fsb_probe(const AVProbeData *p) @@ -156,7 +157,6 @@ static int fsb_read_header(AVFormatContext *s) } avio_skip(pb, offset - avio_tell(pb)); - ffformatcontext(s)->data_offset = avio_tell(pb); avpriv_set_pts_info(st, 64, 1, par->sample_rate); @@ -200,12 +200,12 @@ static int fsb_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_fsb_demuxer = { - .name = "fsb", - .long_name = NULL_IF_CONFIG_SMALL("FMOD Sample Bank"), +const FFInputFormat ff_fsb_demuxer = { + .p.name = "fsb", + .p.long_name = NULL_IF_CONFIG_SMALL("FMOD Sample Bank"), + .p.extensions = "fsb", + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = fsb_probe, .read_header = fsb_read_header, .read_packet = fsb_read_packet, - .extensions = "fsb", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/ftp.c b/libavformat/ftp.c index 883668b37ba..f9b069a59e2 100644 --- a/libavformat/ftp.c +++ b/libavformat/ftp.c @@ -19,6 +19,7 @@ */ #include +#include #include "libavutil/avstring.h" #include "libavutil/internal.h" diff --git a/libavformat/fwse.c b/libavformat/fwse.c index 28a322d9d63..dc4750d946a 100644 --- a/libavformat/fwse.c +++ b/libavformat/fwse.c @@ -22,6 +22,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "pcm.h" @@ -66,7 +67,7 @@ static int fwse_read_header(AVFormatContext *s) av_channel_layout_default(&par->ch_layout, channels); st->duration = avio_rl32(pb); par->sample_rate = avio_rl32(pb); - if (par->sample_rate <= 0 || par->sample_rate > INT_MAX) + if (par->sample_rate <= 0) return AVERROR_INVALIDDATA; par->block_align = 1; @@ -77,11 +78,11 @@ static int fwse_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_fwse_demuxer = { - .name = "fwse", - .long_name = NULL_IF_CONFIG_SMALL("Capcom's MT Framework sound"), +const FFInputFormat ff_fwse_demuxer = { + .p.name = "fwse", + .p.long_name = NULL_IF_CONFIG_SMALL("Capcom's MT Framework sound"), + .p.extensions = "fwse", .read_probe = fwse_probe, .read_header = fwse_read_header, .read_packet = ff_pcm_read_packet, - .extensions = "fwse", }; diff --git a/libavformat/g722.c b/libavformat/g722.c index 08cd2cbc87e..adb78e8db52 100644 --- a/libavformat/g722.c +++ b/libavformat/g722.c @@ -19,8 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/avassert.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "rawdec.h" @@ -37,23 +37,20 @@ static int g722_read_header(AVFormatContext *s) st->codecpar->sample_rate = 16000; st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; - st->codecpar->bits_per_coded_sample = - av_get_bits_per_sample(st->codecpar->codec_id); - - av_assert0(st->codecpar->bits_per_coded_sample > 0); + st->codecpar->bits_per_coded_sample = 4; avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); return 0; } -const AVInputFormat ff_g722_demuxer = { - .name = "g722", - .long_name = NULL_IF_CONFIG_SMALL("raw G.722"), +const FFInputFormat ff_g722_demuxer = { + .p.name = "g722", + .p.long_name = NULL_IF_CONFIG_SMALL("raw G.722"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "g722,722", + .p.priv_class = &ff_raw_demuxer_class, .read_header = g722_read_header, .read_packet = ff_raw_read_partial_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "g722,722", .raw_codec_id = AV_CODEC_ID_ADPCM_G722, .priv_data_size = sizeof(FFRawDemuxerContext), - .priv_class = &ff_raw_demuxer_class, }; diff --git a/libavformat/g723_1.c b/libavformat/g723_1.c index e35b4ed503e..17c7a13d384 100644 --- a/libavformat/g723_1.c +++ b/libavformat/g723_1.c @@ -27,6 +27,7 @@ #include "libavutil/attributes.h" #include "libavutil/channel_layout.h" #include "avformat.h" +#include "demux.h" #include "internal.h" static const uint8_t frame_size[4] = { 24, 20, 4, 1 }; @@ -74,11 +75,11 @@ static int g723_1_read_packet(AVFormatContext *s, AVPacket *pkt) return pkt->size; } -const AVInputFormat ff_g723_1_demuxer = { - .name = "g723_1", - .long_name = NULL_IF_CONFIG_SMALL("G.723.1"), +const FFInputFormat ff_g723_1_demuxer = { + .p.name = "g723_1", + .p.long_name = NULL_IF_CONFIG_SMALL("G.723.1"), + .p.extensions = "tco,rco,g723_1", + .p.flags = AVFMT_GENERIC_INDEX, .read_header = g723_1_init, .read_packet = g723_1_read_packet, - .extensions = "tco,rco,g723_1", - .flags = AVFMT_GENERIC_INDEX }; diff --git a/libavformat/g726.c b/libavformat/g726.c index 97580a74f8f..e783fa41231 100644 --- a/libavformat/g726.c +++ b/libavformat/g726.c @@ -22,6 +22,7 @@ #include "config_components.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "libavutil/opt.h" @@ -39,7 +40,7 @@ static int g726_read_header(AVFormatContext *s) return AVERROR(ENOMEM); st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - st->codecpar->codec_id = s->iformat->raw_codec_id; + st->codecpar->codec_id = ffifmt(s->iformat)->raw_codec_id; st->codecpar->sample_rate = c->sample_rate; st->codecpar->bits_per_coded_sample = c->code_size; @@ -75,25 +76,25 @@ static const AVClass g726_demuxer_class = { }; #if CONFIG_G726_DEMUXER -const AVInputFormat ff_g726_demuxer = { - .name = "g726", - .long_name = NULL_IF_CONFIG_SMALL("raw big-endian G.726 (\"left aligned\")"), +const FFInputFormat ff_g726_demuxer = { + .p.name = "g726", + .p.long_name = NULL_IF_CONFIG_SMALL("raw big-endian G.726 (\"left aligned\")"), + .p.priv_class = &g726_demuxer_class, .read_header = g726_read_header, .read_packet = g726_read_packet, .priv_data_size = sizeof(G726Context), - .priv_class = &g726_demuxer_class, .raw_codec_id = AV_CODEC_ID_ADPCM_G726, }; #endif #if CONFIG_G726LE_DEMUXER -const AVInputFormat ff_g726le_demuxer = { - .name = "g726le", - .long_name = NULL_IF_CONFIG_SMALL("raw little-endian G.726 (\"right aligned\")"), +const FFInputFormat ff_g726le_demuxer = { + .p.name = "g726le", + .p.long_name = NULL_IF_CONFIG_SMALL("raw little-endian G.726 (\"right aligned\")"), + .p.priv_class = &g726_demuxer_class, .read_header = g726_read_header, .read_packet = g726_read_packet, .priv_data_size = sizeof(G726Context), - .priv_class = &g726_demuxer_class, .raw_codec_id = AV_CODEC_ID_ADPCM_G726LE, }; #endif diff --git a/libavformat/g729dec.c b/libavformat/g729dec.c index 9c92ea4fcf4..d6f55b70de8 100644 --- a/libavformat/g729dec.c +++ b/libavformat/g729dec.c @@ -23,6 +23,7 @@ #include "libavutil/opt.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct G729DemuxerContext { @@ -93,13 +94,13 @@ static const AVClass g729_demuxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_g729_demuxer = { - .name = "g729", - .long_name = NULL_IF_CONFIG_SMALL("G.729 raw format demuxer"), +const FFInputFormat ff_g729_demuxer = { + .p.name = "g729", + .p.long_name = NULL_IF_CONFIG_SMALL("G.729 raw format demuxer"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "g729", + .p.priv_class = &g729_demuxer_class, .priv_data_size = sizeof(G729DemuxerContext), .read_header = g729_read_header, .read_packet = g729_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "g729", - .priv_class = &g729_demuxer_class, }; diff --git a/libavformat/gdv.c b/libavformat/gdv.c index f8a8f503515..9a042a1fc89 100644 --- a/libavformat/gdv.c +++ b/libavformat/gdv.c @@ -23,6 +23,7 @@ #include "avformat.h" #include "avio.h" +#include "demux.h" #include "internal.h" typedef struct GDVContext { @@ -194,9 +195,9 @@ static int gdv_read_packet(AVFormatContext *ctx, AVPacket *pkt) return 0; } -const AVInputFormat ff_gdv_demuxer = { - .name = "gdv", - .long_name = NULL_IF_CONFIG_SMALL("Gremlin Digital Video"), +const FFInputFormat ff_gdv_demuxer = { + .p.name = "gdv", + .p.long_name = NULL_IF_CONFIG_SMALL("Gremlin Digital Video"), .priv_data_size = sizeof(GDVContext), .read_probe = gdv_read_probe, .read_header = gdv_read_header, diff --git a/libavformat/genh.c b/libavformat/genh.c index 1f707b55552..deecca4a4c5 100644 --- a/libavformat/genh.c +++ b/libavformat/genh.c @@ -22,6 +22,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct GENHDemuxContext { @@ -195,12 +196,12 @@ static int genh_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_genh_demuxer = { - .name = "genh", - .long_name = NULL_IF_CONFIG_SMALL("GENeric Header"), +const FFInputFormat ff_genh_demuxer = { + .p.name = "genh", + .p.long_name = NULL_IF_CONFIG_SMALL("GENeric Header"), + .p.extensions = "genh", .priv_data_size = sizeof(GENHDemuxContext), .read_probe = genh_probe, .read_header = genh_read_header, .read_packet = genh_read_packet, - .extensions = "genh", }; diff --git a/libavformat/gif.c b/libavformat/gif.c index bfa7deb598c..211705facc3 100644 --- a/libavformat/gif.c +++ b/libavformat/gif.c @@ -40,16 +40,8 @@ typedef struct GIFContext { AVPacket *prev_pkt; } GIFContext; -static int gif_write_header(AVFormatContext *s) +static av_cold int gif_init(AVFormatContext *s) { - if (s->nb_streams != 1 || - s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO || - s->streams[0]->codecpar->codec_id != AV_CODEC_ID_GIF) { - av_log(s, AV_LOG_ERROR, - "GIF muxer supports only a single video GIF stream.\n"); - return AVERROR(EINVAL); - } - avpriv_set_pts_info(s->streams[0], 64, 1, 100); return 0; @@ -88,6 +80,8 @@ static int gif_get_delay(GIFContext *gif, AVPacket *prev, AVPacket *new) gif->duration = av_clip_uint16(new->pts - prev->pts); else if (!new && gif->last_delay >= 0) gif->duration = gif->last_delay; + else if (prev->duration) + gif->duration = prev->duration; return gif->duration; } @@ -211,7 +205,10 @@ const FFOutputFormat ff_gif_muxer = { .priv_data_size = sizeof(GIFContext), .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_GIF, - .write_header = gif_write_header, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, + .init = gif_init, .write_packet = gif_write_packet, .write_trailer = gif_write_trailer, .p.priv_class = &gif_muxer_class, diff --git a/libavformat/gifdec.c b/libavformat/gifdec.c index 1977f46e3aa..294007682b7 100644 --- a/libavformat/gifdec.c +++ b/libavformat/gifdec.c @@ -25,12 +25,16 @@ */ #include "avformat.h" +#include "demux.h" #include "libavutil/bprint.h" #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" +#include "avio_internal.h" #include "internal.h" #include "libavcodec/gif.h" +#define GIF_PACKET_SIZE 1024 + typedef struct GIFDemuxContext { const AVClass *class; /** @@ -53,9 +57,6 @@ typedef struct GIFDemuxContext { int total_iter; int iter_count; int ignore_loop; - - int nb_frames; - int last_duration; } GIFDemuxContext; /** @@ -84,8 +85,8 @@ static int gif_probe(const AVProbeData *p) static int resync(AVIOContext *pb) { - int i; - for (i = 0; i < 6; i++) { + ffio_ensure_seekback(pb, 13); + for (int i = 0; i < 6; i++) { int b = avio_r8(pb); if (b != gif87a_sig[i] && b != gif89a_sig[i]) i = -(b != 'G'); @@ -113,11 +114,12 @@ static int gif_read_header(AVFormatContext *s) AVIOContext *pb = s->pb; AVStream *st; int type, width, height, ret, n, flags; - int64_t nb_frames = 0, duration = 0; + int64_t nb_frames = 0, duration = 0, pos; if ((ret = resync(pb)) < 0) return ret; + pos = avio_tell(pb); gdc->delay = gdc->default_delay; width = avio_rl16(pb); height = avio_rl16(pb); @@ -132,6 +134,9 @@ static int gif_read_header(AVFormatContext *s) if (!st) return AVERROR(ENOMEM); + if (!(pb->seekable & AVIO_SEEKABLE_NORMAL)) + goto skip; + if (flags & 0x80) avio_skip(pb, 3 * (1 << ((flags & 0x07) + 1))); @@ -158,15 +163,37 @@ static int gif_read_header(AVFormatContext *s) avio_skip(pb, 1); delay = avio_rl16(pb); - if (delay < gdc->min_delay) - delay = gdc->default_delay; - delay = FFMIN(delay, gdc->max_delay); + delay = delay ? delay : gdc->default_delay; duration += delay; avio_skip(pb, 1); } else { avio_skip(pb, block_size); } gif_skip_subblocks(pb); + } else if (subtype == GIF_APP_EXT_LABEL) { + uint8_t data[256]; + int sb_size; + + sb_size = avio_r8(pb); + ret = avio_read(pb, data, sb_size); + if (ret < 0 || !sb_size) + break; + + if (sb_size == strlen(NETSCAPE_EXT_STR)) { + sb_size = avio_r8(pb); + ret = avio_read(pb, data, sb_size); + if (ret < 0 || !sb_size) + break; + + if (sb_size == 3 && data[0] == 1) { + gdc->total_iter = AV_RL16(data+1); + av_log(s, AV_LOG_DEBUG, "Loop count is %d\n", gdc->total_iter); + + if (gdc->total_iter == 0) + gdc->total_iter = -1; + } + } + gif_skip_subblocks(pb); } else { gif_skip_subblocks(pb); } @@ -183,203 +210,57 @@ static int gif_read_header(AVFormatContext *s) } } +skip: + /* jump to start because gif decoder needs header data too */ + if (avio_seek(pb, pos - 6, SEEK_SET) != pos - 6) + return AVERROR(EIO); + /* GIF format operates with time in "hundredths of second", * therefore timebase is 1/100 */ avpriv_set_pts_info(st, 64, 1, 100); + ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; st->codecpar->codec_id = AV_CODEC_ID_GIF; st->codecpar->width = width; st->codecpar->height = height; + if (nb_frames > 1) { + av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, + 100, duration / nb_frames, INT_MAX); + } else if (duration) { + st->avg_frame_rate = (AVRational) { 100, duration }; + } st->start_time = 0; st->duration = duration; st->nb_frames = nb_frames; - if (n) { - st->codecpar->sample_aspect_ratio.num = n + 15; - st->codecpar->sample_aspect_ratio.den = 64; - } - - /* jump to start because gif decoder needs header data too */ - if (avio_seek(pb, 0, SEEK_SET) != 0) - return AVERROR(EIO); + if (n) + st->codecpar->sample_aspect_ratio = av_make_q(n + 15, 64); return 0; } -static int gif_read_ext(AVFormatContext *s) +static int gif_read_packet(AVFormatContext *s, AVPacket *pkt) { GIFDemuxContext *gdc = s->priv_data; AVIOContext *pb = s->pb; - int sb_size, ext_label = avio_r8(pb); int ret; - if (ext_label == GIF_GCE_EXT_LABEL) { - if ((sb_size = avio_r8(pb)) < 4) { - av_log(s, AV_LOG_FATAL, "Graphic Control Extension block's size less than 4.\n"); - return AVERROR_INVALIDDATA; - } - - /* skip packed fields */ - if ((ret = avio_skip(pb, 1)) < 0) - return ret; - - gdc->delay = avio_rl16(pb); - - if (gdc->delay < gdc->min_delay) - gdc->delay = gdc->default_delay; - gdc->delay = FFMIN(gdc->delay, gdc->max_delay); - - /* skip the rest of the Graphic Control Extension block */ - if ((ret = avio_skip(pb, sb_size - 3)) < 0 ) - return ret; - } else if (ext_label == GIF_APP_EXT_LABEL) { - uint8_t data[256]; - - sb_size = avio_r8(pb); - ret = avio_read(pb, data, sb_size); - if (ret < 0 || !sb_size) - return ret; - - if (sb_size == strlen(NETSCAPE_EXT_STR)) { - sb_size = avio_r8(pb); - ret = avio_read(pb, data, sb_size); - if (ret < 0 || !sb_size) - return ret; - - if (sb_size == 3 && data[0] == 1) { - gdc->total_iter = AV_RL16(data+1); - av_log(s, AV_LOG_DEBUG, "Loop count is %d\n", gdc->total_iter); - - if (gdc->total_iter == 0) - gdc->total_iter = -1; - } - } + if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && + !gdc->ignore_loop && avio_feof(pb) && + (gdc->total_iter < 0 || (++gdc->iter_count < gdc->total_iter))) { + avio_seek(pb, 0, SEEK_SET); } - - if ((ret = gif_skip_subblocks(pb)) < 0) + if ((ret = av_new_packet(pkt, GIF_PACKET_SIZE)) < 0) return ret; - return 0; -} - -static int gif_read_packet(AVFormatContext *s, AVPacket *pkt) -{ - GIFDemuxContext *gdc = s->priv_data; - AVIOContext *pb = s->pb; - int packed_fields, block_label, ct_size, - keyframe, frame_parsed = 0, ret; - int64_t frame_start = avio_tell(pb), frame_end; - unsigned char buf[6]; - - if ((ret = avio_read(pb, buf, 6)) == 6) { - keyframe = memcmp(buf, gif87a_sig, 6) == 0 || - memcmp(buf, gif89a_sig, 6) == 0; - } else if (ret < 0) { + pkt->pos = avio_tell(pb); + pkt->stream_index = 0; + ret = avio_read_partial(pb, pkt->data, GIF_PACKET_SIZE); + if (ret < 0) { + av_packet_unref(pkt); return ret; - } else { - keyframe = 0; - } - - if (keyframe) { -parse_keyframe: - /* skip 2 bytes of width and 2 of height */ - if ((ret = avio_skip(pb, 4)) < 0) - return ret; - - packed_fields = avio_r8(pb); - - /* skip 1 byte of Background Color Index and 1 byte of Pixel Aspect Ratio */ - if ((ret = avio_skip(pb, 2)) < 0) - return ret; - - /* global color table presence */ - if (packed_fields & 0x80) { - ct_size = 3 * (1 << ((packed_fields & 0x07) + 1)); - - if ((ret = avio_skip(pb, ct_size)) < 0) - return ret; - } - } else { - avio_seek(pb, -ret, SEEK_CUR); - ret = AVERROR_EOF; - } - - while (GIF_TRAILER != (block_label = avio_r8(pb)) && !avio_feof(pb)) { - if (block_label == GIF_EXTENSION_INTRODUCER) { - if ((ret = gif_read_ext (s)) < 0 ) - goto resync; - } else if (block_label == GIF_IMAGE_SEPARATOR) { - /* skip to last byte of Image Descriptor header */ - if ((ret = avio_skip(pb, 8)) < 0) - return ret; - - packed_fields = avio_r8(pb); - - /* local color table presence */ - if (packed_fields & 0x80) { - ct_size = 3 * (1 << ((packed_fields & 0x07) + 1)); - - if ((ret = avio_skip(pb, ct_size)) < 0) - return ret; - } - - /* read LZW Minimum Code Size */ - if (avio_r8(pb) < 1) { - av_log(s, AV_LOG_ERROR, "lzw minimum code size must be >= 1\n"); - goto resync; - } - - if ((ret = gif_skip_subblocks(pb)) < 0) - goto resync; - - frame_end = avio_tell(pb); - - if (avio_seek(pb, frame_start, SEEK_SET) != frame_start) - return AVERROR(EIO); - - ret = av_get_packet(pb, pkt, frame_end - frame_start); - if (ret < 0) - return ret; - - if (keyframe) - pkt->flags |= AV_PKT_FLAG_KEY; - - pkt->stream_index = 0; - pkt->duration = gdc->delay; - - gdc->nb_frames ++; - gdc->last_duration = pkt->duration; - - /* Graphic Control Extension's scope is single frame. - * Remove its influence. */ - gdc->delay = gdc->default_delay; - frame_parsed = 1; - - break; - } else { - av_log(s, AV_LOG_ERROR, "invalid block label\n"); -resync: - if (!keyframe) - avio_seek(pb, frame_start, SEEK_SET); - if ((ret = resync(pb)) < 0) - return ret; - frame_start = avio_tell(pb) - 6; - keyframe = 1; - goto parse_keyframe; - } } - - if ((ret >= 0 && !frame_parsed) || ret == AVERROR_EOF) { - if (gdc->nb_frames == 1) { - s->streams[0]->r_frame_rate = (AVRational) {100, gdc->last_duration}; - } - /* This might happen when there is no image block - * between extension blocks and GIF_TRAILER or EOF */ - if (!gdc->ignore_loop && (block_label == GIF_TRAILER || avio_feof(pb)) - && (gdc->total_iter < 0 || ++gdc->iter_count < gdc->total_iter)) - return avio_seek(pb, 0, SEEK_SET); - return AVERROR_EOF; - } else - return ret; + av_shrink_packet(pkt, ret); + return ret; } static const AVOption options[] = { @@ -398,13 +279,14 @@ static const AVClass demuxer_class = { .category = AV_CLASS_CATEGORY_DEMUXER, }; -const AVInputFormat ff_gif_demuxer = { - .name = "gif", - .long_name = NULL_IF_CONFIG_SMALL("CompuServe Graphics Interchange Format (GIF)"), +const FFInputFormat ff_gif_demuxer = { + .p.name = "gif", + .p.long_name = NULL_IF_CONFIG_SMALL("CompuServe Graphics Interchange Format (GIF)"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "gif", + .p.priv_class = &demuxer_class, .priv_data_size = sizeof(GIFDemuxContext), .read_probe = gif_probe, .read_header = gif_read_header, .read_packet = gif_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .priv_class = &demuxer_class, }; diff --git a/libavformat/gopher.c b/libavformat/gopher.c index 9497ffacf2a..b022494981a 100644 --- a/libavformat/gopher.c +++ b/libavformat/gopher.c @@ -48,8 +48,11 @@ static int gopher_connect(URLContext *h, const char *path) if (!*path) return AVERROR(EINVAL); switch (*++path) { + case ';': + case '<': case '5': case '9': + case 's': path = strchr(path, '/'); if (!path) return AVERROR(EINVAL); break; diff --git a/libavformat/gsmdec.c b/libavformat/gsmdec.c index 09dc0e0fb3f..10fba212e9f 100644 --- a/libavformat/gsmdec.c +++ b/libavformat/gsmdec.c @@ -23,6 +23,7 @@ #include "libavutil/mathematics.h" #include "libavutil/opt.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define GSM_BLOCK_SIZE 33 @@ -78,7 +79,7 @@ static int gsm_read_header(AVFormatContext *s) return AVERROR(ENOMEM); st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - st->codecpar->codec_id = s->iformat->raw_codec_id; + st->codecpar->codec_id = AV_CODEC_ID_GSM; st->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; st->codecpar->sample_rate = c->sample_rate; st->codecpar->bit_rate = GSM_BLOCK_SIZE * 8 * c->sample_rate / GSM_BLOCK_SAMPLES; @@ -102,15 +103,15 @@ static const AVClass gsm_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_gsm_demuxer = { - .name = "gsm", - .long_name = NULL_IF_CONFIG_SMALL("raw GSM"), +const FFInputFormat ff_gsm_demuxer = { + .p.name = "gsm", + .p.long_name = NULL_IF_CONFIG_SMALL("raw GSM"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "gsm", + .p.priv_class = &gsm_class, .priv_data_size = sizeof(GSMDemuxerContext), .read_probe = gsm_probe, .read_header = gsm_read_header, .read_packet = gsm_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "gsm", .raw_codec_id = AV_CODEC_ID_GSM, - .priv_class = &gsm_class, }; diff --git a/libavformat/gxf.c b/libavformat/gxf.c index f720521c1b3..1a9f0d75b07 100644 --- a/libavformat/gxf.c +++ b/libavformat/gxf.c @@ -599,9 +599,9 @@ static int64_t gxf_read_timestamp(AVFormatContext *s, int stream_index, return res; } -const AVInputFormat ff_gxf_demuxer = { - .name = "gxf", - .long_name = NULL_IF_CONFIG_SMALL("GXF (General eXchange Format)"), +const FFInputFormat ff_gxf_demuxer = { + .p.name = "gxf", + .p.long_name = NULL_IF_CONFIG_SMALL("GXF (General eXchange Format)"), .priv_data_size = sizeof(struct gxf_stream_info), .read_probe = gxf_probe, .read_header = gxf_header, diff --git a/libavformat/gxfenc.c b/libavformat/gxfenc.c index 74959247229..0aea7bd7c22 100644 --- a/libavformat/gxfenc.c +++ b/libavformat/gxfenc.c @@ -137,7 +137,7 @@ static void gxf_write_padding(AVIOContext *pb, int64_t to_pad) ffio_fill(pb, 0, to_pad); } -static int64_t updatePacketSize(AVIOContext *pb, int64_t pos) +static int64_t update_packet_size(AVIOContext *pb, int64_t pos) { int64_t curpos; int size; @@ -154,7 +154,7 @@ static int64_t updatePacketSize(AVIOContext *pb, int64_t pos) return curpos - pos; } -static int64_t updateSize(AVIOContext *pb, int64_t pos) +static int64_t update_size(AVIOContext *pb, int64_t pos) { int64_t curpos; @@ -300,7 +300,7 @@ static int gxf_write_track_description(AVFormatContext *s, GXFStreamContext *sc, avio_w8(pb, 4); avio_wb32(pb, sc->fields); - return updateSize(pb, pos); + return update_size(pb, pos); } static int gxf_write_material_data_section(AVFormatContext *s) @@ -351,7 +351,7 @@ static int gxf_write_material_data_section(AVFormatContext *s) avio_w8(pb, 4); avio_wb32(pb, avio_size(pb) / 1024); - return updateSize(pb, pos); + return update_size(pb, pos); } static int gxf_write_track_description_section(AVFormatContext *s) @@ -368,7 +368,7 @@ static int gxf_write_track_description_section(AVFormatContext *s) gxf_write_track_description(s, &gxf->timecode_track, s->nb_streams); - return updateSize(pb, pos); + return update_size(pb, pos); } static int gxf_write_map_packet(AVFormatContext *s, int rewrite) @@ -400,7 +400,7 @@ static int gxf_write_map_packet(AVFormatContext *s, int rewrite) gxf_write_material_data_section(s); gxf_write_track_description_section(s); - return updatePacketSize(pb, pos); + return update_packet_size(pb, pos); } static int gxf_write_flt_packet(AVFormatContext *s) @@ -424,7 +424,7 @@ static int gxf_write_flt_packet(AVFormatContext *s) ffio_fill(pb, 0, (1000 - i) * 4); - return updatePacketSize(pb, pos); + return update_packet_size(pb, pos); } static int gxf_write_umf_material_description(AVFormatContext *s) @@ -643,7 +643,7 @@ static int gxf_write_umf_packet(AVFormatContext *s) gxf->umf_track_size = gxf_write_umf_track_description(s); gxf->umf_media_size = gxf_write_umf_media_description(s); gxf->umf_length = avio_tell(pb) - gxf->umf_start_offset; - return updatePacketSize(pb, pos); + return update_packet_size(pb, pos); } static void gxf_init_timecode_track(GXFStreamContext *sc, GXFStreamContext *vsc) @@ -692,7 +692,7 @@ static int gxf_write_header(AVFormatContext *s) if (!(pb->seekable & AVIO_SEEKABLE_NORMAL)) { av_log(s, AV_LOG_ERROR, "gxf muxer does not support streamed output, patch welcome\n"); - return -1; + return AVERROR_PATCHWELCOME; } gxf->flags |= 0x00080000; /* material is simple clip */ @@ -707,15 +707,15 @@ static int gxf_write_header(AVFormatContext *s) if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { if (st->codecpar->codec_id != AV_CODEC_ID_PCM_S16LE) { av_log(s, AV_LOG_ERROR, "only 16 BIT PCM LE allowed for now\n"); - return -1; + return AVERROR(EINVAL); } if (st->codecpar->sample_rate != 48000) { av_log(s, AV_LOG_ERROR, "only 48000hz sampling rate is allowed\n"); - return -1; + return AVERROR(EINVAL); } if (st->codecpar->ch_layout.nb_channels != 1) { av_log(s, AV_LOG_ERROR, "only mono tracks are allowed\n"); - return -1; + return AVERROR(EINVAL); } ret = ff_stream_add_bitstream_filter(st, "pcm_rechunk", "n="AV_STRINGIFY(GXF_SAMPLES_PER_FRAME)); if (ret < 0) @@ -733,7 +733,7 @@ static int gxf_write_header(AVFormatContext *s) } else if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { if (i != 0) { av_log(s, AV_LOG_ERROR, "video stream must be the first track\n"); - return -1; + return AVERROR(EINVAL); } /* FIXME check from time_base ? */ if (st->codecpar->height == 480 || st->codecpar->height == 512) { /* NTSC or NTSC+VBI */ @@ -750,7 +750,7 @@ static int gxf_write_header(AVFormatContext *s) } else { av_log(s, AV_LOG_ERROR, "unsupported video resolution, " "gxf muxer only accepts PAL or NTSC resolutions currently\n"); - return -1; + return AVERROR(EINVAL); } if (!tcr) tcr = av_dict_get(st->metadata, "timecode", NULL, 0); @@ -823,7 +823,7 @@ static int gxf_write_eos_packet(AVIOContext *pb) int64_t pos = avio_tell(pb); gxf_write_packet_header(pb, PKT_EOS); - return updatePacketSize(pb, pos); + return update_packet_size(pb, pos); } static int gxf_write_trailer(AVFormatContext *s) @@ -956,7 +956,7 @@ static int gxf_write_packet(AVFormatContext *s, AVPacket *pkt) gxf->nb_fields += 2; // count fields } - updatePacketSize(pb, pos); + update_packet_size(pb, pos); gxf->packet_count++; if (gxf->packet_count == 100) { diff --git a/libavformat/hca.c b/libavformat/hca.c index 74ac00acc09..713082f8b0f 100644 --- a/libavformat/hca.c +++ b/libavformat/hca.c @@ -19,18 +19,29 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "libavutil/opt.h" #include "libavutil/intreadwrite.h" #include "libavcodec/bytestream.h" #include "avformat.h" +#include "demux.h" #include "internal.h" +#define HCA_MASK 0x7f7f7f7f + +typedef struct HCADemuxContext { + AVClass *class; + int64_t keyl; + int64_t keyh; + int subkey; +} HCADemuxContext; + static int hca_probe(const AVProbeData *p) { - if (AV_RL32(p->buf) != MKTAG('H', 'C', 'A', 0)) + if ((AV_RL32(p->buf) & HCA_MASK) != MKTAG('H', 'C', 'A', 0)) return 0; - if (AV_RL32(p->buf + 8) != MKTAG('f', 'm', 't', 0)) + if ((AV_RL32(p->buf + 8) & HCA_MASK) != MKTAG('f', 'm', 't', 0)) return 0; return AVPROBE_SCORE_MAX / 3; @@ -38,6 +49,7 @@ static int hca_probe(const AVProbeData *p) static int hca_read_header(AVFormatContext *s) { + HCADemuxContext *hca = s->priv_data; AVCodecParameters *par; GetByteContext gb; AVIOContext *pb = s->pb; @@ -60,20 +72,23 @@ static int hca_read_header(AVFormatContext *s) return AVERROR(ENOMEM); par = st->codecpar; - ret = ff_alloc_extradata(par, data_offset); + ret = ff_alloc_extradata(par, data_offset + 10); if (ret < 0) return ret; - ret = avio_read(pb, par->extradata + 8, par->extradata_size - 8); - if (ret < par->extradata_size - 8) + ret = avio_read(pb, par->extradata + 8, par->extradata_size - 8 - 10); + if (ret < par->extradata_size - 8 - 10) return AVERROR(EIO); AV_WL32(par->extradata, MKTAG('H', 'C', 'A', 0)); AV_WB16(par->extradata + 4, version); AV_WB16(par->extradata + 6, data_offset); + AV_WB32(par->extradata + par->extradata_size - 10, hca->keyh); + AV_WB32(par->extradata + par->extradata_size - 6, hca->keyl); + AV_WB16(par->extradata + par->extradata_size - 2, hca->subkey); bytestream2_init(&gb, par->extradata + 8, par->extradata_size - 8); - if (bytestream2_get_le32(&gb) != MKTAG('f', 'm', 't', 0)) + if ((bytestream2_get_le32(&gb) & HCA_MASK) != MKTAG('f', 'm', 't', 0)) return AVERROR_INVALIDDATA; par->codec_type = AVMEDIA_TYPE_AUDIO; @@ -83,7 +98,7 @@ static int hca_read_header(AVFormatContext *s) par->sample_rate = bytestream2_get_be24(&gb); block_count = bytestream2_get_be32(&gb); bytestream2_skip(&gb, 4); - chunk = bytestream2_get_le32(&gb); + chunk = bytestream2_get_le32(&gb) & HCA_MASK; if (chunk == MKTAG('c', 'o', 'm', 'p')) { block_size = bytestream2_get_be16(&gb); } else if (chunk == MKTAG('d', 'e', 'c', 0)) { @@ -113,12 +128,35 @@ static int hca_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_hca_demuxer = { - .name = "hca", - .long_name = NULL_IF_CONFIG_SMALL("CRI HCA"), +#define OFFSET(x) offsetof(HCADemuxContext, x) +static const AVOption hca_options[] = { + { "hca_lowkey", + "Low key used for handling CRI HCA files", OFFSET(keyl), + AV_OPT_TYPE_INT64, {.i64=0}, .min = 0, .max = UINT32_MAX, .flags = AV_OPT_FLAG_DECODING_PARAM, }, + { "hca_highkey", + "High key used for handling CRI HCA files", OFFSET(keyh), + AV_OPT_TYPE_INT64, {.i64=0}, .min = 0, .max = UINT32_MAX, .flags = AV_OPT_FLAG_DECODING_PARAM, }, + { "hca_subkey", + "Subkey used for handling CRI HCA files", OFFSET(subkey), + AV_OPT_TYPE_INT, {.i64=0}, .min = 0, .max = UINT16_MAX, .flags = AV_OPT_FLAG_DECODING_PARAM }, + { NULL }, +}; + +static const AVClass hca_class = { + .class_name = "hca", + .item_name = av_default_item_name, + .option = hca_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFInputFormat ff_hca_demuxer = { + .p.name = "hca", + .p.long_name = NULL_IF_CONFIG_SMALL("CRI HCA"), + .p.priv_class = &hca_class, + .p.extensions = "hca", + .p.flags = AVFMT_GENERIC_INDEX, + .priv_data_size = sizeof(HCADemuxContext), .read_probe = hca_probe, .read_header = hca_read_header, .read_packet = hca_read_packet, - .extensions = "hca", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/hcom.c b/libavformat/hcom.c index 5031f002978..2a8c5244966 100644 --- a/libavformat/hcom.c +++ b/libavformat/hcom.c @@ -21,6 +21,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "pcm.h" @@ -81,9 +82,9 @@ static int hcom_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_hcom_demuxer = { - .name = "hcom", - .long_name = NULL_IF_CONFIG_SMALL("Macintosh HCOM"), +const FFInputFormat ff_hcom_demuxer = { + .p.name = "hcom", + .p.long_name = NULL_IF_CONFIG_SMALL("Macintosh HCOM"), .read_probe = hcom_probe, .read_header = hcom_read_header, .read_packet = ff_pcm_read_packet, diff --git a/libavformat/hdsenc.c b/libavformat/hdsenc.c index 080a873ee8a..17fa0c807ff 100644 --- a/libavformat/hdsenc.c +++ b/libavformat/hdsenc.c @@ -112,7 +112,7 @@ static int parse_header(OutputStream *os, const uint8_t *buf, int buf_size) return 0; } -static int hds_write(void *opaque, uint8_t *buf, int buf_size) +static int hds_write(void *opaque, const uint8_t *buf, int buf_size) { OutputStream *os = opaque; if (os->out) { diff --git a/libavformat/hls.c b/libavformat/hls.c index ce791e0123b..8702113e9f4 100644 --- a/libavformat/hls.c +++ b/libavformat/hls.c @@ -43,6 +43,7 @@ #include "internal.h" #include "avio_internal.h" #include "id3v2.h" +#include "url.h" #include "hls_sample_encryption.h" @@ -539,11 +540,16 @@ static struct rendition *new_rendition(HLSContext *c, struct rendition_info *inf } if (info->assoc_language[0]) { - int langlen = strlen(rend->language); + size_t langlen = strlen(rend->language); if (langlen < sizeof(rend->language) - 3) { + size_t assoc_len; rend->language[langlen] = ','; - strncpy(rend->language + langlen + 1, info->assoc_language, - sizeof(rend->language) - langlen - 2); + assoc_len = av_strlcpy(rend->language + langlen + 1, + info->assoc_language, + sizeof(rend->language) - langlen - 1); + if (langlen + assoc_len + 2 > sizeof(rend->language)) // truncation occurred + av_log(c->ctx, AV_LOG_WARNING, "Truncated rendition language: %s\n", + info->assoc_language); } } @@ -1265,7 +1271,7 @@ static void intercept_id3(struct playlist *pls, uint8_t *buf, if (pls->id3_buf) { /* Now parse all the ID3 tags */ FFIOContext id3ioctx; - ffio_init_context(&id3ioctx, pls->id3_buf, id3_buf_pos, 0, NULL, NULL, NULL, NULL); + ffio_init_read_context(&id3ioctx, pls->id3_buf, id3_buf_pos); handle_id3(&id3ioctx.pub, pls); } @@ -1849,16 +1855,7 @@ static int set_stream_info_from_input_stream(AVStream *st, struct playlist *pls, // copy disposition st->disposition = ist->disposition; - // copy side data - for (int i = 0; i < ist->nb_side_data; i++) { - const AVPacketSideData *sd_src = &ist->side_data[i]; - uint8_t *dst_data; - - dst_data = av_stream_new_side_data(st, sd_src->type, sd_src->size); - if (!dst_data) - return AVERROR(ENOMEM); - memcpy(dst_data, sd_src->data, sd_src->size); - } + av_dict_copy(&st->metadata, ist->metadata, 0); ffstream(st)->need_context_update = 1; @@ -2103,10 +2100,12 @@ static int hls_read_header(AVFormatContext *s) */ if (seg && seg->key_type == KEY_SAMPLE_AES && pls->is_id3_timestamped && pls->audio_setup_info.codec_id != AV_CODEC_ID_NONE) { - void *iter = NULL; - while ((in_fmt = av_demuxer_iterate(&iter))) - if (in_fmt->raw_codec_id == pls->audio_setup_info.codec_id) - break; + av_assert1(pls->audio_setup_info.codec_id == AV_CODEC_ID_AAC || + pls->audio_setup_info.codec_id == AV_CODEC_ID_AC3 || + pls->audio_setup_info.codec_id == AV_CODEC_ID_EAC3); + // Keep this list in sync with ff_hls_senc_read_audio_setup_info() + in_fmt = av_find_input_format(pls->audio_setup_info.codec_id == AV_CODEC_ID_AAC ? "aac" : + pls->audio_setup_info.codec_id == AV_CODEC_ID_AC3 ? "ac3" : "eac3"); } else { pls->ctx->probesize = s->probesize > 0 ? s->probesize : 1024 * 4; pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ? s->max_analyze_duration : 4 * AV_TIME_BASE; @@ -2504,6 +2503,9 @@ static int hls_read_seek(AVFormatContext *s, int stream_index, /* Flush the packet queue of the subdemuxer. */ ff_read_frame_flush(pls->ctx); + /* Reset the init segment so it's re-fetched and served appropiately */ + pls->cur_init_section = NULL; + pls->seek_timestamp = seek_timestamp; pls->seek_flags = flags; @@ -2532,8 +2534,30 @@ static int hls_probe(const AVProbeData *p) if (strstr(p->buf, "#EXT-X-STREAM-INF:") || strstr(p->buf, "#EXT-X-TARGETDURATION:") || - strstr(p->buf, "#EXT-X-MEDIA-SEQUENCE:")) + strstr(p->buf, "#EXT-X-MEDIA-SEQUENCE:")) { + + int mime_ok = p->mime_type && !( + av_strcasecmp(p->mime_type, "application/vnd.apple.mpegurl") && + av_strcasecmp(p->mime_type, "audio/mpegurl") + ); + + int mime_x = p->mime_type && !( + av_strcasecmp(p->mime_type, "audio/x-mpegurl") && + av_strcasecmp(p->mime_type, "application/x-mpegurl") + ); + + if (!mime_ok && + !mime_x && + !av_match_ext (p->filename, "m3u8,m3u") && + ff_match_url_ext(p->filename, "m3u8,m3u") <= 0) { + av_log(NULL, AV_LOG_ERROR, "Not detecting m3u8/hls with non standard extension and non standard mime type\n"); + return 0; + } + if (mime_x) + av_log(NULL, AV_LOG_WARNING, "mime type is not rfc8216 compliant\n"); + return AVPROBE_SCORE_MAX; + } return 0; } @@ -2572,13 +2596,13 @@ static const AVClass hls_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_hls_demuxer = { - .name = "hls", - .long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"), - .priv_class = &hls_class, +const FFInputFormat ff_hls_demuxer = { + .p.name = "hls", + .p.long_name = NULL_IF_CONFIG_SMALL("Apple HTTP Live Streaming"), + .p.priv_class = &hls_class, + .p.flags = AVFMT_NOGENSEARCH | AVFMT_TS_DISCONT | AVFMT_NO_BYTE_SEEK, .priv_data_size = sizeof(HLSContext), - .flags = AVFMT_NOGENSEARCH | AVFMT_TS_DISCONT | AVFMT_NO_BYTE_SEEK, - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = hls_probe, .read_header = hls_read_header, .read_packet = hls_read_packet, diff --git a/libavformat/hls_sample_encryption.c b/libavformat/hls_sample_encryption.c index 089662905b4..f412836d4f2 100644 --- a/libavformat/hls_sample_encryption.c +++ b/libavformat/hls_sample_encryption.c @@ -64,6 +64,7 @@ void ff_hls_senc_read_audio_setup_info(HLSAudioSetupInfo *info, const uint8_t *b info->codec_tag = AV_RL32(buf); + /* Always keep this list in sync with the one from hls_read_header() */ if (info->codec_tag == MKTAG('z','a','a','c')) info->codec_id = AV_CODEC_ID_AAC; else if (info->codec_tag == MKTAG('z','a','c','3')) @@ -105,8 +106,7 @@ int ff_hls_senc_parse_audio_setup_info(AVStream *st, HLSAudioSetupInfo *info) ret = avpriv_ac3_parse_header(&ac3hdr, info->setup_data, info->setup_data_length); if (ret < 0) { - if (ret != AVERROR(ENOMEM)) - av_free(ac3hdr); + av_free(ac3hdr); return ret; } @@ -317,8 +317,7 @@ static int get_next_ac3_eac3_sync_frame(CodecParserContext *ctx, AudioFrame *fra ret = avpriv_ac3_parse_header(&hdr, frame->data, ctx->buf_end - frame->data); if (ret < 0) { - if (ret != AVERROR(ENOMEM)) - av_free(hdr); + av_free(hdr); return ret; } diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index e1f96feda3e..1ec4d504855 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -23,16 +23,11 @@ #include "config.h" #include "config_components.h" #include +#include #if HAVE_UNISTD_H #include #endif -#if CONFIG_GCRYPT -#include -#elif CONFIG_OPENSSL -#include -#endif - #include "libavutil/avassert.h" #include "libavutil/mathematics.h" #include "libavutil/avstring.h" @@ -40,10 +35,11 @@ #include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/log.h" +#include "libavutil/random_seed.h" #include "libavutil/time.h" #include "libavutil/time_internal.h" -#include "libavcodec/avcodec.h" +#include "libavcodec/defs.h" #include "avformat.h" #include "avio_internal.h" @@ -55,6 +51,7 @@ #include "internal.h" #include "mux.h" #include "os_support.h" +#include "url.h" typedef enum { HLS_START_SEQUENCE_AS_START_NUMBER = 0, @@ -355,20 +352,30 @@ static void write_codec_attr(AVStream *st, VariantStream *vs) if (st->codecpar->codec_id == AV_CODEC_ID_H264) { uint8_t *data = st->codecpar->extradata; - if (data && (data[0] | data[1] | data[2]) == 0 && data[3] == 1 && (data[4] & 0x1F) == 7) { + if (data) { + const uint8_t *p; + + if (AV_RB32(data) == 0x01 && (data[4] & 0x1F) == 7) + p = &data[5]; + else if (AV_RB24(data) == 0x01 && (data[3] & 0x1F) == 7) + p = &data[4]; + else if (data[0] == 0x01) /* avcC */ + p = &data[1]; + else + goto fail; snprintf(attr, sizeof(attr), - "avc1.%02x%02x%02x", data[5], data[6], data[7]); + "avc1.%02x%02x%02x", p[0], p[1], p[2]); } else { goto fail; } } else if (st->codecpar->codec_id == AV_CODEC_ID_HEVC) { uint8_t *data = st->codecpar->extradata; - int profile = FF_PROFILE_UNKNOWN; - int level = FF_LEVEL_UNKNOWN; + int profile = AV_PROFILE_UNKNOWN; + int level = AV_LEVEL_UNKNOWN; - if (st->codecpar->profile != FF_PROFILE_UNKNOWN) + if (st->codecpar->profile != AV_PROFILE_UNKNOWN) profile = st->codecpar->profile; - if (st->codecpar->level != FF_LEVEL_UNKNOWN) + if (st->codecpar->level != AV_LEVEL_UNKNOWN) level = st->codecpar->level; /* check the boundary of data which from current position is small than extradata_size */ @@ -401,8 +408,8 @@ static void write_codec_attr(AVStream *st, VariantStream *vs) data++; } if (st->codecpar->codec_tag == MKTAG('h','v','c','1') && - profile != FF_PROFILE_UNKNOWN && - level != FF_LEVEL_UNKNOWN) { + profile != AV_PROFILE_UNKNOWN && + level != AV_LEVEL_UNKNOWN) { snprintf(attr, sizeof(attr), "%s.%d.4.L%d.B01", av_fourcc2str(st->codecpar->codec_tag), profile, level); } else goto fail; @@ -411,8 +418,11 @@ static void write_codec_attr(AVStream *st, VariantStream *vs) } else if (st->codecpar->codec_id == AV_CODEC_ID_MP3) { snprintf(attr, sizeof(attr), "mp4a.40.34"); } else if (st->codecpar->codec_id == AV_CODEC_ID_AAC) { - /* TODO : For HE-AAC, HE-AACv2, the last digit needs to be set to 5 and 29 respectively */ - snprintf(attr, sizeof(attr), "mp4a.40.2"); + if (st->codecpar->profile != AV_PROFILE_UNKNOWN) + snprintf(attr, sizeof(attr), "mp4a.40.%d", st->codecpar->profile+1); + else + // This is for backward compatibility with the previous implementation. + snprintf(attr, sizeof(attr), "mp4a.40.2"); } else if (st->codecpar->codec_id == AV_CODEC_ID_AC3) { snprintf(attr, sizeof(attr), "ac-3"); } else if (st->codecpar->codec_id == AV_CODEC_ID_EAC3) { @@ -700,20 +710,6 @@ static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls, return ret; } -static int randomize(uint8_t *buf, int len) -{ -#if CONFIG_GCRYPT - gcry_randomize(buf, len, GCRY_VERY_STRONG_RANDOM); - return 0; -#elif CONFIG_OPENSSL - if (RAND_bytes(buf, len)) - return 0; -#else - return AVERROR(ENOSYS); -#endif - return AVERROR(EINVAL); -} - static int do_encrypt(AVFormatContext *s, VariantStream *vs) { HLSContext *hls = s->priv_data; @@ -765,7 +761,7 @@ static int do_encrypt(AVFormatContext *s, VariantStream *vs) if (!*hls->key_string) { AVDictionary *options = NULL; if (!hls->key) { - if ((ret = randomize(key, sizeof(key))) < 0) { + if ((ret = av_random_bytes(key, sizeof(key))) < 0) { av_log(s, AV_LOG_ERROR, "Cannot generate a strong random key\n"); return ret; } @@ -869,11 +865,6 @@ static int hls_mux_init(AVFormatContext *s, VariantStream *vs) oc->max_delay = s->max_delay; oc->opaque = s->opaque; oc->io_open = s->io_open; -#if FF_API_AVFORMAT_IO_CLOSE -FF_DISABLE_DEPRECATION_WARNINGS - oc->io_close = s->io_close; -FF_ENABLE_DEPRECATION_WARNINGS -#endif oc->io_close2 = s->io_close2; oc->strict_std_compliance = s->strict_std_compliance; av_dict_copy(&oc->metadata, s->metadata, 0); @@ -1005,7 +996,7 @@ static int sls_flags_filename_process(struct AVFormatContext *s, HLSContext *hls 't', (int64_t)round(duration * HLS_MICROSECOND_UNIT)) < 1) { av_log(hls, AV_LOG_ERROR, "Invalid second level segment filename template '%s', " - "you can try to remove second_level_segment_time flag\n", + "you can try to remove second_level_segment_duration flag\n", vs->avf->url); av_freep(&filename); return AVERROR(EINVAL); @@ -1098,7 +1089,7 @@ static int sls_flag_use_localtime_filename(AVFormatContext *oc, HLSContext *c, V char *filename = NULL; if (replace_int_data_in_filename(&filename, oc->url, 't', 0) < 1) { av_log(c, AV_LOG_ERROR, "Invalid second level segment filename template '%s', " - "you can try to remove second_level_segment_time flag\n", + "you can try to remove second_level_segment_duration flag\n", oc->url); av_freep(&filename); return AVERROR(EINVAL); @@ -1360,16 +1351,17 @@ static const char* get_relative_url(const char *master_url, const char *media_ur static int64_t get_stream_bit_rate(AVStream *stream) { - AVCPBProperties *props = (AVCPBProperties*)av_stream_get_side_data( - stream, - AV_PKT_DATA_CPB_PROPERTIES, - NULL + const AVPacketSideData *sd = av_packet_side_data_get( + stream->codecpar->coded_side_data, stream->codecpar->nb_coded_side_data, + AV_PKT_DATA_CPB_PROPERTIES ); if (stream->codecpar->bit_rate) return stream->codecpar->bit_rate; - else if (props) + else if (sd) { + AVCPBProperties *props = (AVCPBProperties*)sd->data; return props->max_bitrate; + } return 0; } @@ -1392,6 +1384,7 @@ static int create_master_playlist(AVFormatContext *s, int is_file_proto = proto && !strcmp(proto, "file"); int use_temp_file = is_file_proto && ((hls->flags & HLS_TEMP_FILE) || hls->master_publish_rate); char temp_filename[MAX_URL_SIZE]; + int nb_channels; input_vs->m3u8_created = 1; if (!hls->master_m3u8_created) { @@ -1440,8 +1433,13 @@ static int create_master_playlist(AVFormatContext *s, av_log(s, AV_LOG_ERROR, "Unable to find relative URL\n"); goto fail; } + nb_channels = 0; + for (j = 0; j < vs->nb_streams; j++) + if (vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) + if (vs->streams[j]->codecpar->ch_layout.nb_channels > nb_channels) + nb_channels = vs->streams[j]->codecpar->ch_layout.nb_channels; - ff_hls_write_audio_rendition(hls->m3u8_out, vs->agroup, m3u8_rel_name, vs->language, i, hls->has_default_key ? vs->is_default : 1); + ff_hls_write_audio_rendition(hls->m3u8_out, vs->agroup, m3u8_rel_name, vs->language, i, hls->has_default_key ? vs->is_default : 1, nb_channels); } /* For variant streams with video add #EXT-X-STREAM-INF tag with attributes*/ @@ -1587,7 +1585,9 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) set_http_options(s, &options, hls); snprintf(temp_filename, sizeof(temp_filename), use_temp_file ? "%s.tmp" : "%s", vs->m3u8_name); - if ((ret = hlsenc_io_open(s, byterange_mode ? &hls->m3u8_out : &vs->out, temp_filename, &options)) < 0) { + ret = hlsenc_io_open(s, byterange_mode ? &hls->m3u8_out : &vs->out, temp_filename, &options); + av_dict_free(&options); + if (ret < 0) { if (hls->ignore_io_errors) ret = 0; goto fail; @@ -1642,8 +1642,11 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) ff_hls_write_end_list(byterange_mode ? hls->m3u8_out : vs->out); if (vs->vtt_m3u8_name) { + set_http_options(vs->vtt_avf, &options, hls); snprintf(temp_vtt_filename, sizeof(temp_vtt_filename), use_temp_file ? "%s.tmp" : "%s", vs->vtt_m3u8_name); - if ((ret = hlsenc_io_open(s, &hls->sub_m3u8_out, temp_vtt_filename, &options)) < 0) { + ret = hlsenc_io_open(s, &hls->sub_m3u8_out, temp_vtt_filename, &options); + av_dict_free(&options); + if (ret < 0) { if (hls->ignore_io_errors) ret = 0; goto fail; @@ -1870,18 +1873,23 @@ static int hls_start(AVFormatContext *s, VariantStream *vs) static const char * get_default_pattern_localtime_fmt(AVFormatContext *s) { + HLSContext *hls = s->priv_data; +#if HAVE_LIBC_MSVCRT + // no %s support on MSVC, which invokes the invalid parameter handler + // on unsupported format strings, instead of returning an error + int strftime_s_supported = 0; +#else char b[21]; time_t t = time(NULL); - struct tm *p, tmbuf; - HLSContext *hls = s->priv_data; - - p = localtime_r(&t, &tmbuf); + struct tm tmbuf, *p = localtime_r(&t, &tmbuf); // no %s support when strftime returned error or left format string unchanged - // also no %s support on MSVC, which invokes the invalid parameter handler on unsupported format strings, instead of returning an error + int strftime_s_supported = strftime(b, sizeof(b), "%s", p) && strcmp(b, "%s"); +#endif + if (hls->segment_type == SEGMENT_TYPE_FMP4) { - return (HAVE_LIBC_MSVCRT || !strftime(b, sizeof(b), "%s", p) || !strcmp(b, "%s")) ? "-%Y%m%d%H%M%S.m4s" : "-%s.m4s"; + return strftime_s_supported ? "-%s.m4s" : "-%Y%m%d%H%M%S.m4s"; } - return (HAVE_LIBC_MSVCRT || !strftime(b, sizeof(b), "%s", p) || !strcmp(b, "%s")) ? "-%Y%m%d%H%M%S.ts" : "-%s.ts"; + return strftime_s_supported ? "-%s.ts" : "-%Y%m%d%H%M%S.ts"; } static int append_postfix(char *name, int name_buf_len, int i) @@ -2359,7 +2367,7 @@ static int hls_write_header(AVFormatContext *s) } } - return ret; + return 0; } static int hls_init_file_resend(AVFormatContext *s, VariantStream *vs) @@ -2593,8 +2601,10 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) " will retry with a new http session.\n"); ff_format_io_close(s, &vs->out); ret = hlsenc_io_open(s, &vs->out, filename, &options); - reflush_dynbuf(vs, &range_length); - ret = hlsenc_io_close(s, &vs->out, filename); + if (ret >= 0) { + reflush_dynbuf(vs, &range_length); + ret = hlsenc_io_close(s, &vs->out, filename); + } } av_dict_free(&options); av_freep(&vs->temp_buffer); @@ -2605,6 +2615,9 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) hls_rename_temp_file(s, oc); } + if (ret < 0) + return ret; + old_filename = av_strdup(oc->url); if (!old_filename) { return AVERROR(ENOMEM); @@ -2947,7 +2960,7 @@ static int hls_init(AVFormatContext *s) av_log(hls, AV_LOG_DEBUG, "start_number evaluated to %"PRId64"\n", hls->start_sequence); } - hls->recording_time = hls->init_time ? hls->init_time : hls->time; + hls->recording_time = hls->init_time && hls->max_nb_segments > 0 ? hls->init_time : hls->time; if (hls->flags & HLS_SPLIT_BY_TIME && hls->flags & HLS_INDEPENDENT_SEGMENTS) { // Independent segments cannot be guaranteed when splitting by time @@ -3132,38 +3145,38 @@ static const AVOption options[] = { {"hls_enc_key_url", "url to access the key to decrypt the segments", OFFSET(key_url), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, {"hls_enc_iv", "hex-coded 16 byte initialization vector", OFFSET(iv), AV_OPT_TYPE_STRING, .flags = E}, {"hls_subtitle_path", "set path of hls subtitles", OFFSET(subtitle_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, - {"hls_segment_type", "set hls segment files type", OFFSET(segment_type), AV_OPT_TYPE_INT, {.i64 = SEGMENT_TYPE_MPEGTS }, 0, SEGMENT_TYPE_FMP4, E, "segment_type"}, - {"mpegts", "make segment file to mpegts files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MPEGTS }, 0, UINT_MAX, E, "segment_type"}, - {"fmp4", "make segment file to fragment mp4 files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_FMP4 }, 0, UINT_MAX, E, "segment_type"}, + {"hls_segment_type", "set hls segment files type", OFFSET(segment_type), AV_OPT_TYPE_INT, {.i64 = SEGMENT_TYPE_MPEGTS }, 0, SEGMENT_TYPE_FMP4, E, .unit = "segment_type"}, + {"mpegts", "make segment file to mpegts files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MPEGTS }, 0, UINT_MAX, E, .unit = "segment_type"}, + {"fmp4", "make segment file to fragment mp4 files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_FMP4 }, 0, UINT_MAX, E, .unit = "segment_type"}, {"hls_fmp4_init_filename", "set fragment mp4 file init filename", OFFSET(fmp4_init_filename), AV_OPT_TYPE_STRING, {.str = "init.mp4"}, 0, 0, E}, {"hls_fmp4_init_resend", "resend fragment mp4 init file after refresh m3u8 every time", OFFSET(resend_init_file), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, - {"hls_flags", "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "flags"}, - {"single_file", "generate a single media file indexed with byte ranges", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SINGLE_FILE }, 0, UINT_MAX, E, "flags"}, - {"temp_file", "write segment and playlist to temporary file and rename when complete", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_TEMP_FILE }, 0, UINT_MAX, E, "flags"}, - {"delete_segments", "delete segment files that are no longer part of the playlist", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DELETE_SEGMENTS }, 0, UINT_MAX, E, "flags"}, - {"round_durations", "round durations in m3u8 to whole numbers", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_ROUND_DURATIONS }, 0, UINT_MAX, E, "flags"}, - {"discont_start", "start the playlist with a discontinuity tag", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DISCONT_START }, 0, UINT_MAX, E, "flags"}, - {"omit_endlist", "Do not append an endlist when ending stream", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_OMIT_ENDLIST }, 0, UINT_MAX, E, "flags"}, - {"split_by_time", "split the hls segment by time which user set by hls_time", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SPLIT_BY_TIME }, 0, UINT_MAX, E, "flags"}, - {"append_list", "append the new segments into old hls segment list", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_APPEND_LIST }, 0, UINT_MAX, E, "flags"}, - {"program_date_time", "add EXT-X-PROGRAM-DATE-TIME", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PROGRAM_DATE_TIME }, 0, UINT_MAX, E, "flags"}, - {"second_level_segment_index", "include segment index in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_INDEX }, 0, UINT_MAX, E, "flags"}, - {"second_level_segment_duration", "include segment duration in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_DURATION }, 0, UINT_MAX, E, "flags"}, - {"second_level_segment_size", "include segment size in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_SIZE }, 0, UINT_MAX, E, "flags"}, - {"periodic_rekey", "reload keyinfo file periodically for re-keying", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PERIODIC_REKEY }, 0, UINT_MAX, E, "flags"}, - {"independent_segments", "add EXT-X-INDEPENDENT-SEGMENTS, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_INDEPENDENT_SEGMENTS }, 0, UINT_MAX, E, "flags"}, - {"iframes_only", "add EXT-X-I-FRAMES-ONLY, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_I_FRAMES_ONLY }, 0, UINT_MAX, E, "flags"}, + {"hls_flags", "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, .unit = "flags"}, + {"single_file", "generate a single media file indexed with byte ranges", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SINGLE_FILE }, 0, UINT_MAX, E, .unit = "flags"}, + {"temp_file", "write segment and playlist to temporary file and rename when complete", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_TEMP_FILE }, 0, UINT_MAX, E, .unit = "flags"}, + {"delete_segments", "delete segment files that are no longer part of the playlist", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DELETE_SEGMENTS }, 0, UINT_MAX, E, .unit = "flags"}, + {"round_durations", "round durations in m3u8 to whole numbers", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_ROUND_DURATIONS }, 0, UINT_MAX, E, .unit = "flags"}, + {"discont_start", "start the playlist with a discontinuity tag", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_DISCONT_START }, 0, UINT_MAX, E, .unit = "flags"}, + {"omit_endlist", "Do not append an endlist when ending stream", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_OMIT_ENDLIST }, 0, UINT_MAX, E, .unit = "flags"}, + {"split_by_time", "split the hls segment by time which user set by hls_time", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SPLIT_BY_TIME }, 0, UINT_MAX, E, .unit = "flags"}, + {"append_list", "append the new segments into old hls segment list", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_APPEND_LIST }, 0, UINT_MAX, E, .unit = "flags"}, + {"program_date_time", "add EXT-X-PROGRAM-DATE-TIME", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PROGRAM_DATE_TIME }, 0, UINT_MAX, E, .unit = "flags"}, + {"second_level_segment_index", "include segment index in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_INDEX }, 0, UINT_MAX, E, .unit = "flags"}, + {"second_level_segment_duration", "include segment duration in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_DURATION }, 0, UINT_MAX, E, .unit = "flags"}, + {"second_level_segment_size", "include segment size in segment filenames when use_localtime", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SECOND_LEVEL_SEGMENT_SIZE }, 0, UINT_MAX, E, .unit = "flags"}, + {"periodic_rekey", "reload keyinfo file periodically for re-keying", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_PERIODIC_REKEY }, 0, UINT_MAX, E, .unit = "flags"}, + {"independent_segments", "add EXT-X-INDEPENDENT-SEGMENTS, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_INDEPENDENT_SEGMENTS }, 0, UINT_MAX, E, .unit = "flags"}, + {"iframes_only", "add EXT-X-I-FRAMES-ONLY, whenever applicable", 0, AV_OPT_TYPE_CONST, { .i64 = HLS_I_FRAMES_ONLY }, 0, UINT_MAX, E, .unit = "flags"}, {"strftime", "set filename expansion with strftime at segment creation", OFFSET(use_localtime), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, {"strftime_mkdir", "create last directory component in strftime-generated filename", OFFSET(use_localtime_mkdir), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, - {"hls_playlist_type", "set the HLS playlist type", OFFSET(pl_type), AV_OPT_TYPE_INT, {.i64 = PLAYLIST_TYPE_NONE }, 0, PLAYLIST_TYPE_NB-1, E, "pl_type" }, - {"event", "EVENT playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_EVENT }, INT_MIN, INT_MAX, E, "pl_type" }, - {"vod", "VOD playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_VOD }, INT_MIN, INT_MAX, E, "pl_type" }, + {"hls_playlist_type", "set the HLS playlist type", OFFSET(pl_type), AV_OPT_TYPE_INT, {.i64 = PLAYLIST_TYPE_NONE }, 0, PLAYLIST_TYPE_NB-1, E, .unit = "pl_type" }, + {"event", "EVENT playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_EVENT }, INT_MIN, INT_MAX, E, .unit = "pl_type" }, + {"vod", "VOD playlist", 0, AV_OPT_TYPE_CONST, {.i64 = PLAYLIST_TYPE_VOD }, INT_MIN, INT_MAX, E, .unit = "pl_type" }, {"method", "set the HTTP method(default: PUT)", OFFSET(method), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, - {"hls_start_number_source", "set source of first number in sequence", OFFSET(start_sequence_source_type), AV_OPT_TYPE_INT, {.i64 = HLS_START_SEQUENCE_AS_START_NUMBER }, 0, HLS_START_SEQUENCE_LAST-1, E, "start_sequence_source_type" }, - {"generic", "start_number value (default)", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_START_NUMBER }, INT_MIN, INT_MAX, E, "start_sequence_source_type" }, - {"epoch", "seconds since epoch", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH }, INT_MIN, INT_MAX, E, "start_sequence_source_type" }, - {"epoch_us", "microseconds since epoch", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_MICROSECONDS_SINCE_EPOCH }, INT_MIN, INT_MAX, E, "start_sequence_source_type" }, - {"datetime", "current datetime as YYYYMMDDhhmmss", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_FORMATTED_DATETIME }, INT_MIN, INT_MAX, E, "start_sequence_source_type" }, + {"hls_start_number_source", "set source of first number in sequence", OFFSET(start_sequence_source_type), AV_OPT_TYPE_INT, {.i64 = HLS_START_SEQUENCE_AS_START_NUMBER }, 0, HLS_START_SEQUENCE_LAST-1, E, .unit = "start_sequence_source_type" }, + {"generic", "start_number value (default)", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_START_NUMBER }, INT_MIN, INT_MAX, E, .unit = "start_sequence_source_type" }, + {"epoch", "seconds since epoch", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH }, INT_MIN, INT_MAX, E, .unit = "start_sequence_source_type" }, + {"epoch_us", "microseconds since epoch", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_MICROSECONDS_SINCE_EPOCH }, INT_MIN, INT_MAX, E, .unit = "start_sequence_source_type" }, + {"datetime", "current datetime as YYYYMMDDhhmmss", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_FORMATTED_DATETIME }, INT_MIN, INT_MAX, E, .unit = "start_sequence_source_type" }, {"http_user_agent", "override User-Agent field in HTTP header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, {"var_stream_map", "Variant stream map string", OFFSET(var_stream_map), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, {"cc_stream_map", "Closed captions stream map string", OFFSET(cc_stream_map), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, @@ -3191,8 +3204,13 @@ const FFOutputFormat ff_hls_muxer = { .p.audio_codec = AV_CODEC_ID_AAC, .p.video_codec = AV_CODEC_ID_H264, .p.subtitle_codec = AV_CODEC_ID_WEBVTT, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_NODIMENSIONS, +#else + .p.flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_NODIMENSIONS, +#endif .p.priv_class = &hls_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, .priv_data_size = sizeof(HLSContext), .init = hls_init, .write_header = hls_write_header, diff --git a/libavformat/hlsplaylist.c b/libavformat/hlsplaylist.c index 0e1dcc087f0..4f35d0388fb 100644 --- a/libavformat/hlsplaylist.c +++ b/libavformat/hlsplaylist.c @@ -22,6 +22,7 @@ #include "config.h" #include +#include #include "libavutil/time_internal.h" @@ -38,7 +39,7 @@ void ff_hls_write_playlist_version(AVIOContext *out, int version) void ff_hls_write_audio_rendition(AVIOContext *out, const char *agroup, const char *filename, const char *language, - int name_id, int is_default) + int name_id, int is_default, int nb_channels) { if (!out || !agroup || !filename) return; @@ -48,6 +49,9 @@ void ff_hls_write_audio_rendition(AVIOContext *out, const char *agroup, if (language) { avio_printf(out, "LANGUAGE=\"%s\",", language); } + if (nb_channels) { + avio_printf(out, "CHANNELS=\"%d\",", nb_channels); + } avio_printf(out, "URI=\"%s\"\n", filename); } diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h index 1928fe787dc..c2744c227c0 100644 --- a/libavformat/hlsplaylist.h +++ b/libavformat/hlsplaylist.h @@ -38,7 +38,7 @@ typedef enum { void ff_hls_write_playlist_version(AVIOContext *out, int version); void ff_hls_write_audio_rendition(AVIOContext *out, const char *agroup, const char *filename, const char *language, - int name_id, int is_default); + int name_id, int is_default, int nb_channels); void ff_hls_write_subtitle_rendition(AVIOContext *out, const char *sgroup, const char *filename, const char *language, int name_id, int is_default); diff --git a/libavformat/hlsproto.c b/libavformat/hlsproto.c index 6a2765bbe96..4db61dee305 100644 --- a/libavformat/hlsproto.c +++ b/libavformat/hlsproto.c @@ -27,7 +27,6 @@ #include "libavutil/avstring.h" #include "libavutil/time.h" -#include "avformat.h" #include "avio_internal.h" #include "internal.h" #include "url.h" diff --git a/libavformat/hnm.c b/libavformat/hnm.c index 97990b26733..425dadc5e31 100644 --- a/libavformat/hnm.c +++ b/libavformat/hnm.c @@ -24,6 +24,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define HNM4_TAG MKTAG('H', 'N', 'M', '4') @@ -113,6 +114,8 @@ static int hnm_read_packet(AVFormatContext *s, AVPacket *pkt) if (hnm->superchunk_remaining == 0) { /* parse next superchunk */ superchunk_size = avio_rl24(pb); + if (superchunk_size < 4) + return AVERROR_INVALIDDATA; avio_skip(pb, 1); hnm->superchunk_remaining = superchunk_size - 4; @@ -123,7 +126,7 @@ static int hnm_read_packet(AVFormatContext *s, AVPacket *pkt) chunk_id = avio_rl16(pb); avio_skip(pb, 2); - if (chunk_size > hnm->superchunk_remaining || !chunk_size) { + if (chunk_size > hnm->superchunk_remaining || chunk_size < 8) { av_log(s, AV_LOG_ERROR, "invalid chunk size: %"PRIu32", offset: %"PRId64"\n", chunk_size, avio_tell(pb)); @@ -158,12 +161,12 @@ static int hnm_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_hnm_demuxer = { - .name = "hnm", - .long_name = NULL_IF_CONFIG_SMALL("Cryo HNM v4"), +const FFInputFormat ff_hnm_demuxer = { + .p.name = "hnm", + .p.long_name = NULL_IF_CONFIG_SMALL("Cryo HNM v4"), + .p.flags = AVFMT_NO_BYTE_SEEK | AVFMT_NOGENSEARCH | AVFMT_NOBINSEARCH, .priv_data_size = sizeof(Hnm4DemuxContext), .read_probe = hnm_probe, .read_header = hnm_read_header, .read_packet = hnm_read_packet, - .flags = AVFMT_NO_BYTE_SEEK | AVFMT_NOGENSEARCH | AVFMT_NOBINSEARCH }; diff --git a/libavformat/http.c b/libavformat/http.c index 7bce8215354..9e933b18b3e 100644 --- a/libavformat/http.c +++ b/libavformat/http.c @@ -22,6 +22,7 @@ #include "config.h" #include "config_components.h" +#include #if CONFIG_ZLIB #include #endif /* CONFIG_ZLIB */ @@ -160,9 +161,9 @@ static const AVOption options[] = { { "icy_metadata_headers", "return ICY metadata headers", OFFSET(icy_metadata_headers), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT }, { "icy_metadata_packet", "return current ICY metadata packet", OFFSET(icy_metadata_packet), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_EXPORT }, { "metadata", "metadata read from the bitstream", OFFSET(metadata), AV_OPT_TYPE_DICT, {0}, 0, 0, AV_OPT_FLAG_EXPORT }, - { "auth_type", "HTTP authentication type", OFFSET(auth_state.auth_type), AV_OPT_TYPE_INT, { .i64 = HTTP_AUTH_NONE }, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D | E, "auth_type"}, - { "none", "No auth method set, autodetect", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_NONE }, 0, 0, D | E, "auth_type"}, - { "basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_BASIC }, 0, 0, D | E, "auth_type"}, + { "auth_type", "HTTP authentication type", OFFSET(auth_state.auth_type), AV_OPT_TYPE_INT, { .i64 = HTTP_AUTH_NONE }, HTTP_AUTH_NONE, HTTP_AUTH_BASIC, D | E, .unit = "auth_type"}, + { "none", "No auth method set, autodetect", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_NONE }, 0, 0, D | E, .unit = "auth_type"}, + { "basic", "HTTP basic authentication", 0, AV_OPT_TYPE_CONST, { .i64 = HTTP_AUTH_BASIC }, 0, 0, D | E, .unit = "auth_type"}, { "send_expect_100", "Force sending an Expect: 100-continue header for POST", OFFSET(send_expect_100), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, E }, { "location", "The actual location of the data received", OFFSET(location), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D | E }, { "offset", "initial byte offset", OFFSET(off), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, INT64_MAX, D }, @@ -1205,7 +1206,7 @@ static int process_line(URLContext *h, char *line, int line_count) } } else if (!av_strcasecmp(tag, "Content-Type")) { av_free(s->mime_type); - s->mime_type = av_strdup(p); + s->mime_type = av_get_token((const char **)&p, ";"); } else if (!av_strcasecmp(tag, "Set-Cookie")) { if (parse_cookie(s, p, &s->cookie_dict)) av_log(h, AV_LOG_WARNING, "Unable to parse '%s'\n", p); @@ -1293,9 +1294,9 @@ static int get_cookies(HTTPContext *s, char **cookies, const char *path, goto skip_cookie; } - // ensure this cookie matches the path + // if a cookie path is provided, ensure the request path is within that path e = av_dict_get(cookie_params, "path", NULL, 0); - if (!e || av_strncasecmp(path, e->value, strlen(e->value))) + if (e && av_strncasecmp(path, e->value, strlen(e->value))) goto skip_cookie; // cookie parameters match, so copy the value diff --git a/libavformat/httpauth.c b/libavformat/httpauth.c index 0a98ff80a57..97809283570 100644 --- a/libavformat/httpauth.c +++ b/libavformat/httpauth.c @@ -26,7 +26,6 @@ #include "libavutil/random_seed.h" #include "libavutil/md5.h" #include "urldecode.h" -#include "avformat.h" static void handle_basic_params(HTTPAuthState *state, const char *key, int key_len, char **dest, int *dest_len) diff --git a/libavformat/iamf.c b/libavformat/iamf.c new file mode 100644 index 00000000000..5de70dc0828 --- /dev/null +++ b/libavformat/iamf.c @@ -0,0 +1,125 @@ +/* + * Immersive Audio Model and Formats common helpers and structs + * Copyright (c) 2023 James Almer + * + * 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/iamf.h" +#include "libavutil/mem.h" +#include "iamf.h" + +const AVChannelLayout ff_iamf_scalable_ch_layouts[10] = { + AV_CHANNEL_LAYOUT_MONO, + AV_CHANNEL_LAYOUT_STEREO, + // "Loudspeaker configuration for Sound System B" + AV_CHANNEL_LAYOUT_5POINT1_BACK, + // "Loudspeaker configuration for Sound System C" + AV_CHANNEL_LAYOUT_5POINT1POINT2_BACK, + // "Loudspeaker configuration for Sound System D" + AV_CHANNEL_LAYOUT_5POINT1POINT4_BACK, + // "Loudspeaker configuration for Sound System I" + AV_CHANNEL_LAYOUT_7POINT1, + // "Loudspeaker configuration for Sound System I" + Ltf + Rtf + AV_CHANNEL_LAYOUT_7POINT1POINT2, + // "Loudspeaker configuration for Sound System J" + AV_CHANNEL_LAYOUT_7POINT1POINT4_BACK, + // Front subset of "Loudspeaker configuration for Sound System J" + AV_CHANNEL_LAYOUT_3POINT1POINT2, + // Binaural + AV_CHANNEL_LAYOUT_STEREO, +}; + +const struct IAMFSoundSystemMap ff_iamf_sound_system_map[13] = { + { SOUND_SYSTEM_A_0_2_0, AV_CHANNEL_LAYOUT_STEREO }, + { SOUND_SYSTEM_B_0_5_0, AV_CHANNEL_LAYOUT_5POINT1_BACK }, + { SOUND_SYSTEM_C_2_5_0, AV_CHANNEL_LAYOUT_5POINT1POINT2_BACK }, + { SOUND_SYSTEM_D_4_5_0, AV_CHANNEL_LAYOUT_5POINT1POINT4_BACK }, + { SOUND_SYSTEM_E_4_5_1, + { + .nb_channels = 11, + .order = AV_CHANNEL_ORDER_NATIVE, + .u.mask = AV_CH_LAYOUT_5POINT1POINT4_BACK | AV_CH_BOTTOM_FRONT_CENTER, + }, + }, + { SOUND_SYSTEM_F_3_7_0, AV_CHANNEL_LAYOUT_7POINT2POINT3 }, + { SOUND_SYSTEM_G_4_9_0, AV_CHANNEL_LAYOUT_9POINT1POINT4_BACK }, + { SOUND_SYSTEM_H_9_10_3, AV_CHANNEL_LAYOUT_22POINT2 }, + { SOUND_SYSTEM_I_0_7_0, AV_CHANNEL_LAYOUT_7POINT1 }, + { SOUND_SYSTEM_J_4_7_0, AV_CHANNEL_LAYOUT_7POINT1POINT4_BACK }, + { SOUND_SYSTEM_10_2_7_0, AV_CHANNEL_LAYOUT_7POINT1POINT2 }, + { SOUND_SYSTEM_11_2_3_0, AV_CHANNEL_LAYOUT_3POINT1POINT2 }, + { SOUND_SYSTEM_12_0_1_0, AV_CHANNEL_LAYOUT_MONO }, +}; + +void ff_iamf_free_audio_element(IAMFAudioElement **paudio_element) +{ + IAMFAudioElement *audio_element = *paudio_element; + + if (!audio_element) + return; + + for (int i = 0; i < audio_element->nb_substreams; i++) + avcodec_parameters_free(&audio_element->substreams[i].codecpar); + av_free(audio_element->substreams); + av_free(audio_element->layers); + av_iamf_audio_element_free(&audio_element->element); + av_freep(paudio_element); +} + +void ff_iamf_free_mix_presentation(IAMFMixPresentation **pmix_presentation) +{ + IAMFMixPresentation *mix_presentation = *pmix_presentation; + + if (!mix_presentation) + return; + + for (int i = 0; i < mix_presentation->count_label; i++) + av_free(mix_presentation->language_label[i]); + av_free(mix_presentation->language_label); + av_iamf_mix_presentation_free(&mix_presentation->mix); + av_freep(pmix_presentation); +} + +void ff_iamf_uninit_context(IAMFContext *c) +{ + if (!c) + return; + + for (int i = 0; i < c->nb_codec_configs; i++) { + av_free(c->codec_configs[i]->extradata); + av_free(c->codec_configs[i]); + } + av_freep(&c->codec_configs); + c->nb_codec_configs = 0; + + for (int i = 0; i < c->nb_audio_elements; i++) + ff_iamf_free_audio_element(&c->audio_elements[i]); + av_freep(&c->audio_elements); + c->nb_audio_elements = 0; + + for (int i = 0; i < c->nb_mix_presentations; i++) + ff_iamf_free_mix_presentation(&c->mix_presentations[i]); + av_freep(&c->mix_presentations); + c->nb_mix_presentations = 0; + + for (int i = 0; i < c->nb_param_definitions; i++) + av_free(c->param_definitions[i]); + av_freep(&c->param_definitions); + c->nb_param_definitions = 0; +} diff --git a/libavformat/iamf.h b/libavformat/iamf.h new file mode 100644 index 00000000000..fd8b57a096b --- /dev/null +++ b/libavformat/iamf.h @@ -0,0 +1,202 @@ +/* + * Immersive Audio Model and Formats common helpers and structs + * Copyright (c) 2023 James Almer + * + * 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 + */ + +#ifndef AVFORMAT_IAMF_H +#define AVFORMAT_IAMF_H + +#include +#include + +#include "libavutil/attributes_internal.h" +#include "libavutil/channel_layout.h" +#include "libavutil/iamf.h" +#include "libavcodec/codec_id.h" +#include "libavcodec/codec_par.h" + +#define MAX_IAMF_OBU_HEADER_SIZE (1 + 8 * 3) + +// OBU types (section 3.2). +enum IAMF_OBU_Type { + IAMF_OBU_IA_CODEC_CONFIG = 0, + IAMF_OBU_IA_AUDIO_ELEMENT = 1, + IAMF_OBU_IA_MIX_PRESENTATION = 2, + IAMF_OBU_IA_PARAMETER_BLOCK = 3, + IAMF_OBU_IA_TEMPORAL_DELIMITER = 4, + IAMF_OBU_IA_AUDIO_FRAME = 5, + IAMF_OBU_IA_AUDIO_FRAME_ID0 = 6, + IAMF_OBU_IA_AUDIO_FRAME_ID1 = 7, + IAMF_OBU_IA_AUDIO_FRAME_ID2 = 8, + IAMF_OBU_IA_AUDIO_FRAME_ID3 = 9, + IAMF_OBU_IA_AUDIO_FRAME_ID4 = 10, + IAMF_OBU_IA_AUDIO_FRAME_ID5 = 11, + IAMF_OBU_IA_AUDIO_FRAME_ID6 = 12, + IAMF_OBU_IA_AUDIO_FRAME_ID7 = 13, + IAMF_OBU_IA_AUDIO_FRAME_ID8 = 14, + IAMF_OBU_IA_AUDIO_FRAME_ID9 = 15, + IAMF_OBU_IA_AUDIO_FRAME_ID10 = 16, + IAMF_OBU_IA_AUDIO_FRAME_ID11 = 17, + IAMF_OBU_IA_AUDIO_FRAME_ID12 = 18, + IAMF_OBU_IA_AUDIO_FRAME_ID13 = 19, + IAMF_OBU_IA_AUDIO_FRAME_ID14 = 20, + IAMF_OBU_IA_AUDIO_FRAME_ID15 = 21, + IAMF_OBU_IA_AUDIO_FRAME_ID16 = 22, + IAMF_OBU_IA_AUDIO_FRAME_ID17 = 23, + // 24~30 reserved. + IAMF_OBU_IA_SEQUENCE_HEADER = 31, +}; + +typedef struct IAMFCodecConfig { + unsigned codec_config_id; + enum AVCodecID codec_id; + uint32_t codec_tag; + unsigned nb_samples; + int audio_roll_distance; + int sample_rate; + int extradata_size; + uint8_t *extradata; +} IAMFCodecConfig; + +typedef struct IAMFLayer { + unsigned int substream_count; + unsigned int coupled_substream_count; +} IAMFLayer; + +typedef struct IAMFSubStream { + unsigned int audio_substream_id; + + // demux + AVCodecParameters *codecpar; +} IAMFSubStream; + +typedef struct IAMFAudioElement { + const AVIAMFAudioElement *celement; + /** + * element backs celement iff the AVIAMFAudioElement + * is owned by this structure. + */ + AVIAMFAudioElement *element; + unsigned int audio_element_id; + + IAMFSubStream *substreams; + unsigned int nb_substreams; + + unsigned int codec_config_id; + + IAMFLayer *layers; + unsigned int nb_layers; +} IAMFAudioElement; + +typedef struct IAMFMixPresentation { + const AVIAMFMixPresentation *cmix; + /** + * mix backs cmix iff the AVIAMFMixPresentation + * is owned by this structure. + */ + AVIAMFMixPresentation *mix; + unsigned int mix_presentation_id; + + // demux + unsigned int count_label; + char **language_label; +} IAMFMixPresentation; + +typedef struct IAMFParamDefinition { + const IAMFAudioElement *audio_element; + AVIAMFParamDefinition *param; + int mode; + size_t param_size; +} IAMFParamDefinition; + +typedef struct IAMFContext { + IAMFCodecConfig **codec_configs; + int nb_codec_configs; + IAMFAudioElement **audio_elements; + int nb_audio_elements; + IAMFMixPresentation **mix_presentations; + int nb_mix_presentations; + IAMFParamDefinition **param_definitions; + int nb_param_definitions; +} IAMFContext; + +enum IAMF_Anchor_Element { + IAMF_ANCHOR_ELEMENT_UNKNWONW, + IAMF_ANCHOR_ELEMENT_DIALOGUE, + IAMF_ANCHOR_ELEMENT_ALBUM, +}; + +enum IAMF_Sound_System { + SOUND_SYSTEM_A_0_2_0 = 0, // "Loudspeaker configuration for Sound System A" + SOUND_SYSTEM_B_0_5_0 = 1, // "Loudspeaker configuration for Sound System B" + SOUND_SYSTEM_C_2_5_0 = 2, // "Loudspeaker configuration for Sound System C" + SOUND_SYSTEM_D_4_5_0 = 3, // "Loudspeaker configuration for Sound System D" + SOUND_SYSTEM_E_4_5_1 = 4, // "Loudspeaker configuration for Sound System E" + SOUND_SYSTEM_F_3_7_0 = 5, // "Loudspeaker configuration for Sound System F" + SOUND_SYSTEM_G_4_9_0 = 6, // "Loudspeaker configuration for Sound System G" + SOUND_SYSTEM_H_9_10_3 = 7, // "Loudspeaker configuration for Sound System H" + SOUND_SYSTEM_I_0_7_0 = 8, // "Loudspeaker configuration for Sound System I" + SOUND_SYSTEM_J_4_7_0 = 9, // "Loudspeaker configuration for Sound System J" + SOUND_SYSTEM_10_2_7_0 = 10, // "Loudspeaker configuration for Sound System I" + Ltf + Rtf + SOUND_SYSTEM_11_2_3_0 = 11, // Front subset of "Loudspeaker configuration for Sound System J" + SOUND_SYSTEM_12_0_1_0 = 12, // Mono +}; + +struct IAMFSoundSystemMap { + enum IAMF_Sound_System id; + AVChannelLayout layout; +}; + +FF_VISIBILITY_PUSH_HIDDEN +extern const AVChannelLayout ff_iamf_scalable_ch_layouts[10]; +extern const struct IAMFSoundSystemMap ff_iamf_sound_system_map[13]; + +static inline IAMFCodecConfig *ff_iamf_get_codec_config(const IAMFContext *c, + unsigned int codec_config_id) +{ + IAMFCodecConfig *codec_config = NULL; + + for (int i = 0; i < c->nb_codec_configs; i++) { + if (c->codec_configs[i]->codec_config_id == codec_config_id) + codec_config = c->codec_configs[i]; + } + + return codec_config; +} + +static inline IAMFParamDefinition *ff_iamf_get_param_definition(const IAMFContext *iamf, + unsigned int parameter_id) +{ + IAMFParamDefinition *param_definition = NULL; + + for (int i = 0; i < iamf->nb_param_definitions; i++) + if (iamf->param_definitions[i]->param->parameter_id == parameter_id) { + param_definition = iamf->param_definitions[i]; + break; + } + + return param_definition; +} + +void ff_iamf_free_audio_element(IAMFAudioElement **paudio_element); +void ff_iamf_free_mix_presentation(IAMFMixPresentation **pmix_presentation); +void ff_iamf_uninit_context(IAMFContext *c); +FF_VISIBILITY_POP_HIDDEN + +#endif /* AVFORMAT_IAMF_H */ diff --git a/libavformat/iamf_parse.c b/libavformat/iamf_parse.c new file mode 100644 index 00000000000..a222a46c865 --- /dev/null +++ b/libavformat/iamf_parse.c @@ -0,0 +1,1121 @@ +/* + * Immersive Audio Model and Formats parsing + * Copyright (c) 2023 James Almer + * + * 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/avassert.h" +#include "libavutil/common.h" +#include "libavutil/iamf.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/log.h" +#include "libavcodec/get_bits.h" +#include "libavcodec/flac.h" +#include "libavcodec/leb.h" +#include "libavcodec/mpeg4audio.h" +#include "libavcodec/put_bits.h" +#include "avio_internal.h" +#include "iamf_parse.h" +#include "isom.h" + +static int opus_decoder_config(IAMFCodecConfig *codec_config, + AVIOContext *pb, int len) +{ + int left = len - avio_tell(pb); + + if (left < 11 || codec_config->audio_roll_distance >= 0) + return AVERROR_INVALIDDATA; + + codec_config->extradata = av_malloc(left + 8); + if (!codec_config->extradata) + return AVERROR(ENOMEM); + + AV_WB32(codec_config->extradata, MKBETAG('O','p','u','s')); + AV_WB32(codec_config->extradata + 4, MKBETAG('H','e','a','d')); + codec_config->extradata_size = avio_read(pb, codec_config->extradata + 8, left); + if (codec_config->extradata_size < left) + return AVERROR_INVALIDDATA; + + codec_config->extradata_size += 8; + codec_config->sample_rate = 48000; + + return 0; +} + +static int aac_decoder_config(IAMFCodecConfig *codec_config, + AVIOContext *pb, int len, void *logctx) +{ + MPEG4AudioConfig cfg = { 0 }; + int object_type_id, codec_id, stream_type; + int ret, tag, left; + + if (codec_config->audio_roll_distance >= 0) + return AVERROR_INVALIDDATA; + + tag = avio_r8(pb); + if (tag != MP4DecConfigDescrTag) + return AVERROR_INVALIDDATA; + + object_type_id = avio_r8(pb); + if (object_type_id != 0x40) + return AVERROR_INVALIDDATA; + + stream_type = avio_r8(pb); + if (((stream_type >> 2) != 5) || ((stream_type >> 1) & 1)) + return AVERROR_INVALIDDATA; + + avio_skip(pb, 3); // buffer size db + avio_skip(pb, 4); // rc_max_rate + avio_skip(pb, 4); // avg bitrate + + codec_id = ff_codec_get_id(ff_mp4_obj_type, object_type_id); + if (codec_id && codec_id != codec_config->codec_id) + return AVERROR_INVALIDDATA; + + tag = avio_r8(pb); + if (tag != MP4DecSpecificDescrTag) + return AVERROR_INVALIDDATA; + + left = len - avio_tell(pb); + if (left <= 0) + return AVERROR_INVALIDDATA; + + // We pad extradata here because avpriv_mpeg4audio_get_config2() needs it. + codec_config->extradata = av_malloc((size_t)left + AV_INPUT_BUFFER_PADDING_SIZE); + if (!codec_config->extradata) + return AVERROR(ENOMEM); + + codec_config->extradata_size = avio_read(pb, codec_config->extradata, left); + if (codec_config->extradata_size < left) + return AVERROR_INVALIDDATA; + memset(codec_config->extradata + codec_config->extradata_size, 0, + AV_INPUT_BUFFER_PADDING_SIZE); + + ret = avpriv_mpeg4audio_get_config2(&cfg, codec_config->extradata, + codec_config->extradata_size, 1, logctx); + if (ret < 0) + return ret; + + codec_config->sample_rate = cfg.sample_rate; + + return 0; +} + +static int flac_decoder_config(IAMFCodecConfig *codec_config, + AVIOContext *pb, int len) +{ + int left; + + if (codec_config->audio_roll_distance) + return AVERROR_INVALIDDATA; + + avio_skip(pb, 4); // METADATA_BLOCK_HEADER + + left = len - avio_tell(pb); + if (left < FLAC_STREAMINFO_SIZE) + return AVERROR_INVALIDDATA; + + codec_config->extradata = av_malloc(left); + if (!codec_config->extradata) + return AVERROR(ENOMEM); + + codec_config->extradata_size = avio_read(pb, codec_config->extradata, left); + if (codec_config->extradata_size < left) + return AVERROR_INVALIDDATA; + + codec_config->sample_rate = AV_RB24(codec_config->extradata + 10) >> 4; + + return 0; +} + +static int ipcm_decoder_config(IAMFCodecConfig *codec_config, + AVIOContext *pb, int len) +{ + static const enum AVCodecID sample_fmt[2][3] = { + { AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S24BE, AV_CODEC_ID_PCM_S32BE }, + { AV_CODEC_ID_PCM_S16LE, AV_CODEC_ID_PCM_S24LE, AV_CODEC_ID_PCM_S32LE }, + }; + int sample_format = avio_r8(pb); // 0 = BE, 1 = LE + int sample_size = (avio_r8(pb) / 8 - 2); // 16, 24, 32 + if (sample_format > 1 || sample_size > 2U || codec_config->audio_roll_distance) + return AVERROR_INVALIDDATA; + + codec_config->codec_id = sample_fmt[sample_format][sample_size]; + codec_config->sample_rate = avio_rb32(pb); + + if (len - avio_tell(pb)) + return AVERROR_INVALIDDATA; + + return 0; +} + +static int codec_config_obu(void *s, IAMFContext *c, AVIOContext *pb, int len) +{ + IAMFCodecConfig **tmp, *codec_config = NULL; + FFIOContext b; + AVIOContext *pbc; + uint8_t *buf; + enum AVCodecID avcodec_id; + unsigned codec_config_id, nb_samples, codec_id; + int16_t audio_roll_distance; + int ret; + + buf = av_malloc(len); + if (!buf) + return AVERROR(ENOMEM); + + ret = avio_read(pb, buf, len); + if (ret != len) { + if (ret >= 0) + ret = AVERROR_INVALIDDATA; + goto fail; + } + + ffio_init_context(&b, buf, len, 0, NULL, NULL, NULL, NULL); + pbc = &b.pub; + + codec_config_id = ffio_read_leb(pbc); + codec_id = avio_rb32(pbc); + nb_samples = ffio_read_leb(pbc); + audio_roll_distance = avio_rb16(pbc); + + switch(codec_id) { + case MKBETAG('O','p','u','s'): + avcodec_id = AV_CODEC_ID_OPUS; + break; + case MKBETAG('m','p','4','a'): + avcodec_id = AV_CODEC_ID_AAC; + break; + case MKBETAG('f','L','a','C'): + avcodec_id = AV_CODEC_ID_FLAC; + break; + default: + avcodec_id = AV_CODEC_ID_NONE; + break; + } + + for (int i = 0; i < c->nb_codec_configs; i++) + if (c->codec_configs[i]->codec_config_id == codec_config_id) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + tmp = av_realloc_array(c->codec_configs, c->nb_codec_configs + 1, sizeof(*c->codec_configs)); + if (!tmp) { + ret = AVERROR(ENOMEM); + goto fail; + } + c->codec_configs = tmp; + + codec_config = av_mallocz(sizeof(*codec_config)); + if (!codec_config) { + ret = AVERROR(ENOMEM); + goto fail; + } + + codec_config->codec_config_id = codec_config_id; + codec_config->codec_id = avcodec_id; + codec_config->nb_samples = nb_samples; + codec_config->audio_roll_distance = audio_roll_distance; + + switch(codec_id) { + case MKBETAG('O','p','u','s'): + ret = opus_decoder_config(codec_config, pbc, len); + break; + case MKBETAG('m','p','4','a'): + ret = aac_decoder_config(codec_config, pbc, len, s); + break; + case MKBETAG('f','L','a','C'): + ret = flac_decoder_config(codec_config, pbc, len); + break; + case MKBETAG('i','p','c','m'): + ret = ipcm_decoder_config(codec_config, pbc, len); + break; + default: + break; + } + if (ret < 0) + goto fail; + + if ((codec_config->nb_samples > INT_MAX) || codec_config->nb_samples <= 0 || + (-codec_config->audio_roll_distance > INT_MAX / codec_config->nb_samples)) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + c->codec_configs[c->nb_codec_configs++] = codec_config; + + len -= avio_tell(pbc); + if (len) + av_log(s, AV_LOG_WARNING, "Underread in codec_config_obu. %d bytes left at the end\n", len); + + ret = 0; +fail: + av_free(buf); + if (ret < 0) { + if (codec_config) + av_free(codec_config->extradata); + av_free(codec_config); + } + return ret; +} + +static int update_extradata(AVCodecParameters *codecpar) +{ + GetBitContext gb; + PutBitContext pb; + int ret; + + switch(codecpar->codec_id) { + case AV_CODEC_ID_OPUS: + AV_WB8(codecpar->extradata + 9, codecpar->ch_layout.nb_channels); + AV_WL16(codecpar->extradata + 10, AV_RB16(codecpar->extradata + 10)); // Byte swap pre-skip + AV_WL32(codecpar->extradata + 12, AV_RB32(codecpar->extradata + 12)); // Byte swap sample rate + AV_WL16(codecpar->extradata + 16, AV_RB16(codecpar->extradata + 16)); // Byte swap Output Gain + break; + case AV_CODEC_ID_AAC: { + uint8_t buf[5]; + + init_put_bits(&pb, buf, sizeof(buf)); + ret = init_get_bits8(&gb, codecpar->extradata, codecpar->extradata_size); + if (ret < 0) + return ret; + + ret = get_bits(&gb, 5); + put_bits(&pb, 5, ret); + if (ret == AOT_ESCAPE) // violates section 3.11.2, but better check for it + put_bits(&pb, 6, get_bits(&gb, 6)); + ret = get_bits(&gb, 4); + put_bits(&pb, 4, ret); + if (ret == 0x0f) + put_bits(&pb, 24, get_bits(&gb, 24)); + + skip_bits(&gb, 4); + put_bits(&pb, 4, codecpar->ch_layout.nb_channels); // set channel config + ret = put_bits_left(&pb); + put_bits(&pb, ret, get_bits(&gb, ret)); + flush_put_bits(&pb); + + memcpy(codecpar->extradata, buf, sizeof(buf)); + break; + } + case AV_CODEC_ID_FLAC: { + uint8_t buf[13]; + + init_put_bits(&pb, buf, sizeof(buf)); + ret = init_get_bits8(&gb, codecpar->extradata, codecpar->extradata_size); + if (ret < 0) + return ret; + + put_bits32(&pb, get_bits_long(&gb, 32)); // min/max blocksize + put_bits64(&pb, 48, get_bits64(&gb, 48)); // min/max framesize + put_bits(&pb, 20, get_bits(&gb, 20)); // samplerate + skip_bits(&gb, 3); + put_bits(&pb, 3, codecpar->ch_layout.nb_channels - 1); + ret = put_bits_left(&pb); + put_bits(&pb, ret, get_bits(&gb, ret)); + flush_put_bits(&pb); + + memcpy(codecpar->extradata, buf, sizeof(buf)); + break; + } + } + + return 0; +} + +static int scalable_channel_layout_config(void *s, AVIOContext *pb, + IAMFAudioElement *audio_element, + const IAMFCodecConfig *codec_config) +{ + int nb_layers, k = 0; + + nb_layers = avio_r8(pb) >> 5; // get_bits(&gb, 3); + // skip_bits(&gb, 5); //reserved + + if (nb_layers > 6 || nb_layers == 0) + return AVERROR_INVALIDDATA; + + audio_element->layers = av_calloc(nb_layers, sizeof(*audio_element->layers)); + if (!audio_element->layers) + return AVERROR(ENOMEM); + + audio_element->nb_layers = nb_layers; + for (int i = 0; i < nb_layers; i++) { + AVIAMFLayer *layer; + int loudspeaker_layout, output_gain_is_present_flag; + int substream_count, coupled_substream_count; + int ret, byte = avio_r8(pb); + + layer = av_iamf_audio_element_add_layer(audio_element->element); + if (!layer) + return AVERROR(ENOMEM); + + loudspeaker_layout = byte >> 4; // get_bits(&gb, 4); + output_gain_is_present_flag = (byte >> 3) & 1; //get_bits1(&gb); + if ((byte >> 2) & 1) + layer->flags |= AV_IAMF_LAYER_FLAG_RECON_GAIN; + substream_count = avio_r8(pb); + coupled_substream_count = avio_r8(pb); + + if (substream_count + k > audio_element->nb_substreams) + return AVERROR_INVALIDDATA; + + audio_element->layers[i].substream_count = substream_count; + audio_element->layers[i].coupled_substream_count = coupled_substream_count; + if (output_gain_is_present_flag) { + layer->output_gain_flags = avio_r8(pb) >> 2; // get_bits(&gb, 6); + layer->output_gain = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8); + } + + if (loudspeaker_layout < 10) + av_channel_layout_copy(&layer->ch_layout, &ff_iamf_scalable_ch_layouts[loudspeaker_layout]); + else + layer->ch_layout = (AVChannelLayout){ .order = AV_CHANNEL_ORDER_UNSPEC, + .nb_channels = substream_count + + coupled_substream_count }; + + for (int j = 0; j < substream_count; j++) { + IAMFSubStream *substream = &audio_element->substreams[k++]; + + substream->codecpar->ch_layout = coupled_substream_count-- > 0 ? (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO : + (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; + + ret = update_extradata(substream->codecpar); + if (ret < 0) + return ret; + } + + } + + return 0; +} + +static int ambisonics_config(void *s, AVIOContext *pb, + IAMFAudioElement *audio_element, + const IAMFCodecConfig *codec_config) +{ + AVIAMFLayer *layer; + unsigned ambisonics_mode; + int output_channel_count, substream_count, order; + int ret; + + ambisonics_mode = ffio_read_leb(pb); + if (ambisonics_mode > 1) + return 0; + + output_channel_count = avio_r8(pb); // C + substream_count = avio_r8(pb); // N + if (audio_element->nb_substreams != substream_count) + return AVERROR_INVALIDDATA; + + order = floor(sqrt(output_channel_count - 1)); + /* incomplete order - some harmonics are missing */ + if ((order + 1) * (order + 1) != output_channel_count) + return AVERROR_INVALIDDATA; + + audio_element->layers = av_mallocz(sizeof(*audio_element->layers)); + if (!audio_element->layers) + return AVERROR(ENOMEM); + + audio_element->nb_layers = 1; + audio_element->layers->substream_count = substream_count; + + layer = av_iamf_audio_element_add_layer(audio_element->element); + if (!layer) + return AVERROR(ENOMEM); + + layer->ambisonics_mode = ambisonics_mode; + if (ambisonics_mode == 0) { + for (int i = 0; i < substream_count; i++) { + IAMFSubStream *substream = &audio_element->substreams[i]; + + substream->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; + + ret = update_extradata(substream->codecpar); + if (ret < 0) + return ret; + } + + layer->ch_layout.order = AV_CHANNEL_ORDER_CUSTOM; + layer->ch_layout.nb_channels = output_channel_count; + layer->ch_layout.u.map = av_calloc(output_channel_count, sizeof(*layer->ch_layout.u.map)); + if (!layer->ch_layout.u.map) + return AVERROR(ENOMEM); + + for (int i = 0; i < output_channel_count; i++) + layer->ch_layout.u.map[i].id = avio_r8(pb) + AV_CHAN_AMBISONIC_BASE; + } else { + int coupled_substream_count = avio_r8(pb); // M + int nb_demixing_matrix = substream_count + coupled_substream_count; + int demixing_matrix_size = nb_demixing_matrix * output_channel_count; + + audio_element->layers->coupled_substream_count = coupled_substream_count; + + layer->ch_layout = (AVChannelLayout){ .order = AV_CHANNEL_ORDER_AMBISONIC, .nb_channels = output_channel_count }; + layer->demixing_matrix = av_malloc_array(demixing_matrix_size, sizeof(*layer->demixing_matrix)); + if (!layer->demixing_matrix) + return AVERROR(ENOMEM); + + for (int i = 0; i < demixing_matrix_size; i++) + layer->demixing_matrix[i] = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8); + + for (int i = 0; i < substream_count; i++) { + IAMFSubStream *substream = &audio_element->substreams[i]; + + substream->codecpar->ch_layout = coupled_substream_count-- > 0 ? (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO : + (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO; + + + ret = update_extradata(substream->codecpar); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static int param_parse(void *s, IAMFContext *c, AVIOContext *pb, + unsigned int type, + const IAMFAudioElement *audio_element, + AVIAMFParamDefinition **out_param_definition) +{ + IAMFParamDefinition *param_definition = NULL; + AVIAMFParamDefinition *param; + unsigned int parameter_id, parameter_rate, mode; + unsigned int duration = 0, constant_subblock_duration = 0, nb_subblocks = 0; + size_t param_size; + + parameter_id = ffio_read_leb(pb); + + for (int i = 0; i < c->nb_param_definitions; i++) + if (c->param_definitions[i]->param->parameter_id == parameter_id) { + param_definition = c->param_definitions[i]; + break; + } + + parameter_rate = ffio_read_leb(pb); + mode = avio_r8(pb) >> 7; + + if (mode == 0) { + duration = ffio_read_leb(pb); + if (!duration) + return AVERROR_INVALIDDATA; + constant_subblock_duration = ffio_read_leb(pb); + if (constant_subblock_duration == 0) + nb_subblocks = ffio_read_leb(pb); + else + nb_subblocks = duration / constant_subblock_duration; + } + + param = av_iamf_param_definition_alloc(type, nb_subblocks, ¶m_size); + if (!param) + return AVERROR(ENOMEM); + + for (int i = 0; i < nb_subblocks; i++) { + void *subblock = av_iamf_param_definition_get_subblock(param, i); + unsigned int subblock_duration = constant_subblock_duration; + + if (constant_subblock_duration == 0) + subblock_duration = ffio_read_leb(pb); + + switch (type) { + case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: { + AVIAMFMixGain *mix = subblock; + mix->subblock_duration = subblock_duration; + break; + } + case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: { + AVIAMFDemixingInfo *demix = subblock; + demix->subblock_duration = subblock_duration; + // DefaultDemixingInfoParameterData + av_assert0(audio_element); + demix->dmixp_mode = avio_r8(pb) >> 5; + audio_element->element->default_w = avio_r8(pb) >> 4; + break; + } + case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: { + AVIAMFReconGain *recon = subblock; + recon->subblock_duration = subblock_duration; + break; + } + default: + av_free(param); + return AVERROR_INVALIDDATA; + } + } + + param->parameter_id = parameter_id; + param->parameter_rate = parameter_rate; + param->duration = duration; + param->constant_subblock_duration = constant_subblock_duration; + param->nb_subblocks = nb_subblocks; + + if (param_definition) { + if (param_definition->param_size != param_size || memcmp(param_definition->param, param, param_size)) { + av_log(s, AV_LOG_ERROR, "Incosistent parameters for parameter_id %u\n", parameter_id); + av_free(param); + return AVERROR_INVALIDDATA; + } + } else { + IAMFParamDefinition **tmp = av_realloc_array(c->param_definitions, c->nb_param_definitions + 1, + sizeof(*c->param_definitions)); + if (!tmp) { + av_free(param); + return AVERROR(ENOMEM); + } + c->param_definitions = tmp; + + param_definition = av_mallocz(sizeof(*param_definition)); + if (!param_definition) { + av_free(param); + return AVERROR(ENOMEM); + } + param_definition->param = param; + param_definition->mode = !mode; + param_definition->param_size = param_size; + param_definition->audio_element = audio_element; + + c->param_definitions[c->nb_param_definitions++] = param_definition; + } + + av_assert0(out_param_definition); + *out_param_definition = param; + + return 0; +} + +static int audio_element_obu(void *s, IAMFContext *c, AVIOContext *pb, int len) +{ + const IAMFCodecConfig *codec_config; + AVIAMFAudioElement *element; + IAMFAudioElement **tmp, *audio_element = NULL; + FFIOContext b; + AVIOContext *pbc; + uint8_t *buf; + unsigned audio_element_id, nb_substreams, codec_config_id, num_parameters; + int audio_element_type, ret; + + buf = av_malloc(len); + if (!buf) + return AVERROR(ENOMEM); + + ret = avio_read(pb, buf, len); + if (ret != len) { + if (ret >= 0) + ret = AVERROR_INVALIDDATA; + goto fail; + } + + ffio_init_context(&b, buf, len, 0, NULL, NULL, NULL, NULL); + pbc = &b.pub; + + audio_element_id = ffio_read_leb(pbc); + + for (int i = 0; i < c->nb_audio_elements; i++) + if (c->audio_elements[i]->audio_element_id == audio_element_id) { + av_log(s, AV_LOG_ERROR, "Duplicate audio_element_id %d\n", audio_element_id); + ret = AVERROR_INVALIDDATA; + goto fail; + } + + audio_element_type = avio_r8(pbc) >> 5; + codec_config_id = ffio_read_leb(pbc); + + codec_config = ff_iamf_get_codec_config(c, codec_config_id); + if (!codec_config) { + av_log(s, AV_LOG_ERROR, "Non existant codec config id %d referenced in an audio element\n", codec_config_id); + ret = AVERROR_INVALIDDATA; + goto fail; + } + + if (codec_config->codec_id == AV_CODEC_ID_NONE) { + av_log(s, AV_LOG_DEBUG, "Unknown codec id referenced in an audio element. Ignoring\n"); + ret = 0; + goto fail; + } + + tmp = av_realloc_array(c->audio_elements, c->nb_audio_elements + 1, sizeof(*c->audio_elements)); + if (!tmp) { + ret = AVERROR(ENOMEM); + goto fail; + } + c->audio_elements = tmp; + + audio_element = av_mallocz(sizeof(*audio_element)); + if (!audio_element) { + ret = AVERROR(ENOMEM); + goto fail; + } + + nb_substreams = ffio_read_leb(pbc); + audio_element->codec_config_id = codec_config_id; + audio_element->audio_element_id = audio_element_id; + audio_element->substreams = av_calloc(nb_substreams, sizeof(*audio_element->substreams)); + if (!audio_element->substreams) { + ret = AVERROR(ENOMEM); + goto fail; + } + audio_element->nb_substreams = nb_substreams; + + element = audio_element->element = av_iamf_audio_element_alloc(); + if (!element) { + ret = AVERROR(ENOMEM); + goto fail; + } + audio_element->celement = element; + + element->audio_element_type = audio_element_type; + + for (int i = 0; i < audio_element->nb_substreams; i++) { + IAMFSubStream *substream = &audio_element->substreams[i]; + + substream->codecpar = avcodec_parameters_alloc(); + if (!substream->codecpar) { + ret = AVERROR(ENOMEM); + goto fail; + } + + substream->audio_substream_id = ffio_read_leb(pbc); + + substream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + substream->codecpar->codec_id = codec_config->codec_id; + substream->codecpar->frame_size = codec_config->nb_samples; + substream->codecpar->sample_rate = codec_config->sample_rate; + substream->codecpar->seek_preroll = -codec_config->audio_roll_distance * codec_config->nb_samples; + + switch(substream->codecpar->codec_id) { + case AV_CODEC_ID_AAC: + case AV_CODEC_ID_FLAC: + case AV_CODEC_ID_OPUS: + substream->codecpar->extradata = av_malloc(codec_config->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!substream->codecpar->extradata) { + ret = AVERROR(ENOMEM); + goto fail; + } + memcpy(substream->codecpar->extradata, codec_config->extradata, codec_config->extradata_size); + memset(substream->codecpar->extradata + codec_config->extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE); + substream->codecpar->extradata_size = codec_config->extradata_size; + break; + } + } + + num_parameters = ffio_read_leb(pbc); + if (num_parameters && audio_element_type != 0) { + av_log(s, AV_LOG_ERROR, "Audio Element parameter count %u is invalid" + " for Scene representations\n", num_parameters); + ret = AVERROR_INVALIDDATA; + goto fail; + } + + for (int i = 0; i < num_parameters; i++) { + unsigned type; + + type = ffio_read_leb(pbc); + if (type == AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN) + ret = AVERROR_INVALIDDATA; + else if (type == AV_IAMF_PARAMETER_DEFINITION_DEMIXING) + ret = param_parse(s, c, pbc, type, audio_element, &element->demixing_info); + else if (type == AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN) + ret = param_parse(s, c, pbc, type, audio_element, &element->recon_gain_info); + else { + unsigned param_definition_size = ffio_read_leb(pbc); + avio_skip(pbc, param_definition_size); + } + if (ret < 0) + goto fail; + } + + if (audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL) { + ret = scalable_channel_layout_config(s, pbc, audio_element, codec_config); + if (ret < 0) + goto fail; + } else if (audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_SCENE) { + ret = ambisonics_config(s, pbc, audio_element, codec_config); + if (ret < 0) + goto fail; + } else { + unsigned audio_element_config_size = ffio_read_leb(pbc); + avio_skip(pbc, audio_element_config_size); + } + + c->audio_elements[c->nb_audio_elements++] = audio_element; + + len -= avio_tell(pbc); + if (len) + av_log(s, AV_LOG_WARNING, "Underread in audio_element_obu. %d bytes left at the end\n", len); + + ret = 0; +fail: + av_free(buf); + if (ret < 0) + ff_iamf_free_audio_element(&audio_element); + return ret; +} + +static int label_string(AVIOContext *pb, char **label) +{ + uint8_t buf[128]; + + avio_get_str(pb, sizeof(buf), buf, sizeof(buf)); + + if (pb->error) + return pb->error; + if (pb->eof_reached) + return AVERROR_INVALIDDATA; + *label = av_strdup(buf); + if (!*label) + return AVERROR(ENOMEM); + + return 0; +} + +static int mix_presentation_obu(void *s, IAMFContext *c, AVIOContext *pb, int len) +{ + AVIAMFMixPresentation *mix; + IAMFMixPresentation **tmp, *mix_presentation = NULL; + FFIOContext b; + AVIOContext *pbc; + uint8_t *buf; + unsigned nb_submixes, mix_presentation_id; + int ret; + + buf = av_malloc(len); + if (!buf) + return AVERROR(ENOMEM); + + ret = avio_read(pb, buf, len); + if (ret != len) { + if (ret >= 0) + ret = AVERROR_INVALIDDATA; + goto fail; + } + + ffio_init_context(&b, buf, len, 0, NULL, NULL, NULL, NULL); + pbc = &b.pub; + + mix_presentation_id = ffio_read_leb(pbc); + + for (int i = 0; i < c->nb_mix_presentations; i++) + if (c->mix_presentations[i]->mix_presentation_id == mix_presentation_id) { + av_log(s, AV_LOG_ERROR, "Duplicate mix_presentation_id %d\n", mix_presentation_id); + ret = AVERROR_INVALIDDATA; + goto fail; + } + + tmp = av_realloc_array(c->mix_presentations, c->nb_mix_presentations + 1, sizeof(*c->mix_presentations)); + if (!tmp) { + ret = AVERROR(ENOMEM); + goto fail; + } + c->mix_presentations = tmp; + + mix_presentation = av_mallocz(sizeof(*mix_presentation)); + if (!mix_presentation) { + ret = AVERROR(ENOMEM); + goto fail; + } + + mix_presentation->mix_presentation_id = mix_presentation_id; + mix = mix_presentation->mix = av_iamf_mix_presentation_alloc(); + if (!mix) { + ret = AVERROR(ENOMEM); + goto fail; + } + mix_presentation->cmix = mix; + + mix_presentation->count_label = ffio_read_leb(pbc); + mix_presentation->language_label = av_calloc(mix_presentation->count_label, + sizeof(*mix_presentation->language_label)); + if (!mix_presentation->language_label) { + ret = AVERROR(ENOMEM); + goto fail; + } + + for (int i = 0; i < mix_presentation->count_label; i++) { + ret = label_string(pbc, &mix_presentation->language_label[i]); + if (ret < 0) + goto fail; + } + + for (int i = 0; i < mix_presentation->count_label; i++) { + char *annotation = NULL; + ret = label_string(pbc, &annotation); + if (ret < 0) + goto fail; + ret = av_dict_set(&mix->annotations, mix_presentation->language_label[i], annotation, + AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE); + if (ret < 0) + goto fail; + } + + nb_submixes = ffio_read_leb(pbc); + for (int i = 0; i < nb_submixes; i++) { + AVIAMFSubmix *sub_mix; + unsigned nb_elements, nb_layouts; + + sub_mix = av_iamf_mix_presentation_add_submix(mix); + if (!sub_mix) { + ret = AVERROR(ENOMEM); + goto fail; + } + + nb_elements = ffio_read_leb(pbc); + for (int j = 0; j < nb_elements; j++) { + AVIAMFSubmixElement *submix_element; + IAMFAudioElement *audio_element = NULL; + unsigned int rendering_config_extension_size; + + submix_element = av_iamf_submix_add_element(sub_mix); + if (!submix_element) { + ret = AVERROR(ENOMEM); + goto fail; + } + + submix_element->audio_element_id = ffio_read_leb(pbc); + + for (int k = 0; k < c->nb_audio_elements; k++) + if (c->audio_elements[k]->audio_element_id == submix_element->audio_element_id) { + audio_element = c->audio_elements[k]; + break; + } + + if (!audio_element) { + av_log(s, AV_LOG_ERROR, "Invalid Audio Element with id %u referenced by Mix Parameters %u\n", + submix_element->audio_element_id, mix_presentation_id); + ret = AVERROR_INVALIDDATA; + goto fail; + } + + for (int k = 0; k < mix_presentation->count_label; k++) { + char *annotation = NULL; + ret = label_string(pbc, &annotation); + if (ret < 0) + goto fail; + ret = av_dict_set(&submix_element->annotations, mix_presentation->language_label[k], annotation, + AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE); + if (ret < 0) + goto fail; + } + + submix_element->headphones_rendering_mode = avio_r8(pbc) >> 6; + + rendering_config_extension_size = ffio_read_leb(pbc); + avio_skip(pbc, rendering_config_extension_size); + + ret = param_parse(s, c, pbc, AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, + NULL, + &submix_element->element_mix_config); + if (ret < 0) + goto fail; + submix_element->default_mix_gain = av_make_q(sign_extend(avio_rb16(pbc), 16), 1 << 8); + } + + ret = param_parse(s, c, pbc, AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, NULL, &sub_mix->output_mix_config); + if (ret < 0) + goto fail; + sub_mix->default_mix_gain = av_make_q(sign_extend(avio_rb16(pbc), 16), 1 << 8); + + nb_layouts = ffio_read_leb(pbc); + for (int j = 0; j < nb_layouts; j++) { + AVIAMFSubmixLayout *submix_layout; + int info_type; + int byte = avio_r8(pbc); + + submix_layout = av_iamf_submix_add_layout(sub_mix); + if (!submix_layout) { + ret = AVERROR(ENOMEM); + goto fail; + } + + submix_layout->layout_type = byte >> 6; + if (submix_layout->layout_type < AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS || + submix_layout->layout_type > AV_IAMF_SUBMIX_LAYOUT_TYPE_BINAURAL) { + av_log(s, AV_LOG_ERROR, "Invalid Layout type %u in a submix from Mix Presentation %u\n", + submix_layout->layout_type, mix_presentation_id); + ret = AVERROR_INVALIDDATA; + goto fail; + } + if (submix_layout->layout_type == 2) { + int sound_system; + sound_system = (byte >> 2) & 0xF; + if (sound_system >= FF_ARRAY_ELEMS(ff_iamf_sound_system_map)) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + av_channel_layout_copy(&submix_layout->sound_system, &ff_iamf_sound_system_map[sound_system].layout); + } + + info_type = avio_r8(pbc); + submix_layout->integrated_loudness = av_make_q(sign_extend(avio_rb16(pbc), 16), 1 << 8); + submix_layout->digital_peak = av_make_q(sign_extend(avio_rb16(pbc), 16), 1 << 8); + + if (info_type & 1) + submix_layout->true_peak = av_make_q(sign_extend(avio_rb16(pbc), 16), 1 << 8); + if (info_type & 2) { + unsigned int num_anchored_loudness = avio_r8(pbc); + + for (int k = 0; k < num_anchored_loudness; k++) { + unsigned int anchor_element = avio_r8(pbc); + AVRational anchored_loudness = av_make_q(sign_extend(avio_rb16(pbc), 16), 1 << 8); + if (anchor_element == IAMF_ANCHOR_ELEMENT_DIALOGUE) + submix_layout->dialogue_anchored_loudness = anchored_loudness; + else if (anchor_element <= IAMF_ANCHOR_ELEMENT_ALBUM) + submix_layout->album_anchored_loudness = anchored_loudness; + else + av_log(s, AV_LOG_DEBUG, "Unknown anchor_element. Ignoring\n"); + } + } + + if (info_type & 0xFC) { + unsigned int info_type_size = ffio_read_leb(pbc); + avio_skip(pbc, info_type_size); + } + } + } + + c->mix_presentations[c->nb_mix_presentations++] = mix_presentation; + + len -= avio_tell(pbc); + if (len) + av_log(s, AV_LOG_WARNING, "Underread in mix_presentation_obu. %d bytes left at the end\n", len); + + ret = 0; +fail: + av_free(buf); + if (ret < 0) + ff_iamf_free_mix_presentation(&mix_presentation); + return ret; +} + +int ff_iamf_parse_obu_header(const uint8_t *buf, int buf_size, + unsigned *obu_size, int *start_pos, enum IAMF_OBU_Type *type, + unsigned *skip_samples, unsigned *discard_padding) +{ + GetBitContext gb; + int ret, extension_flag, trimming, start; + unsigned skip = 0, discard = 0; + unsigned size; + + ret = init_get_bits8(&gb, buf, FFMIN(buf_size, MAX_IAMF_OBU_HEADER_SIZE)); + if (ret < 0) + return ret; + + *type = get_bits(&gb, 5); + /*redundant =*/ get_bits1(&gb); + trimming = get_bits1(&gb); + extension_flag = get_bits1(&gb); + + *obu_size = get_leb(&gb); + if (*obu_size > INT_MAX) + return AVERROR_INVALIDDATA; + + start = get_bits_count(&gb) / 8; + + if (trimming) { + discard = get_leb(&gb); // num_samples_to_trim_at_end + skip = get_leb(&gb); // num_samples_to_trim_at_start + } + + if (skip_samples) + *skip_samples = skip; + if (discard_padding) + *discard_padding = discard; + + if (extension_flag) { + unsigned int extension_bytes; + extension_bytes = get_leb(&gb); + if (extension_bytes > INT_MAX / 8) + return AVERROR_INVALIDDATA; + skip_bits_long(&gb, extension_bytes * 8); + } + + if (get_bits_left(&gb) < 0) + return AVERROR_INVALIDDATA; + + size = *obu_size + start; + if (size > INT_MAX) + return AVERROR_INVALIDDATA; + + *obu_size -= get_bits_count(&gb) / 8 - start; + *start_pos = size - *obu_size; + + return size; +} + +int ff_iamfdec_read_descriptors(IAMFContext *c, AVIOContext *pb, + int max_size, void *log_ctx) +{ + uint8_t header[MAX_IAMF_OBU_HEADER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; + int ret; + + while (1) { + unsigned obu_size; + enum IAMF_OBU_Type type; + int start_pos, len, size; + + if ((ret = ffio_ensure_seekback(pb, FFMIN(MAX_IAMF_OBU_HEADER_SIZE, max_size))) < 0) + return ret; + size = avio_read(pb, header, FFMIN(MAX_IAMF_OBU_HEADER_SIZE, max_size)); + if (size < 0) + return size; + + len = ff_iamf_parse_obu_header(header, size, &obu_size, &start_pos, &type, NULL, NULL); + if (len < 0 || obu_size > max_size) { + av_log(log_ctx, AV_LOG_ERROR, "Failed to read obu header\n"); + avio_seek(pb, -size, SEEK_CUR); + return len; + } + + if (type >= IAMF_OBU_IA_PARAMETER_BLOCK && type < IAMF_OBU_IA_SEQUENCE_HEADER) { + avio_seek(pb, -size, SEEK_CUR); + break; + } + + avio_seek(pb, -(size - start_pos), SEEK_CUR); + switch (type) { + case IAMF_OBU_IA_CODEC_CONFIG: + ret = codec_config_obu(log_ctx, c, pb, obu_size); + break; + case IAMF_OBU_IA_AUDIO_ELEMENT: + ret = audio_element_obu(log_ctx, c, pb, obu_size); + break; + case IAMF_OBU_IA_MIX_PRESENTATION: + ret = mix_presentation_obu(log_ctx, c, pb, obu_size); + break; + default: { + int64_t offset = avio_skip(pb, obu_size); + if (offset < 0) + ret = offset; + break; + } + } + if (ret < 0) { + av_log(log_ctx, AV_LOG_ERROR, "Failed to read obu type %d\n", type); + return ret; + } + max_size -= obu_size + start_pos; + if (max_size < 0) + return AVERROR_INVALIDDATA; + if (!max_size) + break; + } + + return 0; +} diff --git a/libavformat/iamf_parse.h b/libavformat/iamf_parse.h new file mode 100644 index 00000000000..bb506486d73 --- /dev/null +++ b/libavformat/iamf_parse.h @@ -0,0 +1,37 @@ +/* + * Immersive Audio Model and Formats parsing + * Copyright (c) 2023 James Almer + * + * 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 + */ + +#ifndef AVFORMAT_IAMF_PARSE_H +#define AVFORMAT_IAMF_PARSE_H + +#include + +#include "avio.h" +#include "iamf.h" + +int ff_iamf_parse_obu_header(const uint8_t *buf, int buf_size, + unsigned *obu_size, int *start_pos, enum IAMF_OBU_Type *type, + unsigned *skip_samples, unsigned *discard_padding); + +int ff_iamfdec_read_descriptors(IAMFContext *c, AVIOContext *pb, + int size, void *log_ctx); + +#endif /* AVFORMAT_IAMF_PARSE_H */ diff --git a/libavformat/iamf_reader.c b/libavformat/iamf_reader.c new file mode 100644 index 00000000000..f3ff4170c62 --- /dev/null +++ b/libavformat/iamf_reader.c @@ -0,0 +1,338 @@ +/* + * Immersive Audio Model and Formats demuxing utils + * Copyright (c) 2024 James Almer + * + * 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/avassert.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/log.h" +#include "libavcodec/mathops.h" +#include "libavcodec/packet.h" +#include "avformat.h" +#include "avio_internal.h" +#include "iamf.h" +#include "iamf_parse.h" +#include "iamf_reader.h" + +static AVStream *find_stream_by_id(AVFormatContext *s, int id) +{ + for (int i = 0; i < s->nb_streams; i++) + if (s->streams[i]->id == id) + return s->streams[i]; + + av_log(s, AV_LOG_ERROR, "Invalid stream id %d\n", id); + return NULL; +} + +static int audio_frame_obu(AVFormatContext *s, const IAMFDemuxContext *c, + AVIOContext *pb, AVPacket *pkt, + int len, enum IAMF_OBU_Type type, + unsigned skip_samples, unsigned discard_padding, + int id_in_bitstream) +{ + AVStream *st; + int ret, audio_substream_id; + + if (id_in_bitstream) { + unsigned explicit_audio_substream_id; + int64_t pos = avio_tell(pb); + explicit_audio_substream_id = ffio_read_leb(pb); + len -= avio_tell(pb) - pos; + audio_substream_id = explicit_audio_substream_id; + } else + audio_substream_id = type - IAMF_OBU_IA_AUDIO_FRAME_ID0; + + st = find_stream_by_id(s, audio_substream_id); + if (!st) + return AVERROR_INVALIDDATA; + + ret = av_get_packet(pb, pkt, len); + if (ret < 0) + return ret; + if (ret != len) + return AVERROR_INVALIDDATA; + + if (skip_samples || discard_padding) { + uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, 10); + if (!side_data) + return AVERROR(ENOMEM); + AV_WL32(side_data, skip_samples); + AV_WL32(side_data + 4, discard_padding); + } + if (c->mix) { + uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_IAMF_MIX_GAIN_PARAM, c->mix_size); + if (!side_data) + return AVERROR(ENOMEM); + memcpy(side_data, c->mix, c->mix_size); + } + if (c->demix) { + uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM, c->demix_size); + if (!side_data) + return AVERROR(ENOMEM); + memcpy(side_data, c->demix, c->demix_size); + } + if (c->recon) { + uint8_t *side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM, c->recon_size); + if (!side_data) + return AVERROR(ENOMEM); + memcpy(side_data, c->recon, c->recon_size); + } + + pkt->stream_index = st->index; + return 0; +} + +static int parameter_block_obu(AVFormatContext *s, IAMFDemuxContext *c, + AVIOContext *pbc, int len) +{ + const IAMFParamDefinition *param_definition; + const AVIAMFParamDefinition *param; + AVIAMFParamDefinition *out_param = NULL; + FFIOContext b; + AVIOContext *pb; + uint8_t *buf; + unsigned int duration, constant_subblock_duration; + unsigned int nb_subblocks; + unsigned int parameter_id; + size_t out_param_size; + int ret; + + buf = av_malloc(len); + if (!buf) + return AVERROR(ENOMEM); + + ret = avio_read(pbc, buf, len); + if (ret != len) { + if (ret >= 0) + ret = AVERROR_INVALIDDATA; + goto fail; + } + + ffio_init_context(&b, buf, len, 0, NULL, NULL, NULL, NULL); + pb = &b.pub; + + parameter_id = ffio_read_leb(pb); + param_definition = ff_iamf_get_param_definition(&c->iamf, parameter_id); + if (!param_definition) { + av_log(s, AV_LOG_VERBOSE, "Non existant parameter_id %d referenced in a parameter block. Ignoring\n", + parameter_id); + ret = 0; + goto fail; + } + + param = param_definition->param; + if (!param_definition->mode) { + duration = ffio_read_leb(pb); + if (!duration) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + constant_subblock_duration = ffio_read_leb(pb); + if (constant_subblock_duration == 0) + nb_subblocks = ffio_read_leb(pb); + else + nb_subblocks = duration / constant_subblock_duration; + } else { + duration = param->duration; + constant_subblock_duration = param->constant_subblock_duration; + nb_subblocks = param->nb_subblocks; + } + + out_param = av_iamf_param_definition_alloc(param->type, nb_subblocks, &out_param_size); + if (!out_param) { + ret = AVERROR(ENOMEM); + goto fail; + } + + out_param->parameter_id = param->parameter_id; + out_param->type = param->type; + out_param->parameter_rate = param->parameter_rate; + out_param->duration = duration; + out_param->constant_subblock_duration = constant_subblock_duration; + out_param->nb_subblocks = nb_subblocks; + + for (int i = 0; i < nb_subblocks; i++) { + void *subblock = av_iamf_param_definition_get_subblock(out_param, i); + unsigned int subblock_duration = constant_subblock_duration; + + if (!param_definition->mode && !constant_subblock_duration) + subblock_duration = ffio_read_leb(pb); + + switch (param->type) { + case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: { + AVIAMFMixGain *mix = subblock; + + mix->animation_type = ffio_read_leb(pb); + if (mix->animation_type > AV_IAMF_ANIMATION_TYPE_BEZIER) { + ret = 0; + av_free(out_param); + goto fail; + } + + mix->start_point_value = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8); + if (mix->animation_type >= AV_IAMF_ANIMATION_TYPE_LINEAR) + mix->end_point_value = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8); + if (mix->animation_type == AV_IAMF_ANIMATION_TYPE_BEZIER) { + mix->control_point_value = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8); + mix->control_point_relative_time = av_make_q(avio_r8(pb), 1 << 8); + } + mix->subblock_duration = subblock_duration; + break; + } + case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: { + AVIAMFDemixingInfo *demix = subblock; + + demix->dmixp_mode = avio_r8(pb) >> 5; + demix->subblock_duration = subblock_duration; + break; + } + case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: { + AVIAMFReconGain *recon = subblock; + const IAMFAudioElement *audio_element = param_definition->audio_element; + const AVIAMFAudioElement *element = audio_element->celement; + + av_assert0(audio_element && element); + for (int i = 0; i < element->nb_layers; i++) { + const AVIAMFLayer *layer = element->layers[i]; + if (layer->flags & AV_IAMF_LAYER_FLAG_RECON_GAIN) { + unsigned int recon_gain_flags = ffio_read_leb(pb); + unsigned int bitcount = 7 + 5 * !!(recon_gain_flags & 0x80); + recon_gain_flags = (recon_gain_flags & 0x7F) | ((recon_gain_flags & 0xFF00) >> 1); + for (int j = 0; j < bitcount; j++) { + if (recon_gain_flags & (1 << j)) + recon->recon_gain[i][j] = avio_r8(pb); + } + } + } + recon->subblock_duration = subblock_duration; + break; + } + default: + av_assert0(0); + } + } + + len -= avio_tell(pb); + if (len) { + int level = (s->error_recognition & AV_EF_EXPLODE) ? AV_LOG_ERROR : AV_LOG_WARNING; + av_log(s, level, "Underread in parameter_block_obu. %d bytes left at the end\n", len); + } + + switch (param->type) { + case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: + av_free(c->mix); + c->mix = out_param; + c->mix_size = out_param_size; + break; + case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: + av_free(c->demix); + c->demix = out_param; + c->demix_size = out_param_size; + break; + case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: + av_free(c->recon); + c->recon = out_param; + c->recon_size = out_param_size; + break; + default: + av_assert0(0); + } + + ret = 0; +fail: + if (ret < 0) + av_free(out_param); + av_free(buf); + + return ret; +} + +int ff_iamf_read_packet(AVFormatContext *s, IAMFDemuxContext *c, + AVIOContext *pb, int max_size, AVPacket *pkt) +{ + int read = 0; + + while (1) { + uint8_t header[MAX_IAMF_OBU_HEADER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; + enum IAMF_OBU_Type type; + unsigned obu_size; + unsigned skip_samples, discard_padding; + int ret, len, size, start_pos; + + if ((ret = ffio_ensure_seekback(pb, FFMIN(MAX_IAMF_OBU_HEADER_SIZE, max_size))) < 0) + return ret; + size = avio_read(pb, header, FFMIN(MAX_IAMF_OBU_HEADER_SIZE, max_size)); + if (size < 0) + return size; + + len = ff_iamf_parse_obu_header(header, size, &obu_size, &start_pos, &type, + &skip_samples, &discard_padding); + if (len < 0 || obu_size > max_size || len > INT_MAX - read) { + av_log(s, AV_LOG_ERROR, "Failed to read obu\n"); + return len < 0 ? len : AVERROR_INVALIDDATA; + } + avio_seek(pb, -(size - start_pos), SEEK_CUR); + + read += len; + if (type >= IAMF_OBU_IA_AUDIO_FRAME && type <= IAMF_OBU_IA_AUDIO_FRAME_ID17) { + ret = audio_frame_obu(s, c, pb, pkt, obu_size, type, + skip_samples, discard_padding, + type == IAMF_OBU_IA_AUDIO_FRAME); + if (ret < 0) + return ret; + return read; + } else if (type == IAMF_OBU_IA_PARAMETER_BLOCK) { + ret = parameter_block_obu(s, c, pb, obu_size); + if (ret < 0) + return ret; + } else if (type == IAMF_OBU_IA_TEMPORAL_DELIMITER) { + av_freep(&c->mix); + c->mix_size = 0; + av_freep(&c->demix); + c->demix_size = 0; + av_freep(&c->recon); + c->recon_size = 0; + } else { + int64_t offset = avio_skip(pb, obu_size); + if (offset < 0) + return offset; + } + max_size -= len; + if (max_size < 0) + return AVERROR_INVALIDDATA; + if (!max_size) + break; + } + + return read; +} + +void ff_iamf_read_deinit(IAMFDemuxContext *c) +{ + IAMFContext *const iamf = &c->iamf; + + ff_iamf_uninit_context(iamf); + + av_freep(&c->mix); + c->mix_size = 0; + av_freep(&c->demix); + c->demix_size = 0; + av_freep(&c->recon); + c->recon_size = 0; +} diff --git a/libavformat/iamf_reader.h b/libavformat/iamf_reader.h new file mode 100644 index 00000000000..ecb92d485a8 --- /dev/null +++ b/libavformat/iamf_reader.h @@ -0,0 +1,49 @@ +/* + * Immersive Audio Model and Formats demuxing utils + * Copyright (c) 2024 James Almer + * + * 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 + */ + +#ifndef AVFORMAT_IAMF_READER_H +#define AVFORMAT_IAMF_READER_H + +#include + +#include "libavcodec/packet.h" +#include "avformat.h" +#include "avio.h" +#include "iamf.h" + +typedef struct IAMFDemuxContext { + IAMFContext iamf; + + // Packet side data + AVIAMFParamDefinition *mix; + size_t mix_size; + AVIAMFParamDefinition *demix; + size_t demix_size; + AVIAMFParamDefinition *recon; + size_t recon_size; +} IAMFDemuxContext; + +int ff_iamf_read_packet(AVFormatContext *s, IAMFDemuxContext *c, + AVIOContext *pb, int max_size, AVPacket *pkt); + +void ff_iamf_read_deinit(IAMFDemuxContext *c); + +#endif /* AVFORMAT_IAMF_READER_H */ diff --git a/libavformat/iamf_writer.c b/libavformat/iamf_writer.c new file mode 100644 index 00000000000..5e8d8f768be --- /dev/null +++ b/libavformat/iamf_writer.c @@ -0,0 +1,1151 @@ +/* + * Immersive Audio Model and Formats muxing helpers and structs + * Copyright (c) 2023 James Almer + * + * 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 "libavutil/iamf.h" +#include "libavutil/mem.h" +#include "libavcodec/get_bits.h" +#include "libavcodec/put_bits.h" +#include "avformat.h" +#include "avio_internal.h" +#include "iamf.h" +#include "iamf_writer.h" + + +static int update_extradata(IAMFCodecConfig *codec_config) +{ + GetBitContext gb; + PutBitContext pb; + int ret; + + switch(codec_config->codec_id) { + case AV_CODEC_ID_OPUS: + if (codec_config->extradata_size != 19) + return AVERROR_INVALIDDATA; + codec_config->extradata_size -= 8; + AV_WB8(codec_config->extradata + 0, AV_RL8(codec_config->extradata + 8)); // version + AV_WB8(codec_config->extradata + 1, 2); // set channels to stereo + AV_WB16(codec_config->extradata + 2, AV_RL16(codec_config->extradata + 10)); // Byte swap pre-skip + AV_WB32(codec_config->extradata + 4, AV_RL32(codec_config->extradata + 12)); // Byte swap sample rate + AV_WB16(codec_config->extradata + 8, 0); // set Output Gain to 0 + AV_WB8(codec_config->extradata + 10, AV_RL8(codec_config->extradata + 18)); // Mapping family + break; + case AV_CODEC_ID_FLAC: { + uint8_t buf[13]; + + init_put_bits(&pb, buf, sizeof(buf)); + ret = init_get_bits8(&gb, codec_config->extradata, codec_config->extradata_size); + if (ret < 0) + return ret; + + put_bits32(&pb, get_bits_long(&gb, 32)); // min/max blocksize + put_bits64(&pb, 48, get_bits64(&gb, 48)); // min/max framesize + put_bits(&pb, 20, get_bits(&gb, 20)); // samplerate + skip_bits(&gb, 3); + put_bits(&pb, 3, 1); // set channels to stereo + ret = put_bits_left(&pb); + put_bits(&pb, ret, get_bits(&gb, ret)); + flush_put_bits(&pb); + + memcpy(codec_config->extradata, buf, sizeof(buf)); + break; + } + default: + break; + } + + return 0; +} + +static int populate_audio_roll_distance(IAMFCodecConfig *codec_config) +{ + switch (codec_config->codec_id) { + case AV_CODEC_ID_OPUS: + if (!codec_config->nb_samples) + return AVERROR(EINVAL); + // ceil(3840 / nb_samples) + codec_config->audio_roll_distance = -(1 + ((3840 - 1) / codec_config->nb_samples)); + break; + case AV_CODEC_ID_AAC: + codec_config->audio_roll_distance = -1; + break; + case AV_CODEC_ID_FLAC: + case AV_CODEC_ID_PCM_S16BE: + case AV_CODEC_ID_PCM_S24BE: + case AV_CODEC_ID_PCM_S32BE: + case AV_CODEC_ID_PCM_S16LE: + case AV_CODEC_ID_PCM_S24LE: + case AV_CODEC_ID_PCM_S32LE: + codec_config->audio_roll_distance = 0; + break; + default: + return AVERROR(EINVAL); + } + + return 0; +} + +static int fill_codec_config(IAMFContext *iamf, const AVStreamGroup *stg, + IAMFCodecConfig *codec_config) +{ + const AVStream *st = stg->streams[0]; + IAMFCodecConfig **tmp; + int j, ret = 0; + + codec_config->codec_id = st->codecpar->codec_id; + codec_config->sample_rate = st->codecpar->sample_rate; + codec_config->codec_tag = st->codecpar->codec_tag; + codec_config->nb_samples = st->codecpar->frame_size; + populate_audio_roll_distance(codec_config); + if (st->codecpar->extradata_size) { + codec_config->extradata = av_memdup(st->codecpar->extradata, st->codecpar->extradata_size); + if (!codec_config->extradata) + return AVERROR(ENOMEM); + codec_config->extradata_size = st->codecpar->extradata_size; + ret = update_extradata(codec_config); + if (ret < 0) + goto fail; + } + + for (j = 0; j < iamf->nb_codec_configs; j++) { + if (!memcmp(iamf->codec_configs[j], codec_config, offsetof(IAMFCodecConfig, extradata)) && + (!codec_config->extradata_size || !memcmp(iamf->codec_configs[j]->extradata, + codec_config->extradata, codec_config->extradata_size))) + break; + } + + if (j < iamf->nb_codec_configs) { + av_free(iamf->codec_configs[j]->extradata); + av_free(iamf->codec_configs[j]); + iamf->codec_configs[j] = codec_config; + return j; + } + + tmp = av_realloc_array(iamf->codec_configs, iamf->nb_codec_configs + 1, sizeof(*iamf->codec_configs)); + if (!tmp) { + ret = AVERROR(ENOMEM); + goto fail; + } + + iamf->codec_configs = tmp; + iamf->codec_configs[iamf->nb_codec_configs] = codec_config; + codec_config->codec_config_id = iamf->nb_codec_configs; + + return iamf->nb_codec_configs++; + +fail: + av_freep(&codec_config->extradata); + return ret; +} + +static int add_param_definition(IAMFContext *iamf, AVIAMFParamDefinition *param, + const IAMFAudioElement *audio_element, void *log_ctx) +{ + IAMFParamDefinition **tmp, *param_definition; + IAMFCodecConfig *codec_config = NULL; + + tmp = av_realloc_array(iamf->param_definitions, iamf->nb_param_definitions + 1, + sizeof(*iamf->param_definitions)); + if (!tmp) + return AVERROR(ENOMEM); + + iamf->param_definitions = tmp; + + if (audio_element) + codec_config = iamf->codec_configs[audio_element->codec_config_id]; + + if (!param->parameter_rate) { + if (!codec_config) { + av_log(log_ctx, AV_LOG_ERROR, "parameter_rate needed but not set for parameter_id %u\n", + param->parameter_id); + return AVERROR(EINVAL); + } + param->parameter_rate = codec_config->sample_rate; + } + if (codec_config) { + if (!param->duration) + param->duration = codec_config->nb_samples; + if (!param->constant_subblock_duration) + param->constant_subblock_duration = codec_config->nb_samples; + } + + param_definition = av_mallocz(sizeof(*param_definition)); + if (!param_definition) + return AVERROR(ENOMEM); + + param_definition->mode = !!param->duration; + param_definition->param = param; + param_definition->audio_element = audio_element; + iamf->param_definitions[iamf->nb_param_definitions++] = param_definition; + + return 0; +} + +int ff_iamf_add_audio_element(IAMFContext *iamf, const AVStreamGroup *stg, void *log_ctx) +{ + const AVIAMFAudioElement *iamf_audio_element; + IAMFAudioElement **tmp, *audio_element; + IAMFCodecConfig *codec_config; + int ret; + + if (stg->type != AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT) + return AVERROR(EINVAL); + + iamf_audio_element = stg->params.iamf_audio_element; + if (iamf_audio_element->audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_SCENE) { + const AVIAMFLayer *layer = iamf_audio_element->layers[0]; + if (iamf_audio_element->nb_layers != 1) { + av_log(log_ctx, AV_LOG_ERROR, "Invalid amount of layers for SCENE_BASED audio element. Must be 1\n"); + return AVERROR(EINVAL); + } + if (layer->ch_layout.order != AV_CHANNEL_ORDER_CUSTOM && + layer->ch_layout.order != AV_CHANNEL_ORDER_AMBISONIC) { + av_log(log_ctx, AV_LOG_ERROR, "Invalid channel layout for SCENE_BASED audio element\n"); + return AVERROR(EINVAL); + } + if (layer->ambisonics_mode >= AV_IAMF_AMBISONICS_MODE_PROJECTION) { + av_log(log_ctx, AV_LOG_ERROR, "Unsuported ambisonics mode %d\n", layer->ambisonics_mode); + return AVERROR_PATCHWELCOME; + } + for (int i = 0; i < stg->nb_streams; i++) { + if (stg->streams[i]->codecpar->ch_layout.nb_channels > 1) { + av_log(log_ctx, AV_LOG_ERROR, "Invalid amount of channels in a stream for MONO mode ambisonics\n"); + return AVERROR(EINVAL); + } + } + } else + for (int j, i = 0; i < iamf_audio_element->nb_layers; i++) { + const AVIAMFLayer *layer = iamf_audio_element->layers[i]; + for (j = 0; j < FF_ARRAY_ELEMS(ff_iamf_scalable_ch_layouts); j++) + if (!av_channel_layout_compare(&layer->ch_layout, &ff_iamf_scalable_ch_layouts[j])) + break; + + if (j >= FF_ARRAY_ELEMS(ff_iamf_scalable_ch_layouts)) { + av_log(log_ctx, AV_LOG_ERROR, "Unsupported channel layout in stream group #%d\n", i); + return AVERROR(EINVAL); + } + } + + for (int i = 0; i < iamf->nb_audio_elements; i++) { + if (stg->id == iamf->audio_elements[i]->audio_element_id) { + av_log(log_ctx, AV_LOG_ERROR, "Duplicated Audio Element id %"PRId64"\n", stg->id); + return AVERROR(EINVAL); + } + } + + codec_config = av_mallocz(sizeof(*codec_config)); + if (!codec_config) + return AVERROR(ENOMEM); + + ret = fill_codec_config(iamf, stg, codec_config); + if (ret < 0) { + av_free(codec_config); + return ret; + } + + audio_element = av_mallocz(sizeof(*audio_element)); + if (!audio_element) + return AVERROR(ENOMEM); + + audio_element->celement = stg->params.iamf_audio_element; + audio_element->audio_element_id = stg->id; + audio_element->codec_config_id = ret; + + audio_element->substreams = av_calloc(stg->nb_streams, sizeof(*audio_element->substreams)); + if (!audio_element->substreams) { + ret = AVERROR(ENOMEM); + goto fail; + } + audio_element->nb_substreams = stg->nb_streams; + + audio_element->layers = av_calloc(iamf_audio_element->nb_layers, sizeof(*audio_element->layers)); + if (!audio_element->layers) { + ret = AVERROR(ENOMEM); + goto fail; + } + + for (int i = 0, j = 0; i < iamf_audio_element->nb_layers; i++) { + int nb_channels = iamf_audio_element->layers[i]->ch_layout.nb_channels; + + IAMFLayer *layer = &audio_element->layers[i]; + + if (i) + nb_channels -= iamf_audio_element->layers[i - 1]->ch_layout.nb_channels; + for (; nb_channels > 0 && j < stg->nb_streams; j++) { + const AVStream *st = stg->streams[j]; + IAMFSubStream *substream = &audio_element->substreams[j]; + + substream->audio_substream_id = st->id; + layer->substream_count++; + layer->coupled_substream_count += st->codecpar->ch_layout.nb_channels == 2; + nb_channels -= st->codecpar->ch_layout.nb_channels; + } + if (nb_channels) { + av_log(log_ctx, AV_LOG_ERROR, "Invalid channel count across substreams in layer %u from stream group %u\n", + i, stg->index); + ret = AVERROR(EINVAL); + goto fail; + } + } + + for (int i = 0; i < audio_element->nb_substreams; i++) { + for (int j = i + 1; j < audio_element->nb_substreams; j++) + if (audio_element->substreams[i].audio_substream_id == + audio_element->substreams[j].audio_substream_id) { + av_log(log_ctx, AV_LOG_ERROR, "Duplicate id %u in streams %u and %u from stream group %u\n", + audio_element->substreams[i].audio_substream_id, i, j, stg->index); + ret = AVERROR(EINVAL); + goto fail; + } + } + + if (iamf_audio_element->demixing_info) { + AVIAMFParamDefinition *param = iamf_audio_element->demixing_info; + const IAMFParamDefinition *param_definition = ff_iamf_get_param_definition(iamf, param->parameter_id); + + if (param->nb_subblocks != 1) { + av_log(log_ctx, AV_LOG_ERROR, "nb_subblocks in demixing_info for stream group %u is not 1\n", stg->index); + ret = AVERROR(EINVAL); + goto fail; + } + + if (!param_definition) { + ret = add_param_definition(iamf, param, audio_element, log_ctx); + if (ret < 0) + goto fail; + } + } + if (iamf_audio_element->recon_gain_info) { + AVIAMFParamDefinition *param = iamf_audio_element->recon_gain_info; + const IAMFParamDefinition *param_definition = ff_iamf_get_param_definition(iamf, param->parameter_id); + + if (param->nb_subblocks != 1) { + av_log(log_ctx, AV_LOG_ERROR, "nb_subblocks in recon_gain_info for stream group %u is not 1\n", stg->index); + ret = AVERROR(EINVAL); + goto fail; + } + + if (!param_definition) { + ret = add_param_definition(iamf, param, audio_element, log_ctx); + if (ret < 0) + goto fail; + } + } + + tmp = av_realloc_array(iamf->audio_elements, iamf->nb_audio_elements + 1, sizeof(*iamf->audio_elements)); + if (!tmp) { + ret = AVERROR(ENOMEM); + goto fail; + } + + iamf->audio_elements = tmp; + iamf->audio_elements[iamf->nb_audio_elements++] = audio_element; + + return 0; +fail: + ff_iamf_free_audio_element(&audio_element); + return ret; +} + +int ff_iamf_add_mix_presentation(IAMFContext *iamf, const AVStreamGroup *stg, void *log_ctx) +{ + IAMFMixPresentation **tmp, *mix_presentation; + int ret; + + if (stg->type != AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION) + return AVERROR(EINVAL); + + for (int i = 0; i < iamf->nb_mix_presentations; i++) { + if (stg->id == iamf->mix_presentations[i]->mix_presentation_id) { + av_log(log_ctx, AV_LOG_ERROR, "Duplicate Mix Presentation id %"PRId64"\n", stg->id); + return AVERROR(EINVAL); + } + } + + mix_presentation = av_mallocz(sizeof(*mix_presentation)); + if (!mix_presentation) + return AVERROR(ENOMEM); + + mix_presentation->cmix = stg->params.iamf_mix_presentation; + mix_presentation->mix_presentation_id = stg->id; + + for (int i = 0; i < mix_presentation->cmix->nb_submixes; i++) { + const AVIAMFSubmix *submix = mix_presentation->cmix->submixes[i]; + AVIAMFParamDefinition *param = submix->output_mix_config; + IAMFParamDefinition *param_definition; + + if (!param) { + av_log(log_ctx, AV_LOG_ERROR, "output_mix_config is not present in submix %u from " + "Mix Presentation ID %"PRId64"\n", i, stg->id); + ret = AVERROR(EINVAL); + goto fail; + } + + param_definition = ff_iamf_get_param_definition(iamf, param->parameter_id); + if (!param_definition) { + ret = add_param_definition(iamf, param, NULL, log_ctx); + if (ret < 0) + goto fail; + } + + for (int j = 0; j < submix->nb_elements; j++) { + const AVIAMFSubmixElement *element = submix->elements[j]; + param = element->element_mix_config; + + if (!param) { + av_log(log_ctx, AV_LOG_ERROR, "element_mix_config is not present for element %u in submix %u from " + "Mix Presentation ID %"PRId64"\n", j, i, stg->id); + ret = AVERROR(EINVAL); + goto fail; + } + param_definition = ff_iamf_get_param_definition(iamf, param->parameter_id); + if (!param_definition) { + ret = add_param_definition(iamf, param, NULL, log_ctx); + if (ret < 0) + goto fail; + } + } + } + + tmp = av_realloc_array(iamf->mix_presentations, iamf->nb_mix_presentations + 1, sizeof(*iamf->mix_presentations)); + if (!tmp) { + ret = AVERROR(ENOMEM); + goto fail; + } + + iamf->mix_presentations = tmp; + iamf->mix_presentations[iamf->nb_mix_presentations++] = mix_presentation; + + return 0; +fail: + ff_iamf_free_mix_presentation(&mix_presentation); + return ret; +} + +static int iamf_write_codec_config(const IAMFContext *iamf, + const IAMFCodecConfig *codec_config, + AVIOContext *pb) +{ + uint8_t header[MAX_IAMF_OBU_HEADER_SIZE]; + AVIOContext *dyn_bc; + uint8_t *dyn_buf = NULL; + PutBitContext pbc; + int dyn_size; + + int ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + + ffio_write_leb(dyn_bc, codec_config->codec_config_id); + avio_wl32(dyn_bc, codec_config->codec_tag); + + ffio_write_leb(dyn_bc, codec_config->nb_samples); + avio_wb16(dyn_bc, codec_config->audio_roll_distance); + + switch(codec_config->codec_id) { + case AV_CODEC_ID_OPUS: + avio_write(dyn_bc, codec_config->extradata, codec_config->extradata_size); + break; + case AV_CODEC_ID_AAC: + return AVERROR_PATCHWELCOME; + case AV_CODEC_ID_FLAC: + avio_w8(dyn_bc, 0x80); + avio_wb24(dyn_bc, codec_config->extradata_size); + avio_write(dyn_bc, codec_config->extradata, codec_config->extradata_size); + break; + case AV_CODEC_ID_PCM_S16LE: + avio_w8(dyn_bc, 1); + avio_w8(dyn_bc, 16); + avio_wb32(dyn_bc, codec_config->sample_rate); + break; + case AV_CODEC_ID_PCM_S24LE: + avio_w8(dyn_bc, 1); + avio_w8(dyn_bc, 24); + avio_wb32(dyn_bc, codec_config->sample_rate); + break; + case AV_CODEC_ID_PCM_S32LE: + avio_w8(dyn_bc, 1); + avio_w8(dyn_bc, 32); + avio_wb32(dyn_bc, codec_config->sample_rate); + break; + case AV_CODEC_ID_PCM_S16BE: + avio_w8(dyn_bc, 0); + avio_w8(dyn_bc, 16); + avio_wb32(dyn_bc, codec_config->sample_rate); + break; + case AV_CODEC_ID_PCM_S24BE: + avio_w8(dyn_bc, 0); + avio_w8(dyn_bc, 24); + avio_wb32(dyn_bc, codec_config->sample_rate); + break; + case AV_CODEC_ID_PCM_S32BE: + avio_w8(dyn_bc, 0); + avio_w8(dyn_bc, 32); + avio_wb32(dyn_bc, codec_config->sample_rate); + break; + default: + break; + } + + init_put_bits(&pbc, header, sizeof(header)); + put_bits(&pbc, 5, IAMF_OBU_IA_CODEC_CONFIG); + put_bits(&pbc, 3, 0); + flush_put_bits(&pbc); + + dyn_size = avio_get_dyn_buf(dyn_bc, &dyn_buf); + avio_write(pb, header, put_bytes_count(&pbc, 1)); + ffio_write_leb(pb, dyn_size); + avio_write(pb, dyn_buf, dyn_size); + ffio_free_dyn_buf(&dyn_bc); + + return 0; +} + +static inline int rescale_rational(AVRational q, int b) +{ + return av_clip_int16(av_rescale(q.num, b, q.den)); +} + +static int scalable_channel_layout_config(const IAMFAudioElement *audio_element, + AVIOContext *dyn_bc) +{ + const AVIAMFAudioElement *element = audio_element->celement; + uint8_t header[MAX_IAMF_OBU_HEADER_SIZE]; + PutBitContext pb; + + init_put_bits(&pb, header, sizeof(header)); + put_bits(&pb, 3, element->nb_layers); + put_bits(&pb, 5, 0); + flush_put_bits(&pb); + avio_write(dyn_bc, header, put_bytes_count(&pb, 1)); + for (int i = 0; i < element->nb_layers; i++) { + const AVIAMFLayer *layer = element->layers[i]; + int layout; + for (layout = 0; layout < FF_ARRAY_ELEMS(ff_iamf_scalable_ch_layouts); layout++) { + if (!av_channel_layout_compare(&layer->ch_layout, &ff_iamf_scalable_ch_layouts[layout])) + break; + } + init_put_bits(&pb, header, sizeof(header)); + put_bits(&pb, 4, layout); + put_bits(&pb, 1, !!layer->output_gain_flags); + put_bits(&pb, 1, !!(layer->flags & AV_IAMF_LAYER_FLAG_RECON_GAIN)); + put_bits(&pb, 2, 0); // reserved + put_bits(&pb, 8, audio_element->layers[i].substream_count); + put_bits(&pb, 8, audio_element->layers[i].coupled_substream_count); + if (layer->output_gain_flags) { + put_bits(&pb, 6, layer->output_gain_flags); + put_bits(&pb, 2, 0); + put_bits(&pb, 16, rescale_rational(layer->output_gain, 1 << 8)); + } + flush_put_bits(&pb); + avio_write(dyn_bc, header, put_bytes_count(&pb, 1)); + } + + return 0; +} + +static int ambisonics_config(const IAMFAudioElement *audio_element, + AVIOContext *dyn_bc) +{ + const AVIAMFAudioElement *element = audio_element->celement; + const AVIAMFLayer *layer = element->layers[0]; + + ffio_write_leb(dyn_bc, 0); // ambisonics_mode + ffio_write_leb(dyn_bc, layer->ch_layout.nb_channels); // output_channel_count + ffio_write_leb(dyn_bc, audio_element->nb_substreams); // substream_count + + if (layer->ch_layout.order == AV_CHANNEL_ORDER_AMBISONIC) + for (int i = 0; i < layer->ch_layout.nb_channels; i++) + avio_w8(dyn_bc, i); + else + for (int i = 0; i < layer->ch_layout.nb_channels; i++) + avio_w8(dyn_bc, layer->ch_layout.u.map[i].id); + + return 0; +} + +static int param_definition(const IAMFContext *iamf, + const IAMFParamDefinition *param_def, + AVIOContext *dyn_bc, void *log_ctx) +{ + const AVIAMFParamDefinition *param = param_def->param; + + ffio_write_leb(dyn_bc, param->parameter_id); + ffio_write_leb(dyn_bc, param->parameter_rate); + avio_w8(dyn_bc, param->duration ? 0 : 1 << 7); + if (param->duration) { + ffio_write_leb(dyn_bc, param->duration); + ffio_write_leb(dyn_bc, param->constant_subblock_duration); + if (param->constant_subblock_duration == 0) { + ffio_write_leb(dyn_bc, param->nb_subblocks); + for (int i = 0; i < param->nb_subblocks; i++) { + const void *subblock = av_iamf_param_definition_get_subblock(param, i); + + switch (param->type) { + case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: { + const AVIAMFMixGain *mix = subblock; + ffio_write_leb(dyn_bc, mix->subblock_duration); + break; + } + case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: { + const AVIAMFDemixingInfo *demix = subblock; + ffio_write_leb(dyn_bc, demix->subblock_duration); + break; + } + case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: { + const AVIAMFReconGain *recon = subblock; + ffio_write_leb(dyn_bc, recon->subblock_duration); + break; + } + } + } + } + } + + return 0; +} + +static int iamf_write_audio_element(const IAMFContext *iamf, + const IAMFAudioElement *audio_element, + AVIOContext *pb, void *log_ctx) +{ + const AVIAMFAudioElement *element = audio_element->celement; + const IAMFCodecConfig *codec_config = iamf->codec_configs[audio_element->codec_config_id]; + uint8_t header[MAX_IAMF_OBU_HEADER_SIZE]; + AVIOContext *dyn_bc; + uint8_t *dyn_buf = NULL; + PutBitContext pbc; + int param_definition_types = AV_IAMF_PARAMETER_DEFINITION_DEMIXING, dyn_size; + + int ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + + ffio_write_leb(dyn_bc, audio_element->audio_element_id); + + init_put_bits(&pbc, header, sizeof(header)); + put_bits(&pbc, 3, element->audio_element_type); + put_bits(&pbc, 5, 0); + flush_put_bits(&pbc); + avio_write(dyn_bc, header, put_bytes_count(&pbc, 1)); + + ffio_write_leb(dyn_bc, audio_element->codec_config_id); + ffio_write_leb(dyn_bc, audio_element->nb_substreams); + + for (int i = 0; i < audio_element->nb_substreams; i++) + ffio_write_leb(dyn_bc, audio_element->substreams[i].audio_substream_id); + + if (element->nb_layers == 1) + param_definition_types &= ~AV_IAMF_PARAMETER_DEFINITION_DEMIXING; + if (element->nb_layers > 1) + param_definition_types |= AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN; + if (codec_config->codec_tag == MKTAG('f','L','a','C') || + codec_config->codec_tag == MKTAG('i','p','c','m')) + param_definition_types &= ~AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN; + + ffio_write_leb(dyn_bc, av_popcount(param_definition_types)); // num_parameters + + if (param_definition_types & 1) { + const AVIAMFParamDefinition *param = element->demixing_info; + const IAMFParamDefinition *param_def; + const AVIAMFDemixingInfo *demix; + + if (!param) { + av_log(log_ctx, AV_LOG_ERROR, "demixing_info needed but not set in Stream Group #%u\n", + audio_element->audio_element_id); + return AVERROR(EINVAL); + } + + demix = av_iamf_param_definition_get_subblock(param, 0); + ffio_write_leb(dyn_bc, AV_IAMF_PARAMETER_DEFINITION_DEMIXING); // type + + param_def = ff_iamf_get_param_definition(iamf, param->parameter_id); + ret = param_definition(iamf, param_def, dyn_bc, log_ctx); + if (ret < 0) + return ret; + + avio_w8(dyn_bc, demix->dmixp_mode << 5); // dmixp_mode + avio_w8(dyn_bc, element->default_w << 4); // default_w + } + if (param_definition_types & 2) { + const AVIAMFParamDefinition *param = element->recon_gain_info; + const IAMFParamDefinition *param_def; + + if (!param) { + av_log(log_ctx, AV_LOG_ERROR, "recon_gain_info needed but not set in Stream Group #%u\n", + audio_element->audio_element_id); + return AVERROR(EINVAL); + } + ffio_write_leb(dyn_bc, AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN); // type + + param_def = ff_iamf_get_param_definition(iamf, param->parameter_id); + ret = param_definition(iamf, param_def, dyn_bc, log_ctx); + if (ret < 0) + return ret; + } + + if (element->audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL) { + ret = scalable_channel_layout_config(audio_element, dyn_bc); + if (ret < 0) + return ret; + } else { + ret = ambisonics_config(audio_element, dyn_bc); + if (ret < 0) + return ret; + } + + init_put_bits(&pbc, header, sizeof(header)); + put_bits(&pbc, 5, IAMF_OBU_IA_AUDIO_ELEMENT); + put_bits(&pbc, 3, 0); + flush_put_bits(&pbc); + + dyn_size = avio_get_dyn_buf(dyn_bc, &dyn_buf); + avio_write(pb, header, put_bytes_count(&pbc, 1)); + ffio_write_leb(pb, dyn_size); + avio_write(pb, dyn_buf, dyn_size); + ffio_free_dyn_buf(&dyn_bc); + + return 0; +} + +static int iamf_write_mixing_presentation(const IAMFContext *iamf, + const IAMFMixPresentation *mix_presentation, + AVIOContext *pb, void *log_ctx) +{ + uint8_t header[MAX_IAMF_OBU_HEADER_SIZE]; + const AVIAMFMixPresentation *mix = mix_presentation->cmix; + const AVDictionaryEntry *tag = NULL; + PutBitContext pbc; + AVIOContext *dyn_bc; + uint8_t *dyn_buf = NULL; + int dyn_size; + + int ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + + ffio_write_leb(dyn_bc, mix_presentation->mix_presentation_id); // mix_presentation_id + ffio_write_leb(dyn_bc, av_dict_count(mix->annotations)); // count_label + + while ((tag = av_dict_iterate(mix->annotations, tag))) + avio_put_str(dyn_bc, tag->key); + while ((tag = av_dict_iterate(mix->annotations, tag))) + avio_put_str(dyn_bc, tag->value); + + ffio_write_leb(dyn_bc, mix->nb_submixes); + for (int i = 0; i < mix->nb_submixes; i++) { + const AVIAMFSubmix *sub_mix = mix->submixes[i]; + const IAMFParamDefinition *param_def; + + ffio_write_leb(dyn_bc, sub_mix->nb_elements); + for (int j = 0; j < sub_mix->nb_elements; j++) { + const IAMFAudioElement *audio_element = NULL; + const AVIAMFSubmixElement *submix_element = sub_mix->elements[j]; + + for (int k = 0; k < iamf->nb_audio_elements; k++) + if (iamf->audio_elements[k]->audio_element_id == submix_element->audio_element_id) { + audio_element = iamf->audio_elements[k]; + break; + } + + av_assert0(audio_element); + ffio_write_leb(dyn_bc, submix_element->audio_element_id); + + if (av_dict_count(submix_element->annotations) != av_dict_count(mix->annotations)) { + av_log(log_ctx, AV_LOG_ERROR, "Inconsistent amount of labels in submix %d from Mix Presentation id #%u\n", + j, audio_element->audio_element_id); + return AVERROR(EINVAL); + } + while ((tag = av_dict_iterate(submix_element->annotations, tag))) + avio_put_str(dyn_bc, tag->value); + + init_put_bits(&pbc, header, sizeof(header)); + put_bits(&pbc, 2, submix_element->headphones_rendering_mode); + put_bits(&pbc, 6, 0); // reserved + flush_put_bits(&pbc); + avio_write(dyn_bc, header, put_bytes_count(&pbc, 1)); + ffio_write_leb(dyn_bc, 0); // rendering_config_extension_size + + param_def = ff_iamf_get_param_definition(iamf, submix_element->element_mix_config->parameter_id); + ret = param_definition(iamf, param_def, dyn_bc, log_ctx); + if (ret < 0) + return ret; + + avio_wb16(dyn_bc, rescale_rational(submix_element->default_mix_gain, 1 << 8)); + } + + param_def = ff_iamf_get_param_definition(iamf, sub_mix->output_mix_config->parameter_id); + ret = param_definition(iamf, param_def, dyn_bc, log_ctx); + if (ret < 0) + return ret; + avio_wb16(dyn_bc, rescale_rational(sub_mix->default_mix_gain, 1 << 8)); + + ffio_write_leb(dyn_bc, sub_mix->nb_layouts); // nb_layouts + for (int i = 0; i < sub_mix->nb_layouts; i++) { + const AVIAMFSubmixLayout *submix_layout = sub_mix->layouts[i]; + int layout, info_type; + int dialogue = submix_layout->dialogue_anchored_loudness.num && + submix_layout->dialogue_anchored_loudness.den; + int album = submix_layout->album_anchored_loudness.num && + submix_layout->album_anchored_loudness.den; + + if (submix_layout->layout_type == AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS) { + for (layout = 0; layout < FF_ARRAY_ELEMS(ff_iamf_sound_system_map); layout++) { + if (!av_channel_layout_compare(&submix_layout->sound_system, &ff_iamf_sound_system_map[layout].layout)) + break; + } + if (layout == FF_ARRAY_ELEMS(ff_iamf_sound_system_map)) { + av_log(log_ctx, AV_LOG_ERROR, "Invalid Sound System value in a submix\n"); + return AVERROR(EINVAL); + } + } + init_put_bits(&pbc, header, sizeof(header)); + put_bits(&pbc, 2, submix_layout->layout_type); // layout_type + if (submix_layout->layout_type == AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS) { + put_bits(&pbc, 4, ff_iamf_sound_system_map[layout].id); // sound_system + put_bits(&pbc, 2, 0); // reserved + } else + put_bits(&pbc, 6, 0); // reserved + flush_put_bits(&pbc); + avio_write(dyn_bc, header, put_bytes_count(&pbc, 1)); + + info_type = (submix_layout->true_peak.num && submix_layout->true_peak.den); + info_type |= (dialogue || album) << 1; + avio_w8(dyn_bc, info_type); + avio_wb16(dyn_bc, rescale_rational(submix_layout->integrated_loudness, 1 << 8)); + avio_wb16(dyn_bc, rescale_rational(submix_layout->digital_peak, 1 << 8)); + if (info_type & 1) + avio_wb16(dyn_bc, rescale_rational(submix_layout->true_peak, 1 << 8)); + if (info_type & 2) { + avio_w8(dyn_bc, dialogue + album); // num_anchored_loudness + if (dialogue) { + avio_w8(dyn_bc, IAMF_ANCHOR_ELEMENT_DIALOGUE); + avio_wb16(dyn_bc, rescale_rational(submix_layout->dialogue_anchored_loudness, 1 << 8)); + } + if (album) { + avio_w8(dyn_bc, IAMF_ANCHOR_ELEMENT_ALBUM); + avio_wb16(dyn_bc, rescale_rational(submix_layout->album_anchored_loudness, 1 << 8)); + } + } + } + } + + init_put_bits(&pbc, header, sizeof(header)); + put_bits(&pbc, 5, IAMF_OBU_IA_MIX_PRESENTATION); + put_bits(&pbc, 3, 0); + flush_put_bits(&pbc); + + dyn_size = avio_get_dyn_buf(dyn_bc, &dyn_buf); + avio_write(pb, header, put_bytes_count(&pbc, 1)); + ffio_write_leb(pb, dyn_size); + avio_write(pb, dyn_buf, dyn_size); + ffio_free_dyn_buf(&dyn_bc); + + return 0; +} + +int ff_iamf_write_descriptors(const IAMFContext *iamf, AVIOContext *pb, void *log_ctx) +{ + int ret; + + // Sequence Header + avio_w8(pb, IAMF_OBU_IA_SEQUENCE_HEADER << 3); + + ffio_write_leb(pb, 6); + avio_wb32(pb, MKBETAG('i','a','m','f')); + avio_w8(pb, iamf->nb_audio_elements > 1); // primary_profile + avio_w8(pb, iamf->nb_audio_elements > 1); // additional_profile + + for (int i = 0; i < iamf->nb_codec_configs; i++) { + ret = iamf_write_codec_config(iamf, iamf->codec_configs[i], pb); + if (ret < 0) + return ret; + } + + for (int i = 0; i < iamf->nb_audio_elements; i++) { + ret = iamf_write_audio_element(iamf, iamf->audio_elements[i], pb, log_ctx); + if (ret < 0) + return ret; + } + + for (int i = 0; i < iamf->nb_mix_presentations; i++) { + ret = iamf_write_mixing_presentation(iamf, iamf->mix_presentations[i], pb, log_ctx); + if (ret < 0) + return ret; + } + + return 0; +} + +static int write_parameter_block(const IAMFContext *iamf, AVIOContext *pb, + const AVIAMFParamDefinition *param, void *log_ctx) +{ + uint8_t header[MAX_IAMF_OBU_HEADER_SIZE]; + const IAMFParamDefinition *param_definition = ff_iamf_get_param_definition(iamf, param->parameter_id); + PutBitContext pbc; + AVIOContext *dyn_bc; + uint8_t *dyn_buf = NULL; + int dyn_size, ret; + + if (param->type > AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN) { + av_log(log_ctx, AV_LOG_DEBUG, "Ignoring side data with unknown type %u\n", + param->type); + return 0; + } + + if (!param_definition) { + av_log(log_ctx, AV_LOG_ERROR, "Non-existent Parameter Definition with ID %u referenced by a packet\n", + param->parameter_id); + return AVERROR(EINVAL); + } + + if (param->type != param_definition->param->type) { + av_log(log_ctx, AV_LOG_ERROR, "Inconsistent values for Parameter Definition " + "with ID %u in a packet\n", + param->parameter_id); + return AVERROR(EINVAL); + } + + ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + + // Sequence Header + init_put_bits(&pbc, header, sizeof(header)); + put_bits(&pbc, 5, IAMF_OBU_IA_PARAMETER_BLOCK); + put_bits(&pbc, 3, 0); + flush_put_bits(&pbc); + avio_write(pb, header, put_bytes_count(&pbc, 1)); + + ffio_write_leb(dyn_bc, param->parameter_id); + if (!param_definition->mode) { + ffio_write_leb(dyn_bc, param->duration); + ffio_write_leb(dyn_bc, param->constant_subblock_duration); + if (param->constant_subblock_duration == 0) + ffio_write_leb(dyn_bc, param->nb_subblocks); + } + + for (int i = 0; i < param->nb_subblocks; i++) { + const void *subblock = av_iamf_param_definition_get_subblock(param, i); + + switch (param->type) { + case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: { + const AVIAMFMixGain *mix = subblock; + if (!param_definition->mode && param->constant_subblock_duration == 0) + ffio_write_leb(dyn_bc, mix->subblock_duration); + + ffio_write_leb(dyn_bc, mix->animation_type); + + avio_wb16(dyn_bc, rescale_rational(mix->start_point_value, 1 << 8)); + if (mix->animation_type >= AV_IAMF_ANIMATION_TYPE_LINEAR) + avio_wb16(dyn_bc, rescale_rational(mix->end_point_value, 1 << 8)); + if (mix->animation_type == AV_IAMF_ANIMATION_TYPE_BEZIER) { + avio_wb16(dyn_bc, rescale_rational(mix->control_point_value, 1 << 8)); + avio_w8(dyn_bc, av_clip_uint8(av_rescale(mix->control_point_relative_time.num, 1 << 8, + mix->control_point_relative_time.den))); + } + break; + } + case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: { + const AVIAMFDemixingInfo *demix = subblock; + if (!param_definition->mode && param->constant_subblock_duration == 0) + ffio_write_leb(dyn_bc, demix->subblock_duration); + + avio_w8(dyn_bc, demix->dmixp_mode << 5); + break; + } + case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: { + const AVIAMFReconGain *recon = subblock; + const AVIAMFAudioElement *audio_element = param_definition->audio_element->celement; + + if (!param_definition->mode && param->constant_subblock_duration == 0) + ffio_write_leb(dyn_bc, recon->subblock_duration); + + if (!audio_element) { + av_log(log_ctx, AV_LOG_ERROR, "Invalid Parameter Definition with ID %u referenced by a packet\n", param->parameter_id); + return AVERROR(EINVAL); + } + + for (int j = 0; j < audio_element->nb_layers; j++) { + const AVIAMFLayer *layer = audio_element->layers[j]; + + if (layer->flags & AV_IAMF_LAYER_FLAG_RECON_GAIN) { + unsigned int recon_gain_flags = 0; + int k = 0; + + for (; k < 7; k++) + recon_gain_flags |= (1 << k) * !!recon->recon_gain[j][k]; + for (; k < 12; k++) + recon_gain_flags |= (2 << k) * !!recon->recon_gain[j][k]; + if (recon_gain_flags >> 8) + recon_gain_flags |= (1 << k); + + ffio_write_leb(dyn_bc, recon_gain_flags); + for (k = 0; k < 12; k++) { + if (recon->recon_gain[j][k]) + avio_w8(dyn_bc, recon->recon_gain[j][k]); + } + } + } + break; + } + default: + av_assert0(0); + } + } + + dyn_size = avio_get_dyn_buf(dyn_bc, &dyn_buf); + ffio_write_leb(pb, dyn_size); + avio_write(pb, dyn_buf, dyn_size); + ffio_free_dyn_buf(&dyn_bc); + + return 0; +} + +int ff_iamf_write_parameter_blocks(const IAMFContext *iamf, AVIOContext *pb, + const AVPacket *pkt, void *log_ctx) +{ + AVIAMFParamDefinition *mix = + (AVIAMFParamDefinition *)av_packet_get_side_data(pkt, + AV_PKT_DATA_IAMF_MIX_GAIN_PARAM, + NULL); + AVIAMFParamDefinition *demix = + (AVIAMFParamDefinition *)av_packet_get_side_data(pkt, + AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM, + NULL); + AVIAMFParamDefinition *recon = + (AVIAMFParamDefinition *)av_packet_get_side_data(pkt, + AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM, + NULL); + + if (mix) { + int ret = write_parameter_block(iamf, pb, mix, log_ctx); + if (ret < 0) + return ret; + } + if (demix) { + int ret = write_parameter_block(iamf, pb, demix, log_ctx); + if (ret < 0) + return ret; + } + if (recon) { + int ret = write_parameter_block(iamf, pb, recon, log_ctx); + if (ret < 0) + return ret; + } + + return 0; +} + +static IAMFAudioElement *get_audio_element(const IAMFContext *c, + unsigned int audio_substream_id) +{ + for (int i = 0; i < c->nb_audio_elements; i++) { + IAMFAudioElement *audio_element = c->audio_elements[i]; + for (int j = 0; j < audio_element->nb_substreams; j++) { + IAMFSubStream *substream = &audio_element->substreams[j]; + if (substream->audio_substream_id == audio_substream_id) + return audio_element; + } + } + + return NULL; +} + +int ff_iamf_write_audio_frame(const IAMFContext *iamf, AVIOContext *pb, + unsigned audio_substream_id, const AVPacket *pkt) +{ + uint8_t header[MAX_IAMF_OBU_HEADER_SIZE]; + PutBitContext pbc; + AVIOContext *dyn_bc; + const uint8_t *side_data; + uint8_t *dyn_buf = NULL; + unsigned int skip_samples = 0, discard_padding = 0; + size_t side_data_size; + int dyn_size, type = audio_substream_id <= 17 ? + audio_substream_id + IAMF_OBU_IA_AUDIO_FRAME_ID0 : IAMF_OBU_IA_AUDIO_FRAME; + int ret; + + if (!pkt->size) { + const IAMFAudioElement *audio_element; + IAMFCodecConfig *codec_config; + size_t new_extradata_size; + const uint8_t *new_extradata = av_packet_get_side_data(pkt, + AV_PKT_DATA_NEW_EXTRADATA, + &new_extradata_size); + + if (!new_extradata) + return AVERROR_INVALIDDATA; + audio_element = get_audio_element(iamf, audio_substream_id); + if (!audio_element) + return AVERROR(EINVAL); + codec_config = ff_iamf_get_codec_config(iamf, audio_element->codec_config_id); + if (!codec_config) + return AVERROR(EINVAL); + + av_free(codec_config->extradata); + codec_config->extradata = av_memdup(new_extradata, new_extradata_size); + if (!codec_config->extradata) { + codec_config->extradata_size = 0; + return AVERROR(ENOMEM); + } + codec_config->extradata_size = new_extradata_size; + + return update_extradata(codec_config); + } + + side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES, + &side_data_size); + + if (side_data && side_data_size >= 10) { + skip_samples = AV_RL32(side_data); + discard_padding = AV_RL32(side_data + 4); + } + + ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + + init_put_bits(&pbc, header, sizeof(header)); + put_bits(&pbc, 5, type); + put_bits(&pbc, 1, 0); // obu_redundant_copy + put_bits(&pbc, 1, skip_samples || discard_padding); + put_bits(&pbc, 1, 0); // obu_extension_flag + flush_put_bits(&pbc); + avio_write(pb, header, put_bytes_count(&pbc, 1)); + + if (skip_samples || discard_padding) { + ffio_write_leb(dyn_bc, discard_padding); + ffio_write_leb(dyn_bc, skip_samples); + } + + if (audio_substream_id > 17) + ffio_write_leb(dyn_bc, audio_substream_id); + + dyn_size = avio_get_dyn_buf(dyn_bc, &dyn_buf); + ffio_write_leb(pb, dyn_size + pkt->size); + avio_write(pb, dyn_buf, dyn_size); + ffio_free_dyn_buf(&dyn_bc); + avio_write(pb, pkt->data, pkt->size); + + return 0; +} diff --git a/libavformat/iamf_writer.h b/libavformat/iamf_writer.h new file mode 100644 index 00000000000..05f3d322b82 --- /dev/null +++ b/libavformat/iamf_writer.h @@ -0,0 +1,42 @@ +/* + * Immersive Audio Model and Formats muxing helpers and structs + * Copyright (c) 2023 James Almer + * + * 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 + */ + +#ifndef AVFORMAT_IAMF_WRITER_H +#define AVFORMAT_IAMF_WRITER_H + +#include + +#include "libavcodec/packet.h" +#include "avformat.h" +#include "avio.h" +#include "iamf.h" + +int ff_iamf_add_audio_element(IAMFContext *iamf, const AVStreamGroup *stg, void *log_ctx); +int ff_iamf_add_mix_presentation(IAMFContext *iamf, const AVStreamGroup *stg, void *log_ctx); + +int ff_iamf_write_descriptors(const IAMFContext *iamf, AVIOContext *pb, void *log_ctx); + +int ff_iamf_write_parameter_blocks(const IAMFContext *iamf, AVIOContext *pb, + const AVPacket *pkt, void *log_ctx); +int ff_iamf_write_audio_frame(const IAMFContext *iamf, AVIOContext *pb, + unsigned audio_substream_id, const AVPacket *pkt); + +#endif /* AVFORMAT_IAMF_WRITER_H */ diff --git a/libavformat/iamfdec.c b/libavformat/iamfdec.c new file mode 100644 index 00000000000..ce6d4aa0647 --- /dev/null +++ b/libavformat/iamfdec.c @@ -0,0 +1,195 @@ +/* + * Immersive Audio Model and Formats demuxer + * Copyright (c) 2023 James Almer + * + * 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/avassert.h" +#include "libavutil/intreadwrite.h" +#include "avformat.h" +#include "demux.h" +#include "iamf.h" +#include "iamf_reader.h" +#include "iamf_parse.h" +#include "internal.h" + +//return < 0 if we need more data +static int get_score(const uint8_t *buf, int buf_size, enum IAMF_OBU_Type type, int *seq) +{ + if (type == IAMF_OBU_IA_SEQUENCE_HEADER) { + if (buf_size < 4 || AV_RB32(buf) != MKBETAG('i','a','m','f')) + return 0; + *seq = 1; + return -1; + } + if (type >= IAMF_OBU_IA_CODEC_CONFIG && type <= IAMF_OBU_IA_TEMPORAL_DELIMITER) + return *seq ? -1 : 0; + if (type >= IAMF_OBU_IA_AUDIO_FRAME && type <= IAMF_OBU_IA_AUDIO_FRAME_ID17) + return *seq ? AVPROBE_SCORE_EXTENSION + 1 : 0; + return 0; +} + +static int iamf_probe(const AVProbeData *p) +{ + unsigned obu_size; + enum IAMF_OBU_Type type; + int seq = 0, cnt = 0, start_pos; + int ret; + + while (1) { + int size = ff_iamf_parse_obu_header(p->buf + cnt, p->buf_size - cnt, + &obu_size, &start_pos, &type, + NULL, NULL); + if (size < 0) + return 0; + + ret = get_score(p->buf + cnt + start_pos, + p->buf_size - cnt - start_pos, + type, &seq); + if (ret >= 0) + return ret; + + cnt += FFMIN(size, p->buf_size - cnt); + } + return 0; +} + +static int iamf_read_header(AVFormatContext *s) +{ + IAMFDemuxContext *const c = s->priv_data; + IAMFContext *const iamf = &c->iamf; + int ret; + + ret = ff_iamfdec_read_descriptors(iamf, s->pb, INT_MAX, s); + if (ret < 0) + return ret; + + for (int i = 0; i < iamf->nb_audio_elements; i++) { + IAMFAudioElement *audio_element = iamf->audio_elements[i]; + AVStreamGroup *stg = avformat_stream_group_create(s, AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT, NULL); + + if (!stg) + return AVERROR(ENOMEM); + + av_iamf_audio_element_free(&stg->params.iamf_audio_element); + stg->id = audio_element->audio_element_id; + /* Transfer ownership */ + stg->params.iamf_audio_element = audio_element->element; + audio_element->element = NULL; + + for (int j = 0; j < audio_element->nb_substreams; j++) { + IAMFSubStream *substream = &audio_element->substreams[j]; + AVStream *st = avformat_new_stream(s, NULL); + + if (!st) + return AVERROR(ENOMEM); + + ret = avformat_stream_group_add_stream(stg, st); + if (ret < 0) + return ret; + + ret = avcodec_parameters_copy(st->codecpar, substream->codecpar); + if (ret < 0) + return ret; + + if (!i && !j && audio_element->layers[0].substream_count == 1) + st->disposition |= AV_DISPOSITION_DEFAULT; + else + st->disposition |= AV_DISPOSITION_DEPENDENT; + st->id = substream->audio_substream_id; + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); + } + } + + for (int i = 0; i < iamf->nb_mix_presentations; i++) { + IAMFMixPresentation *mix_presentation = iamf->mix_presentations[i]; + AVStreamGroup *stg = avformat_stream_group_create(s, AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION, NULL); + const AVIAMFMixPresentation *mix = mix_presentation->cmix; + + if (!stg) + return AVERROR(ENOMEM); + + av_iamf_mix_presentation_free(&stg->params.iamf_mix_presentation); + stg->id = mix_presentation->mix_presentation_id; + /* Transfer ownership */ + stg->params.iamf_mix_presentation = mix_presentation->mix; + mix_presentation->mix = NULL; + + for (int j = 0; j < mix->nb_submixes; j++) { + const AVIAMFSubmix *sub_mix = mix->submixes[j]; + + for (int k = 0; k < sub_mix->nb_elements; k++) { + const AVIAMFSubmixElement *submix_element = sub_mix->elements[k]; + AVStreamGroup *audio_element = NULL; + + for (int l = 0; l < s->nb_stream_groups; l++) + if (s->stream_groups[l]->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT && + s->stream_groups[l]->id == submix_element->audio_element_id) { + audio_element = s->stream_groups[l]; + break; + } + av_assert0(audio_element); + + for (int l = 0; l < audio_element->nb_streams; l++) { + ret = avformat_stream_group_add_stream(stg, audio_element->streams[l]); + if (ret < 0 && ret != AVERROR(EEXIST)) + return ret; + } + } + } + } + + if (!s->nb_streams) + return AVERROR_INVALIDDATA; + + return 0; +} + +static int iamf_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + IAMFDemuxContext *const c = s->priv_data; + int ret; + + ret = ff_iamf_read_packet(s, c, s->pb, INT_MAX, pkt); + if (ret < 0) + return ret; + + return 0; +} + +static int iamf_read_close(AVFormatContext *s) +{ + IAMFDemuxContext *const c = s->priv_data; + + ff_iamf_read_deinit(c); + + return 0; +} + +const FFInputFormat ff_iamf_demuxer = { + .p.name = "iamf", + .p.long_name = NULL_IF_CONFIG_SMALL("Raw Immersive Audio Model and Formats"), + .p.extensions = "iamf", + .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS | AVFMT_SHOW_IDS, + .priv_data_size = sizeof(IAMFDemuxContext), + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, + .read_probe = iamf_probe, + .read_header = iamf_read_header, + .read_packet = iamf_read_packet, + .read_close = iamf_read_close, +}; diff --git a/libavformat/iamfenc.c b/libavformat/iamfenc.c new file mode 100644 index 00000000000..3169ff1eb8b --- /dev/null +++ b/libavformat/iamfenc.c @@ -0,0 +1,201 @@ +/* + * IAMF muxer + * Copyright (c) 2023 James Almer + * + * 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 + +#include "avformat.h" +#include "iamf.h" +#include "iamf_writer.h" +#include "internal.h" +#include "mux.h" + +typedef struct IAMFMuxContext { + IAMFContext iamf; + + int64_t descriptors_offset; + int update_extradata; + + int first_stream_id; +} IAMFMuxContext; + +static int iamf_init(AVFormatContext *s) +{ + IAMFMuxContext *const c = s->priv_data; + IAMFContext *const iamf = &c->iamf; + int nb_audio_elements = 0, nb_mix_presentations = 0; + int ret; + + for (int i = 0; i < s->nb_streams; i++) { + if (s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO || + (s->streams[i]->codecpar->codec_tag != MKTAG('m','p','4','a') && + s->streams[i]->codecpar->codec_tag != MKTAG('O','p','u','s') && + s->streams[i]->codecpar->codec_tag != MKTAG('f','L','a','C') && + s->streams[i]->codecpar->codec_tag != MKTAG('i','p','c','m'))) { + av_log(s, AV_LOG_ERROR, "Unsupported codec id %s\n", + avcodec_get_name(s->streams[i]->codecpar->codec_id)); + return AVERROR(EINVAL); + } + + if (s->streams[i]->codecpar->ch_layout.nb_channels > 2) { + av_log(s, AV_LOG_ERROR, "Unsupported channel layout on stream #%d\n", i); + return AVERROR(EINVAL); + } + + for (int j = 0; j < i; j++) { + if (s->streams[i]->id == s->streams[j]->id) { + av_log(s, AV_LOG_ERROR, "Duplicated stream id %d\n", s->streams[j]->id); + return AVERROR(EINVAL); + } + } + } + + if (s->nb_stream_groups <= 1) { + av_log(s, AV_LOG_ERROR, "There must be at least two stream groups\n"); + return AVERROR(EINVAL); + } + + for (int i = 0; i < s->nb_stream_groups; i++) { + const AVStreamGroup *stg = s->stream_groups[i]; + + if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT) + nb_audio_elements++; + if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION) + nb_mix_presentations++; + } + if ((nb_audio_elements < 1 || nb_audio_elements > 2) || nb_mix_presentations < 1) { + av_log(s, AV_LOG_ERROR, "There must be >= 1 and <= 2 IAMF_AUDIO_ELEMENT and at least " + "one IAMF_MIX_PRESENTATION stream groups\n"); + return AVERROR(EINVAL); + } + + for (int i = 0; i < s->nb_stream_groups; i++) { + const AVStreamGroup *stg = s->stream_groups[i]; + if (stg->type != AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT) + continue; + + ret = ff_iamf_add_audio_element(iamf, stg, s); + if (ret < 0) + return ret; + } + + for (int i = 0; i < s->nb_stream_groups; i++) { + const AVStreamGroup *stg = s->stream_groups[i]; + if (stg->type != AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION) + continue; + + ret = ff_iamf_add_mix_presentation(iamf, stg, s); + if (ret < 0) + return ret; + } + + c->first_stream_id = s->streams[0]->id; + + return 0; +} + +static int iamf_write_header(AVFormatContext *s) +{ + IAMFMuxContext *const c = s->priv_data; + IAMFContext *const iamf = &c->iamf; + int ret; + + c->descriptors_offset = avio_tell(s->pb); + ret = ff_iamf_write_descriptors(iamf, s->pb, s); + if (ret < 0) + return ret; + + c->first_stream_id = s->streams[0]->id; + + return 0; +} + +static int iamf_write_packet(AVFormatContext *s, AVPacket *pkt) +{ + IAMFMuxContext *const c = s->priv_data; + AVStream *st = s->streams[pkt->stream_index]; + int ret = 0; + + if (st->id == c->first_stream_id) + ret = ff_iamf_write_parameter_blocks(&c->iamf, s->pb, pkt, s); + if (!ret) + ret = ff_iamf_write_audio_frame(&c->iamf, s->pb, st->id, pkt); + if (!ret && !pkt->size) + c->update_extradata = 1; + + return ret; +} + +static int iamf_write_trailer(AVFormatContext *s) +{ + const IAMFMuxContext *const c = s->priv_data; + const IAMFContext *const iamf = &c->iamf; + int64_t pos; + int ret; + + if (!c->update_extradata || !(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) + return 0; + + pos = avio_tell(s->pb); + avio_seek(s->pb, c->descriptors_offset, SEEK_SET); + ret = ff_iamf_write_descriptors(iamf, s->pb, s); + if (ret < 0) + return ret; + + avio_seek(s->pb, pos, SEEK_SET); + + return 0; +} + +static void iamf_deinit(AVFormatContext *s) +{ + IAMFMuxContext *const c = s->priv_data; + IAMFContext *const iamf = &c->iamf; + + ff_iamf_uninit_context(iamf); +} + +static const AVCodecTag iamf_codec_tags[] = { + { AV_CODEC_ID_AAC, MKTAG('m','p','4','a') }, + { AV_CODEC_ID_FLAC, MKTAG('f','L','a','C') }, + { AV_CODEC_ID_OPUS, MKTAG('O','p','u','s') }, + { AV_CODEC_ID_PCM_S16LE, MKTAG('i','p','c','m') }, + { AV_CODEC_ID_PCM_S16BE, MKTAG('i','p','c','m') }, + { AV_CODEC_ID_PCM_S24LE, MKTAG('i','p','c','m') }, + { AV_CODEC_ID_PCM_S24BE, MKTAG('i','p','c','m') }, + { AV_CODEC_ID_PCM_S32LE, MKTAG('i','p','c','m') }, + { AV_CODEC_ID_PCM_S32BE, MKTAG('i','p','c','m') }, + { AV_CODEC_ID_NONE, MKTAG('i','p','c','m') } +}; + +const FFOutputFormat ff_iamf_muxer = { + .p.name = "iamf", + .p.long_name = NULL_IF_CONFIG_SMALL("Raw Immersive Audio Model and Formats"), + .p.extensions = "iamf", + .priv_data_size = sizeof(IAMFMuxContext), + .p.audio_codec = AV_CODEC_ID_OPUS, + .init = iamf_init, + .deinit = iamf_deinit, + .write_header = iamf_write_header, + .write_packet = iamf_write_packet, + .write_trailer = iamf_write_trailer, + .p.codec_tag = (const AVCodecTag* const []){ iamf_codec_tags, NULL }, + .p.flags = AVFMT_GLOBALHEADER | AVFMT_NOTIMESTAMPS, +}; diff --git a/libavformat/icodec.c b/libavformat/icodec.c index 85dab3bca0a..808c7ab7957 100644 --- a/libavformat/icodec.c +++ b/libavformat/icodec.c @@ -28,6 +28,7 @@ #include "libavcodec/bytestream.h" #include "libavcodec/png.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct { @@ -216,14 +217,14 @@ static int ico_read_close(AVFormatContext * s) return 0; } -const AVInputFormat ff_ico_demuxer = { - .name = "ico", - .long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"), +const FFInputFormat ff_ico_demuxer = { + .p.name = "ico", + .p.long_name = NULL_IF_CONFIG_SMALL("Microsoft Windows ICO"), + .p.flags = AVFMT_NOTIMESTAMPS, .priv_data_size = sizeof(IcoDemuxContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = probe, .read_header = read_header, .read_packet = read_packet, .read_close = ico_read_close, - .flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/id3v2.c b/libavformat/id3v2.c index cb318640455..e0a7e3f3eaa 100644 --- a/libavformat/id3v2.c +++ b/libavformat/id3v2.c @@ -246,7 +246,7 @@ static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding, int ret; uint8_t tmp; uint32_t ch = 1; - int left = *maxread; + int left = *maxread, dynsize; unsigned int (*get)(AVIOContext*) = avio_rb16; AVIOContext *dynbuf; @@ -308,7 +308,11 @@ static int decode_str(AVFormatContext *s, AVIOContext *pb, int encoding, if (ch) avio_w8(dynbuf, 0); - avio_close_dyn_buf(dynbuf, dst); + dynsize = avio_close_dyn_buf(dynbuf, dst); + if (dynsize <= 0) { + av_freep(dst); + return AVERROR(ENOMEM); + } *maxread = left; return 0; @@ -366,7 +370,7 @@ static void read_uslt(AVFormatContext *s, AVIOContext *pb, int taglen, int encoding; int ok = 0; - if (taglen < 1) + if (taglen < 4) goto error; encoding = avio_r8(pb); @@ -997,8 +1001,7 @@ static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata, t++; } - ffio_init_context(&pb_local, buffer, b - buffer, 0, NULL, NULL, NULL, - NULL); + ffio_init_read_context(&pb_local, buffer, b - buffer); tlen = b - buffer; pbx = &pb_local.pub; // read from sync buffer } @@ -1034,7 +1037,7 @@ static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata, av_log(s, AV_LOG_ERROR, "Failed to uncompress tag: %d\n", err); goto seek; } - ffio_init_context(&pb_local, uncompressed_buffer, dlen, 0, NULL, NULL, NULL, NULL); + ffio_init_read_context(&pb_local, uncompressed_buffer, dlen); tlen = dlen; pbx = &pb_local.pub; // read from sync buffer } diff --git a/libavformat/idcin.c b/libavformat/idcin.c index 1560d58e390..561715d3d9b 100644 --- a/libavformat/idcin.c +++ b/libavformat/idcin.c @@ -365,13 +365,13 @@ static int idcin_read_seek(AVFormatContext *s, int stream_index, return -1; } -const AVInputFormat ff_idcin_demuxer = { - .name = "idcin", - .long_name = NULL_IF_CONFIG_SMALL("id Cinematic"), +const FFInputFormat ff_idcin_demuxer = { + .p.name = "idcin", + .p.long_name = NULL_IF_CONFIG_SMALL("id Cinematic"), + .p.flags = AVFMT_NO_BYTE_SEEK, .priv_data_size = sizeof(IdcinDemuxContext), .read_probe = idcin_probe, .read_header = idcin_read_header, .read_packet = idcin_read_packet, .read_seek = idcin_read_seek, - .flags = AVFMT_NO_BYTE_SEEK, }; diff --git a/libavformat/idroqdec.c b/libavformat/idroqdec.c index c9fc972780a..67bc1246e6d 100644 --- a/libavformat/idroqdec.c +++ b/libavformat/idroqdec.c @@ -30,6 +30,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "avio_internal.h" @@ -107,7 +108,7 @@ static int roq_read_packet(AVFormatContext *s, while (!packet_read) { if (avio_feof(s->pb)) - return AVERROR(EIO); + return AVERROR_EOF; /* get the next chunk preamble */ if ((ret = avio_read(pb, preamble, RoQ_CHUNK_PREAMBLE_SIZE)) != @@ -237,9 +238,9 @@ static int roq_read_packet(AVFormatContext *s, return ret; } -const AVInputFormat ff_roq_demuxer = { - .name = "roq", - .long_name = NULL_IF_CONFIG_SMALL("id RoQ"), +const FFInputFormat ff_roq_demuxer = { + .p.name = "roq", + .p.long_name = NULL_IF_CONFIG_SMALL("id RoQ"), .priv_data_size = sizeof(RoqDemuxContext), .read_probe = roq_probe, .read_header = roq_read_header, diff --git a/libavformat/idroqenc.c b/libavformat/idroqenc.c index 9baf9ad1b1b..f3172692e0f 100644 --- a/libavformat/idroqenc.c +++ b/libavformat/idroqenc.c @@ -66,6 +66,9 @@ const FFOutputFormat ff_roq_muxer = { .p.extensions = "roq", .p.audio_codec = AV_CODEC_ID_ROQ_DPCM, .p.video_codec = AV_CODEC_ID_ROQ, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_header = roq_write_header, .write_packet = ff_raw_write_packet, }; diff --git a/libavformat/iff.c b/libavformat/iff.c index b8e8bffe03f..0cbe1253374 100644 --- a/libavformat/iff.c +++ b/libavformat/iff.c @@ -36,6 +36,7 @@ #include "libavutil/dict.h" #include "libavcodec/bytestream.h" #include "avformat.h" +#include "demux.h" #include "id3v2.h" #include "internal.h" @@ -217,7 +218,7 @@ static int parse_dsd_diin(AVFormatContext *s, AVStream *st, uint64_t eof) { AVIOContext *pb = s->pb; - while (avio_tell(pb) + 12 <= eof && !avio_feof(pb)) { + while (av_sat_add64(avio_tell(pb), 12) <= eof && !avio_feof(pb)) { uint32_t tag = avio_rl32(pb); uint64_t size = avio_rb64(pb); uint64_t orig_pos = avio_tell(pb); @@ -254,7 +255,7 @@ static int parse_dsd_prop(AVFormatContext *s, AVStream *st, uint64_t eof) int dsd_layout[6]; ID3v2ExtraMeta *id3v2_extra_meta; - while (avio_tell(pb) + 12 <= eof && !avio_feof(pb)) { + while (av_sat_add64(avio_tell(pb), 12) <= eof && !avio_feof(pb)) { uint32_t tag = avio_rl32(pb); uint64_t size = avio_rb64(pb); uint64_t orig_pos = avio_tell(pb); @@ -901,12 +902,12 @@ static int iff_read_packet(AVFormatContext *s, return ret; } -const AVInputFormat ff_iff_demuxer = { - .name = "iff", - .long_name = NULL_IF_CONFIG_SMALL("IFF (Interchange File Format)"), +const FFInputFormat ff_iff_demuxer = { + .p.name = "iff", + .p.long_name = NULL_IF_CONFIG_SMALL("IFF (Interchange File Format)"), + .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK, .priv_data_size = sizeof(IffDemuxContext), .read_probe = iff_probe, .read_header = iff_read_header, .read_packet = iff_read_packet, - .flags = AVFMT_GENERIC_INDEX | AVFMT_NO_BYTE_SEEK, }; diff --git a/libavformat/ifv.c b/libavformat/ifv.c index 694abd951b1..0cfd2763a95 100644 --- a/libavformat/ifv.c +++ b/libavformat/ifv.c @@ -23,6 +23,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/dict_internal.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "avio_internal.h" @@ -309,11 +310,11 @@ static int ifv_read_seek(AVFormatContext *s, int stream_index, int64_t ts, int f return 0; } -const AVInputFormat ff_ifv_demuxer = { - .name = "ifv", - .long_name = NULL_IF_CONFIG_SMALL("IFV CCTV DVR"), +const FFInputFormat ff_ifv_demuxer = { + .p.name = "ifv", + .p.long_name = NULL_IF_CONFIG_SMALL("IFV CCTV DVR"), + .p.extensions = "ifv", .priv_data_size = sizeof(IFVContext), - .extensions = "ifv", .read_probe = ifv_probe, .read_header = ifv_read_header, .read_packet = ifv_read_packet, diff --git a/libavformat/ilbc.c b/libavformat/ilbc.c index 6b5bb33b620..a24aa3da9d3 100644 --- a/libavformat/ilbc.c +++ b/libavformat/ilbc.c @@ -22,6 +22,7 @@ #include "config_components.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "mux.h" #include "rawenc.h" @@ -32,18 +33,7 @@ static const char mode30_header[] = "#!iLBC30\n"; static int ilbc_write_header(AVFormatContext *s) { AVIOContext *pb = s->pb; - AVCodecParameters *par; - - if (s->nb_streams != 1) { - av_log(s, AV_LOG_ERROR, "Unsupported number of streams\n"); - return AVERROR(EINVAL); - } - par = s->streams[0]->codecpar; - - if (par->codec_id != AV_CODEC_ID_ILBC) { - av_log(s, AV_LOG_ERROR, "Unsupported codec\n"); - return AVERROR(EINVAL); - } + AVCodecParameters *par = s->streams[0]->codecpar; if (par->block_align == 50) { avio_write(pb, mode30_header, sizeof(mode30_header) - 1); @@ -111,13 +101,13 @@ static int ilbc_read_packet(AVFormatContext *s, return 0; } -const AVInputFormat ff_ilbc_demuxer = { - .name = "ilbc", - .long_name = NULL_IF_CONFIG_SMALL("iLBC storage"), +const FFInputFormat ff_ilbc_demuxer = { + .p.name = "ilbc", + .p.long_name = NULL_IF_CONFIG_SMALL("iLBC storage"), + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = ilbc_probe, .read_header = ilbc_read_header, .read_packet = ilbc_read_packet, - .flags = AVFMT_GENERIC_INDEX, }; #if CONFIG_ILBC_MUXER @@ -126,8 +116,12 @@ const FFOutputFormat ff_ilbc_muxer = { .p.long_name = NULL_IF_CONFIG_SMALL("iLBC storage"), .p.mime_type = "audio/iLBC", .p.extensions = "lbc", + .p.video_codec = AV_CODEC_ID_NONE, .p.audio_codec = AV_CODEC_ID_ILBC, + .p.subtitle_codec = AV_CODEC_ID_NONE, .p.flags = AVFMT_NOTIMESTAMPS, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_header = ilbc_write_header, .write_packet = ff_raw_write_packet, }; diff --git a/libavformat/imf.h b/libavformat/imf.h index 70ed007312c..ef124bf4124 100644 --- a/libavformat/imf.h +++ b/libavformat/imf.h @@ -140,6 +140,7 @@ typedef struct FFIMFCPL { /** * Parse an IMF CompositionPlaylist element into the FFIMFCPL data structure. + * @param[in] log_ctx Logging context (points to an instance of AVClass). May be NULL. * @param[in] doc An XML document from which the CPL is read. * @param[out] cpl Pointer to a memory area (allocated by the client), where the * function writes a pointer to the newly constructed FFIMFCPL structure (or @@ -147,10 +148,11 @@ typedef struct FFIMFCPL { * the FFIMFCPL structure using ff_imf_cpl_free(). * @return A non-zero value in case of an error. */ -int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl); +int ff_imf_parse_cpl_from_xml_dom(void *log_ctx, xmlDocPtr doc, FFIMFCPL **cpl); /** * Parse an IMF Composition Playlist document into the FFIMFCPL data structure. + * @param[in] log_ctx Logging context (points to an instance of AVClass). May be NULL. * @param[in] in The context from which the CPL is read. * @param[out] cpl Pointer to a memory area (allocated by the client), where the * function writes a pointer to the newly constructed FFIMFCPL structure (or @@ -158,7 +160,7 @@ int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl); * the FFIMFCPL structure using ff_imf_cpl_free(). * @return A non-zero value in case of an error. */ -int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl); +int ff_imf_parse_cpl(void *log_ctx, AVIOContext *in, FFIMFCPL **cpl); /** * Allocates and initializes an FFIMFCPL data structure. diff --git a/libavformat/imf_cpl.c b/libavformat/imf_cpl.c index b59c3614e12..5f1a67443f2 100644 --- a/libavformat/imf_cpl.c +++ b/libavformat/imf_cpl.c @@ -78,10 +78,8 @@ int ff_imf_xml_read_uuid(xmlNodePtr element, AVUUID uuid) if (!element_text) return AVERROR_INVALIDDATA; ret = av_uuid_urn_parse(element_text, uuid); - if (ret) { - av_log(NULL, AV_LOG_ERROR, "Invalid UUID\n"); + if (ret) ret = AVERROR_INVALIDDATA; - } xmlFree(element_text); return ret; @@ -92,10 +90,8 @@ int ff_imf_xml_read_rational(xmlNodePtr element, AVRational *rational) int ret = 0; xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); - if (element_text == NULL || sscanf(element_text, "%i %i", &rational->num, &rational->den) != 2) { - av_log(NULL, AV_LOG_ERROR, "Invalid rational number\n"); + if (element_text == NULL || sscanf(element_text, "%i %i", &rational->num, &rational->den) != 2) ret = AVERROR_INVALIDDATA; - } xmlFree(element_text); return ret; @@ -106,10 +102,8 @@ int ff_imf_xml_read_uint32(xmlNodePtr element, uint32_t *number) int ret = 0; xmlChar *element_text = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); - if (element_text == NULL || sscanf(element_text, "%" PRIu32, number) != 1) { - av_log(NULL, AV_LOG_ERROR, "Invalid unsigned 32-bit integer"); + if (element_text == NULL || sscanf(element_text, "%" PRIu32, number) != 1) ret = AVERROR_INVALIDDATA; - } xmlFree(element_text); return ret; @@ -183,10 +177,8 @@ static int fill_content_title(xmlNodePtr cpl_element, FFIMFCPL *cpl) { xmlNodePtr element = NULL; - if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "ContentTitle"))) { - av_log(NULL, AV_LOG_ERROR, "ContentTitle element not found in the IMF CPL\n"); + if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "ContentTitle"))) return AVERROR_INVALIDDATA; - } cpl->content_title_utf8 = xmlNodeListGetString(cpl_element->doc, element->xmlChildrenNode, 1); @@ -248,22 +240,15 @@ static int fill_timecode(xmlNodePtr cpl_element, FFIMFCPL *cpl) return 0; element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeDropFrame"); - if (!element) { - av_log(NULL, AV_LOG_ERROR, "CompositionTimecode element is missing\ - a TimecodeDropFrame child element\n"); + if (!element) return AVERROR_INVALIDDATA; - } - if (ff_imf_xml_read_boolean(element, &df)) { - av_log(NULL, AV_LOG_ERROR, "TimecodeDropFrame element is invalid\n"); + if (ff_imf_xml_read_boolean(element, &df)) return AVERROR_INVALIDDATA; - } + element = ff_imf_xml_get_child_element_by_name(tc_element, "TimecodeStartAddress"); - if (!element) { - av_log(NULL, AV_LOG_ERROR, "CompositionTimecode element is missing\ - a TimecodeStartAddress child element\n"); + if (!element) return AVERROR_INVALIDDATA; - } tc_str = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1); if (!tc_str) @@ -288,10 +273,8 @@ static int fill_edit_rate(xmlNodePtr cpl_element, FFIMFCPL *cpl) { xmlNodePtr element = NULL; - if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "EditRate"))) { - av_log(NULL, AV_LOG_ERROR, "EditRate element not found in the IMF CPL\n"); + if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "EditRate"))) return AVERROR_INVALIDDATA; - } return ff_imf_xml_read_rational(element, &cpl->edit_rate); } @@ -300,10 +283,8 @@ static int fill_id(xmlNodePtr cpl_element, FFIMFCPL *cpl) { xmlNodePtr element = NULL; - if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "Id"))) { - av_log(NULL, AV_LOG_ERROR, "Id element not found in the IMF CPL\n"); + if (!(element = ff_imf_xml_get_child_element_by_name(cpl_element, "Id"))) return AVERROR_INVALIDDATA; - } return ff_imf_xml_read_uuid(element, cpl->id_uuid); } @@ -314,22 +295,19 @@ static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker) int ret = 0; /* read Offset */ - if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Offset"))) { - av_log(NULL, AV_LOG_ERROR, "Offset element not found in a Marker\n"); + if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Offset"))) return AVERROR_INVALIDDATA; - } + if ((ret = ff_imf_xml_read_uint32(element, &marker->offset))) return ret; /* read Label and Scope */ - if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Label"))) { - av_log(NULL, AV_LOG_ERROR, "Label element not found in a Marker\n"); + if (!(element = ff_imf_xml_get_child_element_by_name(marker_elem, "Label"))) return AVERROR_INVALIDDATA; - } - if (!(marker->label_utf8 = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1))) { - av_log(NULL, AV_LOG_ERROR, "Empty Label element found in a Marker\n"); + + if (!(marker->label_utf8 = xmlNodeListGetString(element->doc, element->xmlChildrenNode, 1))) return AVERROR_INVALIDDATA; - } + if (!(marker->scope_utf8 = xmlGetNoNsProp(element, "scope"))) { marker->scope_utf8 = xmlCharStrdup("http://www.smpte-ra.org/schemas/2067-3/2013#standard-markers"); @@ -342,7 +320,8 @@ static int fill_marker(xmlNodePtr marker_elem, FFIMFMarker *marker) return ret; } -static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resource, FFIMFCPL *cpl) +static int fill_base_resource(void *log_ctx, xmlNodePtr resource_elem, + FFIMFBaseResource *resource, FFIMFCPL *cpl) { xmlNodePtr element = NULL; int ret = 0; @@ -351,14 +330,14 @@ static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resou if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "EditRate"))) { resource->edit_rate = cpl->edit_rate; } else if ((ret = ff_imf_xml_read_rational(element, &resource->edit_rate))) { - av_log(NULL, AV_LOG_ERROR, "Invalid EditRate element found in a Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid EditRate element found in a Resource\n"); return ret; } /* read EntryPoint */ if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "EntryPoint"))) { if ((ret = ff_imf_xml_read_uint32(element, &resource->entry_point))) { - av_log(NULL, AV_LOG_ERROR, "Invalid EntryPoint element found in a Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid EntryPoint element found in a Resource\n"); return ret; } } else { @@ -367,11 +346,11 @@ static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resou /* read IntrinsicDuration */ if (!(element = ff_imf_xml_get_child_element_by_name(resource_elem, "IntrinsicDuration"))) { - av_log(NULL, AV_LOG_ERROR, "IntrinsicDuration element missing from Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "IntrinsicDuration element missing from Resource\n"); return AVERROR_INVALIDDATA; } if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) { - av_log(NULL, AV_LOG_ERROR, "Invalid IntrinsicDuration element found in a Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid IntrinsicDuration element found in a Resource\n"); return ret; } resource->duration -= resource->entry_point; @@ -379,7 +358,7 @@ static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resou /* read SourceDuration */ if ((element = ff_imf_xml_get_child_element_by_name(resource_elem, "SourceDuration"))) { if ((ret = ff_imf_xml_read_uint32(element, &resource->duration))) { - av_log(NULL, AV_LOG_ERROR, "SourceDuration element missing from Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "SourceDuration element missing from Resource\n"); return ret; } } @@ -391,38 +370,38 @@ static int fill_base_resource(xmlNodePtr resource_elem, FFIMFBaseResource *resou return ret; } -static int fill_trackfile_resource(xmlNodePtr tf_resource_elem, +static int fill_trackfile_resource(void *log_ctx, xmlNodePtr tf_resource_elem, FFIMFTrackFileResource *tf_resource, FFIMFCPL *cpl) { xmlNodePtr element = NULL; int ret = 0; - if ((ret = fill_base_resource(tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl))) + if ((ret = fill_base_resource(log_ctx, tf_resource_elem, (FFIMFBaseResource *)tf_resource, cpl))) return ret; /* read TrackFileId */ if ((element = ff_imf_xml_get_child_element_by_name(tf_resource_elem, "TrackFileId"))) { if ((ret = ff_imf_xml_read_uuid(element, tf_resource->track_file_uuid))) { - av_log(NULL, AV_LOG_ERROR, "Invalid TrackFileId element found in Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackFileId element found in Resource\n"); return ret; } } else { - av_log(NULL, AV_LOG_ERROR, "TrackFileId element missing from Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "TrackFileId element missing from Resource\n"); return AVERROR_INVALIDDATA; } return ret; } -static int fill_marker_resource(xmlNodePtr marker_resource_elem, +static int fill_marker_resource(void *log_ctx, xmlNodePtr marker_resource_elem, FFIMFMarkerResource *marker_resource, FFIMFCPL *cpl) { xmlNodePtr element = NULL; int ret = 0; - if ((ret = fill_base_resource(marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl))) + if ((ret = fill_base_resource(log_ctx, marker_resource_elem, (FFIMFBaseResource *)marker_resource, cpl))) return ret; /* read markers */ @@ -444,8 +423,10 @@ static int fill_marker_resource(xmlNodePtr marker_resource_elem, ret = fill_marker(element, &marker_resource->markers[marker_resource->marker_count]); marker_resource->marker_count++; - if (ret) + if (ret) { + av_log(log_ctx, AV_LOG_ERROR, "Invalid Marker element\n"); return ret; + } } element = xmlNextElementSibling(element); @@ -454,7 +435,7 @@ static int fill_marker_resource(xmlNodePtr marker_resource_elem, return ret; } -static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl) +static int push_marker_sequence(void *log_ctx, xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl) { int ret = 0; AVUUID uuid; @@ -466,14 +447,14 @@ static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl) /* read TrackID element */ if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(marker_sequence_elem, "TrackId"))) { - av_log(NULL, AV_LOG_ERROR, "TrackId element missing from Sequence\n"); + av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from Sequence\n"); return AVERROR_INVALIDDATA; } if (ff_imf_xml_read_uuid(track_id_elem, uuid)) { - av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in Sequence\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in Sequence\n"); return AVERROR_INVALIDDATA; } - av_log(NULL, + av_log(log_ctx, AV_LOG_DEBUG, "Processing IMF CPL Marker Sequence for Virtual Track " AV_PRI_UUID "\n", AV_UUID_ARG(uuid)); @@ -487,7 +468,7 @@ static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl) av_uuid_copy(cpl->main_markers_track->base.id_uuid, uuid); } else if (!av_uuid_equal(cpl->main_markers_track->base.id_uuid, uuid)) { - av_log(NULL, AV_LOG_ERROR, "Multiple marker virtual tracks were found\n"); + av_log(log_ctx, AV_LOG_ERROR, "Multiple marker virtual tracks were found\n"); return AVERROR_INVALIDDATA; } @@ -504,7 +485,7 @@ static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl) cpl->main_markers_track->resource_count + resource_elem_count, sizeof(FFIMFMarkerResource)); if (!tmp) { - av_log(NULL, AV_LOG_ERROR, "Cannot allocate Marker Resources\n"); + av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Marker Resources\n"); return AVERROR(ENOMEM); } cpl->main_markers_track->resources = tmp; @@ -512,7 +493,7 @@ static int push_marker_sequence(xmlNodePtr marker_sequence_elem, FFIMFCPL *cpl) resource_elem = xmlFirstElementChild(resource_list_elem); while (resource_elem) { imf_marker_resource_init(&cpl->main_markers_track->resources[cpl->main_markers_track->resource_count]); - ret = fill_marker_resource(resource_elem, + ret = fill_marker_resource(log_ctx, resource_elem, &cpl->main_markers_track->resources[cpl->main_markers_track->resource_count], cpl); cpl->main_markers_track->resource_count++; @@ -541,7 +522,7 @@ static int has_stereo_resources(xmlNodePtr element) return 0; } -static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl) +static int push_main_audio_sequence(void *log_ctx, xmlNodePtr audio_sequence_elem, FFIMFCPL *cpl) { int ret = 0; AVUUID uuid; @@ -554,14 +535,14 @@ static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cp /* read TrackID element */ if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(audio_sequence_elem, "TrackId"))) { - av_log(NULL, AV_LOG_ERROR, "TrackId element missing from audio sequence\n"); + av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from audio sequence\n"); return AVERROR_INVALIDDATA; } if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) { - av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n"); return ret; } - av_log(NULL, + av_log(log_ctx, AV_LOG_DEBUG, "Processing IMF CPL Audio Sequence for Virtual Track " AV_PRI_UUID "\n", AV_UUID_ARG(uuid)); @@ -605,7 +586,7 @@ static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cp (vt->resource_count + resource_elem_count) * sizeof(FFIMFTrackFileResource)); if (!tmp) { - av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n"); + av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Main Audio Resources\n"); return AVERROR(ENOMEM); } vt->resources = tmp; @@ -613,11 +594,11 @@ static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cp resource_elem = xmlFirstElementChild(resource_list_elem); while (resource_elem) { imf_trackfile_resource_init(&vt->resources[vt->resource_count]); - ret = fill_trackfile_resource(resource_elem, + ret = fill_trackfile_resource(log_ctx, resource_elem, &vt->resources[vt->resource_count], cpl); if (ret) - av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid Resource\n"); else vt->resource_count++; @@ -627,7 +608,7 @@ static int push_main_audio_sequence(xmlNodePtr audio_sequence_elem, FFIMFCPL *cp return ret; } -static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL *cpl) +static int push_main_image_2d_sequence(void *log_ctx, xmlNodePtr image_sequence_elem, FFIMFCPL *cpl) { int ret = 0; AVUUID uuid; @@ -639,17 +620,17 @@ static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL /* skip stereoscopic resources */ if (has_stereo_resources(image_sequence_elem)) { - av_log(NULL, AV_LOG_ERROR, "Stereoscopic 3D image virtual tracks not supported\n"); + av_log(log_ctx, AV_LOG_ERROR, "Stereoscopic 3D image virtual tracks not supported\n"); return AVERROR_PATCHWELCOME; } /* read TrackId element*/ if (!(track_id_elem = ff_imf_xml_get_child_element_by_name(image_sequence_elem, "TrackId"))) { - av_log(NULL, AV_LOG_ERROR, "TrackId element missing from audio sequence\n"); + av_log(log_ctx, AV_LOG_ERROR, "TrackId element missing from audio sequence\n"); return AVERROR_INVALIDDATA; } if ((ret = ff_imf_xml_read_uuid(track_id_elem, uuid))) { - av_log(NULL, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid TrackId element found in audio sequence\n"); return ret; } @@ -662,10 +643,10 @@ static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL av_uuid_copy(cpl->main_image_2d_track->base.id_uuid, uuid); } else if (!av_uuid_equal(cpl->main_image_2d_track->base.id_uuid, uuid)) { - av_log(NULL, AV_LOG_ERROR, "Multiple MainImage virtual tracks found\n"); + av_log(log_ctx, AV_LOG_ERROR, "Multiple MainImage virtual tracks found\n"); return AVERROR_INVALIDDATA; } - av_log(NULL, + av_log(log_ctx, AV_LOG_DEBUG, "Processing IMF CPL Main Image Sequence for Virtual Track " AV_PRI_UUID "\n", AV_UUID_ARG(uuid)); @@ -686,7 +667,7 @@ static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL (cpl->main_image_2d_track->resource_count + resource_elem_count) * sizeof(FFIMFTrackFileResource)); if (!tmp) { - av_log(NULL, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n"); + av_log(log_ctx, AV_LOG_ERROR, "Cannot allocate Main Image Resources\n"); return AVERROR(ENOMEM); } cpl->main_image_2d_track->resources = tmp; @@ -695,11 +676,11 @@ static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL while (resource_elem) { imf_trackfile_resource_init( &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count]); - ret = fill_trackfile_resource(resource_elem, + ret = fill_trackfile_resource(log_ctx, resource_elem, &cpl->main_image_2d_track->resources[cpl->main_image_2d_track->resource_count], cpl); if (ret) - av_log(NULL, AV_LOG_ERROR, "Invalid Resource\n"); + av_log(log_ctx, AV_LOG_ERROR, "Invalid Resource\n"); else cpl->main_image_2d_track->resource_count++; @@ -709,7 +690,7 @@ static int push_main_image_2d_sequence(xmlNodePtr image_sequence_elem, FFIMFCPL return 0; } -static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl) +static int fill_virtual_tracks(void *log_ctx, xmlNodePtr cpl_element, FFIMFCPL *cpl) { int ret = 0; xmlNodePtr segment_list_elem = NULL; @@ -718,14 +699,14 @@ static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl) xmlNodePtr sequence_elem = NULL; if (!(segment_list_elem = ff_imf_xml_get_child_element_by_name(cpl_element, "SegmentList"))) { - av_log(NULL, AV_LOG_ERROR, "SegmentList element missing\n"); + av_log(log_ctx, AV_LOG_ERROR, "SegmentList element missing\n"); return AVERROR_INVALIDDATA; } /* process sequences */ segment_elem = xmlFirstElementChild(segment_list_elem); while (segment_elem) { - av_log(NULL, AV_LOG_DEBUG, "Processing IMF CPL Segment\n"); + av_log(log_ctx, AV_LOG_DEBUG, "Processing IMF CPL Segment\n"); sequence_list_elem = ff_imf_xml_get_child_element_by_name(segment_elem, "SequenceList"); if (!sequence_list_elem) @@ -734,16 +715,16 @@ static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl) sequence_elem = xmlFirstElementChild(sequence_list_elem); while (sequence_elem) { if (xmlStrcmp(sequence_elem->name, "MarkerSequence") == 0) - ret = push_marker_sequence(sequence_elem, cpl); + ret = push_marker_sequence(log_ctx, sequence_elem, cpl); else if (xmlStrcmp(sequence_elem->name, "MainImageSequence") == 0) - ret = push_main_image_2d_sequence(sequence_elem, cpl); + ret = push_main_image_2d_sequence(log_ctx, sequence_elem, cpl); else if (xmlStrcmp(sequence_elem->name, "MainAudioSequence") == 0) - ret = push_main_audio_sequence(sequence_elem, cpl); + ret = push_main_audio_sequence(log_ctx, sequence_elem, cpl); else - av_log(NULL, + av_log(log_ctx, AV_LOG_INFO, "The following Sequence is not supported and is ignored: %s\n", sequence_elem->name); @@ -761,7 +742,7 @@ static int fill_virtual_tracks(xmlNodePtr cpl_element, FFIMFCPL *cpl) return ret; } -int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl) +int ff_imf_parse_cpl_from_xml_dom(void *log_ctx, xmlDocPtr doc, FFIMFCPL **cpl) { int ret = 0; xmlNodePtr cpl_element = NULL; @@ -774,20 +755,28 @@ int ff_imf_parse_cpl_from_xml_dom(xmlDocPtr doc, FFIMFCPL **cpl) cpl_element = xmlDocGetRootElement(doc); if (!cpl_element || xmlStrcmp(cpl_element->name, "CompositionPlaylist")) { - av_log(NULL, AV_LOG_ERROR, "The root element of the CPL is not CompositionPlaylist\n"); + av_log(log_ctx, AV_LOG_ERROR, "The root element of the CPL is not CompositionPlaylist\n"); ret = AVERROR_INVALIDDATA; goto cleanup; } - if ((ret = fill_content_title(cpl_element, *cpl))) + if ((ret = fill_content_title(cpl_element, *cpl))) { + av_log(log_ctx, AV_LOG_ERROR, "Cannot read the ContentTitle element from the IMF CPL\n"); goto cleanup; - if ((ret = fill_id(cpl_element, *cpl))) + } + if ((ret = fill_id(cpl_element, *cpl))) { + av_log(log_ctx, AV_LOG_ERROR, "Id element not found in the IMF CPL\n"); goto cleanup; - if ((ret = fill_edit_rate(cpl_element, *cpl))) + } + if ((ret = fill_edit_rate(cpl_element, *cpl))) { + av_log(log_ctx, AV_LOG_ERROR, "EditRate element not found in the IMF CPL\n"); goto cleanup; - if ((ret = fill_timecode(cpl_element, *cpl))) + } + if ((ret = fill_timecode(cpl_element, *cpl))) { + av_log(log_ctx, AV_LOG_ERROR, "Invalid CompositionTimecode element found in the IMF CPL\n"); goto cleanup; - if ((ret = fill_virtual_tracks(cpl_element, *cpl))) + } + if ((ret = fill_virtual_tracks(log_ctx, cpl_element, *cpl))) goto cleanup; cleanup: @@ -883,7 +872,7 @@ void ff_imf_cpl_free(FFIMFCPL *cpl) av_freep(&cpl); } -int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl) +int ff_imf_parse_cpl(void *log_ctx, AVIOContext *in, FFIMFCPL **cpl) { AVBPrint buf; xmlDoc *doc = NULL; @@ -893,7 +882,7 @@ int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl) ret = avio_read_to_bprint(in, &buf, SIZE_MAX); if (ret < 0 || !avio_feof(in)) { - av_log(NULL, AV_LOG_ERROR, "Cannot read IMF CPL\n"); + av_log(log_ctx, AV_LOG_ERROR, "Cannot read IMF CPL\n"); if (ret == 0) ret = AVERROR_INVALIDDATA; goto clean_up; @@ -903,21 +892,21 @@ int ff_imf_parse_cpl(AVIOContext *in, FFIMFCPL **cpl) doc = xmlReadMemory(buf.str, buf.len, NULL, NULL, 0); if (!doc) { - av_log(NULL, + av_log(log_ctx, AV_LOG_ERROR, "XML parsing failed when reading the IMF CPL\n"); ret = AVERROR_INVALIDDATA; goto clean_up; } - if ((ret = ff_imf_parse_cpl_from_xml_dom(doc, cpl))) { - av_log(NULL, AV_LOG_ERROR, "Cannot parse IMF CPL\n"); + if ((ret = ff_imf_parse_cpl_from_xml_dom(log_ctx, doc, cpl))) { + av_log(log_ctx, AV_LOG_ERROR, "Cannot parse IMF CPL\n"); } else { - av_log(NULL, + av_log(log_ctx, AV_LOG_INFO, "IMF CPL ContentTitle: %s\n", (*cpl)->content_title_utf8); - av_log(NULL, + av_log(log_ctx, AV_LOG_INFO, "IMF CPL Id: " AV_PRI_UUID "\n", AV_UUID_ARG((*cpl)->id_uuid)); diff --git a/libavformat/imfdec.c b/libavformat/imfdec.c index 4932b7ff1fc..bf56c782661 100644 --- a/libavformat/imfdec.c +++ b/libavformat/imfdec.c @@ -379,11 +379,6 @@ static int open_track_resource_context(AVFormatContext *s, return AVERROR(ENOMEM); track_resource->ctx->io_open = s->io_open; -#if FF_API_AVFORMAT_IO_CLOSE -FF_DISABLE_DEPRECATION_WARNINGS - track_resource->ctx->io_close = s->io_close; -FF_ENABLE_DEPRECATION_WARNINGS -#endif track_resource->ctx->io_close2 = s->io_close2; track_resource->ctx->flags |= s->flags & ~AVFMT_FLAG_CUSTOM_IO; @@ -649,7 +644,7 @@ static int imf_read_header(AVFormatContext *s) av_log(s, AV_LOG_DEBUG, "start parsing IMF CPL: %s\n", s->url); - if ((ret = ff_imf_parse_cpl(s->pb, &c->cpl)) < 0) + if ((ret = ff_imf_parse_cpl(s, s->pb, &c->cpl)) < 0) return ret; tcr = av_dict_get(s->metadata, "timecode", NULL, 0); @@ -700,12 +695,9 @@ static int imf_read_header(AVFormatContext *s) static IMFVirtualTrackPlaybackCtx *get_next_track_with_minimum_timestamp(AVFormatContext *s) { IMFContext *c = s->priv_data; - IMFVirtualTrackPlaybackCtx *track; + IMFVirtualTrackPlaybackCtx *track = NULL; AVRational minimum_timestamp = av_make_q(INT32_MAX, 1); - if (!c->track_count) - return NULL; - for (uint32_t i = c->track_count; i > 0; i--) { av_log(s, AV_LOG_TRACE, "Compare track %d timestamp " AVRATIONAL_FORMAT " to minimum " AVRATIONAL_FORMAT @@ -1019,12 +1011,12 @@ static const AVClass imf_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_imf_demuxer = { - .name = "imf", - .long_name = NULL_IF_CONFIG_SMALL("IMF (Interoperable Master Format)"), - .flags = AVFMT_EXPERIMENTAL | AVFMT_NO_BYTE_SEEK, - .flags_internal = FF_FMT_INIT_CLEANUP, - .priv_class = &imf_class, +const FFInputFormat ff_imf_demuxer = { + .p.name = "imf", + .p.long_name = NULL_IF_CONFIG_SMALL("IMF (Interoperable Master Format)"), + .p.flags = AVFMT_NO_BYTE_SEEK, + .p.priv_class = &imf_class, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .priv_data_size = sizeof(IMFContext), .read_probe = imf_probe, .read_header = imf_read_header, diff --git a/libavformat/img2_alias_pix.c b/libavformat/img2_alias_pix.c index d0aac83924f..d96c0ccf55b 100644 --- a/libavformat/img2_alias_pix.c +++ b/libavformat/img2_alias_pix.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "demux.h" #include "img2.h" #include "libavcodec/bytestream.h" @@ -61,13 +62,13 @@ static const AVClass image2_alias_pix_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_image2_alias_pix_demuxer = { - .name = "alias_pix", - .long_name = NULL_IF_CONFIG_SMALL("Alias/Wavefront PIX image"), +const FFInputFormat ff_image2_alias_pix_demuxer = { + .p.name = "alias_pix", + .p.long_name = NULL_IF_CONFIG_SMALL("Alias/Wavefront PIX image"), + .p.priv_class = &image2_alias_pix_class, .priv_data_size = sizeof(VideoDemuxData), .read_probe = alias_pix_read_probe, .read_header = ff_img_read_header, .read_packet = ff_img_read_packet, .raw_codec_id = AV_CODEC_ID_ALIAS_PIX, - .priv_class = &image2_alias_pix_class, }; diff --git a/libavformat/img2_brender_pix.c b/libavformat/img2_brender_pix.c index 9d9a7c0819e..9e9335eafe6 100644 --- a/libavformat/img2_brender_pix.c +++ b/libavformat/img2_brender_pix.c @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "demux.h" #include "img2.h" #include "libavutil/intreadwrite.h" @@ -45,13 +46,13 @@ static const AVClass image2_brender_pix_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_image2_brender_pix_demuxer = { - .name = "brender_pix", - .long_name = NULL_IF_CONFIG_SMALL("BRender PIX image"), +const FFInputFormat ff_image2_brender_pix_demuxer = { + .p.name = "brender_pix", + .p.long_name = NULL_IF_CONFIG_SMALL("BRender PIX image"), + .p.priv_class = &image2_brender_pix_class, .priv_data_size = sizeof(VideoDemuxData), .read_probe = brender_read_probe, .read_header = ff_img_read_header, .read_packet = ff_img_read_packet, .raw_codec_id = AV_CODEC_ID_BRENDER_PIX, - .priv_class = &image2_brender_pix_class, }; diff --git a/libavformat/img2dec.c b/libavformat/img2dec.c index 5a63d7c81d8..69cce36bedd 100644 --- a/libavformat/img2dec.c +++ b/libavformat/img2dec.c @@ -25,6 +25,7 @@ #define _DEFAULT_SOURCE #define _BSD_SOURCE #include +#include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/log.h" #include "libavutil/opt.h" @@ -34,9 +35,11 @@ #include "libavcodec/gif.h" #include "avformat.h" #include "avio_internal.h" +#include "demux.h" #include "internal.h" #include "img2.h" -#include "jpegxl_probe.h" +#include "os_support.h" +#include "libavcodec/jpegxl_parse.h" #include "libavcodec/mjpeg.h" #include "libavcodec/vbn.h" #include "libavcodec/xwd.h" @@ -321,9 +324,9 @@ int ff_img_read_header(AVFormatContext *s1) } else if (s1->audio_codec_id) { st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; st->codecpar->codec_id = s1->audio_codec_id; - } else if (s1->iformat->raw_codec_id) { + } else if (ffifmt(s1->iformat)->raw_codec_id) { st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; - st->codecpar->codec_id = s1->iformat->raw_codec_id; + st->codecpar->codec_id = ffifmt(s1->iformat)->raw_codec_id; } else { const char *str = strrchr(s->path, '.'); s->split_planes = str && !av_strcasecmp(str + 1, "y"); @@ -350,13 +353,14 @@ int ff_img_read_header(AVFormatContext *s1) pd.filename = s1->url; while ((fmt = av_demuxer_iterate(&fmt_iter))) { - if (fmt->read_header != ff_img_read_header || - !fmt->read_probe || + const FFInputFormat *fmt2 = ffifmt(fmt); + if (fmt2->read_header != ff_img_read_header || + !fmt2->read_probe || (fmt->flags & AVFMT_NOFILE) || - !fmt->raw_codec_id) + !fmt2->raw_codec_id) continue; - if (fmt->read_probe(&pd) > 0) { - st->codecpar->codec_id = fmt->raw_codec_id; + if (fmt2->read_probe(&pd) > 0) { + st->codecpar->codec_id = fmt2->raw_codec_id; break; } } @@ -457,7 +461,7 @@ int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt) if (par->codec_id == AV_CODEC_ID_NONE) { AVProbeData pd = { 0 }; - const AVInputFormat *ifmt; + const FFInputFormat *ifmt; uint8_t header[PROBE_BUF_MIN + AVPROBE_PADDING_SIZE]; int ret; int score = 0; @@ -471,7 +475,7 @@ int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt) pd.buf_size = ret; pd.filename = filename; - ifmt = av_probe_input_format3(&pd, 1, &score); + ifmt = ffifmt(av_probe_input_format3(&pd, 1, &score)); if (ifmt && ifmt->read_packet == ff_img_read_packet && ifmt->raw_codec_id) par->codec_id = ifmt->raw_codec_id; } @@ -501,6 +505,7 @@ int ff_img_read_packet(AVFormatContext *s1, AVPacket *pkt) pkt->flags |= AV_PKT_FLAG_KEY; if (s->ts_from_file) { struct stat img_stat; + av_assert0(!s->is_pipe); // The ts_from_file option is not supported by piped input demuxers if (stat(filename, &img_stat)) { res = AVERROR(EIO); goto fail; @@ -616,17 +621,17 @@ static int img_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp #if CONFIG_IMAGE2_DEMUXER const AVOption ff_img_options[] = { - { "pattern_type", "set pattern type", OFFSET(pattern_type), AV_OPT_TYPE_INT, {.i64=PT_DEFAULT}, 0, INT_MAX, DEC, "pattern_type"}, - { "glob_sequence","select glob/sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB_SEQUENCE}, INT_MIN, INT_MAX, DEC, "pattern_type" }, - { "glob", "select glob pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB }, INT_MIN, INT_MAX, DEC, "pattern_type" }, - { "sequence", "select sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_SEQUENCE }, INT_MIN, INT_MAX, DEC, "pattern_type" }, - { "none", "disable pattern matching", 0, AV_OPT_TYPE_CONST, {.i64=PT_NONE }, INT_MIN, INT_MAX, DEC, "pattern_type" }, + { "pattern_type", "set pattern type", OFFSET(pattern_type), AV_OPT_TYPE_INT, {.i64=PT_DEFAULT}, 0, INT_MAX, DEC, .unit = "pattern_type"}, + { "glob_sequence","select glob/sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB_SEQUENCE}, INT_MIN, INT_MAX, DEC, .unit = "pattern_type" }, + { "glob", "select glob pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_GLOB }, INT_MIN, INT_MAX, DEC, .unit = "pattern_type" }, + { "sequence", "select sequence pattern type", 0, AV_OPT_TYPE_CONST, {.i64=PT_SEQUENCE }, INT_MIN, INT_MAX, DEC, .unit = "pattern_type" }, + { "none", "disable pattern matching", 0, AV_OPT_TYPE_CONST, {.i64=PT_NONE }, INT_MIN, INT_MAX, DEC, .unit = "pattern_type" }, { "start_number", "set first number in the sequence", OFFSET(start_number), AV_OPT_TYPE_INT, {.i64 = 0 }, INT_MIN, INT_MAX, DEC }, { "start_number_range", "set range for looking at the first sequence number", OFFSET(start_number_range), AV_OPT_TYPE_INT, {.i64 = 5}, 1, INT_MAX, DEC }, - { "ts_from_file", "set frame timestamp from file's one", OFFSET(ts_from_file), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 2, DEC, "ts_type" }, - { "none", "none", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 2, DEC, "ts_type" }, - { "sec", "second precision", 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 2, DEC, "ts_type" }, - { "ns", "nano second precision", 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 2, DEC, "ts_type" }, + { "ts_from_file", "set frame timestamp from file's one", OFFSET(ts_from_file), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 2, DEC, .unit = "ts_type" }, + { "none", "none", 0, AV_OPT_TYPE_CONST, {.i64 = 0 }, 0, 2, DEC, .unit = "ts_type" }, + { "sec", "second precision", 0, AV_OPT_TYPE_CONST, {.i64 = 1 }, 0, 2, DEC, .unit = "ts_type" }, + { "ns", "nano second precision", 0, AV_OPT_TYPE_CONST, {.i64 = 2 }, 0, 2, DEC, .unit = "ts_type" }, { "export_path_metadata", "enable metadata containing input path information", OFFSET(export_path_metadata), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, DEC }, \ COMMON_OPTIONS }; @@ -637,17 +642,17 @@ static const AVClass img2_class = { .option = ff_img_options, .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_image2_demuxer = { - .name = "image2", - .long_name = NULL_IF_CONFIG_SMALL("image2 sequence"), +const FFInputFormat ff_image2_demuxer = { + .p.name = "image2", + .p.long_name = NULL_IF_CONFIG_SMALL("image2 sequence"), + .p.flags = AVFMT_NOFILE, + .p.priv_class = &img2_class, .priv_data_size = sizeof(VideoDemuxData), .read_probe = img_read_probe, .read_header = ff_img_read_header, .read_packet = ff_img_read_packet, .read_close = img_read_close, .read_seek = img_read_seek, - .flags = AVFMT_NOFILE, - .priv_class = &img2_class, }; #endif @@ -663,13 +668,13 @@ static const AVClass imagepipe_class = { }; #if CONFIG_IMAGE2PIPE_DEMUXER -const AVInputFormat ff_image2pipe_demuxer = { - .name = "image2pipe", - .long_name = NULL_IF_CONFIG_SMALL("piped image2 sequence"), +const FFInputFormat ff_image2pipe_demuxer = { + .p.name = "image2pipe", + .p.long_name = NULL_IF_CONFIG_SMALL("piped image2 sequence"), + .p.priv_class = &imagepipe_class, .priv_data_size = sizeof(VideoDemuxData), .read_header = ff_img_read_header, .read_packet = ff_img_read_packet, - .priv_class = &imagepipe_class, }; #endif @@ -791,13 +796,14 @@ static int jpeg_probe(const AVProbeData *p) return 0; state = EOI; break; - case DQT: case APP0: - if (AV_RL32(&b[i + 4]) == MKTAG('J','F','I','F')) + if (c == APP0 && AV_RL32(&b[i + 4]) == MKTAG('J','F','I','F')) got_header = 1; + /* fallthrough */ case APP1: - if (AV_RL32(&b[i + 4]) == MKTAG('E','x','i','f')) + if (c == APP1 && AV_RL32(&b[i + 4]) == MKTAG('E','x','i','f')) got_header = 1; + /* fallthrough */ case APP2: case APP3: case APP4: @@ -812,6 +818,7 @@ static int jpeg_probe(const AVProbeData *p) case APP13: case APP14: case APP15: + case DQT: /* fallthrough */ case COM: i += AV_RB16(&b[i + 2]) + 1; break; @@ -850,7 +857,7 @@ static int jpegxl_probe(const AVProbeData *p) if (AV_RL16(b) != FF_JPEGXL_CODESTREAM_SIGNATURE_LE) return 0; #if CONFIG_IMAGE_JPEGXL_PIPE_DEMUXER - if (ff_jpegxl_verify_codestream_header(p->buf, p->buf_size) >= 0) + if (ff_jpegxl_parse_codestream_header(p->buf, p->buf_size, NULL, 5) >= 0) return AVPROBE_SCORE_MAX - 2; #endif return 0; @@ -964,8 +971,13 @@ static int svg_probe(const AVProbeData *p) { const uint8_t *b = p->buf; const uint8_t *end = p->buf + p->buf_size; - - if (memcmp(p->buf, "= end - 5) + return 0; + if (!memcmp(b, "buf, " + #include "config_components.h" #include "libavutil/intreadwrite.h" diff --git a/libavformat/imx.c b/libavformat/imx.c index c8b1a3b2ad9..70b62b63d75 100644 --- a/libavformat/imx.c +++ b/libavformat/imx.c @@ -21,6 +21,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" #include "libavutil/intreadwrite.h" #include "libavutil/channel_layout.h" @@ -155,13 +156,13 @@ static int simbiosis_imx_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_simbiosis_imx_demuxer = { - .name = "simbiosis_imx", - .long_name = NULL_IF_CONFIG_SMALL("Simbiosis Interactive IMX"), +const FFInputFormat ff_simbiosis_imx_demuxer = { + .p.name = "simbiosis_imx", + .p.long_name = NULL_IF_CONFIG_SMALL("Simbiosis Interactive IMX"), + .p.extensions = "imx", + .p.flags = AVFMT_GENERIC_INDEX, .priv_data_size = sizeof(SimbiosisIMXDemuxContext), .read_probe = simbiosis_imx_probe, .read_header = simbiosis_imx_read_header, .read_packet = simbiosis_imx_read_packet, - .extensions = "imx", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/ingenientdec.c b/libavformat/ingenientdec.c index 7a9cce155fd..63624372a64 100644 --- a/libavformat/ingenientdec.c +++ b/libavformat/ingenientdec.c @@ -20,6 +20,7 @@ */ #include "avformat.h" +#include "demux.h" #include "rawdec.h" #include "libavutil/intreadwrite.h" @@ -61,15 +62,15 @@ static int ingenient_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_ingenient_demuxer = { - .name = "ingenient", - .long_name = NULL_IF_CONFIG_SMALL("raw Ingenient MJPEG"), +const FFInputFormat ff_ingenient_demuxer = { + .p.name = "ingenient", + .p.long_name = NULL_IF_CONFIG_SMALL("raw Ingenient MJPEG"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "cgi", // FIXME + .p.priv_class = &ff_rawvideo_demuxer_class, .priv_data_size = sizeof(FFRawVideoDemuxerContext), .read_probe = ingenient_probe, .read_header = ff_raw_video_read_header, .read_packet = ingenient_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "cgi", // FIXME .raw_codec_id = AV_CODEC_ID_MJPEG, - .priv_class = &ff_rawvideo_demuxer_class, }; diff --git a/libavformat/internal.h b/libavformat/internal.h index f575064e8fb..7f3d1c00864 100644 --- a/libavformat/internal.h +++ b/libavformat/internal.h @@ -26,7 +26,6 @@ #include "libavcodec/packet_internal.h" #include "avformat.h" -#include "os_support.h" #define MAX_URL_SIZE 4096 @@ -40,12 +39,6 @@ # define hex_dump_debug(class, buf, size) do { if (0) av_hex_dump_log(class, AV_LOG_DEBUG, buf, size); } while(0) #endif -/** - * For an AVInputFormat with this flag set read_close() needs to be called - * by the caller upon read_header() failure. - */ -#define FF_FMT_INIT_CLEANUP (1 << 0) - typedef struct AVCodecTag { enum AVCodecID id; unsigned int tag; @@ -148,14 +141,18 @@ typedef struct FFFormatContext { int missing_ts_warning; #endif +#if FF_API_AVSTREAM_SIDE_DATA int inject_global_side_data; +#endif int avoid_negative_ts_use_pts; +#if FF_API_LAVF_SHORTEST /** * Timestamp of the end of the shortest stream. */ int64_t shortest_end; +#endif /** * Whether or not avformat_init_output has already been called @@ -199,6 +196,7 @@ typedef struct FFStream { */ AVStream pub; + AVFormatContext *fmtctx; /** * Set to 1 if the codec allows reordering, so pts can be different * from dts. @@ -241,7 +239,7 @@ typedef struct FFStream { int is_intra_only; - FFFrac *priv_pts; + FFFrac priv_pts; /** * Stream information used internally by avformat_find_stream_info() @@ -355,10 +353,12 @@ typedef struct FFStream { uint8_t dts_ordered; uint8_t dts_misordered; +#if FF_API_AVSTREAM_SIDE_DATA /** * Internal data to inject global side data */ int inject_global_side_data; +#endif /** * display aspect ratio (0 if unknown) @@ -408,6 +408,10 @@ typedef struct FFStream { */ int64_t first_dts; int64_t cur_dts; + + const struct AVCodecDescriptor *codec_desc; + + AVRational transferred_mux_tb; } FFStream; static av_always_inline FFStream *ffstream(AVStream *st) @@ -417,7 +421,27 @@ static av_always_inline FFStream *ffstream(AVStream *st) static av_always_inline const FFStream *cffstream(const AVStream *st) { - return (FFStream*)st; + return (const FFStream*)st; +} + +typedef struct FFStreamGroup { + /** + * The public context. + */ + AVStreamGroup pub; + + AVFormatContext *fmtctx; +} FFStreamGroup; + + +static av_always_inline FFStreamGroup *ffstreamgroup(AVStreamGroup *stg) +{ + return (FFStreamGroup*)stg; +} + +static av_always_inline const FFStreamGroup *cffstreamgroup(const AVStreamGroup *stg) +{ + return (const FFStreamGroup*)stg; } #ifdef __GNUC__ @@ -565,8 +589,8 @@ void ff_parse_key_value(const char *str, ff_parse_key_val_cb callback_get_buf, enum AVCodecID ff_guess_image2_codec(const char *filename); -const AVCodec *ff_find_decoder(AVFormatContext *s, const AVStream *st, - enum AVCodecID codec_id); +const struct AVCodec *ff_find_decoder(AVFormatContext *s, const AVStream *st, + enum AVCodecID codec_id); /** * Set the time base and wrapping info for a given stream. This will be used @@ -601,6 +625,18 @@ void ff_free_stream(AVStream **st); */ void ff_remove_stream(AVFormatContext *s, AVStream *st); +/** + * Frees a stream group without modifying the corresponding AVFormatContext. + * Must only be called if the latter doesn't matter or if the stream + * is not yet attached to an AVFormatContext. + */ +void ff_free_stream_group(AVStreamGroup **pstg); +/** + * Remove a stream group from its AVFormatContext and free it. + * The stream group must be the last stream group of the AVFormatContext. + */ +void ff_remove_stream_group(AVFormatContext *s, AVStreamGroup *stg); + unsigned int ff_codec_get_tag(const AVCodecTag *tags, enum AVCodecID id); enum AVCodecID ff_codec_get_id(const AVCodecTag *tags, unsigned int tag); @@ -677,10 +713,6 @@ int ff_copy_whiteblacklists(AVFormatContext *dst, const AVFormatContext *src); */ int ff_format_io_close(AVFormatContext *s, AVIOContext **pb); -/* Default io_close callback, not to be used directly, use ff_format_io_close - * instead. */ -void ff_format_io_close_default(AVFormatContext *s, AVIOContext *pb); - /** * Utility function to check if the file uses http or https protocol * @@ -705,7 +737,18 @@ int ff_unlock_avformat(void); */ void ff_format_set_url(AVFormatContext *s, char *url); +/** + * Return a positive value if the given url has one of the given + * extensions, negative AVERROR on error, 0 otherwise. + * + * @param url url to check against the given extensions + * @param extensions a comma-separated list of filename extensions + */ +int ff_match_url_ext(const char *url, const char *extensions); + struct FFOutputFormat; -void avpriv_register_devices(const struct FFOutputFormat * const o[], const AVInputFormat * const i[]); +struct FFInputFormat; +void avpriv_register_devices(const struct FFOutputFormat * const o[], + const struct FFInputFormat * const i[]); #endif /* AVFORMAT_INTERNAL_H */ diff --git a/libavformat/ipmovie.c b/libavformat/ipmovie.c index 4f5c164d3fe..5d1748953ab 100644 --- a/libavformat/ipmovie.c +++ b/libavformat/ipmovie.c @@ -700,9 +700,9 @@ static int ipmovie_read_packet(AVFormatContext *s, } } -const AVInputFormat ff_ipmovie_demuxer = { - .name = "ipmovie", - .long_name = NULL_IF_CONFIG_SMALL("Interplay MVE"), +const FFInputFormat ff_ipmovie_demuxer = { + .p.name = "ipmovie", + .p.long_name = NULL_IF_CONFIG_SMALL("Interplay MVE"), .priv_data_size = sizeof(IPMVEContext), .read_probe = ipmovie_probe, .read_header = ipmovie_read_header, diff --git a/libavformat/ipudec.c b/libavformat/ipudec.c index 4e346f9638c..770eb8a3d17 100644 --- a/libavformat/ipudec.c +++ b/libavformat/ipudec.c @@ -20,6 +20,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" #include "avio_internal.h" #include "rawdec.h" @@ -67,15 +68,15 @@ static int ipu_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_ipu_demuxer = { - .name = "ipu", - .long_name = NULL_IF_CONFIG_SMALL("raw IPU Video"), +const FFInputFormat ff_ipu_demuxer = { + .p.name = "ipu", + .p.long_name = NULL_IF_CONFIG_SMALL("raw IPU Video"), + .p.extensions = "ipu", + .p.flags = AVFMT_GENERIC_INDEX, + .p.priv_class = &ff_raw_demuxer_class, .read_probe = ipu_read_probe, .read_header = ipu_read_header, .read_packet = ff_raw_read_partial_packet, - .extensions = "ipu", - .flags = AVFMT_GENERIC_INDEX, .raw_codec_id = AV_CODEC_ID_IPU, .priv_data_size = sizeof(FFRawDemuxerContext), - .priv_class = &ff_raw_demuxer_class, }; diff --git a/libavformat/ircamdec.c b/libavformat/ircamdec.c index eb59c95e14d..03a61e9f131 100644 --- a/libavformat/ircamdec.c +++ b/libavformat/ircamdec.c @@ -22,6 +22,7 @@ #include "libavutil/intreadwrite.h" #include "libavcodec/internal.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "pcm.h" #include "ircam.h" @@ -107,13 +108,13 @@ static int ircam_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_ircam_demuxer = { - .name = "ircam", - .long_name = NULL_IF_CONFIG_SMALL("Berkeley/IRCAM/CARL Sound Format"), +const FFInputFormat ff_ircam_demuxer = { + .p.name = "ircam", + .p.long_name = NULL_IF_CONFIG_SMALL("Berkeley/IRCAM/CARL Sound Format"), + .p.extensions = "sf,ircam", + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = ircam_probe, .read_header = ircam_read_header, .read_packet = ff_pcm_read_packet, .read_seek = ff_pcm_read_seek, - .extensions = "sf,ircam", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/ircamenc.c b/libavformat/ircamenc.c index ceeab2b305b..7600d9a8a90 100644 --- a/libavformat/ircamenc.c +++ b/libavformat/ircamenc.c @@ -32,11 +32,6 @@ static int ircam_write_header(AVFormatContext *s) AVCodecParameters *par = s->streams[0]->codecpar; uint32_t tag; - if (s->nb_streams != 1) { - av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); - return AVERROR(EINVAL); - } - tag = ff_codec_get_tag(ff_codec_ircam_le_tags, par->codec_id); if (!tag) { av_log(s, AV_LOG_ERROR, "unsupported codec\n"); @@ -57,6 +52,8 @@ const FFOutputFormat ff_ircam_muxer = { .p.long_name = NULL_IF_CONFIG_SMALL("Berkeley/IRCAM/CARL Sound Format"), .p.audio_codec = AV_CODEC_ID_PCM_S16LE, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH, .write_header = ircam_write_header, .write_packet = ff_raw_write_packet, .p.codec_tag = (const AVCodecTag *const []){ ff_codec_ircam_le_tags, 0 }, diff --git a/libavformat/isom.c b/libavformat/isom.c index 6d019881e58..c5930bd4d87 100644 --- a/libavformat/isom.c +++ b/libavformat/isom.c @@ -36,6 +36,7 @@ const AVCodecTag ff_mp4_obj_type[] = { { AV_CODEC_ID_MPEG4 , 0x20 }, { AV_CODEC_ID_H264 , 0x21 }, { AV_CODEC_ID_HEVC , 0x23 }, + { AV_CODEC_ID_VVC , 0x33 }, { AV_CODEC_ID_AAC , 0x40 }, { AV_CODEC_ID_MP4ALS , 0x40 }, /* 14496-3 ALS */ { AV_CODEC_ID_MPEG2VIDEO , 0x61 }, /* MPEG-2 Main */ @@ -358,6 +359,7 @@ int ff_mp4_read_dec_config_descr(AVFormatContext *fc, AVStream *st, AVIOContext st->codecpar->extradata_size, 1, fc); if (ret < 0) return ret; + av_channel_layout_uninit(&st->codecpar->ch_layout); st->codecpar->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; st->codecpar->ch_layout.nb_channels = cfg.channels; if (cfg.object_type == 29 && cfg.sampling_index < 3) // old mp3on4 diff --git a/libavformat/isom.h b/libavformat/isom.h index 5107b4eb748..c0a5788e085 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -29,6 +29,7 @@ #include "libavutil/encryption_info.h" #include "libavutil/mastering_display_metadata.h" +#include "libavutil/ambient_viewing_environment.h" #include "libavutil/spherical.h" #include "libavutil/stereo3d.h" @@ -165,7 +166,9 @@ typedef struct MOVIndexRange { typedef struct MOVStreamContext { AVIOContext *pb; + int refcount; int pb_is_copied; + int id; ///< AVStream id int ffindex; ///< AVStream index int next_chunk; unsigned int chunk_count; @@ -190,7 +193,7 @@ typedef struct MOVStreamContext { unsigned int sample_size; ///< may contain value calculated from stsd or value from stsz atom unsigned int stsz_sample_size; ///< always contains sample size from stsz atom unsigned int sample_count; - int *sample_sizes; + unsigned int *sample_sizes; int keyframe_absent; unsigned int keyframe_count; int *keyframes; @@ -249,6 +252,8 @@ typedef struct MOVStreamContext { AVMasteringDisplayMetadata *mastering; AVContentLightMetadata *coll; size_t coll_size; + AVAmbientViewingEnvironment *ambient; + size_t ambient_size; uint32_t format; @@ -260,14 +265,37 @@ typedef struct MOVStreamContext { AVEncryptionInfo *default_encrypted_sample; MOVEncryptionIndex *encryption_index; } cenc; + + struct IAMFDemuxContext *iamf; } MOVStreamContext; +typedef struct HEIFItem { + AVStream *st; + char *name; + int item_id; + int64_t extent_length; + int64_t extent_offset; + int width; + int height; + int type; + int is_idat_relative; +} HEIFItem; + +typedef struct HEIFGrid { + HEIFItem *item; + HEIFItem **tile_item_list; + int16_t *tile_id_list; + int nb_tiles; +} HEIFGrid; + typedef struct MOVContext { const AVClass *class; ///< class for private options AVFormatContext *fc; int time_scale; int64_t duration; ///< duration of the longest track int found_moov; ///< 'moov' atom has been found + int found_iloc; ///< 'iloc' atom has been found + int found_iinf; ///< 'iinf' atom has been found int found_mdat; ///< 'mdat' atom has been found int found_hdlr_mdta; ///< 'hdlr' atom with type 'mdta' has been found int trak_index; ///< Index of the current 'trak' @@ -319,14 +347,15 @@ typedef struct MOVContext { int have_read_mfra_size; uint32_t mfra_size; uint32_t max_stts_delta; - int is_still_picture_avif; int primary_item_id; - struct { - int item_id; - int extent_length; - int64_t extent_offset; - } *avif_info; - int avif_info_size; + int cur_item_id; + HEIFItem *heif_item; + int nb_heif_item; + HEIFGrid *heif_grid; + int nb_heif_grid; + int thmb_item_id; + int64_t idat_offset; + int interleaved_read; } MOVContext; int ff_mp4_read_descr_len(AVIOContext *pb); @@ -418,6 +447,8 @@ static inline enum AVCodecID ff_mov_get_lpcm_codec_id(int bps, int flags) #define MOV_ISMV_TTML_TAG MKTAG('d', 'f', 'x', 'p') #define MOV_MP4_TTML_TAG MKTAG('s', 't', 'p', 'p') +#define MOV_MP4_FPCM_TAG MKTAG('f', 'p', 'c', 'm') +#define MOV_MP4_IPCM_TAG MKTAG('i', 'p', 'c', 'm') struct MP4TrackKindValueMapping { int disposition; diff --git a/libavformat/isom_tags.c b/libavformat/isom_tags.c index e2b80405cc8..058f0f2a594 100644 --- a/libavformat/isom_tags.c +++ b/libavformat/isom_tags.c @@ -118,6 +118,9 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_RAWVIDEO, MKTAG('W', 'R', 'A', 'W') }, + { AV_CODEC_ID_VVC, MKTAG('v', 'v', 'c', '1') }, + { AV_CODEC_ID_VVC, MKTAG('v', 'v', 'i', '1') }, + { AV_CODEC_ID_HEVC, MKTAG('h', 'e', 'v', '1') }, /* HEVC/H.265 which indicates parameter sets may be in ES */ { AV_CODEC_ID_HEVC, MKTAG('h', 'v', 'c', '1') }, /* HEVC/H.265 which indicates parameter sets shall not be in ES */ { AV_CODEC_ID_HEVC, MKTAG('d', 'v', 'h', 'e') }, /* HEVC-based Dolby Vision derived from hev1 */ @@ -147,6 +150,8 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_H264, MKTAG('d', 'v', 'a', '1') }, /* AVC-based Dolby Vision derived from avc1 */ { AV_CODEC_ID_H264, MKTAG('d', 'v', 'a', 'v') }, /* AVC-based Dolby Vision derived from avc3 */ + { AV_CODEC_ID_EVC, MKTAG('e', 'v', 'c', '1') }, /* EVC/MPEG-5 */ + { AV_CODEC_ID_VP8, MKTAG('v', 'p', '0', '8') }, /* VP8 */ { AV_CODEC_ID_VP9, MKTAG('v', 'p', '0', '9') }, /* VP9 */ { AV_CODEC_ID_AV1, MKTAG('a', 'v', '0', '1') }, /* AV1 */ @@ -258,6 +263,8 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_MAGICYUV, MKTAG('M', '8', 'Y', 'A') }, { AV_CODEC_ID_MAGICYUV, MKTAG('M', '2', 'R', 'A') }, { AV_CODEC_ID_MAGICYUV, MKTAG('M', '2', 'R', 'G') }, + { AV_CODEC_ID_MAGICYUV, MKTAG('M', '4', 'R', 'A') }, + { AV_CODEC_ID_MAGICYUV, MKTAG('M', '4', 'R', 'G') }, { AV_CODEC_ID_SHEERVIDEO, MKTAG('S', 'h', 'r', '0') }, { AV_CODEC_ID_SHEERVIDEO, MKTAG('S', 'h', 'r', '1') }, @@ -281,6 +288,8 @@ const AVCodecTag ff_codec_movvideo_tags[] = { { AV_CODEC_ID_MEDIA100, MKTAG('d', 't', 'p', 'a') }, { AV_CODEC_ID_MEDIA100, MKTAG('d', 't', 'P', 'A') }, + { AV_CODEC_ID_CFHD, MKTAG('C', 'F', 'H', 'D') }, + { AV_CODEC_ID_NONE, 0 }, }; @@ -288,6 +297,7 @@ const AVCodecTag ff_codec_movaudio_tags[] = { { AV_CODEC_ID_AAC, MKTAG('m', 'p', '4', 'a') }, { AV_CODEC_ID_AC3, MKTAG('a', 'c', '-', '3') }, /* ETSI TS 102 366 Annex F */ { AV_CODEC_ID_AC3, MKTAG('s', 'a', 'c', '3') }, /* Nero Recode */ + { AV_CODEC_ID_AC4, MKTAG('a', 'c', '-', '4') }, { AV_CODEC_ID_ADPCM_IMA_QT, MKTAG('i', 'm', 'a', '4') }, { AV_CODEC_ID_ALAC, MKTAG('a', 'l', 'a', 'c') }, { AV_CODEC_ID_AMR_NB, MKTAG('s', 'a', 'm', 'r') }, /* AMR-NB 3gp */ @@ -321,8 +331,6 @@ const AVCodecTag ff_codec_movaudio_tags[] = { { AV_CODEC_ID_PCM_S16LE, MKTAG('s', 'o', 'w', 't') }, { AV_CODEC_ID_PCM_S16BE, MKTAG('l', 'p', 'c', 'm') }, { AV_CODEC_ID_PCM_S16LE, MKTAG('l', 'p', 'c', 'm') }, - { AV_CODEC_ID_PCM_S16BE, MKTAG('i', 'p', 'c', 'm') }, - { AV_CODEC_ID_PCM_S16LE, MKTAG('i', 'p', 'c', 'm') }, { AV_CODEC_ID_PCM_S24BE, MKTAG('i', 'n', '2', '4') }, { AV_CODEC_ID_PCM_S24LE, MKTAG('i', 'n', '2', '4') }, { AV_CODEC_ID_PCM_S32BE, MKTAG('i', 'n', '3', '2') }, diff --git a/libavformat/iss.c b/libavformat/iss.c index f54ff8b0de8..7a68fcaf637 100644 --- a/libavformat/iss.c +++ b/libavformat/iss.c @@ -28,6 +28,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "libavutil/avstring.h" @@ -144,9 +145,9 @@ static int iss_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVInputFormat ff_iss_demuxer = { - .name = "iss", - .long_name = NULL_IF_CONFIG_SMALL("Funcom ISS"), +const FFInputFormat ff_iss_demuxer = { + .p.name = "iss", + .p.long_name = NULL_IF_CONFIG_SMALL("Funcom ISS"), .priv_data_size = sizeof(IssDemuxContext), .read_probe = iss_probe, .read_header = iss_read_header, diff --git a/libavformat/iv8.c b/libavformat/iv8.c index a3954ca1ed2..635675cdc7e 100644 --- a/libavformat/iv8.c +++ b/libavformat/iv8.c @@ -19,6 +19,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" @@ -107,11 +108,11 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVInputFormat ff_iv8_demuxer = { - .name = "iv8", - .long_name = NULL_IF_CONFIG_SMALL("IndigoVision 8000 video"), +const FFInputFormat ff_iv8_demuxer = { + .p.name = "iv8", + .p.long_name = NULL_IF_CONFIG_SMALL("IndigoVision 8000 video"), + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = probe, .read_header = read_header, .read_packet = read_packet, - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/ivfdec.c b/libavformat/ivfdec.c index 511f2387ede..9e34fb014ed 100644 --- a/libavformat/ivfdec.c +++ b/libavformat/ivfdec.c @@ -19,6 +19,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" #include "riff.h" #include "libavutil/intreadwrite.h" @@ -51,11 +52,18 @@ static int read_header(AVFormatContext *s) st->codecpar->codec_id = ff_codec_get_id(ff_codec_bmp_tags, st->codecpar->codec_tag); st->codecpar->width = avio_rl16(s->pb); st->codecpar->height = avio_rl16(s->pb); - time_base.den = avio_rl32(s->pb); - time_base.num = avio_rl32(s->pb); - st->duration = avio_rl32(s->pb); + time_base.den = avio_rl32(s->pb); + time_base.num = avio_rl32(s->pb); + st->nb_frames = avio_rl32(s->pb); avio_skip(s->pb, 4); // unused + // Infer duration from nb_frames, in order to be backward compatible with + // previous IVF demuxer. + // It is popular to configure time_base to 1/frame_rate by IVF muxer, that + // the duration happens to be the same with nb_frames. See + // `https://chromium.googlesource.com/webm/vp8-test-vectors/+/refs/heads/main` + st->duration = st->nb_frames; + ffstream(st)->need_parsing = AVSTREAM_PARSE_HEADERS; if (!time_base.den || !time_base.num) { @@ -81,12 +89,12 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_ivf_demuxer = { - .name = "ivf", - .long_name = NULL_IF_CONFIG_SMALL("On2 IVF"), +const FFInputFormat ff_ivf_demuxer = { + .p.name = "ivf", + .p.long_name = NULL_IF_CONFIG_SMALL("On2 IVF"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.codec_tag = (const AVCodecTag* const []){ ff_codec_bmp_tags, 0 }, .read_probe = probe, .read_header = read_header, .read_packet = read_packet, - .flags = AVFMT_GENERIC_INDEX, - .codec_tag = (const AVCodecTag* const []){ ff_codec_bmp_tags, 0 }, }; diff --git a/libavformat/ivfenc.c b/libavformat/ivfenc.c index 47b4efbcd10..09782eecd61 100644 --- a/libavformat/ivfenc.c +++ b/libavformat/ivfenc.c @@ -29,15 +29,9 @@ typedef struct IVFEncContext { static int ivf_init(AVFormatContext *s) { - AVCodecParameters *par; + AVCodecParameters *par = s->streams[0]->codecpar; - if (s->nb_streams != 1) { - av_log(s, AV_LOG_ERROR, "Format supports only exactly one video stream\n"); - return AVERROR(EINVAL); - } - par = s->streams[0]->codecpar; - if (par->codec_type != AVMEDIA_TYPE_VIDEO || - !(par->codec_id == AV_CODEC_ID_AV1 || + if (!(par->codec_id == AV_CODEC_ID_AV1 || par->codec_id == AV_CODEC_ID_VP8 || par->codec_id == AV_CODEC_ID_VP9)) { av_log(s, AV_LOG_ERROR, "Currently only VP8, VP9 and AV1 are supported!\n"); @@ -72,7 +66,8 @@ static int ivf_write_header(AVFormatContext *s) avio_wl16(pb, par->height); avio_wl32(pb, s->streams[0]->time_base.den); avio_wl32(pb, s->streams[0]->time_base.num); - avio_wl64(pb, 0xFFFFFFFFFFFFFFFFULL); // length is overwritten at the end of muxing + avio_wl32(pb, 0xFFFFFFFF); // "number of frames" is overwritten at the end of muxing + avio_wl32(pb, 0); // unused return 0; } @@ -99,16 +94,12 @@ static int ivf_write_trailer(AVFormatContext *s) AVIOContext *pb = s->pb; IVFEncContext *ctx = s->priv_data; - if ((pb->seekable & AVIO_SEEKABLE_NORMAL) && - (ctx->frame_cnt > 1 || (ctx->frame_cnt == 1 && ctx->last_pkt_duration))) { + // overwrite the "number of frames" + if ((pb->seekable & AVIO_SEEKABLE_NORMAL)) { int64_t end = avio_tell(pb); avio_seek(pb, 24, SEEK_SET); - // overwrite the "length" field (duration) - avio_wl32(pb, ctx->last_pkt_duration ? - ctx->sum_delta_pts + ctx->last_pkt_duration : - ctx->frame_cnt * ctx->sum_delta_pts / (ctx->frame_cnt - 1)); - avio_wl32(pb, 0); // zero out unused bytes + avio_wl32(pb, ctx->frame_cnt); avio_seek(pb, end, SEEK_SET); } @@ -128,7 +119,9 @@ const FFOutputFormat ff_ivf_muxer = { .p.extensions = "ivf", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_VP8, + .p.subtitle_codec = AV_CODEC_ID_NONE, .p.codec_tag = (const AVCodecTag* const []){ codec_ivf_tags, 0 }, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH, .priv_data_size = sizeof(IVFEncContext), .init = ivf_init, .write_header = ivf_write_header, diff --git a/libavformat/jacosubdec.c b/libavformat/jacosubdec.c index c6e5b4aa6dc..da150a96ab7 100644 --- a/libavformat/jacosubdec.c +++ b/libavformat/jacosubdec.c @@ -26,6 +26,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" #include "subtitles.h" #include "libavcodec/jacosub.h" @@ -127,29 +128,29 @@ static const char *read_ts(JACOsubContext *jacosub, const char *buf, static int get_shift(unsigned timeres, const char *buf) { int sign = 1; - int a = 0, b = 0, c = 0, d = 0; + int h = 0, m = 0, s = 0, d = 0; int64_t ret; #define SSEP "%*1[.:]" - int n = sscanf(buf, "%d"SSEP"%d"SSEP"%d"SSEP"%d", &a, &b, &c, &d); + int n = sscanf(buf, "%d"SSEP"%d"SSEP"%d"SSEP"%d", &h, &m, &s, &d); #undef SSEP - if (a == INT_MIN) + if (h == INT_MIN) return 0; - if (*buf == '-' || a < 0) { + if (*buf == '-' || h < 0) { sign = -1; - a = FFABS(a); + h = FFABS(h); } ret = 0; switch (n) { - case 1: a = 0; - case 2: c = b; b = a; a = 0; - case 3: d = c; c = b; b = a; a = 0; + case 1: h = 0; //clear all in case of a single parameter + case 2: s = m; m = h; h = 0; //shift into second subsecondd + case 3: d = s; s = m; m = h; h = 0; //shift into minute second subsecond } - ret = (int64_t)a*3600 + (int64_t)b*60 + c; - if (FFABS(ret) > (INT64_MAX - FFABS(d)) / timeres) + ret = (int64_t)h*3600 + (int64_t)m*60 + s; + if (FFABS(ret) > (INT64_MAX - FFABS((int64_t)d)) / timeres) return 0; ret = sign * (ret * timeres + d); @@ -256,11 +257,11 @@ static int jacosub_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_jacosub_demuxer = { - .name = "jacosub", - .long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle format"), +const FFInputFormat ff_jacosub_demuxer = { + .p.name = "jacosub", + .p.long_name = NULL_IF_CONFIG_SMALL("JACOsub subtitle format"), .priv_data_size = sizeof(JACOsubContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = jacosub_probe, .read_header = jacosub_read_header, .read_packet = ff_subtitles_read_packet, diff --git a/libavformat/jacosubenc.c b/libavformat/jacosubenc.c index fa0f9fdaa2a..428505e51f6 100644 --- a/libavformat/jacosubenc.c +++ b/libavformat/jacosubenc.c @@ -36,7 +36,11 @@ const FFOutputFormat ff_jacosub_muxer = { .p.mime_type = "text/x-jacosub", .p.extensions = "jss,js", .p.flags = AVFMT_TS_NONSTRICT, + .p.video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_CODEC_ID_NONE, .p.subtitle_codec = AV_CODEC_ID_JACOSUB, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_header = jacosub_write_header, .write_packet = ff_raw_write_packet, }; diff --git a/libavformat/jpegxl_anim_dec.c b/libavformat/jpegxl_anim_dec.c new file mode 100644 index 00000000000..ac95d3b9617 --- /dev/null +++ b/libavformat/jpegxl_anim_dec.c @@ -0,0 +1,206 @@ +/* + * Animated JPEG XL Demuxer + * Copyright (c) 2023 Leo Izen (thebombzen) + * + * 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 + * Animated JPEG XL Demuxer + * @see ISO/IEC 18181-1 and 18181-2 + */ + +#include +#include + +#include "libavcodec/jpegxl.h" +#include "libavcodec/jpegxl_parse.h" +#include "libavutil/intreadwrite.h" +#include "libavutil/opt.h" + +#include "avformat.h" +#include "demux.h" +#include "internal.h" + +typedef struct JXLAnimDemuxContext { + AVBufferRef *initial; +} JXLAnimDemuxContext; + +static int jpegxl_anim_probe(const AVProbeData *p) +{ + uint8_t buffer[4096 + AV_INPUT_BUFFER_PADDING_SIZE]; + int copied = 0, ret; + FFJXLMetadata meta = { 0 }; + + /* this is a raw codestream */ + if (AV_RL16(p->buf) == FF_JPEGXL_CODESTREAM_SIGNATURE_LE) { + ret = ff_jpegxl_parse_codestream_header(p->buf, p->buf_size, &meta, 5); + if (ret >= 0 && meta.animation_offset > 0) + return AVPROBE_SCORE_MAX; + + return 0; + } + + /* not a JPEG XL file at all */ + if (AV_RL64(p->buf) != FF_JPEGXL_CONTAINER_SIGNATURE_LE) + return 0; + + if (ff_jpegxl_collect_codestream_header(p->buf, p->buf_size, buffer, + sizeof(buffer) - AV_INPUT_BUFFER_PADDING_SIZE, &copied) <= 0 + || copied <= 0) + return 0; + + ret = ff_jpegxl_parse_codestream_header(buffer, copied, &meta, 10); + if (ret >= 0 && meta.animation_offset > 0) + return AVPROBE_SCORE_MAX; + + return 0; +} + +static int jpegxl_anim_read_header(AVFormatContext *s) +{ + JXLAnimDemuxContext *ctx = s->priv_data; + AVIOContext *pb = s->pb; + AVStream *st; + uint8_t head[256 + AV_INPUT_BUFFER_PADDING_SIZE]; + const int sizeofhead = sizeof(head) - AV_INPUT_BUFFER_PADDING_SIZE; + int headsize = 0, ret; + FFJXLMetadata meta = { 0 }; + + uint64_t sig16 = avio_rl16(pb); + if (sig16 == FF_JPEGXL_CODESTREAM_SIGNATURE_LE) { + AV_WL16(head, sig16); + headsize = avio_read(s->pb, head + 2, sizeofhead - 2); + if (headsize < 0) + return headsize; + headsize += 2; + ctx->initial = av_buffer_alloc(headsize); + if (!ctx->initial) + return AVERROR(ENOMEM); + memcpy(ctx->initial->data, head, headsize); + } else { + uint64_t sig64 = avio_rl64(pb); + sig64 = (sig64 << 16) | sig16; + if (sig64 != FF_JPEGXL_CONTAINER_SIGNATURE_LE) + return AVERROR_INVALIDDATA; + avio_skip(pb, 2); // first box always 12 bytes + while (1) { + int copied = 0; + uint8_t buf[4096]; + int read = avio_read(pb, buf, sizeof(buf)); + if (read < 0) + return read; + if (!ctx->initial) { + ctx->initial = av_buffer_alloc(read + 12); + if (!ctx->initial) + return AVERROR(ENOMEM); + AV_WL64(ctx->initial->data, FF_JPEGXL_CONTAINER_SIGNATURE_LE); + AV_WL32(ctx->initial->data + 8, 0x0a870a0d); + } else { + /* this only should be happening zero or one times in practice */ + if (av_buffer_realloc(&ctx->initial, ctx->initial->size + read) < 0) + return AVERROR(ENOMEM); + } + ff_jpegxl_collect_codestream_header(buf, read, head + headsize, sizeofhead - headsize, &copied); + memcpy(ctx->initial->data + (ctx->initial->size - read), buf, read); + headsize += copied; + if (headsize >= sizeofhead || read < sizeof(buf)) + break; + } + } + + /* offset in bits of the animation header */ + ret = ff_jpegxl_parse_codestream_header(head, headsize, &meta, 0); + if (ret < 0 || meta.animation_offset <= 0) + return AVERROR_INVALIDDATA; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + st->codecpar->codec_id = AV_CODEC_ID_JPEGXL; + avpriv_set_pts_info(st, 1, meta.timebase.num, meta.timebase.den); + ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL; + + return 0; +} + +/* the decoder requires the full input file as a single packet */ +static int jpegxl_anim_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + JXLAnimDemuxContext *ctx = s->priv_data; + AVIOContext *pb = s->pb; + int ret; + int64_t size; + size_t offset = 0; + + size = avio_size(pb); + if (size < 0) + return size; + if (size > INT_MAX) + return AVERROR(EDOM); + if (size == 0) + size = 4096; + + if (ctx->initial && size < ctx->initial->size) + size = ctx->initial->size; + + ret = av_new_packet(pkt, size); + if (ret < 0) + return ret; + + if (ctx->initial) { + offset = ctx->initial->size; + memcpy(pkt->data, ctx->initial->data, offset); + av_buffer_unref(&ctx->initial); + } + + pkt->pos = avio_tell(pb) - offset; + + ret = avio_read(pb, pkt->data + offset, size - offset); + if (ret < 0) + return ret; + if (ret < size - offset) + pkt->size = ret + offset; + + return 0; +} + +static int jpegxl_anim_close(AVFormatContext *s) +{ + JXLAnimDemuxContext *ctx = s->priv_data; + if (ctx->initial) + av_buffer_unref(&ctx->initial); + + return 0; +} + +const FFInputFormat ff_jpegxl_anim_demuxer = { + .p.name = "jpegxl_anim", + .p.long_name = NULL_IF_CONFIG_SMALL("Animated JPEG XL"), + .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS, + .p.mime_type = "image/jxl", + .p.extensions = "jxl", + .priv_data_size = sizeof(JXLAnimDemuxContext), + .read_probe = jpegxl_anim_probe, + .read_header = jpegxl_anim_read_header, + .read_packet = jpegxl_anim_read_packet, + .read_close = jpegxl_anim_close, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, +}; diff --git a/libavcodec/tests/avfft.c b/libavformat/jpegxl_parse.c similarity index 86% rename from libavcodec/tests/avfft.c rename to libavformat/jpegxl_parse.c index 22aa99abca5..e0d4f96204e 100644 --- a/libavcodec/tests/avfft.c +++ b/libavformat/jpegxl_parse.c @@ -1,4 +1,7 @@ /* + * JPEG XL Header Parser Stub + * Copyright (c) 2023 Leo Izen + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -16,10 +19,4 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -/* - * This test is similar to fft-fixed.c or fft-fixed32.c - */ - -#define AVFFT 1 -#define FFT_FLOAT 1 -#include "fft.c" +#include "libavcodec/jpegxl_parse.c" diff --git a/libavformat/jpegxl_probe.c b/libavformat/jpegxl_probe.c deleted file mode 100644 index 2d2d1283872..00000000000 --- a/libavformat/jpegxl_probe.c +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Jpeg XL header verification - * Copyright (c) 2022 Leo Izen - * - * 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 "jpegxl_probe.h" - -#define UNCHECKED_BITSTREAM_READER 0 -#define BITSTREAM_READER_LE -#include "libavcodec/get_bits.h" - -enum JpegXLExtraChannelType { - FF_JPEGXL_CT_ALPHA = 0, - FF_JPEGXL_CT_DEPTH, - FF_JPEGXL_CT_SPOT_COLOR, - FF_JPEGXL_CT_SELECTION_MASK, - FF_JPEGXL_CT_BLACK, - FF_JPEGXL_CT_CFA, - FF_JPEGXL_CT_THERMAL, - FF_JPEGXL_CT_NON_OPTIONAL = 15, - FF_JPEGXL_CT_OPTIONAL -}; - -enum JpegXLColorSpace { - FF_JPEGXL_CS_RGB = 0, - FF_JPEGXL_CS_GRAY, - FF_JPEGXL_CS_XYB, - FF_JPEGXL_CS_UNKNOWN -}; - -enum JpegXLWhitePoint { - FF_JPEGXL_WP_D65 = 1, - FF_JPEGXL_WP_CUSTOM, - FF_JPEGXL_WP_E = 10, - FF_JPEGXL_WP_DCI = 11 -}; - -enum JpegXLPrimaries { - FF_JPEGXL_PR_SRGB = 1, - FF_JPEGXL_PR_CUSTOM, - FF_JPEGXL_PR_2100 = 9, - FF_JPEGXL_PR_P3 = 11, -}; - -/* read a U32(c_i + u(u_i)) */ -static av_always_inline uint32_t jxl_u32(GetBitContext *gb, - uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3, - uint32_t u0, uint32_t u1, uint32_t u2, uint32_t u3) -{ - const uint32_t constants[4] = {c0, c1, c2, c3}; - const uint32_t ubits [4] = {u0, u1, u2, u3}; - uint32_t ret, choice = get_bits(gb, 2); - - ret = constants[choice]; - if (ubits[choice]) - ret += get_bits_long(gb, ubits[choice]); - - return ret; -} - -static av_always_inline uint32_t jxl_enum(GetBitContext *gb) -{ - return jxl_u32(gb, 0, 1, 2, 18, 0, 0, 4, 6); -} - -/* read a U64() */ -static uint64_t jpegxl_u64(GetBitContext *gb) -{ - uint64_t shift = 12, ret; - - switch (get_bits(gb, 2)) { - case 0: - ret = 0; - break; - case 1: - ret = 1 + get_bits(gb, 4); - break; - case 2: - ret = 17 + get_bits(gb, 8); - break; - case 3: - ret = get_bits(gb, 12); - while (get_bits1(gb)) { - if (shift < 60) { - ret |= (uint64_t)get_bits(gb, 8) << shift; - shift += 8; - } else { - ret |= (uint64_t)get_bits(gb, 4) << shift; - break; - } - } - break; - } - - return ret; -} - -static uint32_t jpegxl_width_from_ratio(uint32_t height, int ratio) -{ - uint64_t height64 = height; /* avoid integer overflow */ - switch (ratio) { - case 1: - return height; - case 2: - return (uint32_t)((height64 * 12) / 10); - case 3: - return (uint32_t)((height64 * 4) / 3); - case 4: - return (uint32_t)((height64 * 3) / 2); - case 5: - return (uint32_t)((height64 * 16) / 9); - case 6: - return (uint32_t)((height64 * 5) / 4); - case 7: - return (uint32_t)(height64 * 2); - default: - break; - } - - return 0; /* manual width */ -} - -/** - * validate a Jpeg XL Size Header - * @return >= 0 upon valid size, < 0 upon invalid size found - */ -static int jpegxl_read_size_header(GetBitContext *gb) -{ - uint32_t width, height; - - if (get_bits1(gb)) { - /* small size header */ - height = (get_bits(gb, 5) + 1) << 3; - width = jpegxl_width_from_ratio(height, get_bits(gb, 3)); - if (!width) - width = (get_bits(gb, 5) + 1) << 3; - } else { - /* large size header */ - height = 1 + jxl_u32(gb, 0, 0, 0, 0, 9, 13, 18, 30); - width = jpegxl_width_from_ratio(height, get_bits(gb, 3)); - if (!width) - width = 1 + jxl_u32(gb, 0, 0, 0, 0, 9, 13, 18, 30); - } - if (width > (1 << 18) || height > (1 << 18) - || (width >> 4) * (height >> 4) > (1 << 20)) - return -1; - - return 0; -} - -/** - * validate a Jpeg XL Preview Header - * @return >= 0 upon valid size, < 0 upon invalid size found - */ -static int jpegxl_read_preview_header(GetBitContext *gb) -{ - uint32_t width, height; - - if (get_bits1(gb)) { - /* coded height and width divided by eight */ - height = jxl_u32(gb, 16, 32, 1, 33, 0, 0, 5, 9) << 3; - width = jpegxl_width_from_ratio(height, get_bits(gb, 3)); - if (!width) - width = jxl_u32(gb, 16, 32, 1, 33, 0, 0, 5, 9) << 3; - } else { - /* full height and width coded */ - height = jxl_u32(gb, 1, 65, 321, 1345, 6, 8, 10, 12); - width = jpegxl_width_from_ratio(height, get_bits(gb, 3)); - if (!width) - width = jxl_u32(gb, 1, 65, 321, 1345, 6, 8, 10, 12); - } - if (width > 4096 || height > 4096) - return -1; - - return 0; -} - -/** - * skip a Jpeg XL BitDepth Header. These cannot be invalid. - */ -static void jpegxl_skip_bit_depth(GetBitContext *gb) -{ - if (get_bits1(gb)) { - /* float samples */ - jxl_u32(gb, 32, 16, 24, 1, 0, 0, 0, 6); /* mantissa */ - skip_bits_long(gb, 4); /* exponent */ - } else { - /* integer samples */ - jxl_u32(gb, 8, 10, 12, 1, 0, 0, 0, 6); - } -} - -/** - * validate a Jpeg XL Extra Channel Info bundle - * @return >= 0 upon valid, < 0 upon invalid - */ -static int jpegxl_read_extra_channel_info(GetBitContext *gb) -{ - int all_default = get_bits1(gb); - uint32_t type, name_len = 0; - - if (!all_default) { - type = jxl_enum(gb); - if (type > 63) - return -1; /* enum types cannot be 64+ */ - if (type == FF_JPEGXL_CT_BLACK) - return -1; - jpegxl_skip_bit_depth(gb); - jxl_u32(gb, 0, 3, 4, 1, 0, 0, 0, 3); /* dim-shift */ - /* max of name_len is 1071 = 48 + 2^10 - 1 */ - name_len = jxl_u32(gb, 0, 0, 16, 48, 0, 4, 5, 10); - } else { - type = FF_JPEGXL_CT_ALPHA; - } - - /* skip over the name */ - skip_bits_long(gb, 8 * name_len); - - if (!all_default && type == FF_JPEGXL_CT_ALPHA) - skip_bits1(gb); - - if (type == FF_JPEGXL_CT_SPOT_COLOR) - skip_bits_long(gb, 16 * 4); - - if (type == FF_JPEGXL_CT_CFA) - jxl_u32(gb, 1, 0, 3, 19, 0, 2, 4, 8); - - return 0; -} - -/* verify that a codestream header is valid */ -int ff_jpegxl_verify_codestream_header(const uint8_t *buf, int buflen) -{ - GetBitContext gbi, *gb = &gbi; - int all_default, extra_fields = 0; - int xyb_encoded = 1, have_icc_profile = 0; - uint32_t num_extra_channels; - uint64_t extensions; - int ret; - - ret = init_get_bits8(gb, buf, buflen); - if (ret < 0) - return ret; - - if (get_bits_long(gb, 16) != FF_JPEGXL_CODESTREAM_SIGNATURE_LE) - return -1; - - if ((ret = jpegxl_read_size_header(gb)) < 0) - return ret; - - all_default = get_bits1(gb); - if (!all_default) - extra_fields = get_bits1(gb); - - if (extra_fields) { - skip_bits_long(gb, 3); /* orientation */ - - /* - * intrinstic size - * any size header here is valid, but as it - * is variable length we have to read it - */ - if (get_bits1(gb)) - jpegxl_read_size_header(gb); - - /* preview header */ - if (get_bits1(gb)) { - ret = jpegxl_read_preview_header(gb); - if (ret < 0) - return ret; - } - - /* animation header */ - if (get_bits1(gb)) { - jxl_u32(gb, 100, 1000, 1, 1, 0, 0, 10, 30); - jxl_u32(gb, 1, 1001, 1, 1, 0, 0, 8, 10); - jxl_u32(gb, 0, 0, 0, 0, 0, 3, 16, 32); - skip_bits_long(gb, 1); - } - } - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - - if (!all_default) { - jpegxl_skip_bit_depth(gb); - - /* modular_16bit_buffers must equal 1 */ - if (!get_bits1(gb)) - return -1; - - num_extra_channels = jxl_u32(gb, 0, 1, 2, 1, 0, 0, 4, 12); - if (num_extra_channels > 4) - return -1; - for (uint32_t i = 0; i < num_extra_channels; i++) { - ret = jpegxl_read_extra_channel_info(gb); - if (ret < 0) - return ret; - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - } - - xyb_encoded = get_bits1(gb); - - /* color encoding bundle */ - if (!get_bits1(gb)) { - uint32_t color_space; - have_icc_profile = get_bits1(gb); - color_space = jxl_enum(gb); - if (color_space > 63) - return -1; - - if (!have_icc_profile) { - if (color_space != FF_JPEGXL_CS_XYB) { - uint32_t white_point = jxl_enum(gb); - if (white_point > 63) - return -1; - if (white_point == FF_JPEGXL_WP_CUSTOM) { - /* ux and uy values */ - jxl_u32(gb, 0, 524288, 1048576, 2097152, 19, 19, 20, 21); - jxl_u32(gb, 0, 524288, 1048576, 2097152, 19, 19, 20, 21); - } - if (color_space != FF_JPEGXL_CS_GRAY) { - /* primaries */ - uint32_t primaries = jxl_enum(gb); - if (primaries > 63) - return -1; - if (primaries == FF_JPEGXL_PR_CUSTOM) { - /* ux/uy values for r,g,b */ - for (int i = 0; i < 6; i++) { - jxl_u32(gb, 0, 524288, 1048576, 2097152, 19, 19, 20, 21); - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - } - } - } - } - - /* transfer characteristics */ - if (get_bits1(gb)) { - /* gamma */ - skip_bits_long(gb, 24); - } else { - /* transfer function */ - if (jxl_enum(gb) > 63) - return -1; - } - - /* rendering intent */ - if (jxl_enum(gb) > 63) - return -1; - } - } - - /* tone mapping bundle */ - if (extra_fields && !get_bits1(gb)) - skip_bits_long(gb, 16 + 16 + 1 + 16); - - extensions = jpegxl_u64(gb); - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - if (extensions) { - for (int i = 0; i < 64; i++) { - if (extensions & (UINT64_C(1) << i)) - jpegxl_u64(gb); - if (get_bits_left(gb) < 1) - return AVERROR_INVALIDDATA; - } - } - } - - /* default transform */ - if (!get_bits1(gb)) { - /* opsin inverse matrix */ - if (xyb_encoded && !get_bits1(gb)) - skip_bits_long(gb, 16 * 16); - /* cw_mask and default weights */ - if (get_bits1(gb)) - skip_bits_long(gb, 16 * 15); - if (get_bits1(gb)) - skip_bits_long(gb, 16 * 55); - if (get_bits1(gb)) - skip_bits_long(gb, 16 * 210); - } - - if (!have_icc_profile) { - int bits_remaining = 7 - (get_bits_count(gb) - 1) % 8; - if (bits_remaining && get_bits(gb, bits_remaining)) - return -1; - } - - if (get_bits_left(gb) < 0) - return -1; - - return 0; -} diff --git a/libavformat/jvdec.c b/libavformat/jvdec.c index 5e0e2239f50..41dad2392f1 100644 --- a/libavformat/jvdec.c +++ b/libavformat/jvdec.c @@ -29,6 +29,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define JV_PREAMBLE_SIZE 5 @@ -250,11 +251,11 @@ static int read_seek(AVFormatContext *s, int stream_index, return 0; } -const AVInputFormat ff_jv_demuxer = { - .name = "jv", - .long_name = NULL_IF_CONFIG_SMALL("Bitmap Brothers JV"), +const FFInputFormat ff_jv_demuxer = { + .p.name = "jv", + .p.long_name = NULL_IF_CONFIG_SMALL("Bitmap Brothers JV"), .priv_data_size = sizeof(JVDemuxContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = read_probe, .read_header = read_header, .read_packet = read_packet, diff --git a/libavformat/kvag.c b/libavformat/kvag.c index 9487e7dd0eb..b55aa893ec2 100644 --- a/libavformat/kvag.c +++ b/libavformat/kvag.c @@ -25,6 +25,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" #include "avio_internal.h" +#include "demux.h" #include "internal.h" #include "mux.h" #include "rawenc.h" @@ -37,7 +38,7 @@ typedef struct KVAGHeader { uint32_t magic; uint32_t data_size; - uint32_t sample_rate; + int sample_rate; uint16_t stereo; } KVAGHeader; @@ -69,6 +70,9 @@ static int kvag_read_header(AVFormatContext *s) hdr.sample_rate = AV_RL32(buf + 8); hdr.stereo = AV_RL16(buf + 12); + if (hdr.sample_rate <= 0) + return AVERROR_INVALIDDATA; + par = st->codecpar; par->codec_type = AVMEDIA_TYPE_AUDIO; par->codec_id = AV_CODEC_ID_ADPCM_IMA_SSI; @@ -115,9 +119,9 @@ static int kvag_seek(AVFormatContext *s, int stream_index, return avio_seek(s->pb, KVAG_HEADER_SIZE, SEEK_SET); } -const AVInputFormat ff_kvag_demuxer = { - .name = "kvag", - .long_name = NULL_IF_CONFIG_SMALL("Simon & Schuster Interactive VAG"), +const FFInputFormat ff_kvag_demuxer = { + .p.name = "kvag", + .p.long_name = NULL_IF_CONFIG_SMALL("Simon & Schuster Interactive VAG"), .read_probe = kvag_probe, .read_header = kvag_read_header, .read_packet = kvag_read_packet, @@ -128,20 +132,7 @@ const AVInputFormat ff_kvag_demuxer = { #if CONFIG_KVAG_MUXER static int kvag_write_init(AVFormatContext *s) { - AVCodecParameters *par; - - if (s->nb_streams != 1) { - av_log(s, AV_LOG_ERROR, "KVAG files have exactly one stream\n"); - return AVERROR(EINVAL); - } - - par = s->streams[0]->codecpar; - - if (par->codec_id != AV_CODEC_ID_ADPCM_IMA_SSI) { - av_log(s, AV_LOG_ERROR, "%s codec not supported\n", - avcodec_get_name(par->codec_id)); - return AVERROR(EINVAL); - } + AVCodecParameters *par = s->streams[0]->codecpar; if (par->ch_layout.nb_channels > 2) { av_log(s, AV_LOG_ERROR, "KVAG files only support up to 2 channels\n"); @@ -195,6 +186,9 @@ const FFOutputFormat ff_kvag_muxer = { .p.extensions = "vag", .p.audio_codec = AV_CODEC_ID_ADPCM_IMA_SSI, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .init = kvag_write_init, .write_header = kvag_write_header, .write_packet = ff_raw_write_packet, diff --git a/libavformat/lafdec.c b/libavformat/lafdec.c index b867f106aee..4ce4449c4eb 100644 --- a/libavformat/lafdec.c +++ b/libavformat/lafdec.c @@ -22,6 +22,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" #include "avio_internal.h" +#include "demux.h" #include "internal.h" #define MAX_STREAMS 4096 @@ -147,8 +148,8 @@ static int laf_read_header(AVFormatContext *ctx) if (!s->data) return AVERROR(ENOMEM); - for (int st = 0; st < st_count; st++) { - StreamParams *stp = &s->p[st]; + for (unsigned i = 0; i < st_count; i++) { + StreamParams *stp = &s->p[i]; AVCodecParameters *par; AVStream *st = avformat_new_stream(ctx, NULL); if (!st) @@ -277,16 +278,16 @@ static int laf_read_seek(AVFormatContext *ctx, int stream_index, return -1; } -const AVInputFormat ff_laf_demuxer = { - .name = "laf", - .long_name = NULL_IF_CONFIG_SMALL("LAF (Limitless Audio Format)"), +const FFInputFormat ff_laf_demuxer = { + .p.name = "laf", + .p.long_name = NULL_IF_CONFIG_SMALL("LAF (Limitless Audio Format)"), + .p.extensions = "laf", + .p.flags = AVFMT_GENERIC_INDEX, .priv_data_size = sizeof(LAFContext), .read_probe = laf_probe, .read_header = laf_read_header, .read_packet = laf_read_packet, .read_close = laf_read_close, .read_seek = laf_read_seek, - .extensions = "laf", - .flags = AVFMT_GENERIC_INDEX, - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, }; diff --git a/libavformat/latmenc.c b/libavformat/latmenc.c index 8eec632c546..05004048aa5 100644 --- a/libavformat/latmenc.c +++ b/libavformat/latmenc.c @@ -268,6 +268,8 @@ const FFOutputFormat ff_latm_muxer = { .priv_data_size = sizeof(LATMContext), .p.audio_codec = AV_CODEC_ID_AAC, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH, .write_header = latm_write_header, .write_packet = latm_write_packet, .p.priv_class = &latm_muxer_class, diff --git a/libavformat/libamqp.c b/libavformat/libamqp.c index c3b9c484ea1..27851e1b289 100644 --- a/libavformat/libamqp.c +++ b/libavformat/libamqp.c @@ -53,9 +53,9 @@ static const AVOption options[] = { { "exchange", "Exchange to send/read packets", OFFSET(exchange), AV_OPT_TYPE_STRING, { .str = "amq.direct" }, 0, 0, .flags = D | E }, { "routing_key", "Key to filter streams", OFFSET(routing_key), AV_OPT_TYPE_STRING, { .str = "amqp" }, 0, 0, .flags = D | E }, { "connection_timeout", "Initial connection timeout", OFFSET(connection_timeout), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT64_MAX, .flags = D | E}, - { "delivery_mode", "Delivery mode", OFFSET(delivery_mode), AV_OPT_TYPE_INT, { .i64 = AMQP_DELIVERY_PERSISTENT }, 1, 2, .flags = E, "delivery_mode"}, - { "persistent", "Persistent delivery mode", 0, AV_OPT_TYPE_CONST, { .i64 = AMQP_DELIVERY_PERSISTENT }, 0, 0, E, "delivery_mode" }, - { "non-persistent", "Non-persistent delivery mode", 0, AV_OPT_TYPE_CONST, { .i64 = AMQP_DELIVERY_NONPERSISTENT }, 0, 0, E, "delivery_mode" }, + { "delivery_mode", "Delivery mode", OFFSET(delivery_mode), AV_OPT_TYPE_INT, { .i64 = AMQP_DELIVERY_PERSISTENT }, 1, 2, .flags = E, .unit = "delivery_mode"}, + { "persistent", "Persistent delivery mode", 0, AV_OPT_TYPE_CONST, { .i64 = AMQP_DELIVERY_PERSISTENT }, 0, 0, E, .unit = "delivery_mode" }, + { "non-persistent", "Non-persistent delivery mode", 0, AV_OPT_TYPE_CONST, { .i64 = AMQP_DELIVERY_NONPERSISTENT }, 0, 0, E, .unit = "delivery_mode" }, { NULL } }; diff --git a/libavformat/libgme.c b/libavformat/libgme.c index 695155c9ac6..26d079c270d 100644 --- a/libavformat/libgme.c +++ b/libavformat/libgme.c @@ -26,6 +26,7 @@ #include "libavutil/eval.h" #include "libavutil/opt.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct GMEContext { @@ -193,15 +194,15 @@ static const AVClass class_gme = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_libgme_demuxer = { - .name = "libgme", - .long_name = NULL_IF_CONFIG_SMALL("Game Music Emu demuxer"), +const FFInputFormat ff_libgme_demuxer = { + .p.name = "libgme", + .p.long_name = NULL_IF_CONFIG_SMALL("Game Music Emu demuxer"), + .p.priv_class = &class_gme, .priv_data_size = sizeof(GMEContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = probe_gme, .read_header = read_header_gme, .read_packet = read_packet_gme, .read_close = read_close_gme, .read_seek = read_seek_gme, - .priv_class = &class_gme, }; diff --git a/libavformat/libmodplug.c b/libavformat/libmodplug.c index 0cae13bd2ec..8c6569f7784 100644 --- a/libavformat/libmodplug.c +++ b/libavformat/libmodplug.c @@ -28,6 +28,7 @@ #include "libavutil/eval.h" #include "libavutil/opt.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct ModPlugContext { @@ -380,15 +381,15 @@ static const AVClass modplug_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_libmodplug_demuxer = { - .name = "libmodplug", - .long_name = NULL_IF_CONFIG_SMALL("ModPlug demuxer"), +const FFInputFormat ff_libmodplug_demuxer = { + .p.name = "libmodplug", + .p.long_name = NULL_IF_CONFIG_SMALL("ModPlug demuxer"), + .p.extensions = modplug_extensions, + .p.priv_class = &modplug_class, .priv_data_size = sizeof(ModPlugContext), .read_probe = modplug_probe, .read_header = modplug_read_header, .read_packet = modplug_read_packet, .read_close = modplug_read_close, .read_seek = modplug_read_seek, - .extensions = modplug_extensions, - .priv_class = &modplug_class, }; diff --git a/libavformat/libopenmpt.c b/libavformat/libopenmpt.c index 3ca59f506fb..c270a60cb2c 100644 --- a/libavformat/libopenmpt.c +++ b/libavformat/libopenmpt.c @@ -34,6 +34,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/opt.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct OpenMPTContext { @@ -53,9 +54,9 @@ typedef struct OpenMPTContext { static const AVOption options[] = { { "sample_rate", "set sample rate", OFFSET(sample_rate), AV_OPT_TYPE_INT, { .i64 = 48000 }, 1000, INT_MAX, A | D }, { "layout", "set channel layout", OFFSET(ch_layout), AV_OPT_TYPE_CHLAYOUT, { .str = "stereo" }, 0, 0, A | D }, - { "subsong", "set subsong", OFFSET(subsong), AV_OPT_TYPE_INT, { .i64 = -2 }, -2, INT_MAX, A | D, "subsong"}, - { "all", "all", 0, AV_OPT_TYPE_CONST, { .i64 = -1}, 0, 0, A | D, "subsong" }, - { "auto", "auto", 0, AV_OPT_TYPE_CONST, { .i64 = -2}, 0, 0, A | D, "subsong" }, + { "subsong", "set subsong", OFFSET(subsong), AV_OPT_TYPE_INT, { .i64 = -2 }, -2, INT_MAX, A | D, .unit = "subsong"}, + { "all", "all", 0, AV_OPT_TYPE_CONST, { .i64 = -1}, 0, 0, A | D, .unit = "subsong" }, + { "auto", "auto", 0, AV_OPT_TYPE_CONST, { .i64 = -2}, 0, 0, A | D, .unit = "subsong" }, { NULL } }; @@ -278,20 +279,20 @@ static const AVClass class_openmpt = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_libopenmpt_demuxer = { - .name = "libopenmpt", - .long_name = NULL_IF_CONFIG_SMALL("Tracker formats (libopenmpt)"), +const FFInputFormat ff_libopenmpt_demuxer = { + .p.name = "libopenmpt", + .p.long_name = NULL_IF_CONFIG_SMALL("Tracker formats (libopenmpt)"), + .p.priv_class = &class_openmpt, +#if OPENMPT_API_VERSION_AT_LEAST(0,3,0) + .p.extensions = "669,amf,ams,dbm,digi,dmf,dsm,dtm,far,gdm,ice,imf,it,j2b,m15,mdl,med,mmcmp,mms,mo3,mod,mptm,mt2,mtm,nst,okt,plm,ppm,psm,pt36,ptm,s3m,sfx,sfx2,st26,stk,stm,stp,ult,umx,wow,xm,xpk", +#else + .p.extensions = "669,amf,ams,dbm,digi,dmf,dsm,far,gdm,ice,imf,it,j2b,m15,mdl,med,mmcmp,mms,mo3,mod,mptm,mt2,mtm,nst,okt,plm,ppm,psm,pt36,ptm,s3m,sfx,sfx2,st26,stk,stm,ult,umx,wow,xm,xpk", +#endif .priv_data_size = sizeof(OpenMPTContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = read_probe_openmpt, .read_header = read_header_openmpt, .read_packet = read_packet_openmpt, .read_close = read_close_openmpt, .read_seek = read_seek_openmpt, - .priv_class = &class_openmpt, -#if OPENMPT_API_VERSION_AT_LEAST(0,3,0) - .extensions = "669,amf,ams,dbm,digi,dmf,dsm,dtm,far,gdm,ice,imf,it,j2b,m15,mdl,med,mmcmp,mms,mo3,mod,mptm,mt2,mtm,nst,okt,plm,ppm,psm,pt36,ptm,s3m,sfx,sfx2,st26,stk,stm,stp,ult,umx,wow,xm,xpk", -#else - .extensions = "669,amf,ams,dbm,digi,dmf,dsm,far,gdm,ice,imf,it,j2b,m15,mdl,med,mmcmp,mms,mo3,mod,mptm,mt2,mtm,nst,okt,plm,ppm,psm,pt36,ptm,s3m,sfx,sfx2,st26,stk,stm,ult,umx,wow,xm,xpk", -#endif }; diff --git a/libavformat/librist.c b/libavformat/librist.c index 5f27cacd441..9669d5b5df5 100644 --- a/libavformat/librist.c +++ b/libavformat/librist.c @@ -63,10 +63,10 @@ typedef struct RISTContext { #define E AV_OPT_FLAG_ENCODING_PARAM #define OFFSET(x) offsetof(RISTContext, x) static const AVOption librist_options[] = { - { "rist_profile","set profile", OFFSET(profile), AV_OPT_TYPE_INT, {.i64=RIST_PROFILE_MAIN}, 0, 2, .flags = D|E, "profile" }, - { "simple", NULL, 0, AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_SIMPLE}, 0, 0, .flags = D|E, "profile" }, - { "main", NULL, 0, AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_MAIN}, 0, 0, .flags = D|E, "profile" }, - { "advanced", NULL, 0, AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_ADVANCED}, 0, 0, .flags = D|E, "profile" }, + { "rist_profile","set profile", OFFSET(profile), AV_OPT_TYPE_INT, {.i64=RIST_PROFILE_MAIN}, 0, 2, .flags = D|E, .unit = "profile" }, + { "simple", NULL, 0, AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_SIMPLE}, 0, 0, .flags = D|E, .unit = "profile" }, + { "main", NULL, 0, AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_MAIN}, 0, 0, .flags = D|E, .unit = "profile" }, + { "advanced", NULL, 0, AV_OPT_TYPE_CONST, {.i64=RIST_PROFILE_ADVANCED}, 0, 0, .flags = D|E, .unit = "profile" }, { "buffer_size", "set buffer_size in ms", OFFSET(buffer_size), AV_OPT_TYPE_INT, {.i64=0}, 0, 30000, .flags = D|E }, { "fifo_size", "set fifo buffer size, must be a power of 2", OFFSET(fifo_size), AV_OPT_TYPE_INT, {.i64=FIFO_SIZE_DEFAULT}, 32, 262144, .flags = D|E }, { "overrun_nonfatal", "survive in case of receiving fifo buffer overrun", OFFSET(overrun_nonfatal), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, D }, diff --git a/libavformat/librtmp.c b/libavformat/librtmp.c index b7e9fc81cf8..bdd82ce15f6 100644 --- a/libavformat/librtmp.c +++ b/libavformat/librtmp.c @@ -220,8 +220,9 @@ static int rtmp_read(URLContext *s, uint8_t *buf, int size) return ret; } -static int rtmp_read_pause(URLContext *s, int pause) +static int rtmp_read_pause(void *opaque, int pause) { + URLContext *s = opaque; LibRTMPContext *ctx = s->priv_data; RTMP *r = &ctx->rtmp; @@ -230,9 +231,10 @@ static int rtmp_read_pause(URLContext *s, int pause) return 0; } -static int64_t rtmp_read_seek(URLContext *s, int stream_index, +static int64_t rtmp_read_seek(void *opaque, int stream_index, int64_t timestamp, int flags) { + URLContext *s = opaque; LibRTMPContext *ctx = s->priv_data; RTMP *r = &ctx->rtmp; @@ -265,10 +267,10 @@ static const AVOption options[] = { {"rtmp_buffer", "Set buffer time in milliseconds. The default is 3000.", OFFSET(client_buffer_time), AV_OPT_TYPE_STRING, {.str = "3000"}, 0, 0, DEC|ENC}, {"rtmp_conn", "Append arbitrary AMF data to the Connect message", OFFSET(conn), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, {"rtmp_flashver", "Version of the Flash plugin used to run the SWF player.", OFFSET(flashver), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, - {"rtmp_live", "Specify that the media is a live stream.", OFFSET(live), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC, "rtmp_live"}, - {"any", "both", 0, AV_OPT_TYPE_CONST, {.i64 = -2}, 0, 0, DEC, "rtmp_live"}, - {"live", "live stream", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, DEC, "rtmp_live"}, - {"recorded", "recorded stream", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, DEC, "rtmp_live"}, + {"rtmp_live", "Specify that the media is a live stream.", OFFSET(live), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC, .unit = "rtmp_live"}, + {"any", "both", 0, AV_OPT_TYPE_CONST, {.i64 = -2}, 0, 0, DEC, .unit = "rtmp_live"}, + {"live", "live stream", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, DEC, .unit = "rtmp_live"}, + {"recorded", "recorded stream", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, DEC, .unit = "rtmp_live"}, {"rtmp_pageurl", "URL of the web page in which the media was embedded. By default no value will be sent.", OFFSET(pageurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC}, {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC}, {"rtmp_subscribe", "Name of live stream to subscribe to. Defaults to rtmp_playpath.", OFFSET(subscribe), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC}, diff --git a/libavformat/libsmbclient.c b/libavformat/libsmbclient.c index 32858689574..f81fff2dfc3 100644 --- a/libavformat/libsmbclient.c +++ b/libavformat/libsmbclient.c @@ -21,8 +21,6 @@ #include #include "libavutil/avstring.h" #include "libavutil/opt.h" -#include "avformat.h" -#include "internal.h" #include "url.h" typedef struct { diff --git a/libavformat/libsrt.c b/libavformat/libsrt.c index cd8f5b1e7db..a7aafea5364 100644 --- a/libavformat/libsrt.c +++ b/libavformat/libsrt.c @@ -32,6 +32,7 @@ #include "network.h" #include "os_support.h" #include "url.h" +#include "urldecode.h" /* This is for MPEG-TS and it's a default SRTO_PAYLOADSIZE for SRTT_LIVE (8 TS packets) */ #ifndef SRT_LIVE_DEFAULT_PAYLOAD_SIZE @@ -101,10 +102,10 @@ static const AVOption libsrt_options[] = { { "listen_timeout", "Connection awaiting timeout (in microseconds)" , OFFSET(listen_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, { "send_buffer_size", "Socket send buffer size (in bytes)", OFFSET(send_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, { "recv_buffer_size", "Socket receive buffer size (in bytes)", OFFSET(recv_buffer_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, - { "pkt_size", "Maximum SRT packet size", OFFSET(payload_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, SRT_LIVE_MAX_PAYLOAD_SIZE, .flags = D|E, "payload_size" }, - { "payload_size", "Maximum SRT packet size", OFFSET(payload_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, SRT_LIVE_MAX_PAYLOAD_SIZE, .flags = D|E, "payload_size" }, - { "ts_size", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRT_LIVE_DEFAULT_PAYLOAD_SIZE }, INT_MIN, INT_MAX, .flags = D|E, "payload_size" }, - { "max_size", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRT_LIVE_MAX_PAYLOAD_SIZE }, INT_MIN, INT_MAX, .flags = D|E, "payload_size" }, + { "pkt_size", "Maximum SRT packet size", OFFSET(payload_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, SRT_LIVE_MAX_PAYLOAD_SIZE, .flags = D|E, .unit = "payload_size" }, + { "payload_size", "Maximum SRT packet size", OFFSET(payload_size), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, SRT_LIVE_MAX_PAYLOAD_SIZE, .flags = D|E, .unit = "payload_size" }, + { "ts_size", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRT_LIVE_DEFAULT_PAYLOAD_SIZE }, INT_MIN, INT_MAX, .flags = D|E, .unit = "payload_size" }, + { "max_size", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRT_LIVE_MAX_PAYLOAD_SIZE }, INT_MIN, INT_MAX, .flags = D|E, .unit = "payload_size" }, { "maxbw", "Maximum bandwidth (bytes per second) that the connection can use", OFFSET(maxbw), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, { "pbkeylen", "Crypto key len in bytes {16,24,32} Default: 16 (128-bit)", OFFSET(pbkeylen), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 32, .flags = D|E }, { "passphrase", "Crypto PBKDF2 Passphrase size[0,10..64] 0:disable crypto", OFFSET(passphrase), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E }, @@ -127,10 +128,10 @@ static const AVOption libsrt_options[] = { { "tlpktdrop", "Enable too-late pkt drop", OFFSET(tlpktdrop), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, .flags = D|E }, { "nakreport", "Enable receiver to send periodic NAK reports", OFFSET(nakreport), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, .flags = D|E }, { "connect_timeout", "Connect timeout(in milliseconds). Caller default: 3000, rendezvous (x 10)", OFFSET(connect_timeout), AV_OPT_TYPE_INT64, { .i64 = -1 }, -1, INT64_MAX, .flags = D|E }, - { "mode", "Connection mode (caller, listener, rendezvous)", OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = SRT_MODE_CALLER }, SRT_MODE_CALLER, SRT_MODE_RENDEZVOUS, .flags = D|E, "mode" }, - { "caller", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRT_MODE_CALLER }, INT_MIN, INT_MAX, .flags = D|E, "mode" }, - { "listener", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRT_MODE_LISTENER }, INT_MIN, INT_MAX, .flags = D|E, "mode" }, - { "rendezvous", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRT_MODE_RENDEZVOUS }, INT_MIN, INT_MAX, .flags = D|E, "mode" }, + { "mode", "Connection mode (caller, listener, rendezvous)", OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = SRT_MODE_CALLER }, SRT_MODE_CALLER, SRT_MODE_RENDEZVOUS, .flags = D|E, .unit = "mode" }, + { "caller", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRT_MODE_CALLER }, INT_MIN, INT_MAX, .flags = D|E, .unit = "mode" }, + { "listener", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRT_MODE_LISTENER }, INT_MIN, INT_MAX, .flags = D|E, .unit = "mode" }, + { "rendezvous", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRT_MODE_RENDEZVOUS }, INT_MIN, INT_MAX, .flags = D|E, .unit = "mode" }, { "sndbuf", "Send buffer size (in bytes)", OFFSET(sndbuf), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, { "rcvbuf", "Receive buffer size (in bytes)", OFFSET(rcvbuf), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, { "lossmaxttl", "Maximum possible packet reorder tolerance", OFFSET(lossmaxttl), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, @@ -139,9 +140,9 @@ static const AVOption libsrt_options[] = { { "srt_streamid", "A string of up to 512 characters that an Initiator can pass to a Responder", OFFSET(streamid), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E }, { "smoother", "The type of Smoother used for the transmission for that socket", OFFSET(smoother), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = D|E }, { "messageapi", "Enable message API", OFFSET(messageapi), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, .flags = D|E }, - { "transtype", "The transmission type for the socket", OFFSET(transtype), AV_OPT_TYPE_INT, { .i64 = SRTT_INVALID }, SRTT_LIVE, SRTT_INVALID, .flags = D|E, "transtype" }, - { "live", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRTT_LIVE }, INT_MIN, INT_MAX, .flags = D|E, "transtype" }, - { "file", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRTT_FILE }, INT_MIN, INT_MAX, .flags = D|E, "transtype" }, + { "transtype", "The transmission type for the socket", OFFSET(transtype), AV_OPT_TYPE_INT, { .i64 = SRTT_INVALID }, SRTT_LIVE, SRTT_INVALID, .flags = D|E, .unit = "transtype" }, + { "live", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRTT_LIVE }, INT_MIN, INT_MAX, .flags = D|E, .unit = "transtype" }, + { "file", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = SRTT_FILE }, INT_MIN, INT_MAX, .flags = D|E, .unit = "transtype" }, { "linger", "Number of seconds that the socket waits for unsent data when closing", OFFSET(linger), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, .flags = D|E }, { "tsbpd", "Timestamp-based packet delivery", OFFSET(tsbpd), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, .flags = D|E }, { NULL } @@ -249,7 +250,7 @@ static int libsrt_listen(int eid, int fd, const struct sockaddr *addr, socklen_t if (srt_listen(fd, 1)) return libsrt_neterrno(h); - ret = libsrt_network_wait_fd_timeout(h, eid, 1, timeout, &h->interrupt_callback); + ret = libsrt_network_wait_fd_timeout(h, eid, 0, timeout, &h->interrupt_callback); if (ret < 0) return ret; @@ -390,7 +391,7 @@ static int libsrt_setup(URLContext *h, const char *uri, int flags) char hostname[1024],proto[1024],path[1024]; char portstr[10]; int64_t open_timeout = 0; - int eid, write_eid; + int eid; av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port, path, sizeof(path), uri); @@ -454,18 +455,21 @@ static int libsrt_setup(URLContext *h, const char *uri, int flags) if (libsrt_socket_nonblock(fd, 1) < 0) av_log(h, AV_LOG_DEBUG, "libsrt_socket_nonblock failed\n"); - ret = write_eid = libsrt_epoll_create(h, fd, 1); - if (ret < 0) - goto fail1; if (s->mode == SRT_MODE_LISTENER) { + int read_eid = ret = libsrt_epoll_create(h, fd, 0); + if (ret < 0) + goto fail1; // multi-client - ret = libsrt_listen(write_eid, fd, cur_ai->ai_addr, cur_ai->ai_addrlen, h, s->listen_timeout); - srt_epoll_release(write_eid); + ret = libsrt_listen(read_eid, fd, cur_ai->ai_addr, cur_ai->ai_addrlen, h, s->listen_timeout); + srt_epoll_release(read_eid); if (ret < 0) goto fail1; srt_close(fd); fd = ret; } else { + int write_eid = ret = libsrt_epoll_create(h, fd, 1); + if (ret < 0) + goto fail1; if (s->mode == SRT_MODE_RENDEZVOUS) { if (srt_bind(fd, cur_ai->ai_addr, cur_ai->ai_addrlen)) { ret = libsrt_neterrno(h); @@ -547,7 +551,11 @@ static int libsrt_open(URLContext *h, const char *uri, int flags) } if (av_find_info_tag(buf, sizeof(buf), "passphrase", p)) { av_freep(&s->passphrase); - s->passphrase = av_strndup(buf, strlen(buf)); + s->passphrase = ff_urldecode(buf, 1); + if (!s->passphrase) { + ret = AVERROR(ENOMEM); + goto err; + } } #if SRT_VERSION_VALUE >= 0x010302 if (av_find_info_tag(buf, sizeof(buf), "enforced_encryption", p)) { @@ -632,7 +640,7 @@ static int libsrt_open(URLContext *h, const char *uri, int flags) } if (av_find_info_tag(buf, sizeof(buf), "streamid", p)) { av_freep(&s->streamid); - s->streamid = av_strdup(buf); + s->streamid = ff_urldecode(buf, 1); if (!s->streamid) { ret = AVERROR(ENOMEM); goto err; @@ -640,7 +648,7 @@ static int libsrt_open(URLContext *h, const char *uri, int flags) } if (av_find_info_tag(buf, sizeof(buf), "smoother", p)) { av_freep(&s->smoother); - s->smoother = av_strdup(buf); + s->smoother = ff_urldecode(buf, 1); if(!s->smoother) { ret = AVERROR(ENOMEM); goto err; @@ -669,8 +677,6 @@ static int libsrt_open(URLContext *h, const char *uri, int flags) return 0; err: - av_freep(&s->smoother); - av_freep(&s->streamid); srt_cleanup(); return ret; } diff --git a/libavformat/libssh.c b/libavformat/libssh.c index 21474f0f0a5..127faaabd36 100644 --- a/libavformat/libssh.c +++ b/libavformat/libssh.c @@ -84,12 +84,9 @@ static av_cold int libssh_authentication(LIBSSHContext *libssh, const char *user if (auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { if (libssh->priv_key) { - ssh_string pub_key; - ssh_private_key priv_key; - int type; - if (!ssh_try_publickey_from_file(libssh->session, libssh->priv_key, &pub_key, &type)) { - priv_key = privatekey_from_file(libssh->session, libssh->priv_key, type, password); - if (ssh_userauth_pubkey(libssh->session, NULL, pub_key, priv_key) == SSH_AUTH_SUCCESS) { + ssh_key priv_key; + if (ssh_pki_import_privkey_file(libssh->priv_key, password, NULL, NULL, &priv_key) == SSH_OK) { + if (ssh_userauth_publickey(libssh->session, NULL, priv_key) == SSH_AUTH_SUCCESS) { av_log(libssh, AV_LOG_DEBUG, "Authentication successful with selected private key.\n"); authorized = 1; } @@ -97,7 +94,7 @@ static av_cold int libssh_authentication(LIBSSHContext *libssh, const char *user av_log(libssh, AV_LOG_DEBUG, "Invalid key is provided.\n"); return AVERROR(EACCES); } - } else if (ssh_userauth_autopubkey(libssh->session, password) == SSH_AUTH_SUCCESS) { + } else if (ssh_userauth_publickey_auto(libssh->session, NULL, password) == SSH_AUTH_SUCCESS) { av_log(libssh, AV_LOG_DEBUG, "Authentication successful with auto selected key.\n"); authorized = 1; } diff --git a/libavformat/libzmq.c b/libavformat/libzmq.c index 04c72ac601c..f4bb849e46b 100644 --- a/libavformat/libzmq.c +++ b/libavformat/libzmq.c @@ -94,7 +94,10 @@ static int zmq_proto_open(URLContext *h, const char *uri, int flags) return AVERROR_EXTERNAL; } - av_strstart(uri, "zmq:", &uri); + if (av_strstart(uri, "zmq:", &uri)) { + av_log(h, AV_LOG_ERROR, "URL %s lacks prefix\n", uri); + return AVERROR(EINVAL); + } /*publish during write*/ if (h->flags & AVIO_FLAG_WRITE) { diff --git a/libavformat/lmlm4.c b/libavformat/lmlm4.c index b0bfad001b4..209b544ccd8 100644 --- a/libavformat/lmlm4.c +++ b/libavformat/lmlm4.c @@ -25,6 +25,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define LMLM4_I_FRAME 0x00 @@ -121,9 +122,9 @@ static int lmlm4_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_lmlm4_demuxer = { - .name = "lmlm4", - .long_name = NULL_IF_CONFIG_SMALL("raw lmlm4"), +const FFInputFormat ff_lmlm4_demuxer = { + .p.name = "lmlm4", + .p.long_name = NULL_IF_CONFIG_SMALL("raw lmlm4"), .read_probe = lmlm4_probe, .read_header = lmlm4_read_header, .read_packet = lmlm4_read_packet, diff --git a/libavformat/loasdec.c b/libavformat/loasdec.c index e739b6c196e..fcb812f5caf 100644 --- a/libavformat/loasdec.c +++ b/libavformat/loasdec.c @@ -22,6 +22,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/internal.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "rawdec.h" @@ -74,7 +75,7 @@ static int loas_read_header(AVFormatContext *s) return AVERROR(ENOMEM); st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - st->codecpar->codec_id = s->iformat->raw_codec_id; + st->codecpar->codec_id = AV_CODEC_ID_AAC_LATM; ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; //LCM of all possible AAC sample rates @@ -83,14 +84,14 @@ static int loas_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_loas_demuxer = { - .name = "loas", - .long_name = NULL_IF_CONFIG_SMALL("LOAS AudioSyncStream"), +const FFInputFormat ff_loas_demuxer = { + .p.name = "loas", + .p.long_name = NULL_IF_CONFIG_SMALL("LOAS AudioSyncStream"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.priv_class = &ff_raw_demuxer_class, .read_probe = loas_probe, .read_header = loas_read_header, .read_packet = ff_raw_read_partial_packet, - .flags= AVFMT_GENERIC_INDEX, .raw_codec_id = AV_CODEC_ID_AAC_LATM, .priv_data_size = sizeof(FFRawDemuxerContext), - .priv_class = &ff_raw_demuxer_class, }; diff --git a/libavformat/lrcdec.c b/libavformat/lrcdec.c index fff39495f87..5435a65b151 100644 --- a/libavformat/lrcdec.c +++ b/libavformat/lrcdec.c @@ -24,6 +24,7 @@ #include #include "avformat.h" +#include "demux.h" #include "internal.h" #include "lrc.h" #include "metadata.h" @@ -170,8 +171,11 @@ static int lrc_read_header(AVFormatContext *s) av_bprint_init(&line, 0, AV_BPRINT_SIZE_UNLIMITED); while(!avio_feof(s->pb)) { - int64_t pos = read_line(&line, s->pb); - int64_t header_offset = find_header(line.str); + int64_t header_offset, pos = read_line(&line, s->pb); + + if (!av_bprint_is_complete(&line)) + goto err_nomem_out; + header_offset = find_header(line.str); if(header_offset >= 0) { char *comma_offset = strchr(line.str, ':'); if(comma_offset) { @@ -205,7 +209,7 @@ static int lrc_read_header(AVFormatContext *s) sub = ff_subtitles_queue_insert(&lrc->q, line.str + ts_strlength, line.len - ts_strlength, 0); if (!sub) - return AVERROR(ENOMEM); + goto err_nomem_out; sub->pos = pos; sub->pts = ts_start - lrc->ts_offset; sub->duration = -1; @@ -216,13 +220,16 @@ static int lrc_read_header(AVFormatContext *s) ff_metadata_conv_ctx(s, NULL, ff_lrc_metadata_conv); av_bprint_finalize(&line, NULL); return 0; +err_nomem_out: + av_bprint_finalize(&line, NULL); + return AVERROR(ENOMEM); } -const AVInputFormat ff_lrc_demuxer = { - .name = "lrc", - .long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"), +const FFInputFormat ff_lrc_demuxer = { + .p.name = "lrc", + .p.long_name = NULL_IF_CONFIG_SMALL("LRC lyrics"), .priv_data_size = sizeof (LRCContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = lrc_probe, .read_header = lrc_read_header, .read_packet = ff_subtitles_read_packet, diff --git a/libavformat/lrcenc.c b/libavformat/lrcenc.c index d66be9a8faa..15c31d33b3c 100644 --- a/libavformat/lrcenc.c +++ b/libavformat/lrcenc.c @@ -37,12 +37,6 @@ static int lrc_write_header(AVFormatContext *s) { const AVDictionaryEntry *metadata_item; - if(s->nb_streams != 1 || - s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) { - av_log(s, AV_LOG_ERROR, - "LRC supports only a single subtitle stream.\n"); - return AVERROR(EINVAL); - } if(s->streams[0]->codecpar->codec_id != AV_CODEC_ID_SUBRIP && s->streams[0]->codecpar->codec_id != AV_CODEC_ID_TEXT) { av_log(s, AV_LOG_ERROR, "Unsupported subtitle codec: %s\n", @@ -131,7 +125,10 @@ const FFOutputFormat ff_lrc_muxer = { .p.extensions = "lrc", .p.flags = AVFMT_VARIABLE_FPS | AVFMT_GLOBALHEADER | AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT, + .p.video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_CODEC_ID_NONE, .p.subtitle_codec = AV_CODEC_ID_SUBRIP, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH, .priv_data_size = 0, .write_header = lrc_write_header, .write_packet = lrc_write_packet, diff --git a/libavformat/luodatdec.c b/libavformat/luodatdec.c index fbd621dae66..c166ad71f07 100644 --- a/libavformat/luodatdec.c +++ b/libavformat/luodatdec.c @@ -23,6 +23,7 @@ #include "libavutil/intreadwrite.h" #include "avio_internal.h" #include "avformat.h" +#include "demux.h" #include "internal.h" static int dat_probe(const AVProbeData *p) @@ -115,12 +116,12 @@ static int dat_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_luodat_demuxer = { - .name = "luodat", - .long_name = NULL_IF_CONFIG_SMALL("Video CCTV DAT"), +const FFInputFormat ff_luodat_demuxer = { + .p.name = "luodat", + .p.long_name = NULL_IF_CONFIG_SMALL("Video CCTV DAT"), + .p.extensions = "dat", + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = dat_probe, .read_header = dat_read_header, .read_packet = dat_read_packet, - .extensions = "dat", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/lvfdec.c b/libavformat/lvfdec.c index cc1e9be58c6..cd013844fb6 100644 --- a/libavformat/lvfdec.c +++ b/libavformat/lvfdec.c @@ -21,6 +21,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "riff.h" static int lvf_probe(const AVProbeData *p) @@ -145,12 +146,12 @@ static int lvf_read_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; } -const AVInputFormat ff_lvf_demuxer = { - .name = "lvf", - .long_name = NULL_IF_CONFIG_SMALL("LVF"), +const FFInputFormat ff_lvf_demuxer = { + .p.name = "lvf", + .p.long_name = NULL_IF_CONFIG_SMALL("LVF"), + .p.extensions = "lvf", + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = lvf_probe, .read_header = lvf_read_header, .read_packet = lvf_read_packet, - .extensions = "lvf", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/lxfdec.c b/libavformat/lxfdec.c index 8003ae98b70..00a7813984c 100644 --- a/libavformat/lxfdec.c +++ b/libavformat/lxfdec.c @@ -335,12 +335,12 @@ static int lxf_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_lxf_demuxer = { - .name = "lxf", - .long_name = NULL_IF_CONFIG_SMALL("VR native stream (LXF)"), +const FFInputFormat ff_lxf_demuxer = { + .p.name = "lxf", + .p.long_name = NULL_IF_CONFIG_SMALL("VR native stream (LXF)"), + .p.codec_tag = (const AVCodecTag* const []){lxf_tags, 0}, .priv_data_size = sizeof(LXFDemuxContext), .read_probe = lxf_probe, .read_header = lxf_read_header, .read_packet = lxf_read_packet, - .codec_tag = (const AVCodecTag* const []){lxf_tags, 0}, }; diff --git a/libavformat/matroska.c b/libavformat/matroska.c index 90d94b65bf7..d0ecfbeb6a8 100644 --- a/libavformat/matroska.c +++ b/libavformat/matroska.c @@ -19,8 +19,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/stereo3d.h" - #include "matroska.h" /* If you add a tag here that is not in ff_codec_bmp_tags[] @@ -30,6 +28,7 @@ const CodecTags ff_mkv_codec_tags[]={ {"A_AAC" , AV_CODEC_ID_AAC}, {"A_AC3" , AV_CODEC_ID_AC3}, {"A_ALAC" , AV_CODEC_ID_ALAC}, + {"A_ATRAC/AT1" , AV_CODEC_ID_ATRAC1}, {"A_DTS" , AV_CODEC_ID_DTS}, {"A_EAC3" , AV_CODEC_ID_EAC3}, {"A_FLAC" , AV_CODEC_ID_FLAC}, @@ -76,6 +75,7 @@ const CodecTags ff_mkv_codec_tags[]={ {"S_DVBSUB" , AV_CODEC_ID_DVB_SUBTITLE}, {"S_HDMV/PGS" , AV_CODEC_ID_HDMV_PGS_SUBTITLE}, {"S_HDMV/TEXTST" , AV_CODEC_ID_HDMV_TEXT_SUBTITLE}, + {"S_ARIBSUB" , AV_CODEC_ID_ARIB_CAPTION}, {"V_AV1" , AV_CODEC_ID_AV1}, {"V_AVS2" , AV_CODEC_ID_AVS2}, @@ -144,65 +144,3 @@ const char * const ff_matroska_video_stereo_mode[MATROSKA_VIDEO_STEREOMODE_TYPE_ "block_lr", "block_rl", }; - -const char * const ff_matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_PLANE_COUNT] = { - "left", - "right", - "background", -}; - -int ff_mkv_stereo3d_conv(AVStream *st, MatroskaVideoStereoModeType stereo_mode) -{ - AVStereo3D *stereo; - int ret; - - stereo = av_stereo3d_alloc(); - if (!stereo) - return AVERROR(ENOMEM); - - // note: the missing breaks are intentional - switch (stereo_mode) { - case MATROSKA_VIDEO_STEREOMODE_TYPE_MONO: - stereo->type = AV_STEREO3D_2D; - break; - case MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT: - stereo->flags |= AV_STEREO3D_FLAG_INVERT; - case MATROSKA_VIDEO_STEREOMODE_TYPE_LEFT_RIGHT: - stereo->type = AV_STEREO3D_SIDEBYSIDE; - break; - case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTTOM_TOP: - stereo->flags |= AV_STEREO3D_FLAG_INVERT; - case MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM: - stereo->type = AV_STEREO3D_TOPBOTTOM; - break; - case MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_RL: - stereo->flags |= AV_STEREO3D_FLAG_INVERT; - case MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_LR: - stereo->type = AV_STEREO3D_CHECKERBOARD; - break; - case MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_RL: - stereo->flags |= AV_STEREO3D_FLAG_INVERT; - case MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_LR: - stereo->type = AV_STEREO3D_LINES; - break; - case MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_RL: - stereo->flags |= AV_STEREO3D_FLAG_INVERT; - case MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_LR: - stereo->type = AV_STEREO3D_COLUMNS; - break; - case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_RL: - stereo->flags |= AV_STEREO3D_FLAG_INVERT; - case MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_LR: - stereo->type = AV_STEREO3D_FRAMESEQUENCE; - break; - } - - ret = av_stream_add_side_data(st, AV_PKT_DATA_STEREO3D, (uint8_t *)stereo, - sizeof(*stereo)); - if (ret < 0) { - av_freep(&stereo); - return ret; - } - - return 0; -} diff --git a/libavformat/matroska.h b/libavformat/matroska.h index 45077ed33f9..719f2ef796d 100644 --- a/libavformat/matroska.h +++ b/libavformat/matroska.h @@ -23,7 +23,6 @@ #define AVFORMAT_MATROSKA_H #include "libavcodec/codec_id.h" -#include "avformat.h" #include "metadata.h" /* EBML version supported */ @@ -358,6 +357,17 @@ typedef enum { MATROSKA_VIDEO_PROJECTION_TYPE_MESH = 3, } MatroskaVideoProjectionType; +typedef enum { + MATROSKA_BLOCK_ADD_ID_TYPE_DEFAULT = 0, + MATROSKA_BLOCK_ADD_ID_TYPE_OPAQUE = 1, + MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35 = 4, + MATROSKA_BLOCK_ADD_ID_TYPE_DVCC = 0x64766343, // MKBETAG('d','v','c','C') + MATROSKA_BLOCK_ADD_ID_TYPE_DVVC = 0x64767643, // MKBETAG('d','v','v','C') +} MatroskaBlockAddIDType; + +#define MATROSKA_BLOCK_ADD_ID_OPAQUE 1 +#define MATROSKA_BLOCK_ADD_ID_ITU_T_T35 4 + /* * Matroska Codec IDs, strings */ @@ -375,8 +385,44 @@ typedef struct CodecTags{ extern const CodecTags ff_mkv_codec_tags[]; extern const CodecTags ff_webm_codec_tags[]; extern const AVMetadataConv ff_mkv_metadata_conv[]; + +/* The following macro contains all the information about which + * MATROSKA_VIDEO_STEREOMODE_TYPE_* correspond to which AVStereo3D + * structures and also the relevant horizontal/vertical divisors + * as well as WebM compatibility. + * + * MAP and MKV_ONLY are macros to be provided by the user. + * MAP(MatroskaVideoStereoModeType, AVStereo3DType, AV_STEREO3D_FLAG_*, + * HALF_WIDTH, HALF_HEIGHT, WebM-compatibility) + * is for the stereo modes that have a Stereo3D counterpart. + * MKV_ONLY(MatroskaVideoStereoModeType, HALF_WIDTH, HALF_HEIGHT, WebM) + * is for those that don't have a Stereo3D counterpart. + * */ +#define STEREOMODE_STEREO3D_MAPPING(MAP, MKV_ONLY) \ + MAP(MATROSKA_VIDEO_STEREOMODE_TYPE_MONO, AV_STEREO3D_2D, 0, 0, 0, 1) \ + MAP(MATROSKA_VIDEO_STEREOMODE_TYPE_LEFT_RIGHT, AV_STEREO3D_SIDEBYSIDE, 0, 1, 0, 1) \ + MAP(MATROSKA_VIDEO_STEREOMODE_TYPE_BOTTOM_TOP, AV_STEREO3D_TOPBOTTOM, \ + AV_STEREO3D_FLAG_INVERT, 0, 1, 1) \ + MAP(MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM, AV_STEREO3D_TOPBOTTOM, 0, 0, 1, 1) \ + MAP(MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_RL, AV_STEREO3D_CHECKERBOARD, \ + AV_STEREO3D_FLAG_INVERT, 0, 0, 0) \ + MAP(MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_LR, AV_STEREO3D_CHECKERBOARD, 0, 0, 0, 0) \ + MAP(MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_RL, AV_STEREO3D_LINES, \ + AV_STEREO3D_FLAG_INVERT, 0, 1, 0) \ + MAP(MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_LR, AV_STEREO3D_LINES, 0, 0, 1, 0) \ + MAP(MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_RL, AV_STEREO3D_COLUMNS, \ + AV_STEREO3D_FLAG_INVERT, 1, 0, 0) \ + MAP(MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_LR, AV_STEREO3D_COLUMNS, 0, 1, 0, 0) \ + MKV_ONLY(MATROSKA_VIDEO_STEREOMODE_TYPE_ANAGLYPH_CYAN_RED, 0, 0, 0) \ + MAP(MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT, AV_STEREO3D_SIDEBYSIDE, \ + AV_STEREO3D_FLAG_INVERT, 1, 0, 1) \ + MKV_ONLY(MATROSKA_VIDEO_STEREOMODE_TYPE_ANAGLYPH_GREEN_MAG, 0, 0, 0) \ + MAP(MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_LR, AV_STEREO3D_FRAMESEQUENCE, \ + 0, 0, 0, 0) \ + MAP(MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_RL, AV_STEREO3D_FRAMESEQUENCE, \ + AV_STEREO3D_FLAG_INVERT, 0, 0, 0) + extern const char * const ff_matroska_video_stereo_mode[MATROSKA_VIDEO_STEREOMODE_TYPE_NB]; -extern const char * const ff_matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_PLANE_COUNT]; /* AVStream Metadata tag keys for WebM Dash Manifest */ #define INITIALIZATION_RANGE "webm_dash_manifest_initialization_range" @@ -390,8 +436,6 @@ extern const char * const ff_matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_P #define TRACK_NUMBER "webm_dash_manifest_track_number" #define CODEC_PRIVATE_SIZE "webm_dash_manifest_codec_priv_size" -int ff_mkv_stereo3d_conv(AVStream *st, MatroskaVideoStereoModeType stereo_mode); - #define DVCC_DVVC_BLOCK_TYPE_NAME "Dolby Vision configuration" #endif /* AVFORMAT_MATROSKA_H */ diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index b5ddeafa16b..ae3565b0c32 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -40,6 +40,7 @@ #include "libavutil/dict.h" #include "libavutil/dict_internal.h" #include "libavutil/display.h" +#include "libavutil/hdr_dynamic_metadata.h" #include "libavutil/intfloat.h" #include "libavutil/intreadwrite.h" #include "libavutil/lzo.h" @@ -51,7 +52,9 @@ #include "libavutil/spherical.h" #include "libavcodec/bytestream.h" +#include "libavcodec/defs.h" #include "libavcodec/flac.h" +#include "libavcodec/itut35.h" #include "libavcodec/mpeg4audio.h" #include "libavcodec/packet_internal.h" @@ -348,13 +351,17 @@ typedef struct MatroskaLevel { uint64_t length; } MatroskaLevel; +typedef struct MatroskaBlockMore { + uint64_t additional_id; + EbmlBin additional; +} MatroskaBlockMore; + typedef struct MatroskaBlock { uint64_t duration; CountedElement reference; uint64_t non_simple; EbmlBin bin; - uint64_t additional_id; - EbmlBin additional; + EbmlList blockmore; int64_t discard_padding; } MatroskaBlock; @@ -418,6 +425,8 @@ typedef struct MatroskaDemuxContext { MatroskaCluster current_cluster; + int is_webm; + /* WebM DASH Manifest live flag */ int is_live; @@ -586,7 +595,7 @@ static EbmlSyntax matroska_track_operation[] = { static EbmlSyntax matroska_block_addition_mapping[] = { { MATROSKA_ID_BLKADDIDVALUE, EBML_UINT, 0, 0, offsetof(MatroskaBlockAdditionMapping, value) }, { MATROSKA_ID_BLKADDIDNAME, EBML_STR, 0, 0, offsetof(MatroskaBlockAdditionMapping, name) }, - { MATROSKA_ID_BLKADDIDTYPE, EBML_UINT, 0, 0, offsetof(MatroskaBlockAdditionMapping, type) }, + { MATROSKA_ID_BLKADDIDTYPE, EBML_UINT, 0, 0, offsetof(MatroskaBlockAdditionMapping, type), { .u = MATROSKA_BLOCK_ADD_ID_TYPE_DEFAULT } }, { MATROSKA_ID_BLKADDIDEXTRADATA, EBML_BIN, 0, 0, offsetof(MatroskaBlockAdditionMapping, extradata) }, CHILD_OF(matroska_track) }; @@ -758,13 +767,13 @@ static EbmlSyntax matroska_segments[] = { }; static EbmlSyntax matroska_blockmore[] = { - { MATROSKA_ID_BLOCKADDID, EBML_UINT, 0, 0, offsetof(MatroskaBlock,additional_id), { .u = 1 } }, - { MATROSKA_ID_BLOCKADDITIONAL, EBML_BIN, 0, 0, offsetof(MatroskaBlock,additional) }, + { MATROSKA_ID_BLOCKADDID, EBML_UINT, 0, 0, offsetof(MatroskaBlockMore,additional_id), { .u = MATROSKA_BLOCK_ADD_ID_OPAQUE } }, + { MATROSKA_ID_BLOCKADDITIONAL, EBML_BIN, 0, 0, offsetof(MatroskaBlockMore,additional) }, CHILD_OF(matroska_blockadditions) }; static EbmlSyntax matroska_blockadditions[] = { - { MATROSKA_ID_BLOCKMORE, EBML_NEST, 0, 0, 0, {.n = matroska_blockmore} }, + { MATROSKA_ID_BLOCKMORE, EBML_NEST, 0, sizeof(MatroskaBlockMore), offsetof(MatroskaBlock, blockmore), { .n = matroska_blockmore } }, CHILD_OF(matroska_blockgroup) }; @@ -816,6 +825,12 @@ static const CodecMime mkv_mime_tags[] = { {"" , AV_CODEC_ID_NONE} }; +static const char * const matroska_video_stereo_plane[MATROSKA_VIDEO_STEREO_PLANE_COUNT] = { + "left", + "right", + "background", +}; + static const char *const matroska_doctypes[] = { "matroska", "webm" }; /* @@ -2015,6 +2030,80 @@ static void matroska_parse_cues(MatroskaDemuxContext *matroska) { matroska_add_index_entries(matroska); } +static int matroska_parse_content_encodings(MatroskaTrackEncoding *encodings, + unsigned nb_encodings, + MatroskaTrack *track, + char **key_id_base64, void *logctx) +{ + if (nb_encodings > 1) { + av_log(logctx, AV_LOG_ERROR, + "Multiple combined encodings not supported\n"); + return 0; + } + if (!nb_encodings) + return 0; + if (encodings->type) { + if (encodings->encryption.key_id.size > 0) { + /* Save the encryption key id to be stored later + * as a metadata tag. */ + const int b64_size = AV_BASE64_SIZE(encodings->encryption.key_id.size); + *key_id_base64 = av_malloc(b64_size); + if (!*key_id_base64) + return AVERROR(ENOMEM); + + av_base64_encode(*key_id_base64, b64_size, + encodings->encryption.key_id.data, + encodings->encryption.key_id.size); + } else { + encodings->scope = 0; + av_log(logctx, AV_LOG_ERROR, "Unsupported encoding type\n"); + } + } else if ( +#if CONFIG_ZLIB + encodings->compression.algo != MATROSKA_TRACK_ENCODING_COMP_ZLIB && +#endif +#if CONFIG_BZLIB + encodings->compression.algo != MATROSKA_TRACK_ENCODING_COMP_BZLIB && +#endif + encodings->compression.algo != MATROSKA_TRACK_ENCODING_COMP_LZO && + encodings->compression.algo != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP) { + encodings->scope = 0; + av_log(logctx, AV_LOG_ERROR, "Unsupported encoding type\n"); + } else if (track->codec_priv.size && encodings[0].scope & 2) { + uint8_t *codec_priv = track->codec_priv.data; + int ret = matroska_decode_buffer(&track->codec_priv.data, + &track->codec_priv.size, + track); + if (ret < 0) { + track->codec_priv.data = NULL; + track->codec_priv.size = 0; + av_log(logctx, AV_LOG_ERROR, + "Failed to decode codec private data\n"); + } + + if (codec_priv != track->codec_priv.data) { + av_buffer_unref(&track->codec_priv.buf); + if (track->codec_priv.data) { + track->codec_priv.buf = av_buffer_create(track->codec_priv.data, + track->codec_priv.size + AV_INPUT_BUFFER_PADDING_SIZE, + NULL, NULL, 0); + if (!track->codec_priv.buf) { + av_freep(&track->codec_priv.data); + track->codec_priv.size = 0; + return AVERROR(ENOMEM); + } + } + } + } + track->needs_decoding = !encodings->type && + encodings->scope & 1 && + (encodings->compression.algo != + MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP || + encodings->compression.settings.size); + + return 0; +} + static int matroska_aac_profile(char *codec_id) { static const char *const aac_profiles[] = { "MAIN", "LC", "SSR" }; @@ -2097,7 +2186,7 @@ static int matroska_parse_flac(AVFormatContext *s, return 0; } -static int mkv_field_order(MatroskaDemuxContext *matroska, uint64_t field_order) +static int mkv_field_order(const MatroskaDemuxContext *matroska, uint64_t field_order) { int minor, micro, bttb = 0; @@ -2149,6 +2238,35 @@ static void mkv_stereo_mode_display_mul(int stereo_mode, } } +static int mkv_stereo3d_conv(AVStream *st, MatroskaVideoStereoModeType stereo_mode) +{ + static const struct { + char type; + char flags; + } stereo_mode_conv [] = { +#define STEREO_MODE_CONV(STEREOMODETYPE, STEREO3DTYPE, FLAGS, WDIV, HDIV, WEBM) \ + [(STEREOMODETYPE)] = { .type = (STEREO3DTYPE), .flags = (FLAGS) }, +#define NOTHING(STEREOMODETYPE, WDIV, HDIV, WEBM) + STEREOMODE_STEREO3D_MAPPING(STEREO_MODE_CONV, NOTHING) + }; + AVStereo3D *stereo; + + stereo = av_stereo3d_alloc(); + if (!stereo) + return AVERROR(ENOMEM); + + stereo->type = stereo_mode_conv[stereo_mode].type; + stereo->flags = stereo_mode_conv[stereo_mode].flags; + + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_STEREO3D, stereo, sizeof(*stereo), 0)) { + av_freep(&stereo); + return AVERROR(ENOMEM); + } + + return 0; +} + static int mkv_parse_video_color(AVStream *st, const MatroskaTrack *track) { const MatroskaTrackVideoColor *color = track->video.color.elem; const MatroskaMasteringMeta *mastering_meta; @@ -2190,28 +2308,27 @@ static int mkv_parse_video_color(AVStream *st, const MatroskaTrack *track) { } if (color->max_cll && color->max_fall) { size_t size = 0; - int ret; AVContentLightMetadata *metadata = av_content_light_metadata_alloc(&size); if (!metadata) return AVERROR(ENOMEM); - ret = av_stream_add_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, - (uint8_t *)metadata, size); - if (ret < 0) { + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_CONTENT_LIGHT_LEVEL, metadata, size, 0)) { av_freep(&metadata); - return ret; + return AVERROR(ENOMEM); } metadata->MaxCLL = color->max_cll; metadata->MaxFALL = color->max_fall; } if (has_mastering_primaries || has_mastering_luminance) { - AVMasteringDisplayMetadata *metadata = - (AVMasteringDisplayMetadata*) av_stream_new_side_data( - st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, - sizeof(AVMasteringDisplayMetadata)); - if (!metadata) { + AVMasteringDisplayMetadata *metadata; + AVPacketSideData *sd = av_packet_side_data_new(&st->codecpar->coded_side_data, + &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_MASTERING_DISPLAY_METADATA, + sizeof(AVMasteringDisplayMetadata), 0); + if (!sd) return AVERROR(ENOMEM); - } + metadata = (AVMasteringDisplayMetadata*)sd->data; memset(metadata, 0, sizeof(AVMasteringDisplayMetadata)); if (has_mastering_primaries) { metadata->display_primaries[0][0] = av_d2q(mastering_meta->r_x, INT_MAX); @@ -2237,6 +2354,7 @@ static int mkv_create_display_matrix(AVStream *st, const MatroskaTrackVideoProjection *proj, void *logctx) { + AVPacketSideData *sd; double pitch = proj->pitch, yaw = proj->yaw, roll = proj->roll; int32_t *matrix; int hflip; @@ -2253,10 +2371,13 @@ static int mkv_create_display_matrix(AVStream *st, st->index, yaw, pitch, roll); return 0; } - matrix = (int32_t*)av_stream_new_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, - 9 * sizeof(*matrix)); - if (!matrix) + sd = av_packet_side_data_new(&st->codecpar->coded_side_data, + &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_DISPLAYMATRIX, + 9 * sizeof(*matrix), 0); + if (!sd) return AVERROR(ENOMEM); + matrix = (int32_t*)sd->data; hflip = yaw != 0.0; /* ProjectionPoseRoll is in the counter-clockwise direction @@ -2281,7 +2402,6 @@ static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track, size_t spherical_size; uint32_t l = 0, t = 0, r = 0, b = 0; uint32_t padding = 0; - int ret; if (mkv_projection->private.size && priv_data[0] != 0) { av_log(logctx, AV_LOG_WARNING, "Unknown spherical metadata\n"); @@ -2357,11 +2477,10 @@ static int mkv_parse_video_projection(AVStream *st, const MatroskaTrack *track, spherical->bound_right = r; spherical->bound_bottom = b; - ret = av_stream_add_side_data(st, AV_PKT_DATA_SPHERICAL, (uint8_t *)spherical, - spherical_size); - if (ret < 0) { + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_SPHERICAL, spherical, spherical_size, 0)) { av_freep(&spherical); - return ret; + return AVERROR(ENOMEM); } return 0; @@ -2373,7 +2492,7 @@ static int mkv_parse_dvcc_dvvc(AVFormatContext *s, AVStream *st, const MatroskaT return ff_isom_parse_dvcc_dvvc(s, st, bin->data, bin->size); } -static int mkv_parse_block_addition_mappings(AVFormatContext *s, AVStream *st, const MatroskaTrack *track) +static int mkv_parse_block_addition_mappings(AVFormatContext *s, AVStream *st, MatroskaTrack *track) { const EbmlList *mappings_list = &track->block_addition_mappings; MatroskaBlockAdditionMapping *mappings = mappings_list->elem; @@ -2381,18 +2500,47 @@ static int mkv_parse_block_addition_mappings(AVFormatContext *s, AVStream *st, c for (int i = 0; i < mappings_list->nb_elem; i++) { MatroskaBlockAdditionMapping *mapping = &mappings[i]; + uint64_t type = mapping->type; switch (mapping->type) { - case MKBETAG('d','v','c','C'): - case MKBETAG('d','v','v','C'): + case MATROSKA_BLOCK_ADD_ID_TYPE_DEFAULT: + av_log(s, AV_LOG_DEBUG, + "Explicit block Addition Mapping type \"Use BlockAddIDValue\", value %"PRIu64"," + " name \"%s\" found.\n", mapping->value, mapping->name ? mapping->name : ""); + type = MATROSKA_BLOCK_ADD_ID_TYPE_OPAQUE; + // fall-through + case MATROSKA_BLOCK_ADD_ID_TYPE_OPAQUE: + case MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35: + if (mapping->value != type) { + int strict = s->strict_std_compliance >= FF_COMPLIANCE_STRICT; + av_log(s, strict ? AV_LOG_ERROR : AV_LOG_WARNING, + "Invalid Block Addition Value 0x%"PRIx64" for Block Addition Mapping Type " + "0x%"PRIx64", name \"%s\"\n", mapping->value, mapping->type, + mapping->name ? mapping->name : ""); + if (strict) + return AVERROR_INVALIDDATA; + } + break; + case MATROSKA_BLOCK_ADD_ID_TYPE_DVCC: + case MATROSKA_BLOCK_ADD_ID_TYPE_DVVC: if ((ret = mkv_parse_dvcc_dvvc(s, st, track, &mapping->extradata)) < 0) return ret; break; default: av_log(s, AV_LOG_DEBUG, - "Unknown block additional mapping type 0x%"PRIx64", value %"PRIu64", name \"%s\"\n", + "Unknown Block Addition Mapping type 0x%"PRIx64", value %"PRIu64", name \"%s\"\n", mapping->type, mapping->value, mapping->name ? mapping->name : ""); + if (mapping->value < 2) { + int strict = s->strict_std_compliance >= FF_COMPLIANCE_STRICT; + av_log(s, strict ? AV_LOG_ERROR : AV_LOG_WARNING, + "Invalid Block Addition value 0x%"PRIu64" for unknown Block Addition Mapping " + "type %"PRIx64", name \"%s\"\n", mapping->value, mapping->type, + mapping->name ? mapping->name : ""); + if (strict) + return AVERROR_INVALIDDATA; + } + break; } } @@ -2427,27 +2575,524 @@ static int get_qt_codec(MatroskaTrack *track, uint32_t *fourcc, enum AVCodecID * return 0; } +/* An enum with potential return values of the functions for parsing a track. + * Apart from that all these functions can also indicate ordinary errors via + * negative return values. */ +enum { + SKIP_TRACK = 1, +}; + +#define AAC_MAX_EXTRADATA_SIZE 5 +#define TTA_EXTRADATA_SIZE 22 +#define WAVPACK_EXTRADATA_SIZE 2 +/* Performs the codec-specific part of parsing an audio track. */ +static int mka_parse_audio_codec(MatroskaTrack *track, AVCodecParameters *par, + const MatroskaDemuxContext *matroska, + AVFormatContext *s, int *extradata_offset) +{ + uint8_t extradata[FFMAX3(AAC_MAX_EXTRADATA_SIZE, + TTA_EXTRADATA_SIZE, + WAVPACK_EXTRADATA_SIZE)]; + int extradata_size = 0; // > 0 means that the extradata buffer is used + int ret; + + if (!strcmp(track->codec_id, "A_MS/ACM") && + track->codec_priv.size >= 14) { + FFIOContext b; + ffio_init_read_context(&b, track->codec_priv.data, + track->codec_priv.size); + ret = ff_get_wav_header(s, &b.pub, par, + track->codec_priv.size, 0); + if (ret < 0) + return ret; + *extradata_offset = FFMIN(track->codec_priv.size, 18); + return 0; + } else if (!strcmp(track->codec_id, "A_QUICKTIME") && + /* Normally 36, but allow noncompliant private data */ + track->codec_priv.size >= 32) { + enum AVCodecID codec_id; + uint32_t fourcc; + uint16_t sample_size; + + ret = get_qt_codec(track, &fourcc, &codec_id); + if (ret < 0) + return ret; + sample_size = AV_RB16(track->codec_priv.data + 26); + if (fourcc == 0) { + if (sample_size == 8) { + fourcc = MKTAG('r','a','w',' '); + codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc); + } else if (sample_size == 16) { + fourcc = MKTAG('t','w','o','s'); + codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc); + } + } + if ((fourcc == MKTAG('t','w','o','s') || + fourcc == MKTAG('s','o','w','t')) && sample_size == 8) + codec_id = AV_CODEC_ID_PCM_S8; + par->codec_id = codec_id; + par->codec_tag = fourcc; + return 0; + } + + switch (par->codec_id) { + case AV_CODEC_ID_PCM_S16BE: + switch (track->audio.bitdepth) { + case 8: + par->codec_id = AV_CODEC_ID_PCM_U8; + break; + case 24: + par->codec_id = AV_CODEC_ID_PCM_S24BE; + break; + case 32: + par->codec_id = AV_CODEC_ID_PCM_S32BE; + break; + } + break; + case AV_CODEC_ID_PCM_S16LE: + switch (track->audio.bitdepth) { + case 8: + par->codec_id = AV_CODEC_ID_PCM_U8; + break; + case 24: + par->codec_id = AV_CODEC_ID_PCM_S24LE; + break; + case 32: + par->codec_id = AV_CODEC_ID_PCM_S32LE; + break; + } + break; + case AV_CODEC_ID_PCM_F32LE: + if (track->audio.bitdepth == 64) + par->codec_id = AV_CODEC_ID_PCM_F64LE; + break; + case AV_CODEC_ID_AAC: + if (!track->codec_priv.size) { + int profile = matroska_aac_profile(track->codec_id); + int sri = matroska_aac_sri(track->audio.samplerate); + + extradata[0] = (profile << 3) | ((sri & 0x0E) >> 1); + extradata[1] = ((sri & 0x01) << 7) | (track->audio.channels << 3); + if (strstr(track->codec_id, "SBR")) { + sri = matroska_aac_sri(track->audio.out_samplerate); + extradata[2] = 0x56; + extradata[3] = 0xE5; + extradata[4] = 0x80 | (sri << 3); + extradata_size = 5; + } else + extradata_size = 2; + } + break; + case AV_CODEC_ID_ALAC: + if (track->codec_priv.size && track->codec_priv.size < INT_MAX - 12 - AV_INPUT_BUFFER_PADDING_SIZE) { + /* Only ALAC's magic cookie is stored in Matroska's track headers. + * Create the "atom size", "tag", and "tag version" fields the + * decoder expects manually. */ + ret = ff_alloc_extradata(par, 12 + track->codec_priv.size); + if (ret < 0) + return ret; + AV_WB32(par->extradata, par->extradata_size); + AV_WB32(&par->extradata[4], MKBETAG('a', 'l', 'a', 'c')); + AV_WB32(&par->extradata[8], 0); + memcpy(&par->extradata[12], track->codec_priv.data, + track->codec_priv.size); + } + break; + case AV_CODEC_ID_TTA: + { + uint8_t *ptr; + if (track->audio.channels > UINT16_MAX || + track->audio.bitdepth > UINT16_MAX) { + av_log(matroska->ctx, AV_LOG_WARNING, + "Too large audio channel number %"PRIu64 + " or bitdepth %"PRIu64". Skipping track.\n", + track->audio.channels, track->audio.bitdepth); + if (matroska->ctx->error_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + else + return SKIP_TRACK; + } + if (track->audio.out_samplerate < 0 || track->audio.out_samplerate > INT_MAX) + return AVERROR_INVALIDDATA; + extradata_size = TTA_EXTRADATA_SIZE; + ptr = extradata; + bytestream_put_be32(&ptr, AV_RB32("TTA1")); + bytestream_put_le16(&ptr, 1); + bytestream_put_le16(&ptr, track->audio.channels); + bytestream_put_le16(&ptr, track->audio.bitdepth); + bytestream_put_le32(&ptr, track->audio.out_samplerate); + bytestream_put_le32(&ptr, av_rescale(matroska->duration * matroska->time_scale, + track->audio.out_samplerate, + AV_TIME_BASE * 1000)); + break; + } + case AV_CODEC_ID_RA_144: + track->audio.out_samplerate = 8000; + track->audio.channels = 1; + break; + case AV_CODEC_ID_RA_288: + case AV_CODEC_ID_COOK: + case AV_CODEC_ID_ATRAC3: + case AV_CODEC_ID_SIPR: + { + const uint8_t *ptr = track->codec_priv.data; + int flavor; + + if (!track->codec_priv.size) + break; + + if (track->codec_priv.size < 46) + return AVERROR_INVALIDDATA; + ptr += 22; + flavor = bytestream_get_be16(&ptr); + track->audio.coded_framesize = bytestream_get_be32(&ptr); + ptr += 12; + track->audio.sub_packet_h = bytestream_get_be16(&ptr); + track->audio.frame_size = bytestream_get_be16(&ptr); + track->audio.sub_packet_size = bytestream_get_be16(&ptr); + if (track->audio.coded_framesize <= 0 || + track->audio.sub_packet_h <= 0 || + track->audio.frame_size <= 0) + return AVERROR_INVALIDDATA; + + if (par->codec_id == AV_CODEC_ID_RA_288) { + if (track->audio.sub_packet_h & 1 || 2 * track->audio.frame_size + != (int64_t)track->audio.sub_packet_h * track->audio.coded_framesize) + return AVERROR_INVALIDDATA; + par->block_align = track->audio.coded_framesize; + track->codec_priv.size = 0; + } else { + if (par->codec_id == AV_CODEC_ID_SIPR) { + static const int sipr_bit_rate[4] = { 6504, 8496, 5000, 16000 }; + if (flavor > 3) + return AVERROR_INVALIDDATA; + track->audio.sub_packet_size = ff_sipr_subpk_size[flavor]; + par->bit_rate = sipr_bit_rate[flavor]; + } else if (track->audio.sub_packet_size <= 0 || + track->audio.frame_size % track->audio.sub_packet_size) + return AVERROR_INVALIDDATA; + par->block_align = track->audio.sub_packet_size; + *extradata_offset = 78; + } + track->audio.buf = av_malloc_array(track->audio.sub_packet_h, + track->audio.frame_size); + if (!track->audio.buf) + return AVERROR(ENOMEM); + break; + } + case AV_CODEC_ID_ATRAC1: + /* ATRAC1 uses a constant frame size. + * Typical ATRAC1 streams are either mono or stereo. + * At most, ATRAC1 was used to store 8 channels of audio. */ + if (track->audio.channels > 8) + return AVERROR_INVALIDDATA; + par->block_align = track->audio.channels * 212; + break; + case AV_CODEC_ID_FLAC: + if (track->codec_priv.size) { + ret = matroska_parse_flac(s, track, extradata_offset); + if (ret < 0) + return ret; + } + break; + case AV_CODEC_ID_WAVPACK: + if (track->codec_priv.size < 2) { + av_log(matroska->ctx, AV_LOG_INFO, "Assuming WavPack version 4.10 " + "in absence of valid CodecPrivate.\n"); + extradata_size = WAVPACK_EXTRADATA_SIZE; + AV_WL16(extradata, 0x410); + } + break; + } + + if (extradata_size > 0) { + ret = ff_alloc_extradata(par, extradata_size); + if (ret < 0) + return ret; + memcpy(par->extradata, extradata, extradata_size); + } + + return 0; +} + +/* Performs the generic part of parsing an audio track. */ +static int mka_parse_audio(MatroskaTrack *track, AVStream *st, + AVCodecParameters *par, + const MatroskaDemuxContext *matroska, + AVFormatContext *s, int *extradata_offset) +{ + FFStream *const sti = ffstream(st); + int ret; + + ret = mka_parse_audio_codec(track, par, matroska, + s, extradata_offset); + if (ret) + return ret; + + par->codec_type = AVMEDIA_TYPE_AUDIO; + par->sample_rate = track->audio.out_samplerate; + // channel layout may be already set by codec private checks above + if (!av_channel_layout_check(&par->ch_layout)) { + par->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; + par->ch_layout.nb_channels = track->audio.channels; + } + if (!par->bits_per_coded_sample) + par->bits_per_coded_sample = track->audio.bitdepth; + if (par->codec_id == AV_CODEC_ID_MP3 || + par->codec_id == AV_CODEC_ID_MLP || + par->codec_id == AV_CODEC_ID_TRUEHD) + sti->need_parsing = AVSTREAM_PARSE_FULL; + else if (par->codec_id != AV_CODEC_ID_AAC) + sti->need_parsing = AVSTREAM_PARSE_HEADERS; + if (track->codec_delay > 0) { + par->initial_padding = av_rescale_q(track->codec_delay, + (AVRational){1, 1000000000}, + (AVRational){1, par->codec_id == AV_CODEC_ID_OPUS ? + 48000 : par->sample_rate}); + } + if (track->seek_preroll > 0) { + par->seek_preroll = av_rescale_q(track->seek_preroll, + (AVRational){1, 1000000000}, + (AVRational){1, par->sample_rate}); + } + + return 0; +} + +/* Performs the codec-specific part of parsing a video track. */ +static int mkv_parse_video_codec(MatroskaTrack *track, AVCodecParameters *par, + const MatroskaDemuxContext *matroska, + int *extradata_offset) +{ + if (!strcmp(track->codec_id, "V_MS/VFW/FOURCC") && + track->codec_priv.size >= 40) { + track->ms_compat = 1; + par->bits_per_coded_sample = AV_RL16(track->codec_priv.data + 14); + par->codec_tag = AV_RL32(track->codec_priv.data + 16); + par->codec_id = ff_codec_get_id(ff_codec_bmp_tags, + par->codec_tag); + if (!par->codec_id) + par->codec_id = ff_codec_get_id(ff_codec_movvideo_tags, + par->codec_tag); + *extradata_offset = 40; + return 0; + } else if (!strcmp(track->codec_id, "V_QUICKTIME") && + track->codec_priv.size >= 21) { + enum AVCodecID codec_id; + uint32_t fourcc; + int ret = get_qt_codec(track, &fourcc, &codec_id); + if (ret < 0) + return ret; + if (codec_id == AV_CODEC_ID_NONE && AV_RL32(track->codec_priv.data+4) == AV_RL32("SMI ")) { + fourcc = MKTAG('S','V','Q','3'); + codec_id = ff_codec_get_id(ff_codec_movvideo_tags, fourcc); + } + par->codec_id = codec_id; + if (codec_id == AV_CODEC_ID_NONE) + av_log(matroska->ctx, AV_LOG_ERROR, + "mov FourCC not found %s.\n", av_fourcc2str(fourcc)); + if (track->codec_priv.size >= 86) { + FFIOContext b; + unsigned bit_depth = AV_RB16(track->codec_priv.data + 82); + ffio_init_read_context(&b, track->codec_priv.data, + track->codec_priv.size); + if (ff_get_qtpalette(codec_id, &b.pub, track->palette)) { + bit_depth &= 0x1F; + track->has_palette = 1; + } + par->bits_per_coded_sample = bit_depth; + } + par->codec_tag = fourcc; + return 0; + } + + switch (par->codec_id) { + case AV_CODEC_ID_RV10: + case AV_CODEC_ID_RV20: + case AV_CODEC_ID_RV30: + case AV_CODEC_ID_RV40: + *extradata_offset = 26; + break; + case AV_CODEC_ID_PRORES: + if (track->codec_priv.size == 4) + par->codec_tag = AV_RL32(track->codec_priv.data); + break; + case AV_CODEC_ID_VP9: + /* we don't need any value stored in CodecPrivate. + * make sure that it's not exported as extradata. */ + track->codec_priv.size = 0; + break; + } + + return 0; +} + +/* Performs the generic part of parsing a video track. */ +static int mkv_parse_video(MatroskaTrack *track, AVStream *st, + AVCodecParameters *par, + const MatroskaDemuxContext *matroska, + int *extradata_offset) +{ + FFStream *const sti = ffstream(st); + MatroskaTrackPlane *planes; + int display_width_mul = 1; + int display_height_mul = 1; + int ret; + + if (track->video.color_space.size == 4) + par->codec_tag = AV_RL32(track->video.color_space.data); + + ret = mkv_parse_video_codec(track, par, matroska, + extradata_offset); + if (ret < 0) + return ret; + + par->codec_type = AVMEDIA_TYPE_VIDEO; + par->width = track->video.pixel_width; + par->height = track->video.pixel_height; + + if (track->video.interlaced == MATROSKA_VIDEO_INTERLACE_FLAG_INTERLACED) + par->field_order = mkv_field_order(matroska, track->video.field_order); + else if (track->video.interlaced == MATROSKA_VIDEO_INTERLACE_FLAG_PROGRESSIVE) + par->field_order = AV_FIELD_PROGRESSIVE; + + if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB) + mkv_stereo_mode_display_mul(track->video.stereo_mode, + &display_width_mul, &display_height_mul); + + if (track->video.display_unit < MATROSKA_VIDEO_DISPLAYUNIT_UNKNOWN) { + if (track->video.display_width && track->video.display_height && + par->height < INT64_MAX / track->video.display_width / display_width_mul && + par->width < INT64_MAX / track->video.display_height / display_height_mul) + av_reduce(&st->sample_aspect_ratio.num, + &st->sample_aspect_ratio.den, + par->height * track->video.display_width * display_width_mul, + par->width * track->video.display_height * display_height_mul, + INT_MAX); + } + if (par->codec_id != AV_CODEC_ID_HEVC) + sti->need_parsing = AVSTREAM_PARSE_HEADERS; + + if (track->default_duration) { + int div = track->default_duration <= INT64_MAX ? 1 : 2; + av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, + 1000000000 / div, track->default_duration / div, 30000); +#if FF_API_R_FRAME_RATE + if ( st->avg_frame_rate.num < st->avg_frame_rate.den * 1000LL + && st->avg_frame_rate.num > st->avg_frame_rate.den * 5LL) + st->r_frame_rate = st->avg_frame_rate; +#endif + } + + /* export stereo mode flag as metadata tag */ + if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB) + av_dict_set(&st->metadata, "stereo_mode", ff_matroska_video_stereo_mode[track->video.stereo_mode], 0); + + /* export alpha mode flag as metadata tag */ + if (track->video.alpha_mode) + av_dict_set_int(&st->metadata, "alpha_mode", 1, 0); + + /* if we have virtual track, mark the real tracks */ + planes = track->operation.combine_planes.elem; + for (int j = 0; j < track->operation.combine_planes.nb_elem; j++) { + MatroskaTrack *tracks = matroska->tracks.elem; + char buf[32]; + if (planes[j].type >= MATROSKA_VIDEO_STEREO_PLANE_COUNT) + continue; + snprintf(buf, sizeof(buf), "%s_%d", + matroska_video_stereo_plane[planes[j].type], st->index); + for (int k = 0; k < matroska->tracks.nb_elem; k++) + if (planes[j].uid == tracks[k].uid && tracks[k].stream) { + av_dict_set(&tracks[k].stream->metadata, + "stereo_mode", buf, 0); + break; + } + } + // add stream level stereo3d side data if it is a supported format + if (track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB && + track->video.stereo_mode != MATROSKA_VIDEO_STEREOMODE_TYPE_ANAGLYPH_CYAN_RED && + track->video.stereo_mode != MATROSKA_VIDEO_STEREOMODE_TYPE_ANAGLYPH_GREEN_MAG) { + int ret = mkv_stereo3d_conv(st, track->video.stereo_mode); + if (ret < 0) + return ret; + } + + ret = mkv_parse_video_color(st, track); + if (ret < 0) + return ret; + ret = mkv_parse_video_projection(st, track, matroska->ctx); + if (ret < 0) + return ret; + + return 0; +} + +/* Performs the codec-specific part of parsing a subtitle track. */ +static int mkv_parse_subtitle_codec(MatroskaTrack *track, AVStream *st, + AVCodecParameters *par, + const MatroskaDemuxContext *matroska) +{ + switch (par->codec_id) { + case AV_CODEC_ID_ARIB_CAPTION: + if (track->codec_priv.size == 3) { + int component_tag = track->codec_priv.data[0]; + int data_component_id = AV_RB16(track->codec_priv.data + 1); + + switch (data_component_id) { + case 0x0008: + // [0x30..0x37] are component tags utilized for + // non-mobile captioning service ("profile A"). + if (component_tag >= 0x30 && component_tag <= 0x37) { + par->profile = AV_PROFILE_ARIB_PROFILE_A; + } + break; + case 0x0012: + // component tag 0x87 signifies a mobile/partial reception + // (1seg) captioning service ("profile C"). + if (component_tag == 0x87) { + par->profile = AV_PROFILE_ARIB_PROFILE_C; + } + break; + default: + break; + } + + if (par->profile == AV_PROFILE_UNKNOWN) + av_log(matroska->ctx, AV_LOG_WARNING, + "Unknown ARIB caption profile utilized: %02x / %04x\n", + component_tag, data_component_id); + + track->codec_priv.size = 0; + } + break; + case AV_CODEC_ID_WEBVTT: + if (!strcmp(track->codec_id, "D_WEBVTT/CAPTIONS")) { + st->disposition |= AV_DISPOSITION_CAPTIONS; + } else if (!strcmp(track->codec_id, "D_WEBVTT/DESCRIPTIONS")) { + st->disposition |= AV_DISPOSITION_DESCRIPTIONS; + } else if (!strcmp(track->codec_id, "D_WEBVTT/METADATA")) { + st->disposition |= AV_DISPOSITION_METADATA; + } + break; + } + + return 0; +} + static int matroska_parse_tracks(AVFormatContext *s) { MatroskaDemuxContext *matroska = s->priv_data; MatroskaTrack *tracks = matroska->tracks.elem; int i, j, ret; - int k; for (i = 0; i < matroska->tracks.nb_elem; i++) { MatroskaTrack *track = &tracks[i]; enum AVCodecID codec_id = AV_CODEC_ID_NONE; - EbmlList *encodings_list = &track->encodings; - MatroskaTrackEncoding *encodings = encodings_list->elem; - uint8_t *extradata = NULL; - int extradata_size = 0; + AVCodecParameters *par; + MatroskaTrackType type; int extradata_offset = 0; - uint32_t fourcc = 0; - FFIOContext b; AVStream *st; - FFStream *sti; char* key_id_base64 = NULL; - int bit_depth = -1; /* Apply some sanity checks. */ if (track->type != MATROSKA_TRACK_TYPE_VIDEO && @@ -2494,77 +3139,15 @@ static int matroska_parse_tracks(AVFormatContext *s) track->video.display_width = track->video.pixel_width; if (track->video.display_height == -1) track->video.display_height = track->video.pixel_height; - if (track->video.color_space.size == 4) - fourcc = AV_RL32(track->video.color_space.data); } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) { if (!track->audio.out_samplerate) track->audio.out_samplerate = track->audio.samplerate; } - if (encodings_list->nb_elem > 1) { - av_log(matroska->ctx, AV_LOG_ERROR, - "Multiple combined encodings not supported"); - } else if (encodings_list->nb_elem == 1) { - if (encodings[0].type) { - if (encodings[0].encryption.key_id.size > 0) { - /* Save the encryption key id to be stored later as a - metadata tag. */ - const int b64_size = AV_BASE64_SIZE(encodings[0].encryption.key_id.size); - key_id_base64 = av_malloc(b64_size); - if (key_id_base64 == NULL) - return AVERROR(ENOMEM); - - av_base64_encode(key_id_base64, b64_size, - encodings[0].encryption.key_id.data, - encodings[0].encryption.key_id.size); - } else { - encodings[0].scope = 0; - av_log(matroska->ctx, AV_LOG_ERROR, - "Unsupported encoding type"); - } - } else if ( -#if CONFIG_ZLIB - encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_ZLIB && -#endif -#if CONFIG_BZLIB - encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_BZLIB && -#endif - encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_LZO && - encodings[0].compression.algo != MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP) { - encodings[0].scope = 0; - av_log(matroska->ctx, AV_LOG_ERROR, - "Unsupported encoding type"); - } else if (track->codec_priv.size && encodings[0].scope & 2) { - uint8_t *codec_priv = track->codec_priv.data; - int ret = matroska_decode_buffer(&track->codec_priv.data, - &track->codec_priv.size, - track); - if (ret < 0) { - track->codec_priv.data = NULL; - track->codec_priv.size = 0; - av_log(matroska->ctx, AV_LOG_ERROR, - "Failed to decode codec private data\n"); - } - - if (codec_priv != track->codec_priv.data) { - av_buffer_unref(&track->codec_priv.buf); - if (track->codec_priv.data) { - track->codec_priv.buf = av_buffer_create(track->codec_priv.data, - track->codec_priv.size + AV_INPUT_BUFFER_PADDING_SIZE, - NULL, NULL, 0); - if (!track->codec_priv.buf) { - av_freep(&track->codec_priv.data); - track->codec_priv.size = 0; - return AVERROR(ENOMEM); - } - } - } - } - } - track->needs_decoding = encodings && !encodings[0].type && - encodings[0].scope & 1 && - (encodings[0].compression.algo != - MATROSKA_TRACK_ENCODING_COMP_HEADERSTRIP || - encodings[0].compression.settings.size); + ret = matroska_parse_content_encodings(track->encodings.elem, + track->encodings.nb_elem, + track, &key_id_base64, matroska->ctx); + if (ret < 0) + return ret; for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) { if (av_strstart(track->codec_id, ff_mkv_codec_tags[j].str, NULL)) { @@ -2578,267 +3161,9 @@ static int matroska_parse_tracks(AVFormatContext *s) av_free(key_id_base64); return AVERROR(ENOMEM); } - sti = ffstream(st); - - if (key_id_base64) { - /* export encryption key id as base64 metadata tag */ - av_dict_set(&st->metadata, "enc_key_id", key_id_base64, - AV_DICT_DONT_STRDUP_VAL); - } - - if (!strcmp(track->codec_id, "V_MS/VFW/FOURCC") && - track->codec_priv.size >= 40 && - track->codec_priv.data) { - track->ms_compat = 1; - bit_depth = AV_RL16(track->codec_priv.data + 14); - fourcc = AV_RL32(track->codec_priv.data + 16); - codec_id = ff_codec_get_id(ff_codec_bmp_tags, - fourcc); - if (!codec_id) - codec_id = ff_codec_get_id(ff_codec_movvideo_tags, - fourcc); - extradata_offset = 40; - } else if (!strcmp(track->codec_id, "A_MS/ACM") && - track->codec_priv.size >= 14 && - track->codec_priv.data) { - int ret; - ffio_init_context(&b, track->codec_priv.data, - track->codec_priv.size, - 0, NULL, NULL, NULL, NULL); - ret = ff_get_wav_header(s, &b.pub, st->codecpar, - track->codec_priv.size, 0); - if (ret < 0) - return ret; - codec_id = st->codecpar->codec_id; - fourcc = st->codecpar->codec_tag; - extradata_offset = FFMIN(track->codec_priv.size, 18); - } else if (!strcmp(track->codec_id, "A_QUICKTIME") - /* Normally 36, but allow noncompliant private data */ - && (track->codec_priv.size >= 32) - && (track->codec_priv.data)) { - uint16_t sample_size; - int ret = get_qt_codec(track, &fourcc, &codec_id); - if (ret < 0) - return ret; - sample_size = AV_RB16(track->codec_priv.data + 26); - if (fourcc == 0) { - if (sample_size == 8) { - fourcc = MKTAG('r','a','w',' '); - codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc); - } else if (sample_size == 16) { - fourcc = MKTAG('t','w','o','s'); - codec_id = ff_codec_get_id(ff_codec_movaudio_tags, fourcc); - } - } - if ((fourcc == MKTAG('t','w','o','s') || - fourcc == MKTAG('s','o','w','t')) && - sample_size == 8) - codec_id = AV_CODEC_ID_PCM_S8; - } else if (!strcmp(track->codec_id, "V_QUICKTIME") && - (track->codec_priv.size >= 21) && - (track->codec_priv.data)) { - int ret = get_qt_codec(track, &fourcc, &codec_id); - if (ret < 0) - return ret; - if (codec_id == AV_CODEC_ID_NONE && AV_RL32(track->codec_priv.data+4) == AV_RL32("SMI ")) { - fourcc = MKTAG('S','V','Q','3'); - codec_id = ff_codec_get_id(ff_codec_movvideo_tags, fourcc); - } - if (codec_id == AV_CODEC_ID_NONE) - av_log(matroska->ctx, AV_LOG_ERROR, - "mov FourCC not found %s.\n", av_fourcc2str(fourcc)); - if (track->codec_priv.size >= 86) { - bit_depth = AV_RB16(track->codec_priv.data + 82); - ffio_init_context(&b, track->codec_priv.data, - track->codec_priv.size, - 0, NULL, NULL, NULL, NULL); - if (ff_get_qtpalette(codec_id, &b.pub, track->palette)) { - bit_depth &= 0x1F; - track->has_palette = 1; - } - } - } else if (codec_id == AV_CODEC_ID_PCM_S16BE) { - switch (track->audio.bitdepth) { - case 8: - codec_id = AV_CODEC_ID_PCM_U8; - break; - case 24: - codec_id = AV_CODEC_ID_PCM_S24BE; - break; - case 32: - codec_id = AV_CODEC_ID_PCM_S32BE; - break; - } - } else if (codec_id == AV_CODEC_ID_PCM_S16LE) { - switch (track->audio.bitdepth) { - case 8: - codec_id = AV_CODEC_ID_PCM_U8; - break; - case 24: - codec_id = AV_CODEC_ID_PCM_S24LE; - break; - case 32: - codec_id = AV_CODEC_ID_PCM_S32LE; - break; - } - } else if (codec_id == AV_CODEC_ID_PCM_F32LE && - track->audio.bitdepth == 64) { - codec_id = AV_CODEC_ID_PCM_F64LE; - } else if (codec_id == AV_CODEC_ID_AAC && !track->codec_priv.size) { - int profile = matroska_aac_profile(track->codec_id); - int sri = matroska_aac_sri(track->audio.samplerate); - extradata = av_mallocz(5 + AV_INPUT_BUFFER_PADDING_SIZE); - if (!extradata) - return AVERROR(ENOMEM); - extradata[0] = (profile << 3) | ((sri & 0x0E) >> 1); - extradata[1] = ((sri & 0x01) << 7) | (track->audio.channels << 3); - if (strstr(track->codec_id, "SBR")) { - sri = matroska_aac_sri(track->audio.out_samplerate); - extradata[2] = 0x56; - extradata[3] = 0xE5; - extradata[4] = 0x80 | (sri << 3); - extradata_size = 5; - } else - extradata_size = 2; - } else if (codec_id == AV_CODEC_ID_ALAC && track->codec_priv.size && track->codec_priv.size < INT_MAX - 12 - AV_INPUT_BUFFER_PADDING_SIZE) { - /* Only ALAC's magic cookie is stored in Matroska's track headers. - * Create the "atom size", "tag", and "tag version" fields the - * decoder expects manually. */ - extradata_size = 12 + track->codec_priv.size; - extradata = av_mallocz(extradata_size + - AV_INPUT_BUFFER_PADDING_SIZE); - if (!extradata) - return AVERROR(ENOMEM); - AV_WB32(extradata, extradata_size); - memcpy(&extradata[4], "alac", 4); - AV_WB32(&extradata[8], 0); - memcpy(&extradata[12], track->codec_priv.data, - track->codec_priv.size); - } else if (codec_id == AV_CODEC_ID_TTA) { - uint8_t *ptr; - if (track->audio.channels > UINT16_MAX || - track->audio.bitdepth > UINT16_MAX) { - av_log(matroska->ctx, AV_LOG_WARNING, - "Too large audio channel number %"PRIu64 - " or bitdepth %"PRIu64". Skipping track.\n", - track->audio.channels, track->audio.bitdepth); - if (matroska->ctx->error_recognition & AV_EF_EXPLODE) - return AVERROR_INVALIDDATA; - else - continue; - } - if (track->audio.out_samplerate < 0 || track->audio.out_samplerate > INT_MAX) - return AVERROR_INVALIDDATA; - extradata_size = 22; - extradata = av_mallocz(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); - if (!extradata) - return AVERROR(ENOMEM); - ptr = extradata; - bytestream_put_be32(&ptr, AV_RB32("TTA1")); - bytestream_put_le16(&ptr, 1); - bytestream_put_le16(&ptr, track->audio.channels); - bytestream_put_le16(&ptr, track->audio.bitdepth); - bytestream_put_le32(&ptr, track->audio.out_samplerate); - bytestream_put_le32(&ptr, av_rescale(matroska->duration * matroska->time_scale, - track->audio.out_samplerate, - AV_TIME_BASE * 1000)); - } else if (codec_id == AV_CODEC_ID_RV10 || - codec_id == AV_CODEC_ID_RV20 || - codec_id == AV_CODEC_ID_RV30 || - codec_id == AV_CODEC_ID_RV40) { - extradata_offset = 26; - } else if (codec_id == AV_CODEC_ID_RA_144) { - track->audio.out_samplerate = 8000; - track->audio.channels = 1; - } else if ((codec_id == AV_CODEC_ID_RA_288 || - codec_id == AV_CODEC_ID_COOK || - codec_id == AV_CODEC_ID_ATRAC3 || - codec_id == AV_CODEC_ID_SIPR) - && track->codec_priv.data) { - const uint8_t *ptr = track->codec_priv.data; - int flavor; - - if (track->codec_priv.size < 46) - return AVERROR_INVALIDDATA; - ptr += 22; - flavor = bytestream_get_be16(&ptr); - track->audio.coded_framesize = bytestream_get_be32(&ptr); - ptr += 12; - track->audio.sub_packet_h = bytestream_get_be16(&ptr); - track->audio.frame_size = bytestream_get_be16(&ptr); - track->audio.sub_packet_size = bytestream_get_be16(&ptr); - if (track->audio.coded_framesize <= 0 || - track->audio.sub_packet_h <= 0 || - track->audio.frame_size <= 0) - return AVERROR_INVALIDDATA; - - if (codec_id == AV_CODEC_ID_RA_288) { - if (track->audio.sub_packet_h & 1 || 2 * track->audio.frame_size - != (int64_t)track->audio.sub_packet_h * track->audio.coded_framesize) - return AVERROR_INVALIDDATA; - st->codecpar->block_align = track->audio.coded_framesize; - track->codec_priv.size = 0; - } else { - if (codec_id == AV_CODEC_ID_SIPR) { - static const int sipr_bit_rate[4] = { 6504, 8496, 5000, 16000 }; - if (flavor > 3) - return AVERROR_INVALIDDATA; - track->audio.sub_packet_size = ff_sipr_subpk_size[flavor]; - st->codecpar->bit_rate = sipr_bit_rate[flavor]; - } else if (track->audio.sub_packet_size <= 0 || - track->audio.frame_size % track->audio.sub_packet_size) - return AVERROR_INVALIDDATA; - st->codecpar->block_align = track->audio.sub_packet_size; - extradata_offset = 78; - } - track->audio.buf = av_malloc_array(track->audio.sub_packet_h, - track->audio.frame_size); - if (!track->audio.buf) - return AVERROR(ENOMEM); - } else if (codec_id == AV_CODEC_ID_FLAC && track->codec_priv.size) { - ret = matroska_parse_flac(s, track, &extradata_offset); - if (ret < 0) - return ret; - } else if (codec_id == AV_CODEC_ID_WAVPACK && track->codec_priv.size < 2) { - av_log(matroska->ctx, AV_LOG_INFO, "Assuming WavPack version 4.10 " - "in absence of valid CodecPrivate.\n"); - extradata_size = 2; - extradata = av_mallocz(2 + AV_INPUT_BUFFER_PADDING_SIZE); - if (!extradata) - return AVERROR(ENOMEM); - AV_WL16(extradata, 0x410); - } else if (codec_id == AV_CODEC_ID_PRORES && track->codec_priv.size == 4) { - fourcc = AV_RL32(track->codec_priv.data); - } else if (codec_id == AV_CODEC_ID_VP9 && track->codec_priv.size) { - /* we don't need any value stored in CodecPrivate. - make sure that it's not exported as extradata. */ - track->codec_priv.size = 0; - } - track->codec_priv.size -= extradata_offset; + par = st->codecpar; - if (codec_id == AV_CODEC_ID_NONE) - av_log(matroska->ctx, AV_LOG_INFO, - "Unknown/unsupported AVCodecID %s.\n", track->codec_id); - - if (track->time_scale < 0.01) { - av_log(matroska->ctx, AV_LOG_WARNING, - "Track TimestampScale too small %f, assuming 1.0.\n", - track->time_scale); - track->time_scale = 1.0; - } - avpriv_set_pts_info(st, 64, matroska->time_scale * track->time_scale, - 1000 * 1000 * 1000); /* 64 bit pts in ns */ - - /* convert the delay from ns to the track timebase */ - track->codec_delay_in_track_tb = av_rescale_q(track->codec_delay, - (AVRational){ 1, 1000000000 }, - st->time_base); - - st->codecpar->codec_id = codec_id; - - if (strcmp(track->language, "und")) - av_dict_set(&st->metadata, "language", track->language, 0); - av_dict_set(&st->metadata, "title", track->name, 0); + par->codec_id = codec_id; if (track->flag_default) st->disposition |= AV_DISPOSITION_DEFAULT; @@ -2854,142 +3179,73 @@ static int matroska_parse_tracks(AVFormatContext *s) st->disposition |= track->flag_original.el.u ? AV_DISPOSITION_ORIGINAL : AV_DISPOSITION_DUB; - if (!st->codecpar->extradata) { - if (extradata) { - st->codecpar->extradata = extradata; - st->codecpar->extradata_size = extradata_size; - } else if (track->codec_priv.data && track->codec_priv.size > 0) { - if (ff_alloc_extradata(st->codecpar, track->codec_priv.size)) - return AVERROR(ENOMEM); - memcpy(st->codecpar->extradata, - track->codec_priv.data + extradata_offset, - track->codec_priv.size); - } + if (key_id_base64) { + /* export encryption key id as base64 metadata tag */ + av_dict_set(&st->metadata, "enc_key_id", key_id_base64, + AV_DICT_DONT_STRDUP_VAL); } - if (track->type == MATROSKA_TRACK_TYPE_VIDEO) { - MatroskaTrackPlane *planes = track->operation.combine_planes.elem; - int display_width_mul = 1; - int display_height_mul = 1; - - st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; - st->codecpar->codec_tag = fourcc; - if (bit_depth >= 0) - st->codecpar->bits_per_coded_sample = bit_depth; - st->codecpar->width = track->video.pixel_width; - st->codecpar->height = track->video.pixel_height; - - if (track->video.interlaced == MATROSKA_VIDEO_INTERLACE_FLAG_INTERLACED) - st->codecpar->field_order = mkv_field_order(matroska, track->video.field_order); - else if (track->video.interlaced == MATROSKA_VIDEO_INTERLACE_FLAG_PROGRESSIVE) - st->codecpar->field_order = AV_FIELD_PROGRESSIVE; - - if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB) - mkv_stereo_mode_display_mul(track->video.stereo_mode, &display_width_mul, &display_height_mul); - - if (track->video.display_unit < MATROSKA_VIDEO_DISPLAYUNIT_UNKNOWN) { - if (track->video.display_width && track->video.display_height && - st->codecpar->height < INT64_MAX / track->video.display_width / display_width_mul && - st->codecpar->width < INT64_MAX / track->video.display_height / display_height_mul) - av_reduce(&st->sample_aspect_ratio.num, - &st->sample_aspect_ratio.den, - st->codecpar->height * track->video.display_width * display_width_mul, - st->codecpar->width * track->video.display_height * display_height_mul, - INT_MAX); - } - if (st->codecpar->codec_id != AV_CODEC_ID_HEVC) - sti->need_parsing = AVSTREAM_PARSE_HEADERS; + if (strcmp(track->language, "und")) + av_dict_set(&st->metadata, "language", track->language, 0); + av_dict_set(&st->metadata, "title", track->name, 0); - if (track->default_duration) { - int div = track->default_duration <= INT64_MAX ? 1 : 2; - av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den, - 1000000000 / div, track->default_duration / div, 30000); -#if FF_API_R_FRAME_RATE - if ( st->avg_frame_rate.num < st->avg_frame_rate.den * 1000LL - && st->avg_frame_rate.num > st->avg_frame_rate.den * 5LL) - st->r_frame_rate = st->avg_frame_rate; -#endif - } + if (track->time_scale < 0.01) { + av_log(matroska->ctx, AV_LOG_WARNING, + "Track TimestampScale too small %f, assuming 1.0.\n", + track->time_scale); + track->time_scale = 1.0; + } - /* export stereo mode flag as metadata tag */ - if (track->video.stereo_mode && track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB) - av_dict_set(&st->metadata, "stereo_mode", ff_matroska_video_stereo_mode[track->video.stereo_mode], 0); - - /* export alpha mode flag as metadata tag */ - if (track->video.alpha_mode) - av_dict_set(&st->metadata, "alpha_mode", "1", 0); - - /* if we have virtual track, mark the real tracks */ - for (j=0; j < track->operation.combine_planes.nb_elem; j++) { - char buf[32]; - if (planes[j].type >= MATROSKA_VIDEO_STEREO_PLANE_COUNT) - continue; - snprintf(buf, sizeof(buf), "%s_%d", - ff_matroska_video_stereo_plane[planes[j].type], i); - for (k=0; k < matroska->tracks.nb_elem; k++) - if (planes[j].uid == tracks[k].uid && tracks[k].stream) { - av_dict_set(&tracks[k].stream->metadata, - "stereo_mode", buf, 0); - break; - } - } - // add stream level stereo3d side data if it is a supported format - if (track->video.stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB && - track->video.stereo_mode != 10 && track->video.stereo_mode != 12) { - int ret = ff_mkv_stereo3d_conv(st, track->video.stereo_mode); - if (ret < 0) - return ret; - } + if (matroska->time_scale * track->time_scale > UINT_MAX) + return AVERROR_INVALIDDATA; - ret = mkv_parse_video_color(st, track); + avpriv_set_pts_info(st, 64, matroska->time_scale * track->time_scale, + 1000 * 1000 * 1000); /* 64 bit pts in ns */ + + /* convert the delay from ns to the track timebase */ + track->codec_delay_in_track_tb = av_rescale_q(track->codec_delay, + (AVRational){ 1, 1000000000 }, + st->time_base); + + type = track->type; + if (par->codec_id == AV_CODEC_ID_WEBVTT) + type = MATROSKA_TRACK_TYPE_SUBTITLE; + switch (type) { + case MATROSKA_TRACK_TYPE_AUDIO: + ret = mka_parse_audio(track, st, par, matroska, + s, &extradata_offset); if (ret < 0) return ret; - ret = mkv_parse_video_projection(st, track, matroska->ctx); + if (ret == SKIP_TRACK) + continue; + break; + case MATROSKA_TRACK_TYPE_VIDEO: + ret = mkv_parse_video(track, st, par, matroska, &extradata_offset); if (ret < 0) return ret; - } else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) { - st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - st->codecpar->codec_tag = fourcc; - st->codecpar->sample_rate = track->audio.out_samplerate; - // channel layout may be already set by codec private checks above - if (!av_channel_layout_check(&st->codecpar->ch_layout)) { - st->codecpar->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; - st->codecpar->ch_layout.nb_channels = track->audio.channels; - } - if (!st->codecpar->bits_per_coded_sample) - st->codecpar->bits_per_coded_sample = track->audio.bitdepth; - if (st->codecpar->codec_id == AV_CODEC_ID_MP3 || - st->codecpar->codec_id == AV_CODEC_ID_MLP || - st->codecpar->codec_id == AV_CODEC_ID_TRUEHD) - sti->need_parsing = AVSTREAM_PARSE_FULL; - else if (st->codecpar->codec_id != AV_CODEC_ID_AAC) - sti->need_parsing = AVSTREAM_PARSE_HEADERS; - if (track->codec_delay > 0) { - st->codecpar->initial_padding = av_rescale_q(track->codec_delay, - (AVRational){1, 1000000000}, - (AVRational){1, st->codecpar->codec_id == AV_CODEC_ID_OPUS ? - 48000 : st->codecpar->sample_rate}); - } - if (track->seek_preroll > 0) { - st->codecpar->seek_preroll = av_rescale_q(track->seek_preroll, - (AVRational){1, 1000000000}, - (AVRational){1, st->codecpar->sample_rate}); - } - } else if (codec_id == AV_CODEC_ID_WEBVTT) { - st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; - - if (!strcmp(track->codec_id, "D_WEBVTT/CAPTIONS")) { - st->disposition |= AV_DISPOSITION_CAPTIONS; - } else if (!strcmp(track->codec_id, "D_WEBVTT/DESCRIPTIONS")) { - st->disposition |= AV_DISPOSITION_DESCRIPTIONS; - } else if (!strcmp(track->codec_id, "D_WEBVTT/METADATA")) { - st->disposition |= AV_DISPOSITION_METADATA; - } - } else if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE) { - st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; + break; + case MATROSKA_TRACK_TYPE_SUBTITLE: + ret = mkv_parse_subtitle_codec(track, st, par, matroska); + if (ret < 0) + return ret; + par->codec_type = AVMEDIA_TYPE_SUBTITLE; if (track->flag_textdescriptions) st->disposition |= AV_DISPOSITION_DESCRIPTIONS; + break; + } + + if (par->codec_id == AV_CODEC_ID_NONE) + av_log(matroska->ctx, AV_LOG_INFO, + "Unknown/unsupported AVCodecID %s.\n", track->codec_id); + + if (!par->extradata && track->codec_priv.size > extradata_offset) { + const uint8_t *src = track->codec_priv.data + extradata_offset; + unsigned extra_size = track->codec_priv.size - extradata_offset; + ret = ff_alloc_extradata(par, extra_size); + if (ret < 0) + return ret; + memcpy(par->extradata, src, extra_size); } ret = mkv_parse_block_addition_mappings(s, st, track); @@ -3047,6 +3303,8 @@ static int matroska_read_header(AVFormatContext *s) return AVERROR_INVALIDDATA; } } + matroska->is_webm = !strcmp(ebml.doctype, "webm"); + ebml_free(ebml_syntax, &ebml); matroska->pkt = si->parse_pkt; @@ -3580,12 +3838,102 @@ static int matroska_parse_webvtt(MatroskaDemuxContext *matroska, return 0; } +static int matroska_parse_block_additional(MatroskaDemuxContext *matroska, + MatroskaTrack *track, AVPacket *pkt, + const uint8_t *data, int size, uint64_t id) +{ + const EbmlList *mappings_list = &track->block_addition_mappings; + MatroskaBlockAdditionMapping *mappings = mappings_list->elem, *mapping = NULL; + uint8_t *side_data; + int res; + + if (!matroska->is_webm && track->max_block_additional_id && id > track->max_block_additional_id) { + int strict = matroska->ctx->strict_std_compliance >= FF_COMPLIANCE_STRICT; + av_log(matroska->ctx, strict ? AV_LOG_ERROR : AV_LOG_WARNING, + "BlockAddID %"PRIu64" is higher than the reported MaxBlockAdditionID %"PRIu64" " + "for Track with TrackNumber %"PRIu64"\n", id, track->max_block_additional_id, + track->num); + if (strict) + return AVERROR_INVALIDDATA; + } + + for (int i = 0; i < mappings_list->nb_elem; i++) { + if (id != mappings[i].value) + continue; + mapping = &mappings[i]; + break; + } + + if (id != 1 && !matroska->is_webm && !mapping) { + av_log(matroska->ctx, AV_LOG_WARNING, "BlockAddID %"PRIu64" has no mapping. Skipping\n", id); + return 0; + } + + if (mapping && mapping->type) + id = mapping->type; + + switch (id) { + case MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35: { + GetByteContext bc; + int country_code, provider_code; + int provider_oriented_code, application_identifier; + size_t hdrplus_size; + AVDynamicHDRPlus *hdrplus; + + if (size < 6) + break; //ignore + + bytestream2_init(&bc, data, size); + + /* ITU-T T.35 metadata */ + country_code = bytestream2_get_byteu(&bc); + provider_code = bytestream2_get_be16u(&bc); + + if (country_code != ITU_T_T35_COUNTRY_CODE_US || + provider_code != ITU_T_T35_PROVIDER_CODE_SMTPE) + break; // ignore + + provider_oriented_code = bytestream2_get_be16u(&bc); + application_identifier = bytestream2_get_byteu(&bc); + + if (provider_oriented_code != 1 || application_identifier != 4) + break; // ignore + + hdrplus = av_dynamic_hdr_plus_alloc(&hdrplus_size); + if (!hdrplus) + return AVERROR(ENOMEM); + + if ((res = av_dynamic_hdr_plus_from_t35(hdrplus, bc.buffer, + bytestream2_get_bytes_left(&bc))) < 0 || + (res = av_packet_add_side_data(pkt, AV_PKT_DATA_DYNAMIC_HDR10_PLUS, + (uint8_t *)hdrplus, hdrplus_size)) < 0) { + av_free(hdrplus); + return res; + } + + return 0; + } + default: + break; + } + + side_data = av_packet_new_side_data(pkt, AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, + size + (size_t)8); + if (!side_data) + return AVERROR(ENOMEM); + + AV_WB64(side_data, id); + memcpy(side_data + 8, data, size); + + return 0; +} + static int matroska_parse_frame(MatroskaDemuxContext *matroska, MatroskaTrack *track, AVStream *st, AVBufferRef *buf, uint8_t *data, int pkt_size, uint64_t timecode, uint64_t lace_duration, int64_t pos, int is_keyframe, - uint8_t *additional, uint64_t additional_id, int additional_size, + MatroskaBlockMore *blockmore, int nb_blockmore, int64_t discard_padding) { uint8_t *pkt_data = data; @@ -3617,9 +3965,20 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, buf = NULL; } - if (!pkt_size && !additional_size) + if (!pkt_size && !nb_blockmore) goto no_output; + if (!matroska->is_webm && nb_blockmore && !track->max_block_additional_id) { + int strict = matroska->ctx->strict_std_compliance >= FF_COMPLIANCE_STRICT; + av_log(matroska->ctx, strict ? AV_LOG_ERROR : AV_LOG_WARNING, + "Unexpected BlockAdditions found in a Block from Track with TrackNumber %"PRIu64" " + "where MaxBlockAdditionID is 0\n", track->num); + if (strict) { + res = AVERROR_INVALIDDATA; + goto fail; + } + } + if (!buf) pkt->buf = av_buffer_create(pkt_data, pkt_size + AV_INPUT_BUFFER_PADDING_SIZE, NULL, NULL, 0); @@ -3636,16 +3995,18 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, pkt->flags = is_keyframe; pkt->stream_index = st->index; - if (additional_size > 0) { - uint8_t *side_data = av_packet_new_side_data(pkt, - AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, - additional_size + 8); - if (!side_data) { + for (int i = 0; i < nb_blockmore; i++) { + MatroskaBlockMore *more = &blockmore[i]; + + if (!more->additional.size) + continue; + + res = matroska_parse_block_additional(matroska, track, pkt, more->additional.data, + more->additional.size, more->additional_id); + if (res < 0) { av_packet_unref(pkt); - return AVERROR(ENOMEM); + return res; } - AV_WB64(side_data, additional_id); - memcpy(side_data + 8, additional, additional_size); } if (discard_padding) { @@ -3691,7 +4052,7 @@ static int matroska_parse_frame(MatroskaDemuxContext *matroska, static int matroska_parse_block(MatroskaDemuxContext *matroska, AVBufferRef *buf, uint8_t *data, int size, int64_t pos, uint64_t cluster_time, uint64_t block_duration, int is_keyframe, - uint8_t *additional, uint64_t additional_id, int additional_size, + MatroskaBlockMore *blockmore, int nb_blockmore, int64_t cluster_pos, int64_t discard_padding) { uint64_t timecode = AV_NOPTS_VALUE; @@ -3707,7 +4068,7 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, AVBufferRef *buf av_assert1(buf); - ffio_init_context(&pb, data, size, 0, NULL, NULL, NULL, NULL); + ffio_init_read_context(&pb, data, size); if ((n = ebml_read_num(matroska, &pb.pub, 8, &num, 1)) < 0) return n; @@ -3826,7 +4187,7 @@ static int matroska_parse_block(MatroskaDemuxContext *matroska, AVBufferRef *buf res = matroska_parse_frame(matroska, track, st, buf, out_data, out_size, timecode, lace_duration, pos, !n ? is_keyframe : 0, - additional, additional_id, additional_size, + blockmore, nb_blockmore, discard_padding); if (res) return res; @@ -3846,7 +4207,7 @@ static int matroska_parse_cluster(MatroskaDemuxContext *matroska) MatroskaBlock *block = &cluster->block; int res; - av_assert0(matroska->num_levels <= 2); + av_assert0(matroska->num_levels <= 2U); if (matroska->num_levels == 1) { res = ebml_parse(matroska, matroska_segment, NULL); @@ -3867,14 +4228,12 @@ static int matroska_parse_cluster(MatroskaDemuxContext *matroska) if (res >= 0 && block->bin.size > 0) { int is_keyframe = block->non_simple ? block->reference.count == 0 : -1; - uint8_t* additional = block->additional.size > 0 ? - block->additional.data : NULL; res = matroska_parse_block(matroska, block->bin.buf, block->bin.data, block->bin.size, block->bin.pos, cluster->timecode, block->duration, - is_keyframe, additional, block->additional_id, - block->additional.size, cluster->pos, + is_keyframe, block->blockmore.elem, + block->blockmore.nb_elem, cluster->pos, block->discard_padding); } @@ -4432,28 +4791,28 @@ static const AVClass webm_dash_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_webm_dash_manifest_demuxer = { - .name = "webm_dash_manifest", - .long_name = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"), - .priv_class = &webm_dash_class, +const FFInputFormat ff_webm_dash_manifest_demuxer = { + .p.name = "webm_dash_manifest", + .p.long_name = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"), + .p.priv_class = &webm_dash_class, .priv_data_size = sizeof(MatroskaDemuxContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_header = webm_dash_manifest_read_header, .read_packet = webm_dash_manifest_read_packet, .read_close = matroska_read_close, }; #endif -const AVInputFormat ff_matroska_demuxer = { - .name = "matroska,webm", - .long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"), - .extensions = "mkv,mk3d,mka,mks,webm", +const FFInputFormat ff_matroska_demuxer = { + .p.name = "matroska,webm", + .p.long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"), + .p.extensions = "mkv,mk3d,mka,mks,webm", + .p.mime_type = "audio/webm,audio/x-matroska,video/webm,video/x-matroska", .priv_data_size = sizeof(MatroskaDemuxContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = matroska_probe, .read_header = matroska_read_header, .read_packet = matroska_read_packet, .read_close = matroska_read_close, .read_seek = matroska_read_seek, - .mime_type = "audio/webm,audio/x-matroska,video/webm,video/x-matroska" }; diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c index 113541bd9a2..0de4ec1dc03 100644 --- a/libavformat/matroskaenc.c +++ b/libavformat/matroskaenc.c @@ -44,6 +44,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/crc.h" #include "libavutil/dict.h" +#include "libavutil/hdr_dynamic_metadata.h" #include "libavutil/intfloat.h" #include "libavutil/intreadwrite.h" #include "libavutil/lfg.h" @@ -58,7 +59,11 @@ #include "libavutil/stereo3d.h" #include "libavcodec/av1.h" +#include "libavcodec/bytestream.h" #include "libavcodec/codec_desc.h" +#include "libavcodec/codec_par.h" +#include "libavcodec/defs.h" +#include "libavcodec/itut35.h" #include "libavcodec/xiph.h" #include "libavcodec/mpeg4audio.h" @@ -187,9 +192,13 @@ typedef struct mkv_track { int64_t last_timestamp; int64_t duration; int64_t duration_offset; + uint64_t max_blockaddid; + int64_t blockadditionmapping_offset; int codecpriv_offset; unsigned codecpriv_size; ///< size reserved for CodecPrivate excluding header+length field int64_t ts_offset; + uint64_t default_duration_low; + uint64_t default_duration_high; /* This callback will be called twice: First with a NULL AVIOContext * to return the size of the (Simple)Block's data via size * and a second time with the AVIOContext set when the data @@ -259,8 +268,12 @@ typedef struct MatroskaMuxContext { /** 4 * (1-byte EBML ID, 1-byte EBML size, 8-byte uint max) */ #define MAX_CUETRACKPOS_SIZE 40 -/** 2 + 1 Simpletag header, 2 + 1 + 8 Name "DURATION", 23B for TagString */ -#define DURATION_SIMPLETAG_SIZE (2 + 1 + (2 + 1 + 8) + 23) +/** DURATION_STRING_LENGTH must be <= 112 or the containing + * simpletag will need more than one byte for its length field. */ +#define DURATION_STRING_LENGTH 19 + +/** 2 + 1 Simpletag header, 2 + 1 + 8 Name "DURATION", rest for TagString */ +#define DURATION_SIMPLETAG_SIZE (2 + 1 + (2 + 1 + 8) + (2 + 1 + DURATION_STRING_LENGTH)) /** Seek preroll value for opus */ #define OPUS_SEEK_PREROLL 80000000 @@ -1142,6 +1155,27 @@ static int mkv_assemble_native_codecprivate(AVFormatContext *s, AVIOContext *dyn else *size_to_reserve = MAX_PCE_SIZE; break; + case AV_CODEC_ID_ARIB_CAPTION: { + unsigned stream_identifier, data_component_id; + switch (par->profile) { + case AV_PROFILE_ARIB_PROFILE_A: + stream_identifier = 0x30; + data_component_id = 0x0008; + break; + case AV_PROFILE_ARIB_PROFILE_C: + stream_identifier = 0x87; + data_component_id = 0x0012; + break; + default: + av_log(s, AV_LOG_ERROR, + "Unset/unknown ARIB caption profile %d utilized!\n", + par->profile); + return AVERROR_INVALIDDATA; + } + avio_w8(dyn_cp, stream_identifier); + avio_wb16(dyn_cp, data_component_id); + break; + } #endif default: if (CONFIG_MATROSKA_MUXER && par->codec_id == AV_CODEC_ID_PRORES && @@ -1297,7 +1331,7 @@ static int mkv_update_codecprivate(AVFormatContext *s, MatroskaMuxContext *mkv, static void mkv_write_video_color(EbmlWriter *writer, const AVStream *st, const AVCodecParameters *par) { - const void *side_data; + const AVPacketSideData *side_data; ebml_writer_open_master(writer, MATROSKA_ID_VIDEOCOLOR); @@ -1331,20 +1365,20 @@ static void mkv_write_video_color(EbmlWriter *writer, const AVStream *st, (ypos >> 7) + 1); } - side_data = av_stream_get_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, - NULL); + side_data = av_packet_side_data_get(st->codecpar->coded_side_data, st->codecpar->nb_coded_side_data, + AV_PKT_DATA_CONTENT_LIGHT_LEVEL); if (side_data) { - const AVContentLightMetadata *metadata = side_data; + const AVContentLightMetadata *metadata = (AVContentLightMetadata *)side_data->data; ebml_writer_add_uint(writer, MATROSKA_ID_VIDEOCOLORMAXCLL, metadata->MaxCLL); ebml_writer_add_uint(writer, MATROSKA_ID_VIDEOCOLORMAXFALL, metadata->MaxFALL); } - side_data = av_stream_get_side_data(st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, - NULL); + side_data = av_packet_side_data_get(st->codecpar->coded_side_data, st->codecpar->nb_coded_side_data, + AV_PKT_DATA_MASTERING_DISPLAY_METADATA); if (side_data) { - const AVMasteringDisplayMetadata *metadata = side_data; + const AVMasteringDisplayMetadata *metadata = (AVMasteringDisplayMetadata *)side_data->data; ebml_writer_open_master(writer, MATROSKA_ID_VIDEOCOLORMASTERINGMETA); if (metadata->has_primaries) { ebml_writer_add_float(writer, MATROSKA_ID_VIDEOCOLOR_RX, @@ -1377,25 +1411,81 @@ static void mkv_write_video_color(EbmlWriter *writer, const AVStream *st, } #define MAX_VIDEO_PROJECTION_ELEMS 6 -static void mkv_write_video_projection(AVFormatContext *s, EbmlWriter *writer, - const AVStream *st, uint8_t private[]) +static void mkv_handle_rotation(void *logctx, const AVStream *st, + double *yaw, double *roll) { - const AVSphericalMapping *spherical = - (const AVSphericalMapping *)av_stream_get_side_data(st, AV_PKT_DATA_SPHERICAL, - NULL); + const int32_t *matrix; + const AVPacketSideData *side_data = + av_packet_side_data_get(st->codecpar->coded_side_data, st->codecpar->nb_coded_side_data, + AV_PKT_DATA_DISPLAYMATRIX); - if (!spherical) + if (!side_data) + return; + + matrix = (int32_t *)side_data->data; + + /* Check whether this is an affine transformation */ + if (matrix[2] || matrix[5]) + goto ignore; + + /* This together with the checks below test whether + * the upper-left 2x2 matrix is nonsingular. */ + if (!matrix[0] && !matrix[1]) + goto ignore; + + /* We ignore the translation part of the matrix (matrix[6] and matrix[7]) + * as well as any scaling, i.e. we only look at the upper left 2x2 matrix. + * We only accept matrices that are an exact multiple of an orthogonal one. + * Apart from the multiple, every such matrix can be obtained by + * potentially flipping in the x-direction (corresponding to yaw = 180) + * followed by a rotation of (say) an angle phi in the counterclockwise + * direction. The upper-left 2x2 matrix then looks like this: + * | (+/-)cos(phi) (-/+)sin(phi) | + * scale * | | + * | sin(phi) cos(phi) | + * The first set of signs in the first row apply in case of no flipping, + * the second set applies in case of flipping. */ + + /* The casts to int64_t are needed because -INT32_MIN doesn't fit + * in an int32_t. */ + if (matrix[0] == matrix[4] && -(int64_t)matrix[1] == matrix[3]) { + /* No flipping case */ + *yaw = 0; + } else if (-(int64_t)matrix[0] == matrix[4] && matrix[1] == matrix[3]) { + /* Horizontal flip */ + *yaw = 180; + } else { +ignore: + av_log(logctx, AV_LOG_INFO, "Ignoring display matrix indicating " + "non-orthogonal transformation.\n"); return; + } + *roll = 180 / M_PI * atan2(matrix[3], matrix[4]); + + /* We do not write a ProjectionType element indicating "rectangular", + * because this is the default value. */ +} + +static int mkv_handle_spherical(void *logctx, EbmlWriter *writer, + const AVStream *st, uint8_t private[], + double *yaw, double *pitch, double *roll) +{ + const AVPacketSideData *sd = av_packet_side_data_get(st->codecpar->coded_side_data, + st->codecpar->nb_coded_side_data, + AV_PKT_DATA_SPHERICAL); + const AVSphericalMapping *spherical; + + if (!sd) + return 0; + spherical = (const AVSphericalMapping *)sd->data; if (spherical->projection != AV_SPHERICAL_EQUIRECTANGULAR && spherical->projection != AV_SPHERICAL_EQUIRECTANGULAR_TILE && spherical->projection != AV_SPHERICAL_CUBEMAP) { - av_log(s, AV_LOG_WARNING, "Unknown projection type\n"); - return; + av_log(logctx, AV_LOG_WARNING, "Unknown projection type\n"); + return 0; } - ebml_writer_open_master(writer, MATROSKA_ID_VIDEOPROJECTION); - switch (spherical->projection) { case AV_SPHERICAL_EQUIRECTANGULAR: case AV_SPHERICAL_EQUIRECTANGULAR_TILE: @@ -1429,17 +1519,33 @@ static void mkv_write_video_projection(AVFormatContext *s, EbmlWriter *writer, av_assert0(0); } - if (spherical->yaw) - ebml_writer_add_float(writer, MATROSKA_ID_VIDEOPROJECTIONPOSEYAW, - (double) spherical->yaw / (1 << 16)); - if (spherical->pitch) - ebml_writer_add_float(writer, MATROSKA_ID_VIDEOPROJECTIONPOSEPITCH, - (double) spherical->pitch / (1 << 16)); - if (spherical->roll) - ebml_writer_add_float(writer, MATROSKA_ID_VIDEOPROJECTIONPOSEROLL, - (double) spherical->roll / (1 << 16)); + *yaw = (double) spherical->yaw / (1 << 16); + *pitch = (double) spherical->pitch / (1 << 16); + *roll = (double) spherical->roll / (1 << 16); - ebml_writer_close_master(writer); + return 1; /* Projection included */ +} + +static void mkv_write_video_projection(void *logctx, EbmlWriter *wr, + const AVStream *st, uint8_t private[]) +{ + double yaw = 0, pitch = 0, roll = 0; + int ret; + + ebml_writer_open_master(wr, MATROSKA_ID_VIDEOPROJECTION); + + ret = mkv_handle_spherical(logctx, wr, st, private, &yaw, &pitch, &roll); + if (!ret) + mkv_handle_rotation(logctx, st, &yaw, &roll); + + if (yaw) + ebml_writer_add_float(wr, MATROSKA_ID_VIDEOPROJECTIONPOSEYAW, yaw); + if (pitch) + ebml_writer_add_float(wr, MATROSKA_ID_VIDEOPROJECTIONPOSEPITCH, pitch); + if (roll) + ebml_writer_add_float(wr, MATROSKA_ID_VIDEOPROJECTIONPOSEROLL, roll); + + ebml_writer_close_or_discard_master(wr); } #define MAX_FIELD_ORDER_ELEMS 2 @@ -1484,104 +1590,135 @@ static void mkv_write_field_order(EbmlWriter *writer, int is_webm, #define MAX_STEREO_MODE_ELEMS 1 static int mkv_write_stereo_mode(AVFormatContext *s, EbmlWriter *writer, - AVStream *st, int is_webm, + const AVStream *st, int is_webm, int *h_width, int *h_height) { + const char *error_message_addendum = ""; const AVDictionaryEntry *tag; MatroskaVideoStereoModeType format = MATROSKA_VIDEO_STEREOMODE_TYPE_NB; - const AVStereo3D *stereo; + + /* The following macros create bitfields where the ith bit + * indicates whether the MatroskaVideoStereoModeType with that value + * uses double width/height or is WebM compatible. */ +#define FLAG(STEREOMODETYPE, BOOL) | (BOOL) << (STEREOMODETYPE) +#define WDIV1(STEREOMODETYPE, STEREO3DTYPE, FLAGS, WDIV, HDIV, WEBM) \ + FLAG(STEREOMODETYPE, WDIV) +#define WDIV2(STEREOMODETYPE, WDIV, HDIV, WEBM) \ + FLAG(STEREOMODETYPE, WDIV) + // The zero in the following line consumes the first '|'. + const unsigned width_bitfield = 0 STEREOMODE_STEREO3D_MAPPING(WDIV1, WDIV2); + +#define HDIV1(STEREOMODETYPE, STEREO3DTYPE, FLAGS, WDIV, HDIV, WEBM) \ + FLAG(STEREOMODETYPE, HDIV) +#define HDIV2(STEREOMODETYPE, WDIV, HDIV, WEBM) \ + FLAG(STEREOMODETYPE, HDIV) + const unsigned height_bitfield = 0 STEREOMODE_STEREO3D_MAPPING(HDIV1, HDIV2); + +#define WEBM1(STEREOMODETYPE, STEREO3DTYPE, FLAGS, WDIV, HDIV, WEBM) \ + FLAG(STEREOMODETYPE, WEBM) +#define WEBM2(STEREOMODETYPE, WDIV, HDIV, WEBM) \ + FLAG(STEREOMODETYPE, WEBM) + const unsigned webm_bitfield = 0 STEREOMODE_STEREO3D_MAPPING(WEBM1, WEBM2); *h_width = 1; *h_height = 1; - // convert metadata into proper side data and add it to the stream + if ((tag = av_dict_get(st->metadata, "stereo_mode", NULL, 0)) || (tag = av_dict_get( s->metadata, "stereo_mode", NULL, 0))) { - int stereo_mode = atoi(tag->value); for (int i = 0; i < MATROSKA_VIDEO_STEREOMODE_TYPE_NB; i++) if (!strcmp(tag->value, ff_matroska_video_stereo_mode[i])){ - stereo_mode = i; + format = i; break; } - - if (stereo_mode < MATROSKA_VIDEO_STEREOMODE_TYPE_NB && - stereo_mode != 10 && stereo_mode != 12) { - int ret = ff_mkv_stereo3d_conv(st, stereo_mode); - if (ret < 0) - return ret; - } - } - - stereo = (const AVStereo3D*)av_stream_get_side_data(st, AV_PKT_DATA_STEREO3D, - NULL); - if (stereo) { - switch (stereo->type) { - case AV_STEREO3D_2D: - format = MATROSKA_VIDEO_STEREOMODE_TYPE_MONO; - break; - case AV_STEREO3D_SIDEBYSIDE: - format = (stereo->flags & AV_STEREO3D_FLAG_INVERT) - ? MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT - : MATROSKA_VIDEO_STEREOMODE_TYPE_LEFT_RIGHT; - *h_width = 2; - break; - case AV_STEREO3D_TOPBOTTOM: - format = MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM; - if (stereo->flags & AV_STEREO3D_FLAG_INVERT) - format--; - *h_height = 2; - break; - case AV_STEREO3D_CHECKERBOARD: - format = MATROSKA_VIDEO_STEREOMODE_TYPE_CHECKERBOARD_LR; - if (stereo->flags & AV_STEREO3D_FLAG_INVERT) - format--; - break; - case AV_STEREO3D_LINES: - format = MATROSKA_VIDEO_STEREOMODE_TYPE_ROW_INTERLEAVED_LR; - if (stereo->flags & AV_STEREO3D_FLAG_INVERT) - format--; - *h_height = 2; - break; - case AV_STEREO3D_COLUMNS: - format = MATROSKA_VIDEO_STEREOMODE_TYPE_COL_INTERLEAVED_LR; - if (stereo->flags & AV_STEREO3D_FLAG_INVERT) - format--; - *h_width = 2; - break; - case AV_STEREO3D_FRAMESEQUENCE: - format = MATROSKA_VIDEO_STEREOMODE_TYPE_BOTH_EYES_BLOCK_LR; - if (stereo->flags & AV_STEREO3D_FLAG_INVERT) - format++; - break; + if (format == MATROSKA_VIDEO_STEREOMODE_TYPE_NB) { + long stereo_mode = strtol(tag->value, NULL, 0); + if ((unsigned long)stereo_mode >= MATROSKA_VIDEO_STEREOMODE_TYPE_NB) + goto fail; + format = stereo_mode; } + } else { + const AVPacketSideData *sd; + const AVStereo3D *stereo; + /* The following macro presumes all MATROSKA_VIDEO_STEREOMODE_TYPE_* + * values to be in the range 0..254. */ +#define STEREOMODE(STEREOMODETYPE, STEREO3DTYPE, FLAGS, WDIV, HDIV, WEBM) \ + [(STEREO3DTYPE)][!!((FLAGS) & AV_STEREO3D_FLAG_INVERT)] = (STEREOMODETYPE) + 1, +#define NOTHING(STEREOMODETYPE, WDIV, HDIV, WEBM) + static const unsigned char conversion_table[][2] = { + STEREOMODE_STEREO3D_MAPPING(STEREOMODE, NOTHING) + }; + int fmt; + + sd = av_packet_side_data_get(st->codecpar->coded_side_data, st->codecpar->nb_coded_side_data, + AV_PKT_DATA_STEREO3D); + if (!sd) + return 0; + + stereo = (const AVStereo3D*)sd->data; + + /* A garbage AVStereo3D or something with no Matroska analogon. */ + if ((unsigned)stereo->type >= FF_ARRAY_ELEMS(conversion_table)) + return 0; + + fmt = conversion_table[stereo->type][!!(stereo->flags & AV_STEREO3D_FLAG_INVERT)]; + /* Format with no Matroska analogon; ignore it */ + if (!fmt) + return 0; + format = fmt - 1; } - if (format == MATROSKA_VIDEO_STEREOMODE_TYPE_NB) - return 0; - // if webm, do not write unsupported modes - if ((is_webm && - format > MATROSKA_VIDEO_STEREOMODE_TYPE_TOP_BOTTOM && - format != MATROSKA_VIDEO_STEREOMODE_TYPE_RIGHT_LEFT) - || format >= MATROSKA_VIDEO_STEREOMODE_TYPE_NB) { - av_log(s, AV_LOG_ERROR, - "The specified stereo mode is not valid.\n"); - return AVERROR(EINVAL); + if (is_webm && !(webm_bitfield >> format)) { + error_message_addendum = " for WebM"; + goto fail; } + *h_width = 1 << ((width_bitfield >> format) & 1); + *h_height = 1 << ((height_bitfield >> format) & 1); + // write StereoMode if format is valid ebml_writer_add_uint(writer, MATROSKA_ID_VIDEOSTEREOMODE, format); return 0; +fail: + av_log(s, AV_LOG_ERROR, + "The specified stereo mode is not valid%s.\n", + error_message_addendum); + return AVERROR(EINVAL); } -static void mkv_write_dovi(AVFormatContext *s, AVIOContext *pb, AVStream *st) +static void mkv_write_blockadditionmapping(AVFormatContext *s, const MatroskaMuxContext *mkv, + const AVCodecParameters *par, AVIOContext *pb, + mkv_track *track, const AVStream *st) { #if CONFIG_MATROSKA_MUXER - AVDOVIDecoderConfigurationRecord *dovi = (AVDOVIDecoderConfigurationRecord *) - av_stream_get_side_data(st, AV_PKT_DATA_DOVI_CONF, NULL); + const AVDOVIDecoderConfigurationRecord *dovi; + const AVPacketSideData *sd; + + if (IS_SEEKABLE(s->pb, mkv)) { + track->blockadditionmapping_offset = avio_tell(pb); + // We can't know at this point if there will be a block with BlockAdditions, so + // we either write the default value here, or a void element. Either of them will + // be overwritten when finishing the track. + put_ebml_uint(pb, MATROSKA_ID_TRACKMAXBLKADDID, 0); + if (par->codec_type == AVMEDIA_TYPE_VIDEO) { + // Similarly, reserve space for an eventual + // HDR10+ ITU T.35 metadata BlockAdditionMapping. + put_ebml_void(pb, 3 /* BlockAdditionMapping */ + + 4 /* BlockAddIDValue */ + + 4 /* BlockAddIDType */); + } + } + + sd = av_packet_side_data_get(st->codecpar->coded_side_data, st->codecpar->nb_coded_side_data, + AV_PKT_DATA_DOVI_CONF); + + if (!sd) + return; - if (dovi && dovi->dv_profile <= 10) { + dovi = (const AVDOVIDecoderConfigurationRecord *)sd->data; + if (dovi->dv_profile <= 10) { ebml_master mapping; uint8_t buf[ISOM_DVCC_DVVC_SIZE]; uint32_t type; @@ -1590,9 +1727,9 @@ static void mkv_write_dovi(AVFormatContext *s, AVIOContext *pb, AVStream *st) + (2 + 1 + 4) + (2 + 1 + ISOM_DVCC_DVVC_SIZE); if (dovi->dv_profile > 7) { - type = MKBETAG('d', 'v', 'v', 'C'); + type = MATROSKA_BLOCK_ADD_ID_TYPE_DVVC; } else { - type = MKBETAG('d', 'v', 'c', 'C'); + type = MATROSKA_BLOCK_ADD_ID_TYPE_DVCC; } ff_isom_put_dvcc_dvvc(s, buf, dovi); @@ -1609,7 +1746,7 @@ static void mkv_write_dovi(AVFormatContext *s, AVIOContext *pb, AVStream *st) } static int mkv_write_track_video(AVFormatContext *s, MatroskaMuxContext *mkv, - AVStream *st, const AVCodecParameters *par, + const AVStream *st, const AVCodecParameters *par, AVIOContext *pb) { const AVDictionaryEntry *tag; @@ -1634,11 +1771,10 @@ static int mkv_write_track_video(AVFormatContext *s, MatroskaMuxContext *mkv, if (ret < 0) return ret; - if (((tag = av_dict_get(st->metadata, "alpha_mode", NULL, 0)) && atoi(tag->value)) || - ((tag = av_dict_get( s->metadata, "alpha_mode", NULL, 0)) && atoi(tag->value)) || - (par->format == AV_PIX_FMT_YUVA420P)) { + if (par->format == AV_PIX_FMT_YUVA420P || + ((tag = av_dict_get(st->metadata, "alpha_mode", NULL, 0)) || + (tag = av_dict_get( s->metadata, "alpha_mode", NULL, 0))) && strtol(tag->value, NULL, 0)) ebml_writer_add_uint(&writer, MATROSKA_ID_VIDEOALPHAMODE, 1); - } // write DisplayWidth and DisplayHeight, they contain the size of // a single source view and/or the display aspect ratio @@ -1688,6 +1824,16 @@ static int mkv_write_track_video(AVFormatContext *s, MatroskaMuxContext *mkv, return ebml_writer_write(&writer, pb); } +static void mkv_write_default_duration(mkv_track *track, AVIOContext *pb, + AVRational duration) +{ + put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, + 1000000000LL * duration.num / duration.den); + track->default_duration_low = 1000LL * duration.num / duration.den; + track->default_duration_high = track->default_duration_low + + !!(1000LL * duration.num % duration.den); +} + static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, AVStream *st, mkv_track *track, AVIOContext *pb, int is_default) @@ -1726,7 +1872,7 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, if (IS_WEBM(mkv)) { const char *codec_id; - if (par->codec_type != AVMEDIA_TYPE_SUBTITLE) { + if (par->codec_id != AV_CODEC_ID_WEBVTT) { for (j = 0; ff_webm_codec_tags[j].id != AV_CODEC_ID_NONE; j++) { if (ff_webm_codec_tags[j].id == par->codec_id) { codec_id = ff_webm_codec_tags[j].str; @@ -1734,7 +1880,7 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, break; } } - } else if (par->codec_id == AV_CODEC_ID_WEBVTT) { + } else { if (st->disposition & AV_DISPOSITION_CAPTIONS) { codec_id = "D_WEBVTT/CAPTIONS"; native_id = MATROSKA_TRACK_TYPE_SUBTITLE; @@ -1772,9 +1918,13 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, // look for a codec ID string specific to mkv to use, // if none are found, use AVI codes - if (par->codec_id != AV_CODEC_ID_RAWVIDEO || par->codec_tag) { + if (par->codec_id == AV_CODEC_ID_FFV1) { + /* FFV1 is actually supported natively in Matroska, + * yet we use the VfW way to mux it for compatibility + * with old demuxers. (FIXME: Are they really important?) */ + } else if (par->codec_id != AV_CODEC_ID_RAWVIDEO || par->codec_tag) { for (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) { - if (ff_mkv_codec_tags[j].id == par->codec_id && par->codec_id != AV_CODEC_ID_FFV1) { + if (ff_mkv_codec_tags[j].id == par->codec_id) { put_ebml_string(pb, MATROSKA_ID_CODECID, ff_mkv_codec_tags[j].str); native_id = 1; break; @@ -1792,16 +1942,21 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, } switch (par->codec_type) { + AVRational frame_rate; + int audio_frame_samples; + case AVMEDIA_TYPE_VIDEO: mkv->have_video = 1; put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_VIDEO); - if( st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0 - && av_cmp_q(av_inv_q(st->avg_frame_rate), st->time_base) > 0) - put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, 1000000000LL * st->avg_frame_rate.den / st->avg_frame_rate.num); - else if( st->r_frame_rate.num > 0 && st->r_frame_rate.den > 0 - && av_cmp_q(av_inv_q(st->r_frame_rate), st->time_base) > 0) - put_ebml_uint(pb, MATROSKA_ID_TRACKDEFAULTDURATION, 1000000000LL * st->r_frame_rate.den / st->r_frame_rate.num); + frame_rate = (AVRational){ 0, 1 }; + if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0) + frame_rate = st->avg_frame_rate; + else if (st->r_frame_rate.num > 0 && st->r_frame_rate.den > 0) + frame_rate = st->r_frame_rate; + + if (frame_rate.num > 0) + mkv_write_default_duration(track, pb, av_inv_q(frame_rate)); if (CONFIG_MATROSKA_MUXER && !native_id && ff_codec_get_tag(ff_codec_movvideo_tags, par->codec_id) && @@ -1824,9 +1979,6 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, if (ret < 0) return ret; - if (!IS_WEBM(mkv)) - mkv_write_dovi(s, pb, st); - break; case AVMEDIA_TYPE_AUDIO: @@ -1858,6 +2010,10 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, put_ebml_uint(pb, MATROSKA_ID_TRACKTYPE, MATROSKA_TRACK_TYPE_AUDIO); + audio_frame_samples = av_get_audio_frame_duration2(par, 0); + if (audio_frame_samples) + mkv_write_default_duration(track, pb, (AVRational){ audio_frame_samples, + par->sample_rate }); if (!native_id) // no mkv-specific ID, use ACM mode put_ebml_string(pb, MATROSKA_ID_CODECID, "A_MS/ACM"); @@ -1902,6 +2058,9 @@ static int mkv_write_track(AVFormatContext *s, MatroskaMuxContext *mkv, return AVERROR(EINVAL); } + if (!IS_WEBM(mkv)) + mkv_write_blockadditionmapping(s, mkv, par, pb, track, st); + if (!IS_WEBM(mkv) || par->codec_id != AV_CODEC_ID_WEBVTT) { uint8_t *codecpriv; int codecpriv_size, max_payload_size; @@ -2585,17 +2744,28 @@ static int webm_reformat_vtt(MatroskaMuxContext *mkv, AVIOContext *pb, return 0; } +static void mkv_write_blockadditional(EbmlWriter *writer, const uint8_t *buf, + size_t size, uint64_t additional_id) +{ + ebml_writer_open_master(writer, MATROSKA_ID_BLOCKMORE); + ebml_writer_add_uint(writer, MATROSKA_ID_BLOCKADDID, additional_id); + ebml_writer_add_bin (writer, MATROSKA_ID_BLOCKADDITIONAL, buf, size); + ebml_writer_close_master(writer); +} + static int mkv_write_block(void *logctx, MatroskaMuxContext *mkv, AVIOContext *pb, const AVCodecParameters *par, mkv_track *track, const AVPacket *pkt, int keyframe, int64_t ts, uint64_t duration, int force_blockgroup, int64_t relative_packet_pos) { + uint8_t t35_buf[6 + AV_HDR_PLUS_MAX_PAYLOAD_SIZE]; uint8_t *side_data; size_t side_data_size; uint64_t additional_id; unsigned track_number = track->track_num; - EBML_WRITER(9); + EBML_WRITER(12); + int ret; mkv->cur_block.track = track; mkv->cur_block.pkt = pkt; @@ -2607,7 +2777,12 @@ static int mkv_write_block(void *logctx, MatroskaMuxContext *mkv, ebml_writer_open_master(&writer, MATROSKA_ID_BLOCKGROUP); ebml_writer_add_block(&writer, mkv); - if (duration) + if (duration > 0 && (par->codec_type == AVMEDIA_TYPE_SUBTITLE || + /* If the packet's duration is inconsistent with the default duration, + * add an explicit duration element. */ + track->default_duration_high > 0 && + duration != track->default_duration_high && + duration != track->default_duration_low)) ebml_writer_add_uint(&writer, MATROSKA_ID_BLOCKDURATION, duration); av_log(logctx, AV_LOG_DEBUG, @@ -2630,23 +2805,45 @@ static int mkv_write_block(void *logctx, MatroskaMuxContext *mkv, } } + ebml_writer_open_master(&writer, MATROSKA_ID_BLOCKADDITIONS); side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL, &side_data_size); if (side_data && side_data_size >= 8 && // Only the Codec-specific BlockMore (id == 1) is currently supported. - (additional_id = AV_RB64(side_data)) == 1) { - ebml_writer_open_master(&writer, MATROSKA_ID_BLOCKADDITIONS); - ebml_writer_open_master(&writer, MATROSKA_ID_BLOCKMORE); - /* Until dbc50f8a our demuxer used a wrong default value - * of BlockAddID, so we write it unconditionally. */ - ebml_writer_add_uint(&writer, MATROSKA_ID_BLOCKADDID, additional_id); - ebml_writer_add_bin (&writer, MATROSKA_ID_BLOCKADDITIONAL, - side_data + 8, side_data_size - 8); - ebml_writer_close_master(&writer); - ebml_writer_close_master(&writer); + (additional_id = AV_RB64(side_data)) == MATROSKA_BLOCK_ADD_ID_TYPE_OPAQUE) { + mkv_write_blockadditional(&writer, side_data + 8, side_data_size - 8, + additional_id); + track->max_blockaddid = FFMAX(track->max_blockaddid, additional_id); + } + + if (par->codec_type == AVMEDIA_TYPE_VIDEO) { + side_data = av_packet_get_side_data(pkt, + AV_PKT_DATA_DYNAMIC_HDR10_PLUS, + &side_data_size); + if (side_data && side_data_size) { + uint8_t *payload = t35_buf; + size_t payload_size = sizeof(t35_buf) - 6; + + bytestream_put_byte(&payload, ITU_T_T35_COUNTRY_CODE_US); + bytestream_put_be16(&payload, ITU_T_T35_PROVIDER_CODE_SMTPE); + bytestream_put_be16(&payload, 0x01); // provider_oriented_code + bytestream_put_byte(&payload, 0x04); // application_identifier + + ret = av_dynamic_hdr_plus_to_t35((AVDynamicHDRPlus *)side_data, &payload, + &payload_size); + if (ret < 0) + return ret; + + mkv_write_blockadditional(&writer, t35_buf, payload_size + 6, + MATROSKA_BLOCK_ADD_ID_ITU_T_T35); + track->max_blockaddid = FFMAX(track->max_blockaddid, + MATROSKA_BLOCK_ADD_ID_ITU_T_T35); + } } + ebml_writer_close_or_discard_master(&writer); + if (!force_blockgroup && writer.nb_elements == 2) { /* Nothing except the BlockGroup + Block. Can use a SimpleBlock. */ writer.elements++; // Skip the BlockGroup. @@ -2763,7 +2960,7 @@ static int mkv_write_packet_internal(AVFormatContext *s, const AVPacket *pkt) /* All subtitle blocks are considered to be keyframes. */ int keyframe = is_sub || !!(pkt->flags & AV_PKT_FLAG_KEY); int64_t duration = FFMAX(pkt->duration, 0); - int64_t write_duration = is_sub ? duration : 0; + int64_t cue_duration = is_sub ? duration : 0; int ret; int64_t ts = track->write_dts ? pkt->dts : pkt->pts; int64_t relative_packet_pos; @@ -2804,7 +3001,7 @@ static int mkv_write_packet_internal(AVFormatContext *s, const AVPacket *pkt) /* The WebM spec requires WebVTT to be muxed in BlockGroups; * so we force it even for packets without duration. */ ret = mkv_write_block(s, mkv, pb, par, track, pkt, - keyframe, ts, write_duration, + keyframe, ts, duration, par->codec_id == AV_CODEC_ID_WEBVTT, relative_packet_pos); if (ret < 0) @@ -2815,7 +3012,7 @@ static int mkv_write_packet_internal(AVFormatContext *s, const AVPacket *pkt) !mkv->have_video && !track->has_cue)) { ret = mkv_add_cuepoint(mkv, pkt->stream_index, ts, mkv->cluster_pos, relative_packet_pos, - write_duration); + cue_duration); if (ret < 0) return ret; track->has_cue = 1; @@ -2876,7 +3073,7 @@ static int mkv_write_packet(AVFormatContext *s, const AVPacket *pkt) } } - if (!mkv->cluster_pos) + if (mkv->cluster_pos == -1) avio_write_marker(s->pb, av_rescale_q(pkt->dts, s->streams[pkt->stream_index]->time_base, AV_TIME_BASE_Q), keyframe && (mkv->have_video ? codec_type == AVMEDIA_TYPE_VIDEO : 1) ? AVIO_DATA_MARKER_SYNC_POINT : AVIO_DATA_MARKER_BOUNDARY_POINT); @@ -3048,6 +3245,33 @@ static int mkv_write_trailer(AVFormatContext *s) if (mkv->track.bc) { // write Tracks master + if (!IS_WEBM(mkv)) { + AVIOContext *track_bc = mkv->track.bc; + + for (unsigned i = 0; i < s->nb_streams; i++) { + const mkv_track *track = &mkv->tracks[i]; + + if (!track->max_blockaddid) + continue; + + // We reserved a single byte to write this value. + av_assert0(track->max_blockaddid <= 0xFF); + + avio_seek(track_bc, track->blockadditionmapping_offset, SEEK_SET); + + put_ebml_uint(track_bc, MATROSKA_ID_TRACKMAXBLKADDID, + track->max_blockaddid); + if (track->max_blockaddid == MATROSKA_BLOCK_ADD_ID_ITU_T_T35) { + ebml_master mapping_master = start_ebml_master(track_bc, MATROSKA_ID_TRACKBLKADDMAPPING, 8); + put_ebml_uint(track_bc, MATROSKA_ID_BLKADDIDTYPE, + MATROSKA_BLOCK_ADD_ID_TYPE_ITU_T_T35); + put_ebml_uint(track_bc, MATROSKA_ID_BLKADDIDVALUE, + MATROSKA_BLOCK_ADD_ID_ITU_T_T35); + end_ebml_master(track_bc, mapping_master); + } + } + } + avio_seek(pb, mkv->track.pos, SEEK_SET); ret = end_ebml_master_crc32(pb, &mkv->track.bc, mkv, MATROSKA_ID_TRACKS, 0, 0, 0); @@ -3065,7 +3289,7 @@ static int mkv_write_trailer(AVFormatContext *s) if (track->duration_offset > 0) { double duration_sec = track->duration * av_q2d(st->time_base); - char duration_string[20] = ""; + char duration_string[DURATION_STRING_LENGTH + 1] = ""; ebml_master simpletag; av_log(s, AV_LOG_DEBUG, "stream %d end duration = %" PRIu64 "\n", i, @@ -3076,11 +3300,12 @@ static int mkv_write_trailer(AVFormatContext *s) 2 + 1 + 8 + 23); put_ebml_string(tags_bc, MATROSKA_ID_TAGNAME, "DURATION"); - snprintf(duration_string, 20, "%02d:%02d:%012.9f", + snprintf(duration_string, sizeof(duration_string), "%02d:%02d:%012.9f", (int) duration_sec / 3600, ((int) duration_sec / 60) % 60, fmod(duration_sec, 60)); - put_ebml_binary(tags_bc, MATROSKA_ID_TAGSTRING, duration_string, 20); + put_ebml_binary(tags_bc, MATROSKA_ID_TAGSTRING, + duration_string, DURATION_STRING_LENGTH); end_ebml_master(tags_bc, simpletag); } } @@ -3131,7 +3356,8 @@ static int mkv_init(struct AVFormatContext *s) s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RA_288 || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_SIPR || s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RV10 || - s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RV20) { + s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RV20 || + s->streams[i]->codecpar->codec_id == AV_CODEC_ID_RV30) { av_log(s, AV_LOG_ERROR, "The Matroska muxer does not yet support muxing %s\n", avcodec_get_name(s->streams[i]->codecpar->codec_id)); @@ -3249,6 +3475,7 @@ static int mkv_check_bitstream(AVFormatContext *s, AVStream *st, static const AVCodecTag additional_audio_tags[] = { { AV_CODEC_ID_ALAC, 0XFFFFFFFF }, + { AV_CODEC_ID_ATRAC1, 0xFFFFFFFF }, { AV_CODEC_ID_MLP, 0xFFFFFFFF }, { AV_CODEC_ID_OPUS, 0xFFFFFFFF }, { AV_CODEC_ID_PCM_S16BE, 0xFFFFFFFF }, @@ -3257,23 +3484,15 @@ static const AVCodecTag additional_audio_tags[] = { { AV_CODEC_ID_QDMC, 0xFFFFFFFF }, { AV_CODEC_ID_QDM2, 0xFFFFFFFF }, { AV_CODEC_ID_RA_144, 0xFFFFFFFF }, - { AV_CODEC_ID_RA_288, 0xFFFFFFFF }, - { AV_CODEC_ID_COOK, 0xFFFFFFFF }, { AV_CODEC_ID_TRUEHD, 0xFFFFFFFF }, { AV_CODEC_ID_NONE, 0xFFFFFFFF } }; -static const AVCodecTag additional_video_tags[] = { - { AV_CODEC_ID_RV10, 0xFFFFFFFF }, - { AV_CODEC_ID_RV20, 0xFFFFFFFF }, - { AV_CODEC_ID_RV30, 0xFFFFFFFF }, - { AV_CODEC_ID_NONE, 0xFFFFFFFF } -}; - static const AVCodecTag additional_subtitle_tags[] = { { AV_CODEC_ID_DVB_SUBTITLE, 0xFFFFFFFF }, { AV_CODEC_ID_DVD_SUBTITLE, 0xFFFFFFFF }, { AV_CODEC_ID_HDMV_PGS_SUBTITLE, 0xFFFFFFFF }, + { AV_CODEC_ID_ARIB_CAPTION, 0xFFFFFFFF }, { AV_CODEC_ID_NONE, 0xFFFFFFFF } }; @@ -3290,10 +3509,10 @@ static const AVOption options[] = { { "allow_raw_vfw", "allow RAW VFW mode", OFFSET(allow_raw_vfw), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "flipped_raw_rgb", "Raw RGB bitmaps in VFW mode are stored bottom-up", OFFSET(flipped_raw_rgb), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS }, { "write_crc32", "write a CRC32 element inside every Level 1 element", OFFSET(write_crc), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, FLAGS }, - { "default_mode", "Controls how a track's FlagDefault is inferred", OFFSET(default_mode), AV_OPT_TYPE_INT, { .i64 = DEFAULT_MODE_PASSTHROUGH }, DEFAULT_MODE_INFER, DEFAULT_MODE_PASSTHROUGH, FLAGS, "default_mode" }, - { "infer", "For each track type, mark each track of disposition default as default; if none exists, mark the first track as default.", 0, AV_OPT_TYPE_CONST, { .i64 = DEFAULT_MODE_INFER }, 0, 0, FLAGS, "default_mode" }, - { "infer_no_subs", "For each track type, mark each track of disposition default as default; for audio and video: if none exists, mark the first track as default.", 0, AV_OPT_TYPE_CONST, { .i64 = DEFAULT_MODE_INFER_NO_SUBS }, 0, 0, FLAGS, "default_mode" }, - { "passthrough", "Use the disposition flag as-is", 0, AV_OPT_TYPE_CONST, { .i64 = DEFAULT_MODE_PASSTHROUGH }, 0, 0, FLAGS, "default_mode" }, + { "default_mode", "Controls how a track's FlagDefault is inferred", OFFSET(default_mode), AV_OPT_TYPE_INT, { .i64 = DEFAULT_MODE_PASSTHROUGH }, DEFAULT_MODE_INFER, DEFAULT_MODE_PASSTHROUGH, FLAGS, .unit = "default_mode" }, + { "infer", "For each track type, mark each track of disposition default as default; if none exists, mark the first track as default.", 0, AV_OPT_TYPE_CONST, { .i64 = DEFAULT_MODE_INFER }, 0, 0, FLAGS, .unit = "default_mode" }, + { "infer_no_subs", "For each track type, mark each track of disposition default as default; for audio and video: if none exists, mark the first track as default.", 0, AV_OPT_TYPE_CONST, { .i64 = DEFAULT_MODE_INFER_NO_SUBS }, 0, 0, FLAGS, .unit = "default_mode" }, + { "passthrough", "Use the disposition flag as-is", 0, AV_OPT_TYPE_CONST, { .i64 = DEFAULT_MODE_PASSTHROUGH }, 0, 0, FLAGS, .unit = "default_mode" }, { NULL }, }; @@ -3337,15 +3556,20 @@ const FFOutputFormat ff_matroska_muxer = { .write_packet = mkv_write_flush_packet, .write_trailer = mkv_write_trailer, .p.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | +#if FF_API_ALLOW_FLUSH AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, +#else + AVFMT_TS_NONSTRICT, +#endif .p.codec_tag = (const AVCodecTag* const []){ ff_codec_bmp_tags, ff_codec_wav_tags, - additional_audio_tags, additional_video_tags, additional_subtitle_tags, 0 + additional_audio_tags, additional_subtitle_tags, 0 }, .p.subtitle_codec = AV_CODEC_ID_ASS, .query_codec = mkv_query_codec, .check_bitstream = mkv_check_bitstream, .p.priv_class = &matroska_webm_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif @@ -3376,8 +3600,13 @@ const FFOutputFormat ff_webm_muxer = { .query_codec = webm_query_codec, .check_bitstream = mkv_check_bitstream, .p.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | +#if FF_API_ALLOW_FLUSH AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, +#else + AVFMT_TS_NONSTRICT, +#endif .p.priv_class = &matroska_webm_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif @@ -3397,11 +3626,16 @@ const FFOutputFormat ff_matroska_audio_muxer = { .write_packet = mkv_write_flush_packet, .write_trailer = mkv_write_trailer, .check_bitstream = mkv_check_bitstream, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_GLOBALHEADER | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, +#else + .p.flags = AVFMT_GLOBALHEADER | AVFMT_TS_NONSTRICT, +#endif .p.codec_tag = (const AVCodecTag* const []){ ff_codec_wav_tags, additional_audio_tags, 0 }, .p.priv_class = &matroska_webm_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif diff --git a/libavformat/mca.c b/libavformat/mca.c index 74654c3b39e..e707de3c3b1 100644 --- a/libavformat/mca.c +++ b/libavformat/mca.c @@ -218,13 +218,13 @@ static int read_seek(AVFormatContext *s, int stream_index, return 0; } -const AVInputFormat ff_mca_demuxer = { - .name = "mca", - .long_name = NULL_IF_CONFIG_SMALL("MCA Audio Format"), +const FFInputFormat ff_mca_demuxer = { + .p.name = "mca", + .p.long_name = NULL_IF_CONFIG_SMALL("MCA Audio Format"), + .p.extensions = "mca", .priv_data_size = sizeof(MCADemuxContext), .read_probe = probe, .read_header = read_header, .read_packet = read_packet, .read_seek = read_seek, - .extensions = "mca", }; diff --git a/libavformat/mccdec.c b/libavformat/mccdec.c index 8c36b27f123..85bf93cd3b1 100644 --- a/libavformat/mccdec.c +++ b/libavformat/mccdec.c @@ -20,6 +20,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" #include "subtitles.h" #include "libavutil/avstring.h" @@ -200,14 +201,14 @@ static int mcc_read_header(AVFormatContext *s) return ret; } -const AVInputFormat ff_mcc_demuxer = { - .name = "mcc", - .long_name = NULL_IF_CONFIG_SMALL("MacCaption"), +const FFInputFormat ff_mcc_demuxer = { + .p.name = "mcc", + .p.long_name = NULL_IF_CONFIG_SMALL("MacCaption"), + .p.extensions = "mcc", .priv_data_size = sizeof(MCCContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = mcc_probe, .read_header = mcc_read_header, - .extensions = "mcc", .read_packet = ff_subtitles_read_packet, .read_seek2 = ff_subtitles_read_seek, .read_close = ff_subtitles_read_close, diff --git a/libavformat/md5proto.c b/libavformat/md5proto.c index 14cefe719c1..653bf10e662 100644 --- a/libavformat/md5proto.c +++ b/libavformat/md5proto.c @@ -23,7 +23,6 @@ #include "libavutil/md5.h" #include "libavutil/mem.h" #include "libavutil/error.h" -#include "avformat.h" #include "avio.h" #include "internal.h" #include "url.h" diff --git a/libavformat/mgsts.c b/libavformat/mgsts.c index b5c704829da..07ea66163c8 100644 --- a/libavformat/mgsts.c +++ b/libavformat/mgsts.c @@ -22,6 +22,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/intfloat.h" #include "avformat.h" +#include "demux.h" #include "riff.h" static int read_probe(const AVProbeData *p) @@ -96,11 +97,11 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_mgsts_demuxer = { - .name = "mgsts", - .long_name = NULL_IF_CONFIG_SMALL("Metal Gear Solid: The Twin Snakes"), +const FFInputFormat ff_mgsts_demuxer = { + .p.name = "mgsts", + .p.long_name = NULL_IF_CONFIG_SMALL("Metal Gear Solid: The Twin Snakes"), + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = read_probe, .read_header = read_header, .read_packet = read_packet, - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/microdvddec.c b/libavformat/microdvddec.c index e536d12b85f..001f1cec158 100644 --- a/libavformat/microdvddec.c +++ b/libavformat/microdvddec.c @@ -21,6 +21,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" #include "subtitles.h" #include "libavutil/intreadwrite.h" @@ -198,15 +199,15 @@ static const AVClass microdvd_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_microdvd_demuxer = { - .name = "microdvd", - .long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle format"), +const FFInputFormat ff_microdvd_demuxer = { + .p.name = "microdvd", + .p.long_name = NULL_IF_CONFIG_SMALL("MicroDVD subtitle format"), + .p.priv_class = µdvd_class, .priv_data_size = sizeof(MicroDVDContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = microdvd_probe, .read_header = microdvd_read_header, .read_packet = microdvd_read_packet, .read_seek2 = microdvd_read_seek, .read_close = microdvd_read_close, - .priv_class = µdvd_class, }; diff --git a/libavformat/microdvdenc.c b/libavformat/microdvdenc.c index 950309981bb..d4910ecf71a 100644 --- a/libavformat/microdvdenc.c +++ b/libavformat/microdvdenc.c @@ -29,11 +29,6 @@ static int microdvd_write_header(struct AVFormatContext *s) AVCodecParameters *par = s->streams[0]->codecpar; AVRational framerate = s->streams[0]->avg_frame_rate; - if (s->nb_streams != 1 || par->codec_id != AV_CODEC_ID_MICRODVD) { - av_log(s, AV_LOG_ERROR, "Exactly one MicroDVD stream is needed.\n"); - return -1; - } - if (par->extradata && par->extradata_size > 0) { avio_write(s->pb, "{DEFAULT}{}", 11); avio_write(s->pb, par->extradata, par->extradata_size); @@ -62,7 +57,11 @@ const FFOutputFormat ff_microdvd_muxer = { .p.mime_type = "text/x-microdvd", .p.extensions = "sub", .p.flags = AVFMT_NOTIMESTAMPS, + .p.video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_CODEC_ID_NONE, .p.subtitle_codec = AV_CODEC_ID_MICRODVD, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_header = microdvd_write_header, .write_packet = microdvd_write_packet, }; diff --git a/libavformat/mlpdec.c b/libavformat/mlpdec.c index f4fed658513..4927f9d3517 100644 --- a/libavformat/mlpdec.c +++ b/libavformat/mlpdec.c @@ -25,6 +25,7 @@ #include "avformat.h" #include "avio_internal.h" +#include "demux.h" #include "internal.h" #include "rawdec.h" #include "libavutil/intreadwrite.h" @@ -95,17 +96,17 @@ static int mlp_probe(const AVProbeData *p) return mlp_thd_probe(p, 0xf8726fbb); } -const AVInputFormat ff_mlp_demuxer = { - .name = "mlp", - .long_name = NULL_IF_CONFIG_SMALL("raw MLP"), +const FFInputFormat ff_mlp_demuxer = { + .p.name = "mlp", + .p.long_name = NULL_IF_CONFIG_SMALL("raw MLP"), + .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS, + .p.extensions = "mlp", + .p.priv_class = &ff_raw_demuxer_class, .read_probe = mlp_probe, .read_header = mlp_read_header, .read_packet = ff_raw_read_partial_packet, - .flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS, - .extensions = "mlp", .raw_codec_id = AV_CODEC_ID_MLP, .priv_data_size = sizeof(FFRawDemuxerContext), - .priv_class = &ff_raw_demuxer_class, }; #endif @@ -115,16 +116,16 @@ static int thd_probe(const AVProbeData *p) return mlp_thd_probe(p, 0xf8726fba); } -const AVInputFormat ff_truehd_demuxer = { - .name = "truehd", - .long_name = NULL_IF_CONFIG_SMALL("raw TrueHD"), +const FFInputFormat ff_truehd_demuxer = { + .p.name = "truehd", + .p.long_name = NULL_IF_CONFIG_SMALL("raw TrueHD"), + .p.flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS, + .p.extensions = "thd", + .p.priv_class = &ff_raw_demuxer_class, .read_probe = thd_probe, .read_header = mlp_read_header, .read_packet = ff_raw_read_partial_packet, - .flags = AVFMT_GENERIC_INDEX | AVFMT_NOTIMESTAMPS, - .extensions = "thd", .raw_codec_id = AV_CODEC_ID_TRUEHD, .priv_data_size = sizeof(FFRawDemuxerContext), - .priv_class = &ff_raw_demuxer_class, }; #endif diff --git a/libavformat/mlvdec.c b/libavformat/mlvdec.c index db3b77bb9b8..e3165e3811c 100644 --- a/libavformat/mlvdec.c +++ b/libavformat/mlvdec.c @@ -24,6 +24,8 @@ * Magic Lantern Video (MLV) demuxer */ +#include + #include "libavutil/imgutils.h" #include "libavutil/intreadwrite.h" #include "libavutil/rational.h" @@ -486,11 +488,11 @@ static int read_close(AVFormatContext *s) return 0; } -const AVInputFormat ff_mlv_demuxer = { - .name = "mlv", - .long_name = NULL_IF_CONFIG_SMALL("Magic Lantern Video (MLV)"), +const FFInputFormat ff_mlv_demuxer = { + .p.name = "mlv", + .p.long_name = NULL_IF_CONFIG_SMALL("Magic Lantern Video (MLV)"), .priv_data_size = sizeof(MlvContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = probe, .read_header = read_header, .read_packet = read_packet, diff --git a/libavformat/mm.c b/libavformat/mm.c index 1d44f41a944..e377ed4fbb9 100644 --- a/libavformat/mm.c +++ b/libavformat/mm.c @@ -34,6 +34,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define MM_PREAMBLE_SIZE 6 @@ -94,7 +95,7 @@ static int read_header(AVFormatContext *s) type = avio_rl16(pb); length = avio_rl32(pb); - if (type != MM_TYPE_HEADER) + if (type != MM_TYPE_HEADER || length < 10) return AVERROR_INVALIDDATA; /* read header */ @@ -189,9 +190,9 @@ static int read_packet(AVFormatContext *s, } } -const AVInputFormat ff_mm_demuxer = { - .name = "mm", - .long_name = NULL_IF_CONFIG_SMALL("American Laser Games MM"), +const FFInputFormat ff_mm_demuxer = { + .p.name = "mm", + .p.long_name = NULL_IF_CONFIG_SMALL("American Laser Games MM"), .priv_data_size = sizeof(MmDemuxContext), .read_probe = probe, .read_header = read_header, diff --git a/libavformat/mmf.c b/libavformat/mmf.c index 5cac4381f45..42a88cff90c 100644 --- a/libavformat/mmf.c +++ b/libavformat/mmf.c @@ -24,6 +24,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" #include "avio_internal.h" +#include "demux.h" #include "internal.h" #include "mux.h" #include "pcm.h" @@ -298,14 +299,14 @@ static int mmf_read_packet(AVFormatContext *s, AVPacket *pkt) } #if CONFIG_MMF_DEMUXER -const AVInputFormat ff_mmf_demuxer = { - .name = "mmf", - .long_name = NULL_IF_CONFIG_SMALL("Yamaha SMAF"), +const FFInputFormat ff_mmf_demuxer = { + .p.name = "mmf", + .p.long_name = NULL_IF_CONFIG_SMALL("Yamaha SMAF"), + .p.flags = AVFMT_GENERIC_INDEX, .priv_data_size = sizeof(MMFContext), .read_probe = mmf_probe, .read_header = mmf_read_header, .read_packet = mmf_read_packet, - .flags = AVFMT_GENERIC_INDEX, }; #endif @@ -318,6 +319,9 @@ const FFOutputFormat ff_mmf_muxer = { .priv_data_size = sizeof(MMFContext), .p.audio_codec = AV_CODEC_ID_ADPCM_YAMAHA, .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_header = mmf_write_header, .write_packet = ff_raw_write_packet, .write_trailer = mmf_write_trailer, diff --git a/libavformat/mmsh.c b/libavformat/mmsh.c index 672f4b3788f..60113d61d26 100644 --- a/libavformat/mmsh.c +++ b/libavformat/mmsh.c @@ -371,9 +371,10 @@ static int mmsh_read(URLContext *h, uint8_t *buf, int size) return res; } -static int64_t mmsh_read_seek(URLContext *h, int stream_index, +static int64_t mmsh_read_seek(void *opaque, int stream_index, int64_t timestamp, int flags) { + URLContext *h = opaque; MMSHContext *mmsh_old = h->priv_data; MMSHContext *mmsh = av_mallocz(sizeof(*mmsh)); int ret; diff --git a/libavformat/mmst.c b/libavformat/mmst.c index 20b04b12aa7..d7f71304e5b 100644 --- a/libavformat/mmst.c +++ b/libavformat/mmst.c @@ -157,8 +157,8 @@ static int mms_put_utf16(MMSContext *mms, const uint8_t *src) FFIOContext bic; int size = mms->write_out_ptr - mms->out_buffer; int len; - ffio_init_context(&bic, mms->write_out_ptr, - sizeof(mms->out_buffer) - size, 1, NULL, NULL, NULL, NULL); + ffio_init_write_context(&bic, mms->write_out_ptr, + sizeof(mms->out_buffer) - size); len = avio_put_str16le(&bic.pub, src); if (len < 0) diff --git a/libavformat/mods.c b/libavformat/mods.c index 34524a19d8c..7f76124821c 100644 --- a/libavformat/mods.c +++ b/libavformat/mods.c @@ -24,6 +24,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" static int mods_probe(const AVProbeData *p) @@ -91,12 +92,12 @@ static int mods_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_mods_demuxer = { - .name = "mods", - .long_name = NULL_IF_CONFIG_SMALL("MobiClip MODS"), +const FFInputFormat ff_mods_demuxer = { + .p.name = "mods", + .p.long_name = NULL_IF_CONFIG_SMALL("MobiClip MODS"), + .p.extensions = "mods", + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = mods_probe, .read_header = mods_read_header, .read_packet = mods_read_packet, - .extensions = "mods", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/moflex.c b/libavformat/moflex.c index 2ea7c1f994e..85511a04be5 100644 --- a/libavformat/moflex.c +++ b/libavformat/moflex.c @@ -24,6 +24,7 @@ #include "libavcodec/bytestream.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct BitReader { @@ -371,16 +372,16 @@ static int moflex_read_close(AVFormatContext *s) return 0; } -const AVInputFormat ff_moflex_demuxer = { - .name = "moflex", - .long_name = NULL_IF_CONFIG_SMALL("MobiClip MOFLEX"), +const FFInputFormat ff_moflex_demuxer = { + .p.name = "moflex", + .p.long_name = NULL_IF_CONFIG_SMALL("MobiClip MOFLEX"), + .p.extensions = "moflex", + .p.flags = AVFMT_GENERIC_INDEX, .priv_data_size = sizeof(MOFLEXDemuxContext), .read_probe = moflex_probe, .read_header = moflex_read_header, .read_packet = moflex_read_packet, .read_seek = moflex_read_seek, .read_close = moflex_read_close, - .extensions = "moflex", - .flags = AVFMT_GENERIC_INDEX, - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, }; diff --git a/libavformat/mov.c b/libavformat/mov.c index 3aea3e60935..0f7b910a792 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -58,6 +58,8 @@ #include "internal.h" #include "avio_internal.h" #include "demux.h" +#include "iamf_parse.h" +#include "iamf_reader.h" #include "dovi_isom.h" #include "riff.h" #include "isom.h" @@ -81,6 +83,7 @@ typedef struct MOVParseTableEntry { static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom); static int mov_read_mfra(MOVContext *c, AVIOContext *f); +static void mov_free_stream_context(AVFormatContext *s, AVStream *st); static int64_t add_ctts_entry(MOVCtts** ctts_data, unsigned int* ctts_count, unsigned int* allocated_size, int count, int duration); @@ -184,6 +187,30 @@ static int mov_read_mac_string(MOVContext *c, AVIOContext *pb, int len, return p - dst; } +static AVStream *get_curr_st(MOVContext *c) +{ + AVStream *st = NULL; + + if (c->fc->nb_streams < 1) + return NULL; + + for (int i = 0; i < c->nb_heif_item; i++) { + HEIFItem *item = &c->heif_item[i]; + + if (!item->st) + continue; + if (item->st->id != c->cur_item_id) + continue; + + st = item->st; + break; + } + if (!st) + st = c->fc->streams[c->fc->nb_streams-1]; + + return st; +} + static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len) { AVStream *st; @@ -211,6 +238,8 @@ static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len) } st = c->fc->streams[c->fc->nb_streams - 1]; st->priv_data = sc; + sc->id = st->id; + sc->refcount = 1; if (st->attached_pic.size >= 8 && id != AV_CODEC_ID_BMP) { if (AV_RB64(st->attached_pic.data) == 0x89504e470d0a1a0a) { @@ -303,7 +332,8 @@ static int mov_read_udta_string(MOVContext *c, AVIOContext *pb, MOVAtom atom) char *str = NULL; const char *key = NULL; uint16_t langcode = 0; - uint32_t data_type = 0, str_size, str_size_alloc; + uint32_t data_type = 0, str_size_alloc; + uint64_t str_size; int (*parse)(MOVContext*, AVIOContext*, unsigned, const char*) = NULL; int raw = 0; int num = 0; @@ -799,6 +829,7 @@ static int mov_read_esds(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_read_dac3(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; + AVPacketSideData *sd; enum AVAudioServiceType *ast; int ac3info, acmod, lfeon, bsmod; uint64_t mask; @@ -807,11 +838,14 @@ static int mov_read_dac3(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; st = c->fc->streams[c->fc->nb_streams-1]; - ast = (enum AVAudioServiceType*)av_stream_new_side_data(st, AV_PKT_DATA_AUDIO_SERVICE_TYPE, - sizeof(*ast)); - if (!ast) + sd = av_packet_side_data_new(&st->codecpar->coded_side_data, + &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_AUDIO_SERVICE_TYPE, + sizeof(*ast), 0); + if (!sd) return AVERROR(ENOMEM); + ast = (enum AVAudioServiceType*)sd->data; ac3info = avio_rb24(pb); bsmod = (ac3info >> 14) & 0x7; acmod = (ac3info >> 11) & 0x7; @@ -830,9 +864,195 @@ static int mov_read_dac3(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +static int mov_read_iacb(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + AVStream *st; + MOVStreamContext *sc; + FFIOContext b; + AVIOContext *descriptor_pb; + AVDictionary *metadata; + IAMFContext *iamf; + int64_t start_time, duration; + unsigned descriptors_size; + int nb_frames, disposition; + int version, ret; + + if (atom.size < 5) + return AVERROR_INVALIDDATA; + + if (c->fc->nb_streams < 1) + return 0; + + version = avio_r8(pb); + if (version != 1) { + av_log(c->fc, AV_LOG_ERROR, "%s configurationVersion %d", + version < 1 ? "invalid" : "unsupported", version); + return AVERROR_INVALIDDATA; + } + + descriptors_size = ffio_read_leb(pb); + if (!descriptors_size || descriptors_size > INT_MAX) + return AVERROR_INVALIDDATA; + + st = c->fc->streams[c->fc->nb_streams - 1]; + sc = st->priv_data; + + if (st->codecpar->extradata) { + av_log(c->fc, AV_LOG_WARNING, "ignoring iacb\n"); + return 0; + } + + sc->iamf = av_mallocz(sizeof(*sc->iamf)); + if (!sc->iamf) + return AVERROR(ENOMEM); + iamf = &sc->iamf->iamf; + + st->codecpar->extradata = av_malloc(descriptors_size); + if (!st->codecpar->extradata) + return AVERROR(ENOMEM); + st->codecpar->extradata_size = descriptors_size; + + ret = avio_read(pb, st->codecpar->extradata, descriptors_size); + if (ret != descriptors_size) + return ret < 0 ? ret : AVERROR_INVALIDDATA; + + ffio_init_read_context(&b, st->codecpar->extradata, descriptors_size); + descriptor_pb = &b.pub; + + ret = ff_iamfdec_read_descriptors(iamf, descriptor_pb, descriptors_size, c->fc); + if (ret < 0) + return ret; + + metadata = st->metadata; + st->metadata = NULL; + start_time = st->start_time; + nb_frames = st->nb_frames; + duration = st->duration; + disposition = st->disposition; + + for (int i = 0; i < iamf->nb_audio_elements; i++) { + IAMFAudioElement *audio_element = iamf->audio_elements[i]; + const AVIAMFAudioElement *element; + AVStreamGroup *stg = + avformat_stream_group_create(c->fc, AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT, NULL); + + if (!stg) { + ret = AVERROR(ENOMEM); + goto fail; + } + + av_iamf_audio_element_free(&stg->params.iamf_audio_element); + stg->id = audio_element->audio_element_id; + /* Transfer ownership */ + element = stg->params.iamf_audio_element = audio_element->element; + audio_element->element = NULL; + + for (int j = 0; j < audio_element->nb_substreams; j++) { + IAMFSubStream *substream = &audio_element->substreams[j]; + AVStream *stream; + + if (!i && !j) { + if (audio_element->layers[0].substream_count != 1) + disposition &= ~AV_DISPOSITION_DEFAULT; + stream = st; + } else + stream = avformat_new_stream(c->fc, NULL); + if (!stream) { + ret = AVERROR(ENOMEM); + goto fail; + } + + stream->start_time = start_time; + stream->nb_frames = nb_frames; + stream->duration = duration; + stream->disposition = disposition; + if (stream != st) { + stream->priv_data = sc; + sc->refcount++; + } + + if (element->audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_SCENE) + stream->disposition |= AV_DISPOSITION_DEPENDENT; + if (i || j) { + stream->disposition |= AV_DISPOSITION_DEPENDENT; + if (audio_element->layers[0].substream_count == 1) + stream->disposition &= ~AV_DISPOSITION_DEFAULT; + } + + ret = avcodec_parameters_copy(stream->codecpar, substream->codecpar); + if (ret < 0) + goto fail; + + stream->id = substream->audio_substream_id; + + avpriv_set_pts_info(st, 64, 1, sc->time_scale); + + ret = avformat_stream_group_add_stream(stg, stream); + if (ret < 0) + goto fail; + } + + ret = av_dict_copy(&stg->metadata, metadata, 0); + if (ret < 0) + goto fail; + } + + for (int i = 0; i < iamf->nb_mix_presentations; i++) { + IAMFMixPresentation *mix_presentation = iamf->mix_presentations[i]; + const AVIAMFMixPresentation *mix = mix_presentation->cmix; + AVStreamGroup *stg = + avformat_stream_group_create(c->fc, AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION, NULL); + + if (!stg) { + ret = AVERROR(ENOMEM); + goto fail; + } + + av_iamf_mix_presentation_free(&stg->params.iamf_mix_presentation); + stg->id = mix_presentation->mix_presentation_id; + /* Transfer ownership */ + stg->params.iamf_mix_presentation = mix_presentation->mix; + mix_presentation->mix = NULL; + + for (int j = 0; j < mix->nb_submixes; j++) { + const AVIAMFSubmix *submix = mix->submixes[j]; + + for (int k = 0; k < submix->nb_elements; k++) { + const AVIAMFSubmixElement *submix_element = submix->elements[k]; + const AVStreamGroup *audio_element = NULL; + + for (int l = 0; l < c->fc->nb_stream_groups; l++) + if (c->fc->stream_groups[l]->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT && + c->fc->stream_groups[l]->id == submix_element->audio_element_id) { + audio_element = c->fc->stream_groups[l]; + break; + } + av_assert0(audio_element); + + for (int l = 0; l < audio_element->nb_streams; l++) { + ret = avformat_stream_group_add_stream(stg, audio_element->streams[l]); + if (ret < 0 && ret != AVERROR(EEXIST)) + goto fail; + } + } + } + + ret = av_dict_copy(&stg->metadata, metadata, 0); + if (ret < 0) + goto fail; + } + + ret = 0; +fail: + av_dict_free(&metadata); + + return ret; +} + static int mov_read_dec3(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; + AVPacketSideData *sd; enum AVAudioServiceType *ast; int eac3info, acmod, lfeon, bsmod; uint64_t mask; @@ -841,11 +1061,15 @@ static int mov_read_dec3(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; st = c->fc->streams[c->fc->nb_streams-1]; - ast = (enum AVAudioServiceType*)av_stream_new_side_data(st, AV_PKT_DATA_AUDIO_SERVICE_TYPE, - sizeof(*ast)); - if (!ast) + sd = av_packet_side_data_new(&st->codecpar->coded_side_data, + &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_AUDIO_SERVICE_TYPE, + sizeof(*ast), 0); + if (!sd) return AVERROR(ENOMEM); + ast = (enum AVAudioServiceType*)sd->data; + /* No need to parse fields for additional independent substreams and its * associated dependent substreams since libavcodec's E-AC-3 decoder * does not support them yet. */ @@ -940,6 +1164,38 @@ static int mov_read_chan(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +static int mov_read_chnl(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int64_t end = av_sat_add64(avio_tell(pb), atom.size); + int version, flags; + int ret; + AVStream *st; + + if (c->fc->nb_streams < 1) + return 0; + st = c->fc->streams[c->fc->nb_streams-1]; + + version = avio_r8(pb); + flags = avio_rb24(pb); + if (version != 0 || flags != 0) { + av_log(c->fc, AV_LOG_ERROR, + "Unsupported 'chnl' box with version %d, flags: %#x", + version, flags); + return AVERROR_INVALIDDATA; + } + + ret = ff_mov_read_chnl(c->fc, pb, st); + if (ret < 0) + return ret; + + if (avio_tell(pb) != end) { + av_log(c->fc, AV_LOG_WARNING, "skip %" PRId64 " bytes of unknown data inside chnl\n", + end - avio_tell(pb)); + avio_seek(pb, end, SEEK_SET); + } + return ret; +} + static int mov_read_wfex(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; @@ -1131,14 +1387,17 @@ static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom) int ret = ffio_read_size(pb, type, 4); if (ret < 0) return ret; - if (c->fc->nb_streams) - return AVERROR_INVALIDDATA; + if (c->fc->nb_streams) { + if (c->fc->strict_std_compliance >= FF_COMPLIANCE_STRICT) + return AVERROR_INVALIDDATA; + av_log(c->fc, AV_LOG_DEBUG, "Ignoring duplicate FTYP\n"); + return 0; + } if (strcmp(type, "qt ")) c->isom = 1; av_log(c->fc, AV_LOG_DEBUG, "ISO: File Type Major Brand: %.4s\n",(char *)&type); av_dict_set(&c->fc->metadata, "major_brand", type, 0); - c->is_still_picture_avif = !strncmp(type, "avif", 4); minor_ver = avio_rb32(pb); /* minor version */ av_dict_set_int(&c->fc->metadata, "minor_version", minor_ver, 0); @@ -1285,7 +1544,7 @@ static int64_t get_frag_time(AVFormatContext *s, AVStream *dst_st, // If the stream is referenced by any sidx, limit the search // to fragments that referenced this stream in the sidx if (sc->has_sidx) { - frag_stream_info = get_frag_stream_info(frag_index, index, dst_st->id); + frag_stream_info = get_frag_stream_info(frag_index, index, sc->id); if (frag_stream_info->sidx_pts != AV_NOPTS_VALUE) return frag_stream_info->sidx_pts; if (frag_stream_info->first_tfra_pts != AV_NOPTS_VALUE) @@ -1296,9 +1555,11 @@ static int64_t get_frag_time(AVFormatContext *s, AVStream *dst_st, for (i = 0; i < frag_index->item[index].nb_stream_info; i++) { AVStream *frag_stream = NULL; frag_stream_info = &frag_index->item[index].stream_info[i]; - for (j = 0; j < s->nb_streams; j++) - if (s->streams[j]->id == frag_stream_info->id) + for (j = 0; j < s->nb_streams; j++) { + MOVStreamContext *sc2 = s->streams[j]->priv_data; + if (sc2->id == frag_stream_info->id) frag_stream = s->streams[j]; + } if (!frag_stream) { av_log(s, AV_LOG_WARNING, "No stream matching sidx ID found.\n"); continue; @@ -1364,12 +1625,13 @@ static int update_frag_index(MOVContext *c, int64_t offset) for (i = 0; i < c->fc->nb_streams; i++) { // Avoid building frag index if streams lack track id. - if (c->fc->streams[i]->id < 0) { + MOVStreamContext *sc = c->fc->streams[i]->priv_data; + if (sc->id < 0) { av_free(frag_stream_info); return AVERROR_INVALIDDATA; } - frag_stream_info[i].id = c->fc->streams[i]->id; + frag_stream_info[i].id = sc->id; frag_stream_info[i].sidx_pts = AV_NOPTS_VALUE; frag_stream_info[i].tfdt_dts = AV_NOPTS_VALUE; frag_stream_info[i].next_trun_dts = AV_NOPTS_VALUE; @@ -1377,6 +1639,7 @@ static int update_frag_index(MOVContext *c, int64_t offset) frag_stream_info[i].index_base = -1; frag_stream_info[i].index_entry = -1; frag_stream_info[i].encryption_index = NULL; + frag_stream_info[i].stsd_id = -1; } if (index < c->frag_index.nb_items) @@ -1435,14 +1698,29 @@ static int mov_read_moof(MOVContext *c, AVIOContext *pb, MOVAtom atom) return mov_read_default(c, pb, atom); } -static void mov_metadata_creation_time(AVDictionary **metadata, int64_t time, void *logctx) +static void mov_metadata_creation_time(MOVContext *c, AVIOContext *pb, AVDictionary **metadata, int version) { + int64_t time; + if (version == 1) { + time = avio_rb64(pb); + avio_rb64(pb); + if (time < 0) { + av_log(c->fc, AV_LOG_DEBUG, "creation_time is negative\n"); + return; + } + } else { + time = avio_rb32(pb); + avio_rb32(pb); /* modification time */ + if (time > 0 && time < 2082844800) { + av_log(c->fc, AV_LOG_WARNING, "Detected creation time before 1970, parsing as unix timestamp.\n"); + time += 2082844800; + } + } if (time) { - if (time >= 2082844800) - time -= 2082844800; /* seconds between 1904-01-01 and Epoch */ + time -= 2082844800; /* seconds between 1904-01-01 and Epoch */ if ((int64_t)(time * 1000000ULL) / 1000000 != time) { - av_log(logctx, AV_LOG_DEBUG, "creation_time is not representable\n"); + av_log(c->fc, AV_LOG_DEBUG, "creation_time is not representable\n"); return; } @@ -1457,7 +1735,6 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) int version; char language[4] = {0}; unsigned lang; - int64_t creation_time; if (c->fc->nb_streams < 1) return 0; @@ -1475,14 +1752,7 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) return AVERROR_PATCHWELCOME; } avio_rb24(pb); /* flags */ - if (version == 1) { - creation_time = avio_rb64(pb); - avio_rb64(pb); - } else { - creation_time = avio_rb32(pb); - avio_rb32(pb); /* modification time */ - } - mov_metadata_creation_time(&st->metadata, creation_time, c->fc); + mov_metadata_creation_time(c, pb, &st->metadata, version); sc->time_scale = avio_rb32(pb); if (sc->time_scale <= 0) { @@ -1507,18 +1777,10 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_read_mvhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) { int i; - int64_t creation_time; int version = avio_r8(pb); /* version */ avio_rb24(pb); /* flags */ - if (version == 1) { - creation_time = avio_rb64(pb); - avio_rb64(pb); - } else { - creation_time = avio_rb32(pb); - avio_rb32(pb); /* modification time */ - } - mov_metadata_creation_time(&c->fc->metadata, creation_time, c->fc); + mov_metadata_creation_time(c, pb, &c->fc->metadata, version); c->time_scale = avio_rb32(pb); /* time scale */ if (c->time_scale <= 0) { av_log(c->fc, AV_LOG_ERROR, "Invalid mvhd time scale %d, defaulting to 1\n", c->time_scale); @@ -1592,17 +1854,76 @@ static int mov_read_enda(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_read_pcmc(MOVContext *c, AVIOContext *pb, MOVAtom atom) { int format_flags; + int version, flags; + int pcm_sample_size; + AVFormatContext *fc = c->fc; + AVStream *st; + MOVStreamContext *sc; if (atom.size < 6) { av_log(c->fc, AV_LOG_ERROR, "Empty pcmC box\n"); return AVERROR_INVALIDDATA; } - avio_r8(pb); // version - avio_rb24(pb); // flags + version = avio_r8(pb); + flags = avio_rb24(pb); + + if (version != 0 || flags != 0) { + av_log(c->fc, AV_LOG_ERROR, + "Unsupported 'pcmC' box with version %d, flags: %x", + version, flags); + return AVERROR_INVALIDDATA; + } + format_flags = avio_r8(pb); - if (format_flags == 1) // indicates little-endian format. If not present, big-endian format is used + pcm_sample_size = avio_r8(pb); + + if (fc->nb_streams < 1) + return AVERROR_INVALIDDATA; + + st = fc->streams[fc->nb_streams - 1]; + sc = st->priv_data; + + if (sc->format == MOV_MP4_FPCM_TAG) { + switch (pcm_sample_size) { + case 32: + st->codecpar->codec_id = AV_CODEC_ID_PCM_F32BE; + break; + case 64: + st->codecpar->codec_id = AV_CODEC_ID_PCM_F64BE; + break; + default: + av_log(fc, AV_LOG_ERROR, "invalid pcm_sample_size %d for %s\n", + pcm_sample_size, + av_fourcc2str(sc->format)); + return AVERROR_INVALIDDATA; + } + } else if (sc->format == MOV_MP4_IPCM_TAG) { + switch (pcm_sample_size) { + case 16: + st->codecpar->codec_id = AV_CODEC_ID_PCM_S16BE; + break; + case 24: + st->codecpar->codec_id = AV_CODEC_ID_PCM_S24BE; + break; + case 32: + st->codecpar->codec_id = AV_CODEC_ID_PCM_S32BE; + break; + default: + av_log(fc, AV_LOG_ERROR, "invalid pcm_sample_size %d for %s\n", + pcm_sample_size, + av_fourcc2str(sc->format)); + return AVERROR_INVALIDDATA; + } + } else { + av_log(fc, AV_LOG_ERROR, "'pcmC' with invalid sample entry '%s'\n", + av_fourcc2str(sc->format)); + return AVERROR_INVALIDDATA; + } + + if (format_flags & 1) // indicates little-endian format. If not present, big-endian format is used set_last_stream_little_endian(c->fc); + st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id); return 0; } @@ -1610,14 +1931,13 @@ static int mov_read_pcmc(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_read_colr(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; - uint8_t *icc_profile; char color_parameter_type[5] = { 0 }; uint16_t color_primaries, color_trc, color_matrix; int ret; - if (c->fc->nb_streams < 1) + st = get_curr_st(c); + if (!st) return 0; - st = c->fc->streams[c->fc->nb_streams - 1]; ret = ffio_read_size(pb, color_parameter_type, 4); if (ret < 0) @@ -1631,10 +1951,13 @@ static int mov_read_colr(MOVContext *c, AVIOContext *pb, MOVAtom atom) } if (!strncmp(color_parameter_type, "prof", 4)) { - icc_profile = av_stream_new_side_data(st, AV_PKT_DATA_ICC_PROFILE, atom.size - 4); - if (!icc_profile) + AVPacketSideData *sd = av_packet_side_data_new(&st->codecpar->coded_side_data, + &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_ICC_PROFILE, + atom.size - 4, 0); + if (!sd) return AVERROR(ENOMEM); - ret = ffio_read_size(pb, icc_profile, atom.size - 4); + ret = ffio_read_size(pb, sd->data, atom.size - 4); if (ret < 0) return ret; } else { @@ -1774,7 +2097,7 @@ static int mov_read_alac(MOVContext *c, AVIOContext *pb, MOVAtom atom) static int mov_read_avss(MOVContext *c, AVIOContext *pb, MOVAtom atom) { - return mov_read_extradata(c, pb, atom, AV_CODEC_ID_AVS); + return mov_read_extradata(c, pb, atom, AV_CODEC_ID_CAVS); } static int mov_read_jp2h(MOVContext *c, AVIOContext *pb, MOVAtom atom) @@ -1962,13 +2285,18 @@ static int mov_read_glbl(MOVContext *c, AVIOContext *pb, MOVAtom atom) AVStream *st; int ret; - if (c->fc->nb_streams < 1) + st = get_curr_st(c); + if (!st) return 0; - st = c->fc->streams[c->fc->nb_streams-1]; if ((uint64_t)atom.size > (1<<30)) return AVERROR_INVALIDDATA; + if (atom.type == MKTAG('v','v','c','C')) { + avio_skip(pb, 4); + atom.size -= 4; + } + if (atom.size >= 10) { // Broken files created by legacy versions of libavformat will // wrap a whole fiel atom inside of a glbl atom. @@ -2068,7 +2396,13 @@ static int mov_read_stco(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_r8(pb); /* version */ avio_rb24(pb); /* flags */ + // Clamp allocation size for `chunk_offsets` -- don't throw an error for an + // invalid count since the EOF path doesn't throw either. entries = avio_rb32(pb); + entries = + FFMIN(entries, + FFMAX(0, (atom.size - 8) / + (atom.type == MKTAG('s', 't', 'c', 'o') ? 4 : 8))); if (!entries) return 0; @@ -2077,6 +2411,7 @@ static int mov_read_stco(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_log(c->fc, AV_LOG_WARNING, "Ignoring duplicated STCO atom\n"); return 0; } + av_free(sc->chunk_offsets); sc->chunk_count = 0; sc->chunk_offsets = av_malloc_array(entries, sizeof(*sc->chunk_offsets)); @@ -2088,8 +2423,13 @@ static int mov_read_stco(MOVContext *c, AVIOContext *pb, MOVAtom atom) for (i = 0; i < entries && !pb->eof_reached; i++) sc->chunk_offsets[i] = avio_rb32(pb); else if (atom.type == MKTAG('c','o','6','4')) - for (i = 0; i < entries && !pb->eof_reached; i++) + for (i = 0; i < entries && !pb->eof_reached; i++) { sc->chunk_offsets[i] = avio_rb64(pb); + if (sc->chunk_offsets[i] < 0) { + av_log(c->fc, AV_LOG_WARNING, "Impossible chunk_offset\n"); + sc->chunk_offsets[i] = 0; + } + } else return AVERROR_INVALIDDATA; @@ -2126,6 +2466,11 @@ static int mov_codec_id(AVStream *st, uint32_t format) (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE && st->codecpar->codec_id == AV_CODEC_ID_NONE)) { id = ff_codec_get_id(ff_codec_movsubtitle_tags, format); + if (id <= 0) { + id = (format == MOV_MP4_TTML_TAG || format == MOV_ISMV_TTML_TAG) ? + AV_CODEC_ID_TTML : id; + } + if (id > 0) st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; else @@ -2320,8 +2665,10 @@ static void mov_parse_stsd_subtitle(MOVContext *c, AVIOContext *pb, // ttxt stsd contains display flags, justification, background // color, fonts, and default styles, so fake an atom to read it MOVAtom fake_atom = { .size = size }; - // mp4s contains a regular esds atom - if (st->codecpar->codec_tag != AV_RL32("mp4s")) + // mp4s contains a regular esds atom, dfxp ISMV TTML has no content + // in extradata unlike stpp MP4 TTML. + if (st->codecpar->codec_tag != AV_RL32("mp4s") && + st->codecpar->codec_tag != MOV_ISMV_TTML_TAG) mov_read_glbl(c, pb, fake_atom); st->codecpar->width = sc->width; st->codecpar->height = sc->height; @@ -2509,6 +2856,7 @@ static int mov_finalize_stsd_codec(MOVContext *c, AVIOContext *pb, case AV_CODEC_ID_VP9: sti->need_parsing = AVSTREAM_PARSE_FULL; break; + case AV_CODEC_ID_EVC: case AV_CODEC_ID_AV1: /* field_order detection of H264 requires parsing */ case AV_CODEC_ID_H264: @@ -2714,6 +3062,11 @@ static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom) MOVStreamContext *sc; unsigned int i, entries; + if (c->trak_index < 0) { + av_log(c->fc, AV_LOG_WARNING, "STSC outside TRAK\n"); + return 0; + } + if (c->fc->nb_streams < 1) return 0; st = c->fc->streams[c->fc->nb_streams-1]; @@ -2810,6 +3163,11 @@ static int mov_read_stps(MOVContext *c, AVIOContext *pb, MOVAtom atom) MOVStreamContext *sc; unsigned i, entries; + if (c->trak_index < 0) { + av_log(c->fc, AV_LOG_WARNING, "STPS outside TRAK\n"); + return 0; + } + if (c->fc->nb_streams < 1) return 0; st = c->fc->streams[c->fc->nb_streams-1]; @@ -2847,6 +3205,11 @@ static int mov_read_stss(MOVContext *c, AVIOContext *pb, MOVAtom atom) MOVStreamContext *sc; unsigned int i, entries; + if (c->trak_index < 0) { + av_log(c->fc, AV_LOG_WARNING, "STSS outside TRAK\n"); + return 0; + } + if (c->fc->nb_streams < 1) return 0; st = c->fc->streams[c->fc->nb_streams-1]; @@ -2899,6 +3262,11 @@ static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom) unsigned char* buf; int ret; + if (c->trak_index < 0) { + av_log(c->fc, AV_LOG_WARNING, "STSZ outside TRAK\n"); + return 0; + } + if (c->fc->nb_streams < 1) return 0; st = c->fc->streams[c->fc->nb_streams-1]; @@ -2963,9 +3331,9 @@ static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom) for (i = 0; i < entries; i++) { sc->sample_sizes[i] = get_bits_long(&gb, field_size); - if (sc->sample_sizes[i] < 0) { + if (sc->sample_sizes[i] > INT64_MAX - sc->data_size) { av_free(buf); - av_log(c->fc, AV_LOG_ERROR, "Invalid sample size %d\n", sc->sample_sizes[i]); + av_log(c->fc, AV_LOG_ERROR, "Sample size overflow in STSZ\n"); return AVERROR_INVALIDDATA; } sc->data_size += sc->sample_sizes[i]; @@ -2988,6 +3356,11 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) int64_t current_dts = 0; int64_t corrected_dts = 0; + if (c->trak_index < 0) { + av_log(c->fc, AV_LOG_WARNING, "STTS outside TRAK\n"); + return 0; + } + if (c->fc->nb_streams < 1) return 0; st = c->fc->streams[c->fc->nb_streams-1]; @@ -3041,15 +3414,15 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc->stts_data[i].duration = 1; corrected_dts += (delta_magnitude < 0 ? (int64_t)delta_magnitude : 1) * sample_count; } else { - corrected_dts += sample_duration * sample_count; + corrected_dts += sample_duration * (int64_t)sample_count; } - current_dts += sc->stts_data[i].duration * sample_count; + current_dts += sc->stts_data[i].duration * (int64_t)sample_count; if (current_dts > corrected_dts) { int64_t drift = (current_dts - corrected_dts)/FFMAX(sample_count, 1); uint32_t correction = (sc->stts_data[i].duration > drift) ? drift : sc->stts_data[i].duration - 1; - current_dts -= correction * sample_count; + current_dts -= correction * (uint64_t)sample_count; sc->stts_data[i].duration -= correction; } @@ -3086,7 +3459,7 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom) "All samples in data stream index:id [%d:%d] have zero " "duration, stream set to be discarded by default. Override " "using AVStream->discard or -discard for ffmpeg command.\n", - st->index, st->id); + st->index, sc->id); st->discard = AVDISCARD_ALL; } sc->track_end = duration; @@ -3144,6 +3517,11 @@ static int mov_read_ctts(MOVContext *c, AVIOContext *pb, MOVAtom atom) MOVStreamContext *sc; unsigned int i, entries, ctts_count = 0; + if (c->trak_index < 0) { + av_log(c->fc, AV_LOG_WARNING, "CTTS outside TRAK\n"); + return 0; + } + if (c->fc->nb_streams < 1) return 0; st = c->fc->streams[c->fc->nb_streams-1]; @@ -3341,6 +3719,10 @@ static int get_edit_list_entry(MOVContext *mov, } *edit_list_duration = av_rescale(*edit_list_duration, msc->time_scale, global_timescale); + + if (*edit_list_duration + (uint64_t)*edit_list_media_time > INT64_MAX) + *edit_list_duration = 0; + return 1; } @@ -3684,7 +4066,7 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) int num_discarded_begin = 0; int first_non_zero_audio_edit = -1; int packet_skip_samples = 0; - MOVIndexRange *current_index_range; + MOVIndexRange *current_index_range = NULL; int found_keyframe_after_edit = 0; int found_non_empty_edit = 0; @@ -3693,13 +4075,13 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) } // allocate the index ranges array - msc->index_ranges = av_malloc((msc->elst_count + 1) * sizeof(msc->index_ranges[0])); + msc->index_ranges = av_malloc_array(msc->elst_count + 1, + sizeof(msc->index_ranges[0])); if (!msc->index_ranges) { av_log(mov->fc, AV_LOG_ERROR, "Cannot allocate index ranges buffer\n"); return; } msc->current_index_range = msc->index_ranges; - current_index_range = msc->index_ranges - 1; // Clean AVStream from traces of old index sti->index_entries = NULL; @@ -3886,8 +4268,9 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) } // Update the index ranges array - if (current_index_range < msc->index_ranges || index != current_index_range->end) { - current_index_range++; + if (!current_index_range || index != current_index_range->end) { + current_index_range = current_index_range ? current_index_range + 1 + : msc->index_ranges; current_index_range->start = index; } current_index_range->end = index + 1; @@ -3950,7 +4333,8 @@ static void mov_fix_index(MOVContext *mov, AVStream *st) av_freep(&frame_duration_buffer); // Null terminate the index ranges array - current_index_range++; + current_index_range = current_index_range ? current_index_range + 1 + : msc->index_ranges; current_index_range->start = 0; current_index_range->end = 0; msc->current_index = msc->index_ranges[0].start; @@ -4466,16 +4850,56 @@ static void fix_timescale(MOVContext *c, MOVStreamContext *sc) } } +static int mov_update_iamf_streams(MOVContext *c, const AVStream *st) +{ + const MOVStreamContext *sc = st->priv_data; + const IAMFContext *iamf = &sc->iamf->iamf; + + for (int i = 0; i < iamf->nb_audio_elements; i++) { + const AVStreamGroup *stg = NULL; + + for (int j = 0; j < c->fc->nb_stream_groups; j++) + if (c->fc->stream_groups[j]->id == iamf->audio_elements[i]->audio_element_id) + stg = c->fc->stream_groups[j]; + av_assert0(stg); + + for (int j = 0; j < stg->nb_streams; j++) { + const FFStream *sti = cffstream(st); + AVStream *out = stg->streams[j]; + FFStream *out_sti = ffstream(stg->streams[j]); + + out->codecpar->bit_rate = 0; + + if (out == st) + continue; + + out->time_base = st->time_base; + out->start_time = st->start_time; + out->duration = st->duration; + out->nb_frames = st->nb_frames; + out->discard = st->discard; + + av_assert0(!out_sti->index_entries); + out_sti->index_entries = av_malloc(sti->index_entries_allocated_size); + if (!out_sti->index_entries) + return AVERROR(ENOMEM); + + out_sti->index_entries_allocated_size = sti->index_entries_allocated_size; + out_sti->nb_index_entries = sti->nb_index_entries; + out_sti->skip_samples = sti->skip_samples; + memcpy(out_sti->index_entries, sti->index_entries, sti->index_entries_allocated_size); + } + } + + return 0; +} + static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; MOVStreamContext *sc; int ret; - if (c->is_still_picture_avif) { - return AVERROR_INVALIDDATA; - } - st = avformat_new_stream(c->fc, NULL); if (!st) return AVERROR(ENOMEM); st->id = -1; @@ -4486,6 +4910,7 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) st->codecpar->codec_type = AVMEDIA_TYPE_DATA; sc->ffindex = st->index; c->trak_index = st->index; + sc->refcount = 1; if ((ret = mov_read_default(c, pb, atom)) < 0) return ret; @@ -4521,12 +4946,8 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) * Advanced edit list support does not work with fragemented MP4s, which * have stsc, stsz, stco, and stts with zero entries in the moov atom. * In these files, trun atoms may be streamed in. - * - * It cannot be used with use_mfra_for = {pts,dts} either, as the index - * is not complete, but filled in as more trun atoms are read, as well. */ - if (!sc->stts_count || c->use_mfra_for != FF_MOV_FLAG_MFRA_AUTO && - c->advanced_editlist) { + if (!sc->stts_count && c->advanced_editlist) { av_log(c->fc, AV_LOG_VERBOSE, "advanced_editlist does not work with fragmented " "MP4. disabling.\n"); @@ -4536,6 +4957,12 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom) mov_build_index(c, st); + if (sc->iamf) { + ret = mov_update_iamf_streams(c, st); + if (ret < 0) + return ret; + } + if (sc->dref_id-1 < sc->drefs_count && sc->drefs[sc->dref_id-1].path) { MOVDref *dref = &sc->drefs[sc->dref_id - 1]; if (c->enable_drefs) { @@ -4637,6 +5064,7 @@ static int mov_read_keys(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_skip(pb, 4); count = avio_rb32(pb); + atom.size -= 8; if (count > UINT_MAX / sizeof(*c->meta_keys) - 1) { av_log(c->fc, AV_LOG_ERROR, "The 'keys' atom with the invalid key count: %"PRIu32"\n", count); @@ -4651,15 +5079,17 @@ static int mov_read_keys(MOVContext *c, AVIOContext *pb, MOVAtom atom) for (i = 1; i <= count; ++i) { uint32_t key_size = avio_rb32(pb); uint32_t type = avio_rl32(pb); - if (key_size < 8) { + if (key_size < 8 || key_size > atom.size) { av_log(c->fc, AV_LOG_ERROR, "The key# %"PRIu32" in meta has invalid size:" "%"PRIu32"\n", i, key_size); return AVERROR_INVALIDDATA; } + atom.size -= key_size; key_size -= 8; if (type != MKTAG('m','d','t','a')) { avio_skip(pb, key_size); + continue; } c->meta_keys[i] = av_mallocz(key_size + 1); if (!c->meta_keys[i]) @@ -4751,38 +5181,35 @@ static int mov_read_custom(MOVContext *c, AVIOContext *pb, MOVAtom atom) return ret; } -static int avif_add_stream(MOVContext *c, int item_id) +static int heif_add_stream(MOVContext *c, HEIFItem *item) { MOVStreamContext *sc; AVStream *st; - int item_index = -1; - for (int i = 0; i < c->avif_info_size; i++) - if (c->avif_info[i].item_id == item_id) { - item_index = i; - break; - } - if (item_index < 0) - return AVERROR_INVALIDDATA; + st = avformat_new_stream(c->fc, NULL); if (!st) return AVERROR(ENOMEM); - st->id = c->fc->nb_streams; sc = av_mallocz(sizeof(MOVStreamContext)); if (!sc) return AVERROR(ENOMEM); + item->st = st; + st->id = item->item_id; st->priv_data = sc; st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; - st->codecpar->codec_id = AV_CODEC_ID_AV1; + st->codecpar->codec_id = mov_codec_id(st, item->type); + sc->id = st->id; sc->ffindex = st->index; - c->trak_index = st->index; st->avg_frame_rate.num = st->avg_frame_rate.den = 1; st->time_base.num = st->time_base.den = 1; st->nb_frames = 1; sc->time_scale = 1; - sc = st->priv_data; sc->pb = c->fc->pb; sc->pb_is_copied = 1; + sc->refcount = 1; + + if (item->name) + av_dict_set(&st->metadata, "title", item->name, 0); // Populate the necessary fields used by mov_build_index. sc->stsc_count = 1; @@ -4807,10 +5234,7 @@ static int avif_add_stream(MOVContext *c, int item_id) sc->stts_data[0].count = 1; // Not used for still images. But needed by mov_build_index. sc->stts_data[0].duration = 0; - sc->sample_sizes[0] = c->avif_info[item_index].extent_length; - sc->chunk_offsets[0] = c->avif_info[item_index].extent_offset; - mov_build_index(c, st); return 0; } @@ -4823,23 +5247,9 @@ static int mov_read_meta(MOVContext *c, AVIOContext *pb, MOVAtom atom) tag = avio_rl32(pb); atom.size -= 4; if (tag == MKTAG('h','d','l','r')) { - int ret; avio_seek(pb, -8, SEEK_CUR); atom.size += 8; - if ((ret = mov_read_default(c, pb, atom)) < 0) - return ret; - if (c->is_still_picture_avif) { - int ret; - // Add a stream for the YUV planes (primary item). - if ((ret = avif_add_stream(c, c->primary_item_id)) < 0) - return ret; - // For still AVIF images, the meta box contains all the - // necessary information that would generally be provided by the - // moov box. So simply mark that we have found the moov box so - // that parsing can continue. - c->found_moov = 1; - } - return ret; + return mov_read_default(c, pb, atom); } } return 0; @@ -4888,6 +5298,7 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) avio_rb32(pb); /* modification time */ } st->id = (int)avio_rb32(pb); /* track id (NOT 0 !)*/ + sc->id = st->id; avio_rb32(pb); /* reserved */ /* highlevel (considering edits) duration in movie timebase */ @@ -5062,7 +5473,8 @@ static int mov_read_tfdt(MOVContext *c, AVIOContext *pb, MOVAtom atom) int64_t base_media_decode_time; for (i = 0; i < c->fc->nb_streams; i++) { - if (c->fc->streams[i]->id == frag->track_id) { + sc = c->fc->streams[i]->priv_data; + if (sc->id == frag->track_id) { st = c->fc->streams[i]; break; } @@ -5115,7 +5527,8 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom) } for (i = 0; i < c->fc->nb_streams; i++) { - if (c->fc->streams[i]->id == frag->track_id) { + sc = c->fc->streams[i]->priv_data; + if (sc->id == frag->track_id) { st = c->fc->streams[i]; sti = ffstream(st); break; @@ -5418,7 +5831,8 @@ static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom) track_id = avio_rb32(pb); // Reference ID for (i = 0; i < c->fc->nb_streams; i++) { - if (c->fc->streams[i]->id == track_id) { + sc = c->fc->streams[i]->priv_data; + if (sc->id == track_id) { st = c->fc->streams[i]; break; } @@ -5590,7 +6004,7 @@ static int mov_read_cmov(MOVContext *c, AVIOContext *pb, MOVAtom atom) ret = AVERROR_INVALIDDATA; if (uncompress (moov_data, (uLongf *) &moov_len, (const Bytef *)cmov_data, cmov_len) != Z_OK) goto free_and_return; - ffio_init_context(&ctx, moov_data, moov_len, 0, NULL, NULL, NULL, NULL); + ffio_init_read_context(&ctx, moov_data, moov_len); ctx.pub.seekable = AVIO_SEEKABLE_NORMAL; atom.type = MKTAG('m','o','o','v'); atom.size = moov_len; @@ -5750,8 +6164,10 @@ static int mov_read_smdm(MOVContext *c, AVIOContext *pb, MOVAtom atom) av_log(c->fc, AV_LOG_WARNING, "Unsupported Mastering Display Metadata box version %d\n", version); return 0; } - if (sc->mastering) - return AVERROR_INVALIDDATA; + if (sc->mastering) { + av_log(c->fc, AV_LOG_WARNING, "Ignoring duplicate Mastering Display Metadata\n"); + return 0; + } avio_skip(pb, 3); /* flags */ @@ -5788,11 +6204,16 @@ static int mov_read_mdcv(MOVContext *c, AVIOContext *pb, MOVAtom atom) sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data; - if (atom.size < 24 || sc->mastering) { + if (atom.size < 24) { av_log(c->fc, AV_LOG_ERROR, "Invalid Mastering Display Color Volume box\n"); return AVERROR_INVALIDDATA; } + if (sc->mastering) { + av_log(c->fc, AV_LOG_WARNING, "Ignoring duplicate Mastering Display Color Volume\n"); + return 0; + } + sc->mastering = av_mastering_display_metadata_alloc(); if (!sc->mastering) return AVERROR(ENOMEM); @@ -5880,6 +6301,31 @@ static int mov_read_clli(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } +static int mov_read_amve(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + MOVStreamContext *sc; + const int illuminance_den = 10000; + const int ambient_den = 50000; + if (c->fc->nb_streams < 1) + return AVERROR_INVALIDDATA; + sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data; + if (atom.size < 6) { + av_log(c->fc, AV_LOG_ERROR, "Empty Ambient Viewing Environment Info box\n"); + return AVERROR_INVALIDDATA; + } + if (sc->ambient){ + av_log(c->fc, AV_LOG_WARNING, "Ignoring duplicate AMVE\n"); + return 0; + } + sc->ambient = av_ambient_viewing_environment_alloc(&sc->ambient_size); + if (!sc->ambient) + return AVERROR(ENOMEM); + sc->ambient->ambient_illuminance = av_make_q(avio_rb32(pb), illuminance_den); + sc->ambient->ambient_light_x = av_make_q(avio_rb16(pb), ambient_den); + sc->ambient->ambient_light_y = av_make_q(avio_rb16(pb), ambient_den); + return 0; +} + static int mov_read_st3d(MOVContext *c, AVIOContext *pb, MOVAtom atom) { AVStream *st; @@ -6310,7 +6756,8 @@ static int get_current_encryption_info(MOVContext *c, MOVEncryptionIndex **encry frag_stream_info = get_current_frag_stream_info(&c->frag_index); if (frag_stream_info) { for (i = 0; i < c->fc->nb_streams; i++) { - if (c->fc->streams[i]->id == frag_stream_info->id) { + *sc = c->fc->streams[i]->priv_data; + if ((*sc)->id == frag_stream_info->id) { st = c->fc->streams[i]; break; } @@ -6362,9 +6809,12 @@ static int mov_read_sample_encryption_info(MOVContext *c, AVIOContext *pb, MOVSt return AVERROR_INVALIDDATA; } - *sample = av_encryption_info_clone(sc->cenc.default_encrypted_sample); - if (!*sample) - return AVERROR(ENOMEM); + if (sc->cenc.per_sample_iv_size || use_subsamples) { + *sample = av_encryption_info_clone(sc->cenc.default_encrypted_sample); + if (!*sample) + return AVERROR(ENOMEM); + } else + *sample = NULL; if (sc->cenc.per_sample_iv_size != 0) { if ((ret = ffio_read_size(pb, (*sample)->iv, sc->cenc.per_sample_iv_size)) < 0) { @@ -6520,38 +6970,6 @@ static int mov_parse_auxiliary_info(MOVContext *c, MOVStreamContext *sc, AVIOCon return ret; } -/** - * Tries to read the given number of bytes from the stream and puts it in a - * newly allocated buffer. This reads in small chunks to avoid allocating large - * memory if the file contains an invalid/malicious size value. - */ -static int mov_try_read_block(AVIOContext *pb, size_t size, uint8_t **data) -{ - const unsigned int block_size = 1024 * 1024; - uint8_t *buffer = NULL; - unsigned int alloc_size = 0, offset = 0; - while (offset < size) { - unsigned int new_size = - alloc_size >= INT_MAX - block_size ? INT_MAX : alloc_size + block_size; - uint8_t *new_buffer = av_fast_realloc(buffer, &alloc_size, new_size); - unsigned int to_read = FFMIN(size, alloc_size) - offset; - if (!new_buffer) { - av_free(buffer); - return AVERROR(ENOMEM); - } - buffer = new_buffer; - - if (avio_read(pb, buffer + offset, to_read) != to_read) { - av_free(buffer); - return AVERROR_INVALIDDATA; - } - offset += to_read; - } - - *data = buffer; - return 0; -} - static int mov_read_saiz(MOVContext *c, AVIOContext *pb, MOVAtom atom) { MOVEncryptionIndex *encryption_index; @@ -6607,15 +7025,27 @@ static int mov_read_saiz(MOVContext *c, AVIOContext *pb, MOVAtom atom) encryption_index->auxiliary_info_default_size = avio_r8(pb); sample_count = avio_rb32(pb); - encryption_index->auxiliary_info_sample_count = sample_count; if (encryption_index->auxiliary_info_default_size == 0) { - ret = mov_try_read_block(pb, sample_count, &encryption_index->auxiliary_info_sizes); - if (ret < 0) { - av_log(c->fc, AV_LOG_ERROR, "Failed to read the auxiliary info\n"); + if (sample_count == 0) + return AVERROR_INVALIDDATA; + + encryption_index->auxiliary_info_sizes = av_malloc(sample_count); + if (!encryption_index->auxiliary_info_sizes) + return AVERROR(ENOMEM); + + ret = avio_read(pb, encryption_index->auxiliary_info_sizes, sample_count); + if (ret != sample_count) { + av_freep(&encryption_index->auxiliary_info_sizes); + + if (ret >= 0) + ret = AVERROR_INVALIDDATA; + av_log(c->fc, AV_LOG_ERROR, "Failed to read the auxiliary info, %s\n", + av_err2str(ret)); return ret; } } + encryption_index->auxiliary_info_sample_count = sample_count; if (encryption_index->auxiliary_offsets_count) { return mov_parse_auxiliary_info(c, sc, pb, encryption_index); @@ -6724,8 +7154,9 @@ static int mov_read_pssh(MOVContext *c, AVIOContext *pb, MOVAtom atom) AVEncryptionInitInfo *info, *old_init_info; uint8_t **key_ids; AVStream *st; - uint8_t *side_data, *extra_data, *old_side_data; - size_t side_data_size, old_side_data_size; + const AVPacketSideData *old_side_data; + uint8_t *side_data, *extra_data; + size_t side_data_size; int ret = 0; unsigned int version, kid_count, extra_data_size, alloc_size = 0; @@ -6784,18 +7215,29 @@ static int mov_read_pssh(MOVContext *c, AVIOContext *pb, MOVAtom atom) } extra_data_size = avio_rb32(pb); - ret = mov_try_read_block(pb, extra_data_size, &extra_data); - if (ret < 0) + extra_data = av_malloc(extra_data_size); + if (!extra_data) { + ret = AVERROR(ENOMEM); + goto finish; + } + ret = avio_read(pb, extra_data, extra_data_size); + if (ret != extra_data_size) { + av_free(extra_data); + + if (ret >= 0) + ret = AVERROR_INVALIDDATA; goto finish; + } av_freep(&info->data); // malloc(0) may still allocate something. info->data = extra_data; info->data_size = extra_data_size; // If there is existing initialization data, append to the list. - old_side_data = av_stream_get_side_data(st, AV_PKT_DATA_ENCRYPTION_INIT_INFO, &old_side_data_size); + old_side_data = av_packet_side_data_get(st->codecpar->coded_side_data, st->codecpar->nb_coded_side_data, + AV_PKT_DATA_ENCRYPTION_INIT_INFO); if (old_side_data) { - old_init_info = av_encryption_init_info_get_side_data(old_side_data, old_side_data_size); + old_init_info = av_encryption_init_info_get_side_data(old_side_data->data, old_side_data->size); if (old_init_info) { // Append to the end of the list. for (AVEncryptionInitInfo *cur = old_init_info;; cur = cur->next) { @@ -6817,9 +7259,10 @@ static int mov_read_pssh(MOVContext *c, AVIOContext *pb, MOVAtom atom) ret = AVERROR(ENOMEM); goto finish; } - ret = av_stream_add_side_data(st, AV_PKT_DATA_ENCRYPTION_INIT_INFO, - side_data, side_data_size); - if (ret < 0) + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, + &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_ENCRYPTION_INIT_INFO, + side_data, side_data_size, 0)) av_free(side_data); finish: @@ -7261,7 +7704,7 @@ static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPa AVEncryptionInfo *encrypted_sample; int encrypted_index, ret; - frag_stream_info = get_frag_stream_info_from_pkt(&mov->frag_index, pkt, st->id); + frag_stream_info = get_frag_stream_info_from_pkt(&mov->frag_index, pkt, sc->id); encrypted_index = current_index; encryption_index = NULL; if (frag_stream_info) { @@ -7296,6 +7739,8 @@ static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPa } else if (encrypted_index >= 0 && encrypted_index < encryption_index->nb_encrypted_samples) { // Per-sample setting override. encrypted_sample = encryption_index->encrypted_samples[encrypted_index]; + if (!encrypted_sample) + encrypted_sample = sc->cenc.default_encrypted_sample; } else { av_log(mov->fc, AV_LOG_ERROR, "Incorrect number of samples in encryption info\n"); return AVERROR_INVALIDDATA; @@ -7605,7 +8050,7 @@ static int mov_read_SAND(MOVContext *c, AVIOContext *pb, MOVAtom atom) return 0; } -static int rb_size(AVIOContext *pb, uint64_t* value, int size) +static int rb_size(AVIOContext *pb, int64_t *value, int size) { if (size == 0) *value = 0; @@ -7615,9 +8060,11 @@ static int rb_size(AVIOContext *pb, uint64_t* value, int size) *value = avio_rb16(pb); else if (size == 4) *value = avio_rb32(pb); - else if (size == 8) + else if (size == 8) { *value = avio_rb64(pb); - else + if (*value < 0) + return -1; + } else return -1; return size; } @@ -7626,28 +8073,28 @@ static int mov_read_pitm(MOVContext *c, AVIOContext *pb, MOVAtom atom) { avio_rb32(pb); // version & flags. c->primary_item_id = avio_rb16(pb); + av_log(c->fc, AV_LOG_TRACE, "pitm: primary_item_id %d\n", c->primary_item_id); return atom.size; } +static int mov_read_idat(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + c->idat_offset = avio_tell(pb); + return 0; +} + static int mov_read_iloc(MOVContext *c, AVIOContext *pb, MOVAtom atom) { + HEIFItem *heif_item; int version, offset_size, length_size, base_offset_size, index_size; int item_count, extent_count; - uint64_t base_offset, extent_offset, extent_length; + int64_t base_offset, extent_offset, extent_length; uint8_t value; - if (!c->is_still_picture_avif) { - // * For non-avif, we simply ignore the iloc box. - // * For animated avif, we don't care about the iloc box as all the - // necessary information can be found in the moov box. - return 0; - } - - if (c->avif_info) { + if (c->found_iloc) { av_log(c->fc, AV_LOG_INFO, "Duplicate iloc box found\n"); return 0; } - av_assert0(!c->fc->nb_streams); version = avio_r8(pb); avio_rb24(pb); // flags. @@ -7659,45 +8106,429 @@ static int mov_read_iloc(MOVContext *c, AVIOContext *pb, MOVAtom atom) base_offset_size = (value >> 4) & 0xF; index_size = !version ? 0 : (value & 0xF); if (index_size) { - av_log(c->fc, AV_LOG_ERROR, "iloc: index_size != 0 not supported.\n"); + avpriv_report_missing_feature(c->fc, "iloc: index_size != 0"); return AVERROR_PATCHWELCOME; } item_count = (version < 2) ? avio_rb16(pb) : avio_rb32(pb); - c->avif_info = av_malloc_array(item_count, sizeof(*c->avif_info)); - if (!c->avif_info) + heif_item = av_realloc_array(c->heif_item, FFMAX(item_count, c->nb_heif_item), sizeof(*c->heif_item)); + if (!heif_item) return AVERROR(ENOMEM); - c->avif_info_size = item_count; + c->heif_item = heif_item; + if (item_count > c->nb_heif_item) + memset(c->heif_item + c->nb_heif_item, 0, + sizeof(*c->heif_item) * (item_count - c->nb_heif_item)); + c->nb_heif_item = FFMAX(c->nb_heif_item, item_count); + av_log(c->fc, AV_LOG_TRACE, "iloc: item_count %d\n", item_count); for (int i = 0; i < item_count; i++) { int item_id = (version < 2) ? avio_rb16(pb) : avio_rb32(pb); + int offset_type = (version > 0) ? avio_rb16(pb) & 0xf : 0; + if (avio_feof(pb)) return AVERROR_INVALIDDATA; - c->avif_info[i].item_id = item_id; + if (offset_type > 1) { + avpriv_report_missing_feature(c->fc, "iloc offset type %d", offset_type); + return AVERROR_PATCHWELCOME; + } + c->heif_item[i].item_id = item_id; - if (version > 0) - avio_rb16(pb); // construction_method. avio_rb16(pb); // data_reference_index. if (rb_size(pb, &base_offset, base_offset_size) < 0) return AVERROR_INVALIDDATA; extent_count = avio_rb16(pb); if (extent_count > 1) { // For still AVIF images, we only support one extent item. - av_log(c->fc, AV_LOG_ERROR, "iloc: extent_count > 1 not supported.\n"); + avpriv_report_missing_feature(c->fc, "iloc: extent_count > 1"); return AVERROR_PATCHWELCOME; } for (int j = 0; j < extent_count; j++) { if (rb_size(pb, &extent_offset, offset_size) < 0 || - rb_size(pb, &extent_length, length_size) < 0) + rb_size(pb, &extent_length, length_size) < 0 || + base_offset > INT64_MAX - extent_offset) return AVERROR_INVALIDDATA; - c->avif_info[i].extent_length = extent_length; - c->avif_info[i].extent_offset = base_offset + extent_offset; + if (offset_type == 1) + c->heif_item[i].is_idat_relative = 1; + c->heif_item[i].extent_length = extent_length; + c->heif_item[i].extent_offset = base_offset + extent_offset; + av_log(c->fc, AV_LOG_TRACE, "iloc: item_idx %d, offset_type %d, " + "extent_offset %"PRId64", extent_length %"PRId64"\n", + i, offset_type, c->heif_item[i].extent_offset, c->heif_item[i].extent_length); } } + c->found_iloc = 1; return atom.size; } +static int mov_read_infe(MOVContext *c, AVIOContext *pb, MOVAtom atom, int idx) +{ + AVBPrint item_name; + int64_t size = atom.size; + uint32_t item_type; + int item_id; + int version, ret; + + version = avio_r8(pb); + avio_rb24(pb); // flags. + size -= 4; + if (size < 0) + return AVERROR_INVALIDDATA; + + if (version < 2) { + avpriv_report_missing_feature(c->fc, "infe version < 2"); + avio_skip(pb, size); + return 1; + } + + item_id = version > 2 ? avio_rb32(pb) : avio_rb16(pb); + avio_rb16(pb); // item_protection_index + item_type = avio_rl32(pb); + size -= 8; + if (size < 1) + return AVERROR_INVALIDDATA; + + av_bprint_init(&item_name, 0, AV_BPRINT_SIZE_UNLIMITED); + ret = ff_read_string_to_bprint_overwrite(pb, &item_name, size); + if (ret < 0) { + av_bprint_finalize(&item_name, NULL); + return ret; + } + + av_log(c->fc, AV_LOG_TRACE, "infe: item_id %d, item_type %s, item_name %s\n", + item_id, av_fourcc2str(item_type), item_name.str); + + size -= ret + 1; + if (size > 0) + avio_skip(pb, size); + + if (ret) + av_bprint_finalize(&item_name, &c->heif_item[idx].name); + c->heif_item[idx].item_id = item_id; + c->heif_item[idx].type = item_type; + + switch (item_type) { + case MKTAG('a','v','0','1'): + case MKTAG('h','v','c','1'): + ret = heif_add_stream(c, &c->heif_item[idx]); + if (ret < 0) + return ret; + break; + } + + return 0; +} + +static int mov_read_iinf(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + HEIFItem *heif_item; + int entry_count; + int version, got_stream = 0, ret, i; + + if (c->found_iinf) { + av_log(c->fc, AV_LOG_WARNING, "Duplicate iinf box found\n"); + return 0; + } + + version = avio_r8(pb); + avio_rb24(pb); // flags. + entry_count = version ? avio_rb32(pb) : avio_rb16(pb); + + heif_item = av_realloc_array(c->heif_item, FFMAX(entry_count, c->nb_heif_item), sizeof(*c->heif_item)); + if (!heif_item) + return AVERROR(ENOMEM); + c->heif_item = heif_item; + if (entry_count > c->nb_heif_item) + memset(c->heif_item + c->nb_heif_item, 0, + sizeof(*c->heif_item) * (entry_count - c->nb_heif_item)); + c->nb_heif_item = FFMAX(c->nb_heif_item, entry_count); + + for (i = 0; i < entry_count; i++) { + MOVAtom infe; + + if (avio_feof(pb)) + return AVERROR_INVALIDDATA; + infe.size = avio_rb32(pb) - 8; + infe.type = avio_rl32(pb); + ret = mov_read_infe(c, pb, infe, i); + if (ret < 0) + goto fail; + if (!ret) + got_stream = 1; + } + + c->found_iinf = got_stream; + return 0; +fail: + for (; i >= 0; i--) { + HEIFItem *item = &c->heif_item[i]; + + av_freep(&item->name); + if (!item->st) + continue; + + mov_free_stream_context(c->fc, item->st); + ff_remove_stream(c->fc, item->st); + item->st = NULL; + } + return ret; +} + +static int mov_read_iref_dimg(MOVContext *c, AVIOContext *pb, int version) +{ + HEIFItem *item = NULL; + HEIFGrid *grid; + int entries, i; + int from_item_id = version ? avio_rb32(pb) : avio_rb16(pb); + + for (int i = 0; i < c->nb_heif_grid; i++) { + if (c->heif_grid[i].item->item_id == from_item_id) { + av_log(c->fc, AV_LOG_ERROR, "More than one 'dimg' box " + "referencing the same Derived Image item\n"); + return AVERROR_INVALIDDATA; + } + } + for (int i = 0; i < c->nb_heif_item; i++) { + if (c->heif_item[i].item_id != from_item_id) + continue; + item = &c->heif_item[i]; + + switch (item->type) { + case MKTAG('g','r','i','d'): + case MKTAG('i','o','v','l'): + break; + default: + avpriv_report_missing_feature(c->fc, "Derived Image item of type %s", + av_fourcc2str(item->type)); + return 0; + } + break; + } + if (!item) { + av_log(c->fc, AV_LOG_ERROR, "Missing grid information\n"); + return AVERROR_INVALIDDATA; + } + + grid = av_realloc_array(c->heif_grid, c->nb_heif_grid + 1U, + sizeof(*c->heif_grid)); + if (!grid) + return AVERROR(ENOMEM); + c->heif_grid = grid; + grid = &grid[c->nb_heif_grid++]; + + entries = avio_rb16(pb); + grid->tile_id_list = av_malloc_array(entries, sizeof(*grid->tile_id_list)); + grid->tile_item_list = av_calloc(entries, sizeof(*grid->tile_item_list)); + if (!grid->tile_id_list || !grid->tile_item_list) + return AVERROR(ENOMEM); + /* 'to' item ids */ + for (i = 0; i < entries; i++) + grid->tile_id_list[i] = version ? avio_rb32(pb) : avio_rb16(pb); + grid->nb_tiles = entries; + grid->item = item; + + av_log(c->fc, AV_LOG_TRACE, "dimg: from_item_id %d, entries %d\n", + from_item_id, entries); + + return 0; +} + +static int mov_read_iref_thmb(MOVContext *c, AVIOContext *pb, int version) +{ + int entries; + int to_item_id, from_item_id = version ? avio_rb32(pb) : avio_rb16(pb); + + entries = avio_rb16(pb); + if (entries > 1) { + avpriv_request_sample(c->fc, "thmb in iref referencing several items"); + return AVERROR_PATCHWELCOME; + } + /* 'to' item ids */ + to_item_id = version ? avio_rb32(pb) : avio_rb16(pb); + + if (to_item_id != c->primary_item_id) + return 0; + + c->thmb_item_id = from_item_id; + + av_log(c->fc, AV_LOG_TRACE, "thmb: from_item_id %d, entries %d\n", + from_item_id, entries); + + return 0; +} + +static int mov_read_iref(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + int version = avio_r8(pb); + avio_rb24(pb); // flags + atom.size -= 4; + + if (version > 1) { + av_log(c->fc, AV_LOG_WARNING, "Unknown iref box version %d\n", version); + return 0; + } + + while (atom.size) { + uint32_t type, size = avio_rb32(pb); + int64_t next = avio_tell(pb); + + if (size < 14 || next < 0 || next > INT64_MAX - size) + return AVERROR_INVALIDDATA; + + next += size - 4; + type = avio_rl32(pb); + switch (type) { + case MKTAG('d','i','m','g'): + mov_read_iref_dimg(c, pb, version); + break; + case MKTAG('t','h','m','b'): + mov_read_iref_thmb(c, pb, version); + break; + default: + av_log(c->fc, AV_LOG_DEBUG, "Unknown iref type %s size %"PRIu32"\n", + av_fourcc2str(type), size); + } + + atom.size -= size; + avio_seek(pb, next, SEEK_SET); + } + return 0; +} + +static int mov_read_ispe(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + uint32_t width, height; + + avio_r8(pb); /* version */ + avio_rb24(pb); /* flags */ + width = avio_rb32(pb); + height = avio_rb32(pb); + + av_log(c->fc, AV_LOG_TRACE, "ispe: item_id %d, width %u, height %u\n", + c->cur_item_id, width, height); + + for (int i = 0; i < c->nb_heif_item; i++) { + if (c->heif_item[i].item_id == c->cur_item_id) { + c->heif_item[i].width = width; + c->heif_item[i].height = height; + break; + } + } + + return 0; +} + +static int mov_read_iprp(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + typedef struct MOVAtoms { + FFIOContext b; + uint32_t type; + int64_t size; + uint8_t *data; + } MOVAtoms; + MOVAtoms *atoms = NULL; + MOVAtom a; + unsigned count; + int nb_atoms = 0; + int version, flags; + int ret; + + a.size = avio_rb32(pb); + a.type = avio_rl32(pb); + + if (a.size < 8 || a.type != MKTAG('i','p','c','o')) + return AVERROR_INVALIDDATA; + + a.size -= 8; + while (a.size >= 8) { + MOVAtoms *ref = av_dynarray2_add((void**)&atoms, &nb_atoms, sizeof(MOVAtoms), NULL); + if (!ref) { + ret = AVERROR(ENOMEM); + goto fail; + } + ref->data = NULL; + ref->size = avio_rb32(pb); + ref->type = avio_rl32(pb); + if (ref->size > a.size || ref->size < 8) + break; + ref->data = av_malloc(ref->size); + if (!ref->data) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + av_log(c->fc, AV_LOG_TRACE, "ipco: index %d, box type %s\n", nb_atoms, av_fourcc2str(ref->type)); + avio_seek(pb, -8, SEEK_CUR); + if (avio_read(pb, ref->data, ref->size) != ref->size) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + ffio_init_read_context(&ref->b, ref->data, ref->size); + a.size -= ref->size; + } + + if (a.size) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + a.size = avio_rb32(pb); + a.type = avio_rl32(pb); + + if (a.size < 8 || a.type != MKTAG('i','p','m','a')) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + version = avio_r8(pb); + flags = avio_rb24(pb); + count = avio_rb32(pb); + + for (int i = 0; i < count; i++) { + int item_id = version ? avio_rb32(pb) : avio_rb16(pb); + int assoc_count = avio_r8(pb); + + if (avio_feof(pb)) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + for (int j = 0; j < assoc_count; j++) { + MOVAtoms *ref; + int index = avio_r8(pb) & 0x7f; + if (flags & 1) { + index <<= 8; + index |= avio_r8(pb); + } + if (index > nb_atoms || index <= 0) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + ref = &atoms[--index]; + + av_log(c->fc, AV_LOG_TRACE, "ipma: property_index %d, item_id %d, item_type %s\n", + index + 1, item_id, av_fourcc2str(ref->type)); + + c->cur_item_id = item_id; + + ret = mov_read_default(c, &ref->b.pub, + (MOVAtom) { .size = ref->size, + .type = MKTAG('i','p','c','o') }); + if (ret < 0) + goto fail; + ffio_init_read_context(&ref->b, ref->data, ref->size); + } + } + + ret = 0; +fail: + c->cur_item_id = -1; + for (int i = 0; i < nb_atoms; i++) + av_free(atoms[i].data); + av_free(atoms); + + return ret; +} + static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('A','C','L','R'), mov_read_aclr }, { MKTAG('A','P','R','G'), mov_read_avid }, @@ -7766,11 +8597,13 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('w','i','d','e'), mov_read_wide }, /* place holder */ { MKTAG('w','f','e','x'), mov_read_wfex }, { MKTAG('c','m','o','v'), mov_read_cmov }, -{ MKTAG('c','h','a','n'), mov_read_chan }, /* channel layout */ +{ MKTAG('c','h','a','n'), mov_read_chan }, /* channel layout from quicktime */ +{ MKTAG('c','h','n','l'), mov_read_chnl }, /* channel layout from ISO-14496-12 */ { MKTAG('d','v','c','1'), mov_read_dvc1 }, { MKTAG('s','g','p','d'), mov_read_sgpd }, { MKTAG('s','b','g','p'), mov_read_sbgp }, { MKTAG('h','v','c','C'), mov_read_glbl }, +{ MKTAG('v','v','c','C'), mov_read_glbl }, { MKTAG('u','u','i','d'), mov_read_uuid }, { MKTAG('C','i','n', 0x8e), mov_read_targa_y216 }, { MKTAG('f','r','e','e'), mov_read_free }, @@ -7803,6 +8636,14 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('i','l','o','c'), mov_read_iloc }, { MKTAG('p','c','m','C'), mov_read_pcmc }, /* PCM configuration box */ { MKTAG('p','i','t','m'), mov_read_pitm }, +{ MKTAG('e','v','c','C'), mov_read_glbl }, +{ MKTAG('i','d','a','t'), mov_read_idat }, +{ MKTAG('i','r','e','f'), mov_read_iref }, +{ MKTAG('i','s','p','e'), mov_read_ispe }, +{ MKTAG('i','p','r','p'), mov_read_iprp }, +{ MKTAG('i','i','n','f'), mov_read_iinf }, +{ MKTAG('a','m','v','e'), mov_read_amve }, /* ambient viewing environment box */ +{ MKTAG('i','a','c','b'), mov_read_iacb }, { 0, NULL } }; @@ -7982,6 +8823,8 @@ static int mov_probe(const AVProbeData *p) score = FFMAX(score, AVPROBE_SCORE_MAX - 5); break; case MKTAG(0x82,0x82,0x7f,0x7d): + score = FFMAX(score, AVPROBE_SCORE_EXTENSION - 5); + break; case MKTAG('s','k','i','p'): case MKTAG('u','u','i','d'): case MKTAG('p','r','f','l'): @@ -8032,11 +8875,13 @@ static void mov_read_chapters(AVFormatContext *s) AVStream *st = NULL; FFStream *sti = NULL; chapter_track = mov->chapter_tracks[j]; - for (i = 0; i < s->nb_streams; i++) - if (s->streams[i]->id == chapter_track) { + for (i = 0; i < s->nb_streams; i++) { + sc = mov->fc->streams[i]->priv_data; + if (sc->id == chapter_track) { st = s->streams[i]; break; } + } if (!st) { av_log(s, AV_LOG_ERROR, "Referenced QT chapter track not found\n"); continue; @@ -8048,7 +8893,7 @@ static void mov_read_chapters(AVFormatContext *s) if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { st->disposition |= AV_DISPOSITION_ATTACHED_PIC | AV_DISPOSITION_TIMED_THUMBNAILS; - if (sti->nb_index_entries) { + if (!st->attached_pic.data && sti->nb_index_entries) { // Retrieve the first frame, if possible AVIndexEntry *sample = &sti->index_entries[0]; if (avio_seek(sc->pb, sample->pos, SEEK_SET) != sample->pos) { @@ -8190,7 +9035,7 @@ static int mov_read_timecode_track(AVFormatContext *s, AVStream *st) /* 60 fps content have tmcd_nb_frames set to 30 but tc_rate set to 60, so * we multiply the frame number with the quotient. * See tickets #9492, #9710. */ - rounded_tc_rate = (tc_rate.num + tc_rate.den / 2) / tc_rate.den; + rounded_tc_rate = (tc_rate.num + tc_rate.den / 2LL) / tc_rate.den; /* Work around files where tmcd_nb_frames is rounded down from frame rate * instead of up. See ticket #5978. */ if (tmcd_nb_frames == tc_rate.num / tc_rate.den && @@ -8216,61 +9061,74 @@ static void mov_free_encryption_index(MOVEncryptionIndex **index) { av_freep(index); } -static int mov_read_close(AVFormatContext *s) +static void mov_free_stream_context(AVFormatContext *s, AVStream *st) { - MOVContext *mov = s->priv_data; - int i, j; + MOVStreamContext *sc = st->priv_data; - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - MOVStreamContext *sc = st->priv_data; + if (!sc || --sc->refcount) { + st->priv_data = NULL; + return; + } - if (!sc) - continue; + av_freep(&sc->ctts_data); + for (int i = 0; i < sc->drefs_count; i++) { + av_freep(&sc->drefs[i].path); + av_freep(&sc->drefs[i].dir); + } + av_freep(&sc->drefs); - av_freep(&sc->ctts_data); - for (j = 0; j < sc->drefs_count; j++) { - av_freep(&sc->drefs[j].path); - av_freep(&sc->drefs[j].dir); - } - av_freep(&sc->drefs); + sc->drefs_count = 0; - sc->drefs_count = 0; + if (!sc->pb_is_copied) + ff_format_io_close(s, &sc->pb); - if (!sc->pb_is_copied) - ff_format_io_close(s, &sc->pb); + sc->pb = NULL; + av_freep(&sc->chunk_offsets); + av_freep(&sc->stsc_data); + av_freep(&sc->sample_sizes); + av_freep(&sc->keyframes); + av_freep(&sc->stts_data); + av_freep(&sc->sdtp_data); + av_freep(&sc->stps_data); + av_freep(&sc->elst_data); + av_freep(&sc->rap_group); + av_freep(&sc->sync_group); + av_freep(&sc->sgpd_sync); + av_freep(&sc->sample_offsets); + av_freep(&sc->open_key_samples); + av_freep(&sc->display_matrix); + av_freep(&sc->index_ranges); - sc->pb = NULL; - av_freep(&sc->chunk_offsets); - av_freep(&sc->stsc_data); - av_freep(&sc->sample_sizes); - av_freep(&sc->keyframes); - av_freep(&sc->stts_data); - av_freep(&sc->sdtp_data); - av_freep(&sc->stps_data); - av_freep(&sc->elst_data); - av_freep(&sc->rap_group); - av_freep(&sc->sync_group); - av_freep(&sc->sgpd_sync); - av_freep(&sc->sample_offsets); - av_freep(&sc->open_key_samples); - av_freep(&sc->display_matrix); - av_freep(&sc->index_ranges); + if (sc->extradata) + for (int i = 0; i < sc->stsd_count; i++) + av_free(sc->extradata[i]); + av_freep(&sc->extradata); + av_freep(&sc->extradata_size); - if (sc->extradata) - for (j = 0; j < sc->stsd_count; j++) - av_free(sc->extradata[j]); - av_freep(&sc->extradata); - av_freep(&sc->extradata_size); + mov_free_encryption_index(&sc->cenc.encryption_index); + av_encryption_info_free(sc->cenc.default_encrypted_sample); + av_aes_ctr_free(sc->cenc.aes_ctr); - mov_free_encryption_index(&sc->cenc.encryption_index); - av_encryption_info_free(sc->cenc.default_encrypted_sample); - av_aes_ctr_free(sc->cenc.aes_ctr); + av_freep(&sc->stereo3d); + av_freep(&sc->spherical); + av_freep(&sc->mastering); + av_freep(&sc->coll); + av_freep(&sc->ambient); - av_freep(&sc->stereo3d); - av_freep(&sc->spherical); - av_freep(&sc->mastering); - av_freep(&sc->coll); + if (sc->iamf) + ff_iamf_read_deinit(sc->iamf); + av_freep(&sc->iamf); +} + +static int mov_read_close(AVFormatContext *s) +{ + MOVContext *mov = s->priv_data; + int i, j; + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + + mov_free_stream_context(s, st); } av_freep(&mov->dv_demux); @@ -8298,7 +9156,14 @@ static int mov_read_close(AVFormatContext *s) av_freep(&mov->aes_decrypt); av_freep(&mov->chapter_tracks); - av_freep(&mov->avif_info); + for (i = 0; i < mov->nb_heif_item; i++) + av_freep(&mov->heif_item[i].name); + av_freep(&mov->heif_item); + for (i = 0; i < mov->nb_heif_grid; i++) { + av_freep(&mov->heif_grid[i].tile_id_list); + av_freep(&mov->heif_grid[i].tile_item_list); + } + av_freep(&mov->heif_grid); return 0; } @@ -8438,6 +9303,239 @@ static int mov_read_mfra(MOVContext *c, AVIOContext *f) return ret; } +static int read_image_grid(AVFormatContext *s, const HEIFGrid *grid, + AVStreamGroupTileGrid *tile_grid) +{ + MOVContext *c = s->priv_data; + const HEIFItem *item = grid->item; + int64_t offset = 0, pos = avio_tell(s->pb); + int x = 0, y = 0, i = 0; + int tile_rows, tile_cols; + int flags, size; + + if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) { + av_log(c->fc, AV_LOG_INFO, "grid box with non seekable input\n"); + return AVERROR_PATCHWELCOME; + } + if (item->is_idat_relative) { + if (!c->idat_offset) { + av_log(c->fc, AV_LOG_ERROR, "missing idat box required by the image grid\n"); + return AVERROR_INVALIDDATA; + } + offset = c->idat_offset; + } + + avio_seek(s->pb, item->extent_offset + offset, SEEK_SET); + + avio_r8(s->pb); /* version */ + flags = avio_r8(s->pb); + + tile_rows = avio_r8(s->pb) + 1; + tile_cols = avio_r8(s->pb) + 1; + /* actual width and height of output image */ + tile_grid->width = (flags & 1) ? avio_rb32(s->pb) : avio_rb16(s->pb); + tile_grid->height = (flags & 1) ? avio_rb32(s->pb) : avio_rb16(s->pb); + + av_log(c->fc, AV_LOG_TRACE, "grid: grid_rows %d grid_cols %d output_width %d output_height %d\n", + tile_rows, tile_cols, tile_grid->width, tile_grid->height); + + avio_seek(s->pb, pos, SEEK_SET); + + size = tile_rows * tile_cols; + tile_grid->nb_tiles = grid->nb_tiles; + + if (tile_grid->nb_tiles != size) + return AVERROR_INVALIDDATA; + + for (int i = 0; i < tile_cols; i++) + tile_grid->coded_width += grid->tile_item_list[i]->width; + for (int i = 0; i < size; i += tile_cols) + tile_grid->coded_height += grid->tile_item_list[i]->height; + + tile_grid->offsets = av_calloc(tile_grid->nb_tiles, sizeof(*tile_grid->offsets)); + if (!tile_grid->offsets) + return AVERROR(ENOMEM); + + while (y < tile_grid->coded_height) { + int left_col = i; + + while (x < tile_grid->coded_width) { + if (i == tile_grid->nb_tiles) + return AVERROR_INVALIDDATA; + + tile_grid->offsets[i].idx = i; + tile_grid->offsets[i].horizontal = x; + tile_grid->offsets[i].vertical = y; + + x += grid->tile_item_list[i++]->width; + } + + if (x > tile_grid->coded_width) { + av_log(c->fc, AV_LOG_ERROR, "Non uniform HEIF tiles\n"); + return AVERROR_INVALIDDATA; + } + + x = 0; + y += grid->tile_item_list[left_col]->height; + } + + if (y > tile_grid->coded_height || i != tile_grid->nb_tiles) { + av_log(c->fc, AV_LOG_ERROR, "Non uniform HEIF tiles\n"); + return AVERROR_INVALIDDATA; + } + + return 0; +} + +static int read_image_iovl(AVFormatContext *s, const HEIFGrid *grid, + AVStreamGroupTileGrid *tile_grid) +{ + MOVContext *c = s->priv_data; + const HEIFItem *item = grid->item; + uint16_t canvas_fill_value[4]; + int64_t offset = 0, pos = avio_tell(s->pb); + int ret = 0, flags; + + if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) { + av_log(c->fc, AV_LOG_INFO, "iovl box with non seekable input\n"); + return AVERROR_PATCHWELCOME; + } + if (item->is_idat_relative) { + if (!c->idat_offset) { + av_log(c->fc, AV_LOG_ERROR, "missing idat box required by the image overlay\n"); + return AVERROR_INVALIDDATA; + } + offset = c->idat_offset; + } + + avio_seek(s->pb, item->extent_offset + offset, SEEK_SET); + + avio_r8(s->pb); /* version */ + flags = avio_r8(s->pb); + + for (int i = 0; i < 4; i++) + canvas_fill_value[i] = avio_rb16(s->pb); + av_log(c->fc, AV_LOG_TRACE, "iovl: canvas_fill_value { %u, %u, %u, %u }\n", + canvas_fill_value[0], canvas_fill_value[1], + canvas_fill_value[2], canvas_fill_value[3]); + for (int i = 0; i < 4; i++) + tile_grid->background[i] = canvas_fill_value[i]; + + /* actual width and height of output image */ + tile_grid->width = + tile_grid->coded_width = (flags & 1) ? avio_rb32(s->pb) : avio_rb16(s->pb); + tile_grid->height = + tile_grid->coded_height = (flags & 1) ? avio_rb32(s->pb) : avio_rb16(s->pb); + av_log(c->fc, AV_LOG_TRACE, "iovl: output_width %d, output_height %d\n", + tile_grid->width, tile_grid->height); + + tile_grid->nb_tiles = grid->nb_tiles; + tile_grid->offsets = av_malloc_array(tile_grid->nb_tiles, sizeof(*tile_grid->offsets)); + if (!tile_grid->offsets) { + ret = AVERROR(ENOMEM); + goto fail; + } + + for (int i = 0; i < tile_grid->nb_tiles; i++) { + tile_grid->offsets[i].idx = grid->tile_item_list[i]->st->index; + tile_grid->offsets[i].horizontal = (flags & 1) ? avio_rb32(s->pb) : avio_rb16(s->pb); + tile_grid->offsets[i].vertical = (flags & 1) ? avio_rb32(s->pb) : avio_rb16(s->pb); + av_log(c->fc, AV_LOG_TRACE, "iovl: stream_idx[%d] %u, " + "horizontal_offset[%d] %d, vertical_offset[%d] %d\n", + i, tile_grid->offsets[i].idx, + i, tile_grid->offsets[i].horizontal, i, tile_grid->offsets[i].vertical); + } + +fail: + avio_seek(s->pb, pos, SEEK_SET); + + return ret; +} + +static int mov_parse_tiles(AVFormatContext *s) +{ + MOVContext *mov = s->priv_data; + + for (int i = 0; i < mov->nb_heif_grid; i++) { + AVStreamGroup *stg = avformat_stream_group_create(s, AV_STREAM_GROUP_PARAMS_TILE_GRID, NULL); + AVStreamGroupTileGrid *tile_grid; + const HEIFGrid *grid = &mov->heif_grid[i]; + int err, loop = 1; + + if (!stg) + return AVERROR(ENOMEM); + + stg->id = grid->item->item_id; + tile_grid = stg->params.tile_grid; + + for (int j = 0; j < grid->nb_tiles; j++) { + int tile_id = grid->tile_id_list[j]; + int k; + + for (k = 0; k < mov->nb_heif_item; k++) { + HEIFItem *item = &mov->heif_item[k]; + AVStream *st = item->st; + + if (item->item_id != tile_id) + continue; + if (!st) { + av_log(s, AV_LOG_WARNING, "HEIF item id %d from grid id %d doesn't " + "reference a stream\n", + tile_id, grid->item->item_id); + ff_remove_stream_group(s, stg); + loop = 0; + break; + } + + grid->tile_item_list[j] = item; + + err = avformat_stream_group_add_stream(stg, st); + if (err < 0 && err != AVERROR(EEXIST)) + return err; + + if (item->item_id != mov->primary_item_id) + st->disposition |= AV_DISPOSITION_DEPENDENT; + break; + } + + if (k == mov->nb_heif_item) { + av_assert0(loop); + av_log(s, AV_LOG_WARNING, "HEIF item id %d referenced by grid id %d doesn't " + "exist\n", + tile_id, grid->item->item_id); + ff_remove_stream_group(s, stg); + loop = 0; + } + if (!loop) + break; + } + + if (!loop) + continue; + + switch (grid->item->type) { + case MKTAG('g','r','i','d'): + err = read_image_grid(s, grid, tile_grid); + break; + case MKTAG('i','o','v','l'): + err = read_image_iovl(s, grid, tile_grid); + break; + default: + av_assert0(0); + } + if (err < 0) + return err; + + + if (grid->item->name) + av_dict_set(&stg->metadata, "title", grid->item->name, 0); + if (grid->item->item_id == mov->primary_item_id) + stg->disposition |= AV_DISPOSITION_DEFAULT; + } + + return 0; +} + static int mov_read_header(AVFormatContext *s) { MOVContext *mov = s->priv_data; @@ -8454,6 +9552,9 @@ static int mov_read_header(AVFormatContext *s) mov->fc = s; mov->trak_index = -1; + mov->thmb_item_id = -1; + mov->primary_item_id = -1; + mov->cur_item_id = -1; /* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */ if (pb->seekable & AVIO_SEEKABLE_NORMAL) atom.size = avio_size(pb); @@ -8468,13 +9569,64 @@ static int mov_read_header(AVFormatContext *s) av_log(s, AV_LOG_ERROR, "error reading header\n"); return err; } - } while ((pb->seekable & AVIO_SEEKABLE_NORMAL) && !mov->found_moov && !mov->moov_retry++); - if (!mov->found_moov) { + } while ((pb->seekable & AVIO_SEEKABLE_NORMAL) && + !mov->found_moov && (!mov->found_iloc || !mov->found_iinf) && !mov->moov_retry++); + if (!mov->found_moov && !mov->found_iloc && !mov->found_iinf) { av_log(s, AV_LOG_ERROR, "moov atom not found\n"); return AVERROR_INVALIDDATA; } av_log(mov->fc, AV_LOG_TRACE, "on_parse_exit_offset=%"PRId64"\n", avio_tell(pb)); + if (mov->found_iloc && mov->found_iinf) { + for (i = 0; i < mov->nb_heif_item; i++) { + HEIFItem *item = &mov->heif_item[i]; + MOVStreamContext *sc; + AVStream *st; + int64_t offset = 0; + + if (!item->st) { + if (item->item_id == mov->thmb_item_id) { + av_log(s, AV_LOG_ERROR, "HEIF thumbnail doesn't reference a stream\n"); + return AVERROR_INVALIDDATA; + } + continue; + } + if (item->is_idat_relative) { + if (!mov->idat_offset) { + av_log(s, AV_LOG_ERROR, "Missing idat box for item %d\n", item->item_id); + return AVERROR_INVALIDDATA; + } + offset = mov->idat_offset; + } + + st = item->st; + sc = st->priv_data; + st->codecpar->width = item->width; + st->codecpar->height = item->height; + + if (sc->sample_count != 1 || sc->chunk_count != 1) + return AVERROR_INVALIDDATA; + + sc->sample_sizes[0] = item->extent_length; + sc->chunk_offsets[0] = item->extent_offset + offset; + + if (item->item_id == mov->primary_item_id) + st->disposition |= AV_DISPOSITION_DEFAULT; + + mov_build_index(mov, st); + } + + if (mov->nb_heif_grid) { + err = mov_parse_tiles(s); + if (err < 0) + return err; + } + } + // prevent iloc and iinf boxes from being parsed while reading packets. + // this is needed because an iinf box may have been parsed but ignored + // for having old infe boxes which create no streams. + mov->found_iloc = mov->found_iinf = 1; + if (pb->seekable & AVIO_SEEKABLE_NORMAL) { if (mov->nb_chapter_tracks > 0 && !mov->ignore_chapters) mov_read_chapters(s); @@ -8494,9 +9646,11 @@ static int mov_read_header(AVFormatContext *s) AVDictionaryEntry *tcr; int tmcd_st_id = -1; - for (j = 0; j < s->nb_streams; j++) - if (s->streams[j]->id == sc->timecode_track) + for (j = 0; j < s->nb_streams; j++) { + MOVStreamContext *sc2 = s->streams[j]->priv_data; + if (sc2->id == sc->timecode_track) tmcd_st_id = j; + } if (tmcd_st_id < 0 || tmcd_st_id == i) continue; @@ -8593,49 +9747,53 @@ static int mov_read_header(AVFormatContext *s) break; case AVMEDIA_TYPE_VIDEO: if (sc->display_matrix) { - err = av_stream_add_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, (uint8_t*)sc->display_matrix, - sizeof(int32_t) * 9); - if (err < 0) - return err; + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_DISPLAYMATRIX, + (uint8_t*)sc->display_matrix, sizeof(int32_t) * 9, 0)) + return AVERROR(ENOMEM); sc->display_matrix = NULL; } if (sc->stereo3d) { - err = av_stream_add_side_data(st, AV_PKT_DATA_STEREO3D, - (uint8_t *)sc->stereo3d, - sizeof(*sc->stereo3d)); - if (err < 0) - return err; + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_STEREO3D, + (uint8_t *)sc->stereo3d, sizeof(*sc->stereo3d), 0)) + return AVERROR(ENOMEM); sc->stereo3d = NULL; } if (sc->spherical) { - err = av_stream_add_side_data(st, AV_PKT_DATA_SPHERICAL, - (uint8_t *)sc->spherical, - sc->spherical_size); - if (err < 0) - return err; + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_SPHERICAL, + (uint8_t *)sc->spherical, sc->spherical_size, 0)) + return AVERROR(ENOMEM); sc->spherical = NULL; } if (sc->mastering) { - err = av_stream_add_side_data(st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, - (uint8_t *)sc->mastering, - sizeof(*sc->mastering)); - if (err < 0) - return err; + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_MASTERING_DISPLAY_METADATA, + (uint8_t *)sc->mastering, sizeof(*sc->mastering), 0)) + return AVERROR(ENOMEM); sc->mastering = NULL; } if (sc->coll) { - err = av_stream_add_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, - (uint8_t *)sc->coll, - sc->coll_size); - if (err < 0) - return err; + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_CONTENT_LIGHT_LEVEL, + (uint8_t *)sc->coll, sc->coll_size, 0)) + return AVERROR(ENOMEM); sc->coll = NULL; } + if (sc->ambient) { + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT, + (uint8_t *) sc->ambient, sc->ambient_size, 0)) + return AVERROR(ENOMEM); + + sc->ambient = NULL; + } break; } } @@ -8653,6 +9811,8 @@ static AVIndexEntry *mov_find_next_sample(AVFormatContext *s, AVStream **st) AVIndexEntry *sample = NULL; int64_t best_dts = INT64_MAX; int i; + MOVContext *mov = s->priv_data; + int no_interleave = !mov->interleaved_read || !(s->pb->seekable & AVIO_SEEKABLE_NORMAL); for (i = 0; i < s->nb_streams; i++) { AVStream *avst = s->streams[i]; FFStream *const avsti = ffstream(avst); @@ -8662,7 +9822,7 @@ static AVIndexEntry *mov_find_next_sample(AVFormatContext *s, AVStream **st) int64_t dts = av_rescale(current_sample->timestamp, AV_TIME_BASE, msc->time_scale); uint64_t dtsdiff = best_dts > dts ? best_dts - (uint64_t)dts : ((uint64_t)dts - best_dts); av_log(s, AV_LOG_TRACE, "stream %d, sample %d, dts %"PRId64"\n", i, msc->current_sample, dts); - if (!sample || (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL) && current_sample->pos < sample->pos) || + if (!sample || (no_interleave && current_sample->pos < sample->pos) || ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) && ((msc->pb != s->pb && dts < best_dts) || (msc->pb == s->pb && dts != AV_NOPTS_VALUE && ((dtsdiff <= AV_TIME_BASE && current_sample->pos < sample->pos) || @@ -8719,8 +9879,9 @@ static int mov_switch_root(AVFormatContext *s, int64_t target, int index) return 1; } -static int mov_change_extradata(MOVStreamContext *sc, AVPacket *pkt) +static int mov_change_extradata(AVStream *st, AVPacket *pkt) { + MOVStreamContext *sc = st->priv_data; uint8_t *side, *extradata; int extradata_size; @@ -8730,7 +9891,7 @@ static int mov_change_extradata(MOVStreamContext *sc, AVPacket *pkt) /* Notify the decoder that extradata changed. */ extradata_size = sc->extradata_size[sc->last_stsd_index]; extradata = sc->extradata[sc->last_stsd_index]; - if (extradata_size > 0 && extradata) { + if (st->discard != AVDISCARD_ALL && extradata_size > 0 && extradata) { side = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, extradata_size); @@ -8763,6 +9924,64 @@ static int get_eia608_packet(AVIOContext *pb, AVPacket *pkt, int size) return 0; } +static int mov_finalize_packet(AVFormatContext *s, AVStream *st, AVIndexEntry *sample, + int64_t current_index, AVPacket *pkt) +{ + MOVStreamContext *sc = st->priv_data; + + pkt->stream_index = sc->ffindex; + pkt->dts = sample->timestamp; + if (sample->flags & AVINDEX_DISCARD_FRAME) { + pkt->flags |= AV_PKT_FLAG_DISCARD; + } + if (sc->ctts_data && sc->ctts_index < sc->ctts_count) { + pkt->pts = av_sat_add64(pkt->dts, av_sat_add64(sc->dts_shift, sc->ctts_data[sc->ctts_index].duration)); + /* update ctts context */ + sc->ctts_sample++; + if (sc->ctts_index < sc->ctts_count && + sc->ctts_data[sc->ctts_index].count == sc->ctts_sample) { + sc->ctts_index++; + sc->ctts_sample = 0; + } + } else { + int64_t next_dts = (sc->current_sample < ffstream(st)->nb_index_entries) ? + ffstream(st)->index_entries[sc->current_sample].timestamp : st->duration; + + if (next_dts >= pkt->dts) + pkt->duration = next_dts - pkt->dts; + pkt->pts = pkt->dts; + } + + if (sc->sdtp_data && sc->current_sample <= sc->sdtp_count) { + uint8_t sample_flags = sc->sdtp_data[sc->current_sample - 1]; + uint8_t sample_is_depended_on = (sample_flags >> 2) & 0x3; + pkt->flags |= sample_is_depended_on == MOV_SAMPLE_DEPENDENCY_NO ? AV_PKT_FLAG_DISPOSABLE : 0; + } + pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0; + pkt->pos = sample->pos; + + /* Multiple stsd handling. */ + if (sc->stsc_data) { + if (sc->stsc_data[sc->stsc_index].id > 0 && + sc->stsc_data[sc->stsc_index].id - 1 < sc->stsd_count && + sc->stsc_data[sc->stsc_index].id - 1 != sc->last_stsd_index) { + int ret = mov_change_extradata(st, pkt); + if (ret < 0) + return ret; + } + + /* Update the stsc index for the next sample */ + sc->stsc_sample++; + if (mov_stsc_index_valid(sc->stsc_index, sc->stsc_count) && + mov_get_stsc_samples(sc, sc->stsc_index) == sc->stsc_sample) { + sc->stsc_index++; + sc->stsc_sample = 0; + } + } + + return 0; +} + static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) { MOVContext *mov = s->priv_data; @@ -8811,7 +10030,29 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) if (st->codecpar->codec_id == AV_CODEC_ID_EIA_608 && sample->size > 8) ret = get_eia608_packet(sc->pb, pkt, sample->size); - else + else if (sc->iamf) { + int64_t pts, dts, pos, duration; + int flags, size = sample->size; + ret = mov_finalize_packet(s, st, sample, current_index, pkt); + pts = pkt->pts; dts = pkt->dts; + pos = pkt->pos; flags = pkt->flags; + duration = pkt->duration; + while (!ret && size > 0) { + ret = ff_iamf_read_packet(s, sc->iamf, sc->pb, size, pkt); + if (ret < 0) { + if (should_retry(sc->pb, ret)) + mov_current_sample_dec(sc); + return ret; + } + size -= ret; + pkt->pts = pts; pkt->dts = dts; + pkt->pos = pos; pkt->flags |= flags; + pkt->duration = duration; + ret = ff_buffer_packet(s, pkt); + } + if (!ret) + return FFERROR_REDO; + } else ret = av_get_packet(sc->pb, pkt, sample->size); if (ret < 0) { if (should_retry(sc->pb, ret)) { @@ -8847,56 +10088,12 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt) } } - pkt->stream_index = sc->ffindex; - pkt->dts = sample->timestamp; - if (sample->flags & AVINDEX_DISCARD_FRAME) { - pkt->flags |= AV_PKT_FLAG_DISCARD; - } - if (sc->ctts_data && sc->ctts_index < sc->ctts_count) { - pkt->pts = pkt->dts + sc->dts_shift + sc->ctts_data[sc->ctts_index].duration; - /* update ctts context */ - sc->ctts_sample++; - if (sc->ctts_index < sc->ctts_count && - sc->ctts_data[sc->ctts_index].count == sc->ctts_sample) { - sc->ctts_index++; - sc->ctts_sample = 0; - } - } else { - int64_t next_dts = (sc->current_sample < ffstream(st)->nb_index_entries) ? - ffstream(st)->index_entries[sc->current_sample].timestamp : st->duration; + ret = mov_finalize_packet(s, st, sample, current_index, pkt); + if (ret < 0) + return ret; - if (next_dts >= pkt->dts) - pkt->duration = next_dts - pkt->dts; - pkt->pts = pkt->dts; - } if (st->discard == AVDISCARD_ALL) goto retry; - if (sc->sdtp_data && sc->current_sample <= sc->sdtp_count) { - uint8_t sample_flags = sc->sdtp_data[sc->current_sample - 1]; - uint8_t sample_is_depended_on = (sample_flags >> 2) & 0x3; - pkt->flags |= sample_is_depended_on == MOV_SAMPLE_DEPENDENCY_NO ? AV_PKT_FLAG_DISPOSABLE : 0; - } - pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0; - pkt->pos = sample->pos; - - /* Multiple stsd handling. */ - if (sc->stsc_data) { - if (sc->stsc_data[sc->stsc_index].id > 0 && - sc->stsc_data[sc->stsc_index].id - 1 < sc->stsd_count && - sc->stsc_data[sc->stsc_index].id - 1 != sc->last_stsd_index) { - ret = mov_change_extradata(sc, pkt); - if (ret < 0) - return ret; - } - - /* Update the stsc index for the next sample */ - sc->stsc_sample++; - if (mov_stsc_index_valid(sc->stsc_index, sc->stsc_count) && - mov_get_stsc_samples(sc, sc->stsc_index) == sc->stsc_sample) { - sc->stsc_index++; - sc->stsc_sample = 0; - } - } if (mov->aax_mode) aax_filter(pkt->data, pkt->size, mov); @@ -9129,13 +10326,13 @@ static const AVOption mov_options[] = { "use mfra for fragment timestamps", OFFSET(use_mfra_for), AV_OPT_TYPE_INT, {.i64 = FF_MOV_FLAG_MFRA_AUTO}, -1, FF_MOV_FLAG_MFRA_PTS, FLAGS, - "use_mfra_for"}, + .unit = "use_mfra_for"}, {"auto", "auto", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_MFRA_AUTO}, 0, 0, - FLAGS, "use_mfra_for" }, + FLAGS, .unit = "use_mfra_for" }, {"dts", "dts", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_MFRA_DTS}, 0, 0, - FLAGS, "use_mfra_for" }, + FLAGS, .unit = "use_mfra_for" }, {"pts", "pts", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_MFRA_PTS}, 0, 0, - FLAGS, "use_mfra_for" }, + FLAGS, .unit = "use_mfra_for" }, {"use_tfdt", "use tfdt for fragment timestamps", OFFSET(use_tfdt), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, FLAGS}, { "export_all", "Export unrecognized metadata entries", OFFSET(export_all), @@ -9156,6 +10353,7 @@ static const AVOption mov_options[] = { { "enable_drefs", "Enable external track support.", OFFSET(enable_drefs), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS }, { "max_stts_delta", "treat offsets above this value as invalid", OFFSET(max_stts_delta), AV_OPT_TYPE_INT, {.i64 = UINT_MAX-48000*10 }, 0, UINT_MAX, .flags = AV_OPT_FLAG_DECODING_PARAM }, + { "interleaved_read", "Interleave packets from multiple tracks at demuxer level", OFFSET(interleaved_read), AV_OPT_TYPE_BOOL, {.i64 = 1 }, 0, 1, .flags = AV_OPT_FLAG_DECODING_PARAM }, { NULL }, }; @@ -9167,17 +10365,17 @@ static const AVClass mov_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_mov_demuxer = { - .name = "mov,mp4,m4a,3gp,3g2,mj2", - .long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"), - .priv_class = &mov_class, +const FFInputFormat ff_mov_demuxer = { + .p.name = "mov,mp4,m4a,3gp,3g2,mj2", + .p.long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"), + .p.priv_class = &mov_class, + .p.extensions = "mov,mp4,m4a,3gp,3g2,mj2,psp,m4b,ism,ismv,isma,f4v,avif,heic,heif", + .p.flags = AVFMT_NO_BYTE_SEEK | AVFMT_SEEK_TO_PTS | AVFMT_SHOW_IDS, .priv_data_size = sizeof(MOVContext), - .extensions = "mov,mp4,m4a,3gp,3g2,mj2,psp,m4b,ism,ismv,isma,f4v,avif", - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = mov_probe, .read_header = mov_read_header, .read_packet = mov_read_packet, .read_close = mov_read_close, .read_seek = mov_read_seek, - .flags = AVFMT_NO_BYTE_SEEK | AVFMT_SEEK_TO_PTS | AVFMT_SHOW_IDS, }; diff --git a/libavformat/mov_chan.c b/libavformat/mov_chan.c index f66bf0df7fd..287059d65b6 100644 --- a/libavformat/mov_chan.c +++ b/libavformat/mov_chan.c @@ -25,228 +25,163 @@ #include +#include "libavutil/avassert.h" #include "libavutil/channel_layout.h" #include "libavcodec/codec_id.h" #include "mov_chan.h" -struct MovChannelLayoutMap { - uint32_t tag; - uint64_t layout; -}; - -static const struct MovChannelLayoutMap mov_ch_layout_map_misc[] = { - { MOV_CH_LAYOUT_USE_DESCRIPTIONS, 0 }, - { MOV_CH_LAYOUT_USE_BITMAP, 0 }, - { MOV_CH_LAYOUT_DISCRETEINORDER, 0 }, - { MOV_CH_LAYOUT_UNKNOWN, 0 }, - { MOV_CH_LAYOUT_TMH_10_2_STD, 0 }, // L, R, C, Vhc, Lsd, Rsd, - // Ls, Rs, Vhl, Vhr, Lw, Rw, - // Csd, Cs, LFE1, LFE2 - { MOV_CH_LAYOUT_TMH_10_2_FULL, 0 }, // L, R, C, Vhc, Lsd, Rsd, - // Ls, Rs, Vhl, Vhr, Lw, Rw, - // Csd, Cs, LFE1, LFE2, Lc, Rc, - // HI, VI, Haptic - { 0, 0 }, -}; - -static const struct MovChannelLayoutMap mov_ch_layout_map_1ch[] = { - { MOV_CH_LAYOUT_MONO, AV_CH_LAYOUT_MONO }, // C - { 0, 0 }, -}; - -static const struct MovChannelLayoutMap mov_ch_layout_map_2ch[] = { - { MOV_CH_LAYOUT_STEREO, AV_CH_LAYOUT_STEREO }, // L, R - { MOV_CH_LAYOUT_STEREOHEADPHONES, AV_CH_LAYOUT_STEREO }, // L, R - { MOV_CH_LAYOUT_BINAURAL, AV_CH_LAYOUT_STEREO }, // L, R - { MOV_CH_LAYOUT_MIDSIDE, AV_CH_LAYOUT_STEREO }, // C, sides - { MOV_CH_LAYOUT_XY, AV_CH_LAYOUT_STEREO }, // X (left), Y (right) - - { MOV_CH_LAYOUT_MATRIXSTEREO, AV_CH_LAYOUT_STEREO_DOWNMIX }, // Lt, Rt - - { MOV_CH_LAYOUT_AC3_1_0_1, AV_CH_LAYOUT_MONO | // C, LFE - AV_CH_LOW_FREQUENCY }, - { 0, 0 }, -}; - -static const struct MovChannelLayoutMap mov_ch_layout_map_3ch[] = { - { MOV_CH_LAYOUT_MPEG_3_0_A, AV_CH_LAYOUT_SURROUND }, // L, R, C - { MOV_CH_LAYOUT_MPEG_3_0_B, AV_CH_LAYOUT_SURROUND }, // C, L, R - { MOV_CH_LAYOUT_AC3_3_0, AV_CH_LAYOUT_SURROUND }, // L, C, R - - { MOV_CH_LAYOUT_ITU_2_1, AV_CH_LAYOUT_2_1 }, // L, R, Cs - - { MOV_CH_LAYOUT_DVD_4, AV_CH_LAYOUT_2POINT1 }, // L, R, LFE - { 0, 0 }, -}; - -static const struct MovChannelLayoutMap mov_ch_layout_map_4ch[] = { - { MOV_CH_LAYOUT_AMBISONIC_B_FORMAT, 0 }, // W, X, Y, Z - - { MOV_CH_LAYOUT_QUADRAPHONIC, AV_CH_LAYOUT_QUAD }, // L, R, Rls, Rrs - - { MOV_CH_LAYOUT_MPEG_4_0_A, AV_CH_LAYOUT_4POINT0 }, // L, R, C, Cs - { MOV_CH_LAYOUT_MPEG_4_0_B, AV_CH_LAYOUT_4POINT0 }, // C, L, R, Cs - { MOV_CH_LAYOUT_AC3_3_1, AV_CH_LAYOUT_4POINT0 }, // L, C, R, Cs - - { MOV_CH_LAYOUT_ITU_2_2, AV_CH_LAYOUT_2_2 }, // L, R, Ls, Rs - - { MOV_CH_LAYOUT_DVD_5, AV_CH_LAYOUT_2_1 | // L, R, LFE, Cs - AV_CH_LOW_FREQUENCY }, - { MOV_CH_LAYOUT_AC3_2_1_1, AV_CH_LAYOUT_2_1 | // L, R, Cs, LFE - AV_CH_LOW_FREQUENCY }, - - { MOV_CH_LAYOUT_DVD_10, AV_CH_LAYOUT_3POINT1 }, // L, R, C, LFE - { MOV_CH_LAYOUT_AC3_3_0_1, AV_CH_LAYOUT_3POINT1 }, // L, C, R, LFE - { MOV_CH_LAYOUT_DTS_3_1, AV_CH_LAYOUT_3POINT1 }, // C, L, R, LFE - { 0, 0 }, -}; - -static const struct MovChannelLayoutMap mov_ch_layout_map_5ch[] = { - { MOV_CH_LAYOUT_PENTAGONAL, AV_CH_LAYOUT_5POINT0_BACK }, // L, R, Rls, Rrs, C - - { MOV_CH_LAYOUT_MPEG_5_0_A, AV_CH_LAYOUT_5POINT0 }, // L, R, C, Ls, Rs - { MOV_CH_LAYOUT_MPEG_5_0_B, AV_CH_LAYOUT_5POINT0 }, // L, R, Ls, Rs, C - { MOV_CH_LAYOUT_MPEG_5_0_C, AV_CH_LAYOUT_5POINT0 }, // L, C, R, Ls, Rs - { MOV_CH_LAYOUT_MPEG_5_0_D, AV_CH_LAYOUT_5POINT0 }, // C, L, R, Ls, Rs - - { MOV_CH_LAYOUT_DVD_6, AV_CH_LAYOUT_2_2 | // L, R, LFE, Ls, Rs - AV_CH_LOW_FREQUENCY }, - { MOV_CH_LAYOUT_DVD_18, AV_CH_LAYOUT_2_2 | // L, R, Ls, Rs, LFE - AV_CH_LOW_FREQUENCY }, - - { MOV_CH_LAYOUT_DVD_11, AV_CH_LAYOUT_4POINT1 }, // L, R, C, LFE, Cs - { MOV_CH_LAYOUT_AC3_3_1_1, AV_CH_LAYOUT_4POINT1 }, // L, C, R, Cs, LFE - { MOV_CH_LAYOUT_DTS_4_1, AV_CH_LAYOUT_4POINT1 }, // C, L, R, Cs, LFE - { 0, 0 }, -}; - -static const struct MovChannelLayoutMap mov_ch_layout_map_6ch[] = { - { MOV_CH_LAYOUT_HEXAGONAL, AV_CH_LAYOUT_HEXAGONAL }, // L, R, Rls, Rrs, C, Cs - { MOV_CH_LAYOUT_DTS_6_0_C, AV_CH_LAYOUT_HEXAGONAL }, // C, Cs, L, R, Rls, Rrs - - { MOV_CH_LAYOUT_MPEG_5_1_A, AV_CH_LAYOUT_5POINT1 }, // L, R, C, LFE, Ls, Rs - { MOV_CH_LAYOUT_MPEG_5_1_B, AV_CH_LAYOUT_5POINT1 }, // L, R, Ls, Rs, C, LFE - { MOV_CH_LAYOUT_MPEG_5_1_C, AV_CH_LAYOUT_5POINT1 }, // L, C, R, Ls, Rs, LFE - { MOV_CH_LAYOUT_MPEG_5_1_D, AV_CH_LAYOUT_5POINT1 }, // C, L, R, Ls, Rs, LFE - - { MOV_CH_LAYOUT_AUDIOUNIT_6_0, AV_CH_LAYOUT_6POINT0 }, // L, R, Ls, Rs, C, Cs - { MOV_CH_LAYOUT_AAC_6_0, AV_CH_LAYOUT_6POINT0 }, // C, L, R, Ls, Rs, Cs - { MOV_CH_LAYOUT_EAC3_6_0_A, AV_CH_LAYOUT_6POINT0 }, // L, C, R, Ls, Rs, Cs - - { MOV_CH_LAYOUT_DTS_6_0_A, AV_CH_LAYOUT_6POINT0_FRONT }, // Lc, Rc, L, R, Ls, Rs - - { MOV_CH_LAYOUT_DTS_6_0_B, AV_CH_LAYOUT_5POINT0_BACK | // C, L, R, Rls, Rrs, Ts - AV_CH_TOP_CENTER }, - { 0, 0 }, -}; - -static const struct MovChannelLayoutMap mov_ch_layout_map_7ch[] = { - { MOV_CH_LAYOUT_MPEG_6_1_A, AV_CH_LAYOUT_6POINT1 }, // L, R, C, LFE, Ls, Rs, Cs - { MOV_CH_LAYOUT_AAC_6_1, AV_CH_LAYOUT_6POINT1 }, // C, L, R, Ls, Rs, Cs, LFE - { MOV_CH_LAYOUT_EAC3_6_1_A, AV_CH_LAYOUT_6POINT1 }, // L, C, R, Ls, Rs, LFE, Cs - { MOV_CH_LAYOUT_DTS_6_1_D, AV_CH_LAYOUT_6POINT1 }, // C, L, R, Ls, Rs, LFE, Cs - - { MOV_CH_LAYOUT_AUDIOUNIT_7_0, AV_CH_LAYOUT_7POINT0 }, // L, R, Ls, Rs, C, Rls, Rrs - { MOV_CH_LAYOUT_AAC_7_0, AV_CH_LAYOUT_7POINT0 }, // C, L, R, Ls, Rs, Rls, Rrs - { MOV_CH_LAYOUT_EAC3_7_0_A, AV_CH_LAYOUT_7POINT0 }, // L, C, R, Ls, Rs, Rls, Rrs - - { MOV_CH_LAYOUT_AUDIOUNIT_7_0_FRONT, AV_CH_LAYOUT_7POINT0_FRONT }, // L, R, Ls, Rs, C, Lc, Rc - { MOV_CH_LAYOUT_DTS_7_0, AV_CH_LAYOUT_7POINT0_FRONT }, // Lc, C, Rc, L, R, Ls, Rs - - { MOV_CH_LAYOUT_EAC3_6_1_B, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Ts - AV_CH_TOP_CENTER }, - - { MOV_CH_LAYOUT_EAC3_6_1_C, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Vhc - AV_CH_TOP_FRONT_CENTER }, - - { MOV_CH_LAYOUT_DTS_6_1_A, AV_CH_LAYOUT_6POINT1_FRONT }, // Lc, Rc, L, R, Ls, Rs, LFE - - { MOV_CH_LAYOUT_DTS_6_1_B, AV_CH_LAYOUT_5POINT1_BACK | // C, L, R, Rls, Rrs, Ts, LFE - AV_CH_TOP_CENTER }, - - { MOV_CH_LAYOUT_DTS_6_1_C, AV_CH_LAYOUT_6POINT1_BACK }, // C, Cs, L, R, Rls, Rrs, LFE - { 0, 0 }, -}; - -static const struct MovChannelLayoutMap mov_ch_layout_map_8ch[] = { - { MOV_CH_LAYOUT_OCTAGONAL, AV_CH_LAYOUT_OCTAGONAL }, // L, R, Rls, Rrs, C, Cs, Ls, Rs - { MOV_CH_LAYOUT_AAC_OCTAGONAL, AV_CH_LAYOUT_OCTAGONAL }, // C, L, R, Ls, Rs, Rls, Rrs, Cs - - { MOV_CH_LAYOUT_CUBE, AV_CH_LAYOUT_CUBE }, // L, R, Rls, Rrs, Vhl, Vhr, Rlt, Rrt - - { MOV_CH_LAYOUT_MPEG_7_1_A, AV_CH_LAYOUT_7POINT1_WIDE }, // L, R, C, LFE, Ls, Rs, Lc, Rc - { MOV_CH_LAYOUT_MPEG_7_1_B, AV_CH_LAYOUT_7POINT1_WIDE }, // C, Lc, Rc, L, R, Ls, Rs, LFE - { MOV_CH_LAYOUT_EMAGIC_DEFAULT_7_1, AV_CH_LAYOUT_7POINT1_WIDE }, // L, R, Ls, Rs, C, LFE, Lc, Rc - { MOV_CH_LAYOUT_EAC3_7_1_B, AV_CH_LAYOUT_7POINT1_WIDE }, // L, C, R, Ls, Rs, LFE, Lc, Rc - { MOV_CH_LAYOUT_DTS_7_1, AV_CH_LAYOUT_7POINT1_WIDE }, // Lc, C, Rc, L, R, Ls, Rs, LFE - - { MOV_CH_LAYOUT_MPEG_7_1_C, AV_CH_LAYOUT_7POINT1 }, // L, R, C, LFE, Ls, Rs, Rls, Rrs - { MOV_CH_LAYOUT_EAC3_7_1_A, AV_CH_LAYOUT_7POINT1 }, // L, C, R, Ls, Rs, LFE, Rls, Rrs - - { MOV_CH_LAYOUT_SMPTE_DTV, AV_CH_LAYOUT_5POINT1 | // L, R, C, LFE, Ls, Rs, Lt, Rt - AV_CH_LAYOUT_STEREO_DOWNMIX }, - - { MOV_CH_LAYOUT_EAC3_7_1_C, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Lsd, Rsd - AV_CH_SURROUND_DIRECT_LEFT | - AV_CH_SURROUND_DIRECT_RIGHT }, - - { MOV_CH_LAYOUT_EAC3_7_1_D, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Lw, Rw - AV_CH_WIDE_LEFT | - AV_CH_WIDE_RIGHT }, - - { MOV_CH_LAYOUT_EAC3_7_1_E, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Vhl, Vhr - AV_CH_TOP_FRONT_LEFT | - AV_CH_TOP_FRONT_RIGHT }, - - { MOV_CH_LAYOUT_EAC3_7_1_F, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Cs, Ts - AV_CH_BACK_CENTER | - AV_CH_TOP_CENTER }, - - { MOV_CH_LAYOUT_EAC3_7_1_G, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Cs, Vhc - AV_CH_BACK_CENTER | - AV_CH_TOP_FRONT_CENTER }, - - { MOV_CH_LAYOUT_EAC3_7_1_H, AV_CH_LAYOUT_5POINT1 | // L, C, R, Ls, Rs, LFE, Ts, Vhc - AV_CH_TOP_CENTER | - AV_CH_TOP_FRONT_CENTER }, - - { MOV_CH_LAYOUT_DTS_8_0_A, AV_CH_LAYOUT_2_2 | // Lc, Rc, L, R, Ls, Rs, Rls, Rrs - AV_CH_BACK_LEFT | - AV_CH_BACK_RIGHT | - AV_CH_FRONT_LEFT_OF_CENTER | - AV_CH_FRONT_RIGHT_OF_CENTER }, - - { MOV_CH_LAYOUT_DTS_8_0_B, AV_CH_LAYOUT_5POINT0 | // Lc, C, Rc, L, R, Ls, Cs, Rs - AV_CH_FRONT_LEFT_OF_CENTER | - AV_CH_FRONT_RIGHT_OF_CENTER | - AV_CH_BACK_CENTER }, - { 0, 0 }, +enum { + c_L = AV_CHAN_FRONT_LEFT, + c_R = AV_CHAN_FRONT_RIGHT, + c_C = AV_CHAN_FRONT_CENTER, + c_LFE = AV_CHAN_LOW_FREQUENCY, + c_Rls = AV_CHAN_BACK_LEFT, + c_Rrs = AV_CHAN_BACK_RIGHT, + c_Lc = AV_CHAN_FRONT_LEFT_OF_CENTER, + c_Rc = AV_CHAN_FRONT_RIGHT_OF_CENTER, + c_Cs = AV_CHAN_BACK_CENTER, + c_Ls = AV_CHAN_SIDE_LEFT, + c_Rs = AV_CHAN_SIDE_RIGHT, + c_Ts = AV_CHAN_TOP_CENTER, + c_Vhl = AV_CHAN_TOP_FRONT_LEFT, + c_Vhc = AV_CHAN_TOP_FRONT_CENTER, + c_Vhr = AV_CHAN_TOP_FRONT_RIGHT, + c_Rlt = AV_CHAN_TOP_BACK_LEFT, + // = AV_CHAN_TOP_BACK_CENTER, + c_Rrt = AV_CHAN_TOP_BACK_RIGHT, + c_Lt = AV_CHAN_STEREO_LEFT, + c_Rt = AV_CHAN_STEREO_RIGHT, + c_Lw = AV_CHAN_WIDE_LEFT, + c_Rw = AV_CHAN_WIDE_RIGHT, + c_Lsd = AV_CHAN_SURROUND_DIRECT_LEFT, + c_Rsd = AV_CHAN_SURROUND_DIRECT_RIGHT, + c_LFE2 = AV_CHAN_LOW_FREQUENCY_2, + // = AV_CHAN_TOP_SIDE_LEFT, + // = AV_CHAN_TOP_SIDE_RIGHT, + // = AV_CHAN_BOTTOM_FRONT_CENTER, + // = AV_CHAN_BOTTOM_FRONT_LEFT, + // = AV_CHAN_BOTTOM_FRONT_RIGHT, + c_W = AV_CHAN_AMBISONIC_BASE, + c_Y = AV_CHAN_AMBISONIC_BASE + 1, + c_Z = AV_CHAN_AMBISONIC_BASE + 2, + c_X = AV_CHAN_AMBISONIC_BASE + 3, + /* The following have no exact counterparts */ + c_LFE1 = AV_CHAN_LOW_FREQUENCY, + c_Csd = AV_CHAN_NONE, + c_HI = AV_CHAN_NONE, + c_VI = AV_CHAN_NONE, + c_Haptic = AV_CHAN_NONE, }; -static const struct MovChannelLayoutMap mov_ch_layout_map_9ch[] = { - { MOV_CH_LAYOUT_DTS_8_1_A, AV_CH_LAYOUT_2_2 | // Lc, Rc, L, R, Ls, Rs, Rls, Rrs, LFE - AV_CH_BACK_LEFT | - AV_CH_BACK_RIGHT | - AV_CH_FRONT_LEFT_OF_CENTER | - AV_CH_FRONT_RIGHT_OF_CENTER | - AV_CH_LOW_FREQUENCY }, - - { MOV_CH_LAYOUT_DTS_8_1_B, AV_CH_LAYOUT_7POINT1_WIDE | // Lc, C, Rc, L, R, Ls, Cs, Rs, LFE - AV_CH_BACK_CENTER }, - { 0, 0 }, +struct MovChannelLayoutMap { + union { + uint32_t tag; + enum AVChannel id; + }; }; -static const struct MovChannelLayoutMap * const mov_ch_layout_map[] = { - mov_ch_layout_map_misc, - mov_ch_layout_map_1ch, - mov_ch_layout_map_2ch, - mov_ch_layout_map_3ch, - mov_ch_layout_map_4ch, - mov_ch_layout_map_5ch, - mov_ch_layout_map_6ch, - mov_ch_layout_map_7ch, - mov_ch_layout_map_8ch, - mov_ch_layout_map_9ch, +#define TAG(_0) {.tag = _0} +#define ID(_0) {.id = c_##_0} +#define CHLIST(_0, ...) TAG(_0), __VA_ARGS__ +#define CHLIST01(_0, _1) CHLIST(_0, ID(_1)) +#define CHLIST02(_0, _1, _2) CHLIST(_0, ID(_1), ID(_2)) +#define CHLIST03(_0, _1, _2, _3) CHLIST(_0, ID(_1), ID(_2), ID(_3)) +#define CHLIST04(_0, _1, _2, _3, _4) CHLIST(_0, ID(_1), ID(_2), ID(_3), ID(_4)) +#define CHLIST05(_0, _1, _2, _3, _4, _5) CHLIST(_0, ID(_1), ID(_2), ID(_3), ID(_4), ID(_5)) +#define CHLIST06(_0, _1, _2, _3, _4, _5, _6) CHLIST(_0, ID(_1), ID(_2), ID(_3), ID(_4), ID(_5), ID(_6)) +#define CHLIST07(_0, _1, _2, _3, _4, _5, _6, _7) CHLIST(_0, ID(_1), ID(_2), ID(_3), ID(_4), ID(_5), ID(_6), ID(_7)) +#define CHLIST08(_0, _1, _2, _3, _4, _5, _6, _7, _8) CHLIST(_0, ID(_1), ID(_2), ID(_3), ID(_4), ID(_5), ID(_6), ID(_7), ID(_8)) +#define CHLIST09(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) CHLIST(_0, ID(_1), ID(_2), ID(_3), ID(_4), ID(_5), ID(_6), ID(_7), ID(_8), ID(_9)) +#define CHLIST16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16) \ + CHLIST(_0, ID(_1), ID(_2), ID(_3), ID(_4), ID(_5), ID(_6), ID(_7), ID(_8), ID(_9), ID(_10), \ + ID(_11), ID(_12), ID(_13), ID(_14), ID(_15), ID(_16)) +#define CHLIST21(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21) \ + CHLIST(_0, ID(_1), ID(_2), ID(_3), ID(_4), ID(_5), ID(_6), ID(_7), ID(_8), ID(_9), ID(_10), \ + ID(_11), ID(_12), ID(_13), ID(_14), ID(_15), ID(_16), ID(_17), ID(_18), ID(_19), ID(_20), ID(_21)) + +static const struct MovChannelLayoutMap mov_ch_layout_map[] = { + CHLIST01( MOV_CH_LAYOUT_MONO, C ), + CHLIST02( MOV_CH_LAYOUT_STEREO, L, R ), + CHLIST02( MOV_CH_LAYOUT_STEREOHEADPHONES, L, R ), + CHLIST02( MOV_CH_LAYOUT_BINAURAL, L, R ), + CHLIST02( MOV_CH_LAYOUT_MIDSIDE, L, R ), //C, sides + CHLIST02( MOV_CH_LAYOUT_XY, L, R ), //X (left ), Y (right ) + CHLIST02( MOV_CH_LAYOUT_MATRIXSTEREO, Lt, Rt ), + CHLIST02( MOV_CH_LAYOUT_AC3_1_0_1, C, LFE ), + CHLIST03( MOV_CH_LAYOUT_MPEG_3_0_A, L, R, C ), + CHLIST03( MOV_CH_LAYOUT_MPEG_3_0_B, C, L, R ), + CHLIST03( MOV_CH_LAYOUT_AC3_3_0, L, C, R ), + CHLIST03( MOV_CH_LAYOUT_ITU_2_1, L, R, Cs ), + CHLIST03( MOV_CH_LAYOUT_DVD_4, L, R, LFE ), + CHLIST04( MOV_CH_LAYOUT_AMBISONIC_B_FORMAT, W, X, Y, Z ), + CHLIST04( MOV_CH_LAYOUT_QUADRAPHONIC, L, R, Rls, Rrs ), + CHLIST04( MOV_CH_LAYOUT_MPEG_4_0_A, L, R, C, Cs ), + CHLIST04( MOV_CH_LAYOUT_MPEG_4_0_B, C, L, R, Cs ), + CHLIST04( MOV_CH_LAYOUT_AC3_3_1, L, C, R, Cs ), + CHLIST04( MOV_CH_LAYOUT_ITU_2_2, L, R, Ls, Rs ), + CHLIST04( MOV_CH_LAYOUT_DVD_5, L, R, LFE, Cs ), + CHLIST04( MOV_CH_LAYOUT_AC3_2_1_1, L, R, Cs, LFE ), + CHLIST04( MOV_CH_LAYOUT_DVD_10, L, R, C, LFE ), + CHLIST04( MOV_CH_LAYOUT_AC3_3_0_1, L, C, R, LFE ), + CHLIST04( MOV_CH_LAYOUT_DTS_3_1, C, L, R, LFE ), + CHLIST05( MOV_CH_LAYOUT_PENTAGONAL, L, R, Rls, Rrs, C ), + CHLIST05( MOV_CH_LAYOUT_MPEG_5_0_A, L, R, C, Ls, Rs ), + CHLIST05( MOV_CH_LAYOUT_MPEG_5_0_B, L, R, Ls, Rs, C ), + CHLIST05( MOV_CH_LAYOUT_MPEG_5_0_C, L, C, R, Ls, Rs ), + CHLIST05( MOV_CH_LAYOUT_MPEG_5_0_D, C, L, R, Ls, Rs ), + CHLIST05( MOV_CH_LAYOUT_DVD_6, L, R, LFE, Ls, Rs ), + CHLIST05( MOV_CH_LAYOUT_DVD_18, L, R, Ls, Rs, LFE ), + CHLIST05( MOV_CH_LAYOUT_DVD_11, L, R, C, LFE, Cs ), + CHLIST05( MOV_CH_LAYOUT_AC3_3_1_1, L, C, R, Cs, LFE ), + CHLIST05( MOV_CH_LAYOUT_DTS_4_1, C, L, R, Cs, LFE ), + CHLIST06( MOV_CH_LAYOUT_HEXAGONAL, L, R, Rls, Rrs, C, Cs ), + CHLIST06( MOV_CH_LAYOUT_DTS_6_0_C, C, Cs, L, R, Rls, Rrs ), + CHLIST06( MOV_CH_LAYOUT_MPEG_5_1_A, L, R, C, LFE, Ls, Rs ), + CHLIST06( MOV_CH_LAYOUT_MPEG_5_1_B, L, R, Ls, Rs, C, LFE ), + CHLIST06( MOV_CH_LAYOUT_MPEG_5_1_C, L, C, R, Ls, Rs, LFE ), + CHLIST06( MOV_CH_LAYOUT_MPEG_5_1_D, C, L, R, Ls, Rs, LFE ), + CHLIST06( MOV_CH_LAYOUT_AUDIOUNIT_6_0, L, R, Ls, Rs, C, Cs ), + CHLIST06( MOV_CH_LAYOUT_AAC_6_0, C, L, R, Ls, Rs, Cs ), + CHLIST06( MOV_CH_LAYOUT_EAC3_6_0_A, L, C, R, Ls, Rs, Cs ), + CHLIST06( MOV_CH_LAYOUT_DTS_6_0_A, Lc, Rc, L, R, Ls, Rs ), + CHLIST06( MOV_CH_LAYOUT_DTS_6_0_B, C, L, R, Rls, Rrs, Ts ), + CHLIST07( MOV_CH_LAYOUT_MPEG_6_1_A, L, R, C, LFE, Ls, Rs, Cs ), + CHLIST07( MOV_CH_LAYOUT_AAC_6_1, C, L, R, Ls, Rs, Cs, LFE ), + CHLIST07( MOV_CH_LAYOUT_EAC3_6_1_A, L, C, R, Ls, Rs, LFE, Cs ), + CHLIST07( MOV_CH_LAYOUT_DTS_6_1_D, C, L, R, Ls, Rs, LFE, Cs ), + CHLIST07( MOV_CH_LAYOUT_AUDIOUNIT_7_0, L, R, Ls, Rs, C, Rls, Rrs ), + CHLIST07( MOV_CH_LAYOUT_AAC_7_0, C, L, R, Ls, Rs, Rls, Rrs ), + CHLIST07( MOV_CH_LAYOUT_EAC3_7_0_A, L, C, R, Ls, Rs, Rls, Rrs ), + CHLIST07( MOV_CH_LAYOUT_AUDIOUNIT_7_0_FRONT, L, R, Ls, Rs, C, Lc, Rc ), + CHLIST07( MOV_CH_LAYOUT_DTS_7_0, Lc, C, Rc, L, R, Ls, Rs ), + CHLIST07( MOV_CH_LAYOUT_EAC3_6_1_B, L, C, R, Ls, Rs, LFE, Ts ), + CHLIST07( MOV_CH_LAYOUT_EAC3_6_1_C, L, C, R, Ls, Rs, LFE, Vhc ), + CHLIST07( MOV_CH_LAYOUT_DTS_6_1_A, Lc, Rc, L, R, Ls, Rs, LFE ), + CHLIST07( MOV_CH_LAYOUT_DTS_6_1_B, C, L, R, Rls, Rrs, Ts, LFE ), + CHLIST07( MOV_CH_LAYOUT_DTS_6_1_C, C, Cs, L, R, Rls, Rrs, LFE ), + CHLIST08( MOV_CH_LAYOUT_OCTAGONAL, L, R, Rls, Rrs, C, Cs, Ls, Rs ), + CHLIST08( MOV_CH_LAYOUT_AAC_OCTAGONAL, C, L, R, Ls, Rs, Rls, Rrs, Cs ), + CHLIST08( MOV_CH_LAYOUT_CUBE, L, R, Rls, Rrs, Vhl, Vhr, Rlt, Rrt ), + CHLIST08( MOV_CH_LAYOUT_MPEG_7_1_A, L, R, C, LFE, Ls, Rs, Lc, Rc ), + CHLIST08( MOV_CH_LAYOUT_MPEG_7_1_B, C, Lc, Rc, L, R, Ls, Rs, LFE ), + CHLIST08( MOV_CH_LAYOUT_EMAGIC_DEFAULT_7_1, L, R, Ls, Rs, C, LFE, Lc, Rc ), + CHLIST08( MOV_CH_LAYOUT_EAC3_7_1_B, L, C, R, Ls, Rs, LFE, Lc, Rc ), + CHLIST08( MOV_CH_LAYOUT_DTS_7_1, Lc, C, Rc, L, R, Ls, Rs, LFE ), + CHLIST08( MOV_CH_LAYOUT_MPEG_7_1_C, L, R, C, LFE, Ls, Rs, Rls, Rrs ), + CHLIST08( MOV_CH_LAYOUT_EAC3_7_1_A, L, C, R, Ls, Rs, LFE, Rls, Rrs ), + CHLIST08( MOV_CH_LAYOUT_SMPTE_DTV, L, R, C, LFE, Ls, Rs, Lt, Rt ), + CHLIST08( MOV_CH_LAYOUT_EAC3_7_1_C, L, C, R, Ls, Rs, LFE, Lsd, Rsd ), + CHLIST08( MOV_CH_LAYOUT_EAC3_7_1_D, L, C, R, Ls, Rs, LFE, Lw, Rw ), + CHLIST08( MOV_CH_LAYOUT_EAC3_7_1_E, L, C, R, Ls, Rs, LFE, Vhl, Vhr ), + CHLIST08( MOV_CH_LAYOUT_EAC3_7_1_F, L, C, R, Ls, Rs, LFE, Cs, Ts ), + CHLIST08( MOV_CH_LAYOUT_EAC3_7_1_G, L, C, R, Ls, Rs, LFE, Cs, Vhc ), + CHLIST08( MOV_CH_LAYOUT_EAC3_7_1_H, L, C, R, Ls, Rs, LFE, Ts, Vhc ), + CHLIST08( MOV_CH_LAYOUT_DTS_8_0_A, Lc, Rc, L, R, Ls, Rs, Rls, Rrs ), + CHLIST08( MOV_CH_LAYOUT_DTS_8_0_B, Lc, C, Rc, L, R, Ls, Cs, Rs ), + CHLIST09( MOV_CH_LAYOUT_DTS_8_1_A, Lc, Rc, L, R, Ls, Rs, Rls, Rrs, LFE ), + CHLIST09( MOV_CH_LAYOUT_DTS_8_1_B, Lc, C, Rc, L, R, Ls, Cs, Rs, LFE ), + CHLIST16( MOV_CH_LAYOUT_TMH_10_2_STD, L, R, C, Vhc, Lsd, Rsd, Ls, Rs, Vhl, Vhr, Lw, Rw, Csd, Cs, LFE1, LFE2), + CHLIST21( MOV_CH_LAYOUT_TMH_10_2_FULL, L, R, C, Vhc, Lsd, Rsd, Ls, Rs, Vhl, Vhr, Lw, Rw, Csd, Cs, LFE1, LFE2, Lc, Rc, HI, VI, Haptic), }; static const enum MovChannelLayoutTag mov_ch_layouts_aac[] = { @@ -343,57 +278,71 @@ static const struct { { AV_CODEC_ID_NONE, NULL }, }; +static const struct MovChannelLayoutMap* find_layout_map(uint32_t tag) +{ +#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 1 + { + int i; + for (i = 0; i < FF_ARRAY_ELEMS(mov_ch_layout_map); i += 1 + (mov_ch_layout_map[i].tag & 0xffff)) + av_assert2(mov_ch_layout_map[i].tag & 0xffff0000); + av_assert2(i == FF_ARRAY_ELEMS(mov_ch_layout_map)); + } +#endif + for (int i = 0; i < FF_ARRAY_ELEMS(mov_ch_layout_map); i += 1 + (mov_ch_layout_map[i].tag & 0xffff)) + if (mov_ch_layout_map[i].tag == tag) + return &mov_ch_layout_map[i + 1]; + return NULL; +} + /** - * Get the channel layout for the specified channel layout tag. + * Get the channel layout for the specified non-special channel layout tag if + * known. * - * @param[in] tag channel layout tag - * @param[out] bitmap channel bitmap (only used if needed) - * @return channel layout + * @param[in,out] ch_layout channel layout + * @param[in] tag channel layout tag + * @return <0 on error */ -static uint64_t mov_get_channel_layout(uint32_t tag, uint32_t bitmap) +static int mov_get_channel_layout(AVChannelLayout *ch_layout, uint32_t tag) { int i, channels; const struct MovChannelLayoutMap *layout_map; - /* use ff_mov_get_channel_label() to build a layout instead */ - if (tag == MOV_CH_LAYOUT_USE_DESCRIPTIONS) - return 0; - - /* handle the use of the channel bitmap */ - if (tag == MOV_CH_LAYOUT_USE_BITMAP) - return bitmap < 0x40000 ? bitmap : 0; - - /* get the layout map based on the channel count for the specified layout tag */ channels = tag & 0xFFFF; - if (channels > 9) - channels = 0; - layout_map = mov_ch_layout_map[channels]; /* find the channel layout for the specified layout tag */ - for (i = 0; layout_map[i].tag != 0; i++) { - if (layout_map[i].tag == tag) - break; + layout_map = find_layout_map(tag); + if (layout_map) { + int ret; + av_channel_layout_uninit(ch_layout); + ret = av_channel_layout_custom_init(ch_layout, channels); + if (ret < 0) + return ret; + for (i = 0; i < channels; i++) { + enum AVChannel id = layout_map[i].id; + ch_layout->u.map[i].id = (id != AV_CHAN_NONE ? id : AV_CHAN_UNKNOWN); + } + return av_channel_layout_retype(ch_layout, 0, AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL); } - return layout_map[i].layout; + return 0; } -static uint64_t mov_get_channel_mask(uint32_t label) +static enum AVChannel mov_get_channel_id(uint32_t label) { if (label == 0) - return 0; + return AV_CHAN_UNUSED; if (label <= 18) - return 1U << (label - 1); + return (label - 1); if (label == 35) - return AV_CH_WIDE_LEFT; + return AV_CHAN_WIDE_LEFT; if (label == 36) - return AV_CH_WIDE_RIGHT; + return AV_CHAN_WIDE_RIGHT; if (label == 37) - return AV_CH_LOW_FREQUENCY_2; + return AV_CHAN_LOW_FREQUENCY_2; if (label == 38) - return AV_CH_STEREO_LEFT; + return AV_CHAN_STEREO_LEFT; if (label == 39) - return AV_CH_STEREO_RIGHT; - return 0; + return AV_CHAN_STEREO_RIGHT; + return AV_CHAN_UNKNOWN; } static uint32_t mov_get_channel_label(enum AVChannel channel) @@ -438,22 +387,20 @@ int ff_mov_get_channel_layout_tag(const AVCodecParameters *par, /* get the layout map based on the channel count */ channels = par->ch_layout.nb_channels; - if (channels > 9) - channels = 0; - layout_map = mov_ch_layout_map[channels]; /* find the layout tag for the specified channel layout */ for (i = 0; layouts[i] != 0; i++) { if ((layouts[i] & 0xFFFF) != channels) continue; - for (j = 0; layout_map[j].tag != 0; j++) { - if (layout_map[j].tag == layouts[i] && - (par->ch_layout.order == AV_CHANNEL_ORDER_NATIVE && - layout_map[j].layout == par->ch_layout.u.mask)) + layout_map = find_layout_map(layouts[i]); + if (layout_map) { + for (j = 0; j < channels; j++) { + if (av_channel_layout_channel_from_index(&par->ch_layout, j) != layout_map[j].id) + break; + } + if (j == channels) break; } - if (layout_map[j].tag) - break; } tag = layouts[i]; } @@ -497,8 +444,8 @@ int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st, int64_t size) { uint32_t layout_tag, bitmap, num_descr; - uint64_t label_mask, mask = 0; - int i; + int ret; + AVChannelLayout *ch_layout = &st->codecpar->ch_layout; if (size < 12) return AVERROR_INVALIDDATA; @@ -514,40 +461,364 @@ int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st, if (size < 12ULL + num_descr * 20ULL) return 0; - label_mask = 0; - for (i = 0; i < num_descr; i++) { - uint32_t label; - if (pb->eof_reached) { - av_log(s, AV_LOG_ERROR, - "reached EOF while reading channel layout\n"); - return AVERROR_INVALIDDATA; + if (layout_tag == MOV_CH_LAYOUT_USE_DESCRIPTIONS) { + int nb_channels = ch_layout->nb_channels ? ch_layout->nb_channels : num_descr; + if (num_descr > nb_channels) { + av_log(s, AV_LOG_WARNING, "got %d channel descriptions, capping to the number of channels %d\n", + num_descr, nb_channels); + num_descr = nb_channels; + } + + av_channel_layout_uninit(ch_layout); + ret = av_channel_layout_custom_init(ch_layout, nb_channels); + if (ret < 0) + goto out; + + for (int i = 0; i < num_descr; i++) { + uint32_t label; + if (pb->eof_reached) { + av_log(s, AV_LOG_ERROR, + "reached EOF while reading channel layout\n"); + return AVERROR_INVALIDDATA; + } + label = avio_rb32(pb); // mChannelLabel + avio_rb32(pb); // mChannelFlags + avio_rl32(pb); // mCoordinates[0] + avio_rl32(pb); // mCoordinates[1] + avio_rl32(pb); // mCoordinates[2] + size -= 20; + ch_layout->u.map[i].id = mov_get_channel_id(label); + } + + ret = av_channel_layout_retype(ch_layout, 0, AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL); + if (ret < 0) + goto out; + } else if (layout_tag == MOV_CH_LAYOUT_USE_BITMAP) { + if (!ch_layout->nb_channels || av_popcount(bitmap) == ch_layout->nb_channels) { + if (bitmap < 0x40000) { + av_channel_layout_uninit(ch_layout); + av_channel_layout_from_mask(ch_layout, bitmap); + } + } else { + av_log(s, AV_LOG_WARNING, "ignoring channel layout bitmap with %d channels because number of channels is %d\n", + av_popcount64(bitmap), ch_layout->nb_channels); + } + } else if (layout_tag & 0xFFFF) { + int nb_channels = layout_tag & 0xFFFF; + if (!ch_layout->nb_channels) + ch_layout->nb_channels = nb_channels; + if (nb_channels == ch_layout->nb_channels) { + ret = mov_get_channel_layout(ch_layout, layout_tag); + if (ret < 0) + return ret; + } else { + av_log(s, AV_LOG_WARNING, "ignoring layout tag with %d channels because number of channels is %d\n", + nb_channels, ch_layout->nb_channels); + } + } + ret = 0; + +out: + avio_skip(pb, size - 12); + + return ret; +} + +/* ISO/IEC 23001-8, 8.2 */ +static const AVChannelLayout iso_channel_configuration[] = { + // 0: any setup + {0}, + + // 1: centre front + AV_CHANNEL_LAYOUT_MONO, + + // 2: left front, right front + AV_CHANNEL_LAYOUT_STEREO, + + // 3: centre front, left front, right front + AV_CHANNEL_LAYOUT_SURROUND, + + // 4: centre front, left front, right front, rear centre + AV_CHANNEL_LAYOUT_4POINT0, + + // 5: centre front, left front, right front, left surround, right surround + AV_CHANNEL_LAYOUT_5POINT0, + + // 6: 5 + LFE + AV_CHANNEL_LAYOUT_5POINT1, + + // 7: centre front, left front centre, right front centre, + // left front, right front, left surround, right surround, LFE + AV_CHANNEL_LAYOUT_7POINT1_WIDE, + + // 8: channel1, channel2 + AV_CHANNEL_LAYOUT_STEREO_DOWNMIX, + + // 9: left front, right front, rear centre + AV_CHANNEL_LAYOUT_2_1, + + // 10: left front, right front, left surround, right surround + AV_CHANNEL_LAYOUT_2_2, + + // 11: centre front, left front, right front, left surround, right surround, rear centre, LFE + AV_CHANNEL_LAYOUT_6POINT1, + + // 12: centre front, left front, right front + // left surround, right surround + // rear surround left, rear surround right + // LFE + AV_CHANNEL_LAYOUT_7POINT1, + + // 13: + AV_CHANNEL_LAYOUT_22POINT2, + + // 14: + AV_CHANNEL_LAYOUT_7POINT1_TOP_BACK, + + // TODO: 15 - 20 +}; + +/* ISO/IEC 23001-8, table 8 */ +static const enum AVChannel iso_channel_position[] = { + // 0: left front + AV_CHAN_FRONT_LEFT, + + // 1: right front + AV_CHAN_FRONT_RIGHT, + + // 2: centre front + AV_CHAN_FRONT_CENTER, + + // 3: low frequence enhancement + AV_CHAN_LOW_FREQUENCY, + + // 4: left surround + // TODO + AV_CHAN_NONE, + + // 5: right surround + // TODO + AV_CHAN_NONE, + + // 6: left front centre + AV_CHAN_FRONT_LEFT_OF_CENTER, + + // 7: right front centre + AV_CHAN_FRONT_RIGHT_OF_CENTER, + + // 8: rear surround left + AV_CHAN_BACK_LEFT, + + // 9: rear surround right + AV_CHAN_BACK_RIGHT, + + // 10: rear centre + AV_CHAN_BACK_CENTER, + + // 11: left surround direct + AV_CHAN_SURROUND_DIRECT_LEFT, + + // 12: right surround direct + AV_CHAN_SURROUND_DIRECT_RIGHT, + + // 13: left side surround + AV_CHAN_SIDE_LEFT, + + // 14: right side surround + AV_CHAN_SIDE_RIGHT, + + // 15: left wide front + AV_CHAN_WIDE_LEFT, + + // 16: right wide front + AV_CHAN_WIDE_RIGHT, + + // 17: left front vertical height + AV_CHAN_TOP_FRONT_LEFT, + + // 18: right front vertical height + AV_CHAN_TOP_FRONT_RIGHT, + + // 19: centre front vertical height + AV_CHAN_TOP_FRONT_CENTER, + + // 20: left surround vertical height rear + AV_CHAN_TOP_BACK_LEFT, + + // 21: right surround vertical height rear + AV_CHAN_TOP_BACK_RIGHT, + + // 22: centre vertical height rear + AV_CHAN_TOP_BACK_CENTER, + + // 23: left vertical height side surround + AV_CHAN_TOP_SIDE_LEFT, + + // 24: right vertical height side surround + AV_CHAN_TOP_SIDE_RIGHT, + + // 25: top centre surround + AV_CHAN_TOP_CENTER, + + // 26: low frequency enhancement 2 + AV_CHAN_LOW_FREQUENCY_2, + + // 27: left front vertical bottom + AV_CHAN_BOTTOM_FRONT_LEFT, + + // 28: right front vertical bottom + AV_CHAN_BOTTOM_FRONT_RIGHT, + + // 29: centre front vertical bottom + AV_CHAN_BOTTOM_FRONT_CENTER, + + // 30: left vertical height surround + // TODO + AV_CHAN_NONE, + + // 31: right vertical height surround + // TODO + AV_CHAN_NONE, + + // 32, 33, 34, 35, reserved + AV_CHAN_NONE, + AV_CHAN_NONE, + AV_CHAN_NONE, + AV_CHAN_NONE, + + // 36: low frequency enhancement 3 + AV_CHAN_NONE, + + // 37: left edge of screen + AV_CHAN_NONE, + // 38: right edge of screen + AV_CHAN_NONE, + // 39: half-way between centre of screen and left edge of screen + AV_CHAN_NONE, + // 40: half-way between centre of screen and right edge of screen + AV_CHAN_NONE, + + // 41: left back surround + AV_CHAN_NONE, + + // 42: right back surround + AV_CHAN_NONE, + + // 43 - 125: reserved + // 126: explicit position + // 127: unknown /undefined +}; + +int ff_mov_get_channel_config_from_layout(const AVChannelLayout *layout, int *config) +{ + // Set default value which means any setup in 23001-8 + *config = 0; + for (int i = 0; i < FF_ARRAY_ELEMS(iso_channel_configuration); i++) { + if (!av_channel_layout_compare(layout, iso_channel_configuration + i)) { + *config = i; + break; } - label = avio_rb32(pb); // mChannelLabel - avio_rb32(pb); // mChannelFlags - avio_rl32(pb); // mCoordinates[0] - avio_rl32(pb); // mCoordinates[1] - avio_rl32(pb); // mCoordinates[2] - size -= 20; - if (layout_tag == 0) { - uint64_t mask_incr = mov_get_channel_mask(label); - if (mask_incr == 0) { - label_mask = 0; + } + + return 0; +} + +int ff_mov_get_channel_layout_from_config(int config, AVChannelLayout *layout) +{ + if (config > 0 && config < FF_ARRAY_ELEMS(iso_channel_configuration)) { + av_channel_layout_copy(layout, &iso_channel_configuration[config]); + return 0; + } + + return -1; +} + +int ff_mov_get_channel_positions_from_layout(const AVChannelLayout *layout, + uint8_t *position, int position_num) +{ + enum AVChannel channel; + + if (position_num < layout->nb_channels) + return AVERROR(EINVAL); + + for (int i = 0; i < layout->nb_channels; i++) { + position[i] = 127; + channel = av_channel_layout_channel_from_index(layout, i); + if (channel == AV_CHAN_NONE) + return AVERROR(EINVAL); + + for (int j = 0; j < FF_ARRAY_ELEMS(iso_channel_position); j++) { + if (iso_channel_position[j] == channel) { + position[i] = j; break; } - label_mask |= mask_incr; + } + if (position[i] == 127) + return AVERROR(EINVAL); + } + + return 0; +} + +int ff_mov_read_chnl(AVFormatContext *s, AVIOContext *pb, AVStream *st) +{ + int stream_structure = avio_r8(pb); + int ret; + + // stream carries channels + if (stream_structure & 1) { + int layout = avio_r8(pb); + + av_log(s, AV_LOG_TRACE, "'chnl' layout %d\n", layout); + if (!layout) { + AVChannelLayout *ch_layout = &st->codecpar->ch_layout; + int nb_channels = ch_layout->nb_channels; + + av_channel_layout_uninit(ch_layout); + ret = av_channel_layout_custom_init(ch_layout, nb_channels); + if (ret < 0) + return ret; + + for (int i = 0; i < nb_channels; i++) { + int speaker_pos = avio_r8(pb); + enum AVChannel channel; + + if (speaker_pos == 126) // explicit position + avio_skip(pb, 3); // azimuth, elevation + + if (speaker_pos >= FF_ARRAY_ELEMS(iso_channel_position)) + channel = AV_CHAN_NONE; + else + channel = iso_channel_position[speaker_pos]; + + if (channel == AV_CHAN_NONE) { + av_log(s, AV_LOG_WARNING, "speaker position %d is not implemented\n", speaker_pos); + channel = AV_CHAN_UNKNOWN; + } + + ch_layout->u.map[i].id = channel; + } + + ret = av_channel_layout_retype(ch_layout, 0, AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL); + if (ret < 0) + return ret; + } else { + uint64_t omitted_channel_map = avio_rb64(pb); + + if (omitted_channel_map) { + avpriv_request_sample(s, "omitted_channel_map 0x%" PRIx64 " != 0", + omitted_channel_map); + return AVERROR_PATCHWELCOME; + } + ff_mov_get_channel_layout_from_config(layout, &st->codecpar->ch_layout); } } - if (layout_tag == 0) { - if (label_mask) - mask = label_mask; - } else - mask = mov_get_channel_layout(layout_tag, bitmap); - - if (mask) { - av_channel_layout_uninit(&st->codecpar->ch_layout); - av_channel_layout_from_mask(&st->codecpar->ch_layout, mask); + + // stream carries objects + if (stream_structure & 2) { + int obj_count = avio_r8(pb); + av_log(s, AV_LOG_TRACE, "'chnl' with object_count %d\n", obj_count); } - avio_skip(pb, size - 12); return 0; } diff --git a/libavformat/mov_chan.h b/libavformat/mov_chan.h index 93d98787986..e480809c442 100644 --- a/libavformat/mov_chan.h +++ b/libavformat/mov_chan.h @@ -163,4 +163,34 @@ int ff_mov_get_channel_layout_tag(const AVCodecParameters *par, int ff_mov_read_chan(AVFormatContext *s, AVIOContext *pb, AVStream *st, int64_t size); +/** + * Get ISO/IEC 23001-8 ChannelConfiguration from AVChannelLayout. + * + */ +int ff_mov_get_channel_config_from_layout(const AVChannelLayout *layout, int *config); + +/** + * Get AVChannelLayout from ISO/IEC 23001-8 ChannelConfiguration. + * + * @return 0 for success, -1 for doesn't match, layout is untouched on failure + */ + +int ff_mov_get_channel_layout_from_config(int config, AVChannelLayout *layout); + +/** + * Get ISO/IEC 23001-8 OutputChannelPosition from AVChannelLayout. + */ +int ff_mov_get_channel_positions_from_layout(const AVChannelLayout *layout, + uint8_t *position, int position_num); + +/** + * Read 'chnl' tag from the input stream. + * + * @param s AVFormatContext + * @param pb AVIOContext + * @param st The stream to set codec values for + * @return 0 if ok, or negative AVERROR code on failure + */ +int ff_mov_read_chnl(AVFormatContext *s, AVIOContext *pb, AVStream *st); + #endif /* AVFORMAT_MOV_CHAN_H */ diff --git a/libavformat/movenc.c b/libavformat/movenc.c index c4fcb5f8b1b..2d3a4db1d2f 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -32,20 +32,24 @@ #include "dovi_isom.h" #include "riff.h" #include "avio.h" +#include "iamf_writer.h" #include "isom.h" #include "av1.h" #include "avc.h" +#include "evc.h" #include "libavcodec/ac3_parser_internal.h" #include "libavcodec/dnxhddata.h" #include "libavcodec/flac.h" #include "libavcodec/get_bits.h" +#include "libavcodec/bsf.h" #include "libavcodec/internal.h" #include "libavcodec/put_bits.h" #include "libavcodec/vc1_common.h" #include "libavcodec/raw.h" #include "internal.h" #include "libavutil/avstring.h" +#include "libavutil/bprint.h" #include "libavutil/channel_layout.h" #include "libavutil/csp.h" #include "libavutil/intfloat.h" @@ -67,58 +71,59 @@ #include "ttmlenc.h" #include "version.h" #include "vpcc.h" +#include "vvc.h" static const AVOption options[] = { - { "movflags", "MOV muxer flags", offsetof(MOVMuxContext, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "rtphint", "Add RTP hint tracks", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_RTP_HINT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "moov_size", "maximum moov size so it can be placed at the begin", offsetof(MOVMuxContext, reserved_moov_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, 0 }, - { "empty_moov", "Make the initial moov atom empty", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_EMPTY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "frag_keyframe", "Fragment at video keyframes", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_KEYFRAME}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "frag_every_frame", "Fragment at every frame", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_EVERY_FRAME}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "separate_moof", "Write separate moof/mdat atoms for each track", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SEPARATE_MOOF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "frag_custom", "Flush fragments on caller requests", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_CUSTOM}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "isml", "Create a live smooth streaming feed (for pushing to a publishing point)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_ISML}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "faststart", "Run a second pass to put the index (moov atom) at the beginning of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FASTSTART}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "omit_tfhd_offset", "Omit the base data offset in tfhd atoms", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_OMIT_TFHD_OFFSET}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "disable_chpl", "Disable Nero chapter atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DISABLE_CHPL}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "default_base_moof", "Set the default-base-is-moof flag in tfhd atoms", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DEFAULT_BASE_MOOF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "dash", "Write DASH compatible fragmented MP4", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DASH}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "cmaf", "Write CMAF compatible fragmented MP4", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_CMAF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "frag_discont", "Signal that the next fragment is discontinuous from earlier ones", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_DISCONT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "delay_moov", "Delay writing the initial moov until the first fragment is cut, or until the first fragment flush", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DELAY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "global_sidx", "Write a global sidx index at the start of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_GLOBAL_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "skip_sidx", "Skip writing of sidx atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SKIP_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "write_colr", "Write colr atom even if the color info is unspecified (Experimental, may be renamed or changed, do not use from scripts)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_COLR}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "prefer_icc", "If writing colr atom prioritise usage of ICC profile if it exists in stream packet side data", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_PREFER_ICC}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "write_gama", "Write deprecated gama atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_GAMA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "use_metadata_tags", "Use mdta atom for metadata.", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_USE_MDTA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "skip_trailer", "Skip writing the mfra/tfra/mfro trailer for fragmented files", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SKIP_TRAILER}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - { "negative_cts_offsets", "Use negative CTS offsets (reducing the need for edit lists)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "movflags" }, - FF_RTP_FLAG_OPTS(MOVMuxContext, rtp_flags), - { "skip_iods", "Skip writing iods atom.", offsetof(MOVMuxContext, iods_skip), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, - { "iods_audio_profile", "iods audio profile atom.", offsetof(MOVMuxContext, iods_audio_profile), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM}, - { "iods_video_profile", "iods video profile atom.", offsetof(MOVMuxContext, iods_video_profile), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM}, + { "brand", "Override major brand", offsetof(MOVMuxContext, major_brand), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = AV_OPT_FLAG_ENCODING_PARAM }, + { "empty_hdlr_name", "write zero-length name string in hdlr atoms within mdia and minf atoms", offsetof(MOVMuxContext, empty_hdlr_name), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, + { "encryption_key", "The media encryption key (hex)", offsetof(MOVMuxContext, encryption_key), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_ENCODING_PARAM }, + { "encryption_kid", "The media encryption key identifier (hex)", offsetof(MOVMuxContext, encryption_kid), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_ENCODING_PARAM }, + { "encryption_scheme", "Configures the encryption scheme, allowed values are none, cenc-aes-ctr", offsetof(MOVMuxContext, encryption_scheme_str), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = AV_OPT_FLAG_ENCODING_PARAM }, { "frag_duration", "Maximum fragment duration", offsetof(MOVMuxContext, max_fragment_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, - { "min_frag_duration", "Minimum fragment duration", offsetof(MOVMuxContext, min_fragment_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "frag_interleave", "Interleave samples within fragments (max number of consecutive samples, lower is tighter interleaving, but with more overhead)", offsetof(MOVMuxContext, frag_interleave), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, { "frag_size", "Maximum fragment size", offsetof(MOVMuxContext, max_fragment_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, - { "ism_lookahead", "Number of lookahead entries for ISM files", offsetof(MOVMuxContext, ism_lookahead), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 255, AV_OPT_FLAG_ENCODING_PARAM}, - { "video_track_timescale", "set timescale of all video tracks", offsetof(MOVMuxContext, video_track_timescale), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, - { "brand", "Override major brand", offsetof(MOVMuxContext, major_brand), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = AV_OPT_FLAG_ENCODING_PARAM }, - { "use_editlist", "use edit list", offsetof(MOVMuxContext, use_editlist), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM}, { "fragment_index", "Fragment number of the next fragment", offsetof(MOVMuxContext, fragments), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + { "iods_audio_profile", "iods audio profile atom.", offsetof(MOVMuxContext, iods_audio_profile), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM}, + { "iods_video_profile", "iods video profile atom.", offsetof(MOVMuxContext, iods_video_profile), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 255, AV_OPT_FLAG_ENCODING_PARAM}, + { "ism_lookahead", "Number of lookahead entries for ISM files", offsetof(MOVMuxContext, ism_lookahead), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 255, AV_OPT_FLAG_ENCODING_PARAM}, + { "movflags", "MOV muxer flags", offsetof(MOVMuxContext, flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "cmaf", "Write CMAF compatible fragmented MP4", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_CMAF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "dash", "Write DASH compatible fragmented MP4", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DASH}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "default_base_moof", "Set the default-base-is-moof flag in tfhd atoms", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DEFAULT_BASE_MOOF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "delay_moov", "Delay writing the initial moov until the first fragment is cut, or until the first fragment flush", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DELAY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "disable_chpl", "Disable Nero chapter atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_DISABLE_CHPL}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "empty_moov", "Make the initial moov atom empty", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_EMPTY_MOOV}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "faststart", "Run a second pass to put the index (moov atom) at the beginning of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FASTSTART}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "frag_custom", "Flush fragments on caller requests", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_CUSTOM}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "frag_discont", "Signal that the next fragment is discontinuous from earlier ones", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_DISCONT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "frag_every_frame", "Fragment at every frame", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_EVERY_FRAME}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "frag_keyframe", "Fragment at video keyframes", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_FRAG_KEYFRAME}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "global_sidx", "Write a global sidx index at the start of the file", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_GLOBAL_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "isml", "Create a live smooth streaming feed (for pushing to a publishing point)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_ISML}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "moov_size", "maximum moov size so it can be placed at the begin", offsetof(MOVMuxContext, reserved_moov_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = 0 }, + { "negative_cts_offsets", "Use negative CTS offsets (reducing the need for edit lists)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_NEGATIVE_CTS_OFFSETS}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "omit_tfhd_offset", "Omit the base data offset in tfhd atoms", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_OMIT_TFHD_OFFSET}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "prefer_icc", "If writing colr atom prioritise usage of ICC profile if it exists in stream packet side data", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_PREFER_ICC}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "rtphint", "Add RTP hint tracks", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_RTP_HINT}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "separate_moof", "Write separate moof/mdat atoms for each track", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SEPARATE_MOOF}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "skip_sidx", "Skip writing of sidx atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SKIP_SIDX}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "skip_trailer", "Skip writing the mfra/tfra/mfro trailer for fragmented files", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_SKIP_TRAILER}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "use_metadata_tags", "Use mdta atom for metadata.", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_USE_MDTA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "write_colr", "Write colr atom even if the color info is unspecified (Experimental, may be renamed or changed, do not use from scripts)", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_COLR}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "write_gama", "Write deprecated gama atom", 0, AV_OPT_TYPE_CONST, {.i64 = FF_MOV_FLAG_WRITE_GAMA}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = "movflags" }, + { "min_frag_duration", "Minimum fragment duration", offsetof(MOVMuxContext, min_fragment_duration), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { "mov_gamma", "gamma value for gama atom", offsetof(MOVMuxContext, gamma), AV_OPT_TYPE_FLOAT, {.dbl = 0.0 }, 0.0, 10, AV_OPT_FLAG_ENCODING_PARAM}, - { "frag_interleave", "Interleave samples within fragments (max number of consecutive samples, lower is tighter interleaving, but with more overhead)", offsetof(MOVMuxContext, frag_interleave), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, - { "encryption_scheme", "Configures the encryption scheme, allowed values are none, cenc-aes-ctr", offsetof(MOVMuxContext, encryption_scheme_str), AV_OPT_TYPE_STRING, {.str = NULL}, .flags = AV_OPT_FLAG_ENCODING_PARAM }, - { "encryption_key", "The media encryption key (hex)", offsetof(MOVMuxContext, encryption_key), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_ENCODING_PARAM }, - { "encryption_kid", "The media encryption key identifier (hex)", offsetof(MOVMuxContext, encryption_kid), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_ENCODING_PARAM }, + { "movie_timescale", "set movie timescale", offsetof(MOVMuxContext, movie_timescale), AV_OPT_TYPE_INT, {.i64 = MOV_TIMESCALE}, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, + FF_RTP_FLAG_OPTS(MOVMuxContext, rtp_flags), + { "skip_iods", "Skip writing iods atom.", offsetof(MOVMuxContext, iods_skip), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, + { "use_editlist", "use edit list", offsetof(MOVMuxContext, use_editlist), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM}, { "use_stream_ids_as_track_ids", "use stream ids as track ids", offsetof(MOVMuxContext, use_stream_ids_as_track_ids), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, + { "video_track_timescale", "set timescale of all video tracks", offsetof(MOVMuxContext, video_track_timescale), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { "write_btrt", "force or disable writing btrt", offsetof(MOVMuxContext, write_btrt), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM}, + { "write_prft", "Write producer reference time box with specified time source", offsetof(MOVMuxContext, write_prft), AV_OPT_TYPE_INT, {.i64 = MOV_PRFT_NONE}, 0, MOV_PRFT_NB-1, AV_OPT_FLAG_ENCODING_PARAM, .unit = "prft"}, + { "pts", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MOV_PRFT_SRC_PTS}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, .unit = "prft"}, + { "wallclock", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MOV_PRFT_SRC_WALLCLOCK}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, .unit = "prft"}, { "write_tmcd", "force or disable writing tmcd", offsetof(MOVMuxContext, write_tmcd), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, AV_OPT_FLAG_ENCODING_PARAM}, - { "write_prft", "Write producer reference time box with specified time source", offsetof(MOVMuxContext, write_prft), AV_OPT_TYPE_INT, {.i64 = MOV_PRFT_NONE}, 0, MOV_PRFT_NB-1, AV_OPT_FLAG_ENCODING_PARAM, "prft"}, - { "wallclock", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MOV_PRFT_SRC_WALLCLOCK}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "prft"}, - { "pts", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = MOV_PRFT_SRC_PTS}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM, "prft"}, - { "empty_hdlr_name", "write zero-length name string in hdlr atoms within mdia and minf atoms", offsetof(MOVMuxContext, empty_hdlr_name), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM}, - { "movie_timescale", "set movie timescale", offsetof(MOVMuxContext, movie_timescale), AV_OPT_TYPE_INT, {.i64 = MOV_TIMESCALE}, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, { NULL }, }; @@ -314,6 +319,32 @@ static int mov_write_sdtp_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } +static int mov_write_iacb_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track) +{ + AVIOContext *dyn_bc; + int64_t pos = avio_tell(pb); + uint8_t *dyn_buf = NULL; + int dyn_size; + int ret = avio_open_dyn_buf(&dyn_bc); + if (ret < 0) + return ret; + + avio_wb32(pb, 0); + ffio_wfourcc(pb, "iacb"); + avio_w8(pb, 1); // configurationVersion + + ret = ff_iamf_write_descriptors(track->iamf, dyn_bc, s); + if (ret < 0) + return ret; + + dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf); + ffio_write_leb(pb, dyn_size); + avio_write(pb, dyn_buf, dyn_size); + av_free(dyn_buf); + + return update_size(pb, pos); +} + static int mov_write_amr_tag(AVIOContext *pb, MOVTrack *track) { avio_wb32(pb, 0x11); /* size */ @@ -637,10 +668,10 @@ static int mov_write_enda_tag(AVIOContext *pb) static int mov_write_enda_tag_be(AVIOContext *pb) { - avio_wb32(pb, 10); - ffio_wfourcc(pb, "enda"); - avio_wb16(pb, 0); /* big endian */ - return 10; + avio_wb32(pb, 10); + ffio_wfourcc(pb, "enda"); + avio_wb16(pb, 0); /* big endian */ + return 10; } static void put_descr(AVIOContext *pb, int tag, unsigned int size) @@ -671,11 +702,11 @@ struct mpeg4_bit_rate_values { static struct mpeg4_bit_rate_values calculate_mpeg4_bit_rates(MOVTrack *track) { - AVCPBProperties *props = track->st ? - (AVCPBProperties*)av_stream_get_side_data(track->st, - AV_PKT_DATA_CPB_PROPERTIES, - NULL) : - NULL; + const AVPacketSideData *sd = track->st ? + av_packet_side_data_get(track->st->codecpar->coded_side_data, + track->st->codecpar->nb_coded_side_data, + AV_PKT_DATA_CPB_PROPERTIES) : NULL; + AVCPBProperties *props = sd ? (AVCPBProperties *)sd->data : NULL; struct mpeg4_bit_rate_values bit_rates = { 0 }; bit_rates.avg_bit_rate = compute_avg_bitrate(track); @@ -948,9 +979,9 @@ static int mov_write_wave_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra ffio_wfourcc(pb, "wave"); if (track->par->codec_id != AV_CODEC_ID_QDM2) { - avio_wb32(pb, 12); /* size */ - ffio_wfourcc(pb, "frma"); - avio_wl32(pb, track->tag); + avio_wb32(pb, 12); /* size */ + ffio_wfourcc(pb, "frma"); + avio_wl32(pb, track->tag); } if (track->par->codec_id == AV_CODEC_ID_AAC) { @@ -960,9 +991,9 @@ static int mov_write_wave_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra avio_wb32(pb, 0); mov_write_esds_tag(pb, track); } else if (mov_pcm_le_gt16(track->par->codec_id)) { - mov_write_enda_tag(pb); + mov_write_enda_tag(pb); } else if (mov_pcm_be_gt16(track->par->codec_id)) { - mov_write_enda_tag_be(pb); + mov_write_enda_tag_be(pb); } else if (track->par->codec_id == AV_CODEC_ID_AMR_NB) { mov_write_amr_tag(pb, track); } else if (track->par->codec_id == AV_CODEC_ID_AC3) { @@ -1179,6 +1210,75 @@ static int mov_write_btrt_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } +static int mov_write_chnl_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + int config = 0; + int ret; + uint8_t *speaker_pos = NULL; + const AVChannelLayout *layout = &track->par->ch_layout; + + ret = ff_mov_get_channel_config_from_layout(layout, &config); + if (ret || !config) { + config = 0; + speaker_pos = av_malloc(layout->nb_channels); + if (!speaker_pos) + return AVERROR(ENOMEM); + ret = ff_mov_get_channel_positions_from_layout(layout, + speaker_pos, layout->nb_channels); + if (ret) { + char buf[128] = {0}; + + av_freep(&speaker_pos); + av_channel_layout_describe(layout, buf, sizeof(buf)); + av_log(s, AV_LOG_ERROR, "unsupported channel layout %s\n", buf); + return ret; + } + } + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "chnl"); + avio_wb32(pb, 0); /* version & flags */ + + avio_w8(pb, 1); /* stream_structure */ + avio_w8(pb, config); + if (config) { + avio_wb64(pb, 0); + } else { + for (int i = 0; i < layout->nb_channels; i++) + avio_w8(pb, speaker_pos[i]); + av_freep(&speaker_pos); + } + + return update_size(pb, pos); +} + +static int mov_write_pcmc_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + int format_flags; + int sample_size; + + avio_wb32(pb, 0); /* size */ + ffio_wfourcc(pb, "pcmC"); + avio_wb32(pb, 0); /* version & flags */ + + /* 0x01: indicates little-endian format */ + format_flags = (track->par->codec_id == AV_CODEC_ID_PCM_F32LE || + track->par->codec_id == AV_CODEC_ID_PCM_F64LE || + track->par->codec_id == AV_CODEC_ID_PCM_S16LE || + track->par->codec_id == AV_CODEC_ID_PCM_S24LE || + track->par->codec_id == AV_CODEC_ID_PCM_S32LE); + avio_w8(pb, format_flags); + sample_size = track->par->bits_per_raw_sample; + if (!sample_size) + sample_size = av_get_exact_bits_per_sample(track->par->codec_id); + av_assert0(sample_size); + avio_w8(pb, sample_size); + + return update_size(pb, pos); +} + static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { int64_t pos = avio_tell(pb); @@ -1241,7 +1341,8 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex avio_wb16(pb, 16); avio_wb16(pb, track->audio_vbr ? -2 : 0); /* compression ID */ } else { /* reserved for mp4/3gp */ - avio_wb16(pb, track->par->ch_layout.nb_channels); + avio_wb16(pb, track->tag == MKTAG('i', 'a', 'm', 'f') ? + 0 : track->par->ch_layout.nb_channels); if (track->par->codec_id == AV_CODEC_ID_FLAC || track->par->codec_id == AV_CODEC_ID_ALAC) { avio_wb16(pb, track->par->bits_per_raw_sample); @@ -1252,7 +1353,9 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex } avio_wb16(pb, 0); /* packet size (= 0) */ - if (track->par->codec_id == AV_CODEC_ID_OPUS) + if (track->tag == MKTAG('i','a','m','f')) + avio_wb16(pb, 0); /* samplerate must be 0 for IAMF */ + else if (track->par->codec_id == AV_CODEC_ID_OPUS) avio_wb16(pb, 48000); else if (track->par->codec_id == AV_CODEC_ID_TRUEHD) avio_wb32(pb, track->par->sample_rate); @@ -1289,6 +1392,8 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex ret = mov_write_wave_tag(s, pb, track); else if (track->tag == MKTAG('m','p','4','a')) ret = mov_write_esds_tag(pb, track); + else if (track->tag == MKTAG('i','a','m','f')) + ret = mov_write_iacb_tag(mov->fc, pb, track); else if (track->par->codec_id == AV_CODEC_ID_AMR_NB) ret = mov_write_amr_tag(pb, track); else if (track->par->codec_id == AV_CODEC_ID_AC3) @@ -1305,7 +1410,13 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex ret = mov_write_dops_tag(s, pb, track); else if (track->par->codec_id == AV_CODEC_ID_TRUEHD) ret = mov_write_dmlp_tag(s, pb, track); - else if (track->vos_len > 0) + else if (tag == MOV_MP4_IPCM_TAG || tag == MOV_MP4_FPCM_TAG) { + if (track->par->ch_layout.nb_channels > 1) + ret = mov_write_chnl_tag(s, pb, track); + if (ret < 0) + return ret; + ret = mov_write_pcmc_tag(s, pb, track); + } else if (track->vos_len > 0) ret = mov_write_glbl_tag(pb, track); if (ret < 0) @@ -1367,10 +1478,7 @@ static int mov_write_vpcc_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra avio_wb32(pb, 0); ffio_wfourcc(pb, "vpcC"); - avio_w8(pb, 1); /* version */ - avio_wb24(pb, 0); /* flags */ ff_isom_write_vpcc(s, pb, track->vos_data, track->vos_len, track->par); - return update_size(pb, pos); } @@ -1387,6 +1495,38 @@ static int mov_write_hvcc_tag(AVIOContext *pb, MOVTrack *track) return update_size(pb, pos); } +static int mov_write_evcc_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + + avio_wb32(pb, 0); + ffio_wfourcc(pb, "evcC"); + + if (track->tag == MKTAG('e','v','c','1')) + ff_isom_write_evcc(pb, track->vos_data, track->vos_len, 1); + else + ff_isom_write_evcc(pb, track->vos_data, track->vos_len, 0); + + return update_size(pb, pos); +} + +static int mov_write_vvcc_tag(AVIOContext *pb, MOVTrack *track) +{ + int64_t pos = avio_tell(pb); + + avio_wb32(pb, 0); + ffio_wfourcc(pb, "vvcC"); + + avio_w8 (pb, 0); /* version */ + avio_wb24(pb, 0); /* flags */ + + if (track->tag == MKTAG('v','v','c','1')) + ff_isom_write_vvcc(pb, track->vos_data, track->vos_len, 1); + else + ff_isom_write_vvcc(pb, track->vos_data, track->vos_len, 0); + return update_size(pb, pos); +} + /* also used by all avid codecs (dv, imx, meridien) and their variants */ static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track) { @@ -1636,6 +1776,16 @@ static int mov_get_h264_codec_tag(AVFormatContext *s, MOVTrack *track) return tag; } +static int mov_get_evc_codec_tag(AVFormatContext *s, MOVTrack *track) +{ + int tag = track->par->codec_tag; + + if (!tag) + tag = MKTAG('e', 'v', 'c', '1'); + + return tag; +} + static const struct { enum AVPixelFormat pix_fmt; uint32_t tag; @@ -1660,11 +1810,11 @@ static const struct { static int mov_get_dnxhd_codec_tag(AVFormatContext *s, MOVTrack *track) { - int tag = MKTAG('A','V','d','n'); - if (track->par->profile != FF_PROFILE_UNKNOWN && - track->par->profile != FF_PROFILE_DNXHD) - tag = MKTAG('A','V','d','h'); - return tag; + int tag = MKTAG('A','V','d','n'); + if (track->par->profile != AV_PROFILE_UNKNOWN && + track->par->profile != AV_PROFILE_DNXHD) + tag = MKTAG('A','V','d','h'); + return tag; } static int mov_get_rawvideo_codec_tag(AVFormatContext *s, MOVTrack *track) @@ -1717,6 +1867,8 @@ static unsigned int mov_get_codec_tag(AVFormatContext *s, MOVTrack *track) tag = mov_get_mpeg2_xdcam_codec_tag(s, track); else if (track->par->codec_id == AV_CODEC_ID_H264) tag = mov_get_h264_codec_tag(s, track); + else if (track->par->codec_id == AV_CODEC_ID_EVC) + tag = mov_get_evc_codec_tag(s, track); else if (track->par->codec_id == AV_CODEC_ID_DNXHD) tag = mov_get_dnxhd_codec_tag(s, track); else if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) { @@ -2031,18 +2183,17 @@ static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track, int prefer_icc) // Ref (MOV): https://developer.apple.com/library/mac/technotes/tn2162/_index.html#//apple_ref/doc/uid/DTS40013070-CH1-TNTAG9 // Ref (MP4): ISO/IEC 14496-12:2012 - const uint8_t *icc_profile; - size_t icc_profile_size; - if (prefer_icc) { - icc_profile = av_stream_get_side_data(track->st, AV_PKT_DATA_ICC_PROFILE, &icc_profile_size); + const AVPacketSideData *sd = av_packet_side_data_get(track->st->codecpar->coded_side_data, + track->st->codecpar->nb_coded_side_data, + AV_PKT_DATA_ICC_PROFILE); - if (icc_profile) { - avio_wb32(pb, 12 + icc_profile_size); + if (sd) { + avio_wb32(pb, 12 + sd->size); ffio_wfourcc(pb, "colr"); ffio_wfourcc(pb, "prof"); - avio_write(pb, icc_profile, icc_profile_size); - return 12 + icc_profile_size; + avio_write(pb, sd->data, sd->size); + return 12 + sd->size; } else { av_log(NULL, AV_LOG_INFO, "no ICC profile found, will write nclx/nclc colour info instead\n"); @@ -2075,14 +2226,16 @@ static int mov_write_colr_tag(AVIOContext *pb, MOVTrack *track, int prefer_icc) static int mov_write_clli_tag(AVIOContext *pb, MOVTrack *track) { - const uint8_t *side_data; + const AVPacketSideData *side_data; const AVContentLightMetadata *content_light_metadata; - side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, NULL); + side_data = av_packet_side_data_get(track->st->codecpar->coded_side_data, + track->st->codecpar->nb_coded_side_data, + AV_PKT_DATA_CONTENT_LIGHT_LEVEL); if (!side_data) { return 0; } - content_light_metadata = (const AVContentLightMetadata*)side_data; + content_light_metadata = (const AVContentLightMetadata*)side_data->data; avio_wb32(pb, 12); // size ffio_wfourcc(pb, "clli"); @@ -2091,7 +2244,7 @@ static int mov_write_clli_tag(AVIOContext *pb, MOVTrack *track) return 12; } -static inline int64_t rescale_mdcv(AVRational q, int b) +static inline int64_t rescale_rational(AVRational q, int b) { return av_rescale(q.num, b, q.den); } @@ -2100,30 +2253,60 @@ static int mov_write_mdcv_tag(AVIOContext *pb, MOVTrack *track) { const int chroma_den = 50000; const int luma_den = 10000; - const uint8_t *side_data; - const AVMasteringDisplayMetadata *metadata; - - side_data = av_stream_get_side_data(track->st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, NULL); - metadata = (const AVMasteringDisplayMetadata*)side_data; + const AVPacketSideData *side_data; + const AVMasteringDisplayMetadata *metadata = NULL; + + side_data = av_packet_side_data_get(track->st->codecpar->coded_side_data, + track->st->codecpar->nb_coded_side_data, + AV_PKT_DATA_MASTERING_DISPLAY_METADATA); + if (side_data) + metadata = (const AVMasteringDisplayMetadata*)side_data->data; if (!metadata || !metadata->has_primaries || !metadata->has_luminance) { return 0; } avio_wb32(pb, 32); // size ffio_wfourcc(pb, "mdcv"); - avio_wb16(pb, rescale_mdcv(metadata->display_primaries[1][0], chroma_den)); - avio_wb16(pb, rescale_mdcv(metadata->display_primaries[1][1], chroma_den)); - avio_wb16(pb, rescale_mdcv(metadata->display_primaries[2][0], chroma_den)); - avio_wb16(pb, rescale_mdcv(metadata->display_primaries[2][1], chroma_den)); - avio_wb16(pb, rescale_mdcv(metadata->display_primaries[0][0], chroma_den)); - avio_wb16(pb, rescale_mdcv(metadata->display_primaries[0][1], chroma_den)); - avio_wb16(pb, rescale_mdcv(metadata->white_point[0], chroma_den)); - avio_wb16(pb, rescale_mdcv(metadata->white_point[1], chroma_den)); - avio_wb32(pb, rescale_mdcv(metadata->max_luminance, luma_den)); - avio_wb32(pb, rescale_mdcv(metadata->min_luminance, luma_den)); + avio_wb16(pb, rescale_rational(metadata->display_primaries[1][0], chroma_den)); + avio_wb16(pb, rescale_rational(metadata->display_primaries[1][1], chroma_den)); + avio_wb16(pb, rescale_rational(metadata->display_primaries[2][0], chroma_den)); + avio_wb16(pb, rescale_rational(metadata->display_primaries[2][1], chroma_den)); + avio_wb16(pb, rescale_rational(metadata->display_primaries[0][0], chroma_den)); + avio_wb16(pb, rescale_rational(metadata->display_primaries[0][1], chroma_den)); + avio_wb16(pb, rescale_rational(metadata->white_point[0], chroma_den)); + avio_wb16(pb, rescale_rational(metadata->white_point[1], chroma_den)); + avio_wb32(pb, rescale_rational(metadata->max_luminance, luma_den)); + avio_wb32(pb, rescale_rational(metadata->min_luminance, luma_den)); return 32; } +static int mov_write_amve_tag(AVIOContext *pb, MOVTrack *track) +{ + const int illuminance_den = 10000; + const int ambient_den = 50000; + const AVPacketSideData *side_data; + const AVAmbientViewingEnvironment *ambient; + + + side_data = av_packet_side_data_get(track->st->codecpar->coded_side_data, + track->st->codecpar->nb_coded_side_data, + AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT); + + if (!side_data) + return 0; + + ambient = (const AVAmbientViewingEnvironment*)side_data->data; + if (!ambient || !ambient->ambient_illuminance.num) + return 0; + + avio_wb32(pb, 16); // size + ffio_wfourcc(pb, "amve"); + avio_wb32(pb, rescale_rational(ambient->ambient_illuminance, illuminance_den)); + avio_wb16(pb, rescale_rational(ambient->ambient_light_x, ambient_den)); + avio_wb16(pb, rescale_rational(ambient->ambient_light_y, ambient_den)); + return 16; +} + static void find_compressor(char * compressor_name, int len, MOVTrack *track) { AVDictionaryEntry *encoder; @@ -2280,10 +2463,15 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex avid = 1; } else if (track->par->codec_id == AV_CODEC_ID_HEVC) mov_write_hvcc_tag(pb, track); + else if (track->par->codec_id == AV_CODEC_ID_VVC) + mov_write_vvcc_tag(pb, track); else if (track->par->codec_id == AV_CODEC_ID_H264 && !TAG_IS_AVCI(track->tag)) { mov_write_avcc_tag(pb, track); if (track->mode == MODE_IPOD) mov_write_uuid_tag_ipod(pb); + } + else if (track->par->codec_id ==AV_CODEC_ID_EVC) { + mov_write_evcc_tag(pb, track); } else if (track->par->codec_id == AV_CODEC_ID_VP9) { mov_write_vpcc_tag(mov->fc, pb, track); } else if (track->par->codec_id == AV_CODEC_ID_AV1) { @@ -2320,7 +2508,8 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex track->par->color_trc != AVCOL_TRC_UNSPECIFIED && track->par->color_space != AVCOL_SPC_UNSPECIFIED; if (has_color_info || mov->flags & FF_MOV_FLAG_WRITE_COLR || - av_stream_get_side_data(track->st, AV_PKT_DATA_ICC_PROFILE, NULL)) { + av_packet_side_data_get(track->st->codecpar->coded_side_data, track->st->codecpar->nb_coded_side_data, + AV_PKT_DATA_ICC_PROFILE)) { int prefer_icc = mov->flags & FF_MOV_FLAG_PREFER_ICC || !has_color_info; mov_write_colr_tag(pb, track, prefer_icc); } @@ -2331,20 +2520,26 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex if (track->mode == MODE_MOV || track->mode == MODE_MP4) { mov_write_clli_tag(pb, track); mov_write_mdcv_tag(pb, track); + mov_write_amve_tag(pb, track); } if (track->mode == MODE_MP4 && mov->fc->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL) { - AVStereo3D* stereo_3d = (AVStereo3D*) av_stream_get_side_data(track->st, AV_PKT_DATA_STEREO3D, NULL); - AVSphericalMapping* spherical_mapping = (AVSphericalMapping*)av_stream_get_side_data(track->st, AV_PKT_DATA_SPHERICAL, NULL); - AVDOVIDecoderConfigurationRecord *dovi = (AVDOVIDecoderConfigurationRecord *) - av_stream_get_side_data(track->st, AV_PKT_DATA_DOVI_CONF, NULL); + const AVPacketSideData *stereo_3d = av_packet_side_data_get(track->st->codecpar->coded_side_data, + track->st->codecpar->nb_coded_side_data, + AV_PKT_DATA_STEREO3D); + const AVPacketSideData *spherical_mapping = av_packet_side_data_get(track->st->codecpar->coded_side_data, + track->st->codecpar->nb_coded_side_data, + AV_PKT_DATA_SPHERICAL); + const AVPacketSideData *dovi = av_packet_side_data_get(track->st->codecpar->coded_side_data, + track->st->codecpar->nb_coded_side_data, + AV_PKT_DATA_DOVI_CONF); if (stereo_3d) - mov_write_st3d_tag(s, pb, stereo_3d); + mov_write_st3d_tag(s, pb, (AVStereo3D*)stereo_3d->data); if (spherical_mapping) - mov_write_sv3d_tag(mov->fc, pb, spherical_mapping); + mov_write_sv3d_tag(mov->fc, pb, (AVSphericalMapping*)spherical_mapping->data); if (dovi) - mov_write_dvcc_dvvc_tag(s, pb, dovi); + mov_write_dvcc_dvvc_tag(s, pb, (AVDOVIDecoderConfigurationRecord *)dovi->data); } if (track->par->sample_aspect_ratio.den && track->par->sample_aspect_ratio.num) { @@ -2370,7 +2565,7 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex if (track->mode == MODE_AVIF) { mov_write_ccst_tag(pb); - if (s->nb_streams > 0 && track == &mov->tracks[1]) + if (mov->nb_streams > 0 && track == &mov->tracks[1]) mov_write_aux_tag(pb, "auxi"); } @@ -2807,18 +3002,18 @@ static int mov_write_gmhd_tag(AVIOContext *pb, MOVTrack *track) * bytes are copied verbatim. */ if (track->tag != MKTAG('c','6','0','8')) { - avio_wb32(pb, 0x2C); /* size */ - ffio_wfourcc(pb, "text"); - avio_wb16(pb, 0x01); - avio_wb32(pb, 0x00); - avio_wb32(pb, 0x00); - avio_wb32(pb, 0x00); - avio_wb32(pb, 0x01); - avio_wb32(pb, 0x00); - avio_wb32(pb, 0x00); - avio_wb32(pb, 0x00); - avio_wb32(pb, 0x00004000); - avio_wb16(pb, 0x0000); + avio_wb32(pb, 0x2C); /* size */ + ffio_wfourcc(pb, "text"); + avio_wb16(pb, 0x01); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x01); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00); + avio_wb32(pb, 0x00004000); + avio_wb16(pb, 0x0000); } if (track->par->codec_tag == MKTAG('t','m','c','d')) { @@ -2965,9 +3160,9 @@ static int mov_write_iloc_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte avio_wb32(pb, 0); /* Version & flags */ avio_w8(pb, (4 << 4) + 4); /* offset_size(4) and length_size(4) */ avio_w8(pb, 0); /* base_offset_size(4) and reserved(4) */ - avio_wb16(pb, s->nb_streams); /* item_count */ + avio_wb16(pb, mov->nb_streams); /* item_count */ - for (int i = 0; i < s->nb_streams; i++) { + for (int i = 0; i < mov->nb_streams; i++) { avio_wb16(pb, i + 1); /* item_id */ avio_wb16(pb, 0); /* data_reference_index */ avio_wb16(pb, 1); /* extent_count */ @@ -2986,9 +3181,9 @@ static int mov_write_iinf_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "iinf"); avio_wb32(pb, 0); /* Version & flags */ - avio_wb16(pb, s->nb_streams); /* entry_count */ + avio_wb16(pb, mov->nb_streams); /* entry_count */ - for (int i = 0; i < s->nb_streams; i++) { + for (int i = 0; i < mov->nb_streams; i++) { int64_t infe_pos = avio_tell(pb); avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "infe"); @@ -3057,7 +3252,7 @@ static int mov_write_ipco_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte int64_t pos = avio_tell(pb); avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "ipco"); - for (int i = 0; i < s->nb_streams; i++) { + for (int i = 0; i < mov->nb_streams; i++) { mov_write_ispe_tag(pb, mov, s, i); mov_write_pixi_tag(pb, mov, s, i); mov_write_av1c_tag(pb, &mov->tracks[i]); @@ -3075,9 +3270,9 @@ static int mov_write_ipma_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "ipma"); avio_wb32(pb, 0); /* Version & flags */ - avio_wb32(pb, s->nb_streams); /* entry_count */ + avio_wb32(pb, mov->nb_streams); /* entry_count */ - for (int i = 0, index = 1; i < s->nb_streams; i++) { + for (int i = 0, index = 1; i < mov->nb_streams; i++) { avio_wb16(pb, i + 1); /* item_ID */ avio_w8(pb, 4); /* association_count */ @@ -3201,14 +3396,20 @@ static int64_t calc_pts_duration(MOVMuxContext *mov, MOVTrack *track) return end - start; } +static int mov_mdhd_mvhd_tkhd_version(MOVMuxContext *mov, MOVTrack *track, int64_t duration) +{ + if (track && track->mode == MODE_ISM) + return 1; + if (duration < INT32_MAX) + return 0; + return 1; +} + static int mov_write_mdhd_tag(AVIOContext *pb, MOVMuxContext *mov, MOVTrack *track) { int64_t duration = calc_samples_pts_duration(mov, track); - int version = duration < INT32_MAX ? 0 : 1; - - if (track->mode == MODE_ISM) - version = 1; + int version = mov_mdhd_mvhd_tkhd_version(mov, track, duration); (version == 1) ? avio_wb32(pb, 44) : avio_wb32(pb, 32); /* size */ ffio_wfourcc(pb, "mdhd"); @@ -3285,7 +3486,6 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, int group = 0; uint32_t *display_matrix = NULL; - size_t display_matrix_size; int i; if (mov->mode == MODE_AVIF) @@ -3294,25 +3494,24 @@ static int mov_write_tkhd_tag(AVIOContext *pb, MOVMuxContext *mov, else duration *= mov->avif_loop_count; - version = duration < INT32_MAX ? 0 : 1; - if (st) { + const AVPacketSideData *sd; if (mov->per_stream_grouping) group = st->index; else group = st->codecpar->codec_type; - display_matrix = (uint32_t*)av_stream_get_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, - &display_matrix_size); - if (display_matrix && display_matrix_size < 9 * sizeof(*display_matrix)) - display_matrix = NULL; + sd = av_packet_side_data_get(st->codecpar->coded_side_data, + st->codecpar->nb_coded_side_data, + AV_PKT_DATA_DISPLAYMATRIX); + if (sd && sd->size == 9 * sizeof(*display_matrix)) + display_matrix = (uint32_t *)sd->data; } if (track->flags & MOV_TRACK_ENABLED) flags |= MOV_TKHD_FLAG_ENABLED; - if (track->mode == MODE_ISM) - version = 1; + version = mov_mdhd_mvhd_tkhd_version(mov, track, duration); (version == 1) ? avio_wb32(pb, 104) : avio_wb32(pb, 92); /* size */ ffio_wfourcc(pb, "tkhd"); @@ -3721,7 +3920,7 @@ static int mov_write_iods_tag(AVIOContext *pb, MOVMuxContext *mov) int64_t pos = avio_tell(pb); int audio_profile = mov->iods_audio_profile; int video_profile = mov->iods_video_profile; - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { if (mov->tracks[i].entry > 0 || mov->flags & FF_MOV_FLAG_EMPTY_MOOV) { has_audio |= mov->tracks[i].par->codec_type == AVMEDIA_TYPE_AUDIO; has_video |= mov->tracks[i].par->codec_type == AVMEDIA_TYPE_VIDEO; @@ -3763,7 +3962,7 @@ static int mov_write_mvex_tag(AVIOContext *pb, MOVMuxContext *mov) int i; avio_wb32(pb, 0x0); /* size */ ffio_wfourcc(pb, "mvex"); - for (i = 0; i < mov->nb_streams; i++) + for (i = 0; i < mov->nb_tracks; i++) mov_write_trex_tag(pb, &mov->tracks[i]); return update_size(pb, pos); } @@ -3775,7 +3974,7 @@ static int mov_write_mvhd_tag(AVIOContext *pb, MOVMuxContext *mov) int version; int timescale; - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { if (mov->tracks[i].entry > 0 && mov->tracks[i].timescale) { int64_t max_track_len_temp = av_rescale_rnd( calc_pts_duration(mov, &mov->tracks[i]), @@ -3795,7 +3994,7 @@ static int mov_write_mvhd_tag(AVIOContext *pb, MOVMuxContext *mov) max_track_id = 1; } - version = max_track_len < UINT32_MAX ? 0 : 1; + version = mov_mdhd_mvhd_tkhd_version(mov, NULL, max_track_len); avio_wb32(pb, version == 1 ? 120 : 108); /* size */ ffio_wfourcc(pb, "mvhd"); @@ -3953,13 +4152,13 @@ static int mov_write_loci_tag(AVFormatContext *s, AVIOContext *pb) return 0; ptr = t->value; - longitude = strtod(ptr, &end); + latitude = strtod(ptr, &end); if (end == ptr) { av_log(s, AV_LOG_WARNING, "malformed location metadata\n"); return 0; } ptr = end; - latitude = strtod(ptr, &end); + longitude = strtod(ptr, &end); if (end == ptr) { av_log(s, AV_LOG_WARNING, "malformed location metadata\n"); return 0; @@ -3980,8 +4179,8 @@ static int mov_write_loci_tag(AVFormatContext *s, AVIOContext *pb) avio_wb16(pb, lang); avio_write(pb, place, strlen(place) + 1); avio_w8(pb, 0); /* role of place (0 == shooting location, 1 == real location, 2 == fictional location) */ - avio_wb32(pb, latitude_fix); avio_wb32(pb, longitude_fix); + avio_wb32(pb, latitude_fix); avio_wb32(pb, altitude_fix); avio_write(pb, astronomical_body, strlen(astronomical_body) + 1); avio_w8(pb, 0); /* additional notes, null terminated string */ @@ -4048,9 +4247,8 @@ static int mov_write_covr(AVIOContext *pb, AVFormatContext *s) { MOVMuxContext *mov = s->priv_data; int64_t pos = 0; - int i; - for (i = 0; i < s->nb_streams; i++) { + for (int i = 0; i < mov->nb_streams; i++) { MOVTrack *trk = &mov->tracks[i]; if (!is_cover_image(trk->st) || trk->cover_image->size <= 0) @@ -4197,7 +4395,7 @@ static int mov_write_meta_tag(AVIOContext *pb, MOVMuxContext *mov, mov_write_pitm_tag(pb, 1); mov_write_iloc_tag(pb, mov, s); mov_write_iinf_tag(pb, mov, s); - if (s->nb_streams > 1) + if (mov->nb_streams > 1) mov_write_iref_tag(pb, mov, s); mov_write_iprp_tag(pb, mov, s); } else { @@ -4448,19 +4646,20 @@ static int mov_setup_track_ids(MOVMuxContext *mov, AVFormatContext *s) if (mov->use_stream_ids_as_track_ids) { int next_generated_track_id = 0; - for (i = 0; i < s->nb_streams; i++) { - if (s->streams[i]->id > next_generated_track_id) - next_generated_track_id = s->streams[i]->id; + for (i = 0; i < mov->nb_streams; i++) { + AVStream *st = mov->tracks[i].st; + if (st->id > next_generated_track_id) + next_generated_track_id = st->id; } - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { if (mov->tracks[i].entry <= 0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT)) continue; - mov->tracks[i].track_id = i >= s->nb_streams ? ++next_generated_track_id : s->streams[i]->id; + mov->tracks[i].track_id = i >= mov->nb_streams ? ++next_generated_track_id : mov->tracks[i].st->id; } } else { - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { if (mov->tracks[i].entry <= 0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT)) continue; @@ -4483,7 +4682,7 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, mov_setup_track_ids(mov, s); - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { if (mov->tracks[i].entry <= 0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT)) continue; @@ -4494,30 +4693,29 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, } if (mov->chapter_track) - for (i = 0; i < s->nb_streams; i++) { + for (i = 0; i < mov->nb_streams; i++) { mov->tracks[i].tref_tag = MKTAG('c','h','a','p'); mov->tracks[i].tref_id = mov->tracks[mov->chapter_track].track_id; } - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { MOVTrack *track = &mov->tracks[i]; if (track->tag == MKTAG('r','t','p',' ')) { track->tref_tag = MKTAG('h','i','n','t'); track->tref_id = mov->tracks[track->src_track].track_id; } else if (track->par->codec_type == AVMEDIA_TYPE_AUDIO) { - size_t size; - int *fallback; - fallback = (int*)av_stream_get_side_data(track->st, - AV_PKT_DATA_FALLBACK_TRACK, - &size); - if (fallback != NULL && size == sizeof(int)) { - if (*fallback >= 0 && *fallback < mov->nb_streams) { + const AVPacketSideData *sd = av_packet_side_data_get(track->st->codecpar->coded_side_data, + track->st->codecpar->nb_coded_side_data, + AV_PKT_DATA_FALLBACK_TRACK ); + if (sd && sd->size == sizeof(int)) { + int *fallback = (int *)sd->data; + if (*fallback >= 0 && *fallback < mov->nb_tracks) { track->tref_tag = MKTAG('f','a','l','l'); track->tref_id = mov->tracks[*fallback].track_id; } } } } - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { if (mov->tracks[i].tag == MKTAG('t','m','c','d')) { int src_trk = mov->tracks[i].src_track; mov->tracks[src_trk].tref_tag = mov->tracks[i].tag; @@ -4532,10 +4730,10 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov, mov_write_mvhd_tag(pb, mov); if (mov->mode != MODE_MOV && mov->mode != MODE_AVIF && !mov->iods_skip) mov_write_iods_tag(pb, mov); - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { if (mov->tracks[i].entry > 0 || mov->flags & FF_MOV_FLAG_FRAGMENT || mov->mode == MODE_AVIF) { - int ret = mov_write_trak_tag(s, pb, mov, &(mov->tracks[i]), i < s->nb_streams ? s->streams[i] : NULL); + int ret = mov_write_trak_tag(s, pb, mov, &(mov->tracks[i]), i < mov->nb_streams ? mov->tracks[i].st : NULL); if (ret < 0) return ret; } @@ -4596,7 +4794,7 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov, AVFormat mov_setup_track_ids(mov, s); - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { MOVTrack *track = &mov->tracks[i]; struct mpeg4_bit_rate_values bit_rates = calculate_mpeg4_bit_rates(track); @@ -4660,16 +4858,15 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov, AVFormat param_write_int(pb, "DisplayHeight", track->par->height); } else { if (track->par->codec_id == AV_CODEC_ID_AAC) { - switch (track->par->profile) - { - case FF_PROFILE_AAC_HE_V2: - param_write_string(pb, "FourCC", "AACP"); - break; - case FF_PROFILE_AAC_HE: - param_write_string(pb, "FourCC", "AACH"); - break; - default: - param_write_string(pb, "FourCC", "AACL"); + switch (track->par->profile) { + case AV_PROFILE_AAC_HE_V2: + param_write_string(pb, "FourCC", "AACP"); + break; + case AV_PROFILE_AAC_HE: + param_write_string(pb, "FourCC", "AACH"); + break; + default: + param_write_string(pb, "FourCC", "AACL"); } } else if (track->par->codec_id == AV_CODEC_ID_WMAPRO) { param_write_string(pb, "FourCC", "WMAP"); @@ -4679,7 +4876,8 @@ static int mov_write_isml_manifest(AVIOContext *pb, MOVMuxContext *mov, AVFormat param_write_int(pb, "AudioTag", ff_codec_get_tag(ff_codec_wav_tags, track->par->codec_id)); param_write_int(pb, "Channels", track->par->ch_layout.nb_channels); - param_write_int(pb, "SamplingRate", track->par->sample_rate); + param_write_int(pb, "SamplingRate", track->tag == MKTAG('i','a','m','f') ? + 0 : track->par->sample_rate); param_write_int(pb, "BitsPerSample", 16); param_write_int(pb, "PacketSize", track->par->block_align ? track->par->block_align : 4); @@ -4790,8 +4988,8 @@ static int mov_write_trun_tag(AVIOContext *pb, MOVMuxContext *mov, if (i > first && get_sample_flags(track, &track->cluster[i]) != track->default_sample_flags) flags |= MOV_TRUN_SAMPLE_FLAGS; } - if (!(flags & MOV_TRUN_SAMPLE_FLAGS) && track->entry > 0 && - get_sample_flags(track, &track->cluster[0]) != track->default_sample_flags) + if (!(flags & MOV_TRUN_SAMPLE_FLAGS) && track->entry > first && + get_sample_flags(track, &track->cluster[first]) != track->default_sample_flags) flags |= MOV_TRUN_FIRST_SAMPLE_FLAGS; if (track->flags & MOV_TRACK_CTTS) flags |= MOV_TRUN_SAMPLE_CTS; @@ -4903,7 +5101,7 @@ static int mov_add_tfra_entries(AVIOContext *pb, MOVMuxContext *mov, int tracks, int size) { int i; - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { MOVTrack *track = &mov->tracks[i]; MOVFragmentInfo *info; if ((tracks >= 0 && i != tracks) || !track->entry) @@ -4941,7 +5139,7 @@ static int mov_add_tfra_entries(AVIOContext *pb, MOVMuxContext *mov, int tracks, static void mov_prune_frag_info(MOVMuxContext *mov, int tracks, int max) { int i; - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { MOVTrack *track = &mov->tracks[i]; if ((tracks >= 0 && i != tracks) || !track->entry) continue; @@ -5014,7 +5212,7 @@ static int mov_write_moof_tag_internal(AVIOContext *pb, MOVMuxContext *mov, mov->first_trun = 1; mov_write_mfhd_tag(pb, mov); - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { MOVTrack *track = &mov->tracks[i]; if (tracks >= 0 && i != tracks) continue; @@ -5108,7 +5306,7 @@ static int mov_write_sidx_tags(AVIOContext *pb, MOVMuxContext *mov, } else { avio_buf = pb; } - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { MOVTrack *track = &mov->tracks[i]; if (tracks >= 0 && i != tracks) continue; @@ -5249,7 +5447,7 @@ static int mov_write_mfra_tag(AVIOContext *pb, MOVMuxContext *mov) if (mov->flags & FF_MOV_FLAG_ISML) goto done_mfra; - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { MOVTrack *track = &mov->tracks[i]; if (track->nb_frag_info) mov_write_tfra_tag(mfra_pb, track); @@ -5327,10 +5525,19 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) MOVMuxContext *mov = s->priv_data; int64_t pos = avio_tell(pb); int has_h264 = 0, has_av1 = 0, has_video = 0, has_dolby = 0; - int i; + int has_iamf = 0; - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; + for (int i = 0; i < s->nb_stream_groups; i++) { + const AVStreamGroup *stg = s->stream_groups[i]; + + if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT || + stg->type == AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION) { + has_iamf = 1; + break; + } + } + for (int i = 0; i < mov->nb_streams; i++) { + AVStream *st = mov->tracks[i].st; if (is_cover_image(st)) continue; if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) @@ -5342,7 +5549,9 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) if (st->codecpar->codec_id == AV_CODEC_ID_AC3 || st->codecpar->codec_id == AV_CODEC_ID_EAC3 || st->codecpar->codec_id == AV_CODEC_ID_TRUEHD || - av_stream_get_side_data(st, AV_PKT_DATA_DOVI_CONF, NULL)) + av_packet_side_data_get(st->codecpar->coded_side_data, + st->codecpar->nb_coded_side_data, + AV_PKT_DATA_DOVI_CONF)) has_dolby = 1; } @@ -5396,6 +5605,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s) ffio_wfourcc(pb, "av01"); if (has_dolby) ffio_wfourcc(pb, "dby1"); + if (has_iamf) + ffio_wfourcc(pb, "iamf"); } else { if (mov->flags & FF_MOV_FLAG_FRAGMENT) ffio_wfourcc(pb, "iso6"); @@ -5503,8 +5714,8 @@ static int mov_write_identification(AVIOContext *pb, AVFormatContext *s) mov_write_ftyp_tag(pb,s); if (mov->mode == MODE_PSP) { int video_streams_nb = 0, audio_streams_nb = 0, other_streams_nb = 0; - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; + for (i = 0; i < mov->nb_streams; i++) { + AVStream *st = mov->tracks[i].st; if (is_cover_image(st)) continue; if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) @@ -5691,7 +5902,7 @@ static int mov_write_squashed_packets(AVFormatContext *s) { MOVMuxContext *mov = s->priv_data; - for (int i = 0; i < s->nb_streams; i++) { + for (int i = 0; i < mov->nb_streams; i++) { MOVTrack *track = &mov->tracks[i]; int ret = AVERROR_BUG; @@ -5732,7 +5943,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) // of fragments was triggered automatically by an AVPacket, we // already have reliable info for the end of that track, but other // tracks may need to be filled in. - for (i = 0; i < s->nb_streams; i++) { + for (i = 0; i < mov->nb_streams; i++) { MOVTrack *track = &mov->tracks[i]; if (!track->end_reliable) { const AVPacket *pkt = ff_interleaved_peek(s, i); @@ -5752,7 +5963,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) } } - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { MOVTrack *track = &mov->tracks[i]; if (track->entry <= 1) continue; @@ -5784,15 +5995,15 @@ static int mov_flush_fragment(AVFormatContext *s, int force) uint8_t *buf; int buf_size, moov_size; - for (i = 0; i < mov->nb_streams; i++) + for (i = 0; i < mov->nb_tracks; i++) if (!mov->tracks[i].entry && !is_cover_image(mov->tracks[i].st)) break; /* Don't write the initial moov unless all tracks have data */ - if (i < mov->nb_streams && !force) + if (i < mov->nb_tracks && !force) return 0; moov_size = get_moov_size(s); - for (i = 0; i < mov->nb_streams; i++) + for (i = 0; i < mov->nb_tracks; i++) mov->tracks[i].data_offset = pos + moov_size + 8; avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_HEADER); @@ -5820,7 +6031,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) mov->moov_written = 1; mov->mdat_size = 0; - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { mov->tracks[i].entry = 0; mov->tracks[i].end_reliable = 0; } @@ -5829,7 +6040,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) } if (mov->frag_interleave) { - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { MOVTrack *track = &mov->tracks[i]; int ret; if ((ret = mov_flush_fragment_interleaving(s, track)) < 0) @@ -5841,7 +6052,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) mdat_size = avio_tell(mov->mdat_buf); } - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { MOVTrack *track = &mov->tracks[i]; if (mov->flags & FF_MOV_FLAG_SEPARATE_MOOF || mov->frag_interleave) track->data_offset = 0; @@ -5870,7 +6081,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force) av_rescale(mov->tracks[first_track].cluster[0].dts, AV_TIME_BASE, mov->tracks[first_track].timescale), (has_video ? starts_with_key : mov->tracks[first_track].cluster[0].flags & MOV_SYNC_SAMPLE) ? AVIO_DATA_MARKER_SYNC_POINT : AVIO_DATA_MARKER_BOUNDARY_POINT); - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { MOVTrack *track = &mov->tracks[i]; int buf_size, write_moof = 1, moof_tracks = -1; uint8_t *buf; @@ -5933,10 +6144,8 @@ static int mov_auto_flush_fragment(AVFormatContext *s, int force) return ret; } -static int check_pkt(AVFormatContext *s, AVPacket *pkt) +static int check_pkt(AVFormatContext *s, MOVTrack *trk, AVPacket *pkt) { - MOVMuxContext *mov = s->priv_data; - MOVTrack *trk = &mov->tracks[pkt->stream_index]; int64_t ref; uint64_t duration; @@ -5974,18 +6183,30 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) { MOVMuxContext *mov = s->priv_data; AVIOContext *pb = s->pb; - MOVTrack *trk = &mov->tracks[pkt->stream_index]; - AVCodecParameters *par = trk->par; + MOVTrack *trk; + AVCodecParameters *par; AVProducerReferenceTime *prft; unsigned int samples_in_chunk = 0; int size = pkt->size, ret = 0, offset = 0; size_t prft_size; uint8_t *reformatted_data = NULL; - ret = check_pkt(s, pkt); + if (pkt->stream_index < s->nb_streams) + trk = s->streams[pkt->stream_index]->priv_data; + else // Timecode or chapter + trk = &mov->tracks[pkt->stream_index]; + par = trk->par; + + ret = check_pkt(s, trk, pkt); if (ret < 0) return ret; + if (pkt->pts != AV_NOPTS_VALUE && + (uint64_t)pkt->dts - pkt->pts != (int32_t)((uint64_t)pkt->dts - pkt->pts)) { + av_log(s, AV_LOG_WARNING, "pts/dts pair unsupported\n"); + return AVERROR_PATCHWELCOME; + } + if (mov->flags & FF_MOV_FLAG_FRAGMENT || mov->mode == MODE_AVIF) { int ret; if (mov->moov_written || mov->flags & FF_MOV_FLAG_EMPTY_MOOV) { @@ -6054,7 +6275,9 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) if ((par->codec_id == AV_CODEC_ID_DNXHD || par->codec_id == AV_CODEC_ID_H264 || par->codec_id == AV_CODEC_ID_HEVC || + par->codec_id == AV_CODEC_ID_VVC || par->codec_id == AV_CODEC_ID_VP9 || + par->codec_id == AV_CODEC_ID_EVC || par->codec_id == AV_CODEC_ID_TRUEHD) && !trk->vos_len && !TAG_IS_AVCI(trk->tag)) { /* copy frame to create needed atoms */ @@ -6070,7 +6293,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 && (AV_RB16(pkt->data) & 0xfff0) == 0xfff0) { - if (!s->streams[pkt->stream_index]->nb_frames) { + if (!trk->st->nb_frames) { av_log(s, AV_LOG_ERROR, "Malformed AAC bitstream detected: " "use the audio bitstream filter 'aac_adtstoasc' to fix it " "('-bsf:a aac_adtstoasc' option with ffmpeg)\n"); @@ -6081,7 +6304,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) if (par->codec_id == AV_CODEC_ID_H264 && trk->vos_len > 0 && *(uint8_t *)trk->vos_data != 1 && !TAG_IS_AVCI(trk->tag)) { /* from x264 or from bytestream H.264 */ /* NAL reformatting needed */ - if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) { + if (trk->hint_track >= 0 && trk->hint_track < mov->nb_tracks) { ret = ff_avc_parse_nal_units_buf(pkt->data, &reformatted_data, &size); if (ret < 0) @@ -6101,7 +6324,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) } else if (par->codec_id == AV_CODEC_ID_HEVC && trk->vos_len > 6 && (AV_RB24(trk->vos_data) == 1 || AV_RB32(trk->vos_data) == 1)) { /* extradata is Annex B, assume the bitstream is too and convert it */ - if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) { + if (trk->hint_track >= 0 && trk->hint_track < mov->nb_tracks) { ret = ff_hevc_annexb2mp4_buf(pkt->data, &reformatted_data, &size, 0, NULL); if (ret < 0) @@ -6118,8 +6341,20 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) size = ff_hevc_annexb2mp4(pb, pkt->data, pkt->size, 0, NULL); } } + } else if (par->codec_id == AV_CODEC_ID_VVC && trk->vos_len > 6 && + (AV_RB24(trk->vos_data) == 1 || AV_RB32(trk->vos_data) == 1)) { + /* extradata is Annex B, assume the bitstream is too and convert it */ + if (trk->hint_track >= 0 && trk->hint_track < mov->nb_tracks) { + ret = ff_vvc_annexb2mp4_buf(pkt->data, &reformatted_data, + &size, 0, NULL); + if (ret < 0) + return ret; + avio_write(pb, reformatted_data, size); + } else { + size = ff_vvc_annexb2mp4(pb, pkt->data, pkt->size, 0, NULL); + } } else if (par->codec_id == AV_CODEC_ID_AV1) { - if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) { + if (trk->hint_track >= 0 && trk->hint_track < mov->nb_tracks) { ret = ff_av1_filter_obus_buf(pkt->data, &reformatted_data, &size, &offset); if (ret < 0) @@ -6164,6 +6399,8 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) } else if(par->codec_id == AV_CODEC_ID_HEVC && par->extradata_size > 21) { int nal_size_length = (par->extradata[21] & 0x3) + 1; ret = ff_mov_cenc_avc_write_nal_units(s, &trk->cenc, nal_size_length, pb, pkt->data, size); + } else if(par->codec_id == AV_CODEC_ID_VVC) { + ret = AVERROR_PATCHWELCOME; } else { ret = ff_mov_cenc_write_packet(&trk->cenc, pb, pkt->data, size); } @@ -6302,7 +6539,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) trk->sample_count += samples_in_chunk; mov->mdat_size += size; - if (trk->hint_track >= 0 && trk->hint_track < mov->nb_streams) + if (trk->hint_track >= 0 && trk->hint_track < mov->nb_tracks) ff_mov_add_hinted_packet(s, pkt, trk->hint_track, trk->entry, reformatted_data ? reformatted_data + offset : NULL, size); @@ -6318,18 +6555,17 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt) static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt) { MOVMuxContext *mov = s->priv_data; - MOVTrack *trk = &mov->tracks[pkt->stream_index]; + MOVTrack *trk = s->streams[pkt->stream_index]->priv_data; AVCodecParameters *par = trk->par; int64_t frag_duration = 0; int size = pkt->size; - int ret = check_pkt(s, pkt); + int ret = check_pkt(s, trk, pkt); if (ret < 0) return ret; if (mov->flags & FF_MOV_FLAG_FRAG_DISCONT) { - int i; - for (i = 0; i < s->nb_streams; i++) + for (int i = 0; i < mov->nb_streams; i++) mov->tracks[i].frag_discont = 1; mov->flags &= ~FF_MOV_FLAG_FRAG_DISCONT; } @@ -6371,7 +6607,7 @@ static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt) return 0; /* Discard 0 sized packets */ } - if (trk->entry && pkt->stream_index < s->nb_streams) + if (trk->entry && pkt->stream_index < mov->nb_streams) frag_duration = av_rescale_q(pkt->dts - trk->cluster[0].dts, s->streams[pkt->stream_index]->time_base, AV_TIME_BASE_Q); @@ -6426,6 +6662,57 @@ static int mov_write_subtitle_end_packet(AVFormatContext *s, return ret; } +static int mov_build_iamf_packet(AVFormatContext *s, MOVTrack *trk, AVPacket *pkt) +{ + int ret; + + if (pkt->stream_index == trk->first_iamf_idx) { + ret = ff_iamf_write_parameter_blocks(trk->iamf, trk->iamf_buf, pkt, s); + if (ret < 0) + return ret; + } + + ret = ff_iamf_write_audio_frame(trk->iamf, trk->iamf_buf, + s->streams[pkt->stream_index]->id, pkt); + if (ret < 0) + return ret; + + if (pkt->stream_index == trk->last_iamf_idx) { + uint8_t *data; + + ret = avio_close_dyn_buf(trk->iamf_buf, &data); + trk->iamf_buf = NULL; + + if (!ret) { + if (pkt->size) { + // Either all or none of the packets for a single + // IA Sample may be empty. + av_log(s, AV_LOG_ERROR, "Unexpected packet from " + "stream #%d\n", pkt->stream_index); + ret = AVERROR_INVALIDDATA; + } + av_free(data); + return ret; + } + av_buffer_unref(&pkt->buf); + pkt->buf = av_buffer_create(data, ret, NULL, NULL, 0); + if (!pkt->buf) { + av_free(data); + return AVERROR(ENOMEM); + } + pkt->data = data; + pkt->size = ret; + pkt->stream_index = trk->first_iamf_idx; + + ret = avio_open_dyn_buf(&trk->iamf_buf); + if (ret < 0) + return ret; + } else + ret = AVERROR(EAGAIN); + + return ret; +} + static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) { MOVMuxContext *mov = s->priv_data; @@ -6436,7 +6723,18 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) return 1; } - trk = &mov->tracks[pkt->stream_index]; + trk = s->streams[pkt->stream_index]->priv_data; + + if (trk->iamf) { + int ret = mov_build_iamf_packet(s, trk, pkt); + if (ret < 0) { + if (ret == AVERROR(EAGAIN)) + return 0; + av_log(s, AV_LOG_ERROR, "Error assembling an IAMF packet " + "for stream #%d\n", trk->st->index); + return ret; + } + } if (is_cover_image(trk->st)) { int ret; @@ -6477,7 +6775,7 @@ static int mov_write_packet(AVFormatContext *s, AVPacket *pkt) * 3) See mov_write_trailer for how the final end sample is * handled. */ - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { MOVTrack *trk = &mov->tracks[i]; int ret; @@ -6637,12 +6935,12 @@ static int mov_create_chapter_track(AVFormatContext *s, int tracknum) } -static int mov_check_timecode_track(AVFormatContext *s, AVTimecode *tc, int src_index, const char *tcstr) +static int mov_check_timecode_track(AVFormatContext *s, AVTimecode *tc, AVStream *src_st, const char *tcstr) { int ret; /* compute the frame number */ - ret = av_timecode_init_from_string(tc, s->streams[src_index]->avg_frame_rate, tcstr, s); + ret = av_timecode_init_from_string(tc, src_st->avg_frame_rate, tcstr, s); return ret; } @@ -6650,7 +6948,7 @@ static int mov_create_timecode_track(AVFormatContext *s, int index, int src_inde { MOVMuxContext *mov = s->priv_data; MOVTrack *track = &mov->tracks[index]; - AVStream *src_st = s->streams[src_index]; + AVStream *src_st = mov->tracks[src_index].st; uint8_t data[4]; AVPacket *pkt = mov->pkt; AVRational rate = src_st->avg_frame_rate; @@ -6710,8 +7008,8 @@ static void enable_tracks(AVFormatContext *s) first[i] = -1; } - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; + for (i = 0; i < mov->nb_streams; i++) { + AVStream *st = mov->tracks[i].st; if (st->codecpar->codec_type <= AVMEDIA_TYPE_UNKNOWN || st->codecpar->codec_type >= AVMEDIA_TYPE_NB || @@ -6743,7 +7041,9 @@ static void enable_tracks(AVFormatContext *s) static void mov_free(AVFormatContext *s) { MOVMuxContext *mov = s->priv_data; - int i; + + for (int i = 0; i < s->nb_streams; i++) + s->streams[i]->priv_data = NULL; if (!mov->tracks) return; @@ -6752,7 +7052,7 @@ static void mov_free(AVFormatContext *s) avcodec_parameters_free(&mov->tracks[mov->chapter_track].par); } - for (i = 0; i < mov->nb_streams; i++) { + for (int i = 0; i < mov->nb_tracks; i++) { MOVTrack *const track = &mov->tracks[i]; if (track->tag == MKTAG('r','t','p',' ')) @@ -6774,6 +7074,11 @@ static void mov_free(AVFormatContext *s) ff_mov_cenc_free(&track->cenc); ffio_free_dyn_buf(&track->mdat_buf); + ffio_free_dyn_buf(&track->iamf_buf); + if (track->iamf) + ff_iamf_uninit_context(track->iamf); + av_freep(&track->iamf); + avpriv_packet_list_free(&track->squashed_packet_queue); } @@ -6847,6 +7152,66 @@ static int mov_create_dvd_sub_decoder_specific_info(MOVTrack *track, return 0; } +static int mov_init_iamf_track(AVFormatContext *s) +{ + MOVMuxContext *mov = s->priv_data; + MOVTrack *track = &mov->tracks[0]; // IAMF if present is always the first track + int nb_audio_elements = 0, nb_mix_presentations = 0; + int ret; + + for (int i = 0; i < s->nb_stream_groups; i++) { + const AVStreamGroup *stg = s->stream_groups[i]; + + if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT) + nb_audio_elements++; + if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION) + nb_mix_presentations++; + } + + if (!nb_audio_elements && !nb_mix_presentations) + return 0; + + if (nb_audio_elements < 1 || nb_audio_elements > 2 || nb_mix_presentations < 1) { + av_log(s, AV_LOG_ERROR, "There must be >= 1 and <= 2 IAMF_AUDIO_ELEMENT and at least " + "one IAMF_MIX_PRESENTATION stream groups to write a IMAF track\n"); + return AVERROR(EINVAL); + } + + track->iamf = av_mallocz(sizeof(*track->iamf)); + if (!track->iamf) + return AVERROR(ENOMEM); + + for (int i = 0; i < s->nb_stream_groups; i++) { + const AVStreamGroup *stg = s->stream_groups[i]; + switch(stg->type) { + case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT: + for (int j = 0; j < stg->nb_streams; j++) { + track->first_iamf_idx = FFMIN(stg->streams[j]->index, track->first_iamf_idx); + track->last_iamf_idx = FFMAX(stg->streams[j]->index, track->last_iamf_idx); + stg->streams[j]->priv_data = track; + } + + ret = ff_iamf_add_audio_element(track->iamf, stg, s); + break; + case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: + ret = ff_iamf_add_mix_presentation(track->iamf, stg, s); + break; + default: + av_assert0(0); + } + if (ret < 0) + return ret; + } + + track->tag = MKTAG('i','a','m','f'); + + ret = avio_open_dyn_buf(&track->iamf_buf); + if (ret < 0) + return ret; + + return 0; +} + static int mov_init(AVFormatContext *s) { MOVMuxContext *mov = s->priv_data; @@ -6984,14 +7349,44 @@ static int mov_init(AVFormatContext *s) s->streams[0]->disposition |= AV_DISPOSITION_DEFAULT; } - mov->nb_streams = s->nb_streams; + for (i = 0; i < s->nb_stream_groups; i++) { + AVStreamGroup *stg = s->stream_groups[i]; + + if (stg->type != AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT) + continue; + + for (int j = 0; j < stg->nb_streams; j++) { + AVStream *st = stg->streams[j]; + + if (st->priv_data) { + av_log(s, AV_LOG_ERROR, "Stream %d is present in more than one Stream Group of type " + "IAMF Audio Element\n", j); + return AVERROR(EINVAL); + } + st->priv_data = st; + } + + if (!mov->nb_tracks) // We support one track for the entire IAMF structure + mov->nb_tracks++; + } + + for (i = 0; i < s->nb_streams; i++) { + AVStream *st = s->streams[i]; + if (st->priv_data) + continue; + st->priv_data = st; + mov->nb_tracks++; + } + + mov->nb_streams = mov->nb_tracks; + if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters) - mov->chapter_track = mov->nb_streams++; + mov->chapter_track = mov->nb_tracks++; if (mov->flags & FF_MOV_FLAG_RTP_HINT) { for (i = 0; i < s->nb_streams; i++) if (rtp_hinting_needed(s->streams[i])) - mov->nb_streams++; + mov->nb_tracks++; } if (mov->write_btrt < 0) { @@ -7010,7 +7405,7 @@ static int mov_init(AVFormatContext *s) if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && (t || (t=av_dict_get(st->metadata, "timecode", NULL, 0)))) { AVTimecode tc; - ret = mov_check_timecode_track(s, &tc, i, t->value); + ret = mov_check_timecode_track(s, &tc, st, t->value); if (ret >= 0) mov->nb_meta_tmcd++; } @@ -7028,12 +7423,12 @@ static int mov_init(AVFormatContext *s) } } - mov->nb_streams += mov->nb_meta_tmcd; + mov->nb_tracks += mov->nb_meta_tmcd; } // Reserve an extra stream for chapters for the case where chapters // are written in the trailer - mov->tracks = av_calloc(mov->nb_streams + 1, sizeof(*mov->tracks)); + mov->tracks = av_calloc(mov->nb_tracks + 1, sizeof(*mov->tracks)); if (!mov->tracks) return AVERROR(ENOMEM); @@ -7059,18 +7454,33 @@ static int mov_init(AVFormatContext *s) } } + ret = mov_init_iamf_track(s); + if (ret < 0) + return ret; + + for (int j = 0, i = 0; j < s->nb_streams; j++) { + AVStream *st = s->streams[j]; + + if (st != st->priv_data) + continue; + st->priv_data = &mov->tracks[i++]; + } + for (i = 0; i < s->nb_streams; i++) { AVStream *st= s->streams[i]; - MOVTrack *track= &mov->tracks[i]; + MOVTrack *track = st->priv_data; AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL,0); - track->st = st; - track->par = st->codecpar; + if (!track->st) { + track->st = st; + track->par = st->codecpar; + } track->language = ff_mov_iso639_to_lang(lang?lang->value:"und", mov->mode!=MODE_MOV); if (track->language < 0) track->language = 32767; // Unspecified Macintosh language code track->mode = mov->mode; - track->tag = mov_find_codec_tag(s, track); + if (!track->tag) + track->tag = mov_find_codec_tag(s, track); if (!track->tag) { av_log(s, AV_LOG_ERROR, "Could not find tag for codec %s in stream #%d, " "codec not currently supported in container\n", @@ -7246,7 +7656,8 @@ static int mov_init(AVFormatContext *s) if (mov->encryption_scheme == MOV_ENC_CENC_AES_CTR) { ret = ff_mov_cenc_init(&track->cenc, mov->encryption_key, - (track->par->codec_id == AV_CODEC_ID_H264 || track->par->codec_id == AV_CODEC_ID_HEVC), + (track->par->codec_id == AV_CODEC_ID_H264 || track->par->codec_id == AV_CODEC_ID_HEVC || + track->par->codec_id == AV_CODEC_ID_VVC), s->flags & AVFMT_FLAG_BITEXACT); if (ret) return ret; @@ -7261,25 +7672,25 @@ static int mov_write_header(AVFormatContext *s) { AVIOContext *pb = s->pb; MOVMuxContext *mov = s->priv_data; - int i, ret, hint_track = 0, tmcd_track = 0, nb_tracks = s->nb_streams; + int ret, hint_track = 0, tmcd_track = 0, nb_tracks = mov->nb_streams; if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters) nb_tracks++; if (mov->flags & FF_MOV_FLAG_RTP_HINT) { hint_track = nb_tracks; - for (i = 0; i < s->nb_streams; i++) - if (rtp_hinting_needed(s->streams[i])) + for (int i = 0; i < mov->nb_streams; i++) { + if (rtp_hinting_needed(mov->tracks[i].st)) nb_tracks++; + } } if (mov->nb_meta_tmcd) tmcd_track = nb_tracks; - for (i = 0; i < s->nb_streams; i++) { - int j; - AVStream *st= s->streams[i]; - MOVTrack *track= &mov->tracks[i]; + for (int i = 0; i < mov->nb_streams; i++) { + MOVTrack *track = &mov->tracks[i]; + AVStream *st = track->st; /* copy extradata if it exists */ if (st->codecpar->extradata_size) { @@ -7301,8 +7712,8 @@ static int mov_write_header(AVFormatContext *s) &(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO)) continue; - for (j = 0; j < s->nb_streams; j++) { - AVStream *stj= s->streams[j]; + for (int j = 0; j < mov->nb_streams; j++) { + AVStream *stj= mov->tracks[j].st; MOVTrack *trackj= &mov->tracks[j]; if (j == i) continue; @@ -7365,8 +7776,8 @@ static int mov_write_header(AVFormatContext *s) return ret; if (mov->flags & FF_MOV_FLAG_RTP_HINT) { - for (i = 0; i < s->nb_streams; i++) { - if (rtp_hinting_needed(s->streams[i])) { + for (int i = 0; i < mov->nb_streams; i++) { + if (rtp_hinting_needed(mov->tracks[i].st)) { if ((ret = ff_mov_init_hinting(s, hint_track, i)) < 0) return ret; hint_track++; @@ -7378,8 +7789,8 @@ static int mov_write_header(AVFormatContext *s) const AVDictionaryEntry *t, *global_tcr = av_dict_get(s->metadata, "timecode", NULL, 0); /* Initialize the tmcd tracks */ - for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; + for (int i = 0; i < mov->nb_streams; i++) { + AVStream *st = mov->tracks[i].st; t = global_tcr; if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { @@ -7388,7 +7799,7 @@ static int mov_write_header(AVFormatContext *s) t = av_dict_get(st->metadata, "timecode", NULL, 0); if (!t) continue; - if (mov_check_timecode_track(s, &tc, i, t->value) < 0) + if (mov_check_timecode_track(s, &tc, st, t->value) < 0) continue; if ((ret = mov_create_timecode_track(s, tmcd_track, i, tc)) < 0) return ret; @@ -7454,7 +7865,7 @@ static int compute_moov_size(AVFormatContext *s) if (moov_size < 0) return moov_size; - for (i = 0; i < mov->nb_streams; i++) + for (i = 0; i < mov->nb_tracks; i++) mov->tracks[i].data_offset += moov_size; moov_size2 = get_moov_size(s); @@ -7464,7 +7875,7 @@ static int compute_moov_size(AVFormatContext *s) /* if the size changed, we just switched from stco to co64 and need to * update the offsets */ if (moov_size2 != moov_size) - for (i = 0; i < mov->nb_streams; i++) + for (i = 0; i < mov->nb_tracks; i++) mov->tracks[i].data_offset += moov_size2 - moov_size; return moov_size2; @@ -7479,7 +7890,7 @@ static int compute_sidx_size(AVFormatContext *s) if (sidx_size < 0) return sidx_size; - for (i = 0; i < mov->nb_streams; i++) + for (i = 0; i < mov->nb_tracks; i++) mov->tracks[i].data_offset += sidx_size; return sidx_size; @@ -7509,7 +7920,7 @@ static int mov_write_trailer(AVFormatContext *s) int64_t moov_pos; if (mov->need_rewrite_extradata) { - for (i = 0; i < s->nb_streams; i++) { + for (i = 0; i < mov->nb_streams; i++) { MOVTrack *track = &mov->tracks[i]; AVCodecParameters *par = track->par; @@ -7528,7 +7939,7 @@ static int mov_write_trailer(AVFormatContext *s) * Before actually writing the trailer, make sure that there are no * dangling subtitles, that need a terminating sample. */ - for (i = 0; i < mov->nb_streams; i++) { + for (i = 0; i < mov->nb_tracks; i++) { MOVTrack *trk = &mov->tracks[i]; if (trk->par->codec_id == AV_CODEC_ID_MOV_TEXT && !trk->last_sample_is_subtitle_end) { @@ -7547,7 +7958,7 @@ static int mov_write_trailer(AVFormatContext *s) // when we are not doing fragments. if (!mov->chapter_track && !(mov->flags & FF_MOV_FLAG_FRAGMENT)) { if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters) { - mov->chapter_track = mov->nb_streams++; + mov->chapter_track = mov->nb_tracks++; if ((res = mov_create_chapter_track(s, mov->chapter_track)) < 0) return res; } @@ -7599,7 +8010,7 @@ static int mov_write_trailer(AVFormatContext *s) res = 0; } else { mov_auto_flush_fragment(s, 1); - for (i = 0; i < mov->nb_streams; i++) + for (i = 0; i < mov->nb_tracks; i++) mov->tracks[i].data_offset = 0; if (mov->flags & FF_MOV_FLAG_GLOBAL_SIDX) { int64_t end; @@ -7638,18 +8049,19 @@ static int mov_check_bitstream(AVFormatContext *s, AVStream *st, return ret; } +#if CONFIG_AVIF_MUXER static int avif_write_trailer(AVFormatContext *s) { AVIOContext *pb = s->pb; MOVMuxContext *mov = s->priv_data; int64_t pos_backup, extent_offsets[2]; uint8_t *buf; - int buf_size, moov_size, i; + int buf_size, moov_size; if (mov->moov_written) return 0; mov->is_animated_avif = s->streams[0]->nb_frames > 1; - if (mov->is_animated_avif && s->nb_streams > 1) { + if (mov->is_animated_avif && mov->nb_streams > 1) { // For animated avif with alpha channel, we need to write a tref tag // with type "auxl". mov->tracks[1].tref_tag = MKTAG('a', 'u', 'x', 'l'); @@ -7659,7 +8071,7 @@ static int avif_write_trailer(AVFormatContext *s) mov_write_meta_tag(pb, mov, s); moov_size = get_moov_size(s); - for (i = 0; i < s->nb_streams; i++) + for (int i = 0; i < mov->nb_tracks; i++) mov->tracks[i].data_offset = avio_tell(pb) + moov_size + 8; if (mov->is_animated_avif) { @@ -7681,7 +8093,7 @@ static int avif_write_trailer(AVFormatContext *s) // write extent offsets. pos_backup = avio_tell(pb); - for (i = 0; i < s->nb_streams; i++) { + for (int i = 0; i < mov->nb_streams; i++) { if (extent_offsets[i] != (uint32_t)extent_offsets[i]) { av_log(s, AV_LOG_ERROR, "extent offset does not fit in 32 bits\n"); return AVERROR_INVALIDDATA; @@ -7693,6 +8105,7 @@ static int avif_write_trailer(AVFormatContext *s) return 0; } +#endif #if CONFIG_TGP_MUXER || CONFIG_TG2_MUXER static const AVCodecTag codec_3gp_tags[] = { @@ -7714,6 +8127,10 @@ static const AVCodecTag codec_mp4_tags[] = { { AV_CODEC_ID_H264, MKTAG('a', 'v', 'c', '3') }, { AV_CODEC_ID_HEVC, MKTAG('h', 'e', 'v', '1') }, { AV_CODEC_ID_HEVC, MKTAG('h', 'v', 'c', '1') }, + { AV_CODEC_ID_HEVC, MKTAG('d', 'v', 'h', '1') }, + { AV_CODEC_ID_VVC, MKTAG('v', 'v', 'c', '1') }, + { AV_CODEC_ID_VVC, MKTAG('v', 'v', 'i', '1') }, + { AV_CODEC_ID_EVC, MKTAG('e', 'v', 'c', '1') }, { AV_CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', '4', 'v') }, { AV_CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', '4', 'v') }, { AV_CODEC_ID_MJPEG, MKTAG('m', 'p', '4', 'v') }, @@ -7744,6 +8161,20 @@ static const AVCodecTag codec_mp4_tags[] = { { AV_CODEC_ID_MPEGH_3D_AUDIO, MKTAG('m', 'h', 'm', '1') }, { AV_CODEC_ID_TTML, MOV_MP4_TTML_TAG }, { AV_CODEC_ID_TTML, MOV_ISMV_TTML_TAG }, + + /* ISO/IEC 23003-5 integer formats */ + { AV_CODEC_ID_PCM_S16BE, MOV_MP4_IPCM_TAG }, + { AV_CODEC_ID_PCM_S16LE, MOV_MP4_IPCM_TAG }, + { AV_CODEC_ID_PCM_S24BE, MOV_MP4_IPCM_TAG }, + { AV_CODEC_ID_PCM_S24LE, MOV_MP4_IPCM_TAG }, + { AV_CODEC_ID_PCM_S32BE, MOV_MP4_IPCM_TAG }, + { AV_CODEC_ID_PCM_S32LE, MOV_MP4_IPCM_TAG }, + /* ISO/IEC 23003-5 floating-point formats */ + { AV_CODEC_ID_PCM_F32BE, MOV_MP4_FPCM_TAG }, + { AV_CODEC_ID_PCM_F32LE, MOV_MP4_FPCM_TAG }, + { AV_CODEC_ID_PCM_F64BE, MOV_MP4_FPCM_TAG }, + { AV_CODEC_ID_PCM_F64LE, MOV_MP4_FPCM_TAG }, + { AV_CODEC_ID_NONE, 0 }, }; #if CONFIG_MP4_MUXER || CONFIG_PSP_MUXER @@ -7780,7 +8211,7 @@ static const AVCodecTag codec_f4v_tags[] = { static const AVOption avif_options[] = { { "movie_timescale", "set movie timescale", offsetof(MOVMuxContext, movie_timescale), AV_OPT_TYPE_INT, {.i64 = MOV_TIMESCALE}, 1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM}, - { "loop", "Number of times to loop animated AVIF: 0 - infinite loop", offsetof(MOVMuxContext, avif_loop_count), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, 0 }, + { "loop", "Number of times to loop animated AVIF: 0 - infinite loop", offsetof(MOVMuxContext, avif_loop_count), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, .unit = 0 }, { NULL }, }; static const AVCodecTag codec_avif_tags[] = { @@ -7811,12 +8242,17 @@ const FFOutputFormat ff_mov_muxer = { .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, +#else + .p.flags = AVFMT_GLOBALHEADER | AVFMT_TS_NEGATIVE, +#endif .p.codec_tag = (const AVCodecTag* const []){ ff_codec_movvideo_tags, ff_codec_movaudio_tags, ff_codec_movsubtitle_tags, 0 }, .check_bitstream = mov_check_bitstream, .p.priv_class = &mov_isobmff_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif #if CONFIG_TGP_MUXER @@ -7832,10 +8268,15 @@ const FFOutputFormat ff_tgp_muxer = { .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, +#else + .p.flags = AVFMT_GLOBALHEADER | AVFMT_TS_NEGATIVE, +#endif .p.codec_tag = codec_3gp_tags_list, .check_bitstream = mov_check_bitstream, .p.priv_class = &mov_isobmff_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif #if CONFIG_MP4_MUXER @@ -7853,10 +8294,15 @@ const FFOutputFormat ff_mp4_muxer = { .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, +#else + .p.flags = AVFMT_GLOBALHEADER | AVFMT_TS_NEGATIVE, +#endif .p.codec_tag = mp4_codec_tags_list, .check_bitstream = mov_check_bitstream, .p.priv_class = &mov_isobmff_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif #if CONFIG_PSP_MUXER @@ -7873,10 +8319,15 @@ const FFOutputFormat ff_psp_muxer = { .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, +#else + .p.flags = AVFMT_GLOBALHEADER | AVFMT_TS_NEGATIVE, +#endif .p.codec_tag = mp4_codec_tags_list, .check_bitstream = mov_check_bitstream, .p.priv_class = &mov_isobmff_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif #if CONFIG_TG2_MUXER @@ -7892,10 +8343,15 @@ const FFOutputFormat ff_tg2_muxer = { .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, +#else + .p.flags = AVFMT_GLOBALHEADER | AVFMT_TS_NEGATIVE, +#endif .p.codec_tag = codec_3gp_tags_list, .check_bitstream = mov_check_bitstream, .p.priv_class = &mov_isobmff_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif #if CONFIG_IPOD_MUXER @@ -7912,10 +8368,15 @@ const FFOutputFormat ff_ipod_muxer = { .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, +#else + .p.flags = AVFMT_GLOBALHEADER | AVFMT_TS_NEGATIVE, +#endif .p.codec_tag = (const AVCodecTag* const []){ codec_ipod_tags, 0 }, .check_bitstream = mov_check_bitstream, .p.priv_class = &mov_isobmff_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif #if CONFIG_ISMV_MUXER @@ -7932,11 +8393,16 @@ const FFOutputFormat ff_ismv_muxer = { .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE, +#else + .p.flags = AVFMT_GLOBALHEADER | AVFMT_TS_NEGATIVE, +#endif .p.codec_tag = (const AVCodecTag* const []){ codec_mp4_tags, codec_ism_tags, 0 }, .check_bitstream = mov_check_bitstream, .p.priv_class = &mov_isobmff_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif #if CONFIG_F4V_MUXER @@ -7953,10 +8419,15 @@ const FFOutputFormat ff_f4v_muxer = { .write_packet = mov_write_packet, .write_trailer = mov_write_trailer, .deinit = mov_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, +#else + .p.flags = AVFMT_GLOBALHEADER, +#endif .p.codec_tag = (const AVCodecTag* const []){ codec_f4v_tags, 0 }, .check_bitstream = mov_check_bitstream, .p.priv_class = &mov_isobmff_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif #if CONFIG_AVIF_MUXER @@ -7972,8 +8443,13 @@ const FFOutputFormat ff_avif_muxer = { .write_packet = mov_write_packet, .write_trailer = avif_write_trailer, .deinit = mov_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, +#else + .p.flags = AVFMT_GLOBALHEADER, +#endif .p.codec_tag = codec_avif_tags_list, .p.priv_class = &mov_avif_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif diff --git a/libavformat/movenc.h b/libavformat/movenc.h index e85d83abdbd..08d580594d8 100644 --- a/libavformat/movenc.h +++ b/libavformat/movenc.h @@ -170,6 +170,11 @@ typedef struct MOVTrack { unsigned int squash_fragment_samples_to_one; //< flag to note formats where all samples for a fragment are to be squashed PacketList squashed_packet_queue; + + struct IAMFContext *iamf; + int first_iamf_idx; + int last_iamf_idx; + AVIOContext *iamf_buf; } MOVTrack; typedef enum { @@ -189,6 +194,7 @@ typedef struct MOVMuxContext { int mode; int64_t time; int nb_streams; + int nb_tracks; int nb_meta_tmcd; ///< number of new created tmcd track based on metadata (aka not data copy) int chapter_track; ///< qt chapter track number int64_t mdat_pos; diff --git a/libavformat/mp3dec.c b/libavformat/mp3dec.c index 05c13228bce..0029ba63aa3 100644 --- a/libavformat/mp3dec.c +++ b/libavformat/mp3dec.c @@ -32,6 +32,7 @@ #include "replaygain.h" #include "libavcodec/codec_id.h" +#include "libavcodec/mpegaudio.h" #include "libavcodec/mpegaudiodecheader.h" #define XING_FLAG_FRAMES 0x01 @@ -136,9 +137,10 @@ static void read_xing_toc(AVFormatContext *s, int64_t filesize, int64_t duration int fill_index = (mp3->usetoc || fast_seek) && duration > 0; if (!filesize && - !(filesize = avio_size(s->pb))) { + (filesize = avio_size(s->pb)) <= 0) { av_log(s, AV_LOG_WARNING, "Cannot determine file size, skipping TOC table.\n"); fill_index = 0; + filesize = 0; } for (i = 0; i < XING_TOC_COUNT; i++) { @@ -400,27 +402,22 @@ static int mp3_read_header(AVFormatContext *s) if (ret < 0) return ret; + ret = ffio_ensure_seekback(s->pb, 64 * 1024 + MPA_MAX_CODED_FRAME_SIZE + 4); + if (ret < 0) + return ret; + off = avio_tell(s->pb); for (i = 0; i < 64 * 1024; i++) { uint32_t header, header2; int frame_size; - if (!(i&1023)) - ffio_ensure_seekback(s->pb, i + 1024 + 4); frame_size = check(s->pb, off + i, &header); if (frame_size > 0) { - ffio_ensure_seekback(s->pb, i + 1024 + frame_size + 4); ret = check(s->pb, off + i + frame_size, &header2); - if (ret >= 0 && - (header & MP3_MASK) == (header2 & MP3_MASK)) - { + if (ret >= 0 && (header & MP3_MASK) == (header2 & MP3_MASK)) break; - } else if (ret == CHECK_SEEK_FAILED) { - av_log(s, AV_LOG_ERROR, "Invalid frame size (%d): Could not seek to %"PRId64".\n", frame_size, off + i + frame_size); - return AVERROR(EINVAL); - } } else if (frame_size == CHECK_SEEK_FAILED) { - av_log(s, AV_LOG_ERROR, "Failed to read frame size: Could not seek to %"PRId64".\n", (int64_t) (i + 1024 + frame_size + 4)); - return AVERROR(EINVAL); + av_log(s, AV_LOG_ERROR, "Failed to find two consecutive MPEG audio frames.\n"); + return AVERROR_INVALIDDATA; } } if (i == 64 * 1024) { @@ -588,7 +585,7 @@ static int mp3_seek(AVFormatContext *s, int stream_index, int64_t timestamp, if (best_pos < 0) return best_pos; - if (mp3->is_cbr && ie == &ie1 && mp3->frames) { + if (mp3->is_cbr && ie == &ie1 && mp3->frames && mp3->header_filesize > 0) { int frame_duration = av_rescale(st->duration, 1, mp3->frames); ie1.timestamp = frame_duration * av_rescale(best_pos - si->data_offset, mp3->frames, mp3->header_filesize); } @@ -610,15 +607,15 @@ static const AVClass demuxer_class = { .category = AV_CLASS_CATEGORY_DEMUXER, }; -const AVInputFormat ff_mp3_demuxer = { - .name = "mp3", - .long_name = NULL_IF_CONFIG_SMALL("MP2/3 (MPEG audio layer 2/3)"), +const FFInputFormat ff_mp3_demuxer = { + .p.name = "mp3", + .p.long_name = NULL_IF_CONFIG_SMALL("MP2/3 (MPEG audio layer 2/3)"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "mp2,mp3,m2a,mpa", /* XXX: use probe */ + .p.priv_class = &demuxer_class, .read_probe = mp3_read_probe, .read_header = mp3_read_header, .read_packet = mp3_read_packet, .read_seek = mp3_seek, .priv_data_size = sizeof(MP3DecContext), - .flags = AVFMT_GENERIC_INDEX, - .extensions = "mp2,mp3,m2a,mpa", /* XXX: use probe */ - .priv_class = &demuxer_class, }; diff --git a/libavformat/mp3enc.c b/libavformat/mp3enc.c index 5e81f72a59a..4a02a45069e 100644 --- a/libavformat/mp3enc.c +++ b/libavformat/mp3enc.c @@ -400,10 +400,10 @@ static int mp3_queue_flush(AVFormatContext *s) static void mp3_update_xing(AVFormatContext *s) { MP3Context *mp3 = s->priv_data; + const AVPacketSideData *sd; AVReplayGain *rg; uint16_t tag_crc; uint8_t *toc; - size_t rg_size; int i; int64_t old_pos = avio_tell(s->pb); @@ -423,11 +423,13 @@ static void mp3_update_xing(AVFormatContext *s) } /* write replaygain */ - rg = (AVReplayGain*)av_stream_get_side_data(s->streams[0], AV_PKT_DATA_REPLAYGAIN, - &rg_size); - if (rg && rg_size >= sizeof(*rg)) { + sd = av_packet_side_data_get(s->streams[0]->codecpar->coded_side_data, + s->streams[0]->codecpar->nb_coded_side_data, + AV_PKT_DATA_REPLAYGAIN); + if (sd && sd->size >= sizeof(*rg)) { uint16_t val; + rg = (AVReplayGain *)sd->data; AV_WB32(mp3->xing_frame + mp3->xing_offset + 131, av_rescale(rg->track_peak, 1 << 23, 100000)); @@ -493,12 +495,16 @@ static int mp3_write_trailer(struct AVFormatContext *s) static int query_codec(enum AVCodecID id, int std_compliance) { const CodecMime *cm= ff_id3v2_mime_tags; + + if (id == AV_CODEC_ID_MP3) + return 1; + while(cm->id != AV_CODEC_ID_NONE) { if(id == cm->id) return MKTAG('A', 'P', 'I', 'C'); cm++; } - return -1; + return 0; } static const AVOption options[] = { diff --git a/libavformat/mpc.c b/libavformat/mpc.c index ef16237ab65..89130d5a7ed 100644 --- a/libavformat/mpc.c +++ b/libavformat/mpc.c @@ -219,13 +219,13 @@ static int mpc_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp } -const AVInputFormat ff_mpc_demuxer = { - .name = "mpc", - .long_name = NULL_IF_CONFIG_SMALL("Musepack"), +const FFInputFormat ff_mpc_demuxer = { + .p.name = "mpc", + .p.long_name = NULL_IF_CONFIG_SMALL("Musepack"), + .p.extensions = "mpc", .priv_data_size = sizeof(MPCContext), .read_probe = mpc_probe, .read_header = mpc_read_header, .read_packet = mpc_read_packet, .read_seek = mpc_read_seek, - .extensions = "mpc", }; diff --git a/libavformat/mpc8.c b/libavformat/mpc8.c index 95a1529c5df..42a34a3255d 100644 --- a/libavformat/mpc8.c +++ b/libavformat/mpc8.c @@ -348,9 +348,9 @@ static int mpc8_read_seek(AVFormatContext *s, int stream_index, int64_t timestam } -const AVInputFormat ff_mpc8_demuxer = { - .name = "mpc8", - .long_name = NULL_IF_CONFIG_SMALL("Musepack SV8"), +const FFInputFormat ff_mpc8_demuxer = { + .p.name = "mpc8", + .p.long_name = NULL_IF_CONFIG_SMALL("Musepack SV8"), .priv_data_size = sizeof(MPCContext), .read_probe = mpc8_probe, .read_header = mpc8_read_header, diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c index 864b08d8f8b..d48f95c4568 100644 --- a/libavformat/mpeg.c +++ b/libavformat/mpeg.c @@ -75,6 +75,9 @@ static int mpegps_probe(const AVProbeData *p) int pes = endpes <= i && check_pes(p->buf + i, p->buf + p->buf_size); int pack = check_pack_header(p->buf + i); + if (len > INT_MAX - i) + break; + if (code == SYSTEM_HEADER_START_CODE) sys++; else if (code == PACK_START_CODE && pack) @@ -546,9 +549,15 @@ static int mpegps_read_packet(AVFormatContext *s, } else if (es_type == STREAM_TYPE_VIDEO_HEVC) { codec_id = AV_CODEC_ID_HEVC; type = AVMEDIA_TYPE_VIDEO; + } else if (es_type == STREAM_TYPE_VIDEO_VVC) { + codec_id = AV_CODEC_ID_VVC; + type = AVMEDIA_TYPE_VIDEO; } else if (es_type == STREAM_TYPE_AUDIO_AC3) { codec_id = AV_CODEC_ID_AC3; type = AVMEDIA_TYPE_AUDIO; + } else if (es_type == 0x90) { + codec_id = AV_CODEC_ID_PCM_ALAW; + type = AVMEDIA_TYPE_AUDIO; } else if (m->imkh_cctv && es_type == 0x91) { codec_id = AV_CODEC_ID_PCM_MULAW; type = AVMEDIA_TYPE_AUDIO; @@ -686,15 +695,15 @@ static int64_t mpegps_read_dts(AVFormatContext *s, int stream_index, return dts; } -const AVInputFormat ff_mpegps_demuxer = { - .name = "mpeg", - .long_name = NULL_IF_CONFIG_SMALL("MPEG-PS (MPEG-2 Program Stream)"), +const FFInputFormat ff_mpegps_demuxer = { + .p.name = "mpeg", + .p.long_name = NULL_IF_CONFIG_SMALL("MPEG-PS (MPEG-2 Program Stream)"), + .p.flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, .priv_data_size = sizeof(MpegDemuxContext), .read_probe = mpegps_probe, .read_header = mpegps_read_header, .read_packet = mpegps_read_packet, .read_timestamp = mpegps_read_dts, - .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, }; #if CONFIG_VOBSUB_DEMUXER @@ -1042,18 +1051,18 @@ static const AVClass vobsub_demuxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_vobsub_demuxer = { - .name = "vobsub", - .long_name = NULL_IF_CONFIG_SMALL("VobSub subtitle format"), +const FFInputFormat ff_vobsub_demuxer = { + .p.name = "vobsub", + .p.long_name = NULL_IF_CONFIG_SMALL("VobSub subtitle format"), + .p.flags = AVFMT_SHOW_IDS, + .p.extensions = "idx", + .p.priv_class = &vobsub_demuxer_class, .priv_data_size = sizeof(VobSubDemuxContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = vobsub_probe, .read_header = vobsub_read_header, .read_packet = vobsub_read_packet, .read_seek2 = vobsub_read_seek, .read_close = vobsub_read_close, - .flags = AVFMT_SHOW_IDS, - .extensions = "idx", - .priv_class = &vobsub_demuxer_class, }; #endif diff --git a/libavformat/mpeg.h b/libavformat/mpeg.h index b6352957762..20592eb184b 100644 --- a/libavformat/mpeg.h +++ b/libavformat/mpeg.h @@ -56,6 +56,7 @@ #define STREAM_TYPE_VIDEO_MPEG4 0x10 #define STREAM_TYPE_VIDEO_H264 0x1b #define STREAM_TYPE_VIDEO_HEVC 0x24 +#define STREAM_TYPE_VIDEO_VVC 0x33 #define STREAM_TYPE_VIDEO_CAVS 0x42 #define STREAM_TYPE_AUDIO_AC3 0x81 diff --git a/libavformat/mpegenc.c b/libavformat/mpegenc.c index c06e3082963..14caf9e162a 100644 --- a/libavformat/mpegenc.c +++ b/libavformat/mpegenc.c @@ -342,8 +342,6 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) lpcm_id = LPCM_ID; for (i = 0; i < ctx->nb_streams; i++) { - AVCPBProperties *props; - st = ctx->streams[i]; stream = av_mallocz(sizeof(StreamInfo)); if (!stream) @@ -430,13 +428,19 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) stream->max_buffer_size = 4 * 1024; s->audio_bound++; break; - case AVMEDIA_TYPE_VIDEO: + case AVMEDIA_TYPE_VIDEO: { + const AVPacketSideData *sd; + AVCPBProperties *props = NULL; if (st->codecpar->codec_id == AV_CODEC_ID_H264) stream->id = h264_id++; else stream->id = mpv_id++; - props = (AVCPBProperties*)av_stream_get_side_data(st, AV_PKT_DATA_CPB_PROPERTIES, NULL); + sd = av_packet_side_data_get(st->codecpar->coded_side_data, + st->codecpar->nb_coded_side_data, + AV_PKT_DATA_CPB_PROPERTIES); + if (sd) + props = (AVCPBProperties*)sd->data; if (props && props->buffer_size) stream->max_buffer_size = 6 * 1024 + props->buffer_size / 8; else { @@ -453,6 +457,7 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) } s->video_bound++; break; + } case AVMEDIA_TYPE_SUBTITLE: stream->id = mps_id++; stream->max_buffer_size = 16 * 1024; @@ -470,12 +475,17 @@ static av_cold int mpeg_mux_init(AVFormatContext *ctx) audio_bitrate = 0; video_bitrate = 0; for (i = 0; i < ctx->nb_streams; i++) { - AVCPBProperties *props; + const AVPacketSideData *sd; + AVCPBProperties *props = NULL; int codec_rate; st = ctx->streams[i]; stream = (StreamInfo *)st->priv_data; - props = (AVCPBProperties*)av_stream_get_side_data(st, AV_PKT_DATA_CPB_PROPERTIES, NULL); + sd = av_packet_side_data_get(st->codecpar->coded_side_data, + st->codecpar->nb_coded_side_data, + AV_PKT_DATA_CPB_PROPERTIES); + if (sd) + props = (AVCPBProperties*)sd->data; if (props) codec_rate = props->max_bitrate; else @@ -1281,8 +1291,8 @@ static void mpeg_mux_deinit(AVFormatContext *ctx) #define OFFSET(x) offsetof(MpegMuxContext, x) #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "muxrate", NULL, OFFSET(user_mux_rate), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, ((1<<22) - 1) * (8 * 50), E }, - { "preload", "Initial demux-decode delay in microseconds.", OFFSET(preload), AV_OPT_TYPE_INT, { .i64 = 500000 }, 0, INT_MAX, E }, + { "muxrate", "mux rate as bits/s", OFFSET(user_mux_rate), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, ((1<<22) - 1) * (8 * 50), E }, + { "preload", "initial demux-decode delay in microseconds", OFFSET(preload), AV_OPT_TYPE_INT, { .i64 = 500000 }, 0, INT_MAX, E }, { NULL }, }; diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c index d97702fcd7d..320926248be 100644 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -34,6 +34,7 @@ #include "libavutil/dovi_meta.h" #include "libavcodec/avcodec.h" #include "libavcodec/bytestream.h" +#include "libavcodec/defs.h" #include "libavcodec/get_bits.h" #include "libavcodec/opus.h" #include "avformat.h" @@ -166,6 +167,8 @@ struct MpegTSContext { int merge_pmt_versions; int max_packet_size; + int id; + /******************************************/ /* private mpegts data */ /* scan context */ @@ -183,14 +186,20 @@ struct MpegTSContext { }; #define MPEGTS_OPTIONS \ - { "resync_size", "set size limit for looking up a new synchronization", offsetof(MpegTSContext, resync_size), AV_OPT_TYPE_INT, { .i64 = MAX_RESYNC_SIZE}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM } + { "resync_size", "set size limit for looking up a new synchronization", \ + offsetof(MpegTSContext, resync_size), AV_OPT_TYPE_INT, \ + { .i64 = MAX_RESYNC_SIZE}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, \ + { "ts_id", "transport stream id", \ + offsetof(MpegTSContext, id), AV_OPT_TYPE_INT, \ + { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY }, \ + { "ts_packetsize", "output option carrying the raw packet size", \ + offsetof(MpegTSContext, raw_packet_size), AV_OPT_TYPE_INT, \ + { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY } static const AVOption options[] = { MPEGTS_OPTIONS, {"fix_teletext_pts", "try to fix pts values of dvb teletext streams", offsetof(MpegTSContext, fix_teletext_pts), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, - {"ts_packetsize", "output option carrying the raw packet size", offsetof(MpegTSContext, raw_packet_size), AV_OPT_TYPE_INT, - {.i64 = 0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY }, {"scan_all_pmts", "scan and combine all PMTs", offsetof(MpegTSContext, scan_all_pmts), AV_OPT_TYPE_BOOL, {.i64 = -1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM }, {"skip_unknown_pmt", "skip PMTs for programs not advertised in the PAT", offsetof(MpegTSContext, skip_unknown_pmt), AV_OPT_TYPE_BOOL, @@ -218,10 +227,6 @@ static const AVOption raw_options[] = { { "compute_pcr", "compute exact PCR for each transport stream packet", offsetof(MpegTSContext, mpeg2ts_compute_pcr), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, - { "ts_packetsize", "output option carrying the raw packet size", - offsetof(MpegTSContext, raw_packet_size), AV_OPT_TYPE_INT, - { .i64 = 0 }, 0, 0, - AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY }, { NULL }, }; @@ -271,7 +276,7 @@ typedef struct PESContext { int merged_st; } PESContext; -extern const AVInputFormat ff_mpegts_demuxer; +extern const FFInputFormat ff_mpegts_demuxer; static struct Program * get_program(MpegTSContext *ts, unsigned int programid) { @@ -811,6 +816,7 @@ static const StreamType ISO_types[] = { { 0x20, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, { 0x21, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_JPEG2000 }, { 0x24, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC }, + { 0x33, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VVC }, { 0x42, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_CAVS }, { 0xd1, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, { 0xd2, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_AVS2 }, @@ -859,13 +865,16 @@ static const StreamType HLS_SAMPLE_ENC_types[] = { static const StreamType REGD_types[] = { { MKTAG('d', 'r', 'a', 'c'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_DIRAC }, { MKTAG('A', 'C', '-', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 }, + { MKTAG('A', 'C', '-', '4'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC4 }, { MKTAG('B', 'S', 'S', 'D'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_S302M }, { MKTAG('D', 'T', 'S', '1'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, { MKTAG('D', 'T', 'S', '2'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, { MKTAG('D', 'T', 'S', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_DTS }, { MKTAG('E', 'A', 'C', '3'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_EAC3 }, { MKTAG('H', 'E', 'V', 'C'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_HEVC }, + { MKTAG('V', 'V', 'C', ' '), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VVC }, { MKTAG('K', 'L', 'V', 'A'), AVMEDIA_TYPE_DATA, AV_CODEC_ID_SMPTE_KLV }, + { MKTAG('V', 'A', 'N', 'C'), AVMEDIA_TYPE_DATA, AV_CODEC_ID_SMPTE_2038 }, { MKTAG('I', 'D', '3', ' '), AVMEDIA_TYPE_DATA, AV_CODEC_ID_TIMED_ID3 }, { MKTAG('V', 'C', '-', '1'), AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_VC1 }, { MKTAG('O', 'p', 'u', 's'), AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_OPUS }, @@ -914,11 +923,6 @@ static int mpegts_set_stream_info(AVStream *st, PESContext *pes, int old_codec_id = st->codecpar->codec_id; int old_codec_tag = st->codecpar->codec_tag; - if (avcodec_is_open(sti->avctx)) { - av_log(pes->stream, AV_LOG_DEBUG, "cannot set stream info, internal codec is open\n"); - return 0; - } - avpriv_set_pts_info(st, 33, 1, 90000); st->priv_data = pes; st->codecpar->codec_type = AVMEDIA_TYPE_DATA; @@ -1303,8 +1307,11 @@ static int mpegts_push_data(MpegTSFilter *filter, p += sl_header_bytes; buf_size -= sl_header_bytes; } - if (pes->stream_type == 0x15 && buf_size >= 5) { - /* skip metadata access unit header */ + if (pes->stream_type == STREAM_TYPE_METADATA && + pes->stream_id == STREAM_ID_METADATA_STREAM && + pes->st->codecpar->codec_id == AV_CODEC_ID_SMPTE_KLV && + buf_size >= 5) { + /* skip metadata access unit header - see MISB ST 1402 */ pes->pes_header_size += 5; p += 5; buf_size -= 5; @@ -1468,8 +1475,7 @@ static int init_MP4DescrParseContext(MP4DescrParseContext *d, AVFormatContext *s if (size > (1 << 30)) return AVERROR_INVALIDDATA; - ffio_init_context(&d->pb, (unsigned char *)buf, size, - 0, NULL, NULL, NULL, NULL); + ffio_init_read_context(&d->pb, buf, size); d->s = s; d->level = 0; @@ -1740,9 +1746,8 @@ static void m4sl_cb(MpegTSFilter *filter, const uint8_t *section, pes->sl = mp4_descr[i].sl; - ffio_init_context(&pb, mp4_descr[i].dec_config_descr, - mp4_descr[i].dec_config_descr_len, 0, - NULL, NULL, NULL, NULL); + ffio_init_read_context(&pb, mp4_descr[i].dec_config_descr, + mp4_descr[i].dec_config_descr_len); ff_mp4_read_dec_config_descr(s, st, &pb.pub); if (st->codecpar->codec_id == AV_CODEC_ID_AAC && st->codecpar->extradata_size > 0) @@ -1850,9 +1855,8 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type if (mp4_descr[i].dec_config_descr_len && mp4_descr[i].es_id == desc_es_id) { FFIOContext pb; - ffio_init_context(&pb, mp4_descr[i].dec_config_descr, - mp4_descr[i].dec_config_descr_len, 0, - NULL, NULL, NULL, NULL); + ffio_init_read_context(&pb, mp4_descr[i].dec_config_descr, + mp4_descr[i].dec_config_descr_len); ff_mp4_read_dec_config_descr(fc, st, &pb.pub); if (st->codecpar->codec_id == AV_CODEC_ID_AAC && st->codecpar->extradata_size > 0) { @@ -1872,9 +1876,8 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type sti->request_probe > 0) && mp4_descr->dec_config_descr_len && mp4_descr->es_id == pid) { FFIOContext pb; - ffio_init_context(&pb, mp4_descr->dec_config_descr, - mp4_descr->dec_config_descr_len, 0, - NULL, NULL, NULL, NULL); + ffio_init_read_context(&pb, mp4_descr->dec_config_descr, + mp4_descr->dec_config_descr_len); ff_mp4_read_dec_config_descr(fc, st, &pb.pub); if (st->codecpar->codec_id == AV_CODEC_ID_AAC && st->codecpar->extradata_size > 0) { @@ -2145,7 +2148,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type // Component tag limits are documented in TR-B14, fascicle 2, // Vol. 3, Section 2, 4.2.8.1 int actual_component_tag = sti->stream_identifier - 1; - int picked_profile = FF_PROFILE_UNKNOWN; + int picked_profile = AV_PROFILE_UNKNOWN; int data_component_id = get16(pp, desc_end); if (data_component_id < 0) return AVERROR_INVALIDDATA; @@ -2156,27 +2159,31 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type // non-mobile captioning service ("profile A"). if (actual_component_tag >= 0x30 && actual_component_tag <= 0x37) { - picked_profile = FF_PROFILE_ARIB_PROFILE_A; + picked_profile = AV_PROFILE_ARIB_PROFILE_A; } break; case 0x0012: // component tag 0x87 signifies a mobile/partial reception // (1seg) captioning service ("profile C"). if (actual_component_tag == 0x87) { - picked_profile = FF_PROFILE_ARIB_PROFILE_C; + picked_profile = AV_PROFILE_ARIB_PROFILE_C; } break; default: break; } - if (picked_profile == FF_PROFILE_UNKNOWN) + if (picked_profile == AV_PROFILE_UNKNOWN) break; st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; st->codecpar->codec_id = AV_CODEC_ID_ARIB_CAPTION; - st->codecpar->profile = picked_profile; + if (st->codecpar->profile != picked_profile) { + st->codecpar->profile = picked_profile; + sti->need_context_update = 1; + } sti->request_probe = 0; + sti->need_parsing = 0; } break; case 0xb0: /* DOVI video stream descriptor */ @@ -2184,7 +2191,6 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type uint32_t buf; AVDOVIDecoderConfigurationRecord *dovi; size_t dovi_size; - int ret; int dependency_pid; if (desc_end - *pp < 4) // (8 + 8 + 7 + 6 + 1 + 1 + 1) / 8 @@ -2215,11 +2221,12 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type dovi->dv_bl_signal_compatibility_id = 0; } - ret = av_stream_add_side_data(st, AV_PKT_DATA_DOVI_CONF, - (uint8_t *)dovi, dovi_size); - if (ret < 0) { + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, + &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_DOVI_CONF, + (uint8_t *)dovi, dovi_size, 0)) { av_free(dovi); - return ret; + return AVERROR(ENOMEM); } av_log(fc, AV_LOG_TRACE, "DOVI, version: %d.%d, profile: %d, level: %d, " @@ -2554,7 +2561,7 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (skip_identical(h, tssf)) return; - ts->stream->ts_id = h->id; + ts->id = h->id; for (;;) { sid = get16(&p, p_end); @@ -2598,7 +2605,8 @@ static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len FFSWAP(struct Program, ts->prg[nb_prg], ts->prg[prg_idx]); if (prg_idx >= nb_prg) nb_prg++; - } + } else + nb_prg = 0; } } ts->nb_prg = nb_prg; @@ -3117,7 +3125,7 @@ static int mpegts_read_header(AVFormatContext *s) ts->stream = s; ts->auto_guess = 0; - if (s->iformat == &ff_mpegts_demuxer) { + if (s->iformat == &ff_mpegts_demuxer.p) { /* normal demux */ /* first do a scan to get all the services */ @@ -3425,27 +3433,27 @@ void avpriv_mpegts_parse_close(MpegTSContext *ts) av_free(ts); } -const AVInputFormat ff_mpegts_demuxer = { - .name = "mpegts", - .long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"), +const FFInputFormat ff_mpegts_demuxer = { + .p.name = "mpegts", + .p.long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"), + .p.flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, + .p.priv_class = &mpegts_class, .priv_data_size = sizeof(MpegTSContext), .read_probe = mpegts_probe, .read_header = mpegts_read_header, .read_packet = mpegts_read_packet, .read_close = mpegts_read_close, .read_timestamp = mpegts_get_dts, - .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, - .priv_class = &mpegts_class, }; -const AVInputFormat ff_mpegtsraw_demuxer = { - .name = "mpegtsraw", - .long_name = NULL_IF_CONFIG_SMALL("raw MPEG-TS (MPEG-2 Transport Stream)"), +const FFInputFormat ff_mpegtsraw_demuxer = { + .p.name = "mpegtsraw", + .p.long_name = NULL_IF_CONFIG_SMALL("raw MPEG-TS (MPEG-2 Transport Stream)"), + .p.flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, + .p.priv_class = &mpegtsraw_class, .priv_data_size = sizeof(MpegTSContext), .read_header = mpegts_read_header, .read_packet = mpegts_raw_read_packet, .read_close = mpegts_read_close, .read_timestamp = mpegts_get_dts, - .flags = AVFMT_SHOW_IDS | AVFMT_TS_DISCONT, - .priv_class = &mpegtsraw_class, }; diff --git a/libavformat/mpegts.h b/libavformat/mpegts.h index a48f14e7688..14ae312c504 100644 --- a/libavformat/mpegts.h +++ b/libavformat/mpegts.h @@ -128,6 +128,7 @@ #define STREAM_TYPE_METADATA 0x15 #define STREAM_TYPE_VIDEO_H264 0x1b #define STREAM_TYPE_VIDEO_HEVC 0x24 +#define STREAM_TYPE_VIDEO_VVC 0x33 #define STREAM_TYPE_VIDEO_CAVS 0x42 #define STREAM_TYPE_VIDEO_AVS2 0xd2 #define STREAM_TYPE_VIDEO_AVS3 0xd4 diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index ceed0895871..d8c148bd3c0 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -28,9 +28,11 @@ #include "libavutil/opt.h" #include "libavcodec/ac3_parser_internal.h" -#include "libavcodec/avcodec.h" #include "libavcodec/bytestream.h" +#include "libavcodec/defs.h" #include "libavcodec/h264.h" +#include "libavcodec/hevc.h" +#include "libavcodec/vvc.h" #include "libavcodec/startcode.h" #include "avformat.h" @@ -300,11 +302,11 @@ static int put_arib_caption_descriptor(AVFormatContext *s, uint8_t **q_ptr, uint8_t *q = *q_ptr; switch (codecpar->profile) { - case FF_PROFILE_ARIB_PROFILE_A: + case AV_PROFILE_ARIB_PROFILE_A: stream_identifier = 0x30; data_component_id = 0x0008; break; - case FF_PROFILE_ARIB_PROFILE_C: + case AV_PROFILE_ARIB_PROFILE_C: stream_identifier = 0x87; data_component_id = 0x0012; break; @@ -368,6 +370,9 @@ static int get_dvb_stream_type(AVFormatContext *s, AVStream *st) case AV_CODEC_ID_HEVC: stream_type = STREAM_TYPE_VIDEO_HEVC; break; + case AV_CODEC_ID_VVC: + stream_type = STREAM_TYPE_VIDEO_VVC; + break; case AV_CODEC_ID_CAVS: stream_type = STREAM_TYPE_VIDEO_CAVS; break; @@ -422,13 +427,16 @@ static int get_dvb_stream_type(AVFormatContext *s, AVStream *st) case AV_CODEC_ID_TIMED_ID3: stream_type = STREAM_TYPE_METADATA; break; + case AV_CODEC_ID_SMPTE_2038: + stream_type = STREAM_TYPE_PRIVATE_DATA; + break; case AV_CODEC_ID_DVB_SUBTITLE: case AV_CODEC_ID_DVB_TELETEXT: case AV_CODEC_ID_ARIB_CAPTION: stream_type = STREAM_TYPE_PRIVATE_DATA; break; case AV_CODEC_ID_SMPTE_KLV: - if (st->codecpar->profile == FF_PROFILE_KLVA_SYNC) { + if (st->codecpar->profile == AV_PROFILE_KLVA_SYNC) { stream_type = STREAM_TYPE_METADATA; } else { stream_type = STREAM_TYPE_PRIVATE_DATA; @@ -804,6 +812,8 @@ static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) case AVMEDIA_TYPE_DATA: if (codec_id == AV_CODEC_ID_SMPTE_KLV) { put_registration_descriptor(&q, MKTAG('K', 'L', 'V', 'A')); + } else if (codec_id == AV_CODEC_ID_SMPTE_2038) { + put_registration_descriptor(&q, MKTAG('V', 'A', 'N', 'C')); } else if (codec_id == AV_CODEC_ID_TIMED_ID3) { const char *tag = "ID3 "; *q++ = METADATA_DESCRIPTOR; @@ -1455,7 +1465,8 @@ static int get_pes_stream_id(AVFormatContext *s, AVStream *st, int stream_id, in st->codecpar->codec_id == AV_CODEC_ID_TIMED_ID3) { return STREAM_ID_PRIVATE_STREAM_1; } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) { - if (stream_id == STREAM_ID_PRIVATE_STREAM_1) /* asynchronous KLV */ + if (st->codecpar->codec_id == AV_CODEC_ID_SMPTE_KLV && + stream_id == STREAM_ID_PRIVATE_STREAM_1) /* asynchronous KLV */ *async = 1; return stream_id != -1 ? stream_id : STREAM_ID_METADATA_STREAM; } else { @@ -1753,16 +1764,16 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, ts_st->prev_payload_key = key; } -int ff_check_h264_startcode(AVFormatContext *s, const AVStream *st, const AVPacket *pkt) +static int check_h26x_startcode(AVFormatContext *s, const AVStream *st, const AVPacket *pkt, const char *codec) { if (pkt->size < 5 || AV_RB32(pkt->data) != 0x0000001 && AV_RB24(pkt->data) != 0x000001) { if (!st->nb_frames) { - av_log(s, AV_LOG_ERROR, "H.264 bitstream malformed, " - "no startcode found, use the video bitstream filter 'h264_mp4toannexb' to fix it " - "('-bsf:v h264_mp4toannexb' option with ffmpeg)\n"); + av_log(s, AV_LOG_ERROR, "%s bitstream malformed, " + "no startcode found, use the video bitstream filter '%s_mp4toannexb' to fix it " + "('-bsf:v %s_mp4toannexb' option with ffmpeg)\n", codec, codec, codec); return AVERROR_INVALIDDATA; } - av_log(s, AV_LOG_WARNING, "H.264 bitstream error, startcode missing, size %d", pkt->size); + av_log(s, AV_LOG_WARNING, "%s bitstream error, startcode missing, size %d", codec, pkt->size); if (pkt->size) av_log(s, AV_LOG_WARNING, " data %08"PRIX32, AV_RB32(pkt->data)); av_log(s, AV_LOG_WARNING, "\n"); @@ -1770,19 +1781,9 @@ int ff_check_h264_startcode(AVFormatContext *s, const AVStream *st, const AVPack return 0; } -static int check_hevc_startcode(AVFormatContext *s, const AVStream *st, const AVPacket *pkt) +int ff_check_h264_startcode(AVFormatContext *s, const AVStream *st, const AVPacket *pkt) { - if (pkt->size < 5 || AV_RB32(pkt->data) != 0x0000001 && AV_RB24(pkt->data) != 0x000001) { - if (!st->nb_frames) { - av_log(s, AV_LOG_ERROR, "HEVC bitstream malformed, no startcode found\n"); - return AVERROR_PATCHWELCOME; - } - av_log(s, AV_LOG_WARNING, "HEVC bitstream error, startcode missing, size %d", pkt->size); - if (pkt->size) - av_log(s, AV_LOG_WARNING, " data %08"PRIX32, AV_RB32(pkt->data)); - av_log(s, AV_LOG_WARNING, "\n"); - } - return 0; + return check_h26x_startcode(s, st, pkt, "h264"); } /* Based on GStreamer's gst-plugins-base/ext/ogg/gstoggstream.c @@ -1837,6 +1838,24 @@ static int opus_get_packet_samples(AVFormatContext *s, AVPacket *pkt) return duration; } +static uint8_t *h26x_prefix_aud(const uint8_t *aud, const int aud_size, + const uint8_t *extra_data, const int extra_size, AVPacket *pkt, int *size) +{ + const int sz = 4; //start code size + uint8_t *data = av_malloc(pkt->size + sz + aud_size + extra_size); + if (!data) + return NULL; + AV_WB32(data, 0x00000001); + memcpy(data + sz, aud, aud_size); + memcpy(data + sz + aud_size, extra_data, extra_size); + memcpy(data + sz + aud_size + extra_size, pkt->data, pkt->size); + *size = pkt->size + sz + aud_size + extra_size; + return data; +} + +#define H264_NAL_TYPE(state) (state & 0x1f) +#define HEVC_NAL_TYPE(state) ((state & 0x7e) >> 1) +#define VVC_NAL_TYPE(state) ((state >> 11) & 0x1f) static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) { AVStream *st = s->streams[pkt->stream_index]; @@ -1880,6 +1899,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) if (st->codecpar->codec_id == AV_CODEC_ID_H264) { const uint8_t *p = buf, *buf_end = p + size; const uint8_t *found_aud = NULL, *found_aud_end = NULL; + int nal_type; uint32_t state = -1; int extradd = (pkt->flags & AV_PKT_FLAG_KEY) ? st->codecpar->extradata_size : 0; int ret = ff_check_h264_startcode(s, st, pkt); @@ -1894,10 +1914,11 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) * are assumed to be available in 'extradata' if not found in-band. */ do { p = avpriv_find_start_code(p, buf_end, &state); - av_log(s, AV_LOG_TRACE, "nal %"PRId32"\n", state & 0x1f); - if ((state & 0x1f) == H264_NAL_SPS) + nal_type = H264_NAL_TYPE(state); + av_log(s, AV_LOG_TRACE, "nal %"PRId32"\n", nal_type); + if (nal_type == H264_NAL_SPS) extradd = 0; - if ((state & 0x1f) == H264_NAL_AUD) { + if (nal_type == H264_NAL_AUD) { found_aud = p - 4; // start of the 0x000001 start code. found_aud_end = p + 1; // first byte past the AUD. if (found_aud < buf) @@ -1906,24 +1927,22 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) found_aud_end = buf_end; } } while (p < buf_end - && (state & 0x1f) != H264_NAL_IDR_SLICE - && (state & 0x1f) != H264_NAL_SLICE + && nal_type != H264_NAL_IDR_SLICE + && nal_type != H264_NAL_SLICE && (extradd > 0 || !found_aud)); - if ((state & 0x1f) != H264_NAL_IDR_SLICE) + if (nal_type != H264_NAL_IDR_SLICE) extradd = 0; if (!found_aud) { /* Prefix 'buf' with the missing AUD, and extradata if needed. */ - data = av_malloc(pkt->size + 6 + extradd); + const uint8_t aud[] = { + H264_NAL_AUD, + 0xf0, // any slice type (0xe) + rbsp stop one bit + }; + buf = data = h26x_prefix_aud(aud, FF_ARRAY_ELEMS(aud), + st->codecpar->extradata, extradd, pkt, &size); if (!data) return AVERROR(ENOMEM); - memcpy(data + 6, st->codecpar->extradata, extradd); - memcpy(data + 6 + extradd, pkt->data, pkt->size); - AV_WB32(data, 0x00000001); - data[4] = H264_NAL_AUD; - data[5] = 0xf0; // any slice type (0xe) + rbsp stop one bit - buf = data; - size = pkt->size + 6 + extradd; } else if (extradd != 0) { /* Move the AUD up to the beginning of the frame, where the H.264 * spec requires it to appear. Emit the extradata after it. */ @@ -1978,8 +1997,9 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) } else if (st->codecpar->codec_id == AV_CODEC_ID_HEVC) { const uint8_t *p = buf, *buf_end = p + size; uint32_t state = -1; + int nal_type; int extradd = (pkt->flags & AV_PKT_FLAG_KEY) ? st->codecpar->extradata_size : 0; - int ret = check_hevc_startcode(s, st, pkt); + int ret = check_h26x_startcode(s, st, pkt, "hevc"); if (ret < 0) return ret; @@ -1988,27 +2008,58 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) do { p = avpriv_find_start_code(p, buf_end, &state); - av_log(s, AV_LOG_TRACE, "nal %"PRId32"\n", (state & 0x7e)>>1); - if ((state & 0x7e) == 2*32) + nal_type = HEVC_NAL_TYPE(state); + av_log(s, AV_LOG_TRACE, "nal %"PRId32"\n", nal_type); + if (nal_type == HEVC_NAL_VPS) extradd = 0; - } while (p < buf_end && (state & 0x7e) != 2*35 && - (state & 0x7e) >= 2*32); + } while (p < buf_end && nal_type != HEVC_NAL_AUD && nal_type >= HEVC_NAL_VPS); - if ((state & 0x7e) < 2*16 || (state & 0x7e) >= 2*24) + if (nal_type < HEVC_NAL_BLA_W_LP || nal_type >= HEVC_NAL_RSV_VCL24) extradd = 0; - if ((state & 0x7e) != 2*35) { // AUD NAL - data = av_malloc(pkt->size + 7 + extradd); + if (nal_type != HEVC_NAL_AUD) { // AUD NAL + const uint8_t aud[] = { + (HEVC_NAL_AUD << 1), + 0x01, + 0x50, // any slice type (0x4) + rbsp stop one bit + }; + buf = data = h26x_prefix_aud(aud, FF_ARRAY_ELEMS(aud), + st->codecpar->extradata, extradd, pkt, &size); if (!data) return AVERROR(ENOMEM); - memcpy(data + 7, st->codecpar->extradata, extradd); - memcpy(data + 7 + extradd, pkt->data, pkt->size); - AV_WB32(data, 0x00000001); - data[4] = 2*35; - data[5] = 1; - data[6] = 0x50; // any slice type (0x4) + rbsp stop one bit - buf = data; - size = pkt->size + 7 + extradd; } + } else if (st->codecpar->codec_id == AV_CODEC_ID_VVC) { + const uint8_t *p = buf, *buf_end = p + size; + uint32_t state = -1; + uint32_t nal_type = -1; + int extradd = (pkt->flags & AV_PKT_FLAG_KEY) ? st->codecpar->extradata_size : 0; + int ret = check_h26x_startcode(s, st, pkt, "vvc"); + if (ret < 0) + return ret; + + if (extradd && AV_RB24(st->codecpar->extradata) > 1) + extradd = 0; + + do { + p = avpriv_find_start_code(p, buf_end, &state); + // state contains byte behind start code, p points 2 bytes behind start code + nal_type = VVC_NAL_TYPE(state); + av_log(s, AV_LOG_TRACE, "nal %"PRId32"\n", nal_type ); + if (nal_type == VVC_VPS_NUT) + extradd = 0; + } while (p < buf_end && nal_type != VVC_AUD_NUT && nal_type >= VVC_OPI_NUT); + + if (nal_type >= VVC_OPI_NUT) + extradd = 0; + if (nal_type != VVC_AUD_NUT) { // AUD NAL + const uint8_t aud[] = { + 0, // forbidden_zero_bit, nuh_reserved_zero_bit, nuh_layer_id + (VVC_AUD_NUT << 3) | 1, // nal_unit_type, nuh_temporal_id_plus1(1) + (pkt->flags & AV_PKT_FLAG_KEY) << 7 | 0x28, // aud_irap_or_gdr_flag, aud_pic_type(2), rbsp_stop_one_bit + }; + buf = data = h26x_prefix_aud(aud, FF_ARRAY_ELEMS(aud), st->codecpar->extradata, extradd, pkt, &size); + if (!data) + return AVERROR(ENOMEM); + } } else if (st->codecpar->codec_id == AV_CODEC_ID_OPUS) { if (pkt->size < 2) { av_log(s, AV_LOG_ERROR, "Opus packet too short\n"); @@ -2251,23 +2302,27 @@ static void mpegts_deinit(AVFormatContext *s) static int mpegts_check_bitstream(AVFormatContext *s, AVStream *st, const AVPacket *pkt) { - int ret = 1; + const struct Entry { + enum AVCodecID id; + const char *bsf_name; + uint8_t mask; + uint8_t value; + } list[] = { + { AV_CODEC_ID_H264, "h264_mp4toannexb", 0xff, 0x01 /* configurationVersion in AVCDecoderConfigurationRecord */}, + { AV_CODEC_ID_HEVC, "hevc_mp4toannexb", 0xff, 0x01 /* configurationVersion in HEVCDecoderConfigurationRecord */}, + { AV_CODEC_ID_VVC, "vvc_mp4toannexb", 0xf8, 0xf8 /* reserved '11111'b in VVCDecoderConfigurationRecord */}, + }; - if (st->codecpar->codec_id == AV_CODEC_ID_H264) { - if (pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 && - (AV_RB24(pkt->data) != 0x000001 || - (st->codecpar->extradata_size > 0 && - st->codecpar->extradata[0] == 1))) - ret = ff_stream_add_bitstream_filter(st, "h264_mp4toannexb", NULL); - } else if (st->codecpar->codec_id == AV_CODEC_ID_HEVC) { - if (pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 && - (AV_RB24(pkt->data) != 0x000001 || - (st->codecpar->extradata_size > 0 && - st->codecpar->extradata[0] == 1))) - ret = ff_stream_add_bitstream_filter(st, "hevc_mp4toannexb", NULL); + for (int i = 0; i < FF_ARRAY_ELEMS(list); i++) { + const struct Entry *e = list + i; + if (e->id == st->codecpar->codec_id && + pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 && + (AV_RB24(pkt->data) != 0x000001 || + (st->codecpar->extradata_size > 0 && + ((st->codecpar->extradata[0] & e->mask) == e->value)))) + return ff_stream_add_bitstream_filter(st, e->bsf_name, NULL); } - - return ret; + return 1; } #define OFFSET(x) offsetof(MpegTSWrite, x) @@ -2280,23 +2335,23 @@ static const AVOption options[] = { { "mpegts_service_id", "Set service_id field.", OFFSET(service_id), AV_OPT_TYPE_INT, { .i64 = 0x0001 }, 0x0001, 0xffff, ENC }, { "mpegts_service_type", "Set service_type field.", - OFFSET(service_type), AV_OPT_TYPE_INT, { .i64 = 0x01 }, 0x01, 0xff, ENC, "mpegts_service_type" }, + OFFSET(service_type), AV_OPT_TYPE_INT, { .i64 = 0x01 }, 0x01, 0xff, ENC, .unit = "mpegts_service_type" }, { "digital_tv", "Digital Television.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_TV }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_TV }, 0x01, 0xff, ENC, .unit = "mpegts_service_type" }, { "digital_radio", "Digital Radio.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_RADIO }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_RADIO }, 0x01, 0xff, ENC, .unit = "mpegts_service_type" }, { "teletext", "Teletext.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_TELETEXT }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_TELETEXT }, 0x01, 0xff, ENC, .unit = "mpegts_service_type" }, { "advanced_codec_digital_radio", "Advanced Codec Digital Radio.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_RADIO }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_RADIO }, 0x01, 0xff, ENC, .unit = "mpegts_service_type" }, { "mpeg2_digital_hdtv", "MPEG2 Digital HDTV.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_MPEG2_DIGITAL_HDTV }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_MPEG2_DIGITAL_HDTV }, 0x01, 0xff, ENC, .unit = "mpegts_service_type" }, { "advanced_codec_digital_sdtv", "Advanced Codec Digital SDTV.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_SDTV }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_SDTV }, 0x01, 0xff, ENC, .unit = "mpegts_service_type" }, { "advanced_codec_digital_hdtv", "Advanced Codec Digital HDTV.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_HDTV }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_HDTV }, 0x01, 0xff, ENC, .unit = "mpegts_service_type" }, { "hevc_digital_hdtv", "HEVC Digital Television Service.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_HEVC_DIGITAL_HDTV }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_HEVC_DIGITAL_HDTV }, 0x01, 0xff, ENC, .unit = "mpegts_service_type" }, { "mpegts_pmt_start_pid", "Set the first pid of the PMT.", OFFSET(pmt_start_pid), AV_OPT_TYPE_INT, { .i64 = 0x1000 }, FIRST_OTHER_PID, LAST_OTHER_PID, ENC }, { "mpegts_start_pid", "Set the first pid.", @@ -2305,21 +2360,21 @@ static const AVOption options[] = { { "muxrate", NULL, OFFSET(mux_rate), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, ENC }, { "pes_payload_size", "Minimum PES packet payload in bytes", OFFSET(pes_payload_size), AV_OPT_TYPE_INT, { .i64 = DEFAULT_PES_PAYLOAD_SIZE }, 0, INT_MAX, ENC }, - { "mpegts_flags", "MPEG-TS muxing flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, 0, INT_MAX, ENC, "mpegts_flags" }, + { "mpegts_flags", "MPEG-TS muxing flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, 0, INT_MAX, ENC, .unit = "mpegts_flags" }, { "resend_headers", "Reemit PAT/PMT before writing the next packet", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_REEMIT_PAT_PMT }, 0, INT_MAX, ENC, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_REEMIT_PAT_PMT }, 0, INT_MAX, ENC, .unit = "mpegts_flags" }, { "latm", "Use LATM packetization for AAC", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_AAC_LATM }, 0, INT_MAX, ENC, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_AAC_LATM }, 0, INT_MAX, ENC, .unit = "mpegts_flags" }, { "pat_pmt_at_frames", "Reemit PAT and PMT at each video frame", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_PAT_PMT_AT_FRAMES}, 0, INT_MAX, ENC, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_PAT_PMT_AT_FRAMES}, 0, INT_MAX, ENC, .unit = "mpegts_flags" }, { "system_b", "Conform to System B (DVB) instead of System A (ATSC)", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_SYSTEM_B }, 0, INT_MAX, ENC, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_SYSTEM_B }, 0, INT_MAX, ENC, .unit = "mpegts_flags" }, { "initial_discontinuity", "Mark initial packets as discontinuous", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_DISCONT }, 0, INT_MAX, ENC, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_DISCONT }, 0, INT_MAX, ENC, .unit = "mpegts_flags" }, { "nit", "Enable NIT transmission", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_NIT}, 0, INT_MAX, ENC, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_NIT}, 0, INT_MAX, ENC, .unit = "mpegts_flags" }, { "omit_rai", "Disable writing of random access indicator", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_OMIT_RAI }, 0, INT_MAX, ENC, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_OMIT_RAI }, 0, INT_MAX, ENC, .unit = "mpegts_flags" }, { "mpegts_copyts", "don't offset dts/pts", OFFSET(copyts), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, ENC }, { "tables_version", "set PAT, PMT, SDT and NIT version", OFFSET(tables_version), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 31, ENC }, { "omit_video_pes_length", "Omit the PES packet length for video packets", @@ -2355,6 +2410,11 @@ const FFOutputFormat ff_mpegts_muxer = { .write_trailer = mpegts_write_end, .deinit = mpegts_deinit, .check_bitstream = mpegts_check_bitstream, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_ALLOW_FLUSH | AVFMT_VARIABLE_FPS | AVFMT_NODIMENSIONS, +#else + .p.flags = AVFMT_VARIABLE_FPS | AVFMT_NODIMENSIONS, +#endif + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, .p.priv_class = &mpegts_muxer_class, }; diff --git a/libavformat/mpjpeg.c b/libavformat/mpjpeg.c index 81ace8e9ee1..8641c9c6700 100644 --- a/libavformat/mpjpeg.c +++ b/libavformat/mpjpeg.c @@ -70,6 +70,8 @@ const FFOutputFormat ff_mpjpeg_muxer = { .priv_data_size = sizeof(MPJPEGContext), .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_MJPEG, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_header = mpjpeg_write_header, .write_packet = mpjpeg_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, diff --git a/libavformat/mpjpegdec.c b/libavformat/mpjpegdec.c index 559a375d8d7..e20c61be9be 100644 --- a/libavformat/mpjpegdec.c +++ b/libavformat/mpjpegdec.c @@ -23,11 +23,10 @@ #include "libavutil/opt.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "avio_internal.h" - - typedef struct MPJPEGDemuxContext { const AVClass *class; char *boundary; @@ -36,7 +35,6 @@ typedef struct MPJPEGDemuxContext { int strict_mime_boundary; } MPJPEGDemuxContext; - static void trim_right(char *p) { char *end; @@ -63,8 +61,6 @@ static int get_line(AVIOContext *pb, char *line, int line_size) return 0; } - - static int split_tag_value(char **tag, char **value, char *line) { char *p = line; @@ -73,7 +69,6 @@ static int split_tag_value(char **tag, char **value, char *line) *tag = NULL; *value = NULL; - while (*p != '\0' && *p != ':') { if (!av_isspace(*p)) { foundData = 1; @@ -120,7 +115,7 @@ static int mpjpeg_read_probe(const AVProbeData *p) if (p->buf_size < 2 || p->buf[0] != '-' || p->buf[1] != '-') return 0; - ffio_init_context(&pb, p->buf, p->buf_size, 0, NULL, NULL, NULL, NULL); + ffio_init_read_context(&pb, p->buf, p->buf_size); ret = (parse_multipart_header(&pb.pub, &size, "--", NULL) >= 0) ? AVPROBE_SCORE_MAX : 0; @@ -245,7 +240,6 @@ static int parse_multipart_header(AVIOContext *pb, return found_content_type ? 0 : AVERROR_INVALIDDATA; } - static char* mpjpeg_get_boundary(AVIOContext* pb) { uint8_t *mime_type = NULL; @@ -289,7 +283,6 @@ static char* mpjpeg_get_boundary(AVIOContext* pb) return res; } - static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt) { int size; @@ -318,8 +311,6 @@ static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt) } ret = parse_multipart_header(s->pb, &size, mpjpeg->boundary, s); - - if (ret < 0) return ret; @@ -365,14 +356,12 @@ static int mpjpeg_read_packet(AVFormatContext *s, AVPacket *pkt) } #define OFFSET(x) offsetof(MPJPEGDemuxContext, x) - #define DEC AV_OPT_FLAG_DECODING_PARAM static const AVOption mpjpeg_options[] = { { "strict_mime_boundary", "require MIME boundaries match", OFFSET(strict_mime_boundary), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, DEC }, { NULL } }; - static const AVClass mpjpeg_demuxer_class = { .class_name = "MPJPEG demuxer", .item_name = av_default_item_name, @@ -380,18 +369,16 @@ static const AVClass mpjpeg_demuxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_mpjpeg_demuxer = { - .name = "mpjpeg", - .long_name = NULL_IF_CONFIG_SMALL("MIME multipart JPEG"), - .mime_type = "multipart/x-mixed-replace", - .extensions = "mjpg", +const FFInputFormat ff_mpjpeg_demuxer = { + .p.name = "mpjpeg", + .p.long_name = NULL_IF_CONFIG_SMALL("MIME multipart JPEG"), + .p.mime_type = "multipart/x-mixed-replace", + .p.extensions = "mjpg", + .p.priv_class = &mpjpeg_demuxer_class, + .p.flags = AVFMT_NOTIMESTAMPS, .priv_data_size = sizeof(MPJPEGDemuxContext), .read_probe = mpjpeg_read_probe, .read_header = mpjpeg_read_header, .read_packet = mpjpeg_read_packet, .read_close = mpjpeg_read_close, - .priv_class = &mpjpeg_demuxer_class, - .flags = AVFMT_NOTIMESTAMPS, }; - - diff --git a/libavformat/mpl2dec.c b/libavformat/mpl2dec.c index 912a707d1af..9daf51ff866 100644 --- a/libavformat/mpl2dec.c +++ b/libavformat/mpl2dec.c @@ -26,6 +26,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "subtitles.h" @@ -122,14 +123,14 @@ static int mpl2_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_mpl2_demuxer = { - .name = "mpl2", - .long_name = NULL_IF_CONFIG_SMALL("MPL2 subtitles"), +const FFInputFormat ff_mpl2_demuxer = { + .p.name = "mpl2", + .p.long_name = NULL_IF_CONFIG_SMALL("MPL2 subtitles"), + .p.extensions = "txt,mpl2", .priv_data_size = sizeof(MPL2Context), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = mpl2_probe, .read_header = mpl2_read_header, - .extensions = "txt,mpl2", .read_packet = ff_subtitles_read_packet, .read_seek2 = ff_subtitles_read_seek, .read_close = ff_subtitles_read_close, diff --git a/libavformat/mpsubdec.c b/libavformat/mpsubdec.c index d290a41fb9c..a7fcdab05ec 100644 --- a/libavformat/mpsubdec.c +++ b/libavformat/mpsubdec.c @@ -24,6 +24,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" #include "subtitles.h" @@ -116,9 +117,10 @@ static int mpsub_read_header(AVFormatContext *s) AVPacket *sub; const int64_t pos = avio_tell(s->pb); - ff_subtitles_read_chunk(s->pb, &buf); + res = ff_subtitles_read_chunk(s->pb, &buf); + if (res < 0) goto end; if (buf.len) { - sub = ff_subtitles_queue_insert(&mpsub->q, buf.str, buf.len, 0); + sub = ff_subtitles_queue_insert_bprint(&mpsub->q, &buf, 0); if (!sub) { res = AVERROR(ENOMEM); goto end; @@ -169,14 +171,14 @@ static int mpsub_read_header(AVFormatContext *s) return res; } -const AVInputFormat ff_mpsub_demuxer = { - .name = "mpsub", - .long_name = NULL_IF_CONFIG_SMALL("MPlayer subtitles"), +const FFInputFormat ff_mpsub_demuxer = { + .p.name = "mpsub", + .p.long_name = NULL_IF_CONFIG_SMALL("MPlayer subtitles"), + .p.extensions = "sub", .priv_data_size = sizeof(MPSubContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = mpsub_probe, .read_header = mpsub_read_header, - .extensions = "sub", .read_packet = ff_subtitles_read_packet, .read_seek2 = ff_subtitles_read_seek, .read_close = ff_subtitles_read_close, diff --git a/libavformat/msf.c b/libavformat/msf.c index 354758971ad..37e05b538bd 100644 --- a/libavformat/msf.c +++ b/libavformat/msf.c @@ -21,6 +21,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" static int msf_probe(const AVProbeData *p) @@ -103,11 +104,11 @@ static int msf_read_packet(AVFormatContext *s, AVPacket *pkt) return av_get_packet(s->pb, pkt, par->block_align ? par->block_align : 1024 * par->ch_layout.nb_channels); } -const AVInputFormat ff_msf_demuxer = { - .name = "msf", - .long_name = NULL_IF_CONFIG_SMALL("Sony PS3 MSF"), +const FFInputFormat ff_msf_demuxer = { + .p.name = "msf", + .p.long_name = NULL_IF_CONFIG_SMALL("Sony PS3 MSF"), + .p.extensions = "msf", .read_probe = msf_probe, .read_header = msf_read_header, .read_packet = msf_read_packet, - .extensions = "msf", }; diff --git a/libavformat/msnwc_tcp.c b/libavformat/msnwc_tcp.c index 95d4e2bd336..c0b775e0e5f 100644 --- a/libavformat/msnwc_tcp.c +++ b/libavformat/msnwc_tcp.c @@ -20,6 +20,7 @@ #include "libavcodec/bytestream.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define HEADER_SIZE 24 @@ -136,9 +137,9 @@ static int msnwc_tcp_read_packet(AVFormatContext *ctx, AVPacket *pkt) return HEADER_SIZE + size; } -const AVInputFormat ff_msnwc_tcp_demuxer = { - .name = "msnwctcp", - .long_name = NULL_IF_CONFIG_SMALL("MSN TCP Webcam stream"), +const FFInputFormat ff_msnwc_tcp_demuxer = { + .p.name = "msnwctcp", + .p.long_name = NULL_IF_CONFIG_SMALL("MSN TCP Webcam stream"), .read_probe = msnwc_tcp_probe, .read_header = msnwc_tcp_read_header, .read_packet = msnwc_tcp_read_packet, diff --git a/libavformat/mspdec.c b/libavformat/mspdec.c index 44854e99c21..6b3926f4cda 100644 --- a/libavformat/mspdec.c +++ b/libavformat/mspdec.c @@ -27,6 +27,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/imgutils.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct { @@ -106,12 +107,12 @@ static int msp_read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVInputFormat ff_msp_demuxer = { - .name = "msp", - .long_name = NULL_IF_CONFIG_SMALL("Microsoft Paint (MSP))"), +const FFInputFormat ff_msp_demuxer = { + .p.name = "msp", + .p.long_name = NULL_IF_CONFIG_SMALL("Microsoft Paint (MSP))"), + .p.flags = AVFMT_NOTIMESTAMPS, .read_probe = msp_probe, .read_header = msp_read_header, .read_packet = msp_read_packet, - .flags = AVFMT_NOTIMESTAMPS, .priv_data_size = sizeof(MSPContext), }; diff --git a/libavformat/mtaf.c b/libavformat/mtaf.c index 954ffaa8ba8..251da549634 100644 --- a/libavformat/mtaf.c +++ b/libavformat/mtaf.c @@ -22,6 +22,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" static int mtaf_probe(const AVProbeData *p) @@ -71,11 +72,11 @@ static int mtaf_read_packet(AVFormatContext *s, AVPacket *pkt) return av_get_packet(s->pb, pkt, par->block_align); } -const AVInputFormat ff_mtaf_demuxer = { - .name = "mtaf", - .long_name = NULL_IF_CONFIG_SMALL("Konami PS2 MTAF"), +const FFInputFormat ff_mtaf_demuxer = { + .p.name = "mtaf", + .p.long_name = NULL_IF_CONFIG_SMALL("Konami PS2 MTAF"), + .p.extensions = "mtaf", .read_probe = mtaf_probe, .read_header = mtaf_read_header, .read_packet = mtaf_read_packet, - .extensions = "mtaf", }; diff --git a/libavformat/mtv.c b/libavformat/mtv.c index b6ed43d444b..9f52cb6c675 100644 --- a/libavformat/mtv.c +++ b/libavformat/mtv.c @@ -27,6 +27,7 @@ #include "libavutil/bswap.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define MTV_ASUBCHUNK_DATA_SIZE 500 @@ -225,9 +226,9 @@ static int mtv_read_packet(AVFormatContext *s, AVPacket *pkt) return ret; } -const AVInputFormat ff_mtv_demuxer = { - .name = "mtv", - .long_name = NULL_IF_CONFIG_SMALL("MTV"), +const FFInputFormat ff_mtv_demuxer = { + .p.name = "mtv", + .p.long_name = NULL_IF_CONFIG_SMALL("MTV"), .priv_data_size = sizeof(MTVDemuxContext), .read_probe = mtv_probe, .read_header = mtv_read_header, diff --git a/libavformat/musx.c b/libavformat/musx.c index 5bf793f8821..c87ee3c4ecd 100644 --- a/libavformat/musx.c +++ b/libavformat/musx.c @@ -22,6 +22,7 @@ #include "libavutil/avassert.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" static int musx_probe(const AVProbeData *p) @@ -178,11 +179,11 @@ static int musx_read_packet(AVFormatContext *s, AVPacket *pkt) return av_get_packet(s->pb, pkt, par->block_align); } -const AVInputFormat ff_musx_demuxer = { - .name = "musx", - .long_name = NULL_IF_CONFIG_SMALL("Eurocom MUSX"), +const FFInputFormat ff_musx_demuxer = { + .p.name = "musx", + .p.long_name = NULL_IF_CONFIG_SMALL("Eurocom MUSX"), + .p.extensions = "musx", .read_probe = musx_probe, .read_header = musx_read_header, .read_packet = musx_read_packet, - .extensions = "musx", }; diff --git a/libavformat/mux.c b/libavformat/mux.c index 04de05ec178..630204a8ece 100644 --- a/libavformat/mux.c +++ b/libavformat/mux.c @@ -24,12 +24,14 @@ #include "mux.h" #include "version.h" #include "libavcodec/bsf.h" +#include "libavcodec/codec_desc.h" #include "libavcodec/internal.h" #include "libavcodec/packet_internal.h" #include "libavutil/opt.h" #include "libavutil/dict.h" #include "libavutil/timestamp.h" #include "libavutil/avassert.h" +#include "libavutil/frame.h" #include "libavutil/internal.h" #include "libavutil/mathematics.h" @@ -102,7 +104,7 @@ int avformat_alloc_output_context2(AVFormatContext **avctx, const AVOutputFormat if (format) { oformat = av_guess_format(format, NULL, NULL); if (!oformat) { - av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format\n", format); + av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not known.\n", format); ret = AVERROR(EINVAL); goto error; } @@ -110,8 +112,10 @@ int avformat_alloc_output_context2(AVFormatContext **avctx, const AVOutputFormat oformat = av_guess_format(NULL, filename, NULL); if (!oformat) { ret = AVERROR(EINVAL); - av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'\n", - filename); + av_log(s, AV_LOG_ERROR, + "Unable to choose an output format for '%s'; " + "use a standard extension for the filename or specify " + "the format manually.\n", filename); goto error; } } @@ -184,6 +188,12 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options) AVDictionary *tmp = NULL; const FFOutputFormat *of = ffofmt(s->oformat); AVDictionaryEntry *e; + static const unsigned default_codec_offsets[] = { + [AVMEDIA_TYPE_VIDEO] = offsetof(AVOutputFormat, video_codec), + [AVMEDIA_TYPE_AUDIO] = offsetof(AVOutputFormat, audio_codec), + [AVMEDIA_TYPE_SUBTITLE] = offsetof(AVOutputFormat, subtitle_codec), + }; + unsigned nb_type[FF_ARRAY_ELEMS(default_codec_offsets)] = { 0 }; int ret = 0; if (options) @@ -229,22 +239,6 @@ static int init_muxer(AVFormatContext *s, AVDictionary **options) goto fail; } -#if FF_API_OLD_CHANNEL_LAYOUT -FF_DISABLE_DEPRECATION_WARNINGS - /* if the caller is using the deprecated channel layout API, - * convert it to the new style */ - if (!par->ch_layout.nb_channels && - par->channels) { - if (par->channel_layout) { - av_channel_layout_from_mask(&par->ch_layout, par->channel_layout); - } else { - par->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; - par->ch_layout.nb_channels = par->channels; - } - } -FF_ENABLE_DEPRECATION_WARNINGS -#endif - if (!par->block_align) par->block_align = par->ch_layout.nb_channels * av_get_bits_per_sample(par->codec_id) >> 3; @@ -274,6 +268,51 @@ FF_ENABLE_DEPRECATION_WARNINGS } break; } + if (of->flags_internal & (FF_OFMT_FLAG_MAX_ONE_OF_EACH | FF_OFMT_FLAG_ONLY_DEFAULT_CODECS)) { + enum AVCodecID default_codec_id = AV_CODEC_ID_NONE; + unsigned nb; + if ((unsigned)par->codec_type < FF_ARRAY_ELEMS(default_codec_offsets)) { + nb = ++nb_type[par->codec_type]; + if (default_codec_offsets[par->codec_type]) + default_codec_id = *(const enum AVCodecID*)((const char*)of + default_codec_offsets[par->codec_type]); + } + if (of->flags_internal & FF_OFMT_FLAG_ONLY_DEFAULT_CODECS && + default_codec_id != AV_CODEC_ID_NONE && par->codec_id != default_codec_id) { + av_log(s, AV_LOG_ERROR, "%s muxer supports only codec %s for type %s\n", + of->p.name, avcodec_get_name(default_codec_id), av_get_media_type_string(par->codec_type)); + ret = AVERROR(EINVAL); + goto fail; + } else if (default_codec_id == AV_CODEC_ID_NONE || + (of->flags_internal & FF_OFMT_FLAG_MAX_ONE_OF_EACH && nb > 1)) { + const char *type = av_get_media_type_string(par->codec_type); + av_log(s, AV_LOG_ERROR, "%s muxer does not support %s stream of type %s\n", + of->p.name, default_codec_id == AV_CODEC_ID_NONE ? "any" : "more than one", + type ? type : "unknown"); + ret = AVERROR(EINVAL); + goto fail; + } + } + +#if FF_API_AVSTREAM_SIDE_DATA +FF_DISABLE_DEPRECATION_WARNINGS + /* if the caller is using the deprecated AVStream side_data API, + * copy its contents to AVStream.codecpar, giving it priority + over existing side data in the latter */ + for (int i = 0; i < st->nb_side_data; i++) { + const AVPacketSideData *sd_src = &st->side_data[i]; + AVPacketSideData *sd_dst; + + sd_dst = av_packet_side_data_new(&st->codecpar->coded_side_data, + &st->codecpar->nb_coded_side_data, + sd_src->type, sd_src->size, 0); + if (!sd_dst) { + ret = AVERROR(ENOMEM); + goto fail; + } + memcpy(sd_dst->data, sd_src->data, sd_src->size); + } +FF_ENABLE_DEPRECATION_WARNINGS +#endif desc = avcodec_descriptor_get(par->codec_id); if (desc && desc->props & AV_CODEC_PROP_REORDER) @@ -304,7 +343,8 @@ FF_ENABLE_DEPRECATION_WARNINGS par->codec_tag = av_codec_get_tag(of->p.codec_tag, par->codec_id); } - if (par->codec_type != AVMEDIA_TYPE_ATTACHMENT) + if (par->codec_type != AVMEDIA_TYPE_ATTACHMENT && + par->codec_id != AV_CODEC_ID_SMPTE_2038) si->nb_interleaved_streams++; } si->interleave_packet = of->interleave_packet; @@ -380,16 +420,11 @@ static int init_pts(AVFormatContext *s) break; } - if (!sti->priv_pts) - sti->priv_pts = av_mallocz(sizeof(*sti->priv_pts)); - if (!sti->priv_pts) - return AVERROR(ENOMEM); - if (den != AV_NOPTS_VALUE) { if (den <= 0) return AVERROR_INVALIDDATA; - frac_init(sti->priv_pts, 0, 0, den); + frac_init(&sti->priv_pts, 0, 0, den); } } @@ -459,9 +494,9 @@ int avformat_write_header(AVFormatContext *s, AVDictionary **options) if ((ret = avformat_init_output(s, options)) < 0) return ret; - if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb) - avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_HEADER); if (ffofmt(s->oformat)->write_header) { + if (!(s->oformat->flags & AVFMT_NOFILE) && s->pb) + avio_write_marker(s->pb, AV_NOPTS_VALUE, AVIO_DATA_MARKER_HEADER); ret = ffofmt(s->oformat)->write_header(s); if (ret >= 0 && s->pb && s->pb->error < 0) ret = s->pb->error; @@ -524,7 +559,7 @@ static int compute_muxer_pkt_fields(AVFormatContext *s, AVStream *st, AVPacket * } pkt->dts = // pkt->pts= st->cur_dts; - pkt->pts = sti->priv_pts->val; + pkt->pts = sti->priv_pts.val; } //calculate dts from pts @@ -561,7 +596,7 @@ static int compute_muxer_pkt_fields(AVFormatContext *s, AVStream *st, AVPacket * av_ts2str(pkt->pts), av_ts2str(pkt->dts)); sti->cur_dts = pkt->dts; - sti->priv_pts->val = pkt->dts; + sti->priv_pts.val = pkt->dts; /* update pts */ switch (st->codecpar->codec_type) { @@ -573,12 +608,12 @@ static int compute_muxer_pkt_fields(AVFormatContext *s, AVStream *st, AVPacket * /* HACK/FIXME, we skip the initial 0 size packets as they are most * likely equal to the encoder delay, but it would be better if we * had the real timestamps from the encoder */ - if (frame_size >= 0 && (pkt->size || sti->priv_pts->num != sti->priv_pts->den >> 1 || sti->priv_pts->val)) { - frac_add(sti->priv_pts, (int64_t)st->time_base.den * frame_size); + if (frame_size >= 0 && (pkt->size || sti->priv_pts.num != sti->priv_pts.den >> 1 || sti->priv_pts.val)) { + frac_add(&sti->priv_pts, (int64_t)st->time_base.den * frame_size); } break; case AVMEDIA_TYPE_VIDEO: - frac_add(sti->priv_pts, (int64_t)st->time_base.den * st->time_base.num); + frac_add(&sti->priv_pts, (int64_t)st->time_base.den * st->time_base.num); break; } return 0; @@ -942,7 +977,8 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *pkt, ++stream_count; } else if (par->codec_type != AVMEDIA_TYPE_ATTACHMENT && par->codec_id != AV_CODEC_ID_VP8 && - par->codec_id != AV_CODEC_ID_VP9) { + par->codec_id != AV_CODEC_ID_VP9 && + par->codec_id != AV_CODEC_ID_SMPTE_2038) { ++noninterleaved_count; } } @@ -968,7 +1004,7 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *pkt, const PacketListEntry *const last = sti->last_in_packet_buffer; int64_t last_dts; - if (!last) + if (!last || st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) continue; last_dts = av_rescale_q(last->pkt.dts, @@ -986,6 +1022,7 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *pkt, } } +#if FF_API_LAVF_SHORTEST if (si->packet_buffer.head && eof && (s->flags & AVFMT_FLAG_SHORTEST) && @@ -1021,6 +1058,7 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *pkt, flush = 0; } } +#endif if (stream_count && flush) { PacketListEntry *pktl = si->packet_buffer.head; @@ -1114,7 +1152,7 @@ static int write_packet_common(AVFormatContext *s, AVStream *st, AVPacket *pkt, int ret; if (s->debug & FF_FDEBUG_TS) - av_log(s, AV_LOG_DEBUG, "%s size:%d dts:%s pts:%s\n", __FUNCTION__, + av_log(s, AV_LOG_DEBUG, "%s size:%d dts:%s pts:%s\n", __func__, pkt->size, av_ts2str(pkt->dts), av_ts2str(pkt->pts)); guess_pkt_duration(s, st, pkt); @@ -1198,7 +1236,7 @@ int av_write_frame(AVFormatContext *s, AVPacket *in) int ret; if (!in) { - if (s->oformat->flags & AVFMT_ALLOW_FLUSH) { + if (ffofmt(s->oformat)->flags_internal & FF_OFMT_FLAG_ALLOW_FLUSH) { ret = ffofmt(s->oformat)->write_packet(s, NULL); flush_if_needed(s); if (ret >= 0 && s->pb && s->pb->error < 0) @@ -1422,13 +1460,6 @@ static int write_uncoded_frame_internal(AVFormatContext *s, int stream_index, pkt->size = sizeof(frame); pkt->pts = pkt->dts = frame->pts; -#if FF_API_PKT_DURATION -FF_DISABLE_DEPRECATION_WARNINGS - if (frame->pkt_duration) - pkt->duration = frame->pkt_duration; - else -FF_ENABLE_DEPRECATION_WARNINGS -#endif pkt->duration = frame->duration; pkt->stream_index = stream_index; pkt->flags |= AV_PKT_FLAG_UNCODED_FRAME; diff --git a/libavformat/mux.h b/libavformat/mux.h index c2de45400c2..0b69109174c 100644 --- a/libavformat/mux.h +++ b/libavformat/mux.h @@ -27,6 +27,37 @@ struct AVDeviceInfoList; +/** + * This flag indicates that the muxer stores data internally + * and supports flushing it. Flushing is signalled by sending + * a NULL packet to the muxer's write_packet callback; + * without this flag, a muxer never receives NULL packets. + * So the documentation of write_packet below for the semantics + * of the return value in case of flushing. + */ +#define FF_OFMT_FLAG_ALLOW_FLUSH (1 << 1) +/** + * If this flag is set, it indicates that for each codec type + * whose corresponding default codec (i.e. AVOutputFormat.audio_codec, + * AVOutputFormat.video_codec and AVOutputFormat.subtitle_codec) + * is set (i.e. != AV_CODEC_ID_NONE) only one stream of this type + * can be muxed. It furthermore indicates that no stream with + * a codec type that has no default codec or whose default codec + * is AV_CODEC_ID_NONE can be muxed. + * Both of these restrictions are checked generically before + * the actual muxer's init/write_header callbacks. + */ +#define FF_OFMT_FLAG_MAX_ONE_OF_EACH (1 << 2) +/** + * If this flag is set, then the only permitted audio/video/subtitle + * codec ids are AVOutputFormat.audio/video/subtitle_codec; + * if any of the latter is unset (i.e. equal to AV_CODEC_ID_NONE), + * then no stream of the corresponding type is supported. + * In addition, codec types without default codec field + * are disallowed. + */ +#define FF_OFMT_FLAG_ONLY_DEFAULT_CODECS (1 << 3) + typedef struct FFOutputFormat { /** * The public AVOutputFormat. See avformat.h for it. @@ -38,13 +69,13 @@ typedef struct FFOutputFormat { int priv_data_size; /** - * Internal flags. See FF_FMT_FLAG_* in internal.h. + * Internal flags. See FF_OFMT_FLAG_* above and FF_FMT_FLAG_* in internal.h. */ int flags_internal; int (*write_header)(AVFormatContext *); /** - * Write a packet. If AVFMT_ALLOW_FLUSH is set in flags, + * Write a packet. If FF_OFMT_FLAG_ALLOW_FLUSH is set in flags_internal, * pkt can be NULL in order to flush data buffered in the muxer. * When flushing, return 0 if there still is more data to flush, * or 1 if everything was flushed and there is no more buffered @@ -96,7 +127,7 @@ typedef struct FFOutputFormat { * by setting the pointer to NULL. */ int (*write_uncoded_frame)(AVFormatContext *, int stream_index, - AVFrame **frame, unsigned flags); + struct AVFrame **frame, unsigned flags); /** * Returns device list with it properties. * @see avdevice_list_devices() for more details. diff --git a/libavformat/mux_utils.c b/libavformat/mux_utils.c index 3e63b8039af..ed1242a6a28 100644 --- a/libavformat/mux_utils.c +++ b/libavformat/mux_utils.c @@ -30,16 +30,6 @@ #include "internal.h" #include "mux.h" -#if FF_API_GET_END_PTS -int64_t av_stream_get_end_pts(const AVStream *st) -{ - if (cffstream(st)->priv_pts) { - return cffstream(st)->priv_pts->val; - } else - return AV_NOPTS_VALUE; -} -#endif - int avformat_query_codec(const AVOutputFormat *ofmt, enum AVCodecID codec_id, int std_compliance) { @@ -49,10 +39,32 @@ int avformat_query_codec(const AVOutputFormat *ofmt, enum AVCodecID codec_id, return ffofmt(ofmt)->query_codec(codec_id, std_compliance); else if (ofmt->codec_tag) return !!av_codec_get_tag2(ofmt->codec_tag, codec_id, &codec_tag); - else if (codec_id == ofmt->video_codec || - codec_id == ofmt->audio_codec || - codec_id == ofmt->subtitle_codec) + else if (codec_id != AV_CODEC_ID_NONE && + (codec_id == ofmt->video_codec || + codec_id == ofmt->audio_codec || + codec_id == ofmt->subtitle_codec)) return 1; + else if (ffofmt(ofmt)->flags_internal & FF_OFMT_FLAG_ONLY_DEFAULT_CODECS) + return 0; + else if (ffofmt(ofmt)->flags_internal & FF_OFMT_FLAG_MAX_ONE_OF_EACH) { + enum AVMediaType type = avcodec_get_type(codec_id); + switch (type) { + case AVMEDIA_TYPE_AUDIO: + if (ofmt->audio_codec == AV_CODEC_ID_NONE) + return 0; + break; + case AVMEDIA_TYPE_VIDEO: + if (ofmt->video_codec == AV_CODEC_ID_NONE) + return 0; + break; + case AVMEDIA_TYPE_SUBTITLE: + if (ofmt->subtitle_codec == AV_CODEC_ID_NONE) + return 0; + break; + default: + return 0; + } + } } return AVERROR_PATCHWELCOME; } diff --git a/libavformat/mvdec.c b/libavformat/mvdec.c index b37fe2ce69e..31640f75908 100644 --- a/libavformat/mvdec.c +++ b/libavformat/mvdec.c @@ -31,6 +31,7 @@ #include "libavutil/rational.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct MvContext { @@ -539,9 +540,9 @@ static int mv_read_seek(AVFormatContext *avctx, int stream_index, return 0; } -const AVInputFormat ff_mv_demuxer = { - .name = "mv", - .long_name = NULL_IF_CONFIG_SMALL("Silicon Graphics Movie"), +const FFInputFormat ff_mv_demuxer = { + .p.name = "mv", + .p.long_name = NULL_IF_CONFIG_SMALL("Silicon Graphics Movie"), .priv_data_size = sizeof(MvContext), .read_probe = mv_probe, .read_header = mv_read_header, diff --git a/libavformat/mvi.c b/libavformat/mvi.c index b2d6a92a4b3..05aa25f3483 100644 --- a/libavformat/mvi.c +++ b/libavformat/mvi.c @@ -23,6 +23,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define MVI_FRAC_BITS 10 @@ -142,11 +143,11 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -const AVInputFormat ff_mvi_demuxer = { - .name = "mvi", - .long_name = NULL_IF_CONFIG_SMALL("Motion Pixels MVI"), +const FFInputFormat ff_mvi_demuxer = { + .p.name = "mvi", + .p.long_name = NULL_IF_CONFIG_SMALL("Motion Pixels MVI"), + .p.extensions = "mvi", .priv_data_size = sizeof(MviDemuxContext), .read_header = read_header, .read_packet = read_packet, - .extensions = "mvi", }; diff --git a/libavformat/mxf.c b/libavformat/mxf.c index 8ef928b8fc2..a73e40e514b 100644 --- a/libavformat/mxf.c +++ b/libavformat/mxf.c @@ -78,6 +78,8 @@ const MXFCodecUL ff_mxf_codec_uls[] = { { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x02,0x03,0x02,0x01,0x00 }, 15, AV_CODEC_ID_AC3 }, { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x02,0x03,0x02,0x05,0x00 }, 15, AV_CODEC_ID_MP2 }, /* MP2 or MP3 */ //{ { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x02,0x03,0x02,0x1C,0x00 }, 15, AV_CODEC_ID_DOLBY_E }, /* Dolby-E */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0D,0x04,0x02,0x02,0x02,0x04,0x03,0x00,0x00 }, 14, AV_CODEC_ID_AAC }, /* MPEG-2 AAC SMPTE 381-4 */ + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0D,0x04,0x02,0x02,0x02,0x04,0x04,0x00,0x00 }, 14, AV_CODEC_ID_AAC }, /* MPEG-4 AAC SMPTE 381-4 */ { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, }; diff --git a/libavformat/mxf.h b/libavformat/mxf.h index 2561605ce52..673703f6bc7 100644 --- a/libavformat/mxf.h +++ b/libavformat/mxf.h @@ -30,8 +30,7 @@ typedef AVUUID UID; enum MXFMetadataSetType { - AnyType, - MaterialPackage, + MaterialPackage = 1, SourcePackage, SourceClip, TimecodeComponent, @@ -55,6 +54,8 @@ enum MXFMetadataSetType { SoundfieldGroupLabelSubDescriptor, GroupOfSoundfieldGroupsLabelSubDescriptor, FFV1SubDescriptor, + JPEG2000SubDescriptor, + MetadataSetTypeNB }; enum MXFFrameLayout { diff --git a/libavformat/mxfdec.c b/libavformat/mxfdec.c index 13959069876..0f9c4fa730b 100644 --- a/libavformat/mxfdec.c +++ b/libavformat/mxfdec.c @@ -45,12 +45,14 @@ */ #include +#include #include "libavutil/aes.h" #include "libavutil/avstring.h" #include "libavutil/mastering_display_metadata.h" #include "libavutil/mathematics.h" #include "libavcodec/bytestream.h" +#include "libavcodec/defs.h" #include "libavcodec/internal.h" #include "libavutil/channel_layout.h" #include "libavutil/dict_internal.h" @@ -114,9 +116,13 @@ typedef struct MXFPartition { typedef struct MXFMetadataSet { UID uid; uint64_t partition_score; - enum MXFMetadataSetType type; } MXFMetadataSet; +typedef struct MXFMetadataSetGroup { + MXFMetadataSet **metadata_sets; + int metadata_sets_count; +} MXFMetadataSetGroup; + typedef struct MXFCryptoContext { MXFMetadataSet meta; UID source_container_ul; @@ -182,8 +188,6 @@ typedef struct { int body_sid; MXFWrappingScheme wrapping; int edit_units_per_packet; /* how many edit units to read at a time (PCM, ClipWrapped) */ - int require_reordering; - int channel_ordering[FF_SANE_NB_CHANNELS]; } MXFTrack; typedef struct MXFDescriptor { @@ -246,7 +250,7 @@ typedef struct MXFFFV1SubDescriptor { typedef struct MXFIndexTableSegment { MXFMetadataSet meta; - int edit_unit_byte_count; + unsigned edit_unit_byte_count; int index_sid; int body_sid; AVRational index_edit_rate; @@ -264,7 +268,6 @@ typedef struct MXFPackage { UID package_ul; UID *tracks_refs; int tracks_count; - MXFDescriptor *descriptor; /* only one */ UID descriptor_ref; char *name; UID *comment_refs; @@ -301,8 +304,7 @@ typedef struct MXFContext { int packages_count; UID *essence_container_data_refs; int essence_container_data_count; - MXFMetadataSet **metadata_sets; - int metadata_sets_count; + MXFMetadataSetGroup metadata_set_groups[MetadataSetTypeNB]; AVFormatContext *fc; struct AVAES *aesc; uint8_t *local_tags; @@ -373,10 +375,10 @@ static const uint8_t mxf_mastering_display_uls[4][16] = { #define IS_KLV_KEY(x, y) (!memcmp(x, y, sizeof(y))) -static void mxf_free_metadataset(MXFMetadataSet **ctx, int freectx) +static void mxf_free_metadataset(MXFMetadataSet **ctx, enum MXFMetadataSetType type) { MXFIndexTableSegment *seg; - switch ((*ctx)->type) { + switch (type) { case Descriptor: case MultipleDescriptor: av_freep(&((MXFDescriptor *)*ctx)->extradata); @@ -421,9 +423,7 @@ static void mxf_free_metadataset(MXFMetadataSet **ctx, int freectx) default: break; } - if (freectx) { - av_freep(ctx); - } + av_freep(ctx); } static int64_t klv_decode_ber_length(AVIOContext *pb) @@ -790,6 +790,9 @@ static int mxf_read_partition_pack(void *arg, AVIOContext *pb, int tag, int size partition->index_sid = avio_rb32(pb); partition->body_offset = avio_rb64(pb); partition->body_sid = avio_rb32(pb); + if (partition->body_offset < 0) + return AVERROR_INVALIDDATA; + if (avio_read(pb, op, sizeof(UID)) != sizeof(UID)) { av_log(mxf->fc, AV_LOG_ERROR, "Failed reading UID\n"); return AVERROR_INVALIDDATA; @@ -914,34 +917,32 @@ static uint64_t partition_score(MXFPartition *p) return (score << 60) | ((uint64_t)p->pack_ofs >> 4); } -static int mxf_add_metadata_set(MXFContext *mxf, MXFMetadataSet **metadata_set) +static int mxf_add_metadata_set(MXFContext *mxf, MXFMetadataSet **metadata_set, enum MXFMetadataSetType type) { - MXFMetadataSet **tmp; - enum MXFMetadataSetType type = (*metadata_set)->type; + MXFMetadataSetGroup *mg = &mxf->metadata_set_groups[type]; + int ret; // Index Table is special because it might be added manually without // partition and we iterate thorugh all instances of them. Also some files // use the same Instance UID for different index tables... if (type != IndexTableSegment) { - for (int i = 0; i < mxf->metadata_sets_count; i++) { - if (!memcmp((*metadata_set)->uid, mxf->metadata_sets[i]->uid, 16) && type == mxf->metadata_sets[i]->type) { - uint64_t old_s = mxf->metadata_sets[i]->partition_score; + for (int i = 0; i < mg->metadata_sets_count; i++) { + if (!memcmp((*metadata_set)->uid, mg->metadata_sets[i]->uid, 16)) { + uint64_t old_s = mg->metadata_sets[i]->partition_score; uint64_t new_s = (*metadata_set)->partition_score; if (old_s > new_s) { - mxf_free_metadataset(metadata_set, 1); + mxf_free_metadataset(metadata_set, type); return 0; } } } } - tmp = av_realloc_array(mxf->metadata_sets, mxf->metadata_sets_count + 1, sizeof(*mxf->metadata_sets)); - if (!tmp) { - mxf_free_metadataset(metadata_set, 1); - return AVERROR(ENOMEM); + + ret = av_dynarray_add_nofree(&mg->metadata_sets, &mg->metadata_sets_count, *metadata_set); + if (ret < 0) { + mxf_free_metadataset(metadata_set, type); + return ret; } - mxf->metadata_sets = tmp; - mxf->metadata_sets[mxf->metadata_sets_count] = *metadata_set; - mxf->metadata_sets_count++; return 0; } @@ -1265,6 +1266,9 @@ static int mxf_read_index_table_segment(void *arg, AVIOContext *pb, int tag, int case 0x3F0B: segment->index_edit_rate.num = avio_rb32(pb); segment->index_edit_rate.den = avio_rb32(pb); + if (segment->index_edit_rate.num <= 0 || + segment->index_edit_rate.den <= 0) + return AVERROR_INVALIDDATA; av_log(NULL, AV_LOG_TRACE, "IndexEditRate %d/%d\n", segment->index_edit_rate.num, segment->index_edit_rate.den); break; @@ -1413,8 +1417,8 @@ static int mxf_read_generic_descriptor(void *arg, AVIOContext *pb, int tag, int } if (IS_KLV_KEY(uid, mxf_jp2k_rsiz)) { uint32_t rsiz = avio_rb16(pb); - if (rsiz == FF_PROFILE_JPEG2000_DCINEMA_2K || - rsiz == FF_PROFILE_JPEG2000_DCINEMA_4K) + if (rsiz == AV_PROFILE_JPEG2000_DCINEMA_2K || + rsiz == AV_PROFILE_JPEG2000_DCINEMA_4K) descriptor->pix_fmt = AV_PIX_FMT_XYZ12; } if (IS_KLV_KEY(uid, mxf_mastering_display_prefix)) { @@ -1553,11 +1557,11 @@ static int mxf_read_tagged_value(void *arg, AVIOContext *pb, int tag, int size, * Match an uid independently of the version byte and up to len common bytes * Returns: boolean */ -static int mxf_match_uid(const UID key, const UID uid, int len) +static int mxf_match_uid(const UID key, const uint8_t uid_prefix[], int len) { int i; for (i = 0; i < len; i++) { - if (i != 7 && key[i] != uid[i]) + if (i != 7 && key[i] != uid_prefix[i]) return 0; } return 1; @@ -1575,16 +1579,14 @@ static const MXFCodecUL *mxf_get_codec_ul(const MXFCodecUL *uls, UID *uid) static void *mxf_resolve_strong_ref(MXFContext *mxf, UID *strong_ref, enum MXFMetadataSetType type) { - int i; + MXFMetadataSetGroup *mg = &mxf->metadata_set_groups[type]; if (!strong_ref) return NULL; - for (i = mxf->metadata_sets_count - 1; i >= 0; i--) { - if (!memcmp(*strong_ref, mxf->metadata_sets[i]->uid, 16) && - (type == AnyType || mxf->metadata_sets[i]->type == type)) { - return mxf->metadata_sets[i]; - } - } + for (int i = mg->metadata_sets_count - 1; i >= 0; i--) + if (!memcmp(*strong_ref, mg->metadata_sets[i]->uid, 16)) + return mg->metadata_sets[i]; + return NULL; } @@ -1639,6 +1641,9 @@ static const MXFCodecUL mxf_sound_essence_container_uls[] = { { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x01,0x0d,0x01,0x03,0x01,0x02,0x01,0x01,0x01 }, 14, AV_CODEC_ID_PCM_S16LE, NULL, 13 }, /* D-10 Mapping 50Mbps PAL Extended Template */ { { 0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0xff,0x4b,0x46,0x41,0x41,0x00,0x0d,0x4d,0x4F }, 14, AV_CODEC_ID_PCM_S16LE }, /* 0001GL00.MXF.A1.mxf_opatom.mxf */ { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x03,0x04,0x02,0x02,0x02,0x03,0x03,0x01,0x00 }, 14, AV_CODEC_ID_AAC }, /* MPEG-2 AAC ADTS (legacy) */ + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x0d,0x01,0x03,0x01,0x02,0x16,0x00,0x00 }, 14, AV_CODEC_ID_AAC, NULL, 14 }, /* AAC ADIF (SMPTE 381-4) */ + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x0d,0x01,0x03,0x01,0x02,0x17,0x00,0x00 }, 14, AV_CODEC_ID_AAC, NULL, 14 }, /* AAC ADTS (SMPTE 381-4) */ + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x0d,0x01,0x03,0x01,0x02,0x18,0x00,0x00 }, 14, AV_CODEC_ID_AAC, NULL, 14 }, /* AAC LATM/LOAS (SMPTE 381-4) */ { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_CODEC_ID_NONE }, }; @@ -1651,48 +1656,48 @@ static const MXFCodecUL mxf_data_essence_container_uls[] = { typedef struct MXFChannelOrderingUL { UID uid; - uint64_t layout_mask; + enum AVChannel channel; enum AVAudioServiceType service_type; } MXFChannelOrderingUL; static const MXFChannelOrderingUL mxf_channel_ordering[] = { - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x02,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x03,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x04,0x00,0x00,0x00,0x00 }, AV_CH_LOW_FREQUENCY, AV_AUDIO_SERVICE_TYPE_MAIN }, // Low Frequency Effects - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x05,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Surround - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x06,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Surround - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x07,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side Surround - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x08,0x00,0x00,0x00,0x00 }, AV_CH_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side Surround - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x09,0x00,0x00,0x00,0x00 }, AV_CH_BACK_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear Surround - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0a,0x00,0x00,0x00,0x00 }, AV_CH_BACK_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear Surround - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0b,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_LEFT_OF_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Center - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0c,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_RIGHT_OF_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Center - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0d,0x00,0x00,0x00,0x00 }, AV_CH_BACK_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Surround - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0e,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED }, // Hearing impaired audio channel - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0f,0x00,0x00,0x00,0x00 }, AV_CH_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED }, // Visually impaired narrative audio channel - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x20,0x03,0x00,0x00,0x00 }, AV_CH_STEREO_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Total - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x20,0x04,0x00,0x00,0x00 }, AV_CH_STEREO_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Total - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x01,0x00,0x00 }, AV_CH_TOP_FRONT_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Height - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x02,0x00,0x00 }, AV_CH_TOP_FRONT_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Height - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x03,0x00,0x00 }, AV_CH_TOP_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Height - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x04,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Surround Height - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x05,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Surround Height - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x06,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side Surround Height - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x07,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side Surround Height - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x08,0x00,0x00 }, AV_CH_TOP_BACK_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear Surround Height - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x09,0x00,0x00 }, AV_CH_TOP_BACK_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear Surround Height - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0a,0x00,0x00 }, AV_CH_TOP_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Top Surround - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0b,0x00,0x00 }, AV_CH_TOP_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Top Surround - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0c,0x00,0x00 }, AV_CH_TOP_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Top Surround - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0d,0x00,0x00 }, AV_CH_LOW_FREQUENCY, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Front Subwoofer - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0e,0x00,0x00 }, AV_CH_LOW_FREQUENCY_2, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Front Subwoofer - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0f,0x00,0x00 }, AV_CH_TOP_BACK_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Rear Height - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x10,0x00,0x00 }, AV_CH_BACK_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Rear - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x11,0x00,0x00 }, AV_CH_BOTTOM_FRONT_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Below - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x12,0x00,0x00 }, AV_CH_BOTTOM_FRONT_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Below - { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x13,0x00,0x00 }, AV_CH_BOTTOM_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Below - { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_AUDIO_SERVICE_TYPE_NB }, + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00 }, AV_CHAN_FRONT_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x02,0x00,0x00,0x00,0x00 }, AV_CHAN_FRONT_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x03,0x00,0x00,0x00,0x00 }, AV_CHAN_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x04,0x00,0x00,0x00,0x00 }, AV_CHAN_LOW_FREQUENCY, AV_AUDIO_SERVICE_TYPE_MAIN }, // Low Frequency Effects + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x05,0x00,0x00,0x00,0x00 }, AV_CHAN_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x06,0x00,0x00,0x00,0x00 }, AV_CHAN_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x07,0x00,0x00,0x00,0x00 }, AV_CHAN_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x08,0x00,0x00,0x00,0x00 }, AV_CHAN_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x09,0x00,0x00,0x00,0x00 }, AV_CHAN_BACK_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0a,0x00,0x00,0x00,0x00 }, AV_CHAN_BACK_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0b,0x00,0x00,0x00,0x00 }, AV_CHAN_FRONT_LEFT_OF_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Center + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0c,0x00,0x00,0x00,0x00 }, AV_CHAN_FRONT_RIGHT_OF_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Center + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0d,0x00,0x00,0x00,0x00 }, AV_CHAN_BACK_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0e,0x00,0x00,0x00,0x00 }, AV_CHAN_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED }, // Hearing impaired audio channel + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x0f,0x00,0x00,0x00,0x00 }, AV_CHAN_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED }, // Visually impaired narrative audio channel + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x20,0x03,0x00,0x00,0x00 }, AV_CHAN_STEREO_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Total + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x20,0x04,0x00,0x00,0x00 }, AV_CHAN_STEREO_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Total + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x01,0x00,0x00 }, AV_CHAN_TOP_FRONT_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x02,0x00,0x00 }, AV_CHAN_TOP_FRONT_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x03,0x00,0x00 }, AV_CHAN_TOP_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x04,0x00,0x00 }, AV_CHAN_TOP_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Surround Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x05,0x00,0x00 }, AV_CHAN_TOP_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Surround Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x06,0x00,0x00 }, AV_CHAN_TOP_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Side Surround Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x07,0x00,0x00 }, AV_CHAN_TOP_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Side Surround Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x08,0x00,0x00 }, AV_CHAN_TOP_BACK_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Rear Surround Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x09,0x00,0x00 }, AV_CHAN_TOP_BACK_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Rear Surround Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0a,0x00,0x00 }, AV_CHAN_TOP_SIDE_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Top Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0b,0x00,0x00 }, AV_CHAN_TOP_SIDE_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Top Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0c,0x00,0x00 }, AV_CHAN_TOP_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Top Surround + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0d,0x00,0x00 }, AV_CHAN_LOW_FREQUENCY, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Front Subwoofer + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0e,0x00,0x00 }, AV_CHAN_LOW_FREQUENCY_2, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Front Subwoofer + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x0f,0x00,0x00 }, AV_CHAN_TOP_BACK_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Rear Height + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x10,0x00,0x00 }, AV_CHAN_BACK_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Rear + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x11,0x00,0x00 }, AV_CHAN_BOTTOM_FRONT_LEFT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Left Below + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x12,0x00,0x00 }, AV_CHAN_BOTTOM_FRONT_RIGHT, AV_AUDIO_SERVICE_TYPE_MAIN }, // Right Below + { { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0d,0x03,0x02,0x01,0x30,0x01,0x13,0x00,0x00 }, AV_CHAN_BOTTOM_FRONT_CENTER, AV_AUDIO_SERVICE_TYPE_MAIN }, // Center Below + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, 0, AV_AUDIO_SERVICE_TYPE_NB }, }; static MXFWrappingScheme mxf_get_wrapping_kind(UID *essence_container_ul) @@ -1738,12 +1743,10 @@ static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segment int i, j, nb_segments = 0; MXFIndexTableSegment **unsorted_segments; int last_body_sid = -1, last_index_sid = -1, last_index_start = -1; + MXFMetadataSetGroup *mg = &mxf->metadata_set_groups[IndexTableSegment]; /* count number of segments, allocate arrays and copy unsorted segments */ - for (i = 0; i < mxf->metadata_sets_count; i++) - if (mxf->metadata_sets[i]->type == IndexTableSegment) - nb_segments++; - + nb_segments = mg->metadata_sets_count; if (!nb_segments) return AVERROR_INVALIDDATA; @@ -1754,15 +1757,13 @@ static int mxf_get_sorted_table_segments(MXFContext *mxf, int *nb_sorted_segment return AVERROR(ENOMEM); } - for (i = nb_segments = 0; i < mxf->metadata_sets_count; i++) { - if (mxf->metadata_sets[i]->type == IndexTableSegment) { - MXFIndexTableSegment *s = (MXFIndexTableSegment*)mxf->metadata_sets[i]; - if (s->edit_unit_byte_count || s->nb_index_entries) - unsorted_segments[nb_segments++] = s; - else - av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i segment at %"PRId64" missing EditUnitByteCount and IndexEntryArray\n", - s->index_sid, s->index_start_position); - } + for (i = nb_segments = 0; i < mg->metadata_sets_count; i++) { + MXFIndexTableSegment *s = (MXFIndexTableSegment*)mg->metadata_sets[i]; + if (s->edit_unit_byte_count || s->nb_index_entries) + unsorted_segments[nb_segments++] = s; + else + av_log(mxf->fc, AV_LOG_WARNING, "IndexSID %i segment at %"PRId64" missing EditUnitByteCount and IndexEntryArray\n", + s->index_sid, s->index_start_position); } if (!nb_segments) { @@ -1896,9 +1897,13 @@ static int mxf_edit_unit_absolute_offset(MXFContext *mxf, MXFIndexTable *index_t if (edit_unit < s->index_start_position + s->index_duration) { int64_t index = edit_unit - s->index_start_position; - if (s->edit_unit_byte_count) + if (s->edit_unit_byte_count) { + if (index > INT64_MAX / s->edit_unit_byte_count || + s->edit_unit_byte_count * index > INT64_MAX - offset_temp) + return AVERROR_INVALIDDATA; + offset_temp += s->edit_unit_byte_count * index; - else { + } else { if (s->nb_index_entries == 2 * s->index_duration + 1) index *= 2; /* Avid index */ @@ -2217,22 +2222,17 @@ static int mxf_add_timecode_metadata(AVDictionary **pm, const char *key, AVTimec static MXFTimecodeComponent* mxf_resolve_timecode_component(MXFContext *mxf, UID *strong_ref) { - MXFStructuralComponent *component = NULL; - MXFPulldownComponent *pulldown = NULL; + MXFTimecodeComponent *timecode; + MXFPulldownComponent *pulldown; - component = mxf_resolve_strong_ref(mxf, strong_ref, AnyType); - if (!component) - return NULL; + timecode = mxf_resolve_strong_ref(mxf, strong_ref, TimecodeComponent); + if (timecode) + return timecode; - switch (component->meta.type) { - case TimecodeComponent: - return (MXFTimecodeComponent*)component; - case PulldownComponent: /* timcode component may be located on a pulldown component */ - pulldown = (MXFPulldownComponent*)component; + pulldown = mxf_resolve_strong_ref(mxf, strong_ref, PulldownComponent); + if (pulldown) return mxf_resolve_strong_ref(mxf, &pulldown->input_segment_ref, TimecodeComponent); - default: - break; - } + return NULL; } @@ -2252,17 +2252,16 @@ static MXFPackage* mxf_resolve_source_package(MXFContext *mxf, UID package_ul, U return NULL; } -static MXFDescriptor* mxf_resolve_multidescriptor(MXFContext *mxf, MXFDescriptor *descriptor, int track_id) +static MXFDescriptor* mxf_resolve_descriptor(MXFContext *mxf, UID *strong_ref, int track_id) { - MXFDescriptor *file_descriptor = NULL; - int i; - - if (!descriptor) - return NULL; + MXFDescriptor *descriptor = mxf_resolve_strong_ref(mxf, strong_ref, Descriptor); + if (descriptor) + return descriptor; - if (descriptor->meta.type == MultipleDescriptor) { - for (i = 0; i < descriptor->file_descriptors_count; i++) { - file_descriptor = mxf_resolve_strong_ref(mxf, &descriptor->file_descriptors_refs[i], Descriptor); + descriptor = mxf_resolve_strong_ref(mxf, strong_ref, MultipleDescriptor); + if (descriptor) { + for (int i = 0; i < descriptor->file_descriptors_count; i++) { + MXFDescriptor *file_descriptor = mxf_resolve_strong_ref(mxf, &descriptor->file_descriptors_refs[i], Descriptor); if (!file_descriptor) { av_log(mxf->fc, AV_LOG_ERROR, "could not resolve file descriptor strong ref\n"); @@ -2272,20 +2271,25 @@ static MXFDescriptor* mxf_resolve_multidescriptor(MXFContext *mxf, MXFDescriptor return file_descriptor; } } - } else if (descriptor->meta.type == Descriptor) - return descriptor; + } return NULL; } -static MXFStructuralComponent* mxf_resolve_essence_group_choice(MXFContext *mxf, MXFEssenceGroup *essence_group) +static MXFStructuralComponent* mxf_resolve_sourceclip(MXFContext *mxf, UID *strong_ref) { MXFStructuralComponent *component = NULL; MXFPackage *package = NULL; MXFDescriptor *descriptor = NULL; + MXFEssenceGroup *essence_group; int i; - if (!essence_group || !essence_group->structural_components_count) + component = mxf_resolve_strong_ref(mxf, strong_ref, SourceClip); + if (component) + return component; + + essence_group = mxf_resolve_strong_ref(mxf, strong_ref, EssenceGroup); + if (!essence_group) return NULL; /* essence groups contains multiple representations of the same media, @@ -2302,24 +2306,7 @@ static MXFStructuralComponent* mxf_resolve_essence_group_choice(MXFContext *mxf, if (descriptor) return component; } - return NULL; -} - -static MXFStructuralComponent* mxf_resolve_sourceclip(MXFContext *mxf, UID *strong_ref) -{ - MXFStructuralComponent *component = NULL; - component = mxf_resolve_strong_ref(mxf, strong_ref, AnyType); - if (!component) - return NULL; - switch (component->meta.type) { - case SourceClip: - return component; - case EssenceGroup: - return mxf_resolve_essence_group_choice(mxf, (MXFEssenceGroup*) component); - default: - break; - } return NULL; } @@ -2498,9 +2485,10 @@ static int set_language(AVFormatContext *s, const char *rfc5646, AVDictionary ** static MXFMCASubDescriptor *find_mca_link_id(MXFContext *mxf, enum MXFMetadataSetType type, UID *mca_link_id) { - for (int k = 0; k < mxf->metadata_sets_count; k++) { - MXFMCASubDescriptor *group = (MXFMCASubDescriptor*)mxf->metadata_sets[k]; - if (group->meta.type == type && !memcmp(&group->mca_link_id, mca_link_id, 16)) + MXFMetadataSetGroup *mg = &mxf->metadata_set_groups[type]; + for (int k = 0; k < mg->metadata_sets_count; k++) { + MXFMCASubDescriptor *group = (MXFMCASubDescriptor*)mg->metadata_sets[k]; + if (!memcmp(&group->mca_link_id, mca_link_id, 16)) return group; } return NULL; @@ -2523,12 +2511,12 @@ static void parse_ffv1_sub_descriptor(MXFContext *mxf, MXFTrack *source_track, M static int parse_mca_labels(MXFContext *mxf, MXFTrack *source_track, MXFDescriptor *descriptor, AVStream *st) { - uint64_t routing[FF_SANE_NB_CHANNELS] = {0}; + AVChannelLayout *ch_layout = &st->codecpar->ch_layout; char *language = NULL; int ambigous_language = 0; enum AVAudioServiceType service_type = AV_AUDIO_SERVICE_TYPE_NB; int ambigous_service_type = 0; - int has_channel_label = 0; + int ret; for (int i = 0; i < descriptor->sub_descriptors_count; i++) { char *channel_language; @@ -2537,7 +2525,13 @@ static int parse_mca_labels(MXFContext *mxf, MXFTrack *source_track, MXFDescript if (label == NULL) continue; - has_channel_label = 1; + if (ch_layout->order == AV_CHANNEL_ORDER_UNSPEC) { + av_channel_layout_uninit(ch_layout); + ret = av_channel_layout_custom_init(ch_layout, descriptor->channels); + if (ret < 0) + return ret; + } + for (const MXFChannelOrderingUL* channel_ordering = mxf_channel_ordering; channel_ordering->uid[0]; channel_ordering++) { if (IS_KLV_KEY(channel_ordering->uid, label->mca_label_dictionary_id)) { int target_channel = label->mca_channel_id; @@ -2547,7 +2541,7 @@ static int parse_mca_labels(MXFContext *mxf, MXFTrack *source_track, MXFDescript av_log(mxf->fc, AV_LOG_ERROR, "AudioChannelLabelSubDescriptor has invalid MCA channel ID %d\n", target_channel); return AVERROR_INVALIDDATA; } - routing[target_channel - 1] = channel_ordering->layout_mask; + ch_layout->u.map[target_channel - 1].id = channel_ordering->channel; if (service_type == AV_AUDIO_SERVICE_TYPE_NB) service_type = channel_ordering->service_type; else if (service_type != channel_ordering->service_type) @@ -2585,45 +2579,19 @@ static int parse_mca_labels(MXFContext *mxf, MXFTrack *source_track, MXFDescript if (service_type != AV_AUDIO_SERVICE_TYPE_NB && service_type != AV_AUDIO_SERVICE_TYPE_MAIN && !ambigous_service_type) { enum AVAudioServiceType *ast; - uint8_t* side_data = av_stream_new_side_data(st, AV_PKT_DATA_AUDIO_SERVICE_TYPE, sizeof(*ast)); + AVPacketSideData *side_data = av_packet_side_data_new(&st->codecpar->coded_side_data, + &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_AUDIO_SERVICE_TYPE, + sizeof(*ast), 0); if (!side_data) return AVERROR(ENOMEM); - ast = (enum AVAudioServiceType*)side_data; + ast = (enum AVAudioServiceType*)side_data->data; *ast = service_type; } - if (has_channel_label) { - uint64_t channel_layout = 0; - int ret; - - for (int i = 0; i < descriptor->channels; i++) { - if (!routing[i]) { - av_log(mxf->fc, AV_LOG_WARNING, "Designation of audio channel %d in stream #%d is unknown or unsupported, " - "falling back to unknown channel layout\n", st->index, i); - return 0; - } - if (channel_layout & routing[i]) { - char buf[32]; - av_channel_name(buf, sizeof(buf), routing[i]); - av_log(mxf->fc, AV_LOG_WARNING, "%s audio channel is used multiple times in stream #%d, " - "falling back to unknown channel layout\n", - buf, st->index); - return 0; - } - if (routing[i] < channel_layout) { - av_log(mxf->fc, AV_LOG_WARNING, "stream #%d is not in in native channel order, " - "falling back to unknown channel layout\n", st->index); - return 0; - } - channel_layout |= routing[i]; - } - - av_assert0(descriptor->channels == av_popcount64(channel_layout)); - - ret = av_channel_layout_from_mask(&st->codecpar->ch_layout, channel_layout); - if (ret < 0) - return ret; - } + ret = av_channel_layout_retype(ch_layout, 0, AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL); + if (ret < 0) + return ret; return 0; } @@ -2633,7 +2601,6 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) MXFPackage *material_package = NULL; int i, j, k, ret; - av_log(mxf->fc, AV_LOG_TRACE, "metadata sets count %d\n", mxf->metadata_sets_count); /* TODO: handle multiple material packages (OP3x) */ for (i = 0; i < mxf->packages_count; i++) { material_package = mxf_resolve_strong_ref(mxf, &mxf->packages_refs[i], MaterialPackage); @@ -2774,8 +2741,7 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st->id = material_track->track_id; st->priv_data = source_track; - source_package->descriptor = mxf_resolve_strong_ref(mxf, &source_package->descriptor_ref, AnyType); - descriptor = mxf_resolve_multidescriptor(mxf, source_package->descriptor, source_track->track_id); + descriptor = mxf_resolve_descriptor(mxf, &source_package->descriptor_ref, source_track->track_id); /* A SourceClip from a EssenceGroup may only be a single frame of essence data. The clips duration is then how many * frames its suppose to repeat for. Descriptor->duration, if present, contains the real duration of the essence data */ @@ -2819,13 +2785,11 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) /* HACK: replacing the original key with mxf_encrypted_essence_container * is not allowed according to s429-6, try to find correct information anyway */ if (IS_KLV_KEY(essence_container_ul, mxf_encrypted_essence_container)) { + MXFMetadataSetGroup *mg = &mxf->metadata_set_groups[CryptoContext]; av_log(mxf->fc, AV_LOG_INFO, "broken encrypted mxf file\n"); - for (k = 0; k < mxf->metadata_sets_count; k++) { - MXFMetadataSet *metadata = mxf->metadata_sets[k]; - if (metadata->type == CryptoContext) { - essence_container_ul = &((MXFCryptoContext *)metadata)->source_container_ul; - break; - } + if (mg->metadata_sets_count) { + MXFMetadataSet *metadata = mg->metadata_sets[0]; + essence_container_ul = &((MXFCryptoContext *)metadata)->source_container_ul; } } @@ -2988,19 +2952,21 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st->codecpar->color_trc = mxf_get_codec_ul(ff_mxf_color_trc_uls, &descriptor->color_trc_ul)->id; st->codecpar->color_space = mxf_get_codec_ul(ff_mxf_color_space_uls, &descriptor->color_space_ul)->id; if (descriptor->mastering) { - ret = av_stream_add_side_data(st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, - (uint8_t *)descriptor->mastering, - sizeof(*descriptor->mastering)); - if (ret < 0) + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_MASTERING_DISPLAY_METADATA, + (uint8_t *)descriptor->mastering, sizeof(*descriptor->mastering), 0)) { + ret = AVERROR(ENOMEM); goto fail_and_free; + } descriptor->mastering = NULL; } if (descriptor->coll) { - ret = av_stream_add_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, - (uint8_t *)descriptor->coll, - descriptor->coll_size); - if (ret < 0) + if (!av_packet_side_data_add(&st->codecpar->coded_side_data, &st->codecpar->nb_coded_side_data, + AV_PKT_DATA_CONTENT_LIGHT_LEVEL, + (uint8_t *)descriptor->coll, descriptor->coll_size, 0)) { + ret = AVERROR(ENOMEM); goto fail_and_free; + } descriptor->coll = NULL; } } else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { @@ -3040,6 +3006,8 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) st->codecpar->codec_id = AV_CODEC_ID_PCM_S32BE; } else if (st->codecpar->codec_id == AV_CODEC_ID_MP2) { sti->need_parsing = AVSTREAM_PARSE_FULL; + } else if (st->codecpar->codec_id == AV_CODEC_ID_AAC) { + sti->need_parsing = AVSTREAM_PARSE_FULL; } st->codecpar->bits_per_coded_sample = av_get_bits_per_sample(st->codecpar->codec_id); @@ -3062,6 +3030,7 @@ static int mxf_parse_structural_metadata(MXFContext *mxf) if (container_ul->desc) av_dict_set(&st->metadata, "data_type", container_ul->desc, 0); if (mxf->eia608_extract && + container_ul->desc && !strcmp(container_ul->desc, "vbi_vanc_smpte_436M")) { st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; st->codecpar->codec_id = AV_CODEC_ID_EIA_608; @@ -3235,7 +3204,7 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { { { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x04,0x04,0x00 }, mxf_read_partition_pack }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x2f,0x00 }, mxf_read_preface_metadata }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x30,0x00 }, mxf_read_identification_metadata }, - { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x18,0x00 }, mxf_read_content_storage, 0, AnyType }, + { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x18,0x00 }, mxf_read_content_storage }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x37,0x00 }, mxf_read_package, sizeof(MXFPackage), SourcePackage }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x36,0x00 }, mxf_read_package, sizeof(MXFPackage), MaterialPackage }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x0f,0x00 }, mxf_read_sequence, sizeof(MXFSequence), Sequence }, @@ -3264,12 +3233,11 @@ static const MXFMetadataReadTableEntry mxf_metadata_read_table[] = { { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x04,0x01,0x02,0x02,0x00,0x00 }, mxf_read_cryptographic_context, sizeof(MXFCryptoContext), CryptoContext }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x10,0x01,0x00 }, mxf_read_index_table_segment, sizeof(MXFIndexTableSegment), IndexTableSegment }, { { 0x06,0x0e,0x2b,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x23,0x00 }, mxf_read_essence_container_data, sizeof(MXFEssenceContainerData), EssenceContainerData }, - { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, NULL, 0, AnyType }, + { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, NULL }, }; static int mxf_metadataset_init(MXFMetadataSet *ctx, enum MXFMetadataSetType type, MXFPartition *partition) { - ctx->type = type; ctx->partition_score = partition_score(partition); switch (type){ case MultipleDescriptor: @@ -3308,7 +3276,7 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF UID uid = {0}; if (next < 0 || next > INT64_MAX - size) { if (meta) { - mxf_free_metadataset(&meta, 1); + mxf_free_metadataset(&meta, type); } return next < 0 ? next : AVERROR_INVALIDDATA; } @@ -3334,7 +3302,7 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF avio_read(pb, meta->uid, 16); } else if ((ret = read_child(ctx, pb, tag, size, uid, -1)) < 0) { if (meta) { - mxf_free_metadataset(&meta, 1); + mxf_free_metadataset(&meta, type); } return ret; } @@ -3343,7 +3311,7 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF * it extending past the end of the KLV though (zzuf5.mxf). */ if (avio_tell(pb) > klv_end) { if (meta) { - mxf_free_metadataset(&meta, 1); + mxf_free_metadataset(&meta, type); } av_log(mxf->fc, AV_LOG_ERROR, @@ -3353,7 +3321,7 @@ static int mxf_read_local_tags(MXFContext *mxf, KLVPacket *klv, MXFMetadataReadF } else if (avio_tell(pb) <= next) /* only seek forward, else this can loop for a long time */ avio_seek(pb, next, SEEK_SET); } - return meta ? mxf_add_metadata_set(mxf, &meta) : 0; + return meta ? mxf_add_metadata_set(mxf, &meta, type) : 0; } /** @@ -3609,17 +3577,16 @@ static int mxf_handle_missing_index_segment(MXFContext *mxf, AVStream *st) int essence_partition_count = 0; int edit_unit_byte_count = 0; int i, ret; + MXFMetadataSetGroup *mg = &mxf->metadata_set_groups[IndexTableSegment]; if (!track || track->wrapping != ClipWrapped) return 0; /* check if track already has an IndexTableSegment */ - for (i = 0; i < mxf->metadata_sets_count; i++) { - if (mxf->metadata_sets[i]->type == IndexTableSegment) { - MXFIndexTableSegment *s = (MXFIndexTableSegment*)mxf->metadata_sets[i]; - if (s->body_sid == track->body_sid) - return 0; - } + for (i = 0; i < mg->metadata_sets_count; i++) { + MXFIndexTableSegment *s = (MXFIndexTableSegment*)mg->metadata_sets[i]; + if (s->body_sid == track->body_sid) + return 0; } /* find the essence partition */ @@ -3651,7 +3618,7 @@ static int mxf_handle_missing_index_segment(MXFContext *mxf, AVStream *st) if (!(segment = av_mallocz(sizeof(*segment)))) return AVERROR(ENOMEM); - if ((ret = mxf_add_metadata_set(mxf, (MXFMetadataSet**)&segment))) + if ((ret = mxf_add_metadata_set(mxf, (MXFMetadataSet**)&segment, IndexTableSegment))) return ret; /* Make sure we have nonzero unique index_sid, body_sid will be ok, because @@ -3659,7 +3626,6 @@ static int mxf_handle_missing_index_segment(MXFContext *mxf, AVStream *st) if (!track->index_sid) track->index_sid = track->body_sid; - segment->meta.type = IndexTableSegment; /* stream will be treated as small EditUnitByteCount */ segment->edit_unit_byte_count = edit_unit_byte_count; segment->index_start_position = 0; @@ -3750,7 +3716,10 @@ static int mxf_read_header(AVFormatContext *s) while (!avio_feof(s->pb)) { const MXFMetadataReadTableEntry *metadata; - if (klv_read_packet(mxf, &klv, s->pb) < 0) { + ret = klv_read_packet(mxf, &klv, s->pb); + if (ret < 0 || IS_KLV_KEY(klv.key, ff_mxf_random_index_pack_key)) { + if (ret >= 0 && avio_size(s->pb) > klv.next_klv) + av_log(s, AV_LOG_WARNING, "data after the RandomIndexPack, assuming end of file\n"); /* EOF - seek to previous partition or stop */ if(mxf_parse_handle_partition_or_eof(mxf) <= 0) break; @@ -3850,8 +3819,7 @@ static int mxf_get_next_track_edit_unit(MXFContext *mxf, MXFTrack *track, int64_ a = -1; b = track->original_duration; - - while (b - a > 1) { + while (b - 1 > a) { m = (a + b) >> 1; if (mxf_edit_unit_absolute_offset(mxf, t, m, track->edit_rate, NULL, &offset, NULL, 0) < 0) return -1; @@ -4125,12 +4093,14 @@ static int mxf_read_close(AVFormatContext *s) for (i = 0; i < s->nb_streams; i++) s->streams[i]->priv_data = NULL; - for (i = 0; i < mxf->metadata_sets_count; i++) { - mxf_free_metadataset(mxf->metadata_sets + i, 1); + for (int type = 0; type < FF_ARRAY_ELEMS(mxf->metadata_set_groups); type++) { + MXFMetadataSetGroup *mg = &mxf->metadata_set_groups[type]; + for (i = 0; i < mg->metadata_sets_count; i++) + mxf_free_metadataset(mg->metadata_sets + i, type); + mg->metadata_sets_count = 0; + av_freep(&mg->metadata_sets); } - mxf->metadata_sets_count = 0; av_freep(&mxf->partitions); - av_freep(&mxf->metadata_sets); av_freep(&mxf->aesc); av_freep(&mxf->local_tags); @@ -4299,16 +4269,16 @@ static const AVClass demuxer_class = { .category = AV_CLASS_CATEGORY_DEMUXER, }; -const AVInputFormat ff_mxf_demuxer = { - .name = "mxf", - .long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"), - .flags = AVFMT_SEEK_TO_PTS, +const FFInputFormat ff_mxf_demuxer = { + .p.name = "mxf", + .p.long_name = NULL_IF_CONFIG_SMALL("MXF (Material eXchange Format)"), + .p.flags = AVFMT_SEEK_TO_PTS, + .p.priv_class = &demuxer_class, .priv_data_size = sizeof(MXFContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = mxf_probe, .read_header = mxf_read_header, .read_packet = mxf_read_packet, .read_close = mxf_read_close, .read_seek = mxf_read_seek, - .priv_class = &demuxer_class, }; diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c index a29d678098c..d4fa2bc5bb8 100644 --- a/libavformat/mxfenc.c +++ b/libavformat/mxfenc.c @@ -47,10 +47,13 @@ #include "libavutil/mastering_display_metadata.h" #include "libavutil/pixdesc.h" #include "libavutil/time_internal.h" -#include "libavcodec/avcodec.h" +#include "libavcodec/defs.h" +#include "libavcodec/bytestream.h" #include "libavcodec/golomb.h" #include "libavcodec/h264.h" +#include "libavcodec/jpeg2000.h" #include "libavcodec/packet_internal.h" +#include "libavcodec/rangecoder.h" #include "libavcodec/startcode.h" #include "avformat.h" #include "avio_internal.h" @@ -77,6 +80,20 @@ typedef struct MXFIndexEntry { uint8_t flags; } MXFIndexEntry; +typedef struct j2k_info_t { + uint16_t j2k_cap; ///< j2k required decoder capabilities + uint16_t j2k_rsiz; ///< j2k required decoder capabilities (Rsiz) + uint32_t j2k_xsiz; ///< j2k width of the reference grid (Xsiz) + uint32_t j2k_ysiz; ///< j2k height of the reference grid (Ysiz) + uint32_t j2k_x0siz; ///< j2k horizontal offset from the origin of the reference grid to the left side of the image (X0siz) + uint32_t j2k_y0siz; ///< j2k vertical offset from the origin of the reference grid to the left side of the image (Y0siz) + uint32_t j2k_xtsiz; ///< j2k width of one reference tile with respect to the reference grid (XTsiz) + uint32_t j2k_ytsiz; ///< j2k height of one reference tile with respect to the reference grid (YTsiz) + uint32_t j2k_xt0siz; ///< j2k horizontal offset from the origin of the reference grid to the left side of the first tile (XT0siz) + uint32_t j2k_yt0siz; ///< j2k vertical offset from the origin of the reference grid to the left side of the first tile (YT0siz) + uint8_t j2k_comp_desc[12]; ///< j2k components descriptor (Ssiz(i), XRsiz(i), YRsiz(i)) +} j2k_info_t; + typedef struct MXFStreamContext { int64_t pkt_cnt; ///< pkt counter for muxed packets UID track_essence_element_key; @@ -102,6 +119,8 @@ typedef struct MXFStreamContext { int b_picture_count; ///< maximum number of consecutive b pictures, used in mpeg-2 descriptor int low_delay; ///< low delay, used in mpeg-2 descriptor int avc_intra; + int micro_version; ///< format micro_version, used in ffv1 descriptor + j2k_info_t j2k_info; } MXFStreamContext; typedef struct MXFContainerEssenceEntry { @@ -130,6 +149,7 @@ enum ULIndex { INDEX_H264, INDEX_S436M, INDEX_PRORES, + INDEX_FFV1, }; static const struct { @@ -144,6 +164,7 @@ static const struct { { AV_CODEC_ID_JPEG2000, INDEX_JPEG2000 }, { AV_CODEC_ID_H264, INDEX_H264 }, { AV_CODEC_ID_PRORES, INDEX_PRORES }, + { AV_CODEC_ID_FFV1, INDEX_FFV1 }, { AV_CODEC_ID_NONE } }; @@ -151,6 +172,7 @@ static void mxf_write_wav_desc(AVFormatContext *s, AVStream *st); static void mxf_write_aes3_desc(AVFormatContext *s, AVStream *st); static void mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st); static void mxf_write_h264_desc(AVFormatContext *s, AVStream *st); +static void mxf_write_ffv1_desc(AVFormatContext *s, AVStream *st); static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st); static void mxf_write_generic_sound_desc(AVFormatContext *s, AVStream *st); static void mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st); @@ -208,6 +230,11 @@ static const MXFContainerEssenceEntry mxf_essence_container_uls[] = { { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x17,0x00 }, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x03,0x00 }, mxf_write_cdci_desc }, + // FFV1 + { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x0d,0x01,0x03,0x01,0x02,0x23,0x01,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x1d,0x00 }, + { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x09,0x00,0x00 }, + mxf_write_ffv1_desc }, { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, @@ -232,6 +259,15 @@ static const UID mxf_d10_container_uls[] = { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x01,0x06,0x01 }, // D-10 525/50 NTSC 30mb/s }; + +static const UID mxf_ffv1_codec_uls[] = { + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0D,0x04,0x01,0x02,0x02,0x03,0x09,0x01,0x00 }, // FFV1 version 0 + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0D,0x04,0x01,0x02,0x02,0x03,0x09,0x02,0x00 }, // FFV1 version 1 + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0D,0x04,0x01,0x02,0x02,0x03,0x09,0x03,0x00 }, // FFV1 version 2 (was only experimental) + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0D,0x04,0x01,0x02,0x02,0x03,0x09,0x04,0x00 }, // FFV1 version 3 + { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x0D,0x04,0x01,0x02,0x02,0x03,0x09,0x05,0x00 }, // FFV1 version 4 +}; + static const uint8_t product_uid[] = { 0xAD,0xAB,0x44,0x24,0x2f,0x25,0x4d,0xc7,0x92,0xff,0x29,0xbd,0x00,0x0c,0x00,0x02}; static const uint8_t uuid_base[] = { 0xAD,0xAB,0x44,0x24,0x2f,0x25,0x4d,0xc7,0x92,0xff }; static const uint8_t umid_ul[] = { 0x06,0x0A,0x2B,0x34,0x01,0x01,0x01,0x05,0x01,0x01,0x0D,0x00,0x13 }; @@ -390,6 +426,24 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = { { 0x8302, FF_MXF_MasteringDisplayWhitePointChromaticity }, { 0x8303, FF_MXF_MasteringDisplayMaximumLuminance }, { 0x8304, FF_MXF_MasteringDisplayMinimumLuminance }, + // FFV1 + { 0xDFD9, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x0C,0x06,0x00,0x00,0x00}}, /* FFV1 Micro-version */ + { 0xDFDA, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x0C,0x05,0x00,0x00,0x00}}, /* FFV1 Version */ + { 0xDFDB, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x0C,0x01,0x00,0x00,0x00}}, /* FFV1 Initialization Metadata */ + // ff_mxf_jpeg2000_local_tags + { 0x8400, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x04,0x06,0x10,0x00,0x00}}, /* Sub Descriptors / Opt Ordered array of strong references to sub descriptor sets */ + { 0x8401, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x01,0x00,0x00,0x00}}, /* Rsiz: An enumerated value that defines the decoder capabilities */ + { 0x8402, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x02,0x00,0x00,0x00}}, /* Xsiz: Width of the reference grid */ + { 0x8403, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x03,0x00,0x00,0x00}}, /* Ysiz: Height of the reference grid */ + { 0x8404, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x04,0x00,0x00,0x00}}, /* X0siz: Horizontal offset from the origin of the reference grid to the left side of the image area */ + { 0x8405, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x05,0x00,0x00,0x00}}, /* Y0siz: Vertical offset from the origin of the reference grid to the left side of the image area */ + { 0x8406, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x06,0x00,0x00,0x00}}, /* XTsiz: Width of one reference tile with respect to the reference grid */ + { 0x8407, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x07,0x00,0x00,0x00}}, /* YTsiz: Height of one reference tile with respect to the reference grid */ + { 0x8408, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x08,0x00,0x00,0x00}}, /* XT0siz: Horizontal offset from the origin of the reference grid to the left side of the first tile */ + { 0x8409, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x09,0x00,0x00,0x00}}, /* YT0siz: Vertical offset from the origin of the reference grid to the left side of the first tile */ + { 0x840A, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x0A,0x00,0x00,0x00}}, /* Csiz: The number of components in the picture */ + { 0x840B, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x0B,0x00,0x00,0x00}}, /* Ssizi, XRSizi, YRSizi: Array of picture components where each component comprises 3 bytes named Ssizi, XRSizi, YRSizi. The array of 3-byte groups is preceded by the array header comprising a 4-byte value of the number of components followed by a 4-byte value of 3. */ + { 0x840C, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x0E,0x00,0x00,0x00}}, /* The nature and order of the image components in the compressed domain as carried in the J2C codestream. */ }; #define MXF_NUM_TAGS FF_ARRAY_ELEMS(mxf_local_tag_batch) @@ -526,16 +580,24 @@ static void mxf_write_primer_pack(AVFormatContext *s) MXFContext *mxf = s->priv_data; AVIOContext *pb = s->pb; int local_tag_number = MXF_NUM_TAGS, i; - int will_have_avc_tags = 0, will_have_mastering_tags = 0; + int will_have_avc_tags = 0, will_have_mastering_tags = 0, will_have_ffv1_tags = 0, will_have_jpeg2000_tags = 0; for (i = 0; i < s->nb_streams; i++) { MXFStreamContext *sc = s->streams[i]->priv_data; if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_H264 && !sc->avc_intra) { will_have_avc_tags = 1; } - if (av_stream_get_side_data(s->streams[i], AV_PKT_DATA_MASTERING_DISPLAY_METADATA, NULL)) { + if (av_packet_side_data_get(s->streams[i]->codecpar->coded_side_data, + s->streams[i]->codecpar->nb_coded_side_data, + AV_PKT_DATA_MASTERING_DISPLAY_METADATA)) { will_have_mastering_tags = 1; } + if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_FFV1) { + will_have_ffv1_tags = 1; + } + if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_JPEG2000){ + will_have_jpeg2000_tags = 1; + } } if (!mxf->store_user_comments) { @@ -544,8 +606,11 @@ static void mxf_write_primer_pack(AVFormatContext *s) mxf_mark_tag_unused(mxf, 0x5003); } - if (!will_have_avc_tags) { + if (!will_have_avc_tags && !will_have_ffv1_tags) { mxf_mark_tag_unused(mxf, 0x8100); + } + + if (!will_have_avc_tags) { mxf_mark_tag_unused(mxf, 0x8200); mxf_mark_tag_unused(mxf, 0x8201); mxf_mark_tag_unused(mxf, 0x8202); @@ -558,6 +623,28 @@ static void mxf_write_primer_pack(AVFormatContext *s) mxf_mark_tag_unused(mxf, 0x8304); } + if (!will_have_ffv1_tags) { + mxf_mark_tag_unused(mxf, 0xDFD9); + mxf_mark_tag_unused(mxf, 0xDFDA); + mxf_mark_tag_unused(mxf, 0xDFDB); + } + + if (!will_have_jpeg2000_tags) { + mxf_mark_tag_unused(mxf, 0x8400); + mxf_mark_tag_unused(mxf, 0x8401); + mxf_mark_tag_unused(mxf, 0x8402); + mxf_mark_tag_unused(mxf, 0x8403); + mxf_mark_tag_unused(mxf, 0x8404); + mxf_mark_tag_unused(mxf, 0x8405); + mxf_mark_tag_unused(mxf, 0x8406); + mxf_mark_tag_unused(mxf, 0x8407); + mxf_mark_tag_unused(mxf, 0x8408); + mxf_mark_tag_unused(mxf, 0x8409); + mxf_mark_tag_unused(mxf, 0x840A); + mxf_mark_tag_unused(mxf, 0x840B); + mxf_mark_tag_unused(mxf, 0x840C); + } + for (i = 0; i < MXF_NUM_TAGS; i++) { if (mxf->unused_tags[i]) { local_tag_number--; @@ -1094,9 +1181,12 @@ static const UID mxf_mpegvideo_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53, static const UID mxf_wav_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x48,0x00 }; static const UID mxf_aes3_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x47,0x00 }; static const UID mxf_cdci_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x28,0x00 }; +static const UID mxf_rgba_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x29,0x00 }; static const UID mxf_generic_sound_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x42,0x00 }; static const UID mxf_avc_subdescriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6E,0x00 }; +static const UID mxf_ffv1_subdescriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x81,0x03 }; +static const UID mxf_jpeg2000_subdescriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x5A,0x00}; static inline uint16_t rescale_mastering_chroma(AVRational q) { @@ -1112,15 +1202,16 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID { MXFStreamContext *sc = st->priv_data; AVIOContext *pb = s->pb; - int stored_width = 0; - int stored_height = (st->codecpar->height+15)/16*16; + int stored_width = st->codecpar->width; + int stored_height = st->codecpar->height; + int display_width; int display_height; int f1, f2; const MXFCodecUL *color_primaries_ul; const MXFCodecUL *color_trc_ul; const MXFCodecUL *color_space_ul; int64_t pos = mxf_write_generic_desc(s, st, key); - uint8_t *side_data; + const AVPacketSideData *side_data; color_primaries_ul = mxf_get_codec_ul_by_id(ff_mxf_color_primaries_uls, st->codecpar->color_primaries); color_trc_ul = mxf_get_codec_ul_by_id(ff_mxf_color_trc_uls, st->codecpar->color_trc); @@ -1132,12 +1223,24 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID else if (st->codecpar->height == 720) stored_width = 1280; } - if (!stored_width) - stored_width = (st->codecpar->width+15)/16*16; + display_width = stored_width; + + switch (st->codecpar->codec_id) { + case AV_CODEC_ID_MPEG2VIDEO: + case AV_CODEC_ID_H264: + //Based on 16x16 macroblocks + stored_width = (stored_width+15)/16*16; + stored_height = (stored_height+15)/16*16; + break; + default: + break; + } + //Stored width mxf_write_local_tag(s, 4, 0x3203); avio_wb32(pb, stored_width); + //Stored height mxf_write_local_tag(s, 4, 0x3202); avio_wb32(pb, stored_height>>sc->interlaced); @@ -1157,7 +1260,7 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID //Sampled width mxf_write_local_tag(s, 4, 0x3205); - avio_wb32(pb, stored_width); + avio_wb32(pb, display_width); //Samples height mxf_write_local_tag(s, 4, 0x3204); @@ -1171,8 +1274,9 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID mxf_write_local_tag(s, 4, 0x3207); avio_wb32(pb, 0); + //Display width mxf_write_local_tag(s, 4, 0x3209); - avio_wb32(pb, stored_width); + avio_wb32(pb, display_width); if (st->codecpar->height == 608) // PAL + VBI display_height = 576; @@ -1181,6 +1285,7 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID else display_height = st->codecpar->height; + //Display height mxf_write_local_tag(s, 4, 0x3208); avio_wb32(pb, display_height>>sc->interlaced); @@ -1198,41 +1303,43 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID avio_wb32(pb, -((st->codecpar->height - display_height)&1)); } - // component depth - mxf_write_local_tag(s, 4, 0x3301); - avio_wb32(pb, sc->component_depth); + if (key != mxf_rgba_descriptor_key) { + // component depth + mxf_write_local_tag(s, 4, 0x3301); + avio_wb32(pb, sc->component_depth); - // horizontal subsampling - mxf_write_local_tag(s, 4, 0x3302); - avio_wb32(pb, sc->h_chroma_sub_sample); + // horizontal subsampling + mxf_write_local_tag(s, 4, 0x3302); + avio_wb32(pb, sc->h_chroma_sub_sample); - // vertical subsampling - mxf_write_local_tag(s, 4, 0x3308); - avio_wb32(pb, sc->v_chroma_sub_sample); + // vertical subsampling + mxf_write_local_tag(s, 4, 0x3308); + avio_wb32(pb, sc->v_chroma_sub_sample); - // color siting - mxf_write_local_tag(s, 1, 0x3303); - avio_w8(pb, sc->color_siting); + // color siting + mxf_write_local_tag(s, 1, 0x3303); + avio_w8(pb, sc->color_siting); - // Padding Bits - mxf_write_local_tag(s, 2, 0x3307); - avio_wb16(pb, 0); + // Padding Bits + mxf_write_local_tag(s, 2, 0x3307); + avio_wb16(pb, 0); - if (st->codecpar->color_range != AVCOL_RANGE_UNSPECIFIED) { - int black = 0, - white = (1<component_depth) - 1, - color = (1<component_depth); - if (st->codecpar->color_range == AVCOL_RANGE_MPEG) { - black = 1 << (sc->component_depth - 4); - white = 235 << (sc->component_depth - 8); - color = (14 << (sc->component_depth - 4)) + 1; + if (st->codecpar->color_range != AVCOL_RANGE_UNSPECIFIED) { + int black = 0, + white = (1<component_depth) - 1, + color = (1<component_depth); + if (st->codecpar->color_range == AVCOL_RANGE_MPEG) { + black = 1 << (sc->component_depth - 4); + white = 235 << (sc->component_depth - 8); + color = (14 << (sc->component_depth - 4)) + 1; + } + mxf_write_local_tag(s, 4, 0x3304); + avio_wb32(pb, black); + mxf_write_local_tag(s, 4, 0x3305); + avio_wb32(pb, white); + mxf_write_local_tag(s, 4, 0x3306); + avio_wb32(pb, color); } - mxf_write_local_tag(s, 4, 0x3304); - avio_wb32(pb, black); - mxf_write_local_tag(s, 4, 0x3305); - avio_wb32(pb, white); - mxf_write_local_tag(s, 4, 0x3306); - avio_wb32(pb, color); } if (sc->signal_standard) { @@ -1290,9 +1397,11 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID avio_write(pb, *sc->codec_ul, 16); // Mastering Display metadata - side_data = av_stream_get_side_data(st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, NULL); + side_data = av_packet_side_data_get(st->codecpar->coded_side_data, + st->codecpar->nb_coded_side_data, + AV_PKT_DATA_MASTERING_DISPLAY_METADATA); if (side_data) { - const AVMasteringDisplayMetadata *metadata = (const AVMasteringDisplayMetadata*)side_data; + const AVMasteringDisplayMetadata *metadata = (const AVMasteringDisplayMetadata*)side_data->data; if (metadata->has_primaries) { mxf_write_local_tag(s, 12, 0x8301); avio_wb16(pb, rescale_mastering_chroma(metadata->display_primaries[0][0])); @@ -1329,6 +1438,13 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID mxf_write_uuid(pb, AVCSubDescriptor, 0); } + if (st->codecpar->codec_id == AV_CODEC_ID_FFV1) { + // write ffv1 sub descriptor ref + mxf_write_local_tag(s, 8 + 16, 0x8100); + mxf_write_refs_count(pb, 1); + mxf_write_uuid(pb, FFV1SubDescriptor, 0); + } + return pos; } @@ -1365,6 +1481,63 @@ static void mxf_write_avc_subdesc(AVFormatContext *s, AVStream *st) mxf_update_klv_size(s->pb, pos); } +static void mxf_write_jpeg2000_subdesc(AVFormatContext *s, AVStream *st) +{ + MXFStreamContext *sc = st->priv_data; + AVIOContext *pb = s->pb; + int64_t pos; + int component_count = av_pix_fmt_count_planes(st->codecpar->format); + + /* JPEG2000 subdescriptor key */ + avio_write(pb, mxf_jpeg2000_subdescriptor_key, 16); + klv_encode_ber4_length(pb, 0); + pos = avio_tell(pb); + + mxf_write_local_tag(s, 16, 0x3C0A); + mxf_write_uuid(pb, JPEG2000SubDescriptor, 0); + + /* Value defining the decoder capabilities (rsiz) */ + mxf_write_local_tag(s, 2, 0x8401); + avio_wb16(pb, sc->j2k_info.j2k_rsiz); + /* Width of the JPEG2000 reference grid (Xsiz) */ + mxf_write_local_tag(s, 4, 0x8402); + avio_wb32(pb, st->codecpar->width); + /* Height of the JPEG2000 reference grid (Ysiz) */ + mxf_write_local_tag(s, 4, 0x8403); + avio_wb32(pb, st->codecpar->height); + /* Horizontal offset from the reference grid origin to the left side of the image area (X0siz) */ + mxf_write_local_tag(s, 4, 0x8404); + avio_wb32(pb, sc->j2k_info.j2k_x0siz); + /* Vertical offset from the reference grid origin to the left side of the image area (Y0siz) */ + mxf_write_local_tag(s, 4, 0x8405); + avio_wb32(pb, sc->j2k_info.j2k_y0siz); + /* Width of one reference tile with respect to the reference grid (XTsiz) */ + mxf_write_local_tag(s, 4, 0x8406); + avio_wb32(pb, sc->j2k_info.j2k_xtsiz); + /* Height of one reference tile with respect to the reference grid (YTsiz) */ + mxf_write_local_tag(s, 4, 0x8407); + avio_wb32(pb, sc->j2k_info.j2k_ytsiz); + /* Horizontal offset from the origin of the reference grid to the left side of the first tile (XT0siz) */ + mxf_write_local_tag(s, 4, 0x8408); + avio_wb32(pb, sc->j2k_info.j2k_xt0siz); + /* Vertical offset from the origin of the reference grid to the left side of the first tile (YT0siz) */ + mxf_write_local_tag(s, 4, 0x8409); + avio_wb32(pb, sc->j2k_info.j2k_yt0siz); + /* Image components number (Csiz) */ + mxf_write_local_tag(s, 2, 0x840A); + avio_wb16(pb, component_count); + /* Array of picture components where each component comprises 3 bytes named Ssiz(i) (Pixel bitdepth - 1), + XRSiz(i) (Horizontal sampling), YRSiz(i) (Vertical sampling). The array of 3-byte groups is preceded + by the array header comprising a 4-byte value of the number of components followed by a 4-byte + value of 3. */ + mxf_write_local_tag(s, 8 + 3*component_count, 0x840B); + avio_wb32(pb, component_count); + avio_wb32(pb, 3); + avio_write(pb, sc->j2k_info.j2k_comp_desc, 3*component_count); + + mxf_update_klv_size(pb, pos); +} + static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st) { int64_t pos = mxf_write_cdci_common(s, st, mxf_cdci_descriptor_key); @@ -1373,6 +1546,9 @@ static void mxf_write_cdci_desc(AVFormatContext *s, AVStream *st) if (st->codecpar->codec_id == AV_CODEC_ID_H264) { mxf_write_avc_subdesc(s, st); } + if (st->codecpar->codec_id == AV_CODEC_ID_JPEG2000) { + mxf_write_jpeg2000_subdesc(s, st); + } } static void mxf_write_h264_desc(AVFormatContext *s, AVStream *st) @@ -1387,6 +1563,47 @@ static void mxf_write_h264_desc(AVFormatContext *s, AVStream *st) } } +static void mxf_write_ffv1_subdesc(AVFormatContext *s, AVStream *st) +{ + AVIOContext *pb = s->pb; + MXFStreamContext *sc = st->priv_data; + int64_t pos; + + avio_write(pb, mxf_ffv1_subdescriptor_key, 16); + klv_encode_ber4_length(pb, 0); + pos = avio_tell(pb); + + mxf_write_local_tag(s, 16, 0x3C0A); + mxf_write_uuid(pb, FFV1SubDescriptor, 0); + + if (st->codecpar->extradata_size) { + mxf_write_local_tag(s, st->codecpar->extradata_size, 0xDFDB); + avio_write(pb, st->codecpar->extradata, st->codecpar->extradata_size); // FFV1InitializationMetadata + } + + mxf_write_local_tag(s, 2, 0xDFDA); + avio_wb16(pb, (*sc->codec_ul)[14]); // FFV1Version + + if (st->codecpar->extradata_size) { + mxf_write_local_tag(s, 2, 0xDFD9); + avio_wb16(pb, sc->micro_version); // FFV1MicroVersion + } + + mxf_update_klv_size(s->pb, pos); +} + +static void mxf_write_ffv1_desc(AVFormatContext *s, AVStream *st) +{ + int is_rgb, pos; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(st->codecpar->format); + av_assert0(desc); + is_rgb = desc->flags & AV_PIX_FMT_FLAG_RGB; + + pos = mxf_write_cdci_common(s, st, is_rgb ? mxf_rgba_descriptor_key : mxf_cdci_descriptor_key); + mxf_update_klv_size(s->pb, pos); + mxf_write_ffv1_subdesc(s, st); +} + static void mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st) { int64_t pos = mxf_write_generic_desc(s, st, mxf_s436m_anc_descriptor_key); @@ -2009,12 +2226,12 @@ static const struct { int profile; UID codec_ul; } mxf_prores_codec_uls[] = { - { FF_PROFILE_PRORES_PROXY, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x01,0x00 } }, - { FF_PROFILE_PRORES_LT, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x02,0x00 } }, - { FF_PROFILE_PRORES_STANDARD, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x03,0x00 } }, - { FF_PROFILE_PRORES_HQ, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x04,0x00 } }, - { FF_PROFILE_PRORES_4444, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x05,0x00 } }, - { FF_PROFILE_PRORES_XQ, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x06,0x00 } }, + { AV_PROFILE_PRORES_PROXY, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x01,0x00 } }, + { AV_PROFILE_PRORES_LT, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x02,0x00 } }, + { AV_PROFILE_PRORES_STANDARD, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x03,0x00 } }, + { AV_PROFILE_PRORES_HQ, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x04,0x00 } }, + { AV_PROFILE_PRORES_4444, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x05,0x00 } }, + { AV_PROFILE_PRORES_XQ, { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x06,0x06,0x00 } }, }; static int mxf_parse_prores_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt) @@ -2347,6 +2564,122 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st, return 1; } +static inline int get_ffv1_unsigned_symbol(RangeCoder *c, uint8_t *state) { + if(get_rac(c, state+0)) + return 0; + else{ + int i, e; + unsigned a; + e= 0; + while(get_rac(c, state+1 + FFMIN(e,9))){ //1..10 + e++; + if (e > 31) + return AVERROR_INVALIDDATA; + } + + a= 1; + for(i=e-1; i>=0; i--){ + a += a + get_rac(c, state+22 + FFMIN(i,9)); //22..31 + } + + return a; + } +} +#define FFV1_CONTEXT_SIZE 32 +static int mxf_parse_ffv1_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + MXFContext *mxf = s->priv_data; + MXFStreamContext *sc = st->priv_data; + uint8_t state[FFV1_CONTEXT_SIZE]; + RangeCoder c; + unsigned v; + + sc->frame_size = pkt->size; + + if (mxf->header_written) + return 1; + + memset(state, 128, sizeof(state)); + if (st->codecpar->extradata) { + ff_init_range_decoder(&c, st->codecpar->extradata, st->codecpar->extradata_size); + ff_build_rac_states(&c, 0.05 * (1LL << 32), 256 - 8); + v = get_ffv1_unsigned_symbol(&c, state); + av_assert0(v >= 2); + if (v > 4) { + av_log(s, AV_LOG_ERROR, "unsupported ffv1 version %d\n", v); + return 0; + } + sc->micro_version = get_ffv1_unsigned_symbol(&c, state); + } else { + uint8_t keystate = 128; + ff_init_range_decoder(&c, pkt->data, pkt->size); + ff_build_rac_states(&c, 0.05 * (1LL << 32), 256 - 8); + get_rac(&c, &keystate); // keyframe + v = get_ffv1_unsigned_symbol(&c, state); + av_assert0(v < 2); + } + sc->codec_ul = &mxf_ffv1_codec_uls[v]; + + if (st->codecpar->field_order > AV_FIELD_PROGRESSIVE) { + sc->interlaced = 1; + sc->field_dominance = st->codecpar->field_order == (st->codecpar->field_order == AV_FIELD_TT || st->codecpar->field_order == AV_FIELD_TB) ? 1 : 2; + } + sc->aspect_ratio.num = st->codecpar->width * st->codecpar->sample_aspect_ratio.num; + sc->aspect_ratio.den = st->codecpar->height * st->codecpar->sample_aspect_ratio.den; + av_reduce(&sc->aspect_ratio.num, &sc->aspect_ratio.den, + sc->aspect_ratio.num, sc->aspect_ratio.den, INT_MAX); + + return 1; +} + +static int mxf_parse_jpeg2000_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt) +{ + MXFContext *mxf = s->priv_data; + MXFStreamContext *sc = st->priv_data; + int component_count = av_pix_fmt_count_planes(st->codecpar->format); + GetByteContext g; + uint32_t j2k_ncomponents; + + if (mxf->header_written) + return 1; + + bytestream2_init(&g,pkt->data,pkt->size); + + while (bytestream2_get_bytes_left(&g) >= 3 && bytestream2_peek_be16(&g) != JPEG2000_SOC) + bytestream2_skip(&g, 1); + + if (bytestream2_get_be16u(&g) != JPEG2000_SOC) { + av_log(s, AV_LOG_ERROR, "Mandatory SOC marker is not present\n"); + return AVERROR_INVALIDDATA; + } + + /* Extract usefull size information from the SIZ marker */ + if (bytestream2_get_be16u(&g) != JPEG2000_SIZ) { + av_log(s, AV_LOG_ERROR, "Mandatory SIZ marker is not present\n"); + return AVERROR_INVALIDDATA; + } + bytestream2_skip(&g, 2); // Skip Lsiz + sc->j2k_info.j2k_cap = bytestream2_get_be16u(&g); + sc->j2k_info.j2k_xsiz = bytestream2_get_be32u(&g); + sc->j2k_info.j2k_ysiz = bytestream2_get_be32u(&g); + sc->j2k_info.j2k_x0siz = bytestream2_get_be32u(&g); + sc->j2k_info.j2k_y0siz = bytestream2_get_be32u(&g); + sc->j2k_info.j2k_xtsiz = bytestream2_get_be32u(&g); + sc->j2k_info.j2k_ytsiz = bytestream2_get_be32u(&g); + sc->j2k_info.j2k_xt0siz = bytestream2_get_be32u(&g); + sc->j2k_info.j2k_yt0siz = bytestream2_get_be32u(&g); + j2k_ncomponents = bytestream2_get_be16u(&g); + if (j2k_ncomponents != component_count) { + av_log(s, AV_LOG_ERROR, "Incoherence about components image number.\n"); + return AVERROR_INVALIDDATA; + } + bytestream2_get_bufferu(&g, sc->j2k_info.j2k_comp_desc, 3 * j2k_ncomponents); + + sc->frame_size = pkt->size; + + return 1; +} + static const UID mxf_mpeg2_codec_uls[] = { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x01,0x10,0x00 }, // MP-ML I-Frame { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x01,0x11,0x00 }, // MP-ML Long GOP @@ -2958,6 +3291,16 @@ static int mxf_write_packet(AVFormatContext *s, AVPacket *pkt) av_log(s, AV_LOG_ERROR, "could not get h264 profile\n"); return -1; } + } else if (st->codecpar->codec_id == AV_CODEC_ID_FFV1) { + if (!mxf_parse_ffv1_frame(s, st, pkt)) { + av_log(s, AV_LOG_ERROR, "could not get ffv1 version\n"); + return -1; + } + } else if (st->codecpar->codec_id == AV_CODEC_ID_JPEG2000) { + if (!mxf_parse_jpeg2000_frame(s, st, pkt)) { + av_log(s, AV_LOG_ERROR, "could not get jpeg2000 profile\n"); + return -1; + } } if (mxf->cbr_index) { @@ -3196,23 +3539,33 @@ static int mxf_interleave(AVFormatContext *s, AVPacket *pkt, return mxf_interleave_get_packet(s, pkt, flush); } +static int mxf_check_bitstream(AVFormatContext *s, AVStream *st, const AVPacket *pkt) +{ + if (st->codecpar->codec_id == AV_CODEC_ID_H264) { + if (pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 && + AV_RB24(pkt->data) != 0x000001) + return ff_stream_add_bitstream_filter(st, "h264_mp4toannexb", NULL); + } + return 1; +} + #define MXF_COMMON_OPTIONS \ { "signal_standard", "Force/set Signal Standard",\ - offsetof(MXFContext, signal_standard), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\ + offsetof(MXFContext, signal_standard), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, .unit = "signal_standard"},\ { "bt601", "ITU-R BT.601 and BT.656, also SMPTE 125M (525 and 625 line interlaced)",\ - 0, AV_OPT_TYPE_CONST, {.i64 = 1}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\ + 0, AV_OPT_TYPE_CONST, {.i64 = 1}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, .unit = "signal_standard"},\ { "bt1358", "ITU-R BT.1358 and ITU-R BT.799-3, also SMPTE 293M (525 and 625 line progressive)",\ - 0, AV_OPT_TYPE_CONST, {.i64 = 2}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\ + 0, AV_OPT_TYPE_CONST, {.i64 = 2}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, .unit = "signal_standard"},\ { "smpte347m", "SMPTE 347M (540 Mbps mappings)",\ - 0, AV_OPT_TYPE_CONST, {.i64 = 3}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\ + 0, AV_OPT_TYPE_CONST, {.i64 = 3}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, .unit = "signal_standard"},\ { "smpte274m", "SMPTE 274M (1125 line)",\ - 0, AV_OPT_TYPE_CONST, {.i64 = 4}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\ + 0, AV_OPT_TYPE_CONST, {.i64 = 4}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, .unit = "signal_standard"},\ { "smpte296m", "SMPTE 296M (750 line progressive)",\ - 0, AV_OPT_TYPE_CONST, {.i64 = 5}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\ + 0, AV_OPT_TYPE_CONST, {.i64 = 5}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, .unit = "signal_standard"},\ { "smpte349m", "SMPTE 349M (1485 Mbps mappings)",\ - 0, AV_OPT_TYPE_CONST, {.i64 = 6}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"},\ + 0, AV_OPT_TYPE_CONST, {.i64 = 6}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, .unit = "signal_standard"},\ { "smpte428", "SMPTE 428-1 DCDM",\ - 0, AV_OPT_TYPE_CONST, {.i64 = 7}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, "signal_standard"}, + 0, AV_OPT_TYPE_CONST, {.i64 = 7}, -1, 7, AV_OPT_FLAG_ENCODING_PARAM, .unit = "signal_standard"}, @@ -3277,6 +3630,7 @@ const FFOutputFormat ff_mxf_muxer = { .p.flags = AVFMT_NOTIMESTAMPS, .interleave_packet = mxf_interleave, .p.priv_class = &mxf_muxer_class, + .check_bitstream = mxf_check_bitstream, }; const FFOutputFormat ff_mxf_d10_muxer = { @@ -3310,4 +3664,5 @@ const FFOutputFormat ff_mxf_opatom_muxer = { .p.flags = AVFMT_NOTIMESTAMPS, .interleave_packet = mxf_interleave, .p.priv_class = &mxf_opatom_muxer_class, + .check_bitstream = mxf_check_bitstream, }; diff --git a/libavformat/mxg.c b/libavformat/mxg.c index b160ccb9f97..23d72ad23bc 100644 --- a/libavformat/mxg.c +++ b/libavformat/mxg.c @@ -24,6 +24,7 @@ #include "libavutil/intreadwrite.h" #include "libavcodec/mjpeg.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "avio.h" @@ -248,12 +249,12 @@ static int mxg_close(struct AVFormatContext *s) return 0; } -const AVInputFormat ff_mxg_demuxer = { - .name = "mxg", - .long_name = NULL_IF_CONFIG_SMALL("MxPEG clip"), +const FFInputFormat ff_mxg_demuxer = { + .p.name = "mxg", + .p.long_name = NULL_IF_CONFIG_SMALL("MxPEG clip"), + .p.extensions = "mxg", .priv_data_size = sizeof(MXGContext), .read_header = mxg_read_header, .read_packet = mxg_read_packet, .read_close = mxg_close, - .extensions = "mxg", }; diff --git a/libavformat/ncdec.c b/libavformat/ncdec.c index b2f4dfe2823..050d98bf5d2 100644 --- a/libavformat/ncdec.c +++ b/libavformat/ncdec.c @@ -22,6 +22,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define NC_VIDEO_FLAG 0x1A5 @@ -90,11 +91,11 @@ static int nc_read_packet(AVFormatContext *s, AVPacket *pkt) return size; } -const AVInputFormat ff_nc_demuxer = { - .name = "nc", - .long_name = NULL_IF_CONFIG_SMALL("NC camera feed"), +const FFInputFormat ff_nc_demuxer = { + .p.name = "nc", + .p.long_name = NULL_IF_CONFIG_SMALL("NC camera feed"), + .p.extensions = "v", .read_probe = nc_probe, .read_header = nc_read_header, .read_packet = nc_read_packet, - .extensions = "v", }; diff --git a/libavformat/network.c b/libavformat/network.c index 21e20b3e9a9..f752efc4114 100644 --- a/libavformat/network.c +++ b/libavformat/network.c @@ -356,7 +356,7 @@ struct ConnectionAttempt { static int start_connect_attempt(struct ConnectionAttempt *attempt, struct addrinfo **ptr, int timeout_ms, URLContext *h, - void (*customize_fd)(void *, int), void *customize_ctx) + int (*customize_fd)(void *, int, int), void *customize_ctx) { struct addrinfo *ai = *ptr; int ret; @@ -371,8 +371,14 @@ static int start_connect_attempt(struct ConnectionAttempt *attempt, ff_socket_nonblock(attempt->fd, 1); - if (customize_fd) - customize_fd(customize_ctx, attempt->fd); + if (customize_fd) { + ret = customize_fd(customize_ctx, attempt->fd, ai->ai_family); + if (ret) { + closesocket(attempt->fd); + attempt->fd = -1; + return ret; + } + } while ((ret = connect(attempt->fd, ai->ai_addr, ai->ai_addrlen))) { ret = ff_neterrno(); @@ -402,7 +408,7 @@ static int start_connect_attempt(struct ConnectionAttempt *attempt, int ff_connect_parallel(struct addrinfo *addrs, int timeout_ms_per_address, int parallel, URLContext *h, int *fd, - void (*customize_fd)(void *, int), void *customize_ctx) + int (*customize_fd)(void *, int, int), void *customize_ctx) { struct ConnectionAttempt attempts[3]; struct pollfd pfd[3]; diff --git a/libavformat/network.h b/libavformat/network.h index 71c49a73fbb..ca214087fc9 100644 --- a/libavformat/network.h +++ b/libavformat/network.h @@ -336,6 +336,6 @@ void ff_log_net_error(void *ctx, int level, const char* prefix); */ int ff_connect_parallel(struct addrinfo *addrs, int timeout_ms_per_address, int parallel, URLContext *h, int *fd, - void (*customize_fd)(void *, int), void *customize_ctx); + int (*customize_fd)(void *, int, int), void *customize_ctx); #endif /* AVFORMAT_NETWORK_H */ diff --git a/libavformat/nistspheredec.c b/libavformat/nistspheredec.c index 85aa5e2cbfd..1e6c567e011 100644 --- a/libavformat/nistspheredec.c +++ b/libavformat/nistspheredec.c @@ -22,6 +22,7 @@ #include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "pcm.h" @@ -133,13 +134,13 @@ static int nist_read_header(AVFormatContext *s) return AVERROR_EOF; } -const AVInputFormat ff_nistsphere_demuxer = { - .name = "nistsphere", - .long_name = NULL_IF_CONFIG_SMALL("NIST SPeech HEader REsources"), +const FFInputFormat ff_nistsphere_demuxer = { + .p.name = "nistsphere", + .p.long_name = NULL_IF_CONFIG_SMALL("NIST SPeech HEader REsources"), + .p.extensions = "nist,sph", + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = nist_probe, .read_header = nist_read_header, .read_packet = ff_pcm_read_packet, .read_seek = ff_pcm_read_seek, - .extensions = "nist,sph", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/nspdec.c b/libavformat/nspdec.c index 923432ac928..bb605426f66 100644 --- a/libavformat/nspdec.c +++ b/libavformat/nspdec.c @@ -22,6 +22,7 @@ #include "libavutil/avstring.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "pcm.h" @@ -96,13 +97,13 @@ static int nsp_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_nsp_demuxer = { - .name = "nsp", - .long_name = NULL_IF_CONFIG_SMALL("Computerized Speech Lab NSP"), +const FFInputFormat ff_nsp_demuxer = { + .p.name = "nsp", + .p.long_name = NULL_IF_CONFIG_SMALL("Computerized Speech Lab NSP"), + .p.extensions = "nsp", + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = nsp_probe, .read_header = nsp_read_header, .read_packet = ff_pcm_read_packet, .read_seek = ff_pcm_read_seek, - .extensions = "nsp", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/nsvdec.c b/libavformat/nsvdec.c index b28576ea119..bb91db8378d 100644 --- a/libavformat/nsvdec.c +++ b/libavformat/nsvdec.c @@ -24,6 +24,7 @@ #include "libavutil/attributes.h" #include "libavutil/mathematics.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "libavutil/dict.h" #include "libavutil/intreadwrite.h" @@ -615,7 +616,7 @@ static int nsv_read_chunk(AVFormatContext *s, int fill_header) pkt = &nsv->ahead[NSV_ST_AUDIO]; /* read raw audio specific header on the first audio chunk... */ /* on ALL audio chunks ?? seems so! */ - if (asize && st[NSV_ST_AUDIO]->codecpar->codec_tag == MKTAG('P', 'C', 'M', ' ')/* && fill_header*/) { + if (asize >= 4 && st[NSV_ST_AUDIO]->codecpar->codec_tag == MKTAG('P', 'C', 'M', ' ')/* && fill_header*/) { uint8_t bps; uint8_t channels; uint16_t samplerate; @@ -747,11 +748,11 @@ static int nsv_probe(const AVProbeData *p) return score; } -const AVInputFormat ff_nsv_demuxer = { - .name = "nsv", - .long_name = NULL_IF_CONFIG_SMALL("Nullsoft Streaming Video"), +const FFInputFormat ff_nsv_demuxer = { + .p.name = "nsv", + .p.long_name = NULL_IF_CONFIG_SMALL("Nullsoft Streaming Video"), .priv_data_size = sizeof(NSVContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = nsv_probe, .read_header = nsv_read_header, .read_packet = nsv_read_packet, diff --git a/libavformat/nut.c b/libavformat/nut.c index 47ed1525292..a0bf257c7d4 100644 --- a/libavformat/nut.c +++ b/libavformat/nut.c @@ -184,6 +184,8 @@ const AVCodecTag ff_nut_video_tags[] = { { AV_CODEC_ID_RAWVIDEO, MKTAG(10 , 00 , '4', 'G') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '4', 00 , 12 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG(12 , 00 , '4', 'G') }, + { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '4', 00 , 14 ) }, + { AV_CODEC_ID_RAWVIDEO, MKTAG(14 , 00 , '4', 'G') }, { AV_CODEC_ID_RAWVIDEO, MKTAG('G', '4', 00 , 16 ) }, { AV_CODEC_ID_RAWVIDEO, MKTAG(16 , 00 , '4', 'G') }, diff --git a/libavformat/nutdec.c b/libavformat/nutdec.c index afa27b827cf..8c9b19eebbf 100644 --- a/libavformat/nutdec.c +++ b/libavformat/nutdec.c @@ -969,10 +969,6 @@ static int read_sm_data(AVFormatContext *s, AVIOContext *bc, AVPacket *pkt, int if (!dst) return AVERROR(ENOMEM); bytestream_put_le32(&dst, -#if FF_API_OLD_CHANNEL_LAYOUT - AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT*(!!channels) + - AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT*(!!channel_layout) + -#endif AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE*(!!sample_rate) + AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS*(!!(width|height)) ); @@ -1310,17 +1306,17 @@ static int read_seek(AVFormatContext *s, int stream_index, return 0; } -const AVInputFormat ff_nut_demuxer = { - .name = "nut", - .long_name = NULL_IF_CONFIG_SMALL("NUT"), - .flags = AVFMT_SEEK_TO_PTS, +const FFInputFormat ff_nut_demuxer = { + .p.name = "nut", + .p.long_name = NULL_IF_CONFIG_SMALL("NUT"), + .p.flags = AVFMT_SEEK_TO_PTS, + .p.extensions = "nut", + .p.codec_tag = ff_nut_codec_tags, .priv_data_size = sizeof(NUTContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = nut_probe, .read_header = nut_read_header, .read_packet = nut_read_packet, .read_close = nut_read_close, .read_seek = read_seek, - .extensions = "nut", - .codec_tag = ff_nut_codec_tags, }; diff --git a/libavformat/nutenc.c b/libavformat/nutenc.c index a5198c7ca9f..2a33f1581bc 100644 --- a/libavformat/nutenc.c +++ b/libavformat/nutenc.c @@ -916,21 +916,6 @@ static int write_sm_data(AVFormatContext *s, AVIOContext *bc, AVPacket *pkt, int break; case AV_PKT_DATA_PARAM_CHANGE: flags = bytestream_get_le32(&data); -#if FF_API_OLD_CHANNEL_LAYOUT - if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT) { - put_str(dyn_bc, "Channels"); - put_s(dyn_bc, bytestream_get_le32(&data)); - sm_data_count++; - } - if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT) { - put_str(dyn_bc, "ChannelLayout"); - put_s(dyn_bc, -2); - put_str(dyn_bc, "u64"); - put_v(dyn_bc, 8); - avio_write(dyn_bc, data, 8); data+=8; - sm_data_count++; - } -#endif if (flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE) { put_str(dyn_bc, "SampleRate"); put_s(dyn_bc, bytestream_get_le32(&data)); @@ -1063,21 +1048,21 @@ static int nut_write_packet(AVFormatContext *s, AVPacket *pkt) ffio_free_dyn_buf(&dyn_bc); if (nut->write_index) { - if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts)) < 0) - goto fail; - - if ((1ll<<60) % nut->sp_count == 0) - for (i=0; inb_streams; i++) { - int j; - StreamContext *nus = &nut->stream[i]; - av_reallocp_array(&nus->keyframe_pts, 2*nut->sp_count, sizeof(*nus->keyframe_pts)); - if (!nus->keyframe_pts) { - ret = AVERROR(ENOMEM); - goto fail; - } - for (j=nut->sp_count == 1 ? 0 : nut->sp_count; j<2*nut->sp_count; j++) - nus->keyframe_pts[j] = AV_NOPTS_VALUE; - } + if ((ret = ff_nut_add_sp(nut, nut->last_syncpoint_pos, 0 /*unused*/, pkt->dts)) < 0) + goto fail; + + if ((1ll<<60) % nut->sp_count == 0) + for (unsigned i = 0; i < s->nb_streams; i++) { + StreamContext *nus = &nut->stream[i]; + av_reallocp_array(&nus->keyframe_pts, 2*nut->sp_count, sizeof(*nus->keyframe_pts)); + if (!nus->keyframe_pts) { + ret = AVERROR(ENOMEM); + goto fail; + } + for (int j = nut->sp_count == 1 ? 0 : nut->sp_count; + j < 2 * nut->sp_count; j++) + nus->keyframe_pts[j] = AV_NOPTS_VALUE; + } } } av_assert0(nus->last_pts != AV_NOPTS_VALUE); @@ -1238,10 +1223,10 @@ static void nut_write_deinit(AVFormatContext *s) #define OFFSET(x) offsetof(NUTContext, x) #define E AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { - { "syncpoints", "NUT syncpoint behaviour", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, E, "syncpoints" }, - { "default", "", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, E, "syncpoints" }, - { "none", "Disable syncpoints, low overhead and unseekable", 0, AV_OPT_TYPE_CONST, {.i64 = NUT_PIPE}, INT_MIN, INT_MAX, E, "syncpoints" }, - { "timestamped", "Extend syncpoints with a wallclock timestamp", 0, AV_OPT_TYPE_CONST, {.i64 = NUT_BROADCAST}, INT_MIN, INT_MAX, E, "syncpoints" }, + { "syncpoints", "NUT syncpoint behaviour", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, E, .unit = "syncpoints" }, + { "default", "", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, INT_MIN, INT_MAX, E, .unit = "syncpoints" }, + { "none", "Disable syncpoints, low overhead and unseekable", 0, AV_OPT_TYPE_CONST, {.i64 = NUT_PIPE}, INT_MIN, INT_MAX, E, .unit = "syncpoints" }, + { "timestamped", "Extend syncpoints with a wallclock timestamp", 0, AV_OPT_TYPE_CONST, {.i64 = NUT_BROADCAST}, INT_MIN, INT_MAX, E, .unit = "syncpoints" }, { "write_index", "Write index", OFFSET(write_index), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, E, }, { NULL }, }; diff --git a/libavformat/nuv.c b/libavformat/nuv.c index 12c6c691eed..507a73b0fe3 100644 --- a/libavformat/nuv.c +++ b/libavformat/nuv.c @@ -396,13 +396,13 @@ static int64_t nuv_read_dts(AVFormatContext *s, int stream_index, } -const AVInputFormat ff_nuv_demuxer = { - .name = "nuv", - .long_name = NULL_IF_CONFIG_SMALL("NuppelVideo"), +const FFInputFormat ff_nuv_demuxer = { + .p.name = "nuv", + .p.long_name = NULL_IF_CONFIG_SMALL("NuppelVideo"), + .p.flags = AVFMT_GENERIC_INDEX, .priv_data_size = sizeof(NUVContext), .read_probe = nuv_probe, .read_header = nuv_header, .read_packet = nuv_packet, .read_timestamp = nuv_read_dts, - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/oggdec.c b/libavformat/oggdec.c index 3b19e0bd89b..6efcadd11c9 100644 --- a/libavformat/oggdec.c +++ b/libavformat/oggdec.c @@ -960,17 +960,17 @@ static int ogg_probe(const AVProbeData *p) return 0; } -const AVInputFormat ff_ogg_demuxer = { - .name = "ogg", - .long_name = NULL_IF_CONFIG_SMALL("Ogg"), +const FFInputFormat ff_ogg_demuxer = { + .p.name = "ogg", + .p.long_name = NULL_IF_CONFIG_SMALL("Ogg"), + .p.extensions = "ogg", + .p.flags = AVFMT_GENERIC_INDEX | AVFMT_TS_DISCONT | AVFMT_NOBINSEARCH, .priv_data_size = sizeof(struct ogg), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = ogg_probe, .read_header = ogg_read_header, .read_packet = ogg_read_packet, .read_close = ogg_read_close, .read_seek = ogg_read_seek, .read_timestamp = ogg_read_timestamp, - .extensions = "ogg", - .flags = AVFMT_GENERIC_INDEX | AVFMT_TS_DISCONT | AVFMT_NOBINSEARCH, }; diff --git a/libavformat/oggenc.c b/libavformat/oggenc.c index 2e582d0754c..bdd19530ce8 100644 --- a/libavformat/oggenc.c +++ b/libavformat/oggenc.c @@ -292,7 +292,7 @@ static uint8_t *ogg_write_vorbiscomment(int64_t offset, int bitexact, if (!p) return NULL; - ffio_init_context(&pb, p + offset, size - offset, 1, NULL, NULL, NULL, NULL); + ffio_init_write_context(&pb, p + offset, size - offset); ff_vorbiscomment_write(&pb.pub, *m, vendor, chapters, nb_chapters); if (framing_bit) avio_w8(&pb.pub, 1); @@ -771,8 +771,13 @@ const FFOutputFormat ff_ogg_muxer = { .write_packet = ogg_write_packet, .write_trailer = ogg_write_trailer, .deinit = ogg_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, +#else + .p.flags = AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT, +#endif .p.priv_class = &ogg_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif @@ -789,8 +794,13 @@ const FFOutputFormat ff_oga_muxer = { .write_packet = ogg_write_packet, .write_trailer = ogg_write_trailer, .deinit = ogg_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, +#else + .p.flags = AVFMT_TS_NEGATIVE, +#endif .p.priv_class = &ogg_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif @@ -810,8 +820,13 @@ const FFOutputFormat ff_ogv_muxer = { .write_packet = ogg_write_packet, .write_trailer = ogg_write_trailer, .deinit = ogg_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT | AVFMT_ALLOW_FLUSH, +#else + .p.flags = AVFMT_TS_NEGATIVE | AVFMT_TS_NONSTRICT, +#endif .p.priv_class = &ogg_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif @@ -828,8 +843,13 @@ const FFOutputFormat ff_spx_muxer = { .write_packet = ogg_write_packet, .write_trailer = ogg_write_trailer, .deinit = ogg_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, +#else + .p.flags = AVFMT_TS_NEGATIVE, +#endif .p.priv_class = &ogg_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif @@ -846,7 +866,12 @@ const FFOutputFormat ff_opus_muxer = { .write_packet = ogg_write_packet, .write_trailer = ogg_write_trailer, .deinit = ogg_free, +#if FF_API_ALLOW_FLUSH .p.flags = AVFMT_TS_NEGATIVE | AVFMT_ALLOW_FLUSH, +#else + .p.flags = AVFMT_TS_NEGATIVE, +#endif .p.priv_class = &ogg_muxer_class, + .flags_internal = FF_OFMT_FLAG_ALLOW_FLUSH, }; #endif diff --git a/libavformat/oggparseflac.c b/libavformat/oggparseflac.c index eef6e099277..f25ed9cc155 100644 --- a/libavformat/oggparseflac.c +++ b/libavformat/oggparseflac.c @@ -20,7 +20,7 @@ #include #include "libavcodec/avcodec.h" -#include "libavcodec/get_bits.h" +#include "libavcodec/bytestream.h" #include "libavcodec/flac.h" #include "avformat.h" #include "internal.h" @@ -34,28 +34,28 @@ flac_header (AVFormatContext * s, int idx) struct ogg *ogg = s->priv_data; struct ogg_stream *os = ogg->streams + idx; AVStream *st = s->streams[idx]; - GetBitContext gb; + GetByteContext gb; int mdt, ret; if (os->buf[os->pstart] == 0xff) return 0; - init_get_bits(&gb, os->buf + os->pstart, os->psize*8); - skip_bits1(&gb); /* metadata_last */ - mdt = get_bits(&gb, 7); + bytestream2_init(&gb, os->buf + os->pstart, os->psize); + mdt = bytestream2_get_byte(&gb) & 0x7F; if (mdt == OGG_FLAC_METADATA_TYPE_STREAMINFO) { - uint8_t *streaminfo_start = os->buf + os->pstart + 5 + 4 + 4 + 4; uint32_t samplerate; - skip_bits_long(&gb, 4*8); /* "FLAC" */ - if(get_bits(&gb, 8) != 1) /* unsupported major version */ + if (bytestream2_get_bytes_left(&gb) < 4 + 4 + 4 + 4 + FLAC_STREAMINFO_SIZE) + return AVERROR_INVALIDDATA; + bytestream2_skipu(&gb, 4); /* "FLAC" */ + if (bytestream2_get_byteu(&gb) != 1) /* unsupported major version */ return -1; - skip_bits(&gb, 8 + 16); /* minor version + header count */ - skip_bits_long(&gb, 4*8); /* "fLaC" */ + bytestream2_skipu(&gb, 1 + 2); /* minor version + header count */ + bytestream2_skipu(&gb, 4); /* "fLaC" */ /* METADATA_BLOCK_HEADER */ - if (get_bits_long(&gb, 32) != FLAC_STREAMINFO_SIZE) + if (bytestream2_get_be32u(&gb) != FLAC_STREAMINFO_SIZE) return -1; st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; @@ -64,7 +64,7 @@ flac_header (AVFormatContext * s, int idx) if ((ret = ff_alloc_extradata(st->codecpar, FLAC_STREAMINFO_SIZE)) < 0) return ret; - memcpy(st->codecpar->extradata, streaminfo_start, st->codecpar->extradata_size); + bytestream2_get_bufferu(&gb, st->codecpar->extradata, FLAC_STREAMINFO_SIZE); samplerate = AV_RB24(st->codecpar->extradata + 10) >> 4; if (!samplerate) diff --git a/libavformat/oggparsevorbis.c b/libavformat/oggparsevorbis.c index 061840c2ed4..6fd12560bc9 100644 --- a/libavformat/oggparsevorbis.c +++ b/libavformat/oggparsevorbis.c @@ -311,7 +311,12 @@ static int vorbis_header(AVFormatContext *s, int idx) if (!(pkt_type & 1)) return priv->vp ? 0 : AVERROR_INVALIDDATA; - if (os->psize < 1 || pkt_type > 5) + if (pkt_type > 5) { + av_log(s, AV_LOG_VERBOSE, "Ignoring packet with unknown type %d\n", pkt_type); + return 1; + } + + if (os->psize < 1) return AVERROR_INVALIDDATA; if (priv->packet[pkt_type >> 1]) diff --git a/libavformat/omadec.c b/libavformat/omadec.c index 066b2d85bd4..2ca3f45b4c5 100644 --- a/libavformat/omadec.c +++ b/libavformat/omadec.c @@ -44,6 +44,7 @@ #include "libavutil/channel_layout.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "libavutil/intreadwrite.h" #include "libavutil/des.h" @@ -607,17 +608,17 @@ static int oma_read_seek(struct AVFormatContext *s, return err; } -const AVInputFormat ff_oma_demuxer = { - .name = "oma", - .long_name = NULL_IF_CONFIG_SMALL("Sony OpenMG audio"), +const FFInputFormat ff_oma_demuxer = { + .p.name = "oma", + .p.long_name = NULL_IF_CONFIG_SMALL("Sony OpenMG audio"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "oma,omg,aa3", + .p.codec_tag = ff_oma_codec_tags_list, .priv_data_size = sizeof(OMAContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = oma_read_probe, .read_header = oma_read_header, .read_packet = oma_read_packet, .read_seek = oma_read_seek, .read_close = oma_read_close, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "oma,omg,aa3", - .codec_tag = ff_oma_codec_tags_list, }; diff --git a/libavformat/omaenc.c b/libavformat/omaenc.c index 6d0b47465d1..b553bc3e9df 100644 --- a/libavformat/omaenc.c +++ b/libavformat/omaenc.c @@ -97,9 +97,12 @@ const FFOutputFormat ff_oma_muxer = { .p.long_name = NULL_IF_CONFIG_SMALL("Sony OpenMG audio"), .p.mime_type = "audio/x-oma", .p.extensions = "oma", + .p.video_codec = AV_CODEC_ID_NONE, .p.audio_codec = AV_CODEC_ID_ATRAC3, + .p.subtitle_codec = AV_CODEC_ID_NONE, .write_header = oma_write_header, .write_packet = ff_raw_write_packet, .p.codec_tag = ff_oma_codec_tags_list, .p.flags = AVFMT_NOTIMESTAMPS, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH, }; diff --git a/libavformat/options.c b/libavformat/options.c index 22ad523b2dc..34d2c6e23bc 100644 --- a/libavformat/options.c +++ b/libavformat/options.c @@ -26,6 +26,7 @@ #include "libavcodec/codec_par.h" #include "libavutil/avassert.h" +#include "libavutil/iamf.h" #include "libavutil/internal.h" #include "libavutil/intmath.h" #include "libavutil/opt.h" @@ -44,7 +45,7 @@ static const char* format_to_name(void* ptr) AVFormatContext* fc = (AVFormatContext*) ptr; if(fc->iformat) return fc->iformat->name; else if(fc->oformat) return fc->oformat->name; - else return "NULL"; + else return fc->av_class->class_name; } static void *format_child_next(void *obj, void *prev) @@ -151,13 +152,6 @@ static int io_open_default(AVFormatContext *s, AVIOContext **pb, return ffio_open_whitelist(pb, url, flags, &s->interrupt_callback, options, s->protocol_whitelist, s->protocol_blacklist); } -#if FF_API_AVFORMAT_IO_CLOSE -void ff_format_io_close_default(AVFormatContext *s, AVIOContext *pb) -{ - avio_close(pb); -} -#endif - static int io_close2_default(AVFormatContext *s, AVIOContext *pb) { return avio_close(pb); @@ -174,11 +168,6 @@ AVFormatContext *avformat_alloc_context(void) s = &si->pub; s->av_class = &av_format_context_class; s->io_open = io_open_default; -#if FF_API_AVFORMAT_IO_CLOSE -FF_DISABLE_DEPRECATION_WARNINGS - s->io_close = ff_format_io_close_default; -FF_ENABLE_DEPRECATION_WARNINGS -#endif s->io_close2= io_close2_default; av_opt_set_defaults(s); @@ -190,41 +179,58 @@ FF_ENABLE_DEPRECATION_WARNINGS return NULL; } +#if FF_API_LAVF_SHORTEST si->shortest_end = AV_NOPTS_VALUE; +#endif return s; } +#if FF_API_GET_DUR_ESTIMATE_METHOD enum AVDurationEstimationMethod av_fmt_ctx_get_duration_estimation_method(const AVFormatContext* ctx) { return ctx->duration_estimation_method; } +#endif const AVClass *avformat_get_class(void) { return &av_format_context_class; } +#define DISPOSITION_OPT(ctx) \ + { "disposition", NULL, offsetof(ctx, disposition), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, \ + .flags = AV_OPT_FLAG_ENCODING_PARAM, .unit = "disposition" }, \ + { "default", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DEFAULT }, .unit = "disposition" }, \ + { "dub", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DUB }, .unit = "disposition" }, \ + { "original", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_ORIGINAL }, .unit = "disposition" }, \ + { "comment", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_COMMENT }, .unit = "disposition" }, \ + { "lyrics", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_LYRICS }, .unit = "disposition" }, \ + { "karaoke", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_KARAOKE }, .unit = "disposition" }, \ + { "forced", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_FORCED }, .unit = "disposition" }, \ + { "hearing_impaired", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_HEARING_IMPAIRED }, .unit = "disposition" }, \ + { "visual_impaired", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_VISUAL_IMPAIRED }, .unit = "disposition" }, \ + { "clean_effects", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CLEAN_EFFECTS }, .unit = "disposition" }, \ + { "attached_pic", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_ATTACHED_PIC }, .unit = "disposition" }, \ + { "timed_thumbnails", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_TIMED_THUMBNAILS }, .unit = "disposition" }, \ + { "non_diegetic", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_NON_DIEGETIC }, .unit = "disposition" }, \ + { "captions", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CAPTIONS }, .unit = "disposition" }, \ + { "descriptions", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DESCRIPTIONS }, .unit = "disposition" }, \ + { "metadata", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_METADATA }, .unit = "disposition" }, \ + { "dependent", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DEPENDENT }, .unit = "disposition" }, \ + { "still_image", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_STILL_IMAGE }, .unit = "disposition" } + static const AVOption stream_options[] = { - { "disposition", NULL, offsetof(AVStream, disposition), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, - .flags = AV_OPT_FLAG_ENCODING_PARAM, .unit = "disposition" }, - { "default", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DEFAULT }, .unit = "disposition" }, - { "dub", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DUB }, .unit = "disposition" }, - { "original", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_ORIGINAL }, .unit = "disposition" }, - { "comment", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_COMMENT }, .unit = "disposition" }, - { "lyrics", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_LYRICS }, .unit = "disposition" }, - { "karaoke", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_KARAOKE }, .unit = "disposition" }, - { "forced", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_FORCED }, .unit = "disposition" }, - { "hearing_impaired", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_HEARING_IMPAIRED }, .unit = "disposition" }, - { "visual_impaired", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_VISUAL_IMPAIRED }, .unit = "disposition" }, - { "clean_effects", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CLEAN_EFFECTS }, .unit = "disposition" }, - { "attached_pic", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_ATTACHED_PIC }, .unit = "disposition" }, - { "timed_thumbnails", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_TIMED_THUMBNAILS }, .unit = "disposition" }, - { "captions", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_CAPTIONS }, .unit = "disposition" }, - { "descriptions", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DESCRIPTIONS }, .unit = "disposition" }, - { "metadata", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_METADATA }, .unit = "disposition" }, - { "dependent", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_DEPENDENT }, .unit = "disposition" }, - { "still_image", .type = AV_OPT_TYPE_CONST, { .i64 = AV_DISPOSITION_STILL_IMAGE }, .unit = "disposition" }, + DISPOSITION_OPT(AVStream), + { "discard", NULL, offsetof(AVStream, discard), AV_OPT_TYPE_INT, { .i64 = AVDISCARD_DEFAULT }, INT_MIN, INT_MAX, + .flags = AV_OPT_FLAG_DECODING_PARAM, .unit = "avdiscard" }, + { "none", .type = AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONE }, .unit = "avdiscard" }, + { "default", .type = AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_DEFAULT }, .unit = "avdiscard" }, + { "noref", .type = AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONREF }, .unit = "avdiscard" }, + { "bidir", .type = AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_BIDIR }, .unit = "avdiscard" }, + { "nointra", .type = AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONINTRA }, .unit = "avdiscard" }, + { "nokey", .type = AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_NONKEY }, .unit = "avdiscard" }, + { "all", .type = AV_OPT_TYPE_CONST, {.i64 = AVDISCARD_ALL }, .unit = "avdiscard" }, { NULL } }; @@ -268,11 +274,13 @@ AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c) if (!st->codecpar) goto fail; - sti->avctx = avcodec_alloc_context3(NULL); - if (!sti->avctx) - goto fail; + sti->fmtctx = s; if (s->iformat) { + sti->avctx = avcodec_alloc_context3(NULL); + if (!sti->avctx) + goto fail; + sti->info = av_mallocz(sizeof(*sti->info)); if (!sti->info) goto fail; @@ -308,8 +316,11 @@ AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c) sti->pts_buffer[i] = AV_NOPTS_VALUE; st->sample_aspect_ratio = (AVRational) { 0, 1 }; + sti->transferred_mux_tb = (AVRational) { 0, 1 };; +#if FF_API_AVSTREAM_SIDE_DATA sti->inject_global_side_data = si->inject_global_side_data; +#endif sti->need_context_update = 1; @@ -320,6 +331,181 @@ AVStream *avformat_new_stream(AVFormatContext *s, const AVCodec *c) return NULL; } +#define FLAGS AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM +#define OFFSET(x) offsetof(AVStreamGroupTileGrid, x) +static const AVOption tile_grid_options[] = { + { "grid_size", "size of the output canvas", OFFSET(coded_width), + AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, INT_MAX, FLAGS }, + { "output_size", "size of valid pixels in output image meant for presentation", OFFSET(width), + AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, INT_MAX, FLAGS }, + { "background_color", "set a background color for unused pixels", + OFFSET(background), AV_OPT_TYPE_COLOR, { .str = "black"}, 0, 0, FLAGS }, + { "horizontal_offset", NULL, OFFSET(horizontal_offset), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, + { "vertical_offset", NULL, OFFSET(vertical_offset), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, + { NULL }, +}; +#undef FLAGS +#undef OFFSET + +static const AVClass tile_grid_class = { + .class_name = "AVStreamGroupTileGrid", + .version = LIBAVUTIL_VERSION_INT, + .option = tile_grid_options, +}; + +static void *stream_group_child_next(void *obj, void *prev) +{ + AVStreamGroup *stg = obj; + if (!prev) { + switch(stg->type) { + case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT: + return stg->params.iamf_audio_element; + case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: + return stg->params.iamf_mix_presentation; + case AV_STREAM_GROUP_PARAMS_TILE_GRID: + return stg->params.tile_grid; + default: + break; + } + } + return NULL; +} + +static const AVClass *stream_group_child_iterate(void **opaque) +{ + uintptr_t i = (uintptr_t)*opaque; + const AVClass *ret = NULL; + + switch(i) { + case AV_STREAM_GROUP_PARAMS_NONE: + i++; + // fall-through + case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT: + ret = av_iamf_audio_element_get_class(); + break; + case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: + ret = av_iamf_mix_presentation_get_class(); + break; + case AV_STREAM_GROUP_PARAMS_TILE_GRID: + ret = &tile_grid_class; + break; + default: + break; + } + + if (ret) + *opaque = (void*)(i + 1); + return ret; +} + +static const AVOption stream_group_options[] = { + DISPOSITION_OPT(AVStreamGroup), + {"id", "Set group id", offsetof(AVStreamGroup, id), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { NULL } +}; + +static const AVClass stream_group_class = { + .class_name = "AVStreamGroup", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, + .option = stream_group_options, + .child_next = stream_group_child_next, + .child_class_iterate = stream_group_child_iterate, +}; + +const AVClass *av_stream_group_get_class(void) +{ + return &stream_group_class; +} + +AVStreamGroup *avformat_stream_group_create(AVFormatContext *s, + enum AVStreamGroupParamsType type, + AVDictionary **options) +{ + AVStreamGroup **stream_groups; + AVStreamGroup *stg; + FFStreamGroup *stgi; + + stream_groups = av_realloc_array(s->stream_groups, s->nb_stream_groups + 1, + sizeof(*stream_groups)); + if (!stream_groups) + return NULL; + s->stream_groups = stream_groups; + + stgi = av_mallocz(sizeof(*stgi)); + if (!stgi) + return NULL; + stg = &stgi->pub; + + stg->av_class = &stream_group_class; + av_opt_set_defaults(stg); + stg->type = type; + switch (type) { + case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT: + stg->params.iamf_audio_element = av_iamf_audio_element_alloc(); + if (!stg->params.iamf_audio_element) + goto fail; + break; + case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: + stg->params.iamf_mix_presentation = av_iamf_mix_presentation_alloc(); + if (!stg->params.iamf_mix_presentation) + goto fail; + break; + case AV_STREAM_GROUP_PARAMS_TILE_GRID: + stg->params.tile_grid = av_mallocz(sizeof(*stg->params.tile_grid)); + if (!stg->params.tile_grid) + goto fail; + stg->params.tile_grid->av_class = &tile_grid_class; + av_opt_set_defaults(stg->params.tile_grid); + break; + default: + goto fail; + } + + if (options) { + if (av_opt_set_dict2(stg, options, AV_OPT_SEARCH_CHILDREN)) + goto fail; + } + + stgi->fmtctx = s; + stg->index = s->nb_stream_groups; + + s->stream_groups[s->nb_stream_groups++] = stg; + + return stg; +fail: + ff_free_stream_group(&stg); + return NULL; +} + +static int stream_group_add_stream(AVStreamGroup *stg, AVStream *st) +{ + AVStream **streams = av_realloc_array(stg->streams, stg->nb_streams + 1, + sizeof(*stg->streams)); + if (!streams) + return AVERROR(ENOMEM); + + stg->streams = streams; + stg->streams[stg->nb_streams++] = st; + + return 0; +} + +int avformat_stream_group_add_stream(AVStreamGroup *stg, AVStream *st) +{ + const FFStreamGroup *stgi = cffstreamgroup(stg); + const FFStream *sti = cffstream(st); + + if (stgi->fmtctx != sti->fmtctx) + return AVERROR(EINVAL); + + for (int i = 0; i < stg->nb_streams; i++) + if (stg->streams[i]->index == st->index) + return AVERROR(EEXIST); + + return stream_group_add_stream(stg, st); +} + static int option_is_disposition(const AVOption *opt) { return opt->type == AV_OPT_TYPE_CONST && diff --git a/libavformat/options_table.h b/libavformat/options_table.h index 86d836cfebb..b9dca147f99 100644 --- a/libavformat/options_table.h +++ b/libavformat/options_table.h @@ -34,32 +34,34 @@ #define D AV_OPT_FLAG_DECODING_PARAM static const AVOption avformat_options[] = { -{"avioflags", NULL, OFFSET(avio_flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "avioflags"}, -{"direct", "reduce buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVIO_FLAG_DIRECT }, INT_MIN, INT_MAX, D|E, "avioflags"}, +{"avioflags", NULL, OFFSET(avio_flags), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, .unit = "avioflags"}, +{"direct", "reduce buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVIO_FLAG_DIRECT }, INT_MIN, INT_MAX, D|E, .unit = "avioflags"}, {"probesize", "set probing size", OFFSET(probesize), AV_OPT_TYPE_INT64, {.i64 = 5000000 }, 32, INT64_MAX, D}, {"formatprobesize", "number of bytes to probe file format", OFFSET(format_probesize), AV_OPT_TYPE_INT, {.i64 = PROBE_BUF_MAX}, 0, INT_MAX-1, D}, {"packetsize", "set packet size", OFFSET(packet_size), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, 0, INT_MAX, E}, -{"fflags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = AVFMT_FLAG_AUTO_BSF }, INT_MIN, INT_MAX, D|E, "fflags"}, -{"flush_packets", "reduce the latency by flushing out packets immediately", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_FLUSH_PACKETS }, INT_MIN, INT_MAX, E, "fflags"}, -{"ignidx", "ignore index", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_IGNIDX }, INT_MIN, INT_MAX, D, "fflags"}, -{"genpts", "generate pts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_GENPTS }, INT_MIN, INT_MAX, D, "fflags"}, -{"nofillin", "do not fill in missing values that can be exactly calculated", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOFILLIN }, INT_MIN, INT_MAX, D, "fflags"}, -{"noparse", "disable AVParsers, this needs nofillin too", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOPARSE }, INT_MIN, INT_MAX, D, "fflags"}, -{"igndts", "ignore dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_IGNDTS }, INT_MIN, INT_MAX, D, "fflags"}, -{"discardcorrupt", "discard corrupted frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_DISCARD_CORRUPT }, INT_MIN, INT_MAX, D, "fflags"}, -{"sortdts", "try to interleave outputted packets by dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_SORT_DTS }, INT_MIN, INT_MAX, D, "fflags"}, -{"fastseek", "fast but inaccurate seeks", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_FAST_SEEK }, INT_MIN, INT_MAX, D, "fflags"}, -{"nobuffer", "reduce the latency introduced by optional buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOBUFFER }, 0, INT_MAX, D, "fflags"}, -{"bitexact", "do not write random/volatile data", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_BITEXACT }, 0, 0, E, "fflags" }, -{"shortest", "stop muxing with the shortest stream", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_SHORTEST }, 0, 0, E, "fflags" }, -{"autobsf", "add needed bsfs automatically", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_AUTO_BSF }, 0, 0, E, "fflags" }, +{"fflags", NULL, OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = AVFMT_FLAG_AUTO_BSF }, INT_MIN, INT_MAX, D|E, .unit = "fflags"}, +{"flush_packets", "reduce the latency by flushing out packets immediately", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_FLUSH_PACKETS }, INT_MIN, INT_MAX, E, .unit = "fflags"}, +{"ignidx", "ignore index", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_IGNIDX }, INT_MIN, INT_MAX, D, .unit = "fflags"}, +{"genpts", "generate pts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_GENPTS }, INT_MIN, INT_MAX, D, .unit = "fflags"}, +{"nofillin", "do not fill in missing values that can be exactly calculated", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOFILLIN }, INT_MIN, INT_MAX, D, .unit = "fflags"}, +{"noparse", "disable AVParsers, this needs nofillin too", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOPARSE }, INT_MIN, INT_MAX, D, .unit = "fflags"}, +{"igndts", "ignore dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_IGNDTS }, INT_MIN, INT_MAX, D, .unit = "fflags"}, +{"discardcorrupt", "discard corrupted frames", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_DISCARD_CORRUPT }, INT_MIN, INT_MAX, D, .unit = "fflags"}, +{"sortdts", "try to interleave outputted packets by dts", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_SORT_DTS }, INT_MIN, INT_MAX, D, .unit = "fflags"}, +{"fastseek", "fast but inaccurate seeks", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_FAST_SEEK }, INT_MIN, INT_MAX, D, .unit = "fflags"}, +{"nobuffer", "reduce the latency introduced by optional buffering", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_FLAG_NOBUFFER }, 0, INT_MAX, D, .unit = "fflags"}, +{"bitexact", "do not write random/volatile data", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_BITEXACT }, 0, 0, E, .unit = "fflags" }, +#if FF_API_LAVF_SHORTEST +{"shortest", "stop muxing with the shortest stream", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_SHORTEST }, 0, 0, E | AV_OPT_FLAG_DEPRECATED, .unit = "fflags" }, +#endif +{"autobsf", "add needed bsfs automatically", 0, AV_OPT_TYPE_CONST, { .i64 = AVFMT_FLAG_AUTO_BSF }, 0, 0, E, .unit = "fflags" }, {"seek2any", "allow seeking to non-keyframes on demuxer level when supported", OFFSET(seek2any), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, D}, {"analyzeduration", "specify how many microseconds are analyzed to probe the input", OFFSET(max_analyze_duration), AV_OPT_TYPE_INT64, {.i64 = 0 }, 0, INT64_MAX, D}, {"cryptokey", "decryption key", OFFSET(key), AV_OPT_TYPE_BINARY, {.dbl = 0}, 0, 0, D}, {"indexmem", "max memory used for timestamp index (per stream)", OFFSET(max_index_size), AV_OPT_TYPE_INT, {.i64 = 1<<20 }, 0, INT_MAX, D}, {"rtbufsize", "max memory used for buffering real-time frames", OFFSET(max_picture_buffer), AV_OPT_TYPE_INT, {.i64 = 3041280 }, 0, INT_MAX, D}, /* defaults to 1s of 15fps 352x288 YUYV422 video */ -{"fdebug", "print specific debug info", OFFSET(debug), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, INT_MAX, E|D, "fdebug"}, -{"ts", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_FDEBUG_TS }, INT_MIN, INT_MAX, E|D, "fdebug"}, +{"fdebug", "print specific debug info", OFFSET(debug), AV_OPT_TYPE_FLAGS, {.i64 = DEFAULT }, 0, INT_MAX, E|D, .unit = "fdebug"}, +{"ts", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_FDEBUG_TS }, INT_MIN, INT_MAX, E|D, .unit = "fdebug"}, {"max_delay", "maximum muxing or demuxing delay in microseconds", OFFSET(max_delay), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, INT_MAX, E|D}, {"start_time_realtime", "wall-clock time when stream begins (PTS==0)", OFFSET(start_time_realtime), AV_OPT_TYPE_INT64, {.i64 = AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX, E}, {"fpsprobesize", "number of frames used to probe fps", OFFSET(fps_probe_size), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX-1, D}, @@ -68,16 +70,16 @@ static const AVOption avformat_options[] = { {"chunk_size", "size in bytes for each chunk", OFFSET(max_chunk_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX-1, E}, /* this is a crutch for avconv, since it cannot deal with identically named options in different contexts. * to be removed when avconv is fixed */ -{"f_err_detect", "set error detection flags (deprecated; use err_detect, save via avconv)", OFFSET(error_recognition), AV_OPT_TYPE_FLAGS, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, "err_detect"}, -{"err_detect", "set error detection flags", OFFSET(error_recognition), AV_OPT_TYPE_FLAGS, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, "err_detect"}, -{"crccheck", "verify embedded CRCs", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, "err_detect"}, -{"bitstream", "detect bitstream specification deviations", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BITSTREAM }, INT_MIN, INT_MAX, D, "err_detect"}, -{"buffer", "detect improper bitstream length", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BUFFER }, INT_MIN, INT_MAX, D, "err_detect"}, -{"explode", "abort decoding on minor error detection", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_EXPLODE }, INT_MIN, INT_MAX, D, "err_detect"}, -{"ignore_err", "ignore errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_IGNORE_ERR }, INT_MIN, INT_MAX, D, "err_detect"}, -{"careful", "consider things that violate the spec, are fast to check and have not been seen in the wild as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CAREFUL }, INT_MIN, INT_MAX, D, "err_detect"}, -{"compliant", "consider all spec non compliancies as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_COMPLIANT | AV_EF_CAREFUL }, INT_MIN, INT_MAX, D, "err_detect"}, -{"aggressive", "consider things that a sane encoder shouldn't do as an error", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_AGGRESSIVE | AV_EF_COMPLIANT | AV_EF_CAREFUL}, INT_MIN, INT_MAX, D, "err_detect"}, +{"f_err_detect", "set error detection flags (deprecated; use err_detect, save via avconv)", OFFSET(error_recognition), AV_OPT_TYPE_FLAGS, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, .unit = "err_detect"}, +{"err_detect", "set error detection flags", OFFSET(error_recognition), AV_OPT_TYPE_FLAGS, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, .unit = "err_detect"}, +{"crccheck", "verify embedded CRCs", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CRCCHECK }, INT_MIN, INT_MAX, D, .unit = "err_detect"}, +{"bitstream", "detect bitstream specification deviations", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BITSTREAM }, INT_MIN, INT_MAX, D, .unit = "err_detect"}, +{"buffer", "detect improper bitstream length", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_BUFFER }, INT_MIN, INT_MAX, D, .unit = "err_detect"}, +{"explode", "abort decoding on minor error detection", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_EXPLODE }, INT_MIN, INT_MAX, D, .unit = "err_detect"}, +{"ignore_err", "ignore errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_IGNORE_ERR }, INT_MIN, INT_MAX, D, .unit = "err_detect"}, +{"careful", "consider things that violate the spec, are fast to check and have not been seen in the wild as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_CAREFUL }, INT_MIN, INT_MAX, D, .unit = "err_detect"}, +{"compliant", "consider all spec non compliancies as errors", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_COMPLIANT | AV_EF_CAREFUL }, INT_MIN, INT_MAX, D, .unit = "err_detect"}, +{"aggressive", "consider things that a sane encoder shouldn't do as an error", 0, AV_OPT_TYPE_CONST, {.i64 = AV_EF_AGGRESSIVE | AV_EF_COMPLIANT | AV_EF_CAREFUL}, INT_MIN, INT_MAX, D, .unit = "err_detect"}, {"use_wallclock_as_timestamps", "use wallclock as timestamps", OFFSET(use_wallclock_as_timestamps), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, D}, {"skip_initial_bytes", "set number of bytes to skip before reading header and frames", OFFSET(skip_initial_bytes), AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX-1, D}, {"correct_ts_overflow", "correct single timestamp overflows", OFFSET(correct_ts_overflow), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, D}, @@ -85,19 +87,19 @@ static const AVOption avformat_options[] = { {"metadata_header_padding", "set number of bytes to be written as padding in a metadata header", OFFSET(metadata_header_padding), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, E}, {"output_ts_offset", "set output timestamp offset", OFFSET(output_ts_offset), AV_OPT_TYPE_DURATION, {.i64 = 0}, -INT64_MAX, INT64_MAX, E}, {"max_interleave_delta", "maximum buffering duration for interleaving", OFFSET(max_interleave_delta), AV_OPT_TYPE_INT64, { .i64 = 10000000 }, 0, INT64_MAX, E }, -{"f_strict", "how strictly to follow the standards (deprecated; use strict, save via avconv)", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "strict"}, -{"strict", "how strictly to follow the standards", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, "strict"}, -{"very", "strictly conform to a older more strict version of the spec or reference software", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_VERY_STRICT }, INT_MIN, INT_MAX, D|E, "strict"}, -{"strict", "strictly conform to all the things in the spec no matter what the consequences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_STRICT }, INT_MIN, INT_MAX, D|E, "strict"}, -{"normal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_NORMAL }, INT_MIN, INT_MAX, D|E, "strict"}, -{"unofficial", "allow unofficial extensions", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_UNOFFICIAL }, INT_MIN, INT_MAX, D|E, "strict"}, -{"experimental", "allow non-standardized experimental variants", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_EXPERIMENTAL }, INT_MIN, INT_MAX, D|E, "strict"}, +{"f_strict", "how strictly to follow the standards (deprecated; use strict, save via avconv)", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, .unit = "strict"}, +{"strict", "how strictly to follow the standards", OFFSET(strict_std_compliance), AV_OPT_TYPE_INT, {.i64 = DEFAULT }, INT_MIN, INT_MAX, D|E, .unit = "strict"}, +{"very", "strictly conform to a older more strict version of the spec or reference software", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_VERY_STRICT }, INT_MIN, INT_MAX, D|E, .unit = "strict"}, +{"strict", "strictly conform to all the things in the spec no matter what the consequences", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_STRICT }, INT_MIN, INT_MAX, D|E, .unit = "strict"}, +{"normal", NULL, 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_NORMAL }, INT_MIN, INT_MAX, D|E, .unit = "strict"}, +{"unofficial", "allow unofficial extensions", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_UNOFFICIAL }, INT_MIN, INT_MAX, D|E, .unit = "strict"}, +{"experimental", "allow non-standardized experimental variants", 0, AV_OPT_TYPE_CONST, {.i64 = FF_COMPLIANCE_EXPERIMENTAL }, INT_MIN, INT_MAX, D|E, .unit = "strict"}, {"max_ts_probe", "maximum number of packets to read while waiting for the first timestamp", OFFSET(max_ts_probe), AV_OPT_TYPE_INT, { .i64 = 50 }, 0, INT_MAX, D }, -{"avoid_negative_ts", "shift timestamps so they start at 0", OFFSET(avoid_negative_ts), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 2, E, "avoid_negative_ts"}, -{"auto", "enabled when required by target format", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_AUTO }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, -{"disabled", "do not change timestamps", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_DISABLED }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, -{"make_non_negative", "shift timestamps so they are non negative", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, -{"make_zero", "shift timestamps so they start at 0", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_ZERO }, INT_MIN, INT_MAX, E, "avoid_negative_ts"}, +{"avoid_negative_ts", "shift timestamps so they start at 0", OFFSET(avoid_negative_ts), AV_OPT_TYPE_INT, {.i64 = -1}, -1, 2, E, .unit = "avoid_negative_ts"}, +{"auto", "enabled when required by target format", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_AUTO }, INT_MIN, INT_MAX, E, .unit = "avoid_negative_ts"}, +{"disabled", "do not change timestamps", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_DISABLED }, INT_MIN, INT_MAX, E, .unit = "avoid_negative_ts"}, +{"make_non_negative", "shift timestamps so they are non negative", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_NON_NEGATIVE }, INT_MIN, INT_MAX, E, .unit = "avoid_negative_ts"}, +{"make_zero", "shift timestamps so they start at 0", 0, AV_OPT_TYPE_CONST, {.i64 = AVFMT_AVOID_NEG_TS_MAKE_ZERO }, INT_MIN, INT_MAX, E, .unit = "avoid_negative_ts"}, {"dump_separator", "set information dump field separator", OFFSET(dump_separator), AV_OPT_TYPE_STRING, {.str = ", "}, 0, 0, D|E}, {"codec_whitelist", "List of decoders that are allowed to be used", OFFSET(codec_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, {"format_whitelist", "List of demuxers that are allowed to be used", OFFSET(format_whitelist), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, D }, diff --git a/libavformat/os_support.c b/libavformat/os_support.c index 15cea7fa5b0..2de6a7c3d94 100644 --- a/libavformat/os_support.c +++ b/libavformat/os_support.c @@ -34,11 +34,9 @@ #if HAVE_SYS_TIME_H #include #endif /* HAVE_SYS_TIME_H */ -#if HAVE_WINSOCK2_H -#include -#elif HAVE_SYS_SELECT_H +#if HAVE_SYS_SELECT_H #include -#endif /* HAVE_WINSOCK2_H */ +#endif /* HAVE_SYS_SELECT_H */ #endif /* !HAVE_POLL_H */ #include "network.h" diff --git a/libavformat/osq.c b/libavformat/osq.c new file mode 100644 index 00000000000..9e9dbfdbe1a --- /dev/null +++ b/libavformat/osq.c @@ -0,0 +1,118 @@ +/* + * OSQ 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/intreadwrite.h" +#include "avio_internal.h" +#include "avformat.h" +#include "demux.h" +#include "internal.h" +#include "rawdec.h" + +static int osq_probe(const AVProbeData *p) +{ + if (AV_RL32(p->buf) != MKTAG('O','S','Q',' ')) + return 0; + if (AV_RL32(p->buf + 4) != 48) + return 0; + if (AV_RL16(p->buf + 8) != 1) + return 0; + if (p->buf[10] == 0) + return 0; + if (p->buf[11] == 0) + return 0; + if (AV_RL32(p->buf + 12) == 0) + return 0; + if (AV_RL16(p->buf + 16) == 0) + return 0; + + return AVPROBE_SCORE_MAX; +} + +static int osq_read_header(AVFormatContext *s) +{ + uint32_t t, size; + AVStream *st; + int ret; + + t = avio_rl32(s->pb); + if (t != MKTAG('O','S','Q',' ')) + return AVERROR_INVALIDDATA; + + size = avio_rl32(s->pb); + if (size != 48) + return AVERROR_INVALIDDATA; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + if ((ret = ff_get_extradata(s, st->codecpar, s->pb, size)) < 0) + return ret; + + t = avio_rl32(s->pb); + if (t != MKTAG('R','I','F','F')) + return AVERROR_INVALIDDATA; + avio_skip(s->pb, 8); + + t = avio_rl32(s->pb); + if (t != MKTAG('f','m','t',' ')) + return AVERROR_INVALIDDATA; + size = avio_rl32(s->pb); + avio_skip(s->pb, size); + + t = avio_rl32(s->pb); + size = avio_rl32(s->pb); + while (t != MKTAG('d','a','t','a')) { + avio_skip(s->pb, size); + + t = avio_rl32(s->pb); + size = avio_rl32(s->pb); + if (avio_feof(s->pb)) + return AVERROR_INVALIDDATA; + } + + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->codec_id = AV_CODEC_ID_OSQ; + st->codecpar->sample_rate = AV_RL32(st->codecpar->extradata + 4); + if (st->codecpar->sample_rate <= 0) + return AVERROR_INVALIDDATA; + st->codecpar->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; + st->codecpar->ch_layout.nb_channels = st->codecpar->extradata[3]; + if (st->codecpar->ch_layout.nb_channels == 0) + return AVERROR_INVALIDDATA; + st->start_time = 0; + st->duration = AV_RL32(st->codecpar->extradata + 16); + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); + + return 0; +} + +const FFInputFormat ff_osq_demuxer = { + .p.name = "osq", + .p.long_name = NULL_IF_CONFIG_SMALL("raw OSQ"), + .p.extensions = "osq", + .p.flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS, + .p.priv_class = &ff_raw_demuxer_class, + .read_probe = osq_probe, + .read_header = osq_read_header, + .read_packet = ff_raw_read_partial_packet, + .raw_codec_id = AV_CODEC_ID_OSQ, + .priv_data_size = sizeof(FFRawDemuxerContext), +}; diff --git a/libavformat/paf.c b/libavformat/paf.c index d48cf57645b..056cc6e786c 100644 --- a/libavformat/paf.c +++ b/libavformat/paf.c @@ -22,6 +22,7 @@ #include "libavutil/channel_layout.h" #include "libavcodec/paf.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define MAGIC "Packed Animation File V1.0\n(c) 1992-96 Amazing Studio\x0a\x1a" @@ -265,11 +266,11 @@ static int read_packet(AVFormatContext *s, AVPacket *pkt) return pkt->size; } -const AVInputFormat ff_paf_demuxer = { - .name = "paf", - .long_name = NULL_IF_CONFIG_SMALL("Amazing Studio Packed Animation File"), +const FFInputFormat ff_paf_demuxer = { + .p.name = "paf", + .p.long_name = NULL_IF_CONFIG_SMALL("Amazing Studio Packed Animation File"), .priv_data_size = sizeof(PAFDemuxContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = read_probe, .read_header = read_header, .read_packet = read_packet, diff --git a/libavformat/pcm.c b/libavformat/pcm.c index 9741f736676..a774dbc3726 100644 --- a/libavformat/pcm.c +++ b/libavformat/pcm.c @@ -24,27 +24,44 @@ #include "internal.h" #include "pcm.h" -#define RAW_SAMPLES 1024 +#define PCM_DEMUX_TARGET_FPS 10 -int ff_pcm_read_packet(AVFormatContext *s, AVPacket *pkt) +int ff_pcm_default_packet_size(AVCodecParameters *par) { - AVCodecParameters *par = s->streams[0]->codecpar; - int ret, size; + int nb_samples, max_samples, bits_per_sample; + int64_t bitrate; if (par->block_align <= 0) return AVERROR(EINVAL); - /* - * Compute read size to complete a read every 62ms. - * Clamp to RAW_SAMPLES if larger. - */ - size = FFMAX(par->sample_rate/25, 1); - if (par->block_align <= INT_MAX / RAW_SAMPLES) { - size = FFMIN(size, RAW_SAMPLES) * par->block_align; + max_samples = INT_MAX / par->block_align; + bits_per_sample = av_get_bits_per_sample(par->codec_id); + bitrate = par->bit_rate; + + /* Don't trust the codecpar bitrate if we can calculate it ourselves */ + if (bits_per_sample > 0 && par->sample_rate > 0 && par->ch_layout.nb_channels > 0) + if ((int64_t)par->sample_rate * par->ch_layout.nb_channels < INT64_MAX / bits_per_sample) + bitrate = bits_per_sample * (int64_t)par->sample_rate * par->ch_layout.nb_channels; + + if (bitrate > 0) { + nb_samples = av_clip64(bitrate / 8 / PCM_DEMUX_TARGET_FPS / par->block_align, 1, max_samples); + nb_samples = 1 << av_log2(nb_samples); } else { - size = par->block_align; + /* Fallback to a size based method for a non-pcm codec with unknown bitrate */ + nb_samples = av_clip(4096 / par->block_align, 1, max_samples); } + return par->block_align * nb_samples; +} + +int ff_pcm_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + int ret, size; + + size = ff_pcm_default_packet_size(s->streams[0]->codecpar); + if (size < 0) + return size; + ret = av_get_packet(s->pb, pkt, size); pkt->flags &= ~AV_PKT_FLAG_CORRUPT; diff --git a/libavformat/pcm.h b/libavformat/pcm.h index 9af36d5a2e8..1928497eed0 100644 --- a/libavformat/pcm.h +++ b/libavformat/pcm.h @@ -24,6 +24,7 @@ #include "avformat.h" +int ff_pcm_default_packet_size(AVCodecParameters *par); int ff_pcm_read_packet(AVFormatContext *s, AVPacket *pkt); int ff_pcm_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags); diff --git a/libavformat/pcmdec.c b/libavformat/pcmdec.c index 11ba0b5c0cb..e9c97f7959c 100644 --- a/libavformat/pcmdec.c +++ b/libavformat/pcmdec.c @@ -24,6 +24,7 @@ #include "libavutil/avstring.h" #include "libavutil/channel_layout.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "pcm.h" #include "libavutil/log.h" @@ -33,9 +34,6 @@ typedef struct PCMAudioDemuxerContext { AVClass *class; int sample_rate; -#if FF_API_OLD_CHANNEL_LAYOUT - int channels; -#endif AVChannelLayout ch_layout; } PCMAudioDemuxerContext; @@ -53,18 +51,11 @@ static int pcm_read_header(AVFormatContext *s) par = st->codecpar; par->codec_type = AVMEDIA_TYPE_AUDIO; - par->codec_id = s->iformat->raw_codec_id; + par->codec_id = ffifmt(s->iformat)->raw_codec_id; par->sample_rate = s1->sample_rate; -#if FF_API_OLD_CHANNEL_LAYOUT - if (s1->ch_layout.nb_channels) { -#endif ret = av_channel_layout_copy(&par->ch_layout, &s1->ch_layout); if (ret < 0) return ret; -#if FF_API_OLD_CHANNEL_LAYOUT - } else - par->ch_layout.nb_channels = s1->channels; -#endif av_opt_get(s->pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type); if (mime_type && s->iformat->mime_type) { @@ -114,12 +105,7 @@ static int pcm_read_header(AVFormatContext *s) static const AVOption pcm_options[] = { { "sample_rate", "", offsetof(PCMAudioDemuxerContext, sample_rate), AV_OPT_TYPE_INT, {.i64 = 44100}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, -#if FF_API_OLD_CHANNEL_LAYOUT - { "channels", "", offsetof(PCMAudioDemuxerContext, channels), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_DEPRECATED }, - { "ch_layout", "", offsetof(PCMAudioDemuxerContext, ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, -#else { "ch_layout", "", offsetof(PCMAudioDemuxerContext, ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str = "mono"}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, -#endif { NULL }, }; static const AVClass pcm_demuxer_class = { @@ -131,17 +117,17 @@ static const AVClass pcm_demuxer_class = { #define PCMDEF_0(name_, long_name_, ext, codec, ...) #define PCMDEF_1(name_, long_name_, ext, codec, ...) \ -const AVInputFormat ff_pcm_ ## name_ ## _demuxer = { \ - .name = #name_, \ - .long_name = NULL_IF_CONFIG_SMALL(long_name_), \ +const FFInputFormat ff_pcm_ ## name_ ## _demuxer = { \ + .p.name = #name_, \ + .p.long_name = NULL_IF_CONFIG_SMALL(long_name_), \ + .p.flags = AVFMT_GENERIC_INDEX, \ + .p.extensions = ext, \ + .p.priv_class = &pcm_demuxer_class, \ .priv_data_size = sizeof(PCMAudioDemuxerContext), \ .read_header = pcm_read_header, \ .read_packet = ff_pcm_read_packet, \ .read_seek = ff_pcm_read_seek, \ - .flags = AVFMT_GENERIC_INDEX, \ - .extensions = ext, \ .raw_codec_id = codec, \ - .priv_class = &pcm_demuxer_class, \ __VA_ARGS__ \ }; #define PCMDEF_2(name, long_name, ext, codec, enabled, ...) \ @@ -163,7 +149,7 @@ PCMDEF(s32le, "PCM signed 32-bit little-endian", NULL, S32LE) PCMDEF(s24be, "PCM signed 24-bit big-endian", NULL, S24BE) PCMDEF(s24le, "PCM signed 24-bit little-endian", NULL, S24LE) PCMDEF_EXT(s16be, "PCM signed 16-bit big-endian", - AV_NE("sw", NULL), S16BE, .mime_type = "audio/L16") + AV_NE("sw", NULL), S16BE, .p.mime_type = "audio/L16") PCMDEF(s16le, "PCM signed 16-bit little-endian", AV_NE(NULL, "sw"), S16LE) PCMDEF(s8, "PCM signed 8-bit", "sb", S8) PCMDEF(u32be, "PCM unsigned 32-bit big-endian", NULL, U32BE) @@ -180,12 +166,7 @@ PCMDEF(vidc, "PCM Archimedes VIDC", NULL, VIDC) #if CONFIG_SLN_DEMUXER static const AVOption sln_options[] = { { "sample_rate", "", offsetof(PCMAudioDemuxerContext, sample_rate), AV_OPT_TYPE_INT, {.i64 = 8000}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, -#if FF_API_OLD_CHANNEL_LAYOUT - { "channels", "", offsetof(PCMAudioDemuxerContext, channels), AV_OPT_TYPE_INT, {.i64 = 1}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_DEPRECATED }, - { "ch_layout", "", offsetof(PCMAudioDemuxerContext, ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str = NULL}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, -#else { "ch_layout", "", offsetof(PCMAudioDemuxerContext, ch_layout), AV_OPT_TYPE_CHLAYOUT, {.str = "mono"}, 0, 0, AV_OPT_FLAG_DECODING_PARAM }, -#endif { NULL }, }; @@ -196,16 +177,16 @@ static const AVClass sln_demuxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_sln_demuxer = { - .name = "sln", - .long_name = NULL_IF_CONFIG_SMALL("Asterisk raw pcm"), +const FFInputFormat ff_sln_demuxer = { + .p.name = "sln", + .p.long_name = NULL_IF_CONFIG_SMALL("Asterisk raw pcm"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "sln", + .p.priv_class = &sln_demuxer_class, .priv_data_size = sizeof(PCMAudioDemuxerContext), .read_header = pcm_read_header, .read_packet = ff_pcm_read_packet, .read_seek = ff_pcm_read_seek, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "sln", .raw_codec_id = AV_CODEC_ID_PCM_S16LE, - .priv_class = &sln_demuxer_class, }; #endif diff --git a/libavformat/pcmenc.c b/libavformat/pcmenc.c index 7f716443081..c4dd8e43c50 100644 --- a/libavformat/pcmenc.c +++ b/libavformat/pcmenc.c @@ -33,7 +33,10 @@ const FFOutputFormat ff_pcm_ ## name_ ## _muxer = { \ .p.extensions = ext, \ .p.audio_codec = codec, \ .p.video_codec = AV_CODEC_ID_NONE, \ + .p.subtitle_codec = AV_CODEC_ID_NONE, \ .p.flags = AVFMT_NOTIMESTAMPS, \ + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | \ + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, \ .write_packet = ff_raw_write_packet, \ }; #define PCMDEF_2(name, long_name, ext, codec, enabled) \ diff --git a/libavformat/pdvdec.c b/libavformat/pdvdec.c new file mode 100644 index 00000000000..8ed4e20e6b4 --- /dev/null +++ b/libavformat/pdvdec.c @@ -0,0 +1,174 @@ +/* + * PDV 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 "avformat.h" +#include "demux.h" +#include "internal.h" + +#define PDV_MAGIC "Playdate VID\x00\x00\x00\x00" + +typedef struct PDVDemuxContext { + int current_frame; + uint8_t *frame_flags; + uint32_t *frame_offsets; +} PDVDemuxContext; + +static int pdv_probe(const AVProbeData *pd) +{ + if (strncmp(pd->buf, PDV_MAGIC, sizeof(PDV_MAGIC) - 1) == 0) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int pdv_read_header(AVFormatContext *s) +{ + PDVDemuxContext *p = s->priv_data; + AVIOContext *pb = s->pb; + AVCodecParameters *par; + AVStream *st; + uint64_t start; + uint32_t fps; + + avio_skip(pb, 16); + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + par = st->codecpar; + par->codec_type = AVMEDIA_TYPE_VIDEO; + par->codec_id = AV_CODEC_ID_PDV; + st->start_time = 0; + st->duration = + st->nb_frames = avio_rl16(pb); + avio_skip(pb, 2); + fps = avio_rl32(pb); + st->avg_frame_rate = av_d2q(av_int2float(fps), INT_MAX); + par->width = avio_rl16(pb); + par->height = avio_rl16(pb); + + avpriv_set_pts_info(st, 64, st->avg_frame_rate.den, st->avg_frame_rate.num); + + p->current_frame = 0; + p->frame_flags = av_calloc(st->nb_frames + 1, sizeof(*p->frame_flags)); + p->frame_offsets = av_calloc(st->nb_frames + 1, sizeof(*p->frame_offsets)); + + if (!p->frame_flags || !p->frame_offsets) + return AVERROR(ENOMEM); + + for (int n = 0; n <= st->nb_frames; n++) { + const uint32_t entry = avio_rl32(pb); + + p->frame_flags[n] = entry & 3; + p->frame_offsets[n] = entry >> 2; + } + + start = avio_tell(pb); + + for (int n = 0; n < st->nb_frames; n++) { + const uint64_t pos = start + p->frame_offsets[n]; + const int32_t size = p->frame_offsets[n+1] - p->frame_offsets[n]; + const int flags = p->frame_flags[n] & 1 ? AVINDEX_KEYFRAME : 0; + + if (p->frame_flags[n] == 0 || size <= 0 || + ((pb->seekable & AVIO_SEEKABLE_NORMAL) && pos + size > avio_size(pb))) + break; + av_add_index_entry(st, pos, n, size, 0, flags); + } + + return 0; +} + +static int pdv_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + PDVDemuxContext *p = s->priv_data; + AVStream *st = s->streams[0]; + FFStream *const sti = ffstream(st); + AVIOContext *pb = s->pb; + int32_t size, flags, ret; + int64_t pos; + + if (p->current_frame >= st->nb_frames) + return AVERROR_EOF; + + if (p->current_frame >= sti->nb_index_entries) + return AVERROR(EIO); + + pos = sti->index_entries[p->current_frame].pos; + flags = sti->index_entries[p->current_frame].flags; + size = sti->index_entries[p->current_frame].size; + + avio_seek(pb, pos, SEEK_SET); + if (avio_feof(pb) || ((pb->seekable & AVIO_SEEKABLE_NORMAL) && pos + size > avio_size(pb)) || size == 0) + return AVERROR_EOF; + + ret = av_get_packet(pb, pkt, size); + if (ret < 0) + return ret; + + if (flags & AVINDEX_KEYFRAME) + pkt->flags |= AV_PKT_FLAG_KEY; + pkt->stream_index = 0; + pkt->pts = p->current_frame++; + pkt->duration = 1; + + return 0; +} + +static int pdv_read_close(AVFormatContext *s) +{ + PDVDemuxContext *p = s->priv_data; + + av_freep(&p->frame_flags); + av_freep(&p->frame_offsets); + + return 0; +} + +static int pdv_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) +{ + PDVDemuxContext *p = s->priv_data; + AVStream *st = s->streams[stream_index]; + int index = av_index_search_timestamp(st, timestamp, flags); + + if (index < 0) + return -1; + + if (avio_seek(s->pb, ffstream(st)->index_entries[index].pos, SEEK_SET) < 0) + return -1; + + p->current_frame = index; + + return 0; +} + +const FFInputFormat ff_pdv_demuxer = { + .p.name = "pdv", + .p.long_name = NULL_IF_CONFIG_SMALL("PlayDate Video"), + .p.extensions = "pdv", + .priv_data_size = sizeof(PDVDemuxContext), + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, + .read_probe = pdv_probe, + .read_header = pdv_read_header, + .read_packet = pdv_read_packet, + .read_close = pdv_read_close, + .read_seek = pdv_read_seek, +}; diff --git a/libavformat/pjsdec.c b/libavformat/pjsdec.c index 5b2111f7260..76e029b29fd 100644 --- a/libavformat/pjsdec.c +++ b/libavformat/pjsdec.c @@ -26,6 +26,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" #include "subtitles.h" @@ -105,14 +106,14 @@ static int pjs_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_pjs_demuxer = { - .name = "pjs", - .long_name = NULL_IF_CONFIG_SMALL("PJS (Phoenix Japanimation Society) subtitles"), +const FFInputFormat ff_pjs_demuxer = { + .p.name = "pjs", + .p.long_name = NULL_IF_CONFIG_SMALL("PJS (Phoenix Japanimation Society) subtitles"), + .p.extensions = "pjs", .priv_data_size = sizeof(PJSContext), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = pjs_probe, .read_header = pjs_read_header, - .extensions = "pjs", .read_packet = ff_subtitles_read_packet, .read_seek2 = ff_subtitles_read_seek, .read_close = ff_subtitles_read_close, diff --git a/libavformat/pmpdec.c b/libavformat/pmpdec.c index 554f1a776de..06ef53d2775 100644 --- a/libavformat/pmpdec.c +++ b/libavformat/pmpdec.c @@ -21,6 +21,7 @@ #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct { @@ -183,9 +184,9 @@ static int pmp_close(AVFormatContext *s) return 0; } -const AVInputFormat ff_pmp_demuxer = { - .name = "pmp", - .long_name = NULL_IF_CONFIG_SMALL("Playstation Portable PMP"), +const FFInputFormat ff_pmp_demuxer = { + .p.name = "pmp", + .p.long_name = NULL_IF_CONFIG_SMALL("Playstation Portable PMP"), .priv_data_size = sizeof(PMPContext), .read_probe = pmp_probe, .read_header = pmp_header, diff --git a/libavformat/pp_bnk.c b/libavformat/pp_bnk.c index c4172e2260c..34156dd717c 100644 --- a/libavformat/pp_bnk.c +++ b/libavformat/pp_bnk.c @@ -20,6 +20,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avformat.h" +#include "demux.h" #include "internal.h" #include "libavutil/intreadwrite.h" #include "libavutil/avassert.h" @@ -315,11 +316,11 @@ static int pp_bnk_seek(AVFormatContext *s, int stream_index, return 0; } -const AVInputFormat ff_pp_bnk_demuxer = { - .name = "pp_bnk", - .long_name = NULL_IF_CONFIG_SMALL("Pro Pinball Series Soundbank"), +const FFInputFormat ff_pp_bnk_demuxer = { + .p.name = "pp_bnk", + .p.long_name = NULL_IF_CONFIG_SMALL("Pro Pinball Series Soundbank"), .priv_data_size = sizeof(PPBnkCtx), - .flags_internal = FF_FMT_INIT_CLEANUP, + .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP, .read_probe = pp_bnk_probe, .read_header = pp_bnk_read_header, .read_packet = pp_bnk_read_packet, diff --git a/libavformat/protocols.c b/libavformat/protocols.c index 9491dc7d000..93a6d67261e 100644 --- a/libavformat/protocols.c +++ b/libavformat/protocols.c @@ -16,12 +16,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include "libavutil/avstring.h" #include "libavutil/log.h" #include "libavutil/mem.h" #include "url.h" +extern const URLProtocol ff_android_content_protocol; extern const URLProtocol ff_async_protocol; extern const URLProtocol ff_bluray_protocol; extern const URLProtocol ff_cache_protocol; diff --git a/libavformat/psxstr.c b/libavformat/psxstr.c index bb56b05688d..0dd4e8d3779 100644 --- a/libavformat/psxstr.c +++ b/libavformat/psxstr.c @@ -33,6 +33,7 @@ #include "libavutil/internal.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #define RIFF_TAG MKTAG('R', 'I', 'F', 'F') @@ -48,6 +49,7 @@ #define CDXA_TYPE_DATA 0x08 #define CDXA_TYPE_AUDIO 0x04 #define CDXA_TYPE_VIDEO 0x02 +#define CDXA_TYPE_EMPTY 0x00 #define STR_MAGIC (0x80010160) @@ -165,8 +167,12 @@ static int str_read_packet(AVFormatContext *s, AVStream *st; while (1) { + int read = avio_read(pb, sector, RAW_CD_SECTOR_SIZE); - if (avio_read(pb, sector, RAW_CD_SECTOR_SIZE) != RAW_CD_SECTOR_SIZE) + if (read == AVERROR_EOF) + return AVERROR_EOF; + + if (read != RAW_CD_SECTOR_SIZE) return AVERROR(EIO); channel = sector[0x11]; @@ -270,6 +276,10 @@ static int str_read_packet(AVFormatContext *s, str->channels[channel].audio_stream_index; pkt->duration = 1; return 0; + case CDXA_TYPE_EMPTY: /* CD-ROM XA, May 1991, 4.3.2.3 */ + /* NOTE this also catches 0x80 (EOF bit) because of CDXA_TYPE_MASK */ + /* TODO consider refactoring so as to explicitly handle each case? */ + break; default: av_log(s, AV_LOG_WARNING, "Unknown sector type %02X\n", sector[0x12]); /* drop the sector and move on */ @@ -293,13 +303,13 @@ static int str_read_close(AVFormatContext *s) return 0; } -const AVInputFormat ff_str_demuxer = { - .name = "psxstr", - .long_name = NULL_IF_CONFIG_SMALL("Sony Playstation STR"), +const FFInputFormat ff_str_demuxer = { + .p.name = "psxstr", + .p.long_name = NULL_IF_CONFIG_SMALL("Sony Playstation STR"), + .p.flags = AVFMT_NO_BYTE_SEEK, .priv_data_size = sizeof(StrDemuxContext), .read_probe = str_probe, .read_header = str_read_header, .read_packet = str_read_packet, .read_close = str_read_close, - .flags = AVFMT_NO_BYTE_SEEK, }; diff --git a/libavformat/pva.c b/libavformat/pva.c index da54b30bfc1..047c93c9c44 100644 --- a/libavformat/pva.c +++ b/libavformat/pva.c @@ -21,6 +21,7 @@ #include "avformat.h" #include "avio_internal.h" +#include "demux.h" #include "internal.h" #include "mpeg.h" @@ -228,9 +229,9 @@ static int64_t pva_read_timestamp(struct AVFormatContext *s, int stream_index, return res; } -const AVInputFormat ff_pva_demuxer = { - .name = "pva", - .long_name = NULL_IF_CONFIG_SMALL("TechnoTrend PVA"), +const FFInputFormat ff_pva_demuxer = { + .p.name = "pva", + .p.long_name = NULL_IF_CONFIG_SMALL("TechnoTrend PVA"), .priv_data_size = sizeof(PVAContext), .read_probe = pva_probe, .read_header = pva_read_header, diff --git a/libavformat/pvfdec.c b/libavformat/pvfdec.c index 62c169a3144..aaf60a853e9 100644 --- a/libavformat/pvfdec.c +++ b/libavformat/pvfdec.c @@ -21,6 +21,7 @@ #include "libavcodec/internal.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "pcm.h" @@ -65,13 +66,13 @@ static int pvf_read_header(AVFormatContext *s) return 0; } -const AVInputFormat ff_pvf_demuxer = { - .name = "pvf", - .long_name = NULL_IF_CONFIG_SMALL("PVF (Portable Voice Format)"), +const FFInputFormat ff_pvf_demuxer = { + .p.name = "pvf", + .p.long_name = NULL_IF_CONFIG_SMALL("PVF (Portable Voice Format)"), + .p.extensions = "pvf", + .p.flags = AVFMT_GENERIC_INDEX, .read_probe = pvf_probe, .read_header = pvf_read_header, .read_packet = ff_pcm_read_packet, .read_seek = ff_pcm_read_seek, - .extensions = "pvf", - .flags = AVFMT_GENERIC_INDEX, }; diff --git a/libavformat/qcp.c b/libavformat/qcp.c index 8d80b726a53..fdf18618d23 100644 --- a/libavformat/qcp.c +++ b/libavformat/qcp.c @@ -30,6 +30,7 @@ #include "libavutil/channel_layout.h" #include "libavutil/intreadwrite.h" #include "avformat.h" +#include "demux.h" #include "riff.h" typedef struct QCPContext { @@ -195,9 +196,9 @@ static int qcp_read_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR_EOF; } -const AVInputFormat ff_qcp_demuxer = { - .name = "qcp", - .long_name = NULL_IF_CONFIG_SMALL("QCP"), +const FFInputFormat ff_qcp_demuxer = { + .p.name = "qcp", + .p.long_name = NULL_IF_CONFIG_SMALL("QCP"), .priv_data_size = sizeof(QCPContext), .read_probe = qcp_probe, .read_header = qcp_read_header, diff --git a/libavformat/qoadec.c b/libavformat/qoadec.c new file mode 100644 index 00000000000..9cce5157fc8 --- /dev/null +++ b/libavformat/qoadec.c @@ -0,0 +1,111 @@ +/* + * QOA demuxer + * + * 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 "avformat.h" +#include "avio_internal.h" +#include "demux.h" +#include "internal.h" +#include "libavutil/intreadwrite.h" + +static int qoa_probe(const AVProbeData *p) +{ + if ((p->buf_size < 16) || + (AV_RB32(p->buf) != MKBETAG('q','o','a','f')) || + (AV_RB32(p->buf + 4) == 0) || + (p->buf[8] == 0) || + (AV_RB24(p->buf + 9) == 0) || + (AV_RB16(p->buf + 12) == 0) || + (AV_RB16(p->buf + 14) == 0)) + return 0; + return AVPROBE_SCORE_MAX; +} + +static int qoa_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + AVStream *st; + + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + + avio_skip(pb, 4); + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->codec_id = AV_CODEC_ID_QOA; + st->duration = avio_rb32(pb); + st->start_time = 0; + + ffio_ensure_seekback(pb, 4); + st->codecpar->ch_layout.nb_channels = avio_r8(pb); + if (st->codecpar->ch_layout.nb_channels == 0) + return AVERROR_INVALIDDATA; + + st->codecpar->sample_rate = avio_rb24(pb); + if (st->codecpar->sample_rate == 0) + return AVERROR_INVALIDDATA; + + avio_seek(pb, -4, SEEK_CUR); + + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); + + return 0; +} + +static int qoa_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + AVIOContext *pb = s->pb; + uint16_t size, duration; + uint8_t hdr[8]; + int64_t pos; + int ret; + + if (avio_feof(pb)) + return AVERROR_EOF; + + pos = avio_tell(pb); + ret = avio_read(pb, hdr, sizeof(hdr)); + if (ret != sizeof(hdr)) + return AVERROR_EOF; + + duration = AV_RB16(hdr + 4); + size = AV_RB16(hdr + 6); + if ((ret = av_new_packet(pkt, size)) < 0) + return ret; + + memcpy(pkt->data, hdr, sizeof(hdr)); + ret = avio_read(pb, pkt->data + sizeof(hdr), size - sizeof(hdr)); + if (ret != size - sizeof(hdr)) + return AVERROR(EIO); + pkt->stream_index = 0; + pkt->pos = pos; + pkt->duration = duration; + + return 0; +} + +const FFInputFormat ff_qoa_demuxer = { + .p.name = "qoa", + .p.long_name = NULL_IF_CONFIG_SMALL("QOA"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "qoa", + .read_probe = qoa_probe, + .read_header = qoa_read_header, + .read_packet = qoa_read_packet, +}; diff --git a/libavformat/r3d.c b/libavformat/r3d.c index be119b6b748..c83399b8fc9 100644 --- a/libavformat/r3d.c +++ b/libavformat/r3d.c @@ -23,6 +23,7 @@ #include "libavutil/dict.h" #include "libavutil/mathematics.h" #include "avformat.h" +#include "demux.h" #include "internal.h" typedef struct R3DContext { @@ -401,9 +402,9 @@ static int r3d_seek(AVFormatContext *s, int stream_index, int64_t sample_time, i return 0; } -const AVInputFormat ff_r3d_demuxer = { - .name = "r3d", - .long_name = NULL_IF_CONFIG_SMALL("REDCODE R3D"), +const FFInputFormat ff_r3d_demuxer = { + .p.name = "r3d", + .p.long_name = NULL_IF_CONFIG_SMALL("REDCODE R3D"), .priv_data_size = sizeof(R3DContext), .read_probe = r3d_probe, .read_header = r3d_read_header, diff --git a/libavformat/rangecoder_dec.c b/libavformat/rangecoder_dec.c new file mode 100644 index 00000000000..7b727f656e5 --- /dev/null +++ b/libavformat/rangecoder_dec.c @@ -0,0 +1 @@ +#include "libavcodec/rangecoder.c" diff --git a/libavformat/rawdec.c b/libavformat/rawdec.c index de804366ed1..d0c829dc429 100644 --- a/libavformat/rawdec.c +++ b/libavformat/rawdec.c @@ -23,12 +23,11 @@ #include "config_components.h" #include "avformat.h" +#include "demux.h" #include "internal.h" #include "rawdec.h" #include "libavutil/opt.h" -#include "libavcodec/avcodec.h" - #define RAW_PACKET_SIZE 1024 int ff_raw_read_partial_packet(AVFormatContext *s, AVPacket *pkt) @@ -58,7 +57,7 @@ int ff_raw_audio_read_header(AVFormatContext *s) if (!st) return AVERROR(ENOMEM); st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - st->codecpar->codec_id = s->iformat->raw_codec_id; + st->codecpar->codec_id = ffifmt(s->iformat)->raw_codec_id; ffstream(st)->need_parsing = AVSTREAM_PARSE_FULL_RAW; st->start_time = 0; /* the parameters will be extracted from the compressed bitstream */ @@ -83,10 +82,10 @@ int ff_raw_video_read_header(AVFormatContext *s) sti = ffstream(st); st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; - st->codecpar->codec_id = s->iformat->raw_codec_id; + st->codecpar->codec_id = ffifmt(s->iformat)->raw_codec_id; sti->need_parsing = AVSTREAM_PARSE_FULL_RAW; - sti->avctx->framerate = s1->framerate; + st->avg_frame_rate = s1->framerate; avpriv_set_pts_info(st, 64, 1, 1200000); fail: @@ -99,7 +98,7 @@ int ff_raw_subtitle_read_header(AVFormatContext *s) if (!st) return AVERROR(ENOMEM); st->codecpar->codec_type = AVMEDIA_TYPE_SUBTITLE; - st->codecpar->codec_id = s->iformat->raw_codec_id; + st->codecpar->codec_id = ffifmt(s->iformat)->raw_codec_id; st->start_time = 0; return 0; } @@ -110,7 +109,7 @@ static int raw_data_read_header(AVFormatContext *s) if (!st) return AVERROR(ENOMEM); st->codecpar->codec_type = AVMEDIA_TYPE_DATA; - st->codecpar->codec_id = s->iformat->raw_codec_id; + st->codecpar->codec_id = ffifmt(s->iformat)->raw_codec_id; st->start_time = 0; return 0; } @@ -147,15 +146,15 @@ const AVClass ff_raw_demuxer_class = { }; #if CONFIG_DATA_DEMUXER -const AVInputFormat ff_data_demuxer = { - .name = "data", - .long_name = NULL_IF_CONFIG_SMALL("raw data"), +const FFInputFormat ff_data_demuxer = { + .p.name = "data", + .p.long_name = NULL_IF_CONFIG_SMALL("raw data"), + .p.flags = AVFMT_NOTIMESTAMPS, + .p.priv_class = &ff_raw_demuxer_class, .read_header = raw_data_read_header, .read_packet = ff_raw_read_partial_packet, .raw_codec_id = AV_CODEC_ID_NONE, - .flags = AVFMT_NOTIMESTAMPS, .priv_data_size = sizeof(FFRawDemuxerContext),\ - .priv_class = &ff_raw_demuxer_class, }; #endif diff --git a/libavformat/rawdec.h b/libavformat/rawdec.h index f843fe5a2e0..2773b1a72b6 100644 --- a/libavformat/rawdec.h +++ b/libavformat/rawdec.h @@ -23,6 +23,7 @@ #define AVFORMAT_RAWDEC_H #include "avformat.h" +#include "demux.h" #include "libavutil/log.h" typedef struct FFRawVideoDemuxerContext { @@ -50,34 +51,34 @@ int ff_raw_video_read_header(AVFormatContext *s); int ff_raw_subtitle_read_header(AVFormatContext *s); #define FF_DEF_RAWVIDEO_DEMUXER2(shortname, longname, probe, ext, id, flag)\ -const AVInputFormat ff_ ## shortname ## _demuxer = {\ - .name = #shortname,\ - .long_name = NULL_IF_CONFIG_SMALL(longname),\ +const FFInputFormat ff_ ## shortname ## _demuxer = {\ + .p.name = #shortname,\ + .p.long_name = NULL_IF_CONFIG_SMALL(longname),\ + .p.extensions = ext,\ + .p.flags = flag | AVFMT_NOTIMESTAMPS,\ + .p.priv_class = &ff_rawvideo_demuxer_class,\ .read_probe = probe,\ .read_header = ff_raw_video_read_header,\ .read_packet = ff_raw_read_partial_packet,\ - .extensions = ext,\ - .flags = flag,\ .raw_codec_id = id,\ .priv_data_size = sizeof(FFRawVideoDemuxerContext),\ - .priv_class = &ff_rawvideo_demuxer_class,\ }; #define FF_DEF_RAWVIDEO_DEMUXER(shortname, longname, probe, ext, id)\ FF_DEF_RAWVIDEO_DEMUXER2(shortname, longname, probe, ext, id, AVFMT_GENERIC_INDEX) #define FF_DEF_RAWSUB_DEMUXER(shortname, longname, probe, ext, id, flag)\ -const AVInputFormat ff_ ## shortname ## _demuxer = {\ - .name = #shortname,\ - .long_name = NULL_IF_CONFIG_SMALL(longname),\ +const FFInputFormat ff_ ## shortname ## _demuxer = {\ + .p.name = #shortname,\ + .p.long_name = NULL_IF_CONFIG_SMALL(longname),\ + .p.extensions = ext,\ + .p.flags = flag,\ + .p.priv_class = &ff_raw_demuxer_class,\ .read_probe = probe,\ .read_header = ff_raw_subtitle_read_header,\ .read_packet = ff_raw_read_partial_packet,\ - .extensions = ext,\ - .flags = flag,\ .raw_codec_id = id,\ .priv_data_size = sizeof(FFRawDemuxerContext),\ - .priv_class = &ff_raw_demuxer_class,\ }; #endif /* AVFORMAT_RAWDEC_H */ diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c index 78fadda9679..cf298d223d8 100644 --- a/libavformat/rawenc.c +++ b/libavformat/rawenc.c @@ -34,28 +34,6 @@ int ff_raw_write_packet(AVFormatContext *s, AVPacket *pkt) return 0; } -static int force_one_stream(AVFormatContext *s) -{ - if (s->nb_streams != 1) { - av_log(s, AV_LOG_ERROR, "%s files have exactly one stream\n", - s->oformat->name); - return AVERROR(EINVAL); - } - if ( s->oformat->audio_codec != AV_CODEC_ID_NONE - && s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) { - av_log(s, AV_LOG_ERROR, "%s files have exactly one audio stream\n", - s->oformat->name); - return AVERROR(EINVAL); - } - if ( s->oformat->video_codec != AV_CODEC_ID_NONE - && s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) { - av_log(s, AV_LOG_ERROR, "%s files have exactly one video stream\n", - s->oformat->name); - return AVERROR(EINVAL); - } - return 0; -} - /* Note: Do not forget to add new entries to the Makefile as well. */ #if CONFIG_AC3_MUXER @@ -66,7 +44,9 @@ const FFOutputFormat ff_ac3_muxer = { .p.extensions = "ac3", .p.audio_codec = AV_CODEC_ID_AC3, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -98,7 +78,9 @@ const FFOutputFormat ff_adx_muxer = { .p.extensions = "adx", .p.audio_codec = AV_CODEC_ID_ADPCM_ADX, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .write_trailer = adx_write_trailer, .p.flags = AVFMT_NOTIMESTAMPS, @@ -112,7 +94,9 @@ const FFOutputFormat ff_aptx_muxer = { .p.extensions = "aptx", .p.audio_codec = AV_CODEC_ID_APTX, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -125,7 +109,9 @@ const FFOutputFormat ff_aptx_hd_muxer = { .p.extensions = "aptxhd", .p.audio_codec = AV_CODEC_ID_APTX_HD, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -138,7 +124,9 @@ const FFOutputFormat ff_avs2_muxer = { .p.extensions = "avs,avs2", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_AVS2, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -151,7 +139,9 @@ const FFOutputFormat ff_avs3_muxer = { .p.extensions = "avs3", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_AVS3, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -165,7 +155,9 @@ const FFOutputFormat ff_cavsvideo_muxer = { .p.extensions = "cavs", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_CAVS, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -177,7 +169,9 @@ const FFOutputFormat ff_codec2raw_muxer = { .p.long_name = NULL_IF_CONFIG_SMALL("raw codec2 muxer"), .p.audio_codec = AV_CODEC_ID_CODEC2, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -185,6 +179,15 @@ const FFOutputFormat ff_codec2raw_muxer = { #if CONFIG_DATA_MUXER +static av_cold int force_one_stream(AVFormatContext *s) +{ + if (s->nb_streams != 1) { + av_log(s, AV_LOG_ERROR, "This muxer supports only one stream.\n"); + return AVERROR(EINVAL); + } + return 0; +} + const FFOutputFormat ff_data_muxer = { .p.name = "data", .p.long_name = NULL_IF_CONFIG_SMALL("raw data"), @@ -201,7 +204,9 @@ const FFOutputFormat ff_dfpwm_muxer = { .p.extensions = "dfpwm", .p.audio_codec = AV_CODEC_ID_DFPWM, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -214,7 +219,9 @@ const FFOutputFormat ff_dirac_muxer = { .p.extensions = "drc,vc2", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_DIRAC, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -227,7 +234,9 @@ const FFOutputFormat ff_dnxhd_muxer = { .p.extensions = "dnxhd,dnxhr", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_DNXHD, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -241,7 +250,9 @@ const FFOutputFormat ff_dts_muxer = { .p.extensions = "dts", .p.audio_codec = AV_CODEC_ID_DTS, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -255,7 +266,9 @@ const FFOutputFormat ff_eac3_muxer = { .p.extensions = "eac3,ec3", .p.audio_codec = AV_CODEC_ID_EAC3, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -269,7 +282,9 @@ const FFOutputFormat ff_g722_muxer = { .p.extensions = "g722", .p.audio_codec = AV_CODEC_ID_ADPCM_G722, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -283,7 +298,9 @@ const FFOutputFormat ff_g723_1_muxer = { .p.extensions = "tco,rco", .p.audio_codec = AV_CODEC_ID_G723_1, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -295,7 +312,9 @@ const FFOutputFormat ff_g726_muxer = { .p.long_name = NULL_IF_CONFIG_SMALL("raw big-endian G.726 (\"left-justified\")"), .p.audio_codec = AV_CODEC_ID_ADPCM_G726, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -307,7 +326,9 @@ const FFOutputFormat ff_g726le_muxer = { .p.long_name = NULL_IF_CONFIG_SMALL("raw little-endian G.726 (\"right-justified\")"), .p.audio_codec = AV_CODEC_ID_ADPCM_G726LE, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -321,7 +342,9 @@ const FFOutputFormat ff_gsm_muxer = { .p.extensions = "gsm", .p.audio_codec = AV_CODEC_ID_GSM, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -335,7 +358,9 @@ const FFOutputFormat ff_h261_muxer = { .p.extensions = "h261", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_H261, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -349,7 +374,9 @@ const FFOutputFormat ff_h263_muxer = { .p.extensions = "h263", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_H263, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -371,13 +398,40 @@ const FFOutputFormat ff_h264_muxer = { .p.extensions = "h264,264", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_H264, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .check_bitstream = h264_check_bitstream, .p.flags = AVFMT_NOTIMESTAMPS, }; #endif +#if CONFIG_VVC_MUXER +static int vvc_check_bitstream(AVFormatContext *s, AVStream *st, + const AVPacket *pkt) +{ + if (pkt->size >= 5 && AV_RB32(pkt->data) != 0x0000001 && + AV_RB24(pkt->data) != 0x000001) + return ff_stream_add_bitstream_filter(st, "vvc_mp4toannexb", NULL); + return 1; +} + +const FFOutputFormat ff_vvc_muxer = { + .p.name = "vvc", + .p.long_name = NULL_IF_CONFIG_SMALL("raw H.266/VVC video"), + .p.extensions = "vvc,h266,266", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_VVC, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, + .write_packet = ff_raw_write_packet, + .check_bitstream = vvc_check_bitstream, + .p.flags = AVFMT_NOTIMESTAMPS, +}; +#endif + #if CONFIG_HEVC_MUXER static int hevc_check_bitstream(AVFormatContext *s, AVStream *st, const AVPacket *pkt) @@ -394,13 +448,30 @@ const FFOutputFormat ff_hevc_muxer = { .p.extensions = "hevc,h265,265", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_HEVC, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .check_bitstream = hevc_check_bitstream, .p.flags = AVFMT_NOTIMESTAMPS, }; #endif +#if CONFIG_EVC_MUXER +const FFOutputFormat ff_evc_muxer = { + .p.name = "evc", + .p.long_name = NULL_IF_CONFIG_SMALL("raw EVC video"), + .p.extensions = "evc", + .p.audio_codec = AV_CODEC_ID_NONE, + .p.video_codec = AV_CODEC_ID_EVC, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, + .write_packet = ff_raw_write_packet, + .p.flags = AVFMT_NOTIMESTAMPS, +}; +#endif + #if CONFIG_M4V_MUXER const FFOutputFormat ff_m4v_muxer = { .p.name = "m4v", @@ -408,7 +479,9 @@ const FFOutputFormat ff_m4v_muxer = { .p.extensions = "m4v", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_MPEG4, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -422,7 +495,9 @@ const FFOutputFormat ff_mjpeg_muxer = { .p.extensions = "mjpg,mjpeg", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_MJPEG, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -435,7 +510,9 @@ const FFOutputFormat ff_mlp_muxer = { .p.extensions = "mlp", .p.audio_codec = AV_CODEC_ID_MLP, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -449,7 +526,9 @@ const FFOutputFormat ff_mp2_muxer = { .p.extensions = "mp2,m2a,mpa", .p.audio_codec = AV_CODEC_ID_MP2, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -463,7 +542,9 @@ const FFOutputFormat ff_mpeg1video_muxer = { .p.extensions = "mpg,mpeg,m1v", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_MPEG1VIDEO, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -476,7 +557,9 @@ const FFOutputFormat ff_mpeg2video_muxer = { .p.extensions = "m2v", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_MPEG2VIDEO, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -495,7 +578,9 @@ const FFOutputFormat ff_obu_muxer = { .p.extensions = "obu", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_AV1, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .check_bitstream = obu_check_bitstream, .p.flags = AVFMT_NOTIMESTAMPS, @@ -509,6 +594,7 @@ const FFOutputFormat ff_rawvideo_muxer = { .p.extensions = "yuv,rgb", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_RAWVIDEO, + .p.subtitle_codec = AV_CODEC_ID_NONE, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -521,7 +607,10 @@ const FFOutputFormat ff_sbc_muxer = { .p.mime_type = "audio/x-sbc", .p.extensions = "sbc,msbc", .p.audio_codec = AV_CODEC_ID_SBC, - .init = force_one_stream, + .p.video_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -534,7 +623,9 @@ const FFOutputFormat ff_truehd_muxer = { .p.extensions = "thd", .p.audio_codec = AV_CODEC_ID_TRUEHD, .p.video_codec = AV_CODEC_ID_NONE, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; @@ -547,7 +638,9 @@ const FFOutputFormat ff_vc1_muxer = { .p.extensions = "vc1", .p.audio_codec = AV_CODEC_ID_NONE, .p.video_codec = AV_CODEC_ID_VC1, - .init = force_one_stream, + .p.subtitle_codec = AV_CODEC_ID_NONE, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, .write_packet = ff_raw_write_packet, .p.flags = AVFMT_NOTIMESTAMPS, }; diff --git a/libavformat/rawvideodec.c b/libavformat/rawvideodec.c index 514e4e044fe..0d0b8876e69 100644 --- a/libavformat/rawvideodec.c +++ b/libavformat/rawvideodec.c @@ -25,6 +25,7 @@ #include "libavutil/parseutils.h" #include "libavutil/pixdesc.h" #include "libavutil/opt.h" +#include "demux.h" #include "internal.h" #include "avformat.h" @@ -52,10 +53,10 @@ static int rawvideo_read_header(AVFormatContext *ctx) st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; - st->codecpar->codec_id = ctx->iformat->raw_codec_id; + st->codecpar->codec_id = ffifmt(ctx->iformat)->raw_codec_id; - if ((ctx->iformat->raw_codec_id != AV_CODEC_ID_V210) && - (ctx->iformat->raw_codec_id != AV_CODEC_ID_V210X)) { + if ((ffifmt(ctx->iformat)->raw_codec_id != AV_CODEC_ID_V210) && + (ffifmt(ctx->iformat)->raw_codec_id != AV_CODEC_ID_V210X)) { if ((pix_fmt = av_get_pix_fmt(s->pixel_format)) == AV_PIX_FMT_NONE) { av_log(ctx, AV_LOG_ERROR, "No such pixel format: %s.\n", s->pixel_format); @@ -72,7 +73,7 @@ static int rawvideo_read_header(AVFormatContext *ctx) st->codecpar->width = s->width; st->codecpar->height = s->height; - if (ctx->iformat->raw_codec_id == AV_CODEC_ID_BITPACKED) { + if (ffifmt(ctx->iformat)->raw_codec_id == AV_CODEC_ID_BITPACKED) { unsigned int pgroup; /* size of the pixel group in bytes */ unsigned int xinc; const AVPixFmtDescriptor *desc; @@ -96,9 +97,9 @@ static int rawvideo_read_header(AVFormatContext *ctx) } st->codecpar->codec_tag = tag; packet_size = s->width * s->height * pgroup / xinc; - } else if ((ctx->iformat->raw_codec_id == AV_CODEC_ID_V210) || - (ctx->iformat->raw_codec_id == AV_CODEC_ID_V210X)) { - pix_fmt = ctx->iformat->raw_codec_id == AV_CODEC_ID_V210 ? + } else if ((ffifmt(ctx->iformat)->raw_codec_id == AV_CODEC_ID_V210) || + (ffifmt(ctx->iformat)->raw_codec_id == AV_CODEC_ID_V210X)) { + pix_fmt = ffifmt(ctx->iformat)->raw_codec_id == AV_CODEC_ID_V210 ? AV_PIX_FMT_YUV422P10 : AV_PIX_FMT_YUV422P16; packet_size = GET_PACKET_SIZE(s->width, s->height); @@ -149,16 +150,16 @@ static const AVClass rawvideo_demuxer_class = { .version = LIBAVUTIL_VERSION_INT, }; -const AVInputFormat ff_rawvideo_demuxer = { - .name = "rawvideo", - .long_name = NULL_IF_CONFIG_SMALL("raw video"), +const FFInputFormat ff_rawvideo_demuxer = { + .p.name = "rawvideo", + .p.long_name = NULL_IF_CONFIG_SMALL("raw video"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "yuv,cif,qcif,rgb", + .p.priv_class = &rawvideo_demuxer_class, .priv_data_size = sizeof(RawVideoDemuxerContext), .read_header = rawvideo_read_header, .read_packet = rawvideo_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "yuv,cif,qcif,rgb", .raw_codec_id = AV_CODEC_ID_RAWVIDEO, - .priv_class = &rawvideo_demuxer_class, }; static const AVClass bitpacked_demuxer_class = { @@ -169,16 +170,16 @@ static const AVClass bitpacked_demuxer_class = { }; #if CONFIG_BITPACKED_DEMUXER -const AVInputFormat ff_bitpacked_demuxer = { - .name = "bitpacked", - .long_name = NULL_IF_CONFIG_SMALL("Bitpacked"), +const FFInputFormat ff_bitpacked_demuxer = { + .p.name = "bitpacked", + .p.long_name = NULL_IF_CONFIG_SMALL("Bitpacked"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "bitpacked", + .p.priv_class = &bitpacked_demuxer_class, .priv_data_size = sizeof(RawVideoDemuxerContext), .read_header = rawvideo_read_header, .read_packet = rawvideo_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "bitpacked", .raw_codec_id = AV_CODEC_ID_BITPACKED, - .priv_class = &bitpacked_demuxer_class, }; #endif // CONFIG_BITPACKED_DEMUXER @@ -190,29 +191,29 @@ static const AVClass v210_demuxer_class = { }; #if CONFIG_V210_DEMUXER -const AVInputFormat ff_v210_demuxer = { - .name = "v210", - .long_name = NULL_IF_CONFIG_SMALL("Uncompressed 4:2:2 10-bit"), +const FFInputFormat ff_v210_demuxer = { + .p.name = "v210", + .p.long_name = NULL_IF_CONFIG_SMALL("Uncompressed 4:2:2 10-bit"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "v210", + .p.priv_class = &v210_demuxer_class, .priv_data_size = sizeof(RawVideoDemuxerContext), .read_header = rawvideo_read_header, .read_packet = rawvideo_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "v210", .raw_codec_id = AV_CODEC_ID_V210, - .priv_class = &v210_demuxer_class, }; #endif // CONFIG_V210_DEMUXER #if CONFIG_V210X_DEMUXER -const AVInputFormat ff_v210x_demuxer = { - .name = "v210x", - .long_name = NULL_IF_CONFIG_SMALL("Uncompressed 4:2:2 10-bit"), +const FFInputFormat ff_v210x_demuxer = { + .p.name = "v210x", + .p.long_name = NULL_IF_CONFIG_SMALL("Uncompressed 4:2:2 10-bit"), + .p.flags = AVFMT_GENERIC_INDEX, + .p.extensions = "yuv10", + .p.priv_class = &v210_demuxer_class, .priv_data_size = sizeof(RawVideoDemuxerContext), .read_header = rawvideo_read_header, .read_packet = rawvideo_read_packet, - .flags = AVFMT_GENERIC_INDEX, - .extensions = "yuv10", .raw_codec_id = AV_CODEC_ID_V210X, - .priv_class = &v210_demuxer_class, }; #endif // CONFIG_V210X_DEMUXER diff --git a/libavformat/rcwtenc.c b/libavformat/rcwtenc.c new file mode 100644 index 00000000000..f2459ef1d3d --- /dev/null +++ b/libavformat/rcwtenc.c @@ -0,0 +1,173 @@ +/* + * RCWT (Raw Captions With Time) muxer + * + * 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 + */ + +/* + * RCWT (Raw Captions With Time) is a format native to ccextractor, a commonly + * used open source tool for processing 608/708 Closed Captions (CC) sources. + * It can be used to archive the original, raw CC bitstream and to produce + * a source file for later CC processing or conversion. As a result, + * it also allows for interopability with ccextractor for processing CC data + * extracted via ffmpeg. The format is simple to parse and can be used + * to retain all lines and variants of CC. + * + * This muxer implements the specification as of March 2024, which has + * been stable and unchanged since April 2014. + * + * This muxer will have some nuances from the way that ccextractor muxes RCWT. + * No compatibility issues when processing the output with ccextractor + * have been observed as a result of this so far, but mileage may vary + * and outputs will not be a bit-exact match. + * + * Specifically, the differences are: + * (1) This muxer will identify as "FF" as the writing program identifier, so + * as to be honest about the output's origin. + * + * (2) This muxer will not alter the extracted data except to remove invalid + * packets in between valid CC blocks. On the other hand, ccextractor + * will by default remove mid-stream padding, and add padding at the end + * of the stream (in order to convey the end time of the source video). + * + * A free specification of RCWT can be found here: + * @url{https://github.com/CCExtractor/ccextractor/blob/master/docs/BINARY_FILE_FORMAT.TXT} + */ + +#include "avformat.h" +#include "internal.h" +#include "mux.h" +#include "libavutil/log.h" +#include "libavutil/intreadwrite.h" + +#define RCWT_CLUSTER_MAX_BLOCKS 65535 +#define RCWT_BLOCK_SIZE 3 + +typedef struct RCWTContext { + int cluster_pos; + int64_t cluster_pts; + uint8_t cluster_buf[RCWT_CLUSTER_MAX_BLOCKS * RCWT_BLOCK_SIZE]; +} RCWTContext; + +static void rcwt_init_cluster(RCWTContext *rcwt) +{ + rcwt->cluster_pos = 0; + rcwt->cluster_pts = AV_NOPTS_VALUE; +} + +static void rcwt_flush_cluster(AVFormatContext *avf) +{ + RCWTContext *rcwt = avf->priv_data; + + if (rcwt->cluster_pos > 0) { + avio_wl64(avf->pb, rcwt->cluster_pts); + avio_wl16(avf->pb, rcwt->cluster_pos / RCWT_BLOCK_SIZE); + avio_write(avf->pb, rcwt->cluster_buf, rcwt->cluster_pos); + } + + rcwt_init_cluster(rcwt); +} + +static int rcwt_write_header(AVFormatContext *avf) +{ + avpriv_set_pts_info(avf->streams[0], 64, 1, 1000); + + /* magic number */ + avio_wb16(avf->pb, 0xCCCC); + avio_w8(avf->pb, 0xED); + + /* program version (identify as ffmpeg) */ + avio_wb16(avf->pb, 0xFF00); + avio_w8(avf->pb, 0x60); + + /* format version, only version 0.001 supported for now */ + avio_wb16(avf->pb, 0x0001); + + /* reserved */ + avio_wb16(avf->pb, 0x000); + avio_w8(avf->pb, 0x00); + + rcwt_init_cluster(avf->priv_data); + + return 0; +} + +static int rcwt_write_packet(AVFormatContext *avf, AVPacket *pkt) +{ + RCWTContext *rcwt = avf->priv_data; + + if (pkt->size < RCWT_BLOCK_SIZE) + return 0; + + /* new PTS, new cluster */ + if (pkt->pts != rcwt->cluster_pts) { + rcwt_flush_cluster(avf); + rcwt->cluster_pts = pkt->pts; + } + + if (pkt->pts == AV_NOPTS_VALUE) { + av_log(avf, AV_LOG_WARNING, "Ignoring CC packet with no PTS\n"); + return 0; + } + + for (int i = 0; i <= pkt->size - RCWT_BLOCK_SIZE;) { + uint8_t cc_valid; + uint8_t cc_type; + + if (rcwt->cluster_pos == RCWT_CLUSTER_MAX_BLOCKS * RCWT_BLOCK_SIZE) { + av_log(avf, AV_LOG_WARNING, "Starting new cluster due to size\n"); + rcwt_flush_cluster(avf); + } + + cc_valid = (pkt->data[i] & 0x04) >> 2; + cc_type = pkt->data[i] & 0x03; + + if (!(cc_valid || cc_type == 3)) { + i++; + continue; + } + + memcpy(&rcwt->cluster_buf[rcwt->cluster_pos], &pkt->data[i], 3); + rcwt->cluster_pos += 3; + i += 3; + } + + return 0; +} + +static int rcwt_write_trailer(AVFormatContext *avf) +{ + rcwt_flush_cluster(avf); + + return 0; +} + +const FFOutputFormat ff_rcwt_muxer = { + .p.name = "rcwt", + .p.long_name = NULL_IF_CONFIG_SMALL("RCWT (Raw Captions With Time)"), + .p.extensions = "bin", + .p.flags = AVFMT_GLOBALHEADER | AVFMT_VARIABLE_FPS | AVFMT_TS_NONSTRICT, + .p.video_codec = AV_CODEC_ID_NONE, + .p.audio_codec = AV_CODEC_ID_NONE, + .p.subtitle_codec = AV_CODEC_ID_EIA_608, + .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | + FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, + .priv_data_size = sizeof(RCWTContext), + .write_header = rcwt_write_header, + .write_packet = rcwt_write_packet, + .write_trailer = rcwt_write_trailer +}; diff --git a/libavformat/rdt.c b/libavformat/rdt.c index 2fa3c2d266b..9a6b9b53d8e 100644 --- a/libavformat/rdt.c +++ b/libavformat/rdt.c @@ -27,6 +27,7 @@ #include "avformat.h" #include "libavutil/avstring.h" +#include "demux.h" #include "rtpdec.h" #include "rdt.h" #include "libavutil/base64.h" @@ -152,8 +153,7 @@ rdt_load_mdpr (PayloadContext *rdt, AVStream *st, int rule_nr) */ if (!rdt->mlti_data) return -1; - ffio_init_context(&pb0, rdt->mlti_data, rdt->mlti_data_size, 0, - NULL, NULL, NULL, NULL); + ffio_init_read_context(&pb0, rdt->mlti_data, rdt->mlti_data_size); tag = avio_rl32(pb); if (tag == MKTAG('M', 'L', 'T', 'I')) { int num, chunk_nr; @@ -205,6 +205,8 @@ ff_rdt_parse_header(const uint8_t *buf, int len, return -1; /* not followed by a data packet */ pkt_len = AV_RB16(buf+3); + if (pkt_len > len) + return AVERROR_INVALIDDATA; buf += pkt_len; len -= pkt_len; consumed += pkt_len; @@ -302,7 +304,7 @@ rdt_parse_packet (AVFormatContext *ctx, PayloadContext *rdt, AVStream *st, FFIOContext pb; int pos, rmflags; - ffio_init_context(&pb, (uint8_t *)buf, len, 0, NULL, NULL, NULL, NULL); + ffio_init_read_context(&pb, buf, len); rmflags = (flags & RTP_FLAG_KEY) ? 2 : 0; res = ff_rm_parse_packet(rdt->rmctx, &pb.pub, st, rdt->rmst[st->index], len, pkt, &seq, rmflags, *timestamp); @@ -535,7 +537,7 @@ static av_cold int rdt_init(AVFormatContext *s, int st_index, PayloadContext *rd if ((ret = ff_copy_whiteblacklists(rdt->rmctx, s)) < 0) return ret; - return avformat_open_input(&rdt->rmctx, "", &ff_rdt_demuxer, NULL); + return avformat_open_input(&rdt->rmctx, "", &ff_rdt_demuxer.p, NULL); } static void diff --git a/libavformat/realtextdec.c b/libavformat/realtextdec.c index c281dec346d..ccf87e93752 100644 --- a/libavformat/realtextdec.c +++ b/libavformat/realtextdec.c @@ -25,6 +25,7 @@ */ #include "avformat.h" +#include "demux.h" #include "internal.h" #include "subtitles.h" #include "libavutil/avstring.h" @@ -80,6 +81,10 @@ static int realtext_read_header(AVFormatContext *s) const int64_t pos = ff_text_pos(&tr) - (c != 0); int n = ff_smil_extract_next_text_chunk(&tr, &buf, &c); + if (n < 0) { + res = n; + goto end; + } if (n == 0) break; @@ -103,7 +108,7 @@ static int realtext_read_header(AVFormatContext *s) /* if we just read a